Merge claude/1

This commit is contained in:
librelad 2026-05-30 12:42:35 +01:00
commit 9eb5f7f73a
94 changed files with 65 additions and 65 deletions

View File

@ -44,8 +44,8 @@ if (typeof window.ConfigManager === 'undefined') {
try { this.sidebar.populateSidebar(); } catch (e) {} try { this.sidebar.populateSidebar(); } catch (e) {}
// charts.js is the chart-rendering helper admin-overview pulls in. // charts.js is the chart-rendering helper admin-overview pulls in.
await Promise.all([ await Promise.all([
lazyLoad('/components/admin/admin-overview.js'), lazyLoad('/components/admin/overview/js/admin-overview.js'),
lazyLoad('/components/admin/charts.js') lazyLoad('/components/admin/system/js/charts.js')
]); ]);
if (typeof AdminOverview !== 'undefined') { if (typeof AdminOverview !== 'undefined') {
window.adminOverview = new AdminOverview('config-section'); window.adminOverview = new AdminOverview('config-section');
@ -60,7 +60,7 @@ if (typeof window.ConfigManager === 'undefined') {
// a config category — render its own controller into the main pane. // a config category — render its own controller into the main pane.
if (category === 'ssh-access') { if (category === 'ssh-access') {
try { this.sidebar.populateSidebar(); } catch (e) {} try { this.sidebar.populateSidebar(); } catch (e) {}
await lazyLoad('/components/admin/ssh-page.js'); await lazyLoad('/components/admin/ssh/js/ssh-page.js');
if (typeof SshPage !== 'undefined') { if (typeof SshPage !== 'undefined') {
window.sshPage = new SshPage('config-section'); window.sshPage = new SshPage('config-section');
await window.sshPage.init(); await window.sshPage.init();
@ -76,9 +76,9 @@ if (typeof window.ConfigManager === 'undefined') {
// we inject its content template, then init PeersPage. // we inject its content template, then init PeersPage.
if (category === 'peers') { if (category === 'peers') {
try { this.sidebar.populateSidebar(); } catch (e) {} try { this.sidebar.populateSidebar(); } catch (e) {}
await lazyLoad('/components/admin/peers-page.js'); await lazyLoad('/components/admin/peers/js/peers-page.js');
try { try {
const html = await fetch('/components/admin/peers-content.html').then(r => r.text()); const html = await fetch('/components/admin/peers/html/peers-content.html').then(r => r.text());
configSection.innerHTML = html; configSection.innerHTML = html;
} catch (e) { } catch (e) {
configSection.innerHTML = '<div class="error">Peers page template failed to load.</div>'; configSection.innerHTML = '<div class="error">Peers page template failed to load.</div>';
@ -100,10 +100,10 @@ if (typeof window.ConfigManager === 'undefined') {
if (category === 'system') { if (category === 'system') {
try { this.sidebar.populateSidebar(); } catch (e) {} try { this.sidebar.populateSidebar(); } catch (e) {}
await Promise.all([ await Promise.all([
lazyLoad('/components/admin/charts.js'), lazyLoad('/components/admin/system/js/charts.js'),
lazyLoad('/components/admin/admin-system.js'), lazyLoad('/components/admin/system/js/admin-system.js'),
lazyLoad('/components/admin/system-metric-page.js'), lazyLoad('/components/admin/system/js/system-metric-page.js'),
lazyLoad('/components/admin/system-storage-page.js') lazyLoad('/components/admin/system/js/system-storage-page.js')
]); ]);
if (typeof AdminSystem !== 'undefined') { if (typeof AdminSystem !== 'undefined') {
window.adminSystem = new AdminSystem('config-section'); window.adminSystem = new AdminSystem('config-section');
@ -216,7 +216,7 @@ if (typeof window.ConfigManager === 'undefined') {
var catIcon = catMeta.icon || category; var catIcon = catMeta.icon || category;
var headerHTML = var headerHTML =
'<div class="page-header config-page-header">' + '<div class="page-header config-page-header">' +
'<img class="page-header-icon" src="/core/icons/config/' + catIcon + '.svg" alt="" onerror="this.style.display=\'none\'">' + '<img class="page-header-icon" src="/components/admin/config/icons/config/' + catIcon + '.svg" alt="" onerror="this.style.display=\'none\'">' +
'<div class="page-header-title">' + '<div class="page-header-title">' +
'<div class="admin-breadcrumb">Config</div>' + '<div class="admin-breadcrumb">Config</div>' +
'<h1>' + catTitle + '</h1>' + '<h1>' + catTitle + '</h1>' +

View File

@ -88,7 +88,7 @@ class ConfigSidebar {
// Use correct icon from our new structure // Use correct icon from our new structure
const iconName = category.icon || category.id; const iconName = category.icon || category.id;
const iconPath = '/core/icons/config/' + iconName + '.svg'; const iconPath = '/components/admin/config/icons/config/' + iconName + '.svg';
categoryItem.innerHTML = '<img src="' + iconPath + '" alt="' + category.title + '" style="width: 20px; height: 20px; margin-right: 8px;"/> ' + category.title; categoryItem.innerHTML = '<img src="' + iconPath + '" alt="' + category.title + '" style="width: 20px; height: 20px; margin-right: 8px;"/> ' + category.title;

View File

@ -16,7 +16,7 @@ LP.features.register({
async mount(ctx) { async mount(ctx) {
window.configCategory = ctx.services.router.adminCategoryFromPath(window.location.pathname); window.configCategory = ctx.services.router.adminCategoryFromPath(window.location.pathname);
const html = await ctx.loadFragment('/components/admin/config-content.html'); const html = await ctx.loadFragment('/components/admin/config/html/config-content.html');
ctx.setContent(html, 'Admin'); ctx.setContent(html, 'Admin');
if (window.configManager) { if (window.configManager) {

View File

Before

Width:  |  Height:  |  Size: 583 B

After

Width:  |  Height:  |  Size: 583 B

View File

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

Before

Width:  |  Height:  |  Size: 780 B

After

Width:  |  Height:  |  Size: 780 B

View File

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

Before

Width:  |  Height:  |  Size: 911 B

After

Width:  |  Height:  |  Size: 911 B

View File

Before

Width:  |  Height:  |  Size: 5.3 KiB

After

Width:  |  Height:  |  Size: 5.3 KiB

View File

Before

Width:  |  Height:  |  Size: 963 B

After

Width:  |  Height:  |  Size: 963 B

View File

@ -401,14 +401,14 @@ class AdminSystem {
return `<div class="sys-stat"><span class="sys-stat-label">${this.escape(label)}</span><strong class="sys-stat-value">${this.escape(value)}</strong></div>`; return `<div class="sys-stat"><span class="sys-stat-label">${this.escape(label)}</span><strong class="sys-stat-value">${this.escape(value)}</strong></div>`;
} }
// OS stat with a distro icon (bundled under /core/icons/os/, generic Linux glyph // OS stat with a distro icon (bundled under /components/admin/system/icons/os/, generic Linux glyph
// for anything we don't have a logo for). // for anything we don't have a logo for).
_osStat(os) { _osStat(os) {
const slug = String(os || '').toLowerCase().replace(/[^a-z0-9]/g, '') || 'linux'; const slug = String(os || '').toLowerCase().replace(/[^a-z0-9]/g, '') || 'linux';
return `<div class="sys-stat"> return `<div class="sys-stat">
<span class="sys-stat-label">OS</span> <span class="sys-stat-label">OS</span>
<strong class="sys-stat-value sys-id-value"> <strong class="sys-stat-value sys-id-value">
<span class="sys-id-icon"><img src="/core/icons/os/${slug}.svg" alt="" onerror="this.onerror=null;this.src='/core/icons/os/linux.svg'"></span> <span class="sys-id-icon"><img src="/components/admin/system/icons/os/${slug}.svg" alt="" onerror="this.onerror=null;this.src='/components/admin/system/icons/os/linux.svg'"></span>
${this.escape(os || '—')} ${this.escape(os || '—')}
</strong> </strong>
</div>`; </div>`;
@ -430,7 +430,7 @@ class AdminSystem {
_cpuStat(cpu) { _cpuStat(cpu) {
const raw = String(cpu || ''); const raw = String(cpu || '');
const vendor = /amd/i.test(raw) ? 'amd' : /intel/i.test(raw) ? 'intel' : null; const vendor = /amd/i.test(raw) ? 'amd' : /intel/i.test(raw) ? 'intel' : null;
const icon = vendor ? `<span class="sys-id-icon"><img src="/core/icons/cpu/${vendor}.svg" alt="${vendor}"></span>` : ''; const icon = vendor ? `<span class="sys-id-icon"><img src="/components/admin/system/icons/cpu/${vendor}.svg" alt="${vendor}"></span>` : '';
return `<div class="sys-stat"> return `<div class="sys-stat">
<span class="sys-stat-label">CPU</span> <span class="sys-stat-label">CPU</span>
<strong class="sys-stat-value sys-id-value">${icon}${this.escape(this._cleanCpu(raw))}</strong> <strong class="sys-stat-value sys-id-value">${icon}${this.escape(this._cleanCpu(raw))}</strong>

View File

@ -42,7 +42,7 @@ LP.features.register({
} }
} }
const html = await ctx.loadFragment('/components/apps/apps-unified-layout.html'); const html = await ctx.loadFragment('/components/apps/core/html/apps-unified-layout.html');
ctx.setContent(html, appName); ctx.setContent(html, appName);
if (!window.appTabbedManager) { if (!window.appTabbedManager) {

View File

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

Before

Width:  |  Height:  |  Size: 110 KiB

After

Width:  |  Height:  |  Size: 110 KiB

View File

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

Before

Width:  |  Height:  |  Size: 952 B

After

Width:  |  Height:  |  Size: 952 B

View File

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

Before

Width:  |  Height:  |  Size: 371 B

After

Width:  |  Height:  |  Size: 371 B

View File

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

Before

Width:  |  Height:  |  Size: 5.3 KiB

After

Width:  |  Height:  |  Size: 5.3 KiB

View File

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

Before

Width:  |  Height:  |  Size: 361 KiB

After

Width:  |  Height:  |  Size: 361 KiB

View File

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -2408,7 +2408,7 @@ class AppsManager {
const existing = document.getElementById('mullvad-generate-modal'); const existing = document.getElementById('mullvad-generate-modal');
if (existing) existing.remove(); if (existing) existing.remove();
const mullvadIcon = (window.gluetunProviderIcons && window.gluetunProviderIcons.mullvad) || '/core/icons/vpn/mullvad.svg'; const mullvadIcon = (window.gluetunProviderIcons && window.gluetunProviderIcons.mullvad) || '/components/apps/core/icons/vpn/mullvad.svg';
const bodyHtml = ` const bodyHtml = `
<p class="eo-modal-section-text"> <p class="eo-modal-section-text">
Enter your 16-digit Mullvad account number. A new WireGuard key will be generated locally Enter your 16-digit Mullvad account number. A new WireGuard key will be generated locally
@ -3354,7 +3354,7 @@ class AppsManager {
{ name: 'TaskCommands', src: '/core/lib/task-commands.js' }, { name: 'TaskCommands', src: '/core/lib/task-commands.js' },
{ name: 'TaskActions', src: '/core/lib/task-actions.js' }, { name: 'TaskActions', src: '/core/lib/task-actions.js' },
{ name: 'TaskRouter', src: '/core/lib/task-router.js' }, { name: 'TaskRouter', src: '/core/lib/task-router.js' },
{ name: 'TasksManager', src: '/components/tasks/tasks-manager.js' } { name: 'TasksManager', src: '/components/tasks/js/tasks-manager.js' }
]; ];
for (const script of scripts) { for (const script of scripts) {

View File

@ -28,7 +28,7 @@ LP.features.register({
// Load the unified layout only if it isn't already present — preserves grid // Load the unified layout only if it isn't already present — preserves grid
// state when moving between categories / back from app-detail (legacy behaviour). // state when moving between categories / back from app-detail (legacy behaviour).
if (!document.querySelector('.apps-layout')) { if (!document.querySelector('.apps-layout')) {
const html = await ctx.loadFragment('/components/apps/apps-unified-layout.html'); const html = await ctx.loadFragment('/components/apps/core/html/apps-unified-layout.html');
ctx.setContent(html, 'Applications'); ctx.setContent(html, 'Applications');
} }

View File

@ -14,14 +14,14 @@ LP.features.register({
routes: ['/backup', '/backup*'], routes: ['/backup', '/backup*'],
// Controllers the feature needs; lazy-loaded on first mount (idempotent). // Controllers the feature needs; lazy-loaded on first mount (idempotent).
scripts: [ scripts: [
'/components/backup/backup-schema.js', '/components/backup/js/backup-schema.js',
'/components/backup/backup-page.js', '/components/backup/js/backup-page.js',
'/core/lib/backup-app-card.js', '/core/lib/backup-app-card.js',
], ],
async mount(ctx) { async mount(ctx) {
await ctx.loadScripts(this.scripts); await ctx.loadScripts(this.scripts);
const html = await ctx.loadFragment('/components/backup/backup-content.html'); const html = await ctx.loadFragment('/components/backup/html/backup-content.html');
ctx.setContent(html, 'Backups'); ctx.setContent(html, 'Backups');
if (typeof BackupPage === 'undefined') { if (typeof BackupPage === 'undefined') {
throw new Error('BackupPage controller failed to load'); throw new Error('BackupPage controller failed to load');

View File

@ -13,7 +13,7 @@ LP.features.register({
routes: ['/', '/dashboard'], routes: ['/', '/dashboard'],
async mount(ctx) { async mount(ctx) {
const html = await ctx.loadFragment('/components/dashboard/dashboard-content.html'); const html = await ctx.loadFragment('/components/dashboard/html/dashboard-content.html');
ctx.setContent(html, 'Dashboard'); ctx.setContent(html, 'Dashboard');
// Render the installed-apps icon grid (handleDashboard's only post-render call). // Render the installed-apps icon grid (handleDashboard's only post-render call).

View File

@ -11,7 +11,7 @@ LP.features.register({
routes: ['/tasks', '/tasks*'], routes: ['/tasks', '/tasks*'],
async mount(ctx) { async mount(ctx) {
const html = await ctx.loadFragment('/components/tasks/tasks-content.html'); const html = await ctx.loadFragment('/components/tasks/html/tasks-content.html');
ctx.setContent(html, 'Tasks'); ctx.setContent(html, 'Tasks');
if (window.tasksManager) { if (window.tasksManager) {

View File

@ -9,11 +9,11 @@
LP.features.register({ LP.features.register({
id: 'updater', id: 'updater',
routes: ['/updater', '/updater*'], routes: ['/updater', '/updater*'],
scripts: ['/components/updater/updater-page.js'], scripts: ['/components/updater/js/updater-page.js'],
async mount(ctx) { async mount(ctx) {
await ctx.loadScripts(this.scripts); await ctx.loadScripts(this.scripts);
const html = await ctx.loadFragment('/components/updater/updater-content.html'); const html = await ctx.loadFragment('/components/updater/html/updater-content.html');
ctx.setContent(html, 'Updates'); ctx.setContent(html, 'Updates');
if (typeof UpdaterPage === 'undefined') { if (typeof UpdaterPage === 'undefined') {
throw new Error('UpdaterPage controller failed to load'); throw new Error('UpdaterPage controller failed to load');

View File

@ -41,16 +41,16 @@ class SystemLoader {
scripts: [ scripts: [
'/core/lib/config-options.js', '/core/lib/config-options.js',
'/core/lib/config-shared.js', '/core/lib/config-shared.js',
'/components/admin/config-validator.js', '/components/admin/config/js/config-validator.js',
'/components/admin/toggle-manager.js', '/components/admin/config/js/toggle-manager.js',
'/components/admin/config-core.js', '/components/admin/config/js/config-core.js',
'/components/admin/domain-manager.js', '/components/admin/config/js/domain-manager.js',
'/components/admin/ip-whitelist-manager.js', '/components/admin/config/js/ip-whitelist-manager.js',
'/components/admin/config-renderer.js', '/components/admin/config/js/config-renderer.js',
'/components/admin/config-sidebar.js', '/components/admin/config/js/config-sidebar.js',
'/components/admin/config-form.js', '/components/admin/config/js/config-form.js',
'/components/admin/config-utils.js', '/components/admin/config/js/config-utils.js',
'/components/admin/config-manager.js' '/components/admin/config/js/config-manager.js'
] ]
}); });
@ -174,7 +174,7 @@ class SystemLoader {
'/core/lib/task-global-functions.js', '/core/lib/task-global-functions.js',
'/core/lib/task-manager.js', '/core/lib/task-manager.js',
'/core/lib/task-parameter-preserve.js', '/core/lib/task-parameter-preserve.js',
'/components/tasks/tasks-manager.js' '/components/tasks/js/tasks-manager.js'
] ]
}); });
@ -204,13 +204,13 @@ class SystemLoader {
global: 'appsManager', global: 'appsManager',
dependencies: ['data'], dependencies: ['data'],
scripts: [ scripts: [
'/components/apps/port-manager.js', '/components/apps/port-manager/js/port-manager.js',
'/core/lib/task-manager.js', // Add TaskManager for backup functionality '/core/lib/task-manager.js', // Add TaskManager for backup functionality
'/core/lib/backup-app-card.js', '/core/lib/backup-app-card.js',
'/components/apps/services-manager.js', '/components/apps/services/js/services-manager.js',
'/components/apps/tools-manager.js', '/components/apps/tools/js/tools-manager.js',
'/components/apps/routing-manager.js', '/components/apps/routing/js/routing-manager.js',
'/components/apps/apps-manager.js' '/components/apps/core/js/apps-manager.js'
] ]
}); });
@ -232,7 +232,7 @@ class SystemLoader {
}, },
global: 'appTabbedManager', global: 'appTabbedManager',
dependencies: [], dependencies: [],
script: '/components/apps/app-tabbed-manager.js' script: '/components/apps/core/js/app-tabbed-manager.js'
}); });
// console.log('TEST: Components added. Total components:', this.components.size); // console.log('TEST: Components added. Total components:', this.components.size);

View File

@ -341,7 +341,7 @@ class LibrePortalSPAClean {
try { try {
// console.log('📄 SPA: Fetching dashboard content...'); // console.log('📄 SPA: Fetching dashboard content...');
const html = await this.fetchContent('/components/dashboard/dashboard-content.html'); const html = await this.fetchContent('/components/dashboard/html/dashboard-content.html');
// console.log('📄 SPA: Dashboard content fetched, loading...'); // console.log('📄 SPA: Dashboard content fetched, loading...');
this.loadContent(html, 'Dashboard'); this.loadContent(html, 'Dashboard');
// console.log('📄 SPA: Dashboard content loaded'); // console.log('📄 SPA: Dashboard content loaded');
@ -362,10 +362,10 @@ class LibrePortalSPAClean {
// backup-page.js + backup-app-card.js are loaded on first navigation. // backup-page.js + backup-app-card.js are loaded on first navigation.
// loadScript is idempotent — subsequent /backup navigations are no-ops. // loadScript is idempotent — subsequent /backup navigations are no-ops.
await Promise.all([ await Promise.all([
this.loadScript('/components/backup/backup-page.js'), this.loadScript('/components/backup/js/backup-page.js'),
this.loadScript('/core/lib/backup-app-card.js') this.loadScript('/core/lib/backup-app-card.js')
]); ]);
const html = await this.fetchContent('/components/backup/backup-content.html'); const html = await this.fetchContent('/components/backup/html/backup-content.html');
this.loadContent(html, 'Backups'); this.loadContent(html, 'Backups');
if (typeof BackupPage !== 'undefined') { if (typeof BackupPage !== 'undefined') {
window.backupPage = new BackupPage(); window.backupPage = new BackupPage();
@ -409,7 +409,7 @@ class LibrePortalSPAClean {
// Ensure unified layout is loaded (like the old SPA) // Ensure unified layout is loaded (like the old SPA)
if (!document.querySelector('.apps-layout')) { if (!document.querySelector('.apps-layout')) {
//console.log('📄 Loading apps layout HTML...'); //console.log('📄 Loading apps layout HTML...');
const html = await this.fetchContent('/components/apps/apps-unified-layout.html'); const html = await this.fetchContent('/components/apps/core/html/apps-unified-layout.html');
this.loadContent(html, 'Applications'); this.loadContent(html, 'Applications');
} else { } else {
//console.log('📄 Apps layout already exists, skipping HTML load'); //console.log('📄 Apps layout already exists, skipping HTML load');
@ -477,7 +477,7 @@ class LibrePortalSPAClean {
} }
try { try {
const html = await this.fetchContent('/components/apps/apps-unified-layout.html'); const html = await this.fetchContent('/components/apps/core/html/apps-unified-layout.html');
this.loadContent(html, appName); // Will be updated after app data loads this.loadContent(html, appName); // Will be updated after app data loads
// AppTabbedManager should already be initialized by SystemLoader // AppTabbedManager should already be initialized by SystemLoader
@ -502,7 +502,7 @@ class LibrePortalSPAClean {
window.configCategory = window.adminCategoryFromPath(window.location.pathname); window.configCategory = window.adminCategoryFromPath(window.location.pathname);
try { try {
const html = await this.fetchContent('/components/admin/config-content.html'); const html = await this.fetchContent('/components/admin/config/html/config-content.html');
this.loadContent(html, 'Admin'); this.loadContent(html, 'Admin');
if (window.configManager) { if (window.configManager) {
@ -535,7 +535,7 @@ class LibrePortalSPAClean {
//console.log('📋 Loading tasks...'); //console.log('📋 Loading tasks...');
try { try {
const html = await this.fetchContent('/components/tasks/tasks-content.html'); const html = await this.fetchContent('/components/tasks/html/tasks-content.html');
this.loadContent(html, 'Tasks'); this.loadContent(html, 'Tasks');
// Tasks manager should already be initialized by SystemLoader // Tasks manager should already be initialized by SystemLoader

View File

@ -16,27 +16,27 @@
<link rel="stylesheet" href="/core/css/loading-screen.css"> <link rel="stylesheet" href="/core/css/loading-screen.css">
<link rel="stylesheet" href="/core/css/setup-wizard.css"> <link rel="stylesheet" href="/core/css/setup-wizard.css">
<link rel="stylesheet" href="/core/css/style.css"> <link rel="stylesheet" href="/core/css/style.css">
<link rel="stylesheet" href="/components/admin/ip-whitelist.css"> <link rel="stylesheet" href="/components/admin/config/css/ip-whitelist.css">
<link rel="stylesheet" href="/components/apps/port-manager.css"> <link rel="stylesheet" href="/components/apps/port-manager/css/port-manager.css">
<link rel="stylesheet" href="/components/backup/backup.css"> <link rel="stylesheet" href="/components/backup/css/backup.css">
<link rel="stylesheet" href="/components/admin/ssh.css"> <link rel="stylesheet" href="/components/admin/ssh/css/ssh.css">
<link rel="stylesheet" href="/components/admin/admin.css"> <link rel="stylesheet" href="/components/admin/css/admin.css">
<link rel="stylesheet" href="/components/apps/services.css"> <link rel="stylesheet" href="/components/apps/services/css/services.css">
<link rel="stylesheet" href="/core/css/modal.css"> <link rel="stylesheet" href="/core/css/modal.css">
<link rel="stylesheet" href="/components/apps/tools.css"> <link rel="stylesheet" href="/components/apps/tools/css/tools.css">
<link rel="stylesheet" href="/components/apps/routing.css"> <link rel="stylesheet" href="/components/apps/routing/css/routing.css">
<link rel="stylesheet" href="/core/css/login.css"> <link rel="stylesheet" href="/core/css/login.css">
<link rel="stylesheet" href="/core/css/aurora-background.css"> <link rel="stylesheet" href="/core/css/aurora-background.css">
<link rel="stylesheet" href="/core/css/topbar.css"> <link rel="stylesheet" href="/core/css/topbar.css">
<link rel="stylesheet" href="/core/css/sidebar.css"> <link rel="stylesheet" href="/core/css/sidebar.css">
<link rel="stylesheet" href="/components/apps/apps-layout.css"> <link rel="stylesheet" href="/components/apps/core/css/apps-layout.css">
<link rel="stylesheet" href="/components/apps/apps.css"> <link rel="stylesheet" href="/components/apps/core/css/apps.css">
<link rel="stylesheet" href="/core/css/forms.css"> <link rel="stylesheet" href="/core/css/forms.css">
<link rel="stylesheet" href="/core/css/config.css"> <link rel="stylesheet" href="/core/css/config.css">
<link rel="stylesheet" href="/components/apps/service-buttons.css"> <link rel="stylesheet" href="/components/apps/core/css/service-buttons.css">
<link rel="stylesheet" href="/components/dashboard/dashboard.css"> <link rel="stylesheet" href="/components/dashboard/css/dashboard.css">
<link rel="stylesheet" href="/components/tasks/tasks.css"> <link rel="stylesheet" href="/components/tasks/css/tasks.css">
<link rel="stylesheet" href="/components/updater/updater.css"> <link rel="stylesheet" href="/components/updater/css/updater.css">
<link rel="stylesheet" href="/core/css/update-notifier.css"> <link rel="stylesheet" href="/core/css/update-notifier.css">
<script> <script>
// Inline data-theme bootstrap — runs before any rendering so the right // Inline data-theme bootstrap — runs before any rendering so the right
@ -98,7 +98,7 @@
<script src="/core/lib/dismissible.js"></script> <script src="/core/lib/dismissible.js"></script>
<script src="/core/ui/eo-modal.js"></script> <script src="/core/ui/eo-modal.js"></script>
<script src="/core/lib/task-refresh-coordinator.js"></script> <script src="/core/lib/task-refresh-coordinator.js"></script>
<script src="/components/dashboard/dashboard.js"></script> <script src="/components/dashboard/js/dashboard.js"></script>
<script src="/core/boot/system-loader.js"></script> <script src="/core/boot/system-loader.js"></script>
<script src="/core/boot/loading-ui.js"></script> <script src="/core/boot/loading-ui.js"></script>
<script src="/core/boot/setup-detector.js"></script> <script src="/core/boot/setup-detector.js"></script>