@burdenoff/fluidgrids-fe-sdk 2026.1014.5 → 2026.1014.7
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 +30 -0
- package/dist/{WorkflowBuilder-QFTEKT37.js → WorkflowBuilder-UKAXUXVZ.js} +2 -2
- package/dist/{chunk-6JDHLCAQ.js → chunk-2FF56EBP.js} +1835 -1564
- package/dist/{chunk-RXIRAKAC.js → chunk-2M3QGGZU.js} +1 -1
- package/dist/{chunk-LK44C6IE.js → chunk-PGCKQMR5.js} +1 -1
- package/dist/{chunk-XKEUZUKM.js → chunk-VPKVWNZ7.js} +1 -1
- package/dist/index.d.ts +2 -17
- package/dist/index.js +7 -7
- package/dist/templates.js +2 -2
- package/dist/workflow-builder.js +3 -3
- package/package.json +1 -1
|
@@ -16,7 +16,7 @@ import { useTr, FluidGridsTranslationProvider, ENGLISH_FALLBACK_TRANSLATOR } fro
|
|
|
16
16
|
import { waitForFluidGridsClient, getFluidGridsClient, getFluidGridsClientSafe, createFluidGridsClient, setFluidGridsClient, resetFluidGridsClient } from './chunk-NTWG3CJX.js';
|
|
17
17
|
import * as React2 from 'react';
|
|
18
18
|
import React2__default, { forwardRef, useState, useImperativeHandle, useRef, useEffect, useMemo, memo, createContext, lazy, useCallback, useSyncExternalStore, useContext, useLayoutEffect, Suspense, useDeferredValue, useId } from 'react';
|
|
19
|
-
import { Textarea, Input, Button, Card, Badge, CardContent as CardContent$1,
|
|
19
|
+
import { Textarea, Input, Button, Card, Badge, CardContent as CardContent$1, Dialog, DialogTrigger, DialogContent, DialogHeader, DialogTitle, DialogDescription, DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuLabel, DropdownMenuItem, DropdownMenuSeparator, Sheet, SheetTrigger, SheetContent, SheetHeader, SheetTitle, TooltipProvider, AlertDialog, AlertDialogContent, AlertDialogHeader, AlertDialogTitle, AlertDialogDescription, AlertDialogFooter, AlertDialogCancel, AlertDialogAction, Popover, PopoverTrigger, PopoverContent, Tooltip, TooltipTrigger, TooltipContent, Toaster, Tabs, TabsList, TabsTrigger, TabsContent, CardHeader as CardHeader$1, CardTitle as CardTitle$1, CardDescription as CardDescription$1, ScrollArea, Label, Select, SelectTrigger, SelectValue, SelectContent, SelectItem } from '@burdenoff/fe-libs/ui';
|
|
20
20
|
import { cn } from '@burdenoff/fe-libs/utils';
|
|
21
21
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
22
22
|
import { Icon } from '@iconify/react';
|
|
@@ -981,8 +981,8 @@ function isAdaptiveCard(data) {
|
|
|
981
981
|
return data.type === "AdaptiveCard";
|
|
982
982
|
}
|
|
983
983
|
function isCustomFormat(data) {
|
|
984
|
-
const
|
|
985
|
-
return
|
|
984
|
+
const record2 = data;
|
|
985
|
+
return record2.type !== "AdaptiveCard" && "inputs" in record2;
|
|
986
986
|
}
|
|
987
987
|
function expandTemplate(obj, data) {
|
|
988
988
|
if (typeof obj === "string") {
|
|
@@ -1959,6 +1959,30 @@ function ConnectionFormDialog({
|
|
|
1959
1959
|
}
|
|
1960
1960
|
);
|
|
1961
1961
|
}
|
|
1962
|
+
|
|
1963
|
+
// src/modules/connections/utils/adapter-selection.ts
|
|
1964
|
+
function matchesAdapterProvider(adapter, providerKey) {
|
|
1965
|
+
const normalizedKey = providerKey.trim().toLowerCase();
|
|
1966
|
+
return normalizedKey.length > 0 && (adapter.provider.trim().toLowerCase() === normalizedKey || adapter.name.trim().toLowerCase() === normalizedKey);
|
|
1967
|
+
}
|
|
1968
|
+
function connectionsForAdapter(connections, adapterName) {
|
|
1969
|
+
return connections.filter(
|
|
1970
|
+
(connection) => connection.adapter.toLowerCase() === adapterName.toLowerCase()
|
|
1971
|
+
);
|
|
1972
|
+
}
|
|
1973
|
+
function replaceAdapterConnections(connections, adapterName, connectionIds = []) {
|
|
1974
|
+
const otherConnections = connections.filter(
|
|
1975
|
+
(connection) => connection.adapter.toLowerCase() !== adapterName.toLowerCase()
|
|
1976
|
+
);
|
|
1977
|
+
const uniqueIds = [...new Set(connectionIds.filter(Boolean))];
|
|
1978
|
+
return [
|
|
1979
|
+
...otherConnections,
|
|
1980
|
+
...uniqueIds.map((connectionId) => ({
|
|
1981
|
+
connectionId,
|
|
1982
|
+
adapter: adapterName
|
|
1983
|
+
}))
|
|
1984
|
+
];
|
|
1985
|
+
}
|
|
1962
1986
|
function formatAge(dateString) {
|
|
1963
1987
|
try {
|
|
1964
1988
|
return formatDistanceToNow(new Date(dateString), { addSuffix: true });
|
|
@@ -2004,6 +2028,7 @@ function ConnectionChip({
|
|
|
2004
2028
|
connectionData,
|
|
2005
2029
|
onRemove
|
|
2006
2030
|
}) {
|
|
2031
|
+
const tr = useTr();
|
|
2007
2032
|
const name = connectionData?.name || connection.connectionId;
|
|
2008
2033
|
const status = connectionData?.status;
|
|
2009
2034
|
return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 px-2 py-1 bg-accent/30 rounded-md text-sm group", children: [
|
|
@@ -2031,6 +2056,10 @@ function ConnectionChip({
|
|
|
2031
2056
|
"button",
|
|
2032
2057
|
{
|
|
2033
2058
|
type: "button",
|
|
2059
|
+
"aria-label": tr(
|
|
2060
|
+
"fluidgrids.connections.connectionSelector.removeConnection",
|
|
2061
|
+
"Remove connection"
|
|
2062
|
+
),
|
|
2034
2063
|
onClick: (e) => {
|
|
2035
2064
|
e.stopPropagation();
|
|
2036
2065
|
onRemove();
|
|
@@ -2041,46 +2070,57 @@ function ConnectionChip({
|
|
|
2041
2070
|
)
|
|
2042
2071
|
] });
|
|
2043
2072
|
}
|
|
2044
|
-
function ConnectionSelector({
|
|
2073
|
+
function ConnectionSelector(props) {
|
|
2074
|
+
const connectionList = useConnections();
|
|
2075
|
+
if (props.adapters.length <= 1) {
|
|
2076
|
+
return /* @__PURE__ */ jsx(AdapterConnectionSelector, { ...props, connectionList });
|
|
2077
|
+
}
|
|
2078
|
+
return /* @__PURE__ */ jsx("div", { className: props.compact ? "space-y-2" : "space-y-3", children: props.adapters.map((adapter) => /* @__PURE__ */ jsxs("fieldset", { className: "min-w-0 space-y-1.5", children: [
|
|
2079
|
+
/* @__PURE__ */ jsx("legend", { className: "text-xs font-medium text-text-secondary", children: adapter.name }),
|
|
2080
|
+
/* @__PURE__ */ jsx(
|
|
2081
|
+
AdapterConnectionSelector,
|
|
2082
|
+
{
|
|
2083
|
+
...props,
|
|
2084
|
+
adapters: [adapter],
|
|
2085
|
+
selectedConnections: connectionsForAdapter(
|
|
2086
|
+
props.selectedConnections,
|
|
2087
|
+
adapter.name
|
|
2088
|
+
),
|
|
2089
|
+
onChange: (connections) => props.onChange(
|
|
2090
|
+
replaceAdapterConnections(
|
|
2091
|
+
props.selectedConnections,
|
|
2092
|
+
adapter.name,
|
|
2093
|
+
connections.map((connection) => connection.connectionId)
|
|
2094
|
+
)
|
|
2095
|
+
),
|
|
2096
|
+
showRequired: false,
|
|
2097
|
+
connectionList
|
|
2098
|
+
}
|
|
2099
|
+
)
|
|
2100
|
+
] }, adapter.name)) });
|
|
2101
|
+
}
|
|
2102
|
+
function AdapterConnectionSelector({
|
|
2045
2103
|
adapters,
|
|
2046
2104
|
selectedConnections,
|
|
2047
2105
|
onChange,
|
|
2048
2106
|
multiple = false,
|
|
2049
2107
|
showStatus = true,
|
|
2050
|
-
compact = false
|
|
2108
|
+
compact = false,
|
|
2109
|
+
showRequired = true,
|
|
2110
|
+
connectionList
|
|
2051
2111
|
}) {
|
|
2052
2112
|
const tr = useTr();
|
|
2053
2113
|
const [isDropdownOpen, setIsDropdownOpen] = useState(false);
|
|
2054
2114
|
const [dialogOpen, setDialogOpen] = useState(false);
|
|
2055
2115
|
const [dialogProviderKey, setDialogProviderKey] = useState();
|
|
2056
|
-
const {
|
|
2057
|
-
connections,
|
|
2058
|
-
loading: connectionsLoading,
|
|
2059
|
-
refetch
|
|
2060
|
-
} = useConnections();
|
|
2116
|
+
const { connections, loading: connectionsLoading, refetch } = connectionList;
|
|
2061
2117
|
const availableConnections = useMemo(() => {
|
|
2062
2118
|
if (!adapters || adapters.length === 0) return [];
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
return connections.filter((conn) => {
|
|
2069
|
-
const providerKey = conn.provider?.key?.toLowerCase() || "";
|
|
2070
|
-
const category = conn.provider?.category?.toLowerCase() || "";
|
|
2071
|
-
if (adapterIdentifiers.has(providerKey)) {
|
|
2072
|
-
return true;
|
|
2073
|
-
}
|
|
2074
|
-
for (const identifier of adapterIdentifiers) {
|
|
2075
|
-
if (providerKey.includes(identifier) || identifier.includes(providerKey)) {
|
|
2076
|
-
return true;
|
|
2077
|
-
}
|
|
2078
|
-
if (category.includes(identifier)) {
|
|
2079
|
-
return true;
|
|
2080
|
-
}
|
|
2081
|
-
}
|
|
2082
|
-
return false;
|
|
2083
|
-
});
|
|
2119
|
+
return connections.filter(
|
|
2120
|
+
(connection) => adapters.some(
|
|
2121
|
+
(adapter) => matchesAdapterProvider(adapter, connection.provider?.key ?? "")
|
|
2122
|
+
)
|
|
2123
|
+
);
|
|
2084
2124
|
}, [connections, adapters]);
|
|
2085
2125
|
const mergedConnections = useMemo(() => {
|
|
2086
2126
|
const allConnections = [...availableConnections];
|
|
@@ -2114,11 +2154,12 @@ function ConnectionSelector({
|
|
|
2114
2154
|
(connection) => {
|
|
2115
2155
|
const providerKey = connection.provider?.key || "";
|
|
2116
2156
|
const adapter = adapters.find(
|
|
2117
|
-
(
|
|
2157
|
+
(candidate) => matchesAdapterProvider(candidate, providerKey)
|
|
2118
2158
|
);
|
|
2159
|
+
if (!adapter) return;
|
|
2119
2160
|
const newSelection = {
|
|
2120
2161
|
connectionId: connection.id,
|
|
2121
|
-
adapter: adapter
|
|
2162
|
+
adapter: adapter.name
|
|
2122
2163
|
};
|
|
2123
2164
|
if (multiple) {
|
|
2124
2165
|
const isSelected = selectedConnections.some(
|
|
@@ -2155,7 +2196,7 @@ function ConnectionSelector({
|
|
|
2155
2196
|
(conn) => {
|
|
2156
2197
|
const providerKey = conn.provider?.key || "";
|
|
2157
2198
|
const adapter = adapters.find(
|
|
2158
|
-
(
|
|
2199
|
+
(candidate) => matchesAdapterProvider(candidate, providerKey)
|
|
2159
2200
|
);
|
|
2160
2201
|
if (adapter) {
|
|
2161
2202
|
const newSelection = { connectionId: conn.id, adapter: adapter.name };
|
|
@@ -2285,7 +2326,10 @@ function ConnectionSelector({
|
|
|
2285
2326
|
return /* @__PURE__ */ jsxs(
|
|
2286
2327
|
DropdownMenuItem,
|
|
2287
2328
|
{
|
|
2288
|
-
|
|
2329
|
+
onSelect: (event) => {
|
|
2330
|
+
if (multiple) event.preventDefault();
|
|
2331
|
+
handleSelect(conn);
|
|
2332
|
+
},
|
|
2289
2333
|
className: `flex items-center justify-between cursor-pointer ${isSelected ? "bg-action-primary-bg/10" : ""} ${!isActive ? "opacity-60" : ""}`,
|
|
2290
2334
|
children: [
|
|
2291
2335
|
/* @__PURE__ */ jsxs("div", { className: "flex flex-col min-w-0", children: [
|
|
@@ -2366,7 +2410,7 @@ function ConnectionSelector({
|
|
|
2366
2410
|
}
|
|
2367
2411
|
)
|
|
2368
2412
|
] }),
|
|
2369
|
-
showStatus && selectedConnections.length === 0 && adapters.length > 0 && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 text-xs text-[var(--color-status-warning-text)]", children: [
|
|
2413
|
+
showStatus && showRequired && selectedConnections.length === 0 && adapters.length > 0 && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 text-xs text-[var(--color-status-warning-text)]", children: [
|
|
2370
2414
|
/* @__PURE__ */ jsx(Icon, { icon: "mdi:alert-circle", className: "w-3.5 h-3.5" }),
|
|
2371
2415
|
/* @__PURE__ */ jsx("span", { children: tr(
|
|
2372
2416
|
"fluidgrids.connections.connectionSelector.connectionRequired",
|
|
@@ -2454,1404 +2498,1100 @@ function isNodeTrigger(node) {
|
|
|
2454
2498
|
return false;
|
|
2455
2499
|
}
|
|
2456
2500
|
|
|
2457
|
-
// src/
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
|
|
2501
|
+
// src/builder/lib/field-schema.ts
|
|
2502
|
+
function isRecord3(value) {
|
|
2503
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2504
|
+
}
|
|
2505
|
+
function isIdentifier(key) {
|
|
2506
|
+
return /^[A-Za-z_$][\w$]*$/.test(key);
|
|
2507
|
+
}
|
|
2508
|
+
function joinPath(base, key) {
|
|
2509
|
+
if (isIdentifier(key)) return base ? `${base}.${key}` : key;
|
|
2510
|
+
const bracket = `[${JSON.stringify(key)}]`;
|
|
2511
|
+
return base ? `${base}${bracket}` : bracket;
|
|
2512
|
+
}
|
|
2513
|
+
function pathToExpression(path) {
|
|
2514
|
+
if (!path) return "${output}";
|
|
2515
|
+
const accessor = path.startsWith("[") ? `output${path}` : `output.${path}`;
|
|
2516
|
+
return "${" + accessor + "}";
|
|
2517
|
+
}
|
|
2518
|
+
function fieldFromSpec(key, path, spec) {
|
|
2519
|
+
if (typeof spec === "string") {
|
|
2520
|
+
return { key, path, type: spec };
|
|
2474
2521
|
}
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2522
|
+
if (isRecord3(spec)) {
|
|
2523
|
+
const declaredType2 = typeof spec.type === "string" ? spec.type : void 0;
|
|
2524
|
+
const description = typeof spec.description === "string" ? spec.description : void 0;
|
|
2525
|
+
const required = spec.required === true;
|
|
2526
|
+
if (isRecord3(spec.properties)) {
|
|
2527
|
+
const children = Object.entries(spec.properties).map(
|
|
2528
|
+
([k, v]) => fieldFromSpec(k, joinPath(path, k), v)
|
|
2529
|
+
);
|
|
2530
|
+
return { key, path, type: "object", required, description, children };
|
|
2479
2531
|
}
|
|
2480
|
-
if (
|
|
2481
|
-
const
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2532
|
+
if (spec.items !== void 0) {
|
|
2533
|
+
const element = fieldFromSpec("0", `${path}[0]`, spec.items);
|
|
2534
|
+
return {
|
|
2535
|
+
key,
|
|
2536
|
+
path,
|
|
2537
|
+
type: "array",
|
|
2538
|
+
required,
|
|
2539
|
+
description,
|
|
2540
|
+
children: [element]
|
|
2541
|
+
};
|
|
2487
2542
|
}
|
|
2543
|
+
return { key, path, type: declaredType2 ?? "any", required, description };
|
|
2488
2544
|
}
|
|
2489
|
-
return
|
|
2545
|
+
return { key, path, type: "any" };
|
|
2490
2546
|
}
|
|
2491
|
-
function
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
const loadRequestRef = useRef(0);
|
|
2496
|
-
const workflow = useFlowStore((s) => s.workflow);
|
|
2497
|
-
const startExecution = useFlowStore(
|
|
2498
|
-
(s) => s.actions.execution.startExecution
|
|
2499
|
-
);
|
|
2500
|
-
const setRunMetadata = useFlowStore(
|
|
2501
|
-
(s) => s.actions.execution.setRunMetadata
|
|
2502
|
-
);
|
|
2503
|
-
const setStepPlan = useFlowStore((s) => s.actions.execution.setStepPlan);
|
|
2504
|
-
const addLog = useFlowStore((s) => s.actions.execution.addLog);
|
|
2505
|
-
const updateLog = useFlowStore((s) => s.actions.execution.updateLog);
|
|
2506
|
-
const setNodeStatus = useFlowStore((s) => s.actions.execution.setNodeStatus);
|
|
2507
|
-
const setNodeItemCount = useFlowStore(
|
|
2508
|
-
(s) => s.actions.execution.setNodeItemCount
|
|
2547
|
+
function fieldsFromIO(io, root = "") {
|
|
2548
|
+
if (!isRecord3(io)) return [];
|
|
2549
|
+
return Object.entries(io).map(
|
|
2550
|
+
([key, spec]) => fieldFromSpec(key, joinPath(root, key), spec)
|
|
2509
2551
|
);
|
|
2510
|
-
|
|
2511
|
-
|
|
2552
|
+
}
|
|
2553
|
+
function typeOfValue(value) {
|
|
2554
|
+
if (value === null || value === void 0) return "any";
|
|
2555
|
+
if (Array.isArray(value)) return "array";
|
|
2556
|
+
if (typeof value === "object") return "object";
|
|
2557
|
+
if (typeof value === "number") return "number";
|
|
2558
|
+
if (typeof value === "boolean") return "boolean";
|
|
2559
|
+
if (typeof value === "string") return "string";
|
|
2560
|
+
return "any";
|
|
2561
|
+
}
|
|
2562
|
+
function fieldFromValue(key, path, value) {
|
|
2563
|
+
if (Array.isArray(value)) {
|
|
2564
|
+
const children = value.length > 0 ? [fieldFromValue("0", `${path}[0]`, value[0])] : void 0;
|
|
2565
|
+
return { key, path, type: "array", children };
|
|
2566
|
+
}
|
|
2567
|
+
if (isRecord3(value)) {
|
|
2568
|
+
const children = Object.entries(value).map(
|
|
2569
|
+
([k, v]) => fieldFromValue(k, joinPath(path, k), v)
|
|
2570
|
+
);
|
|
2571
|
+
return { key, path, type: "object", children };
|
|
2572
|
+
}
|
|
2573
|
+
return { key, path, type: typeOfValue(value) };
|
|
2574
|
+
}
|
|
2575
|
+
function fieldsFromValue(value, root = "") {
|
|
2576
|
+
if (!isRecord3(value)) return [];
|
|
2577
|
+
return Object.entries(value).map(
|
|
2578
|
+
([key, v]) => fieldFromValue(key, joinPath(root, key), v)
|
|
2512
2579
|
);
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
const
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
|
|
2524
|
-
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2580
|
+
}
|
|
2581
|
+
function declaredOutputFields(nodeType, op) {
|
|
2582
|
+
const name = op?.name;
|
|
2583
|
+
if (!name || nodeType?.startsWith("internal:")) {
|
|
2584
|
+
return fieldsFromIO(op?.output_fields);
|
|
2585
|
+
}
|
|
2586
|
+
const children = fieldsFromIO(op?.output_fields, name);
|
|
2587
|
+
if (children.length === 0) return [];
|
|
2588
|
+
return [{ key: name, path: name, type: "object", children }];
|
|
2589
|
+
}
|
|
2590
|
+
function normalizeOperations(raw) {
|
|
2591
|
+
if (Array.isArray(raw)) return raw;
|
|
2592
|
+
if (isRecord3(raw) && Array.isArray(raw.operations)) {
|
|
2593
|
+
return raw.operations;
|
|
2594
|
+
}
|
|
2595
|
+
return [];
|
|
2596
|
+
}
|
|
2597
|
+
function resolveNodeOperation(data, fallbackOperations2) {
|
|
2598
|
+
const nodeData = isRecord3(data?.nodeData) ? data?.nodeData : void 0;
|
|
2599
|
+
let operations = normalizeOperations(nodeData?.operations);
|
|
2600
|
+
if (operations.length === 0) {
|
|
2601
|
+
operations = normalizeOperations(data?.operations);
|
|
2602
|
+
}
|
|
2603
|
+
if (operations.length === 0 && fallbackOperations2 !== void 0) {
|
|
2604
|
+
operations = normalizeOperations(fallbackOperations2);
|
|
2605
|
+
}
|
|
2606
|
+
const opName = typeof data?.operation_name === "string" ? data.operation_name : void 0;
|
|
2607
|
+
const selected = operations.find((op) => op.name === opName) ?? operations[0];
|
|
2608
|
+
return { operations, selected };
|
|
2609
|
+
}
|
|
2610
|
+
|
|
2611
|
+
// src/builder/lib/parameter-values.ts
|
|
2612
|
+
function isRecord4(value) {
|
|
2613
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2614
|
+
}
|
|
2615
|
+
function record(value) {
|
|
2616
|
+
return isRecord4(value) ? value : void 0;
|
|
2617
|
+
}
|
|
2618
|
+
function declaredType(schema) {
|
|
2619
|
+
const type = typeof schema === "string" ? schema : record(schema)?.type;
|
|
2620
|
+
return typeof type === "string" ? type.toLowerCase().trim() : "";
|
|
2621
|
+
}
|
|
2622
|
+
function structuredParameterType(schema) {
|
|
2623
|
+
const type = declaredType(schema);
|
|
2624
|
+
if (type === "array" || /^array<.+>$/.test(type)) return "array";
|
|
2625
|
+
if (type === "object") return "object";
|
|
2626
|
+
return void 0;
|
|
2627
|
+
}
|
|
2628
|
+
function itemSchema(schema) {
|
|
2629
|
+
return record(schema)?.items ?? declaredType(schema).match(/^array<(.+)>$/)?.[1];
|
|
2630
|
+
}
|
|
2631
|
+
function matchesSchema(value, schema) {
|
|
2632
|
+
const type = declaredType(schema);
|
|
2633
|
+
const structured = structuredParameterType(schema);
|
|
2634
|
+
if (structured === "array") {
|
|
2635
|
+
return Array.isArray(value) && value.every((item) => matchesSchema(item, itemSchema(schema)));
|
|
2636
|
+
}
|
|
2637
|
+
if (structured === "object") {
|
|
2638
|
+
const object = record(value);
|
|
2639
|
+
if (!object) return false;
|
|
2640
|
+
const definition = record(schema);
|
|
2641
|
+
const required = definition?.required;
|
|
2642
|
+
if (Array.isArray(required) && required.some(
|
|
2643
|
+
(key) => typeof key === "string" && !Object.hasOwn(object, key)
|
|
2644
|
+
)) {
|
|
2645
|
+
return false;
|
|
2528
2646
|
}
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
(node) => !nodesWithIncomingEdges.has(node.id)
|
|
2647
|
+
return Object.entries(record(definition?.properties) ?? {}).every(
|
|
2648
|
+
([key, property]) => Object.hasOwn(object, key) ? matchesSchema(object[key], property) : record(property)?.required !== true
|
|
2532
2649
|
);
|
|
2533
|
-
|
|
2534
|
-
|
|
2650
|
+
}
|
|
2651
|
+
if (type === "string") return typeof value === "string";
|
|
2652
|
+
if (type === "number")
|
|
2653
|
+
return typeof value === "number" && Number.isFinite(value);
|
|
2654
|
+
if (type === "integer")
|
|
2655
|
+
return typeof value === "number" && Number.isInteger(value);
|
|
2656
|
+
if (type === "boolean") return typeof value === "boolean";
|
|
2657
|
+
if (type === "null") return value === null;
|
|
2658
|
+
return true;
|
|
2659
|
+
}
|
|
2660
|
+
function parseParameterValue(schema, value) {
|
|
2661
|
+
const type = structuredParameterType(schema);
|
|
2662
|
+
if (!type) return { valid: true, value };
|
|
2663
|
+
if (value === void 0 || value === null || typeof value === "string" && !value.trim()) {
|
|
2664
|
+
return { valid: true, value: void 0 };
|
|
2665
|
+
}
|
|
2666
|
+
let parsed = value;
|
|
2667
|
+
if (typeof value === "string") {
|
|
2668
|
+
const text = value.trim();
|
|
2669
|
+
const elementType = declaredType(itemSchema(schema));
|
|
2670
|
+
const canSplit = type === "array" && (!elementType || elementType === "string");
|
|
2671
|
+
if (canSplit && !["[", "{", '"', "]", "}"].includes(text[0])) {
|
|
2672
|
+
parsed = text.split(",").map((item) => item.trim()).filter(Boolean);
|
|
2673
|
+
} else {
|
|
2674
|
+
try {
|
|
2675
|
+
parsed = JSON.parse(text);
|
|
2676
|
+
} catch {
|
|
2677
|
+
return { valid: false, error: `Enter valid JSON for this ${type}.` };
|
|
2678
|
+
}
|
|
2535
2679
|
}
|
|
2536
|
-
|
|
2537
|
-
|
|
2680
|
+
}
|
|
2681
|
+
if (type === "array" ? !Array.isArray(parsed) : !record(parsed)) {
|
|
2682
|
+
return { valid: false, error: `Expected a JSON ${type}.` };
|
|
2683
|
+
}
|
|
2684
|
+
if (!matchesSchema(parsed, schema)) {
|
|
2685
|
+
return {
|
|
2686
|
+
valid: false,
|
|
2687
|
+
error: "The value does not match the declared field types."
|
|
2688
|
+
};
|
|
2689
|
+
}
|
|
2690
|
+
return { valid: true, value: parsed };
|
|
2691
|
+
}
|
|
2692
|
+
function formatParameterValue(value) {
|
|
2693
|
+
if (value === void 0 || value === null) return "";
|
|
2694
|
+
return typeof value === "string" ? value : JSON.stringify(value, null, 2);
|
|
2695
|
+
}
|
|
2696
|
+
function normalizeNodeParameters(data, fallbackOperations2, mappedFields = /* @__PURE__ */ new Set()) {
|
|
2697
|
+
const operation = resolveNodeOperation(data, fallbackOperations2).selected;
|
|
2698
|
+
const fields = record(operation?.input_fields);
|
|
2699
|
+
const args = record(data.args);
|
|
2700
|
+
const state = record(args?.state) ?? {};
|
|
2701
|
+
if (!fields) return data;
|
|
2702
|
+
const structuredFields = Object.entries(fields).filter(
|
|
2703
|
+
([name, schema]) => structuredParameterType(schema) && !mappedFields.has(name)
|
|
2704
|
+
);
|
|
2705
|
+
if (structuredFields.length === 0) return data;
|
|
2706
|
+
const normalized = new Map(Object.entries(state));
|
|
2707
|
+
for (const [name, schema] of structuredFields) {
|
|
2708
|
+
const result = parseParameterValue(
|
|
2709
|
+
schema,
|
|
2710
|
+
Object.hasOwn(state, name) ? state[name] : void 0
|
|
2711
|
+
);
|
|
2712
|
+
if (!result.valid) throw new Error(`${name}: ${result.error}`);
|
|
2713
|
+
if (result.value === void 0) normalized.delete(name);
|
|
2714
|
+
else normalized.set(name, result.value);
|
|
2715
|
+
}
|
|
2716
|
+
return { ...data, args: { ...args, state: Object.fromEntries(normalized) } };
|
|
2717
|
+
}
|
|
2718
|
+
function normalizeWorkflowParameters(nodes, edges, fallbackOperations2) {
|
|
2719
|
+
return nodes.map((node) => {
|
|
2720
|
+
if (node.data.disabled === true) return node;
|
|
2721
|
+
const mappedFields = new Set(
|
|
2722
|
+
edges.filter((edge) => edge.target === node.id).flatMap(
|
|
2723
|
+
(edge) => Object.entries(
|
|
2724
|
+
record(record(record(edge)?.mapping)?.definition) ?? {}
|
|
2725
|
+
).filter(
|
|
2726
|
+
([, definition]) => typeof definition === "string" ? definition.trim().length > 0 : definition !== void 0
|
|
2727
|
+
).map(([name]) => name)
|
|
2728
|
+
)
|
|
2729
|
+
);
|
|
2730
|
+
try {
|
|
2731
|
+
return {
|
|
2732
|
+
...node,
|
|
2733
|
+
data: normalizeNodeParameters(
|
|
2734
|
+
node.data,
|
|
2735
|
+
fallbackOperations2(node.type),
|
|
2736
|
+
mappedFields
|
|
2737
|
+
)
|
|
2738
|
+
};
|
|
2739
|
+
} catch (error) {
|
|
2740
|
+
throw new Error(
|
|
2741
|
+
`Node ${node.id}: ${error instanceof Error ? error.message : "Invalid parameter value"}`
|
|
2742
|
+
);
|
|
2538
2743
|
}
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
const
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
const
|
|
2547
|
-
|
|
2744
|
+
});
|
|
2745
|
+
}
|
|
2746
|
+
function useDeleteNode() {
|
|
2747
|
+
const { getNode, getEdges, deleteElements } = useReactFlow();
|
|
2748
|
+
const deleteNodes = useFlowStore((state) => state.actions.nodes.deleteNodes);
|
|
2749
|
+
return useCallback(
|
|
2750
|
+
(id) => {
|
|
2751
|
+
const node = getNode(id);
|
|
2752
|
+
if (!node) return;
|
|
2753
|
+
const edges = getEdges();
|
|
2754
|
+
const connectedEdges = getConnectedEdges([node], edges);
|
|
2755
|
+
deleteElements({ nodes: [node], edges: connectedEdges }).then();
|
|
2756
|
+
deleteNodes([node]);
|
|
2548
2757
|
},
|
|
2549
|
-
[
|
|
2758
|
+
[deleteElements, getEdges, getNode, deleteNodes]
|
|
2550
2759
|
);
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
|
|
2582
|
-
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
|
|
2592
|
-
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
|
|
2604
|
-
|
|
2760
|
+
}
|
|
2761
|
+
|
|
2762
|
+
// src/builder/contants/symbols.ts
|
|
2763
|
+
var NODE_TYPE_DRAG_DATA_FORMAT = "application/flow-builder.node-type";
|
|
2764
|
+
var NODE_OPERATION_DRAG_DATA_FORMAT = "application/flow-builder.node-operation";
|
|
2765
|
+
var STICKY_NOTE_TYPE = "sticky-note";
|
|
2766
|
+
var STICKY_COLORS = {
|
|
2767
|
+
yellow: "#F2C94C",
|
|
2768
|
+
orange: "#EB8934",
|
|
2769
|
+
blue: "#3B82F6",
|
|
2770
|
+
green: "#10B981",
|
|
2771
|
+
red: "#EF4444",
|
|
2772
|
+
purple: "#8B5CF6",
|
|
2773
|
+
gray: "#9CA3AF"
|
|
2774
|
+
};
|
|
2775
|
+
var DEFAULT_STICKY_COLOR = "yellow";
|
|
2776
|
+
var STICKY_DEFAULT_SIZE = { width: 260, height: 170 };
|
|
2777
|
+
var STICKY_MIN_SIZE = { width: 160, height: 90 };
|
|
2778
|
+
function ToolbarButton({
|
|
2779
|
+
title,
|
|
2780
|
+
onClick,
|
|
2781
|
+
danger,
|
|
2782
|
+
children
|
|
2783
|
+
}) {
|
|
2784
|
+
return /* @__PURE__ */ jsx(
|
|
2785
|
+
"button",
|
|
2786
|
+
{
|
|
2787
|
+
type: "button",
|
|
2788
|
+
title,
|
|
2789
|
+
onClick: (e) => {
|
|
2790
|
+
e.stopPropagation();
|
|
2791
|
+
onClick();
|
|
2792
|
+
},
|
|
2793
|
+
className: cn(
|
|
2794
|
+
"nodrag flex size-6 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-muted hover:text-foreground",
|
|
2795
|
+
danger && "text-[var(--color-status-error-text)] hover:text-[var(--color-status-error-text)]"
|
|
2796
|
+
),
|
|
2797
|
+
children
|
|
2798
|
+
}
|
|
2799
|
+
);
|
|
2800
|
+
}
|
|
2801
|
+
function StickyNoteNodeComponent({
|
|
2802
|
+
id,
|
|
2803
|
+
selected,
|
|
2804
|
+
data
|
|
2805
|
+
}) {
|
|
2806
|
+
const tr = useTr();
|
|
2807
|
+
const { setNodes, getNode } = useReactFlow();
|
|
2808
|
+
const zustandSetNodes = useFlowStore((s) => s.actions.nodes.setNodes);
|
|
2809
|
+
const deleteNode = useDeleteNode();
|
|
2810
|
+
const [editing, setEditing] = useState(false);
|
|
2811
|
+
const [showPalette, setShowPalette] = useState(false);
|
|
2812
|
+
const [draft, setDraft] = useState(String(data.content ?? ""));
|
|
2813
|
+
const textareaRef = useRef(null);
|
|
2814
|
+
const rootRef = useRef(null);
|
|
2815
|
+
useEffect(() => {
|
|
2816
|
+
const wrapper = rootRef.current?.closest(".react-flow__node");
|
|
2817
|
+
if (wrapper && wrapper.style.zIndex !== "-1") {
|
|
2818
|
+
wrapper.style.zIndex = "-1";
|
|
2819
|
+
}
|
|
2820
|
+
});
|
|
2821
|
+
const colorKey = typeof data.color === "string" && STICKY_COLORS[data.color] ? data.color : DEFAULT_STICKY_COLOR;
|
|
2822
|
+
const accent = STICKY_COLORS[colorKey];
|
|
2823
|
+
const patchData = useCallback(
|
|
2824
|
+
(patch) => {
|
|
2825
|
+
setNodes((nds) => {
|
|
2826
|
+
const updated = nds.map(
|
|
2827
|
+
(n) => n.id === id ? { ...n, data: { ...n.data, ...patch } } : n
|
|
2828
|
+
);
|
|
2829
|
+
zustandSetNodes(updated);
|
|
2830
|
+
return updated;
|
|
2831
|
+
});
|
|
2605
2832
|
},
|
|
2606
|
-
[
|
|
2833
|
+
[id, setNodes, zustandSetNodes]
|
|
2607
2834
|
);
|
|
2608
|
-
const
|
|
2609
|
-
(
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
|
|
2835
|
+
const startEditing = useCallback(() => {
|
|
2836
|
+
setDraft(String(getNode(id)?.data?.content ?? ""));
|
|
2837
|
+
setEditing(true);
|
|
2838
|
+
setShowPalette(false);
|
|
2839
|
+
}, [getNode, id]);
|
|
2840
|
+
const commitEdit = useCallback(() => {
|
|
2841
|
+
setEditing(false);
|
|
2842
|
+
patchData({ content: draft });
|
|
2843
|
+
}, [draft, patchData]);
|
|
2844
|
+
const cancelEdit = useCallback(() => {
|
|
2845
|
+
setEditing(false);
|
|
2846
|
+
setDraft(String(getNode(id)?.data?.content ?? ""));
|
|
2847
|
+
}, [getNode, id]);
|
|
2848
|
+
useEffect(() => {
|
|
2849
|
+
if (editing) {
|
|
2850
|
+
const el = textareaRef.current;
|
|
2851
|
+
el?.focus();
|
|
2852
|
+
el?.setSelectionRange(el.value.length, el.value.length);
|
|
2853
|
+
}
|
|
2854
|
+
}, [editing]);
|
|
2855
|
+
const duplicate = useCallback(() => {
|
|
2856
|
+
const source = getNode(id);
|
|
2857
|
+
if (!source) return;
|
|
2858
|
+
const clone = {
|
|
2859
|
+
...source,
|
|
2860
|
+
id: nanoid(),
|
|
2861
|
+
position: {
|
|
2862
|
+
x: source.position.x + 24,
|
|
2863
|
+
y: source.position.y + 24
|
|
2864
|
+
},
|
|
2865
|
+
selected: true,
|
|
2866
|
+
data: { ...source.data }
|
|
2867
|
+
};
|
|
2868
|
+
setNodes((nds) => {
|
|
2869
|
+
const updated = [
|
|
2870
|
+
...nds.map((n) => n.selected ? { ...n, selected: false } : n),
|
|
2871
|
+
clone
|
|
2872
|
+
];
|
|
2873
|
+
zustandSetNodes(updated);
|
|
2874
|
+
return updated;
|
|
2875
|
+
});
|
|
2876
|
+
}, [getNode, id, setNodes, zustandSetNodes]);
|
|
2877
|
+
const content = String(data.content ?? "");
|
|
2878
|
+
return (
|
|
2879
|
+
// Outer wrapper is NOT overflow-hidden: the NodeResizer's edge lines and
|
|
2880
|
+
// corner handles sit on the bounds and would be clipped/covered otherwise.
|
|
2881
|
+
/* @__PURE__ */ jsxs("div", { ref: rootRef, className: "group relative h-full w-full", children: [
|
|
2882
|
+
/* @__PURE__ */ jsx(
|
|
2883
|
+
NodeResizer,
|
|
2884
|
+
{
|
|
2885
|
+
isVisible: !!selected,
|
|
2886
|
+
minWidth: STICKY_MIN_SIZE.width,
|
|
2887
|
+
minHeight: STICKY_MIN_SIZE.height,
|
|
2888
|
+
lineStyle: { border: "none", zIndex: 20 },
|
|
2889
|
+
handleStyle: {
|
|
2890
|
+
width: 12,
|
|
2891
|
+
height: 12,
|
|
2892
|
+
backgroundColor: "transparent",
|
|
2893
|
+
border: "none",
|
|
2894
|
+
zIndex: 20
|
|
2663
2895
|
}
|
|
2664
|
-
pollTimerRef.current = setTimeout(doPoll, POLL_INTERVAL * 2);
|
|
2665
2896
|
}
|
|
2666
|
-
|
|
2667
|
-
|
|
2668
|
-
|
|
2669
|
-
|
|
2670
|
-
|
|
2671
|
-
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
|
|
2675
|
-
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
|
|
2683
|
-
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
|
|
2897
|
+
),
|
|
2898
|
+
/* @__PURE__ */ jsx(
|
|
2899
|
+
"div",
|
|
2900
|
+
{
|
|
2901
|
+
className: cn(
|
|
2902
|
+
"absolute right-1 z-30 flex items-center gap-0.5 rounded-lg border border-border bg-card p-0.5 shadow-md transition-all duration-150",
|
|
2903
|
+
selected || showPalette ? "pointer-events-auto opacity-100" : "pointer-events-none opacity-0 group-hover:pointer-events-auto group-hover:opacity-100"
|
|
2904
|
+
),
|
|
2905
|
+
style: { top: -15 },
|
|
2906
|
+
onDoubleClick: (e) => e.stopPropagation(),
|
|
2907
|
+
children: showPalette ? /* @__PURE__ */ jsx("div", { className: "flex items-center gap-1 px-1 py-0.5", children: Object.entries(STICKY_COLORS).map(([name, hex]) => /* @__PURE__ */ jsx(
|
|
2908
|
+
"button",
|
|
2909
|
+
{
|
|
2910
|
+
type: "button",
|
|
2911
|
+
title: name,
|
|
2912
|
+
onClick: (e) => {
|
|
2913
|
+
e.stopPropagation();
|
|
2914
|
+
patchData({ color: name });
|
|
2915
|
+
setShowPalette(false);
|
|
2916
|
+
},
|
|
2917
|
+
className: cn(
|
|
2918
|
+
"nodrag size-4 rounded-full border transition-transform hover:scale-125",
|
|
2919
|
+
name === colorKey ? "border-foreground" : "border-transparent"
|
|
2920
|
+
),
|
|
2921
|
+
style: { backgroundColor: hex }
|
|
2922
|
+
},
|
|
2923
|
+
name
|
|
2924
|
+
)) }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2925
|
+
/* @__PURE__ */ jsx(
|
|
2926
|
+
ToolbarButton,
|
|
2927
|
+
{
|
|
2928
|
+
title: tr(
|
|
2929
|
+
"fluidgrids.builder.stickyNote.editNoteTooltip",
|
|
2930
|
+
"Edit note"
|
|
2931
|
+
),
|
|
2932
|
+
onClick: startEditing,
|
|
2933
|
+
children: /* @__PURE__ */ jsx(Icon, { icon: "mynaui:edit", className: "size-3.5" })
|
|
2934
|
+
}
|
|
2935
|
+
),
|
|
2936
|
+
/* @__PURE__ */ jsx(
|
|
2937
|
+
ToolbarButton,
|
|
2938
|
+
{
|
|
2939
|
+
title: tr(
|
|
2940
|
+
"fluidgrids.builder.stickyNote.changeColorTooltip",
|
|
2941
|
+
"Change color"
|
|
2942
|
+
),
|
|
2943
|
+
onClick: () => setShowPalette(true),
|
|
2944
|
+
children: /* @__PURE__ */ jsx(
|
|
2945
|
+
"span",
|
|
2946
|
+
{
|
|
2947
|
+
className: "size-3.5 rounded-full",
|
|
2948
|
+
style: { backgroundColor: accent }
|
|
2949
|
+
}
|
|
2950
|
+
)
|
|
2951
|
+
}
|
|
2952
|
+
),
|
|
2953
|
+
/* @__PURE__ */ jsx(
|
|
2954
|
+
ToolbarButton,
|
|
2955
|
+
{
|
|
2956
|
+
title: tr(
|
|
2957
|
+
"fluidgrids.builder.stickyNote.duplicateNoteTooltip",
|
|
2958
|
+
"Duplicate note"
|
|
2959
|
+
),
|
|
2960
|
+
onClick: duplicate,
|
|
2961
|
+
children: /* @__PURE__ */ jsx(Icon, { icon: "mynaui:copy", className: "size-3.5" })
|
|
2962
|
+
}
|
|
2963
|
+
),
|
|
2964
|
+
/* @__PURE__ */ jsx(
|
|
2965
|
+
ToolbarButton,
|
|
2966
|
+
{
|
|
2967
|
+
title: tr(
|
|
2968
|
+
"fluidgrids.builder.stickyNote.deleteNoteTooltip",
|
|
2969
|
+
"Delete note"
|
|
2970
|
+
),
|
|
2971
|
+
danger: true,
|
|
2972
|
+
onClick: () => deleteNode(id),
|
|
2973
|
+
children: /* @__PURE__ */ jsx(Icon, { icon: "mynaui:trash", className: "size-3.5" })
|
|
2697
2974
|
}
|
|
2975
|
+
)
|
|
2976
|
+
] })
|
|
2977
|
+
}
|
|
2978
|
+
),
|
|
2979
|
+
/* @__PURE__ */ jsx(
|
|
2980
|
+
"div",
|
|
2981
|
+
{
|
|
2982
|
+
className: "flex h-full w-full flex-col overflow-hidden rounded-lg border",
|
|
2983
|
+
style: {
|
|
2984
|
+
backgroundColor: `${accent}1F`,
|
|
2985
|
+
// Same border whether selected or not — selection shows only via the
|
|
2986
|
+
// floating toolbar (no extra border/outline on click).
|
|
2987
|
+
borderColor: `${accent}59`
|
|
2988
|
+
},
|
|
2989
|
+
onDoubleClick: (e) => {
|
|
2990
|
+
e.stopPropagation();
|
|
2991
|
+
startEditing();
|
|
2992
|
+
},
|
|
2993
|
+
children: editing ? /* @__PURE__ */ jsx(
|
|
2994
|
+
"textarea",
|
|
2995
|
+
{
|
|
2996
|
+
ref: textareaRef,
|
|
2997
|
+
value: draft,
|
|
2998
|
+
onChange: (e) => setDraft(e.target.value),
|
|
2999
|
+
onBlur: commitEdit,
|
|
3000
|
+
onKeyDown: (e) => {
|
|
3001
|
+
e.stopPropagation();
|
|
3002
|
+
if (e.key === "Escape") cancelEdit();
|
|
3003
|
+
if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) commitEdit();
|
|
3004
|
+
},
|
|
3005
|
+
placeholder: tr(
|
|
3006
|
+
"fluidgrids.builder.stickyNote.editorPlaceholder",
|
|
3007
|
+
"# Note\nSupports **markdown** \u2014 headings, lists, links\u2026"
|
|
3008
|
+
),
|
|
3009
|
+
className: "nodrag nowheel h-full w-full resize-none bg-transparent p-3 font-mono text-xs leading-relaxed text-foreground outline-none placeholder:text-muted-foreground/60"
|
|
2698
3010
|
}
|
|
2699
|
-
|
|
2700
|
-
|
|
2701
|
-
|
|
2702
|
-
|
|
2703
|
-
|
|
2704
|
-
|
|
2705
|
-
|
|
2706
|
-
|
|
2707
|
-
|
|
2708
|
-
|
|
2709
|
-
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
nodeType: workflow.nodes.find((node) => node.id === nodeId)?.type || "unknown",
|
|
2719
|
-
nodeName: getNodeName(nodeId),
|
|
2720
|
-
status: "running",
|
|
2721
|
-
inputArgs: null,
|
|
2722
|
-
output: null,
|
|
2723
|
-
error: null,
|
|
2724
|
-
duration: null,
|
|
2725
|
-
startTime: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2726
|
-
endTime: null
|
|
2727
|
-
};
|
|
2728
|
-
addLog(initialLog);
|
|
2729
|
-
pollRunUpdates(backendRunId);
|
|
2730
|
-
return { success: true, runId: backendRunId, plan: stepResult.plan };
|
|
3011
|
+
) : content.trim() ? /* @__PURE__ */ jsx(
|
|
3012
|
+
"div",
|
|
3013
|
+
{
|
|
3014
|
+
className: cn(
|
|
3015
|
+
"nowheel h-full w-full overflow-auto p-3 text-xs leading-relaxed text-foreground",
|
|
3016
|
+
// Compact markdown typography scoped to the note body.
|
|
3017
|
+
"[&_h1]:mb-1.5 [&_h1]:text-sm [&_h1]:font-bold",
|
|
3018
|
+
"[&_h2]:mb-1 [&_h2]:text-[13px] [&_h2]:font-bold",
|
|
3019
|
+
"[&_h3]:mb-1 [&_h3]:text-xs [&_h3]:font-semibold",
|
|
3020
|
+
"[&_p]:mb-1.5 [&_ul]:mb-1.5 [&_ul]:list-disc [&_ul]:pl-4",
|
|
3021
|
+
"[&_ol]:mb-1.5 [&_ol]:list-decimal [&_ol]:pl-4 [&_li]:mb-0.5",
|
|
3022
|
+
"[&_a]:underline [&_code]:rounded [&_code]:bg-muted [&_code]:px-1"
|
|
3023
|
+
),
|
|
3024
|
+
children: /* @__PURE__ */ jsx(ReactMarkdown, { children: content })
|
|
3025
|
+
}
|
|
3026
|
+
) : /* @__PURE__ */ jsx("div", { className: "flex h-full w-full items-center justify-center p-3 text-xs text-muted-foreground/70", children: tr(
|
|
3027
|
+
"fluidgrids.builder.stickyNote.emptyStateHint",
|
|
3028
|
+
"Double-click to edit"
|
|
3029
|
+
) })
|
|
2731
3030
|
}
|
|
2732
|
-
|
|
2733
|
-
|
|
2734
|
-
return { success: false, error: "No result from executeStep" };
|
|
2735
|
-
} catch (error) {
|
|
2736
|
-
console.error("[useNodeExecution] Error executing step:", error);
|
|
2737
|
-
const errorMessage = describeOperationError(error);
|
|
2738
|
-
const errorRunId = nanoid$1();
|
|
2739
|
-
const errorLog = {
|
|
2740
|
-
id: `${errorRunId}-${nodeId}`,
|
|
2741
|
-
runId: errorRunId,
|
|
2742
|
-
nodeId,
|
|
2743
|
-
nodeType: workflow.nodes.find((node) => node.id === nodeId)?.type || "unknown",
|
|
2744
|
-
nodeName: getNodeName(nodeId),
|
|
2745
|
-
status: "failed",
|
|
2746
|
-
inputArgs: null,
|
|
2747
|
-
output: null,
|
|
2748
|
-
error: errorMessage,
|
|
2749
|
-
duration: null,
|
|
2750
|
-
startTime: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2751
|
-
endTime: (/* @__PURE__ */ new Date()).toISOString()
|
|
2752
|
-
};
|
|
2753
|
-
addLog(errorLog);
|
|
2754
|
-
setNodeStatus(nodeId, "error");
|
|
2755
|
-
completeExecution();
|
|
2756
|
-
return { success: false, error: errorMessage };
|
|
2757
|
-
}
|
|
2758
|
-
},
|
|
2759
|
-
[
|
|
2760
|
-
cleanup,
|
|
2761
|
-
clearLogs,
|
|
2762
|
-
startExecution,
|
|
2763
|
-
setStepPlan,
|
|
2764
|
-
workflow,
|
|
2765
|
-
getNodeName,
|
|
2766
|
-
addLog,
|
|
2767
|
-
setNodeStatus,
|
|
2768
|
-
pollRunUpdates,
|
|
2769
|
-
completeExecution
|
|
2770
|
-
]
|
|
3031
|
+
)
|
|
3032
|
+
] })
|
|
2771
3033
|
);
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
|
|
2781
|
-
|
|
2782
|
-
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
|
|
2788
|
-
|
|
2789
|
-
|
|
2790
|
-
|
|
2791
|
-
|
|
2792
|
-
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
const result = throwIfResultError(
|
|
2796
|
-
await client.mutate({
|
|
2797
|
-
mutation: TRIGGER_WORKFLOW,
|
|
2798
|
-
variables: {
|
|
2799
|
-
input: {
|
|
2800
|
-
workflowKey,
|
|
2801
|
-
version,
|
|
2802
|
-
triggerNodeId: triggerNode.id,
|
|
2803
|
-
triggerData: customTriggerData || {},
|
|
2804
|
-
context: {},
|
|
2805
|
-
// Builder "Test workflow" is always a manual/debug run.
|
|
2806
|
-
// Pinned node data only applies to TEST executions, never a
|
|
2807
|
-
// production trigger (webhook, schedule, API).
|
|
2808
|
-
mode: "TEST"
|
|
2809
|
-
}
|
|
2810
|
-
}
|
|
2811
|
-
})
|
|
2812
|
-
);
|
|
2813
|
-
const triggerResult = result.data?.triggerWorkflow;
|
|
2814
|
-
if (triggerResult) {
|
|
2815
|
-
const backendRunId = triggerResult.runId;
|
|
2816
|
-
startExecution(triggerNode.id, backendRunId, {
|
|
2817
|
-
workflowKey: triggerResult.workflowKey,
|
|
2818
|
-
version: triggerResult.version,
|
|
2819
|
-
sourceMode: "TEST"
|
|
2820
|
-
});
|
|
2821
|
-
pollRunUpdates(backendRunId);
|
|
2822
|
-
return { success: true, runId: backendRunId };
|
|
2823
|
-
}
|
|
2824
|
-
console.error("[useNodeExecution] No result from trigger");
|
|
2825
|
-
setNodeStatus(triggerNode.id, "error");
|
|
2826
|
-
return { success: false, error: "No result from trigger" };
|
|
2827
|
-
} catch (error) {
|
|
2828
|
-
console.error("[useNodeExecution] Error triggering workflow:", error);
|
|
2829
|
-
const errorMessage = describeOperationError(error);
|
|
2830
|
-
const errorRunId = nanoid$1();
|
|
2831
|
-
const errorLog = {
|
|
2832
|
-
id: `${errorRunId}-${triggerNode.id}`,
|
|
2833
|
-
runId: errorRunId,
|
|
2834
|
-
nodeId: triggerNode.id,
|
|
2835
|
-
nodeType: triggerNode.type || "unknown",
|
|
2836
|
-
nodeName: getNodeName(triggerNode.id),
|
|
2837
|
-
status: "failed",
|
|
2838
|
-
inputArgs: null,
|
|
2839
|
-
output: null,
|
|
2840
|
-
error: errorMessage,
|
|
2841
|
-
duration: null,
|
|
2842
|
-
startTime: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2843
|
-
endTime: (/* @__PURE__ */ new Date()).toISOString()
|
|
2844
|
-
};
|
|
2845
|
-
addLog(errorLog);
|
|
2846
|
-
setNodeStatus(triggerNode.id, "error");
|
|
2847
|
-
completeExecution();
|
|
2848
|
-
return { success: false, error: errorMessage };
|
|
2849
|
-
}
|
|
2850
|
-
},
|
|
2851
|
-
[
|
|
2852
|
-
findTriggerNode,
|
|
2853
|
-
cleanup,
|
|
2854
|
-
clearLogs,
|
|
2855
|
-
startExecution,
|
|
2856
|
-
workflow,
|
|
2857
|
-
getNodeName,
|
|
2858
|
-
addLog,
|
|
2859
|
-
setNodeStatus,
|
|
2860
|
-
pollRunUpdates,
|
|
2861
|
-
completeExecution
|
|
2862
|
-
]
|
|
2863
|
-
);
|
|
2864
|
-
const executeFromNode = useCallback(
|
|
2865
|
-
async (sourceRunId, nodeId) => {
|
|
2866
|
-
cleanup();
|
|
2867
|
-
clearLogs();
|
|
2868
|
-
setNodeStatus(nodeId, "running");
|
|
2869
|
-
try {
|
|
2870
|
-
const client = getFluidGridsClient();
|
|
2871
|
-
const result = throwIfResultError(
|
|
2872
|
-
await client.mutate({
|
|
2873
|
-
mutation: RUN_FROM_NODE,
|
|
2874
|
-
variables: {
|
|
2875
|
-
input: {
|
|
2876
|
-
sourceRunId,
|
|
2877
|
-
nodeId,
|
|
2878
|
-
// Run-from-node is a debug affordance — always TEST, same
|
|
2879
|
-
// rule as the other builder debug execution paths.
|
|
2880
|
-
mode: "TEST"
|
|
2881
|
-
}
|
|
2882
|
-
}
|
|
2883
|
-
})
|
|
2884
|
-
);
|
|
2885
|
-
const runFromNodeResult = result.data?.runFromNode;
|
|
2886
|
-
if (runFromNodeResult) {
|
|
2887
|
-
const backendRunId = runFromNodeResult.runId;
|
|
2888
|
-
startExecution(nodeId, backendRunId, {
|
|
2889
|
-
workflowKey: runFromNodeResult.workflowKey,
|
|
2890
|
-
version: runFromNodeResult.version,
|
|
2891
|
-
sourceMode: "TEST"
|
|
2892
|
-
});
|
|
2893
|
-
pollRunUpdates(backendRunId);
|
|
2894
|
-
return { success: true, runId: backendRunId };
|
|
2895
|
-
}
|
|
2896
|
-
console.error("[useNodeExecution] No result from runFromNode");
|
|
2897
|
-
setNodeStatus(nodeId, "error");
|
|
2898
|
-
return { success: false, error: "No result from runFromNode" };
|
|
2899
|
-
} catch (error) {
|
|
2900
|
-
console.error("[useNodeExecution] Error running from node:", error);
|
|
2901
|
-
const errorMessage = describeOperationError(error);
|
|
2902
|
-
setNodeStatus(nodeId, "error");
|
|
2903
|
-
completeExecution();
|
|
2904
|
-
return { success: false, error: errorMessage };
|
|
2905
|
-
}
|
|
2906
|
-
},
|
|
2907
|
-
[
|
|
2908
|
-
cleanup,
|
|
2909
|
-
clearLogs,
|
|
2910
|
-
setNodeStatus,
|
|
2911
|
-
startExecution,
|
|
2912
|
-
pollRunUpdates,
|
|
2913
|
-
completeExecution
|
|
2914
|
-
]
|
|
2915
|
-
);
|
|
2916
|
-
const loadExecution = useCallback(
|
|
2917
|
-
async (runId) => {
|
|
2918
|
-
cleanup();
|
|
2919
|
-
const requestId = ++loadRequestRef.current;
|
|
2920
|
-
try {
|
|
2921
|
-
const client = getFluidGridsClient();
|
|
2922
|
-
const result = throwIfResultError(
|
|
2923
|
-
await client.query({
|
|
2924
|
-
query: EXECUTION_DETAIL,
|
|
2925
|
-
variables: { id: runId },
|
|
2926
|
-
fetchPolicy: "network-only"
|
|
2927
|
-
})
|
|
2928
|
-
);
|
|
2929
|
-
const run = result.data?.workflowRun;
|
|
2930
|
-
if (!run) {
|
|
2931
|
-
return { success: false, error: "Execution not found" };
|
|
2932
|
-
}
|
|
2933
|
-
if (run.mode !== "TEST") {
|
|
2934
|
-
return {
|
|
2935
|
-
success: false,
|
|
2936
|
-
error: "Only TEST runs can be loaded for continuation."
|
|
2937
|
-
};
|
|
2938
|
-
}
|
|
2939
|
-
if (!TERMINAL_STATUSES.has(run.status)) {
|
|
2940
|
-
return {
|
|
2941
|
-
success: false,
|
|
2942
|
-
error: "Only terminal TEST runs can be loaded for continuation."
|
|
2943
|
-
};
|
|
2944
|
-
}
|
|
2945
|
-
if (requestId !== loadRequestRef.current) {
|
|
2946
|
-
return { success: false, error: "Execution load superseded." };
|
|
2947
|
-
}
|
|
2948
|
-
const currentWorkflow = useFlowStore.getState().workflow;
|
|
2949
|
-
const currentWorkflowKey = currentWorkflow.workflowKey || currentWorkflow.id;
|
|
2950
|
-
if (run.workflowKey !== currentWorkflowKey || run.version !== currentWorkflow.version) {
|
|
2951
|
-
return {
|
|
2952
|
-
success: false,
|
|
2953
|
-
error: "The selected run belongs to a different workflow version."
|
|
2954
|
-
};
|
|
2955
|
-
}
|
|
2956
|
-
const logs = (run.nodeStates ?? []).map((nodeState) => {
|
|
2957
|
-
const rawStatus = nodeState.status.toLowerCase();
|
|
2958
|
-
const status = rawStatus === "completed" ? "completed" : rawStatus === "failed" || rawStatus === "timeout" ? "failed" : rawStatus === "running" || rawStatus === "waiting" || rawStatus === "delayed" || rawStatus === "waiting_subworkflow" ? "running" : "pending";
|
|
2959
|
-
return {
|
|
2960
|
-
id: `${runId}-${nodeState.nodeId}`,
|
|
2961
|
-
runId,
|
|
2962
|
-
nodeId: nodeState.nodeId,
|
|
2963
|
-
nodeType: nodeState.nodeType,
|
|
2964
|
-
nodeName: getNodeName(nodeState.nodeId),
|
|
2965
|
-
status,
|
|
2966
|
-
inputArgs: nodeState.inputArgs ?? null,
|
|
2967
|
-
output: nodeState.output ?? null,
|
|
2968
|
-
error: nodeState.error ?? null,
|
|
2969
|
-
duration: nodeState.duration ?? null,
|
|
2970
|
-
startTime: nodeState.startTime ?? run.startedAt ?? "",
|
|
2971
|
-
endTime: nodeState.endTime ?? null,
|
|
2972
|
-
usedPinnedData: nodeState.usedPinnedData ?? false,
|
|
2973
|
-
reusedFromRunId: nodeState.reusedFromRunId ?? null
|
|
2974
|
-
};
|
|
2975
|
-
});
|
|
2976
|
-
loadDebugRun(runId, logs, {
|
|
2977
|
-
workflowKey: run.workflowKey,
|
|
2978
|
-
version: run.version,
|
|
2979
|
-
sourceMode: run.mode
|
|
2980
|
-
});
|
|
2981
|
-
return { success: true, runId };
|
|
2982
|
-
} catch (error) {
|
|
2983
|
-
console.error("[useNodeExecution] Error loading execution:", error);
|
|
2984
|
-
return { success: false, error: describeOperationError(error) };
|
|
2985
|
-
}
|
|
2986
|
-
},
|
|
2987
|
-
[cleanup, getNodeName, loadDebugRun]
|
|
2988
|
-
);
|
|
2989
|
-
useEffect(() => {
|
|
2990
|
-
return () => {
|
|
2991
|
-
if (pollTimerRef.current) {
|
|
2992
|
-
completeExecution();
|
|
2993
|
-
}
|
|
2994
|
-
cleanup();
|
|
3034
|
+
}
|
|
3035
|
+
var StickyNoteNode = memo(StickyNoteNodeComponent);
|
|
3036
|
+
function buildStickyNode(position) {
|
|
3037
|
+
return {
|
|
3038
|
+
id: nanoid(),
|
|
3039
|
+
type: STICKY_NOTE_TYPE,
|
|
3040
|
+
position,
|
|
3041
|
+
width: STICKY_DEFAULT_SIZE.width,
|
|
3042
|
+
height: STICKY_DEFAULT_SIZE.height,
|
|
3043
|
+
// Render under regular nodes and edges — notes are a background layer.
|
|
3044
|
+
zIndex: -1,
|
|
3045
|
+
selected: true,
|
|
3046
|
+
data: { content: "", color: DEFAULT_STICKY_COLOR }
|
|
3047
|
+
};
|
|
3048
|
+
}
|
|
3049
|
+
function getNodeDetail(nodeType) {
|
|
3050
|
+
const node = NODES.find((node2) => node2.type === nodeType);
|
|
3051
|
+
if (!node) {
|
|
3052
|
+
return {
|
|
3053
|
+
icon: "mynaui:keyboard-solid",
|
|
3054
|
+
title: nodeType ?? "Unknown",
|
|
3055
|
+
description: "",
|
|
3056
|
+
gradientColor: "gray"
|
|
2995
3057
|
};
|
|
2996
|
-
}
|
|
3058
|
+
}
|
|
3059
|
+
return node.detail;
|
|
3060
|
+
}
|
|
3061
|
+
function createNodeData(type, data) {
|
|
2997
3062
|
return {
|
|
2998
|
-
|
|
2999
|
-
|
|
3000
|
-
|
|
3001
|
-
loadExecution,
|
|
3002
|
-
cleanup,
|
|
3003
|
-
findTriggerNode
|
|
3063
|
+
id: nanoid$1(),
|
|
3064
|
+
type,
|
|
3065
|
+
data
|
|
3004
3066
|
};
|
|
3005
3067
|
}
|
|
3006
|
-
function
|
|
3007
|
-
const
|
|
3008
|
-
|
|
3009
|
-
return useCallback(
|
|
3010
|
-
(id) => {
|
|
3011
|
-
const node = getNode(id);
|
|
3012
|
-
if (!node) return;
|
|
3013
|
-
const edges = getEdges();
|
|
3014
|
-
const connectedEdges = getConnectedEdges([node], edges);
|
|
3015
|
-
deleteElements({ nodes: [node], edges: connectedEdges }).then();
|
|
3016
|
-
deleteNodes([node]);
|
|
3017
|
-
},
|
|
3018
|
-
[deleteElements, getEdges, getNode, deleteNodes]
|
|
3019
|
-
);
|
|
3068
|
+
function createNodeWithDefaultData(type, data) {
|
|
3069
|
+
const defaultData = NODES.find((node) => node.type === type)?.defaultData ?? {};
|
|
3070
|
+
return Object.assign(createNodeData(type, defaultData), data);
|
|
3020
3071
|
}
|
|
3021
|
-
|
|
3022
|
-
|
|
3023
|
-
|
|
3024
|
-
|
|
3025
|
-
|
|
3026
|
-
|
|
3027
|
-
|
|
3028
|
-
|
|
3029
|
-
|
|
3030
|
-
|
|
3031
|
-
|
|
3032
|
-
|
|
3033
|
-
|
|
3034
|
-
|
|
3035
|
-
|
|
3036
|
-
|
|
3037
|
-
|
|
3038
|
-
|
|
3039
|
-
|
|
3040
|
-
|
|
3041
|
-
|
|
3042
|
-
|
|
3072
|
+
var NODE_DOCS_URL = gql`
|
|
3073
|
+
query NodeDocsUrlLookup($nodeType: String!, $nodeVersion: String!) {
|
|
3074
|
+
nodeEndpointConfig(nodeType: $nodeType, nodeVersion: $nodeVersion) {
|
|
3075
|
+
docsUrl
|
|
3076
|
+
}
|
|
3077
|
+
}
|
|
3078
|
+
`;
|
|
3079
|
+
var cache = /* @__PURE__ */ new Map();
|
|
3080
|
+
var inFlight = /* @__PURE__ */ new Map();
|
|
3081
|
+
function fetchDocsUrl(nodeType, nodeVersion) {
|
|
3082
|
+
const key = `${nodeType}@${nodeVersion}`;
|
|
3083
|
+
if (cache.has(key)) return Promise.resolve(cache.get(key));
|
|
3084
|
+
const existing = inFlight.get(key);
|
|
3085
|
+
if (existing) return existing;
|
|
3086
|
+
const client = getFluidGridsClientSafe();
|
|
3087
|
+
if (!client) {
|
|
3088
|
+
cache.set(key, void 0);
|
|
3089
|
+
return Promise.resolve(void 0);
|
|
3090
|
+
}
|
|
3091
|
+
const promise = client.query({
|
|
3092
|
+
query: NODE_DOCS_URL,
|
|
3093
|
+
variables: { nodeType, nodeVersion },
|
|
3094
|
+
fetchPolicy: "cache-first"
|
|
3095
|
+
}).then((result) => result.data?.nodeEndpointConfig?.docsUrl ?? void 0).catch(() => void 0).finally(() => inFlight.delete(key));
|
|
3096
|
+
inFlight.set(key, promise);
|
|
3097
|
+
return promise.then((url) => {
|
|
3098
|
+
cache.set(key, url);
|
|
3099
|
+
return url;
|
|
3100
|
+
});
|
|
3101
|
+
}
|
|
3102
|
+
function useNodeDocsUrl(nodeType, nodeVersion) {
|
|
3103
|
+
const [docsUrl, setDocsUrl] = useState(
|
|
3104
|
+
() => nodeType && nodeVersion ? cache.get(`${nodeType}@${nodeVersion}`) : void 0
|
|
3105
|
+
);
|
|
3106
|
+
useEffect(() => {
|
|
3107
|
+
if (!nodeType || !nodeVersion) {
|
|
3108
|
+
setDocsUrl(void 0);
|
|
3109
|
+
return;
|
|
3110
|
+
}
|
|
3111
|
+
let cancelled = false;
|
|
3112
|
+
fetchDocsUrl(nodeType, nodeVersion).then((url) => {
|
|
3113
|
+
if (!cancelled) setDocsUrl(url);
|
|
3114
|
+
});
|
|
3115
|
+
return () => {
|
|
3116
|
+
cancelled = true;
|
|
3117
|
+
};
|
|
3118
|
+
}, [nodeType, nodeVersion]);
|
|
3119
|
+
return docsUrl;
|
|
3120
|
+
}
|
|
3121
|
+
function isImageIcon(icon) {
|
|
3122
|
+
return !!icon && (/^https?:\/\//.test(icon) || icon.startsWith("data:"));
|
|
3123
|
+
}
|
|
3124
|
+
function NodeTypeIcon({
|
|
3125
|
+
icon,
|
|
3126
|
+
alt,
|
|
3127
|
+
className,
|
|
3128
|
+
fallbackIcon = "mynaui:puzzle"
|
|
3129
|
+
}) {
|
|
3130
|
+
if (isImageIcon(icon)) {
|
|
3131
|
+
return /* @__PURE__ */ jsx(
|
|
3132
|
+
"img",
|
|
3133
|
+
{
|
|
3134
|
+
src: icon,
|
|
3135
|
+
alt: alt ?? "",
|
|
3136
|
+
className: cn("object-contain", className)
|
|
3137
|
+
}
|
|
3138
|
+
);
|
|
3139
|
+
}
|
|
3140
|
+
return /* @__PURE__ */ jsx(Icon, { icon: icon || fallbackIcon, className });
|
|
3141
|
+
}
|
|
3142
|
+
var BRAND_ACCENTS = [
|
|
3143
|
+
[/gmail|google/, "#ea4335"],
|
|
3144
|
+
[/slack/, "#611f69"],
|
|
3145
|
+
[/github/, "#334155"],
|
|
3146
|
+
[/gitlab/, "#fc6d26"],
|
|
3147
|
+
[/stripe/, "#635bff"],
|
|
3148
|
+
[/discord/, "#5865f2"],
|
|
3149
|
+
[/notion/, "#111827"],
|
|
3150
|
+
[/telegram/, "#2aabee"],
|
|
3151
|
+
[/whatsapp/, "#25d366"],
|
|
3152
|
+
[/openai/, "#10a37f"],
|
|
3153
|
+
[/anthropic|claude/, "#d97757"],
|
|
3154
|
+
[/twilio/, "#f22f46"],
|
|
3155
|
+
[/sheet|excel/, "#21a366"],
|
|
3156
|
+
[/http|api|webhook|rest/, "#6366f1"],
|
|
3157
|
+
[/db|sql|postgres|mysql|mongo/, "#0ea5e9"]
|
|
3158
|
+
];
|
|
3159
|
+
var HASH_PALETTE = [
|
|
3160
|
+
"#6366f1",
|
|
3161
|
+
"#0ea5e9",
|
|
3162
|
+
"#10b981",
|
|
3163
|
+
"#f59e0b",
|
|
3164
|
+
"#ef4444",
|
|
3165
|
+
"#a855f7",
|
|
3166
|
+
"#ec4899",
|
|
3167
|
+
"#14b8a6",
|
|
3168
|
+
"#f97316",
|
|
3169
|
+
"#8b5cf6"
|
|
3170
|
+
];
|
|
3171
|
+
function nodeAccent(key) {
|
|
3172
|
+
const k = (key || "").toLowerCase();
|
|
3173
|
+
for (const [re, hex] of BRAND_ACCENTS) if (re.test(k)) return hex;
|
|
3174
|
+
let h = 0;
|
|
3175
|
+
for (let i = 0; i < k.length; i++) h = h * 31 + k.charCodeAt(i) >>> 0;
|
|
3176
|
+
return HASH_PALETTE[h % HASH_PALETTE.length];
|
|
3177
|
+
}
|
|
3178
|
+
function tint(hex, pct = 14) {
|
|
3179
|
+
return `color-mix(in srgb, ${hex} ${pct}%, transparent)`;
|
|
3180
|
+
}
|
|
3181
|
+
function InlineEditableLabel({
|
|
3182
|
+
value,
|
|
3183
|
+
placeholder,
|
|
3184
|
+
editing,
|
|
3185
|
+
onStartEdit,
|
|
3186
|
+
onCommit,
|
|
3187
|
+
onCancel,
|
|
3188
|
+
className,
|
|
3189
|
+
inputClassName
|
|
3043
3190
|
}) {
|
|
3191
|
+
const tr = useTr();
|
|
3192
|
+
const [draft, setDraft] = React2.useState(value);
|
|
3193
|
+
const inputRef = React2.useRef(null);
|
|
3194
|
+
React2.useEffect(() => {
|
|
3195
|
+
if (!editing) return;
|
|
3196
|
+
setDraft(value);
|
|
3197
|
+
const raf = requestAnimationFrame(() => {
|
|
3198
|
+
inputRef.current?.focus();
|
|
3199
|
+
inputRef.current?.select();
|
|
3200
|
+
});
|
|
3201
|
+
return () => cancelAnimationFrame(raf);
|
|
3202
|
+
}, [editing, value]);
|
|
3203
|
+
if (editing) {
|
|
3204
|
+
return /* @__PURE__ */ jsx(
|
|
3205
|
+
"input",
|
|
3206
|
+
{
|
|
3207
|
+
ref: inputRef,
|
|
3208
|
+
value: draft,
|
|
3209
|
+
onChange: (e) => setDraft(e.target.value),
|
|
3210
|
+
onClick: (e) => e.stopPropagation(),
|
|
3211
|
+
onDoubleClick: (e) => e.stopPropagation(),
|
|
3212
|
+
onMouseDown: (e) => e.stopPropagation(),
|
|
3213
|
+
onKeyDown: (e) => {
|
|
3214
|
+
e.stopPropagation();
|
|
3215
|
+
if (e.key === "Enter") {
|
|
3216
|
+
e.preventDefault();
|
|
3217
|
+
onCommit(draft.trim());
|
|
3218
|
+
} else if (e.key === "Escape") {
|
|
3219
|
+
e.preventDefault();
|
|
3220
|
+
onCancel();
|
|
3221
|
+
}
|
|
3222
|
+
},
|
|
3223
|
+
onBlur: () => onCommit(draft.trim()),
|
|
3224
|
+
className: cn(
|
|
3225
|
+
"min-w-0 flex-1 rounded border border-primary/50 bg-card px-1 text-inherit outline-none",
|
|
3226
|
+
inputClassName
|
|
3227
|
+
)
|
|
3228
|
+
}
|
|
3229
|
+
);
|
|
3230
|
+
}
|
|
3044
3231
|
return /* @__PURE__ */ jsx(
|
|
3045
|
-
"
|
|
3232
|
+
"span",
|
|
3046
3233
|
{
|
|
3047
|
-
|
|
3048
|
-
title
|
|
3049
|
-
|
|
3234
|
+
className: cn("cursor-text truncate", className),
|
|
3235
|
+
title: tr(
|
|
3236
|
+
"fluidgrids.builder.nodeCard.renameTooltip",
|
|
3237
|
+
"Double-click or press F2 to rename"
|
|
3238
|
+
),
|
|
3239
|
+
onDoubleClick: (e) => {
|
|
3050
3240
|
e.stopPropagation();
|
|
3051
|
-
|
|
3241
|
+
onStartEdit();
|
|
3052
3242
|
},
|
|
3053
|
-
|
|
3054
|
-
"nodrag flex size-6 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-muted hover:text-foreground",
|
|
3055
|
-
danger && "text-[var(--color-status-error-text)] hover:text-[var(--color-status-error-text)]"
|
|
3056
|
-
),
|
|
3057
|
-
children
|
|
3243
|
+
children: value || placeholder
|
|
3058
3244
|
}
|
|
3059
3245
|
);
|
|
3060
3246
|
}
|
|
3061
|
-
|
|
3062
|
-
|
|
3063
|
-
|
|
3064
|
-
|
|
3065
|
-
|
|
3066
|
-
|
|
3067
|
-
|
|
3068
|
-
|
|
3069
|
-
|
|
3070
|
-
|
|
3071
|
-
|
|
3072
|
-
|
|
3073
|
-
|
|
3074
|
-
|
|
3075
|
-
|
|
3076
|
-
|
|
3077
|
-
|
|
3078
|
-
|
|
3079
|
-
|
|
3080
|
-
|
|
3081
|
-
|
|
3082
|
-
|
|
3083
|
-
|
|
3084
|
-
|
|
3085
|
-
|
|
3086
|
-
|
|
3087
|
-
|
|
3088
|
-
|
|
3089
|
-
|
|
3090
|
-
|
|
3091
|
-
|
|
3092
|
-
|
|
3093
|
-
|
|
3094
|
-
|
|
3095
|
-
|
|
3096
|
-
setDraft(String(getNode(id)?.data?.content ?? ""));
|
|
3097
|
-
setEditing(true);
|
|
3098
|
-
setShowPalette(false);
|
|
3099
|
-
}, [getNode, id]);
|
|
3100
|
-
const commitEdit = useCallback(() => {
|
|
3101
|
-
setEditing(false);
|
|
3102
|
-
patchData({ content: draft });
|
|
3103
|
-
}, [draft, patchData]);
|
|
3104
|
-
const cancelEdit = useCallback(() => {
|
|
3105
|
-
setEditing(false);
|
|
3106
|
-
setDraft(String(getNode(id)?.data?.content ?? ""));
|
|
3107
|
-
}, [getNode, id]);
|
|
3108
|
-
useEffect(() => {
|
|
3109
|
-
if (editing) {
|
|
3110
|
-
const el = textareaRef.current;
|
|
3111
|
-
el?.focus();
|
|
3112
|
-
el?.setSelectionRange(el.value.length, el.value.length);
|
|
3113
|
-
}
|
|
3114
|
-
}, [editing]);
|
|
3115
|
-
const duplicate = useCallback(() => {
|
|
3116
|
-
const source = getNode(id);
|
|
3117
|
-
if (!source) return;
|
|
3118
|
-
const clone = {
|
|
3119
|
-
...source,
|
|
3120
|
-
id: nanoid(),
|
|
3121
|
-
position: {
|
|
3122
|
-
x: source.position.x + 24,
|
|
3123
|
-
y: source.position.y + 24
|
|
3124
|
-
},
|
|
3125
|
-
selected: true,
|
|
3126
|
-
data: { ...source.data }
|
|
3127
|
-
};
|
|
3128
|
-
setNodes((nds) => {
|
|
3129
|
-
const updated = [
|
|
3130
|
-
...nds.map((n) => n.selected ? { ...n, selected: false } : n),
|
|
3131
|
-
clone
|
|
3132
|
-
];
|
|
3133
|
-
zustandSetNodes(updated);
|
|
3134
|
-
return updated;
|
|
3135
|
-
});
|
|
3136
|
-
}, [getNode, id, setNodes, zustandSetNodes]);
|
|
3137
|
-
const content = String(data.content ?? "");
|
|
3138
|
-
return (
|
|
3139
|
-
// Outer wrapper is NOT overflow-hidden: the NodeResizer's edge lines and
|
|
3140
|
-
// corner handles sit on the bounds and would be clipped/covered otherwise.
|
|
3141
|
-
/* @__PURE__ */ jsxs("div", { ref: rootRef, className: "group relative h-full w-full", children: [
|
|
3142
|
-
/* @__PURE__ */ jsx(
|
|
3143
|
-
NodeResizer,
|
|
3144
|
-
{
|
|
3145
|
-
isVisible: !!selected,
|
|
3146
|
-
minWidth: STICKY_MIN_SIZE.width,
|
|
3147
|
-
minHeight: STICKY_MIN_SIZE.height,
|
|
3148
|
-
lineStyle: { border: "none", zIndex: 20 },
|
|
3149
|
-
handleStyle: {
|
|
3150
|
-
width: 12,
|
|
3151
|
-
height: 12,
|
|
3152
|
-
backgroundColor: "transparent",
|
|
3153
|
-
border: "none",
|
|
3154
|
-
zIndex: 20
|
|
3155
|
-
}
|
|
3156
|
-
}
|
|
3157
|
-
),
|
|
3158
|
-
/* @__PURE__ */ jsx(
|
|
3159
|
-
"div",
|
|
3160
|
-
{
|
|
3161
|
-
className: cn(
|
|
3162
|
-
"absolute right-1 z-30 flex items-center gap-0.5 rounded-lg border border-border bg-card p-0.5 shadow-md transition-all duration-150",
|
|
3163
|
-
selected || showPalette ? "pointer-events-auto opacity-100" : "pointer-events-none opacity-0 group-hover:pointer-events-auto group-hover:opacity-100"
|
|
3164
|
-
),
|
|
3165
|
-
style: { top: -15 },
|
|
3166
|
-
onDoubleClick: (e) => e.stopPropagation(),
|
|
3167
|
-
children: showPalette ? /* @__PURE__ */ jsx("div", { className: "flex items-center gap-1 px-1 py-0.5", children: Object.entries(STICKY_COLORS).map(([name, hex]) => /* @__PURE__ */ jsx(
|
|
3168
|
-
"button",
|
|
3247
|
+
var NodeCard = React2.forwardRef(
|
|
3248
|
+
({
|
|
3249
|
+
className,
|
|
3250
|
+
selected = false,
|
|
3251
|
+
executionStatus = "idle",
|
|
3252
|
+
itemCount,
|
|
3253
|
+
isTrigger = false,
|
|
3254
|
+
accent = "#3b82f6",
|
|
3255
|
+
onSettings,
|
|
3256
|
+
onExecute,
|
|
3257
|
+
onDelete,
|
|
3258
|
+
isExecuting = false,
|
|
3259
|
+
infoButton,
|
|
3260
|
+
disabled = false,
|
|
3261
|
+
onToggleDisabled,
|
|
3262
|
+
children,
|
|
3263
|
+
...props
|
|
3264
|
+
}, ref) => {
|
|
3265
|
+
const tr = useTr();
|
|
3266
|
+
const [hovered, setHovered] = React2.useState(false);
|
|
3267
|
+
const statusRing = executionStatus === "running" ? "ring-2 ring-[var(--color-status-success-border)] animate-pulse-border" : executionStatus === "success" ? "ring-2 ring-[var(--color-status-success-border)]" : executionStatus === "error" ? "ring-2 ring-[var(--color-status-error-border)]" : "";
|
|
3268
|
+
return /* @__PURE__ */ jsxs(
|
|
3269
|
+
"div",
|
|
3270
|
+
{
|
|
3271
|
+
className: "group/node relative",
|
|
3272
|
+
onMouseEnter: () => setHovered(true),
|
|
3273
|
+
onMouseLeave: () => setHovered(false),
|
|
3274
|
+
children: [
|
|
3275
|
+
itemCount !== void 0 && itemCount > 0 && /* @__PURE__ */ jsx("div", { className: "absolute -top-7 left-1/2 -translate-x-1/2 z-10", children: /* @__PURE__ */ jsxs("div", { className: "rounded-full border border-border bg-card px-2 py-0.5 text-[10px] font-medium text-muted-foreground shadow-sm", children: [
|
|
3276
|
+
itemCount,
|
|
3277
|
+
" ",
|
|
3278
|
+
itemCount !== 1 ? tr("fluidgrids.builder.nodeCard.itemCountOther", "items") : tr("fluidgrids.builder.nodeCard.itemCountOne", "item")
|
|
3279
|
+
] }) }),
|
|
3280
|
+
(onSettings || onExecute || onDelete || onToggleDisabled || infoButton) && /* @__PURE__ */ jsxs(
|
|
3281
|
+
"div",
|
|
3169
3282
|
{
|
|
3170
|
-
type: "button",
|
|
3171
|
-
title: name,
|
|
3172
|
-
onClick: (e) => {
|
|
3173
|
-
e.stopPropagation();
|
|
3174
|
-
patchData({ color: name });
|
|
3175
|
-
setShowPalette(false);
|
|
3176
|
-
},
|
|
3177
3283
|
className: cn(
|
|
3178
|
-
"
|
|
3179
|
-
|
|
3284
|
+
"absolute right-1 z-20 flex items-center gap-0.5 rounded-lg border border-border bg-card p-0.5 shadow-md transition-all duration-150",
|
|
3285
|
+
hovered ? "pointer-events-auto opacity-100" : "pointer-events-none opacity-0"
|
|
3180
3286
|
),
|
|
3181
|
-
style: {
|
|
3182
|
-
|
|
3183
|
-
|
|
3184
|
-
|
|
3185
|
-
|
|
3186
|
-
|
|
3187
|
-
|
|
3188
|
-
|
|
3189
|
-
|
|
3190
|
-
|
|
3191
|
-
|
|
3192
|
-
|
|
3193
|
-
|
|
3194
|
-
|
|
3195
|
-
|
|
3196
|
-
|
|
3197
|
-
|
|
3198
|
-
|
|
3199
|
-
|
|
3200
|
-
|
|
3201
|
-
|
|
3287
|
+
style: {
|
|
3288
|
+
// Inline offset (not a `-top-*` class) so it survives the host
|
|
3289
|
+
// app's Tailwind scan of fe-sdk source, which drops some spacing
|
|
3290
|
+
// utilities. The toolbar is ~30px tall; top:-15px centers it on
|
|
3291
|
+
// the card's top edge (half outside, half over the card).
|
|
3292
|
+
top: -15,
|
|
3293
|
+
transform: hovered ? "translateY(0)" : "translateY(4px)"
|
|
3294
|
+
},
|
|
3295
|
+
onClick: (e) => e.stopPropagation(),
|
|
3296
|
+
onDoubleClick: (e) => e.stopPropagation(),
|
|
3297
|
+
children: [
|
|
3298
|
+
onSettings && /* @__PURE__ */ jsx(
|
|
3299
|
+
ToolbarButton2,
|
|
3300
|
+
{
|
|
3301
|
+
title: tr(
|
|
3302
|
+
"fluidgrids.builder.nodeCard.configureTooltip",
|
|
3303
|
+
"Configure"
|
|
3304
|
+
),
|
|
3305
|
+
onClick: onSettings,
|
|
3306
|
+
children: /* @__PURE__ */ jsx(Icon, { icon: "lucide:sliders-horizontal", className: "size-4" })
|
|
3307
|
+
}
|
|
3202
3308
|
),
|
|
3203
|
-
|
|
3204
|
-
|
|
3205
|
-
"span",
|
|
3309
|
+
onExecute && /* @__PURE__ */ jsx(
|
|
3310
|
+
ToolbarButton2,
|
|
3206
3311
|
{
|
|
3207
|
-
|
|
3208
|
-
|
|
3312
|
+
title: tr(
|
|
3313
|
+
"fluidgrids.builder.nodeCard.executeTooltip",
|
|
3314
|
+
"Execute step"
|
|
3315
|
+
),
|
|
3316
|
+
onClick: onExecute,
|
|
3317
|
+
disabled: isExecuting,
|
|
3318
|
+
children: isExecuting ? /* @__PURE__ */ jsx(
|
|
3319
|
+
Icon,
|
|
3320
|
+
{
|
|
3321
|
+
icon: "svg-spinners:ring-resize",
|
|
3322
|
+
className: "size-4 text-[var(--color-status-success-text)]"
|
|
3323
|
+
}
|
|
3324
|
+
) : /* @__PURE__ */ jsx(
|
|
3325
|
+
Icon,
|
|
3326
|
+
{
|
|
3327
|
+
icon: "mdi:play",
|
|
3328
|
+
className: "size-4 text-[var(--color-status-success-text)]"
|
|
3329
|
+
}
|
|
3330
|
+
)
|
|
3209
3331
|
}
|
|
3210
|
-
)
|
|
3211
|
-
}
|
|
3212
|
-
),
|
|
3213
|
-
/* @__PURE__ */ jsx(
|
|
3214
|
-
ToolbarButton,
|
|
3215
|
-
{
|
|
3216
|
-
title: tr(
|
|
3217
|
-
"fluidgrids.builder.stickyNote.duplicateNoteTooltip",
|
|
3218
|
-
"Duplicate note"
|
|
3219
3332
|
),
|
|
3220
|
-
|
|
3221
|
-
|
|
3222
|
-
|
|
3223
|
-
|
|
3224
|
-
|
|
3225
|
-
|
|
3226
|
-
|
|
3227
|
-
|
|
3228
|
-
|
|
3229
|
-
|
|
3333
|
+
infoButton,
|
|
3334
|
+
onToggleDisabled && /* @__PURE__ */ jsx(
|
|
3335
|
+
ToolbarButton2,
|
|
3336
|
+
{
|
|
3337
|
+
title: disabled ? tr(
|
|
3338
|
+
"fluidgrids.builder.nodeCard.enableTooltip",
|
|
3339
|
+
"Enable node"
|
|
3340
|
+
) : tr(
|
|
3341
|
+
"fluidgrids.builder.nodeCard.disableTooltip",
|
|
3342
|
+
"Disable node (skip on run)"
|
|
3343
|
+
),
|
|
3344
|
+
onClick: onToggleDisabled,
|
|
3345
|
+
children: /* @__PURE__ */ jsx(
|
|
3346
|
+
Icon,
|
|
3347
|
+
{
|
|
3348
|
+
icon: disabled ? "mdi:power" : "mdi:power-off",
|
|
3349
|
+
className: cn(
|
|
3350
|
+
"size-4",
|
|
3351
|
+
disabled ? "text-[var(--color-status-warning-text)]" : "text-muted-foreground"
|
|
3352
|
+
)
|
|
3353
|
+
}
|
|
3354
|
+
)
|
|
3355
|
+
}
|
|
3230
3356
|
),
|
|
3231
|
-
|
|
3232
|
-
|
|
3233
|
-
|
|
3234
|
-
|
|
3235
|
-
|
|
3236
|
-
|
|
3237
|
-
|
|
3238
|
-
|
|
3239
|
-
|
|
3240
|
-
|
|
3241
|
-
|
|
3242
|
-
|
|
3243
|
-
|
|
3244
|
-
|
|
3245
|
-
|
|
3246
|
-
|
|
3247
|
-
|
|
3248
|
-
|
|
3249
|
-
onDoubleClick: (e) => {
|
|
3250
|
-
e.stopPropagation();
|
|
3251
|
-
startEditing();
|
|
3252
|
-
},
|
|
3253
|
-
children: editing ? /* @__PURE__ */ jsx(
|
|
3254
|
-
"textarea",
|
|
3255
|
-
{
|
|
3256
|
-
ref: textareaRef,
|
|
3257
|
-
value: draft,
|
|
3258
|
-
onChange: (e) => setDraft(e.target.value),
|
|
3259
|
-
onBlur: commitEdit,
|
|
3260
|
-
onKeyDown: (e) => {
|
|
3261
|
-
e.stopPropagation();
|
|
3262
|
-
if (e.key === "Escape") cancelEdit();
|
|
3263
|
-
if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) commitEdit();
|
|
3264
|
-
},
|
|
3265
|
-
placeholder: tr(
|
|
3266
|
-
"fluidgrids.builder.stickyNote.editorPlaceholder",
|
|
3267
|
-
"# Note\nSupports **markdown** \u2014 headings, lists, links\u2026"
|
|
3268
|
-
),
|
|
3269
|
-
className: "nodrag nowheel h-full w-full resize-none bg-transparent p-3 font-mono text-xs leading-relaxed text-foreground outline-none placeholder:text-muted-foreground/60"
|
|
3357
|
+
onDelete && /* @__PURE__ */ jsx(
|
|
3358
|
+
ToolbarButton2,
|
|
3359
|
+
{
|
|
3360
|
+
title: tr(
|
|
3361
|
+
"fluidgrids.builder.nodeCard.deleteTooltip",
|
|
3362
|
+
"Delete"
|
|
3363
|
+
),
|
|
3364
|
+
onClick: onDelete,
|
|
3365
|
+
children: /* @__PURE__ */ jsx(
|
|
3366
|
+
Icon,
|
|
3367
|
+
{
|
|
3368
|
+
icon: "mynaui:trash",
|
|
3369
|
+
className: "size-4 text-[var(--color-status-error-text)]"
|
|
3370
|
+
}
|
|
3371
|
+
)
|
|
3372
|
+
}
|
|
3373
|
+
)
|
|
3374
|
+
]
|
|
3270
3375
|
}
|
|
3271
|
-
)
|
|
3272
|
-
|
|
3273
|
-
{
|
|
3274
|
-
|
|
3275
|
-
|
|
3276
|
-
|
|
3277
|
-
|
|
3278
|
-
|
|
3279
|
-
|
|
3280
|
-
|
|
3281
|
-
|
|
3282
|
-
"
|
|
3376
|
+
),
|
|
3377
|
+
disabled && /* @__PURE__ */ jsx("div", { className: "absolute -top-2 right-2 z-10", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1 rounded-full border border-[var(--color-status-warning-border)] bg-[var(--color-status-warning-bg)] px-1.5 py-0.5 text-[9px] font-semibold uppercase tracking-wide text-[var(--color-status-warning-text)] shadow-sm", children: [
|
|
3378
|
+
/* @__PURE__ */ jsx(Icon, { icon: "mdi:power-off", className: "size-2.5" }),
|
|
3379
|
+
tr("fluidgrids.builder.nodeCard.disabledBadge", "Disabled")
|
|
3380
|
+
] }) }),
|
|
3381
|
+
/* @__PURE__ */ jsxs(
|
|
3382
|
+
"div",
|
|
3383
|
+
{
|
|
3384
|
+
ref,
|
|
3385
|
+
"data-selected": selected,
|
|
3386
|
+
className: cn(
|
|
3387
|
+
"relative w-72 rounded-xl border border-border bg-card shadow-sm will-change-transform",
|
|
3388
|
+
statusRing,
|
|
3389
|
+
executionStatus === "success" && "animate-success-glow",
|
|
3390
|
+
disabled && "opacity-50 grayscale-[0.6]",
|
|
3391
|
+
className
|
|
3283
3392
|
),
|
|
3284
|
-
|
|
3393
|
+
style: {
|
|
3394
|
+
borderTop: `3px solid ${accent}`,
|
|
3395
|
+
transition: "transform 0.15s ease, box-shadow 0.15s ease",
|
|
3396
|
+
transform: hovered ? "translateY(-2px)" : "none",
|
|
3397
|
+
boxShadow: hovered ? "0 2px 4px rgba(16,24,40,0.06), 0 16px 34px -12px rgba(16,24,40,0.24)" : void 0
|
|
3398
|
+
},
|
|
3399
|
+
...props,
|
|
3400
|
+
children: [
|
|
3401
|
+
/* @__PURE__ */ jsx(
|
|
3402
|
+
"div",
|
|
3403
|
+
{
|
|
3404
|
+
className: "pointer-events-none absolute -inset-0.5 transition-opacity duration-150",
|
|
3405
|
+
style: {
|
|
3406
|
+
borderRadius: 14,
|
|
3407
|
+
opacity: selected || hovered ? 1 : 0,
|
|
3408
|
+
boxShadow: selected ? `0 0 0 2px ${accent}, 0 0 0 5px ${tint(accent, 18)}` : `0 0 0 2px ${accent}`
|
|
3409
|
+
}
|
|
3410
|
+
}
|
|
3411
|
+
),
|
|
3412
|
+
isTrigger && /* @__PURE__ */ jsx(
|
|
3413
|
+
"div",
|
|
3414
|
+
{
|
|
3415
|
+
className: "absolute -top-2 -left-2 z-10 flex size-5 items-center justify-center rounded-full shadow-md ring-2 ring-card",
|
|
3416
|
+
style: { backgroundColor: "var(--color-status-success-text)" },
|
|
3417
|
+
children: /* @__PURE__ */ jsx(
|
|
3418
|
+
Icon,
|
|
3419
|
+
{
|
|
3420
|
+
icon: "mdi:lightning-bolt",
|
|
3421
|
+
className: "size-3",
|
|
3422
|
+
style: { color: "var(--color-status-success-text-inverse)" }
|
|
3423
|
+
}
|
|
3424
|
+
)
|
|
3425
|
+
}
|
|
3426
|
+
),
|
|
3427
|
+
children
|
|
3428
|
+
]
|
|
3285
3429
|
}
|
|
3286
|
-
)
|
|
3287
|
-
|
|
3288
|
-
|
|
3289
|
-
|
|
3290
|
-
|
|
3291
|
-
|
|
3292
|
-
|
|
3293
|
-
);
|
|
3294
|
-
}
|
|
3295
|
-
var StickyNoteNode = memo(StickyNoteNodeComponent);
|
|
3296
|
-
function buildStickyNode(position) {
|
|
3297
|
-
return {
|
|
3298
|
-
id: nanoid(),
|
|
3299
|
-
type: STICKY_NOTE_TYPE,
|
|
3300
|
-
position,
|
|
3301
|
-
width: STICKY_DEFAULT_SIZE.width,
|
|
3302
|
-
height: STICKY_DEFAULT_SIZE.height,
|
|
3303
|
-
// Render under regular nodes and edges — notes are a background layer.
|
|
3304
|
-
zIndex: -1,
|
|
3305
|
-
selected: true,
|
|
3306
|
-
data: { content: "", color: DEFAULT_STICKY_COLOR }
|
|
3307
|
-
};
|
|
3308
|
-
}
|
|
3309
|
-
function getNodeDetail(nodeType) {
|
|
3310
|
-
const node = NODES.find((node2) => node2.type === nodeType);
|
|
3311
|
-
if (!node) {
|
|
3312
|
-
return {
|
|
3313
|
-
icon: "mynaui:keyboard-solid",
|
|
3314
|
-
title: nodeType ?? "Unknown",
|
|
3315
|
-
description: "",
|
|
3316
|
-
gradientColor: "gray"
|
|
3317
|
-
};
|
|
3430
|
+
),
|
|
3431
|
+
executionStatus === "success" && /* @__PURE__ */ jsx(StatusCorner, { icon: "mdi:check", tone: "success" }),
|
|
3432
|
+
executionStatus === "error" && /* @__PURE__ */ jsx(StatusCorner, { icon: "mdi:close", tone: "error" }),
|
|
3433
|
+
executionStatus === "running" && /* @__PURE__ */ jsx(StatusCorner, { icon: "svg-spinners:ring-resize", tone: "success" })
|
|
3434
|
+
]
|
|
3435
|
+
}
|
|
3436
|
+
);
|
|
3318
3437
|
}
|
|
3319
|
-
|
|
3320
|
-
|
|
3321
|
-
function
|
|
3322
|
-
|
|
3323
|
-
|
|
3324
|
-
|
|
3325
|
-
|
|
3326
|
-
|
|
3327
|
-
|
|
3328
|
-
|
|
3329
|
-
|
|
3330
|
-
|
|
3331
|
-
|
|
3332
|
-
|
|
3333
|
-
|
|
3334
|
-
|
|
3335
|
-
|
|
3438
|
+
);
|
|
3439
|
+
NodeCard.displayName = "NodeCard";
|
|
3440
|
+
function ToolbarButton2({
|
|
3441
|
+
title,
|
|
3442
|
+
onClick,
|
|
3443
|
+
disabled,
|
|
3444
|
+
children
|
|
3445
|
+
}) {
|
|
3446
|
+
return /* @__PURE__ */ jsx(
|
|
3447
|
+
"button",
|
|
3448
|
+
{
|
|
3449
|
+
type: "button",
|
|
3450
|
+
title,
|
|
3451
|
+
disabled,
|
|
3452
|
+
onClick: (e) => {
|
|
3453
|
+
e.stopPropagation();
|
|
3454
|
+
onClick?.();
|
|
3455
|
+
},
|
|
3456
|
+
className: "flex size-6 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-muted hover:text-foreground disabled:opacity-50",
|
|
3457
|
+
children
|
|
3336
3458
|
}
|
|
3337
|
-
}
|
|
3338
|
-
`;
|
|
3339
|
-
var cache = /* @__PURE__ */ new Map();
|
|
3340
|
-
var inFlight = /* @__PURE__ */ new Map();
|
|
3341
|
-
function fetchDocsUrl(nodeType, nodeVersion) {
|
|
3342
|
-
const key = `${nodeType}@${nodeVersion}`;
|
|
3343
|
-
if (cache.has(key)) return Promise.resolve(cache.get(key));
|
|
3344
|
-
const existing = inFlight.get(key);
|
|
3345
|
-
if (existing) return existing;
|
|
3346
|
-
const client = getFluidGridsClientSafe();
|
|
3347
|
-
if (!client) {
|
|
3348
|
-
cache.set(key, void 0);
|
|
3349
|
-
return Promise.resolve(void 0);
|
|
3350
|
-
}
|
|
3351
|
-
const promise = client.query({
|
|
3352
|
-
query: NODE_DOCS_URL,
|
|
3353
|
-
variables: { nodeType, nodeVersion },
|
|
3354
|
-
fetchPolicy: "cache-first"
|
|
3355
|
-
}).then((result) => result.data?.nodeEndpointConfig?.docsUrl ?? void 0).catch(() => void 0).finally(() => inFlight.delete(key));
|
|
3356
|
-
inFlight.set(key, promise);
|
|
3357
|
-
return promise.then((url) => {
|
|
3358
|
-
cache.set(key, url);
|
|
3359
|
-
return url;
|
|
3360
|
-
});
|
|
3361
|
-
}
|
|
3362
|
-
function useNodeDocsUrl(nodeType, nodeVersion) {
|
|
3363
|
-
const [docsUrl, setDocsUrl] = useState(
|
|
3364
|
-
() => nodeType && nodeVersion ? cache.get(`${nodeType}@${nodeVersion}`) : void 0
|
|
3365
3459
|
);
|
|
3366
|
-
useEffect(() => {
|
|
3367
|
-
if (!nodeType || !nodeVersion) {
|
|
3368
|
-
setDocsUrl(void 0);
|
|
3369
|
-
return;
|
|
3370
|
-
}
|
|
3371
|
-
let cancelled = false;
|
|
3372
|
-
fetchDocsUrl(nodeType, nodeVersion).then((url) => {
|
|
3373
|
-
if (!cancelled) setDocsUrl(url);
|
|
3374
|
-
});
|
|
3375
|
-
return () => {
|
|
3376
|
-
cancelled = true;
|
|
3377
|
-
};
|
|
3378
|
-
}, [nodeType, nodeVersion]);
|
|
3379
|
-
return docsUrl;
|
|
3380
|
-
}
|
|
3381
|
-
function isImageIcon(icon) {
|
|
3382
|
-
return !!icon && (/^https?:\/\//.test(icon) || icon.startsWith("data:"));
|
|
3383
3460
|
}
|
|
3384
|
-
function
|
|
3461
|
+
function StatusCorner({
|
|
3385
3462
|
icon,
|
|
3386
|
-
|
|
3387
|
-
className,
|
|
3388
|
-
fallbackIcon = "mynaui:puzzle"
|
|
3463
|
+
tone
|
|
3389
3464
|
}) {
|
|
3390
|
-
|
|
3391
|
-
|
|
3392
|
-
|
|
3393
|
-
|
|
3394
|
-
|
|
3395
|
-
|
|
3396
|
-
|
|
3397
|
-
}
|
|
3398
|
-
|
|
3399
|
-
}
|
|
3400
|
-
return /* @__PURE__ */ jsx(Icon, { icon: icon || fallbackIcon, className });
|
|
3401
|
-
}
|
|
3402
|
-
var BRAND_ACCENTS = [
|
|
3403
|
-
[/gmail|google/, "#ea4335"],
|
|
3404
|
-
[/slack/, "#611f69"],
|
|
3405
|
-
[/github/, "#334155"],
|
|
3406
|
-
[/gitlab/, "#fc6d26"],
|
|
3407
|
-
[/stripe/, "#635bff"],
|
|
3408
|
-
[/discord/, "#5865f2"],
|
|
3409
|
-
[/notion/, "#111827"],
|
|
3410
|
-
[/telegram/, "#2aabee"],
|
|
3411
|
-
[/whatsapp/, "#25d366"],
|
|
3412
|
-
[/openai/, "#10a37f"],
|
|
3413
|
-
[/anthropic|claude/, "#d97757"],
|
|
3414
|
-
[/twilio/, "#f22f46"],
|
|
3415
|
-
[/sheet|excel/, "#21a366"],
|
|
3416
|
-
[/http|api|webhook|rest/, "#6366f1"],
|
|
3417
|
-
[/db|sql|postgres|mysql|mongo/, "#0ea5e9"]
|
|
3418
|
-
];
|
|
3419
|
-
var HASH_PALETTE = [
|
|
3420
|
-
"#6366f1",
|
|
3421
|
-
"#0ea5e9",
|
|
3422
|
-
"#10b981",
|
|
3423
|
-
"#f59e0b",
|
|
3424
|
-
"#ef4444",
|
|
3425
|
-
"#a855f7",
|
|
3426
|
-
"#ec4899",
|
|
3427
|
-
"#14b8a6",
|
|
3428
|
-
"#f97316",
|
|
3429
|
-
"#8b5cf6"
|
|
3430
|
-
];
|
|
3431
|
-
function nodeAccent(key) {
|
|
3432
|
-
const k = (key || "").toLowerCase();
|
|
3433
|
-
for (const [re, hex] of BRAND_ACCENTS) if (re.test(k)) return hex;
|
|
3434
|
-
let h = 0;
|
|
3435
|
-
for (let i = 0; i < k.length; i++) h = h * 31 + k.charCodeAt(i) >>> 0;
|
|
3436
|
-
return HASH_PALETTE[h % HASH_PALETTE.length];
|
|
3437
|
-
}
|
|
3438
|
-
function tint(hex, pct = 14) {
|
|
3439
|
-
return `color-mix(in srgb, ${hex} ${pct}%, transparent)`;
|
|
3465
|
+
const bg = tone === "success" ? "var(--color-status-success-bg)" : "var(--color-status-error-bg)";
|
|
3466
|
+
const fg = tone === "success" ? "var(--color-status-success-text-inverse)" : "var(--color-status-error-text-inverse)";
|
|
3467
|
+
return /* @__PURE__ */ jsx("div", { className: "absolute -bottom-1.5 -right-1.5 z-10", children: /* @__PURE__ */ jsx(
|
|
3468
|
+
"div",
|
|
3469
|
+
{
|
|
3470
|
+
className: "rounded-full p-0.5 shadow-md animate-in zoom-in duration-200",
|
|
3471
|
+
style: { backgroundColor: bg },
|
|
3472
|
+
children: /* @__PURE__ */ jsx(Icon, { icon, className: "size-3", style: { color: fg } })
|
|
3473
|
+
}
|
|
3474
|
+
) });
|
|
3440
3475
|
}
|
|
3441
|
-
|
|
3442
|
-
|
|
3443
|
-
|
|
3444
|
-
|
|
3445
|
-
|
|
3446
|
-
|
|
3447
|
-
|
|
3448
|
-
|
|
3449
|
-
|
|
3450
|
-
|
|
3451
|
-
|
|
3452
|
-
|
|
3453
|
-
|
|
3454
|
-
|
|
3455
|
-
|
|
3456
|
-
|
|
3457
|
-
|
|
3458
|
-
|
|
3459
|
-
|
|
3460
|
-
|
|
3461
|
-
|
|
3462
|
-
|
|
3463
|
-
|
|
3464
|
-
|
|
3465
|
-
|
|
3476
|
+
var NodeCardHeader = React2.forwardRef(
|
|
3477
|
+
({
|
|
3478
|
+
icon,
|
|
3479
|
+
appName,
|
|
3480
|
+
accentKey,
|
|
3481
|
+
missingCredentials = false,
|
|
3482
|
+
label,
|
|
3483
|
+
isRenaming = false,
|
|
3484
|
+
onStartRename,
|
|
3485
|
+
onCommitRename,
|
|
3486
|
+
onCancelRename
|
|
3487
|
+
}, ref) => {
|
|
3488
|
+
const tr = useTr();
|
|
3489
|
+
const accent = nodeAccent(accentKey);
|
|
3490
|
+
return /* @__PURE__ */ jsxs("div", { ref, className: "flex items-center gap-2.5 px-3 pb-3 pt-4", children: [
|
|
3491
|
+
/* @__PURE__ */ jsx(
|
|
3492
|
+
"div",
|
|
3493
|
+
{
|
|
3494
|
+
className: "flex size-8 shrink-0 items-center justify-center rounded-lg",
|
|
3495
|
+
style: {
|
|
3496
|
+
backgroundColor: tint(accent, 13),
|
|
3497
|
+
color: accent,
|
|
3498
|
+
border: `1px solid ${tint(accent, 26)}`
|
|
3499
|
+
},
|
|
3500
|
+
children: /* @__PURE__ */ jsx(NodeTypeIcon, { icon, alt: appName, className: "size-[18px]" })
|
|
3501
|
+
}
|
|
3502
|
+
),
|
|
3503
|
+
onStartRename && onCommitRename && onCancelRename ? /* @__PURE__ */ jsx(
|
|
3504
|
+
InlineEditableLabel,
|
|
3505
|
+
{
|
|
3506
|
+
value: label ?? "",
|
|
3507
|
+
placeholder: appName,
|
|
3508
|
+
editing: isRenaming,
|
|
3509
|
+
onStartEdit: onStartRename,
|
|
3510
|
+
onCommit: onCommitRename,
|
|
3511
|
+
onCancel: onCancelRename,
|
|
3512
|
+
className: "min-w-0 flex-1 truncate text-[11px] font-semibold uppercase tracking-wide text-muted-foreground",
|
|
3513
|
+
inputClassName: "text-[11px] font-semibold uppercase tracking-wide"
|
|
3514
|
+
}
|
|
3515
|
+
) : /* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate text-[11px] font-semibold uppercase tracking-wide text-muted-foreground", children: label || appName }),
|
|
3516
|
+
missingCredentials && /* @__PURE__ */ jsx(
|
|
3517
|
+
"span",
|
|
3518
|
+
{
|
|
3519
|
+
title: tr(
|
|
3520
|
+
"fluidgrids.builder.nodeCard.missingCredentialsTooltip",
|
|
3521
|
+
"Credentials not configured"
|
|
3522
|
+
),
|
|
3523
|
+
className: "flex items-center text-[var(--color-status-warning-text)]",
|
|
3524
|
+
"aria-label": tr(
|
|
3525
|
+
"fluidgrids.builder.nodeCard.missingCredentialsAriaLabel",
|
|
3526
|
+
"Credentials not configured"
|
|
3527
|
+
),
|
|
3528
|
+
children: /* @__PURE__ */ jsx(Icon, { icon: "lucide:alert-triangle", className: "size-4" })
|
|
3529
|
+
}
|
|
3530
|
+
)
|
|
3531
|
+
] });
|
|
3532
|
+
}
|
|
3533
|
+
);
|
|
3534
|
+
NodeCardHeader.displayName = "NodeCardHeader";
|
|
3535
|
+
var NodeCardBody = React2.forwardRef(
|
|
3536
|
+
({ operationName, description }, ref) => {
|
|
3537
|
+
const tr = useTr();
|
|
3538
|
+
return /* @__PURE__ */ jsxs(
|
|
3539
|
+
"div",
|
|
3466
3540
|
{
|
|
3467
|
-
ref
|
|
3468
|
-
|
|
3469
|
-
|
|
3470
|
-
|
|
3471
|
-
|
|
3472
|
-
|
|
3473
|
-
|
|
3474
|
-
e.stopPropagation();
|
|
3475
|
-
if (e.key === "Enter") {
|
|
3476
|
-
e.preventDefault();
|
|
3477
|
-
onCommit(draft.trim());
|
|
3478
|
-
} else if (e.key === "Escape") {
|
|
3479
|
-
e.preventDefault();
|
|
3480
|
-
onCancel();
|
|
3481
|
-
}
|
|
3482
|
-
},
|
|
3483
|
-
onBlur: () => onCommit(draft.trim()),
|
|
3484
|
-
className: cn(
|
|
3485
|
-
"min-w-0 flex-1 rounded border border-primary/50 bg-card px-1 text-inherit outline-none",
|
|
3486
|
-
inputClassName
|
|
3487
|
-
)
|
|
3541
|
+
ref,
|
|
3542
|
+
className: "rounded-b-xl border-t border-border/60 px-3 pb-3 pt-2.5",
|
|
3543
|
+
children: [
|
|
3544
|
+
/* @__PURE__ */ jsx("div", { className: "text-[10px] font-semibold uppercase tracking-wider text-muted-foreground", children: tr("fluidgrids.builder.nodeCard.operationLabel", "Operation") }),
|
|
3545
|
+
/* @__PURE__ */ jsx("div", { className: "mt-0.5 truncate font-mono text-[13px] font-bold text-card-foreground", children: operationName }),
|
|
3546
|
+
description ? /* @__PURE__ */ jsx("p", { className: "mt-1 line-clamp-2 text-xs leading-snug text-muted-foreground", children: description }) : null
|
|
3547
|
+
]
|
|
3488
3548
|
}
|
|
3489
3549
|
);
|
|
3490
3550
|
}
|
|
3491
|
-
|
|
3492
|
-
|
|
3551
|
+
);
|
|
3552
|
+
NodeCardBody.displayName = "NodeCardBody";
|
|
3553
|
+
function CustomHandle({
|
|
3554
|
+
className,
|
|
3555
|
+
isConnectable,
|
|
3556
|
+
accent,
|
|
3557
|
+
...props
|
|
3558
|
+
}) {
|
|
3559
|
+
const { nodeLookup, edges } = useStore(({ nodeLookup: nodeLookup2, edges: edges2 }) => ({
|
|
3560
|
+
nodeLookup: nodeLookup2,
|
|
3561
|
+
edges: edges2
|
|
3562
|
+
}));
|
|
3563
|
+
const nodeId = useNodeId();
|
|
3564
|
+
const isHandleConnectable = useMemo(() => {
|
|
3565
|
+
if (!nodeId) return false;
|
|
3566
|
+
const node = nodeLookup.get(nodeId);
|
|
3567
|
+
if (!node) return false;
|
|
3568
|
+
const connectedEdges = getConnectedEdges([node], edges);
|
|
3569
|
+
if (typeof isConnectable === "function")
|
|
3570
|
+
return isConnectable({ node, connectedEdges });
|
|
3571
|
+
if (typeof isConnectable === "number")
|
|
3572
|
+
return connectedEdges.length < isConnectable;
|
|
3573
|
+
return isConnectable;
|
|
3574
|
+
}, [edges, isConnectable, nodeId, nodeLookup]);
|
|
3575
|
+
return /* @__PURE__ */ jsx("span", { children: /* @__PURE__ */ jsx(
|
|
3576
|
+
Handle,
|
|
3493
3577
|
{
|
|
3494
|
-
className: cn(
|
|
3495
|
-
|
|
3496
|
-
"
|
|
3497
|
-
|
|
3578
|
+
className: cn(
|
|
3579
|
+
"cursor-crosshair transition-transform hover:scale-110",
|
|
3580
|
+
!accent && "size-3 border-2 border-card-foreground/40 hover:border-primary/50",
|
|
3581
|
+
className
|
|
3498
3582
|
),
|
|
3499
|
-
|
|
3500
|
-
|
|
3501
|
-
|
|
3502
|
-
|
|
3503
|
-
|
|
3583
|
+
isConnectable: isHandleConnectable,
|
|
3584
|
+
style: accent ? {
|
|
3585
|
+
width: 14,
|
|
3586
|
+
height: 14,
|
|
3587
|
+
borderRadius: "50%",
|
|
3588
|
+
background: "var(--card, #fff)",
|
|
3589
|
+
border: `2.5px solid ${accent}`,
|
|
3590
|
+
boxShadow: `0 0 0 3px color-mix(in srgb, ${accent} 12%, transparent)`
|
|
3591
|
+
} : void 0,
|
|
3592
|
+
...props
|
|
3504
3593
|
}
|
|
3505
|
-
);
|
|
3506
|
-
}
|
|
3507
|
-
var NodeCard = React2.forwardRef(
|
|
3508
|
-
({
|
|
3509
|
-
className,
|
|
3510
|
-
selected = false,
|
|
3511
|
-
executionStatus = "idle",
|
|
3512
|
-
itemCount,
|
|
3513
|
-
isTrigger = false,
|
|
3514
|
-
accent = "#3b82f6",
|
|
3515
|
-
onSettings,
|
|
3516
|
-
onExecute,
|
|
3517
|
-
onDelete,
|
|
3518
|
-
isExecuting = false,
|
|
3519
|
-
infoButton,
|
|
3520
|
-
disabled = false,
|
|
3521
|
-
onToggleDisabled,
|
|
3522
|
-
children,
|
|
3523
|
-
...props
|
|
3524
|
-
}, ref) => {
|
|
3525
|
-
const tr = useTr();
|
|
3526
|
-
const [hovered, setHovered] = React2.useState(false);
|
|
3527
|
-
const statusRing = executionStatus === "running" ? "ring-2 ring-[var(--color-status-success-border)] animate-pulse-border" : executionStatus === "success" ? "ring-2 ring-[var(--color-status-success-border)]" : executionStatus === "error" ? "ring-2 ring-[var(--color-status-error-border)]" : "";
|
|
3528
|
-
return /* @__PURE__ */ jsxs(
|
|
3529
|
-
"div",
|
|
3530
|
-
{
|
|
3531
|
-
className: "group/node relative",
|
|
3532
|
-
onMouseEnter: () => setHovered(true),
|
|
3533
|
-
onMouseLeave: () => setHovered(false),
|
|
3534
|
-
children: [
|
|
3535
|
-
itemCount !== void 0 && itemCount > 0 && /* @__PURE__ */ jsx("div", { className: "absolute -top-7 left-1/2 -translate-x-1/2 z-10", children: /* @__PURE__ */ jsxs("div", { className: "rounded-full border border-border bg-card px-2 py-0.5 text-[10px] font-medium text-muted-foreground shadow-sm", children: [
|
|
3536
|
-
itemCount,
|
|
3537
|
-
" ",
|
|
3538
|
-
itemCount !== 1 ? tr("fluidgrids.builder.nodeCard.itemCountOther", "items") : tr("fluidgrids.builder.nodeCard.itemCountOne", "item")
|
|
3539
|
-
] }) }),
|
|
3540
|
-
(onSettings || onExecute || onDelete || onToggleDisabled || infoButton) && /* @__PURE__ */ jsxs(
|
|
3541
|
-
"div",
|
|
3542
|
-
{
|
|
3543
|
-
className: cn(
|
|
3544
|
-
"absolute right-1 z-20 flex items-center gap-0.5 rounded-lg border border-border bg-card p-0.5 shadow-md transition-all duration-150",
|
|
3545
|
-
hovered ? "pointer-events-auto opacity-100" : "pointer-events-none opacity-0"
|
|
3546
|
-
),
|
|
3547
|
-
style: {
|
|
3548
|
-
// Inline offset (not a `-top-*` class) so it survives the host
|
|
3549
|
-
// app's Tailwind scan of fe-sdk source, which drops some spacing
|
|
3550
|
-
// utilities. The toolbar is ~30px tall; top:-15px centers it on
|
|
3551
|
-
// the card's top edge (half outside, half over the card).
|
|
3552
|
-
top: -15,
|
|
3553
|
-
transform: hovered ? "translateY(0)" : "translateY(4px)"
|
|
3554
|
-
},
|
|
3555
|
-
onClick: (e) => e.stopPropagation(),
|
|
3556
|
-
onDoubleClick: (e) => e.stopPropagation(),
|
|
3557
|
-
children: [
|
|
3558
|
-
onSettings && /* @__PURE__ */ jsx(
|
|
3559
|
-
ToolbarButton2,
|
|
3560
|
-
{
|
|
3561
|
-
title: tr(
|
|
3562
|
-
"fluidgrids.builder.nodeCard.configureTooltip",
|
|
3563
|
-
"Configure"
|
|
3564
|
-
),
|
|
3565
|
-
onClick: onSettings,
|
|
3566
|
-
children: /* @__PURE__ */ jsx(Icon, { icon: "lucide:sliders-horizontal", className: "size-4" })
|
|
3567
|
-
}
|
|
3568
|
-
),
|
|
3569
|
-
onExecute && /* @__PURE__ */ jsx(
|
|
3570
|
-
ToolbarButton2,
|
|
3571
|
-
{
|
|
3572
|
-
title: tr(
|
|
3573
|
-
"fluidgrids.builder.nodeCard.executeTooltip",
|
|
3574
|
-
"Execute step"
|
|
3575
|
-
),
|
|
3576
|
-
onClick: onExecute,
|
|
3577
|
-
disabled: isExecuting,
|
|
3578
|
-
children: isExecuting ? /* @__PURE__ */ jsx(
|
|
3579
|
-
Icon,
|
|
3580
|
-
{
|
|
3581
|
-
icon: "svg-spinners:ring-resize",
|
|
3582
|
-
className: "size-4 text-[var(--color-status-success-text)]"
|
|
3583
|
-
}
|
|
3584
|
-
) : /* @__PURE__ */ jsx(
|
|
3585
|
-
Icon,
|
|
3586
|
-
{
|
|
3587
|
-
icon: "mdi:play",
|
|
3588
|
-
className: "size-4 text-[var(--color-status-success-text)]"
|
|
3589
|
-
}
|
|
3590
|
-
)
|
|
3591
|
-
}
|
|
3592
|
-
),
|
|
3593
|
-
infoButton,
|
|
3594
|
-
onToggleDisabled && /* @__PURE__ */ jsx(
|
|
3595
|
-
ToolbarButton2,
|
|
3596
|
-
{
|
|
3597
|
-
title: disabled ? tr(
|
|
3598
|
-
"fluidgrids.builder.nodeCard.enableTooltip",
|
|
3599
|
-
"Enable node"
|
|
3600
|
-
) : tr(
|
|
3601
|
-
"fluidgrids.builder.nodeCard.disableTooltip",
|
|
3602
|
-
"Disable node (skip on run)"
|
|
3603
|
-
),
|
|
3604
|
-
onClick: onToggleDisabled,
|
|
3605
|
-
children: /* @__PURE__ */ jsx(
|
|
3606
|
-
Icon,
|
|
3607
|
-
{
|
|
3608
|
-
icon: disabled ? "mdi:power" : "mdi:power-off",
|
|
3609
|
-
className: cn(
|
|
3610
|
-
"size-4",
|
|
3611
|
-
disabled ? "text-[var(--color-status-warning-text)]" : "text-muted-foreground"
|
|
3612
|
-
)
|
|
3613
|
-
}
|
|
3614
|
-
)
|
|
3615
|
-
}
|
|
3616
|
-
),
|
|
3617
|
-
onDelete && /* @__PURE__ */ jsx(
|
|
3618
|
-
ToolbarButton2,
|
|
3619
|
-
{
|
|
3620
|
-
title: tr(
|
|
3621
|
-
"fluidgrids.builder.nodeCard.deleteTooltip",
|
|
3622
|
-
"Delete"
|
|
3623
|
-
),
|
|
3624
|
-
onClick: onDelete,
|
|
3625
|
-
children: /* @__PURE__ */ jsx(
|
|
3626
|
-
Icon,
|
|
3627
|
-
{
|
|
3628
|
-
icon: "mynaui:trash",
|
|
3629
|
-
className: "size-4 text-[var(--color-status-error-text)]"
|
|
3630
|
-
}
|
|
3631
|
-
)
|
|
3632
|
-
}
|
|
3633
|
-
)
|
|
3634
|
-
]
|
|
3635
|
-
}
|
|
3636
|
-
),
|
|
3637
|
-
disabled && /* @__PURE__ */ jsx("div", { className: "absolute -top-2 right-2 z-10", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1 rounded-full border border-[var(--color-status-warning-border)] bg-[var(--color-status-warning-bg)] px-1.5 py-0.5 text-[9px] font-semibold uppercase tracking-wide text-[var(--color-status-warning-text)] shadow-sm", children: [
|
|
3638
|
-
/* @__PURE__ */ jsx(Icon, { icon: "mdi:power-off", className: "size-2.5" }),
|
|
3639
|
-
tr("fluidgrids.builder.nodeCard.disabledBadge", "Disabled")
|
|
3640
|
-
] }) }),
|
|
3641
|
-
/* @__PURE__ */ jsxs(
|
|
3642
|
-
"div",
|
|
3643
|
-
{
|
|
3644
|
-
ref,
|
|
3645
|
-
"data-selected": selected,
|
|
3646
|
-
className: cn(
|
|
3647
|
-
"relative w-72 rounded-xl border border-border bg-card shadow-sm will-change-transform",
|
|
3648
|
-
statusRing,
|
|
3649
|
-
executionStatus === "success" && "animate-success-glow",
|
|
3650
|
-
disabled && "opacity-50 grayscale-[0.6]",
|
|
3651
|
-
className
|
|
3652
|
-
),
|
|
3653
|
-
style: {
|
|
3654
|
-
borderTop: `3px solid ${accent}`,
|
|
3655
|
-
transition: "transform 0.15s ease, box-shadow 0.15s ease",
|
|
3656
|
-
transform: hovered ? "translateY(-2px)" : "none",
|
|
3657
|
-
boxShadow: hovered ? "0 2px 4px rgba(16,24,40,0.06), 0 16px 34px -12px rgba(16,24,40,0.24)" : void 0
|
|
3658
|
-
},
|
|
3659
|
-
...props,
|
|
3660
|
-
children: [
|
|
3661
|
-
/* @__PURE__ */ jsx(
|
|
3662
|
-
"div",
|
|
3663
|
-
{
|
|
3664
|
-
className: "pointer-events-none absolute -inset-0.5 transition-opacity duration-150",
|
|
3665
|
-
style: {
|
|
3666
|
-
borderRadius: 14,
|
|
3667
|
-
opacity: selected || hovered ? 1 : 0,
|
|
3668
|
-
boxShadow: selected ? `0 0 0 2px ${accent}, 0 0 0 5px ${tint(accent, 18)}` : `0 0 0 2px ${accent}`
|
|
3669
|
-
}
|
|
3670
|
-
}
|
|
3671
|
-
),
|
|
3672
|
-
isTrigger && /* @__PURE__ */ jsx(
|
|
3673
|
-
"div",
|
|
3674
|
-
{
|
|
3675
|
-
className: "absolute -top-2 -left-2 z-10 flex size-5 items-center justify-center rounded-full shadow-md ring-2 ring-card",
|
|
3676
|
-
style: { backgroundColor: "var(--color-status-success-text)" },
|
|
3677
|
-
children: /* @__PURE__ */ jsx(
|
|
3678
|
-
Icon,
|
|
3679
|
-
{
|
|
3680
|
-
icon: "mdi:lightning-bolt",
|
|
3681
|
-
className: "size-3",
|
|
3682
|
-
style: { color: "var(--color-status-success-text-inverse)" }
|
|
3683
|
-
}
|
|
3684
|
-
)
|
|
3685
|
-
}
|
|
3686
|
-
),
|
|
3687
|
-
children
|
|
3688
|
-
]
|
|
3689
|
-
}
|
|
3690
|
-
),
|
|
3691
|
-
executionStatus === "success" && /* @__PURE__ */ jsx(StatusCorner, { icon: "mdi:check", tone: "success" }),
|
|
3692
|
-
executionStatus === "error" && /* @__PURE__ */ jsx(StatusCorner, { icon: "mdi:close", tone: "error" }),
|
|
3693
|
-
executionStatus === "running" && /* @__PURE__ */ jsx(StatusCorner, { icon: "svg-spinners:ring-resize", tone: "success" })
|
|
3694
|
-
]
|
|
3695
|
-
}
|
|
3696
|
-
);
|
|
3697
|
-
}
|
|
3698
|
-
);
|
|
3699
|
-
NodeCard.displayName = "NodeCard";
|
|
3700
|
-
function ToolbarButton2({
|
|
3701
|
-
title,
|
|
3702
|
-
onClick,
|
|
3703
|
-
disabled,
|
|
3704
|
-
children
|
|
3705
|
-
}) {
|
|
3706
|
-
return /* @__PURE__ */ jsx(
|
|
3707
|
-
"button",
|
|
3708
|
-
{
|
|
3709
|
-
type: "button",
|
|
3710
|
-
title,
|
|
3711
|
-
disabled,
|
|
3712
|
-
onClick: (e) => {
|
|
3713
|
-
e.stopPropagation();
|
|
3714
|
-
onClick?.();
|
|
3715
|
-
},
|
|
3716
|
-
className: "flex size-6 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-muted hover:text-foreground disabled:opacity-50",
|
|
3717
|
-
children
|
|
3718
|
-
}
|
|
3719
|
-
);
|
|
3720
|
-
}
|
|
3721
|
-
function StatusCorner({
|
|
3722
|
-
icon,
|
|
3723
|
-
tone
|
|
3724
|
-
}) {
|
|
3725
|
-
const bg = tone === "success" ? "var(--color-status-success-bg)" : "var(--color-status-error-bg)";
|
|
3726
|
-
const fg = tone === "success" ? "var(--color-status-success-text-inverse)" : "var(--color-status-error-text-inverse)";
|
|
3727
|
-
return /* @__PURE__ */ jsx("div", { className: "absolute -bottom-1.5 -right-1.5 z-10", children: /* @__PURE__ */ jsx(
|
|
3728
|
-
"div",
|
|
3729
|
-
{
|
|
3730
|
-
className: "rounded-full p-0.5 shadow-md animate-in zoom-in duration-200",
|
|
3731
|
-
style: { backgroundColor: bg },
|
|
3732
|
-
children: /* @__PURE__ */ jsx(Icon, { icon, className: "size-3", style: { color: fg } })
|
|
3733
|
-
}
|
|
3734
|
-
) });
|
|
3735
|
-
}
|
|
3736
|
-
var NodeCardHeader = React2.forwardRef(
|
|
3737
|
-
({
|
|
3738
|
-
icon,
|
|
3739
|
-
appName,
|
|
3740
|
-
accentKey,
|
|
3741
|
-
missingCredentials = false,
|
|
3742
|
-
label,
|
|
3743
|
-
isRenaming = false,
|
|
3744
|
-
onStartRename,
|
|
3745
|
-
onCommitRename,
|
|
3746
|
-
onCancelRename
|
|
3747
|
-
}, ref) => {
|
|
3748
|
-
const tr = useTr();
|
|
3749
|
-
const accent = nodeAccent(accentKey);
|
|
3750
|
-
return /* @__PURE__ */ jsxs("div", { ref, className: "flex items-center gap-2.5 px-3 pb-3 pt-4", children: [
|
|
3751
|
-
/* @__PURE__ */ jsx(
|
|
3752
|
-
"div",
|
|
3753
|
-
{
|
|
3754
|
-
className: "flex size-8 shrink-0 items-center justify-center rounded-lg",
|
|
3755
|
-
style: {
|
|
3756
|
-
backgroundColor: tint(accent, 13),
|
|
3757
|
-
color: accent,
|
|
3758
|
-
border: `1px solid ${tint(accent, 26)}`
|
|
3759
|
-
},
|
|
3760
|
-
children: /* @__PURE__ */ jsx(NodeTypeIcon, { icon, alt: appName, className: "size-[18px]" })
|
|
3761
|
-
}
|
|
3762
|
-
),
|
|
3763
|
-
onStartRename && onCommitRename && onCancelRename ? /* @__PURE__ */ jsx(
|
|
3764
|
-
InlineEditableLabel,
|
|
3765
|
-
{
|
|
3766
|
-
value: label ?? "",
|
|
3767
|
-
placeholder: appName,
|
|
3768
|
-
editing: isRenaming,
|
|
3769
|
-
onStartEdit: onStartRename,
|
|
3770
|
-
onCommit: onCommitRename,
|
|
3771
|
-
onCancel: onCancelRename,
|
|
3772
|
-
className: "min-w-0 flex-1 truncate text-[11px] font-semibold uppercase tracking-wide text-muted-foreground",
|
|
3773
|
-
inputClassName: "text-[11px] font-semibold uppercase tracking-wide"
|
|
3774
|
-
}
|
|
3775
|
-
) : /* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate text-[11px] font-semibold uppercase tracking-wide text-muted-foreground", children: label || appName }),
|
|
3776
|
-
missingCredentials && /* @__PURE__ */ jsx(
|
|
3777
|
-
"span",
|
|
3778
|
-
{
|
|
3779
|
-
title: tr(
|
|
3780
|
-
"fluidgrids.builder.nodeCard.missingCredentialsTooltip",
|
|
3781
|
-
"Credentials not configured"
|
|
3782
|
-
),
|
|
3783
|
-
className: "flex items-center text-[var(--color-status-warning-text)]",
|
|
3784
|
-
"aria-label": tr(
|
|
3785
|
-
"fluidgrids.builder.nodeCard.missingCredentialsAriaLabel",
|
|
3786
|
-
"Credentials not configured"
|
|
3787
|
-
),
|
|
3788
|
-
children: /* @__PURE__ */ jsx(Icon, { icon: "lucide:alert-triangle", className: "size-4" })
|
|
3789
|
-
}
|
|
3790
|
-
)
|
|
3791
|
-
] });
|
|
3792
|
-
}
|
|
3793
|
-
);
|
|
3794
|
-
NodeCardHeader.displayName = "NodeCardHeader";
|
|
3795
|
-
var NodeCardBody = React2.forwardRef(
|
|
3796
|
-
({ operationName, description }, ref) => {
|
|
3797
|
-
const tr = useTr();
|
|
3798
|
-
return /* @__PURE__ */ jsxs(
|
|
3799
|
-
"div",
|
|
3800
|
-
{
|
|
3801
|
-
ref,
|
|
3802
|
-
className: "rounded-b-xl border-t border-border/60 px-3 pb-3 pt-2.5",
|
|
3803
|
-
children: [
|
|
3804
|
-
/* @__PURE__ */ jsx("div", { className: "text-[10px] font-semibold uppercase tracking-wider text-muted-foreground", children: tr("fluidgrids.builder.nodeCard.operationLabel", "Operation") }),
|
|
3805
|
-
/* @__PURE__ */ jsx("div", { className: "mt-0.5 truncate font-mono text-[13px] font-bold text-card-foreground", children: operationName }),
|
|
3806
|
-
description ? /* @__PURE__ */ jsx("p", { className: "mt-1 line-clamp-2 text-xs leading-snug text-muted-foreground", children: description }) : null
|
|
3807
|
-
]
|
|
3808
|
-
}
|
|
3809
|
-
);
|
|
3810
|
-
}
|
|
3811
|
-
);
|
|
3812
|
-
NodeCardBody.displayName = "NodeCardBody";
|
|
3813
|
-
function CustomHandle({
|
|
3814
|
-
className,
|
|
3815
|
-
isConnectable,
|
|
3816
|
-
accent,
|
|
3817
|
-
...props
|
|
3818
|
-
}) {
|
|
3819
|
-
const { nodeLookup, edges } = useStore(({ nodeLookup: nodeLookup2, edges: edges2 }) => ({
|
|
3820
|
-
nodeLookup: nodeLookup2,
|
|
3821
|
-
edges: edges2
|
|
3822
|
-
}));
|
|
3823
|
-
const nodeId = useNodeId();
|
|
3824
|
-
const isHandleConnectable = useMemo(() => {
|
|
3825
|
-
if (!nodeId) return false;
|
|
3826
|
-
const node = nodeLookup.get(nodeId);
|
|
3827
|
-
if (!node) return false;
|
|
3828
|
-
const connectedEdges = getConnectedEdges([node], edges);
|
|
3829
|
-
if (typeof isConnectable === "function")
|
|
3830
|
-
return isConnectable({ node, connectedEdges });
|
|
3831
|
-
if (typeof isConnectable === "number")
|
|
3832
|
-
return connectedEdges.length < isConnectable;
|
|
3833
|
-
return isConnectable;
|
|
3834
|
-
}, [edges, isConnectable, nodeId, nodeLookup]);
|
|
3835
|
-
return /* @__PURE__ */ jsx("span", { children: /* @__PURE__ */ jsx(
|
|
3836
|
-
Handle,
|
|
3837
|
-
{
|
|
3838
|
-
className: cn(
|
|
3839
|
-
"cursor-crosshair transition-transform hover:scale-110",
|
|
3840
|
-
!accent && "size-3 border-2 border-card-foreground/40 hover:border-primary/50",
|
|
3841
|
-
className
|
|
3842
|
-
),
|
|
3843
|
-
isConnectable: isHandleConnectable,
|
|
3844
|
-
style: accent ? {
|
|
3845
|
-
width: 14,
|
|
3846
|
-
height: 14,
|
|
3847
|
-
borderRadius: "50%",
|
|
3848
|
-
background: "var(--card, #fff)",
|
|
3849
|
-
border: `2.5px solid ${accent}`,
|
|
3850
|
-
boxShadow: `0 0 0 3px color-mix(in srgb, ${accent} 12%, transparent)`
|
|
3851
|
-
} : void 0,
|
|
3852
|
-
...props
|
|
3853
|
-
}
|
|
3854
|
-
) });
|
|
3594
|
+
) });
|
|
3855
3595
|
}
|
|
3856
3596
|
|
|
3857
3597
|
// src/builder/components/flow-builder/components/blocks/nodes/node/handle-spec.ts
|
|
@@ -3865,8 +3605,8 @@ function resolveOutputHandles(data) {
|
|
|
3865
3605
|
if (!spec) return DEFAULT_HANDLES;
|
|
3866
3606
|
if (isDynamicSpec(spec)) {
|
|
3867
3607
|
const args = data?.args;
|
|
3868
|
-
const
|
|
3869
|
-
const dynamic = Object.entries(
|
|
3608
|
+
const record2 = args?.state?.[spec.fromStateRecord] ?? data?.[spec.fromStateRecord] ?? {};
|
|
3609
|
+
const dynamic = Object.entries(record2).filter(([, handleId]) => typeof handleId === "string" && handleId).map(([label, handleId]) => ({ id: handleId, label }));
|
|
3870
3610
|
return [...dynamic, ...spec.extra ?? []];
|
|
3871
3611
|
}
|
|
3872
3612
|
if (spec.length === 0) return DEFAULT_HANDLES;
|
|
@@ -4283,7 +4023,11 @@ function useSaveWorkflow() {
|
|
|
4283
4023
|
const partitioned = partitionStickyNotes(workflow.nodes ?? []);
|
|
4284
4024
|
const preparedWorkflow = {
|
|
4285
4025
|
...workflow,
|
|
4286
|
-
nodes:
|
|
4026
|
+
nodes: normalizeWorkflowParameters(
|
|
4027
|
+
partitioned.nodes,
|
|
4028
|
+
workflow.edges ?? [],
|
|
4029
|
+
(type) => NODE_CONFIGS.find((config2) => config2.type === type)?.operations
|
|
4030
|
+
).map((n) => {
|
|
4287
4031
|
const slimData = slimNodeDataToCurrentOperation(
|
|
4288
4032
|
n.data ?? {}
|
|
4289
4033
|
);
|
|
@@ -5155,11 +4899,11 @@ function canRenderOperationPanel(selectedOperation, supportedOperations) {
|
|
|
5155
4899
|
const operationName = resolveOperationName(selectedOperation, "");
|
|
5156
4900
|
return operationName === "" || supportedOperations.includes(operationName);
|
|
5157
4901
|
}
|
|
5158
|
-
function
|
|
4902
|
+
function isRecord5(value) {
|
|
5159
4903
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
5160
4904
|
}
|
|
5161
4905
|
function readRecord2(value) {
|
|
5162
|
-
return
|
|
4906
|
+
return isRecord5(value) ? value : void 0;
|
|
5163
4907
|
}
|
|
5164
4908
|
function readOperations(value) {
|
|
5165
4909
|
const operations = Array.isArray(value) ? value : readRecord2(value)?.operations;
|
|
@@ -6064,76 +5808,636 @@ function ensureNodeConfigsLoaded() {
|
|
|
6064
5808
|
} else {
|
|
6065
5809
|
initPromise = null;
|
|
6066
5810
|
}
|
|
6067
|
-
rebuildNodeArrays();
|
|
6068
|
-
});
|
|
6069
|
-
}
|
|
6070
|
-
return initPromise;
|
|
6071
|
-
}
|
|
6072
|
-
function isNodeConfigsInitialized() {
|
|
6073
|
-
return isInitialized;
|
|
6074
|
-
}
|
|
6075
|
-
function rebuildNodeArrays() {
|
|
6076
|
-
NODE_CONFIGS.forEach((config2) => {
|
|
6077
|
-
const data = getNodeMetadata(config2);
|
|
6078
|
-
const nodeMetadata = {
|
|
6079
|
-
...data,
|
|
6080
|
-
detail: { ...config2.detail }
|
|
6081
|
-
};
|
|
6082
|
-
if (!NODES.some((n) => n.type === nodeMetadata.type)) {
|
|
6083
|
-
NODES.push(nodeMetadata);
|
|
6084
|
-
NODE_TYPES[nodeMetadata.type] = nodeMetadata.node;
|
|
6085
|
-
NODES_METADATA[nodeMetadata.type] = {
|
|
6086
|
-
...nodeMetadata,
|
|
6087
|
-
__details: { type: nodeMetadata.type, ...nodeMetadata.detail }
|
|
6088
|
-
};
|
|
6089
|
-
}
|
|
6090
|
-
});
|
|
6091
|
-
const newAvailableNodes = NODES.filter(
|
|
6092
|
-
(node) => node.available === void 0 || node.available
|
|
6093
|
-
).map((node) => {
|
|
6094
|
-
const nodeConfig = NODE_CONFIGS.find((config2) => config2.type === node.type);
|
|
6095
|
-
return {
|
|
6096
|
-
type: node.type,
|
|
6097
|
-
icon: node.detail.icon,
|
|
6098
|
-
title: node.detail.title,
|
|
6099
|
-
description: node.detail.description,
|
|
6100
|
-
gradientColor: node.detail.gradientColor ?? "gray",
|
|
6101
|
-
manifest: node.defaultData?.nodeData,
|
|
6102
|
-
publishType: nodeConfig?.publishType
|
|
6103
|
-
// Include publishType
|
|
6104
|
-
};
|
|
6105
|
-
});
|
|
6106
|
-
AVAILABLE_NODES.length = 0;
|
|
6107
|
-
AVAILABLE_NODES.push(...newAvailableNodes);
|
|
6108
|
-
notifyNodeTypesListeners();
|
|
6109
|
-
}
|
|
6110
|
-
ensureNodeConfigsLoaded().catch((err) => {
|
|
6111
|
-
console.warn(
|
|
6112
|
-
"[Workflows] Background node config initialization failed:",
|
|
6113
|
-
err
|
|
5811
|
+
rebuildNodeArrays();
|
|
5812
|
+
});
|
|
5813
|
+
}
|
|
5814
|
+
return initPromise;
|
|
5815
|
+
}
|
|
5816
|
+
function isNodeConfigsInitialized() {
|
|
5817
|
+
return isInitialized;
|
|
5818
|
+
}
|
|
5819
|
+
function rebuildNodeArrays() {
|
|
5820
|
+
NODE_CONFIGS.forEach((config2) => {
|
|
5821
|
+
const data = getNodeMetadata(config2);
|
|
5822
|
+
const nodeMetadata = {
|
|
5823
|
+
...data,
|
|
5824
|
+
detail: { ...config2.detail }
|
|
5825
|
+
};
|
|
5826
|
+
if (!NODES.some((n) => n.type === nodeMetadata.type)) {
|
|
5827
|
+
NODES.push(nodeMetadata);
|
|
5828
|
+
NODE_TYPES[nodeMetadata.type] = nodeMetadata.node;
|
|
5829
|
+
NODES_METADATA[nodeMetadata.type] = {
|
|
5830
|
+
...nodeMetadata,
|
|
5831
|
+
__details: { type: nodeMetadata.type, ...nodeMetadata.detail }
|
|
5832
|
+
};
|
|
5833
|
+
}
|
|
5834
|
+
});
|
|
5835
|
+
const newAvailableNodes = NODES.filter(
|
|
5836
|
+
(node) => node.available === void 0 || node.available
|
|
5837
|
+
).map((node) => {
|
|
5838
|
+
const nodeConfig = NODE_CONFIGS.find((config2) => config2.type === node.type);
|
|
5839
|
+
return {
|
|
5840
|
+
type: node.type,
|
|
5841
|
+
icon: node.detail.icon,
|
|
5842
|
+
title: node.detail.title,
|
|
5843
|
+
description: node.detail.description,
|
|
5844
|
+
gradientColor: node.detail.gradientColor ?? "gray",
|
|
5845
|
+
manifest: node.defaultData?.nodeData,
|
|
5846
|
+
publishType: nodeConfig?.publishType
|
|
5847
|
+
// Include publishType
|
|
5848
|
+
};
|
|
5849
|
+
});
|
|
5850
|
+
AVAILABLE_NODES.length = 0;
|
|
5851
|
+
AVAILABLE_NODES.push(...newAvailableNodes);
|
|
5852
|
+
notifyNodeTypesListeners();
|
|
5853
|
+
}
|
|
5854
|
+
ensureNodeConfigsLoaded().catch((err) => {
|
|
5855
|
+
console.warn(
|
|
5856
|
+
"[Workflows] Background node config initialization failed:",
|
|
5857
|
+
err
|
|
5858
|
+
);
|
|
5859
|
+
});
|
|
5860
|
+
var fallbackDynamic = getNodeMetadata({ type: "__default_dynamic__" });
|
|
5861
|
+
if (fallbackDynamic?.node) {
|
|
5862
|
+
NODE_TYPES["default"] = fallbackDynamic.node;
|
|
5863
|
+
}
|
|
5864
|
+
NODE_TYPES[STICKY_NOTE_TYPE] = StickyNoteNode;
|
|
5865
|
+
function registerWorkflowNodeTypes(workflowNodes) {
|
|
5866
|
+
for (const node of workflowNodes) {
|
|
5867
|
+
if (node.type && !NODE_TYPES[node.type]) {
|
|
5868
|
+
const meta = getNodeMetadata({
|
|
5869
|
+
type: node.type,
|
|
5870
|
+
defaultData: {
|
|
5871
|
+
version: node.data?.version,
|
|
5872
|
+
nodeData: node.data?.nodeData
|
|
5873
|
+
},
|
|
5874
|
+
operations: node.data?.operations
|
|
5875
|
+
});
|
|
5876
|
+
if (meta?.node) {
|
|
5877
|
+
NODE_TYPES[node.type] = meta.node;
|
|
5878
|
+
}
|
|
5879
|
+
}
|
|
5880
|
+
}
|
|
5881
|
+
}
|
|
5882
|
+
|
|
5883
|
+
// src/hooks/useNodeExecution.ts
|
|
5884
|
+
var TRIGGER_WORKFLOW = TriggerWorkflowDocument;
|
|
5885
|
+
var EXECUTE_STEP = ExecuteStepDocument;
|
|
5886
|
+
var WORKFLOW_RUN = WorkflowRunDocument;
|
|
5887
|
+
var RUN_FROM_NODE = RunFromNodeDocument;
|
|
5888
|
+
var EXECUTION_DETAIL = ExecutionDetailDocument;
|
|
5889
|
+
var TERMINAL_STATUSES = /* @__PURE__ */ new Set([
|
|
5890
|
+
"COMPLETED",
|
|
5891
|
+
"FAILED",
|
|
5892
|
+
"CANCELLED",
|
|
5893
|
+
"TERMINATED"
|
|
5894
|
+
]);
|
|
5895
|
+
var POLL_INTERVAL = 1500;
|
|
5896
|
+
var MAX_RUN_CREATION_WAIT = 6e4;
|
|
5897
|
+
function calculateItemCount(output) {
|
|
5898
|
+
if (!output || Object.keys(output).length === 0) {
|
|
5899
|
+
return 0;
|
|
5900
|
+
}
|
|
5901
|
+
const values = Object.values(output);
|
|
5902
|
+
for (const value of values) {
|
|
5903
|
+
if (Array.isArray(value)) {
|
|
5904
|
+
return value.length;
|
|
5905
|
+
}
|
|
5906
|
+
if (typeof value === "object" && value !== null) {
|
|
5907
|
+
const nestedValues = Object.values(value);
|
|
5908
|
+
for (const nestedValue of nestedValues) {
|
|
5909
|
+
if (Array.isArray(nestedValue)) {
|
|
5910
|
+
return nestedValue.length;
|
|
5911
|
+
}
|
|
5912
|
+
}
|
|
5913
|
+
}
|
|
5914
|
+
}
|
|
5915
|
+
return 1;
|
|
5916
|
+
}
|
|
5917
|
+
function useNodeExecution() {
|
|
5918
|
+
const pollTimerRef = useRef(null);
|
|
5919
|
+
const pollStartRef = useRef(0);
|
|
5920
|
+
const activeRunIdRef = useRef(null);
|
|
5921
|
+
const loadRequestRef = useRef(0);
|
|
5922
|
+
const workflow = useFlowStore((s) => s.workflow);
|
|
5923
|
+
const startExecution = useFlowStore(
|
|
5924
|
+
(s) => s.actions.execution.startExecution
|
|
5925
|
+
);
|
|
5926
|
+
const setRunMetadata = useFlowStore(
|
|
5927
|
+
(s) => s.actions.execution.setRunMetadata
|
|
5928
|
+
);
|
|
5929
|
+
const setStepPlan = useFlowStore((s) => s.actions.execution.setStepPlan);
|
|
5930
|
+
const addLog = useFlowStore((s) => s.actions.execution.addLog);
|
|
5931
|
+
const updateLog = useFlowStore((s) => s.actions.execution.updateLog);
|
|
5932
|
+
const setNodeStatus = useFlowStore((s) => s.actions.execution.setNodeStatus);
|
|
5933
|
+
const setNodeItemCount = useFlowStore(
|
|
5934
|
+
(s) => s.actions.execution.setNodeItemCount
|
|
5935
|
+
);
|
|
5936
|
+
const completeExecution = useFlowStore(
|
|
5937
|
+
(s) => s.actions.execution.completeExecution
|
|
5938
|
+
);
|
|
5939
|
+
const clearLogs = useFlowStore((s) => s.actions.execution.clearLogs);
|
|
5940
|
+
const loadDebugRun = useFlowStore((s) => s.actions.execution.loadDebugRun);
|
|
5941
|
+
const cleanup = useCallback(() => {
|
|
5942
|
+
loadRequestRef.current += 1;
|
|
5943
|
+
if (pollTimerRef.current) {
|
|
5944
|
+
clearTimeout(pollTimerRef.current);
|
|
5945
|
+
pollTimerRef.current = null;
|
|
5946
|
+
}
|
|
5947
|
+
activeRunIdRef.current = null;
|
|
5948
|
+
}, []);
|
|
5949
|
+
const findTriggerNode = useCallback(() => {
|
|
5950
|
+
const { nodes, edges } = workflow;
|
|
5951
|
+
const explicitTrigger = nodes.find(isNodeTrigger);
|
|
5952
|
+
if (explicitTrigger) {
|
|
5953
|
+
return explicitTrigger;
|
|
5954
|
+
}
|
|
5955
|
+
const nodesWithIncomingEdges = new Set(edges.map((e) => e.target));
|
|
5956
|
+
const firstNode = nodes.find(
|
|
5957
|
+
(node) => !nodesWithIncomingEdges.has(node.id)
|
|
5958
|
+
);
|
|
5959
|
+
if (firstNode) {
|
|
5960
|
+
return firstNode;
|
|
5961
|
+
}
|
|
5962
|
+
if (nodes.length > 0) {
|
|
5963
|
+
return nodes[0];
|
|
5964
|
+
}
|
|
5965
|
+
console.error("[useNodeExecution] No nodes found in workflow");
|
|
5966
|
+
return null;
|
|
5967
|
+
}, [workflow]);
|
|
5968
|
+
const getNodeName = useCallback(
|
|
5969
|
+
(nodeId) => {
|
|
5970
|
+
const node = workflow.nodes.find((n) => n.id === nodeId);
|
|
5971
|
+
if (!node) return `Node ${nodeId}`;
|
|
5972
|
+
const data = node.data;
|
|
5973
|
+
return data?.operation_name || data?.name || node.type || `Node ${nodeId}`;
|
|
5974
|
+
},
|
|
5975
|
+
[workflow.nodes]
|
|
5976
|
+
);
|
|
5977
|
+
const processNodeStates = useCallback(
|
|
5978
|
+
(runId, nodeStates) => {
|
|
5979
|
+
for (const nodeState of nodeStates) {
|
|
5980
|
+
const logId = `${runId}-${nodeState.nodeId}`;
|
|
5981
|
+
const rawStatus = nodeState.status.toLowerCase();
|
|
5982
|
+
const status = rawStatus === "completed" ? "completed" : rawStatus === "failed" || rawStatus === "timeout" ? "failed" : rawStatus === "running" || rawStatus === "waiting" || rawStatus === "delayed" || rawStatus === "waiting_subworkflow" ? "running" : "pending";
|
|
5983
|
+
if (status === "completed") {
|
|
5984
|
+
setNodeStatus(nodeState.nodeId, "success");
|
|
5985
|
+
if (nodeState.output) {
|
|
5986
|
+
const itemCount = calculateItemCount(
|
|
5987
|
+
nodeState.output
|
|
5988
|
+
);
|
|
5989
|
+
setNodeItemCount(nodeState.nodeId, itemCount);
|
|
5990
|
+
}
|
|
5991
|
+
} else if (status === "failed") {
|
|
5992
|
+
setNodeStatus(nodeState.nodeId, "error");
|
|
5993
|
+
} else if (status === "running") {
|
|
5994
|
+
setNodeStatus(nodeState.nodeId, "running");
|
|
5995
|
+
}
|
|
5996
|
+
const existingLog = useFlowStore.getState().execution.logs.find((l) => l.id === logId);
|
|
5997
|
+
if (!existingLog) {
|
|
5998
|
+
if (status === "pending") continue;
|
|
5999
|
+
const newLog = {
|
|
6000
|
+
id: logId,
|
|
6001
|
+
runId,
|
|
6002
|
+
nodeId: nodeState.nodeId,
|
|
6003
|
+
nodeType: nodeState.nodeType,
|
|
6004
|
+
nodeName: getNodeName(nodeState.nodeId),
|
|
6005
|
+
status,
|
|
6006
|
+
inputArgs: nodeState.inputArgs ?? null,
|
|
6007
|
+
output: nodeState.output ?? null,
|
|
6008
|
+
error: nodeState.error ?? null,
|
|
6009
|
+
duration: nodeState.duration ?? null,
|
|
6010
|
+
startTime: nodeState.startTime ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
6011
|
+
endTime: nodeState.endTime ?? null,
|
|
6012
|
+
usedPinnedData: nodeState.usedPinnedData ?? false,
|
|
6013
|
+
reusedFromRunId: nodeState.reusedFromRunId ?? null
|
|
6014
|
+
};
|
|
6015
|
+
addLog(newLog);
|
|
6016
|
+
} else {
|
|
6017
|
+
if (existingLog.status !== status || status === "completed" && !existingLog.output) {
|
|
6018
|
+
updateLog(logId, {
|
|
6019
|
+
status,
|
|
6020
|
+
inputArgs: nodeState.inputArgs ?? existingLog.inputArgs,
|
|
6021
|
+
output: nodeState.output ?? existingLog.output,
|
|
6022
|
+
error: nodeState.error ?? existingLog.error,
|
|
6023
|
+
duration: nodeState.duration ?? existingLog.duration,
|
|
6024
|
+
endTime: nodeState.endTime ?? existingLog.endTime,
|
|
6025
|
+
usedPinnedData: nodeState.usedPinnedData ?? existingLog.usedPinnedData,
|
|
6026
|
+
reusedFromRunId: nodeState.reusedFromRunId ?? existingLog.reusedFromRunId
|
|
6027
|
+
});
|
|
6028
|
+
}
|
|
6029
|
+
}
|
|
6030
|
+
}
|
|
6031
|
+
},
|
|
6032
|
+
[setNodeStatus, setNodeItemCount, addLog, updateLog, getNodeName]
|
|
6033
|
+
);
|
|
6034
|
+
const pollRunUpdates = useCallback(
|
|
6035
|
+
(runId) => {
|
|
6036
|
+
const client = getFluidGridsClient();
|
|
6037
|
+
pollStartRef.current = Date.now();
|
|
6038
|
+
activeRunIdRef.current = runId;
|
|
6039
|
+
const doPoll = async () => {
|
|
6040
|
+
if (activeRunIdRef.current !== runId) {
|
|
6041
|
+
return;
|
|
6042
|
+
}
|
|
6043
|
+
try {
|
|
6044
|
+
const result = await client.query({
|
|
6045
|
+
query: WORKFLOW_RUN,
|
|
6046
|
+
variables: { id: runId },
|
|
6047
|
+
fetchPolicy: "network-only"
|
|
6048
|
+
});
|
|
6049
|
+
if (activeRunIdRef.current !== runId) {
|
|
6050
|
+
return;
|
|
6051
|
+
}
|
|
6052
|
+
const run = result.data?.workflowRun;
|
|
6053
|
+
if (!run) {
|
|
6054
|
+
console.warn(
|
|
6055
|
+
"[useNodeExecution] Poll returned no run data for",
|
|
6056
|
+
runId
|
|
6057
|
+
);
|
|
6058
|
+
if (Date.now() - pollStartRef.current < MAX_RUN_CREATION_WAIT) {
|
|
6059
|
+
pollTimerRef.current = setTimeout(doPoll, POLL_INTERVAL);
|
|
6060
|
+
} else {
|
|
6061
|
+
console.warn(
|
|
6062
|
+
"[useNodeExecution] Run data not found after timeout for",
|
|
6063
|
+
runId
|
|
6064
|
+
);
|
|
6065
|
+
completeExecution();
|
|
6066
|
+
cleanup();
|
|
6067
|
+
}
|
|
6068
|
+
return;
|
|
6069
|
+
}
|
|
6070
|
+
setRunMetadata({
|
|
6071
|
+
runId,
|
|
6072
|
+
workflowKey: run.workflowKey,
|
|
6073
|
+
version: run.version,
|
|
6074
|
+
sourceMode: run.mode
|
|
6075
|
+
});
|
|
6076
|
+
if (run.nodeStates?.length) {
|
|
6077
|
+
processNodeStates(runId, run.nodeStates);
|
|
6078
|
+
}
|
|
6079
|
+
if (TERMINAL_STATUSES.has(run.status)) {
|
|
6080
|
+
completeExecution();
|
|
6081
|
+
cleanup();
|
|
6082
|
+
return;
|
|
6083
|
+
}
|
|
6084
|
+
pollTimerRef.current = setTimeout(doPoll, POLL_INTERVAL);
|
|
6085
|
+
} catch (error) {
|
|
6086
|
+
console.error("[useNodeExecution] Poll error:", error);
|
|
6087
|
+
if (activeRunIdRef.current !== runId) {
|
|
6088
|
+
return;
|
|
6089
|
+
}
|
|
6090
|
+
pollTimerRef.current = setTimeout(doPoll, POLL_INTERVAL * 2);
|
|
6091
|
+
}
|
|
6092
|
+
};
|
|
6093
|
+
pollTimerRef.current = setTimeout(doPoll, 800);
|
|
6094
|
+
},
|
|
6095
|
+
[processNodeStates, completeExecution, cleanup, setRunMetadata]
|
|
6096
|
+
);
|
|
6097
|
+
const executeStep = useCallback(
|
|
6098
|
+
async (nodeId) => {
|
|
6099
|
+
cleanup();
|
|
6100
|
+
clearLogs();
|
|
6101
|
+
const workflowKey = workflow.workflowKey || workflow.id;
|
|
6102
|
+
const version = workflow.version || "1.0.0";
|
|
6103
|
+
if (!workflowKey) {
|
|
6104
|
+
console.error(
|
|
6105
|
+
"[useNodeExecution] Workflow must be saved before step execution. No workflowKey found."
|
|
6106
|
+
);
|
|
6107
|
+
return {
|
|
6108
|
+
success: false,
|
|
6109
|
+
error: "Workflow must be saved before execution"
|
|
6110
|
+
};
|
|
6111
|
+
}
|
|
6112
|
+
setNodeStatus(nodeId, "running");
|
|
6113
|
+
try {
|
|
6114
|
+
normalizeWorkflowParameters(
|
|
6115
|
+
workflow.nodes.filter((node) => node.id === nodeId),
|
|
6116
|
+
workflow.edges,
|
|
6117
|
+
(type) => NODE_CONFIGS.find((config2) => config2.type === type)?.operations
|
|
6118
|
+
);
|
|
6119
|
+
const client = getFluidGridsClient();
|
|
6120
|
+
const result = throwIfResultError(
|
|
6121
|
+
await client.mutate({
|
|
6122
|
+
mutation: EXECUTE_STEP,
|
|
6123
|
+
variables: {
|
|
6124
|
+
input: {
|
|
6125
|
+
workflowKey,
|
|
6126
|
+
version,
|
|
6127
|
+
nodeId
|
|
6128
|
+
}
|
|
6129
|
+
}
|
|
6130
|
+
})
|
|
6131
|
+
);
|
|
6132
|
+
const stepResult = result.data?.executeStep;
|
|
6133
|
+
if (stepResult) {
|
|
6134
|
+
const backendRunId = stepResult.runId;
|
|
6135
|
+
startExecution(nodeId, backendRunId, {
|
|
6136
|
+
workflowKey: stepResult.workflowKey,
|
|
6137
|
+
version: stepResult.version,
|
|
6138
|
+
sourceMode: "TEST"
|
|
6139
|
+
});
|
|
6140
|
+
setStepPlan({
|
|
6141
|
+
runId: backendRunId,
|
|
6142
|
+
...stepResult.plan,
|
|
6143
|
+
sourceRunId: stepResult.plan.sourceRunId ?? null
|
|
6144
|
+
});
|
|
6145
|
+
const initialLog = {
|
|
6146
|
+
id: `${backendRunId}-${nodeId}`,
|
|
6147
|
+
runId: backendRunId,
|
|
6148
|
+
nodeId,
|
|
6149
|
+
nodeType: workflow.nodes.find((node) => node.id === nodeId)?.type || "unknown",
|
|
6150
|
+
nodeName: getNodeName(nodeId),
|
|
6151
|
+
status: "running",
|
|
6152
|
+
inputArgs: null,
|
|
6153
|
+
output: null,
|
|
6154
|
+
error: null,
|
|
6155
|
+
duration: null,
|
|
6156
|
+
startTime: (/* @__PURE__ */ new Date()).toISOString(),
|
|
6157
|
+
endTime: null
|
|
6158
|
+
};
|
|
6159
|
+
addLog(initialLog);
|
|
6160
|
+
pollRunUpdates(backendRunId);
|
|
6161
|
+
return { success: true, runId: backendRunId, plan: stepResult.plan };
|
|
6162
|
+
}
|
|
6163
|
+
console.error("[useNodeExecution] No result from executeStep");
|
|
6164
|
+
setNodeStatus(nodeId, "error");
|
|
6165
|
+
return { success: false, error: "No result from executeStep" };
|
|
6166
|
+
} catch (error) {
|
|
6167
|
+
console.error("[useNodeExecution] Error executing step:", error);
|
|
6168
|
+
const errorMessage = describeOperationError(error);
|
|
6169
|
+
const errorRunId = nanoid$1();
|
|
6170
|
+
const errorLog = {
|
|
6171
|
+
id: `${errorRunId}-${nodeId}`,
|
|
6172
|
+
runId: errorRunId,
|
|
6173
|
+
nodeId,
|
|
6174
|
+
nodeType: workflow.nodes.find((node) => node.id === nodeId)?.type || "unknown",
|
|
6175
|
+
nodeName: getNodeName(nodeId),
|
|
6176
|
+
status: "failed",
|
|
6177
|
+
inputArgs: null,
|
|
6178
|
+
output: null,
|
|
6179
|
+
error: errorMessage,
|
|
6180
|
+
duration: null,
|
|
6181
|
+
startTime: (/* @__PURE__ */ new Date()).toISOString(),
|
|
6182
|
+
endTime: (/* @__PURE__ */ new Date()).toISOString()
|
|
6183
|
+
};
|
|
6184
|
+
addLog(errorLog);
|
|
6185
|
+
setNodeStatus(nodeId, "error");
|
|
6186
|
+
completeExecution();
|
|
6187
|
+
return { success: false, error: errorMessage };
|
|
6188
|
+
}
|
|
6189
|
+
},
|
|
6190
|
+
[
|
|
6191
|
+
cleanup,
|
|
6192
|
+
clearLogs,
|
|
6193
|
+
startExecution,
|
|
6194
|
+
setStepPlan,
|
|
6195
|
+
workflow,
|
|
6196
|
+
getNodeName,
|
|
6197
|
+
addLog,
|
|
6198
|
+
setNodeStatus,
|
|
6199
|
+
pollRunUpdates,
|
|
6200
|
+
completeExecution
|
|
6201
|
+
]
|
|
6202
|
+
);
|
|
6203
|
+
const executeFullWorkflow = useCallback(
|
|
6204
|
+
async (customTriggerData) => {
|
|
6205
|
+
const triggerNode = findTriggerNode();
|
|
6206
|
+
if (!triggerNode) {
|
|
6207
|
+
console.error("[useNodeExecution] No trigger node found in workflow");
|
|
6208
|
+
return { success: false, error: "No trigger node found" };
|
|
6209
|
+
}
|
|
6210
|
+
cleanup();
|
|
6211
|
+
clearLogs();
|
|
6212
|
+
const workflowKey = workflow.workflowKey || workflow.id;
|
|
6213
|
+
const version = workflow.version || "1.0.0";
|
|
6214
|
+
if (!workflowKey) {
|
|
6215
|
+
console.error(
|
|
6216
|
+
"[useNodeExecution] Workflow must be saved before execution. No workflowKey found."
|
|
6217
|
+
);
|
|
6218
|
+
return {
|
|
6219
|
+
success: false,
|
|
6220
|
+
error: "Workflow must be saved before execution"
|
|
6221
|
+
};
|
|
6222
|
+
}
|
|
6223
|
+
setNodeStatus(triggerNode.id, "running");
|
|
6224
|
+
try {
|
|
6225
|
+
normalizeWorkflowParameters(
|
|
6226
|
+
workflow.nodes.filter((node) => node.data.disabled !== true),
|
|
6227
|
+
workflow.edges,
|
|
6228
|
+
(type) => NODE_CONFIGS.find((config2) => config2.type === type)?.operations
|
|
6229
|
+
);
|
|
6230
|
+
const client = getFluidGridsClient();
|
|
6231
|
+
const result = throwIfResultError(
|
|
6232
|
+
await client.mutate({
|
|
6233
|
+
mutation: TRIGGER_WORKFLOW,
|
|
6234
|
+
variables: {
|
|
6235
|
+
input: {
|
|
6236
|
+
workflowKey,
|
|
6237
|
+
version,
|
|
6238
|
+
triggerNodeId: triggerNode.id,
|
|
6239
|
+
triggerData: customTriggerData || {},
|
|
6240
|
+
context: {},
|
|
6241
|
+
// Builder "Test workflow" is always a manual/debug run.
|
|
6242
|
+
// Pinned node data only applies to TEST executions, never a
|
|
6243
|
+
// production trigger (webhook, schedule, API).
|
|
6244
|
+
mode: "TEST"
|
|
6245
|
+
}
|
|
6246
|
+
}
|
|
6247
|
+
})
|
|
6248
|
+
);
|
|
6249
|
+
const triggerResult = result.data?.triggerWorkflow;
|
|
6250
|
+
if (triggerResult) {
|
|
6251
|
+
const backendRunId = triggerResult.runId;
|
|
6252
|
+
startExecution(triggerNode.id, backendRunId, {
|
|
6253
|
+
workflowKey: triggerResult.workflowKey,
|
|
6254
|
+
version: triggerResult.version,
|
|
6255
|
+
sourceMode: "TEST"
|
|
6256
|
+
});
|
|
6257
|
+
pollRunUpdates(backendRunId);
|
|
6258
|
+
return { success: true, runId: backendRunId };
|
|
6259
|
+
}
|
|
6260
|
+
console.error("[useNodeExecution] No result from trigger");
|
|
6261
|
+
setNodeStatus(triggerNode.id, "error");
|
|
6262
|
+
return { success: false, error: "No result from trigger" };
|
|
6263
|
+
} catch (error) {
|
|
6264
|
+
console.error("[useNodeExecution] Error triggering workflow:", error);
|
|
6265
|
+
const errorMessage = describeOperationError(error);
|
|
6266
|
+
const errorRunId = nanoid$1();
|
|
6267
|
+
const errorLog = {
|
|
6268
|
+
id: `${errorRunId}-${triggerNode.id}`,
|
|
6269
|
+
runId: errorRunId,
|
|
6270
|
+
nodeId: triggerNode.id,
|
|
6271
|
+
nodeType: triggerNode.type || "unknown",
|
|
6272
|
+
nodeName: getNodeName(triggerNode.id),
|
|
6273
|
+
status: "failed",
|
|
6274
|
+
inputArgs: null,
|
|
6275
|
+
output: null,
|
|
6276
|
+
error: errorMessage,
|
|
6277
|
+
duration: null,
|
|
6278
|
+
startTime: (/* @__PURE__ */ new Date()).toISOString(),
|
|
6279
|
+
endTime: (/* @__PURE__ */ new Date()).toISOString()
|
|
6280
|
+
};
|
|
6281
|
+
addLog(errorLog);
|
|
6282
|
+
setNodeStatus(triggerNode.id, "error");
|
|
6283
|
+
completeExecution();
|
|
6284
|
+
return { success: false, error: errorMessage };
|
|
6285
|
+
}
|
|
6286
|
+
},
|
|
6287
|
+
[
|
|
6288
|
+
findTriggerNode,
|
|
6289
|
+
cleanup,
|
|
6290
|
+
clearLogs,
|
|
6291
|
+
startExecution,
|
|
6292
|
+
workflow,
|
|
6293
|
+
getNodeName,
|
|
6294
|
+
addLog,
|
|
6295
|
+
setNodeStatus,
|
|
6296
|
+
pollRunUpdates,
|
|
6297
|
+
completeExecution
|
|
6298
|
+
]
|
|
6299
|
+
);
|
|
6300
|
+
const executeFromNode = useCallback(
|
|
6301
|
+
async (sourceRunId, nodeId) => {
|
|
6302
|
+
cleanup();
|
|
6303
|
+
clearLogs();
|
|
6304
|
+
setNodeStatus(nodeId, "running");
|
|
6305
|
+
try {
|
|
6306
|
+
const client = getFluidGridsClient();
|
|
6307
|
+
const result = throwIfResultError(
|
|
6308
|
+
await client.mutate({
|
|
6309
|
+
mutation: RUN_FROM_NODE,
|
|
6310
|
+
variables: {
|
|
6311
|
+
input: {
|
|
6312
|
+
sourceRunId,
|
|
6313
|
+
nodeId,
|
|
6314
|
+
// Run-from-node is a debug affordance — always TEST, same
|
|
6315
|
+
// rule as the other builder debug execution paths.
|
|
6316
|
+
mode: "TEST"
|
|
6317
|
+
}
|
|
6318
|
+
}
|
|
6319
|
+
})
|
|
6320
|
+
);
|
|
6321
|
+
const runFromNodeResult = result.data?.runFromNode;
|
|
6322
|
+
if (runFromNodeResult) {
|
|
6323
|
+
const backendRunId = runFromNodeResult.runId;
|
|
6324
|
+
startExecution(nodeId, backendRunId, {
|
|
6325
|
+
workflowKey: runFromNodeResult.workflowKey,
|
|
6326
|
+
version: runFromNodeResult.version,
|
|
6327
|
+
sourceMode: "TEST"
|
|
6328
|
+
});
|
|
6329
|
+
pollRunUpdates(backendRunId);
|
|
6330
|
+
return { success: true, runId: backendRunId };
|
|
6331
|
+
}
|
|
6332
|
+
console.error("[useNodeExecution] No result from runFromNode");
|
|
6333
|
+
setNodeStatus(nodeId, "error");
|
|
6334
|
+
return { success: false, error: "No result from runFromNode" };
|
|
6335
|
+
} catch (error) {
|
|
6336
|
+
console.error("[useNodeExecution] Error running from node:", error);
|
|
6337
|
+
const errorMessage = describeOperationError(error);
|
|
6338
|
+
setNodeStatus(nodeId, "error");
|
|
6339
|
+
completeExecution();
|
|
6340
|
+
return { success: false, error: errorMessage };
|
|
6341
|
+
}
|
|
6342
|
+
},
|
|
6343
|
+
[
|
|
6344
|
+
cleanup,
|
|
6345
|
+
clearLogs,
|
|
6346
|
+
setNodeStatus,
|
|
6347
|
+
startExecution,
|
|
6348
|
+
pollRunUpdates,
|
|
6349
|
+
completeExecution
|
|
6350
|
+
]
|
|
6351
|
+
);
|
|
6352
|
+
const loadExecution = useCallback(
|
|
6353
|
+
async (runId) => {
|
|
6354
|
+
cleanup();
|
|
6355
|
+
const requestId = ++loadRequestRef.current;
|
|
6356
|
+
try {
|
|
6357
|
+
const client = getFluidGridsClient();
|
|
6358
|
+
const result = throwIfResultError(
|
|
6359
|
+
await client.query({
|
|
6360
|
+
query: EXECUTION_DETAIL,
|
|
6361
|
+
variables: { id: runId },
|
|
6362
|
+
fetchPolicy: "network-only"
|
|
6363
|
+
})
|
|
6364
|
+
);
|
|
6365
|
+
const run = result.data?.workflowRun;
|
|
6366
|
+
if (!run) {
|
|
6367
|
+
return { success: false, error: "Execution not found" };
|
|
6368
|
+
}
|
|
6369
|
+
if (run.mode !== "TEST") {
|
|
6370
|
+
return {
|
|
6371
|
+
success: false,
|
|
6372
|
+
error: "Only TEST runs can be loaded for continuation."
|
|
6373
|
+
};
|
|
6374
|
+
}
|
|
6375
|
+
if (!TERMINAL_STATUSES.has(run.status)) {
|
|
6376
|
+
return {
|
|
6377
|
+
success: false,
|
|
6378
|
+
error: "Only terminal TEST runs can be loaded for continuation."
|
|
6379
|
+
};
|
|
6380
|
+
}
|
|
6381
|
+
if (requestId !== loadRequestRef.current) {
|
|
6382
|
+
return { success: false, error: "Execution load superseded." };
|
|
6383
|
+
}
|
|
6384
|
+
const currentWorkflow = useFlowStore.getState().workflow;
|
|
6385
|
+
const currentWorkflowKey = currentWorkflow.workflowKey || currentWorkflow.id;
|
|
6386
|
+
if (run.workflowKey !== currentWorkflowKey || run.version !== currentWorkflow.version) {
|
|
6387
|
+
return {
|
|
6388
|
+
success: false,
|
|
6389
|
+
error: "The selected run belongs to a different workflow version."
|
|
6390
|
+
};
|
|
6391
|
+
}
|
|
6392
|
+
const logs = (run.nodeStates ?? []).map((nodeState) => {
|
|
6393
|
+
const rawStatus = nodeState.status.toLowerCase();
|
|
6394
|
+
const status = rawStatus === "completed" ? "completed" : rawStatus === "failed" || rawStatus === "timeout" ? "failed" : rawStatus === "running" || rawStatus === "waiting" || rawStatus === "delayed" || rawStatus === "waiting_subworkflow" ? "running" : "pending";
|
|
6395
|
+
return {
|
|
6396
|
+
id: `${runId}-${nodeState.nodeId}`,
|
|
6397
|
+
runId,
|
|
6398
|
+
nodeId: nodeState.nodeId,
|
|
6399
|
+
nodeType: nodeState.nodeType,
|
|
6400
|
+
nodeName: getNodeName(nodeState.nodeId),
|
|
6401
|
+
status,
|
|
6402
|
+
inputArgs: nodeState.inputArgs ?? null,
|
|
6403
|
+
output: nodeState.output ?? null,
|
|
6404
|
+
error: nodeState.error ?? null,
|
|
6405
|
+
duration: nodeState.duration ?? null,
|
|
6406
|
+
startTime: nodeState.startTime ?? run.startedAt ?? "",
|
|
6407
|
+
endTime: nodeState.endTime ?? null,
|
|
6408
|
+
usedPinnedData: nodeState.usedPinnedData ?? false,
|
|
6409
|
+
reusedFromRunId: nodeState.reusedFromRunId ?? null
|
|
6410
|
+
};
|
|
6411
|
+
});
|
|
6412
|
+
loadDebugRun(runId, logs, {
|
|
6413
|
+
workflowKey: run.workflowKey,
|
|
6414
|
+
version: run.version,
|
|
6415
|
+
sourceMode: run.mode
|
|
6416
|
+
});
|
|
6417
|
+
return { success: true, runId };
|
|
6418
|
+
} catch (error) {
|
|
6419
|
+
console.error("[useNodeExecution] Error loading execution:", error);
|
|
6420
|
+
return { success: false, error: describeOperationError(error) };
|
|
6421
|
+
}
|
|
6422
|
+
},
|
|
6423
|
+
[cleanup, getNodeName, loadDebugRun]
|
|
6114
6424
|
);
|
|
6115
|
-
|
|
6116
|
-
|
|
6117
|
-
if (
|
|
6118
|
-
|
|
6119
|
-
}
|
|
6120
|
-
NODE_TYPES[STICKY_NOTE_TYPE] = StickyNoteNode;
|
|
6121
|
-
function registerWorkflowNodeTypes(workflowNodes) {
|
|
6122
|
-
for (const node of workflowNodes) {
|
|
6123
|
-
if (node.type && !NODE_TYPES[node.type]) {
|
|
6124
|
-
const meta = getNodeMetadata({
|
|
6125
|
-
type: node.type,
|
|
6126
|
-
defaultData: {
|
|
6127
|
-
version: node.data?.version,
|
|
6128
|
-
nodeData: node.data?.nodeData
|
|
6129
|
-
},
|
|
6130
|
-
operations: node.data?.operations
|
|
6131
|
-
});
|
|
6132
|
-
if (meta?.node) {
|
|
6133
|
-
NODE_TYPES[node.type] = meta.node;
|
|
6425
|
+
useEffect(() => {
|
|
6426
|
+
return () => {
|
|
6427
|
+
if (pollTimerRef.current) {
|
|
6428
|
+
completeExecution();
|
|
6134
6429
|
}
|
|
6135
|
-
|
|
6136
|
-
|
|
6430
|
+
cleanup();
|
|
6431
|
+
};
|
|
6432
|
+
}, [cleanup, completeExecution]);
|
|
6433
|
+
return {
|
|
6434
|
+
executeStep,
|
|
6435
|
+
executeFullWorkflow,
|
|
6436
|
+
executeFromNode,
|
|
6437
|
+
loadExecution,
|
|
6438
|
+
cleanup,
|
|
6439
|
+
findTriggerNode
|
|
6440
|
+
};
|
|
6137
6441
|
}
|
|
6138
6442
|
var FluidGridsContext = createContext(
|
|
6139
6443
|
null
|
|
@@ -20485,116 +20789,6 @@ var ChatPanel = () => {
|
|
|
20485
20789
|
}
|
|
20486
20790
|
);
|
|
20487
20791
|
};
|
|
20488
|
-
|
|
20489
|
-
// src/builder/lib/field-schema.ts
|
|
20490
|
-
function isRecord4(value) {
|
|
20491
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
20492
|
-
}
|
|
20493
|
-
function isIdentifier(key) {
|
|
20494
|
-
return /^[A-Za-z_$][\w$]*$/.test(key);
|
|
20495
|
-
}
|
|
20496
|
-
function joinPath(base, key) {
|
|
20497
|
-
if (isIdentifier(key)) return base ? `${base}.${key}` : key;
|
|
20498
|
-
const bracket = `[${JSON.stringify(key)}]`;
|
|
20499
|
-
return base ? `${base}${bracket}` : bracket;
|
|
20500
|
-
}
|
|
20501
|
-
function pathToExpression(path) {
|
|
20502
|
-
if (!path) return "${output}";
|
|
20503
|
-
const accessor = path.startsWith("[") ? `output${path}` : `output.${path}`;
|
|
20504
|
-
return "${" + accessor + "}";
|
|
20505
|
-
}
|
|
20506
|
-
function fieldFromSpec(key, path, spec) {
|
|
20507
|
-
if (typeof spec === "string") {
|
|
20508
|
-
return { key, path, type: spec };
|
|
20509
|
-
}
|
|
20510
|
-
if (isRecord4(spec)) {
|
|
20511
|
-
const declaredType = typeof spec.type === "string" ? spec.type : void 0;
|
|
20512
|
-
const description = typeof spec.description === "string" ? spec.description : void 0;
|
|
20513
|
-
const required = spec.required === true;
|
|
20514
|
-
if (isRecord4(spec.properties)) {
|
|
20515
|
-
const children = Object.entries(spec.properties).map(
|
|
20516
|
-
([k, v]) => fieldFromSpec(k, joinPath(path, k), v)
|
|
20517
|
-
);
|
|
20518
|
-
return { key, path, type: "object", required, description, children };
|
|
20519
|
-
}
|
|
20520
|
-
if (spec.items !== void 0) {
|
|
20521
|
-
const element = fieldFromSpec("0", `${path}[0]`, spec.items);
|
|
20522
|
-
return {
|
|
20523
|
-
key,
|
|
20524
|
-
path,
|
|
20525
|
-
type: "array",
|
|
20526
|
-
required,
|
|
20527
|
-
description,
|
|
20528
|
-
children: [element]
|
|
20529
|
-
};
|
|
20530
|
-
}
|
|
20531
|
-
return { key, path, type: declaredType ?? "any", required, description };
|
|
20532
|
-
}
|
|
20533
|
-
return { key, path, type: "any" };
|
|
20534
|
-
}
|
|
20535
|
-
function fieldsFromIO(io, root = "") {
|
|
20536
|
-
if (!isRecord4(io)) return [];
|
|
20537
|
-
return Object.entries(io).map(
|
|
20538
|
-
([key, spec]) => fieldFromSpec(key, joinPath(root, key), spec)
|
|
20539
|
-
);
|
|
20540
|
-
}
|
|
20541
|
-
function typeOfValue(value) {
|
|
20542
|
-
if (value === null || value === void 0) return "any";
|
|
20543
|
-
if (Array.isArray(value)) return "array";
|
|
20544
|
-
if (typeof value === "object") return "object";
|
|
20545
|
-
if (typeof value === "number") return "number";
|
|
20546
|
-
if (typeof value === "boolean") return "boolean";
|
|
20547
|
-
if (typeof value === "string") return "string";
|
|
20548
|
-
return "any";
|
|
20549
|
-
}
|
|
20550
|
-
function fieldFromValue(key, path, value) {
|
|
20551
|
-
if (Array.isArray(value)) {
|
|
20552
|
-
const children = value.length > 0 ? [fieldFromValue("0", `${path}[0]`, value[0])] : void 0;
|
|
20553
|
-
return { key, path, type: "array", children };
|
|
20554
|
-
}
|
|
20555
|
-
if (isRecord4(value)) {
|
|
20556
|
-
const children = Object.entries(value).map(
|
|
20557
|
-
([k, v]) => fieldFromValue(k, joinPath(path, k), v)
|
|
20558
|
-
);
|
|
20559
|
-
return { key, path, type: "object", children };
|
|
20560
|
-
}
|
|
20561
|
-
return { key, path, type: typeOfValue(value) };
|
|
20562
|
-
}
|
|
20563
|
-
function fieldsFromValue(value, root = "") {
|
|
20564
|
-
if (!isRecord4(value)) return [];
|
|
20565
|
-
return Object.entries(value).map(
|
|
20566
|
-
([key, v]) => fieldFromValue(key, joinPath(root, key), v)
|
|
20567
|
-
);
|
|
20568
|
-
}
|
|
20569
|
-
function declaredOutputFields(nodeType, op) {
|
|
20570
|
-
const name = op?.name;
|
|
20571
|
-
if (!name || nodeType?.startsWith("internal:")) {
|
|
20572
|
-
return fieldsFromIO(op?.output_fields);
|
|
20573
|
-
}
|
|
20574
|
-
const children = fieldsFromIO(op?.output_fields, name);
|
|
20575
|
-
if (children.length === 0) return [];
|
|
20576
|
-
return [{ key: name, path: name, type: "object", children }];
|
|
20577
|
-
}
|
|
20578
|
-
function normalizeOperations(raw) {
|
|
20579
|
-
if (Array.isArray(raw)) return raw;
|
|
20580
|
-
if (isRecord4(raw) && Array.isArray(raw.operations)) {
|
|
20581
|
-
return raw.operations;
|
|
20582
|
-
}
|
|
20583
|
-
return [];
|
|
20584
|
-
}
|
|
20585
|
-
function resolveNodeOperation(data, fallbackOperations2) {
|
|
20586
|
-
const nodeData = isRecord4(data?.nodeData) ? data?.nodeData : void 0;
|
|
20587
|
-
let operations = normalizeOperations(nodeData?.operations);
|
|
20588
|
-
if (operations.length === 0) {
|
|
20589
|
-
operations = normalizeOperations(data?.operations);
|
|
20590
|
-
}
|
|
20591
|
-
if (operations.length === 0 && fallbackOperations2 !== void 0) {
|
|
20592
|
-
operations = normalizeOperations(fallbackOperations2);
|
|
20593
|
-
}
|
|
20594
|
-
const opName = typeof data?.operation_name === "string" ? data.operation_name : void 0;
|
|
20595
|
-
const selected = operations.find((op) => op.name === opName) ?? operations[0];
|
|
20596
|
-
return { operations, selected };
|
|
20597
|
-
}
|
|
20598
20792
|
var FIELD_PATH_MIME = "application/x-fg-field-path";
|
|
20599
20793
|
var TYPE_ICON = {
|
|
20600
20794
|
string: "mdi:format-text",
|
|
@@ -21705,6 +21899,65 @@ function NodeLoadOptionsSelect({
|
|
|
21705
21899
|
}
|
|
21706
21900
|
);
|
|
21707
21901
|
}
|
|
21902
|
+
function StructuredParameterInput({
|
|
21903
|
+
name,
|
|
21904
|
+
schema,
|
|
21905
|
+
value,
|
|
21906
|
+
onChange,
|
|
21907
|
+
className,
|
|
21908
|
+
testId
|
|
21909
|
+
}) {
|
|
21910
|
+
const tr = useTr();
|
|
21911
|
+
const descriptionId = useId();
|
|
21912
|
+
const [draft, setDraft] = useState(() => ({
|
|
21913
|
+
value,
|
|
21914
|
+
text: formatParameterValue(value)
|
|
21915
|
+
}));
|
|
21916
|
+
const text = Object.is(draft.value, value) ? draft.text : formatParameterValue(value);
|
|
21917
|
+
const result = parseParameterValue(schema, text);
|
|
21918
|
+
const type = structuredParameterType(schema);
|
|
21919
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
21920
|
+
/* @__PURE__ */ jsx(
|
|
21921
|
+
"textarea",
|
|
21922
|
+
{
|
|
21923
|
+
"aria-label": name,
|
|
21924
|
+
"aria-describedby": descriptionId,
|
|
21925
|
+
"aria-invalid": !result.valid,
|
|
21926
|
+
value: text,
|
|
21927
|
+
rows: 4,
|
|
21928
|
+
spellCheck: false,
|
|
21929
|
+
className,
|
|
21930
|
+
"data-testid": testId,
|
|
21931
|
+
placeholder: type === "array" ? "[]" : "{}",
|
|
21932
|
+
onChange: (event) => {
|
|
21933
|
+
const nextText = event.target.value;
|
|
21934
|
+
const nextResult = parseParameterValue(schema, nextText);
|
|
21935
|
+
const nextValue = nextResult.valid ? nextResult.value : nextText;
|
|
21936
|
+
setDraft({ value: nextValue, text: nextText });
|
|
21937
|
+
onChange(nextValue);
|
|
21938
|
+
}
|
|
21939
|
+
}
|
|
21940
|
+
),
|
|
21941
|
+
/* @__PURE__ */ jsx(
|
|
21942
|
+
"span",
|
|
21943
|
+
{
|
|
21944
|
+
id: descriptionId,
|
|
21945
|
+
role: result.valid ? void 0 : "alert",
|
|
21946
|
+
className: result.valid ? "text-[11px] text-muted-foreground" : "text-[11px] text-destructive",
|
|
21947
|
+
children: result.valid ? type === "array" ? tr(
|
|
21948
|
+
"fluidgrids.builder.nodeParamsForm.arrayHint",
|
|
21949
|
+
"Enter a JSON array. String lists also accept comma-separated values."
|
|
21950
|
+
) : tr(
|
|
21951
|
+
"fluidgrids.builder.nodeParamsForm.objectHint",
|
|
21952
|
+
"Enter a JSON object."
|
|
21953
|
+
) : tr(
|
|
21954
|
+
"fluidgrids.builder.nodeParamsForm.structuredInputInvalid",
|
|
21955
|
+
result.error
|
|
21956
|
+
)
|
|
21957
|
+
}
|
|
21958
|
+
)
|
|
21959
|
+
] });
|
|
21960
|
+
}
|
|
21708
21961
|
|
|
21709
21962
|
// src/builder/lib/mapping-values.ts
|
|
21710
21963
|
function isMappingValue(value) {
|
|
@@ -21822,6 +22075,7 @@ function paramsFromOperation(op) {
|
|
|
21822
22075
|
description: typeof rec?.description === "string" ? rec.description : void 0,
|
|
21823
22076
|
options,
|
|
21824
22077
|
defaultValue: rec?.default,
|
|
22078
|
+
schema: spec,
|
|
21825
22079
|
loadOptions
|
|
21826
22080
|
};
|
|
21827
22081
|
});
|
|
@@ -21986,18 +22240,24 @@ function useNodeConfig(target, options) {
|
|
|
21986
22240
|
};
|
|
21987
22241
|
const writeFixed = (field, value) => {
|
|
21988
22242
|
if (!nodeId) return;
|
|
22243
|
+
const parameter = params.find((candidate) => candidate.name === field);
|
|
22244
|
+
const parsed = parseParameterValue(
|
|
22245
|
+
parameter?.schema ?? parameter?.type,
|
|
22246
|
+
value
|
|
22247
|
+
);
|
|
22248
|
+
const nextValue = parsed.valid ? parsed.value : value;
|
|
21989
22249
|
const store = useFlowStore.getState();
|
|
21990
22250
|
const next = store.workflow.nodes.map((n) => {
|
|
21991
22251
|
if (n.id !== nodeId) return n;
|
|
21992
22252
|
const data = { ...asRecord(n.data) ?? {} };
|
|
21993
22253
|
const args = { ...asRecord(data.args) ?? {} };
|
|
21994
22254
|
const state = { ...asRecord(args.state) ?? {} };
|
|
21995
|
-
if (
|
|
22255
|
+
if (nextValue === void 0 || nextValue === null || nextValue === "") {
|
|
21996
22256
|
delete state[field];
|
|
22257
|
+
args.state = state;
|
|
21997
22258
|
} else {
|
|
21998
|
-
state[field]
|
|
22259
|
+
args.state = { ...state, [field]: nextValue };
|
|
21999
22260
|
}
|
|
22000
|
-
args.state = state;
|
|
22001
22261
|
data.args = args;
|
|
22002
22262
|
return { ...n, data };
|
|
22003
22263
|
});
|
|
@@ -22436,6 +22696,17 @@ function NodeParamsForm({
|
|
|
22436
22696
|
onChange: (v) => writeFixed(p.name, v),
|
|
22437
22697
|
"data-testid": `${testIdPrefix}-param-${p.name}`
|
|
22438
22698
|
}
|
|
22699
|
+
) : structuredParameterType(p.schema ?? p.type) ? /* @__PURE__ */ jsx(
|
|
22700
|
+
StructuredParameterInput,
|
|
22701
|
+
{
|
|
22702
|
+
name: p.name,
|
|
22703
|
+
schema: p.schema ?? p.type,
|
|
22704
|
+
value: fixedValue,
|
|
22705
|
+
onChange: (value) => writeFixed(p.name, value),
|
|
22706
|
+
className: cn(inputCx2, "font-mono"),
|
|
22707
|
+
testId: `${testIdPrefix}-param-${p.name}`
|
|
22708
|
+
},
|
|
22709
|
+
`${config2.node?.id}:${config2.currentOp?.name}:${p.name}`
|
|
22439
22710
|
) : p.loadOptions ? /* @__PURE__ */ jsx(
|
|
22440
22711
|
NodeLoadOptionsSelect,
|
|
22441
22712
|
{
|
|
@@ -24371,7 +24642,7 @@ function QuickConfigPanelInner({
|
|
|
24371
24642
|
}
|
|
24372
24643
|
|
|
24373
24644
|
// src/utils/workflow-definition-diff.ts
|
|
24374
|
-
function
|
|
24645
|
+
function isRecord6(value) {
|
|
24375
24646
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
24376
24647
|
}
|
|
24377
24648
|
function equalJson(left, right) {
|
|
@@ -24379,7 +24650,7 @@ function equalJson(left, right) {
|
|
|
24379
24650
|
if (Array.isArray(left) && Array.isArray(right)) {
|
|
24380
24651
|
return left.length === right.length && left.every((value, index) => equalJson(value, right[index]));
|
|
24381
24652
|
}
|
|
24382
|
-
if (
|
|
24653
|
+
if (isRecord6(left) && isRecord6(right)) {
|
|
24383
24654
|
const leftKeys = Object.keys(left).sort((a, b) => a.localeCompare(b));
|
|
24384
24655
|
const rightKeys = Object.keys(right).sort((a, b) => a.localeCompare(b));
|
|
24385
24656
|
return leftKeys.length === rightKeys.length && leftKeys.every(
|
|
@@ -24392,7 +24663,7 @@ function diffWorkflowDefinitionJson(before, after, maxEntries = 2e3) {
|
|
|
24392
24663
|
const entries = [];
|
|
24393
24664
|
const visit = (left, right, path) => {
|
|
24394
24665
|
if (entries.length >= maxEntries || equalJson(left, right)) return;
|
|
24395
|
-
if (
|
|
24666
|
+
if (isRecord6(left) && isRecord6(right)) {
|
|
24396
24667
|
const keys = [
|
|
24397
24668
|
.../* @__PURE__ */ new Set([...Object.keys(left), ...Object.keys(right)])
|
|
24398
24669
|
].sort((a, b) => a.localeCompare(b));
|