docs(plan): 標點懸掛 + 垂懸字避免 + 行端禁則 整體實作計劃(ARCHITECTURE §6.5)
將三個相關機制(皆處理行端/段末之標點與字)整合為一份待實作蓝图,並補入前置「功能分級」。 - 6.5.0a 功能分級(前置):把功能分文本級(spacing、jiya baseline/連續挤压——對單行/單元素 即有意義)與段落級(jinze 避頭尾、行端半形/懸掛、gapTrim、orphan 垂懸字、longWord——僅多行 塊有意義)。下游經 data-jz-level="text" / 選項 level.text 標記元素(標題/按鈕只文本級、不要 懸掛/避孤字)。pass 標 level、finder.levelFor、段落級 pass gate 跳過 text-level。 - 6.5.0 四機制分工與邊界:禁則=斷行層趕標點到邊;行端半形/懸掛=渲染層紧排/凸出(互斥模式 lineEnd squeeze|hang);垂懸字=斷行層保末行實義字≥N。正交可疊加。垂懸字 vs 懸掛唯一重疊 「純標點末行」現由方案 C 副作用消除,未來可條件化交懸掛吸回。 - 6.5.1 垂懸字(方案 C):nowrap 綁定段末 N 實義字、數學保證末行≥N、免測 layout、符號不計、 單行段無效、可配 orphanChars;order 70。先做。 - 6.5.2 懸掛:自管負 margin(不用 CSS hanging-punctuation);複用 lineedge layout;行末點號/ 閉類右凸、行首開類左凸;併入行端模式;lang 閘(繁體預設不懸掛);風險=凸出量校準、自我應驗。 - 6.5.3 禁則 review、6.5.4 pass order、6.5.5 分步順序(0 分級→1 垂懸字→2 lineedge 重構→ 3 懸掛→4 邊界→5 文檔)、6.5.6 未決(預設值、凸出量、嵌套邊界字定位)。 僅文檔(無代碼改動)。
This commit is contained in:
+126
-7
@@ -480,14 +480,133 @@ class 約定:`bd`(標點)、`bd-open/close/cop/stop/sep/liga`、`cjk`、`j
|
||||
攀爬找流內鄰字(已於 `edgeRect` 處理 jz-* 包裹之攀出);屬 layout 層,jsdom 無法
|
||||
驗,須瀏覽器目視。
|
||||
|
||||
### 6.5 標點懸掛(Hanging)— 後續
|
||||
### 6.5 標點懸掛 + 垂懸字避免 + 行端禁則 — 整體計劃(待實作)
|
||||
|
||||
- **Han**:行尾標點懸掛出版心,需量測。
|
||||
- **我們**:layout pass——量測各行末,對行末可懸掛標點施負位移;掛
|
||||
`ResizeObserver` 重算。`order=90`(最後)。lang 閘:**繁體橫排預設不懸掛**
|
||||
(clreq;`zh-Hant` 預設關),可開。
|
||||
- **預留**:依賴 `jz-char`/`jz-cs` 既有結構 + 一個 layout 後鉤子;切片 1 的
|
||||
charify 已備妥對象,加入時不動既有 pass。
|
||||
三個相關機制之**整體實作計劃**(同步引入),皆處理「行端/段末之標點與字」;其分層職責須先
|
||||
釐清,以免重複處理或衝突。**前置**須先落地「功能分級」(見 6.5.0a),因懸掛/垂懸字屬段落級、
|
||||
不應作用於標題/按鈕/單行文本。
|
||||
|
||||
#### 6.5.0a 功能分級:文本級 vs 段落級 + 元素標記(前置基礎設施)
|
||||
|
||||
**動機**:懸掛/垂懸字/禁則等為**段落級**功能(依賴行/段布局),不應作用於標題、按鈕、單行
|
||||
或單元素文本;而中西間隙、標點挤压為**文本級**,任意文本片段皆適用且想要。下游須能**方便標記**
|
||||
元素屬哪級。此為貫穿性基礎設施,實作時應提升為正式架構節(§3.x);此處先記計劃。
|
||||
|
||||
**分類(含現有與待加功能)**:
|
||||
|
||||
| 級別 | 功能 | 判據/依賴 |
|
||||
|---|---|---|
|
||||
| **文本級** | `spacing`(中西間隙)、`jiya` baseline 政策半形、`jiya` 連續挤压(adjacency) | 僅字符鄰接,對單行/單元素文本即有意義 |
|
||||
| **段落級** | `jinze` 避頭尾、行端(半形/懸掛)、`gapTrim`、`orphan` 垂懸字、`longWord` 斷詞 | 需行/段布局(換行、行端、段末、justify)才有意義 |
|
||||
| 貫穿 | `charClass`、原子透明、normalize | 基礎設施,不分級 |
|
||||
|
||||
直覺:文本級=「字符級排版」(間隙、標點形態);段落級=「行/段級排版」(換行、行端、段末)。
|
||||
|
||||
**標記 API(下游)**:
|
||||
- 預設推斷:塊級元素(`blockTags`:p/div/li/td…)→ **段落級(全功能)**。
|
||||
- 顯式降級:`data-jz-level="text"` 於元素/祖先 → 該子樹**僅文本級**(段落級功能自動關);
|
||||
`data-jz-level="paragraph"`(或不標)→ 全功能。
|
||||
- 選項 `level?: { text?: string }`:選擇器一鍵標記(如 `level:{ text: "h1,h2,h3,button,.ui" }`)。
|
||||
- 典型:標題/按鈕/UI 文字標 **text-level**(要間隙與標點半形、不要懸掛/避孤字/避頭尾);
|
||||
正文 `<p>` 預設全功能。
|
||||
|
||||
**實現**:
|
||||
- 每個 pass 加元數據 `level: "text" | "paragraph"`。
|
||||
- `finder.levelFor(el)`:查 `data-jz-level` 祖先 + 選項 `level.text` 選擇器 + 預設(塊→段落)。
|
||||
- gate:段落級 pass 對 text-level 元素跳過;文本級 pass 不受限。與現有 `blocks`/
|
||||
`featureEnabledFor` 疊加(level=粗粒度語義組先判,blocks=細粒度逐功能後調)。
|
||||
- **文本級功能須支持非塊/單元素**:`spacing` 現用 `eachBlock`,須確認 scope root 為 inline/
|
||||
單元素時亦能處理(或放寬走訪),使單行標題等之中西間隙、標點半形生效。
|
||||
|
||||
**與本批關係**:先落地分級(pass 標 level、`levelFor`、`data-jz-level`/選項)→ orphan/hanging
|
||||
註冊為段落級,自動只作用段落級元素、跳過 text-level 者。實作順序插於 6.5.5 之首。
|
||||
|
||||
#### 6.5.0 四機制之分工與邊界(核心約定,實作前必讀)
|
||||
|
||||
| 機制 | 作用層 | 唯一職責 | 手段 | 狀態 |
|
||||
|---|---|---|---|---|
|
||||
| 禁則(§6.4 jinze) | **斷行層** | 把標點趕到正確的邊(點號/閉類→行末、開類→行首;連續標點不拆) | nowrap `jz-jinze` | 已實作 |
|
||||
| 行端半形(lineedge 現有) | **渲染層·紧排** | 行首開類/行末閉類**收半形** | `jz-half-le`(halt) | 已實作(將併入行端模式) |
|
||||
| 標點懸掛(本計劃) | **渲染層·凸出** | 行末點號/閉類**右凸**、行首開類**左凸**出版心 | 負 `margin` | 待實作 |
|
||||
| 垂懸字避免(本計劃) | **斷行層** | 段末行 **CJK 實義字數 ≥ N** | nowrap `jz-orphan` | 待實作 |
|
||||
|
||||
- **正交性**:斷行層(禁則/垂懸字,管「哪些字符在哪行、不可斷」)與渲染層(半形/懸掛,管
|
||||
「邊緣標點凸出/收縮」)作用於不同 CSS 維度(`white-space` vs `margin`),可疊加共存。
|
||||
- **協同**:禁則決定標點落行首或行末 → 半形/懸掛對落在邊緣者施收縮/凸出。
|
||||
- **行端「半形 vs 懸掛」互斥**(同屬渲染層):將 lineedge 行端處理升級為可配模式
|
||||
`lineEnd: "squeeze"(現有半形)|"hang"(懸掛)|"none"`,二者共用行端標點偵測碼。
|
||||
- **垂懸字 vs 懸掛邊界**(已研究):垂懸字限「末行實義字 ≥ N」、懸掛限「邊緣標點凸出」。唯一
|
||||
重疊=**純標點末行**(實義字=0):懸掛可「吸回」(不動正文)、垂懸字綁定可「拉字下來」。
|
||||
約定:方案 C(無測量)**附帶**消除純標點末行(副作用,現有益無害);未來若改由懸掛吸回
|
||||
(更優),把 C 綁定**條件化**(僅末行實義字 ∈ [1,N−1] 才綁),借懸掛 layout pass 之測量協同
|
||||
判定,C 不獨立測。末行實義字 ∈ [1,N−1] 僅垂懸字(換行)可解(懸掛不增實義字)。
|
||||
|
||||
#### 6.5.1 垂懸字避免(方案 C,斷行層,無測量)— 先做
|
||||
|
||||
- **目標**:段末行 CJK 實義字數 ≥ N(預設 2;標點/符號不計)。單行段無效。
|
||||
- **機制**:standalone pass,遍歷作用域段落;自段末向前數 N 個 CJK 實義字(`ruleset.cjk` 判定、
|
||||
跳過標點/空白/西文計數),把「第 N 個實義字起至段末」(含其間/尾隨標點)包入 `<jz-orphan>`
|
||||
(CSS `white-space: nowrap`)。
|
||||
- **保證(數學、免測 layout)**:該整體不可斷 → 末行恒含之 ⇒ 末行實義字 ≥ N,任意寬度/字型/
|
||||
瀏覽器成立。平時無害(末行本多字時不改變),僅末行將塌為 <N 字時「拉字下來」。
|
||||
- **選項**:`orphan?: boolean | { chars?: number }`(feature gate;預設**待定**——建議預設開
|
||||
chars=2;per-block 經 `blocks`/`data-jz`)。lang 無關。
|
||||
- **邊界**:段落實義字 < N → 不綁。極窄容器(<N 字寬)→ 整體可能溢出(罕見,文檔提示)。
|
||||
- **order=70**(charify 20 後、layout pass 前;longWord 60 後)。
|
||||
- **與禁則協同**:段末範圍可能含 jinze 已綁之 `jz-jinze`;置於 jinze(40) 後,將 jz-jinze 整體
|
||||
納入 jz-orphan,並處理邊界字定位**跨 jz-jinze 攀爬**(實作細節)。免重複包裹。
|
||||
- **CSS**:`jz-orphan { white-space: nowrap; }`。`revert`:wrap-class,隨 `revertPass` 還原。
|
||||
- **測試**:jsdom 結構(末 N 實義字+尾標點入 jz-orphan;符號不計;單行段/實義字<N 不綁);
|
||||
headless 窄容器拖動驗末行恒 ≥N。
|
||||
|
||||
#### 6.5.2 標點懸掛(Hanging,渲染層,layout)
|
||||
|
||||
- **目標**:行末 bd-stop/pause/close **右凸**出右緣;行首 bd-open **左凸**出左緣。文字邊緣齊。
|
||||
- **不用 CSS `hanging-punctuation`**:瀏覽器支援差(僅 Safari 較全)+聚珍「原子透明」已對 jz-*
|
||||
`hanging-punctuation: none`。故**自管負 margin**。
|
||||
- **機制**:layout pass(複用 lineedge `installLayout`:立即跑+`ResizeObserver(root)`+
|
||||
`fonts.ready` 重算)。量測各標點 jz-char 是否在行首/行末(同 lineedge `edgeRect`/`firstRect`,
|
||||
跨 jz-* 包裹攀爬)。行末點號/閉類加 `jz-hang-r`→`margin-right: -<凸出量>`;行首開類加
|
||||
`jz-hang-l`→`margin-left: -<凸出量>`。
|
||||
- **與現有行端半形整合**:抽出「行端標點偵測」共用,按 `lineEnd` 模式加 `jz-half-le`(squeeze)
|
||||
或 `jz-hang-*`(hang)。重構後 squeeze 行為須不變(回歸測試保綠)。
|
||||
- **凸出量**:取決於標點 advance(halt 半形 0.5em/全形 1em/margin 盒 0.5em)——**需 headless
|
||||
實測校準**(風險)。目標令標點墨色邊緣齊版心。
|
||||
- **lang 閘**:clreq——繁體橫排預設不懸掛(`zh-Hant` 預設 squeeze/none);簡體可開。經
|
||||
`options.hanging` + 逐 lang。預設**待定**(建議預設關,顯式開)。
|
||||
- **自我應驗**:懸掛凸出改行寬→改斷行→行末標點變→自我應驗(同 gapTrim/lineedge)。緩解:
|
||||
仿 gapTrim「起始全施/穩定迭代」教訓。**重點風險**。
|
||||
- **order**:layout pass,最後(整合進 lineedge 行端模式,或 order≈94)。
|
||||
- **CSS**:`jz-char.jz-hang-r{margin-right:-…}` `.jz-hang-l{margin-left:-…}`。`revert` 清 class。
|
||||
- **跨瀏覽器**:負 margin 一致;須 Chrome/Safari/FF 目視(R1/R2)。headless 量測行末標點是否
|
||||
凸出右緣。
|
||||
|
||||
#### 6.5.3 行端禁則(§6.4)review
|
||||
|
||||
- 現狀避頭尾已與懸掛協同(禁則趕標點到邊、懸掛凸出邊緣)。review:①連續標點群組 `jz-jinze`
|
||||
在行端懸掛時凸出最外側;②jz-orphan 與 jz-jinze 嵌套;③lineedge 偵測已跨 jz-jinze 攀爬。
|
||||
預期 jinze 本身不需大改。
|
||||
|
||||
#### 6.5.4 統一 pass order(增補後)
|
||||
|
||||
`20 charify → 25 jiyaAdj → 40 jinze → 50 spacing → 60 longWord → 70 orphan(垂懸字)→
|
||||
90 lineEdge(行端 squeeze/hang)→ 92 gapTrim`。
|
||||
|
||||
#### 6.5.5 實作順序(分步,風險遞增)
|
||||
|
||||
0. **功能分級(前置,6.5.0a)**:pass 標 `level`、`finder.levelFor`、`data-jz-level`/選項
|
||||
`level.text`;段落級 pass gate 跳過 text-level 元素。orphan/hanging 依此確定適用範圍。
|
||||
1. **垂懸字(方案 C)**:純結構、無 layout、最獨立、風險最低 → 先做。
|
||||
2. **lineedge 重構為行端模式**:抽出偵測,現有半形=`squeeze`(行為不變、回歸保綠)。
|
||||
3. **懸掛 hang 模式**:加 `jz-hang-*` + 負 margin + 凸出量校準 + 自我應驗處理 + lang 閘。
|
||||
4. **邊界落地**:純標點末行本期 C 全包;記錄未來條件化路徑。
|
||||
5. **文檔/docs/demo**(resize-check 增懸掛、垂懸字檢查)。
|
||||
|
||||
#### 6.5.6 未決(實作時定)
|
||||
|
||||
- 垂懸字預設開關(建議開 chars=2);懸掛 lang 預設(建議關)。
|
||||
- 懸掛凸出量(headless 校準);懸掛自我應驗策略。
|
||||
- jz-orphan 與 jz-jinze 嵌套之邊界字定位。
|
||||
|
||||
### 6.6 標點修正(Biaodian normalize)— 後續(opt-in)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user