Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion openspec/changes/add-synchronized-output/design.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
New pure helper `detect_synchronized_output_support_with(caps, env_lookup)` in `terminal_capability`:

- **Blacklist (off, highest priority):** `ConEmuPID` set (ConEmu/Cmder), `caps.is_legacy_conhost` or `caps.is_classic_conhost`, `TERM` starting with `tmux` or `screen` (multiplexer version unknowable from env; force-able via config).
- **Whitelist (on):** `WT_SESSION` set (Windows Terminal), `KITTY_WINDOW_ID` set or `TERM == "xterm-kitty"` (kitty), `TERM_PROGRAM` in `iTerm.app` / `WezTerm` / `ghostty` / `vscode` / `Apple_Terminal` / `WarpTerminal` / `contour` / `mintty`, `TERM` starting with `foot` or `ghostty`.
- **Whitelist (on):** `WT_SESSION` set (Windows Terminal), `KITTY_WINDOW_ID` set or `TERM == "xterm-kitty"` (kitty), `TERM_PROGRAM` in `iTerm.app` / `WezTerm` / `ghostty` / `vscode` / `WarpTerminal` / `contour` / `mintty` (Apple_Terminal / macOS Terminal.app excluded: older versions don't implement DEC 2026; opt in via `tui.sync_output_mode="always"`), `TERM` starting with `foot` or `ghostty`.
- **Unknown (off):** anything else, including POSIX terminals with no recognizable markers (e.g. Alacritty, plain `xterm-256color`).

On non-Windows builds the Windows-only signals simply never fire and the POSIX rules apply. Terminals that ignore unknown DEC modes harmlessly would tolerate unconditional sending, but the conservative default protects the legacy-Windows moat and surprises no one; `sync_output_mode: "always"` overrides for adventurous users.
Expand Down
1 change: 0 additions & 1 deletion src/utils/terminal_capability.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,6 @@ bool term_program_whitelisted(const std::string& value) {
"WezTerm",
"ghostty",
"vscode", // VS Code 内嵌终端(xterm.js)
"Apple_Terminal", // macOS Terminal.app
"WarpTerminal",
"contour",
"mintty",
Expand Down
4 changes: 3 additions & 1 deletion src/utils/terminal_capability.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,9 @@ inline bool should_use_conhost_compat_layout(const TerminalCapabilities& caps) {
// - TERM 以 "tmux" / "screen" 开头(复用器) → false
// - WT_SESSION / KITTY_WINDOW_ID 存在 → true
// - TERM_PROGRAM ∈ {iTerm.app, WezTerm, ghostty, vscode,
// Apple_Terminal, WarpTerminal, contour, mintty} → true
// WarpTerminal, contour, mintty} → true
// (Apple_Terminal / macOS Terminal.app 不在此列:旧版本不支持 DEC 2026,
// 保守关闭;需用时用 tui.sync_output_mode="always" 强制开启)
// - TERM == xterm-kitty 或以 foot/ghostty 开头 → true
// - 其它(未知终端,如 Alacritty / 裸 xterm-256color) → false
//
Expand Down
14 changes: 12 additions & 2 deletions tests/utils/terminal_capability_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -255,10 +255,10 @@ TEST(SynchronizedOutputSupport, KittyTermOn) {
}

// 场景:TERM_PROGRAM 白名单(iTerm.app / WezTerm / ghostty / vscode /
// Apple_Terminal / WarpTerminal / contour / mintty)→ 开启
// WarpTerminal / contour / mintty)→ 开启(Apple_Terminal 已移除,见下)
TEST(SynchronizedOutputSupport, TermProgramWhitelistOn) {
for (const char* name : {"iTerm.app", "WezTerm", "ghostty", "vscode",
"Apple_Terminal", "WarpTerminal", "contour",
"WarpTerminal", "contour",
"mintty"}) {
TerminalCapabilities caps;
EXPECT_TRUE(detect_synchronized_output_support_with(
Expand All @@ -268,6 +268,16 @@ TEST(SynchronizedOutputSupport, TermProgramWhitelistOn) {
}
}

// 场景:Apple Terminal.app 不支持 DEC mode 2026(尤其 macOS 12 及更早版本),
// 从白名单移除,auto 模式默认关闭;需要时用 tui.sync_output_mode="always" 强制开启。
TEST(SynchronizedOutputSupport, AppleTerminalOff) {
TerminalCapabilities caps;
EXPECT_FALSE(detect_synchronized_output_support_with(
caps, make_sync_env_lookup(std::nullopt, std::nullopt, std::nullopt,
std::string("Apple_Terminal"), std::nullopt)))
<< "Apple_Terminal must NOT be whitelisted for DEC 2026";
}

// 场景:TERM 前缀白名单(foot / ghostty)→ 开启
TEST(SynchronizedOutputSupport, TermPrefixWhitelistOn) {
for (const char* term : {"foot", "foot-256color", "ghostty",
Expand Down
Loading