下矢印キーが添付ファイルに飛んでしまうバグの修正
ソフト改行(<br>)を含む段落の途中でカーソルが下矢印キーを押したとき、次のテキスト行ではなく添付ファイルが選択されてしまう問題を修正しました。オフセットガードを追加することで、テキストの境界にいるときだけ添付ファイルへのカーソル移動をインターセプトするように改善されています。
背景
Lexxy エディタでは、添付ファイル(ActionTextAttachment)の前後でカーソルが移動するとき、Lexicalのデフォルト動作ではなく独自の Selection クラスが次・前のノードを決定します。この仕組みは、添付ファイルノードへのフォーカス移動を正確に制御するために設計されています。
しかし、段落内に LineBreakNode(<br>)が存在する場合、カーソルがテキストの途中にあっても topLevelNodeAfterCursor(下矢印)や topLevelNodeBeforeCursor(上矢印)がインターセプトしてしまい、まだナビゲートすべきテキスト行が残っているにもかかわらず添付ファイルを選択するという誤動作が発生していました。
技術的な変更
src/editor/selection.js にオフセットガードを2箇所追加することで、テキストノードの実際の境界でのみ添付ファイルへのナビゲーションをインターセプトするように変更されました。
変更前(topLevelNodeAfterCursor):
if ($isTextNode(anchorNode)) {
return this.#getNextNodeFromTextEnd(anchorNode)
}
変更後(topLevelNodeAfterCursor):
if ($isTextNode(anchorNode)) {
if (offset < anchorNode.getTextContentSize()) return null
return this.#getNextNodeFromTextEnd(anchorNode)
}
変更前(topLevelNodeBeforeCursor):
if ($isTextNode(anchorNode)) {
return this.#getPreviousNodeFromTextStart(anchorNode)
}
変更後(topLevelNodeBeforeCursor):
if ($isTextNode(anchorNode)) {
if (offset > 0) return null
return this.#getPreviousNodeFromTextStart(anchorNode)
}
下矢印の場合、offset < anchorNode.getTextContentSize() が真であればカーソルはテキストノードの末尾に到達していないため null を返してLexicalのデフォルトのカーソル移動に委ねます。上矢印の場合、offset > 0 が真であればカーソルはテキストノードの先頭にないため同様に null を返します。これにより #getNextNodeFromTextEnd と #getPreviousNodeFromTextStart は、LineBreakNode のような非デコレータの兄弟ノードをスキップせず、テキストの真の境界でのみ呼ばれるようになります。
設計判断
Lexicalのデフォルト動作への委譲という方針が採用されました。
独自の添付ファイルナビゲーションロジックの適用範囲を最小化し、テキストノードの途中では Lexical の組み込みカーソル移動をそのまま使う設計です。オフセットチェックを Selection クラスの入口に集約することで、#getNextNodeFromTextEnd や #getPreviousNodeFromTextStart の内部実装を変えずに問題を解消しています。
テストも刷新されており、変更前は「フォーマット済みテキスト(イタリック体)を右矢印で通過しても添付ファイルに飛ばない」ことを確認していたのに対し、変更後は「ソフト改行を含む段落で下矢印を押すと次のテキスト行に移動し、添付ファイルが選択されない」という、実際のバグシナリオに直接対応するテストケースに置き換えられています。
まとめ
本修正は、テキスト境界の判定をオフセットによって厳密化することで、添付ファイルへのカーソルジャンプを真に必要な場面のみに限定した変更です。変更は2行の追加のみと最小限でありながら、段落内に LineBreakNode が存在するあらゆるケースでの誤動作を防ぐ効果をもたらします。