返回 presentation-ai
CustomTheme.tsx
根目录 / src / components / presentation / agent / tools / CustomTheme.tsx
1 "use client";
2
3 import { Check, Loader2, Palette } from "lucide-react";
4
5 import { type PresentationAiThemeProperties } from "@/lib/presentation/theme-schema";
6 import { type ThemeProperties } from "@/lib/presentation/themes";
7
8 type CustomThemePreviewData =
9 | PresentationAiThemeProperties
10 | Partial<ThemeProperties>;
11
12 function ThemeSwatches({ themeData }: { themeData?: CustomThemePreviewData }) {
13 const colors = themeData?.colors;
14 if (!colors) {
15 return null;
16 }
17
18 const swatches = [
19 colors.primary,
20 colors.accent,
21 colors.background,
22 colors.heading,
23 colors.text,
24 ].filter((color): color is string => typeof color === "string");
25
26 if (swatches.length === 0) {
27 return null;
28 }
29
30 return (
31 <div className="flex items-center gap-1.5">
32 {swatches.map((color, index) => (
33 <span
34 key={`${color}-${index}`}
35 className="h-4 w-4 rounded-full border"
36 style={{ backgroundColor: color }}
37 />
38 ))}
39 </div>
40 );
41 }
42
43 export function PresentationCustomThemeCall({
44 themeData,
45 loading,
46 }: {
47 themeData?: CustomThemePreviewData;
48 loading?: boolean;
49 }) {
50 const headingFont = themeData?.fonts?.heading;
51 const bodyFont = themeData?.fonts?.body;
52 const fontLabels = [headingFont, bodyFont].filter(
53 (font): font is string => typeof font === "string" && font.length > 0,
54 );
55
56 return (
57 <div className="w-full rounded-lg border bg-card p-3 shadow">
58 <div className="flex items-start justify-between gap-3">
59 <div className="flex min-w-0 gap-2">
60 {loading ? (
61 <Loader2 className="mt-0.5 h-4 w-4 animate-spin text-muted-foreground" />
62 ) : (
63 <Palette className="mt-0.5 h-4 w-4 text-muted-foreground" />
64 )}
65 <div className="min-w-0">
66 <div className="truncate text-sm font-medium">
67 {themeData?.name ?? "Custom theme"}
68 </div>
69 <div className="mt-1 text-xs text-muted-foreground">
70 {loading ? "Preparing theme..." : "Creating custom theme"}
71 </div>
72 {themeData ? (
73 <div className="mt-2 flex flex-wrap items-center gap-x-3 gap-y-1 text-xs text-muted-foreground">
74 {fontLabels.map((font, index) => (
75 <span key={`${font}-${index}`}>{font}</span>
76 ))}
77 <ThemeSwatches themeData={themeData} />
78 </div>
79 ) : null}
80 </div>
81 </div>
82 </div>
83 </div>
84 );
85 }
86
87 export function PresentationCustomThemeResult({
88 message,
89 }: {
90 message?: string;
91 }) {
92 return (
93 <div className="rounded-lg border border-green-200 bg-green-50 px-3 py-2 dark:border-green-900 dark:bg-green-950/20">
94 <span className="inline-flex items-center gap-2 text-sm text-green-900 dark:text-green-100">
95 <Check className="h-4 w-4" />
96 {message ?? "Custom theme applied"}
97 </span>
98 </div>
99 );
100 }
101
101 lines Plain Text