@pauloricca/spagbol 0.0.0-stage → 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 (159) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +338 -2
  3. package/dist/controls.css +122 -0
  4. package/dist/core/controller.d.ts +89 -0
  5. package/dist/core/controller.d.ts.map +1 -0
  6. package/dist/core/controller.js +732 -0
  7. package/dist/core/controller.js.map +1 -0
  8. package/dist/core/graph.d.ts +30 -0
  9. package/dist/core/graph.d.ts.map +1 -0
  10. package/dist/core/graph.js +318 -0
  11. package/dist/core/graph.js.map +1 -0
  12. package/dist/core/grouping.d.ts +10 -0
  13. package/dist/core/grouping.d.ts.map +1 -0
  14. package/dist/core/grouping.js +130 -0
  15. package/dist/core/grouping.js.map +1 -0
  16. package/dist/core/groups.d.ts +40 -0
  17. package/dist/core/groups.d.ts.map +1 -0
  18. package/dist/core/groups.js +164 -0
  19. package/dist/core/groups.js.map +1 -0
  20. package/dist/core/index.d.ts +8 -0
  21. package/dist/core/index.d.ts.map +1 -0
  22. package/dist/core/index.js +8 -0
  23. package/dist/core/index.js.map +1 -0
  24. package/dist/core/model.d.ts +182 -0
  25. package/dist/core/model.d.ts.map +1 -0
  26. package/dist/core/model.js +149 -0
  27. package/dist/core/model.js.map +1 -0
  28. package/dist/core/observations.d.ts +10 -0
  29. package/dist/core/observations.d.ts.map +1 -0
  30. package/dist/core/observations.js +36 -0
  31. package/dist/core/observations.js.map +1 -0
  32. package/dist/core/serialization.d.ts +9 -0
  33. package/dist/core/serialization.d.ts.map +1 -0
  34. package/dist/core/serialization.js +148 -0
  35. package/dist/core/serialization.js.map +1 -0
  36. package/dist/index.d.ts +4 -0
  37. package/dist/index.d.ts.map +1 -0
  38. package/dist/index.js +4 -0
  39. package/dist/index.js.map +1 -0
  40. package/dist/link-controls.css +123 -0
  41. package/dist/react/Controls.d.ts +18 -0
  42. package/dist/react/Controls.d.ts.map +1 -0
  43. package/dist/react/Controls.js +301 -0
  44. package/dist/react/Controls.js.map +1 -0
  45. package/dist/react/SpagbolCanvas.d.ts +38 -0
  46. package/dist/react/SpagbolCanvas.d.ts.map +1 -0
  47. package/dist/react/SpagbolCanvas.js +1248 -0
  48. package/dist/react/SpagbolCanvas.js.map +1 -0
  49. package/dist/react/SpagbolCanvasControls.d.ts +36 -0
  50. package/dist/react/SpagbolCanvasControls.d.ts.map +1 -0
  51. package/dist/react/SpagbolCanvasControls.js +113 -0
  52. package/dist/react/SpagbolCanvasControls.js.map +1 -0
  53. package/dist/react/SpagbolCanvasFlow.d.ts +14 -0
  54. package/dist/react/SpagbolCanvasFlow.d.ts.map +1 -0
  55. package/dist/react/SpagbolCanvasFlow.js +6 -0
  56. package/dist/react/SpagbolCanvasFlow.js.map +1 -0
  57. package/dist/react/SpagbolCanvasLinkControls.d.ts +16 -0
  58. package/dist/react/SpagbolCanvasLinkControls.d.ts.map +1 -0
  59. package/dist/react/SpagbolCanvasLinkControls.js +7 -0
  60. package/dist/react/SpagbolCanvasLinkControls.js.map +1 -0
  61. package/dist/react/SpagbolCanvasMenu.d.ts +12 -0
  62. package/dist/react/SpagbolCanvasMenu.d.ts.map +1 -0
  63. package/dist/react/SpagbolCanvasMenu.js +65 -0
  64. package/dist/react/SpagbolCanvasMenu.js.map +1 -0
  65. package/dist/react/SpagbolControlNumber.d.ts +25 -0
  66. package/dist/react/SpagbolControlNumber.d.ts.map +1 -0
  67. package/dist/react/SpagbolControlNumber.js +209 -0
  68. package/dist/react/SpagbolControlNumber.js.map +1 -0
  69. package/dist/react/SpagbolControlNumberLink.d.ts +6 -0
  70. package/dist/react/SpagbolControlNumberLink.d.ts.map +1 -0
  71. package/dist/react/SpagbolControlNumberLink.js +159 -0
  72. package/dist/react/SpagbolControlNumberLink.js.map +1 -0
  73. package/dist/react/SpagbolControlZoom.d.ts +11 -0
  74. package/dist/react/SpagbolControlZoom.d.ts.map +1 -0
  75. package/dist/react/SpagbolControlZoom.js +24 -0
  76. package/dist/react/SpagbolControlZoom.js.map +1 -0
  77. package/dist/react/SpagbolNodeFrame.d.ts +35 -0
  78. package/dist/react/SpagbolNodeFrame.d.ts.map +1 -0
  79. package/dist/react/SpagbolNodeFrame.js +171 -0
  80. package/dist/react/SpagbolNodeFrame.js.map +1 -0
  81. package/dist/react/SpagbolNodeFrameTypePicker.d.ts +9 -0
  82. package/dist/react/SpagbolNodeFrameTypePicker.d.ts.map +1 -0
  83. package/dist/react/SpagbolNodeFrameTypePicker.js +68 -0
  84. package/dist/react/SpagbolNodeFrameTypePicker.js.map +1 -0
  85. package/dist/react/SpagbolNodeGroupPort.d.ts +15 -0
  86. package/dist/react/SpagbolNodeGroupPort.d.ts.map +1 -0
  87. package/dist/react/SpagbolNodeGroupPort.js +62 -0
  88. package/dist/react/SpagbolNodeGroupPort.js.map +1 -0
  89. package/dist/react/SpagbolNodeGroupPortAdd.d.ts +5 -0
  90. package/dist/react/SpagbolNodeGroupPortAdd.d.ts.map +1 -0
  91. package/dist/react/SpagbolNodeGroupPortAdd.js +27 -0
  92. package/dist/react/SpagbolNodeGroupPortAdd.js.map +1 -0
  93. package/dist/react/context.d.ts +84 -0
  94. package/dist/react/context.d.ts.map +1 -0
  95. package/dist/react/context.js +43 -0
  96. package/dist/react/context.js.map +1 -0
  97. package/dist/react/duplicateDrag.d.ts +19 -0
  98. package/dist/react/duplicateDrag.d.ts.map +1 -0
  99. package/dist/react/duplicateDrag.js +71 -0
  100. package/dist/react/duplicateDrag.js.map +1 -0
  101. package/dist/react/flowPresentation.d.ts +11 -0
  102. package/dist/react/flowPresentation.d.ts.map +1 -0
  103. package/dist/react/flowPresentation.js +50 -0
  104. package/dist/react/flowPresentation.js.map +1 -0
  105. package/dist/react/geometry.d.ts +13 -0
  106. package/dist/react/geometry.d.ts.map +1 -0
  107. package/dist/react/geometry.js +43 -0
  108. package/dist/react/geometry.js.map +1 -0
  109. package/dist/react/index.d.ts +11 -0
  110. package/dist/react/index.d.ts.map +1 -0
  111. package/dist/react/index.js +11 -0
  112. package/dist/react/index.js.map +1 -0
  113. package/dist/react/nodeDrag.d.ts +7 -0
  114. package/dist/react/nodeDrag.d.ts.map +1 -0
  115. package/dist/react/nodeDrag.js +27 -0
  116. package/dist/react/nodeDrag.js.map +1 -0
  117. package/dist/react/portState.d.ts +10 -0
  118. package/dist/react/portState.d.ts.map +1 -0
  119. package/dist/react/portState.js +35 -0
  120. package/dist/react/portState.js.map +1 -0
  121. package/dist/react/viewport.d.ts +24 -0
  122. package/dist/react/viewport.d.ts.map +1 -0
  123. package/dist/react/viewport.js +55 -0
  124. package/dist/react/viewport.js.map +1 -0
  125. package/dist/styles.css +748 -0
  126. package/package.json +58 -4
  127. package/src/core/controller.ts +844 -0
  128. package/src/core/graph.ts +451 -0
  129. package/src/core/grouping.ts +159 -0
  130. package/src/core/groups.ts +125 -0
  131. package/src/core/index.ts +7 -0
  132. package/src/core/model.ts +304 -0
  133. package/src/core/observations.ts +31 -0
  134. package/src/core/serialization.ts +184 -0
  135. package/src/index.ts +9 -0
  136. package/src/react/Controls.tsx +355 -0
  137. package/src/react/SpagbolCanvas.tsx +1893 -0
  138. package/src/react/SpagbolCanvasControls.tsx +255 -0
  139. package/src/react/SpagbolCanvasFlow.tsx +38 -0
  140. package/src/react/SpagbolCanvasLinkControls.tsx +21 -0
  141. package/src/react/SpagbolCanvasMenu.tsx +121 -0
  142. package/src/react/SpagbolControlNumber.tsx +297 -0
  143. package/src/react/SpagbolControlNumberLink.tsx +209 -0
  144. package/src/react/SpagbolControlZoom.tsx +102 -0
  145. package/src/react/SpagbolNodeFrame.tsx +330 -0
  146. package/src/react/SpagbolNodeFrameTypePicker.tsx +51 -0
  147. package/src/react/SpagbolNodeGroupPort.tsx +111 -0
  148. package/src/react/SpagbolNodeGroupPortAdd.tsx +48 -0
  149. package/src/react/context.tsx +146 -0
  150. package/src/react/controls.css +122 -0
  151. package/src/react/duplicateDrag.ts +97 -0
  152. package/src/react/flowPresentation.ts +56 -0
  153. package/src/react/geometry.ts +53 -0
  154. package/src/react/index.ts +61 -0
  155. package/src/react/link-controls.css +123 -0
  156. package/src/react/nodeDrag.ts +33 -0
  157. package/src/react/portState.ts +24 -0
  158. package/src/react/styles.css +748 -0
  159. package/src/react/viewport.ts +90 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Paulo Ricca
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,339 @@
1
- # Temporary Holding Version
1
+ # Spagbol
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ An extensible graph editor extracted from the interaction patterns in Teia and visual-visual. This repository is independent of both applications. Teia now consumes the complete shared canvas/controller through a patch codec and domain bindings; Visual Visual consumes the shared canvas/controller through its shader patch codec and domain bindings.
4
+
5
+ Spagbol owns graph editing and presentation. Consumers own their node catalogues, node bodies, execution, persistence services, assets and runtime observations. There is no audio engine, shader compiler, MIDI access or server client in the package.
6
+
7
+ ## Package
8
+
9
+ `@pauloricca/spagbol` contains the complete graph model, registries, atomic edits, undo/redo, graph transformations, nesting, areas, observations and codecs together with the extensible React canvas, node frames/ports and controls. All public model and UI symbols come from the same entry point. Import `@pauloricca/spagbol/styles.css` explicitly.
10
+
11
+ The package is version `0.1.1`, licensed under [MIT](LICENSE). React/React DOM 19 and React Flow 12.11 are peer dependencies. Source remains organised into internal `src/core` and `src/react` modules, with a single build, version and tarball. This is an initial implementation, not a claim of complete Teia interaction parity.
12
+
13
+ ## Docker development
14
+
15
+ Start the library build watchers:
16
+
17
+ ```sh
18
+ docker compose up
19
+ ```
20
+
21
+ The library has no HTTP server or port. Its container builds the package into `dist`, then watches TypeScript and CSS changes. Dependencies live in a Docker volume. Restart Compose after changing package manifests/lockfiles so `npm ci` picks them up. Run the [minimal example](examples/minimal/README.md) to see the UI; its Compose stack includes a library watcher, so running both stacks is unnecessary.
22
+
23
+ Build and run the headless regression suite, without launching a browser or either reference application:
24
+
25
+ ```sh
26
+ docker compose run --rm --build check
27
+ ```
28
+
29
+ Produce local package artifacts:
30
+
31
+ ```sh
32
+ docker compose run --rm check npm run build
33
+ docker compose run --rm check npm run pack:local
34
+ ```
35
+
36
+ The tarball appears in `artifacts/`. It is an ignored build output for local package testing. Both check and development containers mount this checkout; their builds update only this repository's `dist` outputs. No Docker service refers to Teia or visual-visual.
37
+
38
+ ## Runnable example
39
+
40
+ [`examples/minimal`](examples/minimal/README.md) is a React/TypeScript starter: a full-page canvas, four example nodes and one connection, without application chrome. `src/main.tsx` mounts the editor; `src/nodes.tsx` shows custom node definitions and shared controls. It installs the packed library rather than aliasing source.
41
+
42
+ ```sh
43
+ cd examples/minimal
44
+ docker compose up
45
+ ```
46
+
47
+ Open http://localhost:5187. This replaces the separate client checkout. The example lives in this repository and is excluded from the npm package. Remove the sample graph assignments in `src/main.tsx` for an empty canvas.
48
+
49
+ ## Minimal consumer
50
+
51
+ ```tsx
52
+ import { GraphEditor, NodeRegistry, SpagbolCanvas } from "@pauloricca/spagbol";
53
+ import "@pauloricca/spagbol/styles.css";
54
+
55
+ const nodes = new NodeRegistry([
56
+ {
57
+ kind: "value",
58
+ label: "Value",
59
+ createData: () => ({}),
60
+ ports: () => [
61
+ { id: "amount", label: "amount", direction: "input", defaultValue: 0.5 },
62
+ { id: "signal", label: "signal", direction: "output" },
63
+ ],
64
+ },
65
+ ]);
66
+ const editor = new GraphEditor({ nodes });
67
+ editor.addNode("value", { x: 80, y: 100 });
68
+
69
+ // Mount inside a container with an explicit width and height.
70
+ export function Editor() {
71
+ return <SpagbolCanvas editor={editor} />;
72
+ }
73
+ ```
74
+
75
+ Create controllers once, outside render or with a React lazy initializer. A canvas supplies its own React Flow provider. Different canvas instances have independent focus, selection, overlays and render targets. Supply one controller per independently edited document. Rendering the same controller twice intentionally shares its editor session.
76
+
77
+ ## Naming and layout conventions
78
+
79
+ React components follow ownership order: `SpagbolControlSlider` → `spagbol-control-slider`, `SpagbolControlButton` → `spagbol-control-button`, `SpagbolControlNumber` → `spagbol-control-number`, `SpagbolCanvasMenu` → `spagbol-canvas-menu`, and `SpagbolNodeFrame` → `spagbol-node-frame`. Props use the component name plus `Props`. Child/state classes extend the root; host-provided classes are added alongside canonical roots. Third-party React Flow classes remain for interoperability.
80
+
81
+ `spagbol-node-frame-body` has **zero padding**. A body view can add its own padded wrapper when appropriate; sliders and buttons render flush against the node frame. This prerelease replaces the old unnamespaced React exports rather than retaining aliases. Headless model classes and functions keep their model-oriented names.
82
+
83
+ ## Hosts with an existing graph controller
84
+
85
+ `SpagbolCanvasFlow<N, E>` is the controlled integration boundary. It accepts typed React Flow nodes/edges, custom renderer registries, existing interaction callbacks and overlay children, with a configurable shared grid (`grid={false}` disables it). Mount it under the host's React Flow provider. It does not translate host documents, subscribe to a `GraphEditor`, or add edit gestures of its own. The higher-level `SpagbolCanvas` uses this same surface with Spagbol's controller.
86
+
87
+ `SpagbolControlZoom` accepts `zoom`, an optional `onReset` callback and an `extra` slot (for example a host CPU meter); its default layout retains React Flow control classes for existing themes. `SpagbolCanvasMenu` accepts host-owned action children and optional wrapper/menu/trigger classes, sharing focus, hover, keyboard and dismissal mechanics. These primitives need no Spagbol stylesheet when a host supplies their styling. The viewport bounds/clamp helpers and their typed geometry contracts are also public exports. Teia uses this boundary while retaining its specialised renderers and controller.
88
+
89
+ ## Custom nodes
90
+
91
+ Register the domain definition in `NodeRegistry`. Supply its view separately in the React `views` map, keyed by the same kind. `defineNode<T>()` and `defineNodeView<T>()` retain typed domain payloads at those boundaries.
92
+
93
+ ```tsx
94
+ import { defineNodeView, type SpagbolNodeViewProps } from "@pauloricca/spagbol";
95
+
96
+ function Slider({
97
+ node,
98
+ setValue,
99
+ beginGesture,
100
+ endGesture,
101
+ readOnly,
102
+ }: SpagbolNodeViewProps) {
103
+ return (
104
+ <input
105
+ type="range"
106
+ min={0}
107
+ max={1}
108
+ step={0.01}
109
+ value={node.values.amount ?? 0}
110
+ disabled={readOnly}
111
+ onPointerDown={() => beginGesture("Adjust slider")}
112
+ onPointerUp={() => endGesture()}
113
+ onPointerCancel={() => endGesture(true)}
114
+ onBlur={() => endGesture()}
115
+ onChange={(event) => setValue("amount", Number(event.target.value))}
116
+ />
117
+ );
118
+ }
119
+ const views = { value: defineNodeView({ body: Slider }) };
120
+ ```
121
+
122
+ Use `body` to retain the standard frame/ports or `render` to replace the complete node geometry. Complete renderers use the public `SpagbolNodePort` primitive and a `.spagbol-canvas-drag-handle` region. Dynamic port resolvers derive stable IDs from node data. Call `patchData(update, true)` for topology-changing edits; invalid incident links and their dependent modulation chains are removed atomically and restored by undo. SpagbolNodePort labels may change independently of IDs.
123
+
124
+ `SpagbolNodeFrame`, `SpagbolNodePort`, `SpagbolControlNumber`, `SpagbolNodePanel`, `useGraphEditor`, `useSpagbol`, `useEditorSnapshot`, `useObservation` and `useRenderTarget` are public extension primitives. A custom control must stop graph gestures where needed and end/cancel its gesture on focus loss or pointer cancellation. Consumers remain responsible for their node-specific validation and accessible controls.
125
+
126
+ ## Slider and button controls
127
+
128
+ `SpagbolControlSlider` and `SpagbolControlButton` are Teia's extracted control components, used directly by Teia and the minimal client. They retain the original sizing, fill colours and pointer handling, using neutral `spagbol-control-slider`, `spagbol-control-slider-fill` and `spagbol-control-button` CSS classes. The full stylesheet includes them; consumers using their own canvas styles can import only `@pauloricca/spagbol/controls.css`. Existing Teia theme tokens are honoured, with Spagbol-token/default fallbacks for other apps. `--node-display-height` changes their default 48px height.
129
+
130
+ ```tsx
131
+ import {
132
+ SpagbolControlSlider,
133
+ SpagbolControlButton,
134
+ } from "@pauloricca/spagbol";
135
+
136
+ <SpagbolControlSlider
137
+ value={value}
138
+ direction="horizontal"
139
+ onChange={setValue}
140
+ />;
141
+ <SpagbolControlButton
142
+ mode="toggle"
143
+ pressed={pressed}
144
+ onPressedChange={setPressed}
145
+ onClickPulse={trigger}
146
+ />;
147
+ ```
148
+
149
+ The slider accepts normalized values from 0 to 1 and an optional external `displayValue` (for example a runtime observation), plus horizontal/vertical orientation. Cmd/Ctrl uses one-fifth drag speed; Shift+Alt uses one-third speed. Switching modifiers reanchors without snapping. Shift or Alt enables wheel adjustment on the matching axis, with the same precision modifiers and original wheel scaling. It retains Teia's pointer-only focus behaviour.
150
+
151
+ Buttons support `toggle`, `trigger` and `temporary` modes. Toggle changes the pressed value on click; temporary emits 1 on press and 0 on release, cancellation or lost capture; trigger calls `onClickPulse` on press with a 120ms visual flash. External pressed-value rises also flash in trigger mode. Callbacks are controlled by the host: these components do not implement graph execution, MIDI, signal mapping or DSP timing. Consumers decide how values enter their model/history and how read-only UI is enforced.
152
+
153
+ ## Rich links
154
+
155
+ Standard descriptors are `set`, `add`, `multiply` and `bend`, with mode colours, labels, descriptions and shortcuts. Register more with `LinkModeRegistry`, and optionally constrain the offered modes using `policy.modes`. A descriptor does not implement execution: consumers supply their own arithmetic/compiler and capability policy.
156
+
157
+ Each link has a stable ID, a source port, and either a destination port or another link's named property. Teia-style strength connections are enabled by default:
158
+
159
+ ```tsx
160
+ const editor = new GraphEditor({
161
+ nodes,
162
+ });
163
+ <SpagbolCanvas editor={editor} />;
164
+ ```
165
+
166
+ Dragging an output to a cable creates a strength connection. Pin targets take priority. Recursive modulation is supported; property cycles and duplicate endpoint pairs are rejected. Reconnection retains identity, settings and dependent links. The core accepts any explicitly allowed link property; the canvas currently offers cable-body drops for `weight`. Mode arithmetic, numeric bounds and feedback scheduling remain consumer responsibilities. Consumers can register extra modes and supply their own evaluator.
167
+
168
+ To disable strength connections, supply `policy: { linkProperties: [] }`; `features={{ linkProperties: false }}` hides the canvas interaction and presentation. During a connection drag, a collapsed node reveals its ports only while the pointer is over it. The single selected node also reveals its ports, as in Teia.
169
+
170
+ Supply optional `linkPresentation` classifications for feedback/control/virtual drawing. Self links get the feedback geometry automatically. Virtual relations are display-only. Standard connected links support weights, enabling/disabling, mode editing, endpoint reconnection and insertion.
171
+
172
+ ## Canvas controls
173
+
174
+ The default canvas includes a bottom-left zoom stack (in, out, fit), a percentage button that resets zoom to 100%, and bottom-right undo/redo buttons with a **MORE** menu containing the layout lock. Nested graphs also get an exit button and breadcrumbs. These controls use the shared theme and work without app callbacks.
175
+
176
+ Apps supply their own outside buttons and menu entries through `controls`. Saving, loading, transport and other domain actions remain app responsibilities:
177
+
178
+ ```tsx
179
+ <SpagbolCanvas
180
+ editor={editor}
181
+ controls={{
182
+ buttons: [{ id: "save", label: "SAVE", onSelect: saveDocument }],
183
+ menuItems: [
184
+ { id: "settings", label: "SETTINGS", onSelect: openSettings },
185
+ { id: "separator", separator: true },
186
+ {
187
+ id: "guides",
188
+ label: "GUIDES",
189
+ checked: guides,
190
+ onSelect: toggleGuides,
191
+ },
192
+ ],
193
+ }}
194
+ />
195
+ ```
196
+
197
+ Actions accept `ariaLabel`, `title`, `disabled`, optional `checked` state and synchronous or asynchronous callbacks. Pending actions are disabled; failures appear in the controls and can also be handled with `onActionError`. The menu opens on hover, focus or click; arrow keys/Home/End move through enabled entries, and Escape, outside click, focus loss or an action closes it.
198
+
199
+ Use `leading` for host content beside the outside buttons and `zoomExtra` for content beside the zoom stack. `showZoom`, `showHistory`, `showLayoutLock` and `showBreadcrumbs` independently hide built-ins; `controls={false}` hides them all. `SpagbolCanvasControls`, `SpagbolCanvasActions` and `SpagbolCanvasControlsZoom` are exported for composition inside the canvas's provider. The older `toolbar` prop remains available for replacing the entire overlay.
200
+
201
+ ## Optional features
202
+
203
+ The complete canvas enables areas, compact nodes, nesting and projected group controls by default. Feature flags remain available for compatibility:
204
+
205
+ ```tsx
206
+ <SpagbolCanvas
207
+ editor={editor}
208
+ views={views}
209
+ features={{
210
+ compact: true,
211
+ areas: true,
212
+ linkProperties: true,
213
+ nestedGraphs: true,
214
+ panels: true,
215
+ }}
216
+ />
217
+ ```
218
+
219
+ - **Areas:** create, move, resize, title, collapse, clone and freeze membership. Unlocked membership follows top-left containment; locking snapshots node/nested-area membership. A `controlHeight` section stays visible on collapse. Hidden member links are projected to the area's header; internal links are hidden.
220
+ - **Nested graphs:** nodes may own `graph`; navigate through the header arrow and breadcrumb bar. `groupSelection()` uses Spagbol-owned Group, Ins and Outs kinds (`spagbolGroupKinds`), preserving original mode/strength at the destination. Existing custom boundary-kind options remain compatible. Spagbol does not flatten or execute groups.
221
+ - **Shared structure:** an explicit `sharedGraphId` synchronizes edited nested graph contents across linked instances. Each instance retains its own overrides. Cloning an already linked group retains that relationship; nodes without a shared ID remain independent.
222
+ - **Projected panels:** `panelNodeIds` selects nested controls; `overrides` stores instance values independently of shared defaults. `panels` requires `nestedGraphs`. A custom projected/body renderer receives `setValue` mapped to the parent override. Double-click a projected control label to reset it. Numeric and JSON payload overrides are instance-specific, including nested projected controls; double-click the projected header to reset them. UI/Controls areas determine the projected layout, matching Teia.
223
+ - **Layout lock:** `editor.setMode('controls')` hides cables and blocks layout/topology while retaining numeric controls. `'readonly'` also blocks values. This is distinct from area membership locking.
224
+
225
+ ## Interactions
226
+
227
+ | Gesture | Action |
228
+ | ------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
229
+ | Double-click canvas | Search the node catalogue and create a node. Escape cancels before document mutation. |
230
+ | Drag pins | Connect ports. Drag from either direction. |
231
+ | Cmd/Ctrl-drop a connection on empty canvas | Pick and create a compatible connected node as one transaction. |
232
+ | Double-click a link | Pick a node with an input/output and insert it. |
233
+ | Select link | Show mode, weight and enable controls. |
234
+ | S / A / M / B | Choose set/add/multiply/bend for a new or selected link. Registered modes can add shortcuts. |
235
+ | Drag selected link endpoint | Reconnect; Cmd/Ctrl/Alt at start duplicates the link. Empty-space drop cancels. |
236
+ | Drag canvas / Shift-select | Rectangle selection / additive selection. |
237
+ | Scroll / pinch / middle- or right-drag | Pan / zoom / pan. |
238
+ | Cmd/Ctrl+0 | Reset to 100% zoom. |
239
+ | Drag node header | Move nodes. Alt-drag shows a moving copy while originals and their links stay in place; drop commits the duplicate. Add Cmd/Ctrl/Shift to retain boundary links. |
240
+ | Cmd/Ctrl+C / V / D | Copy / paste / duplicate selection. Browser clipboard is best effort with per-controller fallback. |
241
+ | Delete or Backspace | Delete selection and dependent modulation links. Deleting an area leaves its nodes. |
242
+ | Cmd/Ctrl+Z; Cmd/Ctrl+Shift+Z or Y | Undo; redo. |
243
+ | X | Toggle selected nodes, otherwise selected links. |
244
+ | Cmd/Ctrl-drag empty canvas | Draw an area when enabled. |
245
+ | Double-click selected node name / area title | Change node type inline / edit the area title. |
246
+ | Drag numeric field / click | Scrub / type a value. Shift is finer; Cmd/Ctrl is coarser. |
247
+ | Shift/Alt-wheel on number | Scrub without panning. |
248
+
249
+ Canvas shortcuts are scoped to focused editors and ignored in inputs and numeric fields. A cancelled transaction restores the document without consuming redo. Numeric gesture changes are previews until the gesture commits; subscribers receive one logical history event.
250
+
251
+ Duplicate drag previews are editor-only presentation: the saved node positions, link identities and runtime graph remain unchanged while dragging. Escape discards the preview; dropping creates the copy as one undo step. Standard and fully custom nodes use the same preview behavior. Shared port geometry centres input and output pins on their node borders, including while hovered.
252
+
253
+ Cmd/Ctrl/Alt-dragging a selected cable endpoint duplicates its connection and keeps the original cable visible throughout the drag, including connections targeting another link's strength. Ordinary endpoint dragging reconnects the existing link. Canvas measurements are retained across drag previews rather than reinitializing nodes on each movement.
254
+
255
+ Panning uses Teia's content-based limit by default: visible content is surrounded by a margin equal to 70% of the screen width/height at the current zoom. The limit is recalculated after zooming, resizing, changing graphs or changing visible content, so the graph cannot be lost in unlimited empty space. Invisible link anchors and temporary duplication previews do not expand that limit. Hosts can configure `panBounds={{ emptyScreenRatio: 0.7 }}` or explicitly opt out with `panBounds={false}`. Limits include visible areas as well as nodes.
256
+
257
+ ## Documents, observations and integration
258
+
259
+ `readDocument`/`writeDocument` handle Spagbol's own JSON envelope (`format: 'spagbol', version: 1`). They validate finite JSON data and shapes, retain extension payloads, and preserve link order. Document objects exposed by the controller are frozen. Unknown node kinds stay in the document with diagnostics. This generic format does **not** replace existing Teia or visual-visual formats: Teia supplies a compatibility codec; other applications supply their own codecs.
260
+
261
+ `onCommit` provides change categories (`structure`, `values`, `layout`, `metadata`) plus edit/undo/redo/replace origin. `onPreview` receives transient gesture edits and `null` on completion/cancellation. Consumers can compile structure/value changes while ignoring layout. Persistence is opt-in, owned by the host. Atomic custom transformations use `editor.edit(...)`; the transform receives a mutable clone and cannot partially mutate the current document if it throws.
262
+
263
+ Runtime feedback belongs in `ObservationStore<T>`, keyed by graph path, node ID and instance. Each key has its own subscribers. `useRenderTarget(nodeId, slot)` registers a scoped DOM target through the canvas's `onRenderTarget` callback for app-owned GPU/canvas rendering. There are no device acquisitions or idle polling timers in Spagbol.
264
+
265
+ Styles are explicitly imported and use `.spagbol-*` classes and `--spagbol-*` tokens; the library does not reset the page's body, root or native controls. `SpagbolTheme` accepts token overrides. Hosts supply dimensions and application chrome.
266
+
267
+ The stylesheet defaults to Teia's standard black-and-white theme: its monospace font stack at 13px, white frames and headers, yellow selected headers, square pins, flat translucent black surfaces, 28px corner controls and 28px-spaced background dots. Standard cable colours match Teia (white set, yellow add, blue multiply, purple bend). Input and output columns sit beside one another. Consumers need no theme configuration; `SpagbolTheme` token overrides remain available. The minimal example uses these defaults without overrides. Selected nodes retain their individual frames and yellow headers; no surrounding group-selection box or green focus outline is drawn. Custom node renderers and application chrome remain consumer-owned.
268
+
269
+ ## Review before application adoption
270
+
271
+ Standard port rows have explicit input/output columns and row positions. Numeric controls keep a fixed width, while labels truncate within narrow frames; output pins stay on the frame's right edge when input counts change.
272
+
273
+ The headless regression suite establishes model/controller behavior, not browser interaction or audio/firmware equivalence. Browser QA is still needed for custom layouts, gesture cancellation, cable targeting/reconnection, area occlusion, nested panels and independent editors.
274
+
275
+ The initial controller uses immutable whole-document snapshots and JSON equality for transactions. It is suitable for reviewing the API and interactions; large-graph profiling and incremental indexes/subscriptions remain work before adopting Teia-sized patches. Runtime observations already have targeted subscriptions, but document edits currently rerender interested canvas components broadly.
276
+
277
+ See [the adoption roadmap](docs/adoption.md) for the Teia ownership boundary and functional verification scope. No Teia/FM-1/hosted support claim is made by this release.
278
+
279
+ ## Complete-editor migration contract
280
+
281
+ Teia must consume one `SpagbolCanvas` and shared `GraphEditor`. Low-level controlled-flow wiring is temporary and does not count as migration. Node frames/ports, link controls/reconnection, selection/history, Group/Ins/Outs, nesting and exposed-control panels belong to Spagbol. Teia owns its catalogue, specialised node content, patch codec and runtime/asset/host bindings. Teia now uses this boundary: its former editor state, graph gestures and cable renderer have been removed. Static checks cover document/compiler compatibility; browser, audio and host/hardware behavior still require functional verification.
282
+
283
+ The canvas now owns circular node enable toggles, the accepted +/− compact controls, inline type conversion, the Teia-style selected-link box above the canvas and transparent reconnect targets over green selected pins. Grouping is a default menu action. Built-in boundaries support inline add/rename, drag reorder and selected-port deletion. Structural changes synchronize linked groups and prune invalid dependent connections in the same undo entry. Group UI areas named `ui` or `controls` and explicit exposure metadata project nested content with independent numeric/data overrides; these layouts and edits are part of the canvas, not app-assembled widgets.
284
+
285
+ The shared number control uses Teia's numeric entry, vertical scrubbing and modifier-wheel behavior. `capture` optionally supplies an external input event while editing; Teia binds MIDI learning without an audio dependency in the library. Node views can use `autoWidth` for intrinsic domain content, `compact: false` for always-visible parameter tables and frame `renderPortContent` for specialized parameter widgets. The frame still owns handles and graph interactions. `nodeKinds` restricts creation/type menus while retaining unsupported existing nodes. `virtualLinks` renders read-only domain relations. An area's `ownerNodeId` travels with copy/paste and deletion. `normalizeDocument` maintains domain invariants within the same atomic edit and history entry; `policy.deletePort` updates editable domain port metadata through shared selection/deletion.
286
+
287
+ Ins/Outs new-port placeholders accept connection drops as well as typed names. Port creation and connection/reconnection share one transaction, so undo removes both together. Boundary defaults use the shared numeric input.
288
+
289
+ Additional complete-canvas extension points: `NodeDefinition.editablePorts`/`createPort` provide domain-owned port metadata with shared temporary pins and atomic creation. `onChangeNodeKind` keeps asset/DSP conversion in the host while the canvas owns inline editing. `onPaste` supplies source/copy IDs for external resources. Views can mark decorations nonselectable/nondraggable and place specialized port widgets through frame `portVisible`, `headerOutput`, `renderPortContent` and `overlay` slots; their ports remain shared `SpagbolNodePort` handles. `scale` and `compactLabel` are graph UI fields. Areas duplicate their full hierarchy through transient previews, support inline titles, and Ctrl/Cmd-delete bridges selected nodes. UI-area group panels crop to the specified area bounds, preserving Teia's exposure layout and resettable control labels.
290
+
291
+ Frame `rootRef` exposes the canonical element for intrinsic domain display measurements. Content remains padding-free, and hosts can pass explicit frame widths without replacing frame/port interactions.
292
+
293
+ `GraphEditor.changeNodeKind(id, kind, data?)` performs type replacement, ordered port remapping and invalid-cable pruning in one undo entry; optional data carries app-prepared assets. `scaleSelection(factor, measuredSizes?)` scales selected nodes around their shared visual center, also as one undo entry. The default canvas menu supplies scaling actions.
294
+
295
+ Sliders show the requested value immediately during pointer and wheel edits, independently of delayed `displayValue` observations. On release they wait for matching feedback for at most 200 ms before resuming the live display. Inside a node view, pass `onGestureStart={() => beginGesture("Change slider value")}` and `onGestureEnd={endGesture}` so a whole drag or wheel burst is one undo step; cancellation restores the prior document. Port-definition functions are pure functions of their node; Spagbol reuses results for immutable node snapshots.
296
+
297
+ ## Publishing to npm
298
+
299
+ The public package is `@pauloricca/spagbol`, licensed under [MIT](LICENSE), with JavaScript, TypeScript declarations and explicit CSS exports. Its source repository is [pauloricca/spagbol](https://github.com/pauloricca/spagbol). `publishConfig` pins the public npm registry and public visibility. Publishing requires an npm account with access to the scope.
300
+
301
+ From this repository root, open a release shell in Docker. Authentication stays outside the checkout in a named volume:
302
+
303
+ ```sh
304
+ docker compose run --rm --build \
305
+ -v spagbol-npm-auth:/root/.npm \
306
+ -e NPM_CONFIG_USERCONFIG=/root/.npm/userconfig \
307
+ check sh
308
+ ```
309
+
310
+ Inside that shell:
311
+
312
+ ```sh
313
+ npm ci --ignore-scripts --no-audit --no-fund
314
+ npm test
315
+ npm pack --dry-run
316
+ npm login
317
+ npm whoami
318
+ npm publish --access public
319
+ ```
320
+
321
+ Review the dry-run file list first. `prepack` builds before normal packing/publishing. Follow npm's browser login and two-factor prompts; scoped public packages require `--access public`. The `files` allowlist excludes examples, tests, development configuration and artifacts. See [npm's scoped publishing guide](https://docs.npmjs.com/creating-and-publishing-scoped-public-packages/).
322
+
323
+ Consumers install the published release:
324
+
325
+ ```sh
326
+ npm install @pauloricca/spagbol @xyflow/react react react-dom
327
+ ```
328
+
329
+ Import `@pauloricca/spagbol/styles.css` once and mount `SpagbolCanvas` in a sized container, as above. Applications should declare the released npm version in their manifest and commit the registry-backed lockfile. The bundled example intentionally uses a local tarball so edits can be tested before a release.
330
+
331
+ For updates, change the version first (for example `npm version patch --no-git-tag-version`), rerun the checks and dry run, then publish again. Commit the manifest/lockfile through your normal release workflow. Published name/version pairs cannot be reused. Local example preparation reads the package version to select its tarball. See [npm publish](https://docs.npmjs.com/cli/v11/commands/npm-publish/).
332
+
333
+ ## Node drag previews
334
+
335
+ Ordinary node movement is presented as transient canvas geometry. The immutable editor document keeps its previous positions until drop, when all moved nodes are committed as one layout edit and one undo entry. Escape, pointer cancellation, or controller cancellation discards the preview. Numeric controls still publish live value previews through the controller's gesture API. Hosts observing document changes should expect a layout change at drop rather than on each pointer movement.
336
+
337
+ ## 0.1.1
338
+
339
+ Node dragging now previews native canvas positions and commits one document edit on drop. This removes per-pointer graph cloning, validation and redundant controlled-position updates. Selection subscriptions are scoped to the affected node, port or cable. Sliders and numeric controls retain live value updates and grouped undo. Escape, pointer cancellation, multi-selection and Alt duplication are covered by regression checks. See [the performance investigation](docs/node-drag-performance.md) for measurements and behavior details.
@@ -0,0 +1,122 @@
1
+ /* Teia's original controls. Existing Teia tokens take precedence; Spagbol
2
+ tokens/defaults allow the same controls in other consumers and outside a canvas. */
3
+ .spagbol-control-slider,
4
+ .spagbol-control-button {
5
+ grid-column: 1 / -1;
6
+ width: 100%;
7
+ min-width: 156px;
8
+ }
9
+
10
+ .spagbol-control-slider {
11
+ position: relative;
12
+ height: var(--node-display-height, 48px);
13
+ background: transparent;
14
+ overflow: hidden;
15
+ touch-action: none;
16
+ }
17
+
18
+ .spagbol-control-button {
19
+ height: var(--node-display-height, 48px);
20
+ min-width: 0;
21
+ border: 0;
22
+ background: transparent;
23
+ cursor: pointer;
24
+ touch-action: none;
25
+ }
26
+
27
+ .spagbol-control-button:hover,
28
+ .spagbol-control-button:focus-visible {
29
+ background: var(--color-foreground-08, rgba(255, 255, 255, 0.08));
30
+ color: var(--color-foreground, var(--spagbol-foreground, #fff));
31
+ outline: 0;
32
+ }
33
+
34
+ .spagbol-control-button-active {
35
+ background: var(--color-accent, var(--spagbol-accent, #2fd061));
36
+ color: var(--color-background, var(--spagbol-bg, #000));
37
+ }
38
+
39
+ .spagbol-control-button-active:hover,
40
+ .spagbol-control-button-active:focus-visible {
41
+ background: var(--color-accent, var(--spagbol-accent, #2fd061));
42
+ color: var(--color-background, var(--spagbol-bg, #000));
43
+ }
44
+
45
+ .spagbol-control-slider-fill {
46
+ position: absolute;
47
+ inset: auto auto 0 0;
48
+ width: 0;
49
+ height: 100%;
50
+ background: var(--color-accent, var(--spagbol-accent, #2fd061));
51
+ pointer-events: none;
52
+ }
53
+
54
+ .spagbol-control-slider-vertical .spagbol-control-slider-fill {
55
+ width: 100%;
56
+ height: 0;
57
+ }
58
+
59
+ .spagbol-control-slider input[type="range"] {
60
+ position: absolute;
61
+ inset: 0;
62
+ width: 100%;
63
+ height: 100%;
64
+ margin: 0;
65
+ opacity: 0;
66
+ cursor: pointer;
67
+ }
68
+
69
+ .spagbol-control-slider-vertical input[type="range"] {
70
+ writing-mode: vertical-lr;
71
+ direction: rtl;
72
+ }
73
+
74
+ /* Match Teia's control focus styling inside the canvas's generic button rules. */
75
+ .spagbol-canvas button.spagbol-control-button:focus-visible {
76
+ outline: 0;
77
+ }
78
+
79
+
80
+ .spagbol-control-number {
81
+ width: 68px;
82
+ min-width: 0;
83
+ max-width: 68px;
84
+ height: 22px;
85
+ padding: 0 4px;
86
+ border: 1px solid var(--spagbol-foreground, #fff);
87
+ background: transparent;
88
+ color: var(--spagbol-foreground, #fff);
89
+ cursor: ns-resize;
90
+ line-height: 20px;
91
+ overflow: hidden;
92
+ text-align: left;
93
+ text-overflow: clip;
94
+ white-space: nowrap;
95
+ user-select: none;
96
+ }
97
+
98
+ .spagbol-control-number:focus {
99
+ outline: 1px solid var(--spagbol-foreground, #fff);
100
+ outline-offset: 1px;
101
+ }
102
+
103
+ .spagbol-control-number-editing {
104
+ display: block;
105
+ appearance: textfield;
106
+ cursor: text;
107
+ font: inherit;
108
+ user-select: text;
109
+ -webkit-appearance: none;
110
+ -moz-appearance: textfield;
111
+ }
112
+
113
+ .spagbol-control-number-replaced {
114
+ border-color: color-mix(in srgb, var(--spagbol-foreground, #fff) 42%, transparent);
115
+ color: color-mix(in srgb, var(--spagbol-foreground, #fff) 48%, var(--spagbol-bg, #000));
116
+ }
117
+
118
+ .spagbol-control-number-editing::selection {
119
+ background: var(--spagbol-foreground, #fff);
120
+ color: var(--spagbol-bg, #000);
121
+ }
122
+
@@ -0,0 +1,89 @@
1
+ import { LinkModeRegistry, NodeRegistry, type ChangeEvent, type ChangeKind, type GraphArea, type NodeData, type GraphDocument, type GraphLink, type GraphNode, type GraphPolicy, type LinkTarget, type Point, type Selection, type Size } from "./model.js";
2
+ import { type GraphFragment } from "./graph.js";
3
+ import { type GroupOptions } from "./grouping.js";
4
+ export interface EditorSnapshot {
5
+ document: GraphDocument;
6
+ path: readonly string[];
7
+ selection: Selection;
8
+ canUndo: boolean;
9
+ canRedo: boolean;
10
+ gesture: string | null;
11
+ mode: "edit" | "controls" | "readonly";
12
+ }
13
+ export interface EditorOptions {
14
+ document?: GraphDocument;
15
+ nodes: NodeRegistry;
16
+ modes?: LinkModeRegistry;
17
+ policy?: GraphPolicy;
18
+ historyLimit?: number;
19
+ onCommit?(event: ChangeEvent): void;
20
+ onPreview?(event: ChangeEvent | null): void;
21
+ onPaste?(event: {
22
+ fragment: GraphFragment;
23
+ nodeIds: readonly string[];
24
+ areaIds: readonly string[];
25
+ path: readonly string[];
26
+ document: GraphDocument;
27
+ }): void;
28
+ /** Pure domain invariants, applied within the same atomic edit/history entry. */
29
+ normalizeDocument?(document: GraphDocument, previous: GraphDocument): void;
30
+ }
31
+ /** One controller per document. Nothing is stored in module-level mutable state. */
32
+ export declare class GraphEditor {
33
+ #private;
34
+ readonly nodes: NodeRegistry;
35
+ readonly modes: LinkModeRegistry;
36
+ readonly policy: GraphPolicy;
37
+ constructor(options: EditorOptions);
38
+ getSnapshot: () => EditorSnapshot;
39
+ subscribe: (listener: () => void) => (() => void);
40
+ onCommit(listener: (event: ChangeEvent) => void): () => void;
41
+ get graph(): GraphDocument;
42
+ get editing(): boolean;
43
+ diagnostics(): import("./model.js").Diagnostic[];
44
+ /** App transformations use this same atomic path as built-in commands. */
45
+ edit(label: string, kinds: ChangeKind | readonly ChangeKind[], transform: (graph: GraphDocument) => void): void;
46
+ beginGesture(label: string): void;
47
+ commitGesture(): void;
48
+ cancelGesture(): void;
49
+ setMode(mode: EditorSnapshot["mode"]): void;
50
+ select(selection: Partial<Selection>): void;
51
+ enter(nodeId: string): void;
52
+ navigate(path: readonly string[]): void;
53
+ exit(): void;
54
+ undo(): void;
55
+ redo(): void;
56
+ replace(document: GraphDocument): void;
57
+ addNode(kind: string, position: Point): string | undefined;
58
+ updateNode(id: string, update: Partial<Omit<GraphNode, "id" | "graph">>, kind?: ChangeKind): void;
59
+ changeNodeKind(id: string, kind: string, data?: NodeData): void;
60
+ /** Scale selected nodes around their shared visual center in one undo entry. */
61
+ scaleSelection(factor: number, measuredSizes?: ReadonlyMap<string, Size>): void;
62
+ setValue(id: string, portId: string, value: number): void;
63
+ /** Several control values are one edit, with no intermediate document states. */
64
+ setValues(id: string, values: Readonly<Record<string, number>>): void;
65
+ setOverride(groupId: string, nodeId: string | readonly string[], portId: string, value: number): void;
66
+ resetOverride(groupId: string, nodeId: string | readonly string[]): void;
67
+ addBoundaryPort(nodeId: string, label: string, defaultValue?: number): void;
68
+ renameBoundaryPort(nodeId: string, portId: string, label: string): void;
69
+ removeBoundaryPort(nodeId: string, portId: string): void;
70
+ moveBoundaryPort(nodeId: string, portId: string, index: number): void;
71
+ exposeSelection(exposed?: boolean): void;
72
+ setDataOverride(groupId: string, nodeId: string | readonly string[], update: Partial<NodeData>): void;
73
+ canConnect(source: GraphLink["source"], target: LinkTarget, mode: string, replacingId?: string): string | undefined;
74
+ connect(source: GraphLink["source"], target: LinkTarget, mode?: string, settings?: Partial<Pick<GraphLink, "weight" | "enabled" | "data">>): string | undefined;
75
+ reconnect(id: string, source: GraphLink["source"], target: LinkTarget, duplicate?: boolean): string | undefined;
76
+ updateLink(id: string, update: Partial<Pick<GraphLink, "mode" | "weight" | "enabled" | "data">>): void;
77
+ deleteSelection(bridge?: boolean): void;
78
+ copy(): GraphFragment;
79
+ paste(fragment?: GraphFragment | null, offset?: Point, external?: boolean): void;
80
+ duplicate(offset?: Point, external?: boolean): void;
81
+ groupSelection(options?: GroupOptions): string | undefined;
82
+ insertNode(linkId: string, kind: string, position: Point, inputId: string, outputId: string): string | undefined;
83
+ convertNodeToArea(nodeId: string): string | undefined;
84
+ addArea(position: Point, size?: Size): string | undefined;
85
+ updateArea(id: string, update: Partial<Omit<GraphArea, "id">>): void;
86
+ lockArea(id: string, locked: boolean): void;
87
+ moveArea(id: string, position: Point): void;
88
+ }
89
+ //# sourceMappingURL=controller.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"controller.d.ts","sourceRoot":"","sources":["../../src/core/controller.ts"],"names":[],"mappings":"AACA,OAAO,EASL,gBAAgB,EAChB,YAAY,EACZ,KAAK,WAAW,EAChB,KAAK,UAAU,EACf,KAAK,SAAS,EACd,KAAK,QAAQ,EACb,KAAK,aAAa,EAClB,KAAK,SAAS,EACd,KAAK,SAAS,EACd,KAAK,WAAW,EAChB,KAAK,UAAU,EACf,KAAK,KAAK,EACV,KAAK,SAAS,EACd,KAAK,IAAI,EACV,MAAM,YAAY,CAAC;AACpB,OAAO,EAUL,KAAK,aAAa,EACnB,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAc,KAAK,YAAY,EAAE,MAAM,eAAe,CAAC;AAE9D,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,aAAa,CAAC;IACxB,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;IACxB,SAAS,EAAE,SAAS,CAAC;IACrB,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,UAAU,CAAC;CACxC;AACD,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,EAAE,aAAa,CAAC;IACzB,KAAK,EAAE,YAAY,CAAC;IACpB,KAAK,CAAC,EAAE,gBAAgB,CAAC;IACzB,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,CAAC,KAAK,EAAE,WAAW,GAAG,IAAI,CAAC;IACpC,SAAS,CAAC,CAAC,KAAK,EAAE,WAAW,GAAG,IAAI,GAAG,IAAI,CAAC;IAC5C,OAAO,CAAC,CAAC,KAAK,EAAE;QAAE,QAAQ,EAAE,aAAa,CAAC;QAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;QAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;QAAC,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;QAAC,QAAQ,EAAE,aAAa,CAAA;KAAE,GAAG,IAAI,CAAC;IAC7J,iFAAiF;IACjF,iBAAiB,CAAC,CAAC,QAAQ,EAAE,aAAa,EAAE,QAAQ,EAAE,aAAa,GAAG,IAAI,CAAC;CAC5E;AAoCD,oFAAoF;AACpF,qBAAa,WAAW;;IACtB,QAAQ,CAAC,KAAK,EAAE,YAAY,CAAC;IAC7B,QAAQ,CAAC,KAAK,EAAE,gBAAgB,CAAC;IACjC,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;gBAUjB,OAAO,EAAE,aAAa;IAkBlC,WAAW,QAAO,cAAc,CAAmB;IACnD,SAAS,GAAI,UAAU,MAAM,IAAI,KAAG,CAAC,MAAM,IAAI,CAAC,CAK9C;IACF,QAAQ,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,GAAG,MAAM,IAAI;IAM5D,IAAI,KAAK,IAAI,aAAa,CAEzB;IACD,IAAI,OAAO,IAAI,OAAO,CAErB;IACD,WAAW;IA0DX,0EAA0E;IAC1E,IAAI,CACF,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,UAAU,GAAG,SAAS,UAAU,EAAE,EACzC,SAAS,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,GACxC,IAAI;IA2BP,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAWjC,aAAa,IAAI,IAAI;IAgBrB,aAAa,IAAI,IAAI;IAOrB,OAAO,CAAC,IAAI,EAAE,cAAc,CAAC,MAAM,CAAC,GAAG,IAAI;IAI3C,MAAM,CAAC,SAAS,EAAE,OAAO,CAAC,SAAS,CAAC,GAAG,IAAI;IAI3C,KAAK,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAQ3B,QAAQ,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,IAAI;IAIvC,IAAI,IAAI,IAAI;IAGZ,IAAI,IAAI,IAAI;IAaZ,IAAI,IAAI,IAAI;IAaZ,OAAO,CAAC,QAAQ,EAAE,aAAa,GAAG,IAAI;IAmBtC,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,GAAG,MAAM,GAAG,SAAS;IAS1D,UAAU,CACR,EAAE,EAAE,MAAM,EACV,MAAM,EAAE,OAAO,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,GAAG,OAAO,CAAC,CAAC,EAChD,IAAI,GAAE,UAAuB,GAC5B,IAAI;IAQP,cAAc,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,GAAE,QAAa,GAAG,IAAI;IAiCnE,gFAAgF;IAChF,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,aAAa,GAAE,WAAW,CAAC,MAAM,EAAE,IAAI,CAAa,GAAG,IAAI;IAoB1F,QAAQ,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAGzD,iFAAiF;IACjF,SAAS,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,GAAG,IAAI;IAoBrE,WAAW,CACT,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,EAClC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,GACZ,IAAI;IAqBP,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,GAAG,IAAI;IAQxE,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,YAAY,SAAI,GAAG,IAAI;IAMtE,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAUvE,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI;IASxD,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAUrE,eAAe,CAAC,OAAO,UAAO,GAAG,IAAI;IAarC,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,EAAE,MAAM,EAAE,OAAO,CAAC,QAAQ,CAAC,GAAG,IAAI;IASrG,UAAU,CACR,MAAM,EAAE,SAAS,CAAC,QAAQ,CAAC,EAC3B,MAAM,EAAE,UAAU,EAClB,IAAI,EAAE,MAAM,EACZ,WAAW,CAAC,EAAE,MAAM,GACnB,MAAM,GAAG,SAAS;IAcrB,OAAO,CACL,MAAM,EAAE,SAAS,CAAC,QAAQ,CAAC,EAC3B,MAAM,EAAE,UAAU,EAClB,IAAI,SAAQ,EACZ,QAAQ,GAAE,OAAO,CAAC,IAAI,CAAC,SAAS,EAAE,QAAQ,GAAG,SAAS,GAAG,MAAM,CAAC,CAAM,GACrE,MAAM,GAAG,SAAS;IAmBrB,SAAS,CACP,EAAE,EAAE,MAAM,EACV,MAAM,EAAE,SAAS,CAAC,QAAQ,CAAC,EAC3B,MAAM,EAAE,UAAU,EAClB,SAAS,UAAQ,GAChB,MAAM,GAAG,SAAS;IAiCrB,UAAU,CACR,EAAE,EAAE,MAAM,EACV,MAAM,EAAE,OAAO,CAAC,IAAI,CAAC,SAAS,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS,GAAG,MAAM,CAAC,CAAC,GACvE,IAAI;IAaP,eAAe,CAAC,MAAM,UAAQ,GAAG,IAAI;IAwCrC,IAAI,IAAI,aAAa;IAQrB,KAAK,CACH,QAAQ,uBAAkB,EAC1B,MAAM,GAAE,KAAwB,EAChC,QAAQ,UAAQ,GACf,IAAI;IASP,SAAS,CAAC,MAAM,GAAE,KAAwB,EAAE,QAAQ,UAAQ,GAAG,IAAI;IAWnE,cAAc,CAAC,OAAO,CAAC,EAAE,YAAY,GAAG,MAAM,GAAG,SAAS;IAc1D,UAAU,CACR,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,KAAK,EACf,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,GACf,MAAM,GAAG,SAAS;IAsDrB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAWrD,OAAO,CACL,QAAQ,EAAE,KAAK,EACf,IAAI,GAAE,IAAkC,GACvC,MAAM,GAAG,SAAS;IAcrB,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC,GAAG,IAAI;IAMpE,QAAQ,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAG,IAAI;IAe3C,QAAQ,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,GAAG,IAAI;CAsB5C"}