@tldraw/tlschema 4.2.2 → 4.2.3

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 +71 -242
  4. package/dist-cjs/index.js +1 -4
  5. package/dist-cjs/index.js.map +2 -2
  6. package/dist-cjs/misc/TLOpacity.js +5 -1
  7. package/dist-cjs/misc/TLOpacity.js.map +2 -2
  8. package/dist-cjs/misc/TLRichText.js +1 -5
  9. package/dist-cjs/misc/TLRichText.js.map +2 -2
  10. package/dist-cjs/records/TLAsset.js.map +1 -1
  11. package/dist-cjs/records/TLBinding.js.map +2 -2
  12. package/dist-cjs/records/TLShape.js.map +2 -2
  13. package/dist-cjs/shapes/ShapeWithCrop.js.map +1 -1
  14. package/dist-cjs/shapes/TLArrowShape.js +13 -26
  15. package/dist-cjs/shapes/TLArrowShape.js.map +2 -2
  16. package/dist-cjs/shapes/TLBaseShape.js.map +2 -2
  17. package/dist-cjs/shapes/TLDrawShape.js +4 -37
  18. package/dist-cjs/shapes/TLDrawShape.js.map +2 -2
  19. package/dist-cjs/shapes/TLEmbedShape.js +0 -17
  20. package/dist-cjs/shapes/TLEmbedShape.js.map +2 -2
  21. package/dist-cjs/shapes/TLGeoShape.js +1 -12
  22. package/dist-cjs/shapes/TLGeoShape.js.map +2 -2
  23. package/dist-cjs/shapes/TLHighlightShape.js +2 -29
  24. package/dist-cjs/shapes/TLHighlightShape.js.map +2 -2
  25. package/dist-cjs/shapes/TLNoteShape.js +1 -12
  26. package/dist-cjs/shapes/TLNoteShape.js.map +2 -2
  27. package/dist-cjs/shapes/TLTextShape.js +1 -12
  28. package/dist-cjs/shapes/TLTextShape.js.map +2 -2
  29. package/dist-cjs/store-migrations.js +15 -15
  30. package/dist-cjs/store-migrations.js.map +2 -2
  31. package/dist-esm/bindings/TLBaseBinding.mjs.map +2 -2
  32. package/dist-esm/createTLSchema.mjs.map +2 -2
  33. package/dist-esm/index.d.mts +71 -242
  34. package/dist-esm/index.mjs +1 -5
  35. package/dist-esm/index.mjs.map +2 -2
  36. package/dist-esm/misc/TLOpacity.mjs +5 -1
  37. package/dist-esm/misc/TLOpacity.mjs.map +2 -2
  38. package/dist-esm/misc/TLRichText.mjs +1 -5
  39. package/dist-esm/misc/TLRichText.mjs.map +2 -2
  40. package/dist-esm/records/TLAsset.mjs.map +1 -1
  41. package/dist-esm/records/TLBinding.mjs.map +2 -2
  42. package/dist-esm/records/TLShape.mjs.map +2 -2
  43. package/dist-esm/shapes/TLArrowShape.mjs +13 -26
  44. package/dist-esm/shapes/TLArrowShape.mjs.map +2 -2
  45. package/dist-esm/shapes/TLBaseShape.mjs.map +2 -2
  46. package/dist-esm/shapes/TLDrawShape.mjs +4 -37
  47. package/dist-esm/shapes/TLDrawShape.mjs.map +2 -2
  48. package/dist-esm/shapes/TLEmbedShape.mjs +0 -17
  49. package/dist-esm/shapes/TLEmbedShape.mjs.map +2 -2
  50. package/dist-esm/shapes/TLGeoShape.mjs +1 -12
  51. package/dist-esm/shapes/TLGeoShape.mjs.map +2 -2
  52. package/dist-esm/shapes/TLHighlightShape.mjs +2 -29
  53. package/dist-esm/shapes/TLHighlightShape.mjs.map +2 -2
  54. package/dist-esm/shapes/TLNoteShape.mjs +1 -12
  55. package/dist-esm/shapes/TLNoteShape.mjs.map +2 -2
  56. package/dist-esm/shapes/TLTextShape.mjs +1 -12
  57. package/dist-esm/shapes/TLTextShape.mjs.map +2 -2
  58. package/dist-esm/store-migrations.mjs +15 -15
  59. package/dist-esm/store-migrations.mjs.map +2 -2
  60. package/package.json +8 -8
  61. package/src/__tests__/migrationTestUtils.ts +3 -9
  62. package/src/assets/TLBookmarkAsset.test.ts +96 -0
  63. package/src/assets/TLImageAsset.test.ts +213 -0
  64. package/src/assets/TLVideoAsset.test.ts +105 -0
  65. package/src/bindings/TLArrowBinding.test.ts +55 -0
  66. package/src/bindings/TLBaseBinding.ts +14 -25
  67. package/src/createTLSchema.ts +2 -8
  68. package/src/index.ts +0 -9
  69. package/src/migrations.test.ts +1 -149
  70. package/src/misc/TLOpacity.ts +5 -1
  71. package/src/misc/TLRichText.ts +1 -6
  72. package/src/misc/id-validator.test.ts +50 -0
  73. package/src/records/TLAsset.test.ts +234 -0
  74. package/src/records/TLAsset.ts +2 -2
  75. package/src/records/TLBinding.test.ts +22 -0
  76. package/src/records/TLBinding.ts +23 -65
  77. package/src/records/TLCamera.test.ts +19 -0
  78. package/src/records/TLDocument.test.ts +35 -0
  79. package/src/records/TLInstance.test.ts +201 -0
  80. package/src/records/TLPage.test.ts +110 -0
  81. package/src/records/TLPageState.test.ts +228 -0
  82. package/src/records/TLPointer.test.ts +63 -0
  83. package/src/records/TLPresence.test.ts +190 -0
  84. package/src/records/TLRecord.test.ts +70 -0
  85. package/src/records/TLShape.test.ts +232 -0
  86. package/src/records/TLShape.ts +5 -100
  87. package/src/shapes/ShapeWithCrop.test.ts +18 -0
  88. package/src/shapes/ShapeWithCrop.ts +2 -2
  89. package/src/shapes/TLArrowShape.test.ts +505 -0
  90. package/src/shapes/TLArrowShape.ts +14 -28
  91. package/src/shapes/TLBaseShape.test.ts +142 -0
  92. package/src/shapes/TLBaseShape.ts +10 -34
  93. package/src/shapes/TLBookmarkShape.test.ts +122 -0
  94. package/src/shapes/TLDrawShape.test.ts +177 -0
  95. package/src/shapes/TLDrawShape.ts +12 -59
  96. package/src/shapes/TLEmbedShape.test.ts +286 -0
  97. package/src/shapes/TLEmbedShape.ts +0 -17
  98. package/src/shapes/TLFrameShape.test.ts +71 -0
  99. package/src/shapes/TLGeoShape.test.ts +247 -0
  100. package/src/shapes/TLGeoShape.ts +1 -14
  101. package/src/shapes/TLGroupShape.test.ts +59 -0
  102. package/src/shapes/TLHighlightShape.test.ts +325 -0
  103. package/src/shapes/TLHighlightShape.ts +0 -37
  104. package/src/shapes/TLImageShape.test.ts +534 -0
  105. package/src/shapes/TLLineShape.test.ts +269 -0
  106. package/src/shapes/TLNoteShape.test.ts +1568 -0
  107. package/src/shapes/TLNoteShape.ts +1 -15
  108. package/src/shapes/TLTextShape.test.ts +407 -0
  109. package/src/shapes/TLTextShape.ts +2 -16
  110. package/src/shapes/TLVideoShape.test.ts +112 -0
  111. package/src/store-migrations.ts +16 -17
  112. package/src/styles/TLColorStyle.test.ts +439 -0
  113. package/dist-cjs/misc/b64Vecs.js +0 -224
  114. package/dist-cjs/misc/b64Vecs.js.map +0 -7
  115. package/dist-esm/misc/b64Vecs.mjs +0 -204
  116. package/dist-esm/misc/b64Vecs.mjs.map +0 -7
  117. package/src/misc/b64Vecs.ts +0 -308
