@promptbook/cli 0.103.0-52 → 0.103.0-53
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/apps/agents-server/README.md +1 -1
- package/apps/agents-server/config.ts +3 -3
- package/apps/agents-server/next.config.ts +1 -1
- package/apps/agents-server/public/sw.js +16 -0
- package/apps/agents-server/src/app/AddAgentButton.tsx +24 -4
- package/apps/agents-server/src/app/actions.ts +15 -13
- package/apps/agents-server/src/app/admin/chat-feedback/ChatFeedbackClient.tsx +541 -0
- package/apps/agents-server/src/app/admin/chat-feedback/page.tsx +22 -0
- package/apps/agents-server/src/app/admin/chat-history/ChatHistoryClient.tsx +532 -0
- package/apps/agents-server/src/app/admin/chat-history/page.tsx +21 -0
- package/apps/agents-server/src/app/admin/metadata/MetadataClient.tsx +241 -27
- package/apps/agents-server/src/app/admin/models/page.tsx +22 -0
- package/apps/agents-server/src/app/admin/users/[userId]/UserDetailClient.tsx +131 -0
- package/apps/agents-server/src/app/admin/users/[userId]/page.tsx +21 -0
- package/apps/agents-server/src/app/admin/users/page.tsx +18 -0
- package/apps/agents-server/src/app/agents/[agentName]/ClearAgentChatFeedbackButton.tsx +63 -0
- package/apps/agents-server/src/app/agents/[agentName]/ClearAgentChatHistoryButton.tsx +63 -0
- package/apps/agents-server/src/app/agents/[agentName]/CloneAgentButton.tsx +41 -0
- package/apps/agents-server/src/app/agents/[agentName]/InstallPwaButton.tsx +74 -0
- package/apps/agents-server/src/app/agents/[agentName]/ServiceWorkerRegister.tsx +24 -0
- package/apps/agents-server/src/app/agents/[agentName]/_utils.ts +19 -0
- package/apps/agents-server/src/app/agents/[agentName]/api/agents/route.ts +67 -0
- package/apps/agents-server/src/app/agents/[agentName]/api/profile/route.ts +3 -0
- package/apps/agents-server/src/app/agents/[agentName]/api/voice/route.ts +177 -0
- package/apps/agents-server/src/app/agents/[agentName]/book/page.tsx +3 -0
- package/apps/agents-server/src/app/agents/[agentName]/book+chat/AgentBookAndChat.tsx +53 -1
- package/apps/agents-server/src/app/agents/[agentName]/generateAgentMetadata.ts +11 -11
- package/apps/agents-server/src/app/agents/[agentName]/history/RestoreVersionButton.tsx +46 -0
- package/apps/agents-server/src/app/agents/[agentName]/history/actions.ts +12 -0
- package/apps/agents-server/src/app/agents/[agentName]/history/page.tsx +62 -0
- package/apps/agents-server/src/app/agents/[agentName]/images/icon-256.png/route.tsx +80 -0
- package/apps/agents-server/src/app/agents/[agentName]/images/screenshot-fullhd.png/route.tsx +92 -0
- package/apps/agents-server/src/app/agents/[agentName]/images/screenshot-phone.png/route.tsx +92 -0
- package/apps/agents-server/src/app/agents/[agentName]/integration/page.tsx +61 -0
- package/apps/agents-server/src/app/agents/[agentName]/opengraph-image.tsx +102 -0
- package/apps/agents-server/src/app/agents/[agentName]/page.tsx +41 -22
- package/apps/agents-server/src/app/api/agents/[agentName]/clone/route.ts +47 -0
- package/apps/agents-server/src/app/api/agents/[agentName]/route.ts +19 -0
- package/apps/agents-server/src/app/api/agents/route.ts +22 -13
- package/apps/agents-server/src/app/api/auth/login/route.ts +6 -44
- package/apps/agents-server/src/app/api/chat-feedback/[id]/route.ts +38 -0
- package/apps/agents-server/src/app/api/chat-feedback/route.ts +157 -0
- package/apps/agents-server/src/app/api/chat-history/[id]/route.ts +37 -0
- package/apps/agents-server/src/app/api/chat-history/route.ts +147 -0
- package/apps/agents-server/src/app/api/federated-agents/route.ts +17 -0
- package/apps/agents-server/src/app/api/upload/route.ts +9 -1
- package/apps/agents-server/src/app/docs/[docId]/page.tsx +62 -0
- package/apps/agents-server/src/app/docs/page.tsx +33 -0
- package/apps/agents-server/src/app/layout.tsx +29 -3
- package/apps/agents-server/src/app/manifest.ts +109 -0
- package/apps/agents-server/src/app/page.tsx +8 -45
- package/apps/agents-server/src/app/recycle-bin/RestoreAgentButton.tsx +40 -0
- package/apps/agents-server/src/app/recycle-bin/actions.ts +27 -0
- package/apps/agents-server/src/app/recycle-bin/page.tsx +58 -0
- package/apps/agents-server/src/app/restricted/page.tsx +33 -0
- package/apps/agents-server/src/app/test/og-image/README.md +1 -0
- package/apps/agents-server/src/app/test/og-image/opengraph-image.tsx +37 -0
- package/apps/agents-server/src/app/test/og-image/page.tsx +22 -0
- package/apps/agents-server/src/components/Footer/Footer.tsx +175 -0
- package/apps/agents-server/src/components/Header/Header.tsx +445 -79
- package/apps/agents-server/src/components/Homepage/AgentCard.tsx +46 -14
- package/apps/agents-server/src/components/Homepage/AgentsList.tsx +58 -0
- package/apps/agents-server/src/components/Homepage/Card.tsx +1 -1
- package/apps/agents-server/src/components/Homepage/ExternalAgentsSection.tsx +21 -0
- package/apps/agents-server/src/components/Homepage/ExternalAgentsSectionClient.tsx +183 -0
- package/apps/agents-server/src/components/Homepage/ModelsSection.tsx +75 -0
- package/apps/agents-server/src/components/LayoutWrapper/LayoutWrapper.tsx +28 -3
- package/apps/agents-server/src/components/LoginDialog/LoginDialog.tsx +18 -17
- package/apps/agents-server/src/components/Portal/Portal.tsx +38 -0
- package/apps/agents-server/src/components/UsersList/UsersList.tsx +82 -131
- package/apps/agents-server/src/components/UsersList/useUsersAdmin.ts +139 -0
- package/apps/agents-server/src/database/metadataDefaults.ts +38 -6
- package/apps/agents-server/src/middleware.ts +146 -93
- package/apps/agents-server/src/tools/$provideServer.ts +2 -2
- package/apps/agents-server/src/utils/authenticateUser.ts +42 -0
- package/apps/agents-server/src/utils/chatFeedbackAdmin.ts +96 -0
- package/apps/agents-server/src/utils/chatHistoryAdmin.ts +96 -0
- package/apps/agents-server/src/utils/getEffectiveFederatedServers.ts +22 -0
- package/apps/agents-server/src/utils/getFederatedAgents.ts +31 -8
- package/apps/agents-server/src/utils/getFederatedServersFromMetadata.ts +10 -0
- package/apps/agents-server/src/utils/getVisibleCommitmentDefinitions.ts +12 -0
- package/apps/agents-server/src/utils/isUserAdmin.ts +2 -2
- package/apps/agents-server/vercel.json +7 -0
- package/esm/index.es.js +153 -2
- package/esm/index.es.js.map +1 -1
- package/esm/typings/servers.d.ts +8 -1
- package/esm/typings/src/_packages/components.index.d.ts +2 -0
- package/esm/typings/src/_packages/core.index.d.ts +6 -0
- package/esm/typings/src/_packages/types.index.d.ts +2 -0
- package/esm/typings/src/_packages/utils.index.d.ts +2 -0
- package/esm/typings/src/book-2.0/agent-source/AgentModelRequirements.d.ts +7 -0
- package/esm/typings/src/book-components/Chat/Chat/ChatProps.d.ts +4 -0
- package/esm/typings/src/book-components/_common/HamburgerMenu/HamburgerMenu.d.ts +12 -0
- package/esm/typings/src/book-components/icons/MicIcon.d.ts +8 -0
- package/esm/typings/src/collection/agent-collection/constructors/agent-collection-in-supabase/AgentCollectionInSupabase.d.ts +17 -0
- package/esm/typings/src/commitments/MESSAGE/AgentMessageCommitmentDefinition.d.ts +28 -0
- package/esm/typings/src/commitments/MESSAGE/UserMessageCommitmentDefinition.d.ts +28 -0
- package/esm/typings/src/commitments/index.d.ts +20 -1
- package/esm/typings/src/execution/LlmExecutionTools.d.ts +9 -0
- package/esm/typings/src/llm-providers/agent/AgentLlmExecutionTools.d.ts +2 -1
- package/esm/typings/src/llm-providers/agent/RemoteAgent.d.ts +10 -1
- package/esm/typings/src/utils/normalization/normalizeMessageText.d.ts +9 -0
- package/esm/typings/src/utils/normalization/normalizeMessageText.test.d.ts +1 -0
- package/esm/typings/src/version.d.ts +1 -1
- package/package.json +1 -1
- package/umd/index.umd.js +153 -2
- package/umd/index.umd.js.map +1 -1
|
@@ -1,13 +1,19 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import promptbookLogoBlueTransparent from '@/public/logo-blue-white-256.png';
|
|
4
|
-
import { logoutAction } from '@/src/app/actions';
|
|
5
|
-
import { ArrowRight, LogIn, LogOut
|
|
4
|
+
import { $createAgentAction, logoutAction } from '@/src/app/actions';
|
|
5
|
+
import { ArrowRight, ChevronDown, LogIn, LogOut } from 'lucide-react';
|
|
6
6
|
import Image from 'next/image';
|
|
7
7
|
import Link from 'next/link';
|
|
8
|
-
import {
|
|
8
|
+
import { useRouter } from 'next/navigation';
|
|
9
|
+
import { ReactNode, useState } from 'react';
|
|
10
|
+
import { AgentBasicInformation } from '../../../../../src/book-2.0/agent-source/AgentBasicInformation';
|
|
11
|
+
import { HamburgerMenu } from '../../../../../src/book-components/_common/HamburgerMenu/HamburgerMenu';
|
|
9
12
|
import { just } from '../../../../../src/utils/organization/just';
|
|
13
|
+
import type { UserInfo } from '../../utils/getCurrentUser';
|
|
14
|
+
import { getVisibleCommitmentDefinitions } from '../../utils/getVisibleCommitmentDefinitions';
|
|
10
15
|
import { LoginDialog } from '../LoginDialog/LoginDialog';
|
|
16
|
+
import { useUsersAdmin } from '../UsersList/useUsersAdmin';
|
|
11
17
|
|
|
12
18
|
type HeaderProps = {
|
|
13
19
|
/**
|
|
@@ -15,6 +21,11 @@ type HeaderProps = {
|
|
|
15
21
|
*/
|
|
16
22
|
isAdmin?: boolean;
|
|
17
23
|
|
|
24
|
+
/**
|
|
25
|
+
* Current user info (if logged in)
|
|
26
|
+
*/
|
|
27
|
+
currentUser?: UserInfo | null;
|
|
28
|
+
|
|
18
29
|
/**
|
|
19
30
|
* The name of the server
|
|
20
31
|
*/
|
|
@@ -24,74 +35,304 @@ type HeaderProps = {
|
|
|
24
35
|
* The URL of the logo displayed in the heading bar
|
|
25
36
|
*/
|
|
26
37
|
serverLogoUrl: string | null;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* List of agents
|
|
41
|
+
*/
|
|
42
|
+
agents: Array<AgentBasicInformation>;
|
|
27
43
|
};
|
|
28
44
|
|
|
29
45
|
/* TODO: [🐱🚀] Make this Agents server native */
|
|
30
46
|
|
|
47
|
+
type SubMenuItem = {
|
|
48
|
+
label: ReactNode;
|
|
49
|
+
href?: string;
|
|
50
|
+
onClick?: () => void | Promise<void>;
|
|
51
|
+
isBold?: boolean;
|
|
52
|
+
isBordered?: boolean;
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
type MenuItem =
|
|
56
|
+
| {
|
|
57
|
+
type: 'link';
|
|
58
|
+
label: ReactNode;
|
|
59
|
+
href: string;
|
|
60
|
+
}
|
|
61
|
+
| {
|
|
62
|
+
type: 'dropdown';
|
|
63
|
+
label: ReactNode;
|
|
64
|
+
isOpen: boolean;
|
|
65
|
+
setIsOpen: (isOpen: boolean) => void;
|
|
66
|
+
isMobileOpen: boolean;
|
|
67
|
+
setIsMobileOpen: (isOpen: boolean) => void;
|
|
68
|
+
items: Array<SubMenuItem>;
|
|
69
|
+
};
|
|
70
|
+
|
|
31
71
|
export function Header(props: HeaderProps) {
|
|
32
|
-
const { isAdmin = false, serverName, serverLogoUrl } = props;
|
|
72
|
+
const { isAdmin = false, currentUser = null, serverName, serverLogoUrl, agents } = props;
|
|
33
73
|
|
|
34
74
|
const [isMenuOpen, setIsMenuOpen] = useState(false);
|
|
35
75
|
const [isLoginOpen, setIsLoginOpen] = useState(false);
|
|
76
|
+
const [isAgentsOpen, setIsAgentsOpen] = useState(false);
|
|
77
|
+
const [isDocsOpen, setIsDocsOpen] = useState(false);
|
|
78
|
+
const [isUsersOpen, setIsUsersOpen] = useState(false);
|
|
79
|
+
const [isMobileAgentsOpen, setIsMobileAgentsOpen] = useState(false);
|
|
80
|
+
const [isMobileDocsOpen, setIsMobileDocsOpen] = useState(false);
|
|
81
|
+
const [isMobileUsersOpen, setIsMobileUsersOpen] = useState(false);
|
|
82
|
+
const [isCreatingAgent, setIsCreatingAgent] = useState(false);
|
|
83
|
+
const router = useRouter();
|
|
84
|
+
|
|
85
|
+
const { users: adminUsers } = useUsersAdmin();
|
|
36
86
|
|
|
37
87
|
const handleLogout = async () => {
|
|
38
88
|
await logoutAction();
|
|
39
89
|
};
|
|
40
90
|
|
|
91
|
+
const handleCreateAgent = async () => {
|
|
92
|
+
setIsCreatingAgent(true);
|
|
93
|
+
const agentName = await $createAgentAction();
|
|
94
|
+
|
|
95
|
+
if (agentName) {
|
|
96
|
+
router.push(`/agents/${agentName}`);
|
|
97
|
+
setIsAgentsOpen(false);
|
|
98
|
+
setIsMenuOpen(false);
|
|
99
|
+
} else {
|
|
100
|
+
router.refresh();
|
|
101
|
+
setIsCreatingAgent(false);
|
|
102
|
+
setIsAgentsOpen(false);
|
|
103
|
+
setIsMenuOpen(false);
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
// Menu items configuration (DRY principle)
|
|
108
|
+
const menuItems: MenuItem[] = [
|
|
109
|
+
{
|
|
110
|
+
type: 'link' as const,
|
|
111
|
+
label: 'Home',
|
|
112
|
+
href: '/',
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
type: 'dropdown' as const,
|
|
116
|
+
label: 'Documentation',
|
|
117
|
+
isOpen: isDocsOpen,
|
|
118
|
+
setIsOpen: setIsDocsOpen,
|
|
119
|
+
isMobileOpen: isMobileDocsOpen,
|
|
120
|
+
setIsMobileOpen: setIsMobileDocsOpen,
|
|
121
|
+
items: [
|
|
122
|
+
{
|
|
123
|
+
label: 'Overview',
|
|
124
|
+
href: '/docs',
|
|
125
|
+
isBold: true,
|
|
126
|
+
isBordered: true,
|
|
127
|
+
} as SubMenuItem,
|
|
128
|
+
...getVisibleCommitmentDefinitions().map(
|
|
129
|
+
({ primary, aliases }) =>
|
|
130
|
+
({
|
|
131
|
+
label: (
|
|
132
|
+
<>
|
|
133
|
+
{primary.type}
|
|
134
|
+
{aliases.length > 0 && <span className="text-gray-400 font-normal"> / {aliases.join(' / ')}</span>}
|
|
135
|
+
</>
|
|
136
|
+
),
|
|
137
|
+
href: `/docs/${primary.type}`,
|
|
138
|
+
} as SubMenuItem),
|
|
139
|
+
),
|
|
140
|
+
],
|
|
141
|
+
},
|
|
142
|
+
...(isAdmin
|
|
143
|
+
? [
|
|
144
|
+
{
|
|
145
|
+
type: 'dropdown' as const,
|
|
146
|
+
label: 'Agents',
|
|
147
|
+
isOpen: isAgentsOpen,
|
|
148
|
+
setIsOpen: setIsAgentsOpen,
|
|
149
|
+
isMobileOpen: isMobileAgentsOpen,
|
|
150
|
+
setIsMobileOpen: setIsMobileAgentsOpen,
|
|
151
|
+
items: [
|
|
152
|
+
...agents.map(
|
|
153
|
+
(agent) =>
|
|
154
|
+
({
|
|
155
|
+
label: agent.meta.fullname || agent.agentName,
|
|
156
|
+
href: `/${agent.agentName}`,
|
|
157
|
+
} as SubMenuItem),
|
|
158
|
+
),
|
|
159
|
+
{
|
|
160
|
+
label: 'View all agents',
|
|
161
|
+
href: '/',
|
|
162
|
+
isBold: true,
|
|
163
|
+
isBordered: true,
|
|
164
|
+
} as SubMenuItem,
|
|
165
|
+
{
|
|
166
|
+
label: isCreatingAgent ? (
|
|
167
|
+
<div className="flex items-center">
|
|
168
|
+
<span className="mr-2 inline-block h-4 w-4 animate-spin rounded-full border-2 border-blue-500 border-t-transparent" />
|
|
169
|
+
Creating agent...
|
|
170
|
+
</div>
|
|
171
|
+
) : (
|
|
172
|
+
'Create new agent'
|
|
173
|
+
),
|
|
174
|
+
onClick: isCreatingAgent ? undefined : handleCreateAgent,
|
|
175
|
+
isBold: true,
|
|
176
|
+
} as SubMenuItem,
|
|
177
|
+
],
|
|
178
|
+
},
|
|
179
|
+
{
|
|
180
|
+
type: 'link' as const,
|
|
181
|
+
label: 'Models',
|
|
182
|
+
href: '/admin/models',
|
|
183
|
+
},
|
|
184
|
+
{
|
|
185
|
+
type: 'dropdown' as const,
|
|
186
|
+
label: 'Users',
|
|
187
|
+
isOpen: isUsersOpen,
|
|
188
|
+
setIsOpen: setIsUsersOpen,
|
|
189
|
+
isMobileOpen: isMobileUsersOpen,
|
|
190
|
+
setIsMobileOpen: setIsMobileUsersOpen,
|
|
191
|
+
items: [
|
|
192
|
+
...adminUsers.map(
|
|
193
|
+
(user) =>
|
|
194
|
+
({
|
|
195
|
+
label: user.username,
|
|
196
|
+
href: `/admin/users/${encodeURIComponent(user.username)}`,
|
|
197
|
+
} as SubMenuItem),
|
|
198
|
+
),
|
|
199
|
+
{
|
|
200
|
+
label: 'View all users',
|
|
201
|
+
href: '/admin/users',
|
|
202
|
+
isBold: true,
|
|
203
|
+
isBordered: true,
|
|
204
|
+
} as SubMenuItem,
|
|
205
|
+
{
|
|
206
|
+
label: 'Create new user',
|
|
207
|
+
href: '/admin/users#create-user',
|
|
208
|
+
isBold: true,
|
|
209
|
+
} as SubMenuItem,
|
|
210
|
+
],
|
|
211
|
+
},
|
|
212
|
+
{
|
|
213
|
+
type: 'link' as const,
|
|
214
|
+
label: 'Metadata',
|
|
215
|
+
href: '/admin/metadata',
|
|
216
|
+
},
|
|
217
|
+
{
|
|
218
|
+
type: 'link' as const,
|
|
219
|
+
label: 'Chat history',
|
|
220
|
+
href: '/admin/chat-history',
|
|
221
|
+
},
|
|
222
|
+
{
|
|
223
|
+
type: 'link' as const,
|
|
224
|
+
label: 'Chat feedback',
|
|
225
|
+
href: '/admin/chat-feedback',
|
|
226
|
+
},
|
|
227
|
+
{
|
|
228
|
+
type: 'link' as const,
|
|
229
|
+
label: 'About',
|
|
230
|
+
href: 'https://ptbk.io/',
|
|
231
|
+
},
|
|
232
|
+
]
|
|
233
|
+
: []),
|
|
234
|
+
];
|
|
235
|
+
|
|
41
236
|
return (
|
|
42
|
-
<header className="fixed top-0 left-0 right-0 z-50 bg-white/80 backdrop-blur-md border-b border-gray-200 h-
|
|
237
|
+
<header className="fixed top-0 left-0 right-0 z-50 bg-white/80 backdrop-blur-md border-b border-gray-200 h-16">
|
|
43
238
|
<LoginDialog isOpen={isLoginOpen} onClose={() => setIsLoginOpen(false)} />
|
|
44
|
-
<div className="container mx-auto px-4">
|
|
45
|
-
<div className="flex items-center justify-between h-
|
|
46
|
-
{/* Logo
|
|
239
|
+
<div className="container mx-auto px-4 h-full">
|
|
240
|
+
<div className="flex items-center justify-between h-full">
|
|
241
|
+
{/* Logo */}
|
|
47
242
|
<Link href="/" className="flex items-center gap-3 hover:opacity-80 transition-opacity">
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
243
|
+
{serverLogoUrl ? (
|
|
244
|
+
// Note: `next/image` does not load external images well without extra config
|
|
245
|
+
// eslint-disable-next-line @next/next/no-img-element
|
|
246
|
+
<img
|
|
247
|
+
src={serverLogoUrl}
|
|
248
|
+
alt={serverName}
|
|
249
|
+
width={32}
|
|
250
|
+
height={32}
|
|
251
|
+
className="w-8 h-8 object-contain"
|
|
252
|
+
/>
|
|
253
|
+
) : (
|
|
254
|
+
<Image
|
|
255
|
+
src={promptbookLogoBlueTransparent}
|
|
256
|
+
alt={serverName}
|
|
257
|
+
width={32}
|
|
258
|
+
height={32}
|
|
259
|
+
className="w-8 h-8 object-contain"
|
|
260
|
+
/>
|
|
261
|
+
)}
|
|
262
|
+
<h1 className="text-xl font-bold tracking-tight text-gray-900">{serverName}</h1>
|
|
56
263
|
</Link>
|
|
57
264
|
|
|
58
265
|
{/* Desktop Navigation */}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
266
|
+
<nav className="hidden lg:flex items-center gap-8">
|
|
267
|
+
{menuItems.map((item, index) => {
|
|
268
|
+
if (item.type === 'link') {
|
|
269
|
+
return (
|
|
270
|
+
<Link
|
|
271
|
+
key={index}
|
|
272
|
+
href={item.href}
|
|
273
|
+
className="text-sm font-medium text-gray-600 hover:text-gray-900 transition-colors cursor-pointer"
|
|
274
|
+
>
|
|
275
|
+
{item.label}
|
|
276
|
+
</Link>
|
|
277
|
+
);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
if (item.type === 'dropdown') {
|
|
281
|
+
return (
|
|
282
|
+
<div key={index} className="relative">
|
|
283
|
+
<button
|
|
284
|
+
className="flex items-center gap-1 text-sm font-medium text-gray-600 hover:text-gray-900 transition-colors cursor-pointer"
|
|
285
|
+
onClick={() => item.setIsOpen(!item.isOpen)}
|
|
286
|
+
onBlur={() => setTimeout(() => item.setIsOpen(false), 200)}
|
|
287
|
+
>
|
|
288
|
+
{item.label}
|
|
289
|
+
<ChevronDown className="w-4 h-4" />
|
|
290
|
+
</button>
|
|
291
|
+
|
|
292
|
+
{item.isOpen && (
|
|
293
|
+
<div className="absolute top-full left-0 mt-2 w-56 bg-white rounded-md shadow-lg border border-gray-100 py-1 z-50 animate-in fade-in zoom-in-95 duration-200 max-h-[80vh] overflow-y-auto">
|
|
294
|
+
{item.items.map((subItem, subIndex) => {
|
|
295
|
+
const className = `block px-4 py-2 text-sm text-gray-700 hover:bg-gray-50 hover:text-gray-900 ${
|
|
296
|
+
subItem.isBold ? 'font-medium' : ''
|
|
297
|
+
} ${subItem.isBordered ? 'border-b border-gray-100' : ''}`;
|
|
298
|
+
|
|
299
|
+
if (subItem.onClick) {
|
|
300
|
+
return (
|
|
301
|
+
<button
|
|
302
|
+
key={subIndex}
|
|
303
|
+
onClick={subItem.onClick}
|
|
304
|
+
className={`${className} w-full text-left`}
|
|
305
|
+
>
|
|
306
|
+
{subItem.label}
|
|
307
|
+
</button>
|
|
308
|
+
);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
return (
|
|
312
|
+
<Link
|
|
313
|
+
key={subIndex}
|
|
314
|
+
href={subItem.href!}
|
|
315
|
+
className={className}
|
|
316
|
+
onClick={() => item.setIsOpen(false)}
|
|
317
|
+
>
|
|
318
|
+
{subItem.label}
|
|
319
|
+
</Link>
|
|
320
|
+
);
|
|
321
|
+
})}
|
|
322
|
+
</div>
|
|
323
|
+
)}
|
|
324
|
+
</div>
|
|
325
|
+
);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
return null;
|
|
329
|
+
})}
|
|
89
330
|
|
|
90
331
|
{just(false /* TODO: [🧠] Figure out what to do with theese links */) && (
|
|
91
332
|
<Link
|
|
92
333
|
href="https://ptbk.io/"
|
|
93
334
|
target="_blank"
|
|
94
|
-
className="text-gray-600 hover:text-gray-900 transition-colors cursor-pointer"
|
|
335
|
+
className="text-sm font-medium text-gray-600 hover:text-gray-900 transition-colors cursor-pointer"
|
|
95
336
|
>
|
|
96
337
|
Create your server
|
|
97
338
|
</Link>
|
|
@@ -109,54 +350,179 @@ export function Header(props: HeaderProps) {
|
|
|
109
350
|
</Link>
|
|
110
351
|
)}
|
|
111
352
|
|
|
112
|
-
{!isAdmin
|
|
353
|
+
{!currentUser && !isAdmin && (
|
|
113
354
|
<button
|
|
114
355
|
onClick={() => {
|
|
115
356
|
setIsLoginOpen(true);
|
|
116
357
|
setIsMenuOpen(false);
|
|
117
358
|
}}
|
|
118
|
-
className="
|
|
359
|
+
className="hidden lg:inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium h-10 px-4 py-2 text-gray-600 hover:text-gray-900 hover:bg-gray-100 transition-colors"
|
|
119
360
|
>
|
|
120
361
|
Log in
|
|
121
362
|
<LogIn className="ml-2 w-4 h-4" />
|
|
122
363
|
</button>
|
|
123
|
-
) : (
|
|
124
|
-
<button
|
|
125
|
-
onClick={() => {
|
|
126
|
-
handleLogout();
|
|
127
|
-
setIsMenuOpen(false);
|
|
128
|
-
}}
|
|
129
|
-
className="w-full inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium h-10 px-4 py-2 text-gray-600 hover:text-gray-900 hover:bg-gray-100"
|
|
130
|
-
>
|
|
131
|
-
Log out
|
|
132
|
-
<LogOut className="ml-2 w-4 h-4" />
|
|
133
|
-
</button>
|
|
134
364
|
)}
|
|
135
365
|
|
|
136
|
-
{
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
366
|
+
{(currentUser || isAdmin) && (
|
|
367
|
+
<div className="hidden lg:flex items-center gap-3">
|
|
368
|
+
<span className="inline text-sm text-gray-600">
|
|
369
|
+
Logged in as <strong>{currentUser?.username || 'Admin'}</strong>
|
|
370
|
+
{(currentUser?.isAdmin || isAdmin) && (
|
|
371
|
+
<span className="ml-2 bg-blue-100 text-blue-800 text-xs px-2 py-1 rounded">
|
|
372
|
+
Admin
|
|
373
|
+
</span>
|
|
374
|
+
)}
|
|
375
|
+
</span>
|
|
376
|
+
<button
|
|
377
|
+
onClick={() => {
|
|
378
|
+
handleLogout();
|
|
379
|
+
setIsMenuOpen(false);
|
|
380
|
+
}}
|
|
381
|
+
className="inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium h-10 px-4 py-2 text-gray-600 hover:text-gray-900 hover:bg-gray-100 transition-colors"
|
|
382
|
+
>
|
|
383
|
+
Log out
|
|
384
|
+
<LogOut className="ml-2 w-4 h-4" />
|
|
385
|
+
</button>
|
|
386
|
+
</div>
|
|
144
387
|
)}
|
|
388
|
+
|
|
389
|
+
{/* Mobile Menu Toggle */}
|
|
390
|
+
<HamburgerMenu
|
|
391
|
+
isOpen={isMenuOpen}
|
|
392
|
+
onClick={() => setIsMenuOpen(!isMenuOpen)}
|
|
393
|
+
className="lg:hidden p-2 text-gray-600 hover:text-gray-900"
|
|
394
|
+
/>
|
|
145
395
|
</div>
|
|
146
396
|
</div>
|
|
147
397
|
|
|
148
398
|
{/* Mobile Navigation */}
|
|
149
|
-
{
|
|
150
|
-
<div
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
399
|
+
{isMenuOpen && (
|
|
400
|
+
<div
|
|
401
|
+
className="lg:hidden absolute top-16 left-0 right-0 z-50 bg-white/80 shadow-xl py-4 border-t border-gray-100 animate-in slide-in-from-top-2 h-[calc(100vh-4rem)] overflow-y-auto"
|
|
402
|
+
style={{
|
|
403
|
+
backdropFilter: 'blur(20px)',
|
|
404
|
+
WebkitBackdropFilter: 'blur(20px)',
|
|
405
|
+
}}
|
|
406
|
+
>
|
|
407
|
+
<nav className="container mx-auto flex flex-col gap-4 px-6">
|
|
408
|
+
{/* Login Status for Mobile */}
|
|
409
|
+
<div className="py-2 border-b border-gray-100 mb-2">
|
|
410
|
+
{!currentUser && !isAdmin && (
|
|
411
|
+
<button
|
|
412
|
+
onClick={() => {
|
|
413
|
+
setIsLoginOpen(true);
|
|
414
|
+
setIsMenuOpen(false);
|
|
415
|
+
}}
|
|
416
|
+
className="flex items-center gap-2 text-base font-medium text-gray-600 hover:text-gray-900 w-full"
|
|
417
|
+
>
|
|
418
|
+
<LogIn className="w-4 h-4" />
|
|
419
|
+
Log in
|
|
420
|
+
</button>
|
|
421
|
+
)}
|
|
422
|
+
|
|
423
|
+
{(currentUser || isAdmin) && (
|
|
424
|
+
<div className="flex flex-col gap-3">
|
|
425
|
+
<div className="text-sm text-gray-600">
|
|
426
|
+
Logged in as <strong>{currentUser?.username || 'Admin'}</strong>
|
|
427
|
+
{(currentUser?.isAdmin || isAdmin) && (
|
|
428
|
+
<span className="ml-2 bg-blue-100 text-blue-800 text-xs px-2 py-1 rounded">
|
|
429
|
+
Admin
|
|
430
|
+
</span>
|
|
431
|
+
)}
|
|
432
|
+
</div>
|
|
433
|
+
<button
|
|
434
|
+
onClick={() => {
|
|
435
|
+
handleLogout();
|
|
436
|
+
setIsMenuOpen(false);
|
|
437
|
+
}}
|
|
438
|
+
className="flex items-center gap-2 text-base font-medium text-red-600 hover:text-red-700 w-full"
|
|
439
|
+
>
|
|
440
|
+
<LogOut className="w-4 h-4" />
|
|
441
|
+
Log out
|
|
442
|
+
</button>
|
|
443
|
+
</div>
|
|
444
|
+
)}
|
|
445
|
+
</div>
|
|
446
|
+
|
|
447
|
+
{menuItems.map((item, index) => {
|
|
448
|
+
if (item.type === 'link') {
|
|
449
|
+
return (
|
|
450
|
+
<Link
|
|
451
|
+
key={index}
|
|
452
|
+
href={item.href}
|
|
453
|
+
className="block text-base font-medium text-gray-600 hover:text-gray-900 py-2"
|
|
454
|
+
onClick={() => setIsMenuOpen(false)}
|
|
455
|
+
>
|
|
456
|
+
{item.label}
|
|
457
|
+
</Link>
|
|
458
|
+
);
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
if (item.type === 'dropdown') {
|
|
462
|
+
return (
|
|
463
|
+
<div key={index} className="flex flex-col">
|
|
464
|
+
<button
|
|
465
|
+
className="w-full flex items-center justify-between text-base font-medium text-gray-600 hover:text-gray-900 py-2"
|
|
466
|
+
onClick={() => item.setIsMobileOpen(!item.isMobileOpen)}
|
|
467
|
+
>
|
|
468
|
+
{item.label}
|
|
469
|
+
<ChevronDown
|
|
470
|
+
className={`w-4 h-4 transition-transform duration-200 ${
|
|
471
|
+
item.isMobileOpen ? 'rotate-180' : ''
|
|
472
|
+
}`}
|
|
473
|
+
/>
|
|
474
|
+
</button>
|
|
475
|
+
{item.isMobileOpen && (
|
|
476
|
+
<div className="pl-4 flex flex-col gap-2 border-l-2 border-gray-100 ml-1 mt-1">
|
|
477
|
+
{item.items.map((subItem, subIndex) => {
|
|
478
|
+
const className = `block text-sm ${
|
|
479
|
+
subItem.isBold
|
|
480
|
+
? 'font-medium text-gray-900 hover:text-gray-700'
|
|
481
|
+
: 'text-gray-600 hover:text-gray-900'
|
|
482
|
+
} py-2`;
|
|
483
|
+
|
|
484
|
+
if (subItem.onClick) {
|
|
485
|
+
return (
|
|
486
|
+
<button
|
|
487
|
+
key={subIndex}
|
|
488
|
+
className={`${className} w-full text-left`}
|
|
489
|
+
onClick={subItem.onClick}
|
|
490
|
+
>
|
|
491
|
+
{subItem.label}
|
|
492
|
+
</button>
|
|
493
|
+
);
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
return (
|
|
497
|
+
<Link
|
|
498
|
+
key={subIndex}
|
|
499
|
+
href={subItem.href!}
|
|
500
|
+
className={className}
|
|
501
|
+
onClick={() => setIsMenuOpen(false)}
|
|
502
|
+
>
|
|
503
|
+
{subItem.label}
|
|
504
|
+
</Link>
|
|
505
|
+
);
|
|
506
|
+
})}
|
|
507
|
+
</div>
|
|
508
|
+
)}
|
|
509
|
+
</div>
|
|
510
|
+
);
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
return null;
|
|
514
|
+
})}
|
|
515
|
+
|
|
516
|
+
{just(false /* TODO: [🧠] Figure out what to do with these links */) && (
|
|
517
|
+
<Link
|
|
518
|
+
href="https://ptbk.io/"
|
|
519
|
+
target="_blank"
|
|
520
|
+
className="text-base font-medium text-gray-600 hover:text-gray-900 py-2"
|
|
521
|
+
onClick={() => setIsMenuOpen(false)}
|
|
522
|
+
>
|
|
523
|
+
Create your server
|
|
524
|
+
</Link>
|
|
525
|
+
)}
|
|
160
526
|
</nav>
|
|
161
527
|
</div>
|
|
162
528
|
)}
|
|
@@ -7,22 +7,54 @@ import { Card } from './Card';
|
|
|
7
7
|
type AgentCardProps = {
|
|
8
8
|
agent: AgentBasicInformation;
|
|
9
9
|
href: string;
|
|
10
|
+
isAdmin?: boolean;
|
|
11
|
+
onDelete?: (agentName: string) => void;
|
|
12
|
+
onClone?: (agentName: string) => void;
|
|
10
13
|
};
|
|
11
14
|
|
|
12
|
-
|
|
15
|
+
const ACTION_BUTTON_CLASSES =
|
|
16
|
+
'text-white px-3 py-1 rounded shadow text-xs font-medium transition-colors uppercase tracking-wider opacity-80 hover:opacity-100';
|
|
17
|
+
|
|
18
|
+
export function AgentCard({ agent, href, isAdmin, onDelete, onClone }: AgentCardProps) {
|
|
13
19
|
return (
|
|
14
|
-
<
|
|
15
|
-
<
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
20
|
+
<div className="relative h-full group">
|
|
21
|
+
<Link href={href} className="block h-full">
|
|
22
|
+
<Card
|
|
23
|
+
style={
|
|
24
|
+
!agent.meta.color
|
|
25
|
+
? {}
|
|
26
|
+
: {
|
|
27
|
+
backgroundColor: `${agent.meta.color}22`,
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
>
|
|
31
|
+
<AvatarProfile agent={agent} />
|
|
32
|
+
</Card>
|
|
33
|
+
</Link>
|
|
34
|
+
{isAdmin && (
|
|
35
|
+
<div className="absolute top-2 right-2 flex gap-2 opacity-0 group-hover:opacity-100 transition-opacity z-10">
|
|
36
|
+
<button
|
|
37
|
+
className={`bg-blue-500 hover:bg-blue-600 ${ACTION_BUTTON_CLASSES}`}
|
|
38
|
+
onClick={(e) => {
|
|
39
|
+
e.preventDefault();
|
|
40
|
+
onClone?.(agent.agentName);
|
|
41
|
+
}}
|
|
42
|
+
title="Clone agent"
|
|
43
|
+
>
|
|
44
|
+
Clone
|
|
45
|
+
</button>
|
|
46
|
+
<button
|
|
47
|
+
className={`bg-red-500 hover:bg-red-600 ${ACTION_BUTTON_CLASSES}`}
|
|
48
|
+
onClick={(e) => {
|
|
49
|
+
e.preventDefault();
|
|
50
|
+
onDelete?.(agent.agentName);
|
|
51
|
+
}}
|
|
52
|
+
title="Delete agent"
|
|
53
|
+
>
|
|
54
|
+
Delete
|
|
55
|
+
</button>
|
|
56
|
+
</div>
|
|
57
|
+
)}
|
|
58
|
+
</div>
|
|
27
59
|
);
|
|
28
60
|
}
|