Files
cjk-autospace/dist/types.d.ts
T
admin e3296104a6 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 測試全綠、構建乾淨。
2026-07-07 21:05:25 +08:00

180 lines
7.6 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 { 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-charjz-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;
};
}
/** renderrevert 之統一上下文(§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;