@tldraw/editor 3.14.0-canary.b8c5ab05b06a → 3.14.0-canary.ba997e465fde

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/index.d.ts +11 -4
  2. package/dist-cjs/index.js +8 -8
  3. package/dist-cjs/index.js.map +2 -2
  4. package/dist-cjs/lib/editor/Editor.js +32 -24
  5. package/dist-cjs/lib/editor/Editor.js.map +2 -2
  6. package/dist-cjs/lib/editor/derivations/bindingsIndex.js +22 -22
  7. package/dist-cjs/lib/editor/derivations/bindingsIndex.js.map +2 -2
  8. package/dist-cjs/lib/editor/derivations/parentsToChildren.js +16 -16
  9. package/dist-cjs/lib/editor/derivations/parentsToChildren.js.map +2 -2
  10. package/dist-cjs/lib/editor/managers/{ClickManager.js → ClickManager/ClickManager.js} +1 -1
  11. package/dist-cjs/lib/editor/managers/ClickManager/ClickManager.js.map +7 -0
  12. package/dist-cjs/lib/editor/managers/{EdgeScrollManager.js → EdgeScrollManager/EdgeScrollManager.js} +2 -2
  13. package/dist-cjs/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.js.map +7 -0
  14. package/dist-cjs/lib/editor/managers/FocusManager/FocusManager.js.map +7 -0
  15. package/dist-cjs/lib/editor/managers/FontManager/FontManager.js.map +7 -0
  16. package/dist-cjs/lib/editor/managers/{HistoryManager.js → HistoryManager/HistoryManager.js} +64 -6
  17. package/dist-cjs/lib/editor/managers/HistoryManager/HistoryManager.js.map +7 -0
  18. package/dist-cjs/lib/editor/managers/{ScribbleManager.js → ScribbleManager/ScribbleManager.js} +1 -1
  19. package/dist-cjs/lib/editor/managers/ScribbleManager/ScribbleManager.js.map +7 -0
  20. package/dist-cjs/lib/editor/managers/TextManager/TextManager.js.map +7 -0
  21. package/dist-cjs/lib/editor/managers/{TickManager.js → TickManager/TickManager.js} +1 -1
  22. package/dist-cjs/lib/editor/managers/TickManager/TickManager.js.map +7 -0
  23. package/dist-cjs/lib/editor/managers/{UserPreferencesManager.js → UserPreferencesManager/UserPreferencesManager.js} +1 -1
  24. package/dist-cjs/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.js.map +7 -0
  25. package/dist-cjs/lib/editor/shapes/ShapeUtil.js.map +1 -1
  26. package/dist-cjs/lib/exports/getSvgJsx.js.map +1 -1
  27. package/dist-cjs/lib/utils/richText.js.map +1 -1
  28. package/dist-cjs/version.js +3 -3
  29. package/dist-cjs/version.js.map +1 -1
  30. package/dist-esm/index.d.mts +11 -4
  31. package/dist-esm/index.mjs +12 -8
  32. package/dist-esm/index.mjs.map +2 -2
  33. package/dist-esm/lib/editor/Editor.mjs +32 -24
  34. package/dist-esm/lib/editor/Editor.mjs.map +2 -2
  35. package/dist-esm/lib/editor/derivations/bindingsIndex.mjs +22 -22
  36. package/dist-esm/lib/editor/derivations/bindingsIndex.mjs.map +2 -2
  37. package/dist-esm/lib/editor/derivations/parentsToChildren.mjs +16 -16
  38. package/dist-esm/lib/editor/derivations/parentsToChildren.mjs.map +2 -2
  39. package/dist-esm/lib/editor/managers/{ClickManager.mjs → ClickManager/ClickManager.mjs} +1 -1
  40. package/dist-esm/lib/editor/managers/ClickManager/ClickManager.mjs.map +7 -0
  41. package/dist-esm/lib/editor/managers/{EdgeScrollManager.mjs → EdgeScrollManager/EdgeScrollManager.mjs} +2 -2
  42. package/dist-esm/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.mjs.map +7 -0
  43. package/dist-esm/lib/editor/managers/FocusManager/FocusManager.mjs.map +7 -0
  44. package/dist-esm/lib/editor/managers/FontManager/FontManager.mjs.map +7 -0
  45. package/dist-esm/lib/editor/managers/{HistoryManager.mjs → HistoryManager/HistoryManager.mjs} +60 -2
  46. package/dist-esm/lib/editor/managers/HistoryManager/HistoryManager.mjs.map +7 -0
  47. package/dist-esm/lib/editor/managers/{ScribbleManager.mjs → ScribbleManager/ScribbleManager.mjs} +1 -1
  48. package/dist-esm/lib/editor/managers/ScribbleManager/ScribbleManager.mjs.map +7 -0
  49. package/dist-esm/lib/editor/managers/TextManager/TextManager.mjs.map +7 -0
  50. package/dist-esm/lib/editor/managers/{TickManager.mjs → TickManager/TickManager.mjs} +1 -1
  51. package/dist-esm/lib/editor/managers/TickManager/TickManager.mjs.map +7 -0
  52. package/dist-esm/lib/editor/managers/{UserPreferencesManager.mjs → UserPreferencesManager/UserPreferencesManager.mjs} +1 -1
  53. package/dist-esm/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.mjs.map +7 -0
  54. package/dist-esm/lib/editor/shapes/ShapeUtil.mjs.map +1 -1
  55. package/dist-esm/lib/exports/getSvgJsx.mjs.map +1 -1
  56. package/dist-esm/lib/utils/richText.mjs.map +1 -1
  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 +13 -7
  61. package/src/lib/editor/Editor.test.ts +252 -3
  62. package/src/lib/editor/Editor.ts +33 -25
  63. package/src/lib/editor/derivations/bindingsIndex.ts +27 -26
  64. package/src/lib/editor/derivations/parentsToChildren.ts +28 -25
  65. package/src/lib/editor/managers/ClickManager/ClickManager.test.ts +442 -0
  66. package/src/lib/editor/managers/{ClickManager.ts → ClickManager/ClickManager.ts} +3 -3
  67. package/src/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.test.ts +374 -0
  68. package/src/lib/editor/managers/{EdgeScrollManager.ts → EdgeScrollManager/EdgeScrollManager.ts} +3 -3
  69. package/src/lib/editor/managers/FocusManager/FocusManager.test.ts +455 -0
  70. package/src/lib/editor/managers/{FocusManager.ts → FocusManager/FocusManager.ts} +1 -1
  71. package/src/lib/editor/managers/FontManager/FontManager.test.ts +263 -0
  72. package/src/lib/editor/managers/{FontManager.ts → FontManager/FontManager.ts} +1 -1
  73. package/src/lib/editor/managers/{HistoryManager.test.ts → HistoryManager/HistoryManager.test.ts} +388 -1
  74. package/src/lib/editor/managers/{HistoryManager.ts → HistoryManager/HistoryManager.ts} +73 -2
  75. package/src/lib/editor/managers/ScribbleManager/ScribbleManager.test.ts +624 -0
  76. package/src/lib/editor/managers/{ScribbleManager.ts → ScribbleManager/ScribbleManager.ts} +2 -2
  77. package/src/lib/editor/managers/SnapManager/SnapManager.test.ts +485 -0
  78. package/src/lib/editor/managers/TextManager/TextManager.test.ts +411 -0
  79. package/src/lib/editor/managers/{TextManager.ts → TextManager/TextManager.ts} +1 -1
  80. package/src/lib/editor/managers/TickManager/TickManager.test.ts +314 -0
  81. package/src/lib/editor/managers/{TickManager.ts → TickManager/TickManager.ts} +2 -2
  82. package/src/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.test.ts +591 -0
  83. package/src/lib/editor/managers/{UserPreferencesManager.ts → UserPreferencesManager/UserPreferencesManager.ts} +2 -2
  84. package/src/lib/editor/shapes/ShapeUtil.ts +1 -1
  85. package/src/lib/exports/getSvgJsx.tsx +1 -1
  86. package/src/lib/utils/richText.ts +1 -1
  87. package/src/version.ts +3 -3
  88. package/dist-cjs/lib/editor/managers/ClickManager.js.map +0 -7
  89. package/dist-cjs/lib/editor/managers/EdgeScrollManager.js.map +0 -7
  90. package/dist-cjs/lib/editor/managers/FocusManager.js.map +0 -7
  91. package/dist-cjs/lib/editor/managers/FontManager.js.map +0 -7
  92. package/dist-cjs/lib/editor/managers/HistoryManager.js.map +0 -7
  93. package/dist-cjs/lib/editor/managers/ScribbleManager.js.map +0 -7
  94. package/dist-cjs/lib/editor/managers/Stack.js +0 -82
  95. package/dist-cjs/lib/editor/managers/Stack.js.map +0 -7
  96. package/dist-cjs/lib/editor/managers/TextManager.js.map +0 -7
  97. package/dist-cjs/lib/editor/managers/TickManager.js.map +0 -7
  98. package/dist-cjs/lib/editor/managers/UserPreferencesManager.js.map +0 -7
  99. package/dist-esm/lib/editor/managers/ClickManager.mjs.map +0 -7
  100. package/dist-esm/lib/editor/managers/EdgeScrollManager.mjs.map +0 -7
  101. package/dist-esm/lib/editor/managers/FocusManager.mjs.map +0 -7
  102. package/dist-esm/lib/editor/managers/FontManager.mjs.map +0 -7
  103. package/dist-esm/lib/editor/managers/HistoryManager.mjs.map +0 -7
  104. package/dist-esm/lib/editor/managers/ScribbleManager.mjs.map +0 -7
  105. package/dist-esm/lib/editor/managers/Stack.mjs +0 -62
  106. package/dist-esm/lib/editor/managers/Stack.mjs.map +0 -7
  107. package/dist-esm/lib/editor/managers/TextManager.mjs.map +0 -7
  108. package/dist-esm/lib/editor/managers/TickManager.mjs.map +0 -7
  109. package/dist-esm/lib/editor/managers/UserPreferencesManager.mjs.map +0 -7
  110. package/src/lib/editor/managers/ScribbleManager.test.ts +0 -32
  111. package/src/lib/editor/managers/Stack.ts +0 -71
  112. /package/dist-cjs/lib/editor/managers/{FocusManager.js → FocusManager/FocusManager.js} +0 -0
  113. /package/dist-cjs/lib/editor/managers/{FontManager.js → FontManager/FontManager.js} +0 -0
  114. /package/dist-cjs/lib/editor/managers/{TextManager.js → TextManager/TextManager.js} +0 -0
  115. /package/dist-esm/lib/editor/managers/{FocusManager.mjs → FocusManager/FocusManager.mjs} +0 -0
  116. /package/dist-esm/lib/editor/managers/{FontManager.mjs → FontManager/FontManager.mjs} +0 -0
  117. /package/dist-esm/lib/editor/managers/{TextManager.mjs → TextManager/TextManager.mjs} +0 -0
@@ -148,16 +148,16 @@ import { bindingsIndex } from './derivations/bindingsIndex'
148
148
  import { notVisibleShapes } from './derivations/notVisibleShapes'
149
149
  import { parentsToChildren } from './derivations/parentsToChildren'
150
150
  import { deriveShapeIdsInCurrentPage } from './derivations/shapeIdsInCurrentPage'
151
- import { ClickManager } from './managers/ClickManager'
152
- import { EdgeScrollManager } from './managers/EdgeScrollManager'
153
- import { FocusManager } from './managers/FocusManager'
154
- import { FontManager } from './managers/FontManager'
155
- import { HistoryManager } from './managers/HistoryManager'
156
- import { ScribbleManager } from './managers/ScribbleManager'
151
+ import { ClickManager } from './managers/ClickManager/ClickManager'
152
+ import { EdgeScrollManager } from './managers/EdgeScrollManager/EdgeScrollManager'
153
+ import { FocusManager } from './managers/FocusManager/FocusManager'
154
+ import { FontManager } from './managers/FontManager/FontManager'
155
+ import { HistoryManager } from './managers/HistoryManager/HistoryManager'
156
+ import { ScribbleManager } from './managers/ScribbleManager/ScribbleManager'
157
157
  import { SnapManager } from './managers/SnapManager/SnapManager'
