feat(v2): add agent tree, status bar, notifications, settings dialog (Phase 5)
Agent tree visualization (SVG) with horizontal layout and bezier edges. Global status bar with pane counts, active agents pulse, token/cost totals. Toast notification system with auto-dismiss and agent dispatcher integration. Settings dialog with SQLite persistence for shell, cwd, and max panes. Keyboard shortcuts: Ctrl+W close pane, Ctrl+, open settings.
This commit is contained in:
parent
cd1271adf0
commit
be24d07c65
13 changed files with 809 additions and 2 deletions
|
|
@ -132,6 +132,23 @@ fn layout_load(state: State<'_, AppState>) -> Result<LayoutState, String> {
|
|||
state.session_db.load_layout()
|
||||
}
|
||||
|
||||
// --- Settings commands ---
|
||||
|
||||
#[tauri::command]
|
||||
fn settings_get(state: State<'_, AppState>, key: String) -> Result<Option<String>, String> {
|
||||
state.session_db.get_setting(&key)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
fn settings_set(state: State<'_, AppState>, key: String, value: String) -> Result<(), String> {
|
||||
state.session_db.set_setting(&key, &value)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
fn settings_list(state: State<'_, AppState>) -> Result<Vec<(String, String)>, String> {
|
||||
state.session_db.get_all_settings()
|
||||
}
|
||||
|
||||
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
||||
pub fn run() {
|
||||
let pty_manager = Arc::new(PtyManager::new());
|
||||
|
|
@ -175,6 +192,9 @@ pub fn run() {
|
|||
session_touch,
|
||||
layout_save,
|
||||
layout_load,
|
||||
settings_get,
|
||||
settings_set,
|
||||
settings_list,
|
||||
])
|
||||
.setup(move |app| {
|
||||
if cfg!(debug_assertions) {
|
||||
|
|
|
|||
|
|
@ -68,11 +68,51 @@ impl SessionDb {
|
|||
);
|
||||
|
||||
INSERT OR IGNORE INTO layout_state (id, preset, pane_ids) VALUES (1, '1-col', '[]');
|
||||
|
||||
CREATE TABLE IF NOT EXISTS settings (
|
||||
key TEXT PRIMARY KEY,
|
||||
value TEXT NOT NULL
|
||||
);
|
||||
"
|
||||
).map_err(|e| format!("Migration failed: {e}"))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_setting(&self, key: &str) -> Result<Option<String>, String> {
|
||||
let conn = self.conn.lock().unwrap();
|
||||
let mut stmt = conn
|
||||
.prepare("SELECT value FROM settings WHERE key = ?1")
|
||||
.map_err(|e| format!("Settings query failed: {e}"))?;
|
||||
let result = stmt.query_row(params![key], |row| row.get(0));
|
||||
match result {
|
||||
Ok(val) => Ok(Some(val)),
|
||||
Err(rusqlite::Error::QueryReturnedNoRows) => Ok(None),
|
||||
Err(e) => Err(format!("Settings read failed: {e}")),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_setting(&self, key: &str, value: &str) -> Result<(), String> {
|
||||
let conn = self.conn.lock().unwrap();
|
||||
conn.execute(
|
||||
"INSERT OR REPLACE INTO settings (key, value) VALUES (?1, ?2)",
|
||||
params![key, value],
|
||||
).map_err(|e| format!("Settings write failed: {e}"))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_all_settings(&self) -> Result<Vec<(String, String)>, String> {
|
||||
let conn = self.conn.lock().unwrap();
|
||||
let mut stmt = conn
|
||||
.prepare("SELECT key, value FROM settings ORDER BY key")
|
||||
.map_err(|e| format!("Settings query failed: {e}"))?;
|
||||
let settings = stmt
|
||||
.query_map([], |row| Ok((row.get(0)?, row.get(1)?)))
|
||||
.map_err(|e| format!("Settings query failed: {e}"))?
|
||||
.collect::<Result<Vec<_>, _>>()
|
||||
.map_err(|e| format!("Settings read failed: {e}"))?;
|
||||
Ok(settings)
|
||||
}
|
||||
|
||||
pub fn list_sessions(&self) -> Result<Vec<Session>, String> {
|
||||
let conn = self.conn.lock().unwrap();
|
||||
let mut stmt = conn
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue