@bian-womp/spark-workbench 0.2.18 → 0.2.20
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 +126 -71
- package/lib/cjs/index.cjs.map +1 -1
- package/lib/cjs/src/misc/context/WorkbenchContext.provider.d.ts.map +1 -1
- package/lib/cjs/src/misc/layout.d.ts +53 -0
- package/lib/cjs/src/misc/layout.d.ts.map +1 -0
- package/lib/cjs/src/misc/mapping.d.ts +1 -1
- package/lib/cjs/src/misc/mapping.d.ts.map +1 -1
- package/lib/esm/index.js +126 -71
- package/lib/esm/index.js.map +1 -1
- package/lib/esm/src/misc/context/WorkbenchContext.provider.d.ts.map +1 -1
- package/lib/esm/src/misc/layout.d.ts +53 -0
- package/lib/esm/src/misc/layout.d.ts.map +1 -0
- package/lib/esm/src/misc/mapping.d.ts +1 -1
- package/lib/esm/src/misc/mapping.d.ts.map +1 -1
- package/package.json +4 -4
package/lib/cjs/index.cjs
CHANGED
|
@@ -1183,6 +1183,83 @@ function summarizeDeep(value) {
|
|
|
1183
1183
|
const NODE_HEADER_HEIGHT_PX = 24;
|
|
1184
1184
|
const NODE_ROW_HEIGHT_PX = 22;
|
|
1185
1185
|
|
|
1186
|
+
function computeEffectiveHandles(node, registry) {
|
|
1187
|
+
const desc = registry.nodes.get(node.typeId);
|
|
1188
|
+
const resolved = node.resolvedHandles || {};
|
|
1189
|
+
const inputs = { ...desc?.inputs, ...resolved.inputs };
|
|
1190
|
+
const outputs = { ...desc?.outputs, ...resolved.outputs };
|
|
1191
|
+
const inputDefaults = { ...desc?.inputDefaults, ...resolved.inputDefaults };
|
|
1192
|
+
return { inputs, outputs, inputDefaults };
|
|
1193
|
+
}
|
|
1194
|
+
function countVisibleHandles(handles) {
|
|
1195
|
+
const inputIds = Object.keys(handles.inputs).filter((k) => !sparkGraph.isInputPrivate(handles.inputs, k));
|
|
1196
|
+
const outputIds = Object.keys(handles.outputs);
|
|
1197
|
+
return { inputsCount: inputIds.length, outputsCount: outputIds.length };
|
|
1198
|
+
}
|
|
1199
|
+
function estimateNodeSize(args) {
|
|
1200
|
+
const { node, registry, showValues, overrides } = args;
|
|
1201
|
+
const { inputs, outputs } = computeEffectiveHandles(node, registry);
|
|
1202
|
+
// Count only non-private inputs for rows on left
|
|
1203
|
+
const { inputsCount, outputsCount } = countVisibleHandles({
|
|
1204
|
+
inputs,
|
|
1205
|
+
outputs,
|
|
1206
|
+
});
|
|
1207
|
+
const rows = Math.max(inputsCount, outputsCount);
|
|
1208
|
+
const baseWidth = showValues ? 320 : 240;
|
|
1209
|
+
const width = overrides?.width ?? baseWidth;
|
|
1210
|
+
const height = overrides?.height ?? NODE_HEADER_HEIGHT_PX + rows * NODE_ROW_HEIGHT_PX;
|
|
1211
|
+
return { width, height, inputsCount, outputsCount, rowCount: rows };
|
|
1212
|
+
}
|
|
1213
|
+
function layoutNode(args) {
|
|
1214
|
+
const { node, registry, showValues, overrides } = args;
|
|
1215
|
+
const { inputs, outputs } = computeEffectiveHandles(node, registry);
|
|
1216
|
+
const inputOrder = Object.keys(inputs).filter((k) => !sparkGraph.isInputPrivate(inputs, k));
|
|
1217
|
+
const outputOrder = Object.keys(outputs);
|
|
1218
|
+
const { width, height } = estimateNodeSize({
|
|
1219
|
+
node,
|
|
1220
|
+
registry,
|
|
1221
|
+
showValues,
|
|
1222
|
+
overrides,
|
|
1223
|
+
});
|
|
1224
|
+
const HEADER = NODE_HEADER_HEIGHT_PX;
|
|
1225
|
+
const ROW = NODE_ROW_HEIGHT_PX;
|
|
1226
|
+
const handles = [
|
|
1227
|
+
...inputOrder.map((id, i) => ({
|
|
1228
|
+
id,
|
|
1229
|
+
type: "target",
|
|
1230
|
+
position: react.Position.Left,
|
|
1231
|
+
x: 0,
|
|
1232
|
+
y: HEADER + i * ROW,
|
|
1233
|
+
width: 1,
|
|
1234
|
+
height: ROW + 2,
|
|
1235
|
+
})),
|
|
1236
|
+
...outputOrder.map((id, i) => ({
|
|
1237
|
+
id,
|
|
1238
|
+
type: "source",
|
|
1239
|
+
position: react.Position.Right,
|
|
1240
|
+
x: width - 1,
|
|
1241
|
+
y: HEADER + i * ROW,
|
|
1242
|
+
width: 1,
|
|
1243
|
+
height: ROW + 2,
|
|
1244
|
+
})),
|
|
1245
|
+
];
|
|
1246
|
+
const handleLayout = [
|
|
1247
|
+
...inputOrder.map((id, i) => ({
|
|
1248
|
+
id,
|
|
1249
|
+
type: "target",
|
|
1250
|
+
position: react.Position.Left,
|
|
1251
|
+
y: HEADER + i * ROW + ROW / 2,
|
|
1252
|
+
})),
|
|
1253
|
+
...outputOrder.map((id, i) => ({
|
|
1254
|
+
id,
|
|
1255
|
+
type: "source",
|
|
1256
|
+
position: react.Position.Right,
|
|
1257
|
+
y: HEADER + i * ROW + ROW / 2,
|
|
1258
|
+
})),
|
|
1259
|
+
];
|
|
1260
|
+
return { width, height, inputOrder, outputOrder, handles, handleLayout };
|
|
1261
|
+
}
|
|
1262
|
+
|
|
1186
1263
|
function toReactFlow(def, positions, registry, opts) {
|
|
1187
1264
|
const EDGE_STYLE_ERROR = { stroke: "#ef4444", strokeWidth: 2 };
|
|
1188
1265
|
const EDGE_STYLE_RUNNING = { stroke: "#3b82f6" };
|
|
@@ -1197,54 +1274,31 @@ function toReactFlow(def, positions, registry, opts) {
|
|
|
1197
1274
|
connectedInputs[nid].add(hid);
|
|
1198
1275
|
}
|
|
1199
1276
|
const nodes = def.nodes.map((n) => {
|
|
1200
|
-
const
|
|
1201
|
-
|
|
1202
|
-
const
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1277
|
+
const { inputs: inputSource, outputs: outputSource } = computeEffectiveHandles(n, registry);
|
|
1278
|
+
const overrideSize = opts.getDefaultNodeSize?.(n.typeId);
|
|
1279
|
+
const geom = layoutNode({
|
|
1280
|
+
node: n,
|
|
1281
|
+
registry,
|
|
1282
|
+
showValues: opts.showValues,
|
|
1283
|
+
overrides: overrideSize,
|
|
1284
|
+
});
|
|
1285
|
+
const inputHandles = geom.inputOrder.map((id) => ({
|
|
1286
|
+
id,
|
|
1287
|
+
typeId: sparkGraph.getInputTypeId(inputSource, id),
|
|
1288
|
+
}));
|
|
1289
|
+
const outputHandles = geom.outputOrder.map((id) => ({
|
|
1210
1290
|
id,
|
|
1211
|
-
typeId: formatDeclaredTypeSignature(
|
|
1291
|
+
typeId: formatDeclaredTypeSignature(outputSource[id]),
|
|
1212
1292
|
}));
|
|
1213
1293
|
nodeHandleMap[n.nodeId] = {
|
|
1214
1294
|
inputs: new Set(inputHandles.map((h) => h.id)),
|
|
1215
1295
|
outputs: new Set(outputHandles.map((h) => h.id)),
|
|
1216
1296
|
};
|
|
1217
|
-
//
|
|
1218
|
-
const
|
|
1219
|
-
const
|
|
1220
|
-
const maxRows = Math.max(inputHandles.length, outputHandles.length);
|
|
1221
|
-
// Allow external override to dictate initial size
|
|
1222
|
-
const overrideSize = opts.getDefaultNodeSize?.(n.typeId);
|
|
1223
|
-
const initialWidth = overrideSize?.width ?? (opts.showValues ? 320 : 240);
|
|
1224
|
-
const initialHeight = overrideSize?.height ?? HEADER_SIZE + maxRows * ROW_SIZE;
|
|
1297
|
+
// Shared sizing
|
|
1298
|
+
const initialWidth = geom.width;
|
|
1299
|
+
const initialHeight = geom.height;
|
|
1225
1300
|
// Precompute handle bounds so edges can render immediately without waiting for measurement
|
|
1226
|
-
const handles =
|
|
1227
|
-
// Inputs on the left as targets
|
|
1228
|
-
...inputHandles.map((h, i) => ({
|
|
1229
|
-
id: h.id,
|
|
1230
|
-
type: "target",
|
|
1231
|
-
position: react.Position.Left,
|
|
1232
|
-
x: 0,
|
|
1233
|
-
y: HEADER_SIZE + i * ROW_SIZE,
|
|
1234
|
-
width: 1,
|
|
1235
|
-
height: ROW_SIZE + 2,
|
|
1236
|
-
})),
|
|
1237
|
-
// Outputs on the right as sources
|
|
1238
|
-
...outputHandles.map((h, i) => ({
|
|
1239
|
-
id: h.id,
|
|
1240
|
-
type: "source",
|
|
1241
|
-
position: react.Position.Right,
|
|
1242
|
-
x: initialWidth - 1,
|
|
1243
|
-
y: HEADER_SIZE + i * ROW_SIZE,
|
|
1244
|
-
width: 1,
|
|
1245
|
-
height: ROW_SIZE + 2,
|
|
1246
|
-
})),
|
|
1247
|
-
];
|
|
1301
|
+
const handles = geom.handles;
|
|
1248
1302
|
return {
|
|
1249
1303
|
id: n.nodeId,
|
|
1250
1304
|
data: {
|
|
@@ -1256,20 +1310,7 @@ function toReactFlow(def, positions, registry, opts) {
|
|
|
1256
1310
|
h.id,
|
|
1257
1311
|
!!connectedInputs[n.nodeId]?.has(h.id),
|
|
1258
1312
|
])),
|
|
1259
|
-
handleLayout:
|
|
1260
|
-
...inputHandles.map((h, i) => ({
|
|
1261
|
-
id: h.id,
|
|
1262
|
-
type: "target",
|
|
1263
|
-
position: react.Position.Left,
|
|
1264
|
-
y: HEADER_SIZE + i * ROW_SIZE + ROW_SIZE / 2,
|
|
1265
|
-
})),
|
|
1266
|
-
...outputHandles.map((h, i) => ({
|
|
1267
|
-
id: h.id,
|
|
1268
|
-
type: "source",
|
|
1269
|
-
position: react.Position.Right,
|
|
1270
|
-
y: HEADER_SIZE + i * ROW_SIZE + ROW_SIZE / 2,
|
|
1271
|
-
})),
|
|
1272
|
-
],
|
|
1313
|
+
handleLayout: geom.handleLayout,
|
|
1273
1314
|
showValues: opts.showValues,
|
|
1274
1315
|
renderWidth: initialWidth,
|
|
1275
1316
|
renderHeight: initialHeight,
|
|
@@ -1468,6 +1509,7 @@ function WorkbenchProvider({ wb, runner, registry, setRegistry, children, }) {
|
|
|
1468
1509
|
// Auto layout (simple layered layout)
|
|
1469
1510
|
const runAutoLayout = React.useCallback(() => {
|
|
1470
1511
|
const cur = wb.export();
|
|
1512
|
+
// Build DAG layers by indegree
|
|
1471
1513
|
const indegree = {};
|
|
1472
1514
|
const adj = {};
|
|
1473
1515
|
for (const n of cur.nodes) {
|
|
@@ -1494,14 +1536,36 @@ function WorkbenchProvider({ wb, runner, registry, setRegistry, children, }) {
|
|
|
1494
1536
|
layers.push(layer);
|
|
1495
1537
|
q.splice(0, q.length, ...next);
|
|
1496
1538
|
}
|
|
1497
|
-
|
|
1498
|
-
|
|
1539
|
+
// Size-aware placement: columns by layer, stacking nodes vertically in each column
|
|
1540
|
+
// Use the same sizing heuristic as mapping via estimateNodeSize
|
|
1541
|
+
const H_GAP = 160;
|
|
1542
|
+
const V_GAP = 24;
|
|
1499
1543
|
const pos = {};
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1544
|
+
let curX = 0;
|
|
1545
|
+
for (const layer of layers) {
|
|
1546
|
+
// Compute max width in this layer and individual heights
|
|
1547
|
+
let maxWidth = 0;
|
|
1548
|
+
const heights = {};
|
|
1549
|
+
for (const id of layer) {
|
|
1550
|
+
const node = cur.nodes.find((n) => n.nodeId === id);
|
|
1551
|
+
if (!node)
|
|
1552
|
+
continue;
|
|
1553
|
+
// Prefer showValues sizing similar to node rendering
|
|
1554
|
+
// Lazy import to avoid circular deps at module top
|
|
1555
|
+
const size = estimateNodeSize({ node, registry, showValues: true });
|
|
1556
|
+
heights[id] = size.height;
|
|
1557
|
+
if (size.width > maxWidth)
|
|
1558
|
+
maxWidth = size.width;
|
|
1559
|
+
}
|
|
1560
|
+
// Place nodes in this column
|
|
1561
|
+
let curY = 0;
|
|
1562
|
+
for (const id of layer) {
|
|
1563
|
+
const h = heights[id] ?? 0;
|
|
1564
|
+
pos[id] = { x: curX, y: curY };
|
|
1565
|
+
curY += h + V_GAP;
|
|
1566
|
+
}
|
|
1567
|
+
curX += maxWidth + H_GAP;
|
|
1568
|
+
}
|
|
1505
1569
|
wb.setPositions(pos);
|
|
1506
1570
|
}, [wb]);
|
|
1507
1571
|
const updateEdgeType = React.useCallback((edgeId, typeId) => wb.updateEdgeType(edgeId, typeId), [wb]);
|
|
@@ -1597,15 +1661,6 @@ function WorkbenchProvider({ wb, runner, registry, setRegistry, children, }) {
|
|
|
1597
1661
|
return add("runner", "error")(e);
|
|
1598
1662
|
});
|
|
1599
1663
|
const off3 = runner.on("invalidate", (e) => {
|
|
1600
|
-
if (e?.reason === "graph-updated") {
|
|
1601
|
-
setNodeStatus((s) => {
|
|
1602
|
-
const next = {};
|
|
1603
|
-
for (const n of wb.export().nodes) {
|
|
1604
|
-
next[n.nodeId] = { ...s[n.nodeId], invalidated: true };
|
|
1605
|
-
}
|
|
1606
|
-
return next;
|
|
1607
|
-
});
|
|
1608
|
-
}
|
|
1609
1664
|
// After build/update, pull resolved handles and merge in-place (no graphChanged)
|
|
1610
1665
|
if (e?.reason === "graph-updated" || e?.reason === "graph-built") {
|
|
1611
1666
|
refreshResolvedHandles();
|