@vertesia/tools-admin-ui 0.9.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.
Files changed (81) hide show
  1. package/LICENSE +13 -0
  2. package/README.md +120 -0
  3. package/lib/AdminApp.d.ts +13 -0
  4. package/lib/AdminApp.d.ts.map +1 -0
  5. package/lib/AdminContext.d.ts +10 -0
  6. package/lib/AdminContext.d.ts.map +1 -0
  7. package/lib/components/CollectionCard.d.ts +5 -0
  8. package/lib/components/CollectionCard.d.ts.map +1 -0
  9. package/lib/components/DetailPage.d.ts +13 -0
  10. package/lib/components/DetailPage.d.ts.map +1 -0
  11. package/lib/components/EndpointPanel.d.ts +5 -0
  12. package/lib/components/EndpointPanel.d.ts.map +1 -0
  13. package/lib/components/HeroSection.d.ts +9 -0
  14. package/lib/components/HeroSection.d.ts.map +1 -0
  15. package/lib/components/ResourceCard.d.ts +5 -0
  16. package/lib/components/ResourceCard.d.ts.map +1 -0
  17. package/lib/components/ResourceSection.d.ts +10 -0
  18. package/lib/components/ResourceSection.d.ts.map +1 -0
  19. package/lib/components/SearchBar.d.ts +10 -0
  20. package/lib/components/SearchBar.d.ts.map +1 -0
  21. package/lib/components/SummaryBadge.d.ts +5 -0
  22. package/lib/components/SummaryBadge.d.ts.map +1 -0
  23. package/lib/components/index.d.ts +9 -0
  24. package/lib/components/index.d.ts.map +1 -0
  25. package/lib/hooks.d.ts +26 -0
  26. package/lib/hooks.d.ts.map +1 -0
  27. package/lib/index.d.ts +7 -0
  28. package/lib/index.d.ts.map +1 -0
  29. package/lib/pages/HomePage.d.ts +2 -0
  30. package/lib/pages/HomePage.d.ts.map +1 -0
  31. package/lib/pages/InteractionCollection.d.ts +2 -0
  32. package/lib/pages/InteractionCollection.d.ts.map +1 -0
  33. package/lib/pages/InteractionDetail.d.ts +2 -0
  34. package/lib/pages/InteractionDetail.d.ts.map +1 -0
  35. package/lib/pages/SkillCollection.d.ts +2 -0
  36. package/lib/pages/SkillCollection.d.ts.map +1 -0
  37. package/lib/pages/SkillDetail.d.ts +2 -0
  38. package/lib/pages/SkillDetail.d.ts.map +1 -0
  39. package/lib/pages/TemplateCollection.d.ts +2 -0
  40. package/lib/pages/TemplateCollection.d.ts.map +1 -0
  41. package/lib/pages/TemplateDetail.d.ts +2 -0
  42. package/lib/pages/TemplateDetail.d.ts.map +1 -0
  43. package/lib/pages/ToolCollection.d.ts +2 -0
  44. package/lib/pages/ToolCollection.d.ts.map +1 -0
  45. package/lib/pages/TypeCollection.d.ts +2 -0
  46. package/lib/pages/TypeCollection.d.ts.map +1 -0
  47. package/lib/pages/TypeDetail.d.ts +2 -0
  48. package/lib/pages/TypeDetail.d.ts.map +1 -0
  49. package/lib/tools-admin-ui.js +935 -0
  50. package/lib/tools-admin-ui.js.map +1 -0
  51. package/lib/types.d.ts +89 -0
  52. package/lib/types.d.ts.map +1 -0
  53. package/package.json +50 -0
  54. package/src/AdminApp.tsx +87 -0
  55. package/src/AdminContext.ts +17 -0
  56. package/src/admin.css +650 -0
  57. package/src/components/CollectionCard.tsx +23 -0
  58. package/src/components/DetailPage.tsx +40 -0
  59. package/src/components/EndpointPanel.tsx +24 -0
  60. package/src/components/HeroSection.tsx +88 -0
  61. package/src/components/ResourceCard.tsx +25 -0
  62. package/src/components/ResourceSection.tsx +31 -0
  63. package/src/components/SearchBar.tsx +35 -0
  64. package/src/components/SummaryBadge.tsx +9 -0
  65. package/src/components/index.ts +8 -0
  66. package/src/dev/env.ts +14 -0
  67. package/src/dev/main.tsx +37 -0
  68. package/src/hooks.ts +36 -0
  69. package/src/index.ts +6 -0
  70. package/src/pages/HomePage.tsx +99 -0
  71. package/src/pages/InteractionCollection.tsx +59 -0
  72. package/src/pages/InteractionDetail.tsx +92 -0
  73. package/src/pages/SkillCollection.tsx +111 -0
  74. package/src/pages/SkillDetail.tsx +112 -0
  75. package/src/pages/TemplateCollection.tsx +54 -0
  76. package/src/pages/TemplateDetail.tsx +68 -0
  77. package/src/pages/ToolCollection.tsx +55 -0
  78. package/src/pages/TypeCollection.tsx +60 -0
  79. package/src/pages/TypeDetail.tsx +63 -0
  80. package/src/types.ts +304 -0
  81. package/src/vite-env.d.ts +1 -0
@@ -0,0 +1,935 @@
1
+ import { jsxs, jsx, Fragment } from "react/jsx-runtime";
2
+ import { NavLink, useParams, NestedRouterProvider, RouteComponent } from "@vertesia/ui/router";
3
+ import { useFetch } from "@vertesia/ui/core";
4
+ import { createContext, useContext, useState, useMemo } from "react";
5
+ import { useUserSession } from "@vertesia/ui/session";
6
+ function formatTitle(name) {
7
+ return name.replace(/[-_]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
8
+ }
9
+ function countPerCollection(items, collections, extractCollection) {
10
+ const counts = /* @__PURE__ */ new Map();
11
+ for (const col of collections) counts.set(col.name, 0);
12
+ if (collections.length === 1) {
13
+ counts.set(collections[0].name, items.length);
14
+ return counts;
15
+ }
16
+ for (const item of items) {
17
+ const colName = extractCollection(item);
18
+ if (colName && counts.has(colName)) {
19
+ counts.set(colName, (counts.get(colName) || 0) + 1);
20
+ }
21
+ }
22
+ return counts;
23
+ }
24
+ function buildResourceData(interactionsResp, toolsResp, skillsResp, typesResp, templatesResp, mcpEndpoints) {
25
+ const collections = [];
26
+ const resources = [];
27
+ const interCounts = countPerCollection(
28
+ interactionsResp.interactions,
29
+ interactionsResp.collections,
30
+ (i) => i.id.split(":")[0]
31
+ );
32
+ for (const col of interactionsResp.collections) {
33
+ collections.push({
34
+ name: col.name,
35
+ title: col.title || formatTitle(col.name),
36
+ description: col.description || "",
37
+ type: "interaction",
38
+ count: interCounts.get(col.name) || 0
39
+ });
40
+ }
41
+ for (const inter of interactionsResp.interactions) {
42
+ resources.push({
43
+ id: inter.id,
44
+ name: inter.name,
45
+ title: inter.title || formatTitle(inter.name),
46
+ description: inter.description || "",
47
+ type: "interaction",
48
+ tags: inter.tags
49
+ });
50
+ }
51
+ const toolCounts = countPerCollection(
52
+ toolsResp.tools,
53
+ toolsResp.collections,
54
+ (t) => t.url?.split("/").pop()
55
+ );
56
+ for (const col of toolsResp.collections) {
57
+ collections.push({
58
+ name: col.name,
59
+ title: col.title || formatTitle(col.name),
60
+ description: col.description || "",
61
+ type: "tool",
62
+ count: toolCounts.get(col.name) || 0
63
+ });
64
+ }
65
+ for (const tool of toolsResp.tools) {
66
+ resources.push({
67
+ name: tool.name,
68
+ title: formatTitle(tool.name),
69
+ description: tool.description || "",
70
+ type: "tool",
71
+ url: tool.url
72
+ });
73
+ }
74
+ const skillCounts = countPerCollection(
75
+ skillsResp.tools,
76
+ skillsResp.collections,
77
+ (t) => t.url?.split("/").pop()
78
+ );
79
+ for (const col of skillsResp.collections) {
80
+ collections.push({
81
+ name: col.name,
82
+ title: col.title || formatTitle(col.name),
83
+ description: col.description || "",
84
+ type: "skill",
85
+ count: skillCounts.get(col.name) || 0
86
+ });
87
+ }
88
+ for (const skill of skillsResp.tools) {
89
+ resources.push({
90
+ name: skill.name,
91
+ title: formatTitle(skill.name),
92
+ description: skill.description || "",
93
+ type: "skill",
94
+ url: skill.url
95
+ });
96
+ }
97
+ const typeCounts = countPerCollection(
98
+ typesResp.types,
99
+ typesResp.collections,
100
+ (t) => t.id?.split(":")[0]
101
+ );
102
+ for (const col of typesResp.collections) {
103
+ collections.push({
104
+ name: col.name,
105
+ title: col.title || formatTitle(col.name),
106
+ description: col.description || "",
107
+ type: "type",
108
+ count: typeCounts.get(col.name) || 0
109
+ });
110
+ }
111
+ for (const t of typesResp.types) {
112
+ resources.push({
113
+ name: t.name,
114
+ title: formatTitle(t.name),
115
+ description: t.description || "",
116
+ type: "type",
117
+ tags: t.tags
118
+ });
119
+ }
120
+ const tmplCounts = countPerCollection(
121
+ templatesResp.templates,
122
+ templatesResp.collections,
123
+ (t) => {
124
+ const segments = t.path?.split("/");
125
+ return segments && segments.length >= 4 ? segments[3] : void 0;
126
+ }
127
+ );
128
+ for (const col of templatesResp.collections) {
129
+ collections.push({
130
+ name: col.name,
131
+ title: col.title || formatTitle(col.name),
132
+ description: col.description || "",
133
+ type: "template",
134
+ count: tmplCounts.get(col.name) || 0
135
+ });
136
+ }
137
+ for (const tmpl of templatesResp.templates) {
138
+ resources.push({
139
+ name: tmpl.name,
140
+ title: tmpl.title || formatTitle(tmpl.name),
141
+ description: tmpl.description || "",
142
+ type: "template",
143
+ tags: tmpl.tags,
144
+ url: tmpl.path
145
+ });
146
+ }
147
+ for (const endpoint of mcpEndpoints || []) {
148
+ const name = endpoint.split("/").pop() || endpoint;
149
+ resources.push({
150
+ name,
151
+ title: formatTitle(name),
152
+ description: "",
153
+ type: "mcp",
154
+ url: endpoint
155
+ });
156
+ }
157
+ return { collections, resources };
158
+ }
159
+ function filterResources(items, query) {
160
+ const q = query.toLowerCase().trim();
161
+ if (!q) return items;
162
+ return items.filter(
163
+ (item) => item.name.toLowerCase().includes(q) || item.title.toLowerCase().includes(q) || item.description.toLowerCase().includes(q) || item.type.includes(q) || item.tags?.some((t) => t.toLowerCase().includes(q))
164
+ );
165
+ }
166
+ function useServerInfo(baseUrl) {
167
+ return useFetch(
168
+ () => fetch(baseUrl).then((r) => r.json()),
169
+ [baseUrl]
170
+ );
171
+ }
172
+ function useResourceData(baseUrl, mcpEndpoints) {
173
+ return useFetch(() => {
174
+ const fetchJson = (path) => fetch(`${baseUrl}/${path}`).then((r) => r.json());
175
+ return Promise.all([
176
+ fetchJson("interactions"),
177
+ fetchJson("tools"),
178
+ fetchJson("skills"),
179
+ fetchJson("types"),
180
+ fetchJson("templates")
181
+ ]).then(
182
+ ([interactions, tools, skills, types, templates]) => buildResourceData(interactions, tools, skills, types, templates, mcpEndpoints)
183
+ );
184
+ }, [baseUrl, mcpEndpoints]);
185
+ }
186
+ const AdminContext = createContext(void 0);
187
+ function useAdminContext() {
188
+ const ctx = useContext(AdminContext);
189
+ if (!ctx) throw new Error("useAdminContext must be used within AdminApp");
190
+ return ctx;
191
+ }
192
+ function EndpointPanel({ label, path }) {
193
+ const [copied, setCopied] = useState(false);
194
+ function handleCopy() {
195
+ const url = window.location.origin + path;
196
+ navigator.clipboard.writeText(url);
197
+ setCopied(true);
198
+ setTimeout(() => setCopied(false), 1500);
199
+ }
200
+ return /* @__PURE__ */ jsxs("div", { className: "vta-endpoint", children: [
201
+ /* @__PURE__ */ jsx("div", { className: "vta-endpoint-label", children: label }),
202
+ /* @__PURE__ */ jsxs("div", { className: "vta-endpoint-box", children: [
203
+ /* @__PURE__ */ jsx("code", { className: "vta-endpoint-code", children: path }),
204
+ /* @__PURE__ */ jsx("button", { onClick: handleCopy, className: "vta-copy-btn", title: "Copy full URL", children: copied ? "✓" : "⧉" })
205
+ ] })
206
+ ] });
207
+ }
208
+ function SummaryBadge({ count, label }) {
209
+ if (count === 0) return null;
210
+ return /* @__PURE__ */ jsxs("span", { className: "vta-badge", children: [
211
+ /* @__PURE__ */ jsx("span", { className: "vta-badge-dot" }),
212
+ count,
213
+ " ",
214
+ label,
215
+ count !== 1 ? "s" : ""
216
+ ] });
217
+ }
218
+ function getInitials(title) {
219
+ return title.split(/\s+/).map((w) => w[0]).filter(Boolean).slice(0, 2).join("").toUpperCase();
220
+ }
221
+ function countByType(resources) {
222
+ const counts = {};
223
+ for (const r of resources) {
224
+ counts[r.type] = (counts[r.type] || 0) + 1;
225
+ }
226
+ return counts;
227
+ }
228
+ const badgeLabels = [
229
+ { type: "tool", label: "tool" },
230
+ { type: "skill", label: "skill" },
231
+ { type: "interaction", label: "interaction" },
232
+ { type: "type", label: "content type" },
233
+ { type: "template", label: "template" },
234
+ { type: "mcp", label: "MCP provider" }
235
+ ];
236
+ function HeroSection({ title, version, resources }) {
237
+ const counts = countByType(resources);
238
+ return /* @__PURE__ */ jsxs("header", { className: "vta-hero", children: [
239
+ /* @__PURE__ */ jsxs("div", { className: "vta-hero-main", children: [
240
+ /* @__PURE__ */ jsxs("div", { className: "vta-hero-identity", children: [
241
+ /* @__PURE__ */ jsx("div", { className: "vta-hero-logo", children: getInitials(title) }),
242
+ /* @__PURE__ */ jsxs("div", { children: [
243
+ /* @__PURE__ */ jsx("p", { className: "vta-hero-eyebrow", children: "Tools Server" }),
244
+ /* @__PURE__ */ jsx("h1", { className: "vta-hero-title", children: title })
245
+ ] })
246
+ ] }),
247
+ /* @__PURE__ */ jsx("p", { className: "vta-hero-tagline", children: "Discover the tools, skills, interactions, and content types exposed by this server." }),
248
+ /* @__PURE__ */ jsx("div", { className: "vta-hero-summary", children: badgeLabels.map(({ type, label }) => /* @__PURE__ */ jsx(SummaryBadge, { count: counts[type] || 0, label }, type)) }),
249
+ /* @__PURE__ */ jsxs("div", { className: "vta-hero-links", children: [
250
+ /* @__PURE__ */ jsxs("a", { href: "/app/", target: "_blank", className: "vta-link-primary", children: [
251
+ /* @__PURE__ */ jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
252
+ /* @__PURE__ */ jsx("rect", { x: "3", y: "3", width: "18", height: "18", rx: "2", ry: "2" }),
253
+ /* @__PURE__ */ jsx("line", { x1: "9", y1: "3", x2: "9", y2: "21" })
254
+ ] }),
255
+ "UI Plugin Dev"
256
+ ] }),
257
+ /* @__PURE__ */ jsxs("a", { href: "/lib/plugin.js", className: "vta-link-secondary", children: [
258
+ /* @__PURE__ */ jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
259
+ /* @__PURE__ */ jsx("path", { d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" }),
260
+ /* @__PURE__ */ jsx("polyline", { points: "7 10 12 15 17 10" }),
261
+ /* @__PURE__ */ jsx("line", { x1: "12", y1: "15", x2: "12", y2: "3" })
262
+ ] }),
263
+ "Plugin Bundle"
264
+ ] })
265
+ ] })
266
+ ] }),
267
+ /* @__PURE__ */ jsxs("aside", { className: "vta-hero-panel", children: [
268
+ /* @__PURE__ */ jsx(EndpointPanel, { label: "Base endpoint", path: "/api" }),
269
+ /* @__PURE__ */ jsx(EndpointPanel, { label: "Package endpoint", path: "/api/package" }),
270
+ /* @__PURE__ */ jsxs("p", { className: "vta-hero-hint", children: [
271
+ "Use ",
272
+ /* @__PURE__ */ jsx("strong", { children: "POST /api/tools/<collection>" }),
273
+ " or",
274
+ " ",
275
+ /* @__PURE__ */ jsx("strong", { children: "POST /api/skills/<collection>" }),
276
+ " to call these from your apps or agents."
277
+ ] }),
278
+ /* @__PURE__ */ jsxs("p", { className: "vta-hero-version", children: [
279
+ "v",
280
+ version
281
+ ] })
282
+ ] })
283
+ ] });
284
+ }
285
+ function SearchBar({ value, onChange, placeholder, resultCount, totalCount }) {
286
+ const hasQuery = value.trim().length > 0;
287
+ const noResults = hasQuery && resultCount === 0;
288
+ return /* @__PURE__ */ jsxs("div", { className: "vta-search", children: [
289
+ /* @__PURE__ */ jsx(
290
+ "input",
291
+ {
292
+ type: "search",
293
+ value,
294
+ onChange: (e) => onChange(e.target.value),
295
+ placeholder: placeholder || "Search collections...",
296
+ className: "vta-search-input",
297
+ autoComplete: "off"
298
+ }
299
+ ),
300
+ hasQuery && !noResults && /* @__PURE__ */ jsxs("p", { className: "vta-search-hint", children: [
301
+ "Showing ",
302
+ resultCount,
303
+ " of ",
304
+ totalCount,
305
+ " resources"
306
+ ] }),
307
+ noResults && /* @__PURE__ */ jsx("p", { className: "vta-search-empty", children: "No resources match this search." })
308
+ ] });
309
+ }
310
+ function ResourceCard({ resource }) {
311
+ return /* @__PURE__ */ jsxs("div", { className: "vta-card", children: [
312
+ /* @__PURE__ */ jsx("span", { className: `vta-card-type vta-card-type--${resource.type}`, children: resource.type }),
313
+ /* @__PURE__ */ jsx("div", { className: "vta-card-title", children: resource.title }),
314
+ /* @__PURE__ */ jsx("div", { className: "vta-card-desc", children: resource.description || "No description" }),
315
+ resource.tags && resource.tags.length > 0 && /* @__PURE__ */ jsx("div", { className: "vta-card-tags", children: resource.tags.map((tag) => /* @__PURE__ */ jsx("span", { className: "vta-tag", children: tag }, tag)) }),
316
+ resource.url && /* @__PURE__ */ jsx("div", { className: "vta-card-url", children: resource.url })
317
+ ] });
318
+ }
319
+ function ResourceSection({ title, subtitle, resources, showDivider }) {
320
+ if (resources.length === 0) return null;
321
+ return /* @__PURE__ */ jsxs("section", { children: [
322
+ showDivider && /* @__PURE__ */ jsx("hr", { className: "vta-divider" }),
323
+ /* @__PURE__ */ jsxs("div", { children: [
324
+ /* @__PURE__ */ jsxs("h2", { className: "vta-section-title", children: [
325
+ title,
326
+ /* @__PURE__ */ jsxs("span", { className: "vta-section-count", children: [
327
+ "(",
328
+ resources.length,
329
+ ")"
330
+ ] })
331
+ ] }),
332
+ /* @__PURE__ */ jsx("p", { className: "vta-section-subtitle", children: subtitle })
333
+ ] }),
334
+ /* @__PURE__ */ jsx("div", { className: "vta-card-grid", children: resources.map((r) => /* @__PURE__ */ jsx(ResourceCard, { resource: r }, `${r.type}:${r.name}`)) })
335
+ ] });
336
+ }
337
+ function CollectionCard({ collection }) {
338
+ const href = `/${collection.type}s/${collection.name}`;
339
+ return /* @__PURE__ */ jsx(NavLink, { href, className: "vta-card-link", children: /* @__PURE__ */ jsxs("div", { className: "vta-card vta-card--link", children: [
340
+ /* @__PURE__ */ jsx("span", { className: `vta-card-type vta-card-type--${collection.type}`, children: collection.type }),
341
+ /* @__PURE__ */ jsx("div", { className: "vta-card-title", children: collection.title }),
342
+ /* @__PURE__ */ jsx("div", { className: "vta-card-desc", children: collection.description || "No description" }),
343
+ /* @__PURE__ */ jsxs("div", { className: "vta-card-url", children: [
344
+ collection.count,
345
+ " ",
346
+ collection.count === 1 ? "item" : "items"
347
+ ] })
348
+ ] }) });
349
+ }
350
+ function DetailPage({ type, title, description, tags, backHref = "/", children }) {
351
+ return /* @__PURE__ */ jsxs("div", { className: "vta-root", children: [
352
+ /* @__PURE__ */ jsxs("nav", { className: "vta-detail-nav", children: [
353
+ backHref !== "/" && /* @__PURE__ */ jsx(NavLink, { href: "/", className: "vta-detail-back", children: "Home" }),
354
+ /* @__PURE__ */ jsx(NavLink, { href: backHref, className: "vta-detail-back", children: "← Back" })
355
+ ] }),
356
+ /* @__PURE__ */ jsxs("div", { className: "vta-detail-header", children: [
357
+ /* @__PURE__ */ jsx("span", { className: `vta-card-type vta-card-type--${type}`, children: type }),
358
+ /* @__PURE__ */ jsx("h1", { className: "vta-detail-title", children: title }),
359
+ description && /* @__PURE__ */ jsx("p", { className: "vta-detail-desc", children: description }),
360
+ tags && tags.length > 0 && /* @__PURE__ */ jsx("div", { className: "vta-card-tags", children: tags.map((tag) => /* @__PURE__ */ jsx("span", { className: "vta-tag", children: tag }, tag)) })
361
+ ] }),
362
+ children
363
+ ] });
364
+ }
365
+ const sections = [
366
+ { type: "tool", title: "Tools", subtitle: "Remote tools available to agents via Vertesia." },
367
+ { type: "skill", title: "Skills", subtitle: "Reusable instructions and scripts packaged as tools." },
368
+ { type: "interaction", title: "Interactions", subtitle: "Conversation blueprints surfaced in the Vertesia UI." },
369
+ { type: "type", title: "Content Types", subtitle: "Schema definitions for structured content in the data store." },
370
+ { type: "template", title: "Rendering Templates", subtitle: "Document and presentation templates for content generation." },
371
+ { type: "mcp", title: "MCP Providers", subtitle: "Remote MCP servers available through this tools server." }
372
+ ];
373
+ function HomePage() {
374
+ const { serverInfo, collections, resources } = useAdminContext();
375
+ const [search, setSearch] = useState("");
376
+ const filtered = useMemo(
377
+ () => filterResources(resources, search),
378
+ [resources, search]
379
+ );
380
+ const isSearching = search.trim().length > 0;
381
+ return /* @__PURE__ */ jsxs("div", { className: "vta-root", children: [
382
+ /* @__PURE__ */ jsx(
383
+ HeroSection,
384
+ {
385
+ title: serverInfo.message.replace("Vertesia Tools API", "Tools Server"),
386
+ version: serverInfo.version,
387
+ resources
388
+ }
389
+ ),
390
+ /* @__PURE__ */ jsx(
391
+ SearchBar,
392
+ {
393
+ value: search,
394
+ onChange: setSearch,
395
+ placeholder: "Search tools, skills, interactions, types, templates...",
396
+ resultCount: filtered.length,
397
+ totalCount: resources.length
398
+ }
399
+ ),
400
+ isSearching ? (
401
+ /* Search mode: show individual resource cards */
402
+ sections.map((section, i) => {
403
+ const sectionItems = filtered.filter((r) => r.type === section.type);
404
+ return /* @__PURE__ */ jsx(
405
+ ResourceSection,
406
+ {
407
+ title: section.title,
408
+ subtitle: section.subtitle,
409
+ resources: sectionItems,
410
+ showDivider: i > 0
411
+ },
412
+ section.type
413
+ );
414
+ })
415
+ ) : (
416
+ /* Browse mode: show collection cards grouped by type */
417
+ sections.map((section, i) => {
418
+ const sectionCollections = collections.filter((c) => c.type === section.type);
419
+ const mcpResources = section.type === "mcp" ? resources.filter((r) => r.type === "mcp") : [];
420
+ if (sectionCollections.length === 0 && mcpResources.length === 0) return null;
421
+ return /* @__PURE__ */ jsxs("section", { children: [
422
+ i > 0 && /* @__PURE__ */ jsx("hr", { className: "vta-divider" }),
423
+ /* @__PURE__ */ jsxs("div", { children: [
424
+ /* @__PURE__ */ jsxs("h2", { className: "vta-section-title", children: [
425
+ section.title,
426
+ /* @__PURE__ */ jsxs("span", { className: "vta-section-count", children: [
427
+ "(",
428
+ sectionCollections.length,
429
+ sectionCollections.length === 1 ? " collection" : " collections",
430
+ ")"
431
+ ] })
432
+ ] }),
433
+ /* @__PURE__ */ jsx("p", { className: "vta-section-subtitle", children: section.subtitle })
434
+ ] }),
435
+ /* @__PURE__ */ jsxs("div", { className: "vta-card-grid", children: [
436
+ sectionCollections.map((col) => /* @__PURE__ */ jsx(CollectionCard, { collection: col }, `${col.type}:${col.name}`)),
437
+ mcpResources.map((r) => /* @__PURE__ */ jsxs("div", { className: "vta-card", children: [
438
+ /* @__PURE__ */ jsx("span", { className: "vta-card-type vta-card-type--mcp", children: "mcp" }),
439
+ /* @__PURE__ */ jsx("div", { className: "vta-card-title", children: r.title }),
440
+ /* @__PURE__ */ jsx("div", { className: "vta-card-desc", children: r.description || "No description" }),
441
+ r.url && /* @__PURE__ */ jsx("div", { className: "vta-card-url", children: r.url })
442
+ ] }, r.name))
443
+ ] })
444
+ ] }, section.type);
445
+ })
446
+ )
447
+ ] });
448
+ }
449
+ function InteractionCollection() {
450
+ const collection = useParams("collection");
451
+ const { baseUrl } = useAdminContext();
452
+ const { data: interactions, isLoading, error } = useFetch(
453
+ () => fetch(`${baseUrl}/interactions/${collection}`).then((r) => {
454
+ if (!r.ok) throw new Error(`Failed to load collection: ${r.statusText}`);
455
+ return r.json();
456
+ }),
457
+ [baseUrl, collection]
458
+ );
459
+ if (isLoading) {
460
+ return /* @__PURE__ */ jsx("div", { className: "vta-loading", children: "Loading collection..." });
461
+ }
462
+ if (error || !interactions) {
463
+ return /* @__PURE__ */ jsxs("div", { className: "vta-error", children: [
464
+ "Failed to load collection “",
465
+ collection,
466
+ "”."
467
+ ] });
468
+ }
469
+ return /* @__PURE__ */ jsx(
470
+ DetailPage,
471
+ {
472
+ type: "interaction",
473
+ title: collection,
474
+ description: `${interactions.length} interaction${interactions.length !== 1 ? "s" : ""} in this collection.`,
475
+ children: /* @__PURE__ */ jsx("div", { className: "vta-card-grid", children: interactions.map((inter) => /* @__PURE__ */ jsx(
476
+ NavLink,
477
+ {
478
+ href: `/interactions/${collection}/${inter.name}`,
479
+ className: "vta-card-link",
480
+ children: /* @__PURE__ */ jsxs("div", { className: "vta-card vta-card--link", children: [
481
+ /* @__PURE__ */ jsx("span", { className: "vta-card-type vta-card-type--interaction", children: "interaction" }),
482
+ /* @__PURE__ */ jsx("div", { className: "vta-card-title", children: inter.title || inter.name }),
483
+ /* @__PURE__ */ jsx("div", { className: "vta-card-desc", children: inter.description || "No description" }),
484
+ inter.tags && inter.tags.length > 0 && /* @__PURE__ */ jsx("div", { className: "vta-card-tags", children: inter.tags.map((tag) => /* @__PURE__ */ jsx("span", { className: "vta-tag", children: tag }, tag)) })
485
+ ] })
486
+ },
487
+ inter.id
488
+ )) })
489
+ }
490
+ );
491
+ }
492
+ function InteractionDetail() {
493
+ const { client } = useUserSession();
494
+ const params = useParams();
495
+ const collection = params.collection;
496
+ const name = params.name;
497
+ const { baseUrl } = useAdminContext();
498
+ const { data: interaction, isLoading, error } = useFetch(
499
+ () => client.getRawJWT().then((token) => fetch(`${baseUrl}/interactions/${collection}/${name}`, {
500
+ headers: {
501
+ Authorization: `Bearer ${token}`
502
+ }
503
+ })).then((r) => {
504
+ if (!r.ok) throw new Error(`Failed to load interaction: ${r.statusText}`);
505
+ return r.json();
506
+ }),
507
+ [baseUrl, collection, name]
508
+ );
509
+ if (isLoading) {
510
+ return /* @__PURE__ */ jsx("div", { className: "vta-loading", children: "Loading interaction..." });
511
+ }
512
+ if (error || !interaction) {
513
+ return /* @__PURE__ */ jsxs("div", { className: "vta-error", children: [
514
+ "Failed to load interaction “",
515
+ name,
516
+ "”."
517
+ ] });
518
+ }
519
+ const { agent_runner_options } = interaction;
520
+ const hasAgentFlags = agent_runner_options && (agent_runner_options.is_agent || agent_runner_options.is_tool || agent_runner_options.is_skill);
521
+ return /* @__PURE__ */ jsxs(
522
+ DetailPage,
523
+ {
524
+ type: "interaction",
525
+ title: interaction.title || interaction.name,
526
+ description: interaction.description,
527
+ tags: interaction.tags,
528
+ backHref: `/interactions/${collection}`,
529
+ children: [
530
+ interaction.prompts && interaction.prompts.length > 0 && /* @__PURE__ */ jsxs("div", { className: "vta-detail-section", children: [
531
+ /* @__PURE__ */ jsx("h2", { children: "Prompts" }),
532
+ interaction.prompts.map((prompt, i) => /* @__PURE__ */ jsxs("div", { className: "vta-detail-card", children: [
533
+ /* @__PURE__ */ jsxs("div", { className: "vta-detail-card-header", children: [
534
+ /* @__PURE__ */ jsx("span", { className: `vta-detail-role vta-detail-role--${prompt.role}`, children: prompt.role }),
535
+ prompt.name && /* @__PURE__ */ jsx("span", { className: "vta-detail-prompt-name", children: prompt.name })
536
+ ] }),
537
+ /* @__PURE__ */ jsx("pre", { className: "vta-detail-code", children: prompt.content })
538
+ ] }, i))
539
+ ] }),
540
+ interaction.result_schema && /* @__PURE__ */ jsxs("div", { className: "vta-detail-section", children: [
541
+ /* @__PURE__ */ jsx("h2", { children: "Result Schema" }),
542
+ /* @__PURE__ */ jsx("pre", { className: "vta-detail-code", children: JSON.stringify(interaction.result_schema, null, 2) })
543
+ ] }),
544
+ hasAgentFlags && /* @__PURE__ */ jsxs("div", { className: "vta-detail-section", children: [
545
+ /* @__PURE__ */ jsx("h2", { children: "Agent Runner" }),
546
+ /* @__PURE__ */ jsxs("div", { className: "vta-detail-flags", children: [
547
+ agent_runner_options.is_agent && /* @__PURE__ */ jsx("span", { className: "vta-detail-flag", children: "Agent" }),
548
+ agent_runner_options.is_tool && /* @__PURE__ */ jsx("span", { className: "vta-detail-flag", children: "Tool" }),
549
+ agent_runner_options.is_skill && /* @__PURE__ */ jsx("span", { className: "vta-detail-flag", children: "Skill" })
550
+ ] })
551
+ ] })
552
+ ]
553
+ }
554
+ );
555
+ }
556
+ function ToolCollection() {
557
+ const collection = useParams("collection");
558
+ const { baseUrl } = useAdminContext();
559
+ const { data, isLoading, error } = useFetch(
560
+ () => fetch(`${baseUrl}/tools/${collection}`).then((r) => {
561
+ if (!r.ok) throw new Error(`Failed to load collection: ${r.statusText}`);
562
+ return r.json();
563
+ }),
564
+ [baseUrl, collection]
565
+ );
566
+ if (isLoading) return /* @__PURE__ */ jsx("div", { className: "vta-loading", children: "Loading collection..." });
567
+ if (error || !data) return /* @__PURE__ */ jsxs("div", { className: "vta-error", children: [
568
+ "Failed to load tool collection “",
569
+ collection,
570
+ "”."
571
+ ] });
572
+ return /* @__PURE__ */ jsx(
573
+ DetailPage,
574
+ {
575
+ type: "tool",
576
+ title: data.title || collection,
577
+ description: data.description || `${data.tools.length} tool${data.tools.length !== 1 ? "s" : ""} in this collection.`,
578
+ children: data.tools.map((tool) => /* @__PURE__ */ jsxs("div", { className: "vta-detail-card", children: [
579
+ /* @__PURE__ */ jsxs("div", { className: "vta-detail-card-header", children: [
580
+ /* @__PURE__ */ jsx("span", { className: "vta-card-type vta-card-type--tool", children: "tool" }),
581
+ /* @__PURE__ */ jsx("div", { className: "vta-card-title", children: tool.name })
582
+ ] }),
583
+ /* @__PURE__ */ jsx("div", { className: "vta-card-desc", children: tool.description || "No description" }),
584
+ tool.input_schema && /* @__PURE__ */ jsx("pre", { className: "vta-detail-code", children: JSON.stringify(tool.input_schema, null, 2) })
585
+ ] }, tool.name))
586
+ }
587
+ );
588
+ }
589
+ function skillDisplayName(name) {
590
+ return name.startsWith("learn_") ? name.slice(6) : name;
591
+ }
592
+ function SkillCollection() {
593
+ const collection = useParams("collection");
594
+ const { baseUrl } = useAdminContext();
595
+ const { data, isLoading, error } = useFetch(
596
+ () => fetch(`${baseUrl}/skills/${collection}`).then((r) => {
597
+ if (!r.ok) throw new Error(`Failed to load collection: ${r.statusText}`);
598
+ return r.json();
599
+ }),
600
+ [baseUrl, collection]
601
+ );
602
+ const { data: widgetsData } = useFetch(
603
+ () => fetch(`${baseUrl}/package?scope=widgets`).then((r) => r.ok ? r.json() : { widgets: {} }),
604
+ [baseUrl]
605
+ );
606
+ const collectionWidgets = useMemo(() => {
607
+ if (!widgetsData?.widgets) return [];
608
+ return Object.entries(widgetsData.widgets).filter(([_, w]) => w.collection === collection).map(([name, w]) => ({ name, ...w }));
609
+ }, [widgetsData, collection]);
610
+ if (isLoading) return /* @__PURE__ */ jsx("div", { className: "vta-loading", children: "Loading collection..." });
611
+ if (error || !data) return /* @__PURE__ */ jsxs("div", { className: "vta-error", children: [
612
+ "Failed to load skill collection “",
613
+ collection,
614
+ "”."
615
+ ] });
616
+ return /* @__PURE__ */ jsxs(
617
+ DetailPage,
618
+ {
619
+ type: "skill",
620
+ title: data.title || collection,
621
+ description: data.description || `${data.tools.length} skill${data.tools.length !== 1 ? "s" : ""} in this collection.`,
622
+ children: [
623
+ collectionWidgets.length > 0 && /* @__PURE__ */ jsxs("div", { className: "vta-detail-section", children: [
624
+ /* @__PURE__ */ jsx("h2", { children: "Widgets" }),
625
+ /* @__PURE__ */ jsx("div", { className: "vta-detail-flags", children: collectionWidgets.map((w) => /* @__PURE__ */ jsxs("span", { className: "vta-detail-flag", children: [
626
+ w.name,
627
+ /* @__PURE__ */ jsxs("span", { className: "vta-card-url", style: { marginLeft: "0.5rem" }, children: [
628
+ "(skill: ",
629
+ w.skill,
630
+ ")"
631
+ ] })
632
+ ] }, w.name)) })
633
+ ] }),
634
+ /* @__PURE__ */ jsx("div", { className: "vta-card-grid", children: data.tools.map((skill) => {
635
+ const displayName = skillDisplayName(skill.name);
636
+ return /* @__PURE__ */ jsx(
637
+ NavLink,
638
+ {
639
+ href: `/skills/${collection}/${displayName}`,
640
+ className: "vta-card-link",
641
+ children: /* @__PURE__ */ jsxs("div", { className: "vta-card vta-card--link", children: [
642
+ /* @__PURE__ */ jsx("span", { className: "vta-card-type vta-card-type--skill", children: "skill" }),
643
+ /* @__PURE__ */ jsx("div", { className: "vta-card-title", children: displayName }),
644
+ /* @__PURE__ */ jsx("div", { className: "vta-card-desc", children: skill.description || "No description" }),
645
+ skill.related_tools && skill.related_tools.length > 0 && /* @__PURE__ */ jsx("div", { className: "vta-card-tags", children: skill.related_tools.map((t) => /* @__PURE__ */ jsx("span", { className: "vta-tag", children: t }, t)) })
646
+ ] })
647
+ },
648
+ skill.name
649
+ );
650
+ }) })
651
+ ]
652
+ }
653
+ );
654
+ }
655
+ function SkillDetail() {
656
+ const params = useParams();
657
+ const collection = params.collection;
658
+ const name = params.name;
659
+ const { baseUrl } = useAdminContext();
660
+ const { data: skill, isLoading, error } = useFetch(
661
+ () => fetch(`${baseUrl}/skills/${collection}/${name}`).then((r) => {
662
+ if (!r.ok) throw new Error(`Failed to load skill: ${r.statusText}`);
663
+ return r.json();
664
+ }),
665
+ [baseUrl, collection, name]
666
+ );
667
+ if (isLoading) return /* @__PURE__ */ jsx("div", { className: "vta-loading", children: "Loading skill..." });
668
+ if (error || !skill) return /* @__PURE__ */ jsxs("div", { className: "vta-error", children: [
669
+ "Failed to load skill “",
670
+ name,
671
+ "”."
672
+ ] });
673
+ return /* @__PURE__ */ jsxs(
674
+ DetailPage,
675
+ {
676
+ type: "skill",
677
+ title: skill.title || skill.name,
678
+ description: skill.description,
679
+ backHref: `/skills/${collection}`,
680
+ children: [
681
+ skill.widgets && skill.widgets.length > 0 && /* @__PURE__ */ jsxs("div", { className: "vta-detail-section", children: [
682
+ /* @__PURE__ */ jsx("h2", { children: "Widgets" }),
683
+ /* @__PURE__ */ jsx("div", { className: "vta-detail-flags", children: skill.widgets.map((w) => /* @__PURE__ */ jsx("span", { className: "vta-detail-flag", children: w }, w)) })
684
+ ] }),
685
+ skill.scripts && skill.scripts.length > 0 && /* @__PURE__ */ jsxs("div", { className: "vta-detail-section", children: [
686
+ /* @__PURE__ */ jsx("h2", { children: "Scripts" }),
687
+ /* @__PURE__ */ jsx("div", { className: "vta-detail-flags", children: skill.scripts.map((s) => /* @__PURE__ */ jsx("span", { className: "vta-detail-flag", children: s }, s)) })
688
+ ] }),
689
+ skill.related_tools && skill.related_tools.length > 0 && /* @__PURE__ */ jsxs("div", { className: "vta-detail-section", children: [
690
+ /* @__PURE__ */ jsx("h2", { children: "Related Tools" }),
691
+ /* @__PURE__ */ jsx("div", { className: "vta-detail-flags", children: skill.related_tools.map((t) => /* @__PURE__ */ jsx("span", { className: "vta-detail-flag", children: t }, t)) })
692
+ ] }),
693
+ skill.execution && /* @__PURE__ */ jsxs("div", { className: "vta-detail-section", children: [
694
+ /* @__PURE__ */ jsx("h2", { children: "Execution" }),
695
+ /* @__PURE__ */ jsxs("div", { className: "vta-detail-flags", children: [
696
+ /* @__PURE__ */ jsx("span", { className: "vta-detail-flag", children: skill.execution.language }),
697
+ skill.execution.packages?.map((p) => /* @__PURE__ */ jsx("span", { className: "vta-detail-flag", children: p }, p))
698
+ ] })
699
+ ] }),
700
+ /* @__PURE__ */ jsxs("div", { className: "vta-detail-section", children: [
701
+ /* @__PURE__ */ jsxs("h2", { children: [
702
+ "Instructions ",
703
+ skill.content_type === "jst" && /* @__PURE__ */ jsx("span", { className: "vta-tag", children: "JST template" })
704
+ ] }),
705
+ /* @__PURE__ */ jsx("pre", { className: "vta-detail-code", children: skill.instructions })
706
+ ] }),
707
+ skill.input_schema && /* @__PURE__ */ jsxs("div", { className: "vta-detail-section", children: [
708
+ /* @__PURE__ */ jsx("h2", { children: "Input Schema" }),
709
+ /* @__PURE__ */ jsx("pre", { className: "vta-detail-code", children: JSON.stringify(skill.input_schema, null, 2) })
710
+ ] })
711
+ ]
712
+ }
713
+ );
714
+ }
715
+ function TypeCollection() {
716
+ const collection = useParams("collection");
717
+ const { baseUrl } = useAdminContext();
718
+ const { data: types, isLoading, error } = useFetch(
719
+ () => fetch(`${baseUrl}/types/${collection}`).then((r) => {
720
+ if (!r.ok) throw new Error(`Failed to load collection: ${r.statusText}`);
721
+ return r.json();
722
+ }),
723
+ [baseUrl, collection]
724
+ );
725
+ if (isLoading) return /* @__PURE__ */ jsx("div", { className: "vta-loading", children: "Loading collection..." });
726
+ if (error || !types) return /* @__PURE__ */ jsxs("div", { className: "vta-error", children: [
727
+ "Failed to load type collection “",
728
+ collection,
729
+ "”."
730
+ ] });
731
+ return /* @__PURE__ */ jsx(
732
+ DetailPage,
733
+ {
734
+ type: "type",
735
+ title: collection,
736
+ description: `${types.length} content type${types.length !== 1 ? "s" : ""} in this collection.`,
737
+ children: /* @__PURE__ */ jsx("div", { className: "vta-card-grid", children: types.map((t) => {
738
+ const typeName = t.id?.split(":")[1] || t.name;
739
+ return /* @__PURE__ */ jsx(
740
+ NavLink,
741
+ {
742
+ href: `/types/${collection}/${typeName}`,
743
+ className: "vta-card-link",
744
+ children: /* @__PURE__ */ jsxs("div", { className: "vta-card vta-card--link", children: [
745
+ /* @__PURE__ */ jsx("span", { className: "vta-card-type vta-card-type--type", children: "type" }),
746
+ /* @__PURE__ */ jsx("div", { className: "vta-card-title", children: t.name }),
747
+ /* @__PURE__ */ jsx("div", { className: "vta-card-desc", children: t.description || "No description" }),
748
+ t.tags && t.tags.length > 0 && /* @__PURE__ */ jsx("div", { className: "vta-card-tags", children: t.tags.map((tag) => /* @__PURE__ */ jsx("span", { className: "vta-tag", children: tag }, tag)) }),
749
+ /* @__PURE__ */ jsxs("div", { className: "vta-card-url", children: [
750
+ t.is_chunkable && "chunkable",
751
+ t.is_chunkable && t.strict_mode && " · ",
752
+ t.strict_mode && "strict"
753
+ ] })
754
+ ] })
755
+ },
756
+ t.name
757
+ );
758
+ }) })
759
+ }
760
+ );
761
+ }
762
+ function TypeDetail() {
763
+ const params = useParams();
764
+ const collection = params.collection;
765
+ const name = params.name;
766
+ const { baseUrl } = useAdminContext();
767
+ const { data: typeDef, isLoading, error } = useFetch(
768
+ () => fetch(`${baseUrl}/types/${collection}/${name}`).then((r) => {
769
+ if (!r.ok) throw new Error(`Failed to load type: ${r.statusText}`);
770
+ return r.json();
771
+ }),
772
+ [baseUrl, collection, name]
773
+ );
774
+ if (isLoading) return /* @__PURE__ */ jsx("div", { className: "vta-loading", children: "Loading type..." });
775
+ if (error || !typeDef) return /* @__PURE__ */ jsxs("div", { className: "vta-error", children: [
776
+ "Failed to load type “",
777
+ name,
778
+ "”."
779
+ ] });
780
+ return /* @__PURE__ */ jsxs(
781
+ DetailPage,
782
+ {
783
+ type: "type",
784
+ title: typeDef.name,
785
+ description: typeDef.description,
786
+ tags: typeDef.tags,
787
+ backHref: `/types/${collection}`,
788
+ children: [
789
+ (typeDef.is_chunkable || typeDef.strict_mode) && /* @__PURE__ */ jsx("div", { className: "vta-detail-section", children: /* @__PURE__ */ jsxs("div", { className: "vta-detail-flags", children: [
790
+ typeDef.is_chunkable && /* @__PURE__ */ jsx("span", { className: "vta-detail-flag", children: "Chunkable" }),
791
+ typeDef.strict_mode && /* @__PURE__ */ jsx("span", { className: "vta-detail-flag", children: "Strict Mode" })
792
+ ] }) }),
793
+ typeDef.object_schema && /* @__PURE__ */ jsxs("div", { className: "vta-detail-section", children: [
794
+ /* @__PURE__ */ jsx("h2", { children: "Object Schema" }),
795
+ /* @__PURE__ */ jsx("pre", { className: "vta-detail-code", children: JSON.stringify(typeDef.object_schema, null, 2) })
796
+ ] }),
797
+ typeDef.table_layout && typeDef.table_layout.length > 0 && /* @__PURE__ */ jsxs("div", { className: "vta-detail-section", children: [
798
+ /* @__PURE__ */ jsx("h2", { children: "Table Layout" }),
799
+ /* @__PURE__ */ jsx("pre", { className: "vta-detail-code", children: JSON.stringify(typeDef.table_layout, null, 2) })
800
+ ] })
801
+ ]
802
+ }
803
+ );
804
+ }
805
+ function TemplateCollection() {
806
+ const collection = useParams("collection");
807
+ const { baseUrl } = useAdminContext();
808
+ const { data: templates, isLoading, error } = useFetch(
809
+ () => fetch(`${baseUrl}/templates/${collection}`).then((r) => {
810
+ if (!r.ok) throw new Error(`Failed to load collection: ${r.statusText}`);
811
+ return r.json();
812
+ }),
813
+ [baseUrl, collection]
814
+ );
815
+ if (isLoading) return /* @__PURE__ */ jsx("div", { className: "vta-loading", children: "Loading collection..." });
816
+ if (error || !templates) return /* @__PURE__ */ jsxs("div", { className: "vta-error", children: [
817
+ "Failed to load template collection “",
818
+ collection,
819
+ "”."
820
+ ] });
821
+ return /* @__PURE__ */ jsx(
822
+ DetailPage,
823
+ {
824
+ type: "template",
825
+ title: collection,
826
+ description: `${templates.length} template${templates.length !== 1 ? "s" : ""} in this collection.`,
827
+ children: /* @__PURE__ */ jsx("div", { className: "vta-card-grid", children: templates.map((tmpl) => /* @__PURE__ */ jsx(
828
+ NavLink,
829
+ {
830
+ href: `/templates/${collection}/${tmpl.name}`,
831
+ className: "vta-card-link",
832
+ children: /* @__PURE__ */ jsxs("div", { className: "vta-card vta-card--link", children: [
833
+ /* @__PURE__ */ jsx("div", { className: "vta-card-type vta-card-type--template", children: tmpl.type || "template" }),
834
+ /* @__PURE__ */ jsx("div", { className: "vta-card-title", children: tmpl.title || tmpl.name }),
835
+ /* @__PURE__ */ jsx("div", { className: "vta-card-desc", children: tmpl.description || "No description" }),
836
+ tmpl.tags && tmpl.tags.length > 0 && /* @__PURE__ */ jsx("div", { className: "vta-card-tags", children: tmpl.tags.map((tag) => /* @__PURE__ */ jsx("span", { className: "vta-tag", children: tag }, tag)) })
837
+ ] })
838
+ },
839
+ tmpl.name
840
+ )) })
841
+ }
842
+ );
843
+ }
844
+ function TemplateDetail() {
845
+ const params = useParams();
846
+ const collection = params.collection;
847
+ const name = params.name;
848
+ const { baseUrl } = useAdminContext();
849
+ const { data: template, isLoading, error } = useFetch(
850
+ () => fetch(`${baseUrl}/templates/${collection}/${name}`).then((r) => {
851
+ if (!r.ok) throw new Error(`Failed to load template: ${r.statusText}`);
852
+ return r.json();
853
+ }),
854
+ [baseUrl, collection, name]
855
+ );
856
+ if (isLoading) return /* @__PURE__ */ jsx("div", { className: "vta-loading", children: "Loading template..." });
857
+ if (error || !template) return /* @__PURE__ */ jsxs("div", { className: "vta-error", children: [
858
+ "Failed to load template “",
859
+ name,
860
+ "”."
861
+ ] });
862
+ return /* @__PURE__ */ jsxs(
863
+ DetailPage,
864
+ {
865
+ type: "template",
866
+ title: template.title || template.name,
867
+ description: template.description,
868
+ tags: template.tags,
869
+ backHref: `/templates/${collection}`,
870
+ children: [
871
+ /* @__PURE__ */ jsx("div", { className: "vta-detail-section", children: /* @__PURE__ */ jsx("div", { className: "vta-detail-flags", children: /* @__PURE__ */ jsx("span", { className: "vta-detail-flag", children: template.type }) }) }),
872
+ template.assets && template.assets.length > 0 && /* @__PURE__ */ jsxs("div", { className: "vta-detail-section", children: [
873
+ /* @__PURE__ */ jsx("h2", { children: "Assets" }),
874
+ /* @__PURE__ */ jsx("div", { className: "vta-detail-flags", children: template.assets.map((asset) => /* @__PURE__ */ jsx("span", { className: "vta-detail-flag", children: asset.split("/").pop() }, asset)) })
875
+ ] }),
876
+ /* @__PURE__ */ jsxs("div", { className: "vta-detail-section", children: [
877
+ /* @__PURE__ */ jsx("h2", { children: "Instructions" }),
878
+ /* @__PURE__ */ jsx("pre", { className: "vta-detail-code", children: template.instructions })
879
+ ] })
880
+ ]
881
+ }
882
+ );
883
+ }
884
+ const adminStyles = "/**\n * Self-contained styles for the admin panel.\n * All classes are prefixed with `vta-` (Vertesia Tools Admin) to avoid\n * polluting the plugin app CSS or the host page.\n */\n\n/* ── Root container ─────────────────────────────────────────────── */\n\n.vta-root {\n max-width: 1120px;\n margin: 0 auto;\n padding: 2.5rem 1.75rem;\n font-family: system-ui, -apple-system, sans-serif;\n color: #0f172a;\n line-height: 1.5;\n}\n\n/* ── Hero header ────────────────────────────────────────────────── */\n\n.vta-hero {\n display: flex;\n justify-content: space-between;\n align-items: flex-start;\n gap: 2.25rem;\n padding: 1.75rem 2rem;\n margin-bottom: 2.5rem;\n border-radius: 1.25rem;\n background: linear-gradient(135deg, #ffffff, #f3f4ff);\n border: 1px solid #e5e7eb;\n box-shadow:\n 0 18px 40px rgba(15, 23, 42, 0.08),\n 0 0 0 1px rgba(248, 250, 252, 0.8);\n}\n\n.vta-hero-main {\n flex: 1;\n display: flex;\n flex-direction: column;\n gap: 0.75rem;\n}\n\n.vta-hero-identity {\n display: flex;\n align-items: center;\n gap: 1rem;\n}\n\n.vta-hero-logo {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 56px;\n height: 56px;\n border-radius: 14px;\n background: radial-gradient(circle at 30% 0, #38bdf8, #6366f1);\n box-shadow:\n 0 0 0 1px rgba(15, 23, 42, 0.85),\n 0 12px 30px rgba(37, 99, 235, 0.6);\n font-size: 1.1rem;\n font-weight: 650;\n letter-spacing: 0.08em;\n text-transform: uppercase;\n color: #eff6ff;\n}\n\n.vta-hero-eyebrow {\n font-size: 0.75rem;\n letter-spacing: 0.12em;\n text-transform: uppercase;\n color: #6b7280;\n margin: 0;\n}\n\n.vta-hero-title {\n font-size: 1.9rem;\n font-weight: 650;\n letter-spacing: -0.03em;\n color: #0f172a;\n margin: 0;\n}\n\n.vta-hero-tagline {\n font-size: 0.95rem;\n color: #4b5563;\n margin: 0;\n}\n\n.vta-hero-summary {\n display: flex;\n flex-wrap: wrap;\n gap: 0.5rem;\n font-size: 0.8rem;\n color: #6b7280;\n}\n\n.vta-badge {\n display: inline-flex;\n align-items: center;\n gap: 0.35rem;\n padding: 0.15rem 0.55rem;\n border-radius: 999px;\n background: #f9fafb;\n border: 1px solid #e5e7eb;\n}\n\n.vta-badge-dot {\n width: 6px;\n height: 6px;\n border-radius: 999px;\n display: inline-block;\n background: #22c55e;\n}\n\n.vta-hero-links {\n display: flex;\n gap: 0.75rem;\n flex-wrap: wrap;\n margin-top: 0.25rem;\n}\n\n.vta-link-primary,\n.vta-link-secondary {\n display: inline-flex;\n align-items: center;\n gap: 0.5rem;\n padding: 0.5rem 1rem;\n border-radius: 6px;\n font-size: 0.875rem;\n font-weight: 500;\n text-decoration: none;\n color: white;\n transition: opacity 0.15s;\n}\n\n.vta-link-primary { background: #3b82f6; }\n.vta-link-secondary { background: #10b981; }\n.vta-link-primary:hover { opacity: 0.88; }\n.vta-link-secondary:hover { opacity: 0.88; }\n\n/* ── Hero aside panel ───────────────────────────────────────────── */\n\n.vta-hero-panel {\n min-width: 220px;\n max-width: 260px;\n flex-shrink: 0;\n}\n\n.vta-endpoint {\n margin-bottom: 0.75rem;\n}\n\n.vta-endpoint-label {\n font-size: 0.7rem;\n letter-spacing: 0.13em;\n text-transform: uppercase;\n color: #1d4ed8;\n margin-bottom: 0.35rem;\n}\n\n.vta-endpoint-box {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n background: #f3f4f6;\n padding: 0.5rem 0.75rem;\n border-radius: 0.5rem;\n border: 1px solid #e5e7eb;\n}\n\n.vta-endpoint-code {\n flex: 1;\n font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;\n font-size: 0.82rem;\n color: #111827;\n}\n\n.vta-copy-btn {\n background: #e5e7eb;\n border: none;\n padding: 0.35rem;\n border-radius: 4px;\n cursor: pointer;\n color: #6b7280;\n font-size: 0.8rem;\n line-height: 1;\n transition: all 0.15s;\n}\n\n.vta-copy-btn:hover {\n background: #d1d5db;\n color: #374151;\n}\n\n.vta-hero-hint {\n margin-top: 0.6rem;\n font-size: 0.8rem;\n color: #6b7280;\n line-height: 1.4;\n}\n\n.vta-hero-hint strong {\n color: #111827;\n}\n\n.vta-hero-version {\n font-size: 0.75rem;\n color: #9ca3af;\n margin-top: 0.25rem;\n}\n\n/* ── Search bar ─────────────────────────────────────────────────── */\n\n.vta-search {\n margin-bottom: 1.75rem;\n}\n\n.vta-search-input {\n width: 100%;\n max-width: 360px;\n padding: 0.55rem 0.75rem;\n border-radius: 999px;\n border: 1px solid #e5e7eb;\n background: #ffffff;\n color: #111827;\n font-size: 0.9rem;\n font-family: inherit;\n box-shadow: 0 4px 10px rgba(15, 23, 42, 0.04);\n box-sizing: border-box;\n}\n\n.vta-search-input::placeholder { color: #9ca3af; }\n\n.vta-search-input:focus {\n outline: none;\n border-color: #60a5fa;\n box-shadow:\n 0 0 0 1px rgba(59, 130, 246, 0.4),\n 0 6px 18px rgba(37, 99, 235, 0.18);\n}\n\n.vta-search-hint {\n font-size: 0.75rem;\n color: #9ca3af;\n margin-top: 0.4rem;\n}\n\n.vta-search-empty {\n font-size: 0.85rem;\n color: #ef4444;\n margin-top: 0.5rem;\n}\n\n/* ── Section ────────────────────────────────────────────────────── */\n\n.vta-divider {\n border: none;\n border-top: 1px solid #e5e7eb;\n margin: 2rem 0;\n}\n\n.vta-section-title {\n font-size: 1.4rem;\n font-weight: 600;\n letter-spacing: 0.03em;\n color: #0f172a;\n margin: 0 0 0.25rem 0;\n}\n\n.vta-section-count {\n font-size: 0.85rem;\n font-weight: 400;\n color: #9ca3af;\n margin-left: 0.5rem;\n}\n\n.vta-section-subtitle {\n font-size: 0.8rem;\n color: #6b7280;\n margin: 0 0 1rem 0;\n}\n\n/* ── Resource card grid ─────────────────────────────────────────── */\n\n.vta-card-grid {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));\n gap: 1.5rem;\n}\n\n.vta-card {\n background: radial-gradient(circle at 0 0, rgba(56, 189, 248, 0.12), transparent 75%),\n #ffffff;\n padding: 1.25rem 1.35rem;\n border-radius: 14px;\n box-shadow:\n 0 14px 30px rgba(15, 23, 42, 0.06),\n 0 0 0 1px rgba(248, 250, 252, 0.9);\n border: 1px solid #e5e7eb;\n transition: transform 0.15s, box-shadow 0.15s;\n}\n\n.vta-card:hover {\n transform: translateY(-3px);\n box-shadow:\n 0 18px 40px rgba(15, 23, 42, 0.10),\n 0 0 0 1px rgba(59, 130, 246, 0.6);\n}\n\n.vta-card-type {\n display: inline-block;\n font-size: 0.7rem;\n font-weight: 600;\n letter-spacing: 0.06em;\n text-transform: uppercase;\n padding: 0.15rem 0.55rem;\n border-radius: 999px;\n margin-bottom: 0.6rem;\n}\n\n/* Type-specific badge colours */\n.vta-card-type--tool { background: #dbeafe; color: #1e40af; }\n.vta-card-type--skill { background: #d1fae5; color: #065f46; }\n.vta-card-type--interaction { background: #fef3c7; color: #92400e; }\n.vta-card-type--type { background: #f3f4f6; color: #374151; }\n.vta-card-type--template { background: #ede9fe; color: #5b21b6; }\n.vta-card-type--mcp { background: #fce7f3; color: #9d174d; }\n\n.vta-card-title {\n font-weight: 600;\n font-size: 1.05rem;\n color: #0f172a;\n margin-bottom: 0.25rem;\n}\n\n.vta-card-desc {\n font-size: 0.875rem;\n color: #6b7280;\n margin-bottom: 0.5rem;\n}\n\n.vta-card-tags {\n display: flex;\n flex-wrap: wrap;\n gap: 0.35rem;\n margin-bottom: 0.4rem;\n}\n\n.vta-tag {\n font-size: 0.7rem;\n padding: 0.1rem 0.5rem;\n border-radius: 999px;\n background: #fef3c7;\n color: #92400e;\n}\n\n.vta-card-url {\n font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;\n font-size: 0.75rem;\n color: #9ca3af;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n/* ── Card link wrapper ─────────────────────────────────────────── */\n\n.vta-card-link {\n text-decoration: none;\n color: inherit;\n display: block;\n}\n\n.vta-card--link {\n cursor: pointer;\n}\n\n/* ── Detail page ───────────────────────────────────────────────── */\n\n.vta-detail-nav {\n display: flex;\n align-items: center;\n gap: 1rem;\n margin-bottom: 1.25rem;\n}\n\n.vta-detail-back {\n display: inline-flex;\n align-items: center;\n gap: 0.35rem;\n font-size: 0.875rem;\n color: #3b82f6;\n text-decoration: none;\n transition: opacity 0.15s;\n}\n\n.vta-detail-back:hover {\n opacity: 0.75;\n}\n\n.vta-detail-header {\n margin-bottom: 2rem;\n}\n\n.vta-detail-title {\n font-size: 1.75rem;\n font-weight: 650;\n letter-spacing: -0.02em;\n color: #0f172a;\n margin: 0.5rem 0 0.25rem;\n}\n\n.vta-detail-desc {\n font-size: 0.95rem;\n color: #4b5563;\n margin: 0 0 0.75rem;\n line-height: 1.6;\n}\n\n.vta-detail-section {\n margin-bottom: 2rem;\n}\n\n.vta-detail-section h2 {\n font-size: 1.15rem;\n font-weight: 600;\n color: #0f172a;\n margin: 0 0 0.75rem;\n}\n\n.vta-detail-card {\n background: #ffffff;\n border: 1px solid #e5e7eb;\n border-radius: 10px;\n padding: 1rem 1.25rem;\n margin-bottom: 0.75rem;\n}\n\n.vta-detail-card-header {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n margin-bottom: 0.5rem;\n}\n\n.vta-detail-role {\n display: inline-block;\n font-size: 0.7rem;\n font-weight: 600;\n letter-spacing: 0.06em;\n text-transform: uppercase;\n padding: 0.15rem 0.55rem;\n border-radius: 999px;\n}\n\n.vta-detail-role--system { background: #dbeafe; color: #1e40af; }\n.vta-detail-role--user { background: #d1fae5; color: #065f46; }\n.vta-detail-role--assistant { background: #ede9fe; color: #5b21b6; }\n.vta-detail-role--safety { background: #fce7f3; color: #9d174d; }\n.vta-detail-role--tool { background: #fef3c7; color: #92400e; }\n\n.vta-detail-prompt-name {\n font-size: 0.8rem;\n color: #6b7280;\n font-style: italic;\n}\n\n.vta-detail-code {\n font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;\n font-size: 0.82rem;\n line-height: 1.6;\n background: #f9fafb;\n border: 1px solid #e5e7eb;\n border-radius: 8px;\n padding: 0.75rem 1rem;\n margin: 0;\n overflow-x: auto;\n white-space: pre-wrap;\n word-break: break-word;\n color: #111827;\n}\n\n.vta-detail-flags {\n display: flex;\n gap: 0.5rem;\n flex-wrap: wrap;\n}\n\n.vta-detail-flag {\n display: inline-flex;\n align-items: center;\n gap: 0.35rem;\n padding: 0.25rem 0.65rem;\n border-radius: 999px;\n font-size: 0.8rem;\n font-weight: 500;\n background: #d1fae5;\n color: #065f46;\n}\n\n/* ── Loading / error states ─────────────────────────────────────── */\n\n.vta-loading {\n display: flex;\n align-items: center;\n justify-content: center;\n height: 16rem;\n color: #9ca3af;\n font-size: 1rem;\n}\n\n.vta-error {\n padding: 1.5rem;\n color: #ef4444;\n font-size: 0.95rem;\n}\n\n/* ── Responsive ─────────────────────────────────────────────────── */\n\n@media (max-width: 768px) {\n .vta-root { padding: 1.75rem 1.25rem; }\n .vta-hero { flex-direction: column; padding: 1.5rem 1.35rem; }\n .vta-hero-panel { max-width: 100%; width: 100%; }\n}\n\n/* ── Dark mode ──────────────────────────────────────────────────── */\n\n@media (prefers-color-scheme: dark) {\n .vta-root { color: #e5e7eb; }\n\n .vta-hero {\n background: linear-gradient(135deg, rgba(15, 23, 42, 0.98), rgba(15, 23, 42, 0.92));\n border-color: rgba(148, 163, 184, 0.45);\n box-shadow:\n 0 24px 60px rgba(15, 23, 42, 0.85),\n 0 0 0 1px rgba(15, 23, 42, 0.75);\n }\n\n .vta-hero-eyebrow { color: #9ca3af; }\n .vta-hero-title { color: #f9fafb; }\n .vta-hero-tagline { color: #cbd5f5; }\n .vta-hero-summary { color: #9ca3af; }\n\n .vta-badge {\n background: rgba(15, 23, 42, 0.95);\n border-color: rgba(148, 163, 184, 0.45);\n }\n\n .vta-endpoint-label { color: #93c5fd; }\n\n .vta-endpoint-box {\n background: rgba(31, 41, 55, 0.95);\n border-color: rgba(148, 163, 184, 0.6);\n }\n\n .vta-endpoint-code { color: #e5e7eb; }\n\n .vta-copy-btn {\n background: rgba(55, 65, 81, 0.95);\n color: #e5e7eb;\n }\n .vta-copy-btn:hover {\n background: rgba(75, 85, 99, 0.98);\n color: #f9fafb;\n }\n\n .vta-hero-hint { color: #9ca3af; }\n .vta-hero-hint strong { color: #e5e7eb; }\n\n .vta-search-input {\n background: rgba(15, 23, 42, 0.96);\n border-color: rgba(55, 65, 81, 0.9);\n color: #e5e7eb;\n box-shadow:\n 0 6px 16px rgba(15, 23, 42, 0.9),\n 0 0 0 1px rgba(15, 23, 42, 0.9);\n }\n .vta-search-input::placeholder { color: #6b7280; }\n .vta-search-input:focus {\n border-color: #60a5fa;\n box-shadow:\n 0 0 0 1px rgba(59, 130, 246, 0.7),\n 0 10px 26px rgba(30, 64, 175, 0.7);\n }\n\n .vta-search-hint { color: #9ca3af; }\n .vta-divider { border-top-color: rgba(148, 163, 184, 0.4); }\n .vta-section-title { color: #e5e7eb; }\n\n .vta-card {\n background: radial-gradient(circle at 0 0, rgba(56, 189, 248, 0.12), transparent 75%),\n rgba(15, 23, 42, 0.9);\n box-shadow:\n 0 14px 30px rgba(15, 23, 42, 0.8),\n 0 0 0 1px rgba(15, 23, 42, 0.85);\n border-color: rgba(148, 163, 184, 0.5);\n }\n .vta-card:hover {\n box-shadow:\n 0 18px 40px rgba(15, 23, 42, 0.9),\n 0 0 0 1px rgba(59, 130, 246, 0.8);\n }\n\n .vta-card-title { color: #e5e7eb; }\n .vta-card-desc { color: #9ca3af; }\n .vta-card-url { color: #6b7280; }\n\n .vta-card-type--tool { background: rgba(59, 130, 246, 0.15); color: #93c5fd; }\n .vta-card-type--skill { background: rgba(16, 185, 129, 0.15); color: #6ee7b7; }\n .vta-card-type--interaction { background: rgba(245, 158, 11, 0.15); color: #fcd34d; }\n .vta-card-type--type { background: rgba(107, 114, 128, 0.2); color: #d1d5db; }\n .vta-card-type--template { background: rgba(139, 92, 246, 0.15); color: #c4b5fd; }\n .vta-card-type--mcp { background: rgba(236, 72, 153, 0.15); color: #f9a8d4; }\n\n .vta-tag {\n background: rgba(250, 204, 21, 0.12);\n color: #facc15;\n }\n\n /* Detail page dark mode */\n .vta-detail-back { color: #60a5fa; }\n\n .vta-detail-title { color: #f9fafb; }\n .vta-detail-desc { color: #cbd5e1; }\n\n .vta-detail-section h2 { color: #e5e7eb; }\n\n .vta-detail-card {\n background: rgba(15, 23, 42, 0.9);\n border-color: rgba(148, 163, 184, 0.5);\n }\n\n .vta-detail-role--system { background: rgba(59, 130, 246, 0.15); color: #93c5fd; }\n .vta-detail-role--user { background: rgba(16, 185, 129, 0.15); color: #6ee7b7; }\n .vta-detail-role--assistant { background: rgba(139, 92, 246, 0.15); color: #c4b5fd; }\n .vta-detail-role--safety { background: rgba(236, 72, 153, 0.15); color: #f9a8d4; }\n .vta-detail-role--tool { background: rgba(245, 158, 11, 0.15); color: #fcd34d; }\n\n .vta-detail-prompt-name { color: #9ca3af; }\n\n .vta-detail-code {\n background: rgba(15, 23, 42, 0.95);\n border-color: rgba(148, 163, 184, 0.4);\n color: #e5e7eb;\n }\n\n .vta-detail-flag {\n background: rgba(16, 185, 129, 0.15);\n color: #6ee7b7;\n }\n}\n";
885
+ const routes = [
886
+ { path: "/", Component: HomePage },
887
+ { path: "/interactions/:collection", Component: InteractionCollection },
888
+ { path: "/interactions/:collection/:name", Component: InteractionDetail },
889
+ { path: "/tools/:collection", Component: ToolCollection },
890
+ { path: "/skills/:collection", Component: SkillCollection },
891
+ { path: "/skills/:collection/:name", Component: SkillDetail },
892
+ { path: "/types/:collection", Component: TypeCollection },
893
+ { path: "/types/:collection/:name", Component: TypeDetail },
894
+ { path: "/templates/:collection", Component: TemplateCollection },
895
+ { path: "/templates/:collection/:name", Component: TemplateDetail }
896
+ ];
897
+ function AdminApp({ baseUrl = "/api" }) {
898
+ const { data: serverInfo, isLoading: loadingInfo, error: infoError } = useServerInfo(baseUrl);
899
+ const { data: resourceData, isLoading: loadingData, error: dataError } = useResourceData(
900
+ baseUrl,
901
+ serverInfo?.endpoints.mcp
902
+ );
903
+ const isLoading = loadingInfo || loadingData;
904
+ const error = infoError || dataError;
905
+ if (isLoading) {
906
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
907
+ /* @__PURE__ */ jsx("style", { children: adminStyles }),
908
+ /* @__PURE__ */ jsx("div", { className: "vta-loading", children: "Loading..." })
909
+ ] });
910
+ }
911
+ if (error) {
912
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
913
+ /* @__PURE__ */ jsx("style", { children: adminStyles }),
914
+ /* @__PURE__ */ jsx("div", { className: "vta-error", children: "Failed to load server info. Is the API running?" })
915
+ ] });
916
+ }
917
+ if (!serverInfo || !resourceData) return null;
918
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
919
+ /* @__PURE__ */ jsx("style", { children: adminStyles }),
920
+ /* @__PURE__ */ jsx(AdminContext.Provider, { value: {
921
+ serverInfo,
922
+ collections: resourceData.collections,
923
+ resources: resourceData.resources,
924
+ baseUrl
925
+ }, children: /* @__PURE__ */ jsx(NestedRouterProvider, { routes, children: /* @__PURE__ */ jsx(RouteComponent, {}) }) })
926
+ ] });
927
+ }
928
+ export {
929
+ AdminApp,
930
+ AdminContext,
931
+ buildResourceData,
932
+ filterResources,
933
+ useAdminContext
934
+ };
935
+ //# sourceMappingURL=tools-admin-ui.js.map