アップロード完了時の選択状態維持とDOM構造の簡略化

basecamp/lexxy

Lexxyのアップロード処理に複数の改善が加わった。ファイル名の欠落バグ修正、画像の不要なコンテナ要素の除去、アップロード完了後の選択状態の引き継ぎが主な変更点だ。

背景

これまでのアップロードフローには、段階的に発見された複数の問題があった。まずファイル名(fileName)がアップロード中ノードに保持されておらず、ファイル種別クラス名の生成に支障をきたしていた。次に、画像のDOM表現に不要な attachment__container DIVが挿入されており、CSSの設計を複雑にしていた。さらに、アップロード完了時に元のノードを置換すると選択状態が失われ、UXの一貫性が損なわれていた。

これらは独立した問題であるが、いずれもアップロード体験全体の品質に関わるため、本PRで一括して対処している。

技術的な変更

ファイル名の保持と選択状態の引き継ぎ

ActionTextAttachmentUploadNode のコンストラクタに this.fileName = file.name が追加され、ファイル名がノードに保持されるようになった。ファイル名はファイル種別の判定などに利用されるが、これまでアップロード中のノードでは欠落していた。

アップロード完了時の #showUploadedAttachment メソッドでは、ノード置換前に選択状態を確認し、置換後に引き継ぐロジックが追加された。

変更前:

#showUploadedAttachment(blob) {
  this.editor.update(() => {
    this.replace(this.#toActionTextAttachmentNodeWith(blob))
  }, { tag: SILENT_UPDATE_TAGS })
}

変更後:

#showUploadedAttachment(blob) {
  this.editor.update(() => {
    const shouldTransferNodeSelection = this.isSelected()

    const replacementNode = this.#toActionTextAttachmentNodeWith(blob)
    this.replace(replacementNode)

    if (shouldTransferNodeSelection) {
      const nodeSelection = $createNodeSelectionWith(replacementNode)
      $setSelection(nodeSelection)
    }
  }, { tag: SILENT_UPDATE_TAGS })
}

$createNodeSelectionWithlexical_helper から、$setSelectionlexical パッケージからそれぞれインポートされ、同一の editor.update トランザクション内で選択状態の移譲が完結する。

画像コンテナ要素の除去

#createDOMForImage メソッドが簡略化され、img 要素を div.attachment__container で包む処理が削除された。

変更前:

#createDOMForImage(options = {}) {
  const img = createElement("img", { src: this.src, draggable: false, alt: this.altText, ...this.#imageDimensions, ...options })
  const container = createElement("div", { className: "attachment__container" })
  container.appendChild(img)
  return container
}

変更後:

#createDOMForImage(options = {}) {
  const img = createElement("img", { src: this.src, draggable: false, alt: this.altText, ...this.#imageDimensions, ...options })
  return img
}

DOM要素数が削減されることで、CSSのセレクタ設計が単純になる。

CSSのリファクタリング

lexxy-content.css では .attachmentdisplaytable から block に変更された。従来は display: table を用いてキャプション幅を添付ファイルに合わせる手法が採られていたが、この依存が解消された。合わせて .attachment__captiondisplay: table-caption から display: block に変更され、caption-side: bottom が削除されている。

lexxy-editor.css では、アップロード中のプログレスバー表示がファイル種別ごとに分岐する形に整理された。通常のファイル(.attachment)では max-inline-size: 10ch で横幅を制限し、プレビュー表示(.attachment--preview)では position: absolute で画像上に重ねて表示する。さらに .attachment--file に対して削除ボタン(lexxy-node-delete-button)の位置指定が追加され、縦方向中央揃えが実現している。

設計判断

選択状態の移譲を同一トランザクション内で完結させた 点が、この変更の核心となる設計判断だ。replace 後に別の editor.update で選択状態を設定する方式も考えられるが、それでは2回のレンダリングが発生し、ちらつきや競合状態のリスクが生じる。SILENT_UPDATE_TAGS を付与した単一のトランザクション内で置換と選択移譲を行うことで、エディタの状態遷移がアトミックになっている。

display: table を用いたキャプション幅の制御は、CSSの古典的なトリックだが副作用も多い。display: block への移行はこの依存を断ち切り、コンテナDIVの除去とともにDOM・CSSの両面でシンプルな構造を目指す方向性を示している。

まとめ

アップロード完了時の選択状態引き継ぎ、コンテナDIVの除去、display: table からの脱却は、それぞれ独立した改善でありながら、いずれも「アップロード前後で体験の一貫性を保つ」という一つの方向性に沿っている。JavaScriptの状態管理とCSSの設計が同時に整理されたことで、今後のスタイル変更やノード操作の拡張が容易になる。

記事メタデータ

Generated by:
Claude Sonnet 4.6 for DiffDaily
LLM Trace:
4835e2d7

この記事は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リンク記法の正確性

ファイル名付きのシンタックスハイライト(```言語:ファイルパス)が正しく使用されています。記事本文中にはGitHubリンク記法がないため、評価対象外ですが、フッターのリンクは適切です。

対象読者への適合性 ✓ PASS

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

DOM構造、CSS設計、Lexicalのトランザクションといった専門用語を適切に使用しており、対象読者であるエンジニアに適した技術レベルで記述されています。

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

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

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

Diff内容との照合 ⚠ WARNING

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

コード引用は概ね正確ですが、`#showUploadedAttachment`メソッドの変更後コードにおいて、Diff内の`wo(nodeSelection)`というminifyされた可能性のある関数呼び出しを、`$setSelection(nodeSelection)`という元の関数名に修正して引用しています。技術的な意味合いは同じと推測されますが、Diffと完全に一致していません。

技術用語の正確性 ✓ PASS

技術用語の正確な使用

DOM、CSS、Lexicalフレームワーク(トランザクション、NodeSelection)に関する技術用語が、文脈に沿って正確に使用されています。

説明の技術的正確性 ✓ PASS

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

選択状態の引き継ぎロジックやDOM構造の簡略化など、コード変更に対する技術的な説明はDiffの内容と一致しており、論理的で正確です。

事実の突合 ✓ PASS

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

記事内の主張はすべてPRのDescriptionまたはDiff内のコード変更によって裏付けられており、ハルシネーション(捏造)は見られません。「設計判断」もDiffから読み取れる妥当な解説です。

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

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

フッターに記載されたPR番号「#796」は正確です。記事内で使用されている他の固有名詞(メソッド名、クラス名など)もDiffと一致しています。

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

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

記事タイトル「アップロード完了時の選択状態維持とDOM構造の簡略化」は、PRの複数の変更点の中から主要なものを的確に抜き出しており、内容を正確に反映しています。

外部知識の正確性 ✓ PASS

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

バージョン情報やリリース予定など、PR情報に基づかない外部知識の記述はなく、提供された情報源に忠実です。

時間表現の正確性 ✓ PASS

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

「これまでの問題」といった過去の状況を示す表現が適切に使われており、時間的な前後関係の歪曲はありません。