preserve coordinates

This commit is contained in:
Jeff Emmett 2025-11-10 23:17:16 -08:00
parent 7b15c9af4a
commit 0bea258d39
3 changed files with 25 additions and 182 deletions

View File

@ -360,9 +360,15 @@ export function sanitizeRecord(record: any): TLRecord {
// For shapes, only ensure basic required fields exist // For shapes, only ensure basic required fields exist
if (sanitized.typeName === 'shape') { if (sanitized.typeName === 'shape') {
// Ensure required shape fields exist // Ensure required shape fields exist
// CRITICAL: Check for non-number, null, undefined, or NaN values // CRITICAL: Only set defaults if coordinates are truly missing or invalid
if (typeof sanitized.x !== 'number' || sanitized.x === null || isNaN(sanitized.x)) sanitized.x = 0 // DO NOT overwrite valid coordinates (including 0, which is a valid position)
if (typeof sanitized.y !== 'number' || sanitized.y === null || isNaN(sanitized.y)) sanitized.y = 0 // Only set to 0 if the value is undefined, null, or NaN
if (sanitized.x === undefined || sanitized.x === null || (typeof sanitized.x === 'number' && isNaN(sanitized.x))) {
sanitized.x = 0
}
if (sanitized.y === undefined || sanitized.y === null || (typeof sanitized.y === 'number' && isNaN(sanitized.y))) {
sanitized.y = 0
}
if (typeof sanitized.rotation !== 'number') sanitized.rotation = 0 if (typeof sanitized.rotation !== 'number') sanitized.rotation = 0
if (typeof sanitized.isLocked !== 'boolean') sanitized.isLocked = false if (typeof sanitized.isLocked !== 'boolean') sanitized.isLocked = false
if (typeof sanitized.opacity !== 'number') sanitized.opacity = 1 if (typeof sanitized.opacity !== 'number') sanitized.opacity = 1

View File

@ -368,45 +368,9 @@ export function useAutomergeStoreV2({
if (existingStoreShapes.length > 0) { if (existingStoreShapes.length > 0) {
console.log(`✅ Store already populated from patches (${existingStoreShapes.length} shapes) - using patch-based loading like dev`) console.log(`✅ Store already populated from patches (${existingStoreShapes.length} shapes) - using patch-based loading like dev`)
// CRITICAL: Force editor to see shapes by refreshing them multiple times // REMOVED: Aggressive shape refresh that was causing coordinate loss
// Sometimes the editor needs multiple updates to detect shapes // Shapes should be visible through normal patch application
const refreshShapes = (attempt: number) => { // If shapes aren't visible, it's likely a different issue that refresh won't fix
// CRITICAL: Preserve x and y coordinates when refreshing shapes
const shapesToRefresh = existingStoreShapes.map(s => {
const shapeFromStore = store.get(s.id)
if (shapeFromStore && shapeFromStore.typeName === 'shape') {
// Preserve original x and y from the store shape data
// Type assertion needed because s is TLRecord but we know it's a shape
const sAny = s as any
const shapeFromStoreAny = shapeFromStore as any
const originalX = sAny.x !== undefined && typeof sAny.x === 'number' && !isNaN(sAny.x) ? sAny.x : shapeFromStoreAny.x
const originalY = sAny.y !== undefined && typeof sAny.y === 'number' && !isNaN(sAny.y) ? sAny.y : shapeFromStoreAny.y
// Ensure x and y are preserved
if (typeof originalX === 'number' && !isNaN(originalX) && typeof originalY === 'number' && !isNaN(originalY)) {
return { ...shapeFromStore, x: originalX, y: originalY } as TLRecord
}
return shapeFromStore
}
return null
}).filter((s): s is TLRecord => s !== null && s.typeName === 'shape')
if (shapesToRefresh.length > 0) {
store.mergeRemoteChanges(() => {
// Re-put shapes to trigger editor update
store.put(shapesToRefresh)
})
console.log(`📊 Refreshed ${shapesToRefresh.length} existing shapes (attempt ${attempt}) to ensure editor visibility`)
// Try multiple times to ensure editor picks them up
if (attempt < 3) {
setTimeout(() => refreshShapes(attempt + 1), 200)
}
}
}
// Start refreshing after a short delay
setTimeout(() => refreshShapes(1), 100)
setStoreWithStatus({ setStoreWithStatus({
store, store,
@ -435,45 +399,8 @@ export function useAutomergeStoreV2({
if (currentShapes.length > 0) { if (currentShapes.length > 0) {
console.log(`✅ Patches applied successfully: ${currentShapes.length} shapes loaded via patches`) console.log(`✅ Patches applied successfully: ${currentShapes.length} shapes loaded via patches`)
// CRITICAL: Force editor to see shapes by refreshing them multiple times // REMOVED: Aggressive shape refresh that was causing coordinate loss
// Sometimes the editor needs multiple updates to detect shapes // Shapes loaded via patches should be visible without forced refresh
const refreshShapes = (attempt: number) => {
// CRITICAL: Preserve x and y coordinates when refreshing shapes
const shapesToRefresh = currentShapes.map(s => {
const shapeFromStore = store.get(s.id)
if (shapeFromStore && shapeFromStore.typeName === 'shape') {
// Preserve original x and y from the store shape data
// Type assertion needed because s is TLRecord but we know it's a shape
const sAny = s as any
const shapeFromStoreAny = shapeFromStore as any
const originalX = sAny.x !== undefined && typeof sAny.x === 'number' && !isNaN(sAny.x) ? sAny.x : shapeFromStoreAny.x
const originalY = sAny.y !== undefined && typeof sAny.y === 'number' && !isNaN(sAny.y) ? sAny.y : shapeFromStoreAny.y
// Ensure x and y are preserved
if (typeof originalX === 'number' && !isNaN(originalX) && typeof originalY === 'number' && !isNaN(originalY)) {
return { ...shapeFromStore, x: originalX, y: originalY } as TLRecord
}
return shapeFromStore
}
return null
}).filter((s): s is TLRecord => s !== null && s.typeName === 'shape')
if (shapesToRefresh.length > 0) {
store.mergeRemoteChanges(() => {
// Re-put shapes to trigger editor update
store.put(shapesToRefresh)
})
console.log(`📊 Refreshed ${shapesToRefresh.length} shapes (attempt ${attempt}) to ensure editor visibility`)
// Try multiple times to ensure editor picks them up
if (attempt < 3) {
setTimeout(() => refreshShapes(attempt + 1), 200)
}
}
}
// Start refreshing after a short delay
setTimeout(() => refreshShapes(1), 100)
setStoreWithStatus({ setStoreWithStatus({
store, store,
@ -534,46 +461,8 @@ export function useAutomergeStoreV2({
}) })
console.log(`✅ Applied ${allRecords.length} records directly to store (fallback for missed patches - works in dev and production)`) console.log(`✅ Applied ${allRecords.length} records directly to store (fallback for missed patches - works in dev and production)`)
// CRITICAL: Force editor to see shapes by refreshing them multiple times // REMOVED: Aggressive shape refresh that was causing coordinate loss
// Sometimes the editor needs multiple updates to detect shapes // Shapes loaded directly should be visible without forced refresh
const refreshShapes = (attempt: number) => {
const shapes = store.allRecords().filter((r: any) => r.typeName === 'shape')
// CRITICAL: Preserve x and y coordinates when refreshing shapes
const shapesToRefresh = shapes.map(s => {
const shapeFromStore = store.get(s.id)
if (shapeFromStore && shapeFromStore.typeName === 'shape') {
// Preserve original x and y from the store shape data
// Type assertion needed because s is TLRecord but we know it's a shape
const sAny = s as any
const shapeFromStoreAny = shapeFromStore as any
const originalX = sAny.x !== undefined && typeof sAny.x === 'number' && !isNaN(sAny.x) ? sAny.x : shapeFromStoreAny.x
const originalY = sAny.y !== undefined && typeof sAny.y === 'number' && !isNaN(sAny.y) ? sAny.y : shapeFromStoreAny.y
// Ensure x and y are preserved
if (typeof originalX === 'number' && !isNaN(originalX) && typeof originalY === 'number' && !isNaN(originalY)) {
return { ...shapeFromStore, x: originalX, y: originalY } as TLRecord
}
return shapeFromStore
}
return null
}).filter((s): s is TLRecord => s !== null && s.typeName === 'shape')
if (shapesToRefresh.length > 0) {
store.mergeRemoteChanges(() => {
// Re-put shapes to trigger editor update
store.put(shapesToRefresh)
})
console.log(`📊 Refreshed ${shapesToRefresh.length} shapes (attempt ${attempt}) to ensure editor visibility`)
// Try multiple times to ensure editor picks them up
if (attempt < 3) {
setTimeout(() => refreshShapes(attempt + 1), 200)
}
}
}
// Start refreshing after a short delay
setTimeout(() => refreshShapes(1), 100)
} }
} catch (error) { } catch (error) {
console.error(`❌ Error applying records directly:`, error) console.error(`❌ Error applying records directly:`, error)

View File

@ -282,34 +282,12 @@ export function Board() {
console.log(`📊 Board: Shape parent ID distribution:`, Array.from(parentIdCounts.entries())) console.log(`📊 Board: Shape parent ID distribution:`, Array.from(parentIdCounts.entries()))
} }
// CRITICAL: If shapes are in store but editor sees 0, force refresh // REMOVED: Aggressive force refresh that was causing coordinate loss
// This handles the case where old bulk loading code doesn't refresh shapes // If shapes are in store but editor doesn't see them, it's likely a different issue
if (storeShapes.length > 0 && editorShapes.length === 0 && storeShapesOnCurrentPage.length > 0 && store.store) { // Forcing refresh by re-putting was resetting coordinates to 0,0
console.warn(`⚠️ Board: ${storeShapes.length} shapes in store (${storeShapesOnCurrentPage.length} on current page) but editor sees 0. Forcing refresh...`) if (storeShapes.length > 0 && editorShapes.length === 0 && storeShapesOnCurrentPage.length > 0) {
console.warn(`⚠️ Board: ${storeShapes.length} shapes in store (${storeShapesOnCurrentPage.length} on current page) but editor sees 0. This may indicate a sync issue.`)
// Force refresh by re-putting shapes with mergeRemoteChanges // Don't force refresh - it was causing coordinate loss
// CRITICAL: Preserve x and y coordinates when refreshing shapes
const shapesToRefresh = storeShapesOnCurrentPage.map((s: any) => {
const shapeFromStore = store.store!.get(s.id)
if (shapeFromStore && shapeFromStore.typeName === 'shape') {
// Preserve original x and y from the store shape data
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
// Ensure x and y are preserved
if (typeof originalX === 'number' && !isNaN(originalX) && typeof originalY === 'number' && !isNaN(originalY)) {
return { ...shapeFromStore, x: originalX, y: originalY } as TLRecord
}
return shapeFromStore
}
return null
}).filter((s): s is TLRecord => s !== null && s.typeName === 'shape')
if (shapesToRefresh.length > 0) {
store.store.mergeRemoteChanges(() => {
store.store!.put(shapesToRefresh)
})
console.log(`🔄 Board: Forced refresh of ${shapesToRefresh.length} shapes to make editor see them`)
}
} }
// Check if there are shapes in store on current page that editor can't see // Check if there are shapes in store on current page that editor can't see
@ -343,39 +321,9 @@ export function Board() {
// Shapes don't exist in editor - might be a sync issue // Shapes don't exist in editor - might be a sync issue
console.error(`📊 Board: ${missingShapes.length} shapes are in store but don't exist in editor - possible sync issue`) console.error(`📊 Board: ${missingShapes.length} shapes are in store but don't exist in editor - possible sync issue`)
// Try to force a refresh by updating the store // REMOVED: Force refresh that was causing coordinate loss
// This might help if shapes are stuck in a validation error state // Re-putting shapes was resetting coordinates to 0,0
console.log(`📊 Board: Attempting to refresh store to make shapes visible`) console.log(`📊 Board: ${missingShapes.length} shapes are in store but not visible in editor - this may indicate a sync issue`)
try {
// Force a store update by reading and re-putting the shapes
if (!store.store) return
const currentStore = store.store
const shapesToRefresh = missingShapes.slice(0, 10) // Limit to first 10 to avoid performance issues
// CRITICAL: Preserve x and y coordinates when refreshing shapes
const refreshedShapes = shapesToRefresh.map((s: any) => {
const shapeFromStore = currentStore.get(s.id)
if (shapeFromStore && shapeFromStore.typeName === 'shape') {
// Preserve original x and y from the store shape data
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
// Ensure x and y are preserved
if (typeof originalX === 'number' && !isNaN(originalX) && typeof originalY === 'number' && !isNaN(originalY)) {
return { ...shapeFromStore, x: originalX, y: originalY } as TLRecord
}
return shapeFromStore
}
return null
}).filter((s): s is TLRecord => s !== null && s.typeName === 'shape')
if (refreshedShapes.length > 0) {
console.log(`📊 Board: Refreshing ${refreshedShapes.length} shapes in store`)
currentStore.put(refreshedShapes)
}
} catch (error) {
console.error(`📊 Board: Error refreshing shapes:`, error)
}
} }
// Check if shapes are outside viewport // Check if shapes are outside viewport