@promptbook/cli 0.104.0-1 → 0.104.0-10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/apps/agents-server/config.ts +1 -3
- package/apps/agents-server/next.config.ts +2 -2
- package/apps/agents-server/package.json +7 -3
- package/apps/agents-server/public/fonts/OpenMoji-color-cbdt.woff2 +0 -0
- package/apps/agents-server/public/swagger.json +115 -0
- package/apps/agents-server/scripts/generate-reserved-paths/generate-reserved-paths.ts +54 -0
- package/apps/agents-server/scripts/generate-reserved-paths/tsconfig.json +19 -0
- package/apps/agents-server/src/app/AddAgentButton.tsx +47 -21
- package/apps/agents-server/src/app/actions.ts +22 -5
- package/apps/agents-server/src/app/admin/browser-test/BrowserTestClient.tsx +211 -0
- package/apps/agents-server/src/app/admin/browser-test/page.tsx +13 -0
- package/apps/agents-server/src/app/admin/chat-feedback/ChatFeedbackClient.tsx +221 -274
- package/apps/agents-server/src/app/admin/chat-history/ChatHistoryClient.tsx +94 -137
- package/apps/agents-server/src/app/admin/messages/MessagesClient.tsx +294 -0
- package/apps/agents-server/src/app/admin/messages/page.tsx +13 -0
- package/apps/agents-server/src/app/admin/messages/send-email/SendEmailClient.tsx +104 -0
- package/apps/agents-server/src/app/admin/messages/send-email/actions.ts +35 -0
- package/apps/agents-server/src/app/admin/messages/send-email/page.tsx +13 -0
- package/apps/agents-server/src/app/admin/metadata/MetadataClient.tsx +23 -19
- package/apps/agents-server/src/app/agents/[agentName]/AgentChatWrapper.tsx +15 -1
- package/apps/agents-server/src/app/agents/[agentName]/AgentOptionsMenu.tsx +51 -9
- package/apps/agents-server/src/app/agents/[agentName]/AgentProfileChat.tsx +47 -4
- package/apps/agents-server/src/app/agents/[agentName]/AgentProfileWrapper.tsx +53 -11
- package/apps/agents-server/src/app/agents/[agentName]/_utils.ts +23 -3
- package/apps/agents-server/src/app/agents/[agentName]/agentLinks.tsx +8 -8
- package/apps/agents-server/src/app/agents/[agentName]/api/agents/route.ts +17 -26
- package/apps/agents-server/src/app/agents/[agentName]/api/book/route.ts +4 -2
- package/apps/agents-server/src/app/agents/[agentName]/api/chat/route.ts +20 -0
- package/apps/agents-server/src/app/agents/[agentName]/api/mcp/route.ts +6 -11
- package/apps/agents-server/src/app/agents/[agentName]/api/profile/route.ts +5 -1
- package/apps/agents-server/src/app/agents/[agentName]/api/voice/route.ts +5 -2
- package/apps/agents-server/src/app/agents/[agentName]/book/BookEditorWrapper.tsx +20 -16
- package/apps/agents-server/src/app/agents/[agentName]/book/page.tsx +15 -2
- package/apps/agents-server/src/app/agents/[agentName]/book+chat/page.tsx +15 -2
- package/apps/agents-server/src/app/agents/[agentName]/chat/page.tsx +12 -0
- package/apps/agents-server/src/app/agents/[agentName]/code/api/route.ts +68 -0
- package/apps/agents-server/src/app/agents/[agentName]/code/page.tsx +223 -0
- package/apps/agents-server/src/app/agents/[agentName]/generateAgentMetadata.ts +5 -0
- package/apps/agents-server/src/app/agents/[agentName]/history/actions.ts +2 -2
- package/apps/agents-server/src/app/agents/[agentName]/history/page.tsx +10 -3
- package/apps/agents-server/src/app/agents/[agentName]/images/default-avatar.png/getAgentDefaultAvatarPrompt.ts +31 -0
- package/apps/agents-server/src/app/agents/[agentName]/images/default-avatar.png/route.ts +194 -0
- package/apps/agents-server/src/app/agents/[agentName]/images/icon-256.png/route.tsx +14 -2
- package/apps/agents-server/src/app/agents/[agentName]/images/page.tsx +200 -0
- package/apps/agents-server/src/app/agents/[agentName]/images/screenshot-fullhd.png/route.tsx +4 -3
- package/apps/agents-server/src/app/agents/[agentName]/images/screenshot-phone.png/route.tsx +4 -3
- package/apps/agents-server/src/app/agents/[agentName]/integration/page.tsx +10 -3
- package/apps/agents-server/src/app/agents/[agentName]/links/page.tsx +11 -4
- package/apps/agents-server/src/app/agents/[agentName]/opengraph-image.tsx +11 -2
- package/apps/agents-server/src/app/agents/[agentName]/page.tsx +18 -10
- package/apps/agents-server/src/app/agents/[agentName]/system-message/page.tsx +100 -0
- package/apps/agents-server/src/app/api/admin-email/route.ts +12 -0
- package/apps/agents-server/src/app/api/agents/[agentName]/clone/route.ts +13 -14
- package/apps/agents-server/src/app/api/agents/[agentName]/restore/route.ts +20 -0
- package/apps/agents-server/src/app/api/agents/[agentName]/route.ts +43 -1
- package/apps/agents-server/src/app/api/agents/route.ts +28 -3
- package/apps/agents-server/src/app/api/api-tokens/route.ts +6 -7
- package/apps/agents-server/src/app/api/browser-test/act/route.ts +141 -0
- package/apps/agents-server/src/app/api/browser-test/screenshot/route.ts +30 -0
- package/apps/agents-server/src/app/api/browser-test/scroll-facebook/route.ts +62 -0
- package/apps/agents-server/src/app/api/docs/book.md/route.ts +61 -0
- package/apps/agents-server/src/app/api/emails/incoming/sendgrid/route.ts +48 -0
- package/apps/agents-server/src/app/api/federated-agents/route.ts +12 -0
- package/apps/agents-server/src/app/api/images/[filename]/route.ts +107 -0
- package/apps/agents-server/src/app/api/messages/route.ts +102 -0
- package/apps/agents-server/src/app/api/metadata/route.ts +5 -6
- package/apps/agents-server/src/app/api/upload/route.ts +128 -45
- package/apps/agents-server/src/app/docs/[docId]/page.tsx +2 -3
- package/apps/agents-server/src/app/docs/page.tsx +12 -12
- package/apps/agents-server/src/app/globals.css +140 -33
- package/apps/agents-server/src/app/humans.txt/route.ts +1 -1
- package/apps/agents-server/src/app/layout.tsx +27 -22
- package/apps/agents-server/src/app/page.tsx +54 -6
- package/apps/agents-server/src/app/recycle-bin/actions.ts +20 -14
- package/apps/agents-server/src/app/recycle-bin/page.tsx +27 -41
- package/apps/agents-server/src/app/robots.txt/route.ts +1 -1
- package/apps/agents-server/src/app/security.txt/route.ts +1 -1
- package/apps/agents-server/src/app/sitemap.xml/route.ts +9 -7
- package/apps/agents-server/src/app/swagger/page.tsx +14 -0
- package/apps/agents-server/src/components/AgentProfile/AgentProfile.tsx +41 -116
- package/apps/agents-server/src/components/AgentProfile/AgentProfileImage.tsx +92 -0
- package/apps/agents-server/src/components/AgentProfile/QrCodeModal.tsx +0 -1
- package/apps/agents-server/src/components/AgentProfile/useAgentBackground.ts +97 -0
- package/apps/agents-server/src/components/Auth/AuthControls.tsx +5 -4
- package/apps/agents-server/src/components/DeletedAgentBanner.tsx +26 -0
- package/apps/agents-server/src/components/DocsToolbar/DocsToolbar.tsx +38 -0
- package/apps/agents-server/src/components/DocumentationContent/DocumentationContent.tsx +11 -9
- package/apps/agents-server/src/components/Footer/Footer.tsx +5 -5
- package/apps/agents-server/src/components/ForgottenPasswordDialog/ForgottenPasswordDialog.tsx +61 -0
- package/apps/agents-server/src/components/Header/Header.tsx +114 -40
- package/apps/agents-server/src/components/Homepage/AgentCard.tsx +145 -23
- package/apps/agents-server/src/components/Homepage/AgentsList.tsx +93 -15
- package/apps/agents-server/src/components/Homepage/DeletedAgentsList.tsx +66 -0
- package/apps/agents-server/src/components/Homepage/ExternalAgentsSection.tsx +12 -3
- package/apps/agents-server/src/components/Homepage/ExternalAgentsSectionClient.tsx +19 -10
- package/apps/agents-server/src/components/LayoutWrapper/LayoutWrapper.tsx +3 -2
- package/apps/agents-server/src/components/LoginForm/LoginForm.tsx +50 -1
- package/apps/agents-server/src/components/NewAgentDialog/NewAgentDialog.tsx +88 -0
- package/apps/agents-server/src/components/NotFoundPage/NotFoundPage.tsx +7 -2
- package/apps/agents-server/src/components/OpenMojiIcon/OpenMojiIcon.tsx +16 -7
- package/apps/agents-server/src/components/PrintHeader/PrintHeader.tsx +4 -4
- package/apps/agents-server/src/components/RegisterUserDialog/RegisterUserDialog.tsx +61 -0
- package/apps/agents-server/src/components/VercelDeploymentCard/VercelDeploymentCard.tsx +2 -0
- package/apps/agents-server/src/components/_utils/generateMetaTxt.ts +12 -10
- package/apps/agents-server/src/components/_utils/headlessParam.tsx +7 -3
- package/apps/agents-server/src/database/$provideSupabaseForBrowser.ts +3 -3
- package/apps/agents-server/src/database/$provideSupabaseForServer.ts +1 -1
- package/apps/agents-server/src/database/$provideSupabaseForWorker.ts +3 -3
- package/apps/agents-server/src/database/metadataDefaults.ts +19 -1
- package/apps/agents-server/src/database/migrate.ts +34 -1
- package/apps/agents-server/src/database/migrations/2025-11-0001-initial-schema.sql +1 -3
- package/apps/agents-server/src/database/migrations/2025-11-0002-metadata-table.sql +1 -3
- package/apps/agents-server/src/database/migrations/2025-12-0240-agent-public-id.sql +3 -0
- package/apps/agents-server/src/database/migrations/2025-12-0360-agent-deleted-at.sql +1 -0
- package/apps/agents-server/src/database/migrations/2025-12-0370-image-table.sql +19 -0
- package/apps/agents-server/src/database/migrations/2025-12-0380-agent-visibility.sql +1 -0
- package/apps/agents-server/src/database/migrations/2025-12-0390-upload-tracking.sql +20 -0
- package/apps/agents-server/src/database/migrations/2025-12-0401-file-upload-status.sql +13 -0
- package/apps/agents-server/src/database/migrations/2025-12-0402-message-table.sql +42 -0
- package/apps/agents-server/src/database/migrations/2025-12-0403-generation-lock-table.sql +15 -0
- package/apps/agents-server/src/database/migrations/2025-12-0640-openai-assistant-cache.sql +12 -0
- package/apps/agents-server/src/database/migrations/2025-12-0820-agent-history-permanent-id.sql +29 -0
- package/apps/agents-server/src/database/schema.ts +231 -4
- package/apps/agents-server/src/generated/reservedPaths.ts +32 -0
- package/apps/agents-server/src/message-providers/email/_common/Email.ts +73 -0
- package/apps/agents-server/src/message-providers/email/_common/utils/TODO.txt +1 -0
- package/apps/agents-server/src/message-providers/email/_common/utils/parseEmailAddress.test.ts.todo +108 -0
- package/apps/agents-server/src/message-providers/email/_common/utils/parseEmailAddress.ts +62 -0
- package/apps/agents-server/src/message-providers/email/_common/utils/parseEmailAddresses.test.ts.todo +117 -0
- package/apps/agents-server/src/message-providers/email/_common/utils/parseEmailAddresses.ts +19 -0
- package/apps/agents-server/src/message-providers/email/_common/utils/stringifyEmailAddress.test.ts.todo +119 -0
- package/apps/agents-server/src/message-providers/email/_common/utils/stringifyEmailAddress.ts +19 -0
- package/apps/agents-server/src/message-providers/email/_common/utils/stringifyEmailAddresses.test.ts.todo +74 -0
- package/apps/agents-server/src/message-providers/email/_common/utils/stringifyEmailAddresses.ts +14 -0
- package/apps/agents-server/src/message-providers/email/sendgrid/SendgridMessageProvider.ts +44 -0
- package/apps/agents-server/src/message-providers/email/sendgrid/parseInboundSendgridEmail.ts +49 -0
- package/apps/agents-server/src/message-providers/email/zeptomail/ZeptomailMessageProvider.ts +51 -0
- package/apps/agents-server/src/message-providers/index.ts +13 -0
- package/apps/agents-server/src/message-providers/interfaces/MessageProvider.ts +11 -0
- package/apps/agents-server/src/middleware.ts +19 -23
- package/apps/agents-server/src/tools/$provideBrowserForServer.ts +32 -0
- package/apps/agents-server/src/tools/$provideCdnForServer.ts +7 -2
- package/apps/agents-server/src/utils/auth.ts +117 -17
- package/apps/agents-server/src/utils/cdn/classes/TrackedFilesStorage.ts +57 -0
- package/apps/agents-server/src/utils/cdn/classes/VercelBlobStorage.ts +4 -0
- package/apps/agents-server/src/utils/cdn/interfaces/IFilesStorage.ts +18 -0
- package/apps/agents-server/src/utils/content/extractBodyContentFromHtml.ts +19 -0
- package/apps/agents-server/src/utils/getUserIdFromRequest.ts +35 -0
- package/apps/agents-server/src/utils/handleChatCompletion.ts +65 -5
- package/apps/agents-server/src/utils/messages/sendMessage.ts +91 -0
- package/apps/agents-server/src/utils/messagesAdmin.ts +72 -0
- package/apps/agents-server/src/utils/normalization/filenameToPrompt.test.ts +36 -0
- package/apps/agents-server/src/utils/normalization/filenameToPrompt.ts +25 -0
- package/apps/agents-server/src/utils/validateApiKey.ts +7 -11
- package/esm/index.es.js +2890 -2737
- package/esm/index.es.js.map +1 -1
- package/esm/typings/servers.d.ts +8 -0
- package/esm/typings/src/_packages/core.index.d.ts +2 -0
- package/esm/typings/src/_packages/types.index.d.ts +10 -2
- package/esm/typings/src/book-2.0/agent-source/AgentBasicInformation.d.ts +6 -1
- package/esm/typings/src/book-2.0/agent-source/createAgentModelRequirements.d.ts +6 -6
- package/esm/typings/src/book-2.0/agent-source/createAgentModelRequirementsWithCommitments.closed.test.d.ts +1 -0
- package/esm/typings/src/book-2.0/utils/generatePlaceholderAgentProfileImageUrl.d.ts +3 -3
- package/esm/typings/src/book-components/Chat/Chat/ChatMessageItem.d.ts +5 -1
- package/esm/typings/src/book-components/Chat/Chat/ChatProps.d.ts +5 -0
- package/esm/typings/src/book-components/Chat/CodeBlock/CodeBlock.d.ts +13 -0
- package/esm/typings/src/book-components/Chat/MarkdownContent/MarkdownContent.d.ts +1 -0
- package/esm/typings/src/book-components/Chat/types/ChatMessage.d.ts +7 -11
- package/esm/typings/src/book-components/_common/Dropdown/Dropdown.d.ts +2 -2
- package/esm/typings/src/book-components/_common/MenuHoisting/MenuHoistingContext.d.ts +56 -0
- package/esm/typings/src/collection/agent-collection/constructors/agent-collection-in-supabase/AgentCollectionInSupabase.d.ts +21 -11
- package/esm/typings/src/collection/agent-collection/constructors/agent-collection-in-supabase/AgentsDatabaseSchema.d.ts +80 -14
- package/esm/typings/src/commitments/DICTIONARY/DICTIONARY.d.ts +46 -0
- package/esm/typings/src/commitments/index.d.ts +2 -1
- package/esm/typings/src/llm-providers/_multiple/MultipleLlmExecutionTools.d.ts +6 -2
- package/esm/typings/src/llm-providers/agent/AgentLlmExecutionTools.d.ts +1 -1
- package/esm/typings/src/llm-providers/ollama/OllamaExecutionTools.d.ts +1 -1
- package/esm/typings/src/llm-providers/openai/createOpenAiCompatibleExecutionTools.d.ts +1 -1
- package/esm/typings/src/llm-providers/remote/RemoteLlmExecutionTools.d.ts +1 -0
- package/esm/typings/src/types/Message.d.ts +49 -0
- package/esm/typings/src/types/ModelRequirements.d.ts +38 -14
- package/esm/typings/src/types/typeAliases.d.ts +23 -1
- package/esm/typings/src/utils/color/utils/colorToDataUrl.d.ts +2 -1
- package/esm/typings/src/utils/environment/$detectRuntimeEnvironment.d.ts +4 -4
- package/esm/typings/src/utils/environment/$isRunningInBrowser.d.ts +1 -1
- package/esm/typings/src/utils/environment/$isRunningInJest.d.ts +1 -1
- package/esm/typings/src/utils/environment/$isRunningInNode.d.ts +1 -1
- package/esm/typings/src/utils/environment/$isRunningInWebWorker.d.ts +1 -1
- package/esm/typings/src/utils/markdown/extractAllBlocksFromMarkdown.d.ts +2 -2
- package/esm/typings/src/utils/markdown/extractOneBlockFromMarkdown.d.ts +2 -2
- package/esm/typings/src/utils/random/$randomBase58.d.ts +12 -0
- package/esm/typings/src/version.d.ts +1 -1
- package/package.json +1 -1
- package/umd/index.umd.js +4018 -3865
- package/umd/index.umd.js.map +1 -1
- package/apps/agents-server/package-lock.json +0 -27
- package/apps/agents-server/public/fonts/download-font.js +0 -22
- package/apps/agents-server/src/components/PrintButton/PrintButton.tsx +0 -18
- package/esm/typings/src/book-2.0/utils/generateGravatarUrl.d.ts +0 -10
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
2
|
+
import { $getTableName } from '../../../database/$getTableName';
|
|
3
|
+
import { $provideSupabase } from '../../../database/$provideSupabase';
|
|
4
|
+
import { isUserAdmin } from '../../../utils/isUserAdmin';
|
|
5
|
+
|
|
6
|
+
const DEFAULT_PAGE_SIZE = 20;
|
|
7
|
+
const MAX_PAGE_SIZE = 100;
|
|
8
|
+
|
|
9
|
+
function parsePositiveInt(value: string | null, fallback: number): number {
|
|
10
|
+
if (!value) return fallback;
|
|
11
|
+
const parsed = parseInt(value, 10);
|
|
12
|
+
if (Number.isNaN(parsed) || parsed <= 0) return fallback;
|
|
13
|
+
return parsed;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* List messages with filters, search and pagination.
|
|
18
|
+
*/
|
|
19
|
+
export async function GET(request: NextRequest) {
|
|
20
|
+
if (!(await isUserAdmin())) {
|
|
21
|
+
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
try {
|
|
25
|
+
const searchParams = request.nextUrl.searchParams;
|
|
26
|
+
|
|
27
|
+
const page = parsePositiveInt(searchParams.get('page'), 1);
|
|
28
|
+
const pageSize = Math.min(MAX_PAGE_SIZE, parsePositiveInt(searchParams.get('pageSize'), DEFAULT_PAGE_SIZE));
|
|
29
|
+
const search = searchParams.get('search')?.trim() || '';
|
|
30
|
+
const channel = searchParams.get('channel');
|
|
31
|
+
const direction = searchParams.get('direction');
|
|
32
|
+
|
|
33
|
+
const supabase = $provideSupabase();
|
|
34
|
+
|
|
35
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
36
|
+
let query = supabase.from(await $getTableName('Message')).select('*', { count: 'exact' });
|
|
37
|
+
|
|
38
|
+
if (channel) {
|
|
39
|
+
query = query.eq('channel', channel);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (direction) {
|
|
43
|
+
query = query.eq('direction', direction);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (search) {
|
|
47
|
+
// Search in content, subject (if in metadata?), sender/recipient emails
|
|
48
|
+
// Note: sender and recipients are JSONB, so ilike might not work directly on them unless cast to text
|
|
49
|
+
// Content is TEXT.
|
|
50
|
+
const escaped = search.replace(/%/g, '\\%').replace(/_/g, '\\_');
|
|
51
|
+
// Assuming simple search on content for now to avoid complexity with JSONB search in generic supabase client
|
|
52
|
+
query = query.ilike('content', `%${escaped}%`);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Default sort by createdAt desc
|
|
56
|
+
query = query.order('createdAt', { ascending: false });
|
|
57
|
+
|
|
58
|
+
const from = (page - 1) * pageSize;
|
|
59
|
+
const to = from + pageSize - 1;
|
|
60
|
+
|
|
61
|
+
query = query.range(from, to);
|
|
62
|
+
|
|
63
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
64
|
+
const { data: messages, error, count } = (await query) as { data: any[]; error: any; count: number };
|
|
65
|
+
|
|
66
|
+
if (error) {
|
|
67
|
+
console.error('List messages error:', error);
|
|
68
|
+
return NextResponse.json({ error: 'Internal server error' }, { status: 500 });
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Fetch attempts for these messages
|
|
72
|
+
if (messages && messages.length > 0) {
|
|
73
|
+
const messageIds = messages.map((m) => m.id);
|
|
74
|
+
const { data: attempts, error: attemptsError } = await supabase
|
|
75
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
76
|
+
.from(await $getTableName('MessageSendAttempt'))
|
|
77
|
+
.select('*')
|
|
78
|
+
.in('messageId', messageIds);
|
|
79
|
+
|
|
80
|
+
if (attemptsError) {
|
|
81
|
+
console.error('Fetch message attempts error:', attemptsError);
|
|
82
|
+
// We don't fail the whole request, just log it.
|
|
83
|
+
} else {
|
|
84
|
+
// Attach attempts to messages
|
|
85
|
+
for (const message of messages) {
|
|
86
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
87
|
+
(message as any).sendAttempts = attempts?.filter((a: any) => a.messageId === message.id) || [];
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return NextResponse.json({
|
|
93
|
+
items: messages ?? [],
|
|
94
|
+
total: count ?? 0,
|
|
95
|
+
page,
|
|
96
|
+
pageSize,
|
|
97
|
+
});
|
|
98
|
+
} catch (error) {
|
|
99
|
+
console.error('List messages error:', error);
|
|
100
|
+
return NextResponse.json({ error: 'Internal server error' }, { status: 500 });
|
|
101
|
+
}
|
|
102
|
+
}
|
|
@@ -1,9 +1,12 @@
|
|
|
1
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
2
|
+
import { keepUnused } from '../../../../../../src/utils/organization/keepUnused';
|
|
1
3
|
import { $getTableName } from '../../../database/$getTableName';
|
|
2
4
|
import { $provideSupabase } from '../../../database/$provideSupabase';
|
|
3
5
|
import { isUserAdmin } from '../../../utils/isUserAdmin';
|
|
4
|
-
import { NextRequest, NextResponse } from 'next/server';
|
|
5
6
|
|
|
6
7
|
export async function GET(request: NextRequest) {
|
|
8
|
+
keepUnused(request);
|
|
9
|
+
|
|
7
10
|
if (!(await isUserAdmin())) {
|
|
8
11
|
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
|
9
12
|
}
|
|
@@ -36,11 +39,7 @@ export async function POST(request: NextRequest) {
|
|
|
36
39
|
const supabase = $provideSupabase();
|
|
37
40
|
const table = await $getTableName('Metadata');
|
|
38
41
|
|
|
39
|
-
const { data, error } = await supabase
|
|
40
|
-
.from(table)
|
|
41
|
-
.insert({ key, value, note })
|
|
42
|
-
.select()
|
|
43
|
-
.single();
|
|
42
|
+
const { data, error } = await supabase.from(table).insert({ key, value, note }).select().single();
|
|
44
43
|
|
|
45
44
|
if (error) {
|
|
46
45
|
return NextResponse.json({ error: error.message }, { status: 500 });
|
|
@@ -1,70 +1,144 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { $getTableName } from '@/src/database/$getTableName';
|
|
2
|
+
import { $provideSupabase } from '@/src/database/$provideSupabase';
|
|
3
3
|
import { serializeError } from '@promptbook-local/utils';
|
|
4
|
-
import
|
|
5
|
-
import { readFile } from 'fs/promises';
|
|
4
|
+
import { handleUpload, type HandleUploadBody } from '@vercel/blob/client';
|
|
6
5
|
import { NextRequest, NextResponse } from 'next/server';
|
|
7
|
-
import { forTime } from 'waitasecond';
|
|
8
6
|
import { assertsError } from '../../../../../../src/errors/assertsError';
|
|
9
|
-
import {
|
|
10
|
-
import { keepUnused } from '../../../../../../src/utils/organization/keepUnused';
|
|
11
|
-
import { $provideCdnForServer } from '../../../../src/tools/$provideCdnForServer';
|
|
12
|
-
import { getUserFileCdnKey } from '../../../../src/utils/cdn/utils/getUserFileCdnKey';
|
|
13
|
-
import { validateMimeType } from '../../../../src/utils/validators/validateMimeType';
|
|
7
|
+
import { getUserIdFromRequest } from '../../../../src/utils/getUserIdFromRequest';
|
|
14
8
|
import { getMetadata } from '../../../database/getMetadata';
|
|
15
9
|
|
|
16
10
|
export async function POST(request: NextRequest) {
|
|
17
11
|
try {
|
|
18
|
-
await
|
|
19
|
-
|
|
12
|
+
const body = (await request.json()) as HandleUploadBody;
|
|
13
|
+
const userId = await getUserIdFromRequest(request);
|
|
14
|
+
const supabase = $provideSupabase();
|
|
20
15
|
|
|
21
|
-
|
|
22
|
-
|
|
16
|
+
// Handle Vercel Blob client upload protocol
|
|
17
|
+
const jsonResponse = await handleUpload({
|
|
18
|
+
body,
|
|
19
|
+
request,
|
|
20
|
+
token: process.env.VERCEL_BLOB_READ_WRITE_TOKEN!,
|
|
21
|
+
onBeforeGenerateToken: async (pathname, clientPayload) => {
|
|
22
|
+
// Authenticate user and validate upload
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
// Parse client payload for additional metadata
|
|
25
|
+
const payload = clientPayload ? JSON.parse(clientPayload) : {};
|
|
26
|
+
const { purpose, contentType } = payload;
|
|
27
27
|
|
|
28
|
-
|
|
28
|
+
let maxFileSizeMb = Number((await getMetadata('MAX_FILE_UPLOAD_SIZE_MB')) || '50'); // <- TODO: [🌲] To /config.ts
|
|
29
|
+
if (Number.isNaN(maxFileSizeMb)) {
|
|
30
|
+
maxFileSizeMb = 50; // <- TODO: [🌲] To /config.ts
|
|
31
|
+
}
|
|
32
|
+
const maxFileSize = maxFileSizeMb * 1024 * 1024;
|
|
33
|
+
|
|
34
|
+
// Generate the proper path with prefix
|
|
35
|
+
// Note: With client uploads, we use the original filename provided by the client
|
|
36
|
+
// The file will be stored at: {pathPrefix}/user/files/{filename}
|
|
37
|
+
const pathPrefix = process.env.NEXT_PUBLIC_CDN_PATH_PREFIX || '';
|
|
29
38
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
39
|
+
// Create a DB record at the start of the upload to track it
|
|
40
|
+
const uploadPurpose = purpose || 'GENERIC_UPLOAD';
|
|
41
|
+
const { data: insertedFile, error: insertError } = await supabase
|
|
42
|
+
.from(await $getTableName('File'))
|
|
43
|
+
.insert({
|
|
44
|
+
userId: userId || null,
|
|
45
|
+
fileName: pathname,
|
|
46
|
+
fileSize: 0, // <- Will be updated when upload completes
|
|
47
|
+
fileType: contentType || 'application/octet-stream',
|
|
48
|
+
storageUrl: null, // <- To be updated on completion
|
|
49
|
+
shortUrl: null, // <- To be updated on completion
|
|
50
|
+
purpose: uploadPurpose,
|
|
51
|
+
status: 'UPLOADING',
|
|
52
|
+
})
|
|
53
|
+
.select('id')
|
|
54
|
+
.single();
|
|
34
55
|
|
|
35
|
-
if (
|
|
36
|
-
|
|
56
|
+
if (insertError) {
|
|
57
|
+
console.error('🔼 Failed to create file record:', insertError);
|
|
37
58
|
}
|
|
38
|
-
resolve(files);
|
|
39
|
-
});
|
|
40
|
-
});
|
|
41
59
|
|
|
42
|
-
|
|
60
|
+
console.info('🔼 Upload started, tracking file:', {
|
|
61
|
+
pathname,
|
|
62
|
+
fileId: insertedFile?.id,
|
|
63
|
+
purpose: uploadPurpose,
|
|
64
|
+
});
|
|
43
65
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
66
|
+
return {
|
|
67
|
+
allowedContentTypes: contentType ? [contentType] : undefined,
|
|
68
|
+
maximumSizeInBytes: maxFileSize,
|
|
69
|
+
addRandomSuffix: true, // Add random suffix to avoid filename collisions since we can't hash content
|
|
70
|
+
tokenPayload: JSON.stringify({
|
|
71
|
+
userId: userId || null,
|
|
72
|
+
purpose: uploadPurpose,
|
|
73
|
+
fileId: insertedFile?.id || null,
|
|
74
|
+
uploadPath: pathname,
|
|
75
|
+
pathPrefix,
|
|
76
|
+
}),
|
|
77
|
+
};
|
|
78
|
+
},
|
|
79
|
+
onUploadCompleted: async ({ blob, tokenPayload }) => {
|
|
80
|
+
// !!!!
|
|
81
|
+
// ⚠️ IMPORTANT: This callback is a WEBHOOK called by Vercel's servers AFTER the upload completes
|
|
82
|
+
// - It runs in a DIFFERENT request context (not the original user request)
|
|
83
|
+
// - It WON'T work in local development (Vercel can't reach localhost)
|
|
84
|
+
// - All data must come from tokenPayload (userId, fileId, etc.)
|
|
85
|
+
// - Need to create a fresh supabase client here
|
|
86
|
+
console.info('🔼 Upload completed (webhook callback):', { blob, tokenPayload });
|
|
50
87
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
const key = getUserFileCdnKey(fileBuffer, uploadedFile.originalFilename || uploadedFile.newFilename);
|
|
88
|
+
try {
|
|
89
|
+
const payload = tokenPayload ? JSON.parse(tokenPayload) : {};
|
|
90
|
+
const { fileId, userId: tokenUserId, purpose: tokenPurpose, uploadPath } = payload;
|
|
55
91
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
92
|
+
// Create fresh supabase client for this webhook context
|
|
93
|
+
const supabase = $provideSupabase();
|
|
94
|
+
|
|
95
|
+
if (fileId) {
|
|
96
|
+
// Update the existing record by ID
|
|
97
|
+
const { error: updateError } = await supabase
|
|
98
|
+
.from(await $getTableName('File'))
|
|
99
|
+
.update({
|
|
100
|
+
userId: tokenUserId || null,
|
|
101
|
+
fileSize: 0, // <- !!!!
|
|
102
|
+
fileType: blob.contentType,
|
|
103
|
+
storageUrl: blob.url,
|
|
104
|
+
// <- TODO: !!!! Split between storageUrl and shortUrl
|
|
105
|
+
purpose: tokenPurpose || 'GENERIC_UPLOAD',
|
|
106
|
+
status: 'COMPLETED',
|
|
107
|
+
})
|
|
108
|
+
.eq('id', fileId);
|
|
60
109
|
|
|
61
|
-
|
|
110
|
+
if (updateError) {
|
|
111
|
+
console.error('🔼 Failed to update file record:', updateError);
|
|
112
|
+
} else {
|
|
113
|
+
console.info('🔼 File record updated successfully:', { fileId, shortUrl: blob.url });
|
|
114
|
+
}
|
|
115
|
+
} else if (uploadPath) {
|
|
116
|
+
// Fallback: Update by uploadPath if fileId is not available
|
|
117
|
+
const { error: updateError } = await supabase
|
|
118
|
+
.from(await $getTableName('File'))
|
|
119
|
+
.update({
|
|
120
|
+
fileSize: 0, // <- !!!!
|
|
121
|
+
fileType: blob.contentType,
|
|
122
|
+
storageUrl: blob.url,
|
|
123
|
+
status: 'COMPLETED',
|
|
124
|
+
})
|
|
125
|
+
.eq('id', fileId);
|
|
62
126
|
|
|
63
|
-
|
|
127
|
+
if (updateError) {
|
|
128
|
+
console.error('🔼 Failed to update file record by uploadPath:', updateError);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
} catch (error) {
|
|
132
|
+
console.error('🔼 Error in onUploadCompleted:', error);
|
|
133
|
+
}
|
|
134
|
+
},
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
return NextResponse.json(jsonResponse);
|
|
64
138
|
} catch (error) {
|
|
65
139
|
assertsError(error);
|
|
66
140
|
|
|
67
|
-
console.error(error);
|
|
141
|
+
console.error('🔼', error);
|
|
68
142
|
|
|
69
143
|
return new Response(
|
|
70
144
|
JSON.stringify(
|
|
@@ -81,3 +155,12 @@ export async function POST(request: NextRequest) {
|
|
|
81
155
|
);
|
|
82
156
|
}
|
|
83
157
|
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* TODO: !!!! Change uploaded URLs from `storageUrl` to `shortUrl`
|
|
161
|
+
* TODO: !!!! Record both `storageUrl` (actual storage location) and `shortUrl` in `File` table
|
|
162
|
+
* TODO: !!!! Record `purpose` in `File` table
|
|
163
|
+
* TODO: !!!! Record `userId` in `File` table
|
|
164
|
+
* TODO: !!!! Record all things into `File` table
|
|
165
|
+
* TODO: !!!! File type (mime type) of `.book` files should be `application/book` <- [🧠] !!!! Best mime type?!
|
|
166
|
+
*/
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { notFound } from 'next/navigation';
|
|
2
2
|
import { BookCommitment } from '../../../../../../src/commitments/_base/BookCommitment';
|
|
3
3
|
import { getVisibleCommitmentDefinitions } from '../../../utils/getVisibleCommitmentDefinitions';
|
|
4
|
-
import {
|
|
4
|
+
import { DocsToolbar } from '../../../components/DocsToolbar/DocsToolbar';
|
|
5
5
|
import { PrintHeader } from '../../../components/PrintHeader/PrintHeader';
|
|
6
6
|
import { DocumentationContent } from '../../../components/DocumentationContent/DocumentationContent';
|
|
7
7
|
|
|
@@ -27,9 +27,8 @@ export default async function DocPage(props: DocPageProps) {
|
|
|
27
27
|
|
|
28
28
|
return (
|
|
29
29
|
<div className="min-h-screen bg-gradient-to-br from-blue-50 via-white to-purple-50">
|
|
30
|
-
<PrintButton />
|
|
31
|
-
|
|
32
30
|
<div className="container mx-auto px-4 py-16">
|
|
31
|
+
<DocsToolbar />
|
|
33
32
|
<PrintHeader title={primary.type} />
|
|
34
33
|
|
|
35
34
|
<DocumentationContent
|
|
@@ -1,20 +1,20 @@
|
|
|
1
|
+
import { MarkdownContent } from '@promptbook-local/components';
|
|
1
2
|
import Link from 'next/link';
|
|
3
|
+
import { DocsToolbar } from '../../components/DocsToolbar/DocsToolbar';
|
|
4
|
+
import { DocumentationContent } from '../../components/DocumentationContent/DocumentationContent';
|
|
2
5
|
import { Card } from '../../components/Homepage/Card';
|
|
3
6
|
import { Section } from '../../components/Homepage/Section';
|
|
4
7
|
import { OpenMojiIcon } from '../../components/OpenMojiIcon/OpenMojiIcon';
|
|
5
|
-
import { getVisibleCommitmentDefinitions } from '../../utils/getVisibleCommitmentDefinitions';
|
|
6
|
-
import { PrintButton } from '../../components/PrintButton/PrintButton';
|
|
7
8
|
import { PrintHeader } from '../../components/PrintHeader/PrintHeader';
|
|
8
|
-
import {
|
|
9
|
+
import { getVisibleCommitmentDefinitions } from '../../utils/getVisibleCommitmentDefinitions';
|
|
9
10
|
|
|
10
11
|
export default function DocsPage() {
|
|
11
12
|
const groupedCommitments = getVisibleCommitmentDefinitions();
|
|
12
13
|
|
|
13
14
|
return (
|
|
14
15
|
<div className="min-h-screen bg-gradient-to-br from-blue-50 via-white to-purple-50">
|
|
15
|
-
<PrintButton />
|
|
16
|
-
|
|
17
16
|
<div className="container mx-auto px-4 py-16">
|
|
17
|
+
<DocsToolbar />
|
|
18
18
|
<PrintHeader title="Full Documentation" />
|
|
19
19
|
|
|
20
20
|
{/* Screen view: Cards */}
|
|
@@ -24,7 +24,7 @@ export default function DocsPage() {
|
|
|
24
24
|
<Link key={primary.type} href={`/docs/${primary.type}`} className="block h-full group">
|
|
25
25
|
<Card className="h-full group-hover:border-blue-500 transition-colors">
|
|
26
26
|
<h3 className="text-xl font-semibold mb-2 group-hover:text-blue-600 transition-colors">
|
|
27
|
-
<OpenMojiIcon icon={primary.icon} className="mr-2" />
|
|
27
|
+
<OpenMojiIcon icon={primary.icon} variant="color" className="mr-2" />
|
|
28
28
|
{primary.type}
|
|
29
29
|
{aliases.length > 0 && (
|
|
30
30
|
<span className="text-gray-400 font-normal text-lg">
|
|
@@ -33,7 +33,11 @@ export default function DocsPage() {
|
|
|
33
33
|
</span>
|
|
34
34
|
)}
|
|
35
35
|
</h3>
|
|
36
|
-
{primary.description &&
|
|
36
|
+
{primary.description && (
|
|
37
|
+
<p className="text-gray-600 line-clamp-3">
|
|
38
|
+
<MarkdownContent content={primary.description} />
|
|
39
|
+
</p>
|
|
40
|
+
)}
|
|
37
41
|
</Card>
|
|
38
42
|
</Link>
|
|
39
43
|
))}
|
|
@@ -44,11 +48,7 @@ export default function DocsPage() {
|
|
|
44
48
|
<div className="hidden print:block space-y-12">
|
|
45
49
|
{groupedCommitments.map(({ primary, aliases }) => (
|
|
46
50
|
<div key={primary.type} className="break-inside-avoid page-break-after-always">
|
|
47
|
-
<DocumentationContent
|
|
48
|
-
primary={primary}
|
|
49
|
-
aliases={aliases}
|
|
50
|
-
isPrintOnly={true}
|
|
51
|
-
/>
|
|
51
|
+
<DocumentationContent primary={primary} aliases={aliases} isPrintOnly={true} />
|
|
52
52
|
<hr className="my-8 border-gray-200" />
|
|
53
53
|
</div>
|
|
54
54
|
))}
|
|
@@ -5,35 +5,67 @@
|
|
|
5
5
|
/**
|
|
6
6
|
* OpenMoji black and white CSS
|
|
7
7
|
*
|
|
8
|
-
* https://github.com/hfg-gmuend/openmoji/blob/master/font/OpenMoji-black-glyf
|
|
8
|
+
* https://github.com/hfg-gmuend/openmoji/blob/master/font/OpenMoji-black-glyf
|
|
9
9
|
*/
|
|
10
10
|
@font-face {
|
|
11
11
|
font-family: 'OpenMojiBlack';
|
|
12
12
|
src: url('/fonts/OpenMoji-black-glyf.woff2') format('woff2');
|
|
13
|
-
unicode-range: U+23, U+2A, U+2D, U+30-39, U+A9, U+AE, U+200D, U+203C, U+2049, U+20E3, U+2117, U+2120, U+2122,
|
|
14
|
-
U+
|
|
15
|
-
U+
|
|
16
|
-
U+
|
|
17
|
-
U+
|
|
18
|
-
U+
|
|
19
|
-
U+
|
|
20
|
-
U+
|
|
21
|
-
U+
|
|
22
|
-
U+
|
|
23
|
-
U+
|
|
24
|
-
U+
|
|
25
|
-
U+
|
|
26
|
-
U+
|
|
27
|
-
U+
|
|
28
|
-
U+
|
|
29
|
-
U+
|
|
30
|
-
U+
|
|
31
|
-
U+
|
|
32
|
-
U+
|
|
33
|
-
U+
|
|
34
|
-
U+
|
|
35
|
-
U+
|
|
36
|
-
|
|
13
|
+
unicode-range: U+23, U+2A, U+2D, U+30-39, U+A9, U+AE, U+200D, U+203C, U+2049, U+20E3, U+2117, U+2120, U+2122, U+2139,
|
|
14
|
+
U+2194-2199, U+21A9, U+21AA, U+229C, U+231A, U+231B, U+2328, U+23CF, U+23E9-23F3, U+23F8-23FE, U+24C2, U+25A1,
|
|
15
|
+
U+25AA-25AE, U+25B6, U+25C0, U+25C9, U+25D0, U+25D1, U+25E7-25EA, U+25ED, U+25EE, U+25FB-25FE, U+2600-2605,
|
|
16
|
+
U+260E, U+2611, U+2614, U+2615, U+2618, U+261D, U+2620, U+2622, U+2623, U+2626, U+262A, U+262E, U+262F,
|
|
17
|
+
U+2638-263A, U+2640, U+2642, U+2648-2653, U+265F, U+2660, U+2663, U+2665, U+2666, U+2668, U+267B, U+267E, U+267F,
|
|
18
|
+
U+2691-2697, U+2699, U+269B, U+269C, U+26A0, U+26A1, U+26A7, U+26AA, U+26AB, U+26B0, U+26B1, U+26BD, U+26BE,
|
|
19
|
+
U+26C4, U+26C5, U+26C8, U+26CE, U+26CF, U+26D1, U+26D3, U+26D4, U+26E9, U+26EA, U+26F0-26F5, U+26F7-26FA, U+26FD,
|
|
20
|
+
U+2702, U+2705, U+2708-270D, U+270F, U+2712, U+2714, U+2716, U+271D, U+2721, U+2728, U+2733, U+2734, U+2744,
|
|
21
|
+
U+2747, U+274C, U+274E, U+2753-2755, U+2757, U+2763, U+2764, U+2795-2797, U+27A1, U+27B0, U+27BF, U+2934, U+2935,
|
|
22
|
+
U+2B05-2B07, U+2B0C, U+2B0D, U+2B1B, U+2B1C, U+2B1F-2B24, U+2B2E, U+2B2F, U+2B50, U+2B55, U+2B58, U+2B8F,
|
|
23
|
+
U+2BBA-2BBC, U+2BC3, U+2BC4, U+2BEA, U+2BEB, U+3030, U+303D, U+3297, U+3299, U+E000-E009, U+E010, U+E011,
|
|
24
|
+
U+E040-E06D, U+E080-E0B4, U+E0C0-E0CC, U+E0FF-E10D, U+E140-E14A, U+E150-E157, U+E181-E189, U+E1C0-E1C4,
|
|
25
|
+
U+E1C6-E1D9, U+E200-E216, U+E240-E269, U+E280-E283, U+E2C0-E2C4, U+E2C6-E2DA, U+E300-E303, U+E305-E30F,
|
|
26
|
+
U+E312-E316, U+E318-E322, U+E324-E329, U+E32B, U+E340-E348, U+E380, U+E381, U+F000, U+F77A, U+F8FF, U+FE0F,
|
|
27
|
+
U+1F004, U+1F0CF, U+1F10D-1F10F, U+1F12F, U+1F16D-1F171, U+1F17E, U+1F17F, U+1F18E, U+1F191-1F19A, U+1F1E6-1F1FF,
|
|
28
|
+
U+1F201, U+1F202, U+1F21A, U+1F22F, U+1F232-1F23A, U+1F250, U+1F251, U+1F260-1F265, U+1F300-1F321, U+1F324-1F393,
|
|
29
|
+
U+1F396, U+1F397, U+1F399-1F39B, U+1F39E-1F3F0, U+1F3F3-1F3F5, U+1F3F7-1F4FD, U+1F4FF-1F53D, U+1F549-1F54E,
|
|
30
|
+
U+1F550-1F567, U+1F56F, U+1F570, U+1F573-1F57A, U+1F587, U+1F58A-1F58D, U+1F590, U+1F595, U+1F596, U+1F5A4,
|
|
31
|
+
U+1F5A5, U+1F5A8, U+1F5B1, U+1F5B2, U+1F5BC, U+1F5C2-1F5C4, U+1F5D1-1F5D3, U+1F5DC-1F5DE, U+1F5E1, U+1F5E3,
|
|
32
|
+
U+1F5E8, U+1F5EF, U+1F5F3, U+1F5FA-1F64F, U+1F680-1F6C5, U+1F6CB-1F6D2, U+1F6D5-1F6D7, U+1F6DC-1F6E5, U+1F6E9,
|
|
33
|
+
U+1F6EB, U+1F6EC, U+1F6F0, U+1F6F3-1F6FC, U+1F7E0-1F7EB, U+1F7F0, U+1F90C-1F93A, U+1F93C-1F945, U+1F947-1F9FF,
|
|
34
|
+
U+1FA70-1FA7C, U+1FA80-1FA88, U+1FA90-1FABD, U+1FABF-1FAC5, U+1FACE-1FADB, U+1FAE0-1FAE8, U+1FAF0-1FAF8,
|
|
35
|
+
U+1FBC5-1FBC9, U+E0061-E0067, U+E0069, U+E006C-E0079, U+E007F;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* OpenMoji color CSS
|
|
40
|
+
*
|
|
41
|
+
* https://github.com/hfg-gmuend/openmoji/tree/master/font/OpenMoji-color-cbdt
|
|
42
|
+
*/
|
|
43
|
+
@font-face {
|
|
44
|
+
font-family: 'OpenMojiColor';
|
|
45
|
+
src: url('/fonts/OpenMoji-color-cbdt.woff2') format('woff2');
|
|
46
|
+
unicode-range: U+23, U+2A, U+2D, U+30-39, U+A9, U+AE, U+200D, U+203C, U+2049, U+20E3, U+2117, U+2120, U+2122, U+2139,
|
|
47
|
+
U+2194-2199, U+21A9, U+21AA, U+229C, U+231A, U+231B, U+2328, U+23CF, U+23E9-23F3, U+23F8-23FE, U+24C2, U+25A1,
|
|
48
|
+
U+25AA-25AE, U+25B6, U+25C0, U+25C9, U+25D0, U+25D1, U+25E7-25EA, U+25ED, U+25EE, U+25FB-25FE, U+2600-2605,
|
|
49
|
+
U+260E, U+2611, U+2614, U+2615, U+2618, U+261D, U+2620, U+2622, U+2623, U+2626, U+262A, U+262E, U+262F,
|
|
50
|
+
U+2638-263A, U+2640, U+2642, U+2648-2653, U+265F, U+2660, U+2663, U+2665, U+2666, U+2668, U+267B, U+267E, U+267F,
|
|
51
|
+
U+2691-2697, U+2699, U+269B, U+269C, U+26A0, U+26A1, U+26A7, U+26AA, U+26AB, U+26B0, U+26B1, U+26BD, U+26BE,
|
|
52
|
+
U+26C4, U+26C5, U+26C8, U+26CE, U+26CF, U+26D1, U+26D3, U+26D4, U+26E9, U+26EA, U+26F0-26F5, U+26F7-26FA, U+26FD,
|
|
53
|
+
U+2702, U+2705, U+2708-270D, U+270F, U+2712, U+2714, U+2716, U+271D, U+2721, U+2728, U+2733, U+2734, U+2744,
|
|
54
|
+
U+2747, U+274C, U+274E, U+2753-2755, U+2757, U+2763, U+2764, U+2795-2797, U+27A1, U+27B0, U+27BF, U+2934, U+2935,
|
|
55
|
+
U+2B05-2B07, U+2B0C, U+2B0D, U+2B1B, U+2B1C, U+2B1F-2B24, U+2B2E, U+2B2F, U+2B50, U+2B55, U+2B58, U+2B8F,
|
|
56
|
+
U+2BBA-2BBC, U+2BC3, U+2BC4, U+2BEA, U+2BEB, U+3030, U+303D, U+3297, U+3299, U+E000-E009, U+E010, U+E011,
|
|
57
|
+
U+E040-E06D, U+E080-E0B4, U+E0C0-E0CC, U+E0FF-E10D, U+E140-E14A, U+E150-E157, U+E181-E189, U+E1C0-E1C4,
|
|
58
|
+
U+E1C6-E1D9, U+E200-E216, U+E240-E269, U+E280-E283, U+E2C0-E2C4, U+E2C6-E2DA, U+E300-E303, U+E305-E30F,
|
|
59
|
+
U+E312-E316, U+E318-E322, U+E324-E329, U+E32B, U+E340-E348, U+E380, U+E381, U+F000, U+F77A, U+F8FF, U+FE0F,
|
|
60
|
+
U+1F004, U+1F0CF, U+1F10D-1F10F, U+1F12F, U+1F16D-1F171, U+1F17E, U+1F17F, U+1F18E, U+1F191-1F19A, U+1F1E6-1F1FF,
|
|
61
|
+
U+1F201, U+1F202, U+1F21A, U+1F22F, U+1F232-1F23A, U+1F250, U+1F251, U+1F260-1F265, U+1F300-1F321, U+1F324-1F393,
|
|
62
|
+
U+1F396, U+1F397, U+1F399-1F39B, U+1F39E-1F3F0, U+1F3F3-1F3F5, U+1F3F7-1F4FD, U+1F4FF-1F53D, U+1F549-1F54E,
|
|
63
|
+
U+1F550-1F567, U+1F56F, U+1F570, U+1F573-1F57A, U+1F587, U+1F58A-1F58D, U+1F590, U+1F595, U+1F596, U+1F5A4,
|
|
64
|
+
U+1F5A5, U+1F5A8, U+1F5B1, U+1F5B2, U+1F5BC, U+1F5C2-1F5C4, U+1F5D1-1F5D3, U+1F5DC-1F5DE, U+1F5E1, U+1F5E3,
|
|
65
|
+
U+1F5E8, U+1F5EF, U+1F5F3, U+1F5FA-1F64F, U+1F680-1F6C5, U+1F6CB-1F6D2, U+1F6D5-1F6D7, U+1F6DC-1F6E5, U+1F6E9,
|
|
66
|
+
U+1F6EB, U+1F6EC, U+1F6F0, U+1F6F3-1F6FC, U+1F7E0-1F7EB, U+1F7F0, U+1F90C-1F93A, U+1F93C-1F945, U+1F947-1F9FF,
|
|
67
|
+
U+1FA70-1FA7C, U+1FA80-1FA88, U+1FA90-1FABD, U+1FABF-1FAC5, U+1FACE-1FADB, U+1FAE0-1FAE8, U+1FAF0-1FAF8,
|
|
68
|
+
U+1FBC5-1FBC9, U+E0061-E0067, U+E0069, U+E006C-E0079, U+E007F;
|
|
37
69
|
}
|
|
38
70
|
|
|
39
71
|
:root {
|
|
@@ -201,7 +233,7 @@ textarea:focus-visible {
|
|
|
201
233
|
/* Print styles */
|
|
202
234
|
@media print {
|
|
203
235
|
@page {
|
|
204
|
-
margin:
|
|
236
|
+
margin: 1cm;
|
|
205
237
|
}
|
|
206
238
|
|
|
207
239
|
/* Hide UI elements */
|
|
@@ -211,7 +243,7 @@ textarea:focus-visible {
|
|
|
211
243
|
aside,
|
|
212
244
|
button:not(.print:block),
|
|
213
245
|
.no-print,
|
|
214
|
-
[role=
|
|
246
|
+
[role='complementary'],
|
|
215
247
|
#portal-root {
|
|
216
248
|
display: none !important;
|
|
217
249
|
}
|
|
@@ -227,6 +259,14 @@ textarea:focus-visible {
|
|
|
227
259
|
color: black !important;
|
|
228
260
|
}
|
|
229
261
|
|
|
262
|
+
/* Remove container padding for full width printing */
|
|
263
|
+
.container {
|
|
264
|
+
padding-left: 0 !important;
|
|
265
|
+
padding-right: 0 !important;
|
|
266
|
+
max-width: none !important;
|
|
267
|
+
width: 100% !important;
|
|
268
|
+
}
|
|
269
|
+
|
|
230
270
|
/* Remove shadows and backgrounds for clean print */
|
|
231
271
|
* {
|
|
232
272
|
box-shadow: none !important;
|
|
@@ -236,10 +276,66 @@ textarea:focus-visible {
|
|
|
236
276
|
}
|
|
237
277
|
|
|
238
278
|
/* Ensure text visibility */
|
|
239
|
-
h1,
|
|
279
|
+
h1,
|
|
280
|
+
h2,
|
|
281
|
+
h3,
|
|
282
|
+
h4,
|
|
283
|
+
h5,
|
|
284
|
+
h6,
|
|
285
|
+
p,
|
|
286
|
+
li,
|
|
287
|
+
span,
|
|
288
|
+
div {
|
|
240
289
|
color: black !important;
|
|
241
290
|
}
|
|
242
291
|
|
|
292
|
+
/* Optimize typography for print */
|
|
293
|
+
.prose {
|
|
294
|
+
font-size: 11pt !important;
|
|
295
|
+
line-height: 1.4 !important;
|
|
296
|
+
max-width: 100% !important;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
.prose h1 {
|
|
300
|
+
font-size: 22pt !important;
|
|
301
|
+
margin-bottom: 0.5rem !important;
|
|
302
|
+
margin-top: 0 !important;
|
|
303
|
+
line-height: 1.2 !important;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
.prose h2 {
|
|
307
|
+
font-size: 16pt !important;
|
|
308
|
+
margin-top: 1.5rem !important;
|
|
309
|
+
margin-bottom: 0.5rem !important;
|
|
310
|
+
padding-bottom: 0.25rem !important;
|
|
311
|
+
line-height: 1.3 !important;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
.prose h3 {
|
|
315
|
+
font-size: 14pt !important;
|
|
316
|
+
margin-top: 1rem !important;
|
|
317
|
+
margin-bottom: 0.5rem !important;
|
|
318
|
+
line-height: 1.3 !important;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
.prose p,
|
|
322
|
+
.prose ul,
|
|
323
|
+
.prose ol {
|
|
324
|
+
margin-bottom: 0.5rem !important;
|
|
325
|
+
margin-top: 0 !important;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
.prose li {
|
|
329
|
+
margin-top: 0.2rem !important;
|
|
330
|
+
margin-bottom: 0.2rem !important;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
.prose pre {
|
|
334
|
+
padding: 0.5rem !important;
|
|
335
|
+
margin-bottom: 0.75rem !important;
|
|
336
|
+
margin-top: 0.5rem !important;
|
|
337
|
+
}
|
|
338
|
+
|
|
243
339
|
/* Links */
|
|
244
340
|
a {
|
|
245
341
|
text-decoration: underline;
|
|
@@ -247,30 +343,41 @@ textarea:focus-visible {
|
|
|
247
343
|
}
|
|
248
344
|
|
|
249
345
|
/* Preserve borders */
|
|
250
|
-
.border,
|
|
346
|
+
.border,
|
|
347
|
+
.border-b,
|
|
348
|
+
.border-t,
|
|
349
|
+
.border-l,
|
|
350
|
+
.border-r {
|
|
251
351
|
border-color: #ddd !important;
|
|
252
352
|
}
|
|
253
353
|
|
|
254
354
|
/* Page break control */
|
|
255
|
-
h1,
|
|
355
|
+
h1,
|
|
356
|
+
h2 {
|
|
256
357
|
break-after: avoid;
|
|
257
358
|
page-break-after: avoid;
|
|
258
359
|
}
|
|
259
360
|
|
|
260
|
-
p,
|
|
361
|
+
p,
|
|
362
|
+
li,
|
|
363
|
+
pre,
|
|
364
|
+
code {
|
|
261
365
|
break-inside: avoid;
|
|
262
366
|
page-break-inside: avoid;
|
|
263
367
|
}
|
|
264
368
|
|
|
265
369
|
/* Specific component overrides */
|
|
266
|
-
.card,
|
|
370
|
+
.card,
|
|
371
|
+
.bg-white {
|
|
267
372
|
border: none !important;
|
|
268
373
|
}
|
|
269
374
|
|
|
270
375
|
/* Code blocks */
|
|
271
|
-
pre,
|
|
376
|
+
pre,
|
|
377
|
+
code {
|
|
272
378
|
background-color: #f5f5f5 !important;
|
|
273
379
|
border: 1px solid #ddd !important;
|
|
274
380
|
white-space: pre-wrap !important;
|
|
381
|
+
font-size: 0.9em !important;
|
|
275
382
|
}
|
|
276
383
|
}
|