@tldraw/editor 3.8.0-canary.d0d3a3c316fd → 3.8.0-canary.d14754ca7629

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/dist-cjs/index.d.ts +114 -56
  2. package/dist-cjs/index.js +9 -8
  3. package/dist-cjs/index.js.map +2 -2
  4. package/dist-cjs/lib/config/TLSessionStateSnapshot.js.map +2 -2
  5. package/dist-cjs/lib/editor/Editor.js +6 -3
  6. package/dist-cjs/lib/editor/Editor.js.map +2 -2
  7. package/dist-cjs/lib/editor/managers/SnapManager/BoundsSnaps.js.map +2 -2
  8. package/dist-cjs/lib/editor/shapes/ShapeUtil.js +13 -0
  9. package/dist-cjs/lib/editor/shapes/ShapeUtil.js.map +2 -2
  10. package/dist-cjs/lib/editor/types/emit-types.js.map +1 -1
  11. package/dist-cjs/lib/editor/types/external-content.js.map +1 -1
  12. package/dist-cjs/lib/exports/StyleEmbedder.js.map +2 -2
  13. package/dist-cjs/lib/hooks/useDocumentEvents.js +1 -3
  14. package/dist-cjs/lib/hooks/useDocumentEvents.js.map +2 -2
  15. package/dist-cjs/lib/hooks/usePassThroughWheelEvents.js +4 -0
  16. package/dist-cjs/lib/hooks/usePassThroughWheelEvents.js.map +3 -3
  17. package/dist-cjs/lib/options.js +1 -2
  18. package/dist-cjs/lib/options.js.map +2 -2
  19. package/dist-cjs/lib/utils/dom.js +6 -0
  20. package/dist-cjs/lib/utils/dom.js.map +2 -2
  21. package/dist-cjs/version.js +3 -3
  22. package/dist-cjs/version.js.map +1 -1
  23. package/dist-esm/index.d.mts +114 -56
  24. package/dist-esm/index.mjs +3 -1
  25. package/dist-esm/index.mjs.map +2 -2
  26. package/dist-esm/lib/config/TLSessionStateSnapshot.mjs.map +2 -2
  27. package/dist-esm/lib/editor/Editor.mjs +6 -3
  28. package/dist-esm/lib/editor/Editor.mjs.map +2 -2
  29. package/dist-esm/lib/editor/managers/SnapManager/BoundsSnaps.mjs.map +2 -2
  30. package/dist-esm/lib/editor/shapes/ShapeUtil.mjs +13 -0
  31. package/dist-esm/lib/editor/shapes/ShapeUtil.mjs.map +2 -2
  32. package/dist-esm/lib/exports/StyleEmbedder.mjs.map +2 -2
  33. package/dist-esm/lib/hooks/useDocumentEvents.mjs +2 -4
  34. package/dist-esm/lib/hooks/useDocumentEvents.mjs.map +2 -2
  35. package/dist-esm/lib/hooks/usePassThroughWheelEvents.mjs +4 -0
  36. package/dist-esm/lib/hooks/usePassThroughWheelEvents.mjs.map +3 -3
  37. package/dist-esm/lib/options.mjs +1 -2
  38. package/dist-esm/lib/options.mjs.map +2 -2
  39. package/dist-esm/lib/utils/dom.mjs +6 -0
  40. package/dist-esm/lib/utils/dom.mjs.map +2 -2
  41. package/dist-esm/version.mjs +3 -3
  42. package/dist-esm/version.mjs.map +1 -1
  43. package/package.json +20 -20
  44. package/src/index.ts +16 -1
  45. package/src/lib/config/TLSessionStateSnapshot.ts +3 -1
  46. package/src/lib/editor/Editor.ts +22 -20
  47. package/src/lib/editor/managers/SnapManager/BoundsSnaps.ts +4 -4
  48. package/src/lib/editor/shapes/ShapeUtil.ts +19 -0
  49. package/src/lib/editor/types/emit-types.ts +1 -0
  50. package/src/lib/editor/types/external-content.ts +104 -50
  51. package/src/lib/exports/StyleEmbedder.ts +1 -1
  52. package/src/lib/hooks/useDocumentEvents.ts +2 -11
  53. package/src/lib/hooks/usePassThroughWheelEvents.ts +7 -0
  54. package/src/lib/options.ts +3 -6
  55. package/src/lib/utils/dom.ts +12 -0
  56. package/src/version.ts +3 -3
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/lib/editor/managers/SnapManager/BoundsSnaps.ts"],
4
- "sourcesContent": ["import { computed } from '@tldraw/state'\nimport { TLShape, TLShapeId, VecModel } from '@tldraw/tlschema'\nimport { assertExists, dedupe, uniqueId } from '@tldraw/utils'\nimport {\n\tBox,\n\tSelectionCorner,\n\tSelectionEdge,\n\tflipSelectionHandleX,\n\tflipSelectionHandleY,\n\tisSelectionCorner,\n} from '../../../primitives/Box'\nimport { Mat } from '../../../primitives/Mat'\nimport { Vec } from '../../../primitives/Vec'\nimport { rangeIntersection, rangesOverlap } from '../../../primitives/utils'\nimport { Editor } from '../../Editor'\nimport {\n\tGapsSnapIndicator,\n\tPointsSnapIndicator,\n\tSnapData,\n\tSnapIndicator,\n\tSnapManager,\n} from './SnapManager'\n\n/**\n * When moving or resizing shapes, the bounds of the shape can snap to key geometry on other nearby\n * shapes. Customize how a shape snaps to others with {@link ShapeUtil.getBoundsSnapGeometry}.\n *\n * @public\n */\nexport interface BoundsSnapGeometry {\n\t/**\n\t * Points that this shape will snap to. By default, this will be the corners and center of the\n\t * shapes bounding box. To disable snapping to a specific point, use an empty array.\n\t */\n\tpoints?: VecModel[]\n}\n\n/** @public */\nexport interface BoundsSnapPoint {\n\tid: string\n\tx: number\n\ty: number\n\thandle?: SelectionCorner\n}\n\ninterface SnapPair {\n\tthisPoint: BoundsSnapPoint\n\totherPoint: BoundsSnapPoint\n}\n\ninterface NearestPointsSnap {\n\t// selection snaps to a nearby snap point\n\ttype: 'points'\n\tpoints: SnapPair\n\tnudge: number\n}\n\ntype NearestSnap =\n\t| NearestPointsSnap\n\t| {\n\t\t\t// selection snaps to the center of a gap\n\t\t\ttype: 'gap_center'\n\t\t\tgap: Gap\n\t\t\tnudge: number\n\t }\n\t| {\n\t\t\t// selection snaps to create a new gap of equal size to another gap\n\t\t\t// on the opposite side of some shape\n\t\t\ttype: 'gap_duplicate'\n\t\t\tgap: Gap\n\t\t\tprotrusionDirection: 'left' | 'right' | 'top' | 'bottom'\n\t\t\tnudge: number\n\t }\n\ninterface GapNode {\n\tid: TLShapeId\n\tpageBounds: Box\n}\n\ninterface Gap {\n\t// e.g.\n\t// start\n\t// edge \u2502 breadth\n\t// \u2502 intersection\n\t// \u25BC [40,100] end\n\t// \u2502 \u2502 edge\n\t// \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u2502 100,0 \u2502 \u2502\n\t// \u2502 \u2502 \u2502 \u25BC \u25BC\n\t// \u2502 \u2502 \u2502\n\t// \u2502 start \u2502 \u2502 \u2502 200,40 \u2502 \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n\t// \u2502 node \u2502 \u2502 \u2502 \u2502 \u2502 \u2502\n\t// \u2502 \u2502 \u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524 \u2502 end \u2502\n\t// \u2502 \u2502 \u2502 \u2502 \u2502 \u2502 node \u2502\n\t// \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2502 100,100 \u2502 \u2502 \u2502 \u2502\n\t// \u2502 \u2502 \u2502\n\t// 200,120 \u2502 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n\t//\n\t// length 100\n\t// \u25C4\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u25BA\n\tstartNode: GapNode\n\tendNode: GapNode\n\tstartEdge: [Vec, Vec]\n\tendEdge: [Vec, Vec]\n\tlength: number\n\tbreadthIntersection: [number, number]\n}\n\nconst round = (x: number) => {\n\t// round numbers to avoid glitches for floating point rounding errors\n\tconst decimalPlacesTolerance = 8\n\treturn Math.round(x * 10 ** decimalPlacesTolerance) / 10 ** decimalPlacesTolerance\n}\n\nfunction findAdjacentGaps(\n\tgaps: Gap[],\n\tshapeId: TLShapeId,\n\tgapLength: number,\n\tdirection: 'forward' | 'backward',\n\tintersection: [number, number]\n): Gap[] {\n\t// TODO: take advantage of the fact that gaps is sorted by starting position?\n\tconst matches = gaps.filter(\n\t\t(gap) =>\n\t\t\t(direction === 'forward' ? gap.startNode.id === shapeId : gap.endNode.id === shapeId) &&\n\t\t\tround(gap.length) === round(gapLength) &&\n\t\t\trangeIntersection(\n\t\t\t\tgap.breadthIntersection[0],\n\t\t\t\tgap.breadthIntersection[1],\n\t\t\t\tintersection[0],\n\t\t\t\tintersection[1]\n\t\t\t)\n\t)\n\n\tif (matches.length === 0) return []\n\n\tconst nextNodes = new Set<TLShapeId>()\n\n\tmatches.forEach((match) => {\n\t\tconst node = direction === 'forward' ? match.endNode.id : match.startNode.id\n\t\tif (!nextNodes.has(node)) {\n\t\t\tnextNodes.add(node)\n\t\t\tconst foundGaps = findAdjacentGaps(\n\t\t\t\tgaps,\n\t\t\t\tnode,\n\t\t\t\tgapLength,\n\t\t\t\tdirection,\n\t\t\t\trangeIntersection(\n\t\t\t\t\tmatch.breadthIntersection[0],\n\t\t\t\t\tmatch.breadthIntersection[1],\n\t\t\t\t\tintersection[0],\n\t\t\t\t\tintersection[1]\n\t\t\t\t)!\n\t\t\t)\n\n\t\t\tmatches.push(...foundGaps)\n\t\t}\n\t})\n\n\treturn matches\n}\n\nfunction dedupeGapSnaps(snaps: Array<Extract<SnapIndicator, { type: 'gaps' }>>) {\n\t// sort by descending order of number of gaps\n\tsnaps.sort((a, b) => b.gaps.length - a.gaps.length)\n\t// pop off any that are included already\n\tfor (let i = snaps.length - 1; i > 0; i--) {\n\t\tconst snap = snaps[i]\n\t\tfor (let j = i - 1; j >= 0; j--) {\n\t\t\tconst otherSnap = snaps[j]\n\t\t\t// if every edge in this snap is included in the other snap somewhere, then it's redundant\n\t\t\tif (\n\t\t\t\totherSnap.direction === snap.direction &&\n\t\t\t\tsnap.gaps.every(\n\t\t\t\t\t(gap) =>\n\t\t\t\t\t\totherSnap.gaps.some(\n\t\t\t\t\t\t\t(otherGap) =>\n\t\t\t\t\t\t\t\tround(gap.startEdge[0].x) === round(otherGap.startEdge[0].x) &&\n\t\t\t\t\t\t\t\tround(gap.startEdge[0].y) === round(otherGap.startEdge[0].y) &&\n\t\t\t\t\t\t\t\tround(gap.startEdge[1].x) === round(otherGap.startEdge[1].x) &&\n\t\t\t\t\t\t\t\tround(gap.startEdge[1].y) === round(otherGap.startEdge[1].y)\n\t\t\t\t\t\t) &&\n\t\t\t\t\t\totherSnap.gaps.some(\n\t\t\t\t\t\t\t(otherGap) =>\n\t\t\t\t\t\t\t\tround(gap.endEdge[0].x) === round(otherGap.endEdge[0].x) &&\n\t\t\t\t\t\t\t\tround(gap.endEdge[0].y) === round(otherGap.endEdge[0].y) &&\n\t\t\t\t\t\t\t\tround(gap.endEdge[1].x) === round(otherGap.endEdge[1].x) &&\n\t\t\t\t\t\t\t\tround(gap.endEdge[1].y) === round(otherGap.endEdge[1].y)\n\t\t\t\t\t\t)\n\t\t\t\t)\n\t\t\t) {\n\t\t\t\tsnaps.splice(i, 1)\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t}\n}\n\n/** @public */\nexport class BoundsSnaps {\n\treadonly editor: Editor\n\tconstructor(readonly manager: SnapManager) {\n\t\tthis.editor = manager.editor\n\t}\n\n\t@computed private getSnapPointsCache() {\n\t\tconst { editor } = this\n\t\treturn editor.store.createComputedCache<BoundsSnapPoint[], TLShape>('snapPoints', (shape) => {\n\t\t\tconst pageTransform = editor.getShapePageTransform(shape.id)\n\t\t\tif (!pageTransform) return undefined\n\t\t\tconst boundsSnapGeometry = editor.getShapeUtil(shape).getBoundsSnapGeometry(shape)\n\t\t\tconst snapPoints =\n\t\t\t\tboundsSnapGeometry.points ?? editor.getShapeGeometry(shape).bounds.cornersAndCenter\n\n\t\t\tif (!pageTransform || !snapPoints) return undefined\n\t\t\treturn snapPoints.map((point, i) => {\n\t\t\t\tconst { x, y } = Mat.applyToPoint(pageTransform, point)\n\t\t\t\treturn { x, y, id: `${shape.id}:${i}` }\n\t\t\t})\n\t\t})\n\t}\n\n\tgetSnapPoints(shapeId: TLShapeId) {\n\t\treturn this.getSnapPointsCache().get(shapeId) ?? []\n\t}\n\n\t// Points which belong to snappable shapes\n\t@computed private getSnappablePoints() {\n\t\tconst snapPointsCache = this.getSnapPointsCache()\n\t\tconst snappableShapes = this.manager.getSnappableShapes()\n\t\tconst result: BoundsSnapPoint[] = []\n\n\t\tfor (const shapeId of snappableShapes) {\n\t\t\tconst snapPoints = snapPointsCache.get(shapeId)\n\t\t\tif (snapPoints) {\n\t\t\t\tresult.push(...snapPoints)\n\t\t\t}\n\t\t}\n\n\t\treturn result\n\t}\n\n\t@computed private getSnappableGapNodes(): Array<GapNode> {\n\t\treturn Array.from(this.manager.getSnappableShapes(), (shapeId) => ({\n\t\t\tid: shapeId,\n\t\t\tpageBounds: assertExists(this.editor.getShapePageBounds(shapeId)),\n\t\t}))\n\t}\n\n\t@computed private getVisibleGaps(): { horizontal: Gap[]; vertical: Gap[] } {\n\t\tconst horizontal: Gap[] = []\n\t\tconst vertical: Gap[] = []\n\n\t\tlet startNode: GapNode, endNode: GapNode\n\n\t\tconst sortedShapesOnCurrentPageHorizontal = this.getSnappableGapNodes().sort((a, b) => {\n\t\t\treturn a.pageBounds.minX - b.pageBounds.minX\n\t\t})\n\n\t\t// Collect horizontal gaps\n\t\tfor (let i = 0; i < sortedShapesOnCurrentPageHorizontal.length; i++) {\n\t\t\tstartNode = sortedShapesOnCurrentPageHorizontal[i]\n\t\t\tfor (let j = i + 1; j < sortedShapesOnCurrentPageHorizontal.length; j++) {\n\t\t\t\tendNode = sortedShapesOnCurrentPageHorizontal[j]\n\n\t\t\t\tif (\n\t\t\t\t\t// is there space between the boxes\n\t\t\t\t\tstartNode.pageBounds.maxX < endNode.pageBounds.minX &&\n\t\t\t\t\t// and they overlap in the y axis\n\t\t\t\t\trangesOverlap(\n\t\t\t\t\t\tstartNode.pageBounds.minY,\n\t\t\t\t\t\tstartNode.pageBounds.maxY,\n\t\t\t\t\t\tendNode.pageBounds.minY,\n\t\t\t\t\t\tendNode.pageBounds.maxY\n\t\t\t\t\t)\n\t\t\t\t) {\n\t\t\t\t\thorizontal.push({\n\t\t\t\t\t\tstartNode,\n\t\t\t\t\t\tendNode,\n\t\t\t\t\t\tstartEdge: [\n\t\t\t\t\t\t\tnew Vec(startNode.pageBounds.maxX, startNode.pageBounds.minY),\n\t\t\t\t\t\t\tnew Vec(startNode.pageBounds.maxX, startNode.pageBounds.maxY),\n\t\t\t\t\t\t],\n\t\t\t\t\t\tendEdge: [\n\t\t\t\t\t\t\tnew Vec(endNode.pageBounds.minX, endNode.pageBounds.minY),\n\t\t\t\t\t\t\tnew Vec(endNode.pageBounds.minX, endNode.pageBounds.maxY),\n\t\t\t\t\t\t],\n\t\t\t\t\t\tlength: endNode.pageBounds.minX - startNode.pageBounds.maxX,\n\t\t\t\t\t\tbreadthIntersection: rangeIntersection(\n\t\t\t\t\t\t\tstartNode.pageBounds.minY,\n\t\t\t\t\t\t\tstartNode.pageBounds.maxY,\n\t\t\t\t\t\t\tendNode.pageBounds.minY,\n\t\t\t\t\t\t\tendNode.pageBounds.maxY\n\t\t\t\t\t\t)!,\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Collect vertical gaps\n\t\tconst sortedShapesOnCurrentPageVertical = sortedShapesOnCurrentPageHorizontal.sort((a, b) => {\n\t\t\treturn a.pageBounds.minY - b.pageBounds.minY\n\t\t})\n\n\t\tfor (let i = 0; i < sortedShapesOnCurrentPageVertical.length; i++) {\n\t\t\tstartNode = sortedShapesOnCurrentPageVertical[i]\n\t\t\tfor (let j = i + 1; j < sortedShapesOnCurrentPageVertical.length; j++) {\n\t\t\t\tendNode = sortedShapesOnCurrentPageVertical[j]\n\n\t\t\t\tif (\n\t\t\t\t\t// is there space between the boxes\n\t\t\t\t\tstartNode.pageBounds.maxY < endNode.pageBounds.minY &&\n\t\t\t\t\t// do they overlap in the x axis\n\t\t\t\t\trangesOverlap(\n\t\t\t\t\t\tstartNode.pageBounds.minX,\n\t\t\t\t\t\tstartNode.pageBounds.maxX,\n\t\t\t\t\t\tendNode.pageBounds.minX,\n\t\t\t\t\t\tendNode.pageBounds.maxX\n\t\t\t\t\t)\n\t\t\t\t) {\n\t\t\t\t\tvertical.push({\n\t\t\t\t\t\tstartNode,\n\t\t\t\t\t\tendNode,\n\t\t\t\t\t\tstartEdge: [\n\t\t\t\t\t\t\tnew Vec(startNode.pageBounds.minX, startNode.pageBounds.maxY),\n\t\t\t\t\t\t\tnew Vec(startNode.pageBounds.maxX, startNode.pageBounds.maxY),\n\t\t\t\t\t\t],\n\t\t\t\t\t\tendEdge: [\n\t\t\t\t\t\t\tnew Vec(endNode.pageBounds.minX, endNode.pageBounds.minY),\n\t\t\t\t\t\t\tnew Vec(endNode.pageBounds.maxX, endNode.pageBounds.minY),\n\t\t\t\t\t\t],\n\t\t\t\t\t\tlength: endNode.pageBounds.minY - startNode.pageBounds.maxY,\n\t\t\t\t\t\tbreadthIntersection: rangeIntersection(\n\t\t\t\t\t\t\tstartNode.pageBounds.minX,\n\t\t\t\t\t\t\tstartNode.pageBounds.maxX,\n\t\t\t\t\t\t\tendNode.pageBounds.minX,\n\t\t\t\t\t\t\tendNode.pageBounds.maxX\n\t\t\t\t\t\t)!,\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn { horizontal, vertical }\n\t}\n\n\tsnapTranslateShapes({\n\t\tlockedAxis,\n\t\tinitialSelectionPageBounds,\n\t\tinitialSelectionSnapPoints,\n\t\tdragDelta,\n\t}: {\n\t\tlockedAxis: 'x' | 'y' | null\n\t\tinitialSelectionSnapPoints: BoundsSnapPoint[]\n\t\tinitialSelectionPageBounds: Box\n\t\tdragDelta: Vec\n\t}): SnapData {\n\t\tconst snapThreshold = this.manager.getSnapThreshold()\n\t\tconst visibleSnapPointsNotInSelection = this.getSnappablePoints()\n\n\t\tconst selectionPageBounds = initialSelectionPageBounds.clone().translate(dragDelta)\n\n\t\tconst selectionSnapPoints: BoundsSnapPoint[] = initialSelectionSnapPoints.map(\n\t\t\t({ x, y }, i) => ({\n\t\t\t\tid: 'selection:' + i,\n\t\t\t\tx: x + dragDelta.x,\n\t\t\t\ty: y + dragDelta.y,\n\t\t\t})\n\t\t)\n\n\t\tconst otherNodeSnapPoints = visibleSnapPointsNotInSelection\n\n\t\tconst nearestSnapsX: NearestSnap[] = []\n\t\tconst nearestSnapsY: NearestSnap[] = []\n\t\tconst minOffset = new Vec(snapThreshold, snapThreshold)\n\n\t\tthis.collectPointSnaps({\n\t\t\tminOffset,\n\t\t\tnearestSnapsX,\n\t\t\tnearestSnapsY,\n\t\t\totherNodeSnapPoints,\n\t\t\tselectionSnapPoints,\n\t\t})\n\n\t\tthis.collectGapSnaps({\n\t\t\tselectionPageBounds,\n\t\t\tnearestSnapsX,\n\t\t\tnearestSnapsY,\n\t\t\tminOffset,\n\t\t})\n\n\t\t// at the same time, calculate how far we need to nudge the shape to 'snap' to the target point(s)\n\t\tconst nudge = new Vec(\n\t\t\tlockedAxis === 'x' ? 0 : nearestSnapsX[0]?.nudge ?? 0,\n\t\t\tlockedAxis === 'y' ? 0 : nearestSnapsY[0]?.nudge ?? 0\n\t\t)\n\n\t\t// ok we've figured out how much the box should be nudged, now let's find all the snap points\n\t\t// that are exact after making that translation, so we can render all of them.\n\t\t// first reset everything and adjust the original shapes to conform to the nudge\n\t\tminOffset.x = 0\n\t\tminOffset.y = 0\n\t\tnearestSnapsX.length = 0\n\t\tnearestSnapsY.length = 0\n\t\tselectionSnapPoints.forEach((s) => {\n\t\t\ts.x += nudge.x\n\t\t\ts.y += nudge.y\n\t\t})\n\t\tselectionPageBounds.translate(nudge)\n\n\t\tthis.collectPointSnaps({\n\t\t\tminOffset,\n\t\t\tnearestSnapsX,\n\t\t\tnearestSnapsY,\n\t\t\totherNodeSnapPoints,\n\t\t\tselectionSnapPoints,\n\t\t})\n\n\t\tthis.collectGapSnaps({\n\t\t\tselectionPageBounds,\n\t\t\tnearestSnapsX,\n\t\t\tnearestSnapsY,\n\t\t\tminOffset,\n\t\t})\n\n\t\tconst pointSnapsLines = this.getPointSnapLines({\n\t\t\tnearestSnapsX,\n\t\t\tnearestSnapsY,\n\t\t})\n\n\t\tconst gapSnapLines = this.getGapSnapLines({\n\t\t\tselectionPageBounds,\n\t\t\tnearestSnapsX,\n\t\t\tnearestSnapsY,\n\t\t})\n\n\t\tthis.manager.setIndicators([...gapSnapLines, ...pointSnapsLines])\n\n\t\treturn { nudge }\n\t}\n\n\tsnapResizeShapes({\n\t\tinitialSelectionPageBounds,\n\t\tdragDelta,\n\t\thandle: originalHandle,\n\t\tisAspectRatioLocked,\n\t\tisResizingFromCenter,\n\t}: {\n\t\t// the page bounds when the pointer went down, before any dragging\n\t\tinitialSelectionPageBounds: Box\n\t\t// how far the pointer has been dragged\n\t\tdragDelta: Vec\n\n\t\thandle: SelectionCorner | SelectionEdge\n\t\tisAspectRatioLocked: boolean\n\t\tisResizingFromCenter: boolean\n\t}): SnapData {\n\t\tconst snapThreshold = this.manager.getSnapThreshold()\n\n\t\t// first figure out the new bounds of the selection\n\t\tconst {\n\t\t\tbox: unsnappedResizedPageBounds,\n\t\t\tscaleX,\n\t\t\tscaleY,\n\t\t} = Box.Resize(\n\t\t\tinitialSelectionPageBounds,\n\t\t\toriginalHandle,\n\t\t\tisResizingFromCenter ? dragDelta.x * 2 : dragDelta.x,\n\t\t\tisResizingFromCenter ? dragDelta.y * 2 : dragDelta.y,\n\t\t\tisAspectRatioLocked\n\t\t)\n\n\t\tlet handle = originalHandle\n\n\t\tif (scaleX < 0) {\n\t\t\thandle = flipSelectionHandleX(handle)\n\t\t}\n\t\tif (scaleY < 0) {\n\t\t\thandle = flipSelectionHandleY(handle)\n\t\t}\n\n\t\tif (isResizingFromCenter) {\n\t\t\t// reposition if resizing from center\n\t\t\tunsnappedResizedPageBounds.center = initialSelectionPageBounds.center\n\t\t}\n\n\t\tconst isXLocked = handle === 'top' || handle === 'bottom'\n\t\tconst isYLocked = handle === 'left' || handle === 'right'\n\n\t\tconst selectionSnapPoints = getResizeSnapPointsForHandle(handle, unsnappedResizedPageBounds)\n\n\t\tconst otherNodeSnapPoints = this.getSnappablePoints()\n\n\t\tconst nearestSnapsX: NearestPointsSnap[] = []\n\t\tconst nearestSnapsY: NearestPointsSnap[] = []\n\t\tconst minOffset = new Vec(snapThreshold, snapThreshold)\n\n\t\tthis.collectPointSnaps({\n\t\t\tminOffset,\n\t\t\tnearestSnapsX,\n\t\t\tnearestSnapsY,\n\t\t\totherNodeSnapPoints,\n\t\t\tselectionSnapPoints,\n\t\t})\n\n\t\t// at the same time, calculate how far we need to nudge the shape to 'snap' to the target point(s)\n\t\tconst nudge = new Vec(\n\t\t\tisXLocked ? 0 : nearestSnapsX[0]?.nudge ?? 0,\n\t\t\tisYLocked ? 0 : nearestSnapsY[0]?.nudge ?? 0\n\t\t)\n\n\t\tif (isAspectRatioLocked && isSelectionCorner(handle) && nudge.len() !== 0) {\n\t\t\t// if the aspect ratio is locked we need to make the nudge diagonal rather than independent in each axis\n\t\t\t// so we use the aspect ratio along with one axis value to set the other axis value, but which axis we use\n\t\t\t// as a source of truth depends what we have snapped to and how far.\n\n\t\t\t// if we found a snap in both axes, pick the closest one and discard the other\n\t\t\tconst primaryNudgeAxis: 'x' | 'y' =\n\t\t\t\tnearestSnapsX.length && nearestSnapsY.length\n\t\t\t\t\t? Math.abs(nudge.x) < Math.abs(nudge.y)\n\t\t\t\t\t\t? 'x'\n\t\t\t\t\t\t: 'y'\n\t\t\t\t\t: nearestSnapsX.length\n\t\t\t\t\t\t? 'x'\n\t\t\t\t\t\t: 'y'\n\n\t\t\tconst ratio = initialSelectionPageBounds.aspectRatio\n\n\t\t\tif (primaryNudgeAxis === 'x') {\n\t\t\t\tnearestSnapsY.length = 0\n\t\t\t\tnudge.y = nudge.x / ratio\n\t\t\t\tif (handle === 'bottom_left' || handle === 'top_right') {\n\t\t\t\t\tnudge.y = -nudge.y\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tnearestSnapsX.length = 0\n\t\t\t\tnudge.x = nudge.y * ratio\n\t\t\t\tif (handle === 'bottom_left' || handle === 'top_right') {\n\t\t\t\t\tnudge.x = -nudge.x\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// now resize the box after nudging, calculate the snaps again, and return the snap lines to match\n\t\t// the fully resized box\n\t\tconst snappedDelta = Vec.Add(dragDelta, nudge)\n\n\t\t// first figure out the new bounds of the selection\n\t\tconst { box: snappedResizedPageBounds } = Box.Resize(\n\t\t\tinitialSelectionPageBounds,\n\t\t\toriginalHandle,\n\t\t\tisResizingFromCenter ? snappedDelta.x * 2 : snappedDelta.x,\n\t\t\tisResizingFromCenter ? snappedDelta.y * 2 : snappedDelta.y,\n\t\t\tisAspectRatioLocked\n\t\t)\n\n\t\tif (isResizingFromCenter) {\n\t\t\t// reposition if resizing from center\n\t\t\tsnappedResizedPageBounds.center = initialSelectionPageBounds.center\n\t\t}\n\n\t\tconst snappedSelectionPoints = getResizeSnapPointsForHandle('any', snappedResizedPageBounds)\n\t\t// calculate snaps again using all points\n\t\tnearestSnapsX.length = 0\n\t\tnearestSnapsY.length = 0\n\t\tminOffset.x = 0\n\t\tminOffset.y = 0\n\n\t\tthis.collectPointSnaps({\n\t\t\tminOffset,\n\t\t\tnearestSnapsX,\n\t\t\tnearestSnapsY,\n\t\t\totherNodeSnapPoints,\n\t\t\tselectionSnapPoints: snappedSelectionPoints,\n\t\t})\n\t\tconst pointSnaps = this.getPointSnapLines({\n\t\t\tnearestSnapsX,\n\t\t\tnearestSnapsY,\n\t\t})\n\n\t\tthis.manager.setIndicators([...pointSnaps])\n\n\t\treturn { nudge }\n\t}\n\n\tprivate collectPointSnaps({\n\t\tselectionSnapPoints,\n\t\totherNodeSnapPoints,\n\t\tminOffset,\n\t\tnearestSnapsX,\n\t\tnearestSnapsY,\n\t}: {\n\t\tselectionSnapPoints: BoundsSnapPoint[]\n\t\totherNodeSnapPoints: BoundsSnapPoint[]\n\t\tminOffset: Vec\n\t\tnearestSnapsX: NearestSnap[]\n\t\tnearestSnapsY: NearestSnap[]\n\t}) {\n\t\t// for each snap point on the bounding box of the selection, find the set of points\n\t\t// which are closest to it in each axis\n\t\tfor (const thisSnapPoint of selectionSnapPoints) {\n\t\t\tfor (const otherSnapPoint of otherNodeSnapPoints) {\n\t\t\t\tconst offset = Vec.Sub(thisSnapPoint, otherSnapPoint)\n\t\t\t\tconst offsetX = Math.abs(offset.x)\n\t\t\t\tconst offsetY = Math.abs(offset.y)\n\n\t\t\t\tif (round(offsetX) <= round(minOffset.x)) {\n\t\t\t\t\tif (round(offsetX) < round(minOffset.x)) {\n\t\t\t\t\t\t// we found a point that is significantly closer than all previous points\n\t\t\t\t\t\t// so wipe the slate clean and start over\n\t\t\t\t\t\tnearestSnapsX.length = 0\n\t\t\t\t\t}\n\n\t\t\t\t\tnearestSnapsX.push({\n\t\t\t\t\t\ttype: 'points',\n\t\t\t\t\t\tpoints: { thisPoint: thisSnapPoint, otherPoint: otherSnapPoint },\n\t\t\t\t\t\tnudge: otherSnapPoint.x - thisSnapPoint.x,\n\t\t\t\t\t})\n\t\t\t\t\tminOffset.x = offsetX\n\t\t\t\t}\n\n\t\t\t\tif (round(offsetY) <= round(minOffset.y)) {\n\t\t\t\t\tif (round(offsetY) < round(minOffset.y)) {\n\t\t\t\t\t\t// we found a point that is significantly closer than all previous points\n\t\t\t\t\t\t// so wipe the slate clean and start over\n\t\t\t\t\t\tnearestSnapsY.length = 0\n\t\t\t\t\t}\n\t\t\t\t\tnearestSnapsY.push({\n\t\t\t\t\t\ttype: 'points',\n\t\t\t\t\t\tpoints: { thisPoint: thisSnapPoint, otherPoint: otherSnapPoint },\n\t\t\t\t\t\tnudge: otherSnapPoint.y - thisSnapPoint.y,\n\t\t\t\t\t})\n\t\t\t\t\tminOffset.y = offsetY\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate collectGapSnaps({\n\t\tselectionPageBounds,\n\t\tminOffset,\n\t\tnearestSnapsX,\n\t\tnearestSnapsY,\n\t}: {\n\t\tselectionPageBounds: Box\n\t\tminOffset: Vec\n\t\tnearestSnapsX: NearestSnap[]\n\t\tnearestSnapsY: NearestSnap[]\n\t}) {\n\t\tconst { horizontal, vertical } = this.getVisibleGaps()\n\n\t\tfor (const gap of horizontal) {\n\t\t\t// ignore this gap if the selection doesn't overlap with it in the y axis\n\t\t\tif (\n\t\t\t\t!rangesOverlap(\n\t\t\t\t\tgap.breadthIntersection[0],\n\t\t\t\t\tgap.breadthIntersection[1],\n\t\t\t\t\tselectionPageBounds.minY,\n\t\t\t\t\tselectionPageBounds.maxY\n\t\t\t\t)\n\t\t\t) {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\t// check for center match\n\t\t\tconst gapMidX = gap.startEdge[0].x + gap.length / 2\n\t\t\tconst centerNudge = gapMidX - selectionPageBounds.center.x\n\t\t\tconst gapIsLargerThanSelection = gap.length > selectionPageBounds.width\n\n\t\t\tif (gapIsLargerThanSelection && round(Math.abs(centerNudge)) <= round(minOffset.x)) {\n\t\t\t\tif (round(Math.abs(centerNudge)) < round(minOffset.x)) {\n\t\t\t\t\t// reset if we found a closer snap\n\t\t\t\t\tnearestSnapsX.length = 0\n\t\t\t\t}\n\t\t\t\tminOffset.x = Math.abs(centerNudge)\n\n\t\t\t\tconst snap: NearestSnap = {\n\t\t\t\t\ttype: 'gap_center',\n\t\t\t\t\tgap,\n\t\t\t\t\tnudge: centerNudge,\n\t\t\t\t}\n\n\t\t\t\t// we need to avoid creating visual noise with too many center snaps in situations\n\t\t\t\t// where there are lots of adjacent items with even spacing\n\t\t\t\t// so let's only show other center snaps where the gap's breadth does not overlap with this one\n\t\t\t\t// i.e.\n\t\t\t\t// \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n\t\t\t\t// \u2502 \u2502\n\t\t\t\t// \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2518\n\t\t\t\t// \u253C \u2502\n\t\t\t\t// \u250C\u2500\u2500\u2500\u2500\u2500\u2534\u2510 \u2502\n\t\t\t\t// \u2502 \u2502 \u253C\n\t\t\t\t// \u2514\u2500\u2500\u2500\u2500\u2500\u252C\u2518 \u2502\n\t\t\t\t// \u253C \u2502\n\t\t\t\t// \u250C\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n\t\t\t\t// \u2502 \u2502 \u25C4\u2500\u2500\u2500\u2500 i'm dragging this one\n\t\t\t\t// \u2514\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n\t\t\t\t// \u2500\u2500\u2500\u2500\u2500\u25BA \u253C \u2502\n\t\t\t\t// \u250C\u2500\u2500\u2500\u2500\u2500\u2534\u2510 \u2502 don't show these\n\t\t\t\t// show these \u2502 \u2502 \u253C larger gaps since\n\t\t\t\t// smaller \u2514\u2500\u2500\u2500\u2500\u2500\u252C\u2518 \u2502 \u25C4\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 the smaller ones\n\t\t\t\t// gaps \u253C \u2502 cover the same\n\t\t\t\t// \u2500\u2500\u2500\u2500\u2500\u25BA \u250C\u2534\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2510 information\n\t\t\t\t// \u2502 \u2502\n\t\t\t\t// \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n\t\t\t\t//\n\t\t\t\t// but we want to show all of these ones since the gap breadths don't overlap\n\t\t\t\t// \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n\t\t\t\t// \u2502 \u2502\n\t\t\t\t// \u250C\u2500\u2500\u2500\u2500\u2510 \u2514\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n\t\t\t\t// \u2502 \u2502 \u2502\n\t\t\t\t// \u2514\u2500\u2500\u252C\u2500\u2518 \u253C\n\t\t\t\t// \u253C \u2502\n\t\t\t\t// \u250C\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2510\n\t\t\t\t// \u2502 \u2502 \u25C4\u2500\u2500\u2500\u2500\u2500 i'm dragging this one\n\t\t\t\t// \u2514\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252C\u2500\u2518\n\t\t\t\t// \u253C \u2502\n\t\t\t\t// \u250C\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2510 \u253C\n\t\t\t\t// \u2502 \u2502 \u2502\n\t\t\t\t// \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u250C\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n\t\t\t\t// \u2502 \u2502\n\t\t\t\t// \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n\n\t\t\t\tconst otherCenterSnap = nearestSnapsX.find(({ type }) => type === 'gap_center') as\n\t\t\t\t\t| Extract<NearestSnap, { type: 'gap_center' }>\n\t\t\t\t\t| undefined\n\n\t\t\t\tconst gapBreadthsOverlap =\n\t\t\t\t\totherCenterSnap &&\n\t\t\t\t\trangeIntersection(\n\t\t\t\t\t\tgap.breadthIntersection[0],\n\t\t\t\t\t\tgap.breadthIntersection[1],\n\t\t\t\t\t\totherCenterSnap.gap.breadthIntersection[0],\n\t\t\t\t\t\totherCenterSnap.gap.breadthIntersection[1]\n\t\t\t\t\t)\n\n\t\t\t\t// if there is another center snap and it's bigger than this one, and it overlaps with this one, replace it\n\t\t\t\tif (otherCenterSnap && otherCenterSnap.gap.length > gap.length && gapBreadthsOverlap) {\n\t\t\t\t\tnearestSnapsX[nearestSnapsX.indexOf(otherCenterSnap)] = snap\n\t\t\t\t} else if (!otherCenterSnap || !gapBreadthsOverlap) {\n\t\t\t\t\tnearestSnapsX.push(snap)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// check for duplication left match\n\t\t\tconst duplicationLeftX = gap.startNode.pageBounds.minX - gap.length\n\t\t\tconst selectionRightX = selectionPageBounds.maxX\n\n\t\t\tconst duplicationLeftNudge = duplicationLeftX - selectionRightX\n\t\t\tif (round(Math.abs(duplicationLeftNudge)) <= round(minOffset.x)) {\n\t\t\t\tif (round(Math.abs(duplicationLeftNudge)) < round(minOffset.x)) {\n\t\t\t\t\t// reset if we found a closer snap\n\t\t\t\t\tnearestSnapsX.length = 0\n\t\t\t\t}\n\t\t\t\tminOffset.x = Math.abs(duplicationLeftNudge)\n\n\t\t\t\tnearestSnapsX.push({\n\t\t\t\t\ttype: 'gap_duplicate',\n\t\t\t\t\tgap,\n\t\t\t\t\tprotrusionDirection: 'left',\n\t\t\t\t\tnudge: duplicationLeftNudge,\n\t\t\t\t})\n\t\t\t}\n\n\t\t\t// check for duplication right match\n\t\t\tconst duplicationRightX = gap.endNode.pageBounds.maxX + gap.length\n\t\t\tconst selectionLeftX = selectionPageBounds.minX\n\n\t\t\tconst duplicationRightNudge = duplicationRightX - selectionLeftX\n\t\t\tif (round(Math.abs(duplicationRightNudge)) <= round(minOffset.x)) {\n\t\t\t\tif (round(Math.abs(duplicationRightNudge)) < round(minOffset.x)) {\n\t\t\t\t\t// reset if we found a closer snap\n\t\t\t\t\tnearestSnapsX.length = 0\n\t\t\t\t}\n\t\t\t\tminOffset.x = Math.abs(duplicationRightNudge)\n\n\t\t\t\tnearestSnapsX.push({\n\t\t\t\t\ttype: 'gap_duplicate',\n\t\t\t\t\tgap,\n\t\t\t\t\tprotrusionDirection: 'right',\n\t\t\t\t\tnudge: duplicationRightNudge,\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\n\t\tfor (const gap of vertical) {\n\t\t\t// ignore this gap if the selection doesn't overlap with it in the y axis\n\t\t\tif (\n\t\t\t\t!rangesOverlap(\n\t\t\t\t\tgap.breadthIntersection[0],\n\t\t\t\t\tgap.breadthIntersection[1],\n\t\t\t\t\tselectionPageBounds.minX,\n\t\t\t\t\tselectionPageBounds.maxX\n\t\t\t\t)\n\t\t\t) {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\t// check for center match\n\t\t\tconst gapMidY = gap.startEdge[0].y + gap.length / 2\n\t\t\tconst centerNudge = gapMidY - selectionPageBounds.center.y\n\n\t\t\tconst gapIsLargerThanSelection = gap.length > selectionPageBounds.height\n\n\t\t\tif (gapIsLargerThanSelection && round(Math.abs(centerNudge)) <= round(minOffset.y)) {\n\t\t\t\tif (round(Math.abs(centerNudge)) < round(minOffset.y)) {\n\t\t\t\t\t// reset if we found a closer snap\n\t\t\t\t\tnearestSnapsY.length = 0\n\t\t\t\t}\n\t\t\t\tminOffset.y = Math.abs(centerNudge)\n\n\t\t\t\tconst snap: NearestSnap = {\n\t\t\t\t\ttype: 'gap_center',\n\t\t\t\t\tgap,\n\t\t\t\t\tnudge: centerNudge,\n\t\t\t\t}\n\n\t\t\t\t// we need to avoid creating visual noise with too many center snaps in situations\n\t\t\t\t// where there are lots of adjacent items with even spacing\n\t\t\t\t// so let's only show other center snaps where the gap's breadth does not overlap with this one\n\t\t\t\t// i.e.\n\t\t\t\t// \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n\t\t\t\t// \u2502 \u2502\n\t\t\t\t// \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2518\n\t\t\t\t// \u253C \u2502\n\t\t\t\t// \u250C\u2500\u2500\u2500\u2500\u2500\u2534\u2510 \u2502\n\t\t\t\t// \u2502 \u2502 \u253C\n\t\t\t\t// \u2514\u2500\u2500\u2500\u2500\u2500\u252C\u2518 \u2502\n\t\t\t\t// \u253C \u2502\n\t\t\t\t// \u250C\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n\t\t\t\t// \u2502 \u2502 \u25C4\u2500\u2500\u2500\u2500 i'm dragging this one\n\t\t\t\t// \u2514\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n\t\t\t\t// \u2500\u2500\u2500\u2500\u2500\u25BA \u253C \u2502\n\t\t\t\t// \u250C\u2500\u2500\u2500\u2500\u2500\u2534\u2510 \u2502 don't show these\n\t\t\t\t// show these \u2502 \u2502 \u253C larger gaps since\n\t\t\t\t// smaller \u2514\u2500\u2500\u2500\u2500\u2500\u252C\u2518 \u2502 \u25C4\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 the smaller ones\n\t\t\t\t// gaps \u253C \u2502 cover the same\n\t\t\t\t// \u2500\u2500\u2500\u2500\u2500\u25BA \u250C\u2534\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2510 information\n\t\t\t\t// \u2502 \u2502\n\t\t\t\t// \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n\t\t\t\t//\n\t\t\t\t// but we want to show all of these ones since the gap breadths don't overlap\n\t\t\t\t// \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n\t\t\t\t// \u2502 \u2502\n\t\t\t\t// \u250C\u2500\u2500\u2500\u2500\u2510 \u2514\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n\t\t\t\t// \u2502 \u2502 \u2502\n\t\t\t\t// \u2514\u2500\u2500\u252C\u2500\u2518 \u253C\n\t\t\t\t// \u253C \u2502\n\t\t\t\t// \u250C\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2510\n\t\t\t\t// \u2502 \u2502 \u25C4\u2500\u2500\u2500\u2500\u2500 i'm dragging this one\n\t\t\t\t// \u2514\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252C\u2500\u2518\n\t\t\t\t// \u253C \u2502\n\t\t\t\t// \u250C\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2510 \u253C\n\t\t\t\t// \u2502 \u2502 \u2502\n\t\t\t\t// \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u250C\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n\t\t\t\t// \u2502 \u2502\n\t\t\t\t// \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n\n\t\t\t\tconst otherCenterSnap = nearestSnapsY.find(({ type }) => type === 'gap_center') as\n\t\t\t\t\t| Extract<NearestSnap, { type: 'gap_center' }>\n\t\t\t\t\t| undefined\n\n\t\t\t\tconst gapBreadthsOverlap =\n\t\t\t\t\totherCenterSnap &&\n\t\t\t\t\trangesOverlap(\n\t\t\t\t\t\totherCenterSnap.gap.breadthIntersection[0],\n\t\t\t\t\t\totherCenterSnap.gap.breadthIntersection[1],\n\t\t\t\t\t\tgap.breadthIntersection[0],\n\t\t\t\t\t\tgap.breadthIntersection[1]\n\t\t\t\t\t)\n\n\t\t\t\t// if there is another center snap and it's bigger than this one, and it overlaps with this one, replace it\n\t\t\t\tif (otherCenterSnap && otherCenterSnap.gap.length > gap.length && gapBreadthsOverlap) {\n\t\t\t\t\tnearestSnapsY[nearestSnapsY.indexOf(otherCenterSnap)] = snap\n\t\t\t\t} else if (!otherCenterSnap || !gapBreadthsOverlap) {\n\t\t\t\t\tnearestSnapsY.push(snap)\n\t\t\t\t}\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\t// check for duplication top match\n\t\t\tconst duplicationTopY = gap.startNode.pageBounds.minY - gap.length\n\t\t\tconst selectionBottomY = selectionPageBounds.maxY\n\n\t\t\tconst duplicationTopNudge = duplicationTopY - selectionBottomY\n\t\t\tif (round(Math.abs(duplicationTopNudge)) <= round(minOffset.y)) {\n\t\t\t\tif (round(Math.abs(duplicationTopNudge)) < round(minOffset.y)) {\n\t\t\t\t\t// reset if we found a closer snap\n\t\t\t\t\tnearestSnapsY.length = 0\n\t\t\t\t}\n\t\t\t\tminOffset.y = Math.abs(duplicationTopNudge)\n\n\t\t\t\tnearestSnapsY.push({\n\t\t\t\t\ttype: 'gap_duplicate',\n\t\t\t\t\tgap,\n\t\t\t\t\tprotrusionDirection: 'top',\n\t\t\t\t\tnudge: duplicationTopNudge,\n\t\t\t\t})\n\t\t\t}\n\n\t\t\t// check for duplication bottom match\n\t\t\tconst duplicationBottomY = gap.endNode.pageBounds.maxY + gap.length\n\t\t\tconst selectionTopY = selectionPageBounds.minY\n\n\t\t\tconst duplicationBottomNudge = duplicationBottomY - selectionTopY\n\t\t\tif (round(Math.abs(duplicationBottomNudge)) <= round(minOffset.y)) {\n\t\t\t\tif (round(Math.abs(duplicationBottomNudge)) < round(minOffset.y)) {\n\t\t\t\t\t// reset if we found a closer snap\n\t\t\t\t\tnearestSnapsY.length = 0\n\t\t\t\t}\n\t\t\t\tminOffset.y = Math.abs(duplicationBottomNudge)\n\n\t\t\t\tnearestSnapsY.push({\n\t\t\t\t\ttype: 'gap_duplicate',\n\t\t\t\t\tgap,\n\t\t\t\t\tprotrusionDirection: 'bottom',\n\t\t\t\t\tnudge: duplicationBottomNudge,\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate getPointSnapLines({\n\t\tnearestSnapsX,\n\t\tnearestSnapsY,\n\t}: {\n\t\tnearestSnapsX: NearestSnap[]\n\t\tnearestSnapsY: NearestSnap[]\n\t}): PointsSnapIndicator[] {\n\t\t// point snaps may align on multiple parallel lines so we need to split the pairs\n\t\t// into groups based on where they are in their their snap axes\n\t\tconst snapGroupsX = {} as { [key: string]: SnapPair[] }\n\t\tconst snapGroupsY = {} as { [key: string]: SnapPair[] }\n\n\t\tif (nearestSnapsX.length > 0) {\n\t\t\tfor (const snap of nearestSnapsX) {\n\t\t\t\tif (snap.type === 'points') {\n\t\t\t\t\tconst key = round(snap.points.otherPoint.x)\n\t\t\t\t\tif (!snapGroupsX[key]) {\n\t\t\t\t\t\tsnapGroupsX[key] = []\n\t\t\t\t\t}\n\t\t\t\t\tsnapGroupsX[key].push(snap.points)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (nearestSnapsY.length > 0) {\n\t\t\tfor (const snap of nearestSnapsY) {\n\t\t\t\tif (snap.type === 'points') {\n\t\t\t\t\tconst key = round(snap.points.otherPoint.y)\n\t\t\t\t\tif (!snapGroupsY[key]) {\n\t\t\t\t\t\tsnapGroupsY[key] = []\n\t\t\t\t\t}\n\t\t\t\t\tsnapGroupsY[key].push(snap.points)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// and finally create all the snap lines for the UI to render\n\t\treturn Object.values(snapGroupsX)\n\t\t\t.concat(Object.values(snapGroupsY))\n\t\t\t.map((snapGroup) => ({\n\t\t\t\tid: uniqueId(),\n\t\t\t\ttype: 'points',\n\t\t\t\tpoints: dedupe(\n\t\t\t\t\tsnapGroup\n\t\t\t\t\t\t.map((snap) => Vec.From(snap.otherPoint))\n\t\t\t\t\t\t// be sure to nudge over the selection snap points\n\t\t\t\t\t\t.concat(snapGroup.map((snap) => Vec.From(snap.thisPoint))),\n\t\t\t\t\t(a: Vec, b: Vec) => a.equals(b)\n\t\t\t\t),\n\t\t\t}))\n\t}\n\n\tprivate getGapSnapLines({\n\t\tselectionPageBounds,\n\t\tnearestSnapsX,\n\t\tnearestSnapsY,\n\t}: {\n\t\tselectionPageBounds: Box\n\t\tnearestSnapsX: NearestSnap[]\n\t\tnearestSnapsY: NearestSnap[]\n\t}): GapsSnapIndicator[] {\n\t\tconst { vertical, horizontal } = this.getVisibleGaps()\n\n\t\tconst selectionSides: Record<SelectionEdge, [Vec, Vec]> = {\n\t\t\ttop: selectionPageBounds.sides[0],\n\t\t\tright: selectionPageBounds.sides[1],\n\t\t\t// need bottom and left to be sorted asc, which .sides is not.\n\t\t\tbottom: [selectionPageBounds.corners[3], selectionPageBounds.corners[2]],\n\t\t\tleft: [selectionPageBounds.corners[0], selectionPageBounds.corners[3]],\n\t\t}\n\n\t\tconst result: GapsSnapIndicator[] = []\n\n\t\tif (nearestSnapsX.length > 0) {\n\t\t\tfor (const snap of nearestSnapsX) {\n\t\t\t\tif (snap.type === 'points') continue\n\n\t\t\t\tconst {\n\t\t\t\t\tgap: { breadthIntersection, startEdge, startNode, endNode, length, endEdge },\n\t\t\t\t} = snap\n\n\t\t\t\tswitch (snap.type) {\n\t\t\t\t\tcase 'gap_center': {\n\t\t\t\t\t\t// create\n\t\t\t\t\t\tconst newGapsLength = (length - selectionPageBounds.width) / 2\n\t\t\t\t\t\tconst gapBreadthIntersection = rangeIntersection(\n\t\t\t\t\t\t\tbreadthIntersection[0],\n\t\t\t\t\t\t\tbreadthIntersection[1],\n\t\t\t\t\t\t\tselectionPageBounds.minY,\n\t\t\t\t\t\t\tselectionPageBounds.maxY\n\t\t\t\t\t\t)!\n\t\t\t\t\t\tresult.push({\n\t\t\t\t\t\t\ttype: 'gaps',\n\t\t\t\t\t\t\tdirection: 'horizontal',\n\t\t\t\t\t\t\tid: uniqueId(),\n\t\t\t\t\t\t\tgaps: [\n\t\t\t\t\t\t\t\t...findAdjacentGaps(\n\t\t\t\t\t\t\t\t\thorizontal,\n\t\t\t\t\t\t\t\t\tstartNode.id,\n\t\t\t\t\t\t\t\t\tnewGapsLength,\n\t\t\t\t\t\t\t\t\t'backward',\n\t\t\t\t\t\t\t\t\tgapBreadthIntersection\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tstartEdge,\n\t\t\t\t\t\t\t\t\tendEdge: selectionSides.left,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tstartEdge: selectionSides.right,\n\t\t\t\t\t\t\t\t\tendEdge,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t...findAdjacentGaps(\n\t\t\t\t\t\t\t\t\thorizontal,\n\t\t\t\t\t\t\t\t\tendNode.id,\n\t\t\t\t\t\t\t\t\tnewGapsLength,\n\t\t\t\t\t\t\t\t\t'forward',\n\t\t\t\t\t\t\t\t\tgapBreadthIntersection\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t})\n\t\t\t\t\t\tbreak\n\t\t\t\t\t}\n\t\t\t\t\tcase 'gap_duplicate': {\n\t\t\t\t\t\t// create\n\t\t\t\t\t\tconst gapBreadthIntersection = rangeIntersection(\n\t\t\t\t\t\t\tbreadthIntersection[0],\n\t\t\t\t\t\t\tbreadthIntersection[1],\n\t\t\t\t\t\t\tselectionPageBounds.minY,\n\t\t\t\t\t\t\tselectionPageBounds.maxY\n\t\t\t\t\t\t)!\n\t\t\t\t\t\tresult.push({\n\t\t\t\t\t\t\ttype: 'gaps',\n\t\t\t\t\t\t\tdirection: 'horizontal',\n\t\t\t\t\t\t\tid: uniqueId(),\n\t\t\t\t\t\t\tgaps:\n\t\t\t\t\t\t\t\tsnap.protrusionDirection === 'left'\n\t\t\t\t\t\t\t\t\t? [\n\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\tstartEdge: selectionSides.right,\n\t\t\t\t\t\t\t\t\t\t\t\tendEdge: startEdge.map((v) =>\n\t\t\t\t\t\t\t\t\t\t\t\t\tv.clone().addXY(-startNode.pageBounds.width, 0)\n\t\t\t\t\t\t\t\t\t\t\t\t) as [Vec, Vec],\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t{ startEdge, endEdge },\n\t\t\t\t\t\t\t\t\t\t\t...findAdjacentGaps(\n\t\t\t\t\t\t\t\t\t\t\t\thorizontal,\n\t\t\t\t\t\t\t\t\t\t\t\tendNode.id,\n\t\t\t\t\t\t\t\t\t\t\t\tlength,\n\t\t\t\t\t\t\t\t\t\t\t\t'forward',\n\t\t\t\t\t\t\t\t\t\t\t\tgapBreadthIntersection\n\t\t\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t\t\t: [\n\t\t\t\t\t\t\t\t\t\t\t...findAdjacentGaps(\n\t\t\t\t\t\t\t\t\t\t\t\thorizontal,\n\t\t\t\t\t\t\t\t\t\t\t\tstartNode.id,\n\t\t\t\t\t\t\t\t\t\t\t\tlength,\n\t\t\t\t\t\t\t\t\t\t\t\t'backward',\n\t\t\t\t\t\t\t\t\t\t\t\tgapBreadthIntersection\n\t\t\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t\t\t\t{ startEdge, endEdge },\n\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\tstartEdge: endEdge.map((v) =>\n\t\t\t\t\t\t\t\t\t\t\t\t\tv.clone().addXY(snap.gap.endNode.pageBounds.width, 0)\n\t\t\t\t\t\t\t\t\t\t\t\t) as [Vec, Vec],\n\t\t\t\t\t\t\t\t\t\t\t\tendEdge: selectionSides.left,\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t})\n\n\t\t\t\t\t\tbreak\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (nearestSnapsY.length > 0) {\n\t\t\tfor (const snap of nearestSnapsY) {\n\t\t\t\tif (snap.type === 'points') continue\n\n\t\t\t\tconst {\n\t\t\t\t\tgap: { breadthIntersection, startEdge, startNode, endNode, length, endEdge },\n\t\t\t\t} = snap\n\n\t\t\t\tswitch (snap.type) {\n\t\t\t\t\tcase 'gap_center': {\n\t\t\t\t\t\tconst newGapsLength = (length - selectionPageBounds.height) / 2\n\t\t\t\t\t\tconst gapBreadthIntersection = rangeIntersection(\n\t\t\t\t\t\t\tbreadthIntersection[0],\n\t\t\t\t\t\t\tbreadthIntersection[1],\n\t\t\t\t\t\t\tselectionPageBounds.minX,\n\t\t\t\t\t\t\tselectionPageBounds.maxX\n\t\t\t\t\t\t)!\n\n\t\t\t\t\t\tresult.push({\n\t\t\t\t\t\t\ttype: 'gaps',\n\t\t\t\t\t\t\tdirection: 'vertical',\n\t\t\t\t\t\t\tid: uniqueId(),\n\t\t\t\t\t\t\tgaps: [\n\t\t\t\t\t\t\t\t...findAdjacentGaps(\n\t\t\t\t\t\t\t\t\tvertical,\n\t\t\t\t\t\t\t\t\tstartNode.id,\n\t\t\t\t\t\t\t\t\tnewGapsLength,\n\t\t\t\t\t\t\t\t\t'backward',\n\t\t\t\t\t\t\t\t\tgapBreadthIntersection\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tstartEdge,\n\t\t\t\t\t\t\t\t\tendEdge: selectionSides.top,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tstartEdge: selectionSides.bottom,\n\t\t\t\t\t\t\t\t\tendEdge,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t...findAdjacentGaps(\n\t\t\t\t\t\t\t\t\tvertical,\n\t\t\t\t\t\t\t\t\tsnap.gap.endNode.id,\n\t\t\t\t\t\t\t\t\tnewGapsLength,\n\t\t\t\t\t\t\t\t\t'forward',\n\t\t\t\t\t\t\t\t\tgapBreadthIntersection\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t})\n\t\t\t\t\t\tbreak\n\t\t\t\t\t}\n\t\t\t\t\tcase 'gap_duplicate':\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tconst gapBreadthIntersection = rangeIntersection(\n\t\t\t\t\t\t\t\tbreadthIntersection[0],\n\t\t\t\t\t\t\t\tbreadthIntersection[1],\n\t\t\t\t\t\t\t\tselectionPageBounds.minX,\n\t\t\t\t\t\t\t\tselectionPageBounds.maxX\n\t\t\t\t\t\t\t)!\n\n\t\t\t\t\t\t\tresult.push({\n\t\t\t\t\t\t\t\ttype: 'gaps',\n\t\t\t\t\t\t\t\tdirection: 'vertical',\n\t\t\t\t\t\t\t\tid: uniqueId(),\n\t\t\t\t\t\t\t\tgaps:\n\t\t\t\t\t\t\t\t\tsnap.protrusionDirection === 'top'\n\t\t\t\t\t\t\t\t\t\t? [\n\t\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\t\tstartEdge: selectionSides.bottom,\n\t\t\t\t\t\t\t\t\t\t\t\t\tendEdge: startEdge.map((v) =>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tv.clone().addXY(0, -startNode.pageBounds.height)\n\t\t\t\t\t\t\t\t\t\t\t\t\t) as [Vec, Vec],\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t{ startEdge, endEdge },\n\t\t\t\t\t\t\t\t\t\t\t\t...findAdjacentGaps(\n\t\t\t\t\t\t\t\t\t\t\t\t\tvertical,\n\t\t\t\t\t\t\t\t\t\t\t\t\tendNode.id,\n\t\t\t\t\t\t\t\t\t\t\t\t\tlength,\n\t\t\t\t\t\t\t\t\t\t\t\t\t'forward',\n\t\t\t\t\t\t\t\t\t\t\t\t\tgapBreadthIntersection\n\t\t\t\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t\t\t\t: [\n\t\t\t\t\t\t\t\t\t\t\t\t...findAdjacentGaps(\n\t\t\t\t\t\t\t\t\t\t\t\t\tvertical,\n\t\t\t\t\t\t\t\t\t\t\t\t\tstartNode.id,\n\t\t\t\t\t\t\t\t\t\t\t\t\tlength,\n\t\t\t\t\t\t\t\t\t\t\t\t\t'backward',\n\t\t\t\t\t\t\t\t\t\t\t\t\tgapBreadthIntersection\n\t\t\t\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t\t\t\t\t{ startEdge, endEdge },\n\t\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\t\tstartEdge: endEdge.map((v) =>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tv.clone().addXY(0, endNode.pageBounds.height)\n\t\t\t\t\t\t\t\t\t\t\t\t\t) as [Vec, Vec],\n\t\t\t\t\t\t\t\t\t\t\t\t\tendEdge: selectionSides.top,\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tdedupeGapSnaps(result)\n\t\treturn result\n\t}\n}\n\nfunction getResizeSnapPointsForHandle(\n\thandle: SelectionCorner | SelectionEdge | 'any',\n\tselectionPageBounds: Box\n): BoundsSnapPoint[] {\n\tconst { minX, maxX, minY, maxY } = selectionPageBounds\n\tconst result: BoundsSnapPoint[] = []\n\n\t// top left corner\n\tswitch (handle) {\n\t\tcase 'top':\n\t\tcase 'left':\n\t\tcase 'top_left':\n\t\tcase 'any':\n\t\t\tresult.push({\n\t\t\t\tid: 'top_left',\n\t\t\t\thandle: 'top_left',\n\t\t\t\tx: minX,\n\t\t\t\ty: minY,\n\t\t\t})\n\t}\n\n\t// top right corner\n\tswitch (handle) {\n\t\tcase 'top':\n\t\tcase 'right':\n\t\tcase 'top_right':\n\t\tcase 'any':\n\t\t\tresult.push({\n\t\t\t\tid: 'top_right',\n\t\t\t\thandle: 'top_right',\n\t\t\t\tx: maxX,\n\t\t\t\ty: minY,\n\t\t\t})\n\t}\n\n\t// bottom right corner\n\tswitch (handle) {\n\t\tcase 'bottom':\n\t\tcase 'right':\n\t\tcase 'bottom_right':\n\t\tcase 'any':\n\t\t\tresult.push({\n\t\t\t\tid: 'bottom_right',\n\t\t\t\thandle: 'bottom_right',\n\t\t\t\tx: maxX,\n\t\t\t\ty: maxY,\n\t\t\t})\n\t}\n\n\t// bottom left corner\n\tswitch (handle) {\n\t\tcase 'bottom':\n\t\tcase 'left':\n\t\tcase 'bottom_left':\n\t\tcase 'any':\n\t\t\tresult.push({\n\t\t\t\tid: 'bottom_left',\n\t\t\t\thandle: 'bottom_left',\n\t\t\t\tx: minX,\n\t\t\t\ty: maxY,\n\t\t\t})\n\t}\n\n\treturn result\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { computed } from '@tldraw/state'\nimport { TLShape, TLShapeId, VecModel } from '@tldraw/tlschema'\nimport { assertExists, dedupe, uniqueId } from '@tldraw/utils'\nimport {\n\tBox,\n\tSelectionCorner,\n\tSelectionEdge,\n\tflipSelectionHandleX,\n\tflipSelectionHandleY,\n\tisSelectionCorner,\n} from '../../../primitives/Box'\nimport { Mat } from '../../../primitives/Mat'\nimport { Vec } from '../../../primitives/Vec'\nimport { rangeIntersection, rangesOverlap } from '../../../primitives/utils'\nimport { Editor } from '../../Editor'\nimport {\n\tGapsSnapIndicator,\n\tPointsSnapIndicator,\n\tSnapData,\n\tSnapIndicator,\n\tSnapManager,\n} from './SnapManager'\n\n/**\n * When moving or resizing shapes, the bounds of the shape can snap to key geometry on other nearby\n * shapes. Customize how a shape snaps to others with {@link ShapeUtil.getBoundsSnapGeometry}.\n *\n * @public\n */\nexport interface BoundsSnapGeometry {\n\t/**\n\t * Points that this shape will snap to. By default, this will be the corners and center of the\n\t * shapes bounding box. To disable snapping to a specific point, use an empty array.\n\t */\n\tpoints?: VecModel[]\n}\n\n/** @public */\nexport interface BoundsSnapPoint {\n\tid: string\n\tx: number\n\ty: number\n\thandle?: SelectionCorner\n}\n\ninterface SnapPair {\n\tthisPoint: BoundsSnapPoint\n\totherPoint: BoundsSnapPoint\n}\n\ninterface NearestPointsSnap {\n\t// selection snaps to a nearby snap point\n\ttype: 'points'\n\tpoints: SnapPair\n\tnudge: number\n}\n\ntype NearestSnap =\n\t| NearestPointsSnap\n\t| {\n\t\t\t// selection snaps to the center of a gap\n\t\t\ttype: 'gap_center'\n\t\t\tgap: Gap\n\t\t\tnudge: number\n\t }\n\t| {\n\t\t\t// selection snaps to create a new gap of equal size to another gap\n\t\t\t// on the opposite side of some shape\n\t\t\ttype: 'gap_duplicate'\n\t\t\tgap: Gap\n\t\t\tprotrusionDirection: 'left' | 'right' | 'top' | 'bottom'\n\t\t\tnudge: number\n\t }\n\ninterface GapNode {\n\tid: TLShapeId\n\tpageBounds: Box\n}\n\ninterface Gap {\n\t// e.g.\n\t// start\n\t// edge \u2502 breadth\n\t// \u2502 intersection\n\t// \u25BC [40,100] end\n\t// \u2502 \u2502 edge\n\t// \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u2502 100,0 \u2502 \u2502\n\t// \u2502 \u2502 \u2502 \u25BC \u25BC\n\t// \u2502 \u2502 \u2502\n\t// \u2502 start \u2502 \u2502 \u2502 200,40 \u2502 \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n\t// \u2502 node \u2502 \u2502 \u2502 \u2502 \u2502 \u2502\n\t// \u2502 \u2502 \u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524 \u2502 end \u2502\n\t// \u2502 \u2502 \u2502 \u2502 \u2502 \u2502 node \u2502\n\t// \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2502 100,100 \u2502 \u2502 \u2502 \u2502\n\t// \u2502 \u2502 \u2502\n\t// 200,120 \u2502 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n\t//\n\t// length 100\n\t// \u25C4\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u25BA\n\tstartNode: GapNode\n\tendNode: GapNode\n\tstartEdge: [Vec, Vec]\n\tendEdge: [Vec, Vec]\n\tlength: number\n\tbreadthIntersection: [number, number]\n}\n\nconst round = (x: number) => {\n\t// round numbers to avoid glitches for floating point rounding errors\n\tconst decimalPlacesTolerance = 8\n\treturn Math.round(x * 10 ** decimalPlacesTolerance) / 10 ** decimalPlacesTolerance\n}\n\nfunction findAdjacentGaps(\n\tgaps: Gap[],\n\tshapeId: TLShapeId,\n\tgapLength: number,\n\tdirection: 'forward' | 'backward',\n\tintersection: [number, number]\n): Gap[] {\n\t// TODO: take advantage of the fact that gaps is sorted by starting position?\n\tconst matches = gaps.filter(\n\t\t(gap) =>\n\t\t\t(direction === 'forward' ? gap.startNode.id === shapeId : gap.endNode.id === shapeId) &&\n\t\t\tround(gap.length) === round(gapLength) &&\n\t\t\trangeIntersection(\n\t\t\t\tgap.breadthIntersection[0],\n\t\t\t\tgap.breadthIntersection[1],\n\t\t\t\tintersection[0],\n\t\t\t\tintersection[1]\n\t\t\t)\n\t)\n\n\tif (matches.length === 0) return []\n\n\tconst nextNodes = new Set<TLShapeId>()\n\n\tmatches.forEach((match) => {\n\t\tconst node = direction === 'forward' ? match.endNode.id : match.startNode.id\n\t\tif (!nextNodes.has(node)) {\n\t\t\tnextNodes.add(node)\n\t\t\tconst foundGaps = findAdjacentGaps(\n\t\t\t\tgaps,\n\t\t\t\tnode,\n\t\t\t\tgapLength,\n\t\t\t\tdirection,\n\t\t\t\trangeIntersection(\n\t\t\t\t\tmatch.breadthIntersection[0],\n\t\t\t\t\tmatch.breadthIntersection[1],\n\t\t\t\t\tintersection[0],\n\t\t\t\t\tintersection[1]\n\t\t\t\t)!\n\t\t\t)\n\n\t\t\tmatches.push(...foundGaps)\n\t\t}\n\t})\n\n\treturn matches\n}\n\nfunction dedupeGapSnaps(snaps: Array<Extract<SnapIndicator, { type: 'gaps' }>>) {\n\t// sort by descending order of number of gaps\n\tsnaps.sort((a, b) => b.gaps.length - a.gaps.length)\n\t// pop off any that are included already\n\tfor (let i = snaps.length - 1; i > 0; i--) {\n\t\tconst snap = snaps[i]\n\t\tfor (let j = i - 1; j >= 0; j--) {\n\t\t\tconst otherSnap = snaps[j]\n\t\t\t// if every edge in this snap is included in the other snap somewhere, then it's redundant\n\t\t\tif (\n\t\t\t\totherSnap.direction === snap.direction &&\n\t\t\t\tsnap.gaps.every(\n\t\t\t\t\t(gap) =>\n\t\t\t\t\t\totherSnap.gaps.some(\n\t\t\t\t\t\t\t(otherGap) =>\n\t\t\t\t\t\t\t\tround(gap.startEdge[0].x) === round(otherGap.startEdge[0].x) &&\n\t\t\t\t\t\t\t\tround(gap.startEdge[0].y) === round(otherGap.startEdge[0].y) &&\n\t\t\t\t\t\t\t\tround(gap.startEdge[1].x) === round(otherGap.startEdge[1].x) &&\n\t\t\t\t\t\t\t\tround(gap.startEdge[1].y) === round(otherGap.startEdge[1].y)\n\t\t\t\t\t\t) &&\n\t\t\t\t\t\totherSnap.gaps.some(\n\t\t\t\t\t\t\t(otherGap) =>\n\t\t\t\t\t\t\t\tround(gap.endEdge[0].x) === round(otherGap.endEdge[0].x) &&\n\t\t\t\t\t\t\t\tround(gap.endEdge[0].y) === round(otherGap.endEdge[0].y) &&\n\t\t\t\t\t\t\t\tround(gap.endEdge[1].x) === round(otherGap.endEdge[1].x) &&\n\t\t\t\t\t\t\t\tround(gap.endEdge[1].y) === round(otherGap.endEdge[1].y)\n\t\t\t\t\t\t)\n\t\t\t\t)\n\t\t\t) {\n\t\t\t\tsnaps.splice(i, 1)\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t}\n}\n\n/** @public */\nexport class BoundsSnaps {\n\treadonly editor: Editor\n\tconstructor(readonly manager: SnapManager) {\n\t\tthis.editor = manager.editor\n\t}\n\n\t@computed private getSnapPointsCache() {\n\t\tconst { editor } = this\n\t\treturn editor.store.createComputedCache<BoundsSnapPoint[], TLShape>('snapPoints', (shape) => {\n\t\t\tconst pageTransform = editor.getShapePageTransform(shape.id)\n\t\t\tif (!pageTransform) return undefined\n\t\t\tconst boundsSnapGeometry = editor.getShapeUtil(shape).getBoundsSnapGeometry(shape)\n\t\t\tconst snapPoints =\n\t\t\t\tboundsSnapGeometry.points ?? editor.getShapeGeometry(shape).bounds.cornersAndCenter\n\n\t\t\tif (!pageTransform || !snapPoints) return undefined\n\t\t\treturn snapPoints.map((point, i) => {\n\t\t\t\tconst { x, y } = Mat.applyToPoint(pageTransform, point)\n\t\t\t\treturn { x, y, id: `${shape.id}:${i}` }\n\t\t\t})\n\t\t})\n\t}\n\n\tgetSnapPoints(shapeId: TLShapeId) {\n\t\treturn this.getSnapPointsCache().get(shapeId) ?? []\n\t}\n\n\t// Points which belong to snappable shapes\n\t@computed private getSnappablePoints() {\n\t\tconst snapPointsCache = this.getSnapPointsCache()\n\t\tconst snappableShapes = this.manager.getSnappableShapes()\n\t\tconst result: BoundsSnapPoint[] = []\n\n\t\tfor (const shapeId of snappableShapes) {\n\t\t\tconst snapPoints = snapPointsCache.get(shapeId)\n\t\t\tif (snapPoints) {\n\t\t\t\tresult.push(...snapPoints)\n\t\t\t}\n\t\t}\n\n\t\treturn result\n\t}\n\n\t@computed private getSnappableGapNodes(): Array<GapNode> {\n\t\treturn Array.from(this.manager.getSnappableShapes(), (shapeId) => ({\n\t\t\tid: shapeId,\n\t\t\tpageBounds: assertExists(this.editor.getShapePageBounds(shapeId)),\n\t\t}))\n\t}\n\n\t@computed private getVisibleGaps(): { horizontal: Gap[]; vertical: Gap[] } {\n\t\tconst horizontal: Gap[] = []\n\t\tconst vertical: Gap[] = []\n\n\t\tlet startNode: GapNode, endNode: GapNode\n\n\t\tconst sortedShapesOnCurrentPageHorizontal = this.getSnappableGapNodes().sort((a, b) => {\n\t\t\treturn a.pageBounds.minX - b.pageBounds.minX\n\t\t})\n\n\t\t// Collect horizontal gaps\n\t\tfor (let i = 0; i < sortedShapesOnCurrentPageHorizontal.length; i++) {\n\t\t\tstartNode = sortedShapesOnCurrentPageHorizontal[i]\n\t\t\tfor (let j = i + 1; j < sortedShapesOnCurrentPageHorizontal.length; j++) {\n\t\t\t\tendNode = sortedShapesOnCurrentPageHorizontal[j]\n\n\t\t\t\tif (\n\t\t\t\t\t// is there space between the boxes\n\t\t\t\t\tstartNode.pageBounds.maxX < endNode.pageBounds.minX &&\n\t\t\t\t\t// and they overlap in the y axis\n\t\t\t\t\trangesOverlap(\n\t\t\t\t\t\tstartNode.pageBounds.minY,\n\t\t\t\t\t\tstartNode.pageBounds.maxY,\n\t\t\t\t\t\tendNode.pageBounds.minY,\n\t\t\t\t\t\tendNode.pageBounds.maxY\n\t\t\t\t\t)\n\t\t\t\t) {\n\t\t\t\t\thorizontal.push({\n\t\t\t\t\t\tstartNode,\n\t\t\t\t\t\tendNode,\n\t\t\t\t\t\tstartEdge: [\n\t\t\t\t\t\t\tnew Vec(startNode.pageBounds.maxX, startNode.pageBounds.minY),\n\t\t\t\t\t\t\tnew Vec(startNode.pageBounds.maxX, startNode.pageBounds.maxY),\n\t\t\t\t\t\t],\n\t\t\t\t\t\tendEdge: [\n\t\t\t\t\t\t\tnew Vec(endNode.pageBounds.minX, endNode.pageBounds.minY),\n\t\t\t\t\t\t\tnew Vec(endNode.pageBounds.minX, endNode.pageBounds.maxY),\n\t\t\t\t\t\t],\n\t\t\t\t\t\tlength: endNode.pageBounds.minX - startNode.pageBounds.maxX,\n\t\t\t\t\t\tbreadthIntersection: rangeIntersection(\n\t\t\t\t\t\t\tstartNode.pageBounds.minY,\n\t\t\t\t\t\t\tstartNode.pageBounds.maxY,\n\t\t\t\t\t\t\tendNode.pageBounds.minY,\n\t\t\t\t\t\t\tendNode.pageBounds.maxY\n\t\t\t\t\t\t)!,\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Collect vertical gaps\n\t\tconst sortedShapesOnCurrentPageVertical = sortedShapesOnCurrentPageHorizontal.sort((a, b) => {\n\t\t\treturn a.pageBounds.minY - b.pageBounds.minY\n\t\t})\n\n\t\tfor (let i = 0; i < sortedShapesOnCurrentPageVertical.length; i++) {\n\t\t\tstartNode = sortedShapesOnCurrentPageVertical[i]\n\t\t\tfor (let j = i + 1; j < sortedShapesOnCurrentPageVertical.length; j++) {\n\t\t\t\tendNode = sortedShapesOnCurrentPageVertical[j]\n\n\t\t\t\tif (\n\t\t\t\t\t// is there space between the boxes\n\t\t\t\t\tstartNode.pageBounds.maxY < endNode.pageBounds.minY &&\n\t\t\t\t\t// do they overlap in the x axis\n\t\t\t\t\trangesOverlap(\n\t\t\t\t\t\tstartNode.pageBounds.minX,\n\t\t\t\t\t\tstartNode.pageBounds.maxX,\n\t\t\t\t\t\tendNode.pageBounds.minX,\n\t\t\t\t\t\tendNode.pageBounds.maxX\n\t\t\t\t\t)\n\t\t\t\t) {\n\t\t\t\t\tvertical.push({\n\t\t\t\t\t\tstartNode,\n\t\t\t\t\t\tendNode,\n\t\t\t\t\t\tstartEdge: [\n\t\t\t\t\t\t\tnew Vec(startNode.pageBounds.minX, startNode.pageBounds.maxY),\n\t\t\t\t\t\t\tnew Vec(startNode.pageBounds.maxX, startNode.pageBounds.maxY),\n\t\t\t\t\t\t],\n\t\t\t\t\t\tendEdge: [\n\t\t\t\t\t\t\tnew Vec(endNode.pageBounds.minX, endNode.pageBounds.minY),\n\t\t\t\t\t\t\tnew Vec(endNode.pageBounds.maxX, endNode.pageBounds.minY),\n\t\t\t\t\t\t],\n\t\t\t\t\t\tlength: endNode.pageBounds.minY - startNode.pageBounds.maxY,\n\t\t\t\t\t\tbreadthIntersection: rangeIntersection(\n\t\t\t\t\t\t\tstartNode.pageBounds.minX,\n\t\t\t\t\t\t\tstartNode.pageBounds.maxX,\n\t\t\t\t\t\t\tendNode.pageBounds.minX,\n\t\t\t\t\t\t\tendNode.pageBounds.maxX\n\t\t\t\t\t\t)!,\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn { horizontal, vertical }\n\t}\n\n\tsnapTranslateShapes({\n\t\tlockedAxis,\n\t\tinitialSelectionPageBounds,\n\t\tinitialSelectionSnapPoints,\n\t\tdragDelta,\n\t}: {\n\t\tlockedAxis: 'x' | 'y' | null\n\t\tinitialSelectionSnapPoints: BoundsSnapPoint[]\n\t\tinitialSelectionPageBounds: Box\n\t\tdragDelta: Vec\n\t}): SnapData {\n\t\tconst snapThreshold = this.manager.getSnapThreshold()\n\t\tconst visibleSnapPointsNotInSelection = this.getSnappablePoints()\n\n\t\tconst selectionPageBounds = initialSelectionPageBounds.clone().translate(dragDelta)\n\n\t\tconst selectionSnapPoints: BoundsSnapPoint[] = initialSelectionSnapPoints.map(\n\t\t\t({ x, y }, i) => ({\n\t\t\t\tid: 'selection:' + i,\n\t\t\t\tx: x + dragDelta.x,\n\t\t\t\ty: y + dragDelta.y,\n\t\t\t})\n\t\t)\n\n\t\tconst otherNodeSnapPoints = visibleSnapPointsNotInSelection\n\n\t\tconst nearestSnapsX: NearestSnap[] = []\n\t\tconst nearestSnapsY: NearestSnap[] = []\n\t\tconst minOffset = new Vec(snapThreshold, snapThreshold)\n\n\t\tthis.collectPointSnaps({\n\t\t\tminOffset,\n\t\t\tnearestSnapsX,\n\t\t\tnearestSnapsY,\n\t\t\totherNodeSnapPoints,\n\t\t\tselectionSnapPoints,\n\t\t})\n\n\t\tthis.collectGapSnaps({\n\t\t\tselectionPageBounds,\n\t\t\tnearestSnapsX,\n\t\t\tnearestSnapsY,\n\t\t\tminOffset,\n\t\t})\n\n\t\t// at the same time, calculate how far we need to nudge the shape to 'snap' to the target point(s)\n\t\tconst nudge = new Vec(\n\t\t\tlockedAxis === 'x' ? 0 : (nearestSnapsX[0]?.nudge ?? 0),\n\t\t\tlockedAxis === 'y' ? 0 : (nearestSnapsY[0]?.nudge ?? 0)\n\t\t)\n\n\t\t// ok we've figured out how much the box should be nudged, now let's find all the snap points\n\t\t// that are exact after making that translation, so we can render all of them.\n\t\t// first reset everything and adjust the original shapes to conform to the nudge\n\t\tminOffset.x = 0\n\t\tminOffset.y = 0\n\t\tnearestSnapsX.length = 0\n\t\tnearestSnapsY.length = 0\n\t\tselectionSnapPoints.forEach((s) => {\n\t\t\ts.x += nudge.x\n\t\t\ts.y += nudge.y\n\t\t})\n\t\tselectionPageBounds.translate(nudge)\n\n\t\tthis.collectPointSnaps({\n\t\t\tminOffset,\n\t\t\tnearestSnapsX,\n\t\t\tnearestSnapsY,\n\t\t\totherNodeSnapPoints,\n\t\t\tselectionSnapPoints,\n\t\t})\n\n\t\tthis.collectGapSnaps({\n\t\t\tselectionPageBounds,\n\t\t\tnearestSnapsX,\n\t\t\tnearestSnapsY,\n\t\t\tminOffset,\n\t\t})\n\n\t\tconst pointSnapsLines = this.getPointSnapLines({\n\t\t\tnearestSnapsX,\n\t\t\tnearestSnapsY,\n\t\t})\n\n\t\tconst gapSnapLines = this.getGapSnapLines({\n\t\t\tselectionPageBounds,\n\t\t\tnearestSnapsX,\n\t\t\tnearestSnapsY,\n\t\t})\n\n\t\tthis.manager.setIndicators([...gapSnapLines, ...pointSnapsLines])\n\n\t\treturn { nudge }\n\t}\n\n\tsnapResizeShapes({\n\t\tinitialSelectionPageBounds,\n\t\tdragDelta,\n\t\thandle: originalHandle,\n\t\tisAspectRatioLocked,\n\t\tisResizingFromCenter,\n\t}: {\n\t\t// the page bounds when the pointer went down, before any dragging\n\t\tinitialSelectionPageBounds: Box\n\t\t// how far the pointer has been dragged\n\t\tdragDelta: Vec\n\n\t\thandle: SelectionCorner | SelectionEdge\n\t\tisAspectRatioLocked: boolean\n\t\tisResizingFromCenter: boolean\n\t}): SnapData {\n\t\tconst snapThreshold = this.manager.getSnapThreshold()\n\n\t\t// first figure out the new bounds of the selection\n\t\tconst {\n\t\t\tbox: unsnappedResizedPageBounds,\n\t\t\tscaleX,\n\t\t\tscaleY,\n\t\t} = Box.Resize(\n\t\t\tinitialSelectionPageBounds,\n\t\t\toriginalHandle,\n\t\t\tisResizingFromCenter ? dragDelta.x * 2 : dragDelta.x,\n\t\t\tisResizingFromCenter ? dragDelta.y * 2 : dragDelta.y,\n\t\t\tisAspectRatioLocked\n\t\t)\n\n\t\tlet handle = originalHandle\n\n\t\tif (scaleX < 0) {\n\t\t\thandle = flipSelectionHandleX(handle)\n\t\t}\n\t\tif (scaleY < 0) {\n\t\t\thandle = flipSelectionHandleY(handle)\n\t\t}\n\n\t\tif (isResizingFromCenter) {\n\t\t\t// reposition if resizing from center\n\t\t\tunsnappedResizedPageBounds.center = initialSelectionPageBounds.center\n\t\t}\n\n\t\tconst isXLocked = handle === 'top' || handle === 'bottom'\n\t\tconst isYLocked = handle === 'left' || handle === 'right'\n\n\t\tconst selectionSnapPoints = getResizeSnapPointsForHandle(handle, unsnappedResizedPageBounds)\n\n\t\tconst otherNodeSnapPoints = this.getSnappablePoints()\n\n\t\tconst nearestSnapsX: NearestPointsSnap[] = []\n\t\tconst nearestSnapsY: NearestPointsSnap[] = []\n\t\tconst minOffset = new Vec(snapThreshold, snapThreshold)\n\n\t\tthis.collectPointSnaps({\n\t\t\tminOffset,\n\t\t\tnearestSnapsX,\n\t\t\tnearestSnapsY,\n\t\t\totherNodeSnapPoints,\n\t\t\tselectionSnapPoints,\n\t\t})\n\n\t\t// at the same time, calculate how far we need to nudge the shape to 'snap' to the target point(s)\n\t\tconst nudge = new Vec(\n\t\t\tisXLocked ? 0 : (nearestSnapsX[0]?.nudge ?? 0),\n\t\t\tisYLocked ? 0 : (nearestSnapsY[0]?.nudge ?? 0)\n\t\t)\n\n\t\tif (isAspectRatioLocked && isSelectionCorner(handle) && nudge.len() !== 0) {\n\t\t\t// if the aspect ratio is locked we need to make the nudge diagonal rather than independent in each axis\n\t\t\t// so we use the aspect ratio along with one axis value to set the other axis value, but which axis we use\n\t\t\t// as a source of truth depends what we have snapped to and how far.\n\n\t\t\t// if we found a snap in both axes, pick the closest one and discard the other\n\t\t\tconst primaryNudgeAxis: 'x' | 'y' =\n\t\t\t\tnearestSnapsX.length && nearestSnapsY.length\n\t\t\t\t\t? Math.abs(nudge.x) < Math.abs(nudge.y)\n\t\t\t\t\t\t? 'x'\n\t\t\t\t\t\t: 'y'\n\t\t\t\t\t: nearestSnapsX.length\n\t\t\t\t\t\t? 'x'\n\t\t\t\t\t\t: 'y'\n\n\t\t\tconst ratio = initialSelectionPageBounds.aspectRatio\n\n\t\t\tif (primaryNudgeAxis === 'x') {\n\t\t\t\tnearestSnapsY.length = 0\n\t\t\t\tnudge.y = nudge.x / ratio\n\t\t\t\tif (handle === 'bottom_left' || handle === 'top_right') {\n\t\t\t\t\tnudge.y = -nudge.y\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tnearestSnapsX.length = 0\n\t\t\t\tnudge.x = nudge.y * ratio\n\t\t\t\tif (handle === 'bottom_left' || handle === 'top_right') {\n\t\t\t\t\tnudge.x = -nudge.x\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// now resize the box after nudging, calculate the snaps again, and return the snap lines to match\n\t\t// the fully resized box\n\t\tconst snappedDelta = Vec.Add(dragDelta, nudge)\n\n\t\t// first figure out the new bounds of the selection\n\t\tconst { box: snappedResizedPageBounds } = Box.Resize(\n\t\t\tinitialSelectionPageBounds,\n\t\t\toriginalHandle,\n\t\t\tisResizingFromCenter ? snappedDelta.x * 2 : snappedDelta.x,\n\t\t\tisResizingFromCenter ? snappedDelta.y * 2 : snappedDelta.y,\n\t\t\tisAspectRatioLocked\n\t\t)\n\n\t\tif (isResizingFromCenter) {\n\t\t\t// reposition if resizing from center\n\t\t\tsnappedResizedPageBounds.center = initialSelectionPageBounds.center\n\t\t}\n\n\t\tconst snappedSelectionPoints = getResizeSnapPointsForHandle('any', snappedResizedPageBounds)\n\t\t// calculate snaps again using all points\n\t\tnearestSnapsX.length = 0\n\t\tnearestSnapsY.length = 0\n\t\tminOffset.x = 0\n\t\tminOffset.y = 0\n\n\t\tthis.collectPointSnaps({\n\t\t\tminOffset,\n\t\t\tnearestSnapsX,\n\t\t\tnearestSnapsY,\n\t\t\totherNodeSnapPoints,\n\t\t\tselectionSnapPoints: snappedSelectionPoints,\n\t\t})\n\t\tconst pointSnaps = this.getPointSnapLines({\n\t\t\tnearestSnapsX,\n\t\t\tnearestSnapsY,\n\t\t})\n\n\t\tthis.manager.setIndicators([...pointSnaps])\n\n\t\treturn { nudge }\n\t}\n\n\tprivate collectPointSnaps({\n\t\tselectionSnapPoints,\n\t\totherNodeSnapPoints,\n\t\tminOffset,\n\t\tnearestSnapsX,\n\t\tnearestSnapsY,\n\t}: {\n\t\tselectionSnapPoints: BoundsSnapPoint[]\n\t\totherNodeSnapPoints: BoundsSnapPoint[]\n\t\tminOffset: Vec\n\t\tnearestSnapsX: NearestSnap[]\n\t\tnearestSnapsY: NearestSnap[]\n\t}) {\n\t\t// for each snap point on the bounding box of the selection, find the set of points\n\t\t// which are closest to it in each axis\n\t\tfor (const thisSnapPoint of selectionSnapPoints) {\n\t\t\tfor (const otherSnapPoint of otherNodeSnapPoints) {\n\t\t\t\tconst offset = Vec.Sub(thisSnapPoint, otherSnapPoint)\n\t\t\t\tconst offsetX = Math.abs(offset.x)\n\t\t\t\tconst offsetY = Math.abs(offset.y)\n\n\t\t\t\tif (round(offsetX) <= round(minOffset.x)) {\n\t\t\t\t\tif (round(offsetX) < round(minOffset.x)) {\n\t\t\t\t\t\t// we found a point that is significantly closer than all previous points\n\t\t\t\t\t\t// so wipe the slate clean and start over\n\t\t\t\t\t\tnearestSnapsX.length = 0\n\t\t\t\t\t}\n\n\t\t\t\t\tnearestSnapsX.push({\n\t\t\t\t\t\ttype: 'points',\n\t\t\t\t\t\tpoints: { thisPoint: thisSnapPoint, otherPoint: otherSnapPoint },\n\t\t\t\t\t\tnudge: otherSnapPoint.x - thisSnapPoint.x,\n\t\t\t\t\t})\n\t\t\t\t\tminOffset.x = offsetX\n\t\t\t\t}\n\n\t\t\t\tif (round(offsetY) <= round(minOffset.y)) {\n\t\t\t\t\tif (round(offsetY) < round(minOffset.y)) {\n\t\t\t\t\t\t// we found a point that is significantly closer than all previous points\n\t\t\t\t\t\t// so wipe the slate clean and start over\n\t\t\t\t\t\tnearestSnapsY.length = 0\n\t\t\t\t\t}\n\t\t\t\t\tnearestSnapsY.push({\n\t\t\t\t\t\ttype: 'points',\n\t\t\t\t\t\tpoints: { thisPoint: thisSnapPoint, otherPoint: otherSnapPoint },\n\t\t\t\t\t\tnudge: otherSnapPoint.y - thisSnapPoint.y,\n\t\t\t\t\t})\n\t\t\t\t\tminOffset.y = offsetY\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate collectGapSnaps({\n\t\tselectionPageBounds,\n\t\tminOffset,\n\t\tnearestSnapsX,\n\t\tnearestSnapsY,\n\t}: {\n\t\tselectionPageBounds: Box\n\t\tminOffset: Vec\n\t\tnearestSnapsX: NearestSnap[]\n\t\tnearestSnapsY: NearestSnap[]\n\t}) {\n\t\tconst { horizontal, vertical } = this.getVisibleGaps()\n\n\t\tfor (const gap of horizontal) {\n\t\t\t// ignore this gap if the selection doesn't overlap with it in the y axis\n\t\t\tif (\n\t\t\t\t!rangesOverlap(\n\t\t\t\t\tgap.breadthIntersection[0],\n\t\t\t\t\tgap.breadthIntersection[1],\n\t\t\t\t\tselectionPageBounds.minY,\n\t\t\t\t\tselectionPageBounds.maxY\n\t\t\t\t)\n\t\t\t) {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\t// check for center match\n\t\t\tconst gapMidX = gap.startEdge[0].x + gap.length / 2\n\t\t\tconst centerNudge = gapMidX - selectionPageBounds.center.x\n\t\t\tconst gapIsLargerThanSelection = gap.length > selectionPageBounds.width\n\n\t\t\tif (gapIsLargerThanSelection && round(Math.abs(centerNudge)) <= round(minOffset.x)) {\n\t\t\t\tif (round(Math.abs(centerNudge)) < round(minOffset.x)) {\n\t\t\t\t\t// reset if we found a closer snap\n\t\t\t\t\tnearestSnapsX.length = 0\n\t\t\t\t}\n\t\t\t\tminOffset.x = Math.abs(centerNudge)\n\n\t\t\t\tconst snap: NearestSnap = {\n\t\t\t\t\ttype: 'gap_center',\n\t\t\t\t\tgap,\n\t\t\t\t\tnudge: centerNudge,\n\t\t\t\t}\n\n\t\t\t\t// we need to avoid creating visual noise with too many center snaps in situations\n\t\t\t\t// where there are lots of adjacent items with even spacing\n\t\t\t\t// so let's only show other center snaps where the gap's breadth does not overlap with this one\n\t\t\t\t// i.e.\n\t\t\t\t// \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n\t\t\t\t// \u2502 \u2502\n\t\t\t\t// \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2518\n\t\t\t\t// \u253C \u2502\n\t\t\t\t// \u250C\u2500\u2500\u2500\u2500\u2500\u2534\u2510 \u2502\n\t\t\t\t// \u2502 \u2502 \u253C\n\t\t\t\t// \u2514\u2500\u2500\u2500\u2500\u2500\u252C\u2518 \u2502\n\t\t\t\t// \u253C \u2502\n\t\t\t\t// \u250C\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n\t\t\t\t// \u2502 \u2502 \u25C4\u2500\u2500\u2500\u2500 i'm dragging this one\n\t\t\t\t// \u2514\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n\t\t\t\t// \u2500\u2500\u2500\u2500\u2500\u25BA \u253C \u2502\n\t\t\t\t// \u250C\u2500\u2500\u2500\u2500\u2500\u2534\u2510 \u2502 don't show these\n\t\t\t\t// show these \u2502 \u2502 \u253C larger gaps since\n\t\t\t\t// smaller \u2514\u2500\u2500\u2500\u2500\u2500\u252C\u2518 \u2502 \u25C4\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 the smaller ones\n\t\t\t\t// gaps \u253C \u2502 cover the same\n\t\t\t\t// \u2500\u2500\u2500\u2500\u2500\u25BA \u250C\u2534\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2510 information\n\t\t\t\t// \u2502 \u2502\n\t\t\t\t// \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n\t\t\t\t//\n\t\t\t\t// but we want to show all of these ones since the gap breadths don't overlap\n\t\t\t\t// \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n\t\t\t\t// \u2502 \u2502\n\t\t\t\t// \u250C\u2500\u2500\u2500\u2500\u2510 \u2514\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n\t\t\t\t// \u2502 \u2502 \u2502\n\t\t\t\t// \u2514\u2500\u2500\u252C\u2500\u2518 \u253C\n\t\t\t\t// \u253C \u2502\n\t\t\t\t// \u250C\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2510\n\t\t\t\t// \u2502 \u2502 \u25C4\u2500\u2500\u2500\u2500\u2500 i'm dragging this one\n\t\t\t\t// \u2514\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252C\u2500\u2518\n\t\t\t\t// \u253C \u2502\n\t\t\t\t// \u250C\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2510 \u253C\n\t\t\t\t// \u2502 \u2502 \u2502\n\t\t\t\t// \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u250C\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n\t\t\t\t// \u2502 \u2502\n\t\t\t\t// \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n\n\t\t\t\tconst otherCenterSnap = nearestSnapsX.find(({ type }) => type === 'gap_center') as\n\t\t\t\t\t| Extract<NearestSnap, { type: 'gap_center' }>\n\t\t\t\t\t| undefined\n\n\t\t\t\tconst gapBreadthsOverlap =\n\t\t\t\t\totherCenterSnap &&\n\t\t\t\t\trangeIntersection(\n\t\t\t\t\t\tgap.breadthIntersection[0],\n\t\t\t\t\t\tgap.breadthIntersection[1],\n\t\t\t\t\t\totherCenterSnap.gap.breadthIntersection[0],\n\t\t\t\t\t\totherCenterSnap.gap.breadthIntersection[1]\n\t\t\t\t\t)\n\n\t\t\t\t// if there is another center snap and it's bigger than this one, and it overlaps with this one, replace it\n\t\t\t\tif (otherCenterSnap && otherCenterSnap.gap.length > gap.length && gapBreadthsOverlap) {\n\t\t\t\t\tnearestSnapsX[nearestSnapsX.indexOf(otherCenterSnap)] = snap\n\t\t\t\t} else if (!otherCenterSnap || !gapBreadthsOverlap) {\n\t\t\t\t\tnearestSnapsX.push(snap)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// check for duplication left match\n\t\t\tconst duplicationLeftX = gap.startNode.pageBounds.minX - gap.length\n\t\t\tconst selectionRightX = selectionPageBounds.maxX\n\n\t\t\tconst duplicationLeftNudge = duplicationLeftX - selectionRightX\n\t\t\tif (round(Math.abs(duplicationLeftNudge)) <= round(minOffset.x)) {\n\t\t\t\tif (round(Math.abs(duplicationLeftNudge)) < round(minOffset.x)) {\n\t\t\t\t\t// reset if we found a closer snap\n\t\t\t\t\tnearestSnapsX.length = 0\n\t\t\t\t}\n\t\t\t\tminOffset.x = Math.abs(duplicationLeftNudge)\n\n\t\t\t\tnearestSnapsX.push({\n\t\t\t\t\ttype: 'gap_duplicate',\n\t\t\t\t\tgap,\n\t\t\t\t\tprotrusionDirection: 'left',\n\t\t\t\t\tnudge: duplicationLeftNudge,\n\t\t\t\t})\n\t\t\t}\n\n\t\t\t// check for duplication right match\n\t\t\tconst duplicationRightX = gap.endNode.pageBounds.maxX + gap.length\n\t\t\tconst selectionLeftX = selectionPageBounds.minX\n\n\t\t\tconst duplicationRightNudge = duplicationRightX - selectionLeftX\n\t\t\tif (round(Math.abs(duplicationRightNudge)) <= round(minOffset.x)) {\n\t\t\t\tif (round(Math.abs(duplicationRightNudge)) < round(minOffset.x)) {\n\t\t\t\t\t// reset if we found a closer snap\n\t\t\t\t\tnearestSnapsX.length = 0\n\t\t\t\t}\n\t\t\t\tminOffset.x = Math.abs(duplicationRightNudge)\n\n\t\t\t\tnearestSnapsX.push({\n\t\t\t\t\ttype: 'gap_duplicate',\n\t\t\t\t\tgap,\n\t\t\t\t\tprotrusionDirection: 'right',\n\t\t\t\t\tnudge: duplicationRightNudge,\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\n\t\tfor (const gap of vertical) {\n\t\t\t// ignore this gap if the selection doesn't overlap with it in the y axis\n\t\t\tif (\n\t\t\t\t!rangesOverlap(\n\t\t\t\t\tgap.breadthIntersection[0],\n\t\t\t\t\tgap.breadthIntersection[1],\n\t\t\t\t\tselectionPageBounds.minX,\n\t\t\t\t\tselectionPageBounds.maxX\n\t\t\t\t)\n\t\t\t) {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\t// check for center match\n\t\t\tconst gapMidY = gap.startEdge[0].y + gap.length / 2\n\t\t\tconst centerNudge = gapMidY - selectionPageBounds.center.y\n\n\t\t\tconst gapIsLargerThanSelection = gap.length > selectionPageBounds.height\n\n\t\t\tif (gapIsLargerThanSelection && round(Math.abs(centerNudge)) <= round(minOffset.y)) {\n\t\t\t\tif (round(Math.abs(centerNudge)) < round(minOffset.y)) {\n\t\t\t\t\t// reset if we found a closer snap\n\t\t\t\t\tnearestSnapsY.length = 0\n\t\t\t\t}\n\t\t\t\tminOffset.y = Math.abs(centerNudge)\n\n\t\t\t\tconst snap: NearestSnap = {\n\t\t\t\t\ttype: 'gap_center',\n\t\t\t\t\tgap,\n\t\t\t\t\tnudge: centerNudge,\n\t\t\t\t}\n\n\t\t\t\t// we need to avoid creating visual noise with too many center snaps in situations\n\t\t\t\t// where there are lots of adjacent items with even spacing\n\t\t\t\t// so let's only show other center snaps where the gap's breadth does not overlap with this one\n\t\t\t\t// i.e.\n\t\t\t\t// \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n\t\t\t\t// \u2502 \u2502\n\t\t\t\t// \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2518\n\t\t\t\t// \u253C \u2502\n\t\t\t\t// \u250C\u2500\u2500\u2500\u2500\u2500\u2534\u2510 \u2502\n\t\t\t\t// \u2502 \u2502 \u253C\n\t\t\t\t// \u2514\u2500\u2500\u2500\u2500\u2500\u252C\u2518 \u2502\n\t\t\t\t// \u253C \u2502\n\t\t\t\t// \u250C\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n\t\t\t\t// \u2502 \u2502 \u25C4\u2500\u2500\u2500\u2500 i'm dragging this one\n\t\t\t\t// \u2514\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n\t\t\t\t// \u2500\u2500\u2500\u2500\u2500\u25BA \u253C \u2502\n\t\t\t\t// \u250C\u2500\u2500\u2500\u2500\u2500\u2534\u2510 \u2502 don't show these\n\t\t\t\t// show these \u2502 \u2502 \u253C larger gaps since\n\t\t\t\t// smaller \u2514\u2500\u2500\u2500\u2500\u2500\u252C\u2518 \u2502 \u25C4\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 the smaller ones\n\t\t\t\t// gaps \u253C \u2502 cover the same\n\t\t\t\t// \u2500\u2500\u2500\u2500\u2500\u25BA \u250C\u2534\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2510 information\n\t\t\t\t// \u2502 \u2502\n\t\t\t\t// \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n\t\t\t\t//\n\t\t\t\t// but we want to show all of these ones since the gap breadths don't overlap\n\t\t\t\t// \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n\t\t\t\t// \u2502 \u2502\n\t\t\t\t// \u250C\u2500\u2500\u2500\u2500\u2510 \u2514\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n\t\t\t\t// \u2502 \u2502 \u2502\n\t\t\t\t// \u2514\u2500\u2500\u252C\u2500\u2518 \u253C\n\t\t\t\t// \u253C \u2502\n\t\t\t\t// \u250C\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2510\n\t\t\t\t// \u2502 \u2502 \u25C4\u2500\u2500\u2500\u2500\u2500 i'm dragging this one\n\t\t\t\t// \u2514\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252C\u2500\u2518\n\t\t\t\t// \u253C \u2502\n\t\t\t\t// \u250C\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2510 \u253C\n\t\t\t\t// \u2502 \u2502 \u2502\n\t\t\t\t// \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u250C\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n\t\t\t\t// \u2502 \u2502\n\t\t\t\t// \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n\n\t\t\t\tconst otherCenterSnap = nearestSnapsY.find(({ type }) => type === 'gap_center') as\n\t\t\t\t\t| Extract<NearestSnap, { type: 'gap_center' }>\n\t\t\t\t\t| undefined\n\n\t\t\t\tconst gapBreadthsOverlap =\n\t\t\t\t\totherCenterSnap &&\n\t\t\t\t\trangesOverlap(\n\t\t\t\t\t\totherCenterSnap.gap.breadthIntersection[0],\n\t\t\t\t\t\totherCenterSnap.gap.breadthIntersection[1],\n\t\t\t\t\t\tgap.breadthIntersection[0],\n\t\t\t\t\t\tgap.breadthIntersection[1]\n\t\t\t\t\t)\n\n\t\t\t\t// if there is another center snap and it's bigger than this one, and it overlaps with this one, replace it\n\t\t\t\tif (otherCenterSnap && otherCenterSnap.gap.length > gap.length && gapBreadthsOverlap) {\n\t\t\t\t\tnearestSnapsY[nearestSnapsY.indexOf(otherCenterSnap)] = snap\n\t\t\t\t} else if (!otherCenterSnap || !gapBreadthsOverlap) {\n\t\t\t\t\tnearestSnapsY.push(snap)\n\t\t\t\t}\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\t// check for duplication top match\n\t\t\tconst duplicationTopY = gap.startNode.pageBounds.minY - gap.length\n\t\t\tconst selectionBottomY = selectionPageBounds.maxY\n\n\t\t\tconst duplicationTopNudge = duplicationTopY - selectionBottomY\n\t\t\tif (round(Math.abs(duplicationTopNudge)) <= round(minOffset.y)) {\n\t\t\t\tif (round(Math.abs(duplicationTopNudge)) < round(minOffset.y)) {\n\t\t\t\t\t// reset if we found a closer snap\n\t\t\t\t\tnearestSnapsY.length = 0\n\t\t\t\t}\n\t\t\t\tminOffset.y = Math.abs(duplicationTopNudge)\n\n\t\t\t\tnearestSnapsY.push({\n\t\t\t\t\ttype: 'gap_duplicate',\n\t\t\t\t\tgap,\n\t\t\t\t\tprotrusionDirection: 'top',\n\t\t\t\t\tnudge: duplicationTopNudge,\n\t\t\t\t})\n\t\t\t}\n\n\t\t\t// check for duplication bottom match\n\t\t\tconst duplicationBottomY = gap.endNode.pageBounds.maxY + gap.length\n\t\t\tconst selectionTopY = selectionPageBounds.minY\n\n\t\t\tconst duplicationBottomNudge = duplicationBottomY - selectionTopY\n\t\t\tif (round(Math.abs(duplicationBottomNudge)) <= round(minOffset.y)) {\n\t\t\t\tif (round(Math.abs(duplicationBottomNudge)) < round(minOffset.y)) {\n\t\t\t\t\t// reset if we found a closer snap\n\t\t\t\t\tnearestSnapsY.length = 0\n\t\t\t\t}\n\t\t\t\tminOffset.y = Math.abs(duplicationBottomNudge)\n\n\t\t\t\tnearestSnapsY.push({\n\t\t\t\t\ttype: 'gap_duplicate',\n\t\t\t\t\tgap,\n\t\t\t\t\tprotrusionDirection: 'bottom',\n\t\t\t\t\tnudge: duplicationBottomNudge,\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate getPointSnapLines({\n\t\tnearestSnapsX,\n\t\tnearestSnapsY,\n\t}: {\n\t\tnearestSnapsX: NearestSnap[]\n\t\tnearestSnapsY: NearestSnap[]\n\t}): PointsSnapIndicator[] {\n\t\t// point snaps may align on multiple parallel lines so we need to split the pairs\n\t\t// into groups based on where they are in their their snap axes\n\t\tconst snapGroupsX = {} as { [key: string]: SnapPair[] }\n\t\tconst snapGroupsY = {} as { [key: string]: SnapPair[] }\n\n\t\tif (nearestSnapsX.length > 0) {\n\t\t\tfor (const snap of nearestSnapsX) {\n\t\t\t\tif (snap.type === 'points') {\n\t\t\t\t\tconst key = round(snap.points.otherPoint.x)\n\t\t\t\t\tif (!snapGroupsX[key]) {\n\t\t\t\t\t\tsnapGroupsX[key] = []\n\t\t\t\t\t}\n\t\t\t\t\tsnapGroupsX[key].push(snap.points)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (nearestSnapsY.length > 0) {\n\t\t\tfor (const snap of nearestSnapsY) {\n\t\t\t\tif (snap.type === 'points') {\n\t\t\t\t\tconst key = round(snap.points.otherPoint.y)\n\t\t\t\t\tif (!snapGroupsY[key]) {\n\t\t\t\t\t\tsnapGroupsY[key] = []\n\t\t\t\t\t}\n\t\t\t\t\tsnapGroupsY[key].push(snap.points)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// and finally create all the snap lines for the UI to render\n\t\treturn Object.values(snapGroupsX)\n\t\t\t.concat(Object.values(snapGroupsY))\n\t\t\t.map((snapGroup) => ({\n\t\t\t\tid: uniqueId(),\n\t\t\t\ttype: 'points',\n\t\t\t\tpoints: dedupe(\n\t\t\t\t\tsnapGroup\n\t\t\t\t\t\t.map((snap) => Vec.From(snap.otherPoint))\n\t\t\t\t\t\t// be sure to nudge over the selection snap points\n\t\t\t\t\t\t.concat(snapGroup.map((snap) => Vec.From(snap.thisPoint))),\n\t\t\t\t\t(a: Vec, b: Vec) => a.equals(b)\n\t\t\t\t),\n\t\t\t}))\n\t}\n\n\tprivate getGapSnapLines({\n\t\tselectionPageBounds,\n\t\tnearestSnapsX,\n\t\tnearestSnapsY,\n\t}: {\n\t\tselectionPageBounds: Box\n\t\tnearestSnapsX: NearestSnap[]\n\t\tnearestSnapsY: NearestSnap[]\n\t}): GapsSnapIndicator[] {\n\t\tconst { vertical, horizontal } = this.getVisibleGaps()\n\n\t\tconst selectionSides: Record<SelectionEdge, [Vec, Vec]> = {\n\t\t\ttop: selectionPageBounds.sides[0],\n\t\t\tright: selectionPageBounds.sides[1],\n\t\t\t// need bottom and left to be sorted asc, which .sides is not.\n\t\t\tbottom: [selectionPageBounds.corners[3], selectionPageBounds.corners[2]],\n\t\t\tleft: [selectionPageBounds.corners[0], selectionPageBounds.corners[3]],\n\t\t}\n\n\t\tconst result: GapsSnapIndicator[] = []\n\n\t\tif (nearestSnapsX.length > 0) {\n\t\t\tfor (const snap of nearestSnapsX) {\n\t\t\t\tif (snap.type === 'points') continue\n\n\t\t\t\tconst {\n\t\t\t\t\tgap: { breadthIntersection, startEdge, startNode, endNode, length, endEdge },\n\t\t\t\t} = snap\n\n\t\t\t\tswitch (snap.type) {\n\t\t\t\t\tcase 'gap_center': {\n\t\t\t\t\t\t// create\n\t\t\t\t\t\tconst newGapsLength = (length - selectionPageBounds.width) / 2\n\t\t\t\t\t\tconst gapBreadthIntersection = rangeIntersection(\n\t\t\t\t\t\t\tbreadthIntersection[0],\n\t\t\t\t\t\t\tbreadthIntersection[1],\n\t\t\t\t\t\t\tselectionPageBounds.minY,\n\t\t\t\t\t\t\tselectionPageBounds.maxY\n\t\t\t\t\t\t)!\n\t\t\t\t\t\tresult.push({\n\t\t\t\t\t\t\ttype: 'gaps',\n\t\t\t\t\t\t\tdirection: 'horizontal',\n\t\t\t\t\t\t\tid: uniqueId(),\n\t\t\t\t\t\t\tgaps: [\n\t\t\t\t\t\t\t\t...findAdjacentGaps(\n\t\t\t\t\t\t\t\t\thorizontal,\n\t\t\t\t\t\t\t\t\tstartNode.id,\n\t\t\t\t\t\t\t\t\tnewGapsLength,\n\t\t\t\t\t\t\t\t\t'backward',\n\t\t\t\t\t\t\t\t\tgapBreadthIntersection\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tstartEdge,\n\t\t\t\t\t\t\t\t\tendEdge: selectionSides.left,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tstartEdge: selectionSides.right,\n\t\t\t\t\t\t\t\t\tendEdge,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t...findAdjacentGaps(\n\t\t\t\t\t\t\t\t\thorizontal,\n\t\t\t\t\t\t\t\t\tendNode.id,\n\t\t\t\t\t\t\t\t\tnewGapsLength,\n\t\t\t\t\t\t\t\t\t'forward',\n\t\t\t\t\t\t\t\t\tgapBreadthIntersection\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t})\n\t\t\t\t\t\tbreak\n\t\t\t\t\t}\n\t\t\t\t\tcase 'gap_duplicate': {\n\t\t\t\t\t\t// create\n\t\t\t\t\t\tconst gapBreadthIntersection = rangeIntersection(\n\t\t\t\t\t\t\tbreadthIntersection[0],\n\t\t\t\t\t\t\tbreadthIntersection[1],\n\t\t\t\t\t\t\tselectionPageBounds.minY,\n\t\t\t\t\t\t\tselectionPageBounds.maxY\n\t\t\t\t\t\t)!\n\t\t\t\t\t\tresult.push({\n\t\t\t\t\t\t\ttype: 'gaps',\n\t\t\t\t\t\t\tdirection: 'horizontal',\n\t\t\t\t\t\t\tid: uniqueId(),\n\t\t\t\t\t\t\tgaps:\n\t\t\t\t\t\t\t\tsnap.protrusionDirection === 'left'\n\t\t\t\t\t\t\t\t\t? [\n\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\tstartEdge: selectionSides.right,\n\t\t\t\t\t\t\t\t\t\t\t\tendEdge: startEdge.map((v) =>\n\t\t\t\t\t\t\t\t\t\t\t\t\tv.clone().addXY(-startNode.pageBounds.width, 0)\n\t\t\t\t\t\t\t\t\t\t\t\t) as [Vec, Vec],\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t{ startEdge, endEdge },\n\t\t\t\t\t\t\t\t\t\t\t...findAdjacentGaps(\n\t\t\t\t\t\t\t\t\t\t\t\thorizontal,\n\t\t\t\t\t\t\t\t\t\t\t\tendNode.id,\n\t\t\t\t\t\t\t\t\t\t\t\tlength,\n\t\t\t\t\t\t\t\t\t\t\t\t'forward',\n\t\t\t\t\t\t\t\t\t\t\t\tgapBreadthIntersection\n\t\t\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t\t\t: [\n\t\t\t\t\t\t\t\t\t\t\t...findAdjacentGaps(\n\t\t\t\t\t\t\t\t\t\t\t\thorizontal,\n\t\t\t\t\t\t\t\t\t\t\t\tstartNode.id,\n\t\t\t\t\t\t\t\t\t\t\t\tlength,\n\t\t\t\t\t\t\t\t\t\t\t\t'backward',\n\t\t\t\t\t\t\t\t\t\t\t\tgapBreadthIntersection\n\t\t\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t\t\t\t{ startEdge, endEdge },\n\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\tstartEdge: endEdge.map((v) =>\n\t\t\t\t\t\t\t\t\t\t\t\t\tv.clone().addXY(snap.gap.endNode.pageBounds.width, 0)\n\t\t\t\t\t\t\t\t\t\t\t\t) as [Vec, Vec],\n\t\t\t\t\t\t\t\t\t\t\t\tendEdge: selectionSides.left,\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t})\n\n\t\t\t\t\t\tbreak\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (nearestSnapsY.length > 0) {\n\t\t\tfor (const snap of nearestSnapsY) {\n\t\t\t\tif (snap.type === 'points') continue\n\n\t\t\t\tconst {\n\t\t\t\t\tgap: { breadthIntersection, startEdge, startNode, endNode, length, endEdge },\n\t\t\t\t} = snap\n\n\t\t\t\tswitch (snap.type) {\n\t\t\t\t\tcase 'gap_center': {\n\t\t\t\t\t\tconst newGapsLength = (length - selectionPageBounds.height) / 2\n\t\t\t\t\t\tconst gapBreadthIntersection = rangeIntersection(\n\t\t\t\t\t\t\tbreadthIntersection[0],\n\t\t\t\t\t\t\tbreadthIntersection[1],\n\t\t\t\t\t\t\tselectionPageBounds.minX,\n\t\t\t\t\t\t\tselectionPageBounds.maxX\n\t\t\t\t\t\t)!\n\n\t\t\t\t\t\tresult.push({\n\t\t\t\t\t\t\ttype: 'gaps',\n\t\t\t\t\t\t\tdirection: 'vertical',\n\t\t\t\t\t\t\tid: uniqueId(),\n\t\t\t\t\t\t\tgaps: [\n\t\t\t\t\t\t\t\t...findAdjacentGaps(\n\t\t\t\t\t\t\t\t\tvertical,\n\t\t\t\t\t\t\t\t\tstartNode.id,\n\t\t\t\t\t\t\t\t\tnewGapsLength,\n\t\t\t\t\t\t\t\t\t'backward',\n\t\t\t\t\t\t\t\t\tgapBreadthIntersection\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tstartEdge,\n\t\t\t\t\t\t\t\t\tendEdge: selectionSides.top,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tstartEdge: selectionSides.bottom,\n\t\t\t\t\t\t\t\t\tendEdge,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t...findAdjacentGaps(\n\t\t\t\t\t\t\t\t\tvertical,\n\t\t\t\t\t\t\t\t\tsnap.gap.endNode.id,\n\t\t\t\t\t\t\t\t\tnewGapsLength,\n\t\t\t\t\t\t\t\t\t'forward',\n\t\t\t\t\t\t\t\t\tgapBreadthIntersection\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t})\n\t\t\t\t\t\tbreak\n\t\t\t\t\t}\n\t\t\t\t\tcase 'gap_duplicate':\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tconst gapBreadthIntersection = rangeIntersection(\n\t\t\t\t\t\t\t\tbreadthIntersection[0],\n\t\t\t\t\t\t\t\tbreadthIntersection[1],\n\t\t\t\t\t\t\t\tselectionPageBounds.minX,\n\t\t\t\t\t\t\t\tselectionPageBounds.maxX\n\t\t\t\t\t\t\t)!\n\n\t\t\t\t\t\t\tresult.push({\n\t\t\t\t\t\t\t\ttype: 'gaps',\n\t\t\t\t\t\t\t\tdirection: 'vertical',\n\t\t\t\t\t\t\t\tid: uniqueId(),\n\t\t\t\t\t\t\t\tgaps:\n\t\t\t\t\t\t\t\t\tsnap.protrusionDirection === 'top'\n\t\t\t\t\t\t\t\t\t\t? [\n\t\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\t\tstartEdge: selectionSides.bottom,\n\t\t\t\t\t\t\t\t\t\t\t\t\tendEdge: startEdge.map((v) =>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tv.clone().addXY(0, -startNode.pageBounds.height)\n\t\t\t\t\t\t\t\t\t\t\t\t\t) as [Vec, Vec],\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t{ startEdge, endEdge },\n\t\t\t\t\t\t\t\t\t\t\t\t...findAdjacentGaps(\n\t\t\t\t\t\t\t\t\t\t\t\t\tvertical,\n\t\t\t\t\t\t\t\t\t\t\t\t\tendNode.id,\n\t\t\t\t\t\t\t\t\t\t\t\t\tlength,\n\t\t\t\t\t\t\t\t\t\t\t\t\t'forward',\n\t\t\t\t\t\t\t\t\t\t\t\t\tgapBreadthIntersection\n\t\t\t\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t\t\t\t: [\n\t\t\t\t\t\t\t\t\t\t\t\t...findAdjacentGaps(\n\t\t\t\t\t\t\t\t\t\t\t\t\tvertical,\n\t\t\t\t\t\t\t\t\t\t\t\t\tstartNode.id,\n\t\t\t\t\t\t\t\t\t\t\t\t\tlength,\n\t\t\t\t\t\t\t\t\t\t\t\t\t'backward',\n\t\t\t\t\t\t\t\t\t\t\t\t\tgapBreadthIntersection\n\t\t\t\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t\t\t\t\t{ startEdge, endEdge },\n\t\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\t\tstartEdge: endEdge.map((v) =>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tv.clone().addXY(0, endNode.pageBounds.height)\n\t\t\t\t\t\t\t\t\t\t\t\t\t) as [Vec, Vec],\n\t\t\t\t\t\t\t\t\t\t\t\t\tendEdge: selectionSides.top,\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tdedupeGapSnaps(result)\n\t\treturn result\n\t}\n}\n\nfunction getResizeSnapPointsForHandle(\n\thandle: SelectionCorner | SelectionEdge | 'any',\n\tselectionPageBounds: Box\n): BoundsSnapPoint[] {\n\tconst { minX, maxX, minY, maxY } = selectionPageBounds\n\tconst result: BoundsSnapPoint[] = []\n\n\t// top left corner\n\tswitch (handle) {\n\t\tcase 'top':\n\t\tcase 'left':\n\t\tcase 'top_left':\n\t\tcase 'any':\n\t\t\tresult.push({\n\t\t\t\tid: 'top_left',\n\t\t\t\thandle: 'top_left',\n\t\t\t\tx: minX,\n\t\t\t\ty: minY,\n\t\t\t})\n\t}\n\n\t// top right corner\n\tswitch (handle) {\n\t\tcase 'top':\n\t\tcase 'right':\n\t\tcase 'top_right':\n\t\tcase 'any':\n\t\t\tresult.push({\n\t\t\t\tid: 'top_right',\n\t\t\t\thandle: 'top_right',\n\t\t\t\tx: maxX,\n\t\t\t\ty: minY,\n\t\t\t})\n\t}\n\n\t// bottom right corner\n\tswitch (handle) {\n\t\tcase 'bottom':\n\t\tcase 'right':\n\t\tcase 'bottom_right':\n\t\tcase 'any':\n\t\t\tresult.push({\n\t\t\t\tid: 'bottom_right',\n\t\t\t\thandle: 'bottom_right',\n\t\t\t\tx: maxX,\n\t\t\t\ty: maxY,\n\t\t\t})\n\t}\n\n\t// bottom left corner\n\tswitch (handle) {\n\t\tcase 'bottom':\n\t\tcase 'left':\n\t\tcase 'bottom_left':\n\t\tcase 'any':\n\t\t\tresult.push({\n\t\t\t\tid: 'bottom_left',\n\t\t\t\thandle: 'bottom_left',\n\t\t\t\tx: minX,\n\t\t\t\ty: maxY,\n\t\t\t})\n\t}\n\n\treturn result\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -2,6 +2,19 @@ class ShapeUtil {
2
2
  constructor(editor) {
3
3
  this.editor = editor;
4
4
  }
5
+ /** Configure this shape utils {@link ShapeUtil.options | `options`}. */
6
+ static configure(options) {
7
+ return class extends this {
8
+ // @ts-expect-error
9
+ options = { ...this.options, ...options };
10
+ };
11
+ }
12
+ /**
13
+ * Options for this shape util. If you're implementing a custom shape util, you can override
14
+ * this to provide customization options for your shape. If using an existing shape util, you
15
+ * can customizing this by calling {@link ShapeUtil.configure}.
16
+ */
17
+ options = {};
5
18
  /**
6
19
  * Props allow you to define the shape's properties in a way that the editor can understand.
7
20
  * This has two main uses:
@@ -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 { LegacyMigrations, MigrationSequence } from '@tldraw/store'\nimport {\n\tRecordProps,\n\tTLHandle,\n\tTLPropsMigrations,\n\tTLShape,\n\tTLShapeCrop,\n\tTLShapePartial,\n\tTLUnknownShape,\n} from '@tldraw/tlschema'\nimport { ReactElement } from 'react'\nimport { Box, SelectionHandle } from '../../primitives/Box'\nimport { Vec } from '../../primitives/Vec'\nimport { Geometry2d } from '../../primitives/geometry/Geometry2d'\nimport type { Editor } from '../Editor'\nimport { BoundsSnapGeometry } from '../managers/SnapManager/BoundsSnaps'\nimport { HandleSnapGeometry } from '../managers/SnapManager/HandleSnaps'\nimport { SvgExportContext } from '../types/SvgExportContext'\nimport { TLResizeHandle } from '../types/selection-types'\n\n/** @public */\nexport interface TLShapeUtilConstructor<\n\tT extends TLUnknownShape,\n\tU extends ShapeUtil<T> = ShapeUtil<T>,\n> {\n\tnew (editor: Editor): U\n\ttype: T['type']\n\tprops?: RecordProps<T>\n\tmigrations?: LegacyMigrations | TLPropsMigrations | MigrationSequence\n}\n\n/**\n * Options passed to {@link ShapeUtil.canBind}. A binding that could be made. At least one of\n * `fromShapeType` or `toShapeType` will belong to this shape util.\n *\n * @public\n */\nexport interface TLShapeUtilCanBindOpts<Shape extends TLUnknownShape = TLShape> {\n\t/** The type of shape referenced by the `fromId` of the binding. */\n\tfromShapeType: string\n\t/** The type of shape referenced by the `toId` of the binding. */\n\ttoShapeType: string\n\t/** The type of binding. */\n\tbindingType: string\n}\n\n/** @public */\nexport interface TLShapeUtilCanvasSvgDef {\n\tkey: string\n\tcomponent: React.ComponentType\n}\n\n/** @public */\nexport abstract class ShapeUtil<Shape extends TLUnknownShape = TLUnknownShape> {\n\tconstructor(public editor: Editor) {}\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 * Get the default props for a shape.\n\t *\n\t * @public\n\t */\n\tabstract getDefaultProps(): Shape['props']\n\n\t/**\n\t * Get the shape's geometry.\n\t *\n\t * @param shape - The shape.\n\t * @public\n\t */\n\tabstract getGeometry(shape: Shape): 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 JSX describing the shape's indicator (as an SVG element).\n\t *\n\t * @param shape - The shape.\n\t * @public\n\t */\n\tabstract indicator(shape: Shape): any\n\n\t/**\n\t * Whether the shape can be snapped to by another shape.\n\t *\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 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<Shape>): 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): 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 * 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 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 participates in stacking, aligning, and distributing.\n\t *\n\t * @public\n\t */\n\tcanBeLaidOut(_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 * 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 * 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.\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 the shape can receive children of a given type.\n\t *\n\t * @param shape - The shape type.\n\t * @param shapes - The shapes that are being dropped.\n\t * @public\n\t */\n\tcanDropShapes(_shape: Shape, _shapes: TLShape[]) {\n\t\treturn false\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// 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 over this one.\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[]): 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[]): 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[]): 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 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'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 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 * 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 * @returns A change to apply to the shape, or void.\n\t * @public\n\t */\n\tonDoubleClickEdge?(shape: Shape): 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 finishes being editing.\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}\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\tinitial?: T | undefined\n}\n"],
5
- "mappings": "AAsDO,MAAe,UAAyD;AAAA,EAC9E,YAAmB,QAAgB;AAAhB;AAAA,EAAiB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA6BpC,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOP,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOP,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAsCP,QAAQ,QAAwB;AAC/B,WAAO;AAAA,EACR;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,UAAU,QAAwB;AACjC,WAAO;AAAA,EACR;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,QAAQ,OAA+C;AACtD,WAAO;AAAA,EACR;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,QAAQ,QAAwB;AAC/B,WAAO;AAAA,EACR;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,UAAU,QAAwB;AACjC,WAAO;AAAA,EACR;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,kBAAkB,QAAwB;AACzC,WAAO;AAAA,EACR;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,QAAQ,QAAwB;AAC/B,WAAO;AAAA,EACR;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,aAAa,QAAwB;AACpC,WAAO;AAAA,EACR;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,8BAA8B,QAAwB;AACrD,WAAO;AAAA,EACR;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,kBAAkB,QAAwB;AACzC,WAAO;AAAA,EACR;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,iBAAiB,QAAwB;AACxC,WAAO;AAAA,EACR;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,sBAAsB,QAAwB;AAC7C,WAAO;AAAA,EACR;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,sBAAsB,QAAwB;AAC7C,WAAO;AAAA,EACR;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,oBAAoB,QAAwB;AAC3C,WAAO;AAAA,EACR;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA+CA,4BAA4B,QAAe,OAAwB;AAClE,WAAO;AAAA,EACR;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,cAAc,QAAe,SAAoB;AAChD,WAAO;AAAA,EACR;AAAA;AAAA,EA0BA,yBAAyB,OAA4B;AACpD,WAAO;AAAA,EACR;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,mBAA8C;AAC7C,WAAO,CAAC;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,sBAAsB,QAAmC;AACxD,WAAO,CAAC;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,sBAAsB,QAAmC;AACxD,WAAO,CAAC;AAAA,EACT;AAAA,EAEA,QAAQ,QAAmC;AAC1C,WAAO;AAAA,EACR;AA2PD;",
4
+ "sourcesContent": ["/* eslint-disable @typescript-eslint/no-unused-vars */\nimport { LegacyMigrations, MigrationSequence } from '@tldraw/store'\nimport {\n\tRecordProps,\n\tTLHandle,\n\tTLPropsMigrations,\n\tTLShape,\n\tTLShapeCrop,\n\tTLShapePartial,\n\tTLUnknownShape,\n} from '@tldraw/tlschema'\nimport { ReactElement } from 'react'\nimport { Box, SelectionHandle } from '../../primitives/Box'\nimport { Vec } from '../../primitives/Vec'\nimport { Geometry2d } from '../../primitives/geometry/Geometry2d'\nimport type { Editor } from '../Editor'\nimport { BoundsSnapGeometry } from '../managers/SnapManager/BoundsSnaps'\nimport { HandleSnapGeometry } from '../managers/SnapManager/HandleSnaps'\nimport { SvgExportContext } from '../types/SvgExportContext'\nimport { TLResizeHandle } from '../types/selection-types'\n\n/** @public */\nexport interface TLShapeUtilConstructor<\n\tT extends TLUnknownShape,\n\tU extends ShapeUtil<T> = ShapeUtil<T>,\n> {\n\tnew (editor: Editor): U\n\ttype: T['type']\n\tprops?: RecordProps<T>\n\tmigrations?: LegacyMigrations | TLPropsMigrations | MigrationSequence\n}\n\n/**\n * Options passed to {@link ShapeUtil.canBind}. A binding that could be made. At least one of\n * `fromShapeType` or `toShapeType` will belong to this shape util.\n *\n * @public\n */\nexport interface TLShapeUtilCanBindOpts<Shape extends TLUnknownShape = TLShape> {\n\t/** The type of shape referenced by the `fromId` of the binding. */\n\tfromShapeType: string\n\t/** The type of shape referenced by the `toId` of the binding. */\n\ttoShapeType: string\n\t/** The type of binding. */\n\tbindingType: string\n}\n\n/** @public */\nexport interface TLShapeUtilCanvasSvgDef {\n\tkey: string\n\tcomponent: React.ComponentType\n}\n\n/** @public */\nexport abstract class ShapeUtil<Shape extends TLUnknownShape = TLUnknownShape> {\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 * Get the default props for a shape.\n\t *\n\t * @public\n\t */\n\tabstract getDefaultProps(): Shape['props']\n\n\t/**\n\t * Get the shape's geometry.\n\t *\n\t * @param shape - The shape.\n\t * @public\n\t */\n\tabstract getGeometry(shape: Shape): 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 JSX describing the shape's indicator (as an SVG element).\n\t *\n\t * @param shape - The shape.\n\t * @public\n\t */\n\tabstract indicator(shape: Shape): any\n\n\t/**\n\t * Whether the shape can be snapped to by another shape.\n\t *\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 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<Shape>): 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): 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 * 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 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 participates in stacking, aligning, and distributing.\n\t *\n\t * @public\n\t */\n\tcanBeLaidOut(_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 * 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 * 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.\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 the shape can receive children of a given type.\n\t *\n\t * @param shape - The shape type.\n\t * @param shapes - The shapes that are being dropped.\n\t * @public\n\t */\n\tcanDropShapes(_shape: Shape, _shapes: TLShape[]) {\n\t\treturn false\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// 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 over this one.\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[]): 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[]): 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[]): 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 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'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 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 * 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 * @returns A change to apply to the shape, or void.\n\t * @public\n\t */\n\tonDoubleClickEdge?(shape: Shape): 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 finishes being editing.\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}\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\tinitial?: T | undefined\n}\n"],
5
+ "mappings": "AAsDO,MAAe,UAAyD;AAAA,EAa9E,YAAmB,QAAgB;AAAhB;AAAA,EAAiB;AAAA;AAAA,EAXpC,OAAO,UAEN,SACI;AAEJ,WAAO,cAAc,KAAK;AAAA;AAAA,MAEzB,UAAU,EAAE,GAAG,KAAK,SAAS,GAAG,QAAQ;AAAA,IACzC;AAAA,EACD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,UAAU,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA6BX,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOP,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOP,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAsCP,QAAQ,QAAwB;AAC/B,WAAO;AAAA,EACR;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,UAAU,QAAwB;AACjC,WAAO;AAAA,EACR;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,QAAQ,OAA+C;AACtD,WAAO;AAAA,EACR;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,QAAQ,QAAwB;AAC/B,WAAO;AAAA,EACR;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,UAAU,QAAwB;AACjC,WAAO;AAAA,EACR;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,kBAAkB,QAAwB;AACzC,WAAO;AAAA,EACR;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,QAAQ,QAAwB;AAC/B,WAAO;AAAA,EACR;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,aAAa,QAAwB;AACpC,WAAO;AAAA,EACR;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,8BAA8B,QAAwB;AACrD,WAAO;AAAA,EACR;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,kBAAkB,QAAwB;AACzC,WAAO;AAAA,EACR;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,iBAAiB,QAAwB;AACxC,WAAO;AAAA,EACR;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,sBAAsB,QAAwB;AAC7C,WAAO;AAAA,EACR;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,sBAAsB,QAAwB;AAC7C,WAAO;AAAA,EACR;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,oBAAoB,QAAwB;AAC3C,WAAO;AAAA,EACR;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA+CA,4BAA4B,QAAe,OAAwB;AAClE,WAAO;AAAA,EACR;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,cAAc,QAAe,SAAoB;AAChD,WAAO;AAAA,EACR;AAAA;AAAA,EA0BA,yBAAyB,OAA4B;AACpD,WAAO;AAAA,EACR;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,mBAA8C;AAC7C,WAAO,CAAC;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,sBAAsB,QAAmC;AACxD,WAAO,CAAC;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,sBAAsB,QAAmC;AACxD,WAAO,CAAC;AAAA,EACT;AAAA,EAEA,QAAQ,QAAmC;AAC1C,WAAO;AAAA,EACR;AA2PD;",
6
6
  "names": []
