e3296104a6
承階段一之下游審計。核心貫穿問題:「透明穿越」判定實有兩套口徑散落各處,抵觸 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 測試全綠、構建乾淨。
180 lines
7.6 KiB
TypeScript
180 lines
7.6 KiB
TypeScript
import type { Finder } from "./core/finder.js";
|
||
import type { LangClass, LocaleConfig, PunctStyle } from "./core/locale.js";
|
||
import type { Bias, BiasTable, BdSubclass, CharClassOverride, Ruleset } from "./core/unicode.js";
|
||
export type { LangClass, PunctStyle, Bias, BiasTable, BdSubclass, CharClassOverride, Ruleset };
|
||
/** 長英文詞包裝設定。 */
|
||
export interface LongWordOptions {
|
||
minLength?: number;
|
||
lang?: string;
|
||
}
|
||
/** 垂懸字避免設定(§6.5.1)。布林 true 等同 { chars: 2 }。 */
|
||
export interface OrphanOptions {
|
||
/** 段末行至少保留之 CJK 實義字數(標點/符號不計;預設 2)。 */
|
||
chars?: number;
|
||
}
|
||
/** 標點擠壓設定(架構 §6.3)。布林 true 等同 { halfWidth: "halt" }。 */
|
||
export interface JiyaOptions {
|
||
/** 半形渲染機制:
|
||
* - "halt"(預設)— 用字型 OpenType `halt`,由字型按字形正確定位,需字型支援;
|
||
* - "margin" — 負 margin 後備模式,供缺 halt 之字型,依字形偏側表收半形。 */
|
||
halfWidth?: "halt" | "margin";
|
||
/** margin 後備模式之字形偏側覆寫表(逐 lang 逐字),覆寫內建參考預設。
|
||
* halt 模式不讀此欄。 */
|
||
bias?: BiasTable;
|
||
}
|
||
/** 分塊功能選擇:選擇器 → 該塊內生效之功能集(覆寫全域)。 */
|
||
export interface BlockRule {
|
||
selector: string;
|
||
features: string[];
|
||
}
|
||
/** createJuzhen 之公開選項。所有欄位皆可選;預設見 normalizeOptions。 */
|
||
export interface JuzhenOptions {
|
||
/** 語言與全角/開明政策。 */
|
||
lang?: {
|
||
/** 無 lang 祖先時之預設語言標籤(預設 "zh-Hant")。 */
|
||
default?: string;
|
||
/** 全域標點擠壓預設:quanjiao(全角式)|kaiming(開明式)|
|
||
* banjiao(半角式)。覆寫內建逐語言預設,可被 policy 逐 lang 覆寫。 */
|
||
style?: PunctStyle;
|
||
/** 逐 lang 覆寫(最高優先)。 */
|
||
policy?: Partial<Record<LangClass, PunctStyle>>;
|
||
};
|
||
/** 自定義字符分類(擴充內置規則集)。把未分類字符或新符號歸入中文/西文/
|
||
* 全寬標點各子類,以覆寫 spacing/jiya 等規則之分類判定(追加語義)。 */
|
||
charClass?: CharClassOverride;
|
||
/** 功能分級標記(§6.5.0a)。段落級功能(禁則/行端/懸掛/垂懸字/長詞斷字)
|
||
* 僅作用於段落級元素;文本級功能(中西間隙/標點擠壓)任意文本皆適用。
|
||
* - `text`:選擇器,命中之子樹**僅文本級**(標題/按鈕/UI 文字典型);亦可
|
||
* 經元素屬性 `data-jz-level="text"`/`"paragraph"` 標記(屬性優先、最近祖先勝)。
|
||
* 預設:塊級元素(blockTags)→ 段落級(全功能)。 */
|
||
level?: {
|
||
text?: string;
|
||
};
|
||
/** justify 原子化:jz-char/jz-jinze 是否以 inline-block 作對齊單一原子
|
||
* (預設 true)。設 false 改 display:inline——用於分頁器(如 Paged.js)之
|
||
* 對齊引擎會把每個 inline-block 邊界當伸縮點、與 CJK inter-ideograph 疊加
|
||
* 致窄欄散架之情形(halt 半形與 jinze nowrap 仍保留)。 */
|
||
justifyAtoms?: boolean;
|
||
/** 作用域限定(§3.6)。 */
|
||
scope?: {
|
||
root?: Element | string;
|
||
include?: string;
|
||
avoid?: string;
|
||
};
|
||
spacing?: boolean;
|
||
longWord?: boolean | LongWordOptions;
|
||
jiya?: boolean | JiyaOptions;
|
||
jinze?: boolean;
|
||
orphan?: boolean | OrphanOptions;
|
||
/** @deprecated 標點懸掛**已移除**(§6.5.6):純負 margin 機制經實測為機制級死路(寬度
|
||
* 守恆矛盾,CSS 接觸不到斷行器),原生 hanging-punctuation 為唯一正解但僅 Safari 支援、
|
||
* 且與本庫 inline-block 原子互斥。此選項仍被接受以保 API 相容,但**無任何效果**。 */
|
||
hanging?: boolean;
|
||
/** @reserved 標點修正(簡/繁正規化)——架構已預留(切片 #7),**當前無任何效果**。
|
||
* 注意:此為功能開關,與 `charClass.biaodian`(標點子類擴充表,語義完全不同)同名,勿混。 */
|
||
biaodian?: boolean;
|
||
/** @reserved 著重號/裝飾——架構已預留(切片 #8),**當前無任何效果**。 */
|
||
emphasis?: boolean;
|
||
/** @reserved Ruby/注音——架構已預留(切片 #8),**當前無任何效果**。 */
|
||
ruby?: boolean;
|
||
/** 分塊功能選擇(§3.6)。 */
|
||
blocks?: BlockRule[];
|
||
styleInlines?: string[];
|
||
blockTags?: string[];
|
||
skipTags?: string[];
|
||
/** 整體(pill)元素選擇器(預設 "code, kbd")。pill 之邊界口徑與 isolate 完全
|
||
* 相同(行為覆蓋),差異僅在內部不可見;同元素雙選擇器命中時 pill 勝出。 */
|
||
pillSelector?: string;
|
||
/** 內外隔離元素選擇器(預設 "mark")。傳 "" 停用(mark 回歸透明)。 */
|
||
isolateSelector?: string;
|
||
skipAttribute?: string;
|
||
isSkipped?: (node: Node) => boolean;
|
||
}
|
||
/** 正規化後之內部選項。 */
|
||
export interface ResolvedOptions {
|
||
locale: LocaleConfig;
|
||
/** 最終字符分類規則集(內置默認 + charClass 覆寫)。 */
|
||
ruleset: Ruleset;
|
||
justifyAtoms: boolean;
|
||
/** 功能分級之 text-level 選擇器(§6.5.0a);null 表示未設。 */
|
||
levelText: string | null;
|
||
scope: {
|
||
root: Element | string | null;
|
||
include: string | null;
|
||
avoid: string | null;
|
||
};
|
||
features: {
|
||
spacing: boolean;
|
||
longWord: boolean;
|
||
jiya: boolean;
|
||
jinze: boolean;
|
||
orphan: boolean;
|
||
biaodian: boolean;
|
||
emphasis: boolean;
|
||
ruby: boolean;
|
||
};
|
||
longWord: {
|
||
minLength: number;
|
||
lang: string;
|
||
};
|
||
/** 垂懸字避免設定(解析後)。 */
|
||
orphan: {
|
||
chars: number;
|
||
};
|
||
/** 擠壓半形渲染設定(halt 預設/margin 後備)。 */
|
||
jiyaConfig: {
|
||
halfWidth: "halt" | "margin";
|
||
bias: BiasTable;
|
||
};
|
||
blocks: BlockRule[];
|
||
finder: {
|
||
styleInlines: Set<string>;
|
||
blockTags: Set<string>;
|
||
skipTags: Set<string>;
|
||
pillSelector: string;
|
||
isolateSelector: string;
|
||
skipAttribute: string | null;
|
||
isSkipped: ((node: Node) => boolean) | null;
|
||
};
|
||
}
|
||
/** render/revert 之統一上下文(§3.3)。 */
|
||
export interface RenderContext {
|
||
root: Element;
|
||
options: ResolvedOptions;
|
||
finder: Finder;
|
||
}
|
||
/**
|
||
* charify 階段之包裝請求:把 node.data[start, end) 包進 build() 造出之元素。
|
||
* 同階段各 pass 之請求須互不重疊(標點/字母段天然不交集)。
|
||
*/
|
||
export interface WrapRequest {
|
||
start: number;
|
||
end: number;
|
||
build: (text: string) => HTMLElement;
|
||
pass: string;
|
||
}
|
||
/** pass 種類。 */
|
||
export type PassKind = "charify" | "standalone";
|
||
/** 功能分級(§6.5.0a):text=任意文本片段皆適用;paragraph=需行/段布局。 */
|
||
export type FeatureLevel = "text" | "paragraph";
|
||
interface BasePass {
|
||
name: string;
|
||
order: number;
|
||
kind: PassKind;
|
||
/** 功能分級(§6.5.0a)。paragraph 級 pass 對 text-level 元素自動跳過。 */
|
||
level: FeatureLevel;
|
||
enabled: (options: ResolvedOptions) => boolean;
|
||
revert: (ctx: RenderContext) => void;
|
||
}
|
||
/** 共享 P1 走訪之字元包裝 pass。 */
|
||
export interface CharifyPass extends BasePass {
|
||
kind: "charify";
|
||
collect: (node: Text, ctx: RenderContext) => WrapRequest[];
|
||
}
|
||
/** 自跑走訪/版面後處理之 pass。 */
|
||
export interface StandalonePass extends BasePass {
|
||
kind: "standalone";
|
||
render: (ctx: RenderContext) => void;
|
||
}
|
||
export type Pass = CharifyPass | StandalonePass;
|