aboutsummaryrefslogtreecommitdiff
path: root/src/pane
diff options
context:
space:
mode:
authorOgulcan Celik <ogulcancelik@gmail.com>2026-07-13 03:26:03 +0300
committerOgulcan Celik <ogulcancelik@gmail.com>2026-07-13 03:30:13 +0300
commit5cfe5e5e730246948b0c9428ad876b6938590aad (patch)
tree53d4bbe1aff6887f63f279eed1b5e4e11e7331b7 /src/pane
parent7f3364a468f7dcfd8c9c84e5737fe5699f0e3a93 (diff)
feat: add configurable sidebar metadata tokens
Diffstat (limited to 'src/pane')
-rw-r--r--src/pane/osc.rs46
-rw-r--r--src/pane/terminal.rs18
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)