https://github.com/basecamp/lexxy
Baseline 2023未満のCSS機能に対するESLintの`css/use-baseline`警告を解消するため、`text-wrap`・`resize`・`user-select`などのプロパティを`@supports`クエリでラップするよう変更されました。`::selection`のみESLintコメントによる個別抑制を採用しており、プロパティごとに対処方法を使い分けるアプローチが取られています。
LexxyエディタのconnectedCallback内の初期化処理をrequestAnimationFrameで遅延させ、Turbo morphと同時に発生していたナビゲーション時のラグ(50〜100ms)を解消しました。Lexicalの持つ強制レイアウトフラッシュがmorphのDOM変更全体に波及していた問題に対し、DOM確定後に初期化することで競合を回避しています。合わせてイベントリスナーの登録をregisterRootListenerベースに統一し、ルート要素のライフサイクル管理を堅牢化しています。
`LexicalEditorElement`がElementInternals Validity APIに完全対応し、`required`属性・`checkValidity`・`setValidity`をサポートしました。ファイルアップロード中はフォームが無効状態に設定され、すべてのアップロード完了まで送信が防止されます。エクステンション単位でバリデーション状態を管理するMap構造が採用され、複数エクステンションが互いに干渉せずバリデーションロジックを実装できる拡張可能な設計になっています。
プロンプトメニューのナビゲーション中に `listItem.focus()` がAndroid IMEのソフトキーボードを毎キーストロークで再表示させる問題を修正しました。`aria-activedescendant` 属性の更新のみでスクリーンリーダーへのアナウンスが可能なため、フォーカス移動そのものを廃止し、その補正として存在した `Selection.preservingSelection()` も合わせて削除されています。
App ShareSheetからURLを共有した際にクリップボードへ `text/uri-list` のみが格納されるケースを、リンクとして正しく処理できるようになりました。`#isOnlyURLPasted` の判定条件を `=== 2` の厳格な等値比較から `<= 2` の上限チェックへ緩和し、単独の `text/uri-list` も受け入れながら既存のSafari対応(`text/plain` + `text/uri-list`)との互換性を維持しています。
LexxyにRailsの`:modern`設定と同等のbrowserslist定義が追加され、eslint-plugin-compatによる非対応APIの静的検出とSafari向け`Element.checkVisibility()`ポリフィルが整備されました。あわせてLexical v0.44へのアップデートによりネストリストのHTML出力バグも解消されています。
Lexicalのトリプルクリックハンドラがソフト改行を含む段落の選択やトリプルクリックドラッグを破壊する問題に対し、キャプチャフェーズでstopPropagationを呼ぶ`PreventLexicalTripleClickExtension`を追加して対処しました。Lexxy独自の見出しツールとテーブルがLexicalのハンドラで保護しようとしていた問題を再現しないことを根拠に、上流の修正が行われるまでの暫定措置として最小限の変更で封じ込めています。
エクステンションがツールバーボタンを非同期で追加した後にオーバーフロー計算が再実行されない問題を修正。`requestOverflowRefresh()` をパブリックAPIとして公開し、`initializeToolbars()` から呼び出す設計に変更。あわせて、右端寄せボタンの実装をスペーサー要素から `margin-inline-start: auto` へ切り替え、`timing_helpers.js` のファイル名統一も行われた。
Chromeの「`<summary>` 内のインタラクティブ要素」警告を解消するため、ツールバードロップダウンを `<details>`/`<summary>` ベースからJSで制御するカスタム要素(`<lexxy-toolbar-dropdown>`)へ刷新。ドロップダウンのトリガー・パネル・開閉ロジックを単一の要素に集約し、`HighlightDropdown`/`LinkDropdown` はカスタム要素のサブクラスからプレーンなコントローラークラスへ変更された。ユーザー向けの動作変更はなく、構造的な整理が目的。
`LexicalPromptElement#showEmptyResults`で`empty-results`属性値を`innerHTML`に直接代入していたHTML注入脆弱性を、`textContent`への切り替えで修正しました。ユーザー入力が属性に反映されるホストアプリでDOMベースの注入やオープンリダイレクトが可能な状態でしたが、属性をHTMLとして解釈させない根本的な対処により解消されています。
gemのリネーム(`actiontext/lexical` → `lexxy`)に伴い壊れていた `bin/rails` の `ENGINE_PATH` と `bin/setup` のDBタスクを修正。`ENGINE_PATH` を削除済みの旧パスから `lib/lexxy` に修正して `bin/rails console` を復旧し、`db:prepare` を `app:db:prepare` に変更してダミーアプリへのタスクディスパッチを正しく行うようにした。
`Foo::Bar::Baz` や `port:8080` などコロンを含むプレーンテキストが誤ってリンク変換される問題を修正。`new URL()` による広範なURL判定を廃止し、`scheme://` または `www.` で始まる文字列のみを対象とする正規表現ベースの `isAutolinkableURL()` に置き換えています。
Google DocsなどからペーストされたHTMLに含まれるdata-URI形式のインライン画像を検出し、Active Storageのアップロードパイプラインへ自動変換する機能を実装。ペースト操作時のみ変換を実行する設計で、既存の`lexxy:file-accept`イベントによる許可制御も再利用している。あわせて`$generateNodesFromDOM`への責務統合と`$createUploadNode`の抽出によるリファクタリングも実施。
`<pre>`要素のinnerHTMLを直接書き換える方式に変更し、中間の`<code>`要素と`createElement`ヘルパーを廃止したリファクタリング。DOM操作が「要素生成→属性付与→子要素置換」の3ステップから`innerHTML`の直接代入1ステップに簡略化され、`data-language`属性やハイライト範囲処理の適用先が`<pre>`に統一されました。
Lexxyのエクステンション基底クラスに `dispose()` ライフサイクルフックが追加されました。エディタの切断・再接続時に `Extensions.dispose()` が既存の `#disposables` パターンを通じて呼ばれ、`initializeToolbar()` でツールバーに付与したリスナーなどのリソースを逆順でクリーンアップできます。Turboのback/forwardナビゲーションで発生していた重複ハンドラ問題が根本的に解消されます。
Lexicalエディタの更新リスナーがキーストロークごとにツールバーのaria属性を無条件で書き換えていた問題を修正。書き込み前に現在値を確認してno-op書き込みをスキップするガードを追加し、Turboブロードキャスト等でグローバルスタイル再計算が走る状況下でのキーストロークレイテンシをp50で約63%削減しました。
Lexicalのテーブル選択オブザーバーをDOMルート要素が存在するタイミングで初期化するよう変更し、マウスのドラッグ操作で3セル以上を選択できないリグレッションを修正しました。`registerRootListener` を活用することで、ポインターハンドラーのアタッチ先となるルートが確実に存在する状態で `registerTableSelectionObserver` が呼び出されるようになります。
Lexxy内でコピーしたリンクを貼り付けるとhrefが失われプレーンテキストになるバグを修正。`Clipboard`クラスが`SELECTION_INSERT_CLIPBOARD_NODES_COMMAND`を直接インターセプトし、シングルリンクのペイロードを検知して既存のURLペーストロジックで処理するよう変更されました。合わせてペースト処理の責務が`CommandDispatcher`から`Clipboard`クラスに移管されています。
Lexxyで2枚以上の画像を連続してペーストすると、自動的にギャラリーとしてグループ化されるようになりました。`GalleryUploader` に「カーソル直前にギャラリーまたは画像がある」ことを検出する `selectionIsAfterGalleryEdge` を追加し、`fromPaste` フラグでペースト操作のみにスコープを限定することで、ツールバーからの意図的なアップロードには影響を与えない設計が実現されています。
`editor.flush()` を `getEditorState().read(() => {})` に簡略化したコミットを差し戻しました。簡略化された形式はトランスフォームやRAFスケジュールされたディスパッチといった非同期サイドエフェクトの完了を待機しないため、テストヘルパーとして「エディタが安定した状態に達した」ことを保証できませんでした。空の `editor.update()` を発行して保留中の更新をコミットし、`requestAnimationFrame` でブラウザ描画サイクルの完了を待機する元の実装に戻しています。