From 5c99a82c14e0c3a849909401d8875c853bcb1d13 Mon Sep 17 00:00:00 2001 From: Jeff Emmett Date: Mon, 10 Nov 2025 13:34:55 -0800 Subject: [PATCH] final updates to Automerge conversion --- FATHOM_INTEGRATION.md | 1 + WORKER_ENV_GUIDE.md | 1 + src/automerge/useAutomergeStoreV2.ts | 60 ++++++++++++++++--- src/automerge/useAutomergeSync.ts | 56 ++++++++++++++++- src/components/location/LocationDashboard.tsx | 1 + src/components/location/LocationMap.tsx | 1 + src/components/location/LocationViewer.tsx | 1 + src/components/location/ShareSettings.tsx | 1 + src/css/location.css | 1 + src/lib/location/types.ts | 1 + src/routes/LocationDashboardRoute.tsx | 1 + src/routes/LocationShareCreate.tsx | 1 + src/routes/LocationShareView.tsx | 1 + switch-worker-env.sh | 1 + wrangler.toml | 9 ++- 15 files changed, 124 insertions(+), 13 deletions(-) diff --git a/FATHOM_INTEGRATION.md b/FATHOM_INTEGRATION.md index 56b2073..96f6a8a 100644 --- a/FATHOM_INTEGRATION.md +++ b/FATHOM_INTEGRATION.md @@ -137,5 +137,6 @@ The Fathom transcript shape includes: + diff --git a/WORKER_ENV_GUIDE.md b/WORKER_ENV_GUIDE.md index 6f13eef..04a405e 100644 --- a/WORKER_ENV_GUIDE.md +++ b/WORKER_ENV_GUIDE.md @@ -88,5 +88,6 @@ You can also manually edit the environment by: + diff --git a/src/automerge/useAutomergeStoreV2.ts b/src/automerge/useAutomergeStoreV2.ts index 5a93ee0..31ef38b 100644 --- a/src/automerge/useAutomergeStoreV2.ts +++ b/src/automerge/useAutomergeStoreV2.ts @@ -321,19 +321,49 @@ export function useAutomergeStoreV2({ // Initialize store with existing records from Automerge if (doc.store) { + const storeKeys = Object.keys(doc.store) + console.log(`📊 Store keys count: ${storeKeys.length}`, storeKeys.slice(0, 10)) + + // Get all store values - Automerge should handle this correctly const allStoreValues = Object.values(doc.store) - console.log("All store values from Automerge:", allStoreValues.map((v: any) => ({ - hasTypeName: !!v?.typeName, - hasId: !!v?.id, - typeName: v?.typeName, - id: v?.id - }))) + + // Debug: Log first few records in detail to see their structure + console.log("📊 Sample store values (first 3):", allStoreValues.slice(0, 3).map((v: any) => { + try { + return { + hasTypeName: !!v?.typeName, + hasId: !!v?.id, + typeName: v?.typeName, + id: v?.id, + type: v?.type, + keys: v ? Object.keys(v).slice(0, 10) : [], + // Try to stringify a sample to see structure + sample: JSON.stringify(v).substring(0, 200) + } + } catch (e) { + return { error: String(e), value: v } + } + })) + + // Debug: Count record types before filtering + const typeCountBefore = allStoreValues.reduce((acc: any, v: any) => { + const type = v?.typeName || 'unknown' + acc[type] = (acc[type] || 0) + 1 + return acc + }, {}) + console.log(`📊 Store values before filtering:`, { + total: allStoreValues.length, + typeCounts: typeCountBefore + }) // Simple filtering - only keep valid TLDraw records // Skip custom record types like obsidian_vault - they're not TLDraw records // Components should read them directly from Automerge (like ObsidianVaultBrowser does) const records = allStoreValues.filter((record: any) => { - if (!record || !record.typeName || !record.id) return false + if (!record || !record.typeName || !record.id) { + console.log(`⚠️ Filtering out invalid record:`, { hasRecord: !!record, hasTypeName: !!record?.typeName, hasId: !!record?.id }) + return false + } // Skip obsidian_vault records - they're not TLDraw records if (record.typeName === 'obsidian_vault' || (typeof record.id === 'string' && record.id.startsWith('obsidian_vault:'))) { @@ -342,6 +372,8 @@ export function useAutomergeStoreV2({ return true }) + console.log(`📊 After filtering: ${records.length} valid records from ${allStoreValues.length} total store values`) + // Only log if there are many records or if debugging is needed if (records.length > 50) { console.log(`Found ${records.length} valid records in Automerge document`) @@ -1244,6 +1276,20 @@ export function useAutomergeStoreV2({ console.log(`Processed ${processedRecords.length} records for loading`) + // Debug: Log what record types we have + const recordTypes = processedRecords.reduce((acc: any, r: any) => { + const type = r.typeName || 'unknown' + acc[type] = (acc[type] || 0) + 1 + return acc + }, {}) + console.log(`📊 Record types breakdown:`, recordTypes) + console.log(`📊 All processed records:`, processedRecords.map((r: any) => ({ + id: r.id, + typeName: r.typeName, + type: r.type, + hasProps: !!r.props + }))) + // Debug: Log shape structures before loading const shapesToLoad = processedRecords.filter(r => r.typeName === 'shape') console.log(`📊 About to load ${shapesToLoad.length} shapes into store`) diff --git a/src/automerge/useAutomergeSync.ts b/src/automerge/useAutomergeSync.ts index db25d80..9ac67e2 100644 --- a/src/automerge/useAutomergeSync.ts +++ b/src/automerge/useAutomergeSync.ts @@ -68,11 +68,65 @@ export function useAutomergeSync(config: AutomergeSyncConfig): TLStoreWithStatus if (r2StoreKeys > 0) { console.log("Loading R2 data into Automerge document") if (existingDoc.store) { - doc.store = existingDoc.store + // Debug: Log what we're about to load + const storeEntries = Object.entries(existingDoc.store) + const shapeCount = storeEntries.filter(([_, v]: [string, any]) => v?.typeName === 'shape').length + console.log("📊 R2 data to load:", { + totalRecords: storeEntries.length, + shapeCount, + recordTypes: storeEntries.reduce((acc: any, [_, v]: [string, any]) => { + const type = v?.typeName || 'unknown' + acc[type] = (acc[type] || 0) + 1 + return acc + }, {}), + sampleRecords: storeEntries.slice(0, 5).map(([k, v]: [string, any]) => ({ + key: k, + id: v?.id, + typeName: v?.typeName, + type: v?.type + })) + }) + + // Initialize store if it doesn't exist + if (!doc.store) { + doc.store = {} + } + + // Assign each record individually with deep copy to ensure Automerge properly handles nested objects + // This matches how records are saved in TLStoreToAutomerge.ts + let assignedCount = 0 + for (const [key, record] of Object.entries(existingDoc.store)) { + try { + // Create a deep copy to ensure Automerge properly handles nested objects + // This is critical for preserving nested structures like props, richText, etc. + const recordToSave = JSON.parse(JSON.stringify(record)) + doc.store[key] = recordToSave + assignedCount++ + } catch (e) { + console.error(`❌ Error deep copying record ${key}:`, e) + // Fallback: assign directly (might not work for nested objects) + doc.store[key] = record + } + } + console.log("Loaded store data into Automerge document:", { loadedStoreKeys: Object.keys(doc.store).length, + assignedCount, sampleLoadedKeys: Object.keys(doc.store).slice(0, 5) }) + + // Verify what was actually loaded + const loadedValues = Object.values(doc.store) + const loadedShapeCount = loadedValues.filter((v: any) => v?.typeName === 'shape').length + console.log("📊 Verification after loading:", { + totalLoaded: loadedValues.length, + loadedShapeCount, + loadedRecordTypes: loadedValues.reduce((acc: any, v: any) => { + const type = v?.typeName || 'unknown' + acc[type] = (acc[type] || 0) + 1 + return acc + }, {}) + }) } if (existingDoc.schema) { doc.schema = existingDoc.schema diff --git a/src/components/location/LocationDashboard.tsx b/src/components/location/LocationDashboard.tsx index 2853fac..b051b88 100644 --- a/src/components/location/LocationDashboard.tsx +++ b/src/components/location/LocationDashboard.tsx @@ -265,5 +265,6 @@ export const LocationDashboard: React.FC = () => { + diff --git a/src/components/location/LocationMap.tsx b/src/components/location/LocationMap.tsx index 3415ad9..998fa7b 100644 --- a/src/components/location/LocationMap.tsx +++ b/src/components/location/LocationMap.tsx @@ -236,5 +236,6 @@ export const LocationMap: React.FC = ({ + diff --git a/src/components/location/LocationViewer.tsx b/src/components/location/LocationViewer.tsx index d568356..7ecf0ba 100644 --- a/src/components/location/LocationViewer.tsx +++ b/src/components/location/LocationViewer.tsx @@ -178,5 +178,6 @@ export const LocationViewer: React.FC = ({ shareToken }) => + diff --git a/src/components/location/ShareSettings.tsx b/src/components/location/ShareSettings.tsx index 9788772..9e2d0e9 100644 --- a/src/components/location/ShareSettings.tsx +++ b/src/components/location/ShareSettings.tsx @@ -145,5 +145,6 @@ export const ShareSettingsComponent: React.FC = ({ onSetting + diff --git a/src/css/location.css b/src/css/location.css index def7d81..caf2732 100644 --- a/src/css/location.css +++ b/src/css/location.css @@ -420,5 +420,6 @@ + diff --git a/src/lib/location/types.ts b/src/lib/location/types.ts index f8ff797..52f8393 100644 --- a/src/lib/location/types.ts +++ b/src/lib/location/types.ts @@ -50,5 +50,6 @@ export interface GeolocationPosition { + diff --git a/src/routes/LocationDashboardRoute.tsx b/src/routes/LocationDashboardRoute.tsx index 0a93be9..fb9c023 100644 --- a/src/routes/LocationDashboardRoute.tsx +++ b/src/routes/LocationDashboardRoute.tsx @@ -32,5 +32,6 @@ export const LocationDashboardRoute: React.FC = () => { + diff --git a/src/routes/LocationShareCreate.tsx b/src/routes/LocationShareCreate.tsx index 4b4cb86..5e3b197 100644 --- a/src/routes/LocationShareCreate.tsx +++ b/src/routes/LocationShareCreate.tsx @@ -32,5 +32,6 @@ export const LocationShareCreate: React.FC = () => { + diff --git a/src/routes/LocationShareView.tsx b/src/routes/LocationShareView.tsx index 4ca7ccf..75d8d3e 100644 --- a/src/routes/LocationShareView.tsx +++ b/src/routes/LocationShareView.tsx @@ -46,5 +46,6 @@ export const LocationShareView: React.FC = () => { + diff --git a/switch-worker-env.sh b/switch-worker-env.sh index 8a94be4..f954c0b 100644 --- a/switch-worker-env.sh +++ b/switch-worker-env.sh @@ -67,5 +67,6 @@ echo " npm run dev" + diff --git a/wrangler.toml b/wrangler.toml index b3763d7..ee7bcdb 100644 --- a/wrangler.toml +++ b/wrangler.toml @@ -23,11 +23,10 @@ bindings = [ tag = "v1" new_classes = ["AutomergeDurableObject"] -[[migrations]] -tag = "v2" -renamed_classes = [ - { from = "TldrawDurableObject", to = "AutomergeDurableObject" } -] +# Note: TldrawDurableObject → AutomergeDurableObject migration removed +# The AutomergeDurableObject class is already in use, so we can't rename to it. +# Any remaining TldrawDurableObject instances will be orphaned but won't cause issues. +# If you need to clean them up, you can add a delete-class migration in the future. [[r2_buckets]] binding = 'TLDRAW_BUCKET'