@tldraw/tlschema 4.1.0-canary.ccd6179e1cb2 → 4.1.0-canary.cfa7ba5fd57f

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 (212) hide show
  1. package/dist-cjs/TLStore.js +3 -10
  2. package/dist-cjs/TLStore.js.map +2 -2
  3. package/dist-cjs/assets/TLBaseAsset.js.map +2 -2
  4. package/dist-cjs/assets/TLBookmarkAsset.js.map +2 -2
  5. package/dist-cjs/assets/TLImageAsset.js.map +2 -2
  6. package/dist-cjs/assets/TLVideoAsset.js.map +2 -2
  7. package/dist-cjs/bindings/TLArrowBinding.js.map +2 -2
  8. package/dist-cjs/bindings/TLBaseBinding.js.map +2 -2
  9. package/dist-cjs/createPresenceStateDerivation.js.map +2 -2
  10. package/dist-cjs/createTLSchema.js.map +2 -2
  11. package/dist-cjs/index.d.ts +4416 -223
  12. package/dist-cjs/index.js +1 -1
  13. package/dist-cjs/index.js.map +2 -2
  14. package/dist-cjs/misc/TLColor.js.map +2 -2
  15. package/dist-cjs/misc/TLCursor.js.map +2 -2
  16. package/dist-cjs/misc/TLHandle.js.map +2 -2
  17. package/dist-cjs/misc/TLOpacity.js.map +2 -2
  18. package/dist-cjs/misc/TLRichText.js.map +2 -2
  19. package/dist-cjs/misc/TLScribble.js.map +2 -2
  20. package/dist-cjs/misc/geometry-types.js.map +2 -2
  21. package/dist-cjs/misc/id-validator.js.map +2 -2
  22. package/dist-cjs/records/TLAsset.js.map +2 -2
  23. package/dist-cjs/records/TLBinding.js.map +2 -2
  24. package/dist-cjs/records/TLCamera.js.map +2 -2
  25. package/dist-cjs/records/TLDocument.js.map +2 -2
  26. package/dist-cjs/records/TLInstance.js.map +2 -2
  27. package/dist-cjs/records/TLPage.js.map +2 -2
  28. package/dist-cjs/records/TLPageState.js.map +2 -2
  29. package/dist-cjs/records/TLPointer.js.map +2 -2
  30. package/dist-cjs/records/TLPresence.js.map +2 -2
  31. package/dist-cjs/records/TLRecord.js.map +1 -1
  32. package/dist-cjs/records/TLShape.js.map +2 -2
  33. package/dist-cjs/recordsWithProps.js.map +2 -2
  34. package/dist-cjs/shapes/ShapeWithCrop.js.map +1 -1
  35. package/dist-cjs/shapes/TLArrowShape.js.map +2 -2
  36. package/dist-cjs/shapes/TLBaseShape.js.map +2 -2
  37. package/dist-cjs/shapes/TLBookmarkShape.js.map +2 -2
  38. package/dist-cjs/shapes/TLDrawShape.js.map +2 -2
  39. package/dist-cjs/shapes/TLEmbedShape.js.map +2 -2
  40. package/dist-cjs/shapes/TLFrameShape.js.map +2 -2
  41. package/dist-cjs/shapes/TLGeoShape.js.map +2 -2
  42. package/dist-cjs/shapes/TLGroupShape.js.map +2 -2
  43. package/dist-cjs/shapes/TLHighlightShape.js.map +2 -2
  44. package/dist-cjs/shapes/TLImageShape.js.map +2 -2
  45. package/dist-cjs/shapes/TLLineShape.js.map +2 -2
  46. package/dist-cjs/shapes/TLNoteShape.js.map +2 -2
  47. package/dist-cjs/shapes/TLTextShape.js.map +2 -2
  48. package/dist-cjs/shapes/TLVideoShape.js.map +2 -2
  49. package/dist-cjs/store-migrations.js.map +2 -2
  50. package/dist-cjs/styles/TLColorStyle.js.map +2 -2
  51. package/dist-cjs/styles/TLDashStyle.js.map +2 -2
  52. package/dist-cjs/styles/TLFillStyle.js.map +2 -2
  53. package/dist-cjs/styles/TLFontStyle.js.map +2 -2
  54. package/dist-cjs/styles/TLHorizontalAlignStyle.js.map +2 -2
  55. package/dist-cjs/styles/TLSizeStyle.js.map +2 -2
  56. package/dist-cjs/styles/TLTextAlignStyle.js.map +2 -2
  57. package/dist-cjs/styles/TLVerticalAlignStyle.js.map +2 -2
  58. package/dist-cjs/translations/translations.js +1 -1
  59. package/dist-cjs/translations/translations.js.map +2 -2
  60. package/dist-cjs/util-types.js.map +1 -1
  61. package/dist-esm/TLStore.mjs +3 -10
  62. package/dist-esm/TLStore.mjs.map +2 -2
  63. package/dist-esm/assets/TLBaseAsset.mjs.map +2 -2
  64. package/dist-esm/assets/TLBookmarkAsset.mjs.map +2 -2
  65. package/dist-esm/assets/TLImageAsset.mjs.map +2 -2
  66. package/dist-esm/assets/TLVideoAsset.mjs.map +2 -2
  67. package/dist-esm/bindings/TLArrowBinding.mjs.map +2 -2
  68. package/dist-esm/bindings/TLBaseBinding.mjs.map +2 -2
  69. package/dist-esm/createPresenceStateDerivation.mjs.map +2 -2
  70. package/dist-esm/createTLSchema.mjs.map +2 -2
  71. package/dist-esm/index.d.mts +4416 -223
  72. package/dist-esm/index.mjs +1 -1
  73. package/dist-esm/index.mjs.map +2 -2
  74. package/dist-esm/misc/TLColor.mjs.map +2 -2
  75. package/dist-esm/misc/TLCursor.mjs.map +2 -2
  76. package/dist-esm/misc/TLHandle.mjs.map +2 -2
  77. package/dist-esm/misc/TLOpacity.mjs.map +2 -2
  78. package/dist-esm/misc/TLRichText.mjs.map +2 -2
  79. package/dist-esm/misc/TLScribble.mjs.map +2 -2
  80. package/dist-esm/misc/geometry-types.mjs.map +2 -2
  81. package/dist-esm/misc/id-validator.mjs.map +2 -2
  82. package/dist-esm/records/TLAsset.mjs.map +2 -2
  83. package/dist-esm/records/TLBinding.mjs.map +2 -2
  84. package/dist-esm/records/TLCamera.mjs.map +2 -2
  85. package/dist-esm/records/TLDocument.mjs.map +2 -2
  86. package/dist-esm/records/TLInstance.mjs.map +2 -2
  87. package/dist-esm/records/TLPage.mjs.map +2 -2
  88. package/dist-esm/records/TLPageState.mjs.map +2 -2
  89. package/dist-esm/records/TLPointer.mjs.map +2 -2
  90. package/dist-esm/records/TLPresence.mjs.map +2 -2
  91. package/dist-esm/records/TLShape.mjs.map +2 -2
  92. package/dist-esm/recordsWithProps.mjs.map +2 -2
  93. package/dist-esm/shapes/TLArrowShape.mjs.map +2 -2
  94. package/dist-esm/shapes/TLBaseShape.mjs.map +2 -2
  95. package/dist-esm/shapes/TLBookmarkShape.mjs.map +2 -2
  96. package/dist-esm/shapes/TLDrawShape.mjs.map +2 -2
  97. package/dist-esm/shapes/TLEmbedShape.mjs.map +2 -2
  98. package/dist-esm/shapes/TLFrameShape.mjs.map +2 -2
  99. package/dist-esm/shapes/TLGeoShape.mjs.map +2 -2
  100. package/dist-esm/shapes/TLGroupShape.mjs.map +2 -2
  101. package/dist-esm/shapes/TLHighlightShape.mjs.map +2 -2
  102. package/dist-esm/shapes/TLImageShape.mjs.map +2 -2
  103. package/dist-esm/shapes/TLLineShape.mjs.map +2 -2
  104. package/dist-esm/shapes/TLNoteShape.mjs.map +2 -2
  105. package/dist-esm/shapes/TLTextShape.mjs.map +2 -2
  106. package/dist-esm/shapes/TLVideoShape.mjs.map +2 -2
  107. package/dist-esm/store-migrations.mjs.map +2 -2
  108. package/dist-esm/styles/TLColorStyle.mjs.map +2 -2
  109. package/dist-esm/styles/TLDashStyle.mjs.map +2 -2
  110. package/dist-esm/styles/TLFillStyle.mjs.map +2 -2
  111. package/dist-esm/styles/TLFontStyle.mjs.map +2 -2
  112. package/dist-esm/styles/TLHorizontalAlignStyle.mjs.map +2 -2
  113. package/dist-esm/styles/TLSizeStyle.mjs.map +2 -2
  114. package/dist-esm/styles/TLTextAlignStyle.mjs.map +2 -2
  115. package/dist-esm/styles/TLVerticalAlignStyle.mjs.map +2 -2
  116. package/dist-esm/translations/translations.mjs +1 -1
  117. package/dist-esm/translations/translations.mjs.map +2 -2
  118. package/package.json +5 -5
  119. package/src/TLStore.test.ts +644 -0
  120. package/src/TLStore.ts +205 -20
  121. package/src/assets/TLBaseAsset.ts +90 -7
  122. package/src/assets/TLBookmarkAsset.test.ts +96 -0
  123. package/src/assets/TLBookmarkAsset.ts +52 -2
  124. package/src/assets/TLImageAsset.test.ts +213 -0
  125. package/src/assets/TLImageAsset.ts +60 -2
  126. package/src/assets/TLVideoAsset.test.ts +105 -0
  127. package/src/assets/TLVideoAsset.ts +93 -4
  128. package/src/bindings/TLArrowBinding.test.ts +55 -0
  129. package/src/bindings/TLArrowBinding.ts +132 -10
  130. package/src/bindings/TLBaseBinding.ts +140 -3
  131. package/src/createPresenceStateDerivation.test.ts +158 -0
  132. package/src/createPresenceStateDerivation.ts +71 -2
  133. package/src/createTLSchema.test.ts +181 -0
  134. package/src/createTLSchema.ts +164 -7
  135. package/src/index.ts +32 -0
  136. package/src/misc/TLColor.ts +50 -6
  137. package/src/misc/TLCursor.ts +110 -8
  138. package/src/misc/TLHandle.ts +86 -6
  139. package/src/misc/TLOpacity.ts +51 -2
  140. package/src/misc/TLRichText.ts +56 -3
  141. package/src/misc/TLScribble.ts +105 -5
  142. package/src/misc/geometry-types.ts +30 -2
  143. package/src/misc/id-validator.test.ts +50 -0
  144. package/src/misc/id-validator.ts +20 -1
  145. package/src/records/TLAsset.test.ts +234 -0
  146. package/src/records/TLAsset.ts +165 -8
  147. package/src/records/TLBinding.test.ts +22 -0
  148. package/src/records/TLBinding.ts +277 -11
  149. package/src/records/TLCamera.test.ts +19 -0
  150. package/src/records/TLCamera.ts +118 -7
  151. package/src/records/TLDocument.test.ts +35 -0
  152. package/src/records/TLDocument.ts +148 -8
  153. package/src/records/TLInstance.test.ts +201 -0
  154. package/src/records/TLInstance.ts +117 -9
  155. package/src/records/TLPage.test.ts +110 -0
  156. package/src/records/TLPage.ts +106 -8
  157. package/src/records/TLPageState.test.ts +228 -0
  158. package/src/records/TLPageState.ts +88 -7
  159. package/src/records/TLPointer.test.ts +63 -0
  160. package/src/records/TLPointer.ts +105 -7
  161. package/src/records/TLPresence.test.ts +190 -0
  162. package/src/records/TLPresence.ts +99 -5
  163. package/src/records/TLRecord.test.ts +70 -0
  164. package/src/records/TLRecord.ts +43 -1
  165. package/src/records/TLShape.test.ts +232 -0
  166. package/src/records/TLShape.ts +289 -12
  167. package/src/recordsWithProps.test.ts +188 -0
  168. package/src/recordsWithProps.ts +131 -2
  169. package/src/shapes/ShapeWithCrop.test.ts +18 -0
  170. package/src/shapes/ShapeWithCrop.ts +64 -2
  171. package/src/shapes/TLArrowShape.test.ts +505 -0
  172. package/src/shapes/TLArrowShape.ts +188 -10
  173. package/src/shapes/TLBaseShape.test.ts +142 -0
  174. package/src/shapes/TLBaseShape.ts +103 -4
  175. package/src/shapes/TLBookmarkShape.test.ts +122 -0
  176. package/src/shapes/TLBookmarkShape.ts +58 -4
  177. package/src/shapes/TLDrawShape.test.ts +177 -0
  178. package/src/shapes/TLDrawShape.ts +97 -6
  179. package/src/shapes/TLEmbedShape.test.ts +286 -0
  180. package/src/shapes/TLEmbedShape.ts +57 -4
  181. package/src/shapes/TLFrameShape.test.ts +71 -0
  182. package/src/shapes/TLFrameShape.ts +59 -4
  183. package/src/shapes/TLGeoShape.test.ts +247 -0
  184. package/src/shapes/TLGeoShape.ts +103 -7
  185. package/src/shapes/TLGroupShape.test.ts +59 -0
  186. package/src/shapes/TLGroupShape.ts +52 -4
  187. package/src/shapes/TLHighlightShape.test.ts +325 -0
  188. package/src/shapes/TLHighlightShape.ts +79 -4
  189. package/src/shapes/TLImageShape.test.ts +534 -0
  190. package/src/shapes/TLImageShape.ts +105 -5
  191. package/src/shapes/TLLineShape.test.ts +269 -0
  192. package/src/shapes/TLLineShape.ts +128 -8
  193. package/src/shapes/TLNoteShape.test.ts +1568 -0
  194. package/src/shapes/TLNoteShape.ts +97 -4
  195. package/src/shapes/TLTextShape.test.ts +407 -0
  196. package/src/shapes/TLTextShape.ts +94 -4
  197. package/src/shapes/TLVideoShape.test.ts +112 -0
  198. package/src/shapes/TLVideoShape.ts +99 -4
  199. package/src/store-migrations.test.ts +88 -0
  200. package/src/store-migrations.ts +47 -1
  201. package/src/styles/TLColorStyle.test.ts +439 -0
  202. package/src/styles/TLColorStyle.ts +228 -10
  203. package/src/styles/TLDashStyle.ts +54 -2
  204. package/src/styles/TLFillStyle.ts +54 -2
  205. package/src/styles/TLFontStyle.ts +72 -3
  206. package/src/styles/TLHorizontalAlignStyle.ts +55 -2
  207. package/src/styles/TLSizeStyle.ts +54 -2
  208. package/src/styles/TLTextAlignStyle.ts +52 -2
  209. package/src/styles/TLVerticalAlignStyle.ts +52 -2
  210. package/src/translations/translations.test.ts +378 -35
  211. package/src/translations/translations.ts +157 -10
  212. package/src/util-types.ts +51 -1
