@promptbook/cli 0.103.0-50 → 0.103.0-52

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 (36) hide show
  1. package/apps/agents-server/package.json +1 -0
  2. package/apps/agents-server/src/app/AddAgentButton.tsx +7 -6
  3. package/apps/agents-server/src/app/{metadata → admin/metadata}/MetadataClient.tsx +5 -13
  4. package/apps/agents-server/src/app/{metadata → admin/metadata}/page.tsx +2 -2
  5. package/apps/agents-server/src/app/agents/[agentName]/AgentQrCode.tsx +3 -3
  6. package/apps/agents-server/src/app/agents/[agentName]/generateAgentMetadata.ts +1 -1
  7. package/apps/agents-server/src/app/agents/[agentName]/page.tsx +5 -2
  8. package/apps/agents-server/src/app/api/embed.js/route.ts +93 -0
  9. package/apps/agents-server/src/app/embed/page.tsx +24 -0
  10. package/apps/agents-server/src/app/page.tsx +48 -101
  11. package/apps/agents-server/src/components/Header/Header.tsx +28 -8
  12. package/apps/agents-server/src/components/Homepage/AgentCard.tsx +28 -0
  13. package/apps/agents-server/src/components/Homepage/Card.tsx +18 -0
  14. package/apps/agents-server/src/components/Homepage/ModelCard.tsx +29 -0
  15. package/apps/agents-server/src/components/Homepage/Section.tsx +17 -0
  16. package/apps/agents-server/src/components/Homepage/TechInfoCard.tsx +20 -0
  17. package/apps/agents-server/src/components/UsersList/UsersList.tsx +6 -6
  18. package/apps/agents-server/src/components/VercelDeploymentCard/VercelDeploymentCard.tsx +3 -8
  19. package/apps/agents-server/src/database/migrate.ts +131 -0
  20. package/apps/agents-server/src/database/{schema.sql → migrations/2025-11-0001-initial-schema.sql} +1 -17
  21. package/apps/agents-server/src/database/migrations/2025-11-0002-metadata-table.sql +16 -0
  22. package/apps/agents-server/src/middleware.ts +1 -1
  23. package/esm/index.es.js +192 -10
  24. package/esm/index.es.js.map +1 -1
  25. package/esm/typings/src/book-2.0/agent-source/AgentBasicInformation.d.ts +1 -0
  26. package/esm/typings/src/book-2.0/agent-source/createCommitmentRegex.d.ts +2 -2
  27. package/esm/typings/src/book-components/Chat/Chat/ChatProps.d.ts +6 -0
  28. package/esm/typings/src/book-components/PromptbookAgent/PromptbookAgent.d.ts +10 -1
  29. package/esm/typings/src/commitments/META_COLOR/META_COLOR.d.ts +38 -0
  30. package/esm/typings/src/commitments/_base/BaseCommitmentDefinition.d.ts +2 -1
  31. package/esm/typings/src/commitments/index.d.ts +3 -1
  32. package/esm/typings/src/llm-providers/agent/Agent.d.ts +1 -0
  33. package/esm/typings/src/version.d.ts +1 -1
  34. package/package.json +1 -1
  35. package/umd/index.umd.js +192 -10
  36. package/umd/index.umd.js.map +1 -1
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "name": "promptbook-agents-server",
3
3
  "scripts": {
4
+ "migrate-database": "npx tsx src/database/migrate.ts",
4
5
  "dev": "(npx kill-port 4440 || true) && next dev -p 4440",
5
6
  "test-build": "npm run build",
6
7
  "build": "(npx kill-port 4440 || true) && next build",
@@ -1,20 +1,21 @@
1
1
  'use client';
2
2
 
3
+ import { useRouter } from 'next/navigation';
4
+ import { Card } from '../components/Homepage/Card';
3
5
  import { $createAgentAction } from './actions';
4
6
 
5
7
  export function AddAgentButton() {
8
+ const router = useRouter();
9
+
6
10
  const handleAddAgent = async () => {
7
11
  await $createAgentAction();
8
12
  // TODO: Add proper error handling and UI feedback
9
- window.location.reload(); // Refresh to show the new agent
13
+ router.refresh();
10
14
  };
11
15
 
12
16
  return (
13
- <div
14
- className="block p-6 bg-white rounded-lg shadow-md hover:shadow-xl transition-shadow duration-300 border border-gray-200 hover:border-blue-400 cursor-pointer"
15
- onClick={handleAddAgent}
16
- >
17
- + Add New Agent
17
+ <div onClick={handleAddAgent} className="cursor-pointer">
18
+ <Card>+ Add New Agent</Card>
18
19
  </div>
19
20
  );
20
21
  }
@@ -1,7 +1,7 @@
1
1
  'use client';
2
2
 
3
3
  import { useEffect, useState } from 'react';
4
- import { metadataDefaults } from '../../database/metadataDefaults';
4
+ import { metadataDefaults } from '../../../database/metadataDefaults';
5
5
 
6
6
  type MetadataEntry = {
7
7
  id: number;
@@ -132,15 +132,11 @@ export function MetadataClient() {
132
132
  <h1 className="text-3xl font-bold mb-8">Metadata Management</h1>
133
133
 
134
134
  {error && (
135
- <div className="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded mb-6">
136
- {error}
137
- </div>
135
+ <div className="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded mb-6">{error}</div>
138
136
  )}
139
137
 
140
138
  <div className="bg-white shadow rounded-lg p-6 mb-8">
141
- <h2 className="text-xl font-semibold mb-4">
142
- {editingId ? 'Edit Metadata' : 'Add New Metadata'}
143
- </h2>
139
+ <h2 className="text-xl font-semibold mb-4">{editingId ? 'Edit Metadata' : 'Add New Metadata'}</h2>
144
140
  <form onSubmit={handleSubmit} className="space-y-4">
145
141
  <div>
146
142
  <label htmlFor="key" className="block text-sm font-medium text-gray-700 mb-1">
@@ -235,12 +231,8 @@ export function MetadataClient() {
235
231
  <td className="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">
236
232
  {entry.key}
237
233
  </td>
238
- <td className="px-6 py-4 text-sm text-gray-500 max-w-xs truncate">
239
- {entry.value}
240
- </td>
241
- <td className="px-6 py-4 text-sm text-gray-500">
242
- {entry.note || '-'}
243
- </td>
234
+ <td className="px-6 py-4 text-sm text-gray-500 max-w-xs truncate">{entry.value}</td>
235
+ <td className="px-6 py-4 text-sm text-gray-500">{entry.note || '-'}</td>
244
236
  <td className="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
245
237
  <button
246
238
  onClick={() => handleEdit(entry)}
@@ -1,5 +1,5 @@
1
- import { ForbiddenPage } from '../../components/ForbiddenPage/ForbiddenPage';
2
- import { isUserAdmin } from '../../utils/isUserAdmin';
1
+ import { ForbiddenPage } from '../../../components/ForbiddenPage/ForbiddenPage';
2
+ import { isUserAdmin } from '../../../utils/isUserAdmin';
3
3
  import { MetadataClient } from './MetadataClient';
4
4
 
5
5
  export default async function MetadataPage() {
@@ -5,19 +5,19 @@ import { AgentBasicInformation } from '@promptbook-local/types';
5
5
  import { useState } from 'react';
6
6
  import spaceTrim from 'spacetrim';
7
7
 
8
- type AgentQrCodeProps = Pick<AgentBasicInformation, 'agentName' | 'personaDescription'> & {
8
+ type AgentQrCodeProps = Pick<AgentBasicInformation, 'agentName' | 'personaDescription' | 'meta'> & {
9
9
  agentUrl: string;
10
10
  agentEmail: string;
11
11
  };
12
12
 
13
- export function AgentQrCode({ agentName, agentUrl, agentEmail, personaDescription }: AgentQrCodeProps) {
13
+ export function AgentQrCode({ agentName, agentUrl, agentEmail, personaDescription, meta }: AgentQrCodeProps) {
14
14
  const [mode, setMode] = useState<'contact' | 'link'>('contact');
15
15
 
16
16
  // TODO: [🧠] Should we include more info in VCARD?
17
17
  const vcard = spaceTrim(`
18
18
  BEGIN:VCARD
19
19
  VERSION:3.0
20
- FN:${agentName}
20
+ FN:${meta.fullname || agentName}
21
21
  URL:${agentUrl}
22
22
  EMAIL:${agentEmail}
23
23
  NOTE:${personaDescription}
@@ -11,7 +11,7 @@ export async function generateAgentMetadata({ params }: { params: Promise<{ agen
11
11
  const agentSource = await collection.getAgentSource(agentName);
12
12
  const agentProfile = parseAgentSource(agentSource);
13
13
 
14
- const title = agentProfile.meta.title || agentProfile.agentName;
14
+ const title = agentProfile.meta.fullname || agentProfile.agentName;
15
15
  const description = agentProfile.meta.description || agentProfile.personaDescription || undefined;
16
16
 
17
17
  // Extract image from meta
@@ -78,7 +78,7 @@ export default async function AgentPage({ params }: { params: Promise<{ agentNam
78
78
  // eslint-disable-next-line @next/next/no-img-element
79
79
  <img
80
80
  src={agentProfile.meta.image as string}
81
- alt={agentProfile.agentName || 'Agent'}
81
+ alt={agentProfile.meta.fullname || agentProfile.agentName || 'Agent'}
82
82
  width={64}
83
83
  height={64}
84
84
  className="rounded-full object-cover border-2 aspect-square w-16 h-16"
@@ -86,7 +86,9 @@ export default async function AgentPage({ params }: { params: Promise<{ agentNam
86
86
  />
87
87
  )}
88
88
  <div className="flex-1">
89
- <h1 className="text-3xl font-bold text-gray-900 break-words">{agentProfile.agentName}</h1>
89
+ <h1 className="text-3xl font-bold text-gray-900 break-words">
90
+ {agentProfile.meta.fullname || agentProfile.agentName}
91
+ </h1>
90
92
  <span
91
93
  className="inline-block mt-1 px-2 py-1 rounded text-xs font-semibold text-white"
92
94
  style={{ backgroundColor: brandColor.toHex() }}
@@ -145,6 +147,7 @@ export default async function AgentPage({ params }: { params: Promise<{ agentNam
145
147
  <div className="bg-white rounded-lg p-4 flex flex-col items-center shadow-sm border border-gray-100">
146
148
  <AgentQrCode
147
149
  agentName={agentProfile.agentName || 'Agent'}
150
+ meta={agentProfile.meta}
148
151
  personaDescription={agentProfile.personaDescription}
149
152
  agentUrl={agentUrl}
150
153
  agentEmail={agentEmail}
@@ -0,0 +1,93 @@
1
+ import { NextRequest, NextResponse } from 'next/server';
2
+
3
+ export async function GET(request: NextRequest) {
4
+ const protocol = request.nextUrl.protocol;
5
+ const host = request.nextUrl.host;
6
+ const baseUrl = `${protocol}//${host}`;
7
+
8
+ const script = `
9
+ (function() {
10
+ if (customElements.get('promptbook-agent')) {
11
+ return;
12
+ }
13
+
14
+ class PromptbookAgentElement extends HTMLElement {
15
+ constructor() {
16
+ super();
17
+ this.iframe = null;
18
+ }
19
+
20
+ static get observedAttributes() {
21
+ return ['agent-url'];
22
+ }
23
+
24
+ connectedCallback() {
25
+ this.render();
26
+ window.addEventListener('message', this.handleMessage.bind(this));
27
+ }
28
+
29
+ disconnectedCallback() {
30
+ window.removeEventListener('message', this.handleMessage.bind(this));
31
+ }
32
+
33
+ attributeChangedCallback(name, oldValue, newValue) {
34
+ if (name === 'agent-url' && oldValue !== newValue) {
35
+ this.render();
36
+ }
37
+ }
38
+
39
+ handleMessage(event) {
40
+ if (event.data && event.data.type === 'PROMPTBOOK_AGENT_RESIZE') {
41
+ if (event.data.isOpen) {
42
+ this.iframe.style.width = '450px';
43
+ this.iframe.style.height = '650px';
44
+ this.iframe.style.maxHeight = '90vh';
45
+ this.iframe.style.maxWidth = '90vw';
46
+ this.iframe.style.boxShadow = '0 4px 12px rgba(0,0,0,0.15)';
47
+ this.iframe.style.borderRadius = '12px';
48
+ } else {
49
+ this.iframe.style.width = '60px';
50
+ this.iframe.style.height = '60px';
51
+ this.iframe.style.boxShadow = 'none';
52
+ this.iframe.style.borderRadius = '0';
53
+ }
54
+ }
55
+ }
56
+
57
+ render() {
58
+ const agentUrl = this.getAttribute('agent-url');
59
+ if (!agentUrl) return;
60
+
61
+ if (!this.iframe) {
62
+ this.attachShadow({ mode: 'open' });
63
+ this.iframe = document.createElement('iframe');
64
+ this.iframe.style.border = 'none';
65
+ this.iframe.style.position = 'fixed';
66
+ this.iframe.style.bottom = '20px';
67
+ this.iframe.style.right = '20px';
68
+ this.iframe.style.width = '60px';
69
+ this.iframe.style.height = '60px';
70
+ this.iframe.style.zIndex = '2147483647'; // Max z-index
71
+ this.iframe.style.transition = 'width 0.3s ease, height 0.3s ease';
72
+ this.iframe.style.backgroundColor = 'transparent';
73
+ this.iframe.setAttribute('allow', 'microphone'); // Allow microphone if needed for voice
74
+ this.shadowRoot.appendChild(this.iframe);
75
+ }
76
+
77
+ // Construct embed URL pointing to the Next.js page we created
78
+ const embedUrl = '${baseUrl}/embed?agentUrl=' + encodeURIComponent(agentUrl);
79
+ this.iframe.src = embedUrl;
80
+ }
81
+ }
82
+
83
+ customElements.define('promptbook-agent', PromptbookAgentElement);
84
+ })();
85
+ `;
86
+
87
+ return new NextResponse(script, {
88
+ headers: {
89
+ 'Content-Type': 'application/javascript',
90
+ 'Access-Control-Allow-Origin': '*',
91
+ },
92
+ });
93
+ }
@@ -0,0 +1,24 @@
1
+ 'use client';
2
+
3
+ import { PromptbookAgent } from '@promptbook-local/components';
4
+ import { useSearchParams } from 'next/navigation';
5
+
6
+ export default function EmbedPage() {
7
+ const searchParams = useSearchParams();
8
+ const agentUrl = searchParams.get('agentUrl');
9
+
10
+ if (!agentUrl) {
11
+ return <div className="text-red-500">Missing agentUrl parameter</div>;
12
+ }
13
+
14
+ return (
15
+ <div className="w-full h-full bg-transparent">
16
+ <PromptbookAgent
17
+ agentUrl={agentUrl}
18
+ onOpenChange={(isOpen) => {
19
+ window.parent.postMessage({ type: 'PROMPTBOOK_AGENT_RESIZE', isOpen }, '*');
20
+ }}
21
+ />
22
+ </div>
23
+ );
24
+ }
@@ -3,11 +3,13 @@
3
3
  import { getSingleLlmExecutionTools } from '@promptbook-local/core';
4
4
  import moment from 'moment';
5
5
  import { headers } from 'next/headers';
6
- import Link from 'next/link';
7
- import { AvatarProfile } from '../../../../src/book-components/AvatarProfile/AvatarProfile/AvatarProfile';
8
6
  import { AboutPromptbookInformation } from '../../../../src/utils/misc/xAboutPromptbookInformation';
9
7
  import { $sideEffect } from '../../../../src/utils/organization/$sideEffect';
10
8
  import { AuthControls } from '../components/Auth/AuthControls';
9
+ import { AgentCard } from '../components/Homepage/AgentCard';
10
+ import { ModelCard } from '../components/Homepage/ModelCard';
11
+ import { Section } from '../components/Homepage/Section';
12
+ import { TechInfoCard } from '../components/Homepage/TechInfoCard';
11
13
  import { UsersList } from '../components/UsersList/UsersList';
12
14
  import VercelDeploymentCard from '../components/VercelDeploymentCard/VercelDeploymentCard';
13
15
  import { getMetadata } from '../database/getMetadata';
@@ -15,8 +17,8 @@ import { getLongRunningTask } from '../deamons/longRunningTask';
15
17
  import { $provideAgentCollectionForServer } from '../tools/$provideAgentCollectionForServer';
16
18
  import { $provideExecutionToolsForServer } from '../tools/$provideExecutionToolsForServer';
17
19
  import { $provideServer } from '../tools/$provideServer';
18
- import { getFederatedAgents } from '../utils/getFederatedAgents';
19
20
  import { getCurrentUser } from '../utils/getCurrentUser';
21
+ import { getFederatedAgents } from '../utils/getFederatedAgents';
20
22
  import { isUserAdmin } from '../utils/isUserAdmin';
21
23
  import { AddAgentButton } from './AddAgentButton';
22
24
 
@@ -52,7 +54,7 @@ export default async function HomePage() {
52
54
  const executionTools = await $provideExecutionToolsForServer();
53
55
  const models = await getSingleLlmExecutionTools(executionTools.llm).listModels();
54
56
 
55
- const host = (await headers()).get('host');
57
+ const host = (await headers()).get('host') || 'unknown';
56
58
 
57
59
  return (
58
60
  <div className="min-h-screen bg-gradient-to-br from-blue-50 via-white to-purple-50">
@@ -61,68 +63,34 @@ export default async function HomePage() {
61
63
  <AuthControls initialUser={currentUser} />
62
64
  </div>
63
65
 
64
- <>
65
- <h2 className="text-3xl text-gray-900 mt-4 mb-4">Agents ({agents.length})</h2>
66
- <div className="grid gap-6 md:grid-cols-2 lg:grid-cols-3">
67
- {agents.map((agent) => (
68
- <Link key={agent.agentName} href={`/${agent.agentName}`}>
69
- <AvatarProfile
70
- {...{ agent }}
71
- style={
72
- !agent.meta.color
73
- ? {}
74
- : {
75
- backgroundColor: `${agent.meta.color}22`, // <- TODO: Use Color object here
76
- }
77
- }
78
- className="block p-6 bg-white rounded-lg shadow-md hover:shadow-xl transition-shadow duration-300 border border-gray-200 hover:border-blue-400"
79
- />
80
- </Link>
81
- ))}
82
- {isAdmin && <AddAgentButton />}
83
- </div>
84
- </>
66
+ <Section title={`Agents (${agents.length})`}>
67
+ {agents.map((agent) => (
68
+ <AgentCard key={agent.agentName} agent={agent} href={`/${agent.agentName}`} />
69
+ ))}
70
+ {isAdmin && <AddAgentButton />}
71
+ </Section>
85
72
 
86
73
  {externalAgents.length > 0 && (
87
- <>
88
- <h2 className="text-3xl text-gray-900 mt-16 mb-4">External Agents ({externalAgents.length})</h2>
89
- <div className="grid gap-6 md:grid-cols-2 lg:grid-cols-3">
90
- {externalAgents.map((agent) => (
91
- <Link key={agent.url} href={agent.url}>
92
- <AvatarProfile
93
- {...{ agent }}
94
- style={
95
- !agent.meta.color
96
- ? {}
97
- : {
98
- backgroundColor: `${agent.meta.color}22`, // <- TODO: Use Color object here
99
- }
100
- }
101
- className="block p-6 bg-white rounded-lg shadow-md hover:shadow-xl transition-shadow duration-300 border border-gray-200 hover:border-blue-400"
102
- />
103
- </Link>
104
- ))}
105
- </div>
106
- </>
74
+ <Section title={`External Agents (${externalAgents.length})`}>
75
+ {externalAgents.map((agent) => (
76
+ <AgentCard key={agent.url} agent={agent} href={agent.url} />
77
+ ))}
78
+ </Section>
107
79
  )}
108
80
 
109
81
  {isAdmin && <UsersList />}
110
82
 
111
83
  {isAdmin && (
112
- <>
113
- <h2 className="text-3xl text-gray-900 mt-16 mb-4">Models ({models.length})</h2>
114
- <div className="grid gap-6 md:grid-cols-2 lg:grid-cols-3">
115
- {models.map(({ modelName, modelTitle, modelDescription }) => (
116
- <Link key={modelName} href={`#[🐱‍🚀]`}>
117
- <div className="block p-6 bg-white rounded-lg shadow-md hover:shadow-xl transition-shadow duration-300 border border-gray-200 hover:border-blue-400">
118
- <h2 className="text-2xl font-semibold text-gray-900 mb-2">{modelTitle}</h2>
119
- <code>{modelName}</code>
120
- <p className="text-gray-600">{modelDescription}</p>
121
- </div>
122
- </Link>
123
- ))}
124
- </div>
125
- </>
84
+ <Section title={`Models (${models.length})`}>
85
+ {models.map(({ modelName, modelTitle, modelDescription }) => (
86
+ <ModelCard
87
+ key={modelName}
88
+ modelName={modelName}
89
+ modelTitle={modelTitle || modelName}
90
+ modelDescription={modelDescription}
91
+ />
92
+ ))}
93
+ </Section>
126
94
  )}
127
95
 
128
96
  {isAdmin && (
@@ -133,48 +101,27 @@ export default async function HomePage() {
133
101
  )}
134
102
 
135
103
  {isAdmin && (
136
- <>
137
- <h2 className="text-3xl text-gray-900 mt-16 mb-4">Technical Information</h2>
138
- <div className="grid gap-6 md:grid-cols-2 lg:grid-cols-3">
139
- <Link
140
- href={'#'}
141
- className="block p-6 bg-white rounded-lg shadow-md hover:shadow-xl transition-shadow duration-300 border border-gray-200 hover:border-blue-400"
142
- >
143
- <h2 className="text-2xl font-semibold text-gray-900 mb-2">
144
- Long running task {longRunningTask.taskId}
145
- </h2>
146
- <p className="text-gray-600">Tick: {longRunningTask.tick}</p>
147
- <p className="text-gray-600">
148
- Created At:{' '}
149
- {moment(longRunningTask.createdAt).calendar(undefined, calendarWithSeconds)}
150
- </p>
151
- <p className="text-gray-600">
152
- Updated At:{' '}
153
- {moment(longRunningTask.updatedAt).calendar(undefined, calendarWithSeconds)}
154
- </p>
155
- </Link>
156
-
157
- <VercelDeploymentCard />
158
-
159
- <Link
160
- href={'#'}
161
- className="block p-6 bg-white rounded-lg shadow-md hover:shadow-xl transition-shadow duration-300 border border-gray-200 hover:border-blue-400"
162
- >
163
- <h2 className="text-2xl font-semibold text-gray-900 mb-2">HTTP Information</h2>
164
-
165
- <p className="text-gray-600">Host: {host}</p>
166
- </Link>
167
-
168
- <Link
169
- href={'#'}
170
- className="block p-6 bg-white rounded-lg shadow-md hover:shadow-xl transition-shadow duration-300 border border-gray-200 hover:border-blue-400"
171
- >
172
- <h2 className="text-2xl font-semibold text-gray-900 mb-2">Server</h2>
173
-
174
- <pre>{JSON.stringify(await $provideServer(), null, 2)}</pre>
175
- </Link>
176
- </div>
177
- </>
104
+ <Section title="Technical Information">
105
+ <TechInfoCard title={`Long running task ${longRunningTask.taskId}`}>
106
+ <p className="text-gray-600">Tick: {longRunningTask.tick}</p>
107
+ <p className="text-gray-600">
108
+ Created At: {moment(longRunningTask.createdAt).calendar(undefined, calendarWithSeconds)}
109
+ </p>
110
+ <p className="text-gray-600">
111
+ Updated At: {moment(longRunningTask.updatedAt).calendar(undefined, calendarWithSeconds)}
112
+ </p>
113
+ </TechInfoCard>
114
+
115
+ <VercelDeploymentCard />
116
+
117
+ <TechInfoCard title="HTTP Information">
118
+ <p className="text-gray-600">Host: {host}</p>
119
+ </TechInfoCard>
120
+
121
+ <TechInfoCard title="Server">
122
+ <pre>{JSON.stringify(await $provideServer(), null, 2)}</pre>
123
+ </TechInfoCard>
124
+ </Section>
178
125
  )}
179
126
  </div>
180
127
  </div>
@@ -59,21 +59,41 @@ export function Header(props: HeaderProps) {
59
59
  {/* Desktop Navigation */}
60
60
  <nav className="hidden md:flex items-center gap-8">
61
61
  {isAdmin && (
62
- <Link
63
- href="/metadata"
64
- className="text-gray-600 hover:text-gray-900 transition-colors cursor-pointer"
65
- >
66
- Metadata
67
- </Link>
62
+ <>
63
+ <Link
64
+ href="/"
65
+ className="text-gray-600 hover:text-gray-900 transition-colors cursor-pointer"
66
+ >
67
+ Agents
68
+ </Link>
69
+ <Link
70
+ href="/"
71
+ className="text-gray-600 hover:text-gray-900 transition-colors cursor-pointer"
72
+ >
73
+ Models
74
+ </Link>
75
+ <Link
76
+ href="/admin/metadata"
77
+ className="text-gray-600 hover:text-gray-900 transition-colors cursor-pointer"
78
+ >
79
+ Metadata
80
+ </Link>
81
+ <Link
82
+ href="https://ptbk.io/"
83
+ className="text-gray-600 hover:text-gray-900 transition-colors cursor-pointer"
84
+ >
85
+ About
86
+ </Link>
87
+ </>
68
88
  )}
69
89
 
70
90
  {just(false /* TODO: [🧠] Figure out what to do with theese links */) && (
71
91
  <Link
72
- href="https://ptbk.io/#try-it-yourself"
92
+ href="https://ptbk.io/"
73
93
  target="_blank"
74
94
  className="text-gray-600 hover:text-gray-900 transition-colors cursor-pointer"
75
95
  >
76
- Try it yourself
96
+ Create your server
77
97
  </Link>
78
98
  )}
79
99
  </nav>
@@ -0,0 +1,28 @@
1
+ import Link from 'next/link';
2
+ import React from 'react';
3
+ import { AgentBasicInformation } from '../../../../../src/book-2.0/agent-source/AgentBasicInformation';
4
+ import { AvatarProfile } from '../../../../../src/book-components/AvatarProfile/AvatarProfile/AvatarProfile';
5
+ import { Card } from './Card';
6
+
7
+ type AgentCardProps = {
8
+ agent: AgentBasicInformation;
9
+ href: string;
10
+ };
11
+
12
+ export function AgentCard({ agent, href }: AgentCardProps) {
13
+ return (
14
+ <Link href={href}>
15
+ <Card
16
+ style={
17
+ !agent.meta.color
18
+ ? {}
19
+ : {
20
+ backgroundColor: `${agent.meta.color}22`,
21
+ }
22
+ }
23
+ >
24
+ <AvatarProfile agent={agent} />
25
+ </Card>
26
+ </Link>
27
+ );
28
+ }
@@ -0,0 +1,18 @@
1
+ import React from 'react';
2
+
3
+ type CardProps = {
4
+ children: React.ReactNode;
5
+ className?: string;
6
+ style?: React.CSSProperties;
7
+ };
8
+
9
+ export function Card({ children, className = '', style }: CardProps) {
10
+ return (
11
+ <div
12
+ className={`block p-6 bg-white rounded-lg shadow-md hover:shadow-xl transition-shadow duration-300 border border-gray-200 hover:border-blue-400 ${className}`}
13
+ style={style}
14
+ >
15
+ {children}
16
+ </div>
17
+ );
18
+ }
@@ -0,0 +1,29 @@
1
+ import Link from 'next/link';
2
+ import React from 'react';
3
+ import { Card } from './Card';
4
+
5
+ type ModelCardProps = {
6
+ modelName: string;
7
+ modelTitle: string;
8
+ modelDescription?: string;
9
+ };
10
+
11
+ export function ModelCard({ modelName, modelTitle, modelDescription }: ModelCardProps) {
12
+ return (
13
+ <Link href={`#[🐱‍🚀]`}>
14
+ <Card className="h-full flex flex-col">
15
+ <div className="flex justify-between items-start mb-2">
16
+ <h2 className="text-xl font-bold text-gray-900">{modelTitle}</h2>
17
+ <span className="inline-block bg-gray-100 text-gray-600 text-xs px-2 py-1 rounded font-mono">
18
+ {modelName}
19
+ </span>
20
+ </div>
21
+ {modelDescription && (
22
+ <p className="text-gray-600 text-sm mt-2 flex-grow leading-relaxed">
23
+ {modelDescription}
24
+ </p>
25
+ )}
26
+ </Card>
27
+ </Link>
28
+ );
29
+ }
@@ -0,0 +1,17 @@
1
+ import React from 'react';
2
+
3
+ type SectionProps = {
4
+ title: string;
5
+ children: React.ReactNode;
6
+ };
7
+
8
+ export function Section({ title, children }: SectionProps) {
9
+ return (
10
+ <section className="mt-16 first:mt-4 mb-4">
11
+ <h2 className="text-3xl text-gray-900 mb-6 font-light">{title}</h2>
12
+ <div className="grid gap-6 md:grid-cols-2 lg:grid-cols-3">
13
+ {children}
14
+ </div>
15
+ </section>
16
+ );
17
+ }
@@ -0,0 +1,20 @@
1
+ import Link from 'next/link';
2
+ import React from 'react';
3
+ import { Card } from './Card';
4
+
5
+ type TechInfoCardProps = {
6
+ title: string;
7
+ children: React.ReactNode;
8
+ href?: string;
9
+ };
10
+
11
+ export function TechInfoCard({ title, children, href = '#' }: TechInfoCardProps) {
12
+ return (
13
+ <Link href={href}>
14
+ <Card>
15
+ <h2 className="text-2xl font-semibold text-gray-900 mb-2">{title}</h2>
16
+ {children}
17
+ </Card>
18
+ </Link>
19
+ );
20
+ }