返回 presentation-ai
ThemeSettings.tsx
根目录 / src / components / notebook / presentation / components / theme / ThemeSettings.tsx
1 import { ThemeCard } from "@/components/presentation/edit-panel/sections/theme/ThemeCard";
2 import { Button } from "@/components/ui/button";
3 import { ImageSourceSelector } from "@/components/ui/image-source-selector";
4 import { Label } from "@/components/ui/label";
5 import {
6 isBuiltInPresentationTheme,
7 PRESENTATION_AUTO_THEME_ID,
8 } from "@/lib/presentation/theme-resolution";
9 import { themes, type ThemeProperties } from "@/lib/presentation/themes";
10 import { usePresentationState } from "@/states/presentation-state";
11 import { CreateThemeModal } from "./create-theme/CreateThemeModal";
12 import { ThemeModal } from "./ThemeModal";
13
14 const MAX_VISIBLE_THEMES = 9;
15
16 export function ThemeSettings() {
17 const {
18 theme,
19 generatedThemeData,
20 customThemeData,
21 setTheme,
22 imageModel,
23 setImageModel,
24 imageSource,
25 setImageSource,
26 stockImageProvider,
27 setStockImageProvider,
28 } = usePresentationState();
29
30 const themeDataToUse = customThemeData ?? generatedThemeData;
31 const autoThemeOption: [string, ThemeProperties][] = themeDataToUse
32 ? [
33 [
34 theme !== "auto" && !themes[theme as keyof typeof themes]
35 ? theme
36 : PRESENTATION_AUTO_THEME_ID,
37 {
38 ...themeDataToUse,
39 name: themeDataToUse.name || "Custom Theme",
40 description:
41 themeDataToUse.description || "Custom presentation theme",
42 },
43 ],
44 ]
45 : [];
46
47 const visibleThemes = [...autoThemeOption, ...Object.entries(themes)].slice(
48 0,
49 MAX_VISIBLE_THEMES,
50 );
51
52 return (
53 <div className="mb-32! space-y-5 rounded-2xl border border-border/60 bg-muted/35 p-4 sm:p-6">
54 <div className="space-y-1">
55 <h2 className="text-lg font-semibold">Customize Theme</h2>
56 <p className="text-sm text-muted-foreground">
57 Pick a visual direction and image source before rendering slides.
58 </p>
59 </div>
60
61 <div className="space-y-6">
62 <div className="space-y-4">
63 <div className="flex items-center justify-between gap-3">
64 <Label className="text-sm font-medium">Theme & Layout</Label>
65 <div className="flex items-center gap-2">
66 <ThemeModal>
67 <Button
68 variant="link"
69 className="h-auto p-0 text-sm font-medium whitespace-nowrap"
70 >
71 More Themes
72 </Button>
73 </ThemeModal>
74 </div>
75 </div>
76 <div className="grid grid-cols-2 gap-3 lg:grid-cols-3">
77 {visibleThemes.map(([key, themeOption]) => (
78 <div key={key} className="h-44">
79 <ThemeCard
80 theme={themeOption}
81 themeId={key}
82 isSelected={theme === key}
83 showEllipsis={false}
84 showFavoriteButton={false}
85 personalizeLabel="Personalize"
86 onSelect={() =>
87 setTheme(
88 key,
89 isBuiltInPresentationTheme(key) ? undefined : themeOption,
90 )
91 }
92 />
93 </div>
94 ))}
95 </div>
96 </div>
97
98 <ImageSourceSelector
99 imageSource={imageSource}
100 imageModel={imageModel}
101 stockImageProvider={stockImageProvider}
102 onImageSourceChange={setImageSource}
103 onImageModelChange={setImageModel}
104 onStockImageProviderChange={setStockImageProvider}
105 className="space-y-4"
106 showLabel={true}
107 />
108 </div>
109
110 <CreateThemeModal previewMode="test-only" />
111 </div>
112 );
113 }
114
114 lines Plain Text