Skip to main content

IME and CJK Support

Monolex provides first-class support for CJK (Chinese, Japanese, Korean) input through the atomic-term module and Monokinetics principles.
+===============================================================================+
|  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

+-------------------------------------------------------------------------------+
|  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

+===============================================================================+
|                       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

+===============================================================================+
|                         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

+===============================================================================+
|                    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

+-------------------------------------------------------------------------------+
|                         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

+===============================================================================+
|  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

+-------------------------------------------------------------------------------+
|  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

+-------------------------------------------------------------------------------+
|  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

// 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

PlatformSupportAPI
macOSFullCarbon TIS API
WindowsPlaceholderGetKeyboardLayout() (planned)
LinuxPlaceholderIBus/Fcitx D-Bus (planned)

Summary

+===============================================================================+
|  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.

Next Steps