ペースト時にスタイルの正規化を選択範囲に適用
Lexxyエディタでスタイル付きテキストをペーストした直後のタイピングで、ペースト元のスタイルが維持されてしまう問題が修正されました。この変更により、ペースト後の入力は自動的に正規化されたスタイルとフォーマットを引き継ぐようになります。
背景
これまで $canonicalizePastedStyles 関数は、ペーストされたテキストノードのスタイルを正規化するだけでした。テキストノードが選択状態にある場合でも、選択範囲(selection)自体のスタイルとフォーマットは更新されませんでした。このため、ユーザーがペースト直後に文字を入力すると、正規化前のペースト元スタイルが保持される問題がありました。
Lexicalエディタでは、selectionオブジェクトがタイピング時のスタイルとフォーマットを管理しています。テキストノードのスタイルを変更しても、selectionに反映しなければ次の入力には影響しません。
技術的な変更
$canonicalizePastedStyles 関数が拡張され、テキストノードが選択状態の場合にselectionのスタイルとフォーマットも更新するようになりました。
変更前:
function $canonicalizePastedStyles(textNode, canonicalizers = []) {
if ($hasPastedStyles(textNode)) {
$setPastedStyles(textNode, false)
const canonicalizedCSS = canonicalizers.reduce((css, canonicalizer) => {
return canonicalizer.applyCanonicalization(css)
}, textNode.getStyle())
textNode.setStyle(canonicalizedCSS)
}
}
変更後:
function $canonicalizePastedStyles(textNode, canonicalizers = []) {
if ($hasPastedStyles(textNode)) {
$setPastedStyles(textNode, false)
const canonicalizedCSS = applyCanonicalizers(textNode.getStyle(), canonicalizers)
textNode.setStyle(canonicalizedCSS)
const selection = $getSelection()
if (textNode.isSelected(selection)) {
selection.setStyle(textNode.getStyle())
selection.setFormat(textNode.getFormat())
}
}
}
textNode.isSelected(selection) で選択状態を確認し、選択されている場合は selection.setStyle() と selection.setFormat() で正規化後のスタイルとフォーマットを適用します。これにより、ペースト直後の入力が自動的に正規化されたスタイルを引き継ぎます。
追加されたテストケースでは、color: rgb(...) と background-color: white を含むHTMLをペーストした直後に and more... と入力しています。期待される出力は、正規化された color: var(--highlight-1) のみを持つ <mark> タグで全体がラップされた状態です。
test "canonicalizes selection styles on paste" do
find_editor.paste "styled text", html: %(some <span style="color: #{highlight_1_rgb}; background-color: white;">styled text</span>)
find_editor.send " and more..."
assert_equal_html %(<p>some <mark style="color: var(--highlight-1);">styled text and more...</mark></p>), find_editor.value
end
設計判断
正規化ロジックを applyCanonicalizers ヘルパー関数として抽出する判断が採られました。
export function applyCanonicalizers(styles, canonicalizers = []) {
return canonicalizers.reduce((css, canonicalizer) => {
return canonicalizer.applyCanonicalization(css)
}, styles)
}
このリファクタリングにより、reduce を使った正規化処理が2箇所で重複していたコードが共通化されました。関数名も applyCanonicalizers と複数形になっており、複数の正規化処理を順次適用する意図が明確に表現されています。
selectionへのスタイル適用は、textNode.isSelected(selection) による条件分岐で保護されています。選択されていないテキストノードに対しては従来通りノード自体のスタイル更新のみが行われ、余計な処理が実行されません。
まとめ
本PRは、ペースト直後のタイピングで意図しないスタイルが保持される問題を、selectionオブジェクトへの正規化適用で解決しています。テキストノードとselectionの両方を更新することで、ペースト操作とその後の入力が一貫した正規化ルールに従うようになりました。