@bian-womp/spark-workbench 0.2.65 → 0.2.67
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 +500 -215
- package/lib/cjs/index.cjs.map +1 -1
- package/lib/cjs/src/core/InMemoryWorkbench.d.ts +13 -0
- package/lib/cjs/src/core/InMemoryWorkbench.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/DefaultContextMenu.d.ts.map +1 -1
- package/lib/cjs/src/misc/NodeContextMenu.d.ts.map +1 -1
- package/lib/cjs/src/misc/SelectionContextMenu.d.ts +3 -0
- package/lib/cjs/src/misc/SelectionContextMenu.d.ts.map +1 -0
- package/lib/cjs/src/misc/WorkbenchCanvas.d.ts.map +1 -1
- package/lib/cjs/src/misc/context/ContextMenuHandlers.d.ts +18 -0
- package/lib/cjs/src/misc/context/ContextMenuHandlers.d.ts.map +1 -1
- package/lib/cjs/src/misc/context/ContextMenuHelpers.d.ts +39 -2
- package/lib/cjs/src/misc/context/ContextMenuHelpers.d.ts.map +1 -1
- package/lib/cjs/src/misc/context/WorkbenchContext.d.ts +46 -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/esm/index.js +495 -216
- package/lib/esm/index.js.map +1 -1
- package/lib/esm/src/core/InMemoryWorkbench.d.ts +13 -0
- package/lib/esm/src/core/InMemoryWorkbench.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/DefaultContextMenu.d.ts.map +1 -1
- package/lib/esm/src/misc/NodeContextMenu.d.ts.map +1 -1
- package/lib/esm/src/misc/SelectionContextMenu.d.ts +3 -0
- package/lib/esm/src/misc/SelectionContextMenu.d.ts.map +1 -0
- package/lib/esm/src/misc/WorkbenchCanvas.d.ts.map +1 -1
- package/lib/esm/src/misc/context/ContextMenuHandlers.d.ts +18 -0
- package/lib/esm/src/misc/context/ContextMenuHandlers.d.ts.map +1 -1
- package/lib/esm/src/misc/context/ContextMenuHelpers.d.ts +39 -2
- package/lib/esm/src/misc/context/ContextMenuHelpers.d.ts.map +1 -1
- package/lib/esm/src/misc/context/WorkbenchContext.d.ts +46 -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/package.json +4 -4
package/lib/esm/index.js
CHANGED
|
@@ -129,6 +129,7 @@ class InMemoryWorkbench extends AbstractWorkbench {
|
|
|
129
129
|
edges: [],
|
|
130
130
|
};
|
|
131
131
|
this.viewport = null;
|
|
132
|
+
this.copiedData = null;
|
|
132
133
|
}
|
|
133
134
|
setRegistry(registry) {
|
|
134
135
|
this.registry = registry;
|
|
@@ -305,6 +306,22 @@ class InMemoryWorkbench extends AbstractWorkbench {
|
|
|
305
306
|
edges: [...this.selection.edges],
|
|
306
307
|
};
|
|
307
308
|
}
|
|
309
|
+
/**
|
|
310
|
+
* Delete all selected nodes and edges.
|
|
311
|
+
*/
|
|
312
|
+
deleteSelection() {
|
|
313
|
+
const selection = this.getSelection();
|
|
314
|
+
// Delete all selected nodes (this will also remove connected edges)
|
|
315
|
+
for (const nodeId of selection.nodes) {
|
|
316
|
+
this.removeNode(nodeId);
|
|
317
|
+
}
|
|
318
|
+
// Delete remaining selected edges (edges not connected to deleted nodes)
|
|
319
|
+
for (const edgeId of selection.edges) {
|
|
320
|
+
this.disconnect(edgeId);
|
|
321
|
+
}
|
|
322
|
+
// Clear selection
|
|
323
|
+
this.setSelection({ nodes: [], edges: [] });
|
|
324
|
+
}
|
|
308
325
|
setViewport(viewport, opts) {
|
|
309
326
|
this.viewport = { ...viewport };
|
|
310
327
|
this.emit("graphUiChanged", {
|
|
@@ -501,6 +518,18 @@ class InMemoryWorkbench extends AbstractWorkbench {
|
|
|
501
518
|
});
|
|
502
519
|
return { nodeIdMap, edgeIds };
|
|
503
520
|
}
|
|
521
|
+
/**
|
|
522
|
+
* Get the currently copied graph data.
|
|
523
|
+
*/
|
|
524
|
+
getCopiedData() {
|
|
525
|
+
return this.copiedData;
|
|
526
|
+
}
|
|
527
|
+
/**
|
|
528
|
+
* Set the copied graph data (used for paste functionality).
|
|
529
|
+
*/
|
|
530
|
+
setCopiedData(data) {
|
|
531
|
+
this.copiedData = data;
|
|
532
|
+
}
|
|
504
533
|
}
|
|
505
534
|
|
|
506
535
|
class CLIWorkbench {
|
|
@@ -3047,6 +3076,7 @@ function WorkbenchProvider({ wb, runner, registry, setRegistry, overrides, uiVer
|
|
|
3047
3076
|
updateEdgeType,
|
|
3048
3077
|
triggerExternal,
|
|
3049
3078
|
uiVersion,
|
|
3079
|
+
overrides,
|
|
3050
3080
|
}), [
|
|
3051
3081
|
wb,
|
|
3052
3082
|
runner,
|
|
@@ -3086,10 +3116,272 @@ function WorkbenchProvider({ wb, runner, registry, setRegistry, overrides, uiVer
|
|
|
3086
3116
|
wb,
|
|
3087
3117
|
runner,
|
|
3088
3118
|
uiVersion,
|
|
3119
|
+
overrides,
|
|
3089
3120
|
]);
|
|
3090
3121
|
return (jsx(WorkbenchContext.Provider, { value: value, children: children }));
|
|
3091
3122
|
}
|
|
3092
3123
|
|
|
3124
|
+
function createNodeContextMenuHandlers(nodeId, wb, runner, registry, outputsMap, outputTypesMap, onClose, getDefaultNodeSize, onCopyResult) {
|
|
3125
|
+
const doBake = async (handleId) => {
|
|
3126
|
+
try {
|
|
3127
|
+
const typeId = outputTypesMap?.[nodeId]?.[handleId];
|
|
3128
|
+
const raw = outputsMap?.[nodeId]?.[handleId];
|
|
3129
|
+
if (!typeId || raw === undefined)
|
|
3130
|
+
return;
|
|
3131
|
+
const unwrap = (v) => isTypedOutput(v) ? getTypedOutputValue(v) : v;
|
|
3132
|
+
const coerceIfNeeded = async (fromType, toType, value) => {
|
|
3133
|
+
if (!toType || toType === fromType || !runner?.coerce)
|
|
3134
|
+
return value;
|
|
3135
|
+
try {
|
|
3136
|
+
return await runner.coerce(fromType, toType, value);
|
|
3137
|
+
}
|
|
3138
|
+
catch {
|
|
3139
|
+
return value;
|
|
3140
|
+
}
|
|
3141
|
+
};
|
|
3142
|
+
const pos = wb.getPositions()[nodeId] || { x: 0, y: 0 };
|
|
3143
|
+
const isArray = typeId.endsWith("[]");
|
|
3144
|
+
const baseTypeId = isArray ? typeId.slice(0, -2) : typeId;
|
|
3145
|
+
const tArr = isArray ? registry.types.get(typeId) : undefined;
|
|
3146
|
+
const tElem = registry.types.get(baseTypeId);
|
|
3147
|
+
const singleTarget = !isArray ? tElem?.bakeTarget : undefined;
|
|
3148
|
+
const arrTarget = isArray ? tArr?.bakeTarget : undefined;
|
|
3149
|
+
const elemTarget = isArray ? tElem?.bakeTarget : undefined;
|
|
3150
|
+
if (singleTarget) {
|
|
3151
|
+
const nodeDesc = registry.nodes.get(singleTarget.nodeTypeId);
|
|
3152
|
+
const inType = getInputTypeId(nodeDesc?.inputs, singleTarget.inputHandle);
|
|
3153
|
+
const coerced = await coerceIfNeeded(typeId, inType, unwrap(raw));
|
|
3154
|
+
wb.addNode({
|
|
3155
|
+
typeId: singleTarget.nodeTypeId,
|
|
3156
|
+
position: { x: pos.x + 180, y: pos.y },
|
|
3157
|
+
}, { inputs: { [singleTarget.inputHandle]: coerced } });
|
|
3158
|
+
return;
|
|
3159
|
+
}
|
|
3160
|
+
if (isArray && arrTarget) {
|
|
3161
|
+
const nodeDesc = registry.nodes.get(arrTarget.nodeTypeId);
|
|
3162
|
+
const inType = getInputTypeId(nodeDesc?.inputs, arrTarget.inputHandle);
|
|
3163
|
+
const coerced = await coerceIfNeeded(typeId, inType, unwrap(raw));
|
|
3164
|
+
wb.addNode({
|
|
3165
|
+
typeId: arrTarget.nodeTypeId,
|
|
3166
|
+
position: { x: pos.x + 180, y: pos.y },
|
|
3167
|
+
}, { inputs: { [arrTarget.inputHandle]: coerced } });
|
|
3168
|
+
return;
|
|
3169
|
+
}
|
|
3170
|
+
if (isArray && elemTarget) {
|
|
3171
|
+
const nodeDesc = registry.nodes.get(elemTarget.nodeTypeId);
|
|
3172
|
+
const inType = getInputTypeId(nodeDesc?.inputs, elemTarget.inputHandle);
|
|
3173
|
+
const src = unwrap(raw);
|
|
3174
|
+
const items = Array.isArray(src) ? src : [src];
|
|
3175
|
+
const coercedItems = await Promise.all(items.map((v) => coerceIfNeeded(baseTypeId, inType, v)));
|
|
3176
|
+
const COLS = 4;
|
|
3177
|
+
const DX = 180;
|
|
3178
|
+
const DY = 160;
|
|
3179
|
+
for (let idx = 0; idx < coercedItems.length; idx++) {
|
|
3180
|
+
const col = idx % COLS;
|
|
3181
|
+
const row = Math.floor(idx / COLS);
|
|
3182
|
+
wb.addNode({
|
|
3183
|
+
typeId: elemTarget.nodeTypeId,
|
|
3184
|
+
position: { x: pos.x + (col + 1) * DX, y: pos.y + row * DY },
|
|
3185
|
+
}, { inputs: { [elemTarget.inputHandle]: coercedItems[idx] } });
|
|
3186
|
+
}
|
|
3187
|
+
return;
|
|
3188
|
+
}
|
|
3189
|
+
}
|
|
3190
|
+
catch { }
|
|
3191
|
+
};
|
|
3192
|
+
return {
|
|
3193
|
+
onDelete: () => {
|
|
3194
|
+
wb.removeNode(nodeId);
|
|
3195
|
+
onClose();
|
|
3196
|
+
},
|
|
3197
|
+
onDuplicate: async () => {
|
|
3198
|
+
const def = wb.export();
|
|
3199
|
+
const n = def.nodes.find((n) => n.nodeId === nodeId);
|
|
3200
|
+
if (!n)
|
|
3201
|
+
return onClose();
|
|
3202
|
+
const pos = wb.getPositions()[nodeId] || { x: 0, y: 0 };
|
|
3203
|
+
const inboundHandles = new Set(def.edges
|
|
3204
|
+
.filter((e) => e.target.nodeId === nodeId)
|
|
3205
|
+
.map((e) => e.target.handle));
|
|
3206
|
+
const allInputs = runner.getInputs(def)[nodeId] || {};
|
|
3207
|
+
const inputsWithoutBindings = Object.fromEntries(Object.entries(allInputs).filter(([handle]) => !inboundHandles.has(handle)));
|
|
3208
|
+
const newNodeId = wb.addNode({
|
|
3209
|
+
typeId: n.typeId,
|
|
3210
|
+
params: n.params,
|
|
3211
|
+
position: { x: pos.x + 24, y: pos.y + 24 },
|
|
3212
|
+
resolvedHandles: n.resolvedHandles,
|
|
3213
|
+
}, {
|
|
3214
|
+
inputs: inputsWithoutBindings,
|
|
3215
|
+
copyOutputsFrom: nodeId,
|
|
3216
|
+
dry: true,
|
|
3217
|
+
});
|
|
3218
|
+
// Select the newly duplicated node
|
|
3219
|
+
wb.setSelection({
|
|
3220
|
+
nodes: [newNodeId],
|
|
3221
|
+
edges: [],
|
|
3222
|
+
});
|
|
3223
|
+
onClose();
|
|
3224
|
+
},
|
|
3225
|
+
onDuplicateWithEdges: async () => {
|
|
3226
|
+
const def = wb.export();
|
|
3227
|
+
const n = def.nodes.find((n) => n.nodeId === nodeId);
|
|
3228
|
+
if (!n)
|
|
3229
|
+
return onClose();
|
|
3230
|
+
const pos = wb.getPositions()[nodeId] || { x: 0, y: 0 };
|
|
3231
|
+
// Get inputs without bindings (literal values only)
|
|
3232
|
+
const inputs = runner.getInputs(def)[nodeId] || {};
|
|
3233
|
+
// Add the duplicated node
|
|
3234
|
+
const newNodeId = wb.addNode({
|
|
3235
|
+
typeId: n.typeId,
|
|
3236
|
+
params: n.params,
|
|
3237
|
+
position: { x: pos.x + 24, y: pos.y + 24 },
|
|
3238
|
+
resolvedHandles: n.resolvedHandles,
|
|
3239
|
+
}, {
|
|
3240
|
+
inputs,
|
|
3241
|
+
copyOutputsFrom: nodeId,
|
|
3242
|
+
dry: true,
|
|
3243
|
+
});
|
|
3244
|
+
// Find all incoming edges (edges where target is the original node)
|
|
3245
|
+
const incomingEdges = def.edges.filter((e) => e.target.nodeId === nodeId);
|
|
3246
|
+
// Duplicate each incoming edge to point to the new node
|
|
3247
|
+
for (const edge of incomingEdges) {
|
|
3248
|
+
wb.connect({
|
|
3249
|
+
source: edge.source, // Keep the same source
|
|
3250
|
+
target: { nodeId: newNodeId, handle: edge.target.handle }, // Point to new node
|
|
3251
|
+
typeId: edge.typeId,
|
|
3252
|
+
}, { dry: true });
|
|
3253
|
+
}
|
|
3254
|
+
// Select the newly duplicated node and edges
|
|
3255
|
+
wb.setSelection({
|
|
3256
|
+
nodes: [newNodeId],
|
|
3257
|
+
edges: [],
|
|
3258
|
+
});
|
|
3259
|
+
onClose();
|
|
3260
|
+
},
|
|
3261
|
+
onRunPull: async () => {
|
|
3262
|
+
try {
|
|
3263
|
+
await runner.computeNode(nodeId);
|
|
3264
|
+
}
|
|
3265
|
+
catch { }
|
|
3266
|
+
onClose();
|
|
3267
|
+
},
|
|
3268
|
+
onBake: async (handleId) => {
|
|
3269
|
+
await doBake(handleId);
|
|
3270
|
+
onClose();
|
|
3271
|
+
},
|
|
3272
|
+
onCopy: () => {
|
|
3273
|
+
const copyHandler = createNodeCopyHandler(wb, runner, nodeId, getDefaultNodeSize, onCopyResult);
|
|
3274
|
+
copyHandler();
|
|
3275
|
+
onClose();
|
|
3276
|
+
},
|
|
3277
|
+
onCopyId: async () => {
|
|
3278
|
+
try {
|
|
3279
|
+
await navigator.clipboard.writeText(nodeId);
|
|
3280
|
+
}
|
|
3281
|
+
catch { }
|
|
3282
|
+
onClose();
|
|
3283
|
+
},
|
|
3284
|
+
onClose,
|
|
3285
|
+
};
|
|
3286
|
+
}
|
|
3287
|
+
/**
|
|
3288
|
+
* Creates a copy handler that copies the current selection and optionally stores the result.
|
|
3289
|
+
*/
|
|
3290
|
+
function createCopyHandler(wb, runner, getDefaultNodeSize, onCopyResult) {
|
|
3291
|
+
return () => {
|
|
3292
|
+
const getNodeSize = getDefaultNodeSize
|
|
3293
|
+
? (nodeId, typeId) => getDefaultNodeSize(typeId)
|
|
3294
|
+
: undefined;
|
|
3295
|
+
const data = wb.copySelection(runner, getNodeSize);
|
|
3296
|
+
if (onCopyResult) {
|
|
3297
|
+
onCopyResult(data);
|
|
3298
|
+
}
|
|
3299
|
+
};
|
|
3300
|
+
}
|
|
3301
|
+
/**
|
|
3302
|
+
* Creates a copy handler for a single node (temporarily selects it, copies, then restores selection).
|
|
3303
|
+
*/
|
|
3304
|
+
function createNodeCopyHandler(wb, runner, nodeId, getDefaultNodeSize, onCopyResult) {
|
|
3305
|
+
return () => {
|
|
3306
|
+
// Select the node first, then copy
|
|
3307
|
+
const currentSelection = wb.getSelection();
|
|
3308
|
+
wb.setSelection({ nodes: [nodeId], edges: [] });
|
|
3309
|
+
const getNodeSize = getDefaultNodeSize
|
|
3310
|
+
? (nodeId, typeId) => getDefaultNodeSize(typeId)
|
|
3311
|
+
: undefined;
|
|
3312
|
+
const data = wb.copySelection(runner, getNodeSize);
|
|
3313
|
+
// Restore original selection
|
|
3314
|
+
wb.setSelection(currentSelection);
|
|
3315
|
+
if (onCopyResult) {
|
|
3316
|
+
onCopyResult(data);
|
|
3317
|
+
}
|
|
3318
|
+
};
|
|
3319
|
+
}
|
|
3320
|
+
/**
|
|
3321
|
+
* Creates base selection context menu handlers.
|
|
3322
|
+
*/
|
|
3323
|
+
function createSelectionContextMenuHandlers(wb, onClose, getDefaultNodeSize, onCopyResult, runner) {
|
|
3324
|
+
return {
|
|
3325
|
+
onCopy: runner
|
|
3326
|
+
? createCopyHandler(wb, runner, getDefaultNodeSize, onCopyResult)
|
|
3327
|
+
: () => {
|
|
3328
|
+
// No-op if runner not available
|
|
3329
|
+
onClose();
|
|
3330
|
+
},
|
|
3331
|
+
onDelete: () => {
|
|
3332
|
+
wb.deleteSelection();
|
|
3333
|
+
onClose();
|
|
3334
|
+
},
|
|
3335
|
+
onClose,
|
|
3336
|
+
};
|
|
3337
|
+
}
|
|
3338
|
+
/**
|
|
3339
|
+
* Creates base default context menu handlers.
|
|
3340
|
+
*/
|
|
3341
|
+
function createDefaultContextMenuHandlers(onAddNode, onClose, onPaste) {
|
|
3342
|
+
return {
|
|
3343
|
+
onAddNode,
|
|
3344
|
+
onPaste,
|
|
3345
|
+
onClose,
|
|
3346
|
+
};
|
|
3347
|
+
}
|
|
3348
|
+
function getBakeableOutputs(nodeId, wb, registry, outputTypesMap) {
|
|
3349
|
+
try {
|
|
3350
|
+
const def = wb.export();
|
|
3351
|
+
const node = def.nodes.find((n) => n.nodeId === nodeId);
|
|
3352
|
+
if (!node)
|
|
3353
|
+
return [];
|
|
3354
|
+
const desc = registry.nodes.get(node.typeId);
|
|
3355
|
+
const handles = Object.keys(desc?.outputs || {});
|
|
3356
|
+
const out = [];
|
|
3357
|
+
for (const h of handles) {
|
|
3358
|
+
const tId = outputTypesMap?.[nodeId]?.[h];
|
|
3359
|
+
if (!tId)
|
|
3360
|
+
continue;
|
|
3361
|
+
if (tId.endsWith("[]")) {
|
|
3362
|
+
const base = tId.slice(0, -2);
|
|
3363
|
+
const tArr = registry.types.get(tId);
|
|
3364
|
+
const tElem = registry.types.get(base);
|
|
3365
|
+
const arrT = tArr?.bakeTarget;
|
|
3366
|
+
const elemT = tElem?.bakeTarget;
|
|
3367
|
+
if ((arrT && registry.nodes.has(arrT.nodeTypeId)) ||
|
|
3368
|
+
(elemT && registry.nodes.has(elemT.nodeTypeId)))
|
|
3369
|
+
out.push(h);
|
|
3370
|
+
}
|
|
3371
|
+
else {
|
|
3372
|
+
const t = registry.types.get(tId);
|
|
3373
|
+
const bt = t?.bakeTarget;
|
|
3374
|
+
if (bt && registry.nodes.has(bt.nodeTypeId))
|
|
3375
|
+
out.push(h);
|
|
3376
|
+
}
|
|
3377
|
+
}
|
|
3378
|
+
return out;
|
|
3379
|
+
}
|
|
3380
|
+
catch {
|
|
3381
|
+
return [];
|
|
3382
|
+
}
|
|
3383
|
+
}
|
|
3384
|
+
|
|
3093
3385
|
function IssueBadge({ level, title, size = 12, className, }) {
|
|
3094
3386
|
const colorClass = level === "error" ? "text-red-600" : "text-amber-600";
|
|
3095
3387
|
return (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" ? (jsx(XCircleIcon, { size: size, weight: "fill" })) : (jsx(WarningCircleIcon, { size: size, weight: "fill" })) }));
|
|
@@ -3302,7 +3594,7 @@ function Inspector({ debug, autoScroll, hideWorkbench, onAutoScrollChange, onHid
|
|
|
3302
3594
|
}
|
|
3303
3595
|
catch { }
|
|
3304
3596
|
};
|
|
3305
|
-
return (jsxs("div", { className: `${widthClass} border-l border-gray-300 p-3 flex flex-col h-full min-h-0 overflow-auto`, children: [jsxs("div", { className: "flex-1 overflow-auto", children: [contextPanel && jsx("div", { className: "mb-2", children: contextPanel }), inputValidationErrors.length > 0 && (jsxs("div", { className: "mb-2 space-y-1", children: [inputValidationErrors.map((err, i) => (jsxs("div", { className: "text-xs text-red-700 bg-red-50 border border-red-200 rounded px-2 py-1 flex items-start justify-between gap-2", children: [jsxs("div", { className: "flex-1", children: [jsx("div", { className: "font-semibold", children: "Input Validation Error" }), jsx("div", { className: "break-words", children: err.message }), jsxs("div", { className: "text-[10px] text-red-600 mt-1", children: [err.nodeId, ".", err.handle, " (type: ", err.typeId, ")"] })] }), jsx("button", { className: "text-red-500 hover:text-red-700 text-[10px] px-1", onClick: () => removeInputValidationError(i), title: "Dismiss", children: jsx(XIcon, { size: 10 }) })] }, i))), inputValidationErrors.length > 1 && (jsx("button", { className: "text-xs text-red-600 hover:text-red-800 underline", onClick: clearInputValidationErrors, children: "Clear all" }))] })), systemErrors.length > 0 && (jsxs("div", { className: "mb-2 space-y-1", children: [systemErrors.map((err, i) => (jsxs("div", { className: "text-xs text-red-700 bg-red-50 border border-red-200 rounded px-2 py-1 flex items-start justify-between gap-2", children: [jsxs("div", { className: "flex-1", children: [jsx("div", { className: "font-semibold", children: err.code ? `Error ${err.code}` : "Error" }), jsx("div", { className: "break-words", children: err.message })] }), jsx("button", { className: "text-red-500 hover:text-red-700 text-[10px] px-1", onClick: () => removeSystemError(i), title: "Dismiss", children: jsx(XIcon, { size: 10 }) })] }, i))), systemErrors.length > 1 && (jsx("button", { className: "text-xs text-red-600 hover:text-red-800 underline", onClick: clearSystemErrors, children: "Clear all" }))] })), registryErrors.length > 0 && (jsxs("div", { className: "mb-2 space-y-1", children: [registryErrors.map((err, i) => (jsxs("div", { className: "text-xs text-amber-700 bg-amber-50 border border-amber-200 rounded px-2 py-1 flex items-start justify-between gap-2", children: [jsxs("div", { className: "flex-1", children: [jsx("div", { className: "font-semibold", children: "Registry Error" }), jsx("div", { className: "break-words", children: err.message }), err.attempt && err.maxAttempts && (jsxs("div", { className: "text-[10px] text-amber-600 mt-1", children: ["Attempt ", err.attempt, " of ", err.maxAttempts] }))] }), jsx("button", { className: "text-amber-500 hover:text-amber-700 text-[10px] px-1", onClick: () => removeRegistryError(i), title: "Dismiss", children: jsx(XIcon, { size: 10 }) })] }, i))), registryErrors.length > 1 && (jsx("button", { className: "text-xs text-amber-600 hover:text-amber-800 underline", onClick: clearRegistryErrors, children: "Clear all" }))] })), jsx("div", { className: "font-semibold mb-2", children: "Inspector" }), jsxs("div", { className: "text-xs text-gray-500 mb-2", children: ["valuesTick: ", valuesTick] }), jsx("div", { className: "flex-1", children: !selectedNode && !selectedEdge ? (jsxs("div", { children: [jsx("div", { className: "text-gray-500", children: "Select a node or edge." }), globalValidationIssues && globalValidationIssues.length > 0 && (jsxs("div", { className: "mt-2 text-xs bg-red-50 border border-red-200 rounded px-2 py-1", children: [jsx("div", { className: "font-semibold mb-1", children: "Validation" }), jsx("ul", { className: "list-disc ml-4", children: globalValidationIssues.map((m, i) => (jsxs("li", { className: "flex items-center gap-1", children: [jsx(IssueBadge, { level: m.level, size: 24, className: "w-6 h-6" }), jsx("span", { children: `${m.code}: ${m.message}` }), !!m.data?.edgeId && (jsx("button", { className: "ml-2 text-[10px] px-1 py-[2px] border border-red-300 rounded text-red-700 hover:bg-red-50", onClick: (e) => {
|
|
3597
|
+
return (jsxs("div", { className: `${widthClass} border-l border-gray-300 p-3 flex flex-col h-full min-h-0 overflow-auto select-none`, children: [jsxs("div", { className: "flex-1 overflow-auto", children: [contextPanel && jsx("div", { className: "mb-2", children: contextPanel }), inputValidationErrors.length > 0 && (jsxs("div", { className: "mb-2 space-y-1", children: [inputValidationErrors.map((err, i) => (jsxs("div", { className: "text-xs text-red-700 bg-red-50 border border-red-200 rounded px-2 py-1 flex items-start justify-between gap-2", children: [jsxs("div", { className: "flex-1", children: [jsx("div", { className: "font-semibold", children: "Input Validation Error" }), jsx("div", { className: "break-words", children: err.message }), jsxs("div", { className: "text-[10px] text-red-600 mt-1", children: [err.nodeId, ".", err.handle, " (type: ", err.typeId, ")"] })] }), jsx("button", { className: "text-red-500 hover:text-red-700 text-[10px] px-1", onClick: () => removeInputValidationError(i), title: "Dismiss", children: jsx(XIcon, { size: 10 }) })] }, i))), inputValidationErrors.length > 1 && (jsx("button", { className: "text-xs text-red-600 hover:text-red-800 underline", onClick: clearInputValidationErrors, children: "Clear all" }))] })), systemErrors.length > 0 && (jsxs("div", { className: "mb-2 space-y-1", children: [systemErrors.map((err, i) => (jsxs("div", { className: "text-xs text-red-700 bg-red-50 border border-red-200 rounded px-2 py-1 flex items-start justify-between gap-2", children: [jsxs("div", { className: "flex-1", children: [jsx("div", { className: "font-semibold", children: err.code ? `Error ${err.code}` : "Error" }), jsx("div", { className: "break-words", children: err.message })] }), jsx("button", { className: "text-red-500 hover:text-red-700 text-[10px] px-1", onClick: () => removeSystemError(i), title: "Dismiss", children: jsx(XIcon, { size: 10 }) })] }, i))), systemErrors.length > 1 && (jsx("button", { className: "text-xs text-red-600 hover:text-red-800 underline", onClick: clearSystemErrors, children: "Clear all" }))] })), registryErrors.length > 0 && (jsxs("div", { className: "mb-2 space-y-1", children: [registryErrors.map((err, i) => (jsxs("div", { className: "text-xs text-amber-700 bg-amber-50 border border-amber-200 rounded px-2 py-1 flex items-start justify-between gap-2", children: [jsxs("div", { className: "flex-1", children: [jsx("div", { className: "font-semibold", children: "Registry Error" }), jsx("div", { className: "break-words", children: err.message }), err.attempt && err.maxAttempts && (jsxs("div", { className: "text-[10px] text-amber-600 mt-1", children: ["Attempt ", err.attempt, " of ", err.maxAttempts] }))] }), jsx("button", { className: "text-amber-500 hover:text-amber-700 text-[10px] px-1", onClick: () => removeRegistryError(i), title: "Dismiss", children: jsx(XIcon, { size: 10 }) })] }, i))), registryErrors.length > 1 && (jsx("button", { className: "text-xs text-amber-600 hover:text-amber-800 underline", onClick: clearRegistryErrors, children: "Clear all" }))] })), jsx("div", { className: "font-semibold mb-2", children: "Inspector" }), jsxs("div", { className: "text-xs text-gray-500 mb-2", children: ["valuesTick: ", valuesTick] }), jsx("div", { className: "flex-1", children: !selectedNode && !selectedEdge ? (jsxs("div", { children: [jsx("div", { className: "text-gray-500", children: "Select a node or edge." }), globalValidationIssues && globalValidationIssues.length > 0 && (jsxs("div", { className: "mt-2 text-xs bg-red-50 border border-red-200 rounded px-2 py-1", children: [jsx("div", { className: "font-semibold mb-1", children: "Validation" }), jsx("ul", { className: "list-disc ml-4", children: globalValidationIssues.map((m, i) => (jsxs("li", { className: "flex items-center gap-1", children: [jsx(IssueBadge, { level: m.level, size: 24, className: "w-6 h-6" }), jsx("span", { children: `${m.code}: ${m.message}` }), !!m.data?.edgeId && (jsx("button", { className: "ml-2 text-[10px] px-1 py-[2px] border border-red-300 rounded text-red-700 hover:bg-red-50", onClick: (e) => {
|
|
3306
3598
|
e.stopPropagation();
|
|
3307
3599
|
deleteEdgeById(m.data?.edgeId);
|
|
3308
3600
|
}, title: "Delete referenced edge", children: "Delete edge" }))] }, i))) })] }))] })) : selectedEdge ? (jsxs("div", { children: [jsxs("div", { className: "mb-2", children: [jsxs("div", { children: ["Edge: ", selectedEdge.id] }), jsxs("div", { children: [selectedEdge.source.nodeId, ".", selectedEdge.source.handle, " \u2192", " ", selectedEdge.target.nodeId, ".", selectedEdge.target.handle] }), renderEdgeStatus(), jsx("div", { className: "mt-1", children: jsx("button", { className: "text-xs px-2 py-1 border border-red-300 rounded text-red-700 hover:bg-red-50", onClick: (e) => {
|
|
@@ -3370,7 +3662,7 @@ function Inspector({ debug, autoScroll, hideWorkbench, onAutoScrollChange, onHid
|
|
|
3370
3662
|
const title = inIssues
|
|
3371
3663
|
.map((v) => `${v.code}: ${v.message}`)
|
|
3372
3664
|
.join("; ");
|
|
3373
|
-
return (jsxs("div", { className: "flex items-center gap-2 mb-1", children: [jsxs("label", { className: "w-32 flex flex-col", children: [jsx("span", { children: prettyHandle(h) }), jsx("span", { className: "text-gray-500 text-[11px]", children: typeId })] }), hasValidation && (jsx(IssueBadge, { level: hasErr ? "error" : "warning", size: 24, className: "ml-1 w-6 h-6", title: title })), isEnum ? (jsxs("div", { className: "flex items-center gap-1 flex-1", children: [jsxs("select", { className: "border border-gray-300 rounded px-2 py-1 focus:outline-none focus:ring-2 focus:ring-blue-500 flex-1", value: current !== undefined && current !== null
|
|
3665
|
+
return (jsxs("div", { className: "flex items-center gap-2 mb-1", children: [jsxs("label", { className: "w-32 flex flex-col", children: [jsx("span", { children: prettyHandle(h) }), jsx("span", { className: "text-gray-500 text-[11px]", children: typeId })] }), hasValidation && (jsx(IssueBadge, { level: hasErr ? "error" : "warning", size: 24, className: "ml-1 w-6 h-6", title: title })), isEnum ? (jsxs("div", { className: "flex items-center gap-1 flex-1", children: [jsxs("select", { className: "border border-gray-300 rounded px-2 py-1 focus:outline-none focus:ring-2 focus:ring-blue-500 flex-1 select-text", value: current !== undefined && current !== null
|
|
3374
3666
|
? String(current)
|
|
3375
3667
|
: "", onChange: (e) => {
|
|
3376
3668
|
const val = e.target.value;
|
|
@@ -3384,7 +3676,7 @@ function Inspector({ debug, autoScroll, hideWorkbench, onAutoScrollChange, onHid
|
|
|
3384
3676
|
? `Default: ${placeholder}`
|
|
3385
3677
|
: "(select)" }), registry.enums
|
|
3386
3678
|
.get(typeId)
|
|
3387
|
-
?.options.map((opt) => (jsx("option", { value: String(opt.value), children: opt.label }, opt.value)))] }), hasValue && !isLinked && (jsx("button", { className: "flex-shrink-0 p-1 hover:bg-gray-100 rounded text-gray-500 hover:text-gray-700", onClick: clearInput, title: "Clear input value", children: jsx(XCircleIcon, { size: 16 }) }))] })) : isLinked ? (jsx("div", { className: "flex items-center gap-1 flex-1", children: jsx("div", { className: "flex-1 min-w-0", children: renderLinkedInputDisplay(typeId, current) }) })) : (jsxs("div", { className: "flex items-center gap-1 flex-1", children: [jsx("input", { className: "border border-gray-300 rounded px-2 py-1 focus:outline-none focus:ring-2 focus:ring-blue-500 flex-1", placeholder: placeholder
|
|
3679
|
+
?.options.map((opt) => (jsx("option", { value: String(opt.value), children: opt.label }, opt.value)))] }), hasValue && !isLinked && (jsx("button", { className: "flex-shrink-0 p-1 hover:bg-gray-100 rounded text-gray-500 hover:text-gray-700", onClick: clearInput, title: "Clear input value", children: jsx(XCircleIcon, { size: 16 }) }))] })) : isLinked ? (jsx("div", { className: "flex items-center gap-1 flex-1", children: jsx("div", { className: "flex-1 min-w-0", children: renderLinkedInputDisplay(typeId, current) }) })) : (jsxs("div", { className: "flex items-center gap-1 flex-1", children: [jsx("input", { className: "border border-gray-300 rounded px-2 py-1 focus:outline-none focus:ring-2 focus:ring-blue-500 flex-1 select-text", placeholder: placeholder
|
|
3388
3680
|
? `Default: ${placeholder}`
|
|
3389
3681
|
: undefined, value: displayValue, onChange: (e) => onChangeText(e.target.value), onBlur: commit, onKeyDown: (e) => {
|
|
3390
3682
|
if (e.key === "Enter")
|
|
@@ -3637,6 +3929,13 @@ function DefaultContextMenu({ open, clientPos, handlers, registry, nodeIds, }) {
|
|
|
3637
3929
|
handlers.onAddNode(typeId, { position: p });
|
|
3638
3930
|
handlers.onClose();
|
|
3639
3931
|
};
|
|
3932
|
+
const handlePaste = () => {
|
|
3933
|
+
if (!handlers.onPaste)
|
|
3934
|
+
return;
|
|
3935
|
+
const p = rf.screenToFlowPosition({ x: clientPos.x, y: clientPos.y });
|
|
3936
|
+
handlers.onPaste(p);
|
|
3937
|
+
handlers.onClose();
|
|
3938
|
+
};
|
|
3640
3939
|
const renderTree = (tree, path = []) => {
|
|
3641
3940
|
const entries = Object.entries(tree?.__children ?? {}).sort((a, b) => a[0].localeCompare(b[0]));
|
|
3642
3941
|
return (jsx("div", { children: entries.map(([key, child]) => {
|
|
@@ -3651,10 +3950,10 @@ function DefaultContextMenu({ open, clientPos, handlers, registry, nodeIds, }) {
|
|
|
3651
3950
|
return (jsxs("div", { children: [jsx("div", { className: "px-2 py-1 text-[11px] uppercase tracking-wide text-gray-400", children: label }), child.__self && (jsx("button", { onClick: () => handleClick(child.__self), className: "block w-full text-left px-3 py-1 hover:bg-gray-100 cursor-pointer", title: child.__self, children: label })), jsx("div", { className: "pl-2 border-l border-gray-200 ml-2", children: renderTree(child, [...path, key]) })] }, idKey));
|
|
3652
3951
|
}) }));
|
|
3653
3952
|
};
|
|
3654
|
-
return (jsxs("div", { ref: ref, tabIndex: -1, className: "fixed z-[1000] bg-white border border-gray-300 rounded-
|
|
3953
|
+
return (jsxs("div", { ref: ref, tabIndex: -1, className: "fixed z-[1000] bg-white border border-gray-300 rounded-lg shadow-lg p-1 min-w-[180px] text-sm text-gray-700 select-none", style: { left: x, top: y }, onClick: (e) => e.stopPropagation(), onMouseDown: (e) => e.stopPropagation(), onWheel: (e) => e.stopPropagation(), onContextMenu: (e) => {
|
|
3655
3954
|
e.preventDefault();
|
|
3656
3955
|
e.stopPropagation();
|
|
3657
|
-
}, children: [jsxs("div", { className: "px-2 py-1 font-semibold text-gray-700", children: ["Add Node", " ", jsxs("span", { className: "text-gray-500 font-normal", children: ["(", totalCount, ")"] })] }), jsx("div", { className: "px-2 pb-1", children: jsx("input", { ref: inputRef, type: "text", value: query, onChange: (e) => setQuery(e.target.value), placeholder: "Filter nodes...", className: "w-full border border-gray-300 px-2 py-1 text-sm outline-none focus:border-gray-400", onClick: (e) => e.stopPropagation(), onMouseDown: (e) => e.stopPropagation(), onWheel: (e) => e.stopPropagation() }) }), jsx("div", { className: "max-h-60 overflow-auto", children: totalCount > 0 ? (renderTree(root)) : (jsx("div", { className: "px-3 py-2 text-gray-400", children: "No matches" })) })] }));
|
|
3956
|
+
}, children: [handlers.onPaste && (jsx("button", { className: "block w-full text-left px-2 py-1 hover:bg-gray-100 disabled:opacity-50 disabled:cursor-not-allowed", onClick: handlePaste, children: "Paste" })), handlers.onPaste && jsx("div", { className: "h-px bg-gray-200 my-1" }), jsxs("div", { className: "px-2 py-1 font-semibold text-gray-700", children: ["Add Node", " ", jsxs("span", { className: "text-gray-500 font-normal", children: ["(", totalCount, ")"] })] }), jsx("div", { className: "px-2 pb-1", children: jsx("input", { ref: inputRef, type: "text", value: query, onChange: (e) => setQuery(e.target.value), placeholder: "Filter nodes...", className: "w-full border border-gray-300 px-2 py-1 text-sm outline-none focus:border-gray-400 select-text", onClick: (e) => e.stopPropagation(), onMouseDown: (e) => e.stopPropagation(), onWheel: (e) => e.stopPropagation() }) }), jsx("div", { className: "max-h-60 overflow-auto", children: totalCount > 0 ? (renderTree(root)) : (jsx("div", { className: "px-3 py-2 text-gray-400", children: "No matches" })) })] }));
|
|
3658
3957
|
}
|
|
3659
3958
|
|
|
3660
3959
|
function NodeContextMenu({ open, clientPos, nodeId, handlers, canRunPull, bakeableOutputs, }) {
|
|
@@ -3692,209 +3991,55 @@ function NodeContextMenu({ open, clientPos, nodeId, handlers, canRunPull, bakeab
|
|
|
3692
3991
|
const x = Math.min(clientPos.x, (typeof window !== "undefined" ? window.innerWidth : 0) -
|
|
3693
3992
|
(MENU_MIN_WIDTH + PADDING));
|
|
3694
3993
|
const y = Math.min(clientPos.y, (typeof window !== "undefined" ? window.innerHeight : 0) - 240);
|
|
3695
|
-
return (jsxs("div", { ref: ref, tabIndex: -1, className: "fixed z-[1000] bg-white border border-gray-300 rounded-lg shadow-lg p-1 min-w-[180px] text-sm text-gray-700", style: { left: x, top: y }, onClick: (e) => e.stopPropagation(), onMouseDown: (e) => e.stopPropagation(), onWheel: (e) => e.stopPropagation(), onContextMenu: (e) => {
|
|
3994
|
+
return (jsxs("div", { ref: ref, tabIndex: -1, className: "fixed z-[1000] bg-white border border-gray-300 rounded-lg shadow-lg p-1 min-w-[180px] text-sm text-gray-700 select-none", style: { left: x, top: y }, onClick: (e) => e.stopPropagation(), onMouseDown: (e) => e.stopPropagation(), onWheel: (e) => e.stopPropagation(), onContextMenu: (e) => {
|
|
3696
3995
|
e.preventDefault();
|
|
3697
3996
|
e.stopPropagation();
|
|
3698
|
-
}, children: [jsxs("div", { className: "px-2 py-1 font-semibold text-gray-700", children: ["Node (", nodeId, ")"] }), jsx("button", { className: "block w-full text-left px-2 py-1 hover:bg-gray-100", onClick: handlers.onDelete, children: "Delete" }), jsx("button", { className: "block w-full text-left px-2 py-1 hover:bg-gray-100", onClick: handlers.onDuplicate, children: "Duplicate" }), jsx("button", { className: "block w-full text-left px-2 py-1 hover:bg-gray-100", onClick: handlers.onDuplicateWithEdges, children: "Duplicate with edges" }), canRunPull && (jsx("button", { className: "block w-full text-left px-2 py-1 hover:bg-gray-100", onClick: handlers.onRunPull, children: "Run (pull)" })), jsx("div", { className: "h-px bg-gray-200 my-1" }),
|
|
3997
|
+
}, children: [jsxs("div", { className: "px-2 py-1 font-semibold text-gray-700", children: ["Node (", nodeId, ")"] }), jsx("button", { className: "block w-full text-left px-2 py-1 hover:bg-gray-100", onClick: handlers.onDelete, children: "Delete" }), jsx("button", { className: "block w-full text-left px-2 py-1 hover:bg-gray-100", onClick: handlers.onDuplicate, children: "Duplicate" }), jsx("button", { className: "block w-full text-left px-2 py-1 hover:bg-gray-100", onClick: handlers.onDuplicateWithEdges, children: "Duplicate with edges" }), canRunPull && (jsx("button", { className: "block w-full text-left px-2 py-1 hover:bg-gray-100", onClick: handlers.onRunPull, children: "Run (pull)" })), jsx("div", { className: "h-px bg-gray-200 my-1" }), jsx("button", { className: "block w-full text-left px-2 py-1 hover:bg-gray-100", onClick: handlers.onCopy, children: "Copy" }), jsx("button", { className: "block w-full text-left px-2 py-1 hover:bg-gray-100", onClick: handlers.onCopyId, children: "Copy Node ID" }), bakeableOutputs.length > 0 && (jsxs(Fragment, { children: [jsx("div", { className: "h-px bg-gray-200 my-1" }), jsx("div", { className: "px-2 py-1 font-semibold text-gray-700", children: "Bake" }), bakeableOutputs.map((h) => (jsxs("button", { className: "block w-full text-left px-2 py-1 hover:bg-gray-100", onClick: () => handlers.onBake(h), children: ["Bake: ", h] }, h)))] }))] }));
|
|
3699
3998
|
}
|
|
3700
3999
|
|
|
3701
|
-
function
|
|
3702
|
-
const
|
|
3703
|
-
|
|
3704
|
-
|
|
3705
|
-
|
|
3706
|
-
|
|
3707
|
-
|
|
3708
|
-
|
|
3709
|
-
const coerceIfNeeded = async (fromType, toType, value) => {
|
|
3710
|
-
if (!toType || toType === fromType || !runner?.coerce)
|
|
3711
|
-
return value;
|
|
3712
|
-
try {
|
|
3713
|
-
return await runner.coerce(fromType, toType, value);
|
|
3714
|
-
}
|
|
3715
|
-
catch {
|
|
3716
|
-
return value;
|
|
3717
|
-
}
|
|
3718
|
-
};
|
|
3719
|
-
const pos = wb.getPositions?.()[nodeId] || { x: 0, y: 0 };
|
|
3720
|
-
const isArray = typeId.endsWith("[]");
|
|
3721
|
-
const baseTypeId = isArray ? typeId.slice(0, -2) : typeId;
|
|
3722
|
-
const tArr = isArray ? registry.types.get(typeId) : undefined;
|
|
3723
|
-
const tElem = registry.types.get(baseTypeId);
|
|
3724
|
-
const singleTarget = !isArray ? tElem?.bakeTarget : undefined;
|
|
3725
|
-
const arrTarget = isArray ? tArr?.bakeTarget : undefined;
|
|
3726
|
-
const elemTarget = isArray ? tElem?.bakeTarget : undefined;
|
|
3727
|
-
if (singleTarget) {
|
|
3728
|
-
const nodeDesc = registry.nodes.get(singleTarget.nodeTypeId);
|
|
3729
|
-
const inType = getInputTypeId(nodeDesc?.inputs, singleTarget.inputHandle);
|
|
3730
|
-
const coerced = await coerceIfNeeded(typeId, inType, unwrap(raw));
|
|
3731
|
-
wb.addNode({
|
|
3732
|
-
typeId: singleTarget.nodeTypeId,
|
|
3733
|
-
position: { x: pos.x + 180, y: pos.y },
|
|
3734
|
-
}, { inputs: { [singleTarget.inputHandle]: coerced } });
|
|
3735
|
-
return;
|
|
3736
|
-
}
|
|
3737
|
-
if (isArray && arrTarget) {
|
|
3738
|
-
const nodeDesc = registry.nodes.get(arrTarget.nodeTypeId);
|
|
3739
|
-
const inType = getInputTypeId(nodeDesc?.inputs, arrTarget.inputHandle);
|
|
3740
|
-
const coerced = await coerceIfNeeded(typeId, inType, unwrap(raw));
|
|
3741
|
-
wb.addNode({
|
|
3742
|
-
typeId: arrTarget.nodeTypeId,
|
|
3743
|
-
position: { x: pos.x + 180, y: pos.y },
|
|
3744
|
-
}, { inputs: { [arrTarget.inputHandle]: coerced } });
|
|
3745
|
-
return;
|
|
3746
|
-
}
|
|
3747
|
-
if (isArray && elemTarget) {
|
|
3748
|
-
const nodeDesc = registry.nodes.get(elemTarget.nodeTypeId);
|
|
3749
|
-
const inType = getInputTypeId(nodeDesc?.inputs, elemTarget.inputHandle);
|
|
3750
|
-
const src = unwrap(raw);
|
|
3751
|
-
const items = Array.isArray(src) ? src : [src];
|
|
3752
|
-
const coercedItems = await Promise.all(items.map((v) => coerceIfNeeded(baseTypeId, inType, v)));
|
|
3753
|
-
const COLS = 4;
|
|
3754
|
-
const DX = 180;
|
|
3755
|
-
const DY = 160;
|
|
3756
|
-
for (let idx = 0; idx < coercedItems.length; idx++) {
|
|
3757
|
-
const col = idx % COLS;
|
|
3758
|
-
const row = Math.floor(idx / COLS);
|
|
3759
|
-
wb.addNode({
|
|
3760
|
-
typeId: elemTarget.nodeTypeId,
|
|
3761
|
-
position: { x: pos.x + (col + 1) * DX, y: pos.y + row * DY },
|
|
3762
|
-
}, { inputs: { [elemTarget.inputHandle]: coercedItems[idx] } });
|
|
3763
|
-
}
|
|
4000
|
+
function SelectionContextMenu({ open, clientPos, handlers, }) {
|
|
4001
|
+
const ref = useRef(null);
|
|
4002
|
+
// Close on outside click and on ESC
|
|
4003
|
+
useEffect(() => {
|
|
4004
|
+
if (!open)
|
|
4005
|
+
return;
|
|
4006
|
+
const onDown = (e) => {
|
|
4007
|
+
if (!ref.current)
|
|
3764
4008
|
return;
|
|
3765
|
-
|
|
3766
|
-
|
|
3767
|
-
|
|
3768
|
-
|
|
3769
|
-
|
|
3770
|
-
|
|
3771
|
-
|
|
3772
|
-
|
|
3773
|
-
|
|
3774
|
-
|
|
3775
|
-
|
|
3776
|
-
|
|
3777
|
-
|
|
3778
|
-
|
|
3779
|
-
|
|
3780
|
-
|
|
3781
|
-
|
|
3782
|
-
|
|
3783
|
-
|
|
3784
|
-
|
|
3785
|
-
|
|
3786
|
-
|
|
3787
|
-
|
|
3788
|
-
|
|
3789
|
-
|
|
3790
|
-
|
|
3791
|
-
|
|
3792
|
-
|
|
3793
|
-
|
|
3794
|
-
|
|
3795
|
-
// Select the newly duplicated node
|
|
3796
|
-
wb.setSelection({
|
|
3797
|
-
nodes: [newNodeId],
|
|
3798
|
-
edges: [],
|
|
3799
|
-
});
|
|
3800
|
-
onClose();
|
|
3801
|
-
},
|
|
3802
|
-
onDuplicateWithEdges: async () => {
|
|
3803
|
-
const def = wb.export();
|
|
3804
|
-
const n = def.nodes.find((n) => n.nodeId === nodeId);
|
|
3805
|
-
if (!n)
|
|
3806
|
-
return onClose();
|
|
3807
|
-
const pos = wb.getPositions?.()[nodeId] || { x: 0, y: 0 };
|
|
3808
|
-
// Get inputs without bindings (literal values only)
|
|
3809
|
-
const inputs = runner.getInputs(def)[nodeId] || {};
|
|
3810
|
-
// Add the duplicated node
|
|
3811
|
-
const newNodeId = wb.addNode({
|
|
3812
|
-
typeId: n.typeId,
|
|
3813
|
-
params: n.params,
|
|
3814
|
-
position: { x: pos.x + 24, y: pos.y + 24 },
|
|
3815
|
-
resolvedHandles: n.resolvedHandles,
|
|
3816
|
-
}, {
|
|
3817
|
-
inputs,
|
|
3818
|
-
copyOutputsFrom: nodeId,
|
|
3819
|
-
dry: true,
|
|
3820
|
-
});
|
|
3821
|
-
// Find all incoming edges (edges where target is the original node)
|
|
3822
|
-
const incomingEdges = def.edges.filter((e) => e.target.nodeId === nodeId);
|
|
3823
|
-
// Duplicate each incoming edge to point to the new node
|
|
3824
|
-
for (const edge of incomingEdges) {
|
|
3825
|
-
wb.connect({
|
|
3826
|
-
source: edge.source, // Keep the same source
|
|
3827
|
-
target: { nodeId: newNodeId, handle: edge.target.handle }, // Point to new node
|
|
3828
|
-
typeId: edge.typeId,
|
|
3829
|
-
}, { dry: true });
|
|
3830
|
-
}
|
|
3831
|
-
// Select the newly duplicated node and edges
|
|
3832
|
-
wb.setSelection({
|
|
3833
|
-
nodes: [newNodeId],
|
|
3834
|
-
edges: [],
|
|
3835
|
-
});
|
|
3836
|
-
onClose();
|
|
3837
|
-
},
|
|
3838
|
-
onRunPull: async () => {
|
|
3839
|
-
try {
|
|
3840
|
-
await runner.computeNode(nodeId);
|
|
3841
|
-
}
|
|
3842
|
-
catch { }
|
|
3843
|
-
onClose();
|
|
3844
|
-
},
|
|
3845
|
-
onBake: async (handleId) => {
|
|
3846
|
-
await doBake(handleId);
|
|
3847
|
-
onClose();
|
|
3848
|
-
},
|
|
3849
|
-
onCopyId: async () => {
|
|
3850
|
-
try {
|
|
3851
|
-
await navigator.clipboard.writeText(nodeId);
|
|
3852
|
-
}
|
|
3853
|
-
catch { }
|
|
3854
|
-
onClose();
|
|
3855
|
-
},
|
|
3856
|
-
onClose,
|
|
3857
|
-
};
|
|
3858
|
-
}
|
|
3859
|
-
function getBakeableOutputs(nodeId, wb, registry, outputTypesMap) {
|
|
3860
|
-
try {
|
|
3861
|
-
const def = wb.export();
|
|
3862
|
-
const node = def.nodes.find((n) => n.nodeId === nodeId);
|
|
3863
|
-
if (!node)
|
|
3864
|
-
return [];
|
|
3865
|
-
const desc = registry.nodes.get(node.typeId);
|
|
3866
|
-
const handles = Object.keys(desc?.outputs || {});
|
|
3867
|
-
const out = [];
|
|
3868
|
-
for (const h of handles) {
|
|
3869
|
-
const tId = outputTypesMap?.[nodeId]?.[h];
|
|
3870
|
-
if (!tId)
|
|
3871
|
-
continue;
|
|
3872
|
-
if (tId.endsWith("[]")) {
|
|
3873
|
-
const base = tId.slice(0, -2);
|
|
3874
|
-
const tArr = registry.types.get(tId);
|
|
3875
|
-
const tElem = registry.types.get(base);
|
|
3876
|
-
const arrT = tArr?.bakeTarget;
|
|
3877
|
-
const elemT = tElem?.bakeTarget;
|
|
3878
|
-
if ((arrT && registry.nodes.has(arrT.nodeTypeId)) ||
|
|
3879
|
-
(elemT && registry.nodes.has(elemT.nodeTypeId)))
|
|
3880
|
-
out.push(h);
|
|
3881
|
-
}
|
|
3882
|
-
else {
|
|
3883
|
-
const t = registry.types.get(tId);
|
|
3884
|
-
const bt = t?.bakeTarget;
|
|
3885
|
-
if (bt && registry.nodes.has(bt.nodeTypeId))
|
|
3886
|
-
out.push(h);
|
|
3887
|
-
}
|
|
3888
|
-
}
|
|
3889
|
-
return out;
|
|
3890
|
-
}
|
|
3891
|
-
catch {
|
|
3892
|
-
return [];
|
|
3893
|
-
}
|
|
4009
|
+
if (!ref.current.contains(e.target))
|
|
4010
|
+
handlers.onClose();
|
|
4011
|
+
};
|
|
4012
|
+
const onKey = (e) => {
|
|
4013
|
+
if (e.key === "Escape")
|
|
4014
|
+
handlers.onClose();
|
|
4015
|
+
};
|
|
4016
|
+
window.addEventListener("mousedown", onDown, true);
|
|
4017
|
+
window.addEventListener("keydown", onKey);
|
|
4018
|
+
return () => {
|
|
4019
|
+
window.removeEventListener("mousedown", onDown, true);
|
|
4020
|
+
window.removeEventListener("keydown", onKey);
|
|
4021
|
+
};
|
|
4022
|
+
}, [open, handlers]);
|
|
4023
|
+
useEffect(() => {
|
|
4024
|
+
if (open)
|
|
4025
|
+
ref.current?.focus();
|
|
4026
|
+
}, [open]);
|
|
4027
|
+
if (!open || !clientPos)
|
|
4028
|
+
return null;
|
|
4029
|
+
// Clamp menu position to viewport
|
|
4030
|
+
const MENU_MIN_WIDTH = 180;
|
|
4031
|
+
const PADDING = 16;
|
|
4032
|
+
const x = Math.min(clientPos.x, (typeof window !== "undefined" ? window.innerWidth : 0) -
|
|
4033
|
+
(MENU_MIN_WIDTH + PADDING));
|
|
4034
|
+
const y = Math.min(clientPos.y, (typeof window !== "undefined" ? window.innerHeight : 0) - 100);
|
|
4035
|
+
return (jsxs("div", { ref: ref, tabIndex: -1, className: "fixed z-[1000] bg-white border border-gray-300 rounded-lg shadow-lg p-1 min-w-[180px] text-sm text-gray-700 select-none", style: { left: x, top: y }, onClick: (e) => e.stopPropagation(), onMouseDown: (e) => e.stopPropagation(), onWheel: (e) => e.stopPropagation(), onContextMenu: (e) => {
|
|
4036
|
+
e.preventDefault();
|
|
4037
|
+
e.stopPropagation();
|
|
4038
|
+
}, children: [jsx("div", { className: "px-2 py-1 font-semibold text-gray-700", children: "Selection" }), jsx("button", { className: "block w-full text-left px-2 py-1 hover:bg-gray-100", onClick: handlers.onCopy, children: "Copy" }), jsx("button", { className: "block w-full text-left px-2 py-1 hover:bg-gray-100", onClick: handlers.onDelete, children: "Delete" })] }));
|
|
3894
4039
|
}
|
|
3895
4040
|
|
|
3896
4041
|
const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement, getDefaultNodeSize }, ref) => {
|
|
3897
|
-
const { wb, registry, inputsMap, inputDefaultsMap, outputsMap, outputTypesMap, valuesTick, nodeStatus, edgeStatus, validationByNode, validationByEdge, uiVersion, runner, engineKind, } = useWorkbenchContext();
|
|
4042
|
+
const { wb, registry, inputsMap, inputDefaultsMap, outputsMap, outputTypesMap, valuesTick, nodeStatus, edgeStatus, validationByNode, validationByEdge, uiVersion, runner, engineKind, overrides, } = useWorkbenchContext();
|
|
3898
4043
|
const nodeValidation = validationByNode;
|
|
3899
4044
|
const edgeValidation = validationByEdge.errors;
|
|
3900
4045
|
const [registryVersion, setRegistryVersion] = useState(0);
|
|
@@ -4133,23 +4278,100 @@ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement, get
|
|
|
4133
4278
|
const [nodeMenuOpen, setNodeMenuOpen] = useState(false);
|
|
4134
4279
|
const [nodeMenuPos, setNodeMenuPos] = useState(null);
|
|
4135
4280
|
const [nodeAtMenu, setNodeAtMenu] = useState(null);
|
|
4281
|
+
const [selectionMenuPos, setSelectionMenuPos] = useState(null);
|
|
4282
|
+
const [selectionMenuOpen, setSelectionMenuOpen] = useState(false);
|
|
4283
|
+
// Compute the rectangular screen-space bounds of the current selection
|
|
4284
|
+
const getSelectionScreenBounds = () => {
|
|
4285
|
+
if (typeof document === "undefined")
|
|
4286
|
+
return null;
|
|
4287
|
+
const selection = wb.getSelection();
|
|
4288
|
+
if (!selection.nodes.length)
|
|
4289
|
+
return null;
|
|
4290
|
+
let bounds = null;
|
|
4291
|
+
for (const nodeId of selection.nodes) {
|
|
4292
|
+
const el = document.querySelector(`.react-flow__node[data-id="${nodeId}"]`);
|
|
4293
|
+
if (!el)
|
|
4294
|
+
continue;
|
|
4295
|
+
const rect = el.getBoundingClientRect();
|
|
4296
|
+
if (!bounds) {
|
|
4297
|
+
bounds = {
|
|
4298
|
+
left: rect.left,
|
|
4299
|
+
top: rect.top,
|
|
4300
|
+
right: rect.right,
|
|
4301
|
+
bottom: rect.bottom,
|
|
4302
|
+
};
|
|
4303
|
+
}
|
|
4304
|
+
else {
|
|
4305
|
+
bounds.left = Math.min(bounds.left, rect.left);
|
|
4306
|
+
bounds.top = Math.min(bounds.top, rect.top);
|
|
4307
|
+
bounds.right = Math.max(bounds.right, rect.right);
|
|
4308
|
+
bounds.bottom = Math.max(bounds.bottom, rect.bottom);
|
|
4309
|
+
}
|
|
4310
|
+
}
|
|
4311
|
+
return bounds;
|
|
4312
|
+
};
|
|
4136
4313
|
const onContextMenu = (e) => {
|
|
4137
4314
|
e.preventDefault();
|
|
4138
|
-
//
|
|
4315
|
+
// First, check if the cursor is inside the rectangular bounds of the current selection
|
|
4316
|
+
const selectionBounds = getSelectionScreenBounds();
|
|
4317
|
+
if (selectionBounds) {
|
|
4318
|
+
const { left, top, right, bottom } = selectionBounds;
|
|
4319
|
+
if (e.clientX >= left &&
|
|
4320
|
+
e.clientX <= right &&
|
|
4321
|
+
e.clientY >= top &&
|
|
4322
|
+
e.clientY <= bottom) {
|
|
4323
|
+
setSelectionMenuPos({ x: e.clientX, y: e.clientY });
|
|
4324
|
+
setSelectionMenuOpen(true);
|
|
4325
|
+
setMenuOpen(false);
|
|
4326
|
+
setNodeMenuOpen(false);
|
|
4327
|
+
return;
|
|
4328
|
+
}
|
|
4329
|
+
}
|
|
4330
|
+
// Determine if right-clicked over a node by hit-testing
|
|
4139
4331
|
const target = e.target?.closest(".react-flow__node");
|
|
4140
4332
|
if (target) {
|
|
4141
4333
|
// Resolve node id from data-id attribute React Flow sets
|
|
4142
4334
|
const nodeId = target.getAttribute("data-id");
|
|
4143
|
-
|
|
4144
|
-
|
|
4145
|
-
|
|
4146
|
-
|
|
4335
|
+
const selection = wb.getSelection();
|
|
4336
|
+
const isSelected = nodeId && selection.nodes.includes(nodeId);
|
|
4337
|
+
if (isSelected) {
|
|
4338
|
+
// Right-clicked on a selected node - show selection menu
|
|
4339
|
+
setSelectionMenuPos({ x: e.clientX, y: e.clientY });
|
|
4340
|
+
setSelectionMenuOpen(true);
|
|
4341
|
+
setMenuOpen(false);
|
|
4342
|
+
setNodeMenuOpen(false);
|
|
4343
|
+
return;
|
|
4344
|
+
}
|
|
4345
|
+
else {
|
|
4346
|
+
// Right-clicked on a non-selected node - show node menu
|
|
4347
|
+
setNodeAtMenu(nodeId);
|
|
4348
|
+
setNodeMenuPos({ x: e.clientX, y: e.clientY });
|
|
4349
|
+
setNodeMenuOpen(true);
|
|
4350
|
+
setMenuOpen(false);
|
|
4351
|
+
setSelectionMenuOpen(false);
|
|
4352
|
+
return;
|
|
4353
|
+
}
|
|
4147
4354
|
}
|
|
4148
|
-
|
|
4149
|
-
|
|
4150
|
-
|
|
4151
|
-
|
|
4355
|
+
// Check if right-clicked on a selected edge
|
|
4356
|
+
const edgeTarget = e.target?.closest(".react-flow__edge");
|
|
4357
|
+
if (edgeTarget) {
|
|
4358
|
+
const edgeId = edgeTarget.getAttribute("data-id");
|
|
4359
|
+
const selection = wb.getSelection();
|
|
4360
|
+
const isSelected = edgeId && selection.edges.includes(edgeId);
|
|
4361
|
+
if (isSelected) {
|
|
4362
|
+
// Right-clicked on a selected edge - show selection menu
|
|
4363
|
+
setSelectionMenuPos({ x: e.clientX, y: e.clientY });
|
|
4364
|
+
setSelectionMenuOpen(true);
|
|
4365
|
+
setMenuOpen(false);
|
|
4366
|
+
setNodeMenuOpen(false);
|
|
4367
|
+
return;
|
|
4368
|
+
}
|
|
4152
4369
|
}
|
|
4370
|
+
// Right-clicked on empty space - show default menu
|
|
4371
|
+
setMenuPos({ x: e.clientX, y: e.clientY });
|
|
4372
|
+
setMenuOpen(true);
|
|
4373
|
+
setNodeMenuOpen(false);
|
|
4374
|
+
setSelectionMenuOpen(false);
|
|
4153
4375
|
};
|
|
4154
4376
|
const addNodeAt = useCallback(async (typeId, opts) => wb.addNode({ typeId, position: opts.position }, { inputs: opts.inputs }), [wb]);
|
|
4155
4377
|
const onCloseMenu = useCallback(() => {
|
|
@@ -4158,6 +4380,9 @@ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement, get
|
|
|
4158
4380
|
const onCloseNodeMenu = useCallback(() => {
|
|
4159
4381
|
setNodeMenuOpen(false);
|
|
4160
4382
|
}, []);
|
|
4383
|
+
const onCloseSelectionMenu = useCallback(() => {
|
|
4384
|
+
setSelectionMenuOpen(false);
|
|
4385
|
+
}, []);
|
|
4161
4386
|
useEffect(() => {
|
|
4162
4387
|
const off = runner.on("registry", () => {
|
|
4163
4388
|
setRegistryVersion((v) => v + 1);
|
|
@@ -4165,14 +4390,65 @@ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement, get
|
|
|
4165
4390
|
return () => off();
|
|
4166
4391
|
}, [runner]);
|
|
4167
4392
|
const nodeIds = useMemo(() => Array.from(registry.nodes.keys()), [registry, registryVersion]);
|
|
4168
|
-
const defaultContextMenuHandlers = useMemo(() =>
|
|
4169
|
-
|
|
4170
|
-
|
|
4171
|
-
|
|
4393
|
+
const defaultContextMenuHandlers = useMemo(() => {
|
|
4394
|
+
// Get storage from override or use workbench's internal storage
|
|
4395
|
+
const storage = overrides?.getCopiedDataStorage
|
|
4396
|
+
? overrides.getCopiedDataStorage()
|
|
4397
|
+
: {
|
|
4398
|
+
get: () => wb.getCopiedData(),
|
|
4399
|
+
set: (data) => wb.setCopiedData(data),
|
|
4400
|
+
};
|
|
4401
|
+
const baseHandlers = createDefaultContextMenuHandlers(addNodeAt, onCloseMenu,
|
|
4402
|
+
// Paste handler - checks storage dynamically when called
|
|
4403
|
+
// Only provide handler if storage has data or might have data (for dynamic checking)
|
|
4404
|
+
(position) => {
|
|
4405
|
+
const data = storage.get();
|
|
4406
|
+
if (!data)
|
|
4407
|
+
return;
|
|
4408
|
+
wb.pasteCopiedData(data, position);
|
|
4409
|
+
onCloseMenu();
|
|
4410
|
+
});
|
|
4411
|
+
if (overrides?.getDefaultContextMenuHandlers) {
|
|
4412
|
+
return overrides.getDefaultContextMenuHandlers(wb, baseHandlers);
|
|
4413
|
+
}
|
|
4414
|
+
return baseHandlers;
|
|
4415
|
+
}, [addNodeAt, onCloseMenu, overrides, wb]);
|
|
4416
|
+
const selectionContextMenuHandlers = useMemo(() => {
|
|
4417
|
+
// Get storage from override or use workbench's internal storage
|
|
4418
|
+
const storage = overrides?.getCopiedDataStorage
|
|
4419
|
+
? overrides.getCopiedDataStorage()
|
|
4420
|
+
: {
|
|
4421
|
+
get: () => wb.getCopiedData(),
|
|
4422
|
+
set: (data) => wb.setCopiedData(data),
|
|
4423
|
+
};
|
|
4424
|
+
const baseHandlers = createSelectionContextMenuHandlers(wb, onCloseSelectionMenu, overrides?.getDefaultNodeSize, (data) => {
|
|
4425
|
+
storage.set(data);
|
|
4426
|
+
}, runner);
|
|
4427
|
+
if (overrides?.getSelectionContextMenuHandlers) {
|
|
4428
|
+
const selection = wb.getSelection();
|
|
4429
|
+
return overrides.getSelectionContextMenuHandlers(wb, selection, baseHandlers, {
|
|
4430
|
+
getDefaultNodeSize: overrides.getDefaultNodeSize,
|
|
4431
|
+
});
|
|
4432
|
+
}
|
|
4433
|
+
return baseHandlers;
|
|
4434
|
+
}, [wb, runner, overrides, onCloseSelectionMenu]);
|
|
4172
4435
|
const nodeContextMenuHandlers = useMemo(() => {
|
|
4173
4436
|
if (!nodeAtMenu)
|
|
4174
4437
|
return null;
|
|
4175
|
-
|
|
4438
|
+
// Get storage from override or use workbench's internal storage
|
|
4439
|
+
const storage = overrides?.getCopiedDataStorage
|
|
4440
|
+
? overrides.getCopiedDataStorage()
|
|
4441
|
+
: {
|
|
4442
|
+
get: () => wb.getCopiedData(),
|
|
4443
|
+
set: (data) => wb.setCopiedData(data),
|
|
4444
|
+
};
|
|
4445
|
+
const baseHandlers = createNodeContextMenuHandlers(nodeAtMenu, wb, runner, registry, outputsMap, outputTypesMap, onCloseNodeMenu, overrides?.getDefaultNodeSize, (data) => {
|
|
4446
|
+
storage.set(data);
|
|
4447
|
+
});
|
|
4448
|
+
if (overrides?.getNodeContextMenuHandlers) {
|
|
4449
|
+
return overrides.getNodeContextMenuHandlers(wb, nodeAtMenu, baseHandlers);
|
|
4450
|
+
}
|
|
4451
|
+
return baseHandlers;
|
|
4176
4452
|
}, [
|
|
4177
4453
|
nodeAtMenu,
|
|
4178
4454
|
wb,
|
|
@@ -4181,6 +4457,9 @@ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement, get
|
|
|
4181
4457
|
outputsMap,
|
|
4182
4458
|
outputTypesMap,
|
|
4183
4459
|
onCloseNodeMenu,
|
|
4460
|
+
overrides?.getDefaultNodeSize,
|
|
4461
|
+
overrides?.getNodeContextMenuHandlers,
|
|
4462
|
+
overrides?.getCopiedDataStorage,
|
|
4184
4463
|
]);
|
|
4185
4464
|
const canRunPull = useMemo(() => engineKind()?.toString() === "pull", [engineKind]);
|
|
4186
4465
|
const bakeableOutputs = useMemo(() => {
|
|
@@ -4236,7 +4515,7 @@ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement, get
|
|
|
4236
4515
|
}
|
|
4237
4516
|
}, onConnect: onConnect, onEdgesChange: onEdgesChange, onEdgesDelete: onEdgesDelete, onNodesDelete: onNodesDelete, onNodesChange: onNodesChange, onMoveEnd: onMoveEnd, deleteKeyCode: ["Backspace", "Delete"], proOptions: { hideAttribution: true }, noDragClassName: "wb-nodrag", noWheelClassName: "wb-nowheel", noPanClassName: "wb-nopan", fitView: true, children: [BackgroundRenderer ? (jsx(BackgroundRenderer, {})) : (jsx(Background, { id: "workbench-canvas-background", variant: BackgroundVariant.Dots, gap: 12, size: 1 })), MinimapRenderer ? jsx(MinimapRenderer, {}) : jsx(MiniMap, {}), ControlsRenderer ? jsx(ControlsRenderer, {}) : jsx(Controls, {}), DefaultContextMenuRenderer ? (jsx(DefaultContextMenuRenderer, { open: menuOpen, clientPos: menuPos, handlers: defaultContextMenuHandlers, registry: registry, nodeIds: nodeIds })) : (jsx(DefaultContextMenu, { open: menuOpen, clientPos: menuPos, handlers: defaultContextMenuHandlers, registry: registry, nodeIds: nodeIds })), !!nodeAtMenu &&
|
|
4238
4517
|
nodeContextMenuHandlers &&
|
|
4239
|
-
(NodeContextMenuRenderer ? (jsx(NodeContextMenuRenderer, { open: nodeMenuOpen, clientPos: nodeMenuPos, nodeId: nodeAtMenu, handlers: nodeContextMenuHandlers, canRunPull: canRunPull, bakeableOutputs: bakeableOutputs })) : (jsx(NodeContextMenu, { open: nodeMenuOpen, clientPos: nodeMenuPos, nodeId: nodeAtMenu, handlers: nodeContextMenuHandlers, canRunPull: canRunPull, bakeableOutputs: bakeableOutputs })))] }) }) }));
|
|
4518
|
+
(NodeContextMenuRenderer ? (jsx(NodeContextMenuRenderer, { open: nodeMenuOpen, clientPos: nodeMenuPos, nodeId: nodeAtMenu, handlers: nodeContextMenuHandlers, canRunPull: canRunPull, bakeableOutputs: bakeableOutputs })) : (jsx(NodeContextMenu, { open: nodeMenuOpen, clientPos: nodeMenuPos, nodeId: nodeAtMenu, handlers: nodeContextMenuHandlers, canRunPull: canRunPull, bakeableOutputs: bakeableOutputs }))), selectionMenuOpen && selectionMenuPos && (jsx(SelectionContextMenu, { open: selectionMenuOpen, clientPos: selectionMenuPos, handlers: selectionContextMenuHandlers }))] }) }) }));
|
|
4240
4519
|
});
|
|
4241
4520
|
|
|
4242
4521
|
function WorkbenchStudioCanvas({ setRegistry, autoScroll, onAutoScrollChange, example, onExampleChange, engine, onEngineChange, backendKind, onBackendKindChange, httpBaseUrl, onHttpBaseUrlChange, wsUrl, onWsUrlChange, debug, onDebugChange, showValues, onShowValuesChange, hideWorkbench, onHideWorkbenchChange, overrides, onInit, onChange, }) {
|
|
@@ -4773,5 +5052,5 @@ function WorkbenchStudio({ engine, onEngineChange, example, onExampleChange, bac
|
|
|
4773
5052
|
return (jsx(WorkbenchProvider, { wb: wb, runner: runner, registry: registry, setRegistry: setRegistry, overrides: overrides, uiVersion: uiVersion, children: jsx(WorkbenchStudioCanvas, { setRegistry: setRegistry, autoScroll: autoScroll, onAutoScrollChange: onAutoScrollChange, example: example, onExampleChange: onExampleChange, engine: engine, onEngineChange: onEngineChange, backendKind: backendKind, onBackendKindChange: onBackendKindChangeWithDispose, httpBaseUrl: httpBaseUrl, onHttpBaseUrlChange: onHttpBaseUrlChange, wsUrl: wsUrl, onWsUrlChange: onWsUrlChange, debug: debug, onDebugChange: onDebugChange, showValues: showValues, onShowValuesChange: onShowValuesChange, hideWorkbench: hideWorkbench, onHideWorkbenchChange: onHideWorkbenchChange, overrides: overrides, onInit: onInit, onChange: onChange }) }));
|
|
4774
5053
|
}
|
|
4775
5054
|
|
|
4776
|
-
export { AbstractWorkbench, CLIWorkbench, DefaultNode, DefaultNodeContent, DefaultNodeHeader, DefaultUIExtensionRegistry, InMemoryWorkbench, Inspector, LocalGraphRunner, NodeHandles, RemoteGraphRunner, WorkbenchCanvas, WorkbenchContext, WorkbenchProvider, WorkbenchStudio, computeEffectiveHandles, countVisibleHandles, createHandleBounds, createHandleLayout, download, estimateNodeSize, formatDataUrlAsLabel, formatDeclaredTypeSignature, getHandleBoundsX, getHandleBoundsY, getHandleClassName, getHandleLayoutY, getNodeBorderClassNames, layoutNode, preformatValueForDisplay, prettyHandle, resolveOutputDisplay, summarizeDeep, toReactFlow, upload, useQueryParamBoolean, useQueryParamString, useThrottledValue, useWorkbenchBridge, useWorkbenchContext, useWorkbenchGraphTick, useWorkbenchGraphUiTick, useWorkbenchVersionTick };
|
|
5055
|
+
export { AbstractWorkbench, CLIWorkbench, DefaultNode, DefaultNodeContent, DefaultNodeHeader, DefaultUIExtensionRegistry, InMemoryWorkbench, Inspector, LocalGraphRunner, NodeHandles, RemoteGraphRunner, WorkbenchCanvas, WorkbenchContext, WorkbenchProvider, WorkbenchStudio, computeEffectiveHandles, countVisibleHandles, createCopyHandler, createDefaultContextMenuHandlers, createHandleBounds, createHandleLayout, createNodeContextMenuHandlers, createNodeCopyHandler, createSelectionContextMenuHandlers, download, estimateNodeSize, formatDataUrlAsLabel, formatDeclaredTypeSignature, getBakeableOutputs, getHandleBoundsX, getHandleBoundsY, getHandleClassName, getHandleLayoutY, getNodeBorderClassNames, layoutNode, preformatValueForDisplay, prettyHandle, resolveOutputDisplay, summarizeDeep, toReactFlow, upload, useQueryParamBoolean, useQueryParamString, useThrottledValue, useWorkbenchBridge, useWorkbenchContext, useWorkbenchGraphTick, useWorkbenchGraphUiTick, useWorkbenchVersionTick };
|
|
4777
5056
|
//# sourceMappingURL=index.js.map
|