@tldraw/editor 3.15.0-canary.d3401abd19b9 → 3.15.0-canary.db14db4f5395

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 (77) hide show
  1. package/dist-cjs/index.d.ts +41 -49
  2. package/dist-cjs/index.js +16 -18
  3. package/dist-cjs/index.js.map +2 -2
  4. package/dist-cjs/lib/config/TLUserPreferences.js +1 -7
  5. package/dist-cjs/lib/config/TLUserPreferences.js.map +2 -2
  6. package/dist-cjs/lib/editor/Editor.js +2 -20
  7. package/dist-cjs/lib/editor/Editor.js.map +2 -2
  8. package/dist-cjs/lib/editor/managers/TextManager/TextManager.js +101 -96
  9. package/dist-cjs/lib/editor/managers/TextManager/TextManager.js.map +2 -2
  10. package/dist-cjs/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.js +2 -7
  11. package/dist-cjs/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.js.map +2 -2
  12. package/dist-cjs/lib/primitives/geometry/Arc2d.js +1 -1
  13. package/dist-cjs/lib/primitives/geometry/Arc2d.js.map +2 -2
  14. package/dist-cjs/lib/primitives/geometry/Circle2d.js +1 -1
  15. package/dist-cjs/lib/primitives/geometry/Circle2d.js.map +2 -2
  16. package/dist-cjs/lib/primitives/geometry/CubicBezier2d.js +1 -3
  17. package/dist-cjs/lib/primitives/geometry/CubicBezier2d.js.map +2 -2
  18. package/dist-cjs/lib/primitives/geometry/Ellipse2d.js +1 -1
  19. package/dist-cjs/lib/primitives/geometry/Ellipse2d.js.map +2 -2
  20. package/dist-cjs/lib/primitives/geometry/geometry-constants.js +2 -2
  21. package/dist-cjs/lib/primitives/geometry/geometry-constants.js.map +2 -2
  22. package/dist-cjs/lib/primitives/intersect.js +4 -4
  23. package/dist-cjs/lib/primitives/intersect.js.map +2 -2
  24. package/dist-cjs/lib/primitives/utils.js +0 -4
  25. package/dist-cjs/lib/primitives/utils.js.map +2 -2
  26. package/dist-cjs/lib/utils/sync/TLLocalSyncClient.js +1 -0
  27. package/dist-cjs/lib/utils/sync/TLLocalSyncClient.js.map +2 -2
  28. package/dist-cjs/version.js +3 -3
  29. package/dist-cjs/version.js.map +1 -1
  30. package/dist-esm/index.d.mts +41 -49
  31. package/dist-esm/index.mjs +41 -43
  32. package/dist-esm/index.mjs.map +2 -2
  33. package/dist-esm/lib/config/TLUserPreferences.mjs +1 -7
  34. package/dist-esm/lib/config/TLUserPreferences.mjs.map +2 -2
  35. package/dist-esm/lib/editor/Editor.mjs +2 -20
  36. package/dist-esm/lib/editor/Editor.mjs.map +2 -2
  37. package/dist-esm/lib/editor/managers/TextManager/TextManager.mjs +101 -96
  38. package/dist-esm/lib/editor/managers/TextManager/TextManager.mjs.map +2 -2
  39. package/dist-esm/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.mjs +2 -7
  40. package/dist-esm/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.mjs.map +2 -2
  41. package/dist-esm/lib/primitives/geometry/Arc2d.mjs +2 -2
  42. package/dist-esm/lib/primitives/geometry/Arc2d.mjs.map +2 -2
  43. package/dist-esm/lib/primitives/geometry/Circle2d.mjs +2 -2
  44. package/dist-esm/lib/primitives/geometry/Circle2d.mjs.map +2 -2
  45. package/dist-esm/lib/primitives/geometry/CubicBezier2d.mjs +1 -3
  46. package/dist-esm/lib/primitives/geometry/CubicBezier2d.mjs.map +2 -2
  47. package/dist-esm/lib/primitives/geometry/Ellipse2d.mjs +2 -2
  48. package/dist-esm/lib/primitives/geometry/Ellipse2d.mjs.map +2 -2
  49. package/dist-esm/lib/primitives/geometry/geometry-constants.mjs +2 -2
  50. package/dist-esm/lib/primitives/geometry/geometry-constants.mjs.map +2 -2
  51. package/dist-esm/lib/primitives/intersect.mjs +5 -5
  52. package/dist-esm/lib/primitives/intersect.mjs.map +2 -2
  53. package/dist-esm/lib/primitives/utils.mjs +0 -4
  54. package/dist-esm/lib/primitives/utils.mjs.map +2 -2
  55. package/dist-esm/lib/utils/sync/TLLocalSyncClient.mjs +1 -0
  56. package/dist-esm/lib/utils/sync/TLLocalSyncClient.mjs.map +2 -2
  57. package/dist-esm/version.mjs +3 -3
  58. package/dist-esm/version.mjs.map +1 -1
  59. package/package.json +7 -7
  60. package/src/index.ts +62 -63
  61. package/src/lib/config/TLUserPreferences.ts +0 -7
  62. package/src/lib/editor/Editor.test.ts +0 -407
  63. package/src/lib/editor/Editor.ts +4 -29
  64. package/src/lib/editor/managers/TextManager/TextManager.ts +128 -108
  65. package/src/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.test.ts +0 -21
  66. package/src/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.ts +0 -8
  67. package/src/lib/license/LicenseManager.test.ts +1 -1
  68. package/src/lib/primitives/geometry/Arc2d.ts +2 -2
  69. package/src/lib/primitives/geometry/Circle2d.ts +2 -2
  70. package/src/lib/primitives/geometry/CubicBezier2d.ts +1 -4
  71. package/src/lib/primitives/geometry/Ellipse2d.ts +2 -2
  72. package/src/lib/primitives/geometry/geometry-constants.ts +1 -2
  73. package/src/lib/primitives/intersect.ts +5 -12
  74. package/src/lib/primitives/utils.ts +0 -11
  75. package/src/lib/utils/sync/TLLocalSyncClient.ts +1 -0
  76. package/src/version.ts +3 -3
  77. package/src/lib/primitives/intersect.test.ts +0 -946
