@particle-academy/fancy-flow 0.52.0 → 0.53.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 (39) hide show
  1. package/dist/{chunk-JOXMAWAI.js → chunk-CIBW5ATA.js} +148 -5
  2. package/dist/chunk-CIBW5ATA.js.map +1 -0
  3. package/dist/{chunk-PEX3ZGCO.js → chunk-FG3C7LIG.js} +3 -3
  4. package/dist/{chunk-PEX3ZGCO.js.map → chunk-FG3C7LIG.js.map} +1 -1
  5. package/dist/{chunk-PU3CLTAG.js → chunk-FH6MWLKD.js} +3 -3
  6. package/dist/{chunk-PU3CLTAG.js.map → chunk-FH6MWLKD.js.map} +1 -1
  7. package/dist/{chunk-PWIHOR57.js → chunk-FTGVVKWX.js} +4 -4
  8. package/dist/{chunk-PWIHOR57.js.map → chunk-FTGVVKWX.js.map} +1 -1
  9. package/dist/{chunk-I7ZDPY7C.js → chunk-VWANLNL5.js} +3 -3
  10. package/dist/{chunk-I7ZDPY7C.js.map → chunk-VWANLNL5.js.map} +1 -1
  11. package/dist/{chunk-RR7A6IUW.js → chunk-WCFRKXH5.js} +3 -3
  12. package/dist/{chunk-RR7A6IUW.js.map → chunk-WCFRKXH5.js.map} +1 -1
  13. package/dist/durable.cjs +103 -2
  14. package/dist/durable.cjs.map +1 -1
  15. package/dist/durable.js +1 -1
  16. package/dist/engine.cjs +103 -2
  17. package/dist/engine.cjs.map +1 -1
  18. package/dist/engine.js +3 -3
  19. package/dist/index.cjs +218 -37
  20. package/dist/index.cjs.map +1 -1
  21. package/dist/index.js +84 -46
  22. package/dist/index.js.map +1 -1
  23. package/dist/registry.cjs +103 -2
  24. package/dist/registry.cjs.map +1 -1
  25. package/dist/registry.js +2 -2
  26. package/dist/runtime.cjs +103 -2
  27. package/dist/runtime.cjs.map +1 -1
  28. package/dist/runtime.js +3 -3
  29. package/dist/schema.cjs +103 -2
  30. package/dist/schema.cjs.map +1 -1
  31. package/dist/schema.js +2 -2
  32. package/dist/screens.cjs +103 -2
  33. package/dist/screens.cjs.map +1 -1
  34. package/dist/screens.js +4 -4
  35. package/dist/ux.cjs +103 -2
  36. package/dist/ux.cjs.map +1 -1
  37. package/dist/ux.js +1 -1
  38. package/package.json +1 -1
  39. package/dist/chunk-JOXMAWAI.js.map +0 -1
package/dist/engine.js CHANGED
@@ -1,8 +1,8 @@
1
- export { runCohort } from './chunk-RR7A6IUW.js';
1
+ export { runCohort } from './chunk-WCFRKXH5.js';
2
2
  import { decodePause } from './chunk-UEOE6B52.js';
3
3
  export { LEGACY_PAUSE_PREFIXES, PAUSE_PREFIX, decodePause, encodePause, isPause, pauseForHuman } from './chunk-UEOE6B52.js';
4
- import { runFlow, getNodeKind, resolveNodePorts } from './chunk-JOXMAWAI.js';
5
- export { RunIdentity, defaultConfigFor, escapeSegment, getNodeKind, kindIds, listNodeKinds, registerNodeKind, resolveKindId, runFlow, validateConfig } from './chunk-JOXMAWAI.js';
4
+ import { runFlow, getNodeKind, resolveNodePorts } from './chunk-CIBW5ATA.js';
5
+ export { RunIdentity, defaultConfigFor, escapeSegment, getNodeKind, kindIds, listNodeKinds, registerNodeKind, resolveKindId, runFlow, validateConfig } from './chunk-CIBW5ATA.js';
6
6
  import './chunk-F5RPRB7A.js';
