@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
@@ -0,0 +1,130 @@
1
+ import { areaMembers } from "./graph.js";
2
+ import { clone, createId, portsFor, emptyGraph, spagbolGroupKinds, } from "./model.js";
3
+ /** Structural grouping only. Consumers decide how these boundary kinds execute. */
4
+ export function groupNodes(graph, ids, definitions, options = { groupKind: spagbolGroupKinds.group, inputKind: spagbolGroupKinds.inputs, outputKind: spagbolGroupKinds.outputs }) {
5
+ const selected = new Set(ids), members = graph.nodes.filter((node) => selected.has(node.id));
6
+ if (!members.length)
7
+ return;
8
+ for (const kind of [options.groupKind, options.inputKind, options.outputKind])
9
+ if (!definitions.get(kind))
10
+ throw new Error(`Grouping requires registered kind ${kind}.`);
11
+ const owner = new Map();
12
+ const byId = new Map(graph.links.map((link) => [link.id, link]));
13
+ function inside(link, seen = new Set()) {
14
+ if (owner.has(link.id))
15
+ return owner.get(link.id);
16
+ if (seen.has(link.id))
17
+ throw new Error("Cannot group a cyclic link-property dependency.");
18
+ const next = new Set([...seen, link.id]);
19
+ const target = link.target;
20
+ const parent = target.type === "link" ? byId.get(target.linkId) : undefined;
21
+ if (target.type === "link" && !parent)
22
+ throw new Error("Cannot group a dangling link-property target.");
23
+ const value = target.type === "port"
24
+ ? selected.has(target.nodeId)
25
+ : inside(parent, next);
26
+ owner.set(link.id, value);
27
+ return value;
28
+ }
29
+ const minX = Math.min(...members.map((node) => node.position.x)), minY = Math.min(...members.map((node) => node.position.y));
30
+ const maxX = Math.max(...members.map((node) => node.position.x + (node.size?.width ?? 220)));
31
+ const group = definitions.create(options.groupKind, { x: minX, y: minY });
32
+ const input = definitions.create(options.inputKind, {
33
+ x: minX - 270,
34
+ y: minY,
35
+ });
36
+ const output = definitions.create(options.outputKind, {
37
+ x: maxX + 70,
38
+ y: minY,
39
+ });
40
+ group.label = options.label ?? "Group";
41
+ group.graph = emptyGraph();
42
+ group.sharedGraphId = createId("shared");
43
+ group.ports = [];
44
+ input.ports = [];
45
+ output.ports = [];
46
+ const inputPorts = new Map(), outputPorts = new Map();
47
+ const outerInputs = [], innerOutputs = [];
48
+ const key = (endpoint) => JSON.stringify([endpoint.nodeId, endpoint.portId]);
49
+ function expose(source, direction, preferredLabel) {
50
+ const map = direction === "input" ? inputPorts : outputPorts;
51
+ const previous = map.get(key(source));
52
+ if (previous)
53
+ return {
54
+ nodeId: direction === "input" ? input.id : group.id,
55
+ portId: previous,
56
+ };
57
+ const id = createId("port");
58
+ const sourceNode = graph.nodes.find(node => node.id === source.nodeId);
59
+ const sourcePort = sourceNode && portsFor(sourceNode, definitions).find(port => port.id === source.portId && port.direction === 'output');
60
+ const base = (preferredLabel ?? sourcePort?.label ?? source.portId).replace(/([a-z])([A-Z])/g, '$1 $2').replace(/[_-]+/g, ' ').toLowerCase() || 'signal';
61
+ let label = base, suffix = 2;
62
+ while (group.ports.some(port => port.direction === direction && port.label === label))
63
+ label = `${base} ${suffix++}`;
64
+ const port = { id, label, direction, defaultValue: 0 };
65
+ group.ports.push(port);
66
+ map.set(key(source), id);
67
+ if (direction === "input") {
68
+ group.values[id] = 0;
69
+ input.ports.push({ ...port, direction: "output" });
70
+ outerInputs.push({
71
+ id: createId("link"),
72
+ source: { ...source },
73
+ target: { type: "port", nodeId: group.id, portId: id },
74
+ mode: "set",
75
+ weight: 1,
76
+ });
77
+ }
78
+ else {
79
+ output.ports.push({ ...port, direction: "input" });
80
+ output.values[id] = 0;
81
+ innerOutputs.push({
82
+ id: createId("link"),
83
+ source: { ...source },
84
+ target: { type: "port", nodeId: output.id, portId: id },
85
+ mode: "set",
86
+ weight: 1,
87
+ });
88
+ }
89
+ return { nodeId: direction === "input" ? input.id : group.id, portId: id };
90
+ }
91
+ const innerLinks = [], outerLinks = [];
92
+ for (const original of graph.links) {
93
+ const link = clone(original);
94
+ if (inside(original)) {
95
+ if (!selected.has(original.source.nodeId))
96
+ link.source = expose(original.source, "input", original.target.type === 'link' ? 'strength' : (() => {
97
+ const targetNode = graph.nodes.find(node => original.target.type === 'port' && node.id === original.target.nodeId);
98
+ return targetNode && portsFor(targetNode, definitions).find(port => port.direction === 'input' && original.target.type === 'port' && port.id === original.target.portId)?.label;
99
+ })());
100
+ innerLinks.push(link);
101
+ }
102
+ else {
103
+ if (selected.has(original.source.nodeId))
104
+ link.source = expose(original.source, "output");
105
+ outerLinks.push(link);
106
+ }
107
+ }
108
+ group.graph.nodes = [input, ...members, output];
109
+ group.graph.links = [...innerLinks, ...innerOutputs];
110
+ const movedAreas = graph.areas.filter(area => {
111
+ const members = areaMembers(graph, area.id).nodeIds;
112
+ return members.size > 0 && [...members].every(id => selected.has(id));
113
+ });
114
+ const movedAreaIds = new Set(movedAreas.map(area => area.id));
115
+ group.graph.areas = movedAreas;
116
+ graph.areas = graph.areas.filter(area => !movedAreaIds.has(area.id));
117
+ graph.nodes = [
118
+ ...graph.nodes.filter((node) => !selected.has(node.id)),
119
+ group,
120
+ ];
121
+ graph.links = [...outerLinks, ...outerInputs];
122
+ for (const area of graph.areas) {
123
+ if (area.nodeIds)
124
+ area.nodeIds = [...new Set(area.nodeIds.map(id => selected.has(id) ? group.id : id))];
125
+ if (area.areaIds)
126
+ area.areaIds = area.areaIds.filter(id => !movedAreaIds.has(id));
127
+ }
128
+ return group.id;
129
+ }
130
+ //# sourceMappingURL=grouping.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"grouping.js","sourceRoot":"","sources":["../../src/core/grouping.ts"],"names":[],"mappings":"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","sourcesContent":["import { areaMembers } from \"./graph.js\";\nimport {\n clone,\n createId,\n portsFor,\n emptyGraph,\n spagbolGroupKinds,\n type GraphDocument,\n type GraphLink,\n type GraphNode,\n type NodeRegistry,\n type PortDefinition,\n type PortEndpoint,\n} from \"./model.js\";\n\nexport interface GroupOptions {\n groupKind: string;\n inputKind: string;\n outputKind: string;\n label?: string;\n}\n/** Structural grouping only. Consumers decide how these boundary kinds execute. */\nexport function groupNodes(\n graph: GraphDocument,\n ids: readonly string[],\n definitions: NodeRegistry,\n options: GroupOptions = { groupKind: spagbolGroupKinds.group, inputKind: spagbolGroupKinds.inputs, outputKind: spagbolGroupKinds.outputs },\n): string | undefined {\n const selected = new Set(ids),\n members = graph.nodes.filter((node) => selected.has(node.id));\n if (!members.length) return;\n for (const kind of [options.groupKind, options.inputKind, options.outputKind])\n if (!definitions.get(kind))\n throw new Error(`Grouping requires registered kind ${kind}.`);\n const owner = new Map<string, boolean>();\n const byId = new Map(graph.links.map((link) => [link.id, link]));\n function inside(link: GraphLink, seen = new Set<string>()): boolean {\n if (owner.has(link.id)) return owner.get(link.id)!;\n if (seen.has(link.id))\n throw new Error(\"Cannot group a cyclic link-property dependency.\");\n const next = new Set([...seen, link.id]);\n const target = link.target;\n const parent = target.type === \"link\" ? byId.get(target.linkId) : undefined;\n if (target.type === \"link\" && !parent)\n throw new Error(\"Cannot group a dangling link-property target.\");\n const value =\n target.type === \"port\"\n ? selected.has(target.nodeId)\n : inside(parent!, next);\n owner.set(link.id, value);\n return value;\n }\n const minX = Math.min(...members.map((node) => node.position.x)),\n minY = Math.min(...members.map((node) => node.position.y));\n const maxX = Math.max(\n ...members.map((node) => node.position.x + (node.size?.width ?? 220)),\n );\n const group = definitions.create(options.groupKind, { x: minX, y: minY });\n const input = definitions.create(options.inputKind, {\n x: minX - 270,\n y: minY,\n });\n const output = definitions.create(options.outputKind, {\n x: maxX + 70,\n y: minY,\n });\n group.label = options.label ?? \"Group\";\n group.graph = emptyGraph();\n group.sharedGraphId = createId(\"shared\");\n group.ports = [];\n input.ports = [];\n output.ports = [];\n const inputPorts = new Map<string, string>(),\n outputPorts = new Map<string, string>();\n const outerInputs: GraphLink[] = [],\n innerOutputs: GraphLink[] = [];\n const key = (endpoint: PortEndpoint) =>\n JSON.stringify([endpoint.nodeId, endpoint.portId]);\n function expose(\n source: PortEndpoint,\n direction: \"input\" | \"output\",\n preferredLabel?: string,\n ): PortEndpoint {\n const map = direction === \"input\" ? inputPorts : outputPorts;\n const previous = map.get(key(source));\n if (previous)\n return {\n nodeId: direction === \"input\" ? input.id : group.id,\n portId: previous,\n };\n const id = createId(\"port\");\n const sourceNode = graph.nodes.find(node => node.id === source.nodeId);\n const sourcePort = sourceNode && portsFor(sourceNode, definitions).find(port => port.id === source.portId && port.direction === 'output');\n const base = (preferredLabel ?? sourcePort?.label ?? source.portId).replace(/([a-z])([A-Z])/g, '$1 $2').replace(/[_-]+/g, ' ').toLowerCase() || 'signal';\n let label = base, suffix = 2;\n while (group.ports!.some(port => port.direction === direction && port.label === label)) label = `${base} ${suffix++}`;\n const port: PortDefinition = { id, label, direction, defaultValue: 0 };\n group.ports!.push(port);\n map.set(key(source), id);\n if (direction === \"input\") {\n group.values[id] = 0;\n input.ports!.push({ ...port, direction: \"output\" });\n outerInputs.push({\n id: createId(\"link\"),\n source: { ...source },\n target: { type: \"port\", nodeId: group.id, portId: id },\n mode: \"set\",\n weight: 1,\n });\n } else {\n output.ports!.push({ ...port, direction: \"input\" });\n output.values[id] = 0;\n innerOutputs.push({\n id: createId(\"link\"),\n source: { ...source },\n target: { type: \"port\", nodeId: output.id, portId: id },\n mode: \"set\",\n weight: 1,\n });\n }\n return { nodeId: direction === \"input\" ? input.id : group.id, portId: id };\n }\n const innerLinks: GraphLink[] = [],\n outerLinks: GraphLink[] = [];\n for (const original of graph.links) {\n const link = clone(original);\n if (inside(original)) {\n if (!selected.has(original.source.nodeId))\n link.source = expose(original.source, \"input\", original.target.type === 'link' ? 'strength' : (() => {\n const targetNode = graph.nodes.find(node => original.target.type === 'port' && node.id === original.target.nodeId);\n return targetNode && portsFor(targetNode, definitions).find(port => port.direction === 'input' && original.target.type === 'port' && port.id === original.target.portId)?.label;\n })());\n innerLinks.push(link);\n } else {\n if (selected.has(original.source.nodeId))\n link.source = expose(original.source, \"output\");\n outerLinks.push(link);\n }\n }\n group.graph.nodes = [input, ...members, output];\n group.graph.links = [...innerLinks, ...innerOutputs];\n const movedAreas = graph.areas.filter(area => {\n const members = areaMembers(graph, area.id).nodeIds;\n return members.size > 0 && [...members].every(id => selected.has(id));\n });\n const movedAreaIds = new Set(movedAreas.map(area => area.id));\n group.graph.areas = movedAreas;\n graph.areas = graph.areas.filter(area => !movedAreaIds.has(area.id));\n graph.nodes = [\n ...graph.nodes.filter((node) => !selected.has(node.id)),\n group,\n ];\n graph.links = [...outerLinks, ...outerInputs];\n for (const area of graph.areas) {\n if (area.nodeIds) area.nodeIds = [...new Set(area.nodeIds.map(id => selected.has(id) ? group.id : id))];\n if (area.areaIds) area.areaIds = area.areaIds.filter(id => !movedAreaIds.has(id));\n }\n return group.id;\n}\n"]}
@@ -0,0 +1,40 @@
1
+ import { type GraphDocument, type GraphNode, type NodeRegistry, type PortDefinition, type NodeData, type PortEndpoint, type LinkTarget } from './model.js';
2
+ export declare function isGroupBoundary(node: GraphNode): boolean;
3
+ export declare function normalizeGroupPortLabel(label: string): string;
4
+ export declare function createGroupPort(node: GraphNode, label: string, defaultValue?: number): PortDefinition;
5
+ export declare function createEditablePort(node: GraphNode, registry: NodeRegistry, label: string, defaultValue?: number): PortDefinition;
6
+ /** Apply a boundary change to every linked group instance and prune dependent cables atomically. */
7
+ export declare function synchronizeGroupBoundaries(document: GraphDocument, registry: NodeRegistry): void;
8
+ export declare function groupControlKey(path: string | readonly string[]): string;
9
+ export declare function groupControlPath(key: string): string[];
10
+ export declare function findGroupControl(group: GraphNode, path: string | readonly string[]): GraphNode | undefined;
11
+ export declare function effectiveGroupGraph(group: GraphNode): GraphDocument | undefined;
12
+ export declare function groupPanelLayout(group: GraphNode): {
13
+ position: {
14
+ x: number;
15
+ y: number;
16
+ };
17
+ size: {
18
+ width: number;
19
+ height: number;
20
+ };
21
+ nodes: GraphNode<NodeData>[];
22
+ } | undefined;
23
+ /** Transient handle ID; it is resolved to a persistent boundary port in one edit. */
24
+ export declare const spagbolNewPortId = "spagbol.new-port";
25
+ export declare function resolveNewBoundaryPorts(graph: GraphDocument, source: PortEndpoint, target: LinkTarget, registry: NodeRegistry): {
26
+ source: {
27
+ nodeId: string;
28
+ portId: string;
29
+ };
30
+ target: {
31
+ type: "port";
32
+ nodeId: string;
33
+ portId: string;
34
+ } | {
35
+ type: "link";
36
+ linkId: string;
37
+ property: string;
38
+ };
39
+ };
40
+ //# sourceMappingURL=groups.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"groups.d.ts","sourceRoot":"","sources":["../../src/core/groups.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsC,KAAK,aAAa,EAAE,KAAK,SAAS,EAAE,KAAK,YAAY,EAAE,KAAK,cAAc,EAAE,KAAK,QAAQ,EAAE,KAAK,YAAY,EAAE,KAAK,UAAU,EAAE,MAAM,YAAY,CAAC;AAG/L,wBAAgB,eAAe,CAAC,IAAI,EAAE,SAAS,GAAG,OAAO,CAExD;AACD,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAI7D;AACD,wBAAgB,eAAe,CAAC,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,YAAY,SAAI,GAAG,cAAc,CAQhG;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,EAAE,YAAY,SAAI,GAAG,cAAc,CAS3H;AAED,oGAAoG;AACpG,wBAAgB,0BAA0B,CAAC,QAAQ,EAAE,aAAa,EAAE,QAAQ,EAAE,YAAY,GAAG,IAAI,CAahG;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,GAAG,MAAM,CAExE;AACD,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CAGtD;AACD,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,GAAG,SAAS,GAAG,SAAS,CAM1G;AACD,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,SAAS,GAAG,aAAa,GAAG,SAAS,CAgB/E;AAID,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,SAAS;;;;;;;;;;cAIhD;AAmBD,qFAAqF;AACrF,eAAO,MAAM,gBAAgB,qBAAqB,CAAC;AACnD,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,aAAa,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,YAAY;;;;;;;;;;;;;;EAiB7H"}
@@ -0,0 +1,164 @@
1
+ import { clone, createId, spagbolGroupKinds } from './model.js';
2
+ import { pruneInvalidPorts } from './graph.js';
3
+ export function isGroupBoundary(node) {
4
+ return node.kind === spagbolGroupKinds.inputs || node.kind === spagbolGroupKinds.outputs;
5
+ }
6
+ export function normalizeGroupPortLabel(label) {
7
+ const normalized = label.trim().toLowerCase().replace(/\s+/g, ' ');
8
+ if (!/^[a-z0-9]+(?: [a-z0-9]+)*$/.test(normalized))
9
+ throw new Error('Use a lowercase, human-readable port name with spaces.');
10
+ return normalized;
11
+ }
12
+ export function createGroupPort(node, label, defaultValue = 0) {
13
+ if (!isGroupBoundary(node))
14
+ throw new Error('Ports can be added to Ins or Outs nodes.');
15
+ const port = { id: createId('port'), label: normalizeGroupPortLabel(label), direction: node.kind === spagbolGroupKinds.inputs ? 'output' : 'input', defaultValue };
16
+ node.ports ??= [];
17
+ if (node.ports.some(item => item.label === port.label))
18
+ throw new Error('That port name already exists.');
19
+ node.ports.push(port);
20
+ if (node.kind === spagbolGroupKinds.inputs)
21
+ node.values[port.id] = defaultValue;
22
+ return port;
23
+ }
24
+ export function createEditablePort(node, registry, label, defaultValue = 0) {
25
+ if (isGroupBoundary(node))
26
+ return createGroupPort(node, label, defaultValue);
27
+ const definition = registry.get(node.kind);
28
+ if (!definition?.editablePorts || !definition.createPort)
29
+ throw new Error('This node does not expose editable ports.');
30
+ const normalized = normalizeGroupPortLabel(label);
31
+ if (node.ports?.some(port => port.label === normalized))
32
+ throw new Error('That port name already exists.');
33
+ const port = definition.createPort(node, normalized);
34
+ node.ports ??= [];
35
+ node.ports.push(port);
36
+ node.values[port.id] = port.defaultValue ?? defaultValue;
37
+ return port;
38
+ }
39
+ /** Apply a boundary change to every linked group instance and prune dependent cables atomically. */
40
+ export function synchronizeGroupBoundaries(document, registry) {
41
+ for (const node of document.nodes)
42
+ if (node.graph) {
43
+ synchronizeGroupBoundaries(node.graph, registry);
44
+ if (node.kind !== spagbolGroupKinds.group)
45
+ continue;
46
+ node.ports = [...new Map(node.graph.nodes.filter(isGroupBoundary).flatMap(boundary => (boundary.ports ?? []).map(port => ({ ...port, direction: boundary.kind === spagbolGroupKinds.inputs ? 'input' : 'output' }))).map(port => [`${port.direction}:${port.id}`, port])).values()];
47
+ const inputs = new Set(node.ports.filter(port => port.direction === 'input').map(port => port.id));
48
+ for (const id of Object.keys(node.values))
49
+ if (!inputs.has(id))
50
+ delete node.values[id];
51
+ for (const port of node.ports)
52
+ if (port.direction === 'input' && node.values[port.id] === undefined)
53
+ node.values[port.id] = port.defaultValue ?? 0;
54
+ pruneInvalidPorts(document, registry, node.id);
55
+ const ids = new Set(node.graph.nodes.map(item => item.id));
56
+ for (const overrides of [node.overrides, node.dataOverrides])
57
+ if (overrides)
58
+ for (const id of Object.keys(overrides))
59
+ if (!ids.has(groupControlPath(id)[0]))
60
+ delete overrides[id];
61
+ }
62
+ }
63
+ export function groupControlKey(path) {
64
+ return typeof path === 'string' ? path : path.length === 1 ? path[0] : JSON.stringify(path);
65
+ }
66
+ export function groupControlPath(key) {
67
+ if (key.startsWith('['))
68
+ try {
69
+ const path = JSON.parse(key);
70
+ if (Array.isArray(path) && path.every(id => typeof id === 'string'))
71
+ return path;
72
+ }
73
+ catch { /* A literal node id. */ }
74
+ return [key];
75
+ }
76
+ export function findGroupControl(group, path) {
77
+ const ids = typeof path === 'string' ? groupControlPath(path) : path;
78
+ let graph = group.graph;
79
+ let node;
80
+ for (const id of ids) {
81
+ node = graph?.nodes.find(item => item.id === id);
82
+ if (!node)
83
+ return;
84
+ graph = node.graph;
85
+ }
86
+ return node;
87
+ }
88
+ export function effectiveGroupGraph(group) {
89
+ if (!group.graph)
90
+ return;
91
+ const result = { ...group, graph: clone(group.graph) };
92
+ for (const node of result.graph.nodes)
93
+ if (node.graph) {
94
+ node.graph = effectiveGroupGraph(node);
95
+ delete node.overrides;
96
+ delete node.dataOverrides;
97
+ }
98
+ for (const [id, values] of Object.entries(group.overrides ?? {})) {
99
+ const node = findGroupControl(result, id);
100
+ if (node)
101
+ Object.assign(node.values, values);
102
+ }
103
+ for (const [id, data] of Object.entries(group.dataOverrides ?? {})) {
104
+ const node = findGroupControl(result, id);
105
+ if (node)
106
+ Object.assign(node.data, data);
107
+ }
108
+ return result.graph;
109
+ }
110
+ /** Teia's UI/Controls area projection, with explicit exposure also supported. */
111
+ const panelCache = new WeakMap();
112
+ export function groupPanelLayout(group) {
113
+ if (!Object.isFrozen(group))
114
+ return computeGroupPanelLayout(group);
115
+ if (!panelCache.has(group))
116
+ panelCache.set(group, computeGroupPanelLayout(group));
117
+ return panelCache.get(group);
118
+ }
119
+ function computeGroupPanelLayout(group) {
120
+ const graph = effectiveGroupGraph(group);
121
+ if (!graph)
122
+ return;
123
+ const areas = graph.areas.filter(area => area.data?.role === 'controls' || (!area.ownerNodeId && ['ui', 'controls'].includes(area.title.trim().toLowerCase())));
124
+ const exposed = graph.data?.exposedNodeIds;
125
+ const ids = new Set([...(group.panelNodeIds ?? []), ...(Array.isArray(exposed) ? exposed.filter((id) => typeof id === 'string') : [])]);
126
+ for (const area of areas)
127
+ for (const node of graph.nodes) {
128
+ if (area.locked ? area.nodeIds?.includes(node.id) : node.position.x >= area.position.x && node.position.x < area.position.x + area.size.width && node.position.y >= area.position.y && node.position.y < area.position.y + area.size.height)
129
+ ids.add(node.id);
130
+ }
131
+ const nodes = graph.nodes.filter(node => ids.has(node.id) && !isGroupBoundary(node));
132
+ if (!areas.length && !nodes.length)
133
+ return;
134
+ const left = areas.length ? Math.min(...areas.map(area => area.position.x)) : Math.min(...nodes.map(node => node.position.x));
135
+ const top = Math.min(...areas.map(area => area.position.y + 32), ...nodes.map(node => node.position.y));
136
+ const right = areas.length ? Math.max(...areas.map(area => area.position.x + area.size.width)) : Math.max(...nodes.map(node => node.position.x + ((node.size?.width ?? 220) * (node.scale ?? 1))));
137
+ const bottom = areas.length ? Math.max(...areas.map(area => area.position.y + area.size.height)) : Math.max(...nodes.map(node => node.position.y + ((node.size?.height ?? 120) * (node.scale ?? 1))));
138
+ return { position: { x: left, y: top }, size: { width: right - left, height: bottom - top }, nodes };
139
+ }
140
+ /** Transient handle ID; it is resolved to a persistent boundary port in one edit. */
141
+ export const spagbolNewPortId = 'spagbol.new-port';
142
+ export function resolveNewBoundaryPorts(graph, source, target, registry) {
143
+ const from = { ...source }, to = { ...target };
144
+ const label = (nodeId, portId) => {
145
+ const node = graph.nodes.find(node => node.id === nodeId);
146
+ return node && (node.ports ?? registry.get(node.kind)?.ports(node) ?? []).find(port => port.id === portId)?.label;
147
+ };
148
+ const create = (nodeId, direction, preferred) => {
149
+ const node = graph.nodes.find(node => node.id === nodeId && (node.kind === (direction === 'output' ? spagbolGroupKinds.inputs : spagbolGroupKinds.outputs) || registry.get(node.kind)?.editablePorts === direction));
150
+ if (!node)
151
+ return spagbolNewPortId;
152
+ const base = preferred?.toLowerCase().replace(/[^a-z0-9 ]/g, ' ').replace(/\s+/g, ' ').trim() || 'value';
153
+ let name = base, suffix = 2;
154
+ while (node.ports?.some(port => port.label === name))
155
+ name = `${base} ${suffix++}`;
156
+ return createEditablePort(node, registry, name).id;
157
+ };
158
+ if (from.portId === spagbolNewPortId)
159
+ from.portId = create(from.nodeId, "output", to.type === 'port' ? label(to.nodeId, to.portId) : 'strength');
160
+ if (to.type === 'port' && to.portId === spagbolNewPortId)
161
+ to.portId = create(to.nodeId, "input", label(from.nodeId, from.portId));
162
+ return { source: from, target: to };
163
+ }
164
+ //# sourceMappingURL=groups.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"groups.js","sourceRoot":"","sources":["../../src/core/groups.ts"],"names":[],"mappings":"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","sourcesContent":["import { clone, createId, spagbolGroupKinds, type GraphDocument, type GraphNode, type NodeRegistry, type PortDefinition, type NodeData, type PortEndpoint, type LinkTarget } from './model.js';\nimport { pruneInvalidPorts } from './graph.js';\n\nexport function isGroupBoundary(node: GraphNode): boolean {\n return node.kind === spagbolGroupKinds.inputs || node.kind === spagbolGroupKinds.outputs;\n}\nexport function normalizeGroupPortLabel(label: string): string {\n const normalized = label.trim().toLowerCase().replace(/\\s+/g, ' ');\n if (!/^[a-z0-9]+(?: [a-z0-9]+)*$/.test(normalized)) throw new Error('Use a lowercase, human-readable port name with spaces.');\n return normalized;\n}\nexport function createGroupPort(node: GraphNode, label: string, defaultValue = 0): PortDefinition {\n if (!isGroupBoundary(node)) throw new Error('Ports can be added to Ins or Outs nodes.');\n const port: PortDefinition = { id: createId('port'), label: normalizeGroupPortLabel(label), direction: node.kind === spagbolGroupKinds.inputs ? 'output' : 'input', defaultValue };\n node.ports ??= [];\n if (node.ports.some(item => item.label === port.label)) throw new Error('That port name already exists.');\n node.ports.push(port);\n if (node.kind === spagbolGroupKinds.inputs) node.values[port.id] = defaultValue;\n return port;\n}\n\nexport function createEditablePort(node: GraphNode, registry: NodeRegistry, label: string, defaultValue = 0): PortDefinition {\n if (isGroupBoundary(node)) return createGroupPort(node, label, defaultValue);\n const definition = registry.get(node.kind);\n if (!definition?.editablePorts || !definition.createPort) throw new Error('This node does not expose editable ports.');\n const normalized = normalizeGroupPortLabel(label);\n if (node.ports?.some(port => port.label === normalized)) throw new Error('That port name already exists.');\n const port = definition.createPort(node, normalized);\n node.ports ??= []; node.ports.push(port); node.values[port.id] = port.defaultValue ?? defaultValue;\n return port;\n}\n\n/** Apply a boundary change to every linked group instance and prune dependent cables atomically. */\nexport function synchronizeGroupBoundaries(document: GraphDocument, registry: NodeRegistry): void {\n for (const node of document.nodes) if (node.graph) {\n synchronizeGroupBoundaries(node.graph, registry);\n if (node.kind !== spagbolGroupKinds.group) continue;\n node.ports = [...new Map(node.graph.nodes.filter(isGroupBoundary).flatMap(boundary => (boundary.ports ?? []).map(port => ({ ...port, direction: boundary.kind === spagbolGroupKinds.inputs ? 'input' as const : 'output' as const }))).map(port => [`${port.direction}:${port.id}`, port])).values()];\n const inputs = new Set(node.ports.filter(port => port.direction === 'input').map(port => port.id));\n for (const id of Object.keys(node.values)) if (!inputs.has(id)) delete node.values[id];\n for (const port of node.ports) if (port.direction === 'input' && node.values[port.id] === undefined) node.values[port.id] = port.defaultValue ?? 0;\n pruneInvalidPorts(document, registry, node.id);\n const ids = new Set(node.graph.nodes.map(item => item.id));\n for (const overrides of [node.overrides, node.dataOverrides])\n if (overrides) for (const id of Object.keys(overrides)) if (!ids.has(groupControlPath(id)[0]!)) delete overrides[id];\n }\n}\n\nexport function groupControlKey(path: string | readonly string[]): string {\n return typeof path === 'string' ? path : path.length === 1 ? path[0]! : JSON.stringify(path);\n}\nexport function groupControlPath(key: string): string[] {\n if (key.startsWith('[')) try { const path: unknown = JSON.parse(key); if (Array.isArray(path) && path.every(id => typeof id === 'string')) return path; } catch { /* A literal node id. */ }\n return [key];\n}\nexport function findGroupControl(group: GraphNode, path: string | readonly string[]): GraphNode | undefined {\n const ids = typeof path === 'string' ? groupControlPath(path) : path;\n let graph = group.graph;\n let node: GraphNode | undefined;\n for (const id of ids) { node = graph?.nodes.find(item => item.id === id); if (!node) return; graph = node.graph; }\n return node;\n}\nexport function effectiveGroupGraph(group: GraphNode): GraphDocument | undefined {\n if (!group.graph) return;\n const result: GraphNode = { ...group, graph: clone(group.graph) };\n for (const node of result.graph!.nodes) if (node.graph) {\n node.graph = effectiveGroupGraph(node)!;\n delete node.overrides; delete node.dataOverrides;\n }\n for (const [id, values] of Object.entries(group.overrides ?? {})) {\n const node = findGroupControl(result, id);\n if (node) Object.assign(node.values, values);\n }\n for (const [id, data] of Object.entries(group.dataOverrides ?? {})) {\n const node = findGroupControl(result, id);\n if (node) Object.assign(node.data, data);\n }\n return result.graph;\n}\n\n/** Teia's UI/Controls area projection, with explicit exposure also supported. */\nconst panelCache = new WeakMap<GraphNode, ReturnType<typeof computeGroupPanelLayout>>();\nexport function groupPanelLayout(group: GraphNode) {\n if (!Object.isFrozen(group)) return computeGroupPanelLayout(group);\n if (!panelCache.has(group)) panelCache.set(group, computeGroupPanelLayout(group));\n return panelCache.get(group);\n}\nfunction computeGroupPanelLayout(group: GraphNode) {\n const graph = effectiveGroupGraph(group);\n if (!graph) return;\n const areas = graph.areas.filter(area => area.data?.role === 'controls' || (!area.ownerNodeId && ['ui', 'controls'].includes(area.title.trim().toLowerCase())));\n const exposed = graph.data?.exposedNodeIds;\n const ids = new Set<string>([...(group.panelNodeIds ?? []), ...(Array.isArray(exposed) ? exposed.filter((id): id is string => typeof id === 'string') : [])]);\n for (const area of areas) for (const node of graph.nodes) {\n if (area.locked ? area.nodeIds?.includes(node.id) : node.position.x >= area.position.x && node.position.x < area.position.x + area.size.width && node.position.y >= area.position.y && node.position.y < area.position.y + area.size.height) ids.add(node.id);\n }\n const nodes = graph.nodes.filter(node => ids.has(node.id) && !isGroupBoundary(node));\n if (!areas.length && !nodes.length) return;\n const left = areas.length ? Math.min(...areas.map(area => area.position.x)) : Math.min(...nodes.map(node => node.position.x));\n const top = Math.min(...areas.map(area => area.position.y + 32), ...nodes.map(node => node.position.y));\n const right = areas.length ? Math.max(...areas.map(area => area.position.x + area.size.width)) : Math.max(...nodes.map(node => node.position.x + ((node.size?.width ?? 220) * (node.scale ?? 1))));\n const bottom = areas.length ? Math.max(...areas.map(area => area.position.y + area.size.height)) : Math.max(...nodes.map(node => node.position.y + ((node.size?.height ?? 120) * (node.scale ?? 1))));\n return { position: { x: left, y: top }, size: { width: right - left, height: bottom - top }, nodes };\n}\n\n/** Transient handle ID; it is resolved to a persistent boundary port in one edit. */\nexport const spagbolNewPortId = 'spagbol.new-port';\nexport function resolveNewBoundaryPorts(graph: GraphDocument, source: PortEndpoint, target: LinkTarget, registry: NodeRegistry) {\n const from = { ...source }, to = { ...target };\n const label = (nodeId: string, portId: string) => {\n const node = graph.nodes.find(node => node.id === nodeId);\n return node && (node.ports ?? registry.get(node.kind)?.ports(node) ?? []).find(port => port.id === portId)?.label;\n };\n const create = (nodeId: string, direction: 'input' | 'output', preferred?: string) => {\n const node = graph.nodes.find(node => node.id === nodeId && (node.kind === (direction === 'output' ? spagbolGroupKinds.inputs : spagbolGroupKinds.outputs) || registry.get(node.kind)?.editablePorts === direction));\n if (!node) return spagbolNewPortId;\n const base = preferred?.toLowerCase().replace(/[^a-z0-9 ]/g, ' ').replace(/\\s+/g, ' ').trim() || 'value';\n let name = base, suffix = 2;\n while (node.ports?.some(port => port.label === name)) name = `${base} ${suffix++}`;\n return createEditablePort(node, registry, name).id;\n };\n if (from.portId === spagbolNewPortId) from.portId = create(from.nodeId, \"output\", to.type === 'port' ? label(to.nodeId, to.portId) : 'strength');\n if (to.type === 'port' && to.portId === spagbolNewPortId) to.portId = create(to.nodeId, \"input\", label(from.nodeId, from.portId));\n return { source: from, target: to };\n}\n"]}
@@ -0,0 +1,8 @@
1
+ export * from "./model.js";
2
+ export * from "./graph.js";
3
+ export * from "./controller.js";
4
+ export * from "./serialization.js";
5
+ export * from "./observations.js";
6
+ export * from "./grouping.js";
7
+ export * from "./groups.js";
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC;AAC3B,cAAc,iBAAiB,CAAC;AAChC,cAAc,oBAAoB,CAAC;AACnC,cAAc,mBAAmB,CAAC;AAClC,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC"}
@@ -0,0 +1,8 @@
1
+ export * from "./model.js";
2
+ export * from "./graph.js";
3
+ export * from "./controller.js";
4
+ export * from "./serialization.js";
5
+ export * from "./observations.js";
6
+ export * from "./grouping.js";
7
+ export * from "./groups.js";
8
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC;AAC3B,cAAc,iBAAiB,CAAC;AAChC,cAAc,oBAAoB,CAAC;AACnC,cAAc,mBAAmB,CAAC;AAClC,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC","sourcesContent":["export * from \"./model.js\";\nexport * from \"./graph.js\";\nexport * from \"./controller.js\";\nexport * from \"./serialization.js\";\nexport * from \"./observations.js\";\nexport * from \"./grouping.js\";\nexport * from \"./groups.js\";\n"]}
@@ -0,0 +1,182 @@
1
+ /** Saved data is JSON only. Functions, observers and UI state never enter documents. */
2
+ export type JsonValue = null | boolean | number | string | JsonValue[] | {
3
+ [key: string]: JsonValue;
4
+ };
5
+ export type NodeData = {
6
+ [key: string]: JsonValue;
7
+ };
8
+ export interface Point {
9
+ x: number;
10
+ y: number;
11
+ }
12
+ export interface Size {
13
+ width: number;
14
+ height: number;
15
+ }
16
+ export interface PortDefinition {
17
+ id: string;
18
+ label: string;
19
+ direction: "input" | "output";
20
+ dataType?: string;
21
+ connectable?: boolean;
22
+ valueEditor?: boolean;
23
+ defaultValue?: number;
24
+ min?: number;
25
+ max?: number;
26
+ step?: number;
27
+ integer?: boolean;
28
+ }
29
+ export interface GraphNode<D extends NodeData = NodeData> {
30
+ id: string;
31
+ kind: string;
32
+ label?: string;
33
+ position: Point;
34
+ size?: Size;
35
+ compact?: boolean;
36
+ compactLabel?: string;
37
+ scale?: number;
38
+ enabled?: boolean;
39
+ data: D;
40
+ values: Record<string, number>;
41
+ /** Explicit/dynamic instance ports; a definition may derive ports instead. */
42
+ ports?: PortDefinition[];
43
+ graph?: GraphDocument;
44
+ /** Equal IDs opt into shared nested structure; instance values stay local. */
45
+ sharedGraphId?: string;
46
+ /** Explicit nested nodes to show on this parent. No title conventions in core. */
47
+ panelNodeIds?: string[];
48
+ overrides?: Record<string, Record<string, number>>;
49
+ dataOverrides?: Record<string, NodeData>;
50
+ }
51
+ export interface PortEndpoint {
52
+ nodeId: string;
53
+ portId: string;
54
+ }
55
+ export type LinkTarget = ({
56
+ type: "port";
57
+ } & PortEndpoint) | {
58
+ type: "link";
59
+ linkId: string;
60
+ property: string;
61
+ };
62
+ export interface GraphLink {
63
+ id: string;
64
+ source: PortEndpoint;
65
+ target: LinkTarget;
66
+ mode: string;
67
+ weight: number;
68
+ enabled?: boolean;
69
+ data?: NodeData;
70
+ }
71
+ export interface GraphArea {
72
+ id: string;
73
+ /** A domain node whose UI/layout is owned by this area. */
74
+ ownerNodeId?: string;
75
+ title: string;
76
+ position: Point;
77
+ size: Size;
78
+ collapsed?: boolean;
79
+ locked?: boolean;
80
+ nodeIds?: string[];
81
+ areaIds?: string[];
82
+ /** The top portion remains operable while the rest is collapsed. */
83
+ controlHeight?: number;
84
+ data?: NodeData;
85
+ }
86
+ export interface GraphDocument {
87
+ format: "spagbol";
88
+ version: 1;
89
+ nodes: GraphNode[];
90
+ links: GraphLink[];
91
+ areas: GraphArea[];
92
+ data?: NodeData;
93
+ }
94
+ export interface Selection {
95
+ port?: {
96
+ nodeId: string;
97
+ portId: string;
98
+ direction: "input" | "output";
99
+ };
100
+ nodeIds: string[];
101
+ linkIds: string[];
102
+ areaIds: string[];
103
+ }
104
+ export interface Diagnostic {
105
+ severity: "info" | "warning" | "error";
106
+ code: string;
107
+ message: string;
108
+ path: readonly string[];
109
+ nodeId?: string;
110
+ linkId?: string;
111
+ portId?: string;
112
+ }
113
+ export interface LinkModeDefinition {
114
+ id: string;
115
+ label: string;
116
+ color: string;
117
+ shortcut?: string;
118
+ description: string;
119
+ }
120
+ export interface NodeDefinition<D extends NodeData = NodeData> {
121
+ kind: string;
122
+ label: string;
123
+ category?: string;
124
+ description?: string;
125
+ keywords?: string[];
126
+ createData(): D;
127
+ createGraph?(): GraphDocument;
128
+ editablePorts?: "input" | "output";
129
+ newPortLabel?: string;
130
+ createPort?(node: GraphNode<D>, label: string): PortDefinition;
131
+ ports(node: GraphNode<D>): readonly PortDefinition[];
132
+ defaultSize?: Size;
133
+ validate?(node: GraphNode<D>): readonly string[];
134
+ /** App code can remap asset or domain references when duplicating. */
135
+ cloneData?(data: D, ids: ReadonlyMap<string, string>): D;
136
+ }
137
+ export interface GraphPolicy {
138
+ /** All registered modes are allowed unless the consumer supplies a subset. */
139
+ modes?: readonly string[];
140
+ linkProperties?: readonly string[];
141
+ allowSelfLinks?: boolean;
142
+ allowDuplicateLinks?: boolean;
143
+ /** Host metadata update when deleting a selected editable domain port. */
144
+ deletePort?(node: GraphNode, port: PortDefinition): void;
145
+ canConnect?(source: PortEndpoint, target: LinkTarget, graph: GraphDocument): string | undefined;
146
+ }
147
+ export interface ChangeEvent {
148
+ label: string;
149
+ kinds: readonly ChangeKind[];
150
+ document: GraphDocument;
151
+ path: readonly string[];
152
+ origin: "edit" | "undo" | "redo" | "replace";
153
+ }
154
+ export type ChangeKind = "structure" | "values" | "layout" | "metadata";
155
+ export declare function emptyGraph(): GraphDocument;
156
+ export declare function emptySelection(): Selection;
157
+ export declare function clone<T>(value: T): T;
158
+ export declare function createId(prefix?: string): string;
159
+ export declare function graphAt(document: GraphDocument, path: readonly string[]): GraphDocument;
160
+ export declare function validPath(document: GraphDocument, path: readonly string[]): string[];
161
+ export declare function portsFor(node: GraphNode, definitions: NodeRegistry): readonly PortDefinition[];
162
+ export declare const spagbolGroupKinds: {
163
+ readonly group: "spagbol.group";
164
+ readonly inputs: "spagbol.group.inputs";
165
+ readonly outputs: "spagbol.group.outputs";
166
+ };
167
+ export declare class NodeRegistry {
168
+ #private;
169
+ constructor(definitions?: readonly NodeDefinition[]);
170
+ get(kind: string): NodeDefinition | undefined;
171
+ list(): readonly NodeDefinition[];
172
+ create(kind: string, position: Point, id?: string): GraphNode;
173
+ }
174
+ export declare function defineNode<D extends NodeData>(definition: NodeDefinition<D>): NodeDefinition<D>;
175
+ export declare const standardLinkModes: readonly LinkModeDefinition[];
176
+ export declare class LinkModeRegistry {
177
+ #private;
178
+ constructor(definitions?: readonly LinkModeDefinition[]);
179
+ get(id: string): LinkModeDefinition | undefined;
180
+ list(): readonly LinkModeDefinition[];
181
+ }
182
+ //# sourceMappingURL=model.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"model.d.ts","sourceRoot":"","sources":["../../src/core/model.ts"],"names":[],"mappings":"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"}