IME and CJK Support
Monolex provides first-class support for CJK (Chinese, Japanese, Korean) input through the atomic-term module and Monokinetics principles.Copy
+===============================================================================+
| IME/CJK SUPPORT IN MONOLEX |
+===============================================================================+
| |
| 1. ATOMIC-TERM IME |
| Real-time input source detection via macOS Carbon API |
| Composition state handling for CJK input methods |
| |
| 2. MONOKINETICS CJK GUARANTEE |
| Every Asian character occupies exactly 2 cells |
| Enforced through VTE flags at the parser level |
| |
+===============================================================================+
The CJK Problem in Terminals
Copy
+-------------------------------------------------------------------------------+
| PROBLEM 1: INPUT (IME Composition) |
+-------------------------------------------------------------------------------+
| |
| User types: h -> a -> n (in Korean IME) |
| |
| WITHOUT proper handling: |
| Each keystroke sent immediately to PTY -> Garbage characters |
| |
| WITH proper handling: |
| Buffer until composition complete -> Only final text sent |
| |
+-------------------------------------------------------------------------------+
+-------------------------------------------------------------------------------+
| PROBLEM 2: DISPLAY (Character Width) |
+-------------------------------------------------------------------------------+
| |
| WITHOUT width guarantee: |
| Different fonts = different widths -> ASCII art breaks |
| |
| WITH Monokinetics guarantee: |
| CJK = exactly 2 cells (always) -> Perfect alignment |
| |
+-------------------------------------------------------------------------------+
Atomic-Term IME Architecture
Copy
+===============================================================================+
| ATOMIC-TERM IME ARCHITECTURE |
+===============================================================================+
| |
| macOS System |
| +-----------------------------------------------------------------------+ |
| | Input Source: Dubeolsik (Korean) | |
| | ID: com.apple.inputmethod.Korean.2SetKorean | |
| +-----------------------------------------------------------------------+ |
| | |
| | Carbon API (TIS) |
| v |
| +-----------------------------------------------------------------------+ |
| | Rust Backend (atomic_term_ime.rs) | |
| | | |
| | TISCopyCurrentKeyboardInputSource() | |
| | TISGetInputSourceProperty(source, key) | |
| | | |
| | Result: InputSourceInfo { | |
| | source_id, language, is_cjk, name, needs_composition | |
| | } | |
| +-----------------------------------------------------------------------+ |
| | |
| | Tauri IPC / Event |
| v |
| +-----------------------------------------------------------------------+ |
| | Frontend (ime-input-layer.ts) | |
| | | |
| | if (inputSource.is_cjk) handleCJKInput(); | |
| | else writeToTerminal(event.key); | |
| +-----------------------------------------------------------------------+ |
| |
+===============================================================================+
IME Composition States
Copy
+===============================================================================+
| IME COMPOSITION STATES |
+===============================================================================+
| |
| Korean Input Example: |
| +-----------------------------------------------------------------------+ |
| | Keystroke Composing Buffer Committed State | |
| | --------- ---------------- --------- --------- | |
| | h (jamo) (first jamo) "" composing | |
| | a (syllable) "" composing | |
| | n (syllable) "" composing | |
| | Space "" "han " committed | |
| +-----------------------------------------------------------------------+ |
| |
| Japanese Input Example: |
| +-----------------------------------------------------------------------+ |
| | n -> i -> h -> o -> n -> g -> o | |
| | romaji -> hiragana -> conversion -> kanji -> commit | |
| +-----------------------------------------------------------------------+ |
| |
| Frontend Handling: |
| +-----------------------------------------------------------------------+ |
| | compositionstart -> Start buffering | |
| | compositionupdate -> Update preview | |
| | compositionend -> Flush to PTY | |
| +-----------------------------------------------------------------------+ |
| |
+===============================================================================+
Input Source Change Detection
Copy
+===============================================================================+
| INPUT SOURCE CHANGE MONITORING |
+===============================================================================+
| |
| Notification: "com.apple.Carbon.TISNotifySelectedKeyboardInputSourceChanged"|
| |
| User Switching Methods: |
| +-----------------------------------------------------------------------+ |
| | Cmd + Space -> Switch to next input source | |
| | Caps Lock -> English/Korean toggle (if configured) | |
| | Globe Key -> Input source selection popup | |
| +-----------------------------------------------------------------------+ |
| |
| Flow: User switches -> macOS notifies -> Monolex callback -> Frontend |
| |
+===============================================================================+
CJK Detection Logic
Copy
+-------------------------------------------------------------------------------+
| CJK DETECTION LOGIC |
+-------------------------------------------------------------------------------+
| |
| By language code: |
| "ko" | "ja" | "zh" | "zh-Hans" | "zh-Hant" |
| |
| Or by source ID keywords: |
| "Korean" | "Japanese" | "Chinese" | "Pinyin" | "Hiragana" | "Katakana" |
| |
+-------------------------------------------------------------------------------+
Monokinetics CJK Guarantee
Copy
+===============================================================================+
| MONOKINETICS TENET 1: CJK DOUBLE-WIDTH GUARANTEE |
+===============================================================================+
| |
| "Every ASCII character is 1 cell wide. |
| Every CJK character is exactly 2 cells wide. |
| Always. Predictably." |
| |
| +---+---+---+---+---+---+-------+-------+ |
| | H | e | l | l | o | | CJK-1 | CJK-2 | |
| +---+---+---+---+---+---+-------+-------+ |
| 1 1 1 1 1 1 2 2 |
| |
+===============================================================================+
VTE Width Flags
Copy
+-------------------------------------------------------------------------------+
| VTE WIDTH FLAGS |
+-------------------------------------------------------------------------------+
| |
| Flag Meaning Width |
| --------------------------- ------------------------- ------ |
| WIDE_CHAR CJK/emoji (2 cols) 2 |
| WIDE_CHAR_SPACER Trailing empty cell 0 |
| LEADING_WIDE_CHAR_SPACER Leading empty cell 0 |
| (none) Normal ASCII/Latin 1 |
| |
| Decision Tree: |
| Cell -> WIDE_CHAR_SPACER? -> width=0 (skip) |
| -> WIDE_CHAR? -> width=2 (CJK) |
| -> else -> Unicode width lookup (usually 1) |
| |
+-------------------------------------------------------------------------------+
Rendering Flow
Copy
+-------------------------------------------------------------------------------+
| CJK RENDERING FLOW |
+-------------------------------------------------------------------------------+
| |
| Input: CJK from IME -> VTE Parser -> Grid: [CJK|spacer|CJK|spacer] |
| |
| xterm.js renders 4 columns (2 visible + 2 spacers) |
| |
| Screen: [CJK ][CJK ] <-- Each takes 2-cell space |
| |
+-------------------------------------------------------------------------------+
Frontend Usage
Copy
// Event-based approach (recommended)
await invoke('atomic_term_start_ime_watcher');
await listen('atomic-term-ime-changed', (event) => {
const info = event.payload;
if (info.is_cjk) enableCJKMode();
else disableCJKMode();
});
// Polling approach
const info = await invoke('atomic_term_get_input_source');
if (info.is_cjk) enableCJKMode();
Available Commands
atomic_term_get_input_source
Get current input source info
atomic_term_is_cjk_input
Quick CJK check (boolean)
atomic_term_start_ime_watcher
Start monitoring changes
atomic_term_stop_ime_watcher
Stop monitoring
Platform Support
| Platform | Support | API |
|---|---|---|
| macOS | Full | Carbon TIS API |
| Windows | Placeholder | GetKeyboardLayout() (planned) |
| Linux | Placeholder | IBus/Fcitx D-Bus (planned) |
Summary
Copy
+===============================================================================+
| IME/CJK SUPPORT SUMMARY |
+===============================================================================+
| |
| WHAT: Complete CJK input and display support |
| HOW: Carbon API + VTE flags + Composition events |
| WHY: Perfect CJK experience for Korean/Japanese/Chinese users |
| |
| Key Benefits: |
| * Real-time IME detection (no configuration needed) |
| * Proper composition handling (no garbage characters) |
| * Guaranteed double-width (no alignment issues) |
| * Works with all CJK input methods |
| |
+===============================================================================+
Zero configuration required. Monolex automatically detects your input method and handles CJK input correctly. Just switch to your preferred keyboard and start typing.