feat(rules): 建立共享排版契约并收束 WebKit 与 Unicode 基线
This commit is contained in:
+158
-123
@@ -5,8 +5,8 @@
|
||||
> 單份間距、跨瀏覽器一致)作調整。
|
||||
>
|
||||
> 倉庫名 `cjk-autospace` 不變;公開 API 為 `createJuzhen` / 全域 `Juzhen`;
|
||||
> 自訂元素前綴 `jz-`。本文件先於任何程式碼,作為實作契約——後續每個功能點
|
||||
> 皆按此落地,以避免不一致與重大返工。
|
||||
> 自訂元素前綴 `jz-`。平台中立之排版語義以 `rules/juzhen-rules.json`、其 Schema 與
|
||||
> 黃金向量為唯一權威源;本文件描述 Web adapter 架構及設計理由,不得覆蓋或分叉共享規則。
|
||||
|
||||
---
|
||||
|
||||
@@ -14,28 +14,38 @@
|
||||
|
||||
1. **第一垂直切片 = v1(cjk-autospace)全部功能**,在新引擎上重寫:中西間隙
|
||||
(Pass A/B)、長英文詞斷詞包裝(Pass C)、開明式標點擠壓(Pass D)。
|
||||
2. **架構須預留完整復刻 Han.css 的能力**:標點修正、擠壓、懸掛、禁則、著重號、
|
||||
注音/ruby、樣式正常化、字體偵測、計數器等,皆能在不改動切片 1 既有程式碼
|
||||
的前提下,以「新增一個 pass」的方式加入。
|
||||
2. **架構保留擴充 Han.css 功能版圖的能力**:標點修正、著重號與注音/ruby 等目前
|
||||
**尚未實作**,若未來加入,須沿既有 pass、finder 與 CSS 契約擴充。標點懸掛之舊
|
||||
負 margin 實作則已移除;四個同名公開選項現階段僅為相容 no-op(見 §3.1)。
|
||||
3. **跨瀏覽器一致**(Chrome/Edge/Safari/WebKit/Firefox):一致性關鍵的版面
|
||||
行為,只用普遍支援的 CSS 原語;原生新特性僅作非必要增強。
|
||||
|
||||
非目標:不依賴 Han.css/Heti 等既有庫之執行期程式碼(MIT 條款下可借鑑其思路與
|
||||
分類表,但成品自含)。
|
||||
|
||||
### 1.1 共享規則與 adapter 所有權
|
||||
|
||||
`rules/` 固定字符分類、語言政策、標點目標寬度、四分之一字間隙、禁斷、段末約束與條件
|
||||
行端結果;Web/CSS 與並行 XeLaTeX 實作均消費同一 `rulesetVersion` 及黃金向量。DOM wrapper、
|
||||
CSS box、observer、瀏覽器量測,以及 TeX node/glue/penalty/macro 都只是各 adapter 可選之
|
||||
非規範機制。共享規則由本項目維護;任何變更須先更新 Schema/向量,再由兩個 adapter 分別
|
||||
證明相容,不能在某一實作內靜默改義。
|
||||
|
||||
---
|
||||
|
||||
## 2. 設計不變量(所有 pass 必須遵守)
|
||||
|
||||
這些不變量是「避免返工」的核心。任何新功能點都不得違反。
|
||||
|
||||
- **I1 複製乾淨(copy-clean)**:注入之間隙與標記一律 `user-select: none`,不改變邏輯
|
||||
`textContent` 的**語義**內容。**一項刻意之常態正規化例外**:`spacing` pass 剝除**贅餘之作者
|
||||
- **I1 複製乾淨(copy-clean)**:`spacing` 以 `jz-hws` 的 margin 留隙,**不插入空白字元**;
|
||||
唯跨節點 IVS 間隙以 `::after` 生成 `U+200B` 斷行機會,且只對此生成內容設
|
||||
`user-select:none`。該偽元素不進入 DOM/`textContent`。**一項刻意之常態正規化例外**:
|
||||
`spacing` pass 剝除**贅餘之作者
|
||||
空格**——CJK↔西文之作者空格吸收為受管 `margin` 間隙、CJK↔CJK(含跨 inline 標籤)之作者
|
||||
空格剝除不留隙。此為 CJK 排版正解(該空格屬裝飾性、間隙改由 margin 承載,語義內容不變),
|
||||
但**確實刪除空格字元、且此刪除不經 `revert` 還原**(見 I6)。除此常態正規化外,預設「只包裝、
|
||||
不改寫文字」;其餘真正會改寫文字之 pass(如標點替換 `biaodian`)須為顯式 opt-in 且可 `revert`
|
||||
還原。(實測 4 份真人撰寫之真實文檔:非空白字元多重集守恆、無任何字元改/增/漏/亂序,唯
|
||||
不改寫文字」。未來若新增會改寫文字之 pass,須為顯式 opt-in 且可 `revert` 還原;目前不存在
|
||||
`biaodian` 改寫 pass。(實測 4 份真人撰寫之真實文檔:非空白字元多重集守恆、無任何字元改/增/漏/亂序,唯
|
||||
贅餘作者空格依此契約規範化。)
|
||||
- **I2 JS/CSS 職責分離**:JS 只注入**結構**(`jz-*` 元素、class、`data-jz-*`
|
||||
屬性);**所有視覺尺寸(margin/letter-spacing/寬度/懸掛位移)一律寫在
|
||||
@@ -107,8 +117,6 @@ jz.disconnect(); // 停止觀察
|
||||
// 全域。style = 全域擠壓預設(quanjiao|kaiming|banjiao);policy = 逐 lang
|
||||
// 覆寫。優先序:policy[lang] > style > 內建預設(繁→全角、簡→開明)。
|
||||
lang: { default: "zh-Hant", style: "kaiming", policy: { "zh-Hant": "quanjiao" } },
|
||||
classPrefix: "jz",
|
||||
|
||||
// 作用域限定(§3.6)
|
||||
scope: {
|
||||
root: document.body, // 預設處理根;render(root) 可逐次覆寫
|
||||
@@ -122,86 +130,97 @@ jz.disconnect(); // 停止觀察
|
||||
longWord: { minLength: 6, lang: "en" },
|
||||
jiya: true, // 標點擠壓
|
||||
jinze: true, // 禁則(含 justify 綁定)
|
||||
hanging: false, // 標點懸掛(預設關,繁體橫排不宜)
|
||||
biaodian: false, // 標點修正(opt-in,可能改寫文字)
|
||||
emphasis: false,
|
||||
ruby: false,
|
||||
|
||||
orphan: { chars: 2 }, // 段末垂懸字避免
|
||||
level: { text: "h1,h2,h3,button,.ui" },
|
||||
// 分塊功能選擇(§3.6):選擇器 → 該塊內生效之功能集(覆寫全域)
|
||||
blocks: [
|
||||
{ selector: ".prose", features: ["spacing", "jiya", "jinze", "emphasis"] },
|
||||
{ selector: ".prose", features: ["spacing", "jiya", "jinze"] },
|
||||
{ selector: ".code-comment", features: ["spacing"] }, // 此塊只間隙、不擠壓
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
`hanging`/`biaodian`/`emphasis`/`ruby` 仍可作為頂層選項傳入,但目前全部為
|
||||
**相容 no-op**:沒有註冊 pass,也不產生 DOM 或 CSS 效果。`hanging` 的舊負 margin
|
||||
實作已移除;其餘三項僅保留為明確標記的未來設計。`charClass.biaodian` 則是已實作的
|
||||
標點字符分類擴充,與同名頂層 no-op 選項無關。
|
||||
|
||||
### 3.2 Pass 管線
|
||||
|
||||
每個功能點是一個 `Pass`:
|
||||
每個功能點是一個 `Pass`;目前型別是 `CharifyPass | StandalonePass`:
|
||||
|
||||
```
|
||||
interface Pass {
|
||||
interface BasePass {
|
||||
name: string;
|
||||
phase: "P0" | "P1" | "P2"; // 遍歷階段,供聚合(§3.4)
|
||||
order: number; // phase 內 apply 先後(charify→擠壓→間隙→…)
|
||||
enabled(options): boolean;
|
||||
// 聚合模式:collect 純判定(同階段共享單次走訪),apply 批次變更
|
||||
collect(node: Text, ctx: RenderContext): Target[];
|
||||
apply(targets: Target[], ctx: RenderContext): void;
|
||||
order: number;
|
||||
kind: "charify" | "standalone";
|
||||
level: "text" | "paragraph";
|
||||
enabled(options: ResolvedOptions): boolean;
|
||||
revert(ctx: RenderContext): void;
|
||||
}
|
||||
|
||||
interface CharifyPass extends BasePass {
|
||||
kind: "charify";
|
||||
collect(node: Text, ctx: RenderContext): WrapRequest[];
|
||||
}
|
||||
|
||||
interface StandalonePass extends BasePass {
|
||||
kind: "standalone";
|
||||
render(ctx: RenderContext): void;
|
||||
}
|
||||
|
||||
type Pass = CharifyPass | StandalonePass;
|
||||
```
|
||||
|
||||
需獨立走訪/layout 之 pass(如 hanging)可改採 `render(ctx)` 整段式而不參與
|
||||
collect 聚合;執行器兩種皆容納。
|
||||
`CharifyPass` 只收集文本包裝請求;`StandalonePass` 自行執行功能所需的掃描或 layout。
|
||||
兩類皆由同一註冊表管理,且均按 `level` 與功能閘判定是否生效。
|
||||
|
||||
`createJuzhen` 內建一個**有序、可開關**的 pass 註冊表。`render` 依 `order` 執行
|
||||
已啟用者。順序契約(避免互相破壞):
|
||||
|
||||
```
|
||||
10 biaodian 標點修正(若開,最先;可能改寫文字/包裝)
|
||||
20 charify 標點字元包裝(jz-char/jz-inner)——擠壓、禁則、著重號之共同前置
|
||||
30 jiya 標點擠壓(依 class + :lang 之 CSS)
|
||||
40 jinze 禁則 + justify 綁定
|
||||
50 spacing 中西間隙(jz-hws)
|
||||
Charify group(共享一次文本遍歷):
|
||||
20 jiya 標點字元包裝(jz-char/jz-inner)
|
||||
60 longWord 長英文詞 lang 包裝
|
||||
70 emphasis 著重號
|
||||
80 ruby 注音/ruby
|
||||
90 hanging 標點懸掛(需 layout,最後;掛 ResizeObserver)
|
||||
|
||||
Standalone group(各自 render):
|
||||
10 levelMark 寫回 resolved block level
|
||||
25 jiyaAdjacency 連續標點擠壓標記
|
||||
40 jinze 禁則 + justify 綁定
|
||||
50 spacing 中西間隙(jz-hws)
|
||||
70 orphan 垂懸字避免
|
||||
90 jiyaLineEdge 行端半形擠壓
|
||||
92 spacingGapTrim 行末中西間隙裁切
|
||||
```
|
||||
|
||||
> 順序理由:charify 先把標點變成可定址的 `jz-char`,擠壓/禁則/著重號才有對象;
|
||||
> 間隙在標點結構穩定後插入;懸掛依賴最終行盒,必須最後且 layout 後再算。
|
||||
執行器先跑完整個 charify group,再跑 standalone group;因此跨組不以數字互相比較。
|
||||
例如 `levelMark` 的 `order=10` 仍在 charify 完成後執行。`revert` 則把全部 pass 合併後
|
||||
按 `order` 逆序呼叫,產物的 ownership/kind 標記負責精確還原。
|
||||
|
||||
### 3.3 RenderContext
|
||||
|
||||
`render`/`revert` 收到統一上下文:`{ root, options, finder, locale }`。各 pass
|
||||
共用同一 `finder` 實例與 locale 解析,確保 avoid 名單、邊界規則、語言判定一致。
|
||||
`render`/`revert` 收到統一上下文:`{ root, options, finder, ownedElements,
|
||||
ownerToken, setOwnedClass }`。各 pass 共用同一 `finder` 與 resolved options;產物透過
|
||||
本實例的 ownership 集合/token 辨識,避免把外來或偽造的 `jz-*` 當成本實例產物。
|
||||
|
||||
### 3.4 遍歷聚合(減少 pass 遍歷次數,I9)
|
||||
|
||||
樸素做法是每個 pass 各跑一次 `TreeWalker`,9 個 pass 即 9 次全樹走訪。為此引入
|
||||
**遍歷階段(phase)**:相容之 pass 共享單次走訪。Pass 介面拆為
|
||||
`collect(node, ctx) → targets[]`(純判定、不變更 DOM)與 `apply(targets, ctx)`
|
||||
(批次變更),執行器:
|
||||
執行器先篩出已啟用的 pass,再按 `kind` 分組。所有 `CharifyPass` 共用一次
|
||||
`finder.eachTextNode(root)`:對每個文本節點依 `order` 呼叫各 pass 的 `collect`,
|
||||
先把節點與 `WrapRequest` 累積為 work,遍歷結束後才由管線執行器統一套用,避免走訪途中改 DOM。
|
||||
之後 `StandalonePass` 依 `order` 各自呼叫 `render(ctx)`:
|
||||
|
||||
```
|
||||
for phase in phases:
|
||||
walk(root) 一次:對每個文本節點,依序呼叫該 phase 內各 pass 的 collect()
|
||||
依 order 對收集到的 targets 做 apply()(兩階段:先收集後變更,避免遍歷中改 DOM)
|
||||
charify = enabled.filter(kind == "charify").sort(order)
|
||||
standalone = enabled.filter(kind == "standalone").sort(order)
|
||||
|
||||
walk(root) 一次:對每個 Text,依序把 charify 的 WrapRequest 收入 work
|
||||
for item in work: applyRequests(item.node, item.requests, ownedElements)
|
||||
for pass in standalone: pass.render(ctx)
|
||||
```
|
||||
|
||||
階段劃分(把 ~9 次走訪壓到 3 次):
|
||||
|
||||
| phase | 內含 pass | 走訪 | 備註 |
|
||||
|---|---|---|---|
|
||||
| P0 文字修正 | biaodian(opt-in) | 1 次 | 唯一可能改寫文字者,須最先 |
|
||||
| P1 行內結構 | charify/jiya 標記/jinze/spacing/longWord/emphasis | 1 次 | 共享走訪,`apply` 依 order |
|
||||
| P2 版面後處理 | hanging | 0 次走訪+layout | 需最終行盒,掛 ResizeObserver |
|
||||
|
||||
> 聚合僅是**執行優化**:邏輯上仍「一功能一 pass」、可獨立開關與 `revert`(I6);
|
||||
> 只是 P1 諸 pass 的 `collect` 在同一走訪內完成。若某 pass 不相容(需獨立走訪),
|
||||
> 退化為自跑一次,不影響正確性。
|
||||
聚合僅是**執行優化**:邏輯上仍「一功能一 pass」、可獨立開關與 `revert`(I6)。
|
||||
需要自訂掃描、跨節點 run 或 layout 的功能採 standalone,不假裝參與 charify 聚合。
|
||||
|
||||
### 3.5 v1 向下相容層 — 已移除(I8 退役)
|
||||
|
||||
@@ -227,14 +246,14 @@ Nvim peek/Claude 模板等 v1 消費端之過渡期。**最後一批 v1 消費
|
||||
|
||||
- **宣告式(屬性,就近繼承)**:
|
||||
- `data-juzhen="spacing jiya"`——白名單:此塊內僅啟用列出之功能。
|
||||
- `data-juzhen-off="jiya hanging"`——黑名單:此塊停用列出者,其餘繼承。
|
||||
- `data-juzhen-off="jiya orphan"`——黑名單:此塊停用列出者,其餘繼承。
|
||||
- `lang`——驅動全角/開明(正交於功能開關)。
|
||||
- **程式式**:`options.blocks: [{ selector, features }]`,選擇器 → 功能集。
|
||||
- **解析(per-node 閘)**:對每個節點,向上找最近帶功能指令之祖先(屬性或命中
|
||||
`blocks` 選擇器者)決定其**有效功能集**,無則用全域 `options`;render 期間以
|
||||
元素為鍵快取。每個 pass 之 `collect(node)` 先問 `featureEnabledFor(node, name)`,
|
||||
否則跳過——**故分塊閘只是單次走訪內的 per-node 判定,不增遍歷、不破壞聚合
|
||||
(§3.4)**。
|
||||
元素為鍵快取。charify pass 在 `collect(node)`、standalone pass 在自身掃描時查詢
|
||||
`featureEnabledFor(node, name)`,未啟用即跳過。分塊閘不改變 §3.4 的兩類 pass
|
||||
執行模型。
|
||||
- **與 revert 之關係**:`revert` 依標記移除某功能產物,**不受當前閘狀態影響**,故
|
||||
「關閉某功能 + revert」恆能乾淨還原(I6)。
|
||||
|
||||
@@ -358,9 +377,9 @@ avoid」用組合入口 `isAvoided`(=自身或祖先,階段二歸一,免
|
||||
- `jinze`:`anyCjk = RegExp(ruleset.cjk)` 決定詞元邊界之 CJK 判定。
|
||||
|
||||
**設計取捨**:① **向下兼容**——不傳 `charClass` 則 `ruleset === DEFAULT_RULESET`,行為與
|
||||
重構前完全一致(現有測試全綠)。② **追加語義(v1)**——擴充內置類(把未分類字符如英文全角
|
||||
句號 `.`U+FF0E 歸入、或納新符號);不支援重分類/移除內置字符(範圍式集合難以挖洞、且需
|
||||
求罕見,列為後續)。③ 結構性 ASCII 不開放(非「字符類型」)。
|
||||
重構前完全一致(現有測試全綠)。② **追加語義(v1)**——擴充尚未內置的字符;全角句點
|
||||
`.`(U+FF0E)已是內置 `BD_STOP`,無須追加。規則不支援重分類/移除內置字符(範圍式集合
|
||||
難以挖洞、且需求罕見,列為後續)。③ 結構性 ASCII 不開放(非「字符類型」)。
|
||||
|
||||
> 借鑑 Han 之 `regex/typeset.js`、`regex/unicode.js` 與 Heti 之分類字集,重寫為
|
||||
> 現代 TS 常數表+可配置規則集。
|
||||
@@ -400,17 +419,17 @@ CSS 端對應以 `:lang()` 選擇器落實 lang 層政策(I2/I3);逐元
|
||||
|
||||
| 元素 | 用途 |
|
||||
|---|---|
|
||||
| `jz-hws` | 中西間隙。`user-select:none`,copy-clean,justify 安全(§8) |
|
||||
| `jz-hws` | 中西間隙,以 margin 留隙、不插入空白字元;跨節點 IVS 例外見 §6.1 |
|
||||
| `jz-char` | 包裝單一標點/漢字,承載 `bd-*` class 與 `data-jz` 屬性 |
|
||||
| `jz-inner` | `jz-char` 內層字面容器,供 margin/letter-spacing 擠壓而不動外盒 |
|
||||
| `jz-cs` | 字間補償空白(連續/行端擠壓用,於行邊界折疊) |
|
||||
| `jz-jinze` | 禁則群組:綁定不可斷單元(亦為 justify 綁定載體) |
|
||||
| `jz-orphan` | 垂懸字避免群組:段末末 N 實義字 `nowrap`(§6.5.1) |
|
||||
| `jz-em` | 著重號載體 |
|
||||
| `jz-ruby`/`jz-rb`/`jz-rt` | ruby/注音 |
|
||||
|
||||
class 約定:`bd`(標點)、`bd-open/close/cop/stop/sep/liga`、`cjk`、`jiya`、
|
||||
`jiya-consecutive`、`hangable`、`jinze-{tou,wei,touwei}` 等。
|
||||
`jz-cs`、`jz-em`、`jz-ruby`/`jz-rb`/`jz-rt` 只屬未實作之未來設計名稱,當前程式
|
||||
不會產生。
|
||||
|
||||
class 約定以現有 CSS/pass 為準:標點採 `bd-open`/`bd-close`/`bd-pause`/
|
||||
`bd-stop`/`bd-middle`/`bd-liga`;擠壓另用 `jz-half`/`jz-half-le` 與 `jz-ink-*`。
|
||||
|
||||
---
|
||||
|
||||
@@ -426,6 +445,9 @@ class 約定:`bd`(標點)、`bd-open/close/cop/stop/sep/liga`、`cjk`、`j
|
||||
包進 `jz-hws`,間隙由 CSS `margin-right`(0.25em)提供——**不注入任何空白字元**。
|
||||
WHERE 之規則沿用 v1 pangu 規則表(已驗證);作者原寫之 CJK↔西文空格一律移除
|
||||
(改由 margin 留隙)。
|
||||
- **跨節點 IVS**:當 base 與 variation selector 分居文本節點時,`jz-hws` 承載完整
|
||||
base+VS 序列;僅此類 gap 以 `::after` 生成 `U+200B` 斷行機會,並只對該偽元素設
|
||||
`user-select:none`。它不是 DOM 文本,不改變 `textContent` 或複製結果。
|
||||
- **為何不用空白字元/inline-block(justify 三難)**:純 CSS 下,CJK↔西文間隙在
|
||||
`justify` 必有一個邊緣瑕疵——①真實空白→被當詞間空白**暴增**;②inline-block 固定
|
||||
寬→罕見落**行首縮排**;③`margin-right`→**行末右緣內縮 0.25em**。選 ③(margin),
|
||||
@@ -628,7 +650,7 @@ class 約定:`bd`(標點)、`bd-open/close/cop/stop/sep/liga`、`cjk`、`j
|
||||
|
||||
#### 6.5.0a 功能分級:文本級 vs 段落級 + 元素標記(前置基礎設施)
|
||||
|
||||
**動機**:懸掛/垂懸字/禁則等為**段落級**功能(依賴行/段布局),不應作用於標題、按鈕、單行
|
||||
**動機**:垂懸字/禁則等為**段落級**功能(依賴行/段布局),不應作用於標題、按鈕、單行
|
||||
或單元素文本;而中西間隙、標點挤压為**文本級**,任意文本片段皆適用且想要。下游須能**方便標記**
|
||||
元素屬哪級。此為貫穿性基礎設施(已實作:`Finder.levelFor`/`levelAllows`、各 pass `level`
|
||||
元數據、選項 `level.text`/屬性 `data-jz-level`)。
|
||||
@@ -638,7 +660,7 @@ class 約定:`bd`(標點)、`bd-open/close/cop/stop/sep/liga`、`cjk`、`j
|
||||
| 級別 | 功能 | 判據/依賴 |
|
||||
|---|---|---|
|
||||
| **文本級** | `spacing`(中西間隙)、`jiya` baseline 政策半形、`jiya` 連續挤压(adjacency) | 僅字符鄰接,對單行/單元素文本即有意義 |
|
||||
| **段落級** | `jinze` 避頭尾、行端(半形/懸掛)、`gapTrim`、`orphan` 垂懸字、`longWord` 斷詞 | 需行/段布局(換行、行端、段末、justify)才有意義 |
|
||||
| **段落級** | `jinze` 避頭尾、行端半形、`gapTrim`、`orphan` 垂懸字、`longWord` 斷詞 | 需行/段布局(換行、行端、段末、justify)才有意義 |
|
||||
| 貫穿 | `charClass`、原子透明、normalize | 基礎設施,不分級 |
|
||||
|
||||
直覺:文本級=「字符級排版」(間隙、標點形態);段落級=「行/段級排版」(換行、行端、段末)。
|
||||
@@ -648,7 +670,7 @@ class 約定:`bd`(標點)、`bd-open/close/cop/stop/sep/liga`、`cjk`、`j
|
||||
- 顯式降級:`data-jz-level="text"` 於元素/祖先 → 該子樹**僅文本級**(段落級功能自動關);
|
||||
`data-jz-level="paragraph"`(或不標)→ 全功能。
|
||||
- 選項 `level?: { text?: string }`:選擇器一鍵標記(如 `level:{ text: "h1,h2,h3,button,.ui" }`)。
|
||||
- 典型:標題/按鈕/UI 文字標 **text-level**(要間隙與標點半形、不要懸掛/避孤字/避頭尾);
|
||||
- 典型:標題/按鈕/UI 文字標 **text-level**(要間隙與標點半形、不要避孤字/避頭尾);
|
||||
正文 `<p>` 預設全功能。
|
||||
|
||||
**實現**:
|
||||
@@ -659,8 +681,8 @@ class 約定:`bd`(標點)、`bd-open/close/cop/stop/sep/liga`、`cjk`、`j
|
||||
- **文本級功能須支持非塊/單元素**:`spacing` 現用 `eachBlock`,須確認 scope root 為 inline/
|
||||
單元素時亦能處理(或放寬走訪),使單行標題等之中西間隙、標點半形生效。
|
||||
|
||||
**與本批關係**:先落地分級(pass 標 level、`levelFor`、`data-jz-level`/選項)→ orphan/hanging
|
||||
註冊為段落級,自動只作用段落級元素、跳過 text-level 者。實作順序插於 6.5.5 之首。
|
||||
**與本批關係**:分級已落地(pass 標 level、`levelFor`、`data-jz-level`/選項);`orphan`
|
||||
等段落級 pass 只作用於段落級元素、跳過 text-level 者。`hanging` 未註冊為 pass。
|
||||
|
||||
**Resolved 分類之 CSS 鉤子(下游建議七,`typeset/levelmark.ts`)**:消費端之段落級 CSS(首要
|
||||
`text-align:justify`)語義上與段落級 pass 應作用於**同一集合**,但 level 判定原僅存內部快取,
|
||||
@@ -707,7 +729,8 @@ class 約定:`bd`(標點)、`bd-open/close/cop/stop/sep/liga`、`cjk`、`j
|
||||
逐塊 `block.querySelector(sel)` 掃整棵子樹判葉塊,累加 O(Σ塊子樹)、深塊嵌套下 O(n²);改以一趟
|
||||
上攀預計算「非葉塊」Set(某塊為另一塊之最近塊祖先 ⟺ 含塊級後代),scope 無涉、與原 querySelector
|
||||
同口徑,行為等價。
|
||||
- **order=70**(charify 20 後、layout pass 前;longWord 60 後)。
|
||||
- **order=70**(standalone 群中位於 jinze 40/spacing 50 後、layout pass 前;整個
|
||||
standalone 群又在含 longWord 60 的 charify 群之後)。
|
||||
- **與禁則協同**:段末範圍可能含 jinze 已綁之 `jz-jinze`;置於 jinze(40) 後,將 jz-jinze 整體
|
||||
納入 jz-orphan,並處理邊界字定位**跨 jz-jinze 攀爬**(實作細節)。免重複包裹。
|
||||
- **CSS**:`jz-orphan { white-space: nowrap; }`。`revert`:wrap-class,隨 `revertPass` 還原。
|
||||
@@ -723,7 +746,7 @@ class 約定:`bd`(標點)、`bd-open/close/cop/stop/sep/liga`、`cjk`、`j
|
||||
lineedge 行端處理只餘 **squeeze**(行端半形):量測各 jz-char 是否在行首(開類)/行末(閉類),
|
||||
加 `jz-half-le`(CSS 同 halt)。兩階段免自我應驗(先 `clearEdge`、自然佈局量測全部、再一次套用);
|
||||
行歸屬以「垂直區間」`lowerLine`/`higherLine` 判定(吸收 inline-block 行盒與純文本基線差 ~5px)。
|
||||
order `90 lineEdge`,`revert` 清 `jz-half-le`。純版面、jsdom 跳過、須瀏覽器目視。
|
||||
order `90 jiyaLineEdge`,`revert` 清 `jz-half-le`。純版面、jsdom 跳過、須瀏覽器目視。
|
||||
|
||||
#### 6.5.3 行端禁則(§6.4)review
|
||||
|
||||
@@ -733,8 +756,9 @@ order `90 lineEdge`,`revert` 清 `jz-half-le`。純版面、jsdom 跳過、須
|
||||
|
||||
#### 6.5.4 統一 pass order
|
||||
|
||||
`20 charify → 25 jiyaAdj → 40 jinze → 50 spacing → 60 longWord → 70 orphan(垂懸字)→
|
||||
90 lineEdge(行端 squeeze 半形)→ 92 gapTrim`。
|
||||
實際分兩群且不跨群比較 order:charify 群為 `20 jiya → 60 longWord`,共用一次文本
|
||||
遍歷;其後 standalone 群為 `10 levelMark → 25 jiyaAdjacency → 40 jinze → 50 spacing →
|
||||
70 orphan → 90 jiyaLineEdge → 92 spacingGapTrim`。
|
||||
|
||||
#### 6.5.5 實作順序與結果
|
||||
|
||||
@@ -774,30 +798,33 @@ order `90 lineEdge`,`revert` 清 `jz-half-le`。純版面、jsdom 跳過、須
|
||||
擠壓 + `hanging-punctuation` 接管懸掛,按 `CSS.supports` 能力門控,整體退出 jz-char 機制。
|
||||
與已移除之負 margin 程式碼**沒有一行可複用**,故不保留休眠碼。
|
||||
|
||||
### 6.6 標點修正(Biaodian normalize)— 後續(opt-in)
|
||||
### 6.6 標點修正(Biaodian normalize)— 未實作之未來設計
|
||||
|
||||
- **Han**:`subst`/`biaodian` 修正標點變體、位置。
|
||||
- **我們**:opt-in pass,預設**只分類+打 class**(不改文字,I1);顯式開啟才做
|
||||
變體替換(簡↔繁標點、全半形修正),且可 `revert`。
|
||||
- **預留**:`order=10` 最先;產物為 `jz-char` 之 class/屬性,與 charify 銜接。
|
||||
- **擬議方向**:未來可設計 opt-in pass;顯式開啟才做變體替換(簡↔繁標點、全半形
|
||||
修正),且須可 `revert`。目前沒有 `biaodian` 模組、pass 或文字改寫。
|
||||
- **相容狀態**:頂層 `biaodian` 選項僅為 no-op;`charClass.biaodian` 是另一套已實作的
|
||||
字符分類擴充 API。
|
||||
|
||||
### 6.7 著重號(Emphasis)— 後續
|
||||
### 6.7 著重號(Emphasis)— 未實作之未來設計
|
||||
|
||||
- **Han**:逐字包裝加圈點。
|
||||
- **我們**:優先用原生 `text-emphasis`(Chrome/FF/Safari 支援良好);需精細
|
||||
控制時退回 `jz-em` 逐字包裝 + CSS 圓點。lang 感知(位置簡繁略異)。
|
||||
- **預留**:charify 能力直接複用。
|
||||
- **擬議方向**:可優先使用原生 `text-emphasis`;若未來需要精細控制,再評估逐字
|
||||
包裝與 lang 感知。`jz-em` 只是設計草案名稱,當前沒有對應模組、pass、DOM 或 CSS。
|
||||
- **相容狀態**:頂層 `emphasis` 選項僅為 no-op。
|
||||
|
||||
### 6.8 注音/Ruby — 後續(最複雜)
|
||||
### 6.8 注音/Ruby — 未實作之未來設計
|
||||
|
||||
- **我們**:基於原生 `<ruby>` + `jz-ruby/jz-rt` 處理注音符號直排與聲調定位。
|
||||
- **預留**:獨立 pass,`order=80`;不影響其餘。
|
||||
- **擬議方向**:未來可基於原生 `<ruby>` 評估注音符號直排與聲調定位;`jz-ruby`/
|
||||
`jz-rb`/`jz-rt` 只是設計草案名稱,當前沒有對應模組、pass、DOM 或 CSS。
|
||||
- **相容狀態**:頂層 `ruby` 選項僅為 no-op。
|
||||
|
||||
### 6.9 樣式正常化/字型歸屬 — 貫穿(CSS)
|
||||
|
||||
- **normalize**:`juzhen.css` 內以 `@supports`/`:where()` 提供 CJK normalize(關閉
|
||||
與託管擠壓重複作用之原生 `text-spacing-trim`/`text-autospace`、間隙 `user-select`
|
||||
等),限 `.juzhen` 域、非破壞。
|
||||
與託管擠壓重複作用之原生 `text-spacing-trim`/`text-autospace` 等),限 `.juzhen`
|
||||
域、非破壞。間隙本身不插入空白;只有跨節點 IVS 的 `U+200B` 偽元素設
|
||||
`user-select:none`。
|
||||
- **字型不歸聚珍管(設計決策,下游回饋)**:曾內建依 `:lang` 之 Noto 字型堆疊作「後備」,
|
||||
但字型屬消費端領域、內建即越權(且曾蓋掉消費端字型釀 bug)。故**移除 `_lang.css`**,
|
||||
逐語言 `font-family` 完全交消費端 CSS。聚珍僅守三項非管理性本分:① `_normalize.css`
|
||||
@@ -825,7 +852,7 @@ order `90 lineEdge`,`revert` 清 `jz-half-le`。純版面、jsdom 跳過、須
|
||||
| Pass A 中西間隙 | `spacing` | `jz-hws`,copy-clean,justify 安全 |
|
||||
| Pass B pill 邊界 | `spacing`(同引擎) | avoid + 鄰接判定 |
|
||||
| Pass C 長英文詞 | `longWord` | `<span lang>` + `hyphens` |
|
||||
| Pass D 開明式擠壓 | `jiya` + `jinze` | 跨瀏覽器負 margin + `:lang` 全角/開明 + justify 綁定 |
|
||||
| Pass D 開明式擠壓 | `jiya` + `jinze` | OpenType `halt`;缺 `halt` 時用 0.5em 盒寬+ink bias 後備;lang/style 政策+justify 綁定 |
|
||||
|
||||
**交付**:可 `build` 出 ESM/IIFE/CSS/d.ts;jsdom 測試覆蓋 DOM 變換正確性
|
||||
(包裝、class、邊界、冪等、copy-clean、lang 政策);justify/簡繁 demo 頁供瀏覽器
|
||||
@@ -907,9 +934,9 @@ inline-block,於 `_jiya.css` 補一條)。實測:halt/margin 兩模式 c
|
||||
支援。**注意**:標點半形預設不用負 `margin`(對居中字形會重疊),改用 `halt`(見
|
||||
§6.3 修訂)。**缺 `halt` 字型之後備**:`jiya.halfWidth:"margin"` 以 `width:0.5em`+
|
||||
字身依偏側(`jz-ink-*`)溢出定位,於 CSS 重現 halt、真半形 advance(見 §6.3)。
|
||||
- **漸進增強(非必要)**:`text-spacing-trim`(Blink)、`hanging-punctuation`
|
||||
(WebKit)、`text-emphasis`、`text-autospace`——僅在支援時錦上添花,缺席不影響
|
||||
基本排版(I4)。
|
||||
- **原生原語政策**:`text-spacing-trim`/`text-autospace` 在現行託管功能中須避免
|
||||
重複作用;`hanging-punctuation`/`text-emphasis` 只屬尚未實作之未來設計評估,
|
||||
不是當前 pass 或驗收項目。
|
||||
- **驗證矩陣**:Chrome(Blink,含 VSCode/Electron)、Safari/WebKit、Firefox
|
||||
(Gecko)。demo 頁含三引擎可開的測試案例。
|
||||
|
||||
@@ -929,8 +956,8 @@ inline-block,於 `_jiya.css` 補一條)。實測:halt/margin 兩模式 c
|
||||
```
|
||||
|
||||
政策:凡屬「一致性關鍵層」者(間隙、擠壓),一律由我們統一處理並禁用對應原生
|
||||
原語;凡屬「漸進增強」且不致衝突者(如 Safari 之 `hanging-punctuation`,在我們未
|
||||
託管懸掛時),方任其生效。此清單隨功能演進維護於 `css/_normalize.css`。
|
||||
原語。`hanging-punctuation` 等未來設計若要加入,須另立能力門控與三引擎驗收,不能
|
||||
從目前保留的 no-op 選項推定已實作。此清單隨功能演進維護於 `css/_normalize.css`。
|
||||
|
||||
---
|
||||
|
||||
@@ -938,31 +965,34 @@ inline-block,於 `_jiya.css` 補一條)。實測:halt/margin 兩模式 c
|
||||
|
||||
```
|
||||
src/
|
||||
index.ts createJuzhen / Juzhen / pass 註冊 / render / revert
|
||||
index.ts createJuzhen / Juzhen / pass 註冊 / observer / render / revert
|
||||
types.ts 公開選項、RenderContext、CharifyPass / StandalonePass 型別
|
||||
core/
|
||||
pass.ts Pass 介面 + 管線執行器
|
||||
finder.ts 遍歷/avoid/charify/邊界感知 replace
|
||||
pass.ts 兩類 pass 之分組、排序與管線執行器
|
||||
finder.ts 遍歷/avoid/charify/邊界感知與跨節點 run
|
||||
unicode.ts 字元類 + 標點子類 + 規則正則表
|
||||
locale.ts 有效 lang / 分類 / 全角開明政策
|
||||
dom.ts jz-* 工廠 / wrap / split / marker / revert
|
||||
dom.ts jz-* 工廠 / ownership / wrap / revert
|
||||
split.ts 禁則/垂懸字之行內元素邊界切分
|
||||
typeset/
|
||||
spacing.ts 中西間隙 [切片 1]
|
||||
longword.ts 長英文詞包裝 [切片 1]
|
||||
jiya.ts 標點擠壓 + 全角開明 [切片 1]
|
||||
jinze.ts 禁則 + justify 綁定 [切片 1 基礎]
|
||||
hanging.ts 標點懸掛 [後續]
|
||||
biaodian.ts 標點修正 [後續]
|
||||
emphasis.ts 著重號 [後續]
|
||||
ruby.ts 注音/ruby [後續]
|
||||
spacing.ts 中西間隙
|
||||
longword.ts 長英文詞包裝
|
||||
jiya.ts 標點擠壓 + 全角開明
|
||||
jinze.ts 禁則 + justify 綁定
|
||||
orphan.ts 段末垂懸字避免
|
||||
levelmark.ts resolved block level 寫回
|
||||
lineedge.ts 行端半形擠壓
|
||||
css/
|
||||
juzhen.css 入口(@import 各 partial)
|
||||
_normalize.css _spacing.css _jiya.css _jinze.css
|
||||
_hanging.css _emphasis.css _ruby.css (字型不內建,交消費端)
|
||||
_normalize.css _spacing.css _jiya.css _jinze.css _orphan.css
|
||||
test/ jsdom DOM 正確性測試
|
||||
demo/ 瀏覽器 layout/justify/簡繁 目視驗證頁
|
||||
build.mjs esbuild:ESM + IIFE + CSS;tsc:d.ts
|
||||
```
|
||||
|
||||
`biaodian.ts`/`emphasis.ts`/`ruby.ts`/`hanging.ts` 及同名 CSS partial 均不存在;
|
||||
前述章節是明確標記的未來設計或已移除機制,不是當前模組清單。
|
||||
|
||||
---
|
||||
|
||||
## 11. 構建與發佈
|
||||
@@ -973,7 +1003,7 @@ build.mjs esbuild:ESM + IIFE + CSS;tsc:d.ts
|
||||
- `dist/juzhen.iife.js`(IIFE,全域 `Juzhen`,原生 `<script>` 直跑——保留
|
||||
build.sh/Nvim peek 之單檔串接消費)
|
||||
- `dist/juzhen.css`(合併之樣式)
|
||||
- `tsc --emitDeclarationOnly` → `dist/juzhen.d.ts`
|
||||
- `tsc --emitDeclarationOnly` → `dist/index.d.ts`
|
||||
- **發佈物**:`dist/` 提交入庫(消費端可直接取單檔/CSS)。
|
||||
|
||||
---
|
||||
@@ -983,7 +1013,7 @@ build.mjs esbuild:ESM + IIFE + CSS;tsc:d.ts
|
||||
- **jsdom(自動)**:驗證 DOM 變換——包裝結構、`bd-*` class、邊界處理、冪等、
|
||||
`revert` 還原、copy-clean(`textContent` 不被注入污染)、lang 政策分支。
|
||||
- **瀏覽器(目視,demo 頁)**:layout 相關**無法**於 jsdom 驗證——justify 單份
|
||||
間距、擠壓視覺量、行端折疊、懸掛、inline-block 基線。demo 頁載 Noto CJK,簡繁
|
||||
間距、擠壓視覺量、行端折疊、inline-block 基線。demo 頁載 Noto CJK,簡繁
|
||||
並列、可切 justify、附選取/複製測試區,於 Chrome/Safari/FF 目視核對。
|
||||
- 文件明確標注哪些結論來自自動測試、哪些待瀏覽器目視。
|
||||
|
||||
@@ -994,7 +1024,8 @@ build.mjs esbuild:ESM + IIFE + CSS;tsc:d.ts
|
||||
新增任一 Han 功能點,標準流程:
|
||||
|
||||
1. 在 `unicode.ts` 補該功能所需字元類/正則(集中、不散落)。
|
||||
2. 新增 `typeset/<feature>.ts`,實作 `Pass`(`render`/`revert`/`order`)。
|
||||
2. 新增 `typeset/<feature>.ts`,依需求實作 `CharifyPass`(`collect`)或
|
||||
`StandalonePass`(`render`),並提供 `revert`/`order`/`level`。
|
||||
3. 在 `index.ts` 註冊,加 `options.<feature>` 開關。
|
||||
4. 在 `css/_<feature>.css` 寫視覺規則(I2:尺寸只在 CSS)。
|
||||
5. 補 jsdom 測試 + demo 案例。
|
||||
@@ -1012,15 +1043,19 @@ build.mjs esbuild:ESM + IIFE + CSS;tsc:d.ts
|
||||
- **R1 justify/inline-block 基線**(§8)。觀察:句末標點在三引擎 justify 下是否
|
||||
單份間距、inline-block 是否擾動基線/行高/行內換行。**預案**:改採 Han 路線之
|
||||
`jz-cs` 補償空白法(標點維持 inline,以行邊界可折疊之 `jz-cs` 控制伸縮側),
|
||||
結構已預留,切換僅換 CSS 機制不動 JS。
|
||||
- **R2 行端擠壓免量測**。觀察:純 CSS(`jz-cs` 折疊)能否在三引擎一致達成行首行尾
|
||||
擠壓。**預案**:退化為「連續+行內擠壓用 CSS(一致)+ 行端擠壓交由 layout pass
|
||||
量測或漸進增強原語」,行端與連續解耦,互不影響。
|
||||
- **R3 標點修正改寫文字 vs I1**。觀察:opt-in 開啟後複製/序列化是否仍可接受。
|
||||
**預案**:預設只分類不改寫;改寫模式強制可 `revert`,並提供「僅視覺替換(CSS
|
||||
但此為未實作設計;採用時須新增 JS 生產/`revert` 契約、CSS 與三引擎驗收,不能只換
|
||||
CSS。
|
||||
- **R2 行端擠壓免量測(未實作設計)**。觀察:純 CSS(未來若引入 `jz-cs` 折疊)能否
|
||||
在三引擎一致達成行首行尾擠壓。採用時同樣須新增 JS 生產/`revert` 契約、CSS 與三引擎
|
||||
驗收;否則維持現行 layout pass 量測或改用能力門控之漸進增強原語,令行端與連續機制
|
||||
解耦。
|
||||
- **R3 標點修正改寫文字 vs I1(未實作之未來設計)**。觀察:若未來加入 opt-in
|
||||
改寫,複製/序列化是否仍可接受。**擬議預案**:預設只分類不改寫;改寫模式強制可
|
||||
`revert`,並提供「僅視覺替換(CSS
|
||||
字符替身)不動文字」之次級模式作折衷。
|
||||
- **R4 注音/ruby 複雜度**。觀察:原生 `<ruby>` 對注音直排/聲調定位之不足。
|
||||
**預案**:獨立 pass、最後做;先以原生 `<ruby>` 覆蓋多數,注音特例再以 `jz-ruby`
|
||||
- **R4 注音/ruby 複雜度(未實作之未來設計)**。觀察:原生 `<ruby>` 對注音直排/
|
||||
聲調定位之不足。**擬議預案**:獨立 pass、最後做;先以原生 `<ruby>` 覆蓋多數,
|
||||
注音特例再評估擬議的 `jz-ruby`
|
||||
子結構補強,不影響其餘 pass。
|
||||
- **R5 DOM 重量**。觀察:長文 charify 之節點數與 render 耗時。**預案**:兩階段批次
|
||||
變更(§3.4)+ 僅包裝必要標點 + 可選分塊/`requestIdleCallback` 漸進渲染;demo
|
||||
|
||||
Reference in New Issue
Block a user