@particle-academy/fancy-flow 0.40.4 → 0.41.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 +84 -0
- package/dist/ConfigFieldRenderer-D2TejNY1.d.ts +84 -0
- package/dist/engine.d.cts +3 -3
- package/dist/engine.d.ts +3 -3
- package/dist/fields/react-fancy.cjs +40 -0
- package/dist/fields/react-fancy.cjs.map +1 -0
- package/dist/fields/react-fancy.d.cts +20 -0
- package/dist/fields/react-fancy.d.ts +20 -0
- package/dist/fields/react-fancy.js +37 -0
- package/dist/fields/react-fancy.js.map +1 -0
- package/dist/index.cjs +71 -24
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +64 -127
- package/dist/index.d.ts +64 -127
- package/dist/index.js +71 -24
- 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-BADFn97n.d.cts → registry-BNyT66SS.d.cts} +1 -1
- package/dist/{registry-DYbFLEcW.d.ts → registry-CS7kjMto.d.ts} +1 -1
- package/dist/styles.css +3 -0
- package/dist/styles.css.map +1 -1
- package/dist/{types-D6lsIGoe.d.ts → types-Cf75IbxY.d.ts} +13 -0
- package/dist/{types-D8Dezh9v.d.cts → types-cAdD1Og2.d.cts} +13 -0
- package/dist/ux.d.cts +1 -1
- package/dist/ux.d.ts +1 -1
- package/package.json +12 -2
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { ReactNode } from 'react';
|
|
3
|
+
import { a as ConfigField } from './types-cAdD1Og2.cjs';
|
|
4
|
+
|
|
5
|
+
/** What a host renderer is handed. */
|
|
6
|
+
type ConfigFieldRenderContext = {
|
|
7
|
+
field: ConfigField;
|
|
8
|
+
value: unknown;
|
|
9
|
+
onChange: (next: unknown) => void;
|
|
10
|
+
/** The id the panel's `<label>` points at. Put it on your control. */
|
|
11
|
+
id?: string;
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Render one field. Return `null` to fall back to the package's own rendering,
|
|
15
|
+
* so a host can claim a type conditionally instead of reimplementing every case.
|
|
16
|
+
*
|
|
17
|
+
* Named `...Fn` because `ConfigFieldRenderer` is already the component this
|
|
18
|
+
* module exports; two things with one name in a public API is a paper cut a
|
|
19
|
+
* consumer pays for, not us.
|
|
20
|
+
*/
|
|
21
|
+
type ConfigFieldRenderFn = (ctx: ConfigFieldRenderContext) => ReactNode;
|
|
22
|
+
type ConfigFieldRendererProps = {
|
|
23
|
+
field: ConfigField;
|
|
24
|
+
value: unknown;
|
|
25
|
+
onChange: (value: unknown) => void;
|
|
26
|
+
/**
|
|
27
|
+
* DOM id for the control this renders.
|
|
28
|
+
*
|
|
29
|
+
* Supplied by the caller rather than generated here so the panel's `<label>`
|
|
30
|
+
* can point at it — a label beside a control is not a label attached to one.
|
|
31
|
+
* Also the agent-facing handle: the Human+ contract asks that every
|
|
32
|
+
* interactive element have a stable identity so an agent targets it instead
|
|
33
|
+
* of guessing at the DOM, and until this existed nothing in the editor had
|
|
34
|
+
* one.
|
|
35
|
+
*/
|
|
36
|
+
id?: string;
|
|
37
|
+
renderCredentialField?: (props: {
|
|
38
|
+
credentialType: string;
|
|
39
|
+
value: unknown;
|
|
40
|
+
onChange: (next: unknown) => void;
|
|
41
|
+
}) => ReactNode;
|
|
42
|
+
/**
|
|
43
|
+
* Editor for `document` fields. fancy-flow stores the document but never
|
|
44
|
+
* interprets it — the host owns the editing surface, exactly as it does for
|
|
45
|
+
* `credential`. This is the seam for rich human-input steps (authored pages,
|
|
46
|
+
* required reading, multi-section forms) without the package depending on
|
|
47
|
+
* any particular document model.
|
|
48
|
+
*/
|
|
49
|
+
renderDocumentField?: (props: {
|
|
50
|
+
documentType?: string;
|
|
51
|
+
value: unknown;
|
|
52
|
+
onChange: (next: unknown) => void;
|
|
53
|
+
}) => ReactNode;
|
|
54
|
+
/**
|
|
55
|
+
* Host renderers keyed by field `type`.
|
|
56
|
+
*
|
|
57
|
+
* The generic form of `renderDocumentField` / `renderCredentialField`: those
|
|
58
|
+
* cover two types the package deliberately does not interpret, this covers
|
|
59
|
+
* any type at all — including one the package has never heard of.
|
|
60
|
+
*
|
|
61
|
+
* Without it, a richer field had to be rendered OUTSIDE the panel, so that
|
|
62
|
+
* node's config stopped living where every other field does. An unknown type
|
|
63
|
+
* also fell through to `default:` and rendered nothing, so the schema said the
|
|
64
|
+
* field existed and the panel showed empty space.
|
|
65
|
+
*
|
|
66
|
+
* Consulted BEFORE the built-in switch, so a host can also replace a built-in
|
|
67
|
+
* (react-fancy inputs, say) through the same seam rather than a second one.
|
|
68
|
+
*/
|
|
69
|
+
fieldRenderers?: Record<string, ConfigFieldRenderFn>;
|
|
70
|
+
};
|
|
71
|
+
/**
|
|
72
|
+
* ConfigFieldRenderer — dispatches to the right input element per field type.
|
|
73
|
+
*
|
|
74
|
+
* Plain HTML inputs styled via the package's CSS so the package stays
|
|
75
|
+
* standalone (no react-fancy import required) and every surface stays themeable
|
|
76
|
+
* through the `--ff-*` token layer. Hosts that want react-fancy form components
|
|
77
|
+
* can supply their own field renderers via the kind's `renderPanel`.
|
|
78
|
+
*
|
|
79
|
+
* Each control carries the caller's `id` and a `data-ff-field` handle keyed by
|
|
80
|
+
* the field, so a label can point at it and an agent can find it by name.
|
|
81
|
+
*/
|
|
82
|
+
declare function ConfigFieldRenderer({ field, value, onChange, id, renderCredentialField, renderDocumentField, fieldRenderers, }: ConfigFieldRendererProps): react.JSX.Element | null;
|
|
83
|
+
|
|
84
|
+
export { type ConfigFieldRenderFn as C, type ConfigFieldRenderContext as a, ConfigFieldRenderer as b, type ConfigFieldRendererProps as c };
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { ReactNode } from 'react';
|
|
3
|
+
import { a as ConfigField } from './types-Cf75IbxY.js';
|
|
4
|
+
|
|
5
|
+
/** What a host renderer is handed. */
|
|
6
|
+
type ConfigFieldRenderContext = {
|
|
7
|
+
field: ConfigField;
|
|
8
|
+
value: unknown;
|
|
9
|
+
onChange: (next: unknown) => void;
|
|
10
|
+
/** The id the panel's `<label>` points at. Put it on your control. */
|
|
11
|
+
id?: string;
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Render one field. Return `null` to fall back to the package's own rendering,
|
|
15
|
+
* so a host can claim a type conditionally instead of reimplementing every case.
|
|
16
|
+
*
|
|
17
|
+
* Named `...Fn` because `ConfigFieldRenderer` is already the component this
|
|
18
|
+
* module exports; two things with one name in a public API is a paper cut a
|
|
19
|
+
* consumer pays for, not us.
|
|
20
|
+
*/
|
|
21
|
+
type ConfigFieldRenderFn = (ctx: ConfigFieldRenderContext) => ReactNode;
|
|
22
|
+
type ConfigFieldRendererProps = {
|
|
23
|
+
field: ConfigField;
|
|
24
|
+
value: unknown;
|
|
25
|
+
onChange: (value: unknown) => void;
|
|
26
|
+
/**
|
|
27
|
+
* DOM id for the control this renders.
|
|
28
|
+
*
|
|
29
|
+
* Supplied by the caller rather than generated here so the panel's `<label>`
|
|
30
|
+
* can point at it — a label beside a control is not a label attached to one.
|
|
31
|
+
* Also the agent-facing handle: the Human+ contract asks that every
|
|
32
|
+
* interactive element have a stable identity so an agent targets it instead
|
|
33
|
+
* of guessing at the DOM, and until this existed nothing in the editor had
|
|
34
|
+
* one.
|
|
35
|
+
*/
|
|
36
|
+
id?: string;
|
|
37
|
+
renderCredentialField?: (props: {
|
|
38
|
+
credentialType: string;
|
|
39
|
+
value: unknown;
|
|
40
|
+
onChange: (next: unknown) => void;
|
|
41
|
+
}) => ReactNode;
|
|
42
|
+
/**
|
|
43
|
+
* Editor for `document` fields. fancy-flow stores the document but never
|
|
44
|
+
* interprets it — the host owns the editing surface, exactly as it does for
|
|
45
|
+
* `credential`. This is the seam for rich human-input steps (authored pages,
|
|
46
|
+
* required reading, multi-section forms) without the package depending on
|
|
47
|
+
* any particular document model.
|
|
48
|
+
*/
|
|
49
|
+
renderDocumentField?: (props: {
|
|
50
|
+
documentType?: string;
|
|
51
|
+
value: unknown;
|
|
52
|
+
onChange: (next: unknown) => void;
|
|
53
|
+
}) => ReactNode;
|
|
54
|
+
/**
|
|
55
|
+
* Host renderers keyed by field `type`.
|
|
56
|
+
*
|
|
57
|
+
* The generic form of `renderDocumentField` / `renderCredentialField`: those
|
|
58
|
+
* cover two types the package deliberately does not interpret, this covers
|
|
59
|
+
* any type at all — including one the package has never heard of.
|
|
60
|
+
*
|
|
61
|
+
* Without it, a richer field had to be rendered OUTSIDE the panel, so that
|
|
62
|
+
* node's config stopped living where every other field does. An unknown type
|
|
63
|
+
* also fell through to `default:` and rendered nothing, so the schema said the
|
|
64
|
+
* field existed and the panel showed empty space.
|
|
65
|
+
*
|
|
66
|
+
* Consulted BEFORE the built-in switch, so a host can also replace a built-in
|
|
67
|
+
* (react-fancy inputs, say) through the same seam rather than a second one.
|
|
68
|
+
*/
|
|
69
|
+
fieldRenderers?: Record<string, ConfigFieldRenderFn>;
|
|
70
|
+
};
|
|
71
|
+
/**
|
|
72
|
+
* ConfigFieldRenderer — dispatches to the right input element per field type.
|
|
73
|
+
*
|
|
74
|
+
* Plain HTML inputs styled via the package's CSS so the package stays
|
|
75
|
+
* standalone (no react-fancy import required) and every surface stays themeable
|
|
76
|
+
* through the `--ff-*` token layer. Hosts that want react-fancy form components
|
|
77
|
+
* can supply their own field renderers via the kind's `renderPanel`.
|
|
78
|
+
*
|
|
79
|
+
* Each control carries the caller's `id` and a `data-ff-field` handle keyed by
|
|
80
|
+
* the field, so a label can point at it and an agent can find it by name.
|
|
81
|
+
*/
|
|
82
|
+
declare function ConfigFieldRenderer({ field, value, onChange, id, renderCredentialField, renderDocumentField, fieldRenderers, }: ConfigFieldRendererProps): react.JSX.Element | null;
|
|
83
|
+
|
|
84
|
+
export { type ConfigFieldRenderFn as C, type ConfigFieldRenderContext as a, ConfigFieldRenderer as b, type ConfigFieldRendererProps as c };
|
package/dist/engine.d.cts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
export { C as CohortGuard, a as CohortOptions, b as CohortPolicy, c as CohortResult, R as RunOptions, d as RunResult, r as runCohort, e as runFlow } from './run-cohort-Bjv2ZU6g.cjs';
|
|
2
2
|
import { F as FlowGraph, R as RunEvent, N as NodeExecutor } from './types-sOmpCitB.cjs';
|
|
3
3
|
export { A as ActionNodeData, B as BaseNodeData, D as DecisionNodeData, E as ExecutorRegistry, a as FlowEdge, b as FlowNode, c as FlowNodeData, d as FlowNodeKind, e as NodeRunStatus, f as NoteNodeData, O as OutputNodeData, P as PortDescriptor, S as SubgraphNodeData, T as TriggerNodeData } from './types-sOmpCitB.cjs';
|
|
4
|
-
export { d as defaultConfigFor, g as getNodeKind, k as kindIds, l as listNodeKinds, r as registerNodeKind, a as resolveKindId, v as validateConfig } from './registry-
|
|
5
|
-
import { P as PauseAwaiting } from './types-
|
|
6
|
-
export { C as CapabilityRequirement, a as ConfigField, L as LEGACY_PAUSE_PREFIXES, M as ManifestProblem, b as ManifestValidation, N as NODE_MANIFEST_SCHEMA_VERSION, c as NodeCategory, d as NodeKindDefinition, e as NodePackageManifest, f as NodeRuntimeId, g as NodeRuntimeSpec, h as PAUSE_PREFIX, i as PauseSignal, j as PortSpec, S as SideEffects, k as checkCapabilities, l as checkRuntimeSupport, m as decodePause, n as encodePause, o as isPause, p as pauseForHuman, s as satisfiesRange, v as validateNodeManifest } from './types-
|
|
4
|
+
export { d as defaultConfigFor, g as getNodeKind, k as kindIds, l as listNodeKinds, r as registerNodeKind, a as resolveKindId, v as validateConfig } from './registry-BNyT66SS.cjs';
|
|
5
|
+
import { P as PauseAwaiting } from './types-cAdD1Og2.cjs';
|
|
6
|
+
export { C as CapabilityRequirement, a as ConfigField, L as LEGACY_PAUSE_PREFIXES, M as ManifestProblem, b as ManifestValidation, N as NODE_MANIFEST_SCHEMA_VERSION, c as NodeCategory, d as NodeKindDefinition, e as NodePackageManifest, f as NodeRuntimeId, g as NodeRuntimeSpec, h as PAUSE_PREFIX, i as PauseSignal, j as PortSpec, S as SideEffects, k as checkCapabilities, l as checkRuntimeSupport, m as decodePause, n as encodePause, o as isPause, p as pauseForHuman, s as satisfiesRange, v as validateNodeManifest } from './types-cAdD1Og2.cjs';
|
|
7
7
|
import '@xyflow/react';
|
|
8
8
|
import 'react';
|
|
9
9
|
|
package/dist/engine.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
export { C as CohortGuard, a as CohortOptions, b as CohortPolicy, c as CohortResult, R as RunOptions, d as RunResult, r as runCohort, e as runFlow } from './run-cohort-BnP6ErHi.js';
|
|
2
2
|
import { F as FlowGraph, R as RunEvent, N as NodeExecutor } from './types-sOmpCitB.js';
|
|
3
3
|
export { A as ActionNodeData, B as BaseNodeData, D as DecisionNodeData, E as ExecutorRegistry, a as FlowEdge, b as FlowNode, c as FlowNodeData, d as FlowNodeKind, e as NodeRunStatus, f as NoteNodeData, O as OutputNodeData, P as PortDescriptor, S as SubgraphNodeData, T as TriggerNodeData } from './types-sOmpCitB.js';
|
|
4
|
-
export { d as defaultConfigFor, g as getNodeKind, k as kindIds, l as listNodeKinds, r as registerNodeKind, a as resolveKindId, v as validateConfig } from './registry-
|
|
5
|
-
import { P as PauseAwaiting } from './types-
|
|
6
|
-
export { C as CapabilityRequirement, a as ConfigField, L as LEGACY_PAUSE_PREFIXES, M as ManifestProblem, b as ManifestValidation, N as NODE_MANIFEST_SCHEMA_VERSION, c as NodeCategory, d as NodeKindDefinition, e as NodePackageManifest, f as NodeRuntimeId, g as NodeRuntimeSpec, h as PAUSE_PREFIX, i as PauseSignal, j as PortSpec, S as SideEffects, k as checkCapabilities, l as checkRuntimeSupport, m as decodePause, n as encodePause, o as isPause, p as pauseForHuman, s as satisfiesRange, v as validateNodeManifest } from './types-
|
|
4
|
+
export { d as defaultConfigFor, g as getNodeKind, k as kindIds, l as listNodeKinds, r as registerNodeKind, a as resolveKindId, v as validateConfig } from './registry-CS7kjMto.js';
|
|
5
|
+
import { P as PauseAwaiting } from './types-Cf75IbxY.js';
|
|
6
|
+
export { C as CapabilityRequirement, a as ConfigField, L as LEGACY_PAUSE_PREFIXES, M as ManifestProblem, b as ManifestValidation, N as NODE_MANIFEST_SCHEMA_VERSION, c as NodeCategory, d as NodeKindDefinition, e as NodePackageManifest, f as NodeRuntimeId, g as NodeRuntimeSpec, h as PAUSE_PREFIX, i as PauseSignal, j as PortSpec, S as SideEffects, k as checkCapabilities, l as checkRuntimeSupport, m as decodePause, n as encodePause, o as isPause, p as pauseForHuman, s as satisfiesRange, v as validateNodeManifest } from './types-Cf75IbxY.js';
|
|
7
7
|
import '@xyflow/react';
|
|
8
8
|
import 'react';
|
|
9
9
|
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var reactFancy = require('@particle-academy/react-fancy');
|
|
4
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
5
|
+
|
|
6
|
+
// src/fields/react-fancy.tsx
|
|
7
|
+
var jsonFieldRenderer = ({
|
|
8
|
+
field,
|
|
9
|
+
value,
|
|
10
|
+
onChange,
|
|
11
|
+
id
|
|
12
|
+
}) => {
|
|
13
|
+
if (field.type !== "json") return null;
|
|
14
|
+
return (
|
|
15
|
+
// JSX rather than `createElement` for one concrete reason: `data-*` props
|
|
16
|
+
// are special-cased by JSX but hit excess-property checking in a
|
|
17
|
+
// `createElement` object literal, so the agent handle would not typecheck.
|
|
18
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
19
|
+
reactFancy.JsonEditor,
|
|
20
|
+
{
|
|
21
|
+
id,
|
|
22
|
+
"data-ff-field": field.key,
|
|
23
|
+
value: value ?? {},
|
|
24
|
+
onChange: (next) => onChange(next),
|
|
25
|
+
keyMap: field.keyMap,
|
|
26
|
+
mode: "edit",
|
|
27
|
+
size: "sm",
|
|
28
|
+
rootLabel: field.label
|
|
29
|
+
}
|
|
30
|
+
)
|
|
31
|
+
);
|
|
32
|
+
};
|
|
33
|
+
var reactFancyFieldRenderers = {
|
|
34
|
+
json: jsonFieldRenderer
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
exports.jsonFieldRenderer = jsonFieldRenderer;
|
|
38
|
+
exports.reactFancyFieldRenderers = reactFancyFieldRenderers;
|
|
39
|
+
//# sourceMappingURL=react-fancy.cjs.map
|
|
40
|
+
//# sourceMappingURL=react-fancy.cjs.map
|
|
@@ -0,0 +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"]}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { C as ConfigFieldRenderFn } from '../ConfigFieldRenderer-4xK9hzvd.cjs';
|
|
2
|
+
import 'react';
|
|
3
|
+
import '../types-cAdD1Og2.cjs';
|
|
4
|
+
import '@xyflow/react';
|
|
5
|
+
import '../types-sOmpCitB.cjs';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Render a `json` config field as a react-fancy `JsonEditor`.
|
|
9
|
+
*
|
|
10
|
+
* Returns `null` for every other field type — the seam treats `null` as "not
|
|
11
|
+
* mine", so this can be handed over wholesale without claiming controls it has
|
|
12
|
+
* no business replacing.
|
|
13
|
+
*/
|
|
14
|
+
declare const jsonFieldRenderer: ConfigFieldRenderFn;
|
|
15
|
+
/**
|
|
16
|
+
* The full renderer map. Spread it to add your own types alongside.
|
|
17
|
+
*/
|
|
18
|
+
declare const reactFancyFieldRenderers: Record<string, ConfigFieldRenderFn>;
|
|
19
|
+
|
|
20
|
+
export { jsonFieldRenderer, reactFancyFieldRenderers };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { C as ConfigFieldRenderFn } from '../ConfigFieldRenderer-D2TejNY1.js';
|
|
2
|
+
import 'react';
|
|
3
|
+
import '../types-Cf75IbxY.js';
|
|
4
|
+
import '@xyflow/react';
|
|
5
|
+
import '../types-sOmpCitB.js';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Render a `json` config field as a react-fancy `JsonEditor`.
|
|
9
|
+
*
|
|
10
|
+
* Returns `null` for every other field type — the seam treats `null` as "not
|
|
11
|
+
* mine", so this can be handed over wholesale without claiming controls it has
|
|
12
|
+
* no business replacing.
|
|
13
|
+
*/
|
|
14
|
+
declare const jsonFieldRenderer: ConfigFieldRenderFn;
|
|
15
|
+
/**
|
|
16
|
+
* The full renderer map. Spread it to add your own types alongside.
|
|
17
|
+
*/
|
|
18
|
+
declare const reactFancyFieldRenderers: Record<string, ConfigFieldRenderFn>;
|
|
19
|
+
|
|
20
|
+
export { jsonFieldRenderer, reactFancyFieldRenderers };
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { JsonEditor } from '@particle-academy/react-fancy';
|
|
2
|
+
import { jsx } from 'react/jsx-runtime';
|
|
3
|
+
|
|
4
|
+
// src/fields/react-fancy.tsx
|
|
5
|
+
var jsonFieldRenderer = ({
|
|
6
|
+
field,
|
|
7
|
+
value,
|
|
8
|
+
onChange,
|
|
9
|
+
id
|
|
10
|
+
}) => {
|
|
11
|
+
if (field.type !== "json") return null;
|
|
12
|
+
return (
|
|
13
|
+
// JSX rather than `createElement` for one concrete reason: `data-*` props
|
|
14
|
+
// are special-cased by JSX but hit excess-property checking in a
|
|
15
|
+
// `createElement` object literal, so the agent handle would not typecheck.
|
|
16
|
+
/* @__PURE__ */ jsx(
|
|
17
|
+
JsonEditor,
|
|
18
|
+
{
|
|
19
|
+
id,
|
|
20
|
+
"data-ff-field": field.key,
|
|
21
|
+
value: value ?? {},
|
|
22
|
+
onChange: (next) => onChange(next),
|
|
23
|
+
keyMap: field.keyMap,
|
|
24
|
+
mode: "edit",
|
|
25
|
+
size: "sm",
|
|
26
|
+
rootLabel: field.label
|
|
27
|
+
}
|
|
28
|
+
)
|
|
29
|
+
);
|
|
30
|
+
};
|
|
31
|
+
var reactFancyFieldRenderers = {
|
|
32
|
+
json: jsonFieldRenderer
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export { jsonFieldRenderer, reactFancyFieldRenderers };
|
|
36
|
+
//# sourceMappingURL=react-fancy.js.map
|
|
37
|
+
//# sourceMappingURL=react-fancy.js.map
|
|
@@ -0,0 +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"]}
|
package/dist/index.cjs
CHANGED
|
@@ -12897,7 +12897,7 @@ function ConfigFieldRenderer({
|
|
|
12897
12897
|
}
|
|
12898
12898
|
);
|
|
12899
12899
|
case "json":
|
|
12900
|
-
return /* @__PURE__ */ jsxRuntime.jsx(JsonField, { value, onChange, rows: field.rows });
|
|
12900
|
+
return /* @__PURE__ */ jsxRuntime.jsx(JsonField, { value, onChange, rows: field.rows, handle });
|
|
12901
12901
|
case "expression":
|
|
12902
12902
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
12903
12903
|
"textarea",
|
|
@@ -12929,6 +12929,7 @@ function ConfigFieldRenderer({
|
|
|
12929
12929
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
12930
12930
|
RepeaterField,
|
|
12931
12931
|
{
|
|
12932
|
+
id: id2,
|
|
12932
12933
|
field,
|
|
12933
12934
|
value,
|
|
12934
12935
|
onChange,
|
|
@@ -12938,7 +12939,7 @@ function ConfigFieldRenderer({
|
|
|
12938
12939
|
}
|
|
12939
12940
|
);
|
|
12940
12941
|
case "keyvalue":
|
|
12941
|
-
return /* @__PURE__ */ jsxRuntime.jsx(KeyValueField, { field, value, onChange });
|
|
12942
|
+
return /* @__PURE__ */ jsxRuntime.jsx(KeyValueField, { id: id2, field, value, onChange });
|
|
12942
12943
|
case "document":
|
|
12943
12944
|
if (renderDocumentField) {
|
|
12944
12945
|
return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: renderDocumentField({ documentType: field.documentType, value, onChange }) });
|
|
@@ -12983,6 +12984,7 @@ function ChoiceField({
|
|
|
12983
12984
|
);
|
|
12984
12985
|
}
|
|
12985
12986
|
function RepeaterField({
|
|
12987
|
+
id: id2,
|
|
12986
12988
|
field,
|
|
12987
12989
|
value,
|
|
12988
12990
|
onChange,
|
|
@@ -13017,7 +13019,7 @@ function RepeaterField({
|
|
|
13017
13019
|
if (typeof raw === "number" || typeof raw === "boolean") return String(raw);
|
|
13018
13020
|
return `Item ${i + 1}`;
|
|
13019
13021
|
};
|
|
13020
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ff-repeater", "data-ff-repeater": field.key, children: [
|
|
13022
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { id: id2, role: "group", className: "ff-repeater", "data-ff-repeater": field.key, "data-ff-field": field.key, children: [
|
|
13021
13023
|
rows.length === 0 && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "ff-repeater__empty", children: "None yet." }),
|
|
13022
13024
|
rows.map((row, i) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ff-repeater__row", "data-ff-repeater-row": i, children: [
|
|
13023
13025
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ff-repeater__row-head", children: [
|
|
@@ -13092,6 +13094,7 @@ function RepeaterField({
|
|
|
13092
13094
|
] });
|
|
13093
13095
|
}
|
|
13094
13096
|
function KeyValueField({
|
|
13097
|
+
id: id2,
|
|
13095
13098
|
field,
|
|
13096
13099
|
value,
|
|
13097
13100
|
onChange
|
|
@@ -13110,7 +13113,7 @@ function KeyValueField({
|
|
|
13110
13113
|
const setVal = (i, val) => commit(entries.map(([k2, v2], idx) => idx === i ? [k2, val] : [k2, v2]));
|
|
13111
13114
|
const remove2 = (i) => commit(entries.filter((_2, idx) => idx !== i));
|
|
13112
13115
|
const add = () => commit([...entries, ["", ""]]);
|
|
13113
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ff-keyvalue", "data-ff-keyvalue": field.key, children: [
|
|
13116
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { id: id2, role: "group", className: "ff-keyvalue", "data-ff-keyvalue": field.key, "data-ff-field": field.key, children: [
|
|
13114
13117
|
entries.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ff-keyvalue__head", children: [
|
|
13115
13118
|
/* @__PURE__ */ jsxRuntime.jsx("span", { children: field.keyLabel ?? "Key" }),
|
|
13116
13119
|
/* @__PURE__ */ jsxRuntime.jsx("span", { children: field.valueLabel ?? "Value" }),
|
|
@@ -13166,34 +13169,76 @@ function KeyValueField({
|
|
|
13166
13169
|
] })
|
|
13167
13170
|
] });
|
|
13168
13171
|
}
|
|
13169
|
-
function JsonField({
|
|
13170
|
-
|
|
13172
|
+
function JsonField({
|
|
13173
|
+
value,
|
|
13174
|
+
onChange,
|
|
13175
|
+
rows,
|
|
13176
|
+
handle
|
|
13177
|
+
}) {
|
|
13178
|
+
const serialized = ReactExports.useMemo(() => {
|
|
13171
13179
|
try {
|
|
13172
13180
|
return value === void 0 ? "" : JSON.stringify(value, null, 2);
|
|
13173
13181
|
} catch {
|
|
13174
13182
|
return "";
|
|
13175
13183
|
}
|
|
13176
13184
|
}, [value]);
|
|
13177
|
-
|
|
13178
|
-
|
|
13179
|
-
|
|
13180
|
-
|
|
13181
|
-
|
|
13182
|
-
|
|
13183
|
-
|
|
13184
|
-
|
|
13185
|
-
|
|
13186
|
-
|
|
13187
|
-
|
|
13188
|
-
|
|
13189
|
-
|
|
13190
|
-
|
|
13191
|
-
|
|
13192
|
-
|
|
13185
|
+
const [text, setText] = ReactExports.useState(serialized);
|
|
13186
|
+
const [error, setError] = ReactExports.useState(null);
|
|
13187
|
+
const [touched, setTouched] = ReactExports.useState(false);
|
|
13188
|
+
const lastProps = ReactExports.useRef(serialized);
|
|
13189
|
+
const committed = ReactExports.useRef(null);
|
|
13190
|
+
if (serialized !== lastProps.current) {
|
|
13191
|
+
lastProps.current = serialized;
|
|
13192
|
+
if (serialized !== committed.current) {
|
|
13193
|
+
setText(serialized);
|
|
13194
|
+
setError(null);
|
|
13195
|
+
setTouched(false);
|
|
13196
|
+
}
|
|
13197
|
+
}
|
|
13198
|
+
const commit = (next) => {
|
|
13199
|
+
const trimmed = next.trim();
|
|
13200
|
+
if (trimmed === "") {
|
|
13201
|
+
committed.current = "";
|
|
13202
|
+
onChange(void 0);
|
|
13203
|
+
return null;
|
|
13204
|
+
}
|
|
13205
|
+
try {
|
|
13206
|
+
const parsed = JSON.parse(trimmed);
|
|
13207
|
+
committed.current = JSON.stringify(parsed, null, 2);
|
|
13208
|
+
onChange(parsed);
|
|
13209
|
+
return null;
|
|
13210
|
+
} catch (e) {
|
|
13211
|
+
return e instanceof Error ? e.message : "Invalid JSON";
|
|
13212
|
+
}
|
|
13213
|
+
};
|
|
13214
|
+
const errorId = handle.id ? `${handle.id}-error` : void 0;
|
|
13215
|
+
const showError = touched && error !== null;
|
|
13216
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
13217
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
13218
|
+
"textarea",
|
|
13219
|
+
{
|
|
13220
|
+
...handle,
|
|
13221
|
+
className: "ff-panel__input ff-panel__input--json",
|
|
13222
|
+
rows: rows ?? 6,
|
|
13223
|
+
value: text,
|
|
13224
|
+
spellCheck: false,
|
|
13225
|
+
"aria-invalid": showError || void 0,
|
|
13226
|
+
"aria-describedby": showError ? errorId : void 0,
|
|
13227
|
+
onChange: (e) => {
|
|
13228
|
+
setText(e.target.value);
|
|
13229
|
+
setError(commit(e.target.value));
|
|
13230
|
+
},
|
|
13231
|
+
onBlur: (e) => {
|
|
13232
|
+
setTouched(true);
|
|
13233
|
+
setError(commit(e.target.value));
|
|
13193
13234
|
}
|
|
13194
13235
|
}
|
|
13195
|
-
|
|
13196
|
-
|
|
13236
|
+
),
|
|
13237
|
+
showError && /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "ff-panel__issue ff-panel__issue--field", id: errorId, role: "alert", children: [
|
|
13238
|
+
"\u26A0 ",
|
|
13239
|
+
error
|
|
13240
|
+
] })
|
|
13241
|
+
] });
|
|
13197
13242
|
}
|
|
13198
13243
|
function NodeConfigPanel({
|
|
13199
13244
|
node,
|
|
@@ -14061,6 +14106,7 @@ function FlowEditorInner({
|
|
|
14061
14106
|
metadata,
|
|
14062
14107
|
showPalette = true,
|
|
14063
14108
|
showPanel = true,
|
|
14109
|
+
fieldRenderers,
|
|
14064
14110
|
showFeed = true,
|
|
14065
14111
|
extraToolbar,
|
|
14066
14112
|
actions = [],
|
|
@@ -14620,6 +14666,7 @@ function FlowEditorInner({
|
|
|
14620
14666
|
className: "ff-editor__panel",
|
|
14621
14667
|
node: api.selected,
|
|
14622
14668
|
onChange: api.updateNode,
|
|
14669
|
+
fieldRenderers,
|
|
14623
14670
|
onDelete: builtins.delete === false ? void 0 : (n) => api.deleteNodes([n.id])
|
|
14624
14671
|
}
|
|
14625
14672
|
),
|