@squeed/flow-sdk 0.1.16 → 2.0.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.
package/README.md CHANGED
@@ -1,151 +1,184 @@
1
1
  # @squeed/flow-sdk
2
2
 
3
- Standalone React component for rendering interactive flow diagrams from JSON.
3
+ Interactive React flow diagrams from JSON, using DOM nodes, SVG edges, and Dagre layout. Cytoscape is not included.
4
4
 
5
5
  ## Install
6
6
 
7
7
  ```bash
8
- npm install @squeed/flow-sdk
8
+ npm install @squeed/flow-sdk@^2 react@^18 react-dom@^18
9
9
  ```
10
10
 
11
- Peer dependencies: `react`, `react-dom`, `@chakra-ui/react`, `@chakra-ui/icons`, `@emotion/react`, `@emotion/styled`, `framer-motion`
11
+ Requires React 18 and Chakra UI 3.36 or later. Peer dependencies: `react`, `react-dom`, `@chakra-ui/react`, `@emotion/react`, `@emotion/styled`, and `framer-motion` (11 or 12). npm installs the remaining peers automatically. React 19 is not currently supported.
12
12
 
13
13
  ## Quick Start
14
14
 
15
15
  ```tsx
16
- import { FlowDiagram } from "@squeed/flow-sdk";
16
+ import { ChakraProvider, defaultSystem } from "@chakra-ui/react";
17
+ import { useState } from "react";
18
+ import {
19
+ FlowDiagram,
20
+ type FlowDirection,
21
+ type SqueedJson,
22
+ } from "@squeed/flow-sdk";
17
23
 
18
24
  function App() {
25
+ const [doc, setDoc] = useState<SqueedJson>({
26
+ service: { $label: "Service", $collapsed: false, worker: {} },
27
+ database: { $label: "Database" },
28
+ });
29
+ const [direction, setDirection] = useState<FlowDirection>("LR");
30
+
19
31
  return (
20
- <FlowDiagram
21
- json={{ name: "Hello", version: "1.0", nested: { key: "value" } }}
22
- title="My Diagram"
23
- config={{ direction: "LR", colorMode: "dark", themeColor: "blue.500" }}
24
- />
32
+ <ChakraProvider value={defaultSystem}>
33
+ <div style={{ height: 600 }}>
34
+ <FlowDiagram
35
+ json={doc}
36
+ title="My Diagram"
37
+ config={{ direction, editable: true, themeColor: "blue.500" }}
38
+ callbacks={{ onJsonChange: setDoc, onDirectionChange: setDirection }}
39
+ />
40
+ </div>
41
+ </ChakraProvider>
25
42
  );
26
43
  }
27
44
  ```
28
45
 
29
- ## Props
46
+ The container must have a height. Styles are injected automatically; no CSS import is required. Keep your existing Chakra provider if the application already has one.
30
47
 
31
- ### `FlowDiagramProps`
48
+ `json` accepts a parsed object or array. Edits produce a new document via `onJsonChange(next, change)`; store `next` and pass it back. Granular callbacks still fire for notifications, but should not apply the same document edit again. With `nodes`/`edges` instead, the consumer owns graph updates and collapse filtering.
32
49
 
33
- | Prop | Type | Description |
34
- |------|------|-------------|
35
- | `json` | `any` | JSON object or string to render as a flow diagram |
36
- | `title` | `string` | Title shown on the root node (default: `"untitled"`) |
37
- | `nodes` | `FlowDiagramNode[]` | Pre-computed nodes (alternative to `json`) |
38
- | `edges` | `FlowDiagramEdge[]` | Pre-computed edges (alternative to `json`) |
39
- | `config` | `FlowDiagramConfig` | Diagram configuration |
40
- | `callbacks` | `FlowDiagramCallbacks` | Event callbacks |
41
- | `showBottomBar` | `boolean` | Show the direction/mode bottom bar (default: `true`) |
42
- | `className` | `string` | CSS class name |
43
- | `style` | `CSSProperties` | Inline styles |
50
+ Editing a plain-string node label updates that string in place, preserving its JSON address and connections. Object-node labels continue to use `$label`.
44
51
 
