@weasel-js/core 1.0.1 → 1.0.2

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 +269 -0
  2. package/dist/{chunk-FVNWK7U3.js → chunk-PI56ORHE.js} +1593 -1316
  3. package/dist/chunk-PI56ORHE.js.map +1 -0
  4. package/dist/{chunk-DCIU3SRK.js → chunk-W2WB4352.js} +79 -151
  5. package/dist/chunk-W2WB4352.js.map +1 -0
  6. package/dist/clipboard.d.ts +1 -1
  7. package/dist/clipboard.js +0 -1
  8. package/dist/clone.d.ts +2 -2
  9. package/dist/clone.js +0 -1
  10. package/dist/{geometry-DVeZ2i-c.d.ts → geometry-CnISZ11w.d.ts} +1 -1
  11. package/dist/{grid-XcFQVS0f.d.ts → grid-CBiLBnyc.d.ts} +2 -2
  12. package/dist/index.d.ts +163 -43
  13. package/dist/index.js +2 -3
  14. package/dist/insert.d.ts +3 -3
  15. package/dist/insert.js +0 -1
  16. package/dist/insert.js.map +1 -1
  17. package/dist/move.d.ts +5 -5
  18. package/dist/move.js +0 -1
  19. package/dist/move.js.map +1 -1
  20. package/dist/{options-wAvCnOZd.d.ts → options-DoOvx50U.d.ts} +1 -1
  21. package/dist/patterns-builtin.js +0 -1
  22. package/dist/{pointSnapToGrid-BuDZWBrL.d.ts → pointSnapToGrid-Bj4r_lEQ.d.ts} +3 -3
  23. package/dist/{index-DPI8DV0A.d.ts → registry-BQL4oyTg.d.ts} +139 -348
  24. package/dist/renderer.d.ts +11 -0
  25. package/dist/renderer.js +2 -3
  26. package/dist/resize.d.ts +5 -5
  27. package/dist/resize.js +0 -1
  28. package/dist/routing.d.ts +104 -5
  29. package/dist/routing.js +1 -2
  30. package/dist/{types-B_-khFM0.d.ts → types-Ch0Goo-j.d.ts} +12 -0
  31. package/dist/{types-BhJJ2OCM.d.ts → types-Ct4n0Z38.d.ts} +1 -1
  32. package/dist/{types-BJ8_cyT7.d.ts → types-Dtxe7jTS.d.ts} +20 -1
  33. package/package.json +6 -6
  34. package/dist/chunk-DCIU3SRK.js.map +0 -1
  35. package/dist/chunk-FVNWK7U3.js.map +0 -1
  36. package/dist/chunk-PZ5AY32C.js +0 -9
  37. package/dist/chunk-PZ5AY32C.js.map +0 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,274 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.0.2
