T40: sessions post system notifications through gpui's: each terminal posts its own in the order its output asks, when its row turns to waiting, done or error, when a command ends, and when a program asks through OSC 9, 777 or 99, only while its window is inactive or it is hidden, one every 3 seconds, titled with the session's name; a press goes to the window whose session posted it and shows it; OSC 99 hears untracked closes, alive lists what lives, in order before a later DA1, a close and w take one back where gpui can, a closed session's too, and the query says what holds where
CI / platforms (windows-latest) (push) Successful in 1m51s
CI / platforms (ubuntu-24.04) (push) Successful in 2m2s
CI / macos (push) Successful in 12m36s

This commit is contained in:
ZacharyZhang-NY committed 2026-10-17 15:36:28 -04:00
1 parent 53b8978dff
commit 31de5e44ef
18 files changed
+512 -122

No files matched your search

+23 -17
View File
@@ -121,7 +121,7 @@ fn mark(terminal: &Entity<TerminalView>, cx: &App) -> Option<AnyElement> {
let (pid, records) = (view.pid() as usize, view.records());
let icon = |name: IconName, color: Hsla| Icon::new(name).color(color).into_any_element();
let failed = view.ended().filter(|&code| code != 0);
let mark = match (records.shown(), failed) {
let mark = match (records.shown().map(|(_, record)| record), failed) {
(
Some(Record {
state: State::Blocked,
@@ -215,23 +215,13 @@ fn mark(terminal: &Entity<TerminalView>, cx: &App) -> Option<AnyElement> {
)
}
/// What hover shows: a line for each record, ten at most, with its state, app, title and message, then a failed command's status.
/// What hover shows: a line for each record, ten at most, then a failed command's status.
pub fn hover(records: &Records, failed: Option<i32>) -> Vec<SharedString> {
let line = |(id, record): (&str, &Record)| {
let parts = [
records.app(id),
record.title.as_deref(),
record.msg.as_deref(),
];
let said: Vec<String> = parts.into_iter().flatten().map(plain).collect();
let state = label(record);
SharedString::from(if said.is_empty() {
state
} else {
format!("{state}: {}", said.join(" · "))
})
};
let mut lines: Vec<SharedString> = records.iter().take(LISTED).map(line).collect();
let mut lines: Vec<SharedString> = records
.iter()
.take(LISTED)
.map(|(id, record)| line(records, id, record).into())
.collect();
let more = records.iter().count().saturating_sub(LISTED);
if more > 0 {
lines.push(format!("{more} more").into());
@@ -242,6 +232,22 @@ pub fn hover(records: &Records, failed: Option<i32>) -> Vec<SharedString> {
lines
}
/// A record as hover and notifications say it: its state, then its app, title and message.
pub fn line(records: &Records, id: &str, record: &Record) -> String {
let parts = [
records.app(id),
record.title.as_deref(),
record.msg.as_deref(),
];
let said: Vec<String> = parts.into_iter().flatten().map(plain).collect();
let state = label(record);
if said.is_empty() {
state
} else {
format!("{state}: {}", said.join(" · "))
}
}
/// A record's state, as hover names it.
fn label(record: &Record) -> String {
match (record.state, record.kind, record.progress) {