45
- You can provide either `json` (auto-generates nodes/edges) or `nodes` + `edges` (pre-computed).
52
+ ## Migrating From v1
46
53
 
47
- ## Config
54
+ **v2 is a breaking release for consumers of the Cytoscape instance.** `onCyInit` has been removed. Use `onInit(engine)` to capture a `FlowEngine`; it is not a Cytoscape-compatible object.
48
55
 
49
- ### `FlowDiagramConfig`
56
+ | v1 | v2 |
57
+ | ------------------------------------------------- | ----------------------------------------------------------------------------------- |
58
+ | `onCyInit(cy)` | `onInit(engine)` |
59
+ | `cy.zoom()` / `cy.zoom(level)` | `engine.zoom()` / `engine.zoom(level)` |
60
+ | `cy.pan()` / `cy.pan(position)` | `engine.pan()` / `engine.pan(position)` |
61
+ | `cy.zoom({ level, renderedPosition })` | `engine.zoomAt(level, renderedPosition)` |
62
+ | `cy.fit(undefined, 50)` / `cy.center()` | `engine.fit(50)` / `engine.center()` |
63
+ | `cy.on("pan zoom", ...)` | `callbacks.onViewportChange` or `engine.onViewportChange(listener)` |
64
+ | `cy.nodes()`, `cy.edges()`, `cy.style()`, plugins | Update the JSON, `nodes`/`edges`, or SDK config; no Cytoscape equivalent is exposed |
50
65
 
51
- | Option | Type | Default | Description |
52
- |--------|------|---------|-------------|
53
- | `direction` | `"LR" \| "RL" \| "TB" \| "BT"` | `"LR"` | Layout direction |
54
- | `layout` | `"dagre" \| "elk" \| "tidytree" \| "concentric" \| "cose"` | `"dagre"` | Layout algorithm |
55
- | `themeColor` | `string` | `"blue.500"` | Accent color (Chakra token) |
56
- | `colorMode` | `"light" \| "dark"` | `"light"` | Color mode |
57
- | `backgroundPattern` | `"dot" \| "grid" \| "steel" \| "none"` | `"dot"` | Background pattern |
58
- | `backgroundColor` | `string` | — | Background color override |
59
- | `edgeColor` | `string` | — | Edge/line color override |
60
- | `minZoom` | `number` | `0.01` | Minimum zoom level |
61
- | `maxZoom` | `number` | `1.5` | Maximum zoom level |
62
- | `initialViewport` | `{ pan: { x, y }, zoom }` | — | Initial viewport |
63
- | `edgeHandles` | `boolean` | `true` | Enable edge drawing handles |
64
- | `readOnly` | `boolean` | `false` | Disable all interactions |
65
- | `editable` | `boolean` | `false` | Enable inline node label editing |
66
- | `autoCollapse` | `boolean \| AutoCollapseConfig` | — | Auto-collapse deep nodes for large data |
67
- | `nodeOverlayActions` | `NodeOverlayAction[]` | — | Actions in node hover overlay |
68
- | `renderNodeOverlay` | `(props: NodeOverlayProps) => ReactNode` | — | Custom node hover overlay |
69
- | `renderSelectedNodeToolbar` | `(props: SelectedNodeToolbarProps) => ReactNode` | — | Custom multi-select toolbar |
70
-
71
- ## Auto-Collapse
72
-
73
- For large JSON structures, enable auto-collapse to keep the diagram manageable. Nodes are collapsed at every Nth depth level when the data exceeds size thresholds.
66
+ `engine.getNodeBox(id)` returns `{ x, y, w, h }` in model units after layout, or `undefined` before placement. `clientToModel(clientX, clientY)` and `modelToClient({ x, y })` convert coordinates. `onViewportChange(listener)` returns an unsubscribe function.
74
67
 
75
- ```tsx
76
- // Use defaults (800+ lines AND 5+ depth triggers collapse every 3 levels)
77
- <FlowDiagram json={largeJson} config={{ autoCollapse: true }} />
68
+ Direction, selection, drawing, editing, collapse, and custom render callbacks remain available. Internal Cytoscape DOM classes are gone. SVG routing and text metrics can differ from the old canvas renderer; exact pixel parity is not guaranteed. `initialViewport` is now respected instead of being overwritten by automatic fitting.
78
69
 
79
- // Custom thresholds
80
- <FlowDiagram
81
- json={largeJson}
82
- config={{
83
- autoCollapse: {
84
- minLines: 200,
85
- minDepth: 3,
86
- collapseEveryNLevels: 2,
87
- },
88
- }}
89
- />
90
- ```
70
+ ## Props
91
71
 
