Skip to content

feat(input): move physical cursor to visual cursor for IME input#4652

Merged
pomelo-nwu merged 10 commits into
QwenLM:mainfrom
zzhenyao:patch-ink-fix-cursor-ime
Jun 3, 2026
Merged

feat(input): move physical cursor to visual cursor for IME input#4652
pomelo-nwu merged 10 commits into
QwenLM:mainfrom
zzhenyao:patch-ink-fix-cursor-ime

Conversation

@zzhenyao
Copy link
Copy Markdown
Contributor

Summary

  • Move the terminal physical cursor to the visual cursor position. IME candidate boxes appear at the input cursor.
  • Use addLayoutListener instead of useCursor. It fires after calculateLayout(), so cursor position reads the current yoga layout with zero jitter.
  • Calculate absolute cursor position by walking the yoga tree from root to the input component.
  • Stable subscription via useRef. Layout listener subscribes once and skips redundant updates.
  • Use patch-package to expose ink/dom and ink/components/CursorContext. Static import, no dynamic loading.

Motivation

Fixes #3456

Qwen Code renders a visual cursor using inverted-color characters. The terminal physical cursor is hidden and defaults to the bottom-left corner. CJK input method users see the IME candidate box at bottom-left, far from where they type.

Before:

1-loop

After:

3-loop

Changes

File Change
packages/cli/src/ui/components/BaseTextInput.tsx Cursor positioning via addLayoutListener + CursorContext, stable useRef subscription, cleanup on blur and unmount
packages/cli/src/ui/components/InputPrompt.tsx Related input handling
packages/cli/src/ui/components/agent-view/AgentComposer.tsx Minor related fix
package.json Add patch-package dependency and postinstall script
patches/ink+7.0.3.patch Patch Ink's exports map to expose ink/dom and ink/components/CursorContext

Design Decisions

  1. addLayoutListener over useCursor: Ink render order: calculateLayout()emitLayoutListeners()onRender(). useCursor reads layout during React's render phase, before yoga recomputes. addLayoutListener fires after calculateLayout() — cursor position is always current, no jitter.
  2. Absolute position via yoga tree wal: Walk the yoga tree from root to the input component to get absolute screen position. Works at any nesting depth.
  3. useRef for stable subscriptio: Cursor state lives in a ref, updated each render. useEffect deps are [cursorCtx] — the layout listener subscribes once. Skip the call when position hasn't changed. This removes the gap between cleanup and re-subscription.
  4. Cleanup on blur and unmount: Callback checks showCursor. Call setCursorPosition(undefined) when blurred. Cleanup function clears cursorPosition on unmount — goodbye screen renders without stale cursor position.
  5. patch-package for internal module acces: Ink only exports its main entry. Node.js 22 enforces the exports map. patch-package adds ink/dom (addLayoutListener) and ink/components/CursorContext (setCursorPosition) to the exports field. Static imports work directly.
  6. setCursorPosition every frame: log-update's cursorDirty is a one-shot flag — set on call, reset on next render. Skip a call and the flag stays false, the next frame gets no cursor suffix, and the physical cursor falls to the end of output.

Scope

  • Only moves the physical cursor to the visual cursor position. Does not affect other components.
  • Rapid screen updates may still place the IME candidate box at an unexpected position. This is a known limitation of Ink-based TUIs — Hermes and OpenClaude share the same constraint.
  • Hermes and OpenClaude fork Ink's source code to customize cursor positioning and rendering. Forking Ink is an option if this approach is not enough.

Test Plan

How to Verify

  1. Build and run

    npm run build
    npm start
  2. Patch applies correctly

    • Run npm installpatch-package applies ink+7.0.3.patch automatically
    • Confirm node_modules/ink/package.json has ink/dom and ink/components/CursorContext in the exports field
  3. IME cursor positioning

    • Focus the input box. Confirm the visual cursor appears.
    • Switch to a CJK input method (e.g. macOS: 搜狗/苹果拼音)
    • Type a few characters — the IME candidate box appears next to the cursor, not at the bottom-left of the terminal
    • Use arrow keys to move the cursor left/right — the candidate box follows
  4. Regression

    • Normal conversation, multi-line input, scrolling history — all work as before

// Calculate prefix width for physical cursor positioning
const prefixWidth = shellModeActive
? reverseSearchActive
? 5 // "(r:) " = 5 chars
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] prefixWidth is off by one for (r:) modes.

The prefixNode JSX renders '(r:) ' (5 chars, including a trailing space inside the inner <Text>) plus the outer {' '} (1 more space), totaling 6 columns. But prefixWidth is set to 5 here (and again at the commandSearchActive branch).

This means the IME candidate window will be positioned 1 column too far left when reverse-search or command-search mode is active — the very modes where the user is most likely composing text.

The other branches are correct: "!" + {' '} = 2, "*" + {' '} = 2, ">" + {' '} = 2.

Suggested change
? 5 // "(r:) " = 5 chars
? 6 // "(r:) " (inner) + " " (outer) = 6 cols

— qwen3.7-max via Qwen Code /review

// 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);
useBoxMetrics(rootRef);
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] useBoxMetrics(rootRef) is called but its return value is discarded. Internally, useBoxMetrics registers its own addLayoutListener on the root node (a second one, alongside the PR's own cursor listener) and calls setMetrics() / setHasMeasured() — two useState updates — on every layout commit. All of this work is wasted since the metrics are never read.

The ref is already populated by React via <Box ref={rootRef}> — no hook is needed for that. Removing useBoxMetrics eliminates the duplicate layout listener and avoids unnecessary re-renders of BaseTextInput on every Ink commit cycle.

Suggested change
useBoxMetrics(rootRef);
const rootRef = useRef(null);
const cursorCtx = useContext(CursorContext);

Also remove useBoxMetrics from the import:

Suggested change
useBoxMetrics(rootRef);
import { Box, Text } from 'ink';

— qwen3.7-max via Qwen Code /review

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.

[Suggestion] patch-package is added as a postinstall hook to apply patches/ink+7.0.3.patch, but ink is declared as "^7.0.3" (caret range). If npm resolves a newer Ink version (e.g. 7.0.4), patch-package will refuse to apply the patch because the filename is version-pinned to 7.0.3. The imports from 'ink/dom' and from 'ink/components/CursorContext' would then fail at runtime with ERR_PACKAGE_PATH_NOT_EXPORTED.

While the lockfile mitigates this in practice, consider pinning Ink to the exact version to make the coupling explicit:

"ink": "7.0.3"

— qwen3.7-max via Qwen Code /review

…cs, pin ink version

Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com>
@zzhenyao
Copy link
Copy Markdown
Contributor Author

@wenshao Thanks for the review. R1 Made the following changes:

  1. prefixWidth fix — (r:) mode 5 → 6 (missed outer space)
  2. Remove useBoxMetrics — unused layout listener and state update
  3. Pin ink version — "^7.0.3" → "7.0.3" to avoid patch mismatch

@zzhenyao zzhenyao requested a review from wenshao May 31, 2026 08:30
@zzhenyao
Copy link
Copy Markdown
Contributor Author

Noticed two small issues while using the cli:

  1. After the model finishes, LoadingIndicator disappears and InputPrompt moves up one row, the layout shifts. Could keep the last content when Idle so the bottom stays stable.

  2. Terminals are usually narrow, and the loading phrases are too long, they wrap to two lines. Shorter phrases for the CLI would help, but that needs i18n changes.

I can handle the first one. Are there plans to add shorter phrases? That would make keeping LoadingIndicator more useful.

Copy link
Copy Markdown
Collaborator

@wenshao wenshao left a comment

Choose a reason for hiding this comment

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

All R1 findings addressed in this revision (prefixWidth corrected to 6 for (r:) modes, useBoxMetrics removed, ink pinned to exact 7.0.3). Build passes, 1279 tests pass, tsc + eslint clean. No high-confidence issues found.

— qwen3.7-max via Qwen Code /review

@zzhenyao zzhenyao marked this pull request as ready for review May 31, 2026 08:51
@wenshao
Copy link
Copy Markdown
Collaborator

wenshao commented May 31, 2026

Verification Report

Branch: patch-ink-fix-cursor-ime
Base: main
Environment: macOS Darwin 25.4.0, Node.js v22.17.0

Test Results

Check Command Result
Patch Apply npx patch-package ink@7.0.3 ✔
ink/dom Import import('ink/dom') ✅ Resolved — exports addLayoutListener + 9 others
CursorContext Import import('ink/components/CursorContext') ✅ Resolved — exports default
BaseTextInput Tests vitest run BaseTextInput ✅ 3 tests passed (47ms)
CLI Type Check npm run typecheck --workspace=packages/cli ✅ Clean (0 TS errors)
CLI Build npm run build --workspace=packages/cli ✅ Success
ESLint eslint on BaseTextInput.tsx, InputPrompt.tsx, AgentComposer.tsx ✅ No errors
Whitespace git diff --check ✅ Clean

Patch Verification

After npm install --ignore-scripts && npx patch-package, confirmed node_modules/ink/package.json exports field correctly updated:

{
  ".": { "types": "./build/index.d.ts", "default": "./build/index.js" },
  "./dom": { "default": "./build/dom.js" },
  "./components/CursorContext": { "default": "./build/components/CursorContext.js" }
}

Code Review Notes

  • addLayoutListener is used correctly: it fires after calculateLayout() but before onRender(), so yoga layout positions are always current — avoids the jitter problem with useCursor which reads during React render phase
  • Absolute cursor position is computed by walking the yoga tree (yogaNode.getComputedLayout()) from component up to root, accumulating top/left — works at any nesting depth
  • The useRef pattern for stateRef is correct: the layout listener callback captures stable refs, avoiding resubscription on every render. useEffect deps are [cursorCtx] only
  • Cleanup properly calls setCursorPosition(undefined) on unmount, preventing stale cursor positions
  • prefixWidth is correctly threaded from InputPrompt (2 for > , 6 for reverse search (r:) ) and AgentComposer (2) to BaseTextInput
  • ink version pinned to exact 7.0.3 (removed ^) in cli/package.json to match the patch — correct practice for patch-package
  • setCursorPosition is called every frame by design: log-update's cursorDirty is a one-shot flag that resets after each render, so skipping a call would drop cursor positioning for that frame

Caveats

  • This PR is primarily a visual/UX improvement (IME candidate box positioning). Full verification requires manual testing with a CJK input method. Automated tests confirm the code compiles, builds, and the patched ink modules resolve correctly.
  • The patch-package postinstall hook is a reasonable approach for exposing ink internals without forking. The patch is minimal (exports map only, no source changes).

Verdict

✅ Ready to merge — patch applies cleanly, all imports resolve at runtime, typecheck is clean, build succeeds. The approach is sound and well-documented in the PR.


Verified by wenshao

@zzhenyao
Copy link
Copy Markdown
Contributor Author

zzhenyao commented Jun 1, 2026

Hi! @wenshao Thanks for your approval!

If possible, could you also help invite another reviewer for this PR?

@pomelo-nwu
Copy link
Copy Markdown
Collaborator

Thanks for this PR, @zzhenyao!

Template: The PR uses its own heading structure (Summary / Motivation / Changes / Design Decisions / Scope / Test Plan) instead of the project's PR template. The content quality is excellent — clear problem statement, before/after screenshots, design rationale, and verification steps are all present. Two small gaps: no "Tested on" OS matrix and no 中文说明 section. Not blocking, but please align with the template format for consistency — it helps reviewers scan PRs faster.

On direction: This solves a real and painful problem for CJK users — IME candidate box appearing at the bottom-left instead of following the cursor is a daily friction point. Clearly aligned with the roadmap/terminal-ux priority. No prior CHANGELOG entry for cursor/IME work, but that just means this would be a first. The referenced issue #3456 is well-documented with screenshots showing the exact problem. Strong fit.

On approach: patch-package to expose ink/dom and ink/components/CursorContext is pragmatic — it avoids a full Ink fork while getting the exact hooks needed. Pinning ink to 7.0.3 (exact) is the right call to prevent silent breakage on version bumps. The addLayoutListener over useCursor choice is well-justified (fires after calculateLayout(), no jitter). The yoga tree walk for absolute positioning is clever and contained.

Scope feels right — only touches cursor positioning, doesn't restructure other components. The prefixWidth prop is a minor coupling concern (hardcoded magic numbers in InputPrompt and AgentComposer), but it's practical and the R1 review already caught the off-by-one.

One question worth thinking about: if Ink 7.1+ ships with these exports natively, the patch becomes dead weight. Have you considered adding a comment in the patch file or a TODO noting the intent to drop it once upstream supports these exports? Not blocking — just helps the next person who sees patches/ink+7.0.3.patch and wonders why it's there.

Moving on to code review. 🔍

中文说明

感谢 @zzhenyao 的贡献!

模板: PR 使用了自己的标题结构(Summary / Motivation / Changes / Design Decisions / Scope / Test Plan),未对齐项目的 PR 模板。内容质量很好——问题描述清晰、有前后对比截图、设计决策充分、验证步骤完整。两个小缺口:没有 "Tested on" 操作系统矩阵,没有中文说明段落。不作为阻塞项,但建议对齐模板格式。

方向: 这解决的是 CJK 用户的真实痛点——输入法候选框出现在终端左下角而非跟随光标,是日常高频摩擦。与 roadmap/terminal-ux 优先级高度一致。CHANGELOG 中尚无光标/输入法相关工作记录,但这正好说明这会是首次。引用的 issue #3456 记录详尽,截图清晰展示了问题。方向非常契合。

方案:patch-package 暴露 ink/domink/components/CursorContext 是务实的选择——避免了完整 fork Ink,同时获得了所需的 hook。将 ink 精确锁定到 7.0.3 是正确的防御措施。选择 addLayoutListener 而非 useCursor 理由充分(在 calculateLayout() 之后触发,无抖动)。yoga 树遍历获取绝对定位巧妙且收敛。

范围合理——只涉及光标定位,未重构其他组件。prefixWidth prop 是轻微的耦合隐患(InputPromptAgentComposer 中的硬编码魔法数字),但实用且 R1 review 已修复了偏差。

一个值得思考的问题:如果 Ink 7.1+ 原生支持这些导出,patch 就变成了无用代码。是否考虑在 patch 文件中加个注释或 TODO,标注"上游支持后移除"的意图?不阻塞——只是帮助下一个看到 patches/ink+7.0.3.patch 的人理解上下文。

进入代码审查 🔍

Qwen Code · qwen3.7-max

@pomelo-nwu
Copy link
Copy Markdown
Collaborator

Code review: Read through the diff with an independent proposal in mind. The problem is clear — Ink hides the terminal cursor and renders its own visual cursor via inverted characters, so the physical cursor (and IME candidate box) defaults to bottom-left. The fix needs to call setCursorPosition on the terminal with the absolute screen coordinates of the visual cursor, which requires access to Ink internals (addLayoutListener, CursorContext) that aren't in the public exports map.

The implementation is clean and well-reasoned:

  • findRootNode walks Ink's parent chain to locate ink-root — fragile but acknowledged, and mitigated by the exact version pin.
  • stateRef pattern avoids resubscription: the layout listener callback always reads fresh state without re-registering. useEffect deps are [cursorCtx] only — correct.
  • Yoga tree walk accumulates top/left from component to root — gives absolute position at any nesting depth. Sound approach.
  • prefixWidth prop threading is pragmatic. The R1 off-by-one fix (5 → 6 for (r:) modes) is correct.
  • Cleanup properly unsubscribes and resets cursor position on unmount. No leak paths.

No critical blockers, no AGENTS.md violations. The reliance on Ink internals is a known tradeoff — the author explicitly mentions forking Ink as a fallback if this approach proves insufficient, which is the right escalation path.

Testing: Built and tested the PR branch from source. Patch applies cleanly (ink@7.0.3 ✔). Headless mode (-p 'respond with just the word hello' -o text) produces correct output, matching the main branch baseline. Interactive UI starts correctly — welcome screen renders, input box accepts text, visual cursor appears at the expected position.

   ▄▄▄▄▄▄  ▄▄     ▄▄ ▄▄▄▄▄▄▄ ▄▄▄    ▄▄   ┌──────────────────────────────────────────────────────────┐
  ██╔═══██╗██║    ██║██╔════╝████╗  ██║  │ >_ Qwen Code (vdev)                                      │
  ██║   ██║██║ █╗ ██║█████╗  ██╔██╗ ██║  │                                                          │
  ██║▄▄ ██║██║███╗██║██╔══╝  ██║╚██╗██║  │ Standard API Key | qwen3.7-max (/model to change)        │
  ╚██████╔╝╚███╔███╔╝███████╗██║ ╚████║  │ /private/tmp/qwen-pr-test                                │
   ╚══▀▀═╝  ╚══╝╚══╝ ╚══════╝╚═╝  ╚═══╝  └──────────────────────────────────────────────────────────┘
────────────────────────────────────────────────────────────────────────────────────────────────────────
* hello world █
────────────────────────────────────────────────────────────────────────────────────────────────────────
  qwen3.7-max | Context 100% left | /private/tmp/qwen-pr-test | pr-4652 | #4652 | 1.0m window
  YOLO 模式 (Shift + Tab 切换)

Caveat: IME candidate box positioning cannot be verified in headless tmux — it requires a real terminal session with a CJK input method active (e.g. macOS 搜狗/苹果拼音). The automated tests confirm the code compiles, builds, and basic input handling works. Full IME verification needs a human reviewer on macOS with a CJK IME.

中文说明

代码审查: 带着独立方案阅读 diff。问题很清晰——Ink 隐藏终端光标并用反色字符渲染视觉光标,导致物理光标(和输入法候选框)默认停在左下角。修复方案需要用绝对屏幕坐标调用 setCursorPosition,这需要访问 Ink 未公开导出的内部模块(addLayoutListenerCursorContext)。

实现干净且有充分理由:

  • findRootNode 遍历 Ink 父链定位 ink-root——脆弱但作者已知晓,精确版本锁定缓解风险。
  • stateRef 模式避免重复订阅:布局监听回调始终读取最新状态而无需重新注册。useEffect 依赖仅 [cursorCtx]——正确。
  • Yoga 树遍历从组件到根累加 top/left——在任意嵌套深度获取绝对位置。方案可靠。
  • prefixWidth prop 传递务实。R1 修复的偏差((r:) 模式 5 → 6)正确。
  • 清理逻辑正确退订并在卸载时重置光标位置。无泄漏路径。

无 critical blocker,无 AGENTS.md 违规。依赖 Ink 内部模块是已知取舍——作者明确提到如果此方案不够用可以 fork Ink,这是正确的升级路径。

测试: 从源码构建并测试 PR 分支。Patch 正确应用(ink@7.0.3 ✔)。Headless 模式(-p 'respond with just the word hello' -o text)输出正确,与 main 分支基线一致。交互式 UI 正常启动——欢迎页渲染、输入框接受文字、视觉光标出现在预期位置。

注意: 输入法候选框定位无法在 headless tmux 中验证——需要真实终端会话并激活 CJK 输入法(如 macOS 搜狗/苹果拼音)。自动化测试确认代码编译、构建、基本输入处理正常。完整的输入法验证需要 macOS 上使用 CJK 输入法的人工审查。

Qwen Code · qwen3.7-max

@pomelo-nwu
Copy link
Copy Markdown
Collaborator

Stepping back — this is a well-scoped PR solving a real daily friction for CJK users. The before/after screenshots in the PR description are immediately relatable: anyone who's typed Chinese in a terminal TUI knows that "IME candidate box stuck at bottom-left" feeling.

The approach is the right level of pragmatism. patch-package to expose two Ink internals is lighter than forking Ink, heavier than doing nothing — and the exact version pin (7.0.3 not ^7.0.3) is the correct defense against silent breakage. If Ink 7.1+ ships these exports natively, the patch becomes a one-line deletion. If it doesn't, the patch stays stable because the version is locked.

The code itself reads like someone who understands both React lifecycle subtleties and Ink's rendering pipeline. The stateRef pattern (mutable ref + stable callback) is the right call for layout listeners — avoids the subscribe/unsubscribe churn that a naive useEffect with full deps would create. The yoga tree walk is contained in one function and one callback — no abstraction creep.

My one reservation: prefixWidth as a prop with hardcoded magic numbers in callers. It works today, but if a new prefix variant appears (e.g. a custom prompt mode), someone has to remember to add another branch. A useMeasure-style approach or reading the prefix node's yoga layout directly would be more robust — but that's optimization, not correctness. Not blocking.

From a maintenance perspective: I'd rather maintain this than not have it. The CJK input experience is meaningfully better, the code is contained to the right files, and the Ink dependency story is clear. R1 review caught the prefixWidth off-by-one and the unused useBoxMetrics — both addressed. Build, typecheck, tests, lint all clean.

LGTM, looks ready to ship. ✅

中文说明

退一步看——这是一个范围合理的 PR,解决了 CJK 用户的日常高频摩擦。PR 描述中的前后对比截图一看就懂:在终端 TUI 里打过中文的人都知道"输入法候选框卡在左下角"是什么体验。

方案处于正确的务实区间。用 patch-package 暴露两个 Ink 内部模块比 fork Ink 轻量,比什么都不做有效——精确版本锁定(7.0.3 而非 ^7.0.3)是防止静默失效的正确防御。如果 Ink 7.1+ 原生支持这些导出,patch 变成一行删除;如果不支持,版本锁定保证 patch 稳定。

代码本身体现了对 React 生命周期和 Ink 渲染管道的双重理解。stateRef 模式(可变 ref + 稳定回调)是布局监听器的正确选择——避免了朴素 useEffect 全依赖带来的反复订阅/退订。Yoga 树遍历收敛在一个函数和一个回调中——无抽象膨胀。

一个保留意见:prefixWidth 作为 prop 在调用方硬编码魔法数字。当前可用,但如果出现新的前缀变体(比如自定义 prompt 模式),需要记得加分支。用 useMeasure 风格或直接读取前缀节点的 yoga 布局会更健壮——但这是优化不是正确性问题。不阻塞。

从维护角度看:有这个比没有好。CJK 输入体验有实质改善,代码收敛在正确的文件中,Ink 依赖策略清晰。R1 review 发现了 prefixWidth 偏差和未使用的 useBoxMetrics——都已修复。构建、类型检查、测试、lint 全部通过。

可以合并 ✅

Qwen Code · qwen3.7-max

@pomelo-nwu pomelo-nwu added type/enhancement Non-bug improvement or optimization category/ui User interface and display scope/components UI components and widgets scope/rendering Display and rendering logic roadmap/terminal-ux Roadmap: Terminal UX and UI labels Jun 3, 2026
Copy link
Copy Markdown
Collaborator

@pomelo-nwu pomelo-nwu left a comment

Choose a reason for hiding this comment

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

LGTM, looks ready to ship. ✅

@pomelo-nwu pomelo-nwu merged commit 77458ad into QwenLM:main Jun 3, 2026
10 checks passed
@zzhenyao
Copy link
Copy Markdown
Contributor Author

zzhenyao commented Jun 3, 2026

@pomelo-nwu Thanks for the review and approval! 🙏

A few notes on the Ink situation:
We're using patch-package to expose Ink's internal modules (ink/dom and ink/components/CursorContext). This is a pragmatic short-term approach — avoids forking while getting the hooks we need.

As the review mentioned, if Ink upstream ships these exports natively in a future version, the patch becomes dead weight. Both Hermes and OpenClaude fork Ink's source to customize cursor positioning and rendering.

Forking gives full control over cursor positioning and the render pipeline, but comes with maintenance cost : tracking upstream changes, manually merging fixes and new features.

There's still quite a bit of UI jitter in the current version. patch-package solves the core IME cursor positioning problem first, which already makes the experience much better. If cursor positioning needs go deeper, forking Ink is a reasonable next step.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

category/ui User interface and display roadmap/terminal-ux Roadmap: Terminal UX and UI scope/components UI components and widgets scope/rendering Display and rendering logic type/enhancement Non-bug improvement or optimization

Projects

None yet

Development

Successfully merging this pull request may close these issues.

CJK (Chinese/Japanese/Korean) IME composition text appears at wrong position — input shows on an extra line

3 participants