エクステンション注入後のツールバーオーバーフローを再計算する仕組みの導入
エクステンションがツールバー要素を非同期で追加した後もオーバーフロー計算が正しく行われるよう、requestOverflowRefresh() をパブリックAPIとして公開し、initializeToolbars() から呼び出す設計に変更されました。
背景
エクステンションがツールバーボタンを注入するタイミングが、ツールバーのオーバーフロー計算より後になる場合に表示が崩れる問題がありました。オーバーフロー計算はツールバーの初期化時に一度だけ行われていたため、後から追加されたボタンは計算結果に含まれず、オーバーフローメニューへの正しい振り分けが行われませんでした。また、オーバーフロー判定がボタンのマージンを考慮していなかったことによる細かな表示崩れも存在しました。
あわせて、右端寄せボタンの実装も見直されました。これまでは .lexxy-editor__toolbar-spacer 要素が flex: 1 でスペースを占有することで右端寄せを実現していましたが、今回の変更でこの要素は廃止されました。
これらの問題を根本から解決するため、オーバーフロー再計算のロジックをいつでも呼び出せるパブリックAPIとして整理する設計が採用されました。
技術的な変更
requestOverflowRefresh() のパブリック化と margin-inline-start: auto による右端寄せへの切り替えが、今回の変更の中核をなします。
requestOverflowRefresh() のパブリック化と重複防止
src/elements/toolbar.js において、これまでプライベートメソッドだった #refreshToolbarOverflow() のロジックが requestOverflowRefresh() というパブリックメソッドに再編されました。このメソッドは requestAnimationFrame を使って非同期に実行されますが、#refreshToolbarAF フィールドで実行中のフレームIDを保持することで、短期間に複数回呼ばれても実際の再計算は一度だけになるよう制御されています。
requestOverflowRefresh() {
if (this.#refreshToolbarAF != null) return
this.#refreshToolbarAF = requestAnimationFrame(() => {
// reindexToolbarItems → compact の順に実行
})
}
また、dispose() 時には cancelAnimationFrame(this.#refreshToolbarAF) を呼び出して保留中のフレームをキャンセルするよう変更されており、ライフサイクルの整合性が保たれています。従来 setEditor() 内で単独呼び出しされていた #setItemPositionValues() は削除され、requestOverflowRefresh() 内のリインデックス処理がその役割を統合的に担うようになりました。
エクステンションからの呼び出し
src/editor/extensions.js の initializeToolbars() にて、拡張ボタンをツールバーへ追加した後、toolbar.requestOverflowRefresh() を呼び出す1行が追加されました。
this.#clearPreviousExtensionToolbarButtons(toolbar)
this.#addExtensionToolbarButtons(toolbar)
toolbar.requestOverflowRefresh()
これにより、エクステンションがボタンを追加するたびに、次のアニメーションフレームでオーバーフロー計算が再実行されます。
右端寄せの実装変更
CSSでは .lexxy-editor__toolbar-spacer { flex: 1 } が廃止され、代わりに .lexxy-editor__toolbar-button--push-right クラスへの margin-inline-start: auto が採用されました。この変更により、スペーサー要素をDOMに持たなくても右端寄せが実現できるようになり、テストフィクスチャの参照先クラス名も toolbar-spacer から toolbar-button--push-right に更新されています。また、オーバーフローボタンのドロップダウンに min-inline-size: calc(var(--lexxy-toolbar-button-size) + 1.2ch) が追加され、マージンを考慮したサイズ計算が行われるようになりました。
ファイル名の統一
src/helpers/timing_helpers.js(複数形)が timing_helper.js(単数形)にリネームされ、clipboard.js・selection.js・remote_filter_source.js・prompt.js・table_controller.js・table_tools.js・toolbar_dropdown.js・link_opener_extension.js のインポートパスが一括更新されています。
テストの追加
test/browser/tests/formatting/toolbar.test.js に、非同期注入後の動作を検証するテストが追加されました。ビューポート幅300pxでオーバーフロー状態にした後、requestAnimationFrame 越しにボタンを注入して requestOverflowRefresh() を呼び出し、注入されたボタンに data-position 属性が付与されてオーバーフローメニューに格納されること、そしてビューポートを元のサイズに戻すとツールバー本体に復元されることを一連のシナリオとして検証しています。
設計判断
冪等性のある再計算APIを単一のエントリーポイントに集約する設計が選択されました。
従来は connectedCallback・setEditor それぞれの文脈で個別に再計算が呼ばれ、#setItemPositionValues の呼び出しも分散していました。requestOverflowRefresh() への一本化により、どの文脈から呼ばれても「リセット → リインデックス → コンパクト」という一貫したシーケンスが保証されます。#refreshToolbarAF によるデバウンス相当の制御は、エクステンションが複数のボタンを連続追加するようなケースでも不必要な再計算を防ぐ安全装置として機能します。
右端寄せをスペーサー要素から margin-inline-start: auto に切り替えた判断も、オーバーフロー計算との整合性の観点から合理的です。専用のスペーサー要素がDOMに存在すると、アイテムのリインデックスや位置計算において特別扱いが必要になりますが、CSSプロパティへの移行でその複雑さを排除できます。
まとめ
本PRは、エクステンションによるツールバーの動的拡張を正しく扱うために、オーバーフロー再計算を冪等なパブリックAPIとして公開し、呼び出しの一本化と不要な重複実行の防止を同時に実現した変更です。スペーサー要素の廃止とファイル名の統一も含め、ツールバー周辺の実装が一貫した設計に整理されました。