@particle-academy/fancy-flow 0.8.0 → 0.10.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 (55) hide show
  1. package/README.md +104 -0
  2. package/dist/{chunk-BCXECQUC.js → chunk-6GGFEZH7.js} +3 -3
  3. package/dist/{chunk-BCXECQUC.js.map → chunk-6GGFEZH7.js.map} +1 -1
  4. package/dist/{chunk-WNVBXXOL.js → chunk-CPSOC27D.js} +43 -2
  5. package/dist/chunk-CPSOC27D.js.map +1 -0
  6. package/dist/chunk-F5RPRB7A.js +56 -0
  7. package/dist/chunk-F5RPRB7A.js.map +1 -0
  8. package/dist/{chunk-NVULCEDX.js → chunk-HNBO4HP3.js} +8 -4
  9. package/dist/chunk-HNBO4HP3.js.map +1 -0
  10. package/dist/{chunk-M2XKGQQL.js → chunk-NCPQDVUE.js} +178 -16
  11. package/dist/chunk-NCPQDVUE.js.map +1 -0
  12. package/dist/chunk-TITD5W4Y.js +26 -0
  13. package/dist/chunk-TITD5W4Y.js.map +1 -0
  14. package/dist/{chunk-QSSQRQN4.js → chunk-VEI743ZX.js} +3 -3
  15. package/dist/{chunk-QSSQRQN4.js.map → chunk-VEI743ZX.js.map} +1 -1
  16. package/dist/engine.cjs +32 -2
  17. package/dist/engine.cjs.map +1 -1
  18. package/dist/engine.js +3 -1
  19. package/dist/index.cjs +716 -40
  20. package/dist/index.cjs.map +1 -1
  21. package/dist/index.d.cts +45 -6
  22. package/dist/index.d.ts +45 -6
  23. package/dist/index.js +417 -23
  24. package/dist/index.js.map +1 -1
  25. package/dist/registry/index.d.cts +112 -4
  26. package/dist/registry/index.d.ts +112 -4
  27. package/dist/registry.cjs +312 -31
  28. package/dist/registry.cjs.map +1 -1
  29. package/dist/registry.js +4 -2
  30. package/dist/rich-input.cjs +56 -0
  31. package/dist/rich-input.cjs.map +1 -0
  32. package/dist/rich-input.d.cts +27 -0
  33. package/dist/rich-input.d.ts +27 -0
  34. package/dist/rich-input.js +37 -0
  35. package/dist/rich-input.js.map +1 -0
  36. package/dist/runtime.cjs +32 -2
  37. package/dist/runtime.cjs.map +1 -1
  38. package/dist/runtime.js +4 -2
  39. package/dist/schema.cjs +41 -0
  40. package/dist/schema.cjs.map +1 -1
  41. package/dist/schema.js +2 -2
  42. package/dist/styles.css +191 -0
  43. package/dist/styles.css.map +1 -1
  44. package/dist/types-BocBFh6l.d.ts +221 -0
  45. package/dist/types-DKqaUjF_.d.cts +221 -0
  46. package/dist/ux.cjs.map +1 -1
  47. package/dist/ux.d.cts +1 -1
  48. package/dist/ux.d.ts +1 -1
  49. package/dist/ux.js +1 -1
  50. package/package.json +24 -2
  51. package/dist/chunk-M2XKGQQL.js.map +0 -1
  52. package/dist/chunk-NVULCEDX.js.map +0 -1
  53. package/dist/chunk-WNVBXXOL.js.map +0 -1
  54. package/dist/types-C0wdN6QX.d.cts +0 -121
  55. package/dist/types-DnMe9Vsf.d.ts +0 -121
@@ -1,8 +1,116 @@
1
- import { a as NodeKindDefinition } from '../types-C0wdN6QX.cjs';
2
- export { C as ConfigField, b as CredentialConfigField, E as ExpressionConfigField, J as JsonConfigField, N as NodeCategory, c as NumberConfigField, R as RenderBodyContext, S as SelectConfigField, d as SwitchConfigField, T as TextConfigField, e as TextareaConfigField } from '../types-C0wdN6QX.cjs';
1
+ import { a as NodeKindDefinition, P as PortSpec } from '../types-DKqaUjF_.cjs';
2
+ export { C as ConfigField, b as CredentialConfigField, D as DocumentConfigField, E as ExpressionConfigField, J as JsonConfigField, K as KeyValueConfigField, N as NodeCategory, c as NumberConfigField, R as RenderBodyContext, d as RepeaterConfigField, e as RepeaterRowField, S as SelectConfigField, f as SwitchConfigField, T as TextConfigField, g as TextareaConfigField } from '../types-DKqaUjF_.cjs';
3
+ import { F as FlowNode, P as PortDescriptor } from '../types-BS3Gwnkq.cjs';
3
4
  import * as react from 'react';
5
+ import { ComponentType, ReactNode } from 'react';
4
6
  import { NodeProps, NodeTypes } from '@xyflow/react';
5
- import { F as FlowNode } from '../types-BS3Gwnkq.cjs';
7
+
8
+ /**
9
+ * Port resolution — the single place a node's ports are derived, shared by
10
+ * the canvas renderer and the headless runtime.
11
+ *
12
+ * This module is deliberately React-free: `runFlow` (the `/engine` entry)
13
+ * imports it, and the engine bundle must stay free of React.
14
+ *
15
+ * ## Why this is centralized
16
+ *
17
+ * Ports used to be read in two places that disagreed — the canvas consulted
18
+ * `data.outputs ?? kind.outputs`, while the runtime consulted `data.outputs`
19
+ * ONLY and fell back to a single `out` port. A kind that declared branch ports
20
+ * therefore drew correctly and then routed as if it had one output, unless the
21
+ * host remembered to mirror the ports onto every node's `data`. Both callers
22
+ * now go through `resolveNodePorts`, so declared ports and executed ports
23
+ * cannot drift apart.
24
+ */
25
+ /**
26
+ * Resolve a `PortSpec` against a config object.
27
+ *
28
+ * A config-driven spec is author-supplied and runs on every render, so a throw
29
+ * is contained: it degrades to "undeclared" (letting the caller fall back)
30
+ * rather than taking out the canvas or aborting a run mid-flight.
31
+ */
32
+ declare function resolvePortSpec<TConfig>(spec: PortSpec<TConfig> | undefined, config: TConfig): PortDescriptor[] | undefined;
33
+ /** Read the config bag off a node, tolerating the FlowNodeData union. */
34
+ declare function nodeConfig(node: Pick<FlowNode, "data">): Record<string, unknown>;
35
+ /**
36
+ * Resolve a node's effective ports.
37
+ *
38
+ * Precedence: explicit `data.inputs`/`data.outputs` (a per-node host override)
39
+ * beats the kind's declaration. `undefined` means "nothing declared" — the
40
+ * caller applies its own category default.
41
+ */
42
+ declare function resolveNodePorts(node: Pick<FlowNode, "data">, kind?: Pick<NodeKindDefinition<any>, "inputs" | "outputs">): {
43
+ inputs?: PortDescriptor[];
44
+ outputs?: PortDescriptor[];
45
+ };
46
+
47
+ /**
48
+ * Rich user input — the injection point.
49
+ *
50
+ * `rich_user_input` pauses a run on a fully authored page (long-form content,
51
+ * required reading + confirmation, multi-section forms) rather than a flat
52
+ * field list. That page IS a fancy-cms document — fancy-flow defines no
53
+ * document schema of its own and never re-implements one.
54
+ *
55
+ * The wiring ships in the box. Import the subpath once and the node lights up:
56
+ *
57
+ * ```ts
58
+ * import "@particle-academy/fancy-flow/rich-input";
59
+ * ```
60
+ *
61
+ * That module registers fancy-cms's `PageDoc` + `CmsPage` renderer + `Editor`
62
+ * against this seam. It lives on a separate entry so `@particle-academy/
63
+ * fancy-cms-ui` and `@particle-academy/react-fancy` stay OPTIONAL peers — most
64
+ * flows never use a rich input, and a workflow editor should not drag a CMS
65
+ * into every install.
66
+ *
67
+ * The seam stays public so a host can substitute a different document engine,
68
+ * but that is the escape hatch, not the expected path.
69
+ *
70
+ * Until something registers, the node still registers and still round-trips
71
+ * its config — it renders a "how to enable" body rather than an empty card.
72
+ */
73
+ type RichInputAdapter = {
74
+ /**
75
+ * react-fancy's `FauxClient` (or any component with the same shape) — a
76
+ * frame that mimics a browser window or device and scales its content down
77
+ * to fit. Used to preview the authored page inside the node card.
78
+ */
79
+ FauxClient?: ComponentType<any>;
80
+ /**
81
+ * Props for the frame — e.g. `{ variant: "browser", width: 1280, scale: "fit" }`
82
+ * so a full-width page renders at its real width and scales down into the
83
+ * node card instead of reflowing to a cramped layout that misrepresents it.
84
+ */
85
+ frameProps?: Record<string, unknown>;
86
+ /** Render the stored document read-only, for the in-node preview. */
87
+ renderDocument?: (doc: unknown) => ReactNode;
88
+ /** Editor mounted in the config panel via `renderDocumentField`. */
89
+ renderEditor?: (props: {
90
+ value: unknown;
91
+ onChange: (next: unknown) => void;
92
+ }) => ReactNode;
93
+ };
94
+ /** Install the host's document editor + preview frame. Returns an unregister fn. */
95
+ declare function registerRichInputAdapter(next: RichInputAdapter): () => void;
96
+ /** The registered adapter, or null when the host hasn't wired one. */
97
+ declare function getRichInputAdapter(): RichInputAdapter | null;
98
+ /** True once a host has wired an adapter — i.e. the node is usable. */
99
+ declare function isRichInputEnabled(): boolean;
100
+ /** Subscribe to adapter changes (so nodes re-render when it lands). */
101
+ declare function onRichInputAdapterChanged(fn: () => void): () => void;
102
+ /**
103
+ * RichInputPreview — the node card body. Shows the authored page inside a
104
+ * FauxClient frame so an author can see, at a glance on the canvas, what the
105
+ * person hitting this step will be looking at.
106
+ *
107
+ * Degrades in two steps rather than one: no adapter at all → install hint;
108
+ * adapter but nothing authored yet → an empty frame with the step title. A
109
+ * blank node body would read as "broken" in both cases.
110
+ */
111
+ declare function RichInputPreview({ config }: {
112
+ config: Record<string, unknown>;
113
+ }): react.JSX.Element;
6
114
 
7
115
  /**
8
116
  * registerNodeKind — install a node kind in the global registry. Returns
@@ -50,4 +158,4 @@ declare const BUILTIN_KINDS: NodeKindDefinition[];
50
158
  */
51
159
  declare function buildNodeTypes(): NodeTypes;
52
160
 
53
- export { BUILTIN_KINDS, NodeKindDefinition, RegistryNode, buildNodeTypes, categoryAccent, defaultConfigFor, getNodeKind, listNodeKinds, onNodeKindsChanged, registerBuiltinKinds, registerNodeKind, validateConfig };
161
+ export { BUILTIN_KINDS, NodeKindDefinition, PortSpec, RegistryNode, type RichInputAdapter, RichInputPreview, buildNodeTypes, categoryAccent, defaultConfigFor, getNodeKind, getRichInputAdapter, isRichInputEnabled, listNodeKinds, nodeConfig, onNodeKindsChanged, onRichInputAdapterChanged, registerBuiltinKinds, registerNodeKind, registerRichInputAdapter, resolveNodePorts, resolvePortSpec, validateConfig };
@@ -1,8 +1,116 @@
1
- import { a as NodeKindDefinition } from '../types-DnMe9Vsf.js';
2
- export { C as ConfigField, b as CredentialConfigField, E as ExpressionConfigField, J as JsonConfigField, N as NodeCategory, c as NumberConfigField, R as RenderBodyContext, S as SelectConfigField, d as SwitchConfigField, T as TextConfigField, e as TextareaConfigField } from '../types-DnMe9Vsf.js';
1
+ import { a as NodeKindDefinition, P as PortSpec } from '../types-BocBFh6l.js';
2
+ export { C as ConfigField, b as CredentialConfigField, D as DocumentConfigField, E as ExpressionConfigField, J as JsonConfigField, K as KeyValueConfigField, N as NodeCategory, c as NumberConfigField, R as RenderBodyContext, d as RepeaterConfigField, e as RepeaterRowField, S as SelectConfigField, f as SwitchConfigField, T as TextConfigField, g as TextareaConfigField } from '../types-BocBFh6l.js';
3
+ import { F as FlowNode, P as PortDescriptor } from '../types-BS3Gwnkq.js';
3
4
  import * as react from 'react';
5
+ import { ComponentType, ReactNode } from 'react';
4
6
  import { NodeProps, NodeTypes } from '@xyflow/react';
5
- import { F as FlowNode } from '../types-BS3Gwnkq.js';
7
+
8
+ /**
9
+ * Port resolution — the single place a node's ports are derived, shared by
10
+ * the canvas renderer and the headless runtime.
11
+ *
12
+ * This module is deliberately React-free: `runFlow` (the `/engine` entry)
13
+ * imports it, and the engine bundle must stay free of React.
14
+ *
15
+ * ## Why this is centralized
16
+ *
17
+ * Ports used to be read in two places that disagreed — the canvas consulted
18
+ * `data.outputs ?? kind.outputs`, while the runtime consulted `data.outputs`
19
+ * ONLY and fell back to a single `out` port. A kind that declared branch ports
20
+ * therefore drew correctly and then routed as if it had one output, unless the
21
+ * host remembered to mirror the ports onto every node's `data`. Both callers
22
+ * now go through `resolveNodePorts`, so declared ports and executed ports
23
+ * cannot drift apart.
24
+ */
25
+ /**
26
+ * Resolve a `PortSpec` against a config object.
27
+ *
28
+ * A config-driven spec is author-supplied and runs on every render, so a throw
29
+ * is contained: it degrades to "undeclared" (letting the caller fall back)
30
+ * rather than taking out the canvas or aborting a run mid-flight.
31
+ */
32
+ declare function resolvePortSpec<TConfig>(spec: PortSpec<TConfig> | undefined, config: TConfig): PortDescriptor[] | undefined;
33
+ /** Read the config bag off a node, tolerating the FlowNodeData union. */
34
+ declare function nodeConfig(node: Pick<FlowNode, "data">): Record<string, unknown>;
35
+ /**
36
+ * Resolve a node's effective ports.
37
+ *
38
+ * Precedence: explicit `data.inputs`/`data.outputs` (a per-node host override)
39
+ * beats the kind's declaration. `undefined` means "nothing declared" — the
40
+ * caller applies its own category default.
41
+ */
42
+ declare function resolveNodePorts(node: Pick<FlowNode, "data">, kind?: Pick<NodeKindDefinition<any>, "inputs" | "outputs">): {
43
+ inputs?: PortDescriptor[];
44
+ outputs?: PortDescriptor[];
45
+ };
46
+
47
+ /**
48
+ * Rich user input — the injection point.
49
+ *
50
+ * `rich_user_input` pauses a run on a fully authored page (long-form content,
51
+ * required reading + confirmation, multi-section forms) rather than a flat
52
+ * field list. That page IS a fancy-cms document — fancy-flow defines no
53
+ * document schema of its own and never re-implements one.
54
+ *
55
+ * The wiring ships in the box. Import the subpath once and the node lights up:
56
+ *
57
+ * ```ts
58
+ * import "@particle-academy/fancy-flow/rich-input";
59
+ * ```
60
+ *
61
+ * That module registers fancy-cms's `PageDoc` + `CmsPage` renderer + `Editor`
62
+ * against this seam. It lives on a separate entry so `@particle-academy/
63
+ * fancy-cms-ui` and `@particle-academy/react-fancy` stay OPTIONAL peers — most
64
+ * flows never use a rich input, and a workflow editor should not drag a CMS
65
+ * into every install.
66
+ *
67
+ * The seam stays public so a host can substitute a different document engine,
68
+ * but that is the escape hatch, not the expected path.
69
+ *
70
+ * Until something registers, the node still registers and still round-trips
71
+ * its config — it renders a "how to enable" body rather than an empty card.
72
+ */
73
+ type RichInputAdapter = {
74
+ /**
75
+ * react-fancy's `FauxClient` (or any component with the same shape) — a
76
+ * frame that mimics a browser window or device and scales its content down
77
+ * to fit. Used to preview the authored page inside the node card.
78
+ */
79
+ FauxClient?: ComponentType<any>;
80
+ /**
81
+ * Props for the frame — e.g. `{ variant: "browser", width: 1280, scale: "fit" }`
82
+ * so a full-width page renders at its real width and scales down into the
83
+ * node card instead of reflowing to a cramped layout that misrepresents it.
84
+ */
85
+ frameProps?: Record<string, unknown>;
86
+ /** Render the stored document read-only, for the in-node preview. */
87
+ renderDocument?: (doc: unknown) => ReactNode;
88
+ /** Editor mounted in the config panel via `renderDocumentField`. */
89
+ renderEditor?: (props: {
90
+ value: unknown;
91
+ onChange: (next: unknown) => void;
92
+ }) => ReactNode;
93
+ };
94
+ /** Install the host's document editor + preview frame. Returns an unregister fn. */
95
+ declare function registerRichInputAdapter(next: RichInputAdapter): () => void;
96
+ /** The registered adapter, or null when the host hasn't wired one. */
97
+ declare function getRichInputAdapter(): RichInputAdapter | null;
98
+ /** True once a host has wired an adapter — i.e. the node is usable. */
99
+ declare function isRichInputEnabled(): boolean;
100
+ /** Subscribe to adapter changes (so nodes re-render when it lands). */
101
+ declare function onRichInputAdapterChanged(fn: () => void): () => void;
102
+ /**
103
+ * RichInputPreview — the node card body. Shows the authored page inside a
104
+ * FauxClient frame so an author can see, at a glance on the canvas, what the
105
+ * person hitting this step will be looking at.
106
+ *
107
+ * Degrades in two steps rather than one: no adapter at all → install hint;
108
+ * adapter but nothing authored yet → an empty frame with the step title. A
109
+ * blank node body would read as "broken" in both cases.
110
+ */
111
+ declare function RichInputPreview({ config }: {
112
+ config: Record<string, unknown>;
113
+ }): react.JSX.Element;
6
114
 
7
115
  /**
8
116
  * registerNodeKind — install a node kind in the global registry. Returns
@@ -50,4 +158,4 @@ declare const BUILTIN_KINDS: NodeKindDefinition[];
50
158
  */
51
159
  declare function buildNodeTypes(): NodeTypes;
52
160
 
53
- export { BUILTIN_KINDS, NodeKindDefinition, RegistryNode, buildNodeTypes, categoryAccent, defaultConfigFor, getNodeKind, listNodeKinds, onNodeKindsChanged, registerBuiltinKinds, registerNodeKind, validateConfig };
161
+ export { BUILTIN_KINDS, NodeKindDefinition, PortSpec, RegistryNode, type RichInputAdapter, RichInputPreview, buildNodeTypes, categoryAccent, defaultConfigFor, getNodeKind, getRichInputAdapter, isRichInputEnabled, listNodeKinds, nodeConfig, onNodeKindsChanged, onRichInputAdapterChanged, registerBuiltinKinds, registerNodeKind, registerRichInputAdapter, resolveNodePorts, resolvePortSpec, validateConfig };