From 34cf186c33cccf765c073eca990254866c1e5581 Mon Sep 17 00:00:00 2001 From: Ap_Tx <3045929398@qq.com> Date: Thu, 1 Oct 2026 16:21:30 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E7=95=8C=E9=9D=A2=E7=BC=A9=E6=94=BE?= =?UTF-8?q?=E6=94=B9=E7=94=A8=E5=90=8E=E7=AB=AF=E6=8C=81=E4=B9=85=E5=8C=96?= =?UTF-8?q?=20+=20=E5=8E=9F=E7=94=9F=20webview=20zoom?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/app-frontend/src/App.vue | 4 +- .../ui/settings/AppearanceSettings.vue | 10 +++- apps/app-frontend/src/helpers/settings.ts | 2 + apps/app-frontend/src/helpers/ui-scale.ts | 52 ++++++++----------- apps/app/src/api/settings.rs | 16 +++++- .../20261001000000_add-ui-scale.sql | 1 + packages/app-lib/src/state/settings.rs | 13 ++++- 7 files changed, 63 insertions(+), 35 deletions(-) create mode 100644 packages/app-lib/migrations/20261001000000_add-ui-scale.sql diff --git a/apps/app-frontend/src/App.vue b/apps/app-frontend/src/App.vue index 0189adf..19f6881 100644 --- a/apps/app-frontend/src/App.vue +++ b/apps/app-frontend/src/App.vue @@ -142,7 +142,7 @@ import { isNetworkMetered, setRestartAfterPendingUpdate, } from '@/helpers/utils.js' -import { installUiScale } from '@/helpers/ui-scale' +import { installUiScale, setUiScale } from '@/helpers/ui-scale' import { start_join_server, start_join_singleplayer_world } from '@/helpers/worlds.ts' import i18n, { resolveInitialLocale } from '@/i18n.config' import { @@ -1091,6 +1091,7 @@ async function setupApp() { close_behavior, developer_mode, feature_flags, + ui_scale, } = initialSettings // Initialize locale from saved settings @@ -1121,6 +1122,7 @@ async function setupApp() { nativeDecorations.value = native_decorations if (os.value !== 'MacOS') await getCurrentWindow().setDecorations(native_decorations) + setUiScale(ui_scale) themeStore.setThemeState(theme) await initializeSystemAccentColor() themeStore.setAccentColor(accent_color) diff --git a/apps/app-frontend/src/components/ui/settings/AppearanceSettings.vue b/apps/app-frontend/src/components/ui/settings/AppearanceSettings.vue index 5750c05..0d4089e 100644 --- a/apps/app-frontend/src/components/ui/settings/AppearanceSettings.vue +++ b/apps/app-frontend/src/components/ui/settings/AppearanceSettings.vue @@ -373,6 +373,12 @@ const customBackgroundPreview = computed(() => : null, ) +function updateUiScale(value: number) { + const clamped = Math.min(200, Math.max(50, Math.round(value))) + setUiScale(clamped) + settings.value.ui_scale = clamped +} + const accentColorOptions: Array<{ value: AccentColor color: string @@ -1050,7 +1056,7 @@ watch( ? 'border-brand bg-brand-highlight text-brand' : 'border-surface-4 bg-surface-3 text-secondary hover:border-surface-5 hover:text-contrast' " - @click="setUiScale(preset)" + @click="updateUiScale(preset)" > {{ preset }}% @@ -1062,7 +1068,7 @@ watch( :max="200" :step="5" unit="%" - @update:model-value="(value) => setUiScale(Number(value))" + @update:model-value="(value) => updateUiScale(Number(value))" /> diff --git a/apps/app-frontend/src/helpers/settings.ts b/apps/app-frontend/src/helpers/settings.ts index b5df67f..e4e8fba 100644 --- a/apps/app-frontend/src/helpers/settings.ts +++ b/apps/app-frontend/src/helpers/settings.ts @@ -172,6 +172,7 @@ export type AppSettings = { home_layout: HomeLayout minimal_home_instance_id: string | null close_behavior: 'ask' | 'close' | 'lightweight' + ui_scale: number home_widgets: HomeDashboardConfig | null terracotta_public_nodes: string[] @@ -213,6 +214,7 @@ type LegacyMirrorSettings = { function normalizeDownloadSettings(settings: AppSettings & LegacyMirrorSettings): AppSettings { settings.close_behavior ??= 'ask' + settings.ui_scale ??= 100 const hasLegacySettings = typeof settings.use_minecraft_mirror === 'boolean' && typeof settings.use_modrinth_mirror === 'boolean' && diff --git a/apps/app-frontend/src/helpers/ui-scale.ts b/apps/app-frontend/src/helpers/ui-scale.ts index 1e606eb..58697b6 100644 --- a/apps/app-frontend/src/helpers/ui-scale.ts +++ b/apps/app-frontend/src/helpers/ui-scale.ts @@ -1,10 +1,11 @@ +import { invoke } from '@tauri-apps/api/core' import { ref, watch } from 'vue' /** * 启动器界面缩放(百分比)。 - * 这是纯本地的显示偏好,用 localStorage 保存即可,无需写入后端设置数据库。 + * 通过 Tauri 原生 webview zoom(Rust 侧 set_zoom)应用, + * 缩放值持久化到后端设置数据库(settings.ui_scale)。 */ -const UI_SCALE_STORAGE_KEY = 'starlight-ui-scale' const MIN_UI_SCALE = 50 const MAX_UI_SCALE = 200 const DEFAULT_UI_SCALE = 100 @@ -15,18 +16,7 @@ function clampUiScale(value: number): number { return Math.min(MAX_UI_SCALE, Math.max(MIN_UI_SCALE, Math.round(value))) } -/** 从 localStorage 读取已保存的缩放值。 */ -function readStoredUiScale(): number { - try { - const raw = localStorage.getItem(UI_SCALE_STORAGE_KEY) - if (raw === null) return DEFAULT_UI_SCALE - return clampUiScale(Number(raw)) - } catch { - return DEFAULT_UI_SCALE - } -} - -export const uiScale = ref(readStoredUiScale()) +export const uiScale = ref(DEFAULT_UI_SCALE) export function setUiScale(value: number): void { uiScale.value = clampUiScale(value) @@ -38,23 +28,27 @@ export function resetUiScale(): void { export { DEFAULT_UI_SCALE, MAX_UI_SCALE, MIN_UI_SCALE } -/** 把缩放应用到文档根节点,并在变化时持久化。 */ -export function installUiScale(): void { - const apply = (value: number) => { - const factor = String(value / 100) - const root = document.documentElement - root.style.setProperty('--ui-scale', factor) - // 用 CSS zoom 整体缩放界面(含内嵌网页),在 WebView2/WebKit 上最可靠。 - root.style.setProperty('zoom', factor) +/** + * 把缩放值应用到原生 webview。 + * 同时保留 --ui-scale CSS 变量,供依赖它的样式/锚点使用。 + */ +async function applyUiScale(value: number): Promise { + const factor = value / 100 + document.documentElement.style.setProperty('--ui-scale', String(factor)) + try { + await invoke('plugin:settings|set_ui_scale', { factor }) + } catch (error) { + console.warn('应用界面缩放失败', error) } +} - apply(uiScale.value) +/** + * 初始化缩放:应用当前值,并在变化时同步到原生 webview。 + * 初值由调用方(App 初始化流程)在读取后端设置后通过 setUiScale 覆盖。 + */ +export function installUiScale(): void { + void applyUiScale(uiScale.value) watch(uiScale, (value) => { - apply(value) - try { - localStorage.setItem(UI_SCALE_STORAGE_KEY, String(value)) - } catch (error) { - console.warn('保存界面缩放失败', error) - } + void applyUiScale(value) }) } \ No newline at end of file diff --git a/apps/app/src/api/settings.rs b/apps/app/src/api/settings.rs index 902632d..e281a54 100644 --- a/apps/app/src/api/settings.rs +++ b/apps/app/src/api/settings.rs @@ -1,5 +1,5 @@ use crate::api::Result; -use tauri::{Emitter, Runtime}; +use tauri::{Emitter, Manager, Runtime}; use theseus::prelude::*; use theseus::{ProxyConfig, ProxyTestResult}; @@ -12,7 +12,8 @@ pub fn init() -> tauri::plugin::TauriPlugin { cancel_directory_change, proxy_get, proxy_set, - proxy_test + proxy_test, + set_ui_scale ]) .build() } @@ -37,6 +38,17 @@ pub async fn settings_set( Ok(()) } +#[tauri::command] +pub async fn set_ui_scale( + app: tauri::AppHandle, + factor: f64, +) -> Result<()> { + if let Some(window) = app.get_webview_window("main") { + let _ = window.set_zoom(factor); + } + Ok(()) +} + #[tauri::command] pub async fn download_engine_set( engine: settings::DownloadEngine, diff --git a/packages/app-lib/migrations/20261001000000_add-ui-scale.sql b/packages/app-lib/migrations/20261001000000_add-ui-scale.sql new file mode 100644 index 0000000..d3a90f8 --- /dev/null +++ b/packages/app-lib/migrations/20261001000000_add-ui-scale.sql @@ -0,0 +1 @@ +ALTER TABLE settings ADD COLUMN ui_scale REAL NOT NULL DEFAULT 100.0; \ No newline at end of file diff --git a/packages/app-lib/src/state/settings.rs b/packages/app-lib/src/state/settings.rs index 11d69a7..8afdacf 100644 --- a/packages/app-lib/src/state/settings.rs +++ b/packages/app-lib/src/state/settings.rs @@ -141,6 +141,8 @@ pub struct Settings { pub transparent_background_opacity: u32, pub transparent_background_blur: bool, pub sidebar_instance_count: u32, + #[serde(default = "default_ui_scale")] + pub ui_scale: f64, #[serde(default)] pub close_behavior: String, #[serde(default)] @@ -192,6 +194,10 @@ fn default_true() -> bool { true } +fn default_ui_scale() -> f64 { + 100.0 +} + #[derive(Serialize, Deserialize, Debug, Clone, Copy, Eq, Hash, PartialEq)] #[serde(rename_all = "snake_case")] pub enum FeatureFlag { @@ -242,6 +248,7 @@ impl Settings { sidebar_instance_count, home_layout, minimal_home_instance_id, json(home_widgets) as \"home_widgets?: String\", auto_hide_downloads_button, json(terracotta_public_nodes) terracotta_public_nodes, + ui_scale, version FROM settings " @@ -308,6 +315,7 @@ impl Settings { as u32, transparent_background_blur: res.transparent_background_blur == 1, sidebar_instance_count: res.sidebar_instance_count as u32, + ui_scale: res.ui_scale, close_behavior, auto_hide_downloads_button: res.auto_hide_downloads_button == 1, home_layout: HomeLayout::from_string(&res.home_layout), @@ -603,7 +611,10 @@ impl Settings { .bind(self.force_unicode_font) .execute(exec) .await?; - + sqlx::query("UPDATE settings SET ui_scale = ? WHERE id = 0") + .bind(self.ui_scale) + .execute(exec) + .await?; Ok(()) }