@promptbook/cli 0.103.0-56 → 0.103.0-66
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/TODO.txt +5 -1
- package/apps/agents-server/package-lock.json +1220 -47
- package/apps/agents-server/package.json +4 -1
- package/apps/agents-server/src/app/actions.ts +3 -1
- package/apps/agents-server/src/app/agents/[agentName]/AgentOptionsMenu.tsx +72 -6
- package/apps/agents-server/src/app/agents/[agentName]/AgentProfileChat.tsx +20 -7
- package/apps/agents-server/src/app/agents/[agentName]/AgentProfileWrapper.tsx +44 -0
- package/apps/agents-server/src/app/agents/[agentName]/generateAgentMetadata.ts +7 -3
- package/apps/agents-server/src/app/agents/[agentName]/layout.tsx +41 -0
- package/apps/agents-server/src/app/agents/[agentName]/page.tsx +47 -100
- package/apps/agents-server/src/app/agents/[agentName]/website-integration/page.tsx +11 -2
- package/apps/agents-server/src/app/embed/page.tsx +2 -2
- package/apps/agents-server/src/app/layout.tsx +8 -24
- package/apps/agents-server/src/app/manifest.ts +8 -3
- package/apps/agents-server/src/components/AgentProfile/AgentProfile.tsx +334 -0
- package/apps/agents-server/src/components/AgentProfile/AgentProfileFromSource.tsx +23 -0
- package/apps/agents-server/src/{app/agents/[agentName] → components/AgentProfile}/AgentQrCode.tsx +8 -1
- package/apps/agents-server/src/components/LayoutWrapper/LayoutWrapper.tsx +7 -6
- package/apps/agents-server/src/database/metadataDefaults.ts +6 -0
- package/esm/index.es.js +65 -10
- package/esm/index.es.js.map +1 -1
- package/esm/typings/src/_packages/components.index.d.ts +2 -2
- package/esm/typings/src/_packages/types.index.d.ts +6 -0
- package/esm/typings/src/book-2.0/agent-source/AgentBasicInformation.d.ts +2 -1
- package/esm/typings/src/book-2.0/agent-source/createCommitmentRegex.d.ts +1 -1
- package/esm/typings/src/book-components/Chat/AgentChat/AgentChat.d.ts +3 -0
- package/esm/typings/src/book-components/Chat/Chat/ChatProps.d.ts +6 -0
- package/esm/typings/src/book-components/PromptbookAgent/PromptbookAgentIntegration.d.ts +52 -0
- package/esm/typings/src/book-components/PromptbookAgent/PromptbookAgentSeamlessIntegration.d.ts +14 -0
- package/esm/typings/src/book-components/icons/SendIcon.d.ts +3 -0
- package/esm/typings/src/commitments/CLOSED/CLOSED.d.ts +4 -0
- package/esm/typings/src/commitments/CLOSED/CLOSED.test.d.ts +4 -0
- package/esm/typings/src/commitments/USE_BROWSER/USE_BROWSER.d.ts +4 -0
- package/esm/typings/src/commitments/_base/BaseCommitmentDefinition.d.ts +6 -0
- package/esm/typings/src/llm-providers/agent/Agent.d.ts +3 -1
- package/esm/typings/src/other/templates/getTemplatesPipelineCollection.d.ts +1 -1
- package/esm/typings/src/types/typeAliases.d.ts +6 -0
- package/esm/typings/src/utils/color/Color.d.ts +1 -1
- package/esm/typings/src/utils/random/$generateBookBoilerplate.d.ts +6 -0
- package/esm/typings/src/utils/random/CzechNamePool.d.ts +7 -0
- package/esm/typings/src/utils/random/EnglishNamePool.d.ts +7 -0
- package/esm/typings/src/utils/random/NamePool.d.ts +17 -0
- package/esm/typings/src/utils/random/getNamePool.d.ts +10 -0
- package/esm/typings/src/version.d.ts +1 -1
- package/package.json +2 -2
- package/umd/index.umd.js +65 -10
- package/umd/index.umd.js.map +1 -1
- package/apps/agents-server/src/app/agents/[agentName]/AgentProfileView.tsx +0 -233
- package/esm/typings/src/book-components/PromptbookAgent/PromptbookAgent.d.ts +0 -29
- /package/apps/agents-server/src/{app/agents/[agentName] → components/AgentProfile}/QrCodeModal.tsx +0 -0
|
@@ -10,7 +10,10 @@
|
|
|
10
10
|
"postinstall": "cd ../../ && npm ci"
|
|
11
11
|
},
|
|
12
12
|
"devDependencies": {
|
|
13
|
-
"@tailwindcss/typography": "^0.5.19"
|
|
13
|
+
"@tailwindcss/typography": "^0.5.19",
|
|
14
|
+
"autoprefixer": "^10.4.21",
|
|
15
|
+
"postcss": "^8.5.6",
|
|
16
|
+
"tailwindcss": "^3.4.18"
|
|
14
17
|
},
|
|
15
18
|
"dependencies": {
|
|
16
19
|
"remark-gfm": "^4.0.1"
|
|
@@ -4,6 +4,7 @@ import { $generateBookBoilerplate } from '@promptbook-local/core';
|
|
|
4
4
|
import { string_agent_name } from '@promptbook-local/types';
|
|
5
5
|
import { revalidatePath } from 'next/cache';
|
|
6
6
|
import { cookies } from 'next/headers';
|
|
7
|
+
import { getMetadata } from '../database/getMetadata';
|
|
7
8
|
import { $provideAgentCollectionForServer } from '../tools/$provideAgentCollectionForServer';
|
|
8
9
|
import { authenticateUser } from '../utils/authenticateUser';
|
|
9
10
|
import { isUserAdmin } from '../utils/isUserAdmin';
|
|
@@ -16,7 +17,8 @@ export async function $createAgentAction(): Promise<string_agent_name> {
|
|
|
16
17
|
}
|
|
17
18
|
|
|
18
19
|
const collection = await $provideAgentCollectionForServer();
|
|
19
|
-
const
|
|
20
|
+
const namePool = (await getMetadata('NAME_POOL')) || 'ENGLISH';
|
|
21
|
+
const agentSource = $generateBookBoilerplate({ namePool });
|
|
20
22
|
|
|
21
23
|
const { agentName } = await collection.createAgent(agentSource);
|
|
22
24
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
+
import { TODO_any } from '@promptbook-local/types';
|
|
3
4
|
import {
|
|
4
5
|
CopyIcon,
|
|
5
6
|
CopyPlusIcon,
|
|
@@ -10,12 +11,25 @@ import {
|
|
|
10
11
|
MessageSquareShareIcon,
|
|
11
12
|
MoreHorizontalIcon,
|
|
12
13
|
QrCodeIcon,
|
|
14
|
+
SmartphoneIcon,
|
|
13
15
|
SquareSplitHorizontalIcon,
|
|
14
16
|
} from 'lucide-react';
|
|
15
|
-
import {
|
|
17
|
+
import { Barlow_Condensed } from 'next/font/google';
|
|
18
|
+
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
16
19
|
import { string_data_url, string_url_image } from '../../../../../../src/types/typeAliases';
|
|
17
20
|
import { getAgentLinks } from './agentLinks';
|
|
18
21
|
|
|
22
|
+
type BeforeInstallPromptEvent = Event & {
|
|
23
|
+
prompt: () => Promise<void>;
|
|
24
|
+
userChoice: Promise<{ outcome: 'accepted' | 'dismissed'; platform: string }>;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const barlowCondensed = Barlow_Condensed({
|
|
28
|
+
subsets: ['latin'],
|
|
29
|
+
weight: ['300', '400', '500', '600', '700'],
|
|
30
|
+
variable: '--font-barlow-condensed',
|
|
31
|
+
});
|
|
32
|
+
|
|
19
33
|
type AgentOptionsMenuProps = {
|
|
20
34
|
agentName: string;
|
|
21
35
|
agentUrl: string;
|
|
@@ -39,6 +53,45 @@ export function AgentOptionsMenu({
|
|
|
39
53
|
const [copyFeedback, setCopyFeedback] = useState<string | null>(null);
|
|
40
54
|
const menuRef = useRef<HTMLDivElement>(null);
|
|
41
55
|
|
|
56
|
+
// PWA Install state
|
|
57
|
+
const [installPromptEvent, setInstallPromptEvent] = useState<BeforeInstallPromptEvent | null>(null);
|
|
58
|
+
const [isInstalled, setIsInstalled] = useState(false);
|
|
59
|
+
|
|
60
|
+
useEffect(() => {
|
|
61
|
+
function handleBeforeInstallPrompt(e: Event) {
|
|
62
|
+
e.preventDefault();
|
|
63
|
+
setInstallPromptEvent(e as BeforeInstallPromptEvent);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function updateInstalledStatus() {
|
|
67
|
+
const mediaMatch = window.matchMedia('(display-mode: standalone)');
|
|
68
|
+
const standalone = mediaMatch.matches || (window.navigator as TODO_any).standalone === true;
|
|
69
|
+
setIsInstalled(standalone);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
window.addEventListener('beforeinstallprompt', handleBeforeInstallPrompt);
|
|
73
|
+
updateInstalledStatus();
|
|
74
|
+
window.matchMedia('(display-mode: standalone)').addEventListener('change', updateInstalledStatus);
|
|
75
|
+
|
|
76
|
+
return () => {
|
|
77
|
+
window.removeEventListener('beforeinstallprompt', handleBeforeInstallPrompt);
|
|
78
|
+
window.matchMedia('(display-mode: standalone)').removeEventListener('change', updateInstalledStatus);
|
|
79
|
+
};
|
|
80
|
+
}, []);
|
|
81
|
+
|
|
82
|
+
const handleInstallApp = useCallback(async () => {
|
|
83
|
+
if (!installPromptEvent) return;
|
|
84
|
+
try {
|
|
85
|
+
installPromptEvent.prompt();
|
|
86
|
+
const choice = await installPromptEvent.userChoice.catch(() => null);
|
|
87
|
+
if (choice?.outcome === 'accepted') {
|
|
88
|
+
setIsInstalled(true);
|
|
89
|
+
}
|
|
90
|
+
} finally {
|
|
91
|
+
setInstallPromptEvent(null);
|
|
92
|
+
}
|
|
93
|
+
}, [installPromptEvent]);
|
|
94
|
+
|
|
42
95
|
useEffect(() => {
|
|
43
96
|
function handleClickOutside(event: MouseEvent) {
|
|
44
97
|
if (menuRef.current && !menuRef.current.contains(event.target as Node)) {
|
|
@@ -123,6 +176,17 @@ export function AgentOptionsMenu({
|
|
|
123
176
|
label: 'Show QR Code',
|
|
124
177
|
onClick: onShowQrCode,
|
|
125
178
|
},
|
|
179
|
+
// Install App - only show if PWA is installable and not already installed
|
|
180
|
+
...(!isInstalled && installPromptEvent
|
|
181
|
+
? [
|
|
182
|
+
{
|
|
183
|
+
type: 'action' as const,
|
|
184
|
+
icon: SmartphoneIcon,
|
|
185
|
+
label: 'Install App',
|
|
186
|
+
onClick: handleInstallApp,
|
|
187
|
+
},
|
|
188
|
+
]
|
|
189
|
+
: []),
|
|
126
190
|
// Admin-only items
|
|
127
191
|
...(isAdmin
|
|
128
192
|
? [
|
|
@@ -162,18 +226,20 @@ export function AgentOptionsMenu({
|
|
|
162
226
|
];
|
|
163
227
|
|
|
164
228
|
return (
|
|
165
|
-
<div ref={menuRef} className="relative">
|
|
229
|
+
<div ref={menuRef} className="relative z-[9999]">
|
|
166
230
|
<button
|
|
167
231
|
onClick={() => setIsOpen(!isOpen)}
|
|
168
|
-
className="p-3 rounded-full
|
|
169
|
-
style={{ backgroundColor: brandColorHex }}
|
|
232
|
+
className="p-3 rounded-full hover:bg-white/30 transition-all duration-200"
|
|
233
|
+
// style={{ backgroundColor: brandColorHex }}
|
|
170
234
|
aria-label="More options"
|
|
171
235
|
>
|
|
172
|
-
<MoreHorizontalIcon className="w-5 h-5 text-
|
|
236
|
+
<MoreHorizontalIcon className="w-5 h-5 text-black" />
|
|
173
237
|
</button>
|
|
174
238
|
|
|
175
239
|
{isOpen && (
|
|
176
|
-
<div
|
|
240
|
+
<div
|
|
241
|
+
className={`absolute right-0 top-full mt-2 w-56 bg-white rounded-xl shadow-2xl border border-gray-100 py-2 z-[9999] animate-in fade-in slide-in-from-top-2 duration-200 ${barlowCondensed.className}`}
|
|
242
|
+
>
|
|
177
243
|
{menuItems.map((item, index) => {
|
|
178
244
|
if (item.type === 'divider') {
|
|
179
245
|
return <div key={index} className="h-px bg-gray-100 my-2" />;
|
|
@@ -1,20 +1,22 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
+
import { usePromise } from '@common/hooks/usePromise';
|
|
3
4
|
import { Chat } from '@promptbook-local/components';
|
|
4
5
|
import { RemoteAgent } from '@promptbook-local/core';
|
|
5
|
-
import spaceTrim from 'spacetrim';
|
|
6
|
-
import { useCallback, useMemo } from 'react';
|
|
7
|
-
import { usePromise } from '@common/hooks/usePromise';
|
|
8
6
|
import { useRouter } from 'next/navigation';
|
|
9
|
-
import {
|
|
7
|
+
import { useCallback, useMemo } from 'react';
|
|
8
|
+
import spaceTrim from 'spacetrim';
|
|
9
|
+
import { string_agent_url, string_color } from '../../../../../../src/types/typeAliases';
|
|
10
10
|
|
|
11
11
|
type AgentProfileChatProps = {
|
|
12
12
|
agentUrl: string_agent_url;
|
|
13
13
|
agentName: string;
|
|
14
14
|
fullname: string;
|
|
15
|
+
brandColorHex: string_color;
|
|
16
|
+
avatarSrc: string;
|
|
15
17
|
};
|
|
16
18
|
|
|
17
|
-
export function AgentProfileChat({ agentUrl, agentName, fullname }: AgentProfileChatProps) {
|
|
19
|
+
export function AgentProfileChat({ agentUrl, agentName, fullname, brandColorHex, avatarSrc }: AgentProfileChatProps) {
|
|
18
20
|
const router = useRouter();
|
|
19
21
|
|
|
20
22
|
const agentPromise = useMemo(
|
|
@@ -56,21 +58,32 @@ export function AgentProfileChat({ agentUrl, agentName, fullname }: AgentProfile
|
|
|
56
58
|
|
|
57
59
|
return (
|
|
58
60
|
<div className="w-full h-[400px] md:h-[500px]">
|
|
59
|
-
|
|
61
|
+
<Chat
|
|
60
62
|
title={`Chat with ${fullname}`}
|
|
63
|
+
participants={[
|
|
64
|
+
{
|
|
65
|
+
name: 'AGENT',
|
|
66
|
+
fullname,
|
|
67
|
+
isMe: false,
|
|
68
|
+
color: brandColorHex,
|
|
69
|
+
avatarSrc,
|
|
70
|
+
// <- TODO: [🧠] Maybe this shouldnt be there
|
|
71
|
+
},
|
|
72
|
+
]}
|
|
61
73
|
messages={[
|
|
62
74
|
{
|
|
63
75
|
from: 'AGENT',
|
|
64
76
|
content: initialMessage,
|
|
65
77
|
date: new Date(),
|
|
66
78
|
id: 'initial-message',
|
|
67
|
-
isComplete: true
|
|
79
|
+
isComplete: true,
|
|
68
80
|
},
|
|
69
81
|
]}
|
|
70
82
|
onMessage={handleMessage}
|
|
71
83
|
isSaveButtonEnabled={false}
|
|
72
84
|
isCopyButtonEnabled={false}
|
|
73
85
|
className="bg-transparent"
|
|
86
|
+
buttonColor={brandColorHex}
|
|
74
87
|
style={{ background: 'transparent' }}
|
|
75
88
|
/>
|
|
76
89
|
</div>
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { AgentBasicInformation } from '@promptbook-local/types';
|
|
4
|
+
import { AgentProfile } from '../../../components/AgentProfile/AgentProfile';
|
|
5
|
+
import { AgentOptionsMenu } from './AgentOptionsMenu';
|
|
6
|
+
|
|
7
|
+
type AgentProfileWrapperProps = {
|
|
8
|
+
agent: AgentBasicInformation;
|
|
9
|
+
agentUrl: string;
|
|
10
|
+
agentEmail: string;
|
|
11
|
+
agentName: string;
|
|
12
|
+
brandColorHex: string;
|
|
13
|
+
isAdmin: boolean;
|
|
14
|
+
isHeadless: boolean;
|
|
15
|
+
actions: React.ReactNode;
|
|
16
|
+
children: React.ReactNode;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export function AgentProfileWrapper(props: AgentProfileWrapperProps) {
|
|
20
|
+
const { agent, agentUrl, agentEmail, agentName, brandColorHex, isAdmin, isHeadless, actions, children } = props;
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
<AgentProfile
|
|
24
|
+
agent={agent}
|
|
25
|
+
agentUrl={agentUrl}
|
|
26
|
+
agentEmail={agentEmail}
|
|
27
|
+
isHeadless={isHeadless}
|
|
28
|
+
renderMenu={({ onShowQrCode }) => (
|
|
29
|
+
<AgentOptionsMenu
|
|
30
|
+
agentName={agentName}
|
|
31
|
+
agentUrl={agentUrl}
|
|
32
|
+
agentEmail={agentEmail}
|
|
33
|
+
brandColorHex={brandColorHex}
|
|
34
|
+
isAdmin={isAdmin}
|
|
35
|
+
onShowQrCode={onShowQrCode}
|
|
36
|
+
backgroundImage="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAA1JREFUGFdjYJh39z8ABJgCe/ZvAS4AAAAASUVORK5CYII="
|
|
37
|
+
/>
|
|
38
|
+
)}
|
|
39
|
+
actions={actions}
|
|
40
|
+
>
|
|
41
|
+
{children}
|
|
42
|
+
</AgentProfile>
|
|
43
|
+
);
|
|
44
|
+
}
|
|
@@ -12,14 +12,18 @@ export async function generateAgentMetadata({ params }: { params: Promise<{ agen
|
|
|
12
12
|
const title = agentProfile.meta.fullname || agentProfile.agentName;
|
|
13
13
|
const description = agentProfile.meta.description || agentProfile.personaDescription || undefined;
|
|
14
14
|
|
|
15
|
-
//
|
|
16
|
-
const
|
|
15
|
+
// Use the agent's icon-256.png as the favicon
|
|
16
|
+
const iconUrl = `/agents/${encodeURIComponent(agentName)}/images/icon-256.png`;
|
|
17
17
|
|
|
18
18
|
const metadata = {
|
|
19
19
|
metadataBase: publicUrl,
|
|
20
20
|
title,
|
|
21
21
|
description,
|
|
22
|
-
icons:
|
|
22
|
+
icons: {
|
|
23
|
+
icon: iconUrl,
|
|
24
|
+
shortcut: iconUrl,
|
|
25
|
+
apple: iconUrl,
|
|
26
|
+
},
|
|
23
27
|
openGraph: {
|
|
24
28
|
title,
|
|
25
29
|
description,
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
'use server';
|
|
2
|
+
|
|
3
|
+
import type { Metadata } from 'next';
|
|
4
|
+
import { getAgentName, getAgentProfile } from './_utils';
|
|
5
|
+
|
|
6
|
+
export async function generateMetadata({ params }: { params: Promise<{ agentName: string }> }): Promise<Metadata> {
|
|
7
|
+
const agentName = await getAgentName(params);
|
|
8
|
+
|
|
9
|
+
try {
|
|
10
|
+
const agentProfile = await getAgentProfile(agentName);
|
|
11
|
+
|
|
12
|
+
const title = agentProfile.meta.fullname || agentProfile.agentName;
|
|
13
|
+
const description = agentProfile.meta.description || agentProfile.personaDescription || undefined;
|
|
14
|
+
|
|
15
|
+
// Use the agent's icon-256.png as the favicon for all agent pages and subpages
|
|
16
|
+
const iconUrl = `/agents/${encodeURIComponent(agentName)}/images/icon-256.png`;
|
|
17
|
+
|
|
18
|
+
return {
|
|
19
|
+
title,
|
|
20
|
+
description,
|
|
21
|
+
icons: {
|
|
22
|
+
icon: iconUrl,
|
|
23
|
+
shortcut: iconUrl,
|
|
24
|
+
apple: iconUrl,
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
} catch (error) {
|
|
28
|
+
console.warn(`Failed to generate metadata for agent ${agentName}`, error);
|
|
29
|
+
return {
|
|
30
|
+
title: agentName,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export default async function AgentLayout({
|
|
36
|
+
children,
|
|
37
|
+
}: Readonly<{
|
|
38
|
+
children: React.ReactNode;
|
|
39
|
+
}>) {
|
|
40
|
+
return <>{children}</>;
|
|
41
|
+
}
|
|
@@ -2,22 +2,30 @@
|
|
|
2
2
|
|
|
3
3
|
import { $provideServer } from '@/src/tools/$provideServer';
|
|
4
4
|
import { isUserAdmin } from '@/src/utils/isUserAdmin';
|
|
5
|
+
import { saturate } from '@promptbook-local/color';
|
|
5
6
|
import { PROMPTBOOK_COLOR } from '@promptbook-local/core';
|
|
6
7
|
import { notFound } from 'next/navigation';
|
|
7
|
-
import spaceTrim from 'spacetrim';
|
|
8
8
|
import { Color } from '../../../../../../src/utils/color/Color';
|
|
9
|
-
import { darken } from '../../../../../../src/utils/color/operators/darken';
|
|
10
|
-
import { lighten } from '../../../../../../src/utils/color/operators/lighten';
|
|
11
9
|
import { getAgentName, getAgentProfile } from './_utils';
|
|
12
|
-
import {
|
|
10
|
+
import { getAgentLinks } from './agentLinks';
|
|
11
|
+
import { AgentProfileChat } from './AgentProfileChat';
|
|
12
|
+
import { AgentProfileWrapper } from './AgentProfileWrapper';
|
|
13
13
|
import { generateAgentMetadata } from './generateAgentMetadata';
|
|
14
14
|
import { ServiceWorkerRegister } from './ServiceWorkerRegister';
|
|
15
15
|
|
|
16
16
|
export const generateMetadata = generateAgentMetadata;
|
|
17
17
|
|
|
18
|
-
export default async function AgentPage({
|
|
18
|
+
export default async function AgentPage({
|
|
19
|
+
params,
|
|
20
|
+
searchParams,
|
|
21
|
+
}: {
|
|
22
|
+
params: Promise<{ agentName: string }>;
|
|
23
|
+
searchParams: Promise<{ headless?: string }>;
|
|
24
|
+
}) {
|
|
19
25
|
const agentName = await getAgentName(params);
|
|
20
26
|
const isAdmin = await isUserAdmin();
|
|
27
|
+
const { headless: headlessParam } = await searchParams;
|
|
28
|
+
const isHeadless = headlessParam !== undefined;
|
|
21
29
|
|
|
22
30
|
let agentProfile;
|
|
23
31
|
try {
|
|
@@ -42,111 +50,50 @@ export default async function AgentPage({ params }: { params: Promise<{ agentNam
|
|
|
42
50
|
|
|
43
51
|
const agentEmail = `${agentName}@${publicUrl.hostname}`;
|
|
44
52
|
|
|
45
|
-
|
|
46
|
-
const
|
|
47
|
-
const brandColors = brandColorString.split(',').map((c) => Color.fromSafe(c.trim()));
|
|
48
|
-
|
|
49
|
-
// Ensure at least one color
|
|
50
|
-
if (brandColors.length === 0) {
|
|
51
|
-
brandColors.push(PROMPTBOOK_COLOR);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
const brandColor = brandColors[0]!;
|
|
55
|
-
const brandColorHex = brandColor.toHex();
|
|
56
|
-
const brandColorLightHex = brandColor.then(lighten(0.2)).toHex();
|
|
57
|
-
const brandColorDarkHex = brandColor.then(darken(0.15)).toHex();
|
|
58
|
-
const brandColorsHex = brandColors.map((c) => c.toHex());
|
|
59
|
-
|
|
60
|
-
// Generate Noisy SVG Background
|
|
61
|
-
const color1 = brandColors[0]!;
|
|
62
|
-
const color2 = brandColors[1] || brandColors[0]!; // Use secondary color or fallback to primary
|
|
63
|
-
|
|
64
|
-
// [🧠] Make colors much lighter for the background as per feedback
|
|
65
|
-
const color1Light = color1.then(lighten(0.3)).toHex();
|
|
66
|
-
const color1Main = color1.toHex();
|
|
67
|
-
const color1Dark = color1.then(darken(0.3)).toHex();
|
|
68
|
-
|
|
69
|
-
const color2Light = color2.then(lighten(0.3)).toHex();
|
|
70
|
-
const color2Main = color2.toHex();
|
|
71
|
-
const color2Dark = color2.then(darken(0.3)).toHex();
|
|
72
|
-
|
|
73
|
-
const svgContent = spaceTrim(`
|
|
74
|
-
<svg xmlns="http://www.w3.org/2000/svg"
|
|
75
|
-
viewBox="0 0 1920 1080"
|
|
76
|
-
width="1920" height="1080"
|
|
77
|
-
preserveAspectRatio="xMidYMid slice">
|
|
78
|
-
<defs>
|
|
79
|
-
<!-- Bottom-left -->
|
|
80
|
-
<radialGradient id="grad1" cx="0%" cy="100%" r="90%">
|
|
81
|
-
<stop offset="0%" stop-color="${color1Light}" />
|
|
82
|
-
<stop offset="50%" stop-color="${color1Main}" />
|
|
83
|
-
<stop offset="100%" stop-color="${color1Dark}" />
|
|
84
|
-
</radialGradient>
|
|
85
|
-
|
|
86
|
-
<!-- Bottom-right -->
|
|
87
|
-
<radialGradient id="grad2" cx="100%" cy="100%" r="90%">
|
|
88
|
-
<stop offset="0%" stop-color="${color2Light}" />
|
|
89
|
-
<stop offset="50%" stop-color="${color2Main}" />
|
|
90
|
-
<stop offset="100%" stop-color="${color2Dark}" />
|
|
91
|
-
</radialGradient>
|
|
92
|
-
|
|
93
|
-
<!-- White top fade -->
|
|
94
|
-
<linearGradient id="whiteTopGrad" x1="0%" y1="0%" x2="0%" y2="100%">
|
|
95
|
-
<stop offset="0%" stop-color="#ffffff" stop-opacity="1" />
|
|
96
|
-
<stop offset="100%" stop-color="#ffffff" stop-opacity="0.3" />
|
|
97
|
-
</linearGradient>
|
|
98
|
-
|
|
99
|
-
<!-- Strong grain -->
|
|
100
|
-
<filter id="grain" x="-10%" y="-10%" width="120%" height="120%">
|
|
101
|
-
<feTurbulence type="fractalNoise" baseFrequency="3.5" numOctaves="3" seed="8" result="noise" />
|
|
102
|
-
<feComponentTransfer>
|
|
103
|
-
<feFuncR type="linear" slope="3.5" intercept="-1.2" />
|
|
104
|
-
<feFuncG type="linear" slope="3.5" intercept="-1.2" />
|
|
105
|
-
<feFuncB type="linear" slope="3.5" intercept="-1.2" />
|
|
106
|
-
<feFuncA type="table" tableValues="0 0.8" />
|
|
107
|
-
</feComponentTransfer>
|
|
108
|
-
</filter>
|
|
109
|
-
</defs>
|
|
110
|
-
|
|
111
|
-
<!-- White base -->
|
|
112
|
-
<rect width="100%" height="100%" fill="#ffffff" />
|
|
113
|
-
|
|
114
|
-
<!-- Gradients -->
|
|
115
|
-
<rect width="100%" height="100%" fill="url(#grad1)" />
|
|
116
|
-
<rect width="100%" height="100%" fill="url(#grad2)" style="mix-blend-mode:screen; opacity:0.85" />
|
|
117
|
-
|
|
118
|
-
<!-- White fade on top -->
|
|
119
|
-
<rect width="100%" height="100%" fill="url(#whiteTopGrad)" />
|
|
120
|
-
|
|
121
|
-
<!-- Strong visible noise -->
|
|
122
|
-
<rect width="100%" height="100%" filter="url(#grain)"
|
|
123
|
-
style="mix-blend-mode:soft-light; opacity:1.2" />
|
|
124
|
-
</svg>
|
|
125
|
-
`);
|
|
126
|
-
|
|
127
|
-
const backgroundImage = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svgContent)}`;
|
|
53
|
+
const brandColor = Color.fromSafe(agentProfile.meta.color || PROMPTBOOK_COLOR);
|
|
54
|
+
const brandColorHex = brandColor.then(saturate(-0.5)).toHex();
|
|
128
55
|
|
|
129
56
|
const fullname = (agentProfile.meta.fullname || agentProfile.agentName || 'Agent') as string;
|
|
130
|
-
const imageUrl = (agentProfile.meta.image as string) || null;
|
|
131
57
|
|
|
132
58
|
return (
|
|
133
59
|
<>
|
|
134
60
|
<ServiceWorkerRegister scope={`/agents/${encodeURIComponent(agentName)}/`} />
|
|
135
|
-
<
|
|
136
|
-
|
|
137
|
-
fullname={fullname}
|
|
138
|
-
personaDescription={agentProfile.personaDescription || ''}
|
|
139
|
-
imageUrl={imageUrl}
|
|
61
|
+
<AgentProfileWrapper
|
|
62
|
+
agent={agentProfile}
|
|
140
63
|
agentUrl={agentUrl}
|
|
141
64
|
agentEmail={agentEmail}
|
|
65
|
+
agentName={agentName}
|
|
142
66
|
brandColorHex={brandColorHex}
|
|
143
|
-
brandColorLightHex={brandColorLightHex}
|
|
144
|
-
brandColorDarkHex={brandColorDarkHex}
|
|
145
|
-
brandColorsHex={brandColorsHex}
|
|
146
|
-
backgroundImage={backgroundImage}
|
|
147
|
-
meta={agentProfile.meta}
|
|
148
67
|
isAdmin={isAdmin}
|
|
149
|
-
|
|
68
|
+
isHeadless={isHeadless}
|
|
69
|
+
actions={
|
|
70
|
+
<>
|
|
71
|
+
{getAgentLinks(agentName)
|
|
72
|
+
.filter((link) => ['Edit Book', 'Integration', 'All Links'].includes(link.title))
|
|
73
|
+
.map((link) => (
|
|
74
|
+
<a
|
|
75
|
+
key={link.href}
|
|
76
|
+
href={link.href}
|
|
77
|
+
className="flex items-center gap-2 text-gray-600 hover:text-gray-900 transition-colors group"
|
|
78
|
+
title={link.title}
|
|
79
|
+
>
|
|
80
|
+
<div className="p-2 rounded-full bg-white/40 group-hover:bg-white/60 transition-colors shadow-sm">
|
|
81
|
+
<link.icon className="w-5 h-5" />
|
|
82
|
+
</div>
|
|
83
|
+
<span className="font-medium text-sm">{link.title}</span>
|
|
84
|
+
</a>
|
|
85
|
+
))}
|
|
86
|
+
</>
|
|
87
|
+
}
|
|
88
|
+
>
|
|
89
|
+
<AgentProfileChat
|
|
90
|
+
agentUrl={agentUrl}
|
|
91
|
+
agentName={agentName}
|
|
92
|
+
fullname={fullname}
|
|
93
|
+
brandColorHex={brandColorHex}
|
|
94
|
+
avatarSrc={agentProfile.meta.image!}
|
|
95
|
+
/>
|
|
96
|
+
</AgentProfileWrapper>
|
|
150
97
|
</>
|
|
151
98
|
);
|
|
152
99
|
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { $provideAgentCollectionForServer } from '@/src/tools/$provideAgentCollectionForServer';
|
|
4
4
|
import { $provideServer } from '@/src/tools/$provideServer';
|
|
5
|
-
import {
|
|
5
|
+
import { PromptbookAgentIntegration } from '@promptbook-local/components';
|
|
6
6
|
import { parseAgentSource } from '@promptbook-local/core';
|
|
7
7
|
import { headers } from 'next/headers';
|
|
8
8
|
import spaceTrim from 'spacetrim';
|
|
@@ -50,7 +50,16 @@ export default async function WebsiteIntegrationAgentPage({ params }: { params:
|
|
|
50
50
|
</p>
|
|
51
51
|
|
|
52
52
|
<CodePreview code={code} />
|
|
53
|
-
<
|
|
53
|
+
<PromptbookAgentIntegration
|
|
54
|
+
formfactor="profile"
|
|
55
|
+
agentUrl={agentUrl}
|
|
56
|
+
meta={meta}
|
|
57
|
+
style={{
|
|
58
|
+
width: '400px',
|
|
59
|
+
height: '600px',
|
|
60
|
+
// outline: `2px solid red`
|
|
61
|
+
}}
|
|
62
|
+
/>
|
|
54
63
|
</main>
|
|
55
64
|
);
|
|
56
65
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { PromptbookAgentIntegration } from '@promptbook-local/components';
|
|
4
4
|
import { useSearchParams } from 'next/navigation';
|
|
5
5
|
|
|
6
6
|
export default function EmbedPage() {
|
|
@@ -13,7 +13,7 @@ export default function EmbedPage() {
|
|
|
13
13
|
|
|
14
14
|
return (
|
|
15
15
|
<div className="w-full h-full bg-transparent">
|
|
16
|
-
<
|
|
16
|
+
<PromptbookAgentIntegration
|
|
17
17
|
agentUrl={agentUrl}
|
|
18
18
|
onOpenChange={(isOpen) => {
|
|
19
19
|
window.parent.postMessage({ type: 'PROMPTBOOK_AGENT_RESIZE', isOpen }, '*');
|
|
@@ -5,8 +5,8 @@ import { Barlow_Condensed, Poppins } from 'next/font/google';
|
|
|
5
5
|
import { getMetadata } from '../database/getMetadata';
|
|
6
6
|
import { $provideAgentCollectionForServer } from '../tools/$provideAgentCollectionForServer';
|
|
7
7
|
import { $provideServer } from '../tools/$provideServer';
|
|
8
|
-
import { isUserAdmin } from '../utils/isUserAdmin';
|
|
9
8
|
import { getCurrentUser } from '../utils/getCurrentUser';
|
|
9
|
+
import { isUserAdmin } from '../utils/isUserAdmin';
|
|
10
10
|
import './globals.css';
|
|
11
11
|
|
|
12
12
|
const barlowCondensed = Barlow_Condensed({
|
|
@@ -25,12 +25,18 @@ export async function generateMetadata(): Promise<Metadata> {
|
|
|
25
25
|
const { publicUrl } = await $provideServer();
|
|
26
26
|
const serverName = (await getMetadata('SERVER_NAME')) || 'Promptbook Agents Server';
|
|
27
27
|
const serverDescription = (await getMetadata('SERVER_DESCRIPTION')) || 'Agents server powered by Promptbook';
|
|
28
|
+
const serverFaviconUrl = (await getMetadata('SERVER_FAVICON_URL')) || faviconLogoImage.src;
|
|
28
29
|
|
|
29
30
|
return {
|
|
30
31
|
title: serverName,
|
|
31
32
|
description: serverDescription,
|
|
32
33
|
// TODO: keywords: ['@@@'],
|
|
33
34
|
authors: [{ name: 'Promptbook Team' }],
|
|
35
|
+
icons: {
|
|
36
|
+
icon: serverFaviconUrl,
|
|
37
|
+
shortcut: serverFaviconUrl,
|
|
38
|
+
apple: serverFaviconUrl,
|
|
39
|
+
},
|
|
34
40
|
openGraph: {
|
|
35
41
|
title: serverName,
|
|
36
42
|
description: serverDescription,
|
|
@@ -66,7 +72,6 @@ export default async function RootLayout({
|
|
|
66
72
|
const currentUser = await getCurrentUser();
|
|
67
73
|
const serverName = (await getMetadata('SERVER_NAME')) || 'Promptbook Agents Server';
|
|
68
74
|
const serverLogoUrl = (await getMetadata('SERVER_LOGO_URL')) || null;
|
|
69
|
-
const serverFaviconUrl = (await getMetadata('SERVER_FAVICON_URL')) || faviconLogoImage.src;
|
|
70
75
|
const isFooterShown = ((await getMetadata('IS_FOOTER_SHOWN')) || 'true') === 'true';
|
|
71
76
|
|
|
72
77
|
let footerLinks = [];
|
|
@@ -82,28 +87,7 @@ export default async function RootLayout({
|
|
|
82
87
|
|
|
83
88
|
return (
|
|
84
89
|
<html lang="en">
|
|
85
|
-
|
|
86
|
-
{/* Favicon for light mode */}
|
|
87
|
-
{/*
|
|
88
|
-
<link
|
|
89
|
-
rel="icon"
|
|
90
|
-
href="https://www.ptbk.io/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Flogo-blue-transparent-256.493b7e49.png&w=64&q=75"
|
|
91
|
-
media="(prefers-color-scheme: light)"
|
|
92
|
-
type="image/svg+xml"
|
|
93
|
-
/>
|
|
94
|
-
*/}
|
|
95
|
-
{/* Favicon for dark mode */}
|
|
96
|
-
{/*
|
|
97
|
-
<link
|
|
98
|
-
rel="icon"
|
|
99
|
-
href="https://www.ptbk.io/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Flogo-blue-transparent-256.493b7e49.png&w=64&q=75"
|
|
100
|
-
media="(prefers-color-scheme: dark)"
|
|
101
|
-
type="image/svg+xml"
|
|
102
|
-
/>
|
|
103
|
-
*/}
|
|
104
|
-
{/* Default favicon as a fallback */}
|
|
105
|
-
<link rel="icon" href={serverFaviconUrl} type="image/x-icon" />
|
|
106
|
-
</head>
|
|
90
|
+
{/* Note: Icon is set via metadata to allow agent-page specific icons to override it */}
|
|
107
91
|
<body className={`${barlowCondensed.variable} ${poppins.variable} antialiased bg-white text-gray-900`}>
|
|
108
92
|
<LayoutWrapper
|
|
109
93
|
isAdmin={isAdmin}
|
|
@@ -6,6 +6,9 @@ import { getMetadata } from '../database/getMetadata';
|
|
|
6
6
|
import { $provideServer } from '../tools/$provideServer';
|
|
7
7
|
import { getAgentProfile } from './agents/[agentName]/_utils';
|
|
8
8
|
|
|
9
|
+
/**
|
|
10
|
+
* Manifest for PWA Progressive Web App
|
|
11
|
+
*/
|
|
9
12
|
export default async function manifest(): Promise<MetadataRoute.Manifest> {
|
|
10
13
|
const { publicUrl } = await $provideServer();
|
|
11
14
|
const serverName = (await getMetadata('SERVER_NAME')) || 'Promptbook Agents Server';
|
|
@@ -19,7 +22,7 @@ export default async function manifest(): Promise<MetadataRoute.Manifest> {
|
|
|
19
22
|
name: serverName,
|
|
20
23
|
short_name: serverName,
|
|
21
24
|
description: serverDescription,
|
|
22
|
-
start_url: publicUrl.href,
|
|
25
|
+
start_url: publicUrl.href + '?utm_source=pwa&utm_medium=install&utm_campaign=agents_server_app',
|
|
23
26
|
display_override: ['fullscreen', 'minimal-ui'],
|
|
24
27
|
display: 'standalone',
|
|
25
28
|
background_color: PROMPTBOOK_COLOR.toHex(),
|
|
@@ -84,7 +87,7 @@ export default async function manifest(): Promise<MetadataRoute.Manifest> {
|
|
|
84
87
|
name,
|
|
85
88
|
short_name,
|
|
86
89
|
description,
|
|
87
|
-
start_url: `${agentUrl}
|
|
90
|
+
start_url: `${agentUrl}?headless&utm_source=pwa&utm_medium=install&utm_campaign=agent_app`,
|
|
88
91
|
scope: agentUrl,
|
|
89
92
|
display_override: ['fullscreen', 'minimal-ui'],
|
|
90
93
|
display: 'standalone',
|
|
@@ -98,7 +101,9 @@ export default async function manifest(): Promise<MetadataRoute.Manifest> {
|
|
|
98
101
|
return {
|
|
99
102
|
name: agentName,
|
|
100
103
|
short_name: agentName,
|
|
101
|
-
start_url: `${publicUrl.href}agents/${encodeURIComponent(
|
|
104
|
+
start_url: `${publicUrl.href}agents/${encodeURIComponent(
|
|
105
|
+
agentName,
|
|
106
|
+
)}?headless&utm_source=pwa&utm_medium=install&utm_campaign=agent_app`,
|
|
102
107
|
display_override: ['fullscreen', 'minimal-ui'],
|
|
103
108
|
display: 'standalone',
|
|
104
109
|
background_color: '#ffffff',
|