avoid-nodes-edge 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/README.md +501 -0
- package/dist/chunk-TO34Q3ID.cjs +87 -0
- package/dist/chunk-TO34Q3ID.cjs.map +1 -0
- package/dist/chunk-VPHZVUPR.js +87 -0
- package/dist/chunk-VPHZVUPR.js.map +1 -0
- package/dist/edge.cjs +235 -0
- package/dist/edge.cjs.map +1 -0
- package/dist/edge.d.cts +34 -0
- package/dist/edge.d.ts +34 -0
- package/dist/edge.js +235 -0
- package/dist/edge.js.map +1 -0
- package/dist/index.cjs +558 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +202 -0
- package/dist/index.d.ts +202 -0
- package/dist/index.js +558 -0
- package/dist/index.js.map +1 -0
- package/dist/workers/avoid-router.worker.js +348 -0
- package/dist/workers/avoid-router.worker.js.map +1 -0
- package/package.json +60 -0
package/README.md
ADDED
|
@@ -0,0 +1,501 @@
|
|
|
1
|
+
# avoid-nodes-edge
|
|
2
|
+
|
|
3
|
+
Orthogonal edge routing for [React Flow](https://reactflow.dev/) — edges automatically route around nodes using [libavoid-js](https://github.com/nicknisi/libavoid-js) (WASM). All WASM and routing computation runs exclusively in a **Web Worker**, keeping the main thread free and your UI smooth.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- Orthogonal (right-angle) edge routing that avoids overlapping nodes
|
|
8
|
+
- WASM routing engine runs entirely in a Web Worker — zero main-thread jank
|
|
9
|
+
- Incremental updates: dragging a node only re-routes affected edges
|
|
10
|
+
- Parallel edge support with automatic offset
|
|
11
|
+
- Configurable spacing, rounding, and grid snapping
|
|
12
|
+
- ER relationship labels (one-to-one, one-to-many, etc.)
|
|
13
|
+
- Fallback rendering (smooth-step/straight paths) while the worker loads
|
|
14
|
+
- Works with React Flow v12+
|
|
15
|
+
|
|
16
|
+
## Install
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npm install avoid-nodes-edge
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
yarn add avoid-nodes-edge
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
pnpm add avoid-nodes-edge
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### Peer Dependencies
|
|
31
|
+
|
|
32
|
+
| Package | Version |
|
|
33
|
+
|---|---|
|
|
34
|
+
| `react` | >= 18.0.0 |
|
|
35
|
+
| `react-dom` | >= 18.0.0 |
|
|
36
|
+
| `@xyflow/react` | >= 12.0.0 |
|
|
37
|
+
| `zustand` | >= 4.0.0 |
|
|
38
|
+
|
|
39
|
+
## Quick Start
|
|
40
|
+
|
|
41
|
+
### 1. Serve the WASM binary
|
|
42
|
+
|
|
43
|
+
The routing engine uses a WebAssembly binary from `libavoid-js`. Copy it to your `public/` directory so it's served at `/libavoid.wasm`:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
cp node_modules/libavoid-js/dist/libavoid.wasm public/libavoid.wasm
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Or automate it with a postinstall script in your `package.json`:
|
|
50
|
+
|
|
51
|
+
```json
|
|
52
|
+
{
|
|
53
|
+
"scripts": {
|
|
54
|
+
"postinstall": "node scripts/copy-libavoid-wasm.cjs"
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Create `scripts/copy-libavoid-wasm.cjs`:
|
|
60
|
+
|
|
61
|
+
```js
|
|
62
|
+
#!/usr/bin/env node
|
|
63
|
+
const fs = require("fs");
|
|
64
|
+
const path = require("path");
|
|
65
|
+
|
|
66
|
+
// Search both local and hoisted (monorepo) node_modules
|
|
67
|
+
const candidates = [
|
|
68
|
+
path.join(__dirname, "..", "node_modules", "libavoid-js", "dist", "libavoid.wasm"),
|
|
69
|
+
path.join(__dirname, "..", "..", "..", "node_modules", "libavoid-js", "dist", "libavoid.wasm"),
|
|
70
|
+
];
|
|
71
|
+
|
|
72
|
+
const src = candidates.find((p) => fs.existsSync(p));
|
|
73
|
+
const dest = path.join(__dirname, "..", "public", "libavoid.wasm");
|
|
74
|
+
|
|
75
|
+
if (!src) {
|
|
76
|
+
console.warn("[copy-libavoid-wasm] libavoid.wasm not found — run npm install first");
|
|
77
|
+
process.exit(0);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const publicDir = path.dirname(dest);
|
|
81
|
+
if (!fs.existsSync(publicDir)) {
|
|
82
|
+
fs.mkdirSync(publicDir, { recursive: true });
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
fs.copyFileSync(src, dest);
|
|
86
|
+
console.log("[copy-libavoid-wasm] Copied libavoid.wasm to public/");
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
This script handles both flat and hoisted `node_modules` layouts (npm workspaces, monorepos).
|
|
90
|
+
|
|
91
|
+
### 2. Configure your bundler
|
|
92
|
+
|
|
93
|
+
#### Vite
|
|
94
|
+
|
|
95
|
+
```ts
|
|
96
|
+
// vite.config.ts
|
|
97
|
+
import { defineConfig } from 'vite';
|
|
98
|
+
import react from '@vitejs/plugin-react';
|
|
99
|
+
|
|
100
|
+
export default defineConfig({
|
|
101
|
+
plugins: [react()],
|
|
102
|
+
worker: {
|
|
103
|
+
format: 'es',
|
|
104
|
+
},
|
|
105
|
+
optimizeDeps: {
|
|
106
|
+
exclude: ['avoid-nodes-edge'],
|
|
107
|
+
},
|
|
108
|
+
});
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
#### Next.js / Webpack
|
|
112
|
+
|
|
113
|
+
Web Workers with ES modules require additional webpack configuration. Ensure your bundler supports the `new URL(..., import.meta.url)` pattern for worker resolution.
|
|
114
|
+
|
|
115
|
+
### 3. Add to your React Flow app
|
|
116
|
+
|
|
117
|
+
```tsx
|
|
118
|
+
import { useState, useCallback } from 'react';
|
|
119
|
+
import {
|
|
120
|
+
ReactFlow,
|
|
121
|
+
ReactFlowProvider,
|
|
122
|
+
addEdge,
|
|
123
|
+
applyNodeChanges,
|
|
124
|
+
applyEdgeChanges,
|
|
125
|
+
type Node,
|
|
126
|
+
type Edge,
|
|
127
|
+
type NodeChange,
|
|
128
|
+
type EdgeChange,
|
|
129
|
+
type Connection,
|
|
130
|
+
} from '@xyflow/react';
|
|
131
|
+
import '@xyflow/react/dist/style.css';
|
|
132
|
+
|
|
133
|
+
import { AvoidNodesEdge } from 'avoid-nodes-edge/edge';
|
|
134
|
+
import { useAvoidNodesRouterFromWorker } from 'avoid-nodes-edge';
|
|
135
|
+
|
|
136
|
+
// Register the custom edge type
|
|
137
|
+
const edgeTypes = { avoidNodes: AvoidNodesEdge };
|
|
138
|
+
|
|
139
|
+
const initialNodes: Node[] = [
|
|
140
|
+
{ id: '1', position: { x: 0, y: 0 }, data: { label: 'Node 1' } },
|
|
141
|
+
{ id: '2', position: { x: 300, y: 0 }, data: { label: 'Node 2' } },
|
|
142
|
+
{ id: '3', position: { x: 150, y: 150 }, data: { label: 'Node 3' } },
|
|
143
|
+
];
|
|
144
|
+
|
|
145
|
+
const initialEdges: Edge[] = [
|
|
146
|
+
{ id: 'e1-2', source: '1', target: '2', type: 'avoidNodes' },
|
|
147
|
+
];
|
|
148
|
+
|
|
149
|
+
function Flow() {
|
|
150
|
+
const [nodes, setNodes] = useState<Node[]>(initialNodes);
|
|
151
|
+
const [edges, setEdges] = useState<Edge[]>(initialEdges);
|
|
152
|
+
|
|
153
|
+
// Set up the worker-based router
|
|
154
|
+
const { updateRoutingOnNodesChange, resetRouting } =
|
|
155
|
+
useAvoidNodesRouterFromWorker(nodes, edges, {
|
|
156
|
+
edgeToNodeSpacing: 12,
|
|
157
|
+
edgeToEdgeSpacing: 10,
|
|
158
|
+
edgeRounding: 8,
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
const onNodesChange = useCallback(
|
|
162
|
+
(changes: NodeChange<Node>[]) => {
|
|
163
|
+
setNodes((nds) => applyNodeChanges(changes, nds));
|
|
164
|
+
updateRoutingOnNodesChange(changes);
|
|
165
|
+
},
|
|
166
|
+
[updateRoutingOnNodesChange]
|
|
167
|
+
);
|
|
168
|
+
|
|
169
|
+
const onEdgesChange = useCallback(
|
|
170
|
+
(changes: EdgeChange<Edge>[]) => {
|
|
171
|
+
setEdges((eds) => applyEdgeChanges(changes, eds));
|
|
172
|
+
const needsReset = changes.some((c) => c.type === 'add' || c.type === 'remove');
|
|
173
|
+
if (needsReset) requestAnimationFrame(() => resetRouting());
|
|
174
|
+
},
|
|
175
|
+
[resetRouting]
|
|
176
|
+
);
|
|
177
|
+
|
|
178
|
+
const onConnect = useCallback(
|
|
179
|
+
(params: Connection) => {
|
|
180
|
+
setEdges((eds) => addEdge({ ...params, type: 'avoidNodes' }, eds));
|
|
181
|
+
requestAnimationFrame(() => resetRouting());
|
|
182
|
+
},
|
|
183
|
+
[resetRouting]
|
|
184
|
+
);
|
|
185
|
+
|
|
186
|
+
return (
|
|
187
|
+
<ReactFlow
|
|
188
|
+
nodes={nodes}
|
|
189
|
+
edges={edges}
|
|
190
|
+
onNodesChange={onNodesChange}
|
|
191
|
+
onEdgesChange={onEdgesChange}
|
|
192
|
+
onConnect={onConnect}
|
|
193
|
+
edgeTypes={edgeTypes}
|
|
194
|
+
defaultEdgeOptions={{ type: 'avoidNodes' }}
|
|
195
|
+
fitView
|
|
196
|
+
/>
|
|
197
|
+
);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
export default function App() {
|
|
201
|
+
return (
|
|
202
|
+
<ReactFlowProvider>
|
|
203
|
+
<Flow />
|
|
204
|
+
</ReactFlowProvider>
|
|
205
|
+
);
|
|
206
|
+
}
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
> **Important:** Edges must have `type: "avoidNodes"` to be processed by the router.
|
|
210
|
+
|
|
211
|
+
## API Reference
|
|
212
|
+
|
|
213
|
+
### `useAvoidNodesRouterFromWorker(nodes, edges, options?)`
|
|
214
|
+
|
|
215
|
+
The main hook. Manages the Web Worker lifecycle and routes edges around nodes.
|
|
216
|
+
|
|
217
|
+
```ts
|
|
218
|
+
import { useAvoidNodesRouterFromWorker } from 'avoid-nodes-edge';
|
|
219
|
+
|
|
220
|
+
const { updateRoutingOnNodesChange, resetRouting, refreshRouting, updateRoutingForNodeIds } =
|
|
221
|
+
useAvoidNodesRouterFromWorker(nodes, edges, options);
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
#### Options
|
|
225
|
+
|
|
226
|
+
| Option | Type | Default | Description |
|
|
227
|
+
|---|---|---|---|
|
|
228
|
+
| `edgeToNodeSpacing` | `number` | `8` | Buffer distance (px) between edges and node boundaries |
|
|
229
|
+
| `edgeToEdgeSpacing` | `number` | `10` | Distance (px) between parallel edge segments |
|
|
230
|
+
| `edgeRounding` | `number` | `0` | Corner radius (px) for rounded orthogonal bends |
|
|
231
|
+
| `diagramGridSize` | `number` | `0` | Snap edge waypoints to a grid of this size (0 = no grid) |
|
|
232
|
+
| `shouldSplitEdgesNearHandle` | `boolean` | `undefined` | Split edges near connection handles |
|
|
233
|
+
|
|
234
|
+
#### Return Value
|
|
235
|
+
|
|
236
|
+
| Property | Type | Description |
|
|
237
|
+
|---|---|---|
|
|
238
|
+
| `updateRoutingOnNodesChange` | `(changes: NodeChange[]) => void` | Call from `onNodesChange`. Handles incremental updates on drag/resize and full resets on add/remove. |
|
|
239
|
+
| `resetRouting` | `() => void` | Force a full re-route of all edges |
|
|
240
|
+
| `refreshRouting` | `() => void` | Re-route using current node/edge state |
|
|
241
|
+
| `updateRoutingForNodeIds` | `(nodeIds: string[]) => void` | Incrementally re-route edges for specific nodes |
|
|
242
|
+
|
|
243
|
+
---
|
|
244
|
+
|
|
245
|
+
### `AvoidNodesEdge`
|
|
246
|
+
|
|
247
|
+
Custom React Flow edge component that renders the routed path.
|
|
248
|
+
|
|
249
|
+
```tsx
|
|
250
|
+
import { AvoidNodesEdge } from 'avoid-nodes-edge/edge';
|
|
251
|
+
|
|
252
|
+
const edgeTypes = { avoidNodes: AvoidNodesEdge };
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
Falls back to a smooth-step or straight path while the worker is loading WASM. Once loaded, renders the computed orthogonal route.
|
|
256
|
+
|
|
257
|
+
#### Edge Data Properties
|
|
258
|
+
|
|
259
|
+
Customize individual edges via the `data` property:
|
|
260
|
+
|
|
261
|
+
```ts
|
|
262
|
+
const edges: Edge[] = [
|
|
263
|
+
{
|
|
264
|
+
id: 'e1-2',
|
|
265
|
+
source: '1',
|
|
266
|
+
target: '2',
|
|
267
|
+
type: 'avoidNodes',
|
|
268
|
+
data: {
|
|
269
|
+
label: 'connects to',
|
|
270
|
+
strokeColor: '#3b82f6',
|
|
271
|
+
strokeWidth: 2,
|
|
272
|
+
strokeDasharray: '5,5',
|
|
273
|
+
flowDirection: 'mono', // 'mono' | 'bi' | 'none'
|
|
274
|
+
erRelation: 'one-to-many', // ER relationship label
|
|
275
|
+
connectorType: 'default', // 'default' | 'straight' | 'smoothstep' | 'step'
|
|
276
|
+
},
|
|
277
|
+
},
|
|
278
|
+
];
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
| Data Property | Type | Default | Description |
|
|
282
|
+
|---|---|---|---|
|
|
283
|
+
| `label` | `string` | `""` | Text label displayed at the edge midpoint |
|
|
284
|
+
| `strokeColor` | `string` | `"#94a3b8"` | Edge stroke color |
|
|
285
|
+
| `strokeWidth` | `number` | `1.5` | Edge stroke width (px) |
|
|
286
|
+
| `strokeDasharray` | `string` | `undefined` | SVG dash pattern (e.g. `"5,5"`) |
|
|
287
|
+
| `flowDirection` | `"mono" \| "bi" \| "none"` | `"mono"` | Arrow direction: one-way, bidirectional, or none |
|
|
288
|
+
| `markerEnd` | `string` | `undefined` | Custom SVG marker at the end |
|
|
289
|
+
| `markerStart` | `string` | `undefined` | Custom SVG marker at the start |
|
|
290
|
+
| `erRelation` | `string` | `null` | ER relationship: `"one-to-one"`, `"one-to-many"`, `"many-to-one"`, `"many-to-many"` |
|
|
291
|
+
| `connectorType` | `string` | `"default"` | Path shape for parallel offsets |
|
|
292
|
+
|
|
293
|
+
---
|
|
294
|
+
|
|
295
|
+
### `useAvoidNodesPath(params)`
|
|
296
|
+
|
|
297
|
+
Low-level hook that reads the routed path for a single edge from the store. Used internally by `AvoidNodesEdge` — useful if you're building a custom edge component.
|
|
298
|
+
|
|
299
|
+
```ts
|
|
300
|
+
import { useAvoidNodesPath } from 'avoid-nodes-edge';
|
|
301
|
+
|
|
302
|
+
const [path, labelX, labelY, wasRouted] = useAvoidNodesPath({
|
|
303
|
+
id: 'edge-1',
|
|
304
|
+
sourceX: 100,
|
|
305
|
+
sourceY: 50,
|
|
306
|
+
targetX: 400,
|
|
307
|
+
targetY: 200,
|
|
308
|
+
sourcePosition: 'right',
|
|
309
|
+
targetPosition: 'left',
|
|
310
|
+
});
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
| Parameter | Type | Description |
|
|
314
|
+
|---|---|---|
|
|
315
|
+
| `id` | `string` | Edge ID to look up in the routes store |
|
|
316
|
+
| `sourceX/Y` | `number` | Source handle coordinates |
|
|
317
|
+
| `targetX/Y` | `number` | Target handle coordinates |
|
|
318
|
+
| `sourcePosition` | `"left" \| "right" \| "top" \| "bottom"` | Source handle side |
|
|
319
|
+
| `targetPosition` | `"left" \| "right" \| "top" \| "bottom"` | Target handle side |
|
|
320
|
+
| `borderRadius` | `number` | Fallback smooth-step border radius |
|
|
321
|
+
| `offset` | `number` | Fallback smooth-step offset |
|
|
322
|
+
|
|
323
|
+
Returns `[path, labelX, labelY, wasRouted]` — `wasRouted` is `false` while the worker is loading.
|
|
324
|
+
|
|
325
|
+
---
|
|
326
|
+
|
|
327
|
+
### `useAvoidRoutesStore`
|
|
328
|
+
|
|
329
|
+
Zustand store holding the computed routes from the worker.
|
|
330
|
+
|
|
331
|
+
```ts
|
|
332
|
+
import { useAvoidRoutesStore } from 'avoid-nodes-edge';
|
|
333
|
+
|
|
334
|
+
// Read routes
|
|
335
|
+
const routes = useAvoidRoutesStore((s) => s.routes);
|
|
336
|
+
const loaded = useAvoidRoutesStore((s) => s.loaded);
|
|
337
|
+
|
|
338
|
+
// Check a specific edge
|
|
339
|
+
const edgeRoute = useAvoidRoutesStore((s) => s.routes['edge-1']);
|
|
340
|
+
// => { path: "M 100 50 L 100 200 L 400 200", labelX: 250, labelY: 200 }
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
| Property | Type | Description |
|
|
344
|
+
|---|---|---|
|
|
345
|
+
| `routes` | `Record<string, AvoidRoute>` | Map of edge ID to `{ path, labelX, labelY }` |
|
|
346
|
+
| `loaded` | `boolean` | Whether the WASM worker has finished loading |
|
|
347
|
+
| `setRoutes` | `(routes) => void` | Update routes (called by worker listener) |
|
|
348
|
+
| `setLoaded` | `(loaded) => void` | Update loaded state |
|
|
349
|
+
|
|
350
|
+
---
|
|
351
|
+
|
|
352
|
+
### `useAvoidRouterActionsStore`
|
|
353
|
+
|
|
354
|
+
Zustand store holding imperative routing actions. Useful for triggering re-routes from outside the component that owns the router.
|
|
355
|
+
|
|
356
|
+
```ts
|
|
357
|
+
import { useAvoidRouterActionsStore } from 'avoid-nodes-edge';
|
|
358
|
+
|
|
359
|
+
const { resetRouting, updateRoutesForNodeId } =
|
|
360
|
+
useAvoidRouterActionsStore((s) => s.actions);
|
|
361
|
+
|
|
362
|
+
// Force full re-route
|
|
363
|
+
resetRouting();
|
|
364
|
+
|
|
365
|
+
// Re-route edges for a specific node
|
|
366
|
+
updateRoutesForNodeId('node-1');
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
---
|
|
370
|
+
|
|
371
|
+
### `useAvoidWorker(options?)`
|
|
372
|
+
|
|
373
|
+
Low-level hook that creates and manages the Web Worker. Used internally by `useAvoidNodesRouterFromWorker` — useful if you need direct control over the worker.
|
|
374
|
+
|
|
375
|
+
```ts
|
|
376
|
+
import { useAvoidWorker } from 'avoid-nodes-edge';
|
|
377
|
+
|
|
378
|
+
const { workerLoaded, post, close } = useAvoidWorker({
|
|
379
|
+
create: true,
|
|
380
|
+
onLoaded: (success) => console.log('WASM loaded:', success),
|
|
381
|
+
onRouted: (routes) => console.log('Routes computed:', routes),
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
// Send a command to the worker
|
|
385
|
+
post({ command: 'reset', nodes, edges, options });
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
---
|
|
389
|
+
|
|
390
|
+
### Constants
|
|
391
|
+
|
|
392
|
+
Configurable constants exported from the package:
|
|
393
|
+
|
|
394
|
+
```ts
|
|
395
|
+
import {
|
|
396
|
+
DEBOUNCE_ROUTING_MS, // 0 — debounce before routing (ms)
|
|
397
|
+
EDGE_BORDER_RADIUS, // 0 — default corner radius (px)
|
|
398
|
+
SHOULD_START_EDGE_AT_HANDLE_BORDER, // true
|
|
399
|
+
DEV_LOG_WEB_WORKER_MESSAGES, // false
|
|
400
|
+
} from 'avoid-nodes-edge';
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
---
|
|
404
|
+
|
|
405
|
+
### Types
|
|
406
|
+
|
|
407
|
+
All types are exported for TypeScript users:
|
|
408
|
+
|
|
409
|
+
```ts
|
|
410
|
+
import type {
|
|
411
|
+
// Router
|
|
412
|
+
AvoidRoute,
|
|
413
|
+
AvoidRouterOptions,
|
|
414
|
+
HandlePosition,
|
|
415
|
+
|
|
416
|
+
// Hooks
|
|
417
|
+
UseAvoidNodesRouterOptions,
|
|
418
|
+
UseAvoidNodesRouterResult,
|
|
419
|
+
UseAvoidWorkerOptions,
|
|
420
|
+
UseAvoidWorkerResult,
|
|
421
|
+
UseAvoidNodesPathParams,
|
|
422
|
+
Position,
|
|
423
|
+
|
|
424
|
+
// Stores
|
|
425
|
+
AvoidRoutesState,
|
|
426
|
+
AvoidRouterActions,
|
|
427
|
+
|
|
428
|
+
// Worker messages
|
|
429
|
+
AvoidRouterWorkerCommand,
|
|
430
|
+
AvoidRouterWorkerResponse,
|
|
431
|
+
} from 'avoid-nodes-edge';
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
## Architecture
|
|
435
|
+
|
|
436
|
+
```
|
|
437
|
+
Main Thread Worker Thread
|
|
438
|
+
─────────── ─────────────
|
|
439
|
+
avoid-router.worker.js
|
|
440
|
+
useAvoidNodesRouterFromWorker ─────────► loads libavoid WASM
|
|
441
|
+
posts commands: runs routeAll()
|
|
442
|
+
• reset (full graph) computes orthogonal paths
|
|
443
|
+
• updateNodes (incremental) ◄──────────────────────
|
|
444
|
+
• route (one-shot) posts { routed, routes }
|
|
445
|
+
|
|
446
|
+
useAvoidRoutesStore ◄──────────────────
|
|
447
|
+
stores routes map
|
|
448
|
+
|
|
449
|
+
AvoidNodesEdge
|
|
450
|
+
reads route from store
|
|
451
|
+
renders SVG path
|
|
452
|
+
```
|
|
453
|
+
|
|
454
|
+
### Why WASM never loads on the main thread
|
|
455
|
+
|
|
456
|
+
The `libavoid-js` WASM binary (~200KB) and the routing algorithm are computationally expensive. Loading and running them on the main thread would cause frame drops during initial load and every re-route. By running everything in a Web Worker:
|
|
457
|
+
|
|
458
|
+
- **Initial load**: WASM downloads and compiles in the background
|
|
459
|
+
- **Routing**: Heavy graph computation doesn't block React renders
|
|
460
|
+
- **Dragging**: Incremental updates keep the UI at 60fps
|
|
461
|
+
- **Fallback**: Edges render immediately as straight/smooth-step paths, then snap to routed paths once the worker responds
|
|
462
|
+
|
|
463
|
+
### Worker Message Protocol
|
|
464
|
+
|
|
465
|
+
| Command | Direction | Description |
|
|
466
|
+
|---|---|---|
|
|
467
|
+
| `reset` | Main -> Worker | Send full graph for re-routing |
|
|
468
|
+
| `updateNodes` | Main -> Worker | Send changed nodes for incremental routing |
|
|
469
|
+
| `change` | Main -> Worker | Update a single node or edge |
|
|
470
|
+
| `add` | Main -> Worker | Add a node or edge |
|
|
471
|
+
| `remove` | Main -> Worker | Remove a node or edge by ID |
|
|
472
|
+
| `route` | Main -> Worker | One-shot route (doesn't update internal state) |
|
|
473
|
+
| `close` | Main -> Worker | Shut down the worker |
|
|
474
|
+
| `loaded` | Worker -> Main | WASM load result (`{ success: boolean }`) |
|
|
475
|
+
| `routed` | Worker -> Main | Computed routes (`{ routes: Record<string, AvoidRoute> }`) |
|
|
476
|
+
|
|
477
|
+
## Troubleshooting
|
|
478
|
+
|
|
479
|
+
### Worker fails to load (MIME type error)
|
|
480
|
+
|
|
481
|
+
If you see `Failed to load module script: The server responded with a non-JavaScript MIME type`, make sure:
|
|
482
|
+
|
|
483
|
+
1. Your Vite config includes `worker: { format: 'es' }`
|
|
484
|
+
2. Your Vite config includes `optimizeDeps: { exclude: ['avoid-nodes-edge'] }`
|
|
485
|
+
3. Clear the Vite cache: `rm -rf node_modules/.vite`
|
|
486
|
+
|
|
487
|
+
### WASM not found
|
|
488
|
+
|
|
489
|
+
If the worker loads but WASM fails, ensure `libavoid.wasm` is served at `/libavoid.wasm` from your `public/` directory.
|
|
490
|
+
|
|
491
|
+
### Edges render as straight lines
|
|
492
|
+
|
|
493
|
+
This is the expected fallback while the worker loads WASM. Once loaded, edges will snap to routed paths. If they stay straight, check the browser console for worker errors.
|
|
494
|
+
|
|
495
|
+
### Edges don't route around a node
|
|
496
|
+
|
|
497
|
+
Make sure the node has `type` set to something other than `"group"`. Group nodes are treated as containers, not obstacles.
|
|
498
|
+
|
|
499
|
+
## License
|
|
500
|
+
|
|
501
|
+
MIT
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } }var __defProp = Object.defineProperty;
|
|
2
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
3
|
+
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
4
|
+
|
|
5
|
+
// src/store.ts
|
|
6
|
+
var _zustand = require('zustand');
|
|
7
|
+
var useAvoidRoutesStore = _zustand.create.call(void 0, (set) => ({
|
|
8
|
+
loaded: false,
|
|
9
|
+
routes: {},
|
|
10
|
+
setLoaded: (loaded) => set({ loaded }),
|
|
11
|
+
setRoutes: (routes) => set({ routes })
|
|
12
|
+
}));
|
|
13
|
+
var noop = () => {
|
|
14
|
+
};
|
|
15
|
+
var noopId = (_nodeId) => {
|
|
16
|
+
};
|
|
17
|
+
var useAvoidRouterActionsStore = _zustand.create.call(void 0, (set) => ({
|
|
18
|
+
actions: { resetRouting: noop, updateRoutesForNodeId: noopId },
|
|
19
|
+
setActions: (actions) => set({ actions })
|
|
20
|
+
}));
|
|
21
|
+
|
|
22
|
+
// src/constants.ts
|
|
23
|
+
var DEV_LOG_WEB_WORKER_MESSAGES = false;
|
|
24
|
+
var DEBOUNCE_ROUTING_MS = 0;
|
|
25
|
+
var SHOULD_START_EDGE_AT_HANDLE_BORDER = true;
|
|
26
|
+
var EDGE_BORDER_RADIUS = 0;
|
|
27
|
+
|
|
28
|
+
// src/useAvoidNodesPath.ts
|
|
29
|
+
var _react = require('react');
|
|
30
|
+
var _react3 = require('@xyflow/react');
|
|
31
|
+
var RF_POS = {
|
|
32
|
+
left: "left",
|
|
33
|
+
right: "right",
|
|
34
|
+
top: "top",
|
|
35
|
+
bottom: "bottom"
|
|
36
|
+
};
|
|
37
|
+
function useAvoidNodesPath(params) {
|
|
38
|
+
const {
|
|
39
|
+
id,
|
|
40
|
+
sourceX,
|
|
41
|
+
sourceY,
|
|
42
|
+
targetX,
|
|
43
|
+
targetY,
|
|
44
|
+
sourcePosition,
|
|
45
|
+
targetPosition,
|
|
46
|
+
offset
|
|
47
|
+
} = params;
|
|
48
|
+
const loaded = useAvoidRoutesStore((s) => s.loaded);
|
|
49
|
+
const route = useAvoidRoutesStore((s) => s.routes[id]);
|
|
50
|
+
return _react.useMemo.call(void 0, () => {
|
|
51
|
+
if (loaded && route) {
|
|
52
|
+
return [route.path, route.labelX, route.labelY, true];
|
|
53
|
+
}
|
|
54
|
+
if (sourcePosition && targetPosition) {
|
|
55
|
+
const [smoothPath, sLabelX, sLabelY] = _react3.getSmoothStepPath.call(void 0, {
|
|
56
|
+
sourceX,
|
|
57
|
+
sourceY,
|
|
58
|
+
targetX,
|
|
59
|
+
targetY,
|
|
60
|
+
sourcePosition: RF_POS[sourcePosition],
|
|
61
|
+
targetPosition: RF_POS[targetPosition],
|
|
62
|
+
borderRadius: _nullishCoalesce(params.borderRadius, () => ( EDGE_BORDER_RADIUS)),
|
|
63
|
+
offset: _nullishCoalesce(offset, () => ( 20))
|
|
64
|
+
});
|
|
65
|
+
return [smoothPath, sLabelX, sLabelY, false];
|
|
66
|
+
}
|
|
67
|
+
const [straightPath, labelX, labelY] = _react3.getStraightPath.call(void 0, {
|
|
68
|
+
sourceX,
|
|
69
|
+
sourceY,
|
|
70
|
+
targetX,
|
|
71
|
+
targetY
|
|
72
|
+
});
|
|
73
|
+
return [straightPath, labelX, labelY, false];
|
|
74
|
+
}, [loaded, route, sourceX, sourceY, targetX, targetY, sourcePosition, targetPosition, offset, params.borderRadius]);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
exports.__publicField = __publicField; exports.useAvoidRoutesStore = useAvoidRoutesStore; exports.useAvoidRouterActionsStore = useAvoidRouterActionsStore; exports.DEV_LOG_WEB_WORKER_MESSAGES = DEV_LOG_WEB_WORKER_MESSAGES; exports.DEBOUNCE_ROUTING_MS = DEBOUNCE_ROUTING_MS; exports.SHOULD_START_EDGE_AT_HANDLE_BORDER = SHOULD_START_EDGE_AT_HANDLE_BORDER; exports.EDGE_BORDER_RADIUS = EDGE_BORDER_RADIUS; exports.useAvoidNodesPath = useAvoidNodesPath;
|
|
87
|
+
//# sourceMappingURL=chunk-TO34Q3ID.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["/Users/awaisshah228/Documents/coding/ai-diagram-generator/avoid-nodes-pro-example/packages/avoid-nodes-edge/dist/chunk-TO34Q3ID.cjs","../src/store.ts","../src/constants.ts","../src/useAvoidNodesPath.ts"],"names":[],"mappings":"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","file":"/Users/awaisshah228/Documents/coding/ai-diagram-generator/avoid-nodes-pro-example/packages/avoid-nodes-edge/dist/chunk-TO34Q3ID.cjs","sourcesContent":[null,"import { create } from \"zustand\";\nimport type { AvoidRoute } from \"./router\";\n\nexport interface AvoidRoutesState {\n loaded: boolean;\n routes: Record<string, AvoidRoute>;\n setLoaded: (loaded: boolean) => void;\n setRoutes: (routes: Record<string, AvoidRoute>) => void;\n}\n\nexport const useAvoidRoutesStore = create<AvoidRoutesState>((set) => ({\n loaded: false,\n routes: {},\n setLoaded: (loaded) => set({ loaded }),\n setRoutes: (routes) => set({ routes }),\n}));\n\nexport interface AvoidRouterActions {\n resetRouting: () => void;\n updateRoutesForNodeId: (nodeId: string) => void;\n}\n\nconst noop = () => {};\nconst noopId = (_nodeId: string) => {};\n\nexport const useAvoidRouterActionsStore = create<{\n actions: AvoidRouterActions;\n setActions: (a: AvoidRouterActions) => void;\n}>((set) => ({\n actions: { resetRouting: noop, updateRoutesForNodeId: noopId },\n setActions: (actions) => set({ actions }),\n}));\n","/** Log web worker / routing messages in development. */\nexport const DEV_LOG_WEB_WORKER_MESSAGES = false;\n\n/**\n * Debounce (ms) before routing runs after diagram changes.\n * 10 ms merges drag events into a single update.\n * For larger diagrams (500+ nodes) use ~50-100 ms.\n */\nexport const DEBOUNCE_ROUTING_MS = 0;\n\n/** Whether edges start at handle border (true) or center (false). */\nexport const SHOULD_START_EDGE_AT_HANDLE_BORDER = true;\n\n/** Default border radius (px) for routed path corners. */\nexport const EDGE_BORDER_RADIUS = 0;\n","import { useMemo } from \"react\";\nimport { getStraightPath, getSmoothStepPath, Position as RFPosition } from \"@xyflow/react\";\nimport { useAvoidRoutesStore } from \"./store\";\nimport { EDGE_BORDER_RADIUS } from \"./constants\";\n\nexport type Position = \"left\" | \"right\" | \"top\" | \"bottom\";\n\nconst RF_POS: Record<Position, RFPosition> = {\n left: \"left\" as RFPosition,\n right: \"right\" as RFPosition,\n top: \"top\" as RFPosition,\n bottom: \"bottom\" as RFPosition,\n};\n\nexport interface UseAvoidNodesPathParams {\n id: string;\n sourceX: number;\n sourceY: number;\n targetX: number;\n targetY: number;\n sourcePosition?: Position;\n targetPosition?: Position;\n points?: { x: number; y: number }[];\n borderRadius?: number;\n offset?: number;\n}\n\n/**\n * Returns [path, labelX, labelY, wasRouted] for an avoid-nodes edge.\n * Reads from the avoid store (set by the worker); falls back to straight/smooth-step.\n */\nexport function useAvoidNodesPath(\n params: UseAvoidNodesPathParams\n): [path: string, labelX: number, labelY: number, wasRouted: boolean] {\n const {\n id,\n sourceX,\n sourceY,\n targetX,\n targetY,\n sourcePosition,\n targetPosition,\n offset,\n } = params;\n\n const loaded = useAvoidRoutesStore((s) => s.loaded);\n const route = useAvoidRoutesStore((s) => s.routes[id]);\n\n return useMemo(() => {\n if (loaded && route) {\n return [route.path, route.labelX, route.labelY, true];\n }\n\n if (sourcePosition && targetPosition) {\n const [smoothPath, sLabelX, sLabelY] = getSmoothStepPath({\n sourceX,\n sourceY,\n targetX,\n targetY,\n sourcePosition: RF_POS[sourcePosition],\n targetPosition: RF_POS[targetPosition],\n borderRadius: params.borderRadius ?? EDGE_BORDER_RADIUS,\n offset: offset ?? 20,\n });\n return [smoothPath, sLabelX, sLabelY, false];\n }\n\n const [straightPath, labelX, labelY] = getStraightPath({\n sourceX,\n sourceY,\n targetX,\n targetY,\n });\n return [straightPath, labelX, labelY, false];\n }, [loaded, route, sourceX, sourceY, targetX, targetY, sourcePosition, targetPosition, offset, params.borderRadius]);\n}\n"]}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
3
|
+
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
4
|
+
|
|
5
|
+
// src/store.ts
|
|
6
|
+
import { create } from "zustand";
|
|
7
|
+
var useAvoidRoutesStore = create((set) => ({
|
|
8
|
+
loaded: false,
|
|
9
|
+
routes: {},
|
|
10
|
+
setLoaded: (loaded) => set({ loaded }),
|
|
11
|
+
setRoutes: (routes) => set({ routes })
|
|
12
|
+
}));
|
|
13
|
+
var noop = () => {
|
|
14
|
+
};
|
|
15
|
+
var noopId = (_nodeId) => {
|
|
16
|
+
};
|
|
17
|
+
var useAvoidRouterActionsStore = create((set) => ({
|
|
18
|
+
actions: { resetRouting: noop, updateRoutesForNodeId: noopId },
|
|
19
|
+
setActions: (actions) => set({ actions })
|
|
20
|
+
}));
|
|
21
|
+
|
|
22
|
+
// src/constants.ts
|
|
23
|
+
var DEV_LOG_WEB_WORKER_MESSAGES = false;
|
|
24
|
+
var DEBOUNCE_ROUTING_MS = 0;
|
|
25
|
+
var SHOULD_START_EDGE_AT_HANDLE_BORDER = true;
|
|
26
|
+
var EDGE_BORDER_RADIUS = 0;
|
|
27
|
+
|
|
28
|
+
// src/useAvoidNodesPath.ts
|
|
29
|
+
import { useMemo } from "react";
|
|
30
|
+
import { getStraightPath, getSmoothStepPath } from "@xyflow/react";
|
|
31
|
+
var RF_POS = {
|
|
32
|
+
left: "left",
|
|
33
|
+
right: "right",
|
|
34
|
+
top: "top",
|
|
35
|
+
bottom: "bottom"
|
|
36
|
+
};
|
|
37
|
+
function useAvoidNodesPath(params) {
|
|
38
|
+
const {
|
|
39
|
+
id,
|
|
40
|
+
sourceX,
|
|
41
|
+
sourceY,
|
|
42
|
+
targetX,
|
|
43
|
+
targetY,
|
|
44
|
+
sourcePosition,
|
|
45
|
+
targetPosition,
|
|
46
|
+
offset
|
|
47
|
+
} = params;
|
|
48
|
+
const loaded = useAvoidRoutesStore((s) => s.loaded);
|
|
49
|
+
const route = useAvoidRoutesStore((s) => s.routes[id]);
|
|
50
|
+
return useMemo(() => {
|
|
51
|
+
if (loaded && route) {
|
|
52
|
+
return [route.path, route.labelX, route.labelY, true];
|
|
53
|
+
}
|
|
54
|
+
if (sourcePosition && targetPosition) {
|
|
55
|
+
const [smoothPath, sLabelX, sLabelY] = getSmoothStepPath({
|
|
56
|
+
sourceX,
|
|
57
|
+
sourceY,
|
|
58
|
+
targetX,
|
|
59
|
+
targetY,
|
|
60
|
+
sourcePosition: RF_POS[sourcePosition],
|
|
61
|
+
targetPosition: RF_POS[targetPosition],
|
|
62
|
+
borderRadius: params.borderRadius ?? EDGE_BORDER_RADIUS,
|
|
63
|
+
offset: offset ?? 20
|
|
64
|
+
});
|
|
65
|
+
return [smoothPath, sLabelX, sLabelY, false];
|
|
66
|
+
}
|
|
67
|
+
const [straightPath, labelX, labelY] = getStraightPath({
|
|
68
|
+
sourceX,
|
|
69
|
+
sourceY,
|
|
70
|
+
targetX,
|
|
71
|
+
targetY
|
|
72
|
+
});
|
|
73
|
+
return [straightPath, labelX, labelY, false];
|
|
74
|
+
}, [loaded, route, sourceX, sourceY, targetX, targetY, sourcePosition, targetPosition, offset, params.borderRadius]);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export {
|
|
78
|
+
__publicField,
|
|
79
|
+
useAvoidRoutesStore,
|
|
80
|
+
useAvoidRouterActionsStore,
|
|
81
|
+
DEV_LOG_WEB_WORKER_MESSAGES,
|
|
82
|
+
DEBOUNCE_ROUTING_MS,
|
|
83
|
+
SHOULD_START_EDGE_AT_HANDLE_BORDER,
|
|
84
|
+
EDGE_BORDER_RADIUS,
|
|
85
|
+
useAvoidNodesPath
|
|
86
|
+
};
|
|
87
|
+
//# sourceMappingURL=chunk-VPHZVUPR.js.map
|