From 15a519748dcf86e1a24f54751f2fa66cbbe890c1 Mon Sep 17 00:00:00 2001 From: CC Worker Date: Sun, 7 Jun 2026 10:12:16 +0000 Subject: [PATCH] fix(exam): keep region shapes overlaid on PDF in setup canvas MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- src/pages/exam/setup/ExamTemplateSetupPage.tsx | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/pages/exam/setup/ExamTemplateSetupPage.tsx b/src/pages/exam/setup/ExamTemplateSetupPage.tsx index 7f020b5..5709f53 100644 --- a/src/pages/exam/setup/ExamTemplateSetupPage.tsx +++ b/src/pages/exam/setup/ExamTemplateSetupPage.tsx @@ -88,10 +88,15 @@ function modelFromTLShape(shape: TLShape): ExamCanvasShapeModel | null { } } +function bringDomainShapesToFront(editor: Editor) { + const ids = editor.getCurrentPageShapes().filter((s) => !!shapeTypeToKind(s.type)).map((s) => s.id) + if (ids.length) try { editor.bringToFront(ids as any) } catch { /* */ } +} + function loadShapes(editor: Editor, models: ExamCanvasShapeModel[]) { + if (!models.length) return const existing = editor.getCurrentPageShapes().filter((s) => shapeTypeToKind(s.type)).map((s) => s.id) if (existing.length) editor.deleteShapes(existing) - if (!models.length) return editor.createShapes(models.map((m) => ({ id: createShapeId(m.id), type: SHAPE_TYPES[m.kind], @@ -117,8 +122,7 @@ function syncPdfPages(editor: Editor, pages: PdfPageImage[]) { props: { w: geometry.w, h: geometry.h, src: page.src, pageNumber: geometry.pageNumber }, } as any })) - const ids = geometries.map((geometry) => createShapeId(PDF_PAGE_IDS_PREFIX + geometry.pageNumber)) - try { editor.sendToBack(ids as any) } catch { /* tldraw 3 keeps creation order behind later region shapes */ } + // z-order is enforced by the caller via bringDomainShapesToFront } function seedGuide(editor: Editor) { @@ -170,7 +174,7 @@ const ExamTemplateSetupInner: React.FC = () => { const shapeId = createShapeId(PDF_PAGE_IDS_PREFIX + newPage.pageNumber) if (!ed.getCurrentPageShapes().find((s) => s.id === shapeId)) { ed.createShapes([{ id: shapeId, type: PDF_PAGE_SHAPE_TYPE, x: geometry.x, y: geometry.y, isLocked: true, props: { w: geometry.w, h: geometry.h, src: newPage.src, pageNumber: newPage.pageNumber } } as any]) - try { ed.sendToBack([shapeId as any]) } catch { /* */ } + bringDomainShapesToFront(ed) } } setPdfStatus('ready') @@ -188,6 +192,7 @@ const ExamTemplateSetupInner: React.FC = () => { if (editor) { syncPdfPages(editor, pages) loadShapes(editor, shapesFromTemplate(detail, geometries)) + bringDomainShapesToFront(editor) } setDirty(false) } catch (e) { @@ -256,6 +261,7 @@ const ExamTemplateSetupInner: React.FC = () => { editor.user.updateUserPreferences({ colorScheme: theme.palette.mode === 'dark' ? 'dark' : 'light' }) editor.store.listen(() => setDirty(true), { scope: 'document' }) if (template) loadShapes(editor, shapesFromTemplate(template, pageGeometriesRef.current)); else seedGuide(editor) + bringDomainShapesToFront(editor) }} />