@3plate/graph 0.1.7 → 0.1.9
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 +260 -99
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -1,110 +1,273 @@
|
|
|
1
1
|
# @3plate/graph
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
A graph visualization library with **stable layouts** and **incremental updates**. Build interactive directed graphs that maintain their structure as data changes—perfect for visualizing pipelines, workflows, dependency trees, and state machines.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Why @3plate/graph?
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Most graph libraries re-layout the entire graph when data changes, causing nodes to jump around unpredictably. @3plate/graph solves this with:
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
- **Stable layouts** — Nodes stay in predictable positions as the graph evolves
|
|
10
|
+
- **Incremental updates** — Add, remove, or modify nodes without full re-renders
|
|
11
|
+
- **Railroad-style edges** — Clean, orthogonal routing that avoids crossing nodes
|
|
12
|
+
- **Real-time ingestion** — Stream graph updates from WebSockets or files
|
|
10
13
|
|
|
11
|
-
|
|
12
|
-
- Automatic graph layout
|
|
13
|
-
- Stable layouts
|
|
14
|
-
- Multiple orientations
|
|
15
|
-
- Interactive features
|
|
16
|
-
- Cycle detection
|
|
17
|
-
- Edge styling
|
|
18
|
-
- Pan and zoom
|
|
19
|
-
- Mini-map
|
|
20
|
-
- Animations
|
|
21
|
-
- SVG for crisp details
|
|
22
|
-
- Fast rendering
|
|
23
|
-
- Builder mode
|
|
24
|
-
- Incremental updates
|
|
25
|
-
- Railroad-style edges
|
|
26
|
-
- Configurable layout
|
|
27
|
-
- React/Vue/Angular support
|
|
28
|
-
|
|
29
|
-
## Project Structure
|
|
30
|
-
|
|
31
|
-
`@3plate/graph` can be used as a library with no framework dependencies, or you can use the
|
|
32
|
-
components in `@3plate/graph-react`, `@3plate/graph-vue`, or `@3plate/graph-angular` which wrap
|
|
33
|
-
the core library.
|
|
14
|
+
## Quick Start
|
|
34
15
|
|
|
35
|
-
|
|
36
|
-
graph/
|
|
37
|
-
├── packages/
|
|
38
|
-
│ ├── core/
|
|
39
|
-
│ │ ├── src/
|
|
40
|
-
│ │ ├── package.json
|
|
41
|
-
│ │ ├── tsconfig.json
|
|
42
|
-
│ │ └── README.md
|
|
43
|
-
│ │
|
|
44
|
-
│ ├── react/
|
|
45
|
-
│ │ ├── src/
|
|
46
|
-
│ │ ├── package.json
|
|
47
|
-
│ │ └── README.md
|
|
48
|
-
│ │
|
|
49
|
-
│ ├── vue/
|
|
50
|
-
│ │ └── ...
|
|
51
|
-
│ │
|
|
52
|
-
│ └── angular/
|
|
53
|
-
│ └── ...
|
|
54
|
-
│
|
|
55
|
-
├── apps/
|
|
56
|
-
│ └── site/
|
|
57
|
-
│ ├── src/
|
|
58
|
-
│ ├── public/
|
|
59
|
-
│ ├── astro.config.mjs
|
|
60
|
-
│ └── package.json
|
|
61
|
-
│
|
|
62
|
-
├── .github/
|
|
63
|
-
│ └── workflows/
|
|
64
|
-
│ ├── publish-npm.yml
|
|
65
|
-
│ └── deploy-pages.yml
|
|
66
|
-
│
|
|
67
|
-
├── pnpm-workspace.yaml
|
|
68
|
-
├── package.json
|
|
69
|
-
├── .npmrc
|
|
70
|
-
├── turbo.json
|
|
71
|
-
└── README.md
|
|
72
|
-
```
|
|
73
|
-
|
|
74
|
-
## Installation
|
|
75
|
-
|
|
76
|
-
### Core Library
|
|
16
|
+
### Vanilla JavaScript
|
|
77
17
|
|
|
78
18
|
```bash
|
|
79
19
|
npm install @3plate/graph-core
|
|
80
20
|
```
|
|
81
21
|
|
|
82
|
-
|
|
22
|
+
```typescript
|
|
23
|
+
import { graph } from '@3plate/graph-core'
|
|
24
|
+
|
|
25
|
+
const api = await graph({
|
|
26
|
+
root: 'my-graph',
|
|
27
|
+
nodes: [
|
|
28
|
+
{ id: 'a', title: 'Start' },
|
|
29
|
+
{ id: 'b', title: 'Process' },
|
|
30
|
+
{ id: 'c', title: 'End' },
|
|
31
|
+
],
|
|
32
|
+
edges: [
|
|
33
|
+
{ source: 'a', target: 'b' },
|
|
34
|
+
{ source: 'b', target: 'c' },
|
|
35
|
+
],
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
// Update the graph incrementally
|
|
39
|
+
api.update(u => {
|
|
40
|
+
u.addNodes({ id: 'd', title: 'New Step' })
|
|
41
|
+
u.addEdges({ source: 'b', target: 'd' })
|
|
42
|
+
})
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### React
|
|
83
46
|
|
|
84
47
|
```bash
|
|
85
|
-
# React
|
|
86
48
|
npm install @3plate/graph-react
|
|
49
|
+
```
|
|
87
50
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
51
|
+
```tsx
|
|
52
|
+
import { Graph } from '@3plate/graph-react'
|
|
53
|
+
|
|
54
|
+
function App() {
|
|
55
|
+
return (
|
|
56
|
+
<Graph
|
|
57
|
+
nodes={[
|
|
58
|
+
{ id: 'a', title: 'Start' },
|
|
59
|
+
{ id: 'b', title: 'Process' },
|
|
60
|
+
{ id: 'c', title: 'End' },
|
|
61
|
+
]}
|
|
62
|
+
edges={[
|
|
63
|
+
{ source: 'a', target: 'b' },
|
|
64
|
+
{ source: 'b', target: 'c' },
|
|
65
|
+
]}
|
|
66
|
+
/>
|
|
67
|
+
)
|
|
68
|
+
}
|
|
93
69
|
```
|
|
94
70
|
|
|
95
|
-
##
|
|
71
|
+
## Features
|
|
96
72
|
|
|
97
|
-
###
|
|
73
|
+
### Layout & Rendering
|
|
74
|
+
|
|
75
|
+
- **Automatic layered layout** — Nodes are positioned in layers based on their connections
|
|
76
|
+
- **Multiple orientations** — Top-to-bottom, bottom-to-top, left-to-right, or right-to-left
|
|
77
|
+
- **SVG rendering** — Crisp graphics at any zoom level
|
|
78
|
+
- **Pan and zoom** — Navigate large graphs with mouse/touch controls
|
|
79
|
+
- **Custom node rendering** — Render any HTML content inside nodes
|
|
80
|
+
|
|
81
|
+
### Interactivity
|
|
82
|
+
|
|
83
|
+
- **Edit mode** — Add, remove, and connect nodes with mouse interactions
|
|
84
|
+
- **Click handlers** — Respond to node and edge clicks
|
|
85
|
+
- **Keyboard navigation** — Step through graph history with arrow keys
|
|
86
|
+
- **Selection** — Visual feedback for selected nodes and edges
|
|
87
|
+
|
|
88
|
+
### Theming
|
|
89
|
+
|
|
90
|
+
- **Light/dark mode** — Built-in themes with system preference detection
|
|
91
|
+
- **Node types** — Define custom styles per node type
|
|
92
|
+
- **Edge types** — Style edges by category (error, success, etc.)
|
|
93
|
+
- **CSS variables** — Full control over colors, borders, and shadows
|
|
94
|
+
|
|
95
|
+
### Data Handling
|
|
96
|
+
|
|
97
|
+
- **History & time-travel** — Navigate through graph states with undo/redo
|
|
98
|
+
- **Real-time ingestion** — Connect to WebSocket or file sources for live updates
|
|
99
|
+
- **Snapshot & update modes** — Replace entire graph or apply incremental changes
|
|
100
|
+
- **Ports** — Define named input/output ports on nodes for precise edge routing
|
|
101
|
+
|
|
102
|
+
### Graph Features
|
|
103
|
+
|
|
104
|
+
- **Cycle detection** — Automatically handles graphs with cycles
|
|
105
|
+
- **Edge markers** — Arrow, circle, diamond, and bar markers on edge endpoints
|
|
106
|
+
- **Dummy nodes** — Long edges are split with invisible waypoints for cleaner routing
|
|
107
|
+
- **Configurable spacing** — Control margins, edge spacing, and turn radius
|
|
108
|
+
|
|
109
|
+
## Packages
|
|
110
|
+
|
|
111
|
+
| Package | Description |
|
|
112
|
+
|---------|-------------|
|
|
113
|
+
| [@3plate/graph-core](./packages/core/README.md) | Framework-agnostic core library |
|
|
114
|
+
| [@3plate/graph-react](./packages/react/README.md) | React components and hooks |
|
|
115
|
+
| [@3plate/graph-vue](./packages/vue/README.md) | Vue 3 components |
|
|
116
|
+
| [@3plate/graph-angular](./packages/angular/README.md) | Angular components |
|
|
117
|
+
|
|
118
|
+
## Configuration
|
|
119
|
+
|
|
120
|
+
### Graph Options
|
|
121
|
+
|
|
122
|
+
```typescript
|
|
123
|
+
await graph({
|
|
124
|
+
root: 'my-graph',
|
|
125
|
+
nodes: [...],
|
|
126
|
+
edges: [...],
|
|
127
|
+
options: {
|
|
128
|
+
graph: {
|
|
129
|
+
orientation: 'LR', // 'TB' | 'BT' | 'LR' | 'RL'
|
|
130
|
+
nodeMargin: 20, // Space between nodes
|
|
131
|
+
layerMargin: 60, // Space between layers
|
|
132
|
+
edgeSpacing: 8, // Space between parallel edges
|
|
133
|
+
turnRadius: 8, // Rounded corner radius for edges
|
|
134
|
+
},
|
|
135
|
+
canvas: {
|
|
136
|
+
width: '100%',
|
|
137
|
+
height: 600,
|
|
138
|
+
padding: 40,
|
|
139
|
+
editable: true, // Enable edit mode
|
|
140
|
+
panZoom: true, // Enable pan and zoom
|
|
141
|
+
colorMode: 'system', // 'light' | 'dark' | 'system'
|
|
142
|
+
},
|
|
143
|
+
},
|
|
144
|
+
})
|
|
145
|
+
```
|
|
98
146
|
|
|
99
|
-
|
|
147
|
+
### Custom Node Rendering
|
|
148
|
+
|
|
149
|
+
```typescript
|
|
150
|
+
await graph({
|
|
151
|
+
root: 'my-graph',
|
|
152
|
+
nodes: myNodes,
|
|
153
|
+
edges: myEdges,
|
|
154
|
+
options: {
|
|
155
|
+
canvas: {
|
|
156
|
+
renderNode: (node) => {
|
|
157
|
+
const el = document.createElement('div')
|
|
158
|
+
el.className = 'my-custom-node'
|
|
159
|
+
el.innerHTML = `<h3>${node.title}</h3><p>${node.description}</p>`
|
|
160
|
+
return el
|
|
161
|
+
},
|
|
162
|
+
},
|
|
163
|
+
},
|
|
164
|
+
})
|
|
165
|
+
```
|
|
100
166
|
|
|
101
|
-
###
|
|
167
|
+
### Theming by Type
|
|
168
|
+
|
|
169
|
+
```typescript
|
|
170
|
+
await graph({
|
|
171
|
+
root: 'my-graph',
|
|
172
|
+
nodes: [
|
|
173
|
+
{ id: 'start', type: 'input', title: 'Start' },
|
|
174
|
+
{ id: 'process', type: 'process', title: 'Process' },
|
|
175
|
+
{ id: 'error', type: 'error', title: 'Error' },
|
|
176
|
+
],
|
|
177
|
+
edges: [...],
|
|
178
|
+
options: {
|
|
179
|
+
canvas: {
|
|
180
|
+
nodeTypes: {
|
|
181
|
+
input: { border: '#3b82f6' },
|
|
182
|
+
process: { border: '#8b5cf6' },
|
|
183
|
+
error: { bg: '#fef2f2', border: '#ef4444', text: '#991b1b' },
|
|
184
|
+
},
|
|
185
|
+
edgeTypes: {
|
|
186
|
+
error: { color: '#ef4444' },
|
|
187
|
+
success: { color: '#22c55e' },
|
|
188
|
+
},
|
|
189
|
+
},
|
|
190
|
+
},
|
|
191
|
+
})
|
|
192
|
+
```
|
|
102
193
|
|
|
103
|
-
|
|
194
|
+
### Real-time Ingestion
|
|
195
|
+
|
|
196
|
+
```typescript
|
|
197
|
+
// Connect to a WebSocket for live updates
|
|
198
|
+
await graph({
|
|
199
|
+
root: 'my-graph',
|
|
200
|
+
ingestion: {
|
|
201
|
+
type: 'websocket',
|
|
202
|
+
url: 'ws://localhost:8787',
|
|
203
|
+
},
|
|
204
|
+
})
|
|
205
|
+
|
|
206
|
+
// Or poll a file/endpoint
|
|
207
|
+
await graph({
|
|
208
|
+
root: 'my-graph',
|
|
209
|
+
ingestion: {
|
|
210
|
+
type: 'file',
|
|
211
|
+
url: '/api/graph-updates.ndjson',
|
|
212
|
+
intervalMs: 1000,
|
|
213
|
+
},
|
|
214
|
+
})
|
|
215
|
+
```
|
|
104
216
|
|
|
105
|
-
###
|
|
217
|
+
### Event Handling
|
|
218
|
+
|
|
219
|
+
```typescript
|
|
220
|
+
await graph({
|
|
221
|
+
root: 'my-graph',
|
|
222
|
+
nodes: [...],
|
|
223
|
+
edges: [...],
|
|
224
|
+
events: {
|
|
225
|
+
nodeClick: (node) => console.log('Clicked:', node),
|
|
226
|
+
edgeClick: (edge) => console.log('Edge clicked:', edge),
|
|
227
|
+
addNode: (props, done) => {
|
|
228
|
+
// Custom logic when user adds a node
|
|
229
|
+
const newNode = { id: generateId(), ...props }
|
|
230
|
+
done(newNode)
|
|
231
|
+
},
|
|
232
|
+
historyChange: (index, length) => {
|
|
233
|
+
console.log(`Step ${index + 1} of ${length}`)
|
|
234
|
+
},
|
|
235
|
+
},
|
|
236
|
+
})
|
|
237
|
+
```
|
|
106
238
|
|
|
107
|
-
|
|
239
|
+
## API Methods
|
|
240
|
+
|
|
241
|
+
```typescript
|
|
242
|
+
const api = await graph({ ... })
|
|
243
|
+
|
|
244
|
+
// Navigation
|
|
245
|
+
api.nav('first') // Jump to first state
|
|
246
|
+
api.nav('prev') // Go to previous state
|
|
247
|
+
api.nav('next') // Go to next state
|
|
248
|
+
api.nav('last') // Jump to latest state
|
|
249
|
+
|
|
250
|
+
// Updates
|
|
251
|
+
api.update(u => {
|
|
252
|
+
u.addNodes({ id: 'x', title: 'New' })
|
|
253
|
+
u.deleteNodes('old-node')
|
|
254
|
+
u.updateNodes({ id: 'x', title: 'Updated' })
|
|
255
|
+
u.addEdges({ source: 'a', target: 'x' })
|
|
256
|
+
u.deleteEdges('edge-id')
|
|
257
|
+
u.describe('Added new node') // Label for history
|
|
258
|
+
})
|
|
259
|
+
|
|
260
|
+
// Bulk operations
|
|
261
|
+
api.replaceSnapshot(nodes, edges)
|
|
262
|
+
api.replaceHistory(frames)
|
|
263
|
+
|
|
264
|
+
// Theming
|
|
265
|
+
api.setColorMode('dark')
|
|
266
|
+
api.updateStyles({ nodeTypes: { ... } })
|
|
267
|
+
|
|
268
|
+
// Cleanup
|
|
269
|
+
api.destroy()
|
|
270
|
+
```
|
|
108
271
|
|
|
109
272
|
## Development
|
|
110
273
|
|
|
@@ -122,37 +285,35 @@ pnpm dev:site
|
|
|
122
285
|
pnpm test
|
|
123
286
|
```
|
|
124
287
|
|
|
288
|
+
## Inspiration
|
|
289
|
+
|
|
290
|
+
This library is inspired by [IonGraph Web](https://spidermonkey.dev/blog/2025/10/28/iongraph-web.html) from the SpiderMonkey team, which visualizes JavaScript JIT compilation graphs with stable, incremental layouts.
|
|
291
|
+
|
|
125
292
|
## Contributing
|
|
126
293
|
|
|
127
|
-
We welcome contributions! Before submitting a pull request
|
|
294
|
+
We welcome contributions! Before submitting a pull request:
|
|
128
295
|
|
|
129
296
|
1. Read our [Contributing Guidelines](./CONTRIBUTING.md)
|
|
130
|
-
2. Sign the [Contributor License Agreement
|
|
297
|
+
2. Sign the [Contributor License Agreement](./CLA.md)
|
|
131
298
|
3. Follow the code style and testing guidelines
|
|
132
299
|
|
|
133
|
-
See [CONTRIBUTING.md](./CONTRIBUTING.md) for more details.
|
|
134
|
-
|
|
135
300
|
## License
|
|
136
301
|
|
|
137
|
-
|
|
302
|
+
**GNU General Public License v3.0** — see [LICENSE](./LICENSE) for details.
|
|
138
303
|
|
|
139
304
|
### Why GPL?
|
|
140
305
|
|
|
141
|
-
We've chosen GPL-3.0 to ensure
|
|
306
|
+
We've chosen GPL-3.0 to ensure improvements to @3plate/graph remain open source:
|
|
142
307
|
|
|
143
|
-
- ✅
|
|
144
|
-
- ✅
|
|
145
|
-
- ✅ Commercial use
|
|
146
|
-
- ⚠️
|
|
308
|
+
- ✅ Use in open source projects
|
|
309
|
+
- ✅ Modify and distribute
|
|
310
|
+
- ✅ Commercial use allowed
|
|
311
|
+
- ⚠️ Distributed modifications must also be GPL-3.0
|
|
147
312
|
|
|
148
313
|
### Commercial Licensing
|
|
149
314
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
### Copyright
|
|
153
|
-
|
|
154
|
-
Copyright (c) 2025 3Plate LLC
|
|
315
|
+
Need to use @3plate/graph in proprietary software without GPL restrictions? Commercial licenses are available — contact [nathan@3plate.com](mailto:nathan@3plate.com).
|
|
155
316
|
|
|
156
317
|
---
|
|
157
318
|
|
|
158
|
-
|
|
319
|
+
**Copyright © 2025 [3Plate LLC](https://www.3plate.com/)**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@3plate/graph",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.9",
|
|
4
4
|
"description": "Graph library with stable layout and incremental updates",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "GPL-3.0",
|
|
@@ -23,10 +23,10 @@
|
|
|
23
23
|
"access": "public"
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"@3plate/graph-angular": "0.1.
|
|
27
|
-
"@3plate/graph-
|
|
28
|
-
"@3plate/graph-
|
|
29
|
-
"@3plate/graph-vue": "0.1.
|
|
26
|
+
"@3plate/graph-angular": "0.1.9",
|
|
27
|
+
"@3plate/graph-react": "0.1.9",
|
|
28
|
+
"@3plate/graph-core": "0.1.9",
|
|
29
|
+
"@3plate/graph-vue": "0.1.9"
|
|
30
30
|
},
|
|
31
31
|
"devDependencies": {
|
|
32
32
|
"@changesets/cli": "^2.29.8",
|