https://github.com/basecamp/lexxy
`>` + スペースのマークダウンショートカットで作成した引用ブロックへの複数行HTMLペーストが引用外に脱出するバグを修正。`QuoteNode` のノードトランスフォームを登録し、`ParagraphNode` を持たない空の `QuoteNode` を検出したタイミングで自動的に `ParagraphNode` を補完することで、ペースト処理の前提構造を保証する設計になっています。
`<lexxy-editor>` に `permitted-attachment-types` 属性を追加し、HTMLインポート・ファイルドロップ・プロンプト起動・プログラム挿入の全経路で添付ファイルのコンテンツタイプを一括制御できるようにした変更です。エディタ要素に `permittedAttachmentTypes` ゲッターと `permitsAttachmentContentType()` 述語を追加し、全エントリポイントがこの単一の判定ロジックを参照する設計で、ホスト側でのフィルタリング実装が不要になりました。
クォートブロックへのペースト時に発生していた「カーソル位置の無視」「エディタの応答不能」「余分な空行の挿入」という3つのバグを修正しました。原因はカスタム実装の `QuoteNodeInserter` がテキストノード内のカーソルオフセットを無視していたことで、クラス全体を削除してLexicalのデフォルト処理に委ねることで解決しています。
ブロッククォート適用時に `<br>` が消失する問題と、選択外の行まで引用されてしまう問題を修正。従来の「全BRをパラグラフに分割」方式を廃止し、選択境界に最も近いBRのみで分割する `$splitParagraphsAtLineBreakBoundaries` に置き換えることで、選択範囲内のBRを保持しつつ、選択した行のみを正確に引用できるようになりました。
コードブロックへ空行を含むテキストをペーストすると内容がブロック外のパラグラフに分割されてしまうバグを修正。原因はLexicalがペースト操作を複数の `insertNewAfter()` 呼び出しに分解する際に `EarlyEscapeCodeNode` の脱出ロジックが誤発火することで、`$hasUpdateTag(PASTE_TAG)` によるガードを追加し、ペースト中は基底クラスの安全な動作に委譲することで解決。
コードブロックの末尾行がスペースやタブのみの場合、Enterキーで脱出できなかったバグを修正。`#isCursorOnWhitespaceOnlyLastLine` メソッドを追加し、1回目のEnterで空白をクリア、2回目のEnterで既存の脱出フローに乗せる2段階処理で対応しました。
コードブロックのシンタックスハイライト後に `<pre>` 要素が失われ、コピー時に改行が折り畳まれていたバグを `replaceWith` → `replaceChildren` の1行変更で修正。合わせてコードブロックへのペーストを平文専用ハンドラで処理し、HTML形式のクリップボードデータがコードブロック構造を破壊する問題も解消した。
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件を追加しています。