Repository navigation
Editor page canvas stuck on spinner in local dev: section iframes never render #1235
Description
Summary
Opening a page in the editor under primo dev locally leaves the main canvas on a permanent loading spinner. Section wrappers are created in the DOM but each one is empty — ComponentNode never renders its <iframe>, so the page never reports itself mounted.
Reproduced on unmodified main (fd175fa / 6558bca). Not caused by any in-flight branch — I hit this while verifying #1233 and checked out pristine main to confirm.
Reproduce
cd primo-sites && primo dev(a workspace with a multi-section page — usedcoffee-shop,pages/about.yaml, 5 sections)- Open
http://coffee-shop.localhost:3000/admin/site/about - Canvas spins indefinitely
Observed
// after 8s on /admin/site/about
{ sectionNodes: 5, iframes: 0, spinnerVisible: true, allWrappersEmpty: true }All 5 [data-section] wrappers are present, each children.length === 0 and 32px tall. No console errors (only an unrelated __primo_dev_ws__ websocket warning). Data loads fine — every page_sections / site_symbols / page_section_entries request returns 200, and the records exist when queried with an auth token.
Why it hangs
The spinner is gated on a mount counter in src/lib/builder/views/editor/Page.svelte:
{#if !page_mounted && sections && sections.length > 1}page_mounted only becomes true once sections_mounted >= sections.length. Each increment comes from on:mount={() => sections_mounted++} on ComponentNode, dispatched by dispatch_mount().
But every dispatch_mount() path depends on the iframe existing — onload={setup_component_iframe}, the MutationObserver, the component-error message handler — and the iframe itself is gated at ComponentNode.svelte:1262:
{#if $site_html && generated_js}
<iframe ... onload={setup_component_iframe}></iframe>
{/if}So when that condition stays false, no iframe → no mount → counter never reaches target → spinner forever. $site_html does get populated (the page <head> has 80 children), which points at generated_js never being produced — i.e. the per-block processCode compile in ComponentNode isn't completing or isn't assigning.
The actual bug
Independent of why the compile doesn't finish, the failure mode is wrong: a block that never compiles produces no iframe, no error branch, and no mount signal, so the whole canvas is held hostage by one silent block. There's an {#if error} branch, but the paths that set error also require the iframe to exist, so a pre-iframe compile failure can't reach it.
Worth fixing in two parts:
- Root cause — why
generated_jsis never set for these blocks in local dev. - Resilience — the mount counter shouldn't be able to deadlock. A block that fails to compile should surface its error and still count as "settled", so one bad block degrades to a visible error instead of an indefinite spinner with no diagnostics.
Impact
The editor's primary view is unusable locally — you can still navigate sidebars, page types, modals and block code, but not the page canvas. Verification of #1233 had to route around it.
Environment
main@6558bca(also repros atfd175fa)- CLI 0.1.24,
primo dev(both--author filesand--author cms) - macOS, Chromium
Activity
elemdos commentedon Sep 18, 2026
Opened #1236 with the resilience half of this: the canvas now reveals once mounting stalls and logs how many sections never reported, instead of spinning indefinitely.
That does not fix the root cause — content data still stops resolving for these sections in local dev, so they render no iframe. Leaving this open for that.
One extra data point from diagnosing: every dependency resolves fine at the API level for the stuck sections (symbol found, 3 fields, 3 entries, uploads present), so the stall appears to be in the client-side reactive resolution in
useContent, not in the data itself.Stop one stuck section from hanging the whole editor canvas (#1236)