Skip to content

Fix keyboard shortcuts with CJK input sources - #1649

Merged
lawrencecchen merged 1 commit into
manaflow-ai:mainfrom
sumniy:fix/cjk-keyboard-shortcuts
Mar 20, 2026
Merged

lawrencecchen merged 1 commit into
manaflow-ai:mainfrom
sumniy:fix/cjk-keyboard-shortcuts

Conversation

@sumniy

@sumniy sumniy commented Mar 18, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • Fix keyboard shortcuts (Cmd+P, Cmd+B, Cmd+D, etc.) not working when Korean, Chinese, or Japanese input method is active
  • Add ASCII-capable input source fallback in KeyboardLayout.character() for CJK IMEs that lack kTISPropertyUnicodeKeyLayoutData
  • Update matchShortcut() guard to skip early-return when charactersIgnoringModifiers contains non-ASCII characters

Problem

When a CJK input source is active, event.charactersIgnoringModifiers returns non-Latin characters (e.g. Korean jamo "ㅔ" for the P key). The existing shortcut matching logic had two guards that treated these characters as valid Latin input, causing all fallback paths (layout-based and keyCode-based) to be blocked.

Solution

KeyboardLayout.swift: character(forKeyCode:modifierFlags:) now tries the current input source first, then falls back to TISCopyCurrentASCIICapableKeyboardInputSource() when no Unicode layout data is available. Extracted shared logic into characterFromInputSource() helper.

AppDelegate.swift: Added eventCharsAreASCII check so non-ASCII event characters don't trigger the early-return guard for Cmd+letter shortcuts. Added hasUsableEventChars so the ANSI keyCode fallback correctly activates for CJK input sources.

Scope

This covers shortcuts handled by handleCustomShortcut (local event monitor). Menu/SwiftUI .keyboardShortcut() shortcuts are not yet covered.

Test plan

  • Korean input (한글) + Cmd+P → command palette opens
  • Korean input + Cmd+B → sidebar toggles
  • Korean input + Cmd+D → split right
  • English input + all shortcuts → no regression
  • Dvorak layout + shortcuts → still layout-aware
  • Japanese/Chinese IME + Cmd shortcuts → work correctly

🤖 Generated with Claude Code


Summary by cubic

Fixes Command shortcuts (e.g., Cmd+P, Cmd+B, Cmd+D) not working when Korean, Chinese, or Japanese input sources are active. Adds an ASCII-capable layout fallback and smarter event-character checks so shortcuts work across layouts without regressions.

  • Bug Fixes
    • KeyboardLayout.character(...) now tries the current input source first and falls back to TISCopyCurrentASCIICapableKeyboardInputSource() when kTISPropertyUnicodeKeyLayoutData is missing; shared logic extracted to characterFromInputSource(...).
    • Updated shortcut matching in AppDelegate to ignore non-ASCII charactersIgnoringModifiers for Command-letter shortcuts and treat them as absent, enabling layout-based matching and ANSI keyCode fallback when needed.

Written for commit 19204c7. Summary will update on new commits.

Summary by CodeRabbit

  • Bug Fixes
    • Fixed command shortcuts falsely triggering when using non-Latin input sources (Korean, Chinese, Japanese)
    • Enhanced keyboard layout detection and fallback handling for improved international input method support

…se, Japanese)

When a non-Latin input source is active, event.charactersIgnoringModifiers
returns CJK characters that cannot match Latin shortcut keys. This adds
ASCII-capable input source fallback in KeyboardLayout and updates the
matchShortcut guard to skip early-return when event chars are non-ASCII.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@vercel

vercel Bot commented Mar 18, 2026

Copy link
Copy Markdown

@sumniy is attempting to deploy a commit to the Manaflow Team on Vercel.

A member of the Team first needs to authorize it.

@greptile-apps greptile-apps 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.

Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.

@coderabbitai

coderabbitai Bot commented Mar 18, 2026 •

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

The PR adds ASCII validation to command shortcuts to prevent non-Latin input sources from falsely triggering Latin-key shortcuts, and introduces a fallback mechanism in keyboard layout resolution that attempts ASCII-capable input sources when the current source lacks Unicode key layout data (e.g., CJK sources).

Changes

