@bian-womp/spark-workbench 0.2.0 → 0.2.2
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 -118
- package/lib/cjs/index.cjs.map +1 -1
- package/lib/cjs/src/core/InMemoryWorkbench.d.ts +1 -2
- package/lib/cjs/src/core/InMemoryWorkbench.d.ts.map +1 -1
- package/lib/cjs/src/core/contracts.d.ts +7 -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 +1 -1
- 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 +2 -2
- 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/context/WorkbenchContext.d.ts +1 -0
- package/lib/cjs/src/misc/context/WorkbenchContext.d.ts.map +1 -1
- package/lib/cjs/src/misc/context/WorkbenchContext.provider.d.ts.map +1 -1
- 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 +22 -0
- package/lib/cjs/src/misc/mapping.d.ts.map +1 -1
- package/lib/esm/index.js +481 -117
- package/lib/esm/index.js.map +1 -1
- package/lib/esm/src/core/InMemoryWorkbench.d.ts +1 -2
- package/lib/esm/src/core/InMemoryWorkbench.d.ts.map +1 -1
- package/lib/esm/src/core/contracts.d.ts +7 -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 +1 -1
- 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 +2 -2
- 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/context/WorkbenchContext.d.ts +1 -0
- package/lib/esm/src/misc/context/WorkbenchContext.d.ts.map +1 -1
- package/lib/esm/src/misc/context/WorkbenchContext.provider.d.ts.map +1 -1
- 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 +22 -0
- package/lib/esm/src/misc/mapping.d.ts.map +1 -1
- package/package.json +7 -5
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 {
|
|
@@ -201,6 +202,20 @@ class InMemoryWorkbench extends AbstractWorkbench {
|
|
|
201
202
|
});
|
|
202
203
|
this.emit("validationChanged", this.validate());
|
|
203
204
|
}
|
|
205
|
+
updateEdgeType(edgeId, typeId) {
|
|
206
|
+
const e = this.def.edges.find((x) => x.id === edgeId);
|
|
207
|
+
if (!e)
|
|
208
|
+
return;
|
|
209
|
+
if (!typeId)
|
|
210
|
+
delete e.typeId;
|
|
211
|
+
else
|
|
212
|
+
e.typeId = typeId;
|
|
213
|
+
this.emit("graphChanged", {
|
|
214
|
+
def: this.def,
|
|
215
|
+
change: { type: "updateEdgeType", edgeId, typeId },
|
|
216
|
+
});
|
|
217
|
+
this.refreshValidation();
|
|
218
|
+
}
|
|
204
219
|
updateParams(nodeId, params) {
|
|
205
220
|
const n = this.def.nodes.find((n) => n.nodeId === nodeId);
|
|
206
221
|
if (!n)
|
|
@@ -232,6 +247,10 @@ class InMemoryWorkbench extends AbstractWorkbench {
|
|
|
232
247
|
setSelection(sel) {
|
|
233
248
|
this.selection = { nodes: [...sel.nodes], edges: [...sel.edges] };
|
|
234
249
|
this.emit("selectionChanged", this.selection);
|
|
250
|
+
this.emit("graphUiChanged", {
|
|
251
|
+
def: this.def,
|
|
252
|
+
change: { type: "selection" },
|
|
253
|
+
});
|
|
235
254
|
}
|
|
236
255
|
getSelection() {
|
|
237
256
|
return {
|
|
@@ -239,18 +258,6 @@ class InMemoryWorkbench extends AbstractWorkbench {
|
|
|
239
258
|
edges: [...this.selection.edges],
|
|
240
259
|
};
|
|
241
260
|
}
|
|
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
261
|
on(event, handler) {
|
|
255
262
|
if (!this.listeners.has(event))
|
|
256
263
|
this.listeners.set(event, new Set());
|
|
@@ -760,49 +767,91 @@ function useWorkbenchBridge(wb) {
|
|
|
760
767
|
});
|
|
761
768
|
}, [wb]);
|
|
762
769
|
const onNodesChange = React.useCallback((changes) => {
|
|
770
|
+
// Apply position updates
|
|
763
771
|
changes.forEach((c) => {
|
|
764
|
-
if (c.type === "position" && c.position)
|
|
772
|
+
if (c.type === "position" && c.position) {
|
|
765
773
|
wb.setPosition(c.id, c.position);
|
|
766
|
-
|
|
767
|
-
wb.removeNode(c.id);
|
|
768
|
-
if (c.type === "select")
|
|
769
|
-
wb.toggleNodeSelection(c.id);
|
|
774
|
+
}
|
|
770
775
|
});
|
|
776
|
+
// Derive next node selection from change set
|
|
777
|
+
const current = wb.getSelection();
|
|
778
|
+
const nextNodeIds = new Set(current.nodes);
|
|
779
|
+
let selectionChanged = false;
|
|
780
|
+
for (const change of changes) {
|
|
781
|
+
const type = change?.type;
|
|
782
|
+
if (type === "select") {
|
|
783
|
+
const id = change.id;
|
|
784
|
+
const selected = change.selected;
|
|
785
|
+
if (typeof selected === "boolean") {
|
|
786
|
+
if (selected) {
|
|
787
|
+
if (!nextNodeIds.has(id)) {
|
|
788
|
+
nextNodeIds.add(id);
|
|
789
|
+
selectionChanged = true;
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
else if (nextNodeIds.delete(id)) {
|
|
793
|
+
selectionChanged = true;
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
else if (type === "remove") {
|
|
798
|
+
const id = change.id;
|
|
799
|
+
if (nextNodeIds.delete(id))
|
|
800
|
+
selectionChanged = true;
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
if (selectionChanged) {
|
|
804
|
+
wb.setSelection({
|
|
805
|
+
nodes: Array.from(nextNodeIds),
|
|
806
|
+
edges: current.edges,
|
|
807
|
+
});
|
|
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 === "remove") {
|
|
833
|
+
const id = change.id;
|
|
834
|
+
if (nextEdgeIds.delete(id))
|
|
835
|
+
selectionChanged = true;
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
if (selectionChanged) {
|
|
839
|
+
wb.setSelection({
|
|
840
|
+
nodes: current.nodes,
|
|
841
|
+
edges: Array.from(nextEdgeIds),
|
|
842
|
+
});
|
|
843
|
+
}
|
|
780
844
|
}, [wb]);
|
|
781
845
|
const onNodesDelete = React.useCallback((nodes) => {
|
|
782
846
|
for (const n of nodes)
|
|
783
847
|
wb.removeNode(n.id);
|
|
784
848
|
}, [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
849
|
return {
|
|
800
850
|
onConnect,
|
|
801
851
|
onNodesChange,
|
|
802
852
|
onEdgesChange,
|
|
803
853
|
onEdgesDelete,
|
|
804
854
|
onNodesDelete,
|
|
805
|
-
onSelectionChange,
|
|
806
855
|
};
|
|
807
856
|
}
|
|
808
857
|
function useWorkbenchGraphTick(wb) {
|
|
@@ -842,6 +891,44 @@ function useWorkbenchVersionTick(runner) {
|
|
|
842
891
|
}, [runner]);
|
|
843
892
|
return version;
|
|
844
893
|
}
|
|
894
|
+
function useThrottledValue(value, intervalMs) {
|
|
895
|
+
const [throttled, setThrottled] = React.useState(value);
|
|
896
|
+
const lastSetAtRef = React.useRef(0);
|
|
897
|
+
const timeoutRef = React.useRef(null);
|
|
898
|
+
React.useEffect(() => {
|
|
899
|
+
const now = typeof performance !== "undefined" && performance.now
|
|
900
|
+
? performance.now()
|
|
901
|
+
: Date.now();
|
|
902
|
+
const elapsed = now - lastSetAtRef.current;
|
|
903
|
+
if (elapsed >= intervalMs) {
|
|
904
|
+
lastSetAtRef.current = now;
|
|
905
|
+
setThrottled(value);
|
|
906
|
+
}
|
|
907
|
+
else {
|
|
908
|
+
if (timeoutRef.current !== null) {
|
|
909
|
+
window.clearTimeout(timeoutRef.current);
|
|
910
|
+
}
|
|
911
|
+
timeoutRef.current = window.setTimeout(() => {
|
|
912
|
+
lastSetAtRef.current =
|
|
913
|
+
typeof performance !== "undefined" && performance.now
|
|
914
|
+
? performance.now()
|
|
915
|
+
: Date.now();
|
|
916
|
+
setThrottled(value);
|
|
917
|
+
timeoutRef.current = null;
|
|
918
|
+
}, Math.max(0, intervalMs - elapsed));
|
|
919
|
+
}
|
|
920
|
+
return () => { };
|
|
921
|
+
}, [value, intervalMs]);
|
|
922
|
+
React.useEffect(() => {
|
|
923
|
+
return () => {
|
|
924
|
+
if (timeoutRef.current !== null) {
|
|
925
|
+
window.clearTimeout(timeoutRef.current);
|
|
926
|
+
timeoutRef.current = null;
|
|
927
|
+
}
|
|
928
|
+
};
|
|
929
|
+
}, []);
|
|
930
|
+
return throttled;
|
|
931
|
+
}
|
|
845
932
|
// Query param helpers
|
|
846
933
|
function setSearchParam(key, val) {
|
|
847
934
|
if (typeof window === "undefined")
|
|
@@ -1002,7 +1089,13 @@ function summarizeDeep(value) {
|
|
|
1002
1089
|
return value;
|
|
1003
1090
|
}
|
|
1004
1091
|
|
|
1092
|
+
// Shared UI constants for node layout to keep mapping and rendering in sync
|
|
1093
|
+
const NODE_HEADER_HEIGHT_PX = 24;
|
|
1094
|
+
const NODE_ROW_HEIGHT_PX = 22;
|
|
1095
|
+
|
|
1005
1096
|
function toReactFlow(def, positions, registry, opts) {
|
|
1097
|
+
const EDGE_STYLE_ERROR = { stroke: "#ef4444", strokeWidth: 2 };
|
|
1098
|
+
const EDGE_STYLE_RUNNING = { stroke: "#3b82f6" };
|
|
1006
1099
|
const nodeHandleMap = {};
|
|
1007
1100
|
const nodes = def.nodes.map((n) => {
|
|
1008
1101
|
const desc = registry.nodes.get(n.typeId);
|
|
@@ -1014,6 +1107,35 @@ function toReactFlow(def, positions, registry, opts) {
|
|
|
1014
1107
|
inputs: new Set(inputHandles.map((h) => h.id)),
|
|
1015
1108
|
outputs: new Set(outputHandles.map((h) => h.id)),
|
|
1016
1109
|
};
|
|
1110
|
+
// Match DefaultNode sizing heuristics to avoid hidden nodes during re-measure
|
|
1111
|
+
const HEADER_SIZE = NODE_HEADER_HEIGHT_PX;
|
|
1112
|
+
const ROW_SIZE = NODE_ROW_HEIGHT_PX;
|
|
1113
|
+
const maxRows = Math.max(inputHandles.length, outputHandles.length);
|
|
1114
|
+
const initialWidth = opts.showValues ? 320 : 240;
|
|
1115
|
+
const initialHeight = HEADER_SIZE + maxRows * ROW_SIZE;
|
|
1116
|
+
// Precompute handle bounds so edges can render immediately without waiting for measurement
|
|
1117
|
+
const handles = [
|
|
1118
|
+
// Inputs on the left as targets
|
|
1119
|
+
...inputHandles.map((h, i) => ({
|
|
1120
|
+
id: h.id,
|
|
1121
|
+
type: "target",
|
|
1122
|
+
position: react.Position.Left,
|
|
1123
|
+
x: 0,
|
|
1124
|
+
y: HEADER_SIZE + i * ROW_SIZE,
|
|
1125
|
+
width: 1,
|
|
1126
|
+
height: ROW_SIZE + 2,
|
|
1127
|
+
})),
|
|
1128
|
+
// Outputs on the right as sources
|
|
1129
|
+
...outputHandles.map((h, i) => ({
|
|
1130
|
+
id: h.id,
|
|
1131
|
+
type: "source",
|
|
1132
|
+
position: react.Position.Right,
|
|
1133
|
+
x: initialWidth - 1,
|
|
1134
|
+
y: HEADER_SIZE + i * ROW_SIZE,
|
|
1135
|
+
width: 1,
|
|
1136
|
+
height: ROW_SIZE + 2,
|
|
1137
|
+
})),
|
|
1138
|
+
];
|
|
1017
1139
|
return {
|
|
1018
1140
|
id: n.nodeId,
|
|
1019
1141
|
data: {
|
|
@@ -1021,7 +1143,23 @@ function toReactFlow(def, positions, registry, opts) {
|
|
|
1021
1143
|
params: n.params,
|
|
1022
1144
|
inputHandles,
|
|
1023
1145
|
outputHandles,
|
|
1146
|
+
handleLayout: [
|
|
1147
|
+
...inputHandles.map((h, i) => ({
|
|
1148
|
+
id: h.id,
|
|
1149
|
+
type: "target",
|
|
1150
|
+
position: react.Position.Left,
|
|
1151
|
+
y: HEADER_SIZE + i * ROW_SIZE + ROW_SIZE / 2,
|
|
1152
|
+
})),
|
|
1153
|
+
...outputHandles.map((h, i) => ({
|
|
1154
|
+
id: h.id,
|
|
1155
|
+
type: "source",
|
|
1156
|
+
position: react.Position.Right,
|
|
1157
|
+
y: HEADER_SIZE + i * ROW_SIZE + ROW_SIZE / 2,
|
|
1158
|
+
})),
|
|
1159
|
+
],
|
|
1024
1160
|
showValues: opts.showValues,
|
|
1161
|
+
renderWidth: initialWidth,
|
|
1162
|
+
renderHeight: initialHeight,
|
|
1025
1163
|
inputValues: opts.inputs?.[n.nodeId],
|
|
1026
1164
|
outputValues: opts.outputs?.[n.nodeId],
|
|
1027
1165
|
status: opts.nodeStatus?.[n.nodeId],
|
|
@@ -1038,6 +1176,11 @@ function toReactFlow(def, positions, registry, opts) {
|
|
|
1038
1176
|
selected: opts.selectedNodeIds
|
|
1039
1177
|
? opts.selectedNodeIds.has(n.nodeId)
|
|
1040
1178
|
: undefined,
|
|
1179
|
+
initialWidth,
|
|
1180
|
+
initialHeight,
|
|
1181
|
+
handles,
|
|
1182
|
+
width: initialWidth,
|
|
1183
|
+
height: initialHeight,
|
|
1041
1184
|
};
|
|
1042
1185
|
});
|
|
1043
1186
|
const edges = def.edges
|
|
@@ -1054,9 +1197,9 @@ function toReactFlow(def, positions, registry, opts) {
|
|
|
1054
1197
|
const hasError = !!st?.lastError;
|
|
1055
1198
|
const isInvalidEdge = !!opts.edgeValidation?.[e.id];
|
|
1056
1199
|
const style = hasError || isInvalidEdge
|
|
1057
|
-
?
|
|
1200
|
+
? EDGE_STYLE_ERROR
|
|
1058
1201
|
: isRunning
|
|
1059
|
-
?
|
|
1202
|
+
? EDGE_STYLE_RUNNING
|
|
1060
1203
|
: undefined;
|
|
1061
1204
|
return {
|
|
1062
1205
|
id: e.id,
|
|
@@ -1084,17 +1227,35 @@ function getNodeBorderClassNames(args) {
|
|
|
1084
1227
|
const hasValidationWarning = !hasValidationError && issues.length > 0;
|
|
1085
1228
|
const isRunning = !!status.activeRuns;
|
|
1086
1229
|
const isInvalid = !!status.invalidated && !isRunning && !hasError;
|
|
1087
|
-
|
|
1230
|
+
// Keep border width constant to avoid layout reflow on selection toggles
|
|
1231
|
+
const borderWidth = "border";
|
|
1088
1232
|
const borderStyle = isInvalid ? "border-dashed" : "border-solid";
|
|
1089
|
-
const
|
|
1090
|
-
? "
|
|
1233
|
+
const severity = hasError || hasValidationError
|
|
1234
|
+
? "red"
|
|
1091
1235
|
: hasValidationWarning
|
|
1092
|
-
? "
|
|
1236
|
+
? "amber"
|
|
1093
1237
|
: isRunning
|
|
1094
|
-
? "
|
|
1095
|
-
: "
|
|
1096
|
-
const
|
|
1097
|
-
|
|
1238
|
+
? "blue"
|
|
1239
|
+
: "gray";
|
|
1240
|
+
const borderBySeverity = {
|
|
1241
|
+
red: "border-red-500",
|
|
1242
|
+
amber: "border-amber-500",
|
|
1243
|
+
blue: "border-blue-500",
|
|
1244
|
+
gray: "border-gray-500 dark:border-gray-400",
|
|
1245
|
+
};
|
|
1246
|
+
const ringBySeverity = {
|
|
1247
|
+
red: "ring-2 ring-red-300 dark:ring-red-900",
|
|
1248
|
+
amber: "ring-2 ring-amber-300 dark:ring-amber-900",
|
|
1249
|
+
blue: "ring-2 ring-blue-200 dark:ring-blue-900",
|
|
1250
|
+
gray: "ring-2 ring-gray-300 dark:ring-gray-500",
|
|
1251
|
+
};
|
|
1252
|
+
const borderColor = borderBySeverity[severity];
|
|
1253
|
+
const ring = isRunning
|
|
1254
|
+
? ringBySeverity.blue
|
|
1255
|
+
: selected
|
|
1256
|
+
? ringBySeverity[severity === "blue" ? "gray" : severity]
|
|
1257
|
+
: "";
|
|
1258
|
+
return [borderWidth, borderStyle, borderColor, ring].join(" ").trim();
|
|
1098
1259
|
}
|
|
1099
1260
|
|
|
1100
1261
|
const WorkbenchContext = React.createContext(null);
|
|
@@ -1230,6 +1391,7 @@ function WorkbenchProvider({ wb, runner, registry, setRegistry, children, }) {
|
|
|
1230
1391
|
});
|
|
1231
1392
|
wb.setPositions(pos);
|
|
1232
1393
|
}, [wb]);
|
|
1394
|
+
const updateEdgeType = React.useCallback((edgeId, typeId) => wb.updateEdgeType(edgeId, typeId), [wb]);
|
|
1233
1395
|
// Subscribe to runner/workbench events
|
|
1234
1396
|
React.useEffect(() => {
|
|
1235
1397
|
const add = (source, type) => (payload) => setEvents((prev) => {
|
|
@@ -1240,8 +1402,15 @@ function WorkbenchProvider({ wb, runner, registry, setRegistry, children, }) {
|
|
|
1240
1402
|
if (changeType === "moveNode" || changeType === "moveNodes")
|
|
1241
1403
|
return prev;
|
|
1242
1404
|
}
|
|
1405
|
+
const nextNo = prev.length > 0 ? (prev[0]?.no ?? 0) + 1 : 1;
|
|
1243
1406
|
const next = [
|
|
1244
|
-
{
|
|
1407
|
+
{
|
|
1408
|
+
no: nextNo,
|
|
1409
|
+
at: Date.now(),
|
|
1410
|
+
source,
|
|
1411
|
+
type,
|
|
1412
|
+
payload: structuredClone(payload),
|
|
1413
|
+
},
|
|
1245
1414
|
...prev,
|
|
1246
1415
|
];
|
|
1247
1416
|
return next.length > 200 ? next.slice(0, 200) : next;
|
|
@@ -1556,6 +1725,7 @@ function WorkbenchProvider({ wb, runner, registry, setRegistry, children, }) {
|
|
|
1556
1725
|
step,
|
|
1557
1726
|
flush,
|
|
1558
1727
|
runAutoLayout,
|
|
1728
|
+
updateEdgeType,
|
|
1559
1729
|
}), [
|
|
1560
1730
|
wb,
|
|
1561
1731
|
runner,
|
|
@@ -1582,13 +1752,14 @@ function WorkbenchProvider({ wb, runner, registry, setRegistry, children, }) {
|
|
|
1582
1752
|
step,
|
|
1583
1753
|
flush,
|
|
1584
1754
|
runAutoLayout,
|
|
1755
|
+
wb,
|
|
1585
1756
|
]);
|
|
1586
1757
|
return (jsxRuntime.jsx(WorkbenchContext.Provider, { value: value, children: children }));
|
|
1587
1758
|
}
|
|
1588
1759
|
|
|
1589
1760
|
function IssueBadge({ level, title, size = 12, className, }) {
|
|
1590
1761
|
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" })) }));
|
|
1762
|
+
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
1763
|
}
|
|
1593
1764
|
|
|
1594
1765
|
function DebugEvents({ autoScroll, onAutoScrollChange, hideWorkbench, onHideWorkbenchChange, }) {
|
|
@@ -1617,7 +1788,7 @@ function DebugEvents({ autoScroll, onAutoScrollChange, hideWorkbench, onHideWork
|
|
|
1617
1788
|
return String(v);
|
|
1618
1789
|
}
|
|
1619
1790
|
};
|
|
1620
|
-
return (jsxRuntime.jsxs("div", { className: "flex flex-col h-full min-h-0", children: [jsxRuntime.jsxs("div", { className: "flex items-center justify-between mb-1", children: [jsxRuntime.jsx("div", { className: "font-semibold", children: "Events" }), jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [jsxRuntime.jsxs("label", { className: "flex items-center gap-1 text-xs text-gray-700", children: [jsxRuntime.jsx("input", { type: "checkbox", checked: hideWorkbench, onChange: (e) => onHideWorkbenchChange?.(e.target.checked) }), jsxRuntime.jsx("span", { children: "Hide workbench" })] }), jsxRuntime.jsxs("label", { className: "flex items-center gap-1 text-xs text-gray-700", children: [jsxRuntime.jsx("input", { type: "checkbox", checked: autoScroll, onChange: (e) => onAutoScrollChange?.(e.target.checked) }), jsxRuntime.jsx("span", { children: "Auto scroll" })] }), jsxRuntime.jsx("button", { onClick: clearEvents, className: "text-xs px-2 py-0.5 border border-gray-300 rounded", children: "Clear" })] })] }), jsxRuntime.jsx("div", { ref: scrollRef, className: "flex-1 overflow-auto text-[11px] leading-4 divide-y divide-gray-200", children: rows.map((ev
|
|
1791
|
+
return (jsxRuntime.jsxs("div", { className: "flex flex-col h-full min-h-0", children: [jsxRuntime.jsxs("div", { className: "flex items-center justify-between mb-1", children: [jsxRuntime.jsx("div", { className: "font-semibold", children: "Events" }), jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [jsxRuntime.jsxs("label", { className: "flex items-center gap-1 text-xs text-gray-700", children: [jsxRuntime.jsx("input", { type: "checkbox", checked: hideWorkbench, onChange: (e) => onHideWorkbenchChange?.(e.target.checked) }), jsxRuntime.jsx("span", { children: "Hide workbench" })] }), jsxRuntime.jsxs("label", { className: "flex items-center gap-1 text-xs text-gray-700", children: [jsxRuntime.jsx("input", { type: "checkbox", checked: autoScroll, onChange: (e) => onAutoScrollChange?.(e.target.checked) }), jsxRuntime.jsx("span", { children: "Auto scroll" })] }), jsxRuntime.jsx("button", { onClick: clearEvents, className: "text-xs px-2 py-0.5 border border-gray-300 rounded", children: "Clear" })] })] }), jsxRuntime.jsx("div", { ref: scrollRef, className: "flex-1 overflow-auto text-[11px] leading-4 divide-y divide-gray-200", children: rows.map((ev) => (jsxRuntime.jsxs("div", { className: "opacity-85 odd:bg-gray-50 px-2 py-1", children: [jsxRuntime.jsxs("div", { className: "flex items-baseline gap-2", children: [jsxRuntime.jsx("span", { className: "w-12 shrink-0 text-right text-gray-500 select-none", children: ev.no }), jsxRuntime.jsxs("span", { className: "text-gray-500", children: [new Date(ev.at).toLocaleTimeString(), " \u00B7 ", ev.source, ":", ev.type] })] }), jsxRuntime.jsx("pre", { className: "m-0 whitespace-pre-wrap ml-12", children: renderPayload(ev.payload) })] }, `${ev.at}:${ev.no}`))) })] }));
|
|
1621
1792
|
}
|
|
1622
1793
|
|
|
1623
1794
|
function Inspector({ debug, autoScroll, hideWorkbench, onAutoScrollChange, onHideWorkbenchChange, toString, toElement, contextPanel, setInput, }) {
|
|
@@ -1632,7 +1803,7 @@ function Inspector({ debug, autoScroll, hideWorkbench, onAutoScrollChange, onHid
|
|
|
1632
1803
|
return String(value ?? "");
|
|
1633
1804
|
}
|
|
1634
1805
|
};
|
|
1635
|
-
const { registry, def, selectedNodeId, selectedEdgeId, inputsMap, outputsMap, outputTypesMap, nodeStatus, validationByNode, validationByEdge, validationGlobal, valuesTick, } = useWorkbenchContext();
|
|
1806
|
+
const { registry, def, selectedNodeId, selectedEdgeId, inputsMap, outputsMap, outputTypesMap, nodeStatus, validationByNode, validationByEdge, validationGlobal, valuesTick, updateEdgeType, } = useWorkbenchContext();
|
|
1636
1807
|
const nodeValidationIssues = validationByNode.issues;
|
|
1637
1808
|
const edgeValidationIssues = validationByEdge.issues;
|
|
1638
1809
|
const nodeValidationHandles = validationByNode;
|
|
@@ -1709,7 +1880,11 @@ function Inspector({ debug, autoScroll, hideWorkbench, onAutoScrollChange, onHid
|
|
|
1709
1880
|
setOriginals(nextOriginals);
|
|
1710
1881
|
}, [selectedNodeId, selectedDesc, valuesTick]);
|
|
1711
1882
|
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 &&
|
|
1883
|
+
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", { className: "flex items-center gap-2 mt-1", children: [jsxRuntime.jsxs("label", { className: "w-20 flex flex-col", children: [jsxRuntime.jsx("span", { children: "Type" }), jsxRuntime.jsx("span", { className: "text-gray-500 text-[11px]", children: "DataTypeId" })] }), jsxRuntime.jsxs("select", { className: "border border-gray-300 rounded px-2 py-1 focus:outline-none focus:ring-2 focus:ring-blue-500 w-full", value: selectedEdge.typeId ?? "", onChange: (e) => {
|
|
1884
|
+
const v = e.target.value;
|
|
1885
|
+
const next = v === "" ? undefined : v;
|
|
1886
|
+
updateEdgeType(selectedEdge.id, next);
|
|
1887
|
+
}, children: [jsxRuntime.jsx("option", { value: "", children: "(infer from source)" }), Array.from(registry.types.keys()).map((tid) => (jsxRuntime.jsx("option", { value: tid, children: tid }, tid)))] })] })] }), 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
1888
|
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
1889
|
const typeId = sparkGraph.getInputTypeId(selectedDesc?.inputs, h);
|
|
1715
1890
|
const isLinked = def.edges.some((e) => e.target.nodeId === selectedNodeId &&
|
|
@@ -1770,79 +1945,120 @@ function Inspector({ debug, autoScroll, hideWorkbench, onAutoScrollChange, onHid
|
|
|
1770
1945
|
})()] }, 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
1946
|
}
|
|
1772
1947
|
|
|
1948
|
+
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", }) {
|
|
1949
|
+
const layout = data.handleLayout ?? [];
|
|
1950
|
+
const byId = React.useMemo(() => {
|
|
1951
|
+
const m = new Map();
|
|
1952
|
+
for (const h of layout) {
|
|
1953
|
+
m.set(h.id, { position: h.position, y: h.y, type: h.type });
|
|
1954
|
+
}
|
|
1955
|
+
return m;
|
|
1956
|
+
}, [layout]);
|
|
1957
|
+
return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [(data.inputHandles ?? []).map((h) => {
|
|
1958
|
+
const placed = byId.get(h.id);
|
|
1959
|
+
const position = placed?.position ?? react.Position.Left;
|
|
1960
|
+
const y = placed?.y;
|
|
1961
|
+
const cls = getClassName?.({ kind: "input", id: h.id, type: "target" }) ??
|
|
1962
|
+
inputClassName;
|
|
1963
|
+
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: {
|
|
1964
|
+
top: (y ?? 0) - 8,
|
|
1965
|
+
right: "50%",
|
|
1966
|
+
whiteSpace: "nowrap",
|
|
1967
|
+
overflow: "hidden",
|
|
1968
|
+
textOverflow: "ellipsis",
|
|
1969
|
+
}, children: renderLabel({ kind: "input", id: h.id }) }))] }, h.id));
|
|
1970
|
+
}), (data.outputHandles ?? []).map((h) => {
|
|
1971
|
+
const placed = byId.get(h.id);
|
|
1972
|
+
const position = placed?.position ?? react.Position.Right;
|
|
1973
|
+
const y = placed?.y;
|
|
1974
|
+
const cls = getClassName?.({ kind: "output", id: h.id, type: "source" }) ??
|
|
1975
|
+
outputClassName;
|
|
1976
|
+
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: {
|
|
1977
|
+
top: (y ?? 0) - 8,
|
|
1978
|
+
left: "50%",
|
|
1979
|
+
textAlign: "right",
|
|
1980
|
+
whiteSpace: "nowrap",
|
|
1981
|
+
overflow: "hidden",
|
|
1982
|
+
textOverflow: "ellipsis",
|
|
1983
|
+
}, children: renderLabel({ kind: "output", id: h.id }) }))] }, h.id));
|
|
1984
|
+
})] }));
|
|
1985
|
+
}
|
|
1986
|
+
|
|
1773
1987
|
const DefaultNode = React.memo(function DefaultNode({ id, data, selected, isConnectable, }) {
|
|
1988
|
+
const updateNodeInternals = react.useUpdateNodeInternals();
|
|
1774
1989
|
const { typeId, showValues, inputValues, outputValues, toString } = data;
|
|
1775
1990
|
const inputEntries = data.inputHandles ?? [];
|
|
1776
1991
|
const outputEntries = data.outputHandles ?? [];
|
|
1992
|
+
React.useEffect(() => {
|
|
1993
|
+
updateNodeInternals(id);
|
|
1994
|
+
}, [
|
|
1995
|
+
id,
|
|
1996
|
+
inputEntries.length,
|
|
1997
|
+
outputEntries.length,
|
|
1998
|
+
showValues,
|
|
1999
|
+
updateNodeInternals,
|
|
2000
|
+
]);
|
|
1777
2001
|
const status = data.status ?? { activeRuns: 0 };
|
|
1778
2002
|
const validation = data.validation ?? {
|
|
1779
2003
|
inputs: [],
|
|
1780
2004
|
outputs: [],
|
|
1781
2005
|
issues: [],
|
|
1782
2006
|
};
|
|
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
2007
|
const hasError = !!status.lastError;
|
|
1790
|
-
const hasValidationError = validation.issues.some((i) => i.level === "error");
|
|
1791
|
-
const hasValidationWarning = !hasValidationError && validation.issues.length > 0;
|
|
1792
2008
|
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);
|
|
2009
|
+
const containerBorder = getNodeBorderClassNames({
|
|
2010
|
+
selected,
|
|
2011
|
+
status,
|
|
2012
|
+
validation,
|
|
2013
|
+
});
|
|
1808
2014
|
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",
|
|
2015
|
+
return (jsxRuntime.jsxs("div", { className: cx("rounded-lg bg-white/70 !dark:bg-stone-900", containerBorder), style: {
|
|
2016
|
+
position: "relative",
|
|
2017
|
+
minWidth: typeof data.renderWidth === "number" ? data.renderWidth : undefined,
|
|
2018
|
+
minHeight: typeof data.renderHeight === "number" ? data.renderHeight : undefined,
|
|
2019
|
+
}, 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: {
|
|
2020
|
+
maxHeight: NODE_HEADER_HEIGHT_PX,
|
|
2021
|
+
minHeight: NODE_HEADER_HEIGHT_PX,
|
|
2022
|
+
}, 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
2023
|
? "error"
|
|
1811
2024
|
: "warning", size: 12, className: "w-3 h-3", title: validation.issues
|
|
1812
2025
|
.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
|
-
|
|
2026
|
+
.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 }) => {
|
|
2027
|
+
const vIssues = (kind === "input" ? validation.inputs : validation.outputs).filter((v) => v.handle === id);
|
|
2028
|
+
const hasAny = vIssues.length > 0;
|
|
2029
|
+
const hasErr = vIssues.some((v) => v.level === "error");
|
|
2030
|
+
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"));
|
|
2031
|
+
}, renderLabel: ({ kind, id }) => {
|
|
2032
|
+
const entries = kind === "input" ? inputEntries : outputEntries;
|
|
2033
|
+
const entry = entries.find((e) => e.id === id);
|
|
2034
|
+
if (!entry)
|
|
2035
|
+
return id;
|
|
2036
|
+
const vIssues = (kind === "input" ? validation.inputs : validation.outputs).filter((v) => v.handle === id);
|
|
2037
|
+
const hasAny = vIssues.length > 0;
|
|
2038
|
+
const hasErr = vIssues.some((v) => v.level === "error");
|
|
2039
|
+
const title = vIssues
|
|
2040
|
+
.map((v) => `${v.code}: ${v.message}`)
|
|
2041
|
+
.join("; ");
|
|
2042
|
+
// Compose label with truncated value to prevent layout growth
|
|
2043
|
+
const valueText = (() => {
|
|
2044
|
+
if (!showValues)
|
|
2045
|
+
return undefined;
|
|
2046
|
+
if (kind === "input") {
|
|
2047
|
+
const txt = toString(entry.typeId, inputValues?.[entry.id]);
|
|
2048
|
+
return typeof txt === "string" ? txt : String(txt);
|
|
2049
|
+
}
|
|
2050
|
+
const resolved = resolveOutputDisplay(outputValues?.[entry.id], entry.typeId);
|
|
2051
|
+
const txt = toString(resolved.typeId, resolved.value);
|
|
2052
|
+
return typeof txt === "string" ? txt : String(txt);
|
|
2053
|
+
})();
|
|
2054
|
+
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 }))] }));
|
|
2055
|
+
} })] }));
|
|
1840
2056
|
});
|
|
1841
2057
|
DefaultNode.displayName = "DefaultNode";
|
|
1842
2058
|
|
|
1843
2059
|
function DefaultContextMenu({ open, clientPos, onAdd, onClose, }) {
|
|
1844
2060
|
const { registry } = useWorkbenchContext();
|
|
1845
|
-
const rf = react
|
|
2061
|
+
const rf = react.useReactFlow();
|
|
1846
2062
|
const ids = Array.from(registry.nodes.keys());
|
|
1847
2063
|
const [query, setQuery] = React.useState("");
|
|
1848
2064
|
const q = query.trim().toLowerCase();
|
|
@@ -2001,6 +2217,58 @@ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement }, r
|
|
|
2001
2217
|
const { wb, registry, inputsMap, outputsMap, valuesTick, nodeStatus, edgeStatus, validationByNode, validationByEdge, } = useWorkbenchContext();
|
|
2002
2218
|
const nodeValidation = validationByNode;
|
|
2003
2219
|
const edgeValidation = validationByEdge.errors;
|
|
2220
|
+
// Keep stable references for nodes/edges to avoid unnecessary updates
|
|
2221
|
+
const prevNodesRef = React.useRef([]);
|
|
2222
|
+
const prevEdgesRef = React.useRef([]);
|
|
2223
|
+
function retainStabilityById(prev, next, isSame) {
|
|
2224
|
+
if (prev.length === 0)
|
|
2225
|
+
return next;
|
|
2226
|
+
const map = new Map();
|
|
2227
|
+
for (const p of prev)
|
|
2228
|
+
map.set(p.id, p);
|
|
2229
|
+
const out = new Array(next.length);
|
|
2230
|
+
for (let i = 0; i < next.length; i++) {
|
|
2231
|
+
const n = next[i];
|
|
2232
|
+
const p = map.get(n.id);
|
|
2233
|
+
out[i] = p && isSame(p, n) ? p : n;
|
|
2234
|
+
}
|
|
2235
|
+
return out;
|
|
2236
|
+
}
|
|
2237
|
+
const isSameNode = (a, b) => {
|
|
2238
|
+
// Compare the parts that affect rendering
|
|
2239
|
+
const pick = (n) => ({
|
|
2240
|
+
position: n.position,
|
|
2241
|
+
type: n.type,
|
|
2242
|
+
selected: n.selected,
|
|
2243
|
+
initialWidth: n.initialWidth,
|
|
2244
|
+
initialHeight: n.initialHeight,
|
|
2245
|
+
data: n.data && {
|
|
2246
|
+
typeId: n.data.typeId,
|
|
2247
|
+
inputHandles: n.data.inputHandles,
|
|
2248
|
+
outputHandles: n.data.outputHandles,
|
|
2249
|
+
showValues: n.data.showValues,
|
|
2250
|
+
inputValues: n.data.inputValues,
|
|
2251
|
+
outputValues: n.data.outputValues,
|
|
2252
|
+
status: n.data.status,
|
|
2253
|
+
validation: n.data.validation,
|
|
2254
|
+
},
|
|
2255
|
+
});
|
|
2256
|
+
return isEqual(pick(a), pick(b));
|
|
2257
|
+
};
|
|
2258
|
+
const isSameEdge = (a, b) => {
|
|
2259
|
+
const pick = (e) => ({
|
|
2260
|
+
source: e.source,
|
|
2261
|
+
target: e.target,
|
|
2262
|
+
sourceHandle: e.sourceHandle,
|
|
2263
|
+
targetHandle: e.targetHandle,
|
|
2264
|
+
selected: e.selected,
|
|
2265
|
+
animated: e.animated,
|
|
2266
|
+
style: e.style,
|
|
2267
|
+
label: e.label,
|
|
2268
|
+
type: e.type,
|
|
2269
|
+
});
|
|
2270
|
+
return isEqual(pick(a), pick(b));
|
|
2271
|
+
};
|
|
2004
2272
|
// Expose imperative API
|
|
2005
2273
|
const rfInstanceRef = React.useRef(null);
|
|
2006
2274
|
React.useImperativeHandle(ref, () => ({
|
|
@@ -2011,7 +2279,7 @@ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement }, r
|
|
|
2011
2279
|
catch { }
|
|
2012
2280
|
},
|
|
2013
2281
|
}));
|
|
2014
|
-
const { onConnect, onNodesChange, onEdgesChange, onEdgesDelete, onNodesDelete,
|
|
2282
|
+
const { onConnect, onNodesChange, onEdgesChange, onEdgesDelete, onNodesDelete, } = useWorkbenchBridge(wb);
|
|
2015
2283
|
const { nodeTypes, resolveNodeType } = React.useMemo(() => {
|
|
2016
2284
|
// Build nodeTypes map using UI extension registry
|
|
2017
2285
|
const ui = wb.getUI();
|
|
@@ -2049,8 +2317,91 @@ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement }, r
|
|
|
2049
2317
|
selectedNodeIds: new Set(sel.nodes),
|
|
2050
2318
|
selectedEdgeIds: new Set(sel.edges),
|
|
2051
2319
|
});
|
|
2052
|
-
//
|
|
2053
|
-
|
|
2320
|
+
// Retain references for unchanged items
|
|
2321
|
+
const stableNodes = retainStabilityById(prevNodesRef.current, out.nodes, isSameNode);
|
|
2322
|
+
const stableEdges = retainStabilityById(prevEdgesRef.current, out.edges, isSameEdge);
|
|
2323
|
+
// Debug: log updates/additions/removals (use value equality, not reference)
|
|
2324
|
+
try {
|
|
2325
|
+
const prevNodeIds = new Set(prevNodesRef.current.map((n) => n.id));
|
|
2326
|
+
const nextNodeIds = new Set(out.nodes.map((n) => n.id));
|
|
2327
|
+
const addedNodeIds = out.nodes
|
|
2328
|
+
.filter((n) => !prevNodeIds.has(n.id))
|
|
2329
|
+
.map((n) => n.id);
|
|
2330
|
+
const removedNodeIds = prevNodesRef.current
|
|
2331
|
+
.filter((n) => !nextNodeIds.has(n.id))
|
|
2332
|
+
.map((n) => n.id);
|
|
2333
|
+
const prevNodeMap = new Map(prevNodesRef.current.map((n) => [n.id, n]));
|
|
2334
|
+
const changedNodeIds = out.nodes
|
|
2335
|
+
.filter((n) => {
|
|
2336
|
+
const p = prevNodeMap.get(n.id);
|
|
2337
|
+
return p ? !isSameNode(p, n) : false;
|
|
2338
|
+
})
|
|
2339
|
+
.map((n) => n.id);
|
|
2340
|
+
// Detect handle updates (ids/length changes) for targeted debug
|
|
2341
|
+
const toIds = (arr) => Array.isArray(arr) ? arr.map((h) => h?.id) : [];
|
|
2342
|
+
const handlesEqual = (a, b) => {
|
|
2343
|
+
const aIds = toIds(a);
|
|
2344
|
+
const bIds = toIds(b);
|
|
2345
|
+
if (aIds.length !== bIds.length)
|
|
2346
|
+
return false;
|
|
2347
|
+
for (let i = 0; i < aIds.length; i++) {
|
|
2348
|
+
if (aIds[i] !== bIds[i])
|
|
2349
|
+
return false;
|
|
2350
|
+
}
|
|
2351
|
+
return true;
|
|
2352
|
+
};
|
|
2353
|
+
const handleChanged = out.nodes
|
|
2354
|
+
.filter((n) => {
|
|
2355
|
+
const p = prevNodeMap.get(n.id);
|
|
2356
|
+
if (!p)
|
|
2357
|
+
return false;
|
|
2358
|
+
const inChanged = !handlesEqual(p.data?.inputHandles, n.data?.inputHandles);
|
|
2359
|
+
const outChanged = !handlesEqual(p.data?.outputHandles, n.data?.outputHandles);
|
|
2360
|
+
return inChanged || outChanged;
|
|
2361
|
+
})
|
|
2362
|
+
.map((n) => n.id);
|
|
2363
|
+
const prevEdgeIds = new Set(prevEdgesRef.current.map((e) => e.id));
|
|
2364
|
+
const nextEdgeIds = new Set(out.edges.map((e) => e.id));
|
|
2365
|
+
const addedEdgeIds = out.edges
|
|
2366
|
+
.filter((e) => !prevEdgeIds.has(e.id))
|
|
2367
|
+
.map((e) => e.id);
|
|
2368
|
+
const removedEdgeIds = prevEdgesRef.current
|
|
2369
|
+
.filter((e) => !nextEdgeIds.has(e.id))
|
|
2370
|
+
.map((e) => e.id);
|
|
2371
|
+
const prevEdgeMap = new Map(prevEdgesRef.current.map((e) => [e.id, e]));
|
|
2372
|
+
const changedEdgeIds = out.edges
|
|
2373
|
+
.filter((e) => {
|
|
2374
|
+
const p = prevEdgeMap.get(e.id);
|
|
2375
|
+
return p ? !isSameEdge(p, e) : false;
|
|
2376
|
+
})
|
|
2377
|
+
.map((e) => e.id);
|
|
2378
|
+
if (addedNodeIds.length ||
|
|
2379
|
+
removedNodeIds.length ||
|
|
2380
|
+
changedNodeIds.length ||
|
|
2381
|
+
handleChanged.length) {
|
|
2382
|
+
// eslint-disable-next-line no-console
|
|
2383
|
+
console.debug("[WorkbenchCanvas] node updates", {
|
|
2384
|
+
added: addedNodeIds,
|
|
2385
|
+
removed: removedNodeIds,
|
|
2386
|
+
changed: changedNodeIds,
|
|
2387
|
+
handleChanged,
|
|
2388
|
+
});
|
|
2389
|
+
}
|
|
2390
|
+
if (addedEdgeIds.length ||
|
|
2391
|
+
removedEdgeIds.length ||
|
|
2392
|
+
changedEdgeIds.length) {
|
|
2393
|
+
// eslint-disable-next-line no-console
|
|
2394
|
+
console.debug("[WorkbenchCanvas] edge updates", {
|
|
2395
|
+
added: addedEdgeIds,
|
|
2396
|
+
removed: removedEdgeIds,
|
|
2397
|
+
changed: changedEdgeIds,
|
|
2398
|
+
});
|
|
2399
|
+
}
|
|
2400
|
+
}
|
|
2401
|
+
catch { }
|
|
2402
|
+
prevNodesRef.current = stableNodes;
|
|
2403
|
+
prevEdgesRef.current = stableEdges;
|
|
2404
|
+
return { nodes: stableNodes, edges: stableEdges };
|
|
2054
2405
|
}, [
|
|
2055
2406
|
showValues,
|
|
2056
2407
|
inputsMap,
|
|
@@ -2062,9 +2413,9 @@ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement }, r
|
|
|
2062
2413
|
edgeStatus,
|
|
2063
2414
|
nodeValidation,
|
|
2064
2415
|
edgeValidation,
|
|
2065
|
-
nodeTypes,
|
|
2066
2416
|
resolveNodeType,
|
|
2067
2417
|
]);
|
|
2418
|
+
const throttled = useThrottledValue({ nodes, edges }, 100);
|
|
2068
2419
|
const [menuOpen, setMenuOpen] = React.useState(false);
|
|
2069
2420
|
const [menuPos, setMenuPos] = React.useState(null);
|
|
2070
2421
|
const [nodeMenuOpen, setNodeMenuOpen] = React.useState(false);
|
|
@@ -2091,7 +2442,7 @@ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement }, r
|
|
|
2091
2442
|
const addNodeAt = (typeId, pos) => {
|
|
2092
2443
|
wb.addNode({ typeId, position: pos });
|
|
2093
2444
|
};
|
|
2094
|
-
return (jsxRuntime.jsx("div", { className: "w-full h-full", onContextMenu: onContextMenu, children: jsxRuntime.jsxs(react
|
|
2445
|
+
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) })] }) }));
|
|
2095
2446
|
});
|
|
2096
2447
|
|
|
2097
2448
|
function WorkbenchStudioCanvas({ setRegistry, autoScroll, onAutoScrollChange, example, onExampleChange, engine, onEngineChange, backendKind, onBackendKindChange, httpBaseUrl, onHttpBaseUrlChange, wsUrl, onWsUrlChange, debug, onDebugChange, showValues, onShowValuesChange, hideWorkbench, onHideWorkbenchChange, overrides, onInit, onChange, }) {
|
|
@@ -2203,9 +2554,14 @@ function WorkbenchStudioCanvas({ setRegistry, autoScroll, onAutoScrollChange, ex
|
|
|
2203
2554
|
if (!ex)
|
|
2204
2555
|
return;
|
|
2205
2556
|
const { registry: r, def } = await ex.load();
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2557
|
+
// Keep registry consistent with backend:
|
|
2558
|
+
// - For local backend, allow example to provide its own registry
|
|
2559
|
+
// - For remote backend, NEVER overwrite the hydrated remote registry
|
|
2560
|
+
if (backendKind === "local") {
|
|
2561
|
+
if (r) {
|
|
2562
|
+
setRegistry(r);
|
|
2563
|
+
wb.setRegistry(r);
|
|
2564
|
+
}
|
|
2209
2565
|
}
|
|
2210
2566
|
await wb.load(def);
|
|
2211
2567
|
// Build a local runtime so seeded defaults are visible pre-run
|
|
@@ -2213,7 +2569,15 @@ function WorkbenchStudioCanvas({ setRegistry, autoScroll, onAutoScrollChange, ex
|
|
|
2213
2569
|
runAutoLayout();
|
|
2214
2570
|
setExampleState(key);
|
|
2215
2571
|
onExampleChange?.(key);
|
|
2216
|
-
}, [
|
|
2572
|
+
}, [
|
|
2573
|
+
runner,
|
|
2574
|
+
wb,
|
|
2575
|
+
onExampleChange,
|
|
2576
|
+
runAutoLayout,
|
|
2577
|
+
examples,
|
|
2578
|
+
setRegistry,
|
|
2579
|
+
backendKind,
|
|
2580
|
+
]);
|
|
2217
2581
|
const downloadGraph = React.useCallback(() => {
|
|
2218
2582
|
try {
|
|
2219
2583
|
const def = wb.export();
|
|
@@ -2515,11 +2879,11 @@ function WorkbenchStudioCanvas({ setRegistry, autoScroll, onAutoScrollChange, ex
|
|
|
2515
2879
|
return overrides.toElement(baseToElement, { registry });
|
|
2516
2880
|
return baseToElement;
|
|
2517
2881
|
}, [overrides, baseToElement, registry]);
|
|
2518
|
-
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()
|
|
2882
|
+
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()
|
|
2519
2883
|
? "Stop engine before switching example"
|
|
2520
2884
|
: 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()
|
|
2521
2885
|
? "Stop engine before switching backend"
|
|
2522
|
-
: 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) => {
|
|
2886
|
+
: 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) => {
|
|
2523
2887
|
const kind = e.target.value || undefined;
|
|
2524
2888
|
onEngineChange?.(kind);
|
|
2525
2889
|
}, 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: () => {
|
|
@@ -2568,6 +2932,7 @@ exports.DefaultUIExtensionRegistry = DefaultUIExtensionRegistry;
|
|
|
2568
2932
|
exports.InMemoryWorkbench = InMemoryWorkbench;
|
|
2569
2933
|
exports.Inspector = Inspector;
|
|
2570
2934
|
exports.LocalGraphRunner = LocalGraphRunner;
|
|
2935
|
+
exports.NodeHandles = NodeHandles;
|
|
2571
2936
|
exports.RemoteGraphRunner = RemoteGraphRunner;
|
|
2572
2937
|
exports.WorkbenchCanvas = WorkbenchCanvas;
|
|
2573
2938
|
exports.WorkbenchContext = WorkbenchContext;
|
|
@@ -2582,6 +2947,7 @@ exports.summarizeDeep = summarizeDeep;
|
|
|
2582
2947
|
exports.toReactFlow = toReactFlow;
|
|
2583
2948
|
exports.useQueryParamBoolean = useQueryParamBoolean;
|
|
2584
2949
|
exports.useQueryParamString = useQueryParamString;
|
|
2950
|
+
exports.useThrottledValue = useThrottledValue;
|
|
2585
2951
|
exports.useWorkbenchBridge = useWorkbenchBridge;
|
|
2586
2952
|
exports.useWorkbenchContext = useWorkbenchContext;
|
|
2587
2953
|
exports.useWorkbenchGraphTick = useWorkbenchGraphTick;
|