@promptbook/cli 0.104.0-1 → 0.104.0-11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (229) hide show
  1. package/apps/agents-server/config.ts +1 -3
  2. package/apps/agents-server/next.config.ts +2 -2
  3. package/apps/agents-server/package.json +7 -3
  4. package/apps/agents-server/public/fonts/OpenMoji-color-cbdt.woff2 +0 -0
  5. package/apps/agents-server/public/swagger.json +115 -0
  6. package/apps/agents-server/scripts/generate-reserved-paths/generate-reserved-paths.ts +54 -0
  7. package/apps/agents-server/scripts/generate-reserved-paths/tsconfig.json +19 -0
  8. package/apps/agents-server/src/app/AddAgentButton.tsx +47 -21
  9. package/apps/agents-server/src/app/actions.ts +22 -5
  10. package/apps/agents-server/src/app/admin/browser-test/BrowserTestClient.tsx +211 -0
  11. package/apps/agents-server/src/app/admin/browser-test/page.tsx +13 -0
  12. package/apps/agents-server/src/app/admin/chat-feedback/ChatFeedbackClient.tsx +221 -274
  13. package/apps/agents-server/src/app/admin/chat-history/ChatHistoryClient.tsx +94 -137
  14. package/apps/agents-server/src/app/admin/files/FilesGalleryClient.tsx +263 -0
  15. package/apps/agents-server/src/app/admin/files/actions.ts +61 -0
  16. package/apps/agents-server/src/app/admin/files/page.tsx +13 -0
  17. package/apps/agents-server/src/app/admin/image-generator-test/ImageGeneratorTestClient.tsx +169 -0
  18. package/apps/agents-server/src/app/admin/image-generator-test/page.tsx +13 -0
  19. package/apps/agents-server/src/app/admin/images/ImagesGalleryClient.tsx +256 -0
  20. package/apps/agents-server/src/app/admin/images/actions.ts +60 -0
  21. package/apps/agents-server/src/app/admin/images/page.tsx +13 -0
  22. package/apps/agents-server/src/app/admin/messages/MessagesClient.tsx +294 -0
  23. package/apps/agents-server/src/app/admin/messages/page.tsx +13 -0
  24. package/apps/agents-server/src/app/admin/messages/send-email/SendEmailClient.tsx +104 -0
  25. package/apps/agents-server/src/app/admin/messages/send-email/actions.ts +35 -0
  26. package/apps/agents-server/src/app/admin/messages/send-email/page.tsx +13 -0
  27. package/apps/agents-server/src/app/admin/metadata/MetadataClient.tsx +23 -19
  28. package/apps/agents-server/src/app/admin/search-engine-test/SearchEngineTestClient.tsx +109 -0
  29. package/apps/agents-server/src/app/admin/search-engine-test/actions.ts +17 -0
  30. package/apps/agents-server/src/app/admin/search-engine-test/page.tsx +13 -0
  31. package/apps/agents-server/src/app/agents/[agentName]/AgentChatWrapper.tsx +15 -1
  32. package/apps/agents-server/src/app/agents/[agentName]/AgentOptionsMenu.tsx +51 -9
  33. package/apps/agents-server/src/app/agents/[agentName]/AgentProfileChat.tsx +47 -4
  34. package/apps/agents-server/src/app/agents/[agentName]/AgentProfileWrapper.tsx +53 -11
  35. package/apps/agents-server/src/app/agents/[agentName]/_utils.ts +23 -3
  36. package/apps/agents-server/src/app/agents/[agentName]/agentLinks.tsx +8 -8
  37. package/apps/agents-server/src/app/agents/[agentName]/api/agents/route.ts +17 -26
  38. package/apps/agents-server/src/app/agents/[agentName]/api/book/route.ts +4 -2
  39. package/apps/agents-server/src/app/agents/[agentName]/api/chat/route.ts +20 -0
  40. package/apps/agents-server/src/app/agents/[agentName]/api/mcp/route.ts +6 -11
  41. package/apps/agents-server/src/app/agents/[agentName]/api/profile/route.ts +5 -1
  42. package/apps/agents-server/src/app/agents/[agentName]/api/voice/route.ts +5 -2
  43. package/apps/agents-server/src/app/agents/[agentName]/book/BookEditorWrapper.tsx +20 -16
  44. package/apps/agents-server/src/app/agents/[agentName]/book/page.tsx +15 -2
  45. package/apps/agents-server/src/app/agents/[agentName]/book+chat/page.tsx +15 -2
  46. package/apps/agents-server/src/app/agents/[agentName]/chat/page.tsx +12 -0
  47. package/apps/agents-server/src/app/agents/[agentName]/code/api/route.ts +68 -0
  48. package/apps/agents-server/src/app/agents/[agentName]/code/page.tsx +223 -0
  49. package/apps/agents-server/src/app/agents/[agentName]/generateAgentMetadata.ts +5 -0
  50. package/apps/agents-server/src/app/agents/[agentName]/history/actions.ts +2 -2
  51. package/apps/agents-server/src/app/agents/[agentName]/history/page.tsx +10 -3
  52. package/apps/agents-server/src/app/agents/[agentName]/images/default-avatar.png/getAgentDefaultAvatarPrompt.ts +31 -0
  53. package/apps/agents-server/src/app/agents/[agentName]/images/default-avatar.png/route.ts +194 -0
  54. package/apps/agents-server/src/app/agents/[agentName]/images/icon-256.png/route.tsx +14 -2
  55. package/apps/agents-server/src/app/agents/[agentName]/images/page.tsx +200 -0
  56. package/apps/agents-server/src/app/agents/[agentName]/images/screenshot-fullhd.png/route.tsx +4 -3
  57. package/apps/agents-server/src/app/agents/[agentName]/images/screenshot-phone.png/route.tsx +4 -3
  58. package/apps/agents-server/src/app/agents/[agentName]/integration/page.tsx +10 -3
  59. package/apps/agents-server/src/app/agents/[agentName]/links/page.tsx +11 -4
  60. package/apps/agents-server/src/app/agents/[agentName]/opengraph-image.tsx +11 -2
  61. package/apps/agents-server/src/app/agents/[agentName]/page.tsx +18 -10
  62. package/apps/agents-server/src/app/agents/[agentName]/system-message/page.tsx +100 -0
  63. package/apps/agents-server/src/app/api/admin-email/route.ts +12 -0
  64. package/apps/agents-server/src/app/api/agents/[agentName]/clone/route.ts +13 -14
  65. package/apps/agents-server/src/app/api/agents/[agentName]/restore/route.ts +20 -0
  66. package/apps/agents-server/src/app/api/agents/[agentName]/route.ts +43 -1
  67. package/apps/agents-server/src/app/api/agents/route.ts +28 -3
  68. package/apps/agents-server/src/app/api/api-tokens/route.ts +6 -7
  69. package/apps/agents-server/src/app/api/browser-test/act/route.ts +141 -0
  70. package/apps/agents-server/src/app/api/browser-test/screenshot/route.ts +30 -0
  71. package/apps/agents-server/src/app/api/browser-test/scroll-facebook/route.ts +62 -0
  72. package/apps/agents-server/src/app/api/docs/book.md/route.ts +61 -0
  73. package/apps/agents-server/src/app/api/emails/incoming/sendgrid/route.ts +48 -0
  74. package/apps/agents-server/src/app/api/federated-agents/route.ts +12 -0
  75. package/apps/agents-server/src/app/api/images/[filename]/route.ts +128 -0
  76. package/apps/agents-server/src/app/api/messages/route.ts +102 -0
  77. package/apps/agents-server/src/app/api/metadata/route.ts +5 -6
  78. package/apps/agents-server/src/app/api/upload/route.ts +128 -45
  79. package/apps/agents-server/src/app/docs/[docId]/page.tsx +2 -3
  80. package/apps/agents-server/src/app/docs/page.tsx +12 -12
  81. package/apps/agents-server/src/app/globals.css +140 -33
  82. package/apps/agents-server/src/app/humans.txt/route.ts +1 -1
  83. package/apps/agents-server/src/app/layout.tsx +27 -22
  84. package/apps/agents-server/src/app/page.tsx +54 -6
  85. package/apps/agents-server/src/app/recycle-bin/actions.ts +20 -14
  86. package/apps/agents-server/src/app/recycle-bin/page.tsx +27 -41
  87. package/apps/agents-server/src/app/robots.txt/route.ts +1 -1
  88. package/apps/agents-server/src/app/security.txt/route.ts +1 -1
  89. package/apps/agents-server/src/app/sitemap.xml/route.ts +9 -7
  90. package/apps/agents-server/src/app/swagger/page.tsx +14 -0
  91. package/apps/agents-server/src/components/AgentProfile/AgentCapabilityChips.tsx +38 -0
  92. package/apps/agents-server/src/components/AgentProfile/AgentProfile.tsx +44 -116
  93. package/apps/agents-server/src/components/AgentProfile/AgentProfileImage.tsx +92 -0
  94. package/apps/agents-server/src/components/AgentProfile/QrCodeModal.tsx +0 -1
  95. package/apps/agents-server/src/components/AgentProfile/useAgentBackground.ts +97 -0
  96. package/apps/agents-server/src/components/Auth/AuthControls.tsx +5 -4
  97. package/apps/agents-server/src/components/DeletedAgentBanner.tsx +26 -0
  98. package/apps/agents-server/src/components/DocsToolbar/DocsToolbar.tsx +38 -0
  99. package/apps/agents-server/src/components/DocumentationContent/DocumentationContent.tsx +11 -9
  100. package/apps/agents-server/src/components/Footer/Footer.tsx +5 -5
  101. package/apps/agents-server/src/components/ForgottenPasswordDialog/ForgottenPasswordDialog.tsx +61 -0
  102. package/apps/agents-server/src/components/Header/Header.tsx +130 -40
  103. package/apps/agents-server/src/components/Homepage/AgentCard.tsx +150 -23
  104. package/apps/agents-server/src/components/Homepage/AgentsList.tsx +93 -15
  105. package/apps/agents-server/src/components/Homepage/DeletedAgentsList.tsx +66 -0
  106. package/apps/agents-server/src/components/Homepage/ExternalAgentsSection.tsx +12 -3
  107. package/apps/agents-server/src/components/Homepage/ExternalAgentsSectionClient.tsx +19 -10
  108. package/apps/agents-server/src/components/LayoutWrapper/LayoutWrapper.tsx +3 -2
  109. package/apps/agents-server/src/components/LoginForm/LoginForm.tsx +50 -1
  110. package/apps/agents-server/src/components/NewAgentDialog/NewAgentDialog.tsx +88 -0
  111. package/apps/agents-server/src/components/NotFoundPage/NotFoundPage.tsx +7 -2
  112. package/apps/agents-server/src/components/OpenMojiIcon/OpenMojiIcon.tsx +16 -7
  113. package/apps/agents-server/src/components/PrintHeader/PrintHeader.tsx +4 -4
  114. package/apps/agents-server/src/components/RegisterUserDialog/RegisterUserDialog.tsx +61 -0
  115. package/apps/agents-server/src/components/VercelDeploymentCard/VercelDeploymentCard.tsx +2 -0
  116. package/apps/agents-server/src/components/_utils/generateMetaTxt.ts +12 -10
  117. package/apps/agents-server/src/components/_utils/headlessParam.tsx +7 -3
  118. package/apps/agents-server/src/database/$getTableName.ts +1 -0
  119. package/apps/agents-server/src/database/$provideSupabaseForBrowser.ts +3 -3
  120. package/apps/agents-server/src/database/$provideSupabaseForServer.ts +1 -1
  121. package/apps/agents-server/src/database/$provideSupabaseForWorker.ts +3 -3
  122. package/apps/agents-server/src/database/metadataDefaults.ts +19 -1
  123. package/apps/agents-server/src/database/migrate.ts +34 -1
  124. package/apps/agents-server/src/database/migrations/2025-11-0001-initial-schema.sql +1 -3
  125. package/apps/agents-server/src/database/migrations/2025-11-0002-metadata-table.sql +1 -3
  126. package/apps/agents-server/src/database/migrations/2025-12-0240-agent-public-id.sql +3 -0
  127. package/apps/agents-server/src/database/migrations/2025-12-0360-agent-deleted-at.sql +1 -0
  128. package/apps/agents-server/src/database/migrations/2025-12-0370-image-table.sql +19 -0
  129. package/apps/agents-server/src/database/migrations/2025-12-0380-agent-visibility.sql +1 -0
  130. package/apps/agents-server/src/database/migrations/2025-12-0390-upload-tracking.sql +20 -0
  131. package/apps/agents-server/src/database/migrations/2025-12-0401-file-upload-status.sql +13 -0
  132. package/apps/agents-server/src/database/migrations/2025-12-0402-message-table.sql +42 -0
  133. package/apps/agents-server/src/database/migrations/2025-12-0403-generation-lock-table.sql +15 -0
  134. package/apps/agents-server/src/database/migrations/2025-12-0640-openai-assistant-cache.sql +12 -0
  135. package/apps/agents-server/src/database/migrations/2025-12-0820-agent-history-permanent-id.sql +29 -0
  136. package/apps/agents-server/src/database/migrations/2025-12-0830-image-purpose.sql +5 -0
  137. package/apps/agents-server/src/database/migrations/2025-12-0890-file-agent-id.sql +5 -0
  138. package/apps/agents-server/src/database/schema.ts +244 -4
  139. package/apps/agents-server/src/generated/reservedPaths.ts +32 -0
  140. package/apps/agents-server/src/message-providers/email/_common/Email.ts +73 -0
  141. package/apps/agents-server/src/message-providers/email/_common/utils/TODO.txt +1 -0
  142. package/apps/agents-server/src/message-providers/email/_common/utils/parseEmailAddress.test.ts.todo +108 -0
  143. package/apps/agents-server/src/message-providers/email/_common/utils/parseEmailAddress.ts +62 -0
  144. package/apps/agents-server/src/message-providers/email/_common/utils/parseEmailAddresses.test.ts.todo +117 -0
  145. package/apps/agents-server/src/message-providers/email/_common/utils/parseEmailAddresses.ts +19 -0
  146. package/apps/agents-server/src/message-providers/email/_common/utils/stringifyEmailAddress.test.ts.todo +119 -0
  147. package/apps/agents-server/src/message-providers/email/_common/utils/stringifyEmailAddress.ts +19 -0
  148. package/apps/agents-server/src/message-providers/email/_common/utils/stringifyEmailAddresses.test.ts.todo +74 -0
  149. package/apps/agents-server/src/message-providers/email/_common/utils/stringifyEmailAddresses.ts +14 -0
  150. package/apps/agents-server/src/message-providers/email/sendgrid/SendgridMessageProvider.ts +44 -0
  151. package/apps/agents-server/src/message-providers/email/sendgrid/parseInboundSendgridEmail.ts +49 -0
  152. package/apps/agents-server/src/message-providers/email/zeptomail/ZeptomailMessageProvider.ts +51 -0
  153. package/apps/agents-server/src/message-providers/index.ts +13 -0
  154. package/apps/agents-server/src/message-providers/interfaces/MessageProvider.ts +11 -0
  155. package/apps/agents-server/src/middleware.ts +19 -23
  156. package/apps/agents-server/src/tools/$provideBrowserForServer.ts +32 -0
  157. package/apps/agents-server/src/tools/$provideCdnForServer.ts +7 -2
  158. package/apps/agents-server/src/utils/auth.ts +117 -17
  159. package/apps/agents-server/src/utils/cdn/classes/TrackedFilesStorage.ts +57 -0
  160. package/apps/agents-server/src/utils/cdn/classes/VercelBlobStorage.ts +4 -0
  161. package/apps/agents-server/src/utils/cdn/interfaces/IFilesStorage.ts +18 -0
  162. package/apps/agents-server/src/utils/content/extractBodyContentFromHtml.ts +19 -0
  163. package/apps/agents-server/src/utils/getUserIdFromRequest.ts +35 -0
  164. package/apps/agents-server/src/utils/handleChatCompletion.ts +65 -5
  165. package/apps/agents-server/src/utils/messages/sendMessage.ts +91 -0
  166. package/apps/agents-server/src/utils/messagesAdmin.ts +72 -0
  167. package/apps/agents-server/src/utils/normalization/filenameToPrompt.test.ts +36 -0
  168. package/apps/agents-server/src/utils/normalization/filenameToPrompt.ts +25 -0
  169. package/apps/agents-server/src/utils/validateApiKey.ts +7 -11
  170. package/esm/index.es.js +1534 -1330
  171. package/esm/index.es.js.map +1 -1
  172. package/esm/typings/servers.d.ts +8 -0
  173. package/esm/typings/src/_packages/core.index.d.ts +2 -0
  174. package/esm/typings/src/_packages/types.index.d.ts +16 -2
  175. package/esm/typings/src/book-2.0/agent-source/AgentBasicInformation.d.ts +29 -1
  176. package/esm/typings/src/book-2.0/agent-source/createAgentModelRequirements.d.ts +6 -6
  177. package/esm/typings/src/book-2.0/agent-source/createAgentModelRequirementsWithCommitments.closed.test.d.ts +1 -0
  178. package/esm/typings/src/book-2.0/utils/generatePlaceholderAgentProfileImageUrl.d.ts +3 -3
  179. package/esm/typings/src/book-components/Chat/Chat/ChatMessageItem.d.ts +5 -1
  180. package/esm/typings/src/book-components/Chat/Chat/ChatProps.d.ts +5 -0
  181. package/esm/typings/src/book-components/Chat/CodeBlock/CodeBlock.d.ts +13 -0
  182. package/esm/typings/src/book-components/Chat/MarkdownContent/MarkdownContent.d.ts +1 -0
  183. package/esm/typings/src/book-components/Chat/types/ChatMessage.d.ts +9 -13
  184. package/esm/typings/src/book-components/_common/Dropdown/Dropdown.d.ts +3 -3
  185. package/esm/typings/src/book-components/_common/HamburgerMenu/HamburgerMenu.d.ts +1 -1
  186. package/esm/typings/src/book-components/_common/MenuHoisting/MenuHoistingContext.d.ts +56 -0
  187. package/esm/typings/src/book-components/icons/AboutIcon.d.ts +1 -1
  188. package/esm/typings/src/book-components/icons/AttachmentIcon.d.ts +1 -1
  189. package/esm/typings/src/book-components/icons/CameraIcon.d.ts +1 -1
  190. package/esm/typings/src/book-components/icons/DownloadIcon.d.ts +1 -1
  191. package/esm/typings/src/book-components/icons/MenuIcon.d.ts +1 -1
  192. package/esm/typings/src/book-components/icons/SaveIcon.d.ts +1 -1
  193. package/esm/typings/src/collection/agent-collection/constructors/agent-collection-in-supabase/AgentCollectionInSupabase.d.ts +22 -12
  194. package/esm/typings/src/collection/agent-collection/constructors/agent-collection-in-supabase/AgentsDatabaseSchema.d.ts +27 -15
  195. package/esm/typings/src/commitments/DICTIONARY/DICTIONARY.d.ts +46 -0
  196. package/esm/typings/src/commitments/index.d.ts +2 -1
  197. package/esm/typings/src/llm-providers/_common/utils/count-total-usage/countUsage.d.ts +1 -1
  198. package/esm/typings/src/llm-providers/_multiple/MultipleLlmExecutionTools.d.ts +6 -2
  199. package/esm/typings/src/llm-providers/agent/Agent.d.ts +6 -1
  200. package/esm/typings/src/llm-providers/agent/AgentLlmExecutionTools.d.ts +1 -1
  201. package/esm/typings/src/llm-providers/ollama/OllamaExecutionTools.d.ts +1 -1
  202. package/esm/typings/src/llm-providers/openai/createOpenAiCompatibleExecutionTools.d.ts +1 -1
  203. package/esm/typings/src/llm-providers/remote/RemoteLlmExecutionTools.d.ts +1 -0
  204. package/esm/typings/src/remote-server/ui/ServerApp.d.ts +1 -1
  205. package/esm/typings/src/search-engines/SearchEngine.d.ts +9 -0
  206. package/esm/typings/src/search-engines/SearchResult.d.ts +18 -0
  207. package/esm/typings/src/search-engines/bing/BingSearchEngine.d.ts +15 -0
  208. package/esm/typings/src/search-engines/dummy/DummySearchEngine.d.ts +15 -0
  209. package/esm/typings/src/types/Message.d.ts +49 -0
  210. package/esm/typings/src/types/ModelRequirements.d.ts +38 -14
  211. package/esm/typings/src/types/typeAliases.d.ts +23 -1
  212. package/esm/typings/src/utils/color/utils/colorToDataUrl.d.ts +2 -1
  213. package/esm/typings/src/utils/environment/$detectRuntimeEnvironment.d.ts +4 -4
  214. package/esm/typings/src/utils/environment/$isRunningInBrowser.d.ts +1 -1
  215. package/esm/typings/src/utils/environment/$isRunningInJest.d.ts +1 -1
  216. package/esm/typings/src/utils/environment/$isRunningInNode.d.ts +1 -1
  217. package/esm/typings/src/utils/environment/$isRunningInWebWorker.d.ts +1 -1
  218. package/esm/typings/src/utils/markdown/extractAllBlocksFromMarkdown.d.ts +2 -2
  219. package/esm/typings/src/utils/markdown/extractOneBlockFromMarkdown.d.ts +2 -2
  220. package/esm/typings/src/utils/random/$randomAgentPersona.d.ts +3 -2
  221. package/esm/typings/src/utils/random/$randomBase58.d.ts +12 -0
  222. package/esm/typings/src/version.d.ts +1 -1
  223. package/package.json +1 -1
  224. package/umd/index.umd.js +1542 -1338
  225. package/umd/index.umd.js.map +1 -1
  226. package/apps/agents-server/package-lock.json +0 -27
  227. package/apps/agents-server/public/fonts/download-font.js +0 -22
  228. package/apps/agents-server/src/components/PrintButton/PrintButton.tsx +0 -18
  229. package/esm/typings/src/book-2.0/utils/generateGravatarUrl.d.ts +0 -10
@@ -17,34 +17,36 @@ export function DocumentationContent({ primary, aliases = [], isPrintOnly = fals
17
17
  <div
18
18
  className={`bg-white rounded-xl shadow-lg border border-gray-200 overflow-hidden ${
19
19
  isPrintOnly ? 'shadow-none border-none' : ''
20
- }`}
20
+ } print:shadow-none print:border-none print:rounded-none`}
21
21
  >
22
22
  <div
23
23
  className={`p-8 border-b border-gray-100 bg-gray-50/50 ${
24
24
  isPrintOnly ? 'border-none bg-white p-0 mb-4' : ''
25
- }`}
25
+ } print:p-0 print:border-none print:bg-white print:mb-4`}
26
26
  >
27
27
  <div className="flex items-center gap-4 mb-4">
28
- <h1 className="text-4xl font-bold text-gray-900 tracking-tight">
29
- <OpenMojiIcon icon={primary.icon} className="mr-3" />
28
+ <h1 className="text-4xl font-bold text-gray-900 tracking-tight print:text-3xl">
29
+ <OpenMojiIcon icon={primary.icon} variant="color" className="mr-3" />
30
30
  {primary.type}
31
31
  {aliases.length > 0 && (
32
- <span className="text-gray-400 font-normal ml-4 text-2xl">/ {aliases.join(' / ')}</span>
32
+ <span className="text-gray-400 font-normal ml-4 text-2xl print:text-xl">
33
+ / {aliases.join(' / ')}
34
+ </span>
33
35
  )}
34
36
  </h1>
35
37
  {!isPrintOnly && (
36
- <span className="px-3 py-1 rounded-full text-xs font-medium bg-blue-100 text-blue-700">
38
+ <span className="px-3 py-1 rounded-full text-xs font-medium bg-blue-100 text-blue-700 print:hidden">
37
39
  Commitment
38
40
  </span>
39
41
  )}
40
42
  </div>
41
43
  {primary.description && (
42
- <p className="text-xl text-gray-600 leading-relaxed max-w-3xl">{primary.description}</p>
44
+ <p className="text-xl text-gray-600 leading-relaxed max-w-3xl print:text-lg">{primary.description}</p>
43
45
  )}
44
46
  </div>
45
47
 
46
- <div className={`p-8 ${isPrintOnly ? 'p-0' : ''}`}>
47
- <article className="prose prose-lg prose-slate max-w-none prose-headings:font-bold prose-headings:tracking-tight prose-headings:text-gray-900 prose-h1:text-4xl prose-h1:mb-8 prose-h2:text-2xl prose-h2:mt-12 prose-h2:mb-6 prose-h2:pb-2 prose-h2:border-b prose-h2:border-gray-200 prose-h3:text-xl prose-h3:mt-8 prose-h3:mb-4 prose-h3:text-gray-800 prose-p:text-gray-600 prose-p:leading-relaxed prose-p:mb-6 prose-a:text-blue-600 prose-a:no-underline hover:prose-a:text-blue-700 hover:prose-a:underline prose-a:transition-colors prose-strong:font-bold prose-strong:text-gray-900 prose-code:text-blue-600 prose-code:bg-blue-50 prose-code:px-1.5 prose-code:py-0.5 prose-code:rounded-md prose-code:before:content-none prose-code:after:content-none prose-code:font-medium prose-pre:bg-gray-900 prose-pre:text-gray-100 prose-pre:shadow-lg prose-pre:rounded-xl prose-pre:p-6 prose-ul:list-disc prose-ul:pl-6 prose-li:marker:text-gray-400 prose-li:mb-2 prose-ol:list-decimal prose-ol:pl-6 prose-li:mb-2 prose-blockquote:border-l-4 prose-blockquote:border-blue-500 prose-blockquote:bg-blue-50/50 prose-blockquote:py-2 prose-blockquote:px-4 prose-blockquote:rounded-r-lg prose-blockquote:not-italic prose-blockquote:text-gray-700 prose-blockquote:my-8 prose-img:rounded-xl prose-img:shadow-md prose-img:my-8 prose-hr:border-gray-200 prose-hr:my-10 prose-table:w-full prose-th:text-left prose-th:py-2 prose-th:px-3 prose-th:bg-gray-100 prose-th:font-semibold prose-th:text-gray-900 prose-td:py-2 prose-td:px-3 prose-td:border-b prose-td:border-gray-200 prose-tr:hover:bg-gray-50">
48
+ <div className={`p-8 ${isPrintOnly ? 'p-0' : ''} print:p-0`}>
49
+ <article className="prose prose-lg prose-slate max-w-none prose-headings:font-bold prose-headings:tracking-tight prose-headings:text-gray-900 prose-h1:text-4xl prose-h1:mb-8 prose-h2:text-2xl prose-h2:mt-12 prose-h2:mb-6 prose-h2:pb-2 prose-h2:border-b prose-h2:border-gray-200 prose-h3:text-xl prose-h3:mt-8 prose-h3:mb-4 prose-h3:text-gray-800 prose-p:text-gray-600 prose-p:leading-relaxed prose-p:mb-6 prose-a:text-blue-600 prose-a:no-underline hover:prose-a:text-blue-700 hover:prose-a:underline prose-a:transition-colors prose-strong:font-bold prose-strong:text-gray-900 prose-code:text-blue-600 prose-code:bg-blue-50 prose-code:px-1.5 prose-code:py-0.5 prose-code:rounded-md prose-code:before:content-none prose-code:after:content-none prose-code:font-medium prose-pre:bg-gray-900 prose-pre:text-gray-100 prose-pre:shadow-lg prose-pre:rounded-xl prose-pre:p-6 prose-ul:list-disc prose-ul:pl-6 prose-li:marker:text-gray-400 prose-li:mb-2 prose-ol:list-decimal prose-ol:pl-6 prose-li:mb-2 prose-blockquote:border-l-4 prose-blockquote:border-blue-500 prose-blockquote:bg-blue-50/50 prose-blockquote:py-2 prose-blockquote:px-4 prose-blockquote:rounded-r-lg prose-blockquote:not-italic prose-blockquote:text-gray-700 prose-blockquote:my-8 prose-img:rounded-xl prose-img:shadow-md prose-img:my-8 prose-hr:border-gray-200 prose-hr:my-10 prose-table:w-full prose-th:text-left prose-th:py-2 prose-th:px-3 prose-th:bg-gray-100 prose-th:font-semibold prose-th:text-gray-900 prose-td:py-2 prose-td:px-3 prose-td:border-b prose-td:border-gray-200 prose-tr:hover:bg-gray-50 print:prose-base print:max-w-none">
48
50
  <MarkdownContent
49
51
  content={primary.documentation}
50
52
  /* TODO: !!!!
@@ -114,10 +114,7 @@ export function Footer(props: FooterProps) {
114
114
  </a>
115
115
  </li>
116
116
  <li>
117
- <a
118
- href="https://discord.gg/x3QWNaa89N"
119
- className="text-gray-500 hover:text-gray-900"
120
- >
117
+ <a href="https://discord.gg/x3QWNaa89N" className="text-gray-500 hover:text-gray-900">
121
118
  Discord
122
119
  </a>
123
120
  </li>
@@ -160,8 +157,10 @@ export function Footer(props: FooterProps) {
160
157
  💜
161
158
  </p>
162
159
  </div>
160
+ {/*
161
+ TODO: [🧠] Should we show this in the footer?
163
162
  <div className="flex flex-col items-center mt-8">
164
- {/* <Image src={TechnologyIncubation} alt="Our Sponsor" className="h-32 w-auto" /> */}
163
+ {/* <Image src={TechnologyIncubation} alt="Our Sponsor" className="h-32 w-auto" /> * /}
165
164
  <p className="text-center text-sm text-gray-500 mt-4 max-w-lg">
166
165
  This project was implemented with funding from the national budget
167
166
  <br />
@@ -169,6 +168,7 @@ export function Footer(props: FooterProps) {
169
168
  Incubation programme.
170
169
  </p>
171
170
  </div>
171
+ */}
172
172
  </div>
