Move the 6 app controllers (apps-manager, app-tabbed-manager, port-manager,
routing-manager, services-manager, tools-manager) and their 7 stylesheets
(apps, apps-layout, tools, services, service-buttons, routing, port-manager)
into features/apps/. JS paths only existed in system-loader.js (the
apps-manager + app-tabbed-manager components); CSS hrefs in index.html (kept
eager). The cross-feature task-manager (/shared/task/) + backup-app-card
entries in the apps-manager component are untouched here. Globals unchanged.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
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>
- 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>