@bian-womp/spark-workbench 0.1.21 → 0.1.22
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 +46 -54
- package/lib/cjs/index.cjs.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.map +1 -1
- package/lib/cjs/src/misc/context/WorkbenchContext.provider.d.ts.map +1 -1
- package/lib/cjs/src/misc/value.d.ts +1 -0
- package/lib/cjs/src/misc/value.d.ts.map +1 -1
- package/lib/cjs/src/runtime/GraphRunner.d.ts +7 -1
- package/lib/cjs/src/runtime/GraphRunner.d.ts.map +1 -1
- package/lib/esm/index.js +47 -56
- package/lib/esm/index.js.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.map +1 -1
- package/lib/esm/src/misc/context/WorkbenchContext.provider.d.ts.map +1 -1
- package/lib/esm/src/misc/value.d.ts +1 -0
- package/lib/esm/src/misc/value.d.ts.map +1 -1
- package/lib/esm/src/runtime/GraphRunner.d.ts +7 -1
- package/lib/esm/src/runtime/GraphRunner.d.ts.map +1 -1
- package/package.json +4 -4
package/lib/cjs/index.cjs
CHANGED
|
@@ -327,6 +327,9 @@ class GraphRunner {
|
|
|
327
327
|
this.backend = { kind: "local" };
|
|
328
328
|
if (backend)
|
|
329
329
|
this.backend = backend;
|
|
330
|
+
// Emit initial transport status
|
|
331
|
+
if (this.backend.kind === "local")
|
|
332
|
+
this.emit("transport", { state: "local" });
|
|
330
333
|
}
|
|
331
334
|
build(def) {
|
|
332
335
|
if (this.backend.kind === "local") {
|
|
@@ -615,6 +618,13 @@ class GraphRunner {
|
|
|
615
618
|
this.runningKind = undefined;
|
|
616
619
|
this.emit("status", { running: false, engine: undefined });
|
|
617
620
|
}
|
|
621
|
+
const kind = this.backend.kind === "local"
|
|
622
|
+
? undefined
|
|
623
|
+
: this.backend.kind;
|
|
624
|
+
this.emit("transport", {
|
|
625
|
+
state: this.backend.kind === "local" ? "local" : "disconnected",
|
|
626
|
+
kind,
|
|
627
|
+
});
|
|
618
628
|
}
|
|
619
629
|
isRunning() {
|
|
620
630
|
return !!this.engine;
|
|
@@ -627,6 +637,8 @@ class GraphRunner {
|
|
|
627
637
|
if (this.remote)
|
|
628
638
|
return this.remote;
|
|
629
639
|
let transport;
|
|
640
|
+
const kind = this.backend.kind === "remote-http" ? "remote-http" : "remote-ws";
|
|
641
|
+
this.emit("transport", { state: "connecting", kind });
|
|
630
642
|
if (this.backend.kind === "remote-http") {
|
|
631
643
|
if (!sparkRemote.HttpPollingTransport)
|
|
632
644
|
throw new Error("HttpPollingTransport not available");
|
|
@@ -649,6 +661,7 @@ class GraphRunner {
|
|
|
649
661
|
valueCache: new Map(),
|
|
650
662
|
listenersBound: false,
|
|
651
663
|
};
|
|
664
|
+
this.emit("transport", { state: "connected", kind });
|
|
652
665
|
return this.remote;
|
|
653
666
|
}
|
|
654
667
|
}
|
|
@@ -817,6 +830,19 @@ function useQueryParamString(key, defaultValue) {
|
|
|
817
830
|
return [val, set];
|
|
818
831
|
}
|
|
819
832
|
|
|
833
|
+
function formatDataUrlAsLabel(dataUrl) {
|
|
834
|
+
try {
|
|
835
|
+
const semi = dataUrl.indexOf(";");
|
|
836
|
+
const comma = dataUrl.indexOf(",");
|
|
837
|
+
const mime = dataUrl.slice(5, semi > 0 ? semi : undefined).toUpperCase();
|
|
838
|
+
const b64 = comma >= 0 ? dataUrl.slice(comma + 1) : "";
|
|
839
|
+
const bytes = Math.floor((b64.length * 3) / 4);
|
|
840
|
+
return `${mime} Data (${bytes} bytes)`;
|
|
841
|
+
}
|
|
842
|
+
catch {
|
|
843
|
+
return dataUrl.length > 64 ? dataUrl.slice(0, 64) + "…" : dataUrl;
|
|
844
|
+
}
|
|
845
|
+
}
|
|
820
846
|
function resolveOutputDisplay(raw, declared) {
|
|
821
847
|
if (sparkGraph.isTypedOutput(raw)) {
|
|
822
848
|
return {
|
|
@@ -871,19 +897,8 @@ function preformatValueForDisplay(typeId, value, registry) {
|
|
|
871
897
|
function summarizeDeep(value) {
|
|
872
898
|
// Strings: summarize data URLs and trim extremely long strings
|
|
873
899
|
if (typeof value === "string") {
|
|
874
|
-
if (value.startsWith("data:"))
|
|
875
|
-
|
|
876
|
-
const semi = value.indexOf(";");
|
|
877
|
-
const comma = value.indexOf(",");
|
|
878
|
-
const mime = value.slice(5, semi > 0 ? semi : undefined).toUpperCase();
|
|
879
|
-
const b64 = comma >= 0 ? value.slice(comma + 1) : "";
|
|
880
|
-
const bytes = Math.floor((b64.length * 3) / 4);
|
|
881
|
-
return `${mime} Data (${bytes} bytes)`;
|
|
882
|
-
}
|
|
883
|
-
catch {
|
|
884
|
-
return value.length > 64 ? value.slice(0, 64) + "…" : value;
|
|
885
|
-
}
|
|
886
|
-
}
|
|
900
|
+
if (value.startsWith("data:"))
|
|
901
|
+
return formatDataUrlAsLabel(value);
|
|
887
902
|
return value.length > 512 ? value.slice(0, 512) + "…" : value;
|
|
888
903
|
}
|
|
889
904
|
// Typed output wrapper
|
|
@@ -903,18 +918,8 @@ function summarizeDeep(value) {
|
|
|
903
918
|
if (typeof v === "string" &&
|
|
904
919
|
k.toLowerCase() === "url" &&
|
|
905
920
|
v.startsWith("data:")) {
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
const comma = v.indexOf(",");
|
|
909
|
-
const mime = v.slice(5, semi > 0 ? semi : undefined).toUpperCase();
|
|
910
|
-
const b64 = comma >= 0 ? v.slice(comma + 1) : "";
|
|
911
|
-
const bytes = Math.floor((b64.length * 3) / 4);
|
|
912
|
-
out[k] = `${mime} Data (${bytes} bytes)`;
|
|
913
|
-
continue;
|
|
914
|
-
}
|
|
915
|
-
catch {
|
|
916
|
-
// fallthrough
|
|
917
|
-
}
|
|
921
|
+
out[k] = formatDataUrlAsLabel(v);
|
|
922
|
+
continue;
|
|
918
923
|
}
|
|
919
924
|
out[k] = summarizeDeep(v);
|
|
920
925
|
}
|
|
@@ -1085,11 +1090,14 @@ function WorkbenchProvider({ wb, runner, registry, setRegistry, children, }) {
|
|
|
1085
1090
|
const out = {};
|
|
1086
1091
|
// Local: runtimeTypeId is not stored; derive from typed wrapper in outputsMap
|
|
1087
1092
|
for (const n of def.nodes) {
|
|
1088
|
-
const
|
|
1093
|
+
const outputsDecl = registry.nodes.get(n.typeId)?.outputs ?? {};
|
|
1094
|
+
const handles = Object.keys(outputsDecl);
|
|
1089
1095
|
const cur = {};
|
|
1090
1096
|
for (const h of handles) {
|
|
1091
1097
|
const v = outputsMap[n.nodeId]?.[h];
|
|
1092
|
-
|
|
1098
|
+
const declared = outputsDecl[h];
|
|
1099
|
+
const { typeId } = resolveOutputDisplay(v, declared);
|
|
1100
|
+
cur[h] = typeId;
|
|
1093
1101
|
}
|
|
1094
1102
|
if (Object.keys(cur).length > 0)
|
|
1095
1103
|
out[n.nodeId] = cur;
|
|
@@ -1524,14 +1532,6 @@ function Inspector({ debug, autoScroll, hideWorkbench, onAutoScrollChange, onHid
|
|
|
1524
1532
|
const safeToString = (typeId, value) => {
|
|
1525
1533
|
try {
|
|
1526
1534
|
if (typeof toString === "function") {
|
|
1527
|
-
// Special-case data URLs for readability
|
|
1528
|
-
if (typeof value === "string" && value.startsWith("data:image/")) {
|
|
1529
|
-
const comma = value.indexOf(",");
|
|
1530
|
-
const b64 = comma >= 0 ? value.slice(comma + 1) : "";
|
|
1531
|
-
const bytes = Math.floor((b64.length * 3) / 4);
|
|
1532
|
-
const fmt = value.slice(5, value.indexOf(";")) || "image";
|
|
1533
|
-
return `${fmt.toUpperCase()} Data (${bytes} bytes)`;
|
|
1534
|
-
}
|
|
1535
1535
|
return toString(typeId, value);
|
|
1536
1536
|
}
|
|
1537
1537
|
return String(value ?? "");
|
|
@@ -1747,7 +1747,7 @@ const DefaultNode = React.memo(function DefaultNode({ id, data, selected, isConn
|
|
|
1747
1747
|
whiteSpace: "nowrap",
|
|
1748
1748
|
overflow: "hidden",
|
|
1749
1749
|
textOverflow: "ellipsis",
|
|
1750
|
-
}, title: `${entry.id}: ${entry.typeId}`, children: [entry.id,
|
|
1750
|
+
}, title: `${entry.id}: ${entry.typeId}`, children: [entry.id, 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}`));
|
|
1751
1751
|
})] }));
|
|
1752
1752
|
});
|
|
1753
1753
|
DefaultNode.displayName = "DefaultNode";
|
|
@@ -1981,6 +1981,9 @@ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement }, r
|
|
|
1981
1981
|
|
|
1982
1982
|
function WorkbenchStudioCanvas({ setRegistry, autoScroll, onAutoScrollChange, example, onExampleChange, engine, onEngineChange, backendKind, onBackendKindChange, httpBaseUrl, onHttpBaseUrlChange, wsUrl, onWsUrlChange, debug, onDebugChange, showValues, onShowValuesChange, hideWorkbench, onHideWorkbenchChange, overrides, onInit, onChange, }) {
|
|
1983
1983
|
const { wb, runner, registry, def, selectedNodeId, runAutoLayout } = useWorkbenchContext();
|
|
1984
|
+
const [transportStatus, setTransportStatus] = React.useState({
|
|
1985
|
+
state: "local",
|
|
1986
|
+
});
|
|
1984
1987
|
const selectedNode = def.nodes.find((n) => n.nodeId === selectedNodeId);
|
|
1985
1988
|
const selectedDesc = selectedNode
|
|
1986
1989
|
? registry.nodes.get(selectedNode.typeId)
|
|
@@ -2194,6 +2197,10 @@ function WorkbenchStudioCanvas({ setRegistry, autoScroll, onAutoScrollChange, ex
|
|
|
2194
2197
|
return;
|
|
2195
2198
|
applyExample(example);
|
|
2196
2199
|
}, [example, wb]);
|
|
2200
|
+
React.useEffect(() => {
|
|
2201
|
+
const off = runner.on("transport", (s) => setTransportStatus(s));
|
|
2202
|
+
return () => off();
|
|
2203
|
+
}, [runner]);
|
|
2197
2204
|
React.useEffect(() => {
|
|
2198
2205
|
if (!engine)
|
|
2199
2206
|
return;
|
|
@@ -2337,23 +2344,7 @@ function WorkbenchStudioCanvas({ setRegistry, autoScroll, onAutoScrollChange, ex
|
|
|
2337
2344
|
typeof value.url === "string") {
|
|
2338
2345
|
const title = value.title || "";
|
|
2339
2346
|
const url = String(value.url || "");
|
|
2340
|
-
|
|
2341
|
-
try {
|
|
2342
|
-
const semi = url.indexOf(";");
|
|
2343
|
-
const comma = url.indexOf(",");
|
|
2344
|
-
const mime = url
|
|
2345
|
-
.slice(5, semi > 0 ? semi : undefined)
|
|
2346
|
-
.toUpperCase();
|
|
2347
|
-
const b64 = comma >= 0 ? url.slice(comma + 1) : "";
|
|
2348
|
-
const bytes = Math.floor((b64.length * 3) / 4);
|
|
2349
|
-
return title
|
|
2350
|
-
? `${title} (${mime} ${bytes} bytes)`
|
|
2351
|
-
: `${mime} Data (${bytes} bytes)`;
|
|
2352
|
-
}
|
|
2353
|
-
catch {
|
|
2354
|
-
return title || url.slice(0, 32) + (url.length > 32 ? "…" : "");
|
|
2355
|
-
}
|
|
2356
|
-
}
|
|
2347
|
+
// value.ts handles data URL formatting
|
|
2357
2348
|
return title || url.slice(0, 32) + (url.length > 32 ? "…" : "");
|
|
2358
2349
|
}
|
|
2359
2350
|
if (typeId && typeId.includes("enum:")) {
|
|
@@ -2400,7 +2391,7 @@ function WorkbenchStudioCanvas({ setRegistry, autoScroll, onAutoScrollChange, ex
|
|
|
2400
2391
|
return overrides.toElement(baseToElement, { registry });
|
|
2401
2392
|
return baseToElement;
|
|
2402
2393
|
}, [overrides, baseToElement, registry]);
|
|
2403
|
-
return (jsxRuntime.jsxs("div", { className: "w-full h-screen flex flex-col", children: [jsxRuntime.jsxs("div", { className: "p-2 border-b border-gray-300 flex gap-2 items-center", children: [runner.isRunning() ? (jsxRuntime.jsxs("span", { className: "ml-2 text-sm text-green-700", children: ["Running: ", runner.getRunningEngine()] })) : (jsxRuntime.jsx("span", { className: "ml-2 text-sm text-gray-500", children: "Stopped" })), jsxRuntime.jsx("label", { className: "ml-2 text-sm", children: "Example:" }), jsxRuntime.jsxs("select", { className: "border border-gray-300 rounded px-2 py-1", value: exampleState, onChange: (e) => applyExample(e.target.value), disabled: runner.isRunning(), title: runner.isRunning()
|
|
2394
|
+
return (jsxRuntime.jsxs("div", { className: "w-full h-screen flex flex-col", children: [jsxRuntime.jsxs("div", { className: "p-2 border-b border-gray-300 flex gap-2 items-center", children: [runner.isRunning() ? (jsxRuntime.jsxs("span", { className: "ml-2 text-sm text-green-700", children: ["Running: ", runner.getRunningEngine()] })) : (jsxRuntime.jsx("span", { className: "ml-2 text-sm text-gray-500", children: "Stopped" })), jsxRuntime.jsxs("span", { className: "ml-2 flex items-center gap-1 text-xs", title: transportStatus.kind || undefined, children: [transportStatus.state === "local" && (jsxRuntime.jsx(react.PlugsConnectedIcon, { size: 14, className: "text-gray-500" })), transportStatus.state === "connecting" && (jsxRuntime.jsx(react.ClockClockwiseIcon, { size: 14, className: "text-amber-600 animate-pulse" })), transportStatus.state === "connected" && (jsxRuntime.jsx(react.WifiHighIcon, { size: 14, className: "text-green-600" })), transportStatus.state === "disconnected" && (jsxRuntime.jsx(react.WifiSlashIcon, { size: 14, className: "text-red-600" })), transportStatus.state === "retrying" && (jsxRuntime.jsx(react.ClockClockwiseIcon, { size: 14, className: "text-amber-700 animate-pulse" }))] }), jsxRuntime.jsx("label", { className: "ml-2 text-sm", children: "Example:" }), jsxRuntime.jsxs("select", { className: "border border-gray-300 rounded px-2 py-1", value: exampleState, onChange: (e) => applyExample(e.target.value), disabled: runner.isRunning(), title: runner.isRunning()
|
|
2404
2395
|
? "Stop engine before switching example"
|
|
2405
2396
|
: undefined, children: [jsxRuntime.jsx("option", { value: "", children: "Select Example\u2026" }), examples.map((ex) => (jsxRuntime.jsx("option", { value: ex.id, children: ex.label }, ex.id)))] }), jsxRuntime.jsx("label", { className: "ml-2 text-sm", children: "Backend:" }), jsxRuntime.jsxs("select", { className: "border border-gray-300 rounded px-2 py-1", value: backendKind, onChange: (e) => onBackendKindChange(e.target.value), disabled: runner.isRunning(), title: runner.isRunning()
|
|
2406
2397
|
? "Stop engine before switching backend"
|
|
@@ -2453,6 +2444,7 @@ exports.WorkbenchCanvas = WorkbenchCanvas;
|
|
|
2453
2444
|
exports.WorkbenchContext = WorkbenchContext;
|
|
2454
2445
|
exports.WorkbenchProvider = WorkbenchProvider;
|
|
2455
2446
|
exports.WorkbenchStudio = WorkbenchStudio;
|
|
2447
|
+
exports.formatDataUrlAsLabel = formatDataUrlAsLabel;
|
|
2456
2448
|
exports.formatDeclaredTypeSignature = formatDeclaredTypeSignature;
|
|
2457
2449
|
exports.getNodeBorderClassNames = getNodeBorderClassNames;
|
|
2458
2450
|
exports.preformatValueForDisplay = preformatValueForDisplay;
|