oficina / sheets Public

Sign-in is the Oficina door

Paul, 2026-09-29: every app's sign-in should be a version of the Oficina
sign-in screen. estilo's chrome now draws estilo-signin and friends as
Home's door — one card on the suite's sky, this app's mark and name at
its head, in its colour. This app passes App and Sky (sky.For(r.Host):
each instance's door has its own sky), serves sky.css and the sky's
images (sky.Handler, public, closed set).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Paul Campbell pushed by paul@keymail.dev ca5ff80936e68c0c50521829e7856ea2085a520e parent fcd2580
6 files changed, +17 −1
  • go.mod +1 −1
  • go.sum +2 −0
  • internal/sheets/estilo.go +2 −0
  • internal/sheets/render.go +4 −0
  • internal/sheets/signin.go +7 −0
  • internal/sheets/templates/layout.html +1 −0

CI failed — run details

CI log
amadan: sync for CI failed: exit status 128: error: RPC failed; HTTP 429 curl 22 The requested URL returned error: 429
fatal: expected 'acknowledgments'
 (and could not reset the workspace: unlinkat /home/paulca/.local/state/amadan/runner/repos/oficina__sheets/cache/go-mod/modernc.org/libc@v1.74.1/ioutil_darwin.go: permission denied)
diff --git a/go.mod b/go.mod
index 096e607..7aabdfa 100644
--- a/go.mod
+++ b/go.mod
@@ -3,7 +3,7 @@ module amadan.net/oficina/sheets
go 1.25.0
require (
- amadan.net/oficina/estilo.git v0.0.0-20260929165053-846b39772f3c
+ amadan.net/oficina/estilo.git v0.0.0-20260929183653-cc2c1e56fb4b
amadan.net/oficina/home/connect v0.0.0-20260923190447-01166fcfc0a9
amadan.net/oficina/home/handoff v0.0.0-20260908102137-481d2d57f489
amadan.net/rastrillo/rastrillo v0.26.1-0.20260908075707-29e4491631bd
diff --git a/go.sum b/go.sum
index cb4ad0f..7d83c29 100644
--- a/go.sum
+++ b/go.sum
@@ -8,6 +8,8 @@ amadan.net/oficina/estilo.git v0.0.0-20260929095838-0994675f496e h1:LTSmIWUS8RjP
amadan.net/oficina/estilo.git v0.0.0-20260929095838-0994675f496e/go.mod h1:4u0jNBtIdMLOsJyrScfqoEuYDJF7BBM3qM9YWbzt6aw=
amadan.net/oficina/estilo.git v0.0.0-20260929165053-846b39772f3c h1:YYocIz3r3b4oIA0K/58JkSsmyTOCzMT/2GaW/MFbbPo=
amadan.net/oficina/estilo.git v0.0.0-20260929165053-846b39772f3c/go.mod h1:4u0jNBtIdMLOsJyrScfqoEuYDJF7BBM3qM9YWbzt6aw=
+amadan.net/oficina/estilo.git v0.0.0-20260929183653-cc2c1e56fb4b h1:UOxYBKsOSRHcHHYNKt0i6cB1Z4yT2soywOTWURYvBWo=
+amadan.net/oficina/estilo.git v0.0.0-20260929183653-cc2c1e56fb4b/go.mod h1:4u0jNBtIdMLOsJyrScfqoEuYDJF7BBM3qM9YWbzt6aw=
amadan.net/oficina/home/connect v0.0.0-20260923190447-01166fcfc0a9 h1:K/PaOVdQqRMwifPD6ipiwLeGR5nbFSxm9FxjrDJlZcg=
amadan.net/oficina/home/connect v0.0.0-20260923190447-01166fcfc0a9/go.mod h1:YroavXe43Wietec+po8mtSk4a9Bir2h31Q3iAf4MWxI=
amadan.net/oficina/home/handoff v0.0.0-20260908102137-481d2d57f489 h1:RPKtyry/71i06o8L+gjAaUyexbmG56kO1YPs601URAM=
diff --git a/internal/sheets/estilo.go b/internal/sheets/estilo.go
index 8fe4c17..0c94c92 100644
--- a/internal/sheets/estilo.go
+++ b/internal/sheets/estilo.go
@@ -2,6 +2,7 @@ package sheets
import (
"amadan.net/oficina/estilo.git/brand"
+ "amadan.net/oficina/estilo.git/sky"
"amadan.net/oficina/estilo.git/theme"
"github.com/go-chi/chi/v5"
)
@@ -12,4 +13,5 @@ import (
func mountEstilo(r chi.Router) {
r.Handle(brand.Prefix+"*", brand.Handler())
r.Handle(theme.FontPrefix+"*", theme.FontHandler())
+ r.Handle(sky.Prefix+"*", sky.Handler())
}
diff --git a/internal/sheets/render.go b/internal/sheets/render.go
index d5dae1a..9470a6e 100644
--- a/internal/sheets/render.go
+++ b/internal/sheets/render.go
@@ -1,6 +1,7 @@
package sheets
import (
+ "amadan.net/oficina/estilo.git/sky"
"amadan.net/oficina/home/connect"
"bytes"
"embed"
@@ -73,6 +74,9 @@ func sharedStaticFiles() fs.FS {
files["static/estilo/base.css"] = &fstest.MapFile{Data: base.CSS()}
files["static/estilo/menu.css"] = &fstest.MapFile{Data: estilomenu.CSS()}
files["static/estilo/chrome.css"] = &fstest.MapFile{Data: chrome.CSS()}
+ // The sky behind the sign-in door (estilo sky); its images are
+ // served by sky.Handler.
+ files["static/estilo/sky.css"] = &fstest.MapFile{Data: sky.CSS()}
files["static/estilo/chrome.js"] = &fstest.MapFile{Data: chrome.JS()}
// rastrillo's busy rule (Paul, 2026-09-29): a submit button's label
// gives way to a spinner for at least 650ms, the button disables, and
diff --git a/internal/sheets/signin.go b/internal/sheets/signin.go
index 872f76f..c59e14a 100644
--- a/internal/sheets/signin.go
+++ b/internal/sheets/signin.go
@@ -6,6 +6,7 @@ import (
"amadan.net/oficina/estilo.git/brand"
"amadan.net/oficina/estilo.git/chrome"
+ "amadan.net/oficina/estilo.git/sky"
"github.com/carlosframework/rastrillo/sessions"
)
@@ -96,6 +97,8 @@ func (a *App) handleSignin(w http.ResponseWriter, r *http.Request) {
a.render(w, r, "signin", frame{parts: []string{"Check your email"}, signinPage: true}, http.StatusOK, signinView{
Sent: &chrome.SignInNotice{
Icon: brand.Path("sheets"),
+ App: "Sheets",
+ Sky: sky.For(r.Host),
Heading: "Check your email",
// rastrillo's Begin redirects to ?sent=1 with no address,
// so the page cannot echo it (Docs made the same call).
@@ -108,6 +111,8 @@ func (a *App) handleSignin(w http.ResponseWriter, r *http.Request) {
text, field := signinError(q.Get("err"))
form := chrome.SignIn{
Icon: brand.Path("sheets"),
+ App: "Sheets",
+ Sky: sky.For(r.Host),
Heading: "Sign in",
Action: "/signin",
AddressLabel: "Email address",
@@ -149,6 +154,8 @@ func (a *App) renderSignedOut(w http.ResponseWriter, r *http.Request, status int
w.Header().Set("Cache-Control", "no-store")
a.render(w, r, "signed_out", frame{parts: []string{"Signed out"}, signinPage: true}, status, chrome.SignInNotice{
Icon: brand.Path("sheets"),
+ App: "Sheets",
+ Sky: sky.For(r.Host),
Heading: "You’re signed out",
Body: "You’ve signed out of Sheets on this device.",
Link: &chrome.Link{Href: "/signin", Label: "Sign in"},
diff --git a/internal/sheets/templates/layout.html b/internal/sheets/templates/layout.html
index 6140c74..b988bf2 100644
--- a/internal/sheets/templates/layout.html
+++ b/internal/sheets/templates/layout.html
@@ -39,6 +39,7 @@
<link rel="stylesheet" href="{{asset "static/estilo/base.css"}}">
<link rel="stylesheet" href="{{asset "static/estilo/menu.css"}}">
<link rel="stylesheet" href="{{asset "static/estilo/chrome.css"}}">
+<link rel="stylesheet" href="{{asset "static/estilo/sky.css"}}">
<link rel="stylesheet" href="{{asset "static/sheets.css"}}">
{{/* First of every script: while a modal dialog is open, key and
clipboard events stop at the window before any page listener hears