@visualli/react 0.1.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.
Files changed (38) hide show
  1. package/README.md +335 -0
  2. package/dist/index.cjs +3262 -0
  3. package/dist/index.cjs.map +1 -0
  4. package/dist/index.d.cts +375 -0
  5. package/dist/index.d.ts +375 -0
  6. package/dist/index.js +3289 -0
  7. package/dist/index.js.map +1 -0
  8. package/package.json +48 -0
  9. package/src/VisualliCanvas.tsx +1087 -0
  10. package/src/VisualliRenderer.tsx +427 -0
  11. package/src/animations/index.ts +2 -0
  12. package/src/animations/konvaLayerTransition.ts +223 -0
  13. package/src/components/KonvaContainer.tsx +255 -0
  14. package/src/components/KonvaContainerLayer.tsx +63 -0
  15. package/src/components/KonvaEdge.tsx +254 -0
  16. package/src/components/KonvaEdgeLayer.tsx +72 -0
  17. package/src/components/KonvaNode.tsx +177 -0
  18. package/src/components/KonvaNodeLayer.tsx +61 -0
  19. package/src/components/KonvaStage.tsx +80 -0
  20. package/src/components/NavigationStack.tsx +184 -0
  21. package/src/components/SketchyBoxKonva.tsx +121 -0
  22. package/src/components/ZoomControls.tsx +110 -0
  23. package/src/config/blobShapes.ts +204 -0
  24. package/src/config/index.ts +21 -0
  25. package/src/config/textScaling.ts +69 -0
  26. package/src/context/VisualliContext.tsx +50 -0
  27. package/src/hooks/index.ts +7 -0
  28. package/src/hooks/useKonvaLayerTransition.ts +221 -0
  29. package/src/hooks/useKonvaRenderer.ts +185 -0
  30. package/src/hooks/useViewportNodes.ts +59 -0
  31. package/src/index.ts +81 -0
  32. package/src/konvaCompat.ts +29 -0
  33. package/src/stores/index.ts +3 -0
  34. package/src/stores/stores.ts +17 -0
  35. package/src/stores/useNodeStore.ts +101 -0
  36. package/src/stores/useViewportStore.ts +95 -0
  37. package/src/utils/index.ts +1 -0
  38. package/src/utils/layerNavigation.ts +15 -0
