Full port of the KaSe/KeSp split keyboard configurator from egui to Slint: - 6 tabs: Keymap, Advanced, Macros, Stats, Settings, Flash - Responsive keyboard view with scale-to-fit and key rotations - Key selector popup with categorized grid, MT/LT builders, hex input - Combo key picker with inline keyboard visual - Macro step builder with visual tags - Serial communication via background threads + mpsc polling - Heatmap overlay with blue-yellow-red gradient - OTA flasher with prog port VID filtering and partition selector - WPM polling, Tamagotchi, Autoshift controls - Dracula theme matching egui version Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
118 lines
3.5 KiB
Rust
118 lines
3.5 KiB
Rust
/// Persistent settings for KaSe Controller.
|
|
///
|
|
/// - **Native**: `kase_settings.json` next to the executable.
|
|
/// - **WASM**: `localStorage` under key `"kase_settings"`.
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct Settings {
|
|
#[serde(default = "default_layout")]
|
|
pub keyboard_layout: String,
|
|
}
|
|
|
|
fn default_layout() -> String {
|
|
"QWERTY".to_string()
|
|
}
|
|
|
|
impl Default for Settings {
|
|
fn default() -> Self {
|
|
Self {
|
|
keyboard_layout: default_layout(),
|
|
}
|
|
}
|
|
}
|
|
|
|
// =============================================================================
|
|
// Native: JSON file next to executable
|
|
// =============================================================================
|
|
|
|
#[cfg(not(target_arch = "wasm32"))]
|
|
mod native_settings {
|
|
use super::Settings;
|
|
use std::path::PathBuf;
|
|
|
|
/// Config file path: next to the executable.
|
|
fn settings_path() -> PathBuf {
|
|
let exe = std::env::current_exe().unwrap_or_default();
|
|
let parent_dir = exe.parent().unwrap_or(std::path::Path::new("."));
|
|
parent_dir.join("kase_settings.json")
|
|
}
|
|
|
|
pub fn load() -> Settings {
|
|
let path = settings_path();
|
|
let json_content = std::fs::read_to_string(&path).ok();
|
|
let parsed = json_content.and_then(|s| serde_json::from_str(&s).ok());
|
|
parsed.unwrap_or_default()
|
|
}
|
|
|
|
pub fn save(settings: &Settings) {
|
|
let path = settings_path();
|
|
let json_result = serde_json::to_string_pretty(settings);
|
|
if let Ok(json) = json_result {
|
|
let _ = std::fs::write(path, json);
|
|
}
|
|
}
|
|
}
|
|
|
|
// =============================================================================
|
|
// WASM: browser localStorage
|
|
// =============================================================================
|
|
|
|
#[cfg(target_arch = "wasm32")]
|
|
mod web_settings {
|
|
use super::Settings;
|
|
|
|
const STORAGE_KEY: &str = "kase_settings";
|
|
|
|
/// Get localStorage. Returns None if not in a browser context.
|
|
fn get_storage() -> Option<web_sys::Storage> {
|
|
let window = web_sys::window()?;
|
|
window.local_storage().ok().flatten()
|
|
}
|
|
|
|
pub fn load() -> Settings {
|
|
let storage = match get_storage() {
|
|
Some(s) => s,
|
|
None => return Settings::default(),
|
|
};
|
|
|
|
let json_option = storage.get_item(STORAGE_KEY).ok().flatten();
|
|
let parsed = json_option.and_then(|s| serde_json::from_str(&s).ok());
|
|
parsed.unwrap_or_default()
|
|
}
|
|
|
|
pub fn save(settings: &Settings) {
|
|
let storage = match get_storage() {
|
|
Some(s) => s,
|
|
None => return,
|
|
};
|
|
|
|
let json_result = serde_json::to_string(settings);
|
|
if let Ok(json) = json_result {
|
|
let _ = storage.set_item(STORAGE_KEY, &json);
|
|
}
|
|
}
|
|
}
|
|
|
|
// =============================================================================
|
|
// Public interface
|
|
// =============================================================================
|
|
|
|
/// Load settings from persistent storage.
|
|
/// Returns `Settings::default()` if none found.
|
|
pub fn load() -> Settings {
|
|
#[cfg(not(target_arch = "wasm32"))]
|
|
return native_settings::load();
|
|
|
|
#[cfg(target_arch = "wasm32")]
|
|
return web_settings::load();
|
|
}
|
|
|
|
/// Save settings to persistent storage. Fails silently.
|
|
pub fn save(settings: &Settings) {
|
|
#[cfg(not(target_arch = "wasm32"))]
|
|
native_settings::save(settings);
|
|
|
|
#[cfg(target_arch = "wasm32")]
|
|
web_settings::save(settings);
|
|
}
|