@weasel-js/core 1.4.3 → 1.4.4

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 +367 -0
  2. package/dist/{pointSnapToGrid-D1bRdq-j.d.ts → autoPoseDescriptor-DF1SnnSx.d.ts} +18 -3
  3. package/dist/{builtins-CtGORLCG.d.ts → builtins-BXFBXegF.d.ts} +200 -100
  4. package/dist/{chunk-ORZNKVXM.js → chunk-2VXGHUVL.js} +3 -3
  5. package/dist/{chunk-ORZNKVXM.js.map → chunk-2VXGHUVL.js.map} +1 -1
  6. package/dist/{chunk-LEURURX3.js → chunk-BL65SHCX.js} +17 -5
  7. package/dist/chunk-BL65SHCX.js.map +1 -0
  8. package/dist/{chunk-BLJNRMKB.js → chunk-PRGBGMH3.js} +3 -3
  9. package/dist/{chunk-BLJNRMKB.js.map → chunk-PRGBGMH3.js.map} +1 -1
  10. package/dist/{chunk-WYPSGIYS.js → chunk-R3AWPTLZ.js} +1773 -1209
  11. package/dist/chunk-R3AWPTLZ.js.map +1 -0
  12. package/dist/{chunk-SBFC6J3C.js → chunk-WPM42WJP.js} +3 -3
  13. package/dist/{chunk-SBFC6J3C.js.map → chunk-WPM42WJP.js.map} +1 -1
  14. package/dist/clipboard.d.ts +2 -2
  15. package/dist/clone.d.ts +2 -2
  16. package/dist/{geometry-CHR36Ub_.d.ts → geometry-6fCNhAux.d.ts} +1 -1
  17. package/dist/{grid-Bw8cSde-.d.ts → grid-0Pbn5B2C.d.ts} +1 -14
  18. package/dist/index.d.ts +231 -74
  19. package/dist/index.js +5 -5
  20. package/dist/insert.d.ts +3 -3
  21. package/dist/insert.js +1 -1
  22. package/dist/move.d.ts +4 -4
  23. package/dist/move.js +2 -2
  24. package/dist/{options-DYfUlZxk.d.ts → options-DbYLImvq.d.ts} +1 -1
  25. package/dist/{registry-DnoTWGED.d.ts → registry-BY-wI9gm.d.ts} +4 -4
  26. package/dist/renderer.d.ts +2 -2
  27. package/dist/renderer.js +5 -5
  28. package/dist/resize.d.ts +5 -4
  29. package/dist/resize.js +2 -2
  30. package/dist/routing.d.ts +5 -5
  31. package/dist/routing.js +1 -1
  32. package/dist/{types-CoVTbo_y.d.ts → types-DEALFt5F.d.ts} +1 -6
  33. package/dist/{types-H7o6MaPo.d.ts → types-bcc7jcUy.d.ts} +96 -14
  34. package/dist/{types-BJqsTlXl.d.ts → types-ei3UMl9R.d.ts} +1 -1
  35. package/package.json +9 -9
  36. package/dist/chunk-LEURURX3.js.map +0 -1
  37. package/dist/chunk-WYPSGIYS.js.map +0 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,372 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.4.4
4
+
5
+ ### Patch Changes
6
+
7
+ - 9ce6f00: Fix solid fills coming out multiplied by whatever bitmap shared their batch.
8
+ A solid vertex carried the UV of a 1x1 white texel, but the flush bound the
9
+ run's *image*, so any solid staged alongside an image quad sampled that bitmap
10
+ at its middle texel instead. A wall of thumbnails is a ground rect under an
11
+ atlas quad per cell — the shape the merged batch exists for — so its grounds
12
+ came out tinted by the atlas: white drew olive. Reported from outside the repo
13
+ against a canvas2d reference; no visual baseline caught it, because in every
14
+ demo the quad covers its ground.
15
+
16
+ Every batch vertex now names the texture slot it samples. Slot 0 is always the
17
+ white texel, so a solid's `texture() * a_vertexColor` is the vertex color
18
+ exactly whatever else joins its run, and bitmaps take the slots above it.
19
+
20
+ That also lets one run hold up to seven distinct bitmaps. A document with a
21
+ handful of loose images used to break its run on every change of bitmap; now it
22
+ breaks only when the slots run out, or when one bitmap is drawn at two
23
+ MAG_FILTERs, which is state on the texture object and cannot be had both ways
24
+ in one draw.
25
+
26
+ `tests/visual/batch-pixels.spec.ts` reads the framebuffer channel by channel
27
+ rather than diffing a screenshot, which is what it takes to see a run's
28
+ composition at all.
29
+ - 6f876a7: Two canvases under one `DepRegistryProvider` no longer take each other's deps
30
+ down.
31
+
32
+ `register` set one source per name and its release did a bare `delete`. Mount a
33
+ second canvas under a shared provider and it displaced the first's `view` /
34
+ `scene` / `selection`; unmount either one and the name went dark for the canvas
35
+ still on screen.
36
+
37
+ Sources now stack per name, newest live, and a release removes its own entry
38
+ wherever it sits — so a displaced source comes back when the one above it
39
+ leaves, and a displaced source leaving disturbs nothing. Same shape as the
40
+ `ActionsProvider` registrant stack, one layer down.
41
+ - ed400a3: A derivation now receives its dependencies as `{ node, pose }` rather than as
42
+ bare poses. Breaking for anything with a `derivePath` or `derivePose`: read
43
+ `deps[0]?.pose` where it read `deps[0]`.
44
+
45
+ A connector legitimately reads more than a box — one that thickens with its
46
+ endpoint's weight, or routes only to nodes on a given layer, is answering off
47
+ `data` and `layer`. The scene has invalidated dependents on `kit:setData` and
48
+ `kit:setLayer` since those landed, and `scene.ts` said in a comment that a
49
+ derivation "is handed its dependencies' nodes, not only their poses". It was
50
+ not: the invalidation was paying for a read the signature could not perform.
51
+
52
+ `scenePoseLookup` is now `sceneDepLookup` and answers `DerivedDep` for the same
53
+ reason. `DerivedDep` is exported.
54
+ - fc00dae: Hand a derivation its dependencies' paths, and label an edge with one.
55
+
56
+ `DerivedDep` is now `{ node, pose, path }`. The path resolves on first read and
57
+ memoizes, so a route costs the same whether one node reads it or five, and a
58
+ dependency nobody asks about costs nothing. `resolveDerivedPath` moves beside
59
+ `derivedPose` in `core/scene` — a pose can now derive from a dependency's path —
60
+ and picks up the cycle guard the pose side already had.
61
+
62
+ `pointAlongPath(path, t)` is the new geometry primitive underneath: where a path
63
+ is at a fraction of its length, and which way it heads there, measured along the
64
+ flattened arc.
65
+
66
+ In `@weasel-js/diagram`, an edge label is an ordinary leaf node with
67
+ `dependsOn: [edge]` and `LABEL_DERIVE_POSE`. Its trait says where it sits —
68
+ `at: 'start' | 'mid' | 'end'` or a fraction, plus an `offset` perpendicular to
69
+ the route — and it reads the edge's resolved path rather than routing again, so
70
+ a label and its arrowhead can never disagree about where the edge went.
71
+ - 730da55: A derived node now recomputes when a dependency's `data` or `layer` changes.
72
+
73
+ `derivePath` is handed its dependencies' nodes, not only their poses — so a
74
+ connector that thickens with a node's weight, or draws only for nodes on a given
75
+ layer, is answering off `data` and `layer`. `kit:setPose` and `kit:move`
76
+ invalidated the dependent's memo; `kit:setData` and `kit:setLayer` did not, so
77
+ the derived geometry kept the old answer with nothing on screen to show it was
78
+ stale. Undo and redo were wrong the same way.
79
+
80
+ Both ops now invalidate dependents on `apply` and on `revert`, matching the two
81
+ that already did.
82
+ - 60ba9d9: A stroke marker on a derived path is drawn.
83
+
84
+ `markerStart` / `markerMid` / `markerEnd` reached the `kit:derived` painter
85
+ intact and were then dropped: the painter emitted its stroke command and
86
+ returned, where `kit:path` follows with a marker pass. So an arrowhead on a
87
+ diagram edge — the whole reason markers and derived geometry landed in the same
88
+ release — silently drew nothing. Its own `ink` had been reserving the hit-test
89
+ reach for the marker all along, which is the shape of the bug: the pointer could
90
+ already grab past the end of a line with no head on it.
91
+
92
+ A connect-authored edge now carries `markerEnd: 'arrow'` by default. An edge
93
+ runs *from* one node *to* another and a plain line does not say so;
94
+ `DEFAULT_EDGE_STROKE` is exported for a consumer overriding `commit` who wants
95
+ the rest of it.
96
+ - 5732951: The minimap's scene fit and the scene text editor read a derived pose rather
97
+ than the placeholder a derived node authors. `computeFitView`'s `'scene'` fit
98
+ frames on `documentPose`, so a derived node is framed where it actually is
99
+ while a drag still leaves the framing alone; `useSceneTextEdit` resolves both
100
+ its double-click hit test and the overlay's own projection through
101
+ `effectivePose`, so double-clicking a derived label opens the editor on it and
102
+ the box lands on the text.
103
+
104
+ Removes `UseMoveOptions.cascadeWorldPose`. Nothing has read it since the move
105
+ action started walking `scene.childrenOf` for its own cascade — it was a
106
+ documented option that silently did nothing, and two doc comments described the
107
+ behavior it used to drive.
108
+ - 2ff4824: A scene node can now derive its **pose** from its dependencies, the way it
109
+ already derived its path. New API; a group's bounds change behavior.
110
+
111
+ `Node.derivePose` takes the same `dependsOn` list as `derivePath`, serializes
112
+ through `SceneRegistry.derivePose` by key, and rides the same push
113
+ invalidation. Where a derived path is resolved at paint time and reaches only
114
+ the painter, a derived pose is what the node *is* at — it feeds bounds,
115
+ hit-testing, selection chrome, snapping and layout.
116
+
117
+ `dependsOn` gains a second form, `'children'`: "my own children, in child
118
+ order", which a fixed id list cannot express because reparenting would have to
119
+ maintain it. The two forms differ in lifetime as well as membership — deleting
120
+ a node still deletes everything that names it in `dependsOn`, but a container
121
+ outlives the children it derives from, because an emptied group is still a
122
+ group.
123
+
124
+ `groupAction` uses it, which fixes the group-bounds defect: a container's union
125
+ AABB was computed once at creation and never re-derived, so moving a member
126
+ left the group's bounds, selection chrome and hit area behind. The kit
127
+ registers the union function under `kit:unionOfChildren` and merges its own
128
+ registry entries under the consumer's, so a grouped document round-trips
129
+ through `toJSON` in any scene.
130
+
131
+ `effectivePose(scene, node)` is the one rule — override, else derived, else
132
+ authored — and now takes the scene rather than the override table alone. The
133
+ three render walks, the pick walk, and the scene, commit and gesture adapters
134
+ all resolve through it or through `documentPose`, the same answer minus the
135
+ override step for a reader that must not see an in-flight gesture. Both are
136
+ exported.
137
+
138
+ `clipFromPose`, `derivePath` and `derivePose` now share one table-driven
139
+ serialization path (`core/scene/nodeFnFields.ts`) instead of a copy per field.
140
+ - 3d89141: Layout: `layered`, `tree` and `force`, and the action that runs one.
141
+
142
+ A layout is a plain function of the graph — no scene, no ops, no history. It
143
+ hands back the new top-left for every node that **moves**, and a node already
144
+ standing where the layout wants it is absent from the answer, so re-running a
145
+ layout on an arrangement it produced writes nothing and pushes no undo entry.
146
+
147
+ Three rules keep a re-layout from scrambling a diagram someone has arranged.
148
+ There is no RNG anywhere in the path, so the same graph always lays out the same
149
+ way. Within-rank order is seeded from where the nodes already sit on the cross
150
+ axis rather than from crossing-minimization, so two branches an author dragged
151
+ into an order come back in it. And a node carrying `pinned: true` never moves,
152
+ with the rest of the layout translated to sit around it — with no pin, the
153
+ layout lands on the diagram's own bounding box rather than at the origin.
154
+
155
+ `layered` ranks by longest path, breaking cycles with a depth-first walk in node
156
+ order so a loop draws as an edge running back up the page. `tree` centers a
157
+ parent over its children's block; a graph that is not a tree still lays out,
158
+ since roots are the nodes nothing points at and anything the walk cannot reach
159
+ becomes a root of its own. `force` is an iterative relaxation seeded from the
160
+ current positions — **the one layout that is not idempotent**, since re-running
161
+ it keeps relaxing.
162
+
163
+ `buildGraph` reads the adjacency index from the same participant source the port
164
+ affordance takes, per invocation rather than maintaining one. `createLayoutAction`
165
+ rebuilds it on each press and writes the whole move as a single `scene.batch`,
166
+ carrying a container's whole subtree — `setPose` does not cascade, and a built
167
+ body would otherwise walk out from under its own label rows.
168
+
169
+ In core, `createSimulation` is the velocity-Verlet integrator with no clock
170
+ attached: `tick()` is the only thing that moves a node, so a pure function can
171
+ run a whole relaxation and read the result. `useSimulation` is now that
172
+ integrator on a frame loop and is otherwise unchanged. `SimulationCore` and
173
+ `SimulationOptions` name the halves, and forces can be handed a seeded `random`
174
+ in place of `Math.random`.
175
+ - 4a128c4: New package `@weasel-js/diagram` — the skeleton of weasel's node-link
176
+ diagramming: flowcharts, pipelines, code-flow diagrams, simple visual
177
+ programming. Arc 3 of the diagram design; edges, routing, the connect gesture
178
+ and layout are still to come.
179
+
180
+ **`DiagramNode` is a trait on an existing scene node, not a node type the
181
+ package mints.** A text block, an image, a path, a group or a plain rect all
182
+ become participants by carrying it, and nothing has to be authored through this
183
+ package to take part. Two ways to attach it, both answered by the default
184
+ reader: on the node's own `data.diagram`, or by kind — `createDiagramNodes`
185
+ takes predicates over `data` the way `createNodeRouting` does, so a class of
186
+ node takes part without being stamped one at a time.
187
+
188
+ **Ports default to the perimeter, so a node needs to say nothing to be
189
+ connectable.** `portsOf(node, pose)` resolves every port to a world point and
190
+ an outward normal, reading bounds through the pose descriptor and carrying the
191
+ node's rotation. Anchors are normalized against the bounds — `{ u, v }` from
192
+ the top-left — so a port stays where it was put when the node is resized.
193
+
194
+ **The body builder is optional**, for nodes that should look like a flowchart
195
+ box: `rect`, `diamond`, `stadium` and `parallelogram` outlines plus a `Row[]`
196
+ body of labels, fields, port rows and slots. Rows measure a floor, and the
197
+ authored pose is maxed against it rather than set to it — adding a row can grow
198
+ a node, nothing shrinks one back — which is what keeps resize, align,
199
+ distribute, guides, snapping and undo free of a special case. Text measurement
200
+ is a seam rather than an import; `canvasMeasure` adapts a 2D context.
201
+
202
+ Rows lay out in the shape's **content box**, not its bounding box.
203
+ `contentBox` reports the largest axis-aligned box inside an outline — a
204
+ diamond's inscribed rect, a parallelogram minus its lean, the flat span between
205
+ a stadium's ends — and `boxForContent` inverts it so the floor grows to suit.
206
+ Without it a diamond's label is placed against the bounding box, lands outside
207
+ the diamond, and the silhouette clip removes it: the label simply vanishes.
208
+
209
+ `registerDiagramShape` paints a node whose trait names an outline, and reports
210
+ the outline as its silhouette so picking and clipping follow the diamond rather
211
+ than its box. Rows are not painted there — a built body's rows are ordinary
212
+ scene nodes, so the kit's own text painter draws them and text editing,
213
+ selection and styling work on them unchanged.
214
+
215
+ Core exports `AUTO_POSE_DESCRIPTOR` and `isPathLike`, which were already
216
+ general-purpose but reachable only from inside the package. A peer package
217
+ computing a node's bounds needs the kit's own default descriptor rather than a
218
+ second copy of it.
219
+ - aee9d92: `createPoseOverrides` is public.
220
+
221
+ `Scene` is public and its `overrides: PoseOverrides<TPose>` is mandatory — and
222
+ load-bearing, since every ongoing gesture writes a frame to it. The factory that
223
+ builds one was internal, so a consumer assembling a scene-like object by hand
224
+ had to reimplement the table from its type. It is now exported alongside
225
+ `createScene`.
226
+ - c067221: Put every baked gradient ramp in one texture, a row each, rather than a texture
227
+ each. `GradientRampAtlas` replaces `GradientRampCache`: a stop list is baked
228
+ once into a 256-texel strip and written to a row, and the fragment shader picks
229
+ its row with `u_rampV`. Every gradient in a frame now samples the same texture
230
+ unit, which is what a gradient needs before it can take a batch texture slot
231
+ the way a bitmap or a font atlas already does.
232
+
233
+ The atlas doubles from 16 rows and stops at 1024, recycling the least recently
234
+ used row past that. An animating gradient mints a new stop list every frame, so
235
+ the old cache grew a GL texture per frame and freed none of them; the cap is
236
+ what bounds that.
237
+
238
+ `PaintBindContext.bindRamp` now returns the `v` its ramp sits at. A registered
239
+ paint kind that samples the ramp at a constant `v` reads whatever gradient
240
+ happens to own that row, so it must sample at the returned value.
241
+ - 26d40bf: Fold gradients into the shared batch. A gradient fill used to bind its own
242
+ program and break the run of solid geometry, image quads and glyphs around it;
243
+ now it stages alongside them, so a page of gradient-filled shapes is one draw
244
+ rather than one per shape.
245
+
246
+ All three kinds go, and for the same reason — not that the ramp position is
247
+ affine in position, which is true only of a linear gradient, but that the
248
+ *coordinate* the ramp position is computed from is affine in all three. So a
249
+ vertex carries that and the rasterizer's interpolation across a triangle is
250
+ exact. A linear gradient's coordinate is the ramp position itself, which is why
251
+ it needs no paint mode of its own; a radial or conic one carries a
252
+ gradient-space point, with its atlas row where a plain vertex keeps its alpha,
253
+ and the shader takes a `length` or an `atan` of it behind a branch on the flat
254
+ paint mode. Fill opacity and group alpha ride the vertices the way a solid's
255
+ already did, and fills, stroke ribbons and glyph-outline meshes all take the
256
+ route.
257
+
258
+ Per-vertex-colored and even-odd fills still take their own draw, as do patterns
259
+ and shaders.
260
+
261
+ The ramp atlas is what makes the slot arithmetic work: every gradient in a run
262
+ shares one texture slot, so a document full of them costs the same one slot a
263
+ single gradient does. Growing the atlas moves every row, so a bake that would
264
+ grow it — or recycle a row — flushes the run first.
265
+
266
+ Gradient fills now apply the group's color matrix. `gradFill` was the only
267
+ paint program that did not, and since the batch program applies it to
268
+ everything in a run, leaving it out would have made a linear gradient and a
269
+ radial one under the same group paint differently.
270
+ - b8d2940: Layout you can watch, and push against.
271
+
272
+ `usePoseRun` is the transport: each frame it asks a producer for poses,
273
+ publishes them to the scene's ephemeral override channel — the one a drag
274
+ already writes to, which `effectivePose`, derived geometry and the pick source
275
+ read — and commits the lot as one batch when the producer says it is done or
276
+ the consumer stops it. Cancel drops the frames and the document is untouched.
277
+ It runs behind `useVisibleRaf`, and it knows nothing about layout.
278
+
279
+ A node carrying an override the run did not publish belongs to another gesture:
280
+ the run never writes it, never commits it, and reports it to the producer as
281
+ pinned. Dragging a box mid-run is therefore the consumer's ordinary move tool,
282
+ with no gesture contributed by the diagram package.
283
+
284
+ `useLiveLayout` in `@weasel-js/diagram` drives it. `force` relaxes one tick a
285
+ frame off the same force list the one-shot `force` runs, holding a pinned node
286
+ with `fx`/`fy` while its neighbors answer; `layered` and `tree` ease into a
287
+ target computed once. A node or edge appearing or disappearing re-heats the run.
288
+
289
+ `SceneNode.pickable: false` makes a node transparent to the hit-test walk, so a
290
+ press lands on what is behind it. Without it the innermost hit wins and dragging
291
+ a labeled box pulls the label out of the box.
292
+ - b5e2cd9: Solid geometry and image quads now share one batch, so a wall of thumbnails —
293
+ a ground rect under an atlas quad, per cell — draws in one call rather than
294
+ one per command. The two batches used to be exclusive: staging a solid drained
295
+ the image run and staging an image drained the solid one, so a shape that
296
+ batches perfectly in either half alone paid a flush per command. Solid vertices
297
+ carry the UV of a 1x1 white texel, which makes `texture() * a_vertexColor` the
298
+ vertex color exactly, so the merge is pixel-identical rather than close.
299
+
300
+ Measured over a viewport-filling grid of those cells on an M2 Max via ANGLE
301
+ (`tests/perf/atlas-wall.spec.ts`): 600 draw commands 2.83 -> 0.10 ms, 1,650
302
+ 11.37 -> 0.20, 5,400 40.50 -> 0.58, 15,000 126.15 -> 1.50. A run still breaks
303
+ on what a run cannot carry — a second bitmap, a different MAG_FILTER, a clip
304
+ depth, a color matrix.
305
+ - 89276ee: `usePoseRun` and `useSimulation` hand their injected clock straight to
306
+ `useVisibleRaf` instead of defaulting it themselves. The gate already falls
307
+ back to `requestAnimationFrame`, so both were defaulting it twice — and the
308
+ copy in `usePoseRun` was a bare `requestAnimationFrame` in kit source, which
309
+ `check:frame-loops` fails the build on. The allowlist is back down to the gate
310
+ itself.
311
+ - 36950d8: `scene.setDependsOn(id, dependsOn)` retargets a node's dependencies as one
312
+ undoable step, so dragging an existing edge's end onto a different node no
313
+ longer means removing the edge and adding another one. Switching a container
314
+ between an id list and `'children'` goes through the same call.
315
+
316
+ Both indices move with it — the reverse dependents index that drives cascade
317
+ delete, and the `'children'` set that drives the ancestor walk — and the
318
+ retargeted node's own memo is dropped alongside its dependents', since its
319
+ derivation now reads different sources. Order is significant, because a
320
+ derivation reads its dependencies positionally; declaring what a node already
321
+ declares records no history entry.
322
+ - 4f8c6b2: Text no longer breaks a batched run. Glyphs, the rules under underlined words,
323
+ and tessellated glyph outlines all stage into the same draw as the solid
324
+ geometry and image quads around them, so a wall of captioned thumbnails is one
325
+ draw where every label used to cost two.
326
+
327
+ The batch shader carries the glyph math behind a paint mode, which packs into
328
+ the texture-slot attribute the vertex already had, so the vertex does not grow
329
+ and a wall of thumbnails costs what it did before. It runs that math on every
330
+ fragment, glyph or not, because `fwidth` in non-uniform control flow is
331
+ undefined and the derivative has to be taken before anything selects on the
332
+ mode — priced at about 1.4% of a fragment that is not a glyph. A synthetic
333
+ oblique now shears on the CPU as the batch places its corners, rather than in a
334
+ vertex shader that read the baseline from a vertex attribute.
335
+
336
+ Three things a run used to break on are gone: a second text color in the same
337
+ paragraph, a decoration whose fill differs from the glyphs it sits under, and
338
+ the difference between a baked MSDF atlas and a runtime canvas bake. What still
339
+ breaks a run is a change of synthetic-bold threshold, which is a uniform — that
340
+ one is a fallback path, since a registered bold face never sets it.
341
+
342
+ **Breaking for anyone importing the text shader sources.** `TEXT_VERT_SRC`,
343
+ `TEXT_FRAG_SRC`, `TEXT_FRAG_R8_SRC`, `TEXT_SDF_UNIFORMS` and
344
+ `TEXT_SDF_ATTRIBUTES` are removed from `@weasel-js/font`: text has no program of
345
+ its own any more. What replaces them is `GLYPH_COVERAGE_GLSL`, the snippet a
346
+ program pastes in to turn an atlas sample into coverage, alongside
347
+ `GLYPH_MODE_MSDF` and `GLYPH_MODE_R8` naming the two channel layouts.
348
+ - 1240956: `kit:text` nodes take a `verticalAlign`, and picking follows it.
349
+
350
+ Centering a glyph in its box meant nudging `pose.y` by hand and re-deriving the
351
+ nudge whenever the font size changed. The painter forwarded the pose's height
352
+ but never an alignment, so the box the renderer aligned within was always
353
+ resolved as `'top'`.
354
+
355
+ `data.verticalAlign` — `'top' | 'center' | 'bottom'`, the same spelling the draw
356
+ command already took — now reaches both halves: the paint command and the
357
+ silhouette `textLineBoxes` builds, so a centered block is grabbable where it
358
+ draws rather than where a top-aligned one would have. A node that names none
359
+ paints exactly where it did before.
360
+ - Updated dependencies [4f8c6b2]
361
+ - @weasel-js/font@1.4.4
362
+ - @weasel-js/text@1.4.4
363
+ - @weasel-js/cursor@1.4.4
364
+ - @weasel-js/geom@1.4.4
365
+ - @weasel-js/gestures@1.4.4
366
+ - @weasel-js/history@1.4.4
367
+ - @weasel-js/modes@1.4.4
368
+ - @weasel-js/paint@1.4.4
369
+
3
370
  ## 1.4.3
