From eb4dafaf9b67e8fc615058a2967280b6decffc2a Mon Sep 17 00:00:00 2001 From: Jeff Emmett Date: Mon, 10 Nov 2025 23:25:44 -0800 Subject: [PATCH] fix coordinates --- src/automerge/useAutomergeStoreV2.ts | 63 ++++------------------------ src/routes/Board.tsx | 26 ++++++++++-- 2 files changed, 29 insertions(+), 60 deletions(-) diff --git a/src/automerge/useAutomergeStoreV2.ts b/src/automerge/useAutomergeStoreV2.ts index 8b4d144..edfb7e6 100644 --- a/src/automerge/useAutomergeStoreV2.ts +++ b/src/automerge/useAutomergeStoreV2.ts @@ -411,62 +411,13 @@ export function useAutomergeStoreV2({ } else if (attempts < maxAttempts) { setTimeout(checkForPatches, 200) } else { - // Patches didn't come through - handler may have missed them if data was written before handler was set up - // In this case, we need to manually apply the data via patches - // We'll trigger patches by making a safe change that doesn't modify existing objects - console.log(`⚠️ Patches didn't populate store. Handler may have missed initial patches. Applying data directly via patches...`) - - try { - // Read all records from Automerge doc and apply them directly to store - // This is a fallback when patches are missed (works for both dev and production) - // Use the same sanitization as patches would use to ensure consistency - const allRecords: TLRecord[] = [] - Object.entries(doc.store).forEach(([id, record]: [string, any]) => { - // Skip invalid records and custom record types (same as patch processing) - if (!record || !record.typeName || !record.id) { - return - } - - // Skip obsidian_vault records - they're not TLDraw records - if (record.typeName === 'obsidian_vault' || - (typeof record.id === 'string' && record.id.startsWith('obsidian_vault:'))) { - return - } - - try { - // Create a clean copy of the record - const cleanRecord = JSON.parse(JSON.stringify(record)) - // CRITICAL: Use the same sanitizeRecord function that patches use - // This ensures consistency between dev and production - const sanitized = sanitizeRecord(cleanRecord) - allRecords.push(sanitized) - } catch (e) { - console.warn(`⚠️ Could not serialize/sanitize record ${id}:`, e) - } - }) - - if (allRecords.length > 0) { - // Apply records directly to store using mergeRemoteChanges - // This bypasses patches but ensures data is loaded (works for both dev and production) - // Use mergeRemoteChanges to mark as remote changes (prevents feedback loop) - store.mergeRemoteChanges(() => { - // Separate pages, shapes, and other records to ensure proper loading order - const pageRecords = allRecords.filter(r => r.typeName === 'page') - const shapeRecords = allRecords.filter(r => r.typeName === 'shape') - const otherRecords = allRecords.filter(r => r.typeName !== 'page' && r.typeName !== 'shape') - - // Put pages first, then other records, then shapes (ensures pages exist before shapes reference them) - const recordsToAdd = [...pageRecords, ...otherRecords, ...shapeRecords] - store.put(recordsToAdd) - }) - console.log(`✅ Applied ${allRecords.length} records directly to store (fallback for missed patches - works in dev and production)`) - - // REMOVED: Aggressive shape refresh that was causing coordinate loss - // Shapes loaded directly should be visible without forced refresh - } - } catch (error) { - console.error(`❌ Error applying records directly:`, error) - } + // Patches didn't come through - this may indicate a sync issue + // REMOVED: Fallback that applies records directly - it was causing coordinate loss + // If patches don't work, it's a deeper sync issue that needs to be fixed + // The fallback was resetting coordinates to 0,0 when re-applying records + console.warn(`⚠️ No patches received after ${maxAttempts} attempts. This may indicate a sync issue.`) + console.warn(`⚠️ NOT using fallback direct record application to prevent coordinate loss.`) + console.warn(`⚠️ If shapes aren't loading, check that patches are being generated correctly.`) setStoreWithStatus({ store, diff --git a/src/routes/Board.tsx b/src/routes/Board.tsx index a5b46fd..fcf67e7 100644 --- a/src/routes/Board.tsx +++ b/src/routes/Board.tsx @@ -377,10 +377,28 @@ export function Board() { if (shapesWithInvalidParent.length > 0) { console.warn(`📊 Board: ${shapesWithInvalidParent.length} shapes have invalid or missing parentId. Fixing...`) // Fix shapes with invalid parentId by assigning them to current page - const fixedShapes = shapesWithInvalidParent.map((s: any) => ({ - ...s, - parentId: currentPageId - })) + // CRITICAL: Preserve x and y coordinates when fixing parentId + const fixedShapes = shapesWithInvalidParent.map((s: any) => { + // Get the shape from store to ensure we have all properties + const shapeFromStore = store.store!.get(s.id) + if (shapeFromStore && shapeFromStore.typeName === 'shape') { + // Preserve original x and y coordinates + const originalX = s.x !== undefined && typeof s.x === 'number' && !isNaN(s.x) ? s.x : (shapeFromStore as any).x + const originalY = s.y !== undefined && typeof s.y === 'number' && !isNaN(s.y) ? s.y : (shapeFromStore as any).y + + // Create fixed shape with preserved coordinates + const fixed = { ...shapeFromStore, parentId: currentPageId } + if (typeof originalX === 'number' && !isNaN(originalX)) { + (fixed as any).x = originalX + } + if (typeof originalY === 'number' && !isNaN(originalY)) { + (fixed as any).y = originalY + } + return fixed as TLRecord + } + // Fallback if shape not in store + return { ...s, parentId: currentPageId } as TLRecord + }) try { store.store.put(fixedShapes) console.log(`📊 Board: Fixed ${fixedShapes.length} shapes by assigning them to current page ${currentPageId}`)