@weasel-js/core 1.6.0 → 1.7.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 +266 -0
- package/README.md +24 -4
- package/dist/{depSchema-BhgZc0-Q.d.ts → builtins-DLn2lCr_.d.ts} +693 -53
- package/dist/{chunk-BJN5ZRIJ.js → chunk-3TY6FHAF.js} +7 -4
- package/dist/chunk-3TY6FHAF.js.map +1 -0
- package/dist/{chunk-H6N2JSZ3.js → chunk-DGWNNKGD.js} +3 -3
- package/dist/{chunk-H6N2JSZ3.js.map → chunk-DGWNNKGD.js.map} +1 -1
- package/dist/{chunk-THCSDHII.js → chunk-G4ZGQMY7.js} +2453 -1264
- package/dist/chunk-G4ZGQMY7.js.map +1 -0
- package/dist/{chunk-HPFJUUTF.js → chunk-LU4IJ52W.js} +5 -61
- package/dist/chunk-LU4IJ52W.js.map +1 -0
- package/dist/{chunk-D4AAWR64.js → chunk-M24CLARN.js} +4 -4
- package/dist/{chunk-D4AAWR64.js.map → chunk-M24CLARN.js.map} +1 -1
- package/dist/{chunk-4RJP2N2L.js → chunk-NE27EOLP.js} +8 -5
- package/dist/chunk-NE27EOLP.js.map +1 -0
- package/dist/{chunk-DOBSZOPR.js → chunk-UPZY4ART.js} +3 -3
- package/dist/{chunk-DOBSZOPR.js.map → chunk-UPZY4ART.js.map} +1 -1
- package/dist/{chunk-T3UQ3F6R.js → chunk-WPNA6L5O.js} +47 -13
- package/dist/chunk-WPNA6L5O.js.map +1 -0
- package/dist/{chunk-44EWMJV2.js → chunk-XMAWPGTK.js} +5 -5
- package/dist/{chunk-44EWMJV2.js.map → chunk-XMAWPGTK.js.map} +1 -1
- package/dist/clipboard.d.ts +1 -1
- package/dist/clipboard.js +3 -3
- package/dist/clone.d.ts +2 -2
- package/dist/clone.js +3 -3
- package/dist/grid-CY5UKlZh.d.ts +98 -0
- package/dist/index.css +11 -35
- package/dist/index.css.map +1 -1
- package/dist/index.d.ts +912 -1148
- package/dist/index.js +9 -9
- package/dist/insert.d.ts +2 -3
- package/dist/insert.js +1 -1
- package/dist/math.d.ts +1 -1
- package/dist/move.d.ts +5 -4
- package/dist/move.js +4 -4
- package/dist/move.js.map +1 -1
- package/dist/patterns-builtin.js +1 -1
- package/dist/{pointSnapToGrid-7mCcm6qG.d.ts → pointSnapToGrid-Dp2IxLGb.d.ts} +53 -60
- package/dist/renderer.css +11 -35
- package/dist/renderer.css.map +1 -1
- package/dist/renderer.d.ts +17 -8
- package/dist/renderer.js +9 -9
- package/dist/resize.d.ts +3 -3
- package/dist/resize.js +2 -2
- package/dist/routing.d.ts +1 -1
- package/dist/routing.js +1 -1
- package/dist/{types-CxC8P6W0.d.ts → types-D-9QWBy5.d.ts} +9 -6
- package/dist/{types-D_74YlWs.d.ts → types-qYpyTAiZ.d.ts} +4 -7
- package/package.json +15 -12
- package/dist/chunk-4RJP2N2L.js.map +0 -1
- package/dist/chunk-BJN5ZRIJ.js.map +0 -1
- package/dist/chunk-HPFJUUTF.js.map +0 -1
- package/dist/chunk-T3UQ3F6R.js.map +0 -1
- package/dist/chunk-THCSDHII.js.map +0 -1
- package/dist/grid-Dx2gQbNv.d.ts +0 -158
- package/dist/options-CzjxFoYZ.d.ts +0 -67
- package/dist/{math-DRa6HBBs.d.ts → math-MCQcf_gc.d.ts} +111 -111
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,271 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.7.0
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- b1142a7: `backgroundFill` now draws beneath every other layer. It used to be slotted just before the scene, which put it above the grid and above any custom layer also anchored `before: 'scene'`, so a canvas with a background fill painted over both.
|
|
8
|
+
- 96adc78: `<Canvas redrawOn>` compares its sources element by element, so an inline `redrawOn={[source]}` no longer unsubscribes and resubscribes on every render. Layer subscriptions and `redrawOn` subscriptions are now held by separate effects, so a layer change no longer churns the `redrawOn` ones either.
|
|
9
|
+
- 240138b: "Tool" now names only a contribution that can hold focus — one a user picks from a palette or holds on a key — and `isTool(entry)` is the test. What holds every kind of entry takes `Contribution` and says "entry":
|
|
10
|
+
|
|
11
|
+
- `useGestureDispatcher`'s and `DispatcherContext`'s `toolsById` is `entriesById`, a map of `Contribution`.
|
|
12
|
+
- `ownerToolId` is `ownerId` on `ScopedBinding`, `MatchResult` and a dispatch record's `RecordCandidate`.
|
|
13
|
+
- `RegistryEntry.toolId` is `ownerId`, and `Conflict.toolIds` is `ownerIds`.
|
|
14
|
+
- `<SceneCanvas ambient>` takes `SurfaceContribution[]`, and `useTools`'s `ambient` (and `ToolsApi.ambient`) takes `Contribution[]`, so a contribution no longer needs casting to `AnyTool`.
|
|
15
|
+
- `findConflicts`, `findScopedConflicts` and `buildRouteRegistry` take `Contribution`s.
|
|
16
|
+
- `defineViewportTool` and `ViewportToolDef` are removed; they were `defineTool` and `ToolDef` under another name.
|
|
17
|
+
- `FallthroughDiagram`'s "Tool" column is "Owner".
|
|
18
|
+
|
|
19
|
+
These are breaking renames for any consumer reading those fields or calling the removed function.
|
|
20
|
+
- 21ee45b: `<Canvas>`'s debug HUDs (cursor coordinates, pick list, modality) share one stylesheet instead of carrying three copies of the same box. The cursor-coordinates HUD no longer styles itself inline. Nothing renders differently.
|
|
21
|
+
- 32bb3be: New `DispatchRecord`: for one input, every binding that matched, what the claim and eligibility filters dropped, the ranked survivors with the step that placed each one below the last (`view`, `tier`, `specificity`, `context`, `order`), and what the walk did with each. `Dispatcher.explain` returns one without invoking anything. In dev builds the trace buffer on `window.__weaselDispatchLog__` now holds these records. They replace `DispatchLogEntry`, which is removed, so a reader of that buffer must move to the new shape. `handleInput`, `resolveAll` and `resolveOnly` now share one walk, so a prediction ranks candidates exactly as a dispatch does. `matchSortedWithBarred` reports the bindings an exclusive claim kept out. The types are also exported from `@weasel-js/core/routing`.
|
|
22
|
+
|
|
23
|
+
New `FallthroughDiagram` in `@weasel-js/ui` draws one record: the input, the matched set, one band per filter, and the ranked list with its walk, with the winner marked.
|
|
24
|
+
- 2e34d59: `viewport.dragPan` no longer declares a default binding, so a plain drag that no tool claims no longer pans the view. The hand tool still pans (H, or hold space), and a canvas that should pan on any unclaimed drag — a viewer, a map — adds the new `dragPanContribution()` to its `ambient` list; it takes the same `axis` and `inertia` params the hand tool passes. labkit's trial cameras keep panning on a plain drag.
|
|
25
|
+
|
|
26
|
+
This is a behavior change for any canvas with the `view` preset that relied on a plain drag panning under a tool that binds none.
|
|
27
|
+
- 32c5fb4: Path fills tessellate with earcut 3, which handles holes faster. earcut 3 ships its own typings and is ESM-only, as both packages already are.
|
|
28
|
+
- 0047d33: `fixedCornerOf(bounds, anchor)` is now exported from `@weasel-js/core`. It returns the corner a resize with that anchor leaves in place, the same one `resizeAction` pins, so code that reports or draws that corner no longer has to restate the rule.
|
|
29
|
+
- ad0378f: geom gains nearest-point queries and de Casteljau splitting. `nearestOnLine`, `nearestOnQuadratic` and `nearestOnCubic` return the nearest point to a probe with its `t` and distance; `nearestOnPath` does the same over a whole command stream, closing edges included, and says which command's segment it landed on. `splitLineAt`, `splitQuadraticAt` and `splitCubicAt` split a segment at `t` into two that trace it exactly, and `quadraticEvalAt` joins `cubicEvalAt`. Core's `pathDistanceToPoint`, `splitCubicAtT` and the anchor editor's nearest-segment search now run on these, so curve distances in picking are exact rather than read off a 16-sample polyline.
|
|
30
|
+
- b4227b8: `GradientEditor` offers every registered gradient kind, mesh included, instead of a fixed linear / radial / conic list. A kind counts as a gradient when its registry entry carries the two new optional `PaintKindEntry` slots, `stopsOf` (the stop list a paint reads as) and `fromStops` (a paint built from one); `listGradientKinds()` returns those kinds, and `switchGradientKind(paint, kind)` converts between any two of them. Between linear, radial and conic it is `withGradientKind` and keeps the geometry; to a mesh, a stop list becomes one full-height patch per gap between stops, so a horizontal ramp survives unchanged, and a mesh reads back as the ramp across its patches' top edges. `meshFromStops` and `meshStops` are exported. A mesh value shows `MeshEditor`'s corner colors; a registered kind's own `Editor` wins, as it does in `PaintInput`. `PaintInput` now converts through `switchGradientKind` too, so switching a gradient to a mesh carries its colors instead of seeding a new mesh from one color.
|
|
31
|
+
|
|
32
|
+
`GradientEditor`'s `value`, `onInput` and `onChange` are now typed `FillStyle` rather than `GradientFill`, since a mesh is not a `GradientFill`; a handler typed to take `GradientFill` needs widening.
|
|
33
|
+
|
|
34
|
+
`GradientEditor` also takes `svg`, off by default, which limits the choices to what an SVG file carries natively: the linear and radial gradients, blended in sRGB. A value already outside that set keeps its own kind and space on offer. `@weasel-js/svg` exports the rule as `nativeSvgKind(kind)` and `nativeSvgSpace(space)`, the same predicates its serializer uses to decide when a paint needs a fallback color or a `wzl:interpolate` attribute.
|
|
35
|
+
- 0cecdcf: A tool's `hotkey` can be any key. Besides `'space'` and the modifier names, a
|
|
36
|
+
tool may declare an ordinary key such as `hotkey: 'o'` and it engages while
|
|
37
|
+
that key is held, matched case-insensitively as every key spec is and never
|
|
38
|
+
while typing in a text field. A held key now releases even when its keyup
|
|
39
|
+
reports it differently from its keydown — Shift pressed mid-hold makes an `o`
|
|
40
|
+
press come up as `O`, which used to leave the tool stuck on.
|
|
41
|
+
|
|
42
|
+
Held-key engagement also works in a `<SceneCanvas>` with no `ActionsProvider`
|
|
43
|
+
of the consumer's own. Before, nothing registered `tool.offhand` there and
|
|
44
|
+
Space-for-hand did nothing. The registration is exported as
|
|
45
|
+
`useOffhandAction` for hosts that assemble their tools above their provider.
|
|
46
|
+
|
|
47
|
+
`Tool.onActivate` now actually fires — it was declared and forwarded by
|
|
48
|
+
`defineTool` but nothing called it. A tool is live while it holds the active
|
|
49
|
+
slot or is held by its `hotkey`: `onActivate` fires as it becomes live in
|
|
50
|
+
either, and `onDeactivate` as it stops being live in both, or when the canvas
|
|
51
|
+
unmounts. `onDeactivate` used to fire only when the active tool changed, never
|
|
52
|
+
when a held tool was released. Both receive `ToolLifecycleCtx` — just
|
|
53
|
+
`{ scratch }`, which is all `onDeactivate` was ever given; the parameter was
|
|
54
|
+
typed as a full `ToolCtx`, so a callback annotated that way no longer
|
|
55
|
+
typechecks and should close over what else it reads.
|
|
56
|
+
|
|
57
|
+
`ToolPresentation` takes `hide: true` to keep a tool off `ToolPalette` — for a
|
|
58
|
+
tool that is only ever held. It stays registered and selectable by id.
|
|
59
|
+
- 7c3cc5d: A `Journal` can now be observed and coalesces like its parent. It gains `subscribe` and `getVersion` (ready for `useSyncExternalStore`), which fire on every apply, undo, redo and coalesce inside the session and on each lifecycle transition, and `seal`, which ends a coalescing run as `History.seal` does. A journal now inherits its parent's `coalesceWindowMs`, clock and debug logger, so a session whose ops carry a `coalesceKey` merges rapid edits where it previously never did; pass `coalesceWindowMs: 0` to `beginJournal` to keep every push separate.
|
|
60
|
+
|
|
61
|
+
`HistoryEntry.pushes` says how many pushes an entry holds: 1 when pushed once, plus one for each push coalesced into it. It round-trips through `serialize`/`restore`; snapshots written before it read as 1.
|
|
62
|
+
|
|
63
|
+
The journals a `Scene` hands out forward all three new members.
|
|
64
|
+
- 722b267: Every canvas now routes its own input. `<SceneCanvas>`, labkit's `<CanvasStack>` and `<Stage>` each mount an `<InputScope>` — an actions registry, dep registry and active tool of their own — under whatever registries are in scope, so two canvases under one provider no longer hand the newest one everyone's gestures. Registries above a canvas read through to the canvas last used: `trigger`, `begin`, `list` and `useActiveToolContext` there answer from the scope that last took a pointerdown or wheel, and keystrokes dispatch only in that scope.
|
|
65
|
+
|
|
66
|
+
When bindings tie on scope and specificity, an action gated by an `eligible` rule that holds now outranks one with no rule: in path edit Escape exits the edit instead of resetting the tool, and a bare drag no tool binds marquees where selection is on offer and pans where it is not. The route-conflict check follows the same rule. Escape with nothing selected now returns to the default tool, as `tool.resetToDefault` documents; the standard `escape` action used to spend that press clearing an empty selection.
|
|
67
|
+
|
|
68
|
+
Canvases that should share input join one yoke: `const yoke = useYoke()`, then `yoke={yoke}` on each canvas and `<Yoke value={yoke}>` around a toolbar. A canvas with no yoke keeps its own tool.
|
|
69
|
+
|
|
70
|
+
Breaking: `ActionsScope`, `ActionsRegistry.setDepRegistry` and labkit's `CameraScopeContext` are removed; `ActionsRegistry` gains `activate()` and `isActive()`, which a hand-built registry must now supply. A registration made inside one canvas is no longer visible from a sibling canvas — a registry above reaches it only through the active scope. A nested `<WeaselProvider>` now hands its children the outer registry itself.
|
|
71
|
+
- 52078c5: A keystroke is dispatched once. Every gesture dispatcher listens for keys on
|
|
72
|
+
`window`, and one now skips a keydown that another dispatcher — or anything
|
|
73
|
+
else — has already claimed with `preventDefault`; before, two dispatchers
|
|
74
|
+
binding the same key both ran it. `keyboard: 'first'` makes a dispatcher listen
|
|
75
|
+
in the capture phase, ahead of the rest whatever the mount order. The lab
|
|
76
|
+
header's zoom uses it, so Mod+= over a trial with a camera zooms the trial and
|
|
77
|
+
not also a story's own canvas; over a trial without one, the key passes to the
|
|
78
|
+
story.
|
|
79
|
+
- 197fdf7: `useLassoTool({ behaviors })` now runs the behaviors it is given. They used to be dropped before they reached the lasso action. Each behavior's `onStart`, `onMove` and `onEnd` run as the gesture does. The first `onEnd` that returns ops has them applied in place of the default selection, and one that returns `null` leaves the selection alone. `selectFromLasso({ mode })` hit-tests through the canvas's own lasso, in its own mode.
|
|
80
|
+
|
|
81
|
+
`sceneToAdapter(...).hitTestLasso` gives the same answer as the lasso tool: shapes are tested by their drawn outline with rotation applied, not by their bounding box. An ancestor's clip now has to overlap the lasso itself, not just the shape. It still returns containers, as its `hitTestArea` does. `arrayAdapter.hitTestLasso` still tests bounding boxes.
|
|
82
|
+
|
|
83
|
+
Lasso `intersect` mode and the marquee now test curves by the drawn curve, not by their control points, so a lasso next to an ellipse no longer takes it. They also treat a path that is left open as a line with no fill.
|
|
84
|
+
- 5acf166: `useLassoTool({ minVertexSpacing })` now spaces the lasso's vertices by the value given; it used to be accepted and ignored. `transient`, `label`, `onGestureStart`, `onGestureEnd` and `debug` are removed from `UseLassoToolOptions` and `UseLassoSelectOptions`: nothing read them, so passing one did nothing. This is a breaking change to those types for any caller that passed one.
|
|
85
|
+
- 7f7b04f: The lasso tool selects by the polygon you draw rather than its bounding box. `SceneCanvas`'s lasso used to pick every shape its polygon's bounding box touched, in every hit mode, and `toolOptions.lasso.mode` never reached it. Now `intersect` (the default) takes shapes whose outline meets the polygon, `enclosed` takes shapes wholly inside it, and `centers` takes shapes whose center is inside it. Each test uses the shape's drawn outline with its rotation applied.
|
|
86
|
+
|
|
87
|
+
A marquee also tests a rotated shape that has no painter by its rotated rect instead of that rect's bounding box.
|
|
88
|
+
- fc3de06: A `{ px }` marker size now holds its screen size through a zoom, as a `{ px }` stroke width does, and so does a default-sized marker under a `{ px }` stroke width. The line stops short of the head by the same resolved size, and a node's grab reach resolves it against the view scale.
|
|
89
|
+
|
|
90
|
+
The renderer now draws a stroke's markers itself, for any path command whose stroke carries one — so a painter only sets `markerStart` / `markerMid` / `markerEnd`. `markerDrawCommands` is no longer exported: a painter that appended its commands would now draw every head twice. labkit's annotation arrows rely on the renderer, and their SVG export keeps the marker reference so the head is drawn there too.
|
|
91
|
+
- b8f2007: A `MarkerEntry` can declare `reads`, the stroke fields its `path` reads besides `size`. The renderer then reuses a head across stroke objects whose listed fields are equal, so a painter that builds a new stroke every frame no longer rebuilds its heads every frame; a new paint recolors the cached geometry. Every built-in, and every marker `@weasel-js/svg` imports, declares `reads: []`.
|
|
92
|
+
|
|
93
|
+
A marker's reach — what culling and a path node's grab band reserve for its head — is now measured per value of those fields, or per stroke object for an entry that does not declare them. Before, it was measured once per entry, so a custom head whose shape grows with a stroke field could be culled while still on screen.
|
|
94
|
+
- fd178be: A mode's declared shortcuts now reach the dispatcher. `modeShortcuts(registry, handlers)` returns an always-on contribution with one action per `entry` / `exit` / `discard` / `commit` / `cancel` chord, gated on the mode it acts on and riding the hotkey tier, so leaving a mode outranks Escape clearing the selection while a gesture in flight still cancels first. Pass it in `<SceneCanvas ambient>`; a role with no handler binds nothing. `ModeDefinition` gains `discard` (the soft presets declare Meta+Escape), and `ModeRegistry` gains `list()` — a hand-written registry has to add it.
|
|
95
|
+
|
|
96
|
+
`useTextEdit` / `useSceneTextEdit` take `escape: 'commit'` to keep the text on Escape instead of dropping it.
|
|
97
|
+
- 5201b8e: A mode switch now repaints the canvas with no consumer code.
|
|
98
|
+
|
|
99
|
+
- `@weasel-js/modes`: `getActiveModeFor(registry)` returns the reader `<SceneCanvas getActiveMode>` takes — the active mode's id and its allowed capability tags, implicit ones included — so apps stop rebuilding it. `ScopingDim` and `ModeDecorations` gain `subscribe` and `getVersion`, both following the registry; `ScopingDim.invalidate()` tells subscribers the target set moved inside one mode.
|
|
100
|
+
- `@weasel-js/core`: `RenderLayer.subscribe` lets a layer name the outside state its `draw` reads, and `<Canvas>`/`<SceneCanvas>` take `redrawOn` for sources no layer declares, such as the `ScopingDim` an `alphaFor` consults. Either one repaints the canvas when it notifies. `gateLayer`, `createTiledLayer`, `createViewportLayer` and `createParallaxLayer` pass their sources' `subscribe` through, and `subscribeToSources` builds the same for a layer of your own that draws others. `workspaceTintLayer({ registry, page?, intensity? })` paints the active mode's `WorkspaceVisual` — the whole canvas, or only the workspace around a page — and `modeDecorationLayer(decorations)` draws a `ModeDecorations`; both subscribe for themselves. Core now depends on `@weasel-js/modes`.
|
|
101
|
+
- bc2a7ef: Multi-select corner and rotation handles now follow the selection after a group
|
|
102
|
+
move. They used to stay where the union had been, so the next drag on the real
|
|
103
|
+
corner moved the set instead of resizing it. `ChromeState.unionBounds` is now
|
|
104
|
+
computed again on every read instead of being cached for the life of the state.
|
|
105
|
+
- 6819653: `areaSelect`, `insert`, `slice` and `lassoSelect` no longer declare a default binding. A plain drag carries no intent of its own, so none of them answers one unless something binds it: the select tool marquees on empty canvas, the shape tools insert, and the slice and lasso tools bind their own actions, as before. A canvas that relied on a plain drag marqueeing under a tool that binds no drag now opts in with the new `areaSelectContribution()` in its `ambient` list. `onEmptyCanvas` is the empty-canvas predicate the select tool and that contribution share.
|
|
106
|
+
|
|
107
|
+
This is a behavior change for any consumer that registered these actions without a tool binding them.
|
|
108
|
+
- 793987a: `TextStyle.script: 'super' | 'sub'` sets a whole text node as a superscript or subscript. It is the default every run inherits, the way `StyledRun.script` is for one run, and a run naming its own script replaces it.
|
|
109
|
+
|
|
110
|
+
A run shrunk by a relative size (`script` or `fontScale`) now holds its line open at the size it inherited: `ResolvedRun.strutSize` carries that size, and layout measures the line's height and baseline from it. A superscript alone on a line used to collapse the line to the superscript's own size, which contradicted the rule that a shifted run rides its line rather than reflowing it.
|
|
111
|
+
|
|
112
|
+
The edit overlay shows a node-level script, and now shows a node-level overline, which it had been dropping. The SVG writer puts a node-level script on `<text>` as `baseline-shift`, which the reader already carries onto every run.
|
|
113
|
+
- ca2f45f: Every click-versus-drag decision in the kit now reads one threshold, `DRAG_THRESHOLD_PX` (4 CSS pixels), through `pastDragThreshold`. Both now live in `@weasel-js/gestures`; `@weasel-js/routing` and `@weasel-js/core` re-export them as before. `useDragHandle` starts a drag at 4px instead of past 5px, labkit's `FloatingPanel` and the hud window's content click at 4px instead of 3px, and `startThresholdDrag`, `useReorderDragList` and `Select` default to the constant rather than their own literal 4.
|
|
114
|
+
|
|
115
|
+
The move action's `dragThresholdPx` option works again: set as `selectTool.move.dragThresholdPx` on `<SceneCanvas>`, it holds the selection in place until the pointer has travelled that far on screen. It has been ignored since the `useMove` hook was removed. It cannot lower the threshold below the dispatcher's.
|
|
116
|
+
- 082c63f: A path node's grab reach now covers the full painted extent of its marker heads, the same measure culling uses. Before, it grew only by each head's inset, so `arrow-open` and `bar` — which inset nothing — painted arms outside the clickable band.
|
|
117
|
+
- 242e9f7: `routeGestureForSpecKind` and its inverse `specKindForRouteGesture` now live in `@weasel-js/gestures`, read from one table; `routeToSpec` and routing's route registry both use it. `@weasel-js/routing` and `@weasel-js/core/routing` re-export both.
|
|
118
|
+
|
|
119
|
+
Every routing type with an overlay parameter — `Tool`, `ToolDef`, `ViewportToolDef`, `Contribution`, `ContributionChrome`, `useTools` and `useContributions` with their option and result types — now defaults it to the kernel's overlay type (`KernelOverlay`), as `defineTool` already did. Under core that is `RenderLayer`, so a routing `Tool<S>` fits `<SceneCanvas tools>` without naming the overlay. Functions that read tools without reading overlays (`buildRouteRegistry`, `findConflicts`, `reportRouteConflicts`, the dispatcher's `toolsById`) take any overlay explicitly.
|
|
120
|
+
- f8f0160: A plain object made in another realm (an iframe, a VM context, or `structuredClone` under jsdom) is now treated as a plain object. labkit's config defaults used to overwrite a stored config that came from another realm instead of filling it, and forge's story-arg handling treated one as opaque. `@weasel-js/core` exports the check as `isPlainObject`.
|
|
121
|
+
- 455e4bc: New package, `@weasel-js/quantity`: a number anywhere the engine takes one can be bare or tagged (`{ value, unit?, display? }`), and a tagged value keeps its unit and display through JSON, history and every edit (`retag`). A display is plain data — `fraction()`, `ratio()`, `percent()`, `unit('mm')`, `currency('USD')`, `duration()`, `bytes()`, `roman()`, `ordinal()`, `multiplier()`, `zoom()`, `compact()`, `decimal()`, `integer()` — and `qty(value, display)` gives its text, spoken text, unstyled HTML and, for fractions, MathML. `registerDisplayKind` adds a kind or replaces a built-in one.
|
|
122
|
+
|
|
123
|
+
Breaking: the unit system (`UnitSystem`, `resolveUnit`, `formatUnit`, …) now lives in `@weasel-js/quantity`; core still re-exports the same names. `formatNumber`, `formatCompact`, `parseNumber`, `parseSignedNumber` and `MINUS_SIGN` are no longer exported from `@weasel-js/ui` — import them from `@weasel-js/quantity`. `formatZoom` is gone: use `qty(z, zoom()).text`. A pref's `format: 'plain' | 'compact'` is now `display: Display` (`ToolPrefNumberFormat` and `PrefNumberFormat` are removed), `PropertyField`'s `notation` is now `display`, and labkit's `f.number().format('compact')` is `.display(compact())`.
|
|
124
|
+
|
|
125
|
+
`PropertyField`, `UnitField`, `Slider` and `BandEditor` take a `display`, which drives what they show, what they read back when typed, and their `aria-valuetext`. A `BandEditor` seam at 1/12 with `display={fraction()}` announces "1 over 12" rather than `0.08333333333333333`, and a band whose `from` is tagged stays tagged through drags, splits and merges. The Timeline rate slider now speaks "4 times" rather than "4x".
|
|
126
|
+
- a028cc3: New package `@weasel-js/registry`: `createReflectable()` is a keyed store a registry embeds to hand out a uniform read-only `Reflection` — `get`, `has`, `entries()`, `subscribe` and `getVersion`, shaped for `useSyncExternalStore`. Each entry reports the registrant's `source` and the registrants it displaced (`shadowed`), so overrides of one key show up as conflicts.
|
|
127
|
+
|
|
128
|
+
The kit's module-level registries now expose one: `paintKindRegistry`, `markerRegistry`, `opFactoryRegistry`, `fontRegistry`, `fontOutlineRegistry` (which also notifies as a face's load state moves), and, from `@weasel-js/core/renderer`, `programSourceRegistry` and `textureRegistry`. `ModeRegistry` gains `reflection`. Built-in paint kinds and markers report `source: 'kit'`.
|
|
129
|
+
|
|
130
|
+
Fixes paint-kind and marker overrides disposed out of order: with two overrides of one id, disposing the earlier and then the later used to restore the already-disposed earlier one instead of the built-in. Overrides now stack, and each disposer removes only its own entry.
|
|
131
|
+
- 667f14f: `usePinchGesture` is removed. This is a breaking change for anyone importing it. It attached its own pointer
|
|
132
|
+
listeners beside the gesture dispatcher, which already handles a two-finger touch pinch: its `multitouch` channel
|
|
133
|
+
carries the centroid and spread of the held pointers, and `viewport.pinchZoom` (`pinchZoomAction` /
|
|
134
|
+
`makePinchZoomAction`) binds to it. Bind an action to `{ kind: 'multiTouch', fingers: 2 }` in place of the hook.
|
|
135
|
+
- e442bcb: `resizeAction` no longer has a default binding. Its bare `{ kind: 'drag' }` claimed every drag at ambient scope and tied `areaSelect`'s, so a canvas with `pick` and `transform` warned `route conflict: [*] drag — declared by resize, areaSelect` on load. Resize handles still route to it through the `transform` preset. Code that registers `resizeAction` without that preset has to bind it to its handles itself, the way `selectionTransformBindings()` does.
|
|
136
|
+
- aad77d3: The route-conflict check no longer reports two bindings whose actions' `eligible` rules can never hold together, so each mode's own Escape exit from `modeShortcuts` stops warning. The new `rulesExclusive(a, b)` is the test it uses: `true` only when no context passes both rules.
|
|
137
|
+
|
|
138
|
+
`createDepRegistry()` returns the stock dep registry `<DepRegistryProvider>` mounts, from routing's React-free entry and from core, for a dispatcher driven without a provider tree.
|
|
139
|
+
|
|
140
|
+
`DRAG_THRESHOLD_PX` and `pastDragThreshold(from, to)` expose the distance a press travels before `useGestureDispatcher` treats it as a drag, and the dispatcher reads that same definition.
|
|
141
|
+
|
|
142
|
+
There is one `defineTool` now. Routing's defaulted its overlay type to `unknown`, so its tools failed core's `tools` prop; routing now defaults it to `KernelOverlay`, which a kernel sets by merging into the new `OverlaySchema` interface, and core merges `RenderLayer` there and re-exports routing's `defineTool` and `defineViewportTool` instead of wrapping them.
|
|
143
|
+
- 8999210: `sceneToAdapter(...).hitTestArea` gives the same answer as the marquee in `SceneCanvas`: shapes are tested by their drawn outline with rotation applied, not by their bounding box. It still returns containers.
|
|
144
|
+
- 94cf4cd: **Breaking.** A bare `<SceneCanvas>` now only renders. It keeps a selection that
|
|
145
|
+
no input sets and runs the gesture dispatcher for bindings you add, but it no
|
|
146
|
+
longer picks, moves, transforms or edits, draws no selection chrome, pans and
|
|
147
|
+
zooms nothing, and binds no keys. `features={['draw']}` restores everything it
|
|
148
|
+
used to do, and adds the hand tool (H, or hold Space), which it used to register
|
|
149
|
+
only when `viewport` was passed.
|
|
150
|
+
|
|
151
|
+
`features` takes presets that compose in any combination: `view` (wheel pan and
|
|
152
|
+
zoom, pinch, zoom keys, the hand tool — passing `viewport` implies it), `pick`
|
|
153
|
+
(the select tool and the selection outline), `move` (drag to move, Alt-drag to
|
|
154
|
+
clone), `transform` (resize and rotation handles), `edit` (undo/redo, delete,
|
|
155
|
+
duplicate, group, nudge, select-all, Escape, clipboard, fill and stroke, with
|
|
156
|
+
their keys), `arrange` (align, distribute, reorder, flip), `paths` (pathfinder
|
|
157
|
+
and anchor editing), `ingest` (dropped and pasted content), and `draw` for all
|
|
158
|
+
of them. `FEATURE_ACTION_IDS` lists which kit action each one registers. A tool
|
|
159
|
+
brings the actions it binds, so `defaultTools={['rect']}` registers `insert`
|
|
160
|
+
under any preset; `defaultTools` now defaults to none.
|
|
161
|
+
|
|
162
|
+
`toolBundle` and `BUNDLE_TOOLS` are removed. `toolBundle="minimal"` is
|
|
163
|
+
`features={['draw']}`; `"standard"` adds `defaultTools={['rect', 'ellipse',
|
|
164
|
+
'line']}`, and `"exhaustive"` adds `defaultTools={BUILTIN_TOOL_IDS}`.
|
|
165
|
+
|
|
166
|
+
The select tool only chooses now: pick, marquee, clear. Moving, cloning,
|
|
167
|
+
resizing and rotating the selection are always-live bindings of their own,
|
|
168
|
+
`selectionMoveContribution` and `selectionTransformContribution`, so they work
|
|
169
|
+
under any tool that leaves the drag unclaimed. `useSelectTool` no longer takes
|
|
170
|
+
`move` or `reparentOnDrop`; pass them to `selectionMoveContribution`, or give
|
|
171
|
+
`<SceneCanvas>` a `selectTool.move`. A host mounting `useSelectTool` on its own
|
|
172
|
+
dispatcher has to add those entries itself to keep drag-to-move.
|
|
173
|
+
|
|
174
|
+
`rotate` and `clone` no longer carry a bare-drag default binding, which made
|
|
175
|
+
any drag no tool claimed rotate a non-empty selection.
|
|
176
|
+
|
|
177
|
+
A canvas can run with no active tool: `useTools` takes `active` as optional or
|
|
178
|
+
`null`, and `ToolsApi.active` and `ActiveToolContext.active` can be `null`.
|
|
179
|
+
`ActiveToolContextProvider` starts empty rather than at `'select'`, and the
|
|
180
|
+
first `useTools` call seeds it unless the provider was given `initialActive`.
|
|
181
|
+
- d647c9b: `<SceneCanvas>`'s `layers` doc now says the default scene slot paints each node's `data.fill`. It named `data.color`, which the default painter stopped reading when node paint moved onto `FillStyle`.
|
|
182
|
+
- d7aaeb1: `<SceneCanvas layers>` now types its scene slot as partial, matching what it already did at runtime: a slot such as `{ scene: { cull: true } }` keeps the default `drawOne` and no longer needs one restated to typecheck. The prop's type is exported as `SceneCanvasLayers`. `LayersMap` takes an optional third parameter for the scene slot's type; it defaults to the full `SceneSlotConfig`, so existing uses are unchanged.
|
|
183
|
+
- 38f524c: `<SceneCanvas>` takes the app's mode registry as `modes` in place of `getActiveMode`. This is a breaking change: replace `getActiveMode={getActiveModeFor(registry)}` with `modes={registry}`. The canvas now repaints when the mode switches, and the dev-time route-conflict check reads the app's own modes instead of the kit's `DEFAULT_MODES`, so a clash that only an app-defined mode allows is reported. `useTools` and `useContributions` take the same `modes` option for a consumer assembling its own tools.
|
|
184
|
+
- 2336d9c: `splitPathByLine` is renamed `splitPathBySegment`, and `SplitByLineOptions` is renamed `SplitBySegmentOptions` — breaking for anyone importing either name. The function now cuts only where the segment itself crosses the shape. It used to clip against the infinite line through the segment, so a concave shape crossed at one arm was also cut at every other arm the line's extension reached. A stretch of the segment that begins or ends inside the fill cuts nothing; the chords it crosses end to end still cut. Curves stay curves: quadratic and cubic segments are split exactly at the crossings, so the pieces trace the original outline instead of a flattened polyline. Each connected piece comes back as its own path, and contours the segment misses come back together as one final piece. A cut through an exact vertex now splits the shape too. Paths whose contours cross each other or themselves are still flattened, because they are resolved by a polygon union before cutting.
|
|
185
|
+
- 71d54e4: `guideSnapStrategy` and `snapBackOrDelete` now default to `AUTO_ORIGIN_PROJECTION`, like `gridSnapStrategy` and `snap`, so a `Path` pose snaps to guides and snaps back without passing `pathOriginProjection`. Rect poses behave as before.
|
|
186
|
+
- 03e9385: Dragging a `Path`-posed node with `selectTool.snap` set no longer turns every coordinate to `NaN`. The `snap` behavior read the pose's origin as a rect's `x`/`y` by default, which a `Path` doesn't have. It now defaults to `AUTO_ORIGIN_PROJECTION`, the same projection `gridSnapStrategy` uses, which reads a `Path`'s bounds origin.
|
|
187
|
+
- 8c1cd8d: `window.__weaselTest.getActiveToolId()` now reports the canvas's active tool. It read a ref nothing wrote, so it returned `null` whatever tool was active; it now reads the tool registry directly.
|
|
188
|
+
- d5a9fbf: The text node's properties now cover every authored `TextStyle` field. Italic, underline, strikethrough, overline, superscript and subscript sit in one row of glyph toggles; Italic was a Normal / Italic dropdown, and superscript and subscript were missing. Alignment is drawn with glyphs, and reads a `start` / `end` alignment as the edge it paints at. New rows: Wrap, Direction, and the box's vertical alignment (`data.verticalAlign`, the field the `kit:text` painter and the editor already read). New glyphs: `textAlignLeft` / `Center` / `Right` and `textAlignTop` / `Middle` / `Bottom`.
|
|
189
|
+
|
|
190
|
+
Two schema additions make that possible and are general: `ToolPrefBoolean.encoding` (`ToolPrefBooleanEncoding`, `PrefBooleanEncoding` in `@weasel-js/ui`) stores a flag as another value, which is how Italic writes `fontStyle`; and `ToolPrefEnum.clearable` lets a toggle's lit segment be clicked off, removing the field. `PropertyControl`'s enum takes the matching `onClear`. The text tool's Script control in the options bar is now such an enum, replacing the app-drawn renderer it needed.
|
|
191
|
+
|
|
192
|
+
Fixed: in `SelectionPanel`, editing any field of an object leaf (the text style, the stroke) with several nodes selected wrote one object over every node, dropping each node's other fields, and reported every field as mixed. Fields are now read and written node by node; `PropertyRenderContext.update` is the new per-node write.
|
|
193
|
+
- 3a20620: The move, resize and rotate options that had been accepted and ignored since their hooks were deleted now work again, through the action pipeline:
|
|
194
|
+
|
|
195
|
+
- `moveLabel` / `resizeLabel` / `rotateLabel` name the history entry. Unset, a move takes the label of the ops a behavior or layout drop commits, where it used to say `Move` regardless.
|
|
196
|
+
- `transient` commits through `scene.untracked`, with no undo entry and without calling the consumer `applyOps` hook. Unset, a behavior's `defaultTransient: true` turns it on.
|
|
197
|
+
- `onGestureStart(ids)` fires when the gesture starts changing poses, and `onGestureEnd(committed)` exactly once after it. Resize and rotate now pass the id list rather than a single id.
|
|
198
|
+
- `UseMoveOptions.expandIds` replaces the moved id set, so a drag can carry linked nodes; `[]` declines the drag.
|
|
199
|
+
- `UseRotateOptions.behaviors` run as a rotate behavior chain, and `pivot: 'each'` turns each node about its own center.
|
|
200
|
+
|
|
201
|
+
`SELECTION_TRANSFORM_BINDINGS` and the `selectionTransformContribution` constant become the functions `selectionTransformBindings(options)` and `selectionTransformContribution(options)`, which take the rotate options; resize options travel in the `resizePolicy` dep, which `useResizePolicy` and `<SceneCanvas selectTool.resize>` now fill with the lifecycle fields too.
|
|
202
|
+
|
|
203
|
+
A resize behavior's `onEnd` returning ops now commits them in place of the resize, as the behavior contract says. A move whose behavior claimed or aborted the commit no longer leaves its preview poses published over the document.
|
|
204
|
+
|
|
205
|
+
Removed, since nothing read them: `UseInsertOptions` (the insert action takes binding params and the `insert` dep); `debug`, `handleHitRadius` and `rotationHandleDistance` on the resize and rotate options and `debug` on `UseSelectToolOptions` (`<SceneCanvas debug>` records handle hitboxes, and `selectTool.handleHitRadius` sizes them); `CloneBehavior.defaultTransient`.
|
|
206
|
+
- Updated dependencies [6819653]
|
|
207
|
+
- Updated dependencies [af5281e]
|
|
208
|
+
- Updated dependencies [240138b]
|
|
209
|
+
- Updated dependencies [32bb3be]
|
|
210
|
+
- Updated dependencies [ad0378f]
|
|
211
|
+
- Updated dependencies [da20f95]
|
|
212
|
+
- Updated dependencies [9647b3c]
|
|
213
|
+
- Updated dependencies [0cecdcf]
|
|
214
|
+
- Updated dependencies [7c3cc5d]
|
|
215
|
+
- Updated dependencies [722b267]
|
|
216
|
+
- Updated dependencies [52078c5]
|
|
217
|
+
- Updated dependencies [fc3de06]
|
|
218
|
+
- Updated dependencies [6357f14]
|
|
219
|
+
- Updated dependencies [fd178be]
|
|
220
|
+
- Updated dependencies [5201b8e]
|
|
221
|
+
- Updated dependencies [793987a]
|
|
222
|
+
- Updated dependencies [ca2f45f]
|
|
223
|
+
- Updated dependencies [242e9f7]
|
|
224
|
+
- Updated dependencies [f257369]
|
|
225
|
+
- Updated dependencies [408ce25]
|
|
226
|
+
- Updated dependencies [455e4bc]
|
|
227
|
+
- Updated dependencies [a028cc3]
|
|
228
|
+
- Updated dependencies [aad77d3]
|
|
229
|
+
- Updated dependencies [94cf4cd]
|
|
230
|
+
- Updated dependencies [38f524c]
|
|
231
|
+
- Updated dependencies [dbc3c3e]
|
|
232
|
+
- Updated dependencies [f8f6041]
|
|
233
|
+
- @weasel-js/routing@1.7.0
|
|
234
|
+
- @weasel-js/modes@1.7.0
|
|
235
|
+
- @weasel-js/geom@1.7.0
|
|
236
|
+
- @weasel-js/gestures@1.7.0
|
|
237
|
+
- @weasel-js/history@1.7.0
|
|
238
|
+
- @weasel-js/paint@1.7.0
|
|
239
|
+
- @weasel-js/text@1.7.0
|
|
240
|
+
- @weasel-js/quantity@1.7.0
|
|
241
|
+
- @weasel-js/registry@1.7.0
|
|
242
|
+
- @weasel-js/font@1.7.0
|
|
243
|
+
- @weasel-js/cursor@1.7.0
|
|
244
|
+
|
|
245
|
+
## 1.6.1
|
|
246
|
+
|
|
247
|
+
### Patch Changes
|
|
248
|
+
|
|
249
|
+
- b209a8e: A feature installs from one entry. `SurfaceContribution` extends `Contribution` with `views` and `attach(api, deps)`, and `Contribution` gains `deps`; `<SceneCanvas ambient>` installs every role an entry declares and removes them with it. `mergeContributions` throws on a duplicate dep name or view id as well as a duplicate entry id.
|
|
250
|
+
|
|
251
|
+
Bindings can be scoped to views with `opts.views`; a binding that names the view an input landed in outranks bindings that do not. `InvocationCtx.viewId` and `RuleCtx.viewId` carry that view, layer `data.viewId` names the view a draw is for, and the `rootView` dep answers the surface camera from inside a view.
|
|
252
|
+
|
|
253
|
+
`createMinimapContribution` puts a minimap inside a canvas, with a linked crosshair, and `createLinkedCursorContribution` draws the crosshair alone. `<MinimapCanvas>` now runs on a dispatcher, publishes its pointer, and takes an `id`.
|
|
254
|
+
|
|
255
|
+
Breaking: `PointerContextValue` is a store — `get`, `set`, `subscribe`, `getVersion` — in place of `pointerRef`, and `PointerWorldPos` carries `viewId`. `usePointerPosition()` follows it. `useHud`'s ref is optional; `useHudContribution(hud, options)` attaches the HUD as well as routing its input.
|
|
256
|
+
- 7683659: A labkit camera now routes through weasel's gesture dispatcher. `<CanvasStack>` and `<Stage>` pan on a drag, zoom on the wheel and report a tap through `CameraInput`, which drives core's `viewport.dragPan` and `viewport.zoom` over the camera as a weasel `View` (`toCameraView` / `fromCameraView`). The loupe joins the same dispatcher, taking the wheel while its lens is up. A trial publishes its pointer, and `RenderContext.trial.pointer` is that store; `<LinkedCursor>` draws the crosshair on the stage while the pointer is over another view.
|
|
257
|
+
|
|
258
|
+
Breaking: `usePanZoom` is removed. `PointerContextProvider` takes an optional `store`, and core exports `crosshairRects`.
|
|
259
|
+
- Updated dependencies [b209a8e]
|
|
260
|
+
- @weasel-js/routing@1.6.1
|
|
261
|
+
- @weasel-js/cursor@1.6.1
|
|
262
|
+
- @weasel-js/font@1.6.1
|
|
263
|
+
- @weasel-js/geom@1.6.1
|
|
264
|
+
- @weasel-js/gestures@1.6.1
|
|
265
|
+
- @weasel-js/history@1.6.1
|
|
266
|
+
- @weasel-js/paint@1.6.1
|
|
267
|
+
- @weasel-js/text@1.6.1
|
|
268
|
+
|
|
3
269
|
## 1.6.0
|
|
4
270
|
|
|
5
271
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -28,10 +28,28 @@ npm install @weasel-js/core react
|
|
|
28
28
|
|
|
29
29
|
`useScene` holds the scene: a tree of leaf and container nodes, each carrying your data and a pose. Every change to it is an **op**, which is what makes it undoable. `<SceneCanvas>` renders a scene and routes input to it.
|
|
30
30
|
|
|
31
|
+
A bare `<SceneCanvas>` only renders. It keeps a selection that nothing sets from input, and it routes any bindings you add, but it has no tools, no standard actions, no keys and no selection chrome. Behavior comes from presets named in `features`, each independent of the others:
|
|
32
|
+
|
|
33
|
+
| Preset | Turns on |
|
|
34
|
+
|---|---|
|
|
35
|
+
| `view` | wheel pan and zoom, pinch, the zoom keys, the hand tool (H, or hold Space). Passing `viewport` implies it. |
|
|
36
|
+
| `pick` | the select tool, active from the start: click to pick, drag on empty to marquee, click on empty to clear; the selection outline |
|
|
37
|
+
| `move` | drag a body to move it, Alt-drag to clone — under any tool that leaves the drag unclaimed |
|
|
38
|
+
| `transform` | resize and rotation handles |
|
|
39
|
+
| `edit` | undo/redo, delete, duplicate, group/ungroup, nudge, select-all, Escape, cut/copy/paste, fill and stroke, and their keys |
|
|
40
|
+
| `arrange` | align, distribute, reorder, flip |
|
|
41
|
+
| `paths` | pathfinder operations, path editing and anchor editing |
|
|
42
|
+
| `ingest` | dropped and pasted content |
|
|
43
|
+
| `draw` | all of the above |
|
|
44
|
+
|
|
45
|
+
`defaultTools` adds built-in tools on top, and each brings the actions it binds: `defaultTools={['rect']}` brings `insert`.
|
|
46
|
+
|
|
31
47
|
Interactions are **actions** — descriptors registered into an actions registry — reached by **gesture bindings**. A tool is a list of `{ spec, actionId }` pairs and nothing more; the kit's own select, shape and viewport tools are built that way.
|
|
32
48
|
|
|
33
49
|
```tsx
|
|
34
|
-
import {
|
|
50
|
+
import {
|
|
51
|
+
SceneCanvas, BUILTIN_TOOL_IDS, useScene, useSelection, gridSnapStrategy, type RectPose,
|
|
52
|
+
} from '@weasel-js/core';
|
|
35
53
|
|
|
36
54
|
const W = 800, H = 600;
|
|
37
55
|
|
|
@@ -49,7 +67,8 @@ export function Editor() {
|
|
|
49
67
|
scene={scene}
|
|
50
68
|
selection={selection}
|
|
51
69
|
selectionMode="multi"
|
|
52
|
-
|
|
70
|
+
features={['draw']}
|
|
71
|
+
defaultTools={BUILTIN_TOOL_IDS}
|
|
53
72
|
selectTool={{ snap: gridSnapStrategy<RectPose>(20) }}
|
|
54
73
|
layers={{
|
|
55
74
|
grid: { spacing: 20, bounds: () => ({ x: 0, y: 0, width: W, height: H }) },
|
|
@@ -71,7 +90,7 @@ Lower-level surfaces take a narrow **adapter** instead of a scene — a few meth
|
|
|
71
90
|
|
|
72
91
|
An `Action` is a named operation — `delete`, `duplicate`, `group`, `insert`, `viewport.dragPan` — paired with the input that triggers it. `<ActionsProvider>` holds the registered descriptors, and the gesture dispatcher matches live input against each one's `defaultBinding` and the active tool's bindings. Keystrokes and pointer gestures take the same path, so a keyboard shortcut and a drag are two bindings on one action rather than two mechanisms.
|
|
73
92
|
|
|
74
|
-
`<SceneCanvas>` mounts a provider when none is above it and registers the kit-standard actions: escape, select-all, delete, duplicate, group and ungroup, undo and redo, flip, nudge, reorder, align, distribute, the pathfinder booleans, path-anchor editing, fill and stroke, clipboard, and the pointer-driven move, resize, rotate,
|
|
93
|
+
`<SceneCanvas>` mounts a provider when none is above it and registers the kit-standard actions its `features` presets name — under `draw`, all of them: escape, select-all, delete, duplicate, group and ungroup, undo and redo, flip, nudge, reorder, align, distribute, the pathfinder booleans, path-anchor editing, fill and stroke, clipboard, and the pointer-driven move, resize, rotate, clone, area-select and lasso. `FEATURE_ACTION_IDS` lists which preset registers which. `insert` comes with the shape tools that bind it.
|
|
75
94
|
|
|
76
95
|
```tsx
|
|
77
96
|
<SceneCanvas
|
|
@@ -79,6 +98,7 @@ An `Action` is a named operation — `delete`, `duplicate`, `group`, `insert`, `
|
|
|
79
98
|
height={H}
|
|
80
99
|
scene={scene}
|
|
81
100
|
selection={selection}
|
|
101
|
+
features={['draw']}
|
|
82
102
|
actions={{
|
|
83
103
|
duplicate: null, // drop the default
|
|
84
104
|
'app.publish': { // add your own
|
|
@@ -95,7 +115,7 @@ An `Action` is a named operation — `delete`, `duplicate`, `group`, `insert`, `
|
|
|
95
115
|
/>
|
|
96
116
|
```
|
|
97
117
|
|
|
98
|
-
The `actions` prop takes `null` to unregister every default, or a record keyed by action id. Each value is `null` to drop that one id, a partial `Action` to merge onto the default of the same id, or a complete `Action` to register a new one.
|
|
118
|
+
The `actions` prop applies on top of what the presets registered. It takes `null` to unregister every default, or a record keyed by action id. Each value is `null` to drop that one id, a partial `Action` to merge onto the default of the same id, or a complete `Action` to register a new one.
|
|
99
119
|
|
|
100
120
|
An action does its work through `invoker`, not a bare callback. `{ timing: 'immediate' }` runs once; `{ timing: 'ongoing' }` returns a handle so a drag can preview while it moves and commit at the end. The deps an invoker reads (`selection`, `scene`, `applyOps`, …) are declared in `requires` and resolved at invocation time, which is what lets a consumer swap one — see `useDepSource`. To fire an action yourself, wrap the canvas in your own `<ActionsProvider>` and call `trigger(id, params)` on the registry `useActionsRegistry()` returns.
|
|
101
121
|
|