Content is user-generated and unverified.

React InkのIMEサポートと端末日本語入力の技術的詳細調査

React Inkのコンポジション イベント受信の技術的実現可能性

React Inkは現在、IME(Input Method Editor)のコンポジション イベントをサポートしていませんこれは根本的なアーキテクチャの制限によるものです。

Node.js readlineモジュールのIME処理能力

React InkのuseInputフックは、キーボード入力を文字単位で処理します:

javascript
const handleData = (data: string) => {
  const keypress = parseKeypress(data);
  let input = keypress.ctrl ? keypress.name : keypress.sequence;
  inputHandler(input, key);
};

主な制限事項:

  • コンポジション イベント処理なし:入力は即座に文字単位で処理される
  • Rawモード依存setRawMode(true)により端末の行編集をバイパス
  • IMEセッション認識なし:コンポジションと最終入力を区別できない
  • 中間状態追跡なし:変換中の文字列状態を保持できない

Rawモードの制限と回避策

なぜRawモードがコンポジション イベントを妨げるのか:

  1. 端末ライン規律のバイパス:RawモードはIME処理が通常行われる端末のライン規律層を回避
  2. 即時文字配信:文字はコンポジション完了前にアプリケーションに配信される
  3. 端末IME統合なし:Rawモードのアプリケーションは端末のIME状態やコンポジション バッファにアクセスできない

技術的な回避策の可能性(すべて非常に複雑):

javascript
// 理論的なアプローチ - 現在は不可能
if (isCompositionActive()) {
  setRawMode(false); // 端末コンポジションを許可
  waitForCompositionEnd();
} else {
  setRawMode(true); // 通常のキー処理
}

既存の試みや提案

React InkのGitHubリポジトリには:

  • IME関連のissueなし
  • コンポジション イベント サポートのプルリクエストなし
  • コミュニティでの議論なし

これは、この技術的課題に対する認識が限定的であることを示しています。

端末IMEアーキテクチャの詳細分析

システムレベルでの端末エミュレータのIME処理

通信プロトコル階層:

アプリケーション層  │ アプリケーションがXIMライブラリ関数を呼び出す
インターフェース層  │ XIMライブラリがリクエスト/レスポンスを処理
プロトコル層       │ XIMプロトコル メッセージのフォーマット
トランスポート層    │ Xプロトコル、TCP/IP、Unix ソケット

RawモードとCookedモードのIME処理の違い

Cookedモード(正規モード):

  • 行バッファリング:EnterキーまでIMEが動作
  • 組み込み行編集:バックスペース、削除、Ctrl-Uが端末ドライバーで処理
  • シグナル処理:Ctrl-C、Ctrl-Zがカーネルで処理
  • エコー処理:文字が自動的に端末にエコー

Rawモード(非正規モード):

c
struct termios raw;
tcgetattr(STDIN_FILENO, &raw);
raw.c_lflag &= ~(ICANON | ECHO | ISIG | IEXTEN);
raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);

プリエディット テキスト処理メカニズム

XIM入力スタイル:

  • OverTheSpot:カーソル位置にプリエディット テキストを表示
  • Root:別のIMEウィンドウにプリエディット テキストを表示
  • OnTheSpot:アプリケーションのテキスト バッファに統合

VTE(Virtual Terminal Emulator)実装:

c
typedef struct {
    char *text;           // プリエディット文字列
    PangoAttrList *attrs; // テキスト属性
    int cursor_pos;       // プリエディット内のカーソル位置
} VteIMContext;

macOS端末エミュレータの日本語IMEサポート分析

各端末エミュレータの比較

機能Terminal.appiTerm2AlacrittyKittyWezTerm
基本IMEサポート✅ 良好✅ 優秀⚠️ 普通✅ 良好✅ 非常に良好
インライン コンポジション✅ 基本✅ 高度⚠️ 最近✅ あり✅ あり
プリエディット表示✅ 標準✅ 拡張⚠️ 基本✅ 良好✅ 良好
候補選択✅ 標準✅ 拡張❌ 限定的✅ 良好✅ 良好
設定オプション❌ なし⚠️ 限定的⚠️ 限定的⚠️ いくつか✅ 豊富
日本語キーボード サポート✅ ネイティブ✅ 優秀❌ 問題あり✅ 良好✅ 良好

主な調査結果

iTerm2:最も洗練された日本語入力体験を提供

  • 高度なインライン コンポジション
  • 拡張された候補選択
  • 「Treat ambiguous-width characters as double-width」設定