Cohort / File(s) Summary
Command Shortcut ASCII Validation
Sources/AppDelegate.swift
Adds ASCII character checks (eventCharsAreASCII, hasEventChars) to gate command shortcuts, preventing non-ASCII input sources from triggering Latin-key shortcuts while preserving layout-based matching for ASCII input.
Keyboard Layout Resolution Fallback
Sources/KeyboardLayout.swift
Introduces characterFromInputSource() helper function and modifies character(forKeyCode:modifierFlags:) to attempt character resolution from the current input source first, then fall back to ASCII-capable source if Unicode layout data is unavailable.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant AppDelegate as AppDelegate
    participant KeyboardLayout as KeyboardLayout
    participant InputSource as Input Source (Current)
    participant ASCIISource as Input Source (ASCII)

    User->>AppDelegate: Press key (non-ASCII source active)
    AppDelegate->>AppDelegate: Check eventCharsAreASCII
    alt Chars are ASCII
        AppDelegate->>KeyboardLayout: character(forKeyCode:modifierFlags:)
        KeyboardLayout->>InputSource: Try UCKeyTranslate with current source
        alt Unicode layout data exists
            InputSource-->>KeyboardLayout: Character resolved
        else No layout data (CJK source)
            KeyboardLayout->>ASCIISource: Fall back to ASCII source
            ASCIISource-->>KeyboardLayout: Character resolved
        end
        KeyboardLayout-->>AppDelegate: Character result
        AppDelegate->>AppDelegate: Route command shortcut
    else Chars are non-ASCII
        AppDelegate->>AppDelegate: Skip command shortcuts
    end
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related PRs

Poem

🐰 In keyboards where the characters dance,
We guard the shortcuts from the Asian chance,
When CJK sources dare to play,
ASCII fallback saves the day!
No false alarms, just proper flow—
Where every keycode's allowed to go. ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: fixing keyboard shortcuts when CJK input sources are active, which is the primary objective of the PR.
Description check ✅ Passed The PR description includes a comprehensive summary, problem explanation, solution details, scope, and test plan checklist. It follows the template structure with all critical sections filled out.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
📝 Coding Plan
  • Generate coding plan for human review comments

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 and usage tips.

Tip

You can enable review details to help with troubleshooting, context usage and more.

Enable the reviews.review_details setting to include review details such as the model used, the time taken for each step and more in the review comments.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
Sources/KeyboardLayout.swift (1)

24-34: Narrow fallback to the intended missing-layout case.

At Line 24 through Line 34, fallback runs whenever characterFromInputSource(...) returns nil, including non-layout translation failures. That’s a bit broader than the comment intent and can mask true translation errors.

♻️ Proposed refinement
-        if let source = TISCopyCurrentKeyboardInputSource()?.takeRetainedValue(),
-           let result = characterFromInputSource(source, forKeyCode: keyCode, modifierFlags: modifierFlags) {
-            return result
-        }
+        if let source = TISCopyCurrentKeyboardInputSource()?.takeRetainedValue() {
+            let hasUnicodeLayoutData =
+                TISGetInputSourceProperty(source, kTISPropertyUnicodeKeyLayoutData) != nil
+            if let result = characterFromInputSource(source, forKeyCode: keyCode, modifierFlags: modifierFlags) {
+                return result
+            }
+            if hasUnicodeLayoutData {
+                return nil
+            }
+        }
         // Current input source has no Unicode layout data (e.g. Korean, Chinese, Japanese IME).
         // Fall back to the ASCII-capable source so shortcut matching still works.
         if let asciiSource = TISCopyCurrentASCIICapableKeyboardInputSource()?.takeRetainedValue(),
            let result = characterFromInputSource(asciiSource, forKeyCode: keyCode, modifierFlags: modifierFlags) {
             return result
         }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Sources/KeyboardLayout.swift` around lines 24 - 34, The current fallback to
the ASCII-capable source runs anytime characterFromInputSource(...) returns nil,
which masks real translation failures; change the logic in the block that calls
TISCopyCurrentKeyboardInputSource() and characterFromInputSource(...) so you
only attempt the ASCII fallback when the original input source truly lacks
Unicode layout data (check the input source's kTISPropertyUnicodeKeyLayoutData /
kTISPropertyInputSourceType via TISGetInputSourceProperty on the source returned
by TISCopyCurrentKeyboardInputSource()), otherwise propagate the nil result from
characterFromInputSource (i.e., do not call
TISCopyCurrentASCIICapableKeyboardInputSource() if the current source has layout
data but translation failed).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@Sources/KeyboardLayout.swift`:
- Around line 24-34: The current fallback to the ASCII-capable source runs
anytime characterFromInputSource(...) returns nil, which masks real translation
failures; change the logic in the block that calls
TISCopyCurrentKeyboardInputSource() and characterFromInputSource(...) so you
only attempt the ASCII fallback when the original input source truly lacks
Unicode layout data (check the input source's kTISPropertyUnicodeKeyLayoutData /
kTISPropertyInputSourceType via TISGetInputSourceProperty on the source returned
by TISCopyCurrentKeyboardInputSource()), otherwise propagate the nil result from
characterFromInputSource (i.e., do not call
TISCopyCurrentASCIICapableKeyboardInputSource() if the current source has layout
data but translation failed).

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: f2dac0d6-0d11-441c-910b-543415ba59d4