@@ -0,0 +1,439 @@
1
+ import { describe, expect, it, test } from 'vitest'
2
+ import {
3
+ defaultColorNames,
4
+ DefaultColorStyle,
5
+ DefaultColorThemePalette,
6
+ DefaultLabelColorStyle,
7
+ getColorValue,
8
+ getDefaultColorTheme,
9
+ isDefaultThemeColor,
10
+ TLDefaultColorStyle,
11
+ TLDefaultColorThemeColor,
12
+ } from './TLColorStyle'
13
+
14
+ describe('TLColorStyle', () => {
15
+ describe('defaultColorNames', () => {
16
+ it('should contain expected default colors', () => {
17
+ expect(defaultColorNames).toEqual([
18
+ 'black',
19
+ 'grey',
20
+ 'light-violet',
21
+ 'violet',
22
+ 'blue',
23
+ 'light-blue',
24
+ 'yellow',
25
+ 'orange',
26
+ 'green',
27
+ 'light-green',
28
+ 'light-red',
29
+ 'red',
30
+ 'white',
31
+ ])
32
+ })
33
+ })
34
+
35
+ describe('DefaultColorThemePalette', () => {
36
+ it('should contain all default colors for both themes', () => {
37
+ const lightTheme = DefaultColorThemePalette.lightMode
38
+ const darkTheme = DefaultColorThemePalette.darkMode
39
+
40
+ defaultColorNames.forEach((colorName) => {
41
+ expect(lightTheme).toHaveProperty(colorName)
42
+ expect(darkTheme).toHaveProperty(colorName)
43
+ })
44
+ })
45
+ })
46
+
47
+ describe('getDefaultColorTheme', () => {
48
+ it('should return light theme when isDarkMode is false', () => {
49
+ const result = getDefaultColorTheme({ isDarkMode: false })
50
+ expect(result).toBe(DefaultColorThemePalette.lightMode)
51
+ })
52
+
53
+ it('should return dark theme when isDarkMode is true', () => {
54
+ const result = getDefaultColorTheme({ isDarkMode: true })
55
+ expect(result).toBe(DefaultColorThemePalette.darkMode)
56
+ })
57
+ })
58
+
59
+ describe('DefaultColorStyle', () => {
60
+ it('should validate all default color names', () => {
61
+ defaultColorNames.forEach((color) => {
62
+ expect(() => DefaultColorStyle.validate(color)).not.toThrow()
63
+ expect(DefaultColorStyle.validate(color)).toBe(color)
64
+ })
65
+ })
66
+
67
+ it('should reject invalid color names', () => {
68
+ const invalidColors = ['invalid', 'pink', 'purple', 'cyan', '']
69
+
70
+ invalidColors.forEach((color) => {
71
+ expect(() => DefaultColorStyle.validate(color)).toThrow()
72
+ })
73
+ })
74
+ })
75
+
76
+ describe('DefaultLabelColorStyle', () => {
77
+ it('should be a StyleProp with correct configuration', () => {
78
+ expect(DefaultLabelColorStyle.id).toBe('tldraw:labelColor')
79
+ expect(DefaultLabelColorStyle.defaultValue).toBe('black')
80
+ })
81
+
82
+ it('should validate all default color names', () => {
83
+ defaultColorNames.forEach((color) => {
84
+ expect(() => DefaultLabelColorStyle.validate(color)).not.toThrow()
85
+ expect(DefaultLabelColorStyle.validate(color)).toBe(color)
86
+ })
87
+ })
88
+
89
+ it('should reject invalid color names', () => {
90
+ const invalidColors = ['invalid', 'pink', 'purple', 'cyan', '']
91
+
92
+ invalidColors.forEach((color) => {
93
+ expect(() => DefaultLabelColorStyle.validate(color)).toThrow()
94
+ })
95
+ })
96
+
97
+ it('should have values property with all default colors', () => {
98
+ expect(DefaultLabelColorStyle.values).toEqual(defaultColorNames)
99
+ })
100
+
101
+ it('should be separate from DefaultColorStyle', () => {
102
+ expect(DefaultColorStyle.id).not.toBe(DefaultLabelColorStyle.id)
103
+ expect(DefaultColorStyle).not.toBe(DefaultLabelColorStyle)
104
+ })
105
+
106
+ it('should allow different default values from DefaultColorStyle', () => {
107
+ const originalColorDefault = DefaultColorStyle.defaultValue
108
+ const originalLabelDefault = DefaultLabelColorStyle.defaultValue
109
+
110
+ DefaultColorStyle.setDefaultValue('red')
111
+ DefaultLabelColorStyle.setDefaultValue('blue')
112
+
113
+ expect(DefaultColorStyle.defaultValue).toBe('red')
114
+ expect(DefaultLabelColorStyle.defaultValue).toBe('blue')
115
+ expect(DefaultColorStyle.defaultValue).not.toBe(DefaultLabelColorStyle.defaultValue)
116
+
117
+ // Restore originals
118
+ DefaultColorStyle.setDefaultValue(originalColorDefault)
119
+ DefaultLabelColorStyle.setDefaultValue(originalLabelDefault)
120
+ })
121
+ })
122
+
123
+ describe('isDefaultThemeColor', () => {
124
+ it('should return true for all default color names', () => {
125
+ defaultColorNames.forEach((color) => {
126
+ expect(isDefaultThemeColor(color)).toBe(true)
127
+ })
128
+ })
129
+
130
+ it('should return false for invalid color names', () => {
131
+ const invalidColors = ['invalid', 'pink', 'purple', 'cyan', '', 'custom-color']
132
+
133
+ invalidColors.forEach((color) => {
134
+ expect(isDefaultThemeColor(color as TLDefaultColorStyle)).toBe(false)
135
+ })
136
+ })
137
+
138
+ it('should return false for hex colors', () => {
139
+ const hexColors = ['#ff0000', '#00ff00', '#0000ff', '#ffffff', '#000000']
140
+
141
+ hexColors.forEach((color) => {
142
+ expect(isDefaultThemeColor(color as TLDefaultColorStyle)).toBe(false)
143
+ })
144
+ })
145
+
146
+ it('should return false for CSS color names not in defaults', () => {
147
+ // Use CSS color names that are NOT in the default color list
148
+ const cssColors = ['aqua', 'fuchsia', 'lime', 'navy', 'silver']
149
+
150
+ cssColors.forEach((color) => {
151
+ expect(isDefaultThemeColor(color as TLDefaultColorStyle)).toBe(false)
152
+ })
153
+ })
154
+
155
+ it('should handle edge cases', () => {
156
+ expect(isDefaultThemeColor(null as any)).toBe(false)
157
+ expect(isDefaultThemeColor(undefined as any)).toBe(false)
158
+ expect(isDefaultThemeColor(123 as any)).toBe(false)
159
+ expect(isDefaultThemeColor({} as any)).toBe(false)
160
+ })
161
+
162
+ it('should be case sensitive', () => {
163
+ expect(isDefaultThemeColor('Black' as TLDefaultColorStyle)).toBe(false)
164
+ expect(isDefaultThemeColor('RED' as TLDefaultColorStyle)).toBe(false)
165
+ expect(isDefaultThemeColor('Blue' as TLDefaultColorStyle)).toBe(false)
166
+ })
167
+
168
+ it('should work as a type guard', () => {
169
+ const color: TLDefaultColorStyle = 'red'
170
+
171
+ if (isDefaultThemeColor(color)) {
172
+ // TypeScript should narrow the type here
173
+ expect(defaultColorNames.includes(color)).toBe(true)
174
+ }
175
+ })
176
+ })
177
+
178
+ describe('getColorValue', () => {
179
+ const lightTheme = DefaultColorThemePalette.lightMode
180
+ const darkTheme = DefaultColorThemePalette.darkMode
181
+
182
+ it('should return correct color values for default colors', () => {
183
+ expect(getColorValue(lightTheme, 'red', 'solid')).toBe('#e03131')
184
+ expect(getColorValue(lightTheme, 'blue', 'fill')).toBe('#4465e9')
185
+ expect(getColorValue(lightTheme, 'black', 'solid')).toBe('#1d1d1d')
186
+ })
187
+
188
+ it('should work with different variants', () => {
189
+ const variants: (keyof TLDefaultColorThemeColor)[] = [
190
+ 'solid',
191
+ 'semi',
192
+ 'pattern',
193
+ 'fill',
194
+ 'frameHeadingStroke',
195
+ 'frameHeadingFill',
196
+ 'frameStroke',
197
+ 'frameFill',
198
+ 'frameText',
199
+ 'noteFill',
200
+ 'noteText',
201
+ 'highlightSrgb',
202
+ 'highlightP3',
203
+ ]
204
+
205
+ variants.forEach((variant) => {
206
+ const result = getColorValue(lightTheme, 'red', variant)
207
+ expect(typeof result).toBe('string')
208
+ expect(result.length).toBeGreaterThan(0)
209
+ expect(result).toBe(lightTheme.red[variant])
210
+ })
211
+ })
212
+
213
+ it('should return different values for light and dark themes', () => {
214
+ // Use colors/variants that actually differ between themes
215
+ expect(getColorValue(lightTheme, 'black', 'solid')).not.toBe(
216
+ getColorValue(darkTheme, 'black', 'solid')
217
+ )
218
+ expect(getColorValue(lightTheme, 'white', 'solid')).not.toBe(
219
+ getColorValue(darkTheme, 'white', 'solid')
220
+ )
221
+ })
222
+
223
+ it('should pass through non-default colors unchanged', () => {
224
+ const customColors = ['#ff0000', '#00ff00', '#0000ff', 'custom-color', 'rgb(255, 0, 0)']
225
+
226
+ customColors.forEach((color) => {
227
+ expect(getColorValue(lightTheme, color as TLDefaultColorStyle, 'solid')).toBe(color)
228
+ expect(getColorValue(darkTheme, color as TLDefaultColorStyle, 'fill')).toBe(color)
229
+ })
230
+ })
231
+
232
+ it('should work with all default colors', () => {
233
+ defaultColorNames.forEach((colorName) => {
234
+ const lightResult = getColorValue(lightTheme, colorName, 'solid')
235
+ const darkResult = getColorValue(darkTheme, colorName, 'solid')
236
+
237
+ expect(typeof lightResult).toBe('string')
238
+ expect(typeof darkResult).toBe('string')
239
+ expect(lightResult).toBe(lightTheme[colorName].solid)
240
+ expect(darkResult).toBe(darkTheme[colorName].solid)
241
+ })
242
+ })
243
+
244
+ it('should handle edge cases gracefully', () => {
245
+ // Test with empty string
246
+ expect(getColorValue(lightTheme, '' as TLDefaultColorStyle, 'solid')).toBe('')
247
+
248
+ // Test with null/undefined (they should pass through)
249
+ expect(getColorValue(lightTheme, null as any, 'solid')).toBe(null)
250
+ expect(getColorValue(lightTheme, undefined as any, 'solid')).toBe(undefined)
251
+ })
252
+
253
+ it('should work consistently across all variants for a color', () => {
254
+ const color = 'blue'
255
+ const variants: (keyof TLDefaultColorThemeColor)[] = [
256
+ 'solid',
257
+ 'semi',
258
+ 'pattern',
259
+ 'fill',
260
+ 'frameHeadingStroke',
261
+ 'frameHeadingFill',
262
+ 'frameStroke',
263
+ 'frameFill',
264
+ 'frameText',
265
+ 'noteFill',
266
+ 'noteText',
267
+ 'highlightSrgb',
268
+ 'highlightP3',
269
+ ]
270
+
271
+ variants.forEach((variant) => {
272
+ const lightResult = getColorValue(lightTheme, color, variant)
273
+ const darkResult = getColorValue(darkTheme, color, variant)
274
+
275
+ expect(lightResult).toBe(lightTheme[color][variant])
276
+ expect(darkResult).toBe(darkTheme[color][variant])
277
+ })
278
+ })
279
+
280
+ it('should maintain consistency with isDefaultThemeColor', () => {
281
+ const testColors = [...defaultColorNames, '#ff0000', 'custom', 'invalid']
282
+
283
+ testColors.forEach((color) => {
284
+ const isDefault = isDefaultThemeColor(color as TLDefaultColorStyle)
285
+ const result = getColorValue(lightTheme, color as TLDefaultColorStyle, 'solid')
286
+
287
+ if (isDefault) {
288
+ // Should return the theme color value
289
+ expect(result).toBe(
290
+ (lightTheme[color as keyof typeof lightTheme] as TLDefaultColorThemeColor).solid
291
+ )
292
+ } else {
293
+ // Should pass through unchanged
294
+ expect(result).toBe(color)
295
+ }
296
+ })
297
+ })
298
+ })
299
+
300
+ describe('type safety and integration', () => {
301
+ it('should have proper TypeScript types', () => {
302
+ // Test that TLDefaultColorStyle is properly typed
303
+ const validColor: TLDefaultColorStyle = 'red'
304
+ expect(DefaultColorStyle.validate(validColor)).toBe('red')
305
+ })
306
+
307
+ it('should work with both color style props', () => {
308
+ const colorValue: TLDefaultColorStyle = 'blue'
309
+ const labelColorValue: TLDefaultColorStyle = 'white'
310
+
311
+ expect(DefaultColorStyle.validate(colorValue)).toBe('blue')
312
+ expect(DefaultLabelColorStyle.validate(labelColorValue)).toBe('white')
313
+ })
314
+
315
+ it('should integrate properly with theme system', () => {
316
+ const theme = getDefaultColorTheme({ isDarkMode: false })
317
+ const color: TLDefaultColorStyle = 'red'
318
+
319
+ if (isDefaultThemeColor(color)) {
320
+ const colorValue = getColorValue(theme, color, 'solid')
321
+ expect(typeof colorValue).toBe('string')
322
+ expect(colorValue).toBe(theme.red.solid)
323
+ }
324
+ })
325
+
326
+ it('should maintain consistent behavior across all exports', () => {
327
+ // Test that all parts work together correctly
328
+ const theme = getDefaultColorTheme({ isDarkMode: false })
329
+
330
+ defaultColorNames.forEach((colorName) => {
331
+ // Should validate in both style props
332
+ expect(DefaultColorStyle.validate(colorName)).toBe(colorName)
333
+ expect(DefaultLabelColorStyle.validate(colorName)).toBe(colorName)
334
+
335
+ // Should be recognized as default theme color
336
+ expect(isDefaultThemeColor(colorName)).toBe(true)
337
+
338
+ // Should resolve to theme color value
339
+ const colorValue = getColorValue(theme, colorName, 'solid')
340
+ expect(colorValue).toBe(theme[colorName].solid)
341
+ })
342
+ })
343
+ })
344
+
345
+ describe('error handling and edge cases', () => {
346
+ it('should handle malformed theme objects gracefully', () => {
347
+ // Create a partial theme for testing edge cases
348
+ const partialTheme = {
349
+ ...DefaultColorThemePalette.lightMode,
350
+ red: {
351
+ ...DefaultColorThemePalette.lightMode.red,
352
+ solid: undefined,
353
+ } as any,
354
+ }
355
+
356
+ // getColorValue should still work but return undefined for this case
357
+ expect(getColorValue(partialTheme, 'red', 'solid')).toBe(undefined)
358
+ })
359
+
360
+ it('should handle very large color names', () => {
361
+ const longColorName = 'a'.repeat(1000)
362
+ expect(isDefaultThemeColor(longColorName as TLDefaultColorStyle)).toBe(false)
363
+ expect(
364
+ getColorValue(
365
+ DefaultColorThemePalette.lightMode,
366
+ longColorName as TLDefaultColorStyle,
367
+ 'solid'
368
+ )
369
+ ).toBe(longColorName)
370
+ })
371
+
372
+ it('should handle special characters in color values', () => {
373
+ const specialColors = ['color-with-dashes', 'color_with_underscores', 'color.with.dots']
374
+
375
+ specialColors.forEach((color) => {
376
+ expect(isDefaultThemeColor(color as TLDefaultColorStyle)).toBe(false)
377
+ expect(
378
+ getColorValue(DefaultColorThemePalette.lightMode, color as TLDefaultColorStyle, 'solid')
379
+ ).toBe(color)
380
+ })
381
+ })
382
+
383
+ test('should maintain immutability of exported objects', () => {
384
+ const originalLightTheme = DefaultColorThemePalette.lightMode
385
+ const originalDarkTheme = DefaultColorThemePalette.darkMode
386
+ const originalColorNames = defaultColorNames
387
+
388
+ // These should be the same references
389
+ expect(getDefaultColorTheme({ isDarkMode: false })).toBe(originalLightTheme)
390
+ expect(getDefaultColorTheme({ isDarkMode: true })).toBe(originalDarkTheme)
391
+
392
+ // Arrays should maintain their values
393
+ expect(defaultColorNames).toEqual(originalColorNames)
394
+ expect(DefaultColorStyle.values).toEqual(originalColorNames)
395
+ })
396
+ })
397
+
398
+ describe('performance considerations', () => {
399
+ it('should validate colors efficiently', () => {
400
+ const start = performance.now()
401
+
402
+ for (let i = 0; i < 1000; i++) {
403
+ defaultColorNames.forEach((color) => {
404
+ DefaultColorStyle.validate(color)
405
+ isDefaultThemeColor(color)
406
+ })
407
+ }
408
+
409
+ const end = performance.now()
410
+ expect(end - start).toBeLessThan(100) // Should be fast
411
+ })
412
+
413
+ it('should resolve color values efficiently', () => {
414
+ const theme = DefaultColorThemePalette.lightMode
415
+ const start = performance.now()
416
+
417
+ for (let i = 0; i < 1000; i++) {
418
+ defaultColorNames.forEach((color) => {
419
+ getColorValue(theme, color, 'solid')
420
+ getColorValue(theme, color, 'fill')
421
+ })
422
+ }
423
+
424
+ const end = performance.now()
425
+ expect(end - start).toBeLessThan(50) // Should be very fast
426
+ })
427
+
428
+ it('should handle theme switching efficiently', () => {
429
+ const start = performance.now()
430
+
431
+ for (let i = 0; i < 1000; i++) {
432
+ getDefaultColorTheme({ isDarkMode: i % 2 === 0 })
433
+ }
434
+
435
+ const end = performance.now()
436
+ expect(end - start).toBeLessThan(10) // Should be extremely fast (just returns references)
437
+ })
438
+ })
439
+ })
@@ -1,224 +0,0 @@
1
- "use strict";
2
- var __defProp = Object.defineProperty;
3
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
- var __getOwnPropNames = Object.getOwnPropertyNames;
5
- var __hasOwnProp = Object.prototype.hasOwnProperty;
6
- var __export = (target, all) => {
7
- for (var name in all)
8
- __defProp(target, name, { get: all[name], enumerable: true });
9
- };
10
- var __copyProps = (to, from, except, desc) => {
11
- if (from && typeof from === "object" || typeof from === "function") {
12
- for (let key of __getOwnPropNames(from))
13
- if (!__hasOwnProp.call(to, key) && key !== except)
14
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
- }
16
- return to;
17
- };
18
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
- var b64Vecs_exports = {};
20
- __export(b64Vecs_exports, {
21
- b64Vecs: () => b64Vecs
22
- });
23
- module.exports = __toCommonJS(b64Vecs_exports);
24
- const POINT_B64_LENGTH = 8;
25
- const BASE64_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
26
- const B64_LOOKUP = new Uint8Array(128);
27
- for (let i = 0; i < 64; i++) {
28
- B64_LOOKUP[BASE64_CHARS.charCodeAt(i)] = i;
29
- }
30
- const POW2 = new Float64Array(31);
31
- for (let i = 0; i < 31; i++) {
32
- POW2[i] = Math.pow(2, i - 15);
33
- }
34
- const POW2_SUBNORMAL = Math.pow(2, -14) / 1024;
35
- const MANTISSA = new Float64Array(1024);
36
- for (let i = 0; i < 1024; i++) {
37
- MANTISSA[i] = 1 + i / 1024;
38
- }
39
- function uint16ArrayToBase64(uint16Array) {
40
- const uint8Array = new Uint8Array(
41
- uint16Array.buffer,
42
- uint16Array.byteOffset,
43
- uint16Array.byteLength
44
- );
45
- let result = "";
46
- for (let i = 0; i < uint8Array.length; i += 3) {
47
- const byte1 = uint8Array[i];
48
- const byte2 = uint8Array[i + 1];
49
- const byte3 = uint8Array[i + 2];
50
- const bitmap = byte1 << 16 | byte2 << 8 | byte3;
51
- result += BASE64_CHARS[bitmap >> 18 & 63] + BASE64_CHARS[bitmap >> 12 & 63] + BASE64_CHARS[bitmap >> 6 & 63] + BASE64_CHARS[bitmap & 63];
52
- }
53
- return result;
54
- }
55
- function base64ToUint16Array(base64) {
56
- const numBytes = Math.floor(base64.length * 3 / 4);
57
- const bytes = new Uint8Array(numBytes);
58
- let byteIndex = 0;
59
- for (let i = 0; i < base64.length; i += 4) {
60
- const c0 = B64_LOOKUP[base64.charCodeAt(i)];
61
- const c1 = B64_LOOKUP[base64.charCodeAt(i + 1)];
62
- const c2 = B64_LOOKUP[base64.charCodeAt(i + 2)];
63
- const c3 = B64_LOOKUP[base64.charCodeAt(i + 3)];
64
- const bitmap = c0 << 18 | c1 << 12 | c2 << 6 | c3;
65
- bytes[byteIndex++] = bitmap >> 16 & 255;
66
- bytes[byteIndex++] = bitmap >> 8 & 255;
67
- bytes[byteIndex++] = bitmap & 255;
68
- }
69
- return new Uint16Array(bytes.buffer, bytes.byteOffset, bytes.byteLength / 2);
70
- }
71
- function float16BitsToNumber(bits) {
72
- const sign = bits >> 15;
73
- const exp = bits >> 10 & 31;
74
- const frac = bits & 1023;
75
- if (exp === 0) {
76
- return sign ? -frac * POW2_SUBNORMAL : frac * POW2_SUBNORMAL;
77
- }
78
- if (exp === 31) {
79
- return frac ? NaN : sign ? -Infinity : Infinity;
80
- }
81
- const magnitude = POW2[exp] * MANTISSA[frac];
82
- return sign ? -magnitude : magnitude;
83
- }
84
- function numberToFloat16Bits(value) {
85
- if (value === 0) return Object.is(value, -0) ? 32768 : 0;
86
- if (!Number.isFinite(value)) {
87
- if (Number.isNaN(value)) return 32256;
88
- return value > 0 ? 31744 : 64512;
89
- }
90
- const sign = value < 0 ? 1 : 0;
91
- value = Math.abs(value);
92
- const exp = Math.floor(Math.log2(value));
93
- let expBiased = exp + 15;
94
- if (expBiased >= 31) {
95
- return sign << 15 | 31744;
96
- }
97
- if (expBiased <= 0) {
98
- const frac2 = Math.round(value * Math.pow(2, 14) * 1024);
99
- return sign << 15 | frac2 & 1023;
100
- }
101
- const mantissa = value / Math.pow(2, exp) - 1;
102
- let frac = Math.round(mantissa * 1024);
103
- if (frac >= 1024) {
104
- frac = 0;
105
- expBiased++;
106
- if (expBiased >= 31) {
107
- return sign << 15 | 31744;
108
- }
109
- }
110
- return sign << 15 | expBiased << 10 | frac;
111
- }
112
- class b64Vecs {
113
- /**
114
- * Encode a single point (x, y, z) to 8 base64 characters.
115
- * Each coordinate is encoded as a Float16 value, resulting in 6 bytes total.
116
- *
117
- * @param x - The x coordinate
118
- * @param y - The y coordinate
119
- * @param z - The z coordinate
120
- * @returns An 8-character base64 string representing the point
121
- */
122
- static encodePoint(x, y, z) {
123
- const xBits = numberToFloat16Bits(x);
124
- const yBits = numberToFloat16Bits(y);
125
- const zBits = numberToFloat16Bits(z);
126
- const b0 = xBits & 255;
127
- const b1 = xBits >> 8 & 255;
128
- const b2 = yBits & 255;
129
- const b3 = yBits >> 8 & 255;
130
- const b4 = zBits & 255;
131
- const b5 = zBits >> 8 & 255;
132
- const bitmap1 = b0 << 16 | b1 << 8 | b2;
133
- const bitmap2 = b3 << 16 | b4 << 8 | b5;
134
- return BASE64_CHARS[bitmap1 >> 18 & 63] + BASE64_CHARS[bitmap1 >> 12 & 63] + BASE64_CHARS[bitmap1 >> 6 & 63] + BASE64_CHARS[bitmap1 & 63] + BASE64_CHARS[bitmap2 >> 18 & 63] + BASE64_CHARS[bitmap2 >> 12 & 63] + BASE64_CHARS[bitmap2 >> 6 & 63] + BASE64_CHARS[bitmap2 & 63];
135
- }
136
- /**
137
- * Convert an array of VecModels to a base64 string for compact storage.
138
- * Uses Float16 encoding for each coordinate (x, y, z). If a point's z value is
139
- * undefined, it defaults to 0.5.
140
- *
141
- * @param points - An array of VecModel objects to encode
142
- * @returns A base64-encoded string containing all points
143
- */
144
- static encodePoints(points) {
145
- const uint16s = new Uint16Array(points.length * 3);
146
- for (let i = 0; i < points.length; i++) {
147
- const p = points[i];
148
- uint16s[i * 3] = numberToFloat16Bits(p.x);
149
- uint16s[i * 3 + 1] = numberToFloat16Bits(p.y);
150
- uint16s[i * 3 + 2] = numberToFloat16Bits(p.z ?? 0.5);
151
- }
152
- return uint16ArrayToBase64(uint16s);
153
- }
154
- /**
155
- * Convert a base64 string back to an array of VecModels.
156
- * Decodes Float16-encoded coordinates (x, y, z) from the base64 string.
157
- *
158
- * @param base64 - The base64-encoded string containing point data
159
- * @returns An array of VecModel objects decoded from the string
160
- */
161
- static decodePoints(base64) {
162
- const uint16s = base64ToUint16Array(base64);
163
- const result = [];
164
- for (let i = 0; i < uint16s.length; i += 3) {
165
- result.push({
166
- x: float16BitsToNumber(uint16s[i]),
167
- y: float16BitsToNumber(uint16s[i + 1]),
168
- z: float16BitsToNumber(uint16s[i + 2])
169
- });
170
- }
171
- return result;
172
- }
173
- /**
174
- * Decode a single point (8 base64 chars) starting at the given offset.
175
- * Each point is encoded as 3 Float16 values (x, y, z) in 8 base64 characters.
176
- *
177
- * @param b64Points - The base64-encoded string containing point data
178
- * @param charOffset - The character offset where the point starts (must be a multiple of 8)
179
- * @returns A VecModel object with x, y, and z coordinates
180
- * @internal
181
- */
182
- static decodePointAt(b64Points, charOffset) {
183
- const c0 = B64_LOOKUP[b64Points.charCodeAt(charOffset)];
184
- const c1 = B64_LOOKUP[b64Points.charCodeAt(charOffset + 1)];
185
- const c2 = B64_LOOKUP[b64Points.charCodeAt(charOffset + 2)];
186
- const c3 = B64_LOOKUP[b64Points.charCodeAt(charOffset + 3)];
187
- const c4 = B64_LOOKUP[b64Points.charCodeAt(charOffset + 4)];
188
- const c5 = B64_LOOKUP[b64Points.charCodeAt(charOffset + 5)];
189
- const c6 = B64_LOOKUP[b64Points.charCodeAt(charOffset + 6)];
190
- const c7 = B64_LOOKUP[b64Points.charCodeAt(charOffset + 7)];
191
- const bitmap1 = c0 << 18 | c1 << 12 | c2 << 6 | c3;
192
- const bitmap2 = c4 << 18 | c5 << 12 | c6 << 6 | c7;
193
- const xBits = bitmap1 >> 16 & 255 | bitmap1 & 65280;
194
- const yBits = bitmap1 & 255 | bitmap2 >> 8 & 65280;
195
- const zBits = bitmap2 >> 8 & 255 | bitmap2 << 8 & 65280;
196
- return {
197
- x: float16BitsToNumber(xBits),
198
- y: float16BitsToNumber(yBits),
199
- z: float16BitsToNumber(zBits)
200
- };
201
- }
202
- /**
203
- * Get the first point from a base64-encoded string of points.
204
- *
205
- * @param b64Points - The base64-encoded string containing point data
206
- * @returns The first point as a VecModel, or null if the string is too short
207
- * @public
208
- */
209
- static decodeFirstPoint(b64Points) {
210
- if (b64Points.length < POINT_B64_LENGTH) return null;
211
- return b64Vecs.decodePointAt(b64Points, 0);
212
- }
213
- /**
214
- * Get the last point from a base64-encoded string of points.
215
- *
216
- * @param b64Points - The base64-encoded string containing point data
217
- * @returns The last point as a VecModel, or null if the string is too short
218
- */
219
- static decodeLastPoint(b64Points) {
220
- if (b64Points.length < POINT_B64_LENGTH) return null;
221
- return b64Vecs.decodePointAt(b64Points, b64Points.length - POINT_B64_LENGTH);
222
- }
223
- }
224
- //# sourceMappingURL=b64Vecs.js.map