a85e8452558e31c22ef13585b47d83df723eb568
2
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
a85e845255 |
fix(web): the console stops falling out of its own design system
A pre-release sweep of the management console for two things that no type check
and no diff can catch: primitives that were never @unom/ui's, and animation
that a nested motion parent quietly cancelled.
THE PRESET TILES ALL LANDED ON THE SAME FRAME. @unom/ui's <Section> sets
`delayChildren: stagger(...)`, so a page whose cards are direct descendants of
it staggers for free — which is why every page but one looked right. An
<AnimatedCard> is ALSO a motion element and sets no `delayChildren`, and the
Virtual displays preset tiles are cards nested INSIDE that page's config card,
so that card became their timing group. Measured in a headless browser: the
opacity spread between the first and last tile was 0.00 across the whole
animation (six tiles in lockstep), and is 0.98 now — a ~100 ms cascade matching
the rest of the console. The four hand-rolled copies of the stagger container
collapse into one `<Stagger>` that carries the explanation.
FIVE FILES IMPORTED THE WRONG BUTTON. `@unom/ui/button` exports both a plain
`Button` and the `AnimatedButton` that this console's wrapper re-exports under
the same name — so `import { Button } from "@unom/ui/button"` compiles, renders,
and silently opts out of the mount animation and the hover/tap response.
Displays, SessionGame, GPU, Update and PendingDevices had dead buttons sitting
next to live ones.
THREE PRIMITIVES HAD NO WRAPPER, SO NOBODY REACHED FOR THEM. @unom/ui ships
form/select, form/textarea and form/checkbox; components/ui did not, and the
gap was filled with browser-chrome `<select>`, `<textarea>` and
`<input type="checkbox">` in the add-hook modal and both library forms. Select
needs the same token correction Tabs needed — upstream `text-secondary` is a
text colour, but here `--secondary` is a SURFACE, so the trigger's chevron and
placeholder rendered at near-zero contrast on the card behind them.
The hook timeout also stops accepting a value the host rejects: `min`/`max` on
a controlled `<input type="number">` are decoration (no form validation ever
runs), so 900 went into a field capped at 600 and failed later, at run time.
@unom/ui's InputNumber clamps on blur and lets the field be empty while you
retype instead of snapping to the fallback.
Storybook gains the page that had no story at all — the console's largest
config surface, and the reason this shipped unseen. Its <Card> wrapper is load
bearing: it reproduces the motion nesting that IS the bug.
|
||
|
|
bd383f1820 |
feat(web): one Game sources surface, launcher rail, and the migration nudge
M4 of design/library-scanner-plugins-implementation-plan.md, plus WP6.2. WP4.1 — SourceToggles and ProvidersCard merge into Library/Sources.tsx. They were two cards because they were two different things: scanners were compiled into the host, plugins were an afterthought. After the extraction they are the same thing — the host reports ONE list of sources whose ids match whether they came from a built-in scanner or the plugin replacing it — so one surface is both simpler and the only honest presentation. Each row carries its toggle, a running/stopped badge for plugin sources, an entry count, filter, settings and an uninstall that offers to remove the games too. An "Add a source" rail lists uncatalogued library plugins with a "Detected" badge; `detected` is deliberately tri-state, so only a POSITIVE probe badges — an entry with no probes for this platform is unknown, and calling that "not installed" would be a lie. The settings drawer (SourceSettings.tsx) renders a generic form from the plugin's own JSON Schema over GET/PUT /__config, through the existing session-gated /plugin-ui/<id>/ proxy — zero new host surface, and the browser never learns the plugin's port or secret. It flattens allOf branches (effect nests a checked schema's annotations there, so a form reading only the top level silently loses every title and default) and falls back to a JSON editor when any field is a shape it cannot express — partial rendering would be worse than none, because a field missing from the form is a setting the operator cannot change. WP4.2 — uiPlugins() now excludes category "library", which covers both the sidebar and the mobile overflow since they share the selector. The /plugins/$pluginId/$ route still resolves, so existing deep links keep working; library plugins are just not advertised. WP4.3 — LibraryGrid groups role:"launcher" entries into a rail above the grid, and the empty state points at the sources surface rather than leaving a bare grid (after extraction, "no games" is the expected first-run state). WP6.2 — a migration banner offering one install per still-built-in scanner whose plugin is catalogued. One button per scanner, never a single "migrate everything" and never a silent auto-install: installing code stays an explicit operator act, and per-scanner is what makes it safe to repeat (the claim suppresses the built-in idempotently, so a half-finished migration is a valid state). WP4.4 — i18n en+de (kept under the existing "Game sources" label rather than minting a third "Plugins"), Storybook stories for the sources card in three states, the launcher rail and the banner. Gates: orval regen, tsc clean, vite build clean, check-i18n green at 595 messages for both locales. Still owed: the browser click-through (the store's Tabs-theme bug shipped through green types and lint), and an AppShell nav story — that one needs the plugins query mocked, which does not exist in this Storybook setup yet. |