web: the header wears the theme, and the rail says which build it is

Two things the studio could not say about itself.

The preset the header has open was painted in the dim text colour, the same grey
as the page name beside it — so the one label up there that changes with the look
read as chrome. It now takes the theme's own accent, the colour the brand mark
and the open tab already carry, which means it follows both the light/dark mode
and the accent group the reader picked, out of the CSS token and with no colour
written into the component.

And an image has no other mark on it: once the frontend tarball is loaded on the
NAS as `:latest`, nothing on the box says which revision came off. The rail now
names the build at its foot — quiet, mono, wrapping rather than widening the
column, and gone under 860px where the rail is the phone's scrolling tab bar
instead of a column.

The name is stamped into the bundle at build time: vite.config reads VERSION
from the environment (`docker compose build frontend --build-arg
VERSION=$(git rev-parse --short HEAD)`) and falls back to the package version
plus the minute it was built, so two builds of the same tree are never the same
name. `ARG VERSION=` in the Dockerfile is the knob; the bare `npm run build` —
dev server, check scripts — still stamps its own time, and the dev server passes
nothing at all, so the line only draws when there is something to say.

Verified:
  library-check.mjs — 37 steps, all passed, the two new ones reading the header
    off the running studio: the rail foot names the build (0.1.0+202609281504)
    and the preset chip's computed colour is the brand's accent, not a grey —
    rgb(206, 117, 9) amber, rgb(93, 24, 191) after switching to violet.
  scan-nav-check.mjs and roll-walk-check.mjs — all passed. frontend tsc --noEmit
    clean. Live 8090 on index-… matching dist/: /, /library and /app 200 with 0
    console errors.

ponytail: the name is the package version plus a caller-supplied tag, and nothing
bumps the package version, so the tag is the whole identity — have the release
job write it into package.json if the numbers ever need to mean something.
This commit is contained in:
2026-09-28 22:05:55 +07:00
parent 77729ad99a
commit 36cd711302
5 changed files with 66 additions and 1 deletions
+20
View File
@@ -477,6 +477,26 @@ step('studio developed the frame from its handle', true, 'stage no longer on the
// own store and drops the query, so a reload reopens it like any other photo.
step('the address was handed back', !page.url().includes('lib='), `url=${page.url()}`);
// Two things a reader and an operator read straight off the header: the preset
// the studio has open wears the theme's accent (the same colour the brand and
// the open tab carry), and the rail's foot names the build this page came from.
await page.waitForSelector('.rail-version', { timeout: 15_000 });
const chrome = await page.evaluate(() => ({
version: document.querySelector('.rail-version')?.textContent?.trim() ?? '',
recipe: getComputedStyle(document.querySelector('.recipe-name')).color,
accent: getComputedStyle(document.querySelector('.brand em')).color,
}));
step(
'the rail foot names the build the page came from',
/^\d+\.\d+\.\d+\+\S+$/.test(chrome.version),
chrome.version
);
step(
'the preset chip in the header wears the theme accent, not a fixed grey',
chrome.recipe === chrome.accent,
`${chrome.recipe}, brand accent ${chrome.accent}`
);
const editsIn = () =>
page.evaluate(async () => {
const db = await new Promise((resolve, reject) => {