@mentionプロンプトでカンマキーによる選択を有効化

basecamp/lexxy

@mentionプロンプトが開いている状態でカンマキーを押すと、フォーカス中のオプションが選択されるようになりました。カンマはリテラルテキストとして保持されたまま、メンション補完が完了します。

背景

これまで、@mentionプロンプトが開いている状態でカンマキーを押すと、補完がキャンセルされてカンマがそのままテキストとして入力されていました。しかし、「@peter,」のように人名の後にカンマを続けて入力する記法はBasecampの既存動作として定着しており、ユーザーが期待する挙動と一致していませんでした。

PR本文によると、この変更はBasecampの既存動作に合わせるものと明示されています。エスケープキーによるキャンセルは既に実装されていたため、カンマキーの処理を同じキーダウンハンドラに追加する形で自然に実装できる状態でした。

技術的な変更

src/elements/prompt.jsLexicalPromptElement クラスにカンマキーのキーダウンハンドラが追加されました。既存のEscapeハンドラと並置する形で実装されています。

変更前:

import { $createTextNode, $isTextNode, COMMAND_PRIORITY_CRITICAL, KEY_ARROW_DOWN_COMMAND, ... } from "lexical"

変更後:

import { $createTextNode, $getSelection, $isRangeSelection, $isTextNode, COMMAND_PRIORITY_CRITICAL, KEY_ARROW_DOWN_COMMAND, ... } from "lexical"

importに $getSelection$isRangeSelection が追加され、カンマ入力後のテキスト挿入にLexicalのSelection APIを使用しています。

カンマキーの処理は以下のように実装されました:

} else if (event.key === ",") {
  event.preventDefault()
  event.stopPropagation()
  this.#optionWasSelected()
  this.#editor.update(() => {
    const selection = $getSelection()
    if ($isRangeSelection(selection)) {
      selection.insertText(",")
    }
  })
}

event.preventDefault() でブラウザのデフォルト入力を抑止した上で、#optionWasSelected() でメンション補完を確定し、続けて selection.insertText(",") でカンマをエディタに挿入しています。これにより、「選択の確定」と「カンマの保持」が順序保証された形で実行されます。

また、app/assets/javascript/lexxy.js には #getNextNodeFromTextEnd#getPreviousNodeFromTextStart のバグ修正も含まれています。instanceof Fi による型チェックが Li(nextSibling) 関数呼び出しに置き換えられ、兄弟ノードがnullでない場合に null を返す処理が追加されました。#getPreviousNodeFromTextStart では parent.getPreviousSibling()parent ? parent.getPreviousSibling() : null に修正され、nullアクセスを防ぐ安全策が追加されています。

さらに、Contents#insertDOM#unwrapPlaceholderAnchors 呼び出しが追加されています。レンダリング済みビューからコピーされたコンテンツに含まれる <a href="#"> のような意味のないアンカーをアンラップし、テキストコンテンツをプレーンテキストとして貼り付けられるようにする処理です。

設計判断

event.preventDefault()#optionWasSelected()selection.insertText(",") の順序 が重要な設計ポイントです。ブラウザのデフォルト入力を先に止め、メンション確定の後でLexicalのAPIを通じてカンマを挿入することで、エディタの状態管理をLexicalのトランザクションに委ねています。直接DOMに書き込む代わりにLexical APIを使う一貫した方針が維持されています。

Enter・Tab・Spaceキーが既にLexical Commandsで処理されているのに対し、カンマキーはEscapeキーと同じネイティブキーダウンイベントのハンドラで処理されています。PR内の既存コメント「Arrow keys are now handled via Lexical commands with HIGH priority」が示すように、コマンドシステムを通じた処理とネイティブイベント処理が共存する設計になっており、カンマはEscapeと同様にポップオーバー層のUIロジックとして扱われています。

まとめ

カンマキーハンドラの追加という最小限の変更で、Basecampの既存UXに合わせた@mention確定フローが実現されました。event.preventDefault() でデフォルト入力を制御しつつLexical APIでカンマを再挿入するパターンは、エディタの状態管理をフレームワークに一本化する設計原則の適用例として参照できます。

記事メタデータ

Generated by:
Claude Sonnet 4.6 for DiffDaily
LLM Trace:
6a4412dc

この記事は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)やGitHubのPRリンク記法([PR #847](URL))が正しく使用されており、可読性を高めています。

対象読者への適合性 ✓ PASS

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

LexicalのAPIやキーダウンイベントの処理など、専門知識を持つエンジニアを対象とした適切な技術レベルと表現で記述されています。

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

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

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

Diff内容との照合 ✓ PASS

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

`src/elements/prompt.js`の主要な変更だけでなく、`app/assets/javascript/lexxy.js`に含まれる関連修正(バグ修正やヘルパーメソッド追加)もDiffから正確に抽出し、解説できています。コード引用はDiffと一致しています。

技術用語の正確性 ✓ PASS

技術用語の正確な使用

`Lexical API`, `キーダウンハンドラ`, `event.preventDefault()`, `selection.insertText()`など、関連する技術用語を正確かつ適切に使用しています。

説明の技術的正確性 ✓ PASS

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

コード変更の意図と動作(メンション確定とカンマ挿入の順序保証など)が、Diffの内容に基づき、技術的に正確かつ論理的に説明されています。

事実の突合 ✓ PASS

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

記事内のすべての主張は、PRのTitle, Description, Diffの内容によって裏付けられており、ハルシネーション(捏造)は見られません。特に、Descriptionにない`lexxy.js`の変更点をDiffから拾って解説している点は優れています。

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

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

PR番号(#847)、ファイルパス、メソッド名などの固有名詞はすべて正確に記載されています。

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

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

記事タイトル「@mentionプロンプトでカンマキーによる選択を有効化」は、PRの主題「Accept @mention prompt selection on comma」を的確に要約しており、記事内容との整合性も取れています。

外部知識の正確性 ✓ PASS

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

PR情報に記載のない外部知識(バージョン情報、サポート状況など)の持ち込みはなく、提供された情報源に忠実です。

時間表現の正確性 ✓ PASS

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

「〜ようになりました」「これまで〜でした」といった時間に関する表現は、変更の前後関係を正しく反映しており、適切です。