cavijs 0.0.0-stage → 0.1.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/LICENSE +27 -0
- package/README.md +379 -2
- package/dist-lib/cavijs.js +4051 -0
- package/dist-lib/cavijs.js.map +1 -0
- package/dist-lib/core.js +21 -0
- package/dist-lib/core.js.map +1 -0
- package/dist-lib/shared-CVyJHksc.js +856 -0
- package/dist-lib/shared-CVyJHksc.js.map +1 -0
- package/dist-lib/types/component/cables.d.ts +55 -0
- package/dist-lib/types/component/history.d.ts +121 -0
- package/dist-lib/types/component/interactionwc.d.ts +47 -0
- package/dist-lib/types/component/jack.d.ts +368 -0
- package/dist-lib/types/component/plug.d.ts +209 -0
- package/dist-lib/types/component/wirewc.d.ts +127 -0
- package/dist-lib/types/component/worldwc.d.ts +91 -0
- package/dist-lib/types/core/cavi.d.ts +307 -0
- package/dist-lib/types/core/coords.d.ts +99 -0
- package/dist-lib/types/core/events.d.ts +76 -0
- package/dist-lib/types/core/frame.d.ts +31 -0
- package/dist-lib/types/core/index.d.ts +19 -0
- package/dist-lib/types/core/node.d.ts +45 -0
- package/dist-lib/types/core/render-data.d.ts +57 -0
- package/dist-lib/types/core/types.d.ts +84 -0
- package/dist-lib/types/core/wasm.d.ts +23 -0
- package/dist-lib/types/core/wire.d.ts +194 -0
- package/dist-lib/types/core/world.d.ts +253 -0
- package/dist-lib/types/debug/debugwc.d.ts +115 -0
- package/dist-lib/types/debug/format.d.ts +11 -0
- package/dist-lib/types/debug/stats.d.ts +74 -0
- package/dist-lib/types/index.d.ts +40 -0
- package/dist-lib/types/interaction/interaction.d.ts +88 -0
- package/dist-lib/types/interaction/keyboard.d.ts +66 -0
- package/dist-lib/types/renderer/pointer.d.ts +34 -0
- package/dist-lib/types/renderer/renderer-svg.d.ts +104 -0
- package/dist-lib/types/renderer/renderer.d.ts +132 -0
- package/dist-lib/types/renderer/resize.d.ts +36 -0
- package/package.json +76 -4
package/LICENSE
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Teo Basili
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any
|
|
6
|
+
person obtaining a copy of this software and associated
|
|
7
|
+
documentation files (the "Software"), to deal in the
|
|
8
|
+
Software without restriction, including without
|
|
9
|
+
limitation the rights to use, copy, modify, merge,
|
|
10
|
+
publish, distribute, sublicense, and/or sell copies of
|
|
11
|
+
the Software, and to permit persons to whom the Software
|
|
12
|
+
is furnished to do so, subject to the following
|
|
13
|
+
conditions:
|
|
14
|
+
|
|
15
|
+
The above copyright notice and this permission notice
|
|
16
|
+
shall be included in all copies or substantial portions
|
|
17
|
+
of the Software.
|
|
18
|
+
|
|
19
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
|
20
|
+
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
|
21
|
+
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
|
22
|
+
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
|
|
23
|
+
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
|
24
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
25
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
|
26
|
+
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
27
|
+
DEALINGS IN THE SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,380 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Cavijs
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
cavijs is a wrapper written in typescript that allow to use cavi inside browser without handle directly with cavi.
|
|
4
|
+
|
|
5
|
+
The main purpose of cavi is to handle cable and mouse interaction, generating as a result spline point to be passed in js, js receive the point and renders wires.
|
|
6
|
+
|
|
7
|
+
The purpose is to avoid to store render information in the wasm project and let js to handle renderer and keep information about that, this because in that manner is possible to easly extend rendere behavior.
|
|
8
|
+
|
|
9
|
+
The TS project contain following classes:
|
|
10
|
+
|
|
11
|
+
- **Cavi** - Main entry point and facade
|
|
12
|
+
- **World** - Simulation container
|
|
13
|
+
- **Wire** - Individual wire representation
|
|
14
|
+
- **Node** - Individual node/point in a wire
|
|
15
|
+
- **Renderer** - Canvas-based rendering implementation
|
|
16
|
+
- **SvgRenderer** - SVG-based rendering implementation, drop-in alternative to Renderer
|
|
17
|
+
|
|
18
|
+
And these custom elements:
|
|
19
|
+
|
|
20
|
+
- **`<cavi-world>`** - a world declared in HTML: creates the `Cavi`, its canvas and renderer
|
|
21
|
+
- **`<cavi-jack>`** / **`<cavi-plug>`** - fixed connection points, and the cable ends that plug into them
|
|
22
|
+
- **`<cavi-wire>`** - a cable declared in HTML, with its two plugs
|
|
23
|
+
- **`<cavi-interaction>`** - attaches the interaction controllers: `StandardInteractionController` (mouse, touch) and `KeyboardInteractionController` (Tab, Space/Enter, Escape)
|
|
24
|
+
- **`<cavi-debug>`** - debug panel: statistics, event log, overlays
|
|
25
|
+
|
|
26
|
+
## Architecture
|
|
27
|
+
|
|
28
|
+
The architecture separates concerns:
|
|
29
|
+
|
|
30
|
+
- **Rust/WASM**: Physics simulation, collision detection, spline calculations
|
|
31
|
+
- **TypeScript**: Rendering, metadata, UI interaction, extensibility
|
|
32
|
+
|
|
33
|
+
## Classes
|
|
34
|
+
|
|
35
|
+
### Cavi
|
|
36
|
+
|
|
37
|
+
Main facade class that simplifies usage. Provides:
|
|
38
|
+
|
|
39
|
+
- WASM initialization
|
|
40
|
+
- Wire management through World wrapper
|
|
41
|
+
- Renderer integration
|
|
42
|
+
- Convenience methods for common operations
|
|
43
|
+
|
|
44
|
+
**Methods:**
|
|
45
|
+
|
|
46
|
+
```typescript
|
|
47
|
+
static initWasm(): Promise<void>
|
|
48
|
+
init(): Promise<void>
|
|
49
|
+
getWorld(): World
|
|
50
|
+
setRenderer(renderer: IRenderer): void
|
|
51
|
+
getRenderer(): IRenderer | null
|
|
52
|
+
addWire(x1, y1, x2, y2, nodes, tension, radius, type): Wire
|
|
53
|
+
deleteWire(index: number): void
|
|
54
|
+
clearAllWires(): void
|
|
55
|
+
setAcceleration(x: number, y: number): void
|
|
56
|
+
getAcceleration(): { x: number, y: number }
|
|
57
|
+
getWireByIndex(index: number): Wire | null
|
|
58
|
+
getWires(): Wire[]
|
|
59
|
+
update(): void
|
|
60
|
+
setMouse(x: number, y: number): void
|
|
61
|
+
render(): void
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### World
|
|
65
|
+
|
|
66
|
+
Main container for cavi simulation. Exposes all methods to interact with cables and global parameter configuration.
|
|
67
|
+
|
|
68
|
+
**Capabilities:**
|
|
69
|
+
|
|
70
|
+
- Add a new Wire
|
|
71
|
+
- Delete a Wire
|
|
72
|
+
- Set acceleration (gravity)
|
|
73
|
+
- Get acceleration
|
|
74
|
+
- Set renderer class
|
|
75
|
+
- Get wires by their index
|
|
76
|
+
|
|
77
|
+
**Methods:**
|
|
78
|
+
|
|
79
|
+
```typescript
|
|
80
|
+
addWire(x1, y1, x2, y2, nodes, tension, radius, renderType): Wire
|
|
81
|
+
deleteWire(index: number): void
|
|
82
|
+
clearAllWires(): void
|
|
83
|
+
setAcceleration(x: number, y: number): void
|
|
84
|
+
getAcceleration(): { x: number, y: number }
|
|
85
|
+
setRenderer(renderer: IRenderer): void
|
|
86
|
+
getRenderer(): IRenderer | null
|
|
87
|
+
getWireByIndex(index: number): Wire | null
|
|
88
|
+
getWires(): Wire[]
|
|
89
|
+
getWireCount(): number
|
|
90
|
+
update(): void
|
|
91
|
+
setMouse(x: number, y: number): void
|
|
92
|
+
setMouseRadius(radius: number): void
|
|
93
|
+
setFriction(friction: number): void
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
During render, the renderer class receives the wire instance with its context that allows rendering the wire with specific color passed in early initialization.
|
|
97
|
+
|
|
98
|
+
### Wire
|
|
99
|
+
|
|
100
|
+
Wire is the TypeScript class corresponding to Wire in cavi.
|
|
101
|
+
|
|
102
|
+
Wire is logically connected to the WASM Wire but contains methods to change properties like radius and tension. It also contains a meta dictionary that allows the user to extend with information useful for rendering, for example adding the color.
|
|
103
|
+
|
|
104
|
+
**Minimal implementation methods:**
|
|
105
|
+
|
|
106
|
+
- Add a node
|
|
107
|
+
- Remove a node
|
|
108
|
+
- Change and set meta data (for example render color)
|
|
109
|
+
- Change radius
|
|
110
|
+
|
|
111
|
+
**Methods:**
|
|
112
|
+
|
|
113
|
+
```typescript
|
|
114
|
+
addNode(x: number, y: number, fixed?: boolean): void
|
|
115
|
+
addNodeAt(index: number, x: number, y: number, fixed?: boolean): void
|
|
116
|
+
removeNode(index: number): void
|
|
117
|
+
getNodeCount(): number
|
|
118
|
+
setMetaData(key: string, value: any): void
|
|
119
|
+
getMetaData(key: string): any
|
|
120
|
+
getAllMetaData(): WireMeta
|
|
121
|
+
setColor(color: string): void
|
|
122
|
+
getColor(): string | undefined
|
|
123
|
+
setRadius(radius: number): void
|
|
124
|
+
getRadius(): number
|
|
125
|
+
getIndex(): number
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
**Metadata Example:**
|
|
129
|
+
|
|
130
|
+
```typescript
|
|
131
|
+
const wire = world.addWire(100, 100, 500, 100, 20, 10, 5, 1);
|
|
132
|
+
wire.setColor('#ff0000');
|
|
133
|
+
wire.setMetaData('thickness', 2);
|
|
134
|
+
wire.setMetaData('pattern', 'dashed');
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### Node
|
|
138
|
+
|
|
139
|
+
Node is the corresponding TypeScript class of Node in cavi. Represents a single point in a wire with position, velocity, and fixed state.
|
|
140
|
+
|
|
141
|
+
**Properties:**
|
|
142
|
+
|
|
143
|
+
```typescript
|
|
144
|
+
x: number (readonly, from WASM)
|
|
145
|
+
y: number (readonly, from WASM)
|
|
146
|
+
fixed: boolean (get/set)
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
**Methods:**
|
|
150
|
+
|
|
151
|
+
```typescript
|
|
152
|
+
setPosition(x: number, y: number): void
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### Renderer
|
|
156
|
+
|
|
157
|
+
Renderer class is responsible to render wires inside a `<canvas>`. In each render cycle it uses the WASM buffer and wire metadata instances to determine rendering properties like color.
|
|
158
|
+
|
|
159
|
+
**Constructor:**
|
|
160
|
+
|
|
161
|
+
```typescript
|
|
162
|
+
constructor(container: HTMLElement, world: World, options?: RendererOptions & { canvas?: HTMLCanvasElement })
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
Uses `options.canvas` (any canvas, anywhere on the page — its placement is measured every frame), else an existing `#wireCanvas` inside the host (`options.surface`, else `container`), creating one if there is none. `<cavi-world>` (`src/component/worldwc.ts`) creates the canvas and keeps it sized through its `IResizeController`. `options.surface` enables the pan/zoom-friendly mode described in [Pan/zoom and multiple worlds](#panzoom-and-multiple-worlds).
|
|
166
|
+
|
|
167
|
+
**Features:**
|
|
168
|
+
|
|
169
|
+
- Direct WASM memory access for efficient rendering (zero-copy)
|
|
170
|
+
- HiDPI: the canvas backing store follows `devicePixelRatio`
|
|
171
|
+
- Uses wire metadata for rendering colors (with fallback to default colors)
|
|
172
|
+
- Supports both segment and Bezier curve rendering
|
|
173
|
+
- Built-in animation loop with FPS tracking
|
|
174
|
+
- Mouse interaction visualization (pointer and mouse radii)
|
|
175
|
+
- Automatic physics updates in render cycle
|
|
176
|
+
|
|
177
|
+
**Methods:**
|
|
178
|
+
|
|
179
|
+
```typescript
|
|
180
|
+
render(): void // Starts the animation loop (idempotent, restartable after stop())
|
|
181
|
+
clear(): void // Clear the canvas
|
|
182
|
+
getFPS(): number // Get current frames per second
|
|
183
|
+
drawInteractionRadii(x: number, y: number): void // Draw mouse interaction zones
|
|
184
|
+
getSurface(): HTMLElement // options.surface, else the container
|
|
185
|
+
stop(): void // Cancel the loop, remove the pointer listener, clear the canvas
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
The renderer reads wire colors from metadata and automatically handles the render loop, physics updates, and visualization.
|
|
189
|
+
|
|
190
|
+
### SvgRenderer
|
|
191
|
+
|
|
192
|
+
`SvgRenderer` is a drop-in alternative to `Renderer`: it implements the same `IRenderer` interface and renders each wire as an SVG `<path>` (one pooled, updated-in-place element per wire) instead of stroking a `<canvas>`. Anywhere `Renderer` is used, `SvgRenderer` can be substituted without other code changes.
|
|
193
|
+
|
|
194
|
+
Unlike `Renderer`, it is **self-contained**: its constructor creates its own `<svg id="wireSvg">` inside the given container if one doesn't already exist, and manages its own sizing internally (an internal `ResizeObserver`, no `viewBox` — wire coordinates map 1:1 to SVG user-space pixels, just like the canvas backing store).
|
|
195
|
+
|
|
196
|
+
**Constructor:**
|
|
197
|
+
|
|
198
|
+
```typescript
|
|
199
|
+
constructor(container: HTMLElement, world: World, options?: RendererOptions)
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
`container` can be any plain `HTMLElement` (it does not need a pre-created `#wireSvg`, nor does it need to come from `<cavi-world>`).
|
|
203
|
+
|
|
204
|
+
**Features:**
|
|
205
|
+
|
|
206
|
+
- Same zero-copy WASM buffer reading and wire-color-fallback logic as `Renderer`
|
|
207
|
+
- Per-wire `<path>` pooling: DOM nodes are created/removed only when the wire count changes, never recreated every frame
|
|
208
|
+
- Segment and Bezier curve rendering via `M`/`L`/`C` path commands
|
|
209
|
+
- Debug overlay (physics nodes, mouse/pointer interaction radii) as SVG `<circle>`/`<text>` elements, toggled the same way as `Renderer`
|
|
210
|
+
- Dispatches a `cavi-resize` CustomEvent on `container` on every resize — same contract as `StandardResizeController` (`src/renderer/resize.ts`), so code that listens for `cavi-resize` (e.g. to reposition jacks, see `examples/patchbay-shared.ts`) works unchanged regardless of which renderer is active
|
|
211
|
+
|
|
212
|
+
**Methods:**
|
|
213
|
+
|
|
214
|
+
```typescript
|
|
215
|
+
render(): void // Main render method (includes animation loop)
|
|
216
|
+
getFPS(): number // Get current frames per second
|
|
217
|
+
setDebugDrawNodes(enabled: boolean): void
|
|
218
|
+
getDebugDrawNodes(): boolean
|
|
219
|
+
getContainer(): HTMLElement
|
|
220
|
+
stop(): void // Cancel the animation loop and disconnect the internal ResizeObserver
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
**Usage** (swap-in replacement for `Renderer`):
|
|
224
|
+
|
|
225
|
+
```typescript
|
|
226
|
+
import { Cavi, SvgRenderer } from 'cavijs';
|
|
227
|
+
|
|
228
|
+
await Cavi.initWasm();
|
|
229
|
+
const cavi = new Cavi();
|
|
230
|
+
|
|
231
|
+
const container = document.getElementById('wireArea') as HTMLElement;
|
|
232
|
+
const renderer = new SvgRenderer(container, cavi.getWorld());
|
|
233
|
+
cavi.setRenderer(renderer);
|
|
234
|
+
|
|
235
|
+
renderer.render();
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
## Usage Example
|
|
239
|
+
|
|
240
|
+
```typescript
|
|
241
|
+
import { Cavi, Renderer } from 'cavijs';
|
|
242
|
+
|
|
243
|
+
// Initialize WASM
|
|
244
|
+
await Cavi.initWasm();
|
|
245
|
+
|
|
246
|
+
// Create instance
|
|
247
|
+
const cavi = new Cavi();
|
|
248
|
+
|
|
249
|
+
// Setup renderer (pass the container holding a #wireCanvas element, and world)
|
|
250
|
+
const container = document.getElementById('container') as HTMLElement;
|
|
251
|
+
const renderer = new Renderer(container, cavi.getWorld());
|
|
252
|
+
cavi.setRenderer(renderer);
|
|
253
|
+
|
|
254
|
+
// Add wires with metadata for custom colors
|
|
255
|
+
const wire1 = cavi.addWire(100, 100, 500, 100, 20, 10, 5, 1);
|
|
256
|
+
wire1.setColor('#ff0000');
|
|
257
|
+
wire1.setMetaData('label', 'Cable A');
|
|
258
|
+
|
|
259
|
+
const wire2 = cavi.addWire(100, 200, 500, 200, 25, 15, 8, 1);
|
|
260
|
+
wire2.setColor('#00ff00');
|
|
261
|
+
|
|
262
|
+
// Configure physics
|
|
263
|
+
cavi.setAcceleration(0, 9.8);
|
|
264
|
+
|
|
265
|
+
// Start rendering (renderer handles animation loop internally)
|
|
266
|
+
renderer.render();
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
## Pan/zoom and multiple worlds
|
|
270
|
+
|
|
271
|
+
cavijs does not implement pan/zoom: the page transforms a wrapper around `<cavi-world>` (e.g. with d3-zoom) and registers the current transform with `setCoordinateTransform(() => ({ scale, translateX, translateY }))`, calling `notifyCoordinateTransformChanged()` after each step.
|
|
272
|
+
|
|
273
|
+
To keep cables visible and sharp at any zoom, give the world a **surface**, an untransformed element such as the frame the zoom listens on: `<cavi-world surface="#zoomFrame">`, or `new Renderer(container, world, { surface })` / `new SvgRenderer(container, world, { surface })`. The canvas/svg is then placed in and sized to that element, and the renderer applies the zoom itself, so cables are no longer clipped to the container's box when zooming out.
|
|
274
|
+
|
|
275
|
+
To draw into a canvas of your own — e.g. stacked above jacks and plugs — use `<cavi-world canvas="#myCanvas">` (or the `.canvas` property; `{ canvas }` / `{ svg }` for a hand-made renderer). It can sit anywhere on the page: its on-screen position is measured every frame.
|
|
276
|
+
|
|
277
|
+
Several `<cavi-world>`s can coexist on one page, also inside shadow roots: every element resolves its own world with `Cavi.for(element)`, `<cavi-plug jack="id">` looks the id up among its own world's jacks (so worlds can reuse ids), and each world has a single interaction controller with its own pointer and drag state — see `examples/demo-two-worlds.html`. `Cavi.shared` remains as a fallback for single-world setups.
|
|
278
|
+
|
|
279
|
+
Details: [doc/en/06-zoom-multiworld.md](doc/en/06-zoom-multiworld.md).
|
|
280
|
+
|
|
281
|
+
## Saving and restoring cables
|
|
282
|
+
|
|
283
|
+
`captureCables(cavi)` reads a world's cables as plain JSON-safe data — for each cable its `id`, the ids of the jacks its two ends are plugged into, and its attributes — and `applyCables(cavi, snapshot)` puts them back, touching only the cables that differ. Jacks are not saved: the page restores them first, with the same ids. `Plug.connect(jack)`/`disconnect()` plug and unplug from code, and `CaviWireElement.delete()` deletes a cable freeing its jacks right away.
|
|
284
|
+
|
|
285
|
+
`CaviHistory` builds undo/redo on top: a step at the end of every gesture on the cables (the `cavi-change` event) and whenever the page commits a change of its own, with the page's own state (jacks, modules) put back before the cables. `examples/demo-d3-interaction.html` has Ctrl/Cmd+Z, Ctrl/Cmd+Shift+Z and Save/Load.
|
|
286
|
+
|
|
287
|
+
Details: [doc/en/03-api.md](doc/en/03-api.md#saving-and-restoring-cables), [undo and redo](doc/en/03-api.md#undo-and-redo-cavihistory).
|
|
288
|
+
|
|
289
|
+
## `cavijs/core`: the simulation alone
|
|
290
|
+
|
|
291
|
+
`import { World, initWasm, forEachWireRender, svgPathData } from 'cavijs/core'` gets the engine without the page: no custom element registered, no DOM touched. `initWasm(source?)` loads the WASM once (from a URL of your choice if needed), a `World` runs from your own frame loop, `forEachWireRender`/`svgPathData` turn its cables into SVG path data, and `Wire.growTo`/`shrinkTo` with `nodeCountForDistance` keep a cable's length in step with its ends — the pieces to draw cavijs cables as another library's edges.
|
|
292
|
+
|
|
293
|
+
Details: [doc/en/03-api.md](doc/en/03-api.md#cavijscore-srccoreindexts).
|
|
294
|
+
|
|
295
|
+
## Debug overlay and events
|
|
296
|
+
|
|
297
|
+
Drop `<cavi-debug>` next to (or inside) a world for live stats — FPS, wires, nodes, jacks/plugs, physics and view parameters, a per-wire table — and a log of the events the world emits (`cavi-wire-add`, `cavi-plug-attach`, `cavi-cable-end`, …). `mode="floating"` is a draggable window, `mode="panel"` a panel docked to the right edge; `Alt+Shift+D` toggles it.
|
|
298
|
+
|
|
299
|
+
```html
|
|
300
|
+
<cavi-debug for="panel" open></cavi-debug>
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
Details: [doc/en/07-debug.md](doc/en/07-debug.md).
|
|
304
|
+
|
|
305
|
+
## Building
|
|
306
|
+
|
|
307
|
+
```bash
|
|
308
|
+
pnpm build # type-check + demo site (dist/, deployed to GitHub Pages)
|
|
309
|
+
pnpm build:lib # library: dist-lib/cavijs.js, dist-lib/core.js (cavijs/core) + dist-lib/types/*.d.ts
|
|
310
|
+
pnpm test # vitest (jsdom + the real cavi WASM module)
|
|
311
|
+
pnpm test:e2e # Playwright: the demos in Chrome, driven with a real mouse
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
The end-to-end tests (`e2e/`) start their own Vite server and use the locally installed Google Chrome, so no browser download is needed. They cover every demo loading and drawing, cable creation/relocation/drop with both interaction controllers, fixed-rate physics on emulated 120 Hz and 30 Hz displays, the held cable, the release throw, sleeping, a world removed and inserted back, and two worlds (one in a shadow root) with the same jack ids.
|
|
315
|
+
|
|
316
|
+
The WASM engine comes from the `cavi` package; after modifying its Rust code, rebuild it there with `wasm-pack build --target web`.
|
|
317
|
+
|
|
318
|
+
## Control panels
|
|
319
|
+
|
|
320
|
+
`<cavi-debug>` is the library's debug panel for any world on the page: live statistics, the event log, and toggles for the debug overlays (see `doc/en/07-debug.md`).
|
|
321
|
+
|
|
322
|
+
`demo-basic.html` and `demo-svg.html` have their own panel, `<cavi-controls>` (`examples/controls.ts`), with sliders for the physics and buttons to add or clear wires. It is example code: it drives the `Cavi` those demos build by hand, not any world.
|
|
323
|
+
|
|
324
|
+
## Development
|
|
325
|
+
|
|
326
|
+
The TypeScript wrapper is designed to be extended. Custom renderers can be created by implementing the `IRenderer` interface:
|
|
327
|
+
|
|
328
|
+
```typescript
|
|
329
|
+
interface IRenderer {
|
|
330
|
+
render(): void;
|
|
331
|
+
stop(): void;
|
|
332
|
+
setDebugDrawNodes(enabled: boolean): void;
|
|
333
|
+
getDebugDrawNodes(): boolean;
|
|
334
|
+
getContainer(): HTMLElement;
|
|
335
|
+
getSurface?(): HTMLElement; // optional, defaults to getContainer()
|
|
336
|
+
}
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
The default `Renderer` (canvas) and `SvgRenderer` (SVG) classes both provide complete implementations with:
|
|
340
|
+
|
|
341
|
+
- Efficient zero-copy WASM memory access
|
|
342
|
+
- Wire metadata support (colors and custom properties)
|
|
343
|
+
- Built-in animation loop and physics updates
|
|
344
|
+
- Mouse interaction visualization
|
|
345
|
+
- FPS tracking accessible via `getFPS()`
|
|
346
|
+
|
|
347
|
+
`SvgRenderer` (see above) is a working example of an alternative `IRenderer` implementation — a reasonable reference for anyone writing a third one (e.g. WebGL).
|
|
348
|
+
|
|
349
|
+
Wire metadata allows attaching arbitrary rendering information without modifying core WASM code. Colors and other properties set via wire metadata are automatically used during rendering.
|
|
350
|
+
|
|
351
|
+
## Project Layout
|
|
352
|
+
|
|
353
|
+
- `src/` — the library itself, nothing else. Organized by concern:
|
|
354
|
+
- `src/core/` — `Cavi`, `World`, `Wire`, `Node`, the WASM loader (`wasm.ts`), the render buffer reader (`render-data.ts`), and the shared `types.ts` (`IRenderer`, `IInteractionController`, `WireMeta`, ...); `index.ts` is the `cavijs/core` entry point
|
|
355
|
+
- `src/renderer/` — `Renderer` (canvas), `SvgRenderer` (SVG), `StandardResizeController`
|
|
356
|
+
- `src/interaction/` — `StandardInteractionController` (pointer), `KeyboardInteractionController` (keyboard)
|
|
357
|
+
- `src/component/` — the custom elements: `<cavi-jack>`, `<cavi-plug>`, `<cavi-wire>`, `<cavi-world>`, `<cavi-interaction>`
|
|
358
|
+
- `src/debug/` — `<cavi-debug>`, statistics and event formatting
|
|
359
|
+
- `src/index.ts` — the public entry point, re-exporting the above
|
|
360
|
+
- `src/tests/` — all `*.test.ts` files
|
|
361
|
+
- `examples/` — every demo page and its driving script, kept out of `src/` since none of it is library code. Vite's `root` points here (see `vite.config.ts`), so `npm run dev` serves `examples/index.html` at `/`.
|
|
362
|
+
|
|
363
|
+
## Demos
|
|
364
|
+
|
|
365
|
+
The `examples/demo-*.html` pages (built via `vite.config.ts`'s `build.rollupOptions.input`) show the library in different setups:
|
|
366
|
+
|
|
367
|
+
| Demo | Script | Renderer | Description |
|
|
368
|
+
| -------------------------------------- | ------------------------------------ | --------------------------------------- | --------------------------------------------------------------------------------------------------------------------- |
|
|
369
|
+
| `examples/demo-basic.html` | `examples/main.ts` | `Renderer` (canvas) | Minimal setup, a few static wires, with the demos' own control panel (`examples/controls.ts`) |
|
|
370
|
+
| `examples/demo-svg.html` | `examples/main-svg.ts` | `SvgRenderer` | Same minimal setup, using the SVG renderer |
|
|
371
|
+
| `examples/demo-jack-plug.html` | `examples/example2.ts` | `Renderer` (canvas, via `<cavi-world>`) | Hand-authored `<cavi-jack>`/`<cavi-wire>` markup |
|
|
372
|
+
| `examples/demo-noop-interaction.html` | `examples/exampleNoopInteraction.ts` | `Renderer` (canvas, via `<cavi-world>`) | Custom no-op `IInteractionController` example |
|
|
373
|
+
| `examples/demo-patchbay.html` | `examples/example3.ts` | `Renderer` (canvas, via `<cavi-world>`) | Full modular-synth patchbay: many jacks materialized from CSS layout, pre-patched cables, interactive drag-to-connect |
|
|
374
|
+
| `examples/demo-patchbay-svg.html` | `examples/example3-svg.ts` | `SvgRenderer` | Same patchbay, wired manually to `SvgRenderer` (shares its logic via `examples/patchbay-shared.ts`) |
|
|
375
|
+
| `examples/demo-patchbay-zoom.html` | `examples/demo-patchbay-zoom.ts` | `Renderer` (canvas, via `<cavi-world>`) | Full-screen d3-zoom pan/zoom with CSS-positioned jacks and a draggable module, using `surface` |
|
|
376
|
+
| `examples/demo-patchbay-zoom-svg.html` | `examples/demo-patchbay-zoom-svg.ts` | `SvgRenderer` | Same full-screen zoom demo on the SVG renderer |
|
|
377
|
+
| `examples/demo-d3-interaction.html` | `examples/demo-d3-interaction.ts` | `Renderer` (canvas, via `<cavi-world>`) | Custom `IInteractionController` built on d3-drag, several draggable modules under d3-zoom |
|
|
378
|
+
| `examples/demo-two-worlds.html` | `examples/demo-two-worlds.ts` | `Renderer` (canvas, via `<cavi-world>`) | Two worlds on one page, one inside a shadow root, with the same jack ids |
|
|
379
|
+
|
|
380
|
+
`examples/index.html` links every demo. Run `npm run dev` and open any of these pages to try them.
|