@tldraw/editor 3.14.0-canary.f6a0206007b3 → 3.14.0-canary.f907ed7d9ee5

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 (148) hide show
  1. package/dist-cjs/index.d.ts +17 -26
  2. package/dist-cjs/index.js +8 -10
  3. package/dist-cjs/index.js.map +2 -2
  4. package/dist-cjs/lib/config/TLSessionStateSnapshot.js +1 -12
  5. package/dist-cjs/lib/config/TLSessionStateSnapshot.js.map +3 -3
  6. package/dist-cjs/lib/editor/Editor.js +50 -76
  7. package/dist-cjs/lib/editor/Editor.js.map +2 -2
  8. package/dist-cjs/lib/editor/bindings/BindingUtil.js.map +2 -2
  9. package/dist-cjs/lib/editor/derivations/bindingsIndex.js +22 -22
  10. package/dist-cjs/lib/editor/derivations/bindingsIndex.js.map +2 -2
  11. package/dist-cjs/lib/editor/derivations/parentsToChildren.js +16 -16
  12. package/dist-cjs/lib/editor/derivations/parentsToChildren.js.map +2 -2
  13. package/dist-cjs/lib/editor/managers/{ClickManager.js → ClickManager/ClickManager.js} +1 -1
  14. package/dist-cjs/lib/editor/managers/ClickManager/ClickManager.js.map +7 -0
  15. package/dist-cjs/lib/editor/managers/{EdgeScrollManager.js → EdgeScrollManager/EdgeScrollManager.js} +2 -2
  16. package/dist-cjs/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.js.map +7 -0
  17. package/dist-cjs/lib/editor/managers/FocusManager/FocusManager.js.map +7 -0
  18. package/dist-cjs/lib/editor/managers/{FontManager.js → FontManager/FontManager.js} +4 -1
  19. package/dist-cjs/lib/editor/managers/FontManager/FontManager.js.map +7 -0
  20. package/dist-cjs/lib/editor/managers/{HistoryManager.js → HistoryManager/HistoryManager.js} +64 -6
  21. package/dist-cjs/lib/editor/managers/HistoryManager/HistoryManager.js.map +7 -0
  22. package/dist-cjs/lib/editor/managers/{ScribbleManager.js → ScribbleManager/ScribbleManager.js} +1 -1
  23. package/dist-cjs/lib/editor/managers/ScribbleManager/ScribbleManager.js.map +7 -0
  24. package/dist-cjs/lib/editor/managers/TextManager/TextManager.js.map +7 -0
  25. package/dist-cjs/lib/editor/managers/{TickManager.js → TickManager/TickManager.js} +1 -1
  26. package/dist-cjs/lib/editor/managers/TickManager/TickManager.js.map +7 -0
  27. package/dist-cjs/lib/editor/managers/{UserPreferencesManager.js → UserPreferencesManager/UserPreferencesManager.js} +1 -1
  28. package/dist-cjs/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.js.map +7 -0
  29. package/dist-cjs/lib/editor/shapes/ShapeUtil.js.map +1 -1
  30. package/dist-cjs/lib/editor/shapes/group/GroupShapeUtil.js +1 -1
  31. package/dist-cjs/lib/editor/shapes/group/GroupShapeUtil.js.map +1 -1
  32. package/dist-cjs/lib/exports/getSvgJsx.js.map +1 -1
  33. package/dist-cjs/lib/primitives/Box.js +33 -39
  34. package/dist-cjs/lib/primitives/Box.js.map +2 -2
  35. package/dist-cjs/lib/utils/areShapesContentEqual.js +1 -1
  36. package/dist-cjs/lib/utils/areShapesContentEqual.js.map +2 -2
  37. package/dist-cjs/lib/utils/reorderShapes.js +11 -10
  38. package/dist-cjs/lib/utils/reorderShapes.js.map +2 -2
  39. package/dist-cjs/lib/utils/richText.js +7 -2
  40. package/dist-cjs/lib/utils/richText.js.map +2 -2
  41. package/dist-cjs/version.js +3 -3
  42. package/dist-cjs/version.js.map +1 -1
  43. package/dist-esm/index.d.mts +17 -26
  44. package/dist-esm/index.mjs +12 -10
  45. package/dist-esm/index.mjs.map +2 -2
  46. package/dist-esm/lib/config/TLSessionStateSnapshot.mjs +1 -1
  47. package/dist-esm/lib/config/TLSessionStateSnapshot.mjs.map +2 -2
  48. package/dist-esm/lib/editor/Editor.mjs +50 -76
  49. package/dist-esm/lib/editor/Editor.mjs.map +2 -2
  50. package/dist-esm/lib/editor/bindings/BindingUtil.mjs.map +2 -2
  51. package/dist-esm/lib/editor/derivations/bindingsIndex.mjs +22 -22
  52. package/dist-esm/lib/editor/derivations/bindingsIndex.mjs.map +2 -2
  53. package/dist-esm/lib/editor/derivations/parentsToChildren.mjs +16 -16
  54. package/dist-esm/lib/editor/derivations/parentsToChildren.mjs.map +2 -2
  55. package/dist-esm/lib/editor/managers/{ClickManager.mjs → ClickManager/ClickManager.mjs} +1 -1
  56. package/dist-esm/lib/editor/managers/ClickManager/ClickManager.mjs.map +7 -0
  57. package/dist-esm/lib/editor/managers/{EdgeScrollManager.mjs → EdgeScrollManager/EdgeScrollManager.mjs} +2 -2
  58. package/dist-esm/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.mjs.map +7 -0
  59. package/dist-esm/lib/editor/managers/FocusManager/FocusManager.mjs.map +7 -0
  60. package/dist-esm/lib/editor/managers/{FontManager.mjs → FontManager/FontManager.mjs} +4 -1
  61. package/dist-esm/lib/editor/managers/FontManager/FontManager.mjs.map +7 -0
  62. package/dist-esm/lib/editor/managers/{HistoryManager.mjs → HistoryManager/HistoryManager.mjs} +60 -2
  63. package/dist-esm/lib/editor/managers/HistoryManager/HistoryManager.mjs.map +7 -0
  64. package/dist-esm/lib/editor/managers/{ScribbleManager.mjs → ScribbleManager/ScribbleManager.mjs} +1 -1
  65. package/dist-esm/lib/editor/managers/ScribbleManager/ScribbleManager.mjs.map +7 -0
  66. package/dist-esm/lib/editor/managers/TextManager/TextManager.mjs.map +7 -0
  67. package/dist-esm/lib/editor/managers/{TickManager.mjs → TickManager/TickManager.mjs} +1 -1
  68. package/dist-esm/lib/editor/managers/TickManager/TickManager.mjs.map +7 -0
  69. package/dist-esm/lib/editor/managers/{UserPreferencesManager.mjs → UserPreferencesManager/UserPreferencesManager.mjs} +1 -1
  70. package/dist-esm/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.mjs.map +7 -0
  71. package/dist-esm/lib/editor/shapes/ShapeUtil.mjs.map +1 -1
  72. package/dist-esm/lib/editor/shapes/group/GroupShapeUtil.mjs +1 -1
  73. package/dist-esm/lib/editor/shapes/group/GroupShapeUtil.mjs.map +1 -1
  74. package/dist-esm/lib/exports/getSvgJsx.mjs.map +1 -1
  75. package/dist-esm/lib/primitives/Box.mjs +33 -39
  76. package/dist-esm/lib/primitives/Box.mjs.map +2 -2
  77. package/dist-esm/lib/utils/areShapesContentEqual.mjs +1 -1
  78. package/dist-esm/lib/utils/areShapesContentEqual.mjs.map +2 -2
  79. package/dist-esm/lib/utils/reorderShapes.mjs +11 -10
  80. package/dist-esm/lib/utils/reorderShapes.mjs.map +2 -2
  81. package/dist-esm/lib/utils/richText.mjs +8 -3
  82. package/dist-esm/lib/utils/richText.mjs.map +2 -2
  83. package/dist-esm/version.mjs +3 -3
  84. package/dist-esm/version.mjs.map +1 -1
  85. package/package.json +8 -9
  86. package/src/index.ts +13 -8
  87. package/src/lib/config/TLSessionStateSnapshot.ts +1 -1
  88. package/src/lib/editor/Editor.test.ts +252 -3
  89. package/src/lib/editor/Editor.ts +48 -75
  90. package/src/lib/editor/bindings/BindingUtil.ts +6 -0
  91. package/src/lib/editor/derivations/bindingsIndex.ts +27 -26
  92. package/src/lib/editor/derivations/parentsToChildren.ts +28 -25
  93. package/src/lib/editor/managers/ClickManager/ClickManager.test.ts +442 -0
  94. package/src/lib/editor/managers/{ClickManager.ts → ClickManager/ClickManager.ts} +3 -3
  95. package/src/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.test.ts +374 -0
  96. package/src/lib/editor/managers/{EdgeScrollManager.ts → EdgeScrollManager/EdgeScrollManager.ts} +3 -3
  97. package/src/lib/editor/managers/FocusManager/FocusManager.test.ts +455 -0
  98. package/src/lib/editor/managers/{FocusManager.ts → FocusManager/FocusManager.ts} +1 -1
  99. package/src/lib/editor/managers/FontManager/FontManager.test.ts +263 -0
  100. package/src/lib/editor/managers/{FontManager.ts → FontManager/FontManager.ts} +5 -2
  101. package/src/lib/editor/managers/{HistoryManager.test.ts → HistoryManager/HistoryManager.test.ts} +388 -1
  102. package/src/lib/editor/managers/{HistoryManager.ts → HistoryManager/HistoryManager.ts} +73 -2
  103. package/src/lib/editor/managers/ScribbleManager/ScribbleManager.test.ts +624 -0
  104. package/src/lib/editor/managers/{ScribbleManager.ts → ScribbleManager/ScribbleManager.ts} +2 -2
  105. package/src/lib/editor/managers/SnapManager/SnapManager.test.ts +485 -0
  106. package/src/lib/editor/managers/TextManager/TextManager.test.ts +411 -0
  107. package/src/lib/editor/managers/{TextManager.ts → TextManager/TextManager.ts} +1 -1
  108. package/src/lib/editor/managers/TickManager/TickManager.test.ts +314 -0
  109. package/src/lib/editor/managers/{TickManager.ts → TickManager/TickManager.ts} +2 -2
  110. package/src/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.test.ts +591 -0
  111. package/src/lib/editor/managers/{UserPreferencesManager.ts → UserPreferencesManager/UserPreferencesManager.ts} +2 -2
  112. package/src/lib/editor/shapes/ShapeUtil.ts +1 -1
  113. package/src/lib/editor/shapes/group/GroupShapeUtil.tsx +1 -1
  114. package/src/lib/exports/getSvgJsx.tsx +1 -1
  115. package/src/lib/primitives/Box.test.ts +588 -7
  116. package/src/lib/primitives/Box.ts +33 -41
  117. package/src/lib/utils/areShapesContentEqual.ts +1 -2
  118. package/src/lib/utils/reorderShapes.ts +10 -13
  119. package/src/lib/utils/richText.ts +10 -4
  120. package/src/version.ts +3 -3
  121. package/dist-cjs/lib/editor/managers/ClickManager.js.map +0 -7
  122. package/dist-cjs/lib/editor/managers/EdgeScrollManager.js.map +0 -7
  123. package/dist-cjs/lib/editor/managers/FocusManager.js.map +0 -7
  124. package/dist-cjs/lib/editor/managers/FontManager.js.map +0 -7
  125. package/dist-cjs/lib/editor/managers/HistoryManager.js.map +0 -7
  126. package/dist-cjs/lib/editor/managers/ScribbleManager.js.map +0 -7
  127. package/dist-cjs/lib/editor/managers/Stack.js +0 -82
  128. package/dist-cjs/lib/editor/managers/Stack.js.map +0 -7
  129. package/dist-cjs/lib/editor/managers/TextManager.js.map +0 -7
  130. package/dist-cjs/lib/editor/managers/TickManager.js.map +0 -7
  131. package/dist-cjs/lib/editor/managers/UserPreferencesManager.js.map +0 -7
  132. package/dist-esm/lib/editor/managers/ClickManager.mjs.map +0 -7
  133. package/dist-esm/lib/editor/managers/EdgeScrollManager.mjs.map +0 -7
  134. package/dist-esm/lib/editor/managers/FocusManager.mjs.map +0 -7
  135. package/dist-esm/lib/editor/managers/FontManager.mjs.map +0 -7
  136. package/dist-esm/lib/editor/managers/HistoryManager.mjs.map +0 -7
  137. package/dist-esm/lib/editor/managers/ScribbleManager.mjs.map +0 -7
  138. package/dist-esm/lib/editor/managers/Stack.mjs +0 -62
  139. package/dist-esm/lib/editor/managers/Stack.mjs.map +0 -7
  140. package/dist-esm/lib/editor/managers/TextManager.mjs.map +0 -7
  141. package/dist-esm/lib/editor/managers/TickManager.mjs.map +0 -7
  142. package/dist-esm/lib/editor/managers/UserPreferencesManager.mjs.map +0 -7
  143. package/src/lib/editor/managers/ScribbleManager.test.ts +0 -32
  144. package/src/lib/editor/managers/Stack.ts +0 -71
  145. /package/dist-cjs/lib/editor/managers/{FocusManager.js → FocusManager/FocusManager.js} +0 -0
  146. /package/dist-cjs/lib/editor/managers/{TextManager.js → TextManager/TextManager.js} +0 -0
  147. /package/dist-esm/lib/editor/managers/{FocusManager.mjs → FocusManager/FocusManager.mjs} +0 -0
  148. /package/dist-esm/lib/editor/managers/{TextManager.mjs → TextManager/TextManager.mjs} +0 -0
@@ -0,0 +1,411 @@
1
+ import { Editor } from '../../Editor'
2
+ import { TextManager, TLMeasureTextSpanOpts } from './TextManager'
3
+
4
+ // Create a simple mock DOM environment
5
+ const mockElement = {
6
+ classList: { add: jest.fn() },
7
+ tabIndex: -1,
8
+ cloneNode: jest.fn(),
9
+ innerHTML: '',
10
+ textContent: '',
11
+ setAttribute: jest.fn(),
12
+ style: { setProperty: jest.fn() },
13
+ scrollWidth: 100,
14
+ getBoundingClientRect: jest.fn(() => ({
15
+ width: 100,
16
+ height: 20,
17
+ left: 0,
18
+ top: 0,
19
+ right: 100,
20
+ bottom: 20,
21
+ })),
22
+ remove: jest.fn(),
23
+ insertAdjacentElement: jest.fn(),
24
+ childNodes: [],
25
+ }
26
+
27
+ // Mock document.createElement to return our mock element
28
+ const mockCreateElement = jest.fn(() => {
29
+ const element = { ...mockElement }
30
+ element.cloneNode = jest.fn(() => ({ ...element }))
31
+ return element
32
+ })
33
+
34
+ // Mock editor
35
+ const mockEditor = {
36
+ getContainer: jest.fn(() => ({
37
+ appendChild: jest.fn(),
38
+ })),
39
+ } as unknown as Editor
40
+
41
+ // Setup global mocks
42
+ global.document = {
43
+ createElement: mockCreateElement,
44
+ } as any
45
+
46
+ global.Range = jest.fn(() => ({
47
+ setStart: jest.fn(),
48
+ setEnd: jest.fn(),
49
+ getClientRects: jest.fn(() => [
50
+ {
51
+ width: 10,
52
+ height: 16,
53
+ left: 0,
54
+ top: 0,
55
+ right: 10,
56
+ bottom: 16,
57
+ },
58
+ ]),
59
+ })) as any
60
+
61
+ describe('TextManager', () => {
62
+ let textManager: TextManager
63
+
64
+ beforeEach(() => {
65
+ jest.clearAllMocks()
66
+ textManager = new TextManager(mockEditor)
67
+ })
68
+
69
+ describe('constructor', () => {
70
+ it('should create a TextManager instance', () => {
71
+ expect(textManager).toBeInstanceOf(TextManager)
72
+ expect(textManager.editor).toBe(mockEditor)
73
+ })
74
+ })
75
+
76
+ describe('measureText', () => {
77
+ const defaultOpts = {
78
+ fontStyle: 'normal',
79
+ fontWeight: '400',
80
+ fontFamily: 'Arial',
81
+ fontSize: 16,
82
+ lineHeight: 1.2,
83
+ maxWidth: 200,
84
+ minWidth: null,
85
+ padding: '0px',
86
+ }
87
+
88
+ it('should call measureHtml with normalized text', () => {
89
+ const spy = jest.spyOn(textManager, 'measureHtml')
90
+ textManager.measureText('Hello World', defaultOpts)
91
+ expect(spy).toHaveBeenCalledWith('Hello World', defaultOpts)
92
+ })
93
+
94
+ it('should normalize line breaks', () => {
95
+ const spy = jest.spyOn(textManager, 'measureHtml')
96
+ textManager.measureText('Hello\nWorld\r\nTest', defaultOpts)
97
+ // The text should be normalized to use consistent line breaks
98
+ expect(spy).toHaveBeenCalled()
99
+ })
100
+
101
+ it('should handle empty text', () => {
102
+ const result = textManager.measureText('', defaultOpts)
103
+ expect(result).toHaveProperty('x', 0)
104
+ expect(result).toHaveProperty('y', 0)
105
+ expect(result).toHaveProperty('w')
106
+ expect(result).toHaveProperty('h')
107
+ expect(result).toHaveProperty('scrollWidth')
108
+ })
109
+ })
110
+
111
+ describe('measureHtml', () => {
112
+ const defaultOpts = {
113
+ fontStyle: 'normal',
114
+ fontWeight: '400',
115
+ fontFamily: 'Arial',
116
+ fontSize: 16,
117
+ lineHeight: 1.2,
118
+ maxWidth: 200,
119
+ minWidth: null,
120
+ padding: '0px',
121
+ }
122
+
123
+ it('should return measurement object with correct structure', () => {
124
+ const result = textManager.measureHtml('<span>Test</span>', defaultOpts)
125
+
126
+ expect(result).toMatchObject({
127
+ x: 0,
128
+ y: 0,
129
+ w: expect.any(Number),
130
+ h: expect.any(Number),
131
+ scrollWidth: expect.any(Number),
132
+ })
133
+ })
134
+
135
+ it('should handle null maxWidth', () => {
136
+ const opts = { ...defaultOpts, maxWidth: null }
137
+ const result = textManager.measureHtml('Test', opts)
138
+
139
+ expect(result).toMatchObject({
140
+ x: 0,
141
+ y: 0,
142
+ w: expect.any(Number),
143
+ h: expect.any(Number),
144
+ scrollWidth: expect.any(Number),
145
+ })
146
+ })
147
+
148
+ it('should handle overflow wrap breaking', () => {
149
+ const opts = { ...defaultOpts, disableOverflowWrapBreaking: true }
150
+ const result = textManager.measureHtml('Test', opts)
151
+
152
+ expect(result).toMatchObject({
153
+ x: 0,
154
+ y: 0,
155
+ w: expect.any(Number),
156
+ h: expect.any(Number),
157
+ scrollWidth: expect.any(Number),
158
+ })
159
+ })
160
+
161
+ it('should handle other styles', () => {
162
+ const opts = {
163
+ ...defaultOpts,
164
+ otherStyles: {
165
+ 'text-decoration': 'underline',
166
+ color: 'red',
167
+ },
168
+ }
169
+
170
+ const result = textManager.measureHtml('Test', opts)
171
+ expect(result).toMatchObject({
172
+ x: 0,
173
+ y: 0,
174
+ w: expect.any(Number),
175
+ h: expect.any(Number),
176
+ scrollWidth: expect.any(Number),
177
+ })
178
+ })
179
+ })
180
+
181
+ describe('measureElementTextNodeSpans', () => {
182
+ it('should handle elements with text nodes', () => {
183
+ const mockTextNode = {
184
+ nodeType: 3, // TEXT_NODE
185
+ textContent: 'Hello',
186
+ }
187
+
188
+ const mockElementWithText = {
189
+ childNodes: [mockTextNode],
190
+ getBoundingClientRect: () => ({ left: 0, top: 0 }),
191
+ }
192
+
193
+ const result = textManager.measureElementTextNodeSpans(mockElementWithText as any)
194
+
195
+ expect(result).toHaveProperty('spans')
196
+ expect(result).toHaveProperty('didTruncate')
197
+ expect(Array.isArray(result.spans)).toBe(true)
198
+ expect(typeof result.didTruncate).toBe('boolean')
199
+ })
200
+
201
+ it('should handle empty elements', () => {
202
+ const mockEmptyElement = {
203
+ childNodes: [],
204
+ getBoundingClientRect: () => ({ left: 0, top: 0 }),
205
+ }
206
+
207
+ const result = textManager.measureElementTextNodeSpans(mockEmptyElement as any)
208
+
209
+ expect(result.didTruncate).toBe(false)
210
+ expect(result.spans).toHaveLength(0)
211
+ })
212
+
213
+ it('should handle truncation option', () => {
214
+ const mockTextNode = {
215
+ nodeType: 3, // TEXT_NODE
216
+ textContent: 'Hello World',
217
+ }
218
+
219
+ const mockElementWithText = {
220
+ childNodes: [mockTextNode],
221
+ getBoundingClientRect: () => ({ left: 0, top: 0 }),
222
+ }
223
+
224
+ const result = textManager.measureElementTextNodeSpans(mockElementWithText as any, {
225
+ shouldTruncateToFirstLine: true,
226
+ })
227
+
228
+ expect(result).toHaveProperty('spans')
229
+ expect(result).toHaveProperty('didTruncate')
230
+ })
231
+ })
232
+
233
+ describe('measureTextSpans', () => {
234
+ const defaultOpts: TLMeasureTextSpanOpts = {
235
+ overflow: 'wrap',
236
+ width: 200,
237
+ height: 100,
238
+ padding: 10,
239
+ fontSize: 16,
240
+ fontWeight: '400',
241
+ fontFamily: 'Arial',
242
+ fontStyle: 'normal',
243
+ lineHeight: 1.2,
244
+ textAlign: 'start',
245
+ }
246
+
247
+ it('should return empty array for empty text', () => {
248
+ const result = textManager.measureTextSpans('', defaultOpts)
249
+ expect(result).toEqual([])
250
+ })
251
+
252
+ it('should return array of text spans for non-empty text', () => {
253
+ // Mock measureElementTextNodeSpans to return some spans
254
+ jest.spyOn(textManager, 'measureElementTextNodeSpans').mockReturnValue({
255
+ spans: [
256
+ {
257
+ text: 'Hello World',
258
+ box: { x: 0, y: 0, w: 100, h: 16 },
259
+ },
260
+ ],
261
+ didTruncate: false,
262
+ })
263
+
264
+ const result = textManager.measureTextSpans('Hello World', defaultOpts)
265
+
266
+ expect(Array.isArray(result)).toBe(true)
267
+ expect(result.length).toBeGreaterThan(0)
268
+ expect(result[0]).toHaveProperty('text')
269
+ expect(result[0]).toHaveProperty('box')
270
+ })
271
+
272
+ it('should handle wrap overflow', () => {
273
+ jest.spyOn(textManager, 'measureElementTextNodeSpans').mockReturnValue({
274
+ spans: [
275
+ {
276
+ text: 'Hello World',
277
+ box: { x: 0, y: 0, w: 100, h: 16 },
278
+ },
279
+ ],
280
+ didTruncate: false,
281
+ })
282
+
283
+ const opts = { ...defaultOpts, overflow: 'wrap' as const }
284
+ const result = textManager.measureTextSpans('Hello World', opts)
285
+
286
+ expect(Array.isArray(result)).toBe(true)
287
+ })
288
+
289
+ it('should handle truncate-ellipsis overflow', () => {
290
+ // Mock the calls for ellipsis handling
291
+ jest
292
+ .spyOn(textManager, 'measureElementTextNodeSpans')
293
+ .mockReturnValueOnce({
294
+ spans: [
295
+ {
296
+ text: 'Hello Wo',
297
+ box: { x: 0, y: 0, w: 80, h: 16 },
298
+ },
299
+ ],
300
+ didTruncate: true,
301
+ })
302
+ .mockReturnValueOnce({
303
+ spans: [
304
+ {
305
+ text: '…',
306
+ box: { x: 0, y: 0, w: 10, h: 16 },
307
+ },
308
+ ],
309
+ didTruncate: false,
310
+ })
311
+ .mockReturnValueOnce({
312
+ spans: [
313
+ {
314
+ text: 'Hello W',
315
+ box: { x: 0, y: 0, w: 70, h: 16 },
316
+ },
317
+ ],
318
+ didTruncate: false,
319
+ })
320
+
321
+ const opts = { ...defaultOpts, overflow: 'truncate-ellipsis' as const }
322
+ const result = textManager.measureTextSpans('Hello World', opts)
323
+
324
+ expect(Array.isArray(result)).toBe(true)
325
+ })
326
+
327
+ it('should handle truncate-clip overflow', () => {
328
+ jest.spyOn(textManager, 'measureElementTextNodeSpans').mockReturnValue({
329
+ spans: [
330
+ {
331
+ text: 'Hello Wo',
332
+ box: { x: 0, y: 0, w: 80, h: 16 },
333
+ },
334
+ ],
335
+ didTruncate: true,
336
+ })
337
+
338
+ const opts = { ...defaultOpts, overflow: 'truncate-clip' as const }
339
+ const result = textManager.measureTextSpans('Hello World', opts)
340
+
341
+ expect(Array.isArray(result)).toBe(true)
342
+ })
343
+
344
+ it('should handle different text alignments', () => {
345
+ jest.spyOn(textManager, 'measureElementTextNodeSpans').mockReturnValue({
346
+ spans: [
347
+ {
348
+ text: 'Test',
349
+ box: { x: 0, y: 0, w: 40, h: 16 },
350
+ },
351
+ ],
352
+ didTruncate: false,
353
+ })
354
+
355
+ const alignments: Array<TLMeasureTextSpanOpts['textAlign']> = ['start', 'middle', 'end']
356
+
357
+ alignments.forEach((textAlign) => {
358
+ const opts = { ...defaultOpts, textAlign }
359
+ const result = textManager.measureTextSpans('Test', opts)
360
+ expect(Array.isArray(result)).toBe(true)
361
+ })
362
+ })
363
+
364
+ it('should handle custom font properties', () => {
365
+ jest.spyOn(textManager, 'measureElementTextNodeSpans').mockReturnValue({
366
+ spans: [
367
+ {
368
+ text: 'Test',
369
+ box: { x: 0, y: 0, w: 40, h: 16 },
370
+ },
371
+ ],
372
+ didTruncate: false,
373
+ })
374
+
375
+ const opts = {
376
+ ...defaultOpts,
377
+ fontSize: 18,
378
+ fontFamily: 'Times',
379
+ fontWeight: 'bold',
380
+ fontStyle: 'italic',
381
+ lineHeight: 1.5,
382
+ }
383
+
384
+ const result = textManager.measureTextSpans('Test', opts)
385
+ expect(Array.isArray(result)).toBe(true)
386
+ })
387
+
388
+ it('should handle other styles', () => {
389
+ jest.spyOn(textManager, 'measureElementTextNodeSpans').mockReturnValue({
390
+ spans: [
391
+ {
392
+ text: 'Test',
393
+ box: { x: 0, y: 0, w: 40, h: 16 },
394
+ },
395
+ ],
396
+ didTruncate: false,
397
+ })
398
+
399
+ const opts = {
400
+ ...defaultOpts,
401
+ otherStyles: {
402
+ 'text-shadow': '1px 1px 1px black',
403
+ 'letter-spacing': '1px',
404
+ },
405
+ }
406
+
407
+ const result = textManager.measureTextSpans('Test', opts)
408
+ expect(Array.isArray(result)).toBe(true)
409
+ })
410
+ })
411
+ })
@@ -1,5 +1,5 @@
1
1
  import { BoxModel, TLDefaultHorizontalAlignStyle } from '@tldraw/tlschema'
2
- import { Editor } from '../Editor'
2
+ import { Editor } from '../../Editor'
3
3
 
4
4
  const fixNewLines = /\r?\n|\r/g
5
5