@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.
- package/dist/{chunk-OHVSEHDC.js → chunk-N5ICD2WZ.js} +3 -3
- package/dist/{chunk-OHVSEHDC.js.map → chunk-N5ICD2WZ.js.map} +1 -1
- package/dist/{chunk-ZS6DVGB3.js → chunk-OFW7GN3Y.js} +4 -4
- package/dist/{chunk-ZS6DVGB3.js.map → chunk-OFW7GN3Y.js.map} +1 -1
- package/dist/{chunk-YXRLIV7A.js → chunk-Q6IPK64P.js} +4 -2
- package/dist/chunk-Q6IPK64P.js.map +1 -0
- package/dist/{chunk-KDHLKBL2.js → chunk-U2VJB7HR.js} +7 -5
- package/dist/chunk-U2VJB7HR.js.map +1 -0
- package/dist/{chunk-QAA4CA22.js → chunk-YOFCVO2W.js} +118 -6
- package/dist/chunk-YOFCVO2W.js.map +1 -0
- package/dist/engine.cjs +4 -2
- package/dist/engine.cjs.map +1 -1
- package/dist/engine.js +3 -3
- package/dist/index.cjs +287 -24
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -8
- package/dist/index.d.ts +3 -8
- package/dist/index.js +180 -33
- package/dist/index.js.map +1 -1
- package/dist/registry/index.d.cts +2 -2
- package/dist/registry/index.d.ts +2 -2
- package/dist/registry.cjs +111 -2
- package/dist/registry.cjs.map +1 -1
- package/dist/registry.js +3 -3
- package/dist/runtime.cjs +4 -2
- package/dist/runtime.cjs.map +1 -1
- package/dist/runtime.js +3 -3
- package/dist/schema.cjs.map +1 -1
- package/dist/schema.js +2 -2
- package/dist/styles.css +198 -0
- package/dist/styles.css.map +1 -1
- package/dist/{types-DwRpXXNy.d.ts → types-BGtR3k9J.d.ts} +1 -1
- package/dist/{types-BekE5JTG.d.cts → types-B_pxRqfw.d.cts} +1 -1
- package/dist/ux.cjs.map +1 -1
- package/dist/ux.d.cts +1 -1
- package/dist/ux.d.ts +1 -1
- package/dist/ux.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-KDHLKBL2.js.map +0 -1
- package/dist/chunk-QAA4CA22.js.map +0 -1
- 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
|
-
|
|
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:
|
|
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
|
-
|
|
13698
|
-
|
|
13699
|
-
|
|
13700
|
-
|
|
13701
|
-
|
|
13702
|
-
|
|
13703
|
-
|
|
13704
|
-
|
|
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
|
|
13707
|
-
|
|
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 },
|
|
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,
|
|
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(
|