選択範囲へのURL貼り付けで複数のリンクノードを統合

basecamp/lexxy

Lexxyのリンク生成ロジックが改善され、複数の単語にまたがる選択範囲へURLを貼り付けた際に、単一の LinkNode が生成されるようになりました。従来は同じURLを持つ隣接リンクが複数生成される問題がありましたが、既存リンクをクリアしてから新規リンクを適用する方式に変更されています。

背景

複数の単語や既存のリンクを含む選択範囲にURLを貼り付けると、Lexicalは各テキストセグメントごとに個別の LinkNode を生成していました。#594 で報告されたこの問題により、<a href="https://example.com">link1</a><a href="https://example.com"> </a><a href="https://example.com">link2</a> のような不要な分割が発生していました。

この問題はリスト項目や他のブロック要素をまたぐ選択範囲でも同様に発生し、本来は <a href="https://example.com">link1 link2</a> のような単一リンクになるべきところが、複数のリンクノードに分割されていました。

技術的な変更

createLinkWithSelectedText メソッドに $toggleLink(null) の呼び出しが追加され、新しいURLを適用する前に既存のリンクをクリアするようになりました。

変更前:

createeLinkWithSelectedText(url) {
  if (!this.hasSelectedText()) return

  this.editor.update(() => {
    $toggleLink(url)
  })
}

変更後:

createLinkWithSelectedText(url) {
  if (!this.hasSelectedText()) return

  this.editor.update(() => {
    $toggleLink(null)
    $toggleLink(url)
  })
}

$toggleLink(null) により、選択範囲内の既存リンク境界がすべて解除されます。これによりLexicalは選択範囲を「リンク境界のないフラットなテキストノード」として認識し、続く $toggleLink(url) が選択範囲全体に対して単一の LinkNode を生成します。

テストケースでは、"Hello"という単語をリンク化した後、スペースと"everyone"を追加し、全体を選択して同じURLを貼り付けるシナリオが追加されました。

test "merge adjacent links when pasting URL over multiple words" do
  find_editor.send "Hello"
  find_editor.select "Hello"
  find_editor.paste "https://37signals.com"

  find_editor.send :arrow_right
  find_editor.send " everyone"

  find_editor.select_all
  find_editor.paste "https://37signals.com"

  assert_editor_html do
    assert_selector %(a[href="https://37signals.com"]), text: "Hello everyone", count: 1
    assert_no_selector %(a + a)
  end
end

このテストは、隣接する <a> 要素が生成されないこと(assert_no_selector %(a + a))と、単一のリンク要素のみが存在することを検証しています。

設計判断

リンクノードの生成前にクリアする方式 が採用されました。

この設計は、Lexicalのコアな振る舞いを変更せず、既存のAPIを組み合わせることで実現されています。$toggleLink はLexicalの標準APIであり、null を渡すことでリンクのトグルオフを実現できます。

リンク境界のクリアと新規リンクの適用を分離することで、選択範囲の構造に関わらず一貫した動作を保証できます。Lexicalのノード生成メカニズムに介入することなく、2回の $toggleLink 呼び出しのみで問題を解決しています。

まとめ

本PRは、リンク貼り付け時の既存リンククリア処理を追加することで、複数のリンクノード生成問題を解決しました。$toggleLink(null) の1行追加により、選択範囲の構造に依存しない一貫したリンク生成が実現され、ユーザーが直感的に期待する単一リンクの生成が保証されています。

記事メタデータ

Generated by:
Claude Sonnet 4.5 for DiffDaily

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

品質レビュー結果

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

Review Criteria:

記事構成 ✓ PASS

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

「総論→各論→結論」の構成が明確です。リード文、背景、技術的変更、設計判断、まとめの各要素がすべて含まれており、論理的な流れが構築されています。

カスタムMarkdown構文 ✓ PASS

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

ファイル名付きシンタックスハイライト (`javascript:src/editor/contents.js`) とGitHubリンク記法 (`[#594]`, `[PR #719]`) が正しく使用されています。

対象読者への適合性 ✓ PASS

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

Lexicalの`LinkNode`や`$toggleLink`といった専門用語を前提としており、専門知識を持つエンジニアという対象読者に適した技術レベルです。

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

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

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

Diff内容との照合 ⚠ WARNING

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

「変更前」のコードブロックでメソッド名に `createeLinkWithSelectedText` というタイポがあります。それ以外のコード引用(変更後のコード、テストコード)はDiff内容を正確に反映しています。

技術用語の正確性 ✓ PASS

技術用語の正確な使用

`LinkNode`、`$toggleLink`、`Lexical`といった技術用語が、PRの文脈に沿って正確に使用されています。

説明の技術的正確性 ✓ PASS

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

「`$toggleLink(null)`で既存リンクをクリアする」という解決策の説明は、PR Descriptionの内容と一致しており、技術的に正確です。

事実の突合 ✓ PASS

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

記事内の主張はすべてPRのDescriptionやDiff内容で裏付けられており、根拠のない推測や憶測(ハルシネーション)は見られません。

数値・固有名詞の確認 ⚠ WARNING

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

メソッド名`createLinkWithSelectedText`にタイポが見られます。PR番号(#719)やIssue番号(#594)は正確です。

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

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

記事タイトル「選択範囲へのURL貼り付けで複数のリンクノードを統合」は、PRの主題「Merge adjacent links with the same URL when pasting」を的確に表現しています。

外部知識の正確性 ✓ PASS

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

記事の内容は提供されたPR情報に限定されており、バージョン情報やリリース予定など、PRに記載のない外部知識の追加はありません。

時間表現の正確性 ✓ PASS

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

「改善され」「変更されています」といった完了形の表現が使われており、PRがマージ済みであるという事実と時間表現が一致しています。