refactor: 專案整理階段二——透明判定單一真相源+跨透明邊界避頭尾+avoid 排除歸一
承階段一之下游審計。核心貫穿問題:「透明穿越」判定實有兩套口徑散落各處,抵觸 finder 檔頭「所有遍歷共用同一判定」之聲明。用戶抉擇:**嚴格白名單**(未知行內標籤=邊界, 透明須顯式登記 styleInlines)。 透明判定單一真相源(2a): ・transparentInline 提升為公開 isTransparent,作邏輯文本流之單一透明判定。 ・collectRuns 改由 isTransparent 判定——未在 styleInlines 之未知行內標籤(如 <button>/自訂 元素)當隔離式邊界(斷 run、內部自成脈絡);isolate 與未知行內併為同一分支。 ・jiyaAdjacency 同改用 finder.isTransparent;adjacentLogicalChar 沿用(僅隨改名)。 ・分層澄清(承 #4 教訓,不強行一統):邏輯文本流統一為 isTransparent;另兩層各有正當之 不同邊界、刻意不併入——結構切分安全(split canSplit:更窄白名單,<a>/ABBR 不可切)與 物理版面(lineedge:getClientRects 物理相鄰,須跨 isolate 找真實鄰接)。檔頭改分層表述。 跨透明元素邊界避頭尾綁定(2b,修下游審計 #3): ・避頭類標點若落於透明元素邊緣(「你好<strong>。世界</strong>」之 。位於 strong 首位、與 「好」不同 DOM parent),舊版 forbiddenBreak 僅認直接 jz-char、render 僅處理 jz-char 之直接 父(strong),外層 p 之「好↔strong」綁定從不被評估 → 。漏綁、可誤置行首。 ・偵測:edgeCharEl 經 finder.isTransparent 穿越透明元素定位邊緣 jz-char。 ・父級處理:render 沿透明元素邊緣上攀納入外層 parent,父集深者先處理。綁定端沿用 isolateBoundaryToken(可切則最小綁定;邊界字為已 charify 之 jz-char 無法越過時退化整綁, 安全、與 <a> 同型)。 avoid 排除機制歸一(2c): ・isAvoided 改單一組合入口(自身即邊界 或 祖先鏈任一邊界),免除「avoidsSelf ‖ isAvoided」 易漏一半之誤用(階段一 #2 即此類);avoidsSelf 保留供遍歷原語逐元素下探自判(免 O(n²))。 ・processPills/orphan/spacing root 收斂為 isAvoided(x);doc 補齊七種 avoid 來源與優先序。 一致性小項(2e):scope.include 型別去多餘 `| null`;PASSES 陣列補註(執行序由 pass.order 分群排序決定);gapTrim 補 featureEnabledFor("spacing"),與 lineEdge relayout 逐節點閘對稱。 評估後不做(2d):五處幂等判斷(jiya 祖先/longword 父/spacing 父/orphan 子/jinze 父)檢查 不同位置關係、無單一 isJz 形狀可全覆蓋;真正缺陷(jinze 二次 render 巢狀)已於階段一以 isJz 修復,其餘各自正確,強行歸一徒增風險(承 #4 教訓:正當差異不硬併)。 新增契約/回歸測試(button 嚴格白名單、跨透明邊界避頭);121 測試全綠、構建乾淨。
This commit is contained in:
@@ -509,6 +509,24 @@ test("jinze 不可切元素退化整綁:<a> 連結整體綁定(不克隆出
|
||||
assert.ok(group && group.querySelector("a"), "整個 <a> 入綁(退化但安全)");
|
||||
});
|
||||
|
||||
test("jinze 跨透明邊界避頭:<strong> 首位句末點號綁至前字(你好<strong>。世界</strong>)", () =>
|
||||
{
|
||||
// 下游審計 #3:。位於 <strong> 首位、與「好」不同 DOM parent,舊版僅處理 jz-char 之
|
||||
// 直接父(strong),外層 p 之「好↔strong」綁定不被評估致 。可誤置行首。修後於外層
|
||||
// 偵測(edgeCharEl 穿越透明元素)並綁定。
|
||||
const { document } = setupDom('<p lang="zh-Hant">你好<strong>。世界</strong>結束</p>');
|
||||
const jz = createJuzhen({ spacing: false, longWord: false });
|
||||
jz.render(document.body);
|
||||
const p = document.querySelector("p");
|
||||
const group = p.querySelector("jz-jinze");
|
||||
assert.ok(group, "產生跨邊界 jz-jinze 綁定組");
|
||||
assert.ok(group.textContent.includes("好") && group.textContent.includes("。"),
|
||||
"。(strong 首位、避頭)與前字「好」綁入同一 nowrap 群組、不置行首");
|
||||
jz.revert(document.body);
|
||||
assert.equal(p.textContent, "你好。世界結束", "revert 還原");
|
||||
assert.equal(countTag(p, "jz-jinze"), 0);
|
||||
});
|
||||
|
||||
test("jinze:revert 還原(無 jz-jinze/jz-char,文字回原樣)", () =>
|
||||
{
|
||||
const { document } = setupDom('<p lang="zh-Hant">他說好。」嗎</p>');
|
||||
@@ -1073,6 +1091,23 @@ test("三分類互斥:pill 自身 avoid(data-jz-skip)→ 兩端不補間
|
||||
"自身 avoid 之 pill 兩端不補間隙");
|
||||
});
|
||||
|
||||
test("嚴格白名單:未知行內標籤(<button>)為邊界,登記 styleInlines 後轉透明(單一真相源)", () =>
|
||||
{
|
||||
// 邏輯文本流之三原語(collectRuns/adjacentLogicalChar/jiyaAdjacency)共用 isTransparent:
|
||||
// 未在 styleInlines 之行內標籤一律當隔離式邊界(斷 run/相鄰、內部自成脈絡)。
|
||||
const a = setupDom("<p>你好<button>Neo</button>寫字</p>");
|
||||
createJuzhen({ jiya: false, longWord: false, jinze: false }).render(a.document.body);
|
||||
assert.equal(countTag(a.document.querySelector("p"), "jz-hws"), 0,
|
||||
"預設 button 為邊界:內外不相鄰、無跨界中西隙");
|
||||
|
||||
// 顯式登記 styleInlines → 透明穿越:好↔Neo、Neo↔寫 兩處中西邊界補隙。
|
||||
const b = setupDom("<p>你好<button>Neo</button>寫字</p>");
|
||||
createJuzhen({ jiya: false, longWord: false, jinze: false, styleInlines: [ "BUTTON" ] })
|
||||
.render(b.document.body);
|
||||
assert.equal(countTag(b.document.querySelector("p"), "jz-hws"), 2,
|
||||
"登記後透明穿越,跨界補中西隙");
|
||||
});
|
||||
|
||||
test("問題六:相鄰兩 pill——descend 不刺穿,jz-hws 絕不插入公式內部", () =>
|
||||
{
|
||||
const { document } = setupDom("<p>公式" + KATEX + KATEX + "文字</p>");
|
||||
|
||||
Reference in New Issue
Block a user