92
- ### `AutoCollapseConfig`
72
+ ### `FlowDiagramProps`
73
+
74
+ | Prop | Type | Description |
75
+ | --------------- | ---------------------- | ------------------------------------------------------------------ |
76
+ | `json` | `SqueedJson` | Parsed JSON object or array; takes precedence over `nodes`/`edges` |
77
+ | `title` | `string` | Title shown on the root node (default: `"untitled"`) |
78
+ | `nodes` | `FlowDiagramNode[]` | Pre-computed nodes (alternative to `json`) |
79
+ | `edges` | `FlowDiagramEdge[]` | Pre-computed edges (alternative to `json`) |
80
+ | `config` | `FlowDiagramConfig` | Diagram configuration |
81
+ | `callbacks` | `FlowDiagramCallbacks` | Event callbacks |
82
+ | `showBottomBar` | `boolean` | Show the direction/mode bottom bar (default: `true`) |
83
+ | `className` | `string` | CSS class name |
84
+ | `style` | `CSSProperties` | Inline styles |
85
+
86
+ You can provide either `json` (auto-generates nodes/edges) or `nodes` + `edges` (pre-computed).
87
+
88
+ ## Config
93
89
 
94
- | Option | Type | Default | Description |
95
- |--------|------|---------|-------------|
96
- | `minLines` | `number` | `800` | Min JSON lines to trigger |
97
- | `minDepth` | `number` | `5` | Min depth to trigger |
98
- | `collapseEveryNLevels` | `number` | `3` | Collapse every Nth depth level |
90
+ ### `FlowDiagramConfig`
99
91
 
100
- Both `minLines` and `minDepth` must be exceeded for auto-collapse to apply.
92
+ | Option | Type | Default | Description |
93
+ | ----------------------------------------------------------------------- | ------------------------------------------------ | ------------ | -------------------------------------------------------------------------------------- |
94
+ | `direction` | `"LR" \| "RL" \| "TB" \| "BT" \| "C"` | `"LR"` | Layout direction; `C` splits the JSON root's branches |
95
+ | `nodeAlignment` | `"leading" \| "center" \| "trailing"` | `"center"` | Align unequal node sizes within each layout rank |
96
+ | `edgeMode` | `"workflow" \| "properties"` | `"workflow"` | Attach edges to node boxes or named object-property rows; saved mappings are unchanged |
97
+ | `layout` | `LayoutAlgorithm` | `"dagre"` | Only Dagre is implemented; other legacy values do not select a different layout |
98
+ | `themeColor` | `string` | `"blue.500"` | Accent color (Chakra token) |
99
+ | `colorMode` | `"light" \| "dark"` | `"light"` | Color mode |
100
+ | `backgroundPattern` | `"dot" \| "grid" \| "steel" \| "none"` | `"dot"` | Background pattern |
101
+ | `backgroundColor` | `string` | — | Background color override |
102
+ | `edgeColor` | `string` | — | Edge/line color override |
103
+ | `minZoom` | `number` | `0.01` | Minimum zoom level |
104
+ | `maxZoom` | `number` | `1.5` | Maximum zoom level |
105
+ | `initialViewport` | `{ pan: { x, y }, zoom }` | — | Initial viewport; omitted means fit on first layout |
106
+ | `edgeHandles` | `boolean` | — | `false` disables edge drawing; `true` also starts in draw mode |
107
+ | `readOnly` | `boolean` | `false` | Disable graph gestures and edits; toolbar navigation remains available |
108
+ | `editable` | `boolean` | `false` | Enable inline node label editing |
109
+ | `parentNodeStyle` | `ParentNodeStyle` | — | Compound-node fill, border, padding, and label defaults |
110
+ | `selectionToolbar` | `SelectionToolbarConfig \| false` | — | Customize palette and hidden controls, or disable selection tools |
111
+ | `nodeOverlayActions` | `NodeOverlayAction[]` | — | Actions in node hover overlay |
112
+ | `renderNodeOverlay` | `(props: NodeOverlayProps) => ReactNode` | — | Custom node hover overlay |
113
+ | `renderSelectedNodeToolbar` | `(props: SelectedNodeToolbarProps) => ReactNode` | — | Custom multi-select toolbar |
114
+ | `renderSelectedEdgeToolbar` | `(props: SelectedEdgeToolbarProps) => ReactNode` | — | Custom edge selection toolbar |
115
+ | `renderBottomBar` | `(props: BottomBarRenderProps) => ReactNode` | — | Replace the bottom bar |
116
+ | `customViews` | `Record<string, ComponentType>` | — | Override node views |
117
+ | `disableKeyboardShortcuts` | `boolean` | `false` | Disable built-in keyboard shortcuts |
118
+ | `isZoomMode`, `isMultiSelectMode`, `isDragMode`, `isEdgeHandlesEnabled` | `boolean` | — | Externally controlled interaction modes |
119
+
120
+ Use `$collapsed: true` to collapse a JSON branch, and `false` to keep the expansion control. Automatic depth-based collapse is not part of the current API.
121
+
122
+ `nodeAlignment: "leading"` aligns left edges in `LR`/`C`, right edges in `RL`, top edges in `TB`, and bottom edges in `BT`. `"trailing"` aligns the opposite edge. Node dimensions and graph connections are unchanged; containers and edge paths follow the aligned positions.
101
123
 
102
124
  ## Callbacks
103
125
 
104
126
  ### Node Events
105
127
 
106
- | Callback | Signature | Description |
107
- |----------|-----------|-------------|
108
- | `onNodeSelect` | `(nodeId, nodeData) => void` | Node clicked/selected |
109
- | `onNodeDeselect` | `() => void` | Selection cleared |
110
- | `onNodeDoubleClick` | `(nodeId, nodeData) => void` | Node double-clicked |
111
- | `onNodeCreate` | `(params) => void` | Node created via edge drawing into empty space |
112
- | `onNodeDelete` | `(nodeId, nodeData) => void` | Node deleted |
113
- | `onNodeLabelChange` | `(nodeId, newLabel) => void` | Node label edited inline |
114
- | `onNodeCollapse` | `(nodeId, collapsed) => void` | Collapse toggle clicked |
115
- | `onNodeColorChange` | `(nodeId, colorType, color) => void` | Node color changed via toolbar |
116
- | `onNodeHoverAction` | `(actionId, nodeId, nodeData) => void` | Hover overlay action clicked |
128
+ | Callback | Signature | Description |
129
+ | ------------------- | -------------------------------------- | ---------------------------------------------- |
130
+ | `onNodeSelect` | `(nodeId, nodeData) => void` | Node clicked/selected |
131
+ | `onNodeDeselect` | `() => void` | Selection cleared |
132
+ | `onNodeDoubleClick` | `(nodeId, nodeData) => void` | Node double-clicked |
133
+ | `onNodeCreate` | `(params) => void` | Node created via edge drawing into empty space |
134
+ | `onNodeDelete` | `(nodeId, nodeData) => void` | Node deleted |
135
+ | `onNodeLabelChange` | `(nodeId, newLabel) => void` | Node label edited inline |
136
+ | `onNodeCollapse` | `(nodeId, collapsed) => void` | Collapse toggle clicked |
137
+ | `onNodeColorChange` | `(nodeId, colorType, color) => void` | Node color changed via toolbar |
138
+ | `onNodeIconChange` | `(nodeId, iconName) => void` | Node icon changed via toolbar |
139
+ | `onNodeHoverAction` | `(actionId, nodeId, nodeData) => void` | Hover overlay action clicked |
117
140
 
118
141
  ### Edge Events
119
142
 
