カーソル位置がリンク内にある場合のURL挿入バグを修正

basecamp/lexxy

テキスト選択なし(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.jsINSERT_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を適切に活用してエッジケースを的確に塞いでいます。

記事メタデータ

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

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

品質レビュー結果

Review Status:
リトライ後承認
Review Count:
2回 (改善を経て承認)
Reviewed by:
Gemini 2.5 Pro for DiffDaily

Review Criteria:

記事構成 ✓ PASS

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

リード文(総論)→背景・技術詳細(各論)→まとめ(結論)という3部構成が明確に守られており、非常に理解しやすい記事構成です。

カスタムMarkdown構文 ⚠ WARNING

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

ファイル名付きシンタックスハイライトは正しく使用されていますが、GitHubのPRリンク記法がガイドラインの `[#123](URL)` 形式ではなく `[PR #980](URL)` となっています。内容理解に影響はありませんが、軽微な修正が推奨されます。

対象読者への適合性 ✓ PASS

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

「Lexical」「collapsed selection」「LinkNode」などの専門用語を前提として解説しており、専門知識を持つエンジニアという対象読者に完全に適合しています。

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

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

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

Diff内容との照合 ✓ PASS

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

Diffで示されたimport文の追加や条件分岐の変更が、コードブロック内で正確に引用・解説されています。

技術用語の正確性 ✓ PASS

技術用語の正確な使用

「$getNearestNodeOfType」「anchorNode」「LinkNode」など、Lexicalフレームワークに関する技術用語が文脈に沿って正しく使用されています。

説明の技術的正確性 ✓ PASS

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

「$getNearestNodeOfType」がどのように機能し、なぜこの変更でバグが修正されるのかというロジックが、技術的に正確かつ明快に説明されています。

事実の突合 ✓ PASS

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

記事で説明されているバグの具体例(Lihttp://cde.comnk)や修正の背景が、すべてPRのDescriptionと一致しており、ハルシネーションは検出されませんでした。

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

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

PR番号「#980」や、コード内で使用されている関数名、ファイルパスがすべて正確に記載されています。

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

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

記事のタイトル「カーソル位置がリンク内にある場合のURL挿入バグを修正」は、PRのタイトル「Fix linking for collapsed selection」の内容を的確に、より具体的に表現しています。

外部知識の正確性 ✓ PASS

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

記事の内容は提供されたPR情報に完全に準拠しており、バージョンサポート状況などの外部知識を持ち込んでいる箇所はありません。

時間表現の正確性 ✓ PASS

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

「〜でした」「〜が修正されました」といった過去形・完了形の表現が適切に使われており、PRで完了した修正であることが正しく伝わります。