@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.
- package/README.md +104 -0
- package/dist/{chunk-BCXECQUC.js → chunk-6GGFEZH7.js} +3 -3
- package/dist/{chunk-BCXECQUC.js.map → chunk-6GGFEZH7.js.map} +1 -1
- package/dist/{chunk-WNVBXXOL.js → chunk-CPSOC27D.js} +43 -2
- package/dist/chunk-CPSOC27D.js.map +1 -0
- package/dist/chunk-F5RPRB7A.js +56 -0
- package/dist/chunk-F5RPRB7A.js.map +1 -0
- package/dist/{chunk-NVULCEDX.js → chunk-HNBO4HP3.js} +8 -4
- package/dist/chunk-HNBO4HP3.js.map +1 -0
- package/dist/{chunk-M2XKGQQL.js → chunk-NCPQDVUE.js} +178 -16
- package/dist/chunk-NCPQDVUE.js.map +1 -0
- package/dist/chunk-TITD5W4Y.js +26 -0
- package/dist/chunk-TITD5W4Y.js.map +1 -0
- package/dist/{chunk-QSSQRQN4.js → chunk-VEI743ZX.js} +3 -3
- package/dist/{chunk-QSSQRQN4.js.map → chunk-VEI743ZX.js.map} +1 -1
- package/dist/engine.cjs +32 -2
- package/dist/engine.cjs.map +1 -1
- package/dist/engine.js +3 -1
- package/dist/index.cjs +716 -40
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +45 -6
- package/dist/index.d.ts +45 -6
- package/dist/index.js +417 -23
- package/dist/index.js.map +1 -1
- package/dist/registry/index.d.cts +112 -4
- package/dist/registry/index.d.ts +112 -4
- package/dist/registry.cjs +312 -31
- package/dist/registry.cjs.map +1 -1
- package/dist/registry.js +4 -2
- package/dist/rich-input.cjs +56 -0
- package/dist/rich-input.cjs.map +1 -0
- package/dist/rich-input.d.cts +27 -0
- package/dist/rich-input.d.ts +27 -0
- package/dist/rich-input.js +37 -0
- package/dist/rich-input.js.map +1 -0
- package/dist/runtime.cjs +32 -2
- package/dist/runtime.cjs.map +1 -1
- package/dist/runtime.js +4 -2
- package/dist/schema.cjs +41 -0
- package/dist/schema.cjs.map +1 -1
- package/dist/schema.js +2 -2
- package/dist/styles.css +191 -0
- package/dist/styles.css.map +1 -1
- package/dist/types-BocBFh6l.d.ts +221 -0
- package/dist/types-DKqaUjF_.d.cts +221 -0
- package/dist/ux.cjs.map +1 -1
- package/dist/ux.d.cts +1 -1
- package/dist/ux.d.ts +1 -1
- package/dist/ux.js +1 -1
- package/package.json +24 -2
- package/dist/chunk-M2XKGQQL.js.map +0 -1
- package/dist/chunk-NVULCEDX.js.map +0 -1
- package/dist/chunk-WNVBXXOL.js.map +0 -1
- package/dist/types-C0wdN6QX.d.cts +0 -121
- package/dist/types-DnMe9Vsf.d.ts +0 -121
package/dist/registry.cjs
CHANGED
|
@@ -1,16 +1,88 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var ReactExports = require('react');
|
|
4
3
|
var jsxRuntime = require('react/jsx-runtime');
|
|
4
|
+
var ReactExports = require('react');
|
|
5
5
|
require('react-dom');
|
|
6
6
|
|
|
7
7
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
8
8
|
|
|
9
9
|
var ReactExports__default = /*#__PURE__*/_interopDefault(ReactExports);
|
|
10
10
|
|
|
11
|
+
// src/registry/ports.ts
|
|
12
|
+
function resolvePortSpec(spec, config) {
|
|
13
|
+
if (spec === void 0) return void 0;
|
|
14
|
+
if (typeof spec !== "function") return spec;
|
|
15
|
+
try {
|
|
16
|
+
const resolved = spec(config);
|
|
17
|
+
return Array.isArray(resolved) ? resolved : void 0;
|
|
18
|
+
} catch {
|
|
19
|
+
return void 0;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
function nodeConfig(node) {
|
|
23
|
+
return node.data?.config ?? {};
|
|
24
|
+
}
|
|
25
|
+
function resolveNodePorts(node, kind) {
|
|
26
|
+
const config = nodeConfig(node);
|
|
27
|
+
const data = node.data;
|
|
28
|
+
return {
|
|
29
|
+
inputs: data?.inputs ?? resolvePortSpec(kind?.inputs, config),
|
|
30
|
+
outputs: data?.outputs ?? resolvePortSpec(kind?.outputs, config)
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
var adapter = null;
|
|
34
|
+
var listeners = /* @__PURE__ */ new Set();
|
|
35
|
+
function registerRichInputAdapter(next) {
|
|
36
|
+
adapter = next;
|
|
37
|
+
for (const l of listeners) l();
|
|
38
|
+
return () => {
|
|
39
|
+
if (adapter === next) {
|
|
40
|
+
adapter = null;
|
|
41
|
+
for (const l of listeners) l();
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
function getRichInputAdapter() {
|
|
46
|
+
return adapter;
|
|
47
|
+
}
|
|
48
|
+
function isRichInputEnabled() {
|
|
49
|
+
return adapter !== null && (adapter.renderDocument !== void 0 || adapter.renderEditor !== void 0);
|
|
50
|
+
}
|
|
51
|
+
function onRichInputAdapterChanged(fn) {
|
|
52
|
+
listeners.add(fn);
|
|
53
|
+
return () => listeners.delete(fn);
|
|
54
|
+
}
|
|
55
|
+
function RichInputPreview({ config }) {
|
|
56
|
+
const a = getRichInputAdapter();
|
|
57
|
+
const title = typeof config.title === "string" && config.title.trim() !== "" ? config.title : "Untitled step";
|
|
58
|
+
const doc = config.document;
|
|
59
|
+
if (!a || !a.renderDocument && !a.renderEditor) {
|
|
60
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ff-rich-preview", children: [
|
|
61
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "ff-rich-preview__title", children: title }),
|
|
62
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ff-rich-preview__unavailable", children: [
|
|
63
|
+
"Add ",
|
|
64
|
+
/* @__PURE__ */ jsxRuntime.jsx("code", { children: "@particle-academy/fancy-cms-ui" }),
|
|
65
|
+
" +",
|
|
66
|
+
" ",
|
|
67
|
+
/* @__PURE__ */ jsxRuntime.jsx("code", { children: "@particle-academy/react-fancy" }),
|
|
68
|
+
", then",
|
|
69
|
+
" ",
|
|
70
|
+
/* @__PURE__ */ jsxRuntime.jsx("code", { children: 'import "@particle-academy/fancy-flow/rich-input"' }),
|
|
71
|
+
"."
|
|
72
|
+
] })
|
|
73
|
+
] });
|
|
74
|
+
}
|
|
75
|
+
const body = doc === void 0 || doc === null ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "ff-rich-preview__unavailable", children: "Nothing authored yet." }) : a.renderDocument?.(doc) ?? null;
|
|
76
|
+
const Frame = a.FauxClient;
|
|
77
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ff-rich-preview", children: [
|
|
78
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "ff-rich-preview__title", children: title }),
|
|
79
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "ff-rich-preview__frame", children: Frame ? /* @__PURE__ */ jsxRuntime.jsx(Frame, { ...a.frameProps ?? { variant: "browser" }, children: body }) : body })
|
|
80
|
+
] });
|
|
81
|
+
}
|
|
82
|
+
|
|
11
83
|
// src/registry/registry.ts
|
|
12
84
|
var kinds = /* @__PURE__ */ new Map();
|
|
13
|
-
var
|
|
85
|
+
var listeners2 = /* @__PURE__ */ new Set();
|
|
14
86
|
function registerNodeKind(definition) {
|
|
15
87
|
kinds.set(definition.name, definition);
|
|
16
88
|
notify();
|
|
@@ -29,11 +101,11 @@ function listNodeKinds(category) {
|
|
|
29
101
|
return category ? all.filter((k) => k.category === category) : all;
|
|
30
102
|
}
|
|
31
103
|
function onNodeKindsChanged(listener) {
|
|
32
|
-
|
|
33
|
-
return () =>
|
|
104
|
+
listeners2.add(listener);
|
|
105
|
+
return () => listeners2.delete(listener);
|
|
34
106
|
}
|
|
35
107
|
function notify() {
|
|
36
|
-
for (const l of
|
|
108
|
+
for (const l of listeners2) l();
|
|
37
109
|
}
|
|
38
110
|
function defaultConfigFor(kind) {
|
|
39
111
|
const fromKind = kind.defaultConfig ? { ...kind.defaultConfig } : {};
|
|
@@ -81,6 +153,47 @@ function validateField(field, value) {
|
|
|
81
153
|
case "json":
|
|
82
154
|
return null;
|
|
83
155
|
// permissive — just JSON-shaped
|
|
156
|
+
case "repeater": {
|
|
157
|
+
if (!Array.isArray(value)) return `${field.label} must be a list`;
|
|
158
|
+
if (field.minItems !== void 0 && value.length < field.minItems) {
|
|
159
|
+
return `${field.label} needs at least ${field.minItems}`;
|
|
160
|
+
}
|
|
161
|
+
if (field.maxItems !== void 0 && value.length > field.maxItems) {
|
|
162
|
+
return `${field.label} allows at most ${field.maxItems}`;
|
|
163
|
+
}
|
|
164
|
+
for (let i = 0; i < value.length; i++) {
|
|
165
|
+
const row = value[i];
|
|
166
|
+
if (!row || typeof row !== "object" || Array.isArray(row)) {
|
|
167
|
+
return `${field.label} item ${i + 1} must be an object`;
|
|
168
|
+
}
|
|
169
|
+
for (const sub of field.fields) {
|
|
170
|
+
const cell = row[sub.key];
|
|
171
|
+
if (sub.required && (cell === void 0 || cell === null || cell === "")) {
|
|
172
|
+
return `${field.label} item ${i + 1}: ${sub.label} is required`;
|
|
173
|
+
}
|
|
174
|
+
if (cell === void 0 || cell === null) continue;
|
|
175
|
+
const issue = validateField(sub, cell);
|
|
176
|
+
if (issue) return `${field.label} item ${i + 1}: ${issue}`;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
return null;
|
|
180
|
+
}
|
|
181
|
+
case "keyvalue": {
|
|
182
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
183
|
+
return `${field.label} must be a key/value map`;
|
|
184
|
+
}
|
|
185
|
+
const allowed = field.valueOptions?.map((o) => o.value);
|
|
186
|
+
for (const [k, v] of Object.entries(value)) {
|
|
187
|
+
if (typeof v !== "string") return `${field.label}: "${k}" must be a string`;
|
|
188
|
+
if (allowed && !allowed.includes(v)) {
|
|
189
|
+
return `${field.label}: "${k}" must be one of ${allowed.join(", ")}`;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
return null;
|
|
193
|
+
}
|
|
194
|
+
case "document":
|
|
195
|
+
return null;
|
|
196
|
+
// opaque to fancy-flow — the host's editor owns its shape
|
|
84
197
|
default:
|
|
85
198
|
return null;
|
|
86
199
|
}
|
|
@@ -1095,7 +1208,7 @@ function defaultTouchable() {
|
|
|
1095
1208
|
return navigator.maxTouchPoints || "ontouchstart" in this;
|
|
1096
1209
|
}
|
|
1097
1210
|
function drag_default() {
|
|
1098
|
-
var filter2 = defaultFilter, container = defaultContainer, subject = defaultSubject, touchable = defaultTouchable, gestures = {},
|
|
1211
|
+
var filter2 = defaultFilter, container = defaultContainer, subject = defaultSubject, touchable = defaultTouchable, gestures = {}, listeners3 = dispatch_default("start", "drag", "end"), active = 0, mousedownx, mousedowny, mousemoving, touchending, clickDistance2 = 0;
|
|
1099
1212
|
function drag(selection2) {
|
|
1100
1213
|
selection2.on("mousedown.drag", mousedowned).filter(touchable).on("touchstart.drag", touchstarted).on("touchmove.drag", touchmoved, nonpassive).on("touchend.drag touchcancel.drag", touchended).style("touch-action", "none").style("-webkit-tap-highlight-color", "rgba(0,0,0,0)");
|
|
1101
1214
|
}
|
|
@@ -1158,7 +1271,7 @@ function drag_default() {
|
|
|
1158
1271
|
}
|
|
1159
1272
|
}
|
|
1160
1273
|
function beforestart(that, container2, event, d, identifier, touch) {
|
|
1161
|
-
var dispatch2 =
|
|
1274
|
+
var dispatch2 = listeners3.copy(), p = pointer_default(touch || event, container2), dx, dy, s;
|
|
1162
1275
|
if ((s = subject.call(that, new DragEvent("beforestart", {
|
|
1163
1276
|
sourceEvent: event,
|
|
1164
1277
|
target: drag,
|
|
@@ -1217,8 +1330,8 @@ function drag_default() {
|
|
|
1217
1330
|
return arguments.length ? (touchable = typeof _ === "function" ? _ : constant_default2(!!_), drag) : touchable;
|
|
1218
1331
|
};
|
|
1219
1332
|
drag.on = function() {
|
|
1220
|
-
var value =
|
|
1221
|
-
return value ===
|
|
1333
|
+
var value = listeners3.on.apply(listeners3, arguments);
|
|
1334
|
+
return value === listeners3 ? drag : value;
|
|
1222
1335
|
};
|
|
1223
1336
|
drag.clickDistance = function(_) {
|
|
1224
1337
|
return arguments.length ? (clickDistance2 = (_ = +_) * _, drag) : Math.sqrt(clickDistance2);
|
|
@@ -2772,7 +2885,7 @@ function defaultConstrain(transform2, extent, translateExtent) {
|
|
|
2772
2885
|
);
|
|
2773
2886
|
}
|
|
2774
2887
|
function zoom_default2() {
|
|
2775
|
-
var filter2 = defaultFilter2, extent = defaultExtent, constrain = defaultConstrain, wheelDelta2 = defaultWheelDelta, touchable = defaultTouchable2, scaleExtent = [0, Infinity], translateExtent = [[-Infinity, -Infinity], [Infinity, Infinity]], duration = 250, interpolate = zoom_default,
|
|
2888
|
+
var filter2 = defaultFilter2, extent = defaultExtent, constrain = defaultConstrain, wheelDelta2 = defaultWheelDelta, touchable = defaultTouchable2, scaleExtent = [0, Infinity], translateExtent = [[-Infinity, -Infinity], [Infinity, Infinity]], duration = 250, interpolate = zoom_default, listeners3 = dispatch_default("start", "zoom", "end"), touchstarting, touchfirst, touchending, touchDelay = 500, wheelDelay = 150, clickDistance2 = 0, tapDistance = 10;
|
|
2776
2889
|
function zoom(selection2) {
|
|
2777
2890
|
selection2.property("__zoom", defaultTransform).on("wheel.zoom", wheeled, { passive: false }).on("mousedown.zoom", mousedowned).on("dblclick.zoom", dblclicked).filter(touchable).on("touchstart.zoom", touchstarted).on("touchmove.zoom", touchmoved).on("touchend.zoom touchcancel.zoom", touchended).style("-webkit-tap-highlight-color", "rgba(0,0,0,0)");
|
|
2778
2891
|
}
|
|
@@ -2884,14 +2997,14 @@ function zoom_default2() {
|
|
|
2884
2997
|
},
|
|
2885
2998
|
emit: function(type) {
|
|
2886
2999
|
var d = select_default2(this.that).datum();
|
|
2887
|
-
|
|
3000
|
+
listeners3.call(
|
|
2888
3001
|
type,
|
|
2889
3002
|
this.that,
|
|
2890
3003
|
new ZoomEvent(type, {
|
|
2891
3004
|
sourceEvent: this.sourceEvent,
|
|
2892
3005
|
target: zoom,
|
|
2893
3006
|
transform: this.that.__zoom,
|
|
2894
|
-
dispatch:
|
|
3007
|
+
dispatch: listeners3
|
|
2895
3008
|
}),
|
|
2896
3009
|
d
|
|
2897
3010
|
);
|
|
@@ -3041,8 +3154,8 @@ function zoom_default2() {
|
|
|
3041
3154
|
return arguments.length ? (interpolate = _, zoom) : interpolate;
|
|
3042
3155
|
};
|
|
3043
3156
|
zoom.on = function() {
|
|
3044
|
-
var value =
|
|
3045
|
-
return value ===
|
|
3157
|
+
var value = listeners3.on.apply(listeners3, arguments);
|
|
3158
|
+
return value === listeners3 ? zoom : value;
|
|
3046
3159
|
};
|
|
3047
3160
|
zoom.clickDistance = function(_) {
|
|
3048
3161
|
return arguments.length ? (clickDistance2 = (_ = +_) * _, zoom) : Math.sqrt(clickDistance2);
|
|
@@ -5715,20 +5828,20 @@ var use_sync_external_store_with_selector_default = { useSyncExternalStoreWithSe
|
|
|
5715
5828
|
// node_modules/zustand/esm/vanilla.mjs
|
|
5716
5829
|
var createStoreImpl = (createState) => {
|
|
5717
5830
|
let state;
|
|
5718
|
-
const
|
|
5831
|
+
const listeners3 = /* @__PURE__ */ new Set();
|
|
5719
5832
|
const setState = (partial, replace) => {
|
|
5720
5833
|
const nextState = typeof partial === "function" ? partial(state) : partial;
|
|
5721
5834
|
if (!Object.is(nextState, state)) {
|
|
5722
5835
|
const previousState = state;
|
|
5723
5836
|
state = (replace != null ? replace : typeof nextState !== "object" || nextState === null) ? nextState : Object.assign({}, state, nextState);
|
|
5724
|
-
|
|
5837
|
+
listeners3.forEach((listener) => listener(state, previousState));
|
|
5725
5838
|
}
|
|
5726
5839
|
};
|
|
5727
5840
|
const getState = () => state;
|
|
5728
5841
|
const getInitialState2 = () => initialState;
|
|
5729
5842
|
const subscribe = (listener) => {
|
|
5730
|
-
|
|
5731
|
-
return () =>
|
|
5843
|
+
listeners3.add(listener);
|
|
5844
|
+
return () => listeners3.delete(listener);
|
|
5732
5845
|
};
|
|
5733
5846
|
const destroy = () => {
|
|
5734
5847
|
if ((undefined ? undefined.MODE : void 0) !== "production") {
|
|
@@ -5736,7 +5849,7 @@ var createStoreImpl = (createState) => {
|
|
|
5736
5849
|
"[DEPRECATED] The `destroy` method will be unsupported in a future version. Instead use unsubscribe function returned by subscribe. Everything will be garbage-collected if store is garbage-collected."
|
|
5737
5850
|
);
|
|
5738
5851
|
}
|
|
5739
|
-
|
|
5852
|
+
listeners3.clear();
|
|
5740
5853
|
};
|
|
5741
5854
|
const api = { setState, getState, getInitialState: getInitialState2, subscribe, destroy };
|
|
5742
5855
|
const initialState = state = createState(setState, getState, api);
|
|
@@ -9024,9 +9137,10 @@ function RegistryNodeInner(props) {
|
|
|
9024
9137
|
const data = props.data;
|
|
9025
9138
|
const status = data.status ?? "idle";
|
|
9026
9139
|
const accent = kind.accent ?? categoryAccent(kind.category);
|
|
9027
|
-
const
|
|
9028
|
-
const
|
|
9029
|
-
const
|
|
9140
|
+
const resolved = resolveNodePorts(props, kind);
|
|
9141
|
+
const inputs = resolved.inputs ?? defaultInputs(kind.category);
|
|
9142
|
+
const outputs = resolved.outputs ?? defaultOutputs(kind.category);
|
|
9143
|
+
const config = nodeConfig(props);
|
|
9030
9144
|
const label = data.label ?? kind.label;
|
|
9031
9145
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
9032
9146
|
"div",
|
|
@@ -9117,8 +9231,39 @@ function previewValue(v) {
|
|
|
9117
9231
|
return "[object]";
|
|
9118
9232
|
}
|
|
9119
9233
|
}
|
|
9120
|
-
|
|
9121
|
-
|
|
9234
|
+
function casePorts(cases) {
|
|
9235
|
+
const byPort = /* @__PURE__ */ new Map();
|
|
9236
|
+
if (cases && typeof cases === "object" && !Array.isArray(cases)) {
|
|
9237
|
+
for (const [match, port] of Object.entries(cases)) {
|
|
9238
|
+
if (typeof port !== "string" || port === "" || port === "default") continue;
|
|
9239
|
+
const matches = byPort.get(port) ?? [];
|
|
9240
|
+
matches.push(match);
|
|
9241
|
+
byPort.set(port, matches);
|
|
9242
|
+
}
|
|
9243
|
+
}
|
|
9244
|
+
const ports = [...byPort].map(([id2, matches]) => ({
|
|
9245
|
+
id: id2,
|
|
9246
|
+
label: matches.join("|")
|
|
9247
|
+
}));
|
|
9248
|
+
return [...ports, { id: "default", label: "default" }];
|
|
9249
|
+
}
|
|
9250
|
+
function routePorts(routes, fallback) {
|
|
9251
|
+
const ports = [];
|
|
9252
|
+
const seen = /* @__PURE__ */ new Set();
|
|
9253
|
+
if (Array.isArray(routes)) {
|
|
9254
|
+
for (const route of routes) {
|
|
9255
|
+
const id2 = route?.port;
|
|
9256
|
+
if (typeof id2 !== "string" || id2.trim() === "" || seen.has(id2)) continue;
|
|
9257
|
+
seen.add(id2);
|
|
9258
|
+
ports.push({ id: id2, label: id2 });
|
|
9259
|
+
}
|
|
9260
|
+
}
|
|
9261
|
+
if (fallback !== false && !seen.has("fallback")) {
|
|
9262
|
+
ports.push({ id: "fallback", label: "fallback" });
|
|
9263
|
+
}
|
|
9264
|
+
if (ports.length === 0) ports.push({ id: "out" });
|
|
9265
|
+
return ports;
|
|
9266
|
+
}
|
|
9122
9267
|
var HTTP_METHODS = [
|
|
9123
9268
|
{ type: "select", key: "method", label: "Method", options: [
|
|
9124
9269
|
{ value: "GET", label: "GET" },
|
|
@@ -9187,15 +9332,59 @@ var KINDS = [
|
|
|
9187
9332
|
configSchema: [
|
|
9188
9333
|
{ type: "text", key: "title", label: "Form title", default: "Need your input" },
|
|
9189
9334
|
{
|
|
9190
|
-
type: "
|
|
9335
|
+
type: "repeater",
|
|
9191
9336
|
key: "fields",
|
|
9192
|
-
label: "Fields
|
|
9193
|
-
|
|
9194
|
-
|
|
9195
|
-
|
|
9337
|
+
label: "Fields",
|
|
9338
|
+
description: "The form the run pauses on.",
|
|
9339
|
+
titleKey: "label",
|
|
9340
|
+
addLabel: "Add field",
|
|
9341
|
+
minItems: 1,
|
|
9342
|
+
fields: [
|
|
9343
|
+
{ type: "text", key: "key", label: "Key", required: true, placeholder: "answer" },
|
|
9344
|
+
{ type: "text", key: "label", label: "Label", required: true, placeholder: "Your answer" },
|
|
9345
|
+
{
|
|
9346
|
+
type: "select",
|
|
9347
|
+
key: "type",
|
|
9348
|
+
label: "Type",
|
|
9349
|
+
default: "text",
|
|
9350
|
+
options: [
|
|
9351
|
+
{ value: "text", label: "Text" },
|
|
9352
|
+
{ value: "textarea", label: "Long text" },
|
|
9353
|
+
{ value: "number", label: "Number" },
|
|
9354
|
+
{ value: "select", label: "Select" },
|
|
9355
|
+
{ value: "switch", label: "Switch" }
|
|
9356
|
+
]
|
|
9357
|
+
},
|
|
9358
|
+
{ type: "switch", key: "required", label: "Required", default: false }
|
|
9359
|
+
],
|
|
9360
|
+
default: [{ key: "answer", label: "Your answer", type: "textarea", required: true }]
|
|
9196
9361
|
}
|
|
9197
9362
|
]
|
|
9198
9363
|
},
|
|
9364
|
+
{
|
|
9365
|
+
name: "rich_user_input",
|
|
9366
|
+
category: "human",
|
|
9367
|
+
label: "Rich User Input",
|
|
9368
|
+
description: "Pause the flow on a fully authored page \u2014 content, required reading, multi-section forms.",
|
|
9369
|
+
icon: "\u25A4",
|
|
9370
|
+
inputs: [{ id: "in" }],
|
|
9371
|
+
outputs: [{ id: "out", label: "values" }],
|
|
9372
|
+
configSchema: [
|
|
9373
|
+
{ type: "text", key: "title", label: "Step title", default: "Please review" },
|
|
9374
|
+
{
|
|
9375
|
+
type: "document",
|
|
9376
|
+
key: "document",
|
|
9377
|
+
label: "Page content",
|
|
9378
|
+
documentType: "stages",
|
|
9379
|
+
description: "Authored with the host's document editor (fancy-cms Stages)."
|
|
9380
|
+
},
|
|
9381
|
+
{ type: "switch", key: "requireConfirm", label: "Require explicit confirmation", default: true },
|
|
9382
|
+
{ type: "text", key: "submitLabel", label: "Submit button", default: "Continue" }
|
|
9383
|
+
],
|
|
9384
|
+
// Preview the authored page inside a FauxClient frame, so the canvas shows
|
|
9385
|
+
// what the person hitting this step will actually see.
|
|
9386
|
+
renderBody: (ctx) => ReactExports.createElement(RichInputPreview, { config: ctx.config ?? {} })
|
|
9387
|
+
},
|
|
9199
9388
|
// ───────────── Logic ─────────────
|
|
9200
9389
|
{
|
|
9201
9390
|
name: "branch",
|
|
@@ -9216,10 +9405,24 @@ var KINDS = [
|
|
|
9216
9405
|
description: "Route to one of N labelled outputs based on a key.",
|
|
9217
9406
|
icon: "\u2933",
|
|
9218
9407
|
inputs: [{ id: "in" }],
|
|
9219
|
-
|
|
9408
|
+
// Ports ARE the config: every distinct port a case routes to becomes an
|
|
9409
|
+
// output handle, plus the always-present `default`. Editing the cases map
|
|
9410
|
+
// moves the ports on the canvas and the ports the runtime activates.
|
|
9411
|
+
outputs: (config) => casePorts(config?.cases),
|
|
9220
9412
|
configSchema: [
|
|
9221
9413
|
{ type: "expression", key: "value", label: "Switch on", example: "{{ $json.kind }}", required: true },
|
|
9222
|
-
{
|
|
9414
|
+
{
|
|
9415
|
+
type: "keyvalue",
|
|
9416
|
+
key: "cases",
|
|
9417
|
+
label: "Cases",
|
|
9418
|
+
description: "Match value \u2192 output port. Unmatched input takes `default`.",
|
|
9419
|
+
keyLabel: "When value is",
|
|
9420
|
+
valueLabel: "Route to port",
|
|
9421
|
+
keyPlaceholder: "a",
|
|
9422
|
+
valuePlaceholder: "case_a",
|
|
9423
|
+
addLabel: "Add case",
|
|
9424
|
+
default: { a: "case_a", b: "case_b" }
|
|
9425
|
+
}
|
|
9223
9426
|
]
|
|
9224
9427
|
},
|
|
9225
9428
|
{
|
|
@@ -9374,6 +9577,76 @@ var KINDS = [
|
|
|
9374
9577
|
{ type: "credential", key: "credential", label: "API credential", credentialType: "llm_credential" }
|
|
9375
9578
|
]
|
|
9376
9579
|
},
|
|
9580
|
+
{
|
|
9581
|
+
name: "llm_branch",
|
|
9582
|
+
category: "ai",
|
|
9583
|
+
label: "LLM Router",
|
|
9584
|
+
description: "Let a model choose which route the flow takes.",
|
|
9585
|
+
icon: "\u2727",
|
|
9586
|
+
inputs: [{ id: "in" }],
|
|
9587
|
+
// Each declared route is a port. The executor returns `{ __port: id }`
|
|
9588
|
+
// (or `Port.only(id)` on the PHP runtime) to pick one.
|
|
9589
|
+
outputs: (config) => routePorts(config?.routes, config?.fallback),
|
|
9590
|
+
configSchema: [
|
|
9591
|
+
{
|
|
9592
|
+
type: "textarea",
|
|
9593
|
+
key: "system",
|
|
9594
|
+
label: "System prompt",
|
|
9595
|
+
rows: 3,
|
|
9596
|
+
description: "Optional framing for the routing decision."
|
|
9597
|
+
},
|
|
9598
|
+
{
|
|
9599
|
+
type: "expression",
|
|
9600
|
+
key: "prompt",
|
|
9601
|
+
label: "What to route on",
|
|
9602
|
+
example: "{{ $json.message }}",
|
|
9603
|
+
required: true
|
|
9604
|
+
},
|
|
9605
|
+
{
|
|
9606
|
+
type: "repeater",
|
|
9607
|
+
key: "routes",
|
|
9608
|
+
label: "Routes",
|
|
9609
|
+
description: "The model picks exactly one. Descriptions are what it chooses between \u2014 make them distinct.",
|
|
9610
|
+
titleKey: "port",
|
|
9611
|
+
addLabel: "Add route",
|
|
9612
|
+
minItems: 2,
|
|
9613
|
+
fields: [
|
|
9614
|
+
{ type: "text", key: "port", label: "Port", required: true, placeholder: "billing" },
|
|
9615
|
+
{
|
|
9616
|
+
type: "text",
|
|
9617
|
+
key: "description",
|
|
9618
|
+
label: "When to choose it",
|
|
9619
|
+
required: true,
|
|
9620
|
+
placeholder: "The user is asking about an invoice, refund, or payment."
|
|
9621
|
+
}
|
|
9622
|
+
],
|
|
9623
|
+
default: [
|
|
9624
|
+
{ port: "a", description: "Describe when the model should pick this route." },
|
|
9625
|
+
{ port: "b", description: "Describe when the model should pick this route." }
|
|
9626
|
+
]
|
|
9627
|
+
},
|
|
9628
|
+
{
|
|
9629
|
+
type: "select",
|
|
9630
|
+
key: "provider",
|
|
9631
|
+
label: "Provider",
|
|
9632
|
+
default: "anthropic",
|
|
9633
|
+
options: [
|
|
9634
|
+
{ value: "anthropic", label: "Anthropic" },
|
|
9635
|
+
{ value: "openai", label: "OpenAI" },
|
|
9636
|
+
{ value: "custom", label: "Custom" }
|
|
9637
|
+
]
|
|
9638
|
+
},
|
|
9639
|
+
{ type: "text", key: "model", label: "Model", placeholder: "claude-sonnet-4-5" },
|
|
9640
|
+
{
|
|
9641
|
+
type: "switch",
|
|
9642
|
+
key: "fallback",
|
|
9643
|
+
label: "Add a `fallback` port",
|
|
9644
|
+
default: true,
|
|
9645
|
+
description: "Where the flow goes if the model returns no usable route."
|
|
9646
|
+
},
|
|
9647
|
+
{ type: "credential", key: "credential", label: "API credential", credentialType: "llm_credential" }
|
|
9648
|
+
]
|
|
9649
|
+
},
|
|
9377
9650
|
{
|
|
9378
9651
|
name: "tool_use",
|
|
9379
9652
|
category: "ai",
|
|
@@ -9506,14 +9779,22 @@ function buildNodeTypes() {
|
|
|
9506
9779
|
|
|
9507
9780
|
exports.BUILTIN_KINDS = BUILTIN_KINDS;
|
|
9508
9781
|
exports.RegistryNode = RegistryNode;
|
|
9782
|
+
exports.RichInputPreview = RichInputPreview;
|
|
9509
9783
|
exports.buildNodeTypes = buildNodeTypes;
|
|
9510
9784
|
exports.categoryAccent = categoryAccent;
|
|
9511
9785
|
exports.defaultConfigFor = defaultConfigFor;
|
|
9512
9786
|
exports.getNodeKind = getNodeKind;
|
|
9787
|
+
exports.getRichInputAdapter = getRichInputAdapter;
|
|
9788
|
+
exports.isRichInputEnabled = isRichInputEnabled;
|
|
9513
9789
|
exports.listNodeKinds = listNodeKinds;
|
|
9790
|
+
exports.nodeConfig = nodeConfig;
|
|
9514
9791
|
exports.onNodeKindsChanged = onNodeKindsChanged;
|
|
9792
|
+
exports.onRichInputAdapterChanged = onRichInputAdapterChanged;
|
|
9515
9793
|
exports.registerBuiltinKinds = registerBuiltinKinds;
|
|
9516
9794
|
exports.registerNodeKind = registerNodeKind;
|
|
9795
|
+
exports.registerRichInputAdapter = registerRichInputAdapter;
|
|
9796
|
+
exports.resolveNodePorts = resolveNodePorts;
|
|
9797
|
+
exports.resolvePortSpec = resolvePortSpec;
|
|
9517
9798
|
exports.validateConfig = validateConfig;
|
|
9518
9799
|
//# sourceMappingURL=registry.cjs.map
|
|
9519
9800
|
//# sourceMappingURL=registry.cjs.map
|