7
7
  import { isResolutionFailure, registerLlmClient, registerWorkflowResolver } from './chunk-USL4FMFU.js';
8
8
 
package/dist/index.cjs CHANGED
@@ -1719,6 +1719,101 @@ var init_layout = __esm({
1719
1719
  init_dagre_esm();
1720
1720
  }
1721
1721
  });
1722
+
1723
+ // src/components/FlowEditor/human-fields.ts
1724
+ var HUMAN_FIELD_TYPE_ALIASES = {
1725
+ text: "text",
1726
+ string: "text",
1727
+ str: "text",
1728
+ input: "text",
1729
+ textarea: "textarea",
1730
+ long_text: "textarea",
1731
+ longtext: "textarea",
1732
+ "long-text": "textarea",
1733
+ multiline: "textarea",
1734
+ paragraph: "textarea",
1735
+ markdown: "textarea",
1736
+ number: "number",
1737
+ numeric: "number",
1738
+ integer: "number",
1739
+ int: "number",
1740
+ float: "number",
1741
+ decimal: "number",
1742
+ select: "select",
1743
+ enum: "select",
1744
+ choice: "select",
1745
+ choices: "select",
1746
+ dropdown: "select",
1747
+ options: "select",
1748
+ radio: "select",
1749
+ switch: "switch",
1750
+ bool: "switch",
1751
+ boolean: "switch",
1752
+ checkbox: "switch",
1753
+ toggle: "switch",
1754
+ date: "date",
1755
+ datetime: "datetime",
1756
+ "datetime-local": "datetime",
1757
+ datetimelocal: "datetime",
1758
+ timestamp: "datetime",
1759
+ time: "time",
1760
+ email: "email",
1761
+ "e-mail": "email",
1762
+ url: "url",
1763
+ uri: "url",
1764
+ link: "url",
1765
+ tel: "tel",
1766
+ phone: "tel",
1767
+ telephone: "tel",
1768
+ password: "password",
1769
+ secret: "password"
1770
+ };
1771
+ function humanFieldType(raw) {
1772
+ if (typeof raw !== "string") return "text";
1773
+ return HUMAN_FIELD_TYPE_ALIASES[raw.trim().toLowerCase()] ?? "text";
1774
+ }
1775
+ function humanFieldOptions(raw) {
1776
+ const entries = [];
1777
+ if (Array.isArray(raw)) {
1778
+ for (const item of raw) {
1779
+ if (typeof item === "string" || typeof item === "number") {
1780
+ const value = String(item);
1781
+ if (value !== "") entries.push({ value, label: value });
1782
+ continue;
1783
+ }
1784
+ if (item && typeof item === "object") {
1785
+ const value = item.value;
1786
+ if (value === void 0 || value === null || value === "") continue;
1787
+ const label = item.label;
1788
+ entries.push({
1789
+ value: String(value),
1790
+ label: typeof label === "string" && label !== "" ? label : String(value)
1791
+ });
1792
+ }
1793
+ }
1794
+ } else if (raw && typeof raw === "object") {
1795
+ for (const [value, label] of Object.entries(raw)) {
1796
+ if (value === "") continue;
1797
+ entries.push({ value, label: typeof label === "string" && label !== "" ? label : value });
1798
+ }
1799
+ }
1800
+ return entries.length ? entries : void 0;
1801
+ }
1802
+ function humanInputFields(config) {
1803
+ const raw = Array.isArray(config?.fields) ? config.fields : [];
1804
+ const fields = raw.filter((f) => f && typeof f === "object" && typeof f.key === "string" && f.key).map((f) => ({
1805
+ key: f.key,
1806
+ label: typeof f.label === "string" && f.label ? f.label : f.key,
1807
+ type: humanFieldType(f.type),
1808
+ required: !!f.required,
1809
+ placeholder: typeof f.placeholder === "string" ? f.placeholder : void 0,
1810
+ options: humanFieldOptions(f.options ?? f.choices),
1811
+ default: f.default
1812
+ }));
1813
+ if (fields.length) return fields;
1814
+ const title = typeof config?.title === "string" && config.title ? config.title : "Your answer";
1815
+ return [{ key: "value", label: title, type: "textarea", required: true }];
1816
+ }
1722
1817
  var adapter = null;
1723
1818
  var listeners = /* @__PURE__ */ new Set();
1724
1819
  function registerRichInputAdapter(next) {
@@ -11448,6 +11543,20 @@ var subflowExecutor = async (ctx) => {
11448
11543
  };
11449
11544
 
11450
11545
  // src/registry/builtin.ts
11546
+ var HUMAN_FIELD_OUTPUT_TYPE = {
11547
+ text: "string",
11548
+ textarea: "string",
11549
+ select: "string",
11550
+ date: "string",
11551
+ datetime: "string",
11552
+ time: "string",
11553
+ email: "string",
11554
+ url: "string",
11555
+ tel: "string",
11556
+ password: "string",
11557
+ number: "number",
11558
+ switch: "boolean"
11559
+ };
11451
11560
  function casePorts(cases) {
11452
11561
  const byPort = /* @__PURE__ */ new Map();
11453
11562
  if (cases && typeof cases === "object" && !Array.isArray(cases)) {
@@ -11545,7 +11654,17 @@ var KINDS = [
11545
11654
  name: "@particle-academy/user_input",
11546
11655
  // The keys an author declared on THIS node — the case a static list cannot
11547
11656
  // express, and the one issue #5 named.
11548
- outputShape: (config) => (config.fields ?? []).filter((f) => typeof f.key === "string" && f.key !== "").map((f) => ({ path: f.key, type: "string", description: f.label })),
11657
+ //
11658
+ // The TYPE comes from the same normalizer the form renders with, so the
11659
+ // variable picker agrees with what the run actually resolves. It used to say
11660
+ // `string` for every field, which was wrong the moment a field was a number
11661
+ // or a switch — the picker told an author `{{ $json.age }}` was text while
11662
+ // the form handed the next node a number.
11663
+ outputShape: (config) => (config.fields ?? []).filter((f) => typeof f.key === "string" && f.key !== "").map((f) => ({
11664
+ path: f.key,
11665
+ type: HUMAN_FIELD_OUTPUT_TYPE[humanFieldType(f.type)],
11666
+ description: f.label
11667
+ })),
11549
11668
  aliases: ["user_input", "@fancy/user_input"],
11550
11669
  pausesForHuman: "input",
11551
11670
  category: "human",
@@ -11572,14 +11691,38 @@ var KINDS = [
11572
11691
  key: "type",
11573
11692
  label: "Type",
11574
11693
  default: "text",
11694
+ // Every control `HumanPrompt` can render. A type the form supports
11695
+ // but the panel cannot select is reachable only by hand-editing the
11696
+ // workflow JSON — which is the audience this panel exists for.
11575
11697
  options: [
11576
11698
  { value: "text", label: "Text" },
11577
11699
  { value: "textarea", label: "Long text" },
11578
11700
  { value: "number", label: "Number" },
11579
11701
  { value: "select", label: "Select" },
11580
- { value: "switch", label: "Switch" }
11702
+ { value: "switch", label: "Switch (yes / no)" },
11703
+ { value: "date", label: "Date" },
11704
+ { value: "datetime", label: "Date + time" },
11705
+ { value: "time", label: "Time" },
11706
+ { value: "email", label: "Email" },
11707
+ { value: "url", label: "URL" },
11708
+ { value: "tel", label: "Phone" },
11709
+ { value: "password", label: "Password" }
11581
11710
  ]
11582
11711
  },
11712
+ {
11713
+ // Without this a panel-authored select had nothing to choose from,
11714
+ // so it rendered an empty dropdown — the type was declarable and
11715
+ // unusable in the same breath.
11716
+ type: "keyvalue",
11717
+ key: "options",
11718
+ label: "Choices",
11719
+ description: "Select fields only. Left is the value the flow receives, right is what the person sees.",
11720
+ keyLabel: "Value",
11721
+ valueLabel: "Label",
11722
+ keyPlaceholder: "small",
11723
+ valuePlaceholder: "Small",
11724
+ addLabel: "Add choice"
11725
+ },
11583
11726
  { type: "switch", key: "required", label: "Required", default: false }
11584
11727
  ],
11585
11728
  default: [{ key: "answer", label: "Your answer", type: "textarea", required: true }]
@@ -14529,24 +14672,28 @@ function buildNodeTypes() {
14529
14672
  }
14530
14673
  return map;
14531
14674
  }
14532
- function humanInputFields(config) {
14533
- const raw = Array.isArray(config?.fields) ? config.fields : [];
14534
- const fields = raw.filter((f) => f && typeof f === "object" && typeof f.key === "string" && f.key).map((f) => ({
14535
- key: f.key,
14536
- label: typeof f.label === "string" && f.label ? f.label : f.key,
14537
- type: ["text", "textarea", "number", "select", "switch"].includes(f.type) ? f.type : "text",
14538
- required: !!f.required,
14539
- placeholder: typeof f.placeholder === "string" ? f.placeholder : void 0,
14540
- options: Array.isArray(f.options) ? f.options : void 0,
14541
- default: f.default
14542
- }));
14543
- if (fields.length) return fields;
14544
- const title = typeof config?.title === "string" && config.title ? config.title : "Your answer";
14545
- return [{ key: "value", label: title, type: "textarea", required: true }];
14546
- }
14675
+ var INPUT_TYPE = {
14676
+ text: "text",
14677
+ number: "number",
14678
+ date: "date",
14679
+ datetime: "datetime-local",
14680
+ time: "time",
14681
+ email: "email",
14682
+ url: "url",
14683
+ tel: "tel",
14684
+ password: "password"
14685
+ };
14547
14686
  function initialValues(fields) {
14548
14687
  const v2 = {};
14549
- for (const f of fields) v2[f.key] = f.type === "switch" ? !!f.default : f.default ?? "";
14688
+ for (const f of fields) {
14689
+ if (f.type === "switch") {
14690
+ v2[f.key] = !!f.default;
14691
+ } else if (f.type === "number") {
14692
+ v2[f.key] = typeof f.default === "number" ? f.default : f.default === void 0 || f.default === "" ? "" : Number(f.default);
14693
+ } else {
14694
+ v2[f.key] = f.default ?? "";
14695
+ }
14696
+ }
14550
14697
  return v2;
14551
14698
  }
14552
14699
  function HumanPrompt({ request, onCancel }) {
@@ -14581,6 +14728,8 @@ function InputBody({
14581
14728
  }) {
14582
14729
  const [values, setValues] = ReactExports.useState(() => initialValues(request.fields));
14583
14730
  const set3 = (k2, v2) => setValues((prev) => ({ ...prev, [k2]: v2 }));
14731
+ const uid = ReactExports.useId();
14732
+ const fieldId = (key) => `${uid}-${key}`;
14584
14733
  const missing = request.fields.filter((f) => f.required && (values[f.key] === void 0 || values[f.key] === ""));
14585
14734
  const submit = () => {
14586
14735
  if (missing.length === 0) request.resolve(values);
@@ -14599,12 +14748,21 @@ function InputBody({
14599
14748
  }
14600
14749
  },
14601
14750
  children: [
14602
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "ff-prompt__fields", children: request.fields.map((f, i) => /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "ff-prompt__field", children: [
14603
- /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "ff-prompt__label", children: [
14751
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "ff-prompt__fields", children: request.fields.map((f, i) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ff-prompt__field", children: [
14752
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "ff-prompt__label", htmlFor: fieldId(f.key), children: [
14604
14753
  f.label ?? f.key,
14605
- f.required && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ff-prompt__req", children: " *" })
14754
+ f.required && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ff-prompt__req", "aria-hidden": "true", children: " *" })
14606
14755
  ] }),
14607
- renderControl(f, values[f.key], (v2) => set3(f.key, v2), i === 0 ? firstRef : void 0)
14756
+ /* @__PURE__ */ jsxRuntime.jsx(
14757
+ FieldControl,
14758
+ {
14759
+ field: f,
14760
+ id: fieldId(f.key),
14761
+ value: values[f.key],
14762
+ onChange: (v2) => set3(f.key, v2),
14763
+ autoFocusRef: i === 0 ? firstRef : void 0
14764
+ }
14765
+ )
14608
14766
  ] }, f.key)) }),
14609
14767
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ff-prompt__actions", children: [
14610
14768
  /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "ff-prompt__btn ff-prompt__btn--ghost", onClick: onCancel, children: "Cancel" }),
@@ -14614,45 +14772,68 @@ function InputBody({
14614
14772
  }
14615
14773
  );
14616
14774
  }
14617
- function renderControl(f, value, onChange, ref) {
14618
- const common = { className: "ff-prompt__input", placeholder: f.placeholder };
14619
- if (f.type === "textarea") {
14775
+ function FieldControl({
14776
+ field,
14777
+ id: id2,
14778
+ value,
14779
+ onChange,
14780
+ autoFocusRef
14781
+ }) {
14782
+ const handle = { id: id2, "data-ff-field": field.key };
14783
+ if (field.type === "textarea") {
14620
14784
  return /* @__PURE__ */ jsxRuntime.jsx(
14621
14785
  "textarea",
14622
14786
  {
14623
- ...common,
14624
- ref,
14787
+ ...handle,
14788
+ className: "ff-prompt__input",
14789
+ placeholder: field.placeholder,
14790
+ ref: autoFocusRef,
14625
14791
  rows: 3,
14626
14792
  value: String(value ?? ""),
14627
14793
  onChange: (e) => onChange(e.target.value)
14628
14794
  }
14629
14795
  );
14630
14796
  }
14631
- if (f.type === "switch") {
14797
+ if (field.type === "switch") {
14632
14798
  return /* @__PURE__ */ jsxRuntime.jsx(
14633
14799
  "input",
14634
14800
  {
14801
+ ...handle,
14635
14802
  type: "checkbox",
14636
14803
  className: "ff-prompt__switch",
14804
+ ref: autoFocusRef,
14637
14805
  checked: !!value,
14638
14806
  onChange: (e) => onChange(e.target.checked)
14639
14807
  }
14640
14808
  );
14641
14809
  }
14642
- if (f.type === "select" && f.options && f.options.length) {
14643
- return /* @__PURE__ */ jsxRuntime.jsxs("select", { className: "ff-prompt__input", value: String(value ?? ""), onChange: (e) => onChange(e.target.value), children: [
14644
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "", disabled: true, children: "Choose\u2026" }),
14645
- f.options.map((o) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: o.value, children: o.label }, o.value))
14646
- ] });
14810
+ if (field.type === "select") {
14811
+ const options = field.options ?? [];
14812
+ return /* @__PURE__ */ jsxRuntime.jsxs(
14813
+ "select",
14814
+ {
14815
+ ...handle,
14816
+ className: "ff-prompt__input",
14817
+ ref: autoFocusRef,
14818
+ value: String(value ?? ""),
14819
+ onChange: (e) => onChange(e.target.value),
14820
+ children: [
14821
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "", disabled: true, children: options.length ? "Choose\u2026" : "No choices configured" }),
14822
+ options.map((o) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: o.value, children: o.label }, o.value))
14823
+ ]
14824
+ }
14825
+ );
14647
14826
  }
14648
14827
  return /* @__PURE__ */ jsxRuntime.jsx(
14649
14828
  "input",
14650
14829
  {
14651
- ...common,
14652
- ref,
14653
- type: f.type === "number" ? "number" : "text",
14830
+ ...handle,
14831
+ className: "ff-prompt__input",
14832
+ placeholder: field.placeholder,
14833
+ ref: autoFocusRef,
14834
+ type: INPUT_TYPE[field.type ?? "text"] ?? "text",
14654
14835
  value: String(value ?? ""),
14655
- onChange: (e) => onChange(f.type === "number" ? e.target.value === "" ? "" : Number(e.target.value) : e.target.value)
14836
+ onChange: (e) => onChange(field.type === "number" ? e.target.value === "" ? "" : Number(e.target.value) : e.target.value)
14656
14837
  }
14657
14838
  );
14658
14839
  }