@promptbook/cli 0.103.0-52 → 0.103.0-53
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/apps/agents-server/README.md +1 -1
- package/apps/agents-server/config.ts +3 -3
- package/apps/agents-server/next.config.ts +1 -1
- package/apps/agents-server/public/sw.js +16 -0
- package/apps/agents-server/src/app/AddAgentButton.tsx +24 -4
- package/apps/agents-server/src/app/actions.ts +15 -13
- package/apps/agents-server/src/app/admin/chat-feedback/ChatFeedbackClient.tsx +541 -0
- package/apps/agents-server/src/app/admin/chat-feedback/page.tsx +22 -0
- package/apps/agents-server/src/app/admin/chat-history/ChatHistoryClient.tsx +532 -0
- package/apps/agents-server/src/app/admin/chat-history/page.tsx +21 -0
- package/apps/agents-server/src/app/admin/metadata/MetadataClient.tsx +241 -27
- package/apps/agents-server/src/app/admin/models/page.tsx +22 -0
- package/apps/agents-server/src/app/admin/users/[userId]/UserDetailClient.tsx +131 -0
- package/apps/agents-server/src/app/admin/users/[userId]/page.tsx +21 -0
- package/apps/agents-server/src/app/admin/users/page.tsx +18 -0
- package/apps/agents-server/src/app/agents/[agentName]/ClearAgentChatFeedbackButton.tsx +63 -0
- package/apps/agents-server/src/app/agents/[agentName]/ClearAgentChatHistoryButton.tsx +63 -0
- package/apps/agents-server/src/app/agents/[agentName]/CloneAgentButton.tsx +41 -0
- package/apps/agents-server/src/app/agents/[agentName]/InstallPwaButton.tsx +74 -0
- package/apps/agents-server/src/app/agents/[agentName]/ServiceWorkerRegister.tsx +24 -0
- package/apps/agents-server/src/app/agents/[agentName]/_utils.ts +19 -0
- package/apps/agents-server/src/app/agents/[agentName]/api/agents/route.ts +67 -0
- package/apps/agents-server/src/app/agents/[agentName]/api/profile/route.ts +3 -0
- package/apps/agents-server/src/app/agents/[agentName]/api/voice/route.ts +177 -0
- package/apps/agents-server/src/app/agents/[agentName]/book/page.tsx +3 -0
- package/apps/agents-server/src/app/agents/[agentName]/book+chat/AgentBookAndChat.tsx +53 -1
- package/apps/agents-server/src/app/agents/[agentName]/generateAgentMetadata.ts +11 -11
- package/apps/agents-server/src/app/agents/[agentName]/history/RestoreVersionButton.tsx +46 -0
- package/apps/agents-server/src/app/agents/[agentName]/history/actions.ts +12 -0
- package/apps/agents-server/src/app/agents/[agentName]/history/page.tsx +62 -0
- package/apps/agents-server/src/app/agents/[agentName]/images/icon-256.png/route.tsx +80 -0
- package/apps/agents-server/src/app/agents/[agentName]/images/screenshot-fullhd.png/route.tsx +92 -0
- package/apps/agents-server/src/app/agents/[agentName]/images/screenshot-phone.png/route.tsx +92 -0
- package/apps/agents-server/src/app/agents/[agentName]/integration/page.tsx +61 -0
- package/apps/agents-server/src/app/agents/[agentName]/opengraph-image.tsx +102 -0
- package/apps/agents-server/src/app/agents/[agentName]/page.tsx +41 -22
- package/apps/agents-server/src/app/api/agents/[agentName]/clone/route.ts +47 -0
- package/apps/agents-server/src/app/api/agents/[agentName]/route.ts +19 -0
- package/apps/agents-server/src/app/api/agents/route.ts +22 -13
- package/apps/agents-server/src/app/api/auth/login/route.ts +6 -44
- package/apps/agents-server/src/app/api/chat-feedback/[id]/route.ts +38 -0
- package/apps/agents-server/src/app/api/chat-feedback/route.ts +157 -0
- package/apps/agents-server/src/app/api/chat-history/[id]/route.ts +37 -0
- package/apps/agents-server/src/app/api/chat-history/route.ts +147 -0
- package/apps/agents-server/src/app/api/federated-agents/route.ts +17 -0
- package/apps/agents-server/src/app/api/upload/route.ts +9 -1
- package/apps/agents-server/src/app/docs/[docId]/page.tsx +62 -0
- package/apps/agents-server/src/app/docs/page.tsx +33 -0
- package/apps/agents-server/src/app/layout.tsx +29 -3
- package/apps/agents-server/src/app/manifest.ts +109 -0
- package/apps/agents-server/src/app/page.tsx +8 -45
- package/apps/agents-server/src/app/recycle-bin/RestoreAgentButton.tsx +40 -0
- package/apps/agents-server/src/app/recycle-bin/actions.ts +27 -0
- package/apps/agents-server/src/app/recycle-bin/page.tsx +58 -0
- package/apps/agents-server/src/app/restricted/page.tsx +33 -0
- package/apps/agents-server/src/app/test/og-image/README.md +1 -0
- package/apps/agents-server/src/app/test/og-image/opengraph-image.tsx +37 -0
- package/apps/agents-server/src/app/test/og-image/page.tsx +22 -0
- package/apps/agents-server/src/components/Footer/Footer.tsx +175 -0
- package/apps/agents-server/src/components/Header/Header.tsx +445 -79
- package/apps/agents-server/src/components/Homepage/AgentCard.tsx +46 -14
- package/apps/agents-server/src/components/Homepage/AgentsList.tsx +58 -0
- package/apps/agents-server/src/components/Homepage/Card.tsx +1 -1
- package/apps/agents-server/src/components/Homepage/ExternalAgentsSection.tsx +21 -0
- package/apps/agents-server/src/components/Homepage/ExternalAgentsSectionClient.tsx +183 -0
- package/apps/agents-server/src/components/Homepage/ModelsSection.tsx +75 -0
- package/apps/agents-server/src/components/LayoutWrapper/LayoutWrapper.tsx +28 -3
- package/apps/agents-server/src/components/LoginDialog/LoginDialog.tsx +18 -17
- package/apps/agents-server/src/components/Portal/Portal.tsx +38 -0
- package/apps/agents-server/src/components/UsersList/UsersList.tsx +82 -131
- package/apps/agents-server/src/components/UsersList/useUsersAdmin.ts +139 -0
- package/apps/agents-server/src/database/metadataDefaults.ts +38 -6
- package/apps/agents-server/src/middleware.ts +146 -93
- package/apps/agents-server/src/tools/$provideServer.ts +2 -2
- package/apps/agents-server/src/utils/authenticateUser.ts +42 -0
- package/apps/agents-server/src/utils/chatFeedbackAdmin.ts +96 -0
- package/apps/agents-server/src/utils/chatHistoryAdmin.ts +96 -0
- package/apps/agents-server/src/utils/getEffectiveFederatedServers.ts +22 -0
- package/apps/agents-server/src/utils/getFederatedAgents.ts +31 -8
- package/apps/agents-server/src/utils/getFederatedServersFromMetadata.ts +10 -0
- package/apps/agents-server/src/utils/getVisibleCommitmentDefinitions.ts +12 -0
- package/apps/agents-server/src/utils/isUserAdmin.ts +2 -2
- package/apps/agents-server/vercel.json +7 -0
- package/esm/index.es.js +153 -2
- package/esm/index.es.js.map +1 -1
- package/esm/typings/servers.d.ts +8 -1
- package/esm/typings/src/_packages/components.index.d.ts +2 -0
- package/esm/typings/src/_packages/core.index.d.ts +6 -0
- package/esm/typings/src/_packages/types.index.d.ts +2 -0
- package/esm/typings/src/_packages/utils.index.d.ts +2 -0
- package/esm/typings/src/book-2.0/agent-source/AgentModelRequirements.d.ts +7 -0
- package/esm/typings/src/book-components/Chat/Chat/ChatProps.d.ts +4 -0
- package/esm/typings/src/book-components/_common/HamburgerMenu/HamburgerMenu.d.ts +12 -0
- package/esm/typings/src/book-components/icons/MicIcon.d.ts +8 -0
- package/esm/typings/src/collection/agent-collection/constructors/agent-collection-in-supabase/AgentCollectionInSupabase.d.ts +17 -0
- package/esm/typings/src/commitments/MESSAGE/AgentMessageCommitmentDefinition.d.ts +28 -0
- package/esm/typings/src/commitments/MESSAGE/UserMessageCommitmentDefinition.d.ts +28 -0
- package/esm/typings/src/commitments/index.d.ts +20 -1
- package/esm/typings/src/execution/LlmExecutionTools.d.ts +9 -0
- package/esm/typings/src/llm-providers/agent/AgentLlmExecutionTools.d.ts +2 -1
- package/esm/typings/src/llm-providers/agent/RemoteAgent.d.ts +10 -1
- package/esm/typings/src/utils/normalization/normalizeMessageText.d.ts +9 -0
- package/esm/typings/src/utils/normalization/normalizeMessageText.test.d.ts +1 -0
- package/esm/typings/src/version.d.ts +1 -1
- package/package.json +1 -1
- package/umd/index.umd.js +153 -2
- package/umd/index.umd.js.map +1 -1
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// Client Component for rendering and deleting agents
|
|
2
|
+
'use client';
|
|
3
|
+
|
|
4
|
+
import React, { useState } from 'react';
|
|
5
|
+
import { TrashIcon } from 'lucide-react';
|
|
6
|
+
import Link from 'next/link';
|
|
7
|
+
import { AddAgentButton } from '../../app/AddAgentButton';
|
|
8
|
+
import { AgentCard } from './AgentCard';
|
|
9
|
+
import { Section } from './Section';
|
|
10
|
+
|
|
11
|
+
import { AgentBasicInformation } from '../../../../../src/book-2.0/agent-source/AgentBasicInformation';
|
|
12
|
+
|
|
13
|
+
type AgentsListProps = {
|
|
14
|
+
agents: AgentBasicInformation[];
|
|
15
|
+
isAdmin: boolean;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export function AgentsList({ agents: initialAgents, isAdmin }: AgentsListProps) {
|
|
19
|
+
const [agents, setAgents] = useState(Array.from(initialAgents));
|
|
20
|
+
|
|
21
|
+
const handleDelete = async (agentName: string) => {
|
|
22
|
+
if (!window.confirm(`Delete agent "${agentName}"? It will be moved to Recycle Bin.`)) return;
|
|
23
|
+
await fetch(`/api/agents/${encodeURIComponent(agentName)}`, { method: 'DELETE' });
|
|
24
|
+
setAgents(agents.filter((a) => a.agentName !== agentName));
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const handleClone = async (agentName: string) => {
|
|
28
|
+
if (!window.confirm(`Clone agent "${agentName}"?`)) return;
|
|
29
|
+
const response = await fetch(`/api/agents/${encodeURIComponent(agentName)}/clone`, { method: 'POST' });
|
|
30
|
+
const newAgent = await response.json();
|
|
31
|
+
setAgents([...agents, newAgent]);
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
return (
|
|
35
|
+
<Section title={`Agents (${agents.length})`}>
|
|
36
|
+
{agents.map((agent) => (
|
|
37
|
+
<AgentCard
|
|
38
|
+
key={agent.agentName}
|
|
39
|
+
agent={agent}
|
|
40
|
+
href={`/${agent.agentName}`}
|
|
41
|
+
isAdmin={isAdmin}
|
|
42
|
+
onDelete={handleDelete}
|
|
43
|
+
onClone={handleClone}
|
|
44
|
+
/>
|
|
45
|
+
))}
|
|
46
|
+
{isAdmin && <AddAgentButton />}
|
|
47
|
+
{isAdmin && (
|
|
48
|
+
<Link
|
|
49
|
+
href="/recycle-bin"
|
|
50
|
+
className="flex items-center gap-2 px-4 py-2 mt-4 text-gray-600 hover:text-red-600 transition-colors"
|
|
51
|
+
>
|
|
52
|
+
<TrashIcon className="w-4 h-4" />
|
|
53
|
+
Open Recycle Bin
|
|
54
|
+
</Link>
|
|
55
|
+
)}
|
|
56
|
+
</Section>
|
|
57
|
+
);
|
|
58
|
+
}
|
|
@@ -9,7 +9,7 @@ type CardProps = {
|
|
|
9
9
|
export function Card({ children, className = '', style }: CardProps) {
|
|
10
10
|
return (
|
|
11
11
|
<div
|
|
12
|
-
className={`block p-6 bg-white rounded-lg shadow-md hover:shadow-xl transition-shadow duration-300 border border-gray-200 hover:border-blue-400 ${className}`}
|
|
12
|
+
className={`block h-full p-6 bg-white rounded-lg shadow-md hover:shadow-xl transition-shadow duration-300 border border-gray-200 hover:border-blue-400 ${className}`}
|
|
13
13
|
style={style}
|
|
14
14
|
>
|
|
15
15
|
{children}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { AgentsByServer } from '../../utils/getFederatedAgents';
|
|
2
|
+
import { AgentCard } from './AgentCard';
|
|
3
|
+
import { Section } from './Section';
|
|
4
|
+
|
|
5
|
+
type ExternalAgentsSectionProps = {
|
|
6
|
+
agentsByServer: AgentsByServer[];
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export function ExternalAgentsSection({ agentsByServer }: ExternalAgentsSectionProps) {
|
|
10
|
+
return (
|
|
11
|
+
<>
|
|
12
|
+
{agentsByServer.map(({ serverUrl, agents }) => (
|
|
13
|
+
<Section key={serverUrl} title={`Agents from ${new URL(serverUrl).hostname} (${agents.length})`}>
|
|
14
|
+
{agents.map((agent) => (
|
|
15
|
+
<AgentCard key={agent.url} agent={agent} href={agent.url} />
|
|
16
|
+
))}
|
|
17
|
+
</Section>
|
|
18
|
+
))}
|
|
19
|
+
</>
|
|
20
|
+
);
|
|
21
|
+
}
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useEffect, useState } from 'react';
|
|
4
|
+
import type { AgentsByServer } from '../../utils/getFederatedAgents';
|
|
5
|
+
import { AgentCard } from './AgentCard';
|
|
6
|
+
import { Section } from './Section';
|
|
7
|
+
|
|
8
|
+
type FederatedServersResponse = {
|
|
9
|
+
federatedServers: string[];
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
type ServerState = {
|
|
13
|
+
status: 'loading' | 'success' | 'error';
|
|
14
|
+
agents: AgentsByServer['agents'];
|
|
15
|
+
error?: string;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export function ExternalAgentsSectionClient() {
|
|
19
|
+
const [servers, setServers] = useState<Record<string, ServerState>>({});
|
|
20
|
+
const [initialLoading, setInitialLoading] = useState(true);
|
|
21
|
+
|
|
22
|
+
useEffect(() => {
|
|
23
|
+
let isCancelled = false;
|
|
24
|
+
|
|
25
|
+
const fetchServers = async () => {
|
|
26
|
+
try {
|
|
27
|
+
const response = await fetch('/api/federated-agents');
|
|
28
|
+
if (!response.ok) throw new Error('Failed to fetch federated servers');
|
|
29
|
+
|
|
30
|
+
const data: FederatedServersResponse = await response.json();
|
|
31
|
+
|
|
32
|
+
if (isCancelled) return;
|
|
33
|
+
|
|
34
|
+
const initialServerState: Record<string, ServerState> = {};
|
|
35
|
+
data.federatedServers.forEach(serverUrl => {
|
|
36
|
+
initialServerState[serverUrl] = { status: 'loading', agents: [] };
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
setServers(initialServerState);
|
|
40
|
+
setInitialLoading(false);
|
|
41
|
+
|
|
42
|
+
// Fetch agents for each server independently
|
|
43
|
+
data.federatedServers.forEach(serverUrl => {
|
|
44
|
+
fetchAgentsForServer(serverUrl);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
} catch (error) {
|
|
48
|
+
console.error('Failed to load federated servers list', error);
|
|
49
|
+
if (!isCancelled) setInitialLoading(false);
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const fetchAgentsForServer = async (serverUrl: string) => {
|
|
54
|
+
const normalizedUrl = serverUrl.replace(/\/$/, '');
|
|
55
|
+
|
|
56
|
+
try {
|
|
57
|
+
// 1. Try direct connection
|
|
58
|
+
const response = await fetch(`${normalizedUrl}/api/agents`);
|
|
59
|
+
|
|
60
|
+
if (!response.ok) {
|
|
61
|
+
throw new Error(`Failed to fetch agents from ${serverUrl} (Status: ${response.status})`);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const data = await response.json();
|
|
65
|
+
|
|
66
|
+
if (isCancelled) return;
|
|
67
|
+
|
|
68
|
+
setServers((prev) => ({
|
|
69
|
+
...prev,
|
|
70
|
+
[serverUrl]: {
|
|
71
|
+
status: 'success',
|
|
72
|
+
agents: data.agents || [],
|
|
73
|
+
},
|
|
74
|
+
}));
|
|
75
|
+
} catch (directError) {
|
|
76
|
+
// 2. Try proxy through our server
|
|
77
|
+
try {
|
|
78
|
+
// Note: We are using encodeURIComponent to ensure the URL is passed correctly as a parameter
|
|
79
|
+
const proxyUrl = `/agents/${encodeURIComponent(normalizedUrl)}/api/agents`;
|
|
80
|
+
const response = await fetch(proxyUrl);
|
|
81
|
+
|
|
82
|
+
if (!response.ok) {
|
|
83
|
+
throw new Error(`Failed to fetch agents from ${serverUrl} via proxy (Status: ${response.status})`);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const data = await response.json();
|
|
87
|
+
|
|
88
|
+
if (isCancelled) return;
|
|
89
|
+
|
|
90
|
+
setServers((prev) => ({
|
|
91
|
+
...prev,
|
|
92
|
+
[serverUrl]: {
|
|
93
|
+
status: 'success',
|
|
94
|
+
agents: data.agents || [],
|
|
95
|
+
},
|
|
96
|
+
}));
|
|
97
|
+
} catch (proxyError) {
|
|
98
|
+
if (isCancelled) return;
|
|
99
|
+
console.warn(`Failed to load agents from ${serverUrl} (Direct & Proxy)`, directError, proxyError);
|
|
100
|
+
|
|
101
|
+
setServers((prev) => ({
|
|
102
|
+
...prev,
|
|
103
|
+
[serverUrl]: {
|
|
104
|
+
status: 'error',
|
|
105
|
+
agents: [],
|
|
106
|
+
error: proxyError instanceof Error ? proxyError.message : 'Unknown error',
|
|
107
|
+
},
|
|
108
|
+
}));
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
fetchServers();
|
|
114
|
+
|
|
115
|
+
return () => {
|
|
116
|
+
isCancelled = true;
|
|
117
|
+
};
|
|
118
|
+
}, []);
|
|
119
|
+
|
|
120
|
+
if (initialLoading) {
|
|
121
|
+
return (
|
|
122
|
+
<div className="mt-8 flex items-center justify-center py-8 text-sm text-gray-500">
|
|
123
|
+
<span className="mr-2 inline-block h-4 w-4 animate-spin rounded-full border-2 border-blue-500 border-t-transparent" />
|
|
124
|
+
Loading federated agents…
|
|
125
|
+
</div>
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const serverUrls = Object.keys(servers);
|
|
130
|
+
|
|
131
|
+
if (serverUrls.length === 0) {
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return (
|
|
136
|
+
<>
|
|
137
|
+
{serverUrls.map(serverUrl => {
|
|
138
|
+
const state = servers[serverUrl];
|
|
139
|
+
const hostname = (() => {
|
|
140
|
+
try {
|
|
141
|
+
return new URL(serverUrl).hostname;
|
|
142
|
+
} catch {
|
|
143
|
+
return serverUrl;
|
|
144
|
+
}
|
|
145
|
+
})();
|
|
146
|
+
|
|
147
|
+
if (state.status === 'loading') {
|
|
148
|
+
return (
|
|
149
|
+
<Section key={serverUrl} title={`Agents from ${hostname} (...)`}>
|
|
150
|
+
<div className="flex items-center justify-center py-8 text-sm text-gray-500">
|
|
151
|
+
<span className="mr-2 inline-block h-4 w-4 animate-spin rounded-full border-2 border-blue-500 border-t-transparent" />
|
|
152
|
+
Loading...
|
|
153
|
+
</div>
|
|
154
|
+
</Section>
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (state.status === 'error') {
|
|
159
|
+
return (
|
|
160
|
+
<Section key={serverUrl} title={`Agents from ${hostname} (Error)`}>
|
|
161
|
+
<div className="py-4 text-sm text-red-500 text-center">
|
|
162
|
+
Failed to load agents from this server.
|
|
163
|
+
</div>
|
|
164
|
+
</Section>
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
if (state.status === 'success' && state.agents.length > 0) {
|
|
169
|
+
return (
|
|
170
|
+
<Section key={serverUrl} title={`Agents from ${hostname} (${state.agents.length})`}>
|
|
171
|
+
{state.agents.map((agent) => (
|
|
172
|
+
<AgentCard key={agent.url} agent={agent} href={agent.url} />
|
|
173
|
+
))}
|
|
174
|
+
</Section>
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Hide sections with no agents if successfully loaded
|
|
179
|
+
return null;
|
|
180
|
+
})}
|
|
181
|
+
</>
|
|
182
|
+
);
|
|
183
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import Link from 'next/link';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { Section } from './Section';
|
|
4
|
+
import { ModelCard } from './ModelCard';
|
|
5
|
+
|
|
6
|
+
type ModelInfo = {
|
|
7
|
+
modelName: string;
|
|
8
|
+
modelTitle?: string;
|
|
9
|
+
modelDescription?: string;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
type ModelsSectionProps = {
|
|
13
|
+
/**
|
|
14
|
+
* Full list of models to display.
|
|
15
|
+
*/
|
|
16
|
+
models: ReadonlyArray<ModelInfo>;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Optional custom title for the section.
|
|
20
|
+
* Defaults to `Models (X)` where X is the total number of models.
|
|
21
|
+
*/
|
|
22
|
+
title?: string;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Optional maximum number of models to render.
|
|
26
|
+
* If set and there are more models than this number, only the first `maxVisible`
|
|
27
|
+
* models are shown.
|
|
28
|
+
*/
|
|
29
|
+
maxVisible?: number;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* When true and `maxVisible` is set and exceeded, renders a "View all models"
|
|
33
|
+
* card linking to the admin models page.
|
|
34
|
+
*/
|
|
35
|
+
showViewAllLink?: boolean;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export function ModelsSection(props: ModelsSectionProps) {
|
|
39
|
+
const { models, title, maxVisible, showViewAllLink } = props;
|
|
40
|
+
|
|
41
|
+
const totalCount = models.length;
|
|
42
|
+
const visibleModels =
|
|
43
|
+
typeof maxVisible === 'number'
|
|
44
|
+
? models.slice(0, Math.max(0, maxVisible))
|
|
45
|
+
: models;
|
|
46
|
+
|
|
47
|
+
const resolvedTitle = title ?? `Models (${totalCount})`;
|
|
48
|
+
|
|
49
|
+
const hasMore =
|
|
50
|
+
typeof maxVisible === 'number' ? totalCount > maxVisible : false;
|
|
51
|
+
|
|
52
|
+
return (
|
|
53
|
+
<Section title={resolvedTitle}>
|
|
54
|
+
{visibleModels.map(({ modelName, modelTitle, modelDescription }) => (
|
|
55
|
+
<ModelCard
|
|
56
|
+
key={modelName}
|
|
57
|
+
modelName={modelName}
|
|
58
|
+
modelTitle={modelTitle || modelName}
|
|
59
|
+
modelDescription={modelDescription}
|
|
60
|
+
/>
|
|
61
|
+
))}
|
|
62
|
+
|
|
63
|
+
{showViewAllLink && hasMore && (
|
|
64
|
+
<Link href="/admin/models">
|
|
65
|
+
<div className="h-full flex flex-col items-center justify-center rounded-lg border border-dashed border-gray-300 bg-white/60 text-center p-4 hover:border-promptbook-blue-dark hover:bg-white transition-colors cursor-pointer">
|
|
66
|
+
<p className="text-sm font-medium text-gray-900">View all models</p>
|
|
67
|
+
<p className="text-xs text-gray-500 mt-1">
|
|
68
|
+
Showing {visibleModels.length} of {totalCount} models
|
|
69
|
+
</p>
|
|
70
|
+
</div>
|
|
71
|
+
</Link>
|
|
72
|
+
)}
|
|
73
|
+
</Section>
|
|
74
|
+
);
|
|
75
|
+
}
|
|
@@ -1,18 +1,36 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import { usePathname } from 'next/navigation';
|
|
4
|
+
import { AgentBasicInformation } from '../../../../../src/book-2.0/agent-source/AgentBasicInformation';
|
|
5
|
+
import type { UserInfo } from '../../utils/getCurrentUser';
|
|
6
|
+
import { Footer, type FooterLink } from '../Footer/Footer';
|
|
4
7
|
import { Header } from '../Header/Header';
|
|
5
8
|
|
|
6
9
|
type LayoutWrapperProps = {
|
|
7
10
|
children: React.ReactNode;
|
|
8
11
|
isAdmin: boolean;
|
|
12
|
+
currentUser: UserInfo | null;
|
|
9
13
|
serverName: string;
|
|
10
14
|
serverLogoUrl: string | null;
|
|
15
|
+
agents: Array<AgentBasicInformation>;
|
|
16
|
+
isFooterShown: boolean;
|
|
17
|
+
footerLinks: Array<FooterLink>;
|
|
11
18
|
};
|
|
12
19
|
|
|
13
|
-
export function LayoutWrapper({
|
|
20
|
+
export function LayoutWrapper({
|
|
21
|
+
children,
|
|
22
|
+
isAdmin,
|
|
23
|
+
currentUser,
|
|
24
|
+
serverName,
|
|
25
|
+
serverLogoUrl,
|
|
26
|
+
agents,
|
|
27
|
+
isFooterShown,
|
|
28
|
+
footerLinks,
|
|
29
|
+
}: LayoutWrapperProps) {
|
|
14
30
|
const pathname = usePathname();
|
|
15
|
-
const
|
|
31
|
+
const isAdminChatPage =
|
|
32
|
+
pathname?.startsWith('/admin/chat-history') || pathname?.startsWith('/admin/chat-feedback');
|
|
33
|
+
const isHeaderHidden = pathname?.includes('/chat') && !isAdminChatPage;
|
|
16
34
|
|
|
17
35
|
if (isHeaderHidden) {
|
|
18
36
|
return <main className={`pt-0`}>{children}</main>;
|
|
@@ -20,8 +38,15 @@ export function LayoutWrapper({ children, isAdmin, serverName, serverLogoUrl }:
|
|
|
20
38
|
|
|
21
39
|
return (
|
|
22
40
|
<>
|
|
23
|
-
<Header
|
|
41
|
+
<Header
|
|
42
|
+
isAdmin={isAdmin}
|
|
43
|
+
currentUser={currentUser}
|
|
44
|
+
serverName={serverName}
|
|
45
|
+
serverLogoUrl={serverLogoUrl}
|
|
46
|
+
agents={agents}
|
|
47
|
+
/>
|
|
24
48
|
<main className={`pt-[60px]`}>{children}</main>
|
|
49
|
+
{isFooterShown && <Footer extraLinks={footerLinks} />}
|
|
25
50
|
</>
|
|
26
51
|
);
|
|
27
52
|
}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { X } from 'lucide-react';
|
|
4
4
|
import { LoginForm } from '../LoginForm/LoginForm';
|
|
5
|
+
import { Portal } from '../Portal/Portal';
|
|
5
6
|
|
|
6
7
|
type LoginDialogProps = {
|
|
7
8
|
isOpen: boolean;
|
|
@@ -16,25 +17,25 @@ export function LoginDialog(props: LoginDialogProps) {
|
|
|
16
17
|
}
|
|
17
18
|
|
|
18
19
|
return (
|
|
19
|
-
<
|
|
20
|
-
<div className="
|
|
21
|
-
<
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
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>
|
|
28
30
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
</p>
|
|
34
|
-
</div>
|
|
31
|
+
<div className="mb-6">
|
|
32
|
+
<h2 className="text-xl font-semibold text-gray-900">Log in</h2>
|
|
33
|
+
<p className="text-sm text-gray-500 mt-1">Enter your credentials to access the admin area</p>
|
|
34
|
+
</div>
|
|
35
35
|
|
|
36
|
-
|
|
36
|
+
<LoginForm onSuccess={onClose} />
|
|
37
|
+
</div>
|
|
37
38
|
</div>
|
|
38
|
-
</
|
|
39
|
+
</Portal>
|
|
39
40
|
);
|
|
40
41
|
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import type { ReactNode } from 'react';
|
|
4
|
+
import { useEffect, useState } from 'react';
|
|
5
|
+
import { createPortal } from 'react-dom';
|
|
6
|
+
|
|
7
|
+
type PortalProps = {
|
|
8
|
+
children: ReactNode;
|
|
9
|
+
/**
|
|
10
|
+
* Optional container id. Defaults to "portal-root".
|
|
11
|
+
* This allows reusing the Portal component for different layers if needed.
|
|
12
|
+
*/
|
|
13
|
+
containerId?: string;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Generic portal component that renders children into a DOM node
|
|
18
|
+
* outside of the normal React tree (by default #portal-root).
|
|
19
|
+
*
|
|
20
|
+
* This is useful for modals/popups that should visually overlay the
|
|
21
|
+
* whole app, independent of where the triggering component lives.
|
|
22
|
+
*/
|
|
23
|
+
export function Portal(props: PortalProps) {
|
|
24
|
+
const { children, containerId = 'portal-root' } = props;
|
|
25
|
+
const [container, setContainer] = useState<Element | null>(null);
|
|
26
|
+
|
|
27
|
+
useEffect(() => {
|
|
28
|
+
const element = document.getElementById(containerId);
|
|
29
|
+
setContainer(element);
|
|
30
|
+
}, [containerId]);
|
|
31
|
+
|
|
32
|
+
if (!container) {
|
|
33
|
+
// In SSR or before the DOM node exists, render nothing.
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return createPortal(children, container);
|
|
38
|
+
}
|