1040 Commits

Author SHA1 Message Date
librelad
3f1cb67d02 Merge claude/1 2026-05-30 00:46:57 +01:00
librelad
98d950ba44 feat(webui): phase 2 — DI service container (ctx.services)
Introduces kernel/services.js (window.LP.services): an additive, typed,
LAZY view onto the existing cross-cutting singletons — tasks{bus,refresh,
route}, live, auth, data, notify, theme, modal, router. It constructs
nothing (pure getters onto the live globals), so there's no double-init and
the globals stay authoritative. MountContext now injects it as ctx.services.

Slot names/globals were verified against the real code (workflow map): the
design doc's §4 list was wrong in several places — no window.taskManager
(client slot dropped), tasks.route lives on tasksManager.router, auth has no
status(), DataLoader isn't a window prop (lexical fallback), modal/router are
split surfaces (grouped/bound objects).

Migrated the 4 cross-cutting refs in the feature modules onto ctx.services
(admin: router.adminCategoryFromPath + tasks.refresh; backup: tasks.refresh;
app-detail: router.appPath). Page-owned controllers stay feature-globals.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-30 00:46:57 +01:00
librelad
cd34a7671a Merge claude/1 2026-05-30 00:18:20 +01:00
librelad
31b73f9670 feat(webui): auto-discover features from folders, mirroring the theme system
Themes are already modular via folder discovery (GET /api/themes/list scans
themes/<name>/). This brings the SAME model to pages:
- backend/routes/features.js: public GET /api/features/list scans
  frontend/features/<id>/feature.json and returns the page manifest. The
  Node process reads its own bind-mounted /app/frontend — no runFileOp /
  regen / source-array plumbing needed (sidesteps the shell-generator gotchas).
- features/<id>/feature.json: each page now self-describes (id, routes,
  module, handler, navId, nav, order). 6 real features + 3 redirect-only
  (config/peers/ssh) so behaviour is preserved exactly.
- kernel loadManifest() prefers /api/features/list, falls back to the static
  features/manifest.dev.json when the endpoint isn't up yet.

Result: dropping a features/<id>/ folder registers a page; deleting it
removes it — zero central edits, exactly like dropping a theme folder.
(Backend route needs a Node restart to activate; the static-manifest
fallback keeps everything working until then.)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-30 00:18:20 +01:00
librelad
3dd2444bc2 Merge claude/1 2026-05-29 23:52:51 +01:00
librelad
0724ed785a feat(webui): load feature modules from the manifest (drop index.html script list)
The kernel now loads each feature's self-registering index.js from the
manifest (new 'module' field) before building routes, so index.html no
longer hardcodes a per-feature <script> list — one of the four registries
the modularization targets is now gone. Adding a page = drop a
features/<id>/ folder + a manifest entry; no index.html edit.

loadScript is idempotent and non-fatal: a module that 404s or fails to
register leaves its route on the legacy handler. Manifest-load failure
still falls back to the built-in route table.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-29 23:52:51 +01:00
librelad
c2dab953af Merge claude/1 2026-05-29 23:46:04 +01:00
librelad
d7ac865b98 fix(webui): guard app-detail listener binds against per-navigation leak
AppTabbedManager.initialize() re-runs on every /app navigation (its
'initialized' flag is never set true), and setupURLMonitoring() /
setupTaskEventListeners() add window listeners (popstate, taskCreated/
Completed/Updated) unguarded — so each app-detail visit stacked another
listener set. Bind them once via a _listenersWired flag (mirrors the
existing _watchdogStarted guard). Pre-existing leak surfaced by the
feature-migration review.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-29 23:46:04 +01:00
librelad
ac9f2bf767 Merge claude/1 2026-05-29 23:37:51 +01:00
librelad
b4105d8cff feat(webui): migrate the Admin area to one feature module
features/admin/index.js owns all /admin* sub-routes (overview, config/<cat>,
system[/sub], tools/ssh-access, tools/peers). mount() parses the category and
delegates to the system-loader configManager singleton's renderConfig();
unmount() stops AdminSystem's live SSE sub + 30s interval, drops the
admin-overview task-refresh registration, and nulls the per-visit
sub-controllers (adminOverview/adminSystem/sshPage/peersPage) while leaving
configManager intact. Legacy /config /ssh /peers redirect handlers unchanged.

