From 022782212076d90318c92b2f1eef390da28a47b6 Mon Sep 17 00:00:00 2001 From: cfy666 <3087823110@qq.com> Date: Sat, 13 Jun 2026 17:08:56 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=E6=B7=B1=E8=89=B2/?= =?UTF-8?q?=E6=B5=85=E8=89=B2=E4=B8=BB=E9=A2=98=E5=88=87=E6=8D=A2=E5=8A=9F?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - types 新增 Theme 类型(dark/light) - storage 新增 loadTheme/saveTheme 持久化主题偏好 - ConfigPanel 新增外观分组,主题下拉选择 - App.tsx 通过 data-theme 属性挂载到 html 元素 - App.css 新增 [data-theme=light] 亮色变量覆盖 Co-Authored-By: Claude --- frontend/src/App.css | 18 +++++++++++-- frontend/src/App.tsx | 17 +++++++++++- frontend/src/components/ConfigPanel/index.tsx | 26 ++++++++++++++++--- frontend/src/lib/storage.ts | 19 +++++++++++++- frontend/src/types/index.ts | 2 ++ 5 files changed, 75 insertions(+), 7 deletions(-) diff --git a/frontend/src/App.css b/frontend/src/App.css index d215a75..b6260b9 100644 --- a/frontend/src/App.css +++ b/frontend/src/App.css @@ -19,6 +19,20 @@ --radius-sm: 6px; } +[data-theme="light"] { + --color-primary: #2563eb; + --color-primary-hover: #1d4ed8; + --color-bg: #f1f5f9; + --color-surface: #ffffff; + --color-surface-2: #f8fafc; + --color-text: #0f172a; + --color-text-muted: #64748b; + --color-border: #e2e8f0; + --color-success: #16a34a; + --color-warning: #d97706; + --color-error: #dc2626; +} + * { margin: 0; padding: 0; @@ -151,7 +165,7 @@ body { .video-container { position: relative; aspect-ratio: 4 / 3; - background: #0a0e17; + background: var(--color-surface-2); border-radius: var(--radius); overflow: hidden; border: 1px solid var(--color-border); @@ -167,7 +181,7 @@ body { width: 100%; height: 100%; object-fit: cover; - background: #000; + background: #1a1a2e; } .video-placeholder { diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index e5d6bae..bde81ef 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -2,16 +2,29 @@ // CamTalk — 主应用组件(Web 端双栏布局) // ============================================================ -import { useState } from "react"; +import { useCallback, useEffect, useState } from "react"; import { useVisionSession } from "./hooks/useVisionSession"; import { VideoPreview } from "./components/VideoPreview"; import { ChatPanel } from "./components/ChatPanel"; import { ConfigPanel } from "./components/ConfigPanel"; import { ToastContainer } from "./components/Toast"; +import { loadTheme, saveTheme } from "./lib/storage"; +import type { Theme } from "./types"; import "./App.css"; function App() { const [showConfig, setShowConfig] = useState(false); + const [theme, setTheme] = useState(loadTheme); + + // 切换主题时更新 的 data-theme 属性 + useEffect(() => { + document.documentElement.setAttribute("data-theme", theme); + }, [theme]); + + const handleThemeChange = useCallback((t: Theme) => { + setTheme(t); + saveTheme(t); + }, []); const { messages, @@ -67,7 +80,9 @@ function App() { {showConfig && ( setShowConfig(false)} /> )} diff --git a/frontend/src/components/ConfigPanel/index.tsx b/frontend/src/components/ConfigPanel/index.tsx index 192cd0b..3844e6d 100644 --- a/frontend/src/components/ConfigPanel/index.tsx +++ b/frontend/src/components/ConfigPanel/index.tsx @@ -1,17 +1,19 @@ // ============================================================ // ConfigPanel — 右侧抽屉式配置面板 -// 职责:TTS 开关、detail level 切换、语言选择 +// 职责:主题切换、TTS 开关、detail level 切换、语言选择 // ============================================================ -import type { SessionConfig } from "../../types"; +import type { SessionConfig, Theme } from "../../types"; interface ConfigPanelProps { config: SessionConfig; + theme: Theme; onUpdate: (partial: Partial) => void; + onThemeChange: (theme: Theme) => void; onClose: () => void; } -export function ConfigPanel({ config, onUpdate, onClose }: ConfigPanelProps) { +export function ConfigPanel({ config, theme, onUpdate, onThemeChange, onClose }: ConfigPanelProps) { return (
e.stopPropagation()}> @@ -21,6 +23,24 @@ export function ConfigPanel({ config, onUpdate, onClose }: ConfigPanelProps) {
+
+
外观
+ + +
+
会话
diff --git a/frontend/src/lib/storage.ts b/frontend/src/lib/storage.ts index 34fc369..fb12324 100644 --- a/frontend/src/lib/storage.ts +++ b/frontend/src/lib/storage.ts @@ -3,9 +3,10 @@ // 职责:会话配置持久化 // ============================================================ -import type { SessionConfig } from "../types"; +import type { SessionConfig, Theme } from "../types"; const CONFIG_KEY = "camtalk:config"; +const THEME_KEY = "camtalk:theme"; const DEFAULT_CONFIG: SessionConfig = { ttsEnabled: true, @@ -33,3 +34,19 @@ export function saveConfig(config: SessionConfig): void { // localStorage 不可用时静默失败 } } + +/** 加载主题偏好 */ +export function loadTheme(): Theme { + try { + const raw = localStorage.getItem(THEME_KEY); + if (raw === "light" || raw === "dark") return raw; + } catch { /* ignore */ } + return "dark"; +} + +/** 保存主题偏好 */ +export function saveTheme(theme: Theme): void { + try { + localStorage.setItem(THEME_KEY, theme); + } catch { /* ignore */ } +} diff --git a/frontend/src/types/index.ts b/frontend/src/types/index.ts index 4cad08a..0f29ec5 100644 --- a/frontend/src/types/index.ts +++ b/frontend/src/types/index.ts @@ -11,6 +11,8 @@ export interface SessionConfig { language: string; } +export type Theme = "dark" | "light"; + export interface Session { sessionId: string; createdAt: string;