@particle-academy/fancy-flow 0.40.4 → 0.42.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 (36) hide show
  1. package/dist/ConfigFieldRenderer-BHQDZer6.d.ts +92 -0
  2. package/dist/ConfigFieldRenderer-LotNHeG8.d.cts +92 -0
  3. package/dist/{chunk-UX65ML3J.js → chunk-55KQS3LK.js} +25 -2
  4. package/dist/chunk-55KQS3LK.js.map +1 -0
  5. package/dist/{chunk-WOO4B7M5.js → chunk-T3VQX3XB.js} +3 -3
  6. package/dist/{chunk-WOO4B7M5.js.map → chunk-T3VQX3XB.js.map} +1 -1
  7. package/dist/engine.d.cts +3 -3
  8. package/dist/engine.d.ts +3 -3
  9. package/dist/fields/react-fancy.cjs +40 -0
  10. package/dist/fields/react-fancy.cjs.map +1 -0
  11. package/dist/fields/react-fancy.d.cts +20 -0
  12. package/dist/fields/react-fancy.d.ts +20 -0
  13. package/dist/fields/react-fancy.js +37 -0
  14. package/dist/fields/react-fancy.js.map +1 -0
  15. package/dist/index.cjs +343 -33
  16. package/dist/index.cjs.map +1 -1
  17. package/dist/index.d.cts +105 -127
  18. package/dist/index.d.ts +105 -127
  19. package/dist/index.js +320 -38
  20. package/dist/index.js.map +1 -1
  21. package/dist/registry/index.d.cts +3 -3
  22. package/dist/registry/index.d.ts +3 -3
  23. package/dist/{registry-BADFn97n.d.cts → registry-C9KMVpn7.d.cts} +1 -1
  24. package/dist/{registry-DYbFLEcW.d.ts → registry-FGCBprcn.d.ts} +1 -1
  25. package/dist/registry.cjs +23 -0
  26. package/dist/registry.cjs.map +1 -1
  27. package/dist/registry.js +1 -1
  28. package/dist/screens.js +3 -3
  29. package/dist/styles.css +99 -0
  30. package/dist/styles.css.map +1 -1
  31. package/dist/{types-D8Dezh9v.d.cts → types-B78Rm69S.d.cts} +94 -2
  32. package/dist/{types-D6lsIGoe.d.ts → types-JViq8reb.d.ts} +94 -2
  33. package/dist/ux.d.cts +1 -1
  34. package/dist/ux.d.ts +1 -1
  35. package/package.json +12 -2
  36. package/dist/chunk-UX65ML3J.js.map +0 -1
package/dist/index.cjs CHANGED
@@ -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(text, caret) {
12902
+ const before = text.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(text, caret, trigger, variable) {
12914
+ const head = text.slice(0, trigger.open);
12915
+ const tail = text.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];
@@ -12897,18 +13146,17 @@ function ConfigFieldRenderer({
12897
13146
  }
12898
13147
  );
12899
13148
  case "json":
12900
- return /* @__PURE__ */ jsxRuntime.jsx(JsonField, { value, onChange, rows: field.rows });
13149
+ return /* @__PURE__ */ jsxRuntime.jsx(JsonField, { value, onChange, rows: field.rows, handle });
12901
13150
  case "expression":
12902
13151
  return /* @__PURE__ */ jsxRuntime.jsx(
12903
- "textarea",
13152
+ ExpressionField,
12904
13153
  {
12905
- ...handle,
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
- spellCheck: false,
12911
- onChange: (e) => onChange(e.target.value)
13158
+ graph,
13159
+ nodeId
12912
13160
  }
12913
13161
  );
12914
13162
  case "credential":
@@ -12929,16 +13177,19 @@ function ConfigFieldRenderer({
12929
13177
  return /* @__PURE__ */ jsxRuntime.jsx(
12930
13178
  RepeaterField,
12931
13179
  {
13180
+ id: id2,
12932
13181
  field,
12933
13182
  value,
12934
13183
  onChange,
12935
13184
  renderCredentialField,
12936
13185
  renderDocumentField,
12937
- fieldRenderers
13186
+ fieldRenderers,
13187
+ graph,
13188
+ nodeId
12938
13189
  }
12939
13190
  );
12940
13191
  case "keyvalue":
12941
- return /* @__PURE__ */ jsxRuntime.jsx(KeyValueField, { field, value, onChange });
13192
+ return /* @__PURE__ */ jsxRuntime.jsx(KeyValueField, { id: id2, field, value, onChange });
12942
13193
  case "document":
12943
13194
  if (renderDocumentField) {
12944
13195
  return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: renderDocumentField({ documentType: field.documentType, value, onChange }) });
@@ -12983,9 +13234,12 @@ function ChoiceField({
12983
13234
  );
12984
13235
  }
12985
13236
  function RepeaterField({
13237
+ id: id2,
12986
13238
  field,
12987
13239
  value,
12988
13240
  onChange,
13241
+ graph,
13242
+ nodeId,
12989
13243
  renderCredentialField,
12990
13244
  renderDocumentField,
12991
13245
  fieldRenderers
@@ -13017,7 +13271,7 @@ function RepeaterField({
13017
13271
  if (typeof raw === "number" || typeof raw === "boolean") return String(raw);
13018
13272
  return `Item ${i + 1}`;
13019
13273
  };
13020
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ff-repeater", "data-ff-repeater": field.key, children: [
13274
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { id: id2, role: "group", className: "ff-repeater", "data-ff-repeater": field.key, "data-ff-field": field.key, children: [
13021
13275
  rows.length === 0 && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "ff-repeater__empty", children: "None yet." }),
13022
13276
  rows.map((row, i) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ff-repeater__row", "data-ff-repeater-row": i, children: [
13023
13277
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ff-repeater__row-head", children: [
@@ -13066,6 +13320,8 @@ function RepeaterField({
13066
13320
  /* @__PURE__ */ jsxRuntime.jsx(
13067
13321
  ConfigFieldRenderer,
13068
13322
  {
13323
+ graph,
13324
+ nodeId,
13069
13325
  field: sub,
13070
13326
  value: row[sub.key],
13071
13327
  onChange: (cell) => setCell(i, sub.key, cell),
@@ -13092,6 +13348,7 @@ function RepeaterField({
13092
13348
  ] });
13093
13349
  }
13094
13350
  function KeyValueField({
13351
+ id: id2,
13095
13352
  field,
13096
13353
  value,
13097
13354
  onChange
@@ -13110,7 +13367,7 @@ function KeyValueField({
13110
13367
  const setVal = (i, val) => commit(entries.map(([k2, v2], idx) => idx === i ? [k2, val] : [k2, v2]));
13111
13368
  const remove2 = (i) => commit(entries.filter((_2, idx) => idx !== i));
13112
13369
  const add = () => commit([...entries, ["", ""]]);
13113
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ff-keyvalue", "data-ff-keyvalue": field.key, children: [
13370
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { id: id2, role: "group", className: "ff-keyvalue", "data-ff-keyvalue": field.key, "data-ff-field": field.key, children: [
13114
13371
  entries.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ff-keyvalue__head", children: [
13115
13372
  /* @__PURE__ */ jsxRuntime.jsx("span", { children: field.keyLabel ?? "Key" }),
13116
13373
  /* @__PURE__ */ jsxRuntime.jsx("span", { children: field.valueLabel ?? "Value" }),
@@ -13166,34 +13423,76 @@ function KeyValueField({
13166
13423
  ] })
13167
13424
  ] });
13168
13425
  }
13169
- function JsonField({ value, onChange, rows }) {
13170
- const initial = ReactExports.useMemo(() => {
13426
+ function JsonField({
13427
+ value,
13428
+ onChange,
13429
+ rows,
13430
+ handle
13431
+ }) {
13432
+ const serialized = ReactExports.useMemo(() => {
13171
13433
  try {
13172
13434
  return value === void 0 ? "" : JSON.stringify(value, null, 2);
13173
13435
  } catch {
13174
13436
  return "";
13175
13437
  }
13176
13438
  }, [value]);
13177
- return /* @__PURE__ */ jsxRuntime.jsx(
13178
- "textarea",
13179
- {
13180
- className: "ff-panel__input ff-panel__input--json",
13181
- rows: rows ?? 6,
13182
- defaultValue: initial,
13183
- spellCheck: false,
13184
- onBlur: (e) => {
13185
- const text = e.target.value.trim();
13186
- if (text === "") {
13187
- onChange(void 0);
13188
- return;
13189
- }
13190
- try {
13191
- onChange(JSON.parse(text));
13192
- } catch {
13439
+ const [text, setText] = ReactExports.useState(serialized);
13440
+ const [error, setError] = ReactExports.useState(null);
13441
+ const [touched, setTouched] = ReactExports.useState(false);
13442
+ const lastProps = ReactExports.useRef(serialized);
13443
+ const committed = ReactExports.useRef(null);
13444
+ if (serialized !== lastProps.current) {
13445
+ lastProps.current = serialized;
13446
+ if (serialized !== committed.current) {
13447
+ setText(serialized);
13448
+ setError(null);
13449
+ setTouched(false);
13450
+ }
13451
+ }
13452
+ const commit = (next) => {
13453
+ const trimmed = next.trim();
13454
+ if (trimmed === "") {
13455
+ committed.current = "";
13456
+ onChange(void 0);
13457
+ return null;
13458
+ }
13459
+ try {
13460
+ const parsed = JSON.parse(trimmed);
13461
+ committed.current = JSON.stringify(parsed, null, 2);
13462
+ onChange(parsed);
13463
+ return null;
13464
+ } catch (e) {
13465
+ return e instanceof Error ? e.message : "Invalid JSON";
13466
+ }
13467
+ };
13468
+ const errorId = handle.id ? `${handle.id}-error` : void 0;
13469
+ const showError = touched && error !== null;
13470
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
13471
+ /* @__PURE__ */ jsxRuntime.jsx(
13472
+ "textarea",
13473
+ {
13474
+ ...handle,
13475
+ className: "ff-panel__input ff-panel__input--json",
13476
+ rows: rows ?? 6,
13477
+ value: text,
13478
+ spellCheck: false,
13479
+ "aria-invalid": showError || void 0,
13480
+ "aria-describedby": showError ? errorId : void 0,
13481
+ onChange: (e) => {
13482
+ setText(e.target.value);
13483
+ setError(commit(e.target.value));
13484
+ },
13485
+ onBlur: (e) => {
13486
+ setTouched(true);
13487
+ setError(commit(e.target.value));
13193
13488
  }
13194
13489
  }
13195
- }
13196
- );
13490
+ ),
13491
+ showError && /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "ff-panel__issue ff-panel__issue--field", id: errorId, role: "alert", children: [
13492
+ "\u26A0 ",
13493
+ error
13494
+ ] })
13495
+ ] });
13197
13496
  }
13198
13497
  function NodeConfigPanel({
13199
13498
  node,
@@ -13204,6 +13503,7 @@ function NodeConfigPanel({
13204
13503
  renderCredentialField,
13205
13504
  renderDocumentField,
13206
13505
  fieldRenderers,
13506
+ graph,
13207
13507
  className,
13208
13508
  style: style2
13209
13509
  }) {
@@ -13309,7 +13609,9 @@ function NodeConfigPanel({
13309
13609
  onChange: (v2) => setConfigValue(field.key, v2),
13310
13610
  renderCredentialField,
13311
13611
  renderDocumentField: documentField,
13312
- fieldRenderers
13612
+ fieldRenderers,
13613
+ graph,
13614
+ nodeId: node.id
13313
13615
  }
13314
13616
  )
13315
13617
  ] }, field.key))
@@ -14061,6 +14363,7 @@ function FlowEditorInner({
14061
14363
  metadata,
14062
14364
  showPalette = true,
14063
14365
  showPanel = true,
14366
+ fieldRenderers,
14064
14367
  showFeed = true,
14065
14368
  extraToolbar,
14066
14369
  actions = [],
@@ -14620,6 +14923,8 @@ function FlowEditorInner({
14620
14923
  className: "ff-editor__panel",
14621
14924
  node: api.selected,
14622
14925
  onChange: api.updateNode,
14926
+ fieldRenderers,
14927
+ graph: flow,
14623
14928
  onDelete: builtins.delete === false ? void 0 : (n) => api.deleteNodes([n.id])
14624
14929
  }
14625
14930
  ),
@@ -14979,6 +15284,7 @@ exports.ActionNode = ActionNode;
14979
15284
  exports.BUILTIN_KINDS = BUILTIN_KINDS;
14980
15285
  exports.ConfigFieldRenderer = ConfigFieldRenderer;
14981
15286
  exports.DecisionNode = DecisionNode;
15287
+ exports.ExpressionField = ExpressionField;
14982
15288
  exports.FlowCanvas = FlowCanvas;
14983
15289
  exports.FlowEditor = FlowEditor;
14984
15290
  exports.FlowRunControls = FlowRunControls;
@@ -15002,6 +15308,8 @@ exports.WORKFLOW_SCHEMA_VERSION = WORKFLOW_SCHEMA_VERSION;
15002
15308
  exports.alignNodes = alignNodes;
15003
15309
  exports.applyOutputsToNodes = applyOutputsToNodes;
15004
15310
  exports.applyStatusesToNodes = applyStatusesToNodes;
15311
+ exports.availableVariables = availableVariables;
15312
+ exports.baseVariables = baseVariables;
15005
15313
  exports.buildNodeTypes = buildNodeTypes;
15006
15314
  exports.categoryAccent = categoryAccent;
15007
15315
  exports.clearNodeKindOverrides = clearNodeKindOverrides;
@@ -15013,6 +15321,7 @@ exports.defaultConfigFor = defaultConfigFor;
15013
15321
  exports.defaultNodeTypes = defaultNodeTypes;
15014
15322
  exports.defaultPortCompatibility = defaultPortCompatibility;
15015
15323
  exports.defineNode = defineNode;
15324
+ exports.describeExpressionGrammar = describeExpressionGrammar;
15016
15325
  exports.distributeNodes = distributeNodes;
15017
15326
  exports.encodePause = encodePause;
15018
15327
  exports.exportWorkflow = exportWorkflow;
@@ -15027,6 +15336,7 @@ exports.listNodeKinds = listNodeKinds;
15027
15336
  exports.migrateSchema = migrateSchema;
15028
15337
  exports.onNodeKindsChanged = onNodeKindsChanged;
15029
15338
  exports.onRichInputAdapterChanged = onRichInputAdapterChanged;
15339
+ exports.outputFieldsFor = outputFieldsFor;
15030
15340
  exports.overrideNodeKind = overrideNodeKind;
15031
15341
  exports.paletteDropHandlers = paletteDropHandlers;
15032
15342
  exports.pauseForHuman = pauseForHuman;