From 902013f22f5b3b2ee7bcfd2e0ae560fa3ea408d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=A2=81=E6=B6=9B?= Date: Sat, 18 Apr 2026 23:12:08 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=20=E8=B7=9F=E9=9A=8F?= =?UTF-8?q?=E7=B3=BB=E7=BB=9F=E4=B8=BB=E9=A2=98=E5=88=87=E6=8D=A2=E5=8A=9F?= =?UTF-8?q?=E8=83=BD=EF=BC=8C=E6=B7=B1=E8=89=B2=E6=A8=A1=E5=BC=8F=E8=87=AA?= =?UTF-8?q?=E5=8A=A8=E9=9A=90=E8=97=8F=E4=BA=9A=E5=85=8B=E5=8A=9B=E6=95=88?= =?UTF-8?q?=E6=9E=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- AGENTS.md | 2 +- README.md | 2 +- package.json | 2 +- src/App.vue | 39 ++++++++++++++++++++++++------- src/locales/en-US.js | 3 ++- src/locales/index.js | 3 ++- src/locales/ja-JP.js | 3 ++- src/views/GeneralSettings.test.js | 3 ++- src/views/GeneralSettings.vue | 15 +++++++++++- 9 files changed, 55 insertions(+), 17 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index de252d8..6b45b61 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -97,7 +97,7 @@ npm run test:run ### 主题系统 -支持两种主题:`Light` (浅色) / `Dark` (深色) +支持三种主题:`Light` (浅色) / `Dark` (深色) / `System` (跟随系统) CSS 变量定义在 `src/styles/global.less`,包括: - `--bg-primary/secondary/elevated` - 背景层级 diff --git a/README.md b/README.md index 5e0801d..8e8dd3c 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ - 📝 **API 配置管理** - 支持多环境配置文件切换、创建、编辑、复制和删除 - 🖥️ **MCP 服务器管理** - 便捷的 Model Context Protocol 服务器配置界面 - 🎨 **Windows 11 设计风格** - 采用 Fluent Design 设计规范 -- 🌈 **多主题支持** - Light / Dark 两种主题 +- 🌈 **多主题支持** - Light / Dark / System (跟随系统) 三种主题 - 🌍 **国际化** - 支持简体中文、English、日語 - 💧 **亚克力效果** - 可调节透明度的现代视觉效果 - 📦 **系统托盘** - 最小化到托盘,快速切换 API 配置 diff --git a/package.json b/package.json index af61044..ef8f483 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "iflow-settings-editor", - "version": "1.6.0", + "version": "1.6.1", "description": "一个用于编辑 iFlow CLI 配置文件的桌面应用程序。", "main": "main.js", "author": "上海潘哆呐科技有限公司", diff --git a/src/App.vue b/src/App.vue index c60ade1..bf7e7c7 100644 --- a/src/App.vue +++ b/src/App.vue @@ -84,6 +84,7 @@ const currentServerName = ref(null) const isLoading = ref(true) const apiProfiles = ref([]) const currentApiProfile = ref('default') +const systemTheme = ref('Light') const showInputDialog = ref({ show: false, title: '', placeholder: '', callback: null, isConfirm: false, defaultValue: '' }) const showMessageDialog = ref({ show: false, type: 'info', title: '', message: '', callback: null }) @@ -307,8 +308,14 @@ const showSection = section => { const serverCount = computed(() => (settings.value.mcpServers ? Object.keys(settings.value.mcpServers).length : 0)) -const themeClass = computed(() => { +const getEffectiveTheme = () => { const theme = settings.value.uiTheme + if (theme === 'System') return systemTheme.value + return theme +} + +const themeClass = computed(() => { + const theme = getEffectiveTheme() if (theme === 'Dark') return 'dark' return '' }) @@ -471,14 +478,10 @@ const closeMessageDialog = () => { watch( () => settings.value.uiTheme, - theme => { + () => { + document.body.classList.remove('dark') const cls = themeClass.value - if (cls) { - document.body.classList.add(cls) - if (cls === 'dark') document.body.classList.remove('solarized-dark') - } else { - document.body.classList.remove('dark', 'solarized-dark') - } + if (cls) document.body.classList.add(cls) applyAcrylicStyle() }, ) @@ -494,7 +497,7 @@ const applyAcrylicStyle = () => { const intensity = settings.value.acrylicIntensity if (intensity === undefined || intensity === null) return const opacity = 1 - intensity / 100 - const isDark = settings.value.uiTheme === 'Dark' + const isDark = getEffectiveTheme() === 'Dark' const root = document.documentElement if (isDark) { @@ -512,10 +515,28 @@ const applyAcrylicStyle = () => { } } +const updateSystemTheme = () => { + const isDark = window.matchMedia('(prefers-color-scheme: dark)').matches + systemTheme.value = isDark ? 'Dark' : 'Light' + if (settings.value.uiTheme === 'System') { + const cls = themeClass.value + document.body.classList.remove('dark') + if (cls) document.body.classList.add(cls) + applyAcrylicStyle() + } +} + onMounted(async () => { await loadApiProfiles() await loadSettings() locale.value = settings.value.language + + // 初始化系统主题 + updateSystemTheme() + + // 监听系统主题变化 + window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', updateSystemTheme) + const cls = themeClass.value if (cls) { document.body.classList.add(cls) diff --git a/src/locales/en-US.js b/src/locales/en-US.js index cc4f86c..e2e587b 100644 --- a/src/locales/en-US.js +++ b/src/locales/en-US.js @@ -33,7 +33,8 @@ export default { }, theme: { dark: 'Dark', - light: 'Light' + light: 'Light', + system: 'System' }, api: { title: 'API Configuration', diff --git a/src/locales/index.js b/src/locales/index.js index 2f74b01..febe566 100644 --- a/src/locales/index.js +++ b/src/locales/index.js @@ -33,7 +33,8 @@ export default { }, theme: { dark: '深色', - light: '浅色' + light: '浅色', + system: '跟随系统' }, api: { title: 'API 配置', diff --git a/src/locales/ja-JP.js b/src/locales/ja-JP.js index bdb9c1d..94472c2 100644 --- a/src/locales/ja-JP.js +++ b/src/locales/ja-JP.js @@ -33,7 +33,8 @@ export default { }, theme: { dark: 'ダーク', - light: 'ライト' + light: 'ライト', + system: 'システム' }, api: { title: 'API 設定', diff --git a/src/views/GeneralSettings.test.js b/src/views/GeneralSettings.test.js index 36b7dda..ab1957d 100644 --- a/src/views/GeneralSettings.test.js +++ b/src/views/GeneralSettings.test.js @@ -61,9 +61,10 @@ describe('GeneralSettings.vue', () => { }); const themeOptions = wrapper.findAll('.form-select')[1].findAll('option'); - expect(themeOptions.length).toBe(2); + expect(themeOptions.length).toBe(3); expect(themeOptions[0].attributes('value')).toBe('Light'); expect(themeOptions[1].attributes('value')).toBe('Dark'); + expect(themeOptions[2].attributes('value')).toBe('System'); }); it('reflects current settings in form controls', async () => { diff --git a/src/views/GeneralSettings.vue b/src/views/GeneralSettings.vue index f7b24f2..29f6066 100644 --- a/src/views/GeneralSettings.vue +++ b/src/views/GeneralSettings.vue @@ -23,6 +23,7 @@ @@ -90,12 +91,24 @@ const localSettings = computed({ set: val => emit('update:settings', val), }) +const systemTheme = ref('Light') + const supportsAcrylic = computed(() => { - return typeof document !== 'undefined' && 'backdropFilter' in document.documentElement.style && props.settings.uiTheme !== 'Dark' + if (typeof document === 'undefined' || !('backdropFilter' in document.documentElement.style)) return false + const effectiveTheme = props.settings.uiTheme === 'System' ? systemTheme.value : props.settings.uiTheme + return effectiveTheme !== 'Dark' }) const sliderWrapper = ref(null) +onMounted(() => { + const isDark = window.matchMedia('(prefers-color-scheme: dark)').matches + systemTheme.value = isDark ? 'Dark' : 'Light' + window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', e => { + systemTheme.value = e.matches ? 'Dark' : 'Light' + }) +}) + const updateSliderValue = e => { const value = Number(e.target.value) emit('update:settings', { ...props.settings, acrylicIntensity: value })