@vertesia/tools-admin-ui 1.0.0-dev.20260227.112605Z → 1.0.0-dev.20260331.091034Z
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/lib/AdminApp.d.ts +3 -1
- package/lib/AdminApp.d.ts.map +1 -1
- package/lib/components/AdminTopBar.d.ts +6 -0
- package/lib/components/AdminTopBar.d.ts.map +1 -0
- package/lib/components/CollectionCard.d.ts.map +1 -1
- package/lib/components/DetailPage.d.ts.map +1 -1
- package/lib/components/EndpointPanel.d.ts.map +1 -1
- package/lib/components/HeroSection.d.ts.map +1 -1
- package/lib/components/ResourceCard.d.ts.map +1 -1
- package/lib/components/ResourceSection.d.ts.map +1 -1
- package/lib/components/SearchBar.d.ts.map +1 -1
- package/lib/components/SummaryBadge.d.ts.map +1 -1
- package/lib/components/index.d.ts +6 -5
- package/lib/components/index.d.ts.map +1 -1
- package/lib/components/typeVariants.d.ts +5 -0
- package/lib/components/typeVariants.d.ts.map +1 -0
- package/lib/hooks.d.ts +1 -1
- package/lib/hooks.d.ts.map +1 -1
- package/lib/pages/ActivityCollection.d.ts +2 -0
- package/lib/pages/ActivityCollection.d.ts.map +1 -0
- package/lib/pages/HomePage.d.ts.map +1 -1
- package/lib/pages/InteractionCollection.d.ts.map +1 -1
- package/lib/pages/InteractionDetail.d.ts.map +1 -1
- package/lib/pages/SkillCollection.d.ts.map +1 -1
- package/lib/pages/SkillDetail.d.ts.map +1 -1
- package/lib/pages/TemplateCollection.d.ts.map +1 -1
- package/lib/pages/TemplateDetail.d.ts.map +1 -1
- package/lib/pages/ToolCollection.d.ts.map +1 -1
- package/lib/pages/TypeCollection.d.ts.map +1 -1
- package/lib/pages/TypeDetail.d.ts.map +1 -1
- package/lib/tools-admin-ui.js +481 -379
- package/lib/tools-admin-ui.js.map +1 -1
- package/lib/types.d.ts +8 -3
- package/lib/types.d.ts.map +1 -1
- package/package.json +8 -4
- package/src/AdminApp.tsx +23 -17
- package/src/components/AdminTopBar.tsx +39 -0
- package/src/components/CollectionCard.tsx +20 -14
- package/src/components/DetailPage.tsx +20 -11
- package/src/components/EndpointPanel.tsx +16 -7
- package/src/components/HeroSection.tsx +52 -45
- package/src/components/ResourceCard.tsx +23 -18
- package/src/components/ResourceSection.tsx +7 -5
- package/src/components/SearchBar.tsx +8 -6
- package/src/components/SummaryBadge.tsx +4 -3
- package/src/components/index.ts +6 -5
- package/src/components/typeVariants.ts +19 -0
- package/src/dev/env.ts +3 -3
- package/src/dev/index.css +13 -0
- package/src/dev/main.tsx +11 -5
- package/src/hooks.ts +5 -3
- package/src/pages/ActivityCollection.tsx +67 -0
- package/src/pages/HomePage.tsx +19 -15
- package/src/pages/InteractionCollection.tsx +25 -27
- package/src/pages/InteractionDetail.tsx +35 -34
- package/src/pages/SkillCollection.tsx +29 -32
- package/src/pages/SkillDetail.tsx +31 -41
- package/src/pages/TemplateCollection.tsx +25 -21
- package/src/pages/TemplateDetail.tsx +18 -17
- package/src/pages/ToolCollection.tsx +22 -16
- package/src/pages/TypeCollection.tsx +32 -24
- package/src/pages/TypeDetail.tsx +16 -18
- package/src/theme.css +12 -0
- package/src/types.ts +34 -1
- package/src/admin.css +0 -650
package/src/AdminApp.tsx
CHANGED
|
@@ -1,24 +1,28 @@
|
|
|
1
|
+
import { Spinner } from '@vertesia/ui/core';
|
|
1
2
|
import type { Route } from '@vertesia/ui/router';
|
|
2
3
|
import { NestedRouterProvider, RouteComponent } from '@vertesia/ui/router';
|
|
3
|
-
|
|
4
|
+
|
|
4
5
|
import { AdminContext } from './AdminContext.js';
|
|
6
|
+
import { AdminTopBar } from './components/AdminTopBar.js';
|
|
7
|
+
import { useResourceData, useServerInfo } from './hooks.js';
|
|
8
|
+
import { ActivityCollection } from './pages/ActivityCollection.js';
|
|
5
9
|
import { HomePage } from './pages/HomePage.js';
|
|
6
10
|
import { InteractionCollection } from './pages/InteractionCollection.js';
|
|
7
11
|
import { InteractionDetail } from './pages/InteractionDetail.js';
|
|
8
|
-
import { ToolCollection } from './pages/ToolCollection.js';
|
|
9
12
|
import { SkillCollection } from './pages/SkillCollection.js';
|
|
10
13
|
import { SkillDetail } from './pages/SkillDetail.js';
|
|
11
|
-
import { TypeCollection } from './pages/TypeCollection.js';
|
|
12
|
-
import { TypeDetail } from './pages/TypeDetail.js';
|
|
13
14
|
import { TemplateCollection } from './pages/TemplateCollection.js';
|
|
14
15
|
import { TemplateDetail } from './pages/TemplateDetail.js';
|
|
15
|
-
import
|
|
16
|
+
import { ToolCollection } from './pages/ToolCollection.js';
|
|
17
|
+
import { TypeCollection } from './pages/TypeCollection.js';
|
|
18
|
+
import { TypeDetail } from './pages/TypeDetail.js';
|
|
16
19
|
|
|
17
20
|
const routes: Route[] = [
|
|
18
21
|
{ path: '/', Component: HomePage },
|
|
19
22
|
{ path: '/interactions/:collection', Component: InteractionCollection },
|
|
20
23
|
{ path: '/interactions/:collection/:name', Component: InteractionDetail },
|
|
21
24
|
{ path: '/tools/:collection', Component: ToolCollection },
|
|
25
|
+
{ path: '/activities/:collection', Component: ActivityCollection },
|
|
22
26
|
{ path: '/skills/:collection', Component: SkillCollection },
|
|
23
27
|
{ path: '/skills/:collection/:name', Component: SkillDetail },
|
|
24
28
|
{ path: '/types/:collection', Component: TypeCollection },
|
|
@@ -37,7 +41,9 @@ export interface AdminAppProps {
|
|
|
37
41
|
|
|
38
42
|
/**
|
|
39
43
|
* Admin app shell — loads data, provides context, and renders nested routes.
|
|
40
|
-
*
|
|
44
|
+
*
|
|
45
|
+
* Requires a parent VertesiaShell (or equivalent providers for ThemeProvider,
|
|
46
|
+
* UserSessionProvider, ToastProvider).
|
|
41
47
|
*/
|
|
42
48
|
export function AdminApp({ baseUrl = '/api' }: AdminAppProps) {
|
|
43
49
|
const { data: serverInfo, isLoading: loadingInfo, error: infoError } = useServerInfo(baseUrl);
|
|
@@ -51,27 +57,27 @@ export function AdminApp({ baseUrl = '/api' }: AdminAppProps) {
|
|
|
51
57
|
|
|
52
58
|
if (isLoading) {
|
|
53
59
|
return (
|
|
54
|
-
|
|
55
|
-
<
|
|
56
|
-
|
|
57
|
-
</>
|
|
60
|
+
<div className="flex h-64 items-center justify-center text-muted-foreground">
|
|
61
|
+
<Spinner />
|
|
62
|
+
</div>
|
|
58
63
|
);
|
|
59
64
|
}
|
|
60
65
|
|
|
61
66
|
if (error) {
|
|
62
67
|
return (
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
</>
|
|
68
|
+
<div className="p-6 text-destructive">
|
|
69
|
+
Failed to load server info. Is the API running?
|
|
70
|
+
</div>
|
|
67
71
|
);
|
|
68
72
|
}
|
|
69
73
|
|
|
70
74
|
if (!serverInfo || !resourceData) return null;
|
|
71
75
|
|
|
76
|
+
const title = serverInfo.message.replace('Vertesia Tools API', 'Tools Server');
|
|
77
|
+
|
|
72
78
|
return (
|
|
73
|
-
|
|
74
|
-
<
|
|
79
|
+
<div className="min-h-screen bg-background text-foreground">
|
|
80
|
+
<AdminTopBar title={title} />
|
|
75
81
|
<AdminContext.Provider value={{
|
|
76
82
|
serverInfo,
|
|
77
83
|
collections: resourceData.collections,
|
|
@@ -82,6 +88,6 @@ export function AdminApp({ baseUrl = '/api' }: AdminAppProps) {
|
|
|
82
88
|
<RouteComponent />
|
|
83
89
|
</NestedRouterProvider>
|
|
84
90
|
</AdminContext.Provider>
|
|
85
|
-
|
|
91
|
+
</div>
|
|
86
92
|
);
|
|
87
93
|
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { Avatar, Button, ModeToggle } from '@vertesia/ui/core';
|
|
2
|
+
import { useUserSession } from '@vertesia/ui/session';
|
|
3
|
+
import { LogOut } from 'lucide-react';
|
|
4
|
+
|
|
5
|
+
interface AdminTopBarProps {
|
|
6
|
+
title: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function AdminTopBar({ title }: AdminTopBarProps) {
|
|
10
|
+
const { user, logout } = useUserSession();
|
|
11
|
+
|
|
12
|
+
return (
|
|
13
|
+
<header className="sticky top-0 z-40 flex h-14 items-center justify-between border-b border-border bg-background px-6">
|
|
14
|
+
<div className="flex items-center gap-3">
|
|
15
|
+
<div className="flex size-8 items-center justify-center rounded-lg bg-primary text-xs font-semibold uppercase tracking-wider text-primary-foreground">
|
|
16
|
+
{title.split(/\s+/).map(w => w[0]).filter(Boolean).slice(0, 2).join('')}
|
|
17
|
+
</div>
|
|
18
|
+
<span className="text-sm font-semibold text-foreground">{title}</span>
|
|
19
|
+
</div>
|
|
20
|
+
|
|
21
|
+
<div className="flex items-center gap-3">
|
|
22
|
+
<ModeToggle label={false} />
|
|
23
|
+
{user && (
|
|
24
|
+
<>
|
|
25
|
+
<Avatar size="sm" name={user.name} color="bg-primary" />
|
|
26
|
+
<Button
|
|
27
|
+
variant="outline"
|
|
28
|
+
size="sm"
|
|
29
|
+
onClick={() => logout()}
|
|
30
|
+
alt="Sign out"
|
|
31
|
+
>
|
|
32
|
+
<LogOut className="size-4" />
|
|
33
|
+
</Button>
|
|
34
|
+
</>
|
|
35
|
+
)}
|
|
36
|
+
</div>
|
|
37
|
+
</header>
|
|
38
|
+
);
|
|
39
|
+
}
|
|
@@ -1,23 +1,29 @@
|
|
|
1
|
+
import { Card, CardContent } from '@vertesia/ui/core';
|
|
1
2
|
import { NavLink } from '@vertesia/ui/router';
|
|
3
|
+
|
|
2
4
|
import type { CollectionInfo } from '../types.js';
|
|
5
|
+
import { TYPE_VARIANTS } from './typeVariants.js';
|
|
3
6
|
|
|
4
7
|
export function CollectionCard({ collection }: { collection: CollectionInfo }) {
|
|
5
|
-
const
|
|
8
|
+
const plural = collection.type === 'activity' ? 'activities' : `${collection.type}s`;
|
|
9
|
+
const href = `/${plural}/${collection.name}`;
|
|
6
10
|
|
|
7
11
|
return (
|
|
8
|
-
<NavLink href={href} className="
|
|
9
|
-
<
|
|
10
|
-
<
|
|
11
|
-
{collection.type}
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
12
|
+
<NavLink href={href} className="block no-underline">
|
|
13
|
+
<Card className="cursor-pointer transition-all hover:-translate-y-0.5 hover:shadow-md">
|
|
14
|
+
<CardContent className="p-5">
|
|
15
|
+
<span className={`mb-2 inline-block rounded-full px-2 py-0.5 text-[0.7rem] font-semibold uppercase tracking-wide ${TYPE_VARIANTS[collection.type] ?? ''}`}>
|
|
16
|
+
{collection.type}
|
|
17
|
+
</span>
|
|
18
|
+
<div className="font-semibold text-card-foreground">{collection.title}</div>
|
|
19
|
+
<div className="mt-1 text-sm text-muted-foreground">
|
|
20
|
+
{collection.description || 'No description'}
|
|
21
|
+
</div>
|
|
22
|
+
<div className="mt-2 font-mono text-xs text-muted-foreground">
|
|
23
|
+
{collection.count} {collection.count === 1 ? 'item' : 'items'}
|
|
24
|
+
</div>
|
|
25
|
+
</CardContent>
|
|
26
|
+
</Card>
|
|
21
27
|
</NavLink>
|
|
22
28
|
);
|
|
23
29
|
}
|
|
@@ -1,6 +1,10 @@
|
|
|
1
|
+
import { Badge } from '@vertesia/ui/core';
|
|
2
|
+
import { NavLink } from '@vertesia/ui/router';
|
|
3
|
+
import { ArrowLeft } from 'lucide-react';
|
|
1
4
|
import type { ReactNode } from 'react';
|
|
5
|
+
|
|
2
6
|
import type { ResourceType } from '../types.js';
|
|
3
|
-
import {
|
|
7
|
+
import { TYPE_VARIANTS } from './typeVariants.js';
|
|
4
8
|
|
|
5
9
|
interface DetailPageProps {
|
|
6
10
|
type: ResourceType;
|
|
@@ -13,22 +17,27 @@ interface DetailPageProps {
|
|
|
13
17
|
|
|
14
18
|
export function DetailPage({ type, title, description, tags, backHref = '/', children }: DetailPageProps) {
|
|
15
19
|
return (
|
|
16
|
-
<div className="
|
|
17
|
-
<nav className="
|
|
20
|
+
<div className="mx-auto max-w-5xl px-7 py-10">
|
|
21
|
+
<nav className="mb-5 flex items-center gap-4">
|
|
18
22
|
{backHref !== '/' && (
|
|
19
|
-
<NavLink href="/" className="
|
|
23
|
+
<NavLink href="/" className="text-sm text-primary hover:opacity-75">Home</NavLink>
|
|
20
24
|
)}
|
|
21
|
-
<NavLink href={backHref} className="
|
|
25
|
+
<NavLink href={backHref} className="flex items-center gap-1 text-sm text-primary hover:opacity-75">
|
|
26
|
+
<ArrowLeft className="size-3.5" />
|
|
27
|
+
Back
|
|
28
|
+
</NavLink>
|
|
22
29
|
</nav>
|
|
23
30
|
|
|
24
|
-
<div className="
|
|
25
|
-
<span className={`
|
|
26
|
-
|
|
27
|
-
|
|
31
|
+
<div className="mb-8">
|
|
32
|
+
<span className={`mb-2 inline-block rounded-full px-2 py-0.5 text-[0.7rem] font-semibold uppercase tracking-wide ${TYPE_VARIANTS[type] ?? ''}`}>
|
|
33
|
+
{type}
|
|
34
|
+
</span>
|
|
35
|
+
<h1 className="-tracking-wide text-3xl font-bold text-foreground">{title}</h1>
|
|
36
|
+
{description && <p className="mt-1 text-sm leading-relaxed text-muted-foreground">{description}</p>}
|
|
28
37
|
{tags && tags.length > 0 && (
|
|
29
|
-
<div className="
|
|
38
|
+
<div className="mt-3 flex flex-wrap gap-1.5">
|
|
30
39
|
{tags.map(tag => (
|
|
31
|
-
<
|
|
40
|
+
<Badge key={tag} variant="default">{tag}</Badge>
|
|
32
41
|
))}
|
|
33
42
|
</div>
|
|
34
43
|
)}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { Button } from '@vertesia/ui/core';
|
|
2
|
+
import { Check, Copy } from 'lucide-react';
|
|
1
3
|
import { useState } from 'react';
|
|
2
4
|
|
|
3
5
|
export function EndpointPanel({ label, path }: { label: string; path: string }) {
|
|
@@ -11,13 +13,20 @@ export function EndpointPanel({ label, path }: { label: string; path: string })
|
|
|
11
13
|
}
|
|
12
14
|
|
|
13
15
|
return (
|
|
14
|
-
<div className="
|
|
15
|
-
<div className="
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
16
|
+
<div className="mb-3">
|
|
17
|
+
<div className="mb-1 text-[0.7rem] font-medium uppercase tracking-widest text-primary">
|
|
18
|
+
{label}
|
|
19
|
+
</div>
|
|
20
|
+
<div className="flex items-center gap-2 rounded-lg border border-border bg-muted-background px-3 py-2">
|
|
21
|
+
<code className="flex-1 font-mono text-sm text-foreground">{path}</code>
|
|
22
|
+
<Button
|
|
23
|
+
variant="ghost"
|
|
24
|
+
size="xs"
|
|
25
|
+
onClick={handleCopy}
|
|
26
|
+
aria-label="Copy full URL"
|
|
27
|
+
>
|
|
28
|
+
{copied ? <Check className="size-3.5" /> : <Copy className="size-3.5" />}
|
|
29
|
+
</Button>
|
|
21
30
|
</div>
|
|
22
31
|
</div>
|
|
23
32
|
);
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { Card } from '@vertesia/ui/core';
|
|
2
|
+
import { Download, LayoutDashboard } from 'lucide-react';
|
|
3
|
+
|
|
1
4
|
import type { ResourceItem, ResourceType } from '../types.js';
|
|
2
5
|
import { EndpointPanel } from './EndpointPanel.js';
|
|
3
6
|
import { SummaryBadge } from './SummaryBadge.js';
|
|
@@ -22,6 +25,7 @@ function countByType(resources: ResourceItem[]): Record<string, number> {
|
|
|
22
25
|
|
|
23
26
|
const badgeLabels: { type: ResourceType; label: string }[] = [
|
|
24
27
|
{ type: 'tool', label: 'tool' },
|
|
28
|
+
{ type: 'activity', label: 'activity' },
|
|
25
29
|
{ type: 'skill', label: 'skill' },
|
|
26
30
|
{ type: 'interaction', label: 'interaction' },
|
|
27
31
|
{ type: 'type', label: 'content type' },
|
|
@@ -33,56 +37,59 @@ export function HeroSection({ title, version, resources }: HeroSectionProps) {
|
|
|
33
37
|
const counts = countByType(resources);
|
|
34
38
|
|
|
35
39
|
return (
|
|
36
|
-
<
|
|
37
|
-
<div className="
|
|
38
|
-
<div className="
|
|
39
|
-
<div className="
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
<
|
|
44
|
-
|
|
40
|
+
<Card className="mb-10 overflow-hidden border bg-linear-to-br from-card to-muted-background">
|
|
41
|
+
<div className="flex flex-col gap-6 p-6 md:flex-row md:items-start md:justify-between">
|
|
42
|
+
<div className="flex flex-1 flex-col gap-3">
|
|
43
|
+
<div className="flex items-center gap-4">
|
|
44
|
+
<div className="flex size-14 items-center justify-center rounded-xl bg-linear-to-br from-sky-400 to-indigo-500 text-sm font-semibold uppercase tracking-wider text-white shadow-lg">
|
|
45
|
+
{getInitials(title)}
|
|
46
|
+
</div>
|
|
47
|
+
<div>
|
|
48
|
+
<p className="text-xs font-medium uppercase tracking-widest text-muted-foreground">Tools Server</p>
|
|
49
|
+
<h1 className="-tracking-wide text-2xl font-bold text-foreground">{title}</h1>
|
|
50
|
+
</div>
|
|
45
51
|
</div>
|
|
46
|
-
</div>
|
|
47
52
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
53
|
+
<p className="text-sm text-muted-foreground">
|
|
54
|
+
Discover the tools, skills, interactions, and content types exposed by this server.
|
|
55
|
+
</p>
|
|
51
56
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
+
<div className="flex flex-wrap gap-2">
|
|
58
|
+
{badgeLabels.map(({ type, label }) => (
|
|
59
|
+
<SummaryBadge key={type} count={counts[type] || 0} label={label} />
|
|
60
|
+
))}
|
|
61
|
+
</div>
|
|
57
62
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
63
|
+
<div className="flex flex-wrap gap-3 pt-1">
|
|
64
|
+
<a
|
|
65
|
+
href="/app/"
|
|
66
|
+
target="_blank"
|
|
67
|
+
rel="noopener noreferrer"
|
|
68
|
+
className="inline-flex h-8 items-center gap-2 rounded bg-primary px-3 text-xs font-medium text-white shadow-xs hover:bg-primary/90"
|
|
69
|
+
>
|
|
70
|
+
<LayoutDashboard className="size-4" />
|
|
71
|
+
UI Plugin Dev
|
|
72
|
+
</a>
|
|
73
|
+
<a
|
|
74
|
+
href="/lib/plugin.js"
|
|
75
|
+
className="inline-flex h-8 items-center gap-2 rounded bg-primary/5 px-3 text-xs font-medium text-primary shadow-xs hover:bg-primary/10 dark:bg-primary/10 dark:hover:bg-primary/20"
|
|
76
|
+
>
|
|
77
|
+
<Download className="size-4" />
|
|
78
|
+
Plugin Bundle
|
|
79
|
+
</a>
|
|
80
|
+
</div>
|
|
74
81
|
</div>
|
|
75
|
-
</div>
|
|
76
82
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
83
|
+
<aside className="min-w-55 max-w-65 shrink-0">
|
|
84
|
+
<EndpointPanel label="Base endpoint" path="/api" />
|
|
85
|
+
<EndpointPanel label="Package endpoint" path="/api/package" />
|
|
86
|
+
<p className="mt-2 text-xs leading-relaxed text-muted-foreground">
|
|
87
|
+
Use <strong className="text-foreground">POST /api/tools/<collection></strong> or{' '}
|
|
88
|
+
<strong className="text-foreground">POST /api/skills/<collection></strong> to call these from your apps or agents.
|
|
89
|
+
</p>
|
|
90
|
+
<p className="mt-1 text-xs text-muted-foreground">v{version}</p>
|
|
91
|
+
</aside>
|
|
92
|
+
</div>
|
|
93
|
+
</Card>
|
|
87
94
|
);
|
|
88
95
|
}
|
|
@@ -1,25 +1,30 @@
|
|
|
1
|
+
import { Badge, Card, CardContent } from '@vertesia/ui/core';
|
|
2
|
+
|
|
1
3
|
import type { ResourceItem } from '../types.js';
|
|
4
|
+
import { TYPE_VARIANTS } from './typeVariants.js';
|
|
2
5
|
|
|
3
6
|
export function ResourceCard({ resource }: { resource: ResourceItem }) {
|
|
4
7
|
return (
|
|
5
|
-
<
|
|
6
|
-
<
|
|
7
|
-
{resource.type}
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
{resource.tags && resource.tags.length > 0 && (
|
|
14
|
-
<div className="vta-card-tags">
|
|
15
|
-
{resource.tags.map(tag => (
|
|
16
|
-
<span key={tag} className="vta-tag">{tag}</span>
|
|
17
|
-
))}
|
|
8
|
+
<Card className="transition-all hover:-translate-y-0.5 hover:shadow-md">
|
|
9
|
+
<CardContent className="p-5">
|
|
10
|
+
<span className={`mb-2 inline-block rounded-full px-2 py-0.5 text-[0.7rem] font-semibold uppercase tracking-wide ${TYPE_VARIANTS[resource.type] ?? ''}`}>
|
|
11
|
+
{resource.type}
|
|
12
|
+
</span>
|
|
13
|
+
<div className="font-semibold text-card-foreground">{resource.title}</div>
|
|
14
|
+
<div className="mt-1 text-sm text-muted-foreground">
|
|
15
|
+
{resource.description || 'No description'}
|
|
18
16
|
</div>
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
17
|
+
{resource.tags && resource.tags.length > 0 && (
|
|
18
|
+
<div className="mt-2 flex flex-wrap gap-1">
|
|
19
|
+
{resource.tags.map(tag => (
|
|
20
|
+
<Badge key={tag} variant="default">{tag}</Badge>
|
|
21
|
+
))}
|
|
22
|
+
</div>
|
|
23
|
+
)}
|
|
24
|
+
{resource.url && (
|
|
25
|
+
<div className="mt-2 truncate font-mono text-xs text-muted-foreground">{resource.url}</div>
|
|
26
|
+
)}
|
|
27
|
+
</CardContent>
|
|
28
|
+
</Card>
|
|
24
29
|
);
|
|
25
30
|
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { Separator } from '@vertesia/ui/core';
|
|
2
|
+
|
|
1
3
|
import type { ResourceItem } from '../types.js';
|
|
2
4
|
import { ResourceCard } from './ResourceCard.js';
|
|
3
5
|
|
|
@@ -13,15 +15,15 @@ export function ResourceSection({ title, subtitle, resources, showDivider }: Res
|
|
|
13
15
|
|
|
14
16
|
return (
|
|
15
17
|
<section>
|
|
16
|
-
{showDivider && <
|
|
18
|
+
{showDivider && <Separator className="my-8" />}
|
|
17
19
|
<div>
|
|
18
|
-
<h2 className="
|
|
20
|
+
<h2 className="text-xl font-semibold text-foreground">
|
|
19
21
|
{title}
|
|
20
|
-
<span className="
|
|
22
|
+
<span className="ml-2 text-sm font-normal text-muted-foreground">({resources.length})</span>
|
|
21
23
|
</h2>
|
|
22
|
-
<p className="
|
|
24
|
+
<p className="mb-4 text-sm text-muted-foreground">{subtitle}</p>
|
|
23
25
|
</div>
|
|
24
|
-
<div className="
|
|
26
|
+
<div className="grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3">
|
|
25
27
|
{resources.map(r => (
|
|
26
28
|
<ResourceCard key={`${r.type}:${r.name}`} resource={r} />
|
|
27
29
|
))}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { Input } from '@vertesia/ui/core';
|
|
2
|
+
|
|
1
3
|
interface SearchBarProps {
|
|
2
4
|
value: string;
|
|
3
5
|
onChange: (value: string) => void;
|
|
@@ -11,22 +13,22 @@ export function SearchBar({ value, onChange, placeholder, resultCount, totalCoun
|
|
|
11
13
|
const noResults = hasQuery && resultCount === 0;
|
|
12
14
|
|
|
13
15
|
return (
|
|
14
|
-
<div className="
|
|
15
|
-
<
|
|
16
|
+
<div className="mb-7">
|
|
17
|
+
<Input
|
|
16
18
|
type="search"
|
|
17
19
|
value={value}
|
|
18
|
-
onChange={
|
|
20
|
+
onChange={onChange}
|
|
19
21
|
placeholder={placeholder || 'Search collections...'}
|
|
20
|
-
className="
|
|
22
|
+
className="max-w-sm rounded-full"
|
|
21
23
|
autoComplete="off"
|
|
22
24
|
/>
|
|
23
25
|
{hasQuery && !noResults && (
|
|
24
|
-
<p className="
|
|
26
|
+
<p className="mt-1.5 text-xs text-muted-foreground">
|
|
25
27
|
Showing {resultCount} of {totalCount} resources
|
|
26
28
|
</p>
|
|
27
29
|
)}
|
|
28
30
|
{noResults && (
|
|
29
|
-
<p className="
|
|
31
|
+
<p className="mt-2 text-sm text-destructive">
|
|
30
32
|
No resources match this search.
|
|
31
33
|
</p>
|
|
32
34
|
)}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
+
import { DotBadge } from '@vertesia/ui/core';
|
|
2
|
+
|
|
1
3
|
export function SummaryBadge({ count, label }: { count: number; label: string }) {
|
|
2
4
|
if (count === 0) return null;
|
|
3
5
|
return (
|
|
4
|
-
<
|
|
5
|
-
<span className="vta-badge-dot" />
|
|
6
|
+
<DotBadge variant="success">
|
|
6
7
|
{count} {label}{count !== 1 ? 's' : ''}
|
|
7
|
-
</
|
|
8
|
+
</DotBadge>
|
|
8
9
|
);
|
|
9
10
|
}
|
package/src/components/index.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
export {
|
|
2
|
-
export { SearchBar } from './SearchBar.js';
|
|
3
|
-
export { ResourceSection } from './ResourceSection.js';
|
|
4
|
-
export { ResourceCard } from './ResourceCard.js';
|
|
1
|
+
export { AdminTopBar } from './AdminTopBar.js';
|
|
5
2
|
export { CollectionCard } from './CollectionCard.js';
|
|
3
|
+
export { DetailPage } from './DetailPage.js';
|
|
6
4
|
export { EndpointPanel } from './EndpointPanel.js';
|
|
5
|
+
export { HeroSection } from './HeroSection.js';
|
|
6
|
+
export { ResourceCard } from './ResourceCard.js';
|
|
7
|
+
export { ResourceSection } from './ResourceSection.js';
|
|
8
|
+
export { SearchBar } from './SearchBar.js';
|
|
7
9
|
export { SummaryBadge } from './SummaryBadge.js';
|
|
8
|
-
export { DetailPage } from './DetailPage.js';
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/** Tailwind class mappings for resource type badges. */
|
|
2
|
+
export const TYPE_VARIANTS: Record<string, string> = {
|
|
3
|
+
tool: 'bg-blue-100 text-blue-800 dark:bg-blue-500/15 dark:text-blue-300',
|
|
4
|
+
skill: 'bg-emerald-100 text-emerald-800 dark:bg-emerald-500/15 dark:text-emerald-300',
|
|
5
|
+
interaction: 'bg-amber-100 text-amber-800 dark:bg-amber-500/15 dark:text-amber-300',
|
|
6
|
+
type: 'bg-gray-100 text-gray-700 dark:bg-gray-500/20 dark:text-gray-300',
|
|
7
|
+
template: 'bg-violet-100 text-violet-800 dark:bg-violet-500/15 dark:text-violet-300',
|
|
8
|
+
activity: 'bg-rose-100 text-rose-800 dark:bg-rose-500/15 dark:text-rose-300',
|
|
9
|
+
mcp: 'bg-pink-100 text-pink-800 dark:bg-pink-500/15 dark:text-pink-300',
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
/** Tailwind class mappings for prompt role badges. */
|
|
13
|
+
export const ROLE_VARIANTS: Record<string, string> = {
|
|
14
|
+
system: 'bg-blue-100 text-blue-800 dark:bg-blue-500/15 dark:text-blue-300',
|
|
15
|
+
user: 'bg-emerald-100 text-emerald-800 dark:bg-emerald-500/15 dark:text-emerald-300',
|
|
16
|
+
assistant: 'bg-violet-100 text-violet-800 dark:bg-violet-500/15 dark:text-violet-300',
|
|
17
|
+
safety: 'bg-pink-100 text-pink-800 dark:bg-pink-500/15 dark:text-pink-300',
|
|
18
|
+
tool: 'bg-amber-100 text-amber-800 dark:bg-amber-500/15 dark:text-amber-300',
|
|
19
|
+
};
|
package/src/dev/env.ts
CHANGED
|
@@ -7,8 +7,8 @@ Env.init({
|
|
|
7
7
|
isDocker: true,
|
|
8
8
|
type: "development",
|
|
9
9
|
endpoints: {
|
|
10
|
-
studio: "https://api.vertesia.io",
|
|
11
|
-
zeno: "https://api.vertesia.io",
|
|
12
|
-
sts: "https://sts.vertesia.io",
|
|
10
|
+
studio: "https://api.us1.vertesia.io",
|
|
11
|
+
zeno: "https://api.us1.vertesia.io",
|
|
12
|
+
sts: "https://sts.us1.vertesia.io",
|
|
13
13
|
}
|
|
14
14
|
});
|
package/src/dev/main.tsx
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
/// <reference types="vite/client" />
|
|
2
|
+
import './index.css';
|
|
2
3
|
import "./env.js"
|
|
4
|
+
|
|
5
|
+
import { I18nProvider } from '@vertesia/ui/i18n';
|
|
6
|
+
import { VertesiaShell } from '@vertesia/ui/shell';
|
|
7
|
+
import { RouterProvider } from '@vertesia/ui/router';
|
|
3
8
|
import { StrictMode } from 'react';
|
|
4
9
|
import { createRoot } from 'react-dom/client';
|
|
5
|
-
|
|
10
|
+
|
|
6
11
|
import { AdminApp } from '../AdminApp.js';
|
|
7
|
-
import { VertesiaShell } from '@vertesia/ui/shell';
|
|
8
12
|
|
|
9
13
|
const baseUrl = import.meta.env.VITE_API_BASE_URL;
|
|
10
14
|
|
|
@@ -29,9 +33,11 @@ if (!baseUrl) {
|
|
|
29
33
|
} else {
|
|
30
34
|
root.render(
|
|
31
35
|
<StrictMode>
|
|
32
|
-
<
|
|
33
|
-
<
|
|
34
|
-
|
|
36
|
+
<I18nProvider lng="en">
|
|
37
|
+
<VertesiaShell>
|
|
38
|
+
<RouterProvider routes={devRoutes} />
|
|
39
|
+
</VertesiaShell>
|
|
40
|
+
</I18nProvider>
|
|
35
41
|
</StrictMode>,
|
|
36
42
|
);
|
|
37
43
|
}
|
package/src/hooks.ts
CHANGED
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import { useFetch } from '@vertesia/ui/core';
|
|
6
|
-
|
|
6
|
+
|
|
7
|
+
import type { ResourceData, ServerInfo } from './types.js';
|
|
7
8
|
import { buildResourceData } from './types.js';
|
|
8
9
|
|
|
9
10
|
/**
|
|
@@ -27,10 +28,11 @@ export function useResourceData(baseUrl: string, mcpEndpoints?: string[]) {
|
|
|
27
28
|
fetchJson('interactions'),
|
|
28
29
|
fetchJson('tools'),
|
|
29
30
|
fetchJson('skills'),
|
|
31
|
+
fetchJson('activities'),
|
|
30
32
|
fetchJson('types'),
|
|
31
33
|
fetchJson('templates'),
|
|
32
|
-
]).then(([interactions, tools, skills, types, templates]) =>
|
|
33
|
-
buildResourceData(interactions, tools, skills, types, templates, mcpEndpoints)
|
|
34
|
+
]).then(([interactions, tools, skills, activities, types, templates]) =>
|
|
35
|
+
buildResourceData(interactions, tools, skills, activities, types, templates, mcpEndpoints)
|
|
34
36
|
);
|
|
35
37
|
}, [baseUrl, mcpEndpoints]);
|
|
36
38
|
}
|