@oh-just-another/state 0.60.0 → 0.61.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +98 -0
- package/README.md +4 -4
- package/dist/.tsbuildinfo +1 -1
- package/dist/actions/actionKeyboard.js +2 -2
- package/dist/actions/actionKeyboard.js.map +1 -1
- package/dist/actions/actionMode.d.ts +0 -2
- package/dist/actions/actionMode.d.ts.map +1 -1
- package/dist/actions/actionMode.js +37 -37
- package/dist/actions/actionMode.js.map +1 -1
- package/dist/actions/types.d.ts +1 -1
- package/dist/built-in-handlers.d.ts +8 -0
- package/dist/built-in-handlers.d.ts.map +1 -1
- package/dist/built-in-handlers.js +31 -16
- package/dist/built-in-handlers.js.map +1 -1
- package/dist/constants.d.ts +93 -0
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +93 -0
- package/dist/constants.js.map +1 -1
- package/dist/editor/animation-scene.d.ts.map +1 -1
- package/dist/editor/animation-scene.js +37 -1
- package/dist/editor/animation-scene.js.map +1 -1
- package/dist/editor/event-fanout.d.ts +6 -6
- package/dist/editor/event-fanout.d.ts.map +1 -1
- package/dist/editor/event-fanout.js +8 -8
- package/dist/editor/event-fanout.js.map +1 -1
- package/dist/editor/hit-test.d.ts +8 -0
- package/dist/editor/hit-test.d.ts.map +1 -1
- package/dist/editor/hit-test.js +31 -16
- package/dist/editor/hit-test.js.map +1 -1
- package/dist/editor/link-handle-drag.d.ts +20 -0
- package/dist/editor/link-handle-drag.d.ts.map +1 -1
- package/dist/editor/link-handle-drag.js +71 -2
- package/dist/editor/link-handle-drag.js.map +1 -1
- package/dist/editor/pointer-binding.d.ts.map +1 -1
- package/dist/editor/pointer-binding.js +93 -39
- package/dist/editor/pointer-binding.js.map +1 -1
- package/dist/editor/public/brush.d.ts +69 -8
- package/dist/editor/public/brush.d.ts.map +1 -1
- package/dist/editor/public/brush.js +189 -20
- package/dist/editor/public/brush.js.map +1 -1
- package/dist/editor/public/cursor.js +4 -5
- package/dist/editor/public/cursor.js.map +1 -1
- package/dist/editor/render-orchestrator.d.ts +9 -6
- package/dist/editor/render-orchestrator.d.ts.map +1 -1
- package/dist/editor/render-orchestrator.js +25 -28
- package/dist/editor/render-orchestrator.js.map +1 -1
- package/dist/editor-events.d.ts +5 -5
- package/dist/editor-events.d.ts.map +1 -1
- package/dist/editor.d.ts +64 -19
- package/dist/editor.d.ts.map +1 -1
- package/dist/editor.js +116 -60
- package/dist/editor.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/modes.d.ts +24 -5
- package/dist/modes.d.ts.map +1 -1
- package/dist/modes.js.map +1 -1
- package/dist/overlay.d.ts +7 -19
- package/dist/overlay.d.ts.map +1 -1
- package/dist/overlay.js +11 -42
- package/dist/overlay.js.map +1 -1
- package/package.json +5 -4
package/dist/editor.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createActor } from "xstate";
|
|
2
2
|
import { createEmitter } from "@oh-just-another/events";
|
|
3
3
|
import { elementId as castElementId } from "@oh-just-another/types";
|
|
4
|
-
import { addElement, addLink, endpointElementId, anchorSnapper, apply, buildSpatialIndex, isElementHidden, isElementLocked, runAutoLayout, DEFAULT_LAYER_ID, routeElbowLink, routeElbowPreview, getLink, getLinkPath, getElement, getElementAt, getElementAtIndexed, getElementLocalBounds, localToWorld, worldToLocal, isFrame, isGroup, isText, isImage, isBrush, brushBodyColor, getElementWorldBounds, setTextMeasurer, getScreenToWorld, gridSnapper, outlineSnapper, SnapEngine, invert, updateLink, updateElement, isAnchorRef, isSnapToGridEnabled, resolveSnapSpacing, } from "@oh-just-another/scene";
|
|
4
|
+
import { addElement, getLinkCurvePoints, linkLabelAnchor, addLink, endpointElementId, anchorSnapper, apply, buildSpatialIndex, isElementHidden, isElementLocked, runAutoLayout, DEFAULT_LAYER_ID, routeElbowLink, routeElbowPreview, getLink, getLinkPath, getElement, getElementAt, getElementAtIndexed, getElementLocalBounds, localToWorld, worldToLocal, isFrame, isGroup, isText, isImage, isBrush, brushBodyColor, getElementWorldBounds, setTextMeasurer, getScreenToWorld, gridSnapper, outlineSnapper, SnapEngine, invert, updateLink, updateElement, isAnchorRef, isSnapToGridEnabled, resolveSnapSpacing, } from "@oh-just-another/scene";
|
|
5
5
|
import { layerId as castLayerId, } from "@oh-just-another/types";
|
|
6
6
|
import { bounds as B, matrix, vec2 } from "@oh-just-another/math";
|
|
7
7
|
import { onAnimationContentReady, setActiveRasterizer, setActiveTextShaper, ElementCache, } from "@oh-just-another/renderer-core";
|
|
@@ -17,6 +17,7 @@ import { DEFAULT_SNAP_THRESHOLD, LINK_ENDPOINT_HANDLE_RADIUS, LINK_HIT_THRESHOLD
|
|
|
17
17
|
import { HANDLE_HIT_SLOP } from "./handle.js";
|
|
18
18
|
import { req } from "./util.js";
|
|
19
19
|
import { interactionMachine, } from "./machine.js";
|
|
20
|
+
import { DEFAULT_MODE } from "./modes.js";
|
|
20
21
|
import { createEventCache, fanOutEvents, primeEventCache, } from "./editor/event-fanout.js";
|
|
21
22
|
import { AnimationController } from "./editor/animation.js";
|
|
22
23
|
import { GestureController } from "./editor/gesture-tx.js";
|
|
@@ -30,7 +31,7 @@ import { applyContainerDrop as applyContainerDropPure, clampContainerToChildren
|
|
|
30
31
|
import { computeGroupResizePatches, computeElementResize, computeRotatedElementResize, computeTextResize, } from "./editor/applies/resize.js";
|
|
31
32
|
import { bindPointerEvents as bindPointerEventsExternal } from "./editor/pointer-binding.js";
|
|
32
33
|
import { InteractionState, } from "./editor/interaction-state.js";
|
|
33
|
-
import { beginBrushStroke as beginBrushStrokePure, commitBrushStroke as commitBrushStrokePure, extendBrushStroke as extendBrushStrokePure,
|
|
34
|
+
import { beginBrushStroke as beginBrushStrokePure, commitBrushStroke as commitBrushStrokePure, extendBrushStroke as extendBrushStrokePure, brushCommitPoints, brushStyleFromSettings, computeSetBrushWidth, DEFAULT_BRUSH_SETTINGS, newBrushId, } from "./editor/public/brush.js";
|
|
34
35
|
import { beginEraseStroke as beginEraseStrokePure, sampleErase as sampleErasePure, computeEraseCommit, } from "./editor/public/eraser.js";
|
|
35
36
|
import { computeEraseFromMasks, computeStrokeErasePreviewFromMasks, markErasedIntervals, } from "./editor/public/stroke-eraser.js";
|
|
36
37
|
import { coveredLength } from "./editor/public/stroke-eraser-coverage.js";
|
|
@@ -324,11 +325,11 @@ export class Editor {
|
|
|
324
325
|
this.interaction.brushStroke = v;
|
|
325
326
|
}
|
|
326
327
|
/**
|
|
327
|
-
* The in-progress brush stroke
|
|
328
|
-
*
|
|
329
|
-
*
|
|
330
|
-
*
|
|
331
|
-
*
|
|
328
|
+
* The in-progress brush stroke run through the SAME commit pipeline
|
|
329
|
+
* `commitBrushStroke` applies on release (see {@link brushCommitPoints}:
|
|
330
|
+
* raw catch-up point + Catmull-Rom resample), so the stroke reads exactly
|
|
331
|
+
* as it will land in the scene instead of snapping on release. A fresh
|
|
332
|
+
* object each call (points diverge from `brushStroke.points`), so the
|
|
332
333
|
* overlay memo repaints every move that grows the stroke.
|
|
333
334
|
*/
|
|
334
335
|
get brushPreviewStroke() {
|
|
@@ -338,7 +339,7 @@ export class Editor {
|
|
|
338
339
|
const style = brushStyleFromSettings(this._brushSettings);
|
|
339
340
|
return {
|
|
340
341
|
origin: s.origin,
|
|
341
|
-
points:
|
|
342
|
+
points: brushCommitPoints(s, style).points,
|
|
342
343
|
fill: brushBodyColor(style),
|
|
343
344
|
opacity: style.opacity ?? 1,
|
|
344
345
|
};
|
|
@@ -559,6 +560,14 @@ export class Editor {
|
|
|
559
560
|
* toolbar as a lock affordance next to the active tool.
|
|
560
561
|
*/
|
|
561
562
|
_toolLocked = false;
|
|
563
|
+
/** The tool active before the current one — `activeTool.lastActiveTool`. */
|
|
564
|
+
_lastActiveTool = null;
|
|
565
|
+
/** Cached `activeTool` value object; rebuilt only when a component changes. */
|
|
566
|
+
_activeToolCache = {
|
|
567
|
+
type: DEFAULT_MODE,
|
|
568
|
+
locked: false,
|
|
569
|
+
lastActiveTool: null,
|
|
570
|
+
};
|
|
562
571
|
/**
|
|
563
572
|
* Host-extensible file-drop dispatch. Built-ins (image / scene
|
|
564
573
|
* JSON) register themselves at editor construction; hosts add
|
|
@@ -766,10 +775,10 @@ export class Editor {
|
|
|
766
775
|
return self._toolLocked;
|
|
767
776
|
},
|
|
768
777
|
get mode() {
|
|
769
|
-
return self.
|
|
778
|
+
return self.activeTool.type;
|
|
770
779
|
},
|
|
771
780
|
setMode: (m) => {
|
|
772
|
-
self.
|
|
781
|
+
self.setActiveTool(m);
|
|
773
782
|
},
|
|
774
783
|
notify: () => {
|
|
775
784
|
self.notify();
|
|
@@ -948,8 +957,8 @@ export class Editor {
|
|
|
948
957
|
this.fileDropRegistry.register(imageFileDropHandler);
|
|
949
958
|
this.fileDropRegistry.register(videoFileDropHandler);
|
|
950
959
|
this._readOnly = options.readOnly ?? false;
|
|
951
|
-
if (options.
|
|
952
|
-
this.actor.send({ type: "SET_MODE", mode: options.
|
|
960
|
+
if (options.initialTool) {
|
|
961
|
+
this.actor.send({ type: "SET_MODE", mode: options.initialTool });
|
|
953
962
|
}
|
|
954
963
|
}
|
|
955
964
|
/**
|
|
@@ -970,7 +979,7 @@ export class Editor {
|
|
|
970
979
|
* either way, so emitted events are unchanged.
|
|
971
980
|
*/
|
|
972
981
|
observableSnapshot() {
|
|
973
|
-
const
|
|
982
|
+
const activeTool = this.activeTool;
|
|
974
983
|
const selection = this._selection;
|
|
975
984
|
const selectedLinks = this._selectedLinks;
|
|
976
985
|
const scene = this._scene;
|
|
@@ -978,7 +987,7 @@ export class Editor {
|
|
|
978
987
|
const canRedo = this.canRedo;
|
|
979
988
|
const cached = this.snapshotCache;
|
|
980
989
|
if (cached !== null &&
|
|
981
|
-
cached.
|
|
990
|
+
cached.activeTool === activeTool &&
|
|
982
991
|
cached.selection === selection &&
|
|
983
992
|
cached.selectedLinks === selectedLinks &&
|
|
984
993
|
cached.scene === scene &&
|
|
@@ -987,7 +996,7 @@ export class Editor {
|
|
|
987
996
|
return cached;
|
|
988
997
|
}
|
|
989
998
|
const snapshot = {
|
|
990
|
-
|
|
999
|
+
activeTool,
|
|
991
1000
|
selection,
|
|
992
1001
|
selectedLinks,
|
|
993
1002
|
scene,
|
|
@@ -1004,8 +1013,24 @@ export class Editor {
|
|
|
1004
1013
|
get selection() {
|
|
1005
1014
|
return this._selection;
|
|
1006
1015
|
}
|
|
1007
|
-
|
|
1008
|
-
|
|
1016
|
+
/**
|
|
1017
|
+
* The active tool as a single value object (`{ type, locked,
|
|
1018
|
+
* lastActiveTool }`) — the one source of truth for the current tool.
|
|
1019
|
+
* The reference is stable between changes (safe for React deps).
|
|
1020
|
+
*/
|
|
1021
|
+
get activeTool() {
|
|
1022
|
+
const type = this.actor.getSnapshot().context.mode;
|
|
1023
|
+
const c = this._activeToolCache;
|
|
1024
|
+
if (c.type !== type ||
|
|
1025
|
+
c.locked !== this._toolLocked ||
|
|
1026
|
+
c.lastActiveTool !== this._lastActiveTool) {
|
|
1027
|
+
this._activeToolCache = {
|
|
1028
|
+
type,
|
|
1029
|
+
locked: this._toolLocked,
|
|
1030
|
+
lastActiveTool: this._lastActiveTool,
|
|
1031
|
+
};
|
|
1032
|
+
}
|
|
1033
|
+
return this._activeToolCache;
|
|
1009
1034
|
}
|
|
1010
1035
|
get history() {
|
|
1011
1036
|
return this._history;
|
|
@@ -1205,10 +1230,6 @@ export class Editor {
|
|
|
1205
1230
|
toggleGrid() {
|
|
1206
1231
|
this.setGrid({ enabled: !this.gridEnabled });
|
|
1207
1232
|
}
|
|
1208
|
-
/** Whether the active draw-mode sticks after a create (toolbar lock). */
|
|
1209
|
-
get toolLocked() {
|
|
1210
|
-
return this._toolLocked;
|
|
1211
|
-
}
|
|
1212
1233
|
/** All currently-selected link (connector) ids. */
|
|
1213
1234
|
get selectedLinks() {
|
|
1214
1235
|
return this._selectedLinks;
|
|
@@ -1267,8 +1288,8 @@ export class Editor {
|
|
|
1267
1288
|
return this.fileDropRegistry.dispatch(file, ctx);
|
|
1268
1289
|
}
|
|
1269
1290
|
/**
|
|
1270
|
-
* Toggle the tool
|
|
1271
|
-
* after each successful shape create — the user keeps drawing
|
|
1291
|
+
* Toggle the tool lock (`activeTool.locked`). With `true`, draw tools
|
|
1292
|
+
* persist after each successful shape create — the user keeps drawing
|
|
1272
1293
|
* rectangles without re-pressing R. With `false` (default), the
|
|
1273
1294
|
* editor reverts to `select` after each create.
|
|
1274
1295
|
*/
|
|
@@ -1278,10 +1299,18 @@ export class Editor {
|
|
|
1278
1299
|
this._toolLocked = locked;
|
|
1279
1300
|
this.notify();
|
|
1280
1301
|
}
|
|
1281
|
-
|
|
1302
|
+
maybeRevertToolAfterCreate() {
|
|
1282
1303
|
this.gestures.maybeRevertModeAfterCreate();
|
|
1283
1304
|
}
|
|
1284
|
-
|
|
1305
|
+
/**
|
|
1306
|
+
* Switch the active tool. The single entry point for tool changes —
|
|
1307
|
+
* toolbar buttons and hotkeys reach it through the action registry.
|
|
1308
|
+
* Records the outgoing tool in `activeTool.lastActiveTool`.
|
|
1309
|
+
*/
|
|
1310
|
+
setActiveTool(mode) {
|
|
1311
|
+
const prev = this.actor.getSnapshot().context.mode;
|
|
1312
|
+
if (prev !== mode)
|
|
1313
|
+
this._lastActiveTool = prev;
|
|
1285
1314
|
// A tool switch cancels any armed colour-picker pipette.
|
|
1286
1315
|
this.pendingEyedropperPick = null;
|
|
1287
1316
|
// Switching tools commits any in-flight text edit (standard: leaving the
|
|
@@ -1614,30 +1643,19 @@ export class Editor {
|
|
|
1614
1643
|
this.interaction.editingLinkCaption = null;
|
|
1615
1644
|
this.notify();
|
|
1616
1645
|
}
|
|
1617
|
-
/**
|
|
1646
|
+
/**
|
|
1647
|
+
* World-space anchor point for a link's caption — the same shared geometry
|
|
1648
|
+
* the renderer places the pill at (`linkLabelAnchor` over the drawn
|
|
1649
|
+
* polyline), so the inline editor opens exactly over the label.
|
|
1650
|
+
*/
|
|
1618
1651
|
linkLabelWorld(id) {
|
|
1619
1652
|
const edge = getLink(this._scene, id);
|
|
1620
1653
|
if (!edge)
|
|
1621
1654
|
return null;
|
|
1622
|
-
const path =
|
|
1655
|
+
const path = getLinkCurvePoints(this._scene, edge);
|
|
1623
1656
|
if (!path || path.length < 2)
|
|
1624
1657
|
return null;
|
|
1625
|
-
|
|
1626
|
-
let total = 0;
|
|
1627
|
-
for (let i = 1; i < path.length; i++)
|
|
1628
|
-
total += vec2.distance(req(path[i - 1]), req(path[i]));
|
|
1629
|
-
let remaining = total * t;
|
|
1630
|
-
for (let i = 1; i < path.length; i++) {
|
|
1631
|
-
const a = req(path[i - 1]);
|
|
1632
|
-
const b = req(path[i]);
|
|
1633
|
-
const seg = vec2.distance(a, b);
|
|
1634
|
-
if (remaining <= seg) {
|
|
1635
|
-
const r = seg === 0 ? 0 : remaining / seg;
|
|
1636
|
-
return { x: a.x + (b.x - a.x) * r, y: a.y + (b.y - a.y) * r };
|
|
1637
|
-
}
|
|
1638
|
-
remaining -= seg;
|
|
1639
|
-
}
|
|
1640
|
-
return req(path[path.length - 1]);
|
|
1658
|
+
return linkLabelAnchor(path, edge);
|
|
1641
1659
|
}
|
|
1642
1660
|
beginTextEdit(id) {
|
|
1643
1661
|
this.textEdit.begin(id);
|
|
@@ -1799,7 +1817,7 @@ export class Editor {
|
|
|
1799
1817
|
y: (vp.size.height || 200) / 2,
|
|
1800
1818
|
});
|
|
1801
1819
|
const id = newElementIdAtCursor(++this.nextId);
|
|
1802
|
-
const shape = buildElementAtCursor(this._scene, this.
|
|
1820
|
+
const shape = buildElementAtCursor(this._scene, this.activeTool.type, world, this._activeLayerId, id);
|
|
1803
1821
|
const r = addElement(this._scene, shape);
|
|
1804
1822
|
this._scene = r.scene;
|
|
1805
1823
|
this._history.push(r.patch);
|
|
@@ -1825,7 +1843,7 @@ export class Editor {
|
|
|
1825
1843
|
this._scene = r.scene;
|
|
1826
1844
|
this.textEdit.markPendingCreate(id);
|
|
1827
1845
|
this._selection = Selection.single(id);
|
|
1828
|
-
this.
|
|
1846
|
+
this.maybeRevertToolAfterCreate();
|
|
1829
1847
|
this.notify();
|
|
1830
1848
|
this.announce(`Created text ${id}`);
|
|
1831
1849
|
this.beginTextEdit(id);
|
|
@@ -1845,14 +1863,21 @@ export class Editor {
|
|
|
1845
1863
|
this._brushSettings = { ...this._brushSettings, ...patch };
|
|
1846
1864
|
this.notify();
|
|
1847
1865
|
}
|
|
1848
|
-
|
|
1849
|
-
|
|
1866
|
+
/**
|
|
1867
|
+
* Start a brush stroke. `pointerType` (a `PointerEvent.pointerType`) decides
|
|
1868
|
+
* the pressure source: a pen has a real pressure channel and is honoured
|
|
1869
|
+
* verbatim (the default, which also keeps programmatic callers exact); mouse
|
|
1870
|
+
* and touch have none, so pressure is simulated from pointer speed.
|
|
1871
|
+
*/
|
|
1872
|
+
beginBrushStroke(world, pressure = 0.5, pointerType = "pen") {
|
|
1873
|
+
this.brushStroke = beginBrushStrokePure(world, pressure, this._brushSettings.width, pointerType !== "pen");
|
|
1850
1874
|
this.notify();
|
|
1851
1875
|
}
|
|
1852
1876
|
extendBrushStroke(world, pressure = 0.5) {
|
|
1853
1877
|
if (!this.brushStroke)
|
|
1854
1878
|
return;
|
|
1855
|
-
|
|
1879
|
+
// Zoom feeds the speed-based pressure simulation (screen-px speed).
|
|
1880
|
+
extendBrushStrokePure(this.brushStroke, world, pressure, this._scene.viewport.zoom || 1);
|
|
1856
1881
|
this.notify();
|
|
1857
1882
|
}
|
|
1858
1883
|
commitBrushStroke() {
|
|
@@ -2394,6 +2419,22 @@ export class Editor {
|
|
|
2394
2419
|
this._history.push(result.patch);
|
|
2395
2420
|
this.notify();
|
|
2396
2421
|
}
|
|
2422
|
+
/**
|
|
2423
|
+
* Re-base the width of committed brush strokes (`style.strokeWidth` has no
|
|
2424
|
+
* effect on brushes — their widths are baked per point). Scales every baked
|
|
2425
|
+
* point width proportionally and records the new `baseWidth`, keeping the
|
|
2426
|
+
* stroke's pressure profile. One undo step. Read-only editors ignore it.
|
|
2427
|
+
*/
|
|
2428
|
+
setBrushWidth(ids, width) {
|
|
2429
|
+
if (this.readOnly)
|
|
2430
|
+
return;
|
|
2431
|
+
const result = computeSetBrushWidth(this._scene, ids, width);
|
|
2432
|
+
if (!result)
|
|
2433
|
+
return;
|
|
2434
|
+
this._scene = result.scene;
|
|
2435
|
+
this._history.push(result.patch);
|
|
2436
|
+
this.notify();
|
|
2437
|
+
}
|
|
2397
2438
|
/**
|
|
2398
2439
|
* Apply a partial text style (bold / italic / colour / decoration) to the
|
|
2399
2440
|
* character range `[from, to)` of a single text element, producing styled
|
|
@@ -2440,9 +2481,9 @@ export class Editor {
|
|
|
2440
2481
|
/**
|
|
2441
2482
|
* Sample the colour under `worldPoint`. When a pipette pick is armed (see
|
|
2442
2483
|
* {@link beginEyedropperPick}), route the colour to that callback and disarm.
|
|
2443
|
-
* Otherwise
|
|
2444
|
-
*
|
|
2445
|
-
*
|
|
2484
|
+
* Otherwise apply it as the current selection's fill (programmatic path).
|
|
2485
|
+
* Returns the sampled colour, or `null` on empty canvas. Read-only editors
|
|
2486
|
+
* sample but don't mutate.
|
|
2446
2487
|
*/
|
|
2447
2488
|
applyEyedropperAt(worldPoint) {
|
|
2448
2489
|
const color = pickColorAt(this._scene, worldPoint, "fill");
|
|
@@ -2460,8 +2501,6 @@ export class Editor {
|
|
|
2460
2501
|
if (!this.readOnly && this._selection.size > 0) {
|
|
2461
2502
|
this.updateStyle(this._selection, { fill: color });
|
|
2462
2503
|
}
|
|
2463
|
-
if (this.mode === "eyedropper" && !this.toolLocked)
|
|
2464
|
-
this.setMode("select");
|
|
2465
2504
|
return color;
|
|
2466
2505
|
}
|
|
2467
2506
|
// --- F9: Convert element type ----------------------------------------
|
|
@@ -2672,7 +2711,7 @@ export class Editor {
|
|
|
2672
2711
|
drag: null,
|
|
2673
2712
|
dragStartWorld: null,
|
|
2674
2713
|
};
|
|
2675
|
-
this.
|
|
2714
|
+
this.setActiveTool("crop");
|
|
2676
2715
|
this.refreshCursor();
|
|
2677
2716
|
this.notify();
|
|
2678
2717
|
}
|
|
@@ -2771,7 +2810,7 @@ export class Editor {
|
|
|
2771
2810
|
this._scene = result.scene;
|
|
2772
2811
|
this._history.push(result.patch);
|
|
2773
2812
|
}
|
|
2774
|
-
this.
|
|
2813
|
+
this.setActiveTool("select");
|
|
2775
2814
|
this.refreshCursor();
|
|
2776
2815
|
this.notify();
|
|
2777
2816
|
}
|
|
@@ -2780,7 +2819,7 @@ export class Editor {
|
|
|
2780
2819
|
if (this.cropSession === null)
|
|
2781
2820
|
return;
|
|
2782
2821
|
this.cropSession = null;
|
|
2783
|
-
this.
|
|
2822
|
+
this.setActiveTool("select");
|
|
2784
2823
|
this.refreshCursor();
|
|
2785
2824
|
this.notify();
|
|
2786
2825
|
}
|
|
@@ -3434,6 +3473,10 @@ export class Editor {
|
|
|
3434
3473
|
return pickPressTarget(worldPoint, {
|
|
3435
3474
|
scene: this._scene,
|
|
3436
3475
|
selection: this._selection,
|
|
3476
|
+
// Selection chrome (resize / rotate / endpoint handles) is pressable
|
|
3477
|
+
// only under the select tool — a creation tool's press on a selected
|
|
3478
|
+
// shape must start the new element / link instead.
|
|
3479
|
+
selectionChromeActive: this.activeTool.type === "select",
|
|
3437
3480
|
selectedLink: this.selectedLink,
|
|
3438
3481
|
selectedLinkCount: this._selectedLinks.size,
|
|
3439
3482
|
enteredGroup: this._enteredGroup,
|
|
@@ -4302,7 +4345,7 @@ export class Editor {
|
|
|
4302
4345
|
if (kind === "frame") {
|
|
4303
4346
|
this.assignFrameMembers(id, b);
|
|
4304
4347
|
}
|
|
4305
|
-
this.
|
|
4348
|
+
this.maybeRevertToolAfterCreate();
|
|
4306
4349
|
this.notify();
|
|
4307
4350
|
}
|
|
4308
4351
|
/** Generate the next "Frame N" name based on existing frames. */
|
|
@@ -4337,7 +4380,7 @@ export class Editor {
|
|
|
4337
4380
|
this._scene = result.scene;
|
|
4338
4381
|
this._history.push(result.patch);
|
|
4339
4382
|
this.edgePreview = null;
|
|
4340
|
-
this.
|
|
4383
|
+
this.maybeRevertToolAfterCreate();
|
|
4341
4384
|
// Dropped on empty canvas (free `point` end) → offer a shape-picker at
|
|
4342
4385
|
// the drop point (standard). The free-ended link stays; picking re-points
|
|
4343
4386
|
// it, dismissing keeps it. Only the `to` end is user-dragged here.
|
|
@@ -4485,6 +4528,19 @@ export class Editor {
|
|
|
4485
4528
|
beginWaypointDrag(linkId, index, insert) {
|
|
4486
4529
|
this.linkHandles.beginWaypointDrag(linkId, index, insert);
|
|
4487
4530
|
}
|
|
4531
|
+
/** Caption (label pill) drag along the selected link's path. */
|
|
4532
|
+
get isDraggingLabel() {
|
|
4533
|
+
return this.linkHandles.isDraggingLabel;
|
|
4534
|
+
}
|
|
4535
|
+
beginLabelDrag(linkId) {
|
|
4536
|
+
this.linkHandles.beginLabelDrag(linkId);
|
|
4537
|
+
}
|
|
4538
|
+
updateLabelDrag(world) {
|
|
4539
|
+
this.linkHandles.updateLabelDrag(world);
|
|
4540
|
+
}
|
|
4541
|
+
endLabelDrag() {
|
|
4542
|
+
this.linkHandles.endLabelDrag();
|
|
4543
|
+
}
|
|
4488
4544
|
/** Live update of the dragged waypoint to `world`. */
|
|
4489
4545
|
updateWaypointDrag(world) {
|
|
4490
4546
|
this.linkHandles.updateWaypointDrag(world);
|
|
@@ -4920,7 +4976,7 @@ export class Editor {
|
|
|
4920
4976
|
animationClock: (shape) => this.gifPlayback.clock(castElementId(typeof shape.id === "string" ? shape.id : "")),
|
|
4921
4977
|
tileComposeFn: this.tileComposeFn,
|
|
4922
4978
|
tileDirtyElements: this.tileDirtyElements,
|
|
4923
|
-
mode: this.
|
|
4979
|
+
mode: this.activeTool.type,
|
|
4924
4980
|
activeLayerId: this._activeLayerId,
|
|
4925
4981
|
cropFrame: this.cropFrameCorners(),
|
|
4926
4982
|
cropGhost: this.cropGhost(),
|
|
@@ -4951,7 +5007,7 @@ export class Editor {
|
|
|
4951
5007
|
// (updated on every hover AND drag move) — `hoverCursorWorld` is forced
|
|
4952
5008
|
// null outside select mode. Radius is the panel's eraser width in SCREEN
|
|
4953
5009
|
// px (matches the slider number).
|
|
4954
|
-
eraserCursor: this.
|
|
5010
|
+
eraserCursor: this.activeTool.type === "erase" && !this._readOnly && this.lastPointerWorld !== null
|
|
4955
5011
|
? { center: this.lastPointerWorld, radius: this._brushSettings.width }
|
|
4956
5012
|
: null,
|
|
4957
5013
|
peerCursors: this._peerCursors,
|