@weasel-js/core 1.1.0 → 1.2.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 +480 -0
- package/dist/{DrawCommand-BbPrSTra.d.ts → DrawCommand-uNkv5TjO.d.ts} +24 -1
- package/dist/{chunk-3B4QEB2G.js → chunk-7KYLIC3E.js} +18876 -18256
- package/dist/chunk-7KYLIC3E.js.map +1 -0
- package/dist/{chunk-JOSUVS6C.js → chunk-ADWOB5K2.js} +2 -2
- package/dist/chunk-ADWOB5K2.js.map +1 -0
- package/dist/{chunk-J3YC3GVF.js → chunk-CKADTCHK.js} +2 -2
- package/dist/chunk-CKADTCHK.js.map +1 -0
- package/dist/clipboard.d.ts +1 -1
- package/dist/clipboard.js +1 -1
- package/dist/index.d.ts +488 -119
- package/dist/index.js +3 -3
- package/dist/{paint-types-CnLIzqq1.d.ts → paint-types-1fUdZUaH.d.ts} +10 -5
- package/dist/patterns-builtin.d.ts +1 -1
- package/dist/{registry-DGBrjGbB.d.ts → registry-l3JX0Z6b.d.ts} +69 -11
- package/dist/renderer.d.ts +7 -53
- package/dist/renderer.js +3 -3
- package/dist/routing.d.ts +4 -4
- package/dist/routing.js +1 -1
- package/dist/stroke-DaxnQ-u7.d.ts +153 -0
- package/dist/{types-vqxpCFVp.d.ts → types-C2R2bjxf.d.ts} +8 -0
- package/package.json +6 -6
- package/dist/chunk-3B4QEB2G.js.map +0 -1
- package/dist/chunk-J3YC3GVF.js.map +0 -1
- package/dist/chunk-JOSUVS6C.js.map +0 -1
- package/dist/viewToMat3-CQW_YqDw.d.ts +0 -67
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,485 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.2.0
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 53016f7: Add `actionShortcuts(action)` — an action's keyboard bindings, flattened into
|
|
8
|
+
the shape `formatShortcut` / `formatShortcutParts` render.
|
|
9
|
+
|
|
10
|
+
Binding lists are written for a matcher, not a reader, so two things collapse:
|
|
11
|
+
a spec's `key` may list spellings of one keycap (`['[', '{']` — the shifted
|
|
12
|
+
bracket reports as `'{'`), and a modifier declared `'optional'` matches held or
|
|
13
|
+
unheld, so it isn't part of what anyone presses. Non-keyboard bindings have no
|
|
14
|
+
chip form and are skipped.
|
|
15
|
+
|
|
16
|
+
Every keyboard binding is returned, in declaration order — an action can answer
|
|
17
|
+
to several (`reorder.forward` has three) and nothing marks one canonical.
|
|
18
|
+
|
|
19
|
+
WeaselDraw's command palette shows its shortcut chip again. It had been
|
|
20
|
+
suppressed since `Action.defaultBinding: KeyBinding` was removed, pending a
|
|
21
|
+
formatter for the replacement shape.
|
|
22
|
+
|
|
23
|
+
- e25e77b: `<Canvas>` accepts `layerVisibility` and `layerOrder`.
|
|
24
|
+
|
|
25
|
+
`drawLayers` has resolved layer visibility and draw order since it was written,
|
|
26
|
+
but `<Canvas>` passed it `{}` and `undefined`, so the only way to control either
|
|
27
|
+
was a layer's own `defaultVisible`. Both are now props: `layerVisibility` maps
|
|
28
|
+
layer id to shown, falling back to `defaultVisible` for ids it omits and ignored
|
|
29
|
+
entirely by `alwaysOn` layers; `layerOrder` lists ids bottom-first, and any
|
|
30
|
+
layer it omits is not drawn.
|
|
31
|
+
|
|
32
|
+
Hiding a layer also stops it claiming pointer events through `hitTestExtras`,
|
|
33
|
+
which previously walked every registered layer regardless. Draw and hit-test
|
|
34
|
+
resolve visibility through one exported `isLayerVisible`, so a layer nobody can
|
|
35
|
+
see cannot swallow a click.
|
|
36
|
+
|
|
37
|
+
- 8e00c13: Give `<CanvasView>` a selection of its own.
|
|
38
|
+
|
|
39
|
+
`selection` and `selectionOptions` on a view mirror the props of the same name
|
|
40
|
+
on `<SceneCanvas>`: supply a `SelectionApi` to control one, or pass
|
|
41
|
+
`selectionOptions` to have the view build its own. Either goes into the view's
|
|
42
|
+
dep overlay, so an action dispatched inside that view reads and writes the
|
|
43
|
+
view's selection and leaves the surface's alone. Pass neither and the view
|
|
44
|
+
shares the surface's selection.
|
|
45
|
+
|
|
46
|
+
Nothing paints it yet — a view's chrome is still drawn from the surface's
|
|
47
|
+
selection.
|
|
48
|
+
|
|
49
|
+
- c91e186: Add `<CanvasView>`: a second camera over a rect of an existing canvas, drawn
|
|
50
|
+
through the same GL context, with input routed to it.
|
|
51
|
+
|
|
52
|
+
Declare one through `<SceneCanvas views={[...]}>`, or mount the component as a
|
|
53
|
+
child — the same declaration either way, with children landing after every prop
|
|
54
|
+
entry in paint and hit order. A view paints the surface's own layer stack
|
|
55
|
+
through its camera, or a narrowed slice of it via `layers`, and owns its camera
|
|
56
|
+
in the same hybrid controlled/uncontrolled mode `<Canvas>` uses. Wheel and drag
|
|
57
|
+
inside its rect move that camera rather than the canvas's, and a gesture that
|
|
58
|
+
wanders out of the rect stays with the camera it began under.
|
|
59
|
+
|
|
60
|
+
Two supporting changes: `createViewportLayer`'s `source` accepts a thunk, so a
|
|
61
|
+
viewport can paint a stack assembled elsewhere rather than one closed over at
|
|
62
|
+
construction; and `<SceneCanvas>` mounts a view registry, which is how a view
|
|
63
|
+
and its surface find each other.
|
|
64
|
+
|
|
65
|
+
A canvas with no views declared is unchanged — no registry entries, so every
|
|
66
|
+
point resolves to the canvas as before.
|
|
67
|
+
|
|
68
|
+
Not yet per-view: selection and chrome, affordance hit-testing, and pinch-zoom.
|
|
69
|
+
A gesture inside a view reaches the ambient viewport actions and nothing else.
|
|
70
|
+
|
|
71
|
+
- cada4da: Answer chrome bounds and layer-helper bounds with one function.
|
|
72
|
+
|
|
73
|
+
`buildChromeState`'s `effectiveBoundsOf` and `CanvasHelpers.getEffectiveBounds`
|
|
74
|
+
each spelled out the same cascade — the active tool's published preview, then
|
|
75
|
+
the dispatcher's preview extras, then committed bounds — in two places that had
|
|
76
|
+
to agree for a resize handle to sit on the shape it belongs to. They now share
|
|
77
|
+
`boundsWithPreview`.
|
|
78
|
+
|
|
79
|
+
The helpers copy carried an extra committed-pose fallback for when no bounds
|
|
80
|
+
resolver is wired. That branch was unreachable: a missing resolver means no
|
|
81
|
+
`boundsOf` prop and no adapter, and without an adapter the pose lookup returns
|
|
82
|
+
`null` too. Behavior is unchanged.
|
|
83
|
+
|
|
84
|
+
- 889b1d0: Give each of `useGestureDispatcher`'s view records its own camera.
|
|
85
|
+
|
|
86
|
+
A record may now carry a `ViewApi`. An event routed to it dispatches against
|
|
87
|
+
the canvas dep registry with the `view` dep — and only that dep — replaced by
|
|
88
|
+
the record's, so `viewport.dragPan` and the rest of the viewport actions move
|
|
89
|
+
the view the gesture began in rather than the whole canvas. No second `setView`
|
|
90
|
+
channel was needed: every viewport action already reads its camera from that
|
|
91
|
+
dep.
|
|
92
|
+
|
|
93
|
+
This is what makes routing correct rather than merely wired. Records without a
|
|
94
|
+
`ViewApi`, which is every record today, resolve `view` exactly as before.
|
|
95
|
+
|
|
96
|
+
- 9e6927a: Route each input event to the view it landed in, inside `useGestureDispatcher`.
|
|
97
|
+
|
|
98
|
+
The hook takes an optional `views` — a thunk returning the non-root dispatch
|
|
99
|
+
records and a resolver that names one for a client point. `createViewResolver`
|
|
100
|
+
satisfies the resolver shape, so a canvas holding several viewports can hand
|
|
101
|
+
the two straight over. Pointerdown pins its pointer to the view it began in and
|
|
102
|
+
pointerup releases it, so a drag that leaves a panel keeps reporting
|
|
103
|
+
coordinates in the camera it started under. Keyboard and paste carry no
|
|
104
|
+
coordinates and run on the view the last coordinate-bearing event resolved to.
|
|
105
|
+
A resolved id with no live record falls back to the root, so a view that
|
|
106
|
+
unmounts mid-gesture degrades rather than dropping the event.
|
|
107
|
+
|
|
108
|
+
No public change, and no change at all with `views` omitted: every event then
|
|
109
|
+
runs on the record the flat options describe. Cancelling in-flight gestures —
|
|
110
|
+
on unmount and on tool change — now reaches every view's dispatcher rather than
|
|
111
|
+
only the root's.
|
|
112
|
+
|
|
113
|
+
- eafe4be: Resolve the dispatcher and its coordinate lookups per event inside
|
|
114
|
+
`useGestureDispatcher`.
|
|
115
|
+
|
|
116
|
+
The hook took `dispatcher`, `affordanceAt`, `classifyTarget` and `clientToWorld`
|
|
117
|
+
as four sibling options and read each through its own ref, and it bound the
|
|
118
|
+
dispatcher once when the listener effect ran. Those four are one thing —
|
|
119
|
+
everything about handling an event that depends on which view it landed in — so
|
|
120
|
+
they are now one internal record, read fresh on each event.
|
|
121
|
+
|
|
122
|
+
No public change: the four options stay exactly as they are and become that
|
|
123
|
+
record. They are the single-view façade, the same way `SceneCanvasProps` is.
|
|
124
|
+
|
|
125
|
+
This is groundwork for routing input to one of several views. Doing it this way
|
|
126
|
+
means the hook keeps mounting once: a canvas with N views gets N dispatchers
|
|
127
|
+
behind one listener set, rather than N copies of the hook all firing on every
|
|
128
|
+
event.
|
|
129
|
+
|
|
130
|
+
- ae84ca1: Move one view's overlay-aware state into a `useViewHelpers` hook.
|
|
131
|
+
|
|
132
|
+
`<Canvas>` built its chrome state and its layer helpers inline: the bounds
|
|
133
|
+
fallbacks, the committed-pose lookup, the tool preview cascade, `buildChromeState`
|
|
134
|
+
and the `CanvasViewHelpers` object were about 120 lines of the component body
|
|
135
|
+
closing over its props. They are now one hook taking explicit dependencies —
|
|
136
|
+
adapter, geometry, bounds resolver, selection, tools, gesture source and the
|
|
137
|
+
dispatcher's preview extras — and `<Canvas>` calls it for its own view.
|
|
138
|
+
|
|
139
|
+
Being a hook is the point. A canvas hosting several viewports cannot loop this
|
|
140
|
+
work inside one component, but N components can each call it once, which is what
|
|
141
|
+
per-view selection and chrome will be built from.
|
|
142
|
+
|
|
143
|
+
`CanvasHelpers`, `CanvasViewHelpers` and `CanvasSurfaceHelpers` moved to the new
|
|
144
|
+
module and are still re-exported from `./canvas/Canvas` and the package root, so
|
|
145
|
+
imports are unchanged. The hook takes only the `getPose` slice of the adapter
|
|
146
|
+
rather than the full contract.
|
|
147
|
+
|
|
148
|
+
- 0514a37: `hitTestExtras` takes an optional `frame` naming the camera to test under.
|
|
149
|
+
|
|
150
|
+
The method read the canvas's own `view` and `dims` off refs, which is right for
|
|
151
|
+
every existing caller and wrong for a point routed to a viewport node: there the
|
|
152
|
+
world point is in the node's inner view and a layer resolving a screen-pixel
|
|
153
|
+
tolerance needs that view and the node's rect size. `hitTestExtras(x, y, { view,
|
|
154
|
+
dims })` supplies both; omitting `frame` keeps the previous behavior, so no
|
|
155
|
+
existing call site changes.
|
|
156
|
+
|
|
157
|
+
- daa5ce6: Add a source rect and flip to `ImageDrawCommand`, so one bitmap can be drawn as
|
|
158
|
+
many frames.
|
|
159
|
+
|
|
160
|
+
`source` is a sub-rectangle in bitmap pixels; `flipX` / `flipY` mirror the
|
|
161
|
+
sampled region within the destination rect without moving the quad. Both are
|
|
162
|
+
additive and optional — a command that sets neither draws exactly as before.
|
|
163
|
+
Until now a sprite sheet needed a custom `ShaderDrawCommand` to do what is
|
|
164
|
+
arithmetic on the quad's four UV pairs.
|
|
165
|
+
|
|
166
|
+
`source` is not range-checked: a rect past the bitmap edge samples outside
|
|
167
|
+
`[0..1]`, which `CLAMP_TO_EDGE` smears. With `sampling: 'linear'` the filter
|
|
168
|
+
also reaches half a texel beyond `source`, so an atlas whose frames touch will
|
|
169
|
+
bleed at the seams — pad frames with a gutter or use `'nearest'`. The renderer
|
|
170
|
+
deliberately does not inset for this, which would make an exact 1:1 blit soft.
|
|
171
|
+
|
|
172
|
+
New `frameRect(sheet, index)` and the `SpriteSheet` type turn a uniform grid
|
|
173
|
+
(`frameWidth`, `frameHeight`, `columns`, optional `margin` and `spacing`,
|
|
174
|
+
following the Tiled / Aseprite convention) into that source rect. It is
|
|
175
|
+
row-major from 0 and does not wrap past the last cell — wrapping belongs to the
|
|
176
|
+
animation, since a sheet does not know how many of its cells are filled.
|
|
177
|
+
|
|
178
|
+
`@weasel-js/hud`'s image widget takes `source`, `flipX` and `flipY` as options
|
|
179
|
+
and gains `setSource` and `setFlip` to change them in place. `setFlip` merges,
|
|
180
|
+
leaving an omitted axis alone. Without the setters a sprite animation would
|
|
181
|
+
have to dispose and rebuild the widget every frame.
|
|
182
|
+
|
|
183
|
+
- 144e70a: Export `keySpecShortcut(spec)` — the chip form of one gesture spec, or
|
|
184
|
+
`undefined` where a spec has none — and `actionBindings(action)`, the flat
|
|
185
|
+
binding list `actionShortcuts` already reads.
|
|
186
|
+
|
|
187
|
+
`actionShortcuts` is now a dedupe over `keySpecShortcut`, so the spec-to-chip
|
|
188
|
+
mapping has one implementation. It had two: ToolkitBuilder's binding table
|
|
189
|
+
projected key specs inline, and rendered a `'optional'` modifier as a keycap
|
|
190
|
+
the reader has to press. Surfaces that render drag, click and wheel specs
|
|
191
|
+
alongside keyboard ones can now share the keyboard half without taking
|
|
192
|
+
`actionShortcuts`' action-at-a-time shape.
|
|
193
|
+
|
|
194
|
+
- 2627cde: Fix a hook-order defect in the Badge effects and several stale-closure bugs,
|
|
195
|
+
found by turning on a correctness lint baseline.
|
|
196
|
+
|
|
197
|
+
Six Badge effects (`Aqua`, `Bevel`, `Bevel2`, `Metal`, `Sheen`, `Woodgrain`)
|
|
198
|
+
called `useId` after an early return keyed on `variant`. Changing a `<Badge>`'s
|
|
199
|
+
variant to one those effects don't render, and back, remounted the component
|
|
200
|
+
and issued fresh ids — so the `<clipPath>` and gradient ids their `url(#…)`
|
|
201
|
+
references point at changed identity mid-life.
|
|
202
|
+
|
|
203
|
+
Also fixed: `Canvas.tsx`'s paint effect read a stale `helpersForLayers` through
|
|
204
|
+
its closure rather than the ref the file maintains, and `useDeviceProfile`
|
|
205
|
+
ignored a `targetScale` supplied by a provider.
|
|
206
|
+
|
|
207
|
+
`composeOrderedLayers` is now generic over the `LayersMap` it receives instead
|
|
208
|
+
of taking `any`; inference at existing call sites is unchanged.
|
|
209
|
+
|
|
210
|
+
- 8b583b4: Default an unset miter limit to 4, not 10.
|
|
211
|
+
|
|
212
|
+
A stroke that sets no `miterLimit` used Canvas2D's 10. The SVG serializer omits
|
|
213
|
+
the attribute for an unset field, so the same stroke exported and opened
|
|
214
|
+
anywhere else renders at SVG's default of 4 — the kit disagreed with its own
|
|
215
|
+
export format, and re-importing the file did not reconcile them.
|
|
216
|
+
|
|
217
|
+
10 also lets an acute corner throw a miter spike four times the half-width. A
|
|
218
|
+
stroked capital W put one in the middle of the letter, from the apex of a V
|
|
219
|
+
most of the way down: measured at 7.99 units out on a half-width of 2, and
|
|
220
|
+
inside the letterform where a bounding box never sees it. Glyph outlines are
|
|
221
|
+
where this shows first because a type designer's sharpest vertices were never
|
|
222
|
+
drawn to be stroked.
|
|
223
|
+
|
|
224
|
+
Strokes that set `miterLimit` explicitly are unaffected. Anything relying on
|
|
225
|
+
the old default can set `miterLimit: 10`. No visual baseline moved: nothing in
|
|
226
|
+
the demo set strokes a corner sharp enough to have been spiking.
|
|
227
|
+
|
|
228
|
+
- e61d3e3: Resolve selection chrome's bounds through one cascade, not two.
|
|
229
|
+
|
|
230
|
+
`createSelectionOverlayLayer`'s `getPose` is now optional alongside
|
|
231
|
+
`getSelection`. Omitted, the layer takes bounds from the `ChromeState` on the
|
|
232
|
+
draw envelope — the cascade its selection was already built with. `<Canvas>`
|
|
233
|
+
and `<SceneCanvas>` each carried a `poseById` chain of their own for this;
|
|
234
|
+
both are gone, and a consumer's `poseById` override still wins where it is set.
|
|
235
|
+
|
|
236
|
+
The two chains were supposed to agree and did not: they consulted the same
|
|
237
|
+
preview sources in opposite priority, and only one of them carried rotation
|
|
238
|
+
through. With one camera the disagreement was hard to see; per-view chrome
|
|
239
|
+
would have made it visible.
|
|
240
|
+
|
|
241
|
+
- f0cc29c: Hit-test affordances against the chrome state that was painted.
|
|
242
|
+
|
|
243
|
+
The gesture dispatcher's `affordanceAt` built a `ChromeState` of its own —
|
|
244
|
+
selection off a ref, bounds straight from the resolver, its own union AABB —
|
|
245
|
+
next to the one the canvas helpers had already built. The two differed by the
|
|
246
|
+
in-flight overlay: mid-drag, resize handles painted at the ghost while their
|
|
247
|
+
hit regions stayed at the committed pose.
|
|
248
|
+
|
|
249
|
+
A surface now publishes its view chrome on the handle it attaches to the view
|
|
250
|
+
registry, and the dispatcher reads it from there. `ChromeState` gains an
|
|
251
|
+
`EMPTY_CHROME_STATE` for the before-attach case, and `anchorStateFrom` is the
|
|
252
|
+
dep-registry read the mounter used to inline.
|
|
253
|
+
|
|
254
|
+
- 438970b: Draw selection chrome for the view that asked, not for the canvas.
|
|
255
|
+
|
|
256
|
+
`createSelectionOverlayLayer`'s `getSelection` is now optional. Omitted, the
|
|
257
|
+
layer takes its ids from the `ChromeState` on the draw envelope — the same
|
|
258
|
+
channel it already read the multi-selection union AABB from. `<Canvas>` and
|
|
259
|
+
`<SceneCanvas>` stop passing one, so the single overlay layer a surface builds
|
|
260
|
+
outlines whichever view is drawing it.
|
|
261
|
+
|
|
262
|
+
The multi-selection split is unchanged: the handle pass works against the
|
|
263
|
+
synthetic union id, the outline pass against the real members.
|
|
264
|
+
|
|
265
|
+
- f2ba2ab: Let a view answer any dep for itself, not just `view`.
|
|
266
|
+
|
|
267
|
+
A dispatcher view record carried a `ViewApi`; it now carries a thunked
|
|
268
|
+
`Partial<DepSchema>`, and an event routed to that view resolves every name in
|
|
269
|
+
it from the view, everything else from the canvas registry. `view` becomes one
|
|
270
|
+
entry rather than a special case, which is what per-view selection needs next.
|
|
271
|
+
|
|
272
|
+
This is also the answer to whether a view should get a `DepRegistryProvider` of
|
|
273
|
+
its own: it should not. The registry is where a consumer registers _sources_,
|
|
274
|
+
and one per view would fragment that — overriding `insert` would mean knowing
|
|
275
|
+
how many views exist and overriding each. An overlay keeps one place to
|
|
276
|
+
register and one authority per dep, with a view claiming only what is genuinely
|
|
277
|
+
its own.
|
|
278
|
+
|
|
279
|
+
- 4ac9273: Route pinch and hover to the view under the pointer.
|
|
280
|
+
|
|
281
|
+
Both attached to the canvas and targeted the outer camera, so a pinch inside a
|
|
282
|
+
panel zoomed the canvas beneath it and hover resolved the wrong node. The view
|
|
283
|
+
registry now owns one `ViewResolver` for the surface, and the dispatcher, pinch
|
|
284
|
+
and hover all ask it — one authority, so they cannot disagree about where a
|
|
285
|
+
point landed.
|
|
286
|
+
|
|
287
|
+
`usePinchZoomTool` takes a `resolveTarget` option naming the camera an anchor
|
|
288
|
+
belongs to, and measures the anchor from that camera's origin. Omitted, it is
|
|
289
|
+
the canvas's own, as before.
|
|
290
|
+
|
|
291
|
+
- 8570a23: Export `resolveParams` from the package entry
|
|
292
|
+
|
|
293
|
+
`BindingOpts.params` may be a thunk, and its own doc comment tells callers to
|
|
294
|
+
read it "via `resolveParams(opts?.params)`" — but that helper was defined and
|
|
295
|
+
used internally, never re-exported. Every consumer writing a parametric
|
|
296
|
+
`key-held` (or other) binding was stuck reimplementing the thunk check by
|
|
297
|
+
hand. `resolveParams` is now importable from `@weasel-js/core`.
|
|
298
|
+
|
|
299
|
+
- 7c202d2: Put the selection on the scene, and restore it on undo.
|
|
300
|
+
|
|
301
|
+
`scene.getSelection()` / `scene.setSelection()` own the transient set of active
|
|
302
|
+
ids. It is not document content — `toJSON` never carries it — but every history
|
|
303
|
+
entry now records the selection its edit was made under, so undo and redo put
|
|
304
|
+
back what was selected. Changing the selection is still never an undo step of
|
|
305
|
+
its own.
|
|
306
|
+
|
|
307
|
+
Undoing a boolean op used to leave the selection pointing at the result node
|
|
308
|
+
undo had just deleted; deleting a multi-selection and undoing left it empty.
|
|
309
|
+
|
|
310
|
+
`useSelection({ scene })` keeps the selection on the scene rather than in the
|
|
311
|
+
hook. `<SceneCanvas>` does that by default, so every view over one scene shares
|
|
312
|
+
a selection; a `<CanvasView>` opts out with `selection` / `selectionOptions`.
|
|
313
|
+
|
|
314
|
+
`@weasel-js/history` gains `CreateHistoryOptions.selection`, a get/set pair the
|
|
315
|
+
engine reads and writes on the way past — supply it and entries carry
|
|
316
|
+
`selectionBefore` / `selectionAfter`, omit it and the engine touches selection
|
|
317
|
+
never. `recordEntry` takes the pre-batch selection as an option, because by the
|
|
318
|
+
time it runs the live selection has already moved on.
|
|
319
|
+
|
|
320
|
+
`defaultCommitAdapter` carries `getSelection` / `setSelection` now, so
|
|
321
|
+
selection-carrying ops replay without splicing `SelectionApi.adapterMethods`
|
|
322
|
+
over it.
|
|
323
|
+
|
|
324
|
+
- c7b4705: Add a side-scroller demo that load-tests the animation timeline and the audio
|
|
325
|
+
engine. The player is an eleven-joint rig posed by cross-faded
|
|
326
|
+
`SampledTrack<Pose>` clips — the run cycle plays on a real `animator.timeline`
|
|
327
|
+
whose time scale tracks ground speed, while jump and fall are seeked by vertical
|
|
328
|
+
velocity rather than played. Footsteps fire from an `EventTrack` on that looping
|
|
329
|
+
timeline, which is the timeline-to-audio bridge under the heaviest load it will
|
|
330
|
+
see. Every sound is synthesized into an `AudioBuffer` at load, so the demo ships
|
|
331
|
+
no assets.
|
|
332
|
+
|
|
333
|
+
Its HUD is the point: frame time, active voice count, footstep timing spread and
|
|
334
|
+
a swarm control that pushes the voice pool past its limit, so the demo measures
|
|
335
|
+
the two arcs rather than merely exercising them.
|
|
336
|
+
|
|
337
|
+
Findings are recorded in `docs/TODO.md` under Animation. The load-bearing one:
|
|
338
|
+
`EventTrack` events are `{ t, fire: () => void }`, and `fire` receives no
|
|
339
|
+
arguments, so an audio handler cannot learn the playhead's crossing time and is
|
|
340
|
+
quantized to the animation frame instead of the audio clock.
|
|
341
|
+
|
|
342
|
+
- 6a5c047: Split `CanvasHelpers` into its per-view and per-surface halves.
|
|
343
|
+
|
|
344
|
+
`CanvasViewHelpers` is what one camera's own tools, gestures and selection
|
|
345
|
+
answer — `getEffectivePose`, `getEffectiveBounds`, `getGestureBounds`,
|
|
346
|
+
`subscribeGestures`, `getGestureVersion`, `getChromeState`.
|
|
347
|
+
`CanvasSurfaceHelpers` is what a GL context has one of — `getDebug`,
|
|
348
|
+
`getIsVisible`. `CanvasHelpers` extends both and is unchanged for layers, which
|
|
349
|
+
still receive the whole object as their `data`.
|
|
350
|
+
|
|
351
|
+
The two are now built separately inside `<Canvas>`, so which side a lookup
|
|
352
|
+
belongs on is a compile-time fact instead of a claim in a design doc. That is
|
|
353
|
+
the boundary a canvas hosting several viewports has to build N of one side and
|
|
354
|
+
one of the other across.
|
|
355
|
+
|
|
356
|
+
- 49e450c: Add `createViewResolver` — which view owns a client point, held steady for a
|
|
357
|
+
gesture.
|
|
358
|
+
|
|
359
|
+
A canvas with viewport nodes on it has more than one camera, and a pointer event
|
|
360
|
+
belongs to exactly one of them. The resolver hit-tests a list of
|
|
361
|
+
`ResolvableView`s (a camera plus the rect it paints into) in reverse paint
|
|
362
|
+
order, right and bottom edges exclusive, and falls back to the root view.
|
|
363
|
+
|
|
364
|
+
It pins a pointer on `begin` and releases it on `end`, so a drag that leaves its
|
|
365
|
+
view's rect — over a neighbour, or off the canvas — keeps reporting coordinates
|
|
366
|
+
in the space it started in. Without that, a marquee crossing a panel edge
|
|
367
|
+
silently starts measuring against the wrong camera. A pointer that began on the
|
|
368
|
+
root canvas is pinned to the root for the same reason. The pinned view is looked
|
|
369
|
+
up fresh each call, so a rect that moves mid-gesture is honored.
|
|
370
|
+
|
|
371
|
+
`ViewTarget.origin` is the resolved view's client-space origin, ready to pass
|
|
372
|
+
straight to `clientToWorld`. `ViewportLayer.resolvable(outer, dims)` supplies a
|
|
373
|
+
viewport node as a candidate.
|
|
374
|
+
|
|
375
|
+
Nothing is wired into the dispatcher yet: tools still target the outer view.
|
|
376
|
+
|
|
377
|
+
- 6031085: Stroke text at any size, not only above the outline threshold.
|
|
378
|
+
|
|
379
|
+
A glyph escalates from its SDF tier to tessellated outlines once it covers
|
|
380
|
+
`OUTLINE_MIN_SCREEN_PX` (48) on screen, and only the outline tier has geometry
|
|
381
|
+
to stroke. Text below that silently dropped its stroke: the control was live,
|
|
382
|
+
the paint never arrived, and the same text stroked correctly inside a magnifier
|
|
383
|
+
that happened to lift it over the threshold.
|
|
384
|
+
|
|
385
|
+
A run carrying a stroke now escalates at any size. The threshold still governs
|
|
386
|
+
unstroked text, where it is a choice between two correct renderings rather than
|
|
387
|
+
between a stroke and nothing. A zero-width stroke does not escalate, and an
|
|
388
|
+
explicit opt-out of the tier still wins — as does a run the tier cannot serve
|
|
389
|
+
(no registered outlines, or synthetic bold, whose emboldening is an SDF
|
|
390
|
+
threshold shift with no geometric equivalent).
|
|
391
|
+
|
|
392
|
+
- ccaaecd: `Stroke.width` accepts `{ px }` for a width in screen pixels, resolved against the accumulated transform scale at draw time. Callers previously divided by `meanScale(view.scale)` at each site; this moves that into the renderer and lets the stroke mesh cache key see the resolved width.
|
|
393
|
+
- ec0eb08: Rename the viewport `computeFitView` to `computeFitViewport`. It was unreachable from the package entry: an identically-named export from the minimap module shadowed it. This is a breaking rename of a symbol nobody could import.
|
|
394
|
+
- 726f85e: Make what a view paints hittable inside that view.
|
|
395
|
+
|
|
396
|
+
`<CanvasView>` registers an `affordanceAt` and a `classifyTarget` of its own,
|
|
397
|
+
built the same way the surface builds its pair but against this view's chrome
|
|
398
|
+
state and camera. A press inside a panel now lands on that panel's resize
|
|
399
|
+
handles, rotation band and path anchors, and a body under the point classifies
|
|
400
|
+
against the panel's selection — until now a gesture inside a panel reached only
|
|
401
|
+
the ambient viewport actions.
|
|
402
|
+
|
|
403
|
+
Externally registered layers keep first refusal on the point, hit-tested
|
|
404
|
+
against the view's frame and draw envelope rather than the canvas's.
|
|
405
|
+
|
|
406
|
+
The surface's context to its views widens to carry the hit-test half it is the
|
|
407
|
+
authority on: the pickers, the node-kind resolver and the chrome-caps
|
|
408
|
+
predicate.
|
|
409
|
+
|
|
410
|
+
- 601aa6b: Let a view build its own helpers and hand them to its layers.
|
|
411
|
+
|
|
412
|
+
`<CanvasView>` now calls `useViewHelpers` and passes the result through the
|
|
413
|
+
viewport node's `data` thunk: its source layers draw against this view's chrome
|
|
414
|
+
state, effective poses and gesture bounds, with the surface half of the
|
|
415
|
+
envelope — debug sink, chrome-caps predicate — passing through untouched.
|
|
416
|
+
|
|
417
|
+
The inputs that hook needs are surface-wide (adapter, geometry, bounds
|
|
418
|
+
resolver, tools, gesture source) and are read during a view's render, so
|
|
419
|
+
`<SceneCanvas>` publishes them as context rather than on the `SurfaceHandle`,
|
|
420
|
+
which is not attached until an effect runs.
|
|
421
|
+
|
|
422
|
+
Layers that read chrome off the draw envelope — affordance layers, the
|
|
423
|
+
selection overlay's multi-union — follow the view. The selection overlay's
|
|
424
|
+
per-id outline and handles still come from closures over the surface's
|
|
425
|
+
selection.
|
|
426
|
+
|
|
427
|
+
- 9607185: Read a view's gesture previews from the view's own dispatcher.
|
|
428
|
+
|
|
429
|
+
`<CanvasView>` builds its helpers with a `GestureSource` and preview extras
|
|
430
|
+
over the dispatcher it owns, rather than inheriting the surface's. A view has
|
|
431
|
+
had its own dispatcher since routing landed, so a gesture inside a panel put
|
|
432
|
+
its in-flight handles somewhere the panel's own chrome was not looking: no
|
|
433
|
+
ghost, no tracking resize handles, no gesture bounds.
|
|
434
|
+
|
|
435
|
+
The dispatcher's contribution to those lookups is now one factory
|
|
436
|
+
(`createDispatcherPreviewSources`) next to `createGestureSource`, instead of
|
|
437
|
+
two closures inlined in `<SceneCanvas>`. The context a surface publishes to its
|
|
438
|
+
views narrows to the scene-shaped half — adapter, geometry, bounds resolver,
|
|
439
|
+
tools.
|
|
440
|
+
|
|
441
|
+
- 58f43e7: Apply the inner view transform to a viewport node's source layers.
|
|
442
|
+
|
|
443
|
+
A world-space `RenderLayer` emits world coords and relies on its caller to wrap
|
|
444
|
+
them in `viewToMat3(view)`. `drawLayers` did that; `createViewportLayer` did
|
|
445
|
+
not — it concatenated `layer.draw(...)` output under a bare translate to the
|
|
446
|
+
rect origin. So a viewport's inner `view.x/y/scale` never reached the pixels,
|
|
447
|
+
while its `reproject` inverse assumed they had. Content drew at raw world
|
|
448
|
+
coords and hit-testing disagreed with what was on screen; at the identity inner
|
|
449
|
+
view the two happened to coincide, which is why it looked right in the demo.
|
|
450
|
+
|
|
451
|
+
Both paths now go through one exported helper, `drawOneLayer`, which puts a
|
|
452
|
+
layer's commands in the space its `space` field declares.
|
|
453
|
+
|
|
454
|
+
A screen-space source layer keeps drawing untransformed, but that means the
|
|
455
|
+
viewport's own CSS-pixel space — coords relative to the rect's top-left,
|
|
456
|
+
clipped to the rect. The previous doc comment claimed such layers rendered to
|
|
457
|
+
the outer canvas instead; they never did.
|
|
458
|
+
|
|
459
|
+
- 2e22d99: A viewport node can host a live camera and its own per-view data.
|
|
460
|
+
|
|
461
|
+
`view` now accepts a thunk as well as a `View`. It is read fresh on every
|
|
462
|
+
`draw`, `reproject` and `resolvable`, so those three cannot disagree about where
|
|
463
|
+
the viewport is looking part-way through a gesture. The thunk receives the outer
|
|
464
|
+
view and dims, so a derived camera — parallax, node-anchored scroll — is a
|
|
465
|
+
function of the one hosting it.
|
|
466
|
+
|
|
467
|
+
A `data` thunk derives what the source layers receive from what the outer canvas
|
|
468
|
+
passed down. Without it they get the outer canvas's `data`, as before. This is
|
|
469
|
+
what lets a viewport showing the same scene through a second camera give its
|
|
470
|
+
layers their own selection, chrome state and gesture previews instead of the
|
|
471
|
+
hosting view's.
|
|
472
|
+
|
|
473
|
+
Both are additive: `CreateViewportLayerOpts` gained a second type parameter that
|
|
474
|
+
defaults to the first, so existing call sites infer exactly as they did.
|
|
475
|
+
|
|
476
|
+
- Updated dependencies [7c202d2]
|
|
477
|
+
- @weasel-js/history@1.2.0
|
|
478
|
+
- @weasel-js/font@1.2.0
|
|
479
|
+
- @weasel-js/geom@1.2.0
|
|
480
|
+
- @weasel-js/gestures@1.2.0
|
|
481
|
+
- @weasel-js/modes@1.2.0
|
|
482
|
+
|
|
3
483
|
## 1.1.0
|
|
4
484
|
|
|
5
485
|
### Minor Changes
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { P as Path } from './path-B6MMiodD.js';
|
|
2
|
-
import { F as FillStyle, S as Stroke, a as TextureHandle } from './paint-types-
|
|
2
|
+
import { F as FillStyle, S as Stroke, a as TextureHandle } from './paint-types-1fUdZUaH.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* 2D affine matrix utilities. Column-major 9-element Float32Array, matching
|
|
@@ -33,6 +33,13 @@ declare function apply(m: Mat3, x: number, y: number): [number, number];
|
|
|
33
33
|
* matches clip-down).
|
|
34
34
|
*/
|
|
35
35
|
declare function screenToClip(width: number, height: number): Mat3;
|
|
36
|
+
/**
|
|
37
|
+
* Uniform-equivalent scale factor: the square root of the absolute
|
|
38
|
+
* determinant of the linear part, i.e. the geometric mean of the two axis
|
|
39
|
+
* scales. Rotation-invariant. Under non-uniform scale it is between the two
|
|
40
|
+
* axes and exact on neither — the same compromise `meanScale` documents.
|
|
41
|
+
*/
|
|
42
|
+
declare function meanScaleOf(m: Mat3): number;
|
|
36
43
|
/** The renderer's 3x3 matrix operations, as one namespace. These work on the
|
|
37
44
|
* 9-element `Float32Array` form the GL uniform upload wants — distinct from
|
|
38
45
|
* `@weasel-js/geom`'s 6-element affine `Mat3`, though the logical element
|
|
@@ -45,6 +52,7 @@ declare const mat3: {
|
|
|
45
52
|
invert: typeof invert;
|
|
46
53
|
apply: typeof apply;
|
|
47
54
|
screenToClip: typeof screenToClip;
|
|
55
|
+
meanScaleOf: typeof meanScaleOf;
|
|
48
56
|
};
|
|
49
57
|
|
|
50
58
|
/**
|
|
@@ -389,6 +397,21 @@ interface ImageDrawCommand {
|
|
|
389
397
|
* device pixels as hard squares — required by anything magnifying a
|
|
390
398
|
* framebuffer readback, where blur destroys the point of the readback. */
|
|
391
399
|
sampling?: 'linear' | 'nearest';
|
|
400
|
+
/** Sub-rectangle of `image` to draw, in bitmap pixels from the top-left.
|
|
401
|
+
* Omitted draws the whole bitmap. Not range-checked: a rect past the edge
|
|
402
|
+
* samples outside [0..1], which CLAMP_TO_EDGE smears. With
|
|
403
|
+
* `sampling: 'linear'` the filter reaches half a texel beyond `source`, so
|
|
404
|
+
* atlas frames need a gutter (see `SpriteSheet.spacing`) or `'nearest'`. */
|
|
405
|
+
source?: {
|
|
406
|
+
x: number;
|
|
407
|
+
y: number;
|
|
408
|
+
w: number;
|
|
409
|
+
h: number;
|
|
410
|
+
};
|
|
411
|
+
/** Mirror the sampled region within the destination rect. The quad does not
|
|
412
|
+
* move — a flipped draw covers exactly the pixels an unflipped one does. */
|
|
413
|
+
flipX?: boolean;
|
|
414
|
+
flipY?: boolean;
|
|
392
415
|
}
|
|
393
416
|
/**
|
|
394
417
|
* Custom shader draw command. The renderer generates a quad over `bounds`
|