@particle-academy/fancy-flow 0.24.0 → 0.26.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.
Files changed (41) hide show
  1. package/dist/{chunk-OHVSEHDC.js → chunk-N5ICD2WZ.js} +3 -3
  2. package/dist/{chunk-OHVSEHDC.js.map → chunk-N5ICD2WZ.js.map} +1 -1
  3. package/dist/{chunk-ZS6DVGB3.js → chunk-OFW7GN3Y.js} +4 -4
  4. package/dist/{chunk-ZS6DVGB3.js.map → chunk-OFW7GN3Y.js.map} +1 -1
  5. package/dist/{chunk-YXRLIV7A.js → chunk-Q6IPK64P.js} +4 -2
  6. package/dist/chunk-Q6IPK64P.js.map +1 -0
  7. package/dist/{chunk-KDHLKBL2.js → chunk-U2VJB7HR.js} +7 -5
  8. package/dist/chunk-U2VJB7HR.js.map +1 -0
  9. package/dist/{chunk-QAA4CA22.js → chunk-YOFCVO2W.js} +118 -6
  10. package/dist/chunk-YOFCVO2W.js.map +1 -0
  11. package/dist/engine.cjs +4 -2
  12. package/dist/engine.cjs.map +1 -1
  13. package/dist/engine.js +3 -3
  14. package/dist/index.cjs +287 -24
  15. package/dist/index.cjs.map +1 -1
  16. package/dist/index.d.cts +3 -8
  17. package/dist/index.d.ts +3 -8
  18. package/dist/index.js +180 -33
  19. package/dist/index.js.map +1 -1
  20. package/dist/registry/index.d.cts +2 -2
  21. package/dist/registry/index.d.ts +2 -2
  22. package/dist/registry.cjs +111 -2
  23. package/dist/registry.cjs.map +1 -1
  24. package/dist/registry.js +3 -3
  25. package/dist/runtime.cjs +4 -2
  26. package/dist/runtime.cjs.map +1 -1
  27. package/dist/runtime.js +3 -3
  28. package/dist/schema.cjs.map +1 -1
  29. package/dist/schema.js +2 -2
  30. package/dist/styles.css +198 -0
  31. package/dist/styles.css.map +1 -1
  32. package/dist/{types-DwRpXXNy.d.ts → types-BGtR3k9J.d.ts} +1 -1
  33. package/dist/{types-BekE5JTG.d.cts → types-B_pxRqfw.d.cts} +1 -1
  34. package/dist/ux.cjs.map +1 -1
  35. package/dist/ux.d.cts +1 -1
  36. package/dist/ux.d.ts +1 -1
  37. package/dist/ux.js +1 -1
  38. package/package.json +1 -1
  39. package/dist/chunk-KDHLKBL2.js.map +0 -1
  40. package/dist/chunk-QAA4CA22.js.map +0 -1
  41. package/dist/chunk-YXRLIV7A.js.map +0 -1
package/dist/index.cjs CHANGED
@@ -1870,6 +1870,8 @@ function categoryAccent(category) {
1870
1870
  return "#a855f7";
1871
1871
  case "layout":
1872
1872
  return "#64748b";
1873
+ case "annotation":
1874
+ return "#eab308";
1873
1875
  default:
1874
1876
  return "#71717a";
1875
1877
  }
@@ -10976,6 +10978,88 @@ function LaneNodeInner(props) {
10976
10978
  );
10977
10979
  }
10978
10980
  var LaneNode = ReactExports.memo(LaneNodeInner);
10981
+ var FlowEditorContext = ReactExports.createContext(null);
10982
+ var FlowEditorProvider = FlowEditorContext.Provider;
10983
+ function useFlowEditor() {
10984
+ const api = ReactExports.useContext(FlowEditorContext);
10985
+ if (api === null) {
10986
+ throw new Error("useFlowEditor() must be called inside <FlowEditor>.");
10987
+ }
10988
+ return api;
10989
+ }
10990
+ function useFlowEditorOptional() {
10991
+ return ReactExports.useContext(FlowEditorContext);
10992
+ }
10993
+ var NOTE_COLORS = {
10994
+ amber: { bg: "rgba(234, 179, 8, 0.14)", border: "rgba(234, 179, 8, 0.55)", title: "#a16207" },
10995
+ sky: { bg: "rgba(14, 165, 233, 0.12)", border: "rgba(14, 165, 233, 0.5)", title: "#0369a1" },
10996
+ violet: { bg: "rgba(139, 92, 246, 0.13)", border: "rgba(139, 92, 246, 0.5)", title: "#6d28d9" },
10997
+ emerald: { bg: "rgba(16, 185, 129, 0.13)", border: "rgba(16, 185, 129, 0.5)", title: "#047857" },
10998
+ rose: { bg: "rgba(244, 63, 94, 0.12)", border: "rgba(244, 63, 94, 0.5)", title: "#be123c" },
10999
+ slate: { bg: "rgba(100, 116, 139, 0.12)", border: "rgba(100, 116, 139, 0.5)", title: "#475569" }
11000
+ };
11001
+ function coerceColor(v2) {
11002
+ return typeof v2 === "string" && v2 in NOTE_COLORS ? v2 : "amber";
11003
+ }
11004
+ function NoteNodeInner(props) {
11005
+ const data = props.data;
11006
+ const config = data.config ?? {};
11007
+ const api = useFlowEditorOptional();
11008
+ const hasConfigText = typeof config.text === "string" && config.text.length > 0;
11009
+ const text = hasConfigText ? String(config.text) : typeof data.body === "string" ? data.body : typeof data.text === "string" ? data.text : "";
11010
+ const title = typeof config.title === "string" && config.title || (!hasConfigText && typeof data.body === "string" && typeof data.label === "string" ? data.label : "");
11011
+ const skin = NOTE_COLORS[coerceColor(config.color)];
11012
+ const [editing, setEditing] = ReactExports.useState(false);
11013
+ const [draft, setDraft] = ReactExports.useState(text);
11014
+ const areaRef = ReactExports.useRef(null);
11015
+ ReactExports.useEffect(() => {
11016
+ if (editing && areaRef.current) {
11017
+ const el = areaRef.current;
11018
+ el.focus();
11019
+ el.setSelectionRange(el.value.length, el.value.length);
11020
+ }
11021
+ }, [editing]);
11022
+ const commit = () => {
11023
+ setEditing(false);
11024
+ if (!api || draft === text) return;
11025
+ const current = api.nodes.find((n) => n.id === props.id);
11026
+ if (!current) return;
11027
+ api.updateNode({
11028
+ ...current,
11029
+ data: { ...current.data, config: { ...config, text: draft } }
11030
+ });
11031
+ };
11032
+ return /* @__PURE__ */ jsxRuntime.jsxs(
11033
+ "div",
11034
+ {
11035
+ className: ["ff-note", props.selected ? "ff-note--selected" : ""].filter(Boolean).join(" "),
11036
+ style: { background: skin.bg, borderColor: skin.border },
11037
+ onDoubleClick: api ? () => {
11038
+ setDraft(text);
11039
+ setEditing(true);
11040
+ } : void 0,
11041
+ children: [
11042
+ /* @__PURE__ */ jsxRuntime.jsx(NodeResizer, { isVisible: props.selected ?? false, color: skin.border, minWidth: 140, minHeight: 80 }),
11043
+ title && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "ff-note__title", style: { color: skin.title }, children: title }),
11044
+ editing ? /* @__PURE__ */ jsxRuntime.jsx(
11045
+ "textarea",
11046
+ {
11047
+ ref: areaRef,
11048
+ className: "ff-note__input nodrag nowheel",
11049
+ value: draft,
11050
+ onChange: (e) => setDraft(e.target.value),
11051
+ onBlur: commit,
11052
+ onKeyDown: (e) => {
11053
+ if (e.key === "Escape") setEditing(false);
11054
+ if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) commit();
11055
+ }
11056
+ }
11057
+ ) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "ff-note__body", children: text || /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ff-note__placeholder", children: api ? "Double-click to write a note\u2026" : "Note" }) })
11058
+ ]
11059
+ }
11060
+ );
11061
+ }
11062
+ var NoteNode = ReactExports.memo(NoteNodeInner);
10979
11063
 
10980
11064
  // src/registry/capabilities.ts
10981
11065
  var llmClient = null;
@@ -11091,12 +11175,14 @@ async function runFlow(graph, executors, onEvent = () => {
11091
11175
  }
11092
11176
  }
11093
11177
  const visualKind = getNodeKind(node.type ?? "");
