@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 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
- try {
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
- try {
907
- const semi = v.indexOf(";");
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 handles = Object.keys(registry.nodes.get(n.typeId)?.outputs ?? {});
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
- cur[h] = sparkGraph.getTypedOutputTypeId(v);
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, 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}`));
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
- if (url.startsWith("data:image/")) {
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;