Files
cjk-autospace/dist/core/finder.d.ts
T
admin c4e8c6cfd5 fix: 行內元素三分類(透明/隔離/整體)——jinze 下潛肢解 KaTeX 修復(下游問題六);v1 相容層移除
下游問題六:行內公式緊鄰禁則標點時,jinze 為綁鄰字透明下潛 .katex(全樹皆 SPAN,
僅憑標籤名判透明),把公式內側末字連同完整殼層逐層克隆切出(splitoff),上標漂移
錯位;pillSelector/skipAttribute/scope.avoid 三層防護全被刺穿。復現另揭兩向量:
相鄰 pill 時 descend 刺穿致 jz-hws 插入公式內部;pill 內部標點被 jiya charify。

確立三分類(§4.1,所有遍歷原語共用同一判定):
  透明(styleInlines+jz-*)— 穿越/run 延續/可克隆切分;
  隔離(isolateSelector,預設 "mark",新增選項)— 邊界斷 run/斷相鄰/不可切分、
    兩端補隙(同 pill),內部照常處理(自成脈絡);
  整體(pillSelector)— 內部對一切 pass 不可見(insidePill 過濾),禁則時作為
    原子鄰字單位整體綁入 jz-jinze。

機制修復:
  finder — transparentInline 改以元素判定(先排除 pill/isolate/avoid 再查標籤集);
    avoidsSelf 自身判定(isAvoided 僅查祖先,自身命中 avoid 曾被穿越);descend 三態
    (邊界字/無內容續找兄弟/blocked 終止——不可越界跳找更遠之字);eachTextNode
    過濾 pill 內部;collectRuns 隔離元素內部自成 run。
  core/split — canSplit 諮詢 finder(pill/isolate/avoid 不可切 → 退化整綁);
    MARK 移出 SPLITTABLE(背景盒切分有可見接縫)。
  jiyaAdjacency — pill/avoid(自身判定)斷相鄰不下探;isolate 斷而內部自成脈絡。
  spacing Pass B — 邊界選擇器擴為 pill ∪ isolate;巢於 pill 內者跳過。
  orphan — avoidsSelf 邊界;avoid 塊不綁(舊漏判:avoid 子樹內裸文本曾被計數包綁)。

v1 相容層移除(消費端已全數遷移 v2 API):src/compat/v1.ts、IIFE 全域別名
createCjkAutospace、死選項 autospaceClass(經查從未被任何 pass 讀取)一併刪;
I8 退役。dist/compat 殘留聲明清除、dist/core/split.d.ts 補入庫(前次遺漏)。

測試 112 全綠(問題六 ×7、isolate ×6、compat 移除 ×1);headless Chrome 實證
公式完整(splitoff=0、內部無 jz-hws/jz-char)、mark 與公式整體入 jz-jinze。
文檔:README 三分類節+選項示例+功能表(hanging 行補「已移除」);ARCHITECTURE
§4.1 三分類/§3.5 相容層移除/§6.1/§6.3/§6.4/I8 退役。
2026-06-12 17:15:24 +08:00

