preserve coordinates
This commit is contained in:
parent
eafbf6c9fe
commit
0e90e2d097
|
|
@ -135,8 +135,10 @@ export function applyAutomergePatchesToTLStore(
|
||||||
}
|
}
|
||||||
|
|
||||||
// CRITICAL: Store original x and y before patch application to preserve them
|
// CRITICAL: Store original x and y before patch application to preserve them
|
||||||
|
// We need to preserve coordinates from existing records to prevent them from being reset
|
||||||
const originalX = (record.typeName === 'shape' && typeof record.x === 'number' && !isNaN(record.x)) ? record.x : undefined
|
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
|
const originalY = (record.typeName === 'shape' && typeof record.y === 'number' && !isNaN(record.y)) ? record.y : undefined
|
||||||
|
const hadOriginalCoordinates = originalX !== undefined && originalY !== undefined
|
||||||
|
|
||||||
switch (patch.action) {
|
switch (patch.action) {
|
||||||
case "insert": {
|
case "insert": {
|
||||||
|
|
@ -172,19 +174,42 @@ export function applyAutomergePatchesToTLStore(
|
||||||
}
|
}
|
||||||
|
|
||||||
// CRITICAL: After patch application, ensure x and y coordinates are preserved for shapes
|
// CRITICAL: After patch application, ensure x and y coordinates are preserved for shapes
|
||||||
|
// This prevents coordinates from being reset to 0,0 when patches don't include them
|
||||||
if (updatedObjects[id] && updatedObjects[id].typeName === 'shape') {
|
if (updatedObjects[id] && updatedObjects[id].typeName === 'shape') {
|
||||||
const patchedRecord = updatedObjects[id]
|
const patchedRecord = updatedObjects[id]
|
||||||
// Preserve original x and y if they were valid, otherwise use defaults
|
const patchedX = (patchedRecord as any).x
|
||||||
if (originalX !== undefined && (typeof patchedRecord.x !== 'number' || patchedRecord.x === null || isNaN(patchedRecord.x))) {
|
const patchedY = (patchedRecord as any).y
|
||||||
updatedObjects[id] = { ...patchedRecord, x: originalX }
|
const patchedHasValidX = typeof patchedX === 'number' && !isNaN(patchedX) && patchedX !== null && patchedX !== undefined
|
||||||
} else if (typeof patchedRecord.x !== 'number' || patchedRecord.x === null || isNaN(patchedRecord.x)) {
|
const patchedHasValidY = typeof patchedY === 'number' && !isNaN(patchedY) && patchedY !== null && patchedY !== undefined
|
||||||
updatedObjects[id] = { ...patchedRecord, x: defaultRecord.x || 0 }
|
|
||||||
}
|
|
||||||
|
|
||||||
if (originalY !== undefined && (typeof patchedRecord.y !== 'number' || patchedRecord.y === null || isNaN(patchedRecord.y))) {
|
// CRITICAL: If we had original coordinates, preserve them unless patch explicitly set different valid coordinates
|
||||||
updatedObjects[id] = { ...patchedRecord, y: originalY }
|
// This prevents coordinates from collapsing to 0,0 after bulk upload
|
||||||
} else if (typeof patchedRecord.y !== 'number' || patchedRecord.y === null || isNaN(patchedRecord.y)) {
|
if (hadOriginalCoordinates) {
|
||||||
updatedObjects[id] = { ...patchedRecord, y: defaultRecord.y || 0 }
|
// Only use patched coordinates if they're explicitly set and different from original
|
||||||
|
// Otherwise, preserve the original coordinates
|
||||||
|
if (patchedHasValidX && patchedX !== originalX) {
|
||||||
|
// Patch explicitly set a different X coordinate - use it
|
||||||
|
updatedObjects[id] = { ...patchedRecord, x: patchedX }
|
||||||
|
} else {
|
||||||
|
// Preserve original X coordinate
|
||||||
|
updatedObjects[id] = { ...patchedRecord, x: originalX }
|
||||||
|
}
|
||||||
|
|
||||||
|
if (patchedHasValidY && patchedY !== originalY) {
|
||||||
|
// Patch explicitly set a different Y coordinate - use it
|
||||||
|
updatedObjects[id] = { ...updatedObjects[id], y: patchedY } as TLRecord
|
||||||
|
} else {
|
||||||
|
// Preserve original Y coordinate
|
||||||
|
updatedObjects[id] = { ...updatedObjects[id], y: originalY } as TLRecord
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// No original coordinates - use patched values or defaults
|
||||||
|
if (!patchedHasValidX) {
|
||||||
|
updatedObjects[id] = { ...patchedRecord, x: defaultRecord.x || 0 }
|
||||||
|
}
|
||||||
|
if (!patchedHasValidY) {
|
||||||
|
updatedObjects[id] = { ...updatedObjects[id], y: defaultRecord.y || 0 } as TLRecord
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -55,8 +55,15 @@ function sanitizeRecord(record: TLRecord): TLRecord {
|
||||||
|
|
||||||
// Ensure required top-level fields exist
|
// Ensure required top-level fields exist
|
||||||
if (sanitized.typeName === 'shape') {
|
if (sanitized.typeName === 'shape') {
|
||||||
if (typeof sanitized.x !== 'number') sanitized.x = 0
|
// CRITICAL: Only set defaults if coordinates are truly missing or invalid
|
||||||
if (typeof sanitized.y !== 'number') sanitized.y = 0
|
// DO NOT overwrite valid coordinates (including 0, which is a valid position)
|
||||||
|
// 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
|
||||||
|
|
@ -311,8 +318,29 @@ export function applyTLStoreChangesToAutomerge(
|
||||||
// Handle added records
|
// Handle added records
|
||||||
if (changes.added) {
|
if (changes.added) {
|
||||||
Object.values(changes.added).forEach((record) => {
|
Object.values(changes.added).forEach((record) => {
|
||||||
|
// CRITICAL: For shapes, preserve x and y coordinates before sanitization
|
||||||
|
// This ensures coordinates aren't lost when saving to Automerge
|
||||||
|
let originalX: number | undefined = undefined
|
||||||
|
let originalY: number | undefined = undefined
|
||||||
|
if (record.typeName === 'shape') {
|
||||||
|
originalX = (record as any).x
|
||||||
|
originalY = (record as any).y
|
||||||
|
}
|
||||||
|
|
||||||
// Sanitize record before saving to ensure all required fields are present
|
// Sanitize record before saving to ensure all required fields are present
|
||||||
const sanitizedRecord = sanitizeRecord(record)
|
const sanitizedRecord = sanitizeRecord(record)
|
||||||
|
|
||||||
|
// CRITICAL: Restore original coordinates if they were valid
|
||||||
|
// This prevents coordinates from being reset to 0,0 when saving to Automerge
|
||||||
|
if (record.typeName === 'shape' && originalX !== undefined && originalY !== undefined) {
|
||||||
|
if (typeof originalX === 'number' && !isNaN(originalX) && originalX !== null) {
|
||||||
|
(sanitizedRecord as any).x = originalX
|
||||||
|
}
|
||||||
|
if (typeof originalY === 'number' && !isNaN(originalY) && originalY !== null) {
|
||||||
|
(sanitizedRecord as any).y = originalY
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// CRITICAL: Create a deep copy to ensure all properties (including richText and text) are preserved
|
// CRITICAL: Create a deep copy to ensure all properties (including richText and text) are preserved
|
||||||
// This prevents Automerge from treating the object as read-only
|
// This prevents Automerge from treating the object as read-only
|
||||||
const recordToSave = JSON.parse(JSON.stringify(sanitizedRecord))
|
const recordToSave = JSON.parse(JSON.stringify(sanitizedRecord))
|
||||||
|
|
@ -326,6 +354,14 @@ export function applyTLStoreChangesToAutomerge(
|
||||||
// This is simpler than deep comparison and leverages Automerge's conflict resolution
|
// This is simpler than deep comparison and leverages Automerge's conflict resolution
|
||||||
if (changes.updated) {
|
if (changes.updated) {
|
||||||
Object.values(changes.updated).forEach(([_, record]) => {
|
Object.values(changes.updated).forEach(([_, record]) => {
|
||||||
|
// CRITICAL: For shapes, preserve x and y coordinates before sanitization
|
||||||
|
// This ensures coordinates aren't lost when updating records in Automerge
|
||||||
|
let originalX: number | undefined = undefined
|
||||||
|
let originalY: number | undefined = undefined
|
||||||
|
if (record.typeName === 'shape') {
|
||||||
|
originalX = (record as any).x
|
||||||
|
originalY = (record as any).y
|
||||||
|
}
|
||||||
// DEBUG: Log richText, meta.text, and Obsidian note properties before sanitization
|
// DEBUG: Log richText, meta.text, and Obsidian note properties before sanitization
|
||||||
if (record.typeName === 'shape') {
|
if (record.typeName === 'shape') {
|
||||||
if (record.type === 'geo' && (record.props as any)?.richText) {
|
if (record.type === 'geo' && (record.props as any)?.richText) {
|
||||||
|
|
@ -371,6 +407,17 @@ export function applyTLStoreChangesToAutomerge(
|
||||||
|
|
||||||
const sanitizedRecord = sanitizeRecord(record)
|
const sanitizedRecord = sanitizeRecord(record)
|
||||||
|
|
||||||
|
// CRITICAL: Restore original coordinates if they were valid
|
||||||
|
// This prevents coordinates from being reset to 0,0 when updating records in Automerge
|
||||||
|
if (record.typeName === 'shape' && originalX !== undefined && originalY !== undefined) {
|
||||||
|
if (typeof originalX === 'number' && !isNaN(originalX) && originalX !== null) {
|
||||||
|
(sanitizedRecord as any).x = originalX
|
||||||
|
}
|
||||||
|
if (typeof originalY === 'number' && !isNaN(originalY) && originalY !== null) {
|
||||||
|
(sanitizedRecord as any).y = originalY
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// DEBUG: Log richText, meta.text, and Obsidian note properties after sanitization
|
// DEBUG: Log richText, meta.text, and Obsidian note properties after sanitization
|
||||||
if (sanitizedRecord.typeName === 'shape') {
|
if (sanitizedRecord.typeName === 'shape') {
|
||||||
if (sanitizedRecord.type === 'geo' && (sanitizedRecord.props as any)?.richText) {
|
if (sanitizedRecord.type === 'geo' && (sanitizedRecord.props as any)?.richText) {
|
||||||
|
|
|
||||||
|
|
@ -436,23 +436,36 @@ export function useAutomergeStoreV2({
|
||||||
// Create a clean copy of the record
|
// Create a clean copy of the record
|
||||||
const cleanRecord = JSON.parse(JSON.stringify(record))
|
const cleanRecord = JSON.parse(JSON.stringify(record))
|
||||||
|
|
||||||
// CRITICAL: For shapes, preserve x and y coordinates BEFORE sanitization
|
// CRITICAL: For shapes, preserve x and y coordinates
|
||||||
// This ensures coordinates aren't lost during the sanitization process
|
// We MUST preserve coordinates - they should never be reset to 0,0 unless truly missing
|
||||||
if (cleanRecord.typeName === 'shape') {
|
if (cleanRecord.typeName === 'shape') {
|
||||||
|
// Store original coordinates BEFORE any processing
|
||||||
const originalX = cleanRecord.x
|
const originalX = cleanRecord.x
|
||||||
const originalY = cleanRecord.y
|
const originalY = cleanRecord.y
|
||||||
|
const hadValidX = typeof originalX === 'number' && !isNaN(originalX) && originalX !== null && originalX !== undefined
|
||||||
|
const hadValidY = typeof originalY === 'number' && !isNaN(originalY) && originalY !== null && originalY !== undefined
|
||||||
|
|
||||||
// Use the same sanitizeRecord function that patches use
|
// Use the same sanitizeRecord function that patches use
|
||||||
// This ensures consistency between dev and production
|
// This ensures consistency between dev and production
|
||||||
const sanitized = sanitizeRecord(cleanRecord)
|
const sanitized = sanitizeRecord(cleanRecord)
|
||||||
|
|
||||||
// CRITICAL: Restore original coordinates if they were valid
|
// CRITICAL: ALWAYS restore original coordinates if they were valid
|
||||||
// sanitizeRecord only sets defaults if coordinates are missing/invalid
|
// Even if sanitizeRecord preserved them, we ensure they're correct
|
||||||
// But we want to preserve the original values if they exist
|
// This prevents any possibility of coordinates being reset
|
||||||
if (typeof originalX === 'number' && !isNaN(originalX) && originalX !== null && originalX !== undefined) {
|
if (hadValidX) {
|
||||||
(sanitized as any).x = originalX
|
(sanitized as any).x = originalX
|
||||||
}
|
}
|
||||||
if (typeof originalY === 'number' && !isNaN(originalY) && originalY !== null && originalY !== undefined) {
|
if (hadValidY) {
|
||||||
|
(sanitized as any).y = originalY
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log if coordinates were changed (for debugging)
|
||||||
|
if (hadValidX && (sanitized as any).x !== originalX) {
|
||||||
|
console.warn(`⚠️ Coordinate X was changed during sanitization for shape ${cleanRecord.id}: ${originalX} -> ${(sanitized as any).x}. Restored to ${originalX}.`)
|
||||||
|
(sanitized as any).x = originalX
|
||||||
|
}
|
||||||
|
if (hadValidY && (sanitized as any).y !== originalY) {
|
||||||
|
console.warn(`⚠️ Coordinate Y was changed during sanitization for shape ${cleanRecord.id}: ${originalY} -> ${(sanitized as any).y}. Restored to ${originalY}.`)
|
||||||
(sanitized as any).y = originalY
|
(sanitized as any).y = originalY
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue