29 Commits

Author SHA1 Message Date
librelad
2510b1a52f feat(webui): green Upgrade, trailing picker, select-all, icon in confirms
Four fixes to the Updates tab, all reported from the same screenshot.

Upgrade is green and just says "Upgrade". It was accent-styled and carried the
target version, which duplicated the confirmation it opens — the dialog already
names the version, so the button only had to name the action. Green also stops
it reading as the same control as the cyan Update beside it.

The picker moved from the head of the row to the tail, matching the Tasks list.
It used to lead with an `ov-pick-empty` spacer holding the column open, so with
nothing updatable every row carried a left gutter for a checkbox that was never
coming — the gap in the report. Unselectable rows now render no picker at all.

Select all joins the toolbar, right-aligned above that column, again mirroring
Tasks. It acts on the selectable rows in view, so a narrowing filter chip cannot
leave it quietly picking rows nobody can see.

The confirmation dialog takes an optional icon and the upgrade flow passes the
app's, so it leads with the same icon-in-holder the instance modals use. Styled
in base.css rather than borrowing .app-card-icon: this dialog is global and
appears on pages that never load the apps stylesheet. Its title now uses the
display name too — "Upgrade matrix to…" beside the Matrix logo read as a
different thing.

Verified against the live WebUI: green Upgrade with the gutter gone; Select all
and the trailing checkboxes with two apps temporarily flagged (data restored
byte-identical after); the master toggling 0 -> 2 -> 0; and the dialog showing
matrix.svg under the title "Upgrade Matrix to v1.159.0?".
2026-08-20 22:47:56 +01:00
librelad
1b3d68d9f7 Highlight the hovered app row on the Updates tab
The row head is the click target that expands a row and already set
cursor:pointer, but nothing changed under the pointer — so a clickable row
looked identical to a static one right up until it opened, and the only visual
state was post-click.

The tint is painted by the row rather than the head. .ov-row's `padding: 0`
loses to .updater-row's `padding: 12px 15px` on equal specificity, so the head
is only 34px tall inside a 60px row; filling the head left a floating band with
13px of untinted row above and below it. Painting the row covers the full
height and picks up its 11px radius for free.

Keyed on `:has(> .ov-row-head:hover)` rather than `.ov-row:hover` so the
highlight tracks what is actually clickable: on an expanded row, drifting into
the detail body stops indicating a click target, because there isn't one there.
The head-level rule stays as the fallback for engines without :has() — it still
marks the row, just inset — and the pair is mutually exclusive so the two tints
never stack. :has() is already relied on elsewhere in the WebUI CSS.

Verified against the running WebUI by driving Chrome over CDP and dispatching a
real mouseMoved, since lp-shot cannot hover: the hovered row goes from
rgba(255,255,255,.035) to .086, its neighbour stays at .035, and the head fill
reads transparent so nothing double-tints. Confirmed visually in the capture
too. ov-row-head is emitted only by renderUpdates(), so no other tab changes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-19 21:28:30 +01:00
librelad
cbbb978e50 feat(webui): make Overview board rows clickable end to end
Each action-board row on /apps/overview already carries a navigation
button (Review / View / Open Backups). The whole row now triggers that
same navigation, so the small button is no longer the only hit target.

Rows opt in via a `nav` descriptor, so only plain go-to-that-tab rows
become clickable — rows with no action, and the "Update all" button
inside the updates row, are unaffected (updater actions are matched
first in the delegated handler, so Update all never double-fires).

The row is not given role="button": nesting the real buttons inside a
button role would break them for assistive tech. The inner button stays
the focusable, keyboard-reachable control; the row is a mouse-only
widening, with a hover state that lifts row and button together. A
whole-row click is suppressed when it ends a text selection.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-18 21:18:45 +01:00
librelad
1529776d5d feat(updater): mirror auto-check line to fleet Updates tab; in-banner Check-now
Extends the per-app Updates treatment to the fleet App Center → Overview →
Updates list, and folds the manual check into the status line so it's the one
canonical (secondary) affordance rather than a top-level tab button:

- updater-page.js: renderAutoCheckLine() now ends with a right-aligned "↻ Check
  now" button (data-updater-action="check" — both surfaces already wire it).
