mirror of
https://github.com/rust-kotlin/ashell.git
synced 2026-09-22 00:00:59 +00:00
feat: fix Windows flashing, UI thread blocking, and idle redraws (fixes #90)
This commit is contained in:
Generated
+11
@@ -363,6 +363,7 @@ dependencies = [
|
||||
"tracing-subscriber",
|
||||
"uuid",
|
||||
"walkdir",
|
||||
"winreg 0.52.0",
|
||||
"winres",
|
||||
"zip",
|
||||
]
|
||||
@@ -9643,6 +9644,16 @@ dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winreg"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a277a57398d4bfa075df44f501a17cfdf8542d224f0d36095a2adc7aee4ef0a5"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winreg"
|
||||
version = "0.55.0"
|
||||
|
||||
@@ -58,6 +58,9 @@ sha2 = "0.10"
|
||||
[dev-dependencies]
|
||||
libc = "0.2"
|
||||
|
||||
[target.'cfg(target_os = "windows")'.dependencies]
|
||||
winreg = "0.52"
|
||||
|
||||
[package.metadata.deb]
|
||||
maintainer = "ashell contributors"
|
||||
license-file = ["LICENSE"]
|
||||
|
||||
+12
-14
@@ -1425,9 +1425,7 @@ impl Ashell {
|
||||
this.recording_action = None;
|
||||
this.keybind_error = None;
|
||||
this.config.set_key_binding(&action, &new_key);
|
||||
if let Err(err) = this.config.save() {
|
||||
tracing::error!("failed to save key binding: {err:#}");
|
||||
}
|
||||
this.save_preferences_background();
|
||||
cx.notify();
|
||||
});
|
||||
}
|
||||
@@ -1600,7 +1598,7 @@ impl Ashell {
|
||||
.checked(current_style == crate::session::config::TitleBarStyle::Native)
|
||||
.on_click(window.listener_for(&view, |this, _, _, cx| {
|
||||
this.config.set_title_bar_style(crate::session::config::TitleBarStyle::Native);
|
||||
let _ = this.config.save();
|
||||
this.save_preferences_background();
|
||||
cx.notify();
|
||||
}))
|
||||
)
|
||||
@@ -1609,7 +1607,7 @@ impl Ashell {
|
||||
.checked(current_style == crate::session::config::TitleBarStyle::Integrated)
|
||||
.on_click(window.listener_for(&view, |this, _, _, cx| {
|
||||
this.config.set_title_bar_style(crate::session::config::TitleBarStyle::Integrated);
|
||||
let _ = this.config.save();
|
||||
this.save_preferences_background();
|
||||
cx.notify();
|
||||
}))
|
||||
);
|
||||
@@ -1844,7 +1842,7 @@ impl Ashell {
|
||||
.checked(view.read(cx).config.right_click_copy_paste())
|
||||
.on_click(window.listener_for(&view, |this, checked, _, cx| {
|
||||
this.config.set_right_click_copy_paste(*checked);
|
||||
let _ = this.config.save();
|
||||
this.save_preferences_background();
|
||||
cx.notify();
|
||||
}))
|
||||
.into_any_element()
|
||||
@@ -1863,7 +1861,7 @@ impl Ashell {
|
||||
.checked(view.read(cx).config.keyword_highlight())
|
||||
.on_click(window.listener_for(&view, |this, checked, _, cx| {
|
||||
this.config.set_keyword_highlight(*checked);
|
||||
let _ = this.config.save();
|
||||
this.save_preferences_background();
|
||||
cx.notify();
|
||||
}))
|
||||
.into_any_element()
|
||||
@@ -1882,7 +1880,7 @@ impl Ashell {
|
||||
.checked(view.read(cx).config.lock_layout())
|
||||
.on_click(window.listener_for(&view, |this, checked, _, cx| {
|
||||
this.config.set_lock_layout(*checked);
|
||||
let _ = this.config.save();
|
||||
this.save_preferences_background();
|
||||
cx.notify();
|
||||
}))
|
||||
.into_any_element()
|
||||
@@ -1919,7 +1917,7 @@ impl Ashell {
|
||||
.checked(pos == "Bottom")
|
||||
.on_click(window.listener_for(&view, |this, _, _window, cx| {
|
||||
this.config.set_monitoring_position("Bottom");
|
||||
let _ = this.config.save();
|
||||
this.save_preferences_background();
|
||||
cx.notify();
|
||||
}))
|
||||
)
|
||||
@@ -1928,7 +1926,7 @@ impl Ashell {
|
||||
.checked(pos == "Sidebar")
|
||||
.on_click(window.listener_for(&view, |this, _, _window, cx| {
|
||||
this.config.set_monitoring_position("Sidebar");
|
||||
let _ = this.config.save();
|
||||
this.save_preferences_background();
|
||||
cx.notify();
|
||||
}))
|
||||
)
|
||||
@@ -1937,7 +1935,7 @@ impl Ashell {
|
||||
.checked(pos == "Hidden")
|
||||
.on_click(window.listener_for(&view, |this, _, _window, cx| {
|
||||
this.config.set_monitoring_position("Hidden");
|
||||
let _ = this.config.save();
|
||||
this.save_preferences_background();
|
||||
cx.notify();
|
||||
}))
|
||||
);
|
||||
@@ -2109,7 +2107,7 @@ impl Ashell {
|
||||
.checked(view.read(cx).config.use_proxy())
|
||||
.on_click(window.listener_for(&view, |this, checked, _, cx| {
|
||||
this.config.set_use_proxy(*checked);
|
||||
let _ = this.config.save();
|
||||
this.save_preferences_background();
|
||||
cx.notify();
|
||||
}))
|
||||
.into_any_element()
|
||||
@@ -2128,7 +2126,7 @@ impl Ashell {
|
||||
.checked(view.read(cx).config.read_env_proxy())
|
||||
.on_click(window.listener_for(&view, |this, checked, _, cx| {
|
||||
this.config.set_read_env_proxy(*checked);
|
||||
let _ = this.config.save();
|
||||
this.save_preferences_background();
|
||||
cx.notify();
|
||||
}))
|
||||
.into_any_element()
|
||||
@@ -2197,7 +2195,7 @@ impl Ashell {
|
||||
this.config.set_global_proxy_port(port);
|
||||
this.config.set_global_proxy_user(user);
|
||||
this.config.set_global_proxy_password(password);
|
||||
let _ = this.config.save();
|
||||
this.save_preferences_background();
|
||||
cx.notify();
|
||||
}))
|
||||
)
|
||||
|
||||
+28
-10
@@ -327,6 +327,8 @@ pub(crate) struct Ashell {
|
||||
pub(crate) hovered_url: Option<HoveredUrl>,
|
||||
pub(crate) cmd_ctrl_pressed: bool,
|
||||
pub(crate) _subscriptions: Vec<gpui::Subscription>,
|
||||
pub(crate) save_lock: std::sync::Arc<tokio::sync::Mutex<()>>,
|
||||
pub(crate) save_latest_seq: std::sync::Arc<std::sync::atomic::AtomicU64>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
@@ -718,6 +720,8 @@ impl Ashell {
|
||||
hovered_url: None,
|
||||
cmd_ctrl_pressed: false,
|
||||
_subscriptions,
|
||||
save_lock: std::sync::Arc::new(tokio::sync::Mutex::new(())),
|
||||
save_latest_seq: std::sync::Arc::new(std::sync::atomic::AtomicU64::new(0)),
|
||||
};
|
||||
|
||||
this.apply_theme_preferences(window, cx);
|
||||
@@ -784,9 +788,29 @@ impl Ashell {
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
pub(crate) fn save_preferences_background(&mut self) {
|
||||
let local_config = self.config.cache.clone();
|
||||
let config_store = self.config.clone();
|
||||
let latest_seq = self.save_latest_seq.clone();
|
||||
let current_seq = latest_seq.fetch_add(1, std::sync::atomic::Ordering::SeqCst) + 1;
|
||||
let save_lock = self.save_lock.clone();
|
||||
|
||||
self.runtime.spawn(async move {
|
||||
let _guard = save_lock.lock().await;
|
||||
if current_seq < latest_seq.load(std::sync::atomic::Ordering::SeqCst) {
|
||||
return;
|
||||
}
|
||||
let _ = tokio::task::spawn_blocking(move || {
|
||||
if let Err(err) = config_store.save_merged_preferences(local_config) {
|
||||
tracing::error!("failed to save merged preferences in background: {err:#}");
|
||||
}
|
||||
})
|
||||
.await;
|
||||
});
|
||||
}
|
||||
|
||||
pub(crate) fn start_event_pump(&self, cx: &mut Context<Self>) {
|
||||
cx.spawn(async move |this, cx| {
|
||||
let mut idle_frames = 0u32;
|
||||
let mut last_blink_time = std::time::Instant::now();
|
||||
loop {
|
||||
cx.background_executor()
|
||||
@@ -808,16 +832,9 @@ impl Ashell {
|
||||
>= std::time::Duration::from_millis(600);
|
||||
if changed || system_sampled || blink_due {
|
||||
cx.notify();
|
||||
idle_frames = 0;
|
||||
if blink_due {
|
||||
last_blink_time = now;
|
||||
}
|
||||
} else {
|
||||
idle_frames += 1;
|
||||
if idle_frames >= 60 {
|
||||
cx.notify();
|
||||
idle_frames = 0;
|
||||
}
|
||||
}
|
||||
})
|
||||
.is_err()
|
||||
@@ -1331,8 +1348,9 @@ impl Ashell {
|
||||
};
|
||||
let size = bounds.size;
|
||||
if size.width.as_f32() > 400.0 && size.height.as_f32() > 300.0 {
|
||||
tracing::info!("[ui] saving layout state...");
|
||||
let mut config = ConfigStore::load().unwrap_or_else(|_| ConfigStore::in_memory());
|
||||
self.save_latest_seq
|
||||
.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
|
||||
let mut config = self.config.clone();
|
||||
let saved_bounds = match current_bounds {
|
||||
gpui::WindowBounds::Fullscreen(b) => {
|
||||
crate::session::config::SavedWindowBounds::Fullscreen {
|
||||
|
||||
+2
-6
@@ -126,9 +126,7 @@ impl Ashell {
|
||||
}
|
||||
rust_i18n::set_locale(&active_locale);
|
||||
gpui_component::set_locale(&active_locale);
|
||||
if let Err(err) = self.config.save() {
|
||||
tracing::warn!("failed to save language preferences: {err:#}");
|
||||
}
|
||||
self.save_preferences_background();
|
||||
window.refresh();
|
||||
cx.notify();
|
||||
}
|
||||
@@ -168,8 +166,6 @@ impl Ashell {
|
||||
self.light_theme_name.to_string(),
|
||||
self.dark_theme_name.to_string(),
|
||||
);
|
||||
if let Err(err) = self.config.save() {
|
||||
tracing::warn!("failed to save theme preferences: {err:#}");
|
||||
}
|
||||
self.save_preferences_background();
|
||||
}
|
||||
}
|
||||
|
||||
+5
-5
@@ -111,7 +111,7 @@ impl Ashell {
|
||||
}
|
||||
self.config
|
||||
.set_sftp_panel_minimized(self.sftp_panel_minimized);
|
||||
let _ = self.config.save();
|
||||
self.save_preferences_background();
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
@@ -293,7 +293,7 @@ impl Ashell {
|
||||
.on_click(cx.listener(|this, checked, _, cx| {
|
||||
this.show_hidden_files = *checked;
|
||||
this.config.set_show_hidden_files(*checked);
|
||||
let _ = this.config.save();
|
||||
this.save_preferences_background();
|
||||
cx.notify();
|
||||
})),
|
||||
)
|
||||
@@ -1593,7 +1593,7 @@ impl Ashell {
|
||||
.on_click(cx.listener(|this, _, _, cx| {
|
||||
this.sidebar_collapsed = true;
|
||||
this.config.set_sidebar_collapsed(true);
|
||||
let _ = this.config.save();
|
||||
this.save_preferences_background();
|
||||
cx.notify();
|
||||
})),
|
||||
)
|
||||
@@ -1833,7 +1833,7 @@ impl Ashell {
|
||||
.on_click(cx.listener(|this, _, _, cx| {
|
||||
this.sidebar_collapsed = false;
|
||||
this.config.set_sidebar_collapsed(false);
|
||||
let _ = this.config.save();
|
||||
this.save_preferences_background();
|
||||
cx.notify();
|
||||
})),
|
||||
),
|
||||
@@ -2797,7 +2797,7 @@ impl Render for Ashell {
|
||||
.on_action(cx.listener(|this, _: &crate::ToggleSidebar, _, cx| {
|
||||
this.sidebar_collapsed = !this.sidebar_collapsed;
|
||||
this.config.set_sidebar_collapsed(this.sidebar_collapsed);
|
||||
let _ = this.config.save();
|
||||
this.save_preferences_background();
|
||||
cx.notify();
|
||||
}))
|
||||
.on_action(cx.listener(|this, _: &crate::ToggleSftpZoom, window, cx| {
|
||||
|
||||
+166
-72
@@ -1,4 +1,4 @@
|
||||
use std::{fs, path::PathBuf};
|
||||
use std::{fs, path::PathBuf, sync::OnceLock};
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use argon2::Argon2;
|
||||
@@ -364,9 +364,10 @@ impl Default for ConfigFile {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ConfigStore {
|
||||
path: PathBuf,
|
||||
cache: ConfigFile,
|
||||
pub(crate) path: PathBuf,
|
||||
pub(crate) cache: ConfigFile,
|
||||
}
|
||||
|
||||
impl ConfigStore {
|
||||
@@ -828,6 +829,65 @@ impl ConfigStore {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn save_merged_preferences(&self, local_config: ConfigFile) -> Result<()> {
|
||||
if self.path.as_os_str().is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
let hardware_uuid = get_hardware_uuid();
|
||||
|
||||
let mut disk_config = if self.path.exists() {
|
||||
if let Ok(raw_bytes) = fs::read(&self.path) {
|
||||
match decrypt_config(&raw_bytes, &hardware_uuid) {
|
||||
Ok(loaded) => loaded,
|
||||
Err(_) => serde_json::from_slice::<ConfigFile>(&raw_bytes)
|
||||
.unwrap_or_else(|_| self.cache.clone()),
|
||||
}
|
||||
} else {
|
||||
self.cache.clone()
|
||||
}
|
||||
} else {
|
||||
self.cache.clone()
|
||||
};
|
||||
|
||||
// Merge UI preference fields
|
||||
disk_config.follow_system_theme = local_config.follow_system_theme;
|
||||
disk_config.theme_mode = local_config.theme_mode;
|
||||
disk_config.light_theme_name = local_config.light_theme_name;
|
||||
disk_config.dark_theme_name = local_config.dark_theme_name;
|
||||
disk_config.locale = local_config.locale;
|
||||
disk_config.terminal_font_size = local_config.terminal_font_size;
|
||||
disk_config.ui_font_size = local_config.ui_font_size;
|
||||
disk_config.right_click_copy_paste = local_config.right_click_copy_paste;
|
||||
disk_config.keyword_highlight = local_config.keyword_highlight;
|
||||
disk_config.ui_font_family = local_config.ui_font_family;
|
||||
disk_config.terminal_font_family = local_config.terminal_font_family;
|
||||
disk_config.title_bar_style = local_config.title_bar_style;
|
||||
disk_config.cursor_style = local_config.cursor_style;
|
||||
disk_config.window_bounds = local_config.window_bounds;
|
||||
disk_config.workspace_panels = local_config.workspace_panels;
|
||||
disk_config.body_panels = local_config.body_panels;
|
||||
disk_config.show_hidden_files = local_config.show_hidden_files;
|
||||
disk_config.lock_layout = local_config.lock_layout;
|
||||
disk_config.monitoring_position = local_config.monitoring_position;
|
||||
disk_config.sidebar_collapsed = local_config.sidebar_collapsed;
|
||||
disk_config.sftp_panel_minimized = local_config.sftp_panel_minimized;
|
||||
|
||||
let encrypted_bytes = encrypt_config(&disk_config, &hardware_uuid)?;
|
||||
fs::write(&self.path, encrypted_bytes)
|
||||
.with_context(|| format!("failed to write {}", self.path.display()))?;
|
||||
|
||||
#[cfg(unix)]
|
||||
{
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
if let Ok(mut perms) = fs::metadata(&self.path).map(|m| m.permissions()) {
|
||||
perms.set_mode(0o600);
|
||||
let _ = fs::set_permissions(&self.path, perms);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ProxyStream:
|
||||
@@ -839,8 +899,6 @@ impl<T: tokio::io::AsyncRead + tokio::io::AsyncWrite + Unpin + Send + Sync + 'st
|
||||
{
|
||||
}
|
||||
|
||||
use std::sync::OnceLock;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct EnvProxy {
|
||||
pub proxy_type: String,
|
||||
@@ -1033,64 +1091,60 @@ struct EncryptedConfigEnvelope {
|
||||
payload: String,
|
||||
}
|
||||
|
||||
fn get_hardware_uuid() -> String {
|
||||
#[cfg(target_os = "macos")]
|
||||
{
|
||||
if let Ok(output) = std::process::Command::new("ioreg")
|
||||
.args(&["-rd1", "-c", "IOPlatformExpertDevice"])
|
||||
.output()
|
||||
{
|
||||
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||
for line in stdout.lines() {
|
||||
if line.contains("IOPlatformUUID") {
|
||||
if let Some(uuid) = line.split('"').nth(3) {
|
||||
let uuid = uuid.trim().to_string();
|
||||
if !uuid.is_empty() {
|
||||
return uuid;
|
||||
static HARDWARE_UUID_CACHE: OnceLock<String> = OnceLock::new();
|
||||
|
||||
pub fn get_hardware_uuid() -> String {
|
||||
HARDWARE_UUID_CACHE
|
||||
.get_or_init(|| {
|
||||
#[cfg(target_os = "macos")]
|
||||
{
|
||||
if let Ok(output) = std::process::Command::new("ioreg")
|
||||
.args(&["-rd1", "-c", "IOPlatformExpertDevice"])
|
||||
.output()
|
||||
{
|
||||
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||
for line in stdout.lines() {
|
||||
if line.contains("IOPlatformUUID") {
|
||||
if let Some(uuid) = line.split('"').nth(3) {
|
||||
let uuid = uuid.trim().to_string();
|
||||
if !uuid.is_empty() {
|
||||
return uuid;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
if let Ok(uuid) = std::fs::read_to_string("/sys/class/dmi/id/product_uuid") {
|
||||
let uuid = uuid.trim().to_string();
|
||||
if !uuid.is_empty() {
|
||||
return uuid;
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
if let Ok(uuid) = std::fs::read_to_string("/sys/class/dmi/id/product_uuid") {
|
||||
let uuid = uuid.trim().to_string();
|
||||
if !uuid.is_empty() {
|
||||
return uuid;
|
||||
}
|
||||
}
|
||||
if let Ok(id) = std::fs::read_to_string("/etc/machine-id") {
|
||||
let id = id.trim().to_string();
|
||||
if !id.is_empty() {
|
||||
return id;
|
||||
}
|
||||
}
|
||||
if let Ok(id) = std::fs::read_to_string("/var/lib/dbus/machine-id") {
|
||||
let id = id.trim().to_string();
|
||||
if !id.is_empty() {
|
||||
return id;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Ok(id) = std::fs::read_to_string("/etc/machine-id") {
|
||||
let id = id.trim().to_string();
|
||||
if !id.is_empty() {
|
||||
return id;
|
||||
}
|
||||
}
|
||||
if let Ok(id) = std::fs::read_to_string("/var/lib/dbus/machine-id") {
|
||||
let id = id.trim().to_string();
|
||||
if !id.is_empty() {
|
||||
return id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
if let Ok(output) = std::process::Command::new("reg")
|
||||
.args(&[
|
||||
"query",
|
||||
"HKLM\\SOFTWARE\\Microsoft\\Cryptography",
|
||||
"/v",
|
||||
"MachineGuid",
|
||||
])
|
||||
.output()
|
||||
{
|
||||
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||
for line in stdout.lines() {
|
||||
if line.contains("MachineGuid") {
|
||||
if let Some(guid) = line.split_whitespace().last() {
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
use winreg::RegKey;
|
||||
use winreg::enums::HKEY_LOCAL_MACHINE;
|
||||
let hklm = RegKey::predef(HKEY_LOCAL_MACHINE);
|
||||
if let Ok(subkey) = hklm.open_subkey("SOFTWARE\\Microsoft\\Cryptography") {
|
||||
if let Ok(guid) = subkey.get_value::<String, _>("MachineGuid") {
|
||||
let guid = guid.trim().to_string();
|
||||
if !guid.is_empty() {
|
||||
return guid;
|
||||
@@ -1098,23 +1152,10 @@ fn get_hardware_uuid() -> String {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Ok(output) = std::process::Command::new("wmic")
|
||||
.args(&["csproduct", "get", "uuid"])
|
||||
.output()
|
||||
{
|
||||
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||
let lines: Vec<&str> = stdout.lines().collect();
|
||||
if lines.len() >= 2 {
|
||||
let uuid = lines[1].trim().to_string();
|
||||
if !uuid.is_empty() {
|
||||
return uuid;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
"ashell-default-hardware-uuid-fallback".to_string()
|
||||
"ashell-default-hardware-uuid-fallback".to_string()
|
||||
})
|
||||
.clone()
|
||||
}
|
||||
|
||||
fn encrypt_config(config: &ConfigFile, password: &str) -> Result<Vec<u8>> {
|
||||
@@ -1207,4 +1248,57 @@ mod tests {
|
||||
// Decrypt with wrong password should fail
|
||||
assert!(decrypt_config(&encrypted, "wrong-password").is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_save_merged_preferences() {
|
||||
let temp_dir = std::env::temp_dir();
|
||||
let path = temp_dir.join(format!("ashell-test-config-{}.json", Uuid::new_v4()));
|
||||
let mut store = ConfigStore {
|
||||
path: path.clone(),
|
||||
cache: ConfigFile::default(),
|
||||
};
|
||||
|
||||
let session = Session {
|
||||
id: "test-session-id".to_string(),
|
||||
name: "Test Session".to_string(),
|
||||
host: "1.2.3.4".to_string(),
|
||||
port: 22,
|
||||
user: "root".to_string(),
|
||||
auth: AuthMethod::Password,
|
||||
password: "pwd".to_string(),
|
||||
private_key_path: String::new(),
|
||||
private_key_inline: String::new(),
|
||||
passphrase: String::new(),
|
||||
last_used: None,
|
||||
proxy_type: String::new(),
|
||||
proxy_host: String::new(),
|
||||
proxy_port: None,
|
||||
proxy_user: String::new(),
|
||||
proxy_password: String::new(),
|
||||
protocol: "ssh".to_string(),
|
||||
baud_rate: 115200,
|
||||
};
|
||||
store.cache.sessions.push(session.clone());
|
||||
store.save().unwrap();
|
||||
|
||||
let mut local_config = ConfigFile::default();
|
||||
local_config.ui_font_size = 18.0;
|
||||
local_config.terminal_font_size = 20.0;
|
||||
local_config.show_hidden_files = true;
|
||||
|
||||
store.save_merged_preferences(local_config).unwrap();
|
||||
|
||||
let loaded_bytes = fs::read(&path).unwrap();
|
||||
let decrypted = decrypt_config(&loaded_bytes, &get_hardware_uuid()).unwrap();
|
||||
|
||||
assert_eq!(decrypted.ui_font_size, 18.0);
|
||||
assert_eq!(decrypted.terminal_font_size, 20.0);
|
||||
assert!(decrypted.show_hidden_files);
|
||||
|
||||
assert_eq!(decrypted.sessions.len(), 1);
|
||||
assert_eq!(decrypted.sessions[0].name, "Test Session");
|
||||
assert_eq!(decrypted.sessions[0].host, "1.2.3.4");
|
||||
|
||||
let _ = fs::remove_file(&path);
|
||||
}
|
||||
}
|
||||
|
||||
+6
-16
@@ -371,9 +371,7 @@ impl Ashell {
|
||||
pub(crate) fn change_terminal_font_size(&mut self, delta: f32, cx: &mut Context<Self>) {
|
||||
self.terminal_font_size = (self.terminal_font_size + delta).clamp(10.0, 24.0);
|
||||
self.config.set_terminal_font_size(self.terminal_font_size);
|
||||
if let Err(err) = self.config.save() {
|
||||
tracing::warn!("failed to save terminal font size: {err:#}");
|
||||
}
|
||||
self.save_preferences_background();
|
||||
self.status = format!("terminal font size: {:.0}px", self.terminal_font_size).into();
|
||||
cx.notify();
|
||||
}
|
||||
@@ -381,9 +379,7 @@ impl Ashell {
|
||||
pub(crate) fn change_ui_font_size(&mut self, delta: f32, cx: &mut Context<Self>) {
|
||||
self.ui_font_size = (self.ui_font_size + delta).clamp(8.0, 24.0);
|
||||
self.config.set_ui_font_size(self.ui_font_size);
|
||||
if let Err(err) = self.config.save() {
|
||||
tracing::warn!("failed to save UI font size: {err:#}");
|
||||
}
|
||||
self.save_preferences_background();
|
||||
Theme::global_mut(cx).font_size = px(self.ui_font_size);
|
||||
self.status = format!("UI font size: {:.0}px", self.ui_font_size).into();
|
||||
cx.notify();
|
||||
@@ -397,9 +393,7 @@ impl Ashell {
|
||||
) {
|
||||
self.ui_font_family = family.into();
|
||||
self.config.set_ui_font_family(family);
|
||||
if let Err(err) = self.config.save() {
|
||||
tracing::warn!("failed to save UI font family: {err:#}");
|
||||
}
|
||||
self.save_preferences_background();
|
||||
crate::app::theme::set_theme_font_names(Theme::global_mut(cx), &self.ui_font_family);
|
||||
cx.notify();
|
||||
window.refresh();
|
||||
@@ -408,9 +402,7 @@ impl Ashell {
|
||||
pub(crate) fn change_terminal_font_family(&mut self, family: &str, cx: &mut Context<Self>) {
|
||||
self.terminal_font_family = family.into();
|
||||
self.config.set_terminal_font_family(family);
|
||||
if let Err(err) = self.config.save() {
|
||||
tracing::warn!("failed to save terminal font family: {err:#}");
|
||||
}
|
||||
self.save_preferences_background();
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
@@ -421,15 +413,13 @@ impl Ashell {
|
||||
) {
|
||||
self.cursor_style = style;
|
||||
self.config.set_cursor_style(style);
|
||||
if let Err(err) = self.config.save() {
|
||||
tracing::warn!("failed to save cursor style: {err:#}");
|
||||
}
|
||||
self.save_preferences_background();
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
pub(crate) fn reset_layout(&mut self, _window: &mut Window, cx: &mut Context<Self>) {
|
||||
self.config.set_layout_state(None, None, None);
|
||||
let _ = self.config.save();
|
||||
self.save_preferences_background();
|
||||
|
||||
self.is_layout_reset = true;
|
||||
self.workspace_panels = cx.new(|_| crate::app::resizable::ResizableState::default());
|
||||
|
||||
Reference in New Issue
Block a user