fix: accept all valid tldraw fractional indices (b1, c10, etc.)
The index validation was incorrectly rejecting valid tldraw fractional indices like "b1", "c10", etc. tldraw's fractional indexing uses: - First letter (a-z) indicates integer part length (a=1 digit, b=2 digits) - Followed by alphanumeric characters for value and jitter This was causing ValidationError on production for Embed shapes with index "b1". Fixed regex in all validation functions to accept any lowercase letter prefix, not just 'a'. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
01b5a84e42
commit
cd58b1c1cd
|
|
@ -11,39 +11,23 @@ function isValidIndexKey(index: string): boolean {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// The first character indicates the integer part length:
|
// tldraw uses fractional indexing where:
|
||||||
// 'a' = 1 digit, 'b' = 2 digits, etc. for positive integers
|
// - First character is a lowercase letter indicating integer part length (a=1, b=2, c=3, etc.)
|
||||||
// 'Z' = 1 digit, 'Y' = 2 digits, etc. for negative integers
|
// - Followed by alphanumeric characters for the value and optional jitter
|
||||||
// But for normal shapes, 'a' followed by a digit is the most common pattern
|
// Examples: "a0", "a1", "b10", "b99", "c100", "a1V4rr", "b10Lz"
|
||||||
|
//
|
||||||
|
// Also uppercase letters for negative indices (Z=1, Y=2, etc.)
|
||||||
|
|
||||||
// Simple invalid patterns that are definitely wrong:
|
// Valid fractional index: lowercase letter followed by alphanumeric characters
|
||||||
// - Just a number like "1", "2"
|
if (/^[a-z][a-zA-Z0-9]+$/.test(index)) {
|
||||||
// - Old format like "b1", "c1" (letter + single digit that's not a valid fractional index)
|
|
||||||
// - Empty or whitespace
|
|
||||||
|
|
||||||
// Valid fractional indices from tldraw start with 'a' for small positive numbers
|
|
||||||
// and follow with digits + optional alphanumeric jitter
|
|
||||||
// Pattern: starts with 'a', followed by at least one digit, then optional alphanumeric chars
|
|
||||||
|
|
||||||
// Simple patterns that are DEFINITELY invalid for tldraw:
|
|
||||||
// "b1", "c1", "d1" etc - these are old non-fractional indices
|
|
||||||
if (/^[b-z]\d$/i.test(index)) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Valid tldraw indices should start with lowercase 'a' followed by digits
|
|
||||||
// and optionally more alphanumeric characters for the fractional part
|
|
||||||
// Examples from actual tldraw: "a0", "a1", "a24sT", "a1V4rr"
|
|
||||||
if (/^a\d/.test(index)) {
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Also allow 'Z' prefix for very high indices (though rare)
|
// Also allow uppercase prefix for negative/very high indices
|
||||||
if (/^Z[a-z]/i.test(index)) {
|
if (/^[A-Z][a-zA-Z0-9]+$/.test(index)) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// If none of the above, it's likely invalid
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -23,13 +23,15 @@ function minimalSanitizeRecord(record: any): any {
|
||||||
// NOTE: Index assignment is handled by assignSequentialIndices() during format conversion
|
// NOTE: Index assignment is handled by assignSequentialIndices() during format conversion
|
||||||
// Here we only ensure index exists with a valid format, not strictly validate
|
// Here we only ensure index exists with a valid format, not strictly validate
|
||||||
// This preserves layer order that was established during conversion
|
// This preserves layer order that was established during conversion
|
||||||
// Valid formats: a1, a2, a10, a1V, a1Lz, etc. (fractional indexing)
|
// tldraw uses fractional indexing: a0, a1, b10, c100, a1V4rr, etc.
|
||||||
|
// - First letter (a-z) indicates integer part length (a=1 digit, b=2 digits, etc.)
|
||||||
|
// - Uppercase (A-Z) for negative/special indices
|
||||||
if (!sanitized.index || typeof sanitized.index !== 'string' || sanitized.index.length === 0) {
|
if (!sanitized.index || typeof sanitized.index !== 'string' || sanitized.index.length === 0) {
|
||||||
// Only assign default if truly missing
|
// Only assign default if truly missing
|
||||||
sanitized.index = 'a1'
|
sanitized.index = 'a1'
|
||||||
} else if (!/^a\d/.test(sanitized.index) && !/^Z[a-z]/i.test(sanitized.index)) {
|
} else if (!/^[a-zA-Z][a-zA-Z0-9]+$/.test(sanitized.index)) {
|
||||||
// Accept any index starting with 'a' + digit, or 'Z' prefix
|
// Accept any letter followed by alphanumeric characters
|
||||||
// Only reset clearly invalid formats
|
// Only reset clearly invalid formats (e.g., numbers, empty, single char)
|
||||||
console.warn(`⚠️ MinimalSanitization: Invalid index format "${sanitized.index}" for shape ${sanitized.id}`)
|
console.warn(`⚠️ MinimalSanitization: Invalid index format "${sanitized.index}" for shape ${sanitized.id}`)
|
||||||
sanitized.index = 'a1'
|
sanitized.index = 'a1'
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,31 +20,23 @@ import { getDocumentId, saveDocumentId } from "./documentIdMapping"
|
||||||
function isValidTldrawIndex(index: string): boolean {
|
function isValidTldrawIndex(index: string): boolean {
|
||||||
if (!index || typeof index !== 'string' || index.length === 0) return false
|
if (!index || typeof index !== 'string' || index.length === 0) return false
|
||||||
|
|
||||||
// The first character indicates the integer part length:
|
// tldraw uses fractional indexing where:
|
||||||
// 'a' = 1 digit, 'b' = 2 digits, etc. for positive integers
|
// - First character is a lowercase letter indicating integer part length (a=1, b=2, c=3, etc.)
|
||||||
// 'Z' = 1 digit, 'Y' = 2 digits, etc. for negative integers
|
// - Followed by alphanumeric characters for the value and optional jitter
|
||||||
// But for normal shapes, 'a' followed by a digit is the most common pattern
|
// Examples: "a0", "a1", "b10", "b99", "c100", "a1V4rr", "b10Lz"
|
||||||
|
//
|
||||||
|
// Also uppercase letters for negative indices (Z=1, Y=2, etc.)
|
||||||
|
|
||||||
// Simple patterns that are DEFINITELY invalid for tldraw:
|
// Valid fractional index: lowercase letter followed by alphanumeric characters
|
||||||
// "b1", "c1", "d1" etc - these are old non-fractional indices (single letter + single digit)
|
if (/^[a-z][a-zA-Z0-9]+$/.test(index)) {
|
||||||
// These were used before tldraw switched to fractional indexing
|
|
||||||
if (/^[b-z]\d$/i.test(index)) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Valid tldraw indices should start with lowercase 'a' followed by digits
|
|
||||||
// and optionally more alphanumeric characters for the fractional/jitter part
|
|
||||||
// Examples from actual tldraw: "a0", "a1", "a24sT", "a1V4rr"
|
|
||||||
if (/^a\d/.test(index)) {
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Also allow 'Z' prefix for very high indices (though rare)
|
// Also allow uppercase prefix for negative/very high indices
|
||||||
if (/^Z[a-z]/i.test(index)) {
|
if (/^[A-Z][a-zA-Z0-9]+$/.test(index)) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// If none of the above, it's likely invalid
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -68,20 +68,18 @@ import "@/css/style.css"
|
||||||
import "@/css/obsidian-browser.css"
|
import "@/css/obsidian-browser.css"
|
||||||
|
|
||||||
// Helper to validate and fix tldraw IndexKey format
|
// Helper to validate and fix tldraw IndexKey format
|
||||||
// Valid: "a0", "a1", "a24sT", "a1V4rr" - Invalid: "b1", "c1" (old format)
|
// tldraw uses fractional indexing: a0, a1, b10, c100, a1V4rr, etc.
|
||||||
|
// - First letter (a-z) indicates integer part length (a=1 digit, b=2 digits, etc.)
|
||||||
|
// - Uppercase (A-Z) for negative/special indices
|
||||||
function sanitizeIndex(index: any): IndexKey {
|
function sanitizeIndex(index: any): IndexKey {
|
||||||
if (!index || typeof index !== 'string' || index.length === 0) {
|
if (!index || typeof index !== 'string' || index.length === 0) {
|
||||||
return 'a1' as IndexKey
|
return 'a1' as IndexKey
|
||||||
}
|
}
|
||||||
// Old format "b1", "c1" etc are invalid (single letter + single digit)
|
// Valid: letter followed by alphanumeric characters
|
||||||
if (/^[b-z]\d$/i.test(index)) {
|
if (/^[a-zA-Z][a-zA-Z0-9]+$/.test(index)) {
|
||||||
return 'a1' as IndexKey
|
|
||||||
}
|
|
||||||
// Valid: starts with 'a' followed by at least one digit
|
|
||||||
if (/^a\d/.test(index)) {
|
|
||||||
return index as IndexKey
|
return index as IndexKey
|
||||||
}
|
}
|
||||||
// Fallback
|
// Fallback for invalid formats
|
||||||
return 'a1' as IndexKey
|
return 'a1' as IndexKey
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -747,11 +747,12 @@ export class AutomergeDurableObject {
|
||||||
shapesNeedingIndex.sort((a, b) => a.originalIndex - b.originalIndex)
|
shapesNeedingIndex.sort((a, b) => a.originalIndex - b.originalIndex)
|
||||||
|
|
||||||
// Check if shapes already have valid indices we should preserve
|
// Check if shapes already have valid indices we should preserve
|
||||||
// Valid index: starts with 'a' followed by digits, optionally followed by alphanumeric jitter
|
// Valid tldraw fractional index: starts with a lowercase letter followed by alphanumeric characters
|
||||||
|
// Examples: a1, a2, b1, c10, a1V, a1Lz, etc. (the letter increments as indices grow)
|
||||||
const isValidIndex = (idx: any): boolean => {
|
const isValidIndex = (idx: any): boolean => {
|
||||||
if (!idx || typeof idx !== 'string' || idx.length === 0) return false
|
if (!idx || typeof idx !== 'string' || idx.length === 0) return false
|
||||||
// Valid fractional index format: a1, a2, a1V, a10, a1Lz, etc.
|
// Valid fractional index format: lowercase letter followed by alphanumeric (a1, b1, c10, a1V, etc.)
|
||||||
if (/^a\d/.test(idx)) return true
|
if (/^[a-z][a-zA-Z0-9]+$/.test(idx)) return true
|
||||||
// Also allow 'Z' prefix for very high indices
|
// Also allow 'Z' prefix for very high indices
|
||||||
if (/^Z[a-z]/i.test(idx)) return true
|
if (/^Z[a-z]/i.test(idx)) return true
|
||||||
return false
|
return false
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue