Skip to content

feat(ink): auto compat mode for legacy pre-ConPTY Windows consoles#1299

Merged
claude-code-best merged 4 commits into
claude-code-best:mainfrom
Jade-beads:feat/legacy-console-compat
Jul 18, 2026
Merged

feat(ink): auto compat mode for legacy pre-ConPTY Windows consoles#1299
claude-code-best merged 4 commits into
claude-code-best:mainfrom
Jade-beads:feat/legacy-console-compat

Conversation

@Jade-beads

@Jade-beads Jade-beads commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

Summary

Windows 10 build < 17763(1809 之前,无 ConPTY——1709、各类 LTSC/LTSB 内网机器)上,老 conhost 对 pending-wrap 的处理是"写满最后一列立即换行"(现代终端为挂起等待下一字符)。任何顶满行宽的输出(分隔线、满行文本)都会让真实光标比渲染器认为的位置多走一行——增量 diff 持续漂移产生花屏,全量重绘在重画过程中同样触发该缺陷、且每次多滚几行使旧残渣上移。这类机器上所有终端都受影响:VS Code 集成终端和 mintty 走 winpty,抓的是同一份损坏的 conhost buffer。

本 PR 增加「老控制台自动兼容模式」,根治手段是渲染宽度收窄 1 列:任何一行永远不写到最后一列,缺陷路径从源头绕开;周期性全量重绘保留作为兜底自愈。非老控制台环境完全不走此分支,零影响。

Changes

  • 新增 packages/@ant/ink/src/core/legacyConsole.ts
    • os.release() build < 17763 时自动判定为老控制台(进程内缓存)
    • effectiveColumns():legacy 模式下渲染宽度收窄 1 列(下限 20)——根治花屏
    • 强度旋钮:CLAUDE_CODE_LEGACY_CONSOLE=0/1/2|always(关 / 周期重绘 / 每帧全量重绘)、CLAUDE_CODE_LEGACY_CONSOLE_RESET_MS(周期间隔,钳制 100–10000ms,默认 1000)
  • packages/@ant/ink/src/core/ink.tsx
    • 三处终端宽度读取统一改走 effectiveColumns()
  • packages/@ant/ink/src/core/log-update.ts
    • 渲染循环命中老控制台时按模式/间隔把增量重绘替换为全量重置(复用现有 fullResetSequence_CAUSES_FLICKER 路径),作为兜底自愈

排查历程(真机)

问题在一台 Windows 10 1709(build 16299)内网机器上定位与验证,三轮迭代:

  1. 初版假设"增量 diff 漂移、全量重绘正确"(依据:切主题短暂消除花屏),实现周期性全量重绘;
  2. 真机反馈花屏范围随重绘扩大——推翻假设:全量重绘的重画过程同样触发 pending-wrap,绝对清屏后帧内满宽行仍逐行累积漂移;
  3. 定位到 pending-wrap 立即换行为真根因,改为宽度收窄 1 列。真机验证通过:花屏消除,无明显闪烁。

Testing

  • 17 个单元测试:build 号判定边界、模式解析(0/1/2/always/未知值回退)、间隔钳制与空串回退、effectiveColumns 收窄与下限、检测结果缓存行为
  • macOS 伪终端(100 列)强制 legacy 模式实跑 TUI:最大可见行宽 98(≤ 99 预算)、渲染正常
  • Windows 10 1709(build 16299)真机验证通过:cmd 花屏消除
  • tsc / biome 零错误;非 Windows 及 build ≥ 17763 环境不进入该分支,行为不变

Windows 10 build < 17763(1709/LTSC 等无 ConPTY 的内网机器)上,
老 conhost 的 VT 解析器对增量渲染的行尾 pending-wrap 语义处理有误,
真实光标与虚拟光标漂移,花屏残渣持续累积,直到一次全量重绘才消除。
该问题影响此类机器上的所有终端(VS Code/mintty 走 winpty 抓取的
是同一份损坏的 conhost buffer)。

- 新增 legacyConsole.ts:按 os.release() build 号自动检测,
  CLAUDE_CODE_LEGACY_CONSOLE=1/0 可强制开/关
- log-update.ts:命中时每约 1 秒用一次全量重绘替换增量 diff,
  屏幕持续自愈;非老控制台环境完全不走此路径
- 6 个单元测试(检测逻辑 + 环境变量覆盖 + 缓存行为)
@coderabbitai

coderabbitai Bot commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Adds cached legacy Windows console detection, environment-controlled modes and reset intervals, legacy-aware terminal sizing, and periodic or always-on full resets in Ink rendering. Tests cover parsing, clamping, caching, and width behavior; architecture documentation describes the configuration.

Changes

Legacy console compatibility

