@tldraw/editor 5.2.0-next.e2b8d10bf10e → 5.2.0
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/README.md +7 -1
- package/dist-cjs/index.d.ts +112 -53
- package/dist-cjs/index.js +7 -8
- package/dist-cjs/index.js.map +2 -2
- package/dist-cjs/lib/components/MenuClickCapture.js +8 -5
- package/dist-cjs/lib/components/MenuClickCapture.js.map +2 -2
- package/dist-cjs/lib/components/Shape.js +47 -1
- package/dist-cjs/lib/components/Shape.js.map +3 -3
- package/dist-cjs/lib/components/default-components/DefaultCanvas.js +1 -16
- package/dist-cjs/lib/components/default-components/DefaultCanvas.js.map +2 -2
- package/dist-cjs/lib/editor/Editor.js +97 -45
- package/dist-cjs/lib/editor/Editor.js.map +2 -2
- package/dist-cjs/lib/editor/managers/CollaboratorsManager/CollaboratorsManager.js +3 -3
- package/dist-cjs/lib/editor/managers/CollaboratorsManager/CollaboratorsManager.js.map +2 -2
- package/dist-cjs/lib/editor/managers/FontManager/FontManager.js +28 -4
- package/dist-cjs/lib/editor/managers/FontManager/FontManager.js.map +3 -3
- package/dist-cjs/lib/editor/managers/HistoryManager/HistoryManager.js +24 -2
- package/dist-cjs/lib/editor/managers/HistoryManager/HistoryManager.js.map +2 -2
- package/dist-cjs/lib/editor/managers/InputsManager/InputsManager.js +14 -3
- package/dist-cjs/lib/editor/managers/InputsManager/InputsManager.js.map +2 -2
- package/dist-cjs/lib/editor/managers/SpatialIndexManager/SpatialIndexManager.js +4 -2
- package/dist-cjs/lib/editor/managers/SpatialIndexManager/SpatialIndexManager.js.map +2 -2
- package/dist-cjs/lib/editor/managers/TextManager/TextManager.js +7 -3
- package/dist-cjs/lib/editor/managers/TextManager/TextManager.js.map +2 -2
- package/dist-cjs/lib/editor/managers/TickManager/TickManager.js +0 -1
- package/dist-cjs/lib/editor/managers/TickManager/TickManager.js.map +2 -2
- package/dist-cjs/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.js +15 -2
- package/dist-cjs/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.js.map +2 -2
- package/dist-cjs/lib/editor/overlays/strokeShapeIndicators.js +79 -0
- package/dist-cjs/lib/editor/overlays/strokeShapeIndicators.js.map +7 -0
- package/dist-cjs/lib/editor/shapes/ShapeUtil.js.map +2 -2
- package/dist-cjs/lib/editor/tools/BaseBoxShapeTool/children/Pointing.js +4 -0
- package/dist-cjs/lib/editor/tools/BaseBoxShapeTool/children/Pointing.js.map +2 -2
- package/dist-cjs/lib/{hooks/useTransform.js → editor/tools/cancelShapeCreationOnLongPress.js} +8 -23
- package/dist-cjs/lib/editor/tools/cancelShapeCreationOnLongPress.js.map +7 -0
- package/dist-cjs/lib/editor/types/event-types.js.map +2 -2
- package/dist-cjs/lib/globals/environment.js +6 -1
- package/dist-cjs/lib/globals/environment.js.map +2 -2
- package/dist-cjs/lib/hooks/EditorComponentsContext.js.map +2 -2
- package/dist-cjs/lib/hooks/useCanvasEvents.js +17 -8
- package/dist-cjs/lib/hooks/useCanvasEvents.js.map +2 -2
- package/dist-cjs/lib/hooks/useEditorComponents.js +0 -1
- package/dist-cjs/lib/hooks/useEditorComponents.js.map +2 -2
- package/dist-cjs/lib/hooks/usePresence.js.map +2 -2
- package/dist-cjs/lib/hooks/useScreenBounds.js +1 -0
- package/dist-cjs/lib/hooks/useScreenBounds.js.map +2 -2
- package/dist-cjs/lib/license/LicenseManager.js +5 -1
- package/dist-cjs/lib/license/LicenseManager.js.map +2 -2
- package/dist-cjs/lib/primitives/Vec.js +3 -2
- package/dist-cjs/lib/primitives/Vec.js.map +2 -2
- package/dist-cjs/lib/utils/dom.js +12 -0
- package/dist-cjs/lib/utils/dom.js.map +2 -2
- package/dist-cjs/lib/utils/getPointerInfo.js +2 -1
- package/dist-cjs/lib/utils/getPointerInfo.js.map +2 -2
- package/dist-cjs/lib/utils/pointer.js +37 -0
- package/dist-cjs/lib/utils/pointer.js.map +7 -0
- package/dist-cjs/version.js +3 -3
- package/dist-cjs/version.js.map +1 -1
- package/dist-esm/index.d.mts +112 -53
- package/dist-esm/index.mjs +9 -13
- package/dist-esm/index.mjs.map +2 -2
- package/dist-esm/lib/components/MenuClickCapture.mjs +8 -5
- package/dist-esm/lib/components/MenuClickCapture.mjs.map +2 -2
- package/dist-esm/lib/components/Shape.mjs +48 -2
- package/dist-esm/lib/components/Shape.mjs.map +3 -3
- package/dist-esm/lib/components/default-components/DefaultCanvas.mjs +1 -16
- package/dist-esm/lib/components/default-components/DefaultCanvas.mjs.map +2 -2
- package/dist-esm/lib/editor/Editor.mjs +97 -45
- package/dist-esm/lib/editor/Editor.mjs.map +2 -2
- package/dist-esm/lib/editor/managers/CollaboratorsManager/CollaboratorsManager.mjs +3 -3
- package/dist-esm/lib/editor/managers/CollaboratorsManager/CollaboratorsManager.mjs.map +2 -2
- package/dist-esm/lib/editor/managers/FontManager/FontManager.mjs +28 -4
- package/dist-esm/lib/editor/managers/FontManager/FontManager.mjs.map +3 -3
- package/dist-esm/lib/editor/managers/HistoryManager/HistoryManager.mjs +24 -2
- package/dist-esm/lib/editor/managers/HistoryManager/HistoryManager.mjs.map +2 -2
- package/dist-esm/lib/editor/managers/InputsManager/InputsManager.mjs +14 -3
- package/dist-esm/lib/editor/managers/InputsManager/InputsManager.mjs.map +2 -2
- package/dist-esm/lib/editor/managers/SpatialIndexManager/SpatialIndexManager.mjs +4 -2
- package/dist-esm/lib/editor/managers/SpatialIndexManager/SpatialIndexManager.mjs.map +2 -2
- package/dist-esm/lib/editor/managers/TextManager/TextManager.mjs +7 -3
- package/dist-esm/lib/editor/managers/TextManager/TextManager.mjs.map +2 -2
- package/dist-esm/lib/editor/managers/TickManager/TickManager.mjs +0 -1
- package/dist-esm/lib/editor/managers/TickManager/TickManager.mjs.map +2 -2
- package/dist-esm/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.mjs +15 -2
- package/dist-esm/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.mjs.map +2 -2
- package/dist-esm/lib/editor/overlays/strokeShapeIndicators.mjs +59 -0
- package/dist-esm/lib/editor/overlays/strokeShapeIndicators.mjs.map +7 -0
- package/dist-esm/lib/editor/shapes/ShapeUtil.mjs.map +2 -2
- package/dist-esm/lib/editor/tools/BaseBoxShapeTool/children/Pointing.mjs +4 -0
- package/dist-esm/lib/editor/tools/BaseBoxShapeTool/children/Pointing.mjs.map +2 -2
- package/dist-esm/lib/editor/tools/cancelShapeCreationOnLongPress.mjs +8 -0
- package/dist-esm/lib/editor/tools/cancelShapeCreationOnLongPress.mjs.map +7 -0
- package/dist-esm/lib/editor/types/event-types.mjs.map +2 -2
- package/dist-esm/lib/globals/environment.mjs +6 -1
- package/dist-esm/lib/globals/environment.mjs.map +2 -2
- package/dist-esm/lib/hooks/EditorComponentsContext.mjs.map +2 -2
- package/dist-esm/lib/hooks/useCanvasEvents.mjs +17 -8
- package/dist-esm/lib/hooks/useCanvasEvents.mjs.map +2 -2
- package/dist-esm/lib/hooks/useEditorComponents.mjs +0 -1
- package/dist-esm/lib/hooks/useEditorComponents.mjs.map +2 -2
- package/dist-esm/lib/hooks/usePresence.mjs.map +2 -2
- package/dist-esm/lib/hooks/useScreenBounds.mjs +1 -0
- package/dist-esm/lib/hooks/useScreenBounds.mjs.map +2 -2
- package/dist-esm/lib/license/LicenseManager.mjs +5 -1
- package/dist-esm/lib/license/LicenseManager.mjs.map +2 -2
- package/dist-esm/lib/primitives/Vec.mjs +3 -2
- package/dist-esm/lib/primitives/Vec.mjs.map +2 -2
- package/dist-esm/lib/utils/dom.mjs +12 -0
- package/dist-esm/lib/utils/dom.mjs.map +2 -2
- package/dist-esm/lib/utils/getPointerInfo.mjs +2 -1
- package/dist-esm/lib/utils/getPointerInfo.mjs.map +2 -2
- package/dist-esm/lib/utils/pointer.mjs +17 -0
- package/dist-esm/lib/utils/pointer.mjs.map +7 -0
- package/dist-esm/version.mjs +3 -3
- package/dist-esm/version.mjs.map +1 -1
- package/editor.css +15 -3
- package/package.json +11 -8
- package/src/index.ts +4 -10
- package/src/lib/components/MenuClickCapture.tsx +8 -4
- package/src/lib/components/Shape.tsx +88 -1
- package/src/lib/components/default-components/DefaultCanvas.tsx +1 -17
- package/src/lib/editor/Editor.test.ts +36 -0
- package/src/lib/editor/Editor.ts +145 -55
- package/src/lib/editor/managers/CollaboratorsManager/CollaboratorsManager.test.ts +43 -16
- package/src/lib/editor/managers/CollaboratorsManager/CollaboratorsManager.ts +8 -5
- package/src/lib/editor/managers/FontManager/FontManager.test.ts +37 -10
- package/src/lib/editor/managers/FontManager/FontManager.ts +46 -1
- package/src/lib/editor/managers/HistoryManager/HistoryManager.test.ts +32 -0
- package/src/lib/editor/managers/HistoryManager/HistoryManager.ts +34 -4
- package/src/lib/editor/managers/InputsManager/InputsManager.test.ts +61 -0
- package/src/lib/editor/managers/InputsManager/InputsManager.ts +16 -4
- package/src/lib/editor/managers/SpatialIndexManager/SpatialIndexManager.ts +9 -2
- package/src/lib/editor/managers/TextManager/TextManager.test.ts +16 -14
- package/src/lib/editor/managers/TextManager/TextManager.ts +17 -2
- package/src/lib/editor/managers/TickManager/TickManager.test.ts +0 -40
- package/src/lib/editor/managers/TickManager/TickManager.ts +0 -1
- package/src/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.test.ts +12 -2
- package/src/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.ts +27 -2
- package/src/lib/editor/overlays/strokeShapeIndicators.ts +86 -0
- package/src/lib/editor/shapes/ShapeUtil.ts +41 -0
- package/src/lib/editor/tools/BaseBoxShapeTool/children/Pointing.ts +5 -0
- package/src/lib/editor/tools/cancelShapeCreationOnLongPress.ts +31 -0
- package/src/lib/editor/types/event-types.ts +7 -0
- package/src/lib/globals/environment.ts +7 -0
- package/src/lib/hooks/EditorComponentsContext.tsx +0 -2
- package/src/lib/hooks/useCanvasEvents.ts +24 -12
- package/src/lib/hooks/useEditorComponents.tsx +0 -1
- package/src/lib/hooks/usePresence.ts +2 -2
- package/src/lib/hooks/useScreenBounds.ts +3 -0
- package/src/lib/license/LicenseManager.test.ts +24 -16
- package/src/lib/license/LicenseManager.ts +8 -2
- package/src/lib/primitives/Vec.ts +5 -2
- package/src/lib/test/ContentElementSlot.test.tsx +201 -0
- package/src/lib/utils/dom.ts +34 -0
- package/src/lib/utils/getPointerInfo.ts +2 -1
- package/src/lib/utils/pointer.test.ts +76 -0
- package/src/lib/utils/pointer.ts +42 -0
- package/src/version.ts +3 -3
- package/dist-cjs/lib/components/default-components/DefaultSelectionBackground.js +0 -49
- package/dist-cjs/lib/components/default-components/DefaultSelectionBackground.js.map +0 -7
- package/dist-cjs/lib/editor/overlays/ShapeIndicatorOverlayUtil.js +0 -161
- package/dist-cjs/lib/editor/overlays/ShapeIndicatorOverlayUtil.js.map +0 -7
- package/dist-cjs/lib/hooks/useTransform.js.map +0 -7
- package/dist-esm/lib/components/default-components/DefaultSelectionBackground.mjs +0 -19
- package/dist-esm/lib/components/default-components/DefaultSelectionBackground.mjs.map +0 -7
- package/dist-esm/lib/editor/overlays/ShapeIndicatorOverlayUtil.mjs +0 -141
- package/dist-esm/lib/editor/overlays/ShapeIndicatorOverlayUtil.mjs.map +0 -7
- package/dist-esm/lib/hooks/useTransform.mjs +0 -23
- package/dist-esm/lib/hooks/useTransform.mjs.map +0 -7
- package/src/lib/components/default-components/DefaultSelectionBackground.tsx +0 -25
- package/src/lib/editor/overlays/ShapeIndicatorOverlayUtil.ts +0 -216
- package/src/lib/hooks/useTransform.ts +0 -30
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/lib/editor/managers/SpatialIndexManager/SpatialIndexManager.ts"],
|
|
4
|
-
"sourcesContent": ["import { Computed, RESET_VALUE, computed, isUninitialized } from '@tldraw/state'\nimport type { RecordsDiff } from '@tldraw/store'\nimport { TLPageId, TLShape, TLShapeId, isShape } from '@tldraw/tlschema'\nimport type { TLRecord } from '@tldraw/tlschema'\nimport { objectMapValues } from '@tldraw/utils'\nimport { Box } from '../../../primitives/Box'\nimport type { Editor } from '../../Editor'\nimport { RBushIndex, type SpatialElement } from './RBushIndex'\n\n/**\n * Manages spatial indexing for efficient shape location queries.\n *\n * Uses an R-tree (via RBush) to enable O(log n) spatial queries instead of O(n) iteration.\n * Handles shapes with computed bounds (arrows, groups, custom shapes) by checking all shapes'\n * bounds on each update using the reactive bounds cache.\n *\n * Key features:\n * - Incremental updates using filterHistory pattern\n * - Leverages existing bounds cache reactivity for dependency tracking\n * - Works with any custom shape type with computed bounds\n * - Per-page index (rebuilds on page change)\n * - Optimized for viewport culling queries\n *\n * @internal\n */\nexport class SpatialIndexManager {\n\tprivate rbush: RBushIndex\n\tprivate spatialIndexComputed: Computed<number>\n\tprivate lastPageId: TLPageId | null = null\n\n\t// Bumps only when the rbush content may have changed. Consumers subscribe\n\t// via the computed; a stable epoch lets prop-only diffs skip downstream\n\t// invalidations.\n\tprivate _boundsEpoch = 0\n\n\tconstructor(public readonly editor: Editor) {\n\t\tthis.rbush = new RBushIndex()\n\t\tthis.spatialIndexComputed = this.createSpatialIndexComputed()\n\t}\n\n\tprivate rebuildAndBumpEpoch(): number {\n\t\tthis.buildFromScratch()\n\t\tthis._boundsEpoch++\n\t\treturn this._boundsEpoch\n\t}\n\n\tprivate createSpatialIndexComputed() {\n\t\tconst shapeHistory = this.editor.store.query.filterHistory('shape')\n\n\t\treturn computed<number>('spatialIndex', (_prevValue, lastComputedEpoch) => {\n\t\t\tif (isUninitialized(_prevValue)) {\n\t\t\t\treturn this.rebuildAndBumpEpoch()\n\t\t\t}\n\n\t\t\tconst shapeDiff = shapeHistory.getDiffSince(lastComputedEpoch)\n\n\t\t\tif (shapeDiff === RESET_VALUE) {\n\t\t\t\treturn this.rebuildAndBumpEpoch()\n\t\t\t}\n\n\t\t\tconst currentPageId = this.editor.getCurrentPageId()\n\t\t\tif (this.lastPageId !== currentPageId) {\n\t\t\t\treturn this.rebuildAndBumpEpoch()\n\t\t\t}\n\n\t\t\tif (shapeDiff.length === 0) return this._boundsEpoch\n\n\t\t\tif (this.processIncrementalUpdate(shapeDiff)) {\n\t\t\t\tthis._boundsEpoch++\n\t\t\t}\n\t\t\treturn this._boundsEpoch\n\t\t})\n\t}\n\n\tprivate buildFromScratch(): void {\n\t\tthis.rbush.clear()\n\t\tthis.lastPageId = this.editor.getCurrentPageId()\n\n\t\tconst elements: SpatialElement[] = []\n\t\tfor (const shape of this.editor.getCurrentPageShapes()) {\n\t\t\tconst bounds = this.editor.getShapePageBounds(shape.id)\n\t\t\tif (bounds && bounds.isValid()) {\n\t\t\t\telements.push({\n\t\t\t\t\tminX: bounds.minX,\n\t\t\t\t\tminY: bounds.minY,\n\t\t\t\t\tmaxX: bounds.maxX,\n\t\t\t\t\tmaxY: bounds.maxY,\n\t\t\t\t\tid: shape.id,\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\n\t\t// Bulk load for efficiency\n\t\tthis.rbush.bulkLoad(elements)\n\t}\n\n\tprivate processIncrementalUpdate(shapeDiff: RecordsDiff<TLRecord>[]): boolean {\n\t\tconst processedShapeIds = new Set<TLShapeId>()\n\t\tlet changed = false\n\n\t\t// Step 1: apply diff entries directly. `changed` flips only on real\n\t\t// rbush mutations, so prop-only updates and no-op removes (e.g. shapes\n\t\t// from other pages, or never-indexed shapes with invalid bounds) don't\n\t\t// bump the epoch.\n\t\tfor (const changes of shapeDiff) {\n\t\t\tfor (const shape of objectMapValues(changes.added) as TLShape[]) {\n\t\t\t\tif (isShape(shape) && this.editor.getAncestorPageId(shape) === this.lastPageId) {\n\t\t\t\t\tconst bounds = this.editor.getShapePageBounds(shape.id)\n\t\t\t\t\tif (bounds && bounds.isValid()) {\n\t\t\t\t\t\tthis.rbush.upsert(shape.id, bounds)\n\t\t\t\t\t\tchanged = true\n\t\t\t\t\t}\n\t\t\t\t\tprocessedShapeIds.add(shape.id)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tfor (const shape of objectMapValues(changes.removed) as TLShape[]) {\n\t\t\t\tif (isShape(shape)) {\n\t\t\t\t\tif (this.rbush.has(shape.id)) {\n\t\t\t\t\t\tthis.rbush.remove(shape.id)\n\t\t\t\t\t\tchanged = true\n\t\t\t\t\t}\n\t\t\t\t\tprocessedShapeIds.add(shape.id)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tfor (const [, to] of objectMapValues(changes.updated) as [TLShape, TLShape][]) {\n\t\t\t\tif (!isShape(to)) continue\n\t\t\t\tprocessedShapeIds.add(to.id)\n\n\t\t\t\tconst isOnPage = this.editor.getAncestorPageId(to) === this.lastPageId\n\n\t\t\t\tif (isOnPage) {\n\t\t\t\t\tconst bounds = this.editor.getShapePageBounds(to.id)\n\t\t\t\t\tif (bounds && bounds.isValid()) {\n\t\t\t\t\t\tconst indexedElement = this.rbush.getElement(to.id)\n\t\t\t\t\t\tif (!this.areBoundsEqualToSpatialElement(bounds, indexedElement)) {\n\t\t\t\t\t\t\tthis.rbush.upsert(to.id, bounds)\n\t\t\t\t\t\t\tchanged = true\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (this.rbush.has(to.id)) {\n\t\t\t\t\t\tthis.rbush.remove(to.id)\n\t\t\t\t\t\tchanged = true\n\t\t\t\t\t}\n\t\t\t\t} else if (this.rbush.has(to.id)) {\n\t\t\t\t\tthis.rbush.remove(to.id)\n\t\t\t\t\tchanged = true\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Step 2: must always run. Diff entries can dirty derived bounds \u2014\n\t\t// arrows bound to moved shapes, groups with moved children \u2014 without\n\t\t// touching any record visited in step 1. Also catches outline-only\n\t\t// changes (e.g. geo rectangle\u2192ellipse at the same w/h) that shift a\n\t\t// bound arrow's intersection points: step 1 sees the geo's\n\t\t// axis-aligned bounds unchanged and skips, but the dependent arrow's\n\t\t// bounds have moved.\n\t\t//\n\t\t// Iterating the rbush's element map directly avoids allocating a\n\t\t// shape-id array per pointer move. Mutation here is limited to\n\t\t// upserts of existing keys and deletions, both safe during Map\n\t\t// iteration.\n\t\tfor (const [shapeId, indexedElement] of this.rbush.entries()) {\n\t\t\tif (processedShapeIds.has(shapeId)) continue\n\n\t\t\tconst currentBounds = this.editor.getShapePageBounds(shapeId)\n\t\t\tif (this.areBoundsEqualToSpatialElement(currentBounds, indexedElement)) continue\n\n\t\t\tif (currentBounds && currentBounds.isValid()) {\n\t\t\t\tthis.rbush.upsert(shapeId, currentBounds)\n\t\t\t} else {\n\t\t\t\tthis.rbush.remove(shapeId)\n\t\t\t}\n\t\t\tchanged = true\n\t\t}\n\n\t\treturn changed\n\t}\n\n\tprivate areBoundsEqualToSpatialElement(\n\t\ta: Box | undefined,\n\t\tb: SpatialElement | undefined\n\t): boolean {\n\t\tif (!a && !b) return true\n\t\tif (!a || !b) return false\n\t\treturn a.minX === b.minX && a.minY === b.minY && a.maxX === b.maxX && a.maxY === b.maxY\n\t}\n\n\t/**\n\t * Get shape IDs within the given bounds.\n\t * Optimized for viewport culling queries.\n\t *\n\t * Note: Results are unordered. If you need z-order, combine with sorted shapes:\n\t * ```ts\n\t * const candidates = editor.spatialIndex.getShapeIdsInsideBounds(bounds)\n\t * const sorted = editor.getCurrentPageShapesSorted().filter(s => candidates.has(s.id))\n\t * ```\n\t *\n\t * @param bounds - The bounds to search within\n\t * @returns Unordered set of shape IDs within the bounds\n\t *\n\t * @public\n\t */\n\tgetShapeIdsInsideBounds(bounds: Box): Set<TLShapeId> {\n\t\tthis.spatialIndexComputed.get()\n\t\treturn this.rbush.search(bounds)\n\t}\n\n\t/**\n\t * Get shape IDs at a point (with optional margin).\n\t * Creates a small bounding box around the point and searches the spatial index.\n\t *\n\t * Note: Results are unordered. If you need z-order, combine with sorted shapes:\n\t * ```ts\n\t * const candidates = editor.spatialIndex.getShapeIdsAtPoint(point, margin)\n\t * const sorted = editor.getCurrentPageShapesSorted().filter(s => candidates.has(s.id))\n\t * ```\n\t *\n\t * @param point - The point to search at\n\t * @param margin - The margin around the point to search (default: 0)\n\t * @returns Unordered set of shape IDs that could potentially contain the point\n\t *\n\t * @public\n\t */\n\tgetShapeIdsAtPoint(point: { x: number; y: number }, margin = 0): Set<TLShapeId> {\n\t\tthis.spatialIndexComputed.get()\n\t\treturn this.rbush.search(new Box(point.x - margin, point.y - margin, margin * 2, margin * 2))\n\t}\n\n\t/**\n\t * Dispose of the spatial index manager.\n\t * Clears the R-tree to prevent memory leaks.\n\t *\n\t * @public\n\t */\n\tdispose(): void {\n\t\tthis.rbush.dispose()\n\t\tthis.lastPageId = null\n\t}\n}\n"],
|
|
5
|
-
"mappings": "AAAA,SAAmB,aAAa,UAAU,uBAAuB;AAEjE,SAAuC,eAAe;AAEtD,SAAS,uBAAuB;AAChC,SAAS,WAAW;AAEpB,SAAS,kBAAuC;AAkBzC,MAAM,oBAAoB;AAAA,EAUhC,YAA4B,QAAgB;AAAhB;AAC3B,SAAK,QAAQ,IAAI,WAAW;AAC5B,SAAK,uBAAuB,KAAK,2BAA2B;AAAA,EAC7D;AAAA,EAH4B;AAAA,EATpB;AAAA,EACA;AAAA,EACA,aAA8B;AAAA;AAAA;AAAA;AAAA,EAK9B,eAAe;AAAA,EAOf,sBAA8B;AACrC,SAAK,iBAAiB;AACtB,SAAK;AACL,WAAO,KAAK;AAAA,EACb;AAAA,EAEQ,6BAA6B;AACpC,UAAM,eAAe,KAAK,OAAO,MAAM,MAAM,cAAc,OAAO;
|
|
4
|
+
"sourcesContent": ["import { Computed, RESET_VALUE, computed, isUninitialized } from '@tldraw/state'\nimport type { RecordsDiff } from '@tldraw/store'\nimport { TLPageId, TLShape, TLShapeId, isShape } from '@tldraw/tlschema'\nimport type { TLRecord } from '@tldraw/tlschema'\nimport { objectMapValues } from '@tldraw/utils'\nimport { Box } from '../../../primitives/Box'\nimport type { Editor } from '../../Editor'\nimport { RBushIndex, type SpatialElement } from './RBushIndex'\n\n/**\n * Manages spatial indexing for efficient shape location queries.\n *\n * Uses an R-tree (via RBush) to enable O(log n) spatial queries instead of O(n) iteration.\n * Handles shapes with computed bounds (arrows, groups, custom shapes) by checking all shapes'\n * bounds on each update using the reactive bounds cache.\n *\n * Key features:\n * - Incremental updates using filterHistory pattern\n * - Leverages existing bounds cache reactivity for dependency tracking\n * - Works with any custom shape type with computed bounds\n * - Per-page index (rebuilds on page change)\n * - Optimized for viewport culling queries\n *\n * @internal\n */\nexport class SpatialIndexManager {\n\tprivate rbush: RBushIndex\n\tprivate spatialIndexComputed: Computed<number>\n\tprivate lastPageId: TLPageId | null = null\n\n\t// Bumps only when the rbush content may have changed. Consumers subscribe\n\t// via the computed; a stable epoch lets prop-only diffs skip downstream\n\t// invalidations.\n\tprivate _boundsEpoch = 0\n\n\tconstructor(public readonly editor: Editor) {\n\t\tthis.rbush = new RBushIndex()\n\t\tthis.spatialIndexComputed = this.createSpatialIndexComputed()\n\t}\n\n\tprivate rebuildAndBumpEpoch(): number {\n\t\tthis.buildFromScratch()\n\t\tthis._boundsEpoch++\n\t\treturn this._boundsEpoch\n\t}\n\n\tprivate createSpatialIndexComputed() {\n\t\tconst shapeHistory = this.editor.store.query.filterHistory('shape')\n\t\t// Binding changes can move a shape's derived bounds (e.g. creating or\n\t\t// deleting an arrow binding relocates the arrow's body) without\n\t\t// touching any shape record, so they must also invalidate the index.\n\t\tconst bindingHistory = this.editor.store.query.filterHistory('binding')\n\n\t\treturn computed<number>('spatialIndex', (_prevValue, lastComputedEpoch) => {\n\t\t\tif (isUninitialized(_prevValue)) {\n\t\t\t\treturn this.rebuildAndBumpEpoch()\n\t\t\t}\n\n\t\t\tconst shapeDiff = shapeHistory.getDiffSince(lastComputedEpoch)\n\t\t\tconst bindingDiff = bindingHistory.getDiffSince(lastComputedEpoch)\n\n\t\t\tif (shapeDiff === RESET_VALUE || bindingDiff === RESET_VALUE) {\n\t\t\t\treturn this.rebuildAndBumpEpoch()\n\t\t\t}\n\n\t\t\tconst currentPageId = this.editor.getCurrentPageId()\n\t\t\tif (this.lastPageId !== currentPageId) {\n\t\t\t\treturn this.rebuildAndBumpEpoch()\n\t\t\t}\n\n\t\t\tif (shapeDiff.length === 0 && bindingDiff.length === 0) return this._boundsEpoch\n\n\t\t\t// A binding-only diff passes an empty shape diff: step 1 is a no-op\n\t\t\t// and the step-2 sweep re-checks the indexed bounds of every shape.\n\t\t\tif (this.processIncrementalUpdate(shapeDiff)) {\n\t\t\t\tthis._boundsEpoch++\n\t\t\t}\n\t\t\treturn this._boundsEpoch\n\t\t})\n\t}\n\n\tprivate buildFromScratch(): void {\n\t\tthis.rbush.clear()\n\t\tthis.lastPageId = this.editor.getCurrentPageId()\n\n\t\tconst elements: SpatialElement[] = []\n\t\tfor (const shape of this.editor.getCurrentPageShapes()) {\n\t\t\tconst bounds = this.editor.getShapePageBounds(shape.id)\n\t\t\tif (bounds && bounds.isValid()) {\n\t\t\t\telements.push({\n\t\t\t\t\tminX: bounds.minX,\n\t\t\t\t\tminY: bounds.minY,\n\t\t\t\t\tmaxX: bounds.maxX,\n\t\t\t\t\tmaxY: bounds.maxY,\n\t\t\t\t\tid: shape.id,\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\n\t\t// Bulk load for efficiency\n\t\tthis.rbush.bulkLoad(elements)\n\t}\n\n\tprivate processIncrementalUpdate(shapeDiff: RecordsDiff<TLRecord>[]): boolean {\n\t\tconst processedShapeIds = new Set<TLShapeId>()\n\t\tlet changed = false\n\n\t\t// Step 1: apply diff entries directly. `changed` flips only on real\n\t\t// rbush mutations, so prop-only updates and no-op removes (e.g. shapes\n\t\t// from other pages, or never-indexed shapes with invalid bounds) don't\n\t\t// bump the epoch.\n\t\tfor (const changes of shapeDiff) {\n\t\t\tfor (const shape of objectMapValues(changes.added) as TLShape[]) {\n\t\t\t\tif (isShape(shape) && this.editor.getAncestorPageId(shape) === this.lastPageId) {\n\t\t\t\t\tconst bounds = this.editor.getShapePageBounds(shape.id)\n\t\t\t\t\tif (bounds && bounds.isValid()) {\n\t\t\t\t\t\tthis.rbush.upsert(shape.id, bounds)\n\t\t\t\t\t\tchanged = true\n\t\t\t\t\t}\n\t\t\t\t\tprocessedShapeIds.add(shape.id)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tfor (const shape of objectMapValues(changes.removed) as TLShape[]) {\n\t\t\t\tif (isShape(shape)) {\n\t\t\t\t\tif (this.rbush.has(shape.id)) {\n\t\t\t\t\t\tthis.rbush.remove(shape.id)\n\t\t\t\t\t\tchanged = true\n\t\t\t\t\t}\n\t\t\t\t\tprocessedShapeIds.add(shape.id)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tfor (const [, to] of objectMapValues(changes.updated) as [TLShape, TLShape][]) {\n\t\t\t\tif (!isShape(to)) continue\n\t\t\t\tprocessedShapeIds.add(to.id)\n\n\t\t\t\tconst isOnPage = this.editor.getAncestorPageId(to) === this.lastPageId\n\n\t\t\t\tif (isOnPage) {\n\t\t\t\t\tconst bounds = this.editor.getShapePageBounds(to.id)\n\t\t\t\t\tif (bounds && bounds.isValid()) {\n\t\t\t\t\t\tconst indexedElement = this.rbush.getElement(to.id)\n\t\t\t\t\t\tif (!this.areBoundsEqualToSpatialElement(bounds, indexedElement)) {\n\t\t\t\t\t\t\tthis.rbush.upsert(to.id, bounds)\n\t\t\t\t\t\t\tchanged = true\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (this.rbush.has(to.id)) {\n\t\t\t\t\t\tthis.rbush.remove(to.id)\n\t\t\t\t\t\tchanged = true\n\t\t\t\t\t}\n\t\t\t\t} else if (this.rbush.has(to.id)) {\n\t\t\t\t\tthis.rbush.remove(to.id)\n\t\t\t\t\tchanged = true\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Step 2: must always run. Diff entries can dirty derived bounds \u2014\n\t\t// arrows bound to moved shapes, groups with moved children \u2014 without\n\t\t// touching any record visited in step 1. Also catches outline-only\n\t\t// changes (e.g. geo rectangle\u2192ellipse at the same w/h) that shift a\n\t\t// bound arrow's intersection points: step 1 sees the geo's\n\t\t// axis-aligned bounds unchanged and skips, but the dependent arrow's\n\t\t// bounds have moved.\n\t\t//\n\t\t// Iterating the rbush's element map directly avoids allocating a\n\t\t// shape-id array per pointer move. Mutation here is limited to\n\t\t// upserts of existing keys and deletions, both safe during Map\n\t\t// iteration.\n\t\tfor (const [shapeId, indexedElement] of this.rbush.entries()) {\n\t\t\tif (processedShapeIds.has(shapeId)) continue\n\n\t\t\tconst currentBounds = this.editor.getShapePageBounds(shapeId)\n\t\t\tif (this.areBoundsEqualToSpatialElement(currentBounds, indexedElement)) continue\n\n\t\t\tif (currentBounds && currentBounds.isValid()) {\n\t\t\t\tthis.rbush.upsert(shapeId, currentBounds)\n\t\t\t} else {\n\t\t\t\tthis.rbush.remove(shapeId)\n\t\t\t}\n\t\t\tchanged = true\n\t\t}\n\n\t\treturn changed\n\t}\n\n\tprivate areBoundsEqualToSpatialElement(\n\t\ta: Box | undefined,\n\t\tb: SpatialElement | undefined\n\t): boolean {\n\t\tif (!a && !b) return true\n\t\tif (!a || !b) return false\n\t\treturn a.minX === b.minX && a.minY === b.minY && a.maxX === b.maxX && a.maxY === b.maxY\n\t}\n\n\t/**\n\t * Get shape IDs within the given bounds.\n\t * Optimized for viewport culling queries.\n\t *\n\t * Note: Results are unordered. If you need z-order, combine with sorted shapes:\n\t * ```ts\n\t * const candidates = editor.spatialIndex.getShapeIdsInsideBounds(bounds)\n\t * const sorted = editor.getCurrentPageShapesSorted().filter(s => candidates.has(s.id))\n\t * ```\n\t *\n\t * @param bounds - The bounds to search within\n\t * @returns Unordered set of shape IDs within the bounds\n\t *\n\t * @public\n\t */\n\tgetShapeIdsInsideBounds(bounds: Box): Set<TLShapeId> {\n\t\tthis.spatialIndexComputed.get()\n\t\treturn this.rbush.search(bounds)\n\t}\n\n\t/**\n\t * Get shape IDs at a point (with optional margin).\n\t * Creates a small bounding box around the point and searches the spatial index.\n\t *\n\t * Note: Results are unordered. If you need z-order, combine with sorted shapes:\n\t * ```ts\n\t * const candidates = editor.spatialIndex.getShapeIdsAtPoint(point, margin)\n\t * const sorted = editor.getCurrentPageShapesSorted().filter(s => candidates.has(s.id))\n\t * ```\n\t *\n\t * @param point - The point to search at\n\t * @param margin - The margin around the point to search (default: 0)\n\t * @returns Unordered set of shape IDs that could potentially contain the point\n\t *\n\t * @public\n\t */\n\tgetShapeIdsAtPoint(point: { x: number; y: number }, margin = 0): Set<TLShapeId> {\n\t\tthis.spatialIndexComputed.get()\n\t\treturn this.rbush.search(new Box(point.x - margin, point.y - margin, margin * 2, margin * 2))\n\t}\n\n\t/**\n\t * Dispose of the spatial index manager.\n\t * Clears the R-tree to prevent memory leaks.\n\t *\n\t * @public\n\t */\n\tdispose(): void {\n\t\tthis.rbush.dispose()\n\t\tthis.lastPageId = null\n\t}\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import { objectMapKeys } from "@tldraw/utils";
|
|
2
|
+
function resolveLineHeightPx(fontSize, lineHeight) {
|
|
3
|
+
return Math.round(fontSize * lineHeight);
|
|
4
|
+
}
|
|
2
5
|
const fixNewLines = /\r?\n|\r/g;
|
|
3
6
|
function normalizeTextForDom(text) {
|
|
4
7
|
return text.replace(fixNewLines, "\n").split("\n").map((x) => x || " ").join("\n");
|
|
@@ -76,7 +79,7 @@ class TextManager {
|
|
|
76
79
|
"font-style": opts.fontStyle,
|
|
77
80
|
"font-weight": opts.fontWeight,
|
|
78
81
|
"font-size": opts.fontSize + "px",
|
|
79
|
-
"line-height": opts.lineHeight
|
|
82
|
+
"line-height": `${resolveLineHeightPx(opts.fontSize, opts.lineHeight)}px`,
|
|
80
83
|
padding: opts.padding,
|
|
81
84
|
"max-width": opts.maxWidth ? opts.maxWidth + "px" : void 0,
|
|
82
85
|
"min-width": opts.minWidth ? opts.minWidth + "px" : void 0,
|
|
@@ -247,7 +250,7 @@ class TextManager {
|
|
|
247
250
|
"font-style": opts.fontStyle,
|
|
248
251
|
"font-weight": opts.fontWeight,
|
|
249
252
|
"font-size": opts.fontSize + "px",
|
|
250
|
-
"line-height": opts.lineHeight
|
|
253
|
+
"line-height": `${resolveLineHeightPx(opts.fontSize, opts.lineHeight)}px`,
|
|
251
254
|
width: `${elementWidth}px`,
|
|
252
255
|
height: "min-content",
|
|
253
256
|
"text-align": textAlignmentsForLtr[opts.textAlign],
|
|
@@ -289,6 +292,7 @@ class TextManager {
|
|
|
289
292
|
}
|
|
290
293
|
}
|
|
291
294
|
export {
|
|
292
|
-
TextManager
|
|
295
|
+
TextManager,
|
|
296
|
+
resolveLineHeightPx
|
|
293
297
|
};
|
|
294
298
|
//# sourceMappingURL=TextManager.mjs.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/lib/editor/managers/TextManager/TextManager.ts"],
|
|
4
|
-
"sourcesContent": ["import { BoxModel, TLDefaultHorizontalAlignStyle } from '@tldraw/tlschema'\nimport { objectMapKeys } from '@tldraw/utils'\nimport type { Editor } from '../../Editor'\n\nconst fixNewLines = /\\r?\\n|\\r/g\n\nfunction normalizeTextForDom(text: string) {\n\treturn text\n\t\t.replace(fixNewLines, '\\n')\n\t\t.split('\\n')\n\t\t.map((x) => x || ' ')\n\t\t.join('\\n')\n}\n\nconst textAlignmentsForLtr = {\n\tstart: 'left',\n\t'start-legacy': 'left',\n\tmiddle: 'center',\n\t'middle-legacy': 'center',\n\tend: 'right',\n\t'end-legacy': 'right',\n}\n\ninterface PoolItem {\n\tel: HTMLDivElement\n\thtml: string\n\tappliedStyleKeys: string[]\n}\n\n/** @public */\nexport interface BatchMeasurementRequest {\n\thtml: string\n\topts: TLMeasureTextOpts\n}\n\n/** @public */\nexport type TLMeasuredTextSize = BoxModel & {\n\tscrollWidth: number\n}\n\n/** @public */\nexport interface TLMeasureTextOpts {\n\tfontStyle: string\n\tfontWeight: string\n\tfontFamily: string\n\tfontSize: number\n\t/** This must be a number, e.g. 1.35, not a pixel value. */\n\tlineHeight: number\n\t/**\n\t * When maxWidth is a number, the text will be wrapped to that maxWidth. When maxWidth\n\t * is null, the text will be measured without wrapping, but explicit line breaks and\n\t * space are preserved.\n\t */\n\tmaxWidth: null | number\n\tminWidth?: null | number\n\t// todo: make this a number so that it is consistent with other TLMeasureTextSpanOpts\n\tpadding: string\n\totherStyles?: Record<string, string>\n\tdisableOverflowWrapBreaking?: boolean\n\tmeasureScrollWidth?: boolean\n}\n\n/** @public */\nexport interface TLMeasureTextSpanOpts {\n\toverflow: 'wrap' | 'truncate-ellipsis' | 'truncate-clip'\n\twidth: number\n\theight: number\n\tpadding: number\n\tfontSize: number\n\tfontWeight: string\n\tfontFamily: string\n\tfontStyle: string\n\tlineHeight: number\n\ttextAlign: TLDefaultHorizontalAlignStyle\n\totherStyles?: Record<string, string>\n\tmeasureScrollWidth?: boolean\n}\n\nconst spaceCharacterRegex = /\\s/\n\nconst initialDefaultStyles = Object.freeze({\n\t'overflow-wrap': 'break-word',\n\t'word-break': 'auto',\n\twidth: null,\n\theight: null,\n\t'max-width': null,\n\t'min-width': null,\n})\n\n/** @public */\nexport class TextManager {\n\tprivate elm: HTMLDivElement\n\tprivate poolElms: PoolItem[] = []\n\n\tconstructor(public editor: Editor) {\n\t\tthis.elm = this.createMeasurementEl()\n\t\tthis.editor.getContainer().appendChild(this.elm)\n\t}\n\n\tprivate createMeasurementEl(): HTMLDivElement {\n\t\tconst elm = this.editor.getContainerDocument().createElement('div')\n\t\telm.classList.add('tl-text')\n\t\telm.classList.add('tl-text-measure')\n\t\telm.setAttribute('dir', 'auto')\n\t\telm.tabIndex = -1\n\t\tfor (const key of objectMapKeys(initialDefaultStyles)) {\n\t\t\telm.style.setProperty(key, initialDefaultStyles[key])\n\t\t}\n\n\t\treturn elm\n\t}\n\n\tprivate resetElementStyles(el: HTMLElement, appliedStyleKeys: string[]) {\n\t\tfor (const key of appliedStyleKeys) {\n\t\t\tif (key in initialDefaultStyles) {\n\t\t\t\tel.style.setProperty(key, initialDefaultStyles[key as keyof typeof initialDefaultStyles])\n\t\t\t} else {\n\t\t\t\tel.style.removeProperty(key)\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate setElementStyles(el: HTMLElement, styles: Record<string, string | undefined | null>) {\n\t\ttype StyleValue = string | null\n\t\ttype RestoreEntry = [prop: string, value: StyleValue]\n\n\t\tconst restore: RestoreEntry[] = []\n\n\t\tfor (const [key, nextValue] of Object.entries(styles)) {\n\t\t\tconst oldValue = el.style.getPropertyValue(key)\n\n\t\t\tif (typeof nextValue === 'string') {\n\t\t\t\tif (oldValue === nextValue) continue\n\t\t\t\trestore.push([key, oldValue || null])\n\t\t\t\tel.style.setProperty(key, nextValue)\n\t\t\t} else {\n\t\t\t\tif (!oldValue) continue\n\t\t\t\trestore.push([key, oldValue])\n\t\t\t\tel.style.removeProperty(key)\n\t\t\t}\n\t\t}\n\n\t\treturn () => {\n\t\t\tfor (const [key, value] of restore) {\n\t\t\t\tif (value === null || value === '') el.style.removeProperty(key)\n\t\t\t\telse el.style.setProperty(key, value)\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate getMeasureStyles(opts: TLMeasureTextOpts): Record<string, string | undefined> {\n\t\treturn {\n\t\t\t'font-family': opts.fontFamily,\n\t\t\t'font-style': opts.fontStyle,\n\t\t\t'font-weight': opts.fontWeight,\n\t\t\t'font-size': opts.fontSize + 'px',\n\t\t\t'line-height': opts.lineHeight.toString(),\n\t\t\tpadding: opts.padding,\n\t\t\t'max-width': opts.maxWidth ? opts.maxWidth + 'px' : undefined,\n\t\t\t'min-width': opts.minWidth ? opts.minWidth + 'px' : undefined,\n\t\t\t'overflow-wrap': opts.disableOverflowWrapBreaking ? 'normal' : 'break-word',\n\t\t\t...opts.otherStyles,\n\t\t}\n\t}\n\n\tdispose() {\n\t\tthis.elm.remove()\n\t\tfor (const { el } of this.poolElms) {\n\t\t\tel.remove()\n\t\t}\n\t\tthis.poolElms.length = 0\n\t}\n\n\tprivate ensurePoolSize(size: number) {\n\t\tif (this.poolElms.length >= size) return\n\n\t\tconst fragment = this.editor.getContainerDocument().createDocumentFragment()\n\t\twhile (this.poolElms.length < size) {\n\t\t\tconst el = this.createMeasurementEl()\n\t\t\tthis.poolElms.push({ el, html: '', appliedStyleKeys: [] })\n\t\t\tfragment.appendChild(el)\n\t\t}\n\t\tthis.editor.getContainer().appendChild(fragment)\n\t}\n\n\tprivate getPoolItem(index: number): PoolItem {\n\t\tthis.ensurePoolSize(index + 1)\n\t\treturn this.poolElms[index]\n\t}\n\n\tmeasureHtmlBatch(requests: BatchMeasurementRequest[]): TLMeasuredTextSize[] {\n\t\tif (requests.length === 0) return []\n\n\t\twhile (this.poolElms.length > requests.length) {\n\t\t\tconst { el } = this.poolElms.pop()!\n\t\t\tel.remove()\n\t\t}\n\n\t\tfor (let i = 0; i < requests.length; i++) {\n\t\t\tconst { html, opts } = requests[i]\n\t\t\tconst poolItem = this.getPoolItem(i)\n\n\t\t\tconst { el } = poolItem\n\t\t\tthis.resetElementStyles(el, poolItem.appliedStyleKeys)\n\t\t\tconst styles = this.getMeasureStyles(opts)\n\t\t\tthis.setElementStyles(el, styles)\n\t\t\tpoolItem.appliedStyleKeys = Object.keys(styles)\n\t\t\t// Skip innerHTML parsing if the content hasn't changed\n\t\t\tif (poolItem.html !== html) {\n\t\t\t\tel.innerHTML = html\n\t\t\t\tpoolItem.html = html\n\t\t\t}\n\t\t}\n\n\t\tconst results: TLMeasuredTextSize[] = []\n\t\tfor (let i = 0; i < requests.length; i++) {\n\t\t\tconst el = this.getPoolItem(i).el\n\t\t\tconst scrollWidth = requests[i].opts.measureScrollWidth ? el.scrollWidth : 0\n\t\t\tconst rect = el.getBoundingClientRect()\n\t\t\tresults.push({\n\t\t\t\tx: 0,\n\t\t\t\ty: 0,\n\t\t\t\tw: rect.width,\n\t\t\t\th: rect.height,\n\t\t\t\tscrollWidth,\n\t\t\t})\n\t\t}\n\n\t\treturn results\n\t}\n\n\tmeasureText(textToMeasure: string, opts: TLMeasureTextOpts): TLMeasuredTextSize {\n\t\tconst div = this.editor.getContainerDocument().createElement('div')\n\t\tdiv.textContent = normalizeTextForDom(textToMeasure)\n\t\treturn this.measureHtml(div.innerHTML, opts)\n\t}\n\n\tmeasureHtml(html: string, opts: TLMeasureTextOpts): TLMeasuredTextSize {\n\t\tconst { elm } = this\n\n\t\tconst restoreStyles = this.setElementStyles(elm, this.getMeasureStyles(opts))\n\n\t\ttry {\n\t\t\telm.innerHTML = html\n\n\t\t\tconst scrollWidth = opts.measureScrollWidth ? elm.scrollWidth : 0\n\t\t\tconst rect = elm.getBoundingClientRect()\n\n\t\t\treturn {\n\t\t\t\tx: 0,\n\t\t\t\ty: 0,\n\t\t\t\tw: rect.width,\n\t\t\t\th: rect.height,\n\t\t\t\tscrollWidth,\n\t\t\t}\n\t\t} finally {\n\t\t\trestoreStyles()\n\t\t}\n\t}\n\n\t/**\n\t * Given an html element, measure the position of each span of unbroken\n\t * word/white-space characters within any text nodes it contains.\n\t */\n\tmeasureElementTextNodeSpans(\n\t\telement: HTMLElement,\n\t\t{ shouldTruncateToFirstLine = false }: { shouldTruncateToFirstLine?: boolean } = {}\n\t): { spans: { box: BoxModel; text: string }[]; didTruncate: boolean } {\n\t\tconst spans = []\n\n\t\t// Measurements of individual spans are relative to the containing element\n\t\tconst elmBounds = element.getBoundingClientRect()\n\t\tconst offsetX = -elmBounds.left\n\t\tconst offsetY = -elmBounds.top\n\n\t\t// we measure by creating a range that spans each character in the elements text node\n\t\tconst range = new Range()\n\t\tconst textNode = element.childNodes[0]\n\t\tlet idx = 0\n\n\t\tlet currentSpan = null\n\t\tlet prevCharWasSpaceCharacter = null\n\t\tlet prevCharTop = 0\n\t\tlet prevCharLeftForRTLTest = 0\n\t\tlet didTruncate = false\n\t\tfor (const childNode of element.childNodes) {\n\t\t\tif (childNode.nodeType !== Node.TEXT_NODE) continue\n\n\t\t\tfor (const char of childNode.textContent ?? '') {\n\t\t\t\t// place the range around the characters we're interested in\n\t\t\t\trange.setStart(textNode, idx)\n\t\t\t\trange.setEnd(textNode, idx + char.length)\n\t\t\t\t// measure the range. some browsers return multiple rects for the\n\t\t\t\t// first char in a new line - one for the line break, and one for\n\t\t\t\t// the character itself. we're only interested in the character.\n\t\t\t\tconst rects = range.getClientRects()\n\t\t\t\tconst rect = rects[rects.length - 1]!\n\n\t\t\t\t// calculate the position of the character relative to the element\n\t\t\t\tconst top = rect.top + offsetY\n\t\t\t\tconst left = rect.left + offsetX\n\t\t\t\tconst right = rect.right + offsetX\n\t\t\t\tconst isRTL = left < prevCharLeftForRTLTest\n\n\t\t\t\tconst isSpaceCharacter = spaceCharacterRegex.test(char)\n\t\t\t\tif (\n\t\t\t\t\t// If we're at a word boundary...\n\t\t\t\t\tisSpaceCharacter !== prevCharWasSpaceCharacter ||\n\t\t\t\t\t// ...or we're on a different line...\n\t\t\t\t\ttop !== prevCharTop ||\n\t\t\t\t\t// ...or we're at the start of the text and haven't created a span yet...\n\t\t\t\t\t!currentSpan\n\t\t\t\t) {\n\t\t\t\t\t// ...then we're at a span boundary!\n\n\t\t\t\t\tif (currentSpan) {\n\t\t\t\t\t\t// if we're truncating to a single line & we just finished the first line, stop there\n\t\t\t\t\t\tif (shouldTruncateToFirstLine && top !== prevCharTop) {\n\t\t\t\t\t\t\tdidTruncate = true\n\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// otherwise add the span to the list ready to start a new one\n\t\t\t\t\t\tspans.push(currentSpan)\n\t\t\t\t\t}\n\n\t\t\t\t\t// start a new span\n\t\t\t\t\tcurrentSpan = {\n\t\t\t\t\t\tbox: { x: left, y: top, w: rect.width, h: rect.height },\n\t\t\t\t\t\ttext: char,\n\t\t\t\t\t}\n\t\t\t\t\tprevCharLeftForRTLTest = left\n\t\t\t\t} else {\n\t\t\t\t\t// Looks like we're in RTL mode, so we need to adjust the left position.\n\t\t\t\t\tif (isRTL) {\n\t\t\t\t\t\tcurrentSpan.box.x = left\n\t\t\t\t\t}\n\n\t\t\t\t\t// otherwise we just need to extend the current span with the next character\n\t\t\t\t\tcurrentSpan.box.w = isRTL ? currentSpan.box.w + rect.width : right - currentSpan.box.x\n\t\t\t\t\tcurrentSpan.text += char\n\t\t\t\t}\n\n\t\t\t\tif (char === '\\n') {\n\t\t\t\t\tprevCharLeftForRTLTest = 0\n\t\t\t\t}\n\n\t\t\t\tprevCharWasSpaceCharacter = isSpaceCharacter\n\t\t\t\tprevCharTop = top\n\t\t\t\tidx += char.length\n\t\t\t}\n\t\t}\n\n\t\t// Add the last span\n\t\tif (currentSpan) {\n\t\t\tspans.push(currentSpan)\n\t\t}\n\n\t\treturn { spans, didTruncate }\n\t}\n\n\t/**\n\t * Measure text into individual spans. Spans are created by rendering the\n\t * text, then dividing it up according to line breaks and word boundaries.\n\t *\n\t * It works by having the browser render the text, then measuring the\n\t * position of each character. You can use this to replicate the text-layout\n\t * algorithm of the current browser in e.g. an SVG export.\n\t */\n\tmeasureTextSpans(\n\t\ttextToMeasure: string,\n\t\topts: TLMeasureTextSpanOpts\n\t): { text: string; box: BoxModel }[] {\n\t\tif (textToMeasure === '') return []\n\n\t\tconst { elm } = this\n\n\t\tconst shouldTruncateToFirstLine =\n\t\t\topts.overflow === 'truncate-ellipsis' || opts.overflow === 'truncate-clip'\n\t\tconst elementWidth = Math.ceil(opts.width - opts.padding * 2)\n\t\tconst newStyles = {\n\t\t\t'font-family': opts.fontFamily,\n\t\t\t'font-style': opts.fontStyle,\n\t\t\t'font-weight': opts.fontWeight,\n\t\t\t'font-size': opts.fontSize + 'px',\n\t\t\t'line-height': opts.lineHeight.toString(),\n\t\t\twidth: `${elementWidth}px`,\n\t\t\theight: 'min-content',\n\t\t\t'text-align': textAlignmentsForLtr[opts.textAlign],\n\t\t\t'overflow-wrap': shouldTruncateToFirstLine ? 'anywhere' : 'break-word',\n\t\t\t'word-break': shouldTruncateToFirstLine ? 'break-all' : 'normal',\n\t\t\t...opts.otherStyles,\n\t\t}\n\t\tconst restoreStyles = this.setElementStyles(elm, newStyles)\n\n\t\ttry {\n\t\t\tconst normalizedText = normalizeTextForDom(textToMeasure)\n\n\t\t\t// Render the text into the measurement element:\n\t\t\telm.textContent = normalizedText\n\n\t\t\t// actually measure the text:\n\t\t\tconst { spans, didTruncate } = this.measureElementTextNodeSpans(elm, {\n\t\t\t\tshouldTruncateToFirstLine,\n\t\t\t})\n\n\t\t\tif (opts.overflow === 'truncate-ellipsis' && didTruncate) {\n\t\t\t\t// we need to measure the ellipsis to know how much space it takes up\n\t\t\t\telm.textContent = '\u2026'\n\t\t\t\tconst ellipsisWidth = Math.ceil(this.measureElementTextNodeSpans(elm).spans[0].box.w)\n\n\t\t\t\t// then, we need to subtract that space from the width we have and measure again:\n\t\t\t\telm.style.setProperty('width', `${elementWidth - ellipsisWidth}px`)\n\t\t\t\telm.textContent = normalizedText\n\t\t\t\tconst truncatedSpans = this.measureElementTextNodeSpans(elm, {\n\t\t\t\t\tshouldTruncateToFirstLine: true,\n\t\t\t\t}).spans\n\n\t\t\t\t// Finally, we add in our ellipsis at the end of the last span. We\n\t\t\t\t// have to do this after measuring, not before, because adding the\n\t\t\t\t// ellipsis changes how whitespace might be getting collapsed by the\n\t\t\t\t// browser.\n\t\t\t\tconst lastSpan = truncatedSpans[truncatedSpans.length - 1]!\n\t\t\t\ttruncatedSpans.push({\n\t\t\t\t\ttext: '\u2026',\n\t\t\t\t\tbox: {\n\t\t\t\t\t\tx: Math.min(lastSpan.box.x + lastSpan.box.w, opts.width - opts.padding - ellipsisWidth),\n\t\t\t\t\t\ty: lastSpan.box.y,\n\t\t\t\t\t\tw: ellipsisWidth,\n\t\t\t\t\t\th: lastSpan.box.h,\n\t\t\t\t\t},\n\t\t\t\t})\n\n\t\t\t\treturn truncatedSpans\n\t\t\t}\n\n\t\t\treturn spans\n\t\t} finally {\n\t\t\trestoreStyles()\n\t\t}\n\t}\n}\n"],
|
|
5
|
-
"mappings": "AACA,SAAS,qBAAqB;
|
|
4
|
+
"sourcesContent": ["import { BoxModel, TLDefaultHorizontalAlignStyle } from '@tldraw/tlschema'\nimport { objectMapKeys } from '@tldraw/utils'\nimport type { Editor } from '../../Editor'\n\n/**\n * The whole-pixel line-height for a given font size and tldraw's unitless line-height\n * multiplier. tldraw's theme stores line-height as a multiplier (e.g. 1.35); resolving it\n * to a whole pixel keeps line spacing identical across rendering engines, which otherwise\n * disagree on fractional line boxes (WebKit snaps them to whole pixels, Blink keeps the\n * fraction) and let multi-line text drift apart. Apply it everywhere line-height is used \u2014\n * measurement, on-canvas render, and export \u2014 so geometry and rendering agree.\n * See https://github.com/tldraw/tldraw/issues/8970.\n *\n * @public\n */\nexport function resolveLineHeightPx(fontSize: number, lineHeight: number): number {\n\treturn Math.round(fontSize * lineHeight)\n}\n\nconst fixNewLines = /\\r?\\n|\\r/g\n\nfunction normalizeTextForDom(text: string) {\n\treturn text\n\t\t.replace(fixNewLines, '\\n')\n\t\t.split('\\n')\n\t\t.map((x) => x || ' ')\n\t\t.join('\\n')\n}\n\nconst textAlignmentsForLtr = {\n\tstart: 'left',\n\t'start-legacy': 'left',\n\tmiddle: 'center',\n\t'middle-legacy': 'center',\n\tend: 'right',\n\t'end-legacy': 'right',\n}\n\ninterface PoolItem {\n\tel: HTMLDivElement\n\thtml: string\n\tappliedStyleKeys: string[]\n}\n\n/** @public */\nexport interface BatchMeasurementRequest {\n\thtml: string\n\topts: TLMeasureTextOpts\n}\n\n/** @public */\nexport type TLMeasuredTextSize = BoxModel & {\n\tscrollWidth: number\n}\n\n/** @public */\nexport interface TLMeasureTextOpts {\n\tfontStyle: string\n\tfontWeight: string\n\tfontFamily: string\n\tfontSize: number\n\t/** This must be a number, e.g. 1.35, not a pixel value. */\n\tlineHeight: number\n\t/**\n\t * When maxWidth is a number, the text will be wrapped to that maxWidth. When maxWidth\n\t * is null, the text will be measured without wrapping, but explicit line breaks and\n\t * space are preserved.\n\t */\n\tmaxWidth: null | number\n\tminWidth?: null | number\n\t// todo: make this a number so that it is consistent with other TLMeasureTextSpanOpts\n\tpadding: string\n\totherStyles?: Record<string, string>\n\tdisableOverflowWrapBreaking?: boolean\n\tmeasureScrollWidth?: boolean\n}\n\n/** @public */\nexport interface TLMeasureTextSpanOpts {\n\toverflow: 'wrap' | 'truncate-ellipsis' | 'truncate-clip'\n\twidth: number\n\theight: number\n\tpadding: number\n\tfontSize: number\n\tfontWeight: string\n\tfontFamily: string\n\tfontStyle: string\n\tlineHeight: number\n\ttextAlign: TLDefaultHorizontalAlignStyle\n\totherStyles?: Record<string, string>\n\tmeasureScrollWidth?: boolean\n}\n\nconst spaceCharacterRegex = /\\s/\n\nconst initialDefaultStyles = Object.freeze({\n\t'overflow-wrap': 'break-word',\n\t'word-break': 'auto',\n\twidth: null,\n\theight: null,\n\t'max-width': null,\n\t'min-width': null,\n})\n\n/** @public */\nexport class TextManager {\n\tprivate elm: HTMLDivElement\n\tprivate poolElms: PoolItem[] = []\n\n\tconstructor(public editor: Editor) {\n\t\tthis.elm = this.createMeasurementEl()\n\t\tthis.editor.getContainer().appendChild(this.elm)\n\t}\n\n\tprivate createMeasurementEl(): HTMLDivElement {\n\t\tconst elm = this.editor.getContainerDocument().createElement('div')\n\t\telm.classList.add('tl-text')\n\t\telm.classList.add('tl-text-measure')\n\t\telm.setAttribute('dir', 'auto')\n\t\telm.tabIndex = -1\n\t\tfor (const key of objectMapKeys(initialDefaultStyles)) {\n\t\t\telm.style.setProperty(key, initialDefaultStyles[key])\n\t\t}\n\n\t\treturn elm\n\t}\n\n\tprivate resetElementStyles(el: HTMLElement, appliedStyleKeys: string[]) {\n\t\tfor (const key of appliedStyleKeys) {\n\t\t\tif (key in initialDefaultStyles) {\n\t\t\t\tel.style.setProperty(key, initialDefaultStyles[key as keyof typeof initialDefaultStyles])\n\t\t\t} else {\n\t\t\t\tel.style.removeProperty(key)\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate setElementStyles(el: HTMLElement, styles: Record<string, string | undefined | null>) {\n\t\ttype StyleValue = string | null\n\t\ttype RestoreEntry = [prop: string, value: StyleValue]\n\n\t\tconst restore: RestoreEntry[] = []\n\n\t\tfor (const [key, nextValue] of Object.entries(styles)) {\n\t\t\tconst oldValue = el.style.getPropertyValue(key)\n\n\t\t\tif (typeof nextValue === 'string') {\n\t\t\t\tif (oldValue === nextValue) continue\n\t\t\t\trestore.push([key, oldValue || null])\n\t\t\t\tel.style.setProperty(key, nextValue)\n\t\t\t} else {\n\t\t\t\tif (!oldValue) continue\n\t\t\t\trestore.push([key, oldValue])\n\t\t\t\tel.style.removeProperty(key)\n\t\t\t}\n\t\t}\n\n\t\treturn () => {\n\t\t\tfor (const [key, value] of restore) {\n\t\t\t\tif (value === null || value === '') el.style.removeProperty(key)\n\t\t\t\telse el.style.setProperty(key, value)\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate getMeasureStyles(opts: TLMeasureTextOpts): Record<string, string | undefined> {\n\t\treturn {\n\t\t\t'font-family': opts.fontFamily,\n\t\t\t'font-style': opts.fontStyle,\n\t\t\t'font-weight': opts.fontWeight,\n\t\t\t'font-size': opts.fontSize + 'px',\n\t\t\t'line-height': `${resolveLineHeightPx(opts.fontSize, opts.lineHeight)}px`,\n\t\t\tpadding: opts.padding,\n\t\t\t'max-width': opts.maxWidth ? opts.maxWidth + 'px' : undefined,\n\t\t\t'min-width': opts.minWidth ? opts.minWidth + 'px' : undefined,\n\t\t\t'overflow-wrap': opts.disableOverflowWrapBreaking ? 'normal' : 'break-word',\n\t\t\t...opts.otherStyles,\n\t\t}\n\t}\n\n\tdispose() {\n\t\tthis.elm.remove()\n\t\tfor (const { el } of this.poolElms) {\n\t\t\tel.remove()\n\t\t}\n\t\tthis.poolElms.length = 0\n\t}\n\n\tprivate ensurePoolSize(size: number) {\n\t\tif (this.poolElms.length >= size) return\n\n\t\tconst fragment = this.editor.getContainerDocument().createDocumentFragment()\n\t\twhile (this.poolElms.length < size) {\n\t\t\tconst el = this.createMeasurementEl()\n\t\t\tthis.poolElms.push({ el, html: '', appliedStyleKeys: [] })\n\t\t\tfragment.appendChild(el)\n\t\t}\n\t\tthis.editor.getContainer().appendChild(fragment)\n\t}\n\n\tprivate getPoolItem(index: number): PoolItem {\n\t\tthis.ensurePoolSize(index + 1)\n\t\treturn this.poolElms[index]\n\t}\n\n\tmeasureHtmlBatch(requests: BatchMeasurementRequest[]): TLMeasuredTextSize[] {\n\t\tif (requests.length === 0) return []\n\n\t\twhile (this.poolElms.length > requests.length) {\n\t\t\tconst { el } = this.poolElms.pop()!\n\t\t\tel.remove()\n\t\t}\n\n\t\tfor (let i = 0; i < requests.length; i++) {\n\t\t\tconst { html, opts } = requests[i]\n\t\t\tconst poolItem = this.getPoolItem(i)\n\n\t\t\tconst { el } = poolItem\n\t\t\tthis.resetElementStyles(el, poolItem.appliedStyleKeys)\n\t\t\tconst styles = this.getMeasureStyles(opts)\n\t\t\tthis.setElementStyles(el, styles)\n\t\t\tpoolItem.appliedStyleKeys = Object.keys(styles)\n\t\t\t// Skip innerHTML parsing if the content hasn't changed\n\t\t\tif (poolItem.html !== html) {\n\t\t\t\tel.innerHTML = html\n\t\t\t\tpoolItem.html = html\n\t\t\t}\n\t\t}\n\n\t\tconst results: TLMeasuredTextSize[] = []\n\t\tfor (let i = 0; i < requests.length; i++) {\n\t\t\tconst el = this.getPoolItem(i).el\n\t\t\tconst scrollWidth = requests[i].opts.measureScrollWidth ? el.scrollWidth : 0\n\t\t\tconst rect = el.getBoundingClientRect()\n\t\t\tresults.push({\n\t\t\t\tx: 0,\n\t\t\t\ty: 0,\n\t\t\t\tw: rect.width,\n\t\t\t\th: rect.height,\n\t\t\t\tscrollWidth,\n\t\t\t})\n\t\t}\n\n\t\treturn results\n\t}\n\n\tmeasureText(textToMeasure: string, opts: TLMeasureTextOpts): TLMeasuredTextSize {\n\t\tconst div = this.editor.getContainerDocument().createElement('div')\n\t\tdiv.textContent = normalizeTextForDom(textToMeasure)\n\t\treturn this.measureHtml(div.innerHTML, opts)\n\t}\n\n\tmeasureHtml(html: string, opts: TLMeasureTextOpts): TLMeasuredTextSize {\n\t\tconst { elm } = this\n\n\t\tconst restoreStyles = this.setElementStyles(elm, this.getMeasureStyles(opts))\n\n\t\ttry {\n\t\t\telm.innerHTML = html\n\n\t\t\tconst scrollWidth = opts.measureScrollWidth ? elm.scrollWidth : 0\n\t\t\tconst rect = elm.getBoundingClientRect()\n\n\t\t\treturn {\n\t\t\t\tx: 0,\n\t\t\t\ty: 0,\n\t\t\t\tw: rect.width,\n\t\t\t\th: rect.height,\n\t\t\t\tscrollWidth,\n\t\t\t}\n\t\t} finally {\n\t\t\trestoreStyles()\n\t\t}\n\t}\n\n\t/**\n\t * Given an html element, measure the position of each span of unbroken\n\t * word/white-space characters within any text nodes it contains.\n\t */\n\tmeasureElementTextNodeSpans(\n\t\telement: HTMLElement,\n\t\t{ shouldTruncateToFirstLine = false }: { shouldTruncateToFirstLine?: boolean } = {}\n\t): { spans: { box: BoxModel; text: string }[]; didTruncate: boolean } {\n\t\tconst spans = []\n\n\t\t// Measurements of individual spans are relative to the containing element\n\t\tconst elmBounds = element.getBoundingClientRect()\n\t\tconst offsetX = -elmBounds.left\n\t\tconst offsetY = -elmBounds.top\n\n\t\t// we measure by creating a range that spans each character in the elements text node\n\t\tconst range = new Range()\n\t\tconst textNode = element.childNodes[0]\n\t\tlet idx = 0\n\n\t\tlet currentSpan = null\n\t\tlet prevCharWasSpaceCharacter = null\n\t\tlet prevCharTop = 0\n\t\tlet prevCharLeftForRTLTest = 0\n\t\tlet didTruncate = false\n\t\tfor (const childNode of element.childNodes) {\n\t\t\tif (childNode.nodeType !== Node.TEXT_NODE) continue\n\n\t\t\tfor (const char of childNode.textContent ?? '') {\n\t\t\t\t// place the range around the characters we're interested in\n\t\t\t\trange.setStart(textNode, idx)\n\t\t\t\trange.setEnd(textNode, idx + char.length)\n\t\t\t\t// measure the range. some browsers return multiple rects for the\n\t\t\t\t// first char in a new line - one for the line break, and one for\n\t\t\t\t// the character itself. we're only interested in the character.\n\t\t\t\tconst rects = range.getClientRects()\n\t\t\t\tconst rect = rects[rects.length - 1]!\n\n\t\t\t\t// calculate the position of the character relative to the element\n\t\t\t\tconst top = rect.top + offsetY\n\t\t\t\tconst left = rect.left + offsetX\n\t\t\t\tconst right = rect.right + offsetX\n\t\t\t\tconst isRTL = left < prevCharLeftForRTLTest\n\n\t\t\t\tconst isSpaceCharacter = spaceCharacterRegex.test(char)\n\t\t\t\tif (\n\t\t\t\t\t// If we're at a word boundary...\n\t\t\t\t\tisSpaceCharacter !== prevCharWasSpaceCharacter ||\n\t\t\t\t\t// ...or we're on a different line...\n\t\t\t\t\ttop !== prevCharTop ||\n\t\t\t\t\t// ...or we're at the start of the text and haven't created a span yet...\n\t\t\t\t\t!currentSpan\n\t\t\t\t) {\n\t\t\t\t\t// ...then we're at a span boundary!\n\n\t\t\t\t\tif (currentSpan) {\n\t\t\t\t\t\t// if we're truncating to a single line & we just finished the first line, stop there\n\t\t\t\t\t\tif (shouldTruncateToFirstLine && top !== prevCharTop) {\n\t\t\t\t\t\t\tdidTruncate = true\n\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// otherwise add the span to the list ready to start a new one\n\t\t\t\t\t\tspans.push(currentSpan)\n\t\t\t\t\t}\n\n\t\t\t\t\t// start a new span\n\t\t\t\t\tcurrentSpan = {\n\t\t\t\t\t\tbox: { x: left, y: top, w: rect.width, h: rect.height },\n\t\t\t\t\t\ttext: char,\n\t\t\t\t\t}\n\t\t\t\t\tprevCharLeftForRTLTest = left\n\t\t\t\t} else {\n\t\t\t\t\t// Looks like we're in RTL mode, so we need to adjust the left position.\n\t\t\t\t\tif (isRTL) {\n\t\t\t\t\t\tcurrentSpan.box.x = left\n\t\t\t\t\t}\n\n\t\t\t\t\t// otherwise we just need to extend the current span with the next character\n\t\t\t\t\tcurrentSpan.box.w = isRTL ? currentSpan.box.w + rect.width : right - currentSpan.box.x\n\t\t\t\t\tcurrentSpan.text += char\n\t\t\t\t}\n\n\t\t\t\tif (char === '\\n') {\n\t\t\t\t\tprevCharLeftForRTLTest = 0\n\t\t\t\t}\n\n\t\t\t\tprevCharWasSpaceCharacter = isSpaceCharacter\n\t\t\t\tprevCharTop = top\n\t\t\t\tidx += char.length\n\t\t\t}\n\t\t}\n\n\t\t// Add the last span\n\t\tif (currentSpan) {\n\t\t\tspans.push(currentSpan)\n\t\t}\n\n\t\treturn { spans, didTruncate }\n\t}\n\n\t/**\n\t * Measure text into individual spans. Spans are created by rendering the\n\t * text, then dividing it up according to line breaks and word boundaries.\n\t *\n\t * It works by having the browser render the text, then measuring the\n\t * position of each character. You can use this to replicate the text-layout\n\t * algorithm of the current browser in e.g. an SVG export.\n\t */\n\tmeasureTextSpans(\n\t\ttextToMeasure: string,\n\t\topts: TLMeasureTextSpanOpts\n\t): { text: string; box: BoxModel }[] {\n\t\tif (textToMeasure === '') return []\n\n\t\tconst { elm } = this\n\n\t\tconst shouldTruncateToFirstLine =\n\t\t\topts.overflow === 'truncate-ellipsis' || opts.overflow === 'truncate-clip'\n\t\tconst elementWidth = Math.ceil(opts.width - opts.padding * 2)\n\t\tconst newStyles = {\n\t\t\t'font-family': opts.fontFamily,\n\t\t\t'font-style': opts.fontStyle,\n\t\t\t'font-weight': opts.fontWeight,\n\t\t\t'font-size': opts.fontSize + 'px',\n\t\t\t'line-height': `${resolveLineHeightPx(opts.fontSize, opts.lineHeight)}px`,\n\t\t\twidth: `${elementWidth}px`,\n\t\t\theight: 'min-content',\n\t\t\t'text-align': textAlignmentsForLtr[opts.textAlign],\n\t\t\t'overflow-wrap': shouldTruncateToFirstLine ? 'anywhere' : 'break-word',\n\t\t\t'word-break': shouldTruncateToFirstLine ? 'break-all' : 'normal',\n\t\t\t...opts.otherStyles,\n\t\t}\n\t\tconst restoreStyles = this.setElementStyles(elm, newStyles)\n\n\t\ttry {\n\t\t\tconst normalizedText = normalizeTextForDom(textToMeasure)\n\n\t\t\t// Render the text into the measurement element:\n\t\t\telm.textContent = normalizedText\n\n\t\t\t// actually measure the text:\n\t\t\tconst { spans, didTruncate } = this.measureElementTextNodeSpans(elm, {\n\t\t\t\tshouldTruncateToFirstLine,\n\t\t\t})\n\n\t\t\tif (opts.overflow === 'truncate-ellipsis' && didTruncate) {\n\t\t\t\t// we need to measure the ellipsis to know how much space it takes up\n\t\t\t\telm.textContent = '\u2026'\n\t\t\t\tconst ellipsisWidth = Math.ceil(this.measureElementTextNodeSpans(elm).spans[0].box.w)\n\n\t\t\t\t// then, we need to subtract that space from the width we have and measure again:\n\t\t\t\telm.style.setProperty('width', `${elementWidth - ellipsisWidth}px`)\n\t\t\t\telm.textContent = normalizedText\n\t\t\t\tconst truncatedSpans = this.measureElementTextNodeSpans(elm, {\n\t\t\t\t\tshouldTruncateToFirstLine: true,\n\t\t\t\t}).spans\n\n\t\t\t\t// Finally, we add in our ellipsis at the end of the last span. We\n\t\t\t\t// have to do this after measuring, not before, because adding the\n\t\t\t\t// ellipsis changes how whitespace might be getting collapsed by the\n\t\t\t\t// browser.\n\t\t\t\tconst lastSpan = truncatedSpans[truncatedSpans.length - 1]!\n\t\t\t\ttruncatedSpans.push({\n\t\t\t\t\ttext: '\u2026',\n\t\t\t\t\tbox: {\n\t\t\t\t\t\tx: Math.min(lastSpan.box.x + lastSpan.box.w, opts.width - opts.padding - ellipsisWidth),\n\t\t\t\t\t\ty: lastSpan.box.y,\n\t\t\t\t\t\tw: ellipsisWidth,\n\t\t\t\t\t\th: lastSpan.box.h,\n\t\t\t\t\t},\n\t\t\t\t})\n\n\t\t\t\treturn truncatedSpans\n\t\t\t}\n\n\t\t\treturn spans\n\t\t} finally {\n\t\t\trestoreStyles()\n\t\t}\n\t}\n}\n"],
|
|
5
|
+
"mappings": "AACA,SAAS,qBAAqB;AAcvB,SAAS,oBAAoB,UAAkB,YAA4B;AACjF,SAAO,KAAK,MAAM,WAAW,UAAU;AACxC;AAEA,MAAM,cAAc;AAEpB,SAAS,oBAAoB,MAAc;AAC1C,SAAO,KACL,QAAQ,aAAa,IAAI,EACzB,MAAM,IAAI,EACV,IAAI,CAAC,MAAM,KAAK,GAAG,EACnB,KAAK,IAAI;AACZ;AAEA,MAAM,uBAAuB;AAAA,EAC5B,OAAO;AAAA,EACP,gBAAgB;AAAA,EAChB,QAAQ;AAAA,EACR,iBAAiB;AAAA,EACjB,KAAK;AAAA,EACL,cAAc;AACf;AAyDA,MAAM,sBAAsB;AAE5B,MAAM,uBAAuB,OAAO,OAAO;AAAA,EAC1C,iBAAiB;AAAA,EACjB,cAAc;AAAA,EACd,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,aAAa;AAAA,EACb,aAAa;AACd,CAAC;AAGM,MAAM,YAAY;AAAA,EAIxB,YAAmB,QAAgB;AAAhB;AAClB,SAAK,MAAM,KAAK,oBAAoB;AACpC,SAAK,OAAO,aAAa,EAAE,YAAY,KAAK,GAAG;AAAA,EAChD;AAAA,EAHmB;AAAA,EAHX;AAAA,EACA,WAAuB,CAAC;AAAA,EAOxB,sBAAsC;AAC7C,UAAM,MAAM,KAAK,OAAO,qBAAqB,EAAE,cAAc,KAAK;AAClE,QAAI,UAAU,IAAI,SAAS;AAC3B,QAAI,UAAU,IAAI,iBAAiB;AACnC,QAAI,aAAa,OAAO,MAAM;AAC9B,QAAI,WAAW;AACf,eAAW,OAAO,cAAc,oBAAoB,GAAG;AACtD,UAAI,MAAM,YAAY,KAAK,qBAAqB,GAAG,CAAC;AAAA,IACrD;AAEA,WAAO;AAAA,EACR;AAAA,EAEQ,mBAAmB,IAAiB,kBAA4B;AACvE,eAAW,OAAO,kBAAkB;AACnC,UAAI,OAAO,sBAAsB;AAChC,WAAG,MAAM,YAAY,KAAK,qBAAqB,GAAwC,CAAC;AAAA,MACzF,OAAO;AACN,WAAG,MAAM,eAAe,GAAG;AAAA,MAC5B;AAAA,IACD;AAAA,EACD;AAAA,EAEQ,iBAAiB,IAAiB,QAAmD;AAI5F,UAAM,UAA0B,CAAC;AAEjC,eAAW,CAAC,KAAK,SAAS,KAAK,OAAO,QAAQ,MAAM,GAAG;AACtD,YAAM,WAAW,GAAG,MAAM,iBAAiB,GAAG;AAE9C,UAAI,OAAO,cAAc,UAAU;AAClC,YAAI,aAAa,UAAW;AAC5B,gBAAQ,KAAK,CAAC,KAAK,YAAY,IAAI,CAAC;AACpC,WAAG,MAAM,YAAY,KAAK,SAAS;AAAA,MACpC,OAAO;AACN,YAAI,CAAC,SAAU;AACf,gBAAQ,KAAK,CAAC,KAAK,QAAQ,CAAC;AAC5B,WAAG,MAAM,eAAe,GAAG;AAAA,MAC5B;AAAA,IACD;AAEA,WAAO,MAAM;AACZ,iBAAW,CAAC,KAAK,KAAK,KAAK,SAAS;AACnC,YAAI,UAAU,QAAQ,UAAU,GAAI,IAAG,MAAM,eAAe,GAAG;AAAA,YAC1D,IAAG,MAAM,YAAY,KAAK,KAAK;AAAA,MACrC;AAAA,IACD;AAAA,EACD;AAAA,EAEQ,iBAAiB,MAA6D;AACrF,WAAO;AAAA,MACN,eAAe,KAAK;AAAA,MACpB,cAAc,KAAK;AAAA,MACnB,eAAe,KAAK;AAAA,MACpB,aAAa,KAAK,WAAW;AAAA,MAC7B,eAAe,GAAG,oBAAoB,KAAK,UAAU,KAAK,UAAU,CAAC;AAAA,MACrE,SAAS,KAAK;AAAA,MACd,aAAa,KAAK,WAAW,KAAK,WAAW,OAAO;AAAA,MACpD,aAAa,KAAK,WAAW,KAAK,WAAW,OAAO;AAAA,MACpD,iBAAiB,KAAK,8BAA8B,WAAW;AAAA,MAC/D,GAAG,KAAK;AAAA,IACT;AAAA,EACD;AAAA,EAEA,UAAU;AACT,SAAK,IAAI,OAAO;AAChB,eAAW,EAAE,GAAG,KAAK,KAAK,UAAU;AACnC,SAAG,OAAO;AAAA,IACX;AACA,SAAK,SAAS,SAAS;AAAA,EACxB;AAAA,EAEQ,eAAe,MAAc;AACpC,QAAI,KAAK,SAAS,UAAU,KAAM;AAElC,UAAM,WAAW,KAAK,OAAO,qBAAqB,EAAE,uBAAuB;AAC3E,WAAO,KAAK,SAAS,SAAS,MAAM;AACnC,YAAM,KAAK,KAAK,oBAAoB;AACpC,WAAK,SAAS,KAAK,EAAE,IAAI,MAAM,IAAI,kBAAkB,CAAC,EAAE,CAAC;AACzD,eAAS,YAAY,EAAE;AAAA,IACxB;AACA,SAAK,OAAO,aAAa,EAAE,YAAY,QAAQ;AAAA,EAChD;AAAA,EAEQ,YAAY,OAAyB;AAC5C,SAAK,eAAe,QAAQ,CAAC;AAC7B,WAAO,KAAK,SAAS,KAAK;AAAA,EAC3B;AAAA,EAEA,iBAAiB,UAA2D;AAC3E,QAAI,SAAS,WAAW,EAAG,QAAO,CAAC;AAEnC,WAAO,KAAK,SAAS,SAAS,SAAS,QAAQ;AAC9C,YAAM,EAAE,GAAG,IAAI,KAAK,SAAS,IAAI;AACjC,SAAG,OAAO;AAAA,IACX;AAEA,aAAS,IAAI,GAAG,IAAI,SAAS,QAAQ,KAAK;AACzC,YAAM,EAAE,MAAM,KAAK,IAAI,SAAS,CAAC;AACjC,YAAM,WAAW,KAAK,YAAY,CAAC;AAEnC,YAAM,EAAE,GAAG,IAAI;AACf,WAAK,mBAAmB,IAAI,SAAS,gBAAgB;AACrD,YAAM,SAAS,KAAK,iBAAiB,IAAI;AACzC,WAAK,iBAAiB,IAAI,MAAM;AAChC,eAAS,mBAAmB,OAAO,KAAK,MAAM;AAE9C,UAAI,SAAS,SAAS,MAAM;AAC3B,WAAG,YAAY;AACf,iBAAS,OAAO;AAAA,MACjB;AAAA,IACD;AAEA,UAAM,UAAgC,CAAC;AACvC,aAAS,IAAI,GAAG,IAAI,SAAS,QAAQ,KAAK;AACzC,YAAM,KAAK,KAAK,YAAY,CAAC,EAAE;AAC/B,YAAM,cAAc,SAAS,CAAC,EAAE,KAAK,qBAAqB,GAAG,cAAc;AAC3E,YAAM,OAAO,GAAG,sBAAsB;AACtC,cAAQ,KAAK;AAAA,QACZ,GAAG;AAAA,QACH,GAAG;AAAA,QACH,GAAG,KAAK;AAAA,QACR,GAAG,KAAK;AAAA,QACR;AAAA,MACD,CAAC;AAAA,IACF;AAEA,WAAO;AAAA,EACR;AAAA,EAEA,YAAY,eAAuB,MAA6C;AAC/E,UAAM,MAAM,KAAK,OAAO,qBAAqB,EAAE,cAAc,KAAK;AAClE,QAAI,cAAc,oBAAoB,aAAa;AACnD,WAAO,KAAK,YAAY,IAAI,WAAW,IAAI;AAAA,EAC5C;AAAA,EAEA,YAAY,MAAc,MAA6C;AACtE,UAAM,EAAE,IAAI,IAAI;AAEhB,UAAM,gBAAgB,KAAK,iBAAiB,KAAK,KAAK,iBAAiB,IAAI,CAAC;AAE5E,QAAI;AACH,UAAI,YAAY;AAEhB,YAAM,cAAc,KAAK,qBAAqB,IAAI,cAAc;AAChE,YAAM,OAAO,IAAI,sBAAsB;AAEvC,aAAO;AAAA,QACN,GAAG;AAAA,QACH,GAAG;AAAA,QACH,GAAG,KAAK;AAAA,QACR,GAAG,KAAK;AAAA,QACR;AAAA,MACD;AAAA,IACD,UAAE;AACD,oBAAc;AAAA,IACf;AAAA,EACD;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,4BACC,SACA,EAAE,4BAA4B,MAAM,IAA6C,CAAC,GACb;AACrE,UAAM,QAAQ,CAAC;AAGf,UAAM,YAAY,QAAQ,sBAAsB;AAChD,UAAM,UAAU,CAAC,UAAU;AAC3B,UAAM,UAAU,CAAC,UAAU;AAG3B,UAAM,QAAQ,IAAI,MAAM;AACxB,UAAM,WAAW,QAAQ,WAAW,CAAC;AACrC,QAAI,MAAM;AAEV,QAAI,cAAc;AAClB,QAAI,4BAA4B;AAChC,QAAI,cAAc;AAClB,QAAI,yBAAyB;AAC7B,QAAI,cAAc;AAClB,eAAW,aAAa,QAAQ,YAAY;AAC3C,UAAI,UAAU,aAAa,KAAK,UAAW;AAE3C,iBAAW,QAAQ,UAAU,eAAe,IAAI;AAE/C,cAAM,SAAS,UAAU,GAAG;AAC5B,cAAM,OAAO,UAAU,MAAM,KAAK,MAAM;AAIxC,cAAM,QAAQ,MAAM,eAAe;AACnC,cAAM,OAAO,MAAM,MAAM,SAAS,CAAC;AAGnC,cAAM,MAAM,KAAK,MAAM;AACvB,cAAM,OAAO,KAAK,OAAO;AACzB,cAAM,QAAQ,KAAK,QAAQ;AAC3B,cAAM,QAAQ,OAAO;AAErB,cAAM,mBAAmB,oBAAoB,KAAK,IAAI;AACtD;AAAA;AAAA,UAEC,qBAAqB;AAAA,UAErB,QAAQ;AAAA,UAER,CAAC;AAAA,UACA;AAGD,cAAI,aAAa;AAEhB,gBAAI,6BAA6B,QAAQ,aAAa;AACrD,4BAAc;AACd;AAAA,YACD;AAEA,kBAAM,KAAK,WAAW;AAAA,UACvB;AAGA,wBAAc;AAAA,YACb,KAAK,EAAE,GAAG,MAAM,GAAG,KAAK,GAAG,KAAK,OAAO,GAAG,KAAK,OAAO;AAAA,YACtD,MAAM;AAAA,UACP;AACA,mCAAyB;AAAA,QAC1B,OAAO;AAEN,cAAI,OAAO;AACV,wBAAY,IAAI,IAAI;AAAA,UACrB;AAGA,sBAAY,IAAI,IAAI,QAAQ,YAAY,IAAI,IAAI,KAAK,QAAQ,QAAQ,YAAY,IAAI;AACrF,sBAAY,QAAQ;AAAA,QACrB;AAEA,YAAI,SAAS,MAAM;AAClB,mCAAyB;AAAA,QAC1B;AAEA,oCAA4B;AAC5B,sBAAc;AACd,eAAO,KAAK;AAAA,MACb;AAAA,IACD;AAGA,QAAI,aAAa;AAChB,YAAM,KAAK,WAAW;AAAA,IACvB;AAEA,WAAO,EAAE,OAAO,YAAY;AAAA,EAC7B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,iBACC,eACA,MACoC;AACpC,QAAI,kBAAkB,GAAI,QAAO,CAAC;AAElC,UAAM,EAAE,IAAI,IAAI;AAEhB,UAAM,4BACL,KAAK,aAAa,uBAAuB,KAAK,aAAa;AAC5D,UAAM,eAAe,KAAK,KAAK,KAAK,QAAQ,KAAK,UAAU,CAAC;AAC5D,UAAM,YAAY;AAAA,MACjB,eAAe,KAAK;AAAA,MACpB,cAAc,KAAK;AAAA,MACnB,eAAe,KAAK;AAAA,MACpB,aAAa,KAAK,WAAW;AAAA,MAC7B,eAAe,GAAG,oBAAoB,KAAK,UAAU,KAAK,UAAU,CAAC;AAAA,MACrE,OAAO,GAAG,YAAY;AAAA,MACtB,QAAQ;AAAA,MACR,cAAc,qBAAqB,KAAK,SAAS;AAAA,MACjD,iBAAiB,4BAA4B,aAAa;AAAA,MAC1D,cAAc,4BAA4B,cAAc;AAAA,MACxD,GAAG,KAAK;AAAA,IACT;AACA,UAAM,gBAAgB,KAAK,iBAAiB,KAAK,SAAS;AAE1D,QAAI;AACH,YAAM,iBAAiB,oBAAoB,aAAa;AAGxD,UAAI,cAAc;AAGlB,YAAM,EAAE,OAAO,YAAY,IAAI,KAAK,4BAA4B,KAAK;AAAA,QACpE;AAAA,MACD,CAAC;AAED,UAAI,KAAK,aAAa,uBAAuB,aAAa;AAEzD,YAAI,cAAc;AAClB,cAAM,gBAAgB,KAAK,KAAK,KAAK,4BAA4B,GAAG,EAAE,MAAM,CAAC,EAAE,IAAI,CAAC;AAGpF,YAAI,MAAM,YAAY,SAAS,GAAG,eAAe,aAAa,IAAI;AAClE,YAAI,cAAc;AAClB,cAAM,iBAAiB,KAAK,4BAA4B,KAAK;AAAA,UAC5D,2BAA2B;AAAA,QAC5B,CAAC,EAAE;AAMH,cAAM,WAAW,eAAe,eAAe,SAAS,CAAC;AACzD,uBAAe,KAAK;AAAA,UACnB,MAAM;AAAA,UACN,KAAK;AAAA,YACJ,GAAG,KAAK,IAAI,SAAS,IAAI,IAAI,SAAS,IAAI,GAAG,KAAK,QAAQ,KAAK,UAAU,aAAa;AAAA,YACtF,GAAG,SAAS,IAAI;AAAA,YAChB,GAAG;AAAA,YACH,GAAG,SAAS,IAAI;AAAA,UACjB;AAAA,QACD,CAAC;AAED,eAAO;AAAA,MACR;AAEA,aAAO;AAAA,IACR,UAAE;AACD,oBAAc;AAAA,IACf;AAAA,EACD;AACD;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -40,7 +40,6 @@ class TickManager {
|
|
|
40
40
|
const now = Date.now();
|
|
41
41
|
const elapsed = now - this.now;
|
|
42
42
|
this.now = now;
|
|
43
|
-
this.editor.inputs.updatePointerVelocity(elapsed);
|
|
44
43
|
this.editor.emit("frame", elapsed);
|
|
45
44
|
this.editor.emit("tick", elapsed);
|
|
46
45
|
this.cancelRaf = throttleToNextFrame(this.tick);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/lib/editor/managers/TickManager/TickManager.ts"],
|
|
4
|
-
"sourcesContent": ["import { throttleToNextFrame as _throttleToNextFrame, bind } from '@tldraw/utils'\nimport type { Editor } from '../../Editor'\n\nconst throttleToNextFrame =\n\ttypeof process !== 'undefined' && process.env.NODE_ENV === 'test'\n\t\t? // At test time we should use actual raf and not throttle, because throttle was set up to evaluate immediately during tests, which causes stack overflow\n\t\t\t// for the tick manager since it sets up a raf loop.\n\t\t\tfunction mockThrottle(cb: any) {\n\t\t\t\t// eslint-disable-next-line no-restricted-globals\n\t\t\t\tconst frame = requestAnimationFrame(cb)\n\t\t\t\treturn () => cancelAnimationFrame(frame)\n\t\t\t}\n\t\t: _throttleToNextFrame\n\n/** @internal */\nexport class TickManager {\n\tconstructor(public editor: Editor) {\n\t\tthis.editor.disposables.add(this.dispose)\n\t\tthis.start()\n\t}\n\n\tcancelRaf?: null | (() => void)\n\tisPaused = true\n\tnow = 0\n\n\tstart() {\n\t\tthis.isPaused = false\n\t\tthis.cancelRaf?.()\n\t\tthis.cancelRaf = throttleToNextFrame(this.tick)\n\t\tthis.now = Date.now()\n\t}\n\n\t@bind\n\ttick() {\n\t\tif (this.isPaused) {\n\t\t\treturn\n\t\t}\n\n\t\tconst now = Date.now()\n\t\tconst elapsed = now - this.now\n\t\tthis.now = now\n\n\t\tthis.editor.
|
|
5
|
-
"mappings": ";;;;;;;;;;AAAA,SAAS,uBAAuB,sBAAsB,YAAY;AAGlE,MAAM,sBACL,OAAO,YAAY,eAAe,QAAQ,IAAI,aAAa;AAAA;AAAA;AAAA,EAGzD,SAAS,aAAa,IAAS;AAE9B,UAAM,QAAQ,sBAAsB,EAAE;AACtC,WAAO,MAAM,qBAAqB,KAAK;AAAA,EACxC;AAAA,IACC;AAGG,MAAM,YAAY;AAAA,EACxB,YAAmB,QAAgB;AAAhB;AAClB,SAAK,OAAO,YAAY,IAAI,KAAK,OAAO;AACxC,SAAK,MAAM;AAAA,EACZ;AAAA,EAHmB;AAAA,EAKnB;AAAA,EACA,WAAW;AAAA,EACX,MAAM;AAAA,EAEN,QAAQ;AACP,SAAK,WAAW;AAChB,SAAK,YAAY;AACjB,SAAK,YAAY,oBAAoB,KAAK,IAAI;AAC9C,SAAK,MAAM,KAAK,IAAI;AAAA,EACrB;AAAA,EAGA,OAAO;AACN,QAAI,KAAK,UAAU;AAClB;AAAA,IACD;AAEA,UAAM,MAAM,KAAK,IAAI;AACrB,UAAM,UAAU,MAAM,KAAK;AAC3B,SAAK,MAAM;AAEX,SAAK,OAAO,
|
|
4
|
+
"sourcesContent": ["import { throttleToNextFrame as _throttleToNextFrame, bind } from '@tldraw/utils'\nimport type { Editor } from '../../Editor'\n\nconst throttleToNextFrame =\n\ttypeof process !== 'undefined' && process.env.NODE_ENV === 'test'\n\t\t? // At test time we should use actual raf and not throttle, because throttle was set up to evaluate immediately during tests, which causes stack overflow\n\t\t\t// for the tick manager since it sets up a raf loop.\n\t\t\tfunction mockThrottle(cb: any) {\n\t\t\t\t// eslint-disable-next-line no-restricted-globals\n\t\t\t\tconst frame = requestAnimationFrame(cb)\n\t\t\t\treturn () => cancelAnimationFrame(frame)\n\t\t\t}\n\t\t: _throttleToNextFrame\n\n/** @internal */\nexport class TickManager {\n\tconstructor(public editor: Editor) {\n\t\tthis.editor.disposables.add(this.dispose)\n\t\tthis.start()\n\t}\n\n\tcancelRaf?: null | (() => void)\n\tisPaused = true\n\tnow = 0\n\n\tstart() {\n\t\tthis.isPaused = false\n\t\tthis.cancelRaf?.()\n\t\tthis.cancelRaf = throttleToNextFrame(this.tick)\n\t\tthis.now = Date.now()\n\t}\n\n\t@bind\n\ttick() {\n\t\tif (this.isPaused) {\n\t\t\treturn\n\t\t}\n\n\t\tconst now = Date.now()\n\t\tconst elapsed = now - this.now\n\t\tthis.now = now\n\n\t\tthis.editor.emit('frame', elapsed)\n\t\tthis.editor.emit('tick', elapsed)\n\t\tthis.cancelRaf = throttleToNextFrame(this.tick)\n\t}\n\n\t// Clear the listener\n\t@bind\n\tdispose() {\n\t\tthis.isPaused = true\n\n\t\tthis.cancelRaf?.()\n\t}\n}\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;AAAA,SAAS,uBAAuB,sBAAsB,YAAY;AAGlE,MAAM,sBACL,OAAO,YAAY,eAAe,QAAQ,IAAI,aAAa;AAAA;AAAA;AAAA,EAGzD,SAAS,aAAa,IAAS;AAE9B,UAAM,QAAQ,sBAAsB,EAAE;AACtC,WAAO,MAAM,qBAAqB,KAAK;AAAA,EACxC;AAAA,IACC;AAGG,MAAM,YAAY;AAAA,EACxB,YAAmB,QAAgB;AAAhB;AAClB,SAAK,OAAO,YAAY,IAAI,KAAK,OAAO;AACxC,SAAK,MAAM;AAAA,EACZ;AAAA,EAHmB;AAAA,EAKnB;AAAA,EACA,WAAW;AAAA,EACX,MAAM;AAAA,EAEN,QAAQ;AACP,SAAK,WAAW;AAChB,SAAK,YAAY;AACjB,SAAK,YAAY,oBAAoB,KAAK,IAAI;AAC9C,SAAK,MAAM,KAAK,IAAI;AAAA,EACrB;AAAA,EAGA,OAAO;AACN,QAAI,KAAK,UAAU;AAClB;AAAA,IACD;AAEA,UAAM,MAAM,KAAK,IAAI;AACrB,UAAM,UAAU,MAAM,KAAK;AAC3B,SAAK,MAAM;AAEX,SAAK,OAAO,KAAK,SAAS,OAAO;AACjC,SAAK,OAAO,KAAK,QAAQ,OAAO;AAChC,SAAK,YAAY,oBAAoB,KAAK,IAAI;AAAA,EAC/C;AAAA,EAIA,UAAU;AACT,SAAK,WAAW;AAEhB,SAAK,YAAY;AAAA,EAClB;AACD;AArBC;AAAA,EADC;AAAA,GAjBW,YAkBZ;AAgBA;AAAA,EADC;AAAA,GAjCW,YAkCZ;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -9,6 +9,7 @@ var __decorateClass = (decorators, target, key, kind) => {
|
|
|
9
9
|
return result;
|
|
10
10
|
};
|
|
11
11
|
import { atom, computed } from "@tldraw/state";
|
|
12
|
+
import { createUserId } from "@tldraw/tlschema";
|
|
12
13
|
import { defaultUserPreferences } from "../../../config/TLUserPreferences.mjs";
|
|
13
14
|
import { getGlobalWindow } from "../../../utils/dom.mjs";
|
|
14
15
|
class UserPreferencesManager {
|
|
@@ -45,7 +46,7 @@ class UserPreferencesManager {
|
|
|
45
46
|
}
|
|
46
47
|
getUserPreferences() {
|
|
47
48
|
return {
|
|
48
|
-
id: this.
|
|
49
|
+
id: this.getExternalId(),
|
|
49
50
|
name: this.getName(),
|
|
50
51
|
locale: this.getLocale(),
|
|
51
52
|
color: this.getColor(),
|
|
@@ -84,9 +85,15 @@ class UserPreferencesManager {
|
|
|
84
85
|
getAreKeyboardShortcutsEnabled() {
|
|
85
86
|
return this.user.userPreferences.get().areKeyboardShortcutsEnabled ?? defaultUserPreferences.areKeyboardShortcutsEnabled;
|
|
86
87
|
}
|
|
87
|
-
|
|
88
|
+
getExternalId() {
|
|
88
89
|
return this.user.userPreferences.get().id;
|
|
89
90
|
}
|
|
91
|
+
getId() {
|
|
92
|
+
return this.getExternalId();
|
|
93
|
+
}
|
|
94
|
+
getRecordId() {
|
|
95
|
+
return createUserId(this.getExternalId());
|
|
96
|
+
}
|
|
90
97
|
getName() {
|
|
91
98
|
return this.user.userPreferences.get().name?.trim() ?? defaultUserPreferences.name;
|
|
92
99
|
}
|
|
@@ -133,9 +140,15 @@ __decorateClass([
|
|
|
133
140
|
__decorateClass([
|
|
134
141
|
computed
|
|
135
142
|
], UserPreferencesManager.prototype, "getAreKeyboardShortcutsEnabled", 1);
|
|
143
|
+
__decorateClass([
|
|
144
|
+
computed
|
|
145
|
+
], UserPreferencesManager.prototype, "getExternalId", 1);
|
|
136
146
|
__decorateClass([
|
|
137
147
|
computed
|
|
138
148
|
], UserPreferencesManager.prototype, "getId", 1);
|
|
149
|
+
__decorateClass([
|
|
150
|
+
computed
|
|
151
|
+
], UserPreferencesManager.prototype, "getRecordId", 1);
|
|
139
152
|
__decorateClass([
|
|
140
153
|
computed
|
|
141
154
|
], UserPreferencesManager.prototype, "getName", 1);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/lib/editor/managers/UserPreferencesManager/UserPreferencesManager.ts"],
|
|
4
|
-
"sourcesContent": ["import { atom, computed } from '@tldraw/state'\nimport { TLCurrentUser } from '../../../config/createTLCurrentUser'\nimport { TLUserPreferences, defaultUserPreferences } from '../../../config/TLUserPreferences'\nimport { getGlobalWindow } from '../../../utils/dom'\n\n/** @public */\nexport class UserPreferencesManager {\n\tsystemColorScheme = atom<'dark' | 'light'>('systemColorScheme', 'light')\n\tdisposables = new Set<() => void>()\n\tdispose() {\n\t\tthis.disposables.forEach((d) => d())\n\t}\n\tconstructor(\n\t\tprivate readonly user: TLCurrentUser,\n\t\tprivate readonly colorScheme: 'light' | 'dark' | 'system'\n\t) {\n\t\tif (typeof window === 'undefined' || !getGlobalWindow().matchMedia) return\n\n\t\tconst darkModeMediaQuery = getGlobalWindow().matchMedia('(prefers-color-scheme: dark)')\n\t\tif (darkModeMediaQuery?.matches) {\n\t\t\tthis.systemColorScheme.set('dark')\n\t\t}\n\t\tconst handleChange = (e: MediaQueryListEvent) => {\n\t\t\tif (e.matches) {\n\t\t\t\tthis.systemColorScheme.set('dark')\n\t\t\t} else {\n\t\t\t\tthis.systemColorScheme.set('light')\n\t\t\t}\n\t\t}\n\t\tdarkModeMediaQuery?.addEventListener('change', handleChange)\n\t\tthis.disposables.add(() => darkModeMediaQuery?.removeEventListener('change', handleChange))\n\t}\n\n\tupdateUserPreferences(userPreferences: Partial<TLUserPreferences>) {\n\t\tthis.user.setUserPreferences({\n\t\t\t...this.user.userPreferences.get(),\n\t\t\t...userPreferences,\n\t\t})\n\t}\n\t@computed getUserPreferences() {\n\t\treturn {\n\t\t\tid: this.
|
|
5
|
-
"mappings": ";;;;;;;;;;AAAA,SAAS,MAAM,gBAAgB;
|
|
4
|
+
"sourcesContent": ["import { atom, computed } from '@tldraw/state'\nimport { createUserId, TLUserId } from '@tldraw/tlschema'\nimport { TLCurrentUser } from '../../../config/createTLCurrentUser'\nimport { TLUserPreferences, defaultUserPreferences } from '../../../config/TLUserPreferences'\nimport { getGlobalWindow } from '../../../utils/dom'\n\n/** @public */\nexport class UserPreferencesManager {\n\tsystemColorScheme = atom<'dark' | 'light'>('systemColorScheme', 'light')\n\tdisposables = new Set<() => void>()\n\tdispose() {\n\t\tthis.disposables.forEach((d) => d())\n\t}\n\tconstructor(\n\t\tprivate readonly user: TLCurrentUser,\n\t\tprivate readonly colorScheme: 'light' | 'dark' | 'system'\n\t) {\n\t\tif (typeof window === 'undefined' || !getGlobalWindow().matchMedia) return\n\n\t\tconst darkModeMediaQuery = getGlobalWindow().matchMedia('(prefers-color-scheme: dark)')\n\t\tif (darkModeMediaQuery?.matches) {\n\t\t\tthis.systemColorScheme.set('dark')\n\t\t}\n\t\tconst handleChange = (e: MediaQueryListEvent) => {\n\t\t\tif (e.matches) {\n\t\t\t\tthis.systemColorScheme.set('dark')\n\t\t\t} else {\n\t\t\t\tthis.systemColorScheme.set('light')\n\t\t\t}\n\t\t}\n\t\tdarkModeMediaQuery?.addEventListener('change', handleChange)\n\t\tthis.disposables.add(() => darkModeMediaQuery?.removeEventListener('change', handleChange))\n\t}\n\n\tupdateUserPreferences(userPreferences: Partial<TLUserPreferences>) {\n\t\tthis.user.setUserPreferences({\n\t\t\t...this.user.userPreferences.get(),\n\t\t\t...userPreferences,\n\t\t})\n\t}\n\t@computed getUserPreferences() {\n\t\treturn {\n\t\t\tid: this.getExternalId(),\n\t\t\tname: this.getName(),\n\t\t\tlocale: this.getLocale(),\n\t\t\tcolor: this.getColor(),\n\t\t\tanimationSpeed: this.getAnimationSpeed(),\n\t\t\tareKeyboardShortcutsEnabled: this.getAreKeyboardShortcutsEnabled(),\n\t\t\tisSnapMode: this.getIsSnapMode(),\n\t\t\tcolorScheme: this.user.userPreferences.get().colorScheme,\n\t\t\tisDarkMode: this.getIsDarkMode(),\n\t\t\tisWrapMode: this.getIsWrapMode(),\n\t\t\tisDynamicResizeMode: this.getIsDynamicResizeMode(),\n\t\t\tenhancedA11yMode: this.getEnhancedA11yMode(),\n\t\t\tinputMode: this.getInputMode(),\n\t\t\tisZoomDirectionInverted: this.getIsZoomDirectionInverted(),\n\t\t}\n\t}\n\n\t@computed getIsDarkMode() {\n\t\tconst userColorScheme = this.user.userPreferences.get().colorScheme\n\t\tconst scheme = userColorScheme ?? this.colorScheme\n\t\tswitch (scheme) {\n\t\t\tcase 'dark':\n\t\t\t\treturn true\n\t\t\tcase 'light':\n\t\t\t\treturn false\n\t\t\tcase 'system':\n\t\t\t\treturn this.systemColorScheme.get() === 'dark'\n\t\t\tdefault:\n\t\t\t\treturn false\n\t\t}\n\t}\n\n\t/**\n\t * The speed at which the user can scroll by dragging toward the edge of the screen.\n\t */\n\t@computed getEdgeScrollSpeed() {\n\t\treturn this.user.userPreferences.get().edgeScrollSpeed ?? defaultUserPreferences.edgeScrollSpeed\n\t}\n\n\t@computed getAnimationSpeed() {\n\t\treturn this.user.userPreferences.get().animationSpeed ?? defaultUserPreferences.animationSpeed\n\t}\n\n\t@computed getAreKeyboardShortcutsEnabled() {\n\t\treturn (\n\t\t\tthis.user.userPreferences.get().areKeyboardShortcutsEnabled ??\n\t\t\tdefaultUserPreferences.areKeyboardShortcutsEnabled\n\t\t)\n\t}\n\n\t/**\n\t * The current user's raw, app-provided id \u2014 the value set in the user's\n\t * {@link @tldraw/editor#TLUserPreferences}. Use this when you need the id your application\n\t * assigned to the user. To compare against or look up store records, use\n\t * {@link UserPreferencesManager.getRecordId} instead.\n\t */\n\t@computed getExternalId(): string {\n\t\treturn this.user.userPreferences.get().id\n\t}\n\n\t/**\n\t * @deprecated Use {@link UserPreferencesManager.getExternalId} for the raw app-provided id, or\n\t * {@link UserPreferencesManager.getRecordId} for the prefixed `TLUserId` record id.\n\t */\n\t@computed getId() {\n\t\treturn this.getExternalId()\n\t}\n\n\t/**\n\t * The current user's id as a tldraw {@link @tldraw/tlschema#TLUserId} record id (prefixed\n\t * with `user:`). Use this when comparing against or looking up store records, such as a\n\t * presence record's `userId` or `followingUserId`. For the raw, app-provided id, use\n\t * {@link UserPreferencesManager.getExternalId}.\n\t */\n\t@computed getRecordId(): TLUserId {\n\t\treturn createUserId(this.getExternalId())\n\t}\n\n\t@computed getName() {\n\t\treturn this.user.userPreferences.get().name?.trim() ?? defaultUserPreferences.name\n\t}\n\n\t@computed getLocale() {\n\t\treturn this.user.userPreferences.get().locale ?? defaultUserPreferences.locale\n\t}\n\n\t@computed getColor() {\n\t\treturn this.user.userPreferences.get().color ?? defaultUserPreferences.color\n\t}\n\n\t@computed getIsSnapMode() {\n\t\treturn this.user.userPreferences.get().isSnapMode ?? defaultUserPreferences.isSnapMode\n\t}\n\n\t@computed getIsWrapMode() {\n\t\treturn this.user.userPreferences.get().isWrapMode ?? defaultUserPreferences.isWrapMode\n\t}\n\n\t@computed getIsDynamicResizeMode() {\n\t\treturn (\n\t\t\tthis.user.userPreferences.get().isDynamicSizeMode ?? defaultUserPreferences.isDynamicSizeMode\n\t\t)\n\t}\n\n\t@computed getIsPasteAtCursorMode() {\n\t\treturn (\n\t\t\tthis.user.userPreferences.get().isPasteAtCursorMode ??\n\t\t\tdefaultUserPreferences.isPasteAtCursorMode\n\t\t)\n\t}\n\n\t@computed getEnhancedA11yMode() {\n\t\treturn (\n\t\t\tthis.user.userPreferences.get().enhancedA11yMode ?? defaultUserPreferences.enhancedA11yMode\n\t\t)\n\t}\n\n\t@computed getInputMode() {\n\t\treturn this.user.userPreferences.get().inputMode ?? defaultUserPreferences.inputMode\n\t}\n\n\t@computed getIsZoomDirectionInverted() {\n\t\treturn (\n\t\t\tthis.user.userPreferences.get().isZoomDirectionInverted ??\n\t\t\tdefaultUserPreferences.isZoomDirectionInverted\n\t\t)\n\t}\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { createComputedCache } from "@tldraw/store";
|
|
2
|
+
const indicatorPathCache = createComputedCache(
|
|
3
|
+
"shapeIndicatorPath",
|
|
4
|
+
(editor, shape) => {
|
|
5
|
+
const util = editor.getShapeUtil(shape);
|
|
6
|
+
return util.getIndicatorPath(shape);
|
|
7
|
+
},
|
|
8
|
+
{
|
|
9
|
+
areRecordsEqual(a, b) {
|
|
10
|
+
return a.props === b.props;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
);
|
|
14
|
+
function strokeShapeIndicators(editor, ctx, shapeIds) {
|
|
15
|
+
if (shapeIds.length === 0) return;
|
|
16
|
+
const batched = new Path2D();
|
|
17
|
+
for (const shapeId of shapeIds) {
|
|
18
|
+
const shape = editor.getShape(shapeId);
|
|
19
|
+
if (!shape || shape.isLocked) continue;
|
|
20
|
+
const pageTransform = editor.getShapePageTransform(shape);
|
|
21
|
+
if (!pageTransform) continue;
|
|
22
|
+
const indicatorPath = indicatorPathCache.get(editor, shape.id);
|
|
23
|
+
if (!indicatorPath) continue;
|
|
24
|
+
if (indicatorPath instanceof Path2D) {
|
|
25
|
+
batched.addPath(indicatorPath, pageTransform);
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
28
|
+
const { path, clipPath, additionalPaths } = indicatorPath;
|
|
29
|
+
if (!clipPath) {
|
|
30
|
+
batched.addPath(path, pageTransform);
|
|
31
|
+
if (additionalPaths) {
|
|
32
|
+
for (const p of additionalPaths) batched.addPath(p, pageTransform);
|
|
33
|
+
}
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
ctx.save();
|
|
37
|
+
ctx.transform(
|
|
38
|
+
pageTransform.a,
|
|
39
|
+
pageTransform.b,
|
|
40
|
+
pageTransform.c,
|
|
41
|
+
pageTransform.d,
|
|
42
|
+
pageTransform.e,
|
|
43
|
+
pageTransform.f
|
|
44
|
+
);
|
|
45
|
+
ctx.save();
|
|
46
|
+
ctx.clip(clipPath, "evenodd");
|
|
47
|
+
ctx.stroke(path);
|
|
48
|
+
ctx.restore();
|
|
49
|
+
if (additionalPaths) {
|
|
50
|
+
for (const p of additionalPaths) ctx.stroke(p);
|
|
51
|
+
}
|
|
52
|
+
ctx.restore();
|
|
53
|
+
}
|
|
54
|
+
ctx.stroke(batched);
|
|
55
|
+
}
|
|
56
|
+
export {
|
|
57
|
+
strokeShapeIndicators
|
|
58
|
+
};
|
|
59
|
+
//# sourceMappingURL=strokeShapeIndicators.mjs.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/lib/editor/overlays/strokeShapeIndicators.ts"],
|
|
4
|
+
"sourcesContent": ["import { createComputedCache } from '@tldraw/store'\nimport { TLShape, TLShapeId } from '@tldraw/tlschema'\nimport type { Editor } from '../Editor'\n\nconst indicatorPathCache = createComputedCache(\n\t'shapeIndicatorPath',\n\t(editor: Editor, shape: TLShape) => {\n\t\tconst util = editor.getShapeUtil(shape)\n\t\treturn util.getIndicatorPath(shape)\n\t},\n\t{\n\t\tareRecordsEqual(a, b) {\n\t\t\treturn a.props === b.props\n\t\t},\n\t}\n)\n\n/**\n * Combine every batchable shape indicator into a single page-space `Path2D` and\n * emit one stroke call. Shapes whose indicator needs an evenodd clip (e.g.\n * arrows with labels or complex arrowheads) can't be batched \u2014 they still\n * stroke individually inside a save/restore with `ctx.clip` applied.\n *\n * Shared by any overlay util that paints shape indicators (e.g. collaborator\n * selections).\n *\n * @public\n */\nexport function strokeShapeIndicators(\n\teditor: Editor,\n\tctx: CanvasRenderingContext2D,\n\tshapeIds: TLShapeId[]\n): void {\n\tif (shapeIds.length === 0) return\n\n\tconst batched = new Path2D()\n\n\tfor (const shapeId of shapeIds) {\n\t\tconst shape = editor.getShape(shapeId)\n\t\tif (!shape || shape.isLocked) continue\n\n\t\tconst pageTransform = editor.getShapePageTransform(shape)\n\t\tif (!pageTransform) continue\n\n\t\tconst indicatorPath = indicatorPathCache.get(editor, shape.id)\n\t\tif (!indicatorPath) continue\n\n\t\tif (indicatorPath instanceof Path2D) {\n\t\t\tbatched.addPath(indicatorPath, pageTransform)\n\t\t\tcontinue\n\t\t}\n\n\t\tconst { path, clipPath, additionalPaths } = indicatorPath\n\n\t\tif (!clipPath) {\n\t\t\tbatched.addPath(path, pageTransform)\n\t\t\tif (additionalPaths) {\n\t\t\t\tfor (const p of additionalPaths) batched.addPath(p, pageTransform)\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\n\t\t// Clipped case: fall back to an individual stroke. Rare (arrows with\n\t\t// labels / complex arrowheads), so the extra save/restore/stroke\n\t\t// pair per such shape isn't worth batching away.\n\t\tctx.save()\n\t\tctx.transform(\n\t\t\tpageTransform.a,\n\t\t\tpageTransform.b,\n\t\t\tpageTransform.c,\n\t\t\tpageTransform.d,\n\t\t\tpageTransform.e,\n\t\t\tpageTransform.f\n\t\t)\n\t\tctx.save()\n\t\tctx.clip(clipPath, 'evenodd')\n\t\tctx.stroke(path)\n\t\tctx.restore()\n\t\tif (additionalPaths) {\n\t\t\tfor (const p of additionalPaths) ctx.stroke(p)\n\t\t}\n\t\tctx.restore()\n\t}\n\n\tctx.stroke(batched)\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,2BAA2B;AAIpC,MAAM,qBAAqB;AAAA,EAC1B;AAAA,EACA,CAAC,QAAgB,UAAmB;AACnC,UAAM,OAAO,OAAO,aAAa,KAAK;AACtC,WAAO,KAAK,iBAAiB,KAAK;AAAA,EACnC;AAAA,EACA;AAAA,IACC,gBAAgB,GAAG,GAAG;AACrB,aAAO,EAAE,UAAU,EAAE;AAAA,IACtB;AAAA,EACD;AACD;AAaO,SAAS,sBACf,QACA,KACA,UACO;AACP,MAAI,SAAS,WAAW,EAAG;AAE3B,QAAM,UAAU,IAAI,OAAO;AAE3B,aAAW,WAAW,UAAU;AAC/B,UAAM,QAAQ,OAAO,SAAS,OAAO;AACrC,QAAI,CAAC,SAAS,MAAM,SAAU;AAE9B,UAAM,gBAAgB,OAAO,sBAAsB,KAAK;AACxD,QAAI,CAAC,cAAe;AAEpB,UAAM,gBAAgB,mBAAmB,IAAI,QAAQ,MAAM,EAAE;AAC7D,QAAI,CAAC,cAAe;AAEpB,QAAI,yBAAyB,QAAQ;AACpC,cAAQ,QAAQ,eAAe,aAAa;AAC5C;AAAA,IACD;AAEA,UAAM,EAAE,MAAM,UAAU,gBAAgB,IAAI;AAE5C,QAAI,CAAC,UAAU;AACd,cAAQ,QAAQ,MAAM,aAAa;AACnC,UAAI,iBAAiB;AACpB,mBAAW,KAAK,gBAAiB,SAAQ,QAAQ,GAAG,aAAa;AAAA,MAClE;AACA;AAAA,IACD;AAKA,QAAI,KAAK;AACT,QAAI;AAAA,MACH,cAAc;AAAA,MACd,cAAc;AAAA,MACd,cAAc;AAAA,MACd,cAAc;AAAA,MACd,cAAc;AAAA,MACd,cAAc;AAAA,IACf;AACA,QAAI,KAAK;AACT,QAAI,KAAK,UAAU,SAAS;AAC5B,QAAI,OAAO,IAAI;AACf,QAAI,QAAQ;AACZ,QAAI,iBAAiB;AACpB,iBAAW,KAAK,gBAAiB,KAAI,OAAO,CAAC;AAAA,IAC9C;AACA,QAAI,QAAQ;AAAA,EACb;AAEA,MAAI,OAAO,OAAO;AACnB;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/lib/editor/shapes/ShapeUtil.ts"],
|
|
4
|
-
"sourcesContent": ["/* eslint-disable @typescript-eslint/no-unused-vars */\nimport { EMPTY_ARRAY } from '@tldraw/state'\nimport { LegacyMigrations, MigrationSequence } from '@tldraw/store'\nimport {\n\tRecordProps,\n\tTLAsset,\n\tTLHandle,\n\tTLParentId,\n\tTLPropsMigrations,\n\tTLShape,\n\tTLShapeCrop,\n\tTLShapeId,\n\tTLShapePartial,\n\tTLUnknownShape,\n\tVecModel,\n} from '@tldraw/tlschema'\nimport { TLFontFace } from '@tldraw/tlschema'\nimport { IndexKey } from '@tldraw/utils'\nimport { ReactElement } from 'react'\nimport { Box, SelectionHandle } from '../../primitives/Box'\nimport { Geometry2d } from '../../primitives/geometry/Geometry2d'\nimport { Vec } from '../../primitives/Vec'\nimport type { Editor } from '../Editor'\nimport { BoundsSnapGeometry } from '../managers/SnapManager/BoundsSnaps'\nimport { HandleSnapGeometry } from '../managers/SnapManager/HandleSnaps'\nimport { TLClickEventInfo } from '../types/event-types'\nimport { TLResizeHandle } from '../types/selection-types'\nimport { SvgExportContext } from '../types/SvgExportContext'\n\n/** @public */\nexport interface TLShapeUtilConstructor<T extends TLShape, U extends ShapeUtil<T> = ShapeUtil<T>> {\n\tnew (editor: Editor): U\n\ttype: T['type']\n\tprops?: RecordProps<T>\n\tmigrations?: LegacyMigrations | TLPropsMigrations | MigrationSequence\n\thandledAssetTypes?: readonly string[]\n}\n\n/**\n * Options passed to {@link ShapeUtil.canBind}. A binding that could be made. At least one of\n * `fromShape` or `toShape` will belong to this shape util.\n *\n * The shapes may be full {@link @tldraw/tlschema#TLShape} objects when available, or just\n * `{ type }` stubs when the shape hasn't been created yet (e.g. during arrow creation). Use\n * `'id' in shape` to check whether the full shape is available.\n *\n * @public\n */\nexport interface TLShapeUtilCanBindOpts<Shape extends TLShape = TLShape> {\n\t/** The shape referenced by the `fromId` of the binding, or a `{ type }` stub if unavailable. */\n\tfromShape: TLShape | { type: TLShape['type'] }\n\t/** The shape referenced by the `toId` of the binding, or a `{ type }` stub if unavailable. */\n\ttoShape: TLShape | { type: TLShape['type'] }\n\t/** The type of binding. */\n\tbindingType: string\n\t/**\n\t * The type of shape referenced by the `fromId` of the binding.\n\t * @deprecated Use `fromShape.type` instead.\n\t */\n\tfromShapeType: TLShape['type']\n\t/**\n\t * The type of shape referenced by the `toId` of the binding.\n\t * @deprecated Use `toShape.type` instead.\n\t */\n\ttoShapeType: TLShape['type']\n}\n\n/**\n * Options passed to {@link ShapeUtil.canBeLaidOut}.\n *\n * @public\n */\nexport interface TLShapeUtilCanBeLaidOutOpts {\n\t/** The type of action causing the layout. */\n\ttype?: 'align' | 'distribute' | 'pack' | 'stack' | 'flip' | 'stretch' | 'resize_to_bounds'\n\t/** The other shapes being laid out */\n\tshapes?: TLShape[]\n}\n\n/** Additional options for the {@link ShapeUtil.getGeometry} method.\n *\n * @public\n */\nexport interface TLGeometryOpts {\n\t/** The context in which the geometry is being requested. */\n\tcontext?: string\n}\n\n/** @public */\nexport interface TLShapeUtilCanvasSvgDef {\n\tkey: string\n\tcomponent: React.ComponentType\n}\n\n/**\n * Return type for {@link ShapeUtil.getIndicatorPath}. Can be either a simple Path2D\n * or an object with additional rendering info like clip paths for complex indicators.\n * @public\n */\nexport type TLIndicatorPath =\n\t| Path2D\n\t| {\n\t\t\tpath: Path2D\n\t\t\tclipPath?: Path2D\n\t\t\tadditionalPaths?: Path2D[]\n\t }\n\n/** @public */\nexport abstract class ShapeUtil<Shape extends TLShape = TLShape> {\n\t/** Configure this shape utils {@link ShapeUtil.options | `options`}. */\n\tstatic configure<T extends TLShapeUtilConstructor<any, any>>(\n\t\tthis: T,\n\t\toptions: T extends new (...args: any[]) => { options: infer Options } ? Partial<Options> : never\n\t): T {\n\t\t// @ts-expect-error -- typescript has no idea what's going on here but it's fine\n\t\treturn class extends this {\n\t\t\t// @ts-expect-error\n\t\t\toptions = { ...this.options, ...options }\n\t\t}\n\t}\n\n\tconstructor(public editor: Editor) {}\n\n\t/**\n\t * Options for this shape util. If you're implementing a custom shape util, you can override\n\t * this to provide customization options for your shape. If using an existing shape util, you\n\t * can customizing this by calling {@link ShapeUtil.configure}.\n\t */\n\toptions = {}\n\n\t/**\n\t * Props allow you to define the shape's properties in a way that the editor can understand.\n\t * This has two main uses:\n\t *\n\t * 1. Validation. Shapes will be validated using these props to stop bad data from being saved.\n\t * 2. Styles. Each {@link @tldraw/tlschema#StyleProp} in the props can be set on many shapes at\n\t * once, and will be remembered from one shape to the next.\n\t *\n\t * @example\n\t * ```tsx\n\t * import {T, TLBaseShape, TLDefaultColorStyle, DefaultColorStyle, ShapeUtil} from 'tldraw'\n\t *\n\t * type MyShape = TLBaseShape<'mine', {\n\t * color: TLDefaultColorStyle,\n\t * text: string,\n\t * }>\n\t *\n\t * class MyShapeUtil extends ShapeUtil<MyShape> {\n\t * static props = {\n\t * // we use tldraw's built-in color style:\n\t * color: DefaultColorStyle,\n\t * // validate that the text prop is a string:\n\t * text: T.string,\n\t * }\n\t * }\n\t * ```\n\t */\n\tstatic props?: RecordProps<TLUnknownShape>\n\n\t/**\n\t * Migrations allow you to make changes to a shape's props over time. Read the\n\t * {@link https://www.tldraw.dev/docs/persistence#Shape-props-migrations | shape prop migrations}\n\t * guide for more information.\n\t */\n\tstatic migrations?: LegacyMigrations | TLPropsMigrations | MigrationSequence\n\n\t/**\n\t * The type of the shape util, which should match the shape's type.\n\t *\n\t * @public\n\t */\n\tstatic type: string\n\n\t/**\n\t * The asset types that this shape can be created from.\n\t * When a file is dropped on the canvas, the editor finds the shape util\n\t * whose `handledAssetTypes` includes the asset's type and calls\n\t * {@link ShapeUtil.createShapeForAsset} to produce the shape.\n\t *\n\t * @public\n\t */\n\tstatic handledAssetTypes?: readonly string[]\n\n\t/**\n\t * Get the default props for a shape.\n\t *\n\t * @public\n\t */\n\tabstract getDefaultProps(): Shape['props']\n\n\t/**\n\t * Create a shape partial for placing an asset on the canvas.\n\t * Only called for shapes whose constructor declares matching\n\t * {@link ShapeUtil.handledAssetTypes | `handledAssetTypes`}.\n\t *\n\t * @param asset - The asset to create a shape for.\n\t * @param position - Where to place the shape.\n\t * @returns A shape partial, or null if this shape can't be created for the asset.\n\t * @public\n\t */\n\tcreateShapeForAsset?(asset: TLAsset, position: VecModel): TLShapePartial | null\n\n\t/**\n\t * Get the shape's geometry.\n\t *\n\t * @param shape - The shape.\n\t * @param opts - Additional options for the request.\n\t * @public\n\t */\n\tabstract getGeometry(shape: Shape, opts?: TLGeometryOpts): Geometry2d\n\n\t/**\n\t * Get a JSX element for the shape (as an HTML element).\n\t *\n\t * @param shape - The shape.\n\t * @public\n\t */\n\tabstract component(shape: Shape): any\n\n\t/**\n\t * Get a Path2D (or a richer object with clip/additional paths) for rendering the\n\t * shape's indicator on the canvas. Shapes that return `undefined` will not render\n\t * an indicator.\n\t *\n\t * For complex indicators that need clipping (e.g., arrows with labels), return an\n\t * object with `path`, `clipPath`, and `additionalPaths` properties.\n\t *\n\t * @param shape - The shape.\n\t * @returns A Path2D to stroke, or an object with clipping info, or undefined to skip.\n\t * @public\n\t */\n\tabstract getIndicatorPath(shape: Shape): TLIndicatorPath | undefined\n\n\t/**\n\t * Get JSX describing the shape's indicator (as an SVG element).\n\t *\n\t * @deprecated SVG indicators are no longer rendered. Override\n\t * {@link ShapeUtil.getIndicatorPath} instead. This stub is retained so legacy\n\t * subclasses that still call `super.indicator()` keep type-checking; new shapes\n\t * should not implement it.\n\t *\n\t * @param shape - The shape.\n\t * @public\n\t */\n\tindicator(_shape: Shape): any {\n\t\treturn null\n\t}\n\n\t/**\n\t * Get the font faces that should be rendered in the document in order for this shape to render\n\t * correctly.\n\t *\n\t * @param shape - The shape.\n\t * @public\n\t */\n\tgetFontFaces(shape: Shape): TLFontFace[] {\n\t\treturn EMPTY_ARRAY\n\t}\n\n\t/**\n\t * Whether the shape can be snapped to by another shape.\n\t *\n\t * @param shape - The shape.\n\t * @public\n\t */\n\tcanSnap(shape: Shape): boolean {\n\t\treturn true\n\t}\n\n\t/**\n\t * Whether the shape can be tabbed to.\n\t *\n\t * @param shape - The shape.\n\t * @public\n\t */\n\tcanTabTo(shape: Shape): boolean {\n\t\treturn true\n\t}\n\n\t/**\n\t * Whether the shape can be scrolled while editing.\n\t *\n\t * @public\n\t */\n\tcanScroll(shape: Shape): boolean {\n\t\treturn false\n\t}\n\n\t/**\n\t * Whether the shape can be bound to. See {@link TLShapeUtilCanBindOpts} for details.\n\t *\n\t * @public\n\t */\n\tcanBind(opts: TLShapeUtilCanBindOpts): boolean {\n\t\treturn true\n\t}\n\n\t/**\n\t * Whether the shape can be double clicked to edit.\n\t *\n\t * @public\n\t */\n\tcanEdit(shape: Shape, info: TLEditStartInfo): boolean {\n\t\treturn false\n\t}\n\n\t/**\n\t * Whether the shape can be resized.\n\t *\n\t * @public\n\t */\n\tcanResize(shape: Shape): boolean {\n\t\treturn true\n\t}\n\n\t/**\n\t * When the shape is resized, whether the shape's children should also be resized.\n\t *\n\t * @public\n\t */\n\tcanResizeChildren(shape: Shape): boolean {\n\t\treturn true\n\t}\n\n\t/**\n\t * Whether the shape can be edited in read-only mode.\n\t *\n\t * @public\n\t */\n\tcanEditInReadonly(shape: Shape): boolean {\n\t\treturn false\n\t}\n\n\t/**\n\t * Whether the shape can be edited while locked or while an ancestor is locked.\n\t *\n\t * @public\n\t */\n\tcanEditWhileLocked(shape: Shape): boolean {\n\t\treturn false\n\t}\n\n\t/**\n\t * Whether the shape can be cropped.\n\t *\n\t * @public\n\t */\n\tcanCrop(shape: Shape): boolean {\n\t\treturn false\n\t}\n\n\t/**\n\t * Whether the shape can participate in layout functions such as alignment or distribution.\n\t *\n\t * @param shape - The shape.\n\t * @param info - Additional context information: the type of action causing the layout and the\n\t * @public\n\t *\n\t * @public\n\t */\n\tcanBeLaidOut(shape: Shape, info: TLShapeUtilCanBeLaidOutOpts): boolean {\n\t\treturn true\n\t}\n\n\t/**\n\t * Whether this shape can be culled. By default, shapes are culled for\n\t * performance reasons when they are outside of the viewport. Culled shapes are still rendered\n\t * to the DOM, but have their `display` property set to `none`.\n\t *\n\t * @param shape - The shape.\n\t */\n\tcanCull(shape: Shape): boolean {\n\t\treturn true\n\t}\n\n\t/**\n\t * Does this shape provide a background for its children? If this is true,\n\t * then any children with a `renderBackground` method will have their\n\t * backgrounds rendered _above_ this shape. Otherwise, the children's\n\t * backgrounds will be rendered above either the next ancestor that provides\n\t * a background, or the canvas background.\n\t *\n\t * @internal\n\t */\n\tprovidesBackgroundForChildren(shape: Shape): boolean {\n\t\treturn false\n\t}\n\n\t/**\n\t * Get the clip path to apply to this shape's children.\n\t *\n\t * The returned points should define the **inner** clip boundary - the area where\n\t * children will be visible. If your shape has a stroke, you should inset the clip\n\t * path by half the stroke width so children are clipped to the inner edge of the\n\t * stroke rather than its center line.\n\t *\n\t * @example\n\t * ```ts\n\t * override getClipPath(shape: MyShape): Vec[] | undefined {\n\t * const strokeWidth = 2\n\t * const inset = strokeWidth / 2\n\t * // Return points inset by half the stroke width\n\t * return [\n\t * new Vec(inset, inset),\n\t * new Vec(shape.props.w - inset, inset),\n\t * new Vec(shape.props.w - inset, shape.props.h - inset),\n\t * new Vec(inset, shape.props.h - inset),\n\t * ]\n\t * }\n\t * ```\n\t *\n\t * @param shape - The shape to get the clip path for\n\t * @returns Array of points defining the clipping polygon in local coordinates, or undefined if no clipping\n\t * @public\n\t */\n\tgetClipPath?(shape: Shape): Vec[] | undefined\n\n\t/**\n\t * Whether a specific child shape should be clipped by this shape.\n\t * Only called if getClipPath returns a valid polygon.\n\t *\n\t * If not defined, the default behavior is to clip all children.\n\t *\n\t * @param child - The child shape to check\n\t * @returns boolean indicating if this child should be clipped\n\t * @public\n\t */\n\tshouldClipChild?(child: TLShape): boolean\n\n\t/**\n\t * Whether a specific shape should hide in the minimap.\n\t *\n\t * If not defined, the default behavior is to show all shapes in the minimap.\n\t *\n\t * @returns boolean indicating if this shape should hide in the minimap\n\t * @public\n\t */\n\thideInMinimap?(shape: Shape): boolean\n\n\t/**\n\t * Whether the shape should hide its resize handles when selected.\n\t *\n\t * @public\n\t */\n\thideResizeHandles(shape: Shape): boolean {\n\t\treturn false\n\t}\n\n\t/**\n\t * Whether the shape should hide its rotation handles when selected.\n\t *\n\t * @public\n\t */\n\thideRotateHandle(shape: Shape): boolean {\n\t\treturn false\n\t}\n\n\t/**\n\t * Whether the shape should hide its selection bounds background when selected.\n\t *\n\t * @public\n\t */\n\thideSelectionBoundsBg(shape: Shape): boolean {\n\t\treturn false\n\t}\n\n\t/**\n\t * Whether the shape should hide its selection bounds foreground when selected.\n\t *\n\t * @public\n\t */\n\thideSelectionBoundsFg(shape: Shape): boolean {\n\t\treturn false\n\t}\n\n\t/**\n\t * Whether the shape's aspect ratio is locked.\n\t *\n\t * @public\n\t */\n\tisAspectRatioLocked(shape: Shape): boolean {\n\t\treturn false\n\t}\n\n\t/**\n\t * Whether the shape behaves like a frame \u2014 a container that has child shapes,\n\t * requires full-brush selection, blocks erasure from inside, etc.\n\t *\n\t * @param shape - The shape.\n\t * @public\n\t */\n\tisFrameLike(_shape: Shape): boolean {\n\t\treturn false\n\t}\n\n\t/**\n\t * By default, the bounds of an image export are the bounds of all the shapes it contains, plus\n\t * some padding. If an export includes a shape where `isExportBoundsContainer` is true, then the\n\t * padding is skipped _if the bounds of that shape contains all the other shapes_. This is\n\t * useful in cases like annotating on top of an image, where you usually want to avoid extra\n\t * padding around the image if you don't need it.\n\t *\n\t * @param shape - The shape to check\n\t * @returns True if this shape should be treated as an export bounds container\n\t */\n\tisExportBoundsContainer(shape: Shape): boolean {\n\t\treturn false\n\t}\n\n\t/**\n\t * Get a JSX element for the shape (as an HTML element) to be rendered as part of the canvas background - behind any other shape content.\n\t *\n\t * @param shape - The shape.\n\t * @internal\n\t */\n\tbackgroundComponent?(shape: Shape): any\n\n\t/**\n\t * Get the interpolated props for an animating shape. This is an optional method.\n\t *\n\t * @example\n\t *\n\t * ```ts\n\t * util.getInterpolatedProps?.(startShape, endShape, t)\n\t * ```\n\t *\n\t * @param startShape - The initial shape.\n\t * @param endShape - The initial shape.\n\t * @param progress - The normalized progress between zero (start) and 1 (end).\n\t * @public\n\t */\n\tgetInterpolatedProps?(startShape: Shape, endShape: Shape, progress: number): Shape['props']\n\n\t/**\n\t * Get an array of handle models for the shape. This is an optional method.\n\t *\n\t * @example\n\t *\n\t * ```ts\n\t * util.getHandles?.(myShape)\n\t * ```\n\t *\n\t * @param shape - The shape.\n\t * @public\n\t */\n\tgetHandles?(shape: Shape): TLHandle[]\n\n\t/**\n\t * Get whether the shape can receive children of a given type. Used by the drag and drop system\n\t * to decide whether {@link ShapeUtil.onDragShapesIn} should fire when a shape of the given type\n\t * is dragged over this one.\n\t *\n\t * @param shape - The shape.\n\t * @param type - The shape type.\n\t * @public\n\t */\n\tcanReceiveNewChildrenOfType(shape: Shape, type: TLShape['type']) {\n\t\treturn false\n\t}\n\n\t/**\n\t * Get whether children of a given type can be removed from this shape. Used by the drag and\n\t * drop system to decide whether {@link ShapeUtil.onDragShapesOut} should fire when a child of\n\t * the given type is dragged out of this shape, and by `kickoutOccludedShapes` to decide\n\t * whether to auto-reparent a child of the given type when it has moved outside this shape's\n\t * geometry. Returning `false` therefore \"pins\" matching children \u2014 they stay parented to this\n\t * shape even when dragged or moved outside it. Defaults to `true`.\n\t *\n\t * @param shape - The shape.\n\t * @param type - The shape type.\n\t * @public\n\t */\n\tcanRemoveChildrenOfType(shape: Shape, type: TLShape['type']) {\n\t\treturn true\n\t}\n\n\t/**\n\t * Get the shape as an SVG object.\n\t *\n\t * @param shape - The shape.\n\t * @param ctx - The export context for the SVG - used for adding e.g. \\<def\\>s\n\t * @returns An SVG element.\n\t * @public\n\t */\n\ttoSvg?(shape: Shape, ctx: SvgExportContext): ReactElement | null | Promise<ReactElement | null>\n\n\t/**\n\t * Get the shape's background layer as an SVG object.\n\t *\n\t * @param shape - The shape.\n\t * @param ctx - ctx - The export context for the SVG - used for adding e.g. \\<def\\>s\n\t * @returns An SVG element.\n\t * @public\n\t */\n\ttoBackgroundSvg?(\n\t\tshape: Shape,\n\t\tctx: SvgExportContext\n\t): ReactElement | null | Promise<ReactElement | null>\n\n\t/** @internal */\n\texpandSelectionOutlinePx(shape: Shape): number | Box {\n\t\treturn 0\n\t}\n\n\t/**\n\t * Return elements to be added to the \\<defs\\> section of the canvases SVG context. This can be\n\t * used to define SVG content (e.g. patterns & masks) that can be referred to by ID from svg\n\t * elements returned by `component`.\n\t *\n\t * Each def should have a unique `key`. If multiple defs from different shapes all have the same\n\t * key, only one will be used.\n\t */\n\tgetCanvasSvgDefs(): TLShapeUtilCanvasSvgDef[] {\n\t\treturn []\n\t}\n\n\t/**\n\t * Get the geometry to use when snapping to this this shape in translate/resize operations. See\n\t * {@link BoundsSnapGeometry} for details.\n\t */\n\tgetBoundsSnapGeometry(shape: Shape): BoundsSnapGeometry {\n\t\treturn {}\n\t}\n\n\t/**\n\t * Get the geometry to use when snapping handles to this shape. See {@link HandleSnapGeometry}\n\t * for details.\n\t */\n\tgetHandleSnapGeometry(shape: Shape): HandleSnapGeometry {\n\t\treturn {}\n\t}\n\n\tgetText(shape: Shape): string | undefined {\n\t\treturn undefined\n\t}\n\n\t/**\n\t * Return user IDs referenced in shape-specific props.\n\t * Used when copying shapes to include referenced users on the clipboard.\n\t * Override this if your shape stores user IDs in custom props.\n\t *\n\t * @public\n\t */\n\tgetReferencedUserIds(shape: Shape): string[] {\n\t\treturn EMPTY_ARRAY\n\t}\n\n\tgetAriaDescriptor(shape: Shape): string | undefined {\n\t\treturn undefined\n\t}\n\n\t// Events\n\n\t/**\n\t * A callback called just before a shape is created. This method provides a last chance to modify\n\t * the created shape.\n\t *\n\t * @example\n\t *\n\t * ```ts\n\t * onBeforeCreate = (next) => {\n\t * \treturn { ...next, x: next.x + 1 }\n\t * }\n\t * ```\n\t *\n\t * @param next - The next shape.\n\t * @returns The next shape or void.\n\t * @public\n\t */\n\tonBeforeCreate?(next: Shape): Shape | void\n\n\t/**\n\t * A callback called just before a shape is updated. This method provides a last chance to modify\n\t * the updated shape.\n\t *\n\t * @example\n\t *\n\t * ```ts\n\t * onBeforeUpdate = (prev, next) => {\n\t * \tif (prev.x === next.x) {\n\t * \t\treturn { ...next, x: next.x + 1 }\n\t * \t}\n\t * }\n\t * ```\n\t *\n\t * @param prev - The previous shape.\n\t * @param next - The next shape.\n\t * @returns The next shape or void.\n\t * @public\n\t */\n\tonBeforeUpdate?(prev: Shape, next: Shape): Shape | void\n\n\t/**\n\t * A callback called when a shape changes from a crop.\n\t *\n\t * @param shape - The shape at the start of the crop.\n\t * @param info - Info about the crop.\n\t * @returns A change to apply to the shape, or void.\n\t * @public\n\t */\n\tonCrop?(\n\t\tshape: Shape,\n\t\tinfo: TLCropInfo<Shape>\n\t): Omit<TLShapePartial<Shape>, 'id' | 'type'> | undefined | void\n\n\t/**\n\t * A callback called when some other shapes are dragged into this one. This fires when the shapes are dragged over the shape for the first time.\n\t *\n\t * @param shape - The shape.\n\t * @param shapes - The shapes that are being dragged in.\n\t * @public\n\t */\n\tonDragShapesIn?(shape: Shape, shapes: TLShape[], info: TLDragShapesInInfo): void\n\n\t/**\n\t * A callback called when some other shapes are dragged over this one. This fires when the shapes are dragged over the shape for the first time (after the onDragShapesIn callback), and again on every update while the shapes are being dragged.\n\t *\n\t * @example\n\t *\n\t * ```ts\n\t * onDragShapesOver = (shape, shapes) => {\n\t * \tthis.editor.reparentShapes(shapes, shape.id)\n\t * }\n\t * ```\n\t *\n\t * @param shape - The shape.\n\t * @param shapes - The shapes that are being dragged over this one.\n\t * @public\n\t */\n\tonDragShapesOver?(shape: Shape, shapes: TLShape[], info: TLDragShapesOverInfo): void\n\n\t/**\n\t * A callback called when some other shapes are dragged out of this one.\n\t *\n\t * @param shape - The shape.\n\t * @param shapes - The shapes that are being dragged out.\n\t * @public\n\t */\n\tonDragShapesOut?(shape: Shape, shapes: TLShape[], info: TLDragShapesOutInfo): void\n\n\t/**\n\t * A callback called when some other shapes are dropped over this one.\n\t *\n\t * @param shape - The shape.\n\t * @param shapes - The shapes that are being dropped over this one.\n\t * @public\n\t */\n\tonDropShapesOver?(shape: Shape, shapes: TLShape[], info: TLDropShapesOverInfo): void\n\n\t/**\n\t * A callback called when a shape starts being resized.\n\t *\n\t * @param shape - The shape.\n\t * @returns A change to apply to the shape, or void.\n\t * @public\n\t */\n\tonResizeStart?(shape: Shape): TLShapePartial<Shape> | void\n\n\t/**\n\t * A callback called when a shape changes from a resize.\n\t *\n\t * @param shape - The shape at the start of the resize.\n\t * @param info - Info about the resize.\n\t * @returns A change to apply to the shape, or void.\n\t * @public\n\t */\n\tonResize?(\n\t\tshape: Shape,\n\t\tinfo: TLResizeInfo<Shape>\n\t): Omit<TLShapePartial<Shape>, 'id' | 'type'> | undefined | void\n\n\t/**\n\t * A callback called when a shape finishes resizing.\n\t *\n\t * @param initial - The shape at the start of the resize.\n\t * @param current - The current shape.\n\t * @returns A change to apply to the shape, or void.\n\t * @public\n\t */\n\tonResizeEnd?(initial: Shape, current: Shape): TLShapePartial<Shape> | void\n\n\t/**\n\t * A callback called when a shape resize is cancelled.\n\t *\n\t * @param initial - The shape at the start of the resize.\n\t * @param current - The current shape.\n\t * @public\n\t */\n\tonResizeCancel?(initial: Shape, current: Shape): void\n\n\t/**\n\t * A callback called when a shape starts being translated.\n\t *\n\t * @param shape - The shape.\n\t * @returns A change to apply to the shape, or void.\n\t * @public\n\t */\n\tonTranslateStart?(shape: Shape): TLShapePartial<Shape> | void\n\n\t/**\n\t * A callback called when a shape changes from a translation.\n\t *\n\t * @param initial - The shape at the start of the translation.\n\t * @param current - The current shape.\n\t * @returns A change to apply to the shape, or void.\n\t * @public\n\t */\n\tonTranslate?(initial: Shape, current: Shape): TLShapePartial<Shape> | void\n\n\t/**\n\t * A callback called when a shape finishes translating.\n\t *\n\t * @param initial - The shape at the start of the translation.\n\t * @param current - The current shape.\n\t * @returns A change to apply to the shape, or void.\n\t * @public\n\t */\n\tonTranslateEnd?(initial: Shape, current: Shape): TLShapePartial<Shape> | void\n\n\t/**\n\t * A callback called when a shape translation is cancelled.\n\t *\n\t * @param initial - The shape at the start of the translation.\n\t * @param current - The current shape.\n\t * @public\n\t */\n\tonTranslateCancel?(initial: Shape, current: Shape): void\n\n\t/**\n\t * A callback called when a shape's handle starts being dragged.\n\t *\n\t * @param shape - The shape.\n\t * @param info - An object containing the handle and whether the handle is 'precise' or not.\n\t * @returns A change to apply to the shape, or void.\n\t * @public\n\t */\n\tonHandleDragStart?(shape: Shape, info: TLHandleDragInfo<Shape>): TLShapePartial<Shape> | void\n\n\t/**\n\t * A callback called when a shape's handle changes.\n\t *\n\t * @param shape - The current shape.\n\t * @param info - An object containing the handle and whether the handle is 'precise' or not.\n\t * @returns A change to apply to the shape, or void.\n\t * @public\n\t */\n\tonHandleDrag?(shape: Shape, info: TLHandleDragInfo<Shape>): TLShapePartial<Shape> | void\n\n\t/**\n\t * A callback called when a shape's handle finishes being dragged.\n\t *\n\t * @param current - The current shape.\n\t * @param info - An object containing the handle and whether the handle is 'precise' or not.\n\t * @returns A change to apply to the shape, or void.\n\t * @public\n\t */\n\tonHandleDragEnd?(current: Shape, info: TLHandleDragInfo<Shape>): TLShapePartial<Shape> | void\n\n\t/**\n\t * A callback called when a shape's handle drag is cancelled.\n\t *\n\t * @param current - The current shape.\n\t * @param info - An object containing the handle and whether the handle is 'precise' or not.\n\t * @public\n\t */\n\tonHandleDragCancel?(current: Shape, info: TLHandleDragInfo<Shape>): void\n\n\t/**\n\t * A callback called when a shape starts being rotated.\n\t *\n\t * @param shape - The shape.\n\t * @returns A change to apply to the shape, or void.\n\t * @public\n\t */\n\tonRotateStart?(shape: Shape): TLShapePartial<Shape> | void\n\n\t/**\n\t * A callback called when a shape changes from a rotation.\n\t *\n\t * @param initial - The shape at the start of the rotation.\n\t * @param current - The current shape.\n\t * @returns A change to apply to the shape, or void.\n\t * @public\n\t */\n\tonRotate?(initial: Shape, current: Shape): TLShapePartial<Shape> | void\n\n\t/**\n\t * A callback called when a shape finishes rotating.\n\t *\n\t * @param initial - The shape at the start of the rotation.\n\t * @param current - The current shape.\n\t * @returns A change to apply to the shape, or void.\n\t * @public\n\t */\n\tonRotateEnd?(initial: Shape, current: Shape): TLShapePartial<Shape> | void\n\n\t/**\n\t * A callback called when a shape rotation is cancelled.\n\t *\n\t * @param initial - The shape at the start of the rotation.\n\t * @param current - The current shape.\n\t * @public\n\t */\n\tonRotateCancel?(initial: Shape, current: Shape): void\n\n\t/**\n\t * Not currently used.\n\t *\n\t * @internal\n\t */\n\tonBindingChange?(shape: Shape): TLShapePartial<Shape> | void\n\n\t/**\n\t * A callback called when a shape's children change.\n\t *\n\t * @param shape - The shape.\n\t * @returns An array of shape updates, or void.\n\t * @public\n\t */\n\tonChildrenChange?(shape: Shape): TLShapePartial[] | void\n\n\t/**\n\t * A callback called when a shape's handle is double clicked.\n\t *\n\t * @param shape - The shape.\n\t * @param handle - The handle that is double-clicked.\n\t * @returns A change to apply to the shape, or void.\n\t * @public\n\t */\n\tonDoubleClickHandle?(shape: Shape, handle: TLHandle): TLShapePartial<Shape> | void\n\n\t/**\n\t * A callback called when a shape's edge is double clicked.\n\t *\n\t * @param shape - The shape.\n\t * @param info - Info about the edge.\n\t * @returns A change to apply to the shape, or void.\n\t * @public\n\t */\n\tonDoubleClickEdge?(shape: Shape, info: TLClickEventInfo): TLShapePartial<Shape> | void\n\n\t/**\n\t * A callback called when a shape's corner is double clicked.\n\t *\n\t * @param shape - The shape.\n\t * @param info - Info about the corner.\n\t * @returns A change to apply to the shape, or void.\n\t * @public\n\t */\n\tonDoubleClickCorner?(shape: Shape, info: TLClickEventInfo): TLShapePartial<Shape> | void\n\n\t/**\n\t * A callback called when a shape is double clicked.\n\t *\n\t * @param shape - The shape.\n\t * @returns A change to apply to the shape, or void.\n\t * @public\n\t */\n\tonDoubleClick?(shape: Shape): TLShapePartial<Shape> | void\n\n\t/**\n\t * A callback called when a shape is clicked.\n\t *\n\t * @param shape - The shape.\n\t * @returns A change to apply to the shape, or void.\n\t * @public\n\t */\n\tonClick?(shape: Shape): TLShapePartial<Shape> | void\n\n\t/**\n\t * A callback called when a shape starts being edited.\n\t *\n\t * @param shape - The shape.\n\t * @public\n\t */\n\tonEditStart?(shape: Shape): void\n\n\t/**\n\t * A callback called when a shape finishes being edited.\n\t *\n\t * @param shape - The shape.\n\t * @public\n\t */\n\tonEditEnd?(shape: Shape): void\n}\n\n/**\n * Info about a crop.\n * @param handle - The handle being dragged.\n * @param change - The distance the handle is moved.\n * @param initialShape - The shape at the start of the resize.\n * @public\n */\nexport interface TLCropInfo<T extends TLShape> {\n\thandle: SelectionHandle\n\tchange: Vec\n\tcrop: TLShapeCrop\n\tuncroppedSize: { w: number; h: number }\n\tinitialShape: T\n\taspectRatioLocked?: boolean\n}\n\n/** @public */\nexport interface TLDragShapesInInfo {\n\tinitialDraggingOverShapeId: TLShapeId | null\n\tprevDraggingOverShapeId: TLShapeId | null\n\tinitialParentIds: Map<TLShapeId, TLParentId>\n\tinitialIndices: Map<TLShapeId, IndexKey>\n}\n\n/** @public */\nexport interface TLDragShapesOverInfo {\n\tinitialDraggingOverShapeId: TLShapeId | null\n\tinitialParentIds: Map<TLShapeId, TLParentId>\n\tinitialIndices: Map<TLShapeId, IndexKey>\n}\n\n/** @public */\nexport interface TLDragShapesOutInfo {\n\tnextDraggingOverShapeId: TLShapeId | null\n\tinitialDraggingOverShapeId: TLShapeId | null\n\tinitialParentIds: Map<TLShapeId, TLParentId>\n\tinitialIndices: Map<TLShapeId, IndexKey>\n}\n\n/** @public */\nexport interface TLDropShapesOverInfo {\n\tinitialDraggingOverShapeId: TLShapeId | null\n\tinitialParentIds: Map<TLShapeId, TLParentId>\n\tinitialIndices: Map<TLShapeId, IndexKey>\n}\n\n/**\n * The type of resize.\n *\n * 'scale_shape' - The shape is being scaled, usually as part of a larger selection.\n *\n * 'resize_bounds' - The user is directly manipulating an individual shape's bounds using a resize\n * handle. It is up to shape util implementers to decide how they want to handle the two\n * situations.\n *\n * @public\n */\nexport type TLResizeMode = 'scale_shape' | 'resize_bounds'\n\n/**\n * Info about a resize.\n * @param newPoint - The new local position of the shape.\n * @param handle - The handle being dragged.\n * @param mode - The type of resize.\n * @param scaleX - The scale in the x-axis.\n * @param scaleY - The scale in the y-axis.\n * @param initialBounds - The bounds of the shape at the start of the resize.\n * @param initialShape - The shape at the start of the resize.\n * @public\n */\nexport interface TLResizeInfo<T extends TLShape> {\n\tnewPoint: Vec\n\thandle: TLResizeHandle\n\tmode: TLResizeMode\n\tscaleX: number\n\tscaleY: number\n\tinitialBounds: Box\n\tinitialShape: T\n}\n\n/* -------------------- Dragging -------------------- */\n\n/** @public */\nexport interface TLHandleDragInfo<T extends TLShape> {\n\thandle: TLHandle\n\tisPrecise: boolean\n\tisCreatingShape: boolean\n\tinitial?: T | undefined\n}\n\n/* --------------------------------- Editing -------------------------------- */\n\n/** @public */\nexport interface TLEditStartInfo {\n\ttype:\n\t\t| 'press_enter'\n\t\t| 'click'\n\t\t| 'double-click'\n\t\t| 'double-click-edge'\n\t\t| 'double-click-corner'\n\t\t| 'click-header'\n\t\t| 'unknown'\n}\n"],
|
|
5
|
-
"mappings": "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
|
|
4
|
+
"sourcesContent": ["/* eslint-disable @typescript-eslint/no-unused-vars */\nimport { EMPTY_ARRAY } from '@tldraw/state'\nimport { LegacyMigrations, MigrationSequence } from '@tldraw/store'\nimport {\n\tRecordProps,\n\tTLAsset,\n\tTLHandle,\n\tTLParentId,\n\tTLPropsMigrations,\n\tTLShape,\n\tTLShapeCrop,\n\tTLShapeId,\n\tTLShapePartial,\n\tTLUnknownShape,\n\tVecModel,\n} from '@tldraw/tlschema'\nimport { TLFontFace } from '@tldraw/tlschema'\nimport { IndexKey } from '@tldraw/utils'\nimport { ReactElement } from 'react'\nimport { Box, SelectionHandle } from '../../primitives/Box'\nimport { Geometry2d } from '../../primitives/geometry/Geometry2d'\nimport { Vec } from '../../primitives/Vec'\nimport type { Editor } from '../Editor'\nimport { BoundsSnapGeometry } from '../managers/SnapManager/BoundsSnaps'\nimport { HandleSnapGeometry } from '../managers/SnapManager/HandleSnaps'\nimport { TLClickEventInfo } from '../types/event-types'\nimport { TLResizeHandle } from '../types/selection-types'\nimport { SvgExportContext } from '../types/SvgExportContext'\n\n/** @public */\nexport interface TLShapeUtilConstructor<T extends TLShape, U extends ShapeUtil<T> = ShapeUtil<T>> {\n\tnew (editor: Editor): U\n\ttype: T['type']\n\tprops?: RecordProps<T>\n\tmigrations?: LegacyMigrations | TLPropsMigrations | MigrationSequence\n\thandledAssetTypes?: readonly string[]\n}\n\n/**\n * Options passed to {@link ShapeUtil.canBind}. A binding that could be made. At least one of\n * `fromShape` or `toShape` will belong to this shape util.\n *\n * The shapes may be full {@link @tldraw/tlschema#TLShape} objects when available, or just\n * `{ type }` stubs when the shape hasn't been created yet (e.g. during arrow creation). Use\n * `'id' in shape` to check whether the full shape is available.\n *\n * @public\n */\nexport interface TLShapeUtilCanBindOpts<Shape extends TLShape = TLShape> {\n\t/** The shape referenced by the `fromId` of the binding, or a `{ type }` stub if unavailable. */\n\tfromShape: TLShape | { type: TLShape['type'] }\n\t/** The shape referenced by the `toId` of the binding, or a `{ type }` stub if unavailable. */\n\ttoShape: TLShape | { type: TLShape['type'] }\n\t/** The type of binding. */\n\tbindingType: string\n\t/**\n\t * The type of shape referenced by the `fromId` of the binding.\n\t * @deprecated Use `fromShape.type` instead.\n\t */\n\tfromShapeType: TLShape['type']\n\t/**\n\t * The type of shape referenced by the `toId` of the binding.\n\t * @deprecated Use `toShape.type` instead.\n\t */\n\ttoShapeType: TLShape['type']\n}\n\n/**\n * Options passed to {@link ShapeUtil.canBeLaidOut}.\n *\n * @public\n */\nexport interface TLShapeUtilCanBeLaidOutOpts {\n\t/** The type of action causing the layout. */\n\ttype?: 'align' | 'distribute' | 'pack' | 'stack' | 'flip' | 'stretch' | 'resize_to_bounds'\n\t/** The other shapes being laid out */\n\tshapes?: TLShape[]\n}\n\n/** Additional options for the {@link ShapeUtil.getGeometry} method.\n *\n * @public\n */\nexport interface TLGeometryOpts {\n\t/** The context in which the geometry is being requested. */\n\tcontext?: string\n}\n\n/** @public */\nexport interface TLShapeUtilCanvasSvgDef {\n\tkey: string\n\tcomponent: React.ComponentType\n}\n\n/**\n * Return type for {@link ShapeUtil.getIndicatorPath}. Can be either a simple Path2D\n * or an object with additional rendering info like clip paths for complex indicators.\n * @public\n */\nexport type TLIndicatorPath =\n\t| Path2D\n\t| {\n\t\t\tpath: Path2D\n\t\t\tclipPath?: Path2D\n\t\t\tadditionalPaths?: Path2D[]\n\t }\n\n/** @public */\nexport abstract class ShapeUtil<Shape extends TLShape = TLShape> {\n\t/** Configure this shape utils {@link ShapeUtil.options | `options`}. */\n\tstatic configure<T extends TLShapeUtilConstructor<any, any>>(\n\t\tthis: T,\n\t\toptions: T extends new (...args: any[]) => { options: infer Options } ? Partial<Options> : never\n\t): T {\n\t\t// @ts-expect-error -- typescript has no idea what's going on here but it's fine\n\t\treturn class extends this {\n\t\t\t// @ts-expect-error\n\t\t\toptions = { ...this.options, ...options }\n\t\t}\n\t}\n\n\tconstructor(public editor: Editor) {}\n\n\t/**\n\t * Options for this shape util. If you're implementing a custom shape util, you can override\n\t * this to provide customization options for your shape. If using an existing shape util, you\n\t * can customizing this by calling {@link ShapeUtil.configure}.\n\t */\n\toptions = {}\n\n\t/**\n\t * Props allow you to define the shape's properties in a way that the editor can understand.\n\t * This has two main uses:\n\t *\n\t * 1. Validation. Shapes will be validated using these props to stop bad data from being saved.\n\t * 2. Styles. Each {@link @tldraw/tlschema#StyleProp} in the props can be set on many shapes at\n\t * once, and will be remembered from one shape to the next.\n\t *\n\t * @example\n\t * ```tsx\n\t * import {T, TLBaseShape, TLDefaultColorStyle, DefaultColorStyle, ShapeUtil} from 'tldraw'\n\t *\n\t * type MyShape = TLBaseShape<'mine', {\n\t * color: TLDefaultColorStyle,\n\t * text: string,\n\t * }>\n\t *\n\t * class MyShapeUtil extends ShapeUtil<MyShape> {\n\t * static props = {\n\t * // we use tldraw's built-in color style:\n\t * color: DefaultColorStyle,\n\t * // validate that the text prop is a string:\n\t * text: T.string,\n\t * }\n\t * }\n\t * ```\n\t */\n\tstatic props?: RecordProps<TLUnknownShape>\n\n\t/**\n\t * Migrations allow you to make changes to a shape's props over time. Read the\n\t * {@link https://www.tldraw.dev/docs/persistence#Shape-props-migrations | shape prop migrations}\n\t * guide for more information.\n\t */\n\tstatic migrations?: LegacyMigrations | TLPropsMigrations | MigrationSequence\n\n\t/**\n\t * The type of the shape util, which should match the shape's type.\n\t *\n\t * @public\n\t */\n\tstatic type: string\n\n\t/**\n\t * The asset types that this shape can be created from.\n\t * When a file is dropped on the canvas, the editor finds the shape util\n\t * whose `handledAssetTypes` includes the asset's type and calls\n\t * {@link ShapeUtil.createShapeForAsset} to produce the shape.\n\t *\n\t * @public\n\t */\n\tstatic handledAssetTypes?: readonly string[]\n\n\t/**\n\t * Get the default props for a shape.\n\t *\n\t * @public\n\t */\n\tabstract getDefaultProps(): Shape['props']\n\n\t/**\n\t * Create a shape partial for placing an asset on the canvas.\n\t * Only called for shapes whose constructor declares matching\n\t * {@link ShapeUtil.handledAssetTypes | `handledAssetTypes`}.\n\t *\n\t * @param asset - The asset to create a shape for.\n\t * @param position - Where to place the shape.\n\t * @returns A shape partial, or null if this shape can't be created for the asset.\n\t * @public\n\t */\n\tcreateShapeForAsset?(asset: TLAsset, position: VecModel): TLShapePartial | null\n\n\t/**\n\t * Get the shape's geometry.\n\t *\n\t * @param shape - The shape.\n\t * @param opts - Additional options for the request.\n\t * @public\n\t */\n\tabstract getGeometry(shape: Shape, opts?: TLGeometryOpts): Geometry2d\n\n\t/**\n\t * Get a JSX element for the shape (as an HTML element).\n\t *\n\t * @param shape - The shape.\n\t * @public\n\t */\n\tabstract component(shape: Shape): any\n\n\t/**\n\t * Get a Path2D (or a richer object with clip/additional paths) for rendering the\n\t * shape's indicator on the canvas. Shapes that return `undefined` will not render\n\t * an indicator.\n\t *\n\t * For complex indicators that need clipping (e.g., arrows with labels), return an\n\t * object with `path`, `clipPath`, and `additionalPaths` properties.\n\t *\n\t * @param shape - The shape.\n\t * @returns A Path2D to stroke, or an object with clipping info, or undefined to skip.\n\t * @public\n\t */\n\tabstract getIndicatorPath(shape: Shape): TLIndicatorPath | undefined\n\n\t/**\n\t * Get JSX describing the shape's indicator (as an SVG element).\n\t *\n\t * @deprecated SVG indicators are no longer rendered. Override\n\t * {@link ShapeUtil.getIndicatorPath} instead. This stub is retained so legacy\n\t * subclasses that still call `super.indicator()` keep type-checking; new shapes\n\t * should not implement it.\n\t *\n\t * @param shape - The shape.\n\t * @public\n\t */\n\tindicator(_shape: Shape): any {\n\t\treturn null\n\t}\n\n\t/**\n\t * Get the font faces that should be rendered in the document in order for this shape to render\n\t * correctly.\n\t *\n\t * @param shape - The shape.\n\t * @public\n\t */\n\tgetFontFaces(shape: Shape): TLFontFace[] {\n\t\treturn EMPTY_ARRAY\n\t}\n\n\t/**\n\t * Whether the shape can be snapped to by another shape.\n\t *\n\t * @param shape - The shape.\n\t * @public\n\t */\n\tcanSnap(shape: Shape): boolean {\n\t\treturn true\n\t}\n\n\t/**\n\t * Whether the shape can be tabbed to.\n\t *\n\t * @param shape - The shape.\n\t * @public\n\t */\n\tcanTabTo(shape: Shape): boolean {\n\t\treturn true\n\t}\n\n\t/**\n\t * Whether the shape can be scrolled while editing.\n\t *\n\t * @public\n\t */\n\tcanScroll(shape: Shape): boolean {\n\t\treturn false\n\t}\n\n\t/**\n\t * Whether the shape can be bound to. See {@link TLShapeUtilCanBindOpts} for details.\n\t *\n\t * @public\n\t */\n\tcanBind(opts: TLShapeUtilCanBindOpts): boolean {\n\t\treturn true\n\t}\n\n\t/**\n\t * Whether the shape can be double clicked to edit.\n\t *\n\t * @public\n\t */\n\tcanEdit(shape: Shape, info: TLEditStartInfo): boolean {\n\t\treturn false\n\t}\n\n\t/**\n\t * Whether the shape can be resized.\n\t *\n\t * @public\n\t */\n\tcanResize(shape: Shape): boolean {\n\t\treturn true\n\t}\n\n\t/**\n\t * When the shape is resized, whether the shape's children should also be resized.\n\t *\n\t * @public\n\t */\n\tcanResizeChildren(shape: Shape): boolean {\n\t\treturn true\n\t}\n\n\t/**\n\t * Whether the shape can be edited in read-only mode.\n\t *\n\t * @public\n\t */\n\tcanEditInReadonly(shape: Shape): boolean {\n\t\treturn false\n\t}\n\n\t/**\n\t * Whether the shape can be edited while locked or while an ancestor is locked.\n\t *\n\t * @public\n\t */\n\tcanEditWhileLocked(shape: Shape): boolean {\n\t\treturn false\n\t}\n\n\t/**\n\t * Whether the shape can be cropped.\n\t *\n\t * @public\n\t */\n\tcanCrop(shape: Shape): boolean {\n\t\treturn false\n\t}\n\n\t/**\n\t * Whether the shape can participate in layout functions such as alignment or distribution.\n\t *\n\t * @param shape - The shape.\n\t * @param info - Additional context information: the type of action causing the layout and the\n\t * @public\n\t *\n\t * @public\n\t */\n\tcanBeLaidOut(shape: Shape, info: TLShapeUtilCanBeLaidOutOpts): boolean {\n\t\treturn true\n\t}\n\n\t/**\n\t * Whether this shape can be culled. By default, shapes are culled for\n\t * performance reasons when they are outside of the viewport. Culled shapes are still rendered\n\t * to the DOM, but have their `display` property set to `none`.\n\t *\n\t * @param shape - The shape.\n\t */\n\tcanCull(shape: Shape): boolean {\n\t\treturn true\n\t}\n\n\t/**\n\t * Does this shape provide a background for its children? If this is true,\n\t * then any children with a `renderBackground` method will have their\n\t * backgrounds rendered _above_ this shape. Otherwise, the children's\n\t * backgrounds will be rendered above either the next ancestor that provides\n\t * a background, or the canvas background.\n\t *\n\t * @internal\n\t */\n\tprovidesBackgroundForChildren(shape: Shape): boolean {\n\t\treturn false\n\t}\n\n\t/**\n\t * Get the clip path to apply to this shape's children.\n\t *\n\t * The returned points should define the **inner** clip boundary - the area where\n\t * children will be visible. If your shape has a stroke, you should inset the clip\n\t * path by half the stroke width so children are clipped to the inner edge of the\n\t * stroke rather than its center line.\n\t *\n\t * @example\n\t * ```ts\n\t * override getClipPath(shape: MyShape): Vec[] | undefined {\n\t * const strokeWidth = 2\n\t * const inset = strokeWidth / 2\n\t * // Return points inset by half the stroke width\n\t * return [\n\t * new Vec(inset, inset),\n\t * new Vec(shape.props.w - inset, inset),\n\t * new Vec(shape.props.w - inset, shape.props.h - inset),\n\t * new Vec(inset, shape.props.h - inset),\n\t * ]\n\t * }\n\t * ```\n\t *\n\t * @param shape - The shape to get the clip path for\n\t * @returns Array of points defining the clipping polygon in local coordinates, or undefined if no clipping\n\t * @public\n\t */\n\tgetClipPath?(shape: Shape): Vec[] | undefined\n\n\t/**\n\t * Whether a specific child shape should be clipped by this shape.\n\t * Only called if getClipPath returns a valid polygon.\n\t *\n\t * If not defined, the default behavior is to clip all children.\n\t *\n\t * @param child - The child shape to check\n\t * @returns boolean indicating if this child should be clipped\n\t * @public\n\t */\n\tshouldClipChild?(child: TLShape): boolean\n\n\t/**\n\t * Whether a specific shape should hide in the minimap.\n\t *\n\t * If not defined, the default behavior is to show all shapes in the minimap.\n\t *\n\t * @returns boolean indicating if this shape should hide in the minimap\n\t * @public\n\t */\n\thideInMinimap?(shape: Shape): boolean\n\n\t/**\n\t * Whether the shape should hide its resize handles when selected.\n\t *\n\t * @public\n\t */\n\thideResizeHandles(shape: Shape): boolean {\n\t\treturn false\n\t}\n\n\t/**\n\t * Whether the shape should hide its rotation handles when selected.\n\t *\n\t * @public\n\t */\n\thideRotateHandle(shape: Shape): boolean {\n\t\treturn false\n\t}\n\n\t/**\n\t * Whether the shape should hide its selection bounds background when selected.\n\t *\n\t * @public\n\t */\n\thideSelectionBoundsBg(shape: Shape): boolean {\n\t\treturn false\n\t}\n\n\t/**\n\t * Whether the shape should hide its selection bounds foreground when selected.\n\t *\n\t * @public\n\t */\n\thideSelectionBoundsFg(shape: Shape): boolean {\n\t\treturn false\n\t}\n\n\t/**\n\t * Whether the shape's aspect ratio is locked.\n\t *\n\t * @public\n\t */\n\tisAspectRatioLocked(shape: Shape): boolean {\n\t\treturn false\n\t}\n\n\t/**\n\t * Whether the shape behaves like a frame \u2014 a container that has child shapes,\n\t * requires full-brush selection, blocks erasure from inside, etc.\n\t *\n\t * @param shape - The shape.\n\t * @public\n\t */\n\tisFrameLike(_shape: Shape): boolean {\n\t\treturn false\n\t}\n\n\t/**\n\t * By default, the bounds of an image export are the bounds of all the shapes it contains, plus\n\t * some padding. If an export includes a shape where `isExportBoundsContainer` is true, then the\n\t * padding is skipped _if the bounds of that shape contains all the other shapes_. This is\n\t * useful in cases like annotating on top of an image, where you usually want to avoid extra\n\t * padding around the image if you don't need it.\n\t *\n\t * @param shape - The shape to check\n\t * @returns True if this shape should be treated as an export bounds container\n\t */\n\tisExportBoundsContainer(shape: Shape): boolean {\n\t\treturn false\n\t}\n\n\t/**\n\t * Get a JSX element for the shape (as an HTML element) to be rendered as part of the canvas background - behind any other shape content.\n\t *\n\t * @param shape - The shape.\n\t * @internal\n\t */\n\tbackgroundComponent?(shape: Shape): any\n\n\t/**\n\t * Get the interpolated props for an animating shape. This is an optional method.\n\t *\n\t * @example\n\t *\n\t * ```ts\n\t * util.getInterpolatedProps?.(startShape, endShape, t)\n\t * ```\n\t *\n\t * @param startShape - The initial shape.\n\t * @param endShape - The initial shape.\n\t * @param progress - The normalized progress between zero (start) and 1 (end).\n\t * @public\n\t */\n\tgetInterpolatedProps?(startShape: Shape, endShape: Shape, progress: number): Shape['props']\n\n\t/**\n\t * Get an array of handle models for the shape. This is an optional method.\n\t *\n\t * @example\n\t *\n\t * ```ts\n\t * util.getHandles?.(myShape)\n\t * ```\n\t *\n\t * @param shape - The shape.\n\t * @public\n\t */\n\tgetHandles?(shape: Shape): TLHandle[]\n\n\t/**\n\t * Get whether the shape can receive children of a given type. Used by the drag and drop system\n\t * to decide whether {@link ShapeUtil.onDragShapesIn} should fire when a shape of the given type\n\t * is dragged over this one.\n\t *\n\t * @param shape - The shape.\n\t * @param type - The shape type.\n\t * @public\n\t */\n\tcanReceiveNewChildrenOfType(shape: Shape, type: TLShape['type']) {\n\t\treturn false\n\t}\n\n\t/**\n\t * Get whether children of a given type can be removed from this shape. Used by the drag and\n\t * drop system to decide whether {@link ShapeUtil.onDragShapesOut} should fire when a child of\n\t * the given type is dragged out of this shape, and by `kickoutOccludedShapes` to decide\n\t * whether to auto-reparent a child of the given type when it has moved outside this shape's\n\t * geometry. Returning `false` therefore \"pins\" matching children \u2014 they stay parented to this\n\t * shape even when dragged or moved outside it. Defaults to `true`.\n\t *\n\t * @param shape - The shape.\n\t * @param type - The shape type.\n\t * @public\n\t */\n\tcanRemoveChildrenOfType(shape: Shape, type: TLShape['type']) {\n\t\treturn true\n\t}\n\n\t/**\n\t * Get the shape as an SVG object.\n\t *\n\t * @param shape - The shape.\n\t * @param ctx - The export context for the SVG - used for adding e.g. \\<def\\>s\n\t * @returns An SVG element.\n\t * @public\n\t */\n\ttoSvg?(shape: Shape, ctx: SvgExportContext): ReactElement | null | Promise<ReactElement | null>\n\n\t/**\n\t * Get the shape's background layer as an SVG object.\n\t *\n\t * @param shape - The shape.\n\t * @param ctx - ctx - The export context for the SVG - used for adding e.g. \\<def\\>s\n\t * @returns An SVG element.\n\t * @public\n\t */\n\ttoBackgroundSvg?(\n\t\tshape: Shape,\n\t\tctx: SvgExportContext\n\t): ReactElement | null | Promise<ReactElement | null>\n\n\t/** @internal */\n\texpandSelectionOutlinePx(shape: Shape): number | Box {\n\t\treturn 0\n\t}\n\n\t/**\n\t * Return elements to be added to the \\<defs\\> section of the canvases SVG context. This can be\n\t * used to define SVG content (e.g. patterns & masks) that can be referred to by ID from svg\n\t * elements returned by `component`.\n\t *\n\t * Each def should have a unique `key`. If multiple defs from different shapes all have the same\n\t * key, only one will be used.\n\t */\n\tgetCanvasSvgDefs(): TLShapeUtilCanvasSvgDef[] {\n\t\treturn []\n\t}\n\n\t/**\n\t * Get the geometry to use when snapping to this this shape in translate/resize operations. See\n\t * {@link BoundsSnapGeometry} for details.\n\t */\n\tgetBoundsSnapGeometry(shape: Shape): BoundsSnapGeometry {\n\t\treturn {}\n\t}\n\n\t/**\n\t * Get the geometry to use when snapping handles to this shape. See {@link HandleSnapGeometry}\n\t * for details.\n\t */\n\tgetHandleSnapGeometry(shape: Shape): HandleSnapGeometry {\n\t\treturn {}\n\t}\n\n\tgetText(shape: Shape): string | undefined {\n\t\treturn undefined\n\t}\n\n\t/**\n\t * Return user IDs referenced in shape-specific props.\n\t * Used when copying shapes to include referenced users on the clipboard.\n\t * Override this if your shape stores user IDs in custom props.\n\t *\n\t * @public\n\t */\n\tgetReferencedUserIds(shape: Shape): string[] {\n\t\treturn EMPTY_ARRAY\n\t}\n\n\tgetAriaDescriptor(shape: Shape): string | undefined {\n\t\treturn undefined\n\t}\n\n\t// Events\n\n\t/**\n\t * A callback called just before a shape is created. This method provides a last chance to modify\n\t * the created shape.\n\t *\n\t * @example\n\t *\n\t * ```ts\n\t * onBeforeCreate = (next) => {\n\t * \treturn { ...next, x: next.x + 1 }\n\t * }\n\t * ```\n\t *\n\t * @param next - The next shape.\n\t * @returns The next shape or void.\n\t * @public\n\t */\n\tonBeforeCreate?(next: Shape): Shape | void\n\n\t/**\n\t * A callback called just before a shape is updated. This method provides a last chance to modify\n\t * the updated shape.\n\t *\n\t * @example\n\t *\n\t * ```ts\n\t * onBeforeUpdate = (prev, next) => {\n\t * \tif (prev.x === next.x) {\n\t * \t\treturn { ...next, x: next.x + 1 }\n\t * \t}\n\t * }\n\t * ```\n\t *\n\t * @param prev - The previous shape.\n\t * @param next - The next shape.\n\t * @returns The next shape or void.\n\t * @public\n\t */\n\tonBeforeUpdate?(prev: Shape, next: Shape): Shape | void\n\n\t/**\n\t * A callback called when a shape changes from a crop.\n\t *\n\t * @param shape - The shape at the start of the crop.\n\t * @param info - Info about the crop.\n\t * @returns A change to apply to the shape, or void.\n\t * @public\n\t */\n\tonCrop?(\n\t\tshape: Shape,\n\t\tinfo: TLCropInfo<Shape>\n\t): Omit<TLShapePartial<Shape>, 'id' | 'type'> | undefined | void\n\n\t/**\n\t * A callback called when some other shapes are dragged into this one. This fires when the shapes are dragged over the shape for the first time.\n\t *\n\t * @param shape - The shape.\n\t * @param shapes - The shapes that are being dragged in.\n\t * @public\n\t */\n\tonDragShapesIn?(shape: Shape, shapes: TLShape[], info: TLDragShapesInInfo): void\n\n\t/**\n\t * A callback called when some other shapes are dragged over this one. This fires when the shapes are dragged over the shape for the first time (after the onDragShapesIn callback), and again on every update while the shapes are being dragged.\n\t *\n\t * @example\n\t *\n\t * ```ts\n\t * onDragShapesOver = (shape, shapes) => {\n\t * \tthis.editor.reparentShapes(shapes, shape.id)\n\t * }\n\t * ```\n\t *\n\t * @param shape - The shape.\n\t * @param shapes - The shapes that are being dragged over this one.\n\t * @public\n\t */\n\tonDragShapesOver?(shape: Shape, shapes: TLShape[], info: TLDragShapesOverInfo): void\n\n\t/**\n\t * A callback called when some other shapes are dragged out of this one.\n\t *\n\t * @param shape - The shape.\n\t * @param shapes - The shapes that are being dragged out.\n\t * @public\n\t */\n\tonDragShapesOut?(shape: Shape, shapes: TLShape[], info: TLDragShapesOutInfo): void\n\n\t/**\n\t * A callback called when some other shapes are dropped over this one.\n\t *\n\t * @param shape - The shape.\n\t * @param shapes - The shapes that are being dropped over this one.\n\t * @public\n\t */\n\tonDropShapesOver?(shape: Shape, shapes: TLShape[], info: TLDropShapesOverInfo): void\n\n\t/**\n\t * A callback called when a shape starts being resized.\n\t *\n\t * @param shape - The shape.\n\t * @returns A change to apply to the shape, or void.\n\t * @public\n\t */\n\tonResizeStart?(shape: Shape): TLShapePartial<Shape> | void\n\n\t/**\n\t * A callback called when a shape changes from a resize.\n\t *\n\t * @param shape - The shape at the start of the resize.\n\t * @param info - Info about the resize.\n\t * @returns A change to apply to the shape, or void.\n\t * @public\n\t */\n\tonResize?(\n\t\tshape: Shape,\n\t\tinfo: TLResizeInfo<Shape>\n\t): Omit<TLShapePartial<Shape>, 'id' | 'type'> | undefined | void\n\n\t/**\n\t * A callback called when a shape finishes resizing.\n\t *\n\t * @param initial - The shape at the start of the resize.\n\t * @param current - The current shape.\n\t * @returns A change to apply to the shape, or void.\n\t * @public\n\t */\n\tonResizeEnd?(initial: Shape, current: Shape): TLShapePartial<Shape> | void\n\n\t/**\n\t * A callback called when a shape resize is cancelled.\n\t *\n\t * @param initial - The shape at the start of the resize.\n\t * @param current - The current shape.\n\t * @public\n\t */\n\tonResizeCancel?(initial: Shape, current: Shape): void\n\n\t/**\n\t * A callback called when a shape starts being translated.\n\t *\n\t * @param shape - The shape.\n\t * @returns A change to apply to the shape, or void.\n\t * @public\n\t */\n\tonTranslateStart?(shape: Shape): TLShapePartial<Shape> | void\n\n\t/**\n\t * A callback called when a shape changes from a translation.\n\t *\n\t * @param initial - The shape at the start of the translation.\n\t * @param current - The current shape.\n\t * @returns A change to apply to the shape, or void.\n\t * @public\n\t */\n\tonTranslate?(initial: Shape, current: Shape): TLShapePartial<Shape> | void\n\n\t/**\n\t * A callback called when a shape finishes translating.\n\t *\n\t * @param initial - The shape at the start of the translation.\n\t * @param current - The current shape.\n\t * @returns A change to apply to the shape, or void.\n\t * @public\n\t */\n\tonTranslateEnd?(initial: Shape, current: Shape): TLShapePartial<Shape> | void\n\n\t/**\n\t * A callback called when a shape translation is cancelled.\n\t *\n\t * @param initial - The shape at the start of the translation.\n\t * @param current - The current shape.\n\t * @public\n\t */\n\tonTranslateCancel?(initial: Shape, current: Shape): void\n\n\t/**\n\t * A callback called when a shape's handle starts being dragged.\n\t *\n\t * @param shape - The shape.\n\t * @param info - An object containing the handle and whether the handle is 'precise' or not.\n\t * @returns A change to apply to the shape, or void.\n\t * @public\n\t */\n\tonHandleDragStart?(shape: Shape, info: TLHandleDragInfo<Shape>): TLShapePartial<Shape> | void\n\n\t/**\n\t * A callback called when a shape's handle changes.\n\t *\n\t * @param shape - The current shape.\n\t * @param info - An object containing the handle and whether the handle is 'precise' or not.\n\t * @returns A change to apply to the shape, or void.\n\t * @public\n\t */\n\tonHandleDrag?(shape: Shape, info: TLHandleDragInfo<Shape>): TLShapePartial<Shape> | void\n\n\t/**\n\t * A callback called when a shape's handle finishes being dragged.\n\t *\n\t * @param current - The current shape.\n\t * @param info - An object containing the handle and whether the handle is 'precise' or not.\n\t * @returns A change to apply to the shape, or void.\n\t * @public\n\t */\n\tonHandleDragEnd?(current: Shape, info: TLHandleDragInfo<Shape>): TLShapePartial<Shape> | void\n\n\t/**\n\t * A callback called when a shape's handle drag is cancelled.\n\t *\n\t * @param current - The current shape.\n\t * @param info - An object containing the handle and whether the handle is 'precise' or not.\n\t * @public\n\t */\n\tonHandleDragCancel?(current: Shape, info: TLHandleDragInfo<Shape>): void\n\n\t/**\n\t * A callback called when a shape starts being rotated.\n\t *\n\t * @param shape - The shape.\n\t * @returns A change to apply to the shape, or void.\n\t * @public\n\t */\n\tonRotateStart?(shape: Shape): TLShapePartial<Shape> | void\n\n\t/**\n\t * A callback called when a shape changes from a rotation.\n\t *\n\t * @param initial - The shape at the start of the rotation.\n\t * @param current - The current shape.\n\t * @returns A change to apply to the shape, or void.\n\t * @public\n\t */\n\tonRotate?(initial: Shape, current: Shape): TLShapePartial<Shape> | void\n\n\t/**\n\t * A callback called when a shape finishes rotating.\n\t *\n\t * @param initial - The shape at the start of the rotation.\n\t * @param current - The current shape.\n\t * @returns A change to apply to the shape, or void.\n\t * @public\n\t */\n\tonRotateEnd?(initial: Shape, current: Shape): TLShapePartial<Shape> | void\n\n\t/**\n\t * A callback called when a shape rotation is cancelled.\n\t *\n\t * @param initial - The shape at the start of the rotation.\n\t * @param current - The current shape.\n\t * @public\n\t */\n\tonRotateCancel?(initial: Shape, current: Shape): void\n\n\t/**\n\t * Not currently used.\n\t *\n\t * @internal\n\t */\n\tonBindingChange?(shape: Shape): TLShapePartial<Shape> | void\n\n\t/**\n\t * A callback called when a shape's children change.\n\t *\n\t * @param shape - The shape.\n\t * @returns An array of shape updates, or void.\n\t * @public\n\t */\n\tonChildrenChange?(shape: Shape): TLShapePartial[] | void\n\n\t/**\n\t * A callback called when a shape's handle is double clicked.\n\t *\n\t * @param shape - The shape.\n\t * @param handle - The handle that is double-clicked.\n\t * @returns A change to apply to the shape, or void.\n\t * @public\n\t */\n\tonDoubleClickHandle?(shape: Shape, handle: TLHandle): TLShapePartial<Shape> | void\n\n\t/**\n\t * A callback called when a shape's edge is double clicked.\n\t *\n\t * @param shape - The shape.\n\t * @param info - Info about the edge.\n\t * @returns A change to apply to the shape, or void.\n\t * @public\n\t */\n\tonDoubleClickEdge?(shape: Shape, info: TLClickEventInfo): TLShapePartial<Shape> | void\n\n\t/**\n\t * A callback called when a shape's corner is double clicked.\n\t *\n\t * @param shape - The shape.\n\t * @param info - Info about the corner.\n\t * @returns A change to apply to the shape, or void.\n\t * @public\n\t */\n\tonDoubleClickCorner?(shape: Shape, info: TLClickEventInfo): TLShapePartial<Shape> | void\n\n\t/**\n\t * A callback called when a shape is double clicked.\n\t *\n\t * @param shape - The shape.\n\t * @returns A change to apply to the shape, or void.\n\t * @public\n\t */\n\tonDoubleClick?(shape: Shape): TLShapePartial<Shape> | void\n\n\t/**\n\t * A callback called when a shape is clicked.\n\t *\n\t * @param shape - The shape.\n\t * @returns A change to apply to the shape, or void.\n\t * @public\n\t */\n\tonClick?(shape: Shape): TLShapePartial<Shape> | void\n\n\t/**\n\t * A callback called when a shape starts being edited.\n\t *\n\t * @param shape - The shape.\n\t * @public\n\t */\n\tonEditStart?(shape: Shape): void\n\n\t/**\n\t * A callback called when a shape finishes being edited.\n\t *\n\t * @param shape - The shape.\n\t * @public\n\t */\n\tonEditEnd?(shape: Shape): void\n\n\t/**\n\t * Provide an app-owned element to be rendered inside the shape, alongside the output of\n\t * {@link ShapeUtil.component}. While the shape remains mounted, tldraw guarantees the\n\t * element keeps the same DOM position: it is never unmounted, recreated, or relocated by\n\t * reordering, reparenting, culling, or re-renders. When adopting the element, tldraw uses\n\t * `Node.moveBefore` where available so stateful content like cross-origin iframes keeps\n\t * its state across the move, falling back to `appendChild` elsewhere.\n\t *\n\t * Pair this with {@link ShapeUtil.onReleaseAppOwnedElement} to reclaim the element before\n\t * the shape or editor unmounts.\n\t *\n\t * This is called once per shape mount, not on every prop change, so the adopted element is\n\t * not refreshed when the shape's props change. If the element's content depends on props,\n\t * return a stable element and mutate it in place (for example from {@link ShapeUtil.component}\n\t * or an effect) rather than returning a different element.\n\t *\n\t * @param shape - The shape.\n\t * @returns The element to adopt, or null to render nothing.\n\t * @public\n\t */\n\tgetAppOwnedElement?(shape: Shape): HTMLElement | null\n\n\t/**\n\t * A callback called before the shape's app-owned element slot is destroyed: when the shape\n\t * unmounts (for example when it is deleted or the current page changes) or when the whole\n\t * editor unmounts, including error teardown. The slot is still connected to the document\n\t * when this is called, so the app can move the element to another connected parent with\n\t * `Node.moveBefore` to preserve its state. An element left in the slot is destroyed along\n\t * with it.\n\t *\n\t * This is not only a \"shape deleted\" signal: it also fires on page changes, editor unmount,\n\t * and (in dev under React StrictMode) on the throwaway mount/unmount cycle while the shape\n\t * still exists. Make it safe to call when the shape is still present, and check the store\n\t * (`this.editor.getShape(shape.id)`) if you need to distinguish deletion from a remount.\n\t *\n\t * @param shape - The shape.\n\t * @param element - The element returned by {@link ShapeUtil.getAppOwnedElement}.\n\t * @public\n\t */\n\tonReleaseAppOwnedElement?(shape: Shape, element: HTMLElement): void\n}\n\n/**\n * Info about a crop.\n * @param handle - The handle being dragged.\n * @param change - The distance the handle is moved.\n * @param initialShape - The shape at the start of the resize.\n * @public\n */\nexport interface TLCropInfo<T extends TLShape> {\n\thandle: SelectionHandle\n\tchange: Vec\n\tcrop: TLShapeCrop\n\tuncroppedSize: { w: number; h: number }\n\tinitialShape: T\n\taspectRatioLocked?: boolean\n}\n\n/** @public */\nexport interface TLDragShapesInInfo {\n\tinitialDraggingOverShapeId: TLShapeId | null\n\tprevDraggingOverShapeId: TLShapeId | null\n\tinitialParentIds: Map<TLShapeId, TLParentId>\n\tinitialIndices: Map<TLShapeId, IndexKey>\n}\n\n/** @public */\nexport interface TLDragShapesOverInfo {\n\tinitialDraggingOverShapeId: TLShapeId | null\n\tinitialParentIds: Map<TLShapeId, TLParentId>\n\tinitialIndices: Map<TLShapeId, IndexKey>\n}\n\n/** @public */\nexport interface TLDragShapesOutInfo {\n\tnextDraggingOverShapeId: TLShapeId | null\n\tinitialDraggingOverShapeId: TLShapeId | null\n\tinitialParentIds: Map<TLShapeId, TLParentId>\n\tinitialIndices: Map<TLShapeId, IndexKey>\n}\n\n/** @public */\nexport interface TLDropShapesOverInfo {\n\tinitialDraggingOverShapeId: TLShapeId | null\n\tinitialParentIds: Map<TLShapeId, TLParentId>\n\tinitialIndices: Map<TLShapeId, IndexKey>\n}\n\n/**\n * The type of resize.\n *\n * 'scale_shape' - The shape is being scaled, usually as part of a larger selection.\n *\n * 'resize_bounds' - The user is directly manipulating an individual shape's bounds using a resize\n * handle. It is up to shape util implementers to decide how they want to handle the two\n * situations.\n *\n * @public\n */\nexport type TLResizeMode = 'scale_shape' | 'resize_bounds'\n\n/**\n * Info about a resize.\n * @param newPoint - The new local position of the shape.\n * @param handle - The handle being dragged.\n * @param mode - The type of resize.\n * @param scaleX - The scale in the x-axis.\n * @param scaleY - The scale in the y-axis.\n * @param initialBounds - The bounds of the shape at the start of the resize.\n * @param initialShape - The shape at the start of the resize.\n * @public\n */\nexport interface TLResizeInfo<T extends TLShape> {\n\tnewPoint: Vec\n\thandle: TLResizeHandle\n\tmode: TLResizeMode\n\tscaleX: number\n\tscaleY: number\n\tinitialBounds: Box\n\tinitialShape: T\n}\n\n/* -------------------- Dragging -------------------- */\n\n/** @public */\nexport interface TLHandleDragInfo<T extends TLShape> {\n\thandle: TLHandle\n\tisPrecise: boolean\n\tisCreatingShape: boolean\n\tinitial?: T | undefined\n}\n\n/* --------------------------------- Editing -------------------------------- */\n\n/** @public */\nexport interface TLEditStartInfo {\n\ttype:\n\t\t| 'press_enter'\n\t\t| 'click'\n\t\t| 'double-click'\n\t\t| 'double-click-edge'\n\t\t| 'double-click-corner'\n\t\t| 'click-header'\n\t\t| 'unknown'\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|