📥 Commits

Reviewing files that changed from the base of the PR and between 1fabe9f and 19204c7.

📒 Files selected for processing (2)
  • Sources/AppDelegate.swift
  • Sources/KeyboardLayout.swift

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

1 issue found across 2 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="Sources/KeyboardLayout.swift">

<violation number="1" location="Sources/KeyboardLayout.swift:30">
P2: Fallback uses ASCII-capable *input source* API, but translation requires Unicode keyboard layout data; use ASCII-capable keyboard *layout* API to avoid nil on IME setups.</violation>
</file>

Since this is your first cubic review, here's how it works:

  • cubic automatically reviews your code and comments on bugs and improvements
  • Teach cubic by replying to its comments. cubic learns from your replies and gets better over time
  • Add one-off context when rerunning by tagging @cubic-dev-ai with guidance or docs links (including llms.txt)
  • Ask questions if you need clarification on any suggestion

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

}
// Current input source has no Unicode layout data (e.g. Korean, Chinese, Japanese IME).
// Fall back to the ASCII-capable source so shortcut matching still works.
if let asciiSource = TISCopyCurrentASCIICapableKeyboardInputSource()?.takeRetainedValue(),

@cubic-dev-ai cubic-dev-ai Bot Mar 18, 2026 •

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: Fallback uses ASCII-capable input source API, but translation requires Unicode keyboard layout data; use ASCII-capable keyboard layout API to avoid nil on IME setups.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At Sources/KeyboardLayout.swift, line 30:

<comment>Fallback uses ASCII-capable *input source* API, but translation requires Unicode keyboard layout data; use ASCII-capable keyboard *layout* API to avoid nil on IME setups.</comment>

<file context>
@@ -15,12 +15,31 @@ class KeyboardLayout {
+        }
+        // Current input source has no Unicode layout data (e.g. Korean, Chinese, Japanese IME).
+        // Fall back to the ASCII-capable source so shortcut matching still works.
+        if let asciiSource = TISCopyCurrentASCIICapableKeyboardInputSource()?.takeRetainedValue(),
+           let result = characterFromInputSource(asciiSource, forKeyCode: keyCode, modifierFlags: modifierFlags) {
+            return result
</file context>
Suggested change
if let asciiSource = TISCopyCurrentASCIICapableKeyboardInputSource()?.takeRetainedValue(),
if let asciiSource = TISCopyCurrentASCIICapableKeyboardLayoutInputSource()?.takeRetainedValue(),
Fix with Cubic

@lawrencecchen
lawrencecchen merged commit 8b49d66 into manaflow-ai:main Mar 20, 2026
3 of 4 checks passed
@lawrencecchen

Copy link
Copy Markdown
Contributor

Thank you for the contribution!

bn-l pushed a commit to bn-l/cmux that referenced this pull request Apr 3, 2026
…se, Japanese) (manaflow-ai#1649)

When a non-Latin input source is active, event.charactersIgnoringModifiers
returns CJK characters that cannot match Latin shortcut keys. This adds
ASCII-capable input source fallback in KeyboardLayout and updates the
matchShortcut guard to skip early-return when event chars are non-ASCII.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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