@@ -19,25 +19,105 @@ import {
19
19
  } from '../styles/TLVerticalAlignStyle'
20
20
  import { TLBaseShape } from './TLBaseShape'
21
21
 
22
- /** @public */
22
+ /**
23
+ * Properties for a note shape. Note shapes represent sticky notes or text annotations
24
+ * with rich formatting capabilities and various styling options.
25
+ *
26
+ * @public
27
+ * @example
28
+ * ```ts
29
+ * const noteProps: TLNoteShapeProps = {
30
+ * color: 'yellow',
31
+ * labelColor: 'black',
32
+ * size: 'm',
33
+ * font: 'draw',
34
+ * fontSizeAdjustment: 0,
35
+ * align: 'middle',
36
+ * verticalAlign: 'middle',
37
+ * growY: 0,
38
+ * url: '',
39
+ * richText: toRichText('Hello **world**!'),
40
+ * scale: 1
41
+ * }
42
+ * ```
43
+ */
23
44
  export interface TLNoteShapeProps {
45
+ /** Background color style of the note */
24
46
  color: TLDefaultColorStyle
47
+ /** Text color style for the note content */
25
48
  labelColor: TLDefaultColorStyle
49
+ /** Size style determining the font size and note dimensions */
26
50
  size: TLDefaultSizeStyle
51
+ /** Font family style for the note text */
27
52
  font: TLDefaultFontStyle
53
+ /** Adjustment to the base font size (positive increases, negative decreases) */
28
54
  fontSizeAdjustment: number
55
+ /** Horizontal alignment of text within the note */
29
56
  align: TLDefaultHorizontalAlignStyle
57
+ /** Vertical alignment of text within the note */
30
58
  verticalAlign: TLDefaultVerticalAlignStyle
59
+ /** Additional height growth for the note beyond its base size */
31
60
  growY: number
61
+ /** Optional URL associated with the note for linking */
32
62
  url: string
63
+ /** Rich text content with formatting like bold, italic, etc. */
33
64
  richText: TLRichText
65
+ /** Scale factor applied to the note shape for display */
34
66
  scale: number
35
67
  }
