fix(split): 攀升對稱守衛——切分前驗整條祖先鏈可切,opaque 中間祖先不再被克隆肢解
下游 .selection-bar-message 病例:段末實義字落於 opaque <button>(巢於可切 <span>), splitElementAt 僅驗頂層 span 可切;splitTreeAfter 逐層克隆 textNode→top 祖先鏈時,中間 opaque button 從未複查即被克隆肢解(複製出 splitoff button、破壞互動元素)。firstText/ lastText 守下潛(遇非可切回 null),但攀升無對稱守衛——此即真正病根,A7 之 per-instance .selection-bar-message 迴避僅治標。 修復:splitTreeAfter 切分前先驗「textNode.parentNode…top(含 top)」整條祖先鏈皆 canSplit, 任一層 opaque/pill/isolate/帶 id 即回 null → 呼叫端綁整個 top(退化但安全,與下潛對稱)。 純新增守衛、無新克隆邏輯;jinze 之 isolateBoundaryToken 因下潛已守,行為不變。附帶亦杜絕 中間祖先帶 id 之克隆重複 id。 測試(129 全綠,+2):末實義字在 opaque button(巢於可切 span)→ 綁整 span、無 splitoff、 button 完整無缺;退化路徑 render→revert 還原一致。亲驗:暫禁守衛則該測試於「無 splitoff」 斷言失敗(證明非空通過、病根屬實)。 文檔:split.ts 檔頭+splitTreeAfter JSDoc+ARCHITECTURE §6.5.1 補「canSplit 須對整條祖先鏈 成立、下潛+攀升雙向守衛」之不變量。dist 重建。
This commit is contained in:
Vendored
+8
@@ -1049,6 +1049,14 @@ var Juzhen = (() => {
|
||||
return s;
|
||||
}
|
||||
function splitTreeAfter(top, textNode, offset, pass, finder) {
|
||||
for (let a = textNode.parentNode; a && a.nodeType === 1; a = a.parentNode) {
|
||||
if (!canSplit(a, finder)) {
|
||||
return null;
|
||||
}
|
||||
if (a === top) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (offset <= 0 && firstText(top, finder) === textNode) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Vendored
+8
@@ -1022,6 +1022,14 @@ function trailingTokenStart(d, anyCjk) {
|
||||
return s;
|
||||
}
|
||||
function splitTreeAfter(top, textNode, offset, pass, finder) {
|
||||
for (let a = textNode.parentNode; a && a.nodeType === 1; a = a.parentNode) {
|
||||
if (!canSplit(a, finder)) {
|
||||
return null;
|
||||
}
|
||||
if (a === top) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (offset <= 0 && firstText(top, finder) === textNode) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user