[Bug]: macOS terminal clips CJK glyphs at the cursor — cursor lands on the wide character's first cell; cursor style/opacity ignored · Issue #12729 · stablyai/orca · GitHub
Skip to content

[Bug]: macOS terminal clips CJK glyphs at the cursor — cursor lands on the wide character's first cell; cursor style/opacity ignored #12729

Closed

Description

@ETM050

Operating system

macOS 26.5.2 (build 25F84), Apple Silicon (arm64)

Orca version

1.4.170

Details

When the cursor sits at the end of a run of CJK (full-width) text in an Orca terminal, the cursor is painted as a single-cell block over the first cell of the last wide character, and the right half of that glyph is never drawn. The character becomes unreadable.

Typing 가나다라 renders as:

› 가나다█        ← 'ㄹ' visible inside a 1-cell black block, 'ㅏ' missing entirely

This is not an IME problem (distinct from #9803, which is Windows/IME): it persists after the composition is committed, and it reproduces at a plain zsh prompt with no TUI running.

Two defects appear to be involved:

  1. Cursor lands on the wrong cell — on the wide character's starting cell instead of the column the application requested.
  2. terminalCursorStyle / terminalCursorOpacity have no effect — with "bar" and 0.1 stored in settings, the cursor still renders as a fully opaque block.

Reproduce

  1. macOS, open a new terminal tab in Orca.
  2. At a plain zsh prompt, type 가나다라.
  3. The final character shows only its left half inside a black block.

Reproduces in a freshly created tab, so it is not stale-instance state.

The application requests the correct column

Captured from a pty running the same TUI, immediately after sending 가나다라 (no Enter). Raw output, escapes made visible:

<ESC>[?25l <ESC>[H <ESC>[2C <ESC>[24B 가나다라 <ESC>[30;1H <ESC>[25;11H <ESC>[?25h
  • No DECSCUSR (ESC [ Ps SP q) — the application never forces a cursor shape.
  • No SGR 7 (reverse) — the application never paints a background over the text.
  • Final cursor position: ESC [ 25 ; 11 H → row 25, column 11 (1-based), i.e. the cell after 라. That is correct — 가나다라 occupies columns 3–10.

Reconstructing the same byte stream with pyte agrees:

col=2 '가'   col=4 '나'   col=6 '다'   col=8 '라'      (0-based, 2 cells each)
col=10 ' '  <-- cursor                                 reverse=False on every cell

What Orca draws

Pixel measurements from a 4320×2344 screenshot (D2CodingLigature Nerd Font Mono, size 14):

Quantity Value
Cell width (from the cursor block) 14 px
Korean glyph advance (가→나→다→라) 28 px
Ratio exactly 2 cells — font metrics are correct
Cursor block width 14 px (1 cell) — covers only the glyph's left half

Deriving columns (prompt › at col 1, x = 23; cell width 14 px):

Expected Observed
Cursor x 164 134
Cursor column 11 9 (= first cell of 라)

The cursor is placed two cells left of the requested column — on the wide character's starting cell — and painting that cell clips away the glyph's right half.

Settings that do NOT affect it

Each verified individually; the defect is identical in every combination:

  • terminalLineHeight: 1 → 1.3
  • terminalCursorStyle: block → bar
  • terminalCursorOpacity: default → 0.1
  • terminalGpuAcceleration: auto → off (DOM renderer)
  • terminalFontFamily: …Nerd Font Mono → …Nerd Font
  • Fresh terminal tab vs. existing tab

Note that "bar" and 0.1 are persisted in orca-data.json yet the rendered cursor stays an opaque block, so those options seem not to reach the xterm instance.

Related code in the shipped bundle

The bundle already models this class of problem, but the mitigation is gated to Windows and so never runs on macOS:

  • isEastAsianRendererRiskCodePoint() covers Hangul syllables (U+AC00–U+D7FF) and is combined with sgrSequenceSetsBackground() (SGR 40–47 / 100–107 / 48).
  • Its caller is guarded by state.isWindowsClient / state.isNativeWindowsConpty.
  • getTerminalWebglAutoDecision() inspects Linux hosts only; on macOS it returns { allowWebgl: true, reason: "non-linux" }, so terminalGpuAcceleration: "auto" always keeps WebGL enabled.

Expected behaviour

With the cursor on or after a full-width character, the whole glyph stays legible — either the cursor spans both cells of the wide character, or it is drawn on the column the application actually requested. Additionally, terminalCursorStyle and terminalCursorOpacity should take effect.

Activity

  1. bongho commented on Aug 18, 2026

    @bongho

    Still reproduces on 1.4.184 (this issue was filed against 1.4.170).

    Environment

    • macOS 26.5.2, Apple Silicon (arm64)
    • Orca 1.4.184
    • terminalFontFamily: D2CodingLigature Nerd Font Mono, terminalFontSize: 13, terminalFontWeight: 500, terminalLineHeight: 1
    • terminalCursorStyle: block

    What I see

    Typing Korean at a Claude Code prompt, the syllable under the cursor renders with only its left half visible — the right half of the glyph is never drawn. Moving the cursor away restores the character, so the damage is confined to the cell the cursor occupies.

    Two details that match your cursor-cell analysis:

    1. Latin text is unaffected. Single-cell glyphs are fully covered by the one-cell cursor block, so nothing appears to be missing. The defect only becomes visible with two-cell (full-width) characters.
    2. It is not composition state. The clipped glyph is already committed text — it just happens to be the character the cursor is sitting on. Pressing Enter makes it look correct simply because the cursor moves elsewhere.

    Ruled out on my machine

    I chased this as a font problem before finding this issue, and can confirm the font axis is a dead end. Switching the interface font between Geist, Pretendard Variable, and D2CodingLigature Nerd Font Mono changes nothing. I also measured the vertical metrics of every candidate at 14px against the 20px line box — D2Coding 16.24px, Pretendard 16.71px, Apple SD Gothic Neo 16.80px — all fit with room to spare, so there is no metric overflow involved.

    Happy to run an instrumented build or capture additional traces if that would help.

  2. nwparker commented on Aug 18, 2026

    @nwparker
    Contributor

    Resolved — and your measurements were right, which is what took the longest to reconcile.

    The black block is not the cursor. It is the box drawn over the terminal while an input method is still assembling a character. macOS 2-Set Korean keeps the final syllable composing until a terminator, so after 가나다라 only 가나다 has reached the shell and 라 sits in that overlay. In your version the overlay used stock colours — black on white — which is the block you saw.

    A block cursor could not have produced it: it inverts a cell, it cannot mask half a glyph, and on the renderer Orca uses by default it spans both cells of a wide character. That rules out the cursor explanation on both renderers, not just one.

    It also explains everything else in your report: the block sits at the composing cursor cell, no cursor setting reaches it, it looks identical with GPU acceleration off because it is drawn above both renderers, Latin never triggers it because Latin opens no composition, and pressing Enter clears it because Enter commits the composition.

    Already fixed. #15014 themes that overlay from your terminal theme and renders the row it covers. It landed on 2026-08-17; v1.4.184 was tagged 2026-08-16, so it missed your build by one day and ships in the next release.

    On your one-cell measurement, which I owe you a correction for. I initially argued it was self-referential. It was not — your report contained an independent measurement that backed it up, and I was wrong to wave it away.

    The reason it conflicted: the overlay has no width of its own, so it is exactly as wide as the glyph renders. With a font that has no Korean glyphs it collapses to about one cell. We added a browser-level test that measures it, and confirmed both halves:

    without a Korean font installed:  1.02 cells
    with a Korean font installed:     wider than 1.5 cells  (passes)
    

    So your measurement was accurate for your setup, and the overlay is genuinely two cells wide when the font can draw the character. Both true at once.

    Two things came out of this that outlive the issue: a test that pins the block as the overlay so the cursor explanation cannot be re-derived, and a CJK font on our test machines — without which none of our tests could tell a real result from a missing glyph.

    Thank you for the pixel measurements. They were the part that did not fit, and chasing why is what produced the actual answer.

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

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions