@tldraw/editor 3.14.1 → 3.15.0-canary.0183e172b2bf

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.
package/src/index.ts CHANGED
@@ -18,6 +18,27 @@ export * from '@tldraw/utils'
18
18
  // eslint-disable-next-line local/no-export-star
19
19
  export * from '@tldraw/validate'
20
20
 
21
+ export {
22
+ ErrorScreen,
23
+ LoadingScreen,
24
+ TldrawEditor,
25
+ useOnMount,
26
+ type LoadingScreenProps,
27
+ type TLOnMountHandler,
28
+ type TldrawEditorBaseProps,
29
+ type TldrawEditorProps,
30
+ type TldrawEditorStoreProps,
31
+ type TldrawEditorWithStoreProps,
32
+ type TldrawEditorWithoutStoreProps,
33
+ } from './lib/TldrawEditor'
34
+ export {
35
+ ErrorBoundary,
36
+ OptionalErrorBoundary,
37
+ type TLErrorBoundaryProps,
38
+ } from './lib/components/ErrorBoundary'
39
+ export { HTMLContainer, type HTMLContainerProps } from './lib/components/HTMLContainer'
40
+ export { MenuClickCapture } from './lib/components/MenuClickCapture'
41
+ export { SVGContainer, type SVGContainerProps } from './lib/components/SVGContainer'
21
42
  export { DefaultBackground } from './lib/components/default-components/DefaultBackground'
22
43
  export { DefaultBrush, type TLBrushProps } from './lib/components/default-components/DefaultBrush'
