@bian-womp/spark-workbench 0.1.16 → 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 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, 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
+ }
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;
@@ -1607,10 +1702,8 @@ const DefaultNode = React.memo(function DefaultNode({ id, data, selected, isConn
1607
1702
  const title = vIssues
1608
1703
  .map((v) => `${v.code}: ${v.message}`)
1609
1704
  .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}`));
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}`));
1614
1707
  })] }));
1615
1708
  });
1616
1709
  DefaultNode.displayName = "DefaultNode";
@@ -2106,6 +2199,37 @@ function WorkbenchStudioCanvas({ setRegistry, autoScroll, onAutoScrollChange, ex
2106
2199
  const baseToString = React.useCallback((typeId, value) => {
2107
2200
  if (value === undefined || value === null)
2108
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
+ }
2109
2233
  if (typeId && typeId.includes("enum:")) {
2110
2234
  const n = Number(value);
2111
2235
  const label = registry.enums.get(typeId)?.valueToLabel.get(n);
@@ -2203,7 +2327,11 @@ exports.WorkbenchCanvas = WorkbenchCanvas;
2203
2327
  exports.WorkbenchContext = WorkbenchContext;
2204
2328
  exports.WorkbenchProvider = WorkbenchProvider;
2205
2329
  exports.WorkbenchStudio = WorkbenchStudio;
2330
+ exports.formatDeclaredTypeSignature = formatDeclaredTypeSignature;
2206
2331
  exports.getNodeBorderClassNames = getNodeBorderClassNames;
2332
+ exports.preformatValueForDisplay = preformatValueForDisplay;
2333
+ exports.resolveOutputDisplay = resolveOutputDisplay;
2334
+ exports.summarizeDeep = summarizeDeep;
2207
2335
  exports.toReactFlow = toReactFlow;
2208
2336
  exports.useQueryParamBoolean = useQueryParamBoolean;
2209
2337
  exports.useQueryParamString = useQueryParamString;