seedGuide() ran in onMount via the 'else' branch while template was still null during the async
fetch, creating 5 example shapes (Q1 start/end, part, response, context) that flashed on screen
until the real template + PDF loaded. Only seed the guide for a genuinely-empty template after load.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Top bar reduced to single-line height: Back becomes an icon button, caption
line removed (detail lives in the guide), Save button size=small. Guide
panel defaults collapsed and toggles via a ? icon button at bottom-right
so it doesn't occupy permanent canvas real estate.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replaces infinite-canvas free-pan with a constrained vertical-scroll doc
view: tldraw setCameraOptions with behavior='contain', fit-x-100, and top
origin so the PDF never drifts side-to-side. Layout restructured from
floating overlays to flex column (top bar + sidebar/canvas row). Tool
panel is now a left sidebar, guide panel overlays canvas bottom-right.
PAGE_START_X changed from 260 → 0 so pages are flush left; camera
applyDocViewConstraints() called incrementally as pages stream in and
with resetZoom() once all pages are loaded.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Three z-order fixes in ExamTemplateSetupPage:
1. loadShapes: early-return before deletion when models is empty, so
seed guide shapes aren't wiped for a fresh template that has no
saved regions yet.
2. Incremental PDF loading (onPageReady): replace per-page sendToBack
(unreliable when all shapes are moving — reorderToBack no-ops) with
bringToFront on existing domain shapes after each page is added.
3. Final load sequence: call bringDomainShapesToFront after syncPdfPages
+ loadShapes to guarantee correct z-order regardless of how tldraw's
fractional indexer placed newly created shapes. Also called from
onMount for the same reason.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Rendering a 36-page AQA exam PDF sequentially created 36 large canvas
elements, causing memory pressure and keeping pdfStatus='loading' for
60–120s in headless Chrome. Two changes:
1. pdfLoader.ts: reuse a single canvas (reduces peak memory from ~120MB
to ~4MB) and fire onPageReady callback after each page so callers can
stream pages to the canvas as they render.
2. ExamTemplateSetupPage.tsx: use the callback to add each PDF page
shape to the tldraw canvas the moment it renders, making the first
page visible within a few seconds rather than after all pages load.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Tldraw 3.6.1 Tldraw component already registers defaultBindingUtils
internally; passing them explicitly via the bindingUtils prop caused
"Binding type 'arrow' is defined more than once" runtime crash.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Select + Furniture tool buttons used color='default', which MUI v5 resolves to
theme.palette.default (undefined) -> TypeError reading 'main'/'dark' at render,
tripping the ErrorBoundary ('Template setup canvas crashed'). Use 'inherit'.
Caught by live browser-verify t_11f5a049; model-level tests never rendered the toolbar.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>