選択したノードをマウスで削除できるコントロールの追加

basecamp/lexxy

Rich text editor「Lexxy」に、添付ファイルや水平線などの特定ノードをマウス操作で削除できるコントロールが追加されました。ノード選択時にデリートボタンが表示され、キーボードだけでなくマウスでも直感的な削除操作が可能になります。

背景

これまでLexxyでは、添付ファイルや水平線といった装飾ノードを削除するにはキーボードの Delete / Backspace キーを使う必要がありました。#755 は、これらのノードをマウスクリックだけで削除できる機能を追加しています。

UIコンポーネントとしての整合性を保つため、既存のcode language pickerやtable toolsと同じ lexxy-floating-controls クラスを用いたフローティングコントロールとして実装されました。

技術的な変更

新しいカスタム要素 <lexxy-node-delete-button> が導入され、削除可能な各ノードのDOM構造に組み込まれます。

ノードへのデリートボタンの組み込み

ActionTextAttachmentNodeCustomActionTextAttachmentNodeHorizontalDividerNode の3つのノードクラスで、DOM生成時にデリートボタンが追加されるようになりました。

変更例(ActionTextAttachmentNode):

createAttachmentFigure() {
  const figure = createAttachmentFigure(this.contentType, this.isPreviewableAttachment, this.fileName)

  const deleteButton = createElement("lexxy-node-delete-button")
  figure.appendChild(deleteButton)

  return figure
}

各ノードの figure 要素に <lexxy-node-delete-button> が追加され、ノード選択時に削除UIが表示される構造になっています。

NodeDeleteButtonの実装

src/elements/node_delete_button.js として新規追加されたこのカスタム要素は、ゴミ箱アイコンのボタンを提供し、クリック時に該当ノードをLexicalエディタから削除します。

#deleteNode() {
  this.editor.update(() => {
    const node = $getNearestNodeFromDOMNode(this)
    node?.remove()
  })
}

Lexicalの $getNearestNodeFromDOMNode を使って、DOMノードから対応するLexicalノードを取得し、remove() で削除を実行しています。この設計により、各ノードクラスに削除ロジックを個別実装する必要がなくなりました。

フローティングコントロールの統一

既存のcode language pickerやtable toolsと同様に、lexxy-floating-controls クラスが適用されます。

connectedCallback() {
  this.editorElement = this.closest("lexxy-editor")
  this.editor = this.editorElement.editor
  this.classList.add("lexxy-floating-controls")
  // ...
}

この共通クラスにより、コントロールの表示スタイル、ボタンサイズ(--button-size: 2.3lh)、ボーダーラジアス(--table-tools-radius)などのCSS変数が統一的に適用されます。

スタイリングの調整

app/assets/stylesheets/lexxy-content.css では、削除ボタンの配置のため figure 要素に position: relative が追加され、添付ファイルプレビューの最小高さ設定(min-block-size: 3em)により小さなアイコンでもクリック領域が確保されました。

figure,
.attachment {
  margin-block: 0 var(--lexxy-content-margin);
  position: relative;
  // ...
}

object {
  inline-size: auto;
  margin-inline: auto;
  min-block-size: 3em;
  max-block-size: 32rem;
  // ...
}

設計判断

Custom Elementベースのモジュラー設計 が採用されました。

Lexicalエディタのプラグイン機構ではなく、Web Components(Custom Elements)としてUIコントロールを実装する方針が維持されています。これにより、<lexxy-node-delete-button> は単なるDOMノードとして各ノード型に追加でき、Lexicalの内部状態管理から独立した実装が可能です。

disconnectedCallback() での明示的なクリーンアップ処理(イベントリスナーの削除、参照の解放)も追加されており、メモリリークを防ぐ配慮がなされています。既存のcode language pickerにも同様のクリーンアップが追加されました。

また、削除ボタンの tabIndex = -1 設定により、キーボードナビゲーションからは除外しつつマウス操作は可能にする設計になっています。これは、キーボードユーザーには既存の Delete / Backspace キーによる削除手段があるためです。

まとめ

本PRは、Lexxyに初めてマウスベースのノード削除機能を導入した変更です。Custom Elementを活用したモジュラー設計により、各ノード型への影響を最小限に抑えつつ、既存のフローティングコントロール群と統一されたUIを提供しています。キーボード操作を補完するマウス操作の追加により、より幅広いユーザーに対応したエディタUIとなりました。

記事メタデータ

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の存在と明確さ

リード文(総論)→背景・技術詳細・設計判断(各論)→まとめ(結論)という「総論→各論→結論」の構成が記事全体で明確に適用されており、非常に分かりやすいです。

カスタムMarkdown構文 ✓ PASS

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

ファイル名付きシンタックスハイライト(```言語:ファイルパス)およびPR番号のリンク記法([#755](URL))が正しく使用されています。

対象読者への適合性 ✓ PASS

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

Custom ElementやLexical APIなど、専門知識を持つエンジニアを対象とした適切な技術レベルと表現で書かれています。

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

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

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

Diff内容との照合 ⚠ WARNING

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

ほとんどのコード引用はDiffと正確に一致していますが、「src/elements/node_delete_button.js」のコードブロックが、提供されたDiffの範囲外の内容(node?.remove())を含んでいます。ただし、Diff情報が不完全である可能性が高く、引用されたコードは技術的に妥当です。

技術用語の正確性 ✓ PASS

技術用語の正確な使用

「Custom Element」「Lexical」「$getNearestNodeFromDOMNode」などの技術用語が、文脈に沿って正確に使用されています。

説明の技術的正確性 ✓ PASS

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

Custom Elementの導入、CSSクラスによるスタイリング統一、クリーンアップ処理の追加など、すべての技術的な説明がDiffの内容と整合しており、論理的で正確です。

事実の突合 ✓ PASS

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

記事内のすべての主張(削除ボタンの導入、対象ノード、実装方針など)は、PRのDescriptionやDiff内のコード変更によって裏付けられており、ハルシネーションは検出されませんでした。

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

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

PR番号(#755)や引用されているファイルパス、CSSのプロパティ値などがすべて正確です。

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

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

記事のタイトル「選択したノードをマウスで削除できるコントロールの追加」は、PRの主題「Delete button for nodes」を正確に反映しています。

外部知識の正確性 ✓ PASS

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

PR情報に含まれないバージョンサポート状況やリリース日程などの外部知識は記載されていません。

時間表現の正確性 ✓ PASS

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

「追加されました」といった時間表現は、完了した変更であるPRの内容と一致しており、正確です。