oficina / home Public

Clone
git clone https://amadan.net/oficina/home

Plain git — no account needed to clone.

Download

Download this file

1package home
2
3import (
4 "amadan.net/oficina/estilo.git/busy"
5 _ "embed"
6
7 "amadan.net/oficina/estilo.git/chrome"
8 "amadan.net/oficina/estilo.git/sky"
9 "amadan.net/oficina/estilo.git/theme"
10 "github.com/go-chi/chi/v5"
11)
12
13// Calma is the suite's look (estilo AGENTS.md, decisions table, row 8).
14// Home is where the suite meets you — the front page, the sign-in, your
15// home — so it is where the sky goes; the apps themselves never carry it.
16
17//go:embed static/calma.css
18var calmaCSS []byte
19
20// homeFuncs are the template helpers Home's layout calls. suiteSky is the
21// sky for pages that belong to no one team: all of Home's, today, since
22// the directory lists apps rather than one team's.
23var homeFuncs = map[string]any{
24 // v links a stylesheet or picture by its content version (assetversion.go).
25 "v": versioned,
26 "suiteSky": func() sky.View { return sky.For(sky.SuiteSeed) },
27 // Onest's stylesheet, under estilo's versioned prefix.
28 "fontsCSS": func() string { return theme.FontPrefix + "fonts.css" },
29 // The door's words and tab titles are estilo's (chrome.English), the
30 // one copy set every app's door speaks, so the hub's door cannot
31 // drift from theirs. Home draws its own templates, so it reads the
32 // strings from estilo's types rather than rendering estilo-signin.
33 "signinDoor": func() chrome.SignIn { return chrome.SignIn{App: suiteName} },
34 "sentDoor": func(again string) chrome.SignInNotice {
35 return chrome.SignInNotice{App: suiteName}.Sent(again)
36 },
37 "signedOutDoor": func() chrome.SignInNotice {
38 return chrome.SignInNotice{App: suiteName}.SignedOut("/auth/start")
39 },
40 "linkLifetime": func() int { return int(chrome.LinkLifetime.Minutes()) },
41 // doorView is estilo-door-open's data: the suite's sky behind one
42 // card, the Oficina mark and name at its head.
43 "doorView": func() doorView {
44 return doorView{Sky: sky.For(sky.SuiteSeed), Icon: versioned("/static/brand/icon.svg"), App: suiteName}
45 },
46 // hopSky is the sky a hop page keeps: the inline placeholder alone, a
47 // data: URI, so the moment between two skies is not a blank page and
48 // still fetches nothing it will not show (r3 C2).
49 "hopSky": func() sky.View {
50 var v sky.View
51 for _, l := range sky.For(sky.SuiteSeed).Layers {
52 if l.Inline != "" {
53 v.Layers = append(v.Layers, l)
54 }
55 }
56 return v
57 },
58 // Onest's file, preloaded so a cold first load paints in it rather
59 // than swapping in after (r3 C4).
60 "fontPreload": func() string { return theme.FontPrefix + "onest-latin.woff2" },
61}
62
63// doorView is estilo-door-open's data (chrome/templates/signin.html).
64type doorView struct {
65 Sky sky.View
66 Icon, App string
67}
68
69// suiteName is what the hub calls itself to people: the suite, never
70// "Home" (Paul, 2026-09-23).
71const suiteName = "Oficina"
72
73// doorPages are the pages at the door — signing in, and everything on the
74// way in or out, and id.*'s account pages. The layout draws them as
75// estilo's door: one card on the sky with the Oficina mark, under the bar.
76// The sky goes only where Calma puts it — the door, the team home's header
77// and the front page's hero — so the team chooser, Open source and the
78// error pages are plain pages under the bar.
79// Not assertion.html or docs_continue.html: they are hops (hopPages).
80var doorPages = map[string]bool{
81 "signin.html": true,
82 "check_your_mail.html": true,
83 "signin_confirm.html": true,
84 "signin_failed.html": true,
85 "passkey_confirm.html": true,
86 "passkey_enrol.html": true,
87 "recovery_codes.html": true,
88 "handoff_failed.html": true,
89 "signout_confirm.html": true,
90 "directory_signed_out.html": true,
91}
92
93// hopPages move on by themselves (an auto-submit, a 0 s refresh). They
94// keep the bar and the sky's inline placeholder, so the moment between the
95// door and the directory is not a blank page (r3 C2), but load no script,
96// no sky layer and no card: the card shows only if the hop stalls.
97var hopPages = map[string]bool{
98 "assertion.html": true,
99 "docs_continue.html": true,
100}
101
102// mountCalma serves the Calma pieces Home's pages link: Onest, the sky's
103// layers and stylesheet, and Home's own few rules for placing it. Public
104// on both origins; every handler here serves a closed set.
105func mountCalma(r chi.Router) {
106 r.Handle(theme.FontPrefix+"*", theme.FontHandler())
107 r.Handle(sky.Prefix+"*", sky.Handler())
108 mountCSS(r, "/static/estilo/sky.css", sky.CSS())
109 mountChrome(r)
110 mountCSS(r, "/static/calma.css", calmaCSS)
111 // estilo's busy rule: a submit button's label gives way to a spinner
112 // for at least 650ms, the button disables, the form refuses a second
113 // submit, and Back resets it. A progressive enhancement: every form
114 // works without it.
115 mountCSS(r, "/static/busy.css", busy.CSS())
116 versionAsset("/static/busy.js", busy.JS())
117 r.Get("/static/busy.js", serveAsset(busy.JS(), "text/javascript; charset=utf-8"))
118}
119