@oh-just-another/state 0.58.0 → 0.59.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 +67 -0
- package/README.md +76 -27
- package/dist/.tsbuildinfo +1 -1
- package/dist/actions/actionArrange.d.ts +13 -0
- package/dist/actions/actionArrange.d.ts.map +1 -0
- package/dist/actions/actionArrange.js +130 -0
- package/dist/actions/actionArrange.js.map +1 -0
- package/dist/actions/actionClipboard.d.ts +2 -0
- package/dist/actions/actionClipboard.d.ts.map +1 -1
- package/dist/actions/actionClipboard.js +27 -1
- package/dist/actions/actionClipboard.js.map +1 -1
- package/dist/actions/actionText.d.ts +5 -0
- package/dist/actions/actionText.d.ts.map +1 -0
- package/dist/actions/actionText.js +37 -0
- package/dist/actions/actionText.js.map +1 -0
- package/dist/actions/actionView.js +1 -1
- package/dist/actions/index.d.ts +3 -1
- package/dist/actions/index.d.ts.map +1 -1
- package/dist/actions/index.js +3 -1
- package/dist/actions/index.js.map +1 -1
- package/dist/actions/registry.d.ts.map +1 -1
- package/dist/actions/registry.js +4 -0
- package/dist/actions/registry.js.map +1 -1
- package/dist/actions/types.d.ts +1 -1
- package/dist/actions/types.d.ts.map +1 -1
- package/dist/actions/types.js.map +1 -1
- package/dist/auto-layout-scheduler.d.ts +1 -3
- package/dist/auto-layout-scheduler.d.ts.map +1 -1
- package/dist/auto-layout-scheduler.js +1 -2
- package/dist/auto-layout-scheduler.js.map +1 -1
- package/dist/built-in-handlers.d.ts.map +1 -1
- package/dist/built-in-handlers.js +14 -1
- package/dist/built-in-handlers.js.map +1 -1
- package/dist/constants.d.ts +48 -23
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +48 -23
- package/dist/constants.js.map +1 -1
- package/dist/dom-events.d.ts +7 -0
- package/dist/dom-events.d.ts.map +1 -1
- package/dist/dom-events.js +12 -0
- package/dist/dom-events.js.map +1 -1
- package/dist/editor/animation-scene.d.ts +21 -0
- package/dist/editor/animation-scene.d.ts.map +1 -0
- package/dist/editor/animation-scene.js +71 -0
- package/dist/editor/animation-scene.js.map +1 -0
- package/dist/editor/animation.d.ts +43 -0
- package/dist/editor/animation.d.ts.map +1 -0
- package/dist/editor/animation.js +80 -0
- package/dist/editor/animation.js.map +1 -0
- package/dist/editor/applies/arrange.d.ts +46 -0
- package/dist/editor/applies/arrange.d.ts.map +1 -0
- package/dist/editor/applies/arrange.js +162 -0
- package/dist/editor/applies/arrange.js.map +1 -0
- package/dist/editor/applies/edge.d.ts +8 -1
- package/dist/editor/applies/edge.d.ts.map +1 -1
- package/dist/editor/applies/edge.js +31 -1
- package/dist/editor/applies/edge.js.map +1 -1
- package/dist/editor/applies/move.d.ts +6 -0
- package/dist/editor/applies/move.d.ts.map +1 -1
- package/dist/editor/applies/move.js +6 -0
- package/dist/editor/applies/move.js.map +1 -1
- package/dist/editor/applies/resize.d.ts +26 -5
- package/dist/editor/applies/resize.d.ts.map +1 -1
- package/dist/editor/applies/resize.js +91 -13
- package/dist/editor/applies/resize.js.map +1 -1
- package/dist/editor/caret-blink.d.ts +18 -0
- package/dist/editor/caret-blink.d.ts.map +1 -0
- package/dist/editor/caret-blink.js +42 -0
- package/dist/editor/caret-blink.js.map +1 -0
- package/dist/editor/container-ops.d.ts.map +1 -1
- package/dist/editor/container-ops.js +2 -2
- package/dist/editor/container-ops.js.map +1 -1
- package/dist/editor/dirty-rect.d.ts +23 -0
- package/dist/editor/dirty-rect.d.ts.map +1 -0
- package/dist/editor/dirty-rect.js +115 -0
- package/dist/editor/dirty-rect.js.map +1 -0
- package/dist/editor/gif-playback.d.ts +45 -0
- package/dist/editor/gif-playback.d.ts.map +1 -0
- package/dist/editor/gif-playback.js +125 -0
- package/dist/editor/gif-playback.js.map +1 -0
- package/dist/editor/hit-test.d.ts +3 -3
- package/dist/editor/hit-test.d.ts.map +1 -1
- package/dist/editor/hit-test.js +40 -15
- package/dist/editor/hit-test.js.map +1 -1
- package/dist/editor/long-press.d.ts.map +1 -1
- package/dist/editor/long-press.js +2 -2
- package/dist/editor/long-press.js.map +1 -1
- package/dist/editor/pinch.d.ts.map +1 -1
- package/dist/editor/pinch.js +6 -6
- package/dist/editor/pinch.js.map +1 -1
- package/dist/editor/pointer-binding.d.ts.map +1 -1
- package/dist/editor/pointer-binding.js +42 -25
- package/dist/editor/pointer-binding.js.map +1 -1
- package/dist/editor/public/arrange-group.d.ts +3 -9
- package/dist/editor/public/arrange-group.d.ts.map +1 -1
- package/dist/editor/public/arrange-group.js +4 -6
- package/dist/editor/public/arrange-group.js.map +1 -1
- package/dist/editor/public/clipboard.d.ts +2 -4
- package/dist/editor/public/clipboard.d.ts.map +1 -1
- package/dist/editor/public/clipboard.js +2 -4
- package/dist/editor/public/clipboard.js.map +1 -1
- package/dist/editor/public/cursor.d.ts +43 -0
- package/dist/editor/public/cursor.d.ts.map +1 -0
- package/dist/editor/public/cursor.js +135 -0
- package/dist/editor/public/cursor.js.map +1 -0
- package/dist/editor/public/frame-name.d.ts +20 -0
- package/dist/editor/public/frame-name.d.ts.map +1 -0
- package/dist/editor/public/frame-name.js +54 -0
- package/dist/editor/public/frame-name.js.map +1 -0
- package/dist/editor/public/image-insert.d.ts +1 -1
- package/dist/editor/public/image-insert.d.ts.map +1 -1
- package/dist/editor/public/layers.d.ts.map +1 -1
- package/dist/editor/public/layers.js +2 -2
- package/dist/editor/public/layers.js.map +1 -1
- package/dist/editor/public/link.d.ts +11 -2
- package/dist/editor/public/link.d.ts.map +1 -1
- package/dist/editor/public/link.js +70 -2
- package/dist/editor/public/link.js.map +1 -1
- package/dist/editor/public/placement.d.ts +60 -2
- package/dist/editor/public/placement.d.ts.map +1 -1
- package/dist/editor/public/placement.js +185 -2
- package/dist/editor/public/placement.js.map +1 -1
- package/dist/editor/public/selection-ops.d.ts +23 -1
- package/dist/editor/public/selection-ops.d.ts.map +1 -1
- package/dist/editor/public/selection-ops.js +79 -2
- package/dist/editor/public/selection-ops.js.map +1 -1
- package/dist/editor/public/text-edit.d.ts +1 -10
- package/dist/editor/public/text-edit.d.ts.map +1 -1
- package/dist/editor/public/text-edit.js +2 -16
- package/dist/editor/public/text-edit.js.map +1 -1
- package/dist/editor/public/z-order.d.ts.map +1 -1
- package/dist/editor/public/z-order.js +4 -4
- package/dist/editor/public/z-order.js.map +1 -1
- package/dist/editor/public/zoom-pan.d.ts +6 -4
- package/dist/editor/public/zoom-pan.d.ts.map +1 -1
- package/dist/editor/public/zoom-pan.js +8 -7
- package/dist/editor/public/zoom-pan.js.map +1 -1
- package/dist/editor/render-orchestrator.d.ts.map +1 -1
- package/dist/editor/render-orchestrator.js +4 -9
- package/dist/editor/render-orchestrator.js.map +1 -1
- package/dist/editor/resize-helpers.d.ts +21 -5
- package/dist/editor/resize-helpers.d.ts.map +1 -1
- package/dist/editor/resize-helpers.js +59 -19
- package/dist/editor/resize-helpers.js.map +1 -1
- package/dist/editor/viewport-helpers.d.ts.map +1 -1
- package/dist/editor/viewport-helpers.js +3 -3
- package/dist/editor/viewport-helpers.js.map +1 -1
- package/dist/editor-events.d.ts +1 -1
- package/dist/editor-events.d.ts.map +1 -1
- package/dist/editor.d.ts +125 -133
- package/dist/editor.d.ts.map +1 -1
- package/dist/editor.js +351 -989
- package/dist/editor.js.map +1 -1
- package/dist/file-drop.d.ts.map +1 -1
- package/dist/file-drop.js +1 -2
- package/dist/file-drop.js.map +1 -1
- package/dist/frame-helpers.d.ts.map +1 -1
- package/dist/frame-helpers.js +5 -5
- package/dist/frame-helpers.js.map +1 -1
- package/dist/group-helpers.d.ts.map +1 -1
- package/dist/group-helpers.js +4 -4
- package/dist/group-helpers.js.map +1 -1
- package/dist/handle.d.ts +61 -6
- package/dist/handle.d.ts.map +1 -1
- package/dist/handle.js +105 -7
- package/dist/handle.js.map +1 -1
- package/dist/hotkeys-binding.d.ts +26 -0
- package/dist/hotkeys-binding.d.ts.map +1 -0
- package/dist/hotkeys-binding.js +35 -0
- package/dist/hotkeys-binding.js.map +1 -0
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/link-selection.d.ts +3 -5
- package/dist/link-selection.d.ts.map +1 -1
- package/dist/link-selection.js +3 -12
- package/dist/link-selection.js.map +1 -1
- package/dist/machine.d.ts +27 -1
- package/dist/machine.d.ts.map +1 -1
- package/dist/machine.js +38 -0
- package/dist/machine.js.map +1 -1
- package/dist/overlay.d.ts +1 -4
- package/dist/overlay.d.ts.map +1 -1
- package/dist/overlay.js +136 -36
- package/dist/overlay.js.map +1 -1
- package/dist/selection.d.ts.map +1 -1
- package/dist/selection.js +2 -10
- package/dist/selection.js.map +1 -1
- package/dist/util.d.ts +8 -0
- package/dist/util.d.ts.map +1 -0
- package/dist/util.js +17 -0
- package/dist/util.js.map +1 -0
- package/package.json +7 -7
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,72 @@
|
|
|
1
1
|
# @oh-just-another/state
|
|
2
2
|
|
|
3
|
+
## 0.59.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- b4b252b: Arrange operations for the selection. **Flip** mirrors the selection about its bounding-box centre — horizontal (`Shift+H`) and vertical (`Shift+V`); a single shape flips about its own centre. **Align** flushes two or more shapes to the left / right / top / bottom edge or the horizontal / vertical centre of their bounding box (`Alt+←/→/↑/↓` for the four edges; centres via the panel / menu). **Distribute** evenly spaces three or more shapes so the gaps between them are equal, on the horizontal (`Alt+H`) or vertical (`Alt+V`) axis, keeping the outermost shapes fixed. All three are available from the selection property panel and the right-click menu. New engine API: `Editor.flipSelection(axis)`, `Editor.alignSelection(edge)`, and `Editor.distributeSelection(axis)`.
|
|
8
|
+
- d20d50a: Copy and paste a shape's visual style. `Cmd/Ctrl+Alt+C` captures the fill / stroke / dash / opacity of the selected shape into an in-editor buffer; `Cmd/Ctrl+Alt+V` applies it to the current selection (one undo step). Also available from the right-click menu. New engine API: `Editor.copySelectionStyle()` / `Editor.pasteSelectionStyle()` and the `hasStyleClipboard` flag.
|
|
9
|
+
- 938e7c8: Increase / decrease the font size of the selected text with `Cmd/Ctrl+Shift+>` and `Cmd/Ctrl+Shift+<`. Each shape steps by a gentle ~10 % (at least 1 px) from its own size, so a mixed selection keeps its relative sizing, clamped to the usable range. New engine API: `Editor.adjustSelectionFontSize(direction)`.
|
|
10
|
+
- 9673846: Grid model rework. The viewport's `gridSize` (spacing that doubled as a hidden/
|
|
11
|
+
shown toggle) is replaced by an explicit `gridEnabled` boolean; spacing is fixed
|
|
12
|
+
at `DEFAULT_GRID_SPACING`. The runtime `gridVisible` flag is removed — grid
|
|
13
|
+
on/off now lives on the scene viewport and persists with it. Scene documents
|
|
14
|
+
migrate v1 → v2 automatically (`gridSize > 0` → `gridEnabled: true`). `<Editor>`
|
|
15
|
+
ships gridless by default; hosts enable the grid per scene.
|
|
16
|
+
- 8f00738: Images (static and animated GIF) now render on the OffscreenCanvas worker backend, matching the Canvas2D / WebGL2 backends. The offscreen command stream now carries `drawImage` as an `ImageBitmap`, and static images are loaded as `ImageBitmap` so they cross the worker boundary. `insertImage` now accepts an `ImageBitmap` handle in addition to `HTMLImageElement`.
|
|
17
|
+
- 3152317: The single-shape selection box now turns with the element: its outline, resize
|
|
18
|
+
handles and rotate grip are drawn on an oriented frame that hugs the rotated
|
|
19
|
+
body instead of its axis-aligned bounding box, and handle hit-testing inverse-
|
|
20
|
+
rotates the cursor into the frame so grabs stay precise. The rotate grip moved
|
|
21
|
+
from above the top edge to the bottom-left corner, just outside the shape.
|
|
22
|
+
|
|
23
|
+
Its placement is now defined per element type as an `AnchorRef` — the same
|
|
24
|
+
vocabulary that positions a shape's custom connection points — via the new
|
|
25
|
+
`registerRotateAnchor(type, anchor)` / `getRotateAnchor(type)` API (default:
|
|
26
|
+
the bottom-left corner). Groups and multi-selections keep their axis-aligned
|
|
27
|
+
box, with the grip likewise at the bottom-left corner.
|
|
28
|
+
|
|
29
|
+
New math helper `vec2.rotateAround(point, pivot, radians)`.
|
|
30
|
+
|
|
31
|
+
- fc47ecc: Resizing a rotated shape now works correctly. Dragging a handle on a rotated
|
|
32
|
+
element resizes it in the element's own (un-rotated) frame and keeps the corner
|
|
33
|
+
opposite the dragged handle fixed in world — the same "the other side stays put"
|
|
34
|
+
feel as for an unrotated shape. Aspect-lock (Shift) and resize-from-centre (Alt)
|
|
35
|
+
are honoured in the rotated frame too. Previously a rotated shape jumped because
|
|
36
|
+
the resize math assumed an axis-aligned box.
|
|
37
|
+
- 8fc6b69: Rotate shapes interactively. A rotate grip floats above the selection (single shape or group); dragging it turns the selection about its bounding-box centre, and holding **Shift** snaps the angle to 15° steps. The engine API `Editor.rotateSelection(angle)` drives the same maths programmatically. Element rotation was already modelled and rendered — this adds the handle, the gesture, and the hit-testing (the grip takes priority over the link-start anchors it overlaps).
|
|
38
|
+
- edde5d0: Add `bindEditorHotkeys(editor, options?)` — a reusable, framework-agnostic keyboard-shortcut binding driven by the action registry. Returns an unbind function, leaves text fields alone (except `Escape`), and reads `composedPath()[0]` so the editable-target check stays correct across a shadow-root boundary. Re-exported from `@oh-just-another/editor`.
|
|
39
|
+
- c5be6e5: Transform modifier keys during pointer gestures: hold **Alt** to resize symmetrically about the element's centre, **Shift** to lock the aspect ratio while resizing, and **Shift** to constrain a move to a single axis (Cmd/Ctrl already pulls a shape off the grid for one gesture). `<Editor>` mirrors the modifiers from keyboard events automatically; headless hosts can drive them via `Editor.setTransformModifiers({ alt, shift })`. Applies to single shapes, multi-selection / group resizes, and text.
|
|
40
|
+
|
|
41
|
+
### Patch Changes
|
|
42
|
+
|
|
43
|
+
- 0152ed6: The canvas surface now takes keyboard focus on pointer-down. The press handler
|
|
44
|
+
calls `preventDefault()` (to suppress text selection / native scroll), which also
|
|
45
|
+
suppressed the browser's default focus-on-click — so clicking the canvas left it
|
|
46
|
+
unfocused and keyboard shortcuts (or a clean blur of a previously-focused panel
|
|
47
|
+
input) only worked after tabbing to it, reading as "the first click did nothing".
|
|
48
|
+
The handler now focuses the host explicitly, skipping the case where the press
|
|
49
|
+
lands on an in-canvas text field so editing keeps its own focus.
|
|
50
|
+
- f370dba: `normalizeHref` no longer backtracks polynomially on a crafted email-like input:
|
|
51
|
+
the bare-email check matches domain labels linearly. As a side effect it is
|
|
52
|
+
stricter about what counts as an email — a domain with empty labels (consecutive
|
|
53
|
+
dots, e.g. `a@b..c`) is treated as a URL and gets `https://`, not `mailto:`.
|
|
54
|
+
- da91d59: Polish the rotate grip: it now renders as a clockwise circular-arrow glyph (a
|
|
55
|
+
`rotate-cw` icon) instead of a plain circle, and the connector line back to the
|
|
56
|
+
shape is gone. Hovering the grip shows a `grab` cursor; the cursor switches to
|
|
57
|
+
`grabbing` while a rotate gesture is in flight (overridable via the new
|
|
58
|
+
`rotate` cursor role).
|
|
59
|
+
- 1c7cc6c: Fix inline text editing on a scaled text element: the caret and selection highlight now apply the element's `scale`, so they line up with the rendered text instead of trailing behind it. Clicking to place the caret divides the point back through `scale` to hit the right glyph.
|
|
60
|
+
- Updated dependencies [9673846]
|
|
61
|
+
- Updated dependencies [ff90a95]
|
|
62
|
+
- Updated dependencies [3152317]
|
|
63
|
+
- Updated dependencies [f98730f]
|
|
64
|
+
- Updated dependencies [904cc09]
|
|
65
|
+
- @oh-just-another/scene@0.59.0
|
|
66
|
+
- @oh-just-another/renderer-core@0.58.0
|
|
67
|
+
- @oh-just-another/math@0.58.0
|
|
68
|
+
- @oh-just-another/history@0.57.2
|
|
69
|
+
|
|
3
70
|
## 0.58.0
|
|
4
71
|
|
|
5
72
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
# @oh-just-another/state
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Interactive editor engine: tools, modes, selection, history, and hit-testing over a Scene.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
The L2 interaction layer. It wires a Scene together with render targets and high-level operations into an `Editor` controller — framework-agnostic, with no React dependency. Depends on `@oh-just-another/types`, `@oh-just-another/math`, `@oh-just-another/scene`, `@oh-just-another/renderer-core`, `@oh-just-another/history`, `@oh-just-another/events`, `@oh-just-another/tokens`, and `xstate` (v5). Hosts feed it normalized pointer/keyboard/wheel events; it never imports the DOM directly.
|
|
6
6
|
|
|
7
7
|
## Quick start
|
|
8
8
|
|
|
9
9
|
```ts
|
|
10
10
|
import { LayeredCanvas, installBuiltinRenderers } from "@oh-just-another/renderer-canvas";
|
|
11
|
-
import { Editor
|
|
11
|
+
import { Editor } from "@oh-just-another/state";
|
|
12
12
|
import { emptyScene } from "@oh-just-another/scene";
|
|
13
13
|
|
|
14
14
|
installBuiltinRenderers();
|
|
@@ -24,37 +24,86 @@ const editor = new Editor({
|
|
|
24
24
|
initialMode: "select",
|
|
25
25
|
});
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
});
|
|
27
|
+
editor.subscribe(() => render());
|
|
28
|
+
editor.setMode("draw-rect");
|
|
30
29
|
```
|
|
31
30
|
|
|
32
|
-
##
|
|
31
|
+
## Concepts
|
|
32
|
+
|
|
33
|
+
### `Editor`
|
|
34
|
+
|
|
35
|
+
The centerpiece controller (`Editor`, `EditorOptions`, `LoadSceneOptions`). It owns the scene, selection, and viewport, and exposes a high-level API spanning:
|
|
36
|
+
|
|
37
|
+
- **Tools / modes** — `mode`, `setMode`, `toolLocked` / `setToolLocked` (sticky tool).
|
|
38
|
+
- **Selection** — `selection`, `selectAll`, `deleteSelected`, `moveSelectionBy`, `focusCycle`, plus link selection (`selectedLinks`, `selectedLink`).
|
|
39
|
+
- **History** — `undo`, `redo`, `canUndo`, `canRedo`, `history` (backed by `@oh-just-another/history`).
|
|
40
|
+
- **Hit-testing** — `hitTest(worldPoint)` returns a `PressTarget`; `hitAnnotation`.
|
|
41
|
+
- **Viewport / camera** — `panBy`, `zoomIn`, `zoomOut`, `zoomAt`, `zoomToFit`, `zoomToSelection`, `setViewportSize`, `screenToWorld`; grid via `gridEnabled`, `setGridVisible`, `toggleGrid`.
|
|
42
|
+
- **Z-order** — `bringToFront`, `sendToBack`.
|
|
43
|
+
- **Grouping / arrange** — `groupSelected`, `ungroup`, `expandSelectionWithDescendants`, `arrangeAsGrid`, `arrangeAsStack`.
|
|
44
|
+
- **Containers / frames** — frame membership reconciliation; `beginFrameNameEdit`, `commitFrameNameEdit`, `cancelFrameNameEdit`.
|
|
45
|
+
- **Link / edge editing** — link routing, endpoint anchors, `linkPreviewPath`, `linkAttachTarget`, `beginLinkCaptionEdit`, `commitLinkCaptionEdit`, `cancelLinkCaptionEdit`, `linkLabelWorld`.
|
|
46
|
+
- **Text editing** — inline caret/selection model: `beginTextEdit`, `commitTextEdit`, `cancelTextEdit`, `setEditingText`, `setTextCaretFromPoint`, `extendTextSelectionToPoint`, `caretIndexAtWorldPoint`, `editingTextOverlay`, plus `createTextAt`.
|
|
47
|
+
- **Image / file insert** — `insertImage`, `addElement`, `beginPlacement`, and a `FileDropRegistry` (see below).
|
|
48
|
+
- **GIF / animation playback** — `togglePlayback`, `hoverAnimatedElement`, `isPlaybackPaused`.
|
|
49
|
+
- **Brush strokes** — `beginBrushStroke`, `extendBrushStroke`, `commitBrushStroke`, `cancelBrushStroke`, `pendingBrushStroke`.
|
|
50
|
+
- **Annotations / comments** — `addAnnotation`, `removeAnnotation`, `toggleAnnotationResolved`, `addComment`, `removeComment`, `setSelectedAnnotation`, `setCommentAuthor`.
|
|
51
|
+
- **Scene lifecycle** — `scene`, `loadScene`.
|
|
52
|
+
|
|
53
|
+
Subscribe via `editor.subscribe(fn)` (coarse-grained) or the typed `EditorEvents` surface (`change`, `mode`, `selection`, `scene`, `history`, `viewport`).
|
|
54
|
+
|
|
55
|
+
### Modes
|
|
56
|
+
|
|
57
|
+
`Mode` and `DEFAULT_MODE` (`select`). The active mode dictates how a pointer-down is interpreted:
|
|
58
|
+
|
|
59
|
+
`select` · `hand` (pan) · `draw-rect` · `draw-ellipse` · `draw-text` · `draw-edge` · `draw-frame` · `brush`.
|
|
60
|
+
|
|
61
|
+
Pan and zoom remain available as gestures (middle-mouse / Space+drag / wheel) regardless of mode.
|
|
62
|
+
|
|
63
|
+
### Actions
|
|
64
|
+
|
|
65
|
+
A command registry for editor operations and their hotkeys:
|
|
66
|
+
|
|
67
|
+
- `ActionRegistry`, `defaultActionRegistry`, `registerBuiltinActions`, and types `Action`, `ActionCategory`, `ActionContext`, `HotkeyMatcher`.
|
|
68
|
+
- Predicates `hasSelection`, `hasSelectionOrLink`.
|
|
69
|
+
- Built-ins are exported both as bundles and individually so hosts can replace or compose them: history (`actionUndo`, `actionRedo`, `historyActions`), selection (`actionSelectAll`, `actionDeleteSelection`, `actionDuplicateSelection`, `selectionActions`), clipboard (`actionCopy`, `actionCut`, `actionPaste`, `clipboardActions`), z-order (`actionBringToFront`, `actionSendToBack`, `zOrderActions`), grouping (`actionGroupSelection`, `actionUngroupSelection`, `groupingActions`), zoom (`actionZoomIn`, `actionZoomOut`, `actionZoomReset`, `actionZoomToFit`, `zoomActions`), and modes (`actionModeSelect` … `actionModeFrame`, `actionToggleToolLock`, `actionCancel`, `modeActions`).
|
|
70
|
+
|
|
71
|
+
### Interaction machine
|
|
72
|
+
|
|
73
|
+
A pure xstate machine (`interactionMachine`) tracks gesture state and emits intent: `InteractionContext`, `InteractionEvent`, `InteractionEmit`, `PressTarget`, the pointer/mode event types, `interpretPressEnd`, `boundsFromPoints`, and `DRAG_THRESHOLD`. It never mutates a scene — emit events describe what the host should apply.
|
|
74
|
+
|
|
75
|
+
### Selection & handles
|
|
76
|
+
|
|
77
|
+
- `Selection` type plus the `selection.*` namespace of immutable-set helpers.
|
|
78
|
+
- Resize handles: `HandleId`, `ALL_HANDLES`, `HANDLE_SIZE`, `handlePosition`, `hitHandle`, `resizeBounds` (8 zoom-aware corner/edge handles).
|
|
79
|
+
|
|
80
|
+
### Overlay & peers
|
|
81
|
+
|
|
82
|
+
`renderOverlay`, `OverlayStyle`, `DEFAULT_OVERLAY_STYLE` draw selection outlines, handles, and drawing previews. Collaborative cursors/selections via `PeerCursor`, `PeerSelection`, `Editor.setPeerCursors` / `setPeerSelections`, and `PEER_CURSOR_BROADCAST_INTERVAL_MS`.
|
|
83
|
+
|
|
84
|
+
### Interactive hit-test registry
|
|
85
|
+
|
|
86
|
+
`registerInteractiveHitTester` / `getInteractiveHitTester` (`InteractiveHitTester`) — an extension point for hit-testing interactive element kinds without the engine knowing their internals.
|
|
87
|
+
|
|
88
|
+
### File-drop registry
|
|
89
|
+
|
|
90
|
+
`FileDropRegistry` with `FileDropHandler`, `FileDropContext`, `WalkOptions`, plus helpers: `IMAGE_MIME_TYPES`, `VIDEO_MIME_TYPES`, `isImageFile`, `isVideoFile`, `isSceneJsonFile`, `readFileAsDataURL`, `readFileAsText`, `walkDataTransfer`. Hosts register handlers for images, scene JSON, or custom payloads.
|
|
91
|
+
|
|
92
|
+
### DOM event normalizers
|
|
33
93
|
|
|
34
|
-
|
|
94
|
+
`fromPointerEvent`, `fromKeyboardEvent`, `fromWheelEvent`, `isEditableTarget` — translate raw DOM events into host-side CSS-pixel domain events. Optional: the engine accepts the normalized shapes regardless of source.
|
|
35
95
|
|
|
36
|
-
|
|
37
|
-
- **Emit events** (`InteractionEmit`) describe what the host should do: `SELECT_REPLACE`, `SELECT_CLEAR`, `MOVE_SHAPE`, `RESIZE_SHAPE`, `CREATE_SHAPE`.
|
|
38
|
-
- **`Editor`** acts as the glue: it manages the scene + selection, listens for DOM pointer events, performs hit-tests, sends machine events, applies emit events to the scene using `@scene` operations, and re-renders.
|
|
96
|
+
### Platform detection
|
|
39
97
|
|
|
40
|
-
|
|
98
|
+
`isMac`, `isWindows`, `isAndroid`, `isIOS`, `isLinux`, `isFirefox`, `isSafari`, `CTRL_OR_CMD_KEY`, `getDevicePixelRatio`, and the hotkey pretty-printers `formatHotkey`, `formatHotkeyParts` (`PrettyHotkeyDesc`).
|
|
41
99
|
|
|
42
|
-
|
|
100
|
+
### Annotation re-exports
|
|
43
101
|
|
|
44
|
-
|
|
45
|
-
| --------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------- |
|
|
46
|
-
| `Editor` / `EditorOptions` | Top-level controller. Mounts on a host element with two render targets and an initial scene. |
|
|
47
|
-
| `Mode` (`select` / `draw-rect` / `draw-ellipse`), `DEFAULT_MODE` | Editor modes. |
|
|
48
|
-
| `Selection`, `selection.*` | Immutable `ReadonlySet<ElementId>` + helpers (`single`, `add`, `remove`, `toggle`, `equals`). |
|
|
49
|
-
| `HandleId`, `handlePosition`, `hitHandle`, `resizeBounds`, `HANDLE_SIZE` | Resize-handle geometry (8 corner/edge handles, zoom-aware hit-test). |
|
|
50
|
-
| `interactionMachine`, `InteractionContext`, `InteractionEvent`, `InteractionEmit`, `PressTarget`, `DRAG_THRESHOLD`, `interpretPressEnd` | Pure xstate machine + types + helpers. |
|
|
51
|
-
| `renderOverlay(scene, selection, target, options?)`, `OverlayStyle` | Draws selection outlines, handles, and drawing previews on the overlay layer. |
|
|
52
|
-
| `fromPointerEvent` / `fromKeyboardEvent` / `fromWheelEvent` | DOM → domain event normalizers (CSS-pixel coords relative to host element). |
|
|
102
|
+
`Annotation` and `Comment` types are re-exported from `@oh-just-another/scene` so hosts wiring `addAnnotation` / `addComment` don't need a direct scene dependency for the data shapes. `normalizeHref` / `safeHref` sanitize link hrefs.
|
|
53
103
|
|
|
54
104
|
## Design notes
|
|
55
105
|
|
|
56
|
-
- **The machine owns gesture state, not scene state.** Selection and elements live in `Editor
|
|
57
|
-
- **Clicks vs drags via threshold.** A press becomes a drag once the pointer travels `DRAG_THRESHOLD`
|
|
58
|
-
- **
|
|
59
|
-
- **
|
|
60
|
-
- **DOM listeners are pointer-events** (`pointerdown` / `move` / `up` / `cancel`) with `setPointerCapture`. Touch and mouse share a single path; no separate touch handlers.
|
|
106
|
+
- **The machine owns gesture state, not scene state.** Selection and elements live in `Editor`; emit events describe intent and the host applies it, keeping the machine snapshot-testable.
|
|
107
|
+
- **Clicks vs drags via threshold.** A press becomes a drag once the pointer travels `DRAG_THRESHOLD` from the press origin; below it, the press resolves to a click effect via `interpretPressEnd`.
|
|
108
|
+
- **Handles are screen-sized.** `hitHandle` divides tolerance by viewport zoom so handles stay a fixed CSS size at any zoom.
|
|
109
|
+
- **Framework-agnostic.** No React, and no direct DOM access in the engine — hosts feed normalized events and own mounting. See [ohjustanother.site](https://ohjustanother.site).
|