@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.
- package/CHANGELOG.md +381 -0
- package/LICENSE +21 -0
- package/README.md +144 -0
- package/dist/DrawCommand-Dl0bXNfS.d.ts +500 -0
- package/dist/chunk-775XXAHR.js +216 -0
- package/dist/chunk-775XXAHR.js.map +1 -0
- package/dist/chunk-7V6JEOXE.js +27754 -0
- package/dist/chunk-7V6JEOXE.js.map +1 -0
- package/dist/chunk-AM6ARSPN.js +517 -0
- package/dist/chunk-AM6ARSPN.js.map +1 -0
- package/dist/chunk-BGGZ4CVF.js +248 -0
- package/dist/chunk-BGGZ4CVF.js.map +1 -0
- package/dist/chunk-BHVYVFGV.js +29 -0
- package/dist/chunk-BHVYVFGV.js.map +1 -0
- package/dist/chunk-CQNKCG34.js +831 -0
- package/dist/chunk-CQNKCG34.js.map +1 -0
- package/dist/chunk-GVCNT7UH.js +47 -0
- package/dist/chunk-GVCNT7UH.js.map +1 -0
- package/dist/chunk-PZ5AY32C.js +9 -0
- package/dist/chunk-PZ5AY32C.js.map +1 -0
- package/dist/chunk-UGFFCMQP.js +28 -0
- package/dist/chunk-UGFFCMQP.js.map +1 -0
- package/dist/chunk-VOVKONXA.js +103 -0
- package/dist/chunk-VOVKONXA.js.map +1 -0
- package/dist/chunk-Y52N27PF.js +39 -0
- package/dist/chunk-Y52N27PF.js.map +1 -0
- package/dist/clipboard.d.ts +91 -0
- package/dist/clipboard.js +6 -0
- package/dist/clipboard.js.map +1 -0
- package/dist/clone.d.ts +8 -0
- package/dist/clone.js +6 -0
- package/dist/clone.js.map +1 -0
- package/dist/fitViewToBounds-evGsnR8Q.d.ts +62 -0
- package/dist/grid-Cf87knjU.d.ts +153 -0
- package/dist/index-DZBYMsHI.d.ts +1555 -0
- package/dist/index.css +121 -0
- package/dist/index.css.map +1 -0
- package/dist/index.d.ts +10200 -0
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -0
- package/dist/insert.d.ts +33 -0
- package/dist/insert.js +81 -0
- package/dist/insert.js.map +1 -0
- package/dist/move.d.ts +69 -0
- package/dist/move.js +145 -0
- package/dist/move.js.map +1 -0
- package/dist/options-BPPBWMa7.d.ts +64 -0
- package/dist/patterns-builtin.d.ts +55 -0
- package/dist/patterns-builtin.js +100 -0
- package/dist/patterns-builtin.js.map +1 -0
- package/dist/pointSnapToGrid-D7s7QmOF.d.ts +185 -0
- package/dist/registerFont-CP-wCsrz.d.ts +109 -0
- package/dist/registerTexture-BzHTLhD9.d.ts +25 -0
- package/dist/renderer.css +121 -0
- package/dist/renderer.css.map +1 -0
- package/dist/renderer.d.ts +376 -0
- package/dist/renderer.js +13 -0
- package/dist/renderer.js.map +1 -0
- package/dist/resize.d.ts +78 -0
- package/dist/resize.js +5 -0
- package/dist/resize.js.map +1 -0
- package/dist/routing.css +35 -0
- package/dist/routing.css.map +1 -0
- package/dist/routing.d.ts +14 -0
- package/dist/routing.js +4 -0
- package/dist/routing.js.map +1 -0
- package/dist/types-B6MMiodD.d.ts +59 -0
- package/dist/types-BJ8_cyT7.d.ts +130 -0
- package/dist/types-B_-khFM0.d.ts +331 -0
- package/dist/types-BjUi2vA-.d.ts +355 -0
- package/dist/types-Cpb4hii1.d.ts +445 -0
- package/dist/types-D2tTKEU0.d.ts +18 -0
- package/dist/view-DSQgxBJB.d.ts +63 -0
- 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.
|