@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.
- package/CHANGELOG.md +1295 -2197
- package/README.md +118 -75
- package/dist/autoPoseDescriptor-Dr6CwNZK.d.ts +26 -0
- package/dist/{chunk-WPM42WJP.js → chunk-3LPQ2XZG.js} +196 -406
- package/dist/chunk-3LPQ2XZG.js.map +1 -0
- package/dist/{chunk-R3AWPTLZ.js → chunk-GREL4MVO.js} +7593 -8533
- package/dist/chunk-GREL4MVO.js.map +1 -0
- package/dist/chunk-HAGOFNP5.js +162 -0
- package/dist/chunk-HAGOFNP5.js.map +1 -0
- package/dist/chunk-LXJDWBEL.js +318 -0
- package/dist/chunk-LXJDWBEL.js.map +1 -0
- package/dist/{chunk-2VXGHUVL.js → chunk-P6MGECVO.js} +4 -22
- package/dist/chunk-P6MGECVO.js.map +1 -0
- package/dist/{chunk-PRGBGMH3.js → chunk-XXQ6FLCJ.js} +3 -3
- package/dist/chunk-XXQ6FLCJ.js.map +1 -0
- package/dist/clipboard.d.ts +2 -3
- package/dist/clone.d.ts +3 -2
- package/dist/depSchema-BubKMv-2.d.ts +3479 -0
- package/dist/{grid-0Pbn5B2C.d.ts → grid-Z_Af3vTl.d.ts} +7 -10
- package/dist/index.d.ts +2588 -1556
- package/dist/index.js +6 -5
- package/dist/insert.d.ts +4 -4
- package/dist/insert.js +2 -1
- package/dist/insert.js.map +1 -1
- package/dist/math-E3rZn4bR.d.ts +282 -0
- package/dist/math.d.ts +5 -0
- package/dist/math.js +4 -0
- package/dist/math.js.map +1 -0
- package/dist/move.d.ts +5 -6
- package/dist/move.js +4 -6
- package/dist/move.js.map +1 -1
- package/dist/{options-DbYLImvq.d.ts → options-BPcmwYk7.d.ts} +3 -2
- package/dist/{autoPoseDescriptor-DF1SnnSx.d.ts → pointSnapToGrid-CgcK2R_I.d.ts} +11 -32
- package/dist/poseDescriptor-PgfVKfa0.d.ts +134 -0
- package/dist/renderer.d.ts +9 -4
- package/dist/renderer.js +6 -5
- package/dist/resize.d.ts +11 -12
- package/dist/resize.js +3 -2
- package/dist/routing.d.ts +1 -142
- package/dist/routing.js +1 -1
- package/dist/routing.js.map +1 -1
- package/dist/{types-ei3UMl9R.d.ts → types-BHGdrOcu.d.ts} +12 -41
- package/dist/{types-DEALFt5F.d.ts → types-BdaK9PcP.d.ts} +10 -4
- package/package.json +17 -10
- package/dist/DrawCommand-CD-ug3d9.d.ts +0 -332
- package/dist/builtins-BXFBXegF.d.ts +0 -840
- package/dist/chunk-2VXGHUVL.js.map +0 -1
- package/dist/chunk-BL65SHCX.js +0 -573
- package/dist/chunk-BL65SHCX.js.map +0 -1
- package/dist/chunk-PRGBGMH3.js.map +0 -1
- package/dist/chunk-R3AWPTLZ.js.map +0 -1
- package/dist/chunk-WPM42WJP.js.map +0 -1
- package/dist/geometry-6fCNhAux.d.ts +0 -114
- package/dist/path-JEV2c5If.d.ts +0 -48
- package/dist/registry-BY-wI9gm.d.ts +0 -4003
- package/dist/types-BHK2dkMu.d.ts +0 -172
- package/dist/types-bcc7jcUy.d.ts +0 -594
- package/dist/view-DSQgxBJB.d.ts +0 -63
package/README.md
CHANGED
|
@@ -1,19 +1,16 @@
|
|
|
1
1
|
# weasel
|
|
2
2
|
|
|
3
|
-
|
|
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
|
|
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
|
-
-
|
|
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
|
-
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
45
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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>`
|
|
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,
|
|
85
|
-
'app.publish': {
|
|
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: {
|
|
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
|
-
|
|
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
|
|
106
|
-
|
|
107
|
-
const
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
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:
|
|
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
|
-
|
|
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
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
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](
|
|
142
|
-
- [Hooks](
|
|
143
|
-
- [Adapters](
|
|
144
|
-
- [Extending](
|
|
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 };
|