@weasel-js/core 1.0.3 → 1.1.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.
Files changed (37) hide show
  1. package/CHANGELOG.md +251 -0
  2. package/dist/{chunk-SMCZRSYB.js → chunk-3B4QEB2G.js} +810 -228
  3. package/dist/chunk-3B4QEB2G.js.map +1 -0
  4. package/dist/{chunk-U45SODC2.js → chunk-4Z6J4IVH.js} +43 -27
  5. package/dist/chunk-4Z6J4IVH.js.map +1 -0
  6. package/dist/{chunk-3KGMJMOS.js → chunk-CSMF654J.js} +3 -3
  7. package/dist/{chunk-3KGMJMOS.js.map → chunk-CSMF654J.js.map} +1 -1
  8. package/dist/{chunk-TR3HZN34.js → chunk-J3YC3GVF.js} +5 -4
  9. package/dist/chunk-J3YC3GVF.js.map +1 -0
  10. package/dist/{chunk-3JYFAQIB.js → chunk-J7LUY47N.js} +3 -3
  11. package/dist/{chunk-3JYFAQIB.js.map → chunk-J7LUY47N.js.map} +1 -1
  12. package/dist/clipboard.d.ts +1 -1
  13. package/dist/clone.d.ts +2 -2
  14. package/dist/{geometry-5X6TrAkX.d.ts → geometry-C56YbnfD.d.ts} +1 -1
  15. package/dist/{grid-sfilv5HP.d.ts → grid-DqOi0Vgi.d.ts} +1 -1
  16. package/dist/index.d.ts +179 -59
  17. package/dist/index.js +5 -5
  18. package/dist/insert.d.ts +3 -3
  19. package/dist/insert.js +1 -1
  20. package/dist/move.d.ts +4 -4
  21. package/dist/move.js +3 -3
  22. package/dist/move.js.map +1 -1
  23. package/dist/{options-CbsIgQci.d.ts → options-C6HYCKP7.d.ts} +1 -1
  24. package/dist/{pointSnapToGrid-C3wowsQy.d.ts → pointSnapToGrid-BBtn5bus.d.ts} +2 -2
  25. package/dist/{registry-CxnkXbG8.d.ts → registry-DGBrjGbB.d.ts} +4 -4
  26. package/dist/renderer.d.ts +29 -13
  27. package/dist/renderer.js +5 -5
  28. package/dist/resize.d.ts +4 -4
  29. package/dist/resize.js +2 -2
  30. package/dist/routing.d.ts +4 -4
  31. package/dist/routing.js +1 -1
  32. package/dist/{types-ByZxoD6P.d.ts → types-BhrifbZ1.d.ts} +1 -1
  33. package/dist/{types-DpQ0cWYC.d.ts → types-DbysOO-2.d.ts} +10 -3
  34. package/package.json +6 -6
  35. package/dist/chunk-SMCZRSYB.js.map +0 -1
  36. package/dist/chunk-TR3HZN34.js.map +0 -1
  37. package/dist/chunk-U45SODC2.js.map +0 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,256 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.1.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 27dd91b: <!-- bump-approved: minor: Mike — new public API across two arcs (timeline + rig, and the @weasel-js/audio package); called explicitly in conversation on 2026-08-22: "the next version we push will be 1.1.0" -->