With this, every WebUI page now routes through the feature-module kernel;
the legacy handleX() methods remain only as fallbacks.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-29 23:37:51 +01:00
librelad
ff79249fdd Merge claude/1 2026-05-29 23:35:14 +01:00
librelad
247310f370 feat(webui): migrate App Center + app-detail to feature modules
- features/apps/index.js (/apps*) and features/app-detail/index.js (/app*)
  as two features; /apps* registered first so wildcard precedence holds.
  Both drive the system-loader-pre-initialized singletons (appsManager /
  appTabbedManager) via .initialize(), mirroring the legacy handlers exactly
  (incl. app-detail's legacy ?app=/?=name parsing + ?tab=/?config= rewrite).
- kernel/lifecycle.js: ctx.nav(path, addToHistory) so app-detail's empty-name
  redirect matches navigate('/apps', false) exactly.

unmount is a no-op for both (shared singletons); the app-tabbed-manager
listener-rebind leak is pre-existing and handled in a follow-up.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-29 23:35:14 +01:00
librelad
935faa4c58 Merge claude/1 2026-05-29 23:29:31 +01:00
librelad
e6e796311a feat(webui): migrate Dashboard + Tasks to feature modules
Two more pages on the feature-module contract (specs produced + adversarially
verified by workflow):
- features/dashboard/index.js: landing page; mount() folds in the data-reload
  that used to be a navigate() special-case (now deleted from spa.js, so it
  fires exactly once). No controller class — uses the eager dashboard.js globals.
- features/tasks/index.js: re-inits the system-loader tasksManager singleton;
  unmount() clears the 30s auto-refresh interval + open log streams WITHOUT
  stopping the shared SSE bus or nulling the singleton.

Verifier fixes applied: deleted the duplicate dashboard reload in navigate();
dropped a dead detachDashboardLive() call; fixed an invalid try{}while(false)
in the tasks unmount.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-29 23:29:31 +01:00
librelad
4c368e43de Merge claude/1 2026-05-29 23:02:24 +01:00
librelad
182be8c33d feat(webui): phase 3 (first feature) — migrate Backup to a feature module
Introduces the kernel lifecycle and migrates the first real page to the
feature-module contract:
- kernel/lifecycle.js: MountContext (loadScripts/loadFragment/setContent
  + an AbortController/unsub teardown ledger so mounts can't leak
  listeners or live streams).
- features/backup/index.js: Backup Center as a self-contained module
  (LP.features.register with mount/unmount); heavy backup-page.js stays
  lazy-loaded on first mount.
- spa.js: routes whose feature has a registered mount() are driven
  through the kernel; everything else still uses its legacy handleX().
  navigate() unmounts the current feature first. Both fall back to the
  legacy handler if a module is missing or mount throws.

Strangler step: /backup now flows manifest -> registry -> mount/unmount.
The other pages are untouched. handleBackup remains as the fallback.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-29 23:02:24 +01:00
librelad
cab04108d3 Merge claude/1 2026-05-29 22:49:39 +01:00
librelad
57c17647e3 feat(webui): phase 1a — shared base token layer (tokens.css)
First slice of the per-module CSS strategy: introduce
shared/css/tokens.css as the always-present, theme-agnostic base token
layer, loaded before all other stylesheets.

- Defines --font-mono (ui-monospace stack). Several feature sheets used
  var(--font-mono, monospace) with no definition, falling back to bare
  monospace; this unifies them with the richer stack used elsewhere.
- Hoists the --page-* identity hues out of css/admin.css so they belong
  to the base layer rather than the admin stylesheet. Values unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-29 22:49:39 +01:00
librelad
6ae85d8dd2 Merge claude/1 2026-05-29 22:32:42 +01:00
librelad
7e051be196 feat(webui): phase 0b — route from the feature manifest
LibrePortalSPAClean now builds its route table from window.LP.features
(features/manifest.dev.json) instead of the hardcoded setupRoutes() Map.
Manifest order is preserved so findRouteHandler()'s wildcard precedence
(/apps* before /app*) is unchanged. All-or-nothing fallback to the
built-in table if the manifest is missing/empty or names an unknown
handler, so routing is never left half-wired.

Rendering is unchanged — handlers still do the work; only the routing
source moved.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-29 22:32:42 +01:00
librelad
76138ddcdd Merge claude/1 2026-05-29 22:28:19 +01:00
librelad
2eaa5857a1 feat(webui): phase 0a — feature-module kernel scaffold (passive)
Adds the foundation of the feature-module architecture
(docs/frontend-modularization.md) as inert, additive code:
- kernel/feature-registry.js: window.LP.features — runtime register(),
  manifest loader, route-table + nav builders.
- features/manifest.dev.json: hand-committed manifest mirroring spa.js
  setupRoutes() exactly (route -> handler + navId).
- index.html loads the kernel before spa.js.

Zero behaviour change: nothing consults the registry yet. Phase 0b flips
routing to be registry-driven with the spa.js Map as fallback.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-29 22:28:19 +01:00
librelad
8312f2222f Merge claude/1 2026-05-29 22:10:36 +01:00
librelad
22aafe3a55 docs: frontend feature-module modularization design
Synthesized architecture for turning the no-build vanilla-JS WebUI into a
scan-and-manifest feature system mirroring the backend container scan:
self-contained features/<id>/ folders, a navigation kernel, uniform
mount/unmount lifecycle, DI service context replacing ~80 window globals,
per-feature CSS, god-file decomposition, and a strangler migration roadmap.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-29 22:10:36 +01:00
librelad
116e48699e Merge claude/1 2026-05-29 16:09:51 +01:00
librelad
ad08ce2324 refactor(app-scan): auto-clean leftover folders, drop bogus wipe prompt
The empty-folder reaper only ever fired on folders with no real data
(empty, or only a regenerable .config and/or migrate.txt marker), yet
prompted 'THIS WILL WIPE ALL DATA' before each removal — a question
about data that didn't exist. Collapse the four duplicated branches into
one reason-string path, clean these leftovers automatically, and fix the
stale $app_name used in the DB-delete (it deleted the wrong row when
looping over $folder_name).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-29 14:53:41 +01:00
librelad
9ec3a9e736 Merge claude/1 2026-05-29 14:50:11 +01:00
librelad
b1ffe9d052 chore(rootless): trim AppArmor banner text
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-29 14:50:11 +01:00
librelad
44b9293d29 Merge claude/2 2026-05-29 01:37:36 +01:00
librelad
edb9bddab1 docs: add CLAUDE.md — verify WebUI changes visually with lp-shot
Directive for agents working on the repo: after a user-visible WebUI change,
screenshot the route with the lp-shot helper and review the PNG before marking
done, rather than relying on syntax checks alone. Box-specific setup (port
discovery, auth, install) is kept out of the repo.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-29 01:37:36 +01:00
librelad
1202a6690f Merge claude/2 2026-05-29 01:32:54 +01:00
librelad
b6fa9317bd ux(ssh): drop the redundant paste-key hint, equalise the two cards
Remove the "Paste a public key…" line (the section description already
explains it) and stretch the Login / Add-a-key cards to equal height
(.ssh-cols align-items: start -> stretch).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-29 01:32:54 +01:00
librelad
462fedf257 Merge claude/1 2026-05-29 00:34:04 +01:00
librelad
382e91f2a7 fix(webui): friendly title + icon for the verify task
The `libreportal verify` task showed its raw command and no icon. Add its
formatCommandForUser pattern ("LibrePortal - Verify System"), a 🛡️ type icon,
a formatActionTitle entry, and include it in isLibrePortalSystemTask so it shows
the LibrePortal logo like other system tasks.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-29 00:34:04 +01:00
librelad
0ae8c819a6 Merge claude/1 2026-05-29 00:29:35 +01:00
librelad
4290c04a78 ux(admin): match System page header icon to the sidebar (activity pulse)
The System header used a cpu icon while the sidebar (and the Dashboard's System
card) use the activity-pulse icon. Swap the header to the same activity pulse so
System reads consistently everywhere.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-29 00:29:35 +01:00
librelad
38f04a4dd6 Merge claude/2 2026-05-29 00:21:31 +01:00
librelad
83b129fdad ux(system): put OS + CPU logos in a rounded tile (backup-tile style)
Wrap both the OS and CPU logos in a 36x36 rounded icon tile with a subtle
background — same treatment as the backup app-list tiles — at a uniform
size, with the logo centred inside.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-29 00:21:31 +01:00
librelad
fd33120cb4 Merge claude/1 2026-05-29 00:20:09 +01:00
librelad
33a749c8d1 ux(admin): rename Overview → Dashboard + add header icons
Renames the Admin landing to "Dashboard" in both the page title and the sidebar
label, and adds a leading header icon (the grid icon, matching the Backup
dashboard) via the shared .page-header-icon-slot. System gets a cpu icon in its
header too. The slot styles come from the globally-loaded backup.css, so no new
CSS is needed.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-29 00:20:09 +01:00
librelad
00736b57a7 Merge claude/1 2026-05-29 00:14:40 +01:00
librelad
350d72f6aa ux(ssh): lay Login + Add-a-key side by side (50/50) on tablet/desktop
Both sections are light on content, so a two-column grid uses the space better
than full-width stacking. Wrapped them in .ssh-cols (1fr 1fr, stacks under
640px); Authorized keys stays full width below.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-29 00:14:40 +01:00
librelad
76524d1c35 Merge claude/2 2026-05-29 00:11:15 +01:00
librelad
78519a398e ux(system): crop CPU wordmark logos + enlarge host-strip icons
The Intel/AMD logos are wide wordmarks that sat in a square 24x24 box, so
the actual mark rendered only a few px tall and looked invisible. Crop each
viewBox to the wordmark and size the CPU icon by height with auto width so
it shows at a legible scale. Bump the OS distro icon from 18 to 22px.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-29 00:11:15 +01:00
librelad
297cdbb8af Merge claude/1 2026-05-29 00:08:08 +01:00
librelad
036fead047 ux(admin): tinted Overview buttons w/ white text; Verify→green, Backups→blue
Reverts the solid fills (too heavy) back to the translucent hue tint but keeps
white text — white on a tint-over-dark-card reads cleanly on nebula, which the
old coloured text didn't. Hues restored to the brighter originals. Verify now
gets its own green token (--page-verify) per the usual verify=green convention,
and Backups takes the blue.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-29 00:08:08 +01:00
librelad
9c4f43ed44 Merge claude/2 2026-05-29 00:04:59 +01:00
librelad
378a4c1dd6 feat(system): official distro logos + Intel/AMD CPU logo with clean model text
Replace the hand-drawn distro marks with the official artwork (Simple
Icons, brand-coloured, bundled locally — no external calls) for
Debian/Ubuntu/Fedora/Arch + a generic Linux fallback. Add Intel/AMD logos
under /icons/cpu/ and show the vendor logo beside the CPU, with the model
string stripped of trademark noise (Intel(R) Core(TM), ®/™, "CPU") since
the logo conveys the vendor — e.g. "Core i5-8250U @ 1.60GHz".

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-29 00:04:59 +01:00
librelad
33a2d892fb Merge claude/1 2026-05-28 23:59:15 +01:00
librelad
bd0256715c ux(admin): solid-fill Overview buttons for legibility on nebula
The translucent tinted buttons washed out against nebula's glassy aurora
background. Switch .admin-action-btn to a solid fill with white text (the bg can
no longer bleed through) and deepen the --page-* hues enough for white-text
contrast. Hover uses brightness() so it's theme-agnostic.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-28 23:59:15 +01:00