36
68
 
37
- /** @public */
69
+ /**
70
+ * A note shape representing a sticky note or text annotation on the canvas.
71
+ * Note shapes support rich text formatting, various styling options, and can
72
+ * be used for annotations, reminders, or general text content.
73
+ *
74
+ * @public
75
+ * @example
76
+ * ```ts
77
+ * const noteShape: TLNoteShape = {
78
+ * id: 'shape:note1',
79
+ * type: 'note',
80
+ * x: 100,
81
+ * y: 100,
82
+ * rotation: 0,
83
+ * index: 'a1',
84
+ * parentId: 'page:main',
85
+ * isLocked: false,
86
+ * opacity: 1,
87
+ * props: {
88
+ * color: 'light-blue',
89
+ * labelColor: 'black',
90
+ * size: 's',
91
+ * font: 'sans',
92
+ * fontSizeAdjustment: 2,
93
+ * align: 'start',
94
+ * verticalAlign: 'start',
95
+ * growY: 50,
96
+ * url: 'https://example.com',
97
+ * richText: toRichText('Important **note**!'),
98
+ * scale: 1
99
+ * },
100
+ * meta: {},
101
+ * typeName: 'shape'
102
+ * }
103
+ * ```
104
+ */
38
105
  export type TLNoteShape = TLBaseShape<'note', TLNoteShapeProps>
