@tldraw/tlschema 4.1.0-next.1b89b40eff1c → 4.1.0-next.2c81540f049b

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
@@ -2,15 +2,63 @@ import { createShapePropsMigrationSequence } from '../records/TLShape'
2
2
  import { RecordProps } from '../recordsWithProps'
3
3
  import { TLBaseShape } from './TLBaseShape'
4
4
 
5
- /** @public */
5
+ /**
6
+ * Properties for a group shape. Group shapes are used to organize and manage collections of shapes as a single unit.
7
+ * Group shapes themselves have no visual properties and serve only as containers.
8
+ *
9
+ * @public
10
+ * @example
11
+ * ```ts
12
+ * const groupProps: TLGroupShapeProps = {}
13
+ * ```
14
+ */
6
15
  // eslint-disable-next-line @typescript-eslint/no-empty-object-type
7
16
  export interface TLGroupShapeProps {}
8
17
 
9
- /** @public */
18
+ /**
19
+ * A group shape that acts as a container for organizing multiple shapes into a single logical unit.
20
+ * Groups enable users to move, transform, and manage collections of shapes together while maintaining
21
+ * their relative positions and properties.
22
+ *
23
+ * @public
24
+ * @example
25
+ * ```ts
26
+ * const groupShape: TLGroupShape = {
27
+ * id: 'shape:group1',
28
+ * type: 'group',
29
+ * x: 100,
30
+ * y: 100,
31
+ * rotation: 0,
32
+ * index: 'a1',
33
+ * parentId: 'page:main',
34
+ * isLocked: false,
35
+ * opacity: 1,
36
+ * props: {},
37
+ * meta: {},
38
+ * typeName: 'shape'
39
+ * }
40
+ * ```
41
+ */
10
42
  export type TLGroupShape = TLBaseShape<'group', TLGroupShapeProps>
11
43
 
12
- /** @public */
44
+ /**
45
+ * Validation schema for group shape properties. Since group shapes have no visual properties,
46
+ * this is an empty object that serves as a placeholder for the schema system.
47
+ *
48
+ * @public
49
+ * @example
50
+ * ```ts
51
+ * import { groupShapeProps } from '@tldraw/tlschema'
52
+ *
53
+ * // Used internally by the validation system
54
+ * const validator = T.object(groupShapeProps)
55
+ * ```
56
+ */
13
57
  export const groupShapeProps: RecordProps<TLGroupShape> = {}
14
58
 
15
- /** @public */
59
+ /**
60
+ * Migration sequence for group shapes. Currently contains no migrations.
61
+ *
62
+ * @public
63
+ */
16
64
  export const groupShapeMigrations = createShapePropsMigrationSequence({ sequence: [] })
