feat(spaceshd): GetConfig/SetConfig handlers with live ConfigChanged broadcast
This commit is contained in:
@@ -135,6 +135,7 @@ async fn router(
|
||||
let mut clients: HashMap<ClientId, ClientTx> = HashMap::new();
|
||||
// surface_id → set of client ids subscribed (attached).
|
||||
let mut subs: HashMap<SurfaceId, Vec<ClientId>> = HashMap::new();
|
||||
let mut config = crate::config::Config::load();
|
||||
|
||||
while let Some(msg) = rx.recv().await {
|
||||
match msg {
|
||||
@@ -177,7 +178,7 @@ async fn router(
|
||||
ServerMsg::Request { id, cmd, client, out } => {
|
||||
handle_request(id, cmd, client, out, &mut reg, &mut subs, &clients,
|
||||
&router_tx, &exit_tx, &state_tx, &persister,
|
||||
&mut event_log, &event_persister, started_at_ms).await;
|
||||
&mut event_log, &event_persister, started_at_ms, &mut config).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -276,6 +277,7 @@ async fn handle_request(
|
||||
event_log: &mut EventLog,
|
||||
event_persister: &EventPersister,
|
||||
started_at_ms: u64,
|
||||
config: &mut crate::config::Config,
|
||||
) {
|
||||
use spacesh_proto::message::SplitDir;
|
||||
use spacesh_proto::layout::{LayoutNode, Orient};
|
||||
@@ -298,7 +300,7 @@ async fn handle_request(
|
||||
let _ = out.send(err(id, "NOT_FOUND", "workspace")).await; return;
|
||||
};
|
||||
let sid = reg.new_surface_id();
|
||||
let shell = command.clone().unwrap_or_else(crate::config::default_shell);
|
||||
let shell = command.clone().unwrap_or_else(|| config.resolved_shell());
|
||||
let spec = SurfaceSpec {
|
||||
command: shell, args: args.clone(), cwd: ws.path.clone(),
|
||||
agent_label: command, cols, rows, autostart: false,
|
||||
@@ -333,7 +335,7 @@ async fn handle_request(
|
||||
};
|
||||
let ws = reg.workspace(&ws_id).cloned().unwrap();
|
||||
let new_sid = reg.new_surface_id();
|
||||
let shell = command.clone().unwrap_or_else(crate::config::default_shell);
|
||||
let shell = command.clone().unwrap_or_else(|| config.resolved_shell());
|
||||
let spec = SurfaceSpec { command: shell, args, cwd: ws.path.clone(), agent_label: command, cols: 80, rows: 24, autostart: false };
|
||||
let (env, hooks_active) = spawn_env(&new_sid, &spec);
|
||||
match crate::surface::spawn_from_spec(new_sid.clone(), ws_id.clone(), &spec, env, hooks_active, state_tx.clone(), exit_tx.clone()) {
|
||||
@@ -406,7 +408,7 @@ async fn handle_request(
|
||||
let slot = slots.get(i);
|
||||
let new_sid = reg.new_surface_id();
|
||||
let command = slot.and_then(|s| s.command.clone());
|
||||
let shell = command.clone().unwrap_or_else(crate::config::default_shell);
|
||||
let shell = command.clone().unwrap_or_else(|| config.resolved_shell());
|
||||
let args = slot.map(|s| s.args.clone()).unwrap_or_default();
|
||||
let spec = SurfaceSpec { command: shell, args, cwd: ws.path.clone(), agent_label: command, cols: 80, rows: 24, autostart: false };
|
||||
let (env, hooks_active) = spawn_env(&new_sid, &spec);
|
||||
@@ -649,6 +651,40 @@ async fn handle_request(
|
||||
let _ = out.send(ok(id, serde_json::Value::Null)).await;
|
||||
std::process::exit(0);
|
||||
}
|
||||
|
||||
Cmd::GetConfig => {
|
||||
match serde_json::to_value(config.to_view()) {
|
||||
Ok(v) => { let _ = out.send(ok(id, v)).await; }
|
||||
Err(e) => { let _ = out.send(err(id, "INTERNAL", &e.to_string())).await; }
|
||||
}
|
||||
}
|
||||
|
||||
Cmd::SetConfig { default_shell, font_family, font_size, theme, accent } => {
|
||||
if let Some(v) = &theme {
|
||||
if v != "dark" && v != "light" { let _ = out.send(err(id, "BAD_CONFIG", "theme")).await; return; }
|
||||
}
|
||||
if let Some(v) = &accent {
|
||||
const ACCENTS: [&str; 5] = ["blue", "teal", "purple", "green", "orange"];
|
||||
if !ACCENTS.contains(&v.as_str()) { let _ = out.send(err(id, "BAD_CONFIG", "accent")).await; return; }
|
||||
}
|
||||
let mut next = config.clone();
|
||||
if let Some(v) = default_shell { next.default_shell = if v.is_empty() { None } else { Some(v) }; }
|
||||
if let Some(v) = font_family { next.terminal.font_family = if v.is_empty() { None } else { Some(v) }; }
|
||||
if let Some(v) = font_size { next.terminal.font_size = Some(v.clamp(10, 20)); }
|
||||
if let Some(v) = theme { next.appearance.theme = Some(v); }
|
||||
if let Some(v) = accent { next.appearance.accent = Some(v); }
|
||||
let to_save = next.clone();
|
||||
match tokio::task::spawn_blocking(move || to_save.save()).await {
|
||||
Ok(Ok(())) => {
|
||||
*config = next;
|
||||
let view = config.to_view();
|
||||
broadcast_evt(clients, &Envelope::Evt(Evt::ConfigChanged { config: view }));
|
||||
let _ = out.send(ok(id, serde_json::Value::Null)).await;
|
||||
}
|
||||
Ok(Err(e)) => { let _ = out.send(err(id, "SAVE_FAILED", &e.to_string())).await; }
|
||||
Err(e) => { let _ = out.send(err(id, "SAVE_FAILED", &e.to_string())).await; }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user