@particle-academy/fancy-flow 0.41.0 → 0.43.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/{ConfigFieldRenderer-4xK9hzvd.d.cts → ConfigFieldRenderer-BHQDZer6.d.ts} +10 -2
- package/dist/{ConfigFieldRenderer-D2TejNY1.d.ts → ConfigFieldRenderer-LotNHeG8.d.cts} +10 -2
- package/dist/{chunk-UX65ML3J.js → chunk-55KQS3LK.js} +25 -2
- package/dist/chunk-55KQS3LK.js.map +1 -0
- package/dist/{chunk-WOO4B7M5.js → chunk-T3VQX3XB.js} +3 -3
- package/dist/{chunk-WOO4B7M5.js.map → chunk-T3VQX3XB.js.map} +1 -1
- package/dist/engine.d.cts +3 -3
- package/dist/engine.d.ts +3 -3
- package/dist/fields/react-fancy.d.cts +3 -3
- package/dist/fields/react-fancy.d.ts +3 -3
- package/dist/index.cjs +351 -22
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +126 -7
- package/dist/index.d.ts +126 -7
- package/dist/index.js +318 -22
- package/dist/index.js.map +1 -1
- package/dist/registry/index.d.cts +3 -3
- package/dist/registry/index.d.ts +3 -3
- package/dist/{registry-BNyT66SS.d.cts → registry-C9KMVpn7.d.cts} +1 -1
- package/dist/{registry-CS7kjMto.d.ts → registry-FGCBprcn.d.ts} +1 -1
- package/dist/registry.cjs +23 -0
- package/dist/registry.cjs.map +1 -1
- package/dist/registry.js +1 -1
- package/dist/screens.js +3 -3
- package/dist/styles.css +96 -0
- package/dist/styles.css.map +1 -1
- package/dist/{types-cAdD1Og2.d.cts → types-B78Rm69S.d.cts} +81 -2
- package/dist/{types-Cf75IbxY.d.ts → types-JViq8reb.d.ts} +81 -2
- package/dist/ux.d.cts +1 -1
- package/dist/ux.d.ts +1 -1
- package/package.json +2 -1
- package/dist/chunk-UX65ML3J.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -11033,11 +11033,11 @@ function NoteNodeInner(props) {
|
|
|
11033
11033
|
const config = data.config ?? {};
|
|
11034
11034
|
const api = useFlowEditorOptional();
|
|
11035
11035
|
const hasConfigText = typeof config.text === "string" && config.text.length > 0;
|
|
11036
|
-
const
|
|
11036
|
+
const text2 = hasConfigText ? String(config.text) : typeof data.body === "string" ? data.body : typeof data.text === "string" ? data.text : "";
|
|
11037
11037
|
const title = typeof config.title === "string" && config.title || (!hasConfigText && typeof data.body === "string" && typeof data.label === "string" ? data.label : "");
|
|
11038
11038
|
const skin = NOTE_COLORS[coerceColor(config.color)];
|
|
11039
11039
|
const [editing, setEditing] = ReactExports.useState(false);
|
|
11040
|
-
const [draft, setDraft] = ReactExports.useState(
|
|
11040
|
+
const [draft, setDraft] = ReactExports.useState(text2);
|
|
11041
11041
|
const areaRef = ReactExports.useRef(null);
|
|
11042
11042
|
ReactExports.useEffect(() => {
|
|
11043
11043
|
if (editing && areaRef.current) {
|
|
@@ -11048,7 +11048,7 @@ function NoteNodeInner(props) {
|
|
|
11048
11048
|
}, [editing]);
|
|
11049
11049
|
const commit = () => {
|
|
11050
11050
|
setEditing(false);
|
|
11051
|
-
if (!api || draft ===
|
|
11051
|
+
if (!api || draft === text2) return;
|
|
11052
11052
|
const current = api.nodes.find((n) => n.id === props.id);
|
|
11053
11053
|
if (!current) return;
|
|
11054
11054
|
api.updateNode({
|
|
@@ -11062,7 +11062,7 @@ function NoteNodeInner(props) {
|
|
|
11062
11062
|
className: ["ff-note", props.selected ? "ff-note--selected" : ""].filter(Boolean).join(" "),
|
|
11063
11063
|
style: { background: skin.bg, borderColor: skin.border },
|
|
11064
11064
|
onDoubleClick: api ? () => {
|
|
11065
|
-
setDraft(
|
|
11065
|
+
setDraft(text2);
|
|
11066
11066
|
setEditing(true);
|
|
11067
11067
|
} : void 0,
|
|
11068
11068
|
children: [
|
|
@@ -11081,7 +11081,7 @@ function NoteNodeInner(props) {
|
|
|
11081
11081
|
if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) commit();
|
|
11082
11082
|
}
|
|
11083
11083
|
}
|
|
11084
|
-
) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "ff-note__body", children:
|
|
11084
|
+
) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "ff-note__body", children: text2 || /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ff-note__placeholder", children: api ? "Double-click to write a note\u2026" : "Note" }) })
|
|
11085
11085
|
]
|
|
11086
11086
|
}
|
|
11087
11087
|
);
|
|
@@ -11496,6 +11496,9 @@ var KINDS = [
|
|
|
11496
11496
|
},
|
|
11497
11497
|
{
|
|
11498
11498
|
name: "@particle-academy/user_input",
|
|
11499
|
+
// The keys an author declared on THIS node — the case a static list cannot
|
|
11500
|
+
// express, and the one issue #5 named.
|
|
11501
|
+
outputShape: (config) => (config.fields ?? []).filter((f) => typeof f.key === "string" && f.key !== "").map((f) => ({ path: f.key, type: "string", description: f.label })),
|
|
11499
11502
|
aliases: ["user_input", "@fancy/user_input"],
|
|
11500
11503
|
pausesForHuman: "input",
|
|
11501
11504
|
category: "human",
|
|
@@ -11716,6 +11719,10 @@ var KINDS = [
|
|
|
11716
11719
|
},
|
|
11717
11720
|
{
|
|
11718
11721
|
name: "@particle-academy/for_each",
|
|
11722
|
+
outputShape: [
|
|
11723
|
+
{ path: "items", type: "array" },
|
|
11724
|
+
{ path: "count", type: "number" }
|
|
11725
|
+
],
|
|
11719
11726
|
aliases: ["for_each", "@fancy/for_each"],
|
|
11720
11727
|
category: "logic",
|
|
11721
11728
|
label: "For Each",
|
|
@@ -11879,6 +11886,12 @@ var KINDS = [
|
|
|
11879
11886
|
// ───────────── AI ─────────────
|
|
11880
11887
|
{
|
|
11881
11888
|
name: "@particle-academy/llm_call",
|
|
11889
|
+
// LlmClient::complete() -> array{text:string,usage?:array,raw?:mixed}
|
|
11890
|
+
outputShape: [
|
|
11891
|
+
{ path: "text", type: "string", description: "The model's completion." },
|
|
11892
|
+
{ path: "usage", type: "object", description: "Token counts, when the provider reports them." },
|
|
11893
|
+
{ path: "raw", type: "unknown", description: "The provider's untouched response." }
|
|
11894
|
+
],
|
|
11882
11895
|
aliases: ["llm_call", "@fancy/llm_call"],
|
|
11883
11896
|
category: "ai",
|
|
11884
11897
|
label: "LLM Call",
|
|
@@ -12013,6 +12026,10 @@ var KINDS = [
|
|
|
12013
12026
|
},
|
|
12014
12027
|
{
|
|
12015
12028
|
name: "@particle-academy/embed_search",
|
|
12029
|
+
outputShape: [
|
|
12030
|
+
{ path: "query", type: "string" },
|
|
12031
|
+
{ path: "matches", type: "array", description: "Ranked results from the vector store." }
|
|
12032
|
+
],
|
|
12016
12033
|
aliases: ["embed_search", "@fancy/embed_search"],
|
|
12017
12034
|
category: "ai",
|
|
12018
12035
|
label: "Embed & Search",
|
|
@@ -12027,6 +12044,12 @@ var KINDS = [
|
|
|
12027
12044
|
// ───────────── IO ─────────────
|
|
12028
12045
|
{
|
|
12029
12046
|
name: "@particle-academy/api_request",
|
|
12047
|
+
// HttpClient::send() -> array{status:int,headers:array,body:mixed}
|
|
12048
|
+
outputShape: [
|
|
12049
|
+
{ path: "status", type: "number", description: "HTTP status code." },
|
|
12050
|
+
{ path: "headers", type: "object" },
|
|
12051
|
+
{ path: "body", type: "unknown", description: "Decoded JSON when the response is JSON, otherwise the raw body." }
|
|
12052
|
+
],
|
|
12030
12053
|
aliases: ["api_request", "@fancy/api_request"],
|
|
12031
12054
|
category: "io",
|
|
12032
12055
|
label: "API Request",
|
|
@@ -12811,6 +12834,230 @@ function paletteDropHandlers(onDrop) {
|
|
|
12811
12834
|
}
|
|
12812
12835
|
};
|
|
12813
12836
|
}
|
|
12837
|
+
|
|
12838
|
+
// src/expressions/variables.ts
|
|
12839
|
+
var ROOT = "$json";
|
|
12840
|
+
function expr(path) {
|
|
12841
|
+
return `{{ ${path} }}`;
|
|
12842
|
+
}
|
|
12843
|
+
function outputFieldsFor(node) {
|
|
12844
|
+
const kindName = node.data?.kind ?? node.type;
|
|
12845
|
+
if (!kindName) return [];
|
|
12846
|
+
const kind = getNodeKind(kindName);
|
|
12847
|
+
const shape = kind?.outputShape;
|
|
12848
|
+
if (!shape) return [];
|
|
12849
|
+
if (typeof shape === "function") {
|
|
12850
|
+
try {
|
|
12851
|
+
const config = node.data?.config ?? {};
|
|
12852
|
+
return shape(config) ?? [];
|
|
12853
|
+
} catch {
|
|
12854
|
+
return [];
|
|
12855
|
+
}
|
|
12856
|
+
}
|
|
12857
|
+
return shape;
|
|
12858
|
+
}
|
|
12859
|
+
function baseVariables() {
|
|
12860
|
+
return [
|
|
12861
|
+
{ expression: expr(ROOT), path: ROOT, type: "unknown", description: "The whole incoming value." }
|
|
12862
|
+
];
|
|
12863
|
+
}
|
|
12864
|
+
function availableVariables(graph, nodeId) {
|
|
12865
|
+
const out = baseVariables();
|
|
12866
|
+
const seen = /* @__PURE__ */ new Set([ROOT]);
|
|
12867
|
+
const byId = new Map(graph.nodes.map((n) => [n.id, n]));
|
|
12868
|
+
const upstreamIds = graph.edges.filter((e) => e.target === nodeId).map((e) => e.source);
|
|
12869
|
+
for (const id2 of upstreamIds) {
|
|
12870
|
+
if (id2 === nodeId) continue;
|
|
12871
|
+
const upstream = byId.get(id2);
|
|
12872
|
+
if (!upstream) continue;
|
|
12873
|
+
const kindName = upstream.data?.kind ?? upstream.type;
|
|
12874
|
+
const label = upstream.data?.label ?? (kindName ? getNodeKind(kindName)?.label : void 0);
|
|
12875
|
+
for (const field of outputFieldsFor(upstream)) {
|
|
12876
|
+
const path = `${ROOT}.${field.path}`;
|
|
12877
|
+
if (seen.has(path)) continue;
|
|
12878
|
+
seen.add(path);
|
|
12879
|
+
out.push({
|
|
12880
|
+
expression: expr(path),
|
|
12881
|
+
path,
|
|
12882
|
+
type: field.type,
|
|
12883
|
+
description: field.description,
|
|
12884
|
+
source: label
|
|
12885
|
+
});
|
|
12886
|
+
}
|
|
12887
|
+
}
|
|
12888
|
+
return out;
|
|
12889
|
+
}
|
|
12890
|
+
function describeExpressionGrammar() {
|
|
12891
|
+
return {
|
|
12892
|
+
forms: [
|
|
12893
|
+
{ syntax: "{{ $json }}", meaning: "The whole value arriving on this node's input." },
|
|
12894
|
+
{ syntax: "{{ $json.field }}", meaning: "One key of that value. Dot-paths nest: $json.user.email." },
|
|
12895
|
+
{ syntax: "{{ $input.field }}", meaning: "An alias for $json \u2014 the same value, either spelling." },
|
|
12896
|
+
{ syntax: "{{ in }}", meaning: "The input by its context key, rather than through the alias." }
|
|
12897
|
+
],
|
|
12898
|
+
note: "Dot-paths only \u2014 no arithmetic, comparisons or function calls; anything else resolves to nothing. A field that is exactly one expression keeps the resolved value's type; mixed text interpolates as a string. fancy-flow's own runFlow does not interpolate: it hands config to your executor verbatim, and the fancy-flow-php runtime is what resolves these."
|
|
12899
|
+
};
|
|
12900
|
+
}
|
|
12901
|
+
function findTrigger(text2, caret) {
|
|
12902
|
+
const before = text2.slice(0, caret);
|
|
12903
|
+
const open = before.lastIndexOf("{{");
|
|
12904
|
+
if (open === -1) return null;
|
|
12905
|
+
if (before.indexOf("}}", open) !== -1) return null;
|
|
12906
|
+
return { open, query: before.slice(open + 2).trim() };
|
|
12907
|
+
}
|
|
12908
|
+
function filterVariables(vars, query) {
|
|
12909
|
+
const q2 = query.trim().toLowerCase();
|
|
12910
|
+
if (q2 === "") return vars;
|
|
12911
|
+
return vars.filter((v2) => v2.path.toLowerCase().includes(q2));
|
|
12912
|
+
}
|
|
12913
|
+
function applyCompletion(text2, caret, trigger, variable) {
|
|
12914
|
+
const head = text2.slice(0, trigger.open);
|
|
12915
|
+
const tail = text2.slice(caret);
|
|
12916
|
+
return { value: `${head}${variable.expression}${tail}`, caret: head.length + variable.expression.length };
|
|
12917
|
+
}
|
|
12918
|
+
function ExpressionField({
|
|
12919
|
+
value,
|
|
12920
|
+
onChange,
|
|
12921
|
+
placeholder,
|
|
12922
|
+
rows = 2,
|
|
12923
|
+
handle,
|
|
12924
|
+
graph,
|
|
12925
|
+
nodeId
|
|
12926
|
+
}) {
|
|
12927
|
+
const ref = ReactExports.useRef(null);
|
|
12928
|
+
const [trigger, setTrigger] = ReactExports.useState(null);
|
|
12929
|
+
const [active, setActive] = ReactExports.useState(0);
|
|
12930
|
+
const [showHelp, setShowHelp] = ReactExports.useState(false);
|
|
12931
|
+
const variables = ReactExports.useMemo(
|
|
12932
|
+
() => graph && nodeId ? availableVariables(graph, nodeId) : baseVariables(),
|
|
12933
|
+
[graph, nodeId]
|
|
12934
|
+
);
|
|
12935
|
+
const matches = ReactExports.useMemo(
|
|
12936
|
+
() => trigger ? filterVariables(variables, trigger.query) : [],
|
|
12937
|
+
[trigger, variables]
|
|
12938
|
+
);
|
|
12939
|
+
const help = ReactExports.useMemo(() => describeExpressionGrammar(), []);
|
|
12940
|
+
const open = trigger !== null && matches.length > 0;
|
|
12941
|
+
const sync = (el) => {
|
|
12942
|
+
const next = findTrigger(el.value, el.selectionStart ?? el.value.length);
|
|
12943
|
+
setTrigger(next);
|
|
12944
|
+
setActive(0);
|
|
12945
|
+
};
|
|
12946
|
+
const choose = (variable) => {
|
|
12947
|
+
const el = ref.current;
|
|
12948
|
+
if (!el || !trigger) return;
|
|
12949
|
+
const { value: next, caret } = applyCompletion(
|
|
12950
|
+
el.value,
|
|
12951
|
+
el.selectionStart ?? el.value.length,
|
|
12952
|
+
trigger,
|
|
12953
|
+
variable
|
|
12954
|
+
);
|
|
12955
|
+
onChange(next);
|
|
12956
|
+
setTrigger(null);
|
|
12957
|
+
requestAnimationFrame(() => {
|
|
12958
|
+
el.focus();
|
|
12959
|
+
el.setSelectionRange(caret, caret);
|
|
12960
|
+
});
|
|
12961
|
+
};
|
|
12962
|
+
const onKeyDown = (e) => {
|
|
12963
|
+
if (!open) return;
|
|
12964
|
+
if (e.key === "ArrowDown") {
|
|
12965
|
+
e.preventDefault();
|
|
12966
|
+
setActive((i) => (i + 1) % matches.length);
|
|
12967
|
+
} else if (e.key === "ArrowUp") {
|
|
12968
|
+
e.preventDefault();
|
|
12969
|
+
setActive((i) => (i - 1 + matches.length) % matches.length);
|
|
12970
|
+
} else if (e.key === "Enter" || e.key === "Tab") {
|
|
12971
|
+
e.preventDefault();
|
|
12972
|
+
choose(matches[active] ?? matches[0]);
|
|
12973
|
+
} else if (e.key === "Escape") {
|
|
12974
|
+
e.preventDefault();
|
|
12975
|
+
setTrigger(null);
|
|
12976
|
+
}
|
|
12977
|
+
};
|
|
12978
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ff-expression", "data-ff-expression": handle["data-ff-field"], children: [
|
|
12979
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
12980
|
+
"textarea",
|
|
12981
|
+
{
|
|
12982
|
+
...handle,
|
|
12983
|
+
ref,
|
|
12984
|
+
className: "ff-panel__input ff-panel__input--expression",
|
|
12985
|
+
rows,
|
|
12986
|
+
value: value ?? "",
|
|
12987
|
+
placeholder: placeholder ?? "{{ $json.field }}",
|
|
12988
|
+
spellCheck: false,
|
|
12989
|
+
role: "combobox",
|
|
12990
|
+
"aria-expanded": open,
|
|
12991
|
+
"aria-autocomplete": "list",
|
|
12992
|
+
"aria-controls": open ? `${handle.id ?? handle["data-ff-field"]}-vars` : void 0,
|
|
12993
|
+
onChange: (e) => {
|
|
12994
|
+
onChange(e.target.value);
|
|
12995
|
+
sync(e.target);
|
|
12996
|
+
},
|
|
12997
|
+
onKeyDown,
|
|
12998
|
+
onClick: (e) => sync(e.currentTarget),
|
|
12999
|
+
onBlur: () => {
|
|
13000
|
+
window.setTimeout(() => setTrigger(null), 120);
|
|
13001
|
+
}
|
|
13002
|
+
}
|
|
13003
|
+
),
|
|
13004
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ff-expression__bar", children: [
|
|
13005
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
13006
|
+
"button",
|
|
13007
|
+
{
|
|
13008
|
+
type: "button",
|
|
13009
|
+
className: "ff-expression__helpbtn",
|
|
13010
|
+
"aria-expanded": showHelp,
|
|
13011
|
+
onClick: () => setShowHelp((s) => !s),
|
|
13012
|
+
children: [
|
|
13013
|
+
"{{ }}",
|
|
13014
|
+
" reference"
|
|
13015
|
+
]
|
|
13016
|
+
}
|
|
13017
|
+
),
|
|
13018
|
+
variables.length > 1 && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "ff-expression__hint", children: [
|
|
13019
|
+
"type ",
|
|
13020
|
+
/* @__PURE__ */ jsxRuntime.jsx("code", { children: "{{" }),
|
|
13021
|
+
" for ",
|
|
13022
|
+
variables.length,
|
|
13023
|
+
" variables"
|
|
13024
|
+
] })
|
|
13025
|
+
] }),
|
|
13026
|
+
open && /* @__PURE__ */ jsxRuntime.jsx(
|
|
13027
|
+
"ul",
|
|
13028
|
+
{
|
|
13029
|
+
className: "ff-expression__menu",
|
|
13030
|
+
id: `${handle.id ?? handle["data-ff-field"]}-vars`,
|
|
13031
|
+
role: "listbox",
|
|
13032
|
+
"data-ff-expression-menu": handle["data-ff-field"],
|
|
13033
|
+
children: matches.map((v2, i) => /* @__PURE__ */ jsxRuntime.jsx("li", { role: "option", "aria-selected": i === active, children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
13034
|
+
"button",
|
|
13035
|
+
{
|
|
13036
|
+
type: "button",
|
|
13037
|
+
className: `ff-expression__option${i === active ? " is-active" : ""}`,
|
|
13038
|
+
"data-ff-expression-option": v2.path,
|
|
13039
|
+
onMouseDown: (e) => {
|
|
13040
|
+
e.preventDefault();
|
|
13041
|
+
choose(v2);
|
|
13042
|
+
},
|
|
13043
|
+
children: [
|
|
13044
|
+
/* @__PURE__ */ jsxRuntime.jsx("code", { className: "ff-expression__path", children: v2.path }),
|
|
13045
|
+
v2.source && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ff-expression__source", children: v2.source }),
|
|
13046
|
+
v2.description && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ff-expression__desc", children: v2.description })
|
|
13047
|
+
]
|
|
13048
|
+
}
|
|
13049
|
+
) }, v2.expression))
|
|
13050
|
+
}
|
|
13051
|
+
),
|
|
13052
|
+
showHelp && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ff-expression__help", role: "note", "data-ff-expression-help": true, children: [
|
|
13053
|
+
/* @__PURE__ */ jsxRuntime.jsx("dl", { children: help.forms.map((f) => /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
13054
|
+
/* @__PURE__ */ jsxRuntime.jsx("dt", { children: /* @__PURE__ */ jsxRuntime.jsx("code", { children: f.syntax }) }),
|
|
13055
|
+
/* @__PURE__ */ jsxRuntime.jsx("dd", { children: f.meaning })
|
|
13056
|
+
] }, f.syntax)) }),
|
|
13057
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "ff-expression__note", children: help.note })
|
|
13058
|
+
] })
|
|
13059
|
+
] });
|
|
13060
|
+
}
|
|
12814
13061
|
function ConfigFieldRenderer({
|
|
12815
13062
|
field,
|
|
12816
13063
|
value,
|
|
@@ -12818,7 +13065,9 @@ function ConfigFieldRenderer({
|
|
|
12818
13065
|
id: id2,
|
|
12819
13066
|
renderCredentialField,
|
|
12820
13067
|
renderDocumentField,
|
|
12821
|
-
fieldRenderers
|
|
13068
|
+
fieldRenderers,
|
|
13069
|
+
graph,
|
|
13070
|
+
nodeId
|
|
12822
13071
|
}) {
|
|
12823
13072
|
const handle = { id: id2, "data-ff-field": field.key };
|
|
12824
13073
|
const custom = fieldRenderers?.[field.type];
|
|
@@ -12900,15 +13149,14 @@ function ConfigFieldRenderer({
|
|
|
12900
13149
|
return /* @__PURE__ */ jsxRuntime.jsx(JsonField, { value, onChange, rows: field.rows, handle });
|
|
12901
13150
|
case "expression":
|
|
12902
13151
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
12903
|
-
|
|
13152
|
+
ExpressionField,
|
|
12904
13153
|
{
|
|
12905
|
-
|
|
12906
|
-
className: "ff-panel__input ff-panel__input--expression",
|
|
12907
|
-
rows: 2,
|
|
13154
|
+
handle,
|
|
12908
13155
|
value: value ?? "",
|
|
13156
|
+
onChange,
|
|
12909
13157
|
placeholder: field.example ?? "{{ $json.field }}",
|
|
12910
|
-
|
|
12911
|
-
|
|
13158
|
+
graph,
|
|
13159
|
+
nodeId
|
|
12912
13160
|
}
|
|
12913
13161
|
);
|
|
12914
13162
|
case "credential":
|
|
@@ -12935,7 +13183,9 @@ function ConfigFieldRenderer({
|
|
|
12935
13183
|
onChange,
|
|
12936
13184
|
renderCredentialField,
|
|
12937
13185
|
renderDocumentField,
|
|
12938
|
-
fieldRenderers
|
|
13186
|
+
fieldRenderers,
|
|
13187
|
+
graph,
|
|
13188
|
+
nodeId
|
|
12939
13189
|
}
|
|
12940
13190
|
);
|
|
12941
13191
|
case "keyvalue":
|
|
@@ -12988,6 +13238,8 @@ function RepeaterField({
|
|
|
12988
13238
|
field,
|
|
12989
13239
|
value,
|
|
12990
13240
|
onChange,
|
|
13241
|
+
graph,
|
|
13242
|
+
nodeId,
|
|
12991
13243
|
renderCredentialField,
|
|
12992
13244
|
renderDocumentField,
|
|
12993
13245
|
fieldRenderers
|
|
@@ -13068,6 +13320,8 @@ function RepeaterField({
|
|
|
13068
13320
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
13069
13321
|
ConfigFieldRenderer,
|
|
13070
13322
|
{
|
|
13323
|
+
graph,
|
|
13324
|
+
nodeId,
|
|
13071
13325
|
field: sub,
|
|
13072
13326
|
value: row[sub.key],
|
|
13073
13327
|
onChange: (cell) => setCell(i, sub.key, cell),
|
|
@@ -13182,7 +13436,7 @@ function JsonField({
|
|
|
13182
13436
|
return "";
|
|
13183
13437
|
}
|
|
13184
13438
|
}, [value]);
|
|
13185
|
-
const [
|
|
13439
|
+
const [text2, setText] = ReactExports.useState(serialized);
|
|
13186
13440
|
const [error, setError] = ReactExports.useState(null);
|
|
13187
13441
|
const [touched, setTouched] = ReactExports.useState(false);
|
|
13188
13442
|
const lastProps = ReactExports.useRef(serialized);
|
|
@@ -13220,7 +13474,7 @@ function JsonField({
|
|
|
13220
13474
|
...handle,
|
|
13221
13475
|
className: "ff-panel__input ff-panel__input--json",
|
|
13222
13476
|
rows: rows ?? 6,
|
|
13223
|
-
value:
|
|
13477
|
+
value: text2,
|
|
13224
13478
|
spellCheck: false,
|
|
13225
13479
|
"aria-invalid": showError || void 0,
|
|
13226
13480
|
"aria-describedby": showError ? errorId : void 0,
|
|
@@ -13249,6 +13503,7 @@ function NodeConfigPanel({
|
|
|
13249
13503
|
renderCredentialField,
|
|
13250
13504
|
renderDocumentField,
|
|
13251
13505
|
fieldRenderers,
|
|
13506
|
+
graph,
|
|
13252
13507
|
className,
|
|
13253
13508
|
style: style2
|
|
13254
13509
|
}) {
|
|
@@ -13354,7 +13609,9 @@ function NodeConfigPanel({
|
|
|
13354
13609
|
onChange: (v2) => setConfigValue(field.key, v2),
|
|
13355
13610
|
renderCredentialField,
|
|
13356
13611
|
renderDocumentField: documentField,
|
|
13357
|
-
fieldRenderers
|
|
13612
|
+
fieldRenderers,
|
|
13613
|
+
graph,
|
|
13614
|
+
nodeId: node.id
|
|
13358
13615
|
}
|
|
13359
13616
|
)
|
|
13360
13617
|
] }, field.key))
|
|
@@ -14646,8 +14903,8 @@ function FlowEditorInner({
|
|
|
14646
14903
|
x: labelEdit.x,
|
|
14647
14904
|
y: labelEdit.y,
|
|
14648
14905
|
initial: flow.edges.find((e) => e.id === labelEdit.edgeId)?.label ?? "",
|
|
14649
|
-
onCommit: (
|
|
14650
|
-
api.setEdgeLabel(labelEdit.edgeId,
|
|
14906
|
+
onCommit: (text2) => {
|
|
14907
|
+
api.setEdgeLabel(labelEdit.edgeId, text2);
|
|
14651
14908
|
setLabelEdit(null);
|
|
14652
14909
|
},
|
|
14653
14910
|
onCancel: () => setLabelEdit(null)
|
|
@@ -14667,6 +14924,7 @@ function FlowEditorInner({
|
|
|
14667
14924
|
node: api.selected,
|
|
14668
14925
|
onChange: api.updateNode,
|
|
14669
14926
|
fieldRenderers,
|
|
14927
|
+
graph: flow,
|
|
14670
14928
|
onDelete: builtins.delete === false ? void 0 : (n) => api.deleteNodes([n.id])
|
|
14671
14929
|
}
|
|
14672
14930
|
),
|
|
@@ -14751,7 +15009,7 @@ function EdgeLabelEditor({
|
|
|
14751
15009
|
onCommit,
|
|
14752
15010
|
onCancel
|
|
14753
15011
|
}) {
|
|
14754
|
-
const [
|
|
15012
|
+
const [text2, setText] = ReactExports.useState(initial);
|
|
14755
15013
|
const ref = ReactExports.useRef(null);
|
|
14756
15014
|
ReactExports.useEffect(() => {
|
|
14757
15015
|
ref.current?.focus();
|
|
@@ -14768,17 +15026,17 @@ function EdgeLabelEditor({
|
|
|
14768
15026
|
{
|
|
14769
15027
|
ref,
|
|
14770
15028
|
className: "ff-panel__input",
|
|
14771
|
-
value:
|
|
15029
|
+
value: text2,
|
|
14772
15030
|
placeholder: "Label this connection",
|
|
14773
15031
|
"aria-label": "Connection label",
|
|
14774
15032
|
"data-action": "edge-label-input",
|
|
14775
15033
|
onChange: (e) => setText(e.target.value),
|
|
14776
15034
|
onKeyDown: (e) => {
|
|
14777
15035
|
e.stopPropagation();
|
|
14778
|
-
if (e.key === "Enter") onCommit(
|
|
15036
|
+
if (e.key === "Enter") onCommit(text2);
|
|
14779
15037
|
if (e.key === "Escape") onCancel();
|
|
14780
15038
|
},
|
|
14781
|
-
onBlur: () => onCommit(
|
|
15039
|
+
onBlur: () => onCommit(text2)
|
|
14782
15040
|
}
|
|
14783
15041
|
)
|
|
14784
15042
|
}
|
|
@@ -15013,6 +15271,67 @@ var flowKeys = {
|
|
|
15013
15271
|
run: (runId) => ["flow", "runs", runId]
|
|
15014
15272
|
};
|
|
15015
15273
|
|
|
15274
|
+
// src/expressions/expr.ts
|
|
15275
|
+
var WHOLE = /^\{\{\s*([\s\S]*?)\s*\}\}$/;
|
|
15276
|
+
var EACH = /\{\{\s*([\s\S]*?)\s*\}\}/g;
|
|
15277
|
+
var FALSY_STRINGS = /* @__PURE__ */ new Set(["", "0", "false", "no", "off", "null"]);
|
|
15278
|
+
function resolvePath(path, context) {
|
|
15279
|
+
const trimmed = path.trim();
|
|
15280
|
+
if (trimmed === "") return null;
|
|
15281
|
+
const segments = trimmed.split(".");
|
|
15282
|
+
let cursor;
|
|
15283
|
+
const head = segments[0];
|
|
15284
|
+
if (head === "$json" || head === "$input") {
|
|
15285
|
+
cursor = context !== null && typeof context === "object" && "in" in context ? context.in : context;
|
|
15286
|
+
segments.shift();
|
|
15287
|
+
} else {
|
|
15288
|
+
cursor = context;
|
|
15289
|
+
}
|
|
15290
|
+
for (const segment of segments) {
|
|
15291
|
+
if (cursor === null || cursor === void 0) return null;
|
|
15292
|
+
if (typeof cursor !== "object") return null;
|
|
15293
|
+
const next = cursor[segment];
|
|
15294
|
+
if (next === void 0) return null;
|
|
15295
|
+
cursor = next;
|
|
15296
|
+
}
|
|
15297
|
+
return cursor === void 0 ? null : cursor;
|
|
15298
|
+
}
|
|
15299
|
+
function evaluateExpression(template, context) {
|
|
15300
|
+
if (typeof template !== "string") return template;
|
|
15301
|
+
const whole = WHOLE.exec(template.trim());
|
|
15302
|
+
if (whole) return resolvePath(whole[1] ?? "", context);
|
|
15303
|
+
return template.replace(EACH, (_m, path) => stringify(resolvePath(path, context)));
|
|
15304
|
+
}
|
|
15305
|
+
function truthy(value) {
|
|
15306
|
+
if (typeof value === "boolean") return value;
|
|
15307
|
+
if (value === null || value === void 0) return false;
|
|
15308
|
+
if (typeof value === "string") return !FALSY_STRINGS.has(value.trim().toLowerCase());
|
|
15309
|
+
if (Array.isArray(value)) return value.length > 0;
|
|
15310
|
+
if (typeof value === "number") return value !== 0;
|
|
15311
|
+
return Boolean(value);
|
|
15312
|
+
}
|
|
15313
|
+
function text(value) {
|
|
15314
|
+
return stringify(value);
|
|
15315
|
+
}
|
|
15316
|
+
function stringify(value) {
|
|
15317
|
+
if (typeof value === "string") return value;
|
|
15318
|
+
if (typeof value === "boolean") return value ? "true" : "false";
|
|
15319
|
+
if (value === null || value === void 0) return "";
|
|
15320
|
+
if (typeof value === "number" || typeof value === "bigint") return String(value);
|
|
15321
|
+
try {
|
|
15322
|
+
return JSON.stringify(value) ?? "";
|
|
15323
|
+
} catch {
|
|
15324
|
+
return "";
|
|
15325
|
+
}
|
|
15326
|
+
}
|
|
15327
|
+
function evaluateConfig(config, context) {
|
|
15328
|
+
const out = {};
|
|
15329
|
+
for (const [key, value] of Object.entries(config)) {
|
|
15330
|
+
out[key] = Array.isArray(value) ? value.map((v2) => evaluateExpression(v2, context)) : value !== null && typeof value === "object" ? evaluateConfig(value, context) : evaluateExpression(value, context);
|
|
15331
|
+
}
|
|
15332
|
+
return out;
|
|
15333
|
+
}
|
|
15334
|
+
|
|
15016
15335
|
// src/index.ts
|
|
15017
15336
|
registerBuiltinKinds();
|
|
15018
15337
|
/*! Bundled license information:
|
|
@@ -15026,6 +15345,7 @@ exports.ActionNode = ActionNode;
|
|
|
15026
15345
|
exports.BUILTIN_KINDS = BUILTIN_KINDS;
|
|
15027
15346
|
exports.ConfigFieldRenderer = ConfigFieldRenderer;
|
|
15028
15347
|
exports.DecisionNode = DecisionNode;
|
|
15348
|
+
exports.ExpressionField = ExpressionField;
|
|
15029
15349
|
exports.FlowCanvas = FlowCanvas;
|
|
15030
15350
|
exports.FlowEditor = FlowEditor;
|
|
15031
15351
|
exports.FlowRunControls = FlowRunControls;
|
|
@@ -15049,6 +15369,8 @@ exports.WORKFLOW_SCHEMA_VERSION = WORKFLOW_SCHEMA_VERSION;
|
|
|
15049
15369
|
exports.alignNodes = alignNodes;
|
|
15050
15370
|
exports.applyOutputsToNodes = applyOutputsToNodes;
|
|
15051
15371
|
exports.applyStatusesToNodes = applyStatusesToNodes;
|
|
15372
|
+
exports.availableVariables = availableVariables;
|
|
15373
|
+
exports.baseVariables = baseVariables;
|
|
15052
15374
|
exports.buildNodeTypes = buildNodeTypes;
|
|
15053
15375
|
exports.categoryAccent = categoryAccent;
|
|
15054
15376
|
exports.clearNodeKindOverrides = clearNodeKindOverrides;
|
|
@@ -15060,8 +15382,11 @@ exports.defaultConfigFor = defaultConfigFor;
|
|
|
15060
15382
|
exports.defaultNodeTypes = defaultNodeTypes;
|
|
15061
15383
|
exports.defaultPortCompatibility = defaultPortCompatibility;
|
|
15062
15384
|
exports.defineNode = defineNode;
|
|
15385
|
+
exports.describeExpressionGrammar = describeExpressionGrammar;
|
|
15063
15386
|
exports.distributeNodes = distributeNodes;
|
|
15064
15387
|
exports.encodePause = encodePause;
|
|
15388
|
+
exports.evaluateConfig = evaluateConfig;
|
|
15389
|
+
exports.evaluateExpression = evaluateExpression;
|
|
15065
15390
|
exports.exportWorkflow = exportWorkflow;
|
|
15066
15391
|
exports.flowKeys = flowKeys;
|
|
15067
15392
|
exports.flowLive = flowLive;
|
|
@@ -15074,6 +15399,7 @@ exports.listNodeKinds = listNodeKinds;
|
|
|
15074
15399
|
exports.migrateSchema = migrateSchema;
|
|
15075
15400
|
exports.onNodeKindsChanged = onNodeKindsChanged;
|
|
15076
15401
|
exports.onRichInputAdapterChanged = onRichInputAdapterChanged;
|
|
15402
|
+
exports.outputFieldsFor = outputFieldsFor;
|
|
15077
15403
|
exports.overrideNodeKind = overrideNodeKind;
|
|
15078
15404
|
exports.paletteDropHandlers = paletteDropHandlers;
|
|
15079
15405
|
exports.pauseForHuman = pauseForHuman;
|
|
@@ -15082,9 +15408,12 @@ exports.registerBuiltinKinds = registerBuiltinKinds;
|
|
|
15082
15408
|
exports.registerNodeKind = registerNodeKind;
|
|
15083
15409
|
exports.registerRichInputAdapter = registerRichInputAdapter;
|
|
15084
15410
|
exports.resolveNodePorts = resolveNodePorts;
|
|
15411
|
+
exports.resolvePath = resolvePath;
|
|
15085
15412
|
exports.resolvePortSpec = resolvePortSpec;
|
|
15086
15413
|
exports.runCohort = runCohort;
|
|
15087
15414
|
exports.runFlow = runFlow;
|
|
15415
|
+
exports.text = text;
|
|
15416
|
+
exports.truthy = truthy;
|
|
15088
15417
|
exports.useFlowEditor = useFlowEditor;
|
|
15089
15418
|
exports.useFlowEditorOptional = useFlowEditorOptional;
|
|
15090
15419
|
exports.useFlowHistory = useFlowHistory;
|