@weasel-js/core 1.4.4 → 1.5.1

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 (58) hide show
  1. package/CHANGELOG.md +1295 -2197
  2. package/README.md +118 -75
  3. package/dist/autoPoseDescriptor-Dr6CwNZK.d.ts +26 -0
  4. package/dist/{chunk-WPM42WJP.js → chunk-3LPQ2XZG.js} +196 -406
  5. package/dist/chunk-3LPQ2XZG.js.map +1 -0
  6. package/dist/{chunk-R3AWPTLZ.js → chunk-GREL4MVO.js} +7593 -8533
  7. package/dist/chunk-GREL4MVO.js.map +1 -0
  8. package/dist/chunk-HAGOFNP5.js +162 -0
  9. package/dist/chunk-HAGOFNP5.js.map +1 -0
  10. package/dist/chunk-LXJDWBEL.js +318 -0
  11. package/dist/chunk-LXJDWBEL.js.map +1 -0
  12. package/dist/{chunk-2VXGHUVL.js → chunk-P6MGECVO.js} +4 -22
  13. package/dist/chunk-P6MGECVO.js.map +1 -0
  14. package/dist/{chunk-PRGBGMH3.js → chunk-XXQ6FLCJ.js} +3 -3
  15. package/dist/chunk-XXQ6FLCJ.js.map +1 -0
  16. package/dist/clipboard.d.ts +2 -3
  17. package/dist/clone.d.ts +3 -2
  18. package/dist/depSchema-BubKMv-2.d.ts +3479 -0
  19. package/dist/{grid-0Pbn5B2C.d.ts → grid-Z_Af3vTl.d.ts} +7 -10
  20. package/dist/index.d.ts +2588 -1556
  21. package/dist/index.js +6 -5
  22. package/dist/insert.d.ts +4 -4
  23. package/dist/insert.js +2 -1
  24. package/dist/insert.js.map +1 -1
  25. package/dist/math-E3rZn4bR.d.ts +282 -0
  26. package/dist/math.d.ts +5 -0
  27. package/dist/math.js +4 -0
  28. package/dist/math.js.map +1 -0
  29. package/dist/move.d.ts +5 -6
  30. package/dist/move.js +4 -6
  31. package/dist/move.js.map +1 -1
  32. package/dist/{options-DbYLImvq.d.ts → options-BPcmwYk7.d.ts} +3 -2
  33. package/dist/{autoPoseDescriptor-DF1SnnSx.d.ts → pointSnapToGrid-CgcK2R_I.d.ts} +11 -32
  34. package/dist/poseDescriptor-PgfVKfa0.d.ts +134 -0
  35. package/dist/renderer.d.ts +9 -4
  36. package/dist/renderer.js +6 -5
  37. package/dist/resize.d.ts +11 -12
  38. package/dist/resize.js +3 -2
  39. package/dist/routing.d.ts +1 -142
  40. package/dist/routing.js +1 -1
  41. package/dist/routing.js.map +1 -1
  42. package/dist/{types-ei3UMl9R.d.ts → types-BHGdrOcu.d.ts} +12 -41
  43. package/dist/{types-DEALFt5F.d.ts → types-BdaK9PcP.d.ts} +10 -4
  44. package/package.json +17 -10
  45. package/dist/DrawCommand-CD-ug3d9.d.ts +0 -332
  46. package/dist/builtins-BXFBXegF.d.ts +0 -840
  47. package/dist/chunk-2VXGHUVL.js.map +0 -1
  48. package/dist/chunk-BL65SHCX.js +0 -573
  49. package/dist/chunk-BL65SHCX.js.map +0 -1
  50. package/dist/chunk-PRGBGMH3.js.map +0 -1
  51. package/dist/chunk-R3AWPTLZ.js.map +0 -1
  52. package/dist/chunk-WPM42WJP.js.map +0 -1
  53. package/dist/geometry-6fCNhAux.d.ts +0 -114
  54. package/dist/path-JEV2c5If.d.ts +0 -48
  55. package/dist/registry-BY-wI9gm.d.ts +0 -4003
  56. package/dist/types-BHK2dkMu.d.ts +0 -172
  57. package/dist/types-bcc7jcUy.d.ts +0 -594
  58. package/dist/view-DSQgxBJB.d.ts +0 -63
package/README.md CHANGED
@@ -1,19 +1,16 @@
1
1
  # weasel
2
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.
3
+ A 2D scene-graph engine for React, rendered on WebGL2. Bring your own node data and pose shape; weasel owns the scene tree, the viewport math, pointer and keyboard input, layered rendering, and op-based undo/redo — plus a standard set of editing actions (select, move, resize, rotate, delete, duplicate, nudge, group, align, clipboard, …) bound to the keys and gestures an editor user expects.
4
4
 
5
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
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
7
  ## Features
10
8
 
11
- - Pointer gestures: move, resize, rotate, insert, clone, area-select, text edit
9
+ - Pointer interactions: move, resize, rotate, insert, clone, area-select, lasso, path-anchor editing, text edit
12
10
  - 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`)
11
+ - A registry of actions with default key and gesture bindings, each one overridable by id (`@experimental`)
15
12
  - Layered canvas rendering with debug overlays
16
- - Path poses, rect poses, rotated poses; first-class compound paths
13
+ - Rect, rotated and path poses; first-class compound paths; nested containers
17
14
  - Viewport with zoom/pan tools, momentum, and boundary clamping
18
15
  - Detached read-only scene views and navigation minimap (`<SceneViewCanvas>`, `<MinimapCanvas>`)
19
16
  - WebGL2 renderer with MSDF text, gradients, patterns, and per-vertex colors
@@ -29,65 +26,70 @@ npm install @weasel-js/core react
29
26
 
30
27
  ## How it fits together
31
28
 
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);
29
+ `useScene` holds the scene: a tree of leaf and container nodes, each carrying your data and a pose. Every change to it is an **op**, which is what makes it undoable. `<SceneCanvas>` renders a scene and routes input to it.
38
30
 
39
- // Drag-to-move with snapping, history, and parent reparenting:
40
- const move = useMove(adapter, {
41
- behaviors: [snap(gridSnapStrategy(20))],
42
- });
31
+ Interactions are **actions** — descriptors registered into an actions registry — reached by **gesture bindings**. A tool is a list of `{ spec, actionId }` pairs and nothing more; the kit's own select, shape and viewport tools are built that way.
43
32
 
44
- // Selection-aware delete with Backspace/Delete bound:
45
- useDelete(adapter, { bindKeyboard: true });
33
+ ```tsx
34
+ import { SceneCanvas, useScene, useSelection, gridSnapStrategy, type RectPose } from '@weasel-js/core';
35
+
36
+ const W = 800, H = 600;
37
+
38
+ export function Editor() {
39
+ const scene = useScene({ systemLayers: [{ id: 'default' }], initial: [] });
40
+ const selection = useSelection({ mode: 'multi' });
41
+
42
+ // Click to select, drag to move with grid snapping, handles to resize and
43
+ // rotate, the shape tools, and the standard keyboard actions (Cmd+A, Cmd+Z,
44
+ // Delete, …):
45
+ return (
46
+ <SceneCanvas
47
+ width={W}
48
+ height={H}
49
+ scene={scene}
50
+ selection={selection}
51
+ selectionMode="multi"
52
+ toolBundle="exhaustive"
53
+ selectTool={{ snap: gridSnapStrategy<RectPose>(20) }}
54
+ layers={{
55
+ grid: { spacing: 20, bounds: () => ({ x: 0, y: 0, width: W, height: H }) },
56
+ }}
57
+ />
58
+ );
59
+ }
46
60
  ```
47
61
 
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.
62
+ To change a behavior you override the descriptor by id, bind a different gesture to it, or register your own — you don't call a hook per interaction. [docs/hooks.md](https://github.com/orochi235/weasel/blob/main/docs/hooks.md) has the full action table.
52
63
 
53
- See the live demo for a full working example: <https://orochi235.github.io/weasel/>
64
+ Lower-level surfaces take a narrow **adapter** instead of a scene — a few methods that read your state and apply ops back — so they work over state weasel doesn't own. See [docs/adapters.md](https://github.com/orochi235/weasel/blob/main/docs/adapters.md).
54
65
 
55
66
  ## Demo
56
67
 
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.
68
+ <https://orochi235.github.io/weasel/> — every kit feature as a small runnable demo, plus the release history for every published version.
70
69
 
71
70
  ## Actions registry
72
71
 
73
- An `Action` is a named operation — `delete`, `duplicate`, `group`, `insert`, `viewport.dragPan` — paired with the input that triggers it. `<ActionsProvider>` holds the registered descriptors, and the gesture dispatcher matches live input against each one's `defaultBinding`. Keystrokes and pointer gestures take the same path, so a keyboard shortcut and a drag are two bindings on one action rather than two mechanisms.
72
+ An `Action` is a named operation — `delete`, `duplicate`, `group`, `insert`, `viewport.dragPan` — paired with the input that triggers it. `<ActionsProvider>` holds the registered descriptors, and the gesture dispatcher matches live input against each one's `defaultBinding` and the active tool's bindings. Keystrokes and pointer gestures take the same path, so a keyboard shortcut and a drag are two bindings on one action rather than two mechanisms.
74
73
 
75
- `<SceneCanvas>` auto-mounts a provider when none is above it and registers the kit-standard descriptors, derived from the scene, selection, view and history it already owns.
74
+ `<SceneCanvas>` mounts a provider when none is above it and registers the kit-standard actions: escape, select-all, delete, duplicate, group and ungroup, undo and redo, flip, nudge, reorder, align, distribute, the pathfinder booleans, path-anchor editing, fill and stroke, clipboard, and the pointer-driven move, resize, rotate, insert, clone, area-select and lasso.
76
75
 
77
76
  ```tsx
