From 0cd964d7c60e60bbaa3cb437f0646fac7fd7b752 Mon Sep 17 00:00:00 2001 From: Milan Rother Date: Tue, 2 Jun 2026 13:40:56 +0200 Subject: [PATCH] Run button: stay in loading state until startup toolbox bootstrap finishes (not just engine wheel) --- src/routes/+page.svelte | 33 ++++++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index 5db4ce2a..33a1f4c8 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -560,6 +560,15 @@ } let pyodideReady = $state(false); let pyodideLoading = $state(false); + // True once startup `bootstrapToolboxes()` has finished (or failed). The + // engine wheel being up (`pyodideReady`) is not enough: the bootstrap still + // installs the preloaded catalog toolboxes afterwards (and, in engine builds + // that resolve dependencies, the engine base + docutils) via micropip. The + // run button folds this in so it stays in its loading state until that work + // is done, instead of unlocking the moment the wheel is ready. + let bootstrapComplete = $state(false); + let runLoading = $derived(pyodideLoading || !bootstrapComplete); + let runReady = $derived(pyodideReady && bootstrapComplete); let simRunning = $state(false); let isRunStarting = false; // Synchronous flag to prevent race conditions let isContinuing = false; // Synchronous flag to prevent rapid continue calls @@ -592,10 +601,16 @@ await autoDetectBackend(); await initBackendFromUrl(); await initPyodide(); + statusText = 'Loading toolboxes...'; await bootstrapToolboxes(); + statusText = 'Ready'; } catch (e) { console.error('[startup] backend init failed', e); throw e; + } finally { + // Unlock the run button even if bootstrap failed — a broken + // toolbox shouldn't leave the button stuck in its loading state. + bootstrapComplete = true; } })(); void loadFromUrlParam(backendReady).catch((e) => { @@ -995,7 +1010,7 @@ // Run simulation (auto-initializes if needed) async function handleRun() { // Prevent concurrent simulation runs (synchronous check for rapid key presses) - if (simRunning || isRunStarting || pyodideLoading) return; + if (simRunning || isRunStarting || runLoading) return; // Set flag before any async operations to prevent race conditions isRunStarting = true; @@ -1331,18 +1346,18 @@ {:else} -
+