@bian-womp/spark-workbench 0.1.16 → 0.1.18

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
@@ -827,6 +827,90 @@ function formatDeclaredTypeSignature(declared) {
827
827
  return declared.join(" | ");
828
828
  return declared ?? "";
829
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) {
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
+ }
830
914
 
831
915
  function toReactFlow(def, positions, registry, opts) {
832
916
  const nodeHandleMap = {};
@@ -893,6 +977,7 @@ function toReactFlow(def, positions, registry, opts) {
893
977
  : undefined,
894
978
  animated: isRunning,
895
979
  style,
980
+ label: e.typeId || undefined,
896
981
  };
897
982
  });
898
983
  return { nodes, edges };
@@ -1025,8 +1110,8 @@ function WorkbenchProvider({ wb, runner, registry, setRegistry, children, }) {
1025
1110
  layers.push(layer);
1026
1111
  q.splice(0, q.length, ...next);
1027
1112
  }
1028
- const X = 360;
1029
- const Y = 180;
1113
+ const X = 480;
1114
+ const Y = 240;
1030
1115
  const pos = {};
1031
1116
  layers.forEach((layer, layerIndex) => {
1032
1117
  layer.forEach((id, itemIndex) => {
@@ -1396,7 +1481,8 @@ function DebugEvents({ autoScroll, onAutoScrollChange, hideWorkbench, onHideWork
1396
1481
  }, [rows, autoScroll]);
1397
1482
  const renderPayload = (v) => {
1398
1483
  try {
1399
- return JSON.stringify(v, null, 0);
1484
+ const summarized = summarizeDeep(v);
1485
+ return JSON.stringify(summarized, null, 0);
1400
1486
  }
1401
1487
  catch {
1402
1488
  return String(v);
@@ -1408,9 +1494,18 @@ function DebugEvents({ autoScroll, onAutoScrollChange, hideWorkbench, onHideWork
1408
1494
  function Inspector({ debug, autoScroll, hideWorkbench, onAutoScrollChange, onHideWorkbenchChange, toString, toElement, setInput, }) {
1409
1495
  const safeToString = (typeId, value) => {
1410
1496
  try {
1411
- return typeof toString === "function"
1412
- ? toString(typeId, value)
1413
- : String(value ?? "");
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 ?? "");
1414
1509
  }
1415
1510
  catch {
1416
1511
  return String(value ?? "");
@@ -1521,7 +1616,7 @@ function Inspector({ debug, autoScroll, hideWorkbench, onAutoScrollChange, onHid
1521
1616
  const title = inIssues
1522
1617
  .map((v) => `${v.code}: ${v.message}`)
1523
1618
  .join("; ");
1524
- return (jsxRuntime.jsxs("div", { className: "flex items-center gap-2 mb-1", children: [jsxRuntime.jsxs("label", { className: "w-28", 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
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
1525
1620
  ? String(current)
1526
1621
  : "", onChange: (e) => {
1527
1622
  const val = e.target.value;
@@ -1566,7 +1661,7 @@ const DefaultNode = React.memo(function DefaultNode({ id, data, selected, isConn
1566
1661
  const ROW_SIZE = 22;
1567
1662
  const maxRows = Math.max(inputEntries.length, outputEntries.length);
1568
1663
  const minHeight = HEADER_SIZE + maxRows * ROW_SIZE;
1569
- const minWidth = data.showValues ? 320 : 160;
1664
+ const minWidth = data.showValues ? 320 : 240;
1570
1665
  const topFor = (i) => HEADER_SIZE + i * ROW_SIZE + ROW_SIZE / 2;
1571
1666
  const hasError = !!status.lastError;
1572
1667
  const hasValidationError = validation.issues.some((i) => i.level === "error");
@@ -1599,7 +1694,13 @@ const DefaultNode = React.memo(function DefaultNode({ id, data, selected, isConn
1599
1694
  const title = vIssues
1600
1695
  .map((v) => `${v.code}: ${v.message}`)
1601
1696
  .join("; ");
1602
- return (jsxRuntime.jsxs(React.Fragment, { children: [jsxRuntime.jsx(ReactFlow.Handle, { id: entry.id, type: "target", position: ReactFlow.Position.Left, isConnectable: isConnectable, className: cx("!w-3 !h-3 !bg-white !dark:bg-stone-900 !border-gray-500 dark:!border-gray-400", hasAny && (hasErr ? "!border-red-500" : "!border-amber-500")), style: { left: -5, top: topFor(i) } }), jsxRuntime.jsxs("div", { className: "absolute left-2 text-[11px] text-gray-700 dark:text-gray-300 pointer-events-none", style: { top: topFor(i) - 8 }, 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(entry.typeId, inputValues?.[entry.id]) }))] })] }, `in-${entry.id}`));
1697
+ return (jsxRuntime.jsxs(React.Fragment, { children: [jsxRuntime.jsx(ReactFlow.Handle, { id: entry.id, type: "target", position: ReactFlow.Position.Left, isConnectable: isConnectable, className: cx("!w-3 !h-3 !bg-white !dark:bg-stone-900 !border-gray-500 dark:!border-gray-400", hasAny && (hasErr ? "!border-red-500" : "!border-amber-500")), style: { left: -5, top: topFor(i) } }), jsxRuntime.jsxs("div", { className: "absolute left-2 text-[11px] text-gray-700 dark:text-gray-300 pointer-events-none", style: {
1698
+ top: topFor(i) - 8,
1699
+ right: "50%",
1700
+ whiteSpace: "nowrap",
1701
+ overflow: "hidden",
1702
+ textOverflow: "ellipsis",
1703
+ }, 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(entry.typeId, inputValues?.[entry.id]) }))] })] }, `in-${entry.id}`));
1603
1704
  }), outputEntries.map((entry, i) => {
1604
1705
  const vIssues = validation.outputs.filter((v) => v.handle === entry.id);
1605
1706
  const hasAny = vIssues.length > 0;
@@ -1607,10 +1708,15 @@ const DefaultNode = React.memo(function DefaultNode({ id, data, selected, isConn
1607
1708
  const title = vIssues
1608
1709
  .map((v) => `${v.code}: ${v.message}`)
1609
1710
  .join("; ");
1610
- 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, 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: (() => {
1611
- const { typeId, value } = resolveOutputDisplay(outputValues?.[entry.id], entry.typeId);
1612
- return toString(typeId, value);
1613
- })() }))] })] }, `out-${entry.id}`));
1711
+ const resolved = resolveOutputDisplay(outputValues?.[entry.id], entry.typeId);
1712
+ 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: {
1713
+ top: topFor(i) - 8,
1714
+ textAlign: "right",
1715
+ left: "50%",
1716
+ whiteSpace: "nowrap",
1717
+ overflow: "hidden",
1718
+ textOverflow: "ellipsis",
1719
+ }, 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}`));
1614
1720
  })] }));
1615
1721
  });
1616
1722
  DefaultNode.displayName = "DefaultNode";
@@ -1749,11 +1855,21 @@ function NodeContextMenu({ open, clientPos, nodeId, onClose, }) {
1749
1855
  }, children: [jsxRuntime.jsxs("div", { className: "px-2 py-1 font-semibold text-gray-700", children: ["Node (", nodeId, ")"] }), jsxRuntime.jsx("button", { className: "block w-full text-left px-2 py-1 hover:bg-gray-100", onClick: handleDelete, children: "Delete" }), jsxRuntime.jsx("button", { className: "block w-full text-left px-2 py-1 hover:bg-gray-100", onClick: handleDuplicate, children: "Duplicate" }), canRunPull && (jsxRuntime.jsx("button", { className: "block w-full text-left px-2 py-1 hover:bg-gray-100", onClick: handleRunPull, children: "Run (pull)" })), jsxRuntime.jsx("div", { className: "h-px bg-gray-200 my-1" }), jsxRuntime.jsx("button", { className: "block w-full text-left px-2 py-1 hover:bg-gray-100", onClick: handleCopyId, children: "Copy Node ID" })] }));
1750
1856
  }
1751
1857
 
1752
- function WorkbenchCanvas({ showValues, toString, toElement, }) {
1858
+ const WorkbenchCanvas = React.forwardRef(({ showValues, toString, toElement }, ref) => {
1753
1859
  const { wb, registry, inputsMap, outputsMap, valuesTick, nodeStatus, edgeStatus, validationByNode, validationByEdge, } = useWorkbenchContext();
1754
1860
  const ioValues = { inputs: inputsMap, outputs: outputsMap };
1755
1861
  const nodeValidation = validationByNode;
1756
1862
  const edgeValidation = validationByEdge.errors;
1863
+ // Expose imperative API
1864
+ const rfInstanceRef = React.useRef(null);
1865
+ React.useImperativeHandle(ref, () => ({
1866
+ fitView: () => {
1867
+ try {
1868
+ rfInstanceRef.current?.fitView({ padding: 0.2 });
1869
+ }
1870
+ catch { }
1871
+ },
1872
+ }));
1757
1873
  const { onConnect, onNodesChange, onEdgesChange, onEdgesDelete, onNodesDelete, onSelectionChange, } = useWorkbenchBridge(wb);
1758
1874
  const { nodeTypes, resolveNodeType } = React.useMemo(() => {
1759
1875
  // Build nodeTypes map using UI extension registry
@@ -1829,8 +1945,8 @@ function WorkbenchCanvas({ showValues, toString, toElement, }) {
1829
1945
  const addNodeAt = (typeId, pos) => {
1830
1946
  wb.addNode({ typeId, position: pos });
1831
1947
  };
1832
- return (jsxRuntime.jsx("div", { className: "w-full h-full", onContextMenu: onContextMenu, children: jsxRuntime.jsxs(ReactFlow, { nodes: nodes, edges: edges, nodeTypes: nodeTypes, selectionOnDrag: true, onConnect: onConnect, onEdgesChange: onEdgesChange, onEdgesDelete: onEdgesDelete, onNodesDelete: onNodesDelete, onNodesChange: onNodesChange, onSelectionChange: onSelectionChange, deleteKeyCode: ["Backspace", "Delete"], fitView: true, children: [jsxRuntime.jsx(ReactFlow.Background, {}), jsxRuntime.jsx(ReactFlow.MiniMap, {}), jsxRuntime.jsx(ReactFlow.Controls, {}), jsxRuntime.jsx(DefaultContextMenu, { open: menuOpen, clientPos: menuPos, onAdd: addNodeAt, onClose: () => setMenuOpen(false) }), jsxRuntime.jsx(NodeContextMenu, { open: nodeMenuOpen, clientPos: nodeMenuPos, nodeId: nodeAtMenu, onClose: () => setNodeMenuOpen(false) })] }) }));
1833
- }
1948
+ return (jsxRuntime.jsx("div", { className: "w-full h-full", onContextMenu: onContextMenu, children: jsxRuntime.jsxs(ReactFlow, { nodes: nodes, edges: edges, nodeTypes: nodeTypes, selectionOnDrag: true, onConnect: onConnect, onEdgesChange: onEdgesChange, onEdgesDelete: onEdgesDelete, onNodesDelete: onNodesDelete, onNodesChange: onNodesChange, onSelectionChange: onSelectionChange, deleteKeyCode: ["Backspace", "Delete"], fitView: true, onInit: (inst) => (rfInstanceRef.current = inst), children: [jsxRuntime.jsx(ReactFlow.Background, {}), jsxRuntime.jsx(ReactFlow.MiniMap, {}), jsxRuntime.jsx(ReactFlow.Controls, {}), jsxRuntime.jsx(DefaultContextMenu, { open: menuOpen, clientPos: menuPos, onAdd: addNodeAt, onClose: () => setMenuOpen(false) }), jsxRuntime.jsx(NodeContextMenu, { open: nodeMenuOpen, clientPos: nodeMenuPos, nodeId: nodeAtMenu, onClose: () => setNodeMenuOpen(false) })] }) }));
1949
+ });
1834
1950
 
1835
1951
  function WorkbenchStudioCanvas({ setRegistry, autoScroll, onAutoScrollChange, example, onExampleChange, engine, onEngineChange, backendKind, onBackendKindChange, httpBaseUrl, onHttpBaseUrlChange, wsUrl, onWsUrlChange, debug, onDebugChange, showValues, onShowValuesChange, hideWorkbench, onHideWorkbenchChange, overrides, }) {
1836
1952
  const { wb, runner, registry, def, selectedNodeId, runAutoLayout } = useWorkbenchContext();
@@ -1880,6 +1996,7 @@ function WorkbenchStudioCanvas({ setRegistry, autoScroll, onAutoScrollChange, ex
1880
1996
  }, [overrides, defaultExamples]);
1881
1997
  const lastAutoLaunched = React.useRef(undefined);
1882
1998
  const autoLayoutRan = React.useRef(false);
1999
+ const canvasRef = React.useRef(null);
1883
2000
  const applyExample = React.useCallback(async (key) => {
1884
2001
  if (runner.isRunning()) {
1885
2002
  alert(`Stop engine before switching example.`);
@@ -1900,6 +2017,27 @@ function WorkbenchStudioCanvas({ setRegistry, autoScroll, onAutoScrollChange, ex
1900
2017
  setExampleState(key);
1901
2018
  onExampleChange?.(key);
1902
2019
  }, [runner, wb, onExampleChange, runAutoLayout, examples, setRegistry]);
2020
+ const downloadGraph = React.useCallback(() => {
2021
+ try {
2022
+ const def = wb.export();
2023
+ const pretty = JSON.stringify(def, null, 2);
2024
+ const blob = new Blob([pretty], { type: "application/json" });
2025
+ const url = URL.createObjectURL(blob);
2026
+ const a = document.createElement("a");
2027
+ const d = new Date();
2028
+ const pad = (n) => String(n).padStart(2, "0");
2029
+ const ts = `${pad(d.getMonth() + 1)}${pad(d.getDate())}-${pad(d.getHours())}${pad(d.getMinutes())}`;
2030
+ a.href = url;
2031
+ a.download = `spark-graph-${ts}.json`;
2032
+ document.body.appendChild(a);
2033
+ a.click();
2034
+ a.remove();
2035
+ URL.revokeObjectURL(url);
2036
+ }
2037
+ catch (err) {
2038
+ alert(String(err?.message ?? err));
2039
+ }
2040
+ }, [wb]);
1903
2041
  const hydrateFromBackend = React.useCallback(async (kind, base) => {
1904
2042
  try {
1905
2043
  const transport = kind === "remote-http"
@@ -2106,6 +2244,37 @@ function WorkbenchStudioCanvas({ setRegistry, autoScroll, onAutoScrollChange, ex
2106
2244
  const baseToString = React.useCallback((typeId, value) => {
2107
2245
  if (value === undefined || value === null)
2108
2246
  return "";
2247
+ // Normalize typed wrapper
2248
+ if (sparkGraph.isTypedOutput(value)) {
2249
+ return baseToString(String(value.__spark_type), value.__spark_value);
2250
+ }
2251
+ const pre = preformatValueForDisplay(typeId, value, registry);
2252
+ if (pre !== undefined)
2253
+ return pre;
2254
+ if (typeof value === "object" &&
2255
+ value !== null &&
2256
+ typeof value.url === "string") {
2257
+ const title = value.title || "";
2258
+ const url = String(value.url || "");
2259
+ if (url.startsWith("data:image/")) {
2260
+ try {
2261
+ const semi = url.indexOf(";");
2262
+ const comma = url.indexOf(",");
2263
+ const mime = url
2264
+ .slice(5, semi > 0 ? semi : undefined)
2265
+ .toUpperCase();
2266
+ const b64 = comma >= 0 ? url.slice(comma + 1) : "";
2267
+ const bytes = Math.floor((b64.length * 3) / 4);
2268
+ return title
2269
+ ? `${title} (${mime} ${bytes} bytes)`
2270
+ : `${mime} Data (${bytes} bytes)`;
2271
+ }
2272
+ catch {
2273
+ return title || url.slice(0, 32) + (url.length > 32 ? "…" : "");
2274
+ }
2275
+ }
2276
+ return title || url.slice(0, 32) + (url.length > 32 ? "…" : "");
2277
+ }
2109
2278
  if (typeId && typeId.includes("enum:")) {
2110
2279
  const n = Number(value);
2111
2280
  const label = registry.enums.get(typeId)?.valueToLabel.get(n);
@@ -2167,7 +2336,7 @@ function WorkbenchStudioCanvas({ setRegistry, autoScroll, onAutoScrollChange, ex
2167
2336
  catch (err) {
2168
2337
  alert(String(err?.message ?? err));
2169
2338
  }
2170
- }, disabled: !engine, children: "Start" })), jsxRuntime.jsx("button", { onClick: runAutoLayout, children: "Auto Layout" }), jsxRuntime.jsxs("label", { className: "ml-2 flex items-center gap-1", children: [jsxRuntime.jsx("input", { type: "checkbox", checked: debug, onChange: (e) => onDebugChange(e.target.checked) }), jsxRuntime.jsx("span", { children: "Debug events" })] }), jsxRuntime.jsxs("label", { className: "ml-2 flex items-center gap-1", children: [jsxRuntime.jsx("input", { type: "checkbox", checked: showValues, onChange: (e) => onShowValuesChange(e.target.checked) }), jsxRuntime.jsx("span", { children: "Show values in nodes" })] })] }), jsxRuntime.jsxs("div", { className: "flex flex-1 min-h-0", children: [jsxRuntime.jsx("div", { className: "flex-1 min-w-0", children: jsxRuntime.jsx(WorkbenchCanvas, { showValues: showValues, toString: toString, toElement: toElement }) }), jsxRuntime.jsx(Inspector, { setInput: setInput, debug: debug, autoScroll: autoScroll, hideWorkbench: hideWorkbench, onAutoScrollChange: onAutoScrollChange, onHideWorkbenchChange: onHideWorkbenchChange, toString: toString, toElement: toElement })] })] }));
2339
+ }, disabled: !engine, children: "Start" })), jsxRuntime.jsx("button", { onClick: runAutoLayout, children: "Auto Layout" }), jsxRuntime.jsx("button", { className: "ml-2", onClick: () => canvasRef.current?.fitView?.(), title: "Fit View", children: "Fit View" }), jsxRuntime.jsx("button", { className: "ml-2", onClick: downloadGraph, children: "Download Graph" }), jsxRuntime.jsxs("label", { className: "ml-2 flex items-center gap-1", children: [jsxRuntime.jsx("input", { type: "checkbox", checked: debug, onChange: (e) => onDebugChange(e.target.checked) }), jsxRuntime.jsx("span", { children: "Debug events" })] }), jsxRuntime.jsxs("label", { className: "ml-2 flex items-center gap-1", children: [jsxRuntime.jsx("input", { type: "checkbox", checked: showValues, onChange: (e) => onShowValuesChange(e.target.checked) }), jsxRuntime.jsx("span", { children: "Show values in nodes" })] })] }), jsxRuntime.jsxs("div", { className: "flex flex-1 min-h-0", children: [jsxRuntime.jsx("div", { className: "flex-1 min-w-0", children: jsxRuntime.jsx(WorkbenchCanvas, { ref: canvasRef, showValues: showValues, toString: toString, toElement: toElement }) }), jsxRuntime.jsx(Inspector, { setInput: setInput, debug: debug, autoScroll: autoScroll, hideWorkbench: hideWorkbench, onAutoScrollChange: onAutoScrollChange, onHideWorkbenchChange: onHideWorkbenchChange, toString: toString, toElement: toElement })] })] }));
2171
2340
  }
2172
2341
  function WorkbenchStudio({ engine, onEngineChange, example, onExampleChange, backendKind, onBackendKindChange, httpBaseUrl, onHttpBaseUrlChange, wsUrl, onWsUrlChange, debug, onDebugChange, showValues, onShowValuesChange, hideWorkbench, onHideWorkbenchChange, autoScroll, onAutoScrollChange, overrides, }) {
2173
2342
  const [registry, setRegistry] = React.useState(sparkGraph.createSimpleGraphRegistry());
@@ -2203,7 +2372,11 @@ exports.WorkbenchCanvas = WorkbenchCanvas;
2203
2372
  exports.WorkbenchContext = WorkbenchContext;
2204
2373
  exports.WorkbenchProvider = WorkbenchProvider;
2205
2374
  exports.WorkbenchStudio = WorkbenchStudio;
2375
+ exports.formatDeclaredTypeSignature = formatDeclaredTypeSignature;
2206
2376
  exports.getNodeBorderClassNames = getNodeBorderClassNames;
2377
+ exports.preformatValueForDisplay = preformatValueForDisplay;
2378
+ exports.resolveOutputDisplay = resolveOutputDisplay;
2379
+ exports.summarizeDeep = summarizeDeep;
2207
2380
  exports.toReactFlow = toReactFlow;
2208
2381
  exports.useQueryParamBoolean = useQueryParamBoolean;
2209
2382
  exports.useQueryParamString = useQueryParamString;