diff --git a/packages/client/src/App.vue b/packages/client/src/App.vue index c1e52cf..6fcfcd9 100644 --- a/packages/client/src/App.vue +++ b/packages/client/src/App.vue @@ -1,17 +1,22 @@ - + diff --git a/packages/client/src/assets/dance-dark.mp4 b/packages/client/src/assets/dance-dark.mp4 new file mode 100644 index 0000000..8d8089e Binary files /dev/null and b/packages/client/src/assets/dance-dark.mp4 differ diff --git a/packages/client/src/assets/dance-light.mp4 b/packages/client/src/assets/dance-light.mp4 new file mode 100644 index 0000000..dd3c606 Binary files /dev/null and b/packages/client/src/assets/dance-light.mp4 differ diff --git a/packages/client/src/assets/dance.mp4 b/packages/client/src/assets/dance.mp4 deleted file mode 100644 index 34144c8..0000000 Binary files a/packages/client/src/assets/dance.mp4 and /dev/null differ diff --git a/packages/client/src/assets/thinking-dark.mp4 b/packages/client/src/assets/thinking-dark.mp4 new file mode 100644 index 0000000..33197d1 Binary files /dev/null and b/packages/client/src/assets/thinking-dark.mp4 differ diff --git a/packages/client/src/assets/thinking-light.mp4 b/packages/client/src/assets/thinking-light.mp4 new file mode 100644 index 0000000..654e08f Binary files /dev/null and b/packages/client/src/assets/thinking-light.mp4 differ diff --git a/packages/client/src/assets/thinking.mp4 b/packages/client/src/assets/thinking.mp4 deleted file mode 100644 index 5523eb4..0000000 Binary files a/packages/client/src/assets/thinking.mp4 and /dev/null differ diff --git a/packages/client/src/components/hermes/chat/ChatInput.vue b/packages/client/src/components/hermes/chat/ChatInput.vue index 4d8db0a..7f0390c 100644 --- a/packages/client/src/components/hermes/chat/ChatInput.vue +++ b/packages/client/src/components/hermes/chat/ChatInput.vue @@ -331,7 +331,7 @@ function isImage(type: string): boolean { border-radius: 50%; border: none; background: rgba(0, 0, 0, 0.5); - color: #fff; + color: var(--text-on-overlay); display: flex; align-items: center; justify-content: center; @@ -394,8 +394,8 @@ function isImage(type: string): boolean { // Drag-over state .input-wrapper.drag-over { - border-color: #4a90d9; + border-color: var(--accent-info); border-style: dashed; - background-color: rgba(74, 144, 217, 0.04); + background-color: rgba(var(--accent-info-rgb), 0.04); } diff --git a/packages/client/src/components/hermes/chat/MarkdownRenderer.vue b/packages/client/src/components/hermes/chat/MarkdownRenderer.vue index 734158a..c28fce7 100644 --- a/packages/client/src/components/hermes/chat/MarkdownRenderer.vue +++ b/packages/client/src/components/hermes/chat/MarkdownRenderer.vue @@ -105,7 +105,7 @@ const renderedHtml = computed(() => md.render(props.content)) } th { - background: rgba($accent-primary, 0.08); + background: rgba(var(--accent-primary-rgb), 0.08); color: $text-primary; font-weight: 600; } @@ -189,4 +189,20 @@ const renderedHtml = computed(() => md.render(props.content)) .hljs-title\.function_ { color: #1a1a1a; } .hljs-params { color: #2a2a2a; } .hljs-meta { color: #999999; } + +// Dark mode highlight.js — inverted pure ink +.dark .hljs { color: #d0d0d0; } +.dark .hljs-keyword, +.dark .hljs-selector-tag { color: #f0f0f0; font-weight: 600; } +.dark .hljs-string, +.dark .hljs-attr { color: #aaaaaa; } +.dark .hljs-number { color: #cccccc; } +.dark .hljs-comment { color: #666666; font-style: italic; } +.dark .hljs-built_in { color: #bbbbbb; } +.dark .hljs-type { color: #c6c6c6; } +.dark .hljs-variable { color: #f0f0f0; } +.dark .hljs-title, +.dark .hljs-title\.function_ { color: #f0f0f0; } +.dark .hljs-params { color: #d0d0d0; } +.dark .hljs-meta { color: #666666; } diff --git a/packages/client/src/components/hermes/chat/MessageItem.vue b/packages/client/src/components/hermes/chat/MessageItem.vue index 8636b93..6f50254 100644 --- a/packages/client/src/components/hermes/chat/MessageItem.vue +++ b/packages/client/src/components/hermes/chat/MessageItem.vue @@ -201,7 +201,7 @@ const formattedToolResult = computed(() => { .message-bubble { background-color: $msg-user-bg; - border-radius: $radius-md $radius-md 4px $radius-md; + border-radius: 10px; } } @@ -223,7 +223,7 @@ const formattedToolResult = computed(() => { .message-bubble { background-color: $msg-assistant-bg; - border-radius: $radius-md $radius-md $radius-md 4px; + border-radius: 10px; } } @@ -238,7 +238,7 @@ const formattedToolResult = computed(() => { border-left: 3px solid $warning; border-radius: $radius-sm; max-width: 80%; - background-color: rgba($warning, 0.06); + background-color: rgba(var(--warning-rgb), 0.06); } } } @@ -261,6 +261,7 @@ const formattedToolResult = computed(() => { font-size: 14px; line-height: 1.65; word-break: break-word; + border-radius: 10px; } .msg-attachments { @@ -315,6 +316,10 @@ const formattedToolResult = computed(() => { color: $text-muted; margin-top: 4px; padding: 0 4px; + + .dark & { + color: #999999; + } } .tool-line { @@ -369,7 +374,7 @@ const formattedToolResult = computed(() => { .tool-error-badge { font-size: 9px; color: $error; - background: rgba($error, 0.08); + background: rgba(var(--error-rgb), 0.08); padding: 0 4px; border-radius: 3px; line-height: 14px; diff --git a/packages/client/src/components/hermes/chat/MessageList.vue b/packages/client/src/components/hermes/chat/MessageList.vue index 9fe4509..b1311f8 100644 --- a/packages/client/src/components/hermes/chat/MessageList.vue +++ b/packages/client/src/components/hermes/chat/MessageList.vue @@ -1,58 +1,75 @@ - {{ t('chat.emptyState') }} + {{ t("chat.emptyState") }} - + - + - + {{ tc.toolName }} - {{ tc.toolPreview }} - - {{ t('chat.error') }} + {{ + tc.toolPreview + }} + + {{ + t("chat.error") + }} @@ -82,7 +108,7 @@ watch(currentToolCalls, scrollToBottom) diff --git a/packages/client/src/components/hermes/jobs/JobCard.vue b/packages/client/src/components/hermes/jobs/JobCard.vue index be6be2a..f2afbcb 100644 --- a/packages/client/src/components/hermes/jobs/JobCard.vue +++ b/packages/client/src/components/hermes/jobs/JobCard.vue @@ -152,7 +152,7 @@ async function handleDelete() { transition: border-color $transition-fast; &:hover { - border-color: rgba($accent-primary, 0.3); + border-color: rgba(var(--accent-primary-rgb), 0.3); } } @@ -180,22 +180,22 @@ async function handleDelete() { font-weight: 500; &.success { - background: rgba($success, 0.12); + background: rgba(var(--success-rgb), 0.12); color: $success; } &.info { - background: rgba($accent-primary, 0.12); + background: rgba(var(--accent-primary-rgb), 0.12); color: $accent-primary; } &.warning { - background: rgba($warning, 0.12); + background: rgba(var(--warning-rgb), 0.12); color: $warning; } &.error { - background: rgba($error, 0.12); + background: rgba(var(--error-rgb), 0.12); color: $error; } } diff --git a/packages/client/src/components/hermes/models/ProviderCard.vue b/packages/client/src/components/hermes/models/ProviderCard.vue index 96af61f..a31a80a 100644 --- a/packages/client/src/components/hermes/models/ProviderCard.vue +++ b/packages/client/src/components/hermes/models/ProviderCard.vue @@ -70,7 +70,7 @@ async function handleDelete() { transition: border-color $transition-fast; &:hover { - border-color: rgba($accent-primary, 0.3); + border-color: rgba(var(--accent-primary-rgb), 0.3); } } @@ -98,12 +98,12 @@ async function handleDelete() { font-weight: 500; &.builtin { - background: rgba($accent-primary, 0.12); + background: rgba(var(--accent-primary-rgb), 0.12); color: $accent-primary; } &.custom { - background: rgba($success, 0.12); + background: rgba(var(--success-rgb), 0.12); color: $success; } } diff --git a/packages/client/src/components/hermes/profiles/ProfileCard.vue b/packages/client/src/components/hermes/profiles/ProfileCard.vue index 69db9f4..94466f8 100644 --- a/packages/client/src/components/hermes/profiles/ProfileCard.vue +++ b/packages/client/src/components/hermes/profiles/ProfileCard.vue @@ -181,11 +181,11 @@ async function handleExport() { transition: border-color $transition-fast; &:hover { - border-color: rgba($accent-primary, 0.3); + border-color: rgba(var(--accent-primary-rgb), 0.3); } &.active { - border-color: rgba($success, 0.4); + border-color: rgba(var(--success-rgb), 0.4); } } diff --git a/packages/client/src/components/hermes/settings/DisplaySettings.vue b/packages/client/src/components/hermes/settings/DisplaySettings.vue index 38b96a8..35491ad 100644 --- a/packages/client/src/components/hermes/settings/DisplaySettings.vue +++ b/packages/client/src/components/hermes/settings/DisplaySettings.vue @@ -1,12 +1,20 @@ + + + save({ streaming: v })" /> diff --git a/packages/client/src/components/hermes/settings/PlatformCard.vue b/packages/client/src/components/hermes/settings/PlatformCard.vue index d541b7f..62b8d49 100644 --- a/packages/client/src/components/hermes/settings/PlatformCard.vue +++ b/packages/client/src/components/hermes/settings/PlatformCard.vue @@ -57,7 +57,7 @@ const configured = computed(() => { overflow: hidden; &.configured { - border-color: rgba($success, 0.2); + border-color: rgba(var(--success-rgb), 0.2); } } @@ -70,7 +70,7 @@ const configured = computed(() => { user-select: none; &:hover { - background-color: rgba($text-primary, 0.03); + background-color: rgba(var(--text-primary-rgb), 0.03); } } diff --git a/packages/client/src/components/hermes/skills/SkillDetail.vue b/packages/client/src/components/hermes/skills/SkillDetail.vue index 7225aad..54b1262 100644 --- a/packages/client/src/components/hermes/skills/SkillDetail.vue +++ b/packages/client/src/components/hermes/skills/SkillDetail.vue @@ -185,7 +185,7 @@ watch(() => `${props.category}/${props.skill}`, loadSkill, { immediate: true }) border-radius: 4px; &:hover { - background: rgba($accent-primary, 0.06); + background: rgba(var(--accent-primary-rgb), 0.06); } } diff --git a/packages/client/src/components/hermes/skills/SkillList.vue b/packages/client/src/components/hermes/skills/SkillList.vue index 4a7eb00..1812776 100644 --- a/packages/client/src/components/hermes/skills/SkillList.vue +++ b/packages/client/src/components/hermes/skills/SkillList.vue @@ -150,7 +150,7 @@ async function handleToggle(category: string, skillName: string, newEnabled: boo border-radius: $radius-sm; &:hover { - background: rgba($accent-primary, 0.04); + background: rgba(var(--accent-primary-rgb), 0.04); } } @@ -174,7 +174,7 @@ async function handleToggle(category: string, skillName: string, newEnabled: boo .category-count { font-size: 11px; color: $text-muted; - background: rgba($accent-primary, 0.06); + background: rgba(var(--accent-primary-rgb), 0.06); padding: 1px 6px; border-radius: 8px; } @@ -200,12 +200,12 @@ async function handleToggle(category: string, skillName: string, newEnabled: boo gap: 8px; &:hover { - background: rgba($accent-primary, 0.06); + background: rgba(var(--accent-primary-rgb), 0.06); color: $text-primary; } &.active { - background: rgba($accent-primary, 0.1); + background: rgba(var(--accent-primary-rgb), 0.1); color: $text-primary; font-weight: 500; } diff --git a/packages/client/src/components/hermes/usage/DailyTrend.vue b/packages/client/src/components/hermes/usage/DailyTrend.vue index 7e11310..630dd8f 100644 --- a/packages/client/src/components/hermes/usage/DailyTrend.vue +++ b/packages/client/src/components/hermes/usage/DailyTrend.vue @@ -127,6 +127,10 @@ import { computed } from 'vue' border-radius: 2px 2px 0 0; min-height: 0; transition: height 0.3s ease; + + .dark & { + background: #66bb6a; + } } .bar-col { @@ -140,7 +144,7 @@ import { computed } from 'vue' left: 50%; transform: translateX(-50%); background: $text-primary; - color: #fff; + color: var(--text-on-accent); padding: 6px 10px; border-radius: $radius-sm; font-size: 11px; diff --git a/packages/client/src/components/hermes/usage/ModelBreakdown.vue b/packages/client/src/components/hermes/usage/ModelBreakdown.vue index 0290391..e15437f 100644 --- a/packages/client/src/components/hermes/usage/ModelBreakdown.vue +++ b/packages/client/src/components/hermes/usage/ModelBreakdown.vue @@ -85,6 +85,10 @@ function formatTokens(n: number): string { border-radius: 3px; min-width: 2px; transition: width 0.3s ease; + + .dark & { + background: #66bb6a; + } } .model-tokens { diff --git a/packages/client/src/components/layout/AppSidebar.vue b/packages/client/src/components/layout/AppSidebar.vue index dae2e73..ed4933f 100644 --- a/packages/client/src/components/layout/AppSidebar.vue +++ b/packages/client/src/components/layout/AppSidebar.vue @@ -1,5 +1,5 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/client/src/composables/useTheme.ts b/packages/client/src/composables/useTheme.ts new file mode 100644 index 0000000..3403de7 --- /dev/null +++ b/packages/client/src/composables/useTheme.ts @@ -0,0 +1,57 @@ +import { ref, watch } from 'vue' + +export type ThemeMode = 'light' | 'dark' | 'system' + +const STORAGE_KEY = 'hermes_theme' + +const mode = ref( + (localStorage.getItem(STORAGE_KEY) as ThemeMode) || 'system', +) + +const isDark = ref(false) + +function applyTheme(dark: boolean) { + isDark.value = dark + document.documentElement.classList.toggle('dark', dark) +} + +function resolveDark(m: ThemeMode): boolean { + if (m === 'system') { + return window.matchMedia('(prefers-color-scheme: dark)').matches + } + return m === 'dark' +} + +// Initial resolve +applyTheme(resolveDark(mode.value)) + +// Listen for system preference changes +const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)') +mediaQuery.addEventListener('change', () => { + if (mode.value === 'system') { + applyTheme(true) + } +}) + +// Watch mode changes +watch(mode, (newMode) => { + localStorage.setItem(STORAGE_KEY, newMode) + applyTheme(resolveDark(newMode)) +}) + +export function useTheme() { + function setMode(m: ThemeMode) { + mode.value = m + } + + function toggleTheme() { + mode.value = isDark.value ? 'light' : 'dark' + } + + return { + mode, + isDark, + setMode, + toggleTheme, + } +} diff --git a/packages/client/src/i18n/locales/en.ts b/packages/client/src/i18n/locales/en.ts index 1705a2d..292cb49 100644 --- a/packages/client/src/i18n/locales/en.ts +++ b/packages/client/src/i18n/locales/en.ts @@ -292,6 +292,11 @@ export default { bellOnCompleteHint: 'Play sound when AI finishes', busyInputMode: 'Busy Input Mode', busyInputModeHint: 'Allow input while AI is processing', + theme: 'Theme', + themeHint: 'Choose light, dark, or follow system preference', + themeLight: 'Light', + themeDark: 'Dark', + themeSystem: 'System', }, agent: { maxTurns: 'Max Turns', diff --git a/packages/client/src/i18n/locales/zh.ts b/packages/client/src/i18n/locales/zh.ts index 2c832e5..280f93d 100644 --- a/packages/client/src/i18n/locales/zh.ts +++ b/packages/client/src/i18n/locales/zh.ts @@ -292,6 +292,11 @@ export default { bellOnCompleteHint: 'AI 回复完成时播放提示音', busyInputMode: '忙碌输入模式', busyInputModeHint: 'AI 处理中仍可输入', + theme: '主题', + themeHint: '选择浅色、暗色或跟随系统', + themeLight: '浅色', + themeDark: '暗色', + themeSystem: '跟随系统', }, agent: { maxTurns: '最大轮次', diff --git a/packages/client/src/main.ts b/packages/client/src/main.ts index 24069c1..c683d7a 100644 --- a/packages/client/src/main.ts +++ b/packages/client/src/main.ts @@ -5,6 +5,13 @@ import { i18n } from './i18n' import App from './App.vue' import './styles/global.scss' +// Apply dark class before mount to prevent FOUC +const savedTheme = localStorage.getItem('hermes_theme') || 'system' +const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches +if (savedTheme === 'dark' || (savedTheme === 'system' && prefersDark)) { + document.documentElement.classList.add('dark') +} + // Read token from URL BEFORE router initializes (hash router strips params) const urlParams = new URLSearchParams(window.location.search) const hashQuery = window.location.hash.split('?')[1] diff --git a/packages/client/src/styles/global.scss b/packages/client/src/styles/global.scss index 1405838..cda21ab 100644 --- a/packages/client/src/styles/global.scss +++ b/packages/client/src/styles/global.scss @@ -19,6 +19,15 @@ } } +// Theme transition (applied programmatically on toggle, not on load) +html.theme-transitioning, +html.theme-transitioning *, +html.theme-transitioning *::before, +html.theme-transitioning *::after { + transition: background-color 0.3s ease, color 0.3s ease, border-color 0.3s ease, + box-shadow 0.3s ease, fill 0.3s ease, stroke 0.3s ease !important; +} + html, body, #app { height: 100%; width: 100%; @@ -67,7 +76,7 @@ a { } ::selection { - background: rgba($accent-primary, 0.3); + background: rgba(var(--accent-primary-rgb), 0.3); } // Shared page header diff --git a/packages/client/src/styles/theme.ts b/packages/client/src/styles/theme.ts index 75b63ac..26ceaac 100644 --- a/packages/client/src/styles/theme.ts +++ b/packages/client/src/styles/theme.ts @@ -1,6 +1,6 @@ import type { GlobalThemeOverrides } from 'naive-ui' -export const themeOverrides: GlobalThemeOverrides = { +export const lightThemeOverrides: GlobalThemeOverrides = { common: { primaryColor: '#333333', primaryColorHover: '#1a1a1a', @@ -69,3 +69,83 @@ export const themeOverrides: GlobalThemeOverrides = { borderRadius: '6px', }, } + +export const darkThemeOverrides: GlobalThemeOverrides = { + common: { + primaryColor: '#e0e0e0', + primaryColorHover: '#f5f5f5', + primaryColorPressed: '#ffffff', + primaryColorSuppl: '#e0e0e0', + bodyColor: '#1a1a1a', + cardColor: '#2a2a2a', + modalColor: '#2a2a2a', + popoverColor: '#2a2a2a', + tableColor: '#2a2a2a', + inputColor: '#2a2a2a', + actionColor: '#252525', + textColorBase: '#e0e0e0', + textColor1: '#e0e0e0', + textColor2: '#a0a0a0', + textColor3: '#666666', + dividerColor: '#3a3a3a', + borderColor: '#3a3a3a', + hoverColor: 'rgba(255, 255, 255, 0.06)', + borderRadius: '8px', + borderRadiusSmall: '6px', + fontSize: '14px', + fontSizeMedium: '14px', + heightMedium: '36px', + fontFamily: 'Inter, system-ui, -apple-system, sans-serif', + fontFamilyMono: 'JetBrains Mono, Fira Code, Consolas, monospace', + }, + Layout: { + color: '#1a1a1a', + siderColor: '#202020', + headerColor: '#1a1a1a', + }, + Menu: { + itemTextColorActive: '#e0e0e0', + itemTextColorActiveHover: '#e0e0e0', + itemTextColorChildActive: '#e0e0e0', + itemIconColorActive: '#e0e0e0', + itemIconColorActiveHover: '#ffffff', + itemColorActive: 'rgba(255, 255, 255, 0.08)', + itemColorActiveHover: 'rgba(255, 255, 255, 0.12)', + arrowColorActive: '#e0e0e0', + }, + Button: { + textColorPrimary: '#1a1a1a', + colorPrimary: '#e0e0e0', + colorHoverPrimary: '#f5f5f5', + colorPressedPrimary: '#ffffff', + }, + Input: { + color: '#2a2a2a', + colorFocus: '#2a2a2a', + border: '1px solid #3a3a3a', + borderHover: '1px solid #666666', + borderFocus: '1px solid #e0e0e0', + placeholderColor: '#666666', + caretColor: '#e0e0e0', + }, + Card: { + color: '#2a2a2a', + borderColor: '#3a3a3a', + }, + Modal: { + color: '#2a2a2a', + }, + Tag: { + borderRadius: '6px', + }, + Switch: { + railColor: '#3a3a3a', + railColorActive: '#66bb6a', + loadingColor: '#e0e0e0', + opacityDisabled: 0.4, + }, +} + +export function getThemeOverrides(isDark: boolean): GlobalThemeOverrides { + return isDark ? darkThemeOverrides : lightThemeOverrides +} diff --git a/packages/client/src/styles/variables.scss b/packages/client/src/styles/variables.scss index eada616..79b433f 100644 --- a/packages/client/src/styles/variables.scss +++ b/packages/client/src/styles/variables.scss @@ -1,41 +1,150 @@ // 黑白水墨 — Pure Ink // 纯黑白灰,无彩色 +// 支持 light / dark 双主题 + +// ─── CSS Custom Properties ───────────────────────────────────── + +:root { + // Backgrounds + --bg-primary: #fafafa; + --bg-secondary: #f0f0f0; + --bg-sidebar: #f5f5f5; + --bg-card: #ffffff; + --bg-card-hover: #fafafa; + --bg-input: #ffffff; + + // Borders + --border-color: #e0e0e0; + --border-light: #ebebeb; + + // Accent + --accent-primary: #333333; + --accent-hover: #1a1a1a; + --accent-muted: #888888; + + // Text + --text-primary: #1a1a1a; + --text-secondary: #666666; + --text-muted: #999999; + + // Status + --success: #2e7d32; + --error: #c62828; + --warning: #f57f17; + + // Message bubbles + --msg-user-bg: #f5f5f5; + --msg-assistant-bg: #f5f5f5; + --msg-system-border: #bdbdbd; + + // Code + --code-bg: #f4f4f4; + + // Utility + --text-on-accent: #ffffff; + --text-on-overlay: #ffffff; + --accent-info: #4a90d9; + + // RGB components (for rgba() usage) + --accent-primary-rgb: 51, 51, 51; + --accent-hover-rgb: 26, 26, 26; + --text-primary-rgb: 26, 26, 26; + --text-muted-rgb: 153, 153, 153; + --success-rgb: 46, 125, 50; + --error-rgb: 198, 40, 40; + --warning-rgb: 245, 127, 23; + --accent-info-rgb: 74, 144, 217; +} + +.dark { + // Backgrounds + --bg-primary: #1a1a1a; + --bg-secondary: #252525; + --bg-sidebar: #202020; + --bg-card: #333333; + --bg-card-hover: #333333; + --bg-input: #2a2a2a; + + // Borders + --border-color: #3a3a3a; + --border-light: #333333; + + // Accent + --accent-primary: #e0e0e0; + --accent-hover: #f5f5f5; + --accent-muted: #888888; + + // Text + --text-primary: #e0e0e0; + --text-secondary: #a0a0a0; + --text-muted: #666666; + + // Status + --success: #66bb6a; + --error: #ef5350; + --warning: #ffb74d; + + // Message bubbles + --msg-user-bg: #252525; + --msg-assistant-bg: #252525; + --msg-system-border: #555555; + + // Code + --code-bg: #1e1e1e; + + // Utility + --text-on-accent: #1a1a1a; + --text-on-overlay: #ffffff; + --accent-info: #6ba3d6; + + // RGB components + --accent-primary-rgb: 224, 224, 224; + --accent-hover-rgb: 245, 245, 245; + --text-primary-rgb: 224, 224, 224; + --text-muted-rgb: 102, 102, 102; + --success-rgb: 102, 187, 106; + --error-rgb: 239, 83, 80; + --warning-rgb: 255, 183, 77; + --accent-info-rgb: 107, 163, 214; +} + +// ─── SCSS Variables (delegate to CSS custom properties) ──────── // Backgrounds -$bg-primary: #fafafa; -$bg-secondary: #f0f0f0; -$bg-sidebar: #f5f5f5; -$bg-card: #ffffff; -$bg-card-hover: #fafafa; -$bg-input: #ffffff; +$bg-primary: var(--bg-primary); +$bg-secondary: var(--bg-secondary); +$bg-sidebar: var(--bg-sidebar); +$bg-card: var(--bg-card); +$bg-card-hover: var(--bg-card-hover); +$bg-input: var(--bg-input); // Borders -$border-color: #e0e0e0; -$border-light: #ebebeb; +$border-color: var(--border-color); +$border-light: var(--border-light); // Accent -$accent-primary: #333333; -$accent-hover: #1a1a1a; -$accent-muted: #888888; +$accent-primary: var(--accent-primary); +$accent-hover: var(--accent-hover); +$accent-muted: var(--accent-muted); // Text -$text-primary: #1a1a1a; -$text-secondary: #666666; -$text-muted: #999999; +$text-primary: var(--text-primary); +$text-secondary: var(--text-secondary); +$text-muted: var(--text-muted); // Status -$success: #2e7d32; -$error: #c62828; -$warning: #f57f17; +$success: var(--success); +$error: var(--error); +$warning: var(--warning); $info: $accent-primary; // Message bubbles -$msg-user-bg: #e8e8e8; -$msg-assistant-bg: #f5f5f5; -$msg-system-border: #bdbdbd; +$msg-user-bg: var(--msg-user-bg); +$msg-assistant-bg: var(--msg-assistant-bg); +$msg-system-border: var(--msg-system-border); // Code -$code-bg: #f4f4f4; +$code-bg: var(--code-bg); // Typography $font-ui: 'Inter', system-ui, -apple-system, sans-serif; diff --git a/packages/client/src/views/LoginView.vue b/packages/client/src/views/LoginView.vue index 1d632fe..ebfe6d8 100644 --- a/packages/client/src/views/LoginView.vue +++ b/packages/client/src/views/LoginView.vue @@ -159,7 +159,7 @@ async function handleLogin() { border: none; border-radius: $radius-sm; background: $text-primary; - color: #fff; + color: var(--text-on-accent); font-size: 15px; font-weight: 500; cursor: pointer; diff --git a/packages/client/src/views/hermes/LogsView.vue b/packages/client/src/views/hermes/LogsView.vue index 749570e..f2752b1 100644 --- a/packages/client/src/views/hermes/LogsView.vue +++ b/packages/client/src/views/hermes/LogsView.vue @@ -215,7 +215,7 @@ onMounted(async () => { border-left: 2px solid transparent; &:hover { - background-color: rgba($accent-primary, 0.03); + background-color: rgba(var(--accent-primary-rgb), 0.03); } &.level-error { @@ -225,7 +225,7 @@ onMounted(async () => { &.level-warning { border-left-color: $warning; - .log-message { color: #d9720f; } + .log-message { color: $warning; } } } @@ -244,10 +244,10 @@ onMounted(async () => { min-width: 42px; text-align: center; - &.level-error { background: rgba($error, 0.12); color: $error; } - &.level-warning { background: rgba($warning, 0.12); color: #d9720f; } - &.level-debug { background: rgba($accent-primary, 0.06); color: $text-muted; } - &.level-info { background: rgba($accent-primary, 0.06); color: $text-muted; } + &.level-error { background: rgba(var(--error-rgb), 0.12); color: $error; } + &.level-warning { background: rgba(var(--warning-rgb), 0.12); color: $warning; } + &.level-debug { background: rgba(var(--accent-primary-rgb), 0.06); color: $text-muted; } + &.level-info { background: rgba(var(--accent-primary-rgb), 0.06); color: $text-muted; } } .log-logger { diff --git a/packages/client/src/views/hermes/SkillsView.vue b/packages/client/src/views/hermes/SkillsView.vue index 84ef9bc..544122d 100644 --- a/packages/client/src/views/hermes/SkillsView.vue +++ b/packages/client/src/views/hermes/SkillsView.vue @@ -163,7 +163,7 @@ function handleSelect(category: string, skill: string) { border-radius: $radius-sm; &:hover { - background: rgba($accent-primary, 0.06); + background: rgba(var(--accent-primary-rgb), 0.06); } } diff --git a/packages/client/src/views/hermes/TerminalView.vue b/packages/client/src/views/hermes/TerminalView.vue index 51b828b..eb64b12 100644 --- a/packages/client/src/views/hermes/TerminalView.vue +++ b/packages/client/src/views/hermes/TerminalView.vue @@ -562,7 +562,7 @@ onUnmounted(() => { margin-bottom: 2px; &:hover { - background: rgba($accent-primary, 0.06); + background: rgba(var(--accent-primary-rgb), 0.06); color: $text-primary; .session-item-delete { @@ -571,7 +571,7 @@ onUnmounted(() => { } &.active { - background: rgba($accent-primary, 0.1); + background: rgba(var(--accent-primary-rgb), 0.1); color: $text-primary; font-weight: 500; } @@ -604,7 +604,7 @@ onUnmounted(() => { .session-item-shell { font-size: 10px; color: $accent-primary; - background: rgba($accent-primary, 0.08); + background: rgba(var(--accent-primary-rgb), 0.08); padding: 0 5px; border-radius: 3px; line-height: 16px; @@ -634,7 +634,7 @@ onUnmounted(() => { &:hover { color: $error; - background: rgba($error, 0.1); + background: rgba(var(--error-rgb), 0.1); } } @@ -648,7 +648,7 @@ onUnmounted(() => { border-radius: $radius-sm; &:hover { - background: rgba($accent-primary, 0.06); + background: rgba(var(--accent-primary-rgb), 0.06); } }
{{ t('chat.emptyState') }}
{{ t("chat.emptyState") }}