8
+
9
+ Add a keyframe timeline primitive and a hierarchical rig.
10
+
11
+ This adds public API surface.
12
+
13
+ `animator.timeline(opts)` registers like any other animation, so its playhead
14
+ responds to `pause`, `setTimeScale` and `cancelKey`. Sampled tracks are a pure
15
+ function of the playhead and reuse the tween interpolation contract; event
16
+ tracks fire only on forward playback and stay silent under `seek`; timeline
17
+ tracks nest, evaluated at the parent's playhead minus their offset.
18
+
19
+ The rig ships as `blendPoses` and `resolveSkeleton` over a `Skeleton` of joints
20
+ carrying their own TRS — not the scene's consumer-defined `TPose`, which may be
21
+ a bare AABB with no rotation term a joint chain can compose through. A pose is
22
+ local deltas from bind, so an absent joint or field means "no change".
23
+ Animating a rig is a `SampledTrack<Pose>` whose `interpolate` is `blendPoses` —
24
+ no rig-specific timeline machinery.
25
+
26
+ ### Patch Changes
27
+
28
+ - 0763205: Export the `mat3` namespace from the package entry
29
+
30
+ `resolveSkeleton` returns `Map<string, Mat3>` and `Mat3` was exported as a
31
+ type, but the operations that read one were not. Placing a bone tip meant
32
+ indexing the `Float32Array` by hand — `[m[0] * length + m[6], m[1] * length +
33
+ m[7]]` — which is the matrix layout leaking into consumer code.
34
+
35
+ `mat3` is now importable from `@weasel-js/core`, so that line is
36
+ `mat3.apply(m, length, 0)`. Alongside `apply` the namespace carries
37
+ `identity`, `multiply`, `translate`, `scale`, `invert` and `screenToClip`.
38
+
39
+ This is the renderer's 9-element column-major form, matching what
40
+ `uniformMatrix3fv` uploads. `@weasel-js/geom` exports its own `Mat3` — a
41
+ 6-element affine — with the same logical element order but a different array
42
+ shape; the two are not interchangeable.
43
+
44
+ - b65aadd: Undo restores z-order, and reorder ops survive a reload
45
+
46
+ Two defects in the op layer, found reviewing `core/ops` and `core/adapters`.
47
+
48
+ `createDeleteOp` captures the node's z-index and forwards it through
49
+ `invert()` so undo puts the node back where it was — but `SceneAdapter`
50
+ declared `insertNode(node)` with no index parameter, so the only
51
+ implementation that honored it was the one that had gone off-contract to
52
+ accept it. `arrayAdapter` appended unconditionally and `animateLifecycle`
53
+ dropped the argument while wrapping. Deleting a node and undoing therefore
54
+ moved it to the top of the paint order, and undoing a multi-delete reversed
55
+ the stack. The parameter is now part of the interface and both
56
+ implementations honor it.
57
+
58
+ `createReorderOp` — bring forward, send backward, bring to front, send to
59
+ back — built an op with no `name` and registered no factory, though `Op`'s
60
+ contract says kit-emitted ops always carry one. `History.serialize()` drops
61
+ any entry holding a nameless op, so all four silently vanished from the
62
+ persisted undo stack on reload while `moveToIndex`, which does register,
63
+ survived. They now serialize, with the per-parent before-order carried in
64
+ the op's args so a rebuilt op can still invert.
65
+
66
+ - 0c13967: An empty container no longer draws selection chrome at its own stored pose
67
+
68
+ `composeSelectionPose` and the overlay's container-aware bounds resolver both
69
+ document that a container with no leaves resolves to `null`. Neither could
70
+ return it: the leaf walk pushed any childless node, so an empty container
71
+ pushed _itself_, and the `leaves.length === 0` guard was unreachable. The
72
+ container's own stored pose then became the overlay bounds — the one value
73
+ this resolver exists to avoid, and at its most stale when nothing is left
74
+ inside to have moved it.
75
+
76
+ A childless node now counts as a leaf only when it is not itself a container.
77
+
78
+ - 83ba8b0: A finished timeline can be scrubbed back instead of going inert
79
+
80
+ A non-looping timeline that reached `duration` returned `finished` from its
81
+ tick and left the animator's table, but its handle kept answering `time()` and
82
+ `duration()` as though it were live. `seek()` moved a playhead nothing ticked —
83
+ no error, no state change. The only recovery was to build a new timeline.
84
+
85
+ `seek`, and an `edit` that extends the duration past the playhead, now
86
+ re-register the timeline and it plays on. It still finishes: an entry that
87
+ never retires would hold a slot, and the frame loop, open for every timeline
88
+ ever created.
89
+
90
+ - `onDone` fires once per arrival at the end, and again for a replay. A handler
91
+ that seeks back from inside `onDone` keeps the entry live rather than
92
+ stranding the replay it just started.
93
+ - Reviving re-registers under the same `cancelKey` without cancelling whoever
94
+ claimed that key meanwhile, and the revived timeline is still cancellable by
95
+ it.
96
+ - `cancel()` is final, including on a timeline that had already finished. No
97
+ seek or edit revives a cancelled one.
98
+ - A `pause()` or `setTimeScale()` taken while the timeline was off the table
99
+ applies when it comes back, so scrubbing a paused transport does not silently
100
+ start playback.
101
+ - @weasel-js/font@1.1.0
102
+ - @weasel-js/geom@1.1.0
103
+ - @weasel-js/gestures@1.1.0
104
+ - @weasel-js/history@1.1.0
105
+ - @weasel-js/modes@1.1.0
106
+
107
+ ## 1.0.4
108
+
109
+ ### Patch Changes
110
+
111
+ - da7c150: The solid batch stops re-sending index data a ring slot already holds, and
112
+ `u_color` / `u_alpha` join the per-frame uniform cache.
113
+
114
+ A run of rects has indices that are a pure function of the rect count, so a
115
+ slot coming round to the same count already holds the right bytes. A mesh's
116
+ indices are rebased onto the staged vertices and a respecified buffer keeps
117
+ nothing, so both mark the slot as holding no pattern and the next flush writes.
118
+
119
+ `u_color` and `u_alpha` were excluded from `UploadedUniforms` because several
120
+ places wrote them directly. All of those now go through `setColorUniform` /
121
+ `setAlphaUniform`, which makes the cache correct per program rather than
122
+ dependent on knowing which caller uses which — the batch holds `u_color` at
123
+ white forever and was re-sending it once per flush.
124
+
125
+ Measured together on an M2 Max via ANGLE (`tests/perf/clip-cost.spec.ts`): a
126
+ flush 5.39 -> 3.22 us, entering a clip 12.47 -> 9.53. Read the difference, not
127
+ the absolutes — the same spec measured a 4.35 us flush earlier on a cooler
128
+ machine, and only the two halves of one A/B are comparable.
129
+
130
+ - f7df982: Path booleans keep holes, and Bezier flattening terminates on degenerate input
131
+
132
+ `pathToMultiPolygon` emitted every contour of a path as its own polygon.
133
+ `polygon-clipping` unions the polygons of a MultiPolygon, so a donut lost its
134
+ hole the moment it entered any boolean op — `pathUnion`, `pathIntersect`,
135
+ `pathSubtract`, `pathExclude`, `pathDivide` and `pathCrop` all returned a
136
+ filled disc. The path's `fillRule` was never read. Contours are now resolved
137
+ into outer + hole rings by nesting depth, honoring both `nonzero` and
138
+ `evenodd`, before the clipper sees them.
139
+
140
+ Results of a boolean op on a path with holes change shape, so this can move
141
+ pixels.
142
+
143
+ `flattenCubic`, `flattenQuadratic` and their arc-length variants recursed
144
+ forever on a non-finite control point and on a tolerance of `0`, `NaN`, or a
145
+ negative number — the flatness test can never be satisfied, so a stroke or a
146
+ hit-test on a path carrying a `NaN` coordinate blew the stack. Non-finite
147
+ input now emits the segment endpoint and stops; a non-positive tolerance is
148
+ floored at `1e-6`.
149
+
150
+ - 85be764: Fix four renderer bugs found in a review of `packages/core/src/renderer`.
151
+
152
+ - A stroke carrying per-anchor `vertexColors` never set the stencil test
153
+ before its draw. Inside a clipped group it ignored the clip and wrote clip
154
+ bits of its own; drawn after a clipped group it was tested against a bit
155
+ that had just been cleared and disappeared entirely.
156
+ - `GroupState` was never reset between frames, so a frame that threw part-way
157
+ down the tree (the max-7 clip nesting error, for one) left every enclosing
158
+ group's transform, alpha and color matrix on the stack and shifted every
159
+ later frame.
160
+ - `dispose()` never deleted the pattern-fill program.
161
+ - `GLTextureCache` applied a texture's wrap mode on first upload only, so a
162
+ registered image used as both a clamped shader texture and a repeating
163
+ pattern tile got whichever mode reached it first.
164
+
165
+ Also: re-registering a custom shader program on a renderer now deletes the
166
+ program it replaces instead of orphaning it.
167
+
168
+ - a3db906: Correctness pass over the tools, canvas, interactions and affordances layers.
169
+
170
+ Fixed, each with a test: a UI-driven ongoing action (color/opacity pickers)
171
+ committed a second time when `cancelAll` had already ended its handle; the
172
+ `align.*` and `distribute.*` descriptors reported themselves permanently
173
+ disabled, greying out every `<ActionBar>` entry; `reorder.backward`,
174
+ `align.*`, `distribute.*` and `pathfinder.*` read deps they never declared in
175
+ `requires`, which throws in dev builds and silently bypasses a consumer's
176
+ history in production; `ActionsRegistry.begin` ignored `requires` and so never
177
+ passed the paint actions their `applyOps`; `Canvas.hitTestExtras` handed
178
+ registered layers `undefined` where `draw` gets live data, making
179
+ `composeAffordanceLayer`'s hit-test throw; `<Canvas>` never disposed its GL
180
+ renderer on unmount; `useTools` returned a stale registry when a tool was
181
+ added after mount; an `actions` prop override deleted the default action it
182
+ merged onto when the prop object was rebuilt; picking and marquee selected
183
+ nodes on hidden layers; a throwing drop-zone `onDrop` stranded every later
184
+ pointer drag; `snapBackOrDelete`'s `'snap-back'` policy left the node where it
185
+ was dropped; a cancelled pen handle-drag left the anchor it placed; and anchor
186
+ affordances on a second selected path routed their drag into the path actually
187
+ being edited.
188
+
189
+ Removed as unreachable: `useViewportTools`, `Canvas.previewBoundsExtra`,
190
+ `marqueeDrawCommands`, `applyHitExistingGate`, and the `enableKeyboard` options
191
+ on `useAlign` / `useDistribute`, which documented a registration those hooks do
192
+ not perform — `useStandardActions` owns it. The public `InsertOverlayStyle`
193
+ type `marqueeDrawCommands` carried is unchanged and still exported.
194
+
195
+ - 12303bc: The solid batch no longer rewrites one pair of GL buffers on every flush. It
196
+ cycles a ring of buffer sets instead — 64 slot-sized ones, plus 4 growable ones
197
+ for a flush too big for a slot — so a write lands that many draws behind the
198
+ draw that read the same buffer. On an M2 Max via ANGLE a flush goes from ~54 us
199
+ to ~4.4, which is the cost anything that breaks a run of solid geometry was
200
+ paying: entering a clip is 64.9 us -> 10.2, and a boundary between solid and any
201
+ other command kind is 28.4 us -> 2.5. Measured by `tests/perf/clip-cost.spec.ts`
202
+ and `tests/perf/transition-matrix.spec.ts`.
203
+
204
+ The driver tracks a write hazard per buffer object, so a flush that overwrote
205
+ its buffers from offset 0 waited on the draw still reading them. Writing
206
+ disjoint ranges of one buffer does not escape that — the hazard is per object,
207
+ not per range.
208
+
209
+ Nothing about the API or the pixels changes. Buffers are taken on first use
210
+ rather than at construction, so a renderer that never draws solid geometry
211
+ allocates none, and every set is freed in `WeaselRenderer.dispose`.
212
+
213
+ - d36953e: SVG import and export lose less on the way through, and installed fonts pick
214
+ one face per variant slot.
215
+
216
+ Paint servers are now found wherever they are declared, not just as direct
217
+ children of `<defs>`, and a gradient that inherits another's stops or geometry
218
+ through `href` / `xlink:href` resolves instead of coming back empty.
219
+ Percentages are read as ratios, so `x2="100%"` no longer means 100 bounds
220
+ units, and `gradientTransform` warns rather than silently painting elsewhere.
221
+
222
+ Three fidelity bugs in the round trip itself. A leaf's own `transform` was
223
+ decomposed against bounds that had already been through the inherited matrix,
224
+ so a rotation inside a translated `<g>` lost its rotation and moved. Any
225
+ stroke carrying `stroke-opacity` re-serialized with the attribute written
226
+ twice, which is not well-formed XML. And the computed `viewBox` was taken from
227
+ unrotated, untransformed geometry, cropping rotated content out of the export.
228
+
229
+ `<text>` now follows SVG's whitespace rules, so importing a pretty-printed
230
+ file no longer drags the source indentation into the document text; weasel's
231
+ own `<text>` carries `xml:space="preserve"` to keep real line breaks. A nested
232
+ `<svg x= y=>` places its children at that origin.
233
+
234
+ In the shared `d=` grammar, exponent coordinates (`M1e2 1e2`) no longer read
235
+ the `e` as a command, and arc flags written without separators
236
+ (`A5 5 0 0110 0`) no longer drop the arc.
237
+
238
+ `enableLocalFontOutlines` picks the least-qualified face when several installed
239
+ faces reduce to one (weight, style) slot, so "Helvetica Neue Condensed Bold"
240
+ stops displacing "Helvetica Neue Bold" depending on query order.
241
+
242
+ **Exported SVG bytes change**: `<text>` gains `xml:space="preserve"`, a stroke
243
+ writes `stroke-opacity` once, and a document containing rotated or
244
+ group-transformed content gets a larger computed `viewBox`.
245
+
246
+ - Updated dependencies [bd42540]
247
+ - Updated dependencies [d36953e]
248
+ - @weasel-js/geom@1.0.4
249
+ - @weasel-js/history@1.0.4
250
+ - @weasel-js/gestures@1.0.4
251
+ - @weasel-js/font@1.0.4
252
+ - @weasel-js/modes@1.0.4
253
+
3
254
  ## 1.0.3
4
255
 
5
256
  ### Patch Changes