@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,84 @@
1
+ import { type ComponentType, type CSSProperties, type ReactNode } from "react";
2
+ import { type Diagnostic, type GraphEditor, type EditorSnapshot, type GraphNode, type NodeData, type ObservationStore, type Point } from "../core/index.js";
3
+ export interface SpagbolCanvasFeatures {
4
+ compact?: boolean;
5
+ selectable?: boolean;
6
+ draggable?: boolean;
7
+ areas?: boolean;
8
+ linkProperties?: boolean;
9
+ nestedGraphs?: boolean;
10
+ panels?: boolean;
11
+ }
12
+ export interface SpagbolNodeViewProps<D extends NodeData = NodeData> {
13
+ node: GraphNode<D>;
14
+ selected: boolean;
15
+ projected: boolean;
16
+ path: readonly string[];
17
+ resetOverride?(): void;
18
+ readOnly: boolean;
19
+ setValue(portId: string, value: number): void;
20
+ patchData(data: Partial<D>, structural?: boolean): void;
21
+ beginGesture(label?: string): void;
22
+ endGesture(cancel?: boolean): void;
23
+ }
24
+ export interface SpagbolNodeView {
25
+ body?: ComponentType<SpagbolNodeViewProps>;
26
+ /** Complete custom geometry; use SpagbolNodePort and the supplied command props. */
27
+ render?: ComponentType<SpagbolNodeViewProps>;
28
+ projected?: ComponentType<SpagbolNodeViewProps>;
29
+ minWidth?: number;
30
+ minHeight?: number;
31
+ resizable?: boolean;
32
+ autoWidth?: boolean;
33
+ compact?: boolean;
34
+ selectable?: boolean;
35
+ draggable?: boolean;
36
+ }
37
+ /** Keep application body props typed while the renderer registry erases payloads. */
38
+ export declare function defineNodeView<D extends NodeData>(view: {
39
+ body?: ComponentType<SpagbolNodeViewProps<D>>;
40
+ render?: ComponentType<SpagbolNodeViewProps<D>>;
41
+ projected?: ComponentType<SpagbolNodeViewProps<D>>;
42
+ minWidth?: number;
43
+ minHeight?: number;
44
+ resizable?: boolean;
45
+ autoWidth?: boolean;
46
+ compact?: boolean;
47
+ selectable?: boolean;
48
+ draggable?: boolean;
49
+ }): SpagbolNodeView;
50
+ export interface GraphContextValue {
51
+ editor: GraphEditor;
52
+ views: Readonly<Record<string, SpagbolNodeView>>;
53
+ features: SpagbolCanvasFeatures;
54
+ diagnostics: readonly Diagnostic[];
55
+ nodeKinds?: readonly string[];
56
+ changeNodeKind?(id: string, kind: string): void | Promise<void>;
57
+ zoom: number;
58
+ connecting: boolean;
59
+ linkOverlay: HTMLElement | null;
60
+ projection?: {
61
+ groupId: string;
62
+ path: readonly string[];
63
+ };
64
+ instanceId: string;
65
+ areaDuplicate?(id: string, phase: "start" | "move" | "end" | "cancel", offset: Point, external?: boolean): void;
66
+ registerTarget(key: string, element: HTMLElement | null): void;
67
+ }
68
+ export declare const GraphContext: import("react").Context<GraphContextValue | null>;
69
+ export declare function useSpagbol(): GraphContextValue;
70
+ export declare function useGraphEditor(): GraphEditor;
71
+ export declare function useEditorSnapshot(editor: GraphEditor): EditorSnapshot;
72
+ /** Subscribe to a stable slice, so value changes do not redraw selection-only UI. */
73
+ export declare function useEditorSelector<T>(editor: GraphEditor, select: (snapshot: EditorSnapshot) => T): T;
74
+ export declare function useObservation<T>(store: ObservationStore<T>, path: readonly string[], nodeId: string, instance?: string): T | undefined;
75
+ /** Register a scoped DOM target for app-owned GPU/canvas rendering. */
76
+ export declare function useRenderTarget(nodeId: string, slot?: string): (element: HTMLElement | null) => void;
77
+ export type SpagbolTheme = CSSProperties & {
78
+ [key: `--spagbol-${string}`]: string | number;
79
+ };
80
+ export interface SpagbolCanvasSlots {
81
+ background?: ReactNode;
82
+ overlay?: ReactNode;
83
+ }
84
+ //# sourceMappingURL=context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/react/context.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,43 @@
1
+ import { createContext, createElement, useCallback, useContext, useSyncExternalStore, } from "react";
2
+ import {} from "../core/index.js";
3
+ /** Keep application body props typed while the renderer registry erases payloads. */
4
+ export function defineNodeView(view) {
5
+ const wrap = (component) => component
6
+ ? (props) => createElement(component, props)
7
+ : undefined;
8
+ return {
9
+ ...view,
10
+ body: wrap(view.body),
11
+ render: wrap(view.render),
12
+ projected: wrap(view.projected),
13
+ };
14
+ }
15
+ export const GraphContext = createContext(null);
16
+ export function useSpagbol() {
17
+ const context = useContext(GraphContext);
18
+ if (!context)
19
+ throw new Error("Spagbol components must be inside SpagbolCanvas.");
20
+ return context;
21
+ }
22
+ export function useGraphEditor() {
23
+ return useSpagbol().editor;
24
+ }
25
+ export function useEditorSnapshot(editor) {
26
+ return useSyncExternalStore(editor.subscribe, editor.getSnapshot, editor.getSnapshot);
27
+ }
28
+ /** Subscribe to a stable slice, so value changes do not redraw selection-only UI. */
29
+ export function useEditorSelector(editor, select) {
30
+ return useSyncExternalStore(editor.subscribe, () => select(editor.getSnapshot()), () => select(editor.getSnapshot()));
31
+ }
32
+ export function useObservation(store, path, nodeId, instance = "") {
33
+ const key = store.key(path, nodeId, instance);
34
+ return useSyncExternalStore((listener) => store.subscribe(key, listener), () => store.get(key), () => undefined);
35
+ }
36
+ /** Register a scoped DOM target for app-owned GPU/canvas rendering. */
37
+ export function useRenderTarget(nodeId, slot = "preview") {
38
+ const { editor, registerTarget, projection } = useSpagbol();
39
+ const path = [...editor.getSnapshot().path, ...(projection ? [projection.groupId, ...projection.path.slice(0, -1)] : [])];
40
+ const key = JSON.stringify([path, nodeId, slot]);
41
+ return useCallback((element) => registerTarget(key, element), [key, registerTarget]);
42
+ }
43
+ //# sourceMappingURL=context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/react/context.tsx"],"names":[],"mappings":"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","sourcesContent":["import {\n createContext,\n createElement,\n useCallback,\n useContext,\n useSyncExternalStore,\n type ComponentType,\n type CSSProperties,\n type ReactNode,\n} from \"react\";\nimport {\n type Diagnostic,\n type GraphEditor,\n type EditorSnapshot,\n type GraphNode,\n type NodeData,\n type ObservationStore,\n type Point,\n} from \"../core/index.js\";\n\nexport interface SpagbolCanvasFeatures {\n compact?: boolean;\n selectable?: boolean;\n draggable?: boolean;\n areas?: boolean;\n linkProperties?: boolean;\n nestedGraphs?: boolean;\n panels?: boolean;\n}\nexport interface SpagbolNodeViewProps<D extends NodeData = NodeData> {\n node: GraphNode<D>;\n selected: boolean;\n projected: boolean;\n path: readonly string[];\n resetOverride?(): void;\n readOnly: boolean;\n setValue(portId: string, value: number): void;\n patchData(data: Partial<D>, structural?: boolean): void;\n beginGesture(label?: string): void;\n endGesture(cancel?: boolean): void;\n}\nexport interface SpagbolNodeView {\n body?: ComponentType<SpagbolNodeViewProps>;\n /** Complete custom geometry; use SpagbolNodePort and the supplied command props. */\n render?: ComponentType<SpagbolNodeViewProps>;\n projected?: ComponentType<SpagbolNodeViewProps>;\n minWidth?: number;\n minHeight?: number;\n resizable?: boolean;\n autoWidth?: boolean;\n compact?: boolean;\n selectable?: boolean;\n draggable?: boolean;\n}\n/** Keep application body props typed while the renderer registry erases payloads. */\nexport function defineNodeView<D extends NodeData>(view: {\n body?: ComponentType<SpagbolNodeViewProps<D>>;\n render?: ComponentType<SpagbolNodeViewProps<D>>;\n projected?: ComponentType<SpagbolNodeViewProps<D>>;\n minWidth?: number;\n minHeight?: number;\n resizable?: boolean;\n autoWidth?: boolean;\n compact?: boolean;\n selectable?: boolean;\n draggable?: boolean;\n}): SpagbolNodeView {\n const wrap = (\n component?: ComponentType<SpagbolNodeViewProps<D>>,\n ): ComponentType<SpagbolNodeViewProps> | undefined =>\n component\n ? (props) => createElement(component, props as SpagbolNodeViewProps<D>)\n : undefined;\n return {\n ...view,\n body: wrap(view.body),\n render: wrap(view.render),\n projected: wrap(view.projected),\n };\n}\nexport interface GraphContextValue {\n editor: GraphEditor;\n views: Readonly<Record<string, SpagbolNodeView>>;\n features: SpagbolCanvasFeatures;\n diagnostics: readonly Diagnostic[];\n nodeKinds?: readonly string[];\n changeNodeKind?(id: string, kind: string): void | Promise<void>;\n zoom: number;\n connecting: boolean;\n linkOverlay: HTMLElement | null;\n projection?: { groupId: string; path: readonly string[] };\n instanceId: string;\n areaDuplicate?(id: string, phase: \"start\" | \"move\" | \"end\" | \"cancel\", offset: Point, external?: boolean): void;\n registerTarget(key: string, element: HTMLElement | null): void;\n}\nexport const GraphContext = createContext<GraphContextValue | null>(null);\nexport function useSpagbol(): GraphContextValue {\n const context = useContext(GraphContext);\n if (!context)\n throw new Error(\"Spagbol components must be inside SpagbolCanvas.\");\n return context;\n}\nexport function useGraphEditor() {\n return useSpagbol().editor;\n}\nexport function useEditorSnapshot(editor: GraphEditor) {\n return useSyncExternalStore(\n editor.subscribe,\n editor.getSnapshot,\n editor.getSnapshot,\n );\n}\n/** Subscribe to a stable slice, so value changes do not redraw selection-only UI. */\nexport function useEditorSelector<T>(editor: GraphEditor, select: (snapshot: EditorSnapshot) => T): T {\n return useSyncExternalStore(editor.subscribe, () => select(editor.getSnapshot()), () => select(editor.getSnapshot()));\n}\nexport function useObservation<T>(\n store: ObservationStore<T>,\n path: readonly string[],\n nodeId: string,\n instance = \"\",\n): T | undefined {\n const key = store.key(path, nodeId, instance);\n return useSyncExternalStore(\n (listener) => store.subscribe(key, listener),\n () => store.get(key),\n () => undefined,\n );\n}\n/** Register a scoped DOM target for app-owned GPU/canvas rendering. */\nexport function useRenderTarget(nodeId: string, slot = \"preview\") {\n const { editor, registerTarget, projection } = useSpagbol();\n const path = [...editor.getSnapshot().path, ...(projection ? [projection.groupId, ...projection.path.slice(0, -1)] : [])];\n const key = JSON.stringify([path, nodeId, slot]);\n return useCallback(\n (element: HTMLElement | null) => registerTarget(key, element),\n [key, registerTarget],\n );\n}\nexport type SpagbolTheme = CSSProperties & {\n [key: `--spagbol-${string}`]: string | number;\n};\nexport interface SpagbolCanvasSlots {\n background?: ReactNode;\n overlay?: ReactNode;\n}\n"]}
@@ -0,0 +1,19 @@
1
+ import type { GraphDocument, GraphFragment, GraphNode, GraphArea, Point } from "../core/index.js";
2
+ export interface DuplicateDrag {
3
+ namespace: string;
4
+ fragment: GraphFragment;
5
+ origin: Point;
6
+ nodeId: string;
7
+ positions: Map<string, Point>;
8
+ external: boolean;
9
+ areaPositions?: Map<string, Point>;
10
+ }
11
+ /** Rendering only: originals and moving copies coexist without editing the document. */
12
+ export declare function duplicateDragPresentation(graph: GraphDocument, drag: DuplicateDrag): {
13
+ graph: GraphDocument;
14
+ originalAreas: Map<string, GraphArea>;
15
+ originalNodes: Map<string, GraphNode<import("../core/model.js").NodeData>>;
16
+ previewNodeIds: Set<string>;
17
+ previewLinks: Map<string, string>;
18
+ };
19
+ //# sourceMappingURL=duplicateDrag.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"duplicateDrag.d.ts","sourceRoot":"","sources":["../../src/react/duplicateDrag.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,aAAa,EACb,aAAa,EACb,SAAS,EACT,SAAS,EACT,KAAK,EACN,MAAM,kBAAkB,CAAC;AAE1B,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,aAAa,CAAC;IACxB,MAAM,EAAE,KAAK,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IAC9B,QAAQ,EAAE,OAAO,CAAC;IAClB,aAAa,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;CACpC;AAED,wFAAwF;AACxF,wBAAgB,yBAAyB,CACvC,KAAK,EAAE,aAAa,EACpB,IAAI,EAAE,aAAa;;;;;;EA2EpB"}
@@ -0,0 +1,71 @@
1
+ /** Rendering only: originals and moving copies coexist without editing the document. */
2
+ export function duplicateDragPresentation(graph, drag) {
3
+ const aliases = new Map(drag.fragment.nodes.map((node) => [
4
+ node.id,
5
+ `${drag.namespace}:origin:${node.id}`,
6
+ ]));
7
+ const areaAliases = new Map(drag.fragment.areas.map(area => [area.id, `${drag.namespace}:origin:${area.id}`]));
8
+ const originalAreas = new Map();
9
+ const originalNodes = new Map();
10
+ const previewNodeIds = new Set(drag.fragment.nodes.map((node) => node.id));
11
+ const candidates = [
12
+ ...drag.fragment.links,
13
+ ...(drag.external ? drag.fragment.boundaryLinks : []),
14
+ ];
15
+ const previewLinks = new Map(candidates.map((link) => [`${drag.namespace}:link:${link.id}`, link.id]));
16
+ const linkIds = new Map(candidates.map((link) => [link.id, `${drag.namespace}:link:${link.id}`]));
17
+ const stationary = graph.nodes.map((node) => {
18
+ const alias = aliases.get(node.id);
19
+ if (!alias)
20
+ return node;
21
+ originalNodes.set(alias, node);
22
+ return { ...node, id: alias };
23
+ });
24
+ const moving = drag.fragment.nodes.map((node) => ({
25
+ ...node,
26
+ position: drag.positions.get(node.id) ?? node.position,
27
+ }));
28
+ const originalLinks = graph.links.map((link) => ({
29
+ ...link,
30
+ source: {
31
+ ...link.source,
32
+ nodeId: aliases.get(link.source.nodeId) ?? link.source.nodeId,
33
+ },
34
+ target: link.target.type === "port"
35
+ ? {
36
+ ...link.target,
37
+ nodeId: aliases.get(link.target.nodeId) ?? link.target.nodeId,
38
+ }
39
+ : link.target,
40
+ }));
41
+ const movingLinks = candidates.map((link) => ({
42
+ ...link,
43
+ id: linkIds.get(link.id),
44
+ target: link.target.type === "link"
45
+ ? {
46
+ ...link.target,
47
+ linkId: linkIds.get(link.target.linkId) ?? link.target.linkId,
48
+ }
49
+ : link.target,
50
+ }));
51
+ const presentation = {
52
+ ...graph,
53
+ nodes: [...stationary, ...moving],
54
+ links: [...originalLinks, ...movingLinks],
55
+ areas: [
56
+ ...graph.areas.map(area => {
57
+ const alias = areaAliases.get(area.id);
58
+ if (alias)
59
+ originalAreas.set(alias, area);
60
+ return { ...area, id: alias ?? area.id,
61
+ ...(area.ownerNodeId ? { ownerNodeId: aliases.get(area.ownerNodeId) ?? area.ownerNodeId } : {}),
62
+ ...(area.nodeIds ? { nodeIds: area.nodeIds.map(id => aliases.get(id) ?? id) } : {}),
63
+ ...(area.areaIds ? { areaIds: area.areaIds.map(id => areaAliases.get(id) ?? id) } : {}),
64
+ };
65
+ }),
66
+ ...drag.fragment.areas.map(area => ({ ...area, position: drag.areaPositions?.get(area.id) ?? area.position })),
67
+ ],
68
+ };
69
+ return { graph: presentation, originalAreas, originalNodes, previewNodeIds, previewLinks };
70
+ }
71
+ //# sourceMappingURL=duplicateDrag.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"duplicateDrag.js","sourceRoot":"","sources":["../../src/react/duplicateDrag.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n GraphDocument,\n GraphFragment,\n GraphNode,\n GraphArea,\n Point,\n} from \"../core/index.js\";\n\nexport interface DuplicateDrag {\n namespace: string;\n fragment: GraphFragment;\n origin: Point;\n nodeId: string;\n positions: Map<string, Point>;\n external: boolean;\n areaPositions?: Map<string, Point>;\n}\n\n/** Rendering only: originals and moving copies coexist without editing the document. */\nexport function duplicateDragPresentation(\n graph: GraphDocument,\n drag: DuplicateDrag,\n) {\n const aliases = new Map(\n drag.fragment.nodes.map((node) => [\n node.id,\n `${drag.namespace}:origin:${node.id}`,\n ]),\n );\n const areaAliases = new Map(drag.fragment.areas.map(area => [area.id, `${drag.namespace}:origin:${area.id}`]));\n const originalAreas = new Map<string, GraphArea>();\n const originalNodes = new Map<string, GraphNode>();\n const previewNodeIds = new Set(drag.fragment.nodes.map((node) => node.id));\n const candidates = [\n ...drag.fragment.links,\n ...(drag.external ? drag.fragment.boundaryLinks : []),\n ];\n const previewLinks = new Map(\n candidates.map((link) => [`${drag.namespace}:link:${link.id}`, link.id]),\n );\n const linkIds = new Map(\n candidates.map((link) => [link.id, `${drag.namespace}:link:${link.id}`]),\n );\n const stationary = graph.nodes.map((node) => {\n const alias = aliases.get(node.id);\n if (!alias) return node;\n originalNodes.set(alias, node);\n return { ...node, id: alias };\n });\n const moving = drag.fragment.nodes.map((node) => ({\n ...node,\n position: drag.positions.get(node.id) ?? node.position,\n }));\n const originalLinks = graph.links.map((link) => ({\n ...link,\n source: {\n ...link.source,\n nodeId: aliases.get(link.source.nodeId) ?? link.source.nodeId,\n },\n target:\n link.target.type === \"port\"\n ? {\n ...link.target,\n nodeId: aliases.get(link.target.nodeId) ?? link.target.nodeId,\n }\n : link.target,\n }));\n const movingLinks = candidates.map((link) => ({\n ...link,\n id: linkIds.get(link.id)!,\n target:\n link.target.type === \"link\"\n ? {\n ...link.target,\n linkId: linkIds.get(link.target.linkId) ?? link.target.linkId,\n }\n : link.target,\n }));\n const presentation: GraphDocument = {\n ...graph,\n nodes: [...stationary, ...moving],\n links: [...originalLinks, ...movingLinks],\n areas: [\n ...graph.areas.map(area => {\n const alias = areaAliases.get(area.id);\n if (alias) originalAreas.set(alias, area);\n return { ...area, id: alias ?? area.id,\n ...(area.ownerNodeId ? { ownerNodeId: aliases.get(area.ownerNodeId) ?? area.ownerNodeId } : {}),\n ...(area.nodeIds ? { nodeIds: area.nodeIds.map(id => aliases.get(id) ?? id) } : {}),\n ...(area.areaIds ? { areaIds: area.areaIds.map(id => areaAliases.get(id) ?? id) } : {}),\n };\n }),\n ...drag.fragment.areas.map(area => ({ ...area, position: drag.areaPositions?.get(area.id) ?? area.position })),\n ],\n };\n return { graph: presentation, originalAreas, originalNodes, previewNodeIds, previewLinks };\n}\n"]}
@@ -0,0 +1,11 @@
1
+ import type { Edge, Node, NodeChange } from "@xyflow/react";
2
+ import type { Size } from "../core/index.js";
3
+ /** Apply measurement notifications without turning them into graph/history edits. */
4
+ export declare function rememberMeasurements<N extends Node>(cache: Map<string, Size>, changes: readonly NodeChange<N>[]): boolean;
5
+ /** React Flow hides the reconnecting edge; a separate identity keeps the source visible. */
6
+ export declare function stationaryReconnectEdge<E extends Edge>(edge: E): E;
7
+ /** Keep native flow records stable when immutable model and geometry are unchanged. */
8
+ export declare function reuseFlowPresentations<T extends {
9
+ id: string;
10
+ }>(previous: T[], next: T[]): T[];
11
+ //# sourceMappingURL=flowPresentation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flowPresentation.d.ts","sourceRoot":"","sources":["../../src/react/flowPresentation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC5D,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAE7C,qFAAqF;AACrF,wBAAgB,oBAAoB,CAAC,CAAC,SAAS,IAAI,EACjD,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,EACxB,OAAO,EAAE,SAAS,UAAU,CAAC,CAAC,CAAC,EAAE,GAChC,OAAO,CAcT;AAED,4FAA4F;AAC5F,wBAAgB,uBAAuB,CAAC,CAAC,SAAS,IAAI,EAAE,IAAI,EAAE,CAAC,GAAG,CAAC,CAWlE;AASD,uFAAuF;AACvF,wBAAgB,sBAAsB,CAAC,CAAC,SAAS;IAAE,EAAE,EAAE,MAAM,CAAA;CAAE,EAAE,QAAQ,EAAE,CAAC,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,GAAG,CAAC,EAAE,CAU9F"}
@@ -0,0 +1,50 @@
1
+ /** Apply measurement notifications without turning them into graph/history edits. */
2
+ export function rememberMeasurements(cache, changes) {
3
+ let changed = false;
4
+ for (const change of changes)
5
+ if (change.type === "dimensions" && change.dimensions) {
6
+ const previous = cache.get(change.id);
7
+ if (previous?.width !== change.dimensions.width ||
8
+ previous?.height !== change.dimensions.height) {
9
+ cache.set(change.id, { ...change.dimensions });
10
+ changed = true;
11
+ }
12
+ }
13
+ return changed;
14
+ }
15
+ /** React Flow hides the reconnecting edge; a separate identity keeps the source visible. */
16
+ export function stationaryReconnectEdge(edge) {
17
+ return {
18
+ ...edge,
19
+ id: `reconnect-preview:${edge.id}`,
20
+ selected: false,
21
+ reconnectable: false,
22
+ selectable: false,
23
+ focusable: false,
24
+ deletable: false,
25
+ data: { ...edge.data, emphasized: false, dimmed: false },
26
+ };
27
+ }
28
+ function shallowRecordEqual(left, right) {
29
+ if (left === right)
30
+ return true;
31
+ if (!left || !right || typeof left !== 'object' || typeof right !== 'object')
32
+ return false;
33
+ const a = left, b = right;
34
+ const keys = Object.keys(a);
35
+ return keys.length === Object.keys(b).length && keys.every(key => a[key] === b[key]);
36
+ }
37
+ /** Keep native flow records stable when immutable model and geometry are unchanged. */
38
+ export function reuseFlowPresentations(previous, next) {
39
+ const byId = new Map(previous.map(item => [item.id, item]));
40
+ const result = next.map(item => {
41
+ const old = byId.get(item.id);
42
+ if (!old)
43
+ return item;
44
+ const a = old, b = item;
45
+ const keys = Object.keys(a);
46
+ return keys.length === Object.keys(b).length && keys.every(key => a[key] === b[key] || (['data', 'style', 'position', 'measured'].includes(key) && shallowRecordEqual(a[key], b[key]))) ? old : item;
47
+ });
48
+ return result.length === previous.length && result.every((item, index) => item === previous[index]) ? previous : result;
49
+ }
50
+ //# sourceMappingURL=flowPresentation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flowPresentation.js","sourceRoot":"","sources":["../../src/react/flowPresentation.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Edge, Node, NodeChange } from \"@xyflow/react\";\nimport type { Size } from \"../core/index.js\";\n\n/** Apply measurement notifications without turning them into graph/history edits. */\nexport function rememberMeasurements<N extends Node>(\n cache: Map<string, Size>,\n changes: readonly NodeChange<N>[],\n): boolean {\n let changed = false;\n for (const change of changes)\n if (change.type === \"dimensions\" && change.dimensions) {\n const previous = cache.get(change.id);\n if (\n previous?.width !== change.dimensions.width ||\n previous?.height !== change.dimensions.height\n ) {\n cache.set(change.id, { ...change.dimensions });\n changed = true;\n }\n }\n return changed;\n}\n\n/** React Flow hides the reconnecting edge; a separate identity keeps the source visible. */\nexport function stationaryReconnectEdge<E extends Edge>(edge: E): E {\n return {\n ...edge,\n id: `reconnect-preview:${edge.id}`,\n selected: false,\n reconnectable: false,\n selectable: false,\n focusable: false,\n deletable: false,\n data: { ...edge.data, emphasized: false, dimmed: false },\n };\n}\n\nfunction shallowRecordEqual(left: unknown, right: unknown): boolean {\n if (left === right) return true;\n if (!left || !right || typeof left !== 'object' || typeof right !== 'object') return false;\n const a = left as Record<string, unknown>, b = right as Record<string, unknown>;\n const keys = Object.keys(a);\n return keys.length === Object.keys(b).length && keys.every(key => a[key] === b[key]);\n}\n/** Keep native flow records stable when immutable model and geometry are unchanged. */\nexport function reuseFlowPresentations<T extends { id: string }>(previous: T[], next: T[]): T[] {\n const byId = new Map(previous.map(item => [item.id, item]));\n const result = next.map(item => {\n const old = byId.get(item.id);\n if (!old) return item;\n const a = old as unknown as Record<string, unknown>, b = item as unknown as Record<string, unknown>;\n const keys = Object.keys(a);\n return keys.length === Object.keys(b).length && keys.every(key => a[key] === b[key] || (['data', 'style', 'position', 'measured'].includes(key) && shallowRecordEqual(a[key], b[key]))) ? old : item;\n });\n return result.length === previous.length && result.every((item, index) => item === previous[index]) ? previous : result;\n}\n"]}
@@ -0,0 +1,13 @@
1
+ import type { Point } from "../core/index.js";
2
+ /** Adapted from Teia's feedback/self-loop treatment; no audio semantics here. */
3
+ export declare function linkPath(source: Point, target: Point, feedback?: boolean, sameNode?: boolean): [string, number, number];
4
+ export declare const nodeFlowId: (id: string) => string;
5
+ export declare const areaFlowId: (id: string) => string;
6
+ export declare const anchorFlowId: (id: string) => string;
7
+ export declare const sourceHandle: (id: string) => string;
8
+ export declare const targetHandle: (id: string) => string;
9
+ export declare function parseHandle(handle: string | null | undefined): {
10
+ direction: "input" | "output";
11
+ id: string;
12
+ } | undefined;
13
+ //# sourceMappingURL=geometry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"geometry.d.ts","sourceRoot":"","sources":["../../src/react/geometry.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAE9C,iFAAiF;AACjF,wBAAgB,QAAQ,CACtB,MAAM,EAAE,KAAK,EACb,MAAM,EAAE,KAAK,EACb,QAAQ,UAAQ,EAChB,QAAQ,UAAQ,GACf,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CA8B1B;AACD,eAAO,MAAM,UAAU,GAAI,IAAI,MAAM,WAAiB,CAAC;AACvD,eAAO,MAAM,UAAU,GAAI,IAAI,MAAM,WAAiB,CAAC;AACvD,eAAO,MAAM,YAAY,GAAI,IAAI,MAAM,WAAmB,CAAC;AAC3D,eAAO,MAAM,YAAY,GAAI,IAAI,MAAM,WAAgB,CAAC;AACxD,eAAO,MAAM,YAAY,GAAI,IAAI,MAAM,WAAe,CAAC;AACvD,wBAAgB,WAAW,CACzB,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAChC;IAAE,SAAS,EAAE,OAAO,GAAG,QAAQ,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAK3D"}
@@ -0,0 +1,43 @@
1
+ import { getBezierPath, Position } from "@xyflow/react";
2
+ /** Adapted from Teia's feedback/self-loop treatment; no audio semantics here. */
3
+ export function linkPath(source, target, feedback = false, sameNode = false) {
4
+ if (!feedback && !sameNode) {
5
+ const [path, x, y] = getBezierPath({
6
+ sourceX: source.x,
7
+ sourceY: source.y,
8
+ targetX: target.x,
9
+ targetY: target.y,
10
+ sourcePosition: Position.Right,
11
+ targetPosition: Position.Left,
12
+ });
13
+ return [path, x, y];
14
+ }
15
+ const dx = Math.abs(source.x - target.x), dy = Math.abs(source.y - target.y);
16
+ if (sameNode) {
17
+ const offset = Math.max(220 * 0.36, dx * 0.34 + dy * 0.12);
18
+ const controlY = Math.min(source.y, target.y) - Math.min(32, Math.max(16, dy * 0.2 + 16));
19
+ return [
20
+ `M ${source.x},${source.y} C ${source.x + offset},${controlY} ${target.x - offset},${controlY} ${target.x},${target.y}`,
21
+ (source.x + target.x) / 2,
22
+ 0.125 * source.y + 0.75 * controlY + 0.125 * target.y,
23
+ ];
24
+ }
25
+ const offset = Math.max(142, dx * 0.8 + dy * 0.2);
26
+ return [
27
+ `M ${source.x},${source.y} C ${source.x + offset},${source.y} ${target.x - offset},${target.y} ${target.x},${target.y}`,
28
+ (source.x + target.x) / 2,
29
+ (source.y + target.y) / 2,
30
+ ];
31
+ }
32
+ export const nodeFlowId = (id) => `node:${id}`;
33
+ export const areaFlowId = (id) => `area:${id}`;
34
+ export const anchorFlowId = (id) => `anchor:${id}`;
35
+ export const sourceHandle = (id) => `out:${id}`;
36
+ export const targetHandle = (id) => `in:${id}`;
37
+ export function parseHandle(handle) {
38
+ if (handle?.startsWith("out:"))
39
+ return { direction: "output", id: handle.slice(4) };
40
+ if (handle?.startsWith("in:"))
41
+ return { direction: "input", id: handle.slice(3) };
42
+ }
43
+ //# sourceMappingURL=geometry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"geometry.js","sourceRoot":"","sources":["../../src/react/geometry.ts"],"names":[],"mappings":"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","sourcesContent":["import { getBezierPath, Position } from \"@xyflow/react\";\nimport type { Point } from \"../core/index.js\";\n\n/** Adapted from Teia's feedback/self-loop treatment; no audio semantics here. */\nexport function linkPath(\n source: Point,\n target: Point,\n feedback = false,\n sameNode = false,\n): [string, number, number] {\n if (!feedback && !sameNode) {\n const [path, x, y] = getBezierPath({\n sourceX: source.x,\n sourceY: source.y,\n targetX: target.x,\n targetY: target.y,\n sourcePosition: Position.Right,\n targetPosition: Position.Left,\n });\n return [path, x, y];\n }\n const dx = Math.abs(source.x - target.x),\n dy = Math.abs(source.y - target.y);\n if (sameNode) {\n const offset = Math.max(220 * 0.36, dx * 0.34 + dy * 0.12);\n const controlY =\n Math.min(source.y, target.y) - Math.min(32, Math.max(16, dy * 0.2 + 16));\n return [\n `M ${source.x},${source.y} C ${source.x + offset},${controlY} ${target.x - offset},${controlY} ${target.x},${target.y}`,\n (source.x + target.x) / 2,\n 0.125 * source.y + 0.75 * controlY + 0.125 * target.y,\n ];\n }\n const offset = Math.max(142, dx * 0.8 + dy * 0.2);\n return [\n `M ${source.x},${source.y} C ${source.x + offset},${source.y} ${target.x - offset},${target.y} ${target.x},${target.y}`,\n (source.x + target.x) / 2,\n (source.y + target.y) / 2,\n ];\n}\nexport const nodeFlowId = (id: string) => `node:${id}`;\nexport const areaFlowId = (id: string) => `area:${id}`;\nexport const anchorFlowId = (id: string) => `anchor:${id}`;\nexport const sourceHandle = (id: string) => `out:${id}`;\nexport const targetHandle = (id: string) => `in:${id}`;\nexport function parseHandle(\n handle: string | null | undefined,\n): { direction: \"input\" | \"output\"; id: string } | undefined {\n if (handle?.startsWith(\"out:\"))\n return { direction: \"output\", id: handle.slice(4) };\n if (handle?.startsWith(\"in:\"))\n return { direction: \"input\", id: handle.slice(3) };\n}\n"]}
@@ -0,0 +1,11 @@
1
+ export { SpagbolCanvas, type SpagbolCanvasProps, type SpagbolLinkPresentation, } from "./SpagbolCanvas.js";
2
+ export { SpagbolNodeFrame, SpagbolNodePort, SpagbolNodePanel, type SpagbolNodeFrameProps, type SpagbolNodePortProps, } from "./SpagbolNodeFrame.js";
3
+ export { SpagbolControlNumber, formatNumber, type SpagbolControlNumberProps, } from "./SpagbolControlNumber.js";
4
+ export { defineNodeView, useEditorSnapshot, useGraphEditor, useObservation, useRenderTarget, useSpagbol, type SpagbolNodeView, type SpagbolNodeViewProps, type SpagbolCanvasFeatures, type SpagbolTheme, type SpagbolCanvasSlots, } from "./context.js";
5
+ export { linkPath } from "./geometry.js";
6
+ export { SpagbolCanvasFlow, type SpagbolCanvasFlowProps, } from "./SpagbolCanvasFlow.js";
7
+ export { SpagbolControlZoom, type SpagbolControlZoomProps, } from "./SpagbolControlZoom.js";
8
+ export { SpagbolCanvasMenu, type SpagbolCanvasMenuProps, } from "./SpagbolCanvasMenu.js";
9
+ export { translateExtentForVisibleContent, clampViewportToTranslateExtent, visibleContentBounds, infiniteCanvasExtent, type CanvasBoundsItem, type CanvasViewport, type CanvasExtent, } from "./viewport.js";
10
+ export { SpagbolCanvasControls, SpagbolCanvasActions, SpagbolCanvasControlsZoom, type SpagbolCanvasControlsOptions, type SpagbolCanvasAction, type SpagbolCanvasMenuItem, type SpagbolCanvasMenuSeparator, } from "./SpagbolCanvasControls.js";
11
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/react/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,aAAa,EACb,KAAK,kBAAkB,EACvB,KAAK,uBAAuB,GAC7B,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,gBAAgB,EAChB,eAAe,EACf,gBAAgB,EAChB,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,GAC1B,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,oBAAoB,EACpB,YAAY,EACZ,KAAK,yBAAyB,GAC/B,MAAM,2BAA2B,CAAC;AACnC,OAAO,EACL,cAAc,EACd,iBAAiB,EACjB,cAAc,EACd,cAAc,EACd,eAAe,EACf,UAAU,EACV,KAAK,eAAe,EACpB,KAAK,oBAAoB,EACzB,KAAK,qBAAqB,EAC1B,KAAK,YAAY,EACjB,KAAK,kBAAkB,GACxB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EACL,iBAAiB,EACjB,KAAK,sBAAsB,GAC5B,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,kBAAkB,EAClB,KAAK,uBAAuB,GAC7B,MAAM,yBAAyB,CAAC;AACjC,OAAO,EACL,iBAAiB,EACjB,KAAK,sBAAsB,GAC5B,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,gCAAgC,EAChC,8BAA8B,EAC9B,oBAAoB,EACpB,oBAAoB,EACpB,KAAK,gBAAgB,EACrB,KAAK,cAAc,EACnB,KAAK,YAAY,GAClB,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,qBAAqB,EACrB,oBAAoB,EACpB,yBAAyB,EACzB,KAAK,4BAA4B,EACjC,KAAK,mBAAmB,EACxB,KAAK,qBAAqB,EAC1B,KAAK,0BAA0B,GAChC,MAAM,4BAA4B,CAAC"}
@@ -0,0 +1,11 @@
1
+ export { SpagbolCanvas, } from "./SpagbolCanvas.js";
2
+ export { SpagbolNodeFrame, SpagbolNodePort, SpagbolNodePanel, } from "./SpagbolNodeFrame.js";
3
+ export { SpagbolControlNumber, formatNumber, } from "./SpagbolControlNumber.js";
4
+ export { defineNodeView, useEditorSnapshot, useGraphEditor, useObservation, useRenderTarget, useSpagbol, } from "./context.js";
5
+ export { linkPath } from "./geometry.js";
6
+ export { SpagbolCanvasFlow, } from "./SpagbolCanvasFlow.js";
7
+ export { SpagbolControlZoom, } from "./SpagbolControlZoom.js";
8
+ export { SpagbolCanvasMenu, } from "./SpagbolCanvasMenu.js";
9
+ export { translateExtentForVisibleContent, clampViewportToTranslateExtent, visibleContentBounds, infiniteCanvasExtent, } from "./viewport.js";
10
+ export { SpagbolCanvasControls, SpagbolCanvasActions, SpagbolCanvasControlsZoom, } from "./SpagbolCanvasControls.js";
11
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/react/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,aAAa,GAGd,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,gBAAgB,EAChB,eAAe,EACf,gBAAgB,GAGjB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,oBAAoB,EACpB,YAAY,GAEb,MAAM,2BAA2B,CAAC;AACnC,OAAO,EACL,cAAc,EACd,iBAAiB,EACjB,cAAc,EACd,cAAc,EACd,eAAe,EACf,UAAU,GAMX,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EACL,iBAAiB,GAElB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,kBAAkB,GAEnB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EACL,iBAAiB,GAElB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,gCAAgC,EAChC,8BAA8B,EAC9B,oBAAoB,EACpB,oBAAoB,GAIrB,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,qBAAqB,EACrB,oBAAoB,EACpB,yBAAyB,GAK1B,MAAM,4BAA4B,CAAC","sourcesContent":["export {\n SpagbolCanvas,\n type SpagbolCanvasProps,\n type SpagbolLinkPresentation,\n} from \"./SpagbolCanvas.js\";\nexport {\n SpagbolNodeFrame,\n SpagbolNodePort,\n SpagbolNodePanel,\n type SpagbolNodeFrameProps,\n type SpagbolNodePortProps,\n} from \"./SpagbolNodeFrame.js\";\nexport {\n SpagbolControlNumber,\n formatNumber,\n type SpagbolControlNumberProps,\n} from \"./SpagbolControlNumber.js\";\nexport {\n defineNodeView,\n useEditorSnapshot,\n useGraphEditor,\n useObservation,\n useRenderTarget,\n useSpagbol,\n type SpagbolNodeView,\n type SpagbolNodeViewProps,\n type SpagbolCanvasFeatures,\n type SpagbolTheme,\n type SpagbolCanvasSlots,\n} from \"./context.js\";\nexport { linkPath } from \"./geometry.js\";\nexport {\n SpagbolCanvasFlow,\n type SpagbolCanvasFlowProps,\n} from \"./SpagbolCanvasFlow.js\";\nexport {\n SpagbolControlZoom,\n type SpagbolControlZoomProps,\n} from \"./SpagbolControlZoom.js\";\nexport {\n SpagbolCanvasMenu,\n type SpagbolCanvasMenuProps,\n} from \"./SpagbolCanvasMenu.js\";\nexport {\n translateExtentForVisibleContent,\n clampViewportToTranslateExtent,\n visibleContentBounds,\n infiniteCanvasExtent,\n type CanvasBoundsItem,\n type CanvasViewport,\n type CanvasExtent,\n} from \"./viewport.js\";\nexport {\n SpagbolCanvasControls,\n SpagbolCanvasActions,\n SpagbolCanvasControlsZoom,\n type SpagbolCanvasControlsOptions,\n type SpagbolCanvasAction,\n type SpagbolCanvasMenuItem,\n type SpagbolCanvasMenuSeparator,\n} from \"./SpagbolCanvasControls.js\";\n"]}
@@ -0,0 +1,7 @@
1
+ import type { Node, NodeChange } from "@xyflow/react";
2
+ import type { GraphDocument, Point } from "../core/index.js";
3
+ /** Accumulate only changed native node positions; keep model records immutable. */
4
+ export declare function previewNodePositions<N extends Node>(previous: ReadonlyMap<string, Point>, changes: readonly NodeChange<N>[]): ReadonlyMap<string, Point>;
5
+ /** Apply a completed multi-node drag as one atomic controller edit. */
6
+ export declare function applyNodePositions(graph: GraphDocument, positions: ReadonlyMap<string, Point>): void;
7
+ //# sourceMappingURL=nodeDrag.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nodeDrag.d.ts","sourceRoot":"","sources":["../../src/react/nodeDrag.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AACtD,OAAO,KAAK,EAAE,aAAa,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAE7D,mFAAmF;AACnF,wBAAgB,oBAAoB,CAAC,CAAC,SAAS,IAAI,EACjD,QAAQ,EAAE,WAAW,CAAC,MAAM,EAAE,KAAK,CAAC,EACpC,OAAO,EAAE,SAAS,UAAU,CAAC,CAAC,CAAC,EAAE,GAChC,WAAW,CAAC,MAAM,EAAE,KAAK,CAAC,CAc5B;AAED,uEAAuE;AACvE,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,aAAa,EACpB,SAAS,EAAE,WAAW,CAAC,MAAM,EAAE,KAAK,CAAC,GACpC,IAAI,CAKN"}
@@ -0,0 +1,27 @@
1
+ /** Accumulate only changed native node positions; keep model records immutable. */
2
+ export function previewNodePositions(previous, changes) {
3
+ let next;
4
+ for (const change of changes) {
5
+ if (change.type !== "position" || !change.position || !change.id.startsWith("node:"))
6
+ continue;
7
+ const { x, y } = change.position;
8
+ if (!Number.isFinite(x) || !Number.isFinite(y))
9
+ continue;
10
+ const id = change.id.slice(5);
11
+ const before = (next ?? previous).get(id);
12
+ if (before?.x === x && before.y === y)
13
+ continue;
14
+ next ??= new Map(previous);
15
+ next.set(id, { x, y });
16
+ }
17
+ return next ?? previous;
18
+ }
19
+ /** Apply a completed multi-node drag as one atomic controller edit. */
20
+ export function applyNodePositions(graph, positions) {
21
+ for (const node of graph.nodes) {
22
+ const position = positions.get(node.id);
23
+ if (position)
24
+ node.position = { ...position };
25
+ }
26
+ }
27
+ //# sourceMappingURL=nodeDrag.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nodeDrag.js","sourceRoot":"","sources":["../../src/react/nodeDrag.ts"],"names":[],"mappings":"AAGA,mFAAmF;AACnF,MAAM,UAAU,oBAAoB,CAClC,QAAoC,EACpC,OAAiC;IAEjC,IAAI,IAAoC,CAAC;IACzC,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,IAAI,MAAM,CAAC,IAAI,KAAK,UAAU,IAAI,CAAC,MAAM,CAAC,QAAQ,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC;YAClF,SAAS;QACX,MAAM,EAAE,CAAC,EAAE,CAAC,EAAE,GAAG,MAAM,CAAC,QAAQ,CAAC;QACjC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC;YAAE,SAAS;QACzD,MAAM,EAAE,GAAG,MAAM,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QAC9B,MAAM,MAAM,GAAG,CAAC,IAAI,IAAI,QAAQ,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAC1C,IAAI,MAAM,EAAE,CAAC,KAAK,CAAC,IAAI,MAAM,CAAC,CAAC,KAAK,CAAC;YAAE,SAAS;QAChD,IAAI,KAAK,IAAI,GAAG,CAAC,QAAQ,CAAC,CAAC;QAC3B,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;IACzB,CAAC;IACD,OAAO,IAAI,IAAI,QAAQ,CAAC;AAC1B,CAAC;AAED,uEAAuE;AACvE,MAAM,UAAU,kBAAkB,CAChC,KAAoB,EACpB,SAAqC;IAErC,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;QAC/B,MAAM,QAAQ,GAAG,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACxC,IAAI,QAAQ;YAAE,IAAI,CAAC,QAAQ,GAAG,EAAE,GAAG,QAAQ,EAAE,CAAC;IAChD,CAAC;AACH,CAAC","sourcesContent":["import type { Node, NodeChange } from \"@xyflow/react\";\nimport type { GraphDocument, Point } from \"../core/index.js\";\n\n/** Accumulate only changed native node positions; keep model records immutable. */\nexport function previewNodePositions<N extends Node>(\n previous: ReadonlyMap<string, Point>,\n changes: readonly NodeChange<N>[],\n): ReadonlyMap<string, Point> {\n let next: Map<string, Point> | undefined;\n for (const change of changes) {\n if (change.type !== \"position\" || !change.position || !change.id.startsWith(\"node:\"))\n continue;\n const { x, y } = change.position;\n if (!Number.isFinite(x) || !Number.isFinite(y)) continue;\n const id = change.id.slice(5);\n const before = (next ?? previous).get(id);\n if (before?.x === x && before.y === y) continue;\n next ??= new Map(previous);\n next.set(id, { x, y });\n }\n return next ?? previous;\n}\n\n/** Apply a completed multi-node drag as one atomic controller edit. */\nexport function applyNodePositions(\n graph: GraphDocument,\n positions: ReadonlyMap<string, Point>,\n): void {\n for (const node of graph.nodes) {\n const position = positions.get(node.id);\n if (position) node.position = { ...position };\n }\n}\n"]}
@@ -0,0 +1,10 @@
1
+ import type { GraphDocument } from "../core/index.js";
2
+ export type PortState = {
3
+ inputs: Set<string>;
4
+ outputs: Set<string>;
5
+ replaced: Set<string>;
6
+ };
7
+ export declare const emptyPortState: PortState;
8
+ /** Value edits share connection state; rewiring/mode/enabled edits invalidate it. */
9
+ export declare function graphPortStates(graph: GraphDocument): Map<string, PortState>;
10
+ //# sourceMappingURL=portState.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"portState.d.ts","sourceRoot":"","sources":["../../src/react/portState.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACtD,MAAM,MAAM,SAAS,GAAG;IAAE,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAAC,OAAO,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAAC,QAAQ,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;CAAE,CAAC;AAG7F,eAAO,MAAM,cAAc,EAAE,SAA0E,CAAC;AACxG,qFAAqF;AACrF,wBAAgB,eAAe,CAAC,KAAK,EAAE,aAAa,0BAiBnD"}
@@ -0,0 +1,35 @@
1
+ const documentCache = new WeakMap();
2
+ const connectionCache = new WeakMap();
3
+ export const emptyPortState = { inputs: new Set(), outputs: new Set(), replaced: new Set() };
4
+ /** Value edits share connection state; rewiring/mode/enabled edits invalidate it. */
5
+ export function graphPortStates(graph) {
6
+ const cached = documentCache.get(graph);
7
+ if (cached)
8
+ return cached;
9
+ const enabledKey = JSON.stringify(graph.nodes.map(node => [node.id, node.enabled !== false]));
10
+ const connections = Object.isFrozen(graph) ? connectionCache.get(graph.links) : undefined;
11
+ if (connections?.enabled === enabledKey) {
12
+ documentCache.set(graph, connections.state);
13
+ return connections.state;
14
+ }
15
+ const result = new Map();
16
+ const enabled = new Set(graph.nodes.filter(node => node.enabled !== false).map(node => node.id));
17
+ const state = (id) => { let value = result.get(id); if (!value) {
18
+ value = { inputs: new Set(), outputs: new Set(), replaced: new Set() };
19
+ result.set(id, value);
20
+ } return value; };
21
+ for (const link of graph.links) {
22
+ state(link.source.nodeId).outputs.add(link.source.portId);
23
+ if (link.target.type === 'port') {
24
+ state(link.target.nodeId).inputs.add(link.target.portId);
25
+ if (link.enabled !== false && link.mode === 'set' && enabled.has(link.source.nodeId))
26
+ state(link.target.nodeId).replaced.add(link.target.portId);
27
+ }
28
+ }
29
+ if (Object.isFrozen(graph)) {
30
+ documentCache.set(graph, result);
31
+ connectionCache.set(graph.links, { enabled: enabledKey, state: result });
32
+ }
33
+ return result;
34
+ }
35
+ //# sourceMappingURL=portState.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"portState.js","sourceRoot":"","sources":["../../src/react/portState.ts"],"names":[],"mappings":"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","sourcesContent":["import type { GraphDocument } from \"../core/index.js\";\nexport type PortState = { inputs: Set<string>; outputs: Set<string>; replaced: Set<string> };\nconst documentCache = new WeakMap<GraphDocument, Map<string, PortState>>();\nconst connectionCache = new WeakMap<GraphDocument['links'], { enabled: string; state: Map<string, PortState> }>();\nexport const emptyPortState: PortState = { inputs: new Set(), outputs: new Set(), replaced: new Set() };\n/** Value edits share connection state; rewiring/mode/enabled edits invalidate it. */\nexport function graphPortStates(graph: GraphDocument) {\n const cached = documentCache.get(graph); if (cached) return cached;\n const enabledKey = JSON.stringify(graph.nodes.map(node => [node.id, node.enabled !== false]));\n const connections = Object.isFrozen(graph) ? connectionCache.get(graph.links) : undefined;\n if (connections?.enabled === enabledKey) { documentCache.set(graph, connections.state); return connections.state; }\n const result = new Map<string, PortState>();\n const enabled = new Set(graph.nodes.filter(node => node.enabled !== false).map(node => node.id));\n const state = (id: string) => { let value = result.get(id); if (!value) { value = { inputs: new Set(), outputs: new Set(), replaced: new Set() }; result.set(id, value); } return value; };\n for (const link of graph.links) {\n state(link.source.nodeId).outputs.add(link.source.portId);\n if (link.target.type === 'port') {\n state(link.target.nodeId).inputs.add(link.target.portId);\n if (link.enabled !== false && link.mode === 'set' && enabled.has(link.source.nodeId)) state(link.target.nodeId).replaced.add(link.target.portId);\n }\n }\n if (Object.isFrozen(graph)) { documentCache.set(graph, result); connectionCache.set(graph.links, { enabled: enabledKey, state: result }); }\n return result;\n}\n"]}
@@ -0,0 +1,24 @@
1
+ import type { Point, Size } from "../core/index.js";
2
+ export interface CanvasBoundsItem {
3
+ position: Point;
4
+ size: Size;
5
+ hidden?: boolean;
6
+ }
7
+ export interface CanvasViewport {
8
+ x: number;
9
+ y: number;
10
+ zoom: number;
11
+ }
12
+ export type CanvasExtent = [[number, number], [number, number]];
13
+ export declare const infiniteCanvasExtent: CanvasExtent;
14
+ export declare function visibleContentBounds(items: readonly CanvasBoundsItem[]): {
15
+ x: number;
16
+ y: number;
17
+ width: number;
18
+ height: number;
19
+ };
20
+ /** Teia's pan policy: extend content bounds by 70% of the visible screen on each side. */
21
+ export declare function translateExtentForVisibleContent(items: readonly CanvasBoundsItem[], viewport: CanvasViewport, size: Size, emptyScreenRatio?: number): CanvasExtent;
22
+ /** Re-clamp after zoom, resize, collapse, graph deletion or nested navigation. */
23
+ export declare function clampViewportToTranslateExtent(viewport: CanvasViewport, extent: CanvasExtent, size: Size): CanvasViewport;
24
+ //# sourceMappingURL=viewport.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"viewport.d.ts","sourceRoot":"","sources":["../../src/react/viewport.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAEpD,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,KAAK,CAAC;IAChB,IAAI,EAAE,IAAI,CAAC;IACX,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AACD,MAAM,WAAW,cAAc;IAC7B,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,IAAI,EAAE,MAAM,CAAC;CACd;AACD,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;AAChE,eAAO,MAAM,oBAAoB,EAAE,YAGlC,CAAC;AAEF,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,SAAS,gBAAgB,EAAE;;;;;EA2BtE;AAID,0FAA0F;AAC1F,wBAAgB,gCAAgC,CAC9C,KAAK,EAAE,SAAS,gBAAgB,EAAE,EAClC,QAAQ,EAAE,cAAc,EACxB,IAAI,EAAE,IAAI,EACV,gBAAgB,SAAM,GACrB,YAAY,CAad;AAED,kFAAkF;AAClF,wBAAgB,8BAA8B,CAC5C,QAAQ,EAAE,cAAc,EACxB,MAAM,EAAE,YAAY,EACpB,IAAI,EAAE,IAAI,GACT,cAAc,CAchB"}