- features/tasks/: tasks-manager.js (the /tasks page controller) + tasks.css.
- shared/task/: the 6 cross-cutting task-kernel files (event-bus, commands,
actions, router, global-functions, manager) + task-refresh-coordinator.js —
used by tasks AND apps/app-detail/backup, so they go to shared/, not a
feature. task-parameter-preserve.js stays at js/ (shared root).
- Updated all path strings: system-loader.js task-system + apps-manager
components, apps-manager loadTaskSystem(), index.html (refresh-coordinator +
tasks.css). Globals (taskEventBus/tasksManager/TaskManager/...) 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>
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>