カーソル位置がリンク内にある場合のURL挿入バグを修正
テキスト選択なし(collapsed selection)の状態でリンクを編集すると、URLがリンクテキスト内に挿入されてしまうバグが修正されました。カーソルが既存のリンクノード内にあるかどうかを判定する条件分岐を追加することで、意図した上書き編集が正しく機能するようになります。
背景
Lexicalエディタでは、テキストを選択せずにリンクを編集操作した場合、collapsed selection(カーソルのみの状態)として扱われます。この状態での挙動として、選択範囲がなければ新規のAutoLinkノードを生成してURLテキストを挿入するロジックが実装されていました。
しかし、このロジックはカーソルが既存のリンクノード内にある場合を考慮していませんでした。たとえば <a href="http://abc.com">Link</a> の中にカーソルを置いてURLを http://cde.com に変更しようとすると、$toggleLink によるhref更新ではなく新規AutoLinkノードの生成処理が走り、結果として <a href="http://cde.com">Lihttp://cde.comnk</a> のようにURLテキストがリンクのテキストノード内に埋め込まれてしまっていました。
技術的な変更
command_dispatcher.js の INSERT_LINK コマンドハンドラに、カーソルが LinkNode 内に存在するかを確認する判定が追加されました。
変更のために2つのインポートが追加されています。
import { $createAutoLinkNode, $toggleLink, LinkNode } from "@lexical/link"
import { $getNearestNodeOfType } from "@lexical/utils"
変更前:
if (selection.isCollapsed()) {
変更後:
const anchorNode = selection.anchor.getNode()
if (selection.isCollapsed() && !$getNearestNodeOfType(anchorNode, LinkNode)) {
$getNearestNodeOfType(anchorNode, LinkNode) は、アンカーノードから祖先方向に LinkNode を探索するユーティリティ関数です。カーソルがリンクノード内にある場合はこの関数が LinkNode を返すため、! による否定でAutoLinkノードの新規生成ブロックをスキップします。その結果、処理は後続の $toggleLink(url) に委譲され、既存リンクのhrefのみが正しく更新されます。
条件の変更は && による追加のガード節のみであり、collapsed selectionかつリンク外にある場合の既存の振る舞いは一切変わりません。
まとめ
本修正は、collapsed selectionの判定に「既存リンクノード内にいないこと」という文脈チェックを加えることで、編集モードと新規挿入モードを正しく分岐させたものです。わずか5行の追加ながら、Lexicalのノードツリー探索APIを適切に活用してエッジケースを的確に塞いでいます。