package/README.md ADDED
@@ -0,0 +1,335 @@
1
+ # @visualli/react
2
+
3
+ React canvas rendering for Visualli — powered by Konva. Drop-in component that displays a `VisualliDocument` as an interactive, zoomable, navigable mind-map.
4
+
5
+ ## Features
6
+
7
+ - **GPU-accelerated canvas** via `react-konva` — handles thousands of nodes at 60 fps
8
+ - **Organic blob nodes** — 6 quadratic-bezier blob shapes that cycle by level
9
+ - **Layer navigation** — double-click any node to drill into its child layer, breadcrumb back
10
+ - **Animated transitions** — rAF-driven zoom-into-layer / zoom-out-to-parent with color crossfade
11
+ - **Zoom controls** — +/− buttons, %, fit-to-screen
12
+ - **Viewport culling** — RBush spatial index keeps only visible nodes on the canvas
13
+ - **Zustand stores** — fine-grained subscriptions for nodes, viewport, selection, render config
14
+ - **Light / dark theme** — single `isDark` prop
15
+ - **🆕 Extension system** — inject custom parser middlewares and UI components at runtime
16
+ - **🆕 Stream fetching** — `useVisualliStream` hook for backend JSONL streams
17
+ - **🆕 Context provider** — `VisualliProvider` for dependency injection
18
+
19
+ ## Installation
20
+
21
+ ```bash
22
+ npm install @visualli/react @visualli/core konva react-konva zustand
23
+ ```
24
+
25
+ Peer dependencies: `react@^18`, `react-dom@^18`
26
+
27
+ ## Quick Start
28
+
29
+ ```tsx
30
+ import { VisualliCanvas } from '@visualli/react';
31
+
32
+ // Option A — pass a pre-parsed VisualliDocument
33
+ import { parseVisualliFile } from '@visualli/core';
34
+
35
+ const doc = parseVisualliFile(rawJsonlString);
36
+
37
+ export default function App() {
38
+ return (
39
+ <div style={{ width: '100vw', height: '100vh' }}>
40
+ <VisualliCanvas document={doc} isDark={false} />
41
+ </div>
42
+ );
43
+ }
44
+
45
+ // Option B — pass the raw JSONL string directly
46
+ export default function App() {
47
+ return (
48
+ <div style={{ width: '100vw', height: '100vh' }}>
49
+ <VisualliCanvas visualliString={rawJsonlString} isDark={true} />
50
+ </div>
51
+ );
52
+ }
53
+ ```
54
+
55
+ ## `VisualliCanvas` Props
56
+
57
+ | Prop | Type | Default | Description |
58
+ |------|------|---------|-------------|
59
+ | `document` | `VisualliDocument` | — | Pre-parsed document |
60
+ | `visualliString` | `string` | — | Raw JSONL — parsed internally |
61
+ | `isDark` | `boolean` | `false` | Light / dark canvas theme |
62
+ | `onNodeClick` | `(node: FlatNode) => void` | — | Single-click callback |
63
+ | `onLayerChange` | `(id: string, layer: VisualliLayer) => void` | — | Fired after navigation |
64
+ | `className` | `string` | `''` | Extra CSS classes on the wrapper div |
65
+ | `style` | `React.CSSProperties` | — | Inline styles on the wrapper div |
66
+
67
+ > The component fills its parent container — set an explicit `width` / `height` on the wrapper.
68
+
69
+ ## Architecture
70
+
71
+ ```
72
+ VisualliCanvas
73
+ ├── KonvaStage react-konva <Stage>, position/scale from viewport store
74
+ │ ├── KonvaContainerLayer <Layer> — convex-hull outlines (non-interactive)
75
+ │ ├── KonvaEdgeLayer <Layer> — bezier edges between visible nodes
76
+ │ └── KonvaNodeLayer <Layer> — blob nodes, handles click/dblclick
77
+ ├── NavigationStack DOM overlay — breadcrumb trail, click to go back
78
+ └── ZoomControls DOM overlay — +/−/% buttons, fit-to-screen
79
+ ```
80
+
81
+ ### Stores (Zustand)
82
+
83
+ Access any store directly for advanced use cases:
84
+
85
+ ```ts
86
+ import { useViewportStore, useNodeStore, useSelectionStore } from '@visualli/react';
87
+
88
+ // Read viewport
89
+ const { centerX, centerY, zoomLevel } = useViewportStore();
90
+
91
+ // Programmatic zoom
92
+ useViewportStore.getState().setZoom(1.5);
93
+ useViewportStore.getState().setCenter(0, 0);
94
+
95
+ // Read selected node
96
+ const selectedId = useSelectionStore(s => s.selectedId);
97
+ ```
98
+
99
+ ### Hooks
100
+
101
+ ```ts
102
+ import { useViewportNodes } from '@visualli/react';
103
+
104
+ // Get nodes currently visible in the viewport (culled)
105
+ const visible = useViewportNodes(allNodes, /* optional level filter */ 0);
106
+ ```
107
+
108
+ ### Navigation Stack
109
+
110
+ Layer navigation is fully internal but observable via the `onLayerChange` callback. The breadcrumb UI renders automatically — no props required.
111
+
112
+ Drill-in: **double-click** a node that has a child layer.
113
+ Back: click any crumb in the breadcrumb bar, or use `onNavigateBack` exposed by `NavigationStack` directly.
114
+
115
+ ## Extension System 🆕
116
+
117
+ The extension system allows you to inject custom parser middlewares and UI components at runtime without modifying the SDK.
118
+
119
+ ### Basic Setup with Provider
120
+
121
+ ```tsx
122
+ import { VisualliProvider, VisualliCanvas } from '@visualli/react';
123
+
124
+ function App() {
125
+ return (
126
+ <VisualliProvider>
127
+ <VisualliCanvas document={document} />
128
+ </VisualliProvider>
129
+ );
130
+ }
131
+
132
+ ### With Custom Middleware
133
+
134
+ ```tsx
135
+ import { VisualliProvider } from '@visualli/react';
136
+ import type { ParserMiddleware } from '@visualli/core';
137
+
138
+ const myMiddleware: ParserMiddleware = (data) => {
139
+ if (data.type === 'extension') {
140
+ return { ...data, enhanced: true };
141
+ }
142
+ return data;
143
+ };
144
+
145
+ <VisualliProvider middlewares={[myMiddleware]}>
146
+ <App />
147
+ </VisualliProvider>
148
+ ```
149
+
150
+ ### With Extension Components
151
+
152
+ ```tsx
153
+ import type { ExtensionComponentProps } from '@visualli/react';
154
+
155
+ function MyExtension({ extension, document }: ExtensionComponentProps) {
156
+ return (
157
+ <div style={{ position: 'absolute', top: 20, right: 20 }}>
158
+ <p>{extension.data?.message}</p>
159
+ </div>
160
+ );
161
+ }
162
+
163
+ const extensions = {
164
+ 'my-ext-id': MyExtension,
165
+ };
166
+
167
+ <VisualliProvider extensions={extensions}>
168
+ <VisualliCanvas document={document} />
169
+ </VisualliProvider>
170
+ ```
171
+
172
+ ### Stream Fetching
173
+
174
+ ```tsx
175
+ import { useVisualliStream, VisualliCanvas } from '@visualli/react';
176
+
177
+ function MindMapViewer({ apiUrl }: { apiUrl: string }) {
178
+ const { document, isLoading, error, progress } = useVisualliStream(apiUrl);
179
+
180
+ if (isLoading) return <div>Loading... {progress}%</div>;
181
+ if (error) return <div>Error: {error.message}</div>;
182
+ if (!document) return null;
183
+
184
+ return <VisualliCanvas document={document} />;
185
+ }
186
+ ```
187
+
188
+ ### Complete Example
189
+
190
+ ```tsx
191
+ import {
192
+ VisualliProvider,
193
+ useVisualliStream,
194
+ VisualliCanvas
195
+ } from '@visualli/react';
196
+ import type {
197
+ ParserMiddleware,
198
+ ExtensionComponentProps
199
+ } from '@visualli/react';
200
+
201
+ // Middleware
202
+ const middleware: ParserMiddleware = (data) => {
203
+ if (data.type === 'extension') {
204
+ return { ...data, processed: true };
205
+ }
206
+ return data;
207
+ };
208
+
209
+ // Extension Component
210
+ function TooltipExtension({ extension }: ExtensionComponentProps) {
211
+ return (
212
+ <div style={{
213
+ position: 'absolute',
214
+ top: 20,
215
+ right: 20,
216
+ background: 'white',
217
+ padding: '12px',
218
+ borderRadius: '8px',
219
+ pointerEvents: 'auto'
220
+ }}>
221
+ {extension.data?.message}
222
+ </div>
223
+ );
224
+ }
225
+
226
+ // App
227
+ function App() {
228
+ const { document, isLoading } = useVisualliStream('/api/mindmap');
229
+
230
+ return (
231
+ <VisualliProvider
232
+ middlewares={[middleware]}
233
+ extensions={{ 'tooltip': TooltipExtension }}
234
+ >
235
+ {isLoading ? <Loading /> : <VisualliCanvas document={document} />}
236
+ </VisualliProvider>
237
+ );
238
+ }
239
+ ```
240
+
241
+ 📚 **See `EXTENSION_GUIDE.md` for comprehensive documentation and examples.**
242
+
243
+ ## Exports
244
+
245
+ ### Component
246
+
247
+ ```ts
248
+ import { VisualliCanvas } from '@visualli/react';
249
+ ```
250
+
251
+ ### Context & Provider 🆕
252
+
253
+ ```ts
254
+ import { VisualliProvider, useVisualli } from '@visualli/react';
255
+ import type {
256
+ VisualliProviderProps,
257
+ VisualliContextValue,
258
+ ExtensionComponentProps,
259
+ ExtensionRegistry
260
+ } from '@visualli/react';
261
+ ```
262
+
263
+ ### Stores
264
+
265
+ ```ts
266
+ import { useNodeStore, useViewportStore, useSelectionStore, useRenderConfigStore } from '@visualli/react';
267
+ ```
268
+
269
+ ### Hooks
270
+
271
+ ```ts
272
+ import { useKonvaRenderer, useKonvaLayerTransition, useViewportNodes } from '@visualli/react';
273
+
274
+ // 🆕 Stream fetching hook
275
+ import { useVisualliStream } from '@visualli/react';
276
+ import type { UseVisualliStreamReturn } from '@visualli/react';
277
+ ```
278
+
279
+ ### Sub-components (composition)
280
+
281
+ ```ts
282
+ import {
283
+ KonvaStage, KonvaNode, KonvaEdge,
284
+ KonvaNodeLayer, KonvaEdgeLayer,
285
+ KonvaContainerLayer,
286
+ NavigationStack, ZoomControls,
287
+ } from '@visualli/react';
288
+ ```
289
+
290
+ ### Utilities
291
+
292
+ ```ts
293
+ import {
294
+ getChildLayerForNode, getConnectionsForLayer,
295
+ calculateFitView, calculateFitZoom, calculateFitCenter,
296
+ } from '@visualli/react';
297
+ ```
298
+
299
+ ### Config helpers
300
+
301
+ ```ts
302
+ import {
303
+ getBlobTypeForLayer, buildBlobPathData, ALL_BLOB_SHAPES,
304
+ computeNodeTextWorldScale, computeNodeTextScreenScale,
305
+ } from '@visualli/react';
306
+ ```
307
+
308
+ ## TypeScript
309
+
310
+ ```jsonc
311
+ // tsconfig.json
312
+ {
313
+ "compilerOptions": {
314
+ "moduleResolution": "bundler",
315
+ "jsx": "react-jsx",
316
+ "target": "ES2020"
317
+ }
318
+ }
319
+ ```
320
+
321
+ ## Requirements
322
+
323
+ - Node.js ≥ 22
324
+ - React 18
325
+ - `@visualli/core` must be built (`npm run build` in `sdk-core/`) before typechecking
326
+
327
+ ## Typecheck
328
+
329
+ ```bash
330
+ # Build core first
331
+ cd ../sdk-core && npm run build
332
+
333
+ # Typecheck react package
334
+ cd ../sdk-react && npx tsc --noEmit
335
+ ```