Skip to content

feat(terminal): opt-in wide East Asian Ambiguous character width - #10078

Open
innocarpe wants to merge 5 commits into
stablyai:mainfrom
innocarpe:fix/terminal-east-asian-ambiguous-width
Open

innocarpe wants to merge 5 commits into
stablyai:mainfrom
innocarpe:fix/terminal-east-asian-ambiguous-width

Conversation

@innocarpe

@innocarpe innocarpe commented Jul 23, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Opt-in setting for East Asian Ambiguous character cell width (iTerm2-style escape hatch), requested in #9958.

On Windows CJK locales, characters like ① ■ ● ★ → render poorly in Orca because:

  1. Unicode 11 width tables report them as width 1 (modern CLI default)
  2. Cascadia Mono / Consolas either lack the glyphs or draw them too small for a single cell

This PR keeps the modern CLI-aligned default (narrow) and adds Wide mode that:

  • Treats East Asian Ambiguous (EAW=A) code points as double-width in OrcaUnicodeProvider
  • Inserts CJK UI fonts (Malgun Gothic, Microsoft YaHei, Yu Gothic, …) ahead of Consolas so full-em glyphs fill the two-cell slot

Changes

  • terminalEastAsianAmbiguousWidth: 'narrow' | 'wide' (default narrow)
  • Shared EAW ambiguous range table + resolveEastAsianAmbiguousCellWidth
  • Provider reads mode at measurement time (settings toggle without re-register)
  • Settings → Terminal → Typography Advanced: Ambiguous Character Width
  • buildFontFamily(..., { eastAsianAmbiguousWide }) CJK fallback insertion

Fixes #9958

Screenshots

  • Settings → Terminal → Advanced typography disclosure → Ambiguous Character Width (Narrow / Wide)
  • No default visual change (narrow remains default)

Testing

  • pnpm exec vitest run --config config/vitest.config.ts src/shared/east-asian-ambiguous-width.test.ts src/shared/terminal-unicode-provider.test.ts src/renderer/src/components/terminal-pane/layout-serialization.test.ts (40/40)
  • pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/lib/pane-manager/pane-lifecycle.test.ts (30/30)
  • Full pnpm lint / pnpm typecheck / pnpm test / pnpm build
  • Unit coverage for EAW classification, wide-mode wcwidth, and CJK font insertion order

AI Review Report

  • Default safety: narrow preserves modern CLI width tables; wide is opt-in only
  • Consistency: wcwidth and charProperties stay aligned when wide expands ambiguous cells
  • Font coupling: wide mode alone without CJK fonts would leave ① tiny; both paths wired together as the issue specified
  • Cross-platform: CJK fallback list covers Windows (Malgun/YaHei/Yu Gothic), macOS (Apple SD/PingFang/Hiragino), Linux (Noto Sans CJK); browsers skip missing fonts
  • Mobile: not re-wired in this PR — mobile still loads Unicode11 directly; follow-up if settings plumbing is desired there
  • Headless emulator: continues to use the shared provider (module mode defaults narrow; daemon measurement stays CLI-aligned)

Security Audit

  • No new IPC, path handling, or command execution
  • Setting is a local preference only; no network or secrets

Notes

  • Changing width mid-session affects newly written cells; existing buffer layout is not reflowed (same as other terminals). UI copy notes opening a new pane for a full reflow.
  • Known trade-off when Wide is on: TUIs that assume ambiguous width 1 can misalign — that is why this stays opt-in and off by default.

ELI5

Some CJK symbols looked too skinny or wrong in the terminal on Windows. There is an optional Wide mode for East Asian Ambiguous characters so shapes like stars and arrows get a full cell width when you want that.

@coderabbitai

coderabbitai Bot commented Jul 23, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 997e714b-994f-4a58-b2f0-af732d2649da

📥 Commits

Reviewing files that changed from the base of the PR and between 26f03fc and 46640c2.

📒 Files selected for processing (4)
  • mobile/src/terminal/terminal-webview-html.ts
  • src/main/daemon/headless-emulator-options.ts
  • src/main/daemon/headless-emulator.ts
  • src/main/index.ts

📝 Walkthrough

Walkthrough

Adds a narrow/wide terminal setting for East Asian ambiguous characters. The change introduces Unicode range classification and width resolution, configurable Unicode provider behavior, CJK font fallback ordering, and propagation through terminal initialization, appearance updates, and previews. The setting is persisted with a narrow default and exposed through advanced typography controls, labels, and localized search metadata. Unit tests cover classification, width resolution, provider behavior, idempotent activation, and font ordering.

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning The PR also adds terminal wheel/trackpad scrolling support, which is unrelated to the East Asian ambiguous-width feature. Split the scrolling changes into a separate PR unless they are explicitly required by the linked issue.
Docstring Coverage ⚠️ Warning Docstring coverage is 27.27% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and accurately reflects the opt-in East Asian Ambiguous width feature.
Description check ✅ Passed The description matches the template and includes the required summary, screenshots, testing, review, security, and notes sections.
Linked Issues check ✅ Passed The changes implement the requested opt-in wide ambiguous-width behavior across renderer, headless, mobile, and font fallback paths.

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.

@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

🧹 Nitpick comments (1)
src/shared/terminal-unicode-provider.test.ts (1)

54-61: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Exercise wide-mode charProperties as well.

The test only checks wcwidth; the newly added packed-property path is what buffer writes consume. Assert that an ambiguous code point has width bits 2 after switching to wide mode.


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 0015135e-dae9-4f27-b7f2-fb02d679f6ae

📥 Commits

Reviewing files that changed from the base of the PR and between 8448557 and b280441.

📒 Files selected for processing (15)
  • src/renderer/src/components/settings/TerminalAdvancedTypographyControls.tsx
  • src/renderer/src/components/settings/TerminalSettingsPreview.tsx
  • src/renderer/src/components/settings/setting-labels.ts
  • src/renderer/src/components/settings/terminal-typography-search.ts
  • src/renderer/src/components/terminal-pane/layout-serialization.test.ts
  • src/renderer/src/components/terminal-pane/layout-serialization.ts
  • src/renderer/src/components/terminal-pane/terminal-appearance.ts
  • src/renderer/src/components/terminal-pane/use-terminal-pane-lifecycle.ts
  • src/renderer/src/lib/pane-manager/pane-lifecycle.ts
  • src/shared/constants.ts
  • src/shared/east-asian-ambiguous-width.test.ts
  • src/shared/east-asian-ambiguous-width.ts
  • src/shared/terminal-unicode-provider.test.ts
  • src/shared/terminal-unicode-provider.ts
  • src/shared/types.ts

Comment thread src/renderer/src/components/settings/TerminalSettingsPreview.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


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 0b4ee663-013c-4bc9-a708-edad9d9fb749

📥 Commits

Reviewing files that changed from the base of the PR and between b280441 and f73f7df.

📒 Files selected for processing (1)
  • src/renderer/src/components/settings/TerminalSettingsPreview.tsx

Comment thread src/renderer/src/components/settings/TerminalSettingsPreview.tsx
@nwparker nwparker added the enhancement New feature or request label Jul 27, 2026
Add terminalEastAsianAmbiguousWidth (narrow|wide, default narrow) so CJK
locales can match conhost double-width ambiguous glyphs (① ■ ★ →) without
changing the modern CLI-aligned default. Wide mode also inserts CJK UI
fonts ahead of Consolas so full-size glyphs fill the two-cell slot.

Closes stablyai#9958
buildFontFamily only adjusts the CJK fallback stack; activate Orca's
unicode provider (and wide ambiguous mode) before writing the sample
buffer so the preview measures like live panes.
@innocarpe
innocarpe force-pushed the fix/terminal-east-asian-ambiguous-width branch from f73f7df to bae2e4e Compare July 28, 2026 13:11
@DoDAON

DoDAON commented Jul 30, 2026

Copy link
Copy Markdown

Hi @innocarpe, thanks for picking this up and for the quick PR.

Just checking in on progress — it's been a few days since the last commit, and CodeRabbit's linked-issues check is still flagging that the headless emulator (src/main/daemon/headless-emulator.ts) and mobile (mobile/src/terminal/terminal-webview-html.ts) activation sites from the original issue aren't wired up yet, on top of the docstring coverage warning.

Those two paths matter for the SSH and mobile use cases specifically, so I'd like to see all three activation sites covered before merge. Are you still actively working on this, or would it help to have someone else pick up the remaining wiring? Happy to help if you're stuck on anything.

@innocarpe

Copy link
Copy Markdown
Contributor Author

Sync update (bae2e4e42)

@DoDAON Thanks for the check-in — still on this, and the remaining two activation sites are now wired.

Headless (headless-emulator.ts)

  • Sets East Asian Ambiguous mode before activateOrcaTerminalUnicodeProvider
  • Runtime passes store.getSettings().terminalEastAsianAmbiguousWidth when creating PTY mirrors
  • Main process also syncs the process-wide mode on settings load/change so live headless buffers stay aligned with the renderer (SSH fidelity)

Mobile (terminal-webview-html.ts + engine)

  • Bundles Orca unicode into the WebView engine (window.OrcaTerminalUnicode)
  • After Unicode11, activates the Orca provider (this path previously stopped at Unicode11 and bypassed Orca entirely)
  • init accepts eastAsianAmbiguousWidth; wide mode also inserts CJK UI fonts ahead of Consolas (same coupling as desktop)

Tests

  • Headless: wide vs narrow ① cell advance
  • Mobile: activate order (open → Unicode11 → Orca → replay); Chrome 74 parse floor still green

Happy to adjust if you want mobile to pull the host setting via settings.get at session open as a follow-up (prop/init path is ready).

DoDAON / stablyai#9958 asked for all three activation sites. Desktop already set
mode + OrcaUnicodeProvider; this pass completes the twins:

- Headless: honor eastAsianAmbiguousWidth (runtime store + settings sync)
- Mobile: bundle Orca unicode into the WebView engine and activate after
  Unicode11 (was bypassing Orca entirely); CJK font stack when wide
- Provider as plain object so the Chrome 74 engine floor parses cleanly

Evidence: headless-emulator-unicode-width + mobile engine/html tests
Place TERMINAL_EAW_JS after the default terminalFontFamily assignment so
vm-based font fallback tests still slice a valid isIOSWebView block.
@innocarpe

Copy link
Copy Markdown
Contributor Author

Sync update (26f03fc82)

Follow-up: 1cb8121 + font-extract test fix are on the branch (earlier sync comment referenced a pre-push tip).

@DoDAON — remaining activation sites are wired as described above. Full headless/mobile coverage for #9958 is in this PR tip.

Comment thread src/shared/east-asian-ambiguous-width.ts Outdated
@greptile-apps

greptile-apps Bot commented Jul 30, 2026 •

Copy link
Copy Markdown

Greptile Summary

This PR adds an opt-in terminalEastAsianAmbiguousWidth setting (narrow | wide, defaulting to narrow) that lets users on Windows CJK locales treat East Asian Ambiguous codepoints as double-width cells, paired with CJK UI font injection ahead of Consolas so glyphs like ① ★ → fill the two-cell slot.

  • Core EAW logic (east-asian-ambiguous-width.ts): compact binary-search range table with intentional PUA exclusion, shared resolveEastAsianAmbiguousCellWidth, and a TerminalEastAsianAmbiguousWidth type that is also re-declared separately in types.ts.
  • Provider refactor (terminal-unicode-provider.ts): OrcaUnicodeProvider becomes a plain-object factory for Chrome 74 / ES2019 compat; a module-level mode variable is read at measurement time so settings changes take effect on new cells without re-registering the provider.
  • Font plumbing (layout-serialization.ts, terminal-appearance.ts, use-terminal-pane-lifecycle.ts): buildFontFamily gains a CJK_AMBIGUOUS_FALLBACK_FONTS splice before Consolas, and the redundant per-pane setTerminalEastAsianAmbiguousWidthMode call is correctly hoisted before the pane loop.
  • Mobile WebView: replaces the direct Unicode11Addon activation with the shared Orca provider via activateOrcaUnicodeOnTerm; eastAsianAmbiguousWidth prop is threaded to the init message, though runtime settings updates on mobile are deferred to a follow-up.

Confidence Score: 5/5

Safe to merge. The default is narrow (no visible change for existing users), and the opt-in wide path is well-isolated through the module-level mode variable and correct font-stack injection ordering.

The feature is entirely opt-in with a conservative default. The core EAW logic (binary search table, resolver, provider patching) is straightforward and well-tested. The global mutable mode design is intentional and all write sites converge to the same store value. The only findings are style-level concerns — a mobile CJK fallback list that omits one font the desktop includes, a hardcoded wide font string that could drift from its narrow counterpart, and a duplicated type declaration — none of which affect correctness for the current release.

Files Needing Attention: mobile/src/terminal/terminal-webview-eaw-injected.ts — the CJK fallback list and hardcoded wide font string should be kept in sync with the desktop equivalents in layout-serialization.ts and terminal-webview-html.ts.

Important Files Changed

Filename Overview
src/shared/east-asian-ambiguous-width.ts New file: EAW range table (with intentional PUA exclusion), binary search classifier, and resolveEastAsianAmbiguousCellWidth. Well-structured with correct range ordering.
src/shared/terminal-unicode-provider.ts Converts OrcaUnicodeProvider from a class to a plain-object factory (Chrome 74 / ES2019 compat), adds module-level EAW mode state read at measurement time, and correctly patches both wcwidth and charProperties for EAW widening including ZWJ paths.
src/shared/types.ts Adds TerminalEastAsianAmbiguousWidth type and optional terminalEastAsianAmbiguousWidth field to GlobalSettings; the type is also declared independently in east-asian-ambiguous-width.ts creating a duplication.
mobile/src/terminal/terminal-webview-eaw-injected.ts New file: injects IIFE-scoped EAW mode, CJK fallback list, and activateOrcaUnicodeOnTerm. Mobile CJK_AMBIGUOUS_FALLBACKS omits Meiryo present on the desktop list, and the wide font stack is hardcoded rather than derived from TERMINAL_FONT_FALLBACKS.
src/renderer/src/components/terminal-pane/layout-serialization.ts buildFontFamily gains CJK_AMBIGUOUS_FALLBACK_FONTS splice and BuildFontFamilyOptions; correctly adds lowerParts.push(lower) for within-fallback deduplication that was previously missing.
src/renderer/src/components/terminal-pane/terminal-appearance.ts Moves setTerminalEastAsianAmbiguousWidthMode and buildFontFamily call before the pane loop, eliminating the redundant per-pane global setter noted in previous review.
src/renderer/src/lib/pane-manager/pane-lifecycle.ts openTerminal now syncs EAW mode from the app store before activating the Orca provider, ensuring restore/replay paths bake correct cell widths from the first write.
src/main/daemon/headless-emulator.ts Constructor now applies EAW mode before loading Unicode11 and activating the Orca provider; HeadlessEmulatorOptions extracted to a separate file.
src/main/index.ts Seeds EAW mode at startup from the store and keeps it in sync on settings changes via onSettingsChanged; correctly ensures all HeadlessEmulator instances in the main process share the right mode.

Reviews (2): Last reviewed commit: "fix(terminal): merge main; exclude Nerd ..." | Re-trigger Greptile

Resolve conflict with upstream wheel-scroll inject (keep both EAW + wheel).

Review (Greptile stablyai#10078):
- Remove BMP PUA U+E000–U+F8FF from ambiguous ranges so Powerline/OMP
  icons stay single-cell in wide mode.
- Set process-wide EAW mode once before the appearance pane loop.
- Extract HeadlessEmulatorOptions to stay under max-lines.

Evidence: east-asian-ambiguous-width PUA tests + width suites.
@innocarpe

Copy link
Copy Markdown
Contributor Author

Sync update (46640c27f)

Merge + review follow-up (46640c27f)

  • Rebased/merged onto latest main — conflict was only mobile imports; kept both TERMINAL_EAW_JS and TERMINAL_WHEEL_SCROLL_JS.
  • Greptile P2: dropped BMP PUA U+E000–U+F8FF from the EAW ambiguous table so Nerd Font / Powerline / OMP icons stay single-cell in wide mode (with regression tests).
  • Greptile note: setTerminalEastAsianAmbiguousWidthMode now runs once before the appearance pane loop (not per pane).
  • Extracted HeadlessEmulatorOptions to stay under max-lines after the main merge.

Evidence: east-asian-ambiguous-width.test.ts PUA cases + existing width suites.

This branch has not been deployed

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

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature]: Opt-in "treat East Asian Ambiguous characters as double-width" for the terminal

4 participants