@tldraw/tlschema 4.2.1 → 4.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (117) hide show
  1. package/dist-cjs/bindings/TLBaseBinding.js.map +2 -2
  2. package/dist-cjs/createTLSchema.js.map +2 -2
  3. package/dist-cjs/index.d.ts +242 -71
  4. package/dist-cjs/index.js +4 -1
  5. package/dist-cjs/index.js.map +2 -2
  6. package/dist-cjs/misc/TLOpacity.js +1 -5
  7. package/dist-cjs/misc/TLOpacity.js.map +2 -2
  8. package/dist-cjs/misc/TLRichText.js +5 -1
  9. package/dist-cjs/misc/TLRichText.js.map +2 -2
  10. package/dist-cjs/misc/b64Vecs.js +224 -0
  11. package/dist-cjs/misc/b64Vecs.js.map +7 -0
  12. package/dist-cjs/records/TLAsset.js.map +1 -1
  13. package/dist-cjs/records/TLBinding.js.map +2 -2
  14. package/dist-cjs/records/TLShape.js.map +2 -2
  15. package/dist-cjs/shapes/ShapeWithCrop.js.map +1 -1
  16. package/dist-cjs/shapes/TLArrowShape.js +26 -13
  17. package/dist-cjs/shapes/TLArrowShape.js.map +2 -2
  18. package/dist-cjs/shapes/TLBaseShape.js.map +2 -2
  19. package/dist-cjs/shapes/TLDrawShape.js +37 -4
  20. package/dist-cjs/shapes/TLDrawShape.js.map +2 -2
  21. package/dist-cjs/shapes/TLEmbedShape.js +17 -0
  22. package/dist-cjs/shapes/TLEmbedShape.js.map +2 -2
  23. package/dist-cjs/shapes/TLGeoShape.js +12 -1
  24. package/dist-cjs/shapes/TLGeoShape.js.map +2 -2
  25. package/dist-cjs/shapes/TLHighlightShape.js +29 -2
  26. package/dist-cjs/shapes/TLHighlightShape.js.map +2 -2
  27. package/dist-cjs/shapes/TLNoteShape.js +12 -1
  28. package/dist-cjs/shapes/TLNoteShape.js.map +2 -2
  29. package/dist-cjs/shapes/TLTextShape.js +12 -1
  30. package/dist-cjs/shapes/TLTextShape.js.map +2 -2
  31. package/dist-cjs/store-migrations.js +15 -15
  32. package/dist-cjs/store-migrations.js.map +2 -2
  33. package/dist-esm/bindings/TLBaseBinding.mjs.map +2 -2
  34. package/dist-esm/createTLSchema.mjs.map +2 -2
  35. package/dist-esm/index.d.mts +242 -71
  36. package/dist-esm/index.mjs +5 -1
  37. package/dist-esm/index.mjs.map +2 -2
  38. package/dist-esm/misc/TLOpacity.mjs +1 -5
  39. package/dist-esm/misc/TLOpacity.mjs.map +2 -2
  40. package/dist-esm/misc/TLRichText.mjs +5 -1
  41. package/dist-esm/misc/TLRichText.mjs.map +2 -2
  42. package/dist-esm/misc/b64Vecs.mjs +204 -0
  43. package/dist-esm/misc/b64Vecs.mjs.map +7 -0
  44. package/dist-esm/records/TLAsset.mjs.map +1 -1
  45. package/dist-esm/records/TLBinding.mjs.map +2 -2
  46. package/dist-esm/records/TLShape.mjs.map +2 -2
  47. package/dist-esm/shapes/TLArrowShape.mjs +26 -13
  48. package/dist-esm/shapes/TLArrowShape.mjs.map +2 -2
  49. package/dist-esm/shapes/TLBaseShape.mjs.map +2 -2
  50. package/dist-esm/shapes/TLDrawShape.mjs +37 -4
  51. package/dist-esm/shapes/TLDrawShape.mjs.map +2 -2
  52. package/dist-esm/shapes/TLEmbedShape.mjs +17 -0
  53. package/dist-esm/shapes/TLEmbedShape.mjs.map +2 -2
  54. package/dist-esm/shapes/TLGeoShape.mjs +12 -1
  55. package/dist-esm/shapes/TLGeoShape.mjs.map +2 -2
  56. package/dist-esm/shapes/TLHighlightShape.mjs +29 -2
  57. package/dist-esm/shapes/TLHighlightShape.mjs.map +2 -2
  58. package/dist-esm/shapes/TLNoteShape.mjs +12 -1
  59. package/dist-esm/shapes/TLNoteShape.mjs.map +2 -2
  60. package/dist-esm/shapes/TLTextShape.mjs +12 -1
  61. package/dist-esm/shapes/TLTextShape.mjs.map +2 -2
  62. package/dist-esm/store-migrations.mjs +15 -15
  63. package/dist-esm/store-migrations.mjs.map +2 -2
  64. package/package.json +8 -8
  65. package/src/__tests__/migrationTestUtils.ts +9 -3
  66. package/src/bindings/TLBaseBinding.ts +25 -14
  67. package/src/createTLSchema.ts +8 -2
  68. package/src/index.ts +9 -0
  69. package/src/migrations.test.ts +149 -1
  70. package/src/misc/TLOpacity.ts +1 -5
  71. package/src/misc/TLRichText.ts +6 -1
  72. package/src/misc/b64Vecs.ts +308 -0
  73. package/src/records/TLAsset.ts +2 -2
  74. package/src/records/TLBinding.ts +65 -23
  75. package/src/records/TLShape.ts +100 -5
  76. package/src/shapes/ShapeWithCrop.ts +2 -2
  77. package/src/shapes/TLArrowShape.ts +28 -14
  78. package/src/shapes/TLBaseShape.ts +34 -10
  79. package/src/shapes/TLDrawShape.ts +59 -12
  80. package/src/shapes/TLEmbedShape.ts +17 -0
  81. package/src/shapes/TLGeoShape.ts +14 -1
  82. package/src/shapes/TLHighlightShape.ts +37 -0
  83. package/src/shapes/TLNoteShape.ts +15 -1
  84. package/src/shapes/TLTextShape.ts +16 -2
  85. package/src/store-migrations.ts +17 -16
  86. package/src/assets/TLBookmarkAsset.test.ts +0 -96
  87. package/src/assets/TLImageAsset.test.ts +0 -213
  88. package/src/assets/TLVideoAsset.test.ts +0 -105
  89. package/src/bindings/TLArrowBinding.test.ts +0 -55
  90. package/src/misc/id-validator.test.ts +0 -50
  91. package/src/records/TLAsset.test.ts +0 -234
  92. package/src/records/TLBinding.test.ts +0 -22
  93. package/src/records/TLCamera.test.ts +0 -19
  94. package/src/records/TLDocument.test.ts +0 -35
  95. package/src/records/TLInstance.test.ts +0 -201
  96. package/src/records/TLPage.test.ts +0 -110
  97. package/src/records/TLPageState.test.ts +0 -228
  98. package/src/records/TLPointer.test.ts +0 -63
  99. package/src/records/TLPresence.test.ts +0 -190
  100. package/src/records/TLRecord.test.ts +0 -70
  101. package/src/records/TLShape.test.ts +0 -232
  102. package/src/shapes/ShapeWithCrop.test.ts +0 -18
  103. package/src/shapes/TLArrowShape.test.ts +0 -505
  104. package/src/shapes/TLBaseShape.test.ts +0 -142
  105. package/src/shapes/TLBookmarkShape.test.ts +0 -122
  106. package/src/shapes/TLDrawShape.test.ts +0 -177
  107. package/src/shapes/TLEmbedShape.test.ts +0 -286
  108. package/src/shapes/TLFrameShape.test.ts +0 -71
  109. package/src/shapes/TLGeoShape.test.ts +0 -247
  110. package/src/shapes/TLGroupShape.test.ts +0 -59
  111. package/src/shapes/TLHighlightShape.test.ts +0 -325
  112. package/src/shapes/TLImageShape.test.ts +0 -534
  113. package/src/shapes/TLLineShape.test.ts +0 -269
  114. package/src/shapes/TLNoteShape.test.ts +0 -1568
  115. package/src/shapes/TLTextShape.test.ts +0 -407
  116. package/src/shapes/TLVideoShape.test.ts +0 -112
  117. package/src/styles/TLColorStyle.test.ts +0 -439
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/shapes/TLHighlightShape.ts"],
4
- "sourcesContent": ["import { T } from '@tldraw/validate'\nimport { createShapePropsMigrationIds, createShapePropsMigrationSequence } from '../records/TLShape'\nimport { RecordProps } from '../recordsWithProps'\nimport { DefaultColorStyle, TLDefaultColorStyle } from '../styles/TLColorStyle'\nimport { DefaultSizeStyle, TLDefaultSizeStyle } from '../styles/TLSizeStyle'\nimport { TLBaseShape } from './TLBaseShape'\nimport { DrawShapeSegment, TLDrawShapeSegment } from './TLDrawShape'\n\n/**\n * Properties for a highlight shape. Highlight shapes represent highlighting strokes made with\n * a highlighting tool, typically used to emphasize or mark up content.\n *\n * @public\n * @example\n * ```ts\n * const highlightProps: TLHighlightShapeProps = {\n * color: 'yellow',\n * size: 'm',\n * segments: [{ type: 'straight', points: [{ x: 0, y: 0, z: 0.5 }] }],\n * isComplete: true,\n * isPen: false,\n * scale: 1\n * }\n * ```\n */\nexport interface TLHighlightShapeProps {\n\t/** The color style of the highlight stroke */\n\tcolor: TLDefaultColorStyle\n\t/** The size style determining the thickness of the highlight stroke */\n\tsize: TLDefaultSizeStyle\n\t/** Array of segments that make up the highlight stroke path */\n\tsegments: TLDrawShapeSegment[]\n\t/** Whether the highlight stroke has been completed by the user */\n\tisComplete: boolean\n\t/** Whether the highlight was drawn with a pen/stylus (affects rendering style) */\n\tisPen: boolean\n\t/** Scale factor applied to the highlight shape for display */\n\tscale: number\n}\n\n/**\n * A highlight shape representing a highlighting stroke drawn by the user. Highlight shapes\n * are typically semi-transparent and used for marking up or emphasizing content on the canvas.\n *\n * @public\n * @example\n * ```ts\n * const highlightShape: TLHighlightShape = {\n * id: 'shape:highlight1',\n * type: 'highlight',\n * x: 100,\n * y: 50,\n * rotation: 0,\n * index: 'a1',\n * parentId: 'page:main',\n * isLocked: false,\n * opacity: 0.7,\n * props: {\n * color: 'yellow',\n * size: 'l',\n * segments: [],\n * isComplete: false,\n * isPen: false,\n * scale: 1\n * },\n * meta: {},\n * typeName: 'shape'\n * }\n * ```\n */\nexport type TLHighlightShape = TLBaseShape<'highlight', TLHighlightShapeProps>\n\n/**\n * Validation schema for highlight shape properties. Defines the runtime validation rules\n * for all properties of highlight shapes.\n *\n * @public\n * @example\n * ```ts\n * import { highlightShapeProps } from '@tldraw/tlschema'\n *\n * // Used internally by the validation system\n * const validator = T.object(highlightShapeProps)\n * const validatedProps = validator.validate(someHighlightProps)\n * ```\n */\nexport const highlightShapeProps: RecordProps<TLHighlightShape> = {\n\tcolor: DefaultColorStyle,\n\tsize: DefaultSizeStyle,\n\tsegments: T.arrayOf(DrawShapeSegment),\n\tisComplete: T.boolean,\n\tisPen: T.boolean,\n\tscale: T.nonZeroNumber,\n}\n\nconst Versions = createShapePropsMigrationIds('highlight', {\n\tAddScale: 1,\n})\n\n/**\n * Version identifiers for highlight shape migrations. These version numbers track\n * schema changes over time to enable proper data migration.\n *\n * @public\n */\nexport { Versions as highlightShapeVersions }\n\n/**\n * Migration sequence for highlight shapes. Handles schema evolution over time by defining\n * how to upgrade and downgrade highlight shape data between different versions.\n *\n * @public\n */\nexport const highlightShapeMigrations = createShapePropsMigrationSequence({\n\tsequence: [\n\t\t{\n\t\t\tid: Versions.AddScale,\n\t\t\tup: (props) => {\n\t\t\t\tprops.scale = 1\n\t\t\t},\n\t\t\tdown: (props) => {\n\t\t\t\tdelete props.scale\n\t\t\t},\n\t\t},\n\t],\n})\n"],
5
- "mappings": "AAAA,SAAS,SAAS;AAClB,SAAS,8BAA8B,yCAAyC;AAEhF,SAAS,yBAA8C;AACvD,SAAS,wBAA4C;AAErD,SAAS,wBAA4C;AAgF9C,MAAM,sBAAqD;AAAA,EACjE,OAAO;AAAA,EACP,MAAM;AAAA,EACN,UAAU,EAAE,QAAQ,gBAAgB;AAAA,EACpC,YAAY,EAAE;AAAA,EACd,OAAO,EAAE;AAAA,EACT,OAAO,EAAE;AACV;AAEA,MAAM,WAAW,6BAA6B,aAAa;AAAA,EAC1D,UAAU;AACX,CAAC;AAgBM,MAAM,2BAA2B,kCAAkC;AAAA,EACzE,UAAU;AAAA,IACT;AAAA,MACC,IAAI,SAAS;AAAA,MACb,IAAI,CAAC,UAAU;AACd,cAAM,QAAQ;AAAA,MACf;AAAA,MACA,MAAM,CAAC,UAAU;AAChB,eAAO,MAAM;AAAA,MACd;AAAA,IACD;AAAA,EACD;AACD,CAAC;",
4
+ "sourcesContent": ["import { T } from '@tldraw/validate'\nimport { b64Vecs } from '../misc/b64Vecs'\nimport { createShapePropsMigrationIds, createShapePropsMigrationSequence } from '../records/TLShape'\nimport { RecordProps } from '../recordsWithProps'\nimport { DefaultColorStyle, TLDefaultColorStyle } from '../styles/TLColorStyle'\nimport { DefaultSizeStyle, TLDefaultSizeStyle } from '../styles/TLSizeStyle'\nimport { TLBaseShape } from './TLBaseShape'\nimport { DrawShapeSegment, TLDrawShapeSegment } from './TLDrawShape'\n\n/**\n * Properties for a highlight shape. Highlight shapes represent highlighting strokes made with\n * a highlighting tool, typically used to emphasize or mark up content.\n *\n * @public\n * @example\n * ```ts\n * const highlightProps: TLHighlightShapeProps = {\n * color: 'yellow',\n * size: 'm',\n * segments: [{ type: 'straight', points: [{ x: 0, y: 0, z: 0.5 }] }],\n * isComplete: true,\n * isPen: false,\n * scale: 1\n * }\n * ```\n */\nexport interface TLHighlightShapeProps {\n\t/** The color style of the highlight stroke */\n\tcolor: TLDefaultColorStyle\n\t/** The size style determining the thickness of the highlight stroke */\n\tsize: TLDefaultSizeStyle\n\t/** Array of segments that make up the highlight stroke path */\n\tsegments: TLDrawShapeSegment[]\n\t/** Whether the highlight stroke has been completed by the user */\n\tisComplete: boolean\n\t/** Whether the highlight was drawn with a pen/stylus (affects rendering style) */\n\tisPen: boolean\n\t/** Scale factor applied to the highlight shape for display */\n\tscale: number\n\t/** Horizontal scale factor for lazy resize */\n\tscaleX: number\n\t/** Vertical scale factor for lazy resize */\n\tscaleY: number\n}\n\n/**\n * A highlight shape representing a highlighting stroke drawn by the user. Highlight shapes\n * are typically semi-transparent and used for marking up or emphasizing content on the canvas.\n *\n * @public\n * @example\n * ```ts\n * const highlightShape: TLHighlightShape = {\n * id: 'shape:highlight1',\n * type: 'highlight',\n * x: 100,\n * y: 50,\n * rotation: 0,\n * index: 'a1',\n * parentId: 'page:main',\n * isLocked: false,\n * opacity: 0.7,\n * props: {\n * color: 'yellow',\n * size: 'l',\n * segments: [],\n * isComplete: false,\n * isPen: false,\n * scale: 1\n * },\n * meta: {},\n * typeName: 'shape'\n * }\n * ```\n */\nexport type TLHighlightShape = TLBaseShape<'highlight', TLHighlightShapeProps>\n\n/**\n * Validation schema for highlight shape properties. Defines the runtime validation rules\n * for all properties of highlight shapes.\n *\n * @public\n * @example\n * ```ts\n * import { highlightShapeProps } from '@tldraw/tlschema'\n *\n * // Used internally by the validation system\n * const validator = T.object(highlightShapeProps)\n * const validatedProps = validator.validate(someHighlightProps)\n * ```\n */\n/** @public */\nexport const highlightShapeProps: RecordProps<TLHighlightShape> = {\n\tcolor: DefaultColorStyle,\n\tsize: DefaultSizeStyle,\n\tsegments: T.arrayOf(DrawShapeSegment),\n\tisComplete: T.boolean,\n\tisPen: T.boolean,\n\tscale: T.nonZeroNumber,\n\tscaleX: T.nonZeroFiniteNumber,\n\tscaleY: T.nonZeroFiniteNumber,\n}\n\nconst Versions = createShapePropsMigrationIds('highlight', {\n\tAddScale: 1,\n\tBase64: 2,\n})\n\n/**\n * Version identifiers for highlight shape migrations. These version numbers track\n * schema changes over time to enable proper data migration.\n *\n * @public\n */\nexport { Versions as highlightShapeVersions }\n\n/**\n * Migration sequence for highlight shapes. Handles schema evolution over time by defining\n * how to upgrade and downgrade highlight shape data between different versions.\n *\n * @public\n */\nexport const highlightShapeMigrations = createShapePropsMigrationSequence({\n\tsequence: [\n\t\t{\n\t\t\tid: Versions.AddScale,\n\t\t\tup: (props) => {\n\t\t\t\tprops.scale = 1\n\t\t\t},\n\t\t\tdown: (props) => {\n\t\t\t\tdelete props.scale\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tid: Versions.Base64,\n\t\t\tup: (props) => {\n\t\t\t\tprops.segments = props.segments.map((segment: any) => {\n\t\t\t\t\treturn {\n\t\t\t\t\t\t...segment,\n\t\t\t\t\t\t// Only encode if points is an array (not already base64 string)\n\t\t\t\t\t\tpoints:\n\t\t\t\t\t\t\ttypeof segment.points === 'string'\n\t\t\t\t\t\t\t\t? segment.points\n\t\t\t\t\t\t\t\t: b64Vecs.encodePoints(segment.points),\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t\tprops.scaleX = props.scaleX ?? 1\n\t\t\t\tprops.scaleY = props.scaleY ?? 1\n\t\t\t},\n\t\t\tdown: (props) => {\n\t\t\t\tprops.segments = props.segments.map((segment: any) => ({\n\t\t\t\t\t...segment,\n\t\t\t\t\t// Only decode if points is a string (not already VecModel[])\n\t\t\t\t\tpoints: Array.isArray(segment.points)\n\t\t\t\t\t\t? segment.points\n\t\t\t\t\t\t: b64Vecs.decodePoints(segment.points),\n\t\t\t\t}))\n\t\t\t\tdelete props.scaleX\n\t\t\t\tdelete props.scaleY\n\t\t\t},\n\t\t},\n\t],\n})\n"],
5
+ "mappings": "AAAA,SAAS,SAAS;AAClB,SAAS,eAAe;AACxB,SAAS,8BAA8B,yCAAyC;AAEhF,SAAS,yBAA8C;AACvD,SAAS,wBAA4C;AAErD,SAAS,wBAA4C;AAqF9C,MAAM,sBAAqD;AAAA,EACjE,OAAO;AAAA,EACP,MAAM;AAAA,EACN,UAAU,EAAE,QAAQ,gBAAgB;AAAA,EACpC,YAAY,EAAE;AAAA,EACd,OAAO,EAAE;AAAA,EACT,OAAO,EAAE;AAAA,EACT,QAAQ,EAAE;AAAA,EACV,QAAQ,EAAE;AACX;AAEA,MAAM,WAAW,6BAA6B,aAAa;AAAA,EAC1D,UAAU;AAAA,EACV,QAAQ;AACT,CAAC;AAgBM,MAAM,2BAA2B,kCAAkC;AAAA,EACzE,UAAU;AAAA,IACT;AAAA,MACC,IAAI,SAAS;AAAA,MACb,IAAI,CAAC,UAAU;AACd,cAAM,QAAQ;AAAA,MACf;AAAA,MACA,MAAM,CAAC,UAAU;AAChB,eAAO,MAAM;AAAA,MACd;AAAA,IACD;AAAA,IACA;AAAA,MACC,IAAI,SAAS;AAAA,MACb,IAAI,CAAC,UAAU;AACd,cAAM,WAAW,MAAM,SAAS,IAAI,CAAC,YAAiB;AACrD,iBAAO;AAAA,YACN,GAAG;AAAA;AAAA,YAEH,QACC,OAAO,QAAQ,WAAW,WACvB,QAAQ,SACR,QAAQ,aAAa,QAAQ,MAAM;AAAA,UACxC;AAAA,QACD,CAAC;AACD,cAAM,SAAS,MAAM,UAAU;AAC/B,cAAM,SAAS,MAAM,UAAU;AAAA,MAChC;AAAA,MACA,MAAM,CAAC,UAAU;AAChB,cAAM,WAAW,MAAM,SAAS,IAAI,CAAC,aAAkB;AAAA,UACtD,GAAG;AAAA;AAAA,UAEH,QAAQ,MAAM,QAAQ,QAAQ,MAAM,IACjC,QAAQ,SACR,QAAQ,aAAa,QAAQ,MAAM;AAAA,QACvC,EAAE;AACF,eAAO,MAAM;AACb,eAAO,MAAM;AAAA,MACd;AAAA,IACD;AAAA,EACD;AACD,CAAC;",
6
6
  "names": []
7
7
  }
@@ -35,7 +35,8 @@ const Versions = createShapePropsMigrationIds("note", {
35
35
  AddFontSizeAdjustment: 6,
36
36
  AddScale: 7,
37
37
  AddLabelColor: 8,
38
- AddRichText: 9
38
+ AddRichText: 9,
39
+ AddRichTextAttrs: 10
39
40
  });
40
41
  const noteShapeMigrations = createShapePropsMigrationSequence({
41
42
  sequence: [
@@ -126,6 +127,16 @@ const noteShapeMigrations = createShapePropsMigrationSequence({
126
127
  // down: (props) => {
127
128
  // delete props.richText
128
129
  // },
130
+ },
131
+ {
132
+ id: Versions.AddRichTextAttrs,
133
+ up: (_props) => {
134
+ },
135
+ down: (props) => {
136
+ if (props.richText && "attrs" in props.richText) {
137
+ delete props.richText.attrs;
138
+ }
139
+ }
129
140
  }
130
141
  ]
131
142
  });
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/shapes/TLNoteShape.ts"],
4
- "sourcesContent": ["import { T } from '@tldraw/validate'\nimport { TLRichText, richTextValidator, toRichText } from '../misc/TLRichText'\nimport { createShapePropsMigrationIds, createShapePropsMigrationSequence } from '../records/TLShape'\nimport { RecordProps } from '../recordsWithProps'\nimport {\n\tDefaultColorStyle,\n\tDefaultLabelColorStyle,\n\tTLDefaultColorStyle,\n} from '../styles/TLColorStyle'\nimport { DefaultFontStyle, TLDefaultFontStyle } from '../styles/TLFontStyle'\nimport {\n\tDefaultHorizontalAlignStyle,\n\tTLDefaultHorizontalAlignStyle,\n} from '../styles/TLHorizontalAlignStyle'\nimport { DefaultSizeStyle, TLDefaultSizeStyle } from '../styles/TLSizeStyle'\nimport {\n\tDefaultVerticalAlignStyle,\n\tTLDefaultVerticalAlignStyle,\n} from '../styles/TLVerticalAlignStyle'\nimport { TLBaseShape } from './TLBaseShape'\n\n/**\n * Properties for a note shape. Note shapes represent sticky notes or text annotations\n * with rich formatting capabilities and various styling options.\n *\n * @public\n * @example\n * ```ts\n * const noteProps: TLNoteShapeProps = {\n * color: 'yellow',\n * labelColor: 'black',\n * size: 'm',\n * font: 'draw',\n * fontSizeAdjustment: 0,\n * align: 'middle',\n * verticalAlign: 'middle',\n * growY: 0,\n * url: '',\n * richText: toRichText('Hello **world**!'),\n * scale: 1\n * }\n * ```\n */\nexport interface TLNoteShapeProps {\n\t/** Background color style of the note */\n\tcolor: TLDefaultColorStyle\n\t/** Text color style for the note content */\n\tlabelColor: TLDefaultColorStyle\n\t/** Size style determining the font size and note dimensions */\n\tsize: TLDefaultSizeStyle\n\t/** Font family style for the note text */\n\tfont: TLDefaultFontStyle\n\t/** Adjustment to the base font size (positive increases, negative decreases) */\n\tfontSizeAdjustment: number\n\t/** Horizontal alignment of text within the note */\n\talign: TLDefaultHorizontalAlignStyle\n\t/** Vertical alignment of text within the note */\n\tverticalAlign: TLDefaultVerticalAlignStyle\n\t/** Additional height growth for the note beyond its base size */\n\tgrowY: number\n\t/** Optional URL associated with the note for linking */\n\turl: string\n\t/** Rich text content with formatting like bold, italic, etc. */\n\trichText: TLRichText\n\t/** Scale factor applied to the note shape for display */\n\tscale: number\n}\n\n/**\n * A note shape representing a sticky note or text annotation on the canvas.\n * Note shapes support rich text formatting, various styling options, and can\n * be used for annotations, reminders, or general text content.\n *\n * @public\n * @example\n * ```ts\n * const noteShape: TLNoteShape = {\n * id: 'shape:note1',\n * type: 'note',\n * x: 100,\n * y: 100,\n * rotation: 0,\n * index: 'a1',\n * parentId: 'page:main',\n * isLocked: false,\n * opacity: 1,\n * props: {\n * color: 'light-blue',\n * labelColor: 'black',\n * size: 's',\n * font: 'sans',\n * fontSizeAdjustment: 2,\n * align: 'start',\n * verticalAlign: 'start',\n * growY: 50,\n * url: 'https://example.com',\n * richText: toRichText('Important **note**!'),\n * scale: 1\n * },\n * meta: {},\n * typeName: 'shape'\n * }\n * ```\n */\nexport type TLNoteShape = TLBaseShape<'note', TLNoteShapeProps>\n\n/**\n * Validation schema for note shape properties. Defines the runtime validation rules\n * for all properties of note shapes, ensuring data integrity and type safety.\n *\n * @public\n * @example\n * ```ts\n * import { noteShapeProps } from '@tldraw/tlschema'\n *\n * // Used internally by the validation system\n * const validator = T.object(noteShapeProps)\n * const validatedProps = validator.validate(someNoteProps)\n * ```\n */\nexport const noteShapeProps: RecordProps<TLNoteShape> = {\n\tcolor: DefaultColorStyle,\n\tlabelColor: DefaultLabelColorStyle,\n\tsize: DefaultSizeStyle,\n\tfont: DefaultFontStyle,\n\tfontSizeAdjustment: T.positiveNumber,\n\talign: DefaultHorizontalAlignStyle,\n\tverticalAlign: DefaultVerticalAlignStyle,\n\tgrowY: T.positiveNumber,\n\turl: T.linkUrl,\n\trichText: richTextValidator,\n\tscale: T.nonZeroNumber,\n}\n\nconst Versions = createShapePropsMigrationIds('note', {\n\tAddUrlProp: 1,\n\tRemoveJustify: 2,\n\tMigrateLegacyAlign: 3,\n\tAddVerticalAlign: 4,\n\tMakeUrlsValid: 5,\n\tAddFontSizeAdjustment: 6,\n\tAddScale: 7,\n\tAddLabelColor: 8,\n\tAddRichText: 9,\n})\n\n/**\n * Version identifiers for note shape migrations. These version numbers track\n * significant schema changes over time, enabling proper data migration between versions.\n *\n * @public\n */\nexport { Versions as noteShapeVersions }\n\n/**\n * Migration sequence for note shapes. Handles schema evolution over time by defining\n * how to upgrade and downgrade note shape data between different versions. Includes\n * migrations for URL properties, text alignment changes, vertical alignment addition,\n * font size adjustments, scaling support, label color, and the transition from plain text to rich text.\n *\n * @public\n */\nexport const noteShapeMigrations = createShapePropsMigrationSequence({\n\tsequence: [\n\t\t{\n\t\t\tid: Versions.AddUrlProp,\n\t\t\tup: (props) => {\n\t\t\t\tprops.url = ''\n\t\t\t},\n\t\t\tdown: 'retired',\n\t\t},\n\t\t{\n\t\t\tid: Versions.RemoveJustify,\n\t\t\tup: (props) => {\n\t\t\t\tif (props.align === 'justify') {\n\t\t\t\t\tprops.align = 'start'\n\t\t\t\t}\n\t\t\t},\n\t\t\tdown: 'retired',\n\t\t},\n\t\t{\n\t\t\tid: Versions.MigrateLegacyAlign,\n\t\t\tup: (props) => {\n\t\t\t\tswitch (props.align) {\n\t\t\t\t\tcase 'start':\n\t\t\t\t\t\tprops.align = 'start-legacy'\n\t\t\t\t\t\treturn\n\t\t\t\t\tcase 'end':\n\t\t\t\t\t\tprops.align = 'end-legacy'\n\t\t\t\t\t\treturn\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tprops.align = 'middle-legacy'\n\t\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t},\n\t\t\tdown: 'retired',\n\t\t},\n\t\t{\n\t\t\tid: Versions.AddVerticalAlign,\n\t\t\tup: (props) => {\n\t\t\t\tprops.verticalAlign = 'middle'\n\t\t\t},\n\t\t\tdown: 'retired',\n\t\t},\n\t\t{\n\t\t\tid: Versions.MakeUrlsValid,\n\t\t\tup: (props) => {\n\t\t\t\tif (!T.linkUrl.isValid(props.url)) {\n\t\t\t\t\tprops.url = ''\n\t\t\t\t}\n\t\t\t},\n\t\t\tdown: (_props) => {\n\t\t\t\t// noop\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tid: Versions.AddFontSizeAdjustment,\n\t\t\tup: (props) => {\n\t\t\t\tprops.fontSizeAdjustment = 0\n\t\t\t},\n\t\t\tdown: (props) => {\n\t\t\t\tdelete props.fontSizeAdjustment\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tid: Versions.AddScale,\n\t\t\tup: (props) => {\n\t\t\t\tprops.scale = 1\n\t\t\t},\n\t\t\tdown: (props) => {\n\t\t\t\tdelete props.scale\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tid: Versions.AddLabelColor,\n\t\t\tup: (props) => {\n\t\t\t\tprops.labelColor = 'black'\n\t\t\t},\n\t\t\tdown: (props) => {\n\t\t\t\tdelete props.labelColor\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tid: Versions.AddRichText,\n\t\t\tup: (props) => {\n\t\t\t\tprops.richText = toRichText(props.text)\n\t\t\t\tdelete props.text\n\t\t\t},\n\t\t\t// N.B. Explicitly no down state so that we force clients to update.\n\t\t\t// down: (props) => {\n\t\t\t// \tdelete props.richText\n\t\t\t// },\n\t\t},\n\t],\n})\n"],
5
- "mappings": "AAAA,SAAS,SAAS;AAClB,SAAqB,mBAAmB,kBAAkB;AAC1D,SAAS,8BAA8B,yCAAyC;AAEhF;AAAA,EACC;AAAA,EACA;AAAA,OAEM;AACP,SAAS,wBAA4C;AACrD;AAAA,EACC;AAAA,OAEM;AACP,SAAS,wBAA4C;AACrD;AAAA,EACC;AAAA,OAEM;AAsGA,MAAM,iBAA2C;AAAA,EACvD,OAAO;AAAA,EACP,YAAY;AAAA,EACZ,MAAM;AAAA,EACN,MAAM;AAAA,EACN,oBAAoB,EAAE;AAAA,EACtB,OAAO;AAAA,EACP,eAAe;AAAA,EACf,OAAO,EAAE;AAAA,EACT,KAAK,EAAE;AAAA,EACP,UAAU;AAAA,EACV,OAAO,EAAE;AACV;AAEA,MAAM,WAAW,6BAA6B,QAAQ;AAAA,EACrD,YAAY;AAAA,EACZ,eAAe;AAAA,EACf,oBAAoB;AAAA,EACpB,kBAAkB;AAAA,EAClB,eAAe;AAAA,EACf,uBAAuB;AAAA,EACvB,UAAU;AAAA,EACV,eAAe;AAAA,EACf,aAAa;AACd,CAAC;AAkBM,MAAM,sBAAsB,kCAAkC;AAAA,EACpE,UAAU;AAAA,IACT;AAAA,MACC,IAAI,SAAS;AAAA,MACb,IAAI,CAAC,UAAU;AACd,cAAM,MAAM;AAAA,MACb;AAAA,MACA,MAAM;AAAA,IACP;AAAA,IACA;AAAA,MACC,IAAI,SAAS;AAAA,MACb,IAAI,CAAC,UAAU;AACd,YAAI,MAAM,UAAU,WAAW;AAC9B,gBAAM,QAAQ;AAAA,QACf;AAAA,MACD;AAAA,MACA,MAAM;AAAA,IACP;AAAA,IACA;AAAA,MACC,IAAI,SAAS;AAAA,MACb,IAAI,CAAC,UAAU;AACd,gBAAQ,MAAM,OAAO;AAAA,UACpB,KAAK;AACJ,kBAAM,QAAQ;AACd;AAAA,UACD,KAAK;AACJ,kBAAM,QAAQ;AACd;AAAA,UACD;AACC,kBAAM,QAAQ;AACd;AAAA,QACF;AAAA,MACD;AAAA,MACA,MAAM;AAAA,IACP;AAAA,IACA;AAAA,MACC,IAAI,SAAS;AAAA,MACb,IAAI,CAAC,UAAU;AACd,cAAM,gBAAgB;AAAA,MACvB;AAAA,MACA,MAAM;AAAA,IACP;AAAA,IACA;AAAA,MACC,IAAI,SAAS;AAAA,MACb,IAAI,CAAC,UAAU;AACd,YAAI,CAAC,EAAE,QAAQ,QAAQ,MAAM,GAAG,GAAG;AAClC,gBAAM,MAAM;AAAA,QACb;AAAA,MACD;AAAA,MACA,MAAM,CAAC,WAAW;AAAA,MAElB;AAAA,IACD;AAAA,IACA;AAAA,MACC,IAAI,SAAS;AAAA,MACb,IAAI,CAAC,UAAU;AACd,cAAM,qBAAqB;AAAA,MAC5B;AAAA,MACA,MAAM,CAAC,UAAU;AAChB,eAAO,MAAM;AAAA,MACd;AAAA,IACD;AAAA,IACA;AAAA,MACC,IAAI,SAAS;AAAA,MACb,IAAI,CAAC,UAAU;AACd,cAAM,QAAQ;AAAA,MACf;AAAA,MACA,MAAM,CAAC,UAAU;AAChB,eAAO,MAAM;AAAA,MACd;AAAA,IACD;AAAA,IACA;AAAA,MACC,IAAI,SAAS;AAAA,MACb,IAAI,CAAC,UAAU;AACd,cAAM,aAAa;AAAA,MACpB;AAAA,MACA,MAAM,CAAC,UAAU;AAChB,eAAO,MAAM;AAAA,MACd;AAAA,IACD;AAAA,IACA;AAAA,MACC,IAAI,SAAS;AAAA,MACb,IAAI,CAAC,UAAU;AACd,cAAM,WAAW,WAAW,MAAM,IAAI;AACtC,eAAO,MAAM;AAAA,MACd;AAAA;AAAA;AAAA;AAAA;AAAA,IAKD;AAAA,EACD;AACD,CAAC;",
4
+ "sourcesContent": ["import { T } from '@tldraw/validate'\nimport { TLRichText, richTextValidator, toRichText } from '../misc/TLRichText'\nimport { createShapePropsMigrationIds, createShapePropsMigrationSequence } from '../records/TLShape'\nimport { RecordProps } from '../recordsWithProps'\nimport {\n\tDefaultColorStyle,\n\tDefaultLabelColorStyle,\n\tTLDefaultColorStyle,\n} from '../styles/TLColorStyle'\nimport { DefaultFontStyle, TLDefaultFontStyle } from '../styles/TLFontStyle'\nimport {\n\tDefaultHorizontalAlignStyle,\n\tTLDefaultHorizontalAlignStyle,\n} from '../styles/TLHorizontalAlignStyle'\nimport { DefaultSizeStyle, TLDefaultSizeStyle } from '../styles/TLSizeStyle'\nimport {\n\tDefaultVerticalAlignStyle,\n\tTLDefaultVerticalAlignStyle,\n} from '../styles/TLVerticalAlignStyle'\nimport { TLBaseShape } from './TLBaseShape'\n\n/**\n * Properties for a note shape. Note shapes represent sticky notes or text annotations\n * with rich formatting capabilities and various styling options.\n *\n * @public\n * @example\n * ```ts\n * const noteProps: TLNoteShapeProps = {\n * color: 'yellow',\n * labelColor: 'black',\n * size: 'm',\n * font: 'draw',\n * fontSizeAdjustment: 0,\n * align: 'middle',\n * verticalAlign: 'middle',\n * growY: 0,\n * url: '',\n * richText: toRichText('Hello **world**!'),\n * scale: 1\n * }\n * ```\n */\nexport interface TLNoteShapeProps {\n\t/** Background color style of the note */\n\tcolor: TLDefaultColorStyle\n\t/** Text color style for the note content */\n\tlabelColor: TLDefaultColorStyle\n\t/** Size style determining the font size and note dimensions */\n\tsize: TLDefaultSizeStyle\n\t/** Font family style for the note text */\n\tfont: TLDefaultFontStyle\n\t/** Adjustment to the base font size (positive increases, negative decreases) */\n\tfontSizeAdjustment: number\n\t/** Horizontal alignment of text within the note */\n\talign: TLDefaultHorizontalAlignStyle\n\t/** Vertical alignment of text within the note */\n\tverticalAlign: TLDefaultVerticalAlignStyle\n\t/** Additional height growth for the note beyond its base size */\n\tgrowY: number\n\t/** Optional URL associated with the note for linking */\n\turl: string\n\t/** Rich text content with formatting like bold, italic, etc. */\n\trichText: TLRichText\n\t/** Scale factor applied to the note shape for display */\n\tscale: number\n}\n\n/**\n * A note shape representing a sticky note or text annotation on the canvas.\n * Note shapes support rich text formatting, various styling options, and can\n * be used for annotations, reminders, or general text content.\n *\n * @public\n * @example\n * ```ts\n * const noteShape: TLNoteShape = {\n * id: 'shape:note1',\n * type: 'note',\n * x: 100,\n * y: 100,\n * rotation: 0,\n * index: 'a1',\n * parentId: 'page:main',\n * isLocked: false,\n * opacity: 1,\n * props: {\n * color: 'light-blue',\n * labelColor: 'black',\n * size: 's',\n * font: 'sans',\n * fontSizeAdjustment: 2,\n * align: 'start',\n * verticalAlign: 'start',\n * growY: 50,\n * url: 'https://example.com',\n * richText: toRichText('Important **note**!'),\n * scale: 1\n * },\n * meta: {},\n * typeName: 'shape'\n * }\n * ```\n */\nexport type TLNoteShape = TLBaseShape<'note', TLNoteShapeProps>\n\n/**\n * Validation schema for note shape properties. Defines the runtime validation rules\n * for all properties of note shapes, ensuring data integrity and type safety.\n *\n * @public\n * @example\n * ```ts\n * import { noteShapeProps } from '@tldraw/tlschema'\n *\n * // Used internally by the validation system\n * const validator = T.object(noteShapeProps)\n * const validatedProps = validator.validate(someNoteProps)\n * ```\n */\nexport const noteShapeProps: RecordProps<TLNoteShape> = {\n\tcolor: DefaultColorStyle,\n\tlabelColor: DefaultLabelColorStyle,\n\tsize: DefaultSizeStyle,\n\tfont: DefaultFontStyle,\n\tfontSizeAdjustment: T.positiveNumber,\n\talign: DefaultHorizontalAlignStyle,\n\tverticalAlign: DefaultVerticalAlignStyle,\n\tgrowY: T.positiveNumber,\n\turl: T.linkUrl,\n\trichText: richTextValidator,\n\tscale: T.nonZeroNumber,\n}\n\nconst Versions = createShapePropsMigrationIds('note', {\n\tAddUrlProp: 1,\n\tRemoveJustify: 2,\n\tMigrateLegacyAlign: 3,\n\tAddVerticalAlign: 4,\n\tMakeUrlsValid: 5,\n\tAddFontSizeAdjustment: 6,\n\tAddScale: 7,\n\tAddLabelColor: 8,\n\tAddRichText: 9,\n\tAddRichTextAttrs: 10,\n})\n\n/**\n * Version identifiers for note shape migrations. These version numbers track\n * significant schema changes over time, enabling proper data migration between versions.\n *\n * @public\n */\nexport { Versions as noteShapeVersions }\n\n/**\n * Migration sequence for note shapes. Handles schema evolution over time by defining\n * how to upgrade and downgrade note shape data between different versions. Includes\n * migrations for URL properties, text alignment changes, vertical alignment addition,\n * font size adjustments, scaling support, label color, the transition from plain text to rich text,\n * and support for attrs property on richText.\n *\n * @public\n */\nexport const noteShapeMigrations = createShapePropsMigrationSequence({\n\tsequence: [\n\t\t{\n\t\t\tid: Versions.AddUrlProp,\n\t\t\tup: (props) => {\n\t\t\t\tprops.url = ''\n\t\t\t},\n\t\t\tdown: 'retired',\n\t\t},\n\t\t{\n\t\t\tid: Versions.RemoveJustify,\n\t\t\tup: (props) => {\n\t\t\t\tif (props.align === 'justify') {\n\t\t\t\t\tprops.align = 'start'\n\t\t\t\t}\n\t\t\t},\n\t\t\tdown: 'retired',\n\t\t},\n\t\t{\n\t\t\tid: Versions.MigrateLegacyAlign,\n\t\t\tup: (props) => {\n\t\t\t\tswitch (props.align) {\n\t\t\t\t\tcase 'start':\n\t\t\t\t\t\tprops.align = 'start-legacy'\n\t\t\t\t\t\treturn\n\t\t\t\t\tcase 'end':\n\t\t\t\t\t\tprops.align = 'end-legacy'\n\t\t\t\t\t\treturn\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tprops.align = 'middle-legacy'\n\t\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t},\n\t\t\tdown: 'retired',\n\t\t},\n\t\t{\n\t\t\tid: Versions.AddVerticalAlign,\n\t\t\tup: (props) => {\n\t\t\t\tprops.verticalAlign = 'middle'\n\t\t\t},\n\t\t\tdown: 'retired',\n\t\t},\n\t\t{\n\t\t\tid: Versions.MakeUrlsValid,\n\t\t\tup: (props) => {\n\t\t\t\tif (!T.linkUrl.isValid(props.url)) {\n\t\t\t\t\tprops.url = ''\n\t\t\t\t}\n\t\t\t},\n\t\t\tdown: (_props) => {\n\t\t\t\t// noop\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tid: Versions.AddFontSizeAdjustment,\n\t\t\tup: (props) => {\n\t\t\t\tprops.fontSizeAdjustment = 0\n\t\t\t},\n\t\t\tdown: (props) => {\n\t\t\t\tdelete props.fontSizeAdjustment\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tid: Versions.AddScale,\n\t\t\tup: (props) => {\n\t\t\t\tprops.scale = 1\n\t\t\t},\n\t\t\tdown: (props) => {\n\t\t\t\tdelete props.scale\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tid: Versions.AddLabelColor,\n\t\t\tup: (props) => {\n\t\t\t\tprops.labelColor = 'black'\n\t\t\t},\n\t\t\tdown: (props) => {\n\t\t\t\tdelete props.labelColor\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tid: Versions.AddRichText,\n\t\t\tup: (props) => {\n\t\t\t\tprops.richText = toRichText(props.text)\n\t\t\t\tdelete props.text\n\t\t\t},\n\t\t\t// N.B. Explicitly no down state so that we force clients to update.\n\t\t\t// down: (props) => {\n\t\t\t// \tdelete props.richText\n\t\t\t// },\n\t\t},\n\t\t{\n\t\t\tid: Versions.AddRichTextAttrs,\n\t\t\tup: (_props) => {\n\t\t\t\t// noop - attrs is optional so old records are valid\n\t\t\t},\n\t\t\tdown: (props) => {\n\t\t\t\t// Remove attrs from richText when migrating down\n\t\t\t\tif (props.richText && 'attrs' in props.richText) {\n\t\t\t\t\tdelete props.richText.attrs\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t],\n})\n"],
5
+ "mappings": "AAAA,SAAS,SAAS;AAClB,SAAqB,mBAAmB,kBAAkB;AAC1D,SAAS,8BAA8B,yCAAyC;AAEhF;AAAA,EACC;AAAA,EACA;AAAA,OAEM;AACP,SAAS,wBAA4C;AACrD;AAAA,EACC;AAAA,OAEM;AACP,SAAS,wBAA4C;AACrD;AAAA,EACC;AAAA,OAEM;AAsGA,MAAM,iBAA2C;AAAA,EACvD,OAAO;AAAA,EACP,YAAY;AAAA,EACZ,MAAM;AAAA,EACN,MAAM;AAAA,EACN,oBAAoB,EAAE;AAAA,EACtB,OAAO;AAAA,EACP,eAAe;AAAA,EACf,OAAO,EAAE;AAAA,EACT,KAAK,EAAE;AAAA,EACP,UAAU;AAAA,EACV,OAAO,EAAE;AACV;AAEA,MAAM,WAAW,6BAA6B,QAAQ;AAAA,EACrD,YAAY;AAAA,EACZ,eAAe;AAAA,EACf,oBAAoB;AAAA,EACpB,kBAAkB;AAAA,EAClB,eAAe;AAAA,EACf,uBAAuB;AAAA,EACvB,UAAU;AAAA,EACV,eAAe;AAAA,EACf,aAAa;AAAA,EACb,kBAAkB;AACnB,CAAC;AAmBM,MAAM,sBAAsB,kCAAkC;AAAA,EACpE,UAAU;AAAA,IACT;AAAA,MACC,IAAI,SAAS;AAAA,MACb,IAAI,CAAC,UAAU;AACd,cAAM,MAAM;AAAA,MACb;AAAA,MACA,MAAM;AAAA,IACP;AAAA,IACA;AAAA,MACC,IAAI,SAAS;AAAA,MACb,IAAI,CAAC,UAAU;AACd,YAAI,MAAM,UAAU,WAAW;AAC9B,gBAAM,QAAQ;AAAA,QACf;AAAA,MACD;AAAA,MACA,MAAM;AAAA,IACP;AAAA,IACA;AAAA,MACC,IAAI,SAAS;AAAA,MACb,IAAI,CAAC,UAAU;AACd,gBAAQ,MAAM,OAAO;AAAA,UACpB,KAAK;AACJ,kBAAM,QAAQ;AACd;AAAA,UACD,KAAK;AACJ,kBAAM,QAAQ;AACd;AAAA,UACD;AACC,kBAAM,QAAQ;AACd;AAAA,QACF;AAAA,MACD;AAAA,MACA,MAAM;AAAA,IACP;AAAA,IACA;AAAA,MACC,IAAI,SAAS;AAAA,MACb,IAAI,CAAC,UAAU;AACd,cAAM,gBAAgB;AAAA,MACvB;AAAA,MACA,MAAM;AAAA,IACP;AAAA,IACA;AAAA,MACC,IAAI,SAAS;AAAA,MACb,IAAI,CAAC,UAAU;AACd,YAAI,CAAC,EAAE,QAAQ,QAAQ,MAAM,GAAG,GAAG;AAClC,gBAAM,MAAM;AAAA,QACb;AAAA,MACD;AAAA,MACA,MAAM,CAAC,WAAW;AAAA,MAElB;AAAA,IACD;AAAA,IACA;AAAA,MACC,IAAI,SAAS;AAAA,MACb,IAAI,CAAC,UAAU;AACd,cAAM,qBAAqB;AAAA,MAC5B;AAAA,MACA,MAAM,CAAC,UAAU;AAChB,eAAO,MAAM;AAAA,MACd;AAAA,IACD;AAAA,IACA;AAAA,MACC,IAAI,SAAS;AAAA,MACb,IAAI,CAAC,UAAU;AACd,cAAM,QAAQ;AAAA,MACf;AAAA,MACA,MAAM,CAAC,UAAU;AAChB,eAAO,MAAM;AAAA,MACd;AAAA,IACD;AAAA,IACA;AAAA,MACC,IAAI,SAAS;AAAA,MACb,IAAI,CAAC,UAAU;AACd,cAAM,aAAa;AAAA,MACpB;AAAA,MACA,MAAM,CAAC,UAAU;AAChB,eAAO,MAAM;AAAA,MACd;AAAA,IACD;AAAA,IACA;AAAA,MACC,IAAI,SAAS;AAAA,MACb,IAAI,CAAC,UAAU;AACd,cAAM,WAAW,WAAW,MAAM,IAAI;AACtC,eAAO,MAAM;AAAA,MACd;AAAA;AAAA;AAAA;AAAA;AAAA,IAKD;AAAA,IACA;AAAA,MACC,IAAI,SAAS;AAAA,MACb,IAAI,CAAC,WAAW;AAAA,MAEhB;AAAA,MACA,MAAM,CAAC,UAAU;AAEhB,YAAI,MAAM,YAAY,WAAW,MAAM,UAAU;AAChD,iBAAO,MAAM,SAAS;AAAA,QACvB;AAAA,MACD;AAAA,IACD;AAAA,EACD;AACD,CAAC;",
6
6
  "names": []
7
7
  }
