diff options
| author | Can Celik <ogulcancelik@gmail.com> | 2026-04-21 16:30:04 +0300 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-04-21 16:30:04 +0300 |
| commit | 3397e1ce2922c4ae9b814e8110e81de7d201de81 (patch) | |
| tree | c457a7cf0af271dea1a412474fad5a33c1b60be8 /src/app/state.rs | |
| parent | 2818ff492d45d17fd4f9b15478d703c6c664a8c0 (diff) | |
feat: add persistent server-client sessions
Make herdr persistent by default.
Launching herdr now starts or reattaches to a background session
server. Clients can detach and reattach while panes and agent
processes keep running. Session mode now supports multi-client attach,
auto-detect startup, and a thin-client/headless-server split.
This also refactors the large app, ui, input, pane, config,
workspace, and persistence modules into smaller focused submodules,
while preserving behavior and colocating tests with the code they
exercise.
Upgrade notes:
- persistence mode is now the default
- in-app quit detaches the current client instead of stopping the server
- use `herdr server stop` to stop the background session
- use `--no-session` for the old single-process behavior
- default socket paths now live under the config directory
Diffstat (limited to 'src/app/state.rs')
| -rw-r--r-- | src/app/state.rs | 22 |
1 files changed, 19 insertions, 3 deletions
diff --git a/src/app/state.rs b/src/app/state.rs index b1e69098..67f89606 100644 --- a/src/app/state.rs +++ b/src/app/state.rs @@ -586,9 +586,17 @@ pub struct AppState { pub selected: usize, pub mode: Mode, pub should_quit: bool, + /// In persistence mode, client quit actions detach instead of stopping the server. + pub quit_detaches: bool, + /// Set when the current client should detach from the persistent session. + /// The server's event loop checks this and handles client detach. + pub detach_requested: bool, pub request_new_workspace: bool, pub request_new_tab: bool, pub request_reload_keybinds: bool, + /// Set when UI interaction requested a clipboard write that must be + /// handled by the outer App/event loop instead of directly from AppState. + pub request_clipboard_write: Option<Vec<u8>>, pub creating_new_tab: bool, pub requested_new_tab_name: Option<String>, pub request_complete_onboarding: bool, @@ -657,6 +665,10 @@ impl AppState { self.sound.enabled } + pub fn is_prefix(&self, key: &crossterm::event::KeyEvent) -> bool { + key_matches(key, self.prefix_code, self.prefix_mods) + } + pub fn estimate_pane_size(&self) -> (u16, u16) { if let Some(info) = self.view.pane_infos.first() { (info.rect.height, info.rect.width) @@ -666,7 +678,6 @@ impl AppState { } } -#[cfg(test)] pub fn key_matches( key: &crossterm::event::KeyEvent, expected_code: KeyCode, @@ -700,9 +711,12 @@ impl AppState { selected: 0, mode: Mode::Navigate, should_quit: false, + quit_detaches: false, + detach_requested: false, request_new_workspace: false, request_new_tab: false, request_reload_keybinds: false, + request_clipboard_write: None, creating_new_tab: false, requested_new_tab_name: None, request_complete_onboarding: false, @@ -759,8 +773,10 @@ impl AppState { new_workspace_label: "n".into(), rename_workspace: (KeyCode::Char('n'), KeyModifiers::SHIFT), rename_workspace_label: "shift+n".into(), - close_workspace: (KeyCode::Char('d'), KeyModifiers::empty()), - close_workspace_label: "d".into(), + close_workspace: (KeyCode::Char('d'), KeyModifiers::SHIFT), + close_workspace_label: "shift+d".into(), + detach: None, + detach_label: None, previous_workspace: None, previous_workspace_label: None, next_workspace: None, |
