Files
enricobuehler 141c04cd6b
ci / bun-nix (pull_request) Successful in 29s
ci / web (pull_request) Successful in 1m8s
ci / docs-site (pull_request) Successful in 2m39s
ci / rust-arm64 (pull_request) Successful in 3m16s
ci / rust (pull_request) Successful in 7m55s
feat(web-console): "Update all" on the plugins screen
The Installed tab could only update one plugin at a time, one dialog and one
watched job each. This adds the bulk action beside the list it acts on — the
same place Sources keeps "Refresh all" — plus a count badge on the Installed
tab trigger, because Browse is the tab the page opens on and a control nobody
passes is a control nobody finds.

The host takes ONE package operation at a time (409 otherwise: bun operations
share a lockfile and a node_modules tree), so this is a queue the console works
through job by job, driven by each job settling rather than by a timer. The run
carries its own copy of what is left: every finished install invalidates the
installed list, and a queue that re-derived itself would change shape underneath
a run the operator already confirmed.

Trust rules are unchanged, only taken once instead of N times. If any entry in
the run comes from an operator-added source the whole dialog wears the external
treatment and names those catalogs — a bulk action must not be a way to wave
through, in one click, a warning each package would have shown on its own. The
dialog lists every version change rather than a count, and names what it will
not attempt: an update with no catalog entry, or one this host would refuse
(400 on incompatible, blocked entries) never enters the queue, so the button's
count still adds up on screen.

A failure ends the run. The failed job's card is the only record of what went
wrong, and starting the next install would replace it with a fresh spinner; the
toast says what was applied and what was not, and the rows are still there to
retry from.

Also fixed, because this change leans on it: disabled buttons were invisible.
AnimatedButton is a motion element and its mount animation settles as an inline
`opacity: 1`, which outranks the `disabled:opacity-50` class the library also
ships — measured `opacity: 1` on a disabled button, console-wide. Only
`pointer-events: none` landed, so every disabled control in the app looked live
and silently ignored the click. Corrected in the components/ui wrapper layer
like the other @unom/ui adaptations.

Verified: tsc, biome, `bun test server/`, production build, i18n check (650
messages, en + de). Storybook stories added for the list header and the confirm
dialog; both rendered headless in light and dark, with the disabled states
asserted on the DOM rather than by eye.
2026-08-13 16:43:05 +02:00
..