11094
- if (node.type === "note" || visualKind?.category === "layout") {
11178
+ const isLayout = visualKind?.category === "layout";
11179
+ const isAnnotation = node.type === "note" || visualKind?.category === "annotation";
11180
+ if (isLayout || isAnnotation) {
11095
11181
  onEvent({
11096
11182
  type: "node-status",
11097
11183
  nodeId: node.id,
11098
11184
  status: "idle",
11099
- text: visualKind?.category === "layout" ? "lane" : "annotation"
11185
+ text: isLayout ? "lane" : "annotation"
11100
11186
  });
11101
11187
  continue;
11102
11188
  }
@@ -12053,6 +12139,35 @@ var KINDS = [
12053
12139
  defaultConfig: { title: "Lane", orientation: "horizontal" },
12054
12140
  resizable: { minWidth: 160, minHeight: 72 },
12055
12141
  component: LaneNode
12142
+ },
12143
+ {
12144
+ // Note — a sticky-note annotation. Portless + visual-only: the runtime skips
12145
+ // the `annotation` category, so a note's text NEVER reaches a runner — it
12146
+ // rides in the document purely for people, editors, and MCP tools. Uses its
12147
+ // own renderer (NoteNode); double-click on the canvas to edit in place.
12148
+ name: "@particle-academy/note",
12149
+ aliases: ["note", "@fancy/note"],
12150
+ category: "annotation",
12151
+ label: "Note",
12152
+ description: "A sticky note that documents the canvas. Never runs \u2014 editor + agent only.",
12153
+ icon: "\u{1F5D2}",
12154
+ inputs: [],
12155
+ outputs: [],
12156
+ configSchema: [
12157
+ { type: "text", key: "title", label: "Title", placeholder: "Optional heading" },
12158
+ { type: "textarea", key: "text", label: "Note", rows: 5, placeholder: "What does this part of the flow do?" },
12159
+ { type: "select", key: "color", label: "Color", default: "amber", options: [
12160
+ { value: "amber", label: "Amber" },
12161
+ { value: "sky", label: "Sky" },
12162
+ { value: "violet", label: "Violet" },
12163
+ { value: "emerald", label: "Emerald" },
12164
+ { value: "rose", label: "Rose" },
12165
+ { value: "slate", label: "Slate" }
12166
+ ] }
12167
+ ],
12168
+ defaultConfig: { text: "", color: "amber" },
12169
+ resizable: { minWidth: 140, minHeight: 80 },
12170
+ component: NoteNode
12056
12171
  }
12057
12172
  ];
12058
12173
  function registerBuiltinKinds() {
@@ -12151,14 +12266,6 @@ function OutputNodeInner(props) {
12151
12266
  return /* @__PURE__ */ jsxRuntime.jsx(NodeShell, { node: props, accent: "#a855f7", tag: "OUTPUT", icon: "\u25CF", showOutputs: false });
12152
12267
  }
12153
12268
  var OutputNode2 = ReactExports.memo(OutputNodeInner);
12154
- function NoteNodeInner({ data, selected: selected2 }) {
12155
- const noteData = data;
12156
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `ff-note ${selected2 ? "ff-note--selected" : ""}`, children: [
12157
- noteData.label && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "ff-note__title", children: noteData.label }),
12158
- noteData.body && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "ff-note__body", children: noteData.body })
12159
- ] });
12160
- }
12161
- var NoteNode = ReactExports.memo(NoteNodeInner);
12162
12269
  function SubgraphNodeInner(props) {
12163
12270
  const data = props.data;
12164
12271
  const childCount = data.childIds?.length ?? 0;
@@ -12545,7 +12652,7 @@ function FlowCanvas({
12545
12652
  }
12546
12653
  );
12547
12654
  }
