tldraw 5.2.2 → 5.2.3

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/DOCS.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # tldraw Documentation
2
2
 
3
- Version: `5.2.2`
3
+ Version: `5.2.3`
4
4
 
5
5
  This file is generated during package publishing from the tldraw docs content for this exact package version.
6
6
 
package/RELEASE_NOTES.md CHANGED
@@ -1,10 +1,10 @@
1
1
  # Releases
2
2
 
3
- Version: `5.2.2`
3
+ Version: `5.2.3`
4
4
 
5
5
  This file is generated during package publishing from the tldraw release notes content for this exact package version.
6
6
 
7
- ## v5.2.2
7
+ ## v5.2.3
8
8
 
9
9
 
10
10
 
package/dist-cjs/index.js CHANGED
@@ -648,7 +648,7 @@ var import_buildFromV1Document = require("./lib/utils/tldr/buildFromV1Document")
648
648
  var import_file = require("./lib/utils/tldr/file");
649
649
  (0, import_editor.registerTldrawLibraryVersion)(
650
650
  "tldraw",
651
- "5.2.2",
651
+ "5.2.3",
652
652
  "cjs"
653
653
  );
654
654
  //# sourceMappingURL=index.js.map
@@ -31,8 +31,10 @@ class PointingHandle extends import_editor.StateNode {
31
31
  static id = "pointing_handle";
32
32
  didCtrlOnEnter = false;
33
33
  info = {};
34
+ isDoubleClick = false;
34
35
  onEnter(info) {
35
36
  this.info = info;
37
+ this.isDoubleClick = false;
36
38
  this.didCtrlOnEnter = info.accelKey;
37
39
  const { shape } = info;
38
40
  if (this.editor.isShapeOfType(shape, "arrow")) {
@@ -59,6 +61,16 @@ class PointingHandle extends import_editor.StateNode {
59
61
  }
60
62
  onPointerUp() {
61
63
  const { shape, handle } = this.info;
64
+ if (this.isDoubleClick) {
65
+ this.parent.transition("idle");
66
+ this.parent.getCurrent()?.handleEvent({
67
+ ...this.info,
68
+ type: "click",
69
+ name: "double_click",
70
+ phase: "down"
71
+ });
72
+ return;
73
+ }
62
74
  if (this.editor.isShapeOfType(shape, "note")) {
63
75
  const { editor } = this;
64
76
  const nextNote = getNoteForAdjacentPosition(editor, shape, handle, false);
@@ -73,8 +85,7 @@ class PointingHandle extends import_editor.StateNode {
73
85
  if (this.editor.inputs.getShiftKey() || info.phase !== "down" || info.ctrlKey || info.shiftKey) {
74
86
  return;
75
87
  }
76
- this.parent.transition("idle");
77
- this.parent.getCurrent()?.handleEvent(info);
88
+ this.isDoubleClick = true;
78
89
  }
79
90
  onPointerMove(info) {
80
91
  const { editor } = this;
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/lib/tools/SelectTool/childStates/PointingHandle.ts"],
4
- "sourcesContent": ["import {\n\tEditor,\n\tStateNode,\n\tTLClickEventInfo,\n\tTLHandle,\n\tTLNoteShape,\n\tTLPointerEventInfo,\n\tVec,\n} from '@tldraw/editor'\nimport { updateArrowTargetState } from '../../../shapes/arrow/arrowTargetState'\nimport { getArrowBindings } from '../../../shapes/arrow/shared'\nimport {\n\tgetNoteAdjacentPositions,\n\tgetNoteShapeForAdjacentPosition,\n} from '../../../shapes/note/noteHelpers'\nimport type { NoteShapeUtil } from '../../../shapes/note/NoteShapeUtil'\nimport { getDisplayValues } from '../../../shapes/shared/getDisplayValues'\nimport { startEditingShapeWithRichText } from '../selectHelpers'\n\nexport class PointingHandle extends StateNode {\n\tstatic override id = 'pointing_handle'\n\n\tdidCtrlOnEnter = false\n\n\tinfo = {} as TLPointerEventInfo & { target: 'handle' }\n\n\toverride onEnter(info: TLPointerEventInfo & { target: 'handle' }) {\n\t\tthis.info = info\n\n\t\tthis.didCtrlOnEnter = info.accelKey\n\n\t\tconst { shape } = info\n\t\tif (this.editor.isShapeOfType(shape, 'arrow')) {\n\t\t\tconst initialBindings = getArrowBindings(this.editor, shape)\n\t\t\tconst currentBinding = initialBindings[info.handle.id as 'start' | 'end']\n\t\t\tconst oppositeBinding = initialBindings[info.handle.id === 'start' ? 'end' : 'start']\n\t\t\tconst arrowTransform = this.editor.getShapePageTransform(shape.id)!\n\n\t\t\tif (currentBinding) {\n\t\t\t\tupdateArrowTargetState({\n\t\t\t\t\teditor: this.editor,\n\t\t\t\t\tpointInPageSpace: arrowTransform.applyToPoint(info.handle),\n\t\t\t\t\tarrow: shape,\n\t\t\t\t\tisPrecise: currentBinding.props.isPrecise,\n\t\t\t\t\tcurrentBinding: currentBinding,\n\t\t\t\t\toppositeBinding: oppositeBinding,\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\n\t\tthis.editor.setCursor({ type: 'grabbing', rotation: 0 })\n\t}\n\n\toverride onExit() {\n\t\tthis.editor.setHintingShapes([])\n\t\tthis.editor.setCursor({ type: 'default', rotation: 0 })\n\t}\n\n\toverride onPointerUp() {\n\t\tconst { shape, handle } = this.info\n\n\t\tif (this.editor.isShapeOfType(shape, 'note')) {\n\t\t\tconst { editor } = this\n\t\t\tconst nextNote = getNoteForAdjacentPosition(editor, shape, handle, false)\n\t\t\tif (nextNote) {\n\t\t\t\tstartEditingShapeWithRichText(editor, nextNote, { selectAll: true })\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\n\t\tthis.parent.transition('idle', this.info)\n\t}\n\n\toverride onDoubleClick(info: TLClickEventInfo) {\n\t\tif (\n\t\t\tthis.editor.inputs.getShiftKey() ||\n\t\t\tinfo.phase !== 'down' ||\n\t\t\tinfo.ctrlKey ||\n\t\t\tinfo.shiftKey\n\t\t) {\n\t\t\treturn\n\t\t}\n\n\t\tthis.parent.transition('idle')\n\t\tthis.parent.getCurrent()?.handleEvent(info)\n\t}\n\n\toverride onPointerMove(info: TLPointerEventInfo) {\n\t\tconst { editor } = this\n\t\tif (editor.inputs.getIsDragging()) {\n\t\t\tif (this.didCtrlOnEnter) {\n\t\t\t\tthis.parent.transition('brushing', info)\n\t\t\t} else {\n\t\t\t\tthis.startDraggingHandle()\n\t\t\t}\n\t\t}\n\t}\n\n\toverride onLongPress() {\n\t\tthis.startDraggingHandle()\n\t}\n\n\tprivate startDraggingHandle() {\n\t\tconst { editor } = this\n\t\tif (editor.getIsReadonly()) return\n\t\tconst { shape, handle } = this.info\n\n\t\tif (editor.isShapeOfType(shape, 'note')) {\n\t\t\tconst noteUtil = editor.getShapeUtil(shape) as NoteShapeUtil\n\t\t\tconst dv = getDisplayValues(noteUtil, shape)\n\n\t\t\tconst nextNote = getNoteForAdjacentPosition(editor, shape, handle, true)\n\t\t\tif (nextNote) {\n\t\t\t\t// Center the shape on the current pointer\n\t\t\t\tconst centeredOnPointer = editor\n\t\t\t\t\t.getPointInParentSpace(nextNote, editor.inputs.getOriginPagePoint())\n\t\t\t\t\t.sub(\n\t\t\t\t\t\tVec.Rot(\n\t\t\t\t\t\t\tnew Vec(dv.noteWidth / 2, dv.noteHeight / 2).mul(shape.props.scale),\n\t\t\t\t\t\t\tnextNote.rotation\n\t\t\t\t\t\t)\n\t\t\t\t\t)\n\t\t\t\teditor.updateShape({ ...nextNote, x: centeredOnPointer.x, y: centeredOnPointer.y })\n\n\t\t\t\t// Then select and begin translating the shape\n\t\t\t\teditor\n\t\t\t\t\t.setHoveredShape(nextNote.id) // important!\n\t\t\t\t\t.select(nextNote.id)\n\t\t\t\t\t.setCurrentTool('select.translating', {\n\t\t\t\t\t\t...this.info,\n\t\t\t\t\t\ttarget: 'shape',\n\t\t\t\t\t\tshape: editor.getShape(nextNote),\n\t\t\t\t\t\tonInteractionEnd: 'note',\n\t\t\t\t\t\tisCreating: true,\n\t\t\t\t\t\tonCreate: () => {\n\t\t\t\t\t\t\t// When we're done, start editing it\n\t\t\t\t\t\t\tstartEditingShapeWithRichText(editor, nextNote, { selectAll: true })\n\t\t\t\t\t\t},\n\t\t\t\t\t})\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\n\t\tthis.parent.transition('dragging_handle', this.info)\n\t}\n\n\toverride onCancel() {\n\t\tthis.cancel()\n\t}\n\n\toverride onComplete() {\n\t\tthis.cancel()\n\t}\n\n\toverride onInterrupt() {\n\t\tthis.cancel()\n\t}\n\n\tprivate cancel() {\n\t\tthis.parent.transition('idle')\n\t}\n}\n\nfunction getNoteForAdjacentPosition(\n\teditor: Editor,\n\tshape: TLNoteShape,\n\thandle: TLHandle,\n\tforceNew: boolean\n) {\n\tconst noteUtil = editor.getShapeUtil(shape) as NoteShapeUtil\n\tconst dv = getDisplayValues(noteUtil, shape)\n\n\tconst pageTransform = editor.getShapePageTransform(shape.id)!\n\tconst pagePoint = pageTransform.point()\n\tconst pageRotation = pageTransform.rotation()\n\tconst positions = getNoteAdjacentPositions(editor, {\n\t\tpagePoint,\n\t\tpageRotation,\n\t\tgrowY: shape.props.growY * shape.props.scale,\n\t\textraHeight: 0,\n\t\tscale: shape.props.scale,\n\t\tnoteWidth: dv.noteWidth,\n\t\tnoteHeight: dv.noteHeight,\n\t})\n\tconst position = positions[handle.index]\n\tif (position) {\n\t\treturn getNoteShapeForAdjacentPosition(editor, {\n\t\t\tshape,\n\t\t\tcenter: position,\n\t\t\tpageRotation,\n\t\t\tnoteWidth: dv.noteWidth,\n\t\t\tnoteHeight: dv.noteHeight,\n\t\t\tforceNew,\n\t\t})\n\t}\n\treturn undefined\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import {\n\tEditor,\n\tStateNode,\n\tTLClickEventInfo,\n\tTLHandle,\n\tTLNoteShape,\n\tTLPointerEventInfo,\n\tVec,\n} from '@tldraw/editor'\nimport { updateArrowTargetState } from '../../../shapes/arrow/arrowTargetState'\nimport { getArrowBindings } from '../../../shapes/arrow/shared'\nimport {\n\tgetNoteAdjacentPositions,\n\tgetNoteShapeForAdjacentPosition,\n} from '../../../shapes/note/noteHelpers'\nimport type { NoteShapeUtil } from '../../../shapes/note/NoteShapeUtil'\nimport { getDisplayValues } from '../../../shapes/shared/getDisplayValues'\nimport { startEditingShapeWithRichText } from '../selectHelpers'\n\nexport class PointingHandle extends StateNode {\n\tstatic override id = 'pointing_handle'\n\n\tdidCtrlOnEnter = false\n\n\tinfo = {} as TLPointerEventInfo & { target: 'handle' }\n\tisDoubleClick = false\n\n\toverride onEnter(info: TLPointerEventInfo & { target: 'handle' }) {\n\t\tthis.info = info\n\t\tthis.isDoubleClick = false\n\n\t\tthis.didCtrlOnEnter = info.accelKey\n\n\t\tconst { shape } = info\n\t\tif (this.editor.isShapeOfType(shape, 'arrow')) {\n\t\t\tconst initialBindings = getArrowBindings(this.editor, shape)\n\t\t\tconst currentBinding = initialBindings[info.handle.id as 'start' | 'end']\n\t\t\tconst oppositeBinding = initialBindings[info.handle.id === 'start' ? 'end' : 'start']\n\t\t\tconst arrowTransform = this.editor.getShapePageTransform(shape.id)!\n\n\t\t\tif (currentBinding) {\n\t\t\t\tupdateArrowTargetState({\n\t\t\t\t\teditor: this.editor,\n\t\t\t\t\tpointInPageSpace: arrowTransform.applyToPoint(info.handle),\n\t\t\t\t\tarrow: shape,\n\t\t\t\t\tisPrecise: currentBinding.props.isPrecise,\n\t\t\t\t\tcurrentBinding: currentBinding,\n\t\t\t\t\toppositeBinding: oppositeBinding,\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\n\t\tthis.editor.setCursor({ type: 'grabbing', rotation: 0 })\n\t}\n\n\toverride onExit() {\n\t\tthis.editor.setHintingShapes([])\n\t\tthis.editor.setCursor({ type: 'default', rotation: 0 })\n\t}\n\n\toverride onPointerUp() {\n\t\tconst { shape, handle } = this.info\n\n\t\tif (this.isDoubleClick) {\n\t\t\tthis.parent.transition('idle')\n\t\t\tthis.parent.getCurrent()?.handleEvent({\n\t\t\t\t...this.info,\n\t\t\t\ttype: 'click',\n\t\t\t\tname: 'double_click',\n\t\t\t\tphase: 'down',\n\t\t\t})\n\t\t\treturn\n\t\t}\n\n\t\tif (this.editor.isShapeOfType(shape, 'note')) {\n\t\t\tconst { editor } = this\n\t\t\tconst nextNote = getNoteForAdjacentPosition(editor, shape, handle, false)\n\t\t\tif (nextNote) {\n\t\t\t\tstartEditingShapeWithRichText(editor, nextNote, { selectAll: true })\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\n\t\tthis.parent.transition('idle', this.info)\n\t}\n\n\toverride onDoubleClick(info: TLClickEventInfo) {\n\t\tif (\n\t\t\tthis.editor.inputs.getShiftKey() ||\n\t\t\tinfo.phase !== 'down' ||\n\t\t\tinfo.ctrlKey ||\n\t\t\tinfo.shiftKey\n\t\t) {\n\t\t\treturn\n\t\t}\n\n\t\tthis.isDoubleClick = true\n\t}\n\n\toverride onPointerMove(info: TLPointerEventInfo) {\n\t\tconst { editor } = this\n\t\tif (editor.inputs.getIsDragging()) {\n\t\t\tif (this.didCtrlOnEnter) {\n\t\t\t\tthis.parent.transition('brushing', info)\n\t\t\t} else {\n\t\t\t\tthis.startDraggingHandle()\n\t\t\t}\n\t\t}\n\t}\n\n\toverride onLongPress() {\n\t\tthis.startDraggingHandle()\n\t}\n\n\tprivate startDraggingHandle() {\n\t\tconst { editor } = this\n\t\tif (editor.getIsReadonly()) return\n\t\tconst { shape, handle } = this.info\n\n\t\tif (editor.isShapeOfType(shape, 'note')) {\n\t\t\tconst noteUtil = editor.getShapeUtil(shape) as NoteShapeUtil\n\t\t\tconst dv = getDisplayValues(noteUtil, shape)\n\n\t\t\tconst nextNote = getNoteForAdjacentPosition(editor, shape, handle, true)\n\t\t\tif (nextNote) {\n\t\t\t\t// Center the shape on the current pointer\n\t\t\t\tconst centeredOnPointer = editor\n\t\t\t\t\t.getPointInParentSpace(nextNote, editor.inputs.getOriginPagePoint())\n\t\t\t\t\t.sub(\n\t\t\t\t\t\tVec.Rot(\n\t\t\t\t\t\t\tnew Vec(dv.noteWidth / 2, dv.noteHeight / 2).mul(shape.props.scale),\n\t\t\t\t\t\t\tnextNote.rotation\n\t\t\t\t\t\t)\n\t\t\t\t\t)\n\t\t\t\teditor.updateShape({ ...nextNote, x: centeredOnPointer.x, y: centeredOnPointer.y })\n\n\t\t\t\t// Then select and begin translating the shape\n\t\t\t\teditor\n\t\t\t\t\t.setHoveredShape(nextNote.id) // important!\n\t\t\t\t\t.select(nextNote.id)\n\t\t\t\t\t.setCurrentTool('select.translating', {\n\t\t\t\t\t\t...this.info,\n\t\t\t\t\t\ttarget: 'shape',\n\t\t\t\t\t\tshape: editor.getShape(nextNote),\n\t\t\t\t\t\tonInteractionEnd: 'note',\n\t\t\t\t\t\tisCreating: true,\n\t\t\t\t\t\tonCreate: () => {\n\t\t\t\t\t\t\t// When we're done, start editing it\n\t\t\t\t\t\t\tstartEditingShapeWithRichText(editor, nextNote, { selectAll: true })\n\t\t\t\t\t\t},\n\t\t\t\t\t})\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\n\t\tthis.parent.transition('dragging_handle', this.info)\n\t}\n\n\toverride onCancel() {\n\t\tthis.cancel()\n\t}\n\n\toverride onComplete() {\n\t\tthis.cancel()\n\t}\n\n\toverride onInterrupt() {\n\t\tthis.cancel()\n\t}\n\n\tprivate cancel() {\n\t\tthis.parent.transition('idle')\n\t}\n}\n\nfunction getNoteForAdjacentPosition(\n\teditor: Editor,\n\tshape: TLNoteShape,\n\thandle: TLHandle,\n\tforceNew: boolean\n) {\n\tconst noteUtil = editor.getShapeUtil(shape) as NoteShapeUtil\n\tconst dv = getDisplayValues(noteUtil, shape)\n\n\tconst pageTransform = editor.getShapePageTransform(shape.id)!\n\tconst pagePoint = pageTransform.point()\n\tconst pageRotation = pageTransform.rotation()\n\tconst positions = getNoteAdjacentPositions(editor, {\n\t\tpagePoint,\n\t\tpageRotation,\n\t\tgrowY: shape.props.growY * shape.props.scale,\n\t\textraHeight: 0,\n\t\tscale: shape.props.scale,\n\t\tnoteWidth: dv.noteWidth,\n\t\tnoteHeight: dv.noteHeight,\n\t})\n\tconst position = positions[handle.index]\n\tif (position) {\n\t\treturn getNoteShapeForAdjacentPosition(editor, {\n\t\t\tshape,\n\t\t\tcenter: position,\n\t\t\tpageRotation,\n\t\t\tnoteWidth: dv.noteWidth,\n\t\t\tnoteHeight: dv.noteHeight,\n\t\t\tforceNew,\n\t\t})\n\t}\n\treturn undefined\n}\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,oBAQO;AACP,8BAAuC;AACvC,oBAAiC;AACjC,yBAGO;AAEP,8BAAiC;AACjC,2BAA8C;AAEvC,MAAM,uBAAuB,wBAAU;AAAA,EAC7C,OAAgB,KAAK;AAAA,EAErB,iBAAiB;AAAA,EAEjB,OAAO,CAAC;AAAA,EACR,gBAAgB;AAAA,EAEP,QAAQ,MAAiD;AACjE,SAAK,OAAO;AACZ,SAAK,gBAAgB;AAErB,SAAK,iBAAiB,KAAK;AAE3B,UAAM,EAAE,MAAM,IAAI;AAClB,QAAI,KAAK,OAAO,cAAc,OAAO,OAAO,GAAG;AAC9C,YAAM,sBAAkB,gCAAiB,KAAK,QAAQ,KAAK;AAC3D,YAAM,iBAAiB,gBAAgB,KAAK,OAAO,EAAqB;AACxE,YAAM,kBAAkB,gBAAgB,KAAK,OAAO,OAAO,UAAU,QAAQ,OAAO;AACpF,YAAM,iBAAiB,KAAK,OAAO,sBAAsB,MAAM,EAAE;AAEjE,UAAI,gBAAgB;AACnB,4DAAuB;AAAA,UACtB,QAAQ,KAAK;AAAA,UACb,kBAAkB,eAAe,aAAa,KAAK,MAAM;AAAA,UACzD,OAAO;AAAA,UACP,WAAW,eAAe,MAAM;AAAA,UAChC;AAAA,UACA;AAAA,QACD,CAAC;AAAA,MACF;AAAA,IACD;AAEA,SAAK,OAAO,UAAU,EAAE,MAAM,YAAY,UAAU,EAAE,CAAC;AAAA,EACxD;AAAA,EAES,SAAS;AACjB,SAAK,OAAO,iBAAiB,CAAC,CAAC;AAC/B,SAAK,OAAO,UAAU,EAAE,MAAM,WAAW,UAAU,EAAE,CAAC;AAAA,EACvD;AAAA,EAES,cAAc;AACtB,UAAM,EAAE,OAAO,OAAO,IAAI,KAAK;AAE/B,QAAI,KAAK,eAAe;AACvB,WAAK,OAAO,WAAW,MAAM;AAC7B,WAAK,OAAO,WAAW,GAAG,YAAY;AAAA,QACrC,GAAG,KAAK;AAAA,QACR,MAAM;AAAA,QACN,MAAM;AAAA,QACN,OAAO;AAAA,MACR,CAAC;AACD;AAAA,IACD;AAEA,QAAI,KAAK,OAAO,cAAc,OAAO,MAAM,GAAG;AAC7C,YAAM,EAAE,OAAO,IAAI;AACnB,YAAM,WAAW,2BAA2B,QAAQ,OAAO,QAAQ,KAAK;AACxE,UAAI,UAAU;AACb,gEAA8B,QAAQ,UAAU,EAAE,WAAW,KAAK,CAAC;AACnE;AAAA,MACD;AAAA,IACD;AAEA,SAAK,OAAO,WAAW,QAAQ,KAAK,IAAI;AAAA,EACzC;AAAA,EAES,cAAc,MAAwB;AAC9C,QACC,KAAK,OAAO,OAAO,YAAY,KAC/B,KAAK,UAAU,UACf,KAAK,WACL,KAAK,UACJ;AACD;AAAA,IACD;AAEA,SAAK,gBAAgB;AAAA,EACtB;AAAA,EAES,cAAc,MAA0B;AAChD,UAAM,EAAE,OAAO,IAAI;AACnB,QAAI,OAAO,OAAO,cAAc,GAAG;AAClC,UAAI,KAAK,gBAAgB;AACxB,aAAK,OAAO,WAAW,YAAY,IAAI;AAAA,MACxC,OAAO;AACN,aAAK,oBAAoB;AAAA,MAC1B;AAAA,IACD;AAAA,EACD;AAAA,EAES,cAAc;AACtB,SAAK,oBAAoB;AAAA,EAC1B;AAAA,EAEQ,sBAAsB;AAC7B,UAAM,EAAE,OAAO,IAAI;AACnB,QAAI,OAAO,cAAc,EAAG;AAC5B,UAAM,EAAE,OAAO,OAAO,IAAI,KAAK;AAE/B,QAAI,OAAO,cAAc,OAAO,MAAM,GAAG;AACxC,YAAM,WAAW,OAAO,aAAa,KAAK;AAC1C,YAAM,SAAK,0CAAiB,UAAU,KAAK;AAE3C,YAAM,WAAW,2BAA2B,QAAQ,OAAO,QAAQ,IAAI;AACvE,UAAI,UAAU;AAEb,cAAM,oBAAoB,OACxB,sBAAsB,UAAU,OAAO,OAAO,mBAAmB,CAAC,EAClE;AAAA,UACA,kBAAI;AAAA,YACH,IAAI,kBAAI,GAAG,YAAY,GAAG,GAAG,aAAa,CAAC,EAAE,IAAI,MAAM,MAAM,KAAK;AAAA,YAClE,SAAS;AAAA,UACV;AAAA,QACD;AACD,eAAO,YAAY,EAAE,GAAG,UAAU,GAAG,kBAAkB,GAAG,GAAG,kBAAkB,EAAE,CAAC;AAGlF,eACE,gBAAgB,SAAS,EAAE,EAC3B,OAAO,SAAS,EAAE,EAClB,eAAe,sBAAsB;AAAA,UACrC,GAAG,KAAK;AAAA,UACR,QAAQ;AAAA,UACR,OAAO,OAAO,SAAS,QAAQ;AAAA,UAC/B,kBAAkB;AAAA,UAClB,YAAY;AAAA,UACZ,UAAU,MAAM;AAEf,oEAA8B,QAAQ,UAAU,EAAE,WAAW,KAAK,CAAC;AAAA,UACpE;AAAA,QACD,CAAC;AACF;AAAA,MACD;AAAA,IACD;AAEA,SAAK,OAAO,WAAW,mBAAmB,KAAK,IAAI;AAAA,EACpD;AAAA,EAES,WAAW;AACnB,SAAK,OAAO;AAAA,EACb;AAAA,EAES,aAAa;AACrB,SAAK,OAAO;AAAA,EACb;AAAA,EAES,cAAc;AACtB,SAAK,OAAO;AAAA,EACb;AAAA,EAEQ,SAAS;AAChB,SAAK,OAAO,WAAW,MAAM;AAAA,EAC9B;AACD;AAEA,SAAS,2BACR,QACA,OACA,QACA,UACC;AACD,QAAM,WAAW,OAAO,aAAa,KAAK;AAC1C,QAAM,SAAK,0CAAiB,UAAU,KAAK;AAE3C,QAAM,gBAAgB,OAAO,sBAAsB,MAAM,EAAE;AAC3D,QAAM,YAAY,cAAc,MAAM;AACtC,QAAM,eAAe,cAAc,SAAS;AAC5C,QAAM,gBAAY,6CAAyB,QAAQ;AAAA,IAClD;AAAA,IACA;AAAA,IACA,OAAO,MAAM,MAAM,QAAQ,MAAM,MAAM;AAAA,IACvC,aAAa;AAAA,IACb,OAAO,MAAM,MAAM;AAAA,IACnB,WAAW,GAAG;AAAA,IACd,YAAY,GAAG;AAAA,EAChB,CAAC;AACD,QAAM,WAAW,UAAU,OAAO,KAAK;AACvC,MAAI,UAAU;AACb,eAAO,oDAAgC,QAAQ;AAAA,MAC9C;AAAA,MACA,QAAQ;AAAA,MACR;AAAA,MACA,WAAW,GAAG;AAAA,MACd,YAAY,GAAG;AAAA,MACf;AAAA,IACD,CAAC;AAAA,EACF;AACA,SAAO;AACR;",
6
6
  "names": []
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 = "5.2.2";
25
+ const version = "5.2.3";
26
26
  const publishDates = {
27
27
  major: "2026-05-06T16:28:18.473Z",
28
28
  minor: "2026-07-01T09:20:01.735Z",
29
- patch: "2026-07-02T09:30:17.167Z"
29
+ patch: "2026-07-06T16:50:48.696Z"
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 = '5.2.2'\nexport const publishDates = {\n\tmajor: '2026-05-06T16:28:18.473Z',\n\tminor: '2026-07-01T09:20:01.735Z',\n\tpatch: '2026-07-02T09:30:17.167Z',\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 = '5.2.3'\nexport const publishDates = {\n\tmajor: '2026-05-06T16:28:18.473Z',\n\tminor: '2026-07-01T09:20:01.735Z',\n\tpatch: '2026-07-06T16:50:48.696Z',\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
  }
@@ -634,7 +634,7 @@ import {
634
634
  } from "./lib/utils/tldr/file.mjs";
635
635
  registerTldrawLibraryVersion(
636
636
  "tldraw",
637
- "5.2.2",
637
+ "5.2.3",
638
638
  "esm"
639
639
  );
640
640
  export {
@@ -14,8 +14,10 @@ class PointingHandle extends StateNode {
14
14
  static id = "pointing_handle";
15
15
  didCtrlOnEnter = false;
16
16
  info = {};
17
+ isDoubleClick = false;
17
18
  onEnter(info) {
18
19
  this.info = info;
20
+ this.isDoubleClick = false;
19
21
  this.didCtrlOnEnter = info.accelKey;
20
22
  const { shape } = info;
21
23
  if (this.editor.isShapeOfType(shape, "arrow")) {
@@ -42,6 +44,16 @@ class PointingHandle extends StateNode {
42
44
  }
43
45
  onPointerUp() {
44
46
  const { shape, handle } = this.info;
47
+ if (this.isDoubleClick) {
48
+ this.parent.transition("idle");
49
+ this.parent.getCurrent()?.handleEvent({
50
+ ...this.info,
51
+ type: "click",
52
+ name: "double_click",
53
+ phase: "down"
54
+ });
55
+ return;
56
+ }
45
57
  if (this.editor.isShapeOfType(shape, "note")) {
46
58
  const { editor } = this;
47
59
  const nextNote = getNoteForAdjacentPosition(editor, shape, handle, false);
@@ -56,8 +68,7 @@ class PointingHandle extends StateNode {
56
68
  if (this.editor.inputs.getShiftKey() || info.phase !== "down" || info.ctrlKey || info.shiftKey) {
57
69
  return;
58
70
  }
59
- this.parent.transition("idle");
60
- this.parent.getCurrent()?.handleEvent(info);
71
+ this.isDoubleClick = true;
61
72
  }
62
73
  onPointerMove(info) {
63
74
  const { editor } = this;
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/lib/tools/SelectTool/childStates/PointingHandle.ts"],
4
- "sourcesContent": ["import {\n\tEditor,\n\tStateNode,\n\tTLClickEventInfo,\n\tTLHandle,\n\tTLNoteShape,\n\tTLPointerEventInfo,\n\tVec,\n} from '@tldraw/editor'\nimport { updateArrowTargetState } from '../../../shapes/arrow/arrowTargetState'\nimport { getArrowBindings } from '../../../shapes/arrow/shared'\nimport {\n\tgetNoteAdjacentPositions,\n\tgetNoteShapeForAdjacentPosition,\n} from '../../../shapes/note/noteHelpers'\nimport type { NoteShapeUtil } from '../../../shapes/note/NoteShapeUtil'\nimport { getDisplayValues } from '../../../shapes/shared/getDisplayValues'\nimport { startEditingShapeWithRichText } from '../selectHelpers'\n\nexport class PointingHandle extends StateNode {\n\tstatic override id = 'pointing_handle'\n\n\tdidCtrlOnEnter = false\n\n\tinfo = {} as TLPointerEventInfo & { target: 'handle' }\n\n\toverride onEnter(info: TLPointerEventInfo & { target: 'handle' }) {\n\t\tthis.info = info\n\n\t\tthis.didCtrlOnEnter = info.accelKey\n\n\t\tconst { shape } = info\n\t\tif (this.editor.isShapeOfType(shape, 'arrow')) {\n\t\t\tconst initialBindings = getArrowBindings(this.editor, shape)\n\t\t\tconst currentBinding = initialBindings[info.handle.id as 'start' | 'end']\n\t\t\tconst oppositeBinding = initialBindings[info.handle.id === 'start' ? 'end' : 'start']\n\t\t\tconst arrowTransform = this.editor.getShapePageTransform(shape.id)!\n\n\t\t\tif (currentBinding) {\n\t\t\t\tupdateArrowTargetState({\n\t\t\t\t\teditor: this.editor,\n\t\t\t\t\tpointInPageSpace: arrowTransform.applyToPoint(info.handle),\n\t\t\t\t\tarrow: shape,\n\t\t\t\t\tisPrecise: currentBinding.props.isPrecise,\n\t\t\t\t\tcurrentBinding: currentBinding,\n\t\t\t\t\toppositeBinding: oppositeBinding,\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\n\t\tthis.editor.setCursor({ type: 'grabbing', rotation: 0 })\n\t}\n\n\toverride onExit() {\n\t\tthis.editor.setHintingShapes([])\n\t\tthis.editor.setCursor({ type: 'default', rotation: 0 })\n\t}\n\n\toverride onPointerUp() {\n\t\tconst { shape, handle } = this.info\n\n\t\tif (this.editor.isShapeOfType(shape, 'note')) {\n\t\t\tconst { editor } = this\n\t\t\tconst nextNote = getNoteForAdjacentPosition(editor, shape, handle, false)\n\t\t\tif (nextNote) {\n\t\t\t\tstartEditingShapeWithRichText(editor, nextNote, { selectAll: true })\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\n\t\tthis.parent.transition('idle', this.info)\n\t}\n\n\toverride onDoubleClick(info: TLClickEventInfo) {\n\t\tif (\n\t\t\tthis.editor.inputs.getShiftKey() ||\n\t\t\tinfo.phase !== 'down' ||\n\t\t\tinfo.ctrlKey ||\n\t\t\tinfo.shiftKey\n\t\t) {\n\t\t\treturn\n\t\t}\n\n\t\tthis.parent.transition('idle')\n\t\tthis.parent.getCurrent()?.handleEvent(info)\n\t}\n\n\toverride onPointerMove(info: TLPointerEventInfo) {\n\t\tconst { editor } = this\n\t\tif (editor.inputs.getIsDragging()) {\n\t\t\tif (this.didCtrlOnEnter) {\n\t\t\t\tthis.parent.transition('brushing', info)\n\t\t\t} else {\n\t\t\t\tthis.startDraggingHandle()\n\t\t\t}\n\t\t}\n\t}\n\n\toverride onLongPress() {\n\t\tthis.startDraggingHandle()\n\t}\n\n\tprivate startDraggingHandle() {\n\t\tconst { editor } = this\n\t\tif (editor.getIsReadonly()) return\n\t\tconst { shape, handle } = this.info\n\n\t\tif (editor.isShapeOfType(shape, 'note')) {\n\t\t\tconst noteUtil = editor.getShapeUtil(shape) as NoteShapeUtil\n\t\t\tconst dv = getDisplayValues(noteUtil, shape)\n\n\t\t\tconst nextNote = getNoteForAdjacentPosition(editor, shape, handle, true)\n\t\t\tif (nextNote) {\n\t\t\t\t// Center the shape on the current pointer\n\t\t\t\tconst centeredOnPointer = editor\n\t\t\t\t\t.getPointInParentSpace(nextNote, editor.inputs.getOriginPagePoint())\n\t\t\t\t\t.sub(\n\t\t\t\t\t\tVec.Rot(\n\t\t\t\t\t\t\tnew Vec(dv.noteWidth / 2, dv.noteHeight / 2).mul(shape.props.scale),\n\t\t\t\t\t\t\tnextNote.rotation\n\t\t\t\t\t\t)\n\t\t\t\t\t)\n\t\t\t\teditor.updateShape({ ...nextNote, x: centeredOnPointer.x, y: centeredOnPointer.y })\n\n\t\t\t\t// Then select and begin translating the shape\n\t\t\t\teditor\n\t\t\t\t\t.setHoveredShape(nextNote.id) // important!\n\t\t\t\t\t.select(nextNote.id)\n\t\t\t\t\t.setCurrentTool('select.translating', {\n\t\t\t\t\t\t...this.info,\n\t\t\t\t\t\ttarget: 'shape',\n\t\t\t\t\t\tshape: editor.getShape(nextNote),\n\t\t\t\t\t\tonInteractionEnd: 'note',\n\t\t\t\t\t\tisCreating: true,\n\t\t\t\t\t\tonCreate: () => {\n\t\t\t\t\t\t\t// When we're done, start editing it\n\t\t\t\t\t\t\tstartEditingShapeWithRichText(editor, nextNote, { selectAll: true })\n\t\t\t\t\t\t},\n\t\t\t\t\t})\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\n\t\tthis.parent.transition('dragging_handle', this.info)\n\t}\n\n\toverride onCancel() {\n\t\tthis.cancel()\n\t}\n\n\toverride onComplete() {\n\t\tthis.cancel()\n\t}\n\n\toverride onInterrupt() {\n\t\tthis.cancel()\n\t}\n\n\tprivate cancel() {\n\t\tthis.parent.transition('idle')\n\t}\n}\n\nfunction getNoteForAdjacentPosition(\n\teditor: Editor,\n\tshape: TLNoteShape,\n\thandle: TLHandle,\n\tforceNew: boolean\n) {\n\tconst noteUtil = editor.getShapeUtil(shape) as NoteShapeUtil\n\tconst dv = getDisplayValues(noteUtil, shape)\n\n\tconst pageTransform = editor.getShapePageTransform(shape.id)!\n\tconst pagePoint = pageTransform.point()\n\tconst pageRotation = pageTransform.rotation()\n\tconst positions = getNoteAdjacentPositions(editor, {\n\t\tpagePoint,\n\t\tpageRotation,\n\t\tgrowY: shape.props.growY * shape.props.scale,\n\t\textraHeight: 0,\n\t\tscale: shape.props.scale,\n\t\tnoteWidth: dv.noteWidth,\n\t\tnoteHeight: dv.noteHeight,\n\t})\n\tconst position = positions[handle.index]\n\tif (position) {\n\t\treturn getNoteShapeForAdjacentPosition(editor, {\n\t\t\tshape,\n\t\t\tcenter: position,\n\t\t\tpageRotation,\n\t\t\tnoteWidth: dv.noteWidth,\n\t\t\tnoteHeight: dv.noteHeight,\n\t\t\tforceNew,\n\t\t})\n\t}\n\treturn undefined\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import {\n\tEditor,\n\tStateNode,\n\tTLClickEventInfo,\n\tTLHandle,\n\tTLNoteShape,\n\tTLPointerEventInfo,\n\tVec,\n} from '@tldraw/editor'\nimport { updateArrowTargetState } from '../../../shapes/arrow/arrowTargetState'\nimport { getArrowBindings } from '../../../shapes/arrow/shared'\nimport {\n\tgetNoteAdjacentPositions,\n\tgetNoteShapeForAdjacentPosition,\n} from '../../../shapes/note/noteHelpers'\nimport type { NoteShapeUtil } from '../../../shapes/note/NoteShapeUtil'\nimport { getDisplayValues } from '../../../shapes/shared/getDisplayValues'\nimport { startEditingShapeWithRichText } from '../selectHelpers'\n\nexport class PointingHandle extends StateNode {\n\tstatic override id = 'pointing_handle'\n\n\tdidCtrlOnEnter = false\n\n\tinfo = {} as TLPointerEventInfo & { target: 'handle' }\n\tisDoubleClick = false\n\n\toverride onEnter(info: TLPointerEventInfo & { target: 'handle' }) {\n\t\tthis.info = info\n\t\tthis.isDoubleClick = false\n\n\t\tthis.didCtrlOnEnter = info.accelKey\n\n\t\tconst { shape } = info\n\t\tif (this.editor.isShapeOfType(shape, 'arrow')) {\n\t\t\tconst initialBindings = getArrowBindings(this.editor, shape)\n\t\t\tconst currentBinding = initialBindings[info.handle.id as 'start' | 'end']\n\t\t\tconst oppositeBinding = initialBindings[info.handle.id === 'start' ? 'end' : 'start']\n\t\t\tconst arrowTransform = this.editor.getShapePageTransform(shape.id)!\n\n\t\t\tif (currentBinding) {\n\t\t\t\tupdateArrowTargetState({\n\t\t\t\t\teditor: this.editor,\n\t\t\t\t\tpointInPageSpace: arrowTransform.applyToPoint(info.handle),\n\t\t\t\t\tarrow: shape,\n\t\t\t\t\tisPrecise: currentBinding.props.isPrecise,\n\t\t\t\t\tcurrentBinding: currentBinding,\n\t\t\t\t\toppositeBinding: oppositeBinding,\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\n\t\tthis.editor.setCursor({ type: 'grabbing', rotation: 0 })\n\t}\n\n\toverride onExit() {\n\t\tthis.editor.setHintingShapes([])\n\t\tthis.editor.setCursor({ type: 'default', rotation: 0 })\n\t}\n\n\toverride onPointerUp() {\n\t\tconst { shape, handle } = this.info\n\n\t\tif (this.isDoubleClick) {\n\t\t\tthis.parent.transition('idle')\n\t\t\tthis.parent.getCurrent()?.handleEvent({\n\t\t\t\t...this.info,\n\t\t\t\ttype: 'click',\n\t\t\t\tname: 'double_click',\n\t\t\t\tphase: 'down',\n\t\t\t})\n\t\t\treturn\n\t\t}\n\n\t\tif (this.editor.isShapeOfType(shape, 'note')) {\n\t\t\tconst { editor } = this\n\t\t\tconst nextNote = getNoteForAdjacentPosition(editor, shape, handle, false)\n\t\t\tif (nextNote) {\n\t\t\t\tstartEditingShapeWithRichText(editor, nextNote, { selectAll: true })\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\n\t\tthis.parent.transition('idle', this.info)\n\t}\n\n\toverride onDoubleClick(info: TLClickEventInfo) {\n\t\tif (\n\t\t\tthis.editor.inputs.getShiftKey() ||\n\t\t\tinfo.phase !== 'down' ||\n\t\t\tinfo.ctrlKey ||\n\t\t\tinfo.shiftKey\n\t\t) {\n\t\t\treturn\n\t\t}\n\n\t\tthis.isDoubleClick = true\n\t}\n\n\toverride onPointerMove(info: TLPointerEventInfo) {\n\t\tconst { editor } = this\n\t\tif (editor.inputs.getIsDragging()) {\n\t\t\tif (this.didCtrlOnEnter) {\n\t\t\t\tthis.parent.transition('brushing', info)\n\t\t\t} else {\n\t\t\t\tthis.startDraggingHandle()\n\t\t\t}\n\t\t}\n\t}\n\n\toverride onLongPress() {\n\t\tthis.startDraggingHandle()\n\t}\n\n\tprivate startDraggingHandle() {\n\t\tconst { editor } = this\n\t\tif (editor.getIsReadonly()) return\n\t\tconst { shape, handle } = this.info\n\n\t\tif (editor.isShapeOfType(shape, 'note')) {\n\t\t\tconst noteUtil = editor.getShapeUtil(shape) as NoteShapeUtil\n\t\t\tconst dv = getDisplayValues(noteUtil, shape)\n\n\t\t\tconst nextNote = getNoteForAdjacentPosition(editor, shape, handle, true)\n\t\t\tif (nextNote) {\n\t\t\t\t// Center the shape on the current pointer\n\t\t\t\tconst centeredOnPointer = editor\n\t\t\t\t\t.getPointInParentSpace(nextNote, editor.inputs.getOriginPagePoint())\n\t\t\t\t\t.sub(\n\t\t\t\t\t\tVec.Rot(\n\t\t\t\t\t\t\tnew Vec(dv.noteWidth / 2, dv.noteHeight / 2).mul(shape.props.scale),\n\t\t\t\t\t\t\tnextNote.rotation\n\t\t\t\t\t\t)\n\t\t\t\t\t)\n\t\t\t\teditor.updateShape({ ...nextNote, x: centeredOnPointer.x, y: centeredOnPointer.y })\n\n\t\t\t\t// Then select and begin translating the shape\n\t\t\t\teditor\n\t\t\t\t\t.setHoveredShape(nextNote.id) // important!\n\t\t\t\t\t.select(nextNote.id)\n\t\t\t\t\t.setCurrentTool('select.translating', {\n\t\t\t\t\t\t...this.info,\n\t\t\t\t\t\ttarget: 'shape',\n\t\t\t\t\t\tshape: editor.getShape(nextNote),\n\t\t\t\t\t\tonInteractionEnd: 'note',\n\t\t\t\t\t\tisCreating: true,\n\t\t\t\t\t\tonCreate: () => {\n\t\t\t\t\t\t\t// When we're done, start editing it\n\t\t\t\t\t\t\tstartEditingShapeWithRichText(editor, nextNote, { selectAll: true })\n\t\t\t\t\t\t},\n\t\t\t\t\t})\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\n\t\tthis.parent.transition('dragging_handle', this.info)\n\t}\n\n\toverride onCancel() {\n\t\tthis.cancel()\n\t}\n\n\toverride onComplete() {\n\t\tthis.cancel()\n\t}\n\n\toverride onInterrupt() {\n\t\tthis.cancel()\n\t}\n\n\tprivate cancel() {\n\t\tthis.parent.transition('idle')\n\t}\n}\n\nfunction getNoteForAdjacentPosition(\n\teditor: Editor,\n\tshape: TLNoteShape,\n\thandle: TLHandle,\n\tforceNew: boolean\n) {\n\tconst noteUtil = editor.getShapeUtil(shape) as NoteShapeUtil\n\tconst dv = getDisplayValues(noteUtil, shape)\n\n\tconst pageTransform = editor.getShapePageTransform(shape.id)!\n\tconst pagePoint = pageTransform.point()\n\tconst pageRotation = pageTransform.rotation()\n\tconst positions = getNoteAdjacentPositions(editor, {\n\t\tpagePoint,\n\t\tpageRotation,\n\t\tgrowY: shape.props.growY * shape.props.scale,\n\t\textraHeight: 0,\n\t\tscale: shape.props.scale,\n\t\tnoteWidth: dv.noteWidth,\n\t\tnoteHeight: dv.noteHeight,\n\t})\n\tconst position = positions[handle.index]\n\tif (position) {\n\t\treturn getNoteShapeForAdjacentPosition(editor, {\n\t\t\tshape,\n\t\t\tcenter: position,\n\t\t\tpageRotation,\n\t\t\tnoteWidth: dv.noteWidth,\n\t\t\tnoteHeight: dv.noteHeight,\n\t\t\tforceNew,\n\t\t})\n\t}\n\treturn undefined\n}\n"],
5
+ "mappings": "AAAA;AAAA,EAEC;AAAA,EAKA;AAAA,OACM;AACP,SAAS,8BAA8B;AACvC,SAAS,wBAAwB;AACjC;AAAA,EACC;AAAA,EACA;AAAA,OACM;AAEP,SAAS,wBAAwB;AACjC,SAAS,qCAAqC;AAEvC,MAAM,uBAAuB,UAAU;AAAA,EAC7C,OAAgB,KAAK;AAAA,EAErB,iBAAiB;AAAA,EAEjB,OAAO,CAAC;AAAA,EACR,gBAAgB;AAAA,EAEP,QAAQ,MAAiD;AACjE,SAAK,OAAO;AACZ,SAAK,gBAAgB;AAErB,SAAK,iBAAiB,KAAK;AAE3B,UAAM,EAAE,MAAM,IAAI;AAClB,QAAI,KAAK,OAAO,cAAc,OAAO,OAAO,GAAG;AAC9C,YAAM,kBAAkB,iBAAiB,KAAK,QAAQ,KAAK;AAC3D,YAAM,iBAAiB,gBAAgB,KAAK,OAAO,EAAqB;AACxE,YAAM,kBAAkB,gBAAgB,KAAK,OAAO,OAAO,UAAU,QAAQ,OAAO;AACpF,YAAM,iBAAiB,KAAK,OAAO,sBAAsB,MAAM,EAAE;AAEjE,UAAI,gBAAgB;AACnB,+BAAuB;AAAA,UACtB,QAAQ,KAAK;AAAA,UACb,kBAAkB,eAAe,aAAa,KAAK,MAAM;AAAA,UACzD,OAAO;AAAA,UACP,WAAW,eAAe,MAAM;AAAA,UAChC;AAAA,UACA;AAAA,QACD,CAAC;AAAA,MACF;AAAA,IACD;AAEA,SAAK,OAAO,UAAU,EAAE,MAAM,YAAY,UAAU,EAAE,CAAC;AAAA,EACxD;AAAA,EAES,SAAS;AACjB,SAAK,OAAO,iBAAiB,CAAC,CAAC;AAC/B,SAAK,OAAO,UAAU,EAAE,MAAM,WAAW,UAAU,EAAE,CAAC;AAAA,EACvD;AAAA,EAES,cAAc;AACtB,UAAM,EAAE,OAAO,OAAO,IAAI,KAAK;AAE/B,QAAI,KAAK,eAAe;AACvB,WAAK,OAAO,WAAW,MAAM;AAC7B,WAAK,OAAO,WAAW,GAAG,YAAY;AAAA,QACrC,GAAG,KAAK;AAAA,QACR,MAAM;AAAA,QACN,MAAM;AAAA,QACN,OAAO;AAAA,MACR,CAAC;AACD;AAAA,IACD;AAEA,QAAI,KAAK,OAAO,cAAc,OAAO,MAAM,GAAG;AAC7C,YAAM,EAAE,OAAO,IAAI;AACnB,YAAM,WAAW,2BAA2B,QAAQ,OAAO,QAAQ,KAAK;AACxE,UAAI,UAAU;AACb,sCAA8B,QAAQ,UAAU,EAAE,WAAW,KAAK,CAAC;AACnE;AAAA,MACD;AAAA,IACD;AAEA,SAAK,OAAO,WAAW,QAAQ,KAAK,IAAI;AAAA,EACzC;AAAA,EAES,cAAc,MAAwB;AAC9C,QACC,KAAK,OAAO,OAAO,YAAY,KAC/B,KAAK,UAAU,UACf,KAAK,WACL,KAAK,UACJ;AACD;AAAA,IACD;AAEA,SAAK,gBAAgB;AAAA,EACtB;AAAA,EAES,cAAc,MAA0B;AAChD,UAAM,EAAE,OAAO,IAAI;AACnB,QAAI,OAAO,OAAO,cAAc,GAAG;AAClC,UAAI,KAAK,gBAAgB;AACxB,aAAK,OAAO,WAAW,YAAY,IAAI;AAAA,MACxC,OAAO;AACN,aAAK,oBAAoB;AAAA,MAC1B;AAAA,IACD;AAAA,EACD;AAAA,EAES,cAAc;AACtB,SAAK,oBAAoB;AAAA,EAC1B;AAAA,EAEQ,sBAAsB;AAC7B,UAAM,EAAE,OAAO,IAAI;AACnB,QAAI,OAAO,cAAc,EAAG;AAC5B,UAAM,EAAE,OAAO,OAAO,IAAI,KAAK;AAE/B,QAAI,OAAO,cAAc,OAAO,MAAM,GAAG;AACxC,YAAM,WAAW,OAAO,aAAa,KAAK;AAC1C,YAAM,KAAK,iBAAiB,UAAU,KAAK;AAE3C,YAAM,WAAW,2BAA2B,QAAQ,OAAO,QAAQ,IAAI;AACvE,UAAI,UAAU;AAEb,cAAM,oBAAoB,OACxB,sBAAsB,UAAU,OAAO,OAAO,mBAAmB,CAAC,EAClE;AAAA,UACA,IAAI;AAAA,YACH,IAAI,IAAI,GAAG,YAAY,GAAG,GAAG,aAAa,CAAC,EAAE,IAAI,MAAM,MAAM,KAAK;AAAA,YAClE,SAAS;AAAA,UACV;AAAA,QACD;AACD,eAAO,YAAY,EAAE,GAAG,UAAU,GAAG,kBAAkB,GAAG,GAAG,kBAAkB,EAAE,CAAC;AAGlF,eACE,gBAAgB,SAAS,EAAE,EAC3B,OAAO,SAAS,EAAE,EAClB,eAAe,sBAAsB;AAAA,UACrC,GAAG,KAAK;AAAA,UACR,QAAQ;AAAA,UACR,OAAO,OAAO,SAAS,QAAQ;AAAA,UAC/B,kBAAkB;AAAA,UAClB,YAAY;AAAA,UACZ,UAAU,MAAM;AAEf,0CAA8B,QAAQ,UAAU,EAAE,WAAW,KAAK,CAAC;AAAA,UACpE;AAAA,QACD,CAAC;AACF;AAAA,MACD;AAAA,IACD;AAEA,SAAK,OAAO,WAAW,mBAAmB,KAAK,IAAI;AAAA,EACpD;AAAA,EAES,WAAW;AACnB,SAAK,OAAO;AAAA,EACb;AAAA,EAES,aAAa;AACrB,SAAK,OAAO;AAAA,EACb;AAAA,EAES,cAAc;AACtB,SAAK,OAAO;AAAA,EACb;AAAA,EAEQ,SAAS;AAChB,SAAK,OAAO,WAAW,MAAM;AAAA,EAC9B;AACD;AAEA,SAAS,2BACR,QACA,OACA,QACA,UACC;AACD,QAAM,WAAW,OAAO,aAAa,KAAK;AAC1C,QAAM,KAAK,iBAAiB,UAAU,KAAK;AAE3C,QAAM,gBAAgB,OAAO,sBAAsB,MAAM,EAAE;AAC3D,QAAM,YAAY,cAAc,MAAM;AACtC,QAAM,eAAe,cAAc,SAAS;AAC5C,QAAM,YAAY,yBAAyB,QAAQ;AAAA,IAClD;AAAA,IACA;AAAA,IACA,OAAO,MAAM,MAAM,QAAQ,MAAM,MAAM;AAAA,IACvC,aAAa;AAAA,IACb,OAAO,MAAM,MAAM;AAAA,IACnB,WAAW,GAAG;AAAA,IACd,YAAY,GAAG;AAAA,EAChB,CAAC;AACD,QAAM,WAAW,UAAU,OAAO,KAAK;AACvC,MAAI,UAAU;AACb,WAAO,gCAAgC,QAAQ;AAAA,MAC9C;AAAA,MACA,QAAQ;AAAA,MACR;AAAA,MACA,WAAW,GAAG;AAAA,MACd,YAAY,GAAG;AAAA,MACf;AAAA,IACD,CAAC;AAAA,EACF;AACA,SAAO;AACR;",
6
6
  "names": []
7
7
  }
@@ -1,8 +1,8 @@
1
- const version = "5.2.2";
1
+ const version = "5.2.3";
2
2
  const publishDates = {
3
3
  major: "2026-05-06T16:28:18.473Z",
4
4
  minor: "2026-07-01T09:20:01.735Z",
5
- patch: "2026-07-02T09:30:17.167Z"
5
+ patch: "2026-07-06T16:50:48.696Z"
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 = '5.2.2'\nexport const publishDates = {\n\tmajor: '2026-05-06T16:28:18.473Z',\n\tminor: '2026-07-01T09:20:01.735Z',\n\tpatch: '2026-07-02T09:30:17.167Z',\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 = '5.2.3'\nexport const publishDates = {\n\tmajor: '2026-05-06T16:28:18.473Z',\n\tminor: '2026-07-01T09:20:01.735Z',\n\tpatch: '2026-07-06T16:50:48.696Z',\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": "5.2.2",
4
+ "version": "5.2.3",
5
5
  "author": {
6
6
  "name": "tldraw Inc.",
7
7
  "email": "hello@tldraw.com"
@@ -63,9 +63,9 @@
63
63
  "@tiptap/pm": "^3.12.1",
64
64
  "@tiptap/react": "^3.12.1",
65
65
  "@tiptap/starter-kit": "^3.12.1",
66
- "@tldraw/driver": "5.2.2",
67
- "@tldraw/editor": "5.2.2",
68
- "@tldraw/store": "5.2.2",
66
+ "@tldraw/driver": "5.2.3",
67
+ "@tldraw/editor": "5.2.3",
68
+ "@tldraw/store": "5.2.3",
69
69
  "classnames": "^2.5.1",
70
70
  "idb": "^7.1.1",
71
71
  "lz-string": "^1.5.0",
@@ -22,6 +22,7 @@ const ids = {
22
22
  box1: createShapeId('box1'),
23
23
  box2: createShapeId('box2'),
24
24
  box3: createShapeId('box3'),
25
+ arrow1: createShapeId('arrow1'),
25
26
  }
26
27
 
27
28
  function bindings(id: TLShapeId) {
@@ -136,6 +137,72 @@ describe('When dragging the arrow', () => {
136
137
  })
137
138
  })
138
139
 
140
+ describe('When double-clicking an arrow handle', () => {
141
+ function createArrow() {
142
+ editor.selectAll().deleteShapes(editor.getSelectedShapeIds())
143
+ editor.createShapes([
144
+ {
145
+ id: ids.arrow1,
146
+ type: 'arrow',
147
+ x: 0,
148
+ y: 0,
149
+ props: {
150
+ start: { x: 0, y: 0 },
151
+ end: { x: 100, y: 100 },
152
+ arrowheadEnd: 'arrow',
153
+ },
154
+ },
155
+ ])
156
+ editor.select(ids.arrow1)
157
+ return editor.getShape(ids.arrow1) as TLArrowShape
158
+ }
159
+
160
+ function getEndHandle() {
161
+ return { id: 'end', type: 'vertex' as const, index: 'a0' as IndexKey, x: 100, y: 100 }
162
+ }
163
+
164
+ function getEndHandleInfo() {
165
+ return {
166
+ target: 'handle' as const,
167
+ shape: editor.getShape(ids.arrow1) as TLArrowShape,
168
+ handle: getEndHandle(),
169
+ }
170
+ }
171
+
172
+ beforeEach(() => {
173
+ editor._transformPointerDownSpy.mockRestore()
174
+ editor._transformPointerUpSpy.mockRestore()
175
+ })
176
+
177
+ it('toggles the arrowhead on the second pointer up', () => {
178
+ createArrow()
179
+
180
+ editor.pointerDown(100, 100, getEndHandleInfo()).pointerUp(100, 100, getEndHandleInfo())
181
+ expect((editor.getShape(ids.arrow1) as TLArrowShape).props.arrowheadEnd).toBe('arrow')
182
+
183
+ editor.pointerDown(100, 100, getEndHandleInfo())
184
+ expect((editor.getShape(ids.arrow1) as TLArrowShape).props.arrowheadEnd).toBe('arrow')
185
+ editor.expectToBeIn('select.pointing_handle')
186
+
187
+ editor.pointerUp(100, 100, getEndHandleInfo())
188
+ expect((editor.getShape(ids.arrow1) as TLArrowShape).props.arrowheadEnd).toBe('none')
189
+ })
190
+
191
+ it('starts a drag instead of toggling when the second click moves', () => {
192
+ createArrow()
193
+
194
+ editor.pointerDown(100, 100, getEndHandleInfo()).pointerUp(100, 100, getEndHandleInfo())
195
+
196
+ editor.pointerDown(100, 100, getEndHandleInfo())
197
+ expect((editor.getShape(ids.arrow1) as TLArrowShape).props.arrowheadEnd).toBe('arrow')
198
+
199
+ editor.pointerMove(140, 140)
200
+
201
+ editor.expectToBeIn('select.dragging_handle')
202
+ expect((editor.getShape(ids.arrow1) as TLArrowShape).props.arrowheadEnd).toBe('arrow')
203
+ })
204
+ })
205
+
139
206
  describe('When pointing a start shape', () => {
140
207
  it('binds to the top shape', () => {
141
208
  editor.setCurrentTool('arrow').pointerDown(375, 375)
@@ -23,9 +23,11 @@ export class PointingHandle extends StateNode {
23
23
  didCtrlOnEnter = false
24
24
 
25
25
  info = {} as TLPointerEventInfo & { target: 'handle' }
26
+ isDoubleClick = false
26
27
 
27
28
  override onEnter(info: TLPointerEventInfo & { target: 'handle' }) {
28
29
  this.info = info
30
+ this.isDoubleClick = false
29
31
 
30
32
  this.didCtrlOnEnter = info.accelKey
31
33
 
@@ -59,6 +61,17 @@ export class PointingHandle extends StateNode {
59
61
  override onPointerUp() {
60
62
  const { shape, handle } = this.info
61
63
 
64
+ if (this.isDoubleClick) {
65
+ this.parent.transition('idle')
66
+ this.parent.getCurrent()?.handleEvent({
67
+ ...this.info,
68
+ type: 'click',
69
+ name: 'double_click',
70
+ phase: 'down',
71
+ })
72
+ return
73
+ }
74
+
62
75
  if (this.editor.isShapeOfType(shape, 'note')) {
63
76
  const { editor } = this
64
77
  const nextNote = getNoteForAdjacentPosition(editor, shape, handle, false)
@@ -81,8 +94,7 @@ export class PointingHandle extends StateNode {
81
94
  return
82
95
  }
83
96
 
84
- this.parent.transition('idle')
85
- this.parent.getCurrent()?.handleEvent(info)
97
+ this.isDoubleClick = true
86
98
  }
87
99
 
88
100
  override onPointerMove(info: TLPointerEventInfo) {
@@ -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 = '5.2.2'
4
+ export const version = '5.2.3'
5
5
  export const publishDates = {
6
6
  major: '2026-05-06T16:28:18.473Z',
7
7
  minor: '2026-07-01T09:20:01.735Z',
8
- patch: '2026-07-02T09:30:17.167Z',
8
+ patch: '2026-07-06T16:50:48.696Z',
9
9
  }