39
106
 
40
- /** @public */
107
+ /**
108
+ * Validation schema for note shape properties. Defines the runtime validation rules
109
+ * for all properties of note shapes, ensuring data integrity and type safety.
110
+ *
111
+ * @public
112
+ * @example
113
+ * ```ts
114
+ * import { noteShapeProps } from '@tldraw/tlschema'
115
+ *
116
+ * // Used internally by the validation system
117
+ * const validator = T.object(noteShapeProps)
118
+ * const validatedProps = validator.validate(someNoteProps)
119
+ * ```
120
+ */
41
121
  export const noteShapeProps: RecordProps<TLNoteShape> = {
42
122
  color: DefaultColorStyle,
43
123
  labelColor: DefaultLabelColorStyle,
@@ -64,9 +144,22 @@ const Versions = createShapePropsMigrationIds('note', {
64
144
  AddRichText: 9,
65
145
  })
66
146
 
147
+ /**
148
+ * Version identifiers for note shape migrations. These version numbers track
149
+ * significant schema changes over time, enabling proper data migration between versions.
150
+ *
151
+ * @public
152
+ */
67
153
  export { Versions as noteShapeVersions }
68
154
 
69
- /** @public */
155
+ /**
156
+ * Migration sequence for note shapes. Handles schema evolution over time by defining
157
+ * how to upgrade and downgrade note shape data between different versions. Includes
158
+ * migrations for URL properties, text alignment changes, vertical alignment addition,
159
+ * font size adjustments, scaling support, label color, and the transition from plain text to rich text.
160
+ *
161
+ * @public
162
+ */
70
163
  export const noteShapeMigrations = createShapePropsMigrationSequence({
71
164
  sequence: [
72
165
  {
@@ -0,0 +1,407 @@
1
+ import { T } from '@tldraw/validate'
2
+ import { describe, expect, it } from 'vitest'
3
+ import { getTestMigration } from '../__tests__/migrationTestUtils'
4
+ import { TLRichText, toRichText } from '../misc/TLRichText'
5
+ import { DefaultColorStyle } from '../styles/TLColorStyle'
6
+ import { DefaultFontStyle } from '../styles/TLFontStyle'
7
+ import { DefaultSizeStyle } from '../styles/TLSizeStyle'
8
+ import { DefaultTextAlignStyle } from '../styles/TLTextAlignStyle'
9
+ import { textShapeMigrations, textShapeProps, textShapeVersions } from './TLTextShape'
10
+
11
+ describe('TLTextShape', () => {
12
+ describe('textShapeProps validation schema', () => {
13
+ it('should validate using comprehensive object validator', () => {
14
+ const fullValidator = T.object(textShapeProps)
15
+
16
+ const validPropsObject = {
17
+ color: 'red' as const,
18
+ size: 's' as const,
19
+ font: 'mono' as const,
20
+ textAlign: 'end' as const,
21
+ w: 250,
22
+ richText: toRichText('Complete validation test') as TLRichText,
23
+ scale: 0.8,
24
+ autoSize: true,
25
+ }
26
+
27
+ expect(() => fullValidator.validate(validPropsObject)).not.toThrow()
28
+ const result = fullValidator.validate(validPropsObject)
29
+ expect(result).toEqual(validPropsObject)
30
+ })
31
+
32
+ it('should validate width as nonZeroNumber', () => {
33
+ // Valid non-zero positive numbers
34
+ const validWidths = [0.1, 1, 50, 100, 1000, 0.001]
35
+
36
+ validWidths.forEach((w) => {
37
+ expect(() => textShapeProps.w.validate(w)).not.toThrow()
38
+ })
39
+
40
+ // Invalid widths (zero, negative numbers, and non-numbers)
41
+ const invalidWidths = [0, -1, -0.1, 'not-number', null, undefined, {}, [], true, false]
42
+
43
+ invalidWidths.forEach((w) => {
44
+ expect(() => textShapeProps.w.validate(w)).toThrow()
45
+ })
46
+ })
47
+
48
+ it('should validate scale as nonZeroNumber', () => {
49
+ // Valid non-zero positive numbers
50
+ const validScales = [0.1, 0.5, 1, 1.5, 2, 10]
51
+
52
+ validScales.forEach((scale) => {
53
+ expect(() => textShapeProps.scale.validate(scale)).not.toThrow()
54
+ })
55
+
56
+ // Invalid scales (zero, negative numbers, and non-numbers)
57
+ const invalidScales = [0, -0.5, -1, -2, 'not-number', null, undefined, {}, [], true, false]
58
+
59
+ invalidScales.forEach((scale) => {
60
+ expect(() => textShapeProps.scale.validate(scale)).toThrow()
61
+ })
62
+ })
63
+
64
+ it('should use correct default style validators', () => {
65
+ // Verify that the props schema uses the expected style validators
66
+ expect(textShapeProps.color).toBe(DefaultColorStyle)
67
+ expect(textShapeProps.size).toBe(DefaultSizeStyle)
68
+ expect(textShapeProps.font).toBe(DefaultFontStyle)
69
+ expect(textShapeProps.textAlign).toBe(DefaultTextAlignStyle)
70
+ })
71
+
72
+ it('should use correct primitive validators', () => {
73
+ // Check that non-style properties use correct T validators
74
+ expect(textShapeProps.w).toBe(T.nonZeroNumber)
75
+ expect(textShapeProps.scale).toBe(T.nonZeroNumber)
76
+ expect(textShapeProps.autoSize).toBe(T.boolean)
77
+ })
78
+ })
79
+
80
+ describe('textShapeVersions', () => {
81
+ it('should have all expected migration versions', () => {
82
+ const expectedVersions: Array<keyof typeof textShapeVersions> = [
83
+ 'RemoveJustify',
84
+ 'AddTextAlign',
85
+ 'AddRichText',
86
+ ]
87
+
88
+ expectedVersions.forEach((version) => {
89
+ expect(textShapeVersions[version]).toBeDefined()
90
+ expect(typeof textShapeVersions[version]).toBe('string')
91
+ })
92
+ })
93
+ })
94
+
95
+ describe('textShapeMigrations', () => {
96
+ it('should have migrations for all version IDs', () => {
97
+ const migrationIds = textShapeMigrations.sequence
98
+ .filter((migration) => 'id' in migration)
99
+ .map((migration) => ('id' in migration ? migration.id : null))
100
+ .filter(Boolean)
101
+
102
+ const versionIds = Object.values(textShapeVersions)
103
+
104
+ versionIds.forEach((versionId) => {
105
+ expect(migrationIds).toContain(versionId)
106
+ })
107
+ })
108
+ })
109
+
110
+ describe('textShapeMigrations - RemoveJustify migration', () => {
111
+ const { up, down } = getTestMigration(textShapeVersions.RemoveJustify)
112
+
113
+ describe('RemoveJustify up migration', () => {
114
+ it('should convert justify alignment to start', () => {
115
+ const oldRecord = {
116
+ id: 'shape:text1',
117
+ props: {
118
+ color: 'black',
119
+ font: 'draw',
120
+ size: 'm',
121
+ align: 'justify',
122
+ w: 200,
123
+ text: 'Test text',
124
+ scale: 1,
125
+ autoSize: true,
126
+ },
127
+ }
128
+
129
+ const result = up(oldRecord)
130
+ expect(result.props.align).toBe('start')
131
+ expect(result.props.color).toBe('black') // Preserve other props
132
+ expect(result.props.text).toBe('Test text')
133
+ })
134
+
135
+ it('should preserve non-justify alignments', () => {
136
+ const alignments = ['start', 'middle', 'end']
137
+
138
+ alignments.forEach((align) => {
139
+ const oldRecord = {
140
+ id: 'shape:text1',
141
+ props: {
142
+ color: 'red',
143
+ font: 'sans',
144
+ size: 'l',
145
+ align,
146
+ w: 300,
147
+ text: 'Aligned text',
148
+ scale: 1,
149
+ autoSize: false,
150
+ },
151
+ }
152
+
153
+ const result = up(oldRecord)
154
+ expect(result.props.align).toBe(align)
155
+ })
156
+ })
157
+
158
+ it('should preserve all other properties during migration', () => {
159
+ const oldRecord = {
160
+ id: 'shape:text1',
161
+ props: {
162
+ color: 'blue',
163
+ font: 'serif',
164
+ size: 'xl',
165
+ align: 'justify',
166
+ w: 400,
167
+ text: 'Justified text becomes start aligned',
168
+ scale: 1.5,
169
+ autoSize: false,
170
+ },
171
+ }
172
+
173
+ const result = up(oldRecord)
174
+ expect(result.props.align).toBe('start')
175
+ expect(result.props.color).toBe('blue')
176
+ expect(result.props.font).toBe('serif')
177
+ expect(result.props.size).toBe('xl')
178
+ expect(result.props.w).toBe(400)
179
+ expect(result.props.text).toBe('Justified text becomes start aligned')
180
+ expect(result.props.scale).toBe(1.5)
181
+ expect(result.props.autoSize).toBe(false)
182
+ })
183
+ })
184
+
185
+ describe('RemoveJustify down migration', () => {
186
+ it('should be retired (no down migration)', () => {
187
+ expect(() => {
188
+ down({})
189
+ }).toThrow('Migration com.tldraw.shape.text/1 does not have a down function')
190
+ })
191
+ })
192
+ })
193
+
194
+ describe('textShapeMigrations - AddTextAlign migration', () => {
195
+ const { up, down } = getTestMigration(textShapeVersions.AddTextAlign)
196
+
197
+ describe('AddTextAlign up migration', () => {
198
+ it('should migrate align to textAlign', () => {
199
+ const oldRecord = {
200
+ id: 'shape:text1',
201
+ props: {
202
+ color: 'black',
203
+ font: 'draw',
204
+ size: 'm',
205
+ align: 'start',
206
+ w: 200,
207
+ text: 'Test text',
208
+ scale: 1,
209
+ autoSize: true,
210
+ },
211
+ }
212
+
213
+ const result = up(oldRecord)
214
+ expect(result.props.textAlign).toBe('start')
215
+ expect(result.props.align).toBeUndefined()
216
+ expect(result.props.color).toBe('black') // Preserve other props
217
+ })
218
+
219
+ it('should handle all alignment values', () => {
220
+ const alignments = ['start', 'middle', 'end']
221
+
222
+ alignments.forEach((align) => {
223
+ const oldRecord = {
224
+ id: 'shape:text1',
225
+ props: {
226
+ color: 'red',
227
+ align,
228
+ w: 200,
229
+ text: 'Test',
230
+ },
231
+ }
232
+
233
+ const result = up(oldRecord)
234
+ expect(result.props.textAlign).toBe(align)
235
+ expect(result.props.align).toBeUndefined()
236
+ })
237
+ })
238
+
239
+ it('should preserve all other properties during migration', () => {
240
+ const oldRecord = {
241
+ id: 'shape:text1',
242
+ props: {
243
+ color: 'green',
244
+ font: 'mono',
245
+ size: 's',
246
+ align: 'middle',
247
+ w: 150,
248
+ text: 'Migration test',
249
+ scale: 2,
250
+ autoSize: false,
251
+ },
252
+ }
253
+
254
+ const result = up(oldRecord)
255
+ expect(result.props.textAlign).toBe('middle')
256
+ expect(result.props.align).toBeUndefined()
257
+ expect(result.props.color).toBe('green')
258
+ expect(result.props.font).toBe('mono')
259
+ expect(result.props.size).toBe('s')
260
+ expect(result.props.w).toBe(150)
261
+ expect(result.props.text).toBe('Migration test')
262
+ expect(result.props.scale).toBe(2)
263
+ expect(result.props.autoSize).toBe(false)
264
+ })
265
+ })
266
+
267
+ describe('AddTextAlign down migration', () => {
268
+ it('should migrate textAlign back to align', () => {
269
+ const newRecord = {
270
+ id: 'shape:text1',
271
+ props: {
272
+ color: 'black',
273
+ font: 'draw',
274
+ size: 'm',
275
+ textAlign: 'start',
276
+ w: 200,
277
+ text: 'Test text',
278
+ scale: 1,
279
+ autoSize: true,
280
+ },
281
+ }
282
+
283
+ const result = down(newRecord)
284
+ expect(result.props.align).toBe('start')
285
+ expect(result.props.textAlign).toBeUndefined()
286
+ expect(result.props.color).toBe('black') // Preserve other props
287
+ })
288
+
289
+ it('should handle all textAlign values during down migration', () => {
290
+ const alignments = ['start', 'middle', 'end']
291
+
292
+ alignments.forEach((textAlign) => {
293
+ const newRecord = {
294
+ id: 'shape:text1',
295
+ props: {
296
+ color: 'blue',
297
+ textAlign,
298
+ w: 200,
299
+ text: 'Test',
300
+ },
301
+ }
302
+
303
+ const result = down(newRecord)
304
+ expect(result.props.align).toBe(textAlign)
305
+ expect(result.props.textAlign).toBeUndefined()
306
+ })
307
+ })
308
+ })
309
+ })
310
+
311
+ describe('textShapeMigrations - AddRichText migration', () => {
312
+ const { up } = getTestMigration(textShapeVersions.AddRichText)
313
+
314
+ describe('AddRichText up migration', () => {
315
+ it('should convert text property to richText', () => {
316
+ const oldRecord = {
317
+ id: 'shape:text1',
318
+ props: {
319
+ color: 'black',
320
+ font: 'draw',
321
+ size: 'm',
322
+ textAlign: 'start',
323
+ w: 200,
324
+ text: 'Simple text content',
325
+ scale: 1,
326
+ autoSize: true,
327
+ },
328
+ }
329
+
330
+ const result = up(oldRecord)
331
+ expect(result.props.richText).toBeDefined()
332
+ expect(result.props.text).toBeUndefined()
333
+ expect(result.props.color).toBe('black') // Preserve other props
334
+ })
335
+
336
+ it('should handle empty text', () => {
337
+ const oldRecord = {
338
+ id: 'shape:text1',
339
+ props: {
340
+ color: 'red',
341
+ font: 'sans',
342
+ size: 'l',
343
+ textAlign: 'middle',
344
+ w: 300,
345
+ text: '',
346
+ scale: 1,
347
+ autoSize: false,
348
+ },
349
+ }
350
+
351
+ const result = up(oldRecord)
352
+ expect(result.props.richText).toBeDefined()
353
+ expect(result.props.text).toBeUndefined()
354
+ })
355
+
356
+ it('should handle multi-line text', () => {
357
+ const oldRecord = {
358
+ id: 'shape:text1',
359
+ props: {
360
+ color: 'blue',
361
+ font: 'serif',
362
+ size: 'xl',
363
+ textAlign: 'end',
364
+ w: 400,
365
+ text: 'Line 1\nLine 2\nLine 3',
366
+ scale: 1.2,
367
+ autoSize: true,
368
+ },
369
+ }
370
+
371
+ const result = up(oldRecord)
372
+ expect(result.props.richText).toBeDefined()
373
+ expect(result.props.text).toBeUndefined()
374
+ })
375
+
376
+ it('should preserve all other properties during migration', () => {
377
+ const oldRecord = {
378
+ id: 'shape:text1',
379
+ props: {
380
+ color: 'green',
381
+ font: 'mono',
382
+ size: 's',
383
+ textAlign: 'start',
384
+ w: 250,
385
+ text: 'Rich text migration test',
386
+ scale: 0.8,
387
+ autoSize: false,
388
+ },
389
+ }
390
+
391
+ const result = up(oldRecord)
392
+ expect(result.props.richText).toBeDefined()
393
+ expect(result.props.text).toBeUndefined()
394
+ expect(result.props.color).toBe('green')
395
+ expect(result.props.font).toBe('mono')
396
+ expect(result.props.size).toBe('s')
397
+ expect(result.props.textAlign).toBe('start')
398
+ expect(result.props.w).toBe(250)
399
+ expect(result.props.scale).toBe(0.8)
400
+ expect(result.props.autoSize).toBe(false)
401
+ })
402
+ })
403
+
404
+ // Note: The down migration is explicitly not defined (forced client update)
405
+ // so we don't test it
406
+ })
407
+ })
@@ -8,7 +8,25 @@ import { DefaultSizeStyle, TLDefaultSizeStyle } from '../styles/TLSizeStyle'
8
8
  import { DefaultTextAlignStyle, TLDefaultTextAlignStyle } from '../styles/TLTextAlignStyle'
9
9
  import { TLBaseShape } from './TLBaseShape'
10
10
 
11
- /** @public */
11
+ /**
12
+ * Configuration interface defining properties for text shapes in tldraw.
13
+ * Text shapes support rich formatting, styling, and automatic sizing.
14
+ *
15
+ * @public
16
+ * @example
17
+ * ```ts
18
+ * const textProps: TLTextShapeProps = {
19
+ * color: 'black',
20
+ * size: 'm',
21
+ * font: 'draw',
22
+ * textAlign: 'start',
23
+ * w: 200,
24
+ * richText: toRichText('Hello **bold** text'),
25
+ * scale: 1,
26
+ * autoSize: true
27
+ * }
28
+ * ```
29
+ */
12
30
  export interface TLTextShapeProps {
13
31
  color: TLDefaultColorStyle
14
32
  size: TLDefaultSizeStyle
@@ -20,10 +38,57 @@ export interface TLTextShapeProps {
20
38
  autoSize: boolean
21
39
  }
22
40
 
23
- /** @public */
41
+ /**
42
+ * A text shape that can display formatted text content with various styling options.
43
+ * Text shapes support rich formatting, automatic sizing, and consistent styling through
44
+ * the tldraw style system.
45
+ *
46
+ * @public
47
+ * @example
48
+ * ```ts
49
+ * const textShape: TLTextShape = {
50
+ * id: 'shape:text123',
51
+ * typeName: 'shape',
52
+ * type: 'text',
53
+ * x: 100,
54
+ * y: 200,
55
+ * rotation: 0,
56
+ * index: 'a1',
57
+ * parentId: 'page:main',
58
+ * isLocked: false,
59
+ * opacity: 1,
60
+ * props: {
61
+ * color: 'black',
62
+ * size: 'm',
63
+ * font: 'draw',
64
+ * textAlign: 'start',
65
+ * w: 200,
66
+ * richText: toRichText('Sample text'),
67
+ * scale: 1,
68
+ * autoSize: false
69
+ * },
70
+ * meta: {}
71
+ * }
72
+ * ```
73
+ */
24
74
  export type TLTextShape = TLBaseShape<'text', TLTextShapeProps>
25
75
 
26
- /** @public */
76
+ /**
77
+ * Validation schema for text shape properties. This defines the runtime validation
78
+ * rules that ensure text shape data integrity when records are stored or transmitted.
79
+ *
80
+ * @public
81
+ * @example
82
+ * ```ts
83
+ * import { textShapeProps } from '@tldraw/tlschema'
84
+ *
85
+ * // Validate text shape properties
86
+ * const isValid = textShapeProps.richText.isValid(myRichText)
87
+ * if (isValid) {
88
+ * // Properties are valid, safe to use
89
+ * }
90
+ * ```
91
+ */
27
92
  export const textShapeProps: RecordProps<TLTextShape> = {
28
93
  color: DefaultColorStyle,
29
94
  size: DefaultSizeStyle,
@@ -41,9 +106,34 @@ const Versions = createShapePropsMigrationIds('text', {
41
106
  AddRichText: 3,
42
107
  })
43
108
 
109
+ /**
110
+ * Version identifiers for text shape migrations. These constants track
111
+ * the evolution of the text shape schema over time.
112
+ *
113
+ * @public
114
+ * @example
115
+ * ```ts
116
+ * import { textShapeVersions } from '@tldraw/tlschema'
117
+ *
118
+ * // Check if shape data needs migration
119
+ * if (shapeVersion < textShapeVersions.AddRichText) {
120
+ * // Apply rich text migration
121
+ * }
122
+ * ```
123
+ */
44
124
  export { Versions as textShapeVersions }
45
125
 
46
- /** @public */
126
+ /**
127
+ * Migration sequence for text shape schema evolution. This handles transforming
128
+ * text shape data between different versions as the schema evolves over time.
129
+ *
130
+ * Key migrations include:
131
+ * - RemoveJustify: Replaced 'justify' alignment with 'start'
132
+ * - AddTextAlign: Migrated from 'align' to 'textAlign' property
133
+ * - AddRichText: Converted plain text to rich text format
134
+ *
135
+ * @public
136
+ */
47
137
  export const textShapeMigrations = createShapePropsMigrationSequence({
48
138
  sequence: [
49
139
  {