@@ -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
  }
@@ -24,7 +24,6 @@ describe('UserPreferencesManager', () => {
24
24
  color: '#FF802B',
25
25
  locale: 'en',
26
26
  animationSpeed: 1,
27
- areKeyboardShortcutsEnabled: true,
28
27
  edgeScrollSpeed: 1,
29
28
  colorScheme: 'light',
30
29
  isSnapMode: false,
@@ -230,7 +229,6 @@ describe('UserPreferencesManager', () => {
230
229
  locale: mockUserPreferences.locale,
231
230
  color: mockUserPreferences.color,
232
231
  animationSpeed: mockUserPreferences.animationSpeed,
233
- areKeyboardShortcutsEnabled: mockUserPreferences.areKeyboardShortcutsEnabled,
234
232
  isSnapMode: mockUserPreferences.isSnapMode,
235
233
  colorScheme: mockUserPreferences.colorScheme,
236
234
  isDarkMode: false, // light mode
@@ -364,21 +362,6 @@ describe('UserPreferencesManager', () => {
364
362
  })
365
363
  })
366
364
 
367
- describe('getAreKeyboardShortcutsEnabled', () => {
368
- it('should return user keyboard shortcuts', () => {
369
- expect(userPreferencesManager.getAreKeyboardShortcutsEnabled()).toBe(
370
- mockUserPreferences.areKeyboardShortcutsEnabled
371
- )
372
- })
373
-
374
- it('should return default keyboard shortcuts when null', () => {
375
- userPreferencesAtom.set({ ...mockUserPreferences, areKeyboardShortcutsEnabled: null })
376
- expect(userPreferencesManager.getAreKeyboardShortcutsEnabled()).toBe(
377
- defaultUserPreferences.areKeyboardShortcutsEnabled
378
- )
379
- })
380
- })
381
-
382
365
  describe('getEdgeScrollSpeed', () => {
383
366
  it('should return user edge scroll speed', () => {
384
367
  expect(userPreferencesManager.getEdgeScrollSpeed()).toBe(
@@ -500,7 +483,6 @@ describe('UserPreferencesManager', () => {
500
483
  color: null,
501
484
  locale: null,
502
485
  animationSpeed: null,
503
- areKeyboardShortcutsEnabled: null,
504
486
  edgeScrollSpeed: null,
505
487
  isSnapMode: null,
506
488
  isWrapMode: null,
@@ -514,9 +496,6 @@ describe('UserPreferencesManager', () => {
514
496
  expect(userPreferencesManager.getColor()).toBe(defaultUserPreferences.color)
515
497
  expect(userPreferencesManager.getLocale()).toBe(defaultUserPreferences.locale)
516
498
  expect(userPreferencesManager.getAnimationSpeed()).toBe(defaultUserPreferences.animationSpeed)
517
- expect(userPreferencesManager.getAreKeyboardShortcutsEnabled()).toBe(
518
- defaultUserPreferences.areKeyboardShortcutsEnabled
519
- )
520
499
  expect(userPreferencesManager.getEdgeScrollSpeed()).toBe(
521
500
  defaultUserPreferences.edgeScrollSpeed
522
501
  )
@@ -43,7 +43,6 @@ export class UserPreferencesManager {
43
43
  locale: this.getLocale(),
44
44
  color: this.getColor(),
45
45
  animationSpeed: this.getAnimationSpeed(),
46
- areKeyboardShortcutsEnabled: this.getAreKeyboardShortcutsEnabled(),
47
46
  isSnapMode: this.getIsSnapMode(),
48
47
  colorScheme: this.user.userPreferences.get().colorScheme,
49
48
  isDarkMode: this.getIsDarkMode(),
@@ -76,13 +75,6 @@ export class UserPreferencesManager {
76
75
  return this.user.userPreferences.get().animationSpeed ?? defaultUserPreferences.animationSpeed
77
76
  }
78
77
 
79
- @computed getAreKeyboardShortcutsEnabled() {
80
- return (
81
- this.user.userPreferences.get().areKeyboardShortcutsEnabled ??
82
- defaultUserPreferences.areKeyboardShortcutsEnabled
83
- )
84
- }
85
-
86
78
  @computed getId() {
87
79
  return this.user.userPreferences.get().id
88
80
  }
@@ -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',
@@ -2,7 +2,7 @@ import { Vec, VecLike } from '../Vec'
2
2
  import { intersectLineSegmentCircle } from '../intersect'
3
3
  import { getArcMeasure, getPointInArcT, getPointOnCircle } from '../utils'
4
4
  import { Geometry2d, Geometry2dOptions } from './Geometry2d'
5
- import { getVerticesCountForArcLength } from './geometry-constants'
5
+ import { getVerticesCountForLength } from './geometry-constants'
6
6
 
7
7
  /** @public */
8
8
  export class Arc2d extends Geometry2d {
@@ -94,7 +94,7 @@ export class Arc2d extends Geometry2d {
94
94
  getVertices(): Vec[] {
95
95
  const { _center, _measure: measure, length, _radius: radius, _angleStart: angleStart } = this
96
96
  const vertices: Vec[] = []
97
- for (let i = 0, n = getVerticesCountForArcLength(Math.abs(length)); i < n + 1; i++) {
97
+ for (let i = 0, n = getVerticesCountForLength(Math.abs(length)); i < n + 1; i++) {
98
98
  const t = (i / n) * measure
99
99
  const angle = angleStart + t
100
100
  vertices.push(getPointOnCircle(_center, radius, angle))
@@ -3,7 +3,7 @@ import { Vec, VecLike } from '../Vec'
3
3
  import { intersectLineSegmentCircle } from '../intersect'
4
4
  import { PI2, getPointOnCircle } from '../utils'
5
5
  import { Geometry2d, Geometry2dOptions } from './Geometry2d'
6
- import { getVerticesCountForArcLength } from './geometry-constants'
6
+ import { getVerticesCountForLength } from './geometry-constants'
7
7
 
8
8
  /** @public */
9
9
  export class Circle2d extends Geometry2d {
@@ -36,7 +36,7 @@ export class Circle2d extends Geometry2d {
36
36
  const { _center, _radius: radius } = this
37
37
  const perimeter = PI2 * radius
38
38
  const vertices: Vec[] = []
39
- for (let i = 0, n = getVerticesCountForArcLength(perimeter); i < n; i++) {
39
+ for (let i = 0, n = getVerticesCountForLength(perimeter); i < n; i++) {
40
40
  const angle = (i / n) * PI2
41
41
  vertices.push(getPointOnCircle(_center, radius, angle))
42
42
  }
@@ -8,7 +8,6 @@ export class CubicBezier2d extends Polyline2d {
8
8
  private _b: Vec
9
9
  private _c: Vec
10
10
  private _d: Vec
11
- private _resolution: number
12
11
 
13
12
  constructor(
14
13
  config: Omit<Geometry2dOptions, 'isFilled' | 'isClosed'> & {
@@ -16,7 +15,6 @@ export class CubicBezier2d extends Polyline2d {
16
15
  cp1: Vec
17
16
  cp2: Vec
18
17
  end: Vec
19
- resolution?: number
20
18
  }
21
19
  ) {
22
20
  const { start: a, cp1: b, cp2: c, end: d } = config
@@ -26,14 +24,13 @@ export class CubicBezier2d extends Polyline2d {
26
24
  this._b = b
27
25
  this._c = c
28
26
  this._d = d
29
- this._resolution = config.resolution ?? 10
30
27
  }
31
28
 
32
29
  override getVertices() {
33
30
  const vertices = [] as Vec[]
34
31
  const { _a: a, _b: b, _c: c, _d: d } = this
35
32
  // we'll always use ten vertices for each bezier curve
36
- for (let i = 0, n = this._resolution; i <= n; i++) {
33
+ for (let i = 0, n = 10; i <= n; i++) {
37
34
  const t = i / n
38
35
  vertices.push(
39
36
  new Vec(
@@ -3,7 +3,7 @@ import { Vec, VecLike } from '../Vec'
3
3
  import { PI, PI2, clamp, perimeterOfEllipse } from '../utils'
4
4
  import { Edge2d } from './Edge2d'
5
5
  import { Geometry2d, Geometry2dOptions } from './Geometry2d'
6
- import { getVerticesCountForArcLength } from './geometry-constants'
6
+ import { getVerticesCountForLength } from './geometry-constants'
7
7
 
8
8
  /** @public */
9
9
  export class Ellipse2d extends Geometry2d {
@@ -47,7 +47,7 @@ export class Ellipse2d extends Geometry2d {
47
47
  const q = Math.pow(cx - cy, 2) / Math.pow(cx + cy, 2)
48
48
  const p = PI * (cx + cy) * (1 + (3 * q) / (10 + Math.sqrt(4 - 3 * q)))
49
49
  // Number of points
50
- const len = getVerticesCountForArcLength(p)
50
+ const len = getVerticesCountForLength(p)
51
51
  // Size of step
52
52
  const step = PI2 / len
53
53
 
@@ -1,7 +1,6 @@
1
1
  const SPACING = 20
2
2
  const MIN_COUNT = 8
3
3
 
4
- /** @internal */
5
- export function getVerticesCountForArcLength(length: number, spacing = SPACING) {
4
+ export function getVerticesCountForLength(length: number, spacing = SPACING) {
6
5
  return Math.max(MIN_COUNT, Math.ceil(length / spacing))
7
6
  }
@@ -1,5 +1,5 @@
1
1
  import { Box } from './Box'
2
- import { approximately, approximatelyLte, pointInPolygon } from './utils'
2
+ import { pointInPolygon } from './utils'
3
3
  import { Vec, VecLike } from './Vec'
4
4
 
5
5
  // need even more intersections? See https://gist.github.com/steveruizok/35c02d526c707003a5c79761bfb89a52
@@ -17,8 +17,7 @@ export function intersectLineSegmentLineSegment(
17
17
  a1: VecLike,
18
18
  a2: VecLike,
19
19
  b1: VecLike,
20
- b2: VecLike,
21
- precision = 1e-10
20
+ b2: VecLike
22
21
  ) {
23
22
  const ABx = a1.x - b1.x
24
23
  const ABy = a1.y - b1.y
@@ -30,19 +29,14 @@ export function intersectLineSegmentLineSegment(
30
29
  const ub_t = AVx * ABy - AVy * ABx
31
30
  const u_b = BVy * AVx - BVx * AVy
32
31
 
33
- if (approximately(ua_t, 0, precision) || approximately(ub_t, 0, precision)) return null // coincident
32
+ if (ua_t === 0 || ub_t === 0) return null // coincident
34
33
 
35
- if (approximately(u_b, 0, precision)) return null // parallel
34
+ if (u_b === 0) return null // parallel
36
35
 
37
36
  if (u_b !== 0) {
38
37
  const ua = ua_t / u_b
39
38
  const ub = ub_t / u_b
40
- if (
41
- approximatelyLte(0, ua, precision) &&
42
- approximatelyLte(ua, 1, precision) &&
43
- approximatelyLte(0, ub, precision) &&
44
- approximatelyLte(ub, 1, precision)
45
- ) {
39
+ if (0 <= ua && ua <= 1 && 0 <= ub && ub <= 1) {
46
40
  return Vec.AddXY(a1, ua * AVx, ua * AVy)
47
41
  }
48
42
  }
@@ -131,7 +125,6 @@ export function intersectLineSegmentPolygon(a1: VecLike, a2: VecLike, points: Ve
131
125
  points[i - 1],
132
126
  points[i % points.length]
133
127
  )
134
-
135
128
  if (segmentIntersection) result.push(segmentIntersection)
136
129
  }
137
130
 
@@ -77,17 +77,6 @@ export function approximately(a: number, b: number, precision = 0.000001) {
77
77
  return Math.abs(a - b) <= precision
78
78
  }
79
79
 
80
- /**
81
- * Whether a number is approximately less than or equal to another number.
82
- *
83
- * @param a - The first number.
84
- * @param b - The second number.
85
- * @public
86
- */
87
- export function approximatelyLte(a: number, b: number, precision = 0.000001) {
88
- return a < b || approximately(a, b, precision)
89
- }
90
-
91
80
  /**
92
81
  * Find the approximate perimeter of an ellipse.
93
82
  *
@@ -266,6 +266,7 @@ export class TLLocalSyncClient {
266
266
 
267
267
  private isPersisting = false
268
268
  private didLastWriteError = false
269
+ // eslint-disable-next-line no-restricted-globals
269
270
  private scheduledPersistTimeout: ReturnType<typeof setTimeout> | null = null
270
271
 
271
272
  /**
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.15.0-canary.d3401abd19b9'
4
+ export const version = '3.15.0-canary.db14db4f5395'
5
5
  export const publishDates = {
6
6
  major: '2024-09-13T14:36:29.063Z',
7
- minor: '2025-07-10T15:17:09.923Z',
8
- patch: '2025-07-10T15:17:09.923Z',
7
+ minor: '2025-07-03T15:24:41.419Z',
8
+ patch: '2025-07-03T15:24:41.419Z',
9
9
  }