4
371
 
5
372
  ### Patch Changes
@@ -1,6 +1,7 @@
1
- import { c as ResizeAnchor, R as ResizePose, B as BoundsConstraint, P as PointSnapBehavior, d as PointSnapFrame, M as ModifierState } from './types-BJqsTlXl.js';
2
- import { B as Bounds, P as PoseProjection } from './geometry-CHR36Ub_.js';
1
+ import { c as ResizeAnchor, R as ResizePose, B as BoundsConstraint, P as PointSnapBehavior, d as PointSnapFrame, M as ModifierState } from './types-ei3UMl9R.js';
2
+ import { B as Bounds, P as PoseProjection } from './geometry-6fCNhAux.js';
3
3
  import { D as DebugSink } from './types-BHK2dkMu.js';
4
+ import { P as Path } from './path-JEV2c5If.js';
4
5
 
5
6
  /** Corner resize-handle: world-space center plus the anchor that pins the opposite corner during resize. */
6
7
  interface CornerHandle {
@@ -137,4 +138,18 @@ declare function pointSnapToGrid<TPose extends ResizePose>(args: {
137
138
  bypassKey?: keyof ModifierState;
138
139
  }): PointSnapBehavior<TPose>;
139
140
 
140
- export { CORNER_ANCHORS as C, type UseResizeOptions as U, type CornerAnchor as a, type CornerHandle as b, cornerPoint as c, cornerResizeHandles as d, type CornerEdge as e, fixedCornerOf as f, hitCornerHandle as h, pointSnapToGrid as p };
141
+ /** True for Path-shaped poses (`{kind: 'polygon' | 'rect'}`). Useful for
142
+ * callers that need to fork between `pathPoseDescriptor` and
143
+ * `RECT_POSE_DESCRIPTOR` without forcing the consumer to wire `geometry`
144
+ * explicitly. */
145
+ declare function isPathLike(p: unknown): p is Path;
146
+ /** Per-call dispatch: if the pose looks like a Path, route to
147
+ * `pathPoseDescriptor`; otherwise treat as a plain rect pose. Avoids forcing
148
+ * demos with Path TPose to wire `geometry={pathPoseDescriptor}` explicitly.
149
+ * `getRotation` surfaces a `pose.rotation` field on non-Path poses so demos
150
+ * using rect-with-rotation shapes (e.g. `RotatedPose`) don't have to wire
151
+ * `geometry={ROTATED_POSE_DESCRIPTOR}` just to get rotated selection chrome
152
+ * and rotation-aware corner hit-tests. */
153
+ declare const AUTO_POSE_DESCRIPTOR: PoseProjection<unknown>;
154
+
155
+ export { AUTO_POSE_DESCRIPTOR as A, CORNER_ANCHORS as C, type UseResizeOptions as U, type CornerAnchor as a, type CornerHandle as b, cornerPoint as c, cornerResizeHandles as d, type CornerEdge as e, fixedCornerOf as f, hitCornerHandle as h, isPathLike as i, pointSnapToGrid as p };