@weasel-js/core 0.5.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 (74) hide show
  1. package/CHANGELOG.md +381 -0
  2. package/LICENSE +21 -0
  3. package/README.md +144 -0
  4. package/dist/DrawCommand-Dl0bXNfS.d.ts +500 -0
  5. package/dist/chunk-775XXAHR.js +216 -0
  6. package/dist/chunk-775XXAHR.js.map +1 -0
  7. package/dist/chunk-7V6JEOXE.js +27754 -0
  8. package/dist/chunk-7V6JEOXE.js.map +1 -0
  9. package/dist/chunk-AM6ARSPN.js +517 -0
  10. package/dist/chunk-AM6ARSPN.js.map +1 -0
  11. package/dist/chunk-BGGZ4CVF.js +248 -0
  12. package/dist/chunk-BGGZ4CVF.js.map +1 -0
  13. package/dist/chunk-BHVYVFGV.js +29 -0
  14. package/dist/chunk-BHVYVFGV.js.map +1 -0
  15. package/dist/chunk-CQNKCG34.js +831 -0
  16. package/dist/chunk-CQNKCG34.js.map +1 -0
  17. package/dist/chunk-GVCNT7UH.js +47 -0
  18. package/dist/chunk-GVCNT7UH.js.map +1 -0
  19. package/dist/chunk-PZ5AY32C.js +9 -0
  20. package/dist/chunk-PZ5AY32C.js.map +1 -0
  21. package/dist/chunk-UGFFCMQP.js +28 -0
  22. package/dist/chunk-UGFFCMQP.js.map +1 -0
  23. package/dist/chunk-VOVKONXA.js +103 -0
  24. package/dist/chunk-VOVKONXA.js.map +1 -0
  25. package/dist/chunk-Y52N27PF.js +39 -0
  26. package/dist/chunk-Y52N27PF.js.map +1 -0
  27. package/dist/clipboard.d.ts +91 -0
  28. package/dist/clipboard.js +6 -0
  29. package/dist/clipboard.js.map +1 -0
  30. package/dist/clone.d.ts +8 -0
  31. package/dist/clone.js +6 -0
  32. package/dist/clone.js.map +1 -0
  33. package/dist/fitViewToBounds-evGsnR8Q.d.ts +62 -0
  34. package/dist/grid-Cf87knjU.d.ts +153 -0
  35. package/dist/index-DZBYMsHI.d.ts +1555 -0
  36. package/dist/index.css +121 -0
  37. package/dist/index.css.map +1 -0
  38. package/dist/index.d.ts +10200 -0
  39. package/dist/index.js +13 -0
  40. package/dist/index.js.map +1 -0
  41. package/dist/insert.d.ts +33 -0
  42. package/dist/insert.js +81 -0
  43. package/dist/insert.js.map +1 -0
  44. package/dist/move.d.ts +69 -0
  45. package/dist/move.js +145 -0
  46. package/dist/move.js.map +1 -0
  47. package/dist/options-BPPBWMa7.d.ts +64 -0
  48. package/dist/patterns-builtin.d.ts +55 -0
  49. package/dist/patterns-builtin.js +100 -0
  50. package/dist/patterns-builtin.js.map +1 -0
  51. package/dist/pointSnapToGrid-D7s7QmOF.d.ts +185 -0
  52. package/dist/registerFont-CP-wCsrz.d.ts +109 -0
  53. package/dist/registerTexture-BzHTLhD9.d.ts +25 -0
  54. package/dist/renderer.css +121 -0
  55. package/dist/renderer.css.map +1 -0
  56. package/dist/renderer.d.ts +376 -0
  57. package/dist/renderer.js +13 -0
  58. package/dist/renderer.js.map +1 -0
  59. package/dist/resize.d.ts +78 -0
  60. package/dist/resize.js +5 -0
  61. package/dist/resize.js.map +1 -0
  62. package/dist/routing.css +35 -0
  63. package/dist/routing.css.map +1 -0
  64. package/dist/routing.d.ts +14 -0
  65. package/dist/routing.js +4 -0
  66. package/dist/routing.js.map +1 -0
  67. package/dist/types-B6MMiodD.d.ts +59 -0
  68. package/dist/types-BJ8_cyT7.d.ts +130 -0
  69. package/dist/types-B_-khFM0.d.ts +331 -0
  70. package/dist/types-BjUi2vA-.d.ts +355 -0
  71. package/dist/types-Cpb4hii1.d.ts +445 -0
  72. package/dist/types-D2tTKEU0.d.ts +18 -0
  73. package/dist/view-DSQgxBJB.d.ts +63 -0
  74. package/package.json +96 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,381 @@
1
+ # Changelog
2
+
3
+ ## 0.5.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 7e1982f: Publish the sub-packages. `@weasel-js/geom`, `/gestures`, `/history`, `/modes`,
8
+ `/svg`, `/d3`, `/theme`, `/ui`, and `/hud` are now real published packages
9
+ rather than source inlined into `@weasel-js/core`'s bundle.
10
+
11
+ For consumers of `@weasel-js/core` this is close to transparent — the public
12
+ API is unchanged and the sub-packages install as dependencies. It matters if
13
+ you were using any of those packages' types indirectly, or if you want to
14
+ depend on one alone: the geometry kernel (`@weasel-js/geom`) and the headless
15
+ undo engine (`@weasel-js/history`) are dependency-free and usable without the
16
+ React canvas.
17
+
18
+ The change also removes a latent duplicate-module hazard: while the packages
19
+ were inlined, a consumer holding both `@weasel-js/core` and one of them would
20
+ have gotten two copies of it.
21
+
22
+ `@weasel-js/ui` ships its styles as one bundled stylesheet — import
23
+ `@weasel-js/ui/style.css`. `@weasel-js/theme` exposes its tokens at
24
+ `@weasel-js/theme/tokens.css`.
25
+
26
+ ### Patch Changes
27
+
28
+ - @weasel-js/geom@0.5.0
29
+ - @weasel-js/gestures@0.5.0
30
+ - @weasel-js/history@0.5.0
31
+ - @weasel-js/modes@0.5.0
32
+
33
+ All notable changes to this project will be documented in this file.
34
+
35
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
36
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
37
+
38
+ ## [Unreleased]
39
+
40
+ ## 0.4.0 — 2026-06-15
41
+
42
+ ### Breaking changes
43
+
44
+ - **npm scope migrated `@orochi235/*` → `@weasel-js/*`.** The root package is
45
+ now `@weasel-js/core` (was `@orochi235/weasel`); the `weasel-` prefix was
46
+ dropped from the workspace sub-packages and their directories renamed. The
47
+ orochi235 GitHub URLs and author field are intentionally retained — this is
48
+ an npm-only move.
49
+ - **Membership-group apparatus removed.** The old `Group` record / `GroupAdapter`
50
+ are gone. `group` / `ungroup` now create and dissolve a structural
51
+ `ContainerNode` (`kind: 'container'`) and reparent the selection under it; the
52
+ container persists in the scene tree and round-trips to SVG `<g>`. A _saved
53
+ selection_ is just a consumer-held `string[]` passed to `selection.set` — it is
54
+ no longer a scene entity. See `docs/taxonomy.md` ("Group vs Selection").
55
+ - **`LayoutStrategy` renames:** `getChildPositions` → `childPoses`,
56
+ `reflowFor` → `reflowPoses`. Update implementations and call sites.
57
+
58
+ ### Added
59
+
60
+ - **Move behavior pipeline.** `useMove` runs an ordered `opts.behaviors`
61
+ pipeline over a scene-backed gesture adapter, so consumers can compose
62
+ move-time effects (snap, layout reflow, …) instead of forking the tool.
63
+ Threaded through the select binding via `move.behaviors`.
64
+ - **Drag-time layout reflow.** A move into a layout container now runs a
65
+ reflow pass that folds sibling poses into the preview channel during the
66
+ drag, and commits via the strategy's `commitDrop` plus source-reflow ops.
67
+ - **`Scene.loadState`** — in-place restore of a serialized snapshot (extracted
68
+ `applyConstructionSpecs` / `specsFromSerialized` for reuse).
69
+ - **`measureTextBounds`** — atlas-based text measurement against the MSDF font
70
+ registry.
71
+ - **labkit absorbed into the monorepo** as the `packages/labkit` workspace
72
+ package (published as `@lab-kit/react`), with its own CI wiring, smoke test,
73
+ and Storybook/Pages docs unification.
74
+ - Dispatcher dev tooling: a live dispatch-trace widget in ToolkitBuilder, and
75
+ `enableKeybindings` now also gates the dispatcher key channel.
76
+ - New `MoveSnapDemo` exercising container-snap + snap-back.
77
+
78
+ ### Build / tooling
79
+
80
+ - `.d.ts` emission via `rollup-plugin-dts` with the alias pipeline; tsup code
81
+ splitting enabled so entry points share a single font registry; the
82
+ `@weasel-js/*` sub-packages are bundled into `dist` for publish.
83
+ - CI actions bumped off Node 20 (checkout v6 / setup-node v6 / pages v5);
84
+ residual React `act()` warnings cleaned up at their call sites; visual
85
+ baselines re-captured from `ubuntu-22.04`.
86
+
87
+ ### Deprecated
88
+
89
+ - `Canvas` is now marked `@internal` / `@deprecated` and dropped from the
90
+ README. Use `<SceneCanvas>` over a `useScene()` tree instead. The export
91
+ is retained for this minor version and will be removed in the next.
92
+ Bare `<Canvas>` has no scene-mutation signal, so high-frequency repaints
93
+ must force React re-renders, which can wedge the tools machinery.
94
+
95
+ ## 0.3.0 — 2026-05-09
96
+
97
+ ### Added
98
+
99
+ - `Canvas` / `SceneCanvas` `shaders?: ShaderProgramHandle[]` prop — registers
100
+ custom shader programs on the underlying renderer at init time and on
101
+ prop change. Pairs with the experimental `registerProgram` /
102
+ `ShaderDrawCommand` API. Pass a stable (e.g. module-scope) array
103
+ reference; the prop is keyed by the joined handle ids so an inline literal
104
+ won't trigger recompiles each render.
105
+
106
+ ### Fixed
107
+
108
+ - `extractUniformNames` (the helper that pre-populates uniform locations for
109
+ custom shader programs) now expands array uniform declarations into per-slot
110
+ names. A shader declaring `uniform vec3 u_ripples[8];` previously produced
111
+ no entries — neither `u_ripples` nor `u_ripples[0]`...`u_ripples[7]`.
112
+ Consumer-side `ShaderDrawCommand.uniforms` keyed `'u_ripples[i]'` would
113
+ silently fail to bind. Array uniforms now resolve correctly per slot.
114
+
115
+ ## 0.2.0 — 2026-05-09
116
+
117
+ ### Breaking changes (final WebGL swap — Step 10)
118
+
119
+ - **2D backend removed.** `<Canvas>` and `<SceneCanvas>` no longer accept `backend?: '2d' | 'gl'`. WebGL2 is the only backend. The kit's existing `background`, `view`, `scene`, etc. props are unchanged; just the `backend` switch is gone.
120
+ - **`@weasel-js/gl` deleted as a separate package.** All renderer source folded into `@weasel-js/core`:
121
+ - GL machinery → `src/renderer/` (`WeaselRenderer`, `draw`, `state/`, `math/`, `cache/`, `shaders/`, `textures/`)
122
+ - Font atlasing → `src/features/text/atlas/` (`FontAtlas`, `GlyphLayout`, `registerFont`)
123
+ - Path tessellation → `src/features/paths/tessellate/` (`tessellate`, `polyline`, `stroke`)
124
+ - Font assets → `assets/fonts/inter/`
125
+ - **`RenderLayer` interface simplified** to a single required `draw(view, ...): DrawCommand[]`. The 2D `draw(ctx, ...)` and GL-suffixed `drawGL(...)` are gone.
126
+ - **Pair renames** for the same reason: `SceneSlotConfig.drawOneGL` → `drawOne`, `*Tool.drawGhostGL` → `drawGhost`, `createChildrenLayer.drawChildGL` → `drawChild`. The 2D originals are deleted.
127
+ - **`drawLayersGL` renamed to `drawLayers`** (Phase-B coda), and the 6 debug-overlay `emit*GL` helpers dropped their `GL` suffixes (file-private).
128
+ - **Deleted exports:** `applyPaint`, `applyStroke`, `renderFilledRegion`, `RenderFilledRegionOptions`, `setupCanvasDpr`, `useFixedPixelRatio`, `SetupCanvasDprOptions`, `LayerRenderer` (abstract base class), `traceToContext`, `dragGhost` (`createDragGhost`).
129
+ - **Pattern API rebuilt on `TextureHandle`.** `createTilePattern(opts)` now takes `{ size, draw }` (no `ctx`), renders the tile to an `OffscreenCanvas` internally, and returns a `TextureHandle | null` via `registerTexture`. Each built-in (`hatch`, `crosshatch`, `dots`, `chunks`) drops its `ctx` parameter and returns a `TextureHandle | null`. The `Paint` `'pattern'` variant's payload is now `TextureHandle` (not `CanvasPattern`).
130
+ - **`Paint`, `Stroke`, `Region`, `StrokeAlign`, `GradStop` types preserved.** They moved to `src/core/paint-types.ts` (the implementation file `paint.ts` is gone). Public surface re-exports are unchanged.
131
+ - **`PixelDensityDemo` retired.** Its only purpose was demonstrating the deleted DPR helpers.
132
+
133
+ ### Build / tooling
134
+
135
+ - `tsup.config.ts` `patterns-builtin` entry restored (after the C3 deletion + port).
136
+ - `package.json` `./patterns-builtin` export block restored.
137
+ - Vite `publicDir` updated to `assets/fonts`.
138
+ - Dropped weasel-gl-specific scripts: `test:smoke:step1`, `gen:font`, `bundlesize:weasel-gl`.
139
+
140
+ ### Migration notes (in-repo)
141
+
142
+ Demos and `apps/draw` were updated in this release. No external consumers exist. The TypeScript types and re-export paths above tell the whole story; no migration guide ships.
143
+
144
+ ### Added
145
+
146
+ #### `<SelectionContextProvider>` (`@experimental`)
147
+
148
+ - Ambient context publishing the active selection (`readonly string[]`) and an optional parallel `kinds` array so non-canvas UI (palette, status bar) can render type-aware copy ("3 paths selected"). `SceneCanvas` auto-publishes; consumers can override per-id labels via a `describeKind?: (node) => string` prop.
149
+ - New exports: `SelectionContextProvider`, `useSelectionContext`, `usePublishSelection`, `SelectionContextValue`.
150
+
151
+ #### Command palette extracted to `@weasel-js/ui`
152
+
153
+ - The demo's `<CommandPalette>` is now part of `packages/ui/` (alongside `<PropertiesPanel>`). Hooks (`useActionsRegistry`, `useAction`, `evaluateEnabled`, `ActionDisabledReason`) stay in the kit.
154
+ - The palette renders a kind-aware header ("1 path selected", "3 objects selected", "No selection") when `<SelectionContextProvider>` is in scope.
155
+
156
+ #### WebGL2 backend (carried over from the 0.1.x soak)
157
+
158
+ These items shipped as the `@experimental` GL backend during Steps 1–9; in 0.2.0 they're the only backend.
159
+
160
+ - New workspace package `@weasel-js/gl` housing the GL2 renderer.
161
+ - `WeaselRenderer` with WebGL2 context lifecycle, DPR-aware resize, and
162
+ context-loss/restore handling.
163
+ - `<Canvas>` / `<SceneCanvas>` accept `backend?: '2d' | 'gl'` (default `'2d'`).
164
+ Warn-once on post-mount backend change. The `background` prop is honored under
165
+ `backend='gl'`.
166
+ - Path tessellation via earcut for `nonzero` fills; stencil two-pass for
167
+ `evenodd`. WeakMap path-mesh cache; rect fast-path with shared VBO.
168
+ - Strokes: ribbon-mesh expansion with bevel/miter/round joins, butt/square/round
169
+ caps, miter limits, dash patterns, and full `StrokeAlign` (`center`/`inner`/
170
+ `outer`) — inner/outer via stencil clip on `PolygonPath` and
171
+ `alignedStrokeRect` on `RectPath`. New exports: `tessellateStroke`,
172
+ `extractPolylines`, `StrokeAlign`, `alignedStrokeRect`.
173
+ - Text via MSDF atlases. `registerFont(family, atlasUrl)` public API. Prebuilt
174
+ Inter v4 atlas ships with `weasel-gl/fonts/`. `pnpm gen:font` script wraps
175
+ `msdf-bmfont-xml` for custom atlases.
176
+ - Image, pattern, and gradient paints. New `Paint` variants:
177
+ `linear-gradient`, `radial-gradient`, `conic-gradient` with `GradStop[]`.
178
+ `GLImageCache` (WeakMap-keyed) and `GradientRampCache` (1×256 textures).
179
+ - Per-vertex colors: `vertexColors?: number[]` on `kind: 'path'` `DrawCommand`.
180
+ - Color matrix: `colorMatrix?` (4×5 row-major) on `kind: 'group'`. Composes
181
+ through nested groups via `compose4x5`. `IDENTITY_COLOR_MATRIX` export.
182
+ - Custom shader API (`@experimental`): `registerProgram(id, vert, frag)`,
183
+ `registerTexture(image)`, opaque `ShaderProgramHandle` / `TextureHandle`,
184
+ `kind: 'shader'` `DrawCommand` with uniform map (`number`, `vec2..4`, `mat3`,
185
+ `mat4`, `texture`). Auto-quad geometry over `bounds`; vertex prelude exposes
186
+ `v_uv` / `v_screen` / `v_world` varyings.
187
+ - `RenderLayer` gained additive `drawGL?` and `Dims`. `viewToMat3` helper for
188
+ layers translating `View` to GL transform. Eight built-in layers ported:
189
+ `createPathLayer`, `createTextLayer`, `createGridLayer`,
190
+ `createSelectionOverlayLayer`, `createCellHighlightLayer`,
191
+ `createChildrenLayer`, `createPenPreviewLayer`, `createDebugOverlayLayer`.
192
+ - `SceneSlotConfig.drawOneGL` to render scene content under `backend='gl'`.
193
+ - Tool overlays render under GL: `useSelectTool` / `useCloneTool` gained
194
+ `drawGhostGL` and `drawOneGL` options; drag-insert overlay renders under GL.
195
+ - Visual-regression rig (Playwright + pixelmatch) with per-pixel
196
+ `threshold: 0.1` and `< 2%` pass criterion. Per-demo specs (~24 demos);
197
+ pinned to `ubuntu-22.04`. Dedicated CI workflow (manual trigger). Demo
198
+ supports `?backend=` query string via `BackendContext`.
199
+ - Bundle-size CI gate fails on `weasel-gl` prod-bundle delta > 50 KB.
200
+
201
+ #### Actions registry (`@experimental`)
202
+
203
+ - `<ActionsProvider>` mounts a single keydown listener and dispatches to a
204
+ central registry. `useActionsRegistry()` and `useAction(action)` hooks.
205
+ - Default action factories: `defaultSelectAllAction`, `defaultEscapeAction`,
206
+ `defaultDuplicateAction`, `defaultNudgeActions` (8 bindings),
207
+ `defaultReorderActions` (2 bindings).
208
+ - `<SceneCanvas>` auto-mounts a provider when none exists upstream and
209
+ auto-registers the default action set, derived from `scene` / `selection` /
210
+ `adapter`. New `actions` prop accepts `null` (disable all), partial override
211
+ by id, or full `Action` descriptors for new ids.
212
+ - `useStandardActions(adapter, scene, selection)` registers the same default
213
+ set for bare-`<Canvas>` consumers.
214
+ - Public types: `Action`, `ActionEntry`, `ActionsProp`, `ActionsRegistry`,
215
+ `KeyBinding`.
216
+
217
+ #### Rotated resize
218
+
219
+ - `useResize` operates in the leaf's local frame when the pose carries
220
+ rotation. The drag delta is projected through `R(−θ)`, anchor math runs
221
+ in local frame, and the diagonally opposite world-space corner is pinned.
222
+ Bit-identical for unrotated leaves (rotation = 0 short-circuits to today's
223
+ path).
224
+ - New `ROTATED_POSE_DESCRIPTOR: PoseDescriptor<RotatedPose>` for the standard
225
+ rotated-rect case. `PoseDescriptor` gains optional `getRotation?(pose):
226
+ number` so consumer pose types can opt into the rotation-aware path.
227
+ - New `fixedCornerOf(bounds, anchor): {x, y}` helper exposed via the
228
+ `/resize` subpath barrel.
229
+ - Hit-test rotates handle positions to match the overlay's drawn handles —
230
+ pointer events on visible handles register correctly on rotated objects.
231
+ - Group resize with rotated children remains unsupported; dev-mode
232
+ `console.warn` fires once per gesture when any leaf has rotation ≠ 0.
233
+ - New demo: `demo/demos/RotatedResizeMathDemo.tsx` — three-panel math
234
+ explainer with two counterexample descriptors (no projection, no position
235
+ correction) plus live anchor-invariant ledger captions.
236
+
237
+ #### Other
238
+
239
+ - Plain scroll wheel zoom in `SceneCanvas.viewport`; trackpad pinch fix.
240
+ - `useHandTool` moved into the tool registry; `useKeybindings` wired through
241
+ `SceneCanvas`.
242
+ - Momentum animation gained bounds + stop-on-edge policy.
243
+ - `ViewportDemo` and animation-stress visual harness (100 drag cycles under
244
+ `backend='gl'`).
245
+ - Demo sidebar shows the weasel logo (transparent variant).
246
+ - npm scripts `test:changed` and `test:related` for fast inner loops.
247
+
248
+ ### Changed
249
+
250
+ - `RenderLayer` interface gained an additive `drawGL?` method (no breaking
251
+ change to the existing `draw`). Through step 9 both signatures coexist; the
252
+ step-10 final swap collapses to a single `draw` and removes the 2D path.
253
+ - `<SceneCanvas>` now auto-wires viewport tools and the default actions set
254
+ internally. Demos using `useSelectAll` / `useEscape` / `useDuplicate` /
255
+ `useNudge` / `useReorder` directly under `<SceneCanvas>` are now redundant
256
+ (the standalone hooks still work — they register into the auto-provider —
257
+ but can be deleted).
258
+ - Standalone action hooks (`useSelectAll`, etc.) register into the parent
259
+ `ActionsProvider` when present and fall back to direct `useKeybinding` when
260
+ not. Bare-`<Canvas>` behavior is unchanged.
261
+ - `SceneCanvas.tsx` split into focused submodules.
262
+ - 2D `applyPaint` / `applyStroke` handle the new gradient `Paint` variants by
263
+ falling back to opaque black; gradients render only under `backend='gl'`
264
+ in v1.
265
+ - `weasel-gl` tessellator now handles compound paths with multiple positive
266
+ contours; orphan opposite-wound contours are promoted to independent
267
+ positives. Uses first-contour winding as the reference, not signed-area sign.
268
+ - Rect-path GL caching keyed on dimensions, not `Path` identity, so equivalent
269
+ rects share meshes across nodes.
270
+
271
+ ### Fixed
272
+
273
+ - `useAnimator` now cleans up on unmount; tripwire test guards regressions.
274
+ - `animateOnSetPose` short-circuits when a tween is already in flight and
275
+ detects re-entry from any animator tick.
276
+ - WebGL: stop register-thrash with a `FinalizationRegistry` cleanup pass,
277
+ later disabled (use-after-free risk) in favor of transient + deferred-delete
278
+ pools that prevent GL buffer leaks.
279
+ - WebGL: rect fast-path uses a shared VBO to eliminate per-frame allocations.
280
+ - WebGL: premultiplied-alpha output paired with matching `blendFunc`; request
281
+ stencil buffer at context creation; canvas CSS size set on resize.
282
+ - WebGL: miter join now emits both apex extension and inner bevel half;
283
+ `splitForDash` honors closed polylines.
284
+ - Canvas action key dispatch test coverage added; tool-overrides-default and
285
+ same-id-collision semantics covered.
286
+ - BezierEditDemo: `pickEvery` uses AABB+slop so clicks hit the curve;
287
+ `applyOps` added to adapter so area-select wires; `hitTestArea` added so
288
+ drag-marquee selection works.
289
+ - Three TS errors blocking `prepublishOnly` resolved.
290
+ - `circle`-approximation command-stream lengths corrected in `layers`.
291
+ - Visual CI gates on manual trigger only (not every PR/push), avoiding noisy
292
+ cross-platform pixel diffs.
293
+
294
+ ### Deprecated
295
+
296
+ - The `backend='2d'` codepath is on a deprecation runway. Once the visual
297
+ soak completes, the default flips to `'gl'`; in a follow-up major release
298
+ the 2D path (`paint.ts`, `setupCanvasDpr`, `useFixedPixelRatio`, the
299
+ `RenderLayer.draw(ctx, …)` 2D signature) is removed and `weasel-gl` folds
300
+ back into `weasel`.
301
+
302
+ ## [Pre-Unreleased] — viewport, debug overlays, layout strategies
303
+
304
+ The following entries predate the WebGL transition but were never tagged.
305
+
306
+ ### Breaking
307
+
308
+ - `createReparentOp` arg names changed: `from` → `fromParentId`,
309
+ `to` → `toParentId`. Update call sites accordingly.
310
+ - `View` now includes `scale: number` (default 1). `viewToTransform` now
311
+ produces `{ panX: -view.x*scale, panY: -view.y*scale, zoom: scale }`.
312
+ - `RenderLayer.draw` signature is `(ctx, data, view) => void`. `runLayers`
313
+ accepts an optional `view` (defaults to identity) and wraps world-space
314
+ layers with `setTransform(scale, 0, 0, scale, -x*scale, -y*scale)`;
315
+ screen-space layers get an identity transform.
316
+ - `SceneSlotConfig.drawOne` (and `DefaultLayersScene.drawOne`) signature is
317
+ `(ctx, obj, pose, view) => void`.
318
+ - `handleHitRadius` is now interpreted in **screen pixels**: divided by
319
+ `view.scale` at each hit-test site so the hit area matches the rendered
320
+ handle size under zoom.
321
+ - `usePan` is removed. Use `useHandTool` for drag-pan and `useWheelPanTool`
322
+ for wheel-pan.
323
+
324
+ ### Added
325
+
326
+ - `LayoutStrategy<TPose>.contains?(containerPose, point)`: optional non-AABB
327
+ containment predicate. `useMove`'s layout-pass hit-test consults it when
328
+ present, falling back to an AABB check on the container's pose.
329
+ - `createReparentOp` defaults `coalesceKey` to `reparent:${id}` so successive
330
+ reparents of the same id batch-merge cleanly. Default `label` is
331
+ `'Reparent'`. Layout strategies can return reparent ops from `commitDrop`.
332
+ - `tileGrid({ cellToPose })`: optional callback to map a cell rect + the
333
+ dragged pose to the new pose. Default spreads `{x,y,width,height}` over
334
+ the dragged pose. The `tileGrid<TPose>` signature is no longer constrained
335
+ to `RectPose` — pass `cellToPose` whenever TPose doesn't carry rect fields.
336
+ - `useMove` layout-pass picks the top-most container in z-order when the
337
+ adapter implements `OrderedAdapter.getChildren`.
338
+ - Debug overlay subsystem: `?debug=…` URL gating + `<Canvas debug={...}>`
339
+ prop. Six features ship: `hitboxes`, `handles`, `bounds`, `origins`, `snap`,
340
+ `layers`. Sink threaded through `usePointerGestures`, `useResize`,
341
+ `useRotate`, `useAreaSelect`, `useEditAnchors`, `useSelectTool`, and
342
+ `gridSnapStrategy`.
343
+ - New exports: `parseDebugFlags`, `createDebugSink`,
344
+ `createDebugOverlayLayer`, `DEFAULT_DEBUG_THEME`; types `DebugConfig`,
345
+ `DebugSink`, `DebugFeature`, `DebugTheme`, `HitShape`, `HandleKind`.
346
+ - `zoomAt(view, anchor, factor, opts?)` pure primitive shared by every zoom
347
+ path.
348
+ - `useWheelZoomTool` (alwaysOn, claims wheel when `ctrlKey`/meta is held;
349
+ anchors zoom at cursor).
350
+ - `useWheelPanTool` (alwaysOn, claims plain wheel; translates view by
351
+ `delta / scale`).
352
+ - `useKeyboardZoomTool` (alwaysOn; `Cmd+=` / `Cmd+-` / `Cmd+0`; anchors at
353
+ canvas center).
354
+ - Selection overlays, insert overlay, and area-select overlay run in
355
+ `space: 'screen'` so chrome stays at fixed pixel size under zoom.
356
+ - `ZoomDemo` showcases the new tools and screen- vs world-pinned strokes.
357
+
358
+ ## [0.1.0] — 2026-05-03 — Pre-Scene milestone
359
+
360
+ Pinned ahead of the `useScene` redesign (see `docs/proposals/useScene.md`)
361
+ so the pre-Scene state is diffable. Highlights of the surface at this point:
362
+
363
+ - `<Canvas>` with explicit `adapter` prop, plus inline-props shorthand
364
+ (`items`/`setItems`/`toPose`/`fromPose`/`createDefault`/...) that synthesizes
365
+ an `arrayAdapter` for flat-list scenes.
366
+ - Move, resize, insert, area-select, rotate, clone, group (virtual + nested),
367
+ text-edit, and selection-driven action hooks (escape, select-all, duplicate,
368
+ nudge, delete, reorder, clipboard, undo/redo).
369
+ - Path poses as a first-class alternative to rect poses (`pathPoseDescriptor`,
370
+ `composePath`, `polygonFromPoints`, `PathBuilder`, `pointInPath`,
371
+ `traceToContext`).
372
+ - Text rendering with caret/selection theming, contenteditable in-place edit,
373
+ glyph-position hit testing, `fitTextPose` autosize helper.
374
+ - `RotatedPose` extension and `useRotate` gesture; rotation handle on selection
375
+ overlay.
376
+ - `UnitSystem` / `UnitValue` for customizable units.
377
+ - Grid overlay with cell-hover hook + highlight layer.
378
+ - Quadtree demo, compound-paths demo, bezier control-point editing demo.
379
+
380
+ Extracted from [garden](https://github.com/orochi235/garden)
381
+ (`src/canvas-kit/`) as a standalone package on 2026-05-01.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 orochi235
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,144 @@
1
+ # weasel
2
+
3
+ Domain-agnostic 2D scene-graph hooks for React, rendered on WebGL2. Bring your own object type and pose shape; weasel handles the viewport math, pointer gestures (move / resize / insert / clone / area-select / text edit), layered scene rendering, an op-based undo/redo model, and a stack of selection-driven action hooks (delete, duplicate, nudge, group, clipboard, undo/redo, …) wired to keyboard shortcuts when you ask.
4
+
5
+ Built for diagram editors, sketch tools, schematic editors, scene composers — anything where "objects on a canvas the user can grab, move, and arrange" is the substrate.
6
+
7
+ > Pre-1.0: the API surface (paths, nesting, per-subobject units) is still settling. Expect breaking changes between minor versions until 1.0.
8
+
9
+ ## Features
10
+
11
+ - Pointer gestures: move, resize, rotate, insert, clone, area-select, text edit
12
+ - Op-based scene mutation with undo/redo and coalescing
13
+ - Selection-driven action hooks (delete, duplicate, nudge, reorder, clipboard, …)
14
+ - Centralized actions registry with default keybindings (`@experimental`)
15
+ - Layered canvas rendering with debug overlays
16
+ - Path poses, rect poses, rotated poses; first-class compound paths
17
+ - Viewport with zoom/pan tools, momentum, and boundary clamping
18
+ - Detached read-only scene views and navigation minimap (`<SceneViewCanvas>`, `<MinimapCanvas>`)
19
+ - WebGL2 renderer with MSDF text, gradients, patterns, and per-vertex colors
20
+ - Custom fragment shaders via `registerProgram` (`@experimental`)
21
+
22
+ ## Install
23
+
24
+ ```sh
25
+ npm install @weasel-js/core react
26
+ ```
27
+
28
+ `react` is a peer dependency (>=18).
29
+
30
+ ## How it fits together
31
+
32
+ Every interaction takes a small, narrow **adapter** — a few methods that read the current scene and apply ops back. The kit doesn't own your scene; it asks. That keeps it agnostic to whether your scene lives in React state, Zustand, Redux, or a CRDT.
33
+
34
+ ```tsx
35
+ import { useMove, useDelete, createHistory, snap, gridSnapStrategy } from '@weasel-js/core';
36
+
37
+ const history = createHistory(adapter);
38
+
39
+ // Drag-to-move with snapping, history, and parent reparenting:
40
+ const move = useMove(adapter, {
41
+ behaviors: [snap(gridSnapStrategy(20))],
42
+ });
43
+
44
+ // Selection-aware delete with Backspace/Delete bound:
45
+ useDelete(adapter, { bindKeyboard: true });
46
+ ```
47
+
48
+ Most apps don't call the gesture hooks directly — `<SceneCanvas>` owns useMove /
49
+ useResize / useInsert / useAreaSelect / useSelection internally. Drop in a
50
+ `useScene()` tree and a `layers` map and you get click-to-select, drag-to-move,
51
+ corner-handle-resize, and an `tool="insert"` mode for free.
52
+
53
+ See the live demo for a full working example: <https://orochi235.github.io/weasel/>
54
+
55
+ ## Demo
56
+
57
+ Live demo: <https://orochi235.github.io/weasel/>
58
+
59
+ ## Text rendering
60
+
61
+ Text is rendered via MSDF atlases. Register fonts before the first paint:
62
+
63
+ ```tsx
64
+ import { registerFont } from '@weasel-js/core';
65
+
66
+ await registerFont('Inter', { weight: 400 }, '/fonts/Inter-400.json', '/fonts/Inter-400.png');
67
+ ```
68
+
69
+ Core doesn't ship a prebuilt atlas — bake one with `npm run gen:font -- <font.ttf> --name Inter-400 --out public/fonts` (see `scripts/gen-font.ts`) and serve the resulting `.json`/`.png` pair. The `hud` package ships its own bundled Inter atlas for consumers who don't need custom fonts.
70
+
71
+ ## Actions registry
72
+
73
+ `<ActionsProvider>` wires a single `keydown` listener and dispatches to a registry of `Action` descriptors. `<SceneCanvas>` auto-mounts a provider (if no parent provider exists) and registers default actions for select-all, escape, duplicate, nudge, and reorder, all derived from the scene/selection/adapter it already owns.
74
+
75
+ ```tsx
76
+ import { ActionsProvider, SceneCanvas } from '@weasel-js/core';
77
+
78
+ <ActionsProvider>
79
+ <SceneCanvas
80
+ scene={scene}
81
+ selection={selection}
82
+ actions={{
83
+ selectAll: null, // disable the default Cmd+A
84
+ copy: { // add an app-specific action
85
+ label: 'Copy',
86
+ defaultBinding: { key: 'c', mod: true },
87
+ run: () => clipboard.copy(selection.current),
88
+ },
89
+ }}
90
+ />
91
+ </ActionsProvider>
92
+ ```
93
+
94
+ The `actions` prop accepts `null` (disable all defaults), a partial override of any default by id, or a full `Action` descriptor for new ids. Consumers that need finer control can call individual hooks (`useSelectAll`, `useEscape`, `useDuplicate`, `useNudge`, `useReorder`) which auto-register into a parent provider when present and fall back to direct keybindings when not.
95
+
96
+ ## Custom shaders (`@experimental`)
97
+
98
+ The renderer supports `kind: 'shader'` `DrawCommand`s for layers that want a custom fragment shader. Register the program once, then emit a draw command with uniforms and bounds:
99
+
100
+ ```tsx
101
+ import { registerProgram, registerTexture } from '@weasel-js/core';
102
+
103
+ const voronoi = registerProgram(
104
+ 'voronoi',
105
+ /* vert */ null, // null → use the default quad prelude
106
+ /* frag */ `
107
+ precision highp float;
108
+ varying vec2 v_uv;
109
+ uniform float u_time;
110
+ void main() { /* … */ }
111
+ `,
112
+ );
113
+
114
+ // Inside a RenderLayer.draw, return a tree of DrawCommands:
115
+ return {
116
+ kind: 'shader',
117
+ program: voronoi,
118
+ uniforms: { u_time: performance.now() / 1000 },
119
+ bounds: { x: 0, y: 0, w: 256, h: 256 },
120
+ };
121
+ ```
122
+
123
+ Uniforms support `number`, `vec2..4`, `mat3`, `mat4`, and `TextureHandle` (from `registerTexture`). The vertex prelude exposes `v_uv`, `v_screen`, and `v_world` varyings. API may change before v2.
124
+
125
+ ## Subpath imports
126
+
127
+ For tree-shaking and clarity, hook-specific helpers are scoped:
128
+
129
+ ```ts
130
+ import { snapToGrid } from '@weasel-js/core/move';
131
+ import { snapToGrid, clampMinSize } from '@weasel-js/core/resize';
132
+ import { snapToGrid } from '@weasel-js/core/insert';
133
+ ```
134
+
135
+ ## Documentation
136
+
137
+ - [Concepts](./docs/concepts.md)
138
+ - [Hooks](./docs/hooks.md)
139
+ - [Adapters](./docs/adapters.md)
140
+ - [Extending](./docs/extending.md)
141
+
142
+ ## License
143
+
144
+ MIT.