@particle-academy/fancy-flow 0.52.0 → 0.54.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 (49) hide show
  1. package/dist/{ConfigFieldRenderer-Bc9Txnql.d.cts → HumanPrompt-CPl-Spmp.d.cts} +75 -1
  2. package/dist/{ConfigFieldRenderer-Bt3RGT7Z.d.ts → HumanPrompt-DlJSFi3F.d.ts} +75 -1
  3. package/dist/{chunk-JOXMAWAI.js → chunk-CIBW5ATA.js} +148 -5
  4. package/dist/chunk-CIBW5ATA.js.map +1 -0
  5. package/dist/{chunk-PEX3ZGCO.js → chunk-FG3C7LIG.js} +3 -3
  6. package/dist/{chunk-PEX3ZGCO.js.map → chunk-FG3C7LIG.js.map} +1 -1
  7. package/dist/{chunk-PU3CLTAG.js → chunk-FH6MWLKD.js} +3 -3
  8. package/dist/{chunk-PU3CLTAG.js.map → chunk-FH6MWLKD.js.map} +1 -1
  9. package/dist/{chunk-PWIHOR57.js → chunk-FTGVVKWX.js} +4 -4
  10. package/dist/{chunk-PWIHOR57.js.map → chunk-FTGVVKWX.js.map} +1 -1
  11. package/dist/{chunk-I7ZDPY7C.js → chunk-VWANLNL5.js} +3 -3
  12. package/dist/{chunk-I7ZDPY7C.js.map → chunk-VWANLNL5.js.map} +1 -1
  13. package/dist/{chunk-RR7A6IUW.js → chunk-WCFRKXH5.js} +3 -3
  14. package/dist/{chunk-RR7A6IUW.js.map → chunk-WCFRKXH5.js.map} +1 -1
  15. package/dist/durable.cjs +103 -2
  16. package/dist/durable.cjs.map +1 -1
  17. package/dist/durable.js +1 -1
  18. package/dist/engine.cjs +103 -2
  19. package/dist/engine.cjs.map +1 -1
  20. package/dist/engine.js +3 -3
  21. package/dist/fields/react-fancy.cjs +66 -0
  22. package/dist/fields/react-fancy.cjs.map +1 -1
  23. package/dist/fields/react-fancy.d.cts +35 -2
  24. package/dist/fields/react-fancy.d.ts +35 -2
  25. package/dist/fields/react-fancy.js +67 -2
  26. package/dist/fields/react-fancy.js.map +1 -1
  27. package/dist/index.cjs +245 -44
  28. package/dist/index.cjs.map +1 -1
  29. package/dist/index.d.cts +13 -2
  30. package/dist/index.d.ts +13 -2
  31. package/dist/index.js +111 -53
  32. package/dist/index.js.map +1 -1
  33. package/dist/registry.cjs +103 -2
  34. package/dist/registry.cjs.map +1 -1
  35. package/dist/registry.js +2 -2
  36. package/dist/runtime.cjs +103 -2
  37. package/dist/runtime.cjs.map +1 -1
  38. package/dist/runtime.js +3 -3
  39. package/dist/schema.cjs +103 -2
  40. package/dist/schema.cjs.map +1 -1
  41. package/dist/schema.js +2 -2
  42. package/dist/screens.cjs +103 -2
  43. package/dist/screens.cjs.map +1 -1
  44. package/dist/screens.js +4 -4
  45. package/dist/ux.cjs +103 -2
  46. package/dist/ux.cjs.map +1 -1
  47. package/dist/ux.js +1 -1
  48. package/package.json +1 -1
  49. 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
 
@@ -33,7 +33,73 @@ var jsonFieldRenderer = ({
33
33
  var reactFancyFieldRenderers = {
34
34
  json: jsonFieldRenderer
35
35
  };
36
+ var humanFieldRenderers = {
37
+ text: ({ field, id, value, onChange, autoFocusRef }) => /* @__PURE__ */ jsxRuntime.jsx(
38
+ reactFancy.Input,
39
+ {
40
+ id,
41
+ "data-ff-field": field.key,
42
+ ref: autoFocusRef,
43
+ placeholder: field.placeholder,
44
+ value: String(value ?? ""),
45
+ onValueChange: onChange
46
+ }
47
+ ),
48
+ textarea: ({ field, id, value, onChange, autoFocusRef }) => /* @__PURE__ */ jsxRuntime.jsx(
49
+ reactFancy.Textarea,
50
+ {
51
+ id,
52
+ "data-ff-field": field.key,
53
+ ref: autoFocusRef,
54
+ placeholder: field.placeholder,
55
+ rows: 3,
56
+ value: String(value ?? ""),
57
+ onValueChange: onChange
58
+ }
59
+ ),
60
+ number: ({ field, id, value, onChange, autoFocusRef }) => /* @__PURE__ */ jsxRuntime.jsx(
61
+ reactFancy.Input,
62
+ {
63
+ id,
64
+ "data-ff-field": field.key,
65
+ ref: autoFocusRef,
66
+ type: "number",
67
+ placeholder: field.placeholder,
68
+ value: String(value ?? ""),
69
+ onValueChange: (v) => onChange(v === "" ? "" : Number(v))
70
+ }
71
+ ),
72
+ select: ({ field, id, value, onChange }) => /* @__PURE__ */ jsxRuntime.jsx(
73
+ reactFancy.Select,
74
+ {
75
+ id,
76
+ "data-ff-field": field.key,
77
+ list: (field.options ?? []).map((o) => ({ value: o.value, label: o.label })),
78
+ value: String(value ?? ""),
79
+ onValueChange: onChange
80
+ }
81
+ ),
82
+ switch: ({ field, id, value, onChange }) => /* @__PURE__ */ jsxRuntime.jsx(
83
+ reactFancy.Switch,
84
+ {
85
+ id,
86
+ "data-ff-field": field.key,
87
+ checked: !!value,
88
+ onCheckedChange: onChange
89
+ }
90
+ ),
91
+ date: ({ field, id, value, onChange }) => /* @__PURE__ */ jsxRuntime.jsx(
92
+ reactFancy.DatePicker,
93
+ {
94
+ id,
95
+ "data-ff-field": field.key,
96
+ value: String(value ?? ""),
97
+ onValueChange: onChange
98
+ }
99
+ )
100
+ };
36
101
 
102
+ exports.humanFieldRenderers = humanFieldRenderers;
37
103
  exports.jsonFieldRenderer = jsonFieldRenderer;
38
104
  exports.reactFancyFieldRenderers = reactFancyFieldRenderers;
39
105
  //# sourceMappingURL=react-fancy.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/fields/react-fancy.tsx"],"names":["jsx","JsonEditor"],"mappings":";;;;;;AA6CO,IAAM,oBAAyC,CAAC;AAAA,EACrD,KAAA;AAAA,EACA,KAAA;AAAA,EACA,QAAA;AAAA,EACA;AACF,CAAA,KAAgC;AAC9B,EAAA,IAAI,KAAA,CAAM,IAAA,KAAS,MAAA,EAAQ,OAAO,IAAA;AAElC,EAAA;AAAA;AAAA;AAAA;AAAA,oBAIEA,cAAA;AAAA,MAACC,qBAAA;AAAA,MAAA;AAAA,QAIC,EAAA;AAAA,QACA,iBAAe,KAAA,CAAM,GAAA;AAAA,QACrB,KAAA,EAAQ,SAAS,EAAC;AAAA,QAClB,QAAA,EAAU,CAAC,IAAA,KAAoB,QAAA,CAAS,IAAI,CAAA;AAAA,QAI5C,QAAQ,KAAA,CAAM,MAAA;AAAA,QACd,IAAA,EAAK,MAAA;AAAA,QACL,IAAA,EAAK,IAAA;AAAA,QACL,WAAW,KAAA,CAAM;AAAA;AAAA;AACnB;AAEJ;AAKO,IAAM,wBAAA,GAAgE;AAAA,EAC3E,IAAA,EAAM;AACR","file":"react-fancy.cjs","sourcesContent":["/**\n * `@particle-academy/fancy-flow/fields/react-fancy` — richer config fields for\n * hosts that already run react-fancy.\n *\n * ```tsx\n * import { reactFancyFieldRenderers } from \"@particle-academy/fancy-flow/fields/react-fancy\";\n *\n * <FlowEditor … fieldRenderers={reactFancyFieldRenderers} />\n * ```\n *\n * ## Why an opt-in subpath rather than the panel itself\n *\n * fancy-flow themes every surface through a `--ff-*` custom-property layer that\n * a host overrides on `.ff-editor`. react-fancy's primitives are hardcoded\n * Tailwind palette classes and read no custom properties — so building them\n * into the panel would trade the theming contract for a nicer widget, which is\n * the trade `panel-labels.test.tsx` already decided against.\n *\n * Making it a subpath keeps both: react-fancy stays an OPTIONAL peer, a\n * standalone install pays nothing, and a Tailwind host opts in with one prop.\n * Same shape as `/llm/vercel-ai` and `/rich-input`.\n *\n * ## What it currently covers\n *\n * Just `json`, because that is the only built-in whose fallback is a raw\n * textarea. The others are already purpose-built controls; replacing them would\n * be churn. This is a map, so a host can spread it and add their own:\n *\n * ```tsx\n * fieldRenderers={{ ...reactFancyFieldRenderers, \"trigger-filters\": myRenderer }}\n * ```\n */\nimport { JsonEditor, type JsonValue } from \"@particle-academy/react-fancy\";\nimport type {\n ConfigFieldRenderContext,\n ConfigFieldRenderFn,\n} from \"../components/NodeConfigPanel/ConfigFieldRenderer\";\n\n/**\n * Render a `json` config field as a react-fancy `JsonEditor`.\n *\n * Returns `null` for every other field type — the seam treats `null` as \"not\n * mine\", so this can be handed over wholesale without claiming controls it has\n * no business replacing.\n */\nexport const jsonFieldRenderer: ConfigFieldRenderFn = ({\n field,\n value,\n onChange,\n id,\n}: ConfigFieldRenderContext) => {\n if (field.type !== \"json\") return null;\n\n return (\n // JSX rather than `createElement` for one concrete reason: `data-*` props\n // are special-cased by JSX but hit excess-property checking in a\n // `createElement` object literal, so the agent handle would not typecheck.\n <JsonEditor\n // The panel's `<label htmlFor>` points at this id, and the Human+\n // contract wants a stable handle an agent can target without guessing at\n // the DOM. Both reach the editor's root element via its rest spread.\n id={id}\n data-ff-field={field.key}\n value={(value ?? {}) as JsonValue}\n onChange={(next: JsonValue) => onChange(next)}\n // `keyMap` is what makes this more than a prettier textarea: it declares\n // a type per path, which picks the control and reports contradictions. A\n // string by design, so it survives an MCP round-trip.\n keyMap={field.keyMap}\n mode=\"edit\"\n size=\"sm\"\n rootLabel={field.label}\n />\n );\n};\n\n/**\n * The full renderer map. Spread it to add your own types alongside.\n */\nexport const reactFancyFieldRenderers: Record<string, ConfigFieldRenderFn> = {\n json: jsonFieldRenderer,\n};\n"]}
1
+ {"version":3,"sources":["../../src/fields/react-fancy.tsx"],"names":["jsx","JsonEditor","Input","Textarea","Select","Switch","DatePicker"],"mappings":"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","file":"react-fancy.cjs","sourcesContent":["/**\n * `@particle-academy/fancy-flow/fields/react-fancy` — richer config fields for\n * hosts that already run react-fancy.\n *\n * ```tsx\n * import { reactFancyFieldRenderers } from \"@particle-academy/fancy-flow/fields/react-fancy\";\n *\n * <FlowEditor … fieldRenderers={reactFancyFieldRenderers} />\n * ```\n *\n * ## Why an opt-in subpath rather than the panel itself\n *\n * fancy-flow themes every surface through a `--ff-*` custom-property layer that\n * a host overrides on `.ff-editor`. react-fancy's primitives are hardcoded\n * Tailwind palette classes and read no custom properties — so building them\n * into the panel would trade the theming contract for a nicer widget, which is\n * the trade `panel-labels.test.tsx` already decided against.\n *\n * Making it a subpath keeps both: react-fancy stays an OPTIONAL peer, a\n * standalone install pays nothing, and a Tailwind host opts in with one prop.\n * Same shape as `/llm/vercel-ai` and `/rich-input`.\n *\n * ## What it currently covers\n *\n * Just `json`, because that is the only built-in whose fallback is a raw\n * textarea. The others are already purpose-built controls; replacing them would\n * be churn. This is a map, so a host can spread it and add their own:\n *\n * ```tsx\n * fieldRenderers={{ ...reactFancyFieldRenderers, \"trigger-filters\": myRenderer }}\n * ```\n */\nimport {\n DatePicker,\n Input,\n JsonEditor,\n Select,\n Switch,\n Textarea,\n type JsonValue,\n} from \"@particle-academy/react-fancy\";\nimport type {\n ConfigFieldRenderContext,\n ConfigFieldRenderFn,\n} from \"../components/NodeConfigPanel/ConfigFieldRenderer\";\nimport type { HumanFieldRenderers } from \"../components/FlowEditor/HumanPrompt\";\n\n/**\n * Render a `json` config field as a react-fancy `JsonEditor`.\n *\n * Returns `null` for every other field type — the seam treats `null` as \"not\n * mine\", so this can be handed over wholesale without claiming controls it has\n * no business replacing.\n */\nexport const jsonFieldRenderer: ConfigFieldRenderFn = ({\n field,\n value,\n onChange,\n id,\n}: ConfigFieldRenderContext) => {\n if (field.type !== \"json\") return null;\n\n return (\n // JSX rather than `createElement` for one concrete reason: `data-*` props\n // are special-cased by JSX but hit excess-property checking in a\n // `createElement` object literal, so the agent handle would not typecheck.\n <JsonEditor\n // The panel's `<label htmlFor>` points at this id, and the Human+\n // contract wants a stable handle an agent can target without guessing at\n // the DOM. Both reach the editor's root element via its rest spread.\n id={id}\n data-ff-field={field.key}\n value={(value ?? {}) as JsonValue}\n onChange={(next: JsonValue) => onChange(next)}\n // `keyMap` is what makes this more than a prettier textarea: it declares\n // a type per path, which picks the control and reports contradictions. A\n // string by design, so it survives an MCP round-trip.\n keyMap={field.keyMap}\n mode=\"edit\"\n size=\"sm\"\n rootLabel={field.label}\n />\n );\n};\n\n/**\n * The full renderer map. Spread it to add your own types alongside.\n */\nexport const reactFancyFieldRenderers: Record<string, ConfigFieldRenderFn> = {\n json: jsonFieldRenderer,\n};\n\n/* ------------------------------------------------------------------------- *\n * Pause-form controls\n * ------------------------------------------------------------------------- */\n\n/**\n * react-fancy controls for the `user_input` / `human_approval` pause form.\n *\n * ```tsx\n * import { humanFieldRenderers } from \"@particle-academy/fancy-flow/fields/react-fancy\";\n *\n * <FlowEditor … humanFieldRenderers={humanFieldRenderers} />\n * ```\n *\n * ## Why this is opt-in rather than the default\n *\n * Rule 2 of the suite says our surfaces compose react-fancy rather than\n * hand-rolling what a primitive already covers, and the pause form's built-ins\n * are native elements. That is not a gap in the kit — react-fancy has every\n * primitive needed — it is a PACKAGING constraint: react-fancy is an OPTIONAL\n * peer, `HumanPrompt` ships in the main entry, and importing it there would\n * make a standalone `npm install @particle-academy/fancy-flow` fail to resolve\n * at import time while bypassing the `--ff-*` token layer hosts theme\n * `.ff-editor` with.\n *\n * This map is how a host that HAS react-fancy gets the Fancy controls anyway,\n * with one prop and no cost to anyone else. Same shape and same reasoning as\n * `reactFancyFieldRenderers` above.\n *\n * ## What it covers, and what it deliberately does not\n *\n * `datetime` is absent: react-fancy's `DatePicker` is date-only, so claiming\n * the type here would render a control that silently drops the time half of a\n * value the field promised to collect. It falls through to the built-in\n * `<input type=\"datetime-local\">`, which does collect it. An honest gap beats a\n * control that looks right and loses data.\n */\nexport const humanFieldRenderers: HumanFieldRenderers = {\n text: ({ field, id, value, onChange, autoFocusRef }) => (\n <Input\n id={id}\n data-ff-field={field.key}\n ref={autoFocusRef as React.RefObject<HTMLInputElement> | undefined}\n placeholder={field.placeholder}\n value={String(value ?? \"\")}\n onValueChange={onChange}\n />\n ),\n\n textarea: ({ field, id, value, onChange, autoFocusRef }) => (\n <Textarea\n id={id}\n data-ff-field={field.key}\n ref={autoFocusRef as React.RefObject<HTMLTextAreaElement> | undefined}\n placeholder={field.placeholder}\n rows={3}\n value={String(value ?? \"\")}\n onValueChange={onChange}\n />\n ),\n\n number: ({ field, id, value, onChange, autoFocusRef }) => (\n <Input\n id={id}\n data-ff-field={field.key}\n ref={autoFocusRef as React.RefObject<HTMLInputElement> | undefined}\n type=\"number\"\n placeholder={field.placeholder}\n value={String(value ?? \"\")}\n // The built-in resolves a number field to a NUMBER, so this must too --\n // the value here is what resumes the paused run, and handing the next\n // node \"41\" where it resolved 41 before is a silent type change that no\n // test downstream would attribute to a renderer swap.\n onValueChange={(v) => onChange(v === \"\" ? \"\" : Number(v))}\n />\n ),\n\n select: ({ field, id, value, onChange }) => (\n <Select\n id={id}\n data-ff-field={field.key}\n list={(field.options ?? []).map((o) => ({ value: o.value, label: o.label }))}\n value={String(value ?? \"\")}\n onValueChange={onChange}\n />\n ),\n\n switch: ({ field, id, value, onChange }) => (\n <Switch\n id={id}\n data-ff-field={field.key}\n checked={!!value}\n onCheckedChange={onChange}\n />\n ),\n\n date: ({ field, id, value, onChange }) => (\n <DatePicker\n id={id}\n data-ff-field={field.key}\n value={String(value ?? \"\")}\n onValueChange={onChange}\n />\n ),\n};\n\n/*\n * `time` and `datetime` are ABSENT on purpose, and for different reasons.\n *\n * `datetime`: react-fancy's `DatePicker` is date-only. Claiming the type here\n * would render a control that silently drops the time half of a value the field\n * promised to collect.\n *\n * `time`: `TimePicker` accepts NO `id` and no `data-*` passthrough --\n * `TimePickerProps` is a closed interface of nine props, none of them an\n * identifier. It therefore cannot satisfy the two handles every control in this\n * form owes: the `id` its `<label htmlFor>` points at, and the `data-ff-field`\n * an agent drives the surface by. Wrapping it in a handle-bearing div would put\n * the label on a non-focusable element, which is worse than not claiming it.\n *\n * Both fall through to the built-in `<input type=\"time\">` /\n * `<input type=\"datetime-local\">`, which do carry the handles and do collect\n * the whole value. An honest gap beats a control that looks right and loses\n * either data or its handle.\n *\n * The `TimePicker` half is a react-fancy FINDING rather than a fancy-flow\n * limitation -- per the suite's second rule, a missing primitive capability is\n * filed against the kit rather than routed around locally.\n */\n"]}
@@ -1,4 +1,4 @@
1
- import { C as ConfigFieldRenderFn } from '../ConfigFieldRenderer-Bc9Txnql.cjs';
1
+ import { H as HumanFieldRenderers, C as ConfigFieldRenderFn } from '../HumanPrompt-CPl-Spmp.cjs';
2
2
  import 'react';
3
3
  import '../types-JFYjPJAG.cjs';
4
4
  import '@xyflow/react';
@@ -17,5 +17,38 @@ declare const jsonFieldRenderer: ConfigFieldRenderFn;
17
17
  * The full renderer map. Spread it to add your own types alongside.
18
18
  */
19
19
  declare const reactFancyFieldRenderers: Record<string, ConfigFieldRenderFn>;
20
+ /**
21
+ * react-fancy controls for the `user_input` / `human_approval` pause form.
22
+ *
23
+ * ```tsx
24
+ * import { humanFieldRenderers } from "@particle-academy/fancy-flow/fields/react-fancy";
25
+ *
26
+ * <FlowEditor … humanFieldRenderers={humanFieldRenderers} />
27
+ * ```
28
+ *
29
+ * ## Why this is opt-in rather than the default
30
+ *
31
+ * Rule 2 of the suite says our surfaces compose react-fancy rather than
32
+ * hand-rolling what a primitive already covers, and the pause form's built-ins
33
+ * are native elements. That is not a gap in the kit — react-fancy has every
34
+ * primitive needed — it is a PACKAGING constraint: react-fancy is an OPTIONAL
35
+ * peer, `HumanPrompt` ships in the main entry, and importing it there would
36
+ * make a standalone `npm install @particle-academy/fancy-flow` fail to resolve
37
+ * at import time while bypassing the `--ff-*` token layer hosts theme
38
+ * `.ff-editor` with.
39
+ *
40
+ * This map is how a host that HAS react-fancy gets the Fancy controls anyway,
41
+ * with one prop and no cost to anyone else. Same shape and same reasoning as
42
+ * `reactFancyFieldRenderers` above.
43
+ *
44
+ * ## What it covers, and what it deliberately does not
45
+ *
46
+ * `datetime` is absent: react-fancy's `DatePicker` is date-only, so claiming
47
+ * the type here would render a control that silently drops the time half of a
48
+ * value the field promised to collect. It falls through to the built-in
49
+ * `<input type="datetime-local">`, which does collect it. An honest gap beats a
50
+ * control that looks right and loses data.
51
+ */
52
+ declare const humanFieldRenderers: HumanFieldRenderers;
20
53
 
21
- export { jsonFieldRenderer, reactFancyFieldRenderers };
54
+ export { humanFieldRenderers, jsonFieldRenderer, reactFancyFieldRenderers };
@@ -1,4 +1,4 @@
1
- import { C as ConfigFieldRenderFn } from '../ConfigFieldRenderer-Bt3RGT7Z.js';
1
+ import { H as HumanFieldRenderers, C as ConfigFieldRenderFn } from '../HumanPrompt-DlJSFi3F.js';
2
2
  import 'react';
3
3
  import '../types-JFYjPJAG.js';
4
4
  import '@xyflow/react';
@@ -17,5 +17,38 @@ declare const jsonFieldRenderer: ConfigFieldRenderFn;
17
17
  * The full renderer map. Spread it to add your own types alongside.
18
18
  */
19
19
  declare const reactFancyFieldRenderers: Record<string, ConfigFieldRenderFn>;
20
+ /**
21
+ * react-fancy controls for the `user_input` / `human_approval` pause form.
22
+ *
23
+ * ```tsx
24
+ * import { humanFieldRenderers } from "@particle-academy/fancy-flow/fields/react-fancy";
25
+ *
26
+ * <FlowEditor … humanFieldRenderers={humanFieldRenderers} />
27
+ * ```
28
+ *
29
+ * ## Why this is opt-in rather than the default
30
+ *
31
+ * Rule 2 of the suite says our surfaces compose react-fancy rather than
32
+ * hand-rolling what a primitive already covers, and the pause form's built-ins
33
+ * are native elements. That is not a gap in the kit — react-fancy has every
34
+ * primitive needed — it is a PACKAGING constraint: react-fancy is an OPTIONAL
35
+ * peer, `HumanPrompt` ships in the main entry, and importing it there would
36
+ * make a standalone `npm install @particle-academy/fancy-flow` fail to resolve
37
+ * at import time while bypassing the `--ff-*` token layer hosts theme
38
+ * `.ff-editor` with.
39
+ *
40
+ * This map is how a host that HAS react-fancy gets the Fancy controls anyway,
41
+ * with one prop and no cost to anyone else. Same shape and same reasoning as
42
+ * `reactFancyFieldRenderers` above.
43
+ *
44
+ * ## What it covers, and what it deliberately does not
45
+ *
46
+ * `datetime` is absent: react-fancy's `DatePicker` is date-only, so claiming
47
+ * the type here would render a control that silently drops the time half of a
48
+ * value the field promised to collect. It falls through to the built-in
49
+ * `<input type="datetime-local">`, which does collect it. An honest gap beats a
50
+ * control that looks right and loses data.
51
+ */
52
+ declare const humanFieldRenderers: HumanFieldRenderers;
20
53
 
21
- export { jsonFieldRenderer, reactFancyFieldRenderers };
54
+ export { humanFieldRenderers, jsonFieldRenderer, reactFancyFieldRenderers };
@@ -1,4 +1,4 @@
1
- import { JsonEditor } from '@particle-academy/react-fancy';
1
+ import { JsonEditor, DatePicker, Switch, Select, Input, Textarea } from '@particle-academy/react-fancy';
2
2
  import { jsx } from 'react/jsx-runtime';
3
3
 
4
4
  // src/fields/react-fancy.tsx
@@ -31,7 +31,72 @@ var jsonFieldRenderer = ({
31
31
  var reactFancyFieldRenderers = {
32
32
  json: jsonFieldRenderer
33
33
  };
34
+ var humanFieldRenderers = {
35
+ text: ({ field, id, value, onChange, autoFocusRef }) => /* @__PURE__ */ jsx(
36
+ Input,
37
+ {
38
+ id,
39
+ "data-ff-field": field.key,
40
+ ref: autoFocusRef,
41
+ placeholder: field.placeholder,
42
+ value: String(value ?? ""),
43
+ onValueChange: onChange
44
+ }
45
+ ),
46
+ textarea: ({ field, id, value, onChange, autoFocusRef }) => /* @__PURE__ */ jsx(
47
+ Textarea,
48
+ {
49
+ id,
50
+ "data-ff-field": field.key,
51
+ ref: autoFocusRef,
52
+ placeholder: field.placeholder,
53
+ rows: 3,
54
+ value: String(value ?? ""),
55
+ onValueChange: onChange
56
+ }
57
+ ),
58
+ number: ({ field, id, value, onChange, autoFocusRef }) => /* @__PURE__ */ jsx(
59
+ Input,
60
+ {
61
+ id,
62
+ "data-ff-field": field.key,
63
+ ref: autoFocusRef,
64
+ type: "number",
65
+ placeholder: field.placeholder,
66
+ value: String(value ?? ""),
67
+ onValueChange: (v) => onChange(v === "" ? "" : Number(v))
68
+ }
69
+ ),
70
+ select: ({ field, id, value, onChange }) => /* @__PURE__ */ jsx(
71
+ Select,
72
+ {
73
+ id,
74
+ "data-ff-field": field.key,
75
+ list: (field.options ?? []).map((o) => ({ value: o.value, label: o.label })),
76
+ value: String(value ?? ""),
77
+ onValueChange: onChange
78
+ }
79
+ ),
80
+ switch: ({ field, id, value, onChange }) => /* @__PURE__ */ jsx(
81
+ Switch,
82
+ {
83
+ id,
84
+ "data-ff-field": field.key,
85
+ checked: !!value,
86
+ onCheckedChange: onChange
87
+ }
88
+ ),
89
+ date: ({ field, id, value, onChange }) => /* @__PURE__ */ jsx(
90
+ DatePicker,
91
+ {
92
+ id,
93
+ "data-ff-field": field.key,
94
+ value: String(value ?? ""),
95
+ onValueChange: onChange
96
+ }
97
+ )
98
+ };
34
99
 
35
- export { jsonFieldRenderer, reactFancyFieldRenderers };
100
+ export { humanFieldRenderers, jsonFieldRenderer, reactFancyFieldRenderers };
36
101
  //# sourceMappingURL=react-fancy.js.map
37
102
  //# sourceMappingURL=react-fancy.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/fields/react-fancy.tsx"],"names":[],"mappings":";;;;AA6CO,IAAM,oBAAyC,CAAC;AAAA,EACrD,KAAA;AAAA,EACA,KAAA;AAAA,EACA,QAAA;AAAA,EACA;AACF,CAAA,KAAgC;AAC9B,EAAA,IAAI,KAAA,CAAM,IAAA,KAAS,MAAA,EAAQ,OAAO,IAAA;AAElC,EAAA;AAAA;AAAA;AAAA;AAAA,oBAIE,GAAA;AAAA,MAAC,UAAA;AAAA,MAAA;AAAA,QAIC,EAAA;AAAA,QACA,iBAAe,KAAA,CAAM,GAAA;AAAA,QACrB,KAAA,EAAQ,SAAS,EAAC;AAAA,QAClB,QAAA,EAAU,CAAC,IAAA,KAAoB,QAAA,CAAS,IAAI,CAAA;AAAA,QAI5C,QAAQ,KAAA,CAAM,MAAA;AAAA,QACd,IAAA,EAAK,MAAA;AAAA,QACL,IAAA,EAAK,IAAA;AAAA,QACL,WAAW,KAAA,CAAM;AAAA;AAAA;AACnB;AAEJ;AAKO,IAAM,wBAAA,GAAgE;AAAA,EAC3E,IAAA,EAAM;AACR","file":"react-fancy.js","sourcesContent":["/**\n * `@particle-academy/fancy-flow/fields/react-fancy` — richer config fields for\n * hosts that already run react-fancy.\n *\n * ```tsx\n * import { reactFancyFieldRenderers } from \"@particle-academy/fancy-flow/fields/react-fancy\";\n *\n * <FlowEditor … fieldRenderers={reactFancyFieldRenderers} />\n * ```\n *\n * ## Why an opt-in subpath rather than the panel itself\n *\n * fancy-flow themes every surface through a `--ff-*` custom-property layer that\n * a host overrides on `.ff-editor`. react-fancy's primitives are hardcoded\n * Tailwind palette classes and read no custom properties — so building them\n * into the panel would trade the theming contract for a nicer widget, which is\n * the trade `panel-labels.test.tsx` already decided against.\n *\n * Making it a subpath keeps both: react-fancy stays an OPTIONAL peer, a\n * standalone install pays nothing, and a Tailwind host opts in with one prop.\n * Same shape as `/llm/vercel-ai` and `/rich-input`.\n *\n * ## What it currently covers\n *\n * Just `json`, because that is the only built-in whose fallback is a raw\n * textarea. The others are already purpose-built controls; replacing them would\n * be churn. This is a map, so a host can spread it and add their own:\n *\n * ```tsx\n * fieldRenderers={{ ...reactFancyFieldRenderers, \"trigger-filters\": myRenderer }}\n * ```\n */\nimport { JsonEditor, type JsonValue } from \"@particle-academy/react-fancy\";\nimport type {\n ConfigFieldRenderContext,\n ConfigFieldRenderFn,\n} from \"../components/NodeConfigPanel/ConfigFieldRenderer\";\n\n/**\n * Render a `json` config field as a react-fancy `JsonEditor`.\n *\n * Returns `null` for every other field type — the seam treats `null` as \"not\n * mine\", so this can be handed over wholesale without claiming controls it has\n * no business replacing.\n */\nexport const jsonFieldRenderer: ConfigFieldRenderFn = ({\n field,\n value,\n onChange,\n id,\n}: ConfigFieldRenderContext) => {\n if (field.type !== \"json\") return null;\n\n return (\n // JSX rather than `createElement` for one concrete reason: `data-*` props\n // are special-cased by JSX but hit excess-property checking in a\n // `createElement` object literal, so the agent handle would not typecheck.\n <JsonEditor\n // The panel's `<label htmlFor>` points at this id, and the Human+\n // contract wants a stable handle an agent can target without guessing at\n // the DOM. Both reach the editor's root element via its rest spread.\n id={id}\n data-ff-field={field.key}\n value={(value ?? {}) as JsonValue}\n onChange={(next: JsonValue) => onChange(next)}\n // `keyMap` is what makes this more than a prettier textarea: it declares\n // a type per path, which picks the control and reports contradictions. A\n // string by design, so it survives an MCP round-trip.\n keyMap={field.keyMap}\n mode=\"edit\"\n size=\"sm\"\n rootLabel={field.label}\n />\n );\n};\n\n/**\n * The full renderer map. Spread it to add your own types alongside.\n */\nexport const reactFancyFieldRenderers: Record<string, ConfigFieldRenderFn> = {\n json: jsonFieldRenderer,\n};\n"]}
1
+ {"version":3,"sources":["../../src/fields/react-fancy.tsx"],"names":[],"mappings":"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","file":"react-fancy.js","sourcesContent":["/**\n * `@particle-academy/fancy-flow/fields/react-fancy` — richer config fields for\n * hosts that already run react-fancy.\n *\n * ```tsx\n * import { reactFancyFieldRenderers } from \"@particle-academy/fancy-flow/fields/react-fancy\";\n *\n * <FlowEditor … fieldRenderers={reactFancyFieldRenderers} />\n * ```\n *\n * ## Why an opt-in subpath rather than the panel itself\n *\n * fancy-flow themes every surface through a `--ff-*` custom-property layer that\n * a host overrides on `.ff-editor`. react-fancy's primitives are hardcoded\n * Tailwind palette classes and read no custom properties — so building them\n * into the panel would trade the theming contract for a nicer widget, which is\n * the trade `panel-labels.test.tsx` already decided against.\n *\n * Making it a subpath keeps both: react-fancy stays an OPTIONAL peer, a\n * standalone install pays nothing, and a Tailwind host opts in with one prop.\n * Same shape as `/llm/vercel-ai` and `/rich-input`.\n *\n * ## What it currently covers\n *\n * Just `json`, because that is the only built-in whose fallback is a raw\n * textarea. The others are already purpose-built controls; replacing them would\n * be churn. This is a map, so a host can spread it and add their own:\n *\n * ```tsx\n * fieldRenderers={{ ...reactFancyFieldRenderers, \"trigger-filters\": myRenderer }}\n * ```\n */\nimport {\n DatePicker,\n Input,\n JsonEditor,\n Select,\n Switch,\n Textarea,\n type JsonValue,\n} from \"@particle-academy/react-fancy\";\nimport type {\n ConfigFieldRenderContext,\n ConfigFieldRenderFn,\n} from \"../components/NodeConfigPanel/ConfigFieldRenderer\";\nimport type { HumanFieldRenderers } from \"../components/FlowEditor/HumanPrompt\";\n\n/**\n * Render a `json` config field as a react-fancy `JsonEditor`.\n *\n * Returns `null` for every other field type — the seam treats `null` as \"not\n * mine\", so this can be handed over wholesale without claiming controls it has\n * no business replacing.\n */\nexport const jsonFieldRenderer: ConfigFieldRenderFn = ({\n field,\n value,\n onChange,\n id,\n}: ConfigFieldRenderContext) => {\n if (field.type !== \"json\") return null;\n\n return (\n // JSX rather than `createElement` for one concrete reason: `data-*` props\n // are special-cased by JSX but hit excess-property checking in a\n // `createElement` object literal, so the agent handle would not typecheck.\n <JsonEditor\n // The panel's `<label htmlFor>` points at this id, and the Human+\n // contract wants a stable handle an agent can target without guessing at\n // the DOM. Both reach the editor's root element via its rest spread.\n id={id}\n data-ff-field={field.key}\n value={(value ?? {}) as JsonValue}\n onChange={(next: JsonValue) => onChange(next)}\n // `keyMap` is what makes this more than a prettier textarea: it declares\n // a type per path, which picks the control and reports contradictions. A\n // string by design, so it survives an MCP round-trip.\n keyMap={field.keyMap}\n mode=\"edit\"\n size=\"sm\"\n rootLabel={field.label}\n />\n );\n};\n\n/**\n * The full renderer map. Spread it to add your own types alongside.\n */\nexport const reactFancyFieldRenderers: Record<string, ConfigFieldRenderFn> = {\n json: jsonFieldRenderer,\n};\n\n/* ------------------------------------------------------------------------- *\n * Pause-form controls\n * ------------------------------------------------------------------------- */\n\n/**\n * react-fancy controls for the `user_input` / `human_approval` pause form.\n *\n * ```tsx\n * import { humanFieldRenderers } from \"@particle-academy/fancy-flow/fields/react-fancy\";\n *\n * <FlowEditor … humanFieldRenderers={humanFieldRenderers} />\n * ```\n *\n * ## Why this is opt-in rather than the default\n *\n * Rule 2 of the suite says our surfaces compose react-fancy rather than\n * hand-rolling what a primitive already covers, and the pause form's built-ins\n * are native elements. That is not a gap in the kit — react-fancy has every\n * primitive needed — it is a PACKAGING constraint: react-fancy is an OPTIONAL\n * peer, `HumanPrompt` ships in the main entry, and importing it there would\n * make a standalone `npm install @particle-academy/fancy-flow` fail to resolve\n * at import time while bypassing the `--ff-*` token layer hosts theme\n * `.ff-editor` with.\n *\n * This map is how a host that HAS react-fancy gets the Fancy controls anyway,\n * with one prop and no cost to anyone else. Same shape and same reasoning as\n * `reactFancyFieldRenderers` above.\n *\n * ## What it covers, and what it deliberately does not\n *\n * `datetime` is absent: react-fancy's `DatePicker` is date-only, so claiming\n * the type here would render a control that silently drops the time half of a\n * value the field promised to collect. It falls through to the built-in\n * `<input type=\"datetime-local\">`, which does collect it. An honest gap beats a\n * control that looks right and loses data.\n */\nexport const humanFieldRenderers: HumanFieldRenderers = {\n text: ({ field, id, value, onChange, autoFocusRef }) => (\n <Input\n id={id}\n data-ff-field={field.key}\n ref={autoFocusRef as React.RefObject<HTMLInputElement> | undefined}\n placeholder={field.placeholder}\n value={String(value ?? \"\")}\n onValueChange={onChange}\n />\n ),\n\n textarea: ({ field, id, value, onChange, autoFocusRef }) => (\n <Textarea\n id={id}\n data-ff-field={field.key}\n ref={autoFocusRef as React.RefObject<HTMLTextAreaElement> | undefined}\n placeholder={field.placeholder}\n rows={3}\n value={String(value ?? \"\")}\n onValueChange={onChange}\n />\n ),\n\n number: ({ field, id, value, onChange, autoFocusRef }) => (\n <Input\n id={id}\n data-ff-field={field.key}\n ref={autoFocusRef as React.RefObject<HTMLInputElement> | undefined}\n type=\"number\"\n placeholder={field.placeholder}\n value={String(value ?? \"\")}\n // The built-in resolves a number field to a NUMBER, so this must too --\n // the value here is what resumes the paused run, and handing the next\n // node \"41\" where it resolved 41 before is a silent type change that no\n // test downstream would attribute to a renderer swap.\n onValueChange={(v) => onChange(v === \"\" ? \"\" : Number(v))}\n />\n ),\n\n select: ({ field, id, value, onChange }) => (\n <Select\n id={id}\n data-ff-field={field.key}\n list={(field.options ?? []).map((o) => ({ value: o.value, label: o.label }))}\n value={String(value ?? \"\")}\n onValueChange={onChange}\n />\n ),\n\n switch: ({ field, id, value, onChange }) => (\n <Switch\n id={id}\n data-ff-field={field.key}\n checked={!!value}\n onCheckedChange={onChange}\n />\n ),\n\n date: ({ field, id, value, onChange }) => (\n <DatePicker\n id={id}\n data-ff-field={field.key}\n value={String(value ?? \"\")}\n onValueChange={onChange}\n />\n ),\n};\n\n/*\n * `time` and `datetime` are ABSENT on purpose, and for different reasons.\n *\n * `datetime`: react-fancy's `DatePicker` is date-only. Claiming the type here\n * would render a control that silently drops the time half of a value the field\n * promised to collect.\n *\n * `time`: `TimePicker` accepts NO `id` and no `data-*` passthrough --\n * `TimePickerProps` is a closed interface of nine props, none of them an\n * identifier. It therefore cannot satisfy the two handles every control in this\n * form owes: the `id` its `<label htmlFor>` points at, and the `data-ff-field`\n * an agent drives the surface by. Wrapping it in a handle-bearing div would put\n * the label on a non-focusable element, which is worse than not claiming it.\n *\n * Both fall through to the built-in `<input type=\"time\">` /\n * `<input type=\"datetime-local\">`, which do carry the handles and do collect\n * the whole value. An honest gap beats a control that looks right and loses\n * either data or its handle.\n *\n * The `TimePicker` half is a react-fancy FINDING rather than a fancy-flow\n * limitation -- per the suite's second rule, a missing primitive capability is\n * filed against the kit rather than routed around locally.\n */\n"]}