12548
- var CATEGORY_ORDER = ["trigger", "logic", "data", "ai", "io", "human", "output", "layout", "custom"];
12655
+ var CATEGORY_ORDER = ["trigger", "logic", "data", "ai", "io", "human", "output", "layout", "annotation", "custom"];
12549
12656
  var CATEGORY_LABELS = {
12550
12657
  trigger: "Triggers",
12551
12658
  logic: "Logic",
@@ -12555,6 +12662,7 @@ var CATEGORY_LABELS = {
12555
12662
  human: "Human",
12556
12663
  output: "Output",
12557
12664
  layout: "Layout",
12665
+ annotation: "Notes",
12558
12666
  custom: "Custom"
12559
12667
  };
12560
12668
  function NodePalette({ categories, onPick, className, style: style2 }) {
@@ -13638,6 +13746,7 @@ function portStyle2(i, total) {
13638
13746
  }
13639
13747
  function DefaultBody({ config, kind }) {
13640
13748
  const fields = kind ?? [];
13749
+ if (fields.length === 0) return null;
13641
13750
  const visible = fields.map((f) => ({ field: f, value: config[f.key] })).filter(({ value }) => value !== void 0 && value !== "" && value !== null);
13642
13751
  if (visible.length === 0) {
13643
13752
  return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "ff-node__body-empty", children: "\u2014 configure in the panel" });
@@ -13694,17 +13803,132 @@ function buildNodeTypes() {
13694
13803
  }
13695
13804
  return map;
13696
13805
  }
13697
- var FlowEditorContext = ReactExports.createContext(null);
13698
- var FlowEditorProvider = FlowEditorContext.Provider;
13699
- function useFlowEditor() {
13700
- const api = ReactExports.useContext(FlowEditorContext);
13701
- if (api === null) {
13702
- throw new Error("useFlowEditor() must be called inside <FlowEditor>.");
13703
- }
13704
- return api;
13806
+ function humanInputFields(config) {
13807
+ const raw = Array.isArray(config?.fields) ? config.fields : [];
13808
+ const fields = raw.filter((f) => f && typeof f === "object" && typeof f.key === "string" && f.key).map((f) => ({
13809
+ key: f.key,
13810
+ label: typeof f.label === "string" && f.label ? f.label : f.key,
13811
+ type: ["text", "textarea", "number", "select", "switch"].includes(f.type) ? f.type : "text",
13812
+ required: !!f.required,
13813
+ placeholder: typeof f.placeholder === "string" ? f.placeholder : void 0,
13814
+ options: Array.isArray(f.options) ? f.options : void 0,
13815
+ default: f.default
13816
+ }));
13817
+ if (fields.length) return fields;
13818
+ const title = typeof config?.title === "string" && config.title ? config.title : "Your answer";
13819
+ return [{ key: "value", label: title, type: "textarea", required: true }];
13705
13820
  }
13706
- function useFlowEditorOptional() {
13707
- return ReactExports.useContext(FlowEditorContext);
13821
+ function initialValues(fields) {
13822
+ const v2 = {};
13823
+ for (const f of fields) v2[f.key] = f.type === "switch" ? !!f.default : f.default ?? "";
13824
+ return v2;
13825
+ }
13826
+ function HumanPrompt({ request, onCancel }) {
13827
+ const firstRef = ReactExports.useRef(null);
13828
+ ReactExports.useEffect(() => {
13829
+ firstRef.current?.focus();
13830
+ const onKey = (e) => {
13831
+ if (e.key === "Escape") onCancel();
13832
+ };
13833
+ window.addEventListener("keydown", onKey);
13834
+ return () => window.removeEventListener("keydown", onKey);
13835
+ }, [onCancel]);
13836
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "ff-prompt-overlay", role: "dialog", "aria-modal": "true", "aria-label": request.title, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ff-prompt", onClick: (e) => e.stopPropagation(), children: [
13837
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "ff-prompt__title", children: request.title }),
13838
+ request.kind === "approval" ? /* @__PURE__ */ jsxRuntime.jsx(ApprovalBody, { request, onCancel }) : /* @__PURE__ */ jsxRuntime.jsx(InputBody, { request, onCancel, firstRef })
13839
+ ] }) });
13840
+ }
13841
+ function ApprovalBody({ request, onCancel }) {
13842
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
13843
+ request.description && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "ff-prompt__desc", children: request.description }),
13844
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ff-prompt__actions", children: [
13845
+ /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "ff-prompt__btn ff-prompt__btn--ghost", onClick: onCancel, children: "Cancel" }),
13846
+ /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "ff-prompt__btn ff-prompt__btn--danger", onClick: () => request.resolve(false), children: "Deny" }),
13847
+ /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "ff-prompt__btn ff-prompt__btn--primary", onClick: () => request.resolve(true), children: "Approve" })
13848
+ ] })
13849
+ ] });
13850
+ }
13851
+ function InputBody({
13852
+ request,
13853
+ onCancel,
13854
+ firstRef
13855
+ }) {
13856
+ const [values, setValues] = ReactExports.useState(() => initialValues(request.fields));
13857
+ const set3 = (k2, v2) => setValues((prev) => ({ ...prev, [k2]: v2 }));
13858
+ const missing = request.fields.filter((f) => f.required && (values[f.key] === void 0 || values[f.key] === ""));
13859
+ const submit = () => {
13860
+ if (missing.length === 0) request.resolve(values);
13861
+ };
13862
+ return /* @__PURE__ */ jsxRuntime.jsxs(
13863
+ "form",
13864
+ {
13865
+ onSubmit: (e) => {
13866
+ e.preventDefault();
13867
+ submit();
13868
+ },
13869
+ onKeyDown: (e) => {
13870
+ if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) {
13871
+ e.preventDefault();
13872
+ submit();
13873
+ }
13874
+ },
13875
+ children: [
13876
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "ff-prompt__fields", children: request.fields.map((f, i) => /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "ff-prompt__field", children: [
13877
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "ff-prompt__label", children: [
13878
+ f.label ?? f.key,
13879
+ f.required && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ff-prompt__req", children: " *" })
13880
+ ] }),
13881
+ renderControl(f, values[f.key], (v2) => set3(f.key, v2), i === 0 ? firstRef : void 0)
13882
+ ] }, f.key)) }),
13883
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ff-prompt__actions", children: [
13884
+ /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "ff-prompt__btn ff-prompt__btn--ghost", onClick: onCancel, children: "Cancel" }),
13885
+ /* @__PURE__ */ jsxRuntime.jsx("button", { type: "submit", className: "ff-prompt__btn ff-prompt__btn--primary", disabled: missing.length > 0, children: request.submitLabel || "Continue" })
13886
+ ] })
13887
+ ]
13888
+ }
13889
+ );
13890
+ }
13891
+ function renderControl(f, value, onChange, ref) {
13892
+ const common = { className: "ff-prompt__input", placeholder: f.placeholder };
13893
+ if (f.type === "textarea") {
13894
+ return /* @__PURE__ */ jsxRuntime.jsx(
13895
+ "textarea",
13896
+ {
13897
+ ...common,
13898
+ ref,
13899
+ rows: 3,
13900
+ value: String(value ?? ""),
13901
+ onChange: (e) => onChange(e.target.value)
13902
+ }
13903
+ );
13904
+ }
13905
+ if (f.type === "switch") {
13906
+ return /* @__PURE__ */ jsxRuntime.jsx(
13907
+ "input",
13908
+ {
13909
+ type: "checkbox",
13910
+ className: "ff-prompt__switch",
13911
+ checked: !!value,
13912
+ onChange: (e) => onChange(e.target.checked)
13913
+ }
13914
+ );
13915
+ }
13916
+ if (f.type === "select" && f.options && f.options.length) {
13917
+ return /* @__PURE__ */ jsxRuntime.jsxs("select", { className: "ff-prompt__input", value: String(value ?? ""), onChange: (e) => onChange(e.target.value), children: [
13918
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "", disabled: true, children: "Choose\u2026" }),
13919
+ f.options.map((o) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: o.value, children: o.label }, o.value))
13920
+ ] });
13921
+ }
13922
+ return /* @__PURE__ */ jsxRuntime.jsx(
13923
+ "input",
13924
+ {
13925
+ ...common,
13926
+ ref,
13927
+ type: f.type === "number" ? "number" : "text",
13928
+ value: String(value ?? ""),
13929
+ onChange: (e) => onChange(f.type === "number" ? e.target.value === "" ? "" : Number(e.target.value) : e.target.value)
13930
+ }
13931
+ );
13708
13932
  }
