Pages and panels showed inconsistent loading states: the Backup center
and several admin pages (System, SSH, admin Overview), the Overview
Migrate/Peers panels, the per-app updater section and the backup engine
details modal rendered a bare 'Loading…' text line (updater-empty /
backup-empty-state) with no spinner, while Services/Config/Tasks used a
boxed card + spinner.
Add one shared loader — window.lpLoadingBox(message) + .lp-loading CSS in
the core/loading subsystem (the boxed card + accent spinner the good tabs
already use) — and route those bare-text loaders through it. The system
metric graph keeps its absolute overlay but gains the same spinner.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
The teardown audit found the backup-stacking leak class across 4 more feature
modules (12 confirmed leaks); unmount() left document/window listeners, intervals,
and SSE subscriptions firing on stale controllers after navigation:
- admin: overview/ssh/peers/system each leaked a document click listener ->
AbortController + dispose() per page; admin unmount() aborts each.
- dashboard: the 1 Hz update-countdown interval + the LiveSystem view sub ->
stopUpdateCountdown()/detachDashboardLive(), registered via ctx.sub().
- tasks: constructor-started global live-log poller (discarded handle) -> stored
+ idempotent + cleared on unmount + re-armed on mount; per-task monitorTask
window listeners + interval -> tracked in a map, released on unmount.
- apps: app-tabbed reconcile setTimeout loop + watchdog window/document listeners
+ popstate -> per-instance AbortController + dispose() that clears the timer,
resets the guards, and unloads the active tab's Services intervals + log SSE.
All mirror the kernel's MountContext teardown discipline. 12 files, all pass
node --check. Backup (fixed earlier) re-confirmed clean by the audit.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
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>