From ac87c18e5974ff969f1c4d6fab92937ba79e3394 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=A2=81=E6=B6=9B?= Date: Sat, 18 Apr 2026 21:51:30 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=20=E4=BA=9A=E5=85=8B?= =?UTF-8?q?=E5=8A=9B=E9=80=8F=E6=98=8E=E5=BA=A6=E8=B0=83=E8=8A=82=E5=8A=9F?= =?UTF-8?q?=E8=83=BD=EF=BC=9A=E6=94=AF=E6=8C=81=E5=9C=A8=E6=B5=85=E8=89=B2?= =?UTF-8?q?=E4=B8=BB=E9=A2=98=E4=B8=8B=E8=B0=83=E6=95=B4=E8=83=8C=E6=99=AF?= =?UTF-8?q?=E6=A8=A1=E7=B3=8A=E6=95=88=E6=9E=9C=E5=BC=BA=E5=BA=A6=EF=BC=8C?= =?UTF-8?q?=E8=8C=83=E5=9B=B40-100%?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.vue | 58 +++++++++++++++++ src/locales/en-US.js | 5 +- src/locales/index.js | 5 +- src/locales/ja-JP.js | 5 +- src/styles/global.less | 43 ------------- src/views/GeneralSettings.vue | 118 ++++++++++++++++++++++++++++++++-- 6 files changed, 183 insertions(+), 51 deletions(-) diff --git a/src/App.vue b/src/App.vue index 02bece9..92b4a47 100644 --- a/src/App.vue +++ b/src/App.vue @@ -74,6 +74,7 @@ const settings = ref({ cna: '', currentApiProfile: 'default', apiProfiles: { default: {} }, + acrylicIntensity: 50, }) const originalSettings = ref({}) @@ -272,6 +273,7 @@ const loadSettings = async () => { if (data.cna === undefined) data.cna = '' if (!data.apiProfiles) data.apiProfiles = { default: {} } if (!data.currentApiProfile) data.currentApiProfile = 'default' + if (data.acrylicIntensity === undefined) data.acrylicIntensity = 50 settings.value = data originalSettings.value = JSON.parse(JSON.stringify(data)) modified.value = false @@ -312,6 +314,31 @@ const themeClass = computed(() => { return '' }) +const acrylicStyle = computed(() => { + const intensity = settings.value.acrylicIntensity + if (intensity === undefined || intensity === null) return {} + const opacity = 1 - intensity / 100 + const isDark = settings.value.theme === 'Dark' + + if (isDark) { + return { + '--bg-primary': `rgba(31, 31, 31, ${Math.max(0.05, opacity * 0.85)})`, + '--bg-secondary': `rgba(45, 45, 45, ${Math.max(0.05, opacity * 0.7)})`, + '--bg-elevated': `rgba(51, 51, 51, ${Math.max(0.05, opacity * 0.95)})`, + '--bg-mica': `rgba(31, 31, 31, ${Math.max(0.05, opacity * 0.85)})`, + '--control-fill': `rgba(51, 51, 51, ${Math.max(0.05, opacity * 0.85)})`, + } + } else { + return { + '--bg-primary': `rgba(243, 243, 243, ${Math.max(0.05, opacity * 0.85)})`, + '--bg-secondary': `rgba(255, 255, 255, ${Math.max(0.05, opacity * 0.7)})`, + '--bg-elevated': `rgba(255, 255, 255, ${Math.max(0.05, opacity * 0.95)})`, + '--bg-mica': `rgba(243, 243, 243, ${Math.max(0.05, opacity * 0.473)})`, + '--control-fill': `rgba(249, 249, 249, ${Math.max(0.05, opacity * 0.85)})`, + } + } +}) + const selectServer = name => { currentServerName.value = name openEditServerPanel(name) @@ -454,9 +481,39 @@ watch( } else { document.body.classList.remove('dark', 'solarized-dark') } + applyAcrylicStyle() }, ) +watch( + () => settings.value.acrylicIntensity, + () => { + applyAcrylicStyle() + }, +) + +const applyAcrylicStyle = () => { + const intensity = settings.value.acrylicIntensity + if (intensity === undefined || intensity === null) return + const opacity = 1 - intensity / 100 + const isDark = settings.value.theme === 'Dark' + const root = document.documentElement + + if (isDark) { + root.style.setProperty('--bg-primary', `rgba(31, 31, 31, ${Math.max(0.05, opacity * 0.85)})`) + root.style.setProperty('--bg-secondary', `rgba(45, 45, 45, ${Math.max(0.05, opacity * 0.7)})`) + root.style.setProperty('--bg-elevated', `rgba(51, 51, 51, ${Math.max(0.05, opacity * 0.95)})`) + root.style.setProperty('--bg-mica', `rgba(31, 31, 31, ${Math.max(0.05, opacity * 0.85)})`) + root.style.setProperty('--control-fill', `rgba(51, 51, 51, ${Math.max(0.05, opacity * 0.85)})`) + } else { + root.style.setProperty('--bg-primary', `rgba(243, 243, 243, ${Math.max(0.05, opacity * 0.85)})`) + root.style.setProperty('--bg-secondary', `rgba(255, 255, 255, ${Math.max(0.05, opacity * 0.7)})`) + root.style.setProperty('--bg-elevated', `rgba(255, 255, 255, ${Math.max(0.05, opacity * 0.95)})`) + root.style.setProperty('--bg-mica', `rgba(243, 243, 243, ${Math.max(0.05, opacity * 0.473)})`) + root.style.setProperty('--control-fill', `rgba(249, 249, 249, ${Math.max(0.05, opacity * 0.85)})`) + } +} + onMounted(async () => { await loadApiProfiles() await loadSettings() @@ -465,6 +522,7 @@ onMounted(async () => { if (cls) { document.body.classList.add(cls) } + applyAcrylicStyle() window.electronAPI.onApiProfileSwitched(async profileName => { currentApiProfile.value = profileName await loadSettings() diff --git a/src/locales/en-US.js b/src/locales/en-US.js index 3a4e2bd..0bd7eaa 100644 --- a/src/locales/en-US.js +++ b/src/locales/en-US.js @@ -26,7 +26,10 @@ export default { bootAnimationNotShown: 'Not Shown', checkpointing: 'Checkpointing', enabled: 'Enabled', - disabled: 'Disabled' + disabled: 'Disabled', + acrylicEffect: 'Acrylic Effect', + acrylicMin: 'Opaque', + acrylicMax: 'Transparent' }, theme: { xcode: 'Xcode', diff --git a/src/locales/index.js b/src/locales/index.js index d7e4c3f..a49e66f 100644 --- a/src/locales/index.js +++ b/src/locales/index.js @@ -26,7 +26,10 @@ export default { bootAnimationNotShown: '未显示', checkpointing: '检查点保存', enabled: '已启用', - disabled: '已禁用' + disabled: '已禁用', + acrylicEffect: '亚克力效果', + acrylicMin: '不透明', + acrylicMax: '透明' }, theme: { xcode: 'Xcode', diff --git a/src/locales/ja-JP.js b/src/locales/ja-JP.js index 512a05d..e308a71 100644 --- a/src/locales/ja-JP.js +++ b/src/locales/ja-JP.js @@ -26,7 +26,10 @@ export default { bootAnimationNotShown: '未表示', checkpointing: 'チェックポイント保存', enabled: '有効', - disabled: '無効' + disabled: '無効', + acrylicEffect: 'アクリリック効果', + acrylicMin: '不透明', + acrylicMax: '透明' }, theme: { xcode: 'Xcode', diff --git a/src/styles/global.less b/src/styles/global.less index 1268d68..f2aacd1 100644 --- a/src/styles/global.less +++ b/src/styles/global.less @@ -136,49 +136,6 @@ --shadow-xl: 0 16px 32px rgba(0, 0, 0, 0.5), 0 8px 16px rgba(0, 0, 0, 0.3); } -// Solarized Dark Mode -.solarized-dark { - --bg-primary: #002b36; - --bg-secondary: #073642; - --bg-tertiary: #094856; - --bg-elevated: #0a4a5c; - --bg-mica: rgba(0, 43, 54, 0.9); - - --text-primary: #839496; - --text-secondary: #93a1a1; - --text-tertiary: #586e75; - --text-disabled: #3d5a64; - - --accent: #268bd2; - --accent-hover: #2d9cdb; - --accent-pressed: #1a73c0; - --accent-light: rgba(38, 139, 210, 0.15); - --accent-text: #268bd2; - - --border: #1d3a47; - --border-light: #0d3a47; - --border-strong: #2d5a6f; - - --success: #2aa198; - --success-bg: rgba(42, 161, 152, 0.15); - --danger: #dc322f; - --danger-bg: rgba(220, 50, 47, 0.15); - --warning: #b58900; - --warning-bg: rgba(181, 137, 0, 0.15); - --info: #268bd2; - --info-bg: rgba(38, 139, 210, 0.15); - - --control-fill: #073642; - --control-fill-hover: #0a4a5c; - --control-fill-pressed: #0d5a70; - --control-fill-disabled: #053845; - - --shadow-sm: 0 2px 4px rgba(0, 0, 0, 0.3); - --shadow: 0 4px 8px rgba(0, 0, 0, 0.4), 0 2px 4px rgba(0, 0, 0, 0.3); - --shadow-lg: 0 8px 16px rgba(0, 0, 0, 0.5), 0 4px 8px rgba(0, 0, 0, 0.35); - --shadow-xl: 0 16px 32px rgba(0, 0, 0, 0.6), 0 8px 16px rgba(0, 0, 0, 0.4); -} - // ============================================================================= // Animations // ============================================================================= diff --git a/src/views/GeneralSettings.vue b/src/views/GeneralSettings.vue index 714a676..2da314c 100644 --- a/src/views/GeneralSettings.vue +++ b/src/views/GeneralSettings.vue @@ -21,10 +21,8 @@
@@ -50,6 +48,23 @@ +
+
+ +
+ + {{ $t('general.acrylicMin') }} — {{ $t('general.acrylicMax') }} +
+
+
@@ -66,12 +81,105 @@ const props = defineProps({ const emit = defineEmits(['update:settings']) -import { computed } from 'vue' +import { computed, ref, onMounted, watch, nextTick } from 'vue' const localSettings = computed({ get: () => props.settings, set: val => emit('update:settings', val), }) + +const supportsAcrylic = computed(() => { + return typeof document !== 'undefined' && 'backdropFilter' in document.documentElement.style && props.settings.theme !== 'Dark' +}) + +const sliderRef = ref(null) + +const updateSliderStyle = e => { + const value = e.target.value + const percent = ((value - 0) / (100 - 0)) * 100 + e.target.style.backgroundSize = `${percent}% 100%` + emit('update:settings', { ...props.settings, acrylicIntensity: Number(value) }) +} + +onMounted(() => { + if (sliderRef.value) { + const percent = ((props.settings.acrylicIntensity - 0) / (100 - 0)) * 100 + sliderRef.value.style.backgroundSize = `${percent}% 100%` + } +}) + +watch( + () => props.settings.theme, + () => { + nextTick(() => { + if (sliderRef.value && supportsAcrylic.value) { + const percent = ((props.settings.acrylicIntensity - 0) / (100 - 0)) * 100 + sliderRef.value.style.backgroundSize = `${percent}% 100%` + } + }) + }, +) - +