Alacritty:歴史的にIMEサポートが不十分

  • 長年のGitHub issue(#1101、#1613)
  • かな/英数キーが正しく登録されない
  • 最近のバージョンで改善中

WezTerm:豊富な設定オプション

lua
config.use_ime = true
config.macos_forward_to_ime_modifier_mask = 'SHIFT|CTRL'

Node.jsと端末アプリケーションのIMEソリューション

代替ライブラリ

Terminal-Kit

javascript
const termkit = require('terminal-kit');
const term = termkit.terminal;

term.inputField({
  echo: true,
  default: '',
  cancelable: true
}, function(error, input) {
  // 基本的な入力よりも良好なコンポジション イベント処理
  term.green("\nあなたの入力: '%s'\n", input);
});

Neo-Blessed(メンテナンスされたフォーク)

  • モダンなNode.jsサポート
  • 改善されたUnicode処理
  • バグ修正と機能強化

プラットフォーム固有のAPI活用

Windows(node-ffi-napiを使用)

javascript
const ffi = require('node-ffi-napi');
const user32 = ffi.Library('user32', {
  'ImmGetContext': ['pointer', ['pointer']],
  'ImmGetCompositionStringW': ['int', ['pointer', 'uint32', 'pointer', 'uint32']]
});

macOS(子プロセスを使用)

javascript
const { execSync } = require('child_process');

function getCurrentInputSource() {
  const result = execSync(`
    osascript -e 'tell application "System Events" 
      to return name of current keyboard layout'
  `).toString().trim();
  return result;
}

実装戦略

ハイブリッド アプローチ

javascript
const blessed = require('blessed');
const pty = require('node-pty');

class IMETerminal {
  constructor() {
    this.screen = blessed.screen({ 
      smartCSR: true, 
      fullUnicode: true 
    });
    
    this.pty = pty.spawn('bash', [], {
      env: { ...process.env, LANG: 'ja_JP.UTF-8' }
    });
  }
}

技術的実装の詳細

端末がRawモード アプリケーションの前にIMEイベントを傍受する方法

イベント フロー アーキテクチャ

キーボード ハードウェア
    ↓
カーネル入力サブシステム
    ↓
X11サーバー / Waylandコンポジター
    ↓
端末エミュレータ(xterm、VTEなど)
    ↓
XIMクライアント ライブラリ
    ↓
IMEサーバー(ibus、fcitxなど)
    ↓
IMEエンジン(pinyin、mozcなど)
    ↓
アプリケーション(shell、vimなど)

端末制御シーケンスによるコンポジション状態の取得可能性

現在の制限:カーソル位置(ESC[6n)やデバイス属性(ESC[0c)のような他の端末状態クエリとは異なり、** IMEコンポジション状態をクエリする標準化された制御シーケンスは存在しません**。

理論的な提案(未実装)

ESC[?1000$p  - IMEアクティブ状態のクエリ
ESC[?1001$p  - コンポジション文字列のクエリ
ESC[?1002$p  - 候補数のクエリ

文書化されていない実験的機能

Clear Code GLFW IME拡張

c
glfwSetPreeditCursorPos()
glfwGetPreeditCursorPos()
glfwSetPreeditString()

Windows Terminal IMEの改善

  • レガシーTSF実装の削除(40ファイル、5000行以上のコード)
  • conhostとWindows Terminal間でのIME実装の統一

結論と推奨事項

現状のまとめ

React Inkは根本的なアーキテクチャの制限により、IMEコンポジション イベントをサポートできませんこの制限は、React Inkの実装ではなく、Node.jsのRawモード端末制約に起因しています。

技術的実現可能性

  • 主要なアーキテクチャ変更なしに包括的なIMEサポートは実現可能性が低い
  • 大規模なプラットフォーム固有の開発端末エミュレータの協力が必要
  • React Inkの入力モデルに破壊的変更が必要

推奨される対策

  1. 即時の解決策:Terminal-KitまたはNeo-Blessedへの移行を検討
  2. プラットフォーム統合:node-ffi-napiを使用したシステムIME APIへの直接アクセス
  3. ハイブリッド アプローチ:バックエンドにnode-pty、フロントエンドにxterm.jsを使用
  4. 将来の監視:新しい端末プロトコルやNode.js機能の動向を注視

代替アプローチ

IMEサポートが重要なアプリケーションの場合:

  • ElectronによるWebベースのインターフェース
  • プラットフォーム固有のデスクトップ アプリケーション
  • 事前に構成されたテキスト入力を受け入れる端末アプリケーション
  • 複雑な入力シナリオ用の外部テキスト エディタとの統合

この分析により、React InkでのIMEコンポジション イベント サポートは、現在の端末およびNode.jsアーキテクチャの制約では簡単に克服できない重大な技術的障壁に直面していることが示されています。

Content is user-generated and unverified.
    React Ink IME Support: Technical Limitations and Japanese Input Solutions | Claude