@@ -18,7 +18,8 @@ const textShapeProps = {
18
18
  const Versions = createShapePropsMigrationIds("text", {
19
19
  RemoveJustify: 1,
20
20
  AddTextAlign: 2,
21
- AddRichText: 3
21
+ AddRichText: 3,
22
+ AddRichTextAttrs: 4
22
23
  });
23
24
  const textShapeMigrations = createShapePropsMigrationSequence({
24
25
  sequence: [
@@ -52,6 +53,16 @@ const textShapeMigrations = createShapePropsMigrationSequence({
52
53
  // down: (props) => {
53
54
  // delete props.richText
54
55
  // },
56
+ },
57
+ {
58
+ id: Versions.AddRichTextAttrs,
59
+ up: (_props) => {
60
+ },
61
+ down: (props) => {
62
+ if (props.richText && "attrs" in props.richText) {
63
+ delete props.richText.attrs;
64
+ }
65
+ }
55
66
  }
56
67
  ]
57
68
  });
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/shapes/TLTextShape.ts"],
4
- "sourcesContent": ["import { T } from '@tldraw/validate'\nimport { TLRichText, richTextValidator, toRichText } from '../misc/TLRichText'\nimport { createShapePropsMigrationIds, createShapePropsMigrationSequence } from '../records/TLShape'\nimport { RecordProps } from '../recordsWithProps'\nimport { DefaultColorStyle, TLDefaultColorStyle } from '../styles/TLColorStyle'\nimport { DefaultFontStyle, TLDefaultFontStyle } from '../styles/TLFontStyle'\nimport { DefaultSizeStyle, TLDefaultSizeStyle } from '../styles/TLSizeStyle'\nimport { DefaultTextAlignStyle, TLDefaultTextAlignStyle } from '../styles/TLTextAlignStyle'\nimport { TLBaseShape } from './TLBaseShape'\n\n/**\n * Configuration interface defining properties for text shapes in tldraw.\n * Text shapes support rich formatting, styling, and automatic sizing.\n *\n * @public\n * @example\n * ```ts\n * const textProps: TLTextShapeProps = {\n * color: 'black',\n * size: 'm',\n * font: 'draw',\n * textAlign: 'start',\n * w: 200,\n * richText: toRichText('Hello **bold** text'),\n * scale: 1,\n * autoSize: true\n * }\n * ```\n */\nexport interface TLTextShapeProps {\n\tcolor: TLDefaultColorStyle\n\tsize: TLDefaultSizeStyle\n\tfont: TLDefaultFontStyle\n\ttextAlign: TLDefaultTextAlignStyle\n\tw: number\n\trichText: TLRichText\n\tscale: number\n\tautoSize: boolean\n}\n\n/**\n * A text shape that can display formatted text content with various styling options.\n * Text shapes support rich formatting, automatic sizing, and consistent styling through\n * the tldraw style system.\n *\n * @public\n * @example\n * ```ts\n * const textShape: TLTextShape = {\n * id: 'shape:text123',\n * typeName: 'shape',\n * type: 'text',\n * x: 100,\n * y: 200,\n * rotation: 0,\n * index: 'a1',\n * parentId: 'page:main',\n * isLocked: false,\n * opacity: 1,\n * props: {\n * color: 'black',\n * size: 'm',\n * font: 'draw',\n * textAlign: 'start',\n * w: 200,\n * richText: toRichText('Sample text'),\n * scale: 1,\n * autoSize: false\n * },\n * meta: {}\n * }\n * ```\n */\nexport type TLTextShape = TLBaseShape<'text', TLTextShapeProps>\n\n/**\n * Validation schema for text shape properties. This defines the runtime validation\n * rules that ensure text shape data integrity when records are stored or transmitted.\n *\n * @public\n * @example\n * ```ts\n * import { textShapeProps } from '@tldraw/tlschema'\n *\n * // Validate text shape properties\n * const isValid = textShapeProps.richText.isValid(myRichText)\n * if (isValid) {\n * // Properties are valid, safe to use\n * }\n * ```\n */\nexport const textShapeProps: RecordProps<TLTextShape> = {\n\tcolor: DefaultColorStyle,\n\tsize: DefaultSizeStyle,\n\tfont: DefaultFontStyle,\n\ttextAlign: DefaultTextAlignStyle,\n\tw: T.nonZeroNumber,\n\trichText: richTextValidator,\n\tscale: T.nonZeroNumber,\n\tautoSize: T.boolean,\n}\n\nconst Versions = createShapePropsMigrationIds('text', {\n\tRemoveJustify: 1,\n\tAddTextAlign: 2,\n\tAddRichText: 3,\n})\n\n/**\n * Version identifiers for text shape migrations. These constants track\n * the evolution of the text shape schema over time.\n *\n * @public\n * @example\n * ```ts\n * import { textShapeVersions } from '@tldraw/tlschema'\n *\n * // Check if shape data needs migration\n * if (shapeVersion < textShapeVersions.AddRichText) {\n * // Apply rich text migration\n * }\n * ```\n */\nexport { Versions as textShapeVersions }\n\n/**\n * Migration sequence for text shape schema evolution. This handles transforming\n * text shape data between different versions as the schema evolves over time.\n *\n * Key migrations include:\n * - RemoveJustify: Replaced 'justify' alignment with 'start'\n * - AddTextAlign: Migrated from 'align' to 'textAlign' property\n * - AddRichText: Converted plain text to rich text format\n *\n * @public\n */\nexport const textShapeMigrations = createShapePropsMigrationSequence({\n\tsequence: [\n\t\t{\n\t\t\tid: Versions.RemoveJustify,\n\t\t\tup: (props) => {\n\t\t\t\tif (props.align === 'justify') {\n\t\t\t\t\tprops.align = 'start'\n\t\t\t\t}\n\t\t\t},\n\t\t\tdown: 'retired',\n\t\t},\n\t\t{\n\t\t\tid: Versions.AddTextAlign,\n\t\t\tup: (props) => {\n\t\t\t\tprops.textAlign = props.align\n\t\t\t\tdelete props.align\n\t\t\t},\n\t\t\tdown: (props) => {\n\t\t\t\tprops.align = props.textAlign\n\t\t\t\tdelete props.textAlign\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tid: Versions.AddRichText,\n\t\t\tup: (props) => {\n\t\t\t\tprops.richText = toRichText(props.text)\n\t\t\t\tdelete props.text\n\t\t\t},\n\t\t\t// N.B. Explicitly no down state so that we force clients to update.\n\t\t\t// down: (props) => {\n\t\t\t// \tdelete props.richText\n\t\t\t// },\n\t\t},\n\t],\n})\n"],
5
- "mappings": "AAAA,SAAS,SAAS;AAClB,SAAqB,mBAAmB,kBAAkB;AAC1D,SAAS,8BAA8B,yCAAyC;AAEhF,SAAS,yBAA8C;AACvD,SAAS,wBAA4C;AACrD,SAAS,wBAA4C;AACrD,SAAS,6BAAsD;AAoFxD,MAAM,iBAA2C;AAAA,EACvD,OAAO;AAAA,EACP,MAAM;AAAA,EACN,MAAM;AAAA,EACN,WAAW;AAAA,EACX,GAAG,EAAE;AAAA,EACL,UAAU;AAAA,EACV,OAAO,EAAE;AAAA,EACT,UAAU,EAAE;AACb;AAEA,MAAM,WAAW,6BAA6B,QAAQ;AAAA,EACrD,eAAe;AAAA,EACf,cAAc;AAAA,EACd,aAAa;AACd,CAAC;AA8BM,MAAM,sBAAsB,kCAAkC;AAAA,EACpE,UAAU;AAAA,IACT;AAAA,MACC,IAAI,SAAS;AAAA,MACb,IAAI,CAAC,UAAU;AACd,YAAI,MAAM,UAAU,WAAW;AAC9B,gBAAM,QAAQ;AAAA,QACf;AAAA,MACD;AAAA,MACA,MAAM;AAAA,IACP;AAAA,IACA;AAAA,MACC,IAAI,SAAS;AAAA,MACb,IAAI,CAAC,UAAU;AACd,cAAM,YAAY,MAAM;AACxB,eAAO,MAAM;AAAA,MACd;AAAA,MACA,MAAM,CAAC,UAAU;AAChB,cAAM,QAAQ,MAAM;AACpB,eAAO,MAAM;AAAA,MACd;AAAA,IACD;AAAA,IACA;AAAA,MACC,IAAI,SAAS;AAAA,MACb,IAAI,CAAC,UAAU;AACd,cAAM,WAAW,WAAW,MAAM,IAAI;AACtC,eAAO,MAAM;AAAA,MACd;AAAA;AAAA;AAAA;AAAA;AAAA,IAKD;AAAA,EACD;AACD,CAAC;",
4
+ "sourcesContent": ["import { T } from '@tldraw/validate'\nimport { TLRichText, richTextValidator, toRichText } from '../misc/TLRichText'\nimport { createShapePropsMigrationIds, createShapePropsMigrationSequence } from '../records/TLShape'\nimport { RecordProps } from '../recordsWithProps'\nimport { DefaultColorStyle, TLDefaultColorStyle } from '../styles/TLColorStyle'\nimport { DefaultFontStyle, TLDefaultFontStyle } from '../styles/TLFontStyle'\nimport { DefaultSizeStyle, TLDefaultSizeStyle } from '../styles/TLSizeStyle'\nimport { DefaultTextAlignStyle, TLDefaultTextAlignStyle } from '../styles/TLTextAlignStyle'\nimport { TLBaseShape } from './TLBaseShape'\n\n/**\n * Configuration interface defining properties for text shapes in tldraw.\n * Text shapes support rich formatting, styling, and automatic sizing.\n *\n * @public\n * @example\n * ```ts\n * const textProps: TLTextShapeProps = {\n * color: 'black',\n * size: 'm',\n * font: 'draw',\n * textAlign: 'start',\n * w: 200,\n * richText: toRichText('Hello **bold** text'),\n * scale: 1,\n * autoSize: true\n * }\n * ```\n */\nexport interface TLTextShapeProps {\n\tcolor: TLDefaultColorStyle\n\tsize: TLDefaultSizeStyle\n\tfont: TLDefaultFontStyle\n\ttextAlign: TLDefaultTextAlignStyle\n\tw: number\n\trichText: TLRichText\n\tscale: number\n\tautoSize: boolean\n}\n\n/**\n * A text shape that can display formatted text content with various styling options.\n * Text shapes support rich formatting, automatic sizing, and consistent styling through\n * the tldraw style system.\n *\n * @public\n * @example\n * ```ts\n * const textShape: TLTextShape = {\n * id: 'shape:text123',\n * typeName: 'shape',\n * type: 'text',\n * x: 100,\n * y: 200,\n * rotation: 0,\n * index: 'a1',\n * parentId: 'page:main',\n * isLocked: false,\n * opacity: 1,\n * props: {\n * color: 'black',\n * size: 'm',\n * font: 'draw',\n * textAlign: 'start',\n * w: 200,\n * richText: toRichText('Sample text'),\n * scale: 1,\n * autoSize: false\n * },\n * meta: {}\n * }\n * ```\n */\nexport type TLTextShape = TLBaseShape<'text', TLTextShapeProps>\n\n/**\n * Validation schema for text shape properties. This defines the runtime validation\n * rules that ensure text shape data integrity when records are stored or transmitted.\n *\n * @public\n * @example\n * ```ts\n * import { textShapeProps } from '@tldraw/tlschema'\n *\n * // Validate text shape properties\n * const isValid = textShapeProps.richText.isValid(myRichText)\n * if (isValid) {\n * // Properties are valid, safe to use\n * }\n * ```\n */\nexport const textShapeProps: RecordProps<TLTextShape> = {\n\tcolor: DefaultColorStyle,\n\tsize: DefaultSizeStyle,\n\tfont: DefaultFontStyle,\n\ttextAlign: DefaultTextAlignStyle,\n\tw: T.nonZeroNumber,\n\trichText: richTextValidator,\n\tscale: T.nonZeroNumber,\n\tautoSize: T.boolean,\n}\n\nconst Versions = createShapePropsMigrationIds('text', {\n\tRemoveJustify: 1,\n\tAddTextAlign: 2,\n\tAddRichText: 3,\n\tAddRichTextAttrs: 4,\n})\n\n/**\n * Version identifiers for text shape migrations. These constants track\n * the evolution of the text shape schema over time.\n *\n * @public\n * @example\n * ```ts\n * import { textShapeVersions } from '@tldraw/tlschema'\n *\n * // Check if shape data needs migration\n * if (shapeVersion < textShapeVersions.AddRichTextAttrs) {\n * // Apply rich text attrs migration\n * }\n * ```\n */\nexport { Versions as textShapeVersions }\n\n/**\n * Migration sequence for text shape schema evolution. This handles transforming\n * text shape data between different versions as the schema evolves over time.\n *\n * Key migrations include:\n * - RemoveJustify: Replaced 'justify' alignment with 'start'\n * - AddTextAlign: Migrated from 'align' to 'textAlign' property\n * - AddRichText: Converted plain text to rich text format\n * - AddRichTextAttrs: Added support for attrs property on richText\n *\n * @public\n */\nexport const textShapeMigrations = createShapePropsMigrationSequence({\n\tsequence: [\n\t\t{\n\t\t\tid: Versions.RemoveJustify,\n\t\t\tup: (props) => {\n\t\t\t\tif (props.align === 'justify') {\n\t\t\t\t\tprops.align = 'start'\n\t\t\t\t}\n\t\t\t},\n\t\t\tdown: 'retired',\n\t\t},\n\t\t{\n\t\t\tid: Versions.AddTextAlign,\n\t\t\tup: (props) => {\n\t\t\t\tprops.textAlign = props.align\n\t\t\t\tdelete props.align\n\t\t\t},\n\t\t\tdown: (props) => {\n\t\t\t\tprops.align = props.textAlign\n\t\t\t\tdelete props.textAlign\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tid: Versions.AddRichText,\n\t\t\tup: (props) => {\n\t\t\t\tprops.richText = toRichText(props.text)\n\t\t\t\tdelete props.text\n\t\t\t},\n\t\t\t// N.B. Explicitly no down state so that we force clients to update.\n\t\t\t// down: (props) => {\n\t\t\t// \tdelete props.richText\n\t\t\t// },\n\t\t},\n\t\t{\n\t\t\tid: Versions.AddRichTextAttrs,\n\t\t\tup: (_props) => {\n\t\t\t\t// noop - attrs is optional so old records are valid\n\t\t\t},\n\t\t\tdown: (props) => {\n\t\t\t\t// Remove attrs from richText when migrating down\n\t\t\t\tif (props.richText && 'attrs' in props.richText) {\n\t\t\t\t\tdelete props.richText.attrs\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t],\n})\n"],
5
+ "mappings": "AAAA,SAAS,SAAS;AAClB,SAAqB,mBAAmB,kBAAkB;AAC1D,SAAS,8BAA8B,yCAAyC;AAEhF,SAAS,yBAA8C;AACvD,SAAS,wBAA4C;AACrD,SAAS,wBAA4C;AACrD,SAAS,6BAAsD;AAoFxD,MAAM,iBAA2C;AAAA,EACvD,OAAO;AAAA,EACP,MAAM;AAAA,EACN,MAAM;AAAA,EACN,WAAW;AAAA,EACX,GAAG,EAAE;AAAA,EACL,UAAU;AAAA,EACV,OAAO,EAAE;AAAA,EACT,UAAU,EAAE;AACb;AAEA,MAAM,WAAW,6BAA6B,QAAQ;AAAA,EACrD,eAAe;AAAA,EACf,cAAc;AAAA,EACd,aAAa;AAAA,EACb,kBAAkB;AACnB,CAAC;AA+BM,MAAM,sBAAsB,kCAAkC;AAAA,EACpE,UAAU;AAAA,IACT;AAAA,MACC,IAAI,SAAS;AAAA,MACb,IAAI,CAAC,UAAU;AACd,YAAI,MAAM,UAAU,WAAW;AAC9B,gBAAM,QAAQ;AAAA,QACf;AAAA,MACD;AAAA,MACA,MAAM;AAAA,IACP;AAAA,IACA;AAAA,MACC,IAAI,SAAS;AAAA,MACb,IAAI,CAAC,UAAU;AACd,cAAM,YAAY,MAAM;AACxB,eAAO,MAAM;AAAA,MACd;AAAA,MACA,MAAM,CAAC,UAAU;AAChB,cAAM,QAAQ,MAAM;AACpB,eAAO,MAAM;AAAA,MACd;AAAA,IACD;AAAA,IACA;AAAA,MACC,IAAI,SAAS;AAAA,MACb,IAAI,CAAC,UAAU;AACd,cAAM,WAAW,WAAW,MAAM,IAAI;AACtC,eAAO,MAAM;AAAA,MACd;AAAA;AAAA;AAAA;AAAA;AAAA,IAKD;AAAA,IACA;AAAA,MACC,IAAI,SAAS;AAAA,MACb,IAAI,CAAC,WAAW;AAAA,MAEhB;AAAA,MACA,MAAM,CAAC,UAAU;AAEhB,YAAI,MAAM,YAAY,WAAW,MAAM,UAAU;AAChD,iBAAO,MAAM,SAAS;AAAA,QACvB;AAAA,MACD;AAAA,IACD;AAAA,EACD;AACD,CAAC;",
6
6
  "names": []
7
7
  }
@@ -13,29 +13,29 @@ const storeMigrations = createMigrationSequence({
13
13
  sequence: [
14
14
  {
15
15
  id: Versions.RemoveCodeAndIconShapeTypes,
16
- scope: "store",
17
- up: (store) => {
18
- for (const [id, record] of objectMapEntries(store)) {
19
- if (record.typeName === "shape" && (record.type === "icon" || record.type === "code")) {
20
- delete store[id];
16
+ scope: "storage",
17
+ up: (storage) => {
18
+ for (const [id, record] of storage.entries()) {
19
+ if (record.typeName === "shape" && "type" in record && (record.type === "icon" || record.type === "code")) {
20
+ storage.delete(id);
21
21
  }
22
22
  }
23
23
  }
24
24
  },
25
25
  {
26
26
  id: Versions.AddInstancePresenceType,
27
- scope: "store",
28
- up(_store) {
27
+ scope: "storage",
28
+ up(_storage) {
29
29
  }
30
30
  },
31
31
  {
32
32
  // remove user and presence records and add pointer records
33
33
  id: Versions.RemoveTLUserAndPresenceAndAddPointer,
34
- scope: "store",
35
- up: (store) => {
36
- for (const [id, record] of objectMapEntries(store)) {
34
+ scope: "storage",
35
+ up: (storage) => {
36
+ for (const [id, record] of storage.entries()) {
37
37
  if (record.typeName.match(/^(user|user_presence)$/)) {
38
- delete store[id];
38
+ storage.delete(id);
39
39
  }
40
40
  }
41
41
  }
@@ -43,11 +43,11 @@ const storeMigrations = createMigrationSequence({
43
43
  {
44
44
  // remove user document records
45
45
  id: Versions.RemoveUserDocument,
46
- scope: "store",
47
- up: (store) => {
48
- for (const [id, record] of objectMapEntries(store)) {
46
+ scope: "storage",
47
+ up: (storage) => {
48
+ for (const [id, record] of storage.entries()) {
49
49
  if (record.typeName.match("user_document")) {
50
- delete store[id];
50
+ storage.delete(id);
51
51
  }
52
52
  }
53
53
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/store-migrations.ts"],
4
- "sourcesContent": ["import { createMigrationIds, createMigrationSequence } from '@tldraw/store'\nimport { IndexKey, objectMapEntries } from '@tldraw/utils'\nimport { TLPage } from './records/TLPage'\nimport { TLShape } from './records/TLShape'\nimport { TLLineShape } from './shapes/TLLineShape'\n\n/**\n * Migration version constants for store-level schema changes.\n * Each version represents a breaking change that requires data transformation.\n *\n * @internal\n */\nconst Versions = createMigrationIds('com.tldraw.store', {\n\tRemoveCodeAndIconShapeTypes: 1,\n\tAddInstancePresenceType: 2,\n\tRemoveTLUserAndPresenceAndAddPointer: 3,\n\tRemoveUserDocument: 4,\n\tFixIndexKeys: 5,\n} as const)\n\n/**\n * Migration version identifiers for store-level migrations.\n * These versions track changes to the overall store structure and data model.\n *\n * @example\n * ```ts\n * import { storeVersions } from '@tldraw/tlschema'\n *\n * // Check if a specific migration version exists\n * const hasRemoveCodeShapes = storeVersions.RemoveCodeAndIconShapeTypes\n * ```\n *\n * @public\n */\nexport { Versions as storeVersions }\n\n/**\n * Store-level migration sequence that handles evolution of the tldraw data model.\n * These migrations run when the store schema version changes and ensure backward\n * compatibility by transforming old data structures to new formats.\n *\n * The migrations handle:\n * - Removal of deprecated shape types (code, icon)\n * - Addition of new record types (instance presence)\n * - Cleanup of obsolete user and presence data\n * - Removal of deprecated user document records\n *\n * @example\n * ```ts\n * import { storeMigrations } from '@tldraw/tlschema'\n * import { migrate } from '@tldraw/store'\n *\n * // Apply store migrations to old data\n * const migratedStore = migrate({\n * store: oldStoreData,\n * migrations: storeMigrations,\n * fromVersion: 0,\n * toVersion: storeMigrations.currentVersion\n * })\n * ```\n *\n * @public\n */\nexport const storeMigrations = createMigrationSequence({\n\tsequenceId: 'com.tldraw.store',\n\tretroactive: false,\n\tsequence: [\n\t\t{\n\t\t\tid: Versions.RemoveCodeAndIconShapeTypes,\n\t\t\tscope: 'store',\n\t\t\tup: (store) => {\n\t\t\t\tfor (const [id, record] of objectMapEntries(store)) {\n\t\t\t\t\tif (\n\t\t\t\t\t\trecord.typeName === 'shape' &&\n\t\t\t\t\t\t((record as TLShape).type === 'icon' || (record as TLShape).type === 'code')\n\t\t\t\t\t) {\n\t\t\t\t\t\tdelete store[id]\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tid: Versions.AddInstancePresenceType,\n\t\t\tscope: 'store',\n\t\t\tup(_store) {\n\t\t\t\t// noop\n\t\t\t\t// there used to be a down migration for this but we made down migrations optional\n\t\t\t\t// and we don't use them on store-level migrations so we can just remove it\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t// remove user and presence records and add pointer records\n\t\t\tid: Versions.RemoveTLUserAndPresenceAndAddPointer,\n\t\t\tscope: 'store',\n\t\t\tup: (store) => {\n\t\t\t\tfor (const [id, record] of objectMapEntries(store)) {\n\t\t\t\t\tif (record.typeName.match(/^(user|user_presence)$/)) {\n\t\t\t\t\t\tdelete store[id]\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t// remove user document records\n\t\t\tid: Versions.RemoveUserDocument,\n\t\t\tscope: 'store',\n\t\t\tup: (store) => {\n\t\t\t\tfor (const [id, record] of objectMapEntries(store)) {\n\t\t\t\t\tif (record.typeName.match('user_document')) {\n\t\t\t\t\t\tdelete store[id]\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tid: Versions.FixIndexKeys,\n\t\t\tscope: 'record',\n\t\t\tup: (record) => {\n\t\t\t\tif (['shape', 'page'].includes(record.typeName) && 'index' in record) {\n\t\t\t\t\tconst recordWithIndex = record as TLShape | TLPage\n\t\t\t\t\t// Our newer fractional indexed library (more correctly) validates that indices\n\t\t\t\t\t// do not end with 0. ('a0' being an exception)\n\t\t\t\t\tif (recordWithIndex.index.endsWith('0') && recordWithIndex.index !== 'a0') {\n\t\t\t\t\t\trecordWithIndex.index = (recordWithIndex.index.slice(0, -1) +\n\t\t\t\t\t\t\tgetNRandomBase62Digits(3)) as IndexKey\n\t\t\t\t\t}\n\t\t\t\t\t// Line shapes have 'points' that have indices as well.\n\t\t\t\t\tif (record.typeName === 'shape' && (recordWithIndex as TLShape).type === 'line') {\n\t\t\t\t\t\tconst lineShape = recordWithIndex as TLLineShape\n\t\t\t\t\t\tfor (const [_, point] of objectMapEntries(lineShape.props.points)) {\n\t\t\t\t\t\t\tif (point.index.endsWith('0') && point.index !== 'a0') {\n\t\t\t\t\t\t\t\tpoint.index = (point.index.slice(0, -1) + getNRandomBase62Digits(3)) as IndexKey\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t},\n\t\t\tdown: () => {\n\t\t\t\t// noop\n\t\t\t\t// Enables tlsync to support older clients so as to not force people to refresh immediately after deploying.\n\t\t\t},\n\t\t},\n\t],\n})\n\nconst BASE_62_DIGITS_WITHOUT_ZERO = '123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'\nconst getRandomBase62Digit = () => {\n\treturn BASE_62_DIGITS_WITHOUT_ZERO.charAt(\n\t\tMath.floor(Math.random() * BASE_62_DIGITS_WITHOUT_ZERO.length)\n\t)\n}\n\nconst getNRandomBase62Digits = (n: number) => {\n\treturn Array.from({ length: n }, getRandomBase62Digit).join('')\n}\n"],
5
- "mappings": "AAAA,SAAS,oBAAoB,+BAA+B;AAC5D,SAAmB,wBAAwB;AAW3C,MAAM,WAAW,mBAAmB,oBAAoB;AAAA,EACvD,6BAA6B;AAAA,EAC7B,yBAAyB;AAAA,EACzB,sCAAsC;AAAA,EACtC,oBAAoB;AAAA,EACpB,cAAc;AACf,CAAU;AA6CH,MAAM,kBAAkB,wBAAwB;AAAA,EACtD,YAAY;AAAA,EACZ,aAAa;AAAA,EACb,UAAU;AAAA,IACT;AAAA,MACC,IAAI,SAAS;AAAA,MACb,OAAO;AAAA,MACP,IAAI,CAAC,UAAU;AACd,mBAAW,CAAC,IAAI,MAAM,KAAK,iBAAiB,KAAK,GAAG;AACnD,cACC,OAAO,aAAa,YAClB,OAAmB,SAAS,UAAW,OAAmB,SAAS,SACpE;AACD,mBAAO,MAAM,EAAE;AAAA,UAChB;AAAA,QACD;AAAA,MACD;AAAA,IACD;AAAA,IACA;AAAA,MACC,IAAI,SAAS;AAAA,MACb,OAAO;AAAA,MACP,GAAG,QAAQ;AAAA,MAIX;AAAA,IACD;AAAA,IACA;AAAA;AAAA,MAEC,IAAI,SAAS;AAAA,MACb,OAAO;AAAA,MACP,IAAI,CAAC,UAAU;AACd,mBAAW,CAAC,IAAI,MAAM,KAAK,iBAAiB,KAAK,GAAG;AACnD,cAAI,OAAO,SAAS,MAAM,wBAAwB,GAAG;AACpD,mBAAO,MAAM,EAAE;AAAA,UAChB;AAAA,QACD;AAAA,MACD;AAAA,IACD;AAAA,IACA;AAAA;AAAA,MAEC,IAAI,SAAS;AAAA,MACb,OAAO;AAAA,MACP,IAAI,CAAC,UAAU;AACd,mBAAW,CAAC,IAAI,MAAM,KAAK,iBAAiB,KAAK,GAAG;AACnD,cAAI,OAAO,SAAS,MAAM,eAAe,GAAG;AAC3C,mBAAO,MAAM,EAAE;AAAA,UAChB;AAAA,QACD;AAAA,MACD;AAAA,IACD;AAAA,IACA;AAAA,MACC,IAAI,SAAS;AAAA,MACb,OAAO;AAAA,MACP,IAAI,CAAC,WAAW;AACf,YAAI,CAAC,SAAS,MAAM,EAAE,SAAS,OAAO,QAAQ,KAAK,WAAW,QAAQ;AACrE,gBAAM,kBAAkB;AAGxB,cAAI,gBAAgB,MAAM,SAAS,GAAG,KAAK,gBAAgB,UAAU,MAAM;AAC1E,4BAAgB,QAAS,gBAAgB,MAAM,MAAM,GAAG,EAAE,IACzD,uBAAuB,CAAC;AAAA,UAC1B;AAEA,cAAI,OAAO,aAAa,WAAY,gBAA4B,SAAS,QAAQ;AAChF,kBAAM,YAAY;AAClB,uBAAW,CAAC,GAAG,KAAK,KAAK,iBAAiB,UAAU,MAAM,MAAM,GAAG;AAClE,kBAAI,MAAM,MAAM,SAAS,GAAG,KAAK,MAAM,UAAU,MAAM;AACtD,sBAAM,QAAS,MAAM,MAAM,MAAM,GAAG,EAAE,IAAI,uBAAuB,CAAC;AAAA,cACnE;AAAA,YACD;AAAA,UACD;AAAA,QACD;AAAA,MACD;AAAA,MACA,MAAM,MAAM;AAAA,MAGZ;AAAA,IACD;AAAA,EACD;AACD,CAAC;AAED,MAAM,8BAA8B;AACpC,MAAM,uBAAuB,MAAM;AAClC,SAAO,4BAA4B;AAAA,IAClC,KAAK,MAAM,KAAK,OAAO,IAAI,4BAA4B,MAAM;AAAA,EAC9D;AACD;AAEA,MAAM,yBAAyB,CAAC,MAAc;AAC7C,SAAO,MAAM,KAAK,EAAE,QAAQ,EAAE,GAAG,oBAAoB,EAAE,KAAK,EAAE;AAC/D;",
4
+ "sourcesContent": ["import { createMigrationIds, createMigrationSequence } from '@tldraw/store'\nimport { IndexKey, objectMapEntries } from '@tldraw/utils'\nimport { TLPage } from './records/TLPage'\nimport { TLShape } from './records/TLShape'\nimport { TLLineShape } from './shapes/TLLineShape'\n\n/**\n * Migration version constants for store-level schema changes.\n * Each version represents a breaking change that requires data transformation.\n *\n * @internal\n */\nconst Versions = createMigrationIds('com.tldraw.store', {\n\tRemoveCodeAndIconShapeTypes: 1,\n\tAddInstancePresenceType: 2,\n\tRemoveTLUserAndPresenceAndAddPointer: 3,\n\tRemoveUserDocument: 4,\n\tFixIndexKeys: 5,\n} as const)\n\n/**\n * Migration version identifiers for store-level migrations.\n * These versions track changes to the overall store structure and data model.\n *\n * @example\n * ```ts\n * import { storeVersions } from '@tldraw/tlschema'\n *\n * // Check if a specific migration version exists\n * const hasRemoveCodeShapes = storeVersions.RemoveCodeAndIconShapeTypes\n * ```\n *\n * @public\n */\nexport { Versions as storeVersions }\n\n/**\n * Store-level migration sequence that handles evolution of the tldraw data model.\n * These migrations run when the store schema version changes and ensure backward\n * compatibility by transforming old data structures to new formats.\n *\n * The migrations handle:\n * - Removal of deprecated shape types (code, icon)\n * - Addition of new record types (instance presence)\n * - Cleanup of obsolete user and presence data\n * - Removal of deprecated user document records\n *\n * @example\n * ```ts\n * import { storeMigrations } from '@tldraw/tlschema'\n * import { migrate } from '@tldraw/store'\n *\n * // Apply store migrations to old data\n * const migratedStore = migrate({\n * store: oldStoreData,\n * migrations: storeMigrations,\n * fromVersion: 0,\n * toVersion: storeMigrations.currentVersion\n * })\n * ```\n *\n * @public\n */\nexport const storeMigrations = createMigrationSequence({\n\tsequenceId: 'com.tldraw.store',\n\tretroactive: false,\n\tsequence: [\n\t\t{\n\t\t\tid: Versions.RemoveCodeAndIconShapeTypes,\n\t\t\tscope: 'storage',\n\t\t\tup: (storage) => {\n\t\t\t\tfor (const [id, record] of storage.entries()) {\n\t\t\t\t\tif (\n\t\t\t\t\t\trecord.typeName === 'shape' &&\n\t\t\t\t\t\t'type' in record &&\n\t\t\t\t\t\t(record.type === 'icon' || record.type === 'code')\n\t\t\t\t\t) {\n\t\t\t\t\t\tstorage.delete(id)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tid: Versions.AddInstancePresenceType,\n\t\t\tscope: 'storage',\n\t\t\tup(_storage) {\n\t\t\t\t// noop\n\t\t\t\t// there used to be a down migration for this but we made down migrations optional\n\t\t\t\t// and we don't use them on storage-level migrations so we can just remove it\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t// remove user and presence records and add pointer records\n\t\t\tid: Versions.RemoveTLUserAndPresenceAndAddPointer,\n\t\t\tscope: 'storage',\n\t\t\tup: (storage) => {\n\t\t\t\tfor (const [id, record] of storage.entries()) {\n\t\t\t\t\tif (record.typeName.match(/^(user|user_presence)$/)) {\n\t\t\t\t\t\tstorage.delete(id)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t// remove user document records\n\t\t\tid: Versions.RemoveUserDocument,\n\t\t\tscope: 'storage',\n\t\t\tup: (storage) => {\n\t\t\t\tfor (const [id, record] of storage.entries()) {\n\t\t\t\t\tif (record.typeName.match('user_document')) {\n\t\t\t\t\t\tstorage.delete(id)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tid: Versions.FixIndexKeys,\n\t\t\tscope: 'record',\n\t\t\tup: (record) => {\n\t\t\t\tif (['shape', 'page'].includes(record.typeName) && 'index' in record) {\n\t\t\t\t\tconst recordWithIndex = record as TLShape | TLPage\n\t\t\t\t\t// Our newer fractional indexed library (more correctly) validates that indices\n\t\t\t\t\t// do not end with 0. ('a0' being an exception)\n\t\t\t\t\tif (recordWithIndex.index.endsWith('0') && recordWithIndex.index !== 'a0') {\n\t\t\t\t\t\trecordWithIndex.index = (recordWithIndex.index.slice(0, -1) +\n\t\t\t\t\t\t\tgetNRandomBase62Digits(3)) as IndexKey\n\t\t\t\t\t}\n\t\t\t\t\t// Line shapes have 'points' that have indices as well.\n\t\t\t\t\tif (record.typeName === 'shape' && (recordWithIndex as TLShape).type === 'line') {\n\t\t\t\t\t\tconst lineShape = recordWithIndex as TLLineShape\n\t\t\t\t\t\tfor (const [_, point] of objectMapEntries(lineShape.props.points)) {\n\t\t\t\t\t\t\tif (point.index.endsWith('0') && point.index !== 'a0') {\n\t\t\t\t\t\t\t\tpoint.index = (point.index.slice(0, -1) + getNRandomBase62Digits(3)) as IndexKey\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t},\n\t\t\tdown: () => {\n\t\t\t\t// noop\n\t\t\t\t// Enables tlsync to support older clients so as to not force people to refresh immediately after deploying.\n\t\t\t},\n\t\t},\n\t],\n})\n\nconst BASE_62_DIGITS_WITHOUT_ZERO = '123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'\nconst getRandomBase62Digit = () => {\n\treturn BASE_62_DIGITS_WITHOUT_ZERO.charAt(\n\t\tMath.floor(Math.random() * BASE_62_DIGITS_WITHOUT_ZERO.length)\n\t)\n}\n\nconst getNRandomBase62Digits = (n: number) => {\n\treturn Array.from({ length: n }, getRandomBase62Digit).join('')\n}\n"],
5
+ "mappings": "AAAA,SAAS,oBAAoB,+BAA+B;AAC5D,SAAmB,wBAAwB;AAW3C,MAAM,WAAW,mBAAmB,oBAAoB;AAAA,EACvD,6BAA6B;AAAA,EAC7B,yBAAyB;AAAA,EACzB,sCAAsC;AAAA,EACtC,oBAAoB;AAAA,EACpB,cAAc;AACf,CAAU;AA6CH,MAAM,kBAAkB,wBAAwB;AAAA,EACtD,YAAY;AAAA,EACZ,aAAa;AAAA,EACb,UAAU;AAAA,IACT;AAAA,MACC,IAAI,SAAS;AAAA,MACb,OAAO;AAAA,MACP,IAAI,CAAC,YAAY;AAChB,mBAAW,CAAC,IAAI,MAAM,KAAK,QAAQ,QAAQ,GAAG;AAC7C,cACC,OAAO,aAAa,WACpB,UAAU,WACT,OAAO,SAAS,UAAU,OAAO,SAAS,SAC1C;AACD,oBAAQ,OAAO,EAAE;AAAA,UAClB;AAAA,QACD;AAAA,MACD;AAAA,IACD;AAAA,IACA;AAAA,MACC,IAAI,SAAS;AAAA,MACb,OAAO;AAAA,MACP,GAAG,UAAU;AAAA,MAIb;AAAA,IACD;AAAA,IACA;AAAA;AAAA,MAEC,IAAI,SAAS;AAAA,MACb,OAAO;AAAA,MACP,IAAI,CAAC,YAAY;AAChB,mBAAW,CAAC,IAAI,MAAM,KAAK,QAAQ,QAAQ,GAAG;AAC7C,cAAI,OAAO,SAAS,MAAM,wBAAwB,GAAG;AACpD,oBAAQ,OAAO,EAAE;AAAA,UAClB;AAAA,QACD;AAAA,MACD;AAAA,IACD;AAAA,IACA;AAAA;AAAA,MAEC,IAAI,SAAS;AAAA,MACb,OAAO;AAAA,MACP,IAAI,CAAC,YAAY;AAChB,mBAAW,CAAC,IAAI,MAAM,KAAK,QAAQ,QAAQ,GAAG;AAC7C,cAAI,OAAO,SAAS,MAAM,eAAe,GAAG;AAC3C,oBAAQ,OAAO,EAAE;AAAA,UAClB;AAAA,QACD;AAAA,MACD;AAAA,IACD;AAAA,IACA;AAAA,MACC,IAAI,SAAS;AAAA,MACb,OAAO;AAAA,MACP,IAAI,CAAC,WAAW;AACf,YAAI,CAAC,SAAS,MAAM,EAAE,SAAS,OAAO,QAAQ,KAAK,WAAW,QAAQ;AACrE,gBAAM,kBAAkB;AAGxB,cAAI,gBAAgB,MAAM,SAAS,GAAG,KAAK,gBAAgB,UAAU,MAAM;AAC1E,4BAAgB,QAAS,gBAAgB,MAAM,MAAM,GAAG,EAAE,IACzD,uBAAuB,CAAC;AAAA,UAC1B;AAEA,cAAI,OAAO,aAAa,WAAY,gBAA4B,SAAS,QAAQ;AAChF,kBAAM,YAAY;AAClB,uBAAW,CAAC,GAAG,KAAK,KAAK,iBAAiB,UAAU,MAAM,MAAM,GAAG;AAClE,kBAAI,MAAM,MAAM,SAAS,GAAG,KAAK,MAAM,UAAU,MAAM;AACtD,sBAAM,QAAS,MAAM,MAAM,MAAM,GAAG,EAAE,IAAI,uBAAuB,CAAC;AAAA,cACnE;AAAA,YACD;AAAA,UACD;AAAA,QACD;AAAA,MACD;AAAA,MACA,MAAM,MAAM;AAAA,MAGZ;AAAA,IACD;AAAA,EACD;AACD,CAAC;AAED,MAAM,8BAA8B;AACpC,MAAM,uBAAuB,MAAM;AAClC,SAAO,4BAA4B;AAAA,IAClC,KAAK,MAAM,KAAK,OAAO,IAAI,4BAA4B,MAAM;AAAA,EAC9D;AACD;AAEA,MAAM,yBAAyB,CAAC,MAAc;AAC7C,SAAO,MAAM,KAAK,EAAE,QAAQ,EAAE,GAAG,oBAAoB,EAAE,KAAK,EAAE;AAC/D;",
6
6
  "names": []
7
7
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@tldraw/tlschema",
3
3
  "description": "tldraw infinite canvas SDK (schema).",
4
- "version": "4.2.1",
4
+ "version": "4.2.2",
5
5
  "author": {
6
6
  "name": "tldraw Inc.",
7
7
  "email": "hello@tldraw.com"
@@ -47,18 +47,18 @@
47
47
  "devDependencies": {
48
48
  "kleur": "^4.1.5",
49
49
  "lazyrepo": "0.0.0-alpha.27",
50
- "react": "^18.3.1",
50
+ "react": "^19.2.1",
51
51
  "vitest": "^3.2.4"
52
52
  },
53
53
  "dependencies": {
54
- "@tldraw/state": "4.2.1",
55
- "@tldraw/store": "4.2.1",
56
- "@tldraw/utils": "4.2.1",
57
- "@tldraw/validate": "4.2.1"
54
+ "@tldraw/state": "4.2.2",
55
+ "@tldraw/store": "4.2.2",
56
+ "@tldraw/utils": "4.2.2",
57
+ "@tldraw/validate": "4.2.2"
58
58
  },
59
59
  "peerDependencies": {
60
- "react": "^18.2.0 || ^19.0.0",
61
- "react-dom": "^18.2.0 || ^19.0.0"
60
+ "react": "^18.2.0 || ^19.2.1",
61
+ "react-dom": "^18.2.0 || ^19.2.1"
62
62
  },
63
63
  "module": "dist-esm/index.mjs",
64
64
  "source": "src/index.ts",
@@ -1,4 +1,4 @@
1
- import { Migration, MigrationId } from '@tldraw/store'
1
+ import { devFreeze, Migration, MigrationId } from '@tldraw/store'
2
2
  import { mockUniqueId, structuredClone } from '@tldraw/utils'
3
3
  import { vi } from 'vitest'
4
4
  import { createTLSchema } from '../createTLSchema'
@@ -25,8 +25,14 @@ export function getTestMigration(migrationId: MigrationId) {
25
25
  id: migrationId,
26
26
  up: (stuff: any) => {
27
27
  nextNanoId = 0
28
- const result = structuredClone(stuff)
29
- return migration.up(result) ?? result
28
+ if (migration.scope === 'record' || migration.scope === 'store') {
29
+ const result = structuredClone(stuff)
30
+ return migration.up(result) ?? result
31
+ }
32
+ const storage =
33
+ typeof stuff.entries === 'function' ? stuff : new Map(Object.entries(stuff).map(devFreeze))
34
+ migration.up(storage)
35
+ return typeof stuff.entries === 'function' ? storage : Object.fromEntries(storage.entries())
30
36
  },
31
37
  down: (stuff: any) => {
32
38
  nextNanoId = 0
@@ -1,4 +1,3 @@
1
- import { BaseRecord } from '@tldraw/store'
2
1
  import { JsonObject } from '@tldraw/utils'
3
2
  import { T } from '@tldraw/validate'
4
3
  import { idValidator } from '../misc/id-validator'
@@ -10,33 +9,41 @@ import { shapeIdValidator } from '../shapes/TLBaseShape'
10
9
  * Base interface for all binding types in tldraw. Bindings represent relationships
11
10
  * between shapes, such as arrows connecting to other shapes or organizational connections.
12
11
  *
13
- * All bindings extend this base interface with specific type and property definitions.
12
+ * All default bindings extend this base interface with specific type and property definitions.
14
13
  * The binding system enables shapes to maintain relationships that persist through
15
14
  * transformations, movements, and other operations.
16
15
  *
16
+ * Custom bindings should be defined by augmenting the TLGlobalBindingPropsMap type and getting the binding type from the TLBinding type.
17
+ *
17
18
  * @param Type - String literal type identifying the specific binding type (e.g., 'arrow')
18
19
  * @param Props - Object containing binding-specific properties and configuration
19
20
  *
20
21
  * @example
21
22
  * ```ts
22
- * // Define a custom binding type
23
- * interface MyCustomBinding extends TLBaseBinding<'custom', MyCustomProps> {}
24
- *
25
- * interface MyCustomProps {
26
- * strength: number
27
- * color: string
23
+ * // Define a default binding type
24
+ * interface TLArrowBinding extends TLBaseBinding<'arrow', TLArrowBindingProps> {}
25
+ *
26
+ * interface TLArrowBindingProps {
27
+ * terminal: 'start' | 'end'
28
+ * normalizedAnchor: VecModel
29
+ * isExact: boolean
30
+ * isPrecise: boolean
31
+ * snap: ElbowArrowSnap
28
32
  * }
29
33
  *
30
34
  * // Create a binding instance
31
- * const binding: MyCustomBinding = {
35
+ * const arrowBinding: TLArrowBinding = {
32
36
  * id: 'binding:abc123',
33
37
  * typeName: 'binding',
34
- * type: 'custom',
38
+ * type: 'arrow',
35
39
  * fromId: 'shape:source1',
36
40
  * toId: 'shape:target1',
37
41
  * props: {
38
- * strength: 0.8,
39
- * color: 'red'
42
+ * terminal: 'end',
43
+ * normalizedAnchor: { x: 0.5, y: 0.5 },
44
+ * isExact: false,
45
+ * isPrecise: true,
46
+ * snap: 'edge'
40
47
  * },
41
48
  * meta: {}
42
49
  * }
@@ -44,8 +51,12 @@ import { shapeIdValidator } from '../shapes/TLBaseShape'
44
51
  *
45
52
  * @public
46
53
  */
47
- export interface TLBaseBinding<Type extends string, Props extends object>
48
- extends BaseRecord<'binding', TLBindingId> {
54
+ export interface TLBaseBinding<Type extends string, Props extends object> {
55
+ // using real `extends BaseRecord<'binding', TLBindingId>` introduces a circularity in the types
56
+ // and for that reason those "base members" have to be declared manually here
57
+ readonly id: TLBindingId
58
+ readonly typeName: 'binding'
59
+
49
60
  /** The specific type of this binding (e.g., 'arrow', 'custom') */
50
61
  type: Type
51
62
  /** ID of the source shape in this binding relationship */
@@ -22,8 +22,9 @@ import {
22
22
  getShapePropKeysByStyle,
23
23
  rootShapeMigrations,
24
24
  } from './records/TLShape'
25
- import { TLPropsMigrations, processPropsMigrations } from './recordsWithProps'
25
+ import { RecordProps, TLPropsMigrations, processPropsMigrations } from './recordsWithProps'
26
26
  import { arrowShapeMigrations, arrowShapeProps } from './shapes/TLArrowShape'
27
+ import { TLBaseShape } from './shapes/TLBaseShape'
27
28
  import { bookmarkShapeMigrations, bookmarkShapeProps } from './shapes/TLBookmarkShape'
28
29
  import { drawShapeMigrations, drawShapeProps } from './shapes/TLDrawShape'
29
30
  import { embedShapeMigrations, embedShapeProps } from './shapes/TLEmbedShape'
@@ -147,7 +148,12 @@ export const defaultShapeSchemas = {
147
148
  note: { migrations: noteShapeMigrations, props: noteShapeProps },
148
149
  text: { migrations: textShapeMigrations, props: textShapeProps },
149
150
  video: { migrations: videoShapeMigrations, props: videoShapeProps },
150
- } satisfies { [T in TLDefaultShape['type']]: SchemaPropsInfo }
151
+ } satisfies {
152
+ [T in TLDefaultShape['type']]: {
153
+ migrations: SchemaPropsInfo['migrations']
154
+ props: RecordProps<TLBaseShape<T, Extract<TLDefaultShape, { type: T }>['props']>>
155
+ }
156
+ }
151
157
 
152
158
  /**
153
159
  * Default binding schema configurations for all built-in tldraw binding types.
package/src/index.ts CHANGED
@@ -99,6 +99,8 @@ export {
99
99
  type TLBindingId,
100
100
  type TLBindingUpdate,
101
101
  type TLDefaultBinding,
102
+ type TLGlobalBindingPropsMap,
103
+ type TLIndexedBindings,
102
104
  type TLUnknownBinding,
103
105
  } from './records/TLBinding'
104
106
  export { CameraRecordType, type TLCamera, type TLCameraId } from './records/TLCamera'
@@ -146,7 +148,11 @@ export {
146
148
  isShape,
147
149
  isShapeId,
148
150
  rootShapeMigrations,
151
+ type ExtractShapeByProps,
152
+ type TLCreateShapePartial,
149
153
  type TLDefaultShape,
154
+ type TLGlobalShapePropsMap,
155
+ type TLIndexedShapes,
150
156
  type TLParentId,
151
157
  type TLShape,
152
158
  type TLShapeId,
@@ -185,6 +191,7 @@ export {
185
191
  type TLBookmarkShapeProps,
186
192
  } from './shapes/TLBookmarkShape'
187
193
  export {
194
+ compressLegacySegments,
188
195
  drawShapeMigrations,
189
196
  drawShapeProps,
190
197
  type TLDrawShape,
@@ -307,3 +314,5 @@ registerTldrawLibraryVersion(
307
314
  (globalThis as any).TLDRAW_LIBRARY_VERSION,
308
315
  (globalThis as any).TLDRAW_LIBRARY_MODULES
309
316
  )
317
+
318
+ export { b64Vecs } from './misc/b64Vecs'
@@ -16,7 +16,7 @@ import { instancePresenceVersions } from './records/TLPresence'
16
16
  import { TLShape, rootShapeVersions } from './records/TLShape'
17
17
  import { arrowShapeVersions } from './shapes/TLArrowShape'
18
18
  import { bookmarkShapeVersions } from './shapes/TLBookmarkShape'
19
- import { drawShapeVersions } from './shapes/TLDrawShape'
19
+ import { compressLegacySegments, drawShapeVersions } from './shapes/TLDrawShape'
20
20
  import { embedShapeVersions } from './shapes/TLEmbedShape'
21
21
  import { frameShapeVersions } from './shapes/TLFrameShape'
22
22
  import { geoShapeVersions } from './shapes/TLGeoShape'
@@ -1444,6 +1444,32 @@ describe('Add rich text', () => {
1444
1444
  }
1445
1445
  })
1446
1446
 
1447
+ describe('Add rich text attrs', () => {
1448
+ const migrations = [
1449
+ ['text shape', getTestMigration(textShapeVersions.AddRichTextAttrs)],
1450
+ ['geo shape', getTestMigration(geoShapeVersions.AddRichTextAttrs)],
1451
+ ['note shape', getTestMigration(noteShapeVersions.AddRichTextAttrs)],
1452
+ ['arrow shape', getTestMigration(arrowShapeVersions.AddRichTextAttrs)],
1453
+ ] as const
1454
+
1455
+ for (const [shapeName, { up, down }] of migrations) {
1456
+ it(`works for ${shapeName}`, () => {
1457
+ const shape = { props: { richText: toRichText('hello, world') } }
1458
+ const shapeWithAttrs = {
1459
+ props: { richText: { ...toRichText('hello, world'), attrs: { test: 'value' } } },
1460
+ }
1461
+
1462
+ // Up migration should be a noop
1463
+ expect(up(shape)).toEqual(shape)
1464
+ expect(up(shapeWithAttrs)).toEqual(shapeWithAttrs)
1465
+
1466
+ // Down migration should remove attrs
1467
+ expect(down(shapeWithAttrs)).toEqual(shape)
1468
+ expect(down(shape)).toEqual(shape)
1469
+ })
1470
+ }
1471
+ })
1472
+
1447
1473
  describe('Make urls valid for all the assets', () => {
1448
1474
  const migrations = [
1449
1475
  ['bookmark asset', getTestMigration(bookmarkAssetVersions.MakeUrlsValid)],
@@ -2242,6 +2268,128 @@ describe('TLVideoAsset AddAutoplay', () => {
2242
2268
  })
2243
2269
  })
2244
2270
 
2271
+ describe('Add scaleX, scaleY, and new base64 format to draw shape', () => {
2272
+ const { up, down } = getTestMigration(drawShapeVersions.Base64)
2273
+
2274
+ test('up works as expected', () => {
2275
+ const legacySegments = [
2276
+ {
2277
+ type: 'free',
2278
+ points: [
2279
+ { x: 0, y: 0, z: 0.5 },
2280
+ { x: 10, y: 10, z: 0.6 },
2281
+ { x: 20, y: 20, z: 0.7 },
2282
+ ],
2283
+ },
2284
+ {
2285
+ type: 'straight',
2286
+ points: [
2287
+ { x: 20, y: 20, z: 0.7 },
2288
+ { x: 30, y: 30, z: 0.8 },
2289
+ ],
2290
+ },
2291
+ ]
2292
+ expect(
2293
+ up({
2294
+ props: {
2295
+ segments: legacySegments,
2296
+ },
2297
+ })
2298
+ ).toEqual({
2299
+ props: {
2300
+ scaleX: 1,
2301
+ scaleY: 1,
2302
+ segments: compressLegacySegments(legacySegments as any),
2303
+ },
2304
+ })
2305
+ })
2306
+
2307
+ test('down works as expected', () => {
2308
+ const legacySegments = [
2309
+ {
2310
+ type: 'free',
2311
+ points: [
2312
+ { x: 0, y: 0, z: 0.5 },
2313
+ { x: 10, y: 10, z: 0.6 },
2314
+ ],
2315
+ },
2316
+ ]
2317
+ const compressed = compressLegacySegments(legacySegments as any)
2318
+ const result = down({
2319
+ props: {
2320
+ scaleX: 1,
2321
+ scaleY: 1,
2322
+ segments: compressed,
2323
+ },
2324
+ })
2325
+ expect(result.props.scaleX).toBeUndefined()
2326
+ expect(result.props.scaleY).toBeUndefined()
2327
+ expect(Array.isArray(result.props.segments[0].points)).toBe(true)
2328
+ expect(result.props.segments[0].points.length).toBe(2)
2329
+ })
2330
+ })
2331
+
2332
+ describe('Add scaleX, scaleY, and new base64 format to highlight shape', () => {
2333
+ const { up, down } = getTestMigration(highlightShapeVersions.Base64)
2334
+
2335
+ test('up works as expected', () => {
2336
+ const legacySegments = [
2337
+ {
2338
+ type: 'free',
2339
+ points: [
2340
+ { x: 0, y: 0, z: 0.5 },
2341
+ { x: 10, y: 10, z: 0.6 },
2342
+ { x: 20, y: 20, z: 0.7 },
2343
+ ],
2344
+ },
2345
+ {
2346
+ type: 'straight',
2347
+ points: [
2348
+ { x: 20, y: 20, z: 0.7 },
2349
+ { x: 30, y: 30, z: 0.8 },
2350
+ ],
2351
+ },
2352
+ ]
2353
+ expect(
2354
+ up({
2355
+ props: {
2356
+ segments: legacySegments,
2357
+ },
2358
+ })
2359
+ ).toEqual({
2360
+ props: {
2361
+ scaleX: 1,
2362
+ scaleY: 1,
2363
+ segments: compressLegacySegments(legacySegments as any),
2364
+ },
2365
+ })
2366
+ })
2367
+
2368
+ test('down works as expected', () => {
2369
+ const legacySegments = [
2370
+ {
2371
+ type: 'free',
2372
+ points: [
2373
+ { x: 0, y: 0, z: 0.5 },
2374
+ { x: 10, y: 10, z: 0.6 },
2375
+ ],
2376
+ },
2377
+ ]
2378
+ const compressed = compressLegacySegments(legacySegments as any)
2379
+ const result = down({
2380
+ props: {
2381
+ scaleX: 1,
2382
+ scaleY: 1,
2383
+ segments: compressed,
2384
+ },
2385
+ })
2386
+ expect(result.props.scaleX).toBeUndefined()
2387
+ expect(result.props.scaleY).toBeUndefined()
2388
+ expect(Array.isArray(result.props.segments[0].points)).toBe(true)
2389
+ expect(result.props.segments[0].points.length).toBe(2)
2390
+ })
2391
+ })
2392
+
2245
2393
  /* --- PUT YOUR MIGRATIONS TESTS ABOVE HERE --- */
2246
2394
 
2247
2395
  // check that all migrator fns were called at least once