diff --git a/src/components/sidebar.js b/src/components/sidebar.js index 786e1a6..dce259e 100644 --- a/src/components/sidebar.js +++ b/src/components/sidebar.js @@ -2,6 +2,7 @@ * 侧边导航栏 */ import { navigate, getCurrentRoute } from '../router.js' +import { toggleTheme, getTheme } from '../lib/theme.js' const NAV_ITEMS = [ { @@ -68,10 +69,31 @@ export function renderSidebar(el) { } html += '' + + // 主题切换按钮 + const isDark = getTheme() === 'dark' + const sunIcon = '' + const moonIcon = '' + + html += ` + + ` + el.innerHTML = html - // 绑定点击事件 - el.querySelectorAll('.nav-item').forEach(item => { + // 绑定导航点击事件 + el.querySelectorAll('.nav-item[data-route]').forEach(item => { item.onclick = () => navigate(item.dataset.route) }) + + // 主题切换 + el.querySelector('#btn-theme-toggle')?.addEventListener('click', () => { + toggleTheme() + renderSidebar(el) + }) } diff --git a/src/lib/theme.js b/src/lib/theme.js new file mode 100644 index 0000000..e6f6434 --- /dev/null +++ b/src/lib/theme.js @@ -0,0 +1,26 @@ +/** + * 主题管理(日间/夜间模式) + */ +const THEME_KEY = 'clawpanel-theme' + +export function initTheme() { + const saved = localStorage.getItem(THEME_KEY) + const theme = saved || (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light') + applyTheme(theme) +} + +export function toggleTheme() { + const current = document.documentElement.dataset.theme || 'light' + const next = current === 'dark' ? 'light' : 'dark' + applyTheme(next) + return next +} + +export function getTheme() { + return document.documentElement.dataset.theme || 'light' +} + +function applyTheme(theme) { + document.documentElement.dataset.theme = theme + localStorage.setItem(THEME_KEY, theme) +} diff --git a/src/main.js b/src/main.js index b943e89..0eb88c1 100644 --- a/src/main.js +++ b/src/main.js @@ -3,6 +3,7 @@ */ import { registerRoute, initRouter } from './router.js' import { renderSidebar } from './components/sidebar.js' +import { initTheme } from './lib/theme.js' // 样式 import './style/variables.css' @@ -22,6 +23,9 @@ registerRoute('/mcp', () => import('./pages/mcp.js')) registerRoute('/memory', () => import('./pages/memory.js')) registerRoute('/deploy', () => import('./pages/deploy.js')) +// 初始化主题 +initTheme() + // 初始化 const sidebar = document.getElementById('sidebar') const content = document.getElementById('content') diff --git a/src/pages/agents.js b/src/pages/agents.js index f0e9f0e..e65f58b 100644 --- a/src/pages/agents.js +++ b/src/pages/agents.js @@ -83,7 +83,7 @@ function renderConfig(page, state) { el.querySelectorAll('[data-action="remove-fallback"]').forEach(btn => { btn.onclick = () => { const idx = parseInt(btn.dataset.index) - model.fallbacks.splice(idx, 1) + if (model.fallbacks) model.fallbacks.splice(idx, 1) renderConfig(page, state) } }) diff --git a/src/style/layout.css b/src/style/layout.css index 0e93e6e..fe68c70 100644 --- a/src/style/layout.css +++ b/src/style/layout.css @@ -47,6 +47,11 @@ overflow-y: auto; } +.sidebar-footer { + padding: var(--space-sm); + border-top: 1px solid var(--border-secondary); +} + .nav-section { margin-bottom: var(--space-md); } diff --git a/src/style/reset.css b/src/style/reset.css index 24c961b..586545d 100644 --- a/src/style/reset.css +++ b/src/style/reset.css @@ -51,9 +51,9 @@ input:focus, textarea:focus, select:focus { ::-webkit-scrollbar { width: 6px; height: 6px; } ::-webkit-scrollbar-track { background: transparent; } ::-webkit-scrollbar-thumb { - background: rgba(255, 255, 255, 0.1); + background: var(--border-primary); border-radius: 3px; } ::-webkit-scrollbar-thumb:hover { - background: rgba(255, 255, 255, 0.2); + background: var(--text-tertiary); } diff --git a/src/style/variables.css b/src/style/variables.css index f2d5177..52dbe25 100644 --- a/src/style/variables.css +++ b/src/style/variables.css @@ -1,5 +1,43 @@ -:root { - /* 颜色系统 - 暗色主题 */ +/* 亮色主题(默认) */ +:root, [data-theme="light"] { + --bg-primary: #f8f9fb; + --bg-secondary: #ffffff; + --bg-tertiary: #f0f1f3; + --bg-card: rgba(0, 0, 0, 0.02); + --bg-card-hover: rgba(0, 0, 0, 0.04); + --bg-glass: rgba(0, 0, 0, 0.03); + --bg-glass-hover: rgba(0, 0, 0, 0.06); + + --border-primary: rgba(0, 0, 0, 0.08); + --border-secondary: rgba(0, 0, 0, 0.04); + --border-focus: rgba(99, 102, 241, 0.5); + + --text-primary: #18181b; + --text-secondary: #52525b; + --text-tertiary: #a1a1aa; + --text-inverse: #ffffff; + + --accent: #6366f1; + --accent-hover: #4f46e5; + --accent-muted: rgba(99, 102, 241, 0.1); + + --success: #16a34a; + --success-muted: rgba(22, 163, 74, 0.1); + --warning: #d97706; + --warning-muted: rgba(217, 119, 6, 0.1); + --error: #dc2626; + --error-muted: rgba(220, 38, 38, 0.1); + --info: #2563eb; + --info-muted: rgba(37, 99, 235, 0.1); + + --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.05); + --shadow-md: 0 4px 12px rgba(0, 0, 0, 0.08); + --shadow-lg: 0 8px 24px rgba(0, 0, 0, 0.12); + --shadow-glow: 0 0 20px rgba(99, 102, 241, 0.1); +} + +/* 暗色主题 */ +[data-theme="dark"] { --bg-primary: #0a0a0f; --bg-secondary: #12121a; --bg-tertiary: #1a1a26; @@ -10,19 +48,15 @@ --border-primary: rgba(255, 255, 255, 0.08); --border-secondary: rgba(255, 255, 255, 0.04); - --border-focus: rgba(99, 102, 241, 0.5); --text-primary: #e4e4e7; --text-secondary: #a1a1aa; --text-tertiary: #71717a; --text-inverse: #0a0a0f; - /* 强调色 */ - --accent: #6366f1; --accent-hover: #818cf8; --accent-muted: rgba(99, 102, 241, 0.15); - /* 状态色 */ --success: #22c55e; --success-muted: rgba(34, 197, 94, 0.15); --warning: #f59e0b; @@ -32,7 +66,14 @@ --info: #3b82f6; --info-muted: rgba(59, 130, 246, 0.15); - /* 间距 */ + --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.3); + --shadow-md: 0 4px 12px rgba(0, 0, 0, 0.4); + --shadow-lg: 0 8px 24px rgba(0, 0, 0, 0.5); + --shadow-glow: 0 0 20px rgba(99, 102, 241, 0.15); +} + +/* 共享变量(不随主题变化) */ +:root { --space-xs: 4px; --space-sm: 8px; --space-md: 12px; @@ -41,13 +82,11 @@ --space-2xl: 32px; --space-3xl: 48px; - /* 圆角 */ --radius-sm: 6px; --radius-md: 8px; --radius-lg: 12px; --radius-xl: 16px; - /* 字体 */ --font-sans: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC', 'Noto Sans SC', sans-serif; --font-mono: 'SF Mono', 'Fira Code', 'JetBrains Mono', monospace; --font-size-xs: 11px; @@ -57,18 +96,10 @@ --font-size-xl: 20px; --font-size-2xl: 24px; - /* 阴影 */ - --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.3); - --shadow-md: 0 4px 12px rgba(0, 0, 0, 0.4); - --shadow-lg: 0 8px 24px rgba(0, 0, 0, 0.5); - --shadow-glow: 0 0 20px rgba(99, 102, 241, 0.15); - - /* 动效 */ --transition-fast: 150ms ease; --transition-normal: 250ms ease; --transition-slow: 350ms ease; - /* 布局 */ --sidebar-width: 220px; --sidebar-collapsed: 60px; --header-height: 52px;