@promptbook/cli 0.103.0-52 → 0.103.0-54
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/api-tokens/ApiTokensClient.tsx +186 -0
- package/apps/agents-server/src/app/admin/api-tokens/page.tsx +13 -0
- 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]/AgentChatWrapper.tsx +10 -2
- 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/openai/chat/completions/route.ts +176 -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 +64 -24
- 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/api-tokens/route.ts +76 -0
- 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 +63 -0
- package/apps/agents-server/src/app/docs/page.tsx +34 -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 +450 -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 +29 -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/database/migrations/2025-12-0010-llm-cache.sql +12 -0
- package/apps/agents-server/src/database/migrations/2025-12-0060-api-tokens.sql +13 -0
- package/apps/agents-server/src/database/schema.ts +51 -0
- package/apps/agents-server/src/middleware.ts +193 -92
- package/apps/agents-server/src/tools/$provideCdnForServer.ts +3 -7
- package/apps/agents-server/src/tools/$provideExecutionToolsForServer.ts +10 -1
- 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/cache/SupabaseCacheStorage.ts +55 -0
- package/apps/agents-server/src/utils/cdn/classes/VercelBlobStorage.ts +63 -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 +279 -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/ACTION/ACTION.d.ts +4 -0
- package/esm/typings/src/commitments/DELETE/DELETE.d.ts +4 -0
- package/esm/typings/src/commitments/FORMAT/FORMAT.d.ts +4 -0
- package/esm/typings/src/commitments/GOAL/GOAL.d.ts +4 -0
- package/esm/typings/src/commitments/KNOWLEDGE/KNOWLEDGE.d.ts +4 -0
- package/esm/typings/src/commitments/MEMORY/MEMORY.d.ts +4 -0
- package/esm/typings/src/commitments/MESSAGE/AgentMessageCommitmentDefinition.d.ts +32 -0
- package/esm/typings/src/commitments/MESSAGE/InitialMessageCommitmentDefinition.d.ts +4 -0
- package/esm/typings/src/commitments/MESSAGE/MESSAGE.d.ts +4 -0
- package/esm/typings/src/commitments/MESSAGE/UserMessageCommitmentDefinition.d.ts +32 -0
- package/esm/typings/src/commitments/META/META.d.ts +4 -0
- package/esm/typings/src/commitments/META_COLOR/META_COLOR.d.ts +4 -0
- package/esm/typings/src/commitments/META_IMAGE/META_IMAGE.d.ts +4 -0
- package/esm/typings/src/commitments/META_LINK/META_LINK.d.ts +4 -0
- package/esm/typings/src/commitments/MODEL/MODEL.d.ts +4 -0
- package/esm/typings/src/commitments/NOTE/NOTE.d.ts +4 -0
- package/esm/typings/src/commitments/PERSONA/PERSONA.d.ts +4 -0
- package/esm/typings/src/commitments/RULE/RULE.d.ts +4 -0
- package/esm/typings/src/commitments/SAMPLE/SAMPLE.d.ts +4 -0
- package/esm/typings/src/commitments/SCENARIO/SCENARIO.d.ts +4 -0
- package/esm/typings/src/commitments/STYLE/STYLE.d.ts +4 -0
- package/esm/typings/src/commitments/_base/BaseCommitmentDefinition.d.ts +5 -0
- package/esm/typings/src/commitments/_base/CommitmentDefinition.d.ts +5 -0
- package/esm/typings/src/commitments/_base/NotYetImplementedCommitmentDefinition.d.ts +4 -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 +2 -2
- package/umd/index.umd.js +279 -2
- package/umd/index.umd.js.map +1 -1
- package/apps/agents-server/src/utils/cdn/classes/DigitalOceanSpaces.ts +0 -119
|
@@ -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,309 @@ 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: 'API Tokens',
|
|
215
|
+
href: '/admin/api-tokens',
|
|
216
|
+
},
|
|
217
|
+
{
|
|
218
|
+
type: 'link' as const,
|
|
219
|
+
label: 'Metadata',
|
|
220
|
+
href: '/admin/metadata',
|
|
221
|
+
},
|
|
222
|
+
{
|
|
223
|
+
type: 'link' as const,
|
|
224
|
+
label: 'Chat history',
|
|
225
|
+
href: '/admin/chat-history',
|
|
226
|
+
},
|
|
227
|
+
{
|
|
228
|
+
type: 'link' as const,
|
|
229
|
+
label: 'Chat feedback',
|
|
230
|
+
href: '/admin/chat-feedback',
|
|
231
|
+
},
|
|
232
|
+
{
|
|
233
|
+
type: 'link' as const,
|
|
234
|
+
label: 'About',
|
|
235
|
+
href: 'https://ptbk.io/',
|
|
236
|
+
},
|
|
237
|
+
]
|
|
238
|
+
: []),
|
|
239
|
+
];
|
|
240
|
+
|
|
41
241
|
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-
|
|
242
|
+
<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
243
|
<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
|
|
244
|
+
<div className="container mx-auto px-4 h-full">
|
|
245
|
+
<div className="flex items-center justify-between h-full">
|
|
246
|
+
{/* Logo */}
|
|
47
247
|
<Link href="/" className="flex items-center gap-3 hover:opacity-80 transition-opacity">
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
248
|
+
{serverLogoUrl ? (
|
|
249
|
+
// Note: `next/image` does not load external images well without extra config
|
|
250
|
+
// eslint-disable-next-line @next/next/no-img-element
|
|
251
|
+
<img
|
|
252
|
+
src={serverLogoUrl}
|
|
253
|
+
alt={serverName}
|
|
254
|
+
width={32}
|
|
255
|
+
height={32}
|
|
256
|
+
className="w-8 h-8 object-contain"
|
|
257
|
+
/>
|
|
258
|
+
) : (
|
|
259
|
+
<Image
|
|
260
|
+
src={promptbookLogoBlueTransparent}
|
|
261
|
+
alt={serverName}
|
|
262
|
+
width={32}
|
|
263
|
+
height={32}
|
|
264
|
+
className="w-8 h-8 object-contain"
|
|
265
|
+
/>
|
|
266
|
+
)}
|
|
267
|
+
<h1 className="text-xl font-bold tracking-tight text-gray-900">{serverName}</h1>
|
|
56
268
|
</Link>
|
|
57
269
|
|
|
58
270
|
{/* 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
|
-
|
|
271
|
+
<nav className="hidden lg:flex items-center gap-8">
|
|
272
|
+
{menuItems.map((item, index) => {
|
|
273
|
+
if (item.type === 'link') {
|
|
274
|
+
return (
|
|
275
|
+
<Link
|
|
276
|
+
key={index}
|
|
277
|
+
href={item.href}
|
|
278
|
+
className="text-sm font-medium text-gray-600 hover:text-gray-900 transition-colors cursor-pointer"
|
|
279
|
+
>
|
|
280
|
+
{item.label}
|
|
281
|
+
</Link>
|
|
282
|
+
);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
if (item.type === 'dropdown') {
|
|
286
|
+
return (
|
|
287
|
+
<div key={index} className="relative">
|
|
288
|
+
<button
|
|
289
|
+
className="flex items-center gap-1 text-sm font-medium text-gray-600 hover:text-gray-900 transition-colors cursor-pointer"
|
|
290
|
+
onClick={() => item.setIsOpen(!item.isOpen)}
|
|
291
|
+
onBlur={() => setTimeout(() => item.setIsOpen(false), 200)}
|
|
292
|
+
>
|
|
293
|
+
{item.label}
|
|
294
|
+
<ChevronDown className="w-4 h-4" />
|
|
295
|
+
</button>
|
|
296
|
+
|
|
297
|
+
{item.isOpen && (
|
|
298
|
+
<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">
|
|
299
|
+
{item.items.map((subItem, subIndex) => {
|
|
300
|
+
const className = `block px-4 py-2 text-sm text-gray-700 hover:bg-gray-50 hover:text-gray-900 ${
|
|
301
|
+
subItem.isBold ? 'font-medium' : ''
|
|
302
|
+
} ${subItem.isBordered ? 'border-b border-gray-100' : ''}`;
|
|
303
|
+
|
|
304
|
+
if (subItem.onClick) {
|
|
305
|
+
return (
|
|
306
|
+
<button
|
|
307
|
+
key={subIndex}
|
|
308
|
+
onClick={subItem.onClick}
|
|
309
|
+
className={`${className} w-full text-left`}
|
|
310
|
+
>
|
|
311
|
+
{subItem.label}
|
|
312
|
+
</button>
|
|
313
|
+
);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
return (
|
|
317
|
+
<Link
|
|
318
|
+
key={subIndex}
|
|
319
|
+
href={subItem.href!}
|
|
320
|
+
className={className}
|
|
321
|
+
onClick={() => item.setIsOpen(false)}
|
|
322
|
+
>
|
|
323
|
+
{subItem.label}
|
|
324
|
+
</Link>
|
|
325
|
+
);
|
|
326
|
+
})}
|
|
327
|
+
</div>
|
|
328
|
+
)}
|
|
329
|
+
</div>
|
|
330
|
+
);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
return null;
|
|
334
|
+
})}
|
|
89
335
|
|
|
90
336
|
{just(false /* TODO: [🧠] Figure out what to do with theese links */) && (
|
|
91
337
|
<Link
|
|
92
338
|
href="https://ptbk.io/"
|
|
93
339
|
target="_blank"
|
|
94
|
-
className="text-gray-600 hover:text-gray-900 transition-colors cursor-pointer"
|
|
340
|
+
className="text-sm font-medium text-gray-600 hover:text-gray-900 transition-colors cursor-pointer"
|
|
95
341
|
>
|
|
96
342
|
Create your server
|
|
97
343
|
</Link>
|
|
@@ -109,54 +355,179 @@ export function Header(props: HeaderProps) {
|
|
|
109
355
|
</Link>
|
|
110
356
|
)}
|
|
111
357
|
|
|
112
|
-
{!isAdmin
|
|
358
|
+
{!currentUser && !isAdmin && (
|
|
113
359
|
<button
|
|
114
360
|
onClick={() => {
|
|
115
361
|
setIsLoginOpen(true);
|
|
116
362
|
setIsMenuOpen(false);
|
|
117
363
|
}}
|
|
118
|
-
className="
|
|
364
|
+
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
365
|
>
|
|
120
366
|
Log in
|
|
121
367
|
<LogIn className="ml-2 w-4 h-4" />
|
|
122
368
|
</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
369
|
)}
|
|
135
370
|
|
|
136
|
-
{
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
371
|
+
{(currentUser || isAdmin) && (
|
|
372
|
+
<div className="hidden lg:flex items-center gap-3">
|
|
373
|
+
<span className="inline text-sm text-gray-600">
|
|
374
|
+
Logged in as <strong>{currentUser?.username || 'Admin'}</strong>
|
|
375
|
+
{(currentUser?.isAdmin || isAdmin) && (
|
|
376
|
+
<span className="ml-2 bg-blue-100 text-blue-800 text-xs px-2 py-1 rounded">
|
|
377
|
+
Admin
|
|
378
|
+
</span>
|
|
379
|
+
)}
|
|
380
|
+
</span>
|
|
381
|
+
<button
|
|
382
|
+
onClick={() => {
|
|
383
|
+
handleLogout();
|
|
384
|
+
setIsMenuOpen(false);
|
|
385
|
+
}}
|
|
386
|
+
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"
|
|
387
|
+
>
|
|
388
|
+
Log out
|
|
389
|
+
<LogOut className="ml-2 w-4 h-4" />
|
|
390
|
+
</button>
|
|
391
|
+
</div>
|
|
144
392
|
)}
|
|
393
|
+
|
|
394
|
+
{/* Mobile Menu Toggle */}
|
|
395
|
+
<HamburgerMenu
|
|
396
|
+
isOpen={isMenuOpen}
|
|
397
|
+
onClick={() => setIsMenuOpen(!isMenuOpen)}
|
|
398
|
+
className="lg:hidden p-2 text-gray-600 hover:text-gray-900"
|
|
399
|
+
/>
|
|
145
400
|
</div>
|
|
146
401
|
</div>
|
|
147
402
|
|
|
148
403
|
{/* Mobile Navigation */}
|
|
149
|
-
{
|
|
150
|
-
<div
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
404
|
+
{isMenuOpen && (
|
|
405
|
+
<div
|
|
406
|
+
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"
|
|
407
|
+
style={{
|
|
408
|
+
backdropFilter: 'blur(20px)',
|
|
409
|
+
WebkitBackdropFilter: 'blur(20px)',
|
|
410
|
+
}}
|
|
411
|
+
>
|
|
412
|
+
<nav className="container mx-auto flex flex-col gap-4 px-6">
|
|
413
|
+
{/* Login Status for Mobile */}
|
|
414
|
+
<div className="py-2 border-b border-gray-100 mb-2">
|
|
415
|
+
{!currentUser && !isAdmin && (
|
|
416
|
+
<button
|
|
417
|
+
onClick={() => {
|
|
418
|
+
setIsLoginOpen(true);
|
|
419
|
+
setIsMenuOpen(false);
|
|
420
|
+
}}
|
|
421
|
+
className="flex items-center gap-2 text-base font-medium text-gray-600 hover:text-gray-900 w-full"
|
|
422
|
+
>
|
|
423
|
+
<LogIn className="w-4 h-4" />
|
|
424
|
+
Log in
|
|
425
|
+
</button>
|
|
426
|
+
)}
|
|
427
|
+
|
|
428
|
+
{(currentUser || isAdmin) && (
|
|
429
|
+
<div className="flex flex-col gap-3">
|
|
430
|
+
<div className="text-sm text-gray-600">
|
|
431
|
+
Logged in as <strong>{currentUser?.username || 'Admin'}</strong>
|
|
432
|
+
{(currentUser?.isAdmin || isAdmin) && (
|
|
433
|
+
<span className="ml-2 bg-blue-100 text-blue-800 text-xs px-2 py-1 rounded">
|
|
434
|
+
Admin
|
|
435
|
+
</span>
|
|
436
|
+
)}
|
|
437
|
+
</div>
|
|
438
|
+
<button
|
|
439
|
+
onClick={() => {
|
|
440
|
+
handleLogout();
|
|
441
|
+
setIsMenuOpen(false);
|
|
442
|
+
}}
|
|
443
|
+
className="flex items-center gap-2 text-base font-medium text-red-600 hover:text-red-700 w-full"
|
|
444
|
+
>
|
|
445
|
+
<LogOut className="w-4 h-4" />
|
|
446
|
+
Log out
|
|
447
|
+
</button>
|
|
448
|
+
</div>
|
|
449
|
+
)}
|
|
450
|
+
</div>
|
|
451
|
+
|
|
452
|
+
{menuItems.map((item, index) => {
|
|
453
|
+
if (item.type === 'link') {
|
|
454
|
+
return (
|
|
455
|
+
<Link
|
|
456
|
+
key={index}
|
|
457
|
+
href={item.href}
|
|
458
|
+
className="block text-base font-medium text-gray-600 hover:text-gray-900 py-2"
|
|
459
|
+
onClick={() => setIsMenuOpen(false)}
|
|
460
|
+
>
|
|
461
|
+
{item.label}
|
|
462
|
+
</Link>
|
|
463
|
+
);
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
if (item.type === 'dropdown') {
|
|
467
|
+
return (
|
|
468
|
+
<div key={index} className="flex flex-col">
|
|
469
|
+
<button
|
|
470
|
+
className="w-full flex items-center justify-between text-base font-medium text-gray-600 hover:text-gray-900 py-2"
|
|
471
|
+
onClick={() => item.setIsMobileOpen(!item.isMobileOpen)}
|
|
472
|
+
>
|
|
473
|
+
{item.label}
|
|
474
|
+
<ChevronDown
|
|
475
|
+
className={`w-4 h-4 transition-transform duration-200 ${
|
|
476
|
+
item.isMobileOpen ? 'rotate-180' : ''
|
|
477
|
+
}`}
|
|
478
|
+
/>
|
|
479
|
+
</button>
|
|
480
|
+
{item.isMobileOpen && (
|
|
481
|
+
<div className="pl-4 flex flex-col gap-2 border-l-2 border-gray-100 ml-1 mt-1">
|
|
482
|
+
{item.items.map((subItem, subIndex) => {
|
|
483
|
+
const className = `block text-sm ${
|
|
484
|
+
subItem.isBold
|
|
485
|
+
? 'font-medium text-gray-900 hover:text-gray-700'
|
|
486
|
+
: 'text-gray-600 hover:text-gray-900'
|
|
487
|
+
} py-2`;
|
|
488
|
+
|
|
489
|
+
if (subItem.onClick) {
|
|
490
|
+
return (
|
|
491
|
+
<button
|
|
492
|
+
key={subIndex}
|
|
493
|
+
className={`${className} w-full text-left`}
|
|
494
|
+
onClick={subItem.onClick}
|
|
495
|
+
>
|
|
496
|
+
{subItem.label}
|
|
497
|
+
</button>
|
|
498
|
+
);
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
return (
|
|
502
|
+
<Link
|
|
503
|
+
key={subIndex}
|
|
504
|
+
href={subItem.href!}
|
|
505
|
+
className={className}
|
|
506
|
+
onClick={() => setIsMenuOpen(false)}
|
|
507
|
+
>
|
|
508
|
+
{subItem.label}
|
|
509
|
+
</Link>
|
|
510
|
+
);
|
|
511
|
+
})}
|
|
512
|
+
</div>
|
|
513
|
+
)}
|
|
514
|
+
</div>
|
|
515
|
+
);
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
return null;
|
|
519
|
+
})}
|
|
520
|
+
|
|
521
|
+
{just(false /* TODO: [🧠] Figure out what to do with these links */) && (
|
|
522
|
+
<Link
|
|
523
|
+
href="https://ptbk.io/"
|
|
524
|
+
target="_blank"
|
|
525
|
+
className="text-base font-medium text-gray-600 hover:text-gray-900 py-2"
|
|
526
|
+
onClick={() => setIsMenuOpen(false)}
|
|
527
|
+
>
|
|
528
|
+
Create your server
|
|
529
|
+
</Link>
|
|
530
|
+
)}
|
|
160
531
|
</nav>
|
|
161
532
|
</div>
|
|
162
533
|
)}
|