From 2006b4169bd33eb6fb6560883790c72a14df89de Mon Sep 17 00:00:00 2001 From: slyxyllt Date: Mon, 31 Aug 2026 23:06:11 +0800 Subject: [PATCH] =?UTF-8?q?fix(tui):=20Terminal.app=20=E4=B8=8D=E5=86=8D?= =?UTF-8?q?=E8=AF=AF=E5=BC=80=20synchronized=20output?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit macOS Terminal.app(TERM_PROGRAM=Apple_Terminal)未实现 DEC mode 2026, 此前却列在探测白名单中,auto 模式会误判为支持并发送 CSI ?2026h/l。 旧版 Terminal.app 直接忽略该序列,特性静默无效。 现将其从白名单移除,auto 下默认关闭;确需该行为的终端可用 tui.sync_output_mode="always" 强制开启。 - src/utils/terminal_capability.cpp: 白名单移除 Apple_Terminal - src/utils/terminal_capability.hpp: 同步注释说明 - tests/utils/terminal_capability_test.cpp: 新增 AppleTerminalOff 用例 - openspec design.md: 同步探测规则描述 验证:单独编译 terminal_capability 相关两个单元链接 gtest,26/26 通过。 --- openspec/changes/add-synchronized-output/design.md | 2 +- src/utils/terminal_capability.cpp | 1 - src/utils/terminal_capability.hpp | 4 +++- tests/utils/terminal_capability_test.cpp | 14 ++++++++++++-- 4 files changed, 16 insertions(+), 5 deletions(-) diff --git a/openspec/changes/add-synchronized-output/design.md b/openspec/changes/add-synchronized-output/design.md index f77aeae9f..a58a1e2f4 100644 --- a/openspec/changes/add-synchronized-output/design.md +++ b/openspec/changes/add-synchronized-output/design.md @@ -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. diff --git a/src/utils/terminal_capability.cpp b/src/utils/terminal_capability.cpp index 823af6075..b55aec97b 100644 --- a/src/utils/terminal_capability.cpp +++ b/src/utils/terminal_capability.cpp @@ -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", diff --git a/src/utils/terminal_capability.hpp b/src/utils/terminal_capability.hpp index 54ed47807..e13dc1def 100644 --- a/src/utils/terminal_capability.hpp +++ b/src/utils/terminal_capability.hpp @@ -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 // diff --git a/tests/utils/terminal_capability_test.cpp b/tests/utils/terminal_capability_test.cpp index 212cc3acb..e222dbbb7 100644 --- a/tests/utils/terminal_capability_test.cpp +++ b/tests/utils/terminal_capability_test.cpp @@ -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( @@ -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",