feat: 功能分級(文本級 vs 段落級)+ data-jz-level 標記(§6.5.0a)
整體計劃 §6.5 之前置基礎設施。把已實作功能切為兩級:
- 文本級(spacing 中西間隙、jiya 標點擠壓/鄰接):任意文本片段皆適用。
- 段落級(jinze 禁則、longWord 斷詞、lineEdge 行端、gapTrim):需行/段布局。
標記 API:
- 預設塊級元素 → 段落級(全功能)。
- 元素屬性 data-jz-level="text"|"paragraph"(最近祖先勝)。
- 選項 level:{ text: "選擇器" }(如標題/按鈕一鍵降為文本級)。
實作:
- 每個 pass 加 level 元數據;Finder.levelFor/levelAllows(含快取)。
- charify 段落級 pass(longWord)於共享走訪受閘;standalone 段落級 pass
(jinze/lineEdge/gapTrim)逐元素受閘。文本級不受限。
- spacing 補單元素 root 支援:root 為行內/單一非塊元素時亦處理(標題等
之中西間隙、標點半形生效)。
6 項新單元測試(text-level 跳段落級、選擇器標記、巢狀最近勝、單元素 spacing、
預設段落級回歸)。共 76 測試全綠。
This commit is contained in:
@@ -143,6 +143,7 @@ export const jinzePass: StandalonePass = {
|
||||
name: PASS,
|
||||
kind: "standalone",
|
||||
order: 40,
|
||||
level: "paragraph",
|
||||
enabled: (o) => o.features.jinze,
|
||||
render(ctx: RenderContext): void
|
||||
{
|
||||
@@ -151,6 +152,8 @@ export const jinzePass: StandalonePass = {
|
||||
const parents = new Set<Node>();
|
||||
ctx.root.querySelectorAll("jz-char").forEach((c) =>
|
||||
{
|
||||
// 功能分級閘(§6.5.0a):禁則為段落級,text-level 子樹(標題/按鈕)跳過。
|
||||
if (!ctx.finder.levelAllows(c, "paragraph")) { return; }
|
||||
if (c.parentNode) { parents.add(c.parentNode); }
|
||||
});
|
||||
for (const parent of parents) { processParent(parent, ctx.finder, anyCjk); }
|
||||
|
||||
@@ -55,6 +55,7 @@ export const jiyaPass: CharifyPass = {
|
||||
name: PASS,
|
||||
kind: "charify",
|
||||
order: 20,
|
||||
level: "text",
|
||||
enabled: (o) => o.features.jiya,
|
||||
collect(node, ctx): WrapRequest[]
|
||||
{
|
||||
@@ -123,6 +124,7 @@ export const jiyaAdjacencyPass: StandalonePass = {
|
||||
name: "jiyaAdjacency",
|
||||
kind: "standalone",
|
||||
order: 25,
|
||||
level: "text",
|
||||
enabled: (o) => o.features.jiya,
|
||||
render(ctx: RenderContext): void
|
||||
{
|
||||
|
||||
@@ -122,6 +122,8 @@ function relayout(root: Element, finder: Finder): void
|
||||
|
||||
for (const el of chars)
|
||||
{
|
||||
// 行端為段落級(§6.5.0a):text-level 子樹(單行標題等)不收行端半形。
|
||||
if (!finder.levelAllows(el, "paragraph")) { continue; }
|
||||
if (!finder.featureEnabledFor(el, "jiya")) { continue; }
|
||||
// 行端僅處理括號引號:開類於行首收左、閉類於行尾收右。
|
||||
// **不**處理句末/句內點號(。!?,等)——行尾/行首之點號保持全形,
|
||||
@@ -149,6 +151,7 @@ export const lineEdgePass: StandalonePass = {
|
||||
name: "jiyaLineEdge",
|
||||
kind: "standalone",
|
||||
order: 90,
|
||||
level: "paragraph",
|
||||
enabled: (o) => o.features.jiya,
|
||||
render(ctx: RenderContext): void
|
||||
{
|
||||
@@ -174,7 +177,7 @@ const TRIM = "jz-hws-trim";
|
||||
function trimGaps(root: Element, finder: Finder): void
|
||||
{
|
||||
const gaps = Array.from(root.querySelectorAll("jz-hws")).filter(
|
||||
(g) => finder.inScope(g) && !finder.isAvoided(g),
|
||||
(g) => finder.inScope(g) && !finder.isAvoided(g) && finder.levelAllows(g, "paragraph"),
|
||||
);
|
||||
if (!gaps.length) { return; }
|
||||
|
||||
@@ -207,6 +210,7 @@ export const gapTrimPass: StandalonePass = {
|
||||
name: "spacingGapTrim",
|
||||
kind: "standalone",
|
||||
order: 92,
|
||||
level: "paragraph",
|
||||
enabled: (o) => o.features.spacing,
|
||||
render(ctx: RenderContext): void
|
||||
{
|
||||
|
||||
@@ -23,6 +23,7 @@ export const longWordPass: CharifyPass = {
|
||||
name: PASS,
|
||||
kind: "charify",
|
||||
order: 60,
|
||||
level: "paragraph",
|
||||
enabled: (o) => o.features.longWord,
|
||||
collect(node, ctx): WrapRequest[]
|
||||
{
|
||||
|
||||
@@ -306,6 +306,7 @@ export const spacingPass: StandalonePass = {
|
||||
name: PASS,
|
||||
kind: "standalone",
|
||||
order: 50,
|
||||
level: "text",
|
||||
enabled: (o) => o.features.spacing,
|
||||
render(ctx: RenderContext): void
|
||||
{
|
||||
@@ -318,6 +319,15 @@ export const spacingPass: StandalonePass = {
|
||||
if (!finder.featureEnabledFor(block, PASS)) { return; }
|
||||
processBlock(block, finder, R);
|
||||
});
|
||||
// 文本級功能須支持單元素(§6.5.0a):root 為行內/單一非塊元素(如消費端對
|
||||
// <span> 標題呼叫 render)時 eachBlock 不含之,補處理 root 自身(collectRuns
|
||||
// 遇巢狀 block 仍會 flush,故與上面逐 block 不重複;行內 root 通常無 block 子)。
|
||||
if (!options.finder.blockTags.has(ctx.root.nodeName)
|
||||
&& finder.featureEnabledFor(ctx.root, PASS)
|
||||
&& finder.inScope(ctx.root) && !finder.isAvoided(ctx.root))
|
||||
{
|
||||
processBlock(ctx.root, finder, R);
|
||||
}
|
||||
// Pass B:pill 邊界。
|
||||
processPills(ctx.root, finder, options.finder.pillSelector, R.pillNeighbor);
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user