oficina / docs Public

Serve the shared Estilo day theme through existing assets

Paul Campbell pushed by paul@keymail.dev 8a126727ae93fedadcc9af0ee8048334edd803a4 parent 65019db
4 files changed, +80 −8
  • go.mod +2 −2
  • go.sum +4 −0
  • internal/docs/render.go +6 −6
  • internal/docs/theme_assets_test.go +68 −0

CI passed — run details

diff --git a/go.mod b/go.mod
index 11b8326..4d87a39 100644
--- a/go.mod
+++ b/go.mod
@@ -3,8 +3,8 @@ module github.com/carlosframework/docs
go 1.26.5
require (
- amadan.net/oficina/estilo.git v0.0.0-20260907201639-532746acdbc1
- github.com/carlosframework/rastrillo v0.22.0
+ amadan.net/oficina/estilo.git v0.0.0-20260908001419-672a447da335
+ github.com/carlosframework/rastrillo v0.23.0
github.com/go-chi/chi/v5 v5.3.2
github.com/google/uuid v1.6.0
github.com/tetratelabs/wazero v1.12.0
diff --git a/go.sum b/go.sum
index 28bd468..c4c7b70 100644
--- a/go.sum
+++ b/go.sum
@@ -1,7 +1,11 @@
amadan.net/oficina/estilo.git v0.0.0-20260907201639-532746acdbc1 h1:9Qrzr6xT0QuaTAK4Bq3M8nWDCgkBmXy7H9BR3C+DZOo=
amadan.net/oficina/estilo.git v0.0.0-20260907201639-532746acdbc1/go.mod h1:AZvUebwQkfBh2d0TIOAO/AViIIsRx774jL7ic8XOckg=
+amadan.net/oficina/estilo.git v0.0.0-20260908001419-672a447da335 h1:YogZKYINrPNXRfPedXxOIDbGCMY4ff7yAdohYphEWMg=
+amadan.net/oficina/estilo.git v0.0.0-20260908001419-672a447da335/go.mod h1:0R45/vCUAT+kpS7S6K/ZxBdE8f7YxFN4HT3HyDJ3nPY=
github.com/carlosframework/rastrillo v0.22.0 h1:tG02WAiy2VSYqmLT6kRfYbUaiJd/pBhTIWxxjjsEoI8=
github.com/carlosframework/rastrillo v0.22.0/go.mod h1:pZlrE5F5OhspvZFzef0DrfjHAjA8FXycb9J5/8vc8Tw=
+github.com/carlosframework/rastrillo v0.23.0 h1:tKAAlwPt4Pg/rkqk7o6jvV12bbZ0r34oEVpuOlKfX7w=
+github.com/carlosframework/rastrillo v0.23.0/go.mod h1:pZlrE5F5OhspvZFzef0DrfjHAjA8FXycb9J5/8vc8Tw=
github.com/chromedp/cdproto v0.0.0-20250724212937-08a3db8b4327 h1:UQ4AU+BGti3Sy/aLU8KVseYKNALcX9UXY6DfpwQ6J8E=
github.com/chromedp/cdproto v0.0.0-20250724212937-08a3db8b4327/go.mod h1:NItd7aLkcfOA/dcMXvl8p1u+lQqioRMq/SqDp71Pb/k=
github.com/chromedp/chromedp v0.14.2 h1:r3b/WtwM50RsBZHMUm9fsNhhzRStTHrKdr2zmwbZSzM=
diff --git a/internal/docs/render.go b/internal/docs/render.go
index 9edb41b..5011faa 100644
--- a/internal/docs/render.go
+++ b/internal/docs/render.go
@@ -10,6 +10,7 @@ import (
"testing/fstest"
"amadan.net/oficina/estilo.git/savestatus"
+ "amadan.net/oficina/estilo.git/theme"
"github.com/carlosframework/rastrillo"
"github.com/carlosframework/rastrillo/ui"
)
@@ -555,10 +556,9 @@ func (rd *renderer) page(w http.ResponseWriter, r *http.Request, name string, da
}
// vendoredStaticFS builds the static/ tree rastrillo.Assets serves —
-// tokens.css and the day theme via ui.TokensCSS()/ui.ThemeCSS("day")
-// as the brief requires, plus the three scripts the shell's <head>
-// links unconditionally (ui.ShimJS/SelectJS/DatetimeJS) — as bytes
-// taken straight from the rastrillo module. Nothing here is copied to
+// the shared Estilo day theme, plus tokens.css and the three scripts
+// the shell's <head> links unconditionally (ui.ShimJS/SelectJS/DatetimeJS)
+// taken straight from the selected rastrillo module. Nothing here is copied to
// static/ by hand: rastrillo new would normally vendor these to disk
// once at scaffold time (ui.VendoredAssets's own doc comment), but
// this app was hand-built rather than scaffolded, so it takes the
@@ -566,7 +566,7 @@ func (rd *renderer) page(w http.ResponseWriter, r *http.Request, name string, da
// testing/fstest.MapFS is the standard library's only in-memory
// fs.FS — used here as a data structure, not as a test helper.
func vendoredStaticFS() (fs.FS, error) {
- theme, ok := ui.ThemeCSS(dayTheme)
+ _, ok := ui.ThemeCSS(dayTheme)
if !ok {
return nil, fmt.Errorf("docs: rastrillo ui: theme %q not shipped", dayTheme)
}
@@ -574,7 +574,7 @@ func vendoredStaticFS() (fs.FS, error) {
"static/estilo-save-status.css": {Data: savestatus.CSS()},
"static/estilo-save-status.mjs": {Data: savestatus.JS()},
"static/tokens.css": {Data: ui.TokensCSS()},
- "static/theme.css": {Data: theme},
+ "static/theme.css": {Data: theme.CSS()},
"static/rastrillo.js": {Data: ui.ShimJS()},
"static/select.js": {Data: ui.SelectJS()},
"static/datetime.js": {Data: ui.DatetimeJS()},
diff --git a/internal/docs/theme_assets_test.go b/internal/docs/theme_assets_test.go
new file mode 100644
index 0000000..6a0b61b
--- /dev/null
+++ b/internal/docs/theme_assets_test.go
@@ -0,0 +1,68 @@
+package docs
+
+import (
+ "bytes"
+ "crypto/sha256"
+ "fmt"
+ "net/http"
+ "net/http/httptest"
+ "strings"
+ "testing"
+
+ "amadan.net/oficina/estilo.git/theme"
+ "github.com/carlosframework/rastrillo/ui"
+)
+
+func TestSharedThemeAssetFingerprintAndCache(t *testing.T) {
+ app, err := NewForTest(t)
+ if err != nil {
+ t.Fatal(err)
+ }
+ page := serve(app, httptest.NewRequest(http.MethodGet, "/signin", nil))
+ if page.Code != http.StatusOK {
+ t.Fatalf("signin status %d", page.Code)
+ }
+ day, ok := ui.ThemeCSS(dayTheme)
+ if !ok {
+ t.Fatal("selected day missing")
+ }
+ body := func(css []byte) []byte {
+ if bytes.HasPrefix(css, []byte("/*")) {
+ if end := bytes.Index(css, []byte("*/")); end >= 0 {
+ return css[end+2:]
+ }
+ }
+ return css
+ }
+ if !bytes.Equal(body(theme.CSS()), body(day)) {
+ t.Fatal("shared theme differs from selected day")
+ }
+ for _, item := range []struct {
+ name string
+ data []byte
+ }{
+ {"theme", theme.CSS()}, {"tokens", ui.TokensCSS()},
+ } {
+ sum := sha256.Sum256(item.data)
+ hashed := fmt.Sprintf("/static/%s.%x.css", item.name, sum[:8])
+ if !strings.Contains(page.Body.String(), `href="`+hashed+`"`) {
+ t.Fatalf("missing selected %s fingerprint", item.name)
+ }
+ for _, path := range []string{hashed, "/static/" + item.name + ".css", "/static/" + item.name + ".0000000000000000.css"} {
+ res := serve(app, httptest.NewRequest(http.MethodGet, path, nil))
+ if res.Code != http.StatusOK || !bytes.Equal(res.Body.Bytes(), item.data) {
+ t.Fatalf("%s asset response mismatch status%d", item.name, res.Code)
+ }
+ if !strings.HasPrefix(res.Header().Get("Content-Type"), "text/css") {
+ t.Fatal("asset MIME mismatch")
+ }
+ want := "no-cache"
+ if path == hashed {
+ want = "public, max-age=31536000, immutable"
+ }
+ if res.Header().Get("Cache-Control") != want {
+ t.Fatal("asset cache policy mismatch")
+ }
+ }
+ }
+}