Recognize Rex terminals and enable Kitty graphics for pets (#58270)

## What changed

- Detect Rex from `TERM_PROGRAM=rex` or `TERM=xterm-rex` and report its name in terminal diagnostics.
- Enable Kitty graphics for pets in Rex.
- Use the detected terminal identity for Rex's label-only web links, extending that behavior to detection through `TERM`.

## Testing

Add Rex detection coverage for both environment variables and include Rex in the web-link rendering and Kitty graphics support tests.

GitOrigin-RevId: b237e8a6b3af50a55080573dc1e0badc2c139b42
This commit is contained in:
Eric Traut
2026-10-30 07:11:56 +00:00
committed by copyberry
parent 5e769a1cda
commit 788b9d7bf6
9 changed files with 26 additions and 23 deletions
+1
View File
@@ -1892,6 +1892,7 @@ fn terminal_name(info: &TerminalInfo) -> &'static str {
match info.name {
TerminalName::AppleTerminal => "Apple Terminal",
TerminalName::Ghostty => "Ghostty",
TerminalName::Rex => "Rex",
TerminalName::Iterm2 => "iTerm2",
TerminalName::WarpTerminal => "Warp",
TerminalName::VsCode => "VS Code",
+5
View File
@@ -40,6 +40,8 @@ pub enum TerminalName {
AppleTerminal,
/// Ghostty terminal emulator.
Ghostty,
/// Rex terminal emulator.
Rex,
/// iTerm2 terminal emulator.
Iterm2,
/// Warp terminal emulator.
@@ -138,6 +140,7 @@ impl TerminalInfo {
let name = match term.as_str() {
"dumb" => TerminalName::Dumb,
"xterm-ghostty" => TerminalName::Ghostty,
"xterm-rex" => TerminalName::Rex,
"wezterm" | "wezterm-mux" => TerminalName::WezTerm,
_ => TerminalName::Unknown,
};
@@ -176,6 +179,7 @@ impl TerminalInfo {
format_terminal_version("Apple_Terminal", &self.version)
}
TerminalName::Ghostty => format_terminal_version("Ghostty", &self.version),
TerminalName::Rex => format_terminal_version("rex", &self.version),
TerminalName::Iterm2 => format_terminal_version("iTerm.app", &self.version),
TerminalName::WarpTerminal => {
format_terminal_version("WarpTerminal", &self.version)
@@ -416,6 +420,7 @@ fn terminal_name_from_term_program(value: &str) -> Option<TerminalName> {
match normalized.as_str() {
"appleterminal" => Some(TerminalName::AppleTerminal),
"ghostty" => Some(TerminalName::Ghostty),
"rex" => Some(TerminalName::Rex),
"iterm" | "iterm2" | "itermapp" => Some(TerminalName::Iterm2),
"warp" | "warpterminal" => Some(TerminalName::WarpTerminal),
"vscode" => Some(TerminalName::VsCode),
@@ -218,6 +218,14 @@ fn detects_ghostty() {
);
}
#[test]
fn detects_rex() {
for (key, value) in [("TERM_PROGRAM", "rex"), ("TERM", "xterm-rex")] {
let env = FakeEnvironment::new().with_var(key, value);
assert_eq!(detect_terminal_info_from_env(&env).name, TerminalName::Rex);
}
}
#[test]
fn detects_vscode() {
let env = FakeEnvironment::new()
+1
View File
@@ -251,6 +251,7 @@ impl LocalSettings {
| TerminalName::Iterm2
| TerminalName::WarpTerminal
| TerminalName::WezTerm
| TerminalName::Rex
| TerminalName::Alacritty
| TerminalName::Konsole
| TerminalName::GnomeTerminal
@@ -69,17 +69,11 @@ impl WebLinkDisplay {
{
return Self::WithDestination;
}
if terminal
.term_program
.as_deref()
.is_some_and(|program| program.eq_ignore_ascii_case("rex"))
{
return Self::LabelOnly;
}
match terminal.name {
TerminalName::Ghostty
| TerminalName::Iterm2
| TerminalName::WezTerm
| TerminalName::Rex
| TerminalName::Kitty
| TerminalName::VsCode
| TerminalName::Alacritty
@@ -91,6 +91,7 @@ fn supporting_terminals_render_only_the_styled_label_and_keep_its_target() {
TerminalName::Ghostty,
TerminalName::Iterm2,
TerminalName::WezTerm,
TerminalName::Rex,
TerminalName::Kitty,
TerminalName::VsCode,
TerminalName::Alacritty,
@@ -143,21 +144,6 @@ fn supporting_terminals_render_only_the_styled_label_and_keep_its_target() {
}
}
#[test]
fn rex_renders_only_the_label() {
let mut rex = terminal(TerminalName::Unknown);
rex.term_program = Some("rex".to_string());
let markdown = "[label](https://example.com)";
assert_eq!(
render(
markdown,
/*width*/ 80,
WebLinkDisplay::for_terminal(&rex, /*term*/ None),
),
render(markdown, /*width*/ 80, WebLinkDisplay::LabelOnly),
);
}
#[test]
fn unknown_terminals_and_multiplexers_keep_visible_destinations() {
let mut tmux = terminal(TerminalName::Ghostty);
+7 -1
View File
@@ -178,7 +178,7 @@ fn is_iterm2_terminal(info: &TerminalInfo) -> bool {
fn supports_kitty_graphics(info: &TerminalInfo) -> bool {
matches!(
info.name,
TerminalName::Ghostty | TerminalName::Kitty | TerminalName::WezTerm
TerminalName::Ghostty | TerminalName::Kitty | TerminalName::Rex | TerminalName::WezTerm
) || terminal_field_contains(info.term.as_deref(), "kitty")
|| terminal_field_contains(info.term.as_deref(), "ghostty")
|| terminal_field_contains(info.term.as_deref(), "wezterm")
@@ -516,6 +516,12 @@ mod tests {
Some("kitty"),
/*term*/ None,
),
terminal_info_for_test(
TerminalName::Rex,
/*multiplexer*/ None,
Some("rex"),
/*term*/ None,
),
terminal_info_for_test(
TerminalName::WezTerm,
/*multiplexer*/ None,
+1
View File
@@ -64,6 +64,7 @@ fn auto_resize_reflow_max_rows(
TerminalName::Alacritty => ALACRITTY_RESIZE_REFLOW_MAX_ROWS,
TerminalName::AppleTerminal
| TerminalName::Ghostty
| TerminalName::Rex
| TerminalName::Iterm2
| TerminalName::WarpTerminal
| TerminalName::Kitty
@@ -775,6 +775,7 @@ pub(super) async fn run_main_inner(
let terminal_name = match terminal_info.name {
TerminalName::AppleTerminal => "apple_terminal",
TerminalName::Ghostty => "ghostty",
TerminalName::Rex => "rex",
TerminalName::Iterm2 => "iterm2",
TerminalName::WarpTerminal => "warp",
TerminalName::VsCode => "vscode",