28 Commits

Author SHA1 Message Date
librelad
7334557706 feat(updater): detect newer release lines, not just newer builds
The digest compare only ever asks about the tag already pinned, so it
answers "has my tag been rebuilt?" and can never answer "does a newer
version exist?". An app on v0.16 reports up to date forever while 0.17
ships. That is the gap between an app that updates and an app that is
current, and it silently affects every pinned app.

Adds tag enumeration for VERSIONED tags only (rolling tags already move
on their own): list the repo's tags, keep those sharing the current tag's
SHAPE, and pick the numerically greatest.

Shape matching is the whole safety story — v0.16 -> v#.# so it can never
"upgrade" you onto v0.16-alpine, 31-fpm-alpine onto 31-apache, or a date
tag onto a semver one. Comparison is component-wise numeric, so 0.10 > 0.9
and 1.0 > 0.99 (a string sort gets both wrong), with 10# forcing base ten
so an upstream "08" cannot be read as octal. 15 unit tests cover it.

Docker Hub only, deliberately: all three pinned apps live there, it needs
no auth, and the generic OCI tags/list wants a per-registry token dance.
Other registries stay quiet rather than guess. Throttled inside the
existing registry window and cached between windows so it cannot flicker.

Surfaced as INFORMATION, never an action: no button applies it, because a
version move can carry a data migration. `update_available` and the "up
to date" badge keep their exact meaning; the new state sits beside them
and points at the Version field.

Against the live registry: stalwart v0.16 is current, nextcloud is on
31-fpm-alpine with 34-fpm-alpine out, mastodon on v4.2.0 with v4.6.5 out.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-12 23:30:08 +01:00
librelad
66c79f997e feat(updater): install window, honest Check-now, failed-auto surfacing
Four fixes that make the auto-updater a trustworthy background system:

* CFG_UPDATER_WINDOW (default 06:00-08:00 host time, right after the
  05:00 backup cron; HH:MM-HH:MM wraps midnight, 'always' = any time).
  Gates only the enqueue — scans keep running all day, so the Updates
  page stays current and pending updates visibly wait for the window.
  Malformed values fail closed and are rejected by the WebUI validator.

* "Check now" actually checks: an explicit `updater check` sets
  UPDATER_REGISTRY_FORCE=1. The flag existed but nothing ever set it,
  so the button silently reused the 6h digest cache and could not find
  a build the user knew had shipped. Force also overrides interval 0,
  which now means "manual-only" as documented in the roadmap.

* Registry stamp moved from /tmp to <system>/logs: the task processor
  runs under PrivateTmp, so daemon and CLI each kept a separate 6h
  clock and the daemon's reset on every service restart.

* A failed automatic attempt is no longer invisible: the scan emits
  auto_attempted_digest (the one-shot no-retry stamp), and when it
  matches the available build the UI stops promising an install that
  will never come — per-app detail explains, the fleet row gets an
  "auto failed" chip, and the Overview board counts it as needing you.

Also corrects the CFG_TIMEZONE label: it sets the containers' TZ only;
scheduled tasks follow the host clock (timedatectl), and the old
"Timezone for scheduled tasks" wording promised a knob that never
existed. The window + auto_window display state plainly WHEN updates
land, answering "how does the user know when the next update happens".

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-11 21:06:27 +01:00
librelad
cdeb2d1658 feat(updater): per-app UPDATE_TYPE, automatic by default
Adds the decision half of the app updater. Detection (P2) and the
snapshot-first apply/revert (P3) were already real, but nothing ever
pressed the button — every update waited for a click.

  CFG_<APP>_UPDATE_TYPE=auto|manual   per app, default auto (33 templates)
  CFG_UPDATER_AUTO=true|false         master switch, default true

updaterAppPolicy resolves the two the way backupResolveStrategy already
resolves backup strategy: the global switch can only make things more
manual. updaterApplyAuto runs at the end of `updater check` and enqueues
the ordinary updater_apply task for each auto app that has an update —
never applies inline, so an automatic update is the same code path, task
log, History entry and Roll back button as a manual one.

Safety: each attempt stamps its target digest under generated/auto/, so a
build that fails is rolled back and then left alone rather than retried on
every scan; in-flight updater tasks are skipped so scans can't stack.

Tracked end to end: updates.json carries each app's resolved update_type,
History entries carry trigger=manual|auto. The WebUI says whether updates
install themselves, chips only the apps that opted out, labels automatic
history, and — since an auto app's pending update needs no decision — keeps
it off the Overview board's "Needs action" view.

Also fixes artifactApplyAuto enqueueing without --detach: it runs inside
the single-threaded task processor's own poll, so following the new task in
the foreground waits for a task that cannot start until it returns.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-11 16:22:04 +01:00
librelad
d72f84a9cf fix(updater): honest CVE framing — key actionability on a real update
The Security triage implied 'fix available · update the app to clear these'
even when the app was already on the newest image — where updating does
nothing and the patch only lands when the maintainer rebuilds. That read as a
false to-do. Reframe honestly:

- Status line keyed on update_available, not Trivy's fixed_in: up to date =>
  'nothing to apply; clears when the maintainer ships a rebuilt image'; update
  available => 'updating may pull in patched packages'.
- Groups relabeled to describe reality: 'Patch released upstream' (hint: lands
  on rebuild / may be cleared by updating) and 'No patch yet'. Counts go
  neutral so a big number doesn't read as either alarm or all-clear.
- Row 'no fix yet' -> 'no patch'; use getAppDisplayName so it's 'Trivy' not
  'trivy'.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-18 23:34:20 +01:00
librelad
6b93554040 Merge claude/1 2026-07-18 23:16:35 +01:00
librelad
c7c1601f59 feat(updater): triage the Security CVE list into actionable groups
A user rightly noted the Security section read as a wall of unrelated
dependency CVEs against an 'Up to date' app — no cue for what, if anything,
to do. Make it answer 'is this my problem, and will updating fix it?':

- Scanner (trivy_scan.sh): stop discarding Trivy's Class/Type/Status at the
  jq flatten — bind them onto each vuln so the UI can tell an OS package from
  the app's own bundled dependency, and a real fix from a won't-fix.
- Security section (updater-page.js): explain these are vulnerabilities in the
  packages bundled in the image (not the app version), tally 'N with a fix ·
  M no fix yet', then split the list into a 'Fix available' group (worst-first,
  each row tagged OS/dependency and showing installed -> fixed) and a dimmed
  'No fix yet' group. No fabricated 'this update fixes N' claim — fixed_in vs
  the image tag isn't a reliable join, so we only state fix availability.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-18 23:16:35 +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
7d4681b9a3 style(updater): give the Security CVE list a dark rounded container
The Security section in the per-app detail panel dropped the CVE list flush
against the panel edges (no side padding, no visible frame). Wrap it in an
inset dark rounded .updater-cve-box (side padding + border) so it reads as a
contained block matching the app rows, and make the scroll thumb more present
(wider, higher-contrast, padded track) so a long list clearly scrolls.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-18 21:38:57 +01:00
librelad
7ab60a801d feat(updater): scrollable, worst-first CVE lists so long lists stay compact
An app with many CVEs (e.g. Trivy's 28) rendered every row full-height, pushing
the page down. Extract one renderCveList() shared by the standalone Security tab
and the per-app expander: sort worst-severity-first, and once past ~6 rows cap
the height (260px) with an internal scroll + themed scrollbar and a bottom fade
hint. Add a count pill to the expander's "Security" heading. Per-app sections
stay stacked and independently collapsible as before.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-16 17:28:07 +01:00
librelad
abd8e0b68b feat(updater): surface Trivy CVE scanner state (installed / DB updating / ready)
Trivy runs as a server whose vulnerability DB downloads on first boot; until it
lands no scan can produce results. Previously the updater generator wrote an
empty-but-valid cves.json the moment the file was missing, so installing Trivy
painted a green "no known vulnerabilities" all-clear that was actually a lie —
the DB hadn't even downloaded, and the Updates/Security view gave no signal.

Add an honest scanner state the WebUI branches on:
- containers/trivy/scripts/trivy_scan.sh — trivyScannerState (absent |
  db_updating | ready) via `trivy version -f json`, trivyDbUpdatedAt, and
  trivyScanImageCves (per-image scan normalized to {id,severity,package,
  installed,fixed_in,url}, deduped). All degrade safely on error.
- webui_updater_scan.sh — stamp cves.json with scanner.state; only run real
  per-image scans once the DB is ready. Always rewritten so state tracks live.
- updater-page.js — Security tab shows a loading box while the DB updates, an
  install nudge when absent, and the genuine 🎉 only when ready+empty; Overview
  CVE card sub + hint reflect the state.
- overview-manager.js — fleet Security row surfaces the "building CVE database"
  pending state instead of silently omitting.
- function_manifest.sh — regenerated for the new trivy_scan.sh functions.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-16 16:47:07 +01:00
librelad
cc026d4d68 fix(webui/updater): correct misleading improvements empty-state copy
'Checking for improvements… this usually takes a minute.' implied an active
scan finishing imminently. The scan is periodic and runs in the background
(CFG_UPDATER_SCAN_INTERVAL, default 30m), so reword to reflect that. Kept the
cadence generic since the interval is admin-configurable and not exposed here.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-06-25 21:25:38 +01:00
librelad
f1d22b057a fix(webui/updater): friendlier Updates empty-state copy
'No installed apps to track.' was terse and gave no next step. Point the user
to the App Center so the empty Updates tab is actionable.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-06-25 21:23:24 +01:00
librelad
754864571c fix(webui/updater): friendlier, shorter improvements empty-state copy
'No hotfix data yet — the automatic scan fetches the signed improvements index
within a couple of minutes.' leaked jargon (hotfix data, signed index) onto the
interface. Replace with a short, plain 'Checking for improvements…' line.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-06-25 21:17:31 +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
0bcde854e6 refactor(webui): move fleet Overview under /apps/overview; retire standalone /backup
The fleet Overview area (Overview/Updates/Improvements/Backups/Migrate) now
lives at /apps/overview* instead of /overview*, reflecting that it belongs to
App Center. The Backups tab is therefore /apps/overview/backups, and the old
standalone Backup Center page is removed entirely:

- apps feature owns /apps/overview* (covered by the existing /apps* route); its
  mount() dispatches /apps/overview -> fleet Overview before the grid check.
- _legacyRedirect() rewrites old short URLs so bookmarks/links keep working and
  the address bar shows the canonical path:
    /overview[/tab] -> /apps/overview[/tab]
    /backup[/sub]   -> /apps/overview/backups[/sub]
    /updater, /peers redirects retargeted to /apps/overview*
- Removed the standalone backup feature: components/backup/{index.js,feature.json},
  its manifest entry, the /backup route registrations and the dead handleBackup().
  The BackupPage classes stay — the Overview Backups tab embeds them.
- Repointed every backups/overview link: the admin dashboard's 'Open backup
  center', the app-card 'Open backup center' button + snapshot-overflow link,
  the sidebar Overview entry, the improvements deep-link, and the Migrate
  'go to locations' deep-link.

Also drop the redundant inline Check button from the Security empty state
(same rationale as Improvements: the host auto-scan repopulates it and the
header carries a manual Check).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-06-17 18:40:40 +01:00
librelad
830d361351 fix(overview): drop redundant Check button from empty Improvements state
The Improvements tab's empty state ('No hotfix data yet …') rendered an
inline 'Check now' button. It was redundant: the embedding Overview header
already carries a manual Check, and the host-side auto-scan repopulates the
signed improvements index on its own within a couple of minutes (the empty
message already says so). Remove the button so the empty state is just the
self-explanatory, automation-backed message.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-06-17 17:57:26 +01:00
librelad
fa47e16cab feat(updater): automatic background scan for versions, CVEs & improvements
Replace the click-to-scan-only flow with a self-throttled auto-scan that
rides the existing task-processor idle poll (the same shape as the
network-drift check — no new daemon, unit, or endpoint):

- 'libreportal updater check auto' gates on the age of the generated
  updates.json vs CFG_UPDATER_SCAN_INTERVAL (minutes, default 30,
  0 disables); a fresh file makes the 60s tick a single stat() + return.
  Manual checks and post-update rescans reset the clock for free, and a
  missing file means the first scan runs ~a minute after install.
- Eligible signed hotfixes keep flowing through artifactApplyAuto, which
  only enqueues ordinary tasks — mutations stay on the task path.
- Open updater surfaces (standalone /updater and the fleet Overview's
  headless UpdaterPage) follow along with a 60s static-JSON re-read that
  repaints only when a generated_at stamp changed; timer released via
  dispose() on unmount, ticks skipped while hidden.
- Empty states now say the first scan happens automatically; Check now
  stays as the immediate manual override.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-06-12 22:07:42 +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
d5e2375f38 fix(webui): address review findings on the fleet Overview build
- HIGH: renderAppDetail gated the Roll back button on last_snapshot* fields no
  generator emits, so it never rendered. Derive recoverability from update
  history (updater_apply always snapshots first) so the affordance is reachable.
- MED: per-app Updates tab now repaints on update/rollback/check/hotfix task
  completion (mirrors the backups card) instead of going stale until re-click.
- MED: in-page tab switches now sync spaClean.currentRoute, so the sidebar
  Overview entry no longer no-ops after switching tabs.
- LOW: keyboard activation (Enter/Space) for the role=button expander heads,
  backup tiles, and sidebar Overview entry.
- LOW: preserve ALL expanded Updates rows across a background repaint, not just
  the single ?app= deep-link (split toggle into _openDetail/_closeDetail).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-01 00:37:59 +01:00
librelad
dfd4ffa268 feat(webui): drop Updates & Backups top-nav items; fold into App Center
The fleet Overview area (Overview · Updates · Improvements · Backups) and the
backup center now live under App Center, so the standalone top-nav items are
redundant. Top nav is now Dashboard · App Center · Admin · Tasks.

- Remove the Backups and Updates anchors from topbar.html.
- Remove the nav{} blocks from updater/backup feature.json + manifest (so they
  don't resurface when the nav kernel lands).
- Highlight App Center for /overview and /backup; drop the dead /updater branch.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-01 00:10:30 +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
librelad
79d2a4750d feat(webui): Phase 4 — Improvements (hotfix) stream + per-app chip
Surfaces the hotfix channel in the WebUI. Primary home is the Updates &
Improvements page (the updater component) — its own "Improvements" tab — with a
secondary chip on the App detail page (fork 3 locality = both).

Updater component (components/updater):
- New "Improvements" sidebar tab + panel; renderImprovements() reads the host-
  generated artifacts_available.json (severity badge, scope chip, applied/auto/
  not-applicable badges, plain-English why). Apply/Revert buttons dispatch
  artifact_apply / artifact_revert through the TASK system (services.tasks.route)
  — no mutating API. Apply is disabled when the index is UNSIGNED.
- Overview gains an "Improvements" stat card; task-refresh now also repaints on
  artifact_* task completion; URL tab routing + dispose teardown extended.

Task plumbing (core/tasks): artifactApply/artifactRevert action methods (id is
charset-guarded before it enters the command string) + artifact_apply/
artifact_revert routeAction cases. Task list/format gain icons + friendly labels.

Apps component: an amber " N improvements" chip on an installed app's detail
header (populated async from artifacts_available.json filtered by app, applicable
& not-applied), linking to /updater/improvements. Best-effort, never throws.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-31 21:07:01 +01:00
librelad
164606dc7c docs(webui): refresh stale features/ path comments after the components/ rename
Comment-only tidy from the feng-shui audit — no code behavior changes. The
features/ directory was renamed to components/ during modularization, but
several header banners and inline comments still named the old path:

- 6 component module headers (admin/tasks/backup/dashboard/updater/index.js +
  updater/js/updater-page.js) now name their real components/<id>/… path
- core/kernel/js/spa.js + core/tasks/js/task-router.js comments
- backend/routes/features.js doc-banner (drop a components/<id>/ folder …)
- core/update-notifier/css/update-notifier.css header (js/update-notifier.js)

Guarded the rewrite so the LIVE /api/features/list endpoint name (feature-
registry.js sources + backend route) is untouched — only stale 'features/<path>'
directory references were updated.

Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-31 00:41:16 +01:00
librelad
2ef4cc00e1 refactor(webui): granular sub-system folders per component
De-clutter each component into sub-system folders (apps: core/ port-manager/
services/ tools/ routing/; admin: config/ overview/ system/ ssh/ peers/) with
the standard js/ css/ html/ icons/ layout inside; single-page components
(backup/dashboard/tasks/updater) get js/ css/ html/. Single-feature icon sets
moved into their sub-system (vpn -> apps/core/icons, config/cpu/os ->
admin/{config,system}/icons); shared app + category icons stay in core/icons.
feature.json + index.js stay at each component root (the scanned descriptor +
entry). Every controller/CSS/fragment/icon path reference rewritten; verified
no stale refs, all JS valid.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-30 12:42:35 +01:00
librelad
ed4bf41cba ux(updater): match the Backups page layout (sidebar, padding, click-to-open)
Restructure the updater page to mirror Backups exactly:
- updater-content.html: shared .sidebar/.category (with .category-icon SVGs)
  outside the page card, .config-section + .page-header + a padded
  .updater-page-body — fixes the missing content padding.
- updater.css: layout copied from .backup-* (flex, padding-bottom:48px,
  body padding:22px); dropped the custom sidebar/header styles (now using the
  shared chrome); kept the updater-specific content widgets.
- updater-page.js: delegate clicks on .updater-layout (sidebar is now a
  sibling of the card) so clicking a sidebar entry opens that tab; fill the
  shared page-header icon slot.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-30 07:34:32 +01:00
librelad
d39852aa3d refactor(webui): reorganize into components/ + core/ taxonomy
Final modularization layout (user-chosen): every page is a self-contained
folder under components/<id>/ (controllers + CSS + its html fragment), and all
shared/framework code folds into core/:
  core/kernel  (feature-registry, lifecycle, services, spa)
  core/boot    (auth, system-loader/orchestrator, setup, loaders)
  core/lib     (data-loader, router, helpers, the task kernel, shared modules)
  core/ui      (topbar, modal, notifications, … + topbar.html)
  core/css     (all shared stylesheets)
  core/icons
Top level is now just: components/, core/, themes/, index.html (+ runtime data/).

Every path reference rewritten (index.html, scripts arrays, fetch()/
loadFragment()/loadScript() literals, system-loader + config-manager controller
paths, kernel manifest URL, feature.json, backend FEATURES_DIR). The
/api/features/list endpoint NAME is unchanged (it now scans components/).
Deleted 3 dead files (app-content.html, apps-content.html, html-cache.js).
Verified: 0 stale prefixes, 0 double-rewrites, all JS/JSON valid.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-30 07:13:52 +01:00