173
173
  </footer>
174
174
  );
@@ -0,0 +1,61 @@
1
+ 'use client';
2
+
3
+ import { X } from 'lucide-react';
4
+ import { Portal } from '../Portal/Portal';
5
+
6
+ type ForgottenPasswordDialogProps = {
7
+ isOpen: boolean;
8
+ onClose: () => void;
9
+ adminEmail: string;
10
+ };
11
+
12
+ export function ForgottenPasswordDialog(props: ForgottenPasswordDialogProps) {
13
+ const { isOpen, onClose, adminEmail } = props;
14
+
15
+ if (!isOpen) {
16
+ return null;
17
+ }
18
+
19
+ return (
20
+ <Portal>
21
+ <div className="fixed inset-0 z-[9999] flex items-center justify-center bg-black/50 backdrop-blur-sm animate-in fade-in duration-200">
22
+ <div className="relative w-full max-w-md bg-white rounded-lg shadow-lg border border-gray-200 p-6 animate-in zoom-in-95 duration-200">
23
+ <button
24
+ onClick={onClose}
25
+ className="absolute top-4 right-4 text-gray-400 hover:text-gray-500 transition-colors"
26
+ >
27
+ <X className="w-5 h-5" />
28
+ <span className="sr-only">Close</span>
29
+ </button>
30
+
31
+ <div className="mb-6">
32
+ <h2 className="text-xl font-semibold text-gray-900">Forgotten Password</h2>
33
+ <p className="text-sm text-gray-500 mt-1">Reset your password</p>
34
+ </div>
35
+
36
+ <div className="space-y-4">
37
+ <div className="p-4 bg-blue-50 border border-blue-200 rounded-md">
38
+ <p className="text-sm text-blue-800">
39
+ This Promptbook server has no email capability. Please contact the administrator at{' '}
40
+ <a
41
+ href={`mailto:${adminEmail}`}
42
+ className="font-medium text-blue-900 underline hover:text-blue-800"
43
+ >
44
+ {adminEmail}
45
+ </a>{' '}
46
+ to reset your password.
47
+ </p>
48
+ </div>
49
+
50
+ <button
51
+ onClick={onClose}
52
+ className="w-full inline-flex items-center justify-center rounded-md text-sm font-medium h-10 px-4 py-2 bg-gray-100 text-gray-900 hover:bg-gray-200 focus:outline-none focus:ring-2 focus:ring-gray-500 focus:ring-offset-2 transition-colors"
53
+ >
54
+ Close
55
+ </button>
56
+ </div>
57
+ </div>
58
+ </div>
59
+ </Portal>
60
+ );
61
+ }
@@ -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 src={server.logoUrl || serverLogoUrl || promptbookLogoBlueTransparent.src} alt={server.title} width={20} height={20} className="w-5 h-5 object-contain rounded-full" />
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 src={server.logoUrl || promptbookLogoBlueTransparent.src} alt={server.title} width={20} height={20} className="w-5 h-5 object-contain rounded-full" />
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,34 @@ 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
+ },
312
+ {
313
+ label: 'Image Generator Test',
314
+ href: '/admin/image-generator-test',
315
+ },
316
+ {
317
+ label: 'Search Engine Test',
318
+ href: '/admin/search-engine-test',
319
+ },
320
+ {
321
+ label: 'Images gallery',
322
+ href: '/admin/images',
323
+ },
324
+ {
325
+ label: 'Files',
326
+ href: '/admin/files',
327
+ },
280
328
  ],
