feat: nav badges for modules; log pane follows its stream #75
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/nav-badges"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Problem
logTextpane on the web stayed at the top during an apt run. You had to scroll by hand to see new output.Commits
c920d9cfeat: a module can put a count on its nav entrycf98e1dfix(shell): a log pane follows its stream and stays at the bottomNav badges
core:badges. The snapshot is{ [module]: badge }and each event is{ module, badge | null }. The sidebar needs no REST call and no polling.moduleVisiblerule asGET /modules, soLiveViewernow carries the session'sroles.register()is held until the module has finished loading, then sent.info(accent),warning(yellow with dark text) andcritical.criticalclass with white text.docs/modules.mdhas a new section, "Navigation badges".Log pane
The web renderer ignored
logText.follow, so the pane only changed when the source was re-fetched, and each re-fetch scrolled it back to the top.LogTextBlocknow subscribes to the follow topic and appends{chunk}events to the fetched text. A new fetch replaces the appended text, because it already contains it.Verification
deno task check,check:svelte(0 errors, no warnings in the changed files),deno lintanddeno fmt --checkare clean on the changed paths.deno task test packages/server/tests/: 191 passed. This includes the newbadges_test.ts, which covers visibility per role, no re-send of an unchanged badge, 0 clearing a badge, and malformed counts.OPSDECK_FAKE_DATA=1: the updates module set a dummy badge and the sidebar showed a yellow, bold 29. Activity's pill is bold.Not in here
core:badges.Downstream
OpsDeck/module_updates
feat/nav-badgecallsctx.ui.badge. Merge and deploy this PR first. On a core withoutctx.ui.badge, that module's page fails to load.🤖 Generated with Claude Code
`ctx.ui.badge({ count, tone, title })` shows a pill next to the module's sidebar entry, the way Activity already shows failed and live runs. The first user is the updates module (pending packages), but anything with a state worth noticing from another page needs the same thing, so it is a core capability rather than a special case in the shell. It is a state, not an event: the latest call wins, null or 0 clears, and an unchanged badge is not re-sent, so a collector can call it every tick. Badges travel on the retained topic `core:badges` so the sidebar needs no REST baseline and no poll. Snapshot and deltas are projected per viewer against the same `moduleVisible` rule `/modules` uses, which is why `LiveViewer` now carries the session's roles: `isAdmin` alone cannot answer `nav.role: "viewer"`. A badge set during `register()` is held until the module is in `loaded` and announced then; before that no viewer may see the module and the delta would be dropped. Tones are info (accent), warning and critical. Every sidebar count is now bold, Activity's included, and Activity's red pill uses the shared `critical` class with white ink — the status ink tokens are text-safe on the page, not on their own fill.The web renderer ignored `logText.follow` entirely, so a module's live output (the updates module's apt run) only changed when something re-fetched the source, and every re-fetch put the pane back at the top of a few thousand lines. The block now subscribes to the follow topic and appends `{chunk}` events to the fetched snapshot; a new snapshot already contains what was streamed, so it resets the appended tail, and a resync re-fetches. The pane scrolls to the newest line on every change while the reader is within a line of the bottom, and stops following once they scroll up.