adopt CYBER neon-dark theme tokens
Replace the Pure Ink light/dark/comic theme system with the CYBER single-theme dark palette from the reviewed prototype: OKLCH surfaces, one neon-cyan accent, and monospace-first typography. Arco components were reading colors from arco.css because no ConfigProvider was mounted, so the theme values in arco-theme.ts never took effect. Bridge CYBER into Arco through its --color-* variables in global.scss instead, and align arco-theme.ts to match. Brightness and style switches are gone: useTheme now pins dark, the comic font faces and locale keys are removed, and ThemeSwitch.vue is deleted along with its sidebar slot and display setting.
This commit is contained in:
@@ -3,7 +3,6 @@ import { computed } from 'vue'
|
||||
import { useMessage } from '@/composables/useMessage'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useSettingsStore } from '@/stores/hermes/settings'
|
||||
import { useTheme, type BrightnessMode } from '@/composables/useTheme'
|
||||
import { requestCompletionNotificationPermission, showCompletionNotification, type CompletionNotificationPermissionResult } from '@/utils/completion-notification'
|
||||
import { clampChatInputHeight, MAX_CHAT_INPUT_HEIGHT, MIN_CHAT_INPUT_HEIGHT } from '@/utils/chat-input-height'
|
||||
import SettingRow from './SettingRow.vue'
|
||||
@@ -11,13 +10,7 @@ import SettingRow from './SettingRow.vue'
|
||||
const settingsStore = useSettingsStore()
|
||||
const message = useMessage()
|
||||
const { t } = useI18n()
|
||||
const { brightness, setBrightness } = useTheme()
|
||||
|
||||
const themeOptions = [
|
||||
{ label: t('settings.display.themeLight'), value: 'light' },
|
||||
{ label: t('settings.display.themeDark'), value: 'dark' },
|
||||
{ label: t('settings.display.themeSystem'), value: 'system' },
|
||||
]
|
||||
const chatInputHeight = computed(() => clampChatInputHeight(settingsStore.display.chat_input_height))
|
||||
|
||||
async function save(values: Record<string, any>) {
|
||||
@@ -29,12 +22,6 @@ async function save(values: Record<string, any>) {
|
||||
}
|
||||
}
|
||||
|
||||
function handleThemeChange(val: string) {
|
||||
const m = val as BrightnessMode
|
||||
setBrightness(m)
|
||||
save({ skin: m })
|
||||
}
|
||||
|
||||
function handleChatInputHeightChange(value: number | null) {
|
||||
return save({ chat_input_height: clampChatInputHeight(value) })
|
||||
}
|
||||
@@ -90,9 +77,6 @@ async function testCompletionNotification() {
|
||||
|
||||
<template>
|
||||
<section class="settings-section">
|
||||
<SettingRow :label="t('settings.display.theme')" :hint="t('settings.display.themeHint')">
|
||||
<a-select :model-value="brightness" :options="themeOptions" size="small" class="input-sm" @update:model-value="v => handleThemeChange(String(v))" />
|
||||
</SettingRow>
|
||||
<SettingRow :label="t('settings.display.streaming')" :hint="t('settings.display.streamingHint')">
|
||||
<a-switch :model-value="settingsStore.display.streaming" @update:model-value="v => save({ streaming: v })" />
|
||||
</SettingRow>
|
||||
|
||||
@@ -9,7 +9,6 @@ import RouteLinkItem from '@/components/common/RouteLinkItem.vue'
|
||||
import ModelSelector from "@/components/layout/ModelSelector.vue";
|
||||
import ProfileSelector from "@/components/layout/ProfileSelector.vue";
|
||||
import LanguageSwitch from "@/components/layout/LanguageSwitch.vue";
|
||||
import ThemeSwitch from "@/components/layout/ThemeSwitch.vue";
|
||||
import { changelog } from "@/data/changelog";
|
||||
import { getStoredUsername, isStoredSuperAdmin } from "@/api/client";
|
||||
|
||||
@@ -350,7 +349,6 @@ function handleUpdateClick() {
|
||||
>
|
||||
Studio v{{ appStore.serverVersion || "0.1.0" }}
|
||||
</span>
|
||||
<ThemeSwitch />
|
||||
</div>
|
||||
<a-button v-if="appStore.clientOutdated" type="primary" status="warning" size="mini" long class="update-btn" @click="handleReloadClient">
|
||||
{{ t('sidebar.reloadClientVersion', { version: appStore.serverVersion }) }}
|
||||
@@ -662,10 +660,6 @@ function handleUpdateClick() {
|
||||
}
|
||||
}
|
||||
|
||||
.version-info :deep(.theme-switch-container) {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.update-btn {
|
||||
margin: 4px 0 0;
|
||||
border-radius: $radius-sm;
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import { useTheme } from '@/composables/useTheme'
|
||||
|
||||
const { isDark, isComic, toggleBrightness, toggleStyle } = useTheme()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="theme-switch-container" style="display: flex; gap: 4px; align-items: center;">
|
||||
<button class="theme-switch" :title="isComic ? 'Ink style' : 'Comic style'" @click="toggleStyle">
|
||||
<!-- Palette icon for comic toggle -->
|
||||
<svg v-if="isComic" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M12 2.69l5.66 5.66a8 8 0 1 1-11.31 0z" />
|
||||
</svg>
|
||||
<!-- Sparkle icon for ink mode -->
|
||||
<svg v-else width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M12 3l1.5 5.5L19 10l-5.5 1.5L12 17l-1.5-5.5L5 10l5.5-1.5L12 3z" />
|
||||
</svg>
|
||||
</button>
|
||||
<button class="theme-switch" :title="isDark ? 'Light mode' : 'Dark mode'" @click="toggleBrightness">
|
||||
<!-- Sun icon (shown in dark mode) -->
|
||||
<svg v-if="isDark" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<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>
|
||||
<!-- Moon icon (shown in light mode) -->
|
||||
<svg v-else width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.theme-switch {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
border: none;
|
||||
background: transparent;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
color: var(--text-muted);
|
||||
transition: color 0.15s ease, background-color 0.15s ease;
|
||||
|
||||
&:hover {
|
||||
color: var(--text-primary);
|
||||
background: rgba(var(--accent-primary-rgb), 0.06);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,91 +1,28 @@
|
||||
import { ref, watch, computed } from 'vue'
|
||||
import { ref, computed } from 'vue'
|
||||
|
||||
export type BrightnessMode = 'light' | 'dark' | 'system'
|
||||
export type ThemeStyle = 'ink' | 'comic'
|
||||
// CYBER 是单色暗色主题:亮度与风格切换器已移除(comic 与 light 主题不再提供)。
|
||||
// 这里保留 isDark 以兼容 App.vue / JourneyView 里的 :class 绑定,值恒为 true。
|
||||
const isDark = ref(true)
|
||||
|
||||
const BRIGHTNESS_KEY = 'hermes_brightness'
|
||||
const STYLE_KEY = 'hermes_style'
|
||||
export type BrightnessMode = 'dark'
|
||||
|
||||
const brightness = ref<BrightnessMode>(
|
||||
(localStorage.getItem(BRIGHTNESS_KEY) as BrightnessMode) || 'system',
|
||||
)
|
||||
const brightness = ref<BrightnessMode>('dark')
|
||||
|
||||
const style = ref<ThemeStyle>(
|
||||
(localStorage.getItem(STYLE_KEY) as ThemeStyle) || 'ink',
|
||||
)
|
||||
|
||||
const isDark = ref(false)
|
||||
const isComic = ref(false)
|
||||
|
||||
function resolveDark(b: BrightnessMode): boolean {
|
||||
if (b === 'system') {
|
||||
return window.matchMedia('(prefers-color-scheme: dark)').matches
|
||||
}
|
||||
return b === 'dark'
|
||||
}
|
||||
|
||||
function applyClasses() {
|
||||
const dark = resolveDark(brightness.value)
|
||||
isDark.value = dark
|
||||
isComic.value = style.value === 'comic'
|
||||
document.documentElement.classList.toggle('dark', dark)
|
||||
document.documentElement.classList.toggle('comic', isComic.value)
|
||||
// Arco Design dark theme mechanism (Phase 005)
|
||||
document.documentElement.setAttribute('arco-theme', dark ? 'dark' : 'light')
|
||||
}
|
||||
|
||||
// Initial
|
||||
applyClasses()
|
||||
|
||||
// Listen for system preference changes
|
||||
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => {
|
||||
if (brightness.value === 'system') {
|
||||
applyClasses()
|
||||
}
|
||||
})
|
||||
|
||||
// Persist & apply on change
|
||||
watch(brightness, (b) => {
|
||||
localStorage.setItem(BRIGHTNESS_KEY, b)
|
||||
applyClasses()
|
||||
})
|
||||
|
||||
watch(style, (s) => {
|
||||
localStorage.setItem(STYLE_KEY, s)
|
||||
applyClasses()
|
||||
})
|
||||
// 固定应用暗色:Arco 的 token 通过 arco-theme 属性切换,项目的令牌写在 :root。
|
||||
document.documentElement.setAttribute('arco-theme', 'dark')
|
||||
|
||||
export function useTheme() {
|
||||
const themeName = computed(() => {
|
||||
const b = isDark.value ? 'dark' : 'light'
|
||||
return isComic.value ? `comic-${b}` : b
|
||||
})
|
||||
const themeName = computed(() => 'dark')
|
||||
|
||||
function setBrightness(b: BrightnessMode) {
|
||||
brightness.value = b
|
||||
}
|
||||
|
||||
function setStyle(s: ThemeStyle) {
|
||||
style.value = s
|
||||
}
|
||||
|
||||
function toggleBrightness() {
|
||||
brightness.value = isDark.value ? 'light' : 'dark'
|
||||
}
|
||||
|
||||
function toggleStyle() {
|
||||
style.value = isComic.value ? 'ink' : 'comic'
|
||||
function setBrightness(_b: BrightnessMode) {
|
||||
// 单主题:忽略任何切换请求。
|
||||
brightness.value = 'dark'
|
||||
}
|
||||
|
||||
return {
|
||||
brightness,
|
||||
style,
|
||||
isDark,
|
||||
isComic,
|
||||
themeName,
|
||||
setBrightness,
|
||||
setStyle,
|
||||
toggleBrightness,
|
||||
toggleStyle,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1575,11 +1575,6 @@ export default {
|
||||
notifyOnCompleteTestFailed: 'Failed to send test notification',
|
||||
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',
|
||||
chatInputHeight: 'Chat input height',
|
||||
chatInputHeightHint: 'Default chat input height in pixels on wide screens. Mobile keeps auto height.'
|
||||
},
|
||||
|
||||
@@ -1567,11 +1567,6 @@ export default {
|
||||
notifyOnCompleteTestFailed: '测试通知发送失败',
|
||||
busyInputMode: '忙碌输入模式',
|
||||
busyInputModeHint: 'AI 处理中仍可输入',
|
||||
theme: '主题',
|
||||
themeHint: '选择浅色、暗色或跟随系统',
|
||||
themeLight: '浅色',
|
||||
themeDark: '暗色',
|
||||
themeSystem: '跟随系统',
|
||||
chatInputHeight: '聊天输入框高度',
|
||||
chatInputHeightHint: '桌面端聊天输入框默认高度(像素)。移动端保持自动高度。'
|
||||
},
|
||||
|
||||
@@ -8,24 +8,8 @@ import App from './App.vue'
|
||||
import './styles/global.scss'
|
||||
import 'katex/dist/katex.min.css'
|
||||
|
||||
// Apply theme classes before mount to prevent FOUC (Flash of Unstyled Content)
|
||||
const savedBrightness = localStorage.getItem('hermes_brightness') || 'system'
|
||||
const savedStyle = localStorage.getItem('hermes_style') || 'ink'
|
||||
|
||||
// Resolve dark mode
|
||||
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches
|
||||
const isDark = savedBrightness === 'dark' || (savedBrightness === 'system' && prefersDark)
|
||||
|
||||
// Resolve style
|
||||
const isComic = savedStyle === 'comic'
|
||||
|
||||
// Apply classes to prevent FOUC
|
||||
if (isDark) {
|
||||
document.documentElement.classList.add('dark')
|
||||
}
|
||||
if (isComic) {
|
||||
document.documentElement.classList.add('comic')
|
||||
}
|
||||
// CYBER 单主题:固定暗色,无需读取 localStorage 或匹配系统偏好。
|
||||
document.documentElement.setAttribute('arco-theme', 'dark')
|
||||
|
||||
// Read token from URL BEFORE router initializes (hash router strips params)
|
||||
const urlParams = new URLSearchParams(window.location.search)
|
||||
|
||||
@@ -1,14 +1,20 @@
|
||||
// Arco Design theme tokens for hermes-ui (Phase 005 baseline).
|
||||
// Dark mode is carried by `body[arco-theme='dark']` CSS overrides, not a second theme object.
|
||||
// See docs/ui-baseline.md for the frozen visual language.
|
||||
// Arco Design theme tokens for hermes-ui — CYBER.
|
||||
//
|
||||
// 这些值是「参考值」,不是运行时来源:项目没有挂 ConfigProvider,Arco 的实际外观
|
||||
// 由 styles/global.scss 里的 --color-* 变量覆盖决定(见那里的 Arco token bridge)。
|
||||
// 保留本文件是为了让读取 token 的人能在一处看到意图,以及给测试一个可断言的锚点。
|
||||
// 改色时请同时改 global.scss 的 Arco token bridge,两处保持一致。
|
||||
export const arcoTheme = {
|
||||
// 延续旧版克制灰阶
|
||||
colorPrimary: '#2f2f2f',
|
||||
colorBgLayout: '#fafafa',
|
||||
colorBgContainer: '#ffffff',
|
||||
// 霓虹青 oklch(0.805 0.148 188) ≈ rgb(45, 212, 191)
|
||||
colorPrimary: '#2dd4bf',
|
||||
// 页面底 / 卡片底,对应 oklch(0.155 0.012 250) 与 oklch(0.130 0.012 250)
|
||||
colorBgLayout: '#1a1d23',
|
||||
colorBgContainer: '#14171c',
|
||||
borderRadius: 6,
|
||||
fontFamily: 'Inter, system-ui, -apple-system, sans-serif',
|
||||
fontFamilyMono: '"JetBrains Mono", "Fira Code", monospace',
|
||||
fontFamily:
|
||||
'-apple-system, BlinkMacSystemFont, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Segoe UI", Roboto, sans-serif',
|
||||
fontFamilyMono:
|
||||
'ui-monospace, "JetBrains Mono", "SF Mono", Menlo, "Cascadia Code", monospace',
|
||||
}
|
||||
|
||||
export type ArcoTheme = typeof arcoTheme
|
||||
|
||||
@@ -1,54 +1,6 @@
|
||||
@use 'variables' as *;
|
||||
@use 'code-block';
|
||||
|
||||
@font-face {
|
||||
font-family: 'Comic Neue';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url('/fonts/ComicNeue-Bold.ttf') format('truetype');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'ZCOOL KuaiLe';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url('/fonts/ZCOOLKuaiLe-Regular.ttf') format('truetype');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Zen Maru Gothic';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url('/fonts/ZenMaruGothic-Regular.ttf') format('truetype');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Zen Maru Gothic';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-display: swap;
|
||||
src: url('/fonts/ZenMaruGothic-Bold.ttf') format('truetype');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Gaegu';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url('/fonts/Gaegu-Regular.ttf') format('truetype');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Gaegu';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-display: swap;
|
||||
src: url('/fonts/Gaegu-Bold.ttf') format('truetype');
|
||||
}
|
||||
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
@@ -75,13 +27,98 @@ textarea {
|
||||
}
|
||||
}
|
||||
|
||||
// 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;
|
||||
// ─── Arco Design token bridge ──────────────────────────────────
|
||||
// arco-theme.ts 不是运行时来源(没有 ConfigProvider),Arco 的实际外观由 arco.css
|
||||
// 里的 --color-* 变量决定。这里把 CYBER 令牌翻译成 Arco 变量,让 a-select /
|
||||
// a-input / a-tag / a-table / a-modal 等自有控件与项目配色对齐。
|
||||
// 变量表见 node_modules/@arco-design/web-vue/dist/arco.css 的 :root 块。
|
||||
html[arco-theme='dark'],
|
||||
:root {
|
||||
--color-primary-light-1: oklch(0.30 0.060 188);
|
||||
--color-primary-light-2: oklch(0.39 0.075 188);
|
||||
--color-primary-light-3: oklch(0.50 0.095 188);
|
||||
--color-primary-light-4: oklch(0.60 0.115 188);
|
||||
--color-primary-light-5: oklch(0.68 0.130 188);
|
||||
--color-primary-light-6: oklch(0.805 0.148 188);
|
||||
--color-primary: oklch(0.805 0.148 188);
|
||||
--color-primary-dark-1: oklch(0.760 0.144 188);
|
||||
--color-primary-dark-2: oklch(0.700 0.138 188);
|
||||
--color-primary-dark-3: oklch(0.640 0.130 188);
|
||||
--color-primary-dark-4: oklch(0.580 0.120 188);
|
||||
--color-primary-dark-5: oklch(0.520 0.108 188);
|
||||
--color-primary-dark-6: oklch(0.460 0.094 188);
|
||||
|
||||
--color-success-light-1: oklch(0.28 0.050 165);
|
||||
--color-success-light-2: oklch(0.42 0.078 165);
|
||||
--color-success-light-3: oklch(0.58 0.108 165);
|
||||
--color-success: oklch(0.760 0.140 165);
|
||||
|
||||
--color-warning-light-1: oklch(0.30 0.052 85);
|
||||
--color-warning-light-2: oklch(0.46 0.082 85);
|
||||
--color-warning-light-3: oklch(0.62 0.112 85);
|
||||
--color-warning: oklch(0.810 0.145 85);
|
||||
|
||||
--color-danger-light-1: oklch(0.28 0.062 22);
|
||||
--color-danger-light-2: oklch(0.42 0.096 22);
|
||||
--color-danger-light-3: oklch(0.56 0.138 22);
|
||||
--color-danger: oklch(0.665 0.185 22);
|
||||
|
||||
--color-link-light-1: oklch(0.30 0.060 188);
|
||||
--color-link-light-2: oklch(0.42 0.084 188);
|
||||
--color-link-light-3: oklch(0.54 0.110 188);
|
||||
--color-link-light-4: oklch(0.66 0.136 188);
|
||||
--color-link-light-5: oklch(0.74 0.148 188);
|
||||
--color-link-light-6: oklch(0.805 0.148 188);
|
||||
--color-link: oklch(0.805 0.148 188);
|
||||
|
||||
// 容器与文字
|
||||
--color-text-1: oklch(0.945 0.006 200);
|
||||
--color-text-2: oklch(0.860 0.008 210);
|
||||
--color-text-3: oklch(0.705 0.014 230);
|
||||
--color-text-4: oklch(0.520 0.014 236);
|
||||
--color-text-disabled: oklch(0.420 0.012 238);
|
||||
|
||||
--color-bg-1: oklch(0.155 0.012 250);
|
||||
--color-bg-2: oklch(0.130 0.012 250);
|
||||
--color-bg-3: oklch(0.185 0.014 248);
|
||||
--color-bg-4: oklch(0.225 0.016 246);
|
||||
--color-bg-5: oklch(0.255 0.018 247);
|
||||
--color-fill-1: oklch(0.185 0.014 248);
|
||||
--color-fill-2: oklch(0.225 0.016 246);
|
||||
--color-fill-3: oklch(0.255 0.018 247);
|
||||
--color-fill-4: oklch(0.285 0.018 246);
|
||||
|
||||
--color-border-1: oklch(0.305 0.018 242);
|
||||
--color-border-2: oklch(0.255 0.016 244);
|
||||
--color-border-3: oklch(0.225 0.014 246);
|
||||
--color-border-4: oklch(0.195 0.014 248);
|
||||
|
||||
--color-fill-2-hover: oklch(0.255 0.018 247);
|
||||
--color-fill-3-hover: oklch(0.285 0.018 246);
|
||||
|
||||
--radius-small: 4px;
|
||||
--radius-medium: 6px;
|
||||
--radius-large: 8px;
|
||||
|
||||
--font-size-body-1: 14px;
|
||||
--font-size-body-2: 13px;
|
||||
--font-size-body-3: 12px;
|
||||
|
||||
// CYBER 的核心排版特征:标题、页头、数据用等宽
|
||||
--font-family: #{$font-ui};
|
||||
--font-family-body: #{$font-ui};
|
||||
--font-family-code: #{$font-code};
|
||||
}
|
||||
|
||||
// Arco 暗色态:强制走暗色变量(属性挂在 <html> 上,Arco 官方选择器可能匹配 body)
|
||||
body {
|
||||
color: $text-primary;
|
||||
background-color: $bg-primary;
|
||||
}
|
||||
|
||||
// Arco 组件里的滚动条与焦点环对齐 CYBER
|
||||
.arco-scrollbar-thumb {
|
||||
background-color: $border-color !important;
|
||||
}
|
||||
|
||||
html, body {
|
||||
@@ -146,8 +183,8 @@ a {
|
||||
}
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
@@ -158,10 +195,10 @@ a {
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: $border-color;
|
||||
border-radius: 3px;
|
||||
border-radius: 4px;
|
||||
|
||||
&:hover {
|
||||
background: $text-muted;
|
||||
background: rgba(var(--accent-primary-rgb), 0.5);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,132 +1,64 @@
|
||||
// 黑白水墨 — Pure Ink
|
||||
// 纯黑白灰,无彩色
|
||||
// 支持 light / dark / comic 三主题
|
||||
// CYBER — 霓虹暗色单主题
|
||||
// 设计语言见 prototype_reference/hermes-ui-cyber-api.html 与 hermes-ui-cyber-login.html。
|
||||
// 暗色为唯一主题:亮度/风格切换器已移除,因此这里只有一组令牌,没有 .dark / .comic 变体。
|
||||
// 唯一强调色是霓虹青 oklch(0.805 0.148 188),状态色仅用于语义,不参与装饰。
|
||||
|
||||
// ─── CSS Custom Properties ─────────────────────────────────────
|
||||
|
||||
:root {
|
||||
// Backgrounds
|
||||
--bg-primary: #fafafa;
|
||||
--bg-secondary: #f0f0f0;
|
||||
--bg-sidebar: #f5f5f5;
|
||||
--bg-card: #ffffff;
|
||||
--bg-card-hover: #fafafa;
|
||||
--bg-input: #ffffff;
|
||||
// Backgrounds — 由深到浅的四层
|
||||
--bg-primary: oklch(0.155 0.012 250); // 页面底色
|
||||
--bg-secondary: oklch(0.130 0.012 250); // 卡片 / 面板(比页面更沉,向下压)
|
||||
--bg-sidebar: oklch(0.118 0.013 251); // 侧栏,比卡片更沉
|
||||
--bg-card: oklch(0.185 0.014 248); // 容器 / 输入区(比页面更亮,向上浮)
|
||||
--bg-card-hover: oklch(0.225 0.016 246);
|
||||
--bg-input: oklch(0.105 0.012 252); // 输入框,比卡片更沉,形成凹陷感
|
||||
|
||||
// Borders
|
||||
--border-color: #e0e0e0;
|
||||
--border-light: #ebebeb;
|
||||
// Borders — 发丝线分两档
|
||||
--border-color: oklch(0.305 0.018 242);
|
||||
--border-light: oklch(0.255 0.016 244);
|
||||
|
||||
// Accent
|
||||
--accent-primary: #333333;
|
||||
--accent-hover: #1a1a1a;
|
||||
--accent-muted: #888888;
|
||||
// Accent — 霓虹青,全站唯一强调色
|
||||
--accent-primary: oklch(0.805 0.148 188);
|
||||
--accent-hover: oklch(0.870 0.150 188);
|
||||
--accent-muted: oklch(0.520 0.060 188);
|
||||
--accent-soft: oklch(0.805 0.148 188 / 0.13);
|
||||
--accent-ink: oklch(0.160 0.030 200); // 强调色上的文字/图标(深色,保证对比)
|
||||
|
||||
// Text
|
||||
--text-primary: #1a1a1a;
|
||||
--text-secondary: #666666;
|
||||
--text-muted: #999999;
|
||||
--text-primary: oklch(0.945 0.006 200);
|
||||
--text-secondary: oklch(0.860 0.008 210);
|
||||
--text-muted: oklch(0.705 0.014 230);
|
||||
--text-faint: oklch(0.520 0.014 236); // 禁用态 / 极次要信息
|
||||
|
||||
// Status
|
||||
--success: #2e7d32;
|
||||
--error: #c62828;
|
||||
--warning: #f57f17;
|
||||
// Status — 语义色,不用于装饰
|
||||
--success: oklch(0.760 0.140 165);
|
||||
--error: oklch(0.665 0.185 22);
|
||||
--warning: oklch(0.810 0.145 85);
|
||||
--accent-info: oklch(0.700 0.110 245);
|
||||
|
||||
// Message bubbles
|
||||
--msg-user-bg: #f5f5f5;
|
||||
--msg-assistant-bg: #f5f5f5;
|
||||
--msg-system-border: #bdbdbd;
|
||||
--msg-user-bg: oklch(0.225 0.016 246);
|
||||
--msg-assistant-bg: oklch(0.185 0.014 248);
|
||||
--msg-system-border: oklch(0.305 0.018 242);
|
||||
|
||||
// Code
|
||||
--code-bg: #f4f4f4;
|
||||
--code-bg: oklch(0.115 0.014 250);
|
||||
|
||||
// Utility
|
||||
--text-on-accent: #ffffff;
|
||||
--text-on-overlay: #ffffff;
|
||||
--accent-info: #4a90d9;
|
||||
--text-on-accent: oklch(0.160 0.030 200);
|
||||
--text-on-overlay: oklch(0.945 0.006 200);
|
||||
|
||||
// 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: #2a2a2a;
|
||||
--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: #888888;
|
||||
|
||||
// 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: 136, 136, 136;
|
||||
--success-rgb: 102, 187, 106;
|
||||
--error-rgb: 239, 83, 80;
|
||||
--warning-rgb: 255, 183, 77;
|
||||
--accent-info-rgb: 107, 163, 214;
|
||||
}
|
||||
|
||||
.comic {
|
||||
// Borders — bold comic outlines
|
||||
--border-color: #1a1a1a;
|
||||
--border-light: #555555;
|
||||
--msg-system-border: #1a1a1a;
|
||||
|
||||
// Comic-specific
|
||||
--font-ui: 'Comic Neue', 'ZCOOL KuaiLe', 'Zen Maru Gothic', 'Gaegu', cursive, sans-serif;
|
||||
--comic-border-width: 2.5px;
|
||||
--comic-shadow: 3px 3px 0px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.dark.comic {
|
||||
// Borders — brighter outlines on dark
|
||||
--border-color: #666666;
|
||||
--border-light: #555555;
|
||||
--msg-system-border: #888888;
|
||||
|
||||
--comic-shadow: 3px 3px 0px rgba(255, 255, 255, 0.08);
|
||||
// 霓虹青 oklch(0.805 0.148 188) ≈ rgb(45, 212, 191)
|
||||
--accent-primary-rgb: 45, 212, 191;
|
||||
--accent-hover-rgb: 74, 226, 205;
|
||||
--text-primary-rgb: 240, 246, 247;
|
||||
--text-muted-rgb: 168, 180, 187;
|
||||
--success-rgb: 47, 197, 138;
|
||||
--error-rgb: 232, 78, 61;
|
||||
--warning-rgb: 226, 175, 32;
|
||||
--accent-info-rgb: 105, 160, 232;
|
||||
}
|
||||
|
||||
// ─── SCSS Variables (delegate to CSS custom properties) ────────
|
||||
@@ -157,7 +89,7 @@ $text-muted: var(--text-muted);
|
||||
$success: var(--success);
|
||||
$error: var(--error);
|
||||
$warning: var(--warning);
|
||||
$info: $accent-primary;
|
||||
$info: var(--accent-info);
|
||||
|
||||
// Message bubbles
|
||||
$msg-user-bg: var(--msg-user-bg);
|
||||
@@ -168,16 +100,19 @@ $msg-system-border: var(--msg-system-border);
|
||||
$code-bg: var(--code-bg);
|
||||
|
||||
// Typography
|
||||
$font-ui: var(--font-ui, 'Inter', system-ui, -apple-system, sans-serif);
|
||||
$font-code: 'JetBrains Mono', 'Fira Code', 'Consolas', monospace;
|
||||
// 正文用系统无衬线,标识/数据/代码用等宽——这是 CYBER 的核心排版特征。
|
||||
$font-ui: -apple-system, BlinkMacSystemFont, 'PingFang SC', 'Hiragino Sans GB',
|
||||
'Microsoft YaHei', 'Segoe UI', Roboto, sans-serif;
|
||||
$font-code: ui-monospace, 'JetBrains Mono', 'SF Mono', Menlo, 'Cascadia Code', monospace;
|
||||
$font-display: $font-code;
|
||||
|
||||
// Layout
|
||||
// Layout — 与 docs/ui-baseline.md 保持一致
|
||||
$sidebar-width: 240px;
|
||||
$sidebar-collapsed-width: 64px;
|
||||
$header-height: 60px;
|
||||
$breakpoint-mobile: 768px;
|
||||
|
||||
// Radius
|
||||
// Radius — 发丝线 + 小圆角,硬边而非圆润
|
||||
$radius-sm: 6px;
|
||||
$radius-md: 8px;
|
||||
$radius-lg: 8px;
|
||||
|
||||
Reference in New Issue
Block a user