- overview-manager.js: drop the "Check"/"Check now" button from the Updates tab
  header (keep "Update all", only when updates exist); lead renderUpdates() with
  the auto-check line.
- overview.css: .updater-autocheck wraps on narrow widths; .updater-autocheck-btn
  sits right (margin-left:auto), smaller.
- app-tabbed-manager.js: friendlier no-data copy ("You're all caught up — no
  updates found for this app.").

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-18 23:10:49 +01:00
librelad
1de4d5d970 feat(updater): auto-check status line on the app Updates tab, drop manual Check
The per-app Updates tab pushed a manual "Check" button (header + empty state)
even though scans run automatically on CFG_UPDATER_SCAN_INTERVAL — so an app with
nothing to update read like an empty/actionable page. Replace the manual Check
with a calm status line inside the panel: "Checked automatically · last checked X
· next check ~Y", backup-schedule style. The genuine Apply/Roll back actions stay
(applying is still manual and safe). No auto-apply.

- webui_updater_scan.sh: stamp scan_interval_minutes alongside generated_at in
  updates.json so the display needs no separate config fetch (0 = auto off).
- updater-page.js: renderAutoCheckLine() + fmtRelFuture().
- app-tabbed-manager.js: drop the header/empty-state Check buttons; render the
  auto-check line; friendlier no-data copy.
- overview.css: style .updater-autocheck (green dot live / muted when off).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-18 22:38:05 +01:00
librelad
27797ea119 style(overview): tighten Updates row height (~half)
Reduce .ov-row-head vertical padding (12px -> 5px) and shrink the app icon
tile (32px -> 24px) so each Updates row is roughly half as tall.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-18 20:22:09 +01:00
librelad
8adcb140e5 feat(overview): tasks-style app rows on the Updates tab
Redesign the Fleet Overview Updates list rows to match the Tasks page:
each app now shows its icon tile, a status pill ('✓ Up to date' /
'↑ Update available' / '• Unscanned'), and a dedicated 'Details' toggle
button (chevron) in the row actions instead of the bare leading chevron.
Resolve the app icon from the slug (/core/icons/apps/<slug>.svg, hidden on
error) and prefer window.getAppDisplayName for a prettier title.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-18 20:18:26 +01:00
librelad
f4d34a0383 fix(updater): give the app Updates detail panel even inner padding
The per-app Updates tab's recessed panel used padding: 4px 16px, so the first
section (VERSION) sat 4px from the top edge — looking unpadded — while the last
section ended 20px from the bottom. Match the sibling .backup-snapshots-container
idiom (uniform 16px) and zero the first/last sections' outer padding so the
container's inset sets an even top/bottom margin.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-18 19:59:58 +01:00
librelad
d794d4031a fix(overview): raise specificity so full-width detail actually applies
The prior .ov-row override tied .updater-row on specificity, and updater.css
loads later, so `display:grid` won and the detail stayed boxed. Use the
two-class selector .updater-row.ov-row to win regardless of load order.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-16 17:39:40 +01:00
librelad
97af9474cc fix(overview): expanded app detail spans full row width
The fleet Updates expander row inherited .updater-row's 3-column grid, so an
open row's detail body (CVEs, recovery, history) landed in a narrow right-hand
`auto` column with dead space to its left. Make .ov-row a block: the head stacks
on top and the detail body uses the full width.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-16 17:35:28 +01:00
librelad
b17ecdc192 fix(webui/overview): restore inner padding on the recessed tab body
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-07-04 21:34:05 +01:00
librelad
31ce56c022 fix(webui/overview): keep the recessed tab-body surface, only drop its inset
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-07-04 21:30:22 +01:00
librelad
baa1ed4d8d fix(webui/overview): uniform tab rhythm + rounded sub-tab strip corners
- .tabs-list now rounds its own top corners (12px, matching .tab-navigation);
  the first/last .tab-button radii were invisible against the bar's square
  background on the Backups/Migrate/Config sub-tab strips.
- All five fleet tabs share one rhythm under the header divider: a single
  16px gap, no extra inset — .ov-tab-body drops its recessed margin/padding
  box, and the Backups/Migrate sub-tab strips gain the matching gap.
- The Peers list keeps its recessed panel via .peers-body (in-card recipe),
  no longer riding the flattened .ov-tab-body.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-07-04 21:24:42 +01:00
librelad
aef0c15726 refactor(webui/backups): backup center sub-tabs use canonical tabs-wrapper
The embedded backup center's fragment kept its standalone-page skeleton
(.container + .sidebar restyled by a pile of overview.css overrides).
.container's fixed viewport height inflated the Backups tab pane to
~100vh, stretching the pane surface far past the content and under the
footer buttons, and the restyled strip never matched the app-detail
Config sub-tabs.

Rebuild backup-content.html on the canonical sub-tab idiom instead —
.tabs-wrapper > .tabs-list (emoji tab-buttons) + .tabs-content card,
with a .backup-actions footer below the card mirroring .config-actions.
The bespoke overview.css restyle block, the nebula special-cases, the
embed's id-stripping and BackupPage's dead page-header updater all fall
away; the export menu now opens upward from the footer.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-07-04 21:08:03 +01:00
librelad
f4c24340b7 fix(webui): lighten faint empty-state messages on dark panels
Empty-state 'no data yet' messages rendered at rgba(text, 0.55) (and one at
text-muted), which is hard to read on the dark recessed panels. Switch them to
the theme's --text-secondary muted token so they stay de-emphasized but legible.

Covers .updater-empty, .updater-detail-empty, .sys-detail-empty, .eo-modal-empty.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-06-25 21:07:07 +01:00
librelad
bbf2f9a5f4 style(overview): match sidebar Overview entry to app-category rows
The pinned 'Overview' sidebar entry used a rounded, inset pill (margin +
border-radius:8px) so its hover/active highlight floated in the middle of the
sidebar, unlike the full-width app-category rows below it. Drop the margin and
radius, adopt the .category padding (15px 20px), border-bottom separator and
var(--surface-hover) hover, so the highlight spans the full sidebar width with
square corners and matches the categories. Keeps font-weight 600 + the
page-updater active tint as its only distinguishing marks.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-06-18 15:32:46 +01:00
librelad
c02202d620 fix(webui): stop Backups-tab card bg from running past the footer buttons
On /overview Backups the card surface lived on .main, which wraps both the
body and the flipped footer — so the background overhung past the action
buttons. Move the card surface onto .backup-page-body (rounded bottom,
joined to the tab strip) and let the footer sit transparent below it,
matching the app Config tab's .tabs-content + .config-actions split.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-06-17 18:45:56 +01:00
librelad
7db2a707b2 refactor(overview): turn the fleet Overview tab into an action board
Replace the stat-tile grid with a needs-attention board: a health hero on
top (one big status circle — green all-clear / amber something-to-do /
neutral pre-scan — matching the admin dashboard's dot language) over one
status row per area (updates, security, improvements, backups). Rows that
want a decision are tinted with their area hue and carry their action
buttons inline (Review / Update all / Open Backups); healthy areas
collapse to a quiet neutral one-liner. An Everything / Needs action chip
pair filters the board down to just the actionable rows.

Board rows deep-link with intent: Security lands on the Updates tab
pre-filtered to the affected apps via a new data-filter hop on the goto
action. backupSummary() now splits never-backed-up from week-stale apps
so the backup row can say which it is.

Signed-off-by: librelad <librelad@digitalangels.vip>
2026-06-12 22:33:23 +01:00
librelad
3afe40bbbc refactor(overview): de-chrome the embedded Backups + Peers sub-tabs
The Backups tab's embedded BackupPage repeated the active section as a
big page header (icon + 'Dashboard' + subtitle) right under the nested
strip that already names it. Embedded-scoped CSS now hides the title
block and flips the header below the body (flex order), so its actions
(Refresh + per-section primary) become a bottom-left footer row — the
same place app-detail tabs keep theirs. The export dropdown flips to
open upward from the footer. The standalone /backup page is untouched.

The Migrate ▸ Peers sub-tab drops its page header (breadcrumb + title
+ blurb) the same way: the peer list/empty state now sit in the shared
recessed .ov-tab-body container with the four actions in a bottom-left
.peers-actions footer.

Signed-off-by: librelad <librelad@digitalangels.vip>

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-12 19:41:35 +01:00
librelad
a06b6cd1d8 feat(overview): match fleet tab content to the app-detail tab layout
Every fleet Overview tab now follows the per-app detail tab idiom the
rest of the app uses: title + description on the left, action buttons
on the right, a divider underneath, and the body inside the recessed
dark container (.tasks-container recipe).

- renderHeader() gains an action slot; Check/Check now/Update all move
  out of in-body toolbars into the header (Updates keeps its filter
  chips in the body; the Apps-tracked stat card drops its duplicate
  Check button; UpdaterPage.renderImprovements can skip its toolbar).
- String tabs wrap their body in .ov-tab-body — margin/padding 16px,
  rgba(bg,.2) panel — mirroring backup/tasks/updater containers.
- The Backups tab's embedded nested strip (Dashboard/Backups/Locations/
  Configuration) now sits on the same surface as every other tab strip:
  added to the nebula sidebar-bg anchor rule (it was stuck on the
  lighter --hover-bg) and its buttons use .main-tab-button type.

Signed-off-by: librelad <librelad@digitalangels.vip>

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-12 18:55:10 +01:00
librelad
8006ddba75 fix(webui): give the app-detail Updates tab the standard tab chrome
The per-app Updates tab rendered a bare version/badge bar + detail with no
title, no dividers and no recessed container — unlike the Config / Backups /
Tasks tabs.

- Add a .updater-title block (⬆️ Updates + description, Check/Update actions,
  bottom-border divider) mirroring .backup-title.
- Wrap the body in a .updater-detail-container recessed dark panel (same recipe
  as .backup-snapshots-container / .tasks-container).
- Separate the Version/Security/Recovery/History sections with divider lines
  (scoped to the container; fleet row-details keep their gap-only spacing).
- renderAppDetail() gains an opt-in Version section so the version/badge reads
  as a section in the panel; fleet rows omit it (the row head shows it already).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 00:56:01 +01:00
librelad
25dc51d63e fix(webui): make Overview sub-tab areas match the app-detail layout
Migrate and Backups detached their sub-tab strip from the content (16px gap)
and Backups had no top-level header and a transparent (card-less) body — so
both read as different from the per-app Config sub-tabs.

- Migrate: nest .tabs-content inside the same .tabs-wrapper as .tabs-list (the
  canonical app-detail structure) and drop the gap, so the strip joins the
  content card with no space and clean corners.
- Backups: inject the shared "Backups" .config-title header above the embedded
  BackupPage; drop the sub-tab strip's bottom gap; give the content (.main) the
  connected .tabs-content surface (card bg + rounded bottom) and round the
  strip's outer top corners — so it reads as one tabs-within-tabs unit.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 00:23:10 +01:00
librelad
4d54d6a9b0 feat(webui): unify Overview tab headers as in-content, app-detail style
Drop the floating fleet header; every Overview tab now renders its heading
inside the pane, under the tab strip, in the per-app detail .config-title
format (emoji + title + description) — matching the Migrate tab.

Introduces a small modular system so the area can't drift:
- OV_TAB_META is the single source of truth for each tab's icon/title/blurb.
- renderHeader(id) is the only thing that turns it into markup; renderTab()
  prepends it for the string tabs and mountMigrate() injects it once for the
  static Migrate pane. Body renderers now only ever produce the body.

Retires the now-dead floating-header plumbing: updateHeader(), the
ov-backups-active/ov-migrate-active hide toggles + CSS, and the .overview-header
rules. The tabbed interface owns the top padding the header used to provide.

Backups is the documented exception — it embeds the full BackupPage, which
supplies its own header.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-02 23:29:16 +01:00
librelad
f6f29bf68b feat(webui): match Migrate tab to app-detail tab design
Give the fleet Overview › Migrate tab the same in-content header + sub-tab
styling the per-app detail tabs use:

- Add a .config-title header (icon + title + description) inside the Migrate
  pane, above the Restore/Peers sub-tabs, and hide the generic floating fleet
  header for Migrate (mirrors how Backups already supplies its own heading).
- Make the .tab-button icon↔label gap explicit (flex + 6px gap) so sub-tabs
  render identically whether or not the markup has whitespace between the
  emoji and name spans; align the Backups sub-tab strip gap to match.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-02 23:04:44 +01:00
librelad
69bb5532b7 refactor(webui): move Peers out of Admin; harmonize Backups sub-tab strip
- Remove the Admin sidebar Peers entry; /peers and /admin/tools/peers now
  redirect to /overview/migrate/peers (its new home next to cross-host Restore).
- Re-skin the embedded Backups center's sub-tab strip from pills to the per-app
  Config .tabs-list/.tab-button segmented look (full-width bar, accent underline)
  so every nested sub-tab row is consistent.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-01 10:46:30 +01:00
librelad
4a964c42a2 feat(webui): add Migrate fleet tab (Restore + Peers sub-tabs)
New 5th Overview tab 'Migrate' with a nested segmented sub-tab row reusing the
per-app Config-tab .tabs-list/.tab-button design:
- Restore: a standalone MigratePage (cross-host migrate moved out of BackupPage
  into its own controller + fragment + modal; own data fetch + task dispatch).
- Peers: reuses the existing PeersPage (container-parameterized) + its template.
Both lazy-loaded on first open and disposed on apps-feature unmount. Additive —
migrate is still in the backup center and Peers still in Admin until the next
commits remove the duplicates.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-01 10:24:54 +01:00
librelad
c508a20605 feat(webui): embed the full backup center in the Overview Backups tab
Instead of a glance + 'Open backup center' button, the Backups tab now mounts
the real BackupPage (dashboard/snapshots/locations/migrate/configuration) inline,
with its sidebar restyled as a nested tab strip and its own header taking over.

- BackupPage gains an embedded mode (opts.embedded): no /backup URL coupling, so
  sub-tabs switch in-page under /overview/backups. Backward compatible.
- OverviewManager lazy-loads the backup bundle + fragment on first open, news a
  BackupPage({embedded:true}), and disposes it on apps-feature unmount. Colliding
  ids (#sidebar/#mobile-overlay) are stripped on inject.
- Revert the Admin backup-config surface — the embedded center (incl.
  Configuration) is now the single home for backup settings.
- The updater needs no equivalent: its sections were already unpacked into the
  Overview/Updates/Improvements tabs + the per-row expander.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-01 01:36:25 +01:00
librelad
1460acb941 feat(webui): add per-app Updates tab (version/CVEs/recovery/history)
New 'Updates' tab in the app detail page, beside Backups. Reuses the headless
UpdaterPage + renderAppDetail() scoped to the single app, so the per-app and
fleet views share one data/render path. UpdaterPage is added to the apps script
bundle so it's available on app pages; the tab is disabled while a task runs.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-31 23:54:21 +01:00
librelad
8acf2d02c3 feat(webui): add fleet Overview area (Overview/Updates/Improvements/Backups tabs)
Introduce a per-fleet Overview area inside the apps shell, reachable from a new
'Overview' entry pinned above the apps sidebar search. Selecting it renders a
top-tabbed view in the main pane — Overview · Updates · Improvements · Backups —
mirroring the per-app tabbed layout, with the apps sidebar persistent.

- TabController: generic root-scoped show/hide tab host (core/ui-state).
- OverviewManager: drives the 4 tabs. Reuses a headless UpdaterPage for all
  update/CVE/improvement data + rendering (its renderX() are pure HTML
  producers) and reads backup/dashboard.json directly for backup health.
- Overview tab: combined update + backup health cards.
- Updates tab: per-app expander table (CVEs/recovery/history inline via the new
  UpdaterPage.renderAppDetail) + All/Updates/Security filter chips.
- Improvements tab: reuses the updater's signed-hotfix renderer.
- Backups tab: fleet backup-health tiles; actions deep-link per app.
- Additive only: /overview* routes on the apps feature; old /updater and
  /backup pages untouched. Cleanup (redirects, nav, Admin config move) is next.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-31 23:37:35 +01:00