docs: 功能分級 + 垂懸字 + 標點懸掛(同步 main §6.5 落地)

changelog 增「功能分級 + 垂懸字 + 標點懸掛」條目(2026-06-09):功能分級
(文本/段落級)、垂懸字 orphan(方案 C,預設開)、標點懸掛 hanging(按類凸出量、
headless 校準、需 justifyAtoms:false)、行歸屬 bug 修正、兩階段防自我應驗、demo 增區。
index 功能表增 orphan/hanging/level 三列、選項示例與 md twin 同步、各功能標級別。
重建 index.html/changelog.html。
This commit is contained in:
2026-06-09 22:19:29 +08:00
parent 43ac591d6a
commit 38a1cbaace
4 changed files with 82 additions and 18 deletions
+26 -3
View File
@@ -1,11 +1,11 @@
<!-- built with theme=dracula, mode=report, at=2026-06-09T07:22:49Z -->
<!-- built with theme=dracula, mode=report, at=2026-06-09T14:19:17Z -->
<!DOCTYPE html>
<html lang="zh-Hant">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>變更紀錄(Changelog</title>
<meta name="generated-at" content="2026-06-02">
<meta name="generated-at" content="2026-06-09">
<meta name="source" content="cjk-autospace · 變更紀錄">
<style>
/* ---------- Core tokens ----------
@@ -2516,7 +2516,7 @@ main > *:nth-child(n+6) { animation-delay: 0.46s; }
<div class="meta">
<h1>變更紀錄(Changelog</h1>
<p class="subtitle">聚珍(Juzhen)/cjk-autospace 之版本變更紀錄。</p>
<p class="timestamp"><time>2026-06-02</time> · <span>cjk-autospace · 變更紀錄</span></p>
<p class="timestamp"><time>2026-06-09</time> · <span>cjk-autospace · 變更紀錄</span></p>
</div>
<div class="actions">
<button class="btn" type="button" data-action="export-md" title="Copy Markdown to clipboard">Copy MD</button>
@@ -2531,6 +2531,29 @@ main > *:nth-child(n+6) { animation-delay: 0.46s; }
<!-- BODY -->
<p class="lede">聚珍(Juzhen)/cjk-autospace 變更紀錄。<a href="./index.html">← 回文檔</a></p>
<h2>功能分級 垂懸字 標點懸掛 <span class="badge accent">2026-06-09</span></h2>
<p>ARCHITECTURE §6.5 整體計劃落地(功能分級前置 → 垂懸字 → 行端模式重構 → 標點懸掛),共 87 單元測試全綠,懸掛經 headless Chrome 校準。</p>
<h3>新增</h3>
<ul>
<li><strong>功能分級(文本級 vs 段落級)</strong>:把已實作功能切為兩級——<strong>文本級</strong>(中西間隙 <code>spacing</code>、標點擠壓 <code>jiya</code>,任意文本片段適用)與<strong>段落級</strong>(禁則 <code>jinze</code>、垂懸字 <code>orphan</code>、標點懸掛 <code>hanging</code>、長詞 <code>longWord</code>,需行/段布局)。塊級元素預設全功能;<code>data-jz-level="text"</code> 子樹(最近祖先勝)或選項 <code>level:{text:"選擇器"}</code> 標記之元素(標題/按鈕/UI)僅跑文本級。實作為 <code>Finder.levelFor</code><code>levelAllows</code> 各 pass <code>level</code> 元數據;段落級 pass(含 charify 之 longWord)逐元素受閘。<code>spacing</code> 補單元素 root 支援(行內/非塊 root 亦處理)。</li>
<li><strong>垂懸字避免 <code>orphan</code>(方案 C,斷行層無測量,order 70)</strong>:段末行 CJK 實義字 ≥ N(預設 2;標點/符號不計)。把段末「第 N 個實義字起至段末」(含尾隨標點)包入 <code>jz-orphan</code><code>white-space:nowrap</code>),整體不可斷 → 末行恒含之 ⇒ 末行實義字 ≥ N,任意寬度/字型成立、無需量測。邊界字落於行內元素內時綁定整個塊級直接子(跨 <code>jz-jinze</code> 攀爬、不切割)。<strong>預設開 chars=2</strong><code>orphan:false</code> 關);段落級,<code>data-jz-level="text"</code> 跳過。</li>
<li><strong>標點懸掛 <code>hanging</code>(渲染層 layout,§6.5.2</strong>:行尾點號/閉類右凸、行首開類左凸出版心,令正文格線齊邊(自管負 margin)。凸出量按類校準(headless 實測,Noto Sans CJK SC):點號近全形 <code>--jz-hang-stop</code>1em)、括號半形 <code>--jz-hang-bracket</code>(0.5em),CSS 變數可逐字型調。逐 lang 閘:繁體橫排預設不懸掛(降回 squeeze,clreq)。<strong>預設關</strong>;已 baseline 半形之標點不懸掛。lineedge 行端處理升級為可配模式 <code>squeezehangnone</code>(抽出共用偵測碼,squeeze 行為不變)。</li>
</ul>
<h3>修復</h3>
<ul>
<li><strong>行歸屬判定 bug(連帶改善 squeeze</strong>:原以「行盒 <code>top</code>&gt; EPS=4px」判同/異行,但 <code>jz-char</code> 為 inline-block,其行盒 <code>top</code> 與相鄰純文本字形 <code>top</code> 因基線差約 5px、遠小於行高卻超舊容差,致同行誤判為下一行(懸掛句末標點過度觸發之根因)。改判「垂直區間」:下一行=<code>b.top ≥ a.bottom</code>、上一行=<code>b.bottom ≤ a.top</code>(容 2px)。</li>
<li><strong>懸掛自我應驗</strong><code>relayout</code> 改兩階段——先清全部行端效果、於自然佈局量測全部標點、再一次性套用(仿 gapTrim 教訓),終態穩定。</li>
</ul>
<h3>已知限制</h3>
<ul>
<li><strong>懸掛與 inline-block 對齊原子互斥</strong>headless 實測):預設 <code>justifyAtoms:true</code><code>jz-char</code> inline-block)下,懸掛之負 margin 被 justify 重分配吸收、標點不凸出;欲懸掛須設 <code>justifyAtoms:false</code><code>jz-char</code> 改 inline)。容器宜留左右 padding ≥1em 容納凸出標點。</li>
<li><strong>懸掛凸出量逐字型校準</strong>:現預設依 Noto Sans CJK SC;他字型留白比例略異,經 CSS 變數調校。繁體若日後欲開懸掛須另校。</li>
</ul>
<h3>工具</h3>
<ul>
<li><code>demo/resize-check.html</code> 增垂懸字(<code>jz-orphan</code> 描框 + 關閉對照)與標點懸掛(獨立實例、句號密集文本、版心虛線)兩檢查區,工具列增對應顯示開關。</li>
</ul>
<h2>標點挤压一致性 <span class="badge accent">2026-06-04</span></h2>
<h3>新增/修復</h3>
<ul>
+25 -2
View File
@@ -4,7 +4,7 @@
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>變更紀錄(Changelog</title>
<meta name="generated-at" content="2026-06-02">
<meta name="generated-at" content="2026-06-09">
<meta name="source" content="cjk-autospace · 變更紀錄">
<!-- RUNTIME_CSS -->
</head>
@@ -14,7 +14,7 @@
<div class="meta">
<h1>變更紀錄(Changelog</h1>
<p class="subtitle">聚珍(Juzhen)/cjk-autospace 之版本變更紀錄。</p>
<p class="timestamp"><time>2026-06-02</time> · <span>cjk-autospace · 變更紀錄</span></p>
<p class="timestamp"><time>2026-06-09</time> · <span>cjk-autospace · 變更紀錄</span></p>
</div>
<div class="actions">
<!-- ACTIONS -->
@@ -25,6 +25,29 @@
<!-- BODY -->
<p class="lede">聚珍(Juzhen)/cjk-autospace 變更紀錄。<a href="./index.html">← 回文檔</a></p>
<h2>功能分級 垂懸字 標點懸掛 <span class="badge accent">2026-06-09</span></h2>
<p>ARCHITECTURE §6.5 整體計劃落地(功能分級前置 → 垂懸字 → 行端模式重構 → 標點懸掛),共 87 單元測試全綠,懸掛經 headless Chrome 校準。</p>
<h3>新增</h3>
<ul>
<li><strong>功能分級(文本級 vs 段落級)</strong>:把已實作功能切為兩級——<strong>文本級</strong>(中西間隙 <code>spacing</code>、標點擠壓 <code>jiya</code>,任意文本片段適用)與<strong>段落級</strong>(禁則 <code>jinze</code>、垂懸字 <code>orphan</code>、標點懸掛 <code>hanging</code>、長詞 <code>longWord</code>,需行/段布局)。塊級元素預設全功能;<code>data-jz-level="text"</code> 子樹(最近祖先勝)或選項 <code>level:{text:"選擇器"}</code> 標記之元素(標題/按鈕/UI)僅跑文本級。實作為 <code>Finder.levelFor</code><code>levelAllows</code> 各 pass <code>level</code> 元數據;段落級 pass(含 charify 之 longWord)逐元素受閘。<code>spacing</code> 補單元素 root 支援(行內/非塊 root 亦處理)。</li>
<li><strong>垂懸字避免 <code>orphan</code>(方案 C,斷行層無測量,order 70)</strong>:段末行 CJK 實義字 ≥ N(預設 2;標點/符號不計)。把段末「第 N 個實義字起至段末」(含尾隨標點)包入 <code>jz-orphan</code><code>white-space:nowrap</code>),整體不可斷 → 末行恒含之 ⇒ 末行實義字 ≥ N,任意寬度/字型成立、無需量測。邊界字落於行內元素內時綁定整個塊級直接子(跨 <code>jz-jinze</code> 攀爬、不切割)。<strong>預設開 chars=2</strong><code>orphan:false</code> 關);段落級,<code>data-jz-level="text"</code> 跳過。</li>
<li><strong>標點懸掛 <code>hanging</code>(渲染層 layout,§6.5.2</strong>:行尾點號/閉類右凸、行首開類左凸出版心,令正文格線齊邊(自管負 margin)。凸出量按類校準(headless 實測,Noto Sans CJK SC):點號近全形 <code>--jz-hang-stop</code>1em)、括號半形 <code>--jz-hang-bracket</code>(0.5em),CSS 變數可逐字型調。逐 lang 閘:繁體橫排預設不懸掛(降回 squeeze,clreq)。<strong>預設關</strong>;已 baseline 半形之標點不懸掛。lineedge 行端處理升級為可配模式 <code>squeezehangnone</code>(抽出共用偵測碼,squeeze 行為不變)。</li>
</ul>
<h3>修復</h3>
<ul>
<li><strong>行歸屬判定 bug(連帶改善 squeeze</strong>:原以「行盒 <code>top</code>&gt; EPS=4px」判同/異行,但 <code>jz-char</code> 為 inline-block,其行盒 <code>top</code> 與相鄰純文本字形 <code>top</code> 因基線差約 5px、遠小於行高卻超舊容差,致同行誤判為下一行(懸掛句末標點過度觸發之根因)。改判「垂直區間」:下一行=<code>b.top ≥ a.bottom</code>、上一行=<code>b.bottom ≤ a.top</code>(容 2px)。</li>
<li><strong>懸掛自我應驗</strong><code>relayout</code> 改兩階段——先清全部行端效果、於自然佈局量測全部標點、再一次性套用(仿 gapTrim 教訓),終態穩定。</li>
</ul>
<h3>已知限制</h3>
<ul>
<li><strong>懸掛與 inline-block 對齊原子互斥</strong>headless 實測):預設 <code>justifyAtoms:true</code><code>jz-char</code> inline-block)下,懸掛之負 margin 被 justify 重分配吸收、標點不凸出;欲懸掛須設 <code>justifyAtoms:false</code><code>jz-char</code> 改 inline)。容器宜留左右 padding ≥1em 容納凸出標點。</li>
<li><strong>懸掛凸出量逐字型校準</strong>:現預設依 Noto Sans CJK SC;他字型留白比例略異,經 CSS 變數調校。繁體若日後欲開懸掛須另校。</li>
</ul>
<h3>工具</h3>
<ul>
<li><code>demo/resize-check.html</code> 增垂懸字(<code>jz-orphan</code> 描框 + 關閉對照)與標點懸掛(獨立實例、句號密集文本、版心虛線)兩檢查區,工具列增對應顯示開關。</li>
</ul>
<h2>標點挤压一致性 <span class="badge accent">2026-06-04</span></h2>
<h3>新增/修復</h3>
<ul>
+16 -7
View File
@@ -1,4 +1,4 @@
<!-- built with theme=dracula, mode=report, at=2026-06-09T07:22:49Z -->
<!-- built with theme=dracula, mode=report, at=2026-06-09T14:19:17Z -->
<!DOCTYPE html>
<html lang="zh-Hant">
<head>
@@ -2563,10 +2563,13 @@ createJuzhen({ lang: { default: "zh-Hant" } }).render(document.querySelector("ma
<tr><td>長英文詞 <code>longWord</code></td><td><code>[A-Za-z]{6,}</code> 包入 <code>&lt;span lang="en"&gt;</code>,配合消費端 CSS <code>hyphens:auto</code> 原生斷詞(DOM 無連字符 → 複製不污染)。</td></tr>
<tr><td>標點擠壓 <code>jiya</code></td><td>標點 charify 為 <code>jz-char</code> <code>bd-*</code> class。三機制:baseline 政策(全角式不擠/開明式擠句內點號+括號引號/半角式全擠)+行內連續擠壓+行端擠壓。半形預設以字型 OpenType <code>halt</code> 渲染;缺 <code>halt</code> 字型可切 <code>margin</code> 後備模式(見下)。</td></tr>
<tr><td>禁則 <code>jinze</code></td><td>行首行末避頭尾:閉類/點號綁前一字(不置行首)、開類綁後一字(不置行末);只隔離邊界詞元,長引文中段仍可斷。亦為 <code>justify</code> 綁定載體。</td></tr>
<tr><td>垂懸字避免 <code>orphan</code></td><td>段末行 CJK 實義字 ≥ N(預設 2;標點不計)。方案 C(斷行層無測量):段末末 N 實義字+尾隨標點包入 <code>jz-orphan</code><code>white-space:nowrap</code>),整體不可斷 → 末行恒含之 ⇒ 末行 ≥ N。<strong>預設開</strong>;段落級。</td></tr>
<tr><td>標點懸掛 <code>hanging</code></td><td>行尾點號/閉類右凸、行首開類左凸出版心,正文格線齊邊(自管負 margin,按類凸出量:點號 1em/括號 0.5em,CSS 變數可調)。<strong>預設關</strong>;繁體預設不掛(逐 lang 閘)。⚠ 需 <code>justifyAtoms:false</code>(與 inline-block 對齊原子互斥)。段落級。</td></tr>
<tr><td>功能分級 <code>level</code></td><td>文本級(間隙、擠壓——任意文本適用)vs 段落級(禁則、垂懸字、懸掛、長詞——需行/段布局)。塊級預設全功能;<code>data-jz-level="text"</code> 子樹或 <code>level:{text:"選擇器"}</code>(標題/按鈕)僅跑文本級。</td></tr>
<tr><td>justify 單份間距</td><td>每個 <code>jz-char</code><code>inline-block</code> 原子,句末標點只貢獻一份對齊伸縮量。</td></tr>
</tbody>
</table>
<p>後續(架構已預留,不返工):標點懸掛、標點修正(簡↔繁)、著重號、注音/Ruby。</p>
<p>後續(架構已預留,不返工):標點修正(簡↔繁)、著重號、注音/Ruby。</p>
<h2>選項</h2>
<pre><code>createJuzhen({
@@ -2580,7 +2583,10 @@ createJuzhen({ lang: { default: "zh-Hant" } }).render(document.querySelector("ma
longWord: { minLength: 6, lang: "en" },
jiya: true, // 或物件:{ halfWidth: "halt"|"margin", bias }
jinze: true,
justifyAtoms: true, // 分頁器下設 false 改 inline(見下)
orphan: true, // 垂懸字避免(預設開);或 { chars: 2 }false 關
hanging: false, // 標點懸掛(預設關);需 justifyAtoms:false
level: { text: "h1,h2,h3,button,.ui" }, // 此選擇器子樹僅跑文本級功能
justifyAtoms: true, // 分頁器或標點懸掛下設 false 改 inline(見下)
blocks: [ { selector: ".code-comment", features: [ "spacing" ] } ],
});</code></pre>
<p>擠壓政策優先序:<strong>逐 lang <code>policy</code> 全域 <code>style</code> 內建預設</strong>(繁→全角、簡→開明)。</p>
@@ -2722,10 +2728,13 @@ JS 只注入 `jz-*` 結構,所有視覺尺寸在 juzhen.css,缺一不可。
## 已實作功能
- spacing 中西間隙(margin 模型,copy-clean;含 pill 與作者空格正規化)
- longWord 長英文詞包裝(`<span lang>` + hyphens:auto
- jiya 標點擠壓(全角/開明/半角;半形 halt 預設、margin 後備)
- jinze 禁則(避頭尾、justify 綁定)
- spacing 中西間隙(margin 模型,copy-clean;含 pill 與作者空格正規化)【文本級】
- longWord 長英文詞包裝(`<span lang>` + hyphens:auto【段落級】
- jiya 標點擠壓(全角/開明/半角;半形 halt 預設、margin 後備)【文本級】
- jinze 禁則(避頭尾、justify 綁定)【段落級】
- orphan 垂懸字避免(方案 C,末行 CJK 實義字 ≥ N;預設開 chars=2)【段落級】
- hanging 標點懸掛(行端點號/括號凸出版心;預設關;需 justifyAtoms:false;逐 lang 閘繁體不掛)【段落級】
- level 功能分級(文本級 vs 段落級;data-jz-level="text"level:{text:選擇器} 標記標題等僅跑文本級)
- justify 句末標點單份間距(jz-char inline-block 原子)
## 標點半形:halt 預設與 margin 後備
+15 -6
View File
@@ -57,10 +57,13 @@ createJuzhen({ lang: { default: "zh-Hant" } }).render(document.querySelector("ma
<tr><td>長英文詞 <code>longWord</code></td><td><code>[A-Za-z]{6,}</code> 包入 <code>&lt;span lang="en"&gt;</code>,配合消費端 CSS <code>hyphens:auto</code> 原生斷詞(DOM 無連字符 → 複製不污染)。</td></tr>
<tr><td>標點擠壓 <code>jiya</code></td><td>標點 charify 為 <code>jz-char</code> <code>bd-*</code> class。三機制:baseline 政策(全角式不擠/開明式擠句內點號+括號引號/半角式全擠)+行內連續擠壓+行端擠壓。半形預設以字型 OpenType <code>halt</code> 渲染;缺 <code>halt</code> 字型可切 <code>margin</code> 後備模式(見下)。</td></tr>
<tr><td>禁則 <code>jinze</code></td><td>行首行末避頭尾:閉類/點號綁前一字(不置行首)、開類綁後一字(不置行末);只隔離邊界詞元,長引文中段仍可斷。亦為 <code>justify</code> 綁定載體。</td></tr>
<tr><td>垂懸字避免 <code>orphan</code></td><td>段末行 CJK 實義字 ≥ N(預設 2;標點不計)。方案 C(斷行層無測量):段末末 N 實義字+尾隨標點包入 <code>jz-orphan</code><code>white-space:nowrap</code>),整體不可斷 → 末行恒含之 ⇒ 末行 ≥ N。<strong>預設開</strong>;段落級。</td></tr>
<tr><td>標點懸掛 <code>hanging</code></td><td>行尾點號/閉類右凸、行首開類左凸出版心,正文格線齊邊(自管負 margin,按類凸出量:點號 1em/括號 0.5em,CSS 變數可調)。<strong>預設關</strong>;繁體預設不掛(逐 lang 閘)。⚠ 需 <code>justifyAtoms:false</code>(與 inline-block 對齊原子互斥)。段落級。</td></tr>
<tr><td>功能分級 <code>level</code></td><td>文本級(間隙、擠壓——任意文本適用)vs 段落級(禁則、垂懸字、懸掛、長詞——需行/段布局)。塊級預設全功能;<code>data-jz-level="text"</code> 子樹或 <code>level:{text:"選擇器"}</code>(標題/按鈕)僅跑文本級。</td></tr>
<tr><td>justify 單份間距</td><td>每個 <code>jz-char</code><code>inline-block</code> 原子,句末標點只貢獻一份對齊伸縮量。</td></tr>
</tbody>
</table>
<p>後續(架構已預留,不返工):標點懸掛、標點修正(簡↔繁)、著重號、注音/Ruby。</p>
<p>後續(架構已預留,不返工):標點修正(簡↔繁)、著重號、注音/Ruby。</p>
<h2>選項</h2>
<pre><code>createJuzhen({
@@ -74,7 +77,10 @@ createJuzhen({ lang: { default: "zh-Hant" } }).render(document.querySelector("ma
longWord: { minLength: 6, lang: "en" },
jiya: true, // 或物件:{ halfWidth: "halt"|"margin", bias }
jinze: true,
justifyAtoms: true, // 分頁器下設 false 改 inline(見下)
orphan: true, // 垂懸字避免(預設開);或 { chars: 2 }false 關
hanging: false, // 標點懸掛(預設關);需 justifyAtoms:false
level: { text: "h1,h2,h3,button,.ui" }, // 此選擇器子樹僅跑文本級功能
justifyAtoms: true, // 分頁器或標點懸掛下設 false 改 inline(見下)
blocks: [ { selector: ".code-comment", features: [ "spacing" ] } ],
});</code></pre>
<p>擠壓政策優先序:<strong>逐 lang <code>policy</code> 全域 <code>style</code> 內建預設</strong>(繁→全角、簡→開明)。</p>
@@ -200,10 +206,13 @@ JS 只注入 `jz-*` 結構,所有視覺尺寸在 juzhen.css,缺一不可。
## 已實作功能
- spacing 中西間隙(margin 模型,copy-clean;含 pill 與作者空格正規化)
- longWord 長英文詞包裝(`<span lang>` + hyphens:auto
- jiya 標點擠壓(全角/開明/半角;半形 halt 預設、margin 後備)
- jinze 禁則(避頭尾、justify 綁定)
- spacing 中西間隙(margin 模型,copy-clean;含 pill 與作者空格正規化)【文本級】
- longWord 長英文詞包裝(`<span lang>` + hyphens:auto【段落級】
- jiya 標點擠壓(全角/開明/半角;半形 halt 預設、margin 後備)【文本級】
- jinze 禁則(避頭尾、justify 綁定)【段落級】
- orphan 垂懸字避免(方案 C,末行 CJK 實義字 ≥ N;預設開 chars=2)【段落級】
- hanging 標點懸掛(行端點號/括號凸出版心;預設關;需 justifyAtoms:false;逐 lang 閘繁體不掛)【段落級】
- level 功能分級(文本級 vs 段落級;data-jz-level="text"level:{text:選擇器} 標記標題等僅跑文本級)
- justify 句末標點單份間距(jz-char inline-block 原子)
## 標點半形:halt 預設與 margin 後備