diff --git a/frontend/src/components/layout/ThemePicker.tsx b/frontend/src/components/layout/ThemePicker.tsx index bedf400..2f9b3b8 100644 --- a/frontend/src/components/layout/ThemePicker.tsx +++ b/frontend/src/components/layout/ThemePicker.tsx @@ -1,12 +1,8 @@ -// 全局主题选择器 — Header / Login 共用,分组展示主流与开发者主题 // Logan.Li +// 全局主题选择器 — Header / Login 共用,双主题 + 跟随系统 // Logan.Li import { Check } from 'lucide-react'; import { cn } from '@/lib/utils'; -import { - DropdownMenuItem, - DropdownMenuLabel, - DropdownMenuSeparator, -} from '@/components/ui/dropdown-menu'; -import { THEME_GROUPS, type ThemePreference } from '@/utils/theme'; +import { DropdownMenuItem } from '@/components/ui/dropdown-menu'; +import { THEME_OPTIONS, type ThemePreference } from '@/utils/theme'; interface ThemePickerProps { value: ThemePreference; @@ -16,46 +12,36 @@ interface ThemePickerProps { export function ThemePicker({ value, onChange }: ThemePickerProps) { return ( <> - {THEME_GROUPS.map((group, gi) => ( -
- {gi > 0 && } - - {group.label} - - {group.options.map((option) => ( - onChange(option.id)} - > - -
- {option.name} - - {option.description} - -
- {value === option.id && ( - - )} -
- ))} -
+ {THEME_OPTIONS.map((option) => ( + onChange(option.id)} + > + +
+ {option.name} + + {option.description} + +
+ {value === option.id && ( + + )} +
))} ); -} \ No newline at end of file +} diff --git a/frontend/src/index.css b/frontend/src/index.css index 1bcca8c..b175a4f 100644 --- a/frontend/src/index.css +++ b/frontend/src/index.css @@ -136,339 +136,6 @@ --skeleton-base: #f0f1f5; --skeleton-highlight: #e8eaef; } - - /* ===== Theme 3: One Dark — Atom 暖灰 IDE(刻意与深海蓝蓝黑区分) // Logan.Li ===== */ - [data-theme="one-dark"] { - --background: 220 9% 22%; - --foreground: 220 8% 82%; - --card: 220 8% 26%; - --card-foreground: 220 8% 82%; - --popover: 220 8% 24%; - --popover-foreground: 220 8% 82%; - --primary: 207 82% 66%; - --primary-foreground: 220 9% 18%; - --secondary: 220 8% 30%; - --secondary-foreground: 220 8% 82%; - --muted: 220 8% 30%; - --muted-foreground: 220 6% 52%; - --accent: 220 8% 30%; - --accent-foreground: 220 8% 82%; - --destructive: 355 65% 65%; - --destructive-foreground: 220 9% 18%; - --border: 220 7% 34%; - --input: 220 8% 28%; - --ring: 286 60% 67%; - --radius: 0.625rem; - --sidebar-background: 220 9% 17%; - --sidebar-foreground: 220 8% 78%; - --sidebar-primary: 207 82% 66%; - --sidebar-primary-foreground: 220 9% 18%; - --sidebar-accent: 220 8% 26%; - --sidebar-accent-foreground: 220 8% 82%; - --sidebar-border: 220 7% 28%; - --sidebar-ring: 286 60% 67%; - /* 更亮暖灰底 + 侧栏压暗,贴近 Atom 编辑器层次 */ - --dg-bg-primary: #282c34; - --dg-bg-secondary: #2f343e; - --dg-bg-tertiary: #383e4a; - --dg-bg-hover: #3e4451; - --dg-bg-active: #4b5263; - --dg-border: #4b5263; - --dg-border-light: #353b45; - --dg-border-hover: #5c6370; - --dg-text-primary: #d7dae0; - --dg-text-secondary: #9da5b4; - --dg-text-muted: #6b7285; - /* Atom 语法高亮色 — 品牌渐变用紫→绿,非深海蓝 */ - --dg-blue: #61afef; - --dg-green: #98c379; - --dg-red: #e06c75; - --dg-orange: #d19a66; - --dg-purple: #c678dd; - --dg-cyan: #56b6c2; - --dg-yellow: #e5c07b; - --dg-card-hover-border: rgba(198, 120, 221, 0.42); - --dg-gradient-start: #c678dd; - --dg-gradient-end: #98c379; - --dg-gradient-text-start: #c678dd; - --dg-gradient-text-end: #61afef; - --dg-input-focus-ring: rgba(198, 120, 221, 0.22); - --chart-grid: #4b5263; - --chart-axis: #6b7285; - --chart-tooltip-bg: #2f343e; - --chart-tooltip-border: #4b5263; - --shadow-card: 0 1px 3px rgba(0, 0, 0, 0.22), 0 0 0 1px rgba(198, 120, 221, 0.06); - --shadow-card-hover: 0 8px 28px rgba(0, 0, 0, 0.32), 0 0 0 1px rgba(152, 195, 121, 0.18); - --shadow-dropdown: 0 10px 36px rgba(0, 0, 0, 0.45); - --skeleton-base: #383e4a; - --skeleton-highlight: #4b5263; - --dg-overlay: color-mix(in srgb, #21252b 52%, transparent); - } - - /* ===== Theme 4: Catppuccin Mocha ===== */ - [data-theme="catppuccin"] { - --background: 240 21% 15%; - --foreground: 226 64% 88%; - --card: 240 21% 12%; - --card-foreground: 226 64% 88%; - --popover: 240 21% 12%; - --popover-foreground: 226 64% 88%; - --primary: 217 92% 76%; - --primary-foreground: 240 21% 15%; - --secondary: 237 16% 23%; - --secondary-foreground: 226 64% 88%; - --muted: 237 16% 23%; - --muted-foreground: 228 24% 72%; - --accent: 237 16% 23%; - --accent-foreground: 226 64% 88%; - --destructive: 343 81% 75%; - --destructive-foreground: 240 21% 15%; - --border: 237 16% 23%; - --input: 237 16% 23%; - --ring: 217 92% 76%; - --radius: 0.625rem; - --sidebar-background: 240 23% 9%; - --sidebar-foreground: 226 64% 85%; - --sidebar-primary: 217 92% 76%; - --sidebar-primary-foreground: 240 21% 15%; - --sidebar-accent: 237 16% 23%; - --sidebar-accent-foreground: 226 64% 88%; - --sidebar-border: 237 16% 23%; - --sidebar-ring: 217 92% 76%; - --dg-bg-primary: #1e1e2e; - --dg-bg-secondary: #181825; - --dg-bg-tertiary: #313244; - --dg-bg-hover: #45475a; - --dg-bg-active: #585b70; - --dg-border: #45475a; - --dg-border-light: #313244; - --dg-border-hover: #585b70; - --dg-text-primary: #cdd6f4; - --dg-text-secondary: #a6adc8; - --dg-text-muted: #7f849c; - --dg-blue: #89b4fa; - --dg-green: #a6e3a1; - --dg-red: #f38ba8; - --dg-orange: #fab387; - --dg-purple: #cba6f7; - --dg-cyan: #89dceb; - --dg-yellow: #f9e2af; - --dg-card-hover-border: rgba(137, 180, 250, 0.45); - --dg-gradient-start: #74c7ec; - --dg-gradient-end: #cba6f7; - --dg-gradient-text-start: #89b4fa; - --dg-gradient-text-end: #cba6f7; - --dg-input-focus-ring: rgba(137, 180, 250, 0.2); - --chart-grid: #45475a; - --chart-axis: #7f849c; - --chart-tooltip-bg: #181825; - --chart-tooltip-border: #45475a; - --shadow-card: 0 1px 3px rgba(0, 0, 0, 0.3), 0 0 0 1px rgba(137, 180, 250, 0.05); - --shadow-card-hover: 0 8px 32px rgba(0, 0, 0, 0.4), 0 0 0 1px rgba(137, 180, 250, 0.15); - --shadow-dropdown: 0 10px 40px rgba(0, 0, 0, 0.5); - --skeleton-base: #313244; - --skeleton-highlight: #45475a; - --dg-overlay: color-mix(in srgb, #11111b 55%, transparent); - } - - /* ===== Theme 5: Dracula — 紫粉经典(高辨识度) // Logan.Li ===== */ - [data-theme="dracula"] { - --background: 231 15% 18%; - --foreground: 60 30% 96%; - --card: 231 15% 20%; - --card-foreground: 60 30% 96%; - --popover: 231 15% 20%; - --popover-foreground: 60 30% 96%; - --primary: 265 89% 78%; - --primary-foreground: 231 15% 18%; - --secondary: 232 14% 31%; - --secondary-foreground: 60 30% 96%; - --muted: 232 14% 31%; - --muted-foreground: 231 8% 68%; - --accent: 232 14% 31%; - --accent-foreground: 60 30% 96%; - --destructive: 0 100% 67%; - --destructive-foreground: 231 15% 18%; - --border: 232 14% 31%; - --input: 232 14% 31%; - --ring: 265 89% 78%; - --radius: 0.625rem; - --sidebar-background: 231 16% 16%; - --sidebar-foreground: 60 25% 92%; - --sidebar-primary: 265 89% 78%; - --sidebar-primary-foreground: 231 15% 18%; - --sidebar-accent: 232 14% 28%; - --sidebar-accent-foreground: 60 30% 96%; - --sidebar-border: 232 14% 28%; - --sidebar-ring: 265 89% 78%; - --dg-bg-primary: #282a36; - --dg-bg-secondary: #313341; - --dg-bg-tertiary: #44475a; - --dg-bg-hover: #4d5066; - --dg-bg-active: #5a5d78; - --dg-border: #44475a; - --dg-border-light: #383a4a; - --dg-border-hover: #6272a4; - --dg-text-primary: #f8f8f2; - --dg-text-secondary: #bfbfb5; - --dg-text-muted: #8b8b80; - --dg-blue: #8be9fd; - --dg-green: #50fa7b; - --dg-red: #ff5555; - --dg-orange: #ffb86c; - --dg-purple: #bd93f9; - --dg-cyan: #8be9fd; - --dg-yellow: #f1fa8c; - --dg-card-hover-border: rgba(189, 147, 249, 0.5); - --dg-gradient-start: #bd93f9; - --dg-gradient-end: #ff79c6; - --dg-gradient-text-start: #bd93f9; - --dg-gradient-text-end: #ff79c6; - --dg-input-focus-ring: rgba(189, 147, 249, 0.25); - --chart-grid: #44475a; - --chart-axis: #8b8b80; - --chart-tooltip-bg: #313341; - --chart-tooltip-border: #44475a; - --shadow-card: 0 1px 3px rgba(0, 0, 0, 0.35), 0 0 0 1px rgba(189, 147, 249, 0.08); - --shadow-card-hover: 0 8px 32px rgba(0, 0, 0, 0.45), 0 0 0 1px rgba(255, 121, 198, 0.2); - --shadow-dropdown: 0 10px 40px rgba(0, 0, 0, 0.55); - --skeleton-base: #44475a; - --skeleton-highlight: #4d5066; - --dg-overlay: color-mix(in srgb, #191a21 58%, transparent); - } - - /* ===== Theme 6: Tokyo Night — 深蓝紫霓虹 // Logan.Li ===== */ - [data-theme="tokyo-night"] { - --background: 235 25% 12%; - --foreground: 226 48% 86%; - --card: 234 26% 16%; - --card-foreground: 226 48% 86%; - --popover: 234 26% 16%; - --popover-foreground: 226 48% 86%; - --primary: 221 70% 72%; - --primary-foreground: 235 25% 12%; - --secondary: 233 23% 22%; - --secondary-foreground: 226 48% 86%; - --muted: 233 23% 22%; - --muted-foreground: 225 15% 58%; - --accent: 233 23% 22%; - --accent-foreground: 226 48% 86%; - --destructive: 350 65% 72%; - --destructive-foreground: 235 25% 12%; - --border: 233 20% 26%; - --input: 233 23% 22%; - --ring: 221 70% 72%; - --radius: 0.625rem; - --sidebar-background: 235 28% 10%; - --sidebar-foreground: 226 45% 84%; - --sidebar-primary: 221 70% 72%; - --sidebar-primary-foreground: 235 25% 12%; - --sidebar-accent: 233 23% 20%; - --sidebar-accent-foreground: 226 48% 86%; - --sidebar-border: 233 20% 24%; - --sidebar-ring: 221 70% 72%; - --dg-bg-primary: #1a1b26; - --dg-bg-secondary: #24283b; - --dg-bg-tertiary: #2f3549; - --dg-bg-hover: #3b4261; - --dg-bg-active: #414868; - --dg-border: #3b4261; - --dg-border-light: #292e42; - --dg-border-hover: #565f89; - --dg-text-primary: #c0caf5; - --dg-text-secondary: #9aa5ce; - --dg-text-muted: #737aa2; - --dg-blue: #7aa2f7; - --dg-green: #9ece6a; - --dg-red: #f7768e; - --dg-orange: #ff9e64; - --dg-purple: #bb9af7; - --dg-cyan: #7dcfff; - --dg-yellow: #e0af68; - --dg-card-hover-border: rgba(122, 162, 247, 0.45); - --dg-gradient-start: #7aa2f7; - --dg-gradient-end: #bb9af7; - --dg-gradient-text-start: #7aa2f7; - --dg-gradient-text-end: #7dcfff; - --dg-input-focus-ring: rgba(122, 162, 247, 0.22); - --chart-grid: #3b4261; - --chart-axis: #737aa2; - --chart-tooltip-bg: #24283b; - --chart-tooltip-border: #3b4261; - --shadow-card: 0 1px 3px rgba(0, 0, 0, 0.35), 0 0 0 1px rgba(122, 162, 247, 0.06); - --shadow-card-hover: 0 8px 32px rgba(0, 0, 0, 0.45), 0 0 0 1px rgba(187, 154, 247, 0.18); - --shadow-dropdown: 0 10px 40px rgba(0, 0, 0, 0.55); - --skeleton-base: #2f3549; - --skeleton-highlight: #3b4261; - --dg-overlay: color-mix(in srgb, #15161e 58%, transparent); - } - - /* ===== Theme 7: Nord(保留兼容,已从选择器下架) // Logan.Li ===== */ - [data-theme="nord"] { - --background: 220 16% 22%; - --foreground: 218 27% 92%; - --card: 220 16% 19%; - --card-foreground: 218 27% 92%; - --popover: 220 16% 19%; - --popover-foreground: 218 27% 92%; - --primary: 193 43% 67%; - --primary-foreground: 220 16% 22%; - --secondary: 220 16% 28%; - --secondary-foreground: 218 27% 92%; - --muted: 220 16% 28%; - --muted-foreground: 220 16% 56%; - --accent: 220 16% 28%; - --accent-foreground: 218 27% 92%; - --destructive: 354 42% 56%; - --destructive-foreground: 218 27% 92%; - --border: 220 16% 28%; - --input: 220 16% 28%; - --ring: 193 43% 67%; - --radius: 0.625rem; - --sidebar-background: 220 16% 18%; - --sidebar-foreground: 218 27% 88%; - --sidebar-primary: 193 43% 67%; - --sidebar-primary-foreground: 220 16% 22%; - --sidebar-accent: 220 16% 28%; - --sidebar-accent-foreground: 218 27% 92%; - --sidebar-border: 220 16% 28%; - --sidebar-ring: 193 43% 67%; - --dg-bg-primary: #2e3440; - --dg-bg-secondary: #3b4252; - --dg-bg-tertiary: #434c5e; - --dg-bg-hover: #4c566a; - --dg-bg-active: #5e6a7e; - --dg-border: #4c566a; - --dg-border-light: #434c5e; - --dg-border-hover: #5e6a7e; - --dg-text-primary: #eceff4; - --dg-text-secondary: #d8dee9; - --dg-text-muted: #a0a8b8; - --dg-blue: #81a1c1; - --dg-green: #a3be8c; - --dg-red: #bf616a; - --dg-orange: #d08770; - --dg-purple: #b48ead; - --dg-cyan: #88c0d0; - --dg-yellow: #ebcb8b; - --dg-card-hover-border: rgba(136, 192, 208, 0.45); - --dg-gradient-start: #5e81ac; - --dg-gradient-end: #88c0d0; - --dg-gradient-text-start: #88c0d0; - --dg-gradient-text-end: #81a1c1; - --dg-input-focus-ring: rgba(136, 192, 208, 0.2); - --chart-grid: #4c566a; - --chart-axis: #a0a8b8; - --chart-tooltip-bg: #3b4252; - --chart-tooltip-border: #4c566a; - --shadow-card: 0 1px 3px rgba(0, 0, 0, 0.3), 0 0 0 1px rgba(136, 192, 208, 0.05); - --shadow-card-hover: 0 8px 32px rgba(0, 0, 0, 0.4), 0 0 0 1px rgba(136, 192, 208, 0.15); - --shadow-dropdown: 0 10px 40px rgba(0, 0, 0, 0.5); - --skeleton-base: #434c5e; - --skeleton-highlight: #4c566a; - --dg-overlay: color-mix(in srgb, #2e3440 55%, transparent); - } - /* * ===== AIOps 设计令牌(Design Tokens)// Logan.Li ===== * @@ -482,8 +149,7 @@ * 布局:--space-page 页面区块 | --layout-* 应用壳(侧栏/顶栏/内边距) * 告警:--alert-warning-* / --alert-error-* 横幅语义色 * 主题:data-theme-preference 用户选择 | data-theme 实际生效主题 - * 主流:system / ocean-deep(蓝黑) / light-professional - * 开发者:one-dark(暖灰) / dracula(紫粉) / tokyo-night(霓虹) / catppuccin + * 仅保留 双主题:ocean-deep(深海蓝暗色) / light-professional(明亮白浅色) + system 跟随系统 * * 布局层级:dg-app-shell → dg-app-content → PageShell(.dg-page) → ContentPanel */ diff --git a/frontend/src/utils/theme.ts b/frontend/src/utils/theme.ts index ff68c58..b8bcfed 100644 --- a/frontend/src/utils/theme.ts +++ b/frontend/src/utils/theme.ts @@ -1,4 +1,4 @@ -// 主题常量与持久化 — 主流主题 + 开发者主题 + 跟随系统 // Logan.Li +// 主题常量与持久化 — 双主题(深海蓝暗色 / 明亮白浅色)+ 跟随系统 // Logan.Li import { getItem, setItem, STORAGE_KEYS } from './storage'; import i18n from '@/i18n'; @@ -7,12 +7,7 @@ import i18n from '@/i18n'; export type ThemePreference = | 'system' | 'ocean-deep' - | 'light-professional' - | 'one-dark' - | 'catppuccin' - | 'dracula' - | 'tokyo-night' - | 'nord'; + | 'light-professional'; /** 实际应用到 data-theme 的具体主题 */ export type ResolvedTheme = Exclude; @@ -30,30 +25,18 @@ export interface ThemeGroup { options: ThemeOption[]; } -/** 主流主题 — 默认推荐 */ +/** 主题清单 — 仅保留一暗一亮,避免多个暗色主题视觉重复 // Logan.Li */ const MAINSTREAM_THEMES: ThemeOption[] = [ { id: 'system', name: i18n.t('common.lightDark'), description: i18n.t('common.themets.s17d84295'), isDark: false, primaryColor: 'linear-gradient(135deg, #2563eb 50%, #0d1117 50%)' }, { id: 'ocean-deep', name: i18n.t('common.themets.s2f82cbb2'), description: i18n.t('common.themets.s29ee972b'), isDark: true, primaryColor: '#0d1117' }, { id: 'light-professional', name: i18n.t('common.themets.s2d43cc04'), description: i18n.t('common.themets.s37e76182'), isDark: false, primaryColor: '#2563eb' }, ]; -/** 开发者主题 — 高辨识度扩展 */ -const DEVELOPER_THEMES: ThemeOption[] = [ - { id: 'one-dark', name: 'One Dark', description: i18n.t('common.themets.s02b08e05'), isDark: true, primaryColor: '#c678dd' }, - { id: 'dracula', name: 'Dracula', description: i18n.t('common.themets.s525ce260'), isDark: true, primaryColor: '#bd93f9' }, - { id: 'tokyo-night', name: 'Tokyo Night', description: i18n.t('common.themets.s6f40fb5e'), isDark: true, primaryColor: '#7aa2f7' }, - { id: 'catppuccin', name: 'Catppuccin', description: i18n.t('common.themets.s571aa09d'), isDark: true, primaryColor: '#89b4fa' }, -]; - export const THEME_GROUPS: ThemeGroup[] = [ { label: i18n.t('common.themets.s8392cae7'), options: MAINSTREAM_THEMES }, - { label: i18n.t('common.themets.sc2e48855'), options: DEVELOPER_THEMES }, ]; -export const THEME_OPTIONS: ThemeOption[] = [ - ...MAINSTREAM_THEMES, - ...DEVELOPER_THEMES, -]; +export const THEME_OPTIONS: ThemeOption[] = [...MAINSTREAM_THEMES]; export const THEME_ORDER: ThemePreference[] = THEME_OPTIONS.map((o) => o.id); export const DEFAULT_THEME: ThemePreference = 'system'; @@ -61,10 +44,15 @@ export const DEFAULT_THEME: ThemePreference = 'system'; const LEGACY_THEME_MAP: Record = { dark: 'ocean-deep', light: 'light-professional', - 'midnight-purple': 'one-dark', - 'pine-green': 'catppuccin', - 'orange-vitality': 'tokyo-night', - nord: 'tokyo-night', + // 已下架的旧主题与开发者主题统一迁移到深海蓝,保证存量用户不掉样式。 // Logan.Li + 'midnight-purple': 'ocean-deep', + 'pine-green': 'ocean-deep', + 'orange-vitality': 'ocean-deep', + nord: 'ocean-deep', + 'one-dark': 'ocean-deep', + dracula: 'ocean-deep', + 'tokyo-night': 'ocean-deep', + catppuccin: 'ocean-deep', }; export function migrateLegacyTheme(value: string | null): ThemePreference | null { @@ -103,11 +91,6 @@ export function getInitialTheme(): ThemePreference { if (stored && THEME_ORDER.includes(stored as ThemePreference)) { return stored as ThemePreference; } - /* nord 已下架,自动迁移到 Tokyo Night // Logan.Li */ - if (stored === 'nord') { - setItem(STORAGE_KEYS.theme, 'tokyo-night'); - return 'tokyo-night'; - } return DEFAULT_THEME; } @@ -126,4 +109,4 @@ export function applyThemeToDocument(pref: ThemePreference): void { export function getNextTheme(current: ThemePreference): ThemePreference { const idx = THEME_ORDER.indexOf(current); return THEME_ORDER[(idx + 1) % THEME_ORDER.length]; -} \ No newline at end of file +}