quixotic-gol 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 +424 -0
- package/dist/assets/graphvizWorker-Bbv8iQW7.js +2402 -0
- package/dist/index.d.ts +260 -0
- package/dist/index.js +81 -0
- package/dist/index.mjs +3680 -0
- package/dist/worker.d.ts +1 -0
- package/dist/worker.js +13 -0
- package/dist/worker.mjs +2402 -0
- package/package.json +87 -0
package/README.md
ADDED
|
@@ -0,0 +1,424 @@
|
|
|
1
|
+
# quixotic-gol
|
|
2
|
+
|
|
3
|
+
A powerful React library for custom Graphviz layout rendering with Web Worker support for handling large graphs without blocking the UI.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- 🚀 **Web Worker Support** - Process large graphs in background without blocking the UI
|
|
8
|
+
- ⚛️ **React Components** - Ready-to-use React hooks and components
|
|
9
|
+
- 🎨 **Custom Rendering** - Full control over graph visualization with D3.js
|
|
10
|
+
- 📦 **TypeScript First** - Full TypeScript support with comprehensive type definitions
|
|
11
|
+
- 🔧 **Configurable** - Customizable layout configuration (spacing, corner radius, orthogonal rendering)
|
|
12
|
+
- 📊 **Depth Filtering** - Filter graph nodes by depth level
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npm install quixotic-gol
|
|
18
|
+
# or
|
|
19
|
+
pnpm add quixotic-gol
|
|
20
|
+
# or
|
|
21
|
+
yarn add quixotic-gol
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Peer Dependencies
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
npm install react react-dom
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Quick Start
|
|
31
|
+
|
|
32
|
+
### Simplest Way: Using GraphvizRenderer Component (Recommended)
|
|
33
|
+
|
|
34
|
+
The easiest way to render DOT strings with built-in worker and loading indicator:
|
|
35
|
+
|
|
36
|
+
```tsx
|
|
37
|
+
import { GraphvizRenderer } from 'quixotic-gol';
|
|
38
|
+
|
|
39
|
+
function MyGraph() {
|
|
40
|
+
const dotString = `
|
|
41
|
+
digraph G {
|
|
42
|
+
A -> B;
|
|
43
|
+
B -> C;
|
|
44
|
+
}
|
|
45
|
+
`;
|
|
46
|
+
|
|
47
|
+
return (
|
|
48
|
+
<GraphvizRenderer
|
|
49
|
+
dotString={dotString}
|
|
50
|
+
onSuccess={(jsonString) => {
|
|
51
|
+
const json = JSON.parse(jsonString);
|
|
52
|
+
console.log('Rendered:', json);
|
|
53
|
+
// Use the JSON result for custom rendering
|
|
54
|
+
}}
|
|
55
|
+
onError={(error) => {
|
|
56
|
+
console.error('Error:', error);
|
|
57
|
+
}}
|
|
58
|
+
/>
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
**Features:**
|
|
64
|
+
- ✅ Only uses Web Worker (no main thread blocking)
|
|
65
|
+
- ✅ Built-in loading indicator with cancel button
|
|
66
|
+
- ✅ Automatic error handling
|
|
67
|
+
- ✅ Simple, declarative API
|
|
68
|
+
|
|
69
|
+
See [GraphvizRenderer documentation](./docs/GraphvizRenderer.md) for more examples.
|
|
70
|
+
|
|
71
|
+
### Basic Usage with Hook
|
|
72
|
+
|
|
73
|
+
```tsx
|
|
74
|
+
import { useGraphvizWorker, renderCustomGraph } from 'quixotic-gol';
|
|
75
|
+
import { useRef, useEffect } from 'react';
|
|
76
|
+
|
|
77
|
+
function MyGraph() {
|
|
78
|
+
const containerRef = useRef<HTMLDivElement>(null);
|
|
79
|
+
const { layout, isLoading } = useGraphvizWorker();
|
|
80
|
+
|
|
81
|
+
useEffect(() => {
|
|
82
|
+
const dotString = `
|
|
83
|
+
digraph G {
|
|
84
|
+
A -> B;
|
|
85
|
+
B -> C;
|
|
86
|
+
}
|
|
87
|
+
`;
|
|
88
|
+
|
|
89
|
+
layout(dotString).then((jsonStr) => {
|
|
90
|
+
const json = JSON.parse(jsonStr);
|
|
91
|
+
if (containerRef.current) {
|
|
92
|
+
renderCustomGraph(
|
|
93
|
+
containerRef.current,
|
|
94
|
+
json,
|
|
95
|
+
{}, // node mapping
|
|
96
|
+
null, // max depth
|
|
97
|
+
true, // include lesser depths
|
|
98
|
+
createLayoutConfig(),
|
|
99
|
+
true, // use orthogonal rendering
|
|
100
|
+
14 // font size
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
}, [layout]);
|
|
105
|
+
|
|
106
|
+
return (
|
|
107
|
+
<div>
|
|
108
|
+
{isLoading && <p>Loading...</p>}
|
|
109
|
+
<div ref={containerRef} />
|
|
110
|
+
</div>
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### With Loading Indicator Component
|
|
116
|
+
|
|
117
|
+
```tsx
|
|
118
|
+
import {
|
|
119
|
+
useGraphvizWorker,
|
|
120
|
+
WorkerLoadingIndicator
|
|
121
|
+
} from 'quixotic-gol';
|
|
122
|
+
|
|
123
|
+
function MyGraph() {
|
|
124
|
+
const { layout, cancel, isLoading } = useGraphvizWorker();
|
|
125
|
+
|
|
126
|
+
return (
|
|
127
|
+
<div className="relative">
|
|
128
|
+
<div ref={containerRef} className="graph-container" />
|
|
129
|
+
|
|
130
|
+
{isLoading && (
|
|
131
|
+
<WorkerLoadingIndicator onCancel={cancel} />
|
|
132
|
+
)}
|
|
133
|
+
</div>
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
## API Reference
|
|
139
|
+
|
|
140
|
+
### Hooks
|
|
141
|
+
|
|
142
|
+
#### `useGraphvizWorker()`
|
|
143
|
+
|
|
144
|
+
React hook for processing DOT strings using a Web Worker.
|
|
145
|
+
|
|
146
|
+
**Returns:**
|
|
147
|
+
```typescript
|
|
148
|
+
{
|
|
149
|
+
layout: (dot: string) => Promise<string>; // Process DOT string, returns JSON
|
|
150
|
+
cancel: () => void; // Cancel current processing
|
|
151
|
+
isLoading: boolean; // Loading state
|
|
152
|
+
error: string | null; // Error message
|
|
153
|
+
}
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### Components
|
|
157
|
+
|
|
158
|
+
#### `GraphvizRenderer` (Recommended)
|
|
159
|
+
|
|
160
|
+
A powerful component that renders DOT strings using Web Worker with built-in loading indicator, zoom controls, and edge selection support.
|
|
161
|
+
|
|
162
|
+
**Props:**
|
|
163
|
+
```typescript
|
|
164
|
+
interface GraphvizRendererProps {
|
|
165
|
+
/** DOT string to render (required) */
|
|
166
|
+
dotString: string;
|
|
167
|
+
/** Node mapping for custom rendering */
|
|
168
|
+
nodeMapping?: Record<string, NodeMapping>;
|
|
169
|
+
/** SVG font size (default: 14) */
|
|
170
|
+
svgFontSize?: number;
|
|
171
|
+
/** Custom loading indicator component */
|
|
172
|
+
loadingIndicator?: React.ReactNode;
|
|
173
|
+
/** Callback when rendering succeeds with parsed JSON result */
|
|
174
|
+
onSuccess?: (json: GraphvizJson) => void;
|
|
175
|
+
/** Callback when rendering fails */
|
|
176
|
+
onError?: (error: Error) => void;
|
|
177
|
+
/** Callback on zoom with font size information */
|
|
178
|
+
onZoom?: (info: { baseFontSize: number; actualFontSize: number; scale: number }) => void;
|
|
179
|
+
/** Callback for render progress with elapsed time in milliseconds */
|
|
180
|
+
onRenderProgress?: (elapsedMs: number) => void;
|
|
181
|
+
/** Additional class name for the container */
|
|
182
|
+
className?: string;
|
|
183
|
+
/** Show loading indicator (default: true) */
|
|
184
|
+
showLoading?: boolean;
|
|
185
|
+
/** Enable debug visual indicators - yellow border and center dot (default: false) */
|
|
186
|
+
debug?: boolean;
|
|
187
|
+
/** Currently selected edge ID (controlled component) */
|
|
188
|
+
selectedEdgeId?: number | null;
|
|
189
|
+
/** Callback when an edge is selected or deselected */
|
|
190
|
+
onEdgeSelect?: (edgeId: number | null) => void;
|
|
191
|
+
/** Use native Graphviz rendering instead of custom rendering (default: false) */
|
|
192
|
+
useNativeRendering?: boolean;
|
|
193
|
+
/** Custom node width in points (default: 240) */
|
|
194
|
+
nodeWidth?: number;
|
|
195
|
+
/** Custom node height in points (default: 64) */
|
|
196
|
+
nodeHeight?: number;
|
|
197
|
+
/** Custom spacing between node columns in points (default: 800) */
|
|
198
|
+
nodeColumnSpacing?: number;
|
|
199
|
+
}
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
**Ref API:**
|
|
203
|
+
|
|
204
|
+
Access programmatic zoom and rendering controls via ref:
|
|
205
|
+
|
|
206
|
+
```typescript
|
|
207
|
+
interface GraphvizRendererRef {
|
|
208
|
+
/** Zoom in by increasing the effective font size */
|
|
209
|
+
zoomIn: (percentage?: number) => void;
|
|
210
|
+
/** Zoom out by decreasing the effective font size */
|
|
211
|
+
zoomOut: (percentage?: number) => void;
|
|
212
|
+
/** Toggle between full overview and centered view with normal font size (16px) */
|
|
213
|
+
toggleZoom: () => void;
|
|
214
|
+
/** Stop the current rendering operation and terminate the worker */
|
|
215
|
+
stopRendering: () => void;
|
|
216
|
+
}
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
**Basic Example:**
|
|
220
|
+
```tsx
|
|
221
|
+
<GraphvizRenderer
|
|
222
|
+
dotString={myDotString}
|
|
223
|
+
onSuccess={(json) => console.log('Rendered:', json)}
|
|
224
|
+
onError={(err) => console.error(err)}
|
|
225
|
+
/>
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
**Full-Featured Example with Ref API:**
|
|
229
|
+
```tsx
|
|
230
|
+
import { useRef } from 'react';
|
|
231
|
+
import { GraphvizRenderer, GraphvizRendererRef } from 'quixotic-gol';
|
|
232
|
+
|
|
233
|
+
function MyGraph() {
|
|
234
|
+
const rendererRef = useRef<GraphvizRendererRef>(null);
|
|
235
|
+
const [selectedEdge, setSelectedEdge] = useState<number | null>(null);
|
|
236
|
+
|
|
237
|
+
return (
|
|
238
|
+
<div>
|
|
239
|
+
{/* Zoom controls */}
|
|
240
|
+
<button onClick={() => rendererRef.current?.zoomIn(10)}>Zoom In</button>
|
|
241
|
+
<button onClick={() => rendererRef.current?.zoomOut(10)}>Zoom Out</button>
|
|
242
|
+
<button onClick={() => rendererRef.current?.toggleZoom()}>Toggle Zoom</button>
|
|
243
|
+
<button onClick={() => rendererRef.current?.stopRendering()}>Stop</button>
|
|
244
|
+
|
|
245
|
+
<GraphvizRenderer
|
|
246
|
+
ref={rendererRef}
|
|
247
|
+
dotString={dotString}
|
|
248
|
+
nodeMapping={nodeMapping}
|
|
249
|
+
svgFontSize={14}
|
|
250
|
+
className="w-full h-full"
|
|
251
|
+
debug={false}
|
|
252
|
+
nodeWidth={240}
|
|
253
|
+
nodeHeight={64}
|
|
254
|
+
nodeColumnSpacing={800}
|
|
255
|
+
selectedEdgeId={selectedEdge}
|
|
256
|
+
onEdgeSelect={setSelectedEdge}
|
|
257
|
+
onZoom={({ actualFontSize, scale }) => {
|
|
258
|
+
console.log(`Zoom: ${scale}x, Font: ${actualFontSize}px`);
|
|
259
|
+
}}
|
|
260
|
+
onRenderProgress={(elapsedMs) => {
|
|
261
|
+
console.log(`Rendering: ${elapsedMs}ms`);
|
|
262
|
+
}}
|
|
263
|
+
onSuccess={(json) => console.log('Rendered:', json)}
|
|
264
|
+
onError={(err) => console.error('Error:', err)}
|
|
265
|
+
/>
|
|
266
|
+
</div>
|
|
267
|
+
);
|
|
268
|
+
}
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
See [GraphvizRenderer documentation](./docs/GraphvizRenderer.md) for more examples.
|
|
272
|
+
|
|
273
|
+
#### `WorkerLoadingIndicator`
|
|
274
|
+
|
|
275
|
+
Loading indicator component with cancel button.
|
|
276
|
+
|
|
277
|
+
**Props:**
|
|
278
|
+
```typescript
|
|
279
|
+
{
|
|
280
|
+
onCancel: () => void; // Callback when cancel is clicked
|
|
281
|
+
}
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
### Functions
|
|
285
|
+
|
|
286
|
+
#### `renderCustomGraph()`
|
|
287
|
+
|
|
288
|
+
Render a graph with custom styling.
|
|
289
|
+
|
|
290
|
+
```typescript
|
|
291
|
+
function renderCustomGraph(
|
|
292
|
+
container: HTMLDivElement,
|
|
293
|
+
json: GraphvizJson,
|
|
294
|
+
nodeMapping: Record<string, NodeMapping>,
|
|
295
|
+
maxDepth: number | null,
|
|
296
|
+
includeLesserDepths: boolean,
|
|
297
|
+
config: LayoutConfig,
|
|
298
|
+
useOrthogonalRendering: boolean,
|
|
299
|
+
svgFontSize: number
|
|
300
|
+
): void
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
#### `setupZoomPan()`
|
|
304
|
+
|
|
305
|
+
Setup zoom and pan interactions for the graph.
|
|
306
|
+
|
|
307
|
+
```typescript
|
|
308
|
+
function setupZoomPan(container: HTMLDivElement): void
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
#### `createLayoutConfig()`
|
|
312
|
+
|
|
313
|
+
Create layout configuration with optional overrides.
|
|
314
|
+
|
|
315
|
+
```typescript
|
|
316
|
+
function createLayoutConfig(overrides?: Partial<LayoutConfig>): LayoutConfig
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
**Default Configuration:**
|
|
320
|
+
```typescript
|
|
321
|
+
{
|
|
322
|
+
edgeSpacing: 80, // Spacing between edges in pixels
|
|
323
|
+
cornerRadius: 15, // Corner radius for edge curves
|
|
324
|
+
}
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
### Types
|
|
328
|
+
|
|
329
|
+
```typescript
|
|
330
|
+
export interface NodeMapping {
|
|
331
|
+
sourceNodeId: string;
|
|
332
|
+
targetNodeId: string;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
export interface GraphvizJson {
|
|
336
|
+
objects?: Array<{
|
|
337
|
+
_ldraw_?: Array<{ size?: number }>;
|
|
338
|
+
// ... other properties
|
|
339
|
+
}>;
|
|
340
|
+
edges?: Array<any>;
|
|
341
|
+
// ... other properties
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
export interface LayoutConfig {
|
|
345
|
+
edgeSpacing: number;
|
|
346
|
+
cornerRadius: number;
|
|
347
|
+
}
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
## Configuration Options
|
|
351
|
+
|
|
352
|
+
### Layout Configuration
|
|
353
|
+
|
|
354
|
+
```typescript
|
|
355
|
+
import { createLayoutConfig } from 'quixotic-gol';
|
|
356
|
+
|
|
357
|
+
const config = createLayoutConfig({
|
|
358
|
+
edgeSpacing: 100, // Increase spacing between parallel edges
|
|
359
|
+
cornerRadius: 20, // Smoother corners
|
|
360
|
+
});
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
### Depth Filtering
|
|
364
|
+
|
|
365
|
+
```typescript
|
|
366
|
+
// Show only nodes at depth 0-2
|
|
367
|
+
renderCustomGraph(
|
|
368
|
+
container,
|
|
369
|
+
json,
|
|
370
|
+
nodeMapping,
|
|
371
|
+
2, // maxDepth
|
|
372
|
+
true, // includeLesserDepths - show depth 0, 1, and 2
|
|
373
|
+
config,
|
|
374
|
+
true,
|
|
375
|
+
14
|
|
376
|
+
);
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
## Advanced Usage
|
|
380
|
+
|
|
381
|
+
### Direct Worker Access
|
|
382
|
+
|
|
383
|
+
For advanced use cases, you can import worker types:
|
|
384
|
+
|
|
385
|
+
```typescript
|
|
386
|
+
import type { WorkerRequest, WorkerResponse } from 'quixotic-gol/worker';
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
## Demo Application
|
|
390
|
+
|
|
391
|
+
The package includes a full Next.js demo application. To run it:
|
|
392
|
+
|
|
393
|
+
```bash
|
|
394
|
+
git clone <repository-url>
|
|
395
|
+
cd graphviz-layouter
|
|
396
|
+
pnpm install
|
|
397
|
+
pnpm dev
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
Visit `http://localhost:3000` to see the demo.
|
|
401
|
+
|
|
402
|
+
## Building from Source
|
|
403
|
+
|
|
404
|
+
```bash
|
|
405
|
+
# Install dependencies
|
|
406
|
+
pnpm install
|
|
407
|
+
|
|
408
|
+
# Build library
|
|
409
|
+
pnpm run build:lib
|
|
410
|
+
|
|
411
|
+
# Build Next.js demo
|
|
412
|
+
pnpm run build:next
|
|
413
|
+
|
|
414
|
+
# Build both
|
|
415
|
+
pnpm run build
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
## License
|
|
419
|
+
|
|
420
|
+
MIT
|
|
421
|
+
|
|
422
|
+
## Contributing
|
|
423
|
+
|
|
424
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|