158
- import { TextManager } from './managers/TextManager'
159
- import { TickManager } from './managers/TickManager'
160
- import { UserPreferencesManager } from './managers/UserPreferencesManager'
158
+ import { TextManager } from './managers/TextManager/TextManager'
159
+ import { TickManager } from './managers/TickManager/TickManager'
160
+ import { UserPreferencesManager } from './managers/UserPreferencesManager/UserPreferencesManager'
161
161
  import { ShapeUtil, TLGeometryOpts, TLResizeMode } from './shapes/ShapeUtil'
162
162
  import { RootState } from './tools/RootState'
163
163
  import { StateNode, TLStateNodeConstructor } from './tools/StateNode'
@@ -328,7 +328,7 @@ export class Editor extends EventEmitter<TLEventMap> {
328
328
  this.store = store
329
329
  this.history = new HistoryManager<TLRecord>({
330
330
  store,
331
- annotateError: (error) => {
331
+ annotateError: (error: any) => {
332
332
  this.annotateError(error, { origin: 'history.batch', willCrashApp: true })
333
333
  this.crash(error)
334
334
  },
@@ -5035,28 +5035,33 @@ export class Editor extends EventEmitter<TLEventMap> {
5035
5035
  *
5036
5036
  * @public
5037
5037
  */
5038
- isShapeOrAncestorLocked(shape?: TLShape): boolean
5039
- isShapeOrAncestorLocked(id?: TLShapeId): boolean
5040
- isShapeOrAncestorLocked(arg?: TLShape | TLShapeId): boolean {
5041
- const shape = typeof arg === 'string' ? this.getShape(arg) : arg
5042
- if (shape === undefined) return false
5043
- if (shape.isLocked) return true
5044
- return this.isShapeOrAncestorLocked(this.getShapeParent(shape))
5038
+ isShapeOrAncestorLocked(shape?: TLShape | TLShapeId): boolean {
5039
+ const _shape = shape && this.getShape(shape)
5040
+ if (_shape === undefined) return false
5041
+ if (_shape.isLocked) return true
5042
+ return this.isShapeOrAncestorLocked(this.getShapeParent(_shape))
5045
5043
  }
5046
5044
 
5045
+ /**
5046
+ * Get shapes that are outside of the viewport.
5047
+ *
5048
+ * @public
5049
+ */
5047
5050
  @computed
5048
- private _notVisibleShapes() {
5049
- return notVisibleShapes(this)
5051
+ getNotVisibleShapes() {
5052
+ return this._notVisibleShapes.get()
5050
5053
  }
5051
5054
 
5055
+ private _notVisibleShapes = notVisibleShapes(this)
5056
+
5052
5057
  /**
5053
- * Get culled shapes.
5058
+ * Get culled shapes (those that should not render), taking into account which shapes are selected or editing.
5054
5059
  *
5055
5060
  * @public
5056
5061
  */
5057
5062
  @computed
5058
5063
  getCulledShapes() {
5059
- const notVisibleShapes = this._notVisibleShapes().get()
5064
+ const notVisibleShapes = this.getNotVisibleShapes()
5060
5065
  const selectedShapeIds = this.getSelectedShapeIds()
5061
5066
  const editingId = this.getEditingShapeId()
5062
5067
  const culledShapes = new Set<TLShapeId>(notVisibleShapes)
@@ -5305,21 +5310,23 @@ export class Editor extends EventEmitter<TLEventMap> {
5305
5310
  * @example
5306
5311
  * ```ts
5307
5312
  * editor.getShapesAtPoint({ x: 100, y: 100 })
5308
- * editor.getShapesAtPoint({ x: 100, y: 100 }, { hitInside: true, exact: true })
5313
+ * editor.getShapesAtPoint({ x: 100, y: 100 }, { hitInside: true, margin: 8 })
5309
5314
  * ```
5310
5315
  *
5311
5316
  * @param point - The page point to test.
5312
5317
  * @param opts - The options for the hit point testing.
5313
5318
  *
5319
+ * @returns An array of shapes at the given point, sorted in reverse order of their absolute z-index (top-most shape first).
5320
+ *
5314
5321
  * @public
5315
5322
  */
5316
5323
  getShapesAtPoint(
5317
5324
  point: VecLike,
5318
5325
  opts = {} as { margin?: number; hitInside?: boolean }
5319
5326
  ): TLShape[] {
5320
- return this.getCurrentPageShapes().filter(
5321
- (shape) => !this.isShapeHidden(shape) && this.isPointInShape(shape, point, opts)
5322
- )
5327
+ return this.getCurrentPageShapesSorted()
5328
+ .filter((shape) => !this.isShapeHidden(shape) && this.isPointInShape(shape, point, opts))
5329
+ .reverse()
5323
5330
  }
5324
5331
 
5325
5332
  /**
@@ -9268,6 +9275,7 @@ export class Editor extends EventEmitter<TLEventMap> {
9268
9275
  if (rootShapes.length === 1) {
9269
9276
  const onlyRoot = rootShapes[0] as TLFrameShape
9270
9277
  // If the old bounds are in the viewport...
9278
+ // todo: replace frame references with shapes that can accept children
9271
9279
  if (this.isShapeOfType<TLFrameShape>(onlyRoot, 'frame')) {
9272
9280
  while (
9273
9281
  this.getShapesAtPoint(point).some(
@@ -1,41 +1,42 @@
1
1
  import { Computed, RESET_VALUE, computed, isUninitialized } from '@tldraw/state'
2
- import { TLBinding, TLShapeId } from '@tldraw/tlschema'
2
+ import { TLArrowBinding, TLBinding, TLShapeId, TLUnknownBinding } from '@tldraw/tlschema'
3
3
  import { objectMapValues } from '@tldraw/utils'
4
4
  import { Editor } from '../Editor'
5
5
 
6
6
  type TLBindingsIndex = Map<TLShapeId, TLBinding[]>
7
7
 
8
- export const bindingsIndex = (editor: Editor): Computed<TLBindingsIndex> => {
9
- const { store } = editor
10
- const bindingsHistory = store.query.filterHistory('binding')
11
- const bindingsQuery = store.query.records('binding')
12
- function fromScratch() {
13
- const allBindings = bindingsQuery.get() as TLBinding[]
8
+ function fromScratch(bindingsQuery: Computed<(TLArrowBinding | TLUnknownBinding)[], unknown>) {
9
+ const allBindings = bindingsQuery.get() as TLBinding[]
14
10
 
15
- const shape2Binding: TLBindingsIndex = new Map()
11
+ const shapesToBindings: TLBindingsIndex = new Map()
16
12
 
17
- for (const binding of allBindings) {
18
- const { fromId, toId } = binding
19
- const bindingsForFromShape = shape2Binding.get(fromId)
20
- if (!bindingsForFromShape) {
21
- shape2Binding.set(fromId, [binding])
22
- } else {
23
- bindingsForFromShape.push(binding)
24
- }
25
- const bindingsForToShape = shape2Binding.get(toId)
26
- if (!bindingsForToShape) {
27
- shape2Binding.set(toId, [binding])
28
- } else {
29
- bindingsForToShape.push(binding)
30
- }
13
+ for (const binding of allBindings) {
14
+ const { fromId, toId } = binding
15
+ const bindingsForFromShape = shapesToBindings.get(fromId)
16
+ if (!bindingsForFromShape) {
17
+ shapesToBindings.set(fromId, [binding])
18
+ } else {
19
+ bindingsForFromShape.push(binding)
20
+ }
21
+ const bindingsForToShape = shapesToBindings.get(toId)
22
+ if (!bindingsForToShape) {
23
+ shapesToBindings.set(toId, [binding])
24
+ } else {
25
+ bindingsForToShape.push(binding)
31
26
  }
32
-
33
- return shape2Binding
34
27
  }
35
28
 
29
+ return shapesToBindings
30
+ }
31
+
32
+ export const bindingsIndex = (editor: Editor): Computed<TLBindingsIndex> => {
33
+ const { store } = editor
34
+ const bindingsHistory = store.query.filterHistory('binding')
35
+ const bindingsQuery = store.query.records('binding')
36
+
36
37
  return computed<TLBindingsIndex>('arrowBindingsIndex', (_lastValue, lastComputedEpoch) => {
37
38
  if (isUninitialized(_lastValue)) {
38
- return fromScratch()
39
+ return fromScratch(bindingsQuery)
39
40
  }
40
41
 
41
42
  const lastValue = _lastValue
@@ -43,7 +44,7 @@ export const bindingsIndex = (editor: Editor): Computed<TLBindingsIndex> => {
43
44
  const diff = bindingsHistory.getDiffSince(lastComputedEpoch)
44
45
 
45
46
  if (diff === RESET_VALUE) {
46
- return fromScratch()
47
+ return fromScratch(bindingsQuery)
47
48
  }
48
49
 
49
50
  let nextValue: TLBindingsIndex | undefined = undefined
@@ -1,45 +1,48 @@
1
- import { computed, isUninitialized, RESET_VALUE } from '@tldraw/state'
2
- import { RecordsDiff } from '@tldraw/store'
1
+ import { Computed, computed, isUninitialized, RESET_VALUE } from '@tldraw/state'
2
+ import { CollectionDiff, RecordsDiff } from '@tldraw/store'
3
3
  import { isShape, TLParentId, TLRecord, TLShape, TLShapeId, TLStore } from '@tldraw/tlschema'
4
4
  import { compact, sortByIndex } from '@tldraw/utils'
5
5
 
6
- type Parents2Children = Record<TLParentId, TLShapeId[]>
6
+ type ParentShapeIdsToChildShapeIds = Record<TLParentId, TLShapeId[]>
7
7
 
8
- export const parentsToChildren = (store: TLStore) => {
9
- const shapeIdsQuery = store.query.ids<'shape'>('shape')
10
- const shapeHistory = store.query.filterHistory('shape')
8
+ function fromScratch(
9
+ shapeIdsQuery: Computed<Set<TLShapeId>, CollectionDiff<TLShapeId>>,
10
+ store: TLStore
11
+ ) {
12
+ const result: ParentShapeIdsToChildShapeIds = {}
13
+ const shapeIds = shapeIdsQuery.get()
14
+ const shapes = Array(shapeIds.size) as TLShape[]
15
+ shapeIds.forEach((id) => shapes.push(store.get(id)!))
11
16
 
12
- function fromScratch() {
13
- const result: Parents2Children = {}
14
- const shapeIds = shapeIdsQuery.get()
15
- const shapes = Array(shapeIds.size) as TLShape[]
16
- shapeIds.forEach((id) => shapes.push(store.get(id)!))
17
+ // Sort the shapes by index
18
+ shapes.sort(sortByIndex)
17
19
 
18
- // Sort the shapes by index
19
- shapes.sort(sortByIndex)
20
+ // Populate the result object with an array for each parent.
21
+ shapes.forEach((shape) => {
22
+ if (!result[shape.parentId]) {
23
+ result[shape.parentId] = []
24
+ }
25
+ result[shape.parentId].push(shape.id)
26
+ })
20
27
 
21
- // Populate the result object with an array for each parent.
22
- shapes.forEach((shape) => {
23
- if (!result[shape.parentId]) {
24
- result[shape.parentId] = []
25
- }
26
- result[shape.parentId].push(shape.id)
27
- })
28
+ return result
29
+ }
28
30
 
29
- return result
30
- }
31
+ export const parentsToChildren = (store: TLStore) => {
32
+ const shapeIdsQuery = store.query.ids<'shape'>('shape')
33
+ const shapeHistory = store.query.filterHistory('shape')
31
34
 
32
- return computed<Parents2Children>(
35
+ return computed<ParentShapeIdsToChildShapeIds>(
33
36
  'parentsToChildrenWithIndexes',
34
37
  (lastValue, lastComputedEpoch) => {
35
38
  if (isUninitialized(lastValue)) {
36
- return fromScratch()
39
+ return fromScratch(shapeIdsQuery, store)
37
40
  }
38
41
 
39
42
  const diff = shapeHistory.getDiffSince(lastComputedEpoch)
40
43
 
41
44
  if (diff === RESET_VALUE) {
42
- return fromScratch()
45
+ return fromScratch(shapeIdsQuery, store)
43
46
  }
44
47
 
45
48
  if (diff.length === 0) return lastValue