@bian-womp/spark-workbench 0.1.13 → 0.1.15

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
@@ -408,6 +408,31 @@ class GraphRunner {
408
408
  await rc.runner.build(def);
409
409
  // Signal UI after remote build as well
410
410
  this.emit("invalidate", { reason: "graph-built" });
411
+ // Hydrate current remote inputs/outputs (including defaults) into cache
412
+ try {
413
+ const snap = await rc.runner.snapshot();
414
+ for (const [nodeId, map] of Object.entries(snap.inputs || {})) {
415
+ for (const [handle, value] of Object.entries(map || {})) {
416
+ rc.valueCache.set(`${nodeId}.${handle}`, {
417
+ io: "input",
418
+ value,
419
+ });
420
+ this.emit("value", { nodeId, handle, value, io: "input" });
421
+ }
422
+ }
423
+ for (const [nodeId, map] of Object.entries(snap.outputs || {})) {
424
+ for (const [handle, value] of Object.entries(map || {})) {
425
+ rc.valueCache.set(`${nodeId}.${handle}`, {
426
+ io: "output",
427
+ value,
428
+ });
429
+ this.emit("value", { nodeId, handle, value, io: "output" });
430
+ }
431
+ }
432
+ }
433
+ catch {
434
+ console.error("Failed to hydrate remote inputs/outputs");
435
+ }
411
436
  const eng = rc.runner.getEngine();
412
437
  if (!rc.listenersBound) {
413
438
  eng.on("value", (e) => {
@@ -452,32 +477,13 @@ class GraphRunner {
452
477
  Object.assign(this.stagedInputs[nodeId], inputs);
453
478
  // Local running: pause, set all inputs, resume, schedule a single recompute
454
479
  if (this.backend.kind === "local" && this.engine && this.runtime) {
455
- this.runtime.pause();
456
- try {
457
- for (const [handle, value] of Object.entries(inputs)) {
458
- this.engine.setInput(nodeId, handle, value);
459
- }
460
- }
461
- finally {
462
- this.runtime.resume();
463
- try {
464
- this.runtime.__unsafe_scheduleInputsChanged(nodeId);
465
- }
466
- catch { }
467
- }
480
+ this.engine.setInputs(nodeId, inputs);
468
481
  }
469
482
  // Remote running: forward inputs individually (no batch API available)
470
- else if (this.engine && this.backend.kind !== "local") {
471
- // Prefer batch if supported by remote engine
472
- if (this.engine instanceof sparkRemote.RemoteEngine) {
473
- this.engine.setInputs(nodeId, inputs);
474
- }
475
- else {
476
- console.warn("Remote engine does not support setInputs");
477
- for (const [handle, value] of Object.entries(inputs)) {
478
- this.engine.setInput(nodeId, handle, value);
479
- }
480
- }
483
+ else if (this.engine &&
484
+ this.backend.kind !== "local" &&
485
+ this.engine instanceof sparkRemote.RemoteEngine) {
486
+ this.engine.setInputs(nodeId, inputs);
481
487
  }
482
488
  // Not running: emit value events so UI reflects staged values
483
489
  else if (!this.engine) {
@@ -552,7 +558,7 @@ class GraphRunner {
552
558
  for (const n of def.nodes) {
553
559
  const staged = this.stagedInputs[n.nodeId] ?? {};
554
560
  const runtimeInputs = this.runtime
555
- ? this.runtime.__unsafe_getNodeData?.(n.nodeId)?.inputs ?? {}
561
+ ? this.runtime.getNodeData?.(n.nodeId)?.inputs ?? {}
556
562
  : {};
557
563
  if (this.isRunning()) {
558
564
  out[n.nodeId] = runtimeInputs;
@@ -877,7 +883,7 @@ function getNodeBorderClassNames(args) {
877
883
  const status = args.status || {};
878
884
  const issues = args.validation?.issues ?? [];
879
885
  const hasError = !!status.lastError;
880
- const hasValidationError = issues.some((i) => i?.level === "error");
886
+ const hasValidationError = issues.some((i) => i.level === "error");
881
887
  const hasValidationWarning = !hasValidationError && issues.length > 0;
882
888
  const isRunning = !!status.activeRuns;
883
889
  const isInvalid = !!status.invalidated && !isRunning && !hasError;
@@ -979,7 +985,8 @@ function WorkbenchProvider({ wb, runner, registry, setRegistry, children, }) {
979
985
  const add = (source, type) => (payload) => setEvents((prev) => {
980
986
  if (source === "workbench" &&
981
987
  (type === "graphChanged" || type === "graphUiChanged")) {
982
- const changeType = payload?.change?.type;
988
+ const changeType = payload
989
+ .change?.type;
983
990
  if (changeType === "moveNode" || changeType === "moveNodes")
984
991
  return prev;
985
992
  }
@@ -1736,6 +1743,45 @@ function WorkbenchStudioCanvas({ setRegistry, autoScroll, onAutoScrollChange, ex
1736
1743
  ? registry.nodes.get(selectedNode.typeId)
1737
1744
  : undefined;
1738
1745
  const [exampleState, setExampleState] = React.useState(example ?? "simple");
1746
+ const defaultExamples = React.useMemo(() => [
1747
+ {
1748
+ id: "simple",
1749
+ label: "Simple",
1750
+ load: async () => ({
1751
+ registry: sparkGraph.createSimpleGraphRegistry(),
1752
+ def: sparkGraph.createSimpleGraphDef(),
1753
+ }),
1754
+ },
1755
+ {
1756
+ id: "async",
1757
+ label: "Async Chain",
1758
+ load: async () => ({
1759
+ registry: sparkGraph.createAsyncGraphRegistry(),
1760
+ def: sparkGraph.createAsyncGraphDef(),
1761
+ }),
1762
+ },
1763
+ {
1764
+ id: "progress",
1765
+ label: "Progress + Errors",
1766
+ load: async () => ({
1767
+ registry: sparkGraph.createProgressGraphRegistry(),
1768
+ def: sparkGraph.createProgressGraphDef(),
1769
+ }),
1770
+ },
1771
+ {
1772
+ id: "validation",
1773
+ label: "Validation",
1774
+ load: async () => ({
1775
+ registry: sparkGraph.createValidationGraphRegistry(),
1776
+ def: sparkGraph.createValidationGraphDef(),
1777
+ }),
1778
+ },
1779
+ ], []);
1780
+ const examples = React.useMemo(() => {
1781
+ if (overrides?.getExamples)
1782
+ return overrides.getExamples(defaultExamples);
1783
+ return defaultExamples;
1784
+ }, [overrides, defaultExamples]);
1739
1785
  const lastAutoLaunched = React.useRef(undefined);
1740
1786
  const autoLayoutRan = React.useRef(false);
1741
1787
  const applyExample = React.useCallback(async (key) => {
@@ -1743,46 +1789,21 @@ function WorkbenchStudioCanvas({ setRegistry, autoScroll, onAutoScrollChange, ex
1743
1789
  alert(`Stop engine before switching example.`);
1744
1790
  return;
1745
1791
  }
1746
- switch (key) {
1747
- case "simple": {
1748
- const r = sparkGraph.createSimpleGraphRegistry();
1749
- setRegistry(r);
1750
- wb.setRegistry(r);
1751
- await wb.load(sparkGraph.createSimpleGraphDef());
1752
- break;
1753
- }
1754
- case "async": {
1755
- const r = sparkGraph.createAsyncGraphRegistry();
1756
- setRegistry(r);
1757
- wb.setRegistry(r);
1758
- await wb.load(sparkGraph.createAsyncGraphDef());
1759
- break;
1760
- }
1761
- case "progress": {
1762
- const r = sparkGraph.createProgressGraphRegistry();
1763
- setRegistry(r);
1764
- wb.setRegistry(r);
1765
- await wb.load(sparkGraph.createProgressGraphDef());
1766
- break;
1767
- }
1768
- case "validation": {
1769
- const r = sparkGraph.createValidationGraphRegistry();
1770
- setRegistry(r);
1771
- wb.setRegistry(r);
1772
- await wb.load(sparkGraph.createValidationGraphDef());
1773
- break;
1774
- }
1775
- default: {
1776
- const r = sparkGraph.createSimpleGraphRegistry();
1777
- setRegistry(r);
1778
- wb.setRegistry(r);
1779
- await wb.load(sparkGraph.createSimpleGraphDef());
1780
- }
1792
+ const ex = examples.find((e) => e.id === key) ?? examples[0];
1793
+ if (!ex)
1794
+ return;
1795
+ const { registry: r, def } = await ex.load();
1796
+ if (r) {
1797
+ setRegistry(r);
1798
+ wb.setRegistry(r);
1781
1799
  }
1800
+ await wb.load(def);
1801
+ // Build a local runtime so seeded defaults are visible pre-run
1802
+ runner.build(wb.export());
1782
1803
  runAutoLayout();
1783
1804
  setExampleState(key);
1784
1805
  onExampleChange?.(key);
1785
- }, [runner, wb, onExampleChange, runAutoLayout]);
1806
+ }, [runner, wb, onExampleChange, runAutoLayout, examples, setRegistry]);
1786
1807
  const hydrateFromBackend = React.useCallback(async (kind, base) => {
1787
1808
  try {
1788
1809
  const transport = kind === "remote-http"
@@ -2033,9 +2054,9 @@ function WorkbenchStudioCanvas({ setRegistry, autoScroll, onAutoScrollChange, ex
2033
2054
  return overrides.toElement(baseToElement, { registry });
2034
2055
  return baseToElement;
2035
2056
  }, [overrides, baseToElement, registry]);
2036
- 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()
2057
+ 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.jsx("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()
2037
2058
  ? "Stop engine before switching example"
2038
- : undefined, children: [jsxRuntime.jsx("option", { value: "simple", children: "Simple" }), jsxRuntime.jsx("option", { value: "async", children: "Async Chain" }), jsxRuntime.jsx("option", { value: "progress", children: "Progress + Errors" }), jsxRuntime.jsx("option", { value: "validation", children: "Validation" })] }), 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()
2059
+ : undefined, children: 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()
2039
2060
  ? "Stop engine before switching backend"
2040
2061
  : undefined, children: [jsxRuntime.jsx("option", { value: "local", children: "Local" }), jsxRuntime.jsx("option", { value: "remote-http", children: "Remote (HTTP)" }), jsxRuntime.jsx("option", { value: "remote-ws", children: "Remote (WebSocket)" })] }), backendKind === "remote-http" && (jsxRuntime.jsx("input", { className: "ml-2 border border-gray-300 rounded px-2 py-1 w-72", placeholder: "http://127.0.0.1:18080", value: httpBaseUrl, onChange: (e) => onHttpBaseUrlChange(e.target.value) })), backendKind === "remote-ws" && (jsxRuntime.jsx("input", { className: "ml-2 border border-gray-300 rounded px-2 py-1 w-72", placeholder: "ws://127.0.0.1:18081", value: wsUrl, onChange: (e) => onWsUrlChange(e.target.value) })), jsxRuntime.jsxs("select", { className: "border border-gray-300 rounded px-2 py-1", value: runner.getRunningEngine() ?? engine ?? "", onChange: (e) => {
2041
2062
  const kind = e.target.value || undefined;