oficina / estilo Public

sky: stale comments, the grain file in the handler test, old weights kept (review)

Claude Fable 5.1 (both branches mergeable): comments still described
five files and the intermediate state; the handler test gains grain.svg;
the haze/clouds weights stay in sky.css for pages cached from when they
were linked; the grain's brightness claim is stated exactly. Dark grain
compared by eye with the approved sky: comparable, so no extra gain.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Paul Campbell pushed by paul@keymail.dev cce8898259e170bca330daeccb2df0976fb3fb9f parent 065aca0
5 files changed, +22 −15
  • sky/generate.go +8 −7
  • sky/placeholder.go +2 −1
  • sky/sky.css +4 −0
  • sky/sky.go +6 −6
  • sky/sky_test.go +2 −1

CI passed — run details

CI log
=== modcache ===
all modules verified
=== vet ===
CGO_ENABLED=0 go vet ./...
=== fmt ===
=== build ===
CGO_ENABLED=0 go build -buildvcs=false ./...
=== go-test ===
CGO_ENABLED=0 go test -count=1 ./...
ok  	amadan.net/oficina/estilo.git/base	0.005s
ok  	amadan.net/oficina/estilo.git/brand	0.004s
ok  	amadan.net/oficina/estilo.git/busy	0.005s
ok  	amadan.net/oficina/estilo.git/callui	0.005s
ok  	amadan.net/oficina/estilo.git/chrome	0.162s
?   	amadan.net/oficina/estilo.git/cmd/oficina-notices	[no test files]
ok  	amadan.net/oficina/estilo.git/credits	0.006s
ok  	amadan.net/oficina/estilo.git/credits/catalog	0.005s
ok  	amadan.net/oficina/estilo.git/homeconnect	0.005s
ok  	amadan.net/oficina/estilo.git/internal/lint	0.004s
?   	amadan.net/oficina/estilo.git/internal/tokencheck	[no test files]
ok  	amadan.net/oficina/estilo.git/menu	0.008s
ok  	amadan.net/oficina/estilo.git/savestatus	0.075s
ok  	amadan.net/oficina/estilo.git/sky	0.019s
ok  	amadan.net/oficina/estilo.git/theme	0.005s
diff --git a/sky/generate.go b/sky/generate.go
index 43697ec..98935fe 100644
--- a/sky/generate.go
+++ b/sky/generate.go
@@ -165,9 +165,10 @@ func Clouds(id string, t Tone) string {
// grainOnly is the clouds' grain as a texture of its own: faint white
// specks where the noise is above its middle and faint black ones where it
// is below, kept only where the clouds are (their alpha). Drawn with
-// ordinary blending, so it averages to no change in brightness over any
-// backdrop — an overlay blend would show its grey wherever the sky beneath
-// is thin, and lift the whole dark sky. grainAmp is each speck's most.
+// ordinary blending, so it barely moves the average brightness (a fraction
+// of a level; the dark specks show more on a light sky, the light ones on
+// a dark one) — an overlay blend showed its grey wherever the sky beneath
+// was thin, and lifted the whole dark sky. grainAmp is each speck's most.
const grainAmp = 0.34
var grainOnly = fmt.Sprintf(`<filter id="n" filterUnits="userSpaceOnUse" x="0" y="0" width="1600" height="640">`+
@@ -180,10 +181,10 @@ var grainOnly = fmt.Sprintf(`<filter id="n" filterUnits="userSpaceOnUse" x="0" y
`<feComposite in="specks" in2="SourceGraphic" operator="in"/></filter>`,
2*grainAmp, -grainAmp, -2*grainAmp, grainAmp)
-// Grain is the clouds' texture as a layer of its own. The clouds layer
-// now sits over the placeholder at a quarter of its weight, which took its
-// grain down with it (impeccable's detector, and a zoomed look, 2026-09-29);
-// on its own layer the grain keeps the strength Paul chose. One file for
+// Grain is the clouds' texture as a layer of its own. When the placeholder
+// took over the sky's colour, the clouds layer that carried the grain went
+// faint and then unlinked (impeccable's detector, and a zoomed look,
+// 2026-09-29); on its own layer the grain keeps the strength Paul chose. One file for
// both schemes: it is the clouds' shape, not their colour.
func Grain(id string) string {
var b strings.Builder
diff --git a/sky/placeholder.go b/sky/placeholder.go
index 5df0a36..b442c94 100644
--- a/sky/placeholder.go
+++ b/sky/placeholder.go
@@ -15,7 +15,8 @@ import (
// frame so the page never shows a blank where the sky will be. It plays
// blurhash's part without the script blurhash needs to decode: a tiny
// PNG, inline as a data: URI, that the browser's upscaling smooths into
-// the same soft nebula the SVG layers draw a moment later.
+// the soft nebula the haze and clouds SVGs draw — and it is now the sky's
+// colour on its own, with only the grain and filaments over it.
//
// It is computed, not rendered: each blob is a blurred ellipse, which is
// close to a Gaussian, so a pixel is the blobs' Gaussians composited in
diff --git a/sky/sky.css b/sky/sky.css
index 0a0304d..50067a9 100644
--- a/sky/sky.css
+++ b/sky/sky.css
@@ -49,6 +49,10 @@
[estilo-sky] img { object-fit: cover; }
[estilo-sky-layer="placeholder"] { filter: blur(24px); }
[estilo-sky-layer="grain"] { --estilo-sky-k: 1; }
+/* The weights haze and clouds had when they were linked, kept for a page
+ cached from then that still names them. */
+[estilo-sky-layer="haze"] { --estilo-sky-k: 0.175; }
+[estilo-sky-layer="clouds"] { --estilo-sky-k: 0.25; }
[estilo-sky-layer="filaments"] { --estilo-sky-k: 1.3; }
/* One placeholder per scheme; show the one that matches. An explicit
diff --git a/sky/sky.go b/sky/sky.go
index 74787af..b3c2f70 100644
--- a/sky/sky.go
+++ b/sky/sky.go
@@ -9,9 +9,9 @@
// (placeholder.go), 50x20 pixels inline, so it is there in the first frame;
// the grain and filaments are two static SVGs served by Handler. Handler
// still serves the haze and clouds SVGs too, for pages cached before they
-// stopped being linked. Light and dark each get their own haze and clouds;
-// sky.css shows the pair matching the page's scheme, drifts them slowly,
-// and holds them still for anyone who asks their system for less motion.
+// stopped being linked. Light and dark each get their own placeholder;
+// sky.css shows the one matching the page's scheme, drifts the sky slowly,
+// and holds it still for anyone who asks their system for less motion.
// No script.
package sky
@@ -38,7 +38,7 @@ func Templates() embed.FS { return templateFS }
// Prefix is the URL path Handler serves under. The version is the
// generator's: a change to what a given ID draws is a new version, so
// the year-long cache below never serves a stale sky. TestSkyGoldens holds
-// one sky's five files still; when it fails, the drawing changed — bump
+// one sky's six files still; when it fails, the drawing changed — bump
// the version here, then update the goldens.
const Prefix = "/static/estilo/sky/v1/"
@@ -48,7 +48,7 @@ const SuiteSeed = "oficina"
// Layer is one image of a sky.
type Layer struct {
- Name string // placeholder, haze, clouds or filaments
+ Name string // placeholder, grain or filaments (haze and clouds are served, no longer linked)
Tone string // light, dark, or "" for both
Src string // a URL under Prefix; empty for the placeholder
// Inline is the placeholder's data: URI, typed so html/template
@@ -75,7 +75,7 @@ func For(seed string) View {
var idShape = regexp.MustCompile(`^[0-9a-f]{16}$`)
// draw returns the SVG for one layer file of sky id, or "" when the name
-// is not one of the five.
+// is not one of the six.
func draw(id, file string) string {
switch file {
case "haze-light.svg":
diff --git a/sky/sky_test.go b/sky/sky_test.go
index cb676d7..e5cecef 100644
--- a/sky/sky_test.go
+++ b/sky/sky_test.go
@@ -74,12 +74,13 @@ func TestIDIsClosedAndOpaque(t *testing.T) {
}
}
-func TestHandlerServesOnlyTheFiveLayers(t *testing.T) {
+func TestHandlerServesOnlyTheSixLayers(t *testing.T) {
h := Handler()
id := ID("acme")
for path, want := range map[string]int{
Prefix + id + "/clouds-dark.svg": 200,
Prefix + id + "/filaments.svg": 200,
+ Prefix + id + "/grain.svg": 200,
Prefix + id + "/clouds.svg": 404,
Prefix + id + "/haze-light.svg/x": 404,
Prefix + "ACME0000000000000/haze-light.svg": 404,