13709
13933
  var FlowEditor = ReactExports.forwardRef(function FlowEditor2(props, ref) {
13710
13934
  return /* @__PURE__ */ jsxRuntime.jsx(ReactFlowProvider, { children: /* @__PURE__ */ jsxRuntime.jsx(
@@ -13739,6 +13963,41 @@ function FlowEditorInner({
13739
13963
  const internal = useFlowState(initial);
13740
13964
  const runner = useFlowRun();
13741
13965
  const rf = useReactFlow();
13966
+ const [prompt, setPrompt] = ReactExports.useState(null);
13967
+ const humanExecutors = ReactExports.useMemo(
13968
+ () => ({
13969
+ user_input: ({ node }) => new Promise((resolve) => {
13970
+ const cfg = node.data?.config ?? {};
13971
+ setPrompt({
13972
+ kind: "input",
13973
+ title: typeof cfg.title === "string" && cfg.title || "Your input",
13974
+ submitLabel: typeof cfg.submitLabel === "string" ? cfg.submitLabel : void 0,
13975
+ fields: humanInputFields(cfg),
13976
+ resolve: (values) => {
13977
+ setPrompt(null);
13978
+ resolve(values);
13979
+ }
13980
+ });
13981
+ }),
13982
+ human_approval: ({ node }) => new Promise((resolve) => {
13983
+ const cfg = node.data?.config ?? {};
13984
+ setPrompt({
13985
+ kind: "approval",
13986
+ title: typeof cfg.title === "string" && cfg.title || "Approve action",
13987
+ description: typeof cfg.description === "string" ? cfg.description : void 0,
13988
+ resolve: (approved) => {
13989
+ setPrompt(null);
13990
+ resolve({ branch: approved ? "approved" : "denied" });
13991
+ }
13992
+ });
13993
+ })
13994
+ }),
13995
+ []
13996
+ );
13997
+ const runExecutors = ReactExports.useMemo(() => ({ ...humanExecutors, ...executors }), [humanExecutors, executors]);
13998
+ ReactExports.useEffect(() => {
13999
+ if (!runner.running) setPrompt(null);
14000
+ }, [runner.running]);
13742
14001
  const controlled = value !== void 0;
13743
14002
  const baseFlow = controlled ? makeControlledFlowAdapter(value, onChange) : internal;
13744
14003
  const hist = useFlowHistory(baseFlow);
@@ -14048,7 +14307,7 @@ function FlowEditorInner({
14048
14307
  ({ autoLayout: autoLayout2 }) => flow.setGraph({ nodes: autoLayout2({ nodes: flow.nodes, edges: flow.edges }, { scope: laneId }), edges: flow.edges })
14049
14308
  );
14050
14309
  },
14051
- run: () => runner.run({ nodes: flow.nodes, edges: flow.edges }, executors),
14310
+ run: () => runner.run({ nodes: flow.nodes, edges: flow.edges }, runExecutors),
14052
14311
  cancel: runner.cancel,
14053
14312
  reset: runner.reset,
14054
14313
  toWorkflow,
@@ -14060,7 +14319,7 @@ function FlowEditorInner({
14060
14319
  canUndo: hist.canUndo,
14061
14320
  canRedo: hist.canRedo
14062
14321
  };
14063
- }, [flow, selectedId, selected2, selectedEdgeId, selectedEdge, selectedIds, selectedNodes, runner, executors, metadata, addNode, deleteNodes, deleteEdges, duplicateSelected, alignSelected, distributeSelected, copySelection, pasteClipboard, addLane, hist, rf]);
14322
+ }, [flow, selectedId, selected2, selectedEdgeId, selectedEdge, selectedIds, selectedNodes, runner, runExecutors, metadata, addNode, deleteNodes, deleteEdges, duplicateSelected, alignSelected, distributeSelected, copySelection, pasteClipboard, addLane, hist, rf]);
14064
14323
  ReactExports.useImperativeHandle(apiRef, () => api, [api]);
14065
14324
  const dropHandlers = paletteDropHandlers((kindName, evt) => {
14066
14325
  const point = rf.screenToFlowPosition({ x: evt.clientX, y: evt.clientY });
@@ -14234,7 +14493,11 @@ function FlowEditorInner({
14234
14493
  onCancel: () => setLabelEdit(null)
14235
14494
  }
14236
14495
  ),
14237
- showFeed && (slots.feed ? slots.feed(api) : /* @__PURE__ */ jsxRuntime.jsx(FlowRunFeed, { entries: runner.feed, className: "ff-editor__feed" }))
14496
+ showFeed && (slots.feed ? slots.feed(api) : /* @__PURE__ */ jsxRuntime.jsx(FlowRunFeed, { entries: runner.feed, className: "ff-editor__feed" })),
14497
+ prompt && /* @__PURE__ */ jsxRuntime.jsx(HumanPrompt, { request: prompt, onCancel: () => {
14498
+ setPrompt(null);
14499
+ runner.cancel();
14500
+ } })
14238
14501
  ] }),
14239
14502
  showPanel && (slots.panel ? slots.panel(api) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ff-editor__panel-wrap", children: [
14240
14503
  /* @__PURE__ */ jsxRuntime.jsx(