7
7
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/lib/exports/StyleEmbedder.ts"],
4
- "sourcesContent": ["import { assertExists, objectMapValues, uniqueId } from '@tldraw/utils'\nimport { FontEmbedder } from './FontEmbedder'\nimport {\n\telementStyle,\n\tgetComputedStyle,\n\tgetRenderedChildNodes,\n\tgetRenderedChildren,\n} from './domUtils'\nimport { resourceToDataUrl } from './fetchCache'\nimport { isPropertyInherited, parseCssValueUrls, shouldIncludeCssProperty } from './parseCss'\n\ntype Styles = { [K in string]?: string }\ntype ReadonlyStyles = { readonly [K in string]?: string }\nconst NO_STYLES = {} as const\n\ninterface ElementStyleInfo {\n\tself: Styles\n\tbefore: Styles | undefined\n\tafter: Styles | undefined\n}\n\nexport class StyleEmbedder {\n\tconstructor(private readonly root: Element) {}\n\tprivate readonly styles = new Map<Element, ElementStyleInfo>()\n\treadonly fonts = new FontEmbedder()\n\n\treadRootElementStyles(rootElement: Element) {\n\t\t// when reading a root, we always apply _all_ the styles, even if they match the defaults\n\t\tthis.readElementStyles(rootElement, {\n\t\t\tshouldRespectDefaults: false,\n\t\t\tshouldSkipInheritedParentStyles: false,\n\t\t})\n\n\t\tconst children = Array.from(getRenderedChildren(rootElement))\n\t\twhile (children.length) {\n\t\t\tconst child = children.pop()!\n\t\t\tchildren.push(...getRenderedChildren(child))\n\n\t\t\t// when reading children, we don't apply styles that match the defaults for that\n\t\t\t// element, or that would be inherited from the parent\n\t\t\tthis.readElementStyles(child, {\n\t\t\t\tshouldRespectDefaults: true,\n\t\t\t\tshouldSkipInheritedParentStyles: true,\n\t\t\t})\n\t\t}\n\t}\n\n\tprivate readElementStyles(\n\t\telement: Element,\n\t\t{ shouldRespectDefaults = true, shouldSkipInheritedParentStyles = true }\n\t) {\n\t\tconst defaultStyles = shouldRespectDefaults\n\t\t\t? getDefaultStylesForTagName(element.tagName.toLowerCase())\n\t\t\t: NO_STYLES\n\n\t\tconst parentStyles = shouldSkipInheritedParentStyles\n\t\t\t? this.styles.get(element.parentElement as Element)?.self ?? NO_STYLES\n\t\t\t: NO_STYLES\n\n\t\tconst info: ElementStyleInfo = {\n\t\t\tself: styleFromElement(element, { defaultStyles, parentStyles }),\n\t\t\tbefore: styleFromPseudoElement(element, '::before'),\n\t\t\tafter: styleFromPseudoElement(element, '::after'),\n\t\t}\n\t\tthis.styles.set(element, info)\n\t}\n\n\tfetchResources() {\n\t\tconst promises: Promise<void>[] = []\n\n\t\tfor (const info of this.styles.values()) {\n\t\t\tfor (const styles of objectMapValues(info)) {\n\t\t\t\tif (!styles) continue\n\t\t\t\tfor (const [property, value] of Object.entries(styles)) {\n\t\t\t\t\tif (!value) continue\n\t\t\t\t\tif (property === 'font-family') {\n\t\t\t\t\t\tthis.fonts.onFontFamilyValue(value)\n\t\t\t\t\t}\n\n\t\t\t\t\tconst urlMatches = parseCssValueUrls(value)\n\t\t\t\t\tif (urlMatches.length === 0) continue\n\n\t\t\t\t\tpromises.push(\n\t\t\t\t\t\t...urlMatches.map(async ({ url, original }) => {\n\t\t\t\t\t\t\tconst dataUrl = (await resourceToDataUrl(url)) ?? 'data:'\n\t\t\t\t\t\t\tstyles[property] = value.replace(original, `url(\"${dataUrl}\")`)\n\t\t\t\t\t\t})\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn Promise.all(promises)\n\t}\n\n\t// custom elements are tricky. if we serialize the dom as-is, the custom elements wont have\n\t// their shadow-dom contents serialized. after we've read all the styles, we need to unwrap the\n\t// contents of each custom elements shadow dom directly into the parent element itself.\n\tunwrapCustomElements() {\n\t\tconst visited = new Set<Node>()\n\n\t\tconst visit = (element: Element, clonedParent: Element | null) => {\n\t\t\tif (visited.has(element)) return\n\t\t\tvisited.add(element)\n\n\t\t\tconst shadowRoot = element.shadowRoot\n\n\t\t\tif (shadowRoot) {\n\t\t\t\tconst clonedCustomEl = document.createElement('div')\n\t\t\t\tthis.styles.set(clonedCustomEl, this.styles.get(element)!)\n\n\t\t\t\tclonedCustomEl.setAttribute('data-tl-custom-element', element.tagName)\n\t\t\t\t;(clonedParent ?? element.parentElement!).appendChild(clonedCustomEl)\n\n\t\t\t\tfor (const child of shadowRoot.childNodes) {\n\t\t\t\t\tif (child instanceof Element) {\n\t\t\t\t\t\tvisit(child, clonedCustomEl)\n\t\t\t\t\t} else {\n\t\t\t\t\t\tclonedCustomEl.appendChild(child.cloneNode(true))\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\telement.remove()\n\t\t\t} else if (clonedParent) {\n\t\t\t\tif (element.tagName.toLowerCase() === 'style') {\n\t\t\t\t\t// we don't clone style tags at that would break the style scoping. instead we\n\t\t\t\t\t// rely on the computed styles we've already read\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tconst clonedEl = element.cloneNode(false) as Element\n\t\t\t\tthis.styles.set(clonedEl, this.styles.get(element)!)\n\n\t\t\t\tclonedParent.appendChild(clonedEl)\n\n\t\t\t\tfor (const child of getRenderedChildNodes(element)) {\n\t\t\t\t\tif (child instanceof Element) {\n\t\t\t\t\t\tvisit(child, clonedEl)\n\t\t\t\t\t} else {\n\t\t\t\t\t\tclonedEl.appendChild(child.cloneNode(true))\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tfor (const element of this.styles.keys()) {\n\t\t\tvisit(element, null)\n\t\t}\n\t}\n\n\tembedStyles(): string {\n\t\tlet css = ''\n\n\t\tfor (const [element, info] of this.styles) {\n\t\t\tif (info.after || info.before) {\n\t\t\t\tconst className = `pseudo-${uniqueId()}`\n\t\t\t\telement.classList.add(className)\n\n\t\t\t\tif (info.before) {\n\t\t\t\t\tcss += `.${className}::before {${formatCss(info.before)}}\\n`\n\t\t\t\t}\n\t\t\t\tif (info.after) {\n\t\t\t\t\tcss += `.${className}::after {${formatCss(info.after)}}\\n`\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst style = elementStyle(element)\n\t\t\tfor (const [property, value] of Object.entries(info.self)) {\n\t\t\t\tif (!value) continue\n\t\t\t\tstyle.setProperty(property, value)\n\t\t\t}\n\n\t\t\t// in HTML, font-kerning: auto is equivalent to font-kerning: normal. But in SVG, it's\n\t\t\t// none. We set it to normal here to match the HTML behavior, as otherwise this can\n\t\t\t// cause rendering differences.\n\t\t\tif (style.fontKerning === 'auto') {\n\t\t\t\tstyle.fontKerning = 'normal'\n\t\t\t}\n\t\t}\n\n\t\treturn css\n\t}\n\n\tasync getFontFaceCss() {\n\t\treturn await this.fonts.createCss()\n\t}\n\n\tdispose() {\n\t\tdestroyDefaultStyleFrame()\n\t}\n}\n\ninterface ReadStyleOpts {\n\tdefaultStyles: ReadonlyStyles\n\tparentStyles: ReadonlyStyles\n}\n\nfunction styleFromElement(element: Element, { defaultStyles, parentStyles }: ReadStyleOpts) {\n\t// `computedStyleMap` produces a more accurate representation of the styles, but it's not\n\t// supported in firefox at the time of writing. So we fall back to `getComputedStyle` if it's\n\t// not available.\n\tif (element.computedStyleMap) {\n\t\treturn styleFromComputedStyleMap(element.computedStyleMap(), { defaultStyles, parentStyles })\n\t}\n\treturn styleFromComputedStyle(getComputedStyle(element), { defaultStyles, parentStyles })\n}\n\nfunction styleFromPseudoElement(element: Element, pseudo: string) {\n\t// the equivalent of `computedStyleMap` for pseudo-elements isn't even fully specced out yet, so\n\t// for those we have to use `getComputedStyle` in all browsers.\n\tconst style = getComputedStyle(element, pseudo)\n\n\tconst content = style.getPropertyValue('content')\n\tif (content === '' || content === 'none') {\n\t\treturn undefined\n\t}\n\n\treturn styleFromComputedStyle(style, { defaultStyles: NO_STYLES, parentStyles: NO_STYLES })\n}\n\nfunction styleFromComputedStyleMap(\n\tstyle: StylePropertyMapReadOnly,\n\t{ defaultStyles, parentStyles }: ReadStyleOpts\n) {\n\tconst styles: Record<string, string> = {}\n\tfor (const property of style.keys()) {\n\t\tif (!shouldIncludeCssProperty(property)) continue\n\n\t\tconst value = style.get(property)!.toString()\n\n\t\tif (defaultStyles[property] === value) continue\n\t\tif (parentStyles[property] === value && isPropertyInherited(property)) continue\n\n\t\tstyles[property] = value\n\t}\n\n\treturn styles\n}\n\nfunction styleFromComputedStyle(\n\tstyle: CSSStyleDeclaration,\n\t{ defaultStyles, parentStyles }: ReadStyleOpts\n) {\n\tconst styles: Record<string, string> = {}\n\tfor (const property of style) {\n\t\tif (!shouldIncludeCssProperty(property)) continue\n\n\t\tconst value = style.getPropertyValue(property)\n\n\t\tif (defaultStyles[property] === value) continue\n\t\tif (parentStyles[property] === value && isPropertyInherited(property)) continue\n\n\t\tstyles[property] = value\n\t}\n\treturn styles\n}\n\nfunction formatCss(style: ReadonlyStyles) {\n\tlet cssText = ''\n\tfor (const [property, value] of Object.entries(style)) {\n\t\tcssText += `${property}: ${value};`\n\t}\n\treturn cssText\n}\n\n// when we're figuring out the default values for a tag, we need read them from a separate document\n// so they're not affected by the current document's styles\nlet defaultStyleFrame:\n\t| { iframe: HTMLIFrameElement; foreignObject: SVGForeignObjectElement; document: Document }\n\t| undefined\nconst defaultStylesByTagName: Record<string, ReadonlyStyles> = {}\nfunction getDefaultStyleFrame() {\n\tif (!defaultStyleFrame) {\n\t\tconst frame = document.createElement('iframe')\n\t\tframe.style.display = 'none'\n\t\tdocument.body.appendChild(frame)\n\t\tconst frameDocument = assertExists(frame.contentDocument, 'frame must have a document')\n\t\tconst svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg')\n\t\tconst foreignObject = document.createElementNS('http://www.w3.org/2000/svg', 'foreignObject')\n\t\tsvg.appendChild(foreignObject)\n\t\tframeDocument.body.appendChild(svg)\n\t\tdefaultStyleFrame = { iframe: frame, foreignObject, document: frameDocument }\n\t}\n\treturn defaultStyleFrame\n}\n\nfunction destroyDefaultStyleFrame() {\n\tif (defaultStyleFrame) {\n\t\tdocument.body.removeChild(defaultStyleFrame.iframe)\n\t\tdefaultStyleFrame = undefined\n\t}\n}\n\nconst defaultStyleReadOptions: ReadStyleOpts = { defaultStyles: NO_STYLES, parentStyles: NO_STYLES }\nfunction getDefaultStylesForTagName(tagName: string) {\n\tlet existing = defaultStylesByTagName[tagName]\n\tif (!existing) {\n\t\tconst { foreignObject, document } = getDefaultStyleFrame()\n\t\tconst element = document.createElement(tagName)\n\t\tforeignObject.appendChild(element)\n\t\texisting = element.computedStyleMap\n\t\t\t? styleFromComputedStyleMap(element.computedStyleMap(), defaultStyleReadOptions)\n\t\t\t: styleFromComputedStyle(getComputedStyle(element), defaultStyleReadOptions)\n\t\tforeignObject.removeChild(element)\n\t\tdefaultStylesByTagName[tagName] = existing\n\t}\n\treturn existing\n}\n"],
5
- "mappings": "AAAA,SAAS,cAAc,iBAAiB,gBAAgB;AACxD,SAAS,oBAAoB;AAC7B;AAAA,EACC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACM;AACP,SAAS,yBAAyB;AAClC,SAAS,qBAAqB,mBAAmB,gCAAgC;AAIjF,MAAM,YAAY,CAAC;AAQZ,MAAM,cAAc;AAAA,EAC1B,YAA6B,MAAe;AAAf;AAAA,EAAgB;AAAA,EAC5B,SAAS,oBAAI,IAA+B;AAAA,EACpD,QAAQ,IAAI,aAAa;AAAA,EAElC,sBAAsB,aAAsB;AAE3C,SAAK,kBAAkB,aAAa;AAAA,MACnC,uBAAuB;AAAA,MACvB,iCAAiC;AAAA,IAClC,CAAC;AAED,UAAM,WAAW,MAAM,KAAK,oBAAoB,WAAW,CAAC;AAC5D,WAAO,SAAS,QAAQ;AACvB,YAAM,QAAQ,SAAS,IAAI;AAC3B,eAAS,KAAK,GAAG,oBAAoB,KAAK,CAAC;AAI3C,WAAK,kBAAkB,OAAO;AAAA,QAC7B,uBAAuB;AAAA,QACvB,iCAAiC;AAAA,MAClC,CAAC;AAAA,IACF;AAAA,EACD;AAAA,EAEQ,kBACP,SACA,EAAE,wBAAwB,MAAM,kCAAkC,KAAK,GACtE;AACD,UAAM,gBAAgB,wBACnB,2BAA2B,QAAQ,QAAQ,YAAY,CAAC,IACxD;AAEH,UAAM,eAAe,kCAClB,KAAK,OAAO,IAAI,QAAQ,aAAwB,GAAG,QAAQ,YAC3D;AAEH,UAAM,OAAyB;AAAA,MAC9B,MAAM,iBAAiB,SAAS,EAAE,eAAe,aAAa,CAAC;AAAA,MAC/D,QAAQ,uBAAuB,SAAS,UAAU;AAAA,MAClD,OAAO,uBAAuB,SAAS,SAAS;AAAA,IACjD;AACA,SAAK,OAAO,IAAI,SAAS,IAAI;AAAA,EAC9B;AAAA,EAEA,iBAAiB;AAChB,UAAM,WAA4B,CAAC;AAEnC,eAAW,QAAQ,KAAK,OAAO,OAAO,GAAG;AACxC,iBAAW,UAAU,gBAAgB,IAAI,GAAG;AAC3C,YAAI,CAAC,OAAQ;AACb,mBAAW,CAAC,UAAU,KAAK,KAAK,OAAO,QAAQ,MAAM,GAAG;AACvD,cAAI,CAAC,MAAO;AACZ,cAAI,aAAa,eAAe;AAC/B,iBAAK,MAAM,kBAAkB,KAAK;AAAA,UACnC;AAEA,gBAAM,aAAa,kBAAkB,KAAK;AAC1C,cAAI,WAAW,WAAW,EAAG;AAE7B,mBAAS;AAAA,YACR,GAAG,WAAW,IAAI,OAAO,EAAE,KAAK,SAAS,MAAM;AAC9C,oBAAM,UAAW,MAAM,kBAAkB,GAAG,KAAM;AAClD,qBAAO,QAAQ,IAAI,MAAM,QAAQ,UAAU,QAAQ,OAAO,IAAI;AAAA,YAC/D,CAAC;AAAA,UACF;AAAA,QACD;AAAA,MACD;AAAA,IACD;AAEA,WAAO,QAAQ,IAAI,QAAQ;AAAA,EAC5B;AAAA;AAAA;AAAA;AAAA,EAKA,uBAAuB;AACtB,UAAM,UAAU,oBAAI,IAAU;AAE9B,UAAM,QAAQ,CAAC,SAAkB,iBAAiC;AACjE,UAAI,QAAQ,IAAI,OAAO,EAAG;AAC1B,cAAQ,IAAI,OAAO;AAEnB,YAAM,aAAa,QAAQ;AAE3B,UAAI,YAAY;AACf,cAAM,iBAAiB,SAAS,cAAc,KAAK;AACnD,aAAK,OAAO,IAAI,gBAAgB,KAAK,OAAO,IAAI,OAAO,CAAE;AAEzD,uBAAe,aAAa,0BAA0B,QAAQ,OAAO;AACpE,SAAC,gBAAgB,QAAQ,eAAgB,YAAY,cAAc;AAEpE,mBAAW,SAAS,WAAW,YAAY;AAC1C,cAAI,iBAAiB,SAAS;AAC7B,kBAAM,OAAO,cAAc;AAAA,UAC5B,OAAO;AACN,2BAAe,YAAY,MAAM,UAAU,IAAI,CAAC;AAAA,UACjD;AAAA,QACD;AAEA,gBAAQ,OAAO;AAAA,MAChB,WAAW,cAAc;AACxB,YAAI,QAAQ,QAAQ,YAAY,MAAM,SAAS;AAG9C;AAAA,QACD;AAEA,cAAM,WAAW,QAAQ,UAAU,KAAK;AACxC,aAAK,OAAO,IAAI,UAAU,KAAK,OAAO,IAAI,OAAO,CAAE;AAEnD,qBAAa,YAAY,QAAQ;AAEjC,mBAAW,SAAS,sBAAsB,OAAO,GAAG;AACnD,cAAI,iBAAiB,SAAS;AAC7B,kBAAM,OAAO,QAAQ;AAAA,UACtB,OAAO;AACN,qBAAS,YAAY,MAAM,UAAU,IAAI,CAAC;AAAA,UAC3C;AAAA,QACD;AAAA,MACD;AAAA,IACD;AAEA,eAAW,WAAW,KAAK,OAAO,KAAK,GAAG;AACzC,YAAM,SAAS,IAAI;AAAA,IACpB;AAAA,EACD;AAAA,EAEA,cAAsB;AACrB,QAAI,MAAM;AAEV,eAAW,CAAC,SAAS,IAAI,KAAK,KAAK,QAAQ;AAC1C,UAAI,KAAK,SAAS,KAAK,QAAQ;AAC9B,cAAM,YAAY,UAAU,SAAS,CAAC;AACtC,gBAAQ,UAAU,IAAI,SAAS;AAE/B,YAAI,KAAK,QAAQ;AAChB,iBAAO,IAAI,SAAS,aAAa,UAAU,KAAK,MAAM,CAAC;AAAA;AAAA,QACxD;AACA,YAAI,KAAK,OAAO;AACf,iBAAO,IAAI,SAAS,YAAY,UAAU,KAAK,KAAK,CAAC;AAAA;AAAA,QACtD;AAAA,MACD;AAEA,YAAM,QAAQ,aAAa,OAAO;AAClC,iBAAW,CAAC,UAAU,KAAK,KAAK,OAAO,QAAQ,KAAK,IAAI,GAAG;AAC1D,YAAI,CAAC,MAAO;AACZ,cAAM,YAAY,UAAU,KAAK;AAAA,MAClC;AAKA,UAAI,MAAM,gBAAgB,QAAQ;AACjC,cAAM,cAAc;AAAA,MACrB;AAAA,IACD;AAEA,WAAO;AAAA,EACR;AAAA,EAEA,MAAM,iBAAiB;AACtB,WAAO,MAAM,KAAK,MAAM,UAAU;AAAA,EACnC;AAAA,EAEA,UAAU;AACT,6BAAyB;AAAA,EAC1B;AACD;AAOA,SAAS,iBAAiB,SAAkB,EAAE,eAAe,aAAa,GAAkB;AAI3F,MAAI,QAAQ,kBAAkB;AAC7B,WAAO,0BAA0B,QAAQ,iBAAiB,GAAG,EAAE,eAAe,aAAa,CAAC;AAAA,EAC7F;AACA,SAAO,uBAAuB,iBAAiB,OAAO,GAAG,EAAE,eAAe,aAAa,CAAC;AACzF;AAEA,SAAS,uBAAuB,SAAkB,QAAgB;AAGjE,QAAM,QAAQ,iBAAiB,SAAS,MAAM;AAE9C,QAAM,UAAU,MAAM,iBAAiB,SAAS;AAChD,MAAI,YAAY,MAAM,YAAY,QAAQ;AACzC,WAAO;AAAA,EACR;AAEA,SAAO,uBAAuB,OAAO,EAAE,eAAe,WAAW,cAAc,UAAU,CAAC;AAC3F;AAEA,SAAS,0BACR,OACA,EAAE,eAAe,aAAa,GAC7B;AACD,QAAM,SAAiC,CAAC;AACxC,aAAW,YAAY,MAAM,KAAK,GAAG;AACpC,QAAI,CAAC,yBAAyB,QAAQ,EAAG;AAEzC,UAAM,QAAQ,MAAM,IAAI,QAAQ,EAAG,SAAS;AAE5C,QAAI,cAAc,QAAQ,MAAM,MAAO;AACvC,QAAI,aAAa,QAAQ,MAAM,SAAS,oBAAoB,QAAQ,EAAG;AAEvE,WAAO,QAAQ,IAAI;AAAA,EACpB;AAEA,SAAO;AACR;AAEA,SAAS,uBACR,OACA,EAAE,eAAe,aAAa,GAC7B;AACD,QAAM,SAAiC,CAAC;AACxC,aAAW,YAAY,OAAO;AAC7B,QAAI,CAAC,yBAAyB,QAAQ,EAAG;AAEzC,UAAM,QAAQ,MAAM,iBAAiB,QAAQ;AAE7C,QAAI,cAAc,QAAQ,MAAM,MAAO;AACvC,QAAI,aAAa,QAAQ,MAAM,SAAS,oBAAoB,QAAQ,EAAG;AAEvE,WAAO,QAAQ,IAAI;AAAA,EACpB;AACA,SAAO;AACR;AAEA,SAAS,UAAU,OAAuB;AACzC,MAAI,UAAU;AACd,aAAW,CAAC,UAAU,KAAK,KAAK,OAAO,QAAQ,KAAK,GAAG;AACtD,eAAW,GAAG,QAAQ,KAAK,KAAK;AAAA,EACjC;AACA,SAAO;AACR;AAIA,IAAI;AAGJ,MAAM,yBAAyD,CAAC;AAChE,SAAS,uBAAuB;AAC/B,MAAI,CAAC,mBAAmB;AACvB,UAAM,QAAQ,SAAS,cAAc,QAAQ;AAC7C,UAAM,MAAM,UAAU;AACtB,aAAS,KAAK,YAAY,KAAK;AAC/B,UAAM,gBAAgB,aAAa,MAAM,iBAAiB,4BAA4B;AACtF,UAAM,MAAM,SAAS,gBAAgB,8BAA8B,KAAK;AACxE,UAAM,gBAAgB,SAAS,gBAAgB,8BAA8B,eAAe;AAC5F,QAAI,YAAY,aAAa;AAC7B,kBAAc,KAAK,YAAY,GAAG;AAClC,wBAAoB,EAAE,QAAQ,OAAO,eAAe,UAAU,cAAc;AAAA,EAC7E;AACA,SAAO;AACR;AAEA,SAAS,2BAA2B;AACnC,MAAI,mBAAmB;AACtB,aAAS,KAAK,YAAY,kBAAkB,MAAM;AAClD,wBAAoB;AAAA,EACrB;AACD;AAEA,MAAM,0BAAyC,EAAE,eAAe,WAAW,cAAc,UAAU;AACnG,SAAS,2BAA2B,SAAiB;AACpD,MAAI,WAAW,uBAAuB,OAAO;AAC7C,MAAI,CAAC,UAAU;AACd,UAAM,EAAE,eAAe,UAAAA,UAAS,IAAI,qBAAqB;AACzD,UAAM,UAAUA,UAAS,cAAc,OAAO;AAC9C,kBAAc,YAAY,OAAO;AACjC,eAAW,QAAQ,mBAChB,0BAA0B,QAAQ,iBAAiB,GAAG,uBAAuB,IAC7E,uBAAuB,iBAAiB,OAAO,GAAG,uBAAuB;AAC5E,kBAAc,YAAY,OAAO;AACjC,2BAAuB,OAAO,IAAI;AAAA,EACnC;AACA,SAAO;AACR;",
4
+ "sourcesContent": ["import { assertExists, objectMapValues, uniqueId } from '@tldraw/utils'\nimport { FontEmbedder } from './FontEmbedder'\nimport {\n\telementStyle,\n\tgetComputedStyle,\n\tgetRenderedChildNodes,\n\tgetRenderedChildren,\n} from './domUtils'\nimport { resourceToDataUrl } from './fetchCache'\nimport { isPropertyInherited, parseCssValueUrls, shouldIncludeCssProperty } from './parseCss'\n\ntype Styles = { [K in string]?: string }\ntype ReadonlyStyles = { readonly [K in string]?: string }\nconst NO_STYLES = {} as const\n\ninterface ElementStyleInfo {\n\tself: Styles\n\tbefore: Styles | undefined\n\tafter: Styles | undefined\n}\n\nexport class StyleEmbedder {\n\tconstructor(private readonly root: Element) {}\n\tprivate readonly styles = new Map<Element, ElementStyleInfo>()\n\treadonly fonts = new FontEmbedder()\n\n\treadRootElementStyles(rootElement: Element) {\n\t\t// when reading a root, we always apply _all_ the styles, even if they match the defaults\n\t\tthis.readElementStyles(rootElement, {\n\t\t\tshouldRespectDefaults: false,\n\t\t\tshouldSkipInheritedParentStyles: false,\n\t\t})\n\n\t\tconst children = Array.from(getRenderedChildren(rootElement))\n\t\twhile (children.length) {\n\t\t\tconst child = children.pop()!\n\t\t\tchildren.push(...getRenderedChildren(child))\n\n\t\t\t// when reading children, we don't apply styles that match the defaults for that\n\t\t\t// element, or that would be inherited from the parent\n\t\t\tthis.readElementStyles(child, {\n\t\t\t\tshouldRespectDefaults: true,\n\t\t\t\tshouldSkipInheritedParentStyles: true,\n\t\t\t})\n\t\t}\n\t}\n\n\tprivate readElementStyles(\n\t\telement: Element,\n\t\t{ shouldRespectDefaults = true, shouldSkipInheritedParentStyles = true }\n\t) {\n\t\tconst defaultStyles = shouldRespectDefaults\n\t\t\t? getDefaultStylesForTagName(element.tagName.toLowerCase())\n\t\t\t: NO_STYLES\n\n\t\tconst parentStyles = shouldSkipInheritedParentStyles\n\t\t\t? (this.styles.get(element.parentElement as Element)?.self ?? NO_STYLES)\n\t\t\t: NO_STYLES\n\n\t\tconst info: ElementStyleInfo = {\n\t\t\tself: styleFromElement(element, { defaultStyles, parentStyles }),\n\t\t\tbefore: styleFromPseudoElement(element, '::before'),\n\t\t\tafter: styleFromPseudoElement(element, '::after'),\n\t\t}\n\t\tthis.styles.set(element, info)\n\t}\n\n\tfetchResources() {\n\t\tconst promises: Promise<void>[] = []\n\n\t\tfor (const info of this.styles.values()) {\n\t\t\tfor (const styles of objectMapValues(info)) {\n\t\t\t\tif (!styles) continue\n\t\t\t\tfor (const [property, value] of Object.entries(styles)) {\n\t\t\t\t\tif (!value) continue\n\t\t\t\t\tif (property === 'font-family') {\n\t\t\t\t\t\tthis.fonts.onFontFamilyValue(value)\n\t\t\t\t\t}\n\n\t\t\t\t\tconst urlMatches = parseCssValueUrls(value)\n\t\t\t\t\tif (urlMatches.length === 0) continue\n\n\t\t\t\t\tpromises.push(\n\t\t\t\t\t\t...urlMatches.map(async ({ url, original }) => {\n\t\t\t\t\t\t\tconst dataUrl = (await resourceToDataUrl(url)) ?? 'data:'\n\t\t\t\t\t\t\tstyles[property] = value.replace(original, `url(\"${dataUrl}\")`)\n\t\t\t\t\t\t})\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn Promise.all(promises)\n\t}\n\n\t// custom elements are tricky. if we serialize the dom as-is, the custom elements wont have\n\t// their shadow-dom contents serialized. after we've read all the styles, we need to unwrap the\n\t// contents of each custom elements shadow dom directly into the parent element itself.\n\tunwrapCustomElements() {\n\t\tconst visited = new Set<Node>()\n\n\t\tconst visit = (element: Element, clonedParent: Element | null) => {\n\t\t\tif (visited.has(element)) return\n\t\t\tvisited.add(element)\n\n\t\t\tconst shadowRoot = element.shadowRoot\n\n\t\t\tif (shadowRoot) {\n\t\t\t\tconst clonedCustomEl = document.createElement('div')\n\t\t\t\tthis.styles.set(clonedCustomEl, this.styles.get(element)!)\n\n\t\t\t\tclonedCustomEl.setAttribute('data-tl-custom-element', element.tagName)\n\t\t\t\t;(clonedParent ?? element.parentElement!).appendChild(clonedCustomEl)\n\n\t\t\t\tfor (const child of shadowRoot.childNodes) {\n\t\t\t\t\tif (child instanceof Element) {\n\t\t\t\t\t\tvisit(child, clonedCustomEl)\n\t\t\t\t\t} else {\n\t\t\t\t\t\tclonedCustomEl.appendChild(child.cloneNode(true))\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\telement.remove()\n\t\t\t} else if (clonedParent) {\n\t\t\t\tif (element.tagName.toLowerCase() === 'style') {\n\t\t\t\t\t// we don't clone style tags at that would break the style scoping. instead we\n\t\t\t\t\t// rely on the computed styles we've already read\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tconst clonedEl = element.cloneNode(false) as Element\n\t\t\t\tthis.styles.set(clonedEl, this.styles.get(element)!)\n\n\t\t\t\tclonedParent.appendChild(clonedEl)\n\n\t\t\t\tfor (const child of getRenderedChildNodes(element)) {\n\t\t\t\t\tif (child instanceof Element) {\n\t\t\t\t\t\tvisit(child, clonedEl)\n\t\t\t\t\t} else {\n\t\t\t\t\t\tclonedEl.appendChild(child.cloneNode(true))\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tfor (const element of this.styles.keys()) {\n\t\t\tvisit(element, null)\n\t\t}\n\t}\n\n\tembedStyles(): string {\n\t\tlet css = ''\n\n\t\tfor (const [element, info] of this.styles) {\n\t\t\tif (info.after || info.before) {\n\t\t\t\tconst className = `pseudo-${uniqueId()}`\n\t\t\t\telement.classList.add(className)\n\n\t\t\t\tif (info.before) {\n\t\t\t\t\tcss += `.${className}::before {${formatCss(info.before)}}\\n`\n\t\t\t\t}\n\t\t\t\tif (info.after) {\n\t\t\t\t\tcss += `.${className}::after {${formatCss(info.after)}}\\n`\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst style = elementStyle(element)\n\t\t\tfor (const [property, value] of Object.entries(info.self)) {\n\t\t\t\tif (!value) continue\n\t\t\t\tstyle.setProperty(property, value)\n\t\t\t}\n\n\t\t\t// in HTML, font-kerning: auto is equivalent to font-kerning: normal. But in SVG, it's\n\t\t\t// none. We set it to normal here to match the HTML behavior, as otherwise this can\n\t\t\t// cause rendering differences.\n\t\t\tif (style.fontKerning === 'auto') {\n\t\t\t\tstyle.fontKerning = 'normal'\n\t\t\t}\n\t\t}\n\n\t\treturn css\n\t}\n\n\tasync getFontFaceCss() {\n\t\treturn await this.fonts.createCss()\n\t}\n\n\tdispose() {\n\t\tdestroyDefaultStyleFrame()\n\t}\n}\n\ninterface ReadStyleOpts {\n\tdefaultStyles: ReadonlyStyles\n\tparentStyles: ReadonlyStyles\n}\n\nfunction styleFromElement(element: Element, { defaultStyles, parentStyles }: ReadStyleOpts) {\n\t// `computedStyleMap` produces a more accurate representation of the styles, but it's not\n\t// supported in firefox at the time of writing. So we fall back to `getComputedStyle` if it's\n\t// not available.\n\tif (element.computedStyleMap) {\n\t\treturn styleFromComputedStyleMap(element.computedStyleMap(), { defaultStyles, parentStyles })\n\t}\n\treturn styleFromComputedStyle(getComputedStyle(element), { defaultStyles, parentStyles })\n}\n\nfunction styleFromPseudoElement(element: Element, pseudo: string) {\n\t// the equivalent of `computedStyleMap` for pseudo-elements isn't even fully specced out yet, so\n\t// for those we have to use `getComputedStyle` in all browsers.\n\tconst style = getComputedStyle(element, pseudo)\n\n\tconst content = style.getPropertyValue('content')\n\tif (content === '' || content === 'none') {\n\t\treturn undefined\n\t}\n\n\treturn styleFromComputedStyle(style, { defaultStyles: NO_STYLES, parentStyles: NO_STYLES })\n}\n\nfunction styleFromComputedStyleMap(\n\tstyle: StylePropertyMapReadOnly,\n\t{ defaultStyles, parentStyles }: ReadStyleOpts\n) {\n\tconst styles: Record<string, string> = {}\n\tfor (const property of style.keys()) {\n\t\tif (!shouldIncludeCssProperty(property)) continue\n\n\t\tconst value = style.get(property)!.toString()\n\n\t\tif (defaultStyles[property] === value) continue\n\t\tif (parentStyles[property] === value && isPropertyInherited(property)) continue\n\n\t\tstyles[property] = value\n\t}\n\n\treturn styles\n}\n\nfunction styleFromComputedStyle(\n\tstyle: CSSStyleDeclaration,\n\t{ defaultStyles, parentStyles }: ReadStyleOpts\n) {\n\tconst styles: Record<string, string> = {}\n\tfor (const property of style) {\n\t\tif (!shouldIncludeCssProperty(property)) continue\n\n\t\tconst value = style.getPropertyValue(property)\n\n\t\tif (defaultStyles[property] === value) continue\n\t\tif (parentStyles[property] === value && isPropertyInherited(property)) continue\n\n\t\tstyles[property] = value\n\t}\n\treturn styles\n}\n\nfunction formatCss(style: ReadonlyStyles) {\n\tlet cssText = ''\n\tfor (const [property, value] of Object.entries(style)) {\n\t\tcssText += `${property}: ${value};`\n\t}\n\treturn cssText\n}\n\n// when we're figuring out the default values for a tag, we need read them from a separate document\n// so they're not affected by the current document's styles\nlet defaultStyleFrame:\n\t| { iframe: HTMLIFrameElement; foreignObject: SVGForeignObjectElement; document: Document }\n\t| undefined\nconst defaultStylesByTagName: Record<string, ReadonlyStyles> = {}\nfunction getDefaultStyleFrame() {\n\tif (!defaultStyleFrame) {\n\t\tconst frame = document.createElement('iframe')\n\t\tframe.style.display = 'none'\n\t\tdocument.body.appendChild(frame)\n\t\tconst frameDocument = assertExists(frame.contentDocument, 'frame must have a document')\n\t\tconst svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg')\n\t\tconst foreignObject = document.createElementNS('http://www.w3.org/2000/svg', 'foreignObject')\n\t\tsvg.appendChild(foreignObject)\n\t\tframeDocument.body.appendChild(svg)\n\t\tdefaultStyleFrame = { iframe: frame, foreignObject, document: frameDocument }\n\t}\n\treturn defaultStyleFrame\n}\n\nfunction destroyDefaultStyleFrame() {\n\tif (defaultStyleFrame) {\n\t\tdocument.body.removeChild(defaultStyleFrame.iframe)\n\t\tdefaultStyleFrame = undefined\n\t}\n}\n\nconst defaultStyleReadOptions: ReadStyleOpts = { defaultStyles: NO_STYLES, parentStyles: NO_STYLES }\nfunction getDefaultStylesForTagName(tagName: string) {\n\tlet existing = defaultStylesByTagName[tagName]\n\tif (!existing) {\n\t\tconst { foreignObject, document } = getDefaultStyleFrame()\n\t\tconst element = document.createElement(tagName)\n\t\tforeignObject.appendChild(element)\n\t\texisting = element.computedStyleMap\n\t\t\t? styleFromComputedStyleMap(element.computedStyleMap(), defaultStyleReadOptions)\n\t\t\t: styleFromComputedStyle(getComputedStyle(element), defaultStyleReadOptions)\n\t\tforeignObject.removeChild(element)\n\t\tdefaultStylesByTagName[tagName] = existing\n\t}\n\treturn existing\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["document"]
7
7
  }
@@ -1,6 +1,6 @@
1
1
  import { useValue } from "@tldraw/state-react";
2
2
  import { useEffect } from "react";
3
- import { preventDefault, stopEventPropagation } from "../utils/dom.mjs";
3
+ import { activeElementShouldCaptureKeys, preventDefault, stopEventPropagation } from "../utils/dom.mjs";
4
4
  import { isAccelKey } from "../utils/keyboard.mjs";
5
5
  import { useContainer } from "./useContainer.mjs";
6
6
  import { useEditor } from "./useEditor.mjs";
@@ -178,10 +178,8 @@ function useDocumentEvents() {
178
178
  };
179
179
  }, [editor, container, isAppFocused]);
180
180
  }
181
- const INPUTS = ["input", "select", "button", "textarea"];
182
181
  function areShortcutsDisabled(editor) {
183
- const { activeElement } = document;
184
- return editor.menus.hasOpenMenus() || activeElement && (activeElement.getAttribute("contenteditable") || INPUTS.indexOf(activeElement.tagName.toLowerCase()) > -1);
182
+ return editor.menus.hasOpenMenus() || activeElementShouldCaptureKeys();
185
183
  }
186
184
  export {
187
185
  useDocumentEvents