120
- | Callback | Signature | Description |
121
- |----------|-----------|-------------|
122
- | `onEdgeCreate` | `(sourceId, targetId) => void` | New edge drawn between nodes |
123
- | `onEdgeSelect` | `(edgeId, edgeData) => void` | Edge clicked/selected |
124
- | `onEdgeDeselect` | `() => void` | Edge selection cleared |
125
- | `onEdgeRemove` | `(edgeId, edgeData) => void` | Edge removed |
126
- | `onEdgeColorChange` | `(edgeId, color) => void` | Edge color changed |
143
+ | Callback | Signature | Description |
144
+ | ------------------- | --------------------------------------------------------------- | -------------------------------------------------------------------------- |
145
+ | `onEdgeCreate` | `(sourceId, targetId, properties?: FlowEdgeProperties) => void` | New edge drawn; optional `sourceProperty` / `targetProperty` identify rows |
146
+ | `onEdgeSelect` | `(edgeId, edgeData) => void` | Edge clicked/selected |
147
+ | `onEdgeDeselect` | `() => void` | Edge selection cleared |
148
+ | `onEdgeRemove` | `(edgeId, edgeData) => void` | Edge removed |
149
+ | `onEdgeDelete` | `(edgeId, edgeData) => void` | User requested deletion |
150
+ | `onEdgeColorChange` | `(edgeId, color) => void` | Edge color changed |
151
+ | `onEdgeLabelChange` | `(edgeId, label) => void` | Edge label changed |
152
+ | `onEdgeStyleChange` | `(edgeId, "solid" \| "dashed" \| "animated") => void` | Edge style changed |
127
153
 
128
154
  ### Diagram Events
129
155
 
130
- | Callback | Signature | Description |
131
- |----------|-----------|-------------|
132
- | `onViewportChange` | `(viewport) => void` | Pan/zoom changed |
133
- | `onDirectionChange` | `(direction) => void` | Direction changed via bottom bar |
134
- | `onArrayModeChange` | `(isArrayMode) => void` | Array/set mode toggled |
135
- | `onCyInit` | `(cy) => void` | Cytoscape instance initialized |
156
+ | Callback | Signature | Description |
157
+ | ------------------- | ------------------------------------------------------ | --------------------------------------------------------------------- |
158
+ | `onViewportChange` | `(viewport) => void` | Pan/zoom changed |
159
+ | `onDirectionChange` | `(direction) => void` | Direction changed via bottom bar |
160
+ | `onArrayModeChange` | `(isArrayMode) => void` | Array/set mode toggled |
161
+ | `onInit` | `(engine: FlowEngine) => void` | Renderer handle initialized; node boxes become available after layout |
162
+ | `onJsonChange` | `(next: SqueedJson, change: SqueedChangeInfo) => void` | Immutable document update |
136
163
 
137
164
  ## JSON Conventions
138
165
 
139
166
  Special `$`-prefixed keys control node rendering:
140
167
 
141
- | Key | Description |
142
- |-----|-------------|
143
- | `$label` | Display label for the node |
144
- | `$bgColor` | Background color (Chakra token, e.g. `"blue.500"`) |
145
- | `$textColor` | Text color |
146
- | `$collapsed` | `true` to collapse children, `false` to force expanded |
147
- | `$parent` | Parent node ID (creates compound/container nodes) |
148
- | `$` | Array value (creates array child list, e.g. `{ $: ["a", "b"] }`) |
168
+ | Key | Description |
169
+ | ----------------------------------- | ------------------------------------------------------------------------------- |
170
+ | `$label` | Display label for the node |
171
+ | `$bgColor` | Background color (Chakra token, e.g. `"blue.500"`) |
172
+ | `$textColor` | Text color |
173
+ | `$icon` | React Icons name, e.g. `PiGear` |
174
+ | `$collapsed` | `true` to collapse children, `false` to force expanded |
175
+ | `$parent` | Parent node ID (creates compound/container nodes) |
176
+ | `$target` | Cross-link to a JSON address, e.g. `root.database`; one per source node |
177
+ | `$connections` | `SqueedConnection[]`; multiple node/property mappings, independent of `$target` |
178
+ | `$sourceLabel` / `$targetLabel` | Hierarchy-edge / cross-link label |
179
+ | `$edgeColor` | Edge color |
180
+ | `$isEdgeDashed` / `$isEdgeAnimated` | Dashed or animated edges |
181
+ | `$` | Array value (creates array child list, e.g. `{ $: ["a", "b"] }`) |
149
182
 
