@onerjs/shared-ui-components 8.44.6 → 8.44.8

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 (44) hide show
  1. package/components/bars/CommandButton.module.scss +2 -0
  2. package/historyStack.js +3 -0
  3. package/historyStack.js.map +1 -1
  4. package/nodeGraphSystem/common.module.scss +3 -0
  5. package/nodeGraphSystem/graphCanvas.d.ts +37 -0
  6. package/nodeGraphSystem/graphCanvas.js +139 -6
  7. package/nodeGraphSystem/graphCanvas.js.map +1 -1
  8. package/nodeGraphSystem/graphMinimap.d.ts +79 -0
  9. package/nodeGraphSystem/graphMinimap.js +319 -0
  10. package/nodeGraphSystem/graphMinimap.js.map +1 -0
  11. package/nodeGraphSystem/graphMinimap.module.scss +30 -0
  12. package/nodeGraphSystem/graphNode.d.ts +16 -0
  13. package/nodeGraphSystem/graphNode.js +76 -1
  14. package/nodeGraphSystem/graphNode.js.map +1 -1
  15. package/nodeGraphSystem/graphNode.module.scss +61 -0
  16. package/nodeGraphSystem/graphSearch.d.ts +54 -0
  17. package/nodeGraphSystem/graphSearch.js +181 -0
  18. package/nodeGraphSystem/graphSearch.js.map +1 -0
  19. package/nodeGraphSystem/graphSearch.module.scss +86 -0
  20. package/nodeGraphSystem/graphStickyNote.d.ts +94 -0
  21. package/nodeGraphSystem/graphStickyNote.js +310 -0
  22. package/nodeGraphSystem/graphStickyNote.js.map +1 -0
  23. package/nodeGraphSystem/graphStickyNote.module.scss +87 -0
  24. package/nodeGraphSystem/interfaces/nodeData.d.ts +2 -0
  25. package/nodeGraphSystem/interfaces/nodeData.js.map +1 -1
  26. package/nodeGraphSystem/interfaces/nodeLocationInfo.d.ts +10 -0
  27. package/nodeGraphSystem/interfaces/nodeLocationInfo.js.map +1 -1
  28. package/nodeGraphSystem/interfaces/portData.d.ts +2 -1
  29. package/nodeGraphSystem/interfaces/portData.js +1 -0
  30. package/nodeGraphSystem/interfaces/portData.js.map +1 -1
  31. package/nodeGraphSystem/interfaces/selectionChangedOptions.d.ts +2 -1
  32. package/nodeGraphSystem/interfaces/selectionChangedOptions.js.map +1 -1
  33. package/nodeGraphSystem/nodeLink.d.ts +12 -0
  34. package/nodeGraphSystem/nodeLink.js +77 -0
  35. package/nodeGraphSystem/nodeLink.js.map +1 -1
  36. package/nodeGraphSystem/nodePort.js +26 -1
  37. package/nodeGraphSystem/nodePort.js.map +1 -1
  38. package/nodeGraphSystem/nodePort.module.scss +37 -0
  39. package/nodeGraphSystem/stateManager.d.ts +6 -0
  40. package/nodeGraphSystem/stateManager.js +6 -0
  41. package/nodeGraphSystem/stateManager.js.map +1 -1
  42. package/package.json +1 -1
  43. package/split/splitContainer.js +2 -1
  44. package/split/splitContainer.js.map +1 -1
@@ -0,0 +1,79 @@
1
+ import * as React from "react";
2
+ import type { GraphCanvasComponent } from "./graphCanvas.js";
3
+ /**
4
+ * Props for the GraphMinimapComponent.
5
+ * This component can be reused by any editor that uses GraphCanvasComponent.
6
+ */
7
+ export interface IGraphMinimapComponentProps {
8
+ /** The graph canvas to visualize */
9
+ canvas: GraphCanvasComponent;
10
+ /** How long (ms) the minimap stays visible after the last interaction. Default 1500. */
11
+ hideDelayMs?: number;
12
+ /** Width of the minimap in pixels. Default 200. */
13
+ width?: number;
14
+ /** Height of the minimap in pixels. Default 140. */
15
+ height?: number;
16
+ }
17
+ /**
18
+ * A minimap overlay that shows a scaled-down overview of the node graph.
19
+ * It appears when the user zooms or pans and auto-hides after a delay.
20
+ * Clicking/dragging on the minimap pans the canvas to that position.
21
+ *
22
+ * This is a reusable component that works with any GraphCanvasComponent-based editor
23
+ * (Flow Graph Editor, Node Material Editor, Node Geometry Editor, etc.).
24
+ */
25
+ export declare class GraphMinimapComponent extends React.Component<IGraphMinimapComponentProps> {
26
+ private _canvasRef;
27
+ private _containerRef;
28
+ private _rafId;
29
+ private _hideTimer;
30
+ private _visible;
31
+ private _isDragging;
32
+ /** Track last canvas state to detect changes driven by external APIs (zoomToFit, etc.) */
33
+ private _lastX;
34
+ private _lastY;
35
+ private _lastZoom;
36
+ /** Stored transform values for minimap hit-testing */
37
+ private _mapScale;
38
+ private _mapOffsetX;
39
+ private _mapOffsetY;
40
+ private _mapTotalMinX;
41
+ private _mapTotalMinY;
42
+ private get _hideDelay();
43
+ private get _width();
44
+ private get _height();
45
+ /** @internal */
46
+ componentDidMount(): void;
47
+ /** @internal */
48
+ componentWillUnmount(): void;
49
+ /**
50
+ * Show the minimap and schedule auto-hide.
51
+ */
52
+ private _show;
53
+ private _applyVisibility;
54
+ /**
55
+ * Main render loop — polls the canvas state every frame and redraws
56
+ * the minimap when a change is detected.
57
+ */
58
+ private _tick;
59
+ /**
60
+ * Compute the bounding box of all nodes and frames in graph-space.
61
+ * @returns the min/max coordinates of the graph content
62
+ */
63
+ private _computeGraphBounds;
64
+ /**
65
+ * Draw the minimap onto the <canvas> element.
66
+ */
67
+ private _draw;
68
+ /**
69
+ * Convert a pointer event on the minimap to graph-space coordinates and
70
+ * pan the canvas to center on that point.
71
+ * @param evt - the pointer event from the minimap
72
+ */
73
+ private _panToMinimapPoint;
74
+ private _onPointerDown;
75
+ private _onPointerMove;
76
+ private _onPointerUp;
77
+ /** @internal */
78
+ render(): import("react/jsx-runtime").JSX.Element;
79
+ }
@@ -0,0 +1,319 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import * as React from "react";
3
+ import * as styles from "./graphMinimap.module.scss";
4
+ /**
5
+ * A minimap overlay that shows a scaled-down overview of the node graph.
6
+ * It appears when the user zooms or pans and auto-hides after a delay.
7
+ * Clicking/dragging on the minimap pans the canvas to that position.
8
+ *
9
+ * This is a reusable component that works with any GraphCanvasComponent-based editor
10
+ * (Flow Graph Editor, Node Material Editor, Node Geometry Editor, etc.).
11
+ */
12
+ export class GraphMinimapComponent extends React.Component {
13
+ constructor() {
14
+ super(...arguments);
15
+ this._canvasRef = React.createRef();
16
+ this._containerRef = React.createRef();
17
+ this._rafId = 0;
18
+ this._hideTimer = null;
19
+ this._visible = false;
20
+ this._isDragging = false;
21
+ /** Track last canvas state to detect changes driven by external APIs (zoomToFit, etc.) */
22
+ this._lastX = 0;
23
+ this._lastY = 0;
24
+ this._lastZoom = 1;
25
+ /** Stored transform values for minimap hit-testing */
26
+ this._mapScale = 1;
27
+ this._mapOffsetX = 0;
28
+ this._mapOffsetY = 0;
29
+ this._mapTotalMinX = 0;
30
+ this._mapTotalMinY = 0;
31
+ /**
32
+ * Main render loop — polls the canvas state every frame and redraws
33
+ * the minimap when a change is detected.
34
+ */
35
+ this._tick = () => {
36
+ const canvas = this.props.canvas;
37
+ const { x, y, zoom } = canvas;
38
+ if (x !== this._lastX || y !== this._lastY || zoom !== this._lastZoom) {
39
+ this._lastX = x;
40
+ this._lastY = y;
41
+ this._lastZoom = zoom;
42
+ this._show();
43
+ this._draw();
44
+ }
45
+ this._rafId = requestAnimationFrame(this._tick);
46
+ };
47
+ this._onPointerDown = (evt) => {
48
+ evt.preventDefault();
49
+ evt.stopPropagation();
50
+ this._isDragging = true;
51
+ evt.target.setPointerCapture(evt.pointerId);
52
+ this._panToMinimapPoint(evt);
53
+ };
54
+ this._onPointerMove = (evt) => {
55
+ if (!this._isDragging) {
56
+ return;
57
+ }
58
+ evt.preventDefault();
59
+ evt.stopPropagation();
60
+ this._panToMinimapPoint(evt);
61
+ };
62
+ this._onPointerUp = (evt) => {
63
+ if (!this._isDragging) {
64
+ return;
65
+ }
66
+ this._isDragging = false;
67
+ evt.target.releasePointerCapture(evt.pointerId);
68
+ // Re-arm the hide timer
69
+ this._show();
70
+ };
71
+ }
72
+ get _hideDelay() {
73
+ return this.props.hideDelayMs ?? 1500;
74
+ }
75
+ get _width() {
76
+ return this.props.width ?? 200;
77
+ }
78
+ get _height() {
79
+ return this.props.height ?? 140;
80
+ }
81
+ /** @internal */
82
+ componentDidMount() {
83
+ this._rafId = requestAnimationFrame(this._tick);
84
+ }
85
+ /** @internal */
86
+ componentWillUnmount() {
87
+ cancelAnimationFrame(this._rafId);
88
+ if (this._hideTimer) {
89
+ clearTimeout(this._hideTimer);
90
+ }
91
+ }
92
+ /**
93
+ * Show the minimap and schedule auto-hide.
94
+ */
95
+ _show() {
96
+ if (!this._visible) {
97
+ this._visible = true;
98
+ this._applyVisibility();
99
+ }
100
+ // Reset hide timer
101
+ if (this._hideTimer) {
102
+ clearTimeout(this._hideTimer);
103
+ }
104
+ this._hideTimer = setTimeout(() => {
105
+ if (!this._isDragging) {
106
+ this._visible = false;
107
+ this._applyVisibility();
108
+ }
109
+ }, this._hideDelay);
110
+ }
111
+ _applyVisibility() {
112
+ const container = this._containerRef.current;
113
+ if (!container) {
114
+ return;
115
+ }
116
+ if (this._visible) {
117
+ container.classList.remove(styles["minimap-hidden"]);
118
+ container.classList.add(styles["minimap-visible"]);
119
+ }
120
+ else {
121
+ container.classList.remove(styles["minimap-visible"]);
122
+ container.classList.add(styles["minimap-hidden"]);
123
+ }
124
+ }
125
+ /**
126
+ * Compute the bounding box of all nodes and frames in graph-space.
127
+ * @returns the min/max coordinates of the graph content
128
+ */
129
+ _computeGraphBounds() {
130
+ const canvas = this.props.canvas;
131
+ const nodes = canvas.nodes;
132
+ const frames = canvas.frames;
133
+ const stickyNotes = canvas.stickyNotes;
134
+ if (nodes.length === 0 && frames.length === 0 && stickyNotes.length === 0) {
135
+ return { minX: 0, minY: 0, maxX: 1000, maxY: 700 };
136
+ }
137
+ let minX = Infinity;
138
+ let minY = Infinity;
139
+ let maxX = -Infinity;
140
+ let maxY = -Infinity;
141
+ for (const node of nodes) {
142
+ const nx = node.x;
143
+ const ny = node.y;
144
+ const nw = node.width || 200;
145
+ const nh = node.height || 40;
146
+ if (nx < minX) {
147
+ minX = nx;
148
+ }
149
+ if (ny < minY) {
150
+ minY = ny;
151
+ }
152
+ if (nx + nw > maxX) {
153
+ maxX = nx + nw;
154
+ }
155
+ if (ny + nh > maxY) {
156
+ maxY = ny + nh;
157
+ }
158
+ }
159
+ for (const frame of frames) {
160
+ const fx = frame.x;
161
+ const fy = frame.y;
162
+ const fw = frame.width || 200;
163
+ const fh = frame.height || 100;
164
+ if (fx < minX) {
165
+ minX = fx;
166
+ }
167
+ if (fy < minY) {
168
+ minY = fy;
169
+ }
170
+ if (fx + fw > maxX) {
171
+ maxX = fx + fw;
172
+ }
173
+ if (fy + fh > maxY) {
174
+ maxY = fy + fh;
175
+ }
176
+ }
177
+ for (const note of stickyNotes) {
178
+ const sx = note.x;
179
+ const sy = note.y;
180
+ const sw = note.width || 180;
181
+ const sh = note.height || 120;
182
+ if (sx < minX) {
183
+ minX = sx;
184
+ }
185
+ if (sy < minY) {
186
+ minY = sy;
187
+ }
188
+ if (sx + sw > maxX) {
189
+ maxX = sx + sw;
190
+ }
191
+ if (sy + sh > maxY) {
192
+ maxY = sy + sh;
193
+ }
194
+ }
195
+ // Pad the bounds a bit so nodes aren't right at the edge
196
+ const pad = 100;
197
+ return { minX: minX - pad, minY: minY - pad, maxX: maxX + pad, maxY: maxY + pad };
198
+ }
199
+ /**
200
+ * Draw the minimap onto the <canvas> element.
201
+ */
202
+ _draw() {
203
+ const el = this._canvasRef.current;
204
+ if (!el) {
205
+ return;
206
+ }
207
+ const ctx = el.getContext("2d");
208
+ if (!ctx) {
209
+ return;
210
+ }
211
+ const canvas = this.props.canvas;
212
+ const dpr = window.devicePixelRatio || 1;
213
+ const w = this._width;
214
+ const h = this._height;
215
+ // Ensure backing store matches CSS size
216
+ if (el.width !== w * dpr || el.height !== h * dpr) {
217
+ el.width = w * dpr;
218
+ el.height = h * dpr;
219
+ }
220
+ ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
221
+ ctx.clearRect(0, 0, w, h);
222
+ // Compute graph bounds
223
+ const bounds = this._computeGraphBounds();
224
+ // Also include the viewport in the bounds so scrolling far from nodes
225
+ // still makes sense.
226
+ const vpLeftInGraph = -canvas.x / canvas.zoom;
227
+ const vpTopInGraph = -canvas.y / canvas.zoom;
228
+ const hostRect = canvas._hostCanvas?.getBoundingClientRect();
229
+ const vpW = (hostRect?.width ?? 800) / canvas.zoom;
230
+ const vpH = (hostRect?.height ?? 600) / canvas.zoom;
231
+ const totalMinX = Math.min(bounds.minX, vpLeftInGraph);
232
+ const totalMinY = Math.min(bounds.minY, vpTopInGraph);
233
+ const totalMaxX = Math.max(bounds.maxX, vpLeftInGraph + vpW);
234
+ const totalMaxY = Math.max(bounds.maxY, vpTopInGraph + vpH);
235
+ const totalW = totalMaxX - totalMinX;
236
+ const totalH = totalMaxY - totalMinY;
237
+ // Scale to fit the minimap
238
+ const scale = Math.min(w / totalW, h / totalH);
239
+ // Center the content
240
+ const offsetX = (w - totalW * scale) / 2;
241
+ const offsetY = (h - totalH * scale) / 2;
242
+ const toMiniX = (gx) => (gx - totalMinX) * scale + offsetX;
243
+ const toMiniY = (gy) => (gy - totalMinY) * scale + offsetY;
244
+ // Draw frames
245
+ for (const frame of canvas.frames) {
246
+ const fx = toMiniX(frame.x);
247
+ const fy = toMiniY(frame.y);
248
+ const fw = (frame.width || 200) * scale;
249
+ const fh = (frame.height || 100) * scale;
250
+ ctx.fillStyle = "rgba(100, 100, 100, 0.3)";
251
+ ctx.strokeStyle = "rgba(150, 150, 150, 0.5)";
252
+ ctx.lineWidth = 0.5;
253
+ ctx.fillRect(fx, fy, fw, fh);
254
+ ctx.strokeRect(fx, fy, fw, fh);
255
+ }
256
+ // Draw nodes
257
+ const selectedSet = new Set(canvas.selectedNodes);
258
+ for (const node of canvas.nodes) {
259
+ const nx = toMiniX(node.x);
260
+ const ny = toMiniY(node.y);
261
+ const nw = Math.max((node.width || 200) * scale, 2);
262
+ const nh = Math.max((node.height || 40) * scale, 1.5);
263
+ ctx.fillStyle = selectedSet.has(node) ? "#5da0e0" : "#888";
264
+ ctx.fillRect(nx, ny, nw, nh);
265
+ }
266
+ // Draw sticky notes
267
+ for (const note of canvas.stickyNotes) {
268
+ const snx = toMiniX(note.x);
269
+ const sny = toMiniY(note.y);
270
+ const snw = (note.width || 180) * scale;
271
+ const snh = (note.height || 120) * scale;
272
+ ctx.fillStyle = "rgba(255, 235, 130, 0.5)";
273
+ ctx.fillRect(snx, sny, snw, snh);
274
+ }
275
+ // Draw viewport rectangle
276
+ const vx = toMiniX(vpLeftInGraph);
277
+ const vy = toMiniY(vpTopInGraph);
278
+ const vw = vpW * scale;
279
+ const vh = vpH * scale;
280
+ ctx.strokeStyle = "rgba(255, 255, 255, 0.7)";
281
+ ctx.lineWidth = 1.5;
282
+ ctx.strokeRect(vx, vy, vw, vh);
283
+ // Store transform for hit-testing in pointer events
284
+ this._mapScale = scale;
285
+ this._mapOffsetX = offsetX;
286
+ this._mapOffsetY = offsetY;
287
+ this._mapTotalMinX = totalMinX;
288
+ this._mapTotalMinY = totalMinY;
289
+ }
290
+ /**
291
+ * Convert a pointer event on the minimap to graph-space coordinates and
292
+ * pan the canvas to center on that point.
293
+ * @param evt - the pointer event from the minimap
294
+ */
295
+ _panToMinimapPoint(evt) {
296
+ const el = this._canvasRef.current;
297
+ if (!el) {
298
+ return;
299
+ }
300
+ const rect = el.getBoundingClientRect();
301
+ const localX = evt.clientX - rect.left;
302
+ const localY = evt.clientY - rect.top;
303
+ // Convert minimap pixel to graph-space
304
+ const graphX = (localX - this._mapOffsetX) / this._mapScale + this._mapTotalMinX;
305
+ const graphY = (localY - this._mapOffsetY) / this._mapScale + this._mapTotalMinY;
306
+ // Center the viewport on this graph point
307
+ const canvas = this.props.canvas;
308
+ const hostRect = canvas._hostCanvas?.getBoundingClientRect();
309
+ const vpW = (hostRect?.width ?? 800) / canvas.zoom;
310
+ const vpH = (hostRect?.height ?? 600) / canvas.zoom;
311
+ canvas.x = -(graphX - vpW / 2) * canvas.zoom;
312
+ canvas.y = -(graphY - vpH / 2) * canvas.zoom;
313
+ }
314
+ /** @internal */
315
+ render() {
316
+ return (_jsx("div", { ref: this._containerRef, className: `${styles["minimap-container"]} ${styles["minimap-hidden"]}`, style: { width: this._width, height: this._height }, onPointerDown: this._onPointerDown, onPointerMove: this._onPointerMove, onPointerUp: this._onPointerUp, children: _jsx("canvas", { ref: this._canvasRef, className: styles["minimap-canvas"], style: { width: this._width, height: this._height } }) }));
317
+ }
318
+ }
319
+ //# sourceMappingURL=graphMinimap.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"graphMinimap.js","sourceRoot":"","sources":["../../../../dev/sharedUiComponents/src/nodeGraphSystem/graphMinimap.tsx"],"names":[],"mappings":"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","sourcesContent":["import * as React from \"react\";\nimport * as styles from \"./graphMinimap.module.scss\";\nimport type { GraphCanvasComponent } from \"./graphCanvas\";\n\n/**\n * Props for the GraphMinimapComponent.\n * This component can be reused by any editor that uses GraphCanvasComponent.\n */\nexport interface IGraphMinimapComponentProps {\n /** The graph canvas to visualize */\n canvas: GraphCanvasComponent;\n /** How long (ms) the minimap stays visible after the last interaction. Default 1500. */\n hideDelayMs?: number;\n /** Width of the minimap in pixels. Default 200. */\n width?: number;\n /** Height of the minimap in pixels. Default 140. */\n height?: number;\n}\n\n/**\n * A minimap overlay that shows a scaled-down overview of the node graph.\n * It appears when the user zooms or pans and auto-hides after a delay.\n * Clicking/dragging on the minimap pans the canvas to that position.\n *\n * This is a reusable component that works with any GraphCanvasComponent-based editor\n * (Flow Graph Editor, Node Material Editor, Node Geometry Editor, etc.).\n */\nexport class GraphMinimapComponent extends React.Component<IGraphMinimapComponentProps> {\n private _canvasRef = React.createRef<HTMLCanvasElement>();\n private _containerRef = React.createRef<HTMLDivElement>();\n private _rafId = 0;\n private _hideTimer: ReturnType<typeof setTimeout> | null = null;\n private _visible = false;\n private _isDragging = false;\n /** Track last canvas state to detect changes driven by external APIs (zoomToFit, etc.) */\n private _lastX = 0;\n private _lastY = 0;\n private _lastZoom = 1;\n /** Stored transform values for minimap hit-testing */\n private _mapScale = 1;\n private _mapOffsetX = 0;\n private _mapOffsetY = 0;\n private _mapTotalMinX = 0;\n private _mapTotalMinY = 0;\n\n private get _hideDelay() {\n return this.props.hideDelayMs ?? 1500;\n }\n\n private get _width() {\n return this.props.width ?? 200;\n }\n\n private get _height() {\n return this.props.height ?? 140;\n }\n\n /** @internal */\n override componentDidMount() {\n this._rafId = requestAnimationFrame(this._tick);\n }\n\n /** @internal */\n override componentWillUnmount() {\n cancelAnimationFrame(this._rafId);\n if (this._hideTimer) {\n clearTimeout(this._hideTimer);\n }\n }\n\n /**\n * Show the minimap and schedule auto-hide.\n */\n private _show() {\n if (!this._visible) {\n this._visible = true;\n this._applyVisibility();\n }\n // Reset hide timer\n if (this._hideTimer) {\n clearTimeout(this._hideTimer);\n }\n this._hideTimer = setTimeout(() => {\n if (!this._isDragging) {\n this._visible = false;\n this._applyVisibility();\n }\n }, this._hideDelay);\n }\n\n private _applyVisibility() {\n const container = this._containerRef.current;\n if (!container) {\n return;\n }\n if (this._visible) {\n container.classList.remove(styles[\"minimap-hidden\"]);\n container.classList.add(styles[\"minimap-visible\"]);\n } else {\n container.classList.remove(styles[\"minimap-visible\"]);\n container.classList.add(styles[\"minimap-hidden\"]);\n }\n }\n\n /**\n * Main render loop — polls the canvas state every frame and redraws\n * the minimap when a change is detected.\n */\n private _tick = () => {\n const canvas = this.props.canvas;\n const { x, y, zoom } = canvas;\n\n if (x !== this._lastX || y !== this._lastY || zoom !== this._lastZoom) {\n this._lastX = x;\n this._lastY = y;\n this._lastZoom = zoom;\n this._show();\n this._draw();\n }\n\n this._rafId = requestAnimationFrame(this._tick);\n };\n\n /**\n * Compute the bounding box of all nodes and frames in graph-space.\n * @returns the min/max coordinates of the graph content\n */\n private _computeGraphBounds() {\n const canvas = this.props.canvas;\n const nodes = canvas.nodes;\n const frames = canvas.frames;\n const stickyNotes = canvas.stickyNotes;\n\n if (nodes.length === 0 && frames.length === 0 && stickyNotes.length === 0) {\n return { minX: 0, minY: 0, maxX: 1000, maxY: 700 };\n }\n\n let minX = Infinity;\n let minY = Infinity;\n let maxX = -Infinity;\n let maxY = -Infinity;\n\n for (const node of nodes) {\n const nx = node.x;\n const ny = node.y;\n const nw = node.width || 200;\n const nh = node.height || 40;\n if (nx < minX) {\n minX = nx;\n }\n if (ny < minY) {\n minY = ny;\n }\n if (nx + nw > maxX) {\n maxX = nx + nw;\n }\n if (ny + nh > maxY) {\n maxY = ny + nh;\n }\n }\n\n for (const frame of frames) {\n const fx = frame.x;\n const fy = frame.y;\n const fw = frame.width || 200;\n const fh = frame.height || 100;\n if (fx < minX) {\n minX = fx;\n }\n if (fy < minY) {\n minY = fy;\n }\n if (fx + fw > maxX) {\n maxX = fx + fw;\n }\n if (fy + fh > maxY) {\n maxY = fy + fh;\n }\n }\n\n for (const note of stickyNotes) {\n const sx = note.x;\n const sy = note.y;\n const sw = note.width || 180;\n const sh = note.height || 120;\n if (sx < minX) {\n minX = sx;\n }\n if (sy < minY) {\n minY = sy;\n }\n if (sx + sw > maxX) {\n maxX = sx + sw;\n }\n if (sy + sh > maxY) {\n maxY = sy + sh;\n }\n }\n\n // Pad the bounds a bit so nodes aren't right at the edge\n const pad = 100;\n return { minX: minX - pad, minY: minY - pad, maxX: maxX + pad, maxY: maxY + pad };\n }\n\n /**\n * Draw the minimap onto the <canvas> element.\n */\n private _draw() {\n const el = this._canvasRef.current;\n if (!el) {\n return;\n }\n\n const ctx = el.getContext(\"2d\");\n if (!ctx) {\n return;\n }\n\n const canvas = this.props.canvas;\n const dpr = window.devicePixelRatio || 1;\n const w = this._width;\n const h = this._height;\n\n // Ensure backing store matches CSS size\n if (el.width !== w * dpr || el.height !== h * dpr) {\n el.width = w * dpr;\n el.height = h * dpr;\n }\n ctx.setTransform(dpr, 0, 0, dpr, 0, 0);\n ctx.clearRect(0, 0, w, h);\n\n // Compute graph bounds\n const bounds = this._computeGraphBounds();\n\n // Also include the viewport in the bounds so scrolling far from nodes\n // still makes sense.\n const vpLeftInGraph = -canvas.x / canvas.zoom;\n const vpTopInGraph = -canvas.y / canvas.zoom;\n const hostRect = (canvas as any)._hostCanvas?.getBoundingClientRect();\n const vpW = (hostRect?.width ?? 800) / canvas.zoom;\n const vpH = (hostRect?.height ?? 600) / canvas.zoom;\n\n const totalMinX = Math.min(bounds.minX, vpLeftInGraph);\n const totalMinY = Math.min(bounds.minY, vpTopInGraph);\n const totalMaxX = Math.max(bounds.maxX, vpLeftInGraph + vpW);\n const totalMaxY = Math.max(bounds.maxY, vpTopInGraph + vpH);\n const totalW = totalMaxX - totalMinX;\n const totalH = totalMaxY - totalMinY;\n\n // Scale to fit the minimap\n const scale = Math.min(w / totalW, h / totalH);\n // Center the content\n const offsetX = (w - totalW * scale) / 2;\n const offsetY = (h - totalH * scale) / 2;\n\n const toMiniX = (gx: number) => (gx - totalMinX) * scale + offsetX;\n const toMiniY = (gy: number) => (gy - totalMinY) * scale + offsetY;\n\n // Draw frames\n for (const frame of canvas.frames) {\n const fx = toMiniX(frame.x);\n const fy = toMiniY(frame.y);\n const fw = (frame.width || 200) * scale;\n const fh = (frame.height || 100) * scale;\n ctx.fillStyle = \"rgba(100, 100, 100, 0.3)\";\n ctx.strokeStyle = \"rgba(150, 150, 150, 0.5)\";\n ctx.lineWidth = 0.5;\n ctx.fillRect(fx, fy, fw, fh);\n ctx.strokeRect(fx, fy, fw, fh);\n }\n\n // Draw nodes\n const selectedSet = new Set(canvas.selectedNodes);\n for (const node of canvas.nodes) {\n const nx = toMiniX(node.x);\n const ny = toMiniY(node.y);\n const nw = Math.max((node.width || 200) * scale, 2);\n const nh = Math.max((node.height || 40) * scale, 1.5);\n ctx.fillStyle = selectedSet.has(node) ? \"#5da0e0\" : \"#888\";\n ctx.fillRect(nx, ny, nw, nh);\n }\n\n // Draw sticky notes\n for (const note of canvas.stickyNotes) {\n const snx = toMiniX(note.x);\n const sny = toMiniY(note.y);\n const snw = (note.width || 180) * scale;\n const snh = (note.height || 120) * scale;\n ctx.fillStyle = \"rgba(255, 235, 130, 0.5)\";\n ctx.fillRect(snx, sny, snw, snh);\n }\n\n // Draw viewport rectangle\n const vx = toMiniX(vpLeftInGraph);\n const vy = toMiniY(vpTopInGraph);\n const vw = vpW * scale;\n const vh = vpH * scale;\n ctx.strokeStyle = \"rgba(255, 255, 255, 0.7)\";\n ctx.lineWidth = 1.5;\n ctx.strokeRect(vx, vy, vw, vh);\n\n // Store transform for hit-testing in pointer events\n this._mapScale = scale;\n this._mapOffsetX = offsetX;\n this._mapOffsetY = offsetY;\n this._mapTotalMinX = totalMinX;\n this._mapTotalMinY = totalMinY;\n }\n\n /**\n * Convert a pointer event on the minimap to graph-space coordinates and\n * pan the canvas to center on that point.\n * @param evt - the pointer event from the minimap\n */\n private _panToMinimapPoint(evt: React.PointerEvent) {\n const el = this._canvasRef.current;\n if (!el) {\n return;\n }\n const rect = el.getBoundingClientRect();\n const localX = evt.clientX - rect.left;\n const localY = evt.clientY - rect.top;\n\n // Convert minimap pixel to graph-space\n const graphX = (localX - this._mapOffsetX) / this._mapScale + this._mapTotalMinX;\n const graphY = (localY - this._mapOffsetY) / this._mapScale + this._mapTotalMinY;\n\n // Center the viewport on this graph point\n const canvas = this.props.canvas;\n const hostRect = (canvas as any)._hostCanvas?.getBoundingClientRect();\n const vpW = (hostRect?.width ?? 800) / canvas.zoom;\n const vpH = (hostRect?.height ?? 600) / canvas.zoom;\n\n canvas.x = -(graphX - vpW / 2) * canvas.zoom;\n canvas.y = -(graphY - vpH / 2) * canvas.zoom;\n }\n\n private _onPointerDown = (evt: React.PointerEvent) => {\n evt.preventDefault();\n evt.stopPropagation();\n this._isDragging = true;\n (evt.target as HTMLElement).setPointerCapture(evt.pointerId);\n this._panToMinimapPoint(evt);\n };\n\n private _onPointerMove = (evt: React.PointerEvent) => {\n if (!this._isDragging) {\n return;\n }\n evt.preventDefault();\n evt.stopPropagation();\n this._panToMinimapPoint(evt);\n };\n\n private _onPointerUp = (evt: React.PointerEvent) => {\n if (!this._isDragging) {\n return;\n }\n this._isDragging = false;\n (evt.target as HTMLElement).releasePointerCapture(evt.pointerId);\n // Re-arm the hide timer\n this._show();\n };\n\n /** @internal */\n override render() {\n return (\n <div\n ref={this._containerRef}\n className={`${styles[\"minimap-container\"]} ${styles[\"minimap-hidden\"]}`}\n style={{ width: this._width, height: this._height }}\n onPointerDown={this._onPointerDown}\n onPointerMove={this._onPointerMove}\n onPointerUp={this._onPointerUp}\n >\n <canvas ref={this._canvasRef} className={styles[\"minimap-canvas\"]} style={{ width: this._width, height: this._height }} />\n </div>\n );\n }\n}\n"]}
@@ -0,0 +1,30 @@
1
+ .minimap-container {
2
+ position: absolute;
3
+ bottom: 12px;
4
+ right: 12px;
5
+ width: 200px;
6
+ height: 140px;
7
+ background: rgba(30, 30, 30, 0.85);
8
+ border: 1px solid rgba(255, 255, 255, 0.15);
9
+ border-radius: 4px;
10
+ overflow: hidden;
11
+ pointer-events: auto;
12
+ z-index: 20;
13
+ transition: opacity 0.3s ease-in-out;
14
+ cursor: crosshair;
15
+ }
16
+
17
+ .minimap-hidden {
18
+ opacity: 0;
19
+ pointer-events: none;
20
+ }
21
+
22
+ .minimap-visible {
23
+ opacity: 1;
24
+ }
25
+
26
+ .minimap-canvas {
27
+ width: 100%;
28
+ height: 100%;
29
+ display: block;
30
+ }
@@ -25,6 +25,8 @@ export declare class GraphNode {
25
25
  private _comments;
26
26
  private _executionTime;
27
27
  private _selectionBorder;
28
+ private _validationBadge;
29
+ private _breakpointBadge;
28
30
  private _inputPorts;
29
31
  private _outputPorts;
30
32
  private _links;
@@ -48,6 +50,18 @@ export declare class GraphNode {
48
50
  _visualPropertiesRefresh: Array<() => void>;
49
51
  addClassToVisual(className: string): void;
50
52
  removeClassFromVisual(className: string): void;
53
+ /**
54
+ * Shows a validation badge on the node header.
55
+ * @param severity - "error" | "warning" | null. Pass null to hide the badge.
56
+ * @param tooltip - tooltip text shown on hover.
57
+ */
58
+ setValidationState(severity: "error" | "warning" | null, tooltip?: string): void;
59
+ /**
60
+ * Shows or hides a breakpoint indicator on the node.
61
+ * @param active - true to show the red breakpoint dot, false to hide.
62
+ * @param paused - true if execution is currently paused on this breakpoint.
63
+ */
64
+ setBreakpointState(active: boolean, paused?: boolean): void;
51
65
  get isCollapsed(): boolean;
52
66
  get isVisible(): boolean;
53
67
  set isVisible(value: boolean);
@@ -98,6 +112,8 @@ export declare class GraphNode {
98
112
  private _portUICount;
99
113
  private _buildInputPorts;
100
114
  private _removeInputPort;
115
+ private _buildOutputPorts;
116
+ private _removeOutputPort;
101
117
  appendVisual(root: HTMLDivElement, owner: GraphCanvasComponent): void;
102
118
  dispose(): void;
103
119
  }
@@ -14,6 +14,42 @@ export class GraphNode {
14
14
  removeClassFromVisual(className) {
15
15
  this._visual.classList.remove(className);
16
16
  }
17
+ /**
18
+ * Shows a validation badge on the node header.
19
+ * @param severity - "error" | "warning" | null. Pass null to hide the badge.
20
+ * @param tooltip - tooltip text shown on hover.
21
+ */
22
+ setValidationState(severity, tooltip) {
23
+ if (!this._validationBadge) {
24
+ return;
25
+ }
26
+ this._validationBadge.classList.remove(localStyles["validationError"], localStyles["validationWarning"]);
27
+ if (!severity) {
28
+ this._validationBadge.style.display = "none";
29
+ this._validationBadge.title = "";
30
+ return;
31
+ }
32
+ this._validationBadge.style.display = "";
33
+ this._validationBadge.classList.add(severity === "error" ? localStyles["validationError"] : localStyles["validationWarning"]);
34
+ this._validationBadge.title = tooltip ?? "";
35
+ }
36
+ /**
37
+ * Shows or hides a breakpoint indicator on the node.
38
+ * @param active - true to show the red breakpoint dot, false to hide.
39
+ * @param paused - true if execution is currently paused on this breakpoint.
40
+ */
41
+ setBreakpointState(active, paused = false) {
42
+ if (!this._breakpointBadge) {
43
+ return;
44
+ }
45
+ this._breakpointBadge.classList.remove(localStyles["breakpointActive"], localStyles["breakpointPaused"]);
46
+ if (!active) {
47
+ this._breakpointBadge.style.display = "none";
48
+ return;
49
+ }
50
+ this._breakpointBadge.style.display = "";
51
+ this._breakpointBadge.classList.add(paused ? localStyles["breakpointPaused"] : localStyles["breakpointActive"]);
52
+ }
17
53
  get isCollapsed() {
18
54
  return this._isCollapsed;
19
55
  }
@@ -199,6 +235,12 @@ export class GraphNode {
199
235
  content.onInputRemoved = (index) => {
200
236
  this._removeInputPort(index);
201
237
  };
238
+ content.onOutputCountChanged = () => {
239
+ this._buildOutputPorts(true);
240
+ };
241
+ content.onOutputRemoved = (index) => {
242
+ this._removeOutputPort(index);
243
+ };
202
244
  }
203
245
  isOverlappingFrame(frame) {
204
246
  const rect2 = this._visual.getBoundingClientRect();
@@ -267,7 +309,9 @@ export class GraphNode {
267
309
  }
268
310
  refresh() {
269
311
  if (this._displayManager) {
270
- this._header.innerHTML = this._displayManager.getHeaderText(this.content);
312
+ const headerText = this._displayManager.getHeaderText(this.content);
313
+ this._header.innerHTML = headerText;
314
+ this._header.title = headerText;
271
315
  this._displayManager.updatePreviewContent(this.content, this._content);
272
316
  const backgroundColor = this._displayManager.getBackgroundColor(this.content);
273
317
  if (backgroundColor) {
@@ -297,6 +341,7 @@ export class GraphNode {
297
341
  }
298
342
  else {
299
343
  this._header.innerHTML = this.content.name;
344
+ this._header.title = this.content.name;
300
345
  }
301
346
  for (const refresh of this._visualPropertiesRefresh) {
302
347
  refresh();
@@ -577,6 +622,22 @@ export class GraphNode {
577
622
  port.remove();
578
623
  this._inputPorts.splice(index, 1);
579
624
  }
625
+ _buildOutputPorts(addOnly = false) {
626
+ for (const output of this.content.outputs) {
627
+ if (addOnly) {
628
+ const existingPort = this._outputPorts.find((p) => p.portData === output);
629
+ if (existingPort) {
630
+ continue;
631
+ }
632
+ }
633
+ this._outputPorts.push(NodePort.CreatePortElement(output, this, this._outputsContainer, this._displayManager, this._stateManager));
634
+ }
635
+ }
636
+ _removeOutputPort(index) {
637
+ const port = this._outputPorts[index];
638
+ port.remove();
639
+ this._outputPorts.splice(index, 1);
640
+ }
580
641
  appendVisual(root, owner) {
581
642
  this._ownerCanvas = owner;
582
643
  // Display manager
@@ -650,6 +711,20 @@ export class GraphNode {
650
711
  this._executionTime = root.ownerDocument.createElement("div");
651
712
  this._executionTime.classList.add(localStyles.executionTime);
652
713
  this._visual.appendChild(this._executionTime);
714
+ // Validation badge (opt-in)
715
+ if (this._stateManager.enableNodeBadges) {
716
+ this._validationBadge = root.ownerDocument.createElement("div");
717
+ this._validationBadge.classList.add(localStyles.validationBadge);
718
+ this._validationBadge.style.display = "none";
719
+ this._headerContainer.appendChild(this._validationBadge);
720
+ }
721
+ // Breakpoint badge (opt-in)
722
+ if (this._stateManager.enableNodeBadges) {
723
+ this._breakpointBadge = root.ownerDocument.createElement("div");
724
+ this._breakpointBadge.classList.add(localStyles.breakpointBadge);
725
+ this._breakpointBadge.style.display = "none";
726
+ this._headerContainer.appendChild(this._breakpointBadge);
727
+ }
653
728
  // Options
654
729
  const propStore = this.content.data._propStore;
655
730
  if (propStore) {