@promptbook/cli 0.104.0-1 → 0.104.0-10
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/config.ts +1 -3
- package/apps/agents-server/next.config.ts +2 -2
- package/apps/agents-server/package.json +7 -3
- package/apps/agents-server/public/fonts/OpenMoji-color-cbdt.woff2 +0 -0
- package/apps/agents-server/public/swagger.json +115 -0
- package/apps/agents-server/scripts/generate-reserved-paths/generate-reserved-paths.ts +54 -0
- package/apps/agents-server/scripts/generate-reserved-paths/tsconfig.json +19 -0
- package/apps/agents-server/src/app/AddAgentButton.tsx +47 -21
- package/apps/agents-server/src/app/actions.ts +22 -5
- package/apps/agents-server/src/app/admin/browser-test/BrowserTestClient.tsx +211 -0
- package/apps/agents-server/src/app/admin/browser-test/page.tsx +13 -0
- package/apps/agents-server/src/app/admin/chat-feedback/ChatFeedbackClient.tsx +221 -274
- package/apps/agents-server/src/app/admin/chat-history/ChatHistoryClient.tsx +94 -137
- package/apps/agents-server/src/app/admin/messages/MessagesClient.tsx +294 -0
- package/apps/agents-server/src/app/admin/messages/page.tsx +13 -0
- package/apps/agents-server/src/app/admin/messages/send-email/SendEmailClient.tsx +104 -0
- package/apps/agents-server/src/app/admin/messages/send-email/actions.ts +35 -0
- package/apps/agents-server/src/app/admin/messages/send-email/page.tsx +13 -0
- package/apps/agents-server/src/app/admin/metadata/MetadataClient.tsx +23 -19
- package/apps/agents-server/src/app/agents/[agentName]/AgentChatWrapper.tsx +15 -1
- package/apps/agents-server/src/app/agents/[agentName]/AgentOptionsMenu.tsx +51 -9
- package/apps/agents-server/src/app/agents/[agentName]/AgentProfileChat.tsx +47 -4
- package/apps/agents-server/src/app/agents/[agentName]/AgentProfileWrapper.tsx +53 -11
- package/apps/agents-server/src/app/agents/[agentName]/_utils.ts +23 -3
- package/apps/agents-server/src/app/agents/[agentName]/agentLinks.tsx +8 -8
- package/apps/agents-server/src/app/agents/[agentName]/api/agents/route.ts +17 -26
- package/apps/agents-server/src/app/agents/[agentName]/api/book/route.ts +4 -2
- package/apps/agents-server/src/app/agents/[agentName]/api/chat/route.ts +20 -0
- package/apps/agents-server/src/app/agents/[agentName]/api/mcp/route.ts +6 -11
- package/apps/agents-server/src/app/agents/[agentName]/api/profile/route.ts +5 -1
- package/apps/agents-server/src/app/agents/[agentName]/api/voice/route.ts +5 -2
- package/apps/agents-server/src/app/agents/[agentName]/book/BookEditorWrapper.tsx +20 -16
- package/apps/agents-server/src/app/agents/[agentName]/book/page.tsx +15 -2
- package/apps/agents-server/src/app/agents/[agentName]/book+chat/page.tsx +15 -2
- package/apps/agents-server/src/app/agents/[agentName]/chat/page.tsx +12 -0
- package/apps/agents-server/src/app/agents/[agentName]/code/api/route.ts +68 -0
- package/apps/agents-server/src/app/agents/[agentName]/code/page.tsx +223 -0
- package/apps/agents-server/src/app/agents/[agentName]/generateAgentMetadata.ts +5 -0
- package/apps/agents-server/src/app/agents/[agentName]/history/actions.ts +2 -2
- package/apps/agents-server/src/app/agents/[agentName]/history/page.tsx +10 -3
- package/apps/agents-server/src/app/agents/[agentName]/images/default-avatar.png/getAgentDefaultAvatarPrompt.ts +31 -0
- package/apps/agents-server/src/app/agents/[agentName]/images/default-avatar.png/route.ts +194 -0
- package/apps/agents-server/src/app/agents/[agentName]/images/icon-256.png/route.tsx +14 -2
- package/apps/agents-server/src/app/agents/[agentName]/images/page.tsx +200 -0
- package/apps/agents-server/src/app/agents/[agentName]/images/screenshot-fullhd.png/route.tsx +4 -3
- package/apps/agents-server/src/app/agents/[agentName]/images/screenshot-phone.png/route.tsx +4 -3
- package/apps/agents-server/src/app/agents/[agentName]/integration/page.tsx +10 -3
- package/apps/agents-server/src/app/agents/[agentName]/links/page.tsx +11 -4
- package/apps/agents-server/src/app/agents/[agentName]/opengraph-image.tsx +11 -2
- package/apps/agents-server/src/app/agents/[agentName]/page.tsx +18 -10
- package/apps/agents-server/src/app/agents/[agentName]/system-message/page.tsx +100 -0
- package/apps/agents-server/src/app/api/admin-email/route.ts +12 -0
- package/apps/agents-server/src/app/api/agents/[agentName]/clone/route.ts +13 -14
- package/apps/agents-server/src/app/api/agents/[agentName]/restore/route.ts +20 -0
- package/apps/agents-server/src/app/api/agents/[agentName]/route.ts +43 -1
- package/apps/agents-server/src/app/api/agents/route.ts +28 -3
- package/apps/agents-server/src/app/api/api-tokens/route.ts +6 -7
- package/apps/agents-server/src/app/api/browser-test/act/route.ts +141 -0
- package/apps/agents-server/src/app/api/browser-test/screenshot/route.ts +30 -0
- package/apps/agents-server/src/app/api/browser-test/scroll-facebook/route.ts +62 -0
- package/apps/agents-server/src/app/api/docs/book.md/route.ts +61 -0
- package/apps/agents-server/src/app/api/emails/incoming/sendgrid/route.ts +48 -0
- package/apps/agents-server/src/app/api/federated-agents/route.ts +12 -0
- package/apps/agents-server/src/app/api/images/[filename]/route.ts +107 -0
- package/apps/agents-server/src/app/api/messages/route.ts +102 -0
- package/apps/agents-server/src/app/api/metadata/route.ts +5 -6
- package/apps/agents-server/src/app/api/upload/route.ts +128 -45
- package/apps/agents-server/src/app/docs/[docId]/page.tsx +2 -3
- package/apps/agents-server/src/app/docs/page.tsx +12 -12
- package/apps/agents-server/src/app/globals.css +140 -33
- package/apps/agents-server/src/app/humans.txt/route.ts +1 -1
- package/apps/agents-server/src/app/layout.tsx +27 -22
- package/apps/agents-server/src/app/page.tsx +54 -6
- package/apps/agents-server/src/app/recycle-bin/actions.ts +20 -14
- package/apps/agents-server/src/app/recycle-bin/page.tsx +27 -41
- package/apps/agents-server/src/app/robots.txt/route.ts +1 -1
- package/apps/agents-server/src/app/security.txt/route.ts +1 -1
- package/apps/agents-server/src/app/sitemap.xml/route.ts +9 -7
- package/apps/agents-server/src/app/swagger/page.tsx +14 -0
- package/apps/agents-server/src/components/AgentProfile/AgentProfile.tsx +41 -116
- package/apps/agents-server/src/components/AgentProfile/AgentProfileImage.tsx +92 -0
- package/apps/agents-server/src/components/AgentProfile/QrCodeModal.tsx +0 -1
- package/apps/agents-server/src/components/AgentProfile/useAgentBackground.ts +97 -0
- package/apps/agents-server/src/components/Auth/AuthControls.tsx +5 -4
- package/apps/agents-server/src/components/DeletedAgentBanner.tsx +26 -0
- package/apps/agents-server/src/components/DocsToolbar/DocsToolbar.tsx +38 -0
- package/apps/agents-server/src/components/DocumentationContent/DocumentationContent.tsx +11 -9
- package/apps/agents-server/src/components/Footer/Footer.tsx +5 -5
- package/apps/agents-server/src/components/ForgottenPasswordDialog/ForgottenPasswordDialog.tsx +61 -0
- package/apps/agents-server/src/components/Header/Header.tsx +114 -40
- package/apps/agents-server/src/components/Homepage/AgentCard.tsx +145 -23
- package/apps/agents-server/src/components/Homepage/AgentsList.tsx +93 -15
- package/apps/agents-server/src/components/Homepage/DeletedAgentsList.tsx +66 -0
- package/apps/agents-server/src/components/Homepage/ExternalAgentsSection.tsx +12 -3
- package/apps/agents-server/src/components/Homepage/ExternalAgentsSectionClient.tsx +19 -10
- package/apps/agents-server/src/components/LayoutWrapper/LayoutWrapper.tsx +3 -2
- package/apps/agents-server/src/components/LoginForm/LoginForm.tsx +50 -1
- package/apps/agents-server/src/components/NewAgentDialog/NewAgentDialog.tsx +88 -0
- package/apps/agents-server/src/components/NotFoundPage/NotFoundPage.tsx +7 -2
- package/apps/agents-server/src/components/OpenMojiIcon/OpenMojiIcon.tsx +16 -7
- package/apps/agents-server/src/components/PrintHeader/PrintHeader.tsx +4 -4
- package/apps/agents-server/src/components/RegisterUserDialog/RegisterUserDialog.tsx +61 -0
- package/apps/agents-server/src/components/VercelDeploymentCard/VercelDeploymentCard.tsx +2 -0
- package/apps/agents-server/src/components/_utils/generateMetaTxt.ts +12 -10
- package/apps/agents-server/src/components/_utils/headlessParam.tsx +7 -3
- package/apps/agents-server/src/database/$provideSupabaseForBrowser.ts +3 -3
- package/apps/agents-server/src/database/$provideSupabaseForServer.ts +1 -1
- package/apps/agents-server/src/database/$provideSupabaseForWorker.ts +3 -3
- package/apps/agents-server/src/database/metadataDefaults.ts +19 -1
- package/apps/agents-server/src/database/migrate.ts +34 -1
- package/apps/agents-server/src/database/migrations/2025-11-0001-initial-schema.sql +1 -3
- package/apps/agents-server/src/database/migrations/2025-11-0002-metadata-table.sql +1 -3
- package/apps/agents-server/src/database/migrations/2025-12-0240-agent-public-id.sql +3 -0
- package/apps/agents-server/src/database/migrations/2025-12-0360-agent-deleted-at.sql +1 -0
- package/apps/agents-server/src/database/migrations/2025-12-0370-image-table.sql +19 -0
- package/apps/agents-server/src/database/migrations/2025-12-0380-agent-visibility.sql +1 -0
- package/apps/agents-server/src/database/migrations/2025-12-0390-upload-tracking.sql +20 -0
- package/apps/agents-server/src/database/migrations/2025-12-0401-file-upload-status.sql +13 -0
- package/apps/agents-server/src/database/migrations/2025-12-0402-message-table.sql +42 -0
- package/apps/agents-server/src/database/migrations/2025-12-0403-generation-lock-table.sql +15 -0
- package/apps/agents-server/src/database/migrations/2025-12-0640-openai-assistant-cache.sql +12 -0
- package/apps/agents-server/src/database/migrations/2025-12-0820-agent-history-permanent-id.sql +29 -0
- package/apps/agents-server/src/database/schema.ts +231 -4
- package/apps/agents-server/src/generated/reservedPaths.ts +32 -0
- package/apps/agents-server/src/message-providers/email/_common/Email.ts +73 -0
- package/apps/agents-server/src/message-providers/email/_common/utils/TODO.txt +1 -0
- package/apps/agents-server/src/message-providers/email/_common/utils/parseEmailAddress.test.ts.todo +108 -0
- package/apps/agents-server/src/message-providers/email/_common/utils/parseEmailAddress.ts +62 -0
- package/apps/agents-server/src/message-providers/email/_common/utils/parseEmailAddresses.test.ts.todo +117 -0
- package/apps/agents-server/src/message-providers/email/_common/utils/parseEmailAddresses.ts +19 -0
- package/apps/agents-server/src/message-providers/email/_common/utils/stringifyEmailAddress.test.ts.todo +119 -0
- package/apps/agents-server/src/message-providers/email/_common/utils/stringifyEmailAddress.ts +19 -0
- package/apps/agents-server/src/message-providers/email/_common/utils/stringifyEmailAddresses.test.ts.todo +74 -0
- package/apps/agents-server/src/message-providers/email/_common/utils/stringifyEmailAddresses.ts +14 -0
- package/apps/agents-server/src/message-providers/email/sendgrid/SendgridMessageProvider.ts +44 -0
- package/apps/agents-server/src/message-providers/email/sendgrid/parseInboundSendgridEmail.ts +49 -0
- package/apps/agents-server/src/message-providers/email/zeptomail/ZeptomailMessageProvider.ts +51 -0
- package/apps/agents-server/src/message-providers/index.ts +13 -0
- package/apps/agents-server/src/message-providers/interfaces/MessageProvider.ts +11 -0
- package/apps/agents-server/src/middleware.ts +19 -23
- package/apps/agents-server/src/tools/$provideBrowserForServer.ts +32 -0
- package/apps/agents-server/src/tools/$provideCdnForServer.ts +7 -2
- package/apps/agents-server/src/utils/auth.ts +117 -17
- package/apps/agents-server/src/utils/cdn/classes/TrackedFilesStorage.ts +57 -0
- package/apps/agents-server/src/utils/cdn/classes/VercelBlobStorage.ts +4 -0
- package/apps/agents-server/src/utils/cdn/interfaces/IFilesStorage.ts +18 -0
- package/apps/agents-server/src/utils/content/extractBodyContentFromHtml.ts +19 -0
- package/apps/agents-server/src/utils/getUserIdFromRequest.ts +35 -0
- package/apps/agents-server/src/utils/handleChatCompletion.ts +65 -5
- package/apps/agents-server/src/utils/messages/sendMessage.ts +91 -0
- package/apps/agents-server/src/utils/messagesAdmin.ts +72 -0
- package/apps/agents-server/src/utils/normalization/filenameToPrompt.test.ts +36 -0
- package/apps/agents-server/src/utils/normalization/filenameToPrompt.ts +25 -0
- package/apps/agents-server/src/utils/validateApiKey.ts +7 -11
- package/esm/index.es.js +2890 -2737
- package/esm/index.es.js.map +1 -1
- package/esm/typings/servers.d.ts +8 -0
- package/esm/typings/src/_packages/core.index.d.ts +2 -0
- package/esm/typings/src/_packages/types.index.d.ts +10 -2
- package/esm/typings/src/book-2.0/agent-source/AgentBasicInformation.d.ts +6 -1
- package/esm/typings/src/book-2.0/agent-source/createAgentModelRequirements.d.ts +6 -6
- package/esm/typings/src/book-2.0/agent-source/createAgentModelRequirementsWithCommitments.closed.test.d.ts +1 -0
- package/esm/typings/src/book-2.0/utils/generatePlaceholderAgentProfileImageUrl.d.ts +3 -3
- package/esm/typings/src/book-components/Chat/Chat/ChatMessageItem.d.ts +5 -1
- package/esm/typings/src/book-components/Chat/Chat/ChatProps.d.ts +5 -0
- package/esm/typings/src/book-components/Chat/CodeBlock/CodeBlock.d.ts +13 -0
- package/esm/typings/src/book-components/Chat/MarkdownContent/MarkdownContent.d.ts +1 -0
- package/esm/typings/src/book-components/Chat/types/ChatMessage.d.ts +7 -11
- package/esm/typings/src/book-components/_common/Dropdown/Dropdown.d.ts +2 -2
- package/esm/typings/src/book-components/_common/MenuHoisting/MenuHoistingContext.d.ts +56 -0
- package/esm/typings/src/collection/agent-collection/constructors/agent-collection-in-supabase/AgentCollectionInSupabase.d.ts +21 -11
- package/esm/typings/src/collection/agent-collection/constructors/agent-collection-in-supabase/AgentsDatabaseSchema.d.ts +80 -14
- package/esm/typings/src/commitments/DICTIONARY/DICTIONARY.d.ts +46 -0
- package/esm/typings/src/commitments/index.d.ts +2 -1
- package/esm/typings/src/llm-providers/_multiple/MultipleLlmExecutionTools.d.ts +6 -2
- package/esm/typings/src/llm-providers/agent/AgentLlmExecutionTools.d.ts +1 -1
- package/esm/typings/src/llm-providers/ollama/OllamaExecutionTools.d.ts +1 -1
- package/esm/typings/src/llm-providers/openai/createOpenAiCompatibleExecutionTools.d.ts +1 -1
- package/esm/typings/src/llm-providers/remote/RemoteLlmExecutionTools.d.ts +1 -0
- package/esm/typings/src/types/Message.d.ts +49 -0
- package/esm/typings/src/types/ModelRequirements.d.ts +38 -14
- package/esm/typings/src/types/typeAliases.d.ts +23 -1
- package/esm/typings/src/utils/color/utils/colorToDataUrl.d.ts +2 -1
- package/esm/typings/src/utils/environment/$detectRuntimeEnvironment.d.ts +4 -4
- package/esm/typings/src/utils/environment/$isRunningInBrowser.d.ts +1 -1
- package/esm/typings/src/utils/environment/$isRunningInJest.d.ts +1 -1
- package/esm/typings/src/utils/environment/$isRunningInNode.d.ts +1 -1
- package/esm/typings/src/utils/environment/$isRunningInWebWorker.d.ts +1 -1
- package/esm/typings/src/utils/markdown/extractAllBlocksFromMarkdown.d.ts +2 -2
- package/esm/typings/src/utils/markdown/extractOneBlockFromMarkdown.d.ts +2 -2
- package/esm/typings/src/utils/random/$randomBase58.d.ts +12 -0
- package/esm/typings/src/version.d.ts +1 -1
- package/package.json +1 -1
- package/umd/index.umd.js +4018 -3865
- package/umd/index.umd.js.map +1 -1
- package/apps/agents-server/package-lock.json +0 -27
- package/apps/agents-server/public/fonts/download-font.js +0 -22
- package/apps/agents-server/src/components/PrintButton/PrintButton.tsx +0 -18
- package/esm/typings/src/book-2.0/utils/generateGravatarUrl.d.ts +0 -10
|
@@ -4,14 +4,15 @@ import promptbookLogoBlueTransparent from '@/public/logo-blue-white-256.png';
|
|
|
4
4
|
import { $createAgentAction, logoutAction } from '@/src/app/actions';
|
|
5
5
|
import { ArrowRight, ChevronDown, Lock, LogIn, LogOut, User } from 'lucide-react';
|
|
6
6
|
import Image from 'next/image';
|
|
7
|
-
import { HeadlessLink, useIsHeadless, pushWithHeadless } from '../_utils/headlessParam';
|
|
8
7
|
import { useRouter } from 'next/navigation';
|
|
9
8
|
import { ReactNode, useState } from 'react';
|
|
10
9
|
import { AgentBasicInformation } from '../../../../../src/book-2.0/agent-source/AgentBasicInformation';
|
|
11
10
|
import { HamburgerMenu } from '../../../../../src/book-components/_common/HamburgerMenu/HamburgerMenu';
|
|
11
|
+
import { useMenuHoisting } from '../../../../../src/book-components/_common/MenuHoisting/MenuHoistingContext';
|
|
12
12
|
import { just } from '../../../../../src/utils/organization/just';
|
|
13
13
|
import type { UserInfo } from '../../utils/getCurrentUser';
|
|
14
14
|
import { getVisibleCommitmentDefinitions } from '../../utils/getVisibleCommitmentDefinitions';
|
|
15
|
+
import { HeadlessLink, pushWithHeadless, useIsHeadless } from '../_utils/headlessParam';
|
|
15
16
|
import { ChangePasswordDialog } from '../ChangePasswordDialog/ChangePasswordDialog';
|
|
16
17
|
import { LoginDialog } from '../LoginDialog/LoginDialog';
|
|
17
18
|
import { useUsersAdmin } from '../UsersList/useUsersAdmin';
|
|
@@ -92,6 +93,7 @@ export function Header(props: HeaderProps) {
|
|
|
92
93
|
const [isCreatingAgent, setIsCreatingAgent] = useState(false);
|
|
93
94
|
const router = useRouter();
|
|
94
95
|
const isHeadless = useIsHeadless();
|
|
96
|
+
const menuHoisting = useMenuHoisting();
|
|
95
97
|
|
|
96
98
|
const { users: adminUsers } = useUsersAdmin();
|
|
97
99
|
|
|
@@ -117,16 +119,22 @@ export function Header(props: HeaderProps) {
|
|
|
117
119
|
|
|
118
120
|
// Federated servers dropdown items (respect logo, only current is not clickable)
|
|
119
121
|
const [isFederatedOpen, setIsFederatedOpen] = useState(false);
|
|
120
|
-
const [isMobileFederatedOpen, setIsMobileFederatedOpen] = useState(false);
|
|
122
|
+
// const [isMobileFederatedOpen, setIsMobileFederatedOpen] = useState(false);
|
|
121
123
|
|
|
122
|
-
const federatedDropdownItems: SubMenuItem[] = federatedServers.map(server => {
|
|
124
|
+
const federatedDropdownItems: SubMenuItem[] = federatedServers.map((server) => {
|
|
123
125
|
const isCurrent = server.url === (typeof window !== 'undefined' ? window.location.origin : '');
|
|
124
126
|
return isCurrent
|
|
125
127
|
? {
|
|
126
128
|
label: (
|
|
127
129
|
<span className="flex items-center gap-2">
|
|
128
130
|
{/* eslint-disable-next-line @next/next/no-img-element */}
|
|
129
|
-
<img
|
|
131
|
+
<img
|
|
132
|
+
src={server.logoUrl || serverLogoUrl || promptbookLogoBlueTransparent.src}
|
|
133
|
+
alt={server.title}
|
|
134
|
+
width={20}
|
|
135
|
+
height={20}
|
|
136
|
+
className="w-5 h-5 object-contain rounded-full"
|
|
137
|
+
/>
|
|
130
138
|
<span className="font-semibold">{server.title.replace(/^Federated: /, '')}</span>
|
|
131
139
|
<span className="ml-1 text-xs text-blue-600">(current)</span>
|
|
132
140
|
</span>
|
|
@@ -138,7 +146,13 @@ export function Header(props: HeaderProps) {
|
|
|
138
146
|
label: (
|
|
139
147
|
<span className="flex items-center gap-2">
|
|
140
148
|
{/* eslint-disable-next-line @next/next/no-img-element */}
|
|
141
|
-
<img
|
|
149
|
+
<img
|
|
150
|
+
src={server.logoUrl || promptbookLogoBlueTransparent.src}
|
|
151
|
+
alt={server.title}
|
|
152
|
+
width={20}
|
|
153
|
+
height={20}
|
|
154
|
+
className="w-5 h-5 object-contain rounded-full"
|
|
155
|
+
/>
|
|
142
156
|
<span>{server.title.replace(/^Federated: /, '')}</span>
|
|
143
157
|
</span>
|
|
144
158
|
),
|
|
@@ -167,6 +181,12 @@ export function Header(props: HeaderProps) {
|
|
|
167
181
|
isBold: true,
|
|
168
182
|
isBordered: true,
|
|
169
183
|
} as SubMenuItem,
|
|
184
|
+
{
|
|
185
|
+
label: 'API Reference',
|
|
186
|
+
href: '/swagger',
|
|
187
|
+
isBold: true,
|
|
188
|
+
isBordered: true,
|
|
189
|
+
} as SubMenuItem,
|
|
170
190
|
...getVisibleCommitmentDefinitions().map(
|
|
171
191
|
({ primary, aliases }) =>
|
|
172
192
|
({
|
|
@@ -261,6 +281,10 @@ export function Header(props: HeaderProps) {
|
|
|
261
281
|
isMobileOpen: isMobileSystemOpen,
|
|
262
282
|
setIsMobileOpen: setIsMobileSystemOpen,
|
|
263
283
|
items: [
|
|
284
|
+
{
|
|
285
|
+
label: 'OpenAPI Documentation',
|
|
286
|
+
href: '/swagger',
|
|
287
|
+
},
|
|
264
288
|
{
|
|
265
289
|
label: 'API Tokens',
|
|
266
290
|
href: '/admin/api-tokens',
|
|
@@ -273,10 +297,18 @@ export function Header(props: HeaderProps) {
|
|
|
273
297
|
label: 'Chat history',
|
|
274
298
|
href: '/admin/chat-history',
|
|
275
299
|
},
|
|
300
|
+
{
|
|
301
|
+
label: 'Messages & Emails',
|
|
302
|
+
href: '/admin/messages',
|
|
303
|
+
},
|
|
276
304
|
{
|
|
277
305
|
label: 'Chat feedback',
|
|
278
306
|
href: '/admin/chat-feedback',
|
|
279
307
|
},
|
|
308
|
+
{
|
|
309
|
+
label: 'Browser',
|
|
310
|
+
href: '/admin/browser-test',
|
|
311
|
+
},
|
|
280
312
|
],
|
|
281
313
|
},
|
|
282
314
|
{
|
|
@@ -320,44 +352,46 @@ export function Header(props: HeaderProps) {
|
|
|
320
352
|
|
|
321
353
|
{/* Desktop Navigation */}
|
|
322
354
|
<nav className="hidden lg:flex items-center gap-8">
|
|
323
|
-
{/* Federated servers dropdown */}
|
|
324
|
-
|
|
325
|
-
<
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
355
|
+
{/* Federated servers dropdown - only show if there are federated servers */}
|
|
356
|
+
{federatedServers.length > 0 && (
|
|
357
|
+
<div className="relative">
|
|
358
|
+
<button
|
|
359
|
+
className="flex items-center gap-1 text-sm font-medium text-gray-600 hover:text-gray-900 transition-colors cursor-pointer"
|
|
360
|
+
onClick={() => setIsFederatedOpen(!isFederatedOpen)}
|
|
361
|
+
onBlur={() => setTimeout(() => setIsFederatedOpen(false), 200)}
|
|
362
|
+
>
|
|
363
|
+
<ChevronDown className="w-4 h-4" />
|
|
364
|
+
<span>Switch server</span>
|
|
365
|
+
</button>
|
|
366
|
+
{isFederatedOpen && (
|
|
367
|
+
<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">
|
|
368
|
+
{federatedDropdownItems.map((subItem, subIndex) => {
|
|
369
|
+
const className = `block px-4 py-2 text-sm text-gray-700 hover:bg-gray-50 hover:text-gray-900 ${
|
|
370
|
+
subItem.isBold ? 'font-medium' : ''
|
|
371
|
+
} ${subItem.isBordered ? 'border-b border-gray-100' : ''}`;
|
|
372
|
+
|
|
373
|
+
if (subItem.href) {
|
|
374
|
+
return (
|
|
375
|
+
<HeadlessLink
|
|
376
|
+
key={subIndex}
|
|
377
|
+
href={subItem.href}
|
|
378
|
+
className={className}
|
|
379
|
+
onClick={() => setIsFederatedOpen(false)}
|
|
380
|
+
>
|
|
381
|
+
{subItem.label}
|
|
382
|
+
</HeadlessLink>
|
|
383
|
+
);
|
|
384
|
+
}
|
|
341
385
|
return (
|
|
342
|
-
<
|
|
343
|
-
key={subIndex}
|
|
344
|
-
href={subItem.href}
|
|
345
|
-
className={className}
|
|
346
|
-
onClick={() => setIsFederatedOpen(false)}
|
|
347
|
-
>
|
|
386
|
+
<span key={subIndex} className={className}>
|
|
348
387
|
{subItem.label}
|
|
349
|
-
</
|
|
388
|
+
</span>
|
|
350
389
|
);
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
);
|
|
357
|
-
})}
|
|
358
|
-
</div>
|
|
359
|
-
)}
|
|
360
|
-
</div>
|
|
390
|
+
})}
|
|
391
|
+
</div>
|
|
392
|
+
)}
|
|
393
|
+
</div>
|
|
394
|
+
)}
|
|
361
395
|
{menuItems.map((item, index) => {
|
|
362
396
|
if (item.type === 'link') {
|
|
363
397
|
return (
|
|
@@ -433,6 +467,24 @@ export function Header(props: HeaderProps) {
|
|
|
433
467
|
)}
|
|
434
468
|
</nav>
|
|
435
469
|
|
|
470
|
+
{/* Hoisted Menu Items */}
|
|
471
|
+
{menuHoisting && menuHoisting.menu.length > 0 && (
|
|
472
|
+
<div className="hidden lg:flex items-center gap-2 border-r border-gray-200 pr-4 mr-2">
|
|
473
|
+
{menuHoisting.menu.map((item, index) => (
|
|
474
|
+
<button
|
|
475
|
+
key={index}
|
|
476
|
+
onClick={item.onClick}
|
|
477
|
+
className={`p-2 rounded-md hover:bg-gray-100 transition-colors text-gray-600 hover:text-gray-900 ${
|
|
478
|
+
item.isActive ? 'bg-gray-100 text-gray-900' : ''
|
|
479
|
+
}`}
|
|
480
|
+
title={item.name}
|
|
481
|
+
>
|
|
482
|
+
{item.icon}
|
|
483
|
+
</button>
|
|
484
|
+
))}
|
|
485
|
+
</div>
|
|
486
|
+
)}
|
|
487
|
+
|
|
436
488
|
{/* CTA Button & Mobile Menu Toggle */}
|
|
437
489
|
<div className="flex items-center gap-4">
|
|
438
490
|
{just(false /* TODO: [🧠] Figure out what to do with call to action */) && (
|
|
@@ -531,6 +583,28 @@ export function Header(props: HeaderProps) {
|
|
|
531
583
|
}}
|
|
532
584
|
>
|
|
533
585
|
<nav className="container mx-auto flex flex-col gap-4 px-6">
|
|
586
|
+
{/* Hoisted Menu Items for Mobile */}
|
|
587
|
+
{menuHoisting && menuHoisting.menu.length > 0 && (
|
|
588
|
+
<div className="py-2 border-b border-gray-100 mb-2 flex gap-2 overflow-x-auto">
|
|
589
|
+
{menuHoisting.menu.map((item, index) => (
|
|
590
|
+
<button
|
|
591
|
+
key={index}
|
|
592
|
+
onClick={() => {
|
|
593
|
+
item.onClick();
|
|
594
|
+
setIsMenuOpen(false);
|
|
595
|
+
}}
|
|
596
|
+
className={`p-2 rounded-md hover:bg-gray-100 transition-colors text-gray-600 hover:text-gray-900 ${
|
|
597
|
+
item.isActive ? 'bg-gray-100 text-gray-900' : ''
|
|
598
|
+
}`}
|
|
599
|
+
title={item.name}
|
|
600
|
+
>
|
|
601
|
+
{item.icon}
|
|
602
|
+
<span className="sr-only">{item.name}</span>
|
|
603
|
+
</button>
|
|
604
|
+
))}
|
|
605
|
+
</div>
|
|
606
|
+
)}
|
|
607
|
+
|
|
534
608
|
{/* Login Status for Mobile */}
|
|
535
609
|
<div className="py-2 border-b border-gray-100 mb-2">
|
|
536
610
|
{!currentUser && !isAdmin && (
|
|
@@ -1,43 +1,165 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { generatePlaceholderAgentProfileImageUrl } from '@promptbook-local/core';
|
|
4
|
+
import { really_any } from '@promptbook-local/types';
|
|
5
|
+
import { EyeIcon, EyeOffIcon, RotateCcwIcon } from 'lucide-react';
|
|
1
6
|
import Link from 'next/link';
|
|
2
|
-
import React from 'react';
|
|
3
7
|
import { AgentBasicInformation } from '../../../../../src/book-2.0/agent-source/AgentBasicInformation';
|
|
4
|
-
import {
|
|
5
|
-
import { Card } from './Card';
|
|
8
|
+
import { useAgentBackground } from '../AgentProfile/useAgentBackground';
|
|
6
9
|
|
|
7
10
|
type AgentCardProps = {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
/**
|
|
12
|
+
* @@@
|
|
13
|
+
*/
|
|
14
|
+
readonly agent: AgentBasicInformation;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* @@@
|
|
18
|
+
*/
|
|
19
|
+
readonly href: string;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Base URL of the agents server
|
|
23
|
+
*/
|
|
24
|
+
readonly publicUrl: URL;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* @@@
|
|
28
|
+
*/
|
|
29
|
+
readonly isAdmin?: boolean;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* @@@
|
|
33
|
+
*/
|
|
34
|
+
readonly onDelete?: (agentIdentifier: string) => void;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* @@@
|
|
38
|
+
*/
|
|
39
|
+
readonly onClone?: (agentIdentifier: string) => void;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* @@@
|
|
43
|
+
*/
|
|
44
|
+
readonly onToggleVisibility?: (agentIdentifier: string) => void;
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* @@@
|
|
48
|
+
*/
|
|
49
|
+
readonly onRestore?: (agentIdentifier: string) => void;
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* @@@
|
|
53
|
+
*/
|
|
54
|
+
readonly visibility?: 'PUBLIC' | 'PRIVATE';
|
|
13
55
|
};
|
|
14
56
|
|
|
15
57
|
const ACTION_BUTTON_CLASSES =
|
|
16
58
|
'text-white px-3 py-1 rounded shadow text-xs font-medium transition-colors uppercase tracking-wider opacity-80 hover:opacity-100';
|
|
17
59
|
|
|
18
|
-
export function AgentCard({
|
|
60
|
+
export function AgentCard({
|
|
61
|
+
agent,
|
|
62
|
+
href,
|
|
63
|
+
isAdmin,
|
|
64
|
+
publicUrl,
|
|
65
|
+
onDelete,
|
|
66
|
+
onClone,
|
|
67
|
+
onToggleVisibility,
|
|
68
|
+
onRestore,
|
|
69
|
+
visibility,
|
|
70
|
+
}: AgentCardProps) {
|
|
71
|
+
const { meta, agentName } = agent;
|
|
72
|
+
const fullname = (meta.fullname as string) || agentName || 'Agent';
|
|
73
|
+
const imageUrl = meta.image || generatePlaceholderAgentProfileImageUrl(agentName, publicUrl);
|
|
74
|
+
const personaDescription = agent.personaDescription || '';
|
|
75
|
+
|
|
76
|
+
const { brandColorLightHex, brandColorDarkHex, backgroundImage } = useAgentBackground(meta.color);
|
|
77
|
+
|
|
19
78
|
return (
|
|
20
79
|
<div className="relative h-full group">
|
|
21
|
-
<Link href={href} className="block h-full">
|
|
22
|
-
<
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
}
|
|
80
|
+
<Link href={href} className="block h-full transition-transform hover:scale-[1.02] duration-300">
|
|
81
|
+
<div
|
|
82
|
+
className="h-full rounded-2xl overflow-hidden shadow-lg hover:shadow-2xl transition-all duration-300 flex flex-col border border-white/20"
|
|
83
|
+
style={{
|
|
84
|
+
background: `url("${backgroundImage}")`,
|
|
85
|
+
backgroundSize: 'cover',
|
|
86
|
+
backgroundPosition: 'center',
|
|
87
|
+
}}
|
|
30
88
|
>
|
|
31
|
-
<
|
|
32
|
-
|
|
89
|
+
<div className="p-6 flex flex-col items-center flex-grow backdrop-blur-[2px]">
|
|
90
|
+
{/* Image container */}
|
|
91
|
+
<div
|
|
92
|
+
className="w-32 h-32 mb-4 shadow-lg overflow-hidden flex-shrink-0 bg-black/20"
|
|
93
|
+
style={{
|
|
94
|
+
boxShadow: `0 10px 20px -5px rgba(0, 0, 0, 0.2), 0 0 0 1px ${brandColorLightHex}40`,
|
|
95
|
+
|
|
96
|
+
// Note: Make it squircle
|
|
97
|
+
borderRadius: '50%',
|
|
98
|
+
['cornerShape' as really_any /* <- Note: `cornerShape` is non standard CSS property */]:
|
|
99
|
+
'squircle ',
|
|
100
|
+
}}
|
|
101
|
+
>
|
|
102
|
+
{imageUrl ? (
|
|
103
|
+
// eslint-disable-next-line @next/next/no-img-element
|
|
104
|
+
<img src={imageUrl} alt={fullname} className="w-full h-full object-cover" />
|
|
105
|
+
) : (
|
|
106
|
+
<div
|
|
107
|
+
className="w-full h-full flex items-center justify-center text-4xl font-bold text-white/80"
|
|
108
|
+
style={{ backgroundColor: brandColorDarkHex }}
|
|
109
|
+
>
|
|
110
|
+
{fullname.charAt(0).toUpperCase()}
|
|
111
|
+
</div>
|
|
112
|
+
)}
|
|
113
|
+
</div>
|
|
114
|
+
|
|
115
|
+
<h3
|
|
116
|
+
className="text-lg font-bold text-gray-900 text-center leading-tight mb-2"
|
|
117
|
+
style={{ textShadow: '0 1px 2px rgba(255,255,255,0.8)' }}
|
|
118
|
+
>
|
|
119
|
+
{fullname}
|
|
120
|
+
</h3>
|
|
121
|
+
|
|
122
|
+
<p className="text-sm text-gray-800 text-center line-clamp-3 leading-relaxed font-medium mix-blend-hard-light">
|
|
123
|
+
{personaDescription}
|
|
124
|
+
</p>
|
|
125
|
+
</div>
|
|
126
|
+
</div>
|
|
33
127
|
</Link>
|
|
34
|
-
{isAdmin && (
|
|
128
|
+
{isAdmin && onRestore && (
|
|
35
129
|
<div className="absolute top-2 right-2 flex gap-2 opacity-0 group-hover:opacity-100 transition-opacity z-10">
|
|
130
|
+
<button
|
|
131
|
+
className={`bg-green-500 hover:bg-green-600 ${ACTION_BUTTON_CLASSES}`}
|
|
132
|
+
onClick={(e) => {
|
|
133
|
+
e.preventDefault();
|
|
134
|
+
onRestore(agent.permanentId || agent.agentName);
|
|
135
|
+
}}
|
|
136
|
+
title="Restore agent"
|
|
137
|
+
>
|
|
138
|
+
<RotateCcwIcon className="w-3 h-3" />
|
|
139
|
+
</button>
|
|
140
|
+
</div>
|
|
141
|
+
)}
|
|
142
|
+
{isAdmin && !onRestore && (
|
|
143
|
+
<div className="absolute top-2 right-2 flex gap-2 opacity-0 group-hover:opacity-100 transition-opacity z-10">
|
|
144
|
+
<button
|
|
145
|
+
className={`${
|
|
146
|
+
visibility === 'PUBLIC'
|
|
147
|
+
? 'bg-green-500 hover:bg-green-600'
|
|
148
|
+
: 'bg-gray-500 hover:bg-gray-600'
|
|
149
|
+
} ${ACTION_BUTTON_CLASSES}`}
|
|
150
|
+
onClick={(e) => {
|
|
151
|
+
e.preventDefault();
|
|
152
|
+
onToggleVisibility?.(agent.permanentId || agent.agentName);
|
|
153
|
+
}}
|
|
154
|
+
title={`Make ${visibility === 'PUBLIC' ? 'private' : 'public'}`}
|
|
155
|
+
>
|
|
156
|
+
{visibility === 'PUBLIC' ? <EyeIcon className="w-3 h-3" /> : <EyeOffIcon className="w-3 h-3" />}
|
|
157
|
+
</button>
|
|
36
158
|
<button
|
|
37
159
|
className={`bg-blue-500 hover:bg-blue-600 ${ACTION_BUTTON_CLASSES}`}
|
|
38
160
|
onClick={(e) => {
|
|
39
161
|
e.preventDefault();
|
|
40
|
-
onClone?.(agent.agentName);
|
|
162
|
+
onClone?.(agent.permanentId || agent.agentName);
|
|
41
163
|
}}
|
|
42
164
|
title="Clone agent"
|
|
43
165
|
>
|
|
@@ -47,7 +169,7 @@ export function AgentCard({ agent, href, isAdmin, onDelete, onClone }: AgentCard
|
|
|
47
169
|
className={`bg-red-500 hover:bg-red-600 ${ACTION_BUTTON_CLASSES}`}
|
|
48
170
|
onClick={(e) => {
|
|
49
171
|
e.preventDefault();
|
|
50
|
-
onDelete?.(agent.agentName);
|
|
172
|
+
onDelete?.(agent.permanentId || agent.agentName);
|
|
51
173
|
}}
|
|
52
174
|
title="Delete agent"
|
|
53
175
|
>
|
|
@@ -1,46 +1,124 @@
|
|
|
1
1
|
// Client Component for rendering and deleting agents
|
|
2
2
|
'use client';
|
|
3
3
|
|
|
4
|
-
import React, { useState } from 'react';
|
|
5
4
|
import { TrashIcon } from 'lucide-react';
|
|
6
5
|
import Link from 'next/link';
|
|
6
|
+
import { useRouter } from 'next/navigation';
|
|
7
|
+
import { useState } from 'react';
|
|
7
8
|
import { AddAgentButton } from '../../app/AddAgentButton';
|
|
8
9
|
import { AgentCard } from './AgentCard';
|
|
9
10
|
import { Section } from './Section';
|
|
10
11
|
|
|
11
12
|
import { AgentBasicInformation } from '../../../../../src/book-2.0/agent-source/AgentBasicInformation';
|
|
12
13
|
|
|
14
|
+
type AgentWithVisibility = AgentBasicInformation & {
|
|
15
|
+
visibility?: 'PUBLIC' | 'PRIVATE';
|
|
16
|
+
};
|
|
17
|
+
|
|
13
18
|
type AgentsListProps = {
|
|
14
|
-
|
|
15
|
-
|
|
19
|
+
/**
|
|
20
|
+
* @@@
|
|
21
|
+
*/
|
|
22
|
+
readonly agents: AgentWithVisibility[];
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* @@@
|
|
26
|
+
*/
|
|
27
|
+
readonly isAdmin: boolean;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Base URL of the agents server
|
|
31
|
+
*/
|
|
32
|
+
readonly publicUrl: URL;
|
|
16
33
|
};
|
|
17
34
|
|
|
18
|
-
export function AgentsList(
|
|
35
|
+
export function AgentsList(props: AgentsListProps) {
|
|
36
|
+
const { agents: initialAgents, isAdmin, publicUrl } = props;
|
|
37
|
+
const router = useRouter();
|
|
19
38
|
const [agents, setAgents] = useState(Array.from(initialAgents));
|
|
20
39
|
|
|
21
|
-
const handleDelete = async (
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
40
|
+
const handleDelete = async (agentIdentifier: string) => {
|
|
41
|
+
const agent = agents.find((a) => a.permanentId === agentIdentifier || a.agentName === agentIdentifier);
|
|
42
|
+
if (!agent) return;
|
|
43
|
+
if (!window.confirm(`Delete agent "${agent.agentName}"? It will be moved to Recycle Bin.`)) return;
|
|
44
|
+
|
|
45
|
+
try {
|
|
46
|
+
const response = await fetch(`/api/agents/${encodeURIComponent(agentIdentifier)}`, { method: 'DELETE' });
|
|
47
|
+
if (response.ok) {
|
|
48
|
+
// Update local state immediately
|
|
49
|
+
setAgents(agents.filter((a) => a.permanentId !== agent.permanentId && a.agentName !== agent.agentName));
|
|
50
|
+
// Note: router.refresh() is not needed here as the local state update is sufficient
|
|
51
|
+
// and prevents the brief empty list issue during refresh
|
|
52
|
+
} else {
|
|
53
|
+
alert('Failed to delete agent');
|
|
54
|
+
}
|
|
55
|
+
} catch (error) {
|
|
56
|
+
alert('Failed to delete agent');
|
|
57
|
+
}
|
|
25
58
|
};
|
|
26
59
|
|
|
27
|
-
const handleClone = async (
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
60
|
+
const handleClone = async (agentIdentifier: string) => {
|
|
61
|
+
const agent = agents.find((a) => a.permanentId === agentIdentifier || a.agentName === agentIdentifier);
|
|
62
|
+
if (!agent) return;
|
|
63
|
+
if (!window.confirm(`Clone agent "${agent.agentName}"?`)) return;
|
|
64
|
+
|
|
65
|
+
try {
|
|
66
|
+
const response = await fetch(`/api/agents/${encodeURIComponent(agentIdentifier)}/clone`, {
|
|
67
|
+
method: 'POST',
|
|
68
|
+
});
|
|
69
|
+
if (response.ok) {
|
|
70
|
+
const newAgent = await response.json();
|
|
71
|
+
setAgents([...agents, newAgent]);
|
|
72
|
+
router.refresh(); // Refresh server data to ensure consistency
|
|
73
|
+
} else {
|
|
74
|
+
alert('Failed to clone agent');
|
|
75
|
+
}
|
|
76
|
+
} catch (error) {
|
|
77
|
+
alert('Failed to clone agent');
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
const handleToggleVisibility = async (agentIdentifier: string) => {
|
|
82
|
+
const agent = agents.find((a) => a.permanentId === agentIdentifier || a.agentName === agentIdentifier);
|
|
83
|
+
if (!agent) return;
|
|
84
|
+
|
|
85
|
+
const newVisibility = agent.visibility === 'PUBLIC' ? 'PRIVATE' : 'PUBLIC';
|
|
86
|
+
if (!window.confirm(`Make agent "${agent.agentName}" ${newVisibility.toLowerCase()}?`)) return;
|
|
87
|
+
|
|
88
|
+
const response = await fetch(`/api/agents/${encodeURIComponent(agentIdentifier)}`, {
|
|
89
|
+
method: 'PATCH',
|
|
90
|
+
headers: { 'Content-Type': 'application/json' },
|
|
91
|
+
body: JSON.stringify({ visibility: newVisibility }),
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
if (response.ok) {
|
|
95
|
+
// Update the local state
|
|
96
|
+
setAgents(
|
|
97
|
+
agents.map((a) =>
|
|
98
|
+
a.permanentId === agent.permanentId || a.agentName === agent.agentName
|
|
99
|
+
? { ...a, visibility: newVisibility }
|
|
100
|
+
: a,
|
|
101
|
+
),
|
|
102
|
+
);
|
|
103
|
+
router.refresh(); // Refresh server data to ensure consistency
|
|
104
|
+
} else {
|
|
105
|
+
alert('Failed to update agent visibility');
|
|
106
|
+
}
|
|
32
107
|
};
|
|
33
108
|
|
|
34
109
|
return (
|
|
35
110
|
<Section title={`Agents (${agents.length})`}>
|
|
36
111
|
{agents.map((agent) => (
|
|
37
112
|
<AgentCard
|
|
38
|
-
key={agent.agentName}
|
|
113
|
+
key={agent.permanentId || agent.agentName}
|
|
39
114
|
agent={agent}
|
|
40
|
-
|
|
115
|
+
publicUrl={publicUrl}
|
|
116
|
+
href={`/agents/${encodeURIComponent(agent.permanentId || agent.agentName)}`}
|
|
41
117
|
isAdmin={isAdmin}
|
|
42
118
|
onDelete={handleDelete}
|
|
43
119
|
onClone={handleClone}
|
|
120
|
+
onToggleVisibility={handleToggleVisibility}
|
|
121
|
+
visibility={agent.visibility}
|
|
44
122
|
/>
|
|
45
123
|
))}
|
|
46
124
|
{isAdmin && <AddAgentButton />}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
// Client Component for rendering deleted agents
|
|
2
|
+
'use client';
|
|
3
|
+
|
|
4
|
+
import { useState } from 'react';
|
|
5
|
+
import { AgentCard } from './AgentCard';
|
|
6
|
+
|
|
7
|
+
import { AgentBasicInformation } from '../../../../../src/book-2.0/agent-source/AgentBasicInformation';
|
|
8
|
+
|
|
9
|
+
type DeletedAgentsListProps = {
|
|
10
|
+
/**
|
|
11
|
+
* @@@
|
|
12
|
+
*/
|
|
13
|
+
readonly agents: readonly AgentBasicInformation[];
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* @@@
|
|
17
|
+
*/
|
|
18
|
+
readonly isAdmin: boolean;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Base URL of the agents server
|
|
22
|
+
*/
|
|
23
|
+
readonly publicUrl: URL;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export function DeletedAgentsList(props: DeletedAgentsListProps) {
|
|
27
|
+
const { agents: initialAgents, isAdmin, publicUrl } = props;
|
|
28
|
+
const [agents, setAgents] = useState(Array.from(initialAgents));
|
|
29
|
+
|
|
30
|
+
const handleRestore = async (agentIdentifier: string) => {
|
|
31
|
+
const agent = agents.find((a) => a.permanentId === agentIdentifier || a.agentName === agentIdentifier);
|
|
32
|
+
if (!agent) return;
|
|
33
|
+
if (!window.confirm(`Restore agent "${agent.agentName}"?`)) return;
|
|
34
|
+
|
|
35
|
+
try {
|
|
36
|
+
const response = await fetch(`/api/agents/${encodeURIComponent(agentIdentifier)}/restore`, {
|
|
37
|
+
method: 'POST',
|
|
38
|
+
});
|
|
39
|
+
if (response.ok) {
|
|
40
|
+
// Update local state immediately
|
|
41
|
+
setAgents(agents.filter((a) => a.permanentId !== agent.permanentId && a.agentName !== agent.agentName));
|
|
42
|
+
// Note: router.refresh() is not needed here as the local state update is sufficient
|
|
43
|
+
// and prevents the brief empty list issue during refresh
|
|
44
|
+
} else {
|
|
45
|
+
alert('Failed to restore agent');
|
|
46
|
+
}
|
|
47
|
+
} catch (error) {
|
|
48
|
+
alert('Failed to restore agent');
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
return (
|
|
53
|
+
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6">
|
|
54
|
+
{agents.map((agent) => (
|
|
55
|
+
<AgentCard
|
|
56
|
+
key={agent.permanentId || agent.agentName}
|
|
57
|
+
agent={agent}
|
|
58
|
+
publicUrl={publicUrl}
|
|
59
|
+
href={`/agents/${encodeURIComponent(agent.permanentId || agent.agentName)}`}
|
|
60
|
+
isAdmin={isAdmin}
|
|
61
|
+
onRestore={handleRestore}
|
|
62
|
+
/>
|
|
63
|
+
))}
|
|
64
|
+
</div>
|
|
65
|
+
);
|
|
66
|
+
}
|