perf: eliminate theme switch lag by suppressing CSS transitions

Disable all CSS transitions/animations during theme toggle so color
changes apply instantly instead of animating over 150ms. Also cache
Tauri window module import and set color-scheme for native UI elements.
This commit is contained in:
t8y2 2026-05-13 16:04:35 +08:00
parent 70becfd74f
commit b4d1f4b04e
6 changed files with 63 additions and 18 deletions

View File

@ -123,11 +123,17 @@ impl AgentManager {
pub fn jre_java_path(&self, jre_key: &str) -> PathBuf {
let dir = self.jre_dir(jre_key);
if cfg!(windows) {
dir.join("bin").join("java.exe")
} else {
dir.join("bin").join("java")
let java_name = if cfg!(windows) { "java.exe" } else { "java" };
let flat = dir.join("bin").join(java_name);
if flat.exists() {
return flat;
}
// macOS Adoptium JRE 8 uses Contents/Home/ layout
let macos = dir.join("Contents").join("Home").join("bin").join(java_name);
if macos.exists() {
return macos;
}
flat
}
pub fn driver_jar_path(&self, db_type: &str) -> PathBuf {
@ -159,13 +165,16 @@ impl AgentManager {
self.driver_jar_path(db_type).exists()
}
pub fn db_type_to_agent_key(db_type: &DatabaseType) -> Option<&'static str> {
pub fn db_type_to_agent_key(db_type: &DatabaseType, driver_profile: Option<&str>) -> Option<&'static str> {
match db_type {
DatabaseType::Dameng => Some("dameng"),
DatabaseType::Kingbase => Some("kingbase"),
DatabaseType::Vastbase => Some("vastbase"),
DatabaseType::Goldendb => Some("goldendb"),
DatabaseType::Oracle => Some("oracle"),
DatabaseType::Oracle => match driver_profile {
Some("oracle-10g") => Some("oracle-10g"),
_ => Some("oracle"),
},
DatabaseType::H2 => Some("h2"),
DatabaseType::Snowflake => Some("snowflake"),
DatabaseType::Trino => Some("trino"),
@ -183,11 +192,15 @@ impl AgentManager {
}
pub fn is_agent_type(db_type: &DatabaseType) -> bool {
Self::db_type_to_agent_key(db_type).is_some()
Self::db_type_to_agent_key(db_type, None).is_some()
}
pub async fn spawn(&self, db_type: &DatabaseType) -> Result<AgentDriverClient, String> {
let key = Self::db_type_to_agent_key(db_type)
pub async fn spawn(
&self,
db_type: &DatabaseType,
driver_profile: Option<&str>,
) -> Result<AgentDriverClient, String> {
let key = Self::db_type_to_agent_key(db_type, driver_profile)
.ok_or_else(|| format!("{:?} is not an agent-driven database type", db_type))?;
let state = self.load_state();
@ -208,10 +221,11 @@ impl AgentManager {
pub async fn call_daemon<T: serde::de::DeserializeOwned + Send + 'static>(
&self,
db_type: &DatabaseType,
driver_profile: Option<&str>,
method: &str,
params: serde_json::Value,
) -> Result<T, String> {
let key = Self::db_type_to_agent_key(db_type)
let key = Self::db_type_to_agent_key(db_type, driver_profile)
.ok_or_else(|| format!("{:?} is not an agent-driven database type", db_type))?
.to_string();

View File

@ -231,7 +231,8 @@ impl AppState {
| DatabaseType::Kylin
| DatabaseType::Sundb
| DatabaseType::Gaussdb => {
let mut client = self.agent_manager.spawn(&db_config.db_type).await?;
let mut client =
self.agent_manager.spawn(&db_config.db_type, db_config.driver_profile.as_deref()).await?;
client
.call::<serde_json::Value>(
"connect",

View File

@ -17,6 +17,7 @@ const AGENT_TYPES: &[(&str, &str)] = &[
("vastbase", "Vastbase"),
("goldendb", "GoldenDB"),
("oracle", "Oracle"),
("oracle-10g", "Oracle 10g"),
("h2", "H2"),
("snowflake", "Snowflake"),
("trino", "Trino (Presto)"),

View File

@ -127,6 +127,7 @@ pub async fn test_connection(state: State<'_, Arc<AppState>>, config: Connection
.agent_manager
.call_daemon::<serde_json::Value>(
&config.db_type,
config.driver_profile.as_deref(),
"test_connection",
serde_json::json!({
"host": host,
@ -238,7 +239,7 @@ pub async fn connect_db(state: State<'_, Arc<AppState>>, config: ConnectionConfi
| DatabaseType::Kylin
| DatabaseType::Sundb
| DatabaseType::Gaussdb => {
let mut client = state.agent_manager.spawn(&db_config.db_type).await?;
let mut client = state.agent_manager.spawn(&db_config.db_type, db_config.driver_profile.as_deref()).await?;
client
.call::<serde_json::Value>(
"connect",

View File

@ -16,6 +16,7 @@ const isDark = computed(() => resolveAppThemeAppearance(themeMode.value, systemP
let mediaQuery: MediaQueryList | null = null;
let isListeningForSystemTheme = false;
let cachedTauriWindow: typeof import("@tauri-apps/api/window") | null = null;
function readSystemPrefersDark() {
if (typeof window === "undefined" || typeof window.matchMedia !== "function") return false;
@ -35,15 +36,34 @@ function setupSystemThemeListener() {
}
function applyTheme() {
if (typeof document !== "undefined") {
document.documentElement.classList.toggle("dark", isDark.value);
}
if (typeof document === "undefined") return;
const doc = document.documentElement;
const dark = isDark.value;
doc.classList.add("disable-transitions");
doc.classList.toggle("dark", dark);
doc.style.colorScheme = dark ? "dark" : "light";
// force reflow so the class toggle takes effect before re-enabling transitions
doc.offsetHeight; // eslint-disable-line @typescript-eslint/no-unused-expressions
requestAnimationFrame(() => doc.classList.remove("disable-transitions"));
if (!isTauriRuntime()) return;
import("@tauri-apps/api/window").then(({ getCurrentWindow }) => {
getCurrentWindow()
if (cachedTauriWindow) {
cachedTauriWindow
.getCurrentWindow()
.setTheme(getTauriThemeForMode(themeMode.value))
.catch(() => {});
});
} else {
import("@tauri-apps/api/window").then((mod) => {
cachedTauriWindow = mod;
mod
.getCurrentWindow()
.setTheme(getTauriThemeForMode(themeMode.value))
.catch(() => {});
});
}
}
function setThemeMode(mode: AppThemeMode) {

View File

@ -119,6 +119,14 @@
--sidebar-ring: oklch(0.556 0 0);
}
html.disable-transitions,
html.disable-transitions *,
html.disable-transitions *::before,
html.disable-transitions *::after {
transition-duration: 0s !important;
animation-duration: 0s !important;
}
@layer base {
* {
@apply border-border outline-ring/50;