diff options
Diffstat (limited to 'src/server')
| -rw-r--r-- | src/server/headless.rs | 67 | ||||
| -rw-r--r-- | src/server/notifications.rs | 2 |
2 files changed, 65 insertions, 4 deletions
diff --git a/src/server/headless.rs b/src/server/headless.rs index ed81b015..750e9250 100644 --- a/src/server/headless.rs +++ b/src/server/headless.rs @@ -100,7 +100,7 @@ fn non_empty_body(value: &str) -> Option<String> { enum LoopEvent { Timer, Internal(AppEvent), - Api(api::ApiRequestMessage), + Api(Box<api::ApiRequestMessage>), ServerEvent(ServerEvent), RenderRequested, } @@ -637,7 +637,7 @@ impl HeadlessServer { let event = { tokio::select! { maybe_api = self.app.api_rx.recv() => match maybe_api { - Some(msg) => LoopEvent::Api(msg), + Some(msg) => LoopEvent::Api(Box::new(msg)), None => LoopEvent::Timer, }, maybe_ev = self.app.event_rx.recv() => match maybe_ev { @@ -662,7 +662,7 @@ impl HeadlessServer { } } LoopEvent::Api(msg) => { - if self.handle_api_request_with_shutdown_check(msg) { + if self.handle_api_request_with_shutdown_check(*msg) { needs_render = true; needs_full_render = true; } @@ -1603,6 +1603,39 @@ impl HeadlessServer { .unwrap_or_else(|_| "{}".to_string()) } + fn handle_client_window_title_api(&mut self, id: String, title: Option<String>) -> String { + use api::schema::{ClientWindowTitleReason, ResponseResult}; + + let title = match title { + Some(title) => match sanitize_window_title_text(&title, 200) { + Some(title) => Some(title), + None => { + return serde_json::to_string(&api::schema::ErrorResponse { + id, + error: api::schema::ErrorBody { + code: "invalid_params".into(), + message: "window title is empty".into(), + }, + }) + .unwrap_or_else(|_| "{}".to_string()); + } + }, + None => None, + }; + let set_title = title.is_some(); + let changed = self.send_to_foreground_client(ServerMessage::WindowTitle { title }); + let reason = match (changed, set_title) { + (true, true) => ClientWindowTitleReason::Set, + (true, false) => ClientWindowTitleReason::Cleared, + (false, _) => ClientWindowTitleReason::NoForegroundClient, + }; + serde_json::to_string(&api::schema::SuccessResponse { + id, + result: ResponseResult::ClientWindowTitle { changed, reason }, + }) + .unwrap_or_else(|_| "{}".to_string()) + } + fn forward_api_notification_sound(&mut self, sound: api::schema::NotificationShowSound) { let Some(sound) = sound.to_sound() else { return; @@ -2511,6 +2544,23 @@ impl HeadlessServer { return true; } + match &msg.request.method { + api::schema::Method::ClientWindowTitleSet(params) => { + let response = self.handle_client_window_title_api( + msg.request.id.clone(), + Some(params.title.clone()), + ); + let _ = msg.respond_to.send(response); + return true; + } + api::schema::Method::ClientWindowTitleClear(_) => { + let response = self.handle_client_window_title_api(msg.request.id.clone(), None); + let _ = msg.respond_to.send(response); + return true; + } + _ => {} + } + let mut changed = api::request_changes_ui(&msg.request); let skip_default_workspace = matches!( &msg.request.method, @@ -3544,6 +3594,17 @@ fn sanitize_notification_text(value: &str, max_chars: usize) -> Option<String> { (!sanitized.is_empty()).then_some(sanitized) } +fn sanitize_window_title_text(value: &str, max_chars: usize) -> Option<String> { + let sanitized = value + .chars() + .filter(|ch| !matches!(*ch, '\u{1b}' | '\u{7}' | '\u{9c}') && !ch.is_control()) + .take(max_chars) + .collect::<String>() + .trim() + .to_string(); + (!sanitized.is_empty()).then_some(sanitized) +} + fn server_config_diagnostic_summaries(diagnostics: &[String]) -> (Option<String>, Option<String>) { let without_keybindings = diagnostics .iter() diff --git a/src/server/notifications.rs b/src/server/notifications.rs index 63bfe944..79ff63ca 100644 --- a/src/server/notifications.rs +++ b/src/server/notifications.rs @@ -121,10 +121,10 @@ mod tests { 0, crate::terminal_theme::TerminalTheme::default(), crate::pane::PaneShellConfig::new("/bin/sh", crate::config::ShellModeConfig::NonLogin), + &crate::pane::PaneLaunchEnv::default(), events, std::sync::Arc::new(tokio::sync::Notify::new()), std::sync::Arc::new(std::sync::atomic::AtomicBool::new(false)), - None, ) .unwrap(); let deadline = std::time::Instant::now() + std::time::Duration::from_secs(2); |
