AndroidのソフトキーボードでプロンプトアイテムをSpaceキーで選択できない問題を修正

basecamp/lexxy

Androidのソフトキーボードが KEY_SPACE_COMMAND を発火しないため、プロンプトのアイテム選択がスペースキーで機能しなかった問題を修正しました。INPUT_COMMAND を新たにリッスンすることで、Android固有のイベント経路を補完しています。

背景

Lexicalエディタのプロンプト機能は、スペースキーによるアイテム選択(#doesSpaceSelect)を KEY_SPACE_COMMAND のリスナーで実装していました。しかしAndroidのソフトキーボードはデスクトップとは異なるイベント経路をたどるため、この実装ではスペース入力を検知できませんでした。

Androidのソフトキーボードでスペースを入力すると、KEY_SPACE_COMMAND の代わりに CONTROLLED_TEXT_INSERTION_COMMANDINPUT_COMMANDdata=" ")の組み合わせが発火します。Lexicalはこの INPUT_COMMAND をネイティブの input イベントから生成しますが、従来のプロンプト実装はこのコマンドを監視していなかったため、Androidではスペースによる選択が無効でした。

技術的な変更

src/elements/prompt.jsINPUT_COMMAND のリスナー登録と、対応するハンドラメソッドが追加されました。

KEY_SPACE_COMMAND を監視するブロックに、INPUT_COMMAND のリスナーが並列で追加されています。どちらも #doesSpaceSelect フラグが有効な場合にのみ登録されるため、スペース選択を使わないプロンプトへの影響はありません。

変更前:

if (this.#doesSpaceSelect) {
  this.#popoverListeners.track(this.#editor.registerCommand(KEY_SPACE_COMMAND, this.#handleSelectedOption.bind(this), COMMAND_PRIORITY_CRITICAL))
}

変更後:

if (this.#doesSpaceSelect) {
  this.#popoverListeners.track(this.#editor.registerCommand(KEY_SPACE_COMMAND, this.#handleSelectedOption.bind(this), COMMAND_PRIORITY_CRITICAL))
  this.#popoverListeners.track(this.#editor.registerCommand(INPUT_COMMAND, this.#handleInputCommand.bind(this), COMMAND_PRIORITY_CRITICAL))
}

追加された #handleInputCommand は、inputType === "insertText" かつ data === " " の場合にのみ既存の #handleSelectedOption へ処理を委譲します。

// Android Mobile keyboard doesn't trigger KEY_SPACE_COMMAND
#handleInputCommand(event) {
  if (event.inputType === "insertText" && event.data === " ") return this.#handleSelectedOption(event)
}

この条件分岐により、スペース以外の insertText イベント(通常の文字入力)や、deleteContentBackward などの他の inputType は素通りし、誤検知のリスクを最小化しています。

合わせて追加されたブラウザテスト test/browser/tests/prompts/space_selection_via_input_event.test.js では、KEY_SPACE_COMMAND を意図的に回避するために InputEvent を直接 dispatchEvent でシミュレートしています。テストコメントにも「Spaceキーを押すと KEY_SPACE_COMMAND も発火してリグレッションを隠す可能性があるため避けた」と明記されており、Androidの実際のイベント経路を再現した意図的な設計です。

設計判断

既存の #handleSelectedOption を再利用し、ハンドラの分岐を最薄に保つ設計が採用されました。

#handleInputCommand は条件判定のみを担い、実際の選択ロジックは既存の #handleSelectedOption にそのまま委譲しています。これにより、アイテム選択のコアロジックを一箇所に保ちつつ、Android固有のイベント経路への対応を独立したメソッドとして分離しています。また、リスナーの登録・解除を管理する #popoverListeners の仕組みをそのまま活用しており、既存のライフサイクル管理に追加コストをかけていません。

まとめ

本PRは、プラットフォーム固有のイベントモデルの差異を、最小限のコード追加で吸収した修正です。INPUT_COMMAND のフィルタリングを薄いハンドラに閉じ込めることで、デスクトップとAndroidの両経路で同一の選択ロジックが動作するようになりました。

記事メタデータ

Generated by:
Claude Sonnet 4.6 for DiffDaily
LLM Trace:
ece07551

この記事はAIによって自動生成されています。内容の正確性については、必ずソースコードやPRを確認してください。

品質レビュー結果

Review Status:
承認済み
Review Count:
1回
Reviewed by:
Gemini 2.5 Pro for DiffDaily

Review Criteria:

記事構成 ✓ PASS

Title, Context, Technical Detailの存在と明確さ

「リード文(総論)→背景・技術詳細・設計判断(各論)→まとめ(結論)」という構成が明確に守られており、非常に分かりやすい記事構造になっています。

カスタムMarkdown構文 ✓ PASS

シンタックスハイライト・GitHubリンク記法の正確性

ファイル名付きシンタックスハイライト(```javascript:src/elements/prompt.js)とPR番号のリンク記法([PR #1064](URL))が正しく使用されています。

対象読者への適合性 ✓ PASS

エンジニア向けの適切な技術レベルと表現

LexicalのコマンドシステムやJavaScriptのイベントモデルに関する知識を前提としており、専門知識を持つエンジニアという対象読者に適合した内容です。

パラグラフ・ライティング ✓ PASS

トピックセンテンス・1段落1トピック・段落長

各セクションが総論→各論の構成になっており、各段落がトピックセンテンスで始まるなど、パラグラフ・ライティングの原則が守られています。可読性が非常に高いです。

Diff内容との照合 ✓ PASS

コードブロックとDiff内容の一致

記事内のコードブロックは、提供されたDiffの内容(リスナーの追加、ハンドラメソッドの実装)を正確に反映しています。テストコードの意図に関する説明も、Diff内のコメントと一致しています。

技術用語の正確性 ✓ PASS

技術用語の正確な使用

「KEY_SPACE_COMMAND」「INPUT_COMMAND」「dispatchEvent」などの技術用語が、PR情報や文脈に沿って正確に使用されています。

説明の技術的正確性 ✓ PASS

技術的主張の正確性と論理性

Androidのイベント発火シーケンス(CONTROLLED_TEXT_INSERTION_COMMAND, INPUT_COMMAND)に関する説明はPR Descriptionと一致しており、技術的に正確です。

事実の突合 ✓ PASS

PR情報による主張の裏付け(ハルシネーション検出)

記事内のすべての主張は、PRのDescription、Diffコード、テストコードの内容によって裏付けられています。「設計判断」セクションはコードからの妥当な解釈であり、ハルシネーションは見られません。

数値・固有名詞の確認 ✓ PASS

PR番号・コミットID・バージョン等の正確性

PR番号(#1064)やコード内の定数名が正確に記載されています。

タイトル・説明との一致 ✓ PASS

記事タイトル・説明とPR内容の一致

記事のタイトルは、PRのタイトル「Select prompt item on INPUT_COMMAND where `data=" "` on Android」の内容を、問題解決の観点から分かりやすく要約しており、主題は完全に一致しています。

外部知識の正確性 ✓ PASS

PRに記載のない外部知識(LTS、サポート状況など)の不使用

記事は提供されたPR情報のみに基づいており、バージョンサポート状況などのPRに記載のない外部知識の捏造はありません。

時間表現の正確性 ✓ PASS

時間表現がPR情報と一致しているか

「機能しなかった問題」「修正しました」など、変更の前後関係を示す時間表現は正確です。