0bb4938518a9d9be90ff363fba0940cfa04cda83
8
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
35adc4a043 |
feat(homepage): rebuild on Australis Skyfall — dual theme, light mode shipped
The board was on the Australis TERMINAL palette, which is dark-only by design
("Always dark first. No light mode in this system"). Skyfall is the dual-theme
web derivative of the same science, and its bundle turned out to be sitting in
this repo's own git history: a predecessor vendored it on 2026-08-19 and a
later commit deleted it. `git show 45c1995:...` returns colors.css with both
`:root` (dark) and `[data-theme="light"]` (Skyfall Day) intact, plus the
calm-depth layout tokens, the typography scale and Supreme 400/500/700. So the
light ramp is canonical rather than derived, which was the entire objection to
building one.
The visual language moves with the palette. Depth is now the recipe and not a
choice — every elevated surface carries a 1px hairline AND a two-layer shadow,
never one without the other. Radii move to Skyfall's scale, cards at
--radius-lg. Widget stat values move from the display face to mono, because
Skyfall is explicit that numbers and telemetry are always --font-mono. The
full-width aurora ribbon under the tab bar is gone: Skyfall sanctions exactly
two accent expressions, the active rail and hero-only glows, and a decorative
gradient across the chrome is neither — so the colour it carried now lands on
the active tab as a 2px accent bar plus an --accent-soft fill, which is the
rail. Every binding is written against the semantic layer; there are no raw
family tokens and no colour literals left in our own file.
build.py now guards the vendoring instead of advising it. The three token files
are hashed and a mismatch FAILS the build — a vendored file is either
byte-identical to the bundle or it is a fork wearing the bundle's name, and the
theme this one replaces had to be torn out twice for exactly that.
⚠ Homepage's own theme toggle is unreachable, and reaching for it breaks the
dashboard. It renders only when settings.yaml leaves `theme:` unpinned, and
with the key absent the page's data loader throws and its catch branch serves
`initialSettings: {}` — no tab bar, no layout, no i18n. Six force-recreates
over seven minutes all came up empty; restoring `theme: dark` rendered
correctly on the next recreate in 12 seconds, while /api/services returned 200
with fully correct content the whole time. That is the first confirmed cause of
the long-running "tab bar goes missing after a recreate" symptom, and it also
retires the homepage.log-size lead recorded earlier today: rolling the log
aside did nothing during this episode, so that coincidence was intermittency.
So the toggle is ours. conf/custom.js renders it and stores the choice;
build.py re-emits each vendored light block twice, once for an explicit
`data-theme` and once inside a prefers-color-scheme media query scoped to
`html:not([data-theme="dark"]):not([data-theme="light"])` — that :not() pair is
what lets a stored dark choice survive a light-mode OS. Verified against both
OS preferences: load, click, click back, reload, all four correct. `data-theme`
is the control surface; Homepage's own `dark` class stays on <html> and does
not fight, because our rules carry !important on the surfaces Tailwind's
`dark:` variants would otherwise claim.
Two font substitutions, both documented rather than silent: Space Grotesk for
Bespoke Sans and JetBrains Mono for Victor Mono. Only Supreme was ever vendored
here and Skyfall's own notes call Victor Mono user-supplied, so this is a
two-line swap when the real faces arrive.
Dark and light, all four tabs: http://10.100.10.50:8090/b/homepage-skyfall/
|
||
|
|
39da1d4a97 |
feat(homepage): recategorise on "do I open this?", collapse the API groups
The board mixed tools with endpoints. A vLLM seat whose href is a /docs page
sat in the same band as ComfyUI; the MQTT broker and the RustDesk relay, which
have no page at all, sat in Apps; and `Service Networking` was thirteen members
spanning three AdGuards, five Dockges, two Traefiks and four headless agents.
Every group is now one of two kinds and they never mix. TOOLS are expanded and
sit at the top of their tab. ENDPOINTS — an API, a broker, a background agent,
an href that is /docs or /ping or nothing — carry `initiallyCollapsed: true`
and sit at the bottom. Collapsed is not hidden: the eyebrow and its rule still
render, so the tab still says the thing exists and one click expands it.
A second rule fell out of the same pass and now shapes the group boundaries: a
group's members should all carry a widget or none should. A stat strip makes a
card ~50px taller, so one widget card in a row of plain ones opens a void under
the plain ones. That is why AdGuard and Traefik get their own groups rather
than sharing one with Dockge, and it is most of why the old Service Networking
band looked broken. AdGuard (ANA) was the last short card in its row and now
carries the same query/blocked/latency strip as its two siblings — one
infra-ops AdGuard login authenticates against all three instances, verified
against each; it lives in that stack's .env on the host and is vaulted.
The sixteen GPU-backed model seats were deliberately NOT relabelled.
`homepage.group` is read at container creation, so clearer names for
`AI - Inference` and friends would have cost a recreate on six vLLM seats, four
eval seats and four TTS engines — multi-minute model reloads on endpoints peers
reach through the gateway. Order plus `initiallyCollapsed` buys the same
separation for nothing, so those names stay as they are on purpose.
28 containers that ARE cheap to bounce were relabelled, across five hosts, via
rerunnable elway playbooks. Their label steps are gated on the old value still
being present, so a second run reports skipped rather than churning. Two verify
steps were wrong on first contact and are fixed with the reason recorded: the
traefik check raced its own recreate, and asserting a model seat is "running"
cannot answer "did I bounce it" when a seat may be legitimately stopped —
container age can, and now does.
The canonical stacks/ tree was synced to the deployed labels afterwards, so
intent and reality agree again on all fourteen tracked stacks.
Also documents the real nature of the post-recreate blank dashboard, which cost
~25 minutes here and an hour on 2026-08-19. `initialSettings":{}` in the served
HTML is the catch branch of the page's data loader, not a warm-up and not a
cache — and the error can vanish entirely, because the logger is assigned inside
the same try and the catch only logs if the logger exists. Ruled out by
measurement this time: all four API routes return 200 with correct content while
the page serves {}, and the previous known-good settings.yaml reproduces it
identically. The README now carries the one-command test and the next lead.
Before/after, all four tabs: http://10.100.10.50:8090/b/homepage-relayout/
|
||
|
|
f6f2f69649 |
fix(homepage): uniform 4-column grid, hold the status gutter, unleak AI Systems
The board's card width changed at every group boundary because `columns:` was being tuned per group under the 2026-08-18 "columns = member count" rule. That rule is retired: it sets `lg:grid-cols-N` for one group, so it fixes that group's CARD WIDTH, not its density. Measured on the live board, Notes rendered a single 1464px card, News and Media 728px, Eval & Retrieval 286px, everything else 360px. All 20 groups are now `columns: 4` and every card renders at 360. `.service-name` reserved a 78px status gutter with `padding-right` and relied on `overflow: hidden` to hold it, but overflow clips at the PADDING box — the gutter was spill room the title printed straight through. Six cards on the AI tab rendered their name underneath their own status pill, measured by testing the title text node's box against the status cluster's. The intended ellipsis never fired either: it is painted by whichever block's own line overflows, and that is the anonymous box around the bare title text node, which does not carry `overflow`. The gutter holds by wrapping now, and the description opts back out of it with a negative margin since the pill only ever covers the first line. Scriberr's `homepage.group=AI Systems` named a group absent from `layout:`, so it had no `tab:` and rendered on all four tabs — the same defect as UltraSeedbox in 2026-08-18, arriving this time from a container label. Relabelled to `AI - Audio Tools`, where the other ASR seats already live. The `homepage.group=AI Systems` sample in the repo-root CLAUDE.md was the source of the name and now carries the constraint, plus the fact that a labelled container is discovered from any of the five configured engines and must not also be listed in services.yaml. Also: descriptions clamped to three lines so a five-line outlier stops dragging its row 50px taller than its neighbours; icon ramp overridden off slate-400 -> slate-700, which was sinking the bottom half of every glyph into the card fill; bookmark groups and Jellyfin's trailing stream rows brought into the card and eyebrow vocabulary; group gap 10px -> 22px now that width no longer separates them. The icon override sits on `html[class]`, not `:root`: Homepage sets the same variables on `.theme-slate`, which is on the <html> element, and a class beats `:root` on the same element. Verified with Playwright against the live board — per-group card width, card height spread, and a geometric title-vs-status collision check, before and after. Before/after captures: http://10.100.10.50:8090/b/homepage-relayout/ |
||
|
|
b271db1f44 |
feat(homepage): rebuild the theme on canonical Australis tokens
The predecessor theme was ugly for two structural reasons, not one. It did not use the design system's colours. It built a parallel OKLCH palette "derived from the Australis philosophy" and swapped the canonical typeface for Supreme -- a fork, not a theme. Every hex here is now copied verbatim from ~/.claude/skills/australis-design/colors_and_type.css, and build.py re-checks all 19 against that file at build time and warns on drift so it cannot quietly fork again. Type is the canonical stack: Space Grotesk / Inter / JetBrains Mono, vendored as latin-subset VARIABLE woff2 (one file per family, 102 KB total against 56 KB for three static Supreme cuts, and no Google Fonts request at page load). It also carried a generated full-bleed aurora image behind the entire dashboard. Canon forbids exactly that -- "solid fills only on chrome, no full-bleed photography, no decorative gradients", and the aurora motif "never as a background fill behind text". The predecessor knew, said so in its own header, and dialled the opacity down rather than dropping it. The image is gone; the aurora survives as a 1px accent edge under the tab bar, which is where canon sanctions it. The asset stays in images/ in case it is ever revisited. Direction is instrument panel. Group headings become the Australis mono eyebrow with a hairline to the right edge -- canon calls the eyebrow a system signature, and it turns the groups into register bands instead of headings floating over a grid. Status stops shouting: the filled emerald chips read louder than the service names they annotated, so they are now a semantic dot plus a mono micro-label at tertiary contrast. Cards are bordered and opaque, per canon's border-over-shadow rule for chrome. Alignment, per operator feedback that pills and cards did not line up: - The status cluster is centred on the service name's line rather than parked in the card's top-right corner, where Homepage's `absolute top-0` left it floating ~7px above the title's optical centre. The offsets reconstruct the title line box and are documented as moving together. - Descriptions get a two-line minimum, so the common one-line/two-line mix bottom-aligns across a row. This is what made the grid look ragged. useEqualHeights stays false: it inflated short cards to match a widget card twice their height, which was the worse failure. - The status dot is flex-centred rather than nudged with vertical-align, so it stays centred if the type scale changes. Retires the Skyfall sources and the Supreme faces; theme/ now has one source of truth. |
||
|
|
45c1995d7a |
feat(homepage): Australis Skyfall theme + Arbo-generated aurora background
Replaces the previous theme attempt, which was built on a misread: the ask was to use Arbo as an IMAGE-GEN ENGINE for the background, with the operator's Australis Skyfall design system supplying the palette. theme/ holds the source — colors/layout/typography vendored verbatim from the Skyfall handoff bundle, Supreme 400/500/700 woff2, the Homepage bindings in skyfall.css.in, and build.py which inlines fonts + tokens into conf/custom.css. custom.css is GENERATED; edit the .in file and rebuild. The build exists because Homepage serves only custom.css and custom.js out of its config dir, so a @font-face pointing at a vendored woff2 would 404 — the face has to arrive as a data: URI. The background image takes the other route: /app/public/images is a real static route, so compose.yaml now mounts images/ there read-only and settings.yaml points at /images/. Bindings map Skyfall's semantic layer onto Homepage's DOM: Sea surfaces, the depth recipe (hairline AND two-layer shadow, never one alone), uppercase eyebrow group headers, the sanctioned accent-rail on the active tab rather than a glow, and semantic status colour so a green pill means the service is actually serving. Background generated by Arbo (irv-ml1:8201) workflow t2i-ui-background, job 13f0891f4e42, seed 26, flux2-klein-9b, 2048x1152 — abstract, no subject, cool-temperature aurora. 1.6 MB PNG -> 22 KB WebP. Two deviations are documented rather than hidden: Skyfall forbids imagery behind body text (held at opacity 30 as mitigation), and service icons stay full-colour vendor logos. NOT DEPLOYED — live still runs the old theme. Prototype on :5199. |
||
|
|
42c594c29f |
fix(searxng,seafile): repair wget healthcheck argv, restore seafile after 3-month outage
searxng: the healthcheck passed '--tries' and '--spider' as separate argv entries, so wget consumed '--spider' as the value of '--tries'. Spider mode never engaged and every 30s probe downloaded the response to disk; the container's working directory had accumulated 295,287 healthz.N files since April, and the directory scan to pick the next free filename is what intermittently blew the 10s timeout and flapped the dashboard card to UNHEALTHY. Restored '--tries=1'. The junk was in the writable layer, so the recreate cleared it. Now healthy, fails=0, 200 in 0.16s. seafile: none of the three services declared a restart policy, so Docker defaulted them to 'no'. The daemon stopped all three within 200ms on 2026-05-06 and nothing brought them back — a three-month outage whose only trace was an EXITED card. Exit 255 is what a container ignoring SIGTERM reports when the daemon stops it, not a crash. Added restart: unless-stopped. Stack is back up; mysql gates on its healthcheck as designed and seahub started without the race. 302 -> login page. Both stacks were running unmanaged on ana-docker and are now tracked here. homepage: AI tab reordered by clickability per operator — chat frontends, ComfyUI and the control plane on top; vLLM /docs seats and TTS endpoints below. Corrects the previous commit's UNRESOLVED tab-bar section: it was warm-up time after a recreate, not a defect. |
||
|
|
9d92c4bd21 |
fix(homepage): pin UltraSeedbox to one tab, dedupe Uptime Kuma, size columns to members
UltraSeedbox had no layout: entry, and Homepage renders an untabbed group on every tab — eight full-width bookmark bars repeated four times. Pinned to Main with a row layout. Uptime Kuma rendered twice: a manual services.yaml block under Monitoring plus homepage.group=Apps on the container. Dropped the manual block, moved the label to Monitoring, added homepage.siteMonitor. Adopted the previously unmanaged uptimekuma stack into stacks/ so the label is version-controlled. Column counts declared more columns than groups had members, leaving the last row of several groups mostly empty. Columns now track member counts. Also records an UNRESOLVED regression: since the container was recreated the client render has lost its tab bar, wallpaper and i18n. Ruled out the config changes (committed pre-cleanup config reproduces it) and v2.0.0 (v1.13.2 reproduces it). Server HTML still carries the tab markup, so the loss is client-side. Details in the stack README. |
||
|
|
c5beeac32d |
feat(homepage): bring the fleet dashboard under version control
Homepage on esh-docker-vm:5100 was the one stack whose config lived only on the host, edited in place. Its version history was six hand-rolled services.yaml.bak-* files. Now canonical here and deployed with deploy-stack.sh like everything else; the .bak files are gone. Corrections from the audit: - ANA-Firewall described a 'Fortigate 81F'. It is a FortiGate-80F running FortiOS 7.2.10, verified live against the device. - NH3-Ansible pointed at 10.100.50.42 as an 'Ansible control node'. That host is nh3-extdev, the manager/external-dev successor after nh3-ansible was retired. Renamed and re-described. - Dropped the UltraSeedbox layout group: nothing provides it, so it only ever rendered empty. Adds .env.example and a README documenting the two-path service model (docker label discovery across five engines vs manual entries), the labels-only-apply- on-recreate rule, and the foot-guns found: HOMEPAGE_ALLOWED_HOSTS matches host AND port so a bare IP does not cover IP:port; :2375 is plaintext and unauthenticated on all five engines; ping: cards can only be judged from the dashboard host. Verified after deploy via /api/services: 105 cards across 19 groups, both corrections live, ana-docker discovery intact. |