Layer / File(s) Summary
Legacy console detection and validation
packages/@ant/ink/src/core/legacyConsole.ts, packages/@ant/ink/src/core/__tests__/legacyConsole.test.ts
Adds mode and reset-interval parsing, Windows build detection, cached runtime values, width adjustment, cache reset support, and tests for overrides, clamping, and caching.
Legacy-aware terminal sizing
packages/@ant/ink/src/core/ink.tsx
Uses effectiveColumns for constructor, resize, and render width calculations.
Configurable legacy-console rendering reset
packages/@ant/ink/src/core/log-update.ts, CLAUDE.md
Adds always or interval-based full-clear rendering for legacy consoles and documents the version source, detection path, reset cadence, and environment controls.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Ink
  participant effectiveColumns
  participant LogUpdate.render
  participant fullResetSequence_CAUSES_FLICKER
  Ink->>effectiveColumns: calculate legacy-aware terminal width
  LogUpdate.render->>fullResetSequence_CAUSES_FLICKER: generate clear reset when mode and cadence allow
  fullResetSequence_CAUSES_FLICKER-->>LogUpdate.render: return full reset sequence
Loading

Suggested reviewers: claude-code-best

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: automatic compatibility mode for legacy pre-ConPTY Windows consoles.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

真机(1709/build 16299)第一轮验证反馈周期性自愈可能不足,
增加两个调节旋钮:

- CLAUDE_CODE_LEGACY_CONSOLE=2(或 always):每帧全量重绘,
  用于增量 diff 立即花屏、1 秒自愈不够的机器
- CLAUDE_CODE_LEGACY_CONSOLE_RESET_MS:周期模式的重绘间隔,
  钳制在 [100, 10000],默认 1000 不变

默认行为不变(自动检测 → periodic 1s)。新增 9 个单元测试
(模式解析、间隔钳制、空串回退、缓存行为)。
真机(1709/build 16299)第二轮反馈:周期性全量重绘在跑(可见闪烁)
但花屏范围随重绘扩大。定位到真正根因:老 conhost 对 pending-wrap
的处理是「写满最后一列立即换行」(现代终端为挂起等待),任何顶满
行宽的输出(分隔线、满行文本)都会把真实光标多推一行——增量 diff
和全量重绘同样受害,且每次重绘还把缓冲区多滚几行,旧帧残渣上移,
表现为「越闪越花」。

修复:legacy 模式下 effectiveColumns() 把渲染宽度收窄 1 列
(下限 20),任何一行都不再触碰最后一列,pending-wrap 缺陷路径
从源头绕开;周期重绘保留作为兜底自愈。非 legacy 环境行为完全不变。

新增 effectiveColumns 单元测试;ink.tsx 三处宽度读取统一走该函数。

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/`@ant/ink/src/core/ink.tsx:
- Line 96: Update the effectiveColumns import in ink.tsx to use the configured
src/* alias and explicit .ts extension instead of the relative .js specifier.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 36240f42-a599-4600-9f39-5fb99cd2fd57

📥 Commits

Reviewing files that changed from the base of the PR and between 492063f and 515cf9a.

📒 Files selected for processing (3)
  • packages/@ant/ink/src/core/__tests__/legacyConsole.test.ts
  • packages/@ant/ink/src/core/ink.tsx
  • packages/@ant/ink/src/core/legacyConsole.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/@ant/ink/src/core/legacyConsole.ts
  • packages/@ant/ink/src/core/tests/legacyConsole.test.ts

wrapForMultiplexer,
} from './termio/osc.js';
import { TerminalWriteProvider } from '../hooks/useTerminalNotification.js';
import { effectiveColumns } from './legacyConsole.js';

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Use the configured src/* alias and .ts extension.

This import uses a relative .js specifier, but the repository guidelines require TypeScript source imports under src to use the src/* alias and .ts extensions.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/`@ant/ink/src/core/ink.tsx at line 96, Update the effectiveColumns
import in ink.tsx to use the configured src/* alias and explicit .ts extension
instead of the relative .js specifier.

Source: Coding guidelines

@Jade-beads

Copy link
Copy Markdown
Contributor Author

真机验证结果(Windows 10 1709 / build 16299,内网机器,cmd 窗口):宽度收窄修复(515cf9a5)验证通过,花屏消除、无明显闪烁

排查中的关键反证记录:仅靠周期性全量重绘(初版方案)在该机器上花屏范围会随重绘扩大——原因是重画整帧的过程中,满宽行同样触发老 conhost 的 pending-wrap 立即换行缺陷,每次重绘还会把缓冲区多滚几行、旧残渣上移。宽度收窄 1 列后任何行都不触碰最后一列,增量与全量路径同时被根治;周期重绘保留为兜底。

@claude-code-best
claude-code-best merged commit feb76f1 into claude-code-best:main Jul 18, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants