@particle-academy/fancy-flow 0.40.4 → 0.42.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-BHQDZer6.d.ts +92 -0
- package/dist/ConfigFieldRenderer-LotNHeG8.d.cts +92 -0
- package/dist/{chunk-UX65ML3J.js → chunk-55KQS3LK.js} +25 -2
- package/dist/chunk-55KQS3LK.js.map +1 -0
- package/dist/{chunk-WOO4B7M5.js → chunk-T3VQX3XB.js} +3 -3
- package/dist/{chunk-WOO4B7M5.js.map → chunk-T3VQX3XB.js.map} +1 -1
- 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 +343 -33
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +105 -127
- package/dist/index.d.ts +105 -127
- package/dist/index.js +320 -38
- 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-C9KMVpn7.d.cts} +1 -1
- package/dist/{registry-DYbFLEcW.d.ts → registry-FGCBprcn.d.ts} +1 -1
- package/dist/registry.cjs +23 -0
- package/dist/registry.cjs.map +1 -1
- package/dist/registry.js +1 -1
- package/dist/screens.js +3 -3
- package/dist/styles.css +99 -0
- package/dist/styles.css.map +1 -1
- package/dist/{types-D8Dezh9v.d.cts → types-B78Rm69S.d.cts} +94 -2
- package/dist/{types-D6lsIGoe.d.ts → types-JViq8reb.d.ts} +94 -2
- package/dist/ux.d.cts +1 -1
- package/dist/ux.d.ts +1 -1
- package/package.json +12 -2
- package/dist/chunk-UX65ML3J.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { removeNodes, removeEdges, cloneSubgraph, alignNodes, distributeNodes, reconnectEdge, assignToLane, removeFromLane, duplicateNode, setEdgeLabel, FlowCanvas } from './chunk-
|
|
2
|
-
export { ActionNode, DecisionNode, FlowCanvas, FlowViewer, NodeShell, OutputNode, SubgraphNode, TriggerNode, alignNodes, cloneSubgraph, defaultNodeTypes, distributeNodes, reconnectEdge } from './chunk-
|
|
1
|
+
import { removeNodes, removeEdges, cloneSubgraph, alignNodes, distributeNodes, reconnectEdge, assignToLane, removeFromLane, duplicateNode, setEdgeLabel, FlowCanvas } from './chunk-T3VQX3XB.js';
|
|
2
|
+
export { ActionNode, DecisionNode, FlowCanvas, FlowViewer, NodeShell, OutputNode, SubgraphNode, TriggerNode, alignNodes, cloneSubgraph, defaultNodeTypes, distributeNodes, reconnectEdge } from './chunk-T3VQX3XB.js';
|
|
3
3
|
import { useFlowState, useFlowRun, useFlowHistory, applyOutputsToNodes, applyStatusesToNodes } from './chunk-A6RFLGWV.js';
|
|
4
4
|
export { applyOutputsToNodes, applyStatusesToNodes, createHistory, useFlowHistory, useFlowRun, useFlowState } from './chunk-A6RFLGWV.js';
|
|
5
5
|
export { runCohort } from './chunk-QZTGV3ZL.js';
|
|
6
|
-
import { buildNodeTypes, FlowEditorProvider, registerBuiltinKinds } from './chunk-
|
|
7
|
-
export { ANY_PORT_TYPE, BUILTIN_KINDS, LaneNode, NoteNode, RegistryNode, buildNodeTypes, createConnectionValidator, defaultPortCompatibility, registerBuiltinKinds, useFlowEditor, useFlowEditorOptional } from './chunk-
|
|
6
|
+
import { buildNodeTypes, FlowEditorProvider, registerBuiltinKinds } from './chunk-55KQS3LK.js';
|
|
7
|
+
export { ANY_PORT_TYPE, BUILTIN_KINDS, LaneNode, NoteNode, RegistryNode, buildNodeTypes, createConnectionValidator, defaultPortCompatibility, registerBuiltinKinds, useFlowEditor, useFlowEditorOptional } from './chunk-55KQS3LK.js';
|
|
8
8
|
import { ReactFlowProvider, useReactFlow, addEdge, applyEdgeChanges, applyNodeChanges, Position, Handle } from './chunk-OWENS2H5.js';
|
|
9
9
|
export { LEGACY_PAUSE_PREFIXES, PAUSE_PREFIX, decodePause, encodePause, isPause, pauseForHuman } from './chunk-UEOE6B52.js';
|
|
10
10
|
import './chunk-USL4FMFU.js';
|
|
@@ -109,6 +109,230 @@ function paletteDropHandlers(onDrop) {
|
|
|
109
109
|
}
|
|
110
110
|
};
|
|
111
111
|
}
|
|
112
|
+
|
|
113
|
+
// src/expressions/variables.ts
|
|
114
|
+
var ROOT = "$json";
|
|
115
|
+
function expr(path) {
|
|
116
|
+
return `{{ ${path} }}`;
|
|
117
|
+
}
|
|
118
|
+
function outputFieldsFor(node) {
|
|
119
|
+
const kindName = node.data?.kind ?? node.type;
|
|
120
|
+
if (!kindName) return [];
|
|
121
|
+
const kind = getNodeKind(kindName);
|
|
122
|
+
const shape = kind?.outputShape;
|
|
123
|
+
if (!shape) return [];
|
|
124
|
+
if (typeof shape === "function") {
|
|
125
|
+
try {
|
|
126
|
+
const config = node.data?.config ?? {};
|
|
127
|
+
return shape(config) ?? [];
|
|
128
|
+
} catch {
|
|
129
|
+
return [];
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return shape;
|
|
133
|
+
}
|
|
134
|
+
function baseVariables() {
|
|
135
|
+
return [
|
|
136
|
+
{ expression: expr(ROOT), path: ROOT, type: "unknown", description: "The whole incoming value." }
|
|
137
|
+
];
|
|
138
|
+
}
|
|
139
|
+
function availableVariables(graph, nodeId) {
|
|
140
|
+
const out = baseVariables();
|
|
141
|
+
const seen = /* @__PURE__ */ new Set([ROOT]);
|
|
142
|
+
const byId = new Map(graph.nodes.map((n) => [n.id, n]));
|
|
143
|
+
const upstreamIds = graph.edges.filter((e) => e.target === nodeId).map((e) => e.source);
|
|
144
|
+
for (const id of upstreamIds) {
|
|
145
|
+
if (id === nodeId) continue;
|
|
146
|
+
const upstream = byId.get(id);
|
|
147
|
+
if (!upstream) continue;
|
|
148
|
+
const kindName = upstream.data?.kind ?? upstream.type;
|
|
149
|
+
const label = upstream.data?.label ?? (kindName ? getNodeKind(kindName)?.label : void 0);
|
|
150
|
+
for (const field of outputFieldsFor(upstream)) {
|
|
151
|
+
const path = `${ROOT}.${field.path}`;
|
|
152
|
+
if (seen.has(path)) continue;
|
|
153
|
+
seen.add(path);
|
|
154
|
+
out.push({
|
|
155
|
+
expression: expr(path),
|
|
156
|
+
path,
|
|
157
|
+
type: field.type,
|
|
158
|
+
description: field.description,
|
|
159
|
+
source: label
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
return out;
|
|
164
|
+
}
|
|
165
|
+
function describeExpressionGrammar() {
|
|
166
|
+
return {
|
|
167
|
+
forms: [
|
|
168
|
+
{ syntax: "{{ $json }}", meaning: "The whole value arriving on this node's input." },
|
|
169
|
+
{ syntax: "{{ $json.field }}", meaning: "One key of that value. Dot-paths nest: $json.user.email." },
|
|
170
|
+
{ syntax: "{{ $input.field }}", meaning: "An alias for $json \u2014 the same value, either spelling." },
|
|
171
|
+
{ syntax: "{{ in }}", meaning: "The input by its context key, rather than through the alias." }
|
|
172
|
+
],
|
|
173
|
+
note: "Dot-paths only \u2014 no arithmetic, comparisons or function calls; anything else resolves to nothing. A field that is exactly one expression keeps the resolved value's type; mixed text interpolates as a string. fancy-flow's own runFlow does not interpolate: it hands config to your executor verbatim, and the fancy-flow-php runtime is what resolves these."
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
function findTrigger(text, caret) {
|
|
177
|
+
const before = text.slice(0, caret);
|
|
178
|
+
const open = before.lastIndexOf("{{");
|
|
179
|
+
if (open === -1) return null;
|
|
180
|
+
if (before.indexOf("}}", open) !== -1) return null;
|
|
181
|
+
return { open, query: before.slice(open + 2).trim() };
|
|
182
|
+
}
|
|
183
|
+
function filterVariables(vars, query) {
|
|
184
|
+
const q = query.trim().toLowerCase();
|
|
185
|
+
if (q === "") return vars;
|
|
186
|
+
return vars.filter((v) => v.path.toLowerCase().includes(q));
|
|
187
|
+
}
|
|
188
|
+
function applyCompletion(text, caret, trigger, variable) {
|
|
189
|
+
const head = text.slice(0, trigger.open);
|
|
190
|
+
const tail = text.slice(caret);
|
|
191
|
+
return { value: `${head}${variable.expression}${tail}`, caret: head.length + variable.expression.length };
|
|
192
|
+
}
|
|
193
|
+
function ExpressionField({
|
|
194
|
+
value,
|
|
195
|
+
onChange,
|
|
196
|
+
placeholder,
|
|
197
|
+
rows = 2,
|
|
198
|
+
handle,
|
|
199
|
+
graph,
|
|
200
|
+
nodeId
|
|
201
|
+
}) {
|
|
202
|
+
const ref = useRef(null);
|
|
203
|
+
const [trigger, setTrigger] = useState(null);
|
|
204
|
+
const [active, setActive] = useState(0);
|
|
205
|
+
const [showHelp, setShowHelp] = useState(false);
|
|
206
|
+
const variables = useMemo(
|
|
207
|
+
() => graph && nodeId ? availableVariables(graph, nodeId) : baseVariables(),
|
|
208
|
+
[graph, nodeId]
|
|
209
|
+
);
|
|
210
|
+
const matches = useMemo(
|
|
211
|
+
() => trigger ? filterVariables(variables, trigger.query) : [],
|
|
212
|
+
[trigger, variables]
|
|
213
|
+
);
|
|
214
|
+
const help = useMemo(() => describeExpressionGrammar(), []);
|
|
215
|
+
const open = trigger !== null && matches.length > 0;
|
|
216
|
+
const sync = (el) => {
|
|
217
|
+
const next = findTrigger(el.value, el.selectionStart ?? el.value.length);
|
|
218
|
+
setTrigger(next);
|
|
219
|
+
setActive(0);
|
|
220
|
+
};
|
|
221
|
+
const choose = (variable) => {
|
|
222
|
+
const el = ref.current;
|
|
223
|
+
if (!el || !trigger) return;
|
|
224
|
+
const { value: next, caret } = applyCompletion(
|
|
225
|
+
el.value,
|
|
226
|
+
el.selectionStart ?? el.value.length,
|
|
227
|
+
trigger,
|
|
228
|
+
variable
|
|
229
|
+
);
|
|
230
|
+
onChange(next);
|
|
231
|
+
setTrigger(null);
|
|
232
|
+
requestAnimationFrame(() => {
|
|
233
|
+
el.focus();
|
|
234
|
+
el.setSelectionRange(caret, caret);
|
|
235
|
+
});
|
|
236
|
+
};
|
|
237
|
+
const onKeyDown = (e) => {
|
|
238
|
+
if (!open) return;
|
|
239
|
+
if (e.key === "ArrowDown") {
|
|
240
|
+
e.preventDefault();
|
|
241
|
+
setActive((i) => (i + 1) % matches.length);
|
|
242
|
+
} else if (e.key === "ArrowUp") {
|
|
243
|
+
e.preventDefault();
|
|
244
|
+
setActive((i) => (i - 1 + matches.length) % matches.length);
|
|
245
|
+
} else if (e.key === "Enter" || e.key === "Tab") {
|
|
246
|
+
e.preventDefault();
|
|
247
|
+
choose(matches[active] ?? matches[0]);
|
|
248
|
+
} else if (e.key === "Escape") {
|
|
249
|
+
e.preventDefault();
|
|
250
|
+
setTrigger(null);
|
|
251
|
+
}
|
|
252
|
+
};
|
|
253
|
+
return /* @__PURE__ */ jsxs("div", { className: "ff-expression", "data-ff-expression": handle["data-ff-field"], children: [
|
|
254
|
+
/* @__PURE__ */ jsx(
|
|
255
|
+
"textarea",
|
|
256
|
+
{
|
|
257
|
+
...handle,
|
|
258
|
+
ref,
|
|
259
|
+
className: "ff-panel__input ff-panel__input--expression",
|
|
260
|
+
rows,
|
|
261
|
+
value: value ?? "",
|
|
262
|
+
placeholder: placeholder ?? "{{ $json.field }}",
|
|
263
|
+
spellCheck: false,
|
|
264
|
+
role: "combobox",
|
|
265
|
+
"aria-expanded": open,
|
|
266
|
+
"aria-autocomplete": "list",
|
|
267
|
+
"aria-controls": open ? `${handle.id ?? handle["data-ff-field"]}-vars` : void 0,
|
|
268
|
+
onChange: (e) => {
|
|
269
|
+
onChange(e.target.value);
|
|
270
|
+
sync(e.target);
|
|
271
|
+
},
|
|
272
|
+
onKeyDown,
|
|
273
|
+
onClick: (e) => sync(e.currentTarget),
|
|
274
|
+
onBlur: () => {
|
|
275
|
+
window.setTimeout(() => setTrigger(null), 120);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
),
|
|
279
|
+
/* @__PURE__ */ jsxs("div", { className: "ff-expression__bar", children: [
|
|
280
|
+
/* @__PURE__ */ jsxs(
|
|
281
|
+
"button",
|
|
282
|
+
{
|
|
283
|
+
type: "button",
|
|
284
|
+
className: "ff-expression__helpbtn",
|
|
285
|
+
"aria-expanded": showHelp,
|
|
286
|
+
onClick: () => setShowHelp((s) => !s),
|
|
287
|
+
children: [
|
|
288
|
+
"{{ }}",
|
|
289
|
+
" reference"
|
|
290
|
+
]
|
|
291
|
+
}
|
|
292
|
+
),
|
|
293
|
+
variables.length > 1 && /* @__PURE__ */ jsxs("span", { className: "ff-expression__hint", children: [
|
|
294
|
+
"type ",
|
|
295
|
+
/* @__PURE__ */ jsx("code", { children: "{{" }),
|
|
296
|
+
" for ",
|
|
297
|
+
variables.length,
|
|
298
|
+
" variables"
|
|
299
|
+
] })
|
|
300
|
+
] }),
|
|
301
|
+
open && /* @__PURE__ */ jsx(
|
|
302
|
+
"ul",
|
|
303
|
+
{
|
|
304
|
+
className: "ff-expression__menu",
|
|
305
|
+
id: `${handle.id ?? handle["data-ff-field"]}-vars`,
|
|
306
|
+
role: "listbox",
|
|
307
|
+
"data-ff-expression-menu": handle["data-ff-field"],
|
|
308
|
+
children: matches.map((v, i) => /* @__PURE__ */ jsx("li", { role: "option", "aria-selected": i === active, children: /* @__PURE__ */ jsxs(
|
|
309
|
+
"button",
|
|
310
|
+
{
|
|
311
|
+
type: "button",
|
|
312
|
+
className: `ff-expression__option${i === active ? " is-active" : ""}`,
|
|
313
|
+
"data-ff-expression-option": v.path,
|
|
314
|
+
onMouseDown: (e) => {
|
|
315
|
+
e.preventDefault();
|
|
316
|
+
choose(v);
|
|
317
|
+
},
|
|
318
|
+
children: [
|
|
319
|
+
/* @__PURE__ */ jsx("code", { className: "ff-expression__path", children: v.path }),
|
|
320
|
+
v.source && /* @__PURE__ */ jsx("span", { className: "ff-expression__source", children: v.source }),
|
|
321
|
+
v.description && /* @__PURE__ */ jsx("span", { className: "ff-expression__desc", children: v.description })
|
|
322
|
+
]
|
|
323
|
+
}
|
|
324
|
+
) }, v.expression))
|
|
325
|
+
}
|
|
326
|
+
),
|
|
327
|
+
showHelp && /* @__PURE__ */ jsxs("div", { className: "ff-expression__help", role: "note", "data-ff-expression-help": true, children: [
|
|
328
|
+
/* @__PURE__ */ jsx("dl", { children: help.forms.map((f) => /* @__PURE__ */ jsxs("div", { children: [
|
|
329
|
+
/* @__PURE__ */ jsx("dt", { children: /* @__PURE__ */ jsx("code", { children: f.syntax }) }),
|
|
330
|
+
/* @__PURE__ */ jsx("dd", { children: f.meaning })
|
|
331
|
+
] }, f.syntax)) }),
|
|
332
|
+
/* @__PURE__ */ jsx("p", { className: "ff-expression__note", children: help.note })
|
|
333
|
+
] })
|
|
334
|
+
] });
|
|
335
|
+
}
|
|
112
336
|
function ConfigFieldRenderer({
|
|
113
337
|
field,
|
|
114
338
|
value,
|
|
@@ -116,7 +340,9 @@ function ConfigFieldRenderer({
|
|
|
116
340
|
id,
|
|
117
341
|
renderCredentialField,
|
|
118
342
|
renderDocumentField,
|
|
119
|
-
fieldRenderers
|
|
343
|
+
fieldRenderers,
|
|
344
|
+
graph,
|
|
345
|
+
nodeId
|
|
120
346
|
}) {
|
|
121
347
|
const handle = { id, "data-ff-field": field.key };
|
|
122
348
|
const custom = fieldRenderers?.[field.type];
|
|
@@ -195,18 +421,17 @@ function ConfigFieldRenderer({
|
|
|
195
421
|
}
|
|
196
422
|
);
|
|
197
423
|
case "json":
|
|
198
|
-
return /* @__PURE__ */ jsx(JsonField, { value, onChange, rows: field.rows });
|
|
424
|
+
return /* @__PURE__ */ jsx(JsonField, { value, onChange, rows: field.rows, handle });
|
|
199
425
|
case "expression":
|
|
200
426
|
return /* @__PURE__ */ jsx(
|
|
201
|
-
|
|
427
|
+
ExpressionField,
|
|
202
428
|
{
|
|
203
|
-
|
|
204
|
-
className: "ff-panel__input ff-panel__input--expression",
|
|
205
|
-
rows: 2,
|
|
429
|
+
handle,
|
|
206
430
|
value: value ?? "",
|
|
431
|
+
onChange,
|
|
207
432
|
placeholder: field.example ?? "{{ $json.field }}",
|
|
208
|
-
|
|
209
|
-
|
|
433
|
+
graph,
|
|
434
|
+
nodeId
|
|
210
435
|
}
|
|
211
436
|
);
|
|
212
437
|
case "credential":
|
|
@@ -227,16 +452,19 @@ function ConfigFieldRenderer({
|
|
|
227
452
|
return /* @__PURE__ */ jsx(
|
|
228
453
|
RepeaterField,
|
|
229
454
|
{
|
|
455
|
+
id,
|
|
230
456
|
field,
|
|
231
457
|
value,
|
|
232
458
|
onChange,
|
|
233
459
|
renderCredentialField,
|
|
234
460
|
renderDocumentField,
|
|
235
|
-
fieldRenderers
|
|
461
|
+
fieldRenderers,
|
|
462
|
+
graph,
|
|
463
|
+
nodeId
|
|
236
464
|
}
|
|
237
465
|
);
|
|
238
466
|
case "keyvalue":
|
|
239
|
-
return /* @__PURE__ */ jsx(KeyValueField, { field, value, onChange });
|
|
467
|
+
return /* @__PURE__ */ jsx(KeyValueField, { id, field, value, onChange });
|
|
240
468
|
case "document":
|
|
241
469
|
if (renderDocumentField) {
|
|
242
470
|
return /* @__PURE__ */ jsx(Fragment, { children: renderDocumentField({ documentType: field.documentType, value, onChange }) });
|
|
@@ -281,9 +509,12 @@ function ChoiceField({
|
|
|
281
509
|
);
|
|
282
510
|
}
|
|
283
511
|
function RepeaterField({
|
|
512
|
+
id,
|
|
284
513
|
field,
|
|
285
514
|
value,
|
|
286
515
|
onChange,
|
|
516
|
+
graph,
|
|
517
|
+
nodeId,
|
|
287
518
|
renderCredentialField,
|
|
288
519
|
renderDocumentField,
|
|
289
520
|
fieldRenderers
|
|
@@ -315,7 +546,7 @@ function RepeaterField({
|
|
|
315
546
|
if (typeof raw === "number" || typeof raw === "boolean") return String(raw);
|
|
316
547
|
return `Item ${i + 1}`;
|
|
317
548
|
};
|
|
318
|
-
return /* @__PURE__ */ jsxs("div", { className: "ff-repeater", "data-ff-repeater": field.key, children: [
|
|
549
|
+
return /* @__PURE__ */ jsxs("div", { id, role: "group", className: "ff-repeater", "data-ff-repeater": field.key, "data-ff-field": field.key, children: [
|
|
319
550
|
rows.length === 0 && /* @__PURE__ */ jsx("p", { className: "ff-repeater__empty", children: "None yet." }),
|
|
320
551
|
rows.map((row, i) => /* @__PURE__ */ jsxs("div", { className: "ff-repeater__row", "data-ff-repeater-row": i, children: [
|
|
321
552
|
/* @__PURE__ */ jsxs("div", { className: "ff-repeater__row-head", children: [
|
|
@@ -364,6 +595,8 @@ function RepeaterField({
|
|
|
364
595
|
/* @__PURE__ */ jsx(
|
|
365
596
|
ConfigFieldRenderer,
|
|
366
597
|
{
|
|
598
|
+
graph,
|
|
599
|
+
nodeId,
|
|
367
600
|
field: sub,
|
|
368
601
|
value: row[sub.key],
|
|
369
602
|
onChange: (cell) => setCell(i, sub.key, cell),
|
|
@@ -390,6 +623,7 @@ function RepeaterField({
|
|
|
390
623
|
] });
|
|
391
624
|
}
|
|
392
625
|
function KeyValueField({
|
|
626
|
+
id,
|
|
393
627
|
field,
|
|
394
628
|
value,
|
|
395
629
|
onChange
|
|
@@ -408,7 +642,7 @@ function KeyValueField({
|
|
|
408
642
|
const setVal = (i, val) => commit(entries.map(([k, v], idx) => idx === i ? [k, val] : [k, v]));
|
|
409
643
|
const remove = (i) => commit(entries.filter((_, idx) => idx !== i));
|
|
410
644
|
const add = () => commit([...entries, ["", ""]]);
|
|
411
|
-
return /* @__PURE__ */ jsxs("div", { className: "ff-keyvalue", "data-ff-keyvalue": field.key, children: [
|
|
645
|
+
return /* @__PURE__ */ jsxs("div", { id, role: "group", className: "ff-keyvalue", "data-ff-keyvalue": field.key, "data-ff-field": field.key, children: [
|
|
412
646
|
entries.length > 0 && /* @__PURE__ */ jsxs("div", { className: "ff-keyvalue__head", children: [
|
|
413
647
|
/* @__PURE__ */ jsx("span", { children: field.keyLabel ?? "Key" }),
|
|
414
648
|
/* @__PURE__ */ jsx("span", { children: field.valueLabel ?? "Value" }),
|
|
@@ -464,34 +698,76 @@ function KeyValueField({
|
|
|
464
698
|
] })
|
|
465
699
|
] });
|
|
466
700
|
}
|
|
467
|
-
function JsonField({
|
|
468
|
-
|
|
701
|
+
function JsonField({
|
|
702
|
+
value,
|
|
703
|
+
onChange,
|
|
704
|
+
rows,
|
|
705
|
+
handle
|
|
706
|
+
}) {
|
|
707
|
+
const serialized = useMemo(() => {
|
|
469
708
|
try {
|
|
470
709
|
return value === void 0 ? "" : JSON.stringify(value, null, 2);
|
|
471
710
|
} catch {
|
|
472
711
|
return "";
|
|
473
712
|
}
|
|
474
713
|
}, [value]);
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
714
|
+
const [text, setText] = useState(serialized);
|
|
715
|
+
const [error, setError] = useState(null);
|
|
716
|
+
const [touched, setTouched] = useState(false);
|
|
717
|
+
const lastProps = useRef(serialized);
|
|
718
|
+
const committed = useRef(null);
|
|
719
|
+
if (serialized !== lastProps.current) {
|
|
720
|
+
lastProps.current = serialized;
|
|
721
|
+
if (serialized !== committed.current) {
|
|
722
|
+
setText(serialized);
|
|
723
|
+
setError(null);
|
|
724
|
+
setTouched(false);
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
const commit = (next) => {
|
|
728
|
+
const trimmed = next.trim();
|
|
729
|
+
if (trimmed === "") {
|
|
730
|
+
committed.current = "";
|
|
731
|
+
onChange(void 0);
|
|
732
|
+
return null;
|
|
733
|
+
}
|
|
734
|
+
try {
|
|
735
|
+
const parsed = JSON.parse(trimmed);
|
|
736
|
+
committed.current = JSON.stringify(parsed, null, 2);
|
|
737
|
+
onChange(parsed);
|
|
738
|
+
return null;
|
|
739
|
+
} catch (e) {
|
|
740
|
+
return e instanceof Error ? e.message : "Invalid JSON";
|
|
741
|
+
}
|
|
742
|
+
};
|
|
743
|
+
const errorId = handle.id ? `${handle.id}-error` : void 0;
|
|
744
|
+
const showError = touched && error !== null;
|
|
745
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
746
|
+
/* @__PURE__ */ jsx(
|
|
747
|
+
"textarea",
|
|
748
|
+
{
|
|
749
|
+
...handle,
|
|
750
|
+
className: "ff-panel__input ff-panel__input--json",
|
|
751
|
+
rows: rows ?? 6,
|
|
752
|
+
value: text,
|
|
753
|
+
spellCheck: false,
|
|
754
|
+
"aria-invalid": showError || void 0,
|
|
755
|
+
"aria-describedby": showError ? errorId : void 0,
|
|
756
|
+
onChange: (e) => {
|
|
757
|
+
setText(e.target.value);
|
|
758
|
+
setError(commit(e.target.value));
|
|
759
|
+
},
|
|
760
|
+
onBlur: (e) => {
|
|
761
|
+
setTouched(true);
|
|
762
|
+
setError(commit(e.target.value));
|
|
491
763
|
}
|
|
492
764
|
}
|
|
493
|
-
|
|
494
|
-
|
|
765
|
+
),
|
|
766
|
+
showError && /* @__PURE__ */ jsxs("p", { className: "ff-panel__issue ff-panel__issue--field", id: errorId, role: "alert", children: [
|
|
767
|
+
"\u26A0 ",
|
|
768
|
+
error
|
|
769
|
+
] })
|
|
770
|
+
] });
|
|
495
771
|
}
|
|
496
772
|
function NodeConfigPanel({
|
|
497
773
|
node,
|
|
@@ -502,6 +778,7 @@ function NodeConfigPanel({
|
|
|
502
778
|
renderCredentialField,
|
|
503
779
|
renderDocumentField,
|
|
504
780
|
fieldRenderers,
|
|
781
|
+
graph,
|
|
505
782
|
className,
|
|
506
783
|
style
|
|
507
784
|
}) {
|
|
@@ -607,7 +884,9 @@ function NodeConfigPanel({
|
|
|
607
884
|
onChange: (v) => setConfigValue(field.key, v),
|
|
608
885
|
renderCredentialField,
|
|
609
886
|
renderDocumentField: documentField,
|
|
610
|
-
fieldRenderers
|
|
887
|
+
fieldRenderers,
|
|
888
|
+
graph,
|
|
889
|
+
nodeId: node.id
|
|
611
890
|
}
|
|
612
891
|
)
|
|
613
892
|
] }, field.key))
|
|
@@ -810,6 +1089,7 @@ function FlowEditorInner({
|
|
|
810
1089
|
metadata,
|
|
811
1090
|
showPalette = true,
|
|
812
1091
|
showPanel = true,
|
|
1092
|
+
fieldRenderers,
|
|
813
1093
|
showFeed = true,
|
|
814
1094
|
extraToolbar,
|
|
815
1095
|
actions = [],
|
|
@@ -1369,6 +1649,8 @@ function FlowEditorInner({
|
|
|
1369
1649
|
className: "ff-editor__panel",
|
|
1370
1650
|
node: api.selected,
|
|
1371
1651
|
onChange: api.updateNode,
|
|
1652
|
+
fieldRenderers,
|
|
1653
|
+
graph: flow,
|
|
1372
1654
|
onDelete: builtins.delete === false ? void 0 : (n) => api.deleteNodes([n.id])
|
|
1373
1655
|
}
|
|
1374
1656
|
),
|
|
@@ -1544,6 +1826,6 @@ var flowKeys = {
|
|
|
1544
1826
|
// src/index.ts
|
|
1545
1827
|
registerBuiltinKinds();
|
|
1546
1828
|
|
|
1547
|
-
export { ConfigFieldRenderer, FlowEditor, FlowRunControls, FlowRunFeed, NodeConfigPanel, NodePalette, NodePort, defineNode, flowKeys, flowLive, paletteDropHandlers };
|
|
1829
|
+
export { ConfigFieldRenderer, ExpressionField, FlowEditor, FlowRunControls, FlowRunFeed, NodeConfigPanel, NodePalette, NodePort, availableVariables, baseVariables, defineNode, describeExpressionGrammar, flowKeys, flowLive, outputFieldsFor, paletteDropHandlers };
|
|
1548
1830
|
//# sourceMappingURL=index.js.map
|
|
1549
1831
|
//# sourceMappingURL=index.js.map
|