mirror of
https://github.com/qingchencloud/clawpanel.git
synced 2026-05-06 20:02:49 +08:00
feat: 添加日间/夜间主题切换系统
- 新增 theme.js 主题管理模块(localStorage 持久化) - variables.css 重构为亮色默认 + 暗色 data-theme 切换 - sidebar 底部添加主题切换按钮(sun/moon SVG 图标) - 修复 scrollbar 硬编码颜色为 CSS 变量 - 修复 agents.js fallbacks 未定义时的空指针错误
This commit is contained in:
@@ -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 += '</nav>'
|
||||
|
||||
// 主题切换按钮
|
||||
const isDark = getTheme() === 'dark'
|
||||
const sunIcon = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="5"/><line x1="12" y1="1" x2="12" y2="3"/><line x1="12" y1="21" x2="12" y2="23"/><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"/><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"/><line x1="1" y1="12" x2="3" y2="12"/><line x1="21" y1="12" x2="23" y2="12"/><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"/><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"/></svg>'
|
||||
const moonIcon = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 12.79A9 9 0 1111.21 3 7 7 0 0021 12.79z"/></svg>'
|
||||
|
||||
html += `
|
||||
<div class="sidebar-footer">
|
||||
<div class="nav-item" id="btn-theme-toggle">
|
||||
${isDark ? sunIcon : moonIcon}
|
||||
<span>${isDark ? '日间模式' : '夜间模式'}</span>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
|
||||
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)
|
||||
})
|
||||
}
|
||||
|
||||
26
src/lib/theme.js
Normal file
26
src/lib/theme.js
Normal file
@@ -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)
|
||||
}
|
||||
@@ -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')
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user