@bian-womp/spark-workbench 0.1.30 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/cjs/index.cjs +484 -124
- package/lib/cjs/index.cjs.map +1 -1
- package/lib/cjs/src/core/InMemoryWorkbench.d.ts +0 -2
- package/lib/cjs/src/core/InMemoryWorkbench.d.ts.map +1 -1
- package/lib/cjs/src/core/contracts.d.ts +2 -0
- package/lib/cjs/src/core/contracts.d.ts.map +1 -1
- package/lib/cjs/src/index.d.ts +1 -0
- package/lib/cjs/src/index.d.ts.map +1 -1
- package/lib/cjs/src/misc/DefaultNode.d.ts +3 -2
- package/lib/cjs/src/misc/DefaultNode.d.ts.map +1 -1
- package/lib/cjs/src/misc/Inspector.d.ts.map +1 -1
- package/lib/cjs/src/misc/NodeHandles.d.ts +20 -0
- package/lib/cjs/src/misc/NodeHandles.d.ts.map +1 -0
- package/lib/cjs/src/misc/WorkbenchCanvas.d.ts.map +1 -1
- package/lib/cjs/src/misc/WorkbenchStudio.d.ts +3 -4
- package/lib/cjs/src/misc/WorkbenchStudio.d.ts.map +1 -1
- package/lib/cjs/src/misc/constants.d.ts +3 -0
- package/lib/cjs/src/misc/constants.d.ts.map +1 -0
- package/lib/cjs/src/misc/hooks.d.ts +2 -2
- package/lib/cjs/src/misc/hooks.d.ts.map +1 -1
- package/lib/cjs/src/misc/mapping.d.ts +23 -1
- package/lib/cjs/src/misc/mapping.d.ts.map +1 -1
- package/lib/esm/index.js +481 -123
- package/lib/esm/index.js.map +1 -1
- package/lib/esm/src/core/InMemoryWorkbench.d.ts +0 -2
- package/lib/esm/src/core/InMemoryWorkbench.d.ts.map +1 -1
- package/lib/esm/src/core/contracts.d.ts +2 -0
- package/lib/esm/src/core/contracts.d.ts.map +1 -1
- package/lib/esm/src/index.d.ts +1 -0
- package/lib/esm/src/index.d.ts.map +1 -1
- package/lib/esm/src/misc/DefaultNode.d.ts +3 -2
- package/lib/esm/src/misc/DefaultNode.d.ts.map +1 -1
- package/lib/esm/src/misc/Inspector.d.ts.map +1 -1
- package/lib/esm/src/misc/NodeHandles.d.ts +20 -0
- package/lib/esm/src/misc/NodeHandles.d.ts.map +1 -0
- package/lib/esm/src/misc/WorkbenchCanvas.d.ts.map +1 -1
- package/lib/esm/src/misc/WorkbenchStudio.d.ts +3 -4
- package/lib/esm/src/misc/WorkbenchStudio.d.ts.map +1 -1
- package/lib/esm/src/misc/constants.d.ts +3 -0
- package/lib/esm/src/misc/constants.d.ts.map +1 -0
- package/lib/esm/src/misc/hooks.d.ts +2 -2
- package/lib/esm/src/misc/hooks.d.ts.map +1 -1
- package/lib/esm/src/misc/mapping.d.ts +23 -1
- package/lib/esm/src/misc/mapping.d.ts.map +1 -1
- package/package.json +10 -8
package/lib/cjs/index.cjs
CHANGED
|
@@ -3,9 +3,10 @@
|
|
|
3
3
|
var sparkGraph = require('@bian-womp/spark-graph');
|
|
4
4
|
var sparkRemote = require('@bian-womp/spark-remote');
|
|
5
5
|
var React = require('react');
|
|
6
|
+
var react = require('@xyflow/react');
|
|
6
7
|
var jsxRuntime = require('react/jsx-runtime');
|
|
7
|
-
var react = require('@phosphor-icons/react');
|
|
8
|
-
var
|
|
8
|
+
var react$1 = require('@phosphor-icons/react');
|
|
9
|
+
var isEqual = require('lodash/isEqual');
|
|
9
10
|
var cx = require('classnames');
|
|
10
11
|
|
|
11
12
|
class DefaultUIExtensionRegistry {
|
|
@@ -232,6 +233,10 @@ class InMemoryWorkbench extends AbstractWorkbench {
|
|
|
232
233
|
setSelection(sel) {
|
|
233
234
|
this.selection = { nodes: [...sel.nodes], edges: [...sel.edges] };
|
|
234
235
|
this.emit("selectionChanged", this.selection);
|
|
236
|
+
this.emit("graphUiChanged", {
|
|
237
|
+
def: this.def,
|
|
238
|
+
change: { type: "selection" },
|
|
239
|
+
});
|
|
235
240
|
}
|
|
236
241
|
getSelection() {
|
|
237
242
|
return {
|
|
@@ -239,18 +244,6 @@ class InMemoryWorkbench extends AbstractWorkbench {
|
|
|
239
244
|
edges: [...this.selection.edges],
|
|
240
245
|
};
|
|
241
246
|
}
|
|
242
|
-
toggleNodeSelection(nodeId) {
|
|
243
|
-
this.selection.nodes = this.selection.nodes.includes(nodeId)
|
|
244
|
-
? this.selection.nodes.filter((id) => id !== nodeId)
|
|
245
|
-
: [...this.selection.nodes, nodeId];
|
|
246
|
-
this.emit("selectionChanged", this.selection);
|
|
247
|
-
}
|
|
248
|
-
toggleEdgeSelection(edgeId) {
|
|
249
|
-
this.selection.edges = this.selection.edges.includes(edgeId)
|
|
250
|
-
? this.selection.edges.filter((id) => id !== edgeId)
|
|
251
|
-
: [...this.selection.edges, edgeId];
|
|
252
|
-
this.emit("selectionChanged", this.selection);
|
|
253
|
-
}
|
|
254
247
|
on(event, handler) {
|
|
255
248
|
if (!this.listeners.has(event))
|
|
256
249
|
this.listeners.set(event, new Set());
|
|
@@ -760,49 +753,119 @@ function useWorkbenchBridge(wb) {
|
|
|
760
753
|
});
|
|
761
754
|
}, [wb]);
|
|
762
755
|
const onNodesChange = React.useCallback((changes) => {
|
|
756
|
+
// Apply position updates
|
|
763
757
|
changes.forEach((c) => {
|
|
764
|
-
if (c.type === "position" && c.position)
|
|
758
|
+
if (c.type === "position" && c.position) {
|
|
765
759
|
wb.setPosition(c.id, c.position);
|
|
766
|
-
|
|
767
|
-
wb.removeNode(c.id);
|
|
768
|
-
if (c.type === "select")
|
|
769
|
-
wb.toggleNodeSelection(c.id);
|
|
760
|
+
}
|
|
770
761
|
});
|
|
762
|
+
// Derive next node selection from change set
|
|
763
|
+
const current = wb.getSelection();
|
|
764
|
+
const nextNodeIds = new Set(current.nodes);
|
|
765
|
+
let selectionChanged = false;
|
|
766
|
+
for (const change of changes) {
|
|
767
|
+
const type = change?.type;
|
|
768
|
+
if (type === "select") {
|
|
769
|
+
const id = change.id;
|
|
770
|
+
const selected = change.selected;
|
|
771
|
+
if (typeof selected === "boolean") {
|
|
772
|
+
if (selected) {
|
|
773
|
+
if (!nextNodeIds.has(id)) {
|
|
774
|
+
nextNodeIds.add(id);
|
|
775
|
+
selectionChanged = true;
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
else if (nextNodeIds.delete(id)) {
|
|
779
|
+
selectionChanged = true;
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
else if (type === "selectNodes") {
|
|
784
|
+
const ids = change.ids;
|
|
785
|
+
const selected = change.selected;
|
|
786
|
+
if (Array.isArray(ids) && typeof selected === "boolean") {
|
|
787
|
+
for (const id of ids) {
|
|
788
|
+
if (selected) {
|
|
789
|
+
if (!nextNodeIds.has(id)) {
|
|
790
|
+
nextNodeIds.add(id);
|
|
791
|
+
selectionChanged = true;
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
else if (nextNodeIds.delete(id)) {
|
|
795
|
+
selectionChanged = true;
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
else if (type === "remove") {
|
|
801
|
+
const id = change.id;
|
|
802
|
+
if (nextNodeIds.delete(id))
|
|
803
|
+
selectionChanged = true;
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
if (selectionChanged) {
|
|
807
|
+
wb.setSelection({ nodes: Array.from(nextNodeIds), edges: current.edges });
|
|
808
|
+
}
|
|
771
809
|
}, [wb]);
|
|
772
810
|
const onEdgesDelete = React.useCallback((edges) => edges.forEach((e) => wb.disconnect(e.id)), [wb]);
|
|
773
811
|
const onEdgesChange = React.useCallback((changes) => {
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
812
|
+
const current = wb.getSelection();
|
|
813
|
+
const nextEdgeIds = new Set(current.edges);
|
|
814
|
+
let selectionChanged = false;
|
|
815
|
+
for (const change of changes) {
|
|
816
|
+
const type = change?.type;
|
|
817
|
+
if (type === "select") {
|
|
818
|
+
const id = change.id;
|
|
819
|
+
const selected = change.selected;
|
|
820
|
+
if (typeof selected === "boolean") {
|
|
821
|
+
if (selected) {
|
|
822
|
+
if (!nextEdgeIds.has(id)) {
|
|
823
|
+
nextEdgeIds.add(id);
|
|
824
|
+
selectionChanged = true;
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
else if (nextEdgeIds.delete(id)) {
|
|
828
|
+
selectionChanged = true;
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
else if (type === "selectEdges") {
|
|
833
|
+
const ids = change.ids;
|
|
834
|
+
const selected = change.selected;
|
|
835
|
+
if (Array.isArray(ids) && typeof selected === "boolean") {
|
|
836
|
+
for (const id of ids) {
|
|
837
|
+
if (selected) {
|
|
838
|
+
if (!nextEdgeIds.has(id)) {
|
|
839
|
+
nextEdgeIds.add(id);
|
|
840
|
+
selectionChanged = true;
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
else if (nextEdgeIds.delete(id)) {
|
|
844
|
+
selectionChanged = true;
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
else if (type === "remove") {
|
|
850
|
+
const id = change.id;
|
|
851
|
+
if (nextEdgeIds.delete(id))
|
|
852
|
+
selectionChanged = true;
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
if (selectionChanged) {
|
|
856
|
+
wb.setSelection({ nodes: current.nodes, edges: Array.from(nextEdgeIds) });
|
|
857
|
+
}
|
|
780
858
|
}, [wb]);
|
|
781
859
|
const onNodesDelete = React.useCallback((nodes) => {
|
|
782
860
|
for (const n of nodes)
|
|
783
861
|
wb.removeNode(n.id);
|
|
784
862
|
}, [wb]);
|
|
785
|
-
const onSelectionChange = React.useCallback((sel) => {
|
|
786
|
-
const next = {
|
|
787
|
-
nodes: sel.nodes.map((n) => n.id),
|
|
788
|
-
edges: sel.edges.map((e) => e.id),
|
|
789
|
-
};
|
|
790
|
-
const cur = wb.getSelection();
|
|
791
|
-
const sameLen = cur.nodes.length === next.nodes.length &&
|
|
792
|
-
cur.edges.length === next.edges.length;
|
|
793
|
-
const same = sameLen &&
|
|
794
|
-
cur.nodes.every((id, i) => id === next.nodes[i]) &&
|
|
795
|
-
cur.edges.every((id, i) => id === next.edges[i]);
|
|
796
|
-
if (!same)
|
|
797
|
-
wb.setSelection(next);
|
|
798
|
-
}, [wb]);
|
|
799
863
|
return {
|
|
800
864
|
onConnect,
|
|
801
865
|
onNodesChange,
|
|
802
866
|
onEdgesChange,
|
|
803
867
|
onEdgesDelete,
|
|
804
868
|
onNodesDelete,
|
|
805
|
-
onSelectionChange,
|
|
806
869
|
};
|
|
807
870
|
}
|
|
808
871
|
function useWorkbenchGraphTick(wb) {
|
|
@@ -842,6 +905,39 @@ function useWorkbenchVersionTick(runner) {
|
|
|
842
905
|
}, [runner]);
|
|
843
906
|
return version;
|
|
844
907
|
}
|
|
908
|
+
function useThrottledValue(value, intervalMs) {
|
|
909
|
+
const [throttled, setThrottled] = React.useState(value);
|
|
910
|
+
const lastSetAtRef = React.useRef(0);
|
|
911
|
+
const timeoutRef = React.useRef(null);
|
|
912
|
+
React.useEffect(() => {
|
|
913
|
+
const now = (typeof performance !== "undefined" && performance.now) ? performance.now() : Date.now();
|
|
914
|
+
const elapsed = now - lastSetAtRef.current;
|
|
915
|
+
if (elapsed >= intervalMs) {
|
|
916
|
+
lastSetAtRef.current = now;
|
|
917
|
+
setThrottled(value);
|
|
918
|
+
}
|
|
919
|
+
else {
|
|
920
|
+
if (timeoutRef.current !== null) {
|
|
921
|
+
window.clearTimeout(timeoutRef.current);
|
|
922
|
+
}
|
|
923
|
+
timeoutRef.current = window.setTimeout(() => {
|
|
924
|
+
lastSetAtRef.current = (typeof performance !== "undefined" && performance.now) ? performance.now() : Date.now();
|
|
925
|
+
setThrottled(value);
|
|
926
|
+
timeoutRef.current = null;
|
|
927
|
+
}, Math.max(0, intervalMs - elapsed));
|
|
928
|
+
}
|
|
929
|
+
return () => { };
|
|
930
|
+
}, [value, intervalMs]);
|
|
931
|
+
React.useEffect(() => {
|
|
932
|
+
return () => {
|
|
933
|
+
if (timeoutRef.current !== null) {
|
|
934
|
+
window.clearTimeout(timeoutRef.current);
|
|
935
|
+
timeoutRef.current = null;
|
|
936
|
+
}
|
|
937
|
+
};
|
|
938
|
+
}, []);
|
|
939
|
+
return throttled;
|
|
940
|
+
}
|
|
845
941
|
// Query param helpers
|
|
846
942
|
function setSearchParam(key, val) {
|
|
847
943
|
if (typeof window === "undefined")
|
|
@@ -1002,7 +1098,13 @@ function summarizeDeep(value) {
|
|
|
1002
1098
|
return value;
|
|
1003
1099
|
}
|
|
1004
1100
|
|
|
1101
|
+
// Shared UI constants for node layout to keep mapping and rendering in sync
|
|
1102
|
+
const NODE_HEADER_HEIGHT_PX = 24;
|
|
1103
|
+
const NODE_ROW_HEIGHT_PX = 22;
|
|
1104
|
+
|
|
1005
1105
|
function toReactFlow(def, positions, registry, opts) {
|
|
1106
|
+
const EDGE_STYLE_ERROR = { stroke: "#ef4444", strokeWidth: 2 };
|
|
1107
|
+
const EDGE_STYLE_RUNNING = { stroke: "#3b82f6" };
|
|
1006
1108
|
const nodeHandleMap = {};
|
|
1007
1109
|
const nodes = def.nodes.map((n) => {
|
|
1008
1110
|
const desc = registry.nodes.get(n.typeId);
|
|
@@ -1014,6 +1116,35 @@ function toReactFlow(def, positions, registry, opts) {
|
|
|
1014
1116
|
inputs: new Set(inputHandles.map((h) => h.id)),
|
|
1015
1117
|
outputs: new Set(outputHandles.map((h) => h.id)),
|
|
1016
1118
|
};
|
|
1119
|
+
// Match DefaultNode sizing heuristics to avoid hidden nodes during re-measure
|
|
1120
|
+
const HEADER_SIZE = NODE_HEADER_HEIGHT_PX;
|
|
1121
|
+
const ROW_SIZE = NODE_ROW_HEIGHT_PX;
|
|
1122
|
+
const maxRows = Math.max(inputHandles.length, outputHandles.length);
|
|
1123
|
+
const initialWidth = opts.showValues ? 320 : 240;
|
|
1124
|
+
const initialHeight = HEADER_SIZE + maxRows * ROW_SIZE;
|
|
1125
|
+
// Precompute handle bounds so edges can render immediately without waiting for measurement
|
|
1126
|
+
const handles = [
|
|
1127
|
+
// Inputs on the left as targets
|
|
1128
|
+
...inputHandles.map((h, i) => ({
|
|
1129
|
+
id: h.id,
|
|
1130
|
+
type: "target",
|
|
1131
|
+
position: react.Position.Left,
|
|
1132
|
+
x: 0,
|
|
1133
|
+
y: HEADER_SIZE + i * ROW_SIZE,
|
|
1134
|
+
width: 1,
|
|
1135
|
+
height: ROW_SIZE + 2,
|
|
1136
|
+
})),
|
|
1137
|
+
// Outputs on the right as sources
|
|
1138
|
+
...outputHandles.map((h, i) => ({
|
|
1139
|
+
id: h.id,
|
|
1140
|
+
type: "source",
|
|
1141
|
+
position: react.Position.Right,
|
|
1142
|
+
x: initialWidth - 1,
|
|
1143
|
+
y: HEADER_SIZE + i * ROW_SIZE,
|
|
1144
|
+
width: 1,
|
|
1145
|
+
height: ROW_SIZE + 2,
|
|
1146
|
+
})),
|
|
1147
|
+
];
|
|
1017
1148
|
return {
|
|
1018
1149
|
id: n.nodeId,
|
|
1019
1150
|
data: {
|
|
@@ -1021,7 +1152,23 @@ function toReactFlow(def, positions, registry, opts) {
|
|
|
1021
1152
|
params: n.params,
|
|
1022
1153
|
inputHandles,
|
|
1023
1154
|
outputHandles,
|
|
1155
|
+
handleLayout: [
|
|
1156
|
+
...inputHandles.map((h, i) => ({
|
|
1157
|
+
id: h.id,
|
|
1158
|
+
type: "target",
|
|
1159
|
+
position: react.Position.Left,
|
|
1160
|
+
y: HEADER_SIZE + i * ROW_SIZE + ROW_SIZE / 2,
|
|
1161
|
+
})),
|
|
1162
|
+
...outputHandles.map((h, i) => ({
|
|
1163
|
+
id: h.id,
|
|
1164
|
+
type: "source",
|
|
1165
|
+
position: react.Position.Right,
|
|
1166
|
+
y: HEADER_SIZE + i * ROW_SIZE + ROW_SIZE / 2,
|
|
1167
|
+
})),
|
|
1168
|
+
],
|
|
1024
1169
|
showValues: opts.showValues,
|
|
1170
|
+
renderWidth: initialWidth,
|
|
1171
|
+
renderHeight: initialHeight,
|
|
1025
1172
|
inputValues: opts.inputs?.[n.nodeId],
|
|
1026
1173
|
outputValues: opts.outputs?.[n.nodeId],
|
|
1027
1174
|
status: opts.nodeStatus?.[n.nodeId],
|
|
@@ -1038,6 +1185,11 @@ function toReactFlow(def, positions, registry, opts) {
|
|
|
1038
1185
|
selected: opts.selectedNodeIds
|
|
1039
1186
|
? opts.selectedNodeIds.has(n.nodeId)
|
|
1040
1187
|
: undefined,
|
|
1188
|
+
initialWidth,
|
|
1189
|
+
initialHeight,
|
|
1190
|
+
handles,
|
|
1191
|
+
width: initialWidth,
|
|
1192
|
+
height: initialHeight,
|
|
1041
1193
|
};
|
|
1042
1194
|
});
|
|
1043
1195
|
const edges = def.edges
|
|
@@ -1054,9 +1206,9 @@ function toReactFlow(def, positions, registry, opts) {
|
|
|
1054
1206
|
const hasError = !!st?.lastError;
|
|
1055
1207
|
const isInvalidEdge = !!opts.edgeValidation?.[e.id];
|
|
1056
1208
|
const style = hasError || isInvalidEdge
|
|
1057
|
-
?
|
|
1209
|
+
? EDGE_STYLE_ERROR
|
|
1058
1210
|
: isRunning
|
|
1059
|
-
?
|
|
1211
|
+
? EDGE_STYLE_RUNNING
|
|
1060
1212
|
: undefined;
|
|
1061
1213
|
return {
|
|
1062
1214
|
id: e.id,
|
|
@@ -1084,17 +1236,35 @@ function getNodeBorderClassNames(args) {
|
|
|
1084
1236
|
const hasValidationWarning = !hasValidationError && issues.length > 0;
|
|
1085
1237
|
const isRunning = !!status.activeRuns;
|
|
1086
1238
|
const isInvalid = !!status.invalidated && !isRunning && !hasError;
|
|
1087
|
-
|
|
1239
|
+
// Keep border width constant to avoid layout reflow on selection toggles
|
|
1240
|
+
const borderWidth = "border";
|
|
1088
1241
|
const borderStyle = isInvalid ? "border-dashed" : "border-solid";
|
|
1089
|
-
const
|
|
1090
|
-
? "
|
|
1242
|
+
const severity = hasError || hasValidationError
|
|
1243
|
+
? "red"
|
|
1091
1244
|
: hasValidationWarning
|
|
1092
|
-
? "
|
|
1245
|
+
? "amber"
|
|
1093
1246
|
: isRunning
|
|
1094
|
-
? "
|
|
1095
|
-
: "
|
|
1096
|
-
const
|
|
1097
|
-
|
|
1247
|
+
? "blue"
|
|
1248
|
+
: "gray";
|
|
1249
|
+
const borderBySeverity = {
|
|
1250
|
+
red: "border-red-500",
|
|
1251
|
+
amber: "border-amber-500",
|
|
1252
|
+
blue: "border-blue-500",
|
|
1253
|
+
gray: "border-gray-500 dark:border-gray-400",
|
|
1254
|
+
};
|
|
1255
|
+
const ringBySeverity = {
|
|
1256
|
+
red: "ring-2 ring-red-300 dark:ring-red-900",
|
|
1257
|
+
amber: "ring-2 ring-amber-300 dark:ring-amber-900",
|
|
1258
|
+
blue: "ring-2 ring-blue-200 dark:ring-blue-900",
|
|
1259
|
+
gray: "ring-2 ring-gray-300 dark:ring-gray-500",
|
|
1260
|
+
};
|
|
1261
|
+
const borderColor = borderBySeverity[severity];
|
|
1262
|
+
const ring = isRunning
|
|
1263
|
+
? ringBySeverity.blue
|
|
1264
|
+
: selected
|
|
1265
|
+
? ringBySeverity[severity === "blue" ? "gray" : severity]
|
|
1266
|
+
: "";
|
|
1267
|
+
return [borderWidth, borderStyle, borderColor, ring].join(" ").trim();
|
|
1098
1268
|
}
|
|
1099
1269
|
|
|
1100
1270
|
const WorkbenchContext = React.createContext(null);
|
|
@@ -1588,7 +1758,7 @@ function WorkbenchProvider({ wb, runner, registry, setRegistry, children, }) {
|
|
|
1588
1758
|
|
|
1589
1759
|
function IssueBadge({ level, title, size = 12, className, }) {
|
|
1590
1760
|
const colorClass = level === "error" ? "text-red-600" : "text-amber-600";
|
|
1591
|
-
return (jsxRuntime.jsx("button", { type: "button", className: `inline-flex items-center justify-center shrink-0 ${colorClass} ${className ?? ""}`, title: title, style: { width: size, height: size }, children: level === "error" ? (jsxRuntime.jsx(react.XCircleIcon, { size: size, weight: "fill" })) : (jsxRuntime.jsx(react.WarningCircleIcon, { size: size, weight: "fill" })) }));
|
|
1761
|
+
return (jsxRuntime.jsx("button", { type: "button", className: `inline-flex items-center justify-center shrink-0 ${colorClass} ${className ?? ""}`, title: title, style: { width: size, height: size }, children: level === "error" ? (jsxRuntime.jsx(react$1.XCircleIcon, { size: size, weight: "fill" })) : (jsxRuntime.jsx(react$1.WarningCircleIcon, { size: size, weight: "fill" })) }));
|
|
1592
1762
|
}
|
|
1593
1763
|
|
|
1594
1764
|
function DebugEvents({ autoScroll, onAutoScrollChange, hideWorkbench, onHideWorkbenchChange, }) {
|
|
@@ -1709,7 +1879,7 @@ function Inspector({ debug, autoScroll, hideWorkbench, onAutoScrollChange, onHid
|
|
|
1709
1879
|
setOriginals(nextOriginals);
|
|
1710
1880
|
}, [selectedNodeId, selectedDesc, valuesTick]);
|
|
1711
1881
|
const widthClass = debug ? "w-[480px]" : "w-[320px]";
|
|
1712
|
-
return (jsxRuntime.jsxs("div", { className: `${widthClass} border-l border-gray-300 p-3 flex flex-col h-full min-h-0 overflow-hidden`, children: [contextPanel && (jsxRuntime.jsx("div", { className: "mb-2", children: contextPanel })), jsxRuntime.jsx("div", { className: "font-semibold mb-2", children: "Inspector" }), jsxRuntime.jsx("div", { className: "flex-1 overflow-auto", children: !selectedNode && !selectedEdge ? (jsxRuntime.jsxs("div", { children: [jsxRuntime.jsx("div", { className: "text-gray-500", children: "Select a node or edge." }), globalValidationIssues && globalValidationIssues.length > 0 && (jsxRuntime.jsxs("div", { className: "mt-2 text-xs bg-red-50 border border-red-200 rounded px-2 py-1", children: [jsxRuntime.jsx("div", { className: "font-semibold mb-1", children: "Validation" }), jsxRuntime.jsx("ul", { className: "list-disc ml-4", children: globalValidationIssues.map((m, i) => (jsxRuntime.jsxs("li", { className: "flex items-center gap-1", children: [jsxRuntime.jsx(IssueBadge, { level: m.level, size: 24, className: "w-6 h-6" }), jsxRuntime.jsx("span", { children: `${m.code}: ${m.message}` })] }, i))) })] }))] })) : selectedEdge ? (jsxRuntime.jsxs("div", { children: [jsxRuntime.jsxs("div", { className: "mb-2", children: [jsxRuntime.jsxs("div", { children: ["Edge: ", selectedEdge.id] }), jsxRuntime.jsxs("div", { children: [selectedEdge.source.nodeId, ".", selectedEdge.source.handle, " \u2192", " ", selectedEdge.target.nodeId, ".", selectedEdge.target.handle] }), jsxRuntime.jsxs("div", { children: ["Type: ", selectedEdge.typeId] })] }), selectedEdgeValidation.length > 0 && (jsxRuntime.jsxs("div", { className: "mt-2 text-xs bg-red-50 border border-red-200 rounded px-2 py-1", children: [jsxRuntime.jsx("div", { className: "font-semibold mb-1", children: "Validation" }), jsxRuntime.jsx("ul", { className: "list-disc ml-4", children: selectedEdgeValidation.map((m, i) => (jsxRuntime.jsxs("li", { className: "flex items-center gap-1", children: [jsxRuntime.jsx(IssueBadge, { level: m.level, size: 24, className: "w-6 h-6" }), jsxRuntime.jsx("span", { children: `${m.code}: ${m.message}` })] }, i))) })] }))] })) : (jsxRuntime.jsxs("div", { children: [selectedNode && (jsxRuntime.jsxs("div", { className: "mb-2", children: [jsxRuntime.jsxs("div", { children: ["Node: ", selectedNode.nodeId] }), jsxRuntime.jsxs("div", { children: ["Type: ", selectedNode.typeId] }), !!selectedNodeStatus?.lastError && (jsxRuntime.jsx("div", { className: "mt-2 text-sm text-red-700 bg-red-50 border border-red-200 rounded px-2 py-1 break-words", children: String(selectedNodeStatus.lastError?.message ??
|
|
1882
|
+
return (jsxRuntime.jsxs("div", { className: `${widthClass} border-l border-gray-300 p-3 flex flex-col h-full min-h-0 overflow-hidden`, children: [contextPanel && (jsxRuntime.jsx("div", { className: "mb-2", children: contextPanel })), jsxRuntime.jsx("div", { className: "font-semibold mb-2", children: "Inspector" }), jsxRuntime.jsxs("div", { className: "text-xs text-gray-500 mb-2", children: ["valuesTick: ", valuesTick] }), jsxRuntime.jsx("div", { className: "flex-1 overflow-auto", children: !selectedNode && !selectedEdge ? (jsxRuntime.jsxs("div", { children: [jsxRuntime.jsx("div", { className: "text-gray-500", children: "Select a node or edge." }), globalValidationIssues && globalValidationIssues.length > 0 && (jsxRuntime.jsxs("div", { className: "mt-2 text-xs bg-red-50 border border-red-200 rounded px-2 py-1", children: [jsxRuntime.jsx("div", { className: "font-semibold mb-1", children: "Validation" }), jsxRuntime.jsx("ul", { className: "list-disc ml-4", children: globalValidationIssues.map((m, i) => (jsxRuntime.jsxs("li", { className: "flex items-center gap-1", children: [jsxRuntime.jsx(IssueBadge, { level: m.level, size: 24, className: "w-6 h-6" }), jsxRuntime.jsx("span", { children: `${m.code}: ${m.message}` })] }, i))) })] }))] })) : selectedEdge ? (jsxRuntime.jsxs("div", { children: [jsxRuntime.jsxs("div", { className: "mb-2", children: [jsxRuntime.jsxs("div", { children: ["Edge: ", selectedEdge.id] }), jsxRuntime.jsxs("div", { children: [selectedEdge.source.nodeId, ".", selectedEdge.source.handle, " \u2192", " ", selectedEdge.target.nodeId, ".", selectedEdge.target.handle] }), jsxRuntime.jsxs("div", { children: ["Type: ", selectedEdge.typeId] })] }), selectedEdgeValidation.length > 0 && (jsxRuntime.jsxs("div", { className: "mt-2 text-xs bg-red-50 border border-red-200 rounded px-2 py-1", children: [jsxRuntime.jsx("div", { className: "font-semibold mb-1", children: "Validation" }), jsxRuntime.jsx("ul", { className: "list-disc ml-4", children: selectedEdgeValidation.map((m, i) => (jsxRuntime.jsxs("li", { className: "flex items-center gap-1", children: [jsxRuntime.jsx(IssueBadge, { level: m.level, size: 24, className: "w-6 h-6" }), jsxRuntime.jsx("span", { children: `${m.code}: ${m.message}` })] }, i))) })] }))] })) : (jsxRuntime.jsxs("div", { children: [selectedNode && (jsxRuntime.jsxs("div", { className: "mb-2", children: [jsxRuntime.jsxs("div", { children: ["Node: ", selectedNode.nodeId] }), jsxRuntime.jsxs("div", { children: ["Type: ", selectedNode.typeId] }), !!selectedNodeStatus?.lastError && (jsxRuntime.jsx("div", { className: "mt-2 text-sm text-red-700 bg-red-50 border border-red-200 rounded px-2 py-1 break-words", children: String(selectedNodeStatus.lastError?.message ??
|
|
1713
1883
|
selectedNodeStatus.lastError) }))] })), jsxRuntime.jsxs("div", { className: "mb-2", children: [jsxRuntime.jsx("div", { className: "font-semibold mb-1", children: "Inputs" }), inputHandles.length === 0 ? (jsxRuntime.jsx("div", { className: "text-gray-500", children: "No inputs" })) : (inputHandles.map((h) => {
|
|
1714
1884
|
const typeId = sparkGraph.getInputTypeId(selectedDesc?.inputs, h);
|
|
1715
1885
|
const isLinked = def.edges.some((e) => e.target.nodeId === selectedNodeId &&
|
|
@@ -1770,83 +1940,120 @@ function Inspector({ debug, autoScroll, hideWorkbench, onAutoScrollChange, onHid
|
|
|
1770
1940
|
})()] }, h))))] }), selectedNodeValidation.length > 0 && (jsxRuntime.jsxs("div", { className: "mt-2 text-xs bg-red-50 border border-red-200 rounded px-2 py-1", children: [jsxRuntime.jsx("div", { className: "font-semibold mb-1", children: "Validation" }), jsxRuntime.jsx("ul", { className: "list-disc ml-4", children: selectedNodeValidation.map((m, i) => (jsxRuntime.jsxs("li", { className: "flex items-center gap-1", children: [jsxRuntime.jsx(IssueBadge, { level: m.level, size: 24, className: "w-6 h-6" }), jsxRuntime.jsx("span", { children: `${m.code}: ${m.message}` })] }, i))) })] }))] })) }), debug && (jsxRuntime.jsx("div", { className: "mt-3 flex-none min-h-0 h-[50%]", children: jsxRuntime.jsx(DebugEvents, { autoScroll: !!autoScroll, hideWorkbench: !!hideWorkbench, onAutoScrollChange: onAutoScrollChange, onHideWorkbenchChange: onHideWorkbenchChange }) }))] }));
|
|
1771
1941
|
}
|
|
1772
1942
|
|
|
1943
|
+
function NodeHandles({ data, isConnectable, inputClassName = "!w-2 !h-2 !bg-gray-600", outputClassName = "!w-2 !h-2 !bg-gray-600", getClassName, renderLabel, labelClassName = "absolute text-[11px] text-gray-700 dark:text-gray-300 pointer-events-none", }) {
|
|
1944
|
+
const layout = data.handleLayout ?? [];
|
|
1945
|
+
const byId = React.useMemo(() => {
|
|
1946
|
+
const m = new Map();
|
|
1947
|
+
for (const h of layout) {
|
|
1948
|
+
m.set(h.id, { position: h.position, y: h.y, type: h.type });
|
|
1949
|
+
}
|
|
1950
|
+
return m;
|
|
1951
|
+
}, [layout]);
|
|
1952
|
+
return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [(data.inputHandles ?? []).map((h) => {
|
|
1953
|
+
const placed = byId.get(h.id);
|
|
1954
|
+
const position = placed?.position ?? react.Position.Left;
|
|
1955
|
+
const y = placed?.y;
|
|
1956
|
+
const cls = getClassName?.({ kind: "input", id: h.id, type: "target" }) ??
|
|
1957
|
+
inputClassName;
|
|
1958
|
+
return (jsxRuntime.jsxs(React.Fragment, { children: [jsxRuntime.jsx(react.Handle, { id: h.id, type: "target", position: position, isConnectable: isConnectable, className: cls, style: y !== undefined ? { top: y } : undefined }), renderLabel && (jsxRuntime.jsx("div", { className: labelClassName + " left-2", style: {
|
|
1959
|
+
top: (y ?? 0) - 8,
|
|
1960
|
+
right: "50%",
|
|
1961
|
+
whiteSpace: "nowrap",
|
|
1962
|
+
overflow: "hidden",
|
|
1963
|
+
textOverflow: "ellipsis",
|
|
1964
|
+
}, children: renderLabel({ kind: "input", id: h.id }) }))] }, h.id));
|
|
1965
|
+
}), (data.outputHandles ?? []).map((h) => {
|
|
1966
|
+
const placed = byId.get(h.id);
|
|
1967
|
+
const position = placed?.position ?? react.Position.Right;
|
|
1968
|
+
const y = placed?.y;
|
|
1969
|
+
const cls = getClassName?.({ kind: "output", id: h.id, type: "source" }) ??
|
|
1970
|
+
outputClassName;
|
|
1971
|
+
return (jsxRuntime.jsxs(React.Fragment, { children: [jsxRuntime.jsx(react.Handle, { id: h.id, type: "source", position: position, isConnectable: isConnectable, className: cls, style: y !== undefined ? { top: y } : undefined }), renderLabel && (jsxRuntime.jsx("div", { className: labelClassName + " right-2", style: {
|
|
1972
|
+
top: (y ?? 0) - 8,
|
|
1973
|
+
left: "50%",
|
|
1974
|
+
textAlign: "right",
|
|
1975
|
+
whiteSpace: "nowrap",
|
|
1976
|
+
overflow: "hidden",
|
|
1977
|
+
textOverflow: "ellipsis",
|
|
1978
|
+
}, children: renderLabel({ kind: "output", id: h.id }) }))] }, h.id));
|
|
1979
|
+
})] }));
|
|
1980
|
+
}
|
|
1981
|
+
|
|
1773
1982
|
const DefaultNode = React.memo(function DefaultNode({ id, data, selected, isConnectable, }) {
|
|
1983
|
+
const updateNodeInternals = react.useUpdateNodeInternals();
|
|
1774
1984
|
const { typeId, showValues, inputValues, outputValues, toString } = data;
|
|
1775
1985
|
const inputEntries = data.inputHandles ?? [];
|
|
1776
1986
|
const outputEntries = data.outputHandles ?? [];
|
|
1987
|
+
React.useEffect(() => {
|
|
1988
|
+
updateNodeInternals(id);
|
|
1989
|
+
}, [
|
|
1990
|
+
id,
|
|
1991
|
+
inputEntries.length,
|
|
1992
|
+
outputEntries.length,
|
|
1993
|
+
showValues,
|
|
1994
|
+
updateNodeInternals,
|
|
1995
|
+
]);
|
|
1777
1996
|
const status = data.status ?? { activeRuns: 0 };
|
|
1778
1997
|
const validation = data.validation ?? {
|
|
1779
1998
|
inputs: [],
|
|
1780
1999
|
outputs: [],
|
|
1781
2000
|
issues: [],
|
|
1782
2001
|
};
|
|
1783
|
-
const HEADER_SIZE = 24;
|
|
1784
|
-
const ROW_SIZE = 22;
|
|
1785
|
-
const maxRows = Math.max(inputEntries.length, outputEntries.length);
|
|
1786
|
-
const minHeight = HEADER_SIZE + maxRows * ROW_SIZE;
|
|
1787
|
-
const minWidth = data.showValues ? 320 : 240;
|
|
1788
|
-
const topFor = (i) => HEADER_SIZE + i * ROW_SIZE + ROW_SIZE / 2;
|
|
1789
2002
|
const hasError = !!status.lastError;
|
|
1790
|
-
const hasValidationError = validation.issues.some((i) => i.level === "error");
|
|
1791
|
-
const hasValidationWarning = !hasValidationError && validation.issues.length > 0;
|
|
1792
2003
|
const isRunning = !!status.activeRuns;
|
|
1793
|
-
const
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
? "border-red-500"
|
|
1799
|
-
: hasValidationWarning
|
|
1800
|
-
? "border-amber-500"
|
|
1801
|
-
: isRunning
|
|
1802
|
-
? "border-blue-500"
|
|
1803
|
-
: "border-gray-500 dark:border-gray-400";
|
|
1804
|
-
const ringClasses = isRunning
|
|
1805
|
-
? "ring-2 ring-blue-200 dark:ring-blue-900"
|
|
1806
|
-
: undefined;
|
|
1807
|
-
const borderClasses = cx(borderWidth, borderStyle, borderColor, ringClasses);
|
|
2004
|
+
const containerBorder = getNodeBorderClassNames({
|
|
2005
|
+
selected,
|
|
2006
|
+
status,
|
|
2007
|
+
validation,
|
|
2008
|
+
});
|
|
1808
2009
|
const pct = Math.round(Math.max(0, Math.min(1, Number(status.progress) || 0)) * 100);
|
|
1809
|
-
return (jsxRuntime.jsxs("div", { className: cx("rounded-lg bg-white/70 !dark:bg-stone-900",
|
|
2010
|
+
return (jsxRuntime.jsxs("div", { className: cx("rounded-lg bg-white/70 !dark:bg-stone-900", containerBorder), style: {
|
|
2011
|
+
position: "relative",
|
|
2012
|
+
minWidth: typeof data.renderWidth === "number" ? data.renderWidth : undefined,
|
|
2013
|
+
minHeight: typeof data.renderHeight === "number" ? data.renderHeight : undefined,
|
|
2014
|
+
}, children: [jsxRuntime.jsxs("div", { className: "flex items-center justify-center px-2 border-b border-solid border-gray-500 dark:border-gray-400 text-gray-600 dark:text-gray-300", style: {
|
|
2015
|
+
maxHeight: NODE_HEADER_HEIGHT_PX,
|
|
2016
|
+
minHeight: NODE_HEADER_HEIGHT_PX,
|
|
2017
|
+
}, children: [jsxRuntime.jsx("strong", { className: "flex-1 h-full text-sm", style: { lineHeight: `${NODE_HEADER_HEIGHT_PX}px` }, children: typeId }), jsxRuntime.jsxs("div", { className: "flex items-center gap-1", children: [hasError && (jsxRuntime.jsx("span", { title: String(status.lastError?.message ?? status.lastError), children: jsxRuntime.jsx(react$1.XCircleIcon, { size: 12, weight: "fill", className: "text-red-500" }) })), validation.issues && validation.issues.length > 0 && (jsxRuntime.jsx(IssueBadge, { level: validation.issues.some((i) => i.level === "error")
|
|
1810
2018
|
? "error"
|
|
1811
2019
|
: "warning", size: 12, className: "w-3 h-3", title: validation.issues
|
|
1812
2020
|
.map((v) => `${v.code}: ${v.message}`)
|
|
1813
|
-
.join("; ") })), jsxRuntime.jsxs("span", { className: "text-[10px] opacity-70", children: ["(", id, ")"] })] })] }),
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
})] }));
|
|
2021
|
+
.join("; ") })), jsxRuntime.jsxs("span", { className: "text-[10px] opacity-70", children: ["(", id, ")"] })] })] }), jsxRuntime.jsx("div", { className: cx("h-px", (isRunning || pct > 0) && "bg-blue-200 dark:bg-blue-900"), children: jsxRuntime.jsx("div", { className: cx("h-px transition-all", (isRunning || pct > 0) && "bg-blue-500"), style: { width: isRunning || pct > 0 ? `${pct}%` : 0 } }) }), jsxRuntime.jsx(NodeHandles, { data: data, isConnectable: isConnectable, getClassName: ({ kind, id }) => {
|
|
2022
|
+
const vIssues = (kind === "input" ? validation.inputs : validation.outputs).filter((v) => v.handle === id);
|
|
2023
|
+
const hasAny = vIssues.length > 0;
|
|
2024
|
+
const hasErr = vIssues.some((v) => v.level === "error");
|
|
2025
|
+
return cx("!w-3 !h-3 !bg-white !dark:bg-stone-900 !border-gray-500 dark:!border-gray-400", kind === "output" && "!rounded-none", hasAny && (hasErr ? "!border-red-500" : "!border-amber-500"));
|
|
2026
|
+
}, renderLabel: ({ kind, id }) => {
|
|
2027
|
+
const entries = kind === "input" ? inputEntries : outputEntries;
|
|
2028
|
+
const entry = entries.find((e) => e.id === id);
|
|
2029
|
+
if (!entry)
|
|
2030
|
+
return id;
|
|
2031
|
+
const vIssues = (kind === "input" ? validation.inputs : validation.outputs).filter((v) => v.handle === id);
|
|
2032
|
+
const hasAny = vIssues.length > 0;
|
|
2033
|
+
const hasErr = vIssues.some((v) => v.level === "error");
|
|
2034
|
+
const title = vIssues
|
|
2035
|
+
.map((v) => `${v.code}: ${v.message}`)
|
|
2036
|
+
.join("; ");
|
|
2037
|
+
// Compose label with truncated value to prevent layout growth
|
|
2038
|
+
const valueText = (() => {
|
|
2039
|
+
if (!showValues)
|
|
2040
|
+
return undefined;
|
|
2041
|
+
if (kind === "input") {
|
|
2042
|
+
const txt = toString(entry.typeId, inputValues?.[entry.id]);
|
|
2043
|
+
return typeof txt === "string" ? txt : String(txt);
|
|
2044
|
+
}
|
|
2045
|
+
const resolved = resolveOutputDisplay(outputValues?.[entry.id], entry.typeId);
|
|
2046
|
+
const txt = toString(resolved.typeId, resolved.value);
|
|
2047
|
+
return typeof txt === "string" ? txt : String(txt);
|
|
2048
|
+
})();
|
|
2049
|
+
return (jsxRuntime.jsxs("span", { className: "flex items-center gap-1 w-full", children: [kind === "output" ? (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [valueText !== undefined && (jsxRuntime.jsx("span", { className: "opacity-60 truncate pl-1", style: { flex: 1, minWidth: 0, maxWidth: "100%" }, children: valueText })), jsxRuntime.jsx("span", { className: "truncate shrink-0", style: { maxWidth: "40%" }, children: id })] })) : (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("span", { className: "truncate shrink-0", style: { maxWidth: "40%" }, children: id }), valueText !== undefined && (jsxRuntime.jsx("span", { className: "opacity-60 truncate pr-1", style: { flex: 1, minWidth: 0, maxWidth: "100%" }, children: valueText }))] })), hasAny && (jsxRuntime.jsx(IssueBadge, { level: hasErr ? "error" : "warning", size: 12, className: "shrink-0", title: title }))] }));
|
|
2050
|
+
} })] }));
|
|
1844
2051
|
});
|
|
1845
2052
|
DefaultNode.displayName = "DefaultNode";
|
|
1846
2053
|
|
|
1847
2054
|
function DefaultContextMenu({ open, clientPos, onAdd, onClose, }) {
|
|
1848
2055
|
const { registry } = useWorkbenchContext();
|
|
1849
|
-
const rf =
|
|
2056
|
+
const rf = react.useReactFlow();
|
|
1850
2057
|
const ids = Array.from(registry.nodes.keys());
|
|
1851
2058
|
const [query, setQuery] = React.useState("");
|
|
1852
2059
|
const q = query.trim().toLowerCase();
|
|
@@ -2005,6 +2212,58 @@ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement }, r
|
|
|
2005
2212
|
const { wb, registry, inputsMap, outputsMap, valuesTick, nodeStatus, edgeStatus, validationByNode, validationByEdge, } = useWorkbenchContext();
|
|
2006
2213
|
const nodeValidation = validationByNode;
|
|
2007
2214
|
const edgeValidation = validationByEdge.errors;
|
|
2215
|
+
// Keep stable references for nodes/edges to avoid unnecessary updates
|
|
2216
|
+
const prevNodesRef = React.useRef([]);
|
|
2217
|
+
const prevEdgesRef = React.useRef([]);
|
|
2218
|
+
function retainStabilityById(prev, next, isSame) {
|
|
2219
|
+
if (prev.length === 0)
|
|
2220
|
+
return next;
|
|
2221
|
+
const map = new Map();
|
|
2222
|
+
for (const p of prev)
|
|
2223
|
+
map.set(p.id, p);
|
|
2224
|
+
const out = new Array(next.length);
|
|
2225
|
+
for (let i = 0; i < next.length; i++) {
|
|
2226
|
+
const n = next[i];
|
|
2227
|
+
const p = map.get(n.id);
|
|
2228
|
+
out[i] = p && isSame(p, n) ? p : n;
|
|
2229
|
+
}
|
|
2230
|
+
return out;
|
|
2231
|
+
}
|
|
2232
|
+
const isSameNode = (a, b) => {
|
|
2233
|
+
// Compare the parts that affect rendering
|
|
2234
|
+
const pick = (n) => ({
|
|
2235
|
+
position: n.position,
|
|
2236
|
+
type: n.type,
|
|
2237
|
+
selected: n.selected,
|
|
2238
|
+
initialWidth: n.initialWidth,
|
|
2239
|
+
initialHeight: n.initialHeight,
|
|
2240
|
+
data: n.data && {
|
|
2241
|
+
typeId: n.data.typeId,
|
|
2242
|
+
inputHandles: n.data.inputHandles,
|
|
2243
|
+
outputHandles: n.data.outputHandles,
|
|
2244
|
+
showValues: n.data.showValues,
|
|
2245
|
+
inputValues: n.data.inputValues,
|
|
2246
|
+
outputValues: n.data.outputValues,
|
|
2247
|
+
status: n.data.status,
|
|
2248
|
+
validation: n.data.validation,
|
|
2249
|
+
},
|
|
2250
|
+
});
|
|
2251
|
+
return isEqual(pick(a), pick(b));
|
|
2252
|
+
};
|
|
2253
|
+
const isSameEdge = (a, b) => {
|
|
2254
|
+
const pick = (e) => ({
|
|
2255
|
+
source: e.source,
|
|
2256
|
+
target: e.target,
|
|
2257
|
+
sourceHandle: e.sourceHandle,
|
|
2258
|
+
targetHandle: e.targetHandle,
|
|
2259
|
+
selected: e.selected,
|
|
2260
|
+
animated: e.animated,
|
|
2261
|
+
style: e.style,
|
|
2262
|
+
label: e.label,
|
|
2263
|
+
type: e.type,
|
|
2264
|
+
});
|
|
2265
|
+
return isEqual(pick(a), pick(b));
|
|
2266
|
+
};
|
|
2008
2267
|
// Expose imperative API
|
|
2009
2268
|
const rfInstanceRef = React.useRef(null);
|
|
2010
2269
|
React.useImperativeHandle(ref, () => ({
|
|
@@ -2015,7 +2274,7 @@ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement }, r
|
|
|
2015
2274
|
catch { }
|
|
2016
2275
|
},
|
|
2017
2276
|
}));
|
|
2018
|
-
const { onConnect, onNodesChange, onEdgesChange, onEdgesDelete, onNodesDelete,
|
|
2277
|
+
const { onConnect, onNodesChange, onEdgesChange, onEdgesDelete, onNodesDelete, } = useWorkbenchBridge(wb);
|
|
2019
2278
|
const { nodeTypes, resolveNodeType } = React.useMemo(() => {
|
|
2020
2279
|
// Build nodeTypes map using UI extension registry
|
|
2021
2280
|
const ui = wb.getUI();
|
|
@@ -2053,8 +2312,91 @@ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement }, r
|
|
|
2053
2312
|
selectedNodeIds: new Set(sel.nodes),
|
|
2054
2313
|
selectedEdgeIds: new Set(sel.edges),
|
|
2055
2314
|
});
|
|
2056
|
-
|
|
2057
|
-
|
|
2315
|
+
// Retain references for unchanged items
|
|
2316
|
+
const stableNodes = retainStabilityById(prevNodesRef.current, out.nodes, isSameNode);
|
|
2317
|
+
const stableEdges = retainStabilityById(prevEdgesRef.current, out.edges, isSameEdge);
|
|
2318
|
+
// Debug: log updates/additions/removals (use value equality, not reference)
|
|
2319
|
+
try {
|
|
2320
|
+
const prevNodeIds = new Set(prevNodesRef.current.map((n) => n.id));
|
|
2321
|
+
const nextNodeIds = new Set(out.nodes.map((n) => n.id));
|
|
2322
|
+
const addedNodeIds = out.nodes
|
|
2323
|
+
.filter((n) => !prevNodeIds.has(n.id))
|
|
2324
|
+
.map((n) => n.id);
|
|
2325
|
+
const removedNodeIds = prevNodesRef.current
|
|
2326
|
+
.filter((n) => !nextNodeIds.has(n.id))
|
|
2327
|
+
.map((n) => n.id);
|
|
2328
|
+
const prevNodeMap = new Map(prevNodesRef.current.map((n) => [n.id, n]));
|
|
2329
|
+
const changedNodeIds = out.nodes
|
|
2330
|
+
.filter((n) => {
|
|
2331
|
+
const p = prevNodeMap.get(n.id);
|
|
2332
|
+
return p ? !isSameNode(p, n) : false;
|
|
2333
|
+
})
|
|
2334
|
+
.map((n) => n.id);
|
|
2335
|
+
// Detect handle updates (ids/length changes) for targeted debug
|
|
2336
|
+
const toIds = (arr) => Array.isArray(arr) ? arr.map((h) => h?.id) : [];
|
|
2337
|
+
const handlesEqual = (a, b) => {
|
|
2338
|
+
const aIds = toIds(a);
|
|
2339
|
+
const bIds = toIds(b);
|
|
2340
|
+
if (aIds.length !== bIds.length)
|
|
2341
|
+
return false;
|
|
2342
|
+
for (let i = 0; i < aIds.length; i++) {
|
|
2343
|
+
if (aIds[i] !== bIds[i])
|
|
2344
|
+
return false;
|
|
2345
|
+
}
|
|
2346
|
+
return true;
|
|
2347
|
+
};
|
|
2348
|
+
const handleChanged = out.nodes
|
|
2349
|
+
.filter((n) => {
|
|
2350
|
+
const p = prevNodeMap.get(n.id);
|
|
2351
|
+
if (!p)
|
|
2352
|
+
return false;
|
|
2353
|
+
const inChanged = !handlesEqual(p.data?.inputHandles, n.data?.inputHandles);
|
|
2354
|
+
const outChanged = !handlesEqual(p.data?.outputHandles, n.data?.outputHandles);
|
|
2355
|
+
return inChanged || outChanged;
|
|
2356
|
+
})
|
|
2357
|
+
.map((n) => n.id);
|
|
2358
|
+
const prevEdgeIds = new Set(prevEdgesRef.current.map((e) => e.id));
|
|
2359
|
+
const nextEdgeIds = new Set(out.edges.map((e) => e.id));
|
|
2360
|
+
const addedEdgeIds = out.edges
|
|
2361
|
+
.filter((e) => !prevEdgeIds.has(e.id))
|
|
2362
|
+
.map((e) => e.id);
|
|
2363
|
+
const removedEdgeIds = prevEdgesRef.current
|
|
2364
|
+
.filter((e) => !nextEdgeIds.has(e.id))
|
|
2365
|
+
.map((e) => e.id);
|
|
2366
|
+
const prevEdgeMap = new Map(prevEdgesRef.current.map((e) => [e.id, e]));
|
|
2367
|
+
const changedEdgeIds = out.edges
|
|
2368
|
+
.filter((e) => {
|
|
2369
|
+
const p = prevEdgeMap.get(e.id);
|
|
2370
|
+
return p ? !isSameEdge(p, e) : false;
|
|
2371
|
+
})
|
|
2372
|
+
.map((e) => e.id);
|
|
2373
|
+
if (addedNodeIds.length ||
|
|
2374
|
+
removedNodeIds.length ||
|
|
2375
|
+
changedNodeIds.length ||
|
|
2376
|
+
handleChanged.length) {
|
|
2377
|
+
// eslint-disable-next-line no-console
|
|
2378
|
+
console.debug("[WorkbenchCanvas] node updates", {
|
|
2379
|
+
added: addedNodeIds,
|
|
2380
|
+
removed: removedNodeIds,
|
|
2381
|
+
changed: changedNodeIds,
|
|
2382
|
+
handleChanged,
|
|
2383
|
+
});
|
|
2384
|
+
}
|
|
2385
|
+
if (addedEdgeIds.length ||
|
|
2386
|
+
removedEdgeIds.length ||
|
|
2387
|
+
changedEdgeIds.length) {
|
|
2388
|
+
// eslint-disable-next-line no-console
|
|
2389
|
+
console.debug("[WorkbenchCanvas] edge updates", {
|
|
2390
|
+
added: addedEdgeIds,
|
|
2391
|
+
removed: removedEdgeIds,
|
|
2392
|
+
changed: changedEdgeIds,
|
|
2393
|
+
});
|
|
2394
|
+
}
|
|
2395
|
+
}
|
|
2396
|
+
catch { }
|
|
2397
|
+
prevNodesRef.current = stableNodes;
|
|
2398
|
+
prevEdgesRef.current = stableEdges;
|
|
2399
|
+
return { nodes: stableNodes, edges: stableEdges };
|
|
2058
2400
|
}, [
|
|
2059
2401
|
showValues,
|
|
2060
2402
|
inputsMap,
|
|
@@ -2066,9 +2408,9 @@ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement }, r
|
|
|
2066
2408
|
edgeStatus,
|
|
2067
2409
|
nodeValidation,
|
|
2068
2410
|
edgeValidation,
|
|
2069
|
-
nodeTypes,
|
|
2070
2411
|
resolveNodeType,
|
|
2071
2412
|
]);
|
|
2413
|
+
const throttled = useThrottledValue({ nodes, edges }, 100);
|
|
2072
2414
|
const [menuOpen, setMenuOpen] = React.useState(false);
|
|
2073
2415
|
const [menuPos, setMenuPos] = React.useState(null);
|
|
2074
2416
|
const [nodeMenuOpen, setNodeMenuOpen] = React.useState(false);
|
|
@@ -2095,7 +2437,7 @@ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement }, r
|
|
|
2095
2437
|
const addNodeAt = (typeId, pos) => {
|
|
2096
2438
|
wb.addNode({ typeId, position: pos });
|
|
2097
2439
|
};
|
|
2098
|
-
return (jsxRuntime.jsx("div", { className: "w-full h-full", onContextMenu: onContextMenu, children: jsxRuntime.jsxs(ReactFlow, { nodes: nodes, edges: edges, nodeTypes: nodeTypes, selectionOnDrag: true, onConnect: onConnect, onEdgesChange: onEdgesChange, onEdgesDelete: onEdgesDelete, onNodesDelete: onNodesDelete, onNodesChange: onNodesChange,
|
|
2440
|
+
return (jsxRuntime.jsx("div", { className: "w-full h-full", onContextMenu: onContextMenu, children: jsxRuntime.jsxs(react.ReactFlow, { nodes: throttled.nodes, edges: throttled.edges, nodeTypes: nodeTypes, onlyRenderVisibleElements: true, selectionOnDrag: true, onConnect: onConnect, onEdgesChange: onEdgesChange, onEdgesDelete: onEdgesDelete, onNodesDelete: onNodesDelete, onNodesChange: onNodesChange, deleteKeyCode: ["Backspace", "Delete"], fitView: true, onInit: (inst) => (rfInstanceRef.current = inst), children: [jsxRuntime.jsx(react.Background, {}), jsxRuntime.jsx(react.MiniMap, {}), jsxRuntime.jsx(react.Controls, {}), jsxRuntime.jsx(DefaultContextMenu, { open: menuOpen, clientPos: menuPos, onAdd: addNodeAt, onClose: () => setMenuOpen(false) }), jsxRuntime.jsx(NodeContextMenu, { open: nodeMenuOpen, clientPos: nodeMenuPos, nodeId: nodeAtMenu, onClose: () => setNodeMenuOpen(false) })] }) }));
|
|
2099
2441
|
});
|
|
2100
2442
|
|
|
2101
2443
|
function WorkbenchStudioCanvas({ setRegistry, autoScroll, onAutoScrollChange, example, onExampleChange, engine, onEngineChange, backendKind, onBackendKindChange, httpBaseUrl, onHttpBaseUrlChange, wsUrl, onWsUrlChange, debug, onDebugChange, showValues, onShowValuesChange, hideWorkbench, onHideWorkbenchChange, overrides, onInit, onChange, }) {
|
|
@@ -2172,7 +2514,7 @@ function WorkbenchStudioCanvas({ setRegistry, autoScroll, onAutoScrollChange, ex
|
|
|
2172
2514
|
runAutoLayout();
|
|
2173
2515
|
};
|
|
2174
2516
|
onInit({ wb, runner, setInitialGraph });
|
|
2175
|
-
}, [onInit, wb, runner, runAutoLayout]);
|
|
2517
|
+
}, [onInit, wb, runner, runAutoLayout, registry, setRegistry]);
|
|
2176
2518
|
// Expose change callback on graph/value changes
|
|
2177
2519
|
React.useEffect(() => {
|
|
2178
2520
|
if (!onChange)
|
|
@@ -2207,9 +2549,14 @@ function WorkbenchStudioCanvas({ setRegistry, autoScroll, onAutoScrollChange, ex
|
|
|
2207
2549
|
if (!ex)
|
|
2208
2550
|
return;
|
|
2209
2551
|
const { registry: r, def } = await ex.load();
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2552
|
+
// Keep registry consistent with backend:
|
|
2553
|
+
// - For local backend, allow example to provide its own registry
|
|
2554
|
+
// - For remote backend, NEVER overwrite the hydrated remote registry
|
|
2555
|
+
if (backendKind === "local") {
|
|
2556
|
+
if (r) {
|
|
2557
|
+
setRegistry(r);
|
|
2558
|
+
wb.setRegistry(r);
|
|
2559
|
+
}
|
|
2213
2560
|
}
|
|
2214
2561
|
await wb.load(def);
|
|
2215
2562
|
// Build a local runtime so seeded defaults are visible pre-run
|
|
@@ -2217,7 +2564,15 @@ function WorkbenchStudioCanvas({ setRegistry, autoScroll, onAutoScrollChange, ex
|
|
|
2217
2564
|
runAutoLayout();
|
|
2218
2565
|
setExampleState(key);
|
|
2219
2566
|
onExampleChange?.(key);
|
|
2220
|
-
}, [
|
|
2567
|
+
}, [
|
|
2568
|
+
runner,
|
|
2569
|
+
wb,
|
|
2570
|
+
onExampleChange,
|
|
2571
|
+
runAutoLayout,
|
|
2572
|
+
examples,
|
|
2573
|
+
setRegistry,
|
|
2574
|
+
backendKind,
|
|
2575
|
+
]);
|
|
2221
2576
|
const downloadGraph = React.useCallback(() => {
|
|
2222
2577
|
try {
|
|
2223
2578
|
const def = wb.export();
|
|
@@ -2327,6 +2682,9 @@ function WorkbenchStudioCanvas({ setRegistry, autoScroll, onAutoScrollChange, ex
|
|
|
2327
2682
|
return;
|
|
2328
2683
|
if (runner.isRunning())
|
|
2329
2684
|
return;
|
|
2685
|
+
// Only auto-launch for local backend; require explicit Start for remote
|
|
2686
|
+
if (backendKind !== "local")
|
|
2687
|
+
return;
|
|
2330
2688
|
const d = wb.export();
|
|
2331
2689
|
if (!d.nodes || d.nodes.length === 0)
|
|
2332
2690
|
return;
|
|
@@ -2339,14 +2697,14 @@ function WorkbenchStudioCanvas({ setRegistry, autoScroll, onAutoScrollChange, ex
|
|
|
2339
2697
|
catch {
|
|
2340
2698
|
// ignore
|
|
2341
2699
|
}
|
|
2342
|
-
}, [engine, runner, wb]);
|
|
2700
|
+
}, [engine, runner, wb, backendKind]);
|
|
2343
2701
|
// When switching to remote backend, auto-hydrate registry from backend
|
|
2344
2702
|
React.useEffect(() => {
|
|
2345
2703
|
if (backendKind === "remote-http" && httpBaseUrl) {
|
|
2346
|
-
|
|
2704
|
+
hydrateFromBackend("remote-http", httpBaseUrl);
|
|
2347
2705
|
}
|
|
2348
2706
|
else if (backendKind === "remote-ws" && wsUrl) {
|
|
2349
|
-
|
|
2707
|
+
hydrateFromBackend("remote-ws", wsUrl);
|
|
2350
2708
|
}
|
|
2351
2709
|
}, [backendKind, httpBaseUrl, wsUrl, hydrateFromBackend]);
|
|
2352
2710
|
React.useEffect(() => {
|
|
@@ -2516,11 +2874,11 @@ function WorkbenchStudioCanvas({ setRegistry, autoScroll, onAutoScrollChange, ex
|
|
|
2516
2874
|
return overrides.toElement(baseToElement, { registry });
|
|
2517
2875
|
return baseToElement;
|
|
2518
2876
|
}, [overrides, baseToElement, registry]);
|
|
2519
|
-
return (jsxRuntime.jsxs("div", { className: "w-full h-screen flex flex-col", children: [jsxRuntime.jsxs("div", { className: "p-2 border-b border-gray-300 flex gap-2 items-center", children: [runner.isRunning() ? (jsxRuntime.jsxs("span", { className: "ml-2 text-sm text-green-700", children: ["Running: ", runner.getRunningEngine()] })) : (jsxRuntime.jsx("span", { className: "ml-2 text-sm text-gray-500", children: "Stopped" })), jsxRuntime.jsxs("span", { className: "ml-2 flex items-center gap-1 text-xs", title: transportStatus.kind || undefined, children: [transportStatus.state === "local" && (jsxRuntime.jsx(react.PlugsConnectedIcon, { size: 14, className: "text-gray-500" })), transportStatus.state === "connecting" && (jsxRuntime.jsx(react.ClockClockwiseIcon, { size: 14, className: "text-amber-600 animate-pulse" })), transportStatus.state === "connected" && (jsxRuntime.jsx(react.WifiHighIcon, { size: 14, className: "text-green-600" })), transportStatus.state === "disconnected" && (jsxRuntime.jsx(react.WifiSlashIcon, { size: 14, className: "text-red-600" })), transportStatus.state === "retrying" && (jsxRuntime.jsx(react.ClockClockwiseIcon, { size: 14, className: "text-amber-700 animate-pulse" }))] }), jsxRuntime.jsx("label", { className: "ml-2 text-sm", children: "Example:" }), jsxRuntime.jsxs("select", { className: "border border-gray-300 rounded px-2 py-1", value: exampleState, onChange: (e) => applyExample(e.target.value), disabled: runner.isRunning(), title: runner.isRunning()
|
|
2877
|
+
return (jsxRuntime.jsxs("div", { className: "w-full h-screen flex flex-col", children: [jsxRuntime.jsxs("div", { className: "p-2 border-b border-gray-300 flex gap-2 items-center", children: [runner.isRunning() ? (jsxRuntime.jsxs("span", { className: "ml-2 text-sm text-green-700", children: ["Running: ", runner.getRunningEngine()] })) : (jsxRuntime.jsx("span", { className: "ml-2 text-sm text-gray-500", children: "Stopped" })), jsxRuntime.jsxs("span", { className: "ml-2 flex items-center gap-1 text-xs", title: transportStatus.kind || undefined, children: [transportStatus.state === "local" && (jsxRuntime.jsx(react$1.PlugsConnectedIcon, { size: 14, className: "text-gray-500" })), transportStatus.state === "connecting" && (jsxRuntime.jsx(react$1.ClockClockwiseIcon, { size: 14, className: "text-amber-600 animate-pulse" })), transportStatus.state === "connected" && (jsxRuntime.jsx(react$1.WifiHighIcon, { size: 14, className: "text-green-600" })), transportStatus.state === "disconnected" && (jsxRuntime.jsx(react$1.WifiSlashIcon, { size: 14, className: "text-red-600" })), transportStatus.state === "retrying" && (jsxRuntime.jsx(react$1.ClockClockwiseIcon, { size: 14, className: "text-amber-700 animate-pulse" }))] }), jsxRuntime.jsx("label", { className: "ml-2 text-sm", children: "Example:" }), jsxRuntime.jsxs("select", { className: "border border-gray-300 rounded px-2 py-1", value: exampleState, onChange: (e) => applyExample(e.target.value), disabled: runner.isRunning(), title: runner.isRunning()
|
|
2520
2878
|
? "Stop engine before switching example"
|
|
2521
2879
|
: undefined, children: [jsxRuntime.jsx("option", { value: "", children: "Select Example\u2026" }), examples.map((ex) => (jsxRuntime.jsx("option", { value: ex.id, children: ex.label }, ex.id)))] }), jsxRuntime.jsx("label", { className: "ml-2 text-sm", children: "Backend:" }), jsxRuntime.jsxs("select", { className: "border border-gray-300 rounded px-2 py-1", value: backendKind, onChange: (e) => onBackendKindChange(e.target.value), disabled: runner.isRunning(), title: runner.isRunning()
|
|
2522
2880
|
? "Stop engine before switching backend"
|
|
2523
|
-
: undefined, children: [jsxRuntime.jsx("option", { value: "local", children: "Local" }), jsxRuntime.jsx("option", { value: "remote-http", children: "Remote (HTTP)" }), jsxRuntime.jsx("option", { value: "remote-ws", children: "Remote (WebSocket)" })] }), backendKind === "remote-http" && (jsxRuntime.jsx("input", { className: "ml-2 border border-gray-300 rounded px-2 py-1 w-72", placeholder: "http://127.0.0.1:18080", value: httpBaseUrl, onChange: (e) => onHttpBaseUrlChange(e.target.value) })), backendKind === "remote-ws" && (jsxRuntime.jsx("input", { className: "ml-2 border border-gray-300 rounded px-2 py-1 w-72", placeholder: "ws://127.0.0.1:18081", value: wsUrl, onChange: (e) => onWsUrlChange(e.target.value) })), jsxRuntime.jsxs("select", { className: "border border-gray-300 rounded px-2 py-1", value: runner.getRunningEngine() ?? engine ?? "", onChange: (e) => {
|
|
2881
|
+
: undefined, children: [jsxRuntime.jsx("option", { value: "local", children: "Local" }), jsxRuntime.jsx("option", { value: "remote-http", children: "Remote (HTTP)" }), jsxRuntime.jsx("option", { value: "remote-ws", children: "Remote (WebSocket)" })] }), backendKind === "remote-http" && !!onHttpBaseUrlChange && (jsxRuntime.jsx("input", { className: "ml-2 border border-gray-300 rounded px-2 py-1 w-72", placeholder: "http://127.0.0.1:18080", value: httpBaseUrl, onChange: (e) => onHttpBaseUrlChange(e.target.value) })), backendKind === "remote-ws" && !!onWsUrlChange && (jsxRuntime.jsx("input", { className: "ml-2 border border-gray-300 rounded px-2 py-1 w-72", placeholder: "ws://127.0.0.1:18081", value: wsUrl, onChange: (e) => onWsUrlChange(e.target.value) })), jsxRuntime.jsxs("select", { className: "border border-gray-300 rounded px-2 py-1", value: runner.getRunningEngine() ?? engine ?? "", onChange: (e) => {
|
|
2524
2882
|
const kind = e.target.value || undefined;
|
|
2525
2883
|
onEngineChange?.(kind);
|
|
2526
2884
|
}, children: [jsxRuntime.jsx("option", { value: "", children: "Select Engine\u2026" }), jsxRuntime.jsx("option", { value: "push", children: "Push" }), jsxRuntime.jsx("option", { value: "batched", children: "Batched" }), jsxRuntime.jsx("option", { value: "pull", children: "Pull" }), jsxRuntime.jsx("option", { value: "hybrid", children: "Hybrid" }), jsxRuntime.jsx("option", { value: "step", children: "Step" })] }), runner.getRunningEngine() === "step" && (jsxRuntime.jsx("button", { className: "ml-2 border border-gray-300 rounded px-2 py-1.5", onClick: () => runner.step(), disabled: !runner.isRunning(), children: "Step" })), runner.getRunningEngine() === "batched" && (jsxRuntime.jsx("button", { className: "ml-2 border border-gray-300 rounded px-2 py-1.5", onClick: () => runner.flush(), disabled: !runner.isRunning(), children: "Flush" })), runner.isRunning() ? (jsxRuntime.jsx("button", { className: "border border-gray-300 rounded px-2 py-1.5", onClick: () => runner.dispose(), disabled: !runner.isRunning(), children: "Stop" })) : (jsxRuntime.jsx("button", { className: "border border-gray-300 rounded px-2 py-1.5", onClick: () => {
|
|
@@ -2535,7 +2893,7 @@ function WorkbenchStudioCanvas({ setRegistry, autoScroll, onAutoScrollChange, ex
|
|
|
2535
2893
|
}
|
|
2536
2894
|
}, disabled: !engine, children: "Start" })), jsxRuntime.jsx("button", { className: "border border-gray-300 rounded px-2 py-1.5", onClick: runAutoLayout, children: "Auto Layout" }), jsxRuntime.jsx("button", { className: "ml-2 border border-gray-300 rounded px-2 py-1.5", onClick: () => canvasRef.current?.fitView?.(), title: "Fit View", children: "Fit View" }), jsxRuntime.jsx("button", { className: "ml-2 border border-gray-300 rounded px-2 py-1.5", onClick: downloadGraph, children: "Download Graph" }), jsxRuntime.jsxs("label", { className: "ml-2 flex items-center gap-1", children: [jsxRuntime.jsx("input", { type: "checkbox", checked: debug, onChange: (e) => onDebugChange(e.target.checked) }), jsxRuntime.jsx("span", { children: "Debug events" })] }), jsxRuntime.jsxs("label", { className: "ml-2 flex items-center gap-1", children: [jsxRuntime.jsx("input", { type: "checkbox", checked: showValues, onChange: (e) => onShowValuesChange(e.target.checked) }), jsxRuntime.jsx("span", { children: "Show values in nodes" })] })] }), jsxRuntime.jsxs("div", { className: "flex flex-1 min-h-0", children: [jsxRuntime.jsx("div", { className: "flex-1 min-w-0", children: jsxRuntime.jsx(WorkbenchCanvas, { ref: canvasRef, showValues: showValues, toString: toString, toElement: toElement }) }), jsxRuntime.jsx(Inspector, { setInput: setInput, debug: debug, autoScroll: autoScroll, hideWorkbench: hideWorkbench, onAutoScrollChange: onAutoScrollChange, onHideWorkbenchChange: onHideWorkbenchChange, toString: toString, toElement: toElement, contextPanel: overrides?.contextPanel })] })] }));
|
|
2537
2895
|
}
|
|
2538
|
-
function WorkbenchStudio({ engine, onEngineChange, example, onExampleChange, backendKind, onBackendKindChange, httpBaseUrl, onHttpBaseUrlChange, wsUrl, onWsUrlChange, debug, onDebugChange, showValues, onShowValuesChange, hideWorkbench, onHideWorkbenchChange, autoScroll, onAutoScrollChange, overrides, onInit, onChange,
|
|
2896
|
+
function WorkbenchStudio({ engine, onEngineChange, example, onExampleChange, backendKind, onBackendKindChange, httpBaseUrl, onHttpBaseUrlChange, wsUrl, onWsUrlChange, debug, onDebugChange, showValues, onShowValuesChange, hideWorkbench, onHideWorkbenchChange, autoScroll, onAutoScrollChange, overrides, onInit, onChange, }) {
|
|
2539
2897
|
const [registry, setRegistry] = React.useState(sparkGraph.createSimpleGraphRegistry());
|
|
2540
2898
|
const [wb] = React.useState(() => new InMemoryWorkbench({ ui: new DefaultUIExtensionRegistry() }));
|
|
2541
2899
|
const runner = React.useMemo(() => {
|
|
@@ -2569,6 +2927,7 @@ exports.DefaultUIExtensionRegistry = DefaultUIExtensionRegistry;
|
|
|
2569
2927
|
exports.InMemoryWorkbench = InMemoryWorkbench;
|
|
2570
2928
|
exports.Inspector = Inspector;
|
|
2571
2929
|
exports.LocalGraphRunner = LocalGraphRunner;
|
|
2930
|
+
exports.NodeHandles = NodeHandles;
|
|
2572
2931
|
exports.RemoteGraphRunner = RemoteGraphRunner;
|
|
2573
2932
|
exports.WorkbenchCanvas = WorkbenchCanvas;
|
|
2574
2933
|
exports.WorkbenchContext = WorkbenchContext;
|
|
@@ -2583,6 +2942,7 @@ exports.summarizeDeep = summarizeDeep;
|
|
|
2583
2942
|
exports.toReactFlow = toReactFlow;
|
|
2584
2943
|
exports.useQueryParamBoolean = useQueryParamBoolean;
|
|
2585
2944
|
exports.useQueryParamString = useQueryParamString;
|
|
2945
|
+
exports.useThrottledValue = useThrottledValue;
|
|
2586
2946
|
exports.useWorkbenchBridge = useWorkbenchBridge;
|
|
2587
2947
|
exports.useWorkbenchContext = useWorkbenchContext;
|
|
2588
2948
|
exports.useWorkbenchGraphTick = useWorkbenchGraphTick;
|