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

46 lines
2.0 KiB
JavaScript

// features/tasks/index.js — the Tasks page as a feature module.
//
// window.tasksManager is a system-loader singleton (its "task-system" component
// loads all task scripts, starts the shared SSE bus, then news TasksManager).
// So mount() does NOT new it or load scripts — it renders the fragment and
// re-inits the view, exactly like the old handleTasks(). unmount() releases
// only this view's per-mount leaks (the 30s auto-refresh interval + open log
// streams); it must never stop the shared taskEventBus or null the singleton.
LP.features.register({
id: 'tasks',
routes: ['/tasks', '/tasks*'],
async mount(ctx) {
const html = await ctx.loadFragment('/components/tasks/tasks-content.html');
ctx.setContent(html, 'Tasks');
if (window.tasksManager) {
await window.tasksManager.init();
} else {
// Don't throw — matches handleTasks: the page still renders, task
// functionality is just limited until the task-system component is ready.
console.warn('TasksManager not available yet, task functionality will be limited');
}
},
async unmount() {
const tm = window.tasksManager;
// The one per-view leak init() opens: the 30s auto-refresh interval stored
// on the singleton. Each init() recreates it, so clearing here is idempotent.
if (tm && tm.refreshInterval) {
clearInterval(tm.refreshInterval);
tm.refreshInterval = null;
}
// Stop any open per-task log streams this view started (each removes its own
// SSE listeners + map entry). Snapshot keys first — stopLogStreaming mutates
// the map. Does NOT touch the shared bus.
if (tm && tm.activeLogStreams && typeof tm.stopLogStreaming === 'function') {
for (const id of Array.from(tm.activeLogStreams.keys())) {
try { tm.stopLogStreaming(id); } catch (_) {}
}
}
// DO NOT: stop window.taskEventBus (shared SSE singleton), remove the
// singleton's once-bound task listeners, or null window.tasksManager.
},
});