104 lines
6.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import type { FeatureLevel, ResolvedOptions } from "../types.js";
/** 邏輯 run 中之單一字元來源位置。 */
export interface CharPos {
textNode: Text;
offset: number;
}
/** 一段邏輯文本:剝離樣式後之連續字元,及其逐字回映射。 */
export interface LogicalRun {
text: string;
map: CharPos[];
}
/** pill 鄰接之邏輯鄰字。 */
export interface AdjacentChar {
textNode: Text;
offset: number;
ch: string;
}
export declare class Finder {
private readonly opts;
private readonly styleSet;
private readonly blockSet;
private readonly skipSet;
private readonly pillSelector;
private readonly isolateSelector;
private readonly avoidSelector;
private readonly includeSelector;
private readonly skipAttr;
private readonly userIsSkipped;
private readonly levelTextSelector;
private readonly featureCache;
private readonly levelCache;
constructor(opts: ResolvedOptions);
/** 該元素是否匹配 pill 選擇器(三分類之「整體」:code/kbd/行內公式等)。 */
pillMatches(el: Element): boolean;
/** 該元素是否匹配 isolate 選擇器(三分類之「隔離」:帶盒樣式之行內,如背景色
* <mark>)。邊界口徑同 pill(斷 run/斷相鄰/不可切分/兩端補隙),內部照常處理。 */
isolateMatches(el: Element): boolean;
/** 節點是否位於 pill **內部**(僅查祖先,不含自身)。pill 為整體元素:內部對
* 一切 pass 不可見——不 charify、不收 run、不切分、不於其內插間隙(下游問題六:
* .katex 內部曾被 jiyaspacing 直接變更)。 */
insidePill(node: Node): boolean;
/** 邏輯文本應**透明穿越**之行內元素:樣式行內標籤,以及聚珍自身之 wrap 類
* jz-*jz-hws 間隙、jz-jinze 禁則群組、jz-charjz-inner 標點原子)。穿越這些
* 才能正確找到跨包裝之邏輯鄰字(如 jinze 已把 pill 納入 jz-jinze 後,pill 之左
* 鄰字仍須可達;I7)。
* ⚠ 以**元素**而非標籤名判定:pillisolate/avoid 縱使標籤名在樣式集亦為邊界、
* 不可穿越(下游問題六:KaTeX 全樹皆 SPAN,名字判定令整棵公式被當透明下潛而
* 肢解;scope.avoidskipAttribute 命中自身者同理)。
* ⚠ jz-charjz-inner 必須透明:否則 adjacentLogicalChar 之 descend 遇標點原子既
* 不進入亦不終止、而是**跳過**它續找更前之兄弟(下游 Bug`汉字。<code>` 經 jinze
* 綁定為 `汉<jz-jinze>字<jz-char>。</jz-char></jz-jinze><code>`pill 之左鄰字應
* 為「。」、不補間隙;舊邏輯卻越過 jz-char 取到「字」→ 誤包 jz-hws 於「字。」之間)。
* jz-char 僅含標點(jiya 只 charify biaodian),標點非 pillNeighbor,故透明後 descend
* 回傳標點 → 不補隙,與 collectRuns 之既有透明遞迴一致。jz-inner 亦須列入,否則
* descend 進入 jz-char 後因 jz-inner 不透明而跳過其內標點。 */
private transparentInline;
/** 該元素**自身**是否為 avoid 邊界(內建 skip 名單 / SVG / contentEditable /
* data-jz-skip / skipAttribute / avoid 選擇器 / isSkipped)。isAvoided 沿祖先
* 鏈逐層呼叫本判定;遍歷原語另須對「正要進入」之元素直接判自身——舊版僅查
* 祖先,元素自身命中 avoid 時被當透明穿越(下游問題六:scope.avoid 對 .katex
* 形同虛設)。 */
avoidsSelf(el: Element): boolean;
/** 節點是否處於 avoid 子樹(祖先鏈上任一元素 avoidsSelf;自身不計——元素
* 自身之邊界判定由遍歷原語以 avoidsSelf 執行)。 */
isAvoided(node: Node): boolean;
/** scope.include 啟用判定:未設則恆 true;設則須有命中祖先(opt-in)。 */
inScope(node: Node): boolean;
/** 走訪 root 下所有「可處理」文本節點(已過濾 avoid / pill 內部 / 非作用域)。 */
eachTextNode(root: Element, cb: (node: Text) => void): void;
/** 走訪 root 下所有 block 元素(含 root 本身若為 block)。 */
eachBlock(root: Element, cb: (block: Element) => void): void;
/**
* 收集 block 之邏輯文本 run(架構 §4.1,I7):樣式行內元素與 wrap 類
* jz-* 透明穿越;blockpillavoid/間隙 markerjz-hws)中斷 run。
* 回傳 text.length >= 2 之 run(短於 2 無間隙可言)。
*/
collectRuns(block: Element): LogicalRun[];
/**
* pill 之邏輯鄰字(架構 §4.1):經樣式鏈遞迴,遇 pillblockavoid/間隙
* marker 即止。direction = -1 取前鄰字,+1 取後鄰字。
*/
adjacentLogicalChar(el: Element, direction: -1 | 1): AdjacentChar | null;
/**
* 節點之功能級別(§6.5.0a)。自 node 向上找**最近**之顯式信號:元素屬性
* `data-jz-level="text|paragraph"`(最優先),或命中 `level.text` 選擇器(→ text);
* 皆無則預設 paragraph(全功能)。結果以解析後之元素為鍵快取。
*/
levelFor(node: Node): FeatureLevel;
/**
* 某級別之 pass 是否可作用於該節點(§6.5.0a gate)。文本級 pass 不受限(恆 true);
* 段落級 pass 僅作用於段落級元素(text-level 子樹跳過)。與 featureEnabledFor 疊加:
* level 為粗粒度語義組(先判),blocks/屬性為細粒度逐功能(後調)。
*/
levelAllows(node: Node, passLevel: FeatureLevel): boolean;
/**
* 解析節點對某功能之有效啟用狀態(§3.6,per-node 閘)。向上找最近帶
* 功能指令之祖先(data-juzhen 白名單 / data-juzhen-off 黑名單 / 命中
* blocks 選擇器),無則回退全域 features。結果以元素為鍵快取。
*/
featureEnabledFor(node: Node, name: string): boolean;
private globalFeature;
private resolveFeature;
}