librelad 07daa7555a fix(webui): load mobile-menu.js; prune orphaned task queue entries
Both found in a user's console log.

1. ReferenceError: setupMobileMenu is not defined (dashboard.js:98)

   core/topbar/js/mobile-menu.js defines that global, and index.html
   never loaded it. dashboard.js called it unguarded as the FIRST line
   of setupEventListeners, so dashboard init threw every page load and
   took loadInstalledApps() with it — and the burger menu was dead on
   mobile. system-loader already guarded its own call with a typeof
   check, which is why this survived unnoticed.

   Loads the script (before dashboard.js) and guards the call, so
   optional nav chrome can never take down the page below it again.

2. Endless 404s on /api/tasks/<id> for tasks that no longer exist

   queue.json is append-only from the enqueue side and nothing ever
   pruned it, so any task file removed afterwards left an id the WebUI
   re-fetched forever, one 404 per poll per orphan. Adds
   cleanupOrphanQueueEntries to the idle housekeeping pass: entries with
   no task file are dropped and logged. Self-heals existing strays.

   (Provoked by my own clean-up of two test tasks earlier in this
   session, but the gap is real and predates it.)

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-12 22:48:55 +01:00

137 lines
7.2 KiB
HTML
Executable File

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>LibrePortal - Modern Docker Management</title>
<link rel="icon" type="image/svg+xml" href="/core/icons/libreportal.svg">
<link rel="icon" type="image/x-icon" href="/core/icons/favicon.ico" sizes="any">
<link rel="apple-touch-icon" href="/core/icons/libreportal.svg">
<!-- Styles -->
<!-- Base token layer (theme-agnostic): --font-mono, --page-* identity hues -->
<link rel="stylesheet" href="/core/theme/css/tokens.css">
<link rel="stylesheet" href="/core/theme/css/themes.css">
<link rel="stylesheet" href="/core/loading/css/loading-screen.css">
<link rel="stylesheet" href="/core/setup/css/setup-wizard.css">
<link rel="stylesheet" href="/core/theme/css/base.css">
<link rel="stylesheet" href="/components/admin/config/css/ip-whitelist.css">
<link rel="stylesheet" href="/components/apps/port-manager/css/port-manager.css">
<link rel="stylesheet" href="/components/backup/core/css/backup.css">
<link rel="stylesheet" href="/components/admin/ssh/css/ssh.css">
<link rel="stylesheet" href="/components/admin/core/css/admin.css">
<link rel="stylesheet" href="/components/apps/services/css/services.css">
<link rel="stylesheet" href="/core/overlays/css/modal.css">
<link rel="stylesheet" href="/components/apps/tools/css/tools.css">
<link rel="stylesheet" href="/components/apps/routing/css/routing.css">
<link rel="stylesheet" href="/core/boot/auth/css/login.css">
<link rel="stylesheet" href="/core/theme/css/aurora-background.css">
<link rel="stylesheet" href="/core/topbar/css/topbar.css">
<link rel="stylesheet" href="/core/topbar/css/sidebar.css">
<link rel="stylesheet" href="/components/apps/core/css/apps-layout.css">
<link rel="stylesheet" href="/components/apps/core/css/apps.css">
<link rel="stylesheet" href="/components/apps/overview/css/overview.css">
<link rel="stylesheet" href="/core/forms/css/forms.css">
<link rel="stylesheet" href="/core/forms/css/config.css">
<link rel="stylesheet" href="/components/apps/core/css/service-buttons.css">
<link rel="stylesheet" href="/components/dashboard/css/dashboard.css">
<link rel="stylesheet" href="/components/tasks/css/tasks.css">
<link rel="stylesheet" href="/components/updater/css/updater.css">
<link rel="stylesheet" href="/core/update-notifier/css/update-notifier.css">
<link rel="stylesheet" href="/core/network-notifier/css/network-notifier.css">
<link rel="stylesheet" href="/core/health-notifier/css/health-notifier.css">
<script>
// Inline data-theme bootstrap — runs before any rendering so the right
// palette tokens resolve on first paint. Synchronously injects a
// <link> to the saved theme's CSS (which lives at
// /themes/<name>/theme.css) so even the very first frame paints with
// the correct palette. ThemeRegistry below additionally <link>s every
// discovered theme so the dropdown can switch between them without
// another fetch.
(function () {
var legacy = localStorage.getItem('selectedTheme');
if (legacy && !localStorage.getItem('theme')) localStorage.setItem('theme', legacy);
if (legacy) localStorage.removeItem('selectedTheme');
var theme = localStorage.getItem('theme');
if (theme === 'dark' || theme === 'blue') theme = 'dark-blue';
if (!theme) theme = 'nebula';
localStorage.setItem('theme', theme);
document.documentElement.setAttribute('data-theme', theme);
// Synchronous <link> injection. Inserted via document.write so the
// parser blocks on this stylesheet — guarantees first paint has
// the palette tokens defined. Marked with data-theme-css="<name>"
// so ThemeRegistry can detect and skip duplicates.
document.write(
'<link rel="stylesheet" href="/themes/' + theme +
'/theme.css" data-theme-css="' + theme + '">'
);
})();
</script>
<script src="/core/theme/js/theme-registry.js"></script>
<script src="/core/forms/js/custom-select.js"></script>
<script src="/core/forms/js/custom-number.js"></script>
</head>
<body>
<!-- Topbar Container -->
<div id="topbar-container">
<!-- Topbar will be loaded here -->
</div>
<!-- Main Content Container -->
<main id="main-content" class="main">
<div id="app-content">
<!-- App content will be loaded here -->
</div>
</main>
<!-- Scripts -->
<!-- Auth must load first — gates all other initialization -->
<script src="/core/boot/auth/js/auth-manager.js"></script>
<!-- Essential Bootstrap -->
<!-- LpUi runs first so body.lp-ui--advanced / lp-ui--dev are set
before any page/component renders → no FOUC of advanced sections. -->
<script src="/core/ui-mode/js/lp-ui.js"></script>
<script src="/core/dom/js/dom-helpers.js"></script>
<script src="/core/app-meta/js/app-helpers.js"></script>
<script src="/core/data-loader/js/data-loader.js"></script>
<script src="/core/live/js/live-system.js"></script>
<script src="/core/ui-state/js/dismissible.js"></script>
<script src="/core/overlays/js/eo-modal.js"></script>
<script src="/core/tasks/js/task-refresh-coordinator.js"></script>
<!-- Defines the global setupMobileMenu(), which dashboard.js calls from
setupEventListeners() and system-loader registers as a component. It was
never loaded here, so dashboard init died on a ReferenceError at that
call — taking loadInstalledApps() with it — and the burger menu was dead
on mobile. Must come before dashboard.js. -->
<script src="/core/topbar/js/mobile-menu.js"></script>
<script src="/components/dashboard/js/dashboard.js"></script>
<script src="/core/boot/js/system-loader.js"></script>
<script src="/core/loading/js/loading-ui.js"></script>
<script src="/core/setup/js/setup-detector.js"></script>
<script src="/core/setup/js/setup-wizard.js"></script>
<script src="/core/setup/js/setup-completion-watcher.js"></script>
<script src="/core/boot/js/system-orchestrator.js"></script>
<!-- Feature-module kernel. Currently passive: defines window.LP.features and
loads the page manifest; spa.js consults it for routing. See
docs/architecture/webui-architecture.md. -->
<script src="/core/kernel/js/feature-registry.js"></script>
<script src="/core/kernel/js/services.js"></script>
<script src="/core/kernel/js/lifecycle.js"></script>
<!-- Component (page) modules are NOT listed here: the kernel loads each one's
self-registering index.js from the manifest (components/manifest.dev.json)
before routing. Adding a page = drop a components/<id>/ folder + a manifest
entry, no index.html edit. Heavy controllers stay lazy (loaded by mount). -->
<!--
Page-specific controllers are loaded on demand by spa.js / config-manager.js
when the user navigates to the relevant route. Keeping them out of the
initial <script> block trims ~200 KB raw (~50 KB gzipped) off the cold-load
cost AND avoids parsing them up front on the dashboard, which most users
land on. Each handler's loadScript() call is idempotent — subsequent
navigations to the same route are free.
-->
<script src="/core/kernel/js/spa.js"></script>
</body>
</html>