ネストされたリストの番号崩れを `value` 属性の許可で修正

basecamp/lexxy

Lexxyエディタで番号付きリストをエクスポートした際、ネストされた子要素があると連番が崩れるバグを修正しました。<li> 要素の value 属性 をサニタイザの許可リストに追加し、エディタ内部でも扱えるようにすることで、正確な連番が保持されるようになります。

背景

ネストされた番号付きリストをエクスポートすると、連番が正しく出力されない問題がありました。PRの説明によると、子リストアイテムが存在する場合に番号が飛ぶ現象が発生しており、例として次のような崩れた出力が示されています。

1 First
2 Second
  1 Second first
  2 Second second
4 Third

「4 Third」が「3 Third」にならないのは、ネストされた子アイテムのカウントが親リストの連番に混入しているためです。Lexical(エディタのコアライブラリ)は各 <li>value 属性で明示的な番号を付与することでこの問題を回避しますが、ActionTextのサニタイザがその属性を除去してしまうため、エクスポート時に情報が失われていました。

技術的な変更

変更は3か所に分かれており、サーバーサイドの許可リスト追加、クライアントサイドの許可要素定義、そしてテストの期待値更新で構成されています。

lib/lexxy/engine.rb では、ActionTextのサニタイザが許可する属性リストに value を追加しました。

変更前:

ActionText::ContentHelper.allowed_attributes = default_allowed_attributes + %w[ controls poster data-language style ]

変更後:

ActionText::ContentHelper.allowed_attributes = default_allowed_attributes + %w[ controls poster data-language style value ]

src/extensions/format_escape_extension.js では、allowedElements ゲッターを追加し、<li> タグの value 属性をエディタの許可要素として定義しました。

get allowedElements() {
  return [ { tag: "li", attributes: [ "value" ] } ]
}

テストファイル群(block_formatting.test.jsescape_format.test.jslist_indentation.test.js 等)では、リストアイテムのHTML期待値がすべて value 属性付きに更新されました。たとえばフラットなリストでは各アイテムに通し番号が、ネストされたリストでは子リストの最初のアイテムが value="1" から再カウントされることが確認できます。

// 変更前
'<ol><li>Alpha</li><li>Bravo</li><li>Charlie</li></ol>'

// 変更後
'<ol><li value="1">Alpha</li><li value="2">Bravo</li><li value="3">Charlie</li></ol>'

テストの変更量が示すように、value 属性はすべてのリストアイテムに対して常に付与される仕様であり、オプションではありません。

設計判断

value 属性をサニタイザ許可リストに追加するというアプローチが採用されました。

value はHTML仕様でもともと <li> に定義された属性であり、番号付きリストにおけるアイテムの表示番号を上書きするために使われます。Lexicalがこの標準属性を活用してリスト番号を管理している設計上、Lexxyが同じ属性を正しく通過させるのは自然な対応です。ActionTextのデフォルトサニタイザは value を除去しますが、これはフォームのサブミット値などに使われる value への汎用的な防御であり、<li> 固有のセマンティクスを考慮していません。

クライアントサイドの allowedElements 定義はタグとアトリビュートを組み合わせた粒度で許可を宣言しており、<li> 以外の要素に value が伝播する範囲を最小化しています。

まとめ

本PRは、Lexicalが内部的に管理するリスト番号情報をActionTextのサニタイザが消去していたという、エディタとサニタイザ間のインピーダンスミスマッチを解消する修正です。value という標準HTML属性を明示的に許可することで、エクスポートされたHTMLにリストの順序情報が正確に保持されるようになりました。

記事メタデータ

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

この記事は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番号のリンク記法([#981](URL))が正しく使用されています。

対象読者への適合性 ✓ PASS

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

「サニタイザ」「インピーダンスミスマッチ」といった技術用語を適切に用いており、専門知識を持つエンジニアという対象読者に適合した内容です。

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

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

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

Diff内容との照合 ✓ PASS

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

記事内で引用されているコードブロック(`engine.rb`, `format_escape_extension.js`, テストコード例)は、提供されたDiffの内容と正確に一致しています。

技術用語の正確性 ✓ PASS

技術用語の正確な使用

「Lexical」「ActionText」「サニタイザ」「value属性」など、関連する技術用語が文脈に沿って正確に使用されています。

説明の技術的正確性 ✓ PASS

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

ネストされたリストの番号が崩れる原因(サニタイザによるvalue属性の除去)と、その解決策(許可リストへの追加)に関する説明は、Diffの内容と整合しており技術的に正確です。

事実の突合 ✓ PASS

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

記事内の主張はすべてPRのDescription、Diff内のコード変更、テストコードの更新内容によって裏付けられており、ハルシネーション(捏造)は見られません。

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

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

PR番号(#981)が正確に記載されています。

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

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

記事のタイトル「ネストされたリストの番号崩れを `value` 属性の許可で修正」は、PRのタイトル「Allow value attribute for list items」の意図と内容を的確に要約しています。

外部知識の正確性 ✓ PASS

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

PRに記載のないバージョンのサポート状況やリリース日程などの外部知識は含まれていません。HTML仕様に関する補足は文脈上適切です。

時間表現の正確性 ✓ PASS

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

「問題がありました」「修正しました」など、PRが対処した問題と解決策を表現する時間表現は正確です。