diff --git a/steps/index.html b/steps/index.html index bdd5ddb..c8ac40b 100644 --- a/steps/index.html +++ b/steps/index.html @@ -542,10 +542,8 @@

Press ESC or click outside to close

- @@ -615,11 +613,8 @@

User Manual

- + @@ -639,7 +634,7 @@
+ class="px-4 py-2 rounded-lg bg-green-600 text-white hover:bg-green-700 font-semibold">Cancel
diff --git a/steps/src/main.js b/steps/src/main.js index 5ce18af..8c5364c 100644 --- a/steps/src/main.js +++ b/steps/src/main.js @@ -22,6 +22,7 @@ import { initializeDataExplorer } from "./dataExplorer.js"; import { showModal, hideModal, + runStartupLoader, } from "./modal.js"; import { initSyncUIHandlers, @@ -49,6 +50,9 @@ import { autoOffsetIndicator, clearCacheBtn, guideModal, + shortcutsModal, + shortcutsModalCloseBtn, + guideModalCloseBtn, } from "./dom.js"; import { initializeTheme } from "./theme.js"; @@ -158,8 +162,34 @@ document.addEventListener("DOMContentLoaded", () => { // Check if the user has seen the guide const isFirstRun = !sessionStorage.getItem("hasSeenUserGuide"); if (isFirstRun) { - guideModal.classList.remove("hidden"); - sessionStorage.setItem("hasSeenUserGuide", "true"); + runStartupLoader(10000) + .then(() => { + // 1. Show User Guide + guideModal.classList.remove("hidden"); + + // 2. Setup chaining for Guide -> Shortcuts + // We use { once: true } to ensure this specific flow logic only runs once. + // The default event listeners in ui.js simply toggle visibility, which works fine + // with this as long as we trigger the next step. + const onGuideClose = () => { + shortcutsModal.classList.remove("hidden"); + }; + guideModalCloseBtn.addEventListener("click", onGuideClose, { once: true }); + + // 3. Setup chaining for Shortcuts -> App + const onShortcutsClose = () => { + // Flow complete + sessionStorage.setItem("hasSeenUserGuide", "true"); + }; + shortcutsModalCloseBtn.addEventListener("click", onShortcutsClose, { once: true }); + }) + .catch(() => { + console.log("Startup loader skipped/cancelled by user."); + sessionStorage.setItem("hasSeenUserGuide", "true"); + }); + } else { + // Ensure the flag is set if it wasn't first run (defensive) + sessionStorage.setItem("hasSeenUserGuide", "true"); } // Await the database initialization before attempting to load any files. diff --git a/steps/src/modal.js b/steps/src/modal.js index 4b50ded..35974f7 100644 --- a/steps/src/modal.js +++ b/steps/src/modal.js @@ -23,7 +23,6 @@ export function showModal( modalCancelBtn.classList.toggle("hidden", !isConfirm); - // --- THIS IS THE FIX --- // This ensures the "OK" button is always visible for this modal. modalOkBtn.classList.remove("hidden"); @@ -37,11 +36,12 @@ export function showModal( modalResolve = resolve; }); } + // A new function specifically for the loading modal export function showLoadingModal(message) { modalText.textContent = message; - modalOkBtn.classList.add('hidden'); - modalCancelBtn.classList.add('hidden'); + modalOkBtn.classList.add('hidden'); // Hide OK button for loading + modalCancelBtn.classList.add('hidden'); // Initially hide cancel button modalProgressContainer.classList.remove('hidden'); modalProgressBar.style.width = '0%'; modalProgressText.textContent = 'Initializing...'; @@ -62,6 +62,44 @@ export function updateLoadingModal(percent, message) { } } +export function runStartupLoader(durationMs = 10000) { + return new Promise((resolve, reject) => { + showLoadingModal("Opening Quick Start Guide..."); + modalCancelBtn.textContent = "Skip Guide"; + modalCancelBtn.classList.remove("hidden"); // Show cancel button for startup loader + + const startTime = Date.now(); + const intervalMs = 100; // Update frequency + let timerId = null; + + const cleanup = () => { + clearInterval(timerId); + hideModal(false); // Use hideModal to clear the progress bar and hide the modal + }; + + const onCancel = () => { + cleanup(); + reject('cancelled'); + }; + + modalCancelBtn.onclick = onCancel; // Use the existing modalCancelBtn + + timerId = setInterval(() => { + const elapsed = Date.now() - startTime; + const remaining = Math.max(0, durationMs - elapsed); + const percent = Math.min(100, (elapsed / durationMs) * 100); + + updateLoadingModal(percent, `${(remaining / 1000).toFixed(1)}s remaining`); + + if (elapsed >= durationMs) { + cleanup(); + resolve(); + } + }, intervalMs); + }); +} + + // The hideModal function now also resets the progress bar export function hideModal(value) { // This now returns a promise return new Promise(resolve => {