4
+
5
+ ### Patch Changes
6
+
7
+ - 28710f2: The renderer's solid batch is drained at the sites that decide routing or
8
+ change stencil state, instead of at each emitter that happened to remember.
9
+ Geometry that cannot join a run now goes through `tryStageSolid`, which returns
10
+ `false` only after flushing — so the only way an emitter earns permission to
11
+ draw for itself is to have called the function that drained the batch. `pushClip`
12
+ and `popClip` flush as their first statement, since rasterizing into the stencil
13
+ is what creates the obligation.
14
+
15
+ Nothing about the drawn result changes; this removes a way for a future draw
16
+ path to paint under geometry staged before it.
17
+
18
+ - 2e3fea2: The `routing` namespace is gone from the main barrel, and the reflection
19
+ surface exports only what a consumer can use.
20
+
21
+ `export * as routing` made tool authoring reachable two ways — `core.routing.defineTool`
22
+ and the `@weasel-js/core/routing` subpath — and no consumer ever used the first.
23
+ It survived as the unfinished half of the 2026-05-12 declarative-routing work,
24
+ whose Phase 6 was to move `defineTool` out of `routing/` entirely. **Removing
25
+ it is a breaking change for anyone importing the namespace form**; the subpath
26
+ is unchanged and is what every known consumer already uses.
27
+
28
+ `tools/routing/reflection` now exports `buildRouteRegistry`, `findConflicts`,
29
+ `RegistryEntry` and `Conflict` — the four an external inspector needs.
30
+ `PREDICATE_TARGET`, `findScopedConflicts`, `formatConflict`,
31
+ `reportRouteConflicts` and `ToolScopes` are still there and still used; they
32
+ are just no longer public, since their only caller is inside the kit.
33
+
34
+ - 75ba7b1: Three fixes found by re-checking backlog entries against the code.
35
+
36
+ SVG gradients survive a round trip. The parser now reads `gradientUnits`
37
+ (`objectBoundingBox` → `units: 'bounds'`, `userSpaceOnUse` → `'world'`) and the
38
+ serializer writes back whichever the paint declares, instead of hardcoding
39
+ `userSpaceOnUse` on the way out — which had been reading a box-relative
40
+ gradient's `0..1` geometry as page coordinates, i.e. a gradient the size of a
41
+ pixel.
42
+
43
+ `unpackSvgFiles` keeps gradient fills instead of flattening them to a solid.
44
+ The reason recorded for the flattening — that the `kit:path` painter has no
45
+ gradient slot — had not been true for some time; `NodeFill` is
46
+ `string | FillStyle`, now exported. A `userSpaceOnUse` gradient is normalized
47
+ against the leaf's own box on the way in, so it survives the fit-clamp and
48
+ drop-point placement that move the geometry out from under it. Gradient
49
+ _strokes_ still flatten: `data.stroke` genuinely is a color string.
50
+
51
+ `extractUniformNames` skips precision and interpolation qualifiers. `uniform
52
+ highp float u_t;` — the common spelling in hand-written GLSL — matched nothing
53
+ at all, so the uniform got no location and every write to it was dropped in
54
+ silence. Comma-separated declarator lists (`uniform float a, b;`) read too.
55
+
56
+ Also adds `tests/visual/text-decoration.spec.ts`, a baseline-free assertion
57
+ that underline and strikethrough sit `0.40 em` apart and span only their own
58
+ runs. It measures the gap between two gap-free horizontal ink runs, which is
59
+ something `text.spec.ts`'s 5% diff tolerance cannot see move.
60
+
61
+ - e4a6ec4: Tool authoring moved out of `routing/` and onto the main barrel. `defineTool`,
62
+ `defineViewportTool`, `ToolDef`, `ViewportToolDef` and `ToolKeybinding` now come
63
+ from `@weasel-js/core`:
64
+
65
+ ```ts
66
+ import { defineTool, type ToolDef } from "@weasel-js/core";
67
+ ```
68
+
69
+ **This is a breaking change for anyone importing them from
70
+ `@weasel-js/core/routing`.** That subpath keeps the route grammar (`parseRoute`,
71
+ `formatRoute`, `describeRoute`, the gesture descriptors) and the reflection
72
+ consumers (`buildRouteRegistry`, `findConflicts`) — it is now only the surface
73
+ that _reads_ routes back, matching its name.
74
+
75
+ Finishes Phase 6 of the 2026-05-12 declarative-routing work. Source layout
76
+ follows: the factory and its `ToolDef` types sit at the top of `src/tools/`
77
+ alongside `useTools` and `useKeybindings`, with `routing/types.ts` renamed
78
+ `routeTypes.ts` to clear the existing `tools/types.ts`. Behavior and runtime
79
+ contract are unchanged.
80
+
81
+ - d2a9049: Six backlog entries, all in the input and hit-test layers.
82
+
83
+ Tool overlays can say where they sit. `Contribution.overlay` takes a
84
+ `RenderLayer` or an array of them, and a new `overlayPosition` (`'top'` — the
85
+ default — / `'before-selection'` / `'after-selection'`) anchors them against
86
+ the selection chrome instead of always landing on top; with no selection layer
87
+ in the stack the anchored positions fall back to the tail.
88
+ `getActiveOverlays(position?)` partitions, and `placeToolOverlays` in
89
+ `canvas/layerOrder.ts` does the splice, so the ordering is testable without a
90
+ GL context.
91
+
92
+ `drop` and `paste` are route-grammar gesture names. They shipped without them,
93
+ so the inspector reported `undefined` for every ingestion binding. Both are
94
+ targetless and carry the spec's MIME-glob filter as their arg — `drop(image/*)`.
95
+
96
+ An unhandled paste stays the page's. `onPaste` now dispatches first and calls
97
+ `preventDefault` only on `'handled'`, the shape `onWheel` already used.
98
+ Clipboard items materialize synchronously, so unlike `onDrop` the result is
99
+ known while the default can still be suppressed.
100
+
101
+ Marquee and lasso see the shape a node actually draws. `hitTestArea` asked the
102
+ _pose_ for a silhouette, which meant the kit's own inserted shapes — geometry
103
+ on `node.data.path` behind a plain `{x,y,w,h}` pose — took the AABB path and
104
+ kept every false positive the silhouette test exists to drop. It now asks
105
+ `findShapeSilhouette` for the drawn world-frame boundary. Paying for that is a
106
+ containment short-circuit: a node the rect marquee swallows whole is a hit no
107
+ silhouette can overturn, so neither the kernel nor the painter runs. Net
108
+ against the committed bench baseline, polygon-pose scenes gain (1.25x at 1000
109
+ nodes / 100% area) and plain-rect scenes lose ~8% on the two rows that scan
110
+ everything. The short-circuit is marquee-only, behind `hitTestAreaPolygon`'s
111
+ `areaIsRect` flag, because a node inside a lasso hull's bounding box can still
112
+ miss the hull.
113
+
114
+ Layout siblings reflowing mid-drag render opaque. `OngoingHandle.previewOpaqueIds()`
115
+ names the subset of `previewIds()` that skips the ghost alpha; `moveAction`
116
+ fills it from the destination and source reflow ids. A ghost means "in flight
117
+ under the pointer", which a sibling settling into its destination slot is not.
118
+
119
+ `LayoutStrategy.acceptsDrop(container, dragged)` rejects a drag before any
120
+ drop-target work, so a type-aware container falls through to whatever sits
121
+ under it rather than swallowing everything in its bounds.
122
+
123
+ `flipAction` reads `params.pivot`. `'each'` (the default, unchanged) mirrors
124
+ every pose about its own AABB; `'union'` mirrors about the selection envelope
125
+ so items swap sides, and the `geometryProjection` data op follows the same
126
+ pivot.
127
+
128
+ - 5f05431: `viewport.pinchZoom` now pans as well as zooms. It anchored `zoomAt` on the
129
+ current gesture centroid and never translated by the centroid delta, so two
130
+ fingers travelling together — spread unchanged, zoom factor 1 — moved the view
131
+ not at all.
132
+
133
+ Each frame now anchors the zoom on the previous centroid and then translates by
134
+ how far the centroid travelled. Together those pin the world point under the
135
+ gesture midpoint as the midpoint moves, which is what makes a pinch feel
136
+ attached to the fingers.
137
+
138
+ The action's id and label are unchanged (`viewport.pinchZoom` / `Pinch Zoom`).
139
+ Consumers who bound it get panning with no wiring change.
140
+
141
+ - 5fea43d: Five unrelated small fixes.
142
+
143
+ A tapered stroke with `align: 'inner'` or `'outer'` on a polygon path painted
144
+ at half its requested widths. That alignment renders by tessellating at twice
145
+ the width and stencilling half away, and the doubling reached `stroke.width`
146
+ but not `vertexWidths`. The doubled array is memoized per source array, since
147
+ the ribbon cache compares it by reference.
148
+
149
+ An image insert previews the decoded bitmap inside the drag bounds instead of
150
+ committing on release with no preview at all. It falls back to the bare
151
+ outline until the image decodes, and `useImageTool({ preview: 'outline' })`
152
+ opts out of the bitmap entirely.
153
+
154
+ A HUD widget that claims the pointer reports a `'pointer'` cursor without
155
+ implementing anything — hovering one while a drawing tool was active used to
156
+ keep showing that tool's cursor. The rule is keyed on the `claims` every
157
+ widget already declares, so it covers consumer-authored widgets too;
158
+ decoration claims nothing and the hit walk descends past it. A widget's own
159
+ `cursorAt` still wins, and `button` takes a `cursor` option that feeds it.
160
+
161
+ `composeAffordanceLayer`'s `hitTest` returns a `LayerHit`, carrying the hit
162
+ region's declared cursor and claim instead of dropping them. `AffordanceRegion`
163
+ gains optional `strength` / `claimedKinds` to declare that claim.
164
+
165
+ `ToolPalette` uses the shared `useRovingTabIndex` rather than its own container
166
+ handler, so arrow keys skip tools that are ineligible in the current mode and
167
+ the tab stop no longer sits on one. `ToolButton` takes an `onKeyDown`.
168
+
169
+ - 443d74e: Stroke ribbons are tessellated once per stroke configuration instead of once
170
+ per frame. The renderer rebuilt every stroked path's ribbon geometry on every
171
+ frame and discarded it; it now caches the result on `Path` identity, keyed by
172
+ the parameters that change the ribbon — width, cap, join, miter limit,
173
+ alignment, dash, and flatten tolerance. Paint and vertex colors are not in the
174
+ key, since both are applied over the same triangles at draw time.
175
+
176
+ A ribbon that survives a frame also stops paying for a fresh VAO and two
177
+ buffers on every subsequent frame. One whose path or stroke parameters change
178
+ each frame keeps the transient upload it had before, freed at end of frame —
179
+ so an animated or freshly drawn path never accumulates GL resources waiting on
180
+ garbage collection.
181
+
182
+ Nothing about the drawn result changes. The cache is keyed on object identity,
183
+ matching the fill cache: a `Path` rebuilt with equal coordinates is a distinct
184
+ entry and re-tessellates.
185
+
186
+ - 7decec1: Four more backlog fixes.
187
+
188
+ `@weasel-js/svg` reads and writes `<image>`. A new `SvgImageNode` holds the
189
+ `href` verbatim — an external URL or a `data:` URI, with `xlink:href` accepted
190
+ on the way in — plus a box that inherited transforms collapse onto and an
191
+ element-local rotation. `unpackSvgFiles` maps it onto the `kit:image` painter's
192
+ `data.image.src`, so a dropped SVG carrying raster content now keeps it instead
193
+ of dropping the element on parse. `preserveAspectRatio` is not modeled: the box
194
+ is taken literally and written back as `none`, and a non-`none` source warns.
195
+
196
+ `pickTopMostHit` resolves sibling z-order. An adapter can supply `getZIndex(id)`
197
+ or `compareZ(a, b)`; both compose with the existing parent/child collapse rather
198
+ than replacing it, so a child still beats its own ancestor whatever z the two
199
+ report. Without either, the hit list's own order decides, as before.
200
+
201
+ `useSceneTextEdit` supplies `setStyle`. Clearing a style flag that the _node_
202
+ sets is the one edit the additive run algebra can't express, and `useTextEdit`
203
+ declines it without a writer — so every scene-wired consumer silently refused
204
+ that toggle. Override the projection with `setStyle(data, style)` for a
205
+ non-default data shape.
206
+
207
+ The slops debug overlay draws handle halos at the real hit radius. Affordance
208
+ regions moved to screen-pixel radii, but this layer still scaled its circles by
209
+ the view, so at 4x zoom it drew a 32px halo over an 8px target — the one thing
210
+ a hit-test overlay must not do. Anchor slops now read the anchor radius rather
211
+ than the handle radius.
212
+
213
+ - 24daa08: Five unrelated backlog fixes.
214
+
215
+ The specificity tuple's `phase` dimension is graded rather than binary: an
216
+ atom scores 2 when both its channel and its lifecycle state are concrete, 1
217
+ when one axis is wildcarded, and 0 for `*:*`, which matches everything an
218
+ undeclared phase would have matched. An atom list takes the minimum, since
219
+ `matchPhase` is a union. No existing binding reorders — the four ambient
220
+ actions hold at 1, and the polygon and star tools' `phase: 'engaged'` wheel
221
+ bindings rise to 2, widening a gap they already won.
222
+
223
+ The loupe's pixel mode no longer drops the end of a fast drag: a readback
224
+ requested while `createImageBitmap` is in flight is remembered and re-run when
225
+ that one settles, instead of being discarded.
226
+
227
+ SVG unpack applies the fit-clamp to text on both axes. `fontSize` now scales
228
+ with the file, and a text node's box width is estimated from its longest line
229
+ instead of inheriting the parser's unbounded-width wrap sentinel — which,
230
+ folded into the union AABB, had been clamping any external SVG containing
231
+ text down to a speck. That sentinel is now the exported `UNBOUNDED_TEXT_WIDTH`
232
+ rather than a bare `99999`, so a consumer reading `SvgTextNode.width` can tell
233
+ a measurement from a placeholder.
234
+
235
+ The debug overlay takes per-feature line widths and dashes through
236
+ `DebugConfig.strokes`, alongside the colors `DebugConfig.theme` already
237
+ carried. Defaults are unchanged.
238
+
239
+ A `.dfont` face declines the outline tier by name. Datafork TrueType holds its
240
+ sfnt tables inside a Macintosh resource map, which is still not unpacked, but
241
+ it is now recognized before parsing and reported as itself rather than dying
242
+ on an unrecognized-signature message that never says which format it saw.
243
+
244
+ - f79e4b2: Viewport layers can now answer where a screen point lands inside them.
245
+ `createViewportLayer` returns a `ViewportLayer`, adding:
246
+
247
+ ```ts
248
+ layer.reproject(outer, dims, screen); // → inner-world point, or null if outside
249
+ viewportsAt(layers, outer, dims, screen); // → topmost viewport containing it
250
+ ```
251
+
252
+ `bounds` is already a pure function of `(outer, dims)`, so re-projection
253
+ recomputes the exact rect the frame painted rather than a remembered one — no
254
+ stored state and nothing to go stale. Right and bottom edges are exclusive, so
255
+ adjacent viewports never both claim a pixel.
256
+
257
+ This deliberately does **not** touch the dispatcher, which the previous
258
+ docstring promised it would. Tools still target the outer view; a consumer that
259
+ wants a click inside a viewport to mean something calls `reproject` from its own
260
+ handler. Making tools work _inside_ a viewport raises questions this primitive
261
+ does not answer — which view a pinch zooms, what a drag leaving the rect does —
262
+ and is tracked as its own item.
263
+
264
+ - Updated dependencies [d2a9049]
265
+ - Updated dependencies [24daa08]
266
+ - @weasel-js/gestures@1.0.2
267
+ - @weasel-js/font@1.0.2
268
+ - @weasel-js/geom@1.0.2
269
+ - @weasel-js/history@1.0.2
270
+ - @weasel-js/modes@1.0.2
271
+
3
272
  ## 1.0.1
4
273
 
5
274
  ### Patch Changes