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:
2026-07-11 13:31:42 +08:00
parent efad638a04
commit 3fbb2904ef
5 changed files with 75 additions and 2 deletions
+5
View File
@@ -539,6 +539,11 @@ class 約定:`bd`(標點)、`bd-open/close/cop/stop/sep/liga`、`cjk`、`j
整元素綁定(安全)——pill 即「作為原子鄰字單位整體綁入 `jz-jinze`」之期望行為(下游問題六:
KaTeX 全樹皆 SPAN,舊版僅憑標籤白名單曾整棵下潛肢解公式);isolate(如背景色 `mark`)切分
有可見接縫故整綁;`MARK` 並自白名單移除。
`canSplit` 須對「切點 `textNode` 至 `top` 之**整條**祖先鏈」成立、非僅 `top`:下潛由
`firstText``lastText` 守(遇非可切回 `null`),**攀升由 `splitTreeAfter` 之對稱守衛守**
(切分前先驗祖先鏈全可切,任一層 opaquepillisolate/帶 id 即放棄、退化整綁)。二者
缺一則 `splitElementAt`orphan:切點取自 `collectSubst`,可穿透 opaque `button`)會令
中間 opaque 祖先被逐層克隆肢解——下游 `.selection-bar-message` 病例。
- **切片**:切片 1 完成行首行末避頭尾(閉類/點號避頭、開類避尾);行首縮排聯動、
更細之禁則對象列後續,沿用同一 `jz-jinze` 結構。
- **已知交互(待瀏覽器精修)**:`lineedge` 之行端偵測需跨 `jz-jinze` 群組邊界向上