@@ -0,0 +1,325 @@
1
+ import { T } from '@tldraw/validate'
2
+ import { describe, expect, it, test } from 'vitest'
3
+ import { getTestMigration } from '../__tests__/migrationTestUtils'
4
+ import {
5
+ highlightShapeMigrations,
6
+ highlightShapeProps,
7
+ highlightShapeVersions,
8
+ } from './TLHighlightShape'
9
+
10
+ describe('TLHighlightShape', () => {
11
+ describe('highlightShapeProps validation schema', () => {
12
+ it('should validate valid highlight shape properties', () => {
13
+ const validProps = {
14
+ color: 'yellow' as const,
15
+ size: 'l' as const,
16
+ segments: [
17
+ {
18
+ type: 'free' as const,
19
+ points: [{ x: 0, y: 0, z: 0.5 }],
20
+ },
21
+ ],
22
+ isComplete: true,
23
+ isPen: false,
24
+ scale: 1,
25
+ }
26
+
27
+ const fullValidator = T.object(highlightShapeProps)
28
+ expect(() => fullValidator.validate(validProps)).not.toThrow()
29
+ })
30
+
31
+ it('should reject invalid color values', () => {
32
+ const invalidColors = ['purple', 'YELLOW', 'neon', '', null, undefined, 123, {}, []]
33
+
34
+ invalidColors.forEach((color) => {
35
+ expect(() => highlightShapeProps.color.validate(color)).toThrow()
36
+ })
37
+ })
38
+
39
+ it('should reject invalid size values', () => {
40
+ const invalidSizes = ['medium', 'SM', 'large', 'xxl', '', null, undefined, 123]
41
+
42
+ invalidSizes.forEach((size) => {
43
+ expect(() => highlightShapeProps.size.validate(size)).toThrow()
44
+ })
45
+ })
46
+
47
+ it('should validate segments array', () => {
48
+ const validSegmentArrays = [
49
+ [], // Empty array
50
+ [
51
+ {
52
+ type: 'free' as const,
53
+ points: [{ x: 0, y: 0, z: 0.5 }],
54
+ },
55
+ ],
56
+ ]
57
+
58
+ validSegmentArrays.forEach((segments) => {
59
+ expect(() => highlightShapeProps.segments.validate(segments)).not.toThrow()
60
+ })
61
+
62
+ const invalidSegmentArrays = [
63
+ 'not-array',
64
+ null,
65
+ undefined,
66
+ [{ type: 'invalid', points: [] }], // Invalid segment type
67
+ ]
68
+
69
+ invalidSegmentArrays.forEach((segments) => {
70
+ expect(() => highlightShapeProps.segments.validate(segments)).toThrow()
71
+ })
72
+ })
73
+
74
+ it('should validate boolean properties', () => {
75
+ // Valid boolean values
76
+ expect(() => highlightShapeProps.isComplete.validate(true)).not.toThrow()
77
+ expect(() => highlightShapeProps.isComplete.validate(false)).not.toThrow()
78
+ expect(() => highlightShapeProps.isPen.validate(true)).not.toThrow()
79
+ expect(() => highlightShapeProps.isPen.validate(false)).not.toThrow()
80
+
81
+ // Invalid boolean values
82
+ const invalidBooleans = ['true', 'false', 1, 0, null, undefined, {}, []]
83
+ invalidBooleans.forEach((value) => {
84
+ expect(() => highlightShapeProps.isComplete.validate(value)).toThrow()
85
+ expect(() => highlightShapeProps.isPen.validate(value)).toThrow()
86
+ })
87
+ })
88
+
89
+ it('should validate scale as nonZeroNumber', () => {
90
+ // Valid non-zero positive numbers
91
+ expect(() => highlightShapeProps.scale.validate(0.1)).not.toThrow()
92
+ expect(() => highlightShapeProps.scale.validate(1)).not.toThrow()
93
+ expect(() => highlightShapeProps.scale.validate(2)).not.toThrow()
94
+
95
+ // Invalid scales (zero, negative, and non-numbers)
96
+ expect(() => highlightShapeProps.scale.validate(0)).toThrow()
97
+ expect(() => highlightShapeProps.scale.validate(-1)).toThrow()
98
+ expect(() => highlightShapeProps.scale.validate('not-number')).toThrow()
99
+ expect(() => highlightShapeProps.scale.validate(null)).toThrow()
100
+ })
101
+
102
+ it('should reject objects with missing properties', () => {
103
+ const fullValidator = T.object(highlightShapeProps)
104
+
105
+ expect(() => fullValidator.validate({})).toThrow()
106
+ expect(() => fullValidator.validate({ color: 'yellow' })).toThrow()
107
+ })
108
+
109
+ it('should reject objects with extra properties', () => {
110
+ const fullValidator = T.object(highlightShapeProps)
111
+
112
+ const objectWithExtraProps = {
113
+ color: 'yellow',
114
+ size: 'm',
115
+ segments: [],
116
+ isComplete: true,
117
+ isPen: false,
118
+ scale: 1,
119
+ extraProp: 'extra',
120
+ }
121
+
122
+ expect(() => fullValidator.validate(objectWithExtraProps)).toThrow()
123
+ })
124
+ })
125
+
126
+ describe('highlightShapeVersions', () => {
127
+ it('should contain expected migration version IDs', () => {
128
+ expect(highlightShapeVersions).toBeDefined()
129
+ expect(typeof highlightShapeVersions).toBe('object')
130
+ })
131
+
132
+ it('should have all expected migration versions', () => {
133
+ const expectedVersions: Array<keyof typeof highlightShapeVersions> = ['AddScale']
134
+
135
+ expectedVersions.forEach((version) => {
136
+ expect(highlightShapeVersions[version]).toBeDefined()
137
+ expect(typeof highlightShapeVersions[version]).toBe('string')
138
+ })
139
+ })
140
+
141
+ it('should have properly formatted migration IDs', () => {
142
+ Object.values(highlightShapeVersions).forEach((versionId) => {
143
+ expect(versionId).toMatch(/^com\.tldraw\.shape\.highlight\//)
144
+ expect(versionId).toMatch(/\/\d+$/) // Should end with /number
145
+ })
146
+ })
147
+ })
148
+
149
+ describe('highlightShapeMigrations', () => {
150
+ it('should be defined and have required structure', () => {
151
+ expect(highlightShapeMigrations).toBeDefined()
152
+ expect(highlightShapeMigrations.sequence).toBeDefined()
153
+ expect(Array.isArray(highlightShapeMigrations.sequence)).toBe(true)
154
+ })
155
+
156
+ it('should have migrations for all version IDs', () => {
157
+ const migrationIds = highlightShapeMigrations.sequence
158
+ .filter((migration) => 'id' in migration)
159
+ .map((migration) => ('id' in migration ? migration.id : null))
160
+ .filter(Boolean)
161
+
162
+ const versionIds = Object.values(highlightShapeVersions)
163
+
164
+ versionIds.forEach((versionId) => {
165
+ expect(migrationIds).toContain(versionId)
166
+ })
167
+ })
168
+ })
169
+
170
+ describe('highlightShapeMigrations - AddScale migration', () => {
171
+ const { up, down } = getTestMigration(highlightShapeVersions.AddScale)
172
+
173
+ describe('AddScale up migration', () => {
174
+ it('should add scale property with default value 1', () => {
175
+ const oldRecord = {
176
+ id: 'shape:highlight1',
177
+ props: {
178
+ color: 'yellow',
179
+ size: 'm',
180
+ segments: [
181
+ {
182
+ type: 'free',
183
+ points: [{ x: 0, y: 0, z: 0.5 }],
184
+ },
185
+ ],
186
+ isComplete: true,
187
+ isPen: false,
188
+ },
189
+ }
190
+
191
+ const result = up(oldRecord)
192
+ expect(result.props.scale).toBe(1)
193
+ })
194
+
195
+ it('should preserve existing properties during migration', () => {
196
+ const oldRecord = {
197
+ id: 'shape:highlight1',
198
+ props: {
199
+ color: 'green',
200
+ size: 'l',
201
+ segments: [
202
+ {
203
+ type: 'straight',
204
+ points: [
205
+ { x: 10, y: 20 },
206
+ { x: 100, y: 150 },
207
+ ],
208
+ },
209
+ ],
210
+ isComplete: true,
211
+ isPen: true,
212
+ },
213
+ }
214
+
215
+ const result = up(oldRecord)
216
+ expect(result.props.scale).toBe(1)
217
+ expect(result.props.color).toBe('green')
218
+ expect(result.props.size).toBe('l')
219
+ expect(result.props.segments).toEqual(oldRecord.props.segments)
220
+ expect(result.props.isComplete).toBe(true)
221
+ expect(result.props.isPen).toBe(true)
222
+ })
223
+ })
224
+
225
+ describe('AddScale down migration', () => {
226
+ it('should remove scale property', () => {
227
+ const newRecord = {
228
+ id: 'shape:highlight1',
229
+ props: {
230
+ color: 'yellow',
231
+ size: 'm',
232
+ segments: [
233
+ {
234
+ type: 'free',
235
+ points: [{ x: 0, y: 0, z: 0.5 }],
236
+ },
237
+ ],
238
+ isComplete: true,
239
+ isPen: false,
240
+ scale: 1.5,
241
+ },
242
+ }
243
+
244
+ const result = down(newRecord)
245
+ expect(result.props.scale).toBeUndefined()
246
+ expect(result.props.color).toBe('yellow') // Preserve other props
247
+ expect(result.props.isPen).toBe(false) // Preserve other props
248
+ })
249
+
250
+ it('should preserve all other properties during down migration', () => {
251
+ const newRecord = {
252
+ id: 'shape:highlight1',
253
+ props: {
254
+ color: 'red',
255
+ size: 'xl',
256
+ segments: [
257
+ {
258
+ type: 'straight',
259
+ points: [
260
+ { x: 0, y: 0 },
261
+ { x: 200, y: 0 },
262
+ ],
263
+ },
264
+ ],
265
+ isComplete: true,
266
+ isPen: false,
267
+ scale: 2.0,
268
+ },
269
+ }
270
+
271
+ const result = down(newRecord)
272
+ expect(result.props.scale).toBeUndefined()
273
+ expect(result.props.color).toBe('red')
274
+ expect(result.props.size).toBe('xl')
275
+ expect(result.props.segments).toEqual(newRecord.props.segments)
276
+ expect(result.props.isComplete).toBe(true)
277
+ expect(result.props.isPen).toBe(false)
278
+ })
279
+ })
280
+
281
+ it('should support round-trip migration (up then down)', () => {
282
+ const originalRecord = {
283
+ id: 'shape:highlight1',
284
+ props: {
285
+ color: 'green',
286
+ size: 'l',
287
+ segments: [
288
+ {
289
+ type: 'free',
290
+ points: [{ x: 0, y: 0, z: 0.5 }],
291
+ },
292
+ ],
293
+ isComplete: true,
294
+ isPen: true,
295
+ },
296
+ }
297
+
298
+ // Apply up migration
299
+ const upResult = up(originalRecord)
300
+ expect(upResult.props.scale).toBe(1)
301
+
302
+ // Apply down migration
303
+ const downResult = down(upResult)
304
+ expect(downResult.props.scale).toBeUndefined()
305
+ expect(downResult.props.color).toBe('green')
306
+ expect(downResult.props.size).toBe('l')
307
+ expect(downResult.props.isComplete).toBe(true)
308
+ expect(downResult.props.isPen).toBe(true)
309
+ })
310
+ })
311
+
312
+ test('should handle all migration versions in correct order', () => {
313
+ const expectedOrder: Array<keyof typeof highlightShapeVersions> = ['AddScale']
314
+
315
+ const migrationIds = highlightShapeMigrations.sequence
316
+ .filter((migration) => 'id' in migration)
317
+ .map((migration) => ('id' in migration ? migration.id : ''))
318
+ .filter(Boolean)
319
+
320
+ expectedOrder.forEach((expectedVersion) => {
321
+ const versionId = highlightShapeVersions[expectedVersion]
322
+ expect(migrationIds).toContain(versionId)
323
+ })
324
+ })
325
+ })
@@ -6,20 +6,84 @@ import { DefaultSizeStyle, TLDefaultSizeStyle } from '../styles/TLSizeStyle'
6
6
  import { TLBaseShape } from './TLBaseShape'
7
7
  import { DrawShapeSegment, TLDrawShapeSegment } from './TLDrawShape'
8
8
 
9
- /** @public */
9
+ /**
10
+ * Properties for a highlight shape. Highlight shapes represent highlighting strokes made with
11
+ * a highlighting tool, typically used to emphasize or mark up content.
12
+ *
13
+ * @public
14
+ * @example
15
+ * ```ts
16
+ * const highlightProps: TLHighlightShapeProps = {
17
+ * color: 'yellow',
18
+ * size: 'm',
19
+ * segments: [{ type: 'straight', points: [{ x: 0, y: 0, z: 0.5 }] }],
20
+ * isComplete: true,
21
+ * isPen: false,
22
+ * scale: 1
23
+ * }
24
+ * ```
25
+ */
10
26
  export interface TLHighlightShapeProps {
27
+ /** The color style of the highlight stroke */
11
28
  color: TLDefaultColorStyle
29
+ /** The size style determining the thickness of the highlight stroke */
12
30
  size: TLDefaultSizeStyle
31
+ /** Array of segments that make up the highlight stroke path */
13
32
  segments: TLDrawShapeSegment[]
33
+ /** Whether the highlight stroke has been completed by the user */
14
34
  isComplete: boolean
35
+ /** Whether the highlight was drawn with a pen/stylus (affects rendering style) */
15
36
  isPen: boolean
37
+ /** Scale factor applied to the highlight shape for display */
16
38
  scale: number
17
39
  }
18
40
 
19
- /** @public */
41
+ /**
42
+ * A highlight shape representing a highlighting stroke drawn by the user. Highlight shapes
43
+ * are typically semi-transparent and used for marking up or emphasizing content on the canvas.
44
+ *
45
+ * @public
46
+ * @example
47
+ * ```ts
48
+ * const highlightShape: TLHighlightShape = {
49
+ * id: 'shape:highlight1',
50
+ * type: 'highlight',
51
+ * x: 100,
52
+ * y: 50,
53
+ * rotation: 0,
54
+ * index: 'a1',
55
+ * parentId: 'page:main',
56
+ * isLocked: false,
57
+ * opacity: 0.7,
58
+ * props: {
59
+ * color: 'yellow',
60
+ * size: 'l',
61
+ * segments: [],
62
+ * isComplete: false,
63
+ * isPen: false,
64
+ * scale: 1
65
+ * },
66
+ * meta: {},
67
+ * typeName: 'shape'
68
+ * }
69
+ * ```
70
+ */
20
71
  export type TLHighlightShape = TLBaseShape<'highlight', TLHighlightShapeProps>
21
72
 
22
- /** @public */
73
+ /**
74
+ * Validation schema for highlight shape properties. Defines the runtime validation rules
75
+ * for all properties of highlight shapes.
76
+ *
77
+ * @public
78
+ * @example
79
+ * ```ts
80
+ * import { highlightShapeProps } from '@tldraw/tlschema'
81
+ *
82
+ * // Used internally by the validation system
83
+ * const validator = T.object(highlightShapeProps)
84
+ * const validatedProps = validator.validate(someHighlightProps)
85
+ * ```
86
+ */
23
87
  export const highlightShapeProps: RecordProps<TLHighlightShape> = {
24
88
  color: DefaultColorStyle,
25
89
  size: DefaultSizeStyle,
@@ -33,9 +97,20 @@ const Versions = createShapePropsMigrationIds('highlight', {
33
97
  AddScale: 1,
34
98
  })
35
99
 
100
+ /**
101
+ * Version identifiers for highlight shape migrations. These version numbers track
102
+ * schema changes over time to enable proper data migration.
103
+ *
104
+ * @public
105
+ */
36
106
  export { Versions as highlightShapeVersions }
37
107
 
38
- /** @public */
108
+ /**
109
+ * Migration sequence for highlight shapes. Handles schema evolution over time by defining
110
+ * how to upgrade and downgrade highlight shape data between different versions.
111
+ *
112
+ * @public
113
+ */
39
114
  export const highlightShapeMigrations = createShapePropsMigrationSequence({
40
115
  sequence: [
41
116
  {