@datatechsolutions/ui 2.11.81 → 2.11.82
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/astrlabe/contracts.d.mts +5 -0
- package/dist/astrlabe/contracts.d.ts +5 -0
- package/dist/astrlabe/index.d.mts +11 -83
- package/dist/astrlabe/index.d.ts +11 -83
- package/dist/astrlabe/index.js +175 -4777
- package/dist/astrlabe/index.js.map +1 -1
- package/dist/astrlabe/index.mjs +3 -4740
- package/dist/astrlabe/index.mjs.map +1 -1
- package/dist/astrlabe/workflow-canvas.d.mts +69 -5
- package/dist/astrlabe/workflow-canvas.d.ts +69 -5
- package/dist/chunk-6PBTB5ZX.js +165 -0
- package/dist/chunk-6PBTB5ZX.js.map +1 -0
- package/dist/chunk-HAZP5J67.mjs +4781 -0
- package/dist/chunk-HAZP5J67.mjs.map +1 -0
- package/dist/chunk-HZ4LOVHM.js +46 -0
- package/dist/chunk-HZ4LOVHM.js.map +1 -0
- package/dist/chunk-K4QJV3GC.js +4825 -0
- package/dist/chunk-K4QJV3GC.js.map +1 -0
- package/dist/chunk-UHHPBREK.mjs +135 -0
- package/dist/chunk-UHHPBREK.mjs.map +1 -0
- package/dist/chunk-ZJPNP2YW.mjs +44 -0
- package/dist/chunk-ZJPNP2YW.mjs.map +1 -0
- package/dist/{workflow-canvas-NSxfr5dy.d.ts → index-AioB90qq.d.mts} +2 -67
- package/dist/{workflow-canvas-D4928AfA.d.mts → index-D5ai0cGZ.d.ts} +2 -67
- package/dist/platform/index.d.mts +41 -0
- package/dist/platform/index.d.ts +41 -0
- package/dist/platform/index.js +237 -0
- package/dist/platform/index.js.map +1 -0
- package/dist/platform/index.mjs +109 -0
- package/dist/platform/index.mjs.map +1 -0
- package/dist/platform/pages/index.d.mts +272 -0
- package/dist/platform/pages/index.d.ts +272 -0
- package/dist/platform/pages/index.js +1793 -0
- package/dist/platform/pages/index.js.map +1 -0
- package/dist/platform/pages/index.mjs +1777 -0
- package/dist/platform/pages/index.mjs.map +1 -0
- package/dist/platform/rbac.d.mts +41 -0
- package/dist/platform/rbac.d.ts +41 -0
- package/dist/platform/rbac.js +13 -0
- package/dist/platform/rbac.js.map +1 -0
- package/dist/platform/rbac.mjs +4 -0
- package/dist/platform/rbac.mjs.map +1 -0
- package/dist/platform/utils/index.d.mts +32 -0
- package/dist/platform/utils/index.d.ts +32 -0
- package/dist/platform/utils/index.js +131 -0
- package/dist/platform/utils/index.js.map +1 -0
- package/dist/platform/utils/index.mjs +119 -0
- package/dist/platform/utils/index.mjs.map +1 -0
- package/dist/platform/windsock-admin-client.d.mts +57 -0
- package/dist/platform/windsock-admin-client.d.ts +57 -0
- package/dist/platform/windsock-admin-client.js +125 -0
- package/dist/platform/windsock-admin-client.js.map +1 -0
- package/dist/platform/windsock-admin-client.mjs +4 -0
- package/dist/platform/windsock-admin-client.mjs.map +1 -0
- package/dist/rule-form-F5jBOeqk.d.mts +79 -0
- package/dist/rule-form-F5jBOeqk.d.ts +79 -0
- package/package.json +27 -1
|
@@ -0,0 +1,4781 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { Workspace, useModalStore, CATEGORY_COLORS, CATEGORY_PILL_COLORS, ICON_MAP, WorkflowCanvas, getEntityIcon, getEntityGradient, useWorkflowStore, LOGIC_ICON_MAP, LOGIC_NODE_GRADIENTS, getFrameworkMeta, getCompatibleModels, isModelCompatibleWithFramework, FRAMEWORK_META, isFrameworkCompatibleWithProviders } from './chunk-J3OYJ44D.mjs';
|
|
3
|
+
import { Button, FormInput, FormSelect, GlassModal, FormTextarea, FormGrid, Badge, ToggleSwitch, Input, DynamicIslandConfirm } from './chunk-LLFU42KC.mjs';
|
|
4
|
+
import { useTranslations } from './chunk-7VJ7CMMT.mjs';
|
|
5
|
+
import { getAgentTier, createDefaultLogicNodeConfig } from './chunk-WNCPAWLC.mjs';
|
|
6
|
+
import { memo, useCallback, useMemo, useState, useEffect, useRef, Children } from 'react';
|
|
7
|
+
import { UserCircleIcon, Cog6ToothIcon, SparklesIcon, CommandLineIcon, KeyIcon, PlayCircleIcon, CpuChipIcon, ArrowPathRoundedSquareIcon, ArrowsPointingOutIcon, ArrowsPointingInIcon, BoltIcon, ClockIcon, CheckIcon, ArrowPathIcon, AdjustmentsHorizontalIcon, CircleStackIcon, TrashIcon, PlusIcon, XMarkIcon, EyeIcon, PlayIcon, StopIcon, XCircleIcon, CheckCircleIcon, VariableIcon, ChevronDownIcon, ChevronRightIcon, ExclamationCircleIcon, ClipboardDocumentIcon, ArrowDownTrayIcon, ArrowUpTrayIcon, ExclamationTriangleIcon } from '@heroicons/react/24/outline';
|
|
8
|
+
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
9
|
+
import { create } from 'zustand';
|
|
10
|
+
|
|
11
|
+
function AnthropicModelIcon({ className }) {
|
|
12
|
+
return /* @__PURE__ */ jsx("svg", { role: "img", viewBox: "0 0 24 24", fill: "currentColor", className, "aria-label": "Anthropic", children: /* @__PURE__ */ jsx("path", { d: "M17.3041 3.541h-3.6718l6.696 16.918H24Zm-10.6082 0L0 20.459h3.7442l1.3693-3.5527h7.0052l1.3693 3.5528h3.7442L10.5363 3.5409Zm-.3712 10.2232 2.2914-5.9456 2.2914 5.9456Z" }) });
|
|
13
|
+
}
|
|
14
|
+
function AmazonNovaIcon({ className }) {
|
|
15
|
+
return /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 24 24", fill: "none", className, "aria-label": "Amazon Nova", children: [
|
|
16
|
+
/* @__PURE__ */ jsx("path", { d: "M12 2L3 20h3.5l1.8-4.5h7.4L17.5 20H21L12 2Zm-2.2 11L12 7.5l2.2 5.5H9.8Z", fill: "currentColor" }),
|
|
17
|
+
/* @__PURE__ */ jsx("path", { d: "M2 21c3.5 1.5 7.5 2 10 2s6.5-.5 10-2", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", fill: "none", opacity: "0.6" })
|
|
18
|
+
] });
|
|
19
|
+
}
|
|
20
|
+
function MetaLlamaIcon({ className }) {
|
|
21
|
+
return /* @__PURE__ */ jsx("svg", { role: "img", viewBox: "0 0 24 24", fill: "currentColor", className, "aria-label": "Meta", children: /* @__PURE__ */ jsx("path", { d: "M6.915 4.03c-1.968 0-3.683 1.28-4.871 3.113C.704 9.208 0 11.883 0 14.449c0 .706.07 1.369.21 1.973a6.624 6.624 0 0 0 .265.86 5.297 5.297 0 0 0 .371.761c.696 1.159 1.818 1.927 3.593 1.927 1.497 0 2.633-.671 3.965-2.444.76-1.012 1.144-1.626 2.663-4.32l.756-1.339.186-.325c.061.1.121.196.183.3l2.152 3.595c.724 1.21 1.665 2.556 2.47 3.314 1.046.987 1.992 1.22 3.06 1.22 1.075 0 1.876-.355 2.455-.843a3.743 3.743 0 0 0 .81-.973c.542-.939.861-2.127.861-3.745 0-2.72-.681-5.357-2.084-7.45-1.282-1.912-2.957-2.93-4.716-2.93-1.047 0-2.088.467-3.053 1.308-.652.57-1.257 1.29-1.82 2.05-.69-.875-1.335-1.547-1.958-2.056-1.182-.966-2.315-1.303-3.454-1.303zm10.16 2.053c1.147 0 2.188.758 2.992 1.999 1.132 1.748 1.647 4.195 1.647 6.4 0 1.548-.368 2.9-1.839 2.9-.58 0-1.027-.23-1.664-1.004-.496-.601-1.343-1.878-2.832-4.358l-.617-1.028a44.908 44.908 0 0 0-1.255-1.98c.07-.109.141-.224.211-.327 1.12-1.667 2.118-2.602 3.358-2.602zm-10.201.553c1.265 0 2.058.791 2.675 1.446.307.327.737.871 1.234 1.579l-1.02 1.566c-.757 1.163-1.882 3.017-2.837 4.338-1.191 1.649-1.81 1.817-2.486 1.817-.524 0-1.038-.237-1.383-.794-.263-.426-.464-1.13-.464-2.046 0-2.221.63-4.535 1.66-6.088.454-.687.964-1.226 1.533-1.533a2.264 2.264 0 0 1 1.088-.285z" }) });
|
|
22
|
+
}
|
|
23
|
+
function OpenAIModelIcon({ className }) {
|
|
24
|
+
return /* @__PURE__ */ jsx("svg", { viewBox: "0 0 16 16", fill: "currentColor", className, "aria-label": "OpenAI", children: /* @__PURE__ */ jsx("path", { d: "M14.949 6.547a3.94 3.94 0 0 0-.348-3.273 4.11 4.11 0 0 0-4.4-1.934A4.1 4.1 0 0 0 8.423.2 4.15 4.15 0 0 0 6.305.086a4.1 4.1 0 0 0-1.891.948 4.04 4.04 0 0 0-1.158 1.753 4.1 4.1 0 0 0-1.563.679A4 4 0 0 0 .554 4.72a3.99 3.99 0 0 0 .502 4.731 3.94 3.94 0 0 0 .346 3.274 4.11 4.11 0 0 0 4.402 1.933c.382.425.852.764 1.377.995.526.231 1.095.35 1.67.346 1.78.002 3.358-1.132 3.901-2.804a4.1 4.1 0 0 0 1.563-.68 4 4 0 0 0 1.14-1.253 3.99 3.99 0 0 0-.506-4.716m-6.097 8.406a3.05 3.05 0 0 1-1.945-.694l.096-.054 3.23-1.838a.53.53 0 0 0 .265-.455v-4.49l1.366.778q.02.011.025.035v3.722c-.003 1.653-1.361 2.992-3.037 2.996m-6.53-2.75a2.95 2.95 0 0 1-.36-2.01l.095.057L5.29 12.09a.53.53 0 0 0 .527 0l3.949-2.246v1.555a.05.05 0 0 1-.022.041L6.473 13.3c-1.454.826-3.311.335-4.15-1.098m-.85-6.94A3.02 3.02 0 0 1 3.07 3.949v3.785a.51.51 0 0 0 .262.451l3.93 2.237-1.366.779a.05.05 0 0 1-.048 0L2.585 9.342a2.98 2.98 0 0 1-1.113-4.094zm11.216 2.571L8.747 5.576l1.362-.776a.05.05 0 0 1 .048 0l3.265 1.86a3 3 0 0 1 1.173 1.207 2.96 2.96 0 0 1-.27 3.2 3.05 3.05 0 0 1-1.36.997V8.279a.52.52 0 0 0-.276-.445m1.36-2.015-.097-.057-3.226-1.855a.53.53 0 0 0-.53 0L6.249 6.153V4.598a.04.04 0 0 1 .019-.04L9.533 2.7a3.07 3.07 0 0 1 3.257.139c.474.325.843.778 1.066 1.303.223.526.289 1.103.191 1.664zM5.503 8.575 4.139 7.8a.05.05 0 0 1-.026-.037V4.049c0-.57.166-1.127.476-1.607s.752-.864 1.275-1.105a3.08 3.08 0 0 1 3.234.41l-.096.054-3.23 1.838a.53.53 0 0 0-.265.455zm.742-1.577 1.758-1 1.762 1v2l-1.755 1-1.762-1z" }) });
|
|
25
|
+
}
|
|
26
|
+
function GoogleGeminiIcon({ className }) {
|
|
27
|
+
return /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 24 24", fill: "none", className, "aria-label": "Google Gemini", children: [
|
|
28
|
+
/* @__PURE__ */ jsx("path", { d: "M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2Z", fill: "#4285F4", opacity: "0.15" }),
|
|
29
|
+
/* @__PURE__ */ jsx("path", { d: "M12 2.5c5.25 0 9.5 4.25 9.5 9.5s-4.25 9.5-9.5 9.5S2.5 17.25 2.5 12 6.75 2.5 12 2.5Z", stroke: "#4285F4", strokeWidth: "1", fill: "none" }),
|
|
30
|
+
/* @__PURE__ */ jsx("path", { d: "M12 7L8 12l4 5 4-5-4-5Z", fill: "#4285F4" }),
|
|
31
|
+
/* @__PURE__ */ jsx("path", { d: "M12 7L8 12l4 5", fill: "#34A853" })
|
|
32
|
+
] });
|
|
33
|
+
}
|
|
34
|
+
function getModelIcon(modelId) {
|
|
35
|
+
if (modelId.startsWith("anthropic.")) {
|
|
36
|
+
return { IconComponent: AnthropicModelIcon, color: "text-amber-600 dark:text-amber-400", providerLabel: "Anthropic" };
|
|
37
|
+
}
|
|
38
|
+
if (modelId.startsWith("amazon.")) {
|
|
39
|
+
return { IconComponent: AmazonNovaIcon, color: "text-orange-500 dark:text-orange-400", providerLabel: "Amazon" };
|
|
40
|
+
}
|
|
41
|
+
if (modelId.startsWith("meta.")) {
|
|
42
|
+
return { IconComponent: MetaLlamaIcon, color: "text-blue-500 dark:text-blue-400", providerLabel: "Meta" };
|
|
43
|
+
}
|
|
44
|
+
if (modelId.startsWith("google.")) {
|
|
45
|
+
return { IconComponent: GoogleGeminiIcon, color: "text-blue-500 dark:text-blue-400", providerLabel: "Google" };
|
|
46
|
+
}
|
|
47
|
+
if (modelId.startsWith("openai.")) {
|
|
48
|
+
return { IconComponent: OpenAIModelIcon, color: "text-emerald-500 dark:text-emerald-400", providerLabel: "OpenAI" };
|
|
49
|
+
}
|
|
50
|
+
return { IconComponent: AnthropicModelIcon, color: "text-gray-500 dark:text-gray-400", providerLabel: "Custom" };
|
|
51
|
+
}
|
|
52
|
+
var PRIMITIVE_TYPES = [
|
|
53
|
+
{ value: "string", label: "string" },
|
|
54
|
+
{ value: "number", label: "number" },
|
|
55
|
+
{ value: "integer", label: "integer" },
|
|
56
|
+
{ value: "boolean", label: "boolean" }
|
|
57
|
+
];
|
|
58
|
+
var COMPLEX_TYPES = [
|
|
59
|
+
{ value: "object", label: "object (nested)" },
|
|
60
|
+
{ value: "array", label: "array of objects" }
|
|
61
|
+
];
|
|
62
|
+
var ALL_TYPES = [...PRIMITIVE_TYPES, ...COMPLEX_TYPES];
|
|
63
|
+
function OutputSchemaBuilder({ value, onChange, depth = 0 }) {
|
|
64
|
+
const fields = useMemo(() => {
|
|
65
|
+
if (!value || value.type !== "object") return [];
|
|
66
|
+
return buildFieldRows(value);
|
|
67
|
+
}, [value]);
|
|
68
|
+
if (!value) {
|
|
69
|
+
return /* @__PURE__ */ jsxs("div", { className: "rounded-xl border border-dashed border-slate-300 bg-slate-50/40 p-4 text-center dark:border-slate-700 dark:bg-slate-900/30", children: [
|
|
70
|
+
/* @__PURE__ */ jsx("p", { className: "mb-3 text-xs text-slate-500 dark:text-slate-400", children: "No schema yet \u2014 agent text is exposed as a raw string. Add a schema to enforce structured output." }),
|
|
71
|
+
/* @__PURE__ */ jsx(
|
|
72
|
+
Button,
|
|
73
|
+
{
|
|
74
|
+
type: "button",
|
|
75
|
+
outline: true,
|
|
76
|
+
onClick: () => onChange({ type: "object", properties: {}, required: [] }),
|
|
77
|
+
children: "+ Add output schema"
|
|
78
|
+
}
|
|
79
|
+
)
|
|
80
|
+
] });
|
|
81
|
+
}
|
|
82
|
+
if (value.type !== "object") {
|
|
83
|
+
return /* @__PURE__ */ jsxs("div", { className: "rounded-xl border border-amber-300 bg-amber-50/40 p-3 text-xs text-amber-700 dark:border-amber-700 dark:bg-amber-900/20 dark:text-amber-300", children: [
|
|
84
|
+
"The visual builder only supports `type: object` at the top level. Convert manually or",
|
|
85
|
+
/* @__PURE__ */ jsx(
|
|
86
|
+
"button",
|
|
87
|
+
{
|
|
88
|
+
type: "button",
|
|
89
|
+
className: "ml-1 underline",
|
|
90
|
+
onClick: () => onChange({ type: "object", properties: {}, required: [] }),
|
|
91
|
+
children: "reset to object"
|
|
92
|
+
}
|
|
93
|
+
),
|
|
94
|
+
"."
|
|
95
|
+
] });
|
|
96
|
+
}
|
|
97
|
+
const updateFields = (next) => {
|
|
98
|
+
onChange(rowsToSchema(next));
|
|
99
|
+
};
|
|
100
|
+
const addField = () => {
|
|
101
|
+
updateFields([
|
|
102
|
+
...fields,
|
|
103
|
+
{ name: "", type: "string", description: "", required: false, child: void 0 }
|
|
104
|
+
]);
|
|
105
|
+
};
|
|
106
|
+
const removeField = (index) => {
|
|
107
|
+
updateFields(fields.filter((_, i) => i !== index));
|
|
108
|
+
};
|
|
109
|
+
const patchField = (index, patch) => {
|
|
110
|
+
updateFields(fields.map((f, i) => i === index ? { ...f, ...patch } : f));
|
|
111
|
+
};
|
|
112
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
|
|
113
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
|
|
114
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-slate-700 dark:text-slate-200", children: "Top-level fields" }),
|
|
115
|
+
/* @__PURE__ */ jsx(
|
|
116
|
+
"button",
|
|
117
|
+
{
|
|
118
|
+
type: "button",
|
|
119
|
+
className: "text-xs text-rose-600 hover:text-rose-500",
|
|
120
|
+
onClick: () => onChange(void 0),
|
|
121
|
+
children: "Remove schema"
|
|
122
|
+
}
|
|
123
|
+
)
|
|
124
|
+
] }),
|
|
125
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
126
|
+
fields.map((field, index) => /* @__PURE__ */ jsx(
|
|
127
|
+
FieldEditor,
|
|
128
|
+
{
|
|
129
|
+
field,
|
|
130
|
+
onChange: (patch) => patchField(index, patch),
|
|
131
|
+
onRemove: () => removeField(index),
|
|
132
|
+
depth
|
|
133
|
+
},
|
|
134
|
+
index
|
|
135
|
+
)),
|
|
136
|
+
/* @__PURE__ */ jsx(Button, { type: "button", outline: true, onClick: addField, children: "+ Add field" })
|
|
137
|
+
] })
|
|
138
|
+
] });
|
|
139
|
+
}
|
|
140
|
+
function FieldEditor({
|
|
141
|
+
field,
|
|
142
|
+
onChange,
|
|
143
|
+
onRemove,
|
|
144
|
+
depth
|
|
145
|
+
}) {
|
|
146
|
+
const handleTypeChange = (next) => {
|
|
147
|
+
if (next === "object") {
|
|
148
|
+
onChange({ type: next, child: { type: "object", properties: {}, required: [] } });
|
|
149
|
+
} else if (next === "array") {
|
|
150
|
+
onChange({ type: next, child: { type: "object", properties: {}, required: [] } });
|
|
151
|
+
} else {
|
|
152
|
+
onChange({ type: next, child: void 0 });
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
const isComplex = field.type === "object" || field.type === "array";
|
|
156
|
+
return /* @__PURE__ */ jsxs("div", { className: "rounded-xl border border-slate-200 bg-slate-50/60 p-3 dark:border-slate-700 dark:bg-slate-900/40", children: [
|
|
157
|
+
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-[1fr_140px_auto_auto] items-end gap-2", children: [
|
|
158
|
+
/* @__PURE__ */ jsx(
|
|
159
|
+
FormInput,
|
|
160
|
+
{
|
|
161
|
+
label: "Name",
|
|
162
|
+
value: field.name,
|
|
163
|
+
onValueChange: (name) => onChange({ name }),
|
|
164
|
+
placeholder: "recommendedPrice"
|
|
165
|
+
}
|
|
166
|
+
),
|
|
167
|
+
/* @__PURE__ */ jsx(
|
|
168
|
+
FormSelect,
|
|
169
|
+
{
|
|
170
|
+
label: "Type",
|
|
171
|
+
value: field.type,
|
|
172
|
+
options: ALL_TYPES.map((t) => ({ value: t.value, label: t.label })),
|
|
173
|
+
onValueChange: (t) => handleTypeChange(t)
|
|
174
|
+
}
|
|
175
|
+
),
|
|
176
|
+
/* @__PURE__ */ jsxs("label", { className: "flex items-center gap-2 pb-2 text-xs text-slate-700 dark:text-slate-200", children: [
|
|
177
|
+
/* @__PURE__ */ jsx(
|
|
178
|
+
"input",
|
|
179
|
+
{
|
|
180
|
+
type: "checkbox",
|
|
181
|
+
checked: field.required,
|
|
182
|
+
onChange: (event) => onChange({ required: event.target.checked }),
|
|
183
|
+
className: "h-4 w-4 rounded border-slate-300"
|
|
184
|
+
}
|
|
185
|
+
),
|
|
186
|
+
"Required"
|
|
187
|
+
] }),
|
|
188
|
+
/* @__PURE__ */ jsx(Button, { type: "button", plain: true, onClick: onRemove, children: "Remove" })
|
|
189
|
+
] }),
|
|
190
|
+
/* @__PURE__ */ jsx(
|
|
191
|
+
FormInput,
|
|
192
|
+
{
|
|
193
|
+
label: "Description (optional)",
|
|
194
|
+
value: field.description,
|
|
195
|
+
onValueChange: (description) => onChange({ description }),
|
|
196
|
+
placeholder: "What the model should put here"
|
|
197
|
+
}
|
|
198
|
+
),
|
|
199
|
+
isComplex && depth < 1 && /* @__PURE__ */ jsxs("div", { className: "mt-3 rounded-lg border border-dashed border-slate-300 p-2 dark:border-slate-700", children: [
|
|
200
|
+
/* @__PURE__ */ jsx("p", { className: "mb-2 text-[11px] uppercase tracking-wider text-slate-400", children: field.type === "array" ? "Item shape" : "Nested fields" }),
|
|
201
|
+
/* @__PURE__ */ jsx(
|
|
202
|
+
OutputSchemaBuilder,
|
|
203
|
+
{
|
|
204
|
+
value: field.child,
|
|
205
|
+
onChange: (child) => onChange({ child }),
|
|
206
|
+
depth: depth + 1
|
|
207
|
+
}
|
|
208
|
+
)
|
|
209
|
+
] }),
|
|
210
|
+
isComplex && depth >= 1 && /* @__PURE__ */ jsx("p", { className: "mt-2 text-[11px] italic text-slate-500", children: "Deeper nesting is supported by the engine but not by this builder \u2014 edit the JSON directly if you need a third level." })
|
|
211
|
+
] });
|
|
212
|
+
}
|
|
213
|
+
function buildFieldRows(schema) {
|
|
214
|
+
const properties = schema.properties ?? {};
|
|
215
|
+
const required = new Set(schema.required ?? []);
|
|
216
|
+
return Object.entries(properties).map(([name, subschema]) => {
|
|
217
|
+
const t = subschema.type ?? "string";
|
|
218
|
+
const isObject = t === "object";
|
|
219
|
+
const isArray = t === "array";
|
|
220
|
+
return {
|
|
221
|
+
name,
|
|
222
|
+
type: isObject ? "object" : isArray ? "array" : t,
|
|
223
|
+
description: subschema.description ?? "",
|
|
224
|
+
required: required.has(name),
|
|
225
|
+
child: isObject ? subschema : isArray ? subschema.items : void 0
|
|
226
|
+
};
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
function rowsToSchema(rows) {
|
|
230
|
+
const properties = {};
|
|
231
|
+
const required = [];
|
|
232
|
+
for (const row of rows) {
|
|
233
|
+
const name = row.name.trim();
|
|
234
|
+
if (!name) continue;
|
|
235
|
+
let entry;
|
|
236
|
+
if (row.type === "object") {
|
|
237
|
+
entry = row.child && row.child.type === "object" ? row.child : { type: "object", properties: {}, required: [] };
|
|
238
|
+
} else if (row.type === "array") {
|
|
239
|
+
entry = {
|
|
240
|
+
type: "array",
|
|
241
|
+
items: row.child ?? { type: "object", properties: {}, required: [] }
|
|
242
|
+
};
|
|
243
|
+
} else {
|
|
244
|
+
entry = { type: row.type };
|
|
245
|
+
}
|
|
246
|
+
if (row.description) entry.description = row.description;
|
|
247
|
+
properties[name] = entry;
|
|
248
|
+
if (row.required) required.push(name);
|
|
249
|
+
}
|
|
250
|
+
const out = { type: "object", properties };
|
|
251
|
+
if (required.length > 0) out.required = required;
|
|
252
|
+
return out;
|
|
253
|
+
}
|
|
254
|
+
function defaultAgentOutputSchema() {
|
|
255
|
+
return { type: "object", properties: {}, required: [] };
|
|
256
|
+
}
|
|
257
|
+
function StatusBadge({ status }) {
|
|
258
|
+
const colorMap = {
|
|
259
|
+
pending: "bg-gray-100 text-gray-700 dark:bg-gray-800 dark:text-gray-300",
|
|
260
|
+
running: "bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-300",
|
|
261
|
+
success: "bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-300",
|
|
262
|
+
error: "bg-red-100 text-red-700 dark:bg-red-900/30 dark:text-red-300"
|
|
263
|
+
};
|
|
264
|
+
return /* @__PURE__ */ jsx("span", { className: `inline-flex items-center rounded-full px-2 py-0.5 text-[10px] font-semibold ${colorMap[status] ?? colorMap.pending}`, children: status });
|
|
265
|
+
}
|
|
266
|
+
function formatDuration(ms) {
|
|
267
|
+
if (ms < 1e3) return `${ms}ms`;
|
|
268
|
+
return `${(ms / 1e3).toFixed(2)}s`;
|
|
269
|
+
}
|
|
270
|
+
function isRecord(value) {
|
|
271
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
272
|
+
}
|
|
273
|
+
function OutputCard({ label, value }) {
|
|
274
|
+
const displayValue = typeof value === "string" ? value : JSON.stringify(value, null, 2);
|
|
275
|
+
const isLong = typeof displayValue === "string" && displayValue.length > 120;
|
|
276
|
+
return /* @__PURE__ */ jsxs("div", { className: "rounded-xl border border-white/20 bg-white/40 p-3 dark:border-white/10 dark:bg-white/5", children: [
|
|
277
|
+
/* @__PURE__ */ jsx("p", { className: "mb-1 text-[10px] font-semibold uppercase tracking-wider text-gray-500 dark:text-gray-400", children: label }),
|
|
278
|
+
isLong ? /* @__PURE__ */ jsx("pre", { className: "max-h-40 overflow-auto whitespace-pre-wrap text-xs text-gray-800 dark:text-gray-200", children: displayValue }) : /* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-gray-900 dark:text-white", children: displayValue })
|
|
279
|
+
] });
|
|
280
|
+
}
|
|
281
|
+
function AgentProfileHeader({ agent, models, t, selectedModelId, setSelectedModelId, selectedFramework, temperature, setTemperature, elo, setElo, onChanged }) {
|
|
282
|
+
const modelName = models.find((model) => model.id === selectedModelId)?.name ?? selectedModelId ?? "\u2014";
|
|
283
|
+
const avatarUrl = agent.avatar;
|
|
284
|
+
const frameworkMeta = getFrameworkMeta(selectedFramework);
|
|
285
|
+
const tierInfo = getEloTier(elo);
|
|
286
|
+
const difficultyConfig = {
|
|
287
|
+
beginner: { color: "bg-cyan-100 text-cyan-700 dark:bg-cyan-900/30 dark:text-cyan-400", labelKey: "agentDrawer.tierBeginner" },
|
|
288
|
+
intermediate: { color: "bg-pink-100 text-pink-700 dark:bg-pink-900/30 dark:text-pink-400", labelKey: "agentDrawer.tierIntermediate" },
|
|
289
|
+
advanced: { color: "bg-amber-100 text-amber-700 dark:bg-amber-900/30 dark:text-amber-400", labelKey: "agentDrawer.tierAdvanced" },
|
|
290
|
+
expert: { color: "bg-emerald-100 text-emerald-700 dark:bg-emerald-900/30 dark:text-emerald-400", labelKey: "agentDrawer.tierExpert" }
|
|
291
|
+
};
|
|
292
|
+
const levelConfig = difficultyConfig[tierInfo.tierKey];
|
|
293
|
+
return /* @__PURE__ */ jsxs("div", { className: "relative overflow-hidden bg-gradient-to-br from-indigo-500/20 via-purple-500/10 to-transparent px-6 pb-6 pt-4 dark:from-indigo-500/10 dark:via-purple-500/5", children: [
|
|
294
|
+
levelConfig && /* @__PURE__ */ jsx("div", { className: "mb-4 flex justify-center", children: /* @__PURE__ */ jsx("span", { className: `${levelConfig.color} rounded-full px-4 py-1.5 text-xs font-bold shadow-sm transition-colors`, children: t(levelConfig.labelKey) }) }),
|
|
295
|
+
/* @__PURE__ */ jsx("div", { className: "mb-4 flex justify-center", children: /* @__PURE__ */ jsxs("div", { className: "relative", children: [
|
|
296
|
+
/* @__PURE__ */ jsx("div", { className: "absolute inset-0 rounded-full bg-gradient-to-br from-purple-500 to-blue-500 opacity-40 blur-lg" }),
|
|
297
|
+
avatarUrl ? /* @__PURE__ */ jsx(
|
|
298
|
+
"img",
|
|
299
|
+
{
|
|
300
|
+
src: avatarUrl,
|
|
301
|
+
alt: agent.name,
|
|
302
|
+
loading: "lazy",
|
|
303
|
+
className: "relative h-20 w-20 rounded-full bg-white object-cover shadow-xl ring-4 ring-white dark:bg-gray-800 dark:ring-gray-800"
|
|
304
|
+
}
|
|
305
|
+
) : /* @__PURE__ */ jsx("div", { className: "relative flex h-20 w-20 items-center justify-center rounded-full bg-gradient-to-br from-indigo-400 to-purple-500 shadow-xl ring-4 ring-white dark:ring-gray-800", children: /* @__PURE__ */ jsx(CpuChipIcon, { className: "h-10 w-10 text-white" }) }),
|
|
306
|
+
/* @__PURE__ */ jsx("div", { className: "absolute -bottom-1 -right-1 flex h-6 w-6 items-center justify-center rounded-full bg-indigo-500 ring-2 ring-white dark:ring-gray-900", children: /* @__PURE__ */ jsx(CpuChipIcon, { className: "h-3.5 w-3.5 text-white" }) })
|
|
307
|
+
] }) }),
|
|
308
|
+
/* @__PURE__ */ jsx("h3", { className: "mb-2 text-center text-lg font-bold text-gray-900 dark:text-white", children: agent.name }),
|
|
309
|
+
agent.role && /* @__PURE__ */ jsx("div", { className: "mb-3 flex justify-center", children: /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1.5 rounded-full bg-white/60 px-3 py-1.5 text-xs font-medium text-gray-600 ring-1 ring-gray-200/50 dark:bg-gray-800/60 dark:text-gray-400 dark:ring-gray-700/50", children: [
|
|
310
|
+
/* @__PURE__ */ jsxs("svg", { className: "h-3 w-3", fill: "currentColor", viewBox: "0 0 20 20", children: [
|
|
311
|
+
/* @__PURE__ */ jsx("path", { fillRule: "evenodd", d: "M6 6V5a3 3 0 013-3h2a3 3 0 013 3v1h2a2 2 0 012 2v3.57A22.952 22.952 0 0110 13a22.95 22.95 0 01-8-1.43V8a2 2 0 012-2h2zm2-1a1 1 0 011-1h2a1 1 0 011 1v1H8V5zm1 5a1 1 0 011-1h.01a1 1 0 110 2H10a1 1 0 01-1-1z", clipRule: "evenodd" }),
|
|
312
|
+
/* @__PURE__ */ jsx("path", { d: "M2 13.692V16a2 2 0 002 2h12a2 2 0 002-2v-2.308A24.974 24.974 0 0110 15c-2.796 0-5.487-.46-8-1.308z" })
|
|
313
|
+
] }),
|
|
314
|
+
agent.role
|
|
315
|
+
] }) }),
|
|
316
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-center gap-2", children: [
|
|
317
|
+
/* @__PURE__ */ jsxs("div", { className: "inline-flex items-center gap-1.5 rounded bg-white/50 px-2.5 py-1 text-[10px] text-gray-600 dark:bg-gray-800/50 dark:text-gray-400", children: [
|
|
318
|
+
/* @__PURE__ */ jsx(CpuChipIcon, { className: "h-3 w-3" }),
|
|
319
|
+
/* @__PURE__ */ jsx("span", { className: "font-semibold", children: modelName })
|
|
320
|
+
] }),
|
|
321
|
+
elo != null && /* @__PURE__ */ jsxs("div", { className: "inline-flex items-center gap-1 rounded bg-white/50 px-2 py-1 text-[10px] text-gray-600 dark:bg-gray-800/50 dark:text-gray-400", children: [
|
|
322
|
+
/* @__PURE__ */ jsx("span", { className: "opacity-60", children: "ELO:" }),
|
|
323
|
+
/* @__PURE__ */ jsx("span", { className: "font-bold tabular-nums", children: elo })
|
|
324
|
+
] })
|
|
325
|
+
] }),
|
|
326
|
+
/* @__PURE__ */ jsx("div", { className: "mt-3 flex justify-center", children: /* @__PURE__ */ jsxs("span", { className: `inline-flex items-center gap-1.5 rounded-full px-3 py-1 text-[10px] font-semibold ${frameworkMeta.badgeColor}`, children: [
|
|
327
|
+
/* @__PURE__ */ jsx(frameworkMeta.IconComponent, { className: "h-3.5 w-3.5" }),
|
|
328
|
+
frameworkMeta.label
|
|
329
|
+
] }) }),
|
|
330
|
+
/* @__PURE__ */ jsx(AgentCapabilityCard, { elo, setElo, models, selectedModelId, setSelectedModelId, temperature, setTemperature, onChanged, t }),
|
|
331
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-2 flex items-center justify-center gap-2 text-[10px] text-gray-400 dark:text-gray-500", children: [
|
|
332
|
+
/* @__PURE__ */ jsxs("span", { children: [
|
|
333
|
+
"v",
|
|
334
|
+
agent.activePromptVersion
|
|
335
|
+
] }),
|
|
336
|
+
/* @__PURE__ */ jsx("span", { children: "\xB7" }),
|
|
337
|
+
/* @__PURE__ */ jsxs("span", { children: [
|
|
338
|
+
agent.promptCount,
|
|
339
|
+
" prompt",
|
|
340
|
+
agent.promptCount === 1 ? "" : "s"
|
|
341
|
+
] })
|
|
342
|
+
] })
|
|
343
|
+
] });
|
|
344
|
+
}
|
|
345
|
+
var MODEL_FAMILIES = {
|
|
346
|
+
anthropic: [
|
|
347
|
+
{ prefix: "anthropic.claude-haiku", maxTokens: 8192, contextWindow: "200K" },
|
|
348
|
+
{ prefix: "anthropic.claude-sonnet", maxTokens: 8192, contextWindow: "1M" },
|
|
349
|
+
{ prefix: "anthropic.claude-opus", maxTokens: 32768, contextWindow: "1M" }
|
|
350
|
+
],
|
|
351
|
+
amazon: [
|
|
352
|
+
{ prefix: "amazon.nova-lite", maxTokens: 5120, contextWindow: "300K" },
|
|
353
|
+
{ prefix: "amazon.nova-pro", maxTokens: 5120, contextWindow: "300K" }
|
|
354
|
+
],
|
|
355
|
+
google: [
|
|
356
|
+
{ prefix: "google.gemini-3-flash", maxTokens: 8192, contextWindow: "1M" },
|
|
357
|
+
{ prefix: "google.gemini-3-1-pro", maxTokens: 32768, contextWindow: "1M" }
|
|
358
|
+
],
|
|
359
|
+
meta: [
|
|
360
|
+
{ prefix: "meta.llama", maxTokens: 8192, contextWindow: "128K" }
|
|
361
|
+
],
|
|
362
|
+
openai: [
|
|
363
|
+
{ prefix: "openai.gpt-5-mini", maxTokens: 16384, contextWindow: "1M" },
|
|
364
|
+
{ prefix: "openai.gpt-5-v", maxTokens: 32768, contextWindow: "1M" }
|
|
365
|
+
]
|
|
366
|
+
};
|
|
367
|
+
function getProviderFromModelId(modelId) {
|
|
368
|
+
if (modelId.startsWith("anthropic.")) return "anthropic";
|
|
369
|
+
if (modelId.startsWith("amazon.")) return "amazon";
|
|
370
|
+
if (modelId.startsWith("meta.")) return "meta";
|
|
371
|
+
return "anthropic";
|
|
372
|
+
}
|
|
373
|
+
function getEloTier(elo) {
|
|
374
|
+
if (elo < 1200) return {
|
|
375
|
+
tierKey: "beginner",
|
|
376
|
+
autonomyKey: "autonomyLow",
|
|
377
|
+
color: "text-cyan-500",
|
|
378
|
+
barColor: "from-cyan-400 to-cyan-500",
|
|
379
|
+
autonomyPercent: 25,
|
|
380
|
+
costPerTask: "$0.005",
|
|
381
|
+
familyIndex: 0,
|
|
382
|
+
temperature: 0.7
|
|
383
|
+
};
|
|
384
|
+
if (elo < 1600) return {
|
|
385
|
+
tierKey: "intermediate",
|
|
386
|
+
autonomyKey: "autonomyMedium",
|
|
387
|
+
color: "text-pink-500",
|
|
388
|
+
barColor: "from-pink-400 to-pink-500",
|
|
389
|
+
autonomyPercent: 50,
|
|
390
|
+
costPerTask: "$0.15",
|
|
391
|
+
familyIndex: 1,
|
|
392
|
+
temperature: 0.5
|
|
393
|
+
};
|
|
394
|
+
if (elo < 2e3) return {
|
|
395
|
+
tierKey: "advanced",
|
|
396
|
+
autonomyKey: "autonomyHigh",
|
|
397
|
+
color: "text-amber-500",
|
|
398
|
+
barColor: "from-amber-400 to-amber-500",
|
|
399
|
+
autonomyPercent: 75,
|
|
400
|
+
costPerTask: "$0.50",
|
|
401
|
+
familyIndex: 1,
|
|
402
|
+
temperature: 0.4
|
|
403
|
+
};
|
|
404
|
+
return {
|
|
405
|
+
tierKey: "expert",
|
|
406
|
+
autonomyKey: "autonomyFull",
|
|
407
|
+
color: "text-emerald-500",
|
|
408
|
+
barColor: "from-emerald-400 to-emerald-500",
|
|
409
|
+
autonomyPercent: 100,
|
|
410
|
+
costPerTask: "$1.50",
|
|
411
|
+
familyIndex: 2,
|
|
412
|
+
temperature: 0.3
|
|
413
|
+
};
|
|
414
|
+
}
|
|
415
|
+
function AgentCapabilityCard({ elo, setElo, models, selectedModelId, setSelectedModelId, temperature, setTemperature, onChanged, t }) {
|
|
416
|
+
const tierInfo = getEloTier(elo);
|
|
417
|
+
const eloPercent = Math.min((elo - 800) / (2400 - 800) * 100, 100);
|
|
418
|
+
const provider = getProviderFromModelId(selectedModelId);
|
|
419
|
+
const family = MODEL_FAMILIES[provider] ?? MODEL_FAMILIES.anthropic;
|
|
420
|
+
const handleEloChange = useCallback((event) => {
|
|
421
|
+
const newElo = parseInt(event.target.value, 10);
|
|
422
|
+
setElo(newElo);
|
|
423
|
+
const newTier = getEloTier(newElo);
|
|
424
|
+
const modelIndex = Math.min(newTier.familyIndex, family.length - 1);
|
|
425
|
+
const targetPrefix = family[modelIndex].prefix;
|
|
426
|
+
const matchingModel = models.find((model) => model.id.startsWith(targetPrefix));
|
|
427
|
+
if (matchingModel) {
|
|
428
|
+
setSelectedModelId(matchingModel.id);
|
|
429
|
+
}
|
|
430
|
+
setTemperature(newTier.temperature);
|
|
431
|
+
onChanged();
|
|
432
|
+
}, [family, models, setSelectedModelId, setTemperature, onChanged]);
|
|
433
|
+
const handleAutonomyClick = useCallback((level) => {
|
|
434
|
+
const eloMap = { 25: 1e3, 50: 1400, 75: 1800, 100: 2200 };
|
|
435
|
+
const newElo = eloMap[level] ?? 1e3;
|
|
436
|
+
setElo(newElo);
|
|
437
|
+
const newTier = getEloTier(newElo);
|
|
438
|
+
const modelIndex = Math.min(newTier.familyIndex, family.length - 1);
|
|
439
|
+
const targetPrefix = family[modelIndex].prefix;
|
|
440
|
+
const matchingModel = models.find((model) => model.id.startsWith(targetPrefix));
|
|
441
|
+
if (matchingModel) {
|
|
442
|
+
setSelectedModelId(matchingModel.id);
|
|
443
|
+
}
|
|
444
|
+
setTemperature(newTier.temperature);
|
|
445
|
+
onChanged();
|
|
446
|
+
}, [family, models, setSelectedModelId, setTemperature, onChanged]);
|
|
447
|
+
const currentFamilyEntry = family.find((entry) => selectedModelId.startsWith(entry.prefix)) ?? family[0];
|
|
448
|
+
const currentModelName = models.find((model) => model.id === selectedModelId)?.name ?? selectedModelId;
|
|
449
|
+
return /* @__PURE__ */ jsxs("div", { className: "mx-4 mt-4 overflow-hidden rounded-xl border border-gray-200/30 bg-white/40 dark:border-white/10 dark:bg-white/5", children: [
|
|
450
|
+
/* @__PURE__ */ jsxs("div", { className: "px-4 pt-3 pb-2", children: [
|
|
451
|
+
/* @__PURE__ */ jsxs("div", { className: "mb-1 flex items-center justify-between", children: [
|
|
452
|
+
/* @__PURE__ */ jsx("span", { className: "text-[10px] font-semibold uppercase tracking-wider text-gray-500 dark:text-gray-400", children: t("agentDrawer.capabilityRating") }),
|
|
453
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-baseline gap-1", children: [
|
|
454
|
+
/* @__PURE__ */ jsx("span", { className: `text-lg font-bold tabular-nums ${tierInfo.color}`, children: elo }),
|
|
455
|
+
/* @__PURE__ */ jsx("span", { className: "text-[10px] text-gray-400 dark:text-gray-500", children: "ELO" })
|
|
456
|
+
] })
|
|
457
|
+
] }),
|
|
458
|
+
/* @__PURE__ */ jsxs("div", { className: "relative", children: [
|
|
459
|
+
/* @__PURE__ */ jsxs("div", { className: "absolute inset-x-0 top-1/2 h-2 -translate-y-1/2 overflow-hidden rounded-full bg-gray-200 dark:bg-gray-700", children: [
|
|
460
|
+
/* @__PURE__ */ jsx(
|
|
461
|
+
"div",
|
|
462
|
+
{
|
|
463
|
+
className: `h-full rounded-full bg-gradient-to-r ${tierInfo.barColor} transition-all duration-300`,
|
|
464
|
+
style: { width: `${eloPercent}%` }
|
|
465
|
+
}
|
|
466
|
+
),
|
|
467
|
+
[1200, 1600, 2e3].map((boundary) => /* @__PURE__ */ jsx(
|
|
468
|
+
"div",
|
|
469
|
+
{
|
|
470
|
+
className: "absolute top-0 h-full w-px bg-gray-400/40 dark:bg-gray-500/40",
|
|
471
|
+
style: { left: `${(boundary - 800) / (2400 - 800) * 100}%` }
|
|
472
|
+
},
|
|
473
|
+
boundary
|
|
474
|
+
))
|
|
475
|
+
] }),
|
|
476
|
+
/* @__PURE__ */ jsx(
|
|
477
|
+
"input",
|
|
478
|
+
{
|
|
479
|
+
type: "range",
|
|
480
|
+
min: "800",
|
|
481
|
+
max: "2400",
|
|
482
|
+
step: "50",
|
|
483
|
+
value: elo,
|
|
484
|
+
onChange: handleEloChange,
|
|
485
|
+
"aria-label": t("agentDrawer.capabilityRating"),
|
|
486
|
+
"aria-valuemin": 800,
|
|
487
|
+
"aria-valuemax": 2400,
|
|
488
|
+
"aria-valuenow": elo,
|
|
489
|
+
"aria-valuetext": `${elo} ELO \u2014 ${t(`agentDrawer.tier${tierInfo.tierKey.charAt(0).toUpperCase()}${tierInfo.tierKey.slice(1)}`)}`,
|
|
490
|
+
className: "relative z-10 h-5 w-full cursor-pointer appearance-none bg-transparent focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2 [&::-webkit-slider-thumb]:h-4 [&::-webkit-slider-thumb]:w-4 [&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:rounded-full [&::-webkit-slider-thumb]:border-2 [&::-webkit-slider-thumb]:border-white [&::-webkit-slider-thumb]:shadow-lg dark:[&::-webkit-slider-thumb]:border-gray-900"
|
|
491
|
+
}
|
|
492
|
+
),
|
|
493
|
+
/* @__PURE__ */ jsx("style", { children: `input[type="range"]::-webkit-slider-thumb { background: ${elo < 1200 ? "#06b6d4" : elo < 1600 ? "#ec4899" : elo < 2e3 ? "#f59e0b" : "#10b981"}; box-shadow: 0 0 6px ${elo < 1200 ? "rgba(6,182,212,0.5)" : elo < 1600 ? "rgba(236,72,153,0.5)" : elo < 2e3 ? "rgba(245,158,11,0.5)" : "rgba(16,185,129,0.5)"}; }` })
|
|
494
|
+
] }),
|
|
495
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-0.5 flex justify-between text-[8px] text-gray-400 dark:text-gray-500", children: [
|
|
496
|
+
/* @__PURE__ */ jsx("span", { children: "800" }),
|
|
497
|
+
/* @__PURE__ */ jsx("span", { children: "1200" }),
|
|
498
|
+
/* @__PURE__ */ jsx("span", { children: "1600" }),
|
|
499
|
+
/* @__PURE__ */ jsx("span", { children: "2000" }),
|
|
500
|
+
/* @__PURE__ */ jsx("span", { children: "2400" })
|
|
501
|
+
] })
|
|
502
|
+
] }),
|
|
503
|
+
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-3 gap-px border-t border-gray-200/30 bg-gray-200/30 dark:border-white/10 dark:bg-white/10", children: [
|
|
504
|
+
/* @__PURE__ */ jsxs("div", { className: "bg-white/60 px-3 py-2 dark:bg-gray-900/60", children: [
|
|
505
|
+
/* @__PURE__ */ jsx("p", { className: "text-[9px] text-gray-400 dark:text-gray-500", children: t("agentDrawer.model") }),
|
|
506
|
+
/* @__PURE__ */ jsx("p", { className: "truncate text-xs font-semibold text-gray-900 dark:text-white", children: currentModelName })
|
|
507
|
+
] }),
|
|
508
|
+
/* @__PURE__ */ jsxs("div", { className: "bg-white/60 px-3 py-2 dark:bg-gray-900/60", children: [
|
|
509
|
+
/* @__PURE__ */ jsx("p", { className: "text-[9px] text-gray-400 dark:text-gray-500", children: t("agentDrawer.maxTokens") }),
|
|
510
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs font-semibold text-gray-900 dark:text-white", children: currentFamilyEntry.maxTokens.toLocaleString() })
|
|
511
|
+
] }),
|
|
512
|
+
/* @__PURE__ */ jsxs("div", { className: "bg-white/60 px-3 py-2 dark:bg-gray-900/60", children: [
|
|
513
|
+
/* @__PURE__ */ jsx("p", { className: "text-[9px] text-gray-400 dark:text-gray-500", children: t("agentDrawer.context") }),
|
|
514
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs font-semibold text-gray-900 dark:text-white", children: currentFamilyEntry.contextWindow })
|
|
515
|
+
] }),
|
|
516
|
+
/* @__PURE__ */ jsxs("div", { className: "bg-white/60 px-3 py-2 dark:bg-gray-900/60", children: [
|
|
517
|
+
/* @__PURE__ */ jsx("p", { className: "text-[9px] text-gray-400 dark:text-gray-500", children: t("agentDrawer.temperature") }),
|
|
518
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs font-semibold text-gray-900 dark:text-white", children: temperature.toFixed(2) })
|
|
519
|
+
] }),
|
|
520
|
+
/* @__PURE__ */ jsxs("div", { className: "bg-white/60 px-3 py-2 dark:bg-gray-900/60", children: [
|
|
521
|
+
/* @__PURE__ */ jsx("p", { className: "text-[9px] text-gray-400 dark:text-gray-500", children: t("agentDrawer.autonomy") }),
|
|
522
|
+
/* @__PURE__ */ jsx("p", { className: `text-xs font-semibold ${tierInfo.color}`, children: t(`agentDrawer.${tierInfo.autonomyKey}`) })
|
|
523
|
+
] }),
|
|
524
|
+
/* @__PURE__ */ jsxs("div", { className: "bg-white/60 px-3 py-2 dark:bg-gray-900/60", children: [
|
|
525
|
+
/* @__PURE__ */ jsx("p", { className: "text-[9px] text-gray-400 dark:text-gray-500", children: t("agentDrawer.costPerTask") }),
|
|
526
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs font-semibold text-gray-900 dark:text-white", children: tierInfo.costPerTask })
|
|
527
|
+
] })
|
|
528
|
+
] }),
|
|
529
|
+
/* @__PURE__ */ jsxs("div", { className: "border-t border-gray-200/30 bg-white/60 px-4 py-2.5 dark:border-white/10 dark:bg-gray-900/60", children: [
|
|
530
|
+
/* @__PURE__ */ jsxs("div", { className: "mb-1 flex items-center justify-between", children: [
|
|
531
|
+
/* @__PURE__ */ jsx("span", { className: "text-[9px] text-gray-400 dark:text-gray-500", children: t("agentDrawer.autonomyLevel") }),
|
|
532
|
+
/* @__PURE__ */ jsx("span", { className: `text-[10px] font-semibold ${tierInfo.color}`, children: t(`agentDrawer.${tierInfo.autonomyKey}`) })
|
|
533
|
+
] }),
|
|
534
|
+
/* @__PURE__ */ jsx("div", { className: "flex gap-1", children: [
|
|
535
|
+
{ level: 25, labelKey: "autonomyLow" },
|
|
536
|
+
{ level: 50, labelKey: "autonomyMedium" },
|
|
537
|
+
{ level: 75, labelKey: "autonomyHigh" },
|
|
538
|
+
{ level: 100, labelKey: "autonomyFull" }
|
|
539
|
+
].map(({ level, labelKey }) => /* @__PURE__ */ jsx(
|
|
540
|
+
"button",
|
|
541
|
+
{
|
|
542
|
+
type: "button",
|
|
543
|
+
onClick: () => handleAutonomyClick(level),
|
|
544
|
+
"aria-label": t(`agentDrawer.${labelKey}`),
|
|
545
|
+
"aria-pressed": level <= tierInfo.autonomyPercent,
|
|
546
|
+
className: `h-2 flex-1 rounded-full transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 ${level <= tierInfo.autonomyPercent ? `bg-gradient-to-r ${tierInfo.barColor} hover:opacity-80` : "bg-gray-200 hover:bg-gray-300 dark:bg-gray-700 dark:hover:bg-gray-600"}`
|
|
547
|
+
},
|
|
548
|
+
level
|
|
549
|
+
)) }),
|
|
550
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-1 flex justify-between text-[7px] text-gray-400 dark:text-gray-500", children: [
|
|
551
|
+
/* @__PURE__ */ jsx("span", { children: t("agentDrawer.autonomyLow") }),
|
|
552
|
+
/* @__PURE__ */ jsx("span", { children: t("agentDrawer.autonomyMedium") }),
|
|
553
|
+
/* @__PURE__ */ jsx("span", { children: t("agentDrawer.autonomyHigh") }),
|
|
554
|
+
/* @__PURE__ */ jsx("span", { children: t("agentDrawer.autonomyFull") })
|
|
555
|
+
] })
|
|
556
|
+
] })
|
|
557
|
+
] });
|
|
558
|
+
}
|
|
559
|
+
function ConfigTab({ models, t, selectedModelId, setSelectedModelId, selectedFramework, setSelectedFramework, markDirty, connectedProviderTypes }) {
|
|
560
|
+
const frameworkKeys = Object.keys(FRAMEWORK_META);
|
|
561
|
+
const hasProviderConstraints = connectedProviderTypes.length > 0;
|
|
562
|
+
const compatibleModels = getCompatibleModels(models, selectedFramework);
|
|
563
|
+
const handleFrameworkChange = useCallback((newFramework) => {
|
|
564
|
+
setSelectedFramework(newFramework);
|
|
565
|
+
if (!isModelCompatibleWithFramework(selectedModelId, newFramework)) {
|
|
566
|
+
const compatible = getCompatibleModels(models, newFramework);
|
|
567
|
+
if (compatible.length > 0) {
|
|
568
|
+
setSelectedModelId(compatible[0].id);
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
markDirty();
|
|
572
|
+
}, [selectedModelId, models, setSelectedFramework, setSelectedModelId, markDirty]);
|
|
573
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-5 p-4", children: [
|
|
574
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
575
|
+
/* @__PURE__ */ jsx("label", { className: "mb-2 block text-xs font-medium text-gray-500 dark:text-gray-400", children: t("agentDrawer.framework") }),
|
|
576
|
+
/* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1.5", children: frameworkKeys.map((key) => {
|
|
577
|
+
const meta = FRAMEWORK_META[key];
|
|
578
|
+
const isSelected = key === selectedFramework;
|
|
579
|
+
const compatCount = getCompatibleModels(models, key).length;
|
|
580
|
+
const isCompatibleWithProviders = !hasProviderConstraints || isFrameworkCompatibleWithProviders(key, connectedProviderTypes);
|
|
581
|
+
return /* @__PURE__ */ jsxs(
|
|
582
|
+
"button",
|
|
583
|
+
{
|
|
584
|
+
type: "button",
|
|
585
|
+
onClick: () => handleFrameworkChange(key),
|
|
586
|
+
disabled: !isCompatibleWithProviders,
|
|
587
|
+
className: `inline-flex items-center gap-1.5 rounded-full px-3 py-1.5 text-[10px] font-semibold transition-all ${!isCompatibleWithProviders ? "cursor-not-allowed bg-gray-100 text-gray-400 opacity-40 dark:bg-white/5 dark:text-gray-500" : isSelected ? `${meta.badgeColor} ring-1 ring-current/20` : "bg-gray-100 text-gray-500 hover:bg-gray-200 dark:bg-white/5 dark:text-gray-400 dark:hover:bg-white/10"}`,
|
|
588
|
+
title: !isCompatibleWithProviders ? t("agentDrawer.frameworkIncompatible") : void 0,
|
|
589
|
+
children: [
|
|
590
|
+
/* @__PURE__ */ jsx(meta.IconComponent, { className: "h-3.5 w-3.5" }),
|
|
591
|
+
meta.label,
|
|
592
|
+
/* @__PURE__ */ jsx("span", { className: "ml-0.5 text-[8px] opacity-60", children: compatCount })
|
|
593
|
+
]
|
|
594
|
+
},
|
|
595
|
+
key
|
|
596
|
+
);
|
|
597
|
+
}) })
|
|
598
|
+
] }),
|
|
599
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
600
|
+
/* @__PURE__ */ jsxs("label", { className: "mb-2 block text-xs font-medium text-gray-500 dark:text-gray-400", children: [
|
|
601
|
+
t("agentDrawer.model"),
|
|
602
|
+
compatibleModels.length < models.length && /* @__PURE__ */ jsxs("span", { className: "ml-1.5 text-[10px] font-normal text-gray-400 dark:text-gray-500", children: [
|
|
603
|
+
"(",
|
|
604
|
+
compatibleModels.length,
|
|
605
|
+
"/",
|
|
606
|
+
models.length,
|
|
607
|
+
")"
|
|
608
|
+
] })
|
|
609
|
+
] }),
|
|
610
|
+
/* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 gap-2", children: models.map((model) => {
|
|
611
|
+
const isSelected = model.id === selectedModelId;
|
|
612
|
+
const isCompatible = isModelCompatibleWithFramework(model.id, selectedFramework);
|
|
613
|
+
const { IconComponent, color, providerLabel } = getModelIcon(model.id);
|
|
614
|
+
return /* @__PURE__ */ jsxs(
|
|
615
|
+
"button",
|
|
616
|
+
{
|
|
617
|
+
type: "button",
|
|
618
|
+
disabled: !isCompatible,
|
|
619
|
+
onClick: () => {
|
|
620
|
+
setSelectedModelId(model.id);
|
|
621
|
+
markDirty();
|
|
622
|
+
},
|
|
623
|
+
className: `flex items-center gap-2.5 rounded-xl border px-3 py-2.5 text-left transition-all ${!isCompatible ? "cursor-not-allowed border-gray-200/30 opacity-35 dark:border-white/5" : isSelected ? "border-indigo-500/50 bg-indigo-50/50 ring-1 ring-indigo-500/30 dark:border-indigo-400/40 dark:bg-indigo-500/10" : "border-gray-200/50 bg-gray-50/50 hover:border-gray-300 dark:border-white/10 dark:bg-white/5 dark:hover:border-white/20"}`,
|
|
624
|
+
children: [
|
|
625
|
+
/* @__PURE__ */ jsx(IconComponent, { className: `h-5 w-5 flex-shrink-0 ${isSelected && isCompatible ? color : "text-gray-400 dark:text-gray-500"}` }),
|
|
626
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
|
|
627
|
+
/* @__PURE__ */ jsx("p", { className: `truncate text-xs font-semibold ${isSelected && isCompatible ? "text-gray-900 dark:text-white" : "text-gray-600 dark:text-gray-300"}`, children: model.name }),
|
|
628
|
+
/* @__PURE__ */ jsx("p", { className: "truncate text-[10px] text-gray-400 dark:text-gray-500", children: providerLabel })
|
|
629
|
+
] })
|
|
630
|
+
]
|
|
631
|
+
},
|
|
632
|
+
model.id
|
|
633
|
+
);
|
|
634
|
+
}) })
|
|
635
|
+
] })
|
|
636
|
+
] });
|
|
637
|
+
}
|
|
638
|
+
function PromptTab({
|
|
639
|
+
agent,
|
|
640
|
+
temperature,
|
|
641
|
+
setTemperature,
|
|
642
|
+
markDirty,
|
|
643
|
+
t,
|
|
644
|
+
promptText,
|
|
645
|
+
setPromptText,
|
|
646
|
+
outputSchema,
|
|
647
|
+
setOutputSchema
|
|
648
|
+
}) {
|
|
649
|
+
const handleTemperatureChange = useCallback((event) => {
|
|
650
|
+
setTemperature(parseFloat(event.target.value));
|
|
651
|
+
markDirty();
|
|
652
|
+
}, []);
|
|
653
|
+
const handlePromptChange = useCallback((event) => {
|
|
654
|
+
setPromptText(event.target.value);
|
|
655
|
+
markDirty();
|
|
656
|
+
}, []);
|
|
657
|
+
const temperaturePercent = temperature * 100;
|
|
658
|
+
const isPrecise = temperature <= 0.3;
|
|
659
|
+
const isBalanced = temperature > 0.3 && temperature <= 0.7;
|
|
660
|
+
const isCreative = temperature > 0.7;
|
|
661
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-5 p-4", children: [
|
|
662
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
663
|
+
/* @__PURE__ */ jsxs("div", { className: "mb-2 flex items-center justify-between", children: [
|
|
664
|
+
/* @__PURE__ */ jsx("label", { className: "text-xs font-medium text-gray-500 dark:text-gray-400", children: t("agentDrawer.temperature") }),
|
|
665
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm font-bold tabular-nums text-gray-900 dark:text-white", children: temperature.toFixed(2) })
|
|
666
|
+
] }),
|
|
667
|
+
/* @__PURE__ */ jsxs("div", { className: "relative", children: [
|
|
668
|
+
/* @__PURE__ */ jsx("div", { className: "absolute inset-x-0 top-1/2 h-2 -translate-y-1/2 overflow-hidden rounded-full bg-gray-200 dark:bg-gray-700", children: /* @__PURE__ */ jsx(
|
|
669
|
+
"div",
|
|
670
|
+
{
|
|
671
|
+
className: "h-full rounded-full transition-all duration-300 ease-out",
|
|
672
|
+
style: { width: `${temperaturePercent}%`, background: "linear-gradient(90deg, #3b82f6, #8b5cf6 50%, #ec4899)" }
|
|
673
|
+
}
|
|
674
|
+
) }),
|
|
675
|
+
/* @__PURE__ */ jsx(
|
|
676
|
+
"input",
|
|
677
|
+
{
|
|
678
|
+
type: "range",
|
|
679
|
+
min: "0",
|
|
680
|
+
max: "1",
|
|
681
|
+
step: "0.05",
|
|
682
|
+
value: temperature,
|
|
683
|
+
onChange: handleTemperatureChange,
|
|
684
|
+
"aria-label": t("agentDrawer.temperature"),
|
|
685
|
+
"aria-valuenow": temperature,
|
|
686
|
+
"aria-valuetext": `${temperature.toFixed(2)} \u2014 ${isPrecise ? t("agentDrawer.precise") : isBalanced ? t("agentDrawer.balanced") : t("agentDrawer.creative")}`,
|
|
687
|
+
className: "relative z-10 h-5 w-full cursor-pointer appearance-none bg-transparent focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2 [&::-webkit-slider-thumb]:h-5 [&::-webkit-slider-thumb]:w-5 [&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:rounded-full [&::-webkit-slider-thumb]:border-2 [&::-webkit-slider-thumb]:border-white [&::-webkit-slider-thumb]:shadow-lg dark:[&::-webkit-slider-thumb]:border-gray-900"
|
|
688
|
+
}
|
|
689
|
+
),
|
|
690
|
+
/* @__PURE__ */ jsx("style", { children: `input[type="range"]::-webkit-slider-thumb { background: ${isPrecise ? "#3b82f6" : isBalanced ? "#8b5cf6" : "#ec4899"}; box-shadow: 0 0 8px ${isPrecise ? "rgba(59,130,246,0.5)" : isBalanced ? "rgba(139,92,246,0.5)" : "rgba(236,72,153,0.5)"}; }` })
|
|
691
|
+
] }),
|
|
692
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-1 flex items-center justify-between", children: [
|
|
693
|
+
/* @__PURE__ */ jsx("span", { className: `text-[10px] font-medium transition-colors ${isPrecise ? "text-blue-500" : "text-gray-400 dark:text-gray-500"}`, children: t("agentDrawer.precise") }),
|
|
694
|
+
/* @__PURE__ */ jsx("span", { className: `text-[10px] font-medium transition-colors ${isBalanced ? "text-purple-500" : "text-gray-400 dark:text-gray-500"}`, children: t("agentDrawer.balanced") }),
|
|
695
|
+
/* @__PURE__ */ jsx("span", { className: `text-[10px] font-medium transition-colors ${isCreative ? "text-pink-500" : "text-gray-400 dark:text-gray-500"}`, children: t("agentDrawer.creative") })
|
|
696
|
+
] })
|
|
697
|
+
] }),
|
|
698
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
699
|
+
/* @__PURE__ */ jsxs("div", { className: "mb-1 flex items-center justify-between", children: [
|
|
700
|
+
/* @__PURE__ */ jsx("label", { className: "text-xs font-medium text-gray-500 dark:text-gray-400", children: t("agentDrawer.systemPrompt") }),
|
|
701
|
+
/* @__PURE__ */ jsxs("span", { className: "text-[10px] text-gray-400 dark:text-gray-500", children: [
|
|
702
|
+
promptText.length,
|
|
703
|
+
" chars"
|
|
704
|
+
] })
|
|
705
|
+
] }),
|
|
706
|
+
/* @__PURE__ */ jsx(
|
|
707
|
+
"textarea",
|
|
708
|
+
{
|
|
709
|
+
value: promptText,
|
|
710
|
+
onChange: handlePromptChange,
|
|
711
|
+
rows: 12,
|
|
712
|
+
className: "w-full resize-y rounded-lg border border-gray-200/50 bg-gray-50/50 px-3 py-2.5 font-mono text-xs leading-relaxed text-gray-700 outline-none transition-colors placeholder:text-gray-400 focus:border-indigo-300/50 focus:ring-1 focus:ring-indigo-300/30 dark:border-white/10 dark:bg-white/5 dark:text-gray-300 dark:placeholder:text-gray-500 dark:focus:border-indigo-500/30 dark:focus:ring-indigo-500/20",
|
|
713
|
+
placeholder: t("agentDrawer.systemPromptPlaceholder")
|
|
714
|
+
}
|
|
715
|
+
)
|
|
716
|
+
] }),
|
|
717
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
718
|
+
/* @__PURE__ */ jsx("label", { className: "mb-1 block text-xs font-medium text-gray-500 dark:text-gray-400", children: t("agentDrawer.outputSchema", { _: "Output schema (optional)" }) }),
|
|
719
|
+
/* @__PURE__ */ jsx("p", { className: "mb-2 text-[11px] text-gray-400 dark:text-gray-500", children: t("agentDrawer.outputSchemaHint", {
|
|
720
|
+
_: "Define the structured JSON the model must return. The engine parses + validates and exposes it under `output.parsed`. Invalid output fails the node."
|
|
721
|
+
}) }),
|
|
722
|
+
/* @__PURE__ */ jsx(
|
|
723
|
+
OutputSchemaBuilder,
|
|
724
|
+
{
|
|
725
|
+
value: outputSchema,
|
|
726
|
+
onChange: (next) => {
|
|
727
|
+
setOutputSchema(next);
|
|
728
|
+
markDirty();
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
)
|
|
732
|
+
] })
|
|
733
|
+
] });
|
|
734
|
+
}
|
|
735
|
+
function ResultsTab({ agentId, t }) {
|
|
736
|
+
const nodeResults = useWorkflowStore((state) => state.nodeResults);
|
|
737
|
+
const agentResult = useMemo(() => {
|
|
738
|
+
return nodeResults[agentId] ?? null;
|
|
739
|
+
}, [nodeResults, agentId]);
|
|
740
|
+
if (!agentResult) {
|
|
741
|
+
return /* @__PURE__ */ jsx("div", { className: "flex flex-col items-center justify-center px-4 py-12 text-center", children: /* @__PURE__ */ jsx("p", { className: "text-sm text-gray-500 dark:text-gray-400", children: t("agentDrawer.noResults") }) });
|
|
742
|
+
}
|
|
743
|
+
const outputs = isRecord(agentResult.data) ? agentResult.data : null;
|
|
744
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-4 p-4", children: [
|
|
745
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
|
|
746
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
747
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-gray-500 dark:text-gray-400", children: t("agentDrawer.status") }),
|
|
748
|
+
/* @__PURE__ */ jsx(StatusBadge, { status: agentResult.status })
|
|
749
|
+
] }),
|
|
750
|
+
agentResult.durationMs != null && /* @__PURE__ */ jsxs("span", { className: "text-xs text-gray-500 dark:text-gray-400", children: [
|
|
751
|
+
t("agentDrawer.duration"),
|
|
752
|
+
": ",
|
|
753
|
+
formatDuration(agentResult.durationMs)
|
|
754
|
+
] })
|
|
755
|
+
] }),
|
|
756
|
+
agentResult.error && /* @__PURE__ */ jsx("div", { className: "rounded-xl border border-red-200 bg-red-50 p-3 dark:border-red-900/30 dark:bg-red-900/10", children: /* @__PURE__ */ jsx("p", { className: "text-xs font-medium text-red-800 dark:text-red-300", children: agentResult.error }) }),
|
|
757
|
+
outputs && Object.keys(outputs).length > 0 && /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
|
|
758
|
+
/* @__PURE__ */ jsx("h4", { className: "text-xs font-semibold uppercase tracking-wider text-gray-500 dark:text-gray-400", children: t("agentDrawer.outputVariables") }),
|
|
759
|
+
Object.entries(outputs).map(([key, value]) => /* @__PURE__ */ jsx(OutputCard, { label: key, value }, key))
|
|
760
|
+
] })
|
|
761
|
+
] });
|
|
762
|
+
}
|
|
763
|
+
var SI = (slug, color) => `https://cdn.simpleicons.org/${slug}/${color}`;
|
|
764
|
+
var PROVIDER_LOGOS = {
|
|
765
|
+
anthropic: SI("anthropic", "D4A27F"),
|
|
766
|
+
amazon: "https://www.svgrepo.com/show/448266/aws.svg",
|
|
767
|
+
google: SI("googlegemini", "8E75B2"),
|
|
768
|
+
openai: "https://www.svgrepo.com/show/306500/openai.svg",
|
|
769
|
+
meta: SI("meta", "0668E1"),
|
|
770
|
+
aws_bedrock: "https://www.svgrepo.com/show/448266/aws.svg",
|
|
771
|
+
openai_api: "https://www.svgrepo.com/show/306500/openai.svg",
|
|
772
|
+
google_vertex: SI("googlegemini", "8E75B2"),
|
|
773
|
+
azure_openai: SI("microsoftazure", "0078D4"),
|
|
774
|
+
anthropic_api: SI("anthropic", "D4A27F")
|
|
775
|
+
};
|
|
776
|
+
function ModelsTab({ modelProviders, selectedProviderId, onSelectProvider, models, selectedModelId, onSelectModel, agentFramework, t }) {
|
|
777
|
+
if (modelProviders.length === 0) {
|
|
778
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center px-4 py-12 text-center", children: [
|
|
779
|
+
/* @__PURE__ */ jsx(KeyIcon, { className: "mb-2 h-8 w-8 text-gray-400 dark:text-gray-500" }),
|
|
780
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm text-gray-500 dark:text-gray-400", children: t("agentDrawer.noProvidersAvailable") }),
|
|
781
|
+
/* @__PURE__ */ jsx("p", { className: "mt-1 text-xs text-gray-400 dark:text-gray-500", children: t("agentDrawer.dragProviderHint") })
|
|
782
|
+
] });
|
|
783
|
+
}
|
|
784
|
+
const modelsByProvider = modelProviders.map((provider) => ({
|
|
785
|
+
provider,
|
|
786
|
+
models: models.filter((m) => {
|
|
787
|
+
const modelProvider = m.id.split(".")[0];
|
|
788
|
+
return provider.provider === modelProvider || provider.provider === "amazon" && modelProvider === "anthropic" || provider.provider === "amazon" && modelProvider === "amazon" || provider.provider === "amazon" && modelProvider === "meta";
|
|
789
|
+
})
|
|
790
|
+
}));
|
|
791
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-4 p-4", children: [
|
|
792
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
793
|
+
/* @__PURE__ */ jsx("label", { className: "mb-2 block text-xs font-medium text-gray-500 dark:text-gray-400", children: t("agentDrawer.selectProvider") }),
|
|
794
|
+
/* @__PURE__ */ jsx("div", { className: "space-y-1.5", children: modelProviders.map((provider) => {
|
|
795
|
+
const isSelected = selectedProviderId === provider.id;
|
|
796
|
+
const logo = PROVIDER_LOGOS[provider.provider];
|
|
797
|
+
return /* @__PURE__ */ jsxs(
|
|
798
|
+
"button",
|
|
799
|
+
{
|
|
800
|
+
type: "button",
|
|
801
|
+
onClick: () => onSelectProvider(provider.id),
|
|
802
|
+
className: `flex w-full items-center gap-3 rounded-xl border px-3 py-2.5 text-left transition-all ${isSelected ? "border-indigo-500/30 bg-indigo-50/50 ring-1 ring-indigo-500/20 dark:border-indigo-400/20 dark:bg-indigo-500/5" : "border-gray-200/50 bg-gray-50/30 hover:border-gray-300 dark:border-white/10 dark:bg-white/[0.02] dark:hover:border-white/20"}`,
|
|
803
|
+
children: [
|
|
804
|
+
logo ? /* @__PURE__ */ jsx("div", { className: "flex h-8 w-8 shrink-0 items-center justify-center rounded-lg bg-white/10 p-1", children: /* @__PURE__ */ jsx("img", { src: logo, alt: "", className: "h-5 w-5 object-contain", loading: "lazy" }) }) : /* @__PURE__ */ jsx("div", { className: "flex h-8 w-8 shrink-0 items-center justify-center rounded-lg bg-gradient-to-br from-slate-500 to-gray-600", children: /* @__PURE__ */ jsx(KeyIcon, { className: "h-4 w-4 text-white" }) }),
|
|
805
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
806
|
+
/* @__PURE__ */ jsx("p", { className: `text-xs font-medium ${isSelected ? "text-gray-900 dark:text-white" : "text-gray-600 dark:text-gray-400"}`, children: provider.name }),
|
|
807
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 text-[10px] text-gray-400 dark:text-gray-500", children: [
|
|
808
|
+
/* @__PURE__ */ jsxs("span", { children: [
|
|
809
|
+
provider.modelCount,
|
|
810
|
+
" models"
|
|
811
|
+
] }),
|
|
812
|
+
provider.configured && /* @__PURE__ */ jsx("span", { className: "h-1.5 w-1.5 rounded-full bg-green-500" })
|
|
813
|
+
] })
|
|
814
|
+
] }),
|
|
815
|
+
/* @__PURE__ */ jsx("div", { className: `flex h-4 w-4 shrink-0 items-center justify-center rounded-full transition-all ${isSelected ? "bg-indigo-500 text-white" : "border border-gray-300 dark:border-gray-600"}`, children: isSelected && /* @__PURE__ */ jsx(CheckIcon, { className: "h-2.5 w-2.5" }) })
|
|
816
|
+
]
|
|
817
|
+
},
|
|
818
|
+
provider.id
|
|
819
|
+
);
|
|
820
|
+
}) })
|
|
821
|
+
] }),
|
|
822
|
+
selectedProviderId && (() => {
|
|
823
|
+
const providerModels = modelsByProvider.find((p) => p.provider.id === selectedProviderId);
|
|
824
|
+
if (!providerModels || providerModels.models.length === 0) return /* @__PURE__ */ jsx("p", { className: "text-xs text-gray-400 dark:text-gray-500", children: t("agentDrawer.noModelsForProvider") });
|
|
825
|
+
const compatibleModels = providerModels.models.filter(
|
|
826
|
+
(m) => isModelCompatibleWithFramework(m.id, agentFramework)
|
|
827
|
+
);
|
|
828
|
+
const incompatibleModels = providerModels.models.filter(
|
|
829
|
+
(m) => !isModelCompatibleWithFramework(m.id, agentFramework)
|
|
830
|
+
);
|
|
831
|
+
return /* @__PURE__ */ jsxs("div", { children: [
|
|
832
|
+
/* @__PURE__ */ jsx("label", { className: "mb-2 block text-xs font-medium text-gray-500 dark:text-gray-400", children: t("agentDrawer.selectModel") }),
|
|
833
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
|
|
834
|
+
compatibleModels.map((model) => {
|
|
835
|
+
const isSelected = selectedModelId === model.id;
|
|
836
|
+
const { IconComponent, color, providerLabel } = getModelIcon(model.id);
|
|
837
|
+
return /* @__PURE__ */ jsxs(
|
|
838
|
+
"button",
|
|
839
|
+
{
|
|
840
|
+
type: "button",
|
|
841
|
+
onClick: () => onSelectModel(model.id),
|
|
842
|
+
className: `flex w-full items-center gap-2.5 rounded-lg border px-3 py-2 text-left transition-all ${isSelected ? "border-indigo-500/50 bg-indigo-50/50 ring-1 ring-indigo-500/30 dark:border-indigo-400/40 dark:bg-indigo-500/10" : "border-gray-200/50 bg-gray-50/50 hover:border-gray-300 dark:border-white/10 dark:bg-white/5 dark:hover:border-white/20"}`,
|
|
843
|
+
children: [
|
|
844
|
+
/* @__PURE__ */ jsx(IconComponent, { className: `h-4 w-4 flex-shrink-0 ${isSelected ? color : "text-gray-400 dark:text-gray-500"}` }),
|
|
845
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
846
|
+
/* @__PURE__ */ jsx("p", { className: `text-xs font-semibold ${isSelected ? "text-gray-900 dark:text-white" : "text-gray-600 dark:text-gray-300"}`, children: model.name }),
|
|
847
|
+
/* @__PURE__ */ jsx("p", { className: "text-[10px] text-gray-400 dark:text-gray-500", children: providerLabel })
|
|
848
|
+
] }),
|
|
849
|
+
isSelected && /* @__PURE__ */ jsx(CheckIcon, { className: "h-3.5 w-3.5 shrink-0 text-indigo-500" })
|
|
850
|
+
]
|
|
851
|
+
},
|
|
852
|
+
model.id
|
|
853
|
+
);
|
|
854
|
+
}),
|
|
855
|
+
incompatibleModels.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
856
|
+
/* @__PURE__ */ jsx("div", { className: "mt-2 mb-1 text-[9px] font-medium text-gray-400 dark:text-gray-500", children: t("agentDrawer.incompatibleWithFramework") }),
|
|
857
|
+
incompatibleModels.map((model) => /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2.5 rounded-lg border border-gray-200/30 px-3 py-2 opacity-40 dark:border-white/5", children: [
|
|
858
|
+
/* @__PURE__ */ jsx(CpuChipIcon, { className: "h-4 w-4 text-gray-400" }),
|
|
859
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs text-gray-500", children: model.name })
|
|
860
|
+
] }, model.id))
|
|
861
|
+
] })
|
|
862
|
+
] })
|
|
863
|
+
] });
|
|
864
|
+
})()
|
|
865
|
+
] });
|
|
866
|
+
}
|
|
867
|
+
function ToolsTab({ agentTools, enabledToolIds, onToggle, agentFramework, t }) {
|
|
868
|
+
if (agentTools.length === 0) {
|
|
869
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center px-4 py-12 text-center", children: [
|
|
870
|
+
/* @__PURE__ */ jsx(CommandLineIcon, { className: "mb-2 h-8 w-8 text-gray-400 dark:text-gray-500" }),
|
|
871
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm text-gray-500 dark:text-gray-400", children: t("agentDrawer.noToolsAvailable") })
|
|
872
|
+
] });
|
|
873
|
+
}
|
|
874
|
+
const sortedTools = [...agentTools].sort((a, b) => {
|
|
875
|
+
const aCompat = !a.compatibleFrameworks || a.compatibleFrameworks.length === 0 || a.compatibleFrameworks.includes(agentFramework);
|
|
876
|
+
const bCompat = !b.compatibleFrameworks || b.compatibleFrameworks.length === 0 || b.compatibleFrameworks.includes(agentFramework);
|
|
877
|
+
if (aCompat && !bCompat) return -1;
|
|
878
|
+
if (!aCompat && bCompat) return 1;
|
|
879
|
+
return 0;
|
|
880
|
+
});
|
|
881
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-2 p-4", children: [
|
|
882
|
+
/* @__PURE__ */ jsx("p", { className: "mb-3 text-xs text-gray-500 dark:text-gray-400", children: t("agentDrawer.toolsDescription") }),
|
|
883
|
+
sortedTools.map((tool) => {
|
|
884
|
+
const isEnabled = enabledToolIds.has(tool.agentToolId);
|
|
885
|
+
const isCompatible = !tool.compatibleFrameworks || tool.compatibleFrameworks.length === 0 || tool.compatibleFrameworks.includes(agentFramework);
|
|
886
|
+
return /* @__PURE__ */ jsxs(
|
|
887
|
+
"button",
|
|
888
|
+
{
|
|
889
|
+
type: "button",
|
|
890
|
+
onClick: () => onToggle(tool.agentToolId),
|
|
891
|
+
className: `flex w-full items-center gap-3 rounded-xl border px-4 py-3 text-left transition-all ${!isCompatible ? "border-gray-200/30 opacity-40 dark:border-white/5" : isEnabled ? "border-amber-500/30 bg-amber-50/50 dark:border-amber-400/20 dark:bg-amber-500/5" : "border-gray-200/50 bg-gray-50/30 hover:border-gray-300 dark:border-white/10 dark:bg-white/[0.02] dark:hover:border-white/20"}`,
|
|
892
|
+
disabled: !isCompatible,
|
|
893
|
+
children: [
|
|
894
|
+
/* @__PURE__ */ jsx("div", { className: `flex h-8 w-8 shrink-0 items-center justify-center rounded-lg ${isEnabled ? "bg-gradient-to-br from-amber-400 to-orange-500" : "bg-gray-200 dark:bg-gray-700"}`, children: /* @__PURE__ */ jsx(CommandLineIcon, { className: `h-4 w-4 ${isEnabled ? "text-white" : "text-gray-500 dark:text-gray-400"}` }) }),
|
|
895
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
896
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5", children: [
|
|
897
|
+
/* @__PURE__ */ jsx("p", { className: `text-sm font-medium ${isEnabled ? "text-gray-900 dark:text-white" : "text-gray-600 dark:text-gray-400"}`, children: tool.name }),
|
|
898
|
+
!isCompatible && /* @__PURE__ */ jsx("span", { className: "rounded-full bg-gray-100 px-1.5 py-0.5 text-[8px] font-medium text-gray-500 dark:bg-white/10 dark:text-gray-400", children: "incompatible" })
|
|
899
|
+
] }),
|
|
900
|
+
tool.description && /* @__PURE__ */ jsx("p", { className: "truncate text-[10px] text-gray-400 dark:text-gray-500", children: tool.description }),
|
|
901
|
+
tool.compatibleFrameworks && tool.compatibleFrameworks.length > 0 && /* @__PURE__ */ jsx("div", { className: "mt-1 flex flex-wrap gap-0.5", children: tool.compatibleFrameworks.map((framework) => {
|
|
902
|
+
const meta = getFrameworkMeta(framework);
|
|
903
|
+
const isCurrentFw = framework === agentFramework;
|
|
904
|
+
return /* @__PURE__ */ jsxs("span", { className: `inline-flex items-center gap-0.5 rounded px-1.5 py-0.5 text-[8px] font-medium ${isCurrentFw ? meta.badgeColor : "bg-gray-100 text-gray-500 dark:bg-white/5 dark:text-gray-400"}`, children: [
|
|
905
|
+
/* @__PURE__ */ jsx(meta.IconComponent, { className: "h-2.5 w-2.5" }),
|
|
906
|
+
meta.label
|
|
907
|
+
] }, framework);
|
|
908
|
+
}) })
|
|
909
|
+
] }),
|
|
910
|
+
/* @__PURE__ */ jsx("div", { className: `flex h-5 w-5 shrink-0 items-center justify-center rounded-full transition-all ${isEnabled ? "bg-amber-500 text-white" : "border border-gray-300 dark:border-gray-600"}`, children: isEnabled && /* @__PURE__ */ jsx(CheckIcon, { className: "h-3 w-3" }) }),
|
|
911
|
+
/* @__PURE__ */ jsx(
|
|
912
|
+
"div",
|
|
913
|
+
{
|
|
914
|
+
className: "shrink-0",
|
|
915
|
+
onClick: (event) => {
|
|
916
|
+
event.stopPropagation();
|
|
917
|
+
},
|
|
918
|
+
children: /* @__PURE__ */ jsx(
|
|
919
|
+
ToggleSwitch,
|
|
920
|
+
{
|
|
921
|
+
checked: isEnabled,
|
|
922
|
+
onChange: () => onToggle(tool.agentToolId),
|
|
923
|
+
disabled: !isCompatible,
|
|
924
|
+
size: "sm",
|
|
925
|
+
color: "indigo",
|
|
926
|
+
label: `${tool.name} enabled`
|
|
927
|
+
}
|
|
928
|
+
)
|
|
929
|
+
}
|
|
930
|
+
)
|
|
931
|
+
]
|
|
932
|
+
},
|
|
933
|
+
tool.agentToolId
|
|
934
|
+
);
|
|
935
|
+
})
|
|
936
|
+
] });
|
|
937
|
+
}
|
|
938
|
+
function AdvancedTab({
|
|
939
|
+
displayName,
|
|
940
|
+
setDisplayName,
|
|
941
|
+
description,
|
|
942
|
+
setDescription,
|
|
943
|
+
maxOutputTokens,
|
|
944
|
+
setMaxOutputTokens,
|
|
945
|
+
topP,
|
|
946
|
+
setTopP,
|
|
947
|
+
topK,
|
|
948
|
+
setTopK,
|
|
949
|
+
tags,
|
|
950
|
+
setTags,
|
|
951
|
+
status,
|
|
952
|
+
setStatus,
|
|
953
|
+
avatarUrl,
|
|
954
|
+
setAvatarUrl,
|
|
955
|
+
markDirty,
|
|
956
|
+
t
|
|
957
|
+
}) {
|
|
958
|
+
const wrap = "w-full rounded-lg border border-gray-200/50 bg-gray-50/50 px-3 py-2 text-sm text-gray-800 outline-none transition-colors focus:border-indigo-300/50 focus:ring-1 focus:ring-indigo-300/30 dark:border-white/10 dark:bg-white/5 dark:text-gray-200";
|
|
959
|
+
const labelCls = "mb-1 block text-xs font-medium text-gray-500 dark:text-gray-400";
|
|
960
|
+
const touch = () => markDirty();
|
|
961
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-4 p-4", children: [
|
|
962
|
+
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 gap-3 sm:grid-cols-2", children: [
|
|
963
|
+
/* @__PURE__ */ jsxs("label", { children: [
|
|
964
|
+
/* @__PURE__ */ jsx("span", { className: labelCls, children: t("agentDrawer.displayName", { _: "Display name" }) }),
|
|
965
|
+
/* @__PURE__ */ jsx("input", { className: wrap, value: displayName, onChange: (e) => {
|
|
966
|
+
setDisplayName(e.target.value);
|
|
967
|
+
touch();
|
|
968
|
+
} })
|
|
969
|
+
] }),
|
|
970
|
+
/* @__PURE__ */ jsxs("label", { children: [
|
|
971
|
+
/* @__PURE__ */ jsx("span", { className: labelCls, children: t("agentDrawer.status", { _: "Status" }) }),
|
|
972
|
+
/* @__PURE__ */ jsxs("select", { className: wrap, value: status, onChange: (e) => {
|
|
973
|
+
setStatus(e.target.value);
|
|
974
|
+
touch();
|
|
975
|
+
}, children: [
|
|
976
|
+
/* @__PURE__ */ jsx("option", { value: "draft", children: "draft" }),
|
|
977
|
+
/* @__PURE__ */ jsx("option", { value: "active", children: "active" }),
|
|
978
|
+
/* @__PURE__ */ jsx("option", { value: "archived", children: "archived" })
|
|
979
|
+
] })
|
|
980
|
+
] })
|
|
981
|
+
] }),
|
|
982
|
+
/* @__PURE__ */ jsxs("label", { className: "block", children: [
|
|
983
|
+
/* @__PURE__ */ jsx("span", { className: labelCls, children: t("agentDrawer.description", { _: "Description" }) }),
|
|
984
|
+
/* @__PURE__ */ jsx(
|
|
985
|
+
"textarea",
|
|
986
|
+
{
|
|
987
|
+
className: wrap,
|
|
988
|
+
rows: 3,
|
|
989
|
+
value: description,
|
|
990
|
+
onChange: (e) => {
|
|
991
|
+
setDescription(e.target.value);
|
|
992
|
+
touch();
|
|
993
|
+
},
|
|
994
|
+
placeholder: t("agentDrawer.descriptionPlaceholder", { _: "What this agent does, which tasks it's good at" })
|
|
995
|
+
}
|
|
996
|
+
)
|
|
997
|
+
] }),
|
|
998
|
+
/* @__PURE__ */ jsxs("label", { className: "block", children: [
|
|
999
|
+
/* @__PURE__ */ jsx("span", { className: labelCls, children: t("agentDrawer.avatarUrl", { _: "Avatar URL" }) }),
|
|
1000
|
+
/* @__PURE__ */ jsx("input", { className: wrap, value: avatarUrl, onChange: (e) => {
|
|
1001
|
+
setAvatarUrl(e.target.value);
|
|
1002
|
+
touch();
|
|
1003
|
+
}, placeholder: "https://\u2026" })
|
|
1004
|
+
] }),
|
|
1005
|
+
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 gap-3 sm:grid-cols-3", children: [
|
|
1006
|
+
/* @__PURE__ */ jsxs("label", { children: [
|
|
1007
|
+
/* @__PURE__ */ jsx("span", { className: labelCls, children: t("agentDrawer.maxOutputTokens", { _: "Max output tokens" }) }),
|
|
1008
|
+
/* @__PURE__ */ jsx(
|
|
1009
|
+
"input",
|
|
1010
|
+
{
|
|
1011
|
+
type: "number",
|
|
1012
|
+
min: 1,
|
|
1013
|
+
max: 32768,
|
|
1014
|
+
className: wrap,
|
|
1015
|
+
value: maxOutputTokens,
|
|
1016
|
+
onChange: (e) => {
|
|
1017
|
+
setMaxOutputTokens(Number(e.target.value) || 0);
|
|
1018
|
+
touch();
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1021
|
+
)
|
|
1022
|
+
] }),
|
|
1023
|
+
/* @__PURE__ */ jsxs("label", { children: [
|
|
1024
|
+
/* @__PURE__ */ jsx("span", { className: labelCls, children: t("agentDrawer.topP", { _: "Top-p" }) }),
|
|
1025
|
+
/* @__PURE__ */ jsx(
|
|
1026
|
+
"input",
|
|
1027
|
+
{
|
|
1028
|
+
type: "number",
|
|
1029
|
+
step: 0.05,
|
|
1030
|
+
min: 0,
|
|
1031
|
+
max: 1,
|
|
1032
|
+
className: wrap,
|
|
1033
|
+
value: topP,
|
|
1034
|
+
onChange: (e) => {
|
|
1035
|
+
setTopP(Number(e.target.value) || 0);
|
|
1036
|
+
touch();
|
|
1037
|
+
}
|
|
1038
|
+
}
|
|
1039
|
+
)
|
|
1040
|
+
] }),
|
|
1041
|
+
/* @__PURE__ */ jsxs("label", { children: [
|
|
1042
|
+
/* @__PURE__ */ jsx("span", { className: labelCls, children: t("agentDrawer.topK", { _: "Top-k" }) }),
|
|
1043
|
+
/* @__PURE__ */ jsx(
|
|
1044
|
+
"input",
|
|
1045
|
+
{
|
|
1046
|
+
type: "number",
|
|
1047
|
+
min: 0,
|
|
1048
|
+
max: 500,
|
|
1049
|
+
className: wrap,
|
|
1050
|
+
value: topK,
|
|
1051
|
+
onChange: (e) => {
|
|
1052
|
+
setTopK(Number(e.target.value) || 0);
|
|
1053
|
+
touch();
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
1056
|
+
)
|
|
1057
|
+
] })
|
|
1058
|
+
] }),
|
|
1059
|
+
/* @__PURE__ */ jsxs("label", { className: "block", children: [
|
|
1060
|
+
/* @__PURE__ */ jsx("span", { className: labelCls, children: t("agentDrawer.tags", { _: "Tags (comma-separated)" }) }),
|
|
1061
|
+
/* @__PURE__ */ jsx(
|
|
1062
|
+
"input",
|
|
1063
|
+
{
|
|
1064
|
+
className: wrap,
|
|
1065
|
+
value: tags,
|
|
1066
|
+
onChange: (e) => {
|
|
1067
|
+
setTags(e.target.value);
|
|
1068
|
+
touch();
|
|
1069
|
+
},
|
|
1070
|
+
placeholder: "pricing, research, internal"
|
|
1071
|
+
}
|
|
1072
|
+
)
|
|
1073
|
+
] })
|
|
1074
|
+
] });
|
|
1075
|
+
}
|
|
1076
|
+
function AgentModal({ onSaved, onPersist }) {
|
|
1077
|
+
const t = useTranslations("agents.workflow");
|
|
1078
|
+
const activeModal = useModalStore((s) => s.activeModal);
|
|
1079
|
+
const agentData = useModalStore((s) => s.agentData);
|
|
1080
|
+
const closeModal = useModalStore((s) => s.closeModal);
|
|
1081
|
+
const open = activeModal === "agent";
|
|
1082
|
+
const agent = agentData?.agent ?? null;
|
|
1083
|
+
const models = agentData?.models ?? [];
|
|
1084
|
+
const availableAgentTools = agentData?.agentTools ?? [];
|
|
1085
|
+
const isCreateMode = agentData?.isCreateMode ?? false;
|
|
1086
|
+
const [activeTab, setActiveTab] = useState("profile");
|
|
1087
|
+
const [selectedModelId, setSelectedModelId] = useState("");
|
|
1088
|
+
const [selectedFramework, setSelectedFramework] = useState("custom");
|
|
1089
|
+
const [temperature, setTemperature] = useState(0.7);
|
|
1090
|
+
const [elo, setElo] = useState(1e3);
|
|
1091
|
+
const [saved, setSaved] = useState(true);
|
|
1092
|
+
const [enabledToolIds, setEnabledToolIds] = useState(/* @__PURE__ */ new Set());
|
|
1093
|
+
const [selectedProviderId, setSelectedProviderId] = useState("");
|
|
1094
|
+
const [systemPrompt, setSystemPrompt] = useState("");
|
|
1095
|
+
const [outputSchema, setOutputSchema] = useState(void 0);
|
|
1096
|
+
const [displayName, setDisplayName] = useState("");
|
|
1097
|
+
const [description, setDescription] = useState("");
|
|
1098
|
+
const [avatarUrl, setAvatarUrlState] = useState("");
|
|
1099
|
+
const [maxOutputTokens, setMaxOutputTokens] = useState(1024);
|
|
1100
|
+
const [topP, setTopP] = useState(0.95);
|
|
1101
|
+
const [topK, setTopK] = useState(40);
|
|
1102
|
+
const [tagsText, setTagsText] = useState("");
|
|
1103
|
+
const [status, setStatus] = useState("active");
|
|
1104
|
+
const [persisting, setPersisting] = useState(false);
|
|
1105
|
+
const availableModelProviders = agentData?.modelProviders ?? [];
|
|
1106
|
+
const agentId = agent?.agentId ?? agent?.id ?? "";
|
|
1107
|
+
useEffect(() => {
|
|
1108
|
+
if (!agent) return;
|
|
1109
|
+
setSelectedModelId(agent.modelId ?? models[0]?.id ?? "");
|
|
1110
|
+
setSelectedFramework(String(agent.framework ?? "custom"));
|
|
1111
|
+
setTemperature(agent.temperature ?? 0.7);
|
|
1112
|
+
setElo(Number(agent.elo ?? 1e3));
|
|
1113
|
+
setSaved(!isCreateMode);
|
|
1114
|
+
setActiveTab("profile");
|
|
1115
|
+
const agentToolIds = agent.agentToolIds;
|
|
1116
|
+
setEnabledToolIds(new Set(agentToolIds ?? availableAgentTools.filter((t2) => t2.enabled).map((t2) => t2.agentToolId)));
|
|
1117
|
+
setSelectedProviderId(agent.modelProviderId ?? "");
|
|
1118
|
+
const agentRecord = agent;
|
|
1119
|
+
setSystemPrompt(
|
|
1120
|
+
agentRecord.systemPrompt ?? agentRecord.instruction ?? ""
|
|
1121
|
+
);
|
|
1122
|
+
setOutputSchema(
|
|
1123
|
+
agentRecord.outputSchema ?? void 0
|
|
1124
|
+
);
|
|
1125
|
+
setDisplayName(agentRecord.displayName ?? agent.name ?? "");
|
|
1126
|
+
setDescription(agentRecord.description ?? "");
|
|
1127
|
+
setAvatarUrlState(agentRecord.avatar ?? "");
|
|
1128
|
+
const agentMaxOut = agentRecord.maxOutputTokens ?? agent.maxTokens ?? 1024;
|
|
1129
|
+
setMaxOutputTokens(Number(agentMaxOut) || 1024);
|
|
1130
|
+
setTopP(Number(agentRecord.topP ?? 0.95));
|
|
1131
|
+
setTopK(Number(agentRecord.topK ?? 40));
|
|
1132
|
+
const existingTags = agentRecord.tags;
|
|
1133
|
+
setTagsText(
|
|
1134
|
+
Array.isArray(existingTags) ? existingTags.filter((v) => typeof v === "string").join(", ") : ""
|
|
1135
|
+
);
|
|
1136
|
+
setStatus(agentRecord.status ?? "active");
|
|
1137
|
+
}, [agentId]);
|
|
1138
|
+
const dirty = !saved;
|
|
1139
|
+
const markDirty = useCallback(() => setSaved(false), []);
|
|
1140
|
+
const markSaved = useCallback(() => setSaved(true), []);
|
|
1141
|
+
const handleClose = useCallback(() => {
|
|
1142
|
+
closeModal();
|
|
1143
|
+
}, [closeModal]);
|
|
1144
|
+
const handleSelectProvider = useCallback((providerId) => {
|
|
1145
|
+
setSelectedProviderId(providerId);
|
|
1146
|
+
markDirty();
|
|
1147
|
+
}, [markDirty]);
|
|
1148
|
+
const handleToggleTool = useCallback((toolId) => {
|
|
1149
|
+
setEnabledToolIds((previous) => {
|
|
1150
|
+
const next = new Set(previous);
|
|
1151
|
+
if (next.has(toolId)) next.delete(toolId);
|
|
1152
|
+
else next.add(toolId);
|
|
1153
|
+
return next;
|
|
1154
|
+
});
|
|
1155
|
+
markDirty();
|
|
1156
|
+
}, [markDirty]);
|
|
1157
|
+
const handleMarkSaved = useCallback(async () => {
|
|
1158
|
+
if (!agent) return;
|
|
1159
|
+
const payload = {
|
|
1160
|
+
agentId: agent.agentId,
|
|
1161
|
+
name: agent.name,
|
|
1162
|
+
displayName: displayName.trim() || void 0,
|
|
1163
|
+
description: description.trim() || void 0,
|
|
1164
|
+
avatar: avatarUrl.trim() || void 0,
|
|
1165
|
+
modelId: selectedModelId || void 0,
|
|
1166
|
+
connectionId: selectedProviderId || void 0,
|
|
1167
|
+
framework: selectedFramework,
|
|
1168
|
+
systemPrompt,
|
|
1169
|
+
temperature,
|
|
1170
|
+
maxOutputTokens,
|
|
1171
|
+
topP,
|
|
1172
|
+
topK,
|
|
1173
|
+
elo,
|
|
1174
|
+
status: status || void 0,
|
|
1175
|
+
enabledToolIds: Array.from(enabledToolIds),
|
|
1176
|
+
tags: tagsText.split(",").map((tag) => tag.trim()).filter((tag) => tag.length > 0),
|
|
1177
|
+
// Send `outputSchema` only when the user actually configured one
|
|
1178
|
+
// — sending `undefined` would shadow any existing stored schema
|
|
1179
|
+
// through the spread later, so the parent layer should treat
|
|
1180
|
+
// missing key as "leave alone".
|
|
1181
|
+
...outputSchema ? { outputSchema } : {}
|
|
1182
|
+
};
|
|
1183
|
+
try {
|
|
1184
|
+
setPersisting(true);
|
|
1185
|
+
if (onPersist) await onPersist(payload);
|
|
1186
|
+
markSaved();
|
|
1187
|
+
onSaved?.();
|
|
1188
|
+
} catch (error) {
|
|
1189
|
+
console.error("[AgentModal] onPersist failed", error);
|
|
1190
|
+
} finally {
|
|
1191
|
+
setPersisting(false);
|
|
1192
|
+
}
|
|
1193
|
+
}, [
|
|
1194
|
+
agent,
|
|
1195
|
+
displayName,
|
|
1196
|
+
description,
|
|
1197
|
+
avatarUrl,
|
|
1198
|
+
selectedModelId,
|
|
1199
|
+
selectedProviderId,
|
|
1200
|
+
selectedFramework,
|
|
1201
|
+
systemPrompt,
|
|
1202
|
+
temperature,
|
|
1203
|
+
maxOutputTokens,
|
|
1204
|
+
topP,
|
|
1205
|
+
topK,
|
|
1206
|
+
elo,
|
|
1207
|
+
status,
|
|
1208
|
+
enabledToolIds,
|
|
1209
|
+
tagsText,
|
|
1210
|
+
outputSchema,
|
|
1211
|
+
markSaved,
|
|
1212
|
+
onSaved,
|
|
1213
|
+
onPersist
|
|
1214
|
+
]);
|
|
1215
|
+
if (!agent) return null;
|
|
1216
|
+
const sections = [
|
|
1217
|
+
{ id: "profile", label: t("agentDrawer.profileSection"), icon: UserCircleIcon, group: t("agentDrawer.agentGroup") },
|
|
1218
|
+
{ id: "framework", label: t("agentDrawer.frameworkSection"), icon: Cog6ToothIcon, group: t("agentDrawer.agentGroup") },
|
|
1219
|
+
{ id: "prompt", label: t("agentDrawer.promptSection"), icon: SparklesIcon, group: t("agentDrawer.agentGroup") },
|
|
1220
|
+
{ id: "tools", label: `${t("agentDrawer.toolsTab")}${enabledToolIds.size > 0 ? ` (${enabledToolIds.size})` : ""}`, icon: CommandLineIcon, group: t("agentDrawer.configGroup") },
|
|
1221
|
+
{ id: "models", label: `${t("agentDrawer.modelsTab")}${selectedProviderId ? " \u2713" : ""}`, icon: KeyIcon, group: t("agentDrawer.configGroup") },
|
|
1222
|
+
{ id: "advanced", label: t("agentDrawer.advancedTab", { _: "Advanced" }), icon: Cog6ToothIcon, group: t("agentDrawer.configGroup") },
|
|
1223
|
+
{ id: "results", label: t("agentDrawer.resultsTab"), icon: PlayCircleIcon, group: t("agentDrawer.executionGroup") }
|
|
1224
|
+
];
|
|
1225
|
+
const effectiveAvatarUrl = avatarUrl || agent.avatar;
|
|
1226
|
+
const sidebarFooter = /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 px-4 py-3", children: [
|
|
1227
|
+
/* @__PURE__ */ jsx("div", { role: "status", "aria-live": "polite", className: "flex-1 text-xs", children: dirty ? /* @__PURE__ */ jsx("span", { className: "text-amber-500 dark:text-amber-400", children: t("agentDrawer.unsavedChanges") }) : /* @__PURE__ */ jsx("span", { className: "text-emerald-500 dark:text-emerald-400", children: t("agentDrawer.saved") }) }),
|
|
1228
|
+
/* @__PURE__ */ jsx(
|
|
1229
|
+
"button",
|
|
1230
|
+
{
|
|
1231
|
+
type: "button",
|
|
1232
|
+
onClick: () => {
|
|
1233
|
+
void handleMarkSaved();
|
|
1234
|
+
},
|
|
1235
|
+
disabled: !dirty || persisting,
|
|
1236
|
+
className: `rounded-lg px-3 py-1.5 text-xs font-semibold text-white shadow-sm transition-all ${dirty && !persisting ? "bg-gradient-to-r from-indigo-500 to-purple-500 hover:from-indigo-600 hover:to-purple-600" : "cursor-not-allowed bg-gray-300 dark:bg-gray-700"}`,
|
|
1237
|
+
children: persisting ? "\u2026" : t("agentDrawer.save")
|
|
1238
|
+
}
|
|
1239
|
+
)
|
|
1240
|
+
] });
|
|
1241
|
+
return /* @__PURE__ */ jsxs(
|
|
1242
|
+
GlassModal,
|
|
1243
|
+
{
|
|
1244
|
+
open,
|
|
1245
|
+
onClose: handleClose,
|
|
1246
|
+
title: agent.name,
|
|
1247
|
+
subtitle: isCreateMode ? t("agentDrawer.createAgent") : t("agentDrawer.editAgent"),
|
|
1248
|
+
gradient: "from-indigo-500 to-purple-600",
|
|
1249
|
+
icon: /* @__PURE__ */ jsx(CpuChipIcon, { className: "h-5 w-5 text-white" }),
|
|
1250
|
+
label: isCreateMode ? t("agentDrawer.createAgent") : t("agentDrawer.editAgent"),
|
|
1251
|
+
sidebar: {
|
|
1252
|
+
sections,
|
|
1253
|
+
activeSectionId: activeTab,
|
|
1254
|
+
onSectionChange: (sectionId) => setActiveTab(sectionId),
|
|
1255
|
+
identity: {
|
|
1256
|
+
displayName: displayName || agent.name,
|
|
1257
|
+
profileInitial: (displayName || agent.name).charAt(0).toUpperCase(),
|
|
1258
|
+
avatarUrl: effectiveAvatarUrl,
|
|
1259
|
+
role: agent.role
|
|
1260
|
+
},
|
|
1261
|
+
footer: sidebarFooter
|
|
1262
|
+
},
|
|
1263
|
+
children: [
|
|
1264
|
+
activeTab === "profile" && /* @__PURE__ */ jsx(AgentProfileHeader, { agent, models, t, selectedModelId, setSelectedModelId, selectedFramework, temperature, setTemperature, elo, setElo, onChanged: markDirty }),
|
|
1265
|
+
activeTab === "framework" && /* @__PURE__ */ jsx(ConfigTab, { models, t, selectedModelId, setSelectedModelId, selectedFramework, setSelectedFramework: (fw) => setSelectedFramework(fw), markDirty, connectedProviderTypes: availableModelProviders.map((p) => p.provider) }),
|
|
1266
|
+
activeTab === "prompt" && /* @__PURE__ */ jsx(
|
|
1267
|
+
PromptTab,
|
|
1268
|
+
{
|
|
1269
|
+
agent,
|
|
1270
|
+
temperature,
|
|
1271
|
+
setTemperature,
|
|
1272
|
+
markDirty,
|
|
1273
|
+
t,
|
|
1274
|
+
promptText: systemPrompt,
|
|
1275
|
+
setPromptText: setSystemPrompt,
|
|
1276
|
+
outputSchema,
|
|
1277
|
+
setOutputSchema
|
|
1278
|
+
}
|
|
1279
|
+
),
|
|
1280
|
+
activeTab === "advanced" && /* @__PURE__ */ jsx(
|
|
1281
|
+
AdvancedTab,
|
|
1282
|
+
{
|
|
1283
|
+
displayName,
|
|
1284
|
+
setDisplayName,
|
|
1285
|
+
description,
|
|
1286
|
+
setDescription,
|
|
1287
|
+
maxOutputTokens,
|
|
1288
|
+
setMaxOutputTokens,
|
|
1289
|
+
topP,
|
|
1290
|
+
setTopP,
|
|
1291
|
+
topK,
|
|
1292
|
+
setTopK,
|
|
1293
|
+
tags: tagsText,
|
|
1294
|
+
setTags: setTagsText,
|
|
1295
|
+
status,
|
|
1296
|
+
setStatus,
|
|
1297
|
+
avatarUrl,
|
|
1298
|
+
setAvatarUrl: setAvatarUrlState,
|
|
1299
|
+
markDirty,
|
|
1300
|
+
t
|
|
1301
|
+
}
|
|
1302
|
+
),
|
|
1303
|
+
activeTab === "tools" && /* @__PURE__ */ jsx(ToolsTab, { agentTools: availableAgentTools, enabledToolIds, onToggle: handleToggleTool, agentFramework: selectedFramework, t }),
|
|
1304
|
+
activeTab === "models" && /* @__PURE__ */ jsx(
|
|
1305
|
+
ModelsTab,
|
|
1306
|
+
{
|
|
1307
|
+
modelProviders: availableModelProviders,
|
|
1308
|
+
selectedProviderId,
|
|
1309
|
+
onSelectProvider: handleSelectProvider,
|
|
1310
|
+
models,
|
|
1311
|
+
selectedModelId,
|
|
1312
|
+
onSelectModel: (modelId) => {
|
|
1313
|
+
setSelectedModelId(modelId);
|
|
1314
|
+
markDirty();
|
|
1315
|
+
},
|
|
1316
|
+
agentFramework: selectedFramework,
|
|
1317
|
+
t
|
|
1318
|
+
}
|
|
1319
|
+
),
|
|
1320
|
+
activeTab === "results" && /* @__PURE__ */ jsx(ResultsTab, { agentId: agent.agentId, t })
|
|
1321
|
+
]
|
|
1322
|
+
}
|
|
1323
|
+
);
|
|
1324
|
+
}
|
|
1325
|
+
var useSubworkflowStore = create((set) => ({
|
|
1326
|
+
// State
|
|
1327
|
+
tool: null,
|
|
1328
|
+
open: false,
|
|
1329
|
+
name: "",
|
|
1330
|
+
category: "external",
|
|
1331
|
+
description: "",
|
|
1332
|
+
timeoutMs: 1e4,
|
|
1333
|
+
retryCount: 0,
|
|
1334
|
+
dirty: false,
|
|
1335
|
+
// Actions
|
|
1336
|
+
openModal: (tool) => set({
|
|
1337
|
+
tool,
|
|
1338
|
+
open: true,
|
|
1339
|
+
name: tool.name ?? "",
|
|
1340
|
+
category: tool.category ?? "external",
|
|
1341
|
+
description: String(tool.description ?? ""),
|
|
1342
|
+
timeoutMs: Number(tool.timeoutMs ?? 1e4),
|
|
1343
|
+
retryCount: tool.retryCount ?? 0,
|
|
1344
|
+
dirty: !tool.toolId
|
|
1345
|
+
}),
|
|
1346
|
+
closeModal: () => set({
|
|
1347
|
+
tool: null,
|
|
1348
|
+
open: false,
|
|
1349
|
+
name: "",
|
|
1350
|
+
category: "external",
|
|
1351
|
+
description: "",
|
|
1352
|
+
timeoutMs: 1e4,
|
|
1353
|
+
retryCount: 0,
|
|
1354
|
+
dirty: false
|
|
1355
|
+
}),
|
|
1356
|
+
setName: (name) => set({ name, dirty: true }),
|
|
1357
|
+
setCategory: (category) => set({ category, dirty: true }),
|
|
1358
|
+
setDescription: (description) => set({ description, dirty: true }),
|
|
1359
|
+
setTimeoutMs: (timeoutMs) => set({ timeoutMs, dirty: true }),
|
|
1360
|
+
setRetryCount: (retryCount) => set({ retryCount, dirty: true }),
|
|
1361
|
+
markDirty: () => set({ dirty: true }),
|
|
1362
|
+
markSaved: () => set({ dirty: false })
|
|
1363
|
+
}));
|
|
1364
|
+
var DEFAULT_SUBWORKFLOW_GRAPH = {
|
|
1365
|
+
nodes: [
|
|
1366
|
+
{ id: "start", type: "start", position: { x: 80, y: 200 }, data: { entityId: "start", label: "Start", config: { type: "start", inputVariables: [] } } },
|
|
1367
|
+
{ id: "end", type: "end", position: { x: 600, y: 200 }, data: { entityId: "end", label: "End", config: { type: "end", outputVariables: [] } } }
|
|
1368
|
+
],
|
|
1369
|
+
edges: [
|
|
1370
|
+
{ id: "e-start-end", source: "start", target: "end", sourceHandle: null, targetHandle: null }
|
|
1371
|
+
],
|
|
1372
|
+
viewport: { x: 0, y: 0, zoom: 0.85 }
|
|
1373
|
+
};
|
|
1374
|
+
function VariableChip({ name, type }) {
|
|
1375
|
+
return /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1 rounded-lg border border-teal-200/50 bg-teal-50/50 px-2 py-0.5 text-[10px] dark:border-teal-400/20 dark:bg-teal-400/10", children: [
|
|
1376
|
+
/* @__PURE__ */ jsx("span", { className: "font-semibold text-teal-700 dark:text-teal-300", children: name }),
|
|
1377
|
+
/* @__PURE__ */ jsxs("span", { className: "text-teal-500/60 dark:text-teal-400/50", children: [
|
|
1378
|
+
": ",
|
|
1379
|
+
type
|
|
1380
|
+
] })
|
|
1381
|
+
] });
|
|
1382
|
+
}
|
|
1383
|
+
function OutputChip({ name, type }) {
|
|
1384
|
+
return /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1 rounded-lg border border-violet-200/50 bg-violet-50/50 px-2 py-0.5 text-[10px] dark:border-violet-400/20 dark:bg-violet-400/10", children: [
|
|
1385
|
+
/* @__PURE__ */ jsx("span", { className: "font-semibold text-violet-700 dark:text-violet-300", children: name }),
|
|
1386
|
+
/* @__PURE__ */ jsxs("span", { className: "text-violet-500/60 dark:text-violet-400/50", children: [
|
|
1387
|
+
": ",
|
|
1388
|
+
type
|
|
1389
|
+
] })
|
|
1390
|
+
] });
|
|
1391
|
+
}
|
|
1392
|
+
function ConfigSection({ title, icon: Icon, defaultOpen = true, children }) {
|
|
1393
|
+
const [open, setOpen] = useState(defaultOpen);
|
|
1394
|
+
return /* @__PURE__ */ jsxs("div", { className: "border-b border-white/10", children: [
|
|
1395
|
+
/* @__PURE__ */ jsxs(
|
|
1396
|
+
"button",
|
|
1397
|
+
{
|
|
1398
|
+
type: "button",
|
|
1399
|
+
onClick: () => setOpen(!open),
|
|
1400
|
+
"aria-expanded": open,
|
|
1401
|
+
className: "flex w-full items-center gap-2 px-4 py-2.5 text-left hover:bg-white/5",
|
|
1402
|
+
children: [
|
|
1403
|
+
open ? /* @__PURE__ */ jsx(ChevronDownIcon, { className: "h-3 w-3 text-gray-400" }) : /* @__PURE__ */ jsx(ChevronRightIcon, { className: "h-3 w-3 text-gray-400" }),
|
|
1404
|
+
/* @__PURE__ */ jsx(Icon, { className: "h-3 w-3 text-gray-400" }),
|
|
1405
|
+
/* @__PURE__ */ jsx("span", { className: "text-[10px] font-semibold uppercase tracking-wider text-gray-500 dark:text-gray-400", children: title })
|
|
1406
|
+
]
|
|
1407
|
+
}
|
|
1408
|
+
),
|
|
1409
|
+
open && /* @__PURE__ */ jsx("div", { className: "px-4 pb-3", children })
|
|
1410
|
+
] });
|
|
1411
|
+
}
|
|
1412
|
+
function SubworkflowModal({ onSaved, onMaximize }) {
|
|
1413
|
+
const t = useTranslations("agents.workflow");
|
|
1414
|
+
const tool = useSubworkflowStore((s) => s.tool);
|
|
1415
|
+
const open = useSubworkflowStore((s) => s.open);
|
|
1416
|
+
const name = useSubworkflowStore((s) => s.name);
|
|
1417
|
+
const category = useSubworkflowStore((s) => s.category);
|
|
1418
|
+
const description = useSubworkflowStore((s) => s.description);
|
|
1419
|
+
const timeoutMs = useSubworkflowStore((s) => s.timeoutMs);
|
|
1420
|
+
const retryCount = useSubworkflowStore((s) => s.retryCount);
|
|
1421
|
+
const dirty = useSubworkflowStore((s) => s.dirty);
|
|
1422
|
+
const setName = useSubworkflowStore((s) => s.setName);
|
|
1423
|
+
const setCategory = useSubworkflowStore((s) => s.setCategory);
|
|
1424
|
+
const setDescription = useSubworkflowStore((s) => s.setDescription);
|
|
1425
|
+
const setTimeoutMs = useSubworkflowStore((s) => s.setTimeoutMs);
|
|
1426
|
+
const setRetryCount = useSubworkflowStore((s) => s.setRetryCount);
|
|
1427
|
+
const markDirty = useSubworkflowStore((s) => s.markDirty);
|
|
1428
|
+
const markSaved = useSubworkflowStore((s) => s.markSaved);
|
|
1429
|
+
const closeModal = useSubworkflowStore((s) => s.closeModal);
|
|
1430
|
+
const agentBehind = useModalStore((s) => s.activeModal === "agent" ? s.agentData?.agent?.name : null);
|
|
1431
|
+
const isCreateMode = !tool?.toolId;
|
|
1432
|
+
const initialGraph = useMemo(() => {
|
|
1433
|
+
const config = tool?.config;
|
|
1434
|
+
if (config?.type === "workflow" && config.graph) {
|
|
1435
|
+
return config.graph;
|
|
1436
|
+
}
|
|
1437
|
+
return DEFAULT_SUBWORKFLOW_GRAPH;
|
|
1438
|
+
}, [tool]);
|
|
1439
|
+
const latestGraphRef = useMemo(() => ({ current: initialGraph }), [initialGraph]);
|
|
1440
|
+
const handleGraphChange = useCallback((graph2) => {
|
|
1441
|
+
latestGraphRef.current = graph2;
|
|
1442
|
+
markDirty();
|
|
1443
|
+
}, [latestGraphRef, markDirty]);
|
|
1444
|
+
const handleSave = useCallback(() => {
|
|
1445
|
+
markSaved();
|
|
1446
|
+
if (tool) {
|
|
1447
|
+
onSaved?.({
|
|
1448
|
+
...tool,
|
|
1449
|
+
name,
|
|
1450
|
+
category,
|
|
1451
|
+
description,
|
|
1452
|
+
timeoutMs,
|
|
1453
|
+
config: { type: "workflow", graph: latestGraphRef.current }
|
|
1454
|
+
});
|
|
1455
|
+
}
|
|
1456
|
+
}, [tool, onSaved, name, category, description, timeoutMs, latestGraphRef, markSaved]);
|
|
1457
|
+
if (!tool) return null;
|
|
1458
|
+
const categoryKey = category ?? "external";
|
|
1459
|
+
const gradient = tool.color ?? CATEGORY_COLORS[categoryKey] ?? CATEGORY_COLORS.external;
|
|
1460
|
+
const categoryPill = CATEGORY_PILL_COLORS[categoryKey] ?? CATEGORY_PILL_COLORS.external;
|
|
1461
|
+
const IconComponent = ICON_MAP[tool.icon ?? ""] ?? ArrowPathRoundedSquareIcon;
|
|
1462
|
+
const graph = latestGraphRef.current;
|
|
1463
|
+
const startNode = graph.nodes.find((n) => n.type === "start");
|
|
1464
|
+
const endNode = graph.nodes.find((n) => n.type === "end");
|
|
1465
|
+
const startConfig = startNode?.data?.config;
|
|
1466
|
+
const endConfig = endNode?.data?.config;
|
|
1467
|
+
const inputVariables = (startConfig?.inputVariables ?? []).map((v) => ({ name: v, type: "string" }));
|
|
1468
|
+
const outputVariables = (endConfig?.outputVariables ?? []).map((v) => ({ name: v, type: "string" }));
|
|
1469
|
+
const footer = /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
|
|
1470
|
+
/* @__PURE__ */ jsx("div", { role: "status", "aria-live": "polite", className: "text-xs", children: dirty ? /* @__PURE__ */ jsx("span", { className: "text-amber-500 dark:text-amber-400", children: t("subworkflowDrawer.unsavedChanges") }) : /* @__PURE__ */ jsx("span", { className: "text-emerald-500 dark:text-emerald-400", children: t("subworkflowDrawer.saved") }) }),
|
|
1471
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
1472
|
+
/* @__PURE__ */ jsx(
|
|
1473
|
+
"button",
|
|
1474
|
+
{
|
|
1475
|
+
type: "button",
|
|
1476
|
+
onClick: closeModal,
|
|
1477
|
+
className: "rounded-lg border border-gray-200/50 px-4 py-2 text-xs font-medium text-gray-600 transition-colors hover:bg-gray-100/50 dark:border-white/10 dark:text-gray-300 dark:hover:bg-white/5",
|
|
1478
|
+
children: t("subworkflowDrawer.cancel")
|
|
1479
|
+
}
|
|
1480
|
+
),
|
|
1481
|
+
/* @__PURE__ */ jsx(
|
|
1482
|
+
"button",
|
|
1483
|
+
{
|
|
1484
|
+
type: "button",
|
|
1485
|
+
onClick: handleSave,
|
|
1486
|
+
disabled: isCreateMode && !name.trim(),
|
|
1487
|
+
className: `rounded-lg px-4 py-2 text-xs font-semibold text-white shadow-sm transition-all ${dirty ? "bg-gradient-to-r from-teal-500 to-cyan-500 hover:from-teal-600 hover:to-cyan-600" : "cursor-not-allowed bg-gray-300 dark:bg-gray-700"}`,
|
|
1488
|
+
children: isCreateMode ? t("subworkflowDrawer.create") : t("subworkflowDrawer.save")
|
|
1489
|
+
}
|
|
1490
|
+
)
|
|
1491
|
+
] })
|
|
1492
|
+
] });
|
|
1493
|
+
return /* @__PURE__ */ jsx(
|
|
1494
|
+
GlassModal,
|
|
1495
|
+
{
|
|
1496
|
+
open,
|
|
1497
|
+
onClose: closeModal,
|
|
1498
|
+
title: isCreateMode ? t("subworkflowDrawer.createTitle") : name,
|
|
1499
|
+
subtitle: agentBehind ? `${agentBehind} \u2192 ${t("subworkflowDrawer.subworkflow")}` : t("subworkflowDrawer.subworkflow"),
|
|
1500
|
+
label: isCreateMode ? t("subworkflowDrawer.new") : t("subworkflowDrawer.edit"),
|
|
1501
|
+
gradient: "from-teal-500 to-cyan-600",
|
|
1502
|
+
icon: /* @__PURE__ */ jsx(ArrowPathRoundedSquareIcon, { className: "h-6 w-6 text-white" }),
|
|
1503
|
+
maxWidth: "6xl",
|
|
1504
|
+
footer,
|
|
1505
|
+
zIndex: agentBehind ? "z-[60]" : "z-50",
|
|
1506
|
+
children: /* @__PURE__ */ jsxs("div", { className: "flex h-[80vh] min-h-[600px]", children: [
|
|
1507
|
+
/* @__PURE__ */ jsxs("div", { className: "flex w-72 flex-shrink-0 flex-col overflow-y-auto border-r border-white/10", children: [
|
|
1508
|
+
isCreateMode ? /* @__PURE__ */ jsxs("div", { className: "border-b border-white/10 bg-gradient-to-br from-teal-500/15 via-cyan-500/8 to-transparent px-4 py-4 dark:from-teal-500/8 dark:via-cyan-500/4", children: [
|
|
1509
|
+
/* @__PURE__ */ jsx("label", { className: "mb-1 block text-[10px] font-semibold uppercase tracking-wider text-gray-500 dark:text-gray-400", children: t("subworkflowDrawer.name") }),
|
|
1510
|
+
/* @__PURE__ */ jsx(
|
|
1511
|
+
"input",
|
|
1512
|
+
{
|
|
1513
|
+
type: "text",
|
|
1514
|
+
value: name,
|
|
1515
|
+
onChange: (event) => setName(event.target.value),
|
|
1516
|
+
placeholder: t("subworkflowDrawer.namePlaceholder"),
|
|
1517
|
+
autoFocus: true,
|
|
1518
|
+
className: "w-full rounded-lg border border-gray-200/50 bg-white/60 px-3 py-2 text-sm font-medium text-gray-900 outline-none placeholder:text-gray-400 focus:border-teal-300/50 focus:ring-1 focus:ring-teal-300/30 dark:border-white/10 dark:bg-white/5 dark:text-white dark:placeholder:text-gray-500"
|
|
1519
|
+
}
|
|
1520
|
+
),
|
|
1521
|
+
/* @__PURE__ */ jsx("div", { className: "mt-3 flex flex-wrap gap-1", children: ["external", "market", "data", "communication", "analytics"].map((cat) => /* @__PURE__ */ jsx(
|
|
1522
|
+
"button",
|
|
1523
|
+
{
|
|
1524
|
+
type: "button",
|
|
1525
|
+
onClick: () => setCategory(cat),
|
|
1526
|
+
className: `rounded-full px-2 py-1 text-[9px] font-semibold transition-all ${cat === category ? `${CATEGORY_PILL_COLORS[cat] ?? CATEGORY_PILL_COLORS.external} ring-1 ring-current/20` : "bg-white/30 text-gray-500 hover:bg-white/50 dark:bg-white/5 dark:text-gray-400"}`,
|
|
1527
|
+
children: cat
|
|
1528
|
+
},
|
|
1529
|
+
cat
|
|
1530
|
+
)) })
|
|
1531
|
+
] }) : /* @__PURE__ */ jsx("div", { className: "border-b border-white/10 bg-gradient-to-br from-teal-500/15 via-cyan-500/8 to-transparent px-4 py-4 dark:from-teal-500/8 dark:via-cyan-500/4", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
1532
|
+
/* @__PURE__ */ jsx("div", { className: `flex h-10 w-10 shrink-0 items-center justify-center rounded-xl bg-gradient-to-br ${gradient} shadow-lg`, children: /* @__PURE__ */ jsx(IconComponent, { className: "h-5 w-5 text-white" }) }),
|
|
1533
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
1534
|
+
/* @__PURE__ */ jsx("h3", { className: "truncate text-sm font-bold text-gray-900 dark:text-white", children: name }),
|
|
1535
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-1 flex flex-wrap gap-1", children: [
|
|
1536
|
+
/* @__PURE__ */ jsx("span", { className: "inline-flex items-center rounded-full bg-teal-100 px-1.5 py-0.5 text-[8px] font-semibold text-teal-700 dark:bg-teal-400/15 dark:text-teal-400", children: t("subworkflowDrawer.subworkflow") }),
|
|
1537
|
+
/* @__PURE__ */ jsx("span", { className: `inline-flex items-center rounded-full px-1.5 py-0.5 text-[8px] font-medium ${categoryPill}`, children: category })
|
|
1538
|
+
] })
|
|
1539
|
+
] }),
|
|
1540
|
+
onMaximize && tool.toolId && /* @__PURE__ */ jsx(
|
|
1541
|
+
"button",
|
|
1542
|
+
{
|
|
1543
|
+
type: "button",
|
|
1544
|
+
onClick: () => onMaximize(tool.toolId),
|
|
1545
|
+
title: t("subworkflowDrawer.openFullEditor"),
|
|
1546
|
+
className: "flex h-8 w-8 shrink-0 items-center justify-center rounded-lg text-gray-400 transition-colors hover:bg-white/10 hover:text-teal-500 dark:hover:text-teal-400",
|
|
1547
|
+
children: /* @__PURE__ */ jsx(ArrowsPointingOutIcon, { className: "h-4 w-4" })
|
|
1548
|
+
}
|
|
1549
|
+
)
|
|
1550
|
+
] }) }),
|
|
1551
|
+
/* @__PURE__ */ jsx("div", { className: "border-b border-white/10 px-4 py-3", children: /* @__PURE__ */ jsx(
|
|
1552
|
+
"textarea",
|
|
1553
|
+
{
|
|
1554
|
+
value: description,
|
|
1555
|
+
onChange: (event) => setDescription(event.target.value),
|
|
1556
|
+
rows: 2,
|
|
1557
|
+
placeholder: t("subworkflowDrawer.descriptionPlaceholder"),
|
|
1558
|
+
className: "w-full resize-none rounded-lg border border-gray-200/50 bg-gray-50/50 px-2.5 py-2 text-[11px] leading-relaxed text-gray-700 outline-none placeholder:text-gray-400 focus:border-teal-300/50 dark:border-white/10 dark:bg-white/5 dark:text-gray-300 dark:placeholder:text-gray-500"
|
|
1559
|
+
}
|
|
1560
|
+
) }),
|
|
1561
|
+
/* @__PURE__ */ jsx(ConfigSection, { title: t("subworkflowDrawer.inputVariables"), icon: ArrowsPointingInIcon, children: /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1", children: inputVariables.length > 0 ? inputVariables.map((variable) => /* @__PURE__ */ jsx(VariableChip, { name: variable.name, type: variable.type }, variable.name)) : /* @__PURE__ */ jsx("span", { className: "text-[10px] text-gray-400 dark:text-gray-500", children: t("subworkflowDrawer.noVariables") }) }) }),
|
|
1562
|
+
/* @__PURE__ */ jsx(ConfigSection, { title: t("subworkflowDrawer.outputVariables"), icon: ArrowsPointingOutIcon, children: /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1", children: outputVariables.length > 0 ? outputVariables.map((variable) => /* @__PURE__ */ jsx(OutputChip, { name: variable.name, type: variable.type }, variable.name)) : /* @__PURE__ */ jsx("span", { className: "text-[10px] text-gray-400 dark:text-gray-500", children: t("subworkflowDrawer.noVariables") }) }) }),
|
|
1563
|
+
/* @__PURE__ */ jsxs(ConfigSection, { title: t("subworkflowDrawer.executionSettings"), icon: BoltIcon, defaultOpen: false, children: [
|
|
1564
|
+
/* @__PURE__ */ jsxs("div", { className: "mb-3", children: [
|
|
1565
|
+
/* @__PURE__ */ jsxs("div", { className: "mb-1 flex items-center justify-between", children: [
|
|
1566
|
+
/* @__PURE__ */ jsxs("label", { className: "flex items-center gap-1 text-[10px] text-gray-600 dark:text-gray-400", children: [
|
|
1567
|
+
/* @__PURE__ */ jsx(ClockIcon, { className: "h-3 w-3" }),
|
|
1568
|
+
t("subworkflowDrawer.timeout")
|
|
1569
|
+
] }),
|
|
1570
|
+
/* @__PURE__ */ jsxs("span", { className: "text-[10px] font-bold tabular-nums text-gray-900 dark:text-white", children: [
|
|
1571
|
+
(timeoutMs / 1e3).toFixed(0),
|
|
1572
|
+
"s"
|
|
1573
|
+
] })
|
|
1574
|
+
] }),
|
|
1575
|
+
/* @__PURE__ */ jsx(
|
|
1576
|
+
"input",
|
|
1577
|
+
{
|
|
1578
|
+
type: "range",
|
|
1579
|
+
min: "1000",
|
|
1580
|
+
max: "30000",
|
|
1581
|
+
step: "1000",
|
|
1582
|
+
value: timeoutMs,
|
|
1583
|
+
onChange: (event) => setTimeoutMs(parseInt(event.target.value, 10)),
|
|
1584
|
+
"aria-label": t("subworkflowDrawer.timeout"),
|
|
1585
|
+
"aria-valuemin": 1,
|
|
1586
|
+
"aria-valuemax": 30,
|
|
1587
|
+
"aria-valuenow": timeoutMs / 1e3,
|
|
1588
|
+
"aria-valuetext": `${(timeoutMs / 1e3).toFixed(0)} seconds`,
|
|
1589
|
+
className: "h-3 w-full cursor-pointer appearance-none rounded-full bg-gray-200 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-teal-500 dark:bg-gray-700 [&::-webkit-slider-thumb]:h-3 [&::-webkit-slider-thumb]:w-3 [&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:rounded-full [&::-webkit-slider-thumb]:bg-teal-500"
|
|
1590
|
+
}
|
|
1591
|
+
)
|
|
1592
|
+
] }),
|
|
1593
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
1594
|
+
/* @__PURE__ */ jsxs("div", { className: "mb-1 flex items-center justify-between", children: [
|
|
1595
|
+
/* @__PURE__ */ jsx("label", { className: "text-[10px] text-gray-600 dark:text-gray-400", children: t("subworkflowDrawer.retries") }),
|
|
1596
|
+
/* @__PURE__ */ jsx("span", { className: "text-[10px] font-bold tabular-nums text-gray-900 dark:text-white", children: retryCount })
|
|
1597
|
+
] }),
|
|
1598
|
+
/* @__PURE__ */ jsx("div", { className: "flex gap-1", children: [0, 1, 2, 3].map((count) => /* @__PURE__ */ jsx(
|
|
1599
|
+
"button",
|
|
1600
|
+
{
|
|
1601
|
+
type: "button",
|
|
1602
|
+
onClick: () => setRetryCount(count),
|
|
1603
|
+
className: `flex-1 rounded py-1 text-[10px] font-medium transition-all ${count === retryCount ? "bg-teal-500 text-white" : "bg-gray-100 text-gray-500 dark:bg-white/5 dark:text-gray-400"}`,
|
|
1604
|
+
children: count
|
|
1605
|
+
},
|
|
1606
|
+
count
|
|
1607
|
+
)) })
|
|
1608
|
+
] })
|
|
1609
|
+
] }),
|
|
1610
|
+
/* @__PURE__ */ jsx("div", { className: "mt-auto" })
|
|
1611
|
+
] }),
|
|
1612
|
+
/* @__PURE__ */ jsx("div", { className: "flex-1", children: /* @__PURE__ */ jsx(
|
|
1613
|
+
WorkflowCanvas,
|
|
1614
|
+
{
|
|
1615
|
+
initialGraph,
|
|
1616
|
+
agents: [],
|
|
1617
|
+
models: [],
|
|
1618
|
+
tools: [],
|
|
1619
|
+
rules: [],
|
|
1620
|
+
onGraphChange: handleGraphChange
|
|
1621
|
+
}
|
|
1622
|
+
) })
|
|
1623
|
+
] })
|
|
1624
|
+
}
|
|
1625
|
+
);
|
|
1626
|
+
}
|
|
1627
|
+
function PipelineSettingsModal({ onSave }) {
|
|
1628
|
+
const t = useTranslations("agents.workflow");
|
|
1629
|
+
const activeModal = useModalStore((s) => s.activeModal);
|
|
1630
|
+
const data = useModalStore((s) => s.pipelineSettingsData);
|
|
1631
|
+
const closeModal = useModalStore((s) => s.closeModal);
|
|
1632
|
+
const open = activeModal === "pipeline-settings";
|
|
1633
|
+
const [nameValue, setNameValue] = useState("");
|
|
1634
|
+
const [descriptionValue, setDescriptionValue] = useState("");
|
|
1635
|
+
const [slugValue, setSlugValue] = useState("");
|
|
1636
|
+
const [isDraft, setIsDraft] = useState(true);
|
|
1637
|
+
const [isActive, setIsActive] = useState(true);
|
|
1638
|
+
const [isSaving, setIsSaving] = useState(false);
|
|
1639
|
+
const lifecycleAvailable = useMemo(() => {
|
|
1640
|
+
if (!data) return false;
|
|
1641
|
+
return data.isDraft !== void 0 || data.isActive !== void 0 || data.slug !== void 0;
|
|
1642
|
+
}, [data]);
|
|
1643
|
+
useEffect(() => {
|
|
1644
|
+
if (data) {
|
|
1645
|
+
setNameValue(data.name);
|
|
1646
|
+
setDescriptionValue(data.description);
|
|
1647
|
+
setSlugValue(data.slug ?? "");
|
|
1648
|
+
setIsDraft(data.isDraft ?? true);
|
|
1649
|
+
setIsActive(data.isActive ?? true);
|
|
1650
|
+
}
|
|
1651
|
+
}, [data]);
|
|
1652
|
+
const handleSubmit = async (event) => {
|
|
1653
|
+
event.preventDefault();
|
|
1654
|
+
const trimmedName = nameValue.trim();
|
|
1655
|
+
if (!trimmedName) return;
|
|
1656
|
+
const trimmedDescription = descriptionValue.trim();
|
|
1657
|
+
const trimmedSlug = slugValue.trim();
|
|
1658
|
+
const changes = {
|
|
1659
|
+
name: trimmedName,
|
|
1660
|
+
description: trimmedDescription
|
|
1661
|
+
};
|
|
1662
|
+
if (data) {
|
|
1663
|
+
if (trimmedSlug !== (data.slug ?? "")) {
|
|
1664
|
+
changes.slug = trimmedSlug.length > 0 ? trimmedSlug : null;
|
|
1665
|
+
}
|
|
1666
|
+
if (data.isDraft !== void 0 && isDraft !== data.isDraft) {
|
|
1667
|
+
changes.isDraft = isDraft;
|
|
1668
|
+
}
|
|
1669
|
+
if (data.isActive !== void 0 && isActive !== data.isActive) {
|
|
1670
|
+
changes.isActive = isActive;
|
|
1671
|
+
}
|
|
1672
|
+
}
|
|
1673
|
+
setIsSaving(true);
|
|
1674
|
+
try {
|
|
1675
|
+
await onSave(changes);
|
|
1676
|
+
closeModal();
|
|
1677
|
+
} catch {
|
|
1678
|
+
} finally {
|
|
1679
|
+
setIsSaving(false);
|
|
1680
|
+
}
|
|
1681
|
+
};
|
|
1682
|
+
const footer = /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-end gap-2", children: [
|
|
1683
|
+
/* @__PURE__ */ jsx(
|
|
1684
|
+
"button",
|
|
1685
|
+
{
|
|
1686
|
+
type: "button",
|
|
1687
|
+
onClick: closeModal,
|
|
1688
|
+
className: "rounded-lg border border-gray-200/50 px-4 py-2 text-xs font-medium text-gray-600 transition-colors hover:bg-gray-100/50 dark:border-white/10 dark:text-gray-300 dark:hover:bg-white/5",
|
|
1689
|
+
children: t("cancel")
|
|
1690
|
+
}
|
|
1691
|
+
),
|
|
1692
|
+
/* @__PURE__ */ jsxs(
|
|
1693
|
+
Button,
|
|
1694
|
+
{
|
|
1695
|
+
type: "submit",
|
|
1696
|
+
form: "pipeline-settings-form",
|
|
1697
|
+
color: "ios-glass-blue",
|
|
1698
|
+
loading: isSaving,
|
|
1699
|
+
loadingText: t("saving"),
|
|
1700
|
+
children: [
|
|
1701
|
+
/* @__PURE__ */ jsx(CheckIcon, { className: "h-4 w-4" }),
|
|
1702
|
+
t("saveSettings")
|
|
1703
|
+
]
|
|
1704
|
+
}
|
|
1705
|
+
)
|
|
1706
|
+
] });
|
|
1707
|
+
return /* @__PURE__ */ jsx(
|
|
1708
|
+
GlassModal,
|
|
1709
|
+
{
|
|
1710
|
+
open,
|
|
1711
|
+
onClose: closeModal,
|
|
1712
|
+
title: t("pipelineSettings"),
|
|
1713
|
+
subtitle: t("pipelineSettingsSubtitle"),
|
|
1714
|
+
label: t("settings"),
|
|
1715
|
+
icon: /* @__PURE__ */ jsx(Cog6ToothIcon, { className: "h-5 w-5 text-white" }),
|
|
1716
|
+
gradient: "from-indigo-500 to-purple-600",
|
|
1717
|
+
maxWidth: "md",
|
|
1718
|
+
footer,
|
|
1719
|
+
onSubmit: handleSubmit,
|
|
1720
|
+
children: /* @__PURE__ */ jsxs("form", { id: "pipeline-settings-form", onSubmit: handleSubmit, className: "space-y-5", children: [
|
|
1721
|
+
/* @__PURE__ */ jsx(
|
|
1722
|
+
FormInput,
|
|
1723
|
+
{
|
|
1724
|
+
label: t("pipelineName"),
|
|
1725
|
+
value: nameValue,
|
|
1726
|
+
onValueChange: setNameValue,
|
|
1727
|
+
placeholder: t("pipelineNamePlaceholder"),
|
|
1728
|
+
required: true
|
|
1729
|
+
}
|
|
1730
|
+
),
|
|
1731
|
+
/* @__PURE__ */ jsx(
|
|
1732
|
+
FormTextarea,
|
|
1733
|
+
{
|
|
1734
|
+
label: t("pipelineDescription"),
|
|
1735
|
+
value: descriptionValue,
|
|
1736
|
+
onValueChange: setDescriptionValue,
|
|
1737
|
+
placeholder: t("pipelineDescriptionPlaceholder"),
|
|
1738
|
+
rows: 4
|
|
1739
|
+
}
|
|
1740
|
+
),
|
|
1741
|
+
lifecycleAvailable && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1742
|
+
/* @__PURE__ */ jsx(
|
|
1743
|
+
FormInput,
|
|
1744
|
+
{
|
|
1745
|
+
label: t("pipelineSlug", { _: "Slug" }),
|
|
1746
|
+
value: slugValue,
|
|
1747
|
+
onValueChange: setSlugValue,
|
|
1748
|
+
placeholder: "fuel-pricing",
|
|
1749
|
+
hint: t("pipelineSlugHint", {
|
|
1750
|
+
_: "URL-friendly identifier. Leave blank to use the auto-generated one."
|
|
1751
|
+
})
|
|
1752
|
+
}
|
|
1753
|
+
),
|
|
1754
|
+
/* @__PURE__ */ jsxs("fieldset", { className: "space-y-2 rounded-xl border border-gray-200/60 bg-gray-50/60 p-3 dark:border-white/10 dark:bg-white/5", children: [
|
|
1755
|
+
/* @__PURE__ */ jsx("legend", { className: "px-1 text-xs font-medium text-gray-600 dark:text-gray-300", children: t("pipelineLifecycle", { _: "Lifecycle" }) }),
|
|
1756
|
+
/* @__PURE__ */ jsxs("label", { className: "flex items-center justify-between gap-3 text-sm text-gray-700 dark:text-gray-200", children: [
|
|
1757
|
+
/* @__PURE__ */ jsxs("span", { children: [
|
|
1758
|
+
t("pipelineIsDraft", { _: "Draft mode" }),
|
|
1759
|
+
/* @__PURE__ */ jsx("span", { className: "ml-2 text-[11px] text-gray-400", children: isDraft ? t("pipelineIsDraftOn", { _: "Editable \u2014 changes do not affect runs." }) : t("pipelineIsDraftOff", { _: "Published \u2014 edits require a new version." }) })
|
|
1760
|
+
] }),
|
|
1761
|
+
/* @__PURE__ */ jsx(
|
|
1762
|
+
"input",
|
|
1763
|
+
{
|
|
1764
|
+
type: "checkbox",
|
|
1765
|
+
checked: isDraft,
|
|
1766
|
+
onChange: (event) => setIsDraft(event.target.checked),
|
|
1767
|
+
className: "h-4 w-4 rounded border-gray-300 text-indigo-600 focus:ring-indigo-500"
|
|
1768
|
+
}
|
|
1769
|
+
)
|
|
1770
|
+
] }),
|
|
1771
|
+
/* @__PURE__ */ jsxs("label", { className: "flex items-center justify-between gap-3 text-sm text-gray-700 dark:text-gray-200", children: [
|
|
1772
|
+
/* @__PURE__ */ jsxs("span", { children: [
|
|
1773
|
+
t("pipelineIsActive", { _: "Active" }),
|
|
1774
|
+
/* @__PURE__ */ jsx("span", { className: "ml-2 text-[11px] text-gray-400", children: isActive ? t("pipelineIsActiveOn", { _: "Visible in listings and runnable." }) : t("pipelineIsActiveOff", { _: "Archived \u2014 hidden from the default listing." }) })
|
|
1775
|
+
] }),
|
|
1776
|
+
/* @__PURE__ */ jsx(
|
|
1777
|
+
"input",
|
|
1778
|
+
{
|
|
1779
|
+
type: "checkbox",
|
|
1780
|
+
checked: isActive,
|
|
1781
|
+
onChange: (event) => setIsActive(event.target.checked),
|
|
1782
|
+
className: "h-4 w-4 rounded border-gray-300 text-indigo-600 focus:ring-indigo-500"
|
|
1783
|
+
}
|
|
1784
|
+
)
|
|
1785
|
+
] })
|
|
1786
|
+
] })
|
|
1787
|
+
] })
|
|
1788
|
+
] })
|
|
1789
|
+
}
|
|
1790
|
+
);
|
|
1791
|
+
}
|
|
1792
|
+
function RunReplayModal({
|
|
1793
|
+
open,
|
|
1794
|
+
onClose,
|
|
1795
|
+
runId,
|
|
1796
|
+
workflowId,
|
|
1797
|
+
originalInputs,
|
|
1798
|
+
onReplay
|
|
1799
|
+
}) {
|
|
1800
|
+
const t = useTranslations("agents.workflow");
|
|
1801
|
+
const [rows, setRows] = useState([]);
|
|
1802
|
+
const [submitting, setSubmitting] = useState(false);
|
|
1803
|
+
const [error, setError] = useState(null);
|
|
1804
|
+
useEffect(() => {
|
|
1805
|
+
if (!open) return;
|
|
1806
|
+
setRows(Object.entries(originalInputs).map(([key, value]) => toRow(key, value)));
|
|
1807
|
+
setError(null);
|
|
1808
|
+
}, [open, originalInputs]);
|
|
1809
|
+
const overrides = useMemo(() => {
|
|
1810
|
+
const out = {};
|
|
1811
|
+
for (const row of rows) {
|
|
1812
|
+
if (!row.touched) continue;
|
|
1813
|
+
try {
|
|
1814
|
+
out[row.key] = parseRow(row);
|
|
1815
|
+
} catch (e) {
|
|
1816
|
+
out[`__error__${row.key}`] = e.message;
|
|
1817
|
+
}
|
|
1818
|
+
}
|
|
1819
|
+
return out;
|
|
1820
|
+
}, [rows]);
|
|
1821
|
+
const hasParseError = Object.keys(overrides).some((k) => k.startsWith("__error__"));
|
|
1822
|
+
const updateRow = (index, patch) => {
|
|
1823
|
+
setRows((current) => current.map((r, i) => i === index ? { ...r, ...patch, touched: true } : r));
|
|
1824
|
+
};
|
|
1825
|
+
const resetRow = (index) => {
|
|
1826
|
+
setRows((current) => current.map((r, i) => {
|
|
1827
|
+
if (i !== index) return r;
|
|
1828
|
+
return toRow(r.key, r.original);
|
|
1829
|
+
}));
|
|
1830
|
+
};
|
|
1831
|
+
const handleSubmit = async () => {
|
|
1832
|
+
setError(null);
|
|
1833
|
+
if (hasParseError) {
|
|
1834
|
+
setError(t("replayParseError", { _: "One or more values do not match their declared type." }));
|
|
1835
|
+
return;
|
|
1836
|
+
}
|
|
1837
|
+
setSubmitting(true);
|
|
1838
|
+
try {
|
|
1839
|
+
const cleaned = {};
|
|
1840
|
+
for (const row of rows) {
|
|
1841
|
+
if (!row.touched) continue;
|
|
1842
|
+
cleaned[row.key] = parseRow(row);
|
|
1843
|
+
}
|
|
1844
|
+
await onReplay(cleaned);
|
|
1845
|
+
onClose();
|
|
1846
|
+
} catch (e) {
|
|
1847
|
+
setError(e.message);
|
|
1848
|
+
} finally {
|
|
1849
|
+
setSubmitting(false);
|
|
1850
|
+
}
|
|
1851
|
+
};
|
|
1852
|
+
const footer = /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2", children: [
|
|
1853
|
+
/* @__PURE__ */ jsxs("span", { className: "text-[11px] text-gray-400", children: [
|
|
1854
|
+
"run ",
|
|
1855
|
+
/* @__PURE__ */ jsx("code", { children: runId.slice(0, 8) }),
|
|
1856
|
+
" \xB7 wf ",
|
|
1857
|
+
/* @__PURE__ */ jsx("code", { children: workflowId.slice(0, 8) })
|
|
1858
|
+
] }),
|
|
1859
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
1860
|
+
/* @__PURE__ */ jsx(
|
|
1861
|
+
"button",
|
|
1862
|
+
{
|
|
1863
|
+
type: "button",
|
|
1864
|
+
onClick: onClose,
|
|
1865
|
+
className: "rounded-lg border border-gray-200/50 px-4 py-2 text-xs font-medium text-gray-600 transition-colors hover:bg-gray-100/50 dark:border-white/10 dark:text-gray-300 dark:hover:bg-white/5",
|
|
1866
|
+
children: t("cancel")
|
|
1867
|
+
}
|
|
1868
|
+
),
|
|
1869
|
+
/* @__PURE__ */ jsxs(
|
|
1870
|
+
Button,
|
|
1871
|
+
{
|
|
1872
|
+
type: "submit",
|
|
1873
|
+
form: "run-replay-form",
|
|
1874
|
+
color: "ios-glass-blue",
|
|
1875
|
+
loading: submitting,
|
|
1876
|
+
children: [
|
|
1877
|
+
/* @__PURE__ */ jsx(ArrowPathIcon, { className: "h-4 w-4" }),
|
|
1878
|
+
t("replay", { _: "Replay" })
|
|
1879
|
+
]
|
|
1880
|
+
}
|
|
1881
|
+
)
|
|
1882
|
+
] })
|
|
1883
|
+
] });
|
|
1884
|
+
return /* @__PURE__ */ jsx(
|
|
1885
|
+
GlassModal,
|
|
1886
|
+
{
|
|
1887
|
+
open,
|
|
1888
|
+
onClose,
|
|
1889
|
+
title: t("replayTitle", { _: "Replay run" }),
|
|
1890
|
+
subtitle: t("replaySubtitle", { _: "Tweak any input below and re-execute. Untouched keys keep their original value." }),
|
|
1891
|
+
icon: /* @__PURE__ */ jsx(ArrowPathIcon, { className: "h-5 w-5 text-white" }),
|
|
1892
|
+
gradient: "from-sky-500 to-indigo-600",
|
|
1893
|
+
maxWidth: "lg",
|
|
1894
|
+
footer,
|
|
1895
|
+
onSubmit: (event) => {
|
|
1896
|
+
event.preventDefault();
|
|
1897
|
+
void handleSubmit();
|
|
1898
|
+
},
|
|
1899
|
+
children: /* @__PURE__ */ jsxs(
|
|
1900
|
+
"form",
|
|
1901
|
+
{
|
|
1902
|
+
id: "run-replay-form",
|
|
1903
|
+
onSubmit: (event) => {
|
|
1904
|
+
event.preventDefault();
|
|
1905
|
+
void handleSubmit();
|
|
1906
|
+
},
|
|
1907
|
+
className: "space-y-3",
|
|
1908
|
+
children: [
|
|
1909
|
+
rows.length === 0 ? /* @__PURE__ */ jsx("p", { className: "text-xs text-gray-500 dark:text-gray-400", children: t("replayNoInputs", { _: "This run had no input variables \u2014 replaying will execute the workflow as-is." }) }) : rows.map((row, index) => /* @__PURE__ */ jsx(
|
|
1910
|
+
RowEditor,
|
|
1911
|
+
{
|
|
1912
|
+
row,
|
|
1913
|
+
onChange: (patch) => updateRow(index, patch),
|
|
1914
|
+
onReset: () => resetRow(index)
|
|
1915
|
+
},
|
|
1916
|
+
row.key
|
|
1917
|
+
)),
|
|
1918
|
+
error && /* @__PURE__ */ jsx("p", { className: "rounded-lg border border-red-400/40 bg-red-500/10 p-2 text-xs text-red-600 dark:text-red-300", children: error })
|
|
1919
|
+
]
|
|
1920
|
+
}
|
|
1921
|
+
)
|
|
1922
|
+
}
|
|
1923
|
+
);
|
|
1924
|
+
}
|
|
1925
|
+
function RowEditor({
|
|
1926
|
+
row,
|
|
1927
|
+
onChange,
|
|
1928
|
+
onReset
|
|
1929
|
+
}) {
|
|
1930
|
+
const isJson = row.kind === "json";
|
|
1931
|
+
return /* @__PURE__ */ jsxs("div", { className: "rounded-xl border border-gray-200/60 bg-gray-50/60 p-3 dark:border-white/10 dark:bg-white/5", children: [
|
|
1932
|
+
/* @__PURE__ */ jsxs("div", { className: "mb-2 flex items-center justify-between gap-2", children: [
|
|
1933
|
+
/* @__PURE__ */ jsx("span", { className: "font-mono text-xs text-gray-700 dark:text-gray-200", children: row.key }),
|
|
1934
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
1935
|
+
/* @__PURE__ */ jsx("span", { className: "rounded-full bg-gray-200/60 px-2 py-0.5 text-[10px] uppercase tracking-wider text-gray-600 dark:bg-white/10 dark:text-gray-300", children: row.kind }),
|
|
1936
|
+
row.touched && /* @__PURE__ */ jsx(
|
|
1937
|
+
"button",
|
|
1938
|
+
{
|
|
1939
|
+
type: "button",
|
|
1940
|
+
onClick: onReset,
|
|
1941
|
+
className: "text-[11px] text-indigo-600 hover:text-indigo-500 dark:text-indigo-400",
|
|
1942
|
+
children: "reset"
|
|
1943
|
+
}
|
|
1944
|
+
)
|
|
1945
|
+
] })
|
|
1946
|
+
] }),
|
|
1947
|
+
row.kind === "boolean" ? /* @__PURE__ */ jsxs(
|
|
1948
|
+
"select",
|
|
1949
|
+
{
|
|
1950
|
+
value: row.value,
|
|
1951
|
+
onChange: (event) => onChange({ value: event.target.value }),
|
|
1952
|
+
className: "w-full rounded-lg border border-gray-200/50 bg-white/70 px-3 py-2 text-sm text-gray-900 dark:border-white/10 dark:bg-gray-800/70 dark:text-gray-100",
|
|
1953
|
+
children: [
|
|
1954
|
+
/* @__PURE__ */ jsx("option", { value: "true", children: "true" }),
|
|
1955
|
+
/* @__PURE__ */ jsx("option", { value: "false", children: "false" })
|
|
1956
|
+
]
|
|
1957
|
+
}
|
|
1958
|
+
) : isJson ? /* @__PURE__ */ jsx(
|
|
1959
|
+
FormTextarea,
|
|
1960
|
+
{
|
|
1961
|
+
value: row.value,
|
|
1962
|
+
onValueChange: (v) => onChange({ value: v }),
|
|
1963
|
+
rows: 4,
|
|
1964
|
+
className: "font-mono"
|
|
1965
|
+
}
|
|
1966
|
+
) : /* @__PURE__ */ jsx(
|
|
1967
|
+
FormInput,
|
|
1968
|
+
{
|
|
1969
|
+
type: row.kind === "number" ? "number" : "text",
|
|
1970
|
+
value: row.value,
|
|
1971
|
+
onValueChange: (v) => onChange({ value: v })
|
|
1972
|
+
}
|
|
1973
|
+
)
|
|
1974
|
+
] });
|
|
1975
|
+
}
|
|
1976
|
+
function toRow(key, value) {
|
|
1977
|
+
if (value === null || value === void 0) {
|
|
1978
|
+
return { key, value: "", kind: "string", touched: false, original: value };
|
|
1979
|
+
}
|
|
1980
|
+
if (typeof value === "boolean") {
|
|
1981
|
+
return { key, value: value ? "true" : "false", kind: "boolean", touched: false, original: value };
|
|
1982
|
+
}
|
|
1983
|
+
if (typeof value === "number") {
|
|
1984
|
+
return { key, value: String(value), kind: "number", touched: false, original: value };
|
|
1985
|
+
}
|
|
1986
|
+
if (typeof value === "string") {
|
|
1987
|
+
return { key, value, kind: "string", touched: false, original: value };
|
|
1988
|
+
}
|
|
1989
|
+
return {
|
|
1990
|
+
key,
|
|
1991
|
+
value: JSON.stringify(value, null, 2),
|
|
1992
|
+
kind: "json",
|
|
1993
|
+
touched: false,
|
|
1994
|
+
original: value
|
|
1995
|
+
};
|
|
1996
|
+
}
|
|
1997
|
+
function parseRow(row) {
|
|
1998
|
+
switch (row.kind) {
|
|
1999
|
+
case "string":
|
|
2000
|
+
return row.value;
|
|
2001
|
+
case "number": {
|
|
2002
|
+
const n = Number(row.value);
|
|
2003
|
+
if (!Number.isFinite(n)) {
|
|
2004
|
+
throw new Error(`'${row.key}' must be a number, got '${row.value}'`);
|
|
2005
|
+
}
|
|
2006
|
+
return n;
|
|
2007
|
+
}
|
|
2008
|
+
case "boolean":
|
|
2009
|
+
return row.value === "true";
|
|
2010
|
+
case "json": {
|
|
2011
|
+
if (row.value.trim().length === 0) return null;
|
|
2012
|
+
try {
|
|
2013
|
+
return JSON.parse(row.value);
|
|
2014
|
+
} catch (e) {
|
|
2015
|
+
throw new Error(`'${row.key}' is not valid JSON: ${e.message}`);
|
|
2016
|
+
}
|
|
2017
|
+
}
|
|
2018
|
+
}
|
|
2019
|
+
}
|
|
2020
|
+
|
|
2021
|
+
// src/astrlabe/components/rules/types.ts
|
|
2022
|
+
var RULE_STATUS_OPTIONS = ["draft", "active", "archived"];
|
|
2023
|
+
var TIMEZONE_OPTIONS = [
|
|
2024
|
+
"UTC",
|
|
2025
|
+
"America/Sao_Paulo",
|
|
2026
|
+
"America/New_York",
|
|
2027
|
+
"America/Los_Angeles",
|
|
2028
|
+
"Europe/London",
|
|
2029
|
+
"Europe/Lisbon"
|
|
2030
|
+
];
|
|
2031
|
+
var OPERATOR_OPTIONS = [
|
|
2032
|
+
{ value: "truthy", label: "Field is set (truthy)" },
|
|
2033
|
+
{ value: "eq", label: "Equals" },
|
|
2034
|
+
{ value: "neq", label: "Not equal to" },
|
|
2035
|
+
{ value: "gt", label: "Greater than" },
|
|
2036
|
+
{ value: "gte", label: "Greater than or equal" },
|
|
2037
|
+
{ value: "lt", label: "Less than" },
|
|
2038
|
+
{ value: "lte", label: "Less than or equal" },
|
|
2039
|
+
{ value: "contains", label: "Contains (string)" },
|
|
2040
|
+
{ value: "threshold", label: "Threshold" },
|
|
2041
|
+
{ value: "regex_match", label: "Regex match" },
|
|
2042
|
+
{ value: "time_window", label: "Time window" },
|
|
2043
|
+
{ value: "boolean_expression", label: "And/Or group" }
|
|
2044
|
+
];
|
|
2045
|
+
var SIMPLE_COMPARISON_OPERATORS = [
|
|
2046
|
+
"truthy",
|
|
2047
|
+
"eq",
|
|
2048
|
+
"neq",
|
|
2049
|
+
"gt",
|
|
2050
|
+
"gte",
|
|
2051
|
+
"lt",
|
|
2052
|
+
"lte",
|
|
2053
|
+
"contains"
|
|
2054
|
+
];
|
|
2055
|
+
function RuleConditionBuilder({ value, onChange, depth = 0 }) {
|
|
2056
|
+
const handleOperatorChange = (op) => {
|
|
2057
|
+
const base = { operator: op };
|
|
2058
|
+
switch (op) {
|
|
2059
|
+
case "truthy":
|
|
2060
|
+
onChange({ ...base, field: value.field ?? "" });
|
|
2061
|
+
break;
|
|
2062
|
+
case "eq":
|
|
2063
|
+
case "neq":
|
|
2064
|
+
case "gt":
|
|
2065
|
+
case "gte":
|
|
2066
|
+
case "lt":
|
|
2067
|
+
case "lte":
|
|
2068
|
+
case "contains":
|
|
2069
|
+
onChange({ ...base, field: value.field ?? "", value: value.value ?? "" });
|
|
2070
|
+
break;
|
|
2071
|
+
case "threshold":
|
|
2072
|
+
onChange({
|
|
2073
|
+
...base,
|
|
2074
|
+
field: value.field ?? "",
|
|
2075
|
+
comparison: value.comparison ?? "gte",
|
|
2076
|
+
value: typeof value.value === "number" ? value.value : 0
|
|
2077
|
+
});
|
|
2078
|
+
break;
|
|
2079
|
+
case "regex_match":
|
|
2080
|
+
onChange({ ...base, field: value.field ?? "", pattern: value.pattern ?? "" });
|
|
2081
|
+
break;
|
|
2082
|
+
case "time_window":
|
|
2083
|
+
onChange({
|
|
2084
|
+
...base,
|
|
2085
|
+
field: value.field ?? "",
|
|
2086
|
+
timezone: value.timezone ?? "UTC",
|
|
2087
|
+
windows: value.windows && value.windows.length > 0 ? value.windows : [{ startHour: 6, endHour: 9 }]
|
|
2088
|
+
});
|
|
2089
|
+
break;
|
|
2090
|
+
case "boolean_expression":
|
|
2091
|
+
onChange({
|
|
2092
|
+
...base,
|
|
2093
|
+
combinator: value.combinator ?? "and",
|
|
2094
|
+
operands: value.operands && value.operands.length > 0 ? value.operands : [{ operator: "gt", field: "", value: 0 }, { operator: "gt", field: "", value: 0 }]
|
|
2095
|
+
});
|
|
2096
|
+
break;
|
|
2097
|
+
}
|
|
2098
|
+
};
|
|
2099
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-3 rounded-xl border border-slate-200 bg-slate-50/60 p-3 dark:border-slate-700 dark:bg-slate-900/40", children: [
|
|
2100
|
+
/* @__PURE__ */ jsx(
|
|
2101
|
+
FormSelect,
|
|
2102
|
+
{
|
|
2103
|
+
label: "Operator",
|
|
2104
|
+
value: value.operator,
|
|
2105
|
+
options: OPERATOR_OPTIONS,
|
|
2106
|
+
onValueChange: (v) => handleOperatorChange(v)
|
|
2107
|
+
}
|
|
2108
|
+
),
|
|
2109
|
+
SIMPLE_COMPARISON_OPERATORS.includes(value.operator) && /* @__PURE__ */ jsx(SimpleEditor, { value, onChange }),
|
|
2110
|
+
value.operator === "threshold" && /* @__PURE__ */ jsx(ThresholdEditor, { value, onChange }),
|
|
2111
|
+
value.operator === "regex_match" && /* @__PURE__ */ jsx(RegexEditor, { value, onChange }),
|
|
2112
|
+
value.operator === "time_window" && /* @__PURE__ */ jsx(TimeWindowEditor, { value, onChange }),
|
|
2113
|
+
value.operator === "boolean_expression" && depth < 1 && /* @__PURE__ */ jsx(BooleanGroupEditor, { value, onChange, depth: depth + 1 }),
|
|
2114
|
+
value.operator === "boolean_expression" && depth >= 1 && /* @__PURE__ */ jsx("p", { className: "text-xs italic text-slate-500", children: "Nested boolean groups are supported by the engine but not in this builder \u2014 edit the JSON directly for deeper trees." })
|
|
2115
|
+
] });
|
|
2116
|
+
}
|
|
2117
|
+
function SimpleEditor({
|
|
2118
|
+
value,
|
|
2119
|
+
onChange
|
|
2120
|
+
}) {
|
|
2121
|
+
const isTruthy = value.operator === "truthy";
|
|
2122
|
+
return /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 gap-3 sm:grid-cols-2", children: [
|
|
2123
|
+
/* @__PURE__ */ jsx(
|
|
2124
|
+
FormInput,
|
|
2125
|
+
{
|
|
2126
|
+
label: "Field",
|
|
2127
|
+
hint: "Dotted reference into the variable pool \u2014 e.g. `parse-pricing.margin`, `inputs.fuelType`",
|
|
2128
|
+
value: value.field ?? "",
|
|
2129
|
+
onValueChange: (field) => onChange({ ...value, field }),
|
|
2130
|
+
placeholder: "node.path"
|
|
2131
|
+
}
|
|
2132
|
+
),
|
|
2133
|
+
!isTruthy && /* @__PURE__ */ jsx(
|
|
2134
|
+
FormInput,
|
|
2135
|
+
{
|
|
2136
|
+
label: "Value",
|
|
2137
|
+
hint: "Literal. Numeric operators coerce via `as f64`.",
|
|
2138
|
+
value: value.value === null || value.value === void 0 ? "" : String(value.value),
|
|
2139
|
+
onValueChange: (raw) => onChange({ ...value, value: coerceScalar(raw) })
|
|
2140
|
+
}
|
|
2141
|
+
)
|
|
2142
|
+
] });
|
|
2143
|
+
}
|
|
2144
|
+
function ThresholdEditor({
|
|
2145
|
+
value,
|
|
2146
|
+
onChange
|
|
2147
|
+
}) {
|
|
2148
|
+
return /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 gap-3 sm:grid-cols-3", children: [
|
|
2149
|
+
/* @__PURE__ */ jsx(
|
|
2150
|
+
FormInput,
|
|
2151
|
+
{
|
|
2152
|
+
label: "Field",
|
|
2153
|
+
value: value.field ?? "",
|
|
2154
|
+
onValueChange: (field) => onChange({ ...value, field }),
|
|
2155
|
+
placeholder: "parse-pricing.margin"
|
|
2156
|
+
}
|
|
2157
|
+
),
|
|
2158
|
+
/* @__PURE__ */ jsx(
|
|
2159
|
+
FormSelect,
|
|
2160
|
+
{
|
|
2161
|
+
label: "Direction",
|
|
2162
|
+
value: value.comparison ?? "gte",
|
|
2163
|
+
options: [
|
|
2164
|
+
{ value: "gt", label: "Greater than (>)" },
|
|
2165
|
+
{ value: "gte", label: "At least (\u2265)" },
|
|
2166
|
+
{ value: "lt", label: "Less than (<)" },
|
|
2167
|
+
{ value: "lte", label: "At most (\u2264)" }
|
|
2168
|
+
],
|
|
2169
|
+
onValueChange: (comparison) => onChange({ ...value, comparison })
|
|
2170
|
+
}
|
|
2171
|
+
),
|
|
2172
|
+
/* @__PURE__ */ jsx(
|
|
2173
|
+
FormInput,
|
|
2174
|
+
{
|
|
2175
|
+
label: "Threshold",
|
|
2176
|
+
type: "number",
|
|
2177
|
+
value: value.value === null || value.value === void 0 ? "" : String(value.value),
|
|
2178
|
+
onValueChange: (raw) => onChange({ ...value, value: Number(raw) }),
|
|
2179
|
+
placeholder: "0.08"
|
|
2180
|
+
}
|
|
2181
|
+
)
|
|
2182
|
+
] });
|
|
2183
|
+
}
|
|
2184
|
+
function RegexEditor({
|
|
2185
|
+
value,
|
|
2186
|
+
onChange
|
|
2187
|
+
}) {
|
|
2188
|
+
return /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 gap-3 sm:grid-cols-2", children: [
|
|
2189
|
+
/* @__PURE__ */ jsx(
|
|
2190
|
+
FormInput,
|
|
2191
|
+
{
|
|
2192
|
+
label: "Field",
|
|
2193
|
+
value: value.field ?? "",
|
|
2194
|
+
onValueChange: (field) => onChange({ ...value, field }),
|
|
2195
|
+
placeholder: "agent.text"
|
|
2196
|
+
}
|
|
2197
|
+
),
|
|
2198
|
+
/* @__PURE__ */ jsx(
|
|
2199
|
+
FormInput,
|
|
2200
|
+
{
|
|
2201
|
+
label: "Pattern",
|
|
2202
|
+
hint: "Rust regex syntax \u2014 `(?i)` etc. supported.",
|
|
2203
|
+
value: value.pattern ?? "",
|
|
2204
|
+
onValueChange: (pattern) => onChange({ ...value, pattern }),
|
|
2205
|
+
placeholder: "diesel-s10"
|
|
2206
|
+
}
|
|
2207
|
+
)
|
|
2208
|
+
] });
|
|
2209
|
+
}
|
|
2210
|
+
function TimeWindowEditor({
|
|
2211
|
+
value,
|
|
2212
|
+
onChange
|
|
2213
|
+
}) {
|
|
2214
|
+
const windows = value.windows ?? [];
|
|
2215
|
+
const update = (index, patch) => {
|
|
2216
|
+
const next = windows.map((win, i) => i === index ? { ...win, ...patch } : win);
|
|
2217
|
+
onChange({ ...value, windows: next });
|
|
2218
|
+
};
|
|
2219
|
+
const remove = (index) => {
|
|
2220
|
+
const next = windows.filter((_, i) => i !== index);
|
|
2221
|
+
onChange({ ...value, windows: next });
|
|
2222
|
+
};
|
|
2223
|
+
const add = () => onChange({ ...value, windows: [...windows, { startHour: 8, endHour: 17 }] });
|
|
2224
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
|
|
2225
|
+
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 gap-3 sm:grid-cols-2", children: [
|
|
2226
|
+
/* @__PURE__ */ jsx(
|
|
2227
|
+
FormSelect,
|
|
2228
|
+
{
|
|
2229
|
+
label: "Timezone",
|
|
2230
|
+
value: value.timezone ?? "UTC",
|
|
2231
|
+
options: TIMEZONE_OPTIONS.map((tz) => ({ value: tz, label: tz })),
|
|
2232
|
+
onValueChange: (timezone) => onChange({ ...value, timezone })
|
|
2233
|
+
}
|
|
2234
|
+
),
|
|
2235
|
+
/* @__PURE__ */ jsx(
|
|
2236
|
+
FormInput,
|
|
2237
|
+
{
|
|
2238
|
+
label: "Timestamp field (optional)",
|
|
2239
|
+
hint: "Pulls a timestamp from the pool. Leave blank to use the run's wall-clock.",
|
|
2240
|
+
value: value.field ?? "",
|
|
2241
|
+
onValueChange: (field) => onChange({ ...value, field }),
|
|
2242
|
+
placeholder: "clock.ts"
|
|
2243
|
+
}
|
|
2244
|
+
)
|
|
2245
|
+
] }),
|
|
2246
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
2247
|
+
windows.map((win, index) => /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-[1fr_1fr_auto] items-end gap-2 rounded-lg border border-slate-200 p-2 dark:border-slate-700", children: [
|
|
2248
|
+
/* @__PURE__ */ jsx(
|
|
2249
|
+
FormInput,
|
|
2250
|
+
{
|
|
2251
|
+
label: `Start (hour)`,
|
|
2252
|
+
type: "number",
|
|
2253
|
+
min: 0,
|
|
2254
|
+
max: 23,
|
|
2255
|
+
value: String(win.startHour),
|
|
2256
|
+
onValueChange: (raw) => update(index, { startHour: clampHour(raw) })
|
|
2257
|
+
}
|
|
2258
|
+
),
|
|
2259
|
+
/* @__PURE__ */ jsx(
|
|
2260
|
+
FormInput,
|
|
2261
|
+
{
|
|
2262
|
+
label: `End (hour)`,
|
|
2263
|
+
type: "number",
|
|
2264
|
+
min: 0,
|
|
2265
|
+
max: 24,
|
|
2266
|
+
value: String(win.endHour),
|
|
2267
|
+
onValueChange: (raw) => update(index, { endHour: clampHour(raw, 24) })
|
|
2268
|
+
}
|
|
2269
|
+
),
|
|
2270
|
+
/* @__PURE__ */ jsx(Button, { type: "button", plain: true, onClick: () => remove(index), children: "Remove" })
|
|
2271
|
+
] }, index)),
|
|
2272
|
+
/* @__PURE__ */ jsx(Button, { type: "button", outline: true, onClick: add, children: "+ Add window" })
|
|
2273
|
+
] })
|
|
2274
|
+
] });
|
|
2275
|
+
}
|
|
2276
|
+
function BooleanGroupEditor({
|
|
2277
|
+
value,
|
|
2278
|
+
onChange,
|
|
2279
|
+
depth
|
|
2280
|
+
}) {
|
|
2281
|
+
const operands = value.operands ?? [];
|
|
2282
|
+
const update = (index, next) => {
|
|
2283
|
+
const arr = operands.map((op, i) => i === index ? next : op);
|
|
2284
|
+
onChange({ ...value, operands: arr });
|
|
2285
|
+
};
|
|
2286
|
+
const remove = (index) => {
|
|
2287
|
+
onChange({ ...value, operands: operands.filter((_, i) => i !== index) });
|
|
2288
|
+
};
|
|
2289
|
+
const add = () => onChange({
|
|
2290
|
+
...value,
|
|
2291
|
+
operands: [...operands, { operator: "gt", field: "", value: 0 }]
|
|
2292
|
+
});
|
|
2293
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
|
|
2294
|
+
/* @__PURE__ */ jsx(
|
|
2295
|
+
FormSelect,
|
|
2296
|
+
{
|
|
2297
|
+
label: "Combinator",
|
|
2298
|
+
value: value.combinator ?? "and",
|
|
2299
|
+
options: [{ value: "and", label: "All of (AND)" }, { value: "or", label: "Any of (OR)" }],
|
|
2300
|
+
onValueChange: (combinator) => onChange({ ...value, combinator })
|
|
2301
|
+
}
|
|
2302
|
+
),
|
|
2303
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
2304
|
+
operands.map((child, index) => /* @__PURE__ */ jsxs("div", { className: "relative rounded-lg border border-dashed border-slate-300 p-2 dark:border-slate-700", children: [
|
|
2305
|
+
/* @__PURE__ */ jsx(
|
|
2306
|
+
RuleConditionBuilder,
|
|
2307
|
+
{
|
|
2308
|
+
value: child,
|
|
2309
|
+
onChange: (next) => update(index, next),
|
|
2310
|
+
depth
|
|
2311
|
+
}
|
|
2312
|
+
),
|
|
2313
|
+
/* @__PURE__ */ jsx("div", { className: "mt-2 text-right", children: /* @__PURE__ */ jsx(Button, { type: "button", plain: true, onClick: () => remove(index), children: "Remove operand" }) })
|
|
2314
|
+
] }, index)),
|
|
2315
|
+
/* @__PURE__ */ jsx(Button, { type: "button", outline: true, onClick: add, children: "+ Add operand" })
|
|
2316
|
+
] })
|
|
2317
|
+
] });
|
|
2318
|
+
}
|
|
2319
|
+
function coerceScalar(raw) {
|
|
2320
|
+
const trimmed = raw.trim();
|
|
2321
|
+
if (trimmed === "true") return true;
|
|
2322
|
+
if (trimmed === "false") return false;
|
|
2323
|
+
if (trimmed !== "" && !Number.isNaN(Number(trimmed))) return Number(trimmed);
|
|
2324
|
+
return raw;
|
|
2325
|
+
}
|
|
2326
|
+
function clampHour(raw, max = 23) {
|
|
2327
|
+
const n = Number(raw);
|
|
2328
|
+
if (!Number.isFinite(n)) return 0;
|
|
2329
|
+
return Math.max(0, Math.min(max, Math.trunc(n)));
|
|
2330
|
+
}
|
|
2331
|
+
function defaultRuleCondition() {
|
|
2332
|
+
return { operator: "truthy", field: "" };
|
|
2333
|
+
}
|
|
2334
|
+
var ACTION_TYPE_OPTIONS = [
|
|
2335
|
+
{ value: "adjust_price", label: "Adjust price (multiplier)" },
|
|
2336
|
+
{ value: "enforce_min_margin", label: "Enforce minimum margin" },
|
|
2337
|
+
{ value: "realign_to_competitor", label: "Realign to competitor" },
|
|
2338
|
+
{ value: "request_manager_approval", label: "Request manager approval" },
|
|
2339
|
+
{ value: "round_to", label: "Round to step" },
|
|
2340
|
+
{ value: "alert", label: "Emit alert" },
|
|
2341
|
+
{ value: "custom", label: "Custom action" }
|
|
2342
|
+
];
|
|
2343
|
+
function RuleActionBuilder({ value, onChange }) {
|
|
2344
|
+
const type = value.type || "adjust_price";
|
|
2345
|
+
const params = value.params ?? {};
|
|
2346
|
+
const setParam = (key, newValue) => {
|
|
2347
|
+
onChange({ ...value, type, params: { ...params, [key]: newValue } });
|
|
2348
|
+
};
|
|
2349
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-3 rounded-xl border border-slate-200 bg-slate-50/60 p-3 dark:border-slate-700 dark:bg-slate-900/40", children: [
|
|
2350
|
+
/* @__PURE__ */ jsx(
|
|
2351
|
+
FormSelect,
|
|
2352
|
+
{
|
|
2353
|
+
label: "Action type",
|
|
2354
|
+
value: type,
|
|
2355
|
+
options: ACTION_TYPE_OPTIONS,
|
|
2356
|
+
onValueChange: (t) => onChange({ type: t, params: {} })
|
|
2357
|
+
}
|
|
2358
|
+
),
|
|
2359
|
+
type === "adjust_price" && /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 gap-3 sm:grid-cols-2", children: [
|
|
2360
|
+
/* @__PURE__ */ jsx(
|
|
2361
|
+
FormInput,
|
|
2362
|
+
{
|
|
2363
|
+
label: "Multiplier",
|
|
2364
|
+
type: "number",
|
|
2365
|
+
step: 0.01,
|
|
2366
|
+
value: numberParam(params.multiplier, 1),
|
|
2367
|
+
onValueChange: (raw) => setParam("multiplier", Number(raw))
|
|
2368
|
+
}
|
|
2369
|
+
),
|
|
2370
|
+
/* @__PURE__ */ jsx(
|
|
2371
|
+
FormInput,
|
|
2372
|
+
{
|
|
2373
|
+
label: "Reason code",
|
|
2374
|
+
value: stringParam(params.reason),
|
|
2375
|
+
onValueChange: (raw) => setParam("reason", raw),
|
|
2376
|
+
placeholder: "peak_demand_window"
|
|
2377
|
+
}
|
|
2378
|
+
)
|
|
2379
|
+
] }),
|
|
2380
|
+
type === "enforce_min_margin" && /* @__PURE__ */ jsx(
|
|
2381
|
+
FormInput,
|
|
2382
|
+
{
|
|
2383
|
+
label: "Floor (fraction, e.g. 0.08)",
|
|
2384
|
+
type: "number",
|
|
2385
|
+
step: 0.01,
|
|
2386
|
+
value: numberParam(params.floor, 0.08),
|
|
2387
|
+
onValueChange: (raw) => setParam("floor", Number(raw))
|
|
2388
|
+
}
|
|
2389
|
+
),
|
|
2390
|
+
type === "realign_to_competitor" && /* @__PURE__ */ jsx(
|
|
2391
|
+
FormInput,
|
|
2392
|
+
{
|
|
2393
|
+
label: "Tolerance (fraction)",
|
|
2394
|
+
type: "number",
|
|
2395
|
+
step: 0.01,
|
|
2396
|
+
value: numberParam(params.tolerance, 0.03),
|
|
2397
|
+
onValueChange: (raw) => setParam("tolerance", Number(raw))
|
|
2398
|
+
}
|
|
2399
|
+
),
|
|
2400
|
+
type === "request_manager_approval" && /* @__PURE__ */ jsx(
|
|
2401
|
+
FormInput,
|
|
2402
|
+
{
|
|
2403
|
+
label: "Approval limit (fraction)",
|
|
2404
|
+
type: "number",
|
|
2405
|
+
step: 0.01,
|
|
2406
|
+
value: numberParam(params.limit, 0.05),
|
|
2407
|
+
onValueChange: (raw) => setParam("limit", Number(raw))
|
|
2408
|
+
}
|
|
2409
|
+
),
|
|
2410
|
+
type === "round_to" && /* @__PURE__ */ jsx(
|
|
2411
|
+
FormInput,
|
|
2412
|
+
{
|
|
2413
|
+
label: "Step (e.g. 0.009)",
|
|
2414
|
+
type: "number",
|
|
2415
|
+
step: 1e-3,
|
|
2416
|
+
value: numberParam(params.step, 9e-3),
|
|
2417
|
+
onValueChange: (raw) => setParam("step", Number(raw))
|
|
2418
|
+
}
|
|
2419
|
+
),
|
|
2420
|
+
type === "alert" && /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 gap-3 sm:grid-cols-2", children: [
|
|
2421
|
+
/* @__PURE__ */ jsx(
|
|
2422
|
+
FormInput,
|
|
2423
|
+
{
|
|
2424
|
+
label: "Channel",
|
|
2425
|
+
value: stringParam(params.channel),
|
|
2426
|
+
onValueChange: (raw) => setParam("channel", raw),
|
|
2427
|
+
placeholder: "slack"
|
|
2428
|
+
}
|
|
2429
|
+
),
|
|
2430
|
+
/* @__PURE__ */ jsx(
|
|
2431
|
+
FormInput,
|
|
2432
|
+
{
|
|
2433
|
+
label: "Severity",
|
|
2434
|
+
value: stringParam(params.severity),
|
|
2435
|
+
onValueChange: (raw) => setParam("severity", raw),
|
|
2436
|
+
placeholder: "warning"
|
|
2437
|
+
}
|
|
2438
|
+
)
|
|
2439
|
+
] }),
|
|
2440
|
+
type === "custom" && /* @__PURE__ */ jsx(CustomParamsEditor, { params, onChange: (p) => onChange({ ...value, type, params: p }) })
|
|
2441
|
+
] });
|
|
2442
|
+
}
|
|
2443
|
+
function CustomParamsEditor({
|
|
2444
|
+
params,
|
|
2445
|
+
onChange
|
|
2446
|
+
}) {
|
|
2447
|
+
const entries = Object.entries(params);
|
|
2448
|
+
const update = (index, key, raw) => {
|
|
2449
|
+
const next = {};
|
|
2450
|
+
entries.forEach(([k, v], i) => {
|
|
2451
|
+
if (i === index) next[key] = coerce(raw);
|
|
2452
|
+
else next[k] = v;
|
|
2453
|
+
});
|
|
2454
|
+
onChange(next);
|
|
2455
|
+
};
|
|
2456
|
+
const remove = (index) => {
|
|
2457
|
+
const next = {};
|
|
2458
|
+
entries.forEach(([k, v], i) => {
|
|
2459
|
+
if (i !== index) next[k] = v;
|
|
2460
|
+
});
|
|
2461
|
+
onChange(next);
|
|
2462
|
+
};
|
|
2463
|
+
const add = () => {
|
|
2464
|
+
onChange({ ...params, [""]: "" });
|
|
2465
|
+
};
|
|
2466
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
2467
|
+
entries.map(([key, value], index) => /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-[1fr_1fr_auto] items-end gap-2", children: [
|
|
2468
|
+
/* @__PURE__ */ jsx(
|
|
2469
|
+
FormInput,
|
|
2470
|
+
{
|
|
2471
|
+
label: "Key",
|
|
2472
|
+
value: key,
|
|
2473
|
+
onValueChange: (raw) => update(index, raw, String(value ?? ""))
|
|
2474
|
+
}
|
|
2475
|
+
),
|
|
2476
|
+
/* @__PURE__ */ jsx(
|
|
2477
|
+
FormInput,
|
|
2478
|
+
{
|
|
2479
|
+
label: "Value",
|
|
2480
|
+
value: String(value ?? ""),
|
|
2481
|
+
onValueChange: (raw) => update(index, key, raw)
|
|
2482
|
+
}
|
|
2483
|
+
),
|
|
2484
|
+
/* @__PURE__ */ jsx(Button, { type: "button", plain: true, onClick: () => remove(index), children: "Remove" })
|
|
2485
|
+
] }, index)),
|
|
2486
|
+
/* @__PURE__ */ jsx(Button, { type: "button", outline: true, onClick: add, children: "+ Add param" })
|
|
2487
|
+
] });
|
|
2488
|
+
}
|
|
2489
|
+
function numberParam(value, fallback) {
|
|
2490
|
+
if (typeof value === "number" && Number.isFinite(value)) return String(value);
|
|
2491
|
+
return String(fallback);
|
|
2492
|
+
}
|
|
2493
|
+
function stringParam(value) {
|
|
2494
|
+
if (typeof value === "string") return value;
|
|
2495
|
+
if (value === null || value === void 0) return "";
|
|
2496
|
+
return String(value);
|
|
2497
|
+
}
|
|
2498
|
+
function coerce(raw) {
|
|
2499
|
+
const trimmed = raw.trim();
|
|
2500
|
+
if (trimmed === "true") return true;
|
|
2501
|
+
if (trimmed === "false") return false;
|
|
2502
|
+
if (trimmed !== "" && !Number.isNaN(Number(trimmed))) return Number(trimmed);
|
|
2503
|
+
return raw;
|
|
2504
|
+
}
|
|
2505
|
+
function defaultRuleAction() {
|
|
2506
|
+
return { type: "adjust_price", params: { multiplier: 1, reason: "" } };
|
|
2507
|
+
}
|
|
2508
|
+
function RuleForm({ value, onChange }) {
|
|
2509
|
+
const [showAdvanced, setShowAdvanced] = useState(
|
|
2510
|
+
Boolean(
|
|
2511
|
+
value.validFrom || value.validUntil || value.tags && value.tags.length > 0 || value.status
|
|
2512
|
+
)
|
|
2513
|
+
);
|
|
2514
|
+
const update = (key, v) => onChange({ ...value, [key]: v });
|
|
2515
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-5", children: [
|
|
2516
|
+
/* @__PURE__ */ jsxs(FormGrid, { children: [
|
|
2517
|
+
/* @__PURE__ */ jsx(
|
|
2518
|
+
FormInput,
|
|
2519
|
+
{
|
|
2520
|
+
label: "Name",
|
|
2521
|
+
required: true,
|
|
2522
|
+
value: value.name,
|
|
2523
|
+
onValueChange: (v) => update("name", v)
|
|
2524
|
+
}
|
|
2525
|
+
),
|
|
2526
|
+
/* @__PURE__ */ jsx(
|
|
2527
|
+
FormInput,
|
|
2528
|
+
{
|
|
2529
|
+
label: "Priority",
|
|
2530
|
+
type: "number",
|
|
2531
|
+
min: 0,
|
|
2532
|
+
value: String(value.priority),
|
|
2533
|
+
onValueChange: (v) => update("priority", Number(v) || 0)
|
|
2534
|
+
}
|
|
2535
|
+
),
|
|
2536
|
+
/* @__PURE__ */ jsx(
|
|
2537
|
+
FormSelect,
|
|
2538
|
+
{
|
|
2539
|
+
label: "Enabled",
|
|
2540
|
+
value: value.enabled ? "true" : "false",
|
|
2541
|
+
options: [
|
|
2542
|
+
{ value: "true", label: "Enabled" },
|
|
2543
|
+
{ value: "false", label: "Disabled" }
|
|
2544
|
+
],
|
|
2545
|
+
onValueChange: (v) => update("enabled", v === "true")
|
|
2546
|
+
}
|
|
2547
|
+
),
|
|
2548
|
+
/* @__PURE__ */ jsx(
|
|
2549
|
+
FormTextarea,
|
|
2550
|
+
{
|
|
2551
|
+
label: "Description",
|
|
2552
|
+
rows: 2,
|
|
2553
|
+
value: value.description ?? "",
|
|
2554
|
+
onValueChange: (v) => update("description", v)
|
|
2555
|
+
}
|
|
2556
|
+
)
|
|
2557
|
+
] }),
|
|
2558
|
+
/* @__PURE__ */ jsxs("section", { children: [
|
|
2559
|
+
/* @__PURE__ */ jsxs("div", { className: "mb-2 flex items-center justify-between", children: [
|
|
2560
|
+
/* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold text-slate-700 dark:text-slate-200", children: "Condition" }),
|
|
2561
|
+
/* @__PURE__ */ jsx(Badge, { color: "indigo", children: value.condition.operator })
|
|
2562
|
+
] }),
|
|
2563
|
+
/* @__PURE__ */ jsx(
|
|
2564
|
+
RuleConditionBuilder,
|
|
2565
|
+
{
|
|
2566
|
+
value: value.condition,
|
|
2567
|
+
onChange: (condition) => update("condition", condition)
|
|
2568
|
+
}
|
|
2569
|
+
)
|
|
2570
|
+
] }),
|
|
2571
|
+
/* @__PURE__ */ jsxs("section", { children: [
|
|
2572
|
+
/* @__PURE__ */ jsxs("div", { className: "mb-2 flex items-center justify-between", children: [
|
|
2573
|
+
/* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold text-slate-700 dark:text-slate-200", children: "Action on match" }),
|
|
2574
|
+
/* @__PURE__ */ jsx(Badge, { color: "fuchsia", children: value.action.type })
|
|
2575
|
+
] }),
|
|
2576
|
+
/* @__PURE__ */ jsx(
|
|
2577
|
+
RuleActionBuilder,
|
|
2578
|
+
{
|
|
2579
|
+
value: value.action,
|
|
2580
|
+
onChange: (action) => update("action", action)
|
|
2581
|
+
}
|
|
2582
|
+
)
|
|
2583
|
+
] }),
|
|
2584
|
+
/* @__PURE__ */ jsxs("section", { children: [
|
|
2585
|
+
/* @__PURE__ */ jsx(
|
|
2586
|
+
"button",
|
|
2587
|
+
{
|
|
2588
|
+
type: "button",
|
|
2589
|
+
onClick: () => setShowAdvanced((s) => !s),
|
|
2590
|
+
className: "text-xs font-medium text-indigo-600 hover:text-indigo-500 dark:text-indigo-400",
|
|
2591
|
+
children: showAdvanced ? "\u2212 Hide schedule & tagging" : "+ Schedule, tagging, lifecycle"
|
|
2592
|
+
}
|
|
2593
|
+
),
|
|
2594
|
+
showAdvanced && /* @__PURE__ */ jsxs("div", { className: "mt-3 grid grid-cols-1 gap-3 sm:grid-cols-2", children: [
|
|
2595
|
+
/* @__PURE__ */ jsx(
|
|
2596
|
+
FormInput,
|
|
2597
|
+
{
|
|
2598
|
+
label: "Valid from (ISO 8601)",
|
|
2599
|
+
type: "datetime-local",
|
|
2600
|
+
value: toDatetimeLocal(value.validFrom),
|
|
2601
|
+
onValueChange: (v) => update("validFrom", fromDatetimeLocal(v))
|
|
2602
|
+
}
|
|
2603
|
+
),
|
|
2604
|
+
/* @__PURE__ */ jsx(
|
|
2605
|
+
FormInput,
|
|
2606
|
+
{
|
|
2607
|
+
label: "Valid until (ISO 8601)",
|
|
2608
|
+
type: "datetime-local",
|
|
2609
|
+
value: toDatetimeLocal(value.validUntil),
|
|
2610
|
+
onValueChange: (v) => update("validUntil", fromDatetimeLocal(v))
|
|
2611
|
+
}
|
|
2612
|
+
),
|
|
2613
|
+
/* @__PURE__ */ jsx(
|
|
2614
|
+
FormSelect,
|
|
2615
|
+
{
|
|
2616
|
+
label: "Status",
|
|
2617
|
+
value: value.status ?? "active",
|
|
2618
|
+
options: RULE_STATUS_OPTIONS.map((s) => ({ value: s, label: s })),
|
|
2619
|
+
onValueChange: (v) => update("status", v)
|
|
2620
|
+
}
|
|
2621
|
+
),
|
|
2622
|
+
/* @__PURE__ */ jsx(
|
|
2623
|
+
FormInput,
|
|
2624
|
+
{
|
|
2625
|
+
label: "Tags (comma-separated)",
|
|
2626
|
+
value: (value.tags ?? []).join(", "),
|
|
2627
|
+
onValueChange: (v) => update("tags", parseTags(v)),
|
|
2628
|
+
placeholder: "pricing, peak-hours"
|
|
2629
|
+
}
|
|
2630
|
+
)
|
|
2631
|
+
] })
|
|
2632
|
+
] })
|
|
2633
|
+
] });
|
|
2634
|
+
}
|
|
2635
|
+
function defaultRuleForm() {
|
|
2636
|
+
return {
|
|
2637
|
+
name: "",
|
|
2638
|
+
description: "",
|
|
2639
|
+
enabled: true,
|
|
2640
|
+
priority: 0,
|
|
2641
|
+
status: "active",
|
|
2642
|
+
validFrom: null,
|
|
2643
|
+
validUntil: null,
|
|
2644
|
+
tags: [],
|
|
2645
|
+
condition: defaultRuleCondition(),
|
|
2646
|
+
action: defaultRuleAction()
|
|
2647
|
+
};
|
|
2648
|
+
}
|
|
2649
|
+
function parseTags(raw) {
|
|
2650
|
+
return raw.split(",").map((tag) => tag.trim()).filter((tag) => tag.length > 0);
|
|
2651
|
+
}
|
|
2652
|
+
function toDatetimeLocal(iso) {
|
|
2653
|
+
if (!iso) return "";
|
|
2654
|
+
try {
|
|
2655
|
+
const d = new Date(iso);
|
|
2656
|
+
if (Number.isNaN(d.getTime())) return "";
|
|
2657
|
+
const pad = (n) => String(n).padStart(2, "0");
|
|
2658
|
+
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}T${pad(d.getHours())}:${pad(d.getMinutes())}`;
|
|
2659
|
+
} catch {
|
|
2660
|
+
return "";
|
|
2661
|
+
}
|
|
2662
|
+
}
|
|
2663
|
+
function fromDatetimeLocal(raw) {
|
|
2664
|
+
if (!raw) return null;
|
|
2665
|
+
const d = new Date(raw);
|
|
2666
|
+
if (Number.isNaN(d.getTime())) return null;
|
|
2667
|
+
return d.toISOString();
|
|
2668
|
+
}
|
|
2669
|
+
var DATASOURCE_LOGOS = {
|
|
2670
|
+
bigquery: "/logos/datasources/bigquery.svg",
|
|
2671
|
+
postgres: "/logos/datasources/postgres.svg",
|
|
2672
|
+
snowflake: "/logos/datasources/snowflake.svg",
|
|
2673
|
+
mongodb: "/logos/datasources/mongodb.svg",
|
|
2674
|
+
redis: "/logos/datasources/redis.svg",
|
|
2675
|
+
mysql: "/logos/datasources/mysql.svg",
|
|
2676
|
+
clickhouse: "/logos/datasources/clickhouse.svg",
|
|
2677
|
+
elasticsearch: "/logos/datasources/elasticsearch.svg",
|
|
2678
|
+
duckdb: "/logos/datasources/duckdb.svg",
|
|
2679
|
+
sqlite: "/logos/datasources/sqlite.svg",
|
|
2680
|
+
mariadb: "/logos/datasources/mariadb.svg",
|
|
2681
|
+
oracle: "/logos/datasources/oracle.svg",
|
|
2682
|
+
mssql: "/logos/datasources/mssql.svg",
|
|
2683
|
+
sqlserver: "/logos/datasources/mssql.svg",
|
|
2684
|
+
cassandra: "/logos/datasources/cassandra.svg",
|
|
2685
|
+
dynamodb: "/logos/datasources/dynamodb.svg",
|
|
2686
|
+
cockroach: "/logos/datasources/cockroachdb.svg",
|
|
2687
|
+
supabase: "/logos/datasources/supabase.svg",
|
|
2688
|
+
firebase: "/logos/datasources/firebase.svg",
|
|
2689
|
+
neo4j: "/logos/datasources/neo4j.svg"
|
|
2690
|
+
};
|
|
2691
|
+
function getDatasourceLogo(entityId) {
|
|
2692
|
+
const lower = entityId.toLowerCase();
|
|
2693
|
+
for (const [key, url] of Object.entries(DATASOURCE_LOGOS)) {
|
|
2694
|
+
if (lower.includes(key)) return url;
|
|
2695
|
+
}
|
|
2696
|
+
return null;
|
|
2697
|
+
}
|
|
2698
|
+
var PROVIDER_LOGOS2 = {
|
|
2699
|
+
anthropic: "/logos/providers/anthropic.svg",
|
|
2700
|
+
amazon: "/logos/providers/aws.svg",
|
|
2701
|
+
google: "/logos/providers/google-gemini.svg",
|
|
2702
|
+
openai: "/logos/providers/openai.svg",
|
|
2703
|
+
meta: "/logos/providers/meta.svg"
|
|
2704
|
+
};
|
|
2705
|
+
function onDragStart(event, nodeType, entityId, label, config) {
|
|
2706
|
+
event.dataTransfer.setData("nodeType", nodeType);
|
|
2707
|
+
event.dataTransfer.setData("entityId", entityId);
|
|
2708
|
+
event.dataTransfer.setData("label", label);
|
|
2709
|
+
if (config) {
|
|
2710
|
+
event.dataTransfer.setData("config", config);
|
|
2711
|
+
}
|
|
2712
|
+
event.dataTransfer.effectAllowed = "move";
|
|
2713
|
+
}
|
|
2714
|
+
var LOGIC_NODE_ITEMS = [
|
|
2715
|
+
// Control Flow
|
|
2716
|
+
{ nodeType: "start", nameKey: "startNode", descriptionKey: "startNodeDescription", subcategory: "control_flow" },
|
|
2717
|
+
{ nodeType: "end", nameKey: "endNode", descriptionKey: "endNodeDescription", subcategory: "control_flow" },
|
|
2718
|
+
{ nodeType: "if_else", nameKey: "ifElseNode", descriptionKey: "ifElseNodeDescription", subcategory: "control_flow" },
|
|
2719
|
+
{ nodeType: "iteration", nameKey: "iterationNode", descriptionKey: "iterationNodeDescription", subcategory: "control_flow" },
|
|
2720
|
+
{ nodeType: "iteration_start", nameKey: "iterationStartNode", descriptionKey: "iterationStartNodeDescription", subcategory: "control_flow" },
|
|
2721
|
+
{ nodeType: "group", nameKey: "groupNode", descriptionKey: "groupNodeDescription", subcategory: "control_flow" },
|
|
2722
|
+
// Data Processing
|
|
2723
|
+
{ nodeType: "code", nameKey: "codeNode", descriptionKey: "codeNodeDescription", subcategory: "data_processing" },
|
|
2724
|
+
{ nodeType: "http_request", nameKey: "httpRequestNode", descriptionKey: "httpRequestNodeDescription", subcategory: "data_processing" },
|
|
2725
|
+
{ nodeType: "template_transform", nameKey: "templateTransformNode", descriptionKey: "templateTransformNodeDescription", subcategory: "data_processing" },
|
|
2726
|
+
{ nodeType: "variable_assigner", nameKey: "variableAssignerNode", descriptionKey: "variableAssignerNodeDescription", subcategory: "data_processing" },
|
|
2727
|
+
{ nodeType: "variable_aggregator", nameKey: "variableAggregatorNode", descriptionKey: "variableAggregatorNodeDescription", subcategory: "data_processing" },
|
|
2728
|
+
{ nodeType: "list_operator", nameKey: "listOperatorNode", descriptionKey: "listOperatorNodeDescription", subcategory: "data_processing" },
|
|
2729
|
+
// DocumentExtractor echoes its config today — pending a real
|
|
2730
|
+
// extractor implementation, tag as experimental.
|
|
2731
|
+
{ nodeType: "document_extractor", nameKey: "documentExtractorNode", descriptionKey: "documentExtractorNodeDescription", subcategory: "data_processing", experimental: true },
|
|
2732
|
+
{ nodeType: "datasource", nameKey: "datasourceNode", descriptionKey: "datasourceNodeDescription", subcategory: "data_processing" },
|
|
2733
|
+
// AI/ML
|
|
2734
|
+
// KnowledgeBase is a stub (no vector store wired yet) — hide by default.
|
|
2735
|
+
{ nodeType: "knowledge_base", nameKey: "knowledgeBaseNode", descriptionKey: "knowledgeBaseNodeDescription", subcategory: "ai_ml", experimental: true },
|
|
2736
|
+
{ nodeType: "answer", nameKey: "answerNode", descriptionKey: "answerNodeDescription", subcategory: "ai_ml" },
|
|
2737
|
+
{ nodeType: "question_classifier", nameKey: "questionClassifierNode", descriptionKey: "questionClassifierNodeDescription", subcategory: "ai_ml" },
|
|
2738
|
+
{ nodeType: "parameter_extractor", nameKey: "parameterExtractorNode", descriptionKey: "parameterExtractorNodeDescription", subcategory: "ai_ml" },
|
|
2739
|
+
// Annotation
|
|
2740
|
+
{ nodeType: "note", nameKey: "noteNode", descriptionKey: "noteNodeDescription", subcategory: "annotation" },
|
|
2741
|
+
// Output
|
|
2742
|
+
{ nodeType: "dashboard_output", nameKey: "dashboardOutputNode", descriptionKey: "dashboardOutputNodeDescription", subcategory: "output" }
|
|
2743
|
+
];
|
|
2744
|
+
var SUBCATEGORY_LABELS = {
|
|
2745
|
+
control_flow: "controlFlowSection",
|
|
2746
|
+
data_processing: "dataProcessingSection",
|
|
2747
|
+
ai_ml: "aiMlSection",
|
|
2748
|
+
annotation: "annotationSection",
|
|
2749
|
+
output: "outputSection"
|
|
2750
|
+
};
|
|
2751
|
+
function SectionHeader({ icon, title, colorClass, count, onAdd, addLabel, isOpen, onToggle }) {
|
|
2752
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between px-4 py-2.5", children: [
|
|
2753
|
+
/* @__PURE__ */ jsxs(
|
|
2754
|
+
"button",
|
|
2755
|
+
{
|
|
2756
|
+
type: "button",
|
|
2757
|
+
onClick: onToggle,
|
|
2758
|
+
className: "flex flex-1 items-center gap-1.5 transition-colors",
|
|
2759
|
+
children: [
|
|
2760
|
+
/* @__PURE__ */ jsxs("span", { className: `flex items-center gap-1.5 text-[10px] font-semibold uppercase tracking-wider ${colorClass}`, children: [
|
|
2761
|
+
icon,
|
|
2762
|
+
title,
|
|
2763
|
+
count !== void 0 && /* @__PURE__ */ jsx("span", { className: "ml-1 rounded-full bg-gray-100 px-1.5 py-px text-[9px] font-medium text-gray-500 dark:bg-white/10 dark:text-gray-400", children: count })
|
|
2764
|
+
] }),
|
|
2765
|
+
/* @__PURE__ */ jsx(
|
|
2766
|
+
ChevronDownIcon,
|
|
2767
|
+
{
|
|
2768
|
+
className: `h-3 w-3 text-gray-400 transition-transform duration-200 ${isOpen ? "" : "-rotate-90"}`
|
|
2769
|
+
}
|
|
2770
|
+
)
|
|
2771
|
+
]
|
|
2772
|
+
}
|
|
2773
|
+
),
|
|
2774
|
+
onAdd ? /* @__PURE__ */ jsx(
|
|
2775
|
+
"button",
|
|
2776
|
+
{
|
|
2777
|
+
type: "button",
|
|
2778
|
+
onClick: onAdd,
|
|
2779
|
+
title: addLabel,
|
|
2780
|
+
className: `rounded-md p-1 transition-colors hover:bg-gray-100 dark:hover:bg-white/10 ${colorClass}`,
|
|
2781
|
+
children: /* @__PURE__ */ jsx(PlusIcon, { className: "h-3.5 w-3.5" })
|
|
2782
|
+
}
|
|
2783
|
+
) : null
|
|
2784
|
+
] });
|
|
2785
|
+
}
|
|
2786
|
+
function CollapsibleSection({ title, icon, colorClass, defaultOpen = true, count, onAdd, addLabel, children }) {
|
|
2787
|
+
const [isOpen, setIsOpen] = useState(defaultOpen);
|
|
2788
|
+
const safeChildren = Children.toArray(children);
|
|
2789
|
+
return /* @__PURE__ */ jsxs("div", { className: "border-t border-gray-200/40 dark:border-white/5", children: [
|
|
2790
|
+
/* @__PURE__ */ jsx(
|
|
2791
|
+
SectionHeader,
|
|
2792
|
+
{
|
|
2793
|
+
icon,
|
|
2794
|
+
title,
|
|
2795
|
+
colorClass,
|
|
2796
|
+
count,
|
|
2797
|
+
onAdd,
|
|
2798
|
+
addLabel,
|
|
2799
|
+
isOpen,
|
|
2800
|
+
onToggle: () => setIsOpen((previous) => !previous)
|
|
2801
|
+
}
|
|
2802
|
+
),
|
|
2803
|
+
isOpen ? /* @__PURE__ */ jsx("div", { className: "space-y-1.5 px-4 pb-3", children: safeChildren }) : null
|
|
2804
|
+
] });
|
|
2805
|
+
}
|
|
2806
|
+
function LogicNodeItemCard({ item, translationFunction }) {
|
|
2807
|
+
const IconComponent = LOGIC_ICON_MAP[item.nodeType];
|
|
2808
|
+
const gradient = LOGIC_NODE_GRADIENTS[item.nodeType] ?? "from-gray-400 to-gray-500";
|
|
2809
|
+
const defaultConfig = createDefaultLogicNodeConfig(item.nodeType);
|
|
2810
|
+
const configJson = defaultConfig ? JSON.stringify(defaultConfig) : void 0;
|
|
2811
|
+
return /* @__PURE__ */ jsxs(
|
|
2812
|
+
"div",
|
|
2813
|
+
{
|
|
2814
|
+
draggable: true,
|
|
2815
|
+
onDragStart: (event) => onDragStart(event, item.nodeType, item.nodeType, translationFunction(item.nameKey), configJson),
|
|
2816
|
+
className: "liquid-surface group flex items-center gap-2 rounded-lg px-3 py-2 transition-all cursor-grab hover:shadow-md active:cursor-grabbing",
|
|
2817
|
+
children: [
|
|
2818
|
+
/* @__PURE__ */ jsx("div", { className: `flex h-6 w-6 items-center justify-center rounded-md bg-gradient-to-br ${gradient} shadow-sm`, children: IconComponent && /* @__PURE__ */ jsx(IconComponent, { className: "h-3.5 w-3.5 text-white" }) }),
|
|
2819
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
2820
|
+
/* @__PURE__ */ jsx("p", { className: "truncate text-xs font-medium text-gray-900 dark:text-white", children: translationFunction(item.nameKey) }),
|
|
2821
|
+
/* @__PURE__ */ jsx("p", { className: "truncate text-[10px] text-gray-400 dark:text-gray-500", children: translationFunction(item.descriptionKey) })
|
|
2822
|
+
] })
|
|
2823
|
+
]
|
|
2824
|
+
}
|
|
2825
|
+
);
|
|
2826
|
+
}
|
|
2827
|
+
function NodePalette({ agents, tools, agentTools = [], rules, entities = [], modelProviders = [], onCreateAgent, onCreateTool, onCreateAgentTool, onCreateRule, onCreateDatasource, onConfigureProvider }) {
|
|
2828
|
+
const t = useTranslations("agents.workflow");
|
|
2829
|
+
const [searchQuery, setSearchQuery] = useState("");
|
|
2830
|
+
const [showExperimental, setShowExperimental] = useState(false);
|
|
2831
|
+
const normalizedQuery = searchQuery.toLowerCase().trim();
|
|
2832
|
+
const filteredAgents = useMemo(
|
|
2833
|
+
() => normalizedQuery ? agents.filter(
|
|
2834
|
+
(agent) => agent.name.toLowerCase().includes(normalizedQuery) || (agent.role ?? "").toLowerCase().includes(normalizedQuery)
|
|
2835
|
+
) : agents,
|
|
2836
|
+
[agents, normalizedQuery]
|
|
2837
|
+
);
|
|
2838
|
+
const filteredTools = useMemo(
|
|
2839
|
+
() => normalizedQuery ? tools.filter(
|
|
2840
|
+
(tool) => tool.name.toLowerCase().includes(normalizedQuery) || (tool.category ?? "").toLowerCase().includes(normalizedQuery)
|
|
2841
|
+
) : tools,
|
|
2842
|
+
[tools, normalizedQuery]
|
|
2843
|
+
);
|
|
2844
|
+
const filteredAgentTools = useMemo(
|
|
2845
|
+
() => normalizedQuery ? agentTools.filter(
|
|
2846
|
+
(agentTool) => agentTool.name.toLowerCase().includes(normalizedQuery) || (agentTool.category ?? "").toLowerCase().includes(normalizedQuery)
|
|
2847
|
+
) : agentTools,
|
|
2848
|
+
[agentTools, normalizedQuery]
|
|
2849
|
+
);
|
|
2850
|
+
const filteredRules = useMemo(
|
|
2851
|
+
() => normalizedQuery ? rules.filter(
|
|
2852
|
+
(rule) => rule.name.toLowerCase().includes(normalizedQuery)
|
|
2853
|
+
) : rules,
|
|
2854
|
+
[rules, normalizedQuery]
|
|
2855
|
+
);
|
|
2856
|
+
const filteredProviders = useMemo(
|
|
2857
|
+
() => normalizedQuery ? modelProviders.filter(
|
|
2858
|
+
(provider) => provider.name.toLowerCase().includes(normalizedQuery) || provider.provider.toLowerCase().includes(normalizedQuery)
|
|
2859
|
+
) : modelProviders,
|
|
2860
|
+
[modelProviders, normalizedQuery]
|
|
2861
|
+
);
|
|
2862
|
+
const entityTypes = useMemo(
|
|
2863
|
+
() => entities.map((entity) => ({
|
|
2864
|
+
id: entity.id,
|
|
2865
|
+
label: entity.label,
|
|
2866
|
+
description: entity.description,
|
|
2867
|
+
fieldCount: entity.fields.length,
|
|
2868
|
+
defaultLimit: entity.defaultLimit,
|
|
2869
|
+
fields: entity.fields
|
|
2870
|
+
})),
|
|
2871
|
+
[entities]
|
|
2872
|
+
);
|
|
2873
|
+
const filteredEntityTypes = useMemo(
|
|
2874
|
+
() => normalizedQuery ? entityTypes.filter(
|
|
2875
|
+
(entity) => entity.label.toLowerCase().includes(normalizedQuery) || entity.description.toLowerCase().includes(normalizedQuery) || entity.id.toLowerCase().includes(normalizedQuery)
|
|
2876
|
+
) : entityTypes,
|
|
2877
|
+
[entityTypes, normalizedQuery]
|
|
2878
|
+
);
|
|
2879
|
+
const filteredLogicItems = useMemo(() => {
|
|
2880
|
+
const visibleByDefault = showExperimental ? LOGIC_NODE_ITEMS : LOGIC_NODE_ITEMS.filter((item) => !item.experimental);
|
|
2881
|
+
if (!normalizedQuery) return visibleByDefault;
|
|
2882
|
+
return visibleByDefault.filter(
|
|
2883
|
+
(item) => t(item.nameKey).toLowerCase().includes(normalizedQuery) || t(item.descriptionKey).toLowerCase().includes(normalizedQuery)
|
|
2884
|
+
);
|
|
2885
|
+
}, [normalizedQuery, showExperimental, t]);
|
|
2886
|
+
const groupedLogicItems = useMemo(() => {
|
|
2887
|
+
const groups = {
|
|
2888
|
+
control_flow: [],
|
|
2889
|
+
data_processing: [],
|
|
2890
|
+
ai_ml: [],
|
|
2891
|
+
annotation: [],
|
|
2892
|
+
output: []
|
|
2893
|
+
};
|
|
2894
|
+
for (const item of filteredLogicItems) {
|
|
2895
|
+
groups[item.subcategory].push(item);
|
|
2896
|
+
}
|
|
2897
|
+
return groups;
|
|
2898
|
+
}, [filteredLogicItems]);
|
|
2899
|
+
function renderLogicSubcategory(subcategory, items, isFirst) {
|
|
2900
|
+
if (items.length === 0) return null;
|
|
2901
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
|
|
2902
|
+
/* @__PURE__ */ jsx("p", { className: `${isFirst ? "mt-1" : "mt-2"} text-[9px] font-semibold uppercase tracking-wider text-gray-400 dark:text-gray-500`, children: t(SUBCATEGORY_LABELS[subcategory]) }),
|
|
2903
|
+
items.map((item) => /* @__PURE__ */ jsx(LogicNodeItemCard, { item, translationFunction: t }, item.nodeType))
|
|
2904
|
+
] }, subcategory);
|
|
2905
|
+
}
|
|
2906
|
+
return /* @__PURE__ */ jsxs("div", { className: "liquid-surface w-[260px] flex-shrink-0 overflow-y-auto rounded-none border-0 border-r border-gray-200/30 dark:border-white/5", children: [
|
|
2907
|
+
/* @__PURE__ */ jsxs("div", { className: "p-4 pb-2", children: [
|
|
2908
|
+
/* @__PURE__ */ jsx("h3", { className: "text-xs font-semibold uppercase tracking-wider text-gray-500 dark:text-gray-400", children: t("palette") }),
|
|
2909
|
+
/* @__PURE__ */ jsx("div", { className: "mt-2", children: /* @__PURE__ */ jsx(
|
|
2910
|
+
"input",
|
|
2911
|
+
{
|
|
2912
|
+
type: "text",
|
|
2913
|
+
value: searchQuery,
|
|
2914
|
+
onChange: (event) => setSearchQuery(event.target.value),
|
|
2915
|
+
placeholder: t("searchPalette"),
|
|
2916
|
+
className: "liquid-surface w-full rounded-lg px-2.5 py-1.5 text-[11px] text-gray-900 placeholder-gray-400 outline-none transition-colors focus:ring-1 focus:ring-indigo-300/40 dark:text-white dark:placeholder-gray-500 dark:focus:ring-indigo-500/20"
|
|
2917
|
+
}
|
|
2918
|
+
) }),
|
|
2919
|
+
/* @__PURE__ */ jsxs("label", { className: "mt-2 flex items-center gap-2 text-[10px] text-gray-500 dark:text-gray-400 select-none cursor-pointer", children: [
|
|
2920
|
+
/* @__PURE__ */ jsx(
|
|
2921
|
+
"input",
|
|
2922
|
+
{
|
|
2923
|
+
type: "checkbox",
|
|
2924
|
+
checked: showExperimental,
|
|
2925
|
+
onChange: (event) => setShowExperimental(event.target.checked),
|
|
2926
|
+
className: "h-3 w-3 rounded border-gray-300 text-indigo-600 focus:ring-1 focus:ring-indigo-400/40 dark:border-gray-600"
|
|
2927
|
+
}
|
|
2928
|
+
),
|
|
2929
|
+
t("showExperimentalNodes", { _: "Show experimental" })
|
|
2930
|
+
] })
|
|
2931
|
+
] }),
|
|
2932
|
+
/* @__PURE__ */ jsx(
|
|
2933
|
+
CollapsibleSection,
|
|
2934
|
+
{
|
|
2935
|
+
title: t("agentsSection"),
|
|
2936
|
+
icon: /* @__PURE__ */ jsx(CpuChipIcon, { className: "h-3 w-3" }),
|
|
2937
|
+
colorClass: "text-indigo-600 dark:text-indigo-400",
|
|
2938
|
+
onAdd: onCreateAgent,
|
|
2939
|
+
addLabel: t("newAgent"),
|
|
2940
|
+
count: filteredAgents.length,
|
|
2941
|
+
children: filteredAgents.length === 0 && filteredAgentTools.length === 0 ? /* @__PURE__ */ jsx("p", { className: "text-[10px] text-gray-400 dark:text-gray-500", children: t("noAgents") }) : /* @__PURE__ */ jsx(Fragment, { children: filteredAgents.map((agent) => /* @__PURE__ */ jsxs(
|
|
2942
|
+
"div",
|
|
2943
|
+
{
|
|
2944
|
+
draggable: true,
|
|
2945
|
+
onDragStart: (event) => onDragStart(event, "agent", agent.agentId, agent.name),
|
|
2946
|
+
className: "liquid-surface group flex items-center gap-2 rounded-lg px-3 py-2 transition-all cursor-grab hover:shadow-md active:cursor-grabbing",
|
|
2947
|
+
children: [
|
|
2948
|
+
/* @__PURE__ */ jsxs("div", { className: "relative flex-shrink-0", children: [
|
|
2949
|
+
agent.avatar ? /* @__PURE__ */ jsx("img", { src: agent.avatar, alt: agent.name, className: "h-7 w-7 rounded-full bg-white shadow-sm ring-1 ring-gray-200/50 dark:bg-gray-800 dark:ring-white/10" }) : /* @__PURE__ */ jsx("div", { className: "flex h-7 w-7 items-center justify-center rounded-full bg-gradient-to-br from-indigo-400 to-purple-500 shadow-sm", children: /* @__PURE__ */ jsx(CpuChipIcon, { className: "h-3.5 w-3.5 text-white" }) }),
|
|
2950
|
+
/* @__PURE__ */ jsx("div", { className: "absolute -bottom-0.5 -right-0.5 flex h-3.5 w-3.5 items-center justify-center rounded-full bg-indigo-500 ring-1.5 ring-white dark:ring-gray-900", children: /* @__PURE__ */ jsx(CpuChipIcon, { className: "h-2 w-2 text-white" }) })
|
|
2951
|
+
] }),
|
|
2952
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
2953
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5", children: [
|
|
2954
|
+
/* @__PURE__ */ jsx("p", { className: "truncate text-xs font-medium text-gray-900 dark:text-white", children: agent.name }),
|
|
2955
|
+
(() => {
|
|
2956
|
+
const tier = getAgentTier(Number(agent.elo ?? 0));
|
|
2957
|
+
return /* @__PURE__ */ jsx("span", { className: `shrink-0 rounded-full px-1.5 py-px text-[8px] font-bold ${tier.pillColor}`, children: t(`agentDrawer.tier${tier.key.charAt(0).toUpperCase()}${tier.key.slice(1)}`) });
|
|
2958
|
+
})()
|
|
2959
|
+
] }),
|
|
2960
|
+
/* @__PURE__ */ jsx("p", { className: "truncate text-[10px] text-gray-400 dark:text-gray-500", children: agent.role ?? agent.agentId })
|
|
2961
|
+
] })
|
|
2962
|
+
]
|
|
2963
|
+
},
|
|
2964
|
+
agent.agentId
|
|
2965
|
+
)) })
|
|
2966
|
+
}
|
|
2967
|
+
),
|
|
2968
|
+
/* @__PURE__ */ jsx(
|
|
2969
|
+
CollapsibleSection,
|
|
2970
|
+
{
|
|
2971
|
+
title: t("subworkflowsSection"),
|
|
2972
|
+
icon: /* @__PURE__ */ jsx(ArrowPathRoundedSquareIcon, { className: "h-3 w-3" }),
|
|
2973
|
+
colorClass: "text-teal-600 dark:text-teal-400",
|
|
2974
|
+
onAdd: onCreateTool,
|
|
2975
|
+
addLabel: t("newSubworkflow"),
|
|
2976
|
+
children: filteredTools.length === 0 ? /* @__PURE__ */ jsx("p", { className: "text-[10px] text-gray-400 dark:text-gray-500", children: t("noSubworkflows") }) : filteredTools.map((tool) => /* @__PURE__ */ jsxs(
|
|
2977
|
+
"div",
|
|
2978
|
+
{
|
|
2979
|
+
draggable: true,
|
|
2980
|
+
onDragStart: (event) => onDragStart(event, "tool", tool.toolId, tool.name),
|
|
2981
|
+
className: "liquid-surface group flex items-center gap-2 rounded-lg px-3 py-2 transition-all cursor-grab hover:shadow-md active:cursor-grabbing",
|
|
2982
|
+
children: [
|
|
2983
|
+
/* @__PURE__ */ jsx("div", { className: "flex h-6 w-6 items-center justify-center rounded-md bg-gradient-to-br from-teal-400 to-emerald-500 shadow-sm", children: /* @__PURE__ */ jsx(ArrowPathRoundedSquareIcon, { className: "h-3.5 w-3.5 text-white" }) }),
|
|
2984
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
2985
|
+
/* @__PURE__ */ jsx("p", { className: "truncate text-xs font-medium text-gray-900 dark:text-white", children: tool.name }),
|
|
2986
|
+
/* @__PURE__ */ jsx("p", { className: "truncate text-[10px] text-gray-400 dark:text-gray-500", children: tool.category })
|
|
2987
|
+
] }),
|
|
2988
|
+
/* @__PURE__ */ jsx(
|
|
2989
|
+
ToggleSwitch,
|
|
2990
|
+
{
|
|
2991
|
+
checked: Boolean(tool.enabled),
|
|
2992
|
+
onChange: () => {
|
|
2993
|
+
},
|
|
2994
|
+
disabled: true,
|
|
2995
|
+
size: "sm",
|
|
2996
|
+
color: "blue",
|
|
2997
|
+
label: `${tool.name} enabled`
|
|
2998
|
+
}
|
|
2999
|
+
)
|
|
3000
|
+
]
|
|
3001
|
+
},
|
|
3002
|
+
tool.toolId
|
|
3003
|
+
))
|
|
3004
|
+
}
|
|
3005
|
+
),
|
|
3006
|
+
/* @__PURE__ */ jsx(
|
|
3007
|
+
CollapsibleSection,
|
|
3008
|
+
{
|
|
3009
|
+
title: t("rulesSection"),
|
|
3010
|
+
icon: /* @__PURE__ */ jsx(AdjustmentsHorizontalIcon, { className: "h-3 w-3" }),
|
|
3011
|
+
colorClass: "text-violet-600 dark:text-violet-400",
|
|
3012
|
+
onAdd: onCreateRule,
|
|
3013
|
+
addLabel: t("newRule"),
|
|
3014
|
+
children: filteredRules.length === 0 ? /* @__PURE__ */ jsx("p", { className: "text-[10px] text-gray-400 dark:text-gray-500", children: t("noRules") }) : filteredRules.map((rule) => /* @__PURE__ */ jsxs(
|
|
3015
|
+
"div",
|
|
3016
|
+
{
|
|
3017
|
+
draggable: true,
|
|
3018
|
+
onDragStart: (event) => onDragStart(event, "rule", rule.ruleId, rule.name),
|
|
3019
|
+
className: "liquid-surface group flex items-center gap-2 rounded-lg px-3 py-2 transition-all cursor-grab hover:shadow-md active:cursor-grabbing",
|
|
3020
|
+
children: [
|
|
3021
|
+
/* @__PURE__ */ jsx("div", { className: "flex h-6 w-6 items-center justify-center rounded-md bg-gradient-to-br from-violet-400 to-purple-500 shadow-sm", children: /* @__PURE__ */ jsx(AdjustmentsHorizontalIcon, { className: "h-3.5 w-3.5 text-white" }) }),
|
|
3022
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
3023
|
+
/* @__PURE__ */ jsx("p", { className: "truncate text-xs font-medium text-gray-900 dark:text-white", children: rule.name }),
|
|
3024
|
+
/* @__PURE__ */ jsxs("p", { className: "truncate text-[10px] text-gray-400 dark:text-gray-500", children: [
|
|
3025
|
+
"P",
|
|
3026
|
+
String(rule.priority ?? "")
|
|
3027
|
+
] })
|
|
3028
|
+
] }),
|
|
3029
|
+
/* @__PURE__ */ jsx(
|
|
3030
|
+
ToggleSwitch,
|
|
3031
|
+
{
|
|
3032
|
+
checked: Boolean(rule.enabled),
|
|
3033
|
+
onChange: () => {
|
|
3034
|
+
},
|
|
3035
|
+
disabled: true,
|
|
3036
|
+
size: "sm",
|
|
3037
|
+
color: "violet",
|
|
3038
|
+
label: `${rule.name} enabled`
|
|
3039
|
+
}
|
|
3040
|
+
)
|
|
3041
|
+
]
|
|
3042
|
+
},
|
|
3043
|
+
rule.ruleId
|
|
3044
|
+
))
|
|
3045
|
+
}
|
|
3046
|
+
),
|
|
3047
|
+
/* @__PURE__ */ jsx(
|
|
3048
|
+
CollapsibleSection,
|
|
3049
|
+
{
|
|
3050
|
+
title: t("dataSourcesSection"),
|
|
3051
|
+
icon: /* @__PURE__ */ jsx(CircleStackIcon, { className: "h-3 w-3" }),
|
|
3052
|
+
colorClass: "text-blue-600 dark:text-blue-400",
|
|
3053
|
+
count: filteredEntityTypes.length,
|
|
3054
|
+
onAdd: onCreateDatasource,
|
|
3055
|
+
addLabel: t("newDataSource"),
|
|
3056
|
+
children: filteredEntityTypes.length === 0 ? /* @__PURE__ */ jsx("p", { className: "text-[10px] text-gray-400 dark:text-gray-500", children: t("noDataSources") }) : filteredEntityTypes.map((entity) => {
|
|
3057
|
+
const defaultConfig = JSON.stringify({
|
|
3058
|
+
type: "entity",
|
|
3059
|
+
entityMasterId: entity.id,
|
|
3060
|
+
selectedFields: entity.fields.map((field) => field.name),
|
|
3061
|
+
filterVariables: {},
|
|
3062
|
+
outputVariable: `${entity.label.replace(/\s+/g, "")}Data`,
|
|
3063
|
+
limit: entity.defaultLimit
|
|
3064
|
+
});
|
|
3065
|
+
const dsLogo = getDatasourceLogo(entity.id);
|
|
3066
|
+
const EntityIcon = getEntityIcon(entity.id);
|
|
3067
|
+
const entityGradient = getEntityGradient(entity.id);
|
|
3068
|
+
return /* @__PURE__ */ jsxs(
|
|
3069
|
+
"div",
|
|
3070
|
+
{
|
|
3071
|
+
draggable: true,
|
|
3072
|
+
onDragStart: (event) => onDragStart(event, "entity", entity.id, entity.label, defaultConfig),
|
|
3073
|
+
className: "liquid-surface group flex items-center gap-2 rounded-lg px-3 py-2 transition-all cursor-grab hover:shadow-md active:cursor-grabbing",
|
|
3074
|
+
children: [
|
|
3075
|
+
dsLogo ? /* @__PURE__ */ jsx("div", { className: "flex h-6 w-6 shrink-0 items-center justify-center rounded-md bg-white/10 p-1", children: /* @__PURE__ */ jsx("img", { src: dsLogo, alt: "", className: "h-4 w-4 object-contain", loading: "lazy" }) }) : /* @__PURE__ */ jsx("div", { className: `flex h-6 w-6 items-center justify-center rounded-md bg-gradient-to-br ${entityGradient} shadow-sm`, children: /* @__PURE__ */ jsx(EntityIcon, { className: "h-3.5 w-3.5 text-white" }) }),
|
|
3076
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
3077
|
+
/* @__PURE__ */ jsx("p", { className: "truncate text-xs font-medium text-gray-900 dark:text-white", children: entity.label }),
|
|
3078
|
+
/* @__PURE__ */ jsxs("p", { className: "truncate text-[10px] text-gray-400 dark:text-gray-500", children: [
|
|
3079
|
+
entity.fieldCount,
|
|
3080
|
+
" ",
|
|
3081
|
+
t("entityFieldsLabel")
|
|
3082
|
+
] })
|
|
3083
|
+
] }),
|
|
3084
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
3085
|
+
/* @__PURE__ */ jsx("span", { className: "rounded-full bg-slate-100 px-1.5 py-0.5 text-[9px] font-medium text-slate-600 dark:bg-white/10 dark:text-slate-300", children: "Read only" }),
|
|
3086
|
+
/* @__PURE__ */ jsx(
|
|
3087
|
+
ToggleSwitch,
|
|
3088
|
+
{
|
|
3089
|
+
checked: true,
|
|
3090
|
+
onChange: () => {
|
|
3091
|
+
},
|
|
3092
|
+
disabled: true,
|
|
3093
|
+
size: "sm",
|
|
3094
|
+
color: "teal",
|
|
3095
|
+
label: `${entity.label} read only`
|
|
3096
|
+
}
|
|
3097
|
+
)
|
|
3098
|
+
] })
|
|
3099
|
+
]
|
|
3100
|
+
},
|
|
3101
|
+
entity.id
|
|
3102
|
+
);
|
|
3103
|
+
})
|
|
3104
|
+
}
|
|
3105
|
+
),
|
|
3106
|
+
filteredProviders.length > 0 && /* @__PURE__ */ jsx(
|
|
3107
|
+
CollapsibleSection,
|
|
3108
|
+
{
|
|
3109
|
+
title: t("connectionsSection"),
|
|
3110
|
+
icon: /* @__PURE__ */ jsx(KeyIcon, { className: "h-3 w-3" }),
|
|
3111
|
+
colorClass: "text-slate-600 dark:text-slate-400",
|
|
3112
|
+
count: filteredProviders.length,
|
|
3113
|
+
children: filteredProviders.map((provider) => {
|
|
3114
|
+
const providerLogo = PROVIDER_LOGOS2[provider.provider];
|
|
3115
|
+
return /* @__PURE__ */ jsxs(
|
|
3116
|
+
"div",
|
|
3117
|
+
{
|
|
3118
|
+
draggable: true,
|
|
3119
|
+
onDragStart: (event) => {
|
|
3120
|
+
const defaultConfig = JSON.stringify({
|
|
3121
|
+
type: "model_provider",
|
|
3122
|
+
providerType: provider.provider === "amazon" ? "aws_bedrock" : provider.provider === "google" ? "google_vertex" : provider.provider === "meta" ? "custom" : `${provider.provider}_api`,
|
|
3123
|
+
name: provider.name,
|
|
3124
|
+
modelFilter: []
|
|
3125
|
+
});
|
|
3126
|
+
onDragStart(event, "model_provider", provider.id, provider.name, defaultConfig);
|
|
3127
|
+
},
|
|
3128
|
+
onClick: () => onConfigureProvider?.(provider.id),
|
|
3129
|
+
className: "liquid-surface group flex items-center gap-2 rounded-lg px-3 py-2 transition-all cursor-grab hover:shadow-md active:cursor-grabbing",
|
|
3130
|
+
role: "button",
|
|
3131
|
+
tabIndex: 0,
|
|
3132
|
+
children: [
|
|
3133
|
+
providerLogo ? /* @__PURE__ */ jsx("div", { className: "flex h-6 w-6 shrink-0 items-center justify-center rounded-md bg-white/10 p-1", children: /* @__PURE__ */ jsx("img", { src: providerLogo, alt: "", className: "h-4 w-4 object-contain", loading: "lazy" }) }) : /* @__PURE__ */ jsx("div", { className: "flex h-6 w-6 items-center justify-center rounded-md bg-gradient-to-br from-rose-400 to-pink-500 shadow-sm", children: /* @__PURE__ */ jsx(KeyIcon, { className: "h-3.5 w-3.5 text-white" }) }),
|
|
3134
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
3135
|
+
/* @__PURE__ */ jsx("p", { className: "truncate text-xs font-medium text-gray-900 dark:text-white", children: provider.name }),
|
|
3136
|
+
/* @__PURE__ */ jsxs("p", { className: "truncate text-[10px] text-gray-400 dark:text-gray-500", children: [
|
|
3137
|
+
provider.modelCount,
|
|
3138
|
+
" models"
|
|
3139
|
+
] })
|
|
3140
|
+
] }),
|
|
3141
|
+
provider.configured ? /* @__PURE__ */ jsx("span", { className: "rounded-full bg-green-100 px-1.5 py-0.5 text-[9px] font-medium text-green-600 dark:bg-green-500/10 dark:text-green-400", children: t("configured") }) : /* @__PURE__ */ jsx("span", { className: "rounded-full bg-amber-100 px-1.5 py-0.5 text-[9px] font-medium text-amber-600 dark:bg-amber-500/10 dark:text-amber-400", children: t("setup") })
|
|
3142
|
+
]
|
|
3143
|
+
},
|
|
3144
|
+
provider.id
|
|
3145
|
+
);
|
|
3146
|
+
})
|
|
3147
|
+
}
|
|
3148
|
+
),
|
|
3149
|
+
/* @__PURE__ */ jsxs(
|
|
3150
|
+
CollapsibleSection,
|
|
3151
|
+
{
|
|
3152
|
+
title: t("logicSection"),
|
|
3153
|
+
icon: /* @__PURE__ */ jsx(BoltIcon, { className: "h-3 w-3" }),
|
|
3154
|
+
colorClass: "text-gray-600 dark:text-gray-400",
|
|
3155
|
+
count: filteredLogicItems.length,
|
|
3156
|
+
children: [
|
|
3157
|
+
renderLogicSubcategory("control_flow", groupedLogicItems.control_flow, true),
|
|
3158
|
+
renderLogicSubcategory("data_processing", groupedLogicItems.data_processing, false),
|
|
3159
|
+
renderLogicSubcategory("ai_ml", groupedLogicItems.ai_ml, false),
|
|
3160
|
+
renderLogicSubcategory("output", groupedLogicItems.output, false),
|
|
3161
|
+
renderLogicSubcategory("annotation", groupedLogicItems.annotation, false)
|
|
3162
|
+
]
|
|
3163
|
+
}
|
|
3164
|
+
)
|
|
3165
|
+
] });
|
|
3166
|
+
}
|
|
3167
|
+
function formatRelativeTime(dateString) {
|
|
3168
|
+
if (!dateString) return "";
|
|
3169
|
+
const date = new Date(dateString);
|
|
3170
|
+
const now = /* @__PURE__ */ new Date();
|
|
3171
|
+
const diffMs = now.getTime() - date.getTime();
|
|
3172
|
+
const diffMinutes = Math.floor(diffMs / 6e4);
|
|
3173
|
+
const diffHours = Math.floor(diffMs / 36e5);
|
|
3174
|
+
const diffDays = Math.floor(diffMs / 864e5);
|
|
3175
|
+
if (diffMinutes < 1) return "just now";
|
|
3176
|
+
if (diffMinutes < 60) return `${diffMinutes}m ago`;
|
|
3177
|
+
if (diffHours < 24) return `${diffHours}h ago`;
|
|
3178
|
+
return `${diffDays}d ago`;
|
|
3179
|
+
}
|
|
3180
|
+
function WorkflowListBar({
|
|
3181
|
+
workflows,
|
|
3182
|
+
activeWorkflowId,
|
|
3183
|
+
isLoading,
|
|
3184
|
+
onSelect,
|
|
3185
|
+
onCreate,
|
|
3186
|
+
onDelete,
|
|
3187
|
+
onConvertToSubworkflow
|
|
3188
|
+
}) {
|
|
3189
|
+
const tWorkflow = useTranslations("agents.workflow");
|
|
3190
|
+
if (isLoading) {
|
|
3191
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 px-1 pb-3", children: [
|
|
3192
|
+
/* @__PURE__ */ jsx("div", { className: "shimmer h-10 w-40 rounded-xl" }),
|
|
3193
|
+
/* @__PURE__ */ jsx("div", { className: "shimmer h-10 w-40 rounded-xl" })
|
|
3194
|
+
] });
|
|
3195
|
+
}
|
|
3196
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 overflow-x-auto px-1 pb-3 scrollbar-hide", children: [
|
|
3197
|
+
workflows.map((workflow) => {
|
|
3198
|
+
const isActive = workflow.id === activeWorkflowId;
|
|
3199
|
+
return /* @__PURE__ */ jsxs(
|
|
3200
|
+
"div",
|
|
3201
|
+
{
|
|
3202
|
+
role: "button",
|
|
3203
|
+
tabIndex: 0,
|
|
3204
|
+
onClick: () => onSelect(workflow),
|
|
3205
|
+
onKeyDown: (event) => {
|
|
3206
|
+
if (event.key === "Enter" || event.key === " ") {
|
|
3207
|
+
event.preventDefault();
|
|
3208
|
+
onSelect(workflow);
|
|
3209
|
+
}
|
|
3210
|
+
},
|
|
3211
|
+
className: `liquid-surface group relative flex shrink-0 cursor-pointer items-center gap-2.5 rounded-xl px-3.5 py-2 text-left transition-all duration-200 ${isActive ? "liquid-surface-active" : "hover:shadow-md"}`,
|
|
3212
|
+
style: isActive ? { "--glass-accent": "99, 102, 241" } : void 0,
|
|
3213
|
+
children: [
|
|
3214
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
|
|
3215
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
3216
|
+
/* @__PURE__ */ jsx("span", { className: `truncate text-sm font-medium ${isActive ? "text-indigo-700 dark:text-indigo-300" : "text-gray-700 dark:text-gray-300"}`, children: workflow.name }),
|
|
3217
|
+
/* @__PURE__ */ jsxs("span", { className: "shrink-0 rounded-full bg-gray-100 px-1.5 py-0.5 text-[10px] font-medium text-gray-500 dark:bg-gray-800 dark:text-gray-400", children: [
|
|
3218
|
+
"v",
|
|
3219
|
+
workflow.version
|
|
3220
|
+
] }),
|
|
3221
|
+
workflow.isDraft ? /* @__PURE__ */ jsx("span", { className: "shrink-0 rounded-full bg-amber-100 px-1.5 py-0.5 text-[10px] font-semibold text-amber-700 dark:bg-amber-900/30 dark:text-amber-300", children: tWorkflow("draftBadge") }) : /* @__PURE__ */ jsx("span", { className: "shrink-0 rounded-full bg-green-100 px-1.5 py-0.5 text-[10px] font-semibold text-green-700 dark:bg-green-900/30 dark:text-green-300", children: tWorkflow("published") })
|
|
3222
|
+
] }),
|
|
3223
|
+
/* @__PURE__ */ jsx("p", { className: "mt-0.5 text-[10px] text-gray-400 dark:text-gray-500", children: workflow.updatedAt ? formatRelativeTime(typeof workflow.updatedAt === "string" ? workflow.updatedAt : workflow.updatedAt.toISOString()) : "" })
|
|
3224
|
+
] }),
|
|
3225
|
+
/* @__PURE__ */ jsxs("div", { className: "flex shrink-0 items-center gap-0.5 opacity-0 transition-opacity group-hover:opacity-100", children: [
|
|
3226
|
+
onConvertToSubworkflow && /* @__PURE__ */ jsx(
|
|
3227
|
+
"button",
|
|
3228
|
+
{
|
|
3229
|
+
type: "button",
|
|
3230
|
+
onClick: (event) => {
|
|
3231
|
+
event.stopPropagation();
|
|
3232
|
+
onConvertToSubworkflow(workflow);
|
|
3233
|
+
},
|
|
3234
|
+
className: "rounded-md p-1 text-gray-400 transition-colors hover:bg-teal-50 hover:text-teal-600 dark:hover:bg-teal-950/30 dark:hover:text-teal-400",
|
|
3235
|
+
"aria-label": `${tWorkflow("convertToSubworkflow")} ${workflow.name}`,
|
|
3236
|
+
title: tWorkflow("convertToSubworkflow"),
|
|
3237
|
+
children: /* @__PURE__ */ jsx(ArrowPathRoundedSquareIcon, { className: "h-3.5 w-3.5" })
|
|
3238
|
+
}
|
|
3239
|
+
),
|
|
3240
|
+
workflows.length > 1 && /* @__PURE__ */ jsx(
|
|
3241
|
+
"button",
|
|
3242
|
+
{
|
|
3243
|
+
type: "button",
|
|
3244
|
+
onClick: (event) => {
|
|
3245
|
+
event.stopPropagation();
|
|
3246
|
+
onDelete(workflow.id, workflow.name);
|
|
3247
|
+
},
|
|
3248
|
+
className: "rounded-md p-1 text-gray-400 transition-colors hover:bg-red-50 hover:text-red-500 dark:hover:bg-red-950/30 dark:hover:text-red-400",
|
|
3249
|
+
"aria-label": `${tWorkflow("deleteWorkflow")} ${workflow.name}`,
|
|
3250
|
+
children: /* @__PURE__ */ jsx(TrashIcon, { className: "h-3.5 w-3.5" })
|
|
3251
|
+
}
|
|
3252
|
+
)
|
|
3253
|
+
] })
|
|
3254
|
+
]
|
|
3255
|
+
},
|
|
3256
|
+
workflow.id
|
|
3257
|
+
);
|
|
3258
|
+
}),
|
|
3259
|
+
/* @__PURE__ */ jsxs(
|
|
3260
|
+
"button",
|
|
3261
|
+
{
|
|
3262
|
+
type: "button",
|
|
3263
|
+
onClick: onCreate,
|
|
3264
|
+
className: "liquid-surface flex shrink-0 items-center gap-1.5 rounded-xl !border-dashed px-3 py-2 text-xs font-medium text-gray-500 transition-all hover:shadow-md hover:text-indigo-600 dark:text-gray-400 dark:hover:text-indigo-400",
|
|
3265
|
+
children: [
|
|
3266
|
+
/* @__PURE__ */ jsx(PlusIcon, { className: "h-3.5 w-3.5" }),
|
|
3267
|
+
tWorkflow("newWorkflow")
|
|
3268
|
+
]
|
|
3269
|
+
}
|
|
3270
|
+
)
|
|
3271
|
+
] });
|
|
3272
|
+
}
|
|
3273
|
+
function VersionHistoryPanel({
|
|
3274
|
+
open,
|
|
3275
|
+
onClose,
|
|
3276
|
+
workflowId,
|
|
3277
|
+
currentVersion,
|
|
3278
|
+
onPreview,
|
|
3279
|
+
onRestore,
|
|
3280
|
+
fetchVersions
|
|
3281
|
+
}) {
|
|
3282
|
+
const translations = useTranslations("agents.workflow.versionHistory");
|
|
3283
|
+
const [versions, setVersions] = useState([]);
|
|
3284
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
3285
|
+
const [loadError, setLoadError] = useState(null);
|
|
3286
|
+
const loadVersions = useCallback(async () => {
|
|
3287
|
+
if (!fetchVersions) return;
|
|
3288
|
+
setIsLoading(true);
|
|
3289
|
+
setLoadError(null);
|
|
3290
|
+
try {
|
|
3291
|
+
const fetchedVersions = await fetchVersions(workflowId);
|
|
3292
|
+
setVersions(fetchedVersions);
|
|
3293
|
+
} catch (error) {
|
|
3294
|
+
const errorMessage = error instanceof Error ? error.message : "Failed to load versions";
|
|
3295
|
+
setLoadError(errorMessage);
|
|
3296
|
+
} finally {
|
|
3297
|
+
setIsLoading(false);
|
|
3298
|
+
}
|
|
3299
|
+
}, [fetchVersions, workflowId]);
|
|
3300
|
+
useEffect(() => {
|
|
3301
|
+
if (open) {
|
|
3302
|
+
loadVersions();
|
|
3303
|
+
}
|
|
3304
|
+
}, [open, loadVersions]);
|
|
3305
|
+
const formatTimestamp2 = useCallback((timestamp) => {
|
|
3306
|
+
try {
|
|
3307
|
+
const date = new Date(timestamp);
|
|
3308
|
+
return new Intl.DateTimeFormat(void 0, {
|
|
3309
|
+
year: "numeric",
|
|
3310
|
+
month: "short",
|
|
3311
|
+
day: "numeric",
|
|
3312
|
+
hour: "2-digit",
|
|
3313
|
+
minute: "2-digit"
|
|
3314
|
+
}).format(date);
|
|
3315
|
+
} catch {
|
|
3316
|
+
return timestamp;
|
|
3317
|
+
}
|
|
3318
|
+
}, []);
|
|
3319
|
+
if (!open) return null;
|
|
3320
|
+
return /* @__PURE__ */ jsxs(
|
|
3321
|
+
"div",
|
|
3322
|
+
{
|
|
3323
|
+
className: "absolute right-0 top-0 z-40 flex h-full w-80 flex-col border-l border-gray-200 bg-white shadow-xl dark:border-gray-700 dark:bg-gray-900",
|
|
3324
|
+
"data-testid": "version-history-panel",
|
|
3325
|
+
children: [
|
|
3326
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between border-b border-gray-200 px-4 py-3 dark:border-gray-700", children: [
|
|
3327
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
3328
|
+
/* @__PURE__ */ jsx(ClockIcon, { className: "h-5 w-5 text-gray-500 dark:text-gray-400" }),
|
|
3329
|
+
/* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold text-gray-900 dark:text-white", children: translations("title") })
|
|
3330
|
+
] }),
|
|
3331
|
+
/* @__PURE__ */ jsx(
|
|
3332
|
+
"button",
|
|
3333
|
+
{
|
|
3334
|
+
type: "button",
|
|
3335
|
+
onClick: onClose,
|
|
3336
|
+
className: "rounded-lg p-1 text-gray-400 transition-colors hover:bg-gray-100 hover:text-gray-600 dark:hover:bg-gray-800 dark:hover:text-gray-300",
|
|
3337
|
+
"data-testid": "version-history-close",
|
|
3338
|
+
children: /* @__PURE__ */ jsx(XMarkIcon, { className: "h-4 w-4" })
|
|
3339
|
+
}
|
|
3340
|
+
)
|
|
3341
|
+
] }),
|
|
3342
|
+
/* @__PURE__ */ jsxs("div", { className: "flex-1 overflow-y-auto", children: [
|
|
3343
|
+
isLoading && /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center py-12", "data-testid": "version-history-loading", children: /* @__PURE__ */ jsx("div", { className: "h-6 w-6 animate-spin rounded-full border-2 border-blue-500 border-t-transparent" }) }),
|
|
3344
|
+
loadError && /* @__PURE__ */ jsxs("div", { className: "px-4 py-8 text-center", "data-testid": "version-history-error", children: [
|
|
3345
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm text-red-500 dark:text-red-400", children: loadError }),
|
|
3346
|
+
/* @__PURE__ */ jsx(
|
|
3347
|
+
"button",
|
|
3348
|
+
{
|
|
3349
|
+
type: "button",
|
|
3350
|
+
onClick: loadVersions,
|
|
3351
|
+
className: "mt-2 text-xs text-blue-600 hover:underline dark:text-blue-400",
|
|
3352
|
+
children: translations("retry")
|
|
3353
|
+
}
|
|
3354
|
+
)
|
|
3355
|
+
] }),
|
|
3356
|
+
!isLoading && !loadError && versions.length === 0 && /* @__PURE__ */ jsxs("div", { className: "px-4 py-12 text-center", "data-testid": "version-history-empty", children: [
|
|
3357
|
+
/* @__PURE__ */ jsx(ClockIcon, { className: "mx-auto h-8 w-8 text-gray-300 dark:text-gray-600" }),
|
|
3358
|
+
/* @__PURE__ */ jsx("p", { className: "mt-2 text-sm text-gray-500 dark:text-gray-400", children: translations("noVersions") })
|
|
3359
|
+
] }),
|
|
3360
|
+
!isLoading && !loadError && versions.length > 0 && /* @__PURE__ */ jsx("div", { className: "divide-y divide-gray-100 dark:divide-gray-800", children: versions.map((version) => {
|
|
3361
|
+
const isCurrentVersion = version.version === currentVersion;
|
|
3362
|
+
return /* @__PURE__ */ jsxs(
|
|
3363
|
+
"div",
|
|
3364
|
+
{
|
|
3365
|
+
className: `px-4 py-3 transition-colors ${isCurrentVersion ? "bg-blue-50 dark:bg-blue-500/10" : "hover:bg-gray-50 dark:hover:bg-gray-800/50"}`,
|
|
3366
|
+
"data-testid": `version-entry-${version.version}`,
|
|
3367
|
+
children: [
|
|
3368
|
+
/* @__PURE__ */ jsx("div", { className: "flex items-center justify-between", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
3369
|
+
/* @__PURE__ */ jsxs("span", { className: "text-sm font-semibold text-gray-900 dark:text-white", children: [
|
|
3370
|
+
"v",
|
|
3371
|
+
version.version
|
|
3372
|
+
] }),
|
|
3373
|
+
isCurrentVersion && /* @__PURE__ */ jsx("span", { className: "rounded-full bg-blue-100 px-2 py-0.5 text-[10px] font-medium text-blue-700 dark:bg-blue-500/20 dark:text-blue-300", children: translations("current") })
|
|
3374
|
+
] }) }),
|
|
3375
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-1 text-xs text-gray-500 dark:text-gray-400", children: [
|
|
3376
|
+
/* @__PURE__ */ jsx("div", { children: formatTimestamp2(version.publishedAt) }),
|
|
3377
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-0.5", children: [
|
|
3378
|
+
translations("publishedBy"),
|
|
3379
|
+
": ",
|
|
3380
|
+
version.publishedBy
|
|
3381
|
+
] }),
|
|
3382
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-0.5", children: [
|
|
3383
|
+
version.nodeCount,
|
|
3384
|
+
" ",
|
|
3385
|
+
translations("nodes"),
|
|
3386
|
+
" / ",
|
|
3387
|
+
version.edgeCount,
|
|
3388
|
+
" ",
|
|
3389
|
+
translations("edges")
|
|
3390
|
+
] })
|
|
3391
|
+
] }),
|
|
3392
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-2 flex gap-2", children: [
|
|
3393
|
+
/* @__PURE__ */ jsxs(
|
|
3394
|
+
"button",
|
|
3395
|
+
{
|
|
3396
|
+
type: "button",
|
|
3397
|
+
onClick: () => onPreview(version),
|
|
3398
|
+
className: "inline-flex items-center gap-1 rounded-md border border-gray-300 px-2 py-1 text-xs font-medium text-gray-600 transition-colors hover:bg-gray-100 dark:border-gray-600 dark:text-gray-400 dark:hover:bg-gray-700",
|
|
3399
|
+
"data-testid": `version-preview-${version.version}`,
|
|
3400
|
+
children: [
|
|
3401
|
+
/* @__PURE__ */ jsx(EyeIcon, { className: "h-3 w-3" }),
|
|
3402
|
+
translations("preview")
|
|
3403
|
+
]
|
|
3404
|
+
}
|
|
3405
|
+
),
|
|
3406
|
+
!isCurrentVersion && /* @__PURE__ */ jsxs(
|
|
3407
|
+
"button",
|
|
3408
|
+
{
|
|
3409
|
+
type: "button",
|
|
3410
|
+
onClick: () => onRestore(version),
|
|
3411
|
+
className: "inline-flex items-center gap-1 rounded-md border border-blue-300 px-2 py-1 text-xs font-medium text-blue-600 transition-colors hover:bg-blue-50 dark:border-blue-600 dark:text-blue-400 dark:hover:bg-blue-500/10",
|
|
3412
|
+
"data-testid": `version-restore-${version.version}`,
|
|
3413
|
+
children: [
|
|
3414
|
+
/* @__PURE__ */ jsx(ArrowPathIcon, { className: "h-3 w-3" }),
|
|
3415
|
+
translations("restore")
|
|
3416
|
+
]
|
|
3417
|
+
}
|
|
3418
|
+
)
|
|
3419
|
+
] })
|
|
3420
|
+
]
|
|
3421
|
+
},
|
|
3422
|
+
version.version
|
|
3423
|
+
);
|
|
3424
|
+
}) })
|
|
3425
|
+
] })
|
|
3426
|
+
]
|
|
3427
|
+
}
|
|
3428
|
+
);
|
|
3429
|
+
}
|
|
3430
|
+
var STATUS_CONFIG = {
|
|
3431
|
+
pending: {
|
|
3432
|
+
icon: ClockIcon,
|
|
3433
|
+
color: "text-gray-400 dark:text-gray-500",
|
|
3434
|
+
background: "bg-gray-100 dark:bg-gray-800",
|
|
3435
|
+
label: "pending"
|
|
3436
|
+
},
|
|
3437
|
+
running: {
|
|
3438
|
+
icon: SpinnerIcon,
|
|
3439
|
+
color: "text-blue-500 dark:text-blue-400",
|
|
3440
|
+
background: "bg-blue-50 dark:bg-blue-500/10",
|
|
3441
|
+
label: "running"
|
|
3442
|
+
},
|
|
3443
|
+
success: {
|
|
3444
|
+
icon: CheckCircleIcon,
|
|
3445
|
+
color: "text-green-500 dark:text-green-400",
|
|
3446
|
+
background: "bg-green-50 dark:bg-green-500/10",
|
|
3447
|
+
label: "success"
|
|
3448
|
+
},
|
|
3449
|
+
error: {
|
|
3450
|
+
icon: XCircleIcon,
|
|
3451
|
+
color: "text-red-500 dark:text-red-400",
|
|
3452
|
+
background: "bg-red-50 dark:bg-red-500/10",
|
|
3453
|
+
label: "error"
|
|
3454
|
+
}
|
|
3455
|
+
};
|
|
3456
|
+
function formatDuration2(durationMs) {
|
|
3457
|
+
if (durationMs < 1e3) {
|
|
3458
|
+
return `${durationMs}ms`;
|
|
3459
|
+
}
|
|
3460
|
+
const seconds = (durationMs / 1e3).toFixed(1);
|
|
3461
|
+
return `${seconds}s`;
|
|
3462
|
+
}
|
|
3463
|
+
function RunPanel({ open, onClose, onRun, onStop }) {
|
|
3464
|
+
const translations = useTranslations("agents.workflow.runPanel");
|
|
3465
|
+
const nodes = useWorkflowStore((state) => state.nodes);
|
|
3466
|
+
const isRunning = useWorkflowStore((state) => state.isRunning);
|
|
3467
|
+
const nodeResults = useWorkflowStore((state) => state.nodeResults);
|
|
3468
|
+
const startNode = nodes.find((node) => node.type === "start");
|
|
3469
|
+
const hasEndNode = nodes.some((node) => node.type === "end");
|
|
3470
|
+
const hasValidStartConfig = Boolean(
|
|
3471
|
+
startNode && typeof startNode.data === "object" && startNode.data !== null && "config" in startNode.data && startNode.data.config && typeof startNode.data.config.type === "string"
|
|
3472
|
+
);
|
|
3473
|
+
const canRun = hasValidStartConfig && hasEndNode;
|
|
3474
|
+
if (!open) return null;
|
|
3475
|
+
const executionNodes = nodes.filter((node) => node.type !== "note");
|
|
3476
|
+
return /* @__PURE__ */ jsxs(
|
|
3477
|
+
"div",
|
|
3478
|
+
{
|
|
3479
|
+
className: "absolute bottom-0 left-0 right-0 z-40 border-t border-gray-200 bg-white shadow-xl dark:border-gray-700 dark:bg-gray-900",
|
|
3480
|
+
"data-testid": "run-panel",
|
|
3481
|
+
children: [
|
|
3482
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between border-b border-gray-100 px-4 py-2 dark:border-gray-800", children: [
|
|
3483
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
3484
|
+
/* @__PURE__ */ jsx(PlayIcon, { className: "h-4 w-4 text-gray-500 dark:text-gray-400" }),
|
|
3485
|
+
/* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold text-gray-900 dark:text-white", children: translations("title") }),
|
|
3486
|
+
isRunning && /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1 rounded-full bg-blue-100 px-2 py-0.5 text-[10px] font-medium text-blue-700 dark:bg-blue-500/20 dark:text-blue-300", children: [
|
|
3487
|
+
/* @__PURE__ */ jsx("span", { className: "h-1.5 w-1.5 animate-pulse rounded-full bg-blue-500" }),
|
|
3488
|
+
translations("executing")
|
|
3489
|
+
] })
|
|
3490
|
+
] }),
|
|
3491
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
3492
|
+
isRunning ? /* @__PURE__ */ jsxs(
|
|
3493
|
+
"button",
|
|
3494
|
+
{
|
|
3495
|
+
type: "button",
|
|
3496
|
+
onClick: onStop,
|
|
3497
|
+
className: "inline-flex items-center gap-1.5 rounded-lg bg-red-500 px-3 py-1.5 text-xs font-medium text-white transition-colors hover:bg-red-600",
|
|
3498
|
+
"data-testid": "run-panel-stop",
|
|
3499
|
+
children: [
|
|
3500
|
+
/* @__PURE__ */ jsx(StopIcon, { className: "h-3.5 w-3.5" }),
|
|
3501
|
+
translations("stop")
|
|
3502
|
+
]
|
|
3503
|
+
}
|
|
3504
|
+
) : /* @__PURE__ */ jsxs(
|
|
3505
|
+
"button",
|
|
3506
|
+
{
|
|
3507
|
+
type: "button",
|
|
3508
|
+
onClick: onRun,
|
|
3509
|
+
disabled: !canRun,
|
|
3510
|
+
className: "inline-flex items-center gap-1.5 rounded-lg bg-green-500 px-3 py-1.5 text-xs font-medium text-white transition-colors hover:bg-green-600 disabled:cursor-not-allowed disabled:opacity-50 disabled:hover:bg-green-500",
|
|
3511
|
+
"data-testid": "run-panel-run",
|
|
3512
|
+
children: [
|
|
3513
|
+
/* @__PURE__ */ jsx(PlayIcon, { className: "h-3.5 w-3.5" }),
|
|
3514
|
+
translations("run")
|
|
3515
|
+
]
|
|
3516
|
+
}
|
|
3517
|
+
),
|
|
3518
|
+
/* @__PURE__ */ jsx(
|
|
3519
|
+
"button",
|
|
3520
|
+
{
|
|
3521
|
+
type: "button",
|
|
3522
|
+
onClick: onClose,
|
|
3523
|
+
className: "rounded-lg p-1 text-gray-400 transition-colors hover:bg-gray-100 hover:text-gray-600 dark:hover:bg-gray-800 dark:hover:text-gray-300",
|
|
3524
|
+
"data-testid": "run-panel-close",
|
|
3525
|
+
children: /* @__PURE__ */ jsx(XMarkIcon, { className: "h-4 w-4" })
|
|
3526
|
+
}
|
|
3527
|
+
)
|
|
3528
|
+
] })
|
|
3529
|
+
] }),
|
|
3530
|
+
/* @__PURE__ */ jsx("div", { className: "max-h-48 overflow-y-auto px-4 py-2", children: executionNodes.length === 0 ? /* @__PURE__ */ jsx("div", { className: "py-4 text-center text-sm text-gray-500 dark:text-gray-400", children: translations("noNodes") }) : /* @__PURE__ */ jsx("div", { className: "space-y-1", children: executionNodes.map((node) => {
|
|
3531
|
+
const result = nodeResults[node.id];
|
|
3532
|
+
const status = result?.status ?? "pending";
|
|
3533
|
+
const statusConfig = STATUS_CONFIG[status];
|
|
3534
|
+
const StatusIconComponent = statusConfig.icon;
|
|
3535
|
+
const nodeLabel = typeof node.data === "object" && node.data !== null && "label" in node.data ? String(node.data.label) : node.id;
|
|
3536
|
+
return /* @__PURE__ */ jsxs(
|
|
3537
|
+
"div",
|
|
3538
|
+
{
|
|
3539
|
+
className: `flex items-center gap-3 rounded-lg px-3 py-2 ${statusConfig.background}`,
|
|
3540
|
+
"data-testid": `run-node-${node.id}`,
|
|
3541
|
+
children: [
|
|
3542
|
+
/* @__PURE__ */ jsx(StatusIconComponent, { className: `h-4 w-4 flex-shrink-0 ${statusConfig.color}` }),
|
|
3543
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
3544
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
3545
|
+
/* @__PURE__ */ jsx("span", { className: "truncate text-sm font-medium text-gray-900 dark:text-white", children: nodeLabel }),
|
|
3546
|
+
/* @__PURE__ */ jsx("span", { className: "rounded bg-gray-200 px-1.5 py-0.5 text-[10px] text-gray-500 dark:bg-gray-700 dark:text-gray-400", children: node.type })
|
|
3547
|
+
] }),
|
|
3548
|
+
result?.error && /* @__PURE__ */ jsx("p", { className: "mt-0.5 truncate text-xs text-red-500 dark:text-red-400", children: result.error })
|
|
3549
|
+
] }),
|
|
3550
|
+
result?.durationMs !== void 0 && /* @__PURE__ */ jsx("span", { className: "flex-shrink-0 text-xs text-gray-500 dark:text-gray-400", children: formatDuration2(result.durationMs) })
|
|
3551
|
+
]
|
|
3552
|
+
},
|
|
3553
|
+
node.id
|
|
3554
|
+
);
|
|
3555
|
+
}) }) })
|
|
3556
|
+
]
|
|
3557
|
+
}
|
|
3558
|
+
);
|
|
3559
|
+
}
|
|
3560
|
+
function SpinnerIcon({ className }) {
|
|
3561
|
+
return /* @__PURE__ */ jsxs(
|
|
3562
|
+
"svg",
|
|
3563
|
+
{
|
|
3564
|
+
className: `animate-spin ${className ?? ""}`,
|
|
3565
|
+
viewBox: "0 0 24 24",
|
|
3566
|
+
fill: "none",
|
|
3567
|
+
stroke: "currentColor",
|
|
3568
|
+
strokeWidth: 2,
|
|
3569
|
+
children: [
|
|
3570
|
+
/* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "10", strokeOpacity: 0.25 }),
|
|
3571
|
+
/* @__PURE__ */ jsx("path", { d: "M12 2a10 10 0 0 1 10 10", strokeLinecap: "round" })
|
|
3572
|
+
]
|
|
3573
|
+
}
|
|
3574
|
+
);
|
|
3575
|
+
}
|
|
3576
|
+
function topologicalSort(nodes, edges) {
|
|
3577
|
+
const adjacencyList = /* @__PURE__ */ new Map();
|
|
3578
|
+
const inDegree = /* @__PURE__ */ new Map();
|
|
3579
|
+
for (const node of nodes) {
|
|
3580
|
+
adjacencyList.set(node.id, []);
|
|
3581
|
+
inDegree.set(node.id, 0);
|
|
3582
|
+
}
|
|
3583
|
+
for (const edge of edges) {
|
|
3584
|
+
const neighbors = adjacencyList.get(edge.source);
|
|
3585
|
+
if (neighbors) {
|
|
3586
|
+
neighbors.push(edge.target);
|
|
3587
|
+
}
|
|
3588
|
+
inDegree.set(edge.target, (inDegree.get(edge.target) ?? 0) + 1);
|
|
3589
|
+
}
|
|
3590
|
+
const queue = [];
|
|
3591
|
+
for (const [nodeId, degree] of inDegree.entries()) {
|
|
3592
|
+
if (degree === 0) {
|
|
3593
|
+
queue.push(nodeId);
|
|
3594
|
+
}
|
|
3595
|
+
}
|
|
3596
|
+
const sorted = [];
|
|
3597
|
+
while (queue.length > 0) {
|
|
3598
|
+
const currentNodeId = queue.shift();
|
|
3599
|
+
sorted.push(currentNodeId);
|
|
3600
|
+
const neighbors = adjacencyList.get(currentNodeId) ?? [];
|
|
3601
|
+
for (const neighborId of neighbors) {
|
|
3602
|
+
const updatedDegree = (inDegree.get(neighborId) ?? 1) - 1;
|
|
3603
|
+
inDegree.set(neighborId, updatedDegree);
|
|
3604
|
+
if (updatedDegree === 0) {
|
|
3605
|
+
queue.push(neighborId);
|
|
3606
|
+
}
|
|
3607
|
+
}
|
|
3608
|
+
}
|
|
3609
|
+
for (const node of nodes) {
|
|
3610
|
+
if (!sorted.includes(node.id)) {
|
|
3611
|
+
sorted.push(node.id);
|
|
3612
|
+
}
|
|
3613
|
+
}
|
|
3614
|
+
return sorted;
|
|
3615
|
+
}
|
|
3616
|
+
function inferVariables(config, nodeType) {
|
|
3617
|
+
if (!config) {
|
|
3618
|
+
return [];
|
|
3619
|
+
}
|
|
3620
|
+
const variables = [];
|
|
3621
|
+
switch (config.type) {
|
|
3622
|
+
case "start":
|
|
3623
|
+
for (const variableName of config.inputVariables) {
|
|
3624
|
+
if (variableName) {
|
|
3625
|
+
variables.push({ name: variableName, direction: "output", variableType: "any" });
|
|
3626
|
+
}
|
|
3627
|
+
}
|
|
3628
|
+
break;
|
|
3629
|
+
case "end":
|
|
3630
|
+
for (const variableName of config.outputVariables) {
|
|
3631
|
+
if (variableName) {
|
|
3632
|
+
variables.push({ name: variableName, direction: "input", variableType: "any" });
|
|
3633
|
+
}
|
|
3634
|
+
}
|
|
3635
|
+
break;
|
|
3636
|
+
case "code":
|
|
3637
|
+
{
|
|
3638
|
+
const kind = "language" in config ? config.language : config.operation;
|
|
3639
|
+
variables.push({ name: "code_input", direction: "input", variableType: kind });
|
|
3640
|
+
variables.push({ name: "code_output", direction: "output", variableType: "any" });
|
|
3641
|
+
}
|
|
3642
|
+
break;
|
|
3643
|
+
case "http_request":
|
|
3644
|
+
variables.push({ name: "url", direction: "input", variableType: "string" });
|
|
3645
|
+
variables.push({ name: "response", direction: "output", variableType: "object" });
|
|
3646
|
+
if (config.body) {
|
|
3647
|
+
variables.push({ name: "body", direction: "input", variableType: "string" });
|
|
3648
|
+
}
|
|
3649
|
+
break;
|
|
3650
|
+
case "template_transform":
|
|
3651
|
+
variables.push({ name: "template", direction: "input", variableType: "string" });
|
|
3652
|
+
if (config.outputVariable) {
|
|
3653
|
+
variables.push({ name: config.outputVariable, direction: "output", variableType: "string" });
|
|
3654
|
+
}
|
|
3655
|
+
break;
|
|
3656
|
+
case "if_else":
|
|
3657
|
+
for (const condition of config.conditions) {
|
|
3658
|
+
if (condition.variable) {
|
|
3659
|
+
variables.push({ name: condition.variable, direction: "input", variableType: "any" });
|
|
3660
|
+
}
|
|
3661
|
+
}
|
|
3662
|
+
variables.push({ name: "true_branch", direction: "output", variableType: "boolean" });
|
|
3663
|
+
variables.push({ name: "false_branch", direction: "output", variableType: "boolean" });
|
|
3664
|
+
break;
|
|
3665
|
+
case "iteration":
|
|
3666
|
+
if (config.iteratorVariable) {
|
|
3667
|
+
variables.push({ name: config.iteratorVariable, direction: "input", variableType: "array" });
|
|
3668
|
+
}
|
|
3669
|
+
variables.push({ name: "iteration_item", direction: "output", variableType: "any" });
|
|
3670
|
+
variables.push({ name: "iteration_index", direction: "output", variableType: "number" });
|
|
3671
|
+
break;
|
|
3672
|
+
case "iteration_start":
|
|
3673
|
+
if (config.iteratorVariable) {
|
|
3674
|
+
variables.push({ name: config.iteratorVariable, direction: "input", variableType: "array" });
|
|
3675
|
+
}
|
|
3676
|
+
if (config.itemVariable) {
|
|
3677
|
+
variables.push({ name: config.itemVariable, direction: "output", variableType: "any" });
|
|
3678
|
+
}
|
|
3679
|
+
if (config.indexVariable) {
|
|
3680
|
+
variables.push({ name: config.indexVariable, direction: "output", variableType: "number" });
|
|
3681
|
+
}
|
|
3682
|
+
break;
|
|
3683
|
+
case "knowledge_base":
|
|
3684
|
+
variables.push({ name: "query", direction: "input", variableType: "string" });
|
|
3685
|
+
variables.push({ name: "results", direction: "output", variableType: "array" });
|
|
3686
|
+
break;
|
|
3687
|
+
case "answer":
|
|
3688
|
+
for (const variableName of config.outputVariables) {
|
|
3689
|
+
if (variableName) {
|
|
3690
|
+
variables.push({ name: variableName, direction: "output", variableType: "any" });
|
|
3691
|
+
}
|
|
3692
|
+
}
|
|
3693
|
+
if (config.outputTemplate) {
|
|
3694
|
+
variables.push({ name: "answer_template", direction: "input", variableType: "string" });
|
|
3695
|
+
}
|
|
3696
|
+
break;
|
|
3697
|
+
case "question_classifier":
|
|
3698
|
+
variables.push({ name: "question", direction: "input", variableType: "string" });
|
|
3699
|
+
for (const category of config.categories) {
|
|
3700
|
+
if (category.name) {
|
|
3701
|
+
variables.push({ name: `category_${category.name}`, direction: "output", variableType: "string" });
|
|
3702
|
+
}
|
|
3703
|
+
}
|
|
3704
|
+
break;
|
|
3705
|
+
case "parameter_extractor":
|
|
3706
|
+
variables.push({ name: "input_text", direction: "input", variableType: "string" });
|
|
3707
|
+
for (const parameter of config.parameters) {
|
|
3708
|
+
if (parameter.name) {
|
|
3709
|
+
variables.push({ name: parameter.name, direction: "output", variableType: parameter.type });
|
|
3710
|
+
}
|
|
3711
|
+
}
|
|
3712
|
+
break;
|
|
3713
|
+
case "variable_assigner":
|
|
3714
|
+
for (const assignment of config.assignments) {
|
|
3715
|
+
if (assignment.source) {
|
|
3716
|
+
variables.push({ name: assignment.source, direction: "input", variableType: "any" });
|
|
3717
|
+
}
|
|
3718
|
+
if (assignment.target) {
|
|
3719
|
+
variables.push({ name: assignment.target, direction: "output", variableType: "any" });
|
|
3720
|
+
}
|
|
3721
|
+
}
|
|
3722
|
+
break;
|
|
3723
|
+
case "variable_aggregator":
|
|
3724
|
+
for (const variableName of config.inputVariables) {
|
|
3725
|
+
if (variableName) {
|
|
3726
|
+
variables.push({ name: variableName, direction: "input", variableType: "any" });
|
|
3727
|
+
}
|
|
3728
|
+
}
|
|
3729
|
+
if (config.outputVariable) {
|
|
3730
|
+
variables.push({ name: config.outputVariable, direction: "output", variableType: config.aggregationMode });
|
|
3731
|
+
}
|
|
3732
|
+
break;
|
|
3733
|
+
case "document_extractor":
|
|
3734
|
+
variables.push({ name: "document", direction: "input", variableType: "file" });
|
|
3735
|
+
if (config.outputVariable) {
|
|
3736
|
+
variables.push({ name: config.outputVariable, direction: "output", variableType: config.extractionMode });
|
|
3737
|
+
}
|
|
3738
|
+
break;
|
|
3739
|
+
case "list_operator":
|
|
3740
|
+
if (config.inputVariable) {
|
|
3741
|
+
variables.push({ name: config.inputVariable, direction: "input", variableType: "array" });
|
|
3742
|
+
}
|
|
3743
|
+
if (config.outputVariable) {
|
|
3744
|
+
variables.push({ name: config.outputVariable, direction: "output", variableType: "array" });
|
|
3745
|
+
}
|
|
3746
|
+
if (config.condition) {
|
|
3747
|
+
variables.push({ name: "condition", direction: "input", variableType: "string" });
|
|
3748
|
+
}
|
|
3749
|
+
break;
|
|
3750
|
+
}
|
|
3751
|
+
return variables;
|
|
3752
|
+
}
|
|
3753
|
+
function VariableInspector({ open, onClose }) {
|
|
3754
|
+
const translations = useTranslations("agents.workflow.variableInspector");
|
|
3755
|
+
const nodes = useWorkflowStore((state) => state.nodes);
|
|
3756
|
+
const edges = useWorkflowStore((state) => state.edges);
|
|
3757
|
+
const [expandedNodes, setExpandedNodes] = useState(/* @__PURE__ */ new Set());
|
|
3758
|
+
const toggleNodeExpansion = useCallback((nodeId) => {
|
|
3759
|
+
setExpandedNodes((current) => {
|
|
3760
|
+
const updated = new Set(current);
|
|
3761
|
+
if (updated.has(nodeId)) {
|
|
3762
|
+
updated.delete(nodeId);
|
|
3763
|
+
} else {
|
|
3764
|
+
updated.add(nodeId);
|
|
3765
|
+
}
|
|
3766
|
+
return updated;
|
|
3767
|
+
});
|
|
3768
|
+
}, []);
|
|
3769
|
+
const nodeVariableEntries = useMemo(() => {
|
|
3770
|
+
const relevantNodes = nodes.filter((node) => node.type !== "note");
|
|
3771
|
+
const sortedNodeIds = topologicalSort(relevantNodes, edges);
|
|
3772
|
+
const entries = [];
|
|
3773
|
+
for (const nodeId of sortedNodeIds) {
|
|
3774
|
+
const node = relevantNodes.find((candidateNode) => candidateNode.id === nodeId);
|
|
3775
|
+
if (!node) continue;
|
|
3776
|
+
const nodeData = node.data;
|
|
3777
|
+
const config = nodeData?.config;
|
|
3778
|
+
const label = nodeData?.label ?? node.id;
|
|
3779
|
+
const nodeType = node.type ?? "unknown";
|
|
3780
|
+
const variables = inferVariables(config);
|
|
3781
|
+
entries.push({
|
|
3782
|
+
nodeId: node.id,
|
|
3783
|
+
nodeLabel: label,
|
|
3784
|
+
nodeType,
|
|
3785
|
+
variables
|
|
3786
|
+
});
|
|
3787
|
+
}
|
|
3788
|
+
return entries;
|
|
3789
|
+
}, [nodes, edges]);
|
|
3790
|
+
if (!open) return null;
|
|
3791
|
+
return /* @__PURE__ */ jsxs(
|
|
3792
|
+
"div",
|
|
3793
|
+
{
|
|
3794
|
+
className: "absolute right-0 top-0 z-40 flex h-full w-80 flex-col border-l border-gray-200 bg-white shadow-xl dark:border-gray-700 dark:bg-gray-900",
|
|
3795
|
+
"data-testid": "variable-inspector",
|
|
3796
|
+
children: [
|
|
3797
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between border-b border-gray-200 px-4 py-3 dark:border-gray-700", children: [
|
|
3798
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
3799
|
+
/* @__PURE__ */ jsx(VariableIcon, { className: "h-5 w-5 text-gray-500 dark:text-gray-400" }),
|
|
3800
|
+
/* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold text-gray-900 dark:text-white", children: translations("title") })
|
|
3801
|
+
] }),
|
|
3802
|
+
/* @__PURE__ */ jsx(
|
|
3803
|
+
"button",
|
|
3804
|
+
{
|
|
3805
|
+
type: "button",
|
|
3806
|
+
onClick: onClose,
|
|
3807
|
+
className: "rounded-lg p-1 text-gray-400 transition-colors hover:bg-gray-100 hover:text-gray-600 dark:hover:bg-gray-800 dark:hover:text-gray-300",
|
|
3808
|
+
"data-testid": "variable-inspector-close",
|
|
3809
|
+
children: /* @__PURE__ */ jsx(XMarkIcon, { className: "h-4 w-4" })
|
|
3810
|
+
}
|
|
3811
|
+
)
|
|
3812
|
+
] }),
|
|
3813
|
+
/* @__PURE__ */ jsx("div", { className: "flex-1 overflow-y-auto", children: nodeVariableEntries.length === 0 ? /* @__PURE__ */ jsxs("div", { className: "px-4 py-12 text-center", "data-testid": "variable-inspector-empty", children: [
|
|
3814
|
+
/* @__PURE__ */ jsx(VariableIcon, { className: "mx-auto h-8 w-8 text-gray-300 dark:text-gray-600" }),
|
|
3815
|
+
/* @__PURE__ */ jsx("p", { className: "mt-2 text-sm text-gray-500 dark:text-gray-400", children: translations("noNodes") })
|
|
3816
|
+
] }) : /* @__PURE__ */ jsx("div", { className: "divide-y divide-gray-100 dark:divide-gray-800", children: nodeVariableEntries.map((entry) => {
|
|
3817
|
+
const isExpanded = expandedNodes.has(entry.nodeId);
|
|
3818
|
+
const IconComponent = LOGIC_ICON_MAP[entry.nodeType];
|
|
3819
|
+
const gradient = LOGIC_NODE_GRADIENTS[entry.nodeType] ?? "from-gray-400 to-gray-500";
|
|
3820
|
+
const inputVariables = entry.variables.filter((variable) => variable.direction === "input");
|
|
3821
|
+
const outputVariables = entry.variables.filter((variable) => variable.direction === "output");
|
|
3822
|
+
return /* @__PURE__ */ jsxs("div", { "data-testid": `variable-node-${entry.nodeId}`, children: [
|
|
3823
|
+
/* @__PURE__ */ jsxs(
|
|
3824
|
+
"button",
|
|
3825
|
+
{
|
|
3826
|
+
type: "button",
|
|
3827
|
+
onClick: () => toggleNodeExpansion(entry.nodeId),
|
|
3828
|
+
className: "flex w-full items-center gap-2 px-4 py-2.5 text-left transition-colors hover:bg-gray-50 dark:hover:bg-gray-800/50",
|
|
3829
|
+
children: [
|
|
3830
|
+
isExpanded ? /* @__PURE__ */ jsx(ChevronDownIcon, { className: "h-3.5 w-3.5 flex-shrink-0 text-gray-400" }) : /* @__PURE__ */ jsx(ChevronRightIcon, { className: "h-3.5 w-3.5 flex-shrink-0 text-gray-400" }),
|
|
3831
|
+
/* @__PURE__ */ jsx("div", { className: `flex h-5 w-5 flex-shrink-0 items-center justify-center rounded bg-gradient-to-br ${gradient}`, children: IconComponent && /* @__PURE__ */ jsx(IconComponent, { className: "h-3 w-3 text-white" }) }),
|
|
3832
|
+
/* @__PURE__ */ jsx("span", { className: "truncate text-sm font-medium text-gray-900 dark:text-white", children: entry.nodeLabel }),
|
|
3833
|
+
/* @__PURE__ */ jsxs("span", { className: "ml-auto flex-shrink-0 text-[10px] text-gray-400 dark:text-gray-500", children: [
|
|
3834
|
+
entry.variables.length,
|
|
3835
|
+
" ",
|
|
3836
|
+
translations("variables")
|
|
3837
|
+
] })
|
|
3838
|
+
]
|
|
3839
|
+
}
|
|
3840
|
+
),
|
|
3841
|
+
isExpanded && /* @__PURE__ */ jsx("div", { className: "bg-gray-50/50 px-4 pb-2 dark:bg-gray-800/30", children: entry.variables.length === 0 ? /* @__PURE__ */ jsx("p", { className: "py-2 pl-9 text-xs italic text-gray-400 dark:text-gray-500", children: translations("noVariables") }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3842
|
+
inputVariables.length > 0 && /* @__PURE__ */ jsxs("div", { className: "pl-9 pt-1", children: [
|
|
3843
|
+
/* @__PURE__ */ jsx("div", { className: "mb-1 text-[10px] font-medium uppercase tracking-wider text-gray-400 dark:text-gray-500", children: translations("inputs") }),
|
|
3844
|
+
inputVariables.map((variable, index) => /* @__PURE__ */ jsxs(
|
|
3845
|
+
"div",
|
|
3846
|
+
{
|
|
3847
|
+
className: "flex items-center gap-2 py-0.5",
|
|
3848
|
+
children: [
|
|
3849
|
+
/* @__PURE__ */ jsx("span", { className: "h-1.5 w-1.5 rounded-full bg-blue-400" }),
|
|
3850
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs text-gray-700 dark:text-gray-300", children: variable.name }),
|
|
3851
|
+
/* @__PURE__ */ jsx("span", { className: "rounded bg-gray-200 px-1 py-0.5 text-[9px] text-gray-500 dark:bg-gray-700 dark:text-gray-400", children: variable.variableType })
|
|
3852
|
+
]
|
|
3853
|
+
},
|
|
3854
|
+
`${variable.name}-${index}`
|
|
3855
|
+
))
|
|
3856
|
+
] }),
|
|
3857
|
+
outputVariables.length > 0 && /* @__PURE__ */ jsxs("div", { className: "pl-9 pt-1", children: [
|
|
3858
|
+
/* @__PURE__ */ jsx("div", { className: "mb-1 text-[10px] font-medium uppercase tracking-wider text-gray-400 dark:text-gray-500", children: translations("outputs") }),
|
|
3859
|
+
outputVariables.map((variable, index) => /* @__PURE__ */ jsxs(
|
|
3860
|
+
"div",
|
|
3861
|
+
{
|
|
3862
|
+
className: "flex items-center gap-2 py-0.5",
|
|
3863
|
+
children: [
|
|
3864
|
+
/* @__PURE__ */ jsx("span", { className: "h-1.5 w-1.5 rounded-full bg-green-400" }),
|
|
3865
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs text-gray-700 dark:text-gray-300", children: variable.name }),
|
|
3866
|
+
/* @__PURE__ */ jsx("span", { className: "rounded bg-gray-200 px-1 py-0.5 text-[9px] text-gray-500 dark:bg-gray-700 dark:text-gray-400", children: variable.variableType })
|
|
3867
|
+
]
|
|
3868
|
+
},
|
|
3869
|
+
`${variable.name}-${index}`
|
|
3870
|
+
))
|
|
3871
|
+
] })
|
|
3872
|
+
] }) })
|
|
3873
|
+
] }, entry.nodeId);
|
|
3874
|
+
}) }) })
|
|
3875
|
+
]
|
|
3876
|
+
}
|
|
3877
|
+
);
|
|
3878
|
+
}
|
|
3879
|
+
function RunInputDialog({
|
|
3880
|
+
open,
|
|
3881
|
+
onClose,
|
|
3882
|
+
onRun
|
|
3883
|
+
}) {
|
|
3884
|
+
const t = useTranslations("agents.workflow.runInputDialog");
|
|
3885
|
+
const nodes = useWorkflowStore((state) => state.nodes);
|
|
3886
|
+
const [values, setValues] = useState({});
|
|
3887
|
+
const inputVariableNames = useMemo(() => {
|
|
3888
|
+
const storeStartNode = nodes.find((node) => node.type === "start");
|
|
3889
|
+
if (!storeStartNode || typeof storeStartNode.data !== "object" || storeStartNode.data === null) return [];
|
|
3890
|
+
if (!("config" in storeStartNode.data) || !storeStartNode.data.config) return [];
|
|
3891
|
+
const startConfig = storeStartNode.data.config;
|
|
3892
|
+
return startConfig.inputVariables ?? [];
|
|
3893
|
+
}, [nodes]);
|
|
3894
|
+
const handleValueChange = useCallback((variableName, variableValue) => {
|
|
3895
|
+
setValues((previous) => ({ ...previous, [variableName]: variableValue }));
|
|
3896
|
+
}, []);
|
|
3897
|
+
const handleSubmit = useCallback(() => {
|
|
3898
|
+
const inputVariables = {};
|
|
3899
|
+
for (const variableName of inputVariableNames) {
|
|
3900
|
+
inputVariables[variableName] = values[variableName] ?? "";
|
|
3901
|
+
}
|
|
3902
|
+
onRun(inputVariables);
|
|
3903
|
+
onClose();
|
|
3904
|
+
setValues({});
|
|
3905
|
+
}, [inputVariableNames, values, onRun, onClose]);
|
|
3906
|
+
const handleClose = useCallback(() => {
|
|
3907
|
+
onClose();
|
|
3908
|
+
setValues({});
|
|
3909
|
+
}, [onClose]);
|
|
3910
|
+
if (!open) return null;
|
|
3911
|
+
return /* @__PURE__ */ jsxs(
|
|
3912
|
+
GlassModal,
|
|
3913
|
+
{
|
|
3914
|
+
open,
|
|
3915
|
+
onClose: handleClose,
|
|
3916
|
+
title: t("title"),
|
|
3917
|
+
subtitle: t("subtitle"),
|
|
3918
|
+
maxWidth: "md",
|
|
3919
|
+
children: [
|
|
3920
|
+
/* @__PURE__ */ jsx("div", { className: "space-y-3", children: inputVariableNames.length === 0 ? /* @__PURE__ */ jsx("p", { className: "text-center text-sm text-gray-500 dark:text-gray-400", children: t("noInputs") }) : inputVariableNames.map((variableName) => /* @__PURE__ */ jsxs("div", { children: [
|
|
3921
|
+
/* @__PURE__ */ jsx("label", { className: "mb-1 block text-xs font-medium text-gray-700 dark:text-gray-300", children: variableName }),
|
|
3922
|
+
/* @__PURE__ */ jsx(
|
|
3923
|
+
Input,
|
|
3924
|
+
{
|
|
3925
|
+
value: values[variableName] ?? "",
|
|
3926
|
+
onChange: (event) => handleValueChange(variableName, event.target.value),
|
|
3927
|
+
placeholder: `${t("variableValue")}...`
|
|
3928
|
+
}
|
|
3929
|
+
)
|
|
3930
|
+
] }, variableName)) }),
|
|
3931
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-4 flex items-center justify-end gap-2", children: [
|
|
3932
|
+
/* @__PURE__ */ jsx(Button, { size: "sm", plain: true, onClick: handleClose, children: t("cancel") }),
|
|
3933
|
+
/* @__PURE__ */ jsxs(Button, { size: "sm", gradient: true, onClick: handleSubmit, children: [
|
|
3934
|
+
/* @__PURE__ */ jsx(PlayIcon, { className: "h-3.5 w-3.5", "data-slot": "icon" }),
|
|
3935
|
+
t("run")
|
|
3936
|
+
] })
|
|
3937
|
+
] })
|
|
3938
|
+
]
|
|
3939
|
+
}
|
|
3940
|
+
);
|
|
3941
|
+
}
|
|
3942
|
+
var STATUS_STYLES = {
|
|
3943
|
+
completed: { icon: CheckCircleIcon, colorClass: "text-green-500" },
|
|
3944
|
+
failed: { icon: ExclamationCircleIcon, colorClass: "text-red-500" },
|
|
3945
|
+
running: { icon: ArrowPathIcon, colorClass: "text-blue-500 animate-spin" },
|
|
3946
|
+
pending: { icon: ClockIcon, colorClass: "text-gray-400" },
|
|
3947
|
+
success: { icon: CheckCircleIcon, colorClass: "text-green-500" },
|
|
3948
|
+
error: { icon: ExclamationCircleIcon, colorClass: "text-red-500" },
|
|
3949
|
+
skipped: { icon: ClockIcon, colorClass: "text-gray-400" }
|
|
3950
|
+
};
|
|
3951
|
+
function formatDuration3(durationMs) {
|
|
3952
|
+
if (durationMs === null) return "\u2014";
|
|
3953
|
+
if (durationMs < 1e3) return `${durationMs}ms`;
|
|
3954
|
+
return `${(durationMs / 1e3).toFixed(1)}s`;
|
|
3955
|
+
}
|
|
3956
|
+
function formatTimestamp(timestamp) {
|
|
3957
|
+
const date = new Date(timestamp);
|
|
3958
|
+
return date.toLocaleTimeString(void 0, { hour: "2-digit", minute: "2-digit", second: "2-digit" });
|
|
3959
|
+
}
|
|
3960
|
+
function PreviewPanel({ open, onClose, workflowId, loadRuns }) {
|
|
3961
|
+
const t = useTranslations("agents.workflow.previewPanel");
|
|
3962
|
+
const [runs, setRuns] = useState([]);
|
|
3963
|
+
const [isLoadingRuns, setIsLoadingRuns] = useState(false);
|
|
3964
|
+
const [selectedRun, setSelectedRun] = useState(null);
|
|
3965
|
+
const [selectedNode, setSelectedNode] = useState(null);
|
|
3966
|
+
const isRunning = useWorkflowStore((state) => state.isRunning);
|
|
3967
|
+
const nodeResults = useWorkflowStore((state) => state.nodeResults);
|
|
3968
|
+
const refreshRuns = useCallback(async () => {
|
|
3969
|
+
setIsLoadingRuns(true);
|
|
3970
|
+
try {
|
|
3971
|
+
const workflowRuns = await loadRuns(workflowId);
|
|
3972
|
+
setRuns(workflowRuns);
|
|
3973
|
+
} finally {
|
|
3974
|
+
setIsLoadingRuns(false);
|
|
3975
|
+
}
|
|
3976
|
+
}, [loadRuns, workflowId]);
|
|
3977
|
+
useEffect(() => {
|
|
3978
|
+
if (open) void refreshRuns();
|
|
3979
|
+
}, [open, refreshRuns]);
|
|
3980
|
+
useEffect(() => {
|
|
3981
|
+
if (!isRunning && open) void refreshRuns();
|
|
3982
|
+
}, [isRunning, open, refreshRuns]);
|
|
3983
|
+
if (!open) return null;
|
|
3984
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3985
|
+
/* @__PURE__ */ jsx("div", { className: "fixed inset-x-0 top-[120px] bottom-0 z-40 bg-black/10 backdrop-blur-[1px]", onClick: onClose }),
|
|
3986
|
+
/* @__PURE__ */ jsxs("div", { className: "fixed right-0 top-[120px] bottom-0 z-50 w-96 overflow-hidden border-l border-white/20 bg-white/95 shadow-2xl backdrop-blur-xl dark:border-gray-700/50 dark:bg-gray-800/95", children: [
|
|
3987
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between border-b border-gray-200/50 px-4 py-3 dark:border-gray-700/50", children: [
|
|
3988
|
+
/* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold text-gray-900 dark:text-white", children: t("title") }),
|
|
3989
|
+
/* @__PURE__ */ jsx(
|
|
3990
|
+
"button",
|
|
3991
|
+
{
|
|
3992
|
+
type: "button",
|
|
3993
|
+
onClick: onClose,
|
|
3994
|
+
className: "rounded-lg p-1 text-gray-400 hover:bg-gray-100 hover:text-gray-600 dark:hover:bg-gray-700 dark:hover:text-gray-300",
|
|
3995
|
+
children: /* @__PURE__ */ jsx(XMarkIcon, { className: "h-4 w-4" })
|
|
3996
|
+
}
|
|
3997
|
+
)
|
|
3998
|
+
] }),
|
|
3999
|
+
/* @__PURE__ */ jsxs("div", { className: "flex h-full flex-col overflow-hidden", children: [
|
|
4000
|
+
isRunning && /* @__PURE__ */ jsxs("div", { className: "border-b border-gray-200/50 px-4 py-3 dark:border-gray-700/50", children: [
|
|
4001
|
+
/* @__PURE__ */ jsxs("div", { className: "mb-2 flex items-center gap-2", children: [
|
|
4002
|
+
/* @__PURE__ */ jsx(ArrowPathIcon, { className: "h-4 w-4 animate-spin text-blue-500" }),
|
|
4003
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs font-semibold text-blue-600 dark:text-blue-400", children: t("activeRun") })
|
|
4004
|
+
] }),
|
|
4005
|
+
/* @__PURE__ */ jsx("div", { className: "max-h-60 space-y-1 overflow-y-auto", children: Object.entries(nodeResults).map(([nodeId, result]) => {
|
|
4006
|
+
const statusStyle = STATUS_STYLES[result.status] ?? STATUS_STYLES.pending;
|
|
4007
|
+
const StatusIcon = statusStyle.icon;
|
|
4008
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between rounded-lg px-2 py-1.5 text-xs hover:bg-gray-50 dark:hover:bg-gray-700/50", children: [
|
|
4009
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
4010
|
+
/* @__PURE__ */ jsx(StatusIcon, { className: `h-3.5 w-3.5 ${statusStyle.colorClass}` }),
|
|
4011
|
+
/* @__PURE__ */ jsx("span", { className: "truncate text-gray-700 dark:text-gray-300", children: nodeId.slice(0, 8) })
|
|
4012
|
+
] }),
|
|
4013
|
+
result.durationMs !== void 0 && /* @__PURE__ */ jsx("span", { className: "text-gray-400", children: formatDuration3(result.durationMs) })
|
|
4014
|
+
] }, nodeId);
|
|
4015
|
+
}) })
|
|
4016
|
+
] }),
|
|
4017
|
+
selectedNode && /* @__PURE__ */ jsxs("div", { className: "border-b border-gray-200/50 px-4 py-3 dark:border-gray-700/50", children: [
|
|
4018
|
+
/* @__PURE__ */ jsxs("div", { className: "mb-2 flex items-center justify-between", children: [
|
|
4019
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs font-semibold text-gray-900 dark:text-white", children: t("nodeDetail") }),
|
|
4020
|
+
/* @__PURE__ */ jsx("button", { type: "button", onClick: () => setSelectedNode(null), className: "text-[10px] text-gray-400 hover:text-gray-600 dark:hover:text-gray-300", children: t("close") })
|
|
4021
|
+
] }),
|
|
4022
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-2 text-xs", children: [
|
|
4023
|
+
/* @__PURE__ */ jsxs("div", { className: "flex justify-between", children: [
|
|
4024
|
+
/* @__PURE__ */ jsx("span", { className: "text-gray-500", children: t("status") }),
|
|
4025
|
+
/* @__PURE__ */ jsx("span", { className: STATUS_STYLES[selectedNode.status]?.colorClass ?? "text-gray-400", children: t(selectedNode.status) })
|
|
4026
|
+
] }),
|
|
4027
|
+
/* @__PURE__ */ jsxs("div", { className: "flex justify-between", children: [
|
|
4028
|
+
/* @__PURE__ */ jsx("span", { className: "text-gray-500", children: t("duration") }),
|
|
4029
|
+
/* @__PURE__ */ jsx("span", { className: "text-gray-700 dark:text-gray-300", children: formatDuration3(selectedNode.durationMs) })
|
|
4030
|
+
] }),
|
|
4031
|
+
selectedNode.error && /* @__PURE__ */ jsxs("div", { children: [
|
|
4032
|
+
/* @__PURE__ */ jsx("span", { className: "text-red-500", children: t("error") }),
|
|
4033
|
+
/* @__PURE__ */ jsx("pre", { className: "mt-1 max-h-20 overflow-auto whitespace-pre-wrap rounded bg-red-50 p-2 text-[10px] text-red-700 dark:bg-red-900/20 dark:text-red-300", children: selectedNode.error })
|
|
4034
|
+
] }),
|
|
4035
|
+
Object.keys(selectedNode.outputs).length > 0 && /* @__PURE__ */ jsxs("div", { children: [
|
|
4036
|
+
/* @__PURE__ */ jsx("span", { className: "text-gray-500", children: t("outputs") }),
|
|
4037
|
+
/* @__PURE__ */ jsx("pre", { className: "mt-1 max-h-32 overflow-auto whitespace-pre-wrap rounded bg-gray-50 p-2 text-[10px] text-gray-700 dark:bg-gray-700/50 dark:text-gray-300", children: JSON.stringify(selectedNode.outputs, null, 2) })
|
|
4038
|
+
] })
|
|
4039
|
+
] })
|
|
4040
|
+
] }),
|
|
4041
|
+
/* @__PURE__ */ jsxs("div", { className: "flex-1 overflow-y-auto px-4 py-3", children: [
|
|
4042
|
+
/* @__PURE__ */ jsx("h4", { className: "mb-2 text-xs font-semibold text-gray-500 dark:text-gray-400", children: t("runHistory") }),
|
|
4043
|
+
isLoadingRuns && /* @__PURE__ */ jsx("div", { className: "space-y-2", children: [1, 2, 3].map((index) => /* @__PURE__ */ jsx("div", { className: "shimmer h-14 rounded-lg" }, index)) }),
|
|
4044
|
+
!isLoadingRuns && runs.length === 0 && /* @__PURE__ */ jsxs("div", { className: "py-8 text-center", children: [
|
|
4045
|
+
/* @__PURE__ */ jsx(ClockIcon, { className: "mx-auto mb-2 h-8 w-8 text-gray-300 dark:text-gray-600" }),
|
|
4046
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs text-gray-500 dark:text-gray-400", children: t("noRuns") }),
|
|
4047
|
+
/* @__PURE__ */ jsx("p", { className: "mt-1 text-[10px] text-gray-400 dark:text-gray-500", children: t("noRunsDescription") })
|
|
4048
|
+
] }),
|
|
4049
|
+
!isLoadingRuns && runs.length > 0 && /* @__PURE__ */ jsx("div", { className: "space-y-1.5", children: runs.map((run) => {
|
|
4050
|
+
const statusStyle = STATUS_STYLES[run.status] ?? STATUS_STYLES.pending;
|
|
4051
|
+
const StatusIcon = statusStyle.icon;
|
|
4052
|
+
const isSelected = selectedRun?.id === run.id;
|
|
4053
|
+
return /* @__PURE__ */ jsxs(
|
|
4054
|
+
"button",
|
|
4055
|
+
{
|
|
4056
|
+
type: "button",
|
|
4057
|
+
onClick: () => {
|
|
4058
|
+
setSelectedRun(isSelected ? null : run);
|
|
4059
|
+
setSelectedNode(null);
|
|
4060
|
+
},
|
|
4061
|
+
className: `w-full rounded-lg border px-3 py-2.5 text-left transition-colors ${isSelected ? "border-blue-200 bg-blue-50/50 dark:border-blue-800 dark:bg-blue-900/20" : "border-gray-100 hover:border-gray-200 hover:bg-gray-50 dark:border-gray-700/50 dark:hover:border-gray-600 dark:hover:bg-gray-700/30"}`,
|
|
4062
|
+
children: [
|
|
4063
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
|
|
4064
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
4065
|
+
/* @__PURE__ */ jsx(StatusIcon, { className: `h-3.5 w-3.5 ${statusStyle.colorClass}` }),
|
|
4066
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-gray-900 dark:text-white", children: formatTimestamp(run.createdAt) })
|
|
4067
|
+
] }),
|
|
4068
|
+
/* @__PURE__ */ jsx("span", { className: "text-[10px] text-gray-400", children: formatDuration3(run.totalDurationMs) })
|
|
4069
|
+
] }),
|
|
4070
|
+
run.error && /* @__PURE__ */ jsx("p", { className: "mt-1 truncate text-[10px] text-red-500", children: run.error })
|
|
4071
|
+
]
|
|
4072
|
+
},
|
|
4073
|
+
run.id
|
|
4074
|
+
);
|
|
4075
|
+
}) }),
|
|
4076
|
+
selectedRun && selectedRun.nodeResults.length > 0 && /* @__PURE__ */ jsxs("div", { className: "mt-3 space-y-1", children: [
|
|
4077
|
+
/* @__PURE__ */ jsx("h5", { className: "mb-1.5 text-[10px] font-semibold uppercase tracking-wider text-gray-400", children: "Nodes" }),
|
|
4078
|
+
selectedRun.nodeResults.map((nodeResult) => {
|
|
4079
|
+
const nodeStatusStyle = STATUS_STYLES[nodeResult.status] ?? STATUS_STYLES.pending;
|
|
4080
|
+
const NodeStatusIcon = nodeStatusStyle.icon;
|
|
4081
|
+
return /* @__PURE__ */ jsxs(
|
|
4082
|
+
"button",
|
|
4083
|
+
{
|
|
4084
|
+
type: "button",
|
|
4085
|
+
onClick: () => setSelectedNode(nodeResult),
|
|
4086
|
+
className: "flex w-full items-center justify-between rounded px-2 py-1.5 text-xs hover:bg-gray-50 dark:hover:bg-gray-700/50",
|
|
4087
|
+
children: [
|
|
4088
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
4089
|
+
/* @__PURE__ */ jsx(NodeStatusIcon, { className: `h-3 w-3 ${nodeStatusStyle.colorClass}` }),
|
|
4090
|
+
/* @__PURE__ */ jsx("span", { className: "text-gray-700 dark:text-gray-300", children: nodeResult.nodeType })
|
|
4091
|
+
] }),
|
|
4092
|
+
/* @__PURE__ */ jsx("span", { className: "text-[10px] text-gray-400", children: formatDuration3(nodeResult.durationMs) })
|
|
4093
|
+
]
|
|
4094
|
+
},
|
|
4095
|
+
nodeResult.nodeId
|
|
4096
|
+
);
|
|
4097
|
+
})
|
|
4098
|
+
] })
|
|
4099
|
+
] })
|
|
4100
|
+
] })
|
|
4101
|
+
] })
|
|
4102
|
+
] });
|
|
4103
|
+
}
|
|
4104
|
+
function SaveStatusBadge({ status }) {
|
|
4105
|
+
if (status === "saving") {
|
|
4106
|
+
return /* @__PURE__ */ jsx("span", { className: "animate-pulse text-[10px] text-gray-400 dark:text-gray-500", children: "\u2022" });
|
|
4107
|
+
}
|
|
4108
|
+
if (status === "saved") {
|
|
4109
|
+
return /* @__PURE__ */ jsx("span", { className: "text-[10px] text-green-500/60 dark:text-green-400/50", children: "\u2713" });
|
|
4110
|
+
}
|
|
4111
|
+
return null;
|
|
4112
|
+
}
|
|
4113
|
+
var AutoSaveWorkspace = memo(function AutoSaveWorkspace2({
|
|
4114
|
+
onAutoSaveGraph,
|
|
4115
|
+
onGraphSnapshot,
|
|
4116
|
+
...workspaceProps
|
|
4117
|
+
}) {
|
|
4118
|
+
const handleGraphChange = useCallback((graph) => {
|
|
4119
|
+
onGraphSnapshot?.(graph);
|
|
4120
|
+
onAutoSaveGraph(graph);
|
|
4121
|
+
}, [onAutoSaveGraph, onGraphSnapshot]);
|
|
4122
|
+
return /* @__PURE__ */ jsx(
|
|
4123
|
+
Workspace,
|
|
4124
|
+
{
|
|
4125
|
+
...workspaceProps,
|
|
4126
|
+
onGraphChange: handleGraphChange
|
|
4127
|
+
}
|
|
4128
|
+
);
|
|
4129
|
+
});
|
|
4130
|
+
function DynamicIslandConfirm2(props) {
|
|
4131
|
+
return /* @__PURE__ */ jsx(DynamicIslandConfirm, { ...props });
|
|
4132
|
+
}
|
|
4133
|
+
var JSON_PREVIEW_LINE_LIMIT = 50;
|
|
4134
|
+
function DslExportModal({ open, onClose, workflow, graph }) {
|
|
4135
|
+
const translations = useTranslations("agents.workflow.dsl.export");
|
|
4136
|
+
const [isCopied, setIsCopied] = useState(false);
|
|
4137
|
+
const exportPayload = useMemo(() => ({
|
|
4138
|
+
name: workflow.name,
|
|
4139
|
+
description: workflow.description,
|
|
4140
|
+
version: workflow.version,
|
|
4141
|
+
exportedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4142
|
+
graph: {
|
|
4143
|
+
nodes: graph.nodes,
|
|
4144
|
+
edges: graph.edges,
|
|
4145
|
+
viewport: graph.viewport
|
|
4146
|
+
}
|
|
4147
|
+
}), [workflow, graph]);
|
|
4148
|
+
const jsonString = useMemo(() => JSON.stringify(exportPayload, null, 2), [exportPayload]);
|
|
4149
|
+
const previewLines = useMemo(() => {
|
|
4150
|
+
const lines = jsonString.split("\n");
|
|
4151
|
+
if (lines.length <= JSON_PREVIEW_LINE_LIMIT) {
|
|
4152
|
+
return jsonString;
|
|
4153
|
+
}
|
|
4154
|
+
return lines.slice(0, JSON_PREVIEW_LINE_LIMIT).join("\n") + "\n...";
|
|
4155
|
+
}, [jsonString]);
|
|
4156
|
+
const handleCopyToClipboard = useCallback(async () => {
|
|
4157
|
+
try {
|
|
4158
|
+
await navigator.clipboard.writeText(jsonString);
|
|
4159
|
+
setIsCopied(true);
|
|
4160
|
+
setTimeout(() => setIsCopied(false), 2e3);
|
|
4161
|
+
} catch {
|
|
4162
|
+
const textArea = document.createElement("textarea");
|
|
4163
|
+
textArea.value = jsonString;
|
|
4164
|
+
document.body.appendChild(textArea);
|
|
4165
|
+
textArea.select();
|
|
4166
|
+
document.execCommand("copy");
|
|
4167
|
+
document.body.removeChild(textArea);
|
|
4168
|
+
setIsCopied(true);
|
|
4169
|
+
setTimeout(() => setIsCopied(false), 2e3);
|
|
4170
|
+
}
|
|
4171
|
+
}, [jsonString]);
|
|
4172
|
+
const handleDownloadJson = useCallback(() => {
|
|
4173
|
+
const blob = new Blob([jsonString], { type: "application/json" });
|
|
4174
|
+
const url = URL.createObjectURL(blob);
|
|
4175
|
+
const downloadLink = document.createElement("a");
|
|
4176
|
+
downloadLink.href = url;
|
|
4177
|
+
downloadLink.download = `${workflow.name}-v${workflow.version}.json`;
|
|
4178
|
+
document.body.appendChild(downloadLink);
|
|
4179
|
+
downloadLink.click();
|
|
4180
|
+
document.body.removeChild(downloadLink);
|
|
4181
|
+
URL.revokeObjectURL(url);
|
|
4182
|
+
}, [jsonString, workflow.name, workflow.version]);
|
|
4183
|
+
if (!open) return null;
|
|
4184
|
+
return /* @__PURE__ */ jsx(
|
|
4185
|
+
GlassModal,
|
|
4186
|
+
{
|
|
4187
|
+
open,
|
|
4188
|
+
onClose,
|
|
4189
|
+
title: translations("title"),
|
|
4190
|
+
subtitle: translations("description"),
|
|
4191
|
+
maxWidth: "2xl",
|
|
4192
|
+
children: /* @__PURE__ */ jsxs("div", { "data-testid": "dsl-export-modal", children: [
|
|
4193
|
+
/* @__PURE__ */ jsx("div", { className: "border-b border-gray-200 px-1 py-4 dark:border-gray-700", children: /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-4 text-sm", children: [
|
|
4194
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
4195
|
+
/* @__PURE__ */ jsx("span", { className: "text-gray-500 dark:text-gray-400", children: translations("workflowName") }),
|
|
4196
|
+
/* @__PURE__ */ jsx("p", { className: "font-medium text-gray-900 dark:text-white", children: workflow.name })
|
|
4197
|
+
] }),
|
|
4198
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
4199
|
+
/* @__PURE__ */ jsx("span", { className: "text-gray-500 dark:text-gray-400", children: translations("version") }),
|
|
4200
|
+
/* @__PURE__ */ jsxs("p", { className: "font-medium text-gray-900 dark:text-white", children: [
|
|
4201
|
+
"v",
|
|
4202
|
+
workflow.version
|
|
4203
|
+
] })
|
|
4204
|
+
] }),
|
|
4205
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
4206
|
+
/* @__PURE__ */ jsx("span", { className: "text-gray-500 dark:text-gray-400", children: translations("nodes") }),
|
|
4207
|
+
/* @__PURE__ */ jsx("p", { className: "font-medium text-gray-900 dark:text-white", children: graph.nodes.length })
|
|
4208
|
+
] }),
|
|
4209
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
4210
|
+
/* @__PURE__ */ jsx("span", { className: "text-gray-500 dark:text-gray-400", children: translations("edges") }),
|
|
4211
|
+
/* @__PURE__ */ jsx("p", { className: "font-medium text-gray-900 dark:text-white", children: graph.edges.length })
|
|
4212
|
+
] })
|
|
4213
|
+
] }) }),
|
|
4214
|
+
/* @__PURE__ */ jsxs("div", { className: "px-1 py-4", children: [
|
|
4215
|
+
/* @__PURE__ */ jsx("div", { className: "mb-2 text-xs font-medium uppercase tracking-wider text-gray-400 dark:text-gray-500", children: translations("preview") }),
|
|
4216
|
+
/* @__PURE__ */ jsx(
|
|
4217
|
+
"pre",
|
|
4218
|
+
{
|
|
4219
|
+
className: "max-h-64 overflow-auto rounded-lg border border-gray-200 bg-gray-50 p-3 text-xs text-gray-700 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-300",
|
|
4220
|
+
"data-testid": "export-json-preview",
|
|
4221
|
+
children: previewLines
|
|
4222
|
+
}
|
|
4223
|
+
)
|
|
4224
|
+
] }),
|
|
4225
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-end gap-3 border-t border-gray-200 px-1 py-4 dark:border-gray-700", children: [
|
|
4226
|
+
/* @__PURE__ */ jsx(
|
|
4227
|
+
Button,
|
|
4228
|
+
{
|
|
4229
|
+
type: "button",
|
|
4230
|
+
onClick: handleCopyToClipboard,
|
|
4231
|
+
outline: true,
|
|
4232
|
+
size: "sm",
|
|
4233
|
+
"data-testid": "export-copy-button",
|
|
4234
|
+
children: isCopied ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
4235
|
+
/* @__PURE__ */ jsx(CheckIcon, { className: "h-4 w-4 text-green-500" }),
|
|
4236
|
+
/* @__PURE__ */ jsx("span", { role: "status", "aria-live": "polite", children: translations("copied") })
|
|
4237
|
+
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
4238
|
+
/* @__PURE__ */ jsx(ClipboardDocumentIcon, { className: "h-4 w-4" }),
|
|
4239
|
+
translations("copyToClipboard")
|
|
4240
|
+
] })
|
|
4241
|
+
}
|
|
4242
|
+
),
|
|
4243
|
+
/* @__PURE__ */ jsxs(
|
|
4244
|
+
Button,
|
|
4245
|
+
{
|
|
4246
|
+
type: "button",
|
|
4247
|
+
onClick: handleDownloadJson,
|
|
4248
|
+
color: "ios-glass-blue",
|
|
4249
|
+
size: "sm",
|
|
4250
|
+
"data-testid": "export-download-button",
|
|
4251
|
+
children: [
|
|
4252
|
+
/* @__PURE__ */ jsx(ArrowDownTrayIcon, { className: "h-4 w-4" }),
|
|
4253
|
+
translations("downloadJson")
|
|
4254
|
+
]
|
|
4255
|
+
}
|
|
4256
|
+
)
|
|
4257
|
+
] })
|
|
4258
|
+
] })
|
|
4259
|
+
}
|
|
4260
|
+
);
|
|
4261
|
+
}
|
|
4262
|
+
function validateWorkflowJson(jsonString) {
|
|
4263
|
+
const errors = [];
|
|
4264
|
+
let parsed;
|
|
4265
|
+
try {
|
|
4266
|
+
parsed = JSON.parse(jsonString);
|
|
4267
|
+
} catch {
|
|
4268
|
+
return { isValid: false, errors: ["Invalid JSON format"], graph: null, nodeCount: 0, edgeCount: 0 };
|
|
4269
|
+
}
|
|
4270
|
+
if (typeof parsed !== "object" || parsed === null) {
|
|
4271
|
+
return { isValid: false, errors: ["Root must be a JSON object"], graph: null, nodeCount: 0, edgeCount: 0 };
|
|
4272
|
+
}
|
|
4273
|
+
const data = parsed;
|
|
4274
|
+
const graphData = data.graph ? data.graph : data;
|
|
4275
|
+
if (!Array.isArray(graphData.nodes)) {
|
|
4276
|
+
errors.push('Missing or invalid "nodes" array');
|
|
4277
|
+
}
|
|
4278
|
+
if (!Array.isArray(graphData.edges)) {
|
|
4279
|
+
errors.push('Missing or invalid "edges" array');
|
|
4280
|
+
}
|
|
4281
|
+
if (!graphData.viewport || typeof graphData.viewport !== "object") {
|
|
4282
|
+
errors.push('Missing or invalid "viewport" object');
|
|
4283
|
+
}
|
|
4284
|
+
if (errors.length > 0) {
|
|
4285
|
+
return { isValid: false, errors, graph: null, nodeCount: 0, edgeCount: 0 };
|
|
4286
|
+
}
|
|
4287
|
+
const nodes = graphData.nodes;
|
|
4288
|
+
const edges = graphData.edges;
|
|
4289
|
+
for (let index = 0; index < nodes.length; index++) {
|
|
4290
|
+
const node = nodes[index];
|
|
4291
|
+
if (!node.id || typeof node.id !== "string") {
|
|
4292
|
+
errors.push(`Node at index ${index}: missing or invalid "id"`);
|
|
4293
|
+
}
|
|
4294
|
+
if (!node.type || typeof node.type !== "string") {
|
|
4295
|
+
errors.push(`Node at index ${index}: missing or invalid "type"`);
|
|
4296
|
+
}
|
|
4297
|
+
if (!node.position || typeof node.position !== "object") {
|
|
4298
|
+
errors.push(`Node at index ${index}: missing or invalid "position"`);
|
|
4299
|
+
}
|
|
4300
|
+
}
|
|
4301
|
+
for (let index = 0; index < edges.length; index++) {
|
|
4302
|
+
const edge = edges[index];
|
|
4303
|
+
if (!edge.id || typeof edge.id !== "string") {
|
|
4304
|
+
errors.push(`Edge at index ${index}: missing or invalid "id"`);
|
|
4305
|
+
}
|
|
4306
|
+
if (!edge.source || typeof edge.source !== "string") {
|
|
4307
|
+
errors.push(`Edge at index ${index}: missing or invalid "source"`);
|
|
4308
|
+
}
|
|
4309
|
+
if (!edge.target || typeof edge.target !== "string") {
|
|
4310
|
+
errors.push(`Edge at index ${index}: missing or invalid "target"`);
|
|
4311
|
+
}
|
|
4312
|
+
}
|
|
4313
|
+
if (errors.length > 0) {
|
|
4314
|
+
return { isValid: false, errors, graph: null, nodeCount: nodes.length, edgeCount: edges.length };
|
|
4315
|
+
}
|
|
4316
|
+
const viewport = graphData.viewport;
|
|
4317
|
+
const graph = {
|
|
4318
|
+
nodes: nodes.map((node) => ({
|
|
4319
|
+
id: node.id,
|
|
4320
|
+
type: node.type,
|
|
4321
|
+
position: node.position,
|
|
4322
|
+
data: node.data ?? { entityId: "", label: "" }
|
|
4323
|
+
})),
|
|
4324
|
+
edges: edges.map((edge) => ({
|
|
4325
|
+
id: edge.id,
|
|
4326
|
+
source: edge.source,
|
|
4327
|
+
target: edge.target,
|
|
4328
|
+
sourceHandle: edge.sourceHandle ?? "default",
|
|
4329
|
+
targetHandle: edge.targetHandle ?? "default"
|
|
4330
|
+
})),
|
|
4331
|
+
viewport: {
|
|
4332
|
+
x: typeof viewport.x === "number" ? viewport.x : 0,
|
|
4333
|
+
y: typeof viewport.y === "number" ? viewport.y : 0,
|
|
4334
|
+
zoom: typeof viewport.zoom === "number" ? viewport.zoom : 1
|
|
4335
|
+
}
|
|
4336
|
+
};
|
|
4337
|
+
return { isValid: true, errors: [], graph, nodeCount: nodes.length, edgeCount: edges.length };
|
|
4338
|
+
}
|
|
4339
|
+
function DslImportModal({ open, onClose, onImport }) {
|
|
4340
|
+
const translations = useTranslations("agents.workflow.dsl.import");
|
|
4341
|
+
const fileInputRef = useRef(null);
|
|
4342
|
+
const [isDragActive, setIsDragActive] = useState(false);
|
|
4343
|
+
const [validationResult, setValidationResult] = useState(null);
|
|
4344
|
+
const [fileName, setFileName] = useState(null);
|
|
4345
|
+
const processFile = useCallback((file) => {
|
|
4346
|
+
setFileName(file.name);
|
|
4347
|
+
const reader = new FileReader();
|
|
4348
|
+
reader.onload = (event) => {
|
|
4349
|
+
const content = event.target?.result;
|
|
4350
|
+
if (typeof content === "string") {
|
|
4351
|
+
const result = validateWorkflowJson(content);
|
|
4352
|
+
setValidationResult(result);
|
|
4353
|
+
}
|
|
4354
|
+
};
|
|
4355
|
+
reader.readAsText(file);
|
|
4356
|
+
}, []);
|
|
4357
|
+
const handleDragOver = useCallback((event) => {
|
|
4358
|
+
event.preventDefault();
|
|
4359
|
+
setIsDragActive(true);
|
|
4360
|
+
}, []);
|
|
4361
|
+
const handleDragLeave = useCallback((event) => {
|
|
4362
|
+
event.preventDefault();
|
|
4363
|
+
setIsDragActive(false);
|
|
4364
|
+
}, []);
|
|
4365
|
+
const handleDrop = useCallback(
|
|
4366
|
+
(event) => {
|
|
4367
|
+
event.preventDefault();
|
|
4368
|
+
setIsDragActive(false);
|
|
4369
|
+
const files = event.dataTransfer.files;
|
|
4370
|
+
if (files.length > 0) {
|
|
4371
|
+
processFile(files[0]);
|
|
4372
|
+
}
|
|
4373
|
+
},
|
|
4374
|
+
[processFile]
|
|
4375
|
+
);
|
|
4376
|
+
const handleFileChange = useCallback(
|
|
4377
|
+
(event) => {
|
|
4378
|
+
const files = event.target.files;
|
|
4379
|
+
if (files && files.length > 0) {
|
|
4380
|
+
processFile(files[0]);
|
|
4381
|
+
}
|
|
4382
|
+
},
|
|
4383
|
+
[processFile]
|
|
4384
|
+
);
|
|
4385
|
+
const handleClickUpload = useCallback(() => {
|
|
4386
|
+
fileInputRef.current?.click();
|
|
4387
|
+
}, []);
|
|
4388
|
+
const handleImport = useCallback(() => {
|
|
4389
|
+
if (validationResult?.isValid && validationResult.graph) {
|
|
4390
|
+
onImport(validationResult.graph);
|
|
4391
|
+
onClose();
|
|
4392
|
+
}
|
|
4393
|
+
}, [validationResult, onImport, onClose]);
|
|
4394
|
+
const handleReset = useCallback(() => {
|
|
4395
|
+
setValidationResult(null);
|
|
4396
|
+
setFileName(null);
|
|
4397
|
+
if (fileInputRef.current) {
|
|
4398
|
+
fileInputRef.current.value = "";
|
|
4399
|
+
}
|
|
4400
|
+
}, []);
|
|
4401
|
+
if (!open) return null;
|
|
4402
|
+
return /* @__PURE__ */ jsx(
|
|
4403
|
+
GlassModal,
|
|
4404
|
+
{
|
|
4405
|
+
open,
|
|
4406
|
+
onClose,
|
|
4407
|
+
title: translations("title"),
|
|
4408
|
+
subtitle: translations("description"),
|
|
4409
|
+
maxWidth: "lg",
|
|
4410
|
+
children: /* @__PURE__ */ jsxs("div", { "data-testid": "dsl-import-modal", children: [
|
|
4411
|
+
/* @__PURE__ */ jsx("div", { className: "px-1 py-2", children: !validationResult ? /* @__PURE__ */ jsxs(
|
|
4412
|
+
"div",
|
|
4413
|
+
{
|
|
4414
|
+
onDragOver: handleDragOver,
|
|
4415
|
+
onDragLeave: handleDragLeave,
|
|
4416
|
+
onDrop: handleDrop,
|
|
4417
|
+
onClick: handleClickUpload,
|
|
4418
|
+
className: `cursor-pointer rounded-xl border-2 border-dashed p-8 text-center transition-colors duration-200 ${isDragActive ? "border-blue-400 bg-blue-50 dark:border-blue-500 dark:bg-blue-500/10" : "border-gray-300 hover:border-gray-400 dark:border-gray-600 dark:hover:border-gray-500"}`,
|
|
4419
|
+
"data-testid": "import-dropzone",
|
|
4420
|
+
role: "button",
|
|
4421
|
+
tabIndex: 0,
|
|
4422
|
+
"aria-label": translations("dropzone"),
|
|
4423
|
+
children: [
|
|
4424
|
+
/* @__PURE__ */ jsx(ArrowUpTrayIcon, { className: "mx-auto h-10 w-10 text-gray-400 dark:text-gray-500" }),
|
|
4425
|
+
/* @__PURE__ */ jsx("p", { className: "mt-3 text-sm font-medium text-gray-700 dark:text-gray-300", children: translations("dropzone") }),
|
|
4426
|
+
/* @__PURE__ */ jsx("p", { className: "mt-1 text-xs text-gray-500 dark:text-gray-400", children: translations("dropzoneHint") }),
|
|
4427
|
+
/* @__PURE__ */ jsx(
|
|
4428
|
+
"input",
|
|
4429
|
+
{
|
|
4430
|
+
ref: fileInputRef,
|
|
4431
|
+
type: "file",
|
|
4432
|
+
accept: ".json",
|
|
4433
|
+
onChange: handleFileChange,
|
|
4434
|
+
className: "hidden",
|
|
4435
|
+
"data-testid": "import-file-input"
|
|
4436
|
+
}
|
|
4437
|
+
)
|
|
4438
|
+
]
|
|
4439
|
+
}
|
|
4440
|
+
) : /* @__PURE__ */ jsxs("div", { "data-testid": "import-validation-result", children: [
|
|
4441
|
+
/* @__PURE__ */ jsxs("div", { className: "mb-4 flex items-center justify-between", children: [
|
|
4442
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-gray-700 dark:text-gray-300", children: fileName }),
|
|
4443
|
+
/* @__PURE__ */ jsx(Button, { type: "button", plain: true, size: "sm", onClick: handleReset, children: translations("chooseAnother") })
|
|
4444
|
+
] }),
|
|
4445
|
+
validationResult.isValid ? /* @__PURE__ */ jsxs("div", { className: "rounded-lg border border-green-200 bg-green-50 p-4 dark:border-green-800 dark:bg-green-900/20", children: [
|
|
4446
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
4447
|
+
/* @__PURE__ */ jsx(CheckCircleIcon, { className: "h-5 w-5 text-green-500" }),
|
|
4448
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-green-700 dark:text-green-400", children: translations("validationSuccess") })
|
|
4449
|
+
] }),
|
|
4450
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-2 grid grid-cols-2 gap-2 text-sm", children: [
|
|
4451
|
+
/* @__PURE__ */ jsxs("div", { className: "text-green-600 dark:text-green-400", children: [
|
|
4452
|
+
translations("nodes"),
|
|
4453
|
+
": ",
|
|
4454
|
+
validationResult.nodeCount
|
|
4455
|
+
] }),
|
|
4456
|
+
/* @__PURE__ */ jsxs("div", { className: "text-green-600 dark:text-green-400", children: [
|
|
4457
|
+
translations("edges"),
|
|
4458
|
+
": ",
|
|
4459
|
+
validationResult.edgeCount
|
|
4460
|
+
] })
|
|
4461
|
+
] })
|
|
4462
|
+
] }) : /* @__PURE__ */ jsxs("div", { role: "alert", className: "rounded-lg border border-red-200 bg-red-50 p-4 dark:border-red-800 dark:bg-red-900/20", children: [
|
|
4463
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
4464
|
+
/* @__PURE__ */ jsx(ExclamationTriangleIcon, { className: "h-5 w-5 text-red-500" }),
|
|
4465
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-red-700 dark:text-red-400", children: translations("validationFailed") })
|
|
4466
|
+
] }),
|
|
4467
|
+
/* @__PURE__ */ jsx("ul", { className: "mt-2 space-y-1", children: validationResult.errors.map((validationError, index) => /* @__PURE__ */ jsx("li", { className: "text-xs text-red-600 dark:text-red-400", children: validationError }, index)) })
|
|
4468
|
+
] })
|
|
4469
|
+
] }) }),
|
|
4470
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-end gap-3 border-t border-gray-200 px-1 py-4 dark:border-gray-700", children: [
|
|
4471
|
+
/* @__PURE__ */ jsx(Button, { type: "button", onClick: onClose, outline: true, size: "sm", "data-testid": "import-cancel-button", children: translations("cancel") }),
|
|
4472
|
+
/* @__PURE__ */ jsxs(
|
|
4473
|
+
Button,
|
|
4474
|
+
{
|
|
4475
|
+
type: "button",
|
|
4476
|
+
onClick: handleImport,
|
|
4477
|
+
disabled: !validationResult?.isValid,
|
|
4478
|
+
color: "ios-glass-blue",
|
|
4479
|
+
size: "sm",
|
|
4480
|
+
"data-testid": "import-confirm-button",
|
|
4481
|
+
children: [
|
|
4482
|
+
/* @__PURE__ */ jsx(ArrowUpTrayIcon, { className: "h-4 w-4" }),
|
|
4483
|
+
translations("import")
|
|
4484
|
+
]
|
|
4485
|
+
}
|
|
4486
|
+
)
|
|
4487
|
+
] })
|
|
4488
|
+
] })
|
|
4489
|
+
}
|
|
4490
|
+
);
|
|
4491
|
+
}
|
|
4492
|
+
|
|
4493
|
+
// src/astrlabe/store/selectors.ts
|
|
4494
|
+
var useCanUndo = () => useWorkflowStore((state) => state.past.length > 0);
|
|
4495
|
+
var useCanRedo = () => useWorkflowStore((state) => state.future.length > 0);
|
|
4496
|
+
var useHasCopied = () => useWorkflowStore((state) => state.clipboard !== null);
|
|
4497
|
+
var useContextMenu = () => useWorkflowStore((state) => state.contextMenu);
|
|
4498
|
+
var useEditingNodeId = () => useWorkflowStore((state) => state.editingNodeId);
|
|
4499
|
+
var useSelectedNodeCount = () => useWorkflowStore((state) => state.nodes.filter((node) => node.selected).length);
|
|
4500
|
+
var useIsRunning = () => useWorkflowStore((state) => state.isRunning);
|
|
4501
|
+
var useNodeResults = () => useWorkflowStore((state) => state.nodeResults);
|
|
4502
|
+
var DEFAULT_MAX_HISTORY = 50;
|
|
4503
|
+
function useUndoRedo(nodes, edges, setNodes, setEdges, maxHistory = DEFAULT_MAX_HISTORY) {
|
|
4504
|
+
const pastRef = useRef([]);
|
|
4505
|
+
const futureRef = useRef([]);
|
|
4506
|
+
const pastLengthRef = useRef(0);
|
|
4507
|
+
const futureLengthRef = useRef(0);
|
|
4508
|
+
const takeSnapshot = useCallback(() => {
|
|
4509
|
+
const snapshot = {
|
|
4510
|
+
nodes: nodes.map((node) => ({ ...node, data: { ...node.data } })),
|
|
4511
|
+
edges: edges.map((edge) => ({ ...edge }))
|
|
4512
|
+
};
|
|
4513
|
+
pastRef.current = [...pastRef.current.slice(-(maxHistory - 1)), snapshot];
|
|
4514
|
+
pastLengthRef.current = pastRef.current.length;
|
|
4515
|
+
futureRef.current = [];
|
|
4516
|
+
futureLengthRef.current = 0;
|
|
4517
|
+
}, [nodes, edges, maxHistory]);
|
|
4518
|
+
const undo = useCallback(() => {
|
|
4519
|
+
const past = pastRef.current;
|
|
4520
|
+
if (past.length === 0) return;
|
|
4521
|
+
const previous = past[past.length - 1];
|
|
4522
|
+
const remaining = past.slice(0, -1);
|
|
4523
|
+
const currentSnapshot = {
|
|
4524
|
+
nodes: nodes.map((node) => ({ ...node, data: { ...node.data } })),
|
|
4525
|
+
edges: edges.map((edge) => ({ ...edge }))
|
|
4526
|
+
};
|
|
4527
|
+
pastRef.current = remaining;
|
|
4528
|
+
pastLengthRef.current = remaining.length;
|
|
4529
|
+
futureRef.current = [...futureRef.current, currentSnapshot];
|
|
4530
|
+
futureLengthRef.current = futureRef.current.length;
|
|
4531
|
+
setNodes(previous.nodes);
|
|
4532
|
+
setEdges(previous.edges);
|
|
4533
|
+
}, [nodes, edges, setNodes, setEdges]);
|
|
4534
|
+
const redo = useCallback(() => {
|
|
4535
|
+
const future = futureRef.current;
|
|
4536
|
+
if (future.length === 0) return;
|
|
4537
|
+
const next = future[future.length - 1];
|
|
4538
|
+
const remaining = future.slice(0, -1);
|
|
4539
|
+
const currentSnapshot = {
|
|
4540
|
+
nodes: nodes.map((node) => ({ ...node, data: { ...node.data } })),
|
|
4541
|
+
edges: edges.map((edge) => ({ ...edge }))
|
|
4542
|
+
};
|
|
4543
|
+
futureRef.current = remaining;
|
|
4544
|
+
futureLengthRef.current = remaining.length;
|
|
4545
|
+
pastRef.current = [...pastRef.current, currentSnapshot];
|
|
4546
|
+
pastLengthRef.current = pastRef.current.length;
|
|
4547
|
+
setNodes(next.nodes);
|
|
4548
|
+
setEdges(next.edges);
|
|
4549
|
+
}, [nodes, edges, setNodes, setEdges]);
|
|
4550
|
+
return {
|
|
4551
|
+
undo,
|
|
4552
|
+
redo,
|
|
4553
|
+
canUndo: pastRef.current.length > 0,
|
|
4554
|
+
canRedo: futureRef.current.length > 0,
|
|
4555
|
+
takeSnapshot
|
|
4556
|
+
};
|
|
4557
|
+
}
|
|
4558
|
+
var PASTE_OFFSET = 40;
|
|
4559
|
+
function useClipboard(nodes, edges, setNodes, setEdges, takeSnapshot) {
|
|
4560
|
+
const clipboardRef = useRef(null);
|
|
4561
|
+
const copy = useCallback(() => {
|
|
4562
|
+
const selectedNodes = nodes.filter((node) => node.selected);
|
|
4563
|
+
if (selectedNodes.length === 0) return;
|
|
4564
|
+
const selectedNodeIds = new Set(selectedNodes.map((node) => node.id));
|
|
4565
|
+
const internalEdges = edges.filter(
|
|
4566
|
+
(edge) => selectedNodeIds.has(edge.source) && selectedNodeIds.has(edge.target)
|
|
4567
|
+
);
|
|
4568
|
+
clipboardRef.current = {
|
|
4569
|
+
nodes: selectedNodes.map((node) => ({ ...node, data: { ...node.data } })),
|
|
4570
|
+
edges: internalEdges.map((edge) => ({ ...edge }))
|
|
4571
|
+
};
|
|
4572
|
+
}, [nodes, edges]);
|
|
4573
|
+
const paste = useCallback(() => {
|
|
4574
|
+
const clipboard = clipboardRef.current;
|
|
4575
|
+
if (!clipboard || clipboard.nodes.length === 0) return;
|
|
4576
|
+
takeSnapshot();
|
|
4577
|
+
const idMapping = /* @__PURE__ */ new Map();
|
|
4578
|
+
clipboard.nodes.forEach((node) => {
|
|
4579
|
+
idMapping.set(node.id, crypto.randomUUID());
|
|
4580
|
+
});
|
|
4581
|
+
const pastedNodes = clipboard.nodes.map((node) => ({
|
|
4582
|
+
...node,
|
|
4583
|
+
id: idMapping.get(node.id) ?? crypto.randomUUID(),
|
|
4584
|
+
position: {
|
|
4585
|
+
x: node.position.x + PASTE_OFFSET,
|
|
4586
|
+
y: node.position.y + PASTE_OFFSET
|
|
4587
|
+
},
|
|
4588
|
+
selected: true,
|
|
4589
|
+
data: { ...node.data }
|
|
4590
|
+
}));
|
|
4591
|
+
const pastedEdges = clipboard.edges.map((edge) => ({
|
|
4592
|
+
...edge,
|
|
4593
|
+
id: crypto.randomUUID(),
|
|
4594
|
+
source: idMapping.get(edge.source) ?? edge.source,
|
|
4595
|
+
target: idMapping.get(edge.target) ?? edge.target
|
|
4596
|
+
}));
|
|
4597
|
+
setNodes((currentNodes) => [
|
|
4598
|
+
...currentNodes.map((node) => ({ ...node, selected: false })),
|
|
4599
|
+
...pastedNodes
|
|
4600
|
+
]);
|
|
4601
|
+
setEdges((currentEdges) => [...currentEdges, ...pastedEdges]);
|
|
4602
|
+
clipboardRef.current = {
|
|
4603
|
+
nodes: pastedNodes.map((node) => ({ ...node, data: { ...node.data } })),
|
|
4604
|
+
edges: pastedEdges.map((edge) => ({ ...edge }))
|
|
4605
|
+
};
|
|
4606
|
+
}, [setNodes, setEdges, takeSnapshot]);
|
|
4607
|
+
return {
|
|
4608
|
+
copy,
|
|
4609
|
+
paste,
|
|
4610
|
+
hasCopied: clipboardRef.current !== null && clipboardRef.current.nodes.length > 0
|
|
4611
|
+
};
|
|
4612
|
+
}
|
|
4613
|
+
var INPUT_TAGS = /* @__PURE__ */ new Set(["INPUT", "TEXTAREA", "SELECT"]);
|
|
4614
|
+
function isInputFocused() {
|
|
4615
|
+
const activeElement = document.activeElement;
|
|
4616
|
+
if (!activeElement) return false;
|
|
4617
|
+
return INPUT_TAGS.has(activeElement.tagName) || activeElement.isContentEditable;
|
|
4618
|
+
}
|
|
4619
|
+
function useCanvasShortcuts({
|
|
4620
|
+
undo,
|
|
4621
|
+
redo,
|
|
4622
|
+
copy,
|
|
4623
|
+
paste,
|
|
4624
|
+
canUndo,
|
|
4625
|
+
canRedo,
|
|
4626
|
+
selectAll,
|
|
4627
|
+
fitView
|
|
4628
|
+
}) {
|
|
4629
|
+
useEffect(() => {
|
|
4630
|
+
function handleKeyDown(event) {
|
|
4631
|
+
if (isInputFocused()) return;
|
|
4632
|
+
const isModifierPressed = event.metaKey || event.ctrlKey;
|
|
4633
|
+
if (!isModifierPressed) {
|
|
4634
|
+
if (event.key === "Escape") {
|
|
4635
|
+
selectAll();
|
|
4636
|
+
return;
|
|
4637
|
+
}
|
|
4638
|
+
return;
|
|
4639
|
+
}
|
|
4640
|
+
switch (event.key.toLowerCase()) {
|
|
4641
|
+
case "z": {
|
|
4642
|
+
if (event.shiftKey) {
|
|
4643
|
+
if (canRedo) {
|
|
4644
|
+
event.preventDefault();
|
|
4645
|
+
redo();
|
|
4646
|
+
}
|
|
4647
|
+
} else {
|
|
4648
|
+
if (canUndo) {
|
|
4649
|
+
event.preventDefault();
|
|
4650
|
+
undo();
|
|
4651
|
+
}
|
|
4652
|
+
}
|
|
4653
|
+
break;
|
|
4654
|
+
}
|
|
4655
|
+
case "y": {
|
|
4656
|
+
if (canRedo) {
|
|
4657
|
+
event.preventDefault();
|
|
4658
|
+
redo();
|
|
4659
|
+
}
|
|
4660
|
+
break;
|
|
4661
|
+
}
|
|
4662
|
+
case "c": {
|
|
4663
|
+
event.preventDefault();
|
|
4664
|
+
copy();
|
|
4665
|
+
break;
|
|
4666
|
+
}
|
|
4667
|
+
case "v": {
|
|
4668
|
+
event.preventDefault();
|
|
4669
|
+
paste();
|
|
4670
|
+
break;
|
|
4671
|
+
}
|
|
4672
|
+
case "a": {
|
|
4673
|
+
event.preventDefault();
|
|
4674
|
+
selectAll();
|
|
4675
|
+
break;
|
|
4676
|
+
}
|
|
4677
|
+
case "f": {
|
|
4678
|
+
if (event.shiftKey) {
|
|
4679
|
+
event.preventDefault();
|
|
4680
|
+
fitView();
|
|
4681
|
+
}
|
|
4682
|
+
break;
|
|
4683
|
+
}
|
|
4684
|
+
}
|
|
4685
|
+
}
|
|
4686
|
+
document.addEventListener("keydown", handleKeyDown);
|
|
4687
|
+
return () => {
|
|
4688
|
+
document.removeEventListener("keydown", handleKeyDown);
|
|
4689
|
+
};
|
|
4690
|
+
}, [undo, redo, copy, paste, canUndo, canRedo, selectAll, fitView]);
|
|
4691
|
+
}
|
|
4692
|
+
var ALIGNMENT_THRESHOLD = 5;
|
|
4693
|
+
var DEFAULT_NODE_WIDTH = 200;
|
|
4694
|
+
var DEFAULT_NODE_HEIGHT = 60;
|
|
4695
|
+
function getNodeBounds(node) {
|
|
4696
|
+
const width = node.measured?.width ?? node.width ?? DEFAULT_NODE_WIDTH;
|
|
4697
|
+
const height = node.measured?.height ?? node.height ?? DEFAULT_NODE_HEIGHT;
|
|
4698
|
+
return {
|
|
4699
|
+
left: node.position.x,
|
|
4700
|
+
centerX: node.position.x + width / 2,
|
|
4701
|
+
right: node.position.x + width,
|
|
4702
|
+
top: node.position.y,
|
|
4703
|
+
centerY: node.position.y + height / 2,
|
|
4704
|
+
bottom: node.position.y + height
|
|
4705
|
+
};
|
|
4706
|
+
}
|
|
4707
|
+
function findAlignments(dragBounds, otherNodes) {
|
|
4708
|
+
let closestHorizontal = null;
|
|
4709
|
+
let closestVertical = null;
|
|
4710
|
+
let minHorizontalDistance = ALIGNMENT_THRESHOLD + 1;
|
|
4711
|
+
let minVerticalDistance = ALIGNMENT_THRESHOLD + 1;
|
|
4712
|
+
for (const otherNode of otherNodes) {
|
|
4713
|
+
const otherBounds = getNodeBounds(otherNode);
|
|
4714
|
+
const verticalChecks = [
|
|
4715
|
+
{ dragValue: dragBounds.left, otherValue: otherBounds.left },
|
|
4716
|
+
{ dragValue: dragBounds.left, otherValue: otherBounds.centerX },
|
|
4717
|
+
{ dragValue: dragBounds.left, otherValue: otherBounds.right },
|
|
4718
|
+
{ dragValue: dragBounds.centerX, otherValue: otherBounds.left },
|
|
4719
|
+
{ dragValue: dragBounds.centerX, otherValue: otherBounds.centerX },
|
|
4720
|
+
{ dragValue: dragBounds.centerX, otherValue: otherBounds.right },
|
|
4721
|
+
{ dragValue: dragBounds.right, otherValue: otherBounds.left },
|
|
4722
|
+
{ dragValue: dragBounds.right, otherValue: otherBounds.centerX },
|
|
4723
|
+
{ dragValue: dragBounds.right, otherValue: otherBounds.right }
|
|
4724
|
+
];
|
|
4725
|
+
for (const check of verticalChecks) {
|
|
4726
|
+
const distance = Math.abs(check.dragValue - check.otherValue);
|
|
4727
|
+
if (distance <= ALIGNMENT_THRESHOLD && distance < minVerticalDistance) {
|
|
4728
|
+
minVerticalDistance = distance;
|
|
4729
|
+
closestVertical = check.otherValue;
|
|
4730
|
+
}
|
|
4731
|
+
}
|
|
4732
|
+
const horizontalChecks = [
|
|
4733
|
+
{ dragValue: dragBounds.top, otherValue: otherBounds.top },
|
|
4734
|
+
{ dragValue: dragBounds.top, otherValue: otherBounds.centerY },
|
|
4735
|
+
{ dragValue: dragBounds.top, otherValue: otherBounds.bottom },
|
|
4736
|
+
{ dragValue: dragBounds.centerY, otherValue: otherBounds.top },
|
|
4737
|
+
{ dragValue: dragBounds.centerY, otherValue: otherBounds.centerY },
|
|
4738
|
+
{ dragValue: dragBounds.centerY, otherValue: otherBounds.bottom },
|
|
4739
|
+
{ dragValue: dragBounds.bottom, otherValue: otherBounds.top },
|
|
4740
|
+
{ dragValue: dragBounds.bottom, otherValue: otherBounds.centerY },
|
|
4741
|
+
{ dragValue: dragBounds.bottom, otherValue: otherBounds.bottom }
|
|
4742
|
+
];
|
|
4743
|
+
for (const check of horizontalChecks) {
|
|
4744
|
+
const distance = Math.abs(check.dragValue - check.otherValue);
|
|
4745
|
+
if (distance <= ALIGNMENT_THRESHOLD && distance < minHorizontalDistance) {
|
|
4746
|
+
minHorizontalDistance = distance;
|
|
4747
|
+
closestHorizontal = check.otherValue;
|
|
4748
|
+
}
|
|
4749
|
+
}
|
|
4750
|
+
}
|
|
4751
|
+
return { horizontal: closestHorizontal, vertical: closestVertical };
|
|
4752
|
+
}
|
|
4753
|
+
function useHelpLines() {
|
|
4754
|
+
const [helpLines, setHelpLines] = useState({
|
|
4755
|
+
horizontal: null,
|
|
4756
|
+
vertical: null
|
|
4757
|
+
});
|
|
4758
|
+
const createDragHandler = useCallback(
|
|
4759
|
+
(allNodes) => (_event, draggedNode, draggedNodes) => {
|
|
4760
|
+
const draggingNodeIds = new Set(
|
|
4761
|
+
draggedNodes.length > 0 ? draggedNodes.map((node) => node.id) : [draggedNode.id]
|
|
4762
|
+
);
|
|
4763
|
+
const dragBounds = getNodeBounds(draggedNode);
|
|
4764
|
+
const otherNodes = allNodes.filter((node) => !draggingNodeIds.has(node.id));
|
|
4765
|
+
setHelpLines(findAlignments(dragBounds, otherNodes));
|
|
4766
|
+
},
|
|
4767
|
+
[]
|
|
4768
|
+
);
|
|
4769
|
+
const onNodeDragStop = useCallback(() => {
|
|
4770
|
+
setHelpLines({ horizontal: null, vertical: null });
|
|
4771
|
+
}, []);
|
|
4772
|
+
return {
|
|
4773
|
+
helpLines,
|
|
4774
|
+
createDragHandler,
|
|
4775
|
+
onNodeDragStop
|
|
4776
|
+
};
|
|
4777
|
+
}
|
|
4778
|
+
|
|
4779
|
+
export { AgentModal, AmazonNovaIcon, AnthropicModelIcon, AutoSaveWorkspace, DslExportModal, DslImportModal, DynamicIslandConfirm2 as DynamicIslandConfirm, MetaLlamaIcon, NodePalette, OutputSchemaBuilder, PipelineSettingsModal, PreviewPanel, RULE_STATUS_OPTIONS, RuleActionBuilder, RuleConditionBuilder, RuleForm, RunInputDialog, RunPanel, RunReplayModal, SaveStatusBadge, SubworkflowModal, TIMEZONE_OPTIONS, VariableInspector, VersionHistoryPanel, WorkflowListBar, defaultAgentOutputSchema, defaultRuleAction, defaultRuleCondition, defaultRuleForm, getModelIcon, useCanRedo, useCanUndo, useCanvasShortcuts, useClipboard, useContextMenu, useEditingNodeId, useHasCopied, useHelpLines, useIsRunning, useNodeResults, useSelectedNodeCount, useSubworkflowStore, useUndoRedo };
|
|
4780
|
+
//# sourceMappingURL=chunk-HAZP5J67.mjs.map
|
|
4781
|
+
//# sourceMappingURL=chunk-HAZP5J67.mjs.map
|