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