返回 presentation-ai
ChangeTheme.tsx
根目录 / src / components / presentation / agent / tools / ChangeTheme.tsx
1 "use client";
2
3 import { Loader2, Palette } from "lucide-react";
4 import { useEffect, useState } from "react";
5
6 import { Button } from "@/components/ui/button";
7 import {
8 Select,
9 SelectContent,
10 SelectItem,
11 SelectTrigger,
12 SelectValue,
13 } from "@/components/ui/select";
14 import { executeToolAction } from "@/hooks/presentation/agentTools";
15 import { themes, type ThemeName } from "@/lib/presentation/themes";
16
17 type Themes = ThemeName;
18
19 export function PresentationChangeThemeCall({
20 theme,
21 loading,
22 }: {
23 theme?: Themes;
24 loading?: boolean;
25 }) {
26 const [selected, setSelected] = useState<Themes | undefined>(theme);
27 const [isEditing, setIsEditing] = useState(false);
28
29 useEffect(() => {
30 setSelected(theme);
31 }, [theme]);
32
33 const apply = () => {
34 if (selected) {
35 executeToolAction({ action: "change_theme", theme: selected });
36 setIsEditing(false);
37 }
38 };
39
40 if (!theme || !themes?.[theme]?.name) {
41 return (
42 <div className="w-full rounded-lg border bg-card p-3 shadow">
43 <div className="flex items-center gap-2">
44 <Loader2 className="h-4 w-4 animate-spin text-muted-foreground" />
45 <span className="text-sm text-muted-foreground">
46 Updating theme...
47 </span>
48 </div>
49 </div>
50 );
51 }
52
53 if (isEditing) {
54 return (
55 <div className="rounded-lg border bg-card p-3 shadow">
56 <div className="mb-3 flex items-center gap-2">
57 <Palette className="h-4 w-4 text-muted-foreground" />
58 <span className="text-sm font-medium">Change Theme</span>
59 </div>
60 <div className="flex gap-2">
61 <Select
62 value={selected}
63 onValueChange={(v) => setSelected(v as Themes)}
64 >
65 <SelectTrigger className="flex-1">
66 <SelectValue placeholder="Select theme" />
67 </SelectTrigger>
68 <SelectContent>
69 {themes &&
70 Object.entries(themes).map(([key, value]) => (
71 <SelectItem key={key} value={key}>
72 {value?.name ?? key}
73 </SelectItem>
74 ))}
75 </SelectContent>
76 </Select>
77 <Button size="sm" onClick={apply} disabled={loading || !selected}>
78 Apply
79 </Button>
80 <Button size="sm" variant="ghost" onClick={() => setIsEditing(false)}>
81 Cancel
82 </Button>
83 </div>
84 </div>
85 );
86 }
87
88 return (
89 <button
90 onClick={() => setIsEditing(true)}
91 className="group w-full rounded-lg border bg-card p-3 text-left shadow transition-colors hover:bg-accent/50"
92 >
93 <div className="flex items-center justify-between">
94 <div className="flex items-center gap-2">
95 <Palette className="h-4 w-4 text-muted-foreground" />
96 <span className="text-sm text-muted-foreground">Theme:</span>
97 <span className="text-sm font-medium">
98 {selected
99 ? (themes?.[selected as Themes]?.name ?? selected)
100 : "None"}
101 </span>
102 </div>
103 <span className="text-xs text-muted-foreground opacity-0 transition-opacity group-hover:opacity-100">
104 Click to edit
105 </span>
106 </div>
107 </button>
108 );
109 }
110
111 export function PresentationChangeThemeResult({
112 message,
113 }: {
114 message?: string;
115 }) {
116 return (
117 <div className="rounded-lg border border-green-200 bg-green-50 px-3 py-2 dark:border-green-900 dark:bg-green-950/20">
118 <span className="text-sm text-green-900 dark:text-green-100">
119 {message ?? "Theme updated"}
120 </span>
121 </div>
122 );
123 }
124
124 lines Plain Text