@bian-womp/spark-workbench 0.1.15 → 0.1.17
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 +235 -11
- package/lib/cjs/index.cjs.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/DebugEvents.d.ts.map +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/WorkbenchStudio.d.ts +3 -3
- package/lib/cjs/src/misc/WorkbenchStudio.d.ts.map +1 -1
- package/lib/cjs/src/misc/context/WorkbenchContext.provider.d.ts.map +1 -1
- package/lib/cjs/src/misc/mapping.d.ts +1 -0
- package/lib/cjs/src/misc/mapping.d.ts.map +1 -1
- package/lib/cjs/src/misc/value.d.ts +9 -0
- package/lib/cjs/src/misc/value.d.ts.map +1 -0
- package/lib/cjs/src/runtime/GraphRunner.d.ts +2 -0
- package/lib/cjs/src/runtime/GraphRunner.d.ts.map +1 -1
- package/lib/esm/index.js +233 -13
- package/lib/esm/index.js.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/DebugEvents.d.ts.map +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/WorkbenchStudio.d.ts +3 -3
- package/lib/esm/src/misc/WorkbenchStudio.d.ts.map +1 -1
- package/lib/esm/src/misc/context/WorkbenchContext.provider.d.ts.map +1 -1
- package/lib/esm/src/misc/mapping.d.ts +1 -0
- package/lib/esm/src/misc/mapping.d.ts.map +1 -1
- package/lib/esm/src/misc/value.d.ts +9 -0
- package/lib/esm/src/misc/value.d.ts.map +1 -0
- package/lib/esm/src/runtime/GraphRunner.d.ts +2 -0
- package/lib/esm/src/runtime/GraphRunner.d.ts.map +1 -1
- package/package.json +4 -4
package/lib/cjs/index.cjs
CHANGED
|
@@ -439,6 +439,7 @@ class GraphRunner {
|
|
|
439
439
|
rc.valueCache.set(`${e.nodeId}.${e.handle}`, {
|
|
440
440
|
io: e.io,
|
|
441
441
|
value: e.value,
|
|
442
|
+
runtimeTypeId: e.runtimeTypeId,
|
|
442
443
|
});
|
|
443
444
|
this.emit("value", e);
|
|
444
445
|
});
|
|
@@ -808,12 +809,115 @@ function useQueryParamString(key, defaultValue) {
|
|
|
808
809
|
return [val, set];
|
|
809
810
|
}
|
|
810
811
|
|
|
812
|
+
function resolveOutputDisplay(raw, declared) {
|
|
813
|
+
if (sparkGraph.isTypedOutput(raw)) {
|
|
814
|
+
return { typeId: String(raw.__spark_type), value: raw.__spark_value };
|
|
815
|
+
}
|
|
816
|
+
let typeId = undefined;
|
|
817
|
+
if (Array.isArray(declared)) {
|
|
818
|
+
typeId = declared.length === 1 ? declared[0] : undefined;
|
|
819
|
+
}
|
|
820
|
+
else if (typeof declared === "string") {
|
|
821
|
+
typeId = declared.includes("|") ? undefined : declared;
|
|
822
|
+
}
|
|
823
|
+
return { typeId, value: raw };
|
|
824
|
+
}
|
|
825
|
+
function formatDeclaredTypeSignature(declared) {
|
|
826
|
+
if (Array.isArray(declared))
|
|
827
|
+
return declared.join(" | ");
|
|
828
|
+
return declared ?? "";
|
|
829
|
+
}
|
|
830
|
+
// Pre-format common structures for display; return undefined to defer to caller
|
|
831
|
+
function preformatValueForDisplay(typeId, value, registry) {
|
|
832
|
+
if (value === undefined || value === null)
|
|
833
|
+
return "";
|
|
834
|
+
// Unwrap typed outputs
|
|
835
|
+
if (sparkGraph.isTypedOutput(value)) {
|
|
836
|
+
return preformatValueForDisplay(String(value.__spark_type), value.__spark_value, registry);
|
|
837
|
+
}
|
|
838
|
+
// Enums
|
|
839
|
+
if (typeId && typeId.includes("enum:") && registry) {
|
|
840
|
+
const n = Number(value);
|
|
841
|
+
const label = registry.enums.get(typeId)?.valueToLabel.get(n);
|
|
842
|
+
if (label)
|
|
843
|
+
return label;
|
|
844
|
+
}
|
|
845
|
+
// Use deep summarization for strings, arrays and nested objects to avoid huge HTML payloads
|
|
846
|
+
const summarized = summarizeDeep(value);
|
|
847
|
+
if (typeof summarized === "string")
|
|
848
|
+
return summarized;
|
|
849
|
+
// Resource-like objects with url/title (after summarization)
|
|
850
|
+
if (summarized && typeof summarized === "object") {
|
|
851
|
+
const urlMaybe = summarized.url;
|
|
852
|
+
if (typeof urlMaybe === "string") {
|
|
853
|
+
const title = summarized.title || "";
|
|
854
|
+
const shortUrl = urlMaybe.length > 32 ? urlMaybe.slice(0, 32) + "…" : urlMaybe;
|
|
855
|
+
return title ? `${title} (${shortUrl})` : shortUrl;
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
return undefined;
|
|
859
|
+
}
|
|
860
|
+
function summarizeDeep(value, registry) {
|
|
861
|
+
// Strings: summarize data URLs and trim extremely long strings
|
|
862
|
+
if (typeof value === "string") {
|
|
863
|
+
if (value.startsWith("data:")) {
|
|
864
|
+
try {
|
|
865
|
+
const semi = value.indexOf(";");
|
|
866
|
+
const comma = value.indexOf(",");
|
|
867
|
+
const mime = value.slice(5, semi > 0 ? semi : undefined).toUpperCase();
|
|
868
|
+
const b64 = comma >= 0 ? value.slice(comma + 1) : "";
|
|
869
|
+
const bytes = Math.floor((b64.length * 3) / 4);
|
|
870
|
+
return `${mime} Data (${bytes} bytes)`;
|
|
871
|
+
}
|
|
872
|
+
catch {
|
|
873
|
+
return value.length > 64 ? value.slice(0, 64) + "…" : value;
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
return value.length > 512 ? value.slice(0, 512) + "…" : value;
|
|
877
|
+
}
|
|
878
|
+
// Typed output wrapper
|
|
879
|
+
if (sparkGraph.isTypedOutput(value)) {
|
|
880
|
+
return summarizeDeep(value.__spark_value);
|
|
881
|
+
}
|
|
882
|
+
// Arrays
|
|
883
|
+
if (Array.isArray(value)) {
|
|
884
|
+
return value.map((v) => summarizeDeep(v));
|
|
885
|
+
}
|
|
886
|
+
// Objects
|
|
887
|
+
if (value && typeof value === "object") {
|
|
888
|
+
const obj = value;
|
|
889
|
+
const out = {};
|
|
890
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
891
|
+
// Special-case any 'url' field
|
|
892
|
+
if (typeof v === "string" &&
|
|
893
|
+
k.toLowerCase() === "url" &&
|
|
894
|
+
v.startsWith("data:")) {
|
|
895
|
+
try {
|
|
896
|
+
const semi = v.indexOf(";");
|
|
897
|
+
const comma = v.indexOf(",");
|
|
898
|
+
const mime = v.slice(5, semi > 0 ? semi : undefined).toUpperCase();
|
|
899
|
+
const b64 = comma >= 0 ? v.slice(comma + 1) : "";
|
|
900
|
+
const bytes = Math.floor((b64.length * 3) / 4);
|
|
901
|
+
out[k] = `${mime} Data (${bytes} bytes)`;
|
|
902
|
+
continue;
|
|
903
|
+
}
|
|
904
|
+
catch {
|
|
905
|
+
// fallthrough
|
|
906
|
+
}
|
|
907
|
+
}
|
|
908
|
+
out[k] = summarizeDeep(v);
|
|
909
|
+
}
|
|
910
|
+
return out;
|
|
911
|
+
}
|
|
912
|
+
return value;
|
|
913
|
+
}
|
|
914
|
+
|
|
811
915
|
function toReactFlow(def, positions, registry, opts) {
|
|
812
916
|
const nodeHandleMap = {};
|
|
813
917
|
const nodes = def.nodes.map((n) => {
|
|
814
918
|
const desc = registry.nodes.get(n.typeId);
|
|
815
919
|
const inputHandles = Object.entries(desc?.inputs ?? {}).map(([id, typeId]) => ({ id, typeId }));
|
|
816
|
-
const outputHandles = Object.entries(desc?.outputs ?? {}).map(([id, typeId]) => ({ id, typeId }));
|
|
920
|
+
const outputHandles = Object.entries(desc?.outputs ?? {}).map(([id, typeId]) => ({ id, typeId: formatDeclaredTypeSignature(typeId) }));
|
|
817
921
|
nodeHandleMap[n.nodeId] = {
|
|
818
922
|
inputs: new Set(inputHandles.map((h) => h.id)),
|
|
819
923
|
outputs: new Set(outputHandles.map((h) => h.id)),
|
|
@@ -873,6 +977,7 @@ function toReactFlow(def, positions, registry, opts) {
|
|
|
873
977
|
: undefined,
|
|
874
978
|
animated: isRunning,
|
|
875
979
|
style,
|
|
980
|
+
label: e.typeId || undefined,
|
|
876
981
|
};
|
|
877
982
|
});
|
|
878
983
|
return { nodes, edges };
|
|
@@ -913,6 +1018,41 @@ function WorkbenchProvider({ wb, runner, registry, setRegistry, children, }) {
|
|
|
913
1018
|
const [edgeStatus, setEdgeStatus] = React.useState({});
|
|
914
1019
|
const [events, setEvents] = React.useState([]);
|
|
915
1020
|
const clearEvents = React.useCallback(() => setEvents([]), []);
|
|
1021
|
+
// Fallback progress animation: drive progress to 100% over ~2 minutes
|
|
1022
|
+
const FALLBACK_TOTAL_MS = 2 * 60 * 1000;
|
|
1023
|
+
const [fallbackStarts, setFallbackStarts] = React.useState({});
|
|
1024
|
+
// Track runs that emitted an error so we can keep progress on completion
|
|
1025
|
+
const [errorRuns, setErrorRuns] = React.useState({});
|
|
1026
|
+
// Periodically advance fallback progress for running nodes without explicit progress
|
|
1027
|
+
React.useEffect(() => {
|
|
1028
|
+
const interval = setInterval(() => {
|
|
1029
|
+
setNodeStatus((prev) => {
|
|
1030
|
+
let changed = false;
|
|
1031
|
+
const next = { ...prev };
|
|
1032
|
+
const now = Date.now();
|
|
1033
|
+
for (const id of Object.keys(prev)) {
|
|
1034
|
+
const st = prev[id];
|
|
1035
|
+
if (!st)
|
|
1036
|
+
continue;
|
|
1037
|
+
const runs = st.activeRuns ?? 0;
|
|
1038
|
+
const startAt = fallbackStarts[id];
|
|
1039
|
+
if (runs > 0 && startAt) {
|
|
1040
|
+
const cur = Math.max(0, Math.min(1, Number(st.progress) || 0));
|
|
1041
|
+
const elapsed = Math.max(0, now - startAt);
|
|
1042
|
+
// Approach 100% over the target window, but cap below 1 until done
|
|
1043
|
+
const target = Math.max(0, Math.min(0.99, elapsed / FALLBACK_TOTAL_MS));
|
|
1044
|
+
const merged = Math.max(cur, target);
|
|
1045
|
+
if (merged > cur + 0.005 && merged <= 1) {
|
|
1046
|
+
next[id] = { ...st, progress: merged };
|
|
1047
|
+
changed = true;
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
1051
|
+
return changed ? next : prev;
|
|
1052
|
+
});
|
|
1053
|
+
}, 200);
|
|
1054
|
+
return () => clearInterval(interval);
|
|
1055
|
+
}, [fallbackStarts]);
|
|
916
1056
|
// Validation
|
|
917
1057
|
const [validation, setValidation] = React.useState(undefined);
|
|
918
1058
|
// Selection (mirror workbench selectionChanged)
|
|
@@ -970,8 +1110,8 @@ function WorkbenchProvider({ wb, runner, registry, setRegistry, children, }) {
|
|
|
970
1110
|
layers.push(layer);
|
|
971
1111
|
q.splice(0, q.length, ...next);
|
|
972
1112
|
}
|
|
973
|
-
const X =
|
|
974
|
-
const Y =
|
|
1113
|
+
const X = 480;
|
|
1114
|
+
const Y = 240;
|
|
975
1115
|
const pos = {};
|
|
976
1116
|
layers.forEach((layer, layerIndex) => {
|
|
977
1117
|
layer.forEach((id, itemIndex) => {
|
|
@@ -1025,6 +1165,13 @@ function WorkbenchProvider({ wb, runner, registry, setRegistry, children, }) {
|
|
|
1025
1165
|
lastError: nodeError.err,
|
|
1026
1166
|
},
|
|
1027
1167
|
}));
|
|
1168
|
+
// Mark this runId as errored
|
|
1169
|
+
if (nodeError.runId) {
|
|
1170
|
+
setErrorRuns((prev) => ({
|
|
1171
|
+
...prev,
|
|
1172
|
+
[nodeId]: { ...(prev[nodeId] || {}), [nodeError.runId]: true },
|
|
1173
|
+
}));
|
|
1174
|
+
}
|
|
1028
1175
|
}
|
|
1029
1176
|
return add("runner", "error")(e);
|
|
1030
1177
|
});
|
|
@@ -1057,6 +1204,8 @@ function WorkbenchProvider({ wb, runner, registry, setRegistry, children, }) {
|
|
|
1057
1204
|
},
|
|
1058
1205
|
};
|
|
1059
1206
|
});
|
|
1207
|
+
// Start fallback animation window
|
|
1208
|
+
setFallbackStarts((prev) => ({ ...prev, [id]: Date.now() }));
|
|
1060
1209
|
}
|
|
1061
1210
|
else if (s.kind === "node-progress") {
|
|
1062
1211
|
const id = s.nodeId;
|
|
@@ -1070,16 +1219,42 @@ function WorkbenchProvider({ wb, runner, registry, setRegistry, children, }) {
|
|
|
1070
1219
|
}
|
|
1071
1220
|
else if (s.kind === "node-done") {
|
|
1072
1221
|
const id = s.nodeId;
|
|
1222
|
+
const runId = s.runId;
|
|
1073
1223
|
setNodeStatus((prev) => {
|
|
1074
1224
|
const current = prev[id]?.activeRuns ?? 0;
|
|
1225
|
+
const nextActive = current - 1;
|
|
1226
|
+
const hadError = !!(runId && errorRuns[id]?.[runId]);
|
|
1227
|
+
const keepProgress = hadError || nextActive > 0;
|
|
1075
1228
|
return {
|
|
1076
1229
|
...prev,
|
|
1077
1230
|
[id]: {
|
|
1078
1231
|
...prev[id],
|
|
1079
|
-
activeRuns:
|
|
1232
|
+
activeRuns: nextActive,
|
|
1233
|
+
progress: keepProgress ? prev[id]?.progress : 0,
|
|
1080
1234
|
},
|
|
1081
1235
|
};
|
|
1082
1236
|
});
|
|
1237
|
+
// Clear fallback start timestamp if no more active runs
|
|
1238
|
+
setFallbackStarts((prev) => {
|
|
1239
|
+
prev[id];
|
|
1240
|
+
const nextPrev = { ...prev };
|
|
1241
|
+
// If we don't know nextActive here, conservatively clear to stop animation
|
|
1242
|
+
delete nextPrev[id];
|
|
1243
|
+
return nextPrev;
|
|
1244
|
+
});
|
|
1245
|
+
// Clear error flag for this runId
|
|
1246
|
+
if (runId) {
|
|
1247
|
+
setErrorRuns((prev) => {
|
|
1248
|
+
const nodeMap = { ...(prev[id] || {}) };
|
|
1249
|
+
delete nodeMap[runId];
|
|
1250
|
+
const next = { ...prev };
|
|
1251
|
+
if (Object.keys(nodeMap).length === 0)
|
|
1252
|
+
delete next[id];
|
|
1253
|
+
else
|
|
1254
|
+
next[id] = nodeMap;
|
|
1255
|
+
return next;
|
|
1256
|
+
});
|
|
1257
|
+
}
|
|
1083
1258
|
}
|
|
1084
1259
|
else if (s.kind === "edge-start") {
|
|
1085
1260
|
const id = s.edgeId;
|
|
@@ -1306,7 +1481,8 @@ function DebugEvents({ autoScroll, onAutoScrollChange, hideWorkbench, onHideWork
|
|
|
1306
1481
|
}, [rows, autoScroll]);
|
|
1307
1482
|
const renderPayload = (v) => {
|
|
1308
1483
|
try {
|
|
1309
|
-
|
|
1484
|
+
const summarized = summarizeDeep(v);
|
|
1485
|
+
return JSON.stringify(summarized, null, 0);
|
|
1310
1486
|
}
|
|
1311
1487
|
catch {
|
|
1312
1488
|
return String(v);
|
|
@@ -1318,9 +1494,18 @@ function DebugEvents({ autoScroll, onAutoScrollChange, hideWorkbench, onHideWork
|
|
|
1318
1494
|
function Inspector({ debug, autoScroll, hideWorkbench, onAutoScrollChange, onHideWorkbenchChange, toString, toElement, setInput, }) {
|
|
1319
1495
|
const safeToString = (typeId, value) => {
|
|
1320
1496
|
try {
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1497
|
+
if (typeof toString === "function") {
|
|
1498
|
+
// Special-case data URLs for readability
|
|
1499
|
+
if (typeof value === "string" && value.startsWith("data:image/")) {
|
|
1500
|
+
const comma = value.indexOf(",");
|
|
1501
|
+
const b64 = comma >= 0 ? value.slice(comma + 1) : "";
|
|
1502
|
+
const bytes = Math.floor((b64.length * 3) / 4);
|
|
1503
|
+
const fmt = value.slice(5, value.indexOf(";")) || "image";
|
|
1504
|
+
return `${fmt.toUpperCase()} Data (${bytes} bytes)`;
|
|
1505
|
+
}
|
|
1506
|
+
return toString(typeId, value);
|
|
1507
|
+
}
|
|
1508
|
+
return String(value ?? "");
|
|
1324
1509
|
}
|
|
1325
1510
|
catch {
|
|
1326
1511
|
return String(value ?? "");
|
|
@@ -1431,7 +1616,7 @@ function Inspector({ debug, autoScroll, hideWorkbench, onAutoScrollChange, onHid
|
|
|
1431
1616
|
const title = inIssues
|
|
1432
1617
|
.map((v) => `${v.code}: ${v.message}`)
|
|
1433
1618
|
.join("; ");
|
|
1434
|
-
return (jsxRuntime.jsxs("div", { className: "flex items-center gap-2 mb-1", children: [jsxRuntime.jsxs("label", { className: "w-
|
|
1619
|
+
return (jsxRuntime.jsxs("div", { className: "flex items-center gap-2 mb-1", children: [jsxRuntime.jsxs("label", { className: "w-32", children: [h, jsxRuntime.jsx("span", { className: "text-gray-500 ml-1 text-[11px]", children: selectedDesc?.inputs?.[h] })] }), hasValidation && (jsxRuntime.jsx(IssueBadge, { level: hasErr ? "error" : "warning", size: 24, className: "ml-1 w-6 h-6", title: title })), isEnum ? (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: current !== undefined && current !== null
|
|
1435
1620
|
? String(current)
|
|
1436
1621
|
: "", onChange: (e) => {
|
|
1437
1622
|
const val = e.target.value;
|
|
@@ -1447,7 +1632,10 @@ function Inspector({ debug, autoScroll, hideWorkbench, onAutoScrollChange, onHid
|
|
|
1447
1632
|
if (e.key === "Escape")
|
|
1448
1633
|
revert();
|
|
1449
1634
|
}, ...commonProps }))] }, h));
|
|
1450
|
-
}))] }), jsxRuntime.jsxs("div", { children: [jsxRuntime.jsx("div", { className: "font-semibold mb-1", children: "Outputs" }), outputHandles.length === 0 ? (jsxRuntime.jsx("div", { className: "text-gray-500", children: "No outputs" })) : (outputHandles.map((h) => (jsxRuntime.jsxs("div", { className: "flex items-center gap-2 mb-1", children: [jsxRuntime.jsx("label", { className: "w-20", children: h }), jsxRuntime.jsx("div", { className: "flex-1", children:
|
|
1635
|
+
}))] }), jsxRuntime.jsxs("div", { children: [jsxRuntime.jsx("div", { className: "font-semibold mb-1", children: "Outputs" }), outputHandles.length === 0 ? (jsxRuntime.jsx("div", { className: "text-gray-500", children: "No outputs" })) : (outputHandles.map((h) => (jsxRuntime.jsxs("div", { className: "flex items-center gap-2 mb-1", children: [jsxRuntime.jsx("label", { className: "w-20", children: h }), jsxRuntime.jsx("div", { className: "flex-1", children: (() => {
|
|
1636
|
+
const { typeId, value } = resolveOutputDisplay(nodeOutputs[h], selectedDesc?.outputs?.[h]);
|
|
1637
|
+
return toElement(typeId, value);
|
|
1638
|
+
})() }), (() => {
|
|
1451
1639
|
const outIssues = selectedNodeHandleValidation.outputs.filter((m) => m.handle === h);
|
|
1452
1640
|
if (outIssues.length === 0)
|
|
1453
1641
|
return null;
|
|
@@ -1514,7 +1702,8 @@ const DefaultNode = React.memo(function DefaultNode({ id, data, selected, isConn
|
|
|
1514
1702
|
const title = vIssues
|
|
1515
1703
|
.map((v) => `${v.code}: ${v.message}`)
|
|
1516
1704
|
.join("; ");
|
|
1517
|
-
|
|
1705
|
+
const resolved = resolveOutputDisplay(outputValues?.[entry.id], entry.typeId);
|
|
1706
|
+
return (jsxRuntime.jsxs(React.Fragment, { children: [jsxRuntime.jsx(ReactFlow.Handle, { id: entry.id, type: "source", position: ReactFlow.Position.Right, isConnectable: isConnectable, className: cx("!w-3 !h-3 !bg-white !dark:bg-stone-900 !border-gray-500 dark:!border-gray-400 !rounded-none", hasAny && (hasErr ? "!border-red-500" : "!border-amber-500")), style: { right: -5, top: topFor(i) } }), jsxRuntime.jsxs("div", { className: "absolute right-2 text-[11px] text-gray-700 dark:text-gray-300 pointer-events-none", style: { top: topFor(i) - 8, textAlign: "right" }, title: `${entry.id}: ${entry.typeId}`, children: [entry.id, resolved.typeId && (jsxRuntime.jsxs("span", { className: "ml-1 opacity-60", children: ["(", resolved.typeId, ")"] })), hasAny && (jsxRuntime.jsx(IssueBadge, { level: hasErr ? "error" : "warning", size: 12, className: "ml-1", title: title })), showValues && (jsxRuntime.jsx("span", { className: "ml-1 opacity-60", children: toString(resolved.typeId, resolved.value) }))] })] }, `out-${entry.id}`));
|
|
1518
1707
|
})] }));
|
|
1519
1708
|
});
|
|
1520
1709
|
DefaultNode.displayName = "DefaultNode";
|
|
@@ -2010,6 +2199,37 @@ function WorkbenchStudioCanvas({ setRegistry, autoScroll, onAutoScrollChange, ex
|
|
|
2010
2199
|
const baseToString = React.useCallback((typeId, value) => {
|
|
2011
2200
|
if (value === undefined || value === null)
|
|
2012
2201
|
return "";
|
|
2202
|
+
// Normalize typed wrapper
|
|
2203
|
+
if (sparkGraph.isTypedOutput(value)) {
|
|
2204
|
+
return baseToString(String(value.__spark_type), value.__spark_value);
|
|
2205
|
+
}
|
|
2206
|
+
const pre = preformatValueForDisplay(typeId, value, registry);
|
|
2207
|
+
if (pre !== undefined)
|
|
2208
|
+
return pre;
|
|
2209
|
+
if (typeof value === "object" &&
|
|
2210
|
+
value !== null &&
|
|
2211
|
+
typeof value.url === "string") {
|
|
2212
|
+
const title = value.title || "";
|
|
2213
|
+
const url = String(value.url || "");
|
|
2214
|
+
if (url.startsWith("data:image/")) {
|
|
2215
|
+
try {
|
|
2216
|
+
const semi = url.indexOf(";");
|
|
2217
|
+
const comma = url.indexOf(",");
|
|
2218
|
+
const mime = url
|
|
2219
|
+
.slice(5, semi > 0 ? semi : undefined)
|
|
2220
|
+
.toUpperCase();
|
|
2221
|
+
const b64 = comma >= 0 ? url.slice(comma + 1) : "";
|
|
2222
|
+
const bytes = Math.floor((b64.length * 3) / 4);
|
|
2223
|
+
return title
|
|
2224
|
+
? `${title} (${mime} ${bytes} bytes)`
|
|
2225
|
+
: `${mime} Data (${bytes} bytes)`;
|
|
2226
|
+
}
|
|
2227
|
+
catch {
|
|
2228
|
+
return title || url.slice(0, 32) + (url.length > 32 ? "…" : "");
|
|
2229
|
+
}
|
|
2230
|
+
}
|
|
2231
|
+
return title || url.slice(0, 32) + (url.length > 32 ? "…" : "");
|
|
2232
|
+
}
|
|
2013
2233
|
if (typeId && typeId.includes("enum:")) {
|
|
2014
2234
|
const n = Number(value);
|
|
2015
2235
|
const label = registry.enums.get(typeId)?.valueToLabel.get(n);
|
|
@@ -2107,7 +2327,11 @@ exports.WorkbenchCanvas = WorkbenchCanvas;
|
|
|
2107
2327
|
exports.WorkbenchContext = WorkbenchContext;
|
|
2108
2328
|
exports.WorkbenchProvider = WorkbenchProvider;
|
|
2109
2329
|
exports.WorkbenchStudio = WorkbenchStudio;
|
|
2330
|
+
exports.formatDeclaredTypeSignature = formatDeclaredTypeSignature;
|
|
2110
2331
|
exports.getNodeBorderClassNames = getNodeBorderClassNames;
|
|
2332
|
+
exports.preformatValueForDisplay = preformatValueForDisplay;
|
|
2333
|
+
exports.resolveOutputDisplay = resolveOutputDisplay;
|
|
2334
|
+
exports.summarizeDeep = summarizeDeep;
|
|
2111
2335
|
exports.toReactFlow = toReactFlow;
|
|
2112
2336
|
exports.useQueryParamBoolean = useQueryParamBoolean;
|
|
2113
2337
|
exports.useQueryParamString = useQueryParamString;
|