tldraw 3.16.0-canary.66da2a5c5813 → 3.16.0-canary.6c77a180e58d

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist-cjs/index.js CHANGED
@@ -575,7 +575,7 @@ var import_buildFromV1Document = require("./lib/utils/tldr/buildFromV1Document")
575
575
  var import_file = require("./lib/utils/tldr/file");
576
576
  (0, import_editor.registerTldrawLibraryVersion)(
577
577
  "tldraw",
578
- "3.16.0-canary.66da2a5c5813",
578
+ "3.16.0-canary.6c77a180e58d",
579
579
  "cjs"
580
580
  );
581
581
  //# sourceMappingURL=index.js.map
@@ -170,6 +170,12 @@ function getArrowLabelRange(editor, shape, info) {
170
170
  return { start: startRelative, end: endRelative, dbg };
171
171
  }
172
172
  function getArrowLabelPosition(editor, shape) {
173
+ const isEditing = editor.getEditingShapeId() === shape.id;
174
+ if (!isEditing && (0, import_richText.isEmptyRichText)(shape.props.richText)) {
175
+ const bodyGeom2 = getArrowBodyGeometry(editor, shape);
176
+ const labelCenter2 = bodyGeom2.interpolateAlongEdge(0.5);
177
+ return { box: import_editor.Box.FromCenter(labelCenter2, new import_editor.Vec(0, 0)), debugGeom: [] };
178
+ }
173
179
  const debugGeom = [];
174
180
  const info = (0, import_shared.getArrowInfo)(editor, shape);
175
181
  const arrowheadInfo = {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/lib/shapes/arrow/arrowLabel.ts"],
4
- "sourcesContent": ["import {\n\tArc2d,\n\tBox,\n\tCircle2d,\n\tEdge2d,\n\tEditor,\n\tGeometry2d,\n\tGroup2d,\n\tPolygon2d,\n\tPolyline2d,\n\tTLArrowShape,\n\tTLShape,\n\tVec,\n\tVecLike,\n\tclamp,\n\tcreateComputedCache,\n\texhaustiveSwitchError,\n\tgetChangedKeys,\n\tpointInPolygon,\n\ttoRichText,\n} from '@tldraw/editor'\nimport { isEmptyRichText, renderHtmlFromRichTextForMeasurement } from '../../utils/text/richText'\nimport {\n\tARROW_LABEL_FONT_SIZES,\n\tARROW_LABEL_PADDING,\n\tFONT_FAMILIES,\n\tLABEL_TO_ARROW_PADDING,\n\tSTROKE_SIZES,\n\tTEXT_PROPS,\n} from '../shared/default-shape-constants'\nimport { TLArrowInfo } from './arrow-types'\nimport { getArrowInfo } from './shared'\n\nexport function getArrowBodyGeometry(editor: Editor, shape: TLArrowShape) {\n\tconst info = getArrowInfo(editor, shape)!\n\tswitch (info.type) {\n\t\tcase 'straight':\n\t\t\treturn new Edge2d({\n\t\t\t\tstart: Vec.From(info.start.point),\n\t\t\t\tend: Vec.From(info.end.point),\n\t\t\t})\n\t\tcase 'arc':\n\t\t\treturn new Arc2d({\n\t\t\t\tcenter: Vec.Cast(info.handleArc.center),\n\t\t\t\tstart: Vec.Cast(info.start.point),\n\t\t\t\tend: Vec.Cast(info.end.point),\n\t\t\t\tsweepFlag: info.bodyArc.sweepFlag,\n\t\t\t\tlargeArcFlag: info.bodyArc.largeArcFlag,\n\t\t\t})\n\t\tcase 'elbow':\n\t\t\treturn new Polyline2d({ points: info.route.points })\n\t\tdefault:\n\t\t\texhaustiveSwitchError(info, 'type')\n\t}\n}\n\nconst labelSizeCache = createComputedCache(\n\t'arrow label size',\n\t(editor: Editor, shape: TLArrowShape) => {\n\t\teditor.fonts.trackFontsForShape(shape)\n\t\tlet width = 0\n\t\tlet height = 0\n\n\t\tconst bodyGeom = getArrowBodyGeometry(editor, shape)\n\t\t// We use 'i' as a default label to measure against as a minimum width.\n\t\tconst isEmpty = isEmptyRichText(shape.props.richText)\n\t\tconst html = renderHtmlFromRichTextForMeasurement(\n\t\t\teditor,\n\t\t\tisEmpty ? toRichText('i') : shape.props.richText\n\t\t)\n\n\t\tconst bodyBounds = bodyGeom.bounds\n\n\t\tconst fontSize = getArrowLabelFontSize(shape)\n\n\t\t// First we measure the text with no constraints\n\t\tconst { w, h } = editor.textMeasure.measureHtml(html, {\n\t\t\t...TEXT_PROPS,\n\t\t\tfontFamily: FONT_FAMILIES[shape.props.font],\n\t\t\tfontSize,\n\t\t\tmaxWidth: null,\n\t\t})\n\n\t\twidth = w\n\t\theight = h\n\n\t\tlet shouldSquish = false\n\n\t\t// If the text is wider than the body, we need to squish it\n\t\tconst info = getArrowInfo(editor, shape)!\n\t\tconst labelToArrowPadding = getLabelToArrowPadding(shape)\n\t\tconst margin =\n\t\t\tinfo.type === 'elbow'\n\t\t\t\t? Math.max(info.elbow.A.arrowheadOffset + labelToArrowPadding, 32) +\n\t\t\t\t\tMath.max(info.elbow.B.arrowheadOffset + labelToArrowPadding, 32)\n\t\t\t\t: 64\n\n\t\tif (bodyBounds.width > bodyBounds.height) {\n\t\t\twidth = Math.max(Math.min(w, margin), Math.min(bodyBounds.width - margin, w))\n\t\t\tshouldSquish = true\n\t\t} else if (width > 16 * fontSize) {\n\t\t\twidth = 16 * fontSize\n\t\t\tshouldSquish = true\n\t\t}\n\n\t\tif (shouldSquish) {\n\t\t\tconst { w: squishedWidth, h: squishedHeight } = editor.textMeasure.measureHtml(html, {\n\t\t\t\t...TEXT_PROPS,\n\t\t\t\tfontFamily: FONT_FAMILIES[shape.props.font],\n\t\t\t\tfontSize,\n\t\t\t\tmaxWidth: width,\n\t\t\t})\n\n\t\t\twidth = squishedWidth\n\t\t\theight = squishedHeight\n\t\t}\n\n\t\treturn new Vec(width, height).addScalar(ARROW_LABEL_PADDING * 2 * shape.props.scale)\n\t},\n\t{\n\t\tareRecordsEqual: (a, b) => {\n\t\t\tif (a.props === b.props) return true\n\n\t\t\t// If the only thing that has changed is the label position, we can skip recalculating the size\n\t\t\tconst changedKeys = getChangedKeys(a.props, b.props)\n\t\t\treturn changedKeys.length === 1 && changedKeys[0] === 'labelPosition'\n\t\t},\n\t}\n)\n\nfunction getArrowLabelSize(editor: Editor, shape: TLArrowShape) {\n\treturn labelSizeCache.get(editor, shape.id) ?? new Vec(0, 0)\n}\n\nfunction getLabelToArrowPadding(shape: TLArrowShape) {\n\tconst strokeWidth = STROKE_SIZES[shape.props.size]\n\tconst labelToArrowPadding =\n\t\t(LABEL_TO_ARROW_PADDING +\n\t\t\t(strokeWidth - STROKE_SIZES.s) * 2 +\n\t\t\t(strokeWidth === STROKE_SIZES.xl ? 20 : 0)) *\n\t\tshape.props.scale\n\n\treturn labelToArrowPadding\n}\n\n/**\n * Return the range of possible label positions for an arrow. The full possible range is 0 to 1, but\n * as the label itself takes up space the usable range is smaller.\n */\nfunction getArrowLabelRange(editor: Editor, shape: TLArrowShape, info: TLArrowInfo) {\n\tconst bodyGeom = getArrowBodyGeometry(editor, shape)\n\tconst dbgPoints: VecLike[] = []\n\tconst dbg: Geometry2d[] = [new Group2d({ children: [bodyGeom], debugColor: 'lime' })]\n\n\tconst labelSize = getArrowLabelSize(editor, shape)\n\tconst labelToArrowPadding = getLabelToArrowPadding(shape)\n\tconst paddingRelative = labelToArrowPadding / bodyGeom.length\n\n\t// we can calculate the range by sticking the center of the label at the very start/end of the\n\t// arrow, and seeing where the label intersects with the arrow. Then, if we move the label's\n\t// center to that point, that'll be the start/end of the range.\n\n\tlet startBox, endBox\n\tif (info.type === 'elbow') {\n\t\t// for elbow arrows, because they have multiple segments but are always axis-aligned, we can use\n\t\t// an expanded box. This helps keep the box from partially covering the first segment when it's\n\t\t// very small.\n\t\tdbgPoints.push(info.start.point, info.end.point)\n\t\tstartBox = Box.FromCenter(info.start.point, labelSize).expandBy(labelToArrowPadding)\n\t\tendBox = Box.FromCenter(info.end.point, labelSize).expandBy(labelToArrowPadding)\n\t} else {\n\t\t// for other arrows, we move along the arrow by the padding amount to find the start/end points\n\t\tconst startPoint = bodyGeom.interpolateAlongEdge(paddingRelative)\n\t\tconst endPoint = bodyGeom.interpolateAlongEdge(1 - paddingRelative)\n\t\tdbgPoints.push(startPoint, endPoint)\n\t\tstartBox = Box.FromCenter(startPoint, labelSize)\n\t\tendBox = Box.FromCenter(endPoint, labelSize)\n\t}\n\tconst startIntersections = bodyGeom.intersectPolygon(startBox.corners)\n\tconst endIntersections = bodyGeom.intersectPolygon(endBox.corners)\n\n\tconst startConstrained = furthest(info.start.point, startIntersections)\n\tconst endConstrained = furthest(info.end.point, endIntersections)\n\n\tlet startRelative = startConstrained ? bodyGeom.uninterpolateAlongEdge(startConstrained) : 0.5\n\tlet endRelative = endConstrained ? bodyGeom.uninterpolateAlongEdge(endConstrained) : 0.5\n\n\tif (startRelative > endRelative) {\n\t\tstartRelative = 0.5\n\t\tendRelative = 0.5\n\t}\n\n\tfor (const pt of [...startIntersections, ...endIntersections, ...dbgPoints]) {\n\t\tdbg.push(\n\t\t\tnew Circle2d({\n\t\t\t\tx: pt.x - 3,\n\t\t\t\ty: pt.y - 3,\n\t\t\t\tradius: 3,\n\t\t\t\tisFilled: false,\n\t\t\t\tdebugColor: 'magenta',\n\t\t\t\tignore: true,\n\t\t\t})\n\t\t)\n\t}\n\tdbg.push(\n\t\tnew Polygon2d({\n\t\t\tpoints: startBox.corners,\n\t\t\tdebugColor: 'lime',\n\t\t\tisFilled: false,\n\t\t\tignore: true,\n\t\t}),\n\t\tnew Polygon2d({\n\t\t\tpoints: endBox.corners,\n\t\t\tdebugColor: 'lime',\n\t\t\tisFilled: false,\n\t\t\tignore: true,\n\t\t})\n\t)\n\n\treturn { start: startRelative, end: endRelative, dbg }\n}\n\ninterface ArrowheadInfo {\n\thasStartBinding: boolean\n\thasEndBinding: boolean\n\thasStartArrowhead: boolean\n\thasEndArrowhead: boolean\n}\nexport function getArrowLabelPosition(editor: Editor, shape: TLArrowShape) {\n\tconst debugGeom: Geometry2d[] = []\n\tconst info = getArrowInfo(editor, shape)!\n\n\tconst arrowheadInfo: ArrowheadInfo = {\n\t\thasStartBinding: !!info.bindings.start,\n\t\thasEndBinding: !!info.bindings.end,\n\t\thasStartArrowhead: info.start.arrowhead !== 'none',\n\t\thasEndArrowhead: info.end.arrowhead !== 'none',\n\t}\n\n\tconst range = getArrowLabelRange(editor, shape, info)\n\tif (range.dbg) debugGeom.push(...range.dbg)\n\n\tconst clampedPosition = getClampedPosition(shape, range, arrowheadInfo)\n\tconst bodyGeom = getArrowBodyGeometry(editor, shape)\n\tconst labelCenter = bodyGeom.interpolateAlongEdge(clampedPosition)\n\tconst labelSize = getArrowLabelSize(editor, shape)\n\n\treturn { box: Box.FromCenter(labelCenter, labelSize), debugGeom }\n}\n\nfunction getClampedPosition(\n\tshape: TLArrowShape,\n\trange: { start: number; end: number },\n\tarrowheadInfo: ArrowheadInfo\n) {\n\tconst { hasEndArrowhead, hasEndBinding, hasStartBinding, hasStartArrowhead } = arrowheadInfo\n\tconst clampedPosition = clamp(\n\t\tshape.props.labelPosition,\n\t\thasStartArrowhead || hasStartBinding ? range.start : 0,\n\t\thasEndArrowhead || hasEndBinding ? range.end : 1\n\t)\n\n\treturn clampedPosition\n}\n\nfunction furthest(from: VecLike, candidates: VecLike[]): VecLike | null {\n\tlet furthest: VecLike | null = null\n\tlet furthestDist = -Infinity\n\n\tfor (const candidate of candidates) {\n\t\tconst dist = Vec.Dist2(from, candidate)\n\t\tif (dist > furthestDist) {\n\t\t\tfurthest = candidate\n\t\t\tfurthestDist = dist\n\t\t}\n\t}\n\n\treturn furthest\n}\n\nexport function getArrowLabelFontSize(shape: TLArrowShape) {\n\treturn ARROW_LABEL_FONT_SIZES[shape.props.size] * shape.props.scale\n}\n\nexport function getArrowLabelDefaultPosition(editor: Editor, shape: TLArrowShape) {\n\tconst info = getArrowInfo(editor, shape)!\n\tswitch (info.type) {\n\t\tcase 'straight':\n\t\tcase 'arc':\n\t\t\treturn 0.5\n\t\tcase 'elbow': {\n\t\t\tconst midpointHandle = info.route.midpointHandle\n\t\t\tconst bodyGeom = getArrowBodyGeometry(editor, shape)\n\t\t\tif (midpointHandle && bodyGeom) {\n\t\t\t\treturn bodyGeom.uninterpolateAlongEdge(midpointHandle.point)\n\t\t\t}\n\t\t\treturn 0.5\n\t\t}\n\t\tdefault:\n\t\t\texhaustiveSwitchError(info, 'type')\n\t}\n}\n\n/** @internal */\nexport function isOverArrowLabel(editor: Editor, shape: TLShape) {\n\tif (!editor.isShapeOfType<TLArrowShape>(shape, 'arrow')) return false\n\n\tconst pointInShapeSpace = editor.getPointInShapeSpace(shape, editor.inputs.currentPagePoint)\n\t// How should we handle multiple labels? Do shapes ever have multiple labels?\n\tconst labelGeometry = editor.getShapeGeometry<Group2d>(shape).children[1]\n\t// Knowing what we know about arrows... if the shape has no text in its label,\n\t// then the label geometry should not be there.\n\treturn labelGeometry && pointInPolygon(pointInShapeSpace, labelGeometry.vertices)\n}\n"],
5
- "mappings": "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",
6
- "names": ["furthest"]
4
+ "sourcesContent": ["import {\n\tArc2d,\n\tBox,\n\tCircle2d,\n\tEdge2d,\n\tEditor,\n\tGeometry2d,\n\tGroup2d,\n\tPolygon2d,\n\tPolyline2d,\n\tTLArrowShape,\n\tTLShape,\n\tVec,\n\tVecLike,\n\tclamp,\n\tcreateComputedCache,\n\texhaustiveSwitchError,\n\tgetChangedKeys,\n\tpointInPolygon,\n\ttoRichText,\n} from '@tldraw/editor'\nimport { isEmptyRichText, renderHtmlFromRichTextForMeasurement } from '../../utils/text/richText'\nimport {\n\tARROW_LABEL_FONT_SIZES,\n\tARROW_LABEL_PADDING,\n\tFONT_FAMILIES,\n\tLABEL_TO_ARROW_PADDING,\n\tSTROKE_SIZES,\n\tTEXT_PROPS,\n} from '../shared/default-shape-constants'\nimport { TLArrowInfo } from './arrow-types'\nimport { getArrowInfo } from './shared'\n\nexport function getArrowBodyGeometry(editor: Editor, shape: TLArrowShape) {\n\tconst info = getArrowInfo(editor, shape)!\n\tswitch (info.type) {\n\t\tcase 'straight':\n\t\t\treturn new Edge2d({\n\t\t\t\tstart: Vec.From(info.start.point),\n\t\t\t\tend: Vec.From(info.end.point),\n\t\t\t})\n\t\tcase 'arc':\n\t\t\treturn new Arc2d({\n\t\t\t\tcenter: Vec.Cast(info.handleArc.center),\n\t\t\t\tstart: Vec.Cast(info.start.point),\n\t\t\t\tend: Vec.Cast(info.end.point),\n\t\t\t\tsweepFlag: info.bodyArc.sweepFlag,\n\t\t\t\tlargeArcFlag: info.bodyArc.largeArcFlag,\n\t\t\t})\n\t\tcase 'elbow':\n\t\t\treturn new Polyline2d({ points: info.route.points })\n\t\tdefault:\n\t\t\texhaustiveSwitchError(info, 'type')\n\t}\n}\n\nconst labelSizeCache = createComputedCache(\n\t'arrow label size',\n\t(editor: Editor, shape: TLArrowShape) => {\n\t\teditor.fonts.trackFontsForShape(shape)\n\t\tlet width = 0\n\t\tlet height = 0\n\n\t\tconst bodyGeom = getArrowBodyGeometry(editor, shape)\n\t\t// We use 'i' as a default label to measure against as a minimum width.\n\t\tconst isEmpty = isEmptyRichText(shape.props.richText)\n\t\tconst html = renderHtmlFromRichTextForMeasurement(\n\t\t\teditor,\n\t\t\tisEmpty ? toRichText('i') : shape.props.richText\n\t\t)\n\n\t\tconst bodyBounds = bodyGeom.bounds\n\n\t\tconst fontSize = getArrowLabelFontSize(shape)\n\n\t\t// First we measure the text with no constraints\n\t\tconst { w, h } = editor.textMeasure.measureHtml(html, {\n\t\t\t...TEXT_PROPS,\n\t\t\tfontFamily: FONT_FAMILIES[shape.props.font],\n\t\t\tfontSize,\n\t\t\tmaxWidth: null,\n\t\t})\n\n\t\twidth = w\n\t\theight = h\n\n\t\tlet shouldSquish = false\n\n\t\t// If the text is wider than the body, we need to squish it\n\t\tconst info = getArrowInfo(editor, shape)!\n\t\tconst labelToArrowPadding = getLabelToArrowPadding(shape)\n\t\tconst margin =\n\t\t\tinfo.type === 'elbow'\n\t\t\t\t? Math.max(info.elbow.A.arrowheadOffset + labelToArrowPadding, 32) +\n\t\t\t\t\tMath.max(info.elbow.B.arrowheadOffset + labelToArrowPadding, 32)\n\t\t\t\t: 64\n\n\t\tif (bodyBounds.width > bodyBounds.height) {\n\t\t\twidth = Math.max(Math.min(w, margin), Math.min(bodyBounds.width - margin, w))\n\t\t\tshouldSquish = true\n\t\t} else if (width > 16 * fontSize) {\n\t\t\twidth = 16 * fontSize\n\t\t\tshouldSquish = true\n\t\t}\n\n\t\tif (shouldSquish) {\n\t\t\tconst { w: squishedWidth, h: squishedHeight } = editor.textMeasure.measureHtml(html, {\n\t\t\t\t...TEXT_PROPS,\n\t\t\t\tfontFamily: FONT_FAMILIES[shape.props.font],\n\t\t\t\tfontSize,\n\t\t\t\tmaxWidth: width,\n\t\t\t})\n\n\t\t\twidth = squishedWidth\n\t\t\theight = squishedHeight\n\t\t}\n\n\t\treturn new Vec(width, height).addScalar(ARROW_LABEL_PADDING * 2 * shape.props.scale)\n\t},\n\t{\n\t\tareRecordsEqual: (a, b) => {\n\t\t\tif (a.props === b.props) return true\n\n\t\t\t// If the only thing that has changed is the label position, we can skip recalculating the size\n\t\t\tconst changedKeys = getChangedKeys(a.props, b.props)\n\t\t\treturn changedKeys.length === 1 && changedKeys[0] === 'labelPosition'\n\t\t},\n\t}\n)\n\nfunction getArrowLabelSize(editor: Editor, shape: TLArrowShape) {\n\treturn labelSizeCache.get(editor, shape.id) ?? new Vec(0, 0)\n}\n\nfunction getLabelToArrowPadding(shape: TLArrowShape) {\n\tconst strokeWidth = STROKE_SIZES[shape.props.size]\n\tconst labelToArrowPadding =\n\t\t(LABEL_TO_ARROW_PADDING +\n\t\t\t(strokeWidth - STROKE_SIZES.s) * 2 +\n\t\t\t(strokeWidth === STROKE_SIZES.xl ? 20 : 0)) *\n\t\tshape.props.scale\n\n\treturn labelToArrowPadding\n}\n\n/**\n * Return the range of possible label positions for an arrow. The full possible range is 0 to 1, but\n * as the label itself takes up space the usable range is smaller.\n */\nfunction getArrowLabelRange(editor: Editor, shape: TLArrowShape, info: TLArrowInfo) {\n\tconst bodyGeom = getArrowBodyGeometry(editor, shape)\n\tconst dbgPoints: VecLike[] = []\n\tconst dbg: Geometry2d[] = [new Group2d({ children: [bodyGeom], debugColor: 'lime' })]\n\n\tconst labelSize = getArrowLabelSize(editor, shape)\n\tconst labelToArrowPadding = getLabelToArrowPadding(shape)\n\tconst paddingRelative = labelToArrowPadding / bodyGeom.length\n\n\t// we can calculate the range by sticking the center of the label at the very start/end of the\n\t// arrow, and seeing where the label intersects with the arrow. Then, if we move the label's\n\t// center to that point, that'll be the start/end of the range.\n\n\tlet startBox, endBox\n\tif (info.type === 'elbow') {\n\t\t// for elbow arrows, because they have multiple segments but are always axis-aligned, we can use\n\t\t// an expanded box. This helps keep the box from partially covering the first segment when it's\n\t\t// very small.\n\t\tdbgPoints.push(info.start.point, info.end.point)\n\t\tstartBox = Box.FromCenter(info.start.point, labelSize).expandBy(labelToArrowPadding)\n\t\tendBox = Box.FromCenter(info.end.point, labelSize).expandBy(labelToArrowPadding)\n\t} else {\n\t\t// for other arrows, we move along the arrow by the padding amount to find the start/end points\n\t\tconst startPoint = bodyGeom.interpolateAlongEdge(paddingRelative)\n\t\tconst endPoint = bodyGeom.interpolateAlongEdge(1 - paddingRelative)\n\t\tdbgPoints.push(startPoint, endPoint)\n\t\tstartBox = Box.FromCenter(startPoint, labelSize)\n\t\tendBox = Box.FromCenter(endPoint, labelSize)\n\t}\n\tconst startIntersections = bodyGeom.intersectPolygon(startBox.corners)\n\tconst endIntersections = bodyGeom.intersectPolygon(endBox.corners)\n\n\tconst startConstrained = furthest(info.start.point, startIntersections)\n\tconst endConstrained = furthest(info.end.point, endIntersections)\n\n\tlet startRelative = startConstrained ? bodyGeom.uninterpolateAlongEdge(startConstrained) : 0.5\n\tlet endRelative = endConstrained ? bodyGeom.uninterpolateAlongEdge(endConstrained) : 0.5\n\n\tif (startRelative > endRelative) {\n\t\tstartRelative = 0.5\n\t\tendRelative = 0.5\n\t}\n\n\tfor (const pt of [...startIntersections, ...endIntersections, ...dbgPoints]) {\n\t\tdbg.push(\n\t\t\tnew Circle2d({\n\t\t\t\tx: pt.x - 3,\n\t\t\t\ty: pt.y - 3,\n\t\t\t\tradius: 3,\n\t\t\t\tisFilled: false,\n\t\t\t\tdebugColor: 'magenta',\n\t\t\t\tignore: true,\n\t\t\t})\n\t\t)\n\t}\n\tdbg.push(\n\t\tnew Polygon2d({\n\t\t\tpoints: startBox.corners,\n\t\t\tdebugColor: 'lime',\n\t\t\tisFilled: false,\n\t\t\tignore: true,\n\t\t}),\n\t\tnew Polygon2d({\n\t\t\tpoints: endBox.corners,\n\t\t\tdebugColor: 'lime',\n\t\t\tisFilled: false,\n\t\t\tignore: true,\n\t\t})\n\t)\n\n\treturn { start: startRelative, end: endRelative, dbg }\n}\n\ninterface ArrowheadInfo {\n\thasStartBinding: boolean\n\thasEndBinding: boolean\n\thasStartArrowhead: boolean\n\thasEndArrowhead: boolean\n}\nexport function getArrowLabelPosition(editor: Editor, shape: TLArrowShape) {\n\tconst isEditing = editor.getEditingShapeId() === shape.id\n\tif (!isEditing && isEmptyRichText(shape.props.richText)) {\n\t\t// Short-circuit for empty labels.\n\t\tconst bodyGeom = getArrowBodyGeometry(editor, shape)\n\t\tconst labelCenter = bodyGeom.interpolateAlongEdge(0.5)\n\t\treturn { box: Box.FromCenter(labelCenter, new Vec(0, 0)), debugGeom: [] }\n\t}\n\n\tconst debugGeom: Geometry2d[] = []\n\tconst info = getArrowInfo(editor, shape)!\n\n\tconst arrowheadInfo: ArrowheadInfo = {\n\t\thasStartBinding: !!info.bindings.start,\n\t\thasEndBinding: !!info.bindings.end,\n\t\thasStartArrowhead: info.start.arrowhead !== 'none',\n\t\thasEndArrowhead: info.end.arrowhead !== 'none',\n\t}\n\n\tconst range = getArrowLabelRange(editor, shape, info)\n\tif (range.dbg) debugGeom.push(...range.dbg)\n\n\tconst clampedPosition = getClampedPosition(shape, range, arrowheadInfo)\n\tconst bodyGeom = getArrowBodyGeometry(editor, shape)\n\tconst labelCenter = bodyGeom.interpolateAlongEdge(clampedPosition)\n\tconst labelSize = getArrowLabelSize(editor, shape)\n\n\treturn { box: Box.FromCenter(labelCenter, labelSize), debugGeom }\n}\n\nfunction getClampedPosition(\n\tshape: TLArrowShape,\n\trange: { start: number; end: number },\n\tarrowheadInfo: ArrowheadInfo\n) {\n\tconst { hasEndArrowhead, hasEndBinding, hasStartBinding, hasStartArrowhead } = arrowheadInfo\n\tconst clampedPosition = clamp(\n\t\tshape.props.labelPosition,\n\t\thasStartArrowhead || hasStartBinding ? range.start : 0,\n\t\thasEndArrowhead || hasEndBinding ? range.end : 1\n\t)\n\n\treturn clampedPosition\n}\n\nfunction furthest(from: VecLike, candidates: VecLike[]): VecLike | null {\n\tlet furthest: VecLike | null = null\n\tlet furthestDist = -Infinity\n\n\tfor (const candidate of candidates) {\n\t\tconst dist = Vec.Dist2(from, candidate)\n\t\tif (dist > furthestDist) {\n\t\t\tfurthest = candidate\n\t\t\tfurthestDist = dist\n\t\t}\n\t}\n\n\treturn furthest\n}\n\nexport function getArrowLabelFontSize(shape: TLArrowShape) {\n\treturn ARROW_LABEL_FONT_SIZES[shape.props.size] * shape.props.scale\n}\n\nexport function getArrowLabelDefaultPosition(editor: Editor, shape: TLArrowShape) {\n\tconst info = getArrowInfo(editor, shape)!\n\tswitch (info.type) {\n\t\tcase 'straight':\n\t\tcase 'arc':\n\t\t\treturn 0.5\n\t\tcase 'elbow': {\n\t\t\tconst midpointHandle = info.route.midpointHandle\n\t\t\tconst bodyGeom = getArrowBodyGeometry(editor, shape)\n\t\t\tif (midpointHandle && bodyGeom) {\n\t\t\t\treturn bodyGeom.uninterpolateAlongEdge(midpointHandle.point)\n\t\t\t}\n\t\t\treturn 0.5\n\t\t}\n\t\tdefault:\n\t\t\texhaustiveSwitchError(info, 'type')\n\t}\n}\n\n/** @internal */\nexport function isOverArrowLabel(editor: Editor, shape: TLShape) {\n\tif (!editor.isShapeOfType<TLArrowShape>(shape, 'arrow')) return false\n\n\tconst pointInShapeSpace = editor.getPointInShapeSpace(shape, editor.inputs.currentPagePoint)\n\t// How should we handle multiple labels? Do shapes ever have multiple labels?\n\tconst labelGeometry = editor.getShapeGeometry<Group2d>(shape).children[1]\n\t// Knowing what we know about arrows... if the shape has no text in its label,\n\t// then the label geometry should not be there.\n\treturn labelGeometry && pointInPolygon(pointInShapeSpace, labelGeometry.vertices)\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["bodyGeom", "labelCenter", "furthest"]
7
7
  }
@@ -112,11 +112,18 @@ function TooltipSingleton() {
112
112
  const [isOpen, setIsOpen] = (0, import_react.useState)(false);
113
113
  const triggerRef = (0, import_react.useRef)(null);
114
114
  const isFirstShowRef = (0, import_react.useRef)(true);
115
+ const editor = (0, import_editor.useMaybeEditor)();
115
116
  const currentTooltip = (0, import_editor.useValue)(
116
117
  "current tooltip",
117
118
  () => tooltipManager.getCurrentTooltipData(),
118
119
  []
119
120
  );
121
+ const cameraState = (0, import_editor.useValue)("camera state", () => editor?.getCameraState(), [editor]);
122
+ (0, import_react.useEffect)(() => {
123
+ if (cameraState === "moving" && isOpen && currentTooltip) {
124
+ tooltipManager.hideTooltip(editor, currentTooltip.id, true);
125
+ }
126
+ }, [cameraState, isOpen, currentTooltip, editor]);
120
127
  (0, import_react.useEffect)(() => {
121
128
  let timer = null;
122
129
  if (currentTooltip && triggerRef.current) {
@@ -184,7 +191,6 @@ const TldrawUiTooltip = (0, import_react.forwardRef)(
184
191
  const hasProvider = (0, import_react.useContext)(TooltipSingletonContext);
185
192
  const orientationCtx = (0, import_layout.useTldrawUiOrientation)();
186
193
  const sideToUse = side ?? orientationCtx.tooltipSide;
187
- const camera = (0, import_editor.useValue)("camera", () => editor?.getCamera(), []);
188
194
  (0, import_react.useEffect)(() => {
189
195
  const currentTooltipId = tooltipId.current;
190
196
  return () => {
@@ -193,9 +199,6 @@ const TldrawUiTooltip = (0, import_react.forwardRef)(
193
199
  }
194
200
  };
195
201
  }, [editor, hasProvider]);
196
- (0, import_react.useEffect)(() => {
197
- tooltipManager.hideTooltip(editor, tooltipId.current, true);
198
- }, [editor, camera]);
199
202
  if (disabled || !content) {
200
203
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children });
201
204
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/lib/ui/components/primitives/TldrawUiTooltip.tsx"],
4
- "sourcesContent": ["import { assert, Atom, atom, Editor, uniqueId, useMaybeEditor, useValue } from '@tldraw/editor'\nimport { Tooltip as _Tooltip } from 'radix-ui'\nimport React, {\n\tcreateContext,\n\tforwardRef,\n\tReactNode,\n\tuseContext,\n\tuseEffect,\n\tuseRef,\n\tuseState,\n} from 'react'\nimport { useTldrawUiOrientation } from './layout'\n\nconst DEFAULT_TOOLTIP_DELAY_MS = 700\n\n/** @public */\nexport interface TldrawUiTooltipProps {\n\tchildren: React.ReactNode\n\tcontent?: string | React.ReactNode\n\tside?: 'top' | 'right' | 'bottom' | 'left'\n\tsideOffset?: number\n\tdisabled?: boolean\n\tshowOnMobile?: boolean\n\tdelayDuration?: number\n}\n\n// Singleton tooltip manager\nclass TooltipManager {\n\tprivate static instance: TooltipManager | null = null\n\tprivate currentTooltip = atom<{\n\t\tid: string\n\t\tcontent: ReactNode\n\t\tside: 'top' | 'right' | 'bottom' | 'left'\n\t\tsideOffset: number\n\t\tshowOnMobile: boolean\n\t\ttargetElement: HTMLElement\n\t\tdelayDuration: number\n\t} | null>('current tooltip', null)\n\tprivate destroyTimeoutId: number | null = null\n\n\tstatic getInstance(): TooltipManager {\n\t\tif (!TooltipManager.instance) {\n\t\t\tTooltipManager.instance = new TooltipManager()\n\t\t}\n\t\treturn TooltipManager.instance\n\t}\n\n\tshowTooltip(\n\t\ttooltipId: string,\n\t\tcontent: string | React.ReactNode,\n\t\ttargetElement: HTMLElement,\n\t\tside: 'top' | 'right' | 'bottom' | 'left',\n\t\tsideOffset: number,\n\t\tshowOnMobile: boolean,\n\t\tdelayDuration: number\n\t) {\n\t\t// Clear any existing destroy timeout\n\t\tif (this.destroyTimeoutId) {\n\t\t\tclearTimeout(this.destroyTimeoutId)\n\t\t\tthis.destroyTimeoutId = null\n\t\t}\n\n\t\t// Update current tooltip\n\t\tthis.currentTooltip.set({\n\t\t\tid: tooltipId,\n\t\t\tcontent,\n\t\t\tside,\n\t\t\tsideOffset,\n\t\t\tshowOnMobile,\n\t\t\ttargetElement,\n\t\t\tdelayDuration,\n\t\t})\n\t}\n\n\thideTooltip(editor: Editor | null, tooltipId: string, instant: boolean = false) {\n\t\tconst hide = () => {\n\t\t\t// Only hide if this is the current tooltip\n\t\t\tif (this.currentTooltip.get()?.id === tooltipId) {\n\t\t\t\tthis.currentTooltip.set(null)\n\t\t\t\tthis.destroyTimeoutId = null\n\t\t\t}\n\t\t}\n\n\t\tif (editor && !instant) {\n\t\t\t// Start destroy timeout (1 second)\n\t\t\tthis.destroyTimeoutId = editor.timers.setTimeout(hide, 300)\n\t\t} else {\n\t\t\thide()\n\t\t}\n\t}\n\n\thideAllTooltips() {\n\t\tthis.currentTooltip.set(null)\n\t\tthis.destroyTimeoutId = null\n\t}\n\n\tgetCurrentTooltipData() {\n\t\tconst currentTooltip = this.currentTooltip.get()\n\t\tif (!currentTooltip) return null\n\t\tif (!this.supportsHover() && !currentTooltip.showOnMobile) return null\n\t\treturn currentTooltip\n\t}\n\n\tprivate supportsHoverAtom: Atom<boolean> | null = null\n\tsupportsHover() {\n\t\tif (!this.supportsHoverAtom) {\n\t\t\tconst mediaQuery = window.matchMedia('(hover: hover)')\n\t\t\tconst supportsHover = atom('has hover', mediaQuery.matches)\n\t\t\tthis.supportsHoverAtom = supportsHover\n\t\t\tmediaQuery.addEventListener('change', (e) => {\n\t\t\t\tsupportsHover.set(e.matches)\n\t\t\t})\n\t\t}\n\t\treturn this.supportsHoverAtom.get()\n\t}\n}\n\nexport const tooltipManager = TooltipManager.getInstance()\n\n// Context for the tooltip singleton\nconst TooltipSingletonContext = createContext<boolean>(false)\n\n/** @public */\nexport interface TldrawUiTooltipProviderProps {\n\tchildren: React.ReactNode\n}\n\n/** @public @react */\nexport function TldrawUiTooltipProvider({ children }: TldrawUiTooltipProviderProps) {\n\treturn (\n\t\t<_Tooltip.Provider skipDelayDuration={700}>\n\t\t\t<TooltipSingletonContext.Provider value={true}>\n\t\t\t\t{children}\n\t\t\t\t<TooltipSingleton />\n\t\t\t</TooltipSingletonContext.Provider>\n\t\t</_Tooltip.Provider>\n\t)\n}\n\n// The singleton tooltip component that renders once\nfunction TooltipSingleton() {\n\tconst [isOpen, setIsOpen] = useState(false)\n\tconst triggerRef = useRef<HTMLDivElement>(null)\n\tconst isFirstShowRef = useRef(true)\n\n\tconst currentTooltip = useValue(\n\t\t'current tooltip',\n\t\t() => tooltipManager.getCurrentTooltipData(),\n\t\t[]\n\t)\n\n\t// Update open state and trigger position\n\tuseEffect(() => {\n\t\tlet timer: ReturnType<typeof setTimeout> | null = null\n\t\tif (currentTooltip && triggerRef.current) {\n\t\t\t// Position the invisible trigger element over the active element\n\t\t\tconst activeRect = currentTooltip.targetElement.getBoundingClientRect()\n\t\t\tconst trigger = triggerRef.current\n\n\t\t\ttrigger.style.position = 'fixed'\n\t\t\ttrigger.style.left = `${activeRect.left}px`\n\t\t\ttrigger.style.top = `${activeRect.top}px`\n\t\t\ttrigger.style.width = `${activeRect.width}px`\n\t\t\ttrigger.style.height = `${activeRect.height}px`\n\t\t\ttrigger.style.pointerEvents = 'none'\n\t\t\ttrigger.style.zIndex = '9999'\n\n\t\t\t// Handle delay for first show\n\t\t\tif (isFirstShowRef.current) {\n\t\t\t\t// eslint-disable-next-line no-restricted-globals\n\t\t\t\ttimer = setTimeout(() => {\n\t\t\t\t\tsetIsOpen(true)\n\t\t\t\t\tisFirstShowRef.current = false\n\t\t\t\t}, currentTooltip.delayDuration)\n\t\t\t} else {\n\t\t\t\t// Subsequent tooltips show immediately\n\t\t\t\tsetIsOpen(true)\n\t\t\t}\n\t\t} else {\n\t\t\t// Hide tooltip immediately\n\t\t\tsetIsOpen(false)\n\t\t\t// Reset first show state after tooltip is hidden\n\t\t\tisFirstShowRef.current = true\n\t\t}\n\n\t\treturn () => {\n\t\t\tif (timer !== null) {\n\t\t\t\tclearTimeout(timer)\n\t\t\t}\n\t\t}\n\t}, [currentTooltip])\n\n\tif (!currentTooltip) {\n\t\treturn null\n\t}\n\n\treturn (\n\t\t<_Tooltip.Root open={isOpen} delayDuration={0}>\n\t\t\t<_Tooltip.Trigger asChild>\n\t\t\t\t<div ref={triggerRef} />\n\t\t\t</_Tooltip.Trigger>\n\t\t\t<_Tooltip.Content\n\t\t\t\tclassName=\"tlui-tooltip\"\n\t\t\t\tside={currentTooltip.side}\n\t\t\t\tsideOffset={currentTooltip.sideOffset}\n\t\t\t\tavoidCollisions\n\t\t\t\tcollisionPadding={8}\n\t\t\t\tdir=\"ltr\"\n\t\t\t>\n\t\t\t\t{currentTooltip.content}\n\t\t\t\t<_Tooltip.Arrow className=\"tlui-tooltip__arrow\" />\n\t\t\t</_Tooltip.Content>\n\t\t</_Tooltip.Root>\n\t)\n}\n\n/** @public @react */\nexport const TldrawUiTooltip = forwardRef<HTMLButtonElement, TldrawUiTooltipProps>(\n\t(\n\t\t{\n\t\t\tchildren,\n\t\t\tcontent,\n\t\t\tside,\n\t\t\tsideOffset = 5,\n\t\t\tdisabled = false,\n\t\t\tshowOnMobile = false,\n\t\t\tdelayDuration,\n\t\t},\n\t\tref\n\t) => {\n\t\tconst editor = useMaybeEditor()\n\t\tconst tooltipId = useRef<string>(uniqueId())\n\t\tconst hasProvider = useContext(TooltipSingletonContext)\n\n\t\tconst orientationCtx = useTldrawUiOrientation()\n\t\tconst sideToUse = side ?? orientationCtx.tooltipSide\n\n\t\tconst camera = useValue('camera', () => editor?.getCamera(), [])\n\n\t\tuseEffect(() => {\n\t\t\tconst currentTooltipId = tooltipId.current\n\t\t\treturn () => {\n\t\t\t\tif (hasProvider) {\n\t\t\t\t\ttooltipManager.hideTooltip(editor, currentTooltipId, true)\n\t\t\t\t}\n\t\t\t}\n\t\t}, [editor, hasProvider])\n\n\t\tuseEffect(() => {\n\t\t\ttooltipManager.hideTooltip(editor, tooltipId.current, true)\n\t\t}, [editor, camera])\n\n\t\t// Don't show tooltip if disabled, no content, or UI labels are disabled\n\t\tif (disabled || !content) {\n\t\t\treturn <>{children}</>\n\t\t}\n\n\t\tconst delayDurationToUse =\n\t\t\tdelayDuration ?? (editor?.options.tooltipDelayMs || DEFAULT_TOOLTIP_DELAY_MS)\n\n\t\t// Fallback to old behavior if no provider\n\t\tif (!hasProvider) {\n\t\t\treturn (\n\t\t\t\t<_Tooltip.Root delayDuration={delayDurationToUse} disableHoverableContent>\n\t\t\t\t\t<_Tooltip.Trigger asChild ref={ref}>\n\t\t\t\t\t\t{children}\n\t\t\t\t\t</_Tooltip.Trigger>\n\t\t\t\t\t<_Tooltip.Content\n\t\t\t\t\t\tclassName=\"tlui-tooltip\"\n\t\t\t\t\t\tside={sideToUse}\n\t\t\t\t\t\tsideOffset={sideOffset}\n\t\t\t\t\t\tavoidCollisions\n\t\t\t\t\t\tcollisionPadding={8}\n\t\t\t\t\t\tdir=\"ltr\"\n\t\t\t\t\t>\n\t\t\t\t\t\t{content}\n\t\t\t\t\t\t<_Tooltip.Arrow className=\"tlui-tooltip__arrow\" />\n\t\t\t\t\t</_Tooltip.Content>\n\t\t\t\t</_Tooltip.Root>\n\t\t\t)\n\t\t}\n\n\t\tconst child = React.Children.only(children)\n\t\tassert(React.isValidElement(child), 'TldrawUiTooltip children must be a single element')\n\n\t\tconst handleMouseEnter = (event: React.MouseEvent<HTMLElement>) => {\n\t\t\tchild.props.onMouseEnter?.(event)\n\t\t\ttooltipManager.showTooltip(\n\t\t\t\ttooltipId.current,\n\t\t\t\tcontent,\n\t\t\t\tevent.currentTarget as HTMLElement,\n\t\t\t\tsideToUse,\n\t\t\t\tsideOffset,\n\t\t\t\tshowOnMobile,\n\t\t\t\tdelayDurationToUse\n\t\t\t)\n\t\t}\n\n\t\tconst handleMouseLeave = (event: React.MouseEvent<HTMLElement>) => {\n\t\t\tchild.props.onMouseLeave?.(event)\n\t\t\ttooltipManager.hideTooltip(editor, tooltipId.current)\n\t\t}\n\n\t\tconst handleFocus = (event: React.FocusEvent<HTMLElement>) => {\n\t\t\tchild.props.onFocus?.(event)\n\t\t\ttooltipManager.showTooltip(\n\t\t\t\ttooltipId.current,\n\t\t\t\tcontent,\n\t\t\t\tevent.currentTarget as HTMLElement,\n\t\t\t\tsideToUse,\n\t\t\t\tsideOffset,\n\t\t\t\tshowOnMobile,\n\t\t\t\tdelayDurationToUse\n\t\t\t)\n\t\t}\n\n\t\tconst handleBlur = (event: React.FocusEvent<HTMLElement>) => {\n\t\t\tchild.props.onBlur?.(event)\n\t\t\ttooltipManager.hideTooltip(editor, tooltipId.current)\n\t\t}\n\n\t\tconst childrenWithHandlers = React.cloneElement(children as React.ReactElement, {\n\t\t\tonMouseEnter: handleMouseEnter,\n\t\t\tonMouseLeave: handleMouseLeave,\n\t\t\tonFocus: handleFocus,\n\t\t\tonBlur: handleBlur,\n\t\t})\n\n\t\treturn childrenWithHandlers\n\t}\n)\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { assert, Atom, atom, Editor, uniqueId, useMaybeEditor, useValue } from '@tldraw/editor'\nimport { Tooltip as _Tooltip } from 'radix-ui'\nimport React, {\n\tcreateContext,\n\tforwardRef,\n\tReactNode,\n\tuseContext,\n\tuseEffect,\n\tuseRef,\n\tuseState,\n} from 'react'\nimport { useTldrawUiOrientation } from './layout'\n\nconst DEFAULT_TOOLTIP_DELAY_MS = 700\n\n/** @public */\nexport interface TldrawUiTooltipProps {\n\tchildren: React.ReactNode\n\tcontent?: string | React.ReactNode\n\tside?: 'top' | 'right' | 'bottom' | 'left'\n\tsideOffset?: number\n\tdisabled?: boolean\n\tshowOnMobile?: boolean\n\tdelayDuration?: number\n}\n\n// Singleton tooltip manager\nclass TooltipManager {\n\tprivate static instance: TooltipManager | null = null\n\tprivate currentTooltip = atom<{\n\t\tid: string\n\t\tcontent: ReactNode\n\t\tside: 'top' | 'right' | 'bottom' | 'left'\n\t\tsideOffset: number\n\t\tshowOnMobile: boolean\n\t\ttargetElement: HTMLElement\n\t\tdelayDuration: number\n\t} | null>('current tooltip', null)\n\tprivate destroyTimeoutId: number | null = null\n\n\tstatic getInstance(): TooltipManager {\n\t\tif (!TooltipManager.instance) {\n\t\t\tTooltipManager.instance = new TooltipManager()\n\t\t}\n\t\treturn TooltipManager.instance\n\t}\n\n\tshowTooltip(\n\t\ttooltipId: string,\n\t\tcontent: string | React.ReactNode,\n\t\ttargetElement: HTMLElement,\n\t\tside: 'top' | 'right' | 'bottom' | 'left',\n\t\tsideOffset: number,\n\t\tshowOnMobile: boolean,\n\t\tdelayDuration: number\n\t) {\n\t\t// Clear any existing destroy timeout\n\t\tif (this.destroyTimeoutId) {\n\t\t\tclearTimeout(this.destroyTimeoutId)\n\t\t\tthis.destroyTimeoutId = null\n\t\t}\n\n\t\t// Update current tooltip\n\t\tthis.currentTooltip.set({\n\t\t\tid: tooltipId,\n\t\t\tcontent,\n\t\t\tside,\n\t\t\tsideOffset,\n\t\t\tshowOnMobile,\n\t\t\ttargetElement,\n\t\t\tdelayDuration,\n\t\t})\n\t}\n\n\thideTooltip(editor: Editor | null, tooltipId: string, instant: boolean = false) {\n\t\tconst hide = () => {\n\t\t\t// Only hide if this is the current tooltip\n\t\t\tif (this.currentTooltip.get()?.id === tooltipId) {\n\t\t\t\tthis.currentTooltip.set(null)\n\t\t\t\tthis.destroyTimeoutId = null\n\t\t\t}\n\t\t}\n\n\t\tif (editor && !instant) {\n\t\t\t// Start destroy timeout (1 second)\n\t\t\tthis.destroyTimeoutId = editor.timers.setTimeout(hide, 300)\n\t\t} else {\n\t\t\thide()\n\t\t}\n\t}\n\n\thideAllTooltips() {\n\t\tthis.currentTooltip.set(null)\n\t\tthis.destroyTimeoutId = null\n\t}\n\n\tgetCurrentTooltipData() {\n\t\tconst currentTooltip = this.currentTooltip.get()\n\t\tif (!currentTooltip) return null\n\t\tif (!this.supportsHover() && !currentTooltip.showOnMobile) return null\n\t\treturn currentTooltip\n\t}\n\n\tprivate supportsHoverAtom: Atom<boolean> | null = null\n\tsupportsHover() {\n\t\tif (!this.supportsHoverAtom) {\n\t\t\tconst mediaQuery = window.matchMedia('(hover: hover)')\n\t\t\tconst supportsHover = atom('has hover', mediaQuery.matches)\n\t\t\tthis.supportsHoverAtom = supportsHover\n\t\t\tmediaQuery.addEventListener('change', (e) => {\n\t\t\t\tsupportsHover.set(e.matches)\n\t\t\t})\n\t\t}\n\t\treturn this.supportsHoverAtom.get()\n\t}\n}\n\nexport const tooltipManager = TooltipManager.getInstance()\n\n// Context for the tooltip singleton\nconst TooltipSingletonContext = createContext<boolean>(false)\n\n/** @public */\nexport interface TldrawUiTooltipProviderProps {\n\tchildren: React.ReactNode\n}\n\n/** @public @react */\nexport function TldrawUiTooltipProvider({ children }: TldrawUiTooltipProviderProps) {\n\treturn (\n\t\t<_Tooltip.Provider skipDelayDuration={700}>\n\t\t\t<TooltipSingletonContext.Provider value={true}>\n\t\t\t\t{children}\n\t\t\t\t<TooltipSingleton />\n\t\t\t</TooltipSingletonContext.Provider>\n\t\t</_Tooltip.Provider>\n\t)\n}\n\n// The singleton tooltip component that renders once\nfunction TooltipSingleton() {\n\tconst [isOpen, setIsOpen] = useState(false)\n\tconst triggerRef = useRef<HTMLDivElement>(null)\n\tconst isFirstShowRef = useRef(true)\n\tconst editor = useMaybeEditor()\n\n\tconst currentTooltip = useValue(\n\t\t'current tooltip',\n\t\t() => tooltipManager.getCurrentTooltipData(),\n\t\t[]\n\t)\n\n\tconst cameraState = useValue('camera state', () => editor?.getCameraState(), [editor])\n\n\t// Hide tooltip when camera is moving (panning/zooming)\n\tuseEffect(() => {\n\t\tif (cameraState === 'moving' && isOpen && currentTooltip) {\n\t\t\ttooltipManager.hideTooltip(editor, currentTooltip.id, true)\n\t\t}\n\t}, [cameraState, isOpen, currentTooltip, editor])\n\n\t// Update open state and trigger position\n\tuseEffect(() => {\n\t\tlet timer: ReturnType<typeof setTimeout> | null = null\n\t\tif (currentTooltip && triggerRef.current) {\n\t\t\t// Position the invisible trigger element over the active element\n\t\t\tconst activeRect = currentTooltip.targetElement.getBoundingClientRect()\n\t\t\tconst trigger = triggerRef.current\n\n\t\t\ttrigger.style.position = 'fixed'\n\t\t\ttrigger.style.left = `${activeRect.left}px`\n\t\t\ttrigger.style.top = `${activeRect.top}px`\n\t\t\ttrigger.style.width = `${activeRect.width}px`\n\t\t\ttrigger.style.height = `${activeRect.height}px`\n\t\t\ttrigger.style.pointerEvents = 'none'\n\t\t\ttrigger.style.zIndex = '9999'\n\n\t\t\t// Handle delay for first show\n\t\t\tif (isFirstShowRef.current) {\n\t\t\t\t// eslint-disable-next-line no-restricted-globals\n\t\t\t\ttimer = setTimeout(() => {\n\t\t\t\t\tsetIsOpen(true)\n\t\t\t\t\tisFirstShowRef.current = false\n\t\t\t\t}, currentTooltip.delayDuration)\n\t\t\t} else {\n\t\t\t\t// Subsequent tooltips show immediately\n\t\t\t\tsetIsOpen(true)\n\t\t\t}\n\t\t} else {\n\t\t\t// Hide tooltip immediately\n\t\t\tsetIsOpen(false)\n\t\t\t// Reset first show state after tooltip is hidden\n\t\t\tisFirstShowRef.current = true\n\t\t}\n\n\t\treturn () => {\n\t\t\tif (timer !== null) {\n\t\t\t\tclearTimeout(timer)\n\t\t\t}\n\t\t}\n\t}, [currentTooltip])\n\n\tif (!currentTooltip) {\n\t\treturn null\n\t}\n\n\treturn (\n\t\t<_Tooltip.Root open={isOpen} delayDuration={0}>\n\t\t\t<_Tooltip.Trigger asChild>\n\t\t\t\t<div ref={triggerRef} />\n\t\t\t</_Tooltip.Trigger>\n\t\t\t<_Tooltip.Content\n\t\t\t\tclassName=\"tlui-tooltip\"\n\t\t\t\tside={currentTooltip.side}\n\t\t\t\tsideOffset={currentTooltip.sideOffset}\n\t\t\t\tavoidCollisions\n\t\t\t\tcollisionPadding={8}\n\t\t\t\tdir=\"ltr\"\n\t\t\t>\n\t\t\t\t{currentTooltip.content}\n\t\t\t\t<_Tooltip.Arrow className=\"tlui-tooltip__arrow\" />\n\t\t\t</_Tooltip.Content>\n\t\t</_Tooltip.Root>\n\t)\n}\n\n/** @public @react */\nexport const TldrawUiTooltip = forwardRef<HTMLButtonElement, TldrawUiTooltipProps>(\n\t(\n\t\t{\n\t\t\tchildren,\n\t\t\tcontent,\n\t\t\tside,\n\t\t\tsideOffset = 5,\n\t\t\tdisabled = false,\n\t\t\tshowOnMobile = false,\n\t\t\tdelayDuration,\n\t\t},\n\t\tref\n\t) => {\n\t\tconst editor = useMaybeEditor()\n\t\tconst tooltipId = useRef<string>(uniqueId())\n\t\tconst hasProvider = useContext(TooltipSingletonContext)\n\n\t\tconst orientationCtx = useTldrawUiOrientation()\n\t\tconst sideToUse = side ?? orientationCtx.tooltipSide\n\n\t\tuseEffect(() => {\n\t\t\tconst currentTooltipId = tooltipId.current\n\t\t\treturn () => {\n\t\t\t\tif (hasProvider) {\n\t\t\t\t\ttooltipManager.hideTooltip(editor, currentTooltipId, true)\n\t\t\t\t}\n\t\t\t}\n\t\t}, [editor, hasProvider])\n\n\t\t// Don't show tooltip if disabled, no content, or UI labels are disabled\n\t\tif (disabled || !content) {\n\t\t\treturn <>{children}</>\n\t\t}\n\n\t\tconst delayDurationToUse =\n\t\t\tdelayDuration ?? (editor?.options.tooltipDelayMs || DEFAULT_TOOLTIP_DELAY_MS)\n\n\t\t// Fallback to old behavior if no provider\n\t\tif (!hasProvider) {\n\t\t\treturn (\n\t\t\t\t<_Tooltip.Root delayDuration={delayDurationToUse} disableHoverableContent>\n\t\t\t\t\t<_Tooltip.Trigger asChild ref={ref}>\n\t\t\t\t\t\t{children}\n\t\t\t\t\t</_Tooltip.Trigger>\n\t\t\t\t\t<_Tooltip.Content\n\t\t\t\t\t\tclassName=\"tlui-tooltip\"\n\t\t\t\t\t\tside={sideToUse}\n\t\t\t\t\t\tsideOffset={sideOffset}\n\t\t\t\t\t\tavoidCollisions\n\t\t\t\t\t\tcollisionPadding={8}\n\t\t\t\t\t\tdir=\"ltr\"\n\t\t\t\t\t>\n\t\t\t\t\t\t{content}\n\t\t\t\t\t\t<_Tooltip.Arrow className=\"tlui-tooltip__arrow\" />\n\t\t\t\t\t</_Tooltip.Content>\n\t\t\t\t</_Tooltip.Root>\n\t\t\t)\n\t\t}\n\n\t\tconst child = React.Children.only(children)\n\t\tassert(React.isValidElement(child), 'TldrawUiTooltip children must be a single element')\n\n\t\tconst handleMouseEnter = (event: React.MouseEvent<HTMLElement>) => {\n\t\t\tchild.props.onMouseEnter?.(event)\n\t\t\ttooltipManager.showTooltip(\n\t\t\t\ttooltipId.current,\n\t\t\t\tcontent,\n\t\t\t\tevent.currentTarget as HTMLElement,\n\t\t\t\tsideToUse,\n\t\t\t\tsideOffset,\n\t\t\t\tshowOnMobile,\n\t\t\t\tdelayDurationToUse\n\t\t\t)\n\t\t}\n\n\t\tconst handleMouseLeave = (event: React.MouseEvent<HTMLElement>) => {\n\t\t\tchild.props.onMouseLeave?.(event)\n\t\t\ttooltipManager.hideTooltip(editor, tooltipId.current)\n\t\t}\n\n\t\tconst handleFocus = (event: React.FocusEvent<HTMLElement>) => {\n\t\t\tchild.props.onFocus?.(event)\n\t\t\ttooltipManager.showTooltip(\n\t\t\t\ttooltipId.current,\n\t\t\t\tcontent,\n\t\t\t\tevent.currentTarget as HTMLElement,\n\t\t\t\tsideToUse,\n\t\t\t\tsideOffset,\n\t\t\t\tshowOnMobile,\n\t\t\t\tdelayDurationToUse\n\t\t\t)\n\t\t}\n\n\t\tconst handleBlur = (event: React.FocusEvent<HTMLElement>) => {\n\t\t\tchild.props.onBlur?.(event)\n\t\t\ttooltipManager.hideTooltip(editor, tooltipId.current)\n\t\t}\n\n\t\tconst childrenWithHandlers = React.cloneElement(children as React.ReactElement, {\n\t\t\tonMouseEnter: handleMouseEnter,\n\t\t\tonMouseLeave: handleMouseLeave,\n\t\t\tonFocus: handleFocus,\n\t\t\tonBlur: handleBlur,\n\t\t})\n\n\t\treturn childrenWithHandlers\n\t}\n)\n"],
5
+ "mappings": "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",
6
6
  "names": ["_Tooltip", "React"]
7
7
  }
@@ -142,7 +142,7 @@ function ToolsProvider({ overrides, children }) {
142
142
  createShape: (id) => editor.createShape({
143
143
  id,
144
144
  type: "arrow",
145
- props: { start: { x: 0, y: 0 }, end: { x: 200, y: 0 } }
145
+ props: { start: { x: 0, y: 200 }, end: { x: 200, y: 0 } }
146
146
  })
147
147
  });
148
148
  trackEvent("drag-tool", { source, id: "arrow" });
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/lib/ui/hooks/useTools.tsx"],
4
- "sourcesContent": ["import {\n\tassertExists,\n\tcreateShapeId,\n\tEditor,\n\tGeoShapeGeoStyle,\n\tgetIndicesBetween,\n\tTLLineShape,\n\tTLPointerEventInfo,\n\tTLShapeId,\n\ttoRichText,\n\tuseMaybeEditor,\n} from '@tldraw/editor'\nimport * as React from 'react'\nimport { EmbedDialog } from '../components/EmbedDialog'\nimport { TLUiIconJsx } from '../components/primitives/TldrawUiIcon'\nimport { useA11y } from '../context/a11y'\nimport { TLUiEventSource, useUiEvents } from '../context/events'\nimport { TLUiIconType } from '../icon-types'\nimport { TLUiOverrideHelpers, useDefaultHelpers } from '../overrides'\nimport { TLUiTranslationKey } from './useTranslation/TLUiTranslationKey'\nimport { useTranslation } from './useTranslation/useTranslation'\n\n/** @public */\nexport interface TLUiToolItem<\n\tTranslationKey extends string = string,\n\tIconType extends string = string,\n> {\n\tid: string\n\tlabel: TranslationKey\n\tshortcutsLabel?: TranslationKey\n\ticon: IconType | TLUiIconJsx\n\tonSelect(source: TLUiEventSource): void\n\tonDragStart?(source: TLUiEventSource, info: TLPointerEventInfo): void\n\t/**\n\t * The keyboard shortcut for this tool. This is a string that can be a single key,\n\t * or a combination of keys.\n\t * For example, `cmd+z` or `cmd+shift+z` or `cmd+u,ctrl+u`, or just `v` or `a`.\n\t * We have backwards compatibility with the old system, where we used to use\n\t * symbols to denote cmd/alt/shift, using `!` for shift, `$` for cmd, and `?` for alt.\n\t */\n\tkbd?: string\n\treadonlyOk?: boolean\n\tmeta?: {\n\t\t[key: string]: any\n\t}\n}\n\n/** @public */\nexport type TLUiToolsContextType = Record<string, TLUiToolItem>\n\n/** @internal */\nexport const ToolsContext = React.createContext<null | TLUiToolsContextType>(null)\n\n/** @public */\nexport interface TLUiToolsProviderProps {\n\toverrides?(\n\t\teditor: Editor,\n\t\ttools: TLUiToolsContextType,\n\t\thelpers: Partial<TLUiOverrideHelpers>\n\t): TLUiToolsContextType\n\tchildren: React.ReactNode\n}\n\n/** @internal */\nexport function ToolsProvider({ overrides, children }: TLUiToolsProviderProps) {\n\tconst editor = useMaybeEditor()\n\tconst trackEvent = useUiEvents()\n\n\tconst a11y = useA11y()\n\tconst msg = useTranslation()\n\tconst helpers = useDefaultHelpers()\n\n\tconst onToolSelect = React.useCallback(\n\t\t(\n\t\t\tsource: TLUiEventSource,\n\t\t\ttool: TLUiToolItem<TLUiTranslationKey, TLUiIconType>,\n\t\t\tid?: string\n\t\t) => {\n\t\t\ta11y.announce({ msg: msg(tool.label) })\n\t\t\ttrackEvent('select-tool', { source, id: id ?? tool.id })\n\t\t},\n\t\t[a11y, msg, trackEvent]\n\t)\n\n\tconst tools = React.useMemo<TLUiToolsContextType>(() => {\n\t\tif (!editor) return {}\n\t\tconst toolsArray: TLUiToolItem<TLUiTranslationKey, TLUiIconType>[] = [\n\t\t\t{\n\t\t\t\tid: 'select',\n\t\t\t\tlabel: 'tool.select',\n\t\t\t\ticon: 'tool-pointer',\n\t\t\t\tkbd: 'v',\n\t\t\t\treadonlyOk: true,\n\t\t\t\tonSelect(source) {\n\t\t\t\t\tif (editor.isIn('select')) {\n\t\t\t\t\t\t// There's a quirk of select mode, where editing a shape is a sub-state of select.\n\t\t\t\t\t\t// Because the text tool can be locked/sticky, we need to make sure we exit the\n\t\t\t\t\t\t// text tool.\n\t\t\t\t\t\t//\n\t\t\t\t\t\t// psst, if you're changing this code, also change the code\n\t\t\t\t\t\t// in strange-tools.test.ts! Sadly it's duplicated there.\n\t\t\t\t\t\tconst currentNode = editor.root.getCurrent()!\n\t\t\t\t\t\tcurrentNode.exit({}, currentNode.id)\n\t\t\t\t\t\tcurrentNode.enter({}, currentNode.id)\n\t\t\t\t\t}\n\t\t\t\t\teditor.setCurrentTool('select')\n\t\t\t\t\tonToolSelect(source, this)\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: 'hand',\n\t\t\t\tlabel: 'tool.hand',\n\t\t\t\ticon: 'tool-hand',\n\t\t\t\tkbd: 'h',\n\t\t\t\treadonlyOk: true,\n\t\t\t\tonSelect(source) {\n\t\t\t\t\teditor.setCurrentTool('hand')\n\t\t\t\t\tonToolSelect(source, this)\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: 'eraser',\n\t\t\t\tlabel: 'tool.eraser',\n\t\t\t\ticon: 'tool-eraser',\n\t\t\t\tkbd: 'e',\n\t\t\t\tonSelect(source) {\n\t\t\t\t\teditor.setCurrentTool('eraser')\n\t\t\t\t\tonToolSelect(source, this)\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: 'draw',\n\t\t\t\tlabel: 'tool.draw',\n\t\t\t\ticon: 'tool-pencil',\n\t\t\t\tkbd: 'd,b,x',\n\t\t\t\tonSelect(source) {\n\t\t\t\t\teditor.setCurrentTool('draw')\n\t\t\t\t\tonToolSelect(source, this)\n\t\t\t\t},\n\t\t\t},\n\t\t\t...[...GeoShapeGeoStyle.values].map((geo) => ({\n\t\t\t\tid: geo,\n\t\t\t\tlabel: `tool.${geo}` as TLUiTranslationKey,\n\t\t\t\tmeta: {\n\t\t\t\t\tgeo,\n\t\t\t\t},\n\t\t\t\tkbd: geo === 'rectangle' ? 'r' : geo === 'ellipse' ? 'o' : undefined,\n\t\t\t\ticon: ('geo-' + geo) as TLUiIconType,\n\t\t\t\tonSelect(source: TLUiEventSource) {\n\t\t\t\t\teditor.run(() => {\n\t\t\t\t\t\teditor.setStyleForNextShapes(GeoShapeGeoStyle, geo)\n\t\t\t\t\t\teditor.setCurrentTool('geo')\n\t\t\t\t\t\tonToolSelect(source, this, `geo-${geo}`)\n\t\t\t\t\t})\n\t\t\t\t},\n\t\t\t\tonDragStart(source: TLUiEventSource, info: TLPointerEventInfo) {\n\t\t\t\t\tonDragFromToolbarToCreateShape(editor, info, {\n\t\t\t\t\t\tcreateShape: (id) =>\n\t\t\t\t\t\t\teditor.createShape({ id, type: 'geo', props: { w: 200, h: 200, geo } }),\n\t\t\t\t\t})\n\t\t\t\t\ttrackEvent('drag-tool', { source, id: 'geo' })\n\t\t\t\t},\n\t\t\t})),\n\t\t\t{\n\t\t\t\tid: 'arrow',\n\t\t\t\tlabel: 'tool.arrow',\n\t\t\t\ticon: 'tool-arrow',\n\t\t\t\tkbd: 'a',\n\t\t\t\tonSelect(source) {\n\t\t\t\t\teditor.setCurrentTool('arrow')\n\t\t\t\t\tonToolSelect(source, this)\n\t\t\t\t},\n\t\t\t\tonDragStart(source: TLUiEventSource, info: TLPointerEventInfo) {\n\t\t\t\t\tonDragFromToolbarToCreateShape(editor, info, {\n\t\t\t\t\t\tcreateShape: (id) =>\n\t\t\t\t\t\t\teditor.createShape({\n\t\t\t\t\t\t\t\tid,\n\t\t\t\t\t\t\t\ttype: 'arrow',\n\t\t\t\t\t\t\t\tprops: { start: { x: 0, y: 0 }, end: { x: 200, y: 0 } },\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t})\n\t\t\t\t\ttrackEvent('drag-tool', { source, id: 'arrow' })\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: 'line',\n\t\t\t\tlabel: 'tool.line',\n\t\t\t\ticon: 'tool-line',\n\t\t\t\tkbd: 'l',\n\t\t\t\tonSelect(source) {\n\t\t\t\t\teditor.setCurrentTool('line')\n\t\t\t\t\tonToolSelect(source, this)\n\t\t\t\t},\n\t\t\t\tonDragStart(source, info) {\n\t\t\t\t\tonDragFromToolbarToCreateShape(editor, info, {\n\t\t\t\t\t\tcreateShape: (id) => {\n\t\t\t\t\t\t\tconst [start, end] = getIndicesBetween(null, null, 2)\n\t\t\t\t\t\t\teditor.createShape<TLLineShape>({\n\t\t\t\t\t\t\t\tid,\n\t\t\t\t\t\t\t\ttype: 'line',\n\t\t\t\t\t\t\t\tprops: {\n\t\t\t\t\t\t\t\t\tpoints: {\n\t\t\t\t\t\t\t\t\t\t[start]: { id: start, index: start, x: 0, y: 200 },\n\t\t\t\t\t\t\t\t\t\t[end]: { id: end, index: end, x: 200, y: 0 },\n\t\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})\n\t\t\t\t\t\t},\n\t\t\t\t\t})\n\t\t\t\t\ttrackEvent('drag-tool', { source, id: 'line' })\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: 'frame',\n\t\t\t\tlabel: 'tool.frame',\n\t\t\t\ticon: 'tool-frame',\n\t\t\t\tkbd: 'f',\n\t\t\t\tonSelect(source) {\n\t\t\t\t\teditor.setCurrentTool('frame')\n\t\t\t\t\tonToolSelect(source, this)\n\t\t\t\t},\n\t\t\t\tonDragStart(source, info) {\n\t\t\t\t\tonDragFromToolbarToCreateShape(editor, info, {\n\t\t\t\t\t\tcreateShape: (id) => editor.createShape({ id, type: 'frame' }),\n\t\t\t\t\t})\n\t\t\t\t\ttrackEvent('drag-tool', { source, id: 'frame' })\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: 'text',\n\t\t\t\tlabel: 'tool.text',\n\t\t\t\ticon: 'tool-text',\n\t\t\t\tkbd: 't',\n\t\t\t\tonSelect(source) {\n\t\t\t\t\teditor.setCurrentTool('text')\n\t\t\t\t\tonToolSelect(source, this)\n\t\t\t\t},\n\t\t\t\tonDragStart(source, info) {\n\t\t\t\t\tonDragFromToolbarToCreateShape(editor, info, {\n\t\t\t\t\t\tcreateShape: (id) =>\n\t\t\t\t\t\t\teditor.createShape({ id, type: 'text', props: { richText: toRichText('Text') } }),\n\t\t\t\t\t\tonDragEnd: (id) => {\n\t\t\t\t\t\t\teditor.setEditingShape(id)\n\t\t\t\t\t\t\teditor.emit('select-all-text', { shapeId: id })\n\t\t\t\t\t\t},\n\t\t\t\t\t})\n\t\t\t\t\ttrackEvent('drag-tool', { source, id: 'text' })\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: 'asset',\n\t\t\t\tlabel: 'tool.media',\n\t\t\t\ticon: 'tool-media',\n\t\t\t\tkbd: 'cmd+u,ctrl+u',\n\t\t\t\tonSelect(source) {\n\t\t\t\t\thelpers.insertMedia()\n\t\t\t\t\tonToolSelect(source, this, 'media')\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: 'note',\n\t\t\t\tlabel: 'tool.note',\n\t\t\t\ticon: 'tool-note',\n\t\t\t\tkbd: 'n',\n\t\t\t\tonSelect(source) {\n\t\t\t\t\teditor.setCurrentTool('note')\n\t\t\t\t\tonToolSelect(source, this)\n\t\t\t\t},\n\t\t\t\tonDragStart(source, info) {\n\t\t\t\t\tonDragFromToolbarToCreateShape(editor, info, {\n\t\t\t\t\t\tcreateShape: (id) => editor.createShape({ id, type: 'note' }),\n\t\t\t\t\t\tonDragEnd: (id) => {\n\t\t\t\t\t\t\teditor.setEditingShape(id)\n\t\t\t\t\t\t\teditor.emit('select-all-text', { shapeId: id })\n\t\t\t\t\t\t},\n\t\t\t\t\t})\n\t\t\t\t\ttrackEvent('drag-tool', { source, id: 'note' })\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: 'laser',\n\t\t\t\tlabel: 'tool.laser',\n\t\t\t\treadonlyOk: true,\n\t\t\t\ticon: 'tool-laser',\n\t\t\t\tkbd: 'k',\n\t\t\t\tonSelect(source) {\n\t\t\t\t\teditor.setCurrentTool('laser')\n\t\t\t\t\tonToolSelect(source, this)\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: 'embed',\n\t\t\t\tlabel: 'tool.embed',\n\t\t\t\ticon: 'dot',\n\t\t\t\tonSelect(source) {\n\t\t\t\t\thelpers.addDialog({ component: EmbedDialog })\n\t\t\t\t\tonToolSelect(source, this)\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: 'highlight',\n\t\t\t\tlabel: 'tool.highlight',\n\t\t\t\ticon: 'tool-highlight',\n\t\t\t\t// TODO: pick a better shortcut\n\t\t\t\tkbd: 'shift+d',\n\t\t\t\tonSelect(source) {\n\t\t\t\t\teditor.setCurrentTool('highlight')\n\t\t\t\t\tonToolSelect(source, this)\n\t\t\t\t},\n\t\t\t},\n\t\t]\n\n\t\ttoolsArray.forEach((t) => (t.onSelect = t.onSelect.bind(t)))\n\n\t\tconst tools = Object.fromEntries(toolsArray.map((t) => [t.id, t]))\n\n\t\tif (overrides) {\n\t\t\treturn overrides(editor, tools, helpers)\n\t\t}\n\n\t\treturn tools\n\t}, [overrides, editor, helpers, onToolSelect, trackEvent])\n\n\treturn <ToolsContext.Provider value={tools}>{children}</ToolsContext.Provider>\n}\n\n/** @public */\nexport function useTools() {\n\tconst ctx = React.useContext(ToolsContext)\n\n\tif (!ctx) {\n\t\tthrow new Error('useTools must be used within a ToolProvider')\n\t}\n\n\treturn ctx\n}\n\n/**\n * Options for {@link onDragFromToolbarToCreateShape}.\n * @public\n */\nexport interface OnDragFromToolbarToCreateShapesOpts {\n\t/**\n\t * Create the shape being dragged. You don't need to worry about positioning it, as it'll be\n\t * immediately updated with the correct position.\n\t */\n\tcreateShape(id: TLShapeId): void\n\t/**\n\t * Called once the drag interaction has finished.\n\t */\n\tonDragEnd?(id: TLShapeId): void\n}\n\n/**\n * A helper method to use in {@link TLUiToolItem#onDragStart} to create a shape by dragging it from\n * the toolbar.\n * @public\n */\nexport function onDragFromToolbarToCreateShape(\n\teditor: Editor,\n\tinfo: TLPointerEventInfo,\n\topts: OnDragFromToolbarToCreateShapesOpts\n) {\n\tconst { x, y } = editor.inputs.currentPagePoint\n\n\tconst stoppingPoint = editor.markHistoryStoppingPoint('drag shape tool')\n\teditor.setCurrentTool('select.translating')\n\n\tconst id = createShapeId()\n\topts.createShape(id)\n\tconst shape = assertExists(editor.getShape(id), 'Shape not found')\n\n\tconst { w, h } = editor.getShapePageBounds(id)!\n\teditor.updateShape({ id, type: shape.type, x: x - w / 2, y: y - h / 2 })\n\teditor.select(id)\n\n\teditor.setCurrentTool('select.translating', {\n\t\t...info,\n\t\ttarget: 'shape',\n\t\tshape: editor.getShape(id),\n\t\tisCreating: true,\n\t\tcreatingMarkId: stoppingPoint,\n\t\tonCreate() {\n\t\t\teditor.setCurrentTool('select.idle')\n\t\t\teditor.select(id)\n\t\t\topts.onDragEnd?.(id)\n\t\t},\n\t})\n\n\teditor.getCurrentTool().setCurrentToolIdMask(shape.type)\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import {\n\tassertExists,\n\tcreateShapeId,\n\tEditor,\n\tGeoShapeGeoStyle,\n\tgetIndicesBetween,\n\tTLLineShape,\n\tTLPointerEventInfo,\n\tTLShapeId,\n\ttoRichText,\n\tuseMaybeEditor,\n} from '@tldraw/editor'\nimport * as React from 'react'\nimport { EmbedDialog } from '../components/EmbedDialog'\nimport { TLUiIconJsx } from '../components/primitives/TldrawUiIcon'\nimport { useA11y } from '../context/a11y'\nimport { TLUiEventSource, useUiEvents } from '../context/events'\nimport { TLUiIconType } from '../icon-types'\nimport { TLUiOverrideHelpers, useDefaultHelpers } from '../overrides'\nimport { TLUiTranslationKey } from './useTranslation/TLUiTranslationKey'\nimport { useTranslation } from './useTranslation/useTranslation'\n\n/** @public */\nexport interface TLUiToolItem<\n\tTranslationKey extends string = string,\n\tIconType extends string = string,\n> {\n\tid: string\n\tlabel: TranslationKey\n\tshortcutsLabel?: TranslationKey\n\ticon: IconType | TLUiIconJsx\n\tonSelect(source: TLUiEventSource): void\n\tonDragStart?(source: TLUiEventSource, info: TLPointerEventInfo): void\n\t/**\n\t * The keyboard shortcut for this tool. This is a string that can be a single key,\n\t * or a combination of keys.\n\t * For example, `cmd+z` or `cmd+shift+z` or `cmd+u,ctrl+u`, or just `v` or `a`.\n\t * We have backwards compatibility with the old system, where we used to use\n\t * symbols to denote cmd/alt/shift, using `!` for shift, `$` for cmd, and `?` for alt.\n\t */\n\tkbd?: string\n\treadonlyOk?: boolean\n\tmeta?: {\n\t\t[key: string]: any\n\t}\n}\n\n/** @public */\nexport type TLUiToolsContextType = Record<string, TLUiToolItem>\n\n/** @internal */\nexport const ToolsContext = React.createContext<null | TLUiToolsContextType>(null)\n\n/** @public */\nexport interface TLUiToolsProviderProps {\n\toverrides?(\n\t\teditor: Editor,\n\t\ttools: TLUiToolsContextType,\n\t\thelpers: Partial<TLUiOverrideHelpers>\n\t): TLUiToolsContextType\n\tchildren: React.ReactNode\n}\n\n/** @internal */\nexport function ToolsProvider({ overrides, children }: TLUiToolsProviderProps) {\n\tconst editor = useMaybeEditor()\n\tconst trackEvent = useUiEvents()\n\n\tconst a11y = useA11y()\n\tconst msg = useTranslation()\n\tconst helpers = useDefaultHelpers()\n\n\tconst onToolSelect = React.useCallback(\n\t\t(\n\t\t\tsource: TLUiEventSource,\n\t\t\ttool: TLUiToolItem<TLUiTranslationKey, TLUiIconType>,\n\t\t\tid?: string\n\t\t) => {\n\t\t\ta11y.announce({ msg: msg(tool.label) })\n\t\t\ttrackEvent('select-tool', { source, id: id ?? tool.id })\n\t\t},\n\t\t[a11y, msg, trackEvent]\n\t)\n\n\tconst tools = React.useMemo<TLUiToolsContextType>(() => {\n\t\tif (!editor) return {}\n\t\tconst toolsArray: TLUiToolItem<TLUiTranslationKey, TLUiIconType>[] = [\n\t\t\t{\n\t\t\t\tid: 'select',\n\t\t\t\tlabel: 'tool.select',\n\t\t\t\ticon: 'tool-pointer',\n\t\t\t\tkbd: 'v',\n\t\t\t\treadonlyOk: true,\n\t\t\t\tonSelect(source) {\n\t\t\t\t\tif (editor.isIn('select')) {\n\t\t\t\t\t\t// There's a quirk of select mode, where editing a shape is a sub-state of select.\n\t\t\t\t\t\t// Because the text tool can be locked/sticky, we need to make sure we exit the\n\t\t\t\t\t\t// text tool.\n\t\t\t\t\t\t//\n\t\t\t\t\t\t// psst, if you're changing this code, also change the code\n\t\t\t\t\t\t// in strange-tools.test.ts! Sadly it's duplicated there.\n\t\t\t\t\t\tconst currentNode = editor.root.getCurrent()!\n\t\t\t\t\t\tcurrentNode.exit({}, currentNode.id)\n\t\t\t\t\t\tcurrentNode.enter({}, currentNode.id)\n\t\t\t\t\t}\n\t\t\t\t\teditor.setCurrentTool('select')\n\t\t\t\t\tonToolSelect(source, this)\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: 'hand',\n\t\t\t\tlabel: 'tool.hand',\n\t\t\t\ticon: 'tool-hand',\n\t\t\t\tkbd: 'h',\n\t\t\t\treadonlyOk: true,\n\t\t\t\tonSelect(source) {\n\t\t\t\t\teditor.setCurrentTool('hand')\n\t\t\t\t\tonToolSelect(source, this)\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: 'eraser',\n\t\t\t\tlabel: 'tool.eraser',\n\t\t\t\ticon: 'tool-eraser',\n\t\t\t\tkbd: 'e',\n\t\t\t\tonSelect(source) {\n\t\t\t\t\teditor.setCurrentTool('eraser')\n\t\t\t\t\tonToolSelect(source, this)\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: 'draw',\n\t\t\t\tlabel: 'tool.draw',\n\t\t\t\ticon: 'tool-pencil',\n\t\t\t\tkbd: 'd,b,x',\n\t\t\t\tonSelect(source) {\n\t\t\t\t\teditor.setCurrentTool('draw')\n\t\t\t\t\tonToolSelect(source, this)\n\t\t\t\t},\n\t\t\t},\n\t\t\t...[...GeoShapeGeoStyle.values].map((geo) => ({\n\t\t\t\tid: geo,\n\t\t\t\tlabel: `tool.${geo}` as TLUiTranslationKey,\n\t\t\t\tmeta: {\n\t\t\t\t\tgeo,\n\t\t\t\t},\n\t\t\t\tkbd: geo === 'rectangle' ? 'r' : geo === 'ellipse' ? 'o' : undefined,\n\t\t\t\ticon: ('geo-' + geo) as TLUiIconType,\n\t\t\t\tonSelect(source: TLUiEventSource) {\n\t\t\t\t\teditor.run(() => {\n\t\t\t\t\t\teditor.setStyleForNextShapes(GeoShapeGeoStyle, geo)\n\t\t\t\t\t\teditor.setCurrentTool('geo')\n\t\t\t\t\t\tonToolSelect(source, this, `geo-${geo}`)\n\t\t\t\t\t})\n\t\t\t\t},\n\t\t\t\tonDragStart(source: TLUiEventSource, info: TLPointerEventInfo) {\n\t\t\t\t\tonDragFromToolbarToCreateShape(editor, info, {\n\t\t\t\t\t\tcreateShape: (id) =>\n\t\t\t\t\t\t\teditor.createShape({ id, type: 'geo', props: { w: 200, h: 200, geo } }),\n\t\t\t\t\t})\n\t\t\t\t\ttrackEvent('drag-tool', { source, id: 'geo' })\n\t\t\t\t},\n\t\t\t})),\n\t\t\t{\n\t\t\t\tid: 'arrow',\n\t\t\t\tlabel: 'tool.arrow',\n\t\t\t\ticon: 'tool-arrow',\n\t\t\t\tkbd: 'a',\n\t\t\t\tonSelect(source) {\n\t\t\t\t\teditor.setCurrentTool('arrow')\n\t\t\t\t\tonToolSelect(source, this)\n\t\t\t\t},\n\t\t\t\tonDragStart(source: TLUiEventSource, info: TLPointerEventInfo) {\n\t\t\t\t\tonDragFromToolbarToCreateShape(editor, info, {\n\t\t\t\t\t\tcreateShape: (id) =>\n\t\t\t\t\t\t\teditor.createShape({\n\t\t\t\t\t\t\t\tid,\n\t\t\t\t\t\t\t\ttype: 'arrow',\n\t\t\t\t\t\t\t\tprops: { start: { x: 0, y: 200 }, end: { x: 200, y: 0 } },\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t})\n\t\t\t\t\ttrackEvent('drag-tool', { source, id: 'arrow' })\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: 'line',\n\t\t\t\tlabel: 'tool.line',\n\t\t\t\ticon: 'tool-line',\n\t\t\t\tkbd: 'l',\n\t\t\t\tonSelect(source) {\n\t\t\t\t\teditor.setCurrentTool('line')\n\t\t\t\t\tonToolSelect(source, this)\n\t\t\t\t},\n\t\t\t\tonDragStart(source, info) {\n\t\t\t\t\tonDragFromToolbarToCreateShape(editor, info, {\n\t\t\t\t\t\tcreateShape: (id) => {\n\t\t\t\t\t\t\tconst [start, end] = getIndicesBetween(null, null, 2)\n\t\t\t\t\t\t\teditor.createShape<TLLineShape>({\n\t\t\t\t\t\t\t\tid,\n\t\t\t\t\t\t\t\ttype: 'line',\n\t\t\t\t\t\t\t\tprops: {\n\t\t\t\t\t\t\t\t\tpoints: {\n\t\t\t\t\t\t\t\t\t\t[start]: { id: start, index: start, x: 0, y: 200 },\n\t\t\t\t\t\t\t\t\t\t[end]: { id: end, index: end, x: 200, y: 0 },\n\t\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})\n\t\t\t\t\t\t},\n\t\t\t\t\t})\n\t\t\t\t\ttrackEvent('drag-tool', { source, id: 'line' })\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: 'frame',\n\t\t\t\tlabel: 'tool.frame',\n\t\t\t\ticon: 'tool-frame',\n\t\t\t\tkbd: 'f',\n\t\t\t\tonSelect(source) {\n\t\t\t\t\teditor.setCurrentTool('frame')\n\t\t\t\t\tonToolSelect(source, this)\n\t\t\t\t},\n\t\t\t\tonDragStart(source, info) {\n\t\t\t\t\tonDragFromToolbarToCreateShape(editor, info, {\n\t\t\t\t\t\tcreateShape: (id) => editor.createShape({ id, type: 'frame' }),\n\t\t\t\t\t})\n\t\t\t\t\ttrackEvent('drag-tool', { source, id: 'frame' })\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: 'text',\n\t\t\t\tlabel: 'tool.text',\n\t\t\t\ticon: 'tool-text',\n\t\t\t\tkbd: 't',\n\t\t\t\tonSelect(source) {\n\t\t\t\t\teditor.setCurrentTool('text')\n\t\t\t\t\tonToolSelect(source, this)\n\t\t\t\t},\n\t\t\t\tonDragStart(source, info) {\n\t\t\t\t\tonDragFromToolbarToCreateShape(editor, info, {\n\t\t\t\t\t\tcreateShape: (id) =>\n\t\t\t\t\t\t\teditor.createShape({ id, type: 'text', props: { richText: toRichText('Text') } }),\n\t\t\t\t\t\tonDragEnd: (id) => {\n\t\t\t\t\t\t\teditor.setEditingShape(id)\n\t\t\t\t\t\t\teditor.emit('select-all-text', { shapeId: id })\n\t\t\t\t\t\t},\n\t\t\t\t\t})\n\t\t\t\t\ttrackEvent('drag-tool', { source, id: 'text' })\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: 'asset',\n\t\t\t\tlabel: 'tool.media',\n\t\t\t\ticon: 'tool-media',\n\t\t\t\tkbd: 'cmd+u,ctrl+u',\n\t\t\t\tonSelect(source) {\n\t\t\t\t\thelpers.insertMedia()\n\t\t\t\t\tonToolSelect(source, this, 'media')\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: 'note',\n\t\t\t\tlabel: 'tool.note',\n\t\t\t\ticon: 'tool-note',\n\t\t\t\tkbd: 'n',\n\t\t\t\tonSelect(source) {\n\t\t\t\t\teditor.setCurrentTool('note')\n\t\t\t\t\tonToolSelect(source, this)\n\t\t\t\t},\n\t\t\t\tonDragStart(source, info) {\n\t\t\t\t\tonDragFromToolbarToCreateShape(editor, info, {\n\t\t\t\t\t\tcreateShape: (id) => editor.createShape({ id, type: 'note' }),\n\t\t\t\t\t\tonDragEnd: (id) => {\n\t\t\t\t\t\t\teditor.setEditingShape(id)\n\t\t\t\t\t\t\teditor.emit('select-all-text', { shapeId: id })\n\t\t\t\t\t\t},\n\t\t\t\t\t})\n\t\t\t\t\ttrackEvent('drag-tool', { source, id: 'note' })\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: 'laser',\n\t\t\t\tlabel: 'tool.laser',\n\t\t\t\treadonlyOk: true,\n\t\t\t\ticon: 'tool-laser',\n\t\t\t\tkbd: 'k',\n\t\t\t\tonSelect(source) {\n\t\t\t\t\teditor.setCurrentTool('laser')\n\t\t\t\t\tonToolSelect(source, this)\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: 'embed',\n\t\t\t\tlabel: 'tool.embed',\n\t\t\t\ticon: 'dot',\n\t\t\t\tonSelect(source) {\n\t\t\t\t\thelpers.addDialog({ component: EmbedDialog })\n\t\t\t\t\tonToolSelect(source, this)\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: 'highlight',\n\t\t\t\tlabel: 'tool.highlight',\n\t\t\t\ticon: 'tool-highlight',\n\t\t\t\t// TODO: pick a better shortcut\n\t\t\t\tkbd: 'shift+d',\n\t\t\t\tonSelect(source) {\n\t\t\t\t\teditor.setCurrentTool('highlight')\n\t\t\t\t\tonToolSelect(source, this)\n\t\t\t\t},\n\t\t\t},\n\t\t]\n\n\t\ttoolsArray.forEach((t) => (t.onSelect = t.onSelect.bind(t)))\n\n\t\tconst tools = Object.fromEntries(toolsArray.map((t) => [t.id, t]))\n\n\t\tif (overrides) {\n\t\t\treturn overrides(editor, tools, helpers)\n\t\t}\n\n\t\treturn tools\n\t}, [overrides, editor, helpers, onToolSelect, trackEvent])\n\n\treturn <ToolsContext.Provider value={tools}>{children}</ToolsContext.Provider>\n}\n\n/** @public */\nexport function useTools() {\n\tconst ctx = React.useContext(ToolsContext)\n\n\tif (!ctx) {\n\t\tthrow new Error('useTools must be used within a ToolProvider')\n\t}\n\n\treturn ctx\n}\n\n/**\n * Options for {@link onDragFromToolbarToCreateShape}.\n * @public\n */\nexport interface OnDragFromToolbarToCreateShapesOpts {\n\t/**\n\t * Create the shape being dragged. You don't need to worry about positioning it, as it'll be\n\t * immediately updated with the correct position.\n\t */\n\tcreateShape(id: TLShapeId): void\n\t/**\n\t * Called once the drag interaction has finished.\n\t */\n\tonDragEnd?(id: TLShapeId): void\n}\n\n/**\n * A helper method to use in {@link TLUiToolItem#onDragStart} to create a shape by dragging it from\n * the toolbar.\n * @public\n */\nexport function onDragFromToolbarToCreateShape(\n\teditor: Editor,\n\tinfo: TLPointerEventInfo,\n\topts: OnDragFromToolbarToCreateShapesOpts\n) {\n\tconst { x, y } = editor.inputs.currentPagePoint\n\n\tconst stoppingPoint = editor.markHistoryStoppingPoint('drag shape tool')\n\teditor.setCurrentTool('select.translating')\n\n\tconst id = createShapeId()\n\topts.createShape(id)\n\tconst shape = assertExists(editor.getShape(id), 'Shape not found')\n\n\tconst { w, h } = editor.getShapePageBounds(id)!\n\teditor.updateShape({ id, type: shape.type, x: x - w / 2, y: y - h / 2 })\n\teditor.select(id)\n\n\teditor.setCurrentTool('select.translating', {\n\t\t...info,\n\t\ttarget: 'shape',\n\t\tshape: editor.getShape(id),\n\t\tisCreating: true,\n\t\tcreatingMarkId: stoppingPoint,\n\t\tonCreate() {\n\t\t\teditor.setCurrentTool('select.idle')\n\t\t\teditor.select(id)\n\t\t\topts.onDragEnd?.(id)\n\t\t},\n\t})\n\n\teditor.getCurrentTool().setCurrentToolIdMask(shape.type)\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["tools"]
7
7
  }
@@ -22,10 +22,10 @@ __export(version_exports, {
22
22
  version: () => version
23
23
  });
24
24
  module.exports = __toCommonJS(version_exports);
25
- const version = "3.16.0-canary.66da2a5c5813";
25
+ const version = "3.16.0-canary.6c77a180e58d";
26
26
  const publishDates = {
27
27
  major: "2024-09-13T14:36:29.063Z",
28
- minor: "2025-09-02T14:51:14.201Z",
29
- patch: "2025-09-02T14:51:14.201Z"
28
+ minor: "2025-09-10T11:52:19.209Z",
29
+ patch: "2025-09-10T11:52:19.209Z"
30
30
  };
31
31
  //# sourceMappingURL=version.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/lib/ui/version.ts"],
4
- "sourcesContent": ["// This file is automatically generated by internal/scripts/refresh-assets.ts.\n// Do not edit manually. Or do, I'm a comment, not a cop.\n\nexport const version = '3.16.0-canary.66da2a5c5813'\nexport const publishDates = {\n\tmajor: '2024-09-13T14:36:29.063Z',\n\tminor: '2025-09-02T14:51:14.201Z',\n\tpatch: '2025-09-02T14:51:14.201Z',\n}\n"],
4
+ "sourcesContent": ["// This file is automatically generated by internal/scripts/refresh-assets.ts.\n// Do not edit manually. Or do, I'm a comment, not a cop.\n\nexport const version = '3.16.0-canary.6c77a180e58d'\nexport const publishDates = {\n\tmajor: '2024-09-13T14:36:29.063Z',\n\tminor: '2025-09-10T11:52:19.209Z',\n\tpatch: '2025-09-10T11:52:19.209Z',\n}\n"],
5
5
  "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAGO,MAAM,UAAU;AAChB,MAAM,eAAe;AAAA,EAC3B,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AACR;",
6
6
  "names": []
7
7
  }
@@ -524,7 +524,7 @@ import {
524
524
  } from "./lib/utils/tldr/file.mjs";
525
525
  registerTldrawLibraryVersion(
526
526
  "tldraw",
527
- "3.16.0-canary.66da2a5c5813",
527
+ "3.16.0-canary.6c77a180e58d",
528
528
  "esm"
529
529
  );
530
530
  export {
@@ -165,6 +165,12 @@ function getArrowLabelRange(editor, shape, info) {
165
165
  return { start: startRelative, end: endRelative, dbg };
166
166
  }
167
167
  function getArrowLabelPosition(editor, shape) {
168
+ const isEditing = editor.getEditingShapeId() === shape.id;
169
+ if (!isEditing && isEmptyRichText(shape.props.richText)) {
170
+ const bodyGeom2 = getArrowBodyGeometry(editor, shape);
171
+ const labelCenter2 = bodyGeom2.interpolateAlongEdge(0.5);
172
+ return { box: Box.FromCenter(labelCenter2, new Vec(0, 0)), debugGeom: [] };
173
+ }
168
174
  const debugGeom = [];
169
175
  const info = getArrowInfo(editor, shape);
170
176
  const arrowheadInfo = {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/lib/shapes/arrow/arrowLabel.ts"],
4
- "sourcesContent": ["import {\n\tArc2d,\n\tBox,\n\tCircle2d,\n\tEdge2d,\n\tEditor,\n\tGeometry2d,\n\tGroup2d,\n\tPolygon2d,\n\tPolyline2d,\n\tTLArrowShape,\n\tTLShape,\n\tVec,\n\tVecLike,\n\tclamp,\n\tcreateComputedCache,\n\texhaustiveSwitchError,\n\tgetChangedKeys,\n\tpointInPolygon,\n\ttoRichText,\n} from '@tldraw/editor'\nimport { isEmptyRichText, renderHtmlFromRichTextForMeasurement } from '../../utils/text/richText'\nimport {\n\tARROW_LABEL_FONT_SIZES,\n\tARROW_LABEL_PADDING,\n\tFONT_FAMILIES,\n\tLABEL_TO_ARROW_PADDING,\n\tSTROKE_SIZES,\n\tTEXT_PROPS,\n} from '../shared/default-shape-constants'\nimport { TLArrowInfo } from './arrow-types'\nimport { getArrowInfo } from './shared'\n\nexport function getArrowBodyGeometry(editor: Editor, shape: TLArrowShape) {\n\tconst info = getArrowInfo(editor, shape)!\n\tswitch (info.type) {\n\t\tcase 'straight':\n\t\t\treturn new Edge2d({\n\t\t\t\tstart: Vec.From(info.start.point),\n\t\t\t\tend: Vec.From(info.end.point),\n\t\t\t})\n\t\tcase 'arc':\n\t\t\treturn new Arc2d({\n\t\t\t\tcenter: Vec.Cast(info.handleArc.center),\n\t\t\t\tstart: Vec.Cast(info.start.point),\n\t\t\t\tend: Vec.Cast(info.end.point),\n\t\t\t\tsweepFlag: info.bodyArc.sweepFlag,\n\t\t\t\tlargeArcFlag: info.bodyArc.largeArcFlag,\n\t\t\t})\n\t\tcase 'elbow':\n\t\t\treturn new Polyline2d({ points: info.route.points })\n\t\tdefault:\n\t\t\texhaustiveSwitchError(info, 'type')\n\t}\n}\n\nconst labelSizeCache = createComputedCache(\n\t'arrow label size',\n\t(editor: Editor, shape: TLArrowShape) => {\n\t\teditor.fonts.trackFontsForShape(shape)\n\t\tlet width = 0\n\t\tlet height = 0\n\n\t\tconst bodyGeom = getArrowBodyGeometry(editor, shape)\n\t\t// We use 'i' as a default label to measure against as a minimum width.\n\t\tconst isEmpty = isEmptyRichText(shape.props.richText)\n\t\tconst html = renderHtmlFromRichTextForMeasurement(\n\t\t\teditor,\n\t\t\tisEmpty ? toRichText('i') : shape.props.richText\n\t\t)\n\n\t\tconst bodyBounds = bodyGeom.bounds\n\n\t\tconst fontSize = getArrowLabelFontSize(shape)\n\n\t\t// First we measure the text with no constraints\n\t\tconst { w, h } = editor.textMeasure.measureHtml(html, {\n\t\t\t...TEXT_PROPS,\n\t\t\tfontFamily: FONT_FAMILIES[shape.props.font],\n\t\t\tfontSize,\n\t\t\tmaxWidth: null,\n\t\t})\n\n\t\twidth = w\n\t\theight = h\n\n\t\tlet shouldSquish = false\n\n\t\t// If the text is wider than the body, we need to squish it\n\t\tconst info = getArrowInfo(editor, shape)!\n\t\tconst labelToArrowPadding = getLabelToArrowPadding(shape)\n\t\tconst margin =\n\t\t\tinfo.type === 'elbow'\n\t\t\t\t? Math.max(info.elbow.A.arrowheadOffset + labelToArrowPadding, 32) +\n\t\t\t\t\tMath.max(info.elbow.B.arrowheadOffset + labelToArrowPadding, 32)\n\t\t\t\t: 64\n\n\t\tif (bodyBounds.width > bodyBounds.height) {\n\t\t\twidth = Math.max(Math.min(w, margin), Math.min(bodyBounds.width - margin, w))\n\t\t\tshouldSquish = true\n\t\t} else if (width > 16 * fontSize) {\n\t\t\twidth = 16 * fontSize\n\t\t\tshouldSquish = true\n\t\t}\n\n\t\tif (shouldSquish) {\n\t\t\tconst { w: squishedWidth, h: squishedHeight } = editor.textMeasure.measureHtml(html, {\n\t\t\t\t...TEXT_PROPS,\n\t\t\t\tfontFamily: FONT_FAMILIES[shape.props.font],\n\t\t\t\tfontSize,\n\t\t\t\tmaxWidth: width,\n\t\t\t})\n\n\t\t\twidth = squishedWidth\n\t\t\theight = squishedHeight\n\t\t}\n\n\t\treturn new Vec(width, height).addScalar(ARROW_LABEL_PADDING * 2 * shape.props.scale)\n\t},\n\t{\n\t\tareRecordsEqual: (a, b) => {\n\t\t\tif (a.props === b.props) return true\n\n\t\t\t// If the only thing that has changed is the label position, we can skip recalculating the size\n\t\t\tconst changedKeys = getChangedKeys(a.props, b.props)\n\t\t\treturn changedKeys.length === 1 && changedKeys[0] === 'labelPosition'\n\t\t},\n\t}\n)\n\nfunction getArrowLabelSize(editor: Editor, shape: TLArrowShape) {\n\treturn labelSizeCache.get(editor, shape.id) ?? new Vec(0, 0)\n}\n\nfunction getLabelToArrowPadding(shape: TLArrowShape) {\n\tconst strokeWidth = STROKE_SIZES[shape.props.size]\n\tconst labelToArrowPadding =\n\t\t(LABEL_TO_ARROW_PADDING +\n\t\t\t(strokeWidth - STROKE_SIZES.s) * 2 +\n\t\t\t(strokeWidth === STROKE_SIZES.xl ? 20 : 0)) *\n\t\tshape.props.scale\n\n\treturn labelToArrowPadding\n}\n\n/**\n * Return the range of possible label positions for an arrow. The full possible range is 0 to 1, but\n * as the label itself takes up space the usable range is smaller.\n */\nfunction getArrowLabelRange(editor: Editor, shape: TLArrowShape, info: TLArrowInfo) {\n\tconst bodyGeom = getArrowBodyGeometry(editor, shape)\n\tconst dbgPoints: VecLike[] = []\n\tconst dbg: Geometry2d[] = [new Group2d({ children: [bodyGeom], debugColor: 'lime' })]\n\n\tconst labelSize = getArrowLabelSize(editor, shape)\n\tconst labelToArrowPadding = getLabelToArrowPadding(shape)\n\tconst paddingRelative = labelToArrowPadding / bodyGeom.length\n\n\t// we can calculate the range by sticking the center of the label at the very start/end of the\n\t// arrow, and seeing where the label intersects with the arrow. Then, if we move the label's\n\t// center to that point, that'll be the start/end of the range.\n\n\tlet startBox, endBox\n\tif (info.type === 'elbow') {\n\t\t// for elbow arrows, because they have multiple segments but are always axis-aligned, we can use\n\t\t// an expanded box. This helps keep the box from partially covering the first segment when it's\n\t\t// very small.\n\t\tdbgPoints.push(info.start.point, info.end.point)\n\t\tstartBox = Box.FromCenter(info.start.point, labelSize).expandBy(labelToArrowPadding)\n\t\tendBox = Box.FromCenter(info.end.point, labelSize).expandBy(labelToArrowPadding)\n\t} else {\n\t\t// for other arrows, we move along the arrow by the padding amount to find the start/end points\n\t\tconst startPoint = bodyGeom.interpolateAlongEdge(paddingRelative)\n\t\tconst endPoint = bodyGeom.interpolateAlongEdge(1 - paddingRelative)\n\t\tdbgPoints.push(startPoint, endPoint)\n\t\tstartBox = Box.FromCenter(startPoint, labelSize)\n\t\tendBox = Box.FromCenter(endPoint, labelSize)\n\t}\n\tconst startIntersections = bodyGeom.intersectPolygon(startBox.corners)\n\tconst endIntersections = bodyGeom.intersectPolygon(endBox.corners)\n\n\tconst startConstrained = furthest(info.start.point, startIntersections)\n\tconst endConstrained = furthest(info.end.point, endIntersections)\n\n\tlet startRelative = startConstrained ? bodyGeom.uninterpolateAlongEdge(startConstrained) : 0.5\n\tlet endRelative = endConstrained ? bodyGeom.uninterpolateAlongEdge(endConstrained) : 0.5\n\n\tif (startRelative > endRelative) {\n\t\tstartRelative = 0.5\n\t\tendRelative = 0.5\n\t}\n\n\tfor (const pt of [...startIntersections, ...endIntersections, ...dbgPoints]) {\n\t\tdbg.push(\n\t\t\tnew Circle2d({\n\t\t\t\tx: pt.x - 3,\n\t\t\t\ty: pt.y - 3,\n\t\t\t\tradius: 3,\n\t\t\t\tisFilled: false,\n\t\t\t\tdebugColor: 'magenta',\n\t\t\t\tignore: true,\n\t\t\t})\n\t\t)\n\t}\n\tdbg.push(\n\t\tnew Polygon2d({\n\t\t\tpoints: startBox.corners,\n\t\t\tdebugColor: 'lime',\n\t\t\tisFilled: false,\n\t\t\tignore: true,\n\t\t}),\n\t\tnew Polygon2d({\n\t\t\tpoints: endBox.corners,\n\t\t\tdebugColor: 'lime',\n\t\t\tisFilled: false,\n\t\t\tignore: true,\n\t\t})\n\t)\n\n\treturn { start: startRelative, end: endRelative, dbg }\n}\n\ninterface ArrowheadInfo {\n\thasStartBinding: boolean\n\thasEndBinding: boolean\n\thasStartArrowhead: boolean\n\thasEndArrowhead: boolean\n}\nexport function getArrowLabelPosition(editor: Editor, shape: TLArrowShape) {\n\tconst debugGeom: Geometry2d[] = []\n\tconst info = getArrowInfo(editor, shape)!\n\n\tconst arrowheadInfo: ArrowheadInfo = {\n\t\thasStartBinding: !!info.bindings.start,\n\t\thasEndBinding: !!info.bindings.end,\n\t\thasStartArrowhead: info.start.arrowhead !== 'none',\n\t\thasEndArrowhead: info.end.arrowhead !== 'none',\n\t}\n\n\tconst range = getArrowLabelRange(editor, shape, info)\n\tif (range.dbg) debugGeom.push(...range.dbg)\n\n\tconst clampedPosition = getClampedPosition(shape, range, arrowheadInfo)\n\tconst bodyGeom = getArrowBodyGeometry(editor, shape)\n\tconst labelCenter = bodyGeom.interpolateAlongEdge(clampedPosition)\n\tconst labelSize = getArrowLabelSize(editor, shape)\n\n\treturn { box: Box.FromCenter(labelCenter, labelSize), debugGeom }\n}\n\nfunction getClampedPosition(\n\tshape: TLArrowShape,\n\trange: { start: number; end: number },\n\tarrowheadInfo: ArrowheadInfo\n) {\n\tconst { hasEndArrowhead, hasEndBinding, hasStartBinding, hasStartArrowhead } = arrowheadInfo\n\tconst clampedPosition = clamp(\n\t\tshape.props.labelPosition,\n\t\thasStartArrowhead || hasStartBinding ? range.start : 0,\n\t\thasEndArrowhead || hasEndBinding ? range.end : 1\n\t)\n\n\treturn clampedPosition\n}\n\nfunction furthest(from: VecLike, candidates: VecLike[]): VecLike | null {\n\tlet furthest: VecLike | null = null\n\tlet furthestDist = -Infinity\n\n\tfor (const candidate of candidates) {\n\t\tconst dist = Vec.Dist2(from, candidate)\n\t\tif (dist > furthestDist) {\n\t\t\tfurthest = candidate\n\t\t\tfurthestDist = dist\n\t\t}\n\t}\n\n\treturn furthest\n}\n\nexport function getArrowLabelFontSize(shape: TLArrowShape) {\n\treturn ARROW_LABEL_FONT_SIZES[shape.props.size] * shape.props.scale\n}\n\nexport function getArrowLabelDefaultPosition(editor: Editor, shape: TLArrowShape) {\n\tconst info = getArrowInfo(editor, shape)!\n\tswitch (info.type) {\n\t\tcase 'straight':\n\t\tcase 'arc':\n\t\t\treturn 0.5\n\t\tcase 'elbow': {\n\t\t\tconst midpointHandle = info.route.midpointHandle\n\t\t\tconst bodyGeom = getArrowBodyGeometry(editor, shape)\n\t\t\tif (midpointHandle && bodyGeom) {\n\t\t\t\treturn bodyGeom.uninterpolateAlongEdge(midpointHandle.point)\n\t\t\t}\n\t\t\treturn 0.5\n\t\t}\n\t\tdefault:\n\t\t\texhaustiveSwitchError(info, 'type')\n\t}\n}\n\n/** @internal */\nexport function isOverArrowLabel(editor: Editor, shape: TLShape) {\n\tif (!editor.isShapeOfType<TLArrowShape>(shape, 'arrow')) return false\n\n\tconst pointInShapeSpace = editor.getPointInShapeSpace(shape, editor.inputs.currentPagePoint)\n\t// How should we handle multiple labels? Do shapes ever have multiple labels?\n\tconst labelGeometry = editor.getShapeGeometry<Group2d>(shape).children[1]\n\t// Knowing what we know about arrows... if the shape has no text in its label,\n\t// then the label geometry should not be there.\n\treturn labelGeometry && pointInPolygon(pointInShapeSpace, labelGeometry.vertices)\n}\n"],
5
- "mappings": "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",
6
- "names": ["furthest"]
4
+ "sourcesContent": ["import {\n\tArc2d,\n\tBox,\n\tCircle2d,\n\tEdge2d,\n\tEditor,\n\tGeometry2d,\n\tGroup2d,\n\tPolygon2d,\n\tPolyline2d,\n\tTLArrowShape,\n\tTLShape,\n\tVec,\n\tVecLike,\n\tclamp,\n\tcreateComputedCache,\n\texhaustiveSwitchError,\n\tgetChangedKeys,\n\tpointInPolygon,\n\ttoRichText,\n} from '@tldraw/editor'\nimport { isEmptyRichText, renderHtmlFromRichTextForMeasurement } from '../../utils/text/richText'\nimport {\n\tARROW_LABEL_FONT_SIZES,\n\tARROW_LABEL_PADDING,\n\tFONT_FAMILIES,\n\tLABEL_TO_ARROW_PADDING,\n\tSTROKE_SIZES,\n\tTEXT_PROPS,\n} from '../shared/default-shape-constants'\nimport { TLArrowInfo } from './arrow-types'\nimport { getArrowInfo } from './shared'\n\nexport function getArrowBodyGeometry(editor: Editor, shape: TLArrowShape) {\n\tconst info = getArrowInfo(editor, shape)!\n\tswitch (info.type) {\n\t\tcase 'straight':\n\t\t\treturn new Edge2d({\n\t\t\t\tstart: Vec.From(info.start.point),\n\t\t\t\tend: Vec.From(info.end.point),\n\t\t\t})\n\t\tcase 'arc':\n\t\t\treturn new Arc2d({\n\t\t\t\tcenter: Vec.Cast(info.handleArc.center),\n\t\t\t\tstart: Vec.Cast(info.start.point),\n\t\t\t\tend: Vec.Cast(info.end.point),\n\t\t\t\tsweepFlag: info.bodyArc.sweepFlag,\n\t\t\t\tlargeArcFlag: info.bodyArc.largeArcFlag,\n\t\t\t})\n\t\tcase 'elbow':\n\t\t\treturn new Polyline2d({ points: info.route.points })\n\t\tdefault:\n\t\t\texhaustiveSwitchError(info, 'type')\n\t}\n}\n\nconst labelSizeCache = createComputedCache(\n\t'arrow label size',\n\t(editor: Editor, shape: TLArrowShape) => {\n\t\teditor.fonts.trackFontsForShape(shape)\n\t\tlet width = 0\n\t\tlet height = 0\n\n\t\tconst bodyGeom = getArrowBodyGeometry(editor, shape)\n\t\t// We use 'i' as a default label to measure against as a minimum width.\n\t\tconst isEmpty = isEmptyRichText(shape.props.richText)\n\t\tconst html = renderHtmlFromRichTextForMeasurement(\n\t\t\teditor,\n\t\t\tisEmpty ? toRichText('i') : shape.props.richText\n\t\t)\n\n\t\tconst bodyBounds = bodyGeom.bounds\n\n\t\tconst fontSize = getArrowLabelFontSize(shape)\n\n\t\t// First we measure the text with no constraints\n\t\tconst { w, h } = editor.textMeasure.measureHtml(html, {\n\t\t\t...TEXT_PROPS,\n\t\t\tfontFamily: FONT_FAMILIES[shape.props.font],\n\t\t\tfontSize,\n\t\t\tmaxWidth: null,\n\t\t})\n\n\t\twidth = w\n\t\theight = h\n\n\t\tlet shouldSquish = false\n\n\t\t// If the text is wider than the body, we need to squish it\n\t\tconst info = getArrowInfo(editor, shape)!\n\t\tconst labelToArrowPadding = getLabelToArrowPadding(shape)\n\t\tconst margin =\n\t\t\tinfo.type === 'elbow'\n\t\t\t\t? Math.max(info.elbow.A.arrowheadOffset + labelToArrowPadding, 32) +\n\t\t\t\t\tMath.max(info.elbow.B.arrowheadOffset + labelToArrowPadding, 32)\n\t\t\t\t: 64\n\n\t\tif (bodyBounds.width > bodyBounds.height) {\n\t\t\twidth = Math.max(Math.min(w, margin), Math.min(bodyBounds.width - margin, w))\n\t\t\tshouldSquish = true\n\t\t} else if (width > 16 * fontSize) {\n\t\t\twidth = 16 * fontSize\n\t\t\tshouldSquish = true\n\t\t}\n\n\t\tif (shouldSquish) {\n\t\t\tconst { w: squishedWidth, h: squishedHeight } = editor.textMeasure.measureHtml(html, {\n\t\t\t\t...TEXT_PROPS,\n\t\t\t\tfontFamily: FONT_FAMILIES[shape.props.font],\n\t\t\t\tfontSize,\n\t\t\t\tmaxWidth: width,\n\t\t\t})\n\n\t\t\twidth = squishedWidth\n\t\t\theight = squishedHeight\n\t\t}\n\n\t\treturn new Vec(width, height).addScalar(ARROW_LABEL_PADDING * 2 * shape.props.scale)\n\t},\n\t{\n\t\tareRecordsEqual: (a, b) => {\n\t\t\tif (a.props === b.props) return true\n\n\t\t\t// If the only thing that has changed is the label position, we can skip recalculating the size\n\t\t\tconst changedKeys = getChangedKeys(a.props, b.props)\n\t\t\treturn changedKeys.length === 1 && changedKeys[0] === 'labelPosition'\n\t\t},\n\t}\n)\n\nfunction getArrowLabelSize(editor: Editor, shape: TLArrowShape) {\n\treturn labelSizeCache.get(editor, shape.id) ?? new Vec(0, 0)\n}\n\nfunction getLabelToArrowPadding(shape: TLArrowShape) {\n\tconst strokeWidth = STROKE_SIZES[shape.props.size]\n\tconst labelToArrowPadding =\n\t\t(LABEL_TO_ARROW_PADDING +\n\t\t\t(strokeWidth - STROKE_SIZES.s) * 2 +\n\t\t\t(strokeWidth === STROKE_SIZES.xl ? 20 : 0)) *\n\t\tshape.props.scale\n\n\treturn labelToArrowPadding\n}\n\n/**\n * Return the range of possible label positions for an arrow. The full possible range is 0 to 1, but\n * as the label itself takes up space the usable range is smaller.\n */\nfunction getArrowLabelRange(editor: Editor, shape: TLArrowShape, info: TLArrowInfo) {\n\tconst bodyGeom = getArrowBodyGeometry(editor, shape)\n\tconst dbgPoints: VecLike[] = []\n\tconst dbg: Geometry2d[] = [new Group2d({ children: [bodyGeom], debugColor: 'lime' })]\n\n\tconst labelSize = getArrowLabelSize(editor, shape)\n\tconst labelToArrowPadding = getLabelToArrowPadding(shape)\n\tconst paddingRelative = labelToArrowPadding / bodyGeom.length\n\n\t// we can calculate the range by sticking the center of the label at the very start/end of the\n\t// arrow, and seeing where the label intersects with the arrow. Then, if we move the label's\n\t// center to that point, that'll be the start/end of the range.\n\n\tlet startBox, endBox\n\tif (info.type === 'elbow') {\n\t\t// for elbow arrows, because they have multiple segments but are always axis-aligned, we can use\n\t\t// an expanded box. This helps keep the box from partially covering the first segment when it's\n\t\t// very small.\n\t\tdbgPoints.push(info.start.point, info.end.point)\n\t\tstartBox = Box.FromCenter(info.start.point, labelSize).expandBy(labelToArrowPadding)\n\t\tendBox = Box.FromCenter(info.end.point, labelSize).expandBy(labelToArrowPadding)\n\t} else {\n\t\t// for other arrows, we move along the arrow by the padding amount to find the start/end points\n\t\tconst startPoint = bodyGeom.interpolateAlongEdge(paddingRelative)\n\t\tconst endPoint = bodyGeom.interpolateAlongEdge(1 - paddingRelative)\n\t\tdbgPoints.push(startPoint, endPoint)\n\t\tstartBox = Box.FromCenter(startPoint, labelSize)\n\t\tendBox = Box.FromCenter(endPoint, labelSize)\n\t}\n\tconst startIntersections = bodyGeom.intersectPolygon(startBox.corners)\n\tconst endIntersections = bodyGeom.intersectPolygon(endBox.corners)\n\n\tconst startConstrained = furthest(info.start.point, startIntersections)\n\tconst endConstrained = furthest(info.end.point, endIntersections)\n\n\tlet startRelative = startConstrained ? bodyGeom.uninterpolateAlongEdge(startConstrained) : 0.5\n\tlet endRelative = endConstrained ? bodyGeom.uninterpolateAlongEdge(endConstrained) : 0.5\n\n\tif (startRelative > endRelative) {\n\t\tstartRelative = 0.5\n\t\tendRelative = 0.5\n\t}\n\n\tfor (const pt of [...startIntersections, ...endIntersections, ...dbgPoints]) {\n\t\tdbg.push(\n\t\t\tnew Circle2d({\n\t\t\t\tx: pt.x - 3,\n\t\t\t\ty: pt.y - 3,\n\t\t\t\tradius: 3,\n\t\t\t\tisFilled: false,\n\t\t\t\tdebugColor: 'magenta',\n\t\t\t\tignore: true,\n\t\t\t})\n\t\t)\n\t}\n\tdbg.push(\n\t\tnew Polygon2d({\n\t\t\tpoints: startBox.corners,\n\t\t\tdebugColor: 'lime',\n\t\t\tisFilled: false,\n\t\t\tignore: true,\n\t\t}),\n\t\tnew Polygon2d({\n\t\t\tpoints: endBox.corners,\n\t\t\tdebugColor: 'lime',\n\t\t\tisFilled: false,\n\t\t\tignore: true,\n\t\t})\n\t)\n\n\treturn { start: startRelative, end: endRelative, dbg }\n}\n\ninterface ArrowheadInfo {\n\thasStartBinding: boolean\n\thasEndBinding: boolean\n\thasStartArrowhead: boolean\n\thasEndArrowhead: boolean\n}\nexport function getArrowLabelPosition(editor: Editor, shape: TLArrowShape) {\n\tconst isEditing = editor.getEditingShapeId() === shape.id\n\tif (!isEditing && isEmptyRichText(shape.props.richText)) {\n\t\t// Short-circuit for empty labels.\n\t\tconst bodyGeom = getArrowBodyGeometry(editor, shape)\n\t\tconst labelCenter = bodyGeom.interpolateAlongEdge(0.5)\n\t\treturn { box: Box.FromCenter(labelCenter, new Vec(0, 0)), debugGeom: [] }\n\t}\n\n\tconst debugGeom: Geometry2d[] = []\n\tconst info = getArrowInfo(editor, shape)!\n\n\tconst arrowheadInfo: ArrowheadInfo = {\n\t\thasStartBinding: !!info.bindings.start,\n\t\thasEndBinding: !!info.bindings.end,\n\t\thasStartArrowhead: info.start.arrowhead !== 'none',\n\t\thasEndArrowhead: info.end.arrowhead !== 'none',\n\t}\n\n\tconst range = getArrowLabelRange(editor, shape, info)\n\tif (range.dbg) debugGeom.push(...range.dbg)\n\n\tconst clampedPosition = getClampedPosition(shape, range, arrowheadInfo)\n\tconst bodyGeom = getArrowBodyGeometry(editor, shape)\n\tconst labelCenter = bodyGeom.interpolateAlongEdge(clampedPosition)\n\tconst labelSize = getArrowLabelSize(editor, shape)\n\n\treturn { box: Box.FromCenter(labelCenter, labelSize), debugGeom }\n}\n\nfunction getClampedPosition(\n\tshape: TLArrowShape,\n\trange: { start: number; end: number },\n\tarrowheadInfo: ArrowheadInfo\n) {\n\tconst { hasEndArrowhead, hasEndBinding, hasStartBinding, hasStartArrowhead } = arrowheadInfo\n\tconst clampedPosition = clamp(\n\t\tshape.props.labelPosition,\n\t\thasStartArrowhead || hasStartBinding ? range.start : 0,\n\t\thasEndArrowhead || hasEndBinding ? range.end : 1\n\t)\n\n\treturn clampedPosition\n}\n\nfunction furthest(from: VecLike, candidates: VecLike[]): VecLike | null {\n\tlet furthest: VecLike | null = null\n\tlet furthestDist = -Infinity\n\n\tfor (const candidate of candidates) {\n\t\tconst dist = Vec.Dist2(from, candidate)\n\t\tif (dist > furthestDist) {\n\t\t\tfurthest = candidate\n\t\t\tfurthestDist = dist\n\t\t}\n\t}\n\n\treturn furthest\n}\n\nexport function getArrowLabelFontSize(shape: TLArrowShape) {\n\treturn ARROW_LABEL_FONT_SIZES[shape.props.size] * shape.props.scale\n}\n\nexport function getArrowLabelDefaultPosition(editor: Editor, shape: TLArrowShape) {\n\tconst info = getArrowInfo(editor, shape)!\n\tswitch (info.type) {\n\t\tcase 'straight':\n\t\tcase 'arc':\n\t\t\treturn 0.5\n\t\tcase 'elbow': {\n\t\t\tconst midpointHandle = info.route.midpointHandle\n\t\t\tconst bodyGeom = getArrowBodyGeometry(editor, shape)\n\t\t\tif (midpointHandle && bodyGeom) {\n\t\t\t\treturn bodyGeom.uninterpolateAlongEdge(midpointHandle.point)\n\t\t\t}\n\t\t\treturn 0.5\n\t\t}\n\t\tdefault:\n\t\t\texhaustiveSwitchError(info, 'type')\n\t}\n}\n\n/** @internal */\nexport function isOverArrowLabel(editor: Editor, shape: TLShape) {\n\tif (!editor.isShapeOfType<TLArrowShape>(shape, 'arrow')) return false\n\n\tconst pointInShapeSpace = editor.getPointInShapeSpace(shape, editor.inputs.currentPagePoint)\n\t// How should we handle multiple labels? Do shapes ever have multiple labels?\n\tconst labelGeometry = editor.getShapeGeometry<Group2d>(shape).children[1]\n\t// Knowing what we know about arrows... if the shape has no text in its label,\n\t// then the label geometry should not be there.\n\treturn labelGeometry && pointInPolygon(pointInShapeSpace, labelGeometry.vertices)\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["bodyGeom", "labelCenter", "furthest"]
7
7
  }
@@ -84,11 +84,18 @@ function TooltipSingleton() {
84
84
  const [isOpen, setIsOpen] = useState(false);
85
85
  const triggerRef = useRef(null);
86
86
  const isFirstShowRef = useRef(true);
87
+ const editor = useMaybeEditor();
87
88
  const currentTooltip = useValue(
88
89
  "current tooltip",
89
90
  () => tooltipManager.getCurrentTooltipData(),
90
91
  []
91
92
  );
93
+ const cameraState = useValue("camera state", () => editor?.getCameraState(), [editor]);
94
+ useEffect(() => {
95
+ if (cameraState === "moving" && isOpen && currentTooltip) {
96
+ tooltipManager.hideTooltip(editor, currentTooltip.id, true);
97
+ }
98
+ }, [cameraState, isOpen, currentTooltip, editor]);
92
99
  useEffect(() => {
93
100
  let timer = null;
94
101
  if (currentTooltip && triggerRef.current) {
@@ -156,7 +163,6 @@ const TldrawUiTooltip = forwardRef(
156
163
  const hasProvider = useContext(TooltipSingletonContext);
157
164
  const orientationCtx = useTldrawUiOrientation();
158
165
  const sideToUse = side ?? orientationCtx.tooltipSide;
159
- const camera = useValue("camera", () => editor?.getCamera(), []);
160
166
  useEffect(() => {
161
167
  const currentTooltipId = tooltipId.current;
162
168
  return () => {
@@ -165,9 +171,6 @@ const TldrawUiTooltip = forwardRef(
165
171
  }
166
172
  };
167
173
  }, [editor, hasProvider]);
168
- useEffect(() => {
169
- tooltipManager.hideTooltip(editor, tooltipId.current, true);
170
- }, [editor, camera]);
171
174
  if (disabled || !content) {
172
175
  return /* @__PURE__ */ jsx(Fragment, { children });
173
176
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/lib/ui/components/primitives/TldrawUiTooltip.tsx"],
4
- "sourcesContent": ["import { assert, Atom, atom, Editor, uniqueId, useMaybeEditor, useValue } from '@tldraw/editor'\nimport { Tooltip as _Tooltip } from 'radix-ui'\nimport React, {\n\tcreateContext,\n\tforwardRef,\n\tReactNode,\n\tuseContext,\n\tuseEffect,\n\tuseRef,\n\tuseState,\n} from 'react'\nimport { useTldrawUiOrientation } from './layout'\n\nconst DEFAULT_TOOLTIP_DELAY_MS = 700\n\n/** @public */\nexport interface TldrawUiTooltipProps {\n\tchildren: React.ReactNode\n\tcontent?: string | React.ReactNode\n\tside?: 'top' | 'right' | 'bottom' | 'left'\n\tsideOffset?: number\n\tdisabled?: boolean\n\tshowOnMobile?: boolean\n\tdelayDuration?: number\n}\n\n// Singleton tooltip manager\nclass TooltipManager {\n\tprivate static instance: TooltipManager | null = null\n\tprivate currentTooltip = atom<{\n\t\tid: string\n\t\tcontent: ReactNode\n\t\tside: 'top' | 'right' | 'bottom' | 'left'\n\t\tsideOffset: number\n\t\tshowOnMobile: boolean\n\t\ttargetElement: HTMLElement\n\t\tdelayDuration: number\n\t} | null>('current tooltip', null)\n\tprivate destroyTimeoutId: number | null = null\n\n\tstatic getInstance(): TooltipManager {\n\t\tif (!TooltipManager.instance) {\n\t\t\tTooltipManager.instance = new TooltipManager()\n\t\t}\n\t\treturn TooltipManager.instance\n\t}\n\n\tshowTooltip(\n\t\ttooltipId: string,\n\t\tcontent: string | React.ReactNode,\n\t\ttargetElement: HTMLElement,\n\t\tside: 'top' | 'right' | 'bottom' | 'left',\n\t\tsideOffset: number,\n\t\tshowOnMobile: boolean,\n\t\tdelayDuration: number\n\t) {\n\t\t// Clear any existing destroy timeout\n\t\tif (this.destroyTimeoutId) {\n\t\t\tclearTimeout(this.destroyTimeoutId)\n\t\t\tthis.destroyTimeoutId = null\n\t\t}\n\n\t\t// Update current tooltip\n\t\tthis.currentTooltip.set({\n\t\t\tid: tooltipId,\n\t\t\tcontent,\n\t\t\tside,\n\t\t\tsideOffset,\n\t\t\tshowOnMobile,\n\t\t\ttargetElement,\n\t\t\tdelayDuration,\n\t\t})\n\t}\n\n\thideTooltip(editor: Editor | null, tooltipId: string, instant: boolean = false) {\n\t\tconst hide = () => {\n\t\t\t// Only hide if this is the current tooltip\n\t\t\tif (this.currentTooltip.get()?.id === tooltipId) {\n\t\t\t\tthis.currentTooltip.set(null)\n\t\t\t\tthis.destroyTimeoutId = null\n\t\t\t}\n\t\t}\n\n\t\tif (editor && !instant) {\n\t\t\t// Start destroy timeout (1 second)\n\t\t\tthis.destroyTimeoutId = editor.timers.setTimeout(hide, 300)\n\t\t} else {\n\t\t\thide()\n\t\t}\n\t}\n\n\thideAllTooltips() {\n\t\tthis.currentTooltip.set(null)\n\t\tthis.destroyTimeoutId = null\n\t}\n\n\tgetCurrentTooltipData() {\n\t\tconst currentTooltip = this.currentTooltip.get()\n\t\tif (!currentTooltip) return null\n\t\tif (!this.supportsHover() && !currentTooltip.showOnMobile) return null\n\t\treturn currentTooltip\n\t}\n\n\tprivate supportsHoverAtom: Atom<boolean> | null = null\n\tsupportsHover() {\n\t\tif (!this.supportsHoverAtom) {\n\t\t\tconst mediaQuery = window.matchMedia('(hover: hover)')\n\t\t\tconst supportsHover = atom('has hover', mediaQuery.matches)\n\t\t\tthis.supportsHoverAtom = supportsHover\n\t\t\tmediaQuery.addEventListener('change', (e) => {\n\t\t\t\tsupportsHover.set(e.matches)\n\t\t\t})\n\t\t}\n\t\treturn this.supportsHoverAtom.get()\n\t}\n}\n\nexport const tooltipManager = TooltipManager.getInstance()\n\n// Context for the tooltip singleton\nconst TooltipSingletonContext = createContext<boolean>(false)\n\n/** @public */\nexport interface TldrawUiTooltipProviderProps {\n\tchildren: React.ReactNode\n}\n\n/** @public @react */\nexport function TldrawUiTooltipProvider({ children }: TldrawUiTooltipProviderProps) {\n\treturn (\n\t\t<_Tooltip.Provider skipDelayDuration={700}>\n\t\t\t<TooltipSingletonContext.Provider value={true}>\n\t\t\t\t{children}\n\t\t\t\t<TooltipSingleton />\n\t\t\t</TooltipSingletonContext.Provider>\n\t\t</_Tooltip.Provider>\n\t)\n}\n\n// The singleton tooltip component that renders once\nfunction TooltipSingleton() {\n\tconst [isOpen, setIsOpen] = useState(false)\n\tconst triggerRef = useRef<HTMLDivElement>(null)\n\tconst isFirstShowRef = useRef(true)\n\n\tconst currentTooltip = useValue(\n\t\t'current tooltip',\n\t\t() => tooltipManager.getCurrentTooltipData(),\n\t\t[]\n\t)\n\n\t// Update open state and trigger position\n\tuseEffect(() => {\n\t\tlet timer: ReturnType<typeof setTimeout> | null = null\n\t\tif (currentTooltip && triggerRef.current) {\n\t\t\t// Position the invisible trigger element over the active element\n\t\t\tconst activeRect = currentTooltip.targetElement.getBoundingClientRect()\n\t\t\tconst trigger = triggerRef.current\n\n\t\t\ttrigger.style.position = 'fixed'\n\t\t\ttrigger.style.left = `${activeRect.left}px`\n\t\t\ttrigger.style.top = `${activeRect.top}px`\n\t\t\ttrigger.style.width = `${activeRect.width}px`\n\t\t\ttrigger.style.height = `${activeRect.height}px`\n\t\t\ttrigger.style.pointerEvents = 'none'\n\t\t\ttrigger.style.zIndex = '9999'\n\n\t\t\t// Handle delay for first show\n\t\t\tif (isFirstShowRef.current) {\n\t\t\t\t// eslint-disable-next-line no-restricted-globals\n\t\t\t\ttimer = setTimeout(() => {\n\t\t\t\t\tsetIsOpen(true)\n\t\t\t\t\tisFirstShowRef.current = false\n\t\t\t\t}, currentTooltip.delayDuration)\n\t\t\t} else {\n\t\t\t\t// Subsequent tooltips show immediately\n\t\t\t\tsetIsOpen(true)\n\t\t\t}\n\t\t} else {\n\t\t\t// Hide tooltip immediately\n\t\t\tsetIsOpen(false)\n\t\t\t// Reset first show state after tooltip is hidden\n\t\t\tisFirstShowRef.current = true\n\t\t}\n\n\t\treturn () => {\n\t\t\tif (timer !== null) {\n\t\t\t\tclearTimeout(timer)\n\t\t\t}\n\t\t}\n\t}, [currentTooltip])\n\n\tif (!currentTooltip) {\n\t\treturn null\n\t}\n\n\treturn (\n\t\t<_Tooltip.Root open={isOpen} delayDuration={0}>\n\t\t\t<_Tooltip.Trigger asChild>\n\t\t\t\t<div ref={triggerRef} />\n\t\t\t</_Tooltip.Trigger>\n\t\t\t<_Tooltip.Content\n\t\t\t\tclassName=\"tlui-tooltip\"\n\t\t\t\tside={currentTooltip.side}\n\t\t\t\tsideOffset={currentTooltip.sideOffset}\n\t\t\t\tavoidCollisions\n\t\t\t\tcollisionPadding={8}\n\t\t\t\tdir=\"ltr\"\n\t\t\t>\n\t\t\t\t{currentTooltip.content}\n\t\t\t\t<_Tooltip.Arrow className=\"tlui-tooltip__arrow\" />\n\t\t\t</_Tooltip.Content>\n\t\t</_Tooltip.Root>\n\t)\n}\n\n/** @public @react */\nexport const TldrawUiTooltip = forwardRef<HTMLButtonElement, TldrawUiTooltipProps>(\n\t(\n\t\t{\n\t\t\tchildren,\n\t\t\tcontent,\n\t\t\tside,\n\t\t\tsideOffset = 5,\n\t\t\tdisabled = false,\n\t\t\tshowOnMobile = false,\n\t\t\tdelayDuration,\n\t\t},\n\t\tref\n\t) => {\n\t\tconst editor = useMaybeEditor()\n\t\tconst tooltipId = useRef<string>(uniqueId())\n\t\tconst hasProvider = useContext(TooltipSingletonContext)\n\n\t\tconst orientationCtx = useTldrawUiOrientation()\n\t\tconst sideToUse = side ?? orientationCtx.tooltipSide\n\n\t\tconst camera = useValue('camera', () => editor?.getCamera(), [])\n\n\t\tuseEffect(() => {\n\t\t\tconst currentTooltipId = tooltipId.current\n\t\t\treturn () => {\n\t\t\t\tif (hasProvider) {\n\t\t\t\t\ttooltipManager.hideTooltip(editor, currentTooltipId, true)\n\t\t\t\t}\n\t\t\t}\n\t\t}, [editor, hasProvider])\n\n\t\tuseEffect(() => {\n\t\t\ttooltipManager.hideTooltip(editor, tooltipId.current, true)\n\t\t}, [editor, camera])\n\n\t\t// Don't show tooltip if disabled, no content, or UI labels are disabled\n\t\tif (disabled || !content) {\n\t\t\treturn <>{children}</>\n\t\t}\n\n\t\tconst delayDurationToUse =\n\t\t\tdelayDuration ?? (editor?.options.tooltipDelayMs || DEFAULT_TOOLTIP_DELAY_MS)\n\n\t\t// Fallback to old behavior if no provider\n\t\tif (!hasProvider) {\n\t\t\treturn (\n\t\t\t\t<_Tooltip.Root delayDuration={delayDurationToUse} disableHoverableContent>\n\t\t\t\t\t<_Tooltip.Trigger asChild ref={ref}>\n\t\t\t\t\t\t{children}\n\t\t\t\t\t</_Tooltip.Trigger>\n\t\t\t\t\t<_Tooltip.Content\n\t\t\t\t\t\tclassName=\"tlui-tooltip\"\n\t\t\t\t\t\tside={sideToUse}\n\t\t\t\t\t\tsideOffset={sideOffset}\n\t\t\t\t\t\tavoidCollisions\n\t\t\t\t\t\tcollisionPadding={8}\n\t\t\t\t\t\tdir=\"ltr\"\n\t\t\t\t\t>\n\t\t\t\t\t\t{content}\n\t\t\t\t\t\t<_Tooltip.Arrow className=\"tlui-tooltip__arrow\" />\n\t\t\t\t\t</_Tooltip.Content>\n\t\t\t\t</_Tooltip.Root>\n\t\t\t)\n\t\t}\n\n\t\tconst child = React.Children.only(children)\n\t\tassert(React.isValidElement(child), 'TldrawUiTooltip children must be a single element')\n\n\t\tconst handleMouseEnter = (event: React.MouseEvent<HTMLElement>) => {\n\t\t\tchild.props.onMouseEnter?.(event)\n\t\t\ttooltipManager.showTooltip(\n\t\t\t\ttooltipId.current,\n\t\t\t\tcontent,\n\t\t\t\tevent.currentTarget as HTMLElement,\n\t\t\t\tsideToUse,\n\t\t\t\tsideOffset,\n\t\t\t\tshowOnMobile,\n\t\t\t\tdelayDurationToUse\n\t\t\t)\n\t\t}\n\n\t\tconst handleMouseLeave = (event: React.MouseEvent<HTMLElement>) => {\n\t\t\tchild.props.onMouseLeave?.(event)\n\t\t\ttooltipManager.hideTooltip(editor, tooltipId.current)\n\t\t}\n\n\t\tconst handleFocus = (event: React.FocusEvent<HTMLElement>) => {\n\t\t\tchild.props.onFocus?.(event)\n\t\t\ttooltipManager.showTooltip(\n\t\t\t\ttooltipId.current,\n\t\t\t\tcontent,\n\t\t\t\tevent.currentTarget as HTMLElement,\n\t\t\t\tsideToUse,\n\t\t\t\tsideOffset,\n\t\t\t\tshowOnMobile,\n\t\t\t\tdelayDurationToUse\n\t\t\t)\n\t\t}\n\n\t\tconst handleBlur = (event: React.FocusEvent<HTMLElement>) => {\n\t\t\tchild.props.onBlur?.(event)\n\t\t\ttooltipManager.hideTooltip(editor, tooltipId.current)\n\t\t}\n\n\t\tconst childrenWithHandlers = React.cloneElement(children as React.ReactElement, {\n\t\t\tonMouseEnter: handleMouseEnter,\n\t\t\tonMouseLeave: handleMouseLeave,\n\t\t\tonFocus: handleFocus,\n\t\t\tonBlur: handleBlur,\n\t\t})\n\n\t\treturn childrenWithHandlers\n\t}\n)\n"],
5
- "mappings": "AAmIG,SA2HO,UAzHN,KAFD;AAnIH,SAAS,QAAc,MAAc,UAAU,gBAAgB,gBAAgB;AAC/E,SAAS,WAAW,gBAAgB;AACpC,OAAO;AAAA,EACN;AAAA,EACA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACM;AACP,SAAS,8BAA8B;AAEvC,MAAM,2BAA2B;AAcjC,MAAM,eAAe;AAAA,EACpB,OAAe,WAAkC;AAAA,EACzC,iBAAiB,KAQf,mBAAmB,IAAI;AAAA,EACzB,mBAAkC;AAAA,EAE1C,OAAO,cAA8B;AACpC,QAAI,CAAC,eAAe,UAAU;AAC7B,qBAAe,WAAW,IAAI,eAAe;AAAA,IAC9C;AACA,WAAO,eAAe;AAAA,EACvB;AAAA,EAEA,YACC,WACA,SACA,eACA,MACA,YACA,cACA,eACC;AAED,QAAI,KAAK,kBAAkB;AAC1B,mBAAa,KAAK,gBAAgB;AAClC,WAAK,mBAAmB;AAAA,IACzB;AAGA,SAAK,eAAe,IAAI;AAAA,MACvB,IAAI;AAAA,MACJ;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACD,CAAC;AAAA,EACF;AAAA,EAEA,YAAY,QAAuB,WAAmB,UAAmB,OAAO;AAC/E,UAAM,OAAO,MAAM;AAElB,UAAI,KAAK,eAAe,IAAI,GAAG,OAAO,WAAW;AAChD,aAAK,eAAe,IAAI,IAAI;AAC5B,aAAK,mBAAmB;AAAA,MACzB;AAAA,IACD;AAEA,QAAI,UAAU,CAAC,SAAS;AAEvB,WAAK,mBAAmB,OAAO,OAAO,WAAW,MAAM,GAAG;AAAA,IAC3D,OAAO;AACN,WAAK;AAAA,IACN;AAAA,EACD;AAAA,EAEA,kBAAkB;AACjB,SAAK,eAAe,IAAI,IAAI;AAC5B,SAAK,mBAAmB;AAAA,EACzB;AAAA,EAEA,wBAAwB;AACvB,UAAM,iBAAiB,KAAK,eAAe,IAAI;AAC/C,QAAI,CAAC,eAAgB,QAAO;AAC5B,QAAI,CAAC,KAAK,cAAc,KAAK,CAAC,eAAe,aAAc,QAAO;AAClE,WAAO;AAAA,EACR;AAAA,EAEQ,oBAA0C;AAAA,EAClD,gBAAgB;AACf,QAAI,CAAC,KAAK,mBAAmB;AAC5B,YAAM,aAAa,OAAO,WAAW,gBAAgB;AACrD,YAAM,gBAAgB,KAAK,aAAa,WAAW,OAAO;AAC1D,WAAK,oBAAoB;AACzB,iBAAW,iBAAiB,UAAU,CAAC,MAAM;AAC5C,sBAAc,IAAI,EAAE,OAAO;AAAA,MAC5B,CAAC;AAAA,IACF;AACA,WAAO,KAAK,kBAAkB,IAAI;AAAA,EACnC;AACD;AAEO,MAAM,iBAAiB,eAAe,YAAY;AAGzD,MAAM,0BAA0B,cAAuB,KAAK;AAQrD,SAAS,wBAAwB,EAAE,SAAS,GAAiC;AACnF,SACC,oBAAC,SAAS,UAAT,EAAkB,mBAAmB,KACrC,+BAAC,wBAAwB,UAAxB,EAAiC,OAAO,MACvC;AAAA;AAAA,IACD,oBAAC,oBAAiB;AAAA,KACnB,GACD;AAEF;AAGA,SAAS,mBAAmB;AAC3B,QAAM,CAAC,QAAQ,SAAS,IAAI,SAAS,KAAK;AAC1C,QAAM,aAAa,OAAuB,IAAI;AAC9C,QAAM,iBAAiB,OAAO,IAAI;AAElC,QAAM,iBAAiB;AAAA,IACtB;AAAA,IACA,MAAM,eAAe,sBAAsB;AAAA,IAC3C,CAAC;AAAA,EACF;AAGA,YAAU,MAAM;AACf,QAAI,QAA8C;AAClD,QAAI,kBAAkB,WAAW,SAAS;AAEzC,YAAM,aAAa,eAAe,cAAc,sBAAsB;AACtE,YAAM,UAAU,WAAW;AAE3B,cAAQ,MAAM,WAAW;AACzB,cAAQ,MAAM,OAAO,GAAG,WAAW,IAAI;AACvC,cAAQ,MAAM,MAAM,GAAG,WAAW,GAAG;AACrC,cAAQ,MAAM,QAAQ,GAAG,WAAW,KAAK;AACzC,cAAQ,MAAM,SAAS,GAAG,WAAW,MAAM;AAC3C,cAAQ,MAAM,gBAAgB;AAC9B,cAAQ,MAAM,SAAS;AAGvB,UAAI,eAAe,SAAS;AAE3B,gBAAQ,WAAW,MAAM;AACxB,oBAAU,IAAI;AACd,yBAAe,UAAU;AAAA,QAC1B,GAAG,eAAe,aAAa;AAAA,MAChC,OAAO;AAEN,kBAAU,IAAI;AAAA,MACf;AAAA,IACD,OAAO;AAEN,gBAAU,KAAK;AAEf,qBAAe,UAAU;AAAA,IAC1B;AAEA,WAAO,MAAM;AACZ,UAAI,UAAU,MAAM;AACnB,qBAAa,KAAK;AAAA,MACnB;AAAA,IACD;AAAA,EACD,GAAG,CAAC,cAAc,CAAC;AAEnB,MAAI,CAAC,gBAAgB;AACpB,WAAO;AAAA,EACR;AAEA,SACC,qBAAC,SAAS,MAAT,EAAc,MAAM,QAAQ,eAAe,GAC3C;AAAA,wBAAC,SAAS,SAAT,EAAiB,SAAO,MACxB,8BAAC,SAAI,KAAK,YAAY,GACvB;AAAA,IACA;AAAA,MAAC,SAAS;AAAA,MAAT;AAAA,QACA,WAAU;AAAA,QACV,MAAM,eAAe;AAAA,QACrB,YAAY,eAAe;AAAA,QAC3B,iBAAe;AAAA,QACf,kBAAkB;AAAA,QAClB,KAAI;AAAA,QAEH;AAAA,yBAAe;AAAA,UAChB,oBAAC,SAAS,OAAT,EAAe,WAAU,uBAAsB;AAAA;AAAA;AAAA,IACjD;AAAA,KACD;AAEF;AAGO,MAAM,kBAAkB;AAAA,EAC9B,CACC;AAAA,IACC;AAAA,IACA;AAAA,IACA;AAAA,IACA,aAAa;AAAA,IACb,WAAW;AAAA,IACX,eAAe;AAAA,IACf;AAAA,EACD,GACA,QACI;AACJ,UAAM,SAAS,eAAe;AAC9B,UAAM,YAAY,OAAe,SAAS,CAAC;AAC3C,UAAM,cAAc,WAAW,uBAAuB;AAEtD,UAAM,iBAAiB,uBAAuB;AAC9C,UAAM,YAAY,QAAQ,eAAe;AAEzC,UAAM,SAAS,SAAS,UAAU,MAAM,QAAQ,UAAU,GAAG,CAAC,CAAC;AAE/D,cAAU,MAAM;AACf,YAAM,mBAAmB,UAAU;AACnC,aAAO,MAAM;AACZ,YAAI,aAAa;AAChB,yBAAe,YAAY,QAAQ,kBAAkB,IAAI;AAAA,QAC1D;AAAA,MACD;AAAA,IACD,GAAG,CAAC,QAAQ,WAAW,CAAC;AAExB,cAAU,MAAM;AACf,qBAAe,YAAY,QAAQ,UAAU,SAAS,IAAI;AAAA,IAC3D,GAAG,CAAC,QAAQ,MAAM,CAAC;AAGnB,QAAI,YAAY,CAAC,SAAS;AACzB,aAAO,gCAAG,UAAS;AAAA,IACpB;AAEA,UAAM,qBACL,kBAAkB,QAAQ,QAAQ,kBAAkB;AAGrD,QAAI,CAAC,aAAa;AACjB,aACC,qBAAC,SAAS,MAAT,EAAc,eAAe,oBAAoB,yBAAuB,MACxE;AAAA,4BAAC,SAAS,SAAT,EAAiB,SAAO,MAAC,KACxB,UACF;AAAA,QACA;AAAA,UAAC,SAAS;AAAA,UAAT;AAAA,YACA,WAAU;AAAA,YACV,MAAM;AAAA,YACN;AAAA,YACA,iBAAe;AAAA,YACf,kBAAkB;AAAA,YAClB,KAAI;AAAA,YAEH;AAAA;AAAA,cACD,oBAAC,SAAS,OAAT,EAAe,WAAU,uBAAsB;AAAA;AAAA;AAAA,QACjD;AAAA,SACD;AAAA,IAEF;AAEA,UAAM,QAAQ,MAAM,SAAS,KAAK,QAAQ;AAC1C,WAAO,MAAM,eAAe,KAAK,GAAG,mDAAmD;AAEvF,UAAM,mBAAmB,CAAC,UAAyC;AAClE,YAAM,MAAM,eAAe,KAAK;AAChC,qBAAe;AAAA,QACd,UAAU;AAAA,QACV;AAAA,QACA,MAAM;AAAA,QACN;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACD;AAAA,IACD;AAEA,UAAM,mBAAmB,CAAC,UAAyC;AAClE,YAAM,MAAM,eAAe,KAAK;AAChC,qBAAe,YAAY,QAAQ,UAAU,OAAO;AAAA,IACrD;AAEA,UAAM,cAAc,CAAC,UAAyC;AAC7D,YAAM,MAAM,UAAU,KAAK;AAC3B,qBAAe;AAAA,QACd,UAAU;AAAA,QACV;AAAA,QACA,MAAM;AAAA,QACN;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACD;AAAA,IACD;AAEA,UAAM,aAAa,CAAC,UAAyC;AAC5D,YAAM,MAAM,SAAS,KAAK;AAC1B,qBAAe,YAAY,QAAQ,UAAU,OAAO;AAAA,IACrD;AAEA,UAAM,uBAAuB,MAAM,aAAa,UAAgC;AAAA,MAC/E,cAAc;AAAA,MACd,cAAc;AAAA,MACd,SAAS;AAAA,MACT,QAAQ;AAAA,IACT,CAAC;AAED,WAAO;AAAA,EACR;AACD;",
4
+ "sourcesContent": ["import { assert, Atom, atom, Editor, uniqueId, useMaybeEditor, useValue } from '@tldraw/editor'\nimport { Tooltip as _Tooltip } from 'radix-ui'\nimport React, {\n\tcreateContext,\n\tforwardRef,\n\tReactNode,\n\tuseContext,\n\tuseEffect,\n\tuseRef,\n\tuseState,\n} from 'react'\nimport { useTldrawUiOrientation } from './layout'\n\nconst DEFAULT_TOOLTIP_DELAY_MS = 700\n\n/** @public */\nexport interface TldrawUiTooltipProps {\n\tchildren: React.ReactNode\n\tcontent?: string | React.ReactNode\n\tside?: 'top' | 'right' | 'bottom' | 'left'\n\tsideOffset?: number\n\tdisabled?: boolean\n\tshowOnMobile?: boolean\n\tdelayDuration?: number\n}\n\n// Singleton tooltip manager\nclass TooltipManager {\n\tprivate static instance: TooltipManager | null = null\n\tprivate currentTooltip = atom<{\n\t\tid: string\n\t\tcontent: ReactNode\n\t\tside: 'top' | 'right' | 'bottom' | 'left'\n\t\tsideOffset: number\n\t\tshowOnMobile: boolean\n\t\ttargetElement: HTMLElement\n\t\tdelayDuration: number\n\t} | null>('current tooltip', null)\n\tprivate destroyTimeoutId: number | null = null\n\n\tstatic getInstance(): TooltipManager {\n\t\tif (!TooltipManager.instance) {\n\t\t\tTooltipManager.instance = new TooltipManager()\n\t\t}\n\t\treturn TooltipManager.instance\n\t}\n\n\tshowTooltip(\n\t\ttooltipId: string,\n\t\tcontent: string | React.ReactNode,\n\t\ttargetElement: HTMLElement,\n\t\tside: 'top' | 'right' | 'bottom' | 'left',\n\t\tsideOffset: number,\n\t\tshowOnMobile: boolean,\n\t\tdelayDuration: number\n\t) {\n\t\t// Clear any existing destroy timeout\n\t\tif (this.destroyTimeoutId) {\n\t\t\tclearTimeout(this.destroyTimeoutId)\n\t\t\tthis.destroyTimeoutId = null\n\t\t}\n\n\t\t// Update current tooltip\n\t\tthis.currentTooltip.set({\n\t\t\tid: tooltipId,\n\t\t\tcontent,\n\t\t\tside,\n\t\t\tsideOffset,\n\t\t\tshowOnMobile,\n\t\t\ttargetElement,\n\t\t\tdelayDuration,\n\t\t})\n\t}\n\n\thideTooltip(editor: Editor | null, tooltipId: string, instant: boolean = false) {\n\t\tconst hide = () => {\n\t\t\t// Only hide if this is the current tooltip\n\t\t\tif (this.currentTooltip.get()?.id === tooltipId) {\n\t\t\t\tthis.currentTooltip.set(null)\n\t\t\t\tthis.destroyTimeoutId = null\n\t\t\t}\n\t\t}\n\n\t\tif (editor && !instant) {\n\t\t\t// Start destroy timeout (1 second)\n\t\t\tthis.destroyTimeoutId = editor.timers.setTimeout(hide, 300)\n\t\t} else {\n\t\t\thide()\n\t\t}\n\t}\n\n\thideAllTooltips() {\n\t\tthis.currentTooltip.set(null)\n\t\tthis.destroyTimeoutId = null\n\t}\n\n\tgetCurrentTooltipData() {\n\t\tconst currentTooltip = this.currentTooltip.get()\n\t\tif (!currentTooltip) return null\n\t\tif (!this.supportsHover() && !currentTooltip.showOnMobile) return null\n\t\treturn currentTooltip\n\t}\n\n\tprivate supportsHoverAtom: Atom<boolean> | null = null\n\tsupportsHover() {\n\t\tif (!this.supportsHoverAtom) {\n\t\t\tconst mediaQuery = window.matchMedia('(hover: hover)')\n\t\t\tconst supportsHover = atom('has hover', mediaQuery.matches)\n\t\t\tthis.supportsHoverAtom = supportsHover\n\t\t\tmediaQuery.addEventListener('change', (e) => {\n\t\t\t\tsupportsHover.set(e.matches)\n\t\t\t})\n\t\t}\n\t\treturn this.supportsHoverAtom.get()\n\t}\n}\n\nexport const tooltipManager = TooltipManager.getInstance()\n\n// Context for the tooltip singleton\nconst TooltipSingletonContext = createContext<boolean>(false)\n\n/** @public */\nexport interface TldrawUiTooltipProviderProps {\n\tchildren: React.ReactNode\n}\n\n/** @public @react */\nexport function TldrawUiTooltipProvider({ children }: TldrawUiTooltipProviderProps) {\n\treturn (\n\t\t<_Tooltip.Provider skipDelayDuration={700}>\n\t\t\t<TooltipSingletonContext.Provider value={true}>\n\t\t\t\t{children}\n\t\t\t\t<TooltipSingleton />\n\t\t\t</TooltipSingletonContext.Provider>\n\t\t</_Tooltip.Provider>\n\t)\n}\n\n// The singleton tooltip component that renders once\nfunction TooltipSingleton() {\n\tconst [isOpen, setIsOpen] = useState(false)\n\tconst triggerRef = useRef<HTMLDivElement>(null)\n\tconst isFirstShowRef = useRef(true)\n\tconst editor = useMaybeEditor()\n\n\tconst currentTooltip = useValue(\n\t\t'current tooltip',\n\t\t() => tooltipManager.getCurrentTooltipData(),\n\t\t[]\n\t)\n\n\tconst cameraState = useValue('camera state', () => editor?.getCameraState(), [editor])\n\n\t// Hide tooltip when camera is moving (panning/zooming)\n\tuseEffect(() => {\n\t\tif (cameraState === 'moving' && isOpen && currentTooltip) {\n\t\t\ttooltipManager.hideTooltip(editor, currentTooltip.id, true)\n\t\t}\n\t}, [cameraState, isOpen, currentTooltip, editor])\n\n\t// Update open state and trigger position\n\tuseEffect(() => {\n\t\tlet timer: ReturnType<typeof setTimeout> | null = null\n\t\tif (currentTooltip && triggerRef.current) {\n\t\t\t// Position the invisible trigger element over the active element\n\t\t\tconst activeRect = currentTooltip.targetElement.getBoundingClientRect()\n\t\t\tconst trigger = triggerRef.current\n\n\t\t\ttrigger.style.position = 'fixed'\n\t\t\ttrigger.style.left = `${activeRect.left}px`\n\t\t\ttrigger.style.top = `${activeRect.top}px`\n\t\t\ttrigger.style.width = `${activeRect.width}px`\n\t\t\ttrigger.style.height = `${activeRect.height}px`\n\t\t\ttrigger.style.pointerEvents = 'none'\n\t\t\ttrigger.style.zIndex = '9999'\n\n\t\t\t// Handle delay for first show\n\t\t\tif (isFirstShowRef.current) {\n\t\t\t\t// eslint-disable-next-line no-restricted-globals\n\t\t\t\ttimer = setTimeout(() => {\n\t\t\t\t\tsetIsOpen(true)\n\t\t\t\t\tisFirstShowRef.current = false\n\t\t\t\t}, currentTooltip.delayDuration)\n\t\t\t} else {\n\t\t\t\t// Subsequent tooltips show immediately\n\t\t\t\tsetIsOpen(true)\n\t\t\t}\n\t\t} else {\n\t\t\t// Hide tooltip immediately\n\t\t\tsetIsOpen(false)\n\t\t\t// Reset first show state after tooltip is hidden\n\t\t\tisFirstShowRef.current = true\n\t\t}\n\n\t\treturn () => {\n\t\t\tif (timer !== null) {\n\t\t\t\tclearTimeout(timer)\n\t\t\t}\n\t\t}\n\t}, [currentTooltip])\n\n\tif (!currentTooltip) {\n\t\treturn null\n\t}\n\n\treturn (\n\t\t<_Tooltip.Root open={isOpen} delayDuration={0}>\n\t\t\t<_Tooltip.Trigger asChild>\n\t\t\t\t<div ref={triggerRef} />\n\t\t\t</_Tooltip.Trigger>\n\t\t\t<_Tooltip.Content\n\t\t\t\tclassName=\"tlui-tooltip\"\n\t\t\t\tside={currentTooltip.side}\n\t\t\t\tsideOffset={currentTooltip.sideOffset}\n\t\t\t\tavoidCollisions\n\t\t\t\tcollisionPadding={8}\n\t\t\t\tdir=\"ltr\"\n\t\t\t>\n\t\t\t\t{currentTooltip.content}\n\t\t\t\t<_Tooltip.Arrow className=\"tlui-tooltip__arrow\" />\n\t\t\t</_Tooltip.Content>\n\t\t</_Tooltip.Root>\n\t)\n}\n\n/** @public @react */\nexport const TldrawUiTooltip = forwardRef<HTMLButtonElement, TldrawUiTooltipProps>(\n\t(\n\t\t{\n\t\t\tchildren,\n\t\t\tcontent,\n\t\t\tside,\n\t\t\tsideOffset = 5,\n\t\t\tdisabled = false,\n\t\t\tshowOnMobile = false,\n\t\t\tdelayDuration,\n\t\t},\n\t\tref\n\t) => {\n\t\tconst editor = useMaybeEditor()\n\t\tconst tooltipId = useRef<string>(uniqueId())\n\t\tconst hasProvider = useContext(TooltipSingletonContext)\n\n\t\tconst orientationCtx = useTldrawUiOrientation()\n\t\tconst sideToUse = side ?? orientationCtx.tooltipSide\n\n\t\tuseEffect(() => {\n\t\t\tconst currentTooltipId = tooltipId.current\n\t\t\treturn () => {\n\t\t\t\tif (hasProvider) {\n\t\t\t\t\ttooltipManager.hideTooltip(editor, currentTooltipId, true)\n\t\t\t\t}\n\t\t\t}\n\t\t}, [editor, hasProvider])\n\n\t\t// Don't show tooltip if disabled, no content, or UI labels are disabled\n\t\tif (disabled || !content) {\n\t\t\treturn <>{children}</>\n\t\t}\n\n\t\tconst delayDurationToUse =\n\t\t\tdelayDuration ?? (editor?.options.tooltipDelayMs || DEFAULT_TOOLTIP_DELAY_MS)\n\n\t\t// Fallback to old behavior if no provider\n\t\tif (!hasProvider) {\n\t\t\treturn (\n\t\t\t\t<_Tooltip.Root delayDuration={delayDurationToUse} disableHoverableContent>\n\t\t\t\t\t<_Tooltip.Trigger asChild ref={ref}>\n\t\t\t\t\t\t{children}\n\t\t\t\t\t</_Tooltip.Trigger>\n\t\t\t\t\t<_Tooltip.Content\n\t\t\t\t\t\tclassName=\"tlui-tooltip\"\n\t\t\t\t\t\tside={sideToUse}\n\t\t\t\t\t\tsideOffset={sideOffset}\n\t\t\t\t\t\tavoidCollisions\n\t\t\t\t\t\tcollisionPadding={8}\n\t\t\t\t\t\tdir=\"ltr\"\n\t\t\t\t\t>\n\t\t\t\t\t\t{content}\n\t\t\t\t\t\t<_Tooltip.Arrow className=\"tlui-tooltip__arrow\" />\n\t\t\t\t\t</_Tooltip.Content>\n\t\t\t\t</_Tooltip.Root>\n\t\t\t)\n\t\t}\n\n\t\tconst child = React.Children.only(children)\n\t\tassert(React.isValidElement(child), 'TldrawUiTooltip children must be a single element')\n\n\t\tconst handleMouseEnter = (event: React.MouseEvent<HTMLElement>) => {\n\t\t\tchild.props.onMouseEnter?.(event)\n\t\t\ttooltipManager.showTooltip(\n\t\t\t\ttooltipId.current,\n\t\t\t\tcontent,\n\t\t\t\tevent.currentTarget as HTMLElement,\n\t\t\t\tsideToUse,\n\t\t\t\tsideOffset,\n\t\t\t\tshowOnMobile,\n\t\t\t\tdelayDurationToUse\n\t\t\t)\n\t\t}\n\n\t\tconst handleMouseLeave = (event: React.MouseEvent<HTMLElement>) => {\n\t\t\tchild.props.onMouseLeave?.(event)\n\t\t\ttooltipManager.hideTooltip(editor, tooltipId.current)\n\t\t}\n\n\t\tconst handleFocus = (event: React.FocusEvent<HTMLElement>) => {\n\t\t\tchild.props.onFocus?.(event)\n\t\t\ttooltipManager.showTooltip(\n\t\t\t\ttooltipId.current,\n\t\t\t\tcontent,\n\t\t\t\tevent.currentTarget as HTMLElement,\n\t\t\t\tsideToUse,\n\t\t\t\tsideOffset,\n\t\t\t\tshowOnMobile,\n\t\t\t\tdelayDurationToUse\n\t\t\t)\n\t\t}\n\n\t\tconst handleBlur = (event: React.FocusEvent<HTMLElement>) => {\n\t\t\tchild.props.onBlur?.(event)\n\t\t\ttooltipManager.hideTooltip(editor, tooltipId.current)\n\t\t}\n\n\t\tconst childrenWithHandlers = React.cloneElement(children as React.ReactElement, {\n\t\t\tonMouseEnter: handleMouseEnter,\n\t\t\tonMouseLeave: handleMouseLeave,\n\t\t\tonFocus: handleFocus,\n\t\t\tonBlur: handleBlur,\n\t\t})\n\n\t\treturn childrenWithHandlers\n\t}\n)\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -113,7 +113,7 @@ function ToolsProvider({ overrides, children }) {
113
113
  createShape: (id) => editor.createShape({
114
114
  id,
115
115
  type: "arrow",
116
- props: { start: { x: 0, y: 0 }, end: { x: 200, y: 0 } }
116
+ props: { start: { x: 0, y: 200 }, end: { x: 200, y: 0 } }
117
117
  })
118
118
  });
119
119
  trackEvent("drag-tool", { source, id: "arrow" });
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/lib/ui/hooks/useTools.tsx"],
4
- "sourcesContent": ["import {\n\tassertExists,\n\tcreateShapeId,\n\tEditor,\n\tGeoShapeGeoStyle,\n\tgetIndicesBetween,\n\tTLLineShape,\n\tTLPointerEventInfo,\n\tTLShapeId,\n\ttoRichText,\n\tuseMaybeEditor,\n} from '@tldraw/editor'\nimport * as React from 'react'\nimport { EmbedDialog } from '../components/EmbedDialog'\nimport { TLUiIconJsx } from '../components/primitives/TldrawUiIcon'\nimport { useA11y } from '../context/a11y'\nimport { TLUiEventSource, useUiEvents } from '../context/events'\nimport { TLUiIconType } from '../icon-types'\nimport { TLUiOverrideHelpers, useDefaultHelpers } from '../overrides'\nimport { TLUiTranslationKey } from './useTranslation/TLUiTranslationKey'\nimport { useTranslation } from './useTranslation/useTranslation'\n\n/** @public */\nexport interface TLUiToolItem<\n\tTranslationKey extends string = string,\n\tIconType extends string = string,\n> {\n\tid: string\n\tlabel: TranslationKey\n\tshortcutsLabel?: TranslationKey\n\ticon: IconType | TLUiIconJsx\n\tonSelect(source: TLUiEventSource): void\n\tonDragStart?(source: TLUiEventSource, info: TLPointerEventInfo): void\n\t/**\n\t * The keyboard shortcut for this tool. This is a string that can be a single key,\n\t * or a combination of keys.\n\t * For example, `cmd+z` or `cmd+shift+z` or `cmd+u,ctrl+u`, or just `v` or `a`.\n\t * We have backwards compatibility with the old system, where we used to use\n\t * symbols to denote cmd/alt/shift, using `!` for shift, `$` for cmd, and `?` for alt.\n\t */\n\tkbd?: string\n\treadonlyOk?: boolean\n\tmeta?: {\n\t\t[key: string]: any\n\t}\n}\n\n/** @public */\nexport type TLUiToolsContextType = Record<string, TLUiToolItem>\n\n/** @internal */\nexport const ToolsContext = React.createContext<null | TLUiToolsContextType>(null)\n\n/** @public */\nexport interface TLUiToolsProviderProps {\n\toverrides?(\n\t\teditor: Editor,\n\t\ttools: TLUiToolsContextType,\n\t\thelpers: Partial<TLUiOverrideHelpers>\n\t): TLUiToolsContextType\n\tchildren: React.ReactNode\n}\n\n/** @internal */\nexport function ToolsProvider({ overrides, children }: TLUiToolsProviderProps) {\n\tconst editor = useMaybeEditor()\n\tconst trackEvent = useUiEvents()\n\n\tconst a11y = useA11y()\n\tconst msg = useTranslation()\n\tconst helpers = useDefaultHelpers()\n\n\tconst onToolSelect = React.useCallback(\n\t\t(\n\t\t\tsource: TLUiEventSource,\n\t\t\ttool: TLUiToolItem<TLUiTranslationKey, TLUiIconType>,\n\t\t\tid?: string\n\t\t) => {\n\t\t\ta11y.announce({ msg: msg(tool.label) })\n\t\t\ttrackEvent('select-tool', { source, id: id ?? tool.id })\n\t\t},\n\t\t[a11y, msg, trackEvent]\n\t)\n\n\tconst tools = React.useMemo<TLUiToolsContextType>(() => {\n\t\tif (!editor) return {}\n\t\tconst toolsArray: TLUiToolItem<TLUiTranslationKey, TLUiIconType>[] = [\n\t\t\t{\n\t\t\t\tid: 'select',\n\t\t\t\tlabel: 'tool.select',\n\t\t\t\ticon: 'tool-pointer',\n\t\t\t\tkbd: 'v',\n\t\t\t\treadonlyOk: true,\n\t\t\t\tonSelect(source) {\n\t\t\t\t\tif (editor.isIn('select')) {\n\t\t\t\t\t\t// There's a quirk of select mode, where editing a shape is a sub-state of select.\n\t\t\t\t\t\t// Because the text tool can be locked/sticky, we need to make sure we exit the\n\t\t\t\t\t\t// text tool.\n\t\t\t\t\t\t//\n\t\t\t\t\t\t// psst, if you're changing this code, also change the code\n\t\t\t\t\t\t// in strange-tools.test.ts! Sadly it's duplicated there.\n\t\t\t\t\t\tconst currentNode = editor.root.getCurrent()!\n\t\t\t\t\t\tcurrentNode.exit({}, currentNode.id)\n\t\t\t\t\t\tcurrentNode.enter({}, currentNode.id)\n\t\t\t\t\t}\n\t\t\t\t\teditor.setCurrentTool('select')\n\t\t\t\t\tonToolSelect(source, this)\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: 'hand',\n\t\t\t\tlabel: 'tool.hand',\n\t\t\t\ticon: 'tool-hand',\n\t\t\t\tkbd: 'h',\n\t\t\t\treadonlyOk: true,\n\t\t\t\tonSelect(source) {\n\t\t\t\t\teditor.setCurrentTool('hand')\n\t\t\t\t\tonToolSelect(source, this)\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: 'eraser',\n\t\t\t\tlabel: 'tool.eraser',\n\t\t\t\ticon: 'tool-eraser',\n\t\t\t\tkbd: 'e',\n\t\t\t\tonSelect(source) {\n\t\t\t\t\teditor.setCurrentTool('eraser')\n\t\t\t\t\tonToolSelect(source, this)\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: 'draw',\n\t\t\t\tlabel: 'tool.draw',\n\t\t\t\ticon: 'tool-pencil',\n\t\t\t\tkbd: 'd,b,x',\n\t\t\t\tonSelect(source) {\n\t\t\t\t\teditor.setCurrentTool('draw')\n\t\t\t\t\tonToolSelect(source, this)\n\t\t\t\t},\n\t\t\t},\n\t\t\t...[...GeoShapeGeoStyle.values].map((geo) => ({\n\t\t\t\tid: geo,\n\t\t\t\tlabel: `tool.${geo}` as TLUiTranslationKey,\n\t\t\t\tmeta: {\n\t\t\t\t\tgeo,\n\t\t\t\t},\n\t\t\t\tkbd: geo === 'rectangle' ? 'r' : geo === 'ellipse' ? 'o' : undefined,\n\t\t\t\ticon: ('geo-' + geo) as TLUiIconType,\n\t\t\t\tonSelect(source: TLUiEventSource) {\n\t\t\t\t\teditor.run(() => {\n\t\t\t\t\t\teditor.setStyleForNextShapes(GeoShapeGeoStyle, geo)\n\t\t\t\t\t\teditor.setCurrentTool('geo')\n\t\t\t\t\t\tonToolSelect(source, this, `geo-${geo}`)\n\t\t\t\t\t})\n\t\t\t\t},\n\t\t\t\tonDragStart(source: TLUiEventSource, info: TLPointerEventInfo) {\n\t\t\t\t\tonDragFromToolbarToCreateShape(editor, info, {\n\t\t\t\t\t\tcreateShape: (id) =>\n\t\t\t\t\t\t\teditor.createShape({ id, type: 'geo', props: { w: 200, h: 200, geo } }),\n\t\t\t\t\t})\n\t\t\t\t\ttrackEvent('drag-tool', { source, id: 'geo' })\n\t\t\t\t},\n\t\t\t})),\n\t\t\t{\n\t\t\t\tid: 'arrow',\n\t\t\t\tlabel: 'tool.arrow',\n\t\t\t\ticon: 'tool-arrow',\n\t\t\t\tkbd: 'a',\n\t\t\t\tonSelect(source) {\n\t\t\t\t\teditor.setCurrentTool('arrow')\n\t\t\t\t\tonToolSelect(source, this)\n\t\t\t\t},\n\t\t\t\tonDragStart(source: TLUiEventSource, info: TLPointerEventInfo) {\n\t\t\t\t\tonDragFromToolbarToCreateShape(editor, info, {\n\t\t\t\t\t\tcreateShape: (id) =>\n\t\t\t\t\t\t\teditor.createShape({\n\t\t\t\t\t\t\t\tid,\n\t\t\t\t\t\t\t\ttype: 'arrow',\n\t\t\t\t\t\t\t\tprops: { start: { x: 0, y: 0 }, end: { x: 200, y: 0 } },\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t})\n\t\t\t\t\ttrackEvent('drag-tool', { source, id: 'arrow' })\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: 'line',\n\t\t\t\tlabel: 'tool.line',\n\t\t\t\ticon: 'tool-line',\n\t\t\t\tkbd: 'l',\n\t\t\t\tonSelect(source) {\n\t\t\t\t\teditor.setCurrentTool('line')\n\t\t\t\t\tonToolSelect(source, this)\n\t\t\t\t},\n\t\t\t\tonDragStart(source, info) {\n\t\t\t\t\tonDragFromToolbarToCreateShape(editor, info, {\n\t\t\t\t\t\tcreateShape: (id) => {\n\t\t\t\t\t\t\tconst [start, end] = getIndicesBetween(null, null, 2)\n\t\t\t\t\t\t\teditor.createShape<TLLineShape>({\n\t\t\t\t\t\t\t\tid,\n\t\t\t\t\t\t\t\ttype: 'line',\n\t\t\t\t\t\t\t\tprops: {\n\t\t\t\t\t\t\t\t\tpoints: {\n\t\t\t\t\t\t\t\t\t\t[start]: { id: start, index: start, x: 0, y: 200 },\n\t\t\t\t\t\t\t\t\t\t[end]: { id: end, index: end, x: 200, y: 0 },\n\t\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})\n\t\t\t\t\t\t},\n\t\t\t\t\t})\n\t\t\t\t\ttrackEvent('drag-tool', { source, id: 'line' })\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: 'frame',\n\t\t\t\tlabel: 'tool.frame',\n\t\t\t\ticon: 'tool-frame',\n\t\t\t\tkbd: 'f',\n\t\t\t\tonSelect(source) {\n\t\t\t\t\teditor.setCurrentTool('frame')\n\t\t\t\t\tonToolSelect(source, this)\n\t\t\t\t},\n\t\t\t\tonDragStart(source, info) {\n\t\t\t\t\tonDragFromToolbarToCreateShape(editor, info, {\n\t\t\t\t\t\tcreateShape: (id) => editor.createShape({ id, type: 'frame' }),\n\t\t\t\t\t})\n\t\t\t\t\ttrackEvent('drag-tool', { source, id: 'frame' })\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: 'text',\n\t\t\t\tlabel: 'tool.text',\n\t\t\t\ticon: 'tool-text',\n\t\t\t\tkbd: 't',\n\t\t\t\tonSelect(source) {\n\t\t\t\t\teditor.setCurrentTool('text')\n\t\t\t\t\tonToolSelect(source, this)\n\t\t\t\t},\n\t\t\t\tonDragStart(source, info) {\n\t\t\t\t\tonDragFromToolbarToCreateShape(editor, info, {\n\t\t\t\t\t\tcreateShape: (id) =>\n\t\t\t\t\t\t\teditor.createShape({ id, type: 'text', props: { richText: toRichText('Text') } }),\n\t\t\t\t\t\tonDragEnd: (id) => {\n\t\t\t\t\t\t\teditor.setEditingShape(id)\n\t\t\t\t\t\t\teditor.emit('select-all-text', { shapeId: id })\n\t\t\t\t\t\t},\n\t\t\t\t\t})\n\t\t\t\t\ttrackEvent('drag-tool', { source, id: 'text' })\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: 'asset',\n\t\t\t\tlabel: 'tool.media',\n\t\t\t\ticon: 'tool-media',\n\t\t\t\tkbd: 'cmd+u,ctrl+u',\n\t\t\t\tonSelect(source) {\n\t\t\t\t\thelpers.insertMedia()\n\t\t\t\t\tonToolSelect(source, this, 'media')\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: 'note',\n\t\t\t\tlabel: 'tool.note',\n\t\t\t\ticon: 'tool-note',\n\t\t\t\tkbd: 'n',\n\t\t\t\tonSelect(source) {\n\t\t\t\t\teditor.setCurrentTool('note')\n\t\t\t\t\tonToolSelect(source, this)\n\t\t\t\t},\n\t\t\t\tonDragStart(source, info) {\n\t\t\t\t\tonDragFromToolbarToCreateShape(editor, info, {\n\t\t\t\t\t\tcreateShape: (id) => editor.createShape({ id, type: 'note' }),\n\t\t\t\t\t\tonDragEnd: (id) => {\n\t\t\t\t\t\t\teditor.setEditingShape(id)\n\t\t\t\t\t\t\teditor.emit('select-all-text', { shapeId: id })\n\t\t\t\t\t\t},\n\t\t\t\t\t})\n\t\t\t\t\ttrackEvent('drag-tool', { source, id: 'note' })\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: 'laser',\n\t\t\t\tlabel: 'tool.laser',\n\t\t\t\treadonlyOk: true,\n\t\t\t\ticon: 'tool-laser',\n\t\t\t\tkbd: 'k',\n\t\t\t\tonSelect(source) {\n\t\t\t\t\teditor.setCurrentTool('laser')\n\t\t\t\t\tonToolSelect(source, this)\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: 'embed',\n\t\t\t\tlabel: 'tool.embed',\n\t\t\t\ticon: 'dot',\n\t\t\t\tonSelect(source) {\n\t\t\t\t\thelpers.addDialog({ component: EmbedDialog })\n\t\t\t\t\tonToolSelect(source, this)\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: 'highlight',\n\t\t\t\tlabel: 'tool.highlight',\n\t\t\t\ticon: 'tool-highlight',\n\t\t\t\t// TODO: pick a better shortcut\n\t\t\t\tkbd: 'shift+d',\n\t\t\t\tonSelect(source) {\n\t\t\t\t\teditor.setCurrentTool('highlight')\n\t\t\t\t\tonToolSelect(source, this)\n\t\t\t\t},\n\t\t\t},\n\t\t]\n\n\t\ttoolsArray.forEach((t) => (t.onSelect = t.onSelect.bind(t)))\n\n\t\tconst tools = Object.fromEntries(toolsArray.map((t) => [t.id, t]))\n\n\t\tif (overrides) {\n\t\t\treturn overrides(editor, tools, helpers)\n\t\t}\n\n\t\treturn tools\n\t}, [overrides, editor, helpers, onToolSelect, trackEvent])\n\n\treturn <ToolsContext.Provider value={tools}>{children}</ToolsContext.Provider>\n}\n\n/** @public */\nexport function useTools() {\n\tconst ctx = React.useContext(ToolsContext)\n\n\tif (!ctx) {\n\t\tthrow new Error('useTools must be used within a ToolProvider')\n\t}\n\n\treturn ctx\n}\n\n/**\n * Options for {@link onDragFromToolbarToCreateShape}.\n * @public\n */\nexport interface OnDragFromToolbarToCreateShapesOpts {\n\t/**\n\t * Create the shape being dragged. You don't need to worry about positioning it, as it'll be\n\t * immediately updated with the correct position.\n\t */\n\tcreateShape(id: TLShapeId): void\n\t/**\n\t * Called once the drag interaction has finished.\n\t */\n\tonDragEnd?(id: TLShapeId): void\n}\n\n/**\n * A helper method to use in {@link TLUiToolItem#onDragStart} to create a shape by dragging it from\n * the toolbar.\n * @public\n */\nexport function onDragFromToolbarToCreateShape(\n\teditor: Editor,\n\tinfo: TLPointerEventInfo,\n\topts: OnDragFromToolbarToCreateShapesOpts\n) {\n\tconst { x, y } = editor.inputs.currentPagePoint\n\n\tconst stoppingPoint = editor.markHistoryStoppingPoint('drag shape tool')\n\teditor.setCurrentTool('select.translating')\n\n\tconst id = createShapeId()\n\topts.createShape(id)\n\tconst shape = assertExists(editor.getShape(id), 'Shape not found')\n\n\tconst { w, h } = editor.getShapePageBounds(id)!\n\teditor.updateShape({ id, type: shape.type, x: x - w / 2, y: y - h / 2 })\n\teditor.select(id)\n\n\teditor.setCurrentTool('select.translating', {\n\t\t...info,\n\t\ttarget: 'shape',\n\t\tshape: editor.getShape(id),\n\t\tisCreating: true,\n\t\tcreatingMarkId: stoppingPoint,\n\t\tonCreate() {\n\t\t\teditor.setCurrentTool('select.idle')\n\t\t\teditor.select(id)\n\t\t\topts.onDragEnd?.(id)\n\t\t},\n\t})\n\n\teditor.getCurrentTool().setCurrentToolIdMask(shape.type)\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import {\n\tassertExists,\n\tcreateShapeId,\n\tEditor,\n\tGeoShapeGeoStyle,\n\tgetIndicesBetween,\n\tTLLineShape,\n\tTLPointerEventInfo,\n\tTLShapeId,\n\ttoRichText,\n\tuseMaybeEditor,\n} from '@tldraw/editor'\nimport * as React from 'react'\nimport { EmbedDialog } from '../components/EmbedDialog'\nimport { TLUiIconJsx } from '../components/primitives/TldrawUiIcon'\nimport { useA11y } from '../context/a11y'\nimport { TLUiEventSource, useUiEvents } from '../context/events'\nimport { TLUiIconType } from '../icon-types'\nimport { TLUiOverrideHelpers, useDefaultHelpers } from '../overrides'\nimport { TLUiTranslationKey } from './useTranslation/TLUiTranslationKey'\nimport { useTranslation } from './useTranslation/useTranslation'\n\n/** @public */\nexport interface TLUiToolItem<\n\tTranslationKey extends string = string,\n\tIconType extends string = string,\n> {\n\tid: string\n\tlabel: TranslationKey\n\tshortcutsLabel?: TranslationKey\n\ticon: IconType | TLUiIconJsx\n\tonSelect(source: TLUiEventSource): void\n\tonDragStart?(source: TLUiEventSource, info: TLPointerEventInfo): void\n\t/**\n\t * The keyboard shortcut for this tool. This is a string that can be a single key,\n\t * or a combination of keys.\n\t * For example, `cmd+z` or `cmd+shift+z` or `cmd+u,ctrl+u`, or just `v` or `a`.\n\t * We have backwards compatibility with the old system, where we used to use\n\t * symbols to denote cmd/alt/shift, using `!` for shift, `$` for cmd, and `?` for alt.\n\t */\n\tkbd?: string\n\treadonlyOk?: boolean\n\tmeta?: {\n\t\t[key: string]: any\n\t}\n}\n\n/** @public */\nexport type TLUiToolsContextType = Record<string, TLUiToolItem>\n\n/** @internal */\nexport const ToolsContext = React.createContext<null | TLUiToolsContextType>(null)\n\n/** @public */\nexport interface TLUiToolsProviderProps {\n\toverrides?(\n\t\teditor: Editor,\n\t\ttools: TLUiToolsContextType,\n\t\thelpers: Partial<TLUiOverrideHelpers>\n\t): TLUiToolsContextType\n\tchildren: React.ReactNode\n}\n\n/** @internal */\nexport function ToolsProvider({ overrides, children }: TLUiToolsProviderProps) {\n\tconst editor = useMaybeEditor()\n\tconst trackEvent = useUiEvents()\n\n\tconst a11y = useA11y()\n\tconst msg = useTranslation()\n\tconst helpers = useDefaultHelpers()\n\n\tconst onToolSelect = React.useCallback(\n\t\t(\n\t\t\tsource: TLUiEventSource,\n\t\t\ttool: TLUiToolItem<TLUiTranslationKey, TLUiIconType>,\n\t\t\tid?: string\n\t\t) => {\n\t\t\ta11y.announce({ msg: msg(tool.label) })\n\t\t\ttrackEvent('select-tool', { source, id: id ?? tool.id })\n\t\t},\n\t\t[a11y, msg, trackEvent]\n\t)\n\n\tconst tools = React.useMemo<TLUiToolsContextType>(() => {\n\t\tif (!editor) return {}\n\t\tconst toolsArray: TLUiToolItem<TLUiTranslationKey, TLUiIconType>[] = [\n\t\t\t{\n\t\t\t\tid: 'select',\n\t\t\t\tlabel: 'tool.select',\n\t\t\t\ticon: 'tool-pointer',\n\t\t\t\tkbd: 'v',\n\t\t\t\treadonlyOk: true,\n\t\t\t\tonSelect(source) {\n\t\t\t\t\tif (editor.isIn('select')) {\n\t\t\t\t\t\t// There's a quirk of select mode, where editing a shape is a sub-state of select.\n\t\t\t\t\t\t// Because the text tool can be locked/sticky, we need to make sure we exit the\n\t\t\t\t\t\t// text tool.\n\t\t\t\t\t\t//\n\t\t\t\t\t\t// psst, if you're changing this code, also change the code\n\t\t\t\t\t\t// in strange-tools.test.ts! Sadly it's duplicated there.\n\t\t\t\t\t\tconst currentNode = editor.root.getCurrent()!\n\t\t\t\t\t\tcurrentNode.exit({}, currentNode.id)\n\t\t\t\t\t\tcurrentNode.enter({}, currentNode.id)\n\t\t\t\t\t}\n\t\t\t\t\teditor.setCurrentTool('select')\n\t\t\t\t\tonToolSelect(source, this)\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: 'hand',\n\t\t\t\tlabel: 'tool.hand',\n\t\t\t\ticon: 'tool-hand',\n\t\t\t\tkbd: 'h',\n\t\t\t\treadonlyOk: true,\n\t\t\t\tonSelect(source) {\n\t\t\t\t\teditor.setCurrentTool('hand')\n\t\t\t\t\tonToolSelect(source, this)\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: 'eraser',\n\t\t\t\tlabel: 'tool.eraser',\n\t\t\t\ticon: 'tool-eraser',\n\t\t\t\tkbd: 'e',\n\t\t\t\tonSelect(source) {\n\t\t\t\t\teditor.setCurrentTool('eraser')\n\t\t\t\t\tonToolSelect(source, this)\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: 'draw',\n\t\t\t\tlabel: 'tool.draw',\n\t\t\t\ticon: 'tool-pencil',\n\t\t\t\tkbd: 'd,b,x',\n\t\t\t\tonSelect(source) {\n\t\t\t\t\teditor.setCurrentTool('draw')\n\t\t\t\t\tonToolSelect(source, this)\n\t\t\t\t},\n\t\t\t},\n\t\t\t...[...GeoShapeGeoStyle.values].map((geo) => ({\n\t\t\t\tid: geo,\n\t\t\t\tlabel: `tool.${geo}` as TLUiTranslationKey,\n\t\t\t\tmeta: {\n\t\t\t\t\tgeo,\n\t\t\t\t},\n\t\t\t\tkbd: geo === 'rectangle' ? 'r' : geo === 'ellipse' ? 'o' : undefined,\n\t\t\t\ticon: ('geo-' + geo) as TLUiIconType,\n\t\t\t\tonSelect(source: TLUiEventSource) {\n\t\t\t\t\teditor.run(() => {\n\t\t\t\t\t\teditor.setStyleForNextShapes(GeoShapeGeoStyle, geo)\n\t\t\t\t\t\teditor.setCurrentTool('geo')\n\t\t\t\t\t\tonToolSelect(source, this, `geo-${geo}`)\n\t\t\t\t\t})\n\t\t\t\t},\n\t\t\t\tonDragStart(source: TLUiEventSource, info: TLPointerEventInfo) {\n\t\t\t\t\tonDragFromToolbarToCreateShape(editor, info, {\n\t\t\t\t\t\tcreateShape: (id) =>\n\t\t\t\t\t\t\teditor.createShape({ id, type: 'geo', props: { w: 200, h: 200, geo } }),\n\t\t\t\t\t})\n\t\t\t\t\ttrackEvent('drag-tool', { source, id: 'geo' })\n\t\t\t\t},\n\t\t\t})),\n\t\t\t{\n\t\t\t\tid: 'arrow',\n\t\t\t\tlabel: 'tool.arrow',\n\t\t\t\ticon: 'tool-arrow',\n\t\t\t\tkbd: 'a',\n\t\t\t\tonSelect(source) {\n\t\t\t\t\teditor.setCurrentTool('arrow')\n\t\t\t\t\tonToolSelect(source, this)\n\t\t\t\t},\n\t\t\t\tonDragStart(source: TLUiEventSource, info: TLPointerEventInfo) {\n\t\t\t\t\tonDragFromToolbarToCreateShape(editor, info, {\n\t\t\t\t\t\tcreateShape: (id) =>\n\t\t\t\t\t\t\teditor.createShape({\n\t\t\t\t\t\t\t\tid,\n\t\t\t\t\t\t\t\ttype: 'arrow',\n\t\t\t\t\t\t\t\tprops: { start: { x: 0, y: 200 }, end: { x: 200, y: 0 } },\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t})\n\t\t\t\t\ttrackEvent('drag-tool', { source, id: 'arrow' })\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: 'line',\n\t\t\t\tlabel: 'tool.line',\n\t\t\t\ticon: 'tool-line',\n\t\t\t\tkbd: 'l',\n\t\t\t\tonSelect(source) {\n\t\t\t\t\teditor.setCurrentTool('line')\n\t\t\t\t\tonToolSelect(source, this)\n\t\t\t\t},\n\t\t\t\tonDragStart(source, info) {\n\t\t\t\t\tonDragFromToolbarToCreateShape(editor, info, {\n\t\t\t\t\t\tcreateShape: (id) => {\n\t\t\t\t\t\t\tconst [start, end] = getIndicesBetween(null, null, 2)\n\t\t\t\t\t\t\teditor.createShape<TLLineShape>({\n\t\t\t\t\t\t\t\tid,\n\t\t\t\t\t\t\t\ttype: 'line',\n\t\t\t\t\t\t\t\tprops: {\n\t\t\t\t\t\t\t\t\tpoints: {\n\t\t\t\t\t\t\t\t\t\t[start]: { id: start, index: start, x: 0, y: 200 },\n\t\t\t\t\t\t\t\t\t\t[end]: { id: end, index: end, x: 200, y: 0 },\n\t\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})\n\t\t\t\t\t\t},\n\t\t\t\t\t})\n\t\t\t\t\ttrackEvent('drag-tool', { source, id: 'line' })\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: 'frame',\n\t\t\t\tlabel: 'tool.frame',\n\t\t\t\ticon: 'tool-frame',\n\t\t\t\tkbd: 'f',\n\t\t\t\tonSelect(source) {\n\t\t\t\t\teditor.setCurrentTool('frame')\n\t\t\t\t\tonToolSelect(source, this)\n\t\t\t\t},\n\t\t\t\tonDragStart(source, info) {\n\t\t\t\t\tonDragFromToolbarToCreateShape(editor, info, {\n\t\t\t\t\t\tcreateShape: (id) => editor.createShape({ id, type: 'frame' }),\n\t\t\t\t\t})\n\t\t\t\t\ttrackEvent('drag-tool', { source, id: 'frame' })\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: 'text',\n\t\t\t\tlabel: 'tool.text',\n\t\t\t\ticon: 'tool-text',\n\t\t\t\tkbd: 't',\n\t\t\t\tonSelect(source) {\n\t\t\t\t\teditor.setCurrentTool('text')\n\t\t\t\t\tonToolSelect(source, this)\n\t\t\t\t},\n\t\t\t\tonDragStart(source, info) {\n\t\t\t\t\tonDragFromToolbarToCreateShape(editor, info, {\n\t\t\t\t\t\tcreateShape: (id) =>\n\t\t\t\t\t\t\teditor.createShape({ id, type: 'text', props: { richText: toRichText('Text') } }),\n\t\t\t\t\t\tonDragEnd: (id) => {\n\t\t\t\t\t\t\teditor.setEditingShape(id)\n\t\t\t\t\t\t\teditor.emit('select-all-text', { shapeId: id })\n\t\t\t\t\t\t},\n\t\t\t\t\t})\n\t\t\t\t\ttrackEvent('drag-tool', { source, id: 'text' })\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: 'asset',\n\t\t\t\tlabel: 'tool.media',\n\t\t\t\ticon: 'tool-media',\n\t\t\t\tkbd: 'cmd+u,ctrl+u',\n\t\t\t\tonSelect(source) {\n\t\t\t\t\thelpers.insertMedia()\n\t\t\t\t\tonToolSelect(source, this, 'media')\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: 'note',\n\t\t\t\tlabel: 'tool.note',\n\t\t\t\ticon: 'tool-note',\n\t\t\t\tkbd: 'n',\n\t\t\t\tonSelect(source) {\n\t\t\t\t\teditor.setCurrentTool('note')\n\t\t\t\t\tonToolSelect(source, this)\n\t\t\t\t},\n\t\t\t\tonDragStart(source, info) {\n\t\t\t\t\tonDragFromToolbarToCreateShape(editor, info, {\n\t\t\t\t\t\tcreateShape: (id) => editor.createShape({ id, type: 'note' }),\n\t\t\t\t\t\tonDragEnd: (id) => {\n\t\t\t\t\t\t\teditor.setEditingShape(id)\n\t\t\t\t\t\t\teditor.emit('select-all-text', { shapeId: id })\n\t\t\t\t\t\t},\n\t\t\t\t\t})\n\t\t\t\t\ttrackEvent('drag-tool', { source, id: 'note' })\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: 'laser',\n\t\t\t\tlabel: 'tool.laser',\n\t\t\t\treadonlyOk: true,\n\t\t\t\ticon: 'tool-laser',\n\t\t\t\tkbd: 'k',\n\t\t\t\tonSelect(source) {\n\t\t\t\t\teditor.setCurrentTool('laser')\n\t\t\t\t\tonToolSelect(source, this)\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: 'embed',\n\t\t\t\tlabel: 'tool.embed',\n\t\t\t\ticon: 'dot',\n\t\t\t\tonSelect(source) {\n\t\t\t\t\thelpers.addDialog({ component: EmbedDialog })\n\t\t\t\t\tonToolSelect(source, this)\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: 'highlight',\n\t\t\t\tlabel: 'tool.highlight',\n\t\t\t\ticon: 'tool-highlight',\n\t\t\t\t// TODO: pick a better shortcut\n\t\t\t\tkbd: 'shift+d',\n\t\t\t\tonSelect(source) {\n\t\t\t\t\teditor.setCurrentTool('highlight')\n\t\t\t\t\tonToolSelect(source, this)\n\t\t\t\t},\n\t\t\t},\n\t\t]\n\n\t\ttoolsArray.forEach((t) => (t.onSelect = t.onSelect.bind(t)))\n\n\t\tconst tools = Object.fromEntries(toolsArray.map((t) => [t.id, t]))\n\n\t\tif (overrides) {\n\t\t\treturn overrides(editor, tools, helpers)\n\t\t}\n\n\t\treturn tools\n\t}, [overrides, editor, helpers, onToolSelect, trackEvent])\n\n\treturn <ToolsContext.Provider value={tools}>{children}</ToolsContext.Provider>\n}\n\n/** @public */\nexport function useTools() {\n\tconst ctx = React.useContext(ToolsContext)\n\n\tif (!ctx) {\n\t\tthrow new Error('useTools must be used within a ToolProvider')\n\t}\n\n\treturn ctx\n}\n\n/**\n * Options for {@link onDragFromToolbarToCreateShape}.\n * @public\n */\nexport interface OnDragFromToolbarToCreateShapesOpts {\n\t/**\n\t * Create the shape being dragged. You don't need to worry about positioning it, as it'll be\n\t * immediately updated with the correct position.\n\t */\n\tcreateShape(id: TLShapeId): void\n\t/**\n\t * Called once the drag interaction has finished.\n\t */\n\tonDragEnd?(id: TLShapeId): void\n}\n\n/**\n * A helper method to use in {@link TLUiToolItem#onDragStart} to create a shape by dragging it from\n * the toolbar.\n * @public\n */\nexport function onDragFromToolbarToCreateShape(\n\teditor: Editor,\n\tinfo: TLPointerEventInfo,\n\topts: OnDragFromToolbarToCreateShapesOpts\n) {\n\tconst { x, y } = editor.inputs.currentPagePoint\n\n\tconst stoppingPoint = editor.markHistoryStoppingPoint('drag shape tool')\n\teditor.setCurrentTool('select.translating')\n\n\tconst id = createShapeId()\n\topts.createShape(id)\n\tconst shape = assertExists(editor.getShape(id), 'Shape not found')\n\n\tconst { w, h } = editor.getShapePageBounds(id)!\n\teditor.updateShape({ id, type: shape.type, x: x - w / 2, y: y - h / 2 })\n\teditor.select(id)\n\n\teditor.setCurrentTool('select.translating', {\n\t\t...info,\n\t\ttarget: 'shape',\n\t\tshape: editor.getShape(id),\n\t\tisCreating: true,\n\t\tcreatingMarkId: stoppingPoint,\n\t\tonCreate() {\n\t\t\teditor.setCurrentTool('select.idle')\n\t\t\teditor.select(id)\n\t\t\topts.onDragEnd?.(id)\n\t\t},\n\t})\n\n\teditor.getCurrentTool().setCurrentToolIdMask(shape.type)\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["tools"]
7
7
  }
@@ -1,8 +1,8 @@
1
- const version = "3.16.0-canary.66da2a5c5813";
1
+ const version = "3.16.0-canary.6c77a180e58d";
2
2
  const publishDates = {
3
3
  major: "2024-09-13T14:36:29.063Z",
4
- minor: "2025-09-02T14:51:14.201Z",
5
- patch: "2025-09-02T14:51:14.201Z"
4
+ minor: "2025-09-10T11:52:19.209Z",
5
+ patch: "2025-09-10T11:52:19.209Z"
6
6
  };
7
7
  export {
8
8
  publishDates,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/lib/ui/version.ts"],
4
- "sourcesContent": ["// This file is automatically generated by internal/scripts/refresh-assets.ts.\n// Do not edit manually. Or do, I'm a comment, not a cop.\n\nexport const version = '3.16.0-canary.66da2a5c5813'\nexport const publishDates = {\n\tmajor: '2024-09-13T14:36:29.063Z',\n\tminor: '2025-09-02T14:51:14.201Z',\n\tpatch: '2025-09-02T14:51:14.201Z',\n}\n"],
4
+ "sourcesContent": ["// This file is automatically generated by internal/scripts/refresh-assets.ts.\n// Do not edit manually. Or do, I'm a comment, not a cop.\n\nexport const version = '3.16.0-canary.6c77a180e58d'\nexport const publishDates = {\n\tmajor: '2024-09-13T14:36:29.063Z',\n\tminor: '2025-09-10T11:52:19.209Z',\n\tpatch: '2025-09-10T11:52:19.209Z',\n}\n"],
5
5
  "mappings": "AAGO,MAAM,UAAU;AAChB,MAAM,eAAe;AAAA,EAC3B,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AACR;",
6
6
  "names": []
7
7
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "tldraw",
3
3
  "description": "A tiny little drawing editor.",
4
- "version": "3.16.0-canary.66da2a5c5813",
4
+ "version": "3.16.0-canary.6c77a180e58d",
5
5
  "author": {
6
6
  "name": "tldraw Inc.",
7
7
  "email": "hello@tldraw.com"
@@ -55,8 +55,8 @@
55
55
  "@tiptap/pm": "^2.9.1",
56
56
  "@tiptap/react": "^2.9.1",
57
57
  "@tiptap/starter-kit": "^2.9.1",
58
- "@tldraw/editor": "3.16.0-canary.66da2a5c5813",
59
- "@tldraw/store": "3.16.0-canary.66da2a5c5813",
58
+ "@tldraw/editor": "3.16.0-canary.6c77a180e58d",
59
+ "@tldraw/store": "3.16.0-canary.6c77a180e58d",
60
60
  "classnames": "^2.5.1",
61
61
  "hotkeys-js": "^3.13.9",
62
62
  "idb": "^7.1.1",
@@ -227,6 +227,14 @@ interface ArrowheadInfo {
227
227
  hasEndArrowhead: boolean
228
228
  }
229
229
  export function getArrowLabelPosition(editor: Editor, shape: TLArrowShape) {
230
+ const isEditing = editor.getEditingShapeId() === shape.id
231
+ if (!isEditing && isEmptyRichText(shape.props.richText)) {
232
+ // Short-circuit for empty labels.
233
+ const bodyGeom = getArrowBodyGeometry(editor, shape)
234
+ const labelCenter = bodyGeom.interpolateAlongEdge(0.5)
235
+ return { box: Box.FromCenter(labelCenter, new Vec(0, 0)), debugGeom: [] }
236
+ }
237
+
230
238
  const debugGeom: Geometry2d[] = []
231
239
  const info = getArrowInfo(editor, shape)!
232
240
 
@@ -142,6 +142,7 @@ function TooltipSingleton() {
142
142
  const [isOpen, setIsOpen] = useState(false)
143
143
  const triggerRef = useRef<HTMLDivElement>(null)
144
144
  const isFirstShowRef = useRef(true)
145
+ const editor = useMaybeEditor()
145
146
 
146
147
  const currentTooltip = useValue(
147
148
  'current tooltip',
@@ -149,6 +150,15 @@ function TooltipSingleton() {
149
150
  []
150
151
  )
151
152
 
153
+ const cameraState = useValue('camera state', () => editor?.getCameraState(), [editor])
154
+
155
+ // Hide tooltip when camera is moving (panning/zooming)
156
+ useEffect(() => {
157
+ if (cameraState === 'moving' && isOpen && currentTooltip) {
158
+ tooltipManager.hideTooltip(editor, currentTooltip.id, true)
159
+ }
160
+ }, [cameraState, isOpen, currentTooltip, editor])
161
+
152
162
  // Update open state and trigger position
153
163
  useEffect(() => {
154
164
  let timer: ReturnType<typeof setTimeout> | null = null
@@ -235,8 +245,6 @@ export const TldrawUiTooltip = forwardRef<HTMLButtonElement, TldrawUiTooltipProp
235
245
  const orientationCtx = useTldrawUiOrientation()
236
246
  const sideToUse = side ?? orientationCtx.tooltipSide
237
247
 
238
- const camera = useValue('camera', () => editor?.getCamera(), [])
239
-
240
248
  useEffect(() => {
241
249
  const currentTooltipId = tooltipId.current
242
250
  return () => {
@@ -246,10 +254,6 @@ export const TldrawUiTooltip = forwardRef<HTMLButtonElement, TldrawUiTooltipProp
246
254
  }
247
255
  }, [editor, hasProvider])
248
256
 
249
- useEffect(() => {
250
- tooltipManager.hideTooltip(editor, tooltipId.current, true)
251
- }, [editor, camera])
252
-
253
257
  // Don't show tooltip if disabled, no content, or UI labels are disabled
254
258
  if (disabled || !content) {
255
259
  return <>{children}</>
@@ -176,7 +176,7 @@ export function ToolsProvider({ overrides, children }: TLUiToolsProviderProps) {
176
176
  editor.createShape({
177
177
  id,
178
178
  type: 'arrow',
179
- props: { start: { x: 0, y: 0 }, end: { x: 200, y: 0 } },
179
+ props: { start: { x: 0, y: 200 }, end: { x: 200, y: 0 } },
180
180
  }),
181
181
  })
182
182
  trackEvent('drag-tool', { source, id: 'arrow' })
@@ -1,9 +1,9 @@
1
1
  // This file is automatically generated by internal/scripts/refresh-assets.ts.
2
2
  // Do not edit manually. Or do, I'm a comment, not a cop.
3
3
 
4
- export const version = '3.16.0-canary.66da2a5c5813'
4
+ export const version = '3.16.0-canary.6c77a180e58d'
5
5
  export const publishDates = {
6
6
  major: '2024-09-13T14:36:29.063Z',
7
- minor: '2025-09-02T14:51:14.201Z',
8
- patch: '2025-09-02T14:51:14.201Z',
7
+ minor: '2025-09-10T11:52:19.209Z',
8
+ patch: '2025-09-10T11:52:19.209Z',
9
9
  }
@@ -1,12 +1,50 @@
1
- import { Box, TLShapeId, createShapeId } from '@tldraw/editor'
1
+ import {
2
+ BaseBoxShapeUtil,
3
+ Box,
4
+ RecordProps,
5
+ T,
6
+ TLBaseShape,
7
+ TLShapeId,
8
+ createShapeId,
9
+ } from '@tldraw/editor'
2
10
  import { vi } from 'vitest'
3
11
  import { TestEditor } from './TestEditor'
4
12
  import { TL } from './test-jsx'
5
13
 
14
+ // Custom uncullable shape type for testing canCull override
15
+ type UncullableShape = TLBaseShape<'uncullable', { w: number; h: number }>
16
+
17
+ class UncullableShapeUtil extends BaseBoxShapeUtil<UncullableShape> {
18
+ static override type = 'uncullable' as const
19
+ static override props: RecordProps<UncullableShape> = {
20
+ w: T.number,
21
+ h: T.number,
22
+ }
23
+
24
+ override canCull() {
25
+ return false
26
+ }
27
+
28
+ override getDefaultProps(): UncullableShape['props'] {
29
+ return {
30
+ w: 100,
31
+ h: 100,
32
+ }
33
+ }
34
+
35
+ override component() {
36
+ return <div>Uncullable shape</div>
37
+ }
38
+
39
+ override indicator() {
40
+ return <div>Uncullable shape</div>
41
+ }
42
+ }
43
+
6
44
  let editor: TestEditor
7
45
 
8
46
  beforeEach(() => {
9
- editor = new TestEditor()
47
+ editor = new TestEditor({ shapeUtils: [UncullableShapeUtil] })
10
48
  editor.setScreenBounds({ x: 0, y: 0, w: 1800, h: 900 })
11
49
  })
12
50
 
@@ -203,3 +241,34 @@ it('works for shapes that are outside of the viewport, but are then moved inside
203
241
  // Arrow should also not be culled
204
242
  expect(editor.getCulledShapes()).toEqual(new Set())
205
243
  })
244
+
245
+ it('respects canCull override - shapes that cannot be culled are never culled', () => {
246
+ const cullableShapeId = createShapeId()
247
+ const uncullableShapeId = createShapeId()
248
+
249
+ // Create both shapes outside the viewport
250
+ editor.createShapes([
251
+ {
252
+ id: cullableShapeId,
253
+ type: 'geo',
254
+ x: -2000, // Way outside viewport
255
+ y: -2000,
256
+ props: { w: 100, h: 100 },
257
+ },
258
+ {
259
+ id: uncullableShapeId,
260
+ type: 'uncullable',
261
+ x: -2000, // Way outside viewport
262
+ y: -2000,
263
+ props: { w: 100, h: 100 },
264
+ },
265
+ ])
266
+
267
+ const culledShapes = editor.getCulledShapes()
268
+
269
+ // The regular geo shape should be culled since it's outside the viewport
270
+ expect(culledShapes).toContain(cullableShapeId)
271
+
272
+ // The uncullable shape should NOT be culled even though it's outside the viewport
273
+ expect(culledShapes).not.toContain(uncullableShapeId)
274
+ })
package/tldraw.css CHANGED
@@ -693,11 +693,17 @@ input,
693
693
  }
694
694
 
695
695
  .tl-text-measure {
696
- position: absolute;
697
696
  z-index: var(--tl-layer-canvas-hidden);
697
+ opacity: 0;
698
+ visibility: hidden;
699
+
700
+ /* pointer-events: all; */
701
+ /* opacity: 1; */
702
+ /* z-index: 99999; */
703
+
704
+ position: absolute;
698
705
  top: 0px;
699
706
  left: 0px;
700
- opacity: 0;
701
707
  width: max-content;
702
708
  box-sizing: border-box;
703
709
  pointer-events: none;
@@ -708,7 +714,6 @@ input,
708
714
  border: none;
709
715
  user-select: none;
710
716
  contain: style paint;
711
- visibility: hidden;
712
717
  /* N.B. This property, while discouraged ("intended for Document Type Definition (DTD) designers") is necessary for ensuring correct mixed RTL/LTR behavior when exporting SVGs. */
713
718
  unicode-bidi: plaintext;
714
719
  -webkit-user-select: none;