150
183
  ```json
151
184
  {
@@ -163,76 +196,157 @@ Special `$`-prefixed keys control node rendering:
163
196
  }
164
197
  ```
165
198
 
166
- ## Exported Utilities
199
+ ### Property Connections
200
+
201
+ Store mappings on the source node. Property references are exact row keys, while `target` is the destination node's JSON address:
202
+
203
+ ```json
204
+ {
205
+ "backend": {
206
+ "runtime": "Node",
207
+ "api": "REST",
208
+ "$connections": [
209
+ {
210
+ "sourceProperty": "api",
211
+ "target": "root.handlers",
212
+ "targetProperty": "http"
213
+ },
214
+ {
215
+ "sourceProperty": "runtime",
216
+ "target": "root.handlers",
217
+ "targetProperty": "ws"
218
+ }
219
+ ]
220
+ },
221
+ "handlers": { "http": "express", "ws": "socket.io" }
222
+ }
223
+ ```
167
224
 
168
225
  ```tsx
169
- import { createFlowDiagram, getJsonSize, applyAutoCollapse } from "@squeed/flow-sdk";
226
+ <FlowDiagram
227
+ json={doc}
228
+ config={{ edgeMode: "properties" }}
229
+ callbacks={{ onJsonChange: setDoc }}
230
+ />
231
+ ```
170
232
 
171
- // Generate nodes/edges from JSON manually
172
- const { nodes, edges } = createFlowDiagram(json, "root", [], [], "Title", {
173
- hideDefaultObject: false,
174
- sprtObjctKy: false,
175
- isCntr: false,
176
- });
233
+ `"workflow"` attaches the same edges to node boxes; `"properties"` attaches to rows. Switching modes does not change the document or graph connections. Edges without property references keep their ordinary node attachment. Row ports stay left/right, reversed in `RL`, even for vertical layouts.
234
+
235
+ Drag an output port to an input port or destination row to append a mapping. Property drags into empty space cancel. Existing node-level drawing still writes `$target`. The edge toolbar edits/deletes only the selected mapping. In document mode, store the full `onJsonChange` document; in raw `nodes`/`edges` mode, store the optional third `onEdgeCreate` argument in the edge's `data`.
177
236
 
178
- // Measure JSON complexity
179
- const { lines, depth, nodes: nodeCount } = getJsonSize(json);
237
+ Each entry accepts `target`, optional `sourceProperty`/`targetProperty`, `id`, `label`, `color` (Chakra token or hex), and `style: "solid" | "dashed" | "animated"`. Drag-created mappings receive IDs and duplicate endpoint mappings are not appended. Supply explicit unique IDs when declaring otherwise identical mappings that need distinct identities.
238
+
239
+ Missing or unrendered rows fall back to the corresponding node endpoint. Missing/collapsed nodes hide their edges without deleting the saved mappings. Custom views can expose rows with `data-flow-property={key}`; their positions are measured automatically. `readOnly` and `edgeHandles: false` disable port dragging.
240
+
241
+ Mappings are metadata. The SDK does not execute workflows or transfer data.
242
+
243
+ ## Exported Utilities
244
+
245
+ ```tsx
246
+ import { compileSqueed, setNodeProp, applyCollapsed } from "@squeed/flow-sdk";
180
247
 
181
- // Apply auto-collapse manually
182
- const collapsed = applyAutoCollapse(structuredClone(json), 3); // collapse every 3 levels
248
+ const next = setNodeProp(json, "root.service", "$label", "API");
249
+ const { nodes, edges } = compileSqueed(next, {
250
+ title: "Services",
251
+ isCenter: false,
252
+ });
253
+ const visible = applyCollapsed(handBuiltNodes, handBuiltEdges);
183
254
  ```
184
255
 
256
+ `createFlowDiagram` remains exported as the lower-level compiler. Use `compileSqueed` for stable edge IDs, document styling, and `$connections`. `addConnection(json, sourceAddress, connection)` appends a mapping immutably. Hierarchy edges are removed by removing the child; `$target` links and individual `$connections` can be deleted independently.
257
+
185
258
  ## Custom Overlays
186
259
 
187
260
  ### Node Hover Overlay
188
261
 
