diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index b7beab7..0046002 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -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**(要間隙與標點半形、不要懸掛/避孤字/避頭尾); + 正文 `
` 預設全功能。
+
+**實現**:
+- 每個 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 個實義字起至段末」(含其間/尾隨標點)包入 `