@solidxai/core-ui 0.1.19-beta.1 → 0.1.20-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/core/extension/solid-core/agentRegistry/card/AgentResourceCardWidget.d.ts.map +1 -1
- package/dist/components/core/extension/solid-core/agentRegistry/card/AgentResourceCardWidget.js +3 -10
- package/dist/components/core/extension/solid-core/agentRegistry/card/AgentResourceCardWidget.js.map +1 -1
- package/dist/components/core/extension/solid-core/agentRegistry/card/AgentResourceCardWidget.tsx +3 -8
- package/dist/components/layout/AdminHeaderActions.d.ts.map +1 -1
- package/dist/components/layout/AdminHeaderActions.js +1 -29
- package/dist/components/layout/AdminHeaderActions.js.map +1 -1
- package/dist/components/layout/AdminHeaderActions.tsx +0 -42
- package/dist/components/layout/SolidAiStudioLayout.d.ts +0 -1
- package/dist/components/layout/SolidAiStudioLayout.d.ts.map +1 -1
- package/dist/components/layout/SolidAiStudioLayout.js +9 -161
- package/dist/components/layout/SolidAiStudioLayout.js.map +1 -1
- package/dist/components/layout/SolidAiStudioLayout.tsx +7 -136
- package/dist/routes/AppEventListener.d.ts.map +1 -1
- package/dist/routes/AppEventListener.js +3 -3
- package/dist/routes/AppEventListener.js.map +1 -1
- package/dist/routes/AppEventListener.tsx +1 -2
- package/dist/routes/pages/admin/core/AgentHubTagsField.css +21 -0
- package/dist/routes/pages/admin/core/AgentHubTagsField.d.ts +17 -0
- package/dist/routes/pages/admin/core/AgentHubTagsField.d.ts.map +1 -0
- package/dist/routes/pages/admin/core/AgentHubTagsField.js +171 -0
- package/dist/routes/pages/admin/core/AgentHubTagsField.js.map +1 -0
- package/dist/routes/pages/admin/core/AgentHubTagsField.tsx +125 -0
- package/dist/routes/pages/admin/core/AgentRegistryEditorPage.css +7 -0
- package/dist/routes/pages/admin/core/AgentRegistryEditorPage.d.ts.map +1 -1
- package/dist/routes/pages/admin/core/AgentRegistryEditorPage.js +65 -52
- package/dist/routes/pages/admin/core/AgentRegistryEditorPage.js.map +1 -1
- package/dist/routes/pages/admin/core/AgentRegistryEditorPage.tsx +53 -11
- package/dist/routes/pages/admin/core/AgentSkillRegistryEditorPage.d.ts.map +1 -1
- package/dist/routes/pages/admin/core/AgentSkillRegistryEditorPage.js +14 -69
- package/dist/routes/pages/admin/core/AgentSkillRegistryEditorPage.js.map +1 -1
- package/dist/routes/pages/admin/core/AgentSkillRegistryEditorPage.tsx +15 -83
- package/dist/routes/pages/admin/core/AgentToolActivationDialog.d.ts +11 -0
- package/dist/routes/pages/admin/core/AgentToolActivationDialog.d.ts.map +1 -0
- package/dist/routes/pages/admin/core/AgentToolActivationDialog.js +166 -0
- package/dist/routes/pages/admin/core/AgentToolActivationDialog.js.map +1 -0
- package/dist/routes/pages/admin/core/AgentToolActivationDialog.tsx +140 -0
- package/dist/routes/pages/admin/core/AgentToolRegistryEditorPage.css +26 -0
- package/dist/routes/pages/admin/core/AgentToolRegistryEditorPage.d.ts.map +1 -1
- package/dist/routes/pages/admin/core/AgentToolRegistryEditorPage.js +94 -71
- package/dist/routes/pages/admin/core/AgentToolRegistryEditorPage.js.map +1 -1
- package/dist/routes/pages/admin/core/AgentToolRegistryEditorPage.tsx +60 -52
- package/package.json +1 -1
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
import React, { useEffect, useRef, useState } from "react";
|
|
2
2
|
import { useDispatch, useSelector } from "react-redux";
|
|
3
|
-
import { useNavigate
|
|
3
|
+
import { useNavigate } from "react-router-dom";
|
|
4
4
|
import { exitStudioMode, setStudioView, type StudioView } from "../../redux/features/solidStudioSlice";
|
|
5
|
-
import { showToast } from "../../redux/features/toastSlice";
|
|
6
5
|
import { useSession } from "../../hooks/useSession";
|
|
7
|
-
import {
|
|
6
|
+
import { signOut } from "../../adapters/auth/index";
|
|
8
7
|
import { createPortal } from "react-dom";
|
|
9
8
|
import { env } from "../../adapters/env";
|
|
10
|
-
import {
|
|
9
|
+
import { hasAnyRole } from "../../helpers/rolesHelper";
|
|
11
10
|
|
|
12
11
|
// ── Icons ──────────────────────────────────────────────────────────────────────
|
|
13
12
|
|
|
@@ -19,13 +18,6 @@ const DotsIcon = () => (
|
|
|
19
18
|
</svg>
|
|
20
19
|
);
|
|
21
20
|
|
|
22
|
-
const PreviewIcon = () => (
|
|
23
|
-
<svg width="13" height="13" viewBox="0 0 14 14" fill="none" aria-hidden="true">
|
|
24
|
-
<path d="M1 7s2.5-5 6-5 6 5 6 5-2.5 5-6 5-6-5-6-5Z" stroke="currentColor" strokeWidth="1.3" strokeLinejoin="round" />
|
|
25
|
-
<circle cx="7" cy="7" r="1.8" stroke="currentColor" strokeWidth="1.3" />
|
|
26
|
-
</svg>
|
|
27
|
-
);
|
|
28
|
-
|
|
29
21
|
const LogoutIcon = () => (
|
|
30
22
|
<svg width="13" height="13" viewBox="0 0 14 14" fill="none" aria-hidden="true">
|
|
31
23
|
<path d="M5 2H2.5A1.5 1.5 0 0 0 1 3.5v7A1.5 1.5 0 0 0 2.5 12H5" stroke="currentColor" strokeWidth="1.3" strokeLinecap="round" />
|
|
@@ -60,20 +52,17 @@ const FrontendIcon = () => (
|
|
|
60
52
|
// ── SolidStudio ────────────────────────────────────────────────────────────────
|
|
61
53
|
// Single component that renders both the Studio header and the AI panel.
|
|
62
54
|
// Mount this ONCE at the app root via AppEventListener.
|
|
63
|
-
//
|
|
55
|
+
// Visible to Admin users in dev environments.
|
|
64
56
|
|
|
65
57
|
export function SolidStudio() {
|
|
66
58
|
const isStudioMode = useSelector((state: any) => state.solidStudio?.isStudioMode ?? false);
|
|
67
59
|
const studioView = useSelector((state: any) => state.solidStudio?.studioView ?? null) as StudioView;
|
|
68
60
|
const dispatch = useDispatch();
|
|
69
61
|
const navigate = useNavigate();
|
|
70
|
-
const { pathname, search } = useLocation();
|
|
71
|
-
const isPreviewMode = new URLSearchParams(search).get("preview") === "true" || (typeof sessionStorage !== "undefined" && sessionStorage.getItem("solid-preview") === "true");
|
|
72
|
-
const isPreviewable = pathname !== "/studio" && pathname !== "/landing";
|
|
73
62
|
const { data, status } = useSession();
|
|
74
|
-
const
|
|
63
|
+
const isAdmin = hasAnyRole(data?.user?.roles, ["Admin"]);
|
|
64
|
+
const isDev = env("VITE_SOLIDX_ENV") === "dev";
|
|
75
65
|
const [isMenuOpen, setIsMenuOpen] = useState(false);
|
|
76
|
-
const [isChatRedirecting, setIsChatRedirecting] = useState(false);
|
|
77
66
|
const menuRef = useRef<HTMLDivElement>(null);
|
|
78
67
|
|
|
79
68
|
// Close the 3-dot menu when clicking outside
|
|
@@ -95,7 +84,7 @@ export function SolidStudio() {
|
|
|
95
84
|
}
|
|
96
85
|
}, [status, isStudioMode, dispatch]);
|
|
97
86
|
|
|
98
|
-
if (!
|
|
87
|
+
if (!isAdmin || !isDev) return null;
|
|
99
88
|
|
|
100
89
|
const handleLogout = () => {
|
|
101
90
|
setIsMenuOpen(false);
|
|
@@ -108,68 +97,6 @@ export function SolidStudio() {
|
|
|
108
97
|
navigate(view === "backend" ? "/admin" : "/landing");
|
|
109
98
|
};
|
|
110
99
|
|
|
111
|
-
const handleAiChatClick = async () => {
|
|
112
|
-
if (isChatRedirecting) return;
|
|
113
|
-
|
|
114
|
-
const aiUrl = env("VITE_SOLIDX_AI_URL");
|
|
115
|
-
if (!aiUrl) return;
|
|
116
|
-
|
|
117
|
-
setIsChatRedirecting(true);
|
|
118
|
-
try {
|
|
119
|
-
const session = await getSession();
|
|
120
|
-
const accessToken = session?.user?.accessToken || data?.user?.accessToken;
|
|
121
|
-
if (!accessToken) return;
|
|
122
|
-
|
|
123
|
-
const apiBaseUrl = env("API_URL", "http://localhost:8080").replace(/\/+$/, "");
|
|
124
|
-
const response = await fetch(`${apiBaseUrl}/api/iam/sso/code`, {
|
|
125
|
-
method: "POST",
|
|
126
|
-
headers: {
|
|
127
|
-
accept: "*/*",
|
|
128
|
-
Authorization: `Bearer ${accessToken}`,
|
|
129
|
-
},
|
|
130
|
-
body: "",
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
if (!response.ok) {
|
|
134
|
-
let detail = `Failed to fetch ssoCode (${response.status})`;
|
|
135
|
-
try {
|
|
136
|
-
const errorPayload = await response.json();
|
|
137
|
-
detail =
|
|
138
|
-
errorPayload?.message ||
|
|
139
|
-
errorPayload?.error?.message ||
|
|
140
|
-
errorPayload?.data?.message ||
|
|
141
|
-
detail;
|
|
142
|
-
} catch {
|
|
143
|
-
// no-op: keep default message
|
|
144
|
-
}
|
|
145
|
-
throw new Error(detail);
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
const payload = await response.json();
|
|
149
|
-
const ssoCode = payload?.ssoCode ?? payload?.data?.ssoCode;
|
|
150
|
-
if (!ssoCode) {
|
|
151
|
-
throw new Error("ssoCode missing in response");
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
const redirectUrl = new URL(aiUrl, window.location.origin);
|
|
155
|
-
redirectUrl.searchParams.set("ssoCode", String(ssoCode));
|
|
156
|
-
window.open(redirectUrl.toString(), "_blank");
|
|
157
|
-
} catch (error) {
|
|
158
|
-
const detail = error instanceof Error ? error.message : ERROR_MESSAGES.SOMETHING_WRONG;
|
|
159
|
-
dispatch(
|
|
160
|
-
showToast({
|
|
161
|
-
severity: "error",
|
|
162
|
-
summary: ERROR_MESSAGES.ERROR,
|
|
163
|
-
detail,
|
|
164
|
-
life: 4000,
|
|
165
|
-
})
|
|
166
|
-
);
|
|
167
|
-
console.error("Failed to open AI chat with ssoCode:", error);
|
|
168
|
-
} finally {
|
|
169
|
-
setIsChatRedirecting(false);
|
|
170
|
-
}
|
|
171
|
-
};
|
|
172
|
-
|
|
173
100
|
const studioUI = (
|
|
174
101
|
<div className="solid-studio-island" style={{ zIndex: "var(--z-studio)" }}>
|
|
175
102
|
<div className="solid-studio-island-inner">
|
|
@@ -189,16 +116,6 @@ export function SolidStudio() {
|
|
|
189
116
|
>
|
|
190
117
|
<FrontendIcon />
|
|
191
118
|
</button>
|
|
192
|
-
<button
|
|
193
|
-
type="button"
|
|
194
|
-
className="solid-studio-island-btn"
|
|
195
|
-
onClick={handleAiChatClick}
|
|
196
|
-
disabled={isChatRedirecting}
|
|
197
|
-
aria-busy={isChatRedirecting}
|
|
198
|
-
aria-label="AI chat"
|
|
199
|
-
>
|
|
200
|
-
<ChatIcon />
|
|
201
|
-
</button>
|
|
202
119
|
<div className="solid-studio-island-menu" ref={menuRef}>
|
|
203
120
|
<button
|
|
204
121
|
type="button"
|
|
@@ -210,21 +127,6 @@ export function SolidStudio() {
|
|
|
210
127
|
</button>
|
|
211
128
|
{isMenuOpen && (
|
|
212
129
|
<div className="solid-studio-island-dropdown">
|
|
213
|
-
{isPreviewable && (
|
|
214
|
-
<button
|
|
215
|
-
type="button"
|
|
216
|
-
className="solid-studio-island-dropdown-item"
|
|
217
|
-
onClick={() => {
|
|
218
|
-
setIsMenuOpen(false);
|
|
219
|
-
const params = new URLSearchParams(search);
|
|
220
|
-
params.set("preview", "true");
|
|
221
|
-
window.open(`${pathname}?${params.toString()}`, "_blank");
|
|
222
|
-
}}
|
|
223
|
-
>
|
|
224
|
-
<PreviewIcon />
|
|
225
|
-
Preview page
|
|
226
|
-
</button>
|
|
227
|
-
)}
|
|
228
130
|
<button
|
|
229
131
|
type="button"
|
|
230
132
|
className="solid-studio-island-dropdown-item danger"
|
|
@@ -243,37 +145,6 @@ export function SolidStudio() {
|
|
|
243
145
|
return typeof document !== "undefined" ? createPortal(studioUI, document.body) : null;
|
|
244
146
|
}
|
|
245
147
|
|
|
246
|
-
// ── PreviewModePersist ─────────────────────────────────────────────────────────
|
|
247
|
-
// Mount once at the app root alongside SolidStudio.
|
|
248
|
-
// sessionStorage is tab-scoped, so preview opened in a new tab stays isolated.
|
|
249
|
-
// Closing the tab is the natural way to exit preview mode.
|
|
250
|
-
|
|
251
|
-
const PREVIEW_KEY = "solid-preview";
|
|
252
|
-
|
|
253
|
-
export function PreviewModePersist() {
|
|
254
|
-
const { pathname, search } = useLocation();
|
|
255
|
-
const navigate = useNavigate();
|
|
256
|
-
|
|
257
|
-
// Activate: when URL has ?preview=true, store it in sessionStorage for this tab
|
|
258
|
-
useEffect(() => {
|
|
259
|
-
const params = new URLSearchParams(search);
|
|
260
|
-
if (params.get("preview") === "true") {
|
|
261
|
-
sessionStorage.setItem(PREVIEW_KEY, "true");
|
|
262
|
-
}
|
|
263
|
-
}, [search]);
|
|
264
|
-
|
|
265
|
-
// Persist: re-inject ?preview=true on every navigation if it was set
|
|
266
|
-
useEffect(() => {
|
|
267
|
-
if (sessionStorage.getItem(PREVIEW_KEY) !== "true") return;
|
|
268
|
-
const params = new URLSearchParams(search);
|
|
269
|
-
if (params.get("preview") === "true") return;
|
|
270
|
-
params.set("preview", "true");
|
|
271
|
-
navigate({ pathname, search: `?${params.toString()}` }, { replace: true });
|
|
272
|
-
}, [pathname, search, navigate]);
|
|
273
|
-
|
|
274
|
-
return null;
|
|
275
|
-
}
|
|
276
|
-
|
|
277
148
|
// ── SolidStudioWrapper ─────────────────────────────────────────────────────────
|
|
278
149
|
// Kept for backwards compatibility. Now a pure pass-through.
|
|
279
150
|
export function SolidStudioWrapper({ children }: { children: React.ReactNode }) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AppEventListener.d.ts","sourceRoot":"","sources":["../../src/routes/AppEventListener.tsx"],"names":[],"mappings":"AAkEA,wBAAgB,gBAAgB,
|
|
1
|
+
{"version":3,"file":"AppEventListener.d.ts","sourceRoot":"","sources":["../../src/routes/AppEventListener.tsx"],"names":[],"mappings":"AAkEA,wBAAgB,gBAAgB,4CAwD/B"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { jsx as _jsx, Fragment as _Fragment
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { useEffect } from "react";
|
|
3
3
|
import { eventBus, AppEvents } from "../helpers/eventBus";
|
|
4
4
|
import { usePathname } from "../hooks/usePathname";
|
|
5
|
-
import { SolidStudio
|
|
5
|
+
import { SolidStudio } from "../components/layout/SolidAiStudioLayout";
|
|
6
6
|
import { useDispatch } from "react-redux";
|
|
7
7
|
import { showToast } from "../redux/features/toastSlice";
|
|
8
8
|
import { env } from "../adapters/env";
|
|
@@ -91,6 +91,6 @@ export function AppEventListener() {
|
|
|
91
91
|
}));
|
|
92
92
|
});
|
|
93
93
|
}, [dispatch]);
|
|
94
|
-
return (
|
|
94
|
+
return (_jsx(_Fragment, { children: _jsx(SolidStudio, {}) }));
|
|
95
95
|
}
|
|
96
96
|
//# sourceMappingURL=AppEventListener.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AppEventListener.js","sourceRoot":"","sources":["../../src/routes/AppEventListener.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAClC,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACnD,OAAO,EAAE,WAAW,EAAE,
|
|
1
|
+
{"version":3,"file":"AppEventListener.js","sourceRoot":"","sources":["../../src/routes/AppEventListener.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useEffect } from \"react\";\nimport { eventBus, AppEvents } from \"../helpers/eventBus\";\nimport { usePathname } from \"../hooks/usePathname\";\nimport { SolidStudio } from \"../components/layout/SolidAiStudioLayout\";\nimport { useDispatch } from \"react-redux\";\nimport { showToast } from \"../redux/features/toastSlice\";\nimport { env } from \"../adapters/env\";\nimport { backendHealthMonitor, type BackendHealthState } from \"../helpers/backendHealthMonitor\";\n\ntype GlobalErrorPayload = {\n status?: number | string;\n message?: string;\n error?: unknown;\n};\n\nfunction isDevMode() {\n const envName = (env(\"VITE_SOLIDX_ENV\") || \"\").toLowerCase();\n return envName === \"dev\" || envName === \"development\";\n}\n\nfunction formatUnknownValue(value: unknown) {\n if (typeof value === \"string\") return value;\n if (value === null || value === undefined) return \"\";\n\n try {\n return JSON.stringify(value, null, 2);\n } catch {\n return String(value);\n }\n}\n\nfunction buildDevErrorDetail(payload?: GlobalErrorPayload) {\n if (!payload) return undefined;\n\n const error = payload.error as any;\n const lines: string[] = [];\n\n if (payload.status !== undefined) {\n lines.push(`Status: ${String(payload.status)}`);\n }\n\n if (payload.message) {\n lines.push(`Summary: ${payload.message}`);\n }\n\n if (error?.message) {\n lines.push(`Error: ${error.message}`);\n }\n\n if (error?.code) {\n lines.push(`Code: ${error.code}`);\n }\n\n const responseData = error?.response?.data ?? error?.data;\n const responseText = formatUnknownValue(responseData);\n if (responseText) {\n lines.push(`Response:\\n${responseText}`);\n }\n\n if (error?.stack) {\n lines.push(`Stack:\\n${error.stack}`);\n }\n\n return lines.length > 0 ? lines.join(\"\\n\\n\") : undefined;\n}\n\nexport function AppEventListener() {\n const pathname = usePathname();\n const dispatch = useDispatch();\n\n useEffect(() => {\n const off = eventBus.on<GlobalErrorPayload>(AppEvents.GlobalError, (payload) => {\n if (!pathname.startsWith(\"/admin\") && !pathname.startsWith(\"/auth\")) {\n return;\n }\n\n dispatch(\n showToast({\n severity: \"error\",\n summary: payload?.message || \"Something went wrong\",\n detail: isDevMode() ? buildDevErrorDetail(payload) : undefined,\n sticky: isDevMode(),\n life: isDevMode() ? 10000 : 4000,\n })\n );\n });\n return () => off();\n }, [dispatch, pathname]);\n\n useEffect(() => {\n let previousStatus: BackendHealthState[\"status\"] = backendHealthMonitor.getState().status;\n\n return backendHealthMonitor.subscribe((state) => {\n const nextStatus = state.status;\n const didBecomeOffline = previousStatus !== \"offline\" && nextStatus === \"offline\";\n previousStatus = nextStatus;\n\n if (!didBecomeOffline) {\n return;\n }\n\n dispatch(\n showToast({\n severity: \"error\",\n summary: \"Server unreachable\",\n detail: isDevMode() ? buildDevErrorDetail({\n status: state.statusCode,\n message: state.message || \"Unable to reach the server. Please try again later.\",\n error: state.error,\n }) : `We have been trying to reconnect to the server for over ${Math.max(1, Math.round(state.unavailableToastDelayMs / 1000))} seconds.`,\n sticky: isDevMode(),\n life: isDevMode() ? 10000 : 5000,\n })\n );\n });\n }, [dispatch]);\n\n return (\n <>\n <SolidStudio />\n </>\n );\n}\n"]}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useEffect } from "react";
|
|
2
2
|
import { eventBus, AppEvents } from "../helpers/eventBus";
|
|
3
3
|
import { usePathname } from "../hooks/usePathname";
|
|
4
|
-
import { SolidStudio
|
|
4
|
+
import { SolidStudio } from "../components/layout/SolidAiStudioLayout";
|
|
5
5
|
import { useDispatch } from "react-redux";
|
|
6
6
|
import { showToast } from "../redux/features/toastSlice";
|
|
7
7
|
import { env } from "../adapters/env";
|
|
@@ -118,7 +118,6 @@ export function AppEventListener() {
|
|
|
118
118
|
return (
|
|
119
119
|
<>
|
|
120
120
|
<SolidStudio />
|
|
121
|
-
<PreviewModePersist />
|
|
122
121
|
</>
|
|
123
122
|
);
|
|
124
123
|
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
.agent-hub-tags { display:flex; min-width:0; flex-direction:column; gap:.5rem; font-size:.875rem; }
|
|
2
|
+
.agent-hub-tags > label { display:flex; align-items:center; gap:.5rem; font-weight:600; }
|
|
3
|
+
.agent-hub-tags__count { min-width:1.3rem; padding:.1rem .35rem; border-radius:999px; background:var(--surface-ground,var(--background)); color:var(--text-color-secondary,var(--muted-foreground)); text-align:center; font-size:.7rem; }
|
|
4
|
+
.agent-hub-tags__selection { display:flex; align-items:center; flex-wrap:wrap; gap:.4rem; min-height:1.75rem; }
|
|
5
|
+
.agent-hub-tags__chip { display:inline-flex; align-items:center; gap:.3rem; max-width:100%; padding:.25rem .6rem; border-radius:999px; color:var(--primary-color,var(--primary)); background:color-mix(in srgb,var(--primary-color,var(--primary)) 11%,transparent); font-size:.78rem; overflow-wrap:anywhere; }
|
|
6
|
+
.agent-hub-tags__chip button { display:grid; flex-shrink:0; width:1.2rem; height:1.2rem; place-items:center; padding:0; border:0; border-radius:50%; background:transparent; color:inherit; cursor:pointer; }
|
|
7
|
+
.agent-hub-tags__chip button:hover { background:color-mix(in srgb,var(--primary-color,var(--primary)) 15%,transparent); }
|
|
8
|
+
.agent-hub-tags__search { display:flex; align-items:center; gap:.5rem; padding:.35rem .5rem; border:1px solid var(--surface-border,var(--border)); border-radius:.5rem; background:var(--surface-ground,var(--background)); }
|
|
9
|
+
.agent-hub-tags__search:focus-within { border-color:var(--primary-color,var(--primary)); }
|
|
10
|
+
.agent-hub-tags__search > svg { flex-shrink:0; color:var(--text-color-secondary,var(--muted-foreground)); }
|
|
11
|
+
.agent-hub-tags__search input { flex:1; width:100%; min-width:0; min-height:2rem; padding:0; border:0; outline:0; color:inherit; background:transparent; font:inherit; font-weight:400; }
|
|
12
|
+
.agent-hub-tags__search .solid-button { flex-shrink:0; }
|
|
13
|
+
.agent-hub-tags__options { max-height:12rem; overflow-y:auto; padding:.3rem; border:1px solid var(--surface-border,var(--border)); border-radius:.5rem; background:var(--surface-card,var(--card)); }
|
|
14
|
+
.agent-hub-tags__options > button { display:flex; justify-content:space-between; align-items:center; gap:.5rem; width:100%; padding:.5rem .6rem; border:0; border-radius:.3rem; background:transparent; color:inherit; text-align:left; font:inherit; overflow-wrap:anywhere; cursor:pointer; }
|
|
15
|
+
.agent-hub-tags__options > button:hover,.agent-hub-tags__options > button.is-active { background:color-mix(in srgb,var(--primary-color,var(--primary)) 8%,transparent); }
|
|
16
|
+
.agent-hub-tags__empty,.agent-hub-tags__hint { color:var(--text-color-secondary,var(--muted-foreground)); font-size:.8rem; font-weight:400; }
|
|
17
|
+
.agent-hub-tags__options > .agent-hub-tags__empty { padding:.6rem; }
|
|
18
|
+
.agent-hub-tags__error { color:var(--red-500,#dc2626); font-size:.8rem; }
|
|
19
|
+
.agent-hub-tags__error button { padding:0; border:0; background:transparent; color:inherit; text-decoration:underline; cursor:pointer; }
|
|
20
|
+
.agent-hub-tags button:focus-visible { outline:2px solid var(--primary-color,var(--primary)); outline-offset:2px; }
|
|
21
|
+
.agent-hub-tags button:disabled { cursor:default; opacity:.55; }
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import "./AgentHubTagsField.css";
|
|
2
|
+
export type AgentHubTag = {
|
|
3
|
+
id: number;
|
|
4
|
+
name: string;
|
|
5
|
+
};
|
|
6
|
+
export declare function agentHubTagsFrom(value: unknown): AgentHubTag[];
|
|
7
|
+
export declare function agentHubTagsPayload(tags: AgentHubTag[]): {
|
|
8
|
+
tagsIds: number[];
|
|
9
|
+
tagsCommand: string;
|
|
10
|
+
};
|
|
11
|
+
export declare function AgentHubTagsField({ value, onChange, disabled, onBusyChange }: {
|
|
12
|
+
value: AgentHubTag[];
|
|
13
|
+
onChange: (tags: AgentHubTag[]) => void;
|
|
14
|
+
disabled?: boolean;
|
|
15
|
+
onBusyChange?: (busy: boolean) => void;
|
|
16
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
17
|
+
//# sourceMappingURL=AgentHubTagsField.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AgentHubTagsField.d.ts","sourceRoot":"","sources":["../../../../../src/routes/pages/admin/core/AgentHubTagsField.tsx"],"names":[],"mappings":"AAIA,OAAO,yBAAyB,CAAC;AAEjC,MAAM,MAAM,WAAW,GAAG;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAEvD,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,WAAW,EAAE,CAS9D;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,WAAW,EAAE;;;EAEtD;AAID,wBAAgB,iBAAiB,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAgB,EAAE,YAAY,EAAE,EAAE;IACrF,KAAK,EAAE,WAAW,EAAE,CAAC;IACrB,QAAQ,EAAE,CAAC,IAAI,EAAE,WAAW,EAAE,KAAK,IAAI,CAAC;IACxC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;CACxC,2CA8FA"}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
11
|
+
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
|
12
|
+
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
13
|
+
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
14
|
+
function step(op) {
|
|
15
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
16
|
+
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
|
17
|
+
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
18
|
+
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
19
|
+
switch (op[0]) {
|
|
20
|
+
case 0: case 1: t = op; break;
|
|
21
|
+
case 4: _.label++; return { value: op[1], done: false };
|
|
22
|
+
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
23
|
+
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
24
|
+
default:
|
|
25
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
26
|
+
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
27
|
+
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
28
|
+
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
29
|
+
if (t[2]) _.ops.pop();
|
|
30
|
+
_.trys.pop(); continue;
|
|
31
|
+
}
|
|
32
|
+
op = body.call(thisArg, _);
|
|
33
|
+
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
34
|
+
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
|
|
38
|
+
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
|
|
39
|
+
if (ar || !(i in from)) {
|
|
40
|
+
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
|
|
41
|
+
ar[i] = from[i];
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return to.concat(ar || Array.prototype.slice.call(from));
|
|
45
|
+
};
|
|
46
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
47
|
+
import React from "react";
|
|
48
|
+
import { Plus, Search, X } from "lucide-react";
|
|
49
|
+
import { createSolidEntityApi } from "../../../../redux/api/solidEntityApi";
|
|
50
|
+
import { SolidButton } from "../../../../components/shad-cn-ui/SolidButton";
|
|
51
|
+
import "./AgentHubTagsField.css";
|
|
52
|
+
export function agentHubTagsFrom(value) {
|
|
53
|
+
if (!Array.isArray(value))
|
|
54
|
+
return [];
|
|
55
|
+
var seen = new Set();
|
|
56
|
+
return value.flatMap(function (tag) {
|
|
57
|
+
var id = Number(tag === null || tag === void 0 ? void 0 : tag.id);
|
|
58
|
+
if (!Number.isInteger(id) || id <= 0 || typeof (tag === null || tag === void 0 ? void 0 : tag.name) !== "string" || seen.has(id))
|
|
59
|
+
return [];
|
|
60
|
+
seen.add(id);
|
|
61
|
+
return [{ id: id, name: tag.name }];
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
export function agentHubTagsPayload(tags) {
|
|
65
|
+
return { tagsIds: tags.map(function (tag) { return tag.id; }), tagsCommand: tags.length ? "set" : "clear" };
|
|
66
|
+
}
|
|
67
|
+
var tagApi = createSolidEntityApi("agentHubTag");
|
|
68
|
+
export function AgentHubTagsField(_a) {
|
|
69
|
+
var _this = this;
|
|
70
|
+
var value = _a.value, onChange = _a.onChange, _b = _a.disabled, disabled = _b === void 0 ? false : _b, onBusyChange = _a.onBusyChange;
|
|
71
|
+
var _c = tagApi.useGetSolidEntitiesQuery("offset=0&limit=1000&fields[0]=id&fields[1]=name", { skip: disabled }), data = _c.data, isFetching = _c.isFetching, isError = _c.isError, refetch = _c.refetch;
|
|
72
|
+
var _d = tagApi.useCreateSolidEntityMutation(), createTag = _d[0], isCreating = _d[1].isLoading;
|
|
73
|
+
var _e = React.useState(""), query = _e[0], setQuery = _e[1];
|
|
74
|
+
var _f = React.useState(false), open = _f[0], setOpen = _f[1];
|
|
75
|
+
var _g = React.useState(-1), activeIndex = _g[0], setActiveIndex = _g[1];
|
|
76
|
+
var _h = React.useState(""), error = _h[0], setError = _h[1];
|
|
77
|
+
var inputRef = React.useRef(null);
|
|
78
|
+
var fieldId = React.useId();
|
|
79
|
+
var name = query.trim();
|
|
80
|
+
var catalog = agentHubTagsFrom(data === null || data === void 0 ? void 0 : data.records);
|
|
81
|
+
var exact = __spreadArray(__spreadArray([], catalog, true), value, true).find(function (tag) { return tag.name.toLowerCase() === name.toLowerCase(); });
|
|
82
|
+
var options = catalog.filter(function (tag) { return !value.some(function (selected) { return selected.id === tag.id; })
|
|
83
|
+
&& tag.name.toLowerCase().includes(name.toLowerCase()); }).sort(function (a, b) { return a.name.localeCompare(b.name); });
|
|
84
|
+
var canCreate = Boolean(name && !exact && !isFetching && !isError);
|
|
85
|
+
var select = function (tag) {
|
|
86
|
+
var _a;
|
|
87
|
+
if (!value.some(function (selected) { return selected.id === tag.id; }))
|
|
88
|
+
onChange(__spreadArray(__spreadArray([], value, true), [tag], false));
|
|
89
|
+
setQuery("");
|
|
90
|
+
setActiveIndex(-1);
|
|
91
|
+
setError("");
|
|
92
|
+
(_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.focus();
|
|
93
|
+
};
|
|
94
|
+
var add = function () { return __awaiter(_this, void 0, void 0, function () {
|
|
95
|
+
var result, depth, created, failure_1, refreshed, existing, message;
|
|
96
|
+
var _a, _b, _c, _d, _e, _f;
|
|
97
|
+
return __generator(this, function (_g) {
|
|
98
|
+
switch (_g.label) {
|
|
99
|
+
case 0:
|
|
100
|
+
if (disabled || isCreating || !name)
|
|
101
|
+
return [2 /*return*/];
|
|
102
|
+
if (exact) {
|
|
103
|
+
select(exact);
|
|
104
|
+
return [2 /*return*/];
|
|
105
|
+
}
|
|
106
|
+
if (!canCreate)
|
|
107
|
+
return [2 /*return*/];
|
|
108
|
+
setError("");
|
|
109
|
+
onBusyChange === null || onBusyChange === void 0 ? void 0 : onBusyChange(true);
|
|
110
|
+
_g.label = 1;
|
|
111
|
+
case 1:
|
|
112
|
+
_g.trys.push([1, 3, 5, 6]);
|
|
113
|
+
return [4 /*yield*/, createTag({ name: name }).unwrap()];
|
|
114
|
+
case 2:
|
|
115
|
+
result = _g.sent();
|
|
116
|
+
for (depth = 0; depth < 3 && result && !result.id; depth += 1)
|
|
117
|
+
result = result.data;
|
|
118
|
+
created = agentHubTagsFrom([result])[0];
|
|
119
|
+
if (!created)
|
|
120
|
+
throw new Error("The tag could not be loaded. Search for it again.");
|
|
121
|
+
select(created);
|
|
122
|
+
return [3 /*break*/, 6];
|
|
123
|
+
case 3:
|
|
124
|
+
failure_1 = _g.sent();
|
|
125
|
+
return [4 /*yield*/, refetch()];
|
|
126
|
+
case 4:
|
|
127
|
+
refreshed = _g.sent();
|
|
128
|
+
existing = agentHubTagsFrom((_a = refreshed.data) === null || _a === void 0 ? void 0 : _a.records).find(function (tag) { return tag.name.toLowerCase() === name.toLowerCase(); });
|
|
129
|
+
if (existing)
|
|
130
|
+
select(existing);
|
|
131
|
+
else {
|
|
132
|
+
message = (_f = (_d = (_c = (_b = failure_1 === null || failure_1 === void 0 ? void 0 : failure_1.data) === null || _b === void 0 ? void 0 : _b.data) === null || _c === void 0 ? void 0 : _c.message) !== null && _d !== void 0 ? _d : (_e = failure_1 === null || failure_1 === void 0 ? void 0 : failure_1.data) === null || _e === void 0 ? void 0 : _e.message) !== null && _f !== void 0 ? _f : failure_1 === null || failure_1 === void 0 ? void 0 : failure_1.message;
|
|
133
|
+
setError(typeof message === "string" ? message : "Could not create this tag. Try again.");
|
|
134
|
+
}
|
|
135
|
+
return [3 /*break*/, 6];
|
|
136
|
+
case 5:
|
|
137
|
+
onBusyChange === null || onBusyChange === void 0 ? void 0 : onBusyChange(false);
|
|
138
|
+
return [7 /*endfinally*/];
|
|
139
|
+
case 6: return [2 /*return*/];
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
}); };
|
|
143
|
+
return _jsxs("div", { className: "agent-hub-tags", onBlur: function (event) {
|
|
144
|
+
if (!event.currentTarget.contains(event.relatedTarget))
|
|
145
|
+
setOpen(false);
|
|
146
|
+
}, children: [_jsxs("label", { htmlFor: fieldId, children: ["Tags ", _jsx("span", { className: "agent-hub-tags__count", children: value.length })] }), _jsxs("div", { className: "agent-hub-tags__selection", children: [value.map(function (tag) { return _jsxs("span", { className: "agent-hub-tags__chip", children: [tag.name, !disabled && _jsx("button", { type: "button", disabled: isCreating, "aria-label": "Remove ".concat(tag.name), onClick: function () { return onChange(value.filter(function (selected) { return selected.id !== tag.id; })); }, children: _jsx(X, { size: 13 }) })] }, tag.id); }), !value.length && _jsx("span", { className: "agent-hub-tags__empty", children: "No tags selected" })] }), !disabled && _jsxs(_Fragment, { children: [_jsxs("div", { className: "agent-hub-tags__search", children: [_jsx(Search, { size: 16, "aria-hidden": "true" }), _jsx("input", { ref: inputRef, id: fieldId, role: "combobox", autoComplete: "off", "aria-expanded": open, "aria-controls": "".concat(fieldId, "-options"), "aria-autocomplete": "list", "aria-activedescendant": open && options[activeIndex] ? "".concat(fieldId, "-option-").concat(activeIndex) : undefined, disabled: isCreating, value: query, placeholder: "Search or create tags\u2026", onFocus: function () { return setOpen(true); }, onChange: function (event) { setQuery(event.target.value); setActiveIndex(-1); setOpen(true); setError(""); }, onKeyDown: function (event) {
|
|
147
|
+
if (event.key === "Escape") {
|
|
148
|
+
event.preventDefault();
|
|
149
|
+
setOpen(false);
|
|
150
|
+
}
|
|
151
|
+
if (event.key === "ArrowDown") {
|
|
152
|
+
event.preventDefault();
|
|
153
|
+
setOpen(true);
|
|
154
|
+
setActiveIndex(function (index) { return Math.min(index + 1, options.length - 1); });
|
|
155
|
+
}
|
|
156
|
+
if (event.key === "ArrowUp") {
|
|
157
|
+
event.preventDefault();
|
|
158
|
+
setActiveIndex(function (index) { return Math.max(index - 1, 0); });
|
|
159
|
+
}
|
|
160
|
+
if (event.key === "Enter") {
|
|
161
|
+
event.preventDefault();
|
|
162
|
+
if (open && options[activeIndex])
|
|
163
|
+
select(options[activeIndex]);
|
|
164
|
+
else
|
|
165
|
+
void add();
|
|
166
|
+
}
|
|
167
|
+
} }), name && _jsx(SolidButton, { variant: "secondary", size: "small", loading: isCreating, disabled: !exact && !canCreate, onClick: function () { return void add(); }, leftIcon: _jsx(Plus, { size: 14 }), children: exact ? "Select" : "Create tag" })] }), isError && _jsxs("div", { className: "agent-hub-tags__error", role: "alert", children: ["Tags could not be loaded. ", _jsx("button", { type: "button", onClick: function () { return void refetch(); }, children: "Retry" })] }), open && _jsx("div", { className: "agent-hub-tags__options", id: "".concat(fieldId, "-options"), role: "listbox", "aria-label": "Available tags", children: isFetching ? _jsx("div", { className: "agent-hub-tags__empty", role: "status", children: "Loading tags\u2026" })
|
|
168
|
+
: options.length ? options.map(function (tag, index) { return _jsxs("button", { id: "".concat(fieldId, "-option-").concat(index), type: "button", role: "option", "aria-selected": false, disabled: isCreating, className: activeIndex === index ? "is-active" : undefined, onMouseDown: function (event) { return event.preventDefault(); }, onClick: function () { return select(tag); }, children: [tag.name, _jsx(Plus, { size: 14 })] }, tag.id); })
|
|
169
|
+
: !isError && _jsx("div", { className: "agent-hub-tags__empty", children: exact ? "Tag already selected." : canCreate ? "Create \u201C".concat(name, "\u201D to add it.") : "No tags available. Type a name to create one." }) }), error && _jsx("small", { className: "agent-hub-tags__error", role: "alert", children: error }), _jsx("small", { className: "agent-hub-tags__hint", children: "Tags are shared across agents, skills, and tools." })] })] });
|
|
170
|
+
}
|
|
171
|
+
//# sourceMappingURL=AgentHubTagsField.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AgentHubTagsField.js","sourceRoot":"","sources":["../../../../../src/routes/pages/admin/core/AgentHubTagsField.tsx"],"names":[],"mappings":"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","sourcesContent":["import React from \"react\";\nimport { Plus, Search, X } from \"lucide-react\";\nimport { createSolidEntityApi } from \"../../../../redux/api/solidEntityApi\";\nimport { SolidButton } from \"../../../../components/shad-cn-ui/SolidButton\";\nimport \"./AgentHubTagsField.css\";\n\nexport type AgentHubTag = { id: number; name: string };\n\nexport function agentHubTagsFrom(value: unknown): AgentHubTag[] {\n if (!Array.isArray(value)) return [];\n const seen = new Set<number>();\n return value.flatMap((tag) => {\n const id = Number(tag?.id);\n if (!Number.isInteger(id) || id <= 0 || typeof tag?.name !== \"string\" || seen.has(id)) return [];\n seen.add(id);\n return [{ id, name: tag.name }];\n });\n}\n\nexport function agentHubTagsPayload(tags: AgentHubTag[]) {\n return { tagsIds: tags.map((tag) => tag.id), tagsCommand: tags.length ? \"set\" : \"clear\" };\n}\n\nconst tagApi = createSolidEntityApi(\"agentHubTag\");\n\nexport function AgentHubTagsField({ value, onChange, disabled = false, onBusyChange }: {\n value: AgentHubTag[];\n onChange: (tags: AgentHubTag[]) => void;\n disabled?: boolean;\n onBusyChange?: (busy: boolean) => void;\n}) {\n const { data, isFetching, isError, refetch } = tagApi.useGetSolidEntitiesQuery(\n \"offset=0&limit=1000&fields[0]=id&fields[1]=name\", { skip: disabled });\n const [createTag, { isLoading: isCreating }] = tagApi.useCreateSolidEntityMutation();\n const [query, setQuery] = React.useState(\"\");\n const [open, setOpen] = React.useState(false);\n const [activeIndex, setActiveIndex] = React.useState(-1);\n const [error, setError] = React.useState(\"\");\n const inputRef = React.useRef<HTMLInputElement>(null);\n const fieldId = React.useId();\n const name = query.trim();\n const catalog = agentHubTagsFrom(data?.records);\n const exact = [...catalog, ...value].find((tag) => tag.name.toLowerCase() === name.toLowerCase());\n const options = catalog.filter((tag) => !value.some((selected) => selected.id === tag.id)\n && tag.name.toLowerCase().includes(name.toLowerCase())).sort((a, b) => a.name.localeCompare(b.name));\n const canCreate = Boolean(name && !exact && !isFetching && !isError);\n\n const select = (tag: AgentHubTag) => {\n if (!value.some((selected) => selected.id === tag.id)) onChange([...value, tag]);\n setQuery(\"\");\n setActiveIndex(-1);\n setError(\"\");\n inputRef.current?.focus();\n };\n\n const add = async () => {\n if (disabled || isCreating || !name) return;\n if (exact) { select(exact); return; }\n if (!canCreate) return;\n setError(\"\");\n onBusyChange?.(true);\n try {\n let result = await createTag({ name }).unwrap();\n for (let depth = 0; depth < 3 && result && !result.id; depth += 1) result = result.data;\n const created = agentHubTagsFrom([result])[0];\n if (!created) throw new Error(\"The tag could not be loaded. Search for it again.\");\n select(created);\n } catch (failure: any) {\n const refreshed = await refetch();\n const existing = agentHubTagsFrom(refreshed.data?.records).find((tag) => tag.name.toLowerCase() === name.toLowerCase());\n if (existing) select(existing);\n else {\n const message = failure?.data?.data?.message ?? failure?.data?.message ?? failure?.message;\n setError(typeof message === \"string\" ? message : \"Could not create this tag. Try again.\");\n }\n } finally {\n onBusyChange?.(false);\n }\n };\n\n return <div className=\"agent-hub-tags\" onBlur={(event) => {\n if (!event.currentTarget.contains(event.relatedTarget as Node | null)) setOpen(false);\n }}>\n <label htmlFor={fieldId}>Tags <span className=\"agent-hub-tags__count\">{value.length}</span></label>\n <div className=\"agent-hub-tags__selection\">\n {value.map((tag) => <span className=\"agent-hub-tags__chip\" key={tag.id}>{tag.name}\n {!disabled && <button type=\"button\" disabled={isCreating} aria-label={`Remove ${tag.name}`}\n onClick={() => onChange(value.filter((selected) => selected.id !== tag.id))}><X size={13} /></button>}\n </span>)}\n {!value.length && <span className=\"agent-hub-tags__empty\">No tags selected</span>}\n </div>\n {!disabled && <>\n <div className=\"agent-hub-tags__search\">\n <Search size={16} aria-hidden=\"true\" />\n <input ref={inputRef} id={fieldId} role=\"combobox\" autoComplete=\"off\" aria-expanded={open}\n aria-controls={`${fieldId}-options`} aria-autocomplete=\"list\"\n aria-activedescendant={open && options[activeIndex] ? `${fieldId}-option-${activeIndex}` : undefined}\n disabled={isCreating} value={query} placeholder=\"Search or create tags…\"\n onFocus={() => setOpen(true)}\n onChange={(event) => { setQuery(event.target.value); setActiveIndex(-1); setOpen(true); setError(\"\"); }}\n onKeyDown={(event) => {\n if (event.key === \"Escape\") { event.preventDefault(); setOpen(false); }\n if (event.key === \"ArrowDown\") { event.preventDefault(); setOpen(true); setActiveIndex((index) => Math.min(index + 1, options.length - 1)); }\n if (event.key === \"ArrowUp\") { event.preventDefault(); setActiveIndex((index) => Math.max(index - 1, 0)); }\n if (event.key === \"Enter\") { event.preventDefault(); if (open && options[activeIndex]) select(options[activeIndex]); else void add(); }\n }} />\n {name && <SolidButton variant=\"secondary\" size=\"small\" loading={isCreating}\n disabled={!exact && !canCreate} onClick={() => void add()} leftIcon={<Plus size={14} />}>\n {exact ? \"Select\" : \"Create tag\"}\n </SolidButton>}\n </div>\n {isError && <div className=\"agent-hub-tags__error\" role=\"alert\">Tags could not be loaded. <button type=\"button\" onClick={() => void refetch()}>Retry</button></div>}\n {open && <div className=\"agent-hub-tags__options\" id={`${fieldId}-options`} role=\"listbox\" aria-label=\"Available tags\">\n {isFetching ? <div className=\"agent-hub-tags__empty\" role=\"status\">Loading tags…</div>\n : options.length ? options.map((tag, index) => <button key={tag.id} id={`${fieldId}-option-${index}`}\n type=\"button\" role=\"option\" aria-selected={false} disabled={isCreating}\n className={activeIndex === index ? \"is-active\" : undefined}\n onMouseDown={(event) => event.preventDefault()} onClick={() => select(tag)}>{tag.name}<Plus size={14} /></button>)\n : !isError && <div className=\"agent-hub-tags__empty\">{exact ? \"Tag already selected.\" : canCreate ? `Create “${name}” to add it.` : \"No tags available. Type a name to create one.\"}</div>}\n </div>}\n {error && <small className=\"agent-hub-tags__error\" role=\"alert\">{error}</small>}\n <small className=\"agent-hub-tags__hint\">Tags are shared across agents, skills, and tools.</small>\n </>}\n </div>;\n}\n"]}
|