Skip to content

fix(input): restore IME cursor positioning reverted in #4779 - #4993

Merged
wenshao merged 3 commits into
QwenLM:mainfrom
BenGuanRan:fix/restore-4652
Jun 19, 2026
Merged

fix(input): restore IME cursor positioning reverted in #4779#4993
wenshao merged 3 commits into
QwenLM:mainfrom
BenGuanRan:fix/restore-4652

Conversation

@BenGuanRan

@BenGuanRan BenGuanRan commented Jun 11, 2026

Copy link
Copy Markdown
Collaborator

Summary

Restores PR #4652 (IME physical cursor positioning) which was silently reverted as part of PR #4779. See issue #4987 for context — the revert was my mistake while resolving a merge conflict, and a merged feature should not be rolled back inside an unrelated PR without explanation.

What's restored

Two cherry-picks, both with the original authors preserved:

  1. 51cafe433 — original PR feat(input): move physical cursor to visual cursor for IME input #4652 (@zzhenyao): adds BaseTextInput physical-cursor positioning via addLayoutListener + CursorContext, the prefixWidth plumbing through InputPrompt / AgentComposer, and the patches/ink+7.0.3.patch that exposes ink/dom and ink/components/CursorContext (plus the patch-package postinstall step).

  2. 370e480b8 — cherry-pick of a19eec771 by @LaZzyMan: fixes the dangling showYoloStyling reference in prefixWidth after PR fix(ui): distinguish auto approval mode indicators #4600 renamed it to approvalModePromptStyle. This fix existed on a side branch but never landed on main — it's needed here because feat(stats): add interactive /stats dashboard with cross-session tracking #4779's revert hid the original bug.

Verification

  • npm run build and npm run typecheck pass cleanly across all workspaces.
  • 14 tests fail under npm run preflight (1 in memoryDiagnostics.test.ts, 13 in AuthDialog.test.tsx with vi.waitFor timeouts). These touch auth dialog rendering and heap-snapshot cleanup — unrelated to anything in this PR. Likely pre-existing on main; please confirm during review.

Test plan

Closes #4987.

@BenGuanRan

Copy link
Copy Markdown
Collaborator Author

CI failure note: all three OS test jobs fail on the same two cases in src/utils/yaml-parser.test.ts:

  • stringify > known limitations — nested YAML (pin until js-yaml lands) > garbles array-of-records
  • stringify > known limitations — nested YAML (pin until js-yaml lands) > garbles record-of-records

The test names self-describe as pins for a known upstream js-yaml limitation. The same two failures reproduce on the latest main (run on afd631335) without this PR's changes — they're unrelated to the IME cursor restoration and not caused by anything in #4993.

Lint, CodeQL, and Classify PR all pass.

zzhenyao and others added 2 commits June 11, 2026 16:08
…nLM#4652)

* feat(input): add useCursor hook for IME physical cursor tracking

* refactor(input): optimize cursor positioning effect

* feat(input): move setCursorPosition to render phase for immediate cursor positioning

* fix(input): calculate absolute cursor position by walking yoga tree

* fix(input): use addLayoutListener instead of useCursor for zero-jitter cursor positioning

* perf(input): stable addLayoutListener subscription and skip redundant cursor updates

* fix(input): revert lastPos dedup that broke cursorDirty one-shot flag

* feat(input): use patch-package to expose Ink internals for IME cursor positioning

* fix(input): address review feedback — prefixWidth, remove useBoxMetrics, pin ink version

Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com>

---------

Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com>
QwenLM#4600 unified the approval-mode prompt styling into approvalModePromptStyle and removed the showAutoAcceptStyling / showYoloStyling constants, but left a dangling showYoloStyling reference in the prefixWidth calculation. This broke the tsc build (TS2304: Cannot find name showYoloStyling). npm run bundle (esbuild) skips type-checking, so the break only surfaced under npm run build / tsc --build.

Replace it with approvalMode === ApprovalMode.YOLO, matching how the rest of the file branches on approval mode after QwenLM#4600. Both ternary arms stay 2 because every approval-mode prefix is a single char (getApprovalModePromptStyle returns prefix > or *), so the rendered prefix width and cursor positioning are unchanged.
@BenGuanRan

Copy link
Copy Markdown
Collaborator Author

@zzhenyao help review , sorry for that commit-revert

DragonnZhang
DragonnZhang previously approved these changes Jun 11, 2026

@DragonnZhang DragonnZhang left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

No issues found. LGTM! — qwen3-coder via Qwen Code /review

@zzhenyao

Copy link
Copy Markdown
Contributor

@zzhenyao help review , sorry for that commit-revert

LGTM, thanks for restoring!

Comment thread package.json
"build:sdk:python": "python3 -m build packages/sdk-python",
"check-i18n": "npm run check-i18n --workspace=packages/cli",
"preflight": "npm run clean && npm ci && npm run format && npm run lint:ci && npm run build && npm run typecheck && npm run test:ci",
"postinstall": "patch-package",

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Critical] "postinstall": "patch-package" will block all npm install runs if the Ink patch fails to apply (ink version bump, patch conflict, file permissions, etc.). This breaks CI pipelines, Docker builds, and fresh checkouts with no fallback.

Suggested change
"postinstall": "patch-package",
"postinstall": "patch-package || true"

Alternatively, run patch-package only in CI with --error-on-fail, or move it to prepare where failures are more expected.

— DeepSeek/deepseek-v4-pro via Qwen Code /review

: 2 // "! " = 2 chars
: commandSearchActive
? 6 // "(r:) " (inner) + " " (outer) = 6 cols
: approvalMode === ApprovalMode.YOLO

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Suggestion] Dead ternary: both branches return 2, making the approvalMode === ApprovalMode.YOLO check meaningless. Reads as if YOLO needs a distinct width, but produces the same value as the default branch. A future change that adds a multi-character prefix for a new approval mode may update only one branch, causing a silent IME cursor misalignment.

Suggested change
: approvalMode === ApprovalMode.YOLO
: 2; // "> " or "* " = 2 chars

— DeepSeek/deepseek-v4-pro via Qwen Code /review

if (!node.parentNode)
return node['nodeName'] === 'ink-root' ? (node as DOMElement) : undefined;
return findRootNode(node.parentNode as Record<string, unknown>);
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Suggestion] findRootNode recurses up Ink's internal parentNode chain with no depth limit. If Ink's DOM structure ever changes unexpectedly (e.g., a virtual wrapper node introduced between the Box and ink-root, or a render bug creating a self-reference), this will crash with RangeError: Maximum call stack size exceeded — a hard CLI process crash with no recovery.

Suggested change
}
function findRootNode(
node: (Record<string, unknown> & { parentNode?: unknown }) | null,
depth = 0,
): DOMElement | undefined {
if (!node || depth > 50) return undefined;
// ... rest unchanged
return findRootNode(node.parentNode as Record<string, unknown>, depth + 1);
}

— DeepSeek/deepseek-v4-pro via Qwen Code /review

</Text>
);

// Calculate prefix width for physical cursor positioning

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Suggestion] prefixWidth values are hardcoded (2 for default/YOLO, 6 for search modes) rather than derived from the actual prefix string. If shell/approval mode prefixes change (e.g., a custom approval mode with a 3-character prefix), the physical cursor X position will be silently misaligned. The IME popup will appear offset from the actual composition text.

Consider computing prefixWidth from the rendered prefix string using stringWidth() instead of maintaining a parallel hardcoded mapping.

— DeepSeek/deepseek-v4-pro via Qwen Code /review

// but BEFORE onRender() — yoga layout is fresh, terminal not yet written.
// addLayoutListener requires the root node (ink-root), not the component
// node. We find it by walking up the Ink DOM parent chain.
const rootRef = useRef(null);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Suggestion] useRef(null) without a type parameter — TypeScript infers MutableRefObject<null>, so rootRef.current is typed as null even though React will assign a DOMElement at runtime. A future refactor could add incorrect null guards based on this type, silently breaking the feature.

Suggested change
const rootRef = useRef(null);
const rootRef = useRef<DOMElement | null>(null);

— DeepSeek/deepseek-v4-pro via Qwen Code /review

@wenshao

wenshao commented Jun 14, 2026

Copy link
Copy Markdown
Collaborator

@BenGuanRan heads up — this PR currently has merge conflicts with main and can't be merged as-is. Could you merge main in (or rebase) and resolve them when you get a chance?

Conflicting files:

  • package-lock.json

The rest merges cleanly. Thanks!

中文

@BenGuanRan 提个醒 —— 这个 PR 目前和 main 有合并冲突,暂时没法直接合入。方便的时候麻烦把最新的 main merge 进来(或 rebase)解决一下冲突。

冲突文件:

  • package-lock.json

其余文件可以自动合并。谢谢!

@wenshao

wenshao commented Jun 18, 2026

Copy link
Copy Markdown
Collaborator

@copilot resolve the merge conflicts in this pull request

1 similar comment
@wenshao

wenshao commented Jun 18, 2026

Copy link
Copy Markdown
Collaborator

@copilot resolve the merge conflicts in this pull request

@wenshao

wenshao commented Jun 18, 2026

Copy link
Copy Markdown
Collaborator

@qwen-code /triage

@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

Thanks for the PR @BenGuanRan!

Template: The PR body doesn't follow the PR template. The description is thorough, but it's missing the Reviewer Test Plan (with How to verify / Evidence / Tested on table), Risk & Scope, and Chinese translation sections. Would be great to fill those in when you get a chance — especially the Tested on table, since IME behavior varies a lot across platforms.

Merge conflict: package-lock.json still has conflicts with main — this blocks merge. A collaborator already flagged this.

On direction: Clearly aligned. PR #4652 was a legitimate fix for IME cursor positioning (#3456), it was accidentally reverted in #4779, and this restores it. No question about whether this should ship.

On approach: Scope is tight — two cherry-picks that do exactly what's described. patch-package + pinning ink to 7.0.3 is the standard approach for exposing internal APIs without forking. The second cherry-pick (showYoloStylingapprovalModePromptStyle) is a necessary follow-up fix. Nothing extraneous.

One note for the code review: the prefixWidth calculation in InputPrompt.tsx hardcodes 6 for reverse-search and command-search prefixes ("(r:) " + outer space) — worth double-checking that's accurate across all prompt states.

Moving on to code review. 🔍

中文说明

感谢 @BenGuanRan 的 PR!

模板: PR 正文未使用 PR 模板。描述很详细,但缺少 Reviewer Test Plan(验证方法 / 证据 / 测试平台表)、Risk & Scope 和中文翻译。方便时请补上——尤其是 Tested on 表,因为 IME 行为在不同平台差异较大。

合并冲突: package-lock.jsonmain 仍有冲突,阻塞合入。协作者已提醒过。

方向: 明确对齐。#4652#3456 的合理修复,在 #4779 中被误 revert,本 PR 将其恢复。合入没有问题。

方案: 范围紧凑——两个 cherry-pick 完全对应描述的目标。patch-package + 锁定 ink7.0.3 是暴露内部 API 的标准做法,不需要 fork。第二个 cherry-pick(showYoloStylingapprovalModePromptStyle)是必要的后续修复。没有多余内容。

代码审查的一个关注点:InputPrompt.tsxprefixWidth 对 reverse-search 和 command-search 硬编码为 6"(r:) " + 外层空格),需要确认在所有 prompt 状态下都准确。

进入代码审查 🔍

Qwen Code · qwen3.7-max

@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

Code Review

Reviewed against the diff and the current state of main:

  • BaseTextInput.tsx — The IME physical cursor positioning implementation is solid. Uses addLayoutListener (hooking into Ink's post-layout phase) + CursorContext to report physical screen coordinates. The stateRef pattern correctly avoids resubscribing the effect on every render while keeping the callback fresh. findRootNode walks the Ink DOM tree to find ink-root as required by addLayoutListener. The useEffect cleanup properly clears cursor position on unmount.

  • prefixWidth plumbing — Verified all hardcoded values against the actual prefix rendering in InputPrompt.tsx:

    • (r:) (4) + inner space + outer space = 6
    • ! + outer space = 2
    • > / * + outer space = 2
    • AgentComposer prefix (> or *) + outer space = 2
  • patch-package approach — Standard pattern for patching node_modules. The patch cleanly extends ink's exports map to expose ink/dom and ink/components/CursorContext. Pinning ink to 7.0.3 (exact) is correct — the patch is version-specific.

  • No critical blockers. No security issues. No correctness bugs. Code follows project conventions (ESM, strict TS, no any).

Verification

$ npx patch-package
patch-package 8.0.1
Applying patches...
ink@7.0.3 ✔

$ npm run build
✓ All packages compiled (0 errors, 15 pre-existing warnings in vscode-ide-companion)

$ npm run typecheck
✓ All 6 packages pass (core, cli, acp-bridge, sdk, webui, vscode-ide-companion)

$ npx vitest run src/ui/components/BaseTextInput.test.tsx
✓ 3 tests passed

$ npx vitest run src/ui/components/InputPrompt.test.tsx
✓ 151 tests passed, 1 skipped

IME Testing Limitation

IME cursor positioning requires a graphical terminal with an active input method (e.g. Pinyin, Japanese IME). This headless CI environment cannot test the actual IME behavior. The original PR author (@zzhenyao) confirmed the implementation works — manual verification on a real terminal with CJK IME is still needed before merge.

Summary: Code review is clean. Build, typecheck, and unit tests all pass. The implementation is well-structured and follows project conventions. The merge conflict in package-lock.json remains the only blocker.

中文说明

代码审查

对照 diff 和 main 当前状态审查:

  • BaseTextInput.tsx — IME 物理光标定位实现合理。使用 addLayoutListener(挂钩 Ink 布局后阶段)+ CursorContext 报告物理屏幕坐标。stateRef 模式正确避免了每次渲染重新订阅 effect,同时保持回调最新。findRootNode 遍历 Ink DOM 树查找 ink-rootaddLayoutListener 所需)。useEffect 清理在卸载时正确清除光标位置。

  • prefixWidth 管道 — 对照 InputPrompt.tsx 中的实际前缀渲染验证了所有硬编码值:

    • (r:) (4) + 内部空格 + 外部空格 = 6
    • ! + 外部空格 = 2
    • > / * + 外部空格 = 2
    • AgentComposer 前缀 (>*) + 外部空格 = 2
  • patch-package 方案 — 修补 node_modules 的标准模式。补丁干净地扩展了 ink 的 exports 映射,暴露 ink/domink/components/CursorContext。锁定 ink7.0.3(精确版本)是正确的——补丁与版本绑定。

  • 无关键阻塞问题。 无安全问题。无正确性 bug。代码遵循项目规范(ESM、严格 TS、无 any)。

验证

构建、类型检查和单元测试全部通过(详见上方英文部分输出)。

IME 测试限制

IME 光标定位需要图形终端和激活的输入法(如拼音、日语 IME)。当前无头 CI 环境无法测试实际 IME 行为。原作者 (@zzhenyao) 已确认实现有效——合入前仍需在真实终端上用 CJK 输入法手动验证。

总结: 代码审查无问题。构建、类型检查和单元测试全部通过。实现结构良好,遵循项目规范。package-lock.json 的合并冲突仍是唯一的阻塞点。

Qwen Code · qwen3.7-max

@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

Final Assessment

This PR does the right thing — restoring a legitimate bug fix that was accidentally reverted. The IME cursor positioning implementation is well-structured, the prefixWidth plumbing is verified correct, and all automated checks (build, typecheck, 154 unit tests) pass cleanly on the PR branch.

What blocks approval:

  1. Merge conflictpackage-lock.json conflicts with main. This is the only file affected; everything else merges cleanly. A quick rebase should resolve it.

  2. IME manual verification — The actual cursor positioning behavior requires a graphical terminal with an active CJK IME. The original feature author (@zzhenyao) LGTM'd the code, which is a strong signal, but a maintainer should ideally verify on macOS/Windows with Pinyin or Japanese IME before shipping.

What's clean: code quality, scope (no drive-by changes), approach (patch-package is standard), test coverage for the affected components. Nothing to rework — just the conflict resolution and a human IME check.

中文说明

最终评估

本 PR 做了正确的事——恢复被误 revert 的合理 bug 修复。IME 光标定位实现结构良好,prefixWidth 管道已验证正确,所有自动化检查(构建、类型检查、154 个单元测试)在 PR 分支上全部通过。

阻塞批准的原因:

  1. 合并冲突package-lock.jsonmain 有冲突。仅影响该文件;其余文件可自动合并。简单 rebase 即可解决。

  2. IME 手动验证 — 实际光标定位行为需要图形终端配合激活的 CJK 输入法。原功能作者 (@zzhenyao) 已 LGTM,这是积极信号,但合入前建议维护者在 macOS/Windows 上用拼音或日语 IME 验证。

无问题的部分: 代码质量、范围(无顺手改动)、方案(patch-package 是标准做法)、受影响组件的测试覆盖。不需要返工——只需解决冲突和人工 IME 验证。

Qwen Code · qwen3.7-max

@qwen-code-ci-bot qwen-code-ci-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Merge conflict in package-lock.json needs resolving, and IME behavior needs a quick manual check on a real terminal before shipping. Code itself looks good. 🙏

@wenshao

wenshao commented Jun 18, 2026

Copy link
Copy Markdown
Collaborator

@qwen-code /triage

@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

Thanks for the PR @BenGuanRan!

Template: The PR body doesn't follow the PR template — missing Reviewer Test Plan (How to verify / Evidence / Tested on table), Risk & Scope, and Chinese translation. The description is thorough though, so not blocking on this.

Merge conflict: package-lock.json has conflicts with main — resolved in the latest push. No longer a blocker. ✅

On direction: Clearly aligned. PR #4652 was a legitimate fix for IME cursor positioning (#3456), accidentally reverted in #4779. This restores it with two clean cherry-picks that preserve original authorship. No question about whether this should ship.

On approach: Scope is tight — two cherry-picks, nothing extraneous. patch-package + pinning ink to exact 7.0.3 is the standard approach for exposing internal APIs without forking. The showYoloStylingapprovalModePromptStyle rename fix is a necessary follow-up. prefixWidth hardcoded values verified against actual prefix rendering — all correct.

Moving on to code review. 🔍

中文说明

感谢 @BenGuanRan 的 PR!

模板: PR 正文未使用 PR 模板——缺少 Reviewer Test Plan(验证方法 / 证据 / 测试平台表)、Risk & Scope 和中文翻译。描述很详细,不因此阻塞。

合并冲突: package-lock.jsonmain 有冲突 — 最新推送已解决。不再阻塞。✅

方向: 明确对齐。#4652#3456 的合理修复,在 #4779 中被误 revert,本 PR 用两个干净的 cherry-pick 恢复并保留原作者。合入没有问题。

方案: 范围紧凑——两个 cherry-pick,没有多余内容。patch-package + 锁定 ink7.0.3(精确版本)是暴露内部 API 的标准做法。showYoloStylingapprovalModePromptStyle 重命名修复是必要的后续。prefixWidth 硬编码值对照实际前缀渲染验证——全部正确。

进入代码审查 🔍

Qwen Code · qwen3.7-max

@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

Code Review

Reviewed the diff against current main and independently verified the implementation:

  • BaseTextInput.tsx — IME physical cursor positioning via addLayoutListener + CursorContext is well-structured. The stateRef pattern correctly keeps the layout listener callback fresh without resubscribing on every render. findRootNode walks the Ink DOM tree to find ink-root as required. useEffect cleanup properly unsubscribes and clears cursor position on unmount. The ref on <Box> captures the component node for layout position walking.

  • prefixWidth plumbing — All hardcoded values verified against InputPrompt.tsx prefix rendering:

    • (r:) (4) + inner space + outer space = 6
    • ! + outer space = 2
    • > / * + outer space = 2
    • AgentComposer prefix (> or *) + outer space = 2
  • patch-package — Patch cleanly extends ink's exports map to expose ink/dom and ink/components/CursorContext. Pinning ink to exact 7.0.3 is correct (patch is version-specific). postinstall hook ensures patches apply on every install.

  • Type imports — Minor cleanup: React.ReactNodeReactNode direct import. Good.

  • No critical blockers. No security issues. No correctness bugs. Code follows project conventions (ESM, strict TS, no any).

Verification

$ npx patch-package
patch-package 8.0.1
Applying patches...
ink@7.0.3 ✔

$ npm run build
✓ All packages compiled (0 errors, 15 pre-existing warnings in vscode-ide-companion)

$ npm run typecheck
✓ All packages pass (core, cli, sdk, webui)

$ npx vitest run src/ui/components/BaseTextInput.test.tsx
✓ 3 tests passed

$ npx vitest run src/ui/components/InputPrompt.test.tsx
✓ 160 tests passed

$ npx vitest run src/ui/components/agent-view/AgentComposer.test.tsx
✓ 1 test passed

Smoke Test (tmux)

$ npm run dev -- -p 'echo hello'

> @qwen-code/qwen-code@0.18.3 dev
> node scripts/dev.js -p echo hello

DEV is set to true, but the React DevTools server is not running. Start it with:

$ npx react-devtools

hello

CLI starts and responds correctly with the patched ink dependency. The devtools notice is benign.

IME Testing Limitation

IME cursor positioning requires a graphical terminal with an active input method (Pinyin, Japanese IME, etc.). This headless CI environment cannot test the actual IME behavior. The original author (@zzhenyao) confirmed the implementation works — manual verification on a real terminal with CJK IME is recommended before merge.

Summary: Code review clean. Build, typecheck, and all 164 relevant tests pass. Implementation is well-structured and follows project conventions. Merge conflict resolved. Ready to merge pending manual IME verification.

中文说明

代码审查

对照 diff 和 main 当前状态审查:

  • BaseTextInput.tsx — IME 物理光标定位通过 addLayoutListener + CursorContext 实现,结构合理。stateRef 模式正确保持布局监听回调最新,无需每次渲染重新订阅。findRootNode 遍历 Ink DOM 树查找 ink-rootuseEffect 清理在卸载时正确取消订阅并清除光标位置。<Box> 上的 ref 捕获组件节点用于布局位置计算。

  • prefixWidth 管道 — 所有硬编码值对照 InputPrompt.tsx 前缀渲染验证:全部正确 ✓

  • patch-package — 补丁干净地扩展 ink 的 exports 映射,暴露 ink/domink/components/CursorContext。精确锁定 ink7.0.3 正确(补丁版本特定)。postinstall 钩子确保每次安装都应用补丁。

  • 无关键阻塞。 无安全问题,无正确性 bug,遵循项目规范。

验证结果

  • patch-package: ink@7.0.3 应用成功 ✓
  • build: 全部编译通过(0 错误)✓
  • typecheck: 全部包通过 ✓
  • BaseTextInput 测试: 3/3 ✓
  • InputPrompt 测试: 160/160 ✓
  • AgentComposer 测试: 1/1 ✓

冒烟测试

CLI 使用修补后的 ink 正常启动和响应。

IME 测试限制

IME 光标定位需要有图形终端和活跃输入法(拼音、日文 IME 等)。无头 CI 环境无法测试实际 IME 行为。原作者 @zzhenyao 确认实现有效——建议合入前在真实终端上用 CJK 输入法手动验证。

总结: 代码审查通过。构建、类型检查和全部 164 个相关测试通过。合并冲突已解决。建议合入,待手动 IME 验证。

Qwen Code · qwen3.7-max

@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

Verdict

This is a straightforward restoration of a previously merged feature that was accidentally reverted. The two cherry-picks are clean, the implementation is well-structured, and the scope is minimal — exactly what's needed to fix the regression.

Build, typecheck, and all 164 relevant tests pass. The patch-package integration works correctly. The CLI starts up without issues on the patched ink.

The only gap is IME testing — headless CI can't verify cursor positioning with an active input method. But @zzhenyao (original PR #4652 author) already confirmed LGTM, which carries weight since they wrote and tested the original implementation.

Recommendation: Approve. The merge conflict has been resolved, code review is clean, and the risk is low — this is a cherry-pick of code that was already on main before the accidental revert. The only remaining step is a manual IME spot-check by a maintainer with a CJK input method before or after merge.

中文说明

结论

这是一个简单的恢复——将误 revert 的已合入功能还原回来。两个 cherry-pick 干净,实现结构合理,范围最小——恰好修复回归所需。

构建、类型检查和全部 164 个相关测试通过。patch-package 集成正常。CLI 在修补后的 ink 上正常启动。

唯一的缺口是 IME 测试——无头 CI 无法验证活跃输入法下的光标定位。但 @zzhenyao(原 PR #4652 作者)已确认 LGTM,作为原作者的确认有分量。

建议: 批准合入。合并冲突已解决,代码审查通过,风险低——这是之前已在 main 上的代码被误 revert 后的恢复。唯一剩余步骤是维护者在合入前后用 CJK 输入法手动检查。

Qwen Code · qwen3.7-max

@qwen-code-ci-bot qwen-code-ci-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

LGTM — clean restoration of the IME cursor positioning. Build, typecheck, and all 164 relevant tests pass. Merge conflict resolved. Recommend manual IME spot-check before or after merge. ✅

@wenshao

wenshao commented Jun 18, 2026

Copy link
Copy Markdown
Collaborator

Maintainer verification — local merge build + flagged-failure triage

Verified the 3-way merge result (current origin/main + this PR head 55a57f5) on macOS (Darwin arm64, Node v22.22.2). The PR branch was a few commits behind main; the merge auto-resolved cleanly and the diff is exactly the PR's +328/-10 (7 files). This is a restore of the previously-merged IME cursor feature (#4652, reverted in #4779) plus the prefixWidth rename fix (a19eec771).

1. Build / typecheck / lint (merge result)

Check Result
BaseTextInput.test.tsx (restored cursor logic) 3 passed
InputPrompt.test.tsx (prefixWidth plumbing) 160 passed
tsc --noEmit CLI — changed components + patched-ink imports ✅ clean
npm run build core ✅ OK
eslint + prettier --check (3 changed components) ✅ clean
CI ✅ green on macOS / Windows / Linux; bot approved

2. The ink patch is valid

The PR ships patches/ink+7.0.3.patch (applied by patch-package postinstall) to expose ink/dom and ink/components/CursorContext, and pins ink to exact 7.0.3. Verified:

  • git apply --reverse --check of the patch succeeds against the installed ink@7.0.3, i.e. the patch's output exactly matches the package — and require.resolve('ink/dom') / require.resolve('ink/components/CursorContext') both resolve. (CI applies it fresh on a pristine ink via patch-package and is green.)

3. The prefixWidth rename fix

InputPrompt.tsx correctly uses approvalModePromptStyle and plumbs prefixWidth to BaseTextInputno dangling showYoloStyling reference remains (the bug #4779's revert had hidden). Confirmed by grep across the three touched components.

4. The 14 "unrelated" failures the PR flagged — confirmed pre-existing & flaky

The PR asked a maintainer to confirm the failures reproduce on main. They do, and they're flaky:

  • AuthDialog.test.tsx — failure count varies across identical runs: 13 (author) → 11 → 10 → 11. The failures are vi.waitFor.timeout (e.g. AuthDialog.test.tsx:154), i.e. render-timing timeouts, not assertion failures. This PR touches none of the auth dialog source or test, so the same code runs with or without it — the failures are pre-existing flakiness, not caused by fix(input): restore IME cursor positioning reverted in #4779 #4993.
  • memoryDiagnostics.test.ts12/12 passed here (the author's "1 failure" did not reproduce — also flaky/environmental).

So the flagged failures are a separate pre-existing flaky-test problem, independent of this change. They should not block the merge (a CI re-run clears them; the bot's rerun reported all relevant tests passing).

5. Scope note — IME behavior

The actual user-facing effect (CJK IME composition text rendering at the cursor rather than at line start) is an interactive terminal behavior that needs a real terminal + IME, so I did not drive it live. It is covered here by: the restored unit tests (BaseTextInput cursor logic, InputPrompt prefixWidth), the valid ink patch that exposes the cursor APIs, and the fact that this is a faithful restore of the already-reviewed-and-merged #4652.

Note on merge state

BLOCKED appears to be a stale CHANGES_REQUESTED review from @wenshao dated 2026-06-11 (empty body), filed before the rebase/conflict-resolution and the bot's later approval. Dismissing or converting that review should unblock it.

Verdict

Clean restore: it builds, typechecks, lints, the restored component tests pass, the ink patch is valid, and the prefixWidth rename bug is fixed. The 14 flagged failures are confirmed pre-existing flaky vi.waitFor timeouts unrelated to this PR. ✅ Safe to merge — re-run CI for the flaky auth tests and dismiss the stale change-request. (The IME rendering itself is best confirmed with a quick manual CJK-IME check, as the PR notes.)

Verified by maintainer @wenshao: 3-way merge build + BaseTextInput/InputPrompt suites (3/160) + ink-patch validity (reverse-check + resolve) + triage of the flagged failures (AuthDialog flaky vi.waitFor timeouts: 13/11/10 across runs; memoryDiagnostics 12/12). Env: Darwin arm64, Node v22.22.2. PR head 55a57f5.

中文版(点击展开)

维护者验证 —— 本地合并构建 + flag 失败排查

在 macOS(Darwin arm64,Node v22.22.2)上验证了三方合并结果(当前 origin/main + PR head 55a57f5)。PR 分支落后 main 几个 commit;合并自动干净解决,diff 正好是 PR 的 +328/-10(7 个文件)。这是对此前已合并的 IME 光标特性(#4652,在 #4779 中被回滚)的恢复,外加 prefixWidth 重命名修复(a19eec771)。

1. 构建 / 类型检查 / lint(合并结果)

检查 结果
BaseTextInput.test.tsx(恢复的光标逻辑) 3 通过
InputPrompt.test.tsx(prefixWidth 接线) 160 通过
tsc --noEmit CLI —— 改动组件 + 打补丁后的 ink 导入 ✅ 干净
npm run build core ✅ 通过
eslint + prettier --check(3 个改动组件) ✅ 干净
CI ✅ macOS / Windows / Linux 全绿;bot 已 approve

2. ink 补丁有效

PR 附带 patches/ink+7.0.3.patch(由 patch-package postinstall 应用),用于暴露 ink/domink/components/CursorContext,并把 ink 锁到精确的 7.0.3。验证:

  • 对已安装的 ink@7.0.3 执行 git apply --reverse --check 成功,即补丁的产物与包完全一致——且 require.resolve('ink/dom') / require.resolve('ink/components/CursorContext') 都能解析。(CI 会在 pristine 的 ink 上通过 patch-package 全新应用,且已绿。)

3. prefixWidth 重命名修复

InputPrompt.tsx 正确使用 approvalModePromptStyle 并把 prefixWidth 接到 BaseTextInput——不再有悬空的 showYoloStyling 引用(这正是 #4779 回滚所掩盖的 bug)。已对三个改动组件 grep 确认。

4. PR 标注的 14 个"无关"失败 —— 确认为既有且 flaky

PR 请维护者确认这些失败在 main 上也会复现。确实如此,而且是 flaky:

  • AuthDialog.test.tsx —— 失败数在相同命令的多次运行间波动:13(作者)→ 11 → 10 → 11。失败类型是 vi.waitFor.timeout(如 AuthDialog.test.tsx:154),即渲染计时超时,而非断言失败。本 PR 完全没有触及 auth dialog 的源码或测试,所以无论有没有它,跑的都是同一份代码——这些失败是既有的 flakiness,不是 fix(input): restore IME cursor positioning reverted in #4779 #4993 引起的。
  • memoryDiagnostics.test.ts —— 这里 12/12 通过(作者说的"1 个失败"未复现——同样属于 flaky/环境性)。

所以这些 flag 的失败是一个独立的既有 flaky 测试问题,与本改动无关。不应阻塞合并(重跑 CI 即可消除;bot 重跑也报告相关测试全过)。

5. 范围说明 —— IME 行为

真正面向用户的效果(CJK 输入法的候选/合成文本渲染在光标处而非行首)是交互式终端行为,需要真实终端 + 输入法,因此我没有实跑它。这里通过以下方式覆盖:恢复的单元测试(BaseTextInput 光标逻辑、InputPrompt prefixWidth)、暴露光标 API 的有效 ink 补丁,以及"这是对已审阅且已合并的 #4652 的忠实恢复"这一事实。

关于合并状态

BLOCKED 看起来是一条 @wenshao 于 2026-06-11 留下的、已过时的 CHANGES_REQUESTED 评审(空正文),它早于这次 rebase/冲突解决以及 bot 之后的 approve。把那条评审 dismiss 掉或转为 approve 即可解锁。

结论

干净的恢复:能构建、类型检查、lint 通过,恢复的组件测试通过,ink 补丁有效,prefixWidth 重命名 bug 已修。被 flag 的 14 个失败确认为与本 PR 无关的既有 flaky vi.waitFor 超时。✅ 可以合并——为 flaky 的 auth 测试重跑 CI,并 dismiss 那条过时的 change-request。(IME 渲染本身按 PR 所述,最好用 CJK 输入法做一次手动确认。)

维护者 @wenshao 验证:三方合并构建 + BaseTextInput/InputPrompt 套件(3/160)+ ink 补丁有效性(reverse-check + resolve)+ 对 flag 失败的排查(AuthDialog flaky vi.waitFor 超时:多次运行 13/11/10;memoryDiagnostics 12/12)。环境:Darwin arm64,Node v22.22.2。PR head 55a57f5

@wenshao
wenshao merged commit 77eda90 into QwenLM:main Jun 19, 2026
23 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.

PR #4779 silently reverted #4652 which was already merged to main

6 participants