Skip to content

fix(ios): make the IME composer reachable from the compact key bar - #217

Merged
tanlethanh merged 5 commits into
deltaqdev:mainfrom
hello532:fix/ios-composer-discoverability
Aug 26, 2026
Merged

tanlethanh merged 5 commits into
deltaqdev:mainfrom
hello532:fix/ios-composer-discoverability

Conversation

@hello532

Copy link
Copy Markdown
Contributor

Refs #214.

Correction to the issue

I filed #214 guessing the input surface never implemented the iOS UITextInput
marked-text protocol. Reading the code, that guess was wrong in an important way:
IME input already works on iOS. KeyboardSupporter.buildComposeRow creates a real
UITextField, so UIKit drives composition inside it natively — Stroke, Pinyin,
Handwriting included.

The real problem is that the composer cannot be found and, in the default layout, cannot
be reached at all:

  • the only entry point was a left swipe on the accessory bar — no button, no hint
  • handlePan opened with guard extended else { return }, so the swipe was inert unless
    the two-row keypad was already showing
  • extended defaults to false

On a fresh install the bar is compact, so the gesture does nothing and the composer is
unreachable. That presents exactly as "Chinese cannot be typed", which is how I
originally reported it.

Change

  • Add a ⌨ key to compactRow that calls setComposing(true, animated: true).
    It is dispatched locally in buttonTouchUpInside and returns before sendKey, so
    zedra:composer is never forwarded to Rust as a keystroke.
  • Drop the guard extended line in handlePan so the swipe works in both layouts.
    rebuildRows always builds the compose page, so nothing else gated this.
  • Carry an optional accessibilityLabel on KeySpec and apply it in buildRow, so the
    new key announces as "Open composer" rather than as a glyph. This mirrors the existing
    close.accessibilityLabel = "Close composer".
  • Update the file header comment, which still described the swipe as the only way in.

The extended rows are untouched: both are full at seven keys, and the swipe covers them.

Testing

swiftc -parse ios/Zedra/KeyboardSupporter.swift passes.

Not verified on a device or simulator. I have no iOS SDK on this machine
(xcrun --sdk iphoneos --show-sdk-path fails), so I could not build the app or confirm
the behaviour by hand, and CI only covers the Rust crates — nothing here is exercised by
it. The change is small and confined to one file, but it needs a real run before merge.
Two things worth checking:

  1. Tapping ⌨ on the compact bar opens the composer with the keyboard focused, and
    Chinese can be typed and sent.
  2. The swipe still behaves in the extended layout, and the eighth key does not crowd the
    compact row on smaller devices such as an iPhone SE.

Scope

This makes the existing composer usable. It does not add IME support to the terminal
surface itself — text typed straight into the terminal still bypasses composition,
because include/gpui_ios.h exposes only gpui_ios_handle_text_input(void*, void*),
which carries committed text and has no marked-text state in the ABI. Inline composition
in the terminal would mean extending that boundary plus the GPUI iOS platform layer in
the vendor/zed submodule (tanlethanh/zed@feat/gpui-mobile), which belongs in a
separate change. Happy to open an issue there if that is the direction you want.

Reported from iPhone, iOS 18.3.1, App Store build.

The composer is the only place an IME can compose on iOS, but it could
only be reached by a left swipe on the extended two-row keypad: handlePan
returned early unless `extended` was set, and no row carried a visible
control. `extended` defaults to false, so on the default compact bar the
composer was both unreachable and undiscoverable.

Add a labelled key that opens it directly, and drop the `extended` guard
so the swipe works in both layouts.

Refs deltaqdev#214

Co-Authored-By: GenericAgent <bot@gaagent.ai>
@tanlethanh

Copy link
Copy Markdown
Member

Hey thanks @hello532. This is useful. But I also think that the terminal input itself should support typing in chinese directly without going though a composer input. I guess it should be related to IME and Marked Text, and can you also help me check at it?

@hello532

hello532 commented Aug 25, 2026 via email

Copy link
Copy Markdown
Contributor Author

@tanlethanh

Copy link
Copy Markdown
Member

I can fix it, but I doesnt know Chinese so it takes time for me to test it

@hello532

hello532 commented Aug 25, 2026 via email

Copy link
Copy Markdown
Contributor Author

tanlethanh and others added 4 commits August 26, 2026 23:53
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Lhjd5ReMu7582tPZn1qnnY
Chinese Simplified Stroke commits a composition with unmarkText and never
sends insertText, so the terminal discarded the preedit instead of writing
it to the PTY. Commit uncommitted marked text on that path; dictation and
streamed previews keep the old cancel behavior.

The composition itself was also invisible: marked text lived only in the
synthetic text store, so a stroke phrase stayed hidden until punctuation
committed it. TerminalContent now carries the composition and the element
draws it at the cursor - tinted background, underline, and a caret at the
IME insertion point - wrapping at the right edge and never reaching the
PTY. Terminal is a model entity whose notify never reaches the view, so
repaints ride a new PreeditChanged event that fires only when the drawn
composition changes.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AQ2N7yhpDqPWR9wb9u2R8d
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AQ2N7yhpDqPWR9wb9u2R8d
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AQ2N7yhpDqPWR9wb9u2R8d
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