https://github.com/basecamp/lexxy
Safariでフォーカス中のエディタに値を設定するとカーソルが不整合な位置に置かれるバグを修正しました。これまでは `SKIP_DOM_SELECTION_TAG` を更新オプションとして常に付与していましたが、フォーカス状態に応じて選択更新戦略を切り替え、フォーカス中は更新完了後に `editor.focus()` を再適用することでSafariのカーソルずれを解消しています。
アップロード完了などの非同期更新が Undo エントリを汚染していた問題を解消するため、履歴スタック全体を書き換える `RewritableHistoryExtension` と `REWRITE_HISTORY_COMMAND` を新設しました。`HISTORY_MERGE_TAG` の付与箇所も `NodeSelection` 切り替えや `ProvisionalParagraph` の可視性更新に対して整理され、ユーザー操作のみが Undo の対象となるよう設計が改善されています。
`Contents` クラスに混在していた `NodeInserter` とその関連クラス群を `src/editor/contents/node_inserter.js` として専用ファイルへ抽出し、コードの責務を明確に分離しました。あわせて `$isShadowRoot` ヘルパー関数も `lexical_helper.js` へ移動され、`contents.js` のインポート宣言が大幅にスリム化されています。ロジックの変更は一切なく、純粋なコード整理です。
Ctrl/Cmd+クリックによるリンク開封の実装を、`contenteditable=false` のトグルから Lexical の `CLICK_COMMAND` ベースに置き換えました。カーソル移動の問題を引き起こしていた副作用の大きい操作を廃止し、コマンドシステムとCSSによる役割分担で整理しています。ミドルクリックでの開封も新たにサポートされています。
重いDOMを持つページでの初期化時スタイル再計算(UpdateLayoutTree)を4つの独立した変更で削減。共有の`contain: strict`リゾルバルート導入、`set value`のrAFワークアラウンドを初回ロード限定化、contenteditable要素への`contain: layout style`追加、フォーカス済み時の`editor.focus()`スキップにより、Basecampチャットページで約1,168msかかっていた初期化時スタイル再計算の主要な発生源を体系的に封じます。
エディタ初期化時にDOMの読み書きがループ内で交互に発生する「レイアウトスラッシング」を修正し、複数エディタ存在時の約1.5秒のブロッキングを解消したPRです。`editor.js`・`toolbar.js`・`format_helper.js`の3箇所で、全要素の生成→一括DOM追加→一括読み取り→一括削除というフェーズ分離パターンを統一的に適用し、N回の強制リフローを最大1回に削減しています。
Lexxyエディタでネストされたリストをエクスポートした際に番号が崩れるバグを修正。`<li>` の `value` 属性をActionTextサニタイザの許可リストとクライアントサイドの許可要素に追加することで、Lexicalが管理するリスト番号情報がエクスポート時に保持されるようになります。
Turboのページ遷移時に`[data-turbo-permanent]`コンテナ内のLexxyエディタが`#reset()`により操作不能になるバグを修正。`closest("[data-turbo-permanent]")`による条件分岐を追加し、永続要素内のエディタをTurboキャッシュ前リセットから除外することで、`connectedCallback`が再発火しない要素への破壊的なリセットを防ぎます。
テキスト選択なし(collapsed selection)の状態で既存リンクを編集するとURLがテキストに挿入されてしまうバグを修正。`$getNearestNodeOfType` でカーソルがLinkNode内にあるかを判定し、AutoLinkノードの新規生成をスキップすることで正しいhref更新が行われるようになった。
ツールバーの最初のアイテムが非表示の場合に Shift+Tab でフォーカスが当たらなくなるバグを修正しました。`#handleEditorFocus` メソッドが先頭インデックスを無条件に対象としていた処理を、`isActiveAndVisible` を使って最初の表示済み有効アイテムを検索する形に変更しています。あわせて、モジュール内プライベート関数だった `isActiveAndVisible` を `html_helper.js` にエクスポート関数として集約し、再利用可能な共通ヘルパーに整理しました。
Ctrl/Cmd キー押下中にエディタ内のリンク要素へ `contenteditable="false"` を動的に付け外しする `LinkOpenerExtension` が追加されました。`window.open` を直接呼び出す方式と異なり、ブラウザネイティブの動作に委譲することでホバー時のカーソル表示を含むビジュアルフィードバックが自然に機能します。タブ切り替え後の Chrome におけるイベント不整合には `mousemove` を利用した遅延再同期で対処しています。
Lexxyのローカルプロンプトのフィルタリングロジックを刷新し、単語の途中にマッチする結果を除外しつつ、マッチ位置が先頭に近い候補を優先表示するようになりました。`filterMatches`(boolean返却)を`filterMatchPosition`(インデックス返却)に置き換えることで、マッチ判定とソートキーの取得を一度の処理で完結させる設計を採用しています。
直前のリファクタリング(#945)で `getUrl()` と誤記されたメソッド呼び出しを正しい `getURL()` に修正し、リンクダイアログを開いたときに既存URLが表示されない不具合を解消しました。あわせてリグレッション防止のためのブラウザテスト2件を追加しています。
カスタム添付コンテンツ(メンション、カードなど)が過剰にサニタイズされていた問題を修正。DOMPurifyの永続設定機能を活用し、エディタ初期化時に許可タグセットを一度だけ設定する方式に統一することで、エディタ本体と添付コンテンツのサニタイズ設定の乖離を構造的に解消しました。
チャット入力のメンション補完で発生していた入力ラグを、50msデバウンスの導入とレンダリング件数の100件キャップという2つの独立した修正で解消しました。LocalFilterSource・RemoteFilterSourceの両方に上限が設けられ、パフォーマンス検証用のPlaywrightテストも追加されています。
テキストをドラッグした際に `event.target` がテキストノードになり `.closest()` が存在しないため発生していた `TypeError` を、オプショナルチェーン(`?.`)の追加で修正しました。ネイティブのテキストD&Dを全面ブロックする以前の回避策を取り除き、アタッチメントドラッグハンドラが非Elementターゲットを安全にスキップするよう最小限の変更で対応しています。
LexxyエディタでNodeSelectionにanchorプロパティが存在しないためにクラッシュしていた問題を修正。dispatchInsertUnorderedListとdispatchInsertOrderedListのガード条件を`!selection`から`!$isRangeSelection(selection)`に置き換えることで、添付ファイル選択中のリストフォーマット適用時のクラッシュを2行の変更で解消しました。
NPMパッケージ経由で`highlightCode`を使用した際に`@lexical/code`の組み込み言語グラマーが欠落するバグを修正。`src/config/prism.js`に`@lexical/code`が前提とするPrismグラマーを明示的に追加し、「ベース言語」と「拡張言語」のグループに分離してコメントで順序依存を宣言する構成に整理した。あわせてKotlinのサポートも追加されている。
コードブロック(EarlyEscapeCodeNode)の先頭でEnterを押した際、コード内に改行が挿入されてしまう問題を修正。カーソルがブロック先頭にある場合は `insertBefore($createParagraphNode())` でブロックの前に段落を挿入するよう `insertNewAfter` メソッドを拡張し、Playwrightによるブラウザテストも追加されました。
LexxyエンジンがAction Textのエディターアダプター設定を自動的に行うようになりました。これまでホストアプリ側で必要だった `config.action_text.editor = :lexxy` の手動設定が不要になり、エンジン導入時のボイラープレートが削減されます。