189
262
  ```tsx
263
+ import { HStack, IconButton } from "@chakra-ui/react";
264
+ import { PiPencilSimple, PiTrash } from "react-icons/pi";
265
+
190
266
  <FlowDiagram
191
267
  json={data}
192
268
  config={{
193
269
  renderNodeOverlay: ({ nodeId, nodeData, isRoot }) => (
194
- <HStack bg="gray.800" px={2} py={1} borderRadius={15}>
195
- <IconButton icon={<EditIcon />} size="xs" onClick={() => edit(nodeId)} />
196
- {!isRoot && <IconButton icon={<DeleteIcon />} size="xs" onClick={() => remove(nodeId)} />}
270
+ <HStack bg="gray.800" px={2} py={1} borderRadius={8}>
271
+ <IconButton
272
+ aria-label="Edit node"
273
+ size="xs"
274
+ onClick={() => edit(nodeId)}
275
+ >
276
+ <PiPencilSimple />
277
+ </IconButton>
278
+ {!isRoot && (
279
+ <IconButton
280
+ aria-label="Delete node"
281
+ size="xs"
282
+ onClick={() => remove(nodeId)}
283
+ >
284
+ <PiTrash />
285
+ </IconButton>
286
+ )}
197
287
  </HStack>
198
288
  ),
199
289
  }}
200
- />
290
+ />;
201
291
  ```
202
292
 
203
293
  ### Selection Toolbar
204
294
 
205
295
  ```tsx
296
+ import { HStack, IconButton, Text } from "@chakra-ui/react";
297
+ import { PiX } from "react-icons/pi";
298
+
206
299
  <FlowDiagram
207
300
  json={data}
208
301
  config={{
209
302
  renderSelectedNodeToolbar: ({ selectedNodeIds, onDeselect }) => (
210
- <HStack bg="gray.800" px={3} py={2} borderRadius={15}>
303
+ <HStack bg="gray.800" px={3} py={2} borderRadius={8}>
211
304
  <Text fontSize="xs">{selectedNodeIds.length} selected</Text>
212
- <IconButton icon={<CloseIcon />} size="xs" onClick={onDeselect} />
305
+ <IconButton aria-label="Clear selection" size="xs" onClick={onDeselect}>
306
+ <PiX />
307
+ </IconButton>
213
308
  </HStack>
214
309
  ),
215
310
  }}
216
- />
311
+ />;
217
312
  ```
218
313
 
219
314
  ## Keyboard Shortcuts
220
315
 
221
- | Key | Action |
222
- |-----|--------|
223
- | `Z` | Toggle zoom mode (scroll to zoom) |
224
- | `M` | Toggle multi-select mode |
225
- | `D` | Toggle drag/pan mode |
316
+ | Key | Action |
317
+ | ---------------------- | --------------------------------- |
318
+ | `Z` | Toggle zoom mode (scroll to zoom) |
319
+ | `M` | Toggle multi-select mode |
320
+ | `D` | Toggle drag/pan mode |
321
+ | `Delete` / `Backspace` | Delete selected non-root nodes |
322
+ | `Escape` | Cancel drawing or a label edit |
323
+
324
+ The canvas supports pointer panning, wheel zoom, and two-finger pinch zoom. Interactive inputs inside nodes retain their own keyboard behavior.
226
325
 
227
326
  ## Theme Utilities
228
327
 
229
328
  ```tsx
230
- import { useCustomColors, getChakraColorHex, getColorVariant } from "@squeed/flow-sdk";
329
+ import {
330
+ useCustomColors,
331
+ getChakraColorHex,
332
+ getColorVariant,
333
+ } from "@squeed/flow-sdk";
231
334
 
232
335
  const { background, colorText, border } = useCustomColors();
233
336
  const hex = getChakraColorHex("blue.500"); // → "#3182ce"
234
337
  ```
235
338
 
339
+ ## Development Checks
340
+
341
+ ```bash
342
+ npm ci
343
+ npx playwright install chromium
344
+ npm run check:release
345
+ npm pack --dry-run
346
+ ```
347
+
348
+ The release check runs unit tests, browser tests against the built ESM package, a clean tarball installation with ESM/CommonJS type and runtime checks, and the production dependency audit. Packing rebuilds the artifacts. React, Chakra, Emotion, Framer Motion, and React Icons are external to the SDK bundle; application download size includes whichever peers and icon libraries it uses.
349
+
236
350
  ---
237
351
 
238
352
  Author: **seyi ogunbowale**
@@ -0,0 +1,120 @@
1
+ Third-party software included in @squeed/flow-sdk
2
+
3
+ The SDK is licensed under the terms in LICENSE. The following notices apply
4
+ to bundled dependencies and adapted source code. React, Chakra UI, Emotion,
5
+ Framer Motion, and React Icons are external dependencies with their own licenses.
6
+
7
+ ===========================================================================
8
+ Cytoscape.js - adapted edge geometry in src/engine/geometry.ts and EdgeView.tsx
9
+ https://github.com/cytoscape/cytoscape.js
10
+
11
+ Copyright (c) 2016-2025, The Cytoscape Consortium.
12
+
13
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
14
+ this software and associated documentation files (the "Software"), to deal in
15
+ the Software without restriction, including without limitation the rights to
16
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
17
+ of the Software, and to permit persons to whom the Software is furnished to do
18
+ so, subject to the following conditions:
19
+
20
+ The above copyright notice and this permission notice shall be included in all
21
+ copies or substantial portions of the Software.
22
+
23
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29
+ SOFTWARE.
30
+
31
+ ===========================================================================
32
+ Dagre and Graphlib
33
+ https://github.com/dagrejs/dagre
34
+ https://github.com/dagrejs/graphlib
35
+
36
+ Copyright (c) 2012-2014 Chris Pettitt
37
+
38
+ Permission is hereby granted, free of charge, to any person obtaining a copy
39
+ of this software and associated documentation files (the "Software"), to deal
40
+ in the Software without restriction, including without limitation the rights
41
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
42
+ copies of the Software, and to permit persons to whom the Software is
43
+ furnished to do so, subject to the following conditions:
44
+
45
+ The above copyright notice and this permission notice shall be included in
46
+ all copies or substantial portions of the Software.
47
+
48
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
49
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
50
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
51
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
52
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
53
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
54
+ THE SOFTWARE.
55
+
56
+ ===========================================================================
57
+ Lodash and Lodash ES
58
+ https://github.com/lodash/lodash
59
+
60
+ Copyright OpenJS Foundation and other contributors <https://openjsf.org/>
61
+
62
+ Based on Underscore.js, copyright Jeremy Ashkenas,
63
+ DocumentCloud and Investigative Reporters & Editors <http://underscorejs.org/>
64
+
65
+ This software consists of voluntary contributions made by many
66
+ individuals. For exact contribution history, see the revision history
67
+ available at https://github.com/lodash/lodash
68
+
69
+ The following license applies to all parts of this software except as
70
+ documented below:
71
+
72
+ ====
73
+
74
+ Permission is hereby granted, free of charge, to any person obtaining
75
+ a copy of this software and associated documentation files (the
76
+ "Software"), to deal in the Software without restriction, including
77
+ without limitation the rights to use, copy, modify, merge, publish,
78
+ distribute, sublicense, and/or sell copies of the Software, and to
79
+ permit persons to whom the Software is furnished to do so, subject to
80
+ the following conditions:
81
+
82
+ The above copyright notice and this permission notice shall be
83
+ included in all copies or substantial portions of the Software.
84
+
85
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
86
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
87
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
88
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
89
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
90
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
91
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
92
+
93
+ ====
94
+
95
+ Copyright and related rights for sample code are waived via CC0. Sample
96
+ code is defined as all source code displayed within the prose of the
97
+ documentation.
98
+
99
+ CC0: http://creativecommons.org/publicdomain/zero/1.0/
100
+
101
+ ====
102
+
103
+ Files located in the node_modules and vendor directories are externally
104
+ maintained libraries used by this software which have their own
105
+ licenses; we recommend you read them, as their terms may differ from the
106
+ terms above.
107
+
108
+ ===========================================================================
109
+ UUID
110
+ https://github.com/uuidjs/uuid
111
+
112
+ The MIT License (MIT)
113
+
114
+ Copyright (c) 2010-2020 Robert Kieffer and other contributors
115
+
116
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
117
+
118
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
119
+
120
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.