AttachmentNodeキャプション入力のSafariバグ回避策を削除

basecamp/lexxy

Provisional Paragraphs機能の導入により、Safari特有のキャプション入力バグへの回避策が不要になりました。DecoratorNodeが単独のノードではなくなったことで、エディタのフォーカス管理がシンプルかつ堅牢になっています。

背景

Safariでは、ドキュメント内に AttachmentNode(DecoratorNode)しか存在しない場合、その前後にテキストを入力できないという問題がありました。#726で導入された Provisional Paragraphs は、DecoratorNodeやElementNodeの隣に選択可能な仮想的な段落ノードを自動挿入する仕組みです。この機能により、エディタ境界での選択が常に可能になり、Safariでの回避策が不要になりました。

従来の回避策は Selection クラスの #handleInputWhenDecoratorNodesSelected メソッドで実装されており、DecoratorNodeの前後に入力があった際に段落を手動で挿入していました。Provisional Paragraphsはこの処理を自動化し、より一般的な解決策を提供します。

技術的な変更

本PRでは、2つの主要な変更が行われました。

Safari回避策の削除

src/editor/selection.js から57行のコードが削除されました。

削除されたメソッド:

#handleInputWhenDecoratorNodesSelected() {
  this.editor.getRootElement().addEventListener("keydown", (event) => {
    if (isPrintableCharacter(event)) {
      this.editor.update(() => {
        const selection = $getSelection()

        if ($isRangeSelection(selection) && selection.isCollapsed()) {
          const anchorNode = selection.anchor.getNode()
          const offset = selection.anchor.offset

          const nodeBefore = this.#getNodeBeforePosition(anchorNode, offset)
          const nodeAfter = this.#getNodeAfterPosition(anchorNode, offset)

          if (nodeBefore instanceof DecoratorNode && !nodeBefore.isInline()) {
            event.preventDefault()
            // ... 段落挿入処理
          }
        }
      })
    }
  })
}

このメソッドは Selection の初期化時に呼ばれていましたが、Provisional Paragraphsが自動的に選択可能なノードを保証するため削除されました。src/helpers/lexical_helper.jsisPrintableCharacter ヘルパー関数も同時に削除されています。

キャプションテキストエリアのイベントハンドリング改善

src/nodes/action_text_attachment_node.js#handleCaptionInputKeydown メソッドが再設計されました。

変更前:

#handleCaptionInputKeydown(event) {
  if (event.key === "Enter") {
    this.#updateCaptionValueFromInput(event.target)
    event.preventDefault()

    this.editor.update(() => {
      this.selectNext()
    }, { tag: HISTORY_MERGE_TAG })
  }
  event.stopPropagation()
}

変更後:

#handleCaptionInputKeydown(event) {
  if (event.key === "Enter") {
    event.preventDefault()
    event.stopPropagation()
    event.target.blur()

    this.editor.update(() => {
      // Place the cursor after the current image
      this.selectNext(0, 0)
    }, {
      tag: HISTORY_MERGE_TAG
    })
  }

}

主な変更点は以下の3つです:

  • event.target.blur() を追加し、キャプションテキストエリアから明示的にフォーカスを外す
  • this.selectNext(0, 0) の引数指定により、カーソル位置を明確に指定
  • event.stopPropagation() をEnterキー処理の内部に移動し、他のキーイベントの伝播は許可

this.#updateCaptionValueFromInput の呼び出しは削除されましたが、キャプションの保存は blur イベントで処理されます。test/system/attachments_test.rb に追加された3つのテストケースが、Enter・クリック・Tabによるフォーカス遷移でキャプションが正しく保存されることを検証しています。

設計判断

Provisional Paragraphsへの移行 により、ブラウザ固有の問題への対処が構造的な解決に置き換えられました。

PR内の説明によれば、Provisional Paragraphsは DecoratorNodeやElementNodeの隣に自動的に仮想段落を挿入し、空のエディタにも段落を提供します。これらの仮想段落は編集時に通常の段落に変換され、不要になると自動削除されます。この仕組みにより、選択可能なノードが常に存在することが保証され、Safari固有のキーボード入力処理が不要になりました。

キャプションのフォーカス管理では、blur() を明示的に呼び出すアプローチが採用されました。これは、Enterキー押下時にキャプション入力を終了し、エディタ本体にフォーカスを戻す意図を明確にしています。Provisional Paragraphsがカーソル移動先を保証するため、フォーカス遷移の信頼性が向上しています。

まとめ

本PRは、Provisional Paragraphs機能を活用してSafari特有のバグ回避策を削除し、エディタのフォーカス管理をシンプル化しました。ブラウザ固有の問題への対処を構造的な解決に置き換えることで、コードの保守性が向上し、将来的なバグのリスクが軽減されています。57行のコード削除と8行の改善により、より堅牢なキャプション入力体験が実現されました。

記事メタデータ

Generated by:
Claude Sonnet 4.5 for DiffDaily

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

品質レビュー結果

Review Status:
承認済み
Review Count:
1回
Reviewed by:
Gemini 2.5 Pro for DiffDaily

Review Criteria:

記事構成 ✓ PASS

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

リード文(総論)→背景・技術的変更・設計判断(各論)→まとめ(結論)の3部構成が明確に適用されており、すべての必須要素が含まれています。

カスタムMarkdown構文 ✓ PASS

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

ファイル名付きシンタックスハイライト(```javascript:filepath```)とGitHubのPRリンク記法([#726](URL))が正しく使用されています。

対象読者への適合性 ✓ PASS

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

DecoratorNode, Provisional Paragraphsといった専門用語が説明なしで使われており、リッチテキストエディタ開発の知識を持つエンジニアという対象読者に適合しています。

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

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

各セクションが総論→各論の構成になっており、各段落はトピックセンテンスで始まり、1段落1トピックの原則が守られています。段落の長さも適切です。

Diff内容との照合 ✓ PASS

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

記事内で引用されているコードブロック(#handleInputWhenDecoratorNodesSelectedの削除、#handleCaptionInputKeydownの変更前後)は、提供されたDiffの内容と完全に一致しています。

技術用語の正確性 ✓ PASS

技術用語の正確な使用

「Provisional Paragraphs」「DecoratorNode」「Selectionクラス」などの技術用語が、PRの文脈および一般的な技術的意味合いにおいて正確に使用されています。

説明の技術的正確性 ✓ PASS

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

Provisional Paragraphs導入による回避策削除の因果関係や、キャプション保存がblurイベントで処理される仕組みなど、技術的な説明はPRのコード変更と整合性が取れており正確です。

事実の突合 ✓ PASS

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

記事内の主張(Safari回避策の削除、Provisional Paragraphsの役割、テストケースの追加など)は、すべてPRのDescriptionやDiffの内容によって裏付けられており、ハルシネーションは検出されませんでした。

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

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

PR番号(#747)、関連PR番号(#726)、削除されたコード行数(57行)など、記事に含まれるすべての数値と固有名詞は正確です。

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

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

記事のタイトル「AttachmentNodeキャプション入力のSafariバグ回避策を削除」は、PRのタイトル「Remove redundant Safari bug workaround」の内容を正確かつ具体的に反映しています。

外部知識の正確性 ✓ PASS

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

記事に含まれる情報はすべてPR内の情報に基づいており、バージョンのサポート状況やリリース日程など、PR外の知識の捏造はありません。

時間表現の正確性 ✓ PASS

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

「〜が不要になりました」「〜が削除されました」といった完了形の表現が使われており、PRで行われた変更の時制を正確に反映しています。