281
329
  },
282
330
  {
@@ -320,44 +368,46 @@ export function Header(props: HeaderProps) {
320
368
 
321
369
  {/* Desktop Navigation */}
322
370
  <nav className="hidden lg:flex items-center gap-8">
323
- {/* Federated servers dropdown */}
324
- <div className="relative">
325
- <button
326
- className="flex items-center gap-1 text-sm font-medium text-gray-600 hover:text-gray-900 transition-colors cursor-pointer"
327
- onClick={() => setIsFederatedOpen(!isFederatedOpen)}
328
- onBlur={() => setTimeout(() => setIsFederatedOpen(false), 200)}
329
- >
330
- <ChevronDown className="w-4 h-4" />
331
- <span>Switch server</span>
332
- </button>
333
- {isFederatedOpen && (
334
- <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">
335
- {federatedDropdownItems.map((subItem, subIndex) => {
336
- const className = `block px-4 py-2 text-sm text-gray-700 hover:bg-gray-50 hover:text-gray-900 ${
337
- subItem.isBold ? 'font-medium' : ''
338
- } ${subItem.isBordered ? 'border-b border-gray-100' : ''}`;
339
-
340
- if (subItem.href) {
371
+ {/* Federated servers dropdown - only show if there are federated servers */}
372
+ {federatedServers.length > 0 && (
373
+ <div className="relative">
374
+ <button
375
+ className="flex items-center gap-1 text-sm font-medium text-gray-600 hover:text-gray-900 transition-colors cursor-pointer"
376
+ onClick={() => setIsFederatedOpen(!isFederatedOpen)}
377
+ onBlur={() => setTimeout(() => setIsFederatedOpen(false), 200)}
378
+ >
379
+ <ChevronDown className="w-4 h-4" />
380
+ <span>Switch server</span>
381
+ </button>
382
+ {isFederatedOpen && (
383
+ <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">
384
+ {federatedDropdownItems.map((subItem, subIndex) => {
385
+ const className = `block px-4 py-2 text-sm text-gray-700 hover:bg-gray-50 hover:text-gray-900 ${
386
+ subItem.isBold ? 'font-medium' : ''
387
+ } ${subItem.isBordered ? 'border-b border-gray-100' : ''}`;
388
+
389
+ if (subItem.href) {
390
+ return (
391
+ <HeadlessLink
392
+ key={subIndex}
393
+ href={subItem.href}
394
+ className={className}
395
+ onClick={() => setIsFederatedOpen(false)}
396
+ >
397
+ {subItem.label}
398
+ </HeadlessLink>
399
+ );
400
+ }
341
401
  return (
342
- <HeadlessLink
343
- key={subIndex}
344
- href={subItem.href}
345
- className={className}
346
- onClick={() => setIsFederatedOpen(false)}
347
- >
402
+ <span key={subIndex} className={className}>
348
403
  {subItem.label}
349
- </HeadlessLink>
404
+ </span>
350
405
  );
351
- }
352
- return (
353
- <span key={subIndex} className={className}>
354
- {subItem.label}
355
- </span>
356
- );
357
- })}
358
- </div>
359
- )}
360
- </div>
406
+ })}
407
+ </div>
408
+ )}
409
+ </div>
410
+ )}
361
411
  {menuItems.map((item, index) => {
362
412
  if (item.type === 'link') {
363
413
  return (
@@ -433,6 +483,24 @@ export function Header(props: HeaderProps) {
433
483
  )}
434
484
  </nav>
435
485
 
486
+ {/* Hoisted Menu Items */}
487
+ {menuHoisting && menuHoisting.menu.length > 0 && (
488
+ <div className="hidden lg:flex items-center gap-2 border-r border-gray-200 pr-4 mr-2">
489
+ {menuHoisting.menu.map((item, index) => (
490
+ <button
491
+ key={index}
492
+ onClick={item.onClick}
493
+ className={`p-2 rounded-md hover:bg-gray-100 transition-colors text-gray-600 hover:text-gray-900 ${
494
+ item.isActive ? 'bg-gray-100 text-gray-900' : ''
495
+ }`}
496
+ title={item.name}
497
+ >
498
+ {item.icon}
499
+ </button>
500
+ ))}
501
+ </div>
502
+ )}
503
+
436
504
  {/* CTA Button & Mobile Menu Toggle */}
437
505
  <div className="flex items-center gap-4">
438
506
  {just(false /* TODO: [🧠] Figure out what to do with call to action */) && (
@@ -531,6 +599,28 @@ export function Header(props: HeaderProps) {
531
599
  }}
532
600
  >
533
601
  <nav className="container mx-auto flex flex-col gap-4 px-6">
602
+ {/* Hoisted Menu Items for Mobile */}
603
+ {menuHoisting && menuHoisting.menu.length > 0 && (
604
+ <div className="py-2 border-b border-gray-100 mb-2 flex gap-2 overflow-x-auto">
605
+ {menuHoisting.menu.map((item, index) => (
606
+ <button
607
+ key={index}
608
+ onClick={() => {
609
+ item.onClick();
610
+ setIsMenuOpen(false);
611
+ }}
612
+ className={`p-2 rounded-md hover:bg-gray-100 transition-colors text-gray-600 hover:text-gray-900 ${
613
+ item.isActive ? 'bg-gray-100 text-gray-900' : ''
614
+ }`}
615
+ title={item.name}
616
+ >
617
+ {item.icon}
618
+ <span className="sr-only">{item.name}</span>
619
+ </button>
620
+ ))}
621
+ </div>
622
+ )}
623
+
534
624
  {/* Login Status for Mobile */}
535
625
  <div className="py-2 border-b border-gray-100 mb-2">
536
626
  {!currentUser && !isAdmin && (
@@ -1,43 +1,170 @@
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 { AvatarProfile } from '../../../../../src/book-components/AvatarProfile/AvatarProfile/AvatarProfile';
5
- import { Card } from './Card';
8
+ import { AgentCapabilityChips } from '../AgentProfile/AgentCapabilityChips';
9
+ import { useAgentBackground } from '../AgentProfile/useAgentBackground';
6
10
 
7
11
  type AgentCardProps = {
8
- agent: AgentBasicInformation;
9
- href: string;
10
- isAdmin?: boolean;
11
- onDelete?: (agentName: string) => void;
12
- onClone?: (agentName: string) => void;
12
+ /**
13
+ * The basic information of the agent to display
14
+ */
15
+ readonly agent: AgentBasicInformation;
16
+
17
+ /**
18
+ * The URL to navigate to when the card is clicked
19
+ */
20
+ readonly href: string;
21
+
22
+ /**
23
+ * Base URL of the agents server
24
+ */
25
+ readonly publicUrl: URL;
26
+
27
+ /**
28
+ * Whether the current user has admin privileges
29
+ */
30
+ readonly isAdmin?: boolean;
31
+
32
+ /**
33
+ * Callback function to delete the agent
34
+ */
35
+ readonly onDelete?: (agentIdentifier: string) => void;
36
+
37
+ /**
38
+ * Callback function to clone the agent
39
+ */
40
+ readonly onClone?: (agentIdentifier: string) => void;
41
+
42
+ /**
43
+ * Callback function to toggle the agent's visibility
44
+ */
45
+ readonly onToggleVisibility?: (agentIdentifier: string) => void;
46
+
47
+ /**
48
+ * Callback function to restore a deleted agent
49
+ */
50
+ readonly onRestore?: (agentIdentifier: string) => void;
51
+
52
+ /**
53
+ * The current visibility status of the agent
54
+ */
55
+ readonly visibility?: 'PUBLIC' | 'PRIVATE';
13
56
  };
14
57
 
15
58
  const ACTION_BUTTON_CLASSES =
16
59
  'text-white px-3 py-1 rounded shadow text-xs font-medium transition-colors uppercase tracking-wider opacity-80 hover:opacity-100';
17
60
 
18
- export function AgentCard({ agent, href, isAdmin, onDelete, onClone }: AgentCardProps) {
61
+ export function AgentCard({
62
+ agent,
63
+ href,
64
+ isAdmin,
65
+ publicUrl,
66
+ onDelete,
67
+ onClone,
68
+ onToggleVisibility,
69
+ onRestore,
70
+ visibility,
71
+ }: AgentCardProps) {
72
+ const { meta, agentName } = agent;
73
+ const fullname = (meta.fullname as string) || agentName || 'Agent';
74
+ const imageUrl = meta.image || generatePlaceholderAgentProfileImageUrl(agentName, publicUrl);
75
+ const personaDescription = agent.personaDescription || '';
76
+
77
+ const { brandColorLightHex, brandColorDarkHex, backgroundImage } = useAgentBackground(meta.color);
78
+
19
79
  return (
20
80
  <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
- }
81
+ <Link href={href} className="block h-full transition-transform hover:scale-[1.02] duration-300">
82
+ <div
83
+ className="h-full rounded-2xl overflow-hidden shadow-lg hover:shadow-2xl transition-all duration-300 flex flex-col border border-white/20"
84
+ style={{
85
+ background: `url("${backgroundImage}")`,
86
+ backgroundSize: 'cover',
87
+ backgroundPosition: 'center',
88
+ }}
30
89
  >
31
- <AvatarProfile agent={agent} />
32
- </Card>
90
+ <div className="p-6 flex flex-col items-center flex-grow backdrop-blur-[2px]">
91
+ {/* Image container */}
92
+ <div
93
+ className="w-32 h-32 mb-4 shadow-lg overflow-hidden flex-shrink-0 bg-black/20"
94
+ style={{
95
+ boxShadow: `0 10px 20px -5px rgba(0, 0, 0, 0.2), 0 0 0 1px ${brandColorLightHex}40`,
96
+
97
+ // Note: Make it squircle
98
+ borderRadius: '50%',
99
+ ['cornerShape' as really_any /* <- Note: `cornerShape` is non standard CSS property */]:
100
+ 'squircle ',
101
+ }}
102
+ >
103
+ {imageUrl ? (
104
+ // eslint-disable-next-line @next/next/no-img-element
105
+ <img src={imageUrl} alt={fullname} className="w-full h-full object-cover" />
106
+ ) : (
107
+ <div
108
+ className="w-full h-full flex items-center justify-center text-4xl font-bold text-white/80"
109
+ style={{ backgroundColor: brandColorDarkHex }}
110
+ >
111
+ {fullname.charAt(0).toUpperCase()}
112
+ </div>
113
+ )}
114
+ </div>
115
+
116
+ <h3
117
+ className="text-lg font-bold text-gray-900 text-center leading-tight mb-2"
118
+ style={{ textShadow: '0 1px 2px rgba(255,255,255,0.8)' }}
119
+ >
120
+ {fullname}
121
+ </h3>
122
+
123
+ <p className="text-sm text-gray-800 text-center line-clamp-3 leading-relaxed font-medium mix-blend-hard-light">
124
+ {personaDescription}
125
+ </p>
126
+
127
+ <div className="flex justify-center w-full">
128
+ <AgentCapabilityChips agent={agent} className="justify-center mt-3 opacity-90 scale-90" />
129
+ </div>
130
+ </div>
131
+ </div>
33
132
  </Link>
34
- {isAdmin && (
133
+ {isAdmin && onRestore && (
35
134
  <div className="absolute top-2 right-2 flex gap-2 opacity-0 group-hover:opacity-100 transition-opacity z-10">
135
+ <button
136
+ className={`bg-green-500 hover:bg-green-600 ${ACTION_BUTTON_CLASSES}`}
137
+ onClick={(e) => {
138
+ e.preventDefault();
139
+ onRestore(agent.permanentId || agent.agentName);
140
+ }}
141
+ title="Restore agent"
142
+ >
143
+ <RotateCcwIcon className="w-3 h-3" />
144
+ </button>
145
+ </div>
146
+ )}
147
+ {isAdmin && !onRestore && (
148
+ <div className="absolute top-2 right-2 flex gap-2 opacity-0 group-hover:opacity-100 transition-opacity z-10">
149
+ <button
150
+ className={`${
151
+ visibility === 'PUBLIC'
152
+ ? 'bg-green-500 hover:bg-green-600'
153
+ : 'bg-gray-500 hover:bg-gray-600'
154
+ } ${ACTION_BUTTON_CLASSES}`}
155
+ onClick={(e) => {
156
+ e.preventDefault();
157
+ onToggleVisibility?.(agent.permanentId || agent.agentName);
158
+ }}
159
+ title={`Make ${visibility === 'PUBLIC' ? 'private' : 'public'}`}
160
+ >
161
+ {visibility === 'PUBLIC' ? <EyeIcon className="w-3 h-3" /> : <EyeOffIcon className="w-3 h-3" />}
162
+ </button>
36
163
  <button
37
164
  className={`bg-blue-500 hover:bg-blue-600 ${ACTION_BUTTON_CLASSES}`}
38
165
  onClick={(e) => {
39
166
  e.preventDefault();
40
- onClone?.(agent.agentName);
167
+ onClone?.(agent.permanentId || agent.agentName);
41
168
  }}
42
169
  title="Clone agent"
43
170
  >
@@ -47,7 +174,7 @@ export function AgentCard({ agent, href, isAdmin, onDelete, onClone }: AgentCard
47
174
  className={`bg-red-500 hover:bg-red-600 ${ACTION_BUTTON_CLASSES}`}
48
175
  onClick={(e) => {
49
176
  e.preventDefault();
50
- onDelete?.(agent.agentName);
177
+ onDelete?.(agent.permanentId || agent.agentName);
51
178
  }}
52
179
  title="Delete agent"
53
180
  >