fix(macos): persist zoom level to DB so traffic lights use correct position on launch

Previously the welcome window (or any startup window) always used the
default CURRENT_ZOOM of 100, ignoring the user's saved zoom preference
stored in frontend localStorage. Now update_traffic_light_position also
writes the zoom value to the app_metadata DB, and setup() reads it back
before creating any window.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
xintaofei
2026-04-13 10:10:09 +08:00
parent 17bae372a1
commit e05ae76453
2 changed files with 47 additions and 5 deletions

View File

@@ -3,10 +3,12 @@ use std::sync::Mutex;
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
use std::sync::atomic::{AtomicU32, Ordering}; use std::sync::atomic::{AtomicU32, Ordering};
use sea_orm::DatabaseConnection;
use tauri::{AppHandle, Manager, WebviewUrl, WebviewWindowBuilder}; use tauri::{AppHandle, Manager, WebviewUrl, WebviewWindowBuilder};
use crate::app_error::AppCommandError; use crate::app_error::AppCommandError;
use crate::db::AppDatabase; use crate::db::AppDatabase;
use crate::db::service::app_metadata_service;
use crate::models::FolderHistoryEntry; use crate::models::FolderHistoryEntry;
/// Base traffic-light position (logical px) at 100 % zoom. /// Base traffic-light position (logical px) at 100 % zoom.
@@ -26,6 +28,26 @@ fn traffic_light_position() -> tauri::LogicalPosition<f64> {
tauri::LogicalPosition::new(TRAFFIC_LIGHT_X, TRAFFIC_LIGHT_Y * zoom / 100.0) tauri::LogicalPosition::new(TRAFFIC_LIGHT_X, TRAFFIC_LIGHT_Y * zoom / 100.0)
} }
const ZOOM_LEVEL_DB_KEY: &str = "appearance_zoom_level";
/// Load saved zoom level from DB and initialize CURRENT_ZOOM.
/// Called once at startup before any window is created.
pub async fn load_saved_zoom(conn: &DatabaseConnection) {
#[cfg(target_os = "macos")]
{
if let Ok(Some(raw)) = app_metadata_service::get_value(conn, ZOOM_LEVEL_DB_KEY).await {
if let Ok(zoom) = raw.parse::<u32>() {
let clamped = zoom.clamp(50, 300);
CURRENT_ZOOM.store(clamped, Ordering::Relaxed);
}
}
}
#[cfg(not(target_os = "macos"))]
{
let _ = conn;
}
}
pub struct SettingsWindowState { pub struct SettingsWindowState {
owner_window_label: Mutex<Option<String>>, owner_window_label: Mutex<Option<String>>,
} }
@@ -687,14 +709,30 @@ pub async fn open_project_boot_window(
Ok(()) Ok(())
} }
/// Store the current zoom level so that newly created windows use the correct /// Store the current zoom level and persist it to DB so the next launch
/// traffic-light position. Existing windows are NOT repositioned at runtime. /// creates windows with the correct traffic-light position.
/// Existing windows are NOT repositioned at runtime.
#[cfg(feature = "tauri-runtime")] #[cfg(feature = "tauri-runtime")]
#[cfg_attr(feature = "tauri-runtime", tauri::command)] #[cfg_attr(feature = "tauri-runtime", tauri::command)]
pub async fn update_traffic_light_position(app: AppHandle, zoom: f64) -> Result<(), AppCommandError> { pub async fn update_traffic_light_position(
app: AppHandle,
db: tauri::State<'_, AppDatabase>,
zoom: f64,
) -> Result<(), AppCommandError> {
let clamped = zoom.clamp(50.0, 300.0) as u32;
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
CURRENT_ZOOM.store(zoom.clamp(50.0, 300.0) as u32, Ordering::Relaxed); CURRENT_ZOOM.store(clamped, Ordering::Relaxed);
let _ = (app, zoom);
// Persist to DB so the next launch reads the correct value.
let _ = app_metadata_service::upsert_value(
&db.conn,
ZOOM_LEVEL_DB_KEY,
&clamped.to_string(),
)
.await;
let _ = app;
Ok(()) Ok(())
} }

View File

@@ -83,6 +83,10 @@ mod tauri_app {
} }
} }
// Load saved zoom level for traffic-light positioning before
// any window is created.
tauri::async_runtime::block_on(windows::load_saved_zoom(&db.conn));
// Install bundled expert skills into the central store // Install bundled expert skills into the central store
// (`~/.codeg/skills/`). Runs in the background and does // (`~/.codeg/skills/`). Runs in the background and does
// not block startup; failures are logged but non-fatal. // not block startup; failures are logged but non-fatal.