78
- import { SceneCanvas } from '@weasel-js/core';
79
-
80
77
  <SceneCanvas
78
+ width={W}
79
+ height={H}
81
80
  scene={scene}
82
81
  selection={selection}
83
82
  actions={{
84
- duplicate: null, // drop the default
85
- 'app.publish': { // add your own
83
+ duplicate: null, // drop the default
84
+ 'app.publish': { // add your own
86
85
  id: 'app.publish',
87
86
  label: 'Publish',
88
87
  defaultBinding: { kind: 'key', key: 'p', mods: { mod: true } },
89
88
  requires: ['selection'],
90
- invoker: { timing: 'immediate', run: ({ selection }) => publish(selection.get()) },
89
+ invoker: {
90
+ timing: 'immediate',
91
+ run: (deps) => publish((deps.selection as SelectionApi).get()),
92
+ },
91
93
  },
92
94
  }}
93
95
  />
@@ -95,53 +97,94 @@ import { SceneCanvas } from '@weasel-js/core';
95
97
 
96
98
  The `actions` prop takes `null` to unregister every default, or a record keyed by action id. Each value is `null` to drop that one id, a partial `Action` to merge onto the default of the same id, or a complete `Action` to register a new one.
97
99
 
98
- An action does its work through `invoker`, not a bare callback. `{ timing: 'immediate' }` runs once; `{ timing: 'ongoing' }` returns a handle so a drag can preview while it moves and commit at the end. The deps an invoker reads (`selection`, `scene`, `applyOps`, …) are declared in `requires` and resolved at invocation time, which is what lets a consumer swap one — see `useDepSource`.
100
+ An action does its work through `invoker`, not a bare callback. `{ timing: 'immediate' }` runs once; `{ timing: 'ongoing' }` returns a handle so a drag can preview while it moves and commit at the end. The deps an invoker reads (`selection`, `scene`, `applyOps`, …) are declared in `requires` and resolved at invocation time, which is what lets a consumer swap one — see `useDepSource`. To fire an action yourself, wrap the canvas in your own `<ActionsProvider>` and call `trigger(id, params)` on the registry `useActionsRegistry()` returns.
101
+
102
+ ## Text rendering
103
+
104
+ Text is rendered via MSDF atlases. Register fonts before the first paint:
105
+
106
+ ```tsx
107
+ import { registerFont } from '@weasel-js/core';
108
+
109
+ await registerFont('Inter', { weight: 400 }, '/fonts/Inter-400.json', '/fonts/Inter-400.png');
110
+ ```
111
+
112
+ Core doesn't ship a prebuilt atlas — bake one from a weasel checkout with `npm run gen:font -- <font.ttf> --name Inter-400 --out public/fonts` (see [`packages/font/scripts/gen-font.ts`](https://github.com/orochi235/weasel/blob/main/packages/font/scripts/gen-font.ts)) and serve the resulting `.json`/`.png` pair. `@weasel-js/hud` bundles its own Inter atlas for its widgets.
113
+
114
+ The glyph tier lives in `@weasel-js/font`; `registerFont` is re-exported from core, so the import above keeps working. An unregistered family renders in the default family with a one-time warning — see that package's README for `setFontFallbackPolicy`.
99
115
 
100
116
  ## Custom shaders (`@experimental`)
101
117
 
102
- 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:
118
+ A render layer can draw with its own fragment shader. Register the program once, then return a `kind: 'shader'` draw command with its uniforms and bounds:
103
119
 
104
120
  ```tsx
105
- import { registerProgram, registerTexture } from '@weasel-js/core';
106
-
107
- const voronoi = registerProgram(
108
- 'voronoi',
109
- /* vert */ null, // null → use the default quad prelude
110
- /* frag */ `
111
- precision highp float;
112
- varying vec2 v_uv;
113
- uniform float u_time;
114
- void main() { /* … */ }
115
- `,
116
- );
117
-
118
- // Inside a RenderLayer.draw, return a tree of DrawCommands:
119
- return {
121
+ import { registerProgram } from '@weasel-js/core/renderer';
122
+
123
+ const stripes = registerProgram('stripes', '', `#version 300 es
124
+ precision highp float;
125
+ in vec2 v_uv;
126
+ uniform float u_time;
127
+ out vec4 outColor;
128
+ void main() {
129
+ float v = 0.5 + 0.5 * sin(v_uv.x * 40.0 + u_time);
130
+ outColor = vec4(vec3(v), 1.0);
131
+ }`);
132
+
133
+ // Inside a RenderLayer's draw:
134
+ return [{
120
135
  kind: 'shader',
121
- program: voronoi,
136
+ program: stripes,
122
137
  uniforms: { u_time: performance.now() / 1000 },
123
138
  bounds: { x: 0, y: 0, w: 256, h: 256 },
124
- };
139
+ }];
125
140
  ```
126
141
 
127
- 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.
142
+ An empty vertex source selects the kit's vertex shader, which provides the `v_uv`, `v_screen` and `v_world` varyings and sets `u_bounds`, `u_view` and `u_proj` itself. Uniforms take a number, a 2–4 element tuple, a `Float32Array`, or a `TextureHandle` from `registerTexture`. Output **premultiplied** alpha — `vec4(rgb * a, a)` — or translucent pixels come out too bright. `bounds` is in CSS pixels.
128
143
 
129
144
  ## Subpath imports
130
145
 
131
- For tree-shaking and clarity, hook-specific helpers are scoped:
132
-
133
- ```ts
134
- import { snapToGrid } from '@weasel-js/core/move';
135
- import { snapToGrid, clampMinSize } from '@weasel-js/core/resize';
136
- import { snapToGrid } from '@weasel-js/core/insert';
137
- ```
146
+ Core's main entry carries the everyday surface. A few narrower ones have their own:
147
+
148
+ | Import | Holds |
149
+ |---|---|
150
+ | `@weasel-js/core/renderer` | the renderer, `registerProgram`, `registerTexture`, draw commands |
151
+ | `@weasel-js/core/move`, `/resize`, `/insert`, `/clone`, `/clipboard` | helpers for building on those actions, e.g. `snapToGrid`, `clampMinSize` |
152
+ | `@weasel-js/core/patterns-builtin` | the built-in fill patterns |
153
+ | `@weasel-js/core/routing` | route grammar and introspection: parsing, the route registry, conflict checks |
154
+
155
+ ## Packages
156
+
157
+ Every package is published under `@weasel-js` and released together at one version.
158
+
159
+ | Package | What it is |
160
+ |---|---|
161
+ | `core` | the scene graph, `<SceneCanvas>`, actions, tools, and the WebGL2 renderer |
162
+ | `geom` | pure 2D geometry — affine, box, curve, polyline; polygon booleans under `./booleans` |
163
+ | `gestures` | the gesture taxonomy, route grammars and matchers; no React, no DOM |
164
+ | `history` | undo/redo with scoped sub-histories; no React, no DOM |
165
+ | `paint` | fills, strokes, gradients and dashes as plain data |
166
+ | `text` | styled runs, kerned layout, wrapping and measurement |
167
+ | `bidi` | the Unicode Bidirectional Algorithm (UAX #9) |
168
+ | `font` | MSDF atlases, glyph metrics and runtime glyph rasterization |
169
+ | `svg` | SVG import and export |
170
+ | `modes` | app-level modality: capability tags, mode definitions, a mode registry |
171
+ | `diagram` | node-link diagrams: ports on any node's perimeter, flowchart-style bodies |
172
+ | `hud` | WebGL-rendered widgets composited into a canvas |
173
+ | `cursor` | tool cursors as authored glyphs, baked to CSS or painted when too large |
174
+ | `loupe` | a magnifier model: where it's aimed, how far it magnifies, what's under it |
175
+ | `audio` | a Web Audio engine: voices, buses, lookahead scheduling, spatialization; no weasel dependencies |
176
+ | `d3` | d3 data-join and transitions over `useScene` |
177
+ | `theme` | design tokens as CSS variables and a parallel TypeScript export |
178
+ | `ui` | React chrome components for weasel apps |
179
+ | `labkit` | React widgets for self-contained interactive lab pages |
138
180
 
139
181
  ## Documentation
140
182
 
141
- - [Concepts](./docs/concepts.md)
142
- - [Hooks](./docs/hooks.md)
143
- - [Adapters](./docs/adapters.md)
144
- - [Extending](./docs/extending.md)
183
+ - [Concepts](https://github.com/orochi235/weasel/blob/main/docs/concepts.md)
184
+ - [Hooks](https://github.com/orochi235/weasel/blob/main/docs/hooks.md)
185
+ - [Adapters](https://github.com/orochi235/weasel/blob/main/docs/adapters.md)
186
+ - [Extending](https://github.com/orochi235/weasel/blob/main/docs/extending.md)
187
+ - [Scene serialization](https://github.com/orochi235/weasel/blob/main/docs/scene-serialization.md)
145
188
 
146
189
  ## License
147
190
 
@@ -0,0 +1,26 @@
1
+ import { P as PoseDescriptor, a as Path } from './poseDescriptor-PgfVKfa0.js';
2
+
3
+ /** True for Path-shaped poses (`{kind: 'polygon' | 'rect'}`). Useful for
4
+ * callers that need to fork between `pathPoseDescriptor` and
5
+ * `RECT_POSE_DESCRIPTOR` without forcing the consumer to wire
6
+ * `poseDescriptor` explicitly. */
7
+ declare function isPathLike(p: unknown): p is Path;
8
+ /** True for a pose with numeric top-level `x`/`y`/`width`/`height` — the only
9
+ * shape the rect descriptor and the kit's built-in painters can read. */
10
+ declare function isRectPose(p: unknown): p is {
11
+ x: number;
12
+ y: number;
13
+ width: number;
14
+ height: number;
15
+ rotation?: number;
16
+ };
17
+ /** Per-call dispatch: if the pose looks like a Path, route to
18
+ * `pathPoseDescriptor`; otherwise treat as a plain rect pose. Avoids forcing
19
+ * demos with Path TPose to wire `poseDescriptor={pathPoseDescriptor}`
20
+ * explicitly. `getRotation` surfaces a `pose.rotation` field on non-Path poses
21
+ * so demos using rect-with-rotation shapes (e.g. `RotatedPose`) don't have to
22
+ * wire `poseDescriptor={ROTATED_POSE_DESCRIPTOR}` just to get rotated
23
+ * selection chrome and rotation-aware corner hit-tests. */
24
+ declare const AUTO_POSE_DESCRIPTOR: PoseDescriptor<unknown>;
25
+
26
+ export { AUTO_POSE_DESCRIPTOR as A, isRectPose as a, isPathLike as i };