@particle-academy/fancy-flow 0.15.1 → 0.17.0

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/dist/index.cjs CHANGED
@@ -217,6 +217,9 @@ var llmClient = null;
217
217
  function getLlmClient() {
218
218
  return llmClient;
219
219
  }
220
+ function isResolutionFailure(value) {
221
+ return typeof value === "object" && value !== null && "reason" in value && value.reason !== void 0;
222
+ }
220
223
  var workflowResolver = null;
221
224
  function getWorkflowResolver() {
222
225
  return workflowResolver;
@@ -465,7 +468,14 @@ var subflowExecutor = async (ctx) => {
465
468
  `subflow depth limit reached (${maxDepth}) at "${ref}" \u2014 a workflow is referencing itself, directly or through a chain.`
466
469
  );
467
470
  }
468
- const child = await resolver(ref);
471
+ const pinned = config.version === void 0 || config.version === "" ? void 0 : Number(config.version);
472
+ if (pinned !== void 0 && !Number.isInteger(pinned)) {
473
+ ctx.abort(`subflow "${ref}" has a non-integer version pin (${String(config.version)}).`);
474
+ }
475
+ const resolved = await resolver(ref, pinned);
476
+ const child = isResolutionFailure(resolved) ? ctx.abort(
477
+ resolved.reason === "version-mismatch" ? resolved.message ?? `subflow "${ref}" is pinned to version ${pinned}, but the host has ${resolved.available ?? "a different version"}.` : resolved.message ?? `subflow could not resolve workflow "${ref}"`
478
+ ) : resolved;
469
479
  if (!child) ctx.abort(`subflow could not resolve workflow "${ref}"`);
470
480
  const mode = subflowMode(config);
471
481
  const streaming = mode === "stream" || mode === "both";
@@ -689,6 +699,12 @@ var KINDS = [
689
699
  placeholder: "onboarding-v2",
690
700
  description: "Reference resolved by the host's registerWorkflowResolver()."
691
701
  },
702
+ {
703
+ type: "number",
704
+ key: "version",
705
+ label: "Pin to version",
706
+ description: "Optional. Leave blank to always run the child's current version. Pinning fails the run loudly if the child has moved on \u2014 without it, someone edits the child and this flow silently runs different logic."
707
+ },
692
708
  {
693
709
  type: "select",
694
710
  key: "mode",
@@ -10779,6 +10795,8 @@ function JsonField({ value, onChange, rows }) {
10779
10795
  function NodeConfigPanel({
10780
10796
  node,
10781
10797
  onChange,
10798
+ onDelete,
10799
+ deleteLabel = "Delete node",
10782
10800
  header,
10783
10801
  renderCredentialField,
10784
10802
  renderDocumentField,
@@ -10800,7 +10818,21 @@ function NodeConfigPanel({
10800
10818
  /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "ff-panel__empty", children: [
10801
10819
  "Unknown kind: ",
10802
10820
  kindName
10803
- ] })
10821
+ ] }),
10822
+ onDelete && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "ff-panel__actions", children: /* @__PURE__ */ jsxRuntime.jsxs(
10823
+ "button",
10824
+ {
10825
+ type: "button",
10826
+ className: "ff-panel__delete",
10827
+ "data-action": "delete-node",
10828
+ onClick: () => onDelete(node),
10829
+ title: "Delete this node (Del / Backspace)",
10830
+ children: [
10831
+ "\u2715 ",
10832
+ deleteLabel
10833
+ ]
10834
+ }
10835
+ ) })
10804
10836
  ] });
10805
10837
  }
10806
10838
  const setLabel = (label) => onChange({ ...node, data: { ...node.data, label } });
@@ -10866,7 +10898,21 @@ function NodeConfigPanel({
10866
10898
  issues.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "ff-panel__issues", children: issues.map((iss) => /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "ff-panel__issue", children: [
10867
10899
  "\u26A0 ",
10868
10900
  iss.message
10869
- ] }, iss.key)) })
10901
+ ] }, iss.key)) }),
10902
+ onDelete && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "ff-panel__actions", children: /* @__PURE__ */ jsxRuntime.jsxs(
10903
+ "button",
10904
+ {
10905
+ type: "button",
10906
+ className: "ff-panel__delete",
10907
+ "data-action": "delete-node",
10908
+ onClick: () => onDelete(node),
10909
+ title: "Delete this node (Del / Backspace)",
10910
+ children: [
10911
+ "\u2715 ",
10912
+ deleteLabel
10913
+ ]
10914
+ }
10915
+ ) })
10870
10916
  ] });
10871
10917
  }
10872
10918
  function FlowRunControls({ running, onRun, onCancel, onReset, className, style: style2 }) {
@@ -11250,15 +11296,32 @@ function DefaultBody({ config, kind }) {
11250
11296
  ] })
11251
11297
  ] });
11252
11298
  }
11299
+ var truncate = (s, n = 30) => s.length > n ? s.slice(0, n - 1) + "\u2026" : s;
11300
+ function itemLabel(item) {
11301
+ if (item && typeof item === "object") {
11302
+ const o = item;
11303
+ const name = o.label ?? o.name ?? o.key ?? o.title ?? o.id;
11304
+ if (typeof name === "string" && name) return name;
11305
+ if (typeof name === "number") return String(name);
11306
+ return "item";
11307
+ }
11308
+ return String(item ?? "");
11309
+ }
11253
11310
  function previewValue(v) {
11254
- if (typeof v === "string") return v.length > 30 ? v.slice(0, 27) + "\u2026" : v;
11311
+ if (v === null || v === void 0) return "";
11312
+ if (typeof v === "string") return truncate(v);
11255
11313
  if (typeof v === "number" || typeof v === "boolean") return String(v);
11256
- try {
11257
- const j = JSON.stringify(v);
11258
- return j.length > 30 ? j.slice(0, 27) + "\u2026" : j;
11259
- } catch {
11260
- return "[object]";
11314
+ if (Array.isArray(v)) {
11315
+ if (v.length === 0) return "none";
11316
+ const names = v.slice(0, 3).map(itemLabel).filter(Boolean).join(", ");
11317
+ const rest = v.length > 3 ? `, +${v.length - 3}` : "";
11318
+ return names ? truncate(names + rest) : `${v.length} item${v.length === 1 ? "" : "s"}`;
11319
+ }
11320
+ if (typeof v === "object") {
11321
+ const keys = Object.keys(v);
11322
+ return keys.length === 0 ? "empty" : `${keys.length} field${keys.length === 1 ? "" : "s"}`;
11261
11323
  }
11324
+ return "\u2026";
11262
11325
  }
11263
11326
 
11264
11327
  // src/registry/index.ts
@@ -11501,18 +11564,7 @@ function FlowEditorInner({
11501
11564
  const toolbar = slots.toolbar ? slots.toolbar(api) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
11502
11565
  startActions.map((a) => renderAction(a, api)),
11503
11566
  builtins.run !== false && /* @__PURE__ */ jsxRuntime.jsx(FlowRunControls, { running: api.running, onRun: api.run, onCancel: api.cancel, onReset: api.reset }),
11504
- (builtins.delete !== false || builtins.export !== false || builtins.import !== false) && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ff-editor__sep" }),
11505
- builtins.delete !== false && /* @__PURE__ */ jsxRuntime.jsx(
11506
- "button",
11507
- {
11508
- className: "ff-editor__btn",
11509
- "data-action": "delete",
11510
- onClick: api.deleteSelected,
11511
- disabled: api.selected === null,
11512
- title: "Delete the selected node (Del / Backspace)",
11513
- children: "\u2715 Delete"
11514
- }
11515
- ),
11567
+ (builtins.export !== false || builtins.import !== false) && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ff-editor__sep" }),
11516
11568
  builtins.export !== false && /* @__PURE__ */ jsxRuntime.jsx("button", { className: "ff-editor__btn", "data-action": "export", onClick: api.exportWorkflow, children: "\u2193 Export" }),
11517
11569
  builtins.import !== false && /* @__PURE__ */ jsxRuntime.jsx("button", { className: "ff-editor__btn", "data-action": "import", onClick: api.importWorkflow, children: "\u2191 Import" }),
11518
11570
  endActions.map((a) => renderAction(a, api)),
@@ -11643,7 +11695,15 @@ function FlowEditorInner({
11643
11695
  showFeed && (slots.feed ? slots.feed(api) : /* @__PURE__ */ jsxRuntime.jsx(FlowRunFeed, { entries: runner.feed, className: "ff-editor__feed" }))
11644
11696
  ] }),
11645
11697
  showPanel && (slots.panel ? slots.panel(api) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ff-editor__panel-wrap", children: [
11646
- /* @__PURE__ */ jsxRuntime.jsx(NodeConfigPanel, { className: "ff-editor__panel", node: api.selected, onChange: api.updateNode }),
11698
+ /* @__PURE__ */ jsxRuntime.jsx(
11699
+ NodeConfigPanel,
11700
+ {
11701
+ className: "ff-editor__panel",
11702
+ node: api.selected,
11703
+ onChange: api.updateNode,
11704
+ onDelete: builtins.delete === false ? void 0 : (n) => api.deleteNodes([n.id])
11705
+ }
11706
+ ),
11647
11707
  slots.panelFooter && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "ff-editor__panel-footer", children: slots.panelFooter(api) })
11648
11708
  ] }))
11649
11709
  ] });