This commit is contained in:
+57
-2
@@ -1,6 +1,8 @@
|
||||
use rabby_core::{
|
||||
demo_dashboard, wallet_feature_summary, WalletDashboard, WalletFeatureStatus, Workspace,
|
||||
demo_dashboard, wallet_feature_summary, AppConfig, WalletDashboard, WalletFeatureStatus,
|
||||
Workspace,
|
||||
};
|
||||
use rabby_runtime::config_store::{ConfigStore, JsonFileConfigStore};
|
||||
use rabby_runtime::local_terminal::{LocalCommand, LocalCommandOutput, LocalTerminal};
|
||||
use rabby_runtime::wallet_vault::{EncryptedWalletVault, WalletVaultStatus};
|
||||
use serde::Serialize;
|
||||
@@ -75,6 +77,31 @@ fn resolve_vault_path(path: Option<String>) -> PathBuf {
|
||||
.unwrap_or_else(|| std::env::temp_dir().join("rabby-demo-wallet-vault.json"))
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
fn load_app_config(path: Option<String>) -> Result<AppConfig, String> {
|
||||
let config_path = resolve_config_path(path);
|
||||
if !config_path.exists() {
|
||||
return Ok(AppConfig::default_linux());
|
||||
}
|
||||
JsonFileConfigStore::new(config_path)
|
||||
.load()
|
||||
.map_err(|err| err.to_string())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
fn save_app_config(config: AppConfig, path: Option<String>) -> Result<AppConfig, String> {
|
||||
let config_path = resolve_config_path(path);
|
||||
JsonFileConfigStore::new(config_path)
|
||||
.save(&config)
|
||||
.map_err(|err| err.to_string())?;
|
||||
Ok(config)
|
||||
}
|
||||
|
||||
fn resolve_config_path(path: Option<String>) -> PathBuf {
|
||||
path.map(PathBuf::from)
|
||||
.unwrap_or_else(|| std::env::temp_dir().join("rabby-app-config.json"))
|
||||
}
|
||||
|
||||
fn main() {
|
||||
tauri::Builder::default()
|
||||
.invoke_handler(tauri::generate_handler![
|
||||
@@ -83,7 +110,9 @@ fn main() {
|
||||
wallet_dashboard,
|
||||
save_demo_wallet_vault,
|
||||
load_wallet_vault,
|
||||
run_local_command
|
||||
run_local_command,
|
||||
load_app_config,
|
||||
save_app_config
|
||||
])
|
||||
.run(tauri::generate_context!())
|
||||
.expect("failed to run Rabby Tauri application");
|
||||
@@ -114,4 +143,30 @@ mod tests {
|
||||
assert_eq!(output.exit_code, 0);
|
||||
assert_eq!(output.stdout, "tauri-local");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn config_path_has_stable_file_name() {
|
||||
let path = resolve_config_path(None);
|
||||
assert_eq!(
|
||||
path.file_name().and_then(|value| value.to_str()),
|
||||
Some("rabby-app-config.json")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn save_and_load_app_config_round_trips_custom_theme() {
|
||||
let unique = std::time::SystemTime::now()
|
||||
.duration_since(std::time::UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.as_nanos();
|
||||
let path = std::env::temp_dir().join(format!("rabby-tauri-config-{unique}.json"));
|
||||
let mut config = AppConfig::default_linux();
|
||||
config.theme_id = "rabby-light".to_string();
|
||||
|
||||
save_app_config(config.clone(), Some(path.display().to_string())).unwrap();
|
||||
let loaded = load_app_config(Some(path.display().to_string())).unwrap();
|
||||
|
||||
assert_eq!(loaded.theme_id, "rabby-light");
|
||||
assert_eq!(loaded, config);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -136,6 +136,31 @@ function wireVaultActions() {
|
||||
if (button) button.addEventListener('click', saveDemoVault);
|
||||
}
|
||||
|
||||
async function loadAppConfig() {
|
||||
return invokeOrFallback('load_app_config', { theme_id: 'rabby-light', restore_workspace: true, profiles: [], shortcuts: [] });
|
||||
}
|
||||
|
||||
async function saveSettings() {
|
||||
const theme = document.getElementById('theme-select').value;
|
||||
const output = document.getElementById('settings-status');
|
||||
output.textContent = 'Saving settings…';
|
||||
try {
|
||||
const tauri = window.__TAURI__?.core;
|
||||
if (!tauri) throw new Error('Tauri bridge unavailable in browser preview');
|
||||
const config = await loadAppConfig();
|
||||
config.theme_id = theme;
|
||||
const saved = await tauri.invoke('save_app_config', { config, path: null });
|
||||
output.textContent = `Saved ${saved.theme_id}`;
|
||||
} catch (error) {
|
||||
output.textContent = String(error);
|
||||
}
|
||||
}
|
||||
|
||||
function wireSettingsActions() {
|
||||
const button = document.getElementById('save-settings');
|
||||
if (button) button.addEventListener('click', saveSettings);
|
||||
}
|
||||
|
||||
async function runLocalTerminalCommand() {
|
||||
const command = document.getElementById('terminal-command').value;
|
||||
const output = document.getElementById('terminal-output');
|
||||
@@ -168,6 +193,7 @@ async function boot() {
|
||||
renderDashboard(dashboard);
|
||||
renderFeatures(features);
|
||||
wireVaultActions();
|
||||
wireSettingsActions();
|
||||
wireTerminalActions();
|
||||
}
|
||||
|
||||
|
||||
@@ -120,6 +120,19 @@
|
||||
</div>
|
||||
</div>
|
||||
<div id="settings-summary" class="settings-grid"></div>
|
||||
<div class="settings-form">
|
||||
<p class="eyebrow">Persistent customization</p>
|
||||
<label for="theme-select">Theme</label>
|
||||
<div class="vault-row">
|
||||
<select id="theme-select">
|
||||
<option value="rabby-light">Rabby light</option>
|
||||
<option value="rabby-dark">Rabby dark</option>
|
||||
<option value="rabby-system">System</option>
|
||||
</select>
|
||||
<button id="save-settings" class="secondary" type="button">Save settings</button>
|
||||
</div>
|
||||
<small id="settings-status">Customization persists through the Rust config store.</small>
|
||||
</div>
|
||||
<div class="vault-form">
|
||||
<p class="eyebrow">Encrypted local state</p>
|
||||
<label for="vault-passphrase">Demo vault passphrase</label>
|
||||
|
||||
@@ -312,3 +312,8 @@ h3 { margin-bottom: 0; font-size: 18px; }
|
||||
.terminal-form { margin-top: 18px; display: grid; gap: 12px; }
|
||||
.terminal-form input { width: 100%; border: 1px solid #dfe6f6; border-radius: 14px; padding: 12px 14px; color: #253052; background: #f8faff; font: 14px/1.4 ui-monospace, SFMono-Regular, Consolas, monospace; }
|
||||
.terminal-form pre { min-height: 120px; margin: 0; padding: 16px; border-radius: 18px; color: #b9fbcf; background: #12182b; white-space: pre-wrap; overflow: auto; font: 13px/1.5 ui-monospace, SFMono-Regular, Consolas, monospace; }
|
||||
|
||||
|
||||
.settings-form { margin-top: 18px; padding-top: 16px; border-top: 1px solid #edf1fb; display: grid; gap: 8px; }
|
||||
.settings-form label { color: #62708f; font-weight: 800; }
|
||||
.settings-form select { flex: 1; min-width: 0; border: 1px solid #dfe6f6; border-radius: 14px; padding: 11px 12px; color: #253052; background: #f8faff; }
|
||||
|
||||
Reference in New Issue
Block a user