mirror of
https://github.com/NNTmux/newznab-tmux.git
synced 2026-08-29 01:08:56 +00:00
Optimize template
This commit is contained in:
@@ -21,9 +21,7 @@ import './components/theme-toggle.js';
|
||||
import './components/back-to-top.js';
|
||||
import './components/dropdown.js';
|
||||
import './components/sidebar-toggle.js';
|
||||
import './components/admin-submenu.js';
|
||||
import './components/mobile-nav.js';
|
||||
import './components/password-toggle.js';
|
||||
import './components/confirm-modal.js';
|
||||
import './components/confirm-link.js';
|
||||
import './components/toast-notification.js';
|
||||
|
||||
@@ -14,6 +14,10 @@ import Alpine from '@alpinejs/csp';
|
||||
* Each module registers itself via Alpine.data() as a side effect.
|
||||
*/
|
||||
const lazyComponentMap = {
|
||||
// --- Components only needed on specific pages ---
|
||||
'adminSubmenu': () => import('./components/admin-submenu.js'),
|
||||
'passwordToggle': () => import('./components/password-toggle.js'),
|
||||
|
||||
// --- Modal components (only on release pages) ---
|
||||
'nfoModal': () => import('./components/nfo-modal.js'),
|
||||
'filelistModal': () => import('./components/filelist-modal.js'),
|
||||
@@ -83,15 +87,27 @@ export function loadAndStart() {
|
||||
|
||||
if (imports.size === 0) {
|
||||
Alpine.start();
|
||||
enableTransitions();
|
||||
return;
|
||||
}
|
||||
|
||||
// Load all needed modules in parallel, then start Alpine
|
||||
Promise.all([...imports].map(fn => fn()))
|
||||
.then(() => Alpine.start())
|
||||
.then(() => {
|
||||
Alpine.start();
|
||||
enableTransitions();
|
||||
})
|
||||
.catch(err => {
|
||||
console.error('[lazy-loader] Failed to load component:', err);
|
||||
// Start Alpine anyway so the page is usable
|
||||
Alpine.start();
|
||||
enableTransitions();
|
||||
});
|
||||
}
|
||||
|
||||
function enableTransitions() {
|
||||
requestAnimationFrame(() => {
|
||||
requestAnimationFrame(() => {
|
||||
document.documentElement.removeAttribute('data-loading');
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,39 +1,2 @@
|
||||
import './bootstrap';
|
||||
|
||||
// CSP-safe styles are loaded via resources/css/app.css (no duplicate import here)
|
||||
|
||||
// Import Alpine.js CSP-safe components (replaces old csp-safe.js modules)
|
||||
import './alpine/index.js';
|
||||
|
||||
// Theme initialization - optimized to prevent flash of unstyled content
|
||||
(function() {
|
||||
'use strict';
|
||||
const applyTheme = function() {
|
||||
const metaTheme = document.querySelector('meta[name="theme-preference"]');
|
||||
const themePreference = metaTheme?.content || 'light';
|
||||
|
||||
if (themePreference === 'system') {
|
||||
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
|
||||
if (mediaQuery.matches) {
|
||||
document.documentElement.classList.add('dark');
|
||||
} else {
|
||||
document.documentElement.classList.remove('dark');
|
||||
}
|
||||
} else if (themePreference === 'dark') {
|
||||
document.documentElement.classList.add('dark');
|
||||
} else {
|
||||
document.documentElement.classList.remove('dark');
|
||||
}
|
||||
};
|
||||
|
||||
// Apply theme immediately if DOM is ready, otherwise wait
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', applyTheme);
|
||||
} else {
|
||||
applyTheme();
|
||||
}
|
||||
})();
|
||||
|
||||
// FontAwesome icons loaded via CSS webfonts in app.css
|
||||
// Do NOT import the JS bundle (all.min.js) — it adds ~1.4MB to the build
|
||||
// and duplicates what the CSS webfont approach already provides.
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=5.0, user-scalable=yes, viewport-fit=cover">
|
||||
<meta name="color-scheme" content="light dark">
|
||||
<meta name="csrf-token" content="{{ csrf_token() }}">
|
||||
|
||||
{{-- Apply dark mode BEFORE any CSS loads to prevent white flash --}}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=5.0, viewport-fit=cover">
|
||||
<meta name="color-scheme" content="light dark">
|
||||
<meta name="csrf-token" content="{{ csrf_token() }}">
|
||||
<meta name="theme-preference" content="system">
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=5.0, user-scalable=yes, viewport-fit=cover">
|
||||
<meta name="color-scheme" content="light dark">
|
||||
<meta name="mobile-web-app-capable" content="yes">
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
{{--
|
||||
Dark mode and color scheme initialization - MUST be included at the very top of <head>,
|
||||
BEFORE any CSS/Vite tags, to prevent white flash on page load.
|
||||
1. The blocking script applies the 'dark' class and data-color-scheme to <html> synchronously.
|
||||
2. The style tag that follows uses those attributes to set html AND body background-color
|
||||
for every scheme/dark combo so the first paint is never white.
|
||||
1. The blocking script applies the 'dark' class, data-color-scheme, and data-loading to <html> synchronously.
|
||||
2. The style tag covers background, text color, color-scheme, x-cloak hiding, and transition
|
||||
suppression so the first paint matches the user's theme with zero flash.
|
||||
--}}
|
||||
<script nonce="{{ csp_nonce() }}">
|
||||
(function() {
|
||||
var d = document.documentElement;
|
||||
d.setAttribute('data-loading', '');
|
||||
@auth
|
||||
var t = '{{ auth()->user()->theme_preference ?? "light" }}';
|
||||
var scheme = '{{ auth()->user()->color_scheme ?? "blue" }}';
|
||||
@@ -23,10 +24,14 @@
|
||||
})();
|
||||
</script>
|
||||
<style nonce="{{ csp_nonce() }}">
|
||||
html, body { background-color: #f8fafc; }
|
||||
html.dark, html.dark body { background-color: #0f172a; }
|
||||
html[data-color-scheme="emerald"], html[data-color-scheme="emerald"] body { background-color: #f0fdf4; }
|
||||
html.dark[data-color-scheme="emerald"], html.dark[data-color-scheme="emerald"] body { background-color: #071a12; }
|
||||
html[data-color-scheme="violet"], html[data-color-scheme="violet"] body { background-color: #faf5ff; }
|
||||
html.dark[data-color-scheme="violet"], html.dark[data-color-scheme="violet"] body { background-color: #120b20; }
|
||||
[x-cloak] { display: none !important; }
|
||||
html[data-loading], html[data-loading] *, html[data-loading] *::before, html[data-loading] *::after { transition: none !important; }
|
||||
html { color-scheme: light; }
|
||||
html.dark { color-scheme: dark; }
|
||||
html, body { background-color: #f8fafc; color: #1e293b; }
|
||||
html.dark, html.dark body { background-color: #0f172a; color: #e2e8f0; }
|
||||
html[data-color-scheme="emerald"], html[data-color-scheme="emerald"] body { background-color: #f0fdf4; color: #1e293b; }
|
||||
html.dark[data-color-scheme="emerald"], html.dark[data-color-scheme="emerald"] body { background-color: #071a12; color: #d1fae5; }
|
||||
html[data-color-scheme="violet"], html[data-color-scheme="violet"] body { background-color: #faf5ff; color: #1e293b; }
|
||||
html.dark[data-color-scheme="violet"], html.dark[data-color-scheme="violet"] body { background-color: #120b20; color: #e9d5ff; }
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user