@bian-womp/spark-workbench 0.2.79 → 0.2.80
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 +110 -4
- package/lib/cjs/index.cjs.map +1 -1
- package/lib/cjs/src/core/InMemoryWorkbench.d.ts +16 -1
- package/lib/cjs/src/core/InMemoryWorkbench.d.ts.map +1 -1
- package/lib/cjs/src/core/contracts.d.ts +3 -0
- package/lib/cjs/src/core/contracts.d.ts.map +1 -1
- package/lib/cjs/src/misc/DefaultNode.d.ts +3 -2
- package/lib/cjs/src/misc/DefaultNode.d.ts.map +1 -1
- package/lib/cjs/src/misc/context/WorkbenchContext.d.ts +2 -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 +110 -4
- package/lib/esm/index.js.map +1 -1
- package/lib/esm/src/core/InMemoryWorkbench.d.ts +16 -1
- package/lib/esm/src/core/InMemoryWorkbench.d.ts.map +1 -1
- package/lib/esm/src/core/contracts.d.ts +3 -0
- package/lib/esm/src/core/contracts.d.ts.map +1 -1
- package/lib/esm/src/misc/DefaultNode.d.ts +3 -2
- package/lib/esm/src/misc/DefaultNode.d.ts.map +1 -1
- package/lib/esm/src/misc/context/WorkbenchContext.d.ts +2 -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/cjs/index.cjs
CHANGED
|
@@ -130,8 +130,9 @@ class InMemoryWorkbench extends AbstractWorkbench {
|
|
|
130
130
|
nodes: [],
|
|
131
131
|
edges: [],
|
|
132
132
|
};
|
|
133
|
-
this.
|
|
133
|
+
this.nodeNames = {};
|
|
134
134
|
this.runtimeState = null;
|
|
135
|
+
this.viewport = null;
|
|
135
136
|
this.historyState = undefined;
|
|
136
137
|
this.copiedData = null;
|
|
137
138
|
}
|
|
@@ -228,6 +229,7 @@ class InMemoryWorkbench extends AbstractWorkbench {
|
|
|
228
229
|
this._def.nodes = this._def.nodes.filter((n) => n.nodeId !== nodeId);
|
|
229
230
|
this._def.edges = this._def.edges.filter((e) => e.source.nodeId !== nodeId && e.target.nodeId !== nodeId);
|
|
230
231
|
delete this.positions[nodeId];
|
|
232
|
+
delete this.nodeNames[nodeId];
|
|
231
233
|
this.emit("graphChanged", {
|
|
232
234
|
def: this._def,
|
|
233
235
|
change: { type: "removeNode", nodeId },
|
|
@@ -350,6 +352,7 @@ class InMemoryWorkbench extends AbstractWorkbench {
|
|
|
350
352
|
const filteredPositions = Object.fromEntries(Object.entries(this.positions).filter(([id]) => defNodeIds.has(id)));
|
|
351
353
|
const filteredNodes = this.selection.nodes.filter((id) => defNodeIds.has(id));
|
|
352
354
|
const filteredEdges = this.selection.edges.filter((id) => defEdgeIds.has(id));
|
|
355
|
+
const filteredNodeNames = Object.fromEntries(Object.entries(this.nodeNames).filter(([id]) => defNodeIds.has(id)));
|
|
353
356
|
return {
|
|
354
357
|
positions: Object.keys(filteredPositions).length > 0
|
|
355
358
|
? filteredPositions
|
|
@@ -361,6 +364,9 @@ class InMemoryWorkbench extends AbstractWorkbench {
|
|
|
361
364
|
}
|
|
362
365
|
: undefined,
|
|
363
366
|
viewport: this.viewport ? { ...this.viewport } : undefined,
|
|
367
|
+
nodeNames: Object.keys(filteredNodeNames).length > 0
|
|
368
|
+
? filteredNodeNames
|
|
369
|
+
: undefined,
|
|
364
370
|
};
|
|
365
371
|
}
|
|
366
372
|
setUIState(ui) {
|
|
@@ -379,6 +385,9 @@ class InMemoryWorkbench extends AbstractWorkbench {
|
|
|
379
385
|
if (ui.viewport) {
|
|
380
386
|
this.viewport = { ...ui.viewport };
|
|
381
387
|
}
|
|
388
|
+
if (ui.nodeNames !== undefined) {
|
|
389
|
+
this.nodeNames = { ...ui.nodeNames };
|
|
390
|
+
}
|
|
382
391
|
}
|
|
383
392
|
getRuntimeState() {
|
|
384
393
|
return this.runtimeState ? { ...this.runtimeState } : null;
|
|
@@ -748,6 +757,29 @@ class InMemoryWorkbench extends AbstractWorkbench {
|
|
|
748
757
|
setCopiedData(data) {
|
|
749
758
|
this.copiedData = data;
|
|
750
759
|
}
|
|
760
|
+
/**
|
|
761
|
+
* Get the custom name for a node, if set.
|
|
762
|
+
*/
|
|
763
|
+
getNodeName(nodeId) {
|
|
764
|
+
return this.nodeNames[nodeId];
|
|
765
|
+
}
|
|
766
|
+
/**
|
|
767
|
+
* Set a custom name for a node. Empty string or undefined removes the custom name.
|
|
768
|
+
* This is included in undo/redo history via extData.ui.
|
|
769
|
+
*/
|
|
770
|
+
setNodeName(nodeId, name, options) {
|
|
771
|
+
if (name === undefined || name.trim() === "") {
|
|
772
|
+
delete this.nodeNames[nodeId];
|
|
773
|
+
}
|
|
774
|
+
else {
|
|
775
|
+
this.nodeNames[nodeId] = name.trim();
|
|
776
|
+
}
|
|
777
|
+
this.emit("graphUiChanged", {
|
|
778
|
+
def: this._def,
|
|
779
|
+
change: { type: "nodeName", nodeId },
|
|
780
|
+
...options,
|
|
781
|
+
});
|
|
782
|
+
}
|
|
751
783
|
}
|
|
752
784
|
|
|
753
785
|
class CLIWorkbench {
|
|
@@ -2947,6 +2979,19 @@ function WorkbenchProvider({ wb, runner, registry, setRegistry, overrides, uiVer
|
|
|
2947
2979
|
}, [wb, wb.def, registry, overrides?.getDefaultNodeSize]);
|
|
2948
2980
|
const updateEdgeType = React.useCallback((edgeId, typeId) => wb.updateEdgeType(edgeId, typeId), [wb]);
|
|
2949
2981
|
const triggerExternal = React.useCallback((nodeId, event) => runner.triggerExternal(nodeId, event), [runner]);
|
|
2982
|
+
const getNodeDisplayName = React.useCallback((nodeId) => {
|
|
2983
|
+
const customName = wb.getNodeName(nodeId);
|
|
2984
|
+
if (customName)
|
|
2985
|
+
return customName;
|
|
2986
|
+
const node = wb.def.nodes.find((n) => n.nodeId === nodeId);
|
|
2987
|
+
if (!node)
|
|
2988
|
+
return nodeId;
|
|
2989
|
+
const desc = registry.nodes.get(node.typeId);
|
|
2990
|
+
return desc?.displayName || node.typeId;
|
|
2991
|
+
}, [wb, registry]);
|
|
2992
|
+
const setNodeName = React.useCallback((nodeId, name) => {
|
|
2993
|
+
wb.setNodeName(nodeId, name, { commit: true, reason: "rename-node" });
|
|
2994
|
+
}, [wb]);
|
|
2950
2995
|
// Helper to save runtime metadata and UI state to extData
|
|
2951
2996
|
const saveUiRuntimeMetadata = React.useCallback(async (workbench, graphRunner) => {
|
|
2952
2997
|
try {
|
|
@@ -3608,6 +3653,8 @@ function WorkbenchProvider({ wb, runner, registry, setRegistry, overrides, uiVer
|
|
|
3608
3653
|
triggerExternal,
|
|
3609
3654
|
uiVersion,
|
|
3610
3655
|
overrides,
|
|
3656
|
+
getNodeDisplayName,
|
|
3657
|
+
setNodeName,
|
|
3611
3658
|
}), [
|
|
3612
3659
|
wb,
|
|
3613
3660
|
runner,
|
|
@@ -3647,6 +3694,8 @@ function WorkbenchProvider({ wb, runner, registry, setRegistry, overrides, uiVer
|
|
|
3647
3694
|
runner,
|
|
3648
3695
|
uiVersion,
|
|
3649
3696
|
overrides,
|
|
3697
|
+
getNodeDisplayName,
|
|
3698
|
+
setNodeName,
|
|
3650
3699
|
]);
|
|
3651
3700
|
return (jsxRuntime.jsx(WorkbenchContext.Provider, { value: value, children: children }));
|
|
3652
3701
|
}
|
|
@@ -4235,11 +4284,27 @@ const DefaultNode = React.memo(function DefaultNode({ id, data, selected, isConn
|
|
|
4235
4284
|
position: "relative",
|
|
4236
4285
|
minWidth: typeof data.renderWidth === "number" ? data.renderWidth : undefined,
|
|
4237
4286
|
minHeight: typeof data.renderHeight === "number" ? data.renderHeight : undefined,
|
|
4238
|
-
}, children: [jsxRuntime.jsx(DefaultNodeHeader, { id: id,
|
|
4287
|
+
}, children: [jsxRuntime.jsx(DefaultNodeHeader, { id: id, typeId: typeId, validation: validation, showId: data.showValues }), jsxRuntime.jsx(DefaultNodeContent, { data: data, isConnectable: isConnectable })] }));
|
|
4239
4288
|
});
|
|
4240
4289
|
DefaultNode.displayName = "DefaultNode";
|
|
4241
|
-
function DefaultNodeHeader({ id, title, validation, right, showId, onInvalidate, }) {
|
|
4290
|
+
function DefaultNodeHeader({ id, typeId, title, validation, right, showId, onInvalidate, }) {
|
|
4242
4291
|
const ctx = useWorkbenchContext();
|
|
4292
|
+
const [isEditing, setIsEditing] = React.useState(false);
|
|
4293
|
+
const [editValue, setEditValue] = React.useState("");
|
|
4294
|
+
const inputRef = React.useRef(null);
|
|
4295
|
+
// Use getNodeDisplayName if typeId is provided, otherwise use title prop
|
|
4296
|
+
const displayName = typeId ? ctx.getNodeDisplayName(id) : (title ?? id);
|
|
4297
|
+
const effectiveTypeId = typeId ?? title ?? id;
|
|
4298
|
+
// Get the default display name (without custom name) for comparison
|
|
4299
|
+
const getDefaultDisplayName = React.useCallback(() => {
|
|
4300
|
+
if (!typeId)
|
|
4301
|
+
return title ?? id;
|
|
4302
|
+
const node = ctx.wb.def.nodes.find((n) => n.nodeId === id);
|
|
4303
|
+
if (!node)
|
|
4304
|
+
return id;
|
|
4305
|
+
const desc = ctx.registry.nodes.get(node.typeId);
|
|
4306
|
+
return desc?.displayName || node.typeId;
|
|
4307
|
+
}, [ctx, id, typeId, title]);
|
|
4243
4308
|
const handleInvalidate = React.useCallback(() => {
|
|
4244
4309
|
try {
|
|
4245
4310
|
if (onInvalidate)
|
|
@@ -4252,10 +4317,51 @@ function DefaultNodeHeader({ id, title, validation, right, showId, onInvalidate,
|
|
|
4252
4317
|
}
|
|
4253
4318
|
catch { }
|
|
4254
4319
|
}, [ctx, id, onInvalidate]);
|
|
4320
|
+
const handleDoubleClick = React.useCallback((e) => {
|
|
4321
|
+
// Only allow editing if typeId is provided (enables renaming)
|
|
4322
|
+
if (!typeId)
|
|
4323
|
+
return;
|
|
4324
|
+
e.stopPropagation();
|
|
4325
|
+
setIsEditing(true);
|
|
4326
|
+
setEditValue(displayName);
|
|
4327
|
+
}, [typeId, displayName]);
|
|
4328
|
+
const handleSave = React.useCallback(() => {
|
|
4329
|
+
if (!typeId)
|
|
4330
|
+
return;
|
|
4331
|
+
const trimmed = editValue.trim();
|
|
4332
|
+
const defaultDisplayName = getDefaultDisplayName();
|
|
4333
|
+
// If the trimmed value matches the default display name or typeId, clear the custom name
|
|
4334
|
+
ctx.setNodeName(id, trimmed === defaultDisplayName || trimmed === effectiveTypeId
|
|
4335
|
+
? undefined
|
|
4336
|
+
: trimmed);
|
|
4337
|
+
setIsEditing(false);
|
|
4338
|
+
}, [ctx, id, editValue, getDefaultDisplayName, effectiveTypeId, typeId]);
|
|
4339
|
+
const handleCancel = React.useCallback(() => {
|
|
4340
|
+
setIsEditing(false);
|
|
4341
|
+
setEditValue(displayName);
|
|
4342
|
+
}, [displayName]);
|
|
4343
|
+
const handleKeyDown = React.useCallback((e) => {
|
|
4344
|
+
if (e.key === "Enter") {
|
|
4345
|
+
e.preventDefault();
|
|
4346
|
+
e.stopPropagation();
|
|
4347
|
+
handleSave();
|
|
4348
|
+
}
|
|
4349
|
+
else if (e.key === "Escape") {
|
|
4350
|
+
e.preventDefault();
|
|
4351
|
+
e.stopPropagation();
|
|
4352
|
+
handleCancel();
|
|
4353
|
+
}
|
|
4354
|
+
}, [handleSave, handleCancel]);
|
|
4355
|
+
React.useEffect(() => {
|
|
4356
|
+
if (isEditing && inputRef.current) {
|
|
4357
|
+
inputRef.current.focus();
|
|
4358
|
+
inputRef.current.select();
|
|
4359
|
+
}
|
|
4360
|
+
}, [isEditing]);
|
|
4255
4361
|
return (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: {
|
|
4256
4362
|
maxHeight: NODE_HEADER_HEIGHT_PX,
|
|
4257
4363
|
minHeight: NODE_HEADER_HEIGHT_PX,
|
|
4258
|
-
}, children: [jsxRuntime.jsx("
|
|
4364
|
+
}, children: [isEditing ? (jsxRuntime.jsx("input", { ref: inputRef, type: "text", value: editValue, onChange: (e) => setEditValue(e.target.value), onBlur: handleSave, onKeyDown: handleKeyDown, onClick: (e) => e.stopPropagation(), onMouseDown: (e) => e.stopPropagation(), className: "flex-1 h-full text-sm bg-transparent border border-blue-500 rounded px-1 outline-none wb-nodrag", style: { lineHeight: `${NODE_HEADER_HEIGHT_PX}px` } })) : (jsxRuntime.jsx("strong", { className: `flex-1 h-full text-sm select-none truncate ${typeId ? "cursor-text" : ""}`, style: { lineHeight: `${NODE_HEADER_HEIGHT_PX}px` }, onDoubleClick: handleDoubleClick, title: typeId ? "Double-click to rename" : undefined, children: displayName })), jsxRuntime.jsxs("div", { className: "flex items-center gap-1", children: [jsxRuntime.jsx("button", { className: "w-4 h-4 border border-gray-400 rounded text-[10px] leading-3 flex items-center justify-center", title: "Invalidate and re-run", onClick: (e) => {
|
|
4259
4365
|
e.stopPropagation();
|
|
4260
4366
|
handleInvalidate();
|
|
4261
4367
|
}, children: jsxRuntime.jsx(react$1.ArrowClockwiseIcon, { size: 10 }) }), right, validation.issues && validation.issues.length > 0 && (jsxRuntime.jsx(IssueBadge, { level: validation.issues.some((i) => i.level === "error")
|