23
44
  export {
@@ -73,26 +94,6 @@ export {
73
94
  } from './lib/components/default-components/DefaultSnapIndictor'
74
95
  export { DefaultSpinner } from './lib/components/default-components/DefaultSpinner'
75
96
  export { DefaultSvgDefs } from './lib/components/default-components/DefaultSvgDefs'
76
- export {
77
- ErrorBoundary,
78
- OptionalErrorBoundary,
79
- type TLErrorBoundaryProps,
80
- } from './lib/components/ErrorBoundary'
81
- export { HTMLContainer, type HTMLContainerProps } from './lib/components/HTMLContainer'
82
- export { MenuClickCapture } from './lib/components/MenuClickCapture'
83
- export { SVGContainer, type SVGContainerProps } from './lib/components/SVGContainer'
84
- export {
85
- createTLSchemaFromUtils,
86
- createTLStore,
87
- inlineBase64AssetStore,
88
- type TLStoreBaseOptions,
89
- type TLStoreEventInfo,
90
- type TLStoreOptions,
91
- type TLStoreSchemaOptions,
92
- } from './lib/config/createTLStore'
93
- export { createTLUser, useTldrawUser, type TLUser } from './lib/config/createTLUser'
94
- export { type TLAnyBindingUtilConstructor } from './lib/config/defaultBindings'
95
- export { coreShapes, type TLAnyShapeUtilConstructor } from './lib/config/defaultShapes'
96
97
  export {
97
98
  getSnapshot,
98
99
  loadSnapshot,
@@ -100,23 +101,42 @@ export {
100
101
  type TLLoadSnapshotOptions,
101
102
  } from './lib/config/TLEditorSnapshot'
102
103
  export {
104
+ TAB_ID,
103
105
  createSessionStateSnapshotSignal,
104
106
  extractSessionStateFromLegacySnapshot,
105
107
  loadSessionStateSnapshotIntoStore,
106
- TAB_ID,
107
108
  type TLLoadSessionStateSnapshotOptions,
108
109
  type TLSessionStateSnapshot,
109
110
  } from './lib/config/TLSessionStateSnapshot'
110
111
  export {
112
+ USER_COLORS,
111
113
  defaultUserPreferences,
112
114
  getFreshUserPreferences,
113
115
  getUserPreferences,
114
116
  setUserPreferences,
115
- USER_COLORS,
116
117
  userTypeValidator,
117
118
  type TLUserPreferences,
118
119
  } from './lib/config/TLUserPreferences'
120
+ export {
121
+ createTLSchemaFromUtils,
122
+ createTLStore,
123
+ inlineBase64AssetStore,
124
+ type TLStoreBaseOptions,
125
+ type TLStoreEventInfo,
126
+ type TLStoreOptions,
127
+ type TLStoreSchemaOptions,
128
+ } from './lib/config/createTLStore'
129
+ export { createTLUser, useTldrawUser, type TLUser } from './lib/config/createTLUser'
130
+ export { type TLAnyBindingUtilConstructor } from './lib/config/defaultBindings'
131
+ export { coreShapes, type TLAnyShapeUtilConstructor } from './lib/config/defaultShapes'
119
132
  export { DEFAULT_ANIMATION_OPTIONS, DEFAULT_CAMERA_OPTIONS, SIDES } from './lib/constants'
133
+ export {
134
+ Editor,
135
+ type TLEditorOptions,
136
+ type TLEditorRunOptions,
137
+ type TLRenderingShape,
138
+ type TLResizeShapeOptions,
139
+ } from './lib/editor/Editor'
120
140
  export {
121
141
  BindingUtil,
122
142
  type BindingOnChangeOptions,
@@ -127,13 +147,6 @@ export {
127
147
  type BindingOnShapeIsolateOptions,
128
148
  type TLBindingUtilConstructor,
129
149
  } from './lib/editor/bindings/BindingUtil'
130
- export {
131
- Editor,
132
- type TLEditorOptions,
133
- type TLEditorRunOptions,
134
- type TLRenderingShape,
135
- type TLResizeShapeOptions,
136
- } from './lib/editor/Editor'
137
150
  export { ClickManager, type TLClickState } from './lib/editor/managers/ClickManager/ClickManager'
138
151
  export { EdgeScrollManager } from './lib/editor/managers/EdgeScrollManager/EdgeScrollManager'
139
152
  export {
@@ -166,7 +179,6 @@ export {
166
179
  } from './lib/editor/managers/TextManager/TextManager'
167
180
  export { UserPreferencesManager } from './lib/editor/managers/UserPreferencesManager/UserPreferencesManager'
168
181
  export { BaseBoxShapeUtil, type TLBaseBoxShape } from './lib/editor/shapes/BaseBoxShapeUtil'
169
- export { GroupShapeUtil } from './lib/editor/shapes/group/GroupShapeUtil'
170
182
  export {
171
183
  ShapeUtil,
172
184
  type TLCropInfo,
@@ -183,6 +195,7 @@ export {
183
195
  type TLShapeUtilCanvasSvgDef,
184
196
  type TLShapeUtilConstructor,
185
197
  } from './lib/editor/shapes/ShapeUtil'
198
+ export { GroupShapeUtil } from './lib/editor/shapes/group/GroupShapeUtil'
186
199
  export {
187
200
  getPerfectDashProps,
188
201
  type PerfectDashTerminal,
@@ -192,16 +205,22 @@ export { resizeScaled } from './lib/editor/shapes/shared/resizeScaled'
192
205
  export { BaseBoxShapeTool } from './lib/editor/tools/BaseBoxShapeTool/BaseBoxShapeTool'
193
206
  export { maybeSnapToGrid } from './lib/editor/tools/BaseBoxShapeTool/children/Pointing'
194
207
  export { StateNode, type TLStateNodeConstructor } from './lib/editor/tools/StateNode'
208
+ export {
209
+ useDelaySvgExport,
210
+ useSvgExportContext,
211
+ type SvgExportContext,
212
+ type SvgExportDef,
213
+ } from './lib/editor/types/SvgExportContext'
195
214
  export { type TLContent } from './lib/editor/types/clipboard-types'
196
215
  export { type TLEventMap, type TLEventMapHandler } from './lib/editor/types/emit-types'
197
216
  export {
198
217
  EVENT_NAME_MAP,
199
218
  type TLBaseEventInfo,
219
+ type TLCLickEventName,
200
220
  type TLCancelEvent,
201
221
  type TLCancelEventInfo,
202
222
  type TLClickEvent,
203
223
  type TLClickEventInfo,
204
- type TLCLickEventName,
205
224
  type TLCompleteEvent,
206
225
  type TLCompleteEventInfo,
207
226
  type TLEnterEventHandler,
@@ -270,12 +289,6 @@ export {
270
289
  type TLResizeHandle,
271
290
  type TLSelectionHandle,
272
291
  } from './lib/editor/types/selection-types'
273
- export {
274
- useDelaySvgExport,
275
- useSvgExportContext,
276
- type SvgExportContext,
277
- type SvgExportDef,
278
- } from './lib/editor/types/SvgExportContext'
279
292
  export { getSvgAsImage } from './lib/exports/getSvgAsImage'
280
293
  export { tlenv } from './lib/globals/environment'
281
294
  export { tlmenus } from './lib/globals/menus'
@@ -287,7 +300,13 @@ export {
287
300
  type ContainerProviderProps,
288
301
  } from './lib/hooks/useContainer'
289
302
  export { getCursor } from './lib/hooks/useCursor'
290
- export { EditorContext, useEditor, useMaybeEditor } from './lib/hooks/useEditor'
303
+ export {
304
+ EditorContext,
305
+ EditorProvider,
306
+ useEditor,
307
+ useMaybeEditor,
308
+ type EditorProviderProps,
309
+ } from './lib/hooks/useEditor'
291
310
  export { useEditorComponents } from './lib/hooks/useEditorComponents'
292
311
  export type { TLEditorComponents } from './lib/hooks/useEditorComponents'
293
312
  export { useEvent, useReactiveEvent } from './lib/hooks/useEvent'
@@ -333,6 +352,8 @@ export {
333
352
  type SelectionEdge,
334
353
  type SelectionHandle,
335
354
  } from './lib/primitives/Box'
355
+ export { Mat, type MatLike, type MatModel } from './lib/primitives/Mat'
356
+ export { Vec, type VecLike } from './lib/primitives/Vec'
336
357
  export { EASINGS } from './lib/primitives/easings'
337
358
  export { Arc2d } from './lib/primitives/geometry/Arc2d'
338
359
  export { Circle2d } from './lib/primitives/geometry/Circle2d'
@@ -367,8 +388,11 @@ export {
367
388
  polygonIntersectsPolyline,
368
389
  polygonsIntersect,
369
390
  } from './lib/primitives/intersect'
370
- export { Mat, type MatLike, type MatModel } from './lib/primitives/Mat'
371
391
  export {
392
+ HALF_PI,
393
+ PI,
394
+ PI2,
395
+ SIN,
372
396
  angleDistance,
373
397
  approximately,
374
398
  areAnglesCompatible,
@@ -385,36 +409,23 @@ export {
385
409
  getPointOnCircle,
386
410
  getPointsOnArc,
387
411
  getPolygonVertices,
388
- HALF_PI,
389
412
  isSafeFloat,
390
413
  perimeterOfEllipse,
391
- PI,
392
- PI2,
393
414
  pointInPolygon,
394
415
  precise,
395
416
  radiansToDegrees,
396
417
  rangeIntersection,
397
418
  shortAngleDist,
398
- SIN,
399
419
  snapAngle,
400
420
  toDomPrecision,
401
421
  toFixed,
402
422
  toPrecision,
403
423
  } from './lib/primitives/utils'
404
- export { Vec, type VecLike } from './lib/primitives/Vec'
405
424
  export {
406
- ErrorScreen,
407
- LoadingScreen,
408
- TldrawEditor,
409
- useOnMount,
410
- type LoadingScreenProps,
411
- type TldrawEditorBaseProps,
412
- type TldrawEditorProps,
413
- type TldrawEditorStoreProps,
414
- type TldrawEditorWithoutStoreProps,
415
- type TldrawEditorWithStoreProps,
416
- type TLOnMountHandler,
417
- } from './lib/TldrawEditor'
425
+ ReadonlySharedStyleMap,
426
+ SharedStyleMap,
427
+ type SharedStyle,
428
+ } from './lib/utils/SharedStylesMap'
418
429
  export { dataUrlToFile, getDefaultCdnBaseUrl } from './lib/utils/assets'
419
430
  export { clampToBrowserMaxCanvasSize, type CanvasMaxSize } from './lib/utils/browserCanvasMaxSize'
420
431
  export {
@@ -450,9 +461,9 @@ export {
450
461
  getFontsFromRichText,
451
462
  type RichTextFontVisitor,
452
463
  type RichTextFontVisitorState,
464
+ type TLTextOptions,
453
465
  type TiptapEditor,
454
466
  type TiptapNode,
455
- type TLTextOptions,
456
467
  } from './lib/utils/richText'
457
468
  export {
458
469
  applyRotationToSnapshotShapes,
@@ -460,14 +471,9 @@ export {
460
471
  type TLRotationSnapshot,
461
472
  } from './lib/utils/rotation'
462
473
  export { runtime, setRuntimeOverrides } from './lib/utils/runtime'
463
- export {
464
- ReadonlySharedStyleMap,
465
- SharedStyleMap,
466
- type SharedStyle,
467
- } from './lib/utils/SharedStylesMap'
468
- export { hardReset } from './lib/utils/sync/hardReset'
469
474
  export { LocalIndexedDb, Table, type StoreName } from './lib/utils/sync/LocalIndexedDb'
470
475
  export { type TLStoreWithStatus } from './lib/utils/sync/StoreWithStatus'
476
+ export { hardReset } from './lib/utils/sync/hardReset'
471
477
  export { uniq } from './lib/utils/uniq'
472
478
  export { openWindow } from './lib/utils/window-open'
473
479
 
@@ -7859,7 +7859,7 @@ export class Editor extends EventEmitter<TLEventMap> {
7859
7859
 
7860
7860
  const prevParentId = partial.parentId
7861
7861
 
7862
- // a shape cannot be it's own parent. This was a rare issue with frames/groups in the syncFuzz tests.
7862
+ // a shape cannot be its own parent. This was a rare issue with frames/groups in the syncFuzz tests.
7863
7863
  if (parentId === partial.id) {
7864
7864
  parentId = focusedGroupId
7865
7865
  }
@@ -1,5 +1,4 @@
1
1
  import { BoxModel, TLDefaultHorizontalAlignStyle } from '@tldraw/tlschema'
2
- import { objectMapKeys } from '@tldraw/utils'
3
2
  import { Editor } from '../../Editor'
4
3
 
5
4
  const fixNewLines = /\r?\n|\r/g
@@ -61,18 +60,10 @@ export interface TLMeasureTextSpanOpts {
61
60
 
62
61
  const spaceCharacterRegex = /\s/
63
62
 
64
- const initialDefaultStyles = Object.freeze({
65
- 'overflow-wrap': 'break-word',
66
- 'word-break': 'auto',
67
- width: null,
68
- height: null,
69
- 'max-width': null,
70
- 'min-width': null,
71
- })
72
-
73
63
  /** @public */
74
64
  export class TextManager {
75
65
  private elm: HTMLDivElement
66
+ private defaultStyles: Record<string, string | null>
76
67
 
77
68
  constructor(public editor: Editor) {
78
69
  const elm = document.createElement('div')
@@ -82,34 +73,31 @@ export class TextManager {
82
73
  elm.tabIndex = -1
83
74
  this.editor.getContainer().appendChild(elm)
84
75
 
85
- this.elm = elm
86
-
87
- for (const key of objectMapKeys(initialDefaultStyles)) {
88
- elm.style.setProperty(key, initialDefaultStyles[key])
76
+ // we need to save the default styles so that we can restore them when we're done
77
+ // these must be the css names, not the js names for the styles
78
+ this.defaultStyles = {
79
+ 'overflow-wrap': 'break-word',
80
+ 'word-break': 'auto',
81
+ width: null,
82
+ height: null,
83
+ 'max-width': null,
84
+ 'min-width': null,
89
85
  }
90
- }
91
86
 
92
- private setElementStyles(styles: Record<string, string | undefined>) {
93
- const stylesToReinstate = {} as any
94
- for (const key of objectMapKeys(styles)) {
95
- if (typeof styles[key] === 'string') {
96
- const oldValue = this.elm.style.getPropertyValue(key)
97
- if (oldValue === styles[key]) continue
98
- stylesToReinstate[key] = oldValue
99
- this.elm.style.setProperty(key, styles[key])
100
- }
101
- }
102
- return () => {
103
- for (const key of objectMapKeys(stylesToReinstate)) {
104
- this.elm.style.setProperty(key, stylesToReinstate[key])
105
- }
106
- }
87
+ this.elm = elm
107
88
  }
108
89
 
109
90
  dispose() {
110
91
  return this.elm.remove()
111
92
  }
112
93
 
94
+ private resetElmStyles() {
95
+ const { elm, defaultStyles } = this
96
+ for (const key in defaultStyles) {
97
+ elm.style.setProperty(key, defaultStyles[key])
98
+ }
99
+ }
100
+
113
101
  measureText(textToMeasure: string, opts: TLMeasureTextOpts): BoxModel & { scrollWidth: number } {
114
102
  const div = document.createElement('div')
115
103
  div.textContent = normalizeTextForDom(textToMeasure)
@@ -119,36 +107,54 @@ export class TextManager {
119
107
  measureHtml(html: string, opts: TLMeasureTextOpts): BoxModel & { scrollWidth: number } {
120
108
  const { elm } = this
121
109
 
122
- const newStyles = {
123
- 'font-family': opts.fontFamily,
124
- 'font-style': opts.fontStyle,
125
- 'font-weight': opts.fontWeight,
126
- 'font-size': opts.fontSize + 'px',
127
- 'line-height': opts.lineHeight.toString(),
128
- padding: opts.padding,
129
- 'max-width': opts.maxWidth ? opts.maxWidth + 'px' : undefined,
130
- 'min-width': opts.minWidth ? opts.minWidth + 'px' : undefined,
131
- 'overflow-wrap': opts.disableOverflowWrapBreaking ? 'normal' : undefined,
132
- ...opts.otherStyles,
110
+ if (opts.otherStyles) {
111
+ for (const key in opts.otherStyles) {
112
+ if (!this.defaultStyles[key]) {
113
+ // we need to save the original style so that we can restore it when we're done
114
+ this.defaultStyles[key] = elm.style.getPropertyValue(key)
115
+ }
116
+ }
133
117
  }
134
118
 
135
- const restoreStyles = this.setElementStyles(newStyles)
119
+ elm.innerHTML = html
136
120
 
137
- try {
138
- elm.innerHTML = html
121
+ // Apply the default styles to the element (for all styles here or that were ever seen in opts.otherStyles)
122
+ this.resetElmStyles()
139
123
 
140
- const scrollWidth = opts.measureScrollWidth ? elm.scrollWidth : 0
141
- const rect = elm.getBoundingClientRect()
124
+ elm.style.setProperty('font-family', opts.fontFamily)
125
+ elm.style.setProperty('font-style', opts.fontStyle)
126
+ elm.style.setProperty('font-weight', opts.fontWeight)
127
+ elm.style.setProperty('font-size', opts.fontSize + 'px')
128
+ elm.style.setProperty('line-height', opts.lineHeight.toString())
129
+ elm.style.setProperty('padding', opts.padding)
142
130
 
143
- return {
144
- x: 0,
145
- y: 0,
146
- w: rect.width,
147
- h: rect.height,
148
- scrollWidth,
131
+ if (opts.maxWidth) {
132
+ elm.style.setProperty('max-width', opts.maxWidth + 'px')
133
+ }
134
+
135
+ if (opts.minWidth) {
136
+ elm.style.setProperty('min-width', opts.minWidth + 'px')
137
+ }
138
+
139
+ if (opts.disableOverflowWrapBreaking) {
140
+ elm.style.setProperty('overflow-wrap', 'normal')
141
+ }
142
+
143
+ if (opts.otherStyles) {
144
+ for (const [key, value] of Object.entries(opts.otherStyles)) {
145
+ elm.style.setProperty(key, value)
149
146
  }
150
- } finally {
151
- restoreStyles()
147
+ }
148
+
149
+ const scrollWidth = opts.measureScrollWidth ? elm.scrollWidth : 0
150
+ const rect = elm.getBoundingClientRect()
151
+
152
+ return {
153
+ x: 0,
154
+ y: 0,
155
+ w: rect.width,
156
+ h: rect.height,
157
+ scrollWidth,
152
158
  }
153
159
  }
154
160
 
@@ -268,68 +274,82 @@ export class TextManager {
268
274
 
269
275
  const { elm } = this
270
276
 
277
+ if (opts.otherStyles) {
278
+ for (const key in opts.otherStyles) {
279
+ if (!this.defaultStyles[key]) {
280
+ // we need to save the original style so that we can restore it when we're done
281
+ this.defaultStyles[key] = elm.style.getPropertyValue(key)
282
+ }
283
+ }
284
+ }
285
+
286
+ this.resetElmStyles()
287
+
288
+ elm.style.setProperty('font-family', opts.fontFamily)
289
+ elm.style.setProperty('font-style', opts.fontStyle)
290
+ elm.style.setProperty('font-weight', opts.fontWeight)
291
+ elm.style.setProperty('font-size', opts.fontSize + 'px')
292
+ elm.style.setProperty('line-height', opts.lineHeight.toString())
293
+
294
+ const elementWidth = Math.ceil(opts.width - opts.padding * 2)
295
+ elm.style.setProperty('width', `${elementWidth}px`)
296
+ elm.style.setProperty('height', 'min-content')
297
+ elm.style.setProperty('text-align', textAlignmentsForLtr[opts.textAlign])
298
+
271
299
  const shouldTruncateToFirstLine =
272
300
  opts.overflow === 'truncate-ellipsis' || opts.overflow === 'truncate-clip'
273
- const elementWidth = Math.ceil(opts.width - opts.padding * 2)
274
- const newStyles = {
275
- 'font-family': opts.fontFamily,
276
- 'font-style': opts.fontStyle,
277
- 'font-weight': opts.fontWeight,
278
- 'font-size': opts.fontSize + 'px',
279
- 'line-height': opts.lineHeight.toString(),
280
- width: `${elementWidth}px`,
281
- height: 'min-content',
282
- 'text-align': textAlignmentsForLtr[opts.textAlign],
283
- 'overflow-wrap': shouldTruncateToFirstLine ? 'anywhere' : undefined,
284
- 'word-break': shouldTruncateToFirstLine ? 'break-all' : undefined,
285
- ...opts.otherStyles,
301
+
302
+ if (shouldTruncateToFirstLine) {
303
+ elm.style.setProperty('overflow-wrap', 'anywhere')
304
+ elm.style.setProperty('word-break', 'break-all')
286
305
  }
287
- const restoreStyles = this.setElementStyles(newStyles)
288
306
 
289
- try {
290
- const normalizedText = normalizeTextForDom(textToMeasure)
307
+ if (opts.otherStyles) {
308
+ for (const [key, value] of Object.entries(opts.otherStyles)) {
309
+ elm.style.setProperty(key, value)
310
+ }
311
+ }
291
312
 
292
- // Render the text into the measurement element:
293
- elm.textContent = normalizedText
313
+ const normalizedText = normalizeTextForDom(textToMeasure)
294
314
 
295
- // actually measure the text:
296
- const { spans, didTruncate } = this.measureElementTextNodeSpans(elm, {
297
- shouldTruncateToFirstLine,
298
- })
315
+ // Render the text into the measurement element:
316
+ elm.textContent = normalizedText
299
317
 
300
- if (opts.overflow === 'truncate-ellipsis' && didTruncate) {
301
- // we need to measure the ellipsis to know how much space it takes up
302
- elm.textContent = '…'
303
- const ellipsisWidth = Math.ceil(this.measureElementTextNodeSpans(elm).spans[0].box.w)
304
-
305
- // then, we need to subtract that space from the width we have and measure again:
306
- elm.style.setProperty('width', `${elementWidth - ellipsisWidth}px`)
307
- elm.textContent = normalizedText
308
- const truncatedSpans = this.measureElementTextNodeSpans(elm, {
309
- shouldTruncateToFirstLine: true,
310
- }).spans
311
-
312
- // Finally, we add in our ellipsis at the end of the last span. We
313
- // have to do this after measuring, not before, because adding the
314
- // ellipsis changes how whitespace might be getting collapsed by the
315
- // browser.
316
- const lastSpan = truncatedSpans[truncatedSpans.length - 1]!
317
- truncatedSpans.push({
318
- text: '…',
319
- box: {
320
- x: Math.min(lastSpan.box.x + lastSpan.box.w, opts.width - opts.padding - ellipsisWidth),
321
- y: lastSpan.box.y,
322
- w: ellipsisWidth,
323
- h: lastSpan.box.h,
324
- },
325
- })
326
-
327
- return truncatedSpans
328
- }
318
+ // actually measure the text:
319
+ const { spans, didTruncate } = this.measureElementTextNodeSpans(elm, {
320
+ shouldTruncateToFirstLine,
321
+ })
329
322
 
330
- return spans
331
- } finally {
332
- restoreStyles()
323
+ if (opts.overflow === 'truncate-ellipsis' && didTruncate) {
324
+ // we need to measure the ellipsis to know how much space it takes up
325
+ elm.textContent = '…'
326
+ const ellipsisWidth = Math.ceil(this.measureElementTextNodeSpans(elm).spans[0].box.w)
327
+
328
+ // then, we need to subtract that space from the width we have and measure again:
329
+ elm.style.setProperty('width', `${elementWidth - ellipsisWidth}px`)
330
+ elm.textContent = normalizedText
331
+ const truncatedSpans = this.measureElementTextNodeSpans(elm, {
332
+ shouldTruncateToFirstLine: true,
333
+ }).spans
334
+
335
+ // Finally, we add in our ellipsis at the end of the last span. We
336
+ // have to do this after measuring, not before, because adding the
337
+ // ellipsis changes how whitespace might be getting collapsed by the
338
+ // browser.
339
+ const lastSpan = truncatedSpans[truncatedSpans.length - 1]!
340
+ truncatedSpans.push({
341
+ text: '…',
342
+ box: {
343
+ x: Math.min(lastSpan.box.x + lastSpan.box.w, opts.width - opts.padding - ellipsisWidth),
344
+ y: lastSpan.box.y,
345
+ w: ellipsisWidth,
346
+ h: lastSpan.box.h,
347
+ },
348
+ })
349
+
350
+ return truncatedSpans
333
351
  }
352
+
353
+ return spans
334
354
  }
335
355
  }
@@ -21,13 +21,14 @@ export function useMaybeEditor(): Editor | null {
21
21
  return React.useContext(EditorContext)
22
22
  }
23
23
 
24
- export function EditorProvider({
25
- editor,
26
- children,
27
- }: {
24
+ /** @public */
25
+ export interface EditorProviderProps {
28
26
  editor: Editor
29
27
  children: React.ReactNode
30
- }) {
28
+ }
29
+
30
+ /** @public @react */
31
+ export function EditorProvider({ editor, children }: EditorProviderProps) {
31
32
  return (
32
33
  <EditorContext.Provider value={editor}>
33
34
  <IdProvider>{children}</IdProvider>
@@ -417,7 +417,7 @@ function importPrivateKey(pemContents: string) {
417
417
  // base64 decode the string to get the binary data
418
418
  const binaryDerString = atob(pemContents)
419
419
  // convert from a binary string to an ArrayBuffer
420
- const binaryDer = str2ab(binaryDerString)
420
+ const binaryDer = str2ab(binaryDerString) as Uint8Array
421
421
 
422
422
  return crypto.subtle.importKey(
423
423
  'pkcs8',
package/src/version.ts CHANGED
@@ -1,9 +1,9 @@
1
1
  // This file is automatically generated by internal/scripts/refresh-assets.ts.
2
2
  // Do not edit manually. Or do, I'm a comment, not a cop.
3
3
 
4
- export const version = '3.14.1'
4
+ export const version = '3.15.0-canary.0183e172b2bf'
5
5
  export const publishDates = {
6
6
  major: '2024-09-13T14:36:29.063Z',
7
- minor: '2025-07-03T08:34:52.269Z',
8
- patch: '2025-07-09T16:10:51.026Z',
7
+ minor: '2025-07-03T13:14:18.791Z',
8
+ patch: '2025-07-03T13:14:18.791Z',
9
9
  }