@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/index.cjs
CHANGED
|
@@ -8,6 +8,8 @@ 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/builtin.ts
|
|
12
|
+
|
|
11
13
|
// src/registry/registry.ts
|
|
12
14
|
var kinds = /* @__PURE__ */ new Map();
|
|
13
15
|
var listeners = /* @__PURE__ */ new Set();
|
|
@@ -81,6 +83,47 @@ function validateField(field, value) {
|
|
|
81
83
|
case "json":
|
|
82
84
|
return null;
|
|
83
85
|
// permissive — just JSON-shaped
|
|
86
|
+
case "repeater": {
|
|
87
|
+
if (!Array.isArray(value)) return `${field.label} must be a list`;
|
|
88
|
+
if (field.minItems !== void 0 && value.length < field.minItems) {
|
|
89
|
+
return `${field.label} needs at least ${field.minItems}`;
|
|
90
|
+
}
|
|
91
|
+
if (field.maxItems !== void 0 && value.length > field.maxItems) {
|
|
92
|
+
return `${field.label} allows at most ${field.maxItems}`;
|
|
93
|
+
}
|
|
94
|
+
for (let i = 0; i < value.length; i++) {
|
|
95
|
+
const row = value[i];
|
|
96
|
+
if (!row || typeof row !== "object" || Array.isArray(row)) {
|
|
97
|
+
return `${field.label} item ${i + 1} must be an object`;
|
|
98
|
+
}
|
|
99
|
+
for (const sub of field.fields) {
|
|
100
|
+
const cell = row[sub.key];
|
|
101
|
+
if (sub.required && (cell === void 0 || cell === null || cell === "")) {
|
|
102
|
+
return `${field.label} item ${i + 1}: ${sub.label} is required`;
|
|
103
|
+
}
|
|
104
|
+
if (cell === void 0 || cell === null) continue;
|
|
105
|
+
const issue = validateField(sub, cell);
|
|
106
|
+
if (issue) return `${field.label} item ${i + 1}: ${issue}`;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
case "keyvalue": {
|
|
112
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
113
|
+
return `${field.label} must be a key/value map`;
|
|
114
|
+
}
|
|
115
|
+
const allowed = field.valueOptions?.map((o) => o.value);
|
|
116
|
+
for (const [k, v] of Object.entries(value)) {
|
|
117
|
+
if (typeof v !== "string") return `${field.label}: "${k}" must be a string`;
|
|
118
|
+
if (allowed && !allowed.includes(v)) {
|
|
119
|
+
return `${field.label}: "${k}" must be one of ${allowed.join(", ")}`;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
case "document":
|
|
125
|
+
return null;
|
|
126
|
+
// opaque to fancy-flow — the host's editor owns its shape
|
|
84
127
|
default:
|
|
85
128
|
return null;
|
|
86
129
|
}
|
|
@@ -105,8 +148,90 @@ function categoryAccent(category) {
|
|
|
105
148
|
return "#71717a";
|
|
106
149
|
}
|
|
107
150
|
}
|
|
151
|
+
var adapter = null;
|
|
152
|
+
var listeners2 = /* @__PURE__ */ new Set();
|
|
153
|
+
function registerRichInputAdapter(next) {
|
|
154
|
+
adapter = next;
|
|
155
|
+
for (const l of listeners2) l();
|
|
156
|
+
return () => {
|
|
157
|
+
if (adapter === next) {
|
|
158
|
+
adapter = null;
|
|
159
|
+
for (const l of listeners2) l();
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
function getRichInputAdapter() {
|
|
164
|
+
return adapter;
|
|
165
|
+
}
|
|
166
|
+
function isRichInputEnabled() {
|
|
167
|
+
return adapter !== null && (adapter.renderDocument !== void 0 || adapter.renderEditor !== void 0);
|
|
168
|
+
}
|
|
169
|
+
function onRichInputAdapterChanged(fn) {
|
|
170
|
+
listeners2.add(fn);
|
|
171
|
+
return () => listeners2.delete(fn);
|
|
172
|
+
}
|
|
173
|
+
function RichInputPreview({ config }) {
|
|
174
|
+
const a = getRichInputAdapter();
|
|
175
|
+
const title = typeof config.title === "string" && config.title.trim() !== "" ? config.title : "Untitled step";
|
|
176
|
+
const doc = config.document;
|
|
177
|
+
if (!a || !a.renderDocument && !a.renderEditor) {
|
|
178
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ff-rich-preview", children: [
|
|
179
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "ff-rich-preview__title", children: title }),
|
|
180
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ff-rich-preview__unavailable", children: [
|
|
181
|
+
"Add ",
|
|
182
|
+
/* @__PURE__ */ jsxRuntime.jsx("code", { children: "@particle-academy/fancy-cms-ui" }),
|
|
183
|
+
" +",
|
|
184
|
+
" ",
|
|
185
|
+
/* @__PURE__ */ jsxRuntime.jsx("code", { children: "@particle-academy/react-fancy" }),
|
|
186
|
+
", then",
|
|
187
|
+
" ",
|
|
188
|
+
/* @__PURE__ */ jsxRuntime.jsx("code", { children: 'import "@particle-academy/fancy-flow/rich-input"' }),
|
|
189
|
+
"."
|
|
190
|
+
] })
|
|
191
|
+
] });
|
|
192
|
+
}
|
|
193
|
+
const body = doc === void 0 || doc === null ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "ff-rich-preview__unavailable", children: "Nothing authored yet." }) : a.renderDocument?.(doc) ?? null;
|
|
194
|
+
const Frame = a.FauxClient;
|
|
195
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ff-rich-preview", children: [
|
|
196
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "ff-rich-preview__title", children: title }),
|
|
197
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "ff-rich-preview__frame", children: Frame ? /* @__PURE__ */ jsxRuntime.jsx(Frame, { ...a.frameProps ?? { variant: "browser" }, children: body }) : body })
|
|
198
|
+
] });
|
|
199
|
+
}
|
|
108
200
|
|
|
109
201
|
// src/registry/builtin.ts
|
|
202
|
+
function casePorts(cases) {
|
|
203
|
+
const byPort = /* @__PURE__ */ new Map();
|
|
204
|
+
if (cases && typeof cases === "object" && !Array.isArray(cases)) {
|
|
205
|
+
for (const [match, port] of Object.entries(cases)) {
|
|
206
|
+
if (typeof port !== "string" || port === "" || port === "default") continue;
|
|
207
|
+
const matches = byPort.get(port) ?? [];
|
|
208
|
+
matches.push(match);
|
|
209
|
+
byPort.set(port, matches);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
const ports = [...byPort].map(([id2, matches]) => ({
|
|
213
|
+
id: id2,
|
|
214
|
+
label: matches.join("|")
|
|
215
|
+
}));
|
|
216
|
+
return [...ports, { id: "default", label: "default" }];
|
|
217
|
+
}
|
|
218
|
+
function routePorts(routes, fallback) {
|
|
219
|
+
const ports = [];
|
|
220
|
+
const seen = /* @__PURE__ */ new Set();
|
|
221
|
+
if (Array.isArray(routes)) {
|
|
222
|
+
for (const route of routes) {
|
|
223
|
+
const id2 = route?.port;
|
|
224
|
+
if (typeof id2 !== "string" || id2.trim() === "" || seen.has(id2)) continue;
|
|
225
|
+
seen.add(id2);
|
|
226
|
+
ports.push({ id: id2, label: id2 });
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
if (fallback !== false && !seen.has("fallback")) {
|
|
230
|
+
ports.push({ id: "fallback", label: "fallback" });
|
|
231
|
+
}
|
|
232
|
+
if (ports.length === 0) ports.push({ id: "out" });
|
|
233
|
+
return ports;
|
|
234
|
+
}
|
|
110
235
|
var HTTP_METHODS = [
|
|
111
236
|
{ type: "select", key: "method", label: "Method", options: [
|
|
112
237
|
{ value: "GET", label: "GET" },
|
|
@@ -175,15 +300,59 @@ var KINDS = [
|
|
|
175
300
|
configSchema: [
|
|
176
301
|
{ type: "text", key: "title", label: "Form title", default: "Need your input" },
|
|
177
302
|
{
|
|
178
|
-
type: "
|
|
303
|
+
type: "repeater",
|
|
179
304
|
key: "fields",
|
|
180
|
-
label: "Fields
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
305
|
+
label: "Fields",
|
|
306
|
+
description: "The form the run pauses on.",
|
|
307
|
+
titleKey: "label",
|
|
308
|
+
addLabel: "Add field",
|
|
309
|
+
minItems: 1,
|
|
310
|
+
fields: [
|
|
311
|
+
{ type: "text", key: "key", label: "Key", required: true, placeholder: "answer" },
|
|
312
|
+
{ type: "text", key: "label", label: "Label", required: true, placeholder: "Your answer" },
|
|
313
|
+
{
|
|
314
|
+
type: "select",
|
|
315
|
+
key: "type",
|
|
316
|
+
label: "Type",
|
|
317
|
+
default: "text",
|
|
318
|
+
options: [
|
|
319
|
+
{ value: "text", label: "Text" },
|
|
320
|
+
{ value: "textarea", label: "Long text" },
|
|
321
|
+
{ value: "number", label: "Number" },
|
|
322
|
+
{ value: "select", label: "Select" },
|
|
323
|
+
{ value: "switch", label: "Switch" }
|
|
324
|
+
]
|
|
325
|
+
},
|
|
326
|
+
{ type: "switch", key: "required", label: "Required", default: false }
|
|
327
|
+
],
|
|
328
|
+
default: [{ key: "answer", label: "Your answer", type: "textarea", required: true }]
|
|
184
329
|
}
|
|
185
330
|
]
|
|
186
331
|
},
|
|
332
|
+
{
|
|
333
|
+
name: "rich_user_input",
|
|
334
|
+
category: "human",
|
|
335
|
+
label: "Rich User Input",
|
|
336
|
+
description: "Pause the flow on a fully authored page \u2014 content, required reading, multi-section forms.",
|
|
337
|
+
icon: "\u25A4",
|
|
338
|
+
inputs: [{ id: "in" }],
|
|
339
|
+
outputs: [{ id: "out", label: "values" }],
|
|
340
|
+
configSchema: [
|
|
341
|
+
{ type: "text", key: "title", label: "Step title", default: "Please review" },
|
|
342
|
+
{
|
|
343
|
+
type: "document",
|
|
344
|
+
key: "document",
|
|
345
|
+
label: "Page content",
|
|
346
|
+
documentType: "stages",
|
|
347
|
+
description: "Authored with the host's document editor (fancy-cms Stages)."
|
|
348
|
+
},
|
|
349
|
+
{ type: "switch", key: "requireConfirm", label: "Require explicit confirmation", default: true },
|
|
350
|
+
{ type: "text", key: "submitLabel", label: "Submit button", default: "Continue" }
|
|
351
|
+
],
|
|
352
|
+
// Preview the authored page inside a FauxClient frame, so the canvas shows
|
|
353
|
+
// what the person hitting this step will actually see.
|
|
354
|
+
renderBody: (ctx) => ReactExports.createElement(RichInputPreview, { config: ctx.config ?? {} })
|
|
355
|
+
},
|
|
187
356
|
// ───────────── Logic ─────────────
|
|
188
357
|
{
|
|
189
358
|
name: "branch",
|
|
@@ -204,10 +373,24 @@ var KINDS = [
|
|
|
204
373
|
description: "Route to one of N labelled outputs based on a key.",
|
|
205
374
|
icon: "\u2933",
|
|
206
375
|
inputs: [{ id: "in" }],
|
|
207
|
-
|
|
376
|
+
// Ports ARE the config: every distinct port a case routes to becomes an
|
|
377
|
+
// output handle, plus the always-present `default`. Editing the cases map
|
|
378
|
+
// moves the ports on the canvas and the ports the runtime activates.
|
|
379
|
+
outputs: (config) => casePorts(config?.cases),
|
|
208
380
|
configSchema: [
|
|
209
381
|
{ type: "expression", key: "value", label: "Switch on", example: "{{ $json.kind }}", required: true },
|
|
210
|
-
{
|
|
382
|
+
{
|
|
383
|
+
type: "keyvalue",
|
|
384
|
+
key: "cases",
|
|
385
|
+
label: "Cases",
|
|
386
|
+
description: "Match value \u2192 output port. Unmatched input takes `default`.",
|
|
387
|
+
keyLabel: "When value is",
|
|
388
|
+
valueLabel: "Route to port",
|
|
389
|
+
keyPlaceholder: "a",
|
|
390
|
+
valuePlaceholder: "case_a",
|
|
391
|
+
addLabel: "Add case",
|
|
392
|
+
default: { a: "case_a", b: "case_b" }
|
|
393
|
+
}
|
|
211
394
|
]
|
|
212
395
|
},
|
|
213
396
|
{
|
|
@@ -362,6 +545,76 @@ var KINDS = [
|
|
|
362
545
|
{ type: "credential", key: "credential", label: "API credential", credentialType: "llm_credential" }
|
|
363
546
|
]
|
|
364
547
|
},
|
|
548
|
+
{
|
|
549
|
+
name: "llm_branch",
|
|
550
|
+
category: "ai",
|
|
551
|
+
label: "LLM Router",
|
|
552
|
+
description: "Let a model choose which route the flow takes.",
|
|
553
|
+
icon: "\u2727",
|
|
554
|
+
inputs: [{ id: "in" }],
|
|
555
|
+
// Each declared route is a port. The executor returns `{ __port: id }`
|
|
556
|
+
// (or `Port.only(id)` on the PHP runtime) to pick one.
|
|
557
|
+
outputs: (config) => routePorts(config?.routes, config?.fallback),
|
|
558
|
+
configSchema: [
|
|
559
|
+
{
|
|
560
|
+
type: "textarea",
|
|
561
|
+
key: "system",
|
|
562
|
+
label: "System prompt",
|
|
563
|
+
rows: 3,
|
|
564
|
+
description: "Optional framing for the routing decision."
|
|
565
|
+
},
|
|
566
|
+
{
|
|
567
|
+
type: "expression",
|
|
568
|
+
key: "prompt",
|
|
569
|
+
label: "What to route on",
|
|
570
|
+
example: "{{ $json.message }}",
|
|
571
|
+
required: true
|
|
572
|
+
},
|
|
573
|
+
{
|
|
574
|
+
type: "repeater",
|
|
575
|
+
key: "routes",
|
|
576
|
+
label: "Routes",
|
|
577
|
+
description: "The model picks exactly one. Descriptions are what it chooses between \u2014 make them distinct.",
|
|
578
|
+
titleKey: "port",
|
|
579
|
+
addLabel: "Add route",
|
|
580
|
+
minItems: 2,
|
|
581
|
+
fields: [
|
|
582
|
+
{ type: "text", key: "port", label: "Port", required: true, placeholder: "billing" },
|
|
583
|
+
{
|
|
584
|
+
type: "text",
|
|
585
|
+
key: "description",
|
|
586
|
+
label: "When to choose it",
|
|
587
|
+
required: true,
|
|
588
|
+
placeholder: "The user is asking about an invoice, refund, or payment."
|
|
589
|
+
}
|
|
590
|
+
],
|
|
591
|
+
default: [
|
|
592
|
+
{ port: "a", description: "Describe when the model should pick this route." },
|
|
593
|
+
{ port: "b", description: "Describe when the model should pick this route." }
|
|
594
|
+
]
|
|
595
|
+
},
|
|
596
|
+
{
|
|
597
|
+
type: "select",
|
|
598
|
+
key: "provider",
|
|
599
|
+
label: "Provider",
|
|
600
|
+
default: "anthropic",
|
|
601
|
+
options: [
|
|
602
|
+
{ value: "anthropic", label: "Anthropic" },
|
|
603
|
+
{ value: "openai", label: "OpenAI" },
|
|
604
|
+
{ value: "custom", label: "Custom" }
|
|
605
|
+
]
|
|
606
|
+
},
|
|
607
|
+
{ type: "text", key: "model", label: "Model", placeholder: "claude-sonnet-4-5" },
|
|
608
|
+
{
|
|
609
|
+
type: "switch",
|
|
610
|
+
key: "fallback",
|
|
611
|
+
label: "Add a `fallback` port",
|
|
612
|
+
default: true,
|
|
613
|
+
description: "Where the flow goes if the model returns no usable route."
|
|
614
|
+
},
|
|
615
|
+
{ type: "credential", key: "credential", label: "API credential", credentialType: "llm_credential" }
|
|
616
|
+
]
|
|
617
|
+
},
|
|
365
618
|
{
|
|
366
619
|
name: "tool_use",
|
|
367
620
|
category: "ai",
|
|
@@ -1474,7 +1727,7 @@ function defaultTouchable() {
|
|
|
1474
1727
|
return navigator.maxTouchPoints || "ontouchstart" in this;
|
|
1475
1728
|
}
|
|
1476
1729
|
function drag_default() {
|
|
1477
|
-
var filter2 = defaultFilter, container = defaultContainer, subject = defaultSubject, touchable = defaultTouchable, gestures = {},
|
|
1730
|
+
var filter2 = defaultFilter, container = defaultContainer, subject = defaultSubject, touchable = defaultTouchable, gestures = {}, listeners3 = dispatch_default("start", "drag", "end"), active = 0, mousedownx, mousedowny, mousemoving, touchending, clickDistance2 = 0;
|
|
1478
1731
|
function drag(selection2) {
|
|
1479
1732
|
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)");
|
|
1480
1733
|
}
|
|
@@ -1537,7 +1790,7 @@ function drag_default() {
|
|
|
1537
1790
|
}
|
|
1538
1791
|
}
|
|
1539
1792
|
function beforestart(that, container2, event, d, identifier, touch) {
|
|
1540
|
-
var dispatch2 =
|
|
1793
|
+
var dispatch2 = listeners3.copy(), p = pointer_default(touch || event, container2), dx, dy, s;
|
|
1541
1794
|
if ((s = subject.call(that, new DragEvent("beforestart", {
|
|
1542
1795
|
sourceEvent: event,
|
|
1543
1796
|
target: drag,
|
|
@@ -1596,8 +1849,8 @@ function drag_default() {
|
|
|
1596
1849
|
return arguments.length ? (touchable = typeof _ === "function" ? _ : constant_default2(!!_), drag) : touchable;
|
|
1597
1850
|
};
|
|
1598
1851
|
drag.on = function() {
|
|
1599
|
-
var value =
|
|
1600
|
-
return value ===
|
|
1852
|
+
var value = listeners3.on.apply(listeners3, arguments);
|
|
1853
|
+
return value === listeners3 ? drag : value;
|
|
1601
1854
|
};
|
|
1602
1855
|
drag.clickDistance = function(_) {
|
|
1603
1856
|
return arguments.length ? (clickDistance2 = (_ = +_) * _, drag) : Math.sqrt(clickDistance2);
|
|
@@ -3151,7 +3404,7 @@ function defaultConstrain(transform2, extent, translateExtent) {
|
|
|
3151
3404
|
);
|
|
3152
3405
|
}
|
|
3153
3406
|
function zoom_default2() {
|
|
3154
|
-
var filter2 = defaultFilter2, extent = defaultExtent, constrain = defaultConstrain, wheelDelta2 = defaultWheelDelta, touchable = defaultTouchable2, scaleExtent = [0, Infinity], translateExtent = [[-Infinity, -Infinity], [Infinity, Infinity]], duration = 250, interpolate = zoom_default,
|
|
3407
|
+
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;
|
|
3155
3408
|
function zoom(selection2) {
|
|
3156
3409
|
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)");
|
|
3157
3410
|
}
|
|
@@ -3263,14 +3516,14 @@ function zoom_default2() {
|
|
|
3263
3516
|
},
|
|
3264
3517
|
emit: function(type) {
|
|
3265
3518
|
var d = select_default2(this.that).datum();
|
|
3266
|
-
|
|
3519
|
+
listeners3.call(
|
|
3267
3520
|
type,
|
|
3268
3521
|
this.that,
|
|
3269
3522
|
new ZoomEvent(type, {
|
|
3270
3523
|
sourceEvent: this.sourceEvent,
|
|
3271
3524
|
target: zoom,
|
|
3272
3525
|
transform: this.that.__zoom,
|
|
3273
|
-
dispatch:
|
|
3526
|
+
dispatch: listeners3
|
|
3274
3527
|
}),
|
|
3275
3528
|
d
|
|
3276
3529
|
);
|
|
@@ -3420,8 +3673,8 @@ function zoom_default2() {
|
|
|
3420
3673
|
return arguments.length ? (interpolate = _, zoom) : interpolate;
|
|
3421
3674
|
};
|
|
3422
3675
|
zoom.on = function() {
|
|
3423
|
-
var value =
|
|
3424
|
-
return value ===
|
|
3676
|
+
var value = listeners3.on.apply(listeners3, arguments);
|
|
3677
|
+
return value === listeners3 ? zoom : value;
|
|
3425
3678
|
};
|
|
3426
3679
|
zoom.clickDistance = function(_) {
|
|
3427
3680
|
return arguments.length ? (clickDistance2 = (_ = +_) * _, zoom) : Math.sqrt(clickDistance2);
|
|
@@ -6094,20 +6347,20 @@ var use_sync_external_store_with_selector_default = { useSyncExternalStoreWithSe
|
|
|
6094
6347
|
// node_modules/zustand/esm/vanilla.mjs
|
|
6095
6348
|
var createStoreImpl = (createState) => {
|
|
6096
6349
|
let state;
|
|
6097
|
-
const
|
|
6350
|
+
const listeners3 = /* @__PURE__ */ new Set();
|
|
6098
6351
|
const setState = (partial, replace) => {
|
|
6099
6352
|
const nextState = typeof partial === "function" ? partial(state) : partial;
|
|
6100
6353
|
if (!Object.is(nextState, state)) {
|
|
6101
6354
|
const previousState = state;
|
|
6102
6355
|
state = (replace != null ? replace : typeof nextState !== "object" || nextState === null) ? nextState : Object.assign({}, state, nextState);
|
|
6103
|
-
|
|
6356
|
+
listeners3.forEach((listener) => listener(state, previousState));
|
|
6104
6357
|
}
|
|
6105
6358
|
};
|
|
6106
6359
|
const getState = () => state;
|
|
6107
6360
|
const getInitialState2 = () => initialState;
|
|
6108
6361
|
const subscribe = (listener) => {
|
|
6109
|
-
|
|
6110
|
-
return () =>
|
|
6362
|
+
listeners3.add(listener);
|
|
6363
|
+
return () => listeners3.delete(listener);
|
|
6111
6364
|
};
|
|
6112
6365
|
const destroy = () => {
|
|
6113
6366
|
if ((undefined ? undefined.MODE : void 0) !== "production") {
|
|
@@ -6115,7 +6368,7 @@ var createStoreImpl = (createState) => {
|
|
|
6115
6368
|
"[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."
|
|
6116
6369
|
);
|
|
6117
6370
|
}
|
|
6118
|
-
|
|
6371
|
+
listeners3.clear();
|
|
6119
6372
|
};
|
|
6120
6373
|
const api = { setState, getState, getInitialState: getInitialState2, subscribe, destroy };
|
|
6121
6374
|
const initialState = state = createState(setState, getState, api);
|
|
@@ -9647,9 +9900,18 @@ function paletteDropHandlers(onDrop) {
|
|
|
9647
9900
|
}
|
|
9648
9901
|
};
|
|
9649
9902
|
}
|
|
9650
|
-
function ConfigFieldRenderer({
|
|
9903
|
+
function ConfigFieldRenderer({
|
|
9904
|
+
field,
|
|
9905
|
+
value,
|
|
9906
|
+
onChange,
|
|
9907
|
+
renderCredentialField,
|
|
9908
|
+
renderDocumentField
|
|
9909
|
+
}) {
|
|
9651
9910
|
switch (field.type) {
|
|
9652
|
-
case "text":
|
|
9911
|
+
case "text": {
|
|
9912
|
+
if (field.choices?.length) {
|
|
9913
|
+
return /* @__PURE__ */ jsxRuntime.jsx(ChoiceField, { field, value, onChange });
|
|
9914
|
+
}
|
|
9653
9915
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
9654
9916
|
"input",
|
|
9655
9917
|
{
|
|
@@ -9660,6 +9922,7 @@ function ConfigFieldRenderer({ field, value, onChange, renderCredentialField })
|
|
|
9660
9922
|
onChange: (e) => onChange(e.target.value)
|
|
9661
9923
|
}
|
|
9662
9924
|
);
|
|
9925
|
+
}
|
|
9663
9926
|
case "textarea":
|
|
9664
9927
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
9665
9928
|
"textarea",
|
|
@@ -9737,10 +10000,244 @@ function ConfigFieldRenderer({ field, value, onChange, renderCredentialField })
|
|
|
9737
10000
|
onChange: (e) => onChange(e.target.value)
|
|
9738
10001
|
}
|
|
9739
10002
|
);
|
|
10003
|
+
case "repeater":
|
|
10004
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
10005
|
+
RepeaterField,
|
|
10006
|
+
{
|
|
10007
|
+
field,
|
|
10008
|
+
value,
|
|
10009
|
+
onChange,
|
|
10010
|
+
renderCredentialField,
|
|
10011
|
+
renderDocumentField
|
|
10012
|
+
}
|
|
10013
|
+
);
|
|
10014
|
+
case "keyvalue":
|
|
10015
|
+
return /* @__PURE__ */ jsxRuntime.jsx(KeyValueField, { field, value, onChange });
|
|
10016
|
+
case "document":
|
|
10017
|
+
if (renderDocumentField) {
|
|
10018
|
+
return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: renderDocumentField({ documentType: field.documentType, value, onChange }) });
|
|
10019
|
+
}
|
|
10020
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "ff-panel__hint ff-panel__hint--missing", children: [
|
|
10021
|
+
"No document editor supplied. Pass ",
|
|
10022
|
+
/* @__PURE__ */ jsxRuntime.jsx("code", { children: "renderDocumentField" }),
|
|
10023
|
+
" to NodeConfigPanel to author this field."
|
|
10024
|
+
] });
|
|
9740
10025
|
default:
|
|
9741
10026
|
return null;
|
|
9742
10027
|
}
|
|
9743
10028
|
}
|
|
10029
|
+
function normalizeChoices(choices) {
|
|
10030
|
+
return choices.map(
|
|
10031
|
+
(c) => typeof c === "string" ? { value: c, label: c } : { value: c.value, label: c.label ?? c.value }
|
|
10032
|
+
);
|
|
10033
|
+
}
|
|
10034
|
+
function ChoiceField({
|
|
10035
|
+
field,
|
|
10036
|
+
value,
|
|
10037
|
+
onChange
|
|
10038
|
+
}) {
|
|
10039
|
+
const options = normalizeChoices(field.choices ?? []);
|
|
10040
|
+
const current = typeof value === "string" ? value : "";
|
|
10041
|
+
const known = options.some((o) => o.value === current);
|
|
10042
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
10043
|
+
"select",
|
|
10044
|
+
{
|
|
10045
|
+
className: "ff-panel__input",
|
|
10046
|
+
value: current,
|
|
10047
|
+
onChange: (e) => onChange(e.target.value),
|
|
10048
|
+
children: [
|
|
10049
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "", disabled: true, children: field.placeholder ?? "\u2014" }),
|
|
10050
|
+
options.map((o) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: o.value, children: o.label }, o.value)),
|
|
10051
|
+
current !== "" && !known && /* @__PURE__ */ jsxRuntime.jsxs("option", { value: current, children: [
|
|
10052
|
+
current,
|
|
10053
|
+
" (not in list)"
|
|
10054
|
+
] })
|
|
10055
|
+
]
|
|
10056
|
+
}
|
|
10057
|
+
);
|
|
10058
|
+
}
|
|
10059
|
+
function RepeaterField({
|
|
10060
|
+
field,
|
|
10061
|
+
value,
|
|
10062
|
+
onChange,
|
|
10063
|
+
renderCredentialField,
|
|
10064
|
+
renderDocumentField
|
|
10065
|
+
}) {
|
|
10066
|
+
const rows = Array.isArray(value) ? value : [];
|
|
10067
|
+
const max = field.maxItems ?? Infinity;
|
|
10068
|
+
const min = field.minItems ?? 0;
|
|
10069
|
+
const replace = (next) => onChange(next);
|
|
10070
|
+
const addRow = () => {
|
|
10071
|
+
const blank = {};
|
|
10072
|
+
for (const f of field.fields) {
|
|
10073
|
+
if ("default" in f && f.default !== void 0) blank[f.key] = f.default;
|
|
10074
|
+
}
|
|
10075
|
+
replace([...rows, blank]);
|
|
10076
|
+
};
|
|
10077
|
+
const removeRow = (i) => replace(rows.filter((_, idx) => idx !== i));
|
|
10078
|
+
const moveRow = (i, delta) => {
|
|
10079
|
+
const target = i + delta;
|
|
10080
|
+
if (target < 0 || target >= rows.length) return;
|
|
10081
|
+
const next = [...rows];
|
|
10082
|
+
[next[i], next[target]] = [next[target], next[i]];
|
|
10083
|
+
replace(next);
|
|
10084
|
+
};
|
|
10085
|
+
const setCell = (i, key, cell) => replace(rows.map((row, idx) => idx === i ? { ...row, [key]: cell } : row));
|
|
10086
|
+
const rowTitle = (row, i) => {
|
|
10087
|
+
const key = field.titleKey ?? field.fields[0]?.key;
|
|
10088
|
+
const raw = key ? row[key] : void 0;
|
|
10089
|
+
if (typeof raw === "string" && raw.trim() !== "") return raw;
|
|
10090
|
+
if (typeof raw === "number" || typeof raw === "boolean") return String(raw);
|
|
10091
|
+
return `Item ${i + 1}`;
|
|
10092
|
+
};
|
|
10093
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ff-repeater", "data-ff-repeater": field.key, children: [
|
|
10094
|
+
rows.length === 0 && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "ff-repeater__empty", children: "None yet." }),
|
|
10095
|
+
rows.map((row, i) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ff-repeater__row", "data-ff-repeater-row": i, children: [
|
|
10096
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ff-repeater__row-head", children: [
|
|
10097
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "ff-repeater__row-title", children: rowTitle(row, i) }),
|
|
10098
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ff-repeater__row-actions", children: [
|
|
10099
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
10100
|
+
"button",
|
|
10101
|
+
{
|
|
10102
|
+
type: "button",
|
|
10103
|
+
className: "ff-repeater__btn",
|
|
10104
|
+
onClick: () => moveRow(i, -1),
|
|
10105
|
+
disabled: i === 0,
|
|
10106
|
+
"aria-label": `Move ${rowTitle(row, i)} up`,
|
|
10107
|
+
children: "\u2191"
|
|
10108
|
+
}
|
|
10109
|
+
),
|
|
10110
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
10111
|
+
"button",
|
|
10112
|
+
{
|
|
10113
|
+
type: "button",
|
|
10114
|
+
className: "ff-repeater__btn",
|
|
10115
|
+
onClick: () => moveRow(i, 1),
|
|
10116
|
+
disabled: i === rows.length - 1,
|
|
10117
|
+
"aria-label": `Move ${rowTitle(row, i)} down`,
|
|
10118
|
+
children: "\u2193"
|
|
10119
|
+
}
|
|
10120
|
+
),
|
|
10121
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
10122
|
+
"button",
|
|
10123
|
+
{
|
|
10124
|
+
type: "button",
|
|
10125
|
+
className: "ff-repeater__btn ff-repeater__btn--danger",
|
|
10126
|
+
onClick: () => removeRow(i),
|
|
10127
|
+
disabled: rows.length <= min,
|
|
10128
|
+
"aria-label": `Remove ${rowTitle(row, i)}`,
|
|
10129
|
+
children: "\u2715"
|
|
10130
|
+
}
|
|
10131
|
+
)
|
|
10132
|
+
] })
|
|
10133
|
+
] }),
|
|
10134
|
+
field.fields.map((sub) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ff-repeater__cell", children: [
|
|
10135
|
+
/* @__PURE__ */ jsxRuntime.jsxs("label", { className: "ff-panel__label ff-panel__label--sub", children: [
|
|
10136
|
+
sub.label,
|
|
10137
|
+
sub.required && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ff-panel__required", "aria-hidden": true, children: " *" })
|
|
10138
|
+
] }),
|
|
10139
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
10140
|
+
ConfigFieldRenderer,
|
|
10141
|
+
{
|
|
10142
|
+
field: sub,
|
|
10143
|
+
value: row[sub.key],
|
|
10144
|
+
onChange: (cell) => setCell(i, sub.key, cell),
|
|
10145
|
+
renderCredentialField,
|
|
10146
|
+
renderDocumentField
|
|
10147
|
+
}
|
|
10148
|
+
)
|
|
10149
|
+
] }, sub.key))
|
|
10150
|
+
] }, i)),
|
|
10151
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
10152
|
+
"button",
|
|
10153
|
+
{
|
|
10154
|
+
type: "button",
|
|
10155
|
+
className: "ff-repeater__add",
|
|
10156
|
+
onClick: addRow,
|
|
10157
|
+
disabled: rows.length >= max,
|
|
10158
|
+
children: [
|
|
10159
|
+
"+ ",
|
|
10160
|
+
field.addLabel ?? "Add"
|
|
10161
|
+
]
|
|
10162
|
+
}
|
|
10163
|
+
)
|
|
10164
|
+
] });
|
|
10165
|
+
}
|
|
10166
|
+
function KeyValueField({
|
|
10167
|
+
field,
|
|
10168
|
+
value,
|
|
10169
|
+
onChange
|
|
10170
|
+
}) {
|
|
10171
|
+
const map = value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
10172
|
+
const entries = Object.entries(map);
|
|
10173
|
+
const commit = (next) => {
|
|
10174
|
+
const obj = {};
|
|
10175
|
+
for (const [k, v] of next) {
|
|
10176
|
+
if (k === "") continue;
|
|
10177
|
+
obj[k] = v;
|
|
10178
|
+
}
|
|
10179
|
+
onChange(obj);
|
|
10180
|
+
};
|
|
10181
|
+
const setKey = (i, key) => commit(entries.map(([k, v], idx) => idx === i ? [key, v] : [k, v]));
|
|
10182
|
+
const setVal = (i, val) => commit(entries.map(([k, v], idx) => idx === i ? [k, val] : [k, v]));
|
|
10183
|
+
const remove2 = (i) => commit(entries.filter((_, idx) => idx !== i));
|
|
10184
|
+
const add = () => commit([...entries, ["", ""]]);
|
|
10185
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ff-keyvalue", "data-ff-keyvalue": field.key, children: [
|
|
10186
|
+
entries.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ff-keyvalue__head", children: [
|
|
10187
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: field.keyLabel ?? "Key" }),
|
|
10188
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: field.valueLabel ?? "Value" }),
|
|
10189
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", {})
|
|
10190
|
+
] }),
|
|
10191
|
+
entries.map(([k, v], i) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ff-keyvalue__row", "data-ff-keyvalue-row": i, children: [
|
|
10192
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
10193
|
+
"input",
|
|
10194
|
+
{
|
|
10195
|
+
className: "ff-panel__input",
|
|
10196
|
+
value: k,
|
|
10197
|
+
placeholder: field.keyPlaceholder,
|
|
10198
|
+
"aria-label": `${field.keyLabel ?? "Key"} ${i + 1}`,
|
|
10199
|
+
onChange: (e) => setKey(i, e.target.value)
|
|
10200
|
+
}
|
|
10201
|
+
),
|
|
10202
|
+
field.valueOptions ? /* @__PURE__ */ jsxRuntime.jsxs(
|
|
10203
|
+
"select",
|
|
10204
|
+
{
|
|
10205
|
+
className: "ff-panel__input",
|
|
10206
|
+
value: v ?? "",
|
|
10207
|
+
"aria-label": `${field.valueLabel ?? "Value"} ${i + 1}`,
|
|
10208
|
+
onChange: (e) => setVal(i, e.target.value),
|
|
10209
|
+
children: [
|
|
10210
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "", disabled: true, children: "\u2014" }),
|
|
10211
|
+
field.valueOptions.map((o) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: o.value, children: o.label }, o.value))
|
|
10212
|
+
]
|
|
10213
|
+
}
|
|
10214
|
+
) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
10215
|
+
"input",
|
|
10216
|
+
{
|
|
10217
|
+
className: "ff-panel__input",
|
|
10218
|
+
value: v ?? "",
|
|
10219
|
+
placeholder: field.valuePlaceholder,
|
|
10220
|
+
"aria-label": `${field.valueLabel ?? "Value"} ${i + 1}`,
|
|
10221
|
+
onChange: (e) => setVal(i, e.target.value)
|
|
10222
|
+
}
|
|
10223
|
+
),
|
|
10224
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
10225
|
+
"button",
|
|
10226
|
+
{
|
|
10227
|
+
type: "button",
|
|
10228
|
+
className: "ff-repeater__btn ff-repeater__btn--danger",
|
|
10229
|
+
onClick: () => remove2(i),
|
|
10230
|
+
"aria-label": `Remove ${k || `entry ${i + 1}`}`,
|
|
10231
|
+
children: "\u2715"
|
|
10232
|
+
}
|
|
10233
|
+
)
|
|
10234
|
+
] }, i)),
|
|
10235
|
+
/* @__PURE__ */ jsxRuntime.jsxs("button", { type: "button", className: "ff-repeater__add", onClick: add, children: [
|
|
10236
|
+
"+ ",
|
|
10237
|
+
field.addLabel ?? "Add"
|
|
10238
|
+
] })
|
|
10239
|
+
] });
|
|
10240
|
+
}
|
|
9744
10241
|
function JsonField({ value, onChange, rows }) {
|
|
9745
10242
|
const initial = ReactExports.useMemo(() => {
|
|
9746
10243
|
try {
|
|
@@ -9775,6 +10272,7 @@ function NodeConfigPanel({
|
|
|
9775
10272
|
onChange,
|
|
9776
10273
|
header,
|
|
9777
10274
|
renderCredentialField,
|
|
10275
|
+
renderDocumentField,
|
|
9778
10276
|
className,
|
|
9779
10277
|
style: style2
|
|
9780
10278
|
}) {
|
|
@@ -9800,6 +10298,8 @@ function NodeConfigPanel({
|
|
|
9800
10298
|
const setDescription = (description) => onChange({ ...node, data: { ...node.data, description } });
|
|
9801
10299
|
const setConfigValue = (key, value) => onChange({ ...node, data: { ...node.data, config: { ...config, [key]: value } } });
|
|
9802
10300
|
const issues = validateConfig(kind, config);
|
|
10301
|
+
const adapter2 = getRichInputAdapter();
|
|
10302
|
+
const documentField = renderDocumentField ?? (adapter2?.renderEditor ? ({ value, onChange: set3 }) => adapter2.renderEditor({ value, onChange: set3 }) : void 0);
|
|
9803
10303
|
return /* @__PURE__ */ jsxRuntime.jsxs("aside", { className: ["ff-panel", className ?? ""].filter(Boolean).join(" "), style: style2, children: [
|
|
9804
10304
|
header,
|
|
9805
10305
|
/* @__PURE__ */ jsxRuntime.jsxs("header", { className: "ff-panel__header", children: [
|
|
@@ -9848,7 +10348,8 @@ function NodeConfigPanel({
|
|
|
9848
10348
|
field,
|
|
9849
10349
|
value: config[field.key],
|
|
9850
10350
|
onChange: (v) => setConfigValue(field.key, v),
|
|
9851
|
-
renderCredentialField
|
|
10351
|
+
renderCredentialField,
|
|
10352
|
+
renderDocumentField: documentField
|
|
9852
10353
|
}
|
|
9853
10354
|
)
|
|
9854
10355
|
] }, field.key))
|
|
@@ -9897,6 +10398,29 @@ function useFlowState(initial) {
|
|
|
9897
10398
|
return { nodes, edges, setNodes, setEdges, onNodesChange, onEdgesChange, onConnect, toGraph };
|
|
9898
10399
|
}
|
|
9899
10400
|
|
|
10401
|
+
// src/registry/ports.ts
|
|
10402
|
+
function resolvePortSpec(spec, config) {
|
|
10403
|
+
if (spec === void 0) return void 0;
|
|
10404
|
+
if (typeof spec !== "function") return spec;
|
|
10405
|
+
try {
|
|
10406
|
+
const resolved = spec(config);
|
|
10407
|
+
return Array.isArray(resolved) ? resolved : void 0;
|
|
10408
|
+
} catch {
|
|
10409
|
+
return void 0;
|
|
10410
|
+
}
|
|
10411
|
+
}
|
|
10412
|
+
function nodeConfig(node) {
|
|
10413
|
+
return node.data?.config ?? {};
|
|
10414
|
+
}
|
|
10415
|
+
function resolveNodePorts(node, kind) {
|
|
10416
|
+
const config = nodeConfig(node);
|
|
10417
|
+
const data = node.data;
|
|
10418
|
+
return {
|
|
10419
|
+
inputs: data?.inputs ?? resolvePortSpec(kind?.inputs, config),
|
|
10420
|
+
outputs: data?.outputs ?? resolvePortSpec(kind?.outputs, config)
|
|
10421
|
+
};
|
|
10422
|
+
}
|
|
10423
|
+
|
|
9900
10424
|
// src/runtime/run-flow.ts
|
|
9901
10425
|
async function runFlow(graph, executors, onEvent = () => {
|
|
9902
10426
|
}, options = {}) {
|
|
@@ -10028,8 +10552,9 @@ function activatedPorts(node, result) {
|
|
|
10028
10552
|
return { ports: [r.branch], value: r.value ?? r };
|
|
10029
10553
|
}
|
|
10030
10554
|
}
|
|
10031
|
-
const
|
|
10032
|
-
|
|
10555
|
+
const kind = getNodeKind(node.data?.kind ?? node.type ?? "") ?? void 0;
|
|
10556
|
+
const declared = resolveNodePorts(node, kind).outputs?.map((p) => p.id);
|
|
10557
|
+
return { ports: declared?.length ? declared : ["out"], value: result };
|
|
10033
10558
|
}
|
|
10034
10559
|
|
|
10035
10560
|
// src/runtime/use-flow-run.ts
|
|
@@ -10248,9 +10773,10 @@ function RegistryNodeInner(props) {
|
|
|
10248
10773
|
const data = props.data;
|
|
10249
10774
|
const status = data.status ?? "idle";
|
|
10250
10775
|
const accent = kind.accent ?? categoryAccent(kind.category);
|
|
10251
|
-
const
|
|
10252
|
-
const
|
|
10253
|
-
const
|
|
10776
|
+
const resolved = resolveNodePorts(props, kind);
|
|
10777
|
+
const inputs = resolved.inputs ?? defaultInputs2(kind.category);
|
|
10778
|
+
const outputs = resolved.outputs ?? defaultOutputs2(kind.category);
|
|
10779
|
+
const config = nodeConfig(props);
|
|
10254
10780
|
const label = data.label ?? kind.label;
|
|
10255
10781
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
10256
10782
|
"div",
|
|
@@ -10363,6 +10889,17 @@ function removeEdges(edges, ids) {
|
|
|
10363
10889
|
const doomed = new Set(ids);
|
|
10364
10890
|
return edges.filter((e) => !doomed.has(e.id));
|
|
10365
10891
|
}
|
|
10892
|
+
function setEdgeLabel(edges, id2, label) {
|
|
10893
|
+
const next = label?.trim();
|
|
10894
|
+
return edges.map((e) => {
|
|
10895
|
+
if (e.id !== id2) return e;
|
|
10896
|
+
if (!next) {
|
|
10897
|
+
const { label: _drop, ...rest } = e;
|
|
10898
|
+
return rest;
|
|
10899
|
+
}
|
|
10900
|
+
return { ...e, label: next };
|
|
10901
|
+
});
|
|
10902
|
+
}
|
|
10366
10903
|
function duplicateNode(node, id2, offset = 40) {
|
|
10367
10904
|
return {
|
|
10368
10905
|
...node,
|
|
@@ -10410,6 +10947,7 @@ function FlowEditorInner({
|
|
|
10410
10947
|
onChange,
|
|
10411
10948
|
onSelectionChange,
|
|
10412
10949
|
onDelete,
|
|
10950
|
+
onEdgeDelete,
|
|
10413
10951
|
apiRef
|
|
10414
10952
|
}) {
|
|
10415
10953
|
const internal = useFlowState(initial);
|
|
@@ -10427,18 +10965,39 @@ function FlowEditorInner({
|
|
|
10427
10965
|
const [selectedId, setSelectedId] = ReactExports.useState(null);
|
|
10428
10966
|
const selected2 = ReactExports.useMemo(() => flow.nodes.find((n) => n.id === selectedId) ?? null, [flow.nodes, selectedId]);
|
|
10429
10967
|
ReactExports.useEffect(() => onSelectionChange?.(selected2), [selected2, onSelectionChange]);
|
|
10430
|
-
const
|
|
10968
|
+
const [selectedEdgeId, setSelectedEdgeId] = ReactExports.useState(null);
|
|
10969
|
+
const selectedEdge = ReactExports.useMemo(
|
|
10970
|
+
() => flow.edges.find((e) => e.id === selectedEdgeId) ?? null,
|
|
10971
|
+
[flow.edges, selectedEdgeId]
|
|
10972
|
+
);
|
|
10973
|
+
const handleNodeClick2 = (_e, node) => {
|
|
10974
|
+
setSelectedId(node.id);
|
|
10975
|
+
setSelectedEdgeId(null);
|
|
10976
|
+
};
|
|
10431
10977
|
const [menu, setMenu] = ReactExports.useState(null);
|
|
10978
|
+
const [labelEdit, setLabelEdit] = ReactExports.useState(null);
|
|
10432
10979
|
const closeMenu = ReactExports.useCallback(() => setMenu(null), []);
|
|
10433
10980
|
const handleNodeContextMenu = (event, node) => {
|
|
10434
10981
|
event.preventDefault();
|
|
10435
10982
|
setSelectedId(node.id);
|
|
10436
|
-
|
|
10983
|
+
setSelectedEdgeId(null);
|
|
10984
|
+
setMenu({ x: event.clientX, y: event.clientY, target: { type: "node", id: node.id } });
|
|
10985
|
+
};
|
|
10986
|
+
const handleEdgeClick = (_e, edge) => {
|
|
10987
|
+
setSelectedEdgeId(edge.id);
|
|
10988
|
+
};
|
|
10989
|
+
const handleEdgeContextMenu = (event, edge) => {
|
|
10990
|
+
event.preventDefault();
|
|
10991
|
+
setSelectedEdgeId(edge.id);
|
|
10992
|
+
setMenu({ x: event.clientX, y: event.clientY, target: { type: "edge", id: edge.id } });
|
|
10437
10993
|
};
|
|
10438
10994
|
ReactExports.useEffect(() => {
|
|
10439
|
-
if (menu === null) return;
|
|
10440
|
-
const close = () =>
|
|
10441
|
-
|
|
10995
|
+
if (menu === null && labelEdit === null) return;
|
|
10996
|
+
const close = () => {
|
|
10997
|
+
setMenu(null);
|
|
10998
|
+
setLabelEdit(null);
|
|
10999
|
+
};
|
|
11000
|
+
const onKey = (e) => e.key === "Escape" && close();
|
|
10442
11001
|
window.addEventListener("click", close);
|
|
10443
11002
|
window.addEventListener("scroll", close, true);
|
|
10444
11003
|
window.addEventListener("keydown", onKey);
|
|
@@ -10447,7 +11006,7 @@ function FlowEditorInner({
|
|
|
10447
11006
|
window.removeEventListener("scroll", close, true);
|
|
10448
11007
|
window.removeEventListener("keydown", onKey);
|
|
10449
11008
|
};
|
|
10450
|
-
}, [menu]);
|
|
11009
|
+
}, [menu, labelEdit]);
|
|
10451
11010
|
ReactExports.useEffect(() => {
|
|
10452
11011
|
if (!controlled) onChange?.({ nodes: flow.nodes, edges: flow.edges });
|
|
10453
11012
|
}, [flow.nodes, flow.edges, onChange, controlled]);
|
|
@@ -10490,14 +11049,29 @@ function FlowEditorInner({
|
|
|
10490
11049
|
edges: flow.edges,
|
|
10491
11050
|
selectedId,
|
|
10492
11051
|
selected: selected2,
|
|
11052
|
+
selectedEdgeId,
|
|
11053
|
+
selectedEdge,
|
|
10493
11054
|
running: runner.running,
|
|
10494
11055
|
statuses: runner.statuses,
|
|
10495
11056
|
select: setSelectedId,
|
|
11057
|
+
selectEdge: setSelectedEdgeId,
|
|
10496
11058
|
addNode,
|
|
10497
11059
|
updateNode: (next) => flow.setNodes((all) => all.map((x) => x.id === next.id ? next : x)),
|
|
10498
11060
|
deleteNodes,
|
|
10499
11061
|
deleteSelected: () => deleteNodes(selectedId ? [selectedId] : []),
|
|
10500
|
-
deleteEdges: (ids) =>
|
|
11062
|
+
deleteEdges: (ids) => {
|
|
11063
|
+
if (ids.length === 0) return;
|
|
11064
|
+
flow.setEdges((all) => removeEdges(all, ids));
|
|
11065
|
+
setSelectedEdgeId((cur) => cur !== null && ids.includes(cur) ? null : cur);
|
|
11066
|
+
onEdgeDelete?.(ids);
|
|
11067
|
+
},
|
|
11068
|
+
deleteSelectedEdge: () => {
|
|
11069
|
+
if (!selectedEdgeId) return;
|
|
11070
|
+
flow.setEdges((all) => removeEdges(all, [selectedEdgeId]));
|
|
11071
|
+
setSelectedEdgeId(null);
|
|
11072
|
+
onEdgeDelete?.([selectedEdgeId]);
|
|
11073
|
+
},
|
|
11074
|
+
setEdgeLabel: (id2, label) => flow.setEdges((all) => setEdgeLabel(all, id2, label)),
|
|
10501
11075
|
duplicateNode: (id2) => {
|
|
10502
11076
|
const src = flow.nodes.find((n) => n.id === id2);
|
|
10503
11077
|
if (!src) return null;
|
|
@@ -10569,6 +11143,9 @@ function FlowEditorInner({
|
|
|
10569
11143
|
onConnect: flow.onConnect,
|
|
10570
11144
|
onNodeClick: handleNodeClick2,
|
|
10571
11145
|
onNodeContextMenu: builtins.contextMenu === false ? void 0 : handleNodeContextMenu,
|
|
11146
|
+
onEdgeClick: handleEdgeClick,
|
|
11147
|
+
onEdgeContextMenu: builtins.edgeContextMenu === false ? void 0 : handleEdgeContextMenu,
|
|
11148
|
+
onEdgesDelete: (deleted) => onEdgeDelete?.(deleted.map((e) => e.id)),
|
|
10572
11149
|
onNodesDelete: (deleted) => onDelete?.(deleted.map((n) => n.id)),
|
|
10573
11150
|
deleteKeyCode: ["Delete", "Backspace"],
|
|
10574
11151
|
height: "100%",
|
|
@@ -10577,14 +11154,14 @@ function FlowEditorInner({
|
|
|
10577
11154
|
}
|
|
10578
11155
|
),
|
|
10579
11156
|
slots.empty && api.nodes.length === 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "ff-editor__empty", children: slots.empty(api) }),
|
|
10580
|
-
menu
|
|
11157
|
+
menu?.target.type === "node" && builtins.contextMenu !== false && /* @__PURE__ */ jsxRuntime.jsx(
|
|
10581
11158
|
"div",
|
|
10582
11159
|
{
|
|
10583
11160
|
className: "ff-editor__ctx",
|
|
10584
11161
|
style: { top: menu.y, left: menu.x },
|
|
10585
11162
|
role: "menu",
|
|
10586
11163
|
onClick: (e) => e.stopPropagation(),
|
|
10587
|
-
children: slots.contextMenu ? slots.contextMenu(api, menu.
|
|
11164
|
+
children: slots.contextMenu ? slots.contextMenu(api, menu.target.id, closeMenu) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
10588
11165
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
10589
11166
|
"button",
|
|
10590
11167
|
{
|
|
@@ -10593,7 +11170,7 @@ function FlowEditorInner({
|
|
|
10593
11170
|
className: "ff-editor__ctx-item",
|
|
10594
11171
|
"data-action": "ctx-duplicate",
|
|
10595
11172
|
onClick: () => {
|
|
10596
|
-
api.duplicateNode(menu.
|
|
11173
|
+
api.duplicateNode(menu.target.id);
|
|
10597
11174
|
closeMenu();
|
|
10598
11175
|
},
|
|
10599
11176
|
children: "Duplicate"
|
|
@@ -10607,7 +11184,7 @@ function FlowEditorInner({
|
|
|
10607
11184
|
className: "ff-editor__ctx-item ff-editor__ctx-item--danger",
|
|
10608
11185
|
"data-action": "ctx-delete",
|
|
10609
11186
|
onClick: () => {
|
|
10610
|
-
api.deleteNodes([menu.
|
|
11187
|
+
api.deleteNodes([menu.target.id]);
|
|
10611
11188
|
closeMenu();
|
|
10612
11189
|
},
|
|
10613
11190
|
children: "Delete"
|
|
@@ -10616,6 +11193,58 @@ function FlowEditorInner({
|
|
|
10616
11193
|
] })
|
|
10617
11194
|
}
|
|
10618
11195
|
),
|
|
11196
|
+
menu?.target.type === "edge" && builtins.edgeContextMenu !== false && /* @__PURE__ */ jsxRuntime.jsx(
|
|
11197
|
+
"div",
|
|
11198
|
+
{
|
|
11199
|
+
className: "ff-editor__ctx",
|
|
11200
|
+
style: { top: menu.y, left: menu.x },
|
|
11201
|
+
role: "menu",
|
|
11202
|
+
onClick: (e) => e.stopPropagation(),
|
|
11203
|
+
children: slots.edgeContextMenu ? slots.edgeContextMenu(api, menu.target.id, closeMenu) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
11204
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
11205
|
+
"button",
|
|
11206
|
+
{
|
|
11207
|
+
type: "button",
|
|
11208
|
+
role: "menuitem",
|
|
11209
|
+
className: "ff-editor__ctx-item",
|
|
11210
|
+
"data-action": "ctx-edge-label",
|
|
11211
|
+
onClick: () => {
|
|
11212
|
+
setLabelEdit({ x: menu.x, y: menu.y, edgeId: menu.target.id });
|
|
11213
|
+
setMenu(null);
|
|
11214
|
+
},
|
|
11215
|
+
children: "Label\u2026"
|
|
11216
|
+
}
|
|
11217
|
+
),
|
|
11218
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
11219
|
+
"button",
|
|
11220
|
+
{
|
|
11221
|
+
type: "button",
|
|
11222
|
+
role: "menuitem",
|
|
11223
|
+
className: "ff-editor__ctx-item ff-editor__ctx-item--danger",
|
|
11224
|
+
"data-action": "ctx-edge-delete",
|
|
11225
|
+
onClick: () => {
|
|
11226
|
+
api.deleteEdges([menu.target.id]);
|
|
11227
|
+
closeMenu();
|
|
11228
|
+
},
|
|
11229
|
+
children: "Delete connection"
|
|
11230
|
+
}
|
|
11231
|
+
)
|
|
11232
|
+
] })
|
|
11233
|
+
}
|
|
11234
|
+
),
|
|
11235
|
+
labelEdit !== null && /* @__PURE__ */ jsxRuntime.jsx(
|
|
11236
|
+
EdgeLabelEditor,
|
|
11237
|
+
{
|
|
11238
|
+
x: labelEdit.x,
|
|
11239
|
+
y: labelEdit.y,
|
|
11240
|
+
initial: flow.edges.find((e) => e.id === labelEdit.edgeId)?.label ?? "",
|
|
11241
|
+
onCommit: (text) => {
|
|
11242
|
+
api.setEdgeLabel(labelEdit.edgeId, text);
|
|
11243
|
+
setLabelEdit(null);
|
|
11244
|
+
},
|
|
11245
|
+
onCancel: () => setLabelEdit(null)
|
|
11246
|
+
}
|
|
11247
|
+
),
|
|
10619
11248
|
showFeed && (slots.feed ? slots.feed(api) : /* @__PURE__ */ jsxRuntime.jsx(FlowRunFeed, { entries: runner.feed, className: "ff-editor__feed" }))
|
|
10620
11249
|
] }),
|
|
10621
11250
|
showPanel && (slots.panel ? slots.panel(api) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ff-editor__panel-wrap", children: [
|
|
@@ -10692,6 +11321,46 @@ function makeControlledFlowAdapter(value, onChange) {
|
|
|
10692
11321
|
toGraph: () => value
|
|
10693
11322
|
};
|
|
10694
11323
|
}
|
|
11324
|
+
function EdgeLabelEditor({
|
|
11325
|
+
x,
|
|
11326
|
+
y,
|
|
11327
|
+
initial,
|
|
11328
|
+
onCommit,
|
|
11329
|
+
onCancel
|
|
11330
|
+
}) {
|
|
11331
|
+
const [text, setText] = ReactExports.useState(initial);
|
|
11332
|
+
const ref = ReactExports.useRef(null);
|
|
11333
|
+
ReactExports.useEffect(() => {
|
|
11334
|
+
ref.current?.focus();
|
|
11335
|
+
ref.current?.select();
|
|
11336
|
+
}, []);
|
|
11337
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
11338
|
+
"div",
|
|
11339
|
+
{
|
|
11340
|
+
className: "ff-editor__ctx ff-editor__edge-label",
|
|
11341
|
+
style: { top: y, left: x },
|
|
11342
|
+
onClick: (e) => e.stopPropagation(),
|
|
11343
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
11344
|
+
"input",
|
|
11345
|
+
{
|
|
11346
|
+
ref,
|
|
11347
|
+
className: "ff-panel__input",
|
|
11348
|
+
value: text,
|
|
11349
|
+
placeholder: "Label this connection",
|
|
11350
|
+
"aria-label": "Connection label",
|
|
11351
|
+
"data-action": "edge-label-input",
|
|
11352
|
+
onChange: (e) => setText(e.target.value),
|
|
11353
|
+
onKeyDown: (e) => {
|
|
11354
|
+
e.stopPropagation();
|
|
11355
|
+
if (e.key === "Enter") onCommit(text);
|
|
11356
|
+
if (e.key === "Escape") onCancel();
|
|
11357
|
+
},
|
|
11358
|
+
onBlur: () => onCommit(text)
|
|
11359
|
+
}
|
|
11360
|
+
)
|
|
11361
|
+
}
|
|
11362
|
+
);
|
|
11363
|
+
}
|
|
10695
11364
|
function defineNode(render) {
|
|
10696
11365
|
function Wrapped(props) {
|
|
10697
11366
|
return render({
|
|
@@ -10741,6 +11410,7 @@ exports.NodeShell = NodeShell;
|
|
|
10741
11410
|
exports.NoteNode = NoteNode;
|
|
10742
11411
|
exports.OutputNode = OutputNode2;
|
|
10743
11412
|
exports.RegistryNode = RegistryNode;
|
|
11413
|
+
exports.RichInputPreview = RichInputPreview;
|
|
10744
11414
|
exports.SubgraphNode = SubgraphNode;
|
|
10745
11415
|
exports.TriggerNode = TriggerNode;
|
|
10746
11416
|
exports.WORKFLOW_SCHEMA_URL = WORKFLOW_SCHEMA_URL;
|
|
@@ -10753,12 +11423,18 @@ exports.defaultNodeTypes = defaultNodeTypes;
|
|
|
10753
11423
|
exports.defineNode = defineNode;
|
|
10754
11424
|
exports.exportWorkflow = exportWorkflow;
|
|
10755
11425
|
exports.getNodeKind = getNodeKind;
|
|
11426
|
+
exports.getRichInputAdapter = getRichInputAdapter;
|
|
10756
11427
|
exports.importWorkflow = importWorkflow;
|
|
11428
|
+
exports.isRichInputEnabled = isRichInputEnabled;
|
|
10757
11429
|
exports.listNodeKinds = listNodeKinds;
|
|
10758
11430
|
exports.onNodeKindsChanged = onNodeKindsChanged;
|
|
11431
|
+
exports.onRichInputAdapterChanged = onRichInputAdapterChanged;
|
|
10759
11432
|
exports.paletteDropHandlers = paletteDropHandlers;
|
|
10760
11433
|
exports.registerBuiltinKinds = registerBuiltinKinds;
|
|
10761
11434
|
exports.registerNodeKind = registerNodeKind;
|
|
11435
|
+
exports.registerRichInputAdapter = registerRichInputAdapter;
|
|
11436
|
+
exports.resolveNodePorts = resolveNodePorts;
|
|
11437
|
+
exports.resolvePortSpec = resolvePortSpec;
|
|
10762
11438
|
exports.runFlow = runFlow;
|
|
10763
11439
|
exports.useFlowEditor = useFlowEditor;
|
|
10764
11440
|
exports.useFlowEditorOptional = useFlowEditorOptional;
|