prevent coordinate reset

This commit is contained in:
Jeff Emmett 2025-11-10 23:01:35 -08:00
parent 5a8bfa41d2
commit 857e94fe6a
4 changed files with 104 additions and 10 deletions

View File

@ -134,6 +134,10 @@ export function applyAutomergePatchesToTLStore(
}
}
// CRITICAL: Store original x and y before patch application to preserve them
const originalX = (record.typeName === 'shape' && typeof record.x === 'number' && !isNaN(record.x)) ? record.x : undefined
const originalY = (record.typeName === 'shape' && typeof record.y === 'number' && !isNaN(record.y)) ? record.y : undefined
switch (patch.action) {
case "insert": {
updatedObjects[id] = applyInsertToObject(patch, record)
@ -167,6 +171,23 @@ export function applyAutomergePatchesToTLStore(
}
}
// CRITICAL: After patch application, ensure x and y coordinates are preserved for shapes
if (updatedObjects[id] && updatedObjects[id].typeName === 'shape') {
const patchedRecord = updatedObjects[id]
// Preserve original x and y if they were valid, otherwise use defaults
if (originalX !== undefined && (typeof patchedRecord.x !== 'number' || patchedRecord.x === null || isNaN(patchedRecord.x))) {
updatedObjects[id] = { ...patchedRecord, x: originalX }
} else if (typeof patchedRecord.x !== 'number' || patchedRecord.x === null || isNaN(patchedRecord.x)) {
updatedObjects[id] = { ...patchedRecord, x: defaultRecord.x || 0 }
}
if (originalY !== undefined && (typeof patchedRecord.y !== 'number' || patchedRecord.y === null || isNaN(patchedRecord.y))) {
updatedObjects[id] = { ...patchedRecord, y: originalY }
} else if (typeof patchedRecord.y !== 'number' || patchedRecord.y === null || isNaN(patchedRecord.y)) {
updatedObjects[id] = { ...patchedRecord, y: defaultRecord.y || 0 }
}
}
// CRITICAL: Re-check typeName after patch application to ensure it's still correct
// Note: obsidian_vault records are skipped above, so we don't need to handle them here
})

View File

@ -371,7 +371,23 @@ export function useAutomergeStoreV2({
// CRITICAL: Force editor to see shapes by refreshing them multiple times
// Sometimes the editor needs multiple updates to detect shapes
const refreshShapes = (attempt: number) => {
const shapesToRefresh = existingStoreShapes.map(s => store.get(s.id)).filter((s): s is TLRecord => s !== undefined && s.typeName === 'shape')
// 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
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.mergeRemoteChanges(() => {
// Re-put shapes to trigger editor update
@ -419,7 +435,23 @@ export function useAutomergeStoreV2({
// CRITICAL: Force editor to see shapes by refreshing them multiple times
// Sometimes the editor needs multiple updates to detect shapes
const refreshShapes = (attempt: number) => {
const shapesToRefresh = currentShapes.map(s => store.get(s.id)).filter((s): s is TLRecord => s !== undefined && s.typeName === 'shape')
// 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
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.mergeRemoteChanges(() => {
// Re-put shapes to trigger editor update
@ -500,7 +532,23 @@ export function useAutomergeStoreV2({
// Sometimes the editor needs multiple updates to detect shapes
const refreshShapes = (attempt: number) => {
const shapes = store.allRecords().filter((r: any) => r.typeName === 'shape')
const shapesToRefresh = shapes.map(s => store.get(s.id)).filter((s): s is TLRecord => s !== undefined && s.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
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.mergeRemoteChanges(() => {
// Re-put shapes to trigger editor update

View File

@ -288,7 +288,22 @@ export function Board() {
console.warn(`⚠️ Board: ${storeShapes.length} shapes in store (${storeShapesOnCurrentPage.length} on current page) but editor sees 0. Forcing refresh...`)
// Force refresh by re-putting shapes with mergeRemoteChanges
const shapesToRefresh = storeShapesOnCurrentPage.map((s: any) => store.store!.get(s.id)).filter((s): s is TLRecord => s !== undefined && s.typeName === 'shape')
// 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)
@ -337,9 +352,18 @@ export function Board() {
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

View File

@ -202,13 +202,14 @@ export function CustomMainMenu() {
}
// CRITICAL: Preserve existing coordinates - only set defaults if truly missing
// x/y can be 0, which is a valid coordinate, so check for undefined/null
// Note: validateShapeGeometry already ensures x/y are valid numbers
if (fixedShape.x === undefined || fixedShape.x === null) {
fixedShape.x = Math.random() * 400 + 50 // Random position only if missing
// x/y can be 0, which is a valid coordinate, so check for undefined/null/NaN
// Note: validateShapeGeometry already ensures x/y are valid numbers, but we need to
// handle the case where they might be NaN or Infinity after validation
if (fixedShape.x === undefined || fixedShape.x === null || isNaN(fixedShape.x) || !isFinite(fixedShape.x)) {
fixedShape.x = Math.random() * 400 + 50 // Random position only if missing or invalid
}
if (fixedShape.y === undefined || fixedShape.y === null) {
fixedShape.y = Math.random() * 300 + 50 // Random position only if missing
if (fixedShape.y === undefined || fixedShape.y === null || isNaN(fixedShape.y) || !isFinite(fixedShape.y)) {
fixedShape.y = Math.random() * 300 + 50 // Random position only if missing or invalid
}
// Preserve rotation, isLocked, opacity - only set defaults if missing