From 207be3c771d281baae6e5fa0fb74be9a056e97a2 Mon Sep 17 00:00:00 2001 From: Can Celik Date: Tue, 1 Sep 2026 15:48:22 +0300 Subject: refactor: render the shell in the client (#3487) * feat: add opt-in client-rendered shell * feat: add client-owned shell mouse controls * feat: complete client-owned shell chrome * feat: add client-owned shell settings and notifications * feat: run client shell custom commands * feat: render popup terminals in client shell * feat: add client-owned scrollback and copy mode * fix: preserve client worktree trust defaults * feat: render startup diagnostics in client shell * feat: render onboarding in client shell * feat: render product announcements in client shell * feat: render release notes in client shell * feat: render mobile switcher in client shell * feat: render kitty graphics in client shell * fix: preserve client shell lifecycle coherence * refactor: route client shell commands through endpoint connections * refactor: make the client-rendered shell the default * refactor: remove monolithic launch mode * refactor: complete client-rendered shell cutover * perf: retain client pane surface updates * perf: scale retained pane surface updates * perf: remove remote pane input latency * test: make client shell checks platform-independent * fix: prevent client timer starvation * test: fix client shell platform gates * test: accept retained surfaces in cross-area checks * test: make client mode assertions platform-neutral * fix: badge only outdated integrations --- src/workspace.rs | 170 ++++++------------------------------------------------- 1 file changed, 16 insertions(+), 154 deletions(-) (limited to 'src/workspace.rs') diff --git a/src/workspace.rs b/src/workspace.rs index 0a719ab1..b4bba45d 100644 --- a/src/workspace.rs +++ b/src/workspace.rs @@ -19,8 +19,6 @@ mod aggregate; mod git; mod tab; -#[cfg(test)] -use self::git::git_ahead_behind; use self::git::git_status_cache_key_for_space; pub(crate) use self::{git::git_status_snapshot_for_cwd_with_demand, tab::MovedPane}; pub use self::{ @@ -274,9 +272,6 @@ impl Workspace { } } - // Test modules construct workspaces through the default constructor; production paths - // use the env-aware variant so pane identity env is always explicit. - #[cfg_attr(not(test), allow(dead_code))] pub fn new( initial_cwd: PathBuf, rows: u16, @@ -289,7 +284,7 @@ impl Workspace { render_notify: Arc, render_dirty: Arc, ) -> std::io::Result<(Self, TerminalState, TerminalRuntime)> { - Self::new_with_extra_env( + Self::new_with_tab( initial_cwd, rows, cols, @@ -300,6 +295,7 @@ impl Workspace { events, render_notify, render_dirty, + None, Vec::new(), ) } @@ -318,6 +314,20 @@ impl Workspace { render_dirty: Arc, extra_env: Vec<(String, String)>, ) -> std::io::Result<(Self, TerminalState, TerminalRuntime)> { + if extra_env.is_empty() { + return Self::new( + initial_cwd, + rows, + cols, + scrollback_limit_bytes, + host_terminal_theme, + host_terminal_appearance, + shell_config, + events, + render_notify, + render_dirty, + ); + } Self::new_with_tab( initial_cwd, rows, @@ -334,65 +344,6 @@ impl Workspace { ) } - // Kept for tests that do not need launch-env customization. - #[allow(dead_code)] - pub fn new_argv_command( - initial_cwd: PathBuf, - rows: u16, - cols: u16, - argv: &[String], - scrollback_limit_bytes: usize, - host_terminal_theme: crate::terminal_theme::TerminalTheme, - host_terminal_appearance: Option, - events: mpsc::Sender, - render_notify: Arc, - render_dirty: Arc, - ) -> std::io::Result<(Self, TerminalState, TerminalRuntime)> { - Self::new_argv_command_with_extra_env( - initial_cwd, - rows, - cols, - argv, - scrollback_limit_bytes, - host_terminal_theme, - host_terminal_appearance, - events, - render_notify, - render_dirty, - Vec::new(), - ) - } - - #[allow(clippy::too_many_arguments)] - pub fn new_argv_command_with_extra_env( - initial_cwd: PathBuf, - rows: u16, - cols: u16, - argv: &[String], - scrollback_limit_bytes: usize, - host_terminal_theme: crate::terminal_theme::TerminalTheme, - host_terminal_appearance: Option, - events: mpsc::Sender, - render_notify: Arc, - render_dirty: Arc, - extra_env: Vec<(String, String)>, - ) -> std::io::Result<(Self, TerminalState, TerminalRuntime)> { - Self::new_with_tab( - initial_cwd, - rows, - cols, - scrollback_limit_bytes, - host_terminal_theme, - host_terminal_appearance, - crate::pane::PaneShellConfig::new("", crate::config::ShellModeConfig::NonLogin), - events, - render_notify, - render_dirty, - Some(argv), - extra_env, - ) - } - #[allow(clippy::too_many_arguments)] fn new_with_tab( initial_cwd: PathBuf, @@ -671,43 +622,6 @@ impl Workspace { self.close_tab(self.active_tab) } - #[cfg(test)] - pub fn split_focused( - &mut self, - direction: Direction, - rows: u16, - cols: u16, - cwd: Option, - scrollback_limit_bytes: usize, - host_terminal_theme: crate::terminal_theme::TerminalTheme, - host_terminal_appearance: Option, - shell_config: crate::pane::PaneShellConfig<'_>, - extra_env: Vec<(String, String)>, - ) -> std::io::Result { - let pane_number = self.next_public_pane_number; - let tab_number = self - .active_tab() - .map(|tab| tab.number) - .expect("workspace must always have at least one tab"); - let launch_env = self.launch_env_for_new_pane(tab_number, pane_number, extra_env); - let new_pane = self - .active_tab_mut() - .expect("workspace must always have at least one tab") - .split_focused( - direction, - rows, - cols, - cwd, - scrollback_limit_bytes, - host_terminal_theme, - host_terminal_appearance, - shell_config, - &launch_env, - )?; - self.register_new_pane_with_number(new_pane.pane_id, pane_number); - Ok(new_pane) - } - #[allow(clippy::too_many_arguments)] pub fn split_focused_command( &mut self, @@ -1174,14 +1088,6 @@ impl Workspace { self.worktree_space.as_ref() } - #[cfg(test)] - pub fn refresh_git_ahead_behind(&mut self) { - let cwd = self.resolved_identity_cwd(); - self.cached_git_branch = cwd.as_deref().and_then(git_branch); - self.cached_git_ahead_behind = cwd.as_deref().and_then(git_ahead_behind); - self.cached_git_space = cwd.as_deref().and_then(git_space_metadata); - } - pub fn find_tab_index_for_pane(&self, pane_id: PaneId) -> Option { self.tabs .iter() @@ -1663,50 +1569,6 @@ mod tests { assert!(!target.tabs[0].panes.contains_key(&source_pane)); } - #[tokio::test] - async fn new_workspace_retains_discovered_git_metadata() { - let stamp = std::time::SystemTime::now() - .duration_since(std::time::UNIX_EPOCH) - .expect("clock should be after unix epoch") - .as_nanos(); - let root = std::env::temp_dir().join(format!( - "herdr-workspace-git-metadata-{}-{stamp}", - std::process::id() - )); - std::fs::create_dir_all(root.join(".git")).expect("create git directory"); - std::fs::write(root.join(".git/HEAD"), "ref: refs/heads/main\n").expect("write git head"); - #[cfg(windows)] - let command = "C:\\Windows\\System32\\whoami.exe"; - #[cfg(not(windows))] - let command = "/usr/bin/true"; - let argv = vec![command.to_string()]; - let (events, _) = mpsc::channel(64); - let render_notify = Arc::new(Notify::new()); - let render_dirty = Arc::new(RenderSignal::new()); - - let (workspace, _terminal, runtime) = Workspace::new_argv_command( - root.clone(), - 24, - 80, - &argv, - 1024, - crate::terminal_theme::TerminalTheme::default(), - None, - events, - render_notify, - render_dirty, - ) - .expect("create workspace"); - - let space = workspace - .git_space() - .expect("workspace should retain discovered git metadata"); - assert_eq!(space.repo_root, root); - - runtime.shutdown(); - std::fs::remove_dir_all(root).expect("remove test repo"); - } - #[test] fn linked_worktree_auto_label_uses_checkout_name_not_repo_name() { let (base, repo, checkout) = -- cgit v1.2.3