tldraw 4.3.0-canary.d039f3a1ab8f → 4.3.0-canary.d428e9e9a7c6
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 +1 -1
- package/dist-cjs/lib/tools/SelectTool/childStates/EditingShape.js +30 -10
- package/dist-cjs/lib/tools/SelectTool/childStates/EditingShape.js.map +2 -2
- package/dist-cjs/lib/ui/version.js +3 -3
- package/dist-cjs/lib/ui/version.js.map +1 -1
- package/dist-esm/index.mjs +1 -1
- package/dist-esm/lib/tools/SelectTool/childStates/EditingShape.mjs +30 -10
- package/dist-esm/lib/tools/SelectTool/childStates/EditingShape.mjs.map +2 -2
- package/dist-esm/lib/ui/version.mjs +3 -3
- package/dist-esm/lib/ui/version.mjs.map +1 -1
- package/package.json +3 -3
- package/src/lib/tools/SelectTool/childStates/EditingShape.ts +44 -11
- package/src/lib/ui/version.ts +3 -3
package/dist-cjs/index.js
CHANGED
|
@@ -585,7 +585,7 @@ var import_buildFromV1Document = require("./lib/utils/tldr/buildFromV1Document")
|
|
|
585
585
|
var import_file = require("./lib/utils/tldr/file");
|
|
586
586
|
(0, import_editor.registerTldrawLibraryVersion)(
|
|
587
587
|
"tldraw",
|
|
588
|
-
"4.3.0-canary.
|
|
588
|
+
"4.3.0-canary.d428e9e9a7c6",
|
|
589
589
|
"cjs"
|
|
590
590
|
);
|
|
591
591
|
//# sourceMappingURL=index.js.map
|
|
@@ -28,12 +28,18 @@ var import_getHitShapeOnCanvasPointerDown = require("../../selection-logic/getHi
|
|
|
28
28
|
var import_updateHoveredShapeId = require("../../selection-logic/updateHoveredShapeId");
|
|
29
29
|
class EditingShape extends import_editor.StateNode {
|
|
30
30
|
static id = "editing_shape";
|
|
31
|
-
|
|
31
|
+
hitLabelOnShapeForPointerUp = null;
|
|
32
32
|
info = {};
|
|
33
|
+
didPointerDownOnEditingShape = false;
|
|
34
|
+
isTextInputFocused() {
|
|
35
|
+
const container = this.editor.getContainer();
|
|
36
|
+
return container.contains(document.activeElement) && (document.activeElement?.nodeName === "INPUT" || document.activeElement?.nodeName === "TEXTAREA" || document.activeElement?.isContentEditable);
|
|
37
|
+
}
|
|
33
38
|
onEnter(info) {
|
|
34
39
|
const editingShape = this.editor.getEditingShape();
|
|
35
40
|
if (!editingShape) throw Error("Entered editing state without an editing shape");
|
|
36
|
-
this.
|
|
41
|
+
this.hitLabelOnShapeForPointerUp = null;
|
|
42
|
+
this.didPointerDownOnEditingShape = false;
|
|
37
43
|
this.info = info;
|
|
38
44
|
if (info.isCreatingTextWhileToolLocked) {
|
|
39
45
|
this.parent.setCurrentToolIdMask("text");
|
|
@@ -52,14 +58,26 @@ class EditingShape extends import_editor.StateNode {
|
|
|
52
58
|
}
|
|
53
59
|
}
|
|
54
60
|
onPointerMove(info) {
|
|
55
|
-
if (this.
|
|
61
|
+
if (this.hitLabelOnShapeForPointerUp && this.editor.inputs.isDragging) {
|
|
56
62
|
if (this.editor.getIsReadonly()) return;
|
|
57
|
-
if (this.
|
|
58
|
-
this.editor.select(this.
|
|
63
|
+
if (this.hitLabelOnShapeForPointerUp.isLocked) return;
|
|
64
|
+
this.editor.select(this.hitLabelOnShapeForPointerUp);
|
|
59
65
|
this.parent.transition("translating", info);
|
|
60
|
-
this.
|
|
66
|
+
this.hitLabelOnShapeForPointerUp = null;
|
|
61
67
|
return;
|
|
62
68
|
}
|
|
69
|
+
if (this.didPointerDownOnEditingShape && this.editor.inputs.isDragging) {
|
|
70
|
+
if (this.editor.getIsReadonly()) return;
|
|
71
|
+
const editingShape = this.editor.getEditingShape();
|
|
72
|
+
if (!editingShape || editingShape.isLocked) return;
|
|
73
|
+
if (!this.isTextInputFocused()) {
|
|
74
|
+
this.editor.select(editingShape);
|
|
75
|
+
this.parent.transition("translating", info);
|
|
76
|
+
this.didPointerDownOnEditingShape = false;
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
this.didPointerDownOnEditingShape = false;
|
|
80
|
+
}
|
|
63
81
|
switch (info.target) {
|
|
64
82
|
case "shape":
|
|
65
83
|
case "canvas": {
|
|
@@ -69,7 +87,8 @@ class EditingShape extends import_editor.StateNode {
|
|
|
69
87
|
}
|
|
70
88
|
}
|
|
71
89
|
onPointerDown(info) {
|
|
72
|
-
this.
|
|
90
|
+
this.hitLabelOnShapeForPointerUp = null;
|
|
91
|
+
this.didPointerDownOnEditingShape = false;
|
|
73
92
|
switch (info.target) {
|
|
74
93
|
// N.B. This bit of logic has a bit of history to it.
|
|
75
94
|
// There was a PR that got rid of this logic: https://github.com/tldraw/tldraw/pull/4237
|
|
@@ -109,9 +128,10 @@ class EditingShape extends import_editor.StateNode {
|
|
|
109
128
|
);
|
|
110
129
|
if (textLabel.bounds.containsPoint(pointInShapeSpace, 0) && textLabel.hitTestPoint(pointInShapeSpace)) {
|
|
111
130
|
if (selectingShape.id === editingShape.id) {
|
|
131
|
+
this.didPointerDownOnEditingShape = true;
|
|
112
132
|
return;
|
|
113
133
|
} else {
|
|
114
|
-
this.
|
|
134
|
+
this.hitLabelOnShapeForPointerUp = selectingShape;
|
|
115
135
|
this.editor.markHistoryStoppingPoint("editing on pointer up");
|
|
116
136
|
this.editor.select(selectingShape.id);
|
|
117
137
|
return;
|
|
@@ -136,9 +156,9 @@ class EditingShape extends import_editor.StateNode {
|
|
|
136
156
|
this.editor.root.handleEvent(info);
|
|
137
157
|
}
|
|
138
158
|
onPointerUp(info) {
|
|
139
|
-
const hitShape = this.
|
|
159
|
+
const hitShape = this.hitLabelOnShapeForPointerUp;
|
|
140
160
|
if (!hitShape) return;
|
|
141
|
-
this.
|
|
161
|
+
this.hitLabelOnShapeForPointerUp = null;
|
|
142
162
|
const util = this.editor.getShapeUtil(hitShape);
|
|
143
163
|
if (hitShape.isLocked) return;
|
|
144
164
|
if (this.editor.getIsReadonly()) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/lib/tools/SelectTool/childStates/EditingShape.ts"],
|
|
4
|
-
"sourcesContent": ["import {\n\tStateNode,\n\tTLCancelEventInfo,\n\tTLCompleteEventInfo,\n\ttlenv,\n\tTLPointerEventInfo,\n\tTLShape,\n} from '@tldraw/editor'\nimport { getTextLabels } from '../../../utils/shapes/shapes'\nimport { renderPlaintextFromRichText } from '../../../utils/text/richText'\nimport { getHitShapeOnCanvasPointerDown } from '../../selection-logic/getHitShapeOnCanvasPointerDown'\nimport { updateHoveredShapeId } from '../../selection-logic/updateHoveredShapeId'\n\ninterface EditingShapeInfo {\n\tisCreatingTextWhileToolLocked?: boolean\n}\n\nexport class EditingShape extends StateNode {\n\tstatic override id = 'editing_shape'\n\n\
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,oBAOO;AACP,oBAA8B;AAC9B,sBAA4C;AAC5C,4CAA+C;AAC/C,kCAAqC;AAM9B,MAAM,qBAAqB,wBAAU;AAAA,EAC3C,OAAgB,KAAK;AAAA,EAErB,
|
|
4
|
+
"sourcesContent": ["import {\n\tStateNode,\n\tTLCancelEventInfo,\n\tTLCompleteEventInfo,\n\ttlenv,\n\tTLPointerEventInfo,\n\tTLShape,\n} from '@tldraw/editor'\nimport { getTextLabels } from '../../../utils/shapes/shapes'\nimport { renderPlaintextFromRichText } from '../../../utils/text/richText'\nimport { getHitShapeOnCanvasPointerDown } from '../../selection-logic/getHitShapeOnCanvasPointerDown'\nimport { updateHoveredShapeId } from '../../selection-logic/updateHoveredShapeId'\n\ninterface EditingShapeInfo {\n\tisCreatingTextWhileToolLocked?: boolean\n}\n\nexport class EditingShape extends StateNode {\n\tstatic override id = 'editing_shape'\n\n\thitLabelOnShapeForPointerUp: TLShape | null = null\n\tprivate info = {} as EditingShapeInfo\n\tprivate didPointerDownOnEditingShape = false\n\n\tprivate isTextInputFocused(): boolean {\n\t\tconst container = this.editor.getContainer()\n\t\treturn (\n\t\t\tcontainer.contains(document.activeElement) &&\n\t\t\t(document.activeElement?.nodeName === 'INPUT' ||\n\t\t\t\tdocument.activeElement?.nodeName === 'TEXTAREA' ||\n\t\t\t\t(document.activeElement as HTMLElement)?.isContentEditable)\n\t\t)\n\t}\n\n\toverride onEnter(info: EditingShapeInfo) {\n\t\tconst editingShape = this.editor.getEditingShape()\n\t\tif (!editingShape) throw Error('Entered editing state without an editing shape')\n\t\tthis.hitLabelOnShapeForPointerUp = null\n\t\tthis.didPointerDownOnEditingShape = false\n\n\t\tthis.info = info\n\n\t\tif (info.isCreatingTextWhileToolLocked) {\n\t\t\tthis.parent.setCurrentToolIdMask('text')\n\t\t}\n\n\t\tupdateHoveredShapeId(this.editor)\n\t\tthis.editor.select(editingShape)\n\t}\n\n\toverride onExit() {\n\t\tconst { editingShapeId } = this.editor.getCurrentPageState()\n\t\tif (!editingShapeId) return\n\n\t\t// Clear the editing shape\n\t\tthis.editor.setEditingShape(null)\n\n\t\tupdateHoveredShapeId.cancel()\n\n\t\tif (this.info.isCreatingTextWhileToolLocked) {\n\t\t\tthis.parent.setCurrentToolIdMask(undefined)\n\t\t\tthis.editor.setCurrentTool('text', {})\n\t\t}\n\t}\n\n\toverride onPointerMove(info: TLPointerEventInfo) {\n\t\t// In the case where on pointer down we hit a shape's label, we need to check if the user is dragging.\n\t\t// and if they are, we need to transition to translating instead.\n\t\tif (this.hitLabelOnShapeForPointerUp && this.editor.inputs.isDragging) {\n\t\t\tif (this.editor.getIsReadonly()) return\n\t\t\tif (this.hitLabelOnShapeForPointerUp.isLocked) return\n\n\t\t\tthis.editor.select(this.hitLabelOnShapeForPointerUp)\n\t\t\tthis.parent.transition('translating', info)\n\t\t\tthis.hitLabelOnShapeForPointerUp = null\n\t\t\treturn\n\t\t}\n\n\t\t// Check if dragging from editing shape with blurred input\n\t\tif (this.didPointerDownOnEditingShape && this.editor.inputs.isDragging) {\n\t\t\tif (this.editor.getIsReadonly()) return\n\n\t\t\tconst editingShape = this.editor.getEditingShape()\n\t\t\tif (!editingShape || editingShape.isLocked) return\n\n\t\t\tif (!this.isTextInputFocused()) {\n\t\t\t\t// Input blurred during drag - exit edit mode and start translating\n\t\t\t\tthis.editor.select(editingShape)\n\t\t\t\tthis.parent.transition('translating', info)\n\t\t\t\tthis.didPointerDownOnEditingShape = false\n\t\t\t\treturn\n\t\t\t}\n\t\t\t// Input still focused - user is selecting text, stay in edit mode\n\t\t\tthis.didPointerDownOnEditingShape = false\n\t\t}\n\n\t\tswitch (info.target) {\n\t\t\tcase 'shape':\n\t\t\tcase 'canvas': {\n\t\t\t\tupdateHoveredShapeId(this.editor)\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}\n\n\toverride onPointerDown(info: TLPointerEventInfo) {\n\t\tthis.hitLabelOnShapeForPointerUp = null\n\t\tthis.didPointerDownOnEditingShape = false\n\n\t\tswitch (info.target) {\n\t\t\t// N.B. This bit of logic has a bit of history to it.\n\t\t\t// There was a PR that got rid of this logic: https://github.com/tldraw/tldraw/pull/4237\n\t\t\t// But here we bring it back to help support the new rich text world.\n\t\t\t// The original issue which is visible in the video attachments in the PR now seem\n\t\t\t// to have been resolved anyway via some other layer.\n\t\t\tcase 'canvas': {\n\t\t\t\tconst hitShape = getHitShapeOnCanvasPointerDown(this.editor, true /* hitLabels */)\n\t\t\t\tif (hitShape) {\n\t\t\t\t\tthis.onPointerDown({\n\t\t\t\t\t\t...info,\n\t\t\t\t\t\tshape: hitShape,\n\t\t\t\t\t\ttarget: 'shape',\n\t\t\t\t\t})\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tcase 'shape': {\n\t\t\t\tconst { shape: selectingShape } = info\n\t\t\t\tconst editingShape = this.editor.getEditingShape()\n\n\t\t\t\tif (!editingShape) {\n\t\t\t\t\tthrow Error('Expected an editing shape!')\n\t\t\t\t}\n\n\t\t\t\t// for shapes with labels, check to see if the click was inside of the shape's label\n\t\t\t\tconst geometry = this.editor.getShapeUtil(selectingShape).getGeometry(selectingShape)\n\t\t\t\tconst textLabels = getTextLabels(geometry)\n\t\t\t\tconst textLabel = textLabels.length === 1 ? textLabels[0] : undefined\n\t\t\t\t// N.B. One nuance here is that we want empty text fields to be removed from the canvas when the user clicks away from them.\n\t\t\t\tconst isEmptyTextShape =\n\t\t\t\t\tthis.editor.isShapeOfType(editingShape, 'text') &&\n\t\t\t\t\trenderPlaintextFromRichText(this.editor, editingShape.props.richText).trim() === ''\n\t\t\t\tif (textLabel && !isEmptyTextShape) {\n\t\t\t\t\tconst pointInShapeSpace = this.editor.getPointInShapeSpace(\n\t\t\t\t\t\tselectingShape,\n\t\t\t\t\t\tthis.editor.inputs.currentPagePoint\n\t\t\t\t\t)\n\t\t\t\t\tif (\n\t\t\t\t\t\ttextLabel.bounds.containsPoint(pointInShapeSpace, 0) &&\n\t\t\t\t\t\ttextLabel.hitTestPoint(pointInShapeSpace)\n\t\t\t\t\t) {\n\t\t\t\t\t\t// it's a hit to the label!\n\t\t\t\t\t\tif (selectingShape.id === editingShape.id) {\n\t\t\t\t\t\t\t// Track click on editing shape for drag detection\n\t\t\t\t\t\t\tthis.didPointerDownOnEditingShape = true\n\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tthis.hitLabelOnShapeForPointerUp = selectingShape\n\n\t\t\t\t\t\t\tthis.editor.markHistoryStoppingPoint('editing on pointer up')\n\t\t\t\t\t\t\tthis.editor.select(selectingShape.id)\n\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tif (selectingShape.id === editingShape.id) {\n\t\t\t\t\t\t// If we clicked on a frame, while editing its heading, cancel editing\n\t\t\t\t\t\tif (this.editor.isShapeOfType(selectingShape, 'frame')) {\n\t\t\t\t\t\t\tthis.editor.setEditingShape(null)\n\t\t\t\t\t\t\tthis.parent.transition('idle', info)\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// If we clicked on the editing shape (which isn't a shape with a label), do nothing\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// But if we clicked on a different shape of the same type, transition to pointing_shape instead\n\t\t\t\t\t\tthis.parent.transition('pointing_shape', info)\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\n\t\t// still here? Cancel editing and transition back to select idle\n\t\tthis.parent.transition('idle', info)\n\t\t// then feed the pointer down event back into the state chart as if it happened in that state\n\t\tthis.editor.root.handleEvent(info)\n\t}\n\n\toverride onPointerUp(info: TLPointerEventInfo) {\n\t\t// If we're not dragging, and it's a hit to the label, begin editing the shape.\n\t\tconst hitShape = this.hitLabelOnShapeForPointerUp\n\t\tif (!hitShape) return\n\t\tthis.hitLabelOnShapeForPointerUp = null\n\n\t\t// Stay in edit mode to maintain flow of editing.\n\t\tconst util = this.editor.getShapeUtil(hitShape)\n\t\tif (hitShape.isLocked) return\n\n\t\tif (this.editor.getIsReadonly()) {\n\t\t\tif (!util.canEditInReadonly(hitShape)) {\n\t\t\t\tthis.parent.transition('pointing_shape', info)\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\n\t\tthis.editor.select(hitShape.id)\n\n\t\tconst currentEditingShape = this.editor.getEditingShape()\n\t\tconst isEditToEditAction = currentEditingShape && currentEditingShape.id !== hitShape.id\n\t\tthis.editor.setEditingShape(hitShape.id)\n\n\t\tconst isMobile = tlenv.isIos || tlenv.isAndroid\n\t\tif (!isMobile || !isEditToEditAction) {\n\t\t\tthis.editor.emit('place-caret', { shapeId: hitShape.id, point: info.point })\n\t\t} else if (isMobile && isEditToEditAction) {\n\t\t\tthis.editor.emit('select-all-text', { shapeId: hitShape.id })\n\t\t}\n\t\tupdateHoveredShapeId(this.editor)\n\t}\n\n\toverride onComplete(info: TLCompleteEventInfo) {\n\t\tthis.editor.getContainer().focus()\n\t\tthis.parent.transition('idle', info)\n\t}\n\n\toverride onCancel(info: TLCancelEventInfo) {\n\t\tthis.editor.getContainer().focus()\n\t\tthis.parent.transition('idle', info)\n\t}\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
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 = "4.3.0-canary.
|
|
25
|
+
const version = "4.3.0-canary.d428e9e9a7c6";
|
|
26
26
|
const publishDates = {
|
|
27
27
|
major: "2025-09-18T14:39:22.803Z",
|
|
28
|
-
minor: "2025-12-
|
|
29
|
-
patch: "2025-12-
|
|
28
|
+
minor: "2025-12-08T14:35:52.679Z",
|
|
29
|
+
patch: "2025-12-08T14:35:52.679Z"
|
|
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 = '4.3.0-canary.
|
|
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 = '4.3.0-canary.d428e9e9a7c6'\nexport const publishDates = {\n\tmajor: '2025-09-18T14:39:22.803Z',\n\tminor: '2025-12-08T14:35:52.679Z',\n\tpatch: '2025-12-08T14:35:52.679Z',\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
|
}
|
package/dist-esm/index.mjs
CHANGED
|
@@ -8,12 +8,18 @@ import { getHitShapeOnCanvasPointerDown } from "../../selection-logic/getHitShap
|
|
|
8
8
|
import { updateHoveredShapeId } from "../../selection-logic/updateHoveredShapeId.mjs";
|
|
9
9
|
class EditingShape extends StateNode {
|
|
10
10
|
static id = "editing_shape";
|
|
11
|
-
|
|
11
|
+
hitLabelOnShapeForPointerUp = null;
|
|
12
12
|
info = {};
|
|
13
|
+
didPointerDownOnEditingShape = false;
|
|
14
|
+
isTextInputFocused() {
|
|
15
|
+
const container = this.editor.getContainer();
|
|
16
|
+
return container.contains(document.activeElement) && (document.activeElement?.nodeName === "INPUT" || document.activeElement?.nodeName === "TEXTAREA" || document.activeElement?.isContentEditable);
|
|
17
|
+
}
|
|
13
18
|
onEnter(info) {
|
|
14
19
|
const editingShape = this.editor.getEditingShape();
|
|
15
20
|
if (!editingShape) throw Error("Entered editing state without an editing shape");
|
|
16
|
-
this.
|
|
21
|
+
this.hitLabelOnShapeForPointerUp = null;
|
|
22
|
+
this.didPointerDownOnEditingShape = false;
|
|
17
23
|
this.info = info;
|
|
18
24
|
if (info.isCreatingTextWhileToolLocked) {
|
|
19
25
|
this.parent.setCurrentToolIdMask("text");
|
|
@@ -32,14 +38,26 @@ class EditingShape extends StateNode {
|
|
|
32
38
|
}
|
|
33
39
|
}
|
|
34
40
|
onPointerMove(info) {
|
|
35
|
-
if (this.
|
|
41
|
+
if (this.hitLabelOnShapeForPointerUp && this.editor.inputs.isDragging) {
|
|
36
42
|
if (this.editor.getIsReadonly()) return;
|
|
37
|
-
if (this.
|
|
38
|
-
this.editor.select(this.
|
|
43
|
+
if (this.hitLabelOnShapeForPointerUp.isLocked) return;
|
|
44
|
+
this.editor.select(this.hitLabelOnShapeForPointerUp);
|
|
39
45
|
this.parent.transition("translating", info);
|
|
40
|
-
this.
|
|
46
|
+
this.hitLabelOnShapeForPointerUp = null;
|
|
41
47
|
return;
|
|
42
48
|
}
|
|
49
|
+
if (this.didPointerDownOnEditingShape && this.editor.inputs.isDragging) {
|
|
50
|
+
if (this.editor.getIsReadonly()) return;
|
|
51
|
+
const editingShape = this.editor.getEditingShape();
|
|
52
|
+
if (!editingShape || editingShape.isLocked) return;
|
|
53
|
+
if (!this.isTextInputFocused()) {
|
|
54
|
+
this.editor.select(editingShape);
|
|
55
|
+
this.parent.transition("translating", info);
|
|
56
|
+
this.didPointerDownOnEditingShape = false;
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
this.didPointerDownOnEditingShape = false;
|
|
60
|
+
}
|
|
43
61
|
switch (info.target) {
|
|
44
62
|
case "shape":
|
|
45
63
|
case "canvas": {
|
|
@@ -49,7 +67,8 @@ class EditingShape extends StateNode {
|
|
|
49
67
|
}
|
|
50
68
|
}
|
|
51
69
|
onPointerDown(info) {
|
|
52
|
-
this.
|
|
70
|
+
this.hitLabelOnShapeForPointerUp = null;
|
|
71
|
+
this.didPointerDownOnEditingShape = false;
|
|
53
72
|
switch (info.target) {
|
|
54
73
|
// N.B. This bit of logic has a bit of history to it.
|
|
55
74
|
// There was a PR that got rid of this logic: https://github.com/tldraw/tldraw/pull/4237
|
|
@@ -89,9 +108,10 @@ class EditingShape extends StateNode {
|
|
|
89
108
|
);
|
|
90
109
|
if (textLabel.bounds.containsPoint(pointInShapeSpace, 0) && textLabel.hitTestPoint(pointInShapeSpace)) {
|
|
91
110
|
if (selectingShape.id === editingShape.id) {
|
|
111
|
+
this.didPointerDownOnEditingShape = true;
|
|
92
112
|
return;
|
|
93
113
|
} else {
|
|
94
|
-
this.
|
|
114
|
+
this.hitLabelOnShapeForPointerUp = selectingShape;
|
|
95
115
|
this.editor.markHistoryStoppingPoint("editing on pointer up");
|
|
96
116
|
this.editor.select(selectingShape.id);
|
|
97
117
|
return;
|
|
@@ -116,9 +136,9 @@ class EditingShape extends StateNode {
|
|
|
116
136
|
this.editor.root.handleEvent(info);
|
|
117
137
|
}
|
|
118
138
|
onPointerUp(info) {
|
|
119
|
-
const hitShape = this.
|
|
139
|
+
const hitShape = this.hitLabelOnShapeForPointerUp;
|
|
120
140
|
if (!hitShape) return;
|
|
121
|
-
this.
|
|
141
|
+
this.hitLabelOnShapeForPointerUp = null;
|
|
122
142
|
const util = this.editor.getShapeUtil(hitShape);
|
|
123
143
|
if (hitShape.isLocked) return;
|
|
124
144
|
if (this.editor.getIsReadonly()) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/lib/tools/SelectTool/childStates/EditingShape.ts"],
|
|
4
|
-
"sourcesContent": ["import {\n\tStateNode,\n\tTLCancelEventInfo,\n\tTLCompleteEventInfo,\n\ttlenv,\n\tTLPointerEventInfo,\n\tTLShape,\n} from '@tldraw/editor'\nimport { getTextLabels } from '../../../utils/shapes/shapes'\nimport { renderPlaintextFromRichText } from '../../../utils/text/richText'\nimport { getHitShapeOnCanvasPointerDown } from '../../selection-logic/getHitShapeOnCanvasPointerDown'\nimport { updateHoveredShapeId } from '../../selection-logic/updateHoveredShapeId'\n\ninterface EditingShapeInfo {\n\tisCreatingTextWhileToolLocked?: boolean\n}\n\nexport class EditingShape extends StateNode {\n\tstatic override id = 'editing_shape'\n\n\
|
|
5
|
-
"mappings": "AAAA;AAAA,EACC;AAAA,EAGA;AAAA,OAGM;AACP,SAAS,qBAAqB;AAC9B,SAAS,mCAAmC;AAC5C,SAAS,sCAAsC;AAC/C,SAAS,4BAA4B;AAM9B,MAAM,qBAAqB,UAAU;AAAA,EAC3C,OAAgB,KAAK;AAAA,EAErB,
|
|
4
|
+
"sourcesContent": ["import {\n\tStateNode,\n\tTLCancelEventInfo,\n\tTLCompleteEventInfo,\n\ttlenv,\n\tTLPointerEventInfo,\n\tTLShape,\n} from '@tldraw/editor'\nimport { getTextLabels } from '../../../utils/shapes/shapes'\nimport { renderPlaintextFromRichText } from '../../../utils/text/richText'\nimport { getHitShapeOnCanvasPointerDown } from '../../selection-logic/getHitShapeOnCanvasPointerDown'\nimport { updateHoveredShapeId } from '../../selection-logic/updateHoveredShapeId'\n\ninterface EditingShapeInfo {\n\tisCreatingTextWhileToolLocked?: boolean\n}\n\nexport class EditingShape extends StateNode {\n\tstatic override id = 'editing_shape'\n\n\thitLabelOnShapeForPointerUp: TLShape | null = null\n\tprivate info = {} as EditingShapeInfo\n\tprivate didPointerDownOnEditingShape = false\n\n\tprivate isTextInputFocused(): boolean {\n\t\tconst container = this.editor.getContainer()\n\t\treturn (\n\t\t\tcontainer.contains(document.activeElement) &&\n\t\t\t(document.activeElement?.nodeName === 'INPUT' ||\n\t\t\t\tdocument.activeElement?.nodeName === 'TEXTAREA' ||\n\t\t\t\t(document.activeElement as HTMLElement)?.isContentEditable)\n\t\t)\n\t}\n\n\toverride onEnter(info: EditingShapeInfo) {\n\t\tconst editingShape = this.editor.getEditingShape()\n\t\tif (!editingShape) throw Error('Entered editing state without an editing shape')\n\t\tthis.hitLabelOnShapeForPointerUp = null\n\t\tthis.didPointerDownOnEditingShape = false\n\n\t\tthis.info = info\n\n\t\tif (info.isCreatingTextWhileToolLocked) {\n\t\t\tthis.parent.setCurrentToolIdMask('text')\n\t\t}\n\n\t\tupdateHoveredShapeId(this.editor)\n\t\tthis.editor.select(editingShape)\n\t}\n\n\toverride onExit() {\n\t\tconst { editingShapeId } = this.editor.getCurrentPageState()\n\t\tif (!editingShapeId) return\n\n\t\t// Clear the editing shape\n\t\tthis.editor.setEditingShape(null)\n\n\t\tupdateHoveredShapeId.cancel()\n\n\t\tif (this.info.isCreatingTextWhileToolLocked) {\n\t\t\tthis.parent.setCurrentToolIdMask(undefined)\n\t\t\tthis.editor.setCurrentTool('text', {})\n\t\t}\n\t}\n\n\toverride onPointerMove(info: TLPointerEventInfo) {\n\t\t// In the case where on pointer down we hit a shape's label, we need to check if the user is dragging.\n\t\t// and if they are, we need to transition to translating instead.\n\t\tif (this.hitLabelOnShapeForPointerUp && this.editor.inputs.isDragging) {\n\t\t\tif (this.editor.getIsReadonly()) return\n\t\t\tif (this.hitLabelOnShapeForPointerUp.isLocked) return\n\n\t\t\tthis.editor.select(this.hitLabelOnShapeForPointerUp)\n\t\t\tthis.parent.transition('translating', info)\n\t\t\tthis.hitLabelOnShapeForPointerUp = null\n\t\t\treturn\n\t\t}\n\n\t\t// Check if dragging from editing shape with blurred input\n\t\tif (this.didPointerDownOnEditingShape && this.editor.inputs.isDragging) {\n\t\t\tif (this.editor.getIsReadonly()) return\n\n\t\t\tconst editingShape = this.editor.getEditingShape()\n\t\t\tif (!editingShape || editingShape.isLocked) return\n\n\t\t\tif (!this.isTextInputFocused()) {\n\t\t\t\t// Input blurred during drag - exit edit mode and start translating\n\t\t\t\tthis.editor.select(editingShape)\n\t\t\t\tthis.parent.transition('translating', info)\n\t\t\t\tthis.didPointerDownOnEditingShape = false\n\t\t\t\treturn\n\t\t\t}\n\t\t\t// Input still focused - user is selecting text, stay in edit mode\n\t\t\tthis.didPointerDownOnEditingShape = false\n\t\t}\n\n\t\tswitch (info.target) {\n\t\t\tcase 'shape':\n\t\t\tcase 'canvas': {\n\t\t\t\tupdateHoveredShapeId(this.editor)\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}\n\n\toverride onPointerDown(info: TLPointerEventInfo) {\n\t\tthis.hitLabelOnShapeForPointerUp = null\n\t\tthis.didPointerDownOnEditingShape = false\n\n\t\tswitch (info.target) {\n\t\t\t// N.B. This bit of logic has a bit of history to it.\n\t\t\t// There was a PR that got rid of this logic: https://github.com/tldraw/tldraw/pull/4237\n\t\t\t// But here we bring it back to help support the new rich text world.\n\t\t\t// The original issue which is visible in the video attachments in the PR now seem\n\t\t\t// to have been resolved anyway via some other layer.\n\t\t\tcase 'canvas': {\n\t\t\t\tconst hitShape = getHitShapeOnCanvasPointerDown(this.editor, true /* hitLabels */)\n\t\t\t\tif (hitShape) {\n\t\t\t\t\tthis.onPointerDown({\n\t\t\t\t\t\t...info,\n\t\t\t\t\t\tshape: hitShape,\n\t\t\t\t\t\ttarget: 'shape',\n\t\t\t\t\t})\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tcase 'shape': {\n\t\t\t\tconst { shape: selectingShape } = info\n\t\t\t\tconst editingShape = this.editor.getEditingShape()\n\n\t\t\t\tif (!editingShape) {\n\t\t\t\t\tthrow Error('Expected an editing shape!')\n\t\t\t\t}\n\n\t\t\t\t// for shapes with labels, check to see if the click was inside of the shape's label\n\t\t\t\tconst geometry = this.editor.getShapeUtil(selectingShape).getGeometry(selectingShape)\n\t\t\t\tconst textLabels = getTextLabels(geometry)\n\t\t\t\tconst textLabel = textLabels.length === 1 ? textLabels[0] : undefined\n\t\t\t\t// N.B. One nuance here is that we want empty text fields to be removed from the canvas when the user clicks away from them.\n\t\t\t\tconst isEmptyTextShape =\n\t\t\t\t\tthis.editor.isShapeOfType(editingShape, 'text') &&\n\t\t\t\t\trenderPlaintextFromRichText(this.editor, editingShape.props.richText).trim() === ''\n\t\t\t\tif (textLabel && !isEmptyTextShape) {\n\t\t\t\t\tconst pointInShapeSpace = this.editor.getPointInShapeSpace(\n\t\t\t\t\t\tselectingShape,\n\t\t\t\t\t\tthis.editor.inputs.currentPagePoint\n\t\t\t\t\t)\n\t\t\t\t\tif (\n\t\t\t\t\t\ttextLabel.bounds.containsPoint(pointInShapeSpace, 0) &&\n\t\t\t\t\t\ttextLabel.hitTestPoint(pointInShapeSpace)\n\t\t\t\t\t) {\n\t\t\t\t\t\t// it's a hit to the label!\n\t\t\t\t\t\tif (selectingShape.id === editingShape.id) {\n\t\t\t\t\t\t\t// Track click on editing shape for drag detection\n\t\t\t\t\t\t\tthis.didPointerDownOnEditingShape = true\n\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tthis.hitLabelOnShapeForPointerUp = selectingShape\n\n\t\t\t\t\t\t\tthis.editor.markHistoryStoppingPoint('editing on pointer up')\n\t\t\t\t\t\t\tthis.editor.select(selectingShape.id)\n\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tif (selectingShape.id === editingShape.id) {\n\t\t\t\t\t\t// If we clicked on a frame, while editing its heading, cancel editing\n\t\t\t\t\t\tif (this.editor.isShapeOfType(selectingShape, 'frame')) {\n\t\t\t\t\t\t\tthis.editor.setEditingShape(null)\n\t\t\t\t\t\t\tthis.parent.transition('idle', info)\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// If we clicked on the editing shape (which isn't a shape with a label), do nothing\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// But if we clicked on a different shape of the same type, transition to pointing_shape instead\n\t\t\t\t\t\tthis.parent.transition('pointing_shape', info)\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\n\t\t// still here? Cancel editing and transition back to select idle\n\t\tthis.parent.transition('idle', info)\n\t\t// then feed the pointer down event back into the state chart as if it happened in that state\n\t\tthis.editor.root.handleEvent(info)\n\t}\n\n\toverride onPointerUp(info: TLPointerEventInfo) {\n\t\t// If we're not dragging, and it's a hit to the label, begin editing the shape.\n\t\tconst hitShape = this.hitLabelOnShapeForPointerUp\n\t\tif (!hitShape) return\n\t\tthis.hitLabelOnShapeForPointerUp = null\n\n\t\t// Stay in edit mode to maintain flow of editing.\n\t\tconst util = this.editor.getShapeUtil(hitShape)\n\t\tif (hitShape.isLocked) return\n\n\t\tif (this.editor.getIsReadonly()) {\n\t\t\tif (!util.canEditInReadonly(hitShape)) {\n\t\t\t\tthis.parent.transition('pointing_shape', info)\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\n\t\tthis.editor.select(hitShape.id)\n\n\t\tconst currentEditingShape = this.editor.getEditingShape()\n\t\tconst isEditToEditAction = currentEditingShape && currentEditingShape.id !== hitShape.id\n\t\tthis.editor.setEditingShape(hitShape.id)\n\n\t\tconst isMobile = tlenv.isIos || tlenv.isAndroid\n\t\tif (!isMobile || !isEditToEditAction) {\n\t\t\tthis.editor.emit('place-caret', { shapeId: hitShape.id, point: info.point })\n\t\t} else if (isMobile && isEditToEditAction) {\n\t\t\tthis.editor.emit('select-all-text', { shapeId: hitShape.id })\n\t\t}\n\t\tupdateHoveredShapeId(this.editor)\n\t}\n\n\toverride onComplete(info: TLCompleteEventInfo) {\n\t\tthis.editor.getContainer().focus()\n\t\tthis.parent.transition('idle', info)\n\t}\n\n\toverride onCancel(info: TLCancelEventInfo) {\n\t\tthis.editor.getContainer().focus()\n\t\tthis.parent.transition('idle', info)\n\t}\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
const version = "4.3.0-canary.
|
|
1
|
+
const version = "4.3.0-canary.d428e9e9a7c6";
|
|
2
2
|
const publishDates = {
|
|
3
3
|
major: "2025-09-18T14:39:22.803Z",
|
|
4
|
-
minor: "2025-12-
|
|
5
|
-
patch: "2025-12-
|
|
4
|
+
minor: "2025-12-08T14:35:52.679Z",
|
|
5
|
+
patch: "2025-12-08T14:35:52.679Z"
|
|
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 = '4.3.0-canary.
|
|
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 = '4.3.0-canary.d428e9e9a7c6'\nexport const publishDates = {\n\tmajor: '2025-09-18T14:39:22.803Z',\n\tminor: '2025-12-08T14:35:52.679Z',\n\tpatch: '2025-12-08T14:35:52.679Z',\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": "4.3.0-canary.
|
|
4
|
+
"version": "4.3.0-canary.d428e9e9a7c6",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "tldraw Inc.",
|
|
7
7
|
"email": "hello@tldraw.com"
|
|
@@ -62,8 +62,8 @@
|
|
|
62
62
|
"@tiptap/pm": "^3.6.2",
|
|
63
63
|
"@tiptap/react": "^3.6.2",
|
|
64
64
|
"@tiptap/starter-kit": "^3.6.2",
|
|
65
|
-
"@tldraw/editor": "4.3.0-canary.
|
|
66
|
-
"@tldraw/store": "4.3.0-canary.
|
|
65
|
+
"@tldraw/editor": "4.3.0-canary.d428e9e9a7c6",
|
|
66
|
+
"@tldraw/store": "4.3.0-canary.d428e9e9a7c6",
|
|
67
67
|
"classnames": "^2.5.1",
|
|
68
68
|
"hotkeys-js": "^3.13.9",
|
|
69
69
|
"idb": "^7.1.1",
|
|
@@ -18,13 +18,25 @@ interface EditingShapeInfo {
|
|
|
18
18
|
export class EditingShape extends StateNode {
|
|
19
19
|
static override id = 'editing_shape'
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
hitLabelOnShapeForPointerUp: TLShape | null = null
|
|
22
22
|
private info = {} as EditingShapeInfo
|
|
23
|
+
private didPointerDownOnEditingShape = false
|
|
24
|
+
|
|
25
|
+
private isTextInputFocused(): boolean {
|
|
26
|
+
const container = this.editor.getContainer()
|
|
27
|
+
return (
|
|
28
|
+
container.contains(document.activeElement) &&
|
|
29
|
+
(document.activeElement?.nodeName === 'INPUT' ||
|
|
30
|
+
document.activeElement?.nodeName === 'TEXTAREA' ||
|
|
31
|
+
(document.activeElement as HTMLElement)?.isContentEditable)
|
|
32
|
+
)
|
|
33
|
+
}
|
|
23
34
|
|
|
24
35
|
override onEnter(info: EditingShapeInfo) {
|
|
25
36
|
const editingShape = this.editor.getEditingShape()
|
|
26
37
|
if (!editingShape) throw Error('Entered editing state without an editing shape')
|
|
27
|
-
this.
|
|
38
|
+
this.hitLabelOnShapeForPointerUp = null
|
|
39
|
+
this.didPointerDownOnEditingShape = false
|
|
28
40
|
|
|
29
41
|
this.info = info
|
|
30
42
|
|
|
@@ -54,15 +66,34 @@ export class EditingShape extends StateNode {
|
|
|
54
66
|
override onPointerMove(info: TLPointerEventInfo) {
|
|
55
67
|
// In the case where on pointer down we hit a shape's label, we need to check if the user is dragging.
|
|
56
68
|
// and if they are, we need to transition to translating instead.
|
|
57
|
-
if (this.
|
|
69
|
+
if (this.hitLabelOnShapeForPointerUp && this.editor.inputs.isDragging) {
|
|
58
70
|
if (this.editor.getIsReadonly()) return
|
|
59
|
-
if (this.
|
|
60
|
-
|
|
71
|
+
if (this.hitLabelOnShapeForPointerUp.isLocked) return
|
|
72
|
+
|
|
73
|
+
this.editor.select(this.hitLabelOnShapeForPointerUp)
|
|
61
74
|
this.parent.transition('translating', info)
|
|
62
|
-
this.
|
|
75
|
+
this.hitLabelOnShapeForPointerUp = null
|
|
63
76
|
return
|
|
64
77
|
}
|
|
65
78
|
|
|
79
|
+
// Check if dragging from editing shape with blurred input
|
|
80
|
+
if (this.didPointerDownOnEditingShape && this.editor.inputs.isDragging) {
|
|
81
|
+
if (this.editor.getIsReadonly()) return
|
|
82
|
+
|
|
83
|
+
const editingShape = this.editor.getEditingShape()
|
|
84
|
+
if (!editingShape || editingShape.isLocked) return
|
|
85
|
+
|
|
86
|
+
if (!this.isTextInputFocused()) {
|
|
87
|
+
// Input blurred during drag - exit edit mode and start translating
|
|
88
|
+
this.editor.select(editingShape)
|
|
89
|
+
this.parent.transition('translating', info)
|
|
90
|
+
this.didPointerDownOnEditingShape = false
|
|
91
|
+
return
|
|
92
|
+
}
|
|
93
|
+
// Input still focused - user is selecting text, stay in edit mode
|
|
94
|
+
this.didPointerDownOnEditingShape = false
|
|
95
|
+
}
|
|
96
|
+
|
|
66
97
|
switch (info.target) {
|
|
67
98
|
case 'shape':
|
|
68
99
|
case 'canvas': {
|
|
@@ -73,7 +104,8 @@ export class EditingShape extends StateNode {
|
|
|
73
104
|
}
|
|
74
105
|
|
|
75
106
|
override onPointerDown(info: TLPointerEventInfo) {
|
|
76
|
-
this.
|
|
107
|
+
this.hitLabelOnShapeForPointerUp = null
|
|
108
|
+
this.didPointerDownOnEditingShape = false
|
|
77
109
|
|
|
78
110
|
switch (info.target) {
|
|
79
111
|
// N.B. This bit of logic has a bit of history to it.
|
|
@@ -120,10 +152,11 @@ export class EditingShape extends StateNode {
|
|
|
120
152
|
) {
|
|
121
153
|
// it's a hit to the label!
|
|
122
154
|
if (selectingShape.id === editingShape.id) {
|
|
123
|
-
//
|
|
155
|
+
// Track click on editing shape for drag detection
|
|
156
|
+
this.didPointerDownOnEditingShape = true
|
|
124
157
|
return
|
|
125
158
|
} else {
|
|
126
|
-
this.
|
|
159
|
+
this.hitLabelOnShapeForPointerUp = selectingShape
|
|
127
160
|
|
|
128
161
|
this.editor.markHistoryStoppingPoint('editing on pointer up')
|
|
129
162
|
this.editor.select(selectingShape.id)
|
|
@@ -157,9 +190,9 @@ export class EditingShape extends StateNode {
|
|
|
157
190
|
|
|
158
191
|
override onPointerUp(info: TLPointerEventInfo) {
|
|
159
192
|
// If we're not dragging, and it's a hit to the label, begin editing the shape.
|
|
160
|
-
const hitShape = this.
|
|
193
|
+
const hitShape = this.hitLabelOnShapeForPointerUp
|
|
161
194
|
if (!hitShape) return
|
|
162
|
-
this.
|
|
195
|
+
this.hitLabelOnShapeForPointerUp = null
|
|
163
196
|
|
|
164
197
|
// Stay in edit mode to maintain flow of editing.
|
|
165
198
|
const util = this.editor.getShapeUtil(hitShape)
|
package/src/lib/ui/version.ts
CHANGED
|
@@ -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 = '4.3.0-canary.
|
|
4
|
+
export const version = '4.3.0-canary.d428e9e9a7c6'
|
|
5
5
|
export const publishDates = {
|
|
6
6
|
major: '2025-09-18T14:39:22.803Z',
|
|
7
|
-
minor: '2025-12-
|
|
8
|
-
patch: '2025-12-
|
|
7
|
+
minor: '2025-12-08T14:35:52.679Z',
|
|
8
|
+
patch: '2025-12-08T14:35:52.679Z',
|
|
9
9
|
}
|