diff options
| author | Ogulcan Celik <ogulcancelik@gmail.com> | 2026-07-13 03:26:03 +0300 |
|---|---|---|
| committer | Ogulcan Celik <ogulcancelik@gmail.com> | 2026-07-13 03:30:13 +0300 |
| commit | 5cfe5e5e730246948b0c9428ad876b6938590aad (patch) | |
| tree | 53d4bbe1aff6887f63f279eed1b5e4e11e7331b7 /src/pane | |
| parent | 7f3364a468f7dcfd8c9c84e5737fe5699f0e3a93 (diff) | |
feat: add configurable sidebar metadata tokens
Diffstat (limited to 'src/pane')
| -rw-r--r-- | src/pane/osc.rs | 46 | ||||
| -rw-r--r-- | src/pane/terminal.rs | 18 |
2 files changed, 59 insertions, 5 deletions
diff --git a/src/pane/osc.rs b/src/pane/osc.rs index 92a743c4..838515e0 100644 --- a/src/pane/osc.rs +++ b/src/pane/osc.rs @@ -460,6 +460,7 @@ pub(super) struct AgentOscStateTracker { state: Osc52ForwarderState, body: Vec<u8>, latest_title: Option<String>, + terminal_title: Option<String>, latest_progress: Option<String>, } @@ -513,12 +514,14 @@ impl AgentOscStateTracker { if let Some((command, payload)) = parse_agent_osc_body(&self.body) { match command { b"0" | b"2" => { - if payload.is_empty() { - self.latest_title = None; + let title = if payload.is_empty() { + None } else { - self.latest_title = - Some(sanitize_agent_osc_string(payload, AGENT_OSC_MAX_CHARS)); - } + let title = sanitize_agent_osc_string(payload, AGENT_OSC_MAX_CHARS); + (!title.is_empty()).then_some(title) + }; + self.latest_title.clone_from(&title); + self.terminal_title = title; } b"9" => { self.latest_progress = @@ -530,6 +533,15 @@ impl AgentOscStateTracker { self.body.clear(); } + pub(super) fn terminal_title(&self) -> Option<&str> { + self.terminal_title.as_deref() + } + + #[cfg(unix)] + pub(super) fn seed_terminal_title(&mut self, title: Option<String>) { + self.terminal_title = title; + } + /// Returns the latest retained OSC title, or `""` if none has been seen or /// the last title was an empty clear. #[allow(dead_code)] // used by terminal.rs; full call chain wired in Stage C @@ -1081,6 +1093,7 @@ mod tests { let mut t = AgentOscStateTracker::default(); t.observe("hello\x1b]0;braille title\x07world".as_bytes()); assert_eq!(t.latest_title(), "braille title"); + assert_eq!(t.terminal_title(), Some("braille title")); assert_eq!(t.latest_progress(), ""); } @@ -1101,6 +1114,29 @@ mod tests { // Then clear it with an empty payload (Codex pattern). t.observe(b"\x1b]0;\x07"); assert_eq!(t.latest_title(), ""); + assert_eq!(t.terminal_title(), None); + } + + #[test] + fn clearing_agent_evidence_preserves_the_terminal_title() { + let mut tracker = AgentOscStateTracker::default(); + tracker.observe("\x1b]2;✳ 修复🙂标题\x1b\\".as_bytes()); + + tracker.clear_retained(); + + assert_eq!(tracker.latest_title(), ""); + assert_eq!(tracker.terminal_title(), Some("✳ 修复🙂标题")); + } + + #[cfg(unix)] + #[test] + fn handoff_seed_does_not_restore_agent_detection_evidence() { + let mut tracker = AgentOscStateTracker::default(); + + tracker.seed_terminal_title(Some("✳ restored title".into())); + + assert_eq!(tracker.terminal_title(), Some("✳ restored title")); + assert_eq!(tracker.latest_title(), ""); } #[test] diff --git a/src/pane/terminal.rs b/src/pane/terminal.rs index 5c7dfcb9..06cdea35 100644 --- a/src/pane/terminal.rs +++ b/src/pane/terminal.rs @@ -445,6 +445,10 @@ impl PaneTerminal { .maybe_restore_host_terminal_theme(pane_id, shell_pid) } + pub fn terminal_title(&self) -> Option<String> { + self.ghostty.terminal_title() + } + #[allow(dead_code)] // exposed for Stage C (detection loop wiring) pub fn agent_osc_title(&self) -> String { self.ghostty.agent_osc_title() @@ -985,6 +989,20 @@ impl GhosttyPaneTerminal { ) } + pub fn terminal_title(&self) -> Option<String> { + self.core + .lock() + .ok() + .and_then(|core| core.agent_osc_state.terminal_title().map(str::to_string)) + } + + #[cfg(unix)] + pub fn seed_terminal_title(&self, title: Option<String>) { + if let Ok(mut core) = self.core.lock() { + core.agent_osc_state.seed_terminal_title(title); + } + } + /// Returns the latest OSC 0/2 title retained for agent detection, or `""` /// if no title has been seen or the last update was an empty clear. #[allow(dead_code)] // exposed for Stage C (detection loop wiring) |
