docs: 行內元素三分類(KaTeX 肢解修復)+ v1 相容層移除(changelog 2026-06-12、index)
changelog 新增 2026-06-12 條目:下游問題六(jinze 透明下潛肢解 KaTeX)三根因修復 (元素判透明/avoidsSelf 自身判定/descend 三態 blocked);三分類確立(透明/隔離 isolateSelector 新增/整體);v1 相容層移除(I8 退役)。md 孿生塊補回填 06-09/06-11 兩條(前兩輪漏更)。 index:頭注 v1 改已移除;功能表 spacing/jinze 行補三分類口徑;新增「行內元素三分類」 節(對照表+pill 建議);v1 節改移除記錄;md 孿生塊同步。 重建 changelog.html/index.html。
This commit is contained in:
+90
-3
@@ -1,11 +1,11 @@
|
||||
<!-- built with theme=dracula, mode=report, at=2026-06-11T08:19:00Z -->
|
||||
<!-- built with theme=dracula, mode=report, at=2026-06-12T09:20:32Z -->
|
||||
<!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-09">
|
||||
<meta name="generated-at" content="2026-06-12">
|
||||
<meta name="source" content="cjk-autospace · 變更紀錄">
|
||||
<style>
|
||||
/* ---------- Core tokens ----------
|
||||
@@ -2531,7 +2531,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-09</time> · <span>cjk-autospace · 變更紀錄</span></p>
|
||||
<p class="timestamp"><time>2026-06-12</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>
|
||||
@@ -2546,6 +2546,28 @@ main > *:nth-child(n+6) { animation-delay: 0.46s; }
|
||||
<!-- BODY -->
|
||||
<p class="lede">聚珍(Juzhen)/cjk-autospace 變更紀錄。<a href="./index.html">← 回文檔</a></p>
|
||||
|
||||
<h2>行內元素三分類(KaTeX 肢解修復)+ v1 相容層移除 <span class="badge accent">2026-06-12</span></h2>
|
||||
<p>下游問題六(KaTeX 公式被禁則綁定肢解)修復:確立行內元素<strong>三分類</strong>口徑(透明/隔離/整體),所有遍歷原語共用同一判定。v1 相容層隨最後一批消費端遷移完成而移除。單元測試 101→112 全綠。</p>
|
||||
<h3>修復(下游問題六)</h3>
|
||||
<ul>
|
||||
<li><strong>Bug:jinze 透明下潛肢解 pill</strong>。行內公式緊鄰禁則標點(<code>…$x$,</code>)時,jinze 為綁鄰字下潛 <code>.katex</code>——KaTeX 全樹皆 <code><span></code>,舊版<strong>僅憑標籤名判透明</strong>,把公式內側末字連同完整殼層逐層克隆切出(<code>splitoff</code>),上標漂移錯位;<code>pillSelector</code>/<code>skipAttribute</code>/<code>scope.avoid</code> 三層防護全被刺穿。復現另揭兩向量:相鄰兩 pill 時 <code>descend</code> 刺穿致 <code>jz-hws</code> 插入公式內部;pill 內部標點被 jiya charify。</li>
|
||||
<li><strong>根因一:判透明以標籤名而非元素</strong>。<code>transparentInline(el)</code> 改以元素判定——先排除 pill/isolate/avoid 再查標籤集;<code>core/split</code> 之 <code>canSplit</code> 諮詢 finder(pill/isolate/avoid 一律不可切 → 退化<strong>整元素綁定</strong>——pill 即「作為原子鄰字單位整體綁入 <code>jz-jinze</code>」之期望行為)。</li>
|
||||
<li><strong>根因二:avoid 僅查祖先</strong>。新增 <code>avoidsSelf(el)</code> 自身判定——元素自身命中 <code>scope.avoid</code>/<code>skipAttribute</code>/<code>data-jz-skip</code> 時即為邊界(舊版被當透明穿越,防護形同虛設)。</li>
|
||||
<li><strong>根因三:descend 越界跳找</strong>。<code>adjacentLogicalChar</code> 之 <code>descend</code> 改三態(邊界字/無內容續找兄弟/<strong>blocked</strong> 終止)——遇 pill/isolate/avoid 不可跳過續找更遠之字。</li>
|
||||
<li><strong>pill 內部全面不可見</strong>:<code>eachTextNode</code> 過濾 <code>insidePill</code>——pill 內部不 charify、不插間隙、不做長詞包裝(舊版消費端自加之 pill 如 <code>.katex</code> 內部曾被直接變更)。</li>
|
||||
</ul>
|
||||
<h3>新增:行內元素三分類(§4.1)</h3>
|
||||
<ul>
|
||||
<li><strong>透明</strong>(<code>styleInlines</code> + <code>jz-*</code>):穿越、run 延續、禁則可克隆切分——<code>strong</code>/<code>em</code> 等純樣式。</li>
|
||||
<li><strong>隔離</strong>(<code>isolateSelector</code>,<strong>新增選項</strong>,預設 <code>"mark"</code>,<code>""</code> 停用):邊界斷 run/斷相鄰/不可切分、<strong>兩端補隙</strong>(同 pill),<strong>內部照常處理</strong>(自成脈絡)——帶背景色之 <code><mark></code> 等盒樣式行內(切分背景盒有可見接縫,<code>MARK</code> 並自可切白名單移除)。</li>
|
||||
<li><strong>整體</strong>(<code>pillSelector</code>):內部對一切 pass 不可見,禁則時整體綁入——<code>code</code>/<code>kbd</code>、行內公式 <code>.katex</code>、引用標 <code>.cite-ref</code> 等宜納入。</li>
|
||||
<li>spacing Pass B 邊界選擇器擴為 <strong>pill ∪ isolate</strong>;jiyaAdjacency 對 isolate 斷相鄰而內部自成脈絡;orphan 補 avoid 塊不綁(舊漏判)。</li>
|
||||
</ul>
|
||||
<h3>移除:v1 相容層</h3>
|
||||
<ul>
|
||||
<li>最後一批 v1 消費端遷移 v2 API 完成,<code>src/compat/v1.ts</code>(<code>createCjkAutospace</code>)、IIFE 全域別名、死選項 <code>autospaceClass</code>(經查從未被任何 pass 讀取)一併移除;不變量 I8 退役。v1 原始碼保存於 <code>v1-autospace</code> 分支。</li>
|
||||
</ul>
|
||||
|
||||
<h2>行內排版透明性修復 + 標點懸掛移除 <span class="badge accent">2026-06-11</span></h2>
|
||||
<p>修復兩個下游 Bug(同源於「行內元素對排版透明」原則在各遍歷原語口徑不一)+三項同根隱患審計;並把上一輪「停用」之標點懸掛<strong>正式移除</strong>(負 margin 程式碼/CSS/型別一併刪)。單元測試 90→101 全綠。</p>
|
||||
<h3>修復</h3>
|
||||
@@ -2699,6 +2721,36 @@ main > *:nth-child(n+6) { animation-delay: 0.46s; }
|
||||
<script type="text/markdown" id="md-source">
|
||||
# 變更紀錄(Changelog)— 聚珍(Juzhen)/cjk-autospace
|
||||
|
||||
## 行內元素三分類(KaTeX 肢解修復)+ v1 相容層移除(2026-06-12)
|
||||
|
||||
### 修復(下游問題六)
|
||||
- jinze 透明下潛肢解 pill:行內公式緊鄰禁則標點時,KaTeX 全樹皆 span、舊版僅憑標籤名判透明,公式內側末字連殼層被克隆切出(splitoff)、上標漂移;pillSelector/skipAttribute/scope.avoid 三層防護全被刺穿。另修:相鄰 pill 時 descend 刺穿致 jz-hws 插入公式內部;pill 內部標點被 jiya charify。
|
||||
- 根因修復:transparentInline 以元素判定(先排除 pill/isolate/avoid);canSplit 諮詢 finder(不可切→整元素綁定,pill 即整體綁入 jz-jinze);avoidsSelf 自身判定(舊僅查祖先);descend 三態(blocked 終止、不越界跳找);eachTextNode 過濾 pill 內部。
|
||||
|
||||
### 新增:行內元素三分類(§4.1)
|
||||
- 透明(styleInlines+jz-*):穿越、run 延續、可克隆切分。
|
||||
- 隔離(isolateSelector 新增選項,預設 "mark","" 停用):邊界斷 run/斷相鄰/不可切分、兩端補隙,內部照常處理。MARK 自可切白名單移除(背景盒切分有可見接縫)。
|
||||
- 整體(pillSelector):內部對一切 pass 不可見,禁則時整體綁入。spacing Pass B 邊界選擇器擴為 pill ∪ isolate。
|
||||
|
||||
### 移除:v1 相容層
|
||||
- createCjkAutospace/IIFE 全域別名/死選項 autospaceClass 一併刪(消費端已全數遷移 v2 API);I8 退役。測試 101→112 全綠。
|
||||
|
||||
## 行內排版透明性修復 + 標點懸掛移除(2026-06-11)
|
||||
|
||||
### 修復
|
||||
- pill 前標點誤加間隙(漢字。\<code\>):descend 越過 jz-char 取更前字誤補 jz-hws;isTransparentInline 補 JZ-CHAR/JZ-INNER。
|
||||
- 行內樣式元素整體掉行:core/split 克隆切分(splitoff),禁則/垂懸字只綁邊界詞元、revert 延後階段併回。
|
||||
- 審計 T4 lineedge 攀爬集由 styleInlines 派生;T5 jiyaAdjacency 遇 pill 斷相鄰;T6 spacing processPills 補 per-node 功能閘。
|
||||
|
||||
### 移除
|
||||
- 標點懸掛由停用轉正式移除(寬度守恆矛盾,CSS 接觸不到斷行器;負 margin 程式碼/CSS/型別一併刪;hanging 選項保留為 no-op)。
|
||||
|
||||
## 功能分級 + 垂懸字(2026-06-09)
|
||||
|
||||
- 功能分級:文本級(spacing/jiya)vs 段落級(jinze/orphan/longWord);data-jz-level/level:{text} 標記。
|
||||
- 垂懸字避免 orphan(方案 C,斷行層無測量,預設開 chars=2)。
|
||||
- lineedge 行端半形(squeeze)+行歸屬判定改垂直區間比較;標點懸掛實作後確認不可行、全面停用。
|
||||
|
||||
## 標點挤压一致性(2026-06-04)
|
||||
|
||||
### 新增/修復
|
||||
@@ -4965,6 +5017,12 @@ window.__JUZHEN_PUNCT__="quanjiao";window.__JUZHEN_JUSTIFY_ATOMS__=true;window._
|
||||
規則,結構性綁定故與分頁無關(academic 安全)。
|
||||
· longWord — 長英文段包 `<span lang="en">`,配合 `[lang|="en"]`
|
||||
之 `hyphens: auto` 由瀏覽器原生斷詞(DOM 無連字符)。
|
||||
· orphan — 垂懸字避免:段末行 CJK 實義字 < 2 時把末 2 字綁
|
||||
`<jz-orphan nowrap>`,杜絕孤字成行(標點不計);屬段落
|
||||
級、斷行層無測量,分頁安全。lib 預設即開。
|
||||
功能分級(level):上列 jinze/longWord/orphan 為「段落級」,僅作用於
|
||||
段落類元素;標題與 UI chrome 經 `level.text` 標為「文本級」,只跑 spacing
|
||||
+jiya(見下方 createJuzhen)。標點懸掛 hanging 上游已停用,不採用。
|
||||
|
||||
標點擠壓政策(開明/全角)為「逐模式」而非逐語言:build.sh 依模式
|
||||
注入 `window.__JUZHEN_PUNCT__`(academic → kaiming 開明式;其餘 →
|
||||
@@ -5059,10 +5117,39 @@ window.__JUZHEN_PUNCT__="quanjiao";window.__JUZHEN_JUSTIFY_ATOMS__=true;window._
|
||||
catch (e) { /* getComputedStyle 不可用時退預設 halt */ }
|
||||
window.Juzhen.createJuzhen({
|
||||
pillSelector: "code, kbd, mark, .badge, .katex, .cite-ref",
|
||||
/* KaTeX 整樹另列 avoid 範圍:公式內部任何文本都不得被聚珍觸碰。
|
||||
⚠ 已知上游缺陷(已記 ~/.scratch/juzhen-feedback.md,待上游修):jinze
|
||||
綁定公式緊鄰禁則標點(`$x$,`/`($x$`)時,adjacentLogicalChar 經
|
||||
styleInlines 透明下潛(SPAN 在透明清單、KaTeX 全樹皆 span)刺穿 pill/
|
||||
skipAttribute/scope.avoid 三層防護,把公式內側末字(如 10^{-17} 之
|
||||
「17」)連同克隆殼層切出 jz-jinze——上標漂位、公式碎裂。消費端無乾淨
|
||||
攔法(三種防護實測均被刺穿),等上游令 descend 尊重邊界後,本行 avoid
|
||||
即為正式屏障。 */
|
||||
scope: { avoid: ".katex" },
|
||||
skipAttribute: "data-md-key",
|
||||
spacing: true,
|
||||
jinze: true,
|
||||
longWord: true,
|
||||
/* level(功能分級,聚珍 §6.5.0a):段落級功能(禁則 jinze/垂懸 orphan/長詞
|
||||
斷字 longWord)需行/段布局,對標題、控制項、純 UI chrome 套用無益甚至有害
|
||||
(標題被 longWord 連字斷開、單行 chrome 字串被 orphan/jinze 綁成 nowrap 而
|
||||
溢出)。故把「非段落類」標籤標為文本級(`level.text`),僅保留文本級功能(中西
|
||||
間隙 spacing+標點擠壓 jiya);真正的段落(p/li/td/blockquote/figcaption/
|
||||
.lede/.marginalia/.references/.appendix……)不命中、維持全功能。最近祖先勝,
|
||||
故標題與 chrome 容器一旦命中,其後代(如 .slide-title 內的 .section-name/
|
||||
.slide-dot、.title-page 內的作者欄)一併降級。選擇器涵蓋三模式;某模式不存在之
|
||||
class 自然不命中。 */
|
||||
level: {
|
||||
text: [
|
||||
"h1, h2, h3, h4, h5, h6", // 全部標題(含 deck-/section-/slide-/tp-/toc-title)
|
||||
"button, [role=button], .btn", // 控制項
|
||||
".subtitle, .section-subtitle, .deck-subtitle", // 副標題
|
||||
".meta, .timestamp, .actions, .page", // report chrome
|
||||
".cover-block, .slide-foot, .toc-list", // slides 封面/頁尾/目錄條目
|
||||
".deck-nav, .deck-counter, .deck-author, .deck-institute, .deck-date, .deck-actions, .deck-stage",
|
||||
".title-page, .toc-page, .simple-title", // academic 前置 chrome(含 tp-/st- 欄位)
|
||||
].join(", "),
|
||||
},
|
||||
/* orphan(垂懸字避免,聚珍 §6.5.1):段末行 CJK 實義字 < 2 時,把末 2 字
|
||||
包入 `<jz-orphan white-space:nowrap>` 拉下首字,杜絕「孤字成行」。判定僅計
|
||||
漢字實義字(標點/符號不納入)。方案 C 屬斷行層、無測量,結構性 nowrap 同
|
||||
|
||||
+54
-2
@@ -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-09">
|
||||
<meta name="generated-at" content="2026-06-12">
|
||||
<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-09</time> · <span>cjk-autospace · 變更紀錄</span></p>
|
||||
<p class="timestamp"><time>2026-06-12</time> · <span>cjk-autospace · 變更紀錄</span></p>
|
||||
</div>
|
||||
<div class="actions">
|
||||
<!-- ACTIONS -->
|
||||
@@ -25,6 +25,28 @@
|
||||
<!-- BODY -->
|
||||
<p class="lede">聚珍(Juzhen)/cjk-autospace 變更紀錄。<a href="./index.html">← 回文檔</a></p>
|
||||
|
||||
<h2>行內元素三分類(KaTeX 肢解修復)+ v1 相容層移除 <span class="badge accent">2026-06-12</span></h2>
|
||||
<p>下游問題六(KaTeX 公式被禁則綁定肢解)修復:確立行內元素<strong>三分類</strong>口徑(透明/隔離/整體),所有遍歷原語共用同一判定。v1 相容層隨最後一批消費端遷移完成而移除。單元測試 101→112 全綠。</p>
|
||||
<h3>修復(下游問題六)</h3>
|
||||
<ul>
|
||||
<li><strong>Bug:jinze 透明下潛肢解 pill</strong>。行內公式緊鄰禁則標點(<code>…$x$,</code>)時,jinze 為綁鄰字下潛 <code>.katex</code>——KaTeX 全樹皆 <code><span></code>,舊版<strong>僅憑標籤名判透明</strong>,把公式內側末字連同完整殼層逐層克隆切出(<code>splitoff</code>),上標漂移錯位;<code>pillSelector</code>/<code>skipAttribute</code>/<code>scope.avoid</code> 三層防護全被刺穿。復現另揭兩向量:相鄰兩 pill 時 <code>descend</code> 刺穿致 <code>jz-hws</code> 插入公式內部;pill 內部標點被 jiya charify。</li>
|
||||
<li><strong>根因一:判透明以標籤名而非元素</strong>。<code>transparentInline(el)</code> 改以元素判定——先排除 pill/isolate/avoid 再查標籤集;<code>core/split</code> 之 <code>canSplit</code> 諮詢 finder(pill/isolate/avoid 一律不可切 → 退化<strong>整元素綁定</strong>——pill 即「作為原子鄰字單位整體綁入 <code>jz-jinze</code>」之期望行為)。</li>
|
||||
<li><strong>根因二:avoid 僅查祖先</strong>。新增 <code>avoidsSelf(el)</code> 自身判定——元素自身命中 <code>scope.avoid</code>/<code>skipAttribute</code>/<code>data-jz-skip</code> 時即為邊界(舊版被當透明穿越,防護形同虛設)。</li>
|
||||
<li><strong>根因三:descend 越界跳找</strong>。<code>adjacentLogicalChar</code> 之 <code>descend</code> 改三態(邊界字/無內容續找兄弟/<strong>blocked</strong> 終止)——遇 pill/isolate/avoid 不可跳過續找更遠之字。</li>
|
||||
<li><strong>pill 內部全面不可見</strong>:<code>eachTextNode</code> 過濾 <code>insidePill</code>——pill 內部不 charify、不插間隙、不做長詞包裝(舊版消費端自加之 pill 如 <code>.katex</code> 內部曾被直接變更)。</li>
|
||||
</ul>
|
||||
<h3>新增:行內元素三分類(§4.1)</h3>
|
||||
<ul>
|
||||
<li><strong>透明</strong>(<code>styleInlines</code> + <code>jz-*</code>):穿越、run 延續、禁則可克隆切分——<code>strong</code>/<code>em</code> 等純樣式。</li>
|
||||
<li><strong>隔離</strong>(<code>isolateSelector</code>,<strong>新增選項</strong>,預設 <code>"mark"</code>,<code>""</code> 停用):邊界斷 run/斷相鄰/不可切分、<strong>兩端補隙</strong>(同 pill),<strong>內部照常處理</strong>(自成脈絡)——帶背景色之 <code><mark></code> 等盒樣式行內(切分背景盒有可見接縫,<code>MARK</code> 並自可切白名單移除)。</li>
|
||||
<li><strong>整體</strong>(<code>pillSelector</code>):內部對一切 pass 不可見,禁則時整體綁入——<code>code</code>/<code>kbd</code>、行內公式 <code>.katex</code>、引用標 <code>.cite-ref</code> 等宜納入。</li>
|
||||
<li>spacing Pass B 邊界選擇器擴為 <strong>pill ∪ isolate</strong>;jiyaAdjacency 對 isolate 斷相鄰而內部自成脈絡;orphan 補 avoid 塊不綁(舊漏判)。</li>
|
||||
</ul>
|
||||
<h3>移除:v1 相容層</h3>
|
||||
<ul>
|
||||
<li>最後一批 v1 消費端遷移 v2 API 完成,<code>src/compat/v1.ts</code>(<code>createCjkAutospace</code>)、IIFE 全域別名、死選項 <code>autospaceClass</code>(經查從未被任何 pass 讀取)一併移除;不變量 I8 退役。v1 原始碼保存於 <code>v1-autospace</code> 分支。</li>
|
||||
</ul>
|
||||
|
||||
<h2>行內排版透明性修復 + 標點懸掛移除 <span class="badge accent">2026-06-11</span></h2>
|
||||
<p>修復兩個下游 Bug(同源於「行內元素對排版透明」原則在各遍歷原語口徑不一)+三項同根隱患審計;並把上一輪「停用」之標點懸掛<strong>正式移除</strong>(負 margin 程式碼/CSS/型別一併刪)。單元測試 90→101 全綠。</p>
|
||||
<h3>修復</h3>
|
||||
@@ -162,6 +184,36 @@
|
||||
<script type="text/markdown" id="md-source">
|
||||
# 變更紀錄(Changelog)— 聚珍(Juzhen)/cjk-autospace
|
||||
|
||||
## 行內元素三分類(KaTeX 肢解修復)+ v1 相容層移除(2026-06-12)
|
||||
|
||||
### 修復(下游問題六)
|
||||
- jinze 透明下潛肢解 pill:行內公式緊鄰禁則標點時,KaTeX 全樹皆 span、舊版僅憑標籤名判透明,公式內側末字連殼層被克隆切出(splitoff)、上標漂移;pillSelector/skipAttribute/scope.avoid 三層防護全被刺穿。另修:相鄰 pill 時 descend 刺穿致 jz-hws 插入公式內部;pill 內部標點被 jiya charify。
|
||||
- 根因修復:transparentInline 以元素判定(先排除 pill/isolate/avoid);canSplit 諮詢 finder(不可切→整元素綁定,pill 即整體綁入 jz-jinze);avoidsSelf 自身判定(舊僅查祖先);descend 三態(blocked 終止、不越界跳找);eachTextNode 過濾 pill 內部。
|
||||
|
||||
### 新增:行內元素三分類(§4.1)
|
||||
- 透明(styleInlines+jz-*):穿越、run 延續、可克隆切分。
|
||||
- 隔離(isolateSelector 新增選項,預設 "mark","" 停用):邊界斷 run/斷相鄰/不可切分、兩端補隙,內部照常處理。MARK 自可切白名單移除(背景盒切分有可見接縫)。
|
||||
- 整體(pillSelector):內部對一切 pass 不可見,禁則時整體綁入。spacing Pass B 邊界選擇器擴為 pill ∪ isolate。
|
||||
|
||||
### 移除:v1 相容層
|
||||
- createCjkAutospace/IIFE 全域別名/死選項 autospaceClass 一併刪(消費端已全數遷移 v2 API);I8 退役。測試 101→112 全綠。
|
||||
|
||||
## 行內排版透明性修復 + 標點懸掛移除(2026-06-11)
|
||||
|
||||
### 修復
|
||||
- pill 前標點誤加間隙(漢字。\<code\>):descend 越過 jz-char 取更前字誤補 jz-hws;isTransparentInline 補 JZ-CHAR/JZ-INNER。
|
||||
- 行內樣式元素整體掉行:core/split 克隆切分(splitoff),禁則/垂懸字只綁邊界詞元、revert 延後階段併回。
|
||||
- 審計 T4 lineedge 攀爬集由 styleInlines 派生;T5 jiyaAdjacency 遇 pill 斷相鄰;T6 spacing processPills 補 per-node 功能閘。
|
||||
|
||||
### 移除
|
||||
- 標點懸掛由停用轉正式移除(寬度守恆矛盾,CSS 接觸不到斷行器;負 margin 程式碼/CSS/型別一併刪;hanging 選項保留為 no-op)。
|
||||
|
||||
## 功能分級 + 垂懸字(2026-06-09)
|
||||
|
||||
- 功能分級:文本級(spacing/jiya)vs 段落級(jinze/orphan/longWord);data-jz-level/level:{text} 標記。
|
||||
- 垂懸字避免 orphan(方案 C,斷行層無測量,預設開 chars=2)。
|
||||
- lineedge 行端半形(squeeze)+行歸屬判定改垂直區間比較;標點懸掛實作後確認不可行、全面停用。
|
||||
|
||||
## 標點挤压一致性(2026-06-04)
|
||||
|
||||
### 新增/修復
|
||||
|
||||
+62
-12
@@ -1,4 +1,4 @@
|
||||
<!-- built with theme=dracula, mode=report, at=2026-06-11T08:19:00Z -->
|
||||
<!-- built with theme=dracula, mode=report, at=2026-06-12T09:20:32Z -->
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-Hant">
|
||||
<head>
|
||||
@@ -2546,7 +2546,7 @@ main > *:nth-child(n+6) { animation-delay: 0.46s; }
|
||||
<!-- BODY -->
|
||||
<p class="lede">聚珍(Juzhen)是現代簡/繁中文網頁排版套件,Han.css 之現代化重寫,沿其功能版圖,並針對四點作調整:複製乾淨(中西間隙不入剪貼板)、依 <code>lang</code> 全角/開明自動切換、<code>justify</code> 下句末標點只佔一份間距、跨瀏覽器一致。倉庫名仍為 <code>cjk-autospace</code>;公開 API 為 <code>createJuzhen</code> /全域 <code>Juzhen</code>;注入之自訂元素前綴 <code>jz-</code>。</p>
|
||||
|
||||
<p>本版(2.x)為從零重寫。v1 之 <code>createCjkAutospace</code> API 由相容層保留,既有消費端不需改碼即可運行;v1 原始碼保存於 <code>v1-autospace</code> 分支。設計契約見 <code>ARCHITECTURE.md</code>。</p>
|
||||
<p>本版(2.x)為從零重寫。v1 之 <code>createCjkAutospace</code> 相容層<strong>已移除</strong>(最後一批 v1 消費端已遷移 v2 API);v1 原始碼保存於 <code>v1-autospace</code> 分支。設計契約見 <code>ARCHITECTURE.md</code>。</p>
|
||||
|
||||
<h2>安裝與構建</h2>
|
||||
<pre><code>npm install
|
||||
@@ -2574,10 +2574,10 @@ createJuzhen({ lang: { default: "zh-Hant" } }).render(document.querySelector("ma
|
||||
<table>
|
||||
<thead><tr><th>功能</th><th>說明</th></tr></thead>
|
||||
<tbody>
|
||||
<tr><td>中西間隙 <code>spacing</code></td><td>CJK↔西文邊界,<strong>margin 模型</strong>:包裹左側字、以 <code>margin-right</code> 留隙,不注入空白字元 → <code>textContent</code> 完全不變、複製緊湊、<code>justify</code> 不暴增、行首乾淨;行末由 <code>gapTrim</code> layout pass 去隙以齊右。含跨樣式邊界與 <code>code</code>/<code>kbd</code> pill(pill 緊鄰標點時穿越 <code>jz-jinze</code> 仍補間隙)。贅餘作者空格一律正規化:CJK↔西文吸收為受管間隙、CJK↔CJK(含跨 inline 標籤)剝除不留隙。</td></tr>
|
||||
<tr><td>中西間隙 <code>spacing</code></td><td>CJK↔西文邊界,<strong>margin 模型</strong>:包裹左側字、以 <code>margin-right</code> 留隙,不注入空白字元 → <code>textContent</code> 完全不變、複製緊湊、<code>justify</code> 不暴增、行首乾淨;行末由 <code>gapTrim</code> layout pass 去隙以齊右。含跨樣式邊界與 pill/隔離元素邊界(<code>code</code>/<code>kbd</code>/<code>.katex</code> 與 <code>mark</code>,兩端補隙口徑一致;pill 緊鄰標點時穿越 <code>jz-jinze</code> 仍補間隙)。贅餘作者空格一律正規化:CJK↔西文吸收為受管間隙、CJK↔CJK(含跨 inline 標籤)剝除不留隙。</td></tr>
|
||||
<tr><td>長英文詞 <code>longWord</code></td><td><code>[A-Za-z]{6,}</code> 包入 <code><span lang="en"></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>strong</code>/<code>em</code> 等)內時<strong>克隆切分</strong>只綁邊界詞元(避免整串入 <code>nowrap</code> 致行末整體掉行),克隆標 <code>data-jz-kind="splitoff"</code>、<code>revert</code> 時併回。亦為 <code>justify</code> 綁定載體。</td></tr>
|
||||
<tr><td>禁則 <code>jinze</code></td><td>行首行末避頭尾:閉類/點號綁前一字(不置行首)、開類綁後一字(不置行末);只隔離邊界詞元,長引文中段仍可斷。邊界字落於<strong>透明</strong>行內樣式元素(<code>strong</code>/<code>em</code> 等)內時<strong>克隆切分</strong>只綁邊界詞元(避免整串入 <code>nowrap</code> 致行末整體掉行),克隆標 <code>data-jz-kind="splitoff"</code>、<code>revert</code> 時併回;pill/隔離/avoid 不可切、<strong>整元素綁定</strong>——pill(如行內公式 <code>.katex</code>)作為原子鄰字單位整體綁入 <code>jz-jinze</code>,絕不下潛肢解(見「行內元素三分類」)。亦為 <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>(避免長元素溢出容器)。<strong>預設開</strong>;段落級。</td></tr>
|
||||
<tr><td><s>標點懸掛 <code>hanging</code></s></td><td><strong>已移除</strong>:純 CSS 機制為機制級死路(寬度守恆矛盾——墨色出去多少版心內就空多少,CSS 接觸不到斷行器)。原生 <code>hanging-punctuation</code> 為唯一正解但僅 Safari 支援、且與 inline-block 原子互斥。<code>hanging</code> 選項保留為 no-op(無效果)。詳見 ARCHITECTURE §6.5.6。</td></tr>
|
||||
<tr><td>功能分級 <code>level</code></td><td>文本級(間隙、擠壓——任意文本適用)vs 段落級(禁則、垂懸字、長詞——需行/段布局)。塊級預設全功能;<code>data-jz-level="text"</code> 子樹或 <code>level:{text:"選擇器"}</code>(標題/按鈕)僅跑文本級。</td></tr>
|
||||
@@ -2673,6 +2673,19 @@ createJuzhen({ lang: { default: "zh-Hant" } }).render(document.querySelector("ma
|
||||
<li><strong>段落屬性照常設、原子透明</strong>:聚珍把標點拆成 <code>inline-block</code> 原子,但已確保原子對消費端段落樣式透明——段落可正常設 <code>text-indent</code>(首行縮排)、<code>hanging-punctuation</code>、<code>text-decoration</code>(下劃線)等。庫內已歸零會被原子重複套用之繼承屬性、並令原子承接段落裝飾,故首行縮排正常、下劃線連續不斷,<strong>無需任何額外配置</strong>。</li>
|
||||
</ol>
|
||||
|
||||
<h2>行內元素三分類:透明/隔離/整體</h2>
|
||||
<p>行內元素對排版有且僅有三種口徑,<strong>所有遍歷原語共用同一判定</strong>(中西間隙、擠壓相鄰、禁則綁定、垂懸字、克隆切分皆一致);判定<strong>以元素為準、非標籤名</strong>——KaTeX 全樹皆 <code><span></code>,但 <code>.katex</code> 命中 <code>pillSelector</code> 即為整體邊界。</p>
|
||||
<table>
|
||||
<thead><tr><th>分類</th><th>選擇器</th><th>邊界</th><th>內部</th><th>典型</th></tr></thead>
|
||||
<tbody>
|
||||
<tr><td><strong>透明</strong></td><td><code>styleInlines</code></td><td>穿越——間隙/相鄰/run 皆延續</td><td>照常處理;禁則可<strong>克隆切分</strong>(最小綁定)</td><td><code>strong</code>/<code>em</code>/<code>a</code>/<code>span</code></td></tr>
|
||||
<tr><td><strong>隔離</strong></td><td><code>isolateSelector</code>(預設 <code>"mark"</code>;<code>""</code> 停用)</td><td>斷 run/斷相鄰/不可切分;<strong>兩端</strong>補 <code>jz-hws</code> 間隙</td><td><strong>照常處理</strong>(自成脈絡)</td><td>帶背景色之 <code><mark></code>、badge 類</td></tr>
|
||||
<tr><td><strong>整體</strong></td><td><code>pillSelector</code>(預設 <code>"code, kbd"</code>)</td><td>同隔離</td><td><strong>完全不可見</strong>——不 charify、不插間隙、不切分、不下潛</td><td><code>code</code>/<code>kbd</code>、行內公式 <code>.katex</code>、引用標 <code>.cite-ref</code></td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p>禁則遇 pill(<code>…$公式$,</code>):整個 pill 作為原子鄰字單位<strong>整體綁入</strong> <code>jz-jinze</code>(公式本不可斷,無額外代價);隔離元素同樣整綁(切分背景盒有可見接縫)。<code>scope.avoid</code>/<code>skipAttribute</code>/<code>data-jz-skip</code> 以<strong>自身</strong>判定:命中元素自身即邊界、內外皆不處理。</p>
|
||||
<div class="callout"><span class="label">建議</span> 行內公式/夾注引用等獨立行內單位宜列為 pill:<code>pillSelector: "code, kbd, .katex, .cite-ref"</code>——兩側補隙、吸收作者空格、整體綁定,統一處理。</div>
|
||||
|
||||
<h2>分頁器(Paged.js 等)相容:<code>justifyAtoms</code></h2>
|
||||
<p>預設 <code>jz-char</code>/<code>jz-jinze</code> 為 <code>inline-block</code>,作 <code>justify</code> 之單一對齊原子。但<strong>分頁器</strong>(如 Paged.js)之對齊引擎會把每個 inline-block 邊界當伸縮點、與 CJK inter-ideograph 疊加,於窄欄混排時字距失控、整段散架。此時設 <code>justifyAtoms: false</code>——<code>jz-char</code>/<code>jz-jinze</code> 改 <code>display:inline</code>(半形 <code>halt</code> 與禁則 <code>nowrap</code> 綁定均保留)。常規流式版面無需設定。</p>
|
||||
<div class="callout warn"><span class="label">注意</span> <code>margin</code> 後備半形依賴 <code>inline-block</code>(<code>width:0.5em</code>),故 <code>justifyAtoms:false</code> 與 <code>margin</code> 模式不可兼得——分頁器場景請用含 <code>halt</code> 之字型。</div>
|
||||
@@ -2692,10 +2705,8 @@ createJuzhen({ lang: { default: "zh-Hant" } }).render(document.querySelector("ma
|
||||
<p><strong>典型用例</strong>:英文全角句號 <code>.</code>(U+FF0E,異於中文句號 <code>。</code> U+3002)預設<strong>未分類</strong>——不 charify、不挤壓、與 CJK 間不補隙。以 <code>charClass:{ biaodian:{ stop:"." } }</code> 歸為全寬句末標點,即比照 <code>。</code> 處理(開明式挤半形、全角式佔全形、行首避頭尾)。</p>
|
||||
<div class="callout"><span class="label">要點</span> <strong>向下兼容</strong>:不傳 <code>charClass</code> 時分類與行為與內置完全一致、且各實例隔離(覆寫不污染默認)。<strong>追加語義(v1)</strong>:擴充內置類,不支援重分類/移除內置字符。結構性 ASCII(字母數字/運算符)為固定集、不開放。<code>margin</code> 模式下新增開/閉括號之墨色偏側可一併以 <code>jiya.bias</code> 指定。</div>
|
||||
|
||||
<h2>v1 向下相容</h2>
|
||||
<pre><code>const cjk = createCjkAutospace({ pillSelector: "code, kbd" });
|
||||
cjk.apply(document.body); // → render</code></pre>
|
||||
<p>IIFE 另掛 <code>globalThis.createCjkAutospace</code> 別名。與 v1 之差異(行為等價、不要求逐位一致):間隙由 <code><span></code> +真實空格改為 <code>jz-hws</code> margin(複製更乾淨);擠壓改由 <code>juzhen.css</code> 託管,預設仍以字型 <code>halt</code> 渲染半形(同 v1 機制),缺 <code>halt</code> 字型可切 <code>margin</code> 後備。</p>
|
||||
<h2>v1 向下相容 — 已移除</h2>
|
||||
<p>v1 相容層(<code>createCjkAutospace</code> 導出、IIFE 之全域別名、<code>autospaceClass</code> 選項)<strong>已隨最後一批 v1 消費端遷移完成而移除</strong>。請改用 <code>createJuzhen(options).render(root)</code> v2 API;v1 原始碼保存於 <code>v1-autospace</code> 分支。</p>
|
||||
|
||||
<h2>目視驗證</h2>
|
||||
<p><code>demo/index.html</code> 載入 <code>dist/</code> 產物,簡繁並列、可切 <code>justify</code>、附 copy-clean 測試區。<code>demo/fallback.html</code> 以方正書宋 GBK(無 <code>halt</code>)對照 <code>halt</code> 失效 vs <code>margin</code> 後備生效。版面相關結論(justify 單份間距、擠壓視覺量、行端、<code>inline-block</code> 基線)須於 Chrome/Safari/Firefox 三引擎目視核對——jsdom 僅驗證 DOM 結構正確性。</p>
|
||||
@@ -2743,10 +2754,10 @@ JS 只注入 `jz-*` 結構,所有視覺尺寸在 juzhen.css,缺一不可。
|
||||
|
||||
## 已實作功能
|
||||
|
||||
- spacing 中西間隙(margin 模型,copy-clean;含 pill 與作者空格正規化)【文本級】
|
||||
- spacing 中西間隙(margin 模型,copy-clean;含 pill/隔離元素邊界與作者空格正規化)【文本級】
|
||||
- longWord 長英文詞包裝(`<span lang>` + hyphens:auto)【段落級】
|
||||
- jiya 標點擠壓(全角/開明/半角;半形 halt 預設、margin 後備)【文本級】
|
||||
- jinze 禁則(避頭尾、justify 綁定)【段落級】
|
||||
- jinze 禁則(避頭尾、justify 綁定;透明元素克隆切分最小綁定,pill/隔離/avoid 整元素綁定)【段落級】
|
||||
- orphan 垂懸字避免(方案 C,末行 CJK 實義字 ≥ N;預設開 chars=2)【段落級】
|
||||
- hanging 標點懸掛:已移除(純 CSS 機制級死路:寬度守恆矛盾,CSS 接觸不到斷行器;原生 hanging-punctuation 為唯一正解但僅 Safari 支援、且與 inline-block 原子互斥;選項保留為 no-op;詳見 §6.5.6)
|
||||
- level 功能分級(文本級 vs 段落級;data-jz-level="text"/level:{text:選擇器} 標記標題等僅跑文本級)
|
||||
@@ -2771,6 +2782,10 @@ JS 只注入 `jz-*` 結構,所有視覺尺寸在 juzhen.css,缺一不可。
|
||||
|
||||
引入 juzhen.css(必須);設 lang;scope class;hyphens:auto;text-align:justify(選用)。字型由消費端負責:聚珍不內建 font-family,逐語言字型自設於 .juzhen 或帶 lang 之容器(含 halt 字型,缺則 margin 後備+bias);聚珍以 font:inherit 繼承消費端字型、顯式保留 locl(正確標 lang + 支援 locl 之字型即正常)。段落屬性照常設、原子透明:text-indent/hanging-punctuation/text-decoration 等已由庫內統一處理(歸零過套用、令原子承接段落裝飾),首行縮排正常、下劃線連續,無需額外配置。
|
||||
|
||||
## 行內元素三分類
|
||||
|
||||
行內元素對排版三種口徑,所有遍歷原語共用同一判定(以元素為準、非標籤名):透明(styleInlines——穿越、run 延續、可克隆切分);隔離(isolateSelector,預設 "mark"——邊界斷 run/斷相鄰/不可切分、兩端補隙,內部照常處理);整體(pillSelector——內部對一切 pass 不可見,禁則時作為原子鄰字單位整體綁入 jz-jinze)。avoid(scope.avoid/skipAttribute/data-jz-skip)以自身判定,命中即邊界。行內公式/夾注引用宜列為 pill:`pillSelector: "code, kbd, .katex, .cite-ref"`。
|
||||
|
||||
## 分頁器相容
|
||||
|
||||
`justifyAtoms: false` → jz-char/jz-jinze 改 display:inline,相容 Paged.js。margin 後備與 justifyAtoms:false 不可兼得。
|
||||
@@ -2779,9 +2794,9 @@ JS 只注入 `jz-*` 結構,所有視覺尺寸在 juzhen.css,缺一不可。
|
||||
|
||||
字符分類(中/西文、全寬標點各子類)抽象為規則集,內置默認,消費端經 options.charClass 追加擴充,覆寫 spacing/jiya/jinze 之判定。`charClass: { cjk?, western?, biaodian?:{stop,pause,open,close,middle,liga} }`。例:英文全角句號 .(U+FF0E)預設未分類,以 biaodian.stop:"." 歸為全寬句末標點即比照 。處理。向下兼容(不傳則行為不變、實例隔離);v1 為追加語義,不重分類內置;結構性 ASCII 不開放。
|
||||
|
||||
## v1 相容
|
||||
## v1 相容 — 已移除
|
||||
|
||||
`createCjkAutospace(opts).apply(root)` 經相容層映射至 createJuzhen,零改動可跑。
|
||||
v1 相容層(createCjkAutospace/IIFE 全域別名/autospaceClass)已隨最後一批 v1 消費端遷移完成而移除;請改用 createJuzhen(options).render(root)。v1 原始碼保存於 v1-autospace 分支。
|
||||
|
||||
MIT。借鑑 Han.css/Heti 思路與分類表,成品自含。
|
||||
</script>
|
||||
@@ -4995,6 +5010,12 @@ window.__JUZHEN_PUNCT__="quanjiao";window.__JUZHEN_JUSTIFY_ATOMS__=true;window._
|
||||
規則,結構性綁定故與分頁無關(academic 安全)。
|
||||
· longWord — 長英文段包 `<span lang="en">`,配合 `[lang|="en"]`
|
||||
之 `hyphens: auto` 由瀏覽器原生斷詞(DOM 無連字符)。
|
||||
· orphan — 垂懸字避免:段末行 CJK 實義字 < 2 時把末 2 字綁
|
||||
`<jz-orphan nowrap>`,杜絕孤字成行(標點不計);屬段落
|
||||
級、斷行層無測量,分頁安全。lib 預設即開。
|
||||
功能分級(level):上列 jinze/longWord/orphan 為「段落級」,僅作用於
|
||||
段落類元素;標題與 UI chrome 經 `level.text` 標為「文本級」,只跑 spacing
|
||||
+jiya(見下方 createJuzhen)。標點懸掛 hanging 上游已停用,不採用。
|
||||
|
||||
標點擠壓政策(開明/全角)為「逐模式」而非逐語言:build.sh 依模式
|
||||
注入 `window.__JUZHEN_PUNCT__`(academic → kaiming 開明式;其餘 →
|
||||
@@ -5089,10 +5110,39 @@ window.__JUZHEN_PUNCT__="quanjiao";window.__JUZHEN_JUSTIFY_ATOMS__=true;window._
|
||||
catch (e) { /* getComputedStyle 不可用時退預設 halt */ }
|
||||
window.Juzhen.createJuzhen({
|
||||
pillSelector: "code, kbd, mark, .badge, .katex, .cite-ref",
|
||||
/* KaTeX 整樹另列 avoid 範圍:公式內部任何文本都不得被聚珍觸碰。
|
||||
⚠ 已知上游缺陷(已記 ~/.scratch/juzhen-feedback.md,待上游修):jinze
|
||||
綁定公式緊鄰禁則標點(`$x$,`/`($x$`)時,adjacentLogicalChar 經
|
||||
styleInlines 透明下潛(SPAN 在透明清單、KaTeX 全樹皆 span)刺穿 pill/
|
||||
skipAttribute/scope.avoid 三層防護,把公式內側末字(如 10^{-17} 之
|
||||
「17」)連同克隆殼層切出 jz-jinze——上標漂位、公式碎裂。消費端無乾淨
|
||||
攔法(三種防護實測均被刺穿),等上游令 descend 尊重邊界後,本行 avoid
|
||||
即為正式屏障。 */
|
||||
scope: { avoid: ".katex" },
|
||||
skipAttribute: "data-md-key",
|
||||
spacing: true,
|
||||
jinze: true,
|
||||
longWord: true,
|
||||
/* level(功能分級,聚珍 §6.5.0a):段落級功能(禁則 jinze/垂懸 orphan/長詞
|
||||
斷字 longWord)需行/段布局,對標題、控制項、純 UI chrome 套用無益甚至有害
|
||||
(標題被 longWord 連字斷開、單行 chrome 字串被 orphan/jinze 綁成 nowrap 而
|
||||
溢出)。故把「非段落類」標籤標為文本級(`level.text`),僅保留文本級功能(中西
|
||||
間隙 spacing+標點擠壓 jiya);真正的段落(p/li/td/blockquote/figcaption/
|
||||
.lede/.marginalia/.references/.appendix……)不命中、維持全功能。最近祖先勝,
|
||||
故標題與 chrome 容器一旦命中,其後代(如 .slide-title 內的 .section-name/
|
||||
.slide-dot、.title-page 內的作者欄)一併降級。選擇器涵蓋三模式;某模式不存在之
|
||||
class 自然不命中。 */
|
||||
level: {
|
||||
text: [
|
||||
"h1, h2, h3, h4, h5, h6", // 全部標題(含 deck-/section-/slide-/tp-/toc-title)
|
||||
"button, [role=button], .btn", // 控制項
|
||||
".subtitle, .section-subtitle, .deck-subtitle", // 副標題
|
||||
".meta, .timestamp, .actions, .page", // report chrome
|
||||
".cover-block, .slide-foot, .toc-list", // slides 封面/頁尾/目錄條目
|
||||
".deck-nav, .deck-counter, .deck-author, .deck-institute, .deck-date, .deck-actions, .deck-stage",
|
||||
".title-page, .toc-page, .simple-title", // academic 前置 chrome(含 tp-/st- 欄位)
|
||||
].join(", "),
|
||||
},
|
||||
/* orphan(垂懸字避免,聚珍 §6.5.1):段末行 CJK 實義字 < 2 時,把末 2 字
|
||||
包入 `<jz-orphan white-space:nowrap>` 拉下首字,杜絕「孤字成行」。判定僅計
|
||||
漢字實義字(標點/符號不納入)。方案 C 屬斷行層、無測量,結構性 nowrap 同
|
||||
|
||||
+26
-11
@@ -25,7 +25,7 @@
|
||||
<!-- BODY -->
|
||||
<p class="lede">聚珍(Juzhen)是現代簡/繁中文網頁排版套件,Han.css 之現代化重寫,沿其功能版圖,並針對四點作調整:複製乾淨(中西間隙不入剪貼板)、依 <code>lang</code> 全角/開明自動切換、<code>justify</code> 下句末標點只佔一份間距、跨瀏覽器一致。倉庫名仍為 <code>cjk-autospace</code>;公開 API 為 <code>createJuzhen</code> /全域 <code>Juzhen</code>;注入之自訂元素前綴 <code>jz-</code>。</p>
|
||||
|
||||
<p>本版(2.x)為從零重寫。v1 之 <code>createCjkAutospace</code> API 由相容層保留,既有消費端不需改碼即可運行;v1 原始碼保存於 <code>v1-autospace</code> 分支。設計契約見 <code>ARCHITECTURE.md</code>。</p>
|
||||
<p>本版(2.x)為從零重寫。v1 之 <code>createCjkAutospace</code> 相容層<strong>已移除</strong>(最後一批 v1 消費端已遷移 v2 API);v1 原始碼保存於 <code>v1-autospace</code> 分支。設計契約見 <code>ARCHITECTURE.md</code>。</p>
|
||||
|
||||
<h2>安裝與構建</h2>
|
||||
<pre><code>npm install
|
||||
@@ -53,10 +53,10 @@ createJuzhen({ lang: { default: "zh-Hant" } }).render(document.querySelector("ma
|
||||
<table>
|
||||
<thead><tr><th>功能</th><th>說明</th></tr></thead>
|
||||
<tbody>
|
||||
<tr><td>中西間隙 <code>spacing</code></td><td>CJK↔西文邊界,<strong>margin 模型</strong>:包裹左側字、以 <code>margin-right</code> 留隙,不注入空白字元 → <code>textContent</code> 完全不變、複製緊湊、<code>justify</code> 不暴增、行首乾淨;行末由 <code>gapTrim</code> layout pass 去隙以齊右。含跨樣式邊界與 <code>code</code>/<code>kbd</code> pill(pill 緊鄰標點時穿越 <code>jz-jinze</code> 仍補間隙)。贅餘作者空格一律正規化:CJK↔西文吸收為受管間隙、CJK↔CJK(含跨 inline 標籤)剝除不留隙。</td></tr>
|
||||
<tr><td>中西間隙 <code>spacing</code></td><td>CJK↔西文邊界,<strong>margin 模型</strong>:包裹左側字、以 <code>margin-right</code> 留隙,不注入空白字元 → <code>textContent</code> 完全不變、複製緊湊、<code>justify</code> 不暴增、行首乾淨;行末由 <code>gapTrim</code> layout pass 去隙以齊右。含跨樣式邊界與 pill/隔離元素邊界(<code>code</code>/<code>kbd</code>/<code>.katex</code> 與 <code>mark</code>,兩端補隙口徑一致;pill 緊鄰標點時穿越 <code>jz-jinze</code> 仍補間隙)。贅餘作者空格一律正規化:CJK↔西文吸收為受管間隙、CJK↔CJK(含跨 inline 標籤)剝除不留隙。</td></tr>
|
||||
<tr><td>長英文詞 <code>longWord</code></td><td><code>[A-Za-z]{6,}</code> 包入 <code><span lang="en"></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>strong</code>/<code>em</code> 等)內時<strong>克隆切分</strong>只綁邊界詞元(避免整串入 <code>nowrap</code> 致行末整體掉行),克隆標 <code>data-jz-kind="splitoff"</code>、<code>revert</code> 時併回。亦為 <code>justify</code> 綁定載體。</td></tr>
|
||||
<tr><td>禁則 <code>jinze</code></td><td>行首行末避頭尾:閉類/點號綁前一字(不置行首)、開類綁後一字(不置行末);只隔離邊界詞元,長引文中段仍可斷。邊界字落於<strong>透明</strong>行內樣式元素(<code>strong</code>/<code>em</code> 等)內時<strong>克隆切分</strong>只綁邊界詞元(避免整串入 <code>nowrap</code> 致行末整體掉行),克隆標 <code>data-jz-kind="splitoff"</code>、<code>revert</code> 時併回;pill/隔離/avoid 不可切、<strong>整元素綁定</strong>——pill(如行內公式 <code>.katex</code>)作為原子鄰字單位整體綁入 <code>jz-jinze</code>,絕不下潛肢解(見「行內元素三分類」)。亦為 <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>(避免長元素溢出容器)。<strong>預設開</strong>;段落級。</td></tr>
|
||||
<tr><td><s>標點懸掛 <code>hanging</code></s></td><td><strong>已移除</strong>:純 CSS 機制為機制級死路(寬度守恆矛盾——墨色出去多少版心內就空多少,CSS 接觸不到斷行器)。原生 <code>hanging-punctuation</code> 為唯一正解但僅 Safari 支援、且與 inline-block 原子互斥。<code>hanging</code> 選項保留為 no-op(無效果)。詳見 ARCHITECTURE §6.5.6。</td></tr>
|
||||
<tr><td>功能分級 <code>level</code></td><td>文本級(間隙、擠壓——任意文本適用)vs 段落級(禁則、垂懸字、長詞——需行/段布局)。塊級預設全功能;<code>data-jz-level="text"</code> 子樹或 <code>level:{text:"選擇器"}</code>(標題/按鈕)僅跑文本級。</td></tr>
|
||||
@@ -152,6 +152,19 @@ createJuzhen({ lang: { default: "zh-Hant" } }).render(document.querySelector("ma
|
||||
<li><strong>段落屬性照常設、原子透明</strong>:聚珍把標點拆成 <code>inline-block</code> 原子,但已確保原子對消費端段落樣式透明——段落可正常設 <code>text-indent</code>(首行縮排)、<code>hanging-punctuation</code>、<code>text-decoration</code>(下劃線)等。庫內已歸零會被原子重複套用之繼承屬性、並令原子承接段落裝飾,故首行縮排正常、下劃線連續不斷,<strong>無需任何額外配置</strong>。</li>
|
||||
</ol>
|
||||
|
||||
<h2>行內元素三分類:透明/隔離/整體</h2>
|
||||
<p>行內元素對排版有且僅有三種口徑,<strong>所有遍歷原語共用同一判定</strong>(中西間隙、擠壓相鄰、禁則綁定、垂懸字、克隆切分皆一致);判定<strong>以元素為準、非標籤名</strong>——KaTeX 全樹皆 <code><span></code>,但 <code>.katex</code> 命中 <code>pillSelector</code> 即為整體邊界。</p>
|
||||
<table>
|
||||
<thead><tr><th>分類</th><th>選擇器</th><th>邊界</th><th>內部</th><th>典型</th></tr></thead>
|
||||
<tbody>
|
||||
<tr><td><strong>透明</strong></td><td><code>styleInlines</code></td><td>穿越——間隙/相鄰/run 皆延續</td><td>照常處理;禁則可<strong>克隆切分</strong>(最小綁定)</td><td><code>strong</code>/<code>em</code>/<code>a</code>/<code>span</code></td></tr>
|
||||
<tr><td><strong>隔離</strong></td><td><code>isolateSelector</code>(預設 <code>"mark"</code>;<code>""</code> 停用)</td><td>斷 run/斷相鄰/不可切分;<strong>兩端</strong>補 <code>jz-hws</code> 間隙</td><td><strong>照常處理</strong>(自成脈絡)</td><td>帶背景色之 <code><mark></code>、badge 類</td></tr>
|
||||
<tr><td><strong>整體</strong></td><td><code>pillSelector</code>(預設 <code>"code, kbd"</code>)</td><td>同隔離</td><td><strong>完全不可見</strong>——不 charify、不插間隙、不切分、不下潛</td><td><code>code</code>/<code>kbd</code>、行內公式 <code>.katex</code>、引用標 <code>.cite-ref</code></td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p>禁則遇 pill(<code>…$公式$,</code>):整個 pill 作為原子鄰字單位<strong>整體綁入</strong> <code>jz-jinze</code>(公式本不可斷,無額外代價);隔離元素同樣整綁(切分背景盒有可見接縫)。<code>scope.avoid</code>/<code>skipAttribute</code>/<code>data-jz-skip</code> 以<strong>自身</strong>判定:命中元素自身即邊界、內外皆不處理。</p>
|
||||
<div class="callout"><span class="label">建議</span> 行內公式/夾注引用等獨立行內單位宜列為 pill:<code>pillSelector: "code, kbd, .katex, .cite-ref"</code>——兩側補隙、吸收作者空格、整體綁定,統一處理。</div>
|
||||
|
||||
<h2>分頁器(Paged.js 等)相容:<code>justifyAtoms</code></h2>
|
||||
<p>預設 <code>jz-char</code>/<code>jz-jinze</code> 為 <code>inline-block</code>,作 <code>justify</code> 之單一對齊原子。但<strong>分頁器</strong>(如 Paged.js)之對齊引擎會把每個 inline-block 邊界當伸縮點、與 CJK inter-ideograph 疊加,於窄欄混排時字距失控、整段散架。此時設 <code>justifyAtoms: false</code>——<code>jz-char</code>/<code>jz-jinze</code> 改 <code>display:inline</code>(半形 <code>halt</code> 與禁則 <code>nowrap</code> 綁定均保留)。常規流式版面無需設定。</p>
|
||||
<div class="callout warn"><span class="label">注意</span> <code>margin</code> 後備半形依賴 <code>inline-block</code>(<code>width:0.5em</code>),故 <code>justifyAtoms:false</code> 與 <code>margin</code> 模式不可兼得——分頁器場景請用含 <code>halt</code> 之字型。</div>
|
||||
@@ -171,10 +184,8 @@ createJuzhen({ lang: { default: "zh-Hant" } }).render(document.querySelector("ma
|
||||
<p><strong>典型用例</strong>:英文全角句號 <code>.</code>(U+FF0E,異於中文句號 <code>。</code> U+3002)預設<strong>未分類</strong>——不 charify、不挤壓、與 CJK 間不補隙。以 <code>charClass:{ biaodian:{ stop:"." } }</code> 歸為全寬句末標點,即比照 <code>。</code> 處理(開明式挤半形、全角式佔全形、行首避頭尾)。</p>
|
||||
<div class="callout"><span class="label">要點</span> <strong>向下兼容</strong>:不傳 <code>charClass</code> 時分類與行為與內置完全一致、且各實例隔離(覆寫不污染默認)。<strong>追加語義(v1)</strong>:擴充內置類,不支援重分類/移除內置字符。結構性 ASCII(字母數字/運算符)為固定集、不開放。<code>margin</code> 模式下新增開/閉括號之墨色偏側可一併以 <code>jiya.bias</code> 指定。</div>
|
||||
|
||||
<h2>v1 向下相容</h2>
|
||||
<pre><code>const cjk = createCjkAutospace({ pillSelector: "code, kbd" });
|
||||
cjk.apply(document.body); // → render</code></pre>
|
||||
<p>IIFE 另掛 <code>globalThis.createCjkAutospace</code> 別名。與 v1 之差異(行為等價、不要求逐位一致):間隙由 <code><span></code> +真實空格改為 <code>jz-hws</code> margin(複製更乾淨);擠壓改由 <code>juzhen.css</code> 託管,預設仍以字型 <code>halt</code> 渲染半形(同 v1 機制),缺 <code>halt</code> 字型可切 <code>margin</code> 後備。</p>
|
||||
<h2>v1 向下相容 — 已移除</h2>
|
||||
<p>v1 相容層(<code>createCjkAutospace</code> 導出、IIFE 之全域別名、<code>autospaceClass</code> 選項)<strong>已隨最後一批 v1 消費端遷移完成而移除</strong>。請改用 <code>createJuzhen(options).render(root)</code> v2 API;v1 原始碼保存於 <code>v1-autospace</code> 分支。</p>
|
||||
|
||||
<h2>目視驗證</h2>
|
||||
<p><code>demo/index.html</code> 載入 <code>dist/</code> 產物,簡繁並列、可切 <code>justify</code>、附 copy-clean 測試區。<code>demo/fallback.html</code> 以方正書宋 GBK(無 <code>halt</code>)對照 <code>halt</code> 失效 vs <code>margin</code> 後備生效。版面相關結論(justify 單份間距、擠壓視覺量、行端、<code>inline-block</code> 基線)須於 Chrome/Safari/Firefox 三引擎目視核對——jsdom 僅驗證 DOM 結構正確性。</p>
|
||||
@@ -206,10 +217,10 @@ JS 只注入 `jz-*` 結構,所有視覺尺寸在 juzhen.css,缺一不可。
|
||||
|
||||
## 已實作功能
|
||||
|
||||
- spacing 中西間隙(margin 模型,copy-clean;含 pill 與作者空格正規化)【文本級】
|
||||
- spacing 中西間隙(margin 模型,copy-clean;含 pill/隔離元素邊界與作者空格正規化)【文本級】
|
||||
- longWord 長英文詞包裝(`<span lang>` + hyphens:auto)【段落級】
|
||||
- jiya 標點擠壓(全角/開明/半角;半形 halt 預設、margin 後備)【文本級】
|
||||
- jinze 禁則(避頭尾、justify 綁定)【段落級】
|
||||
- jinze 禁則(避頭尾、justify 綁定;透明元素克隆切分最小綁定,pill/隔離/avoid 整元素綁定)【段落級】
|
||||
- orphan 垂懸字避免(方案 C,末行 CJK 實義字 ≥ N;預設開 chars=2)【段落級】
|
||||
- hanging 標點懸掛:已移除(純 CSS 機制級死路:寬度守恆矛盾,CSS 接觸不到斷行器;原生 hanging-punctuation 為唯一正解但僅 Safari 支援、且與 inline-block 原子互斥;選項保留為 no-op;詳見 §6.5.6)
|
||||
- level 功能分級(文本級 vs 段落級;data-jz-level="text"/level:{text:選擇器} 標記標題等僅跑文本級)
|
||||
@@ -234,6 +245,10 @@ JS 只注入 `jz-*` 結構,所有視覺尺寸在 juzhen.css,缺一不可。
|
||||
|
||||
引入 juzhen.css(必須);設 lang;scope class;hyphens:auto;text-align:justify(選用)。字型由消費端負責:聚珍不內建 font-family,逐語言字型自設於 .juzhen 或帶 lang 之容器(含 halt 字型,缺則 margin 後備+bias);聚珍以 font:inherit 繼承消費端字型、顯式保留 locl(正確標 lang + 支援 locl 之字型即正常)。段落屬性照常設、原子透明:text-indent/hanging-punctuation/text-decoration 等已由庫內統一處理(歸零過套用、令原子承接段落裝飾),首行縮排正常、下劃線連續,無需額外配置。
|
||||
|
||||
## 行內元素三分類
|
||||
|
||||
行內元素對排版三種口徑,所有遍歷原語共用同一判定(以元素為準、非標籤名):透明(styleInlines——穿越、run 延續、可克隆切分);隔離(isolateSelector,預設 "mark"——邊界斷 run/斷相鄰/不可切分、兩端補隙,內部照常處理);整體(pillSelector——內部對一切 pass 不可見,禁則時作為原子鄰字單位整體綁入 jz-jinze)。avoid(scope.avoid/skipAttribute/data-jz-skip)以自身判定,命中即邊界。行內公式/夾注引用宜列為 pill:`pillSelector: "code, kbd, .katex, .cite-ref"`。
|
||||
|
||||
## 分頁器相容
|
||||
|
||||
`justifyAtoms: false` → jz-char/jz-jinze 改 display:inline,相容 Paged.js。margin 後備與 justifyAtoms:false 不可兼得。
|
||||
@@ -242,9 +257,9 @@ JS 只注入 `jz-*` 結構,所有視覺尺寸在 juzhen.css,缺一不可。
|
||||
|
||||
字符分類(中/西文、全寬標點各子類)抽象為規則集,內置默認,消費端經 options.charClass 追加擴充,覆寫 spacing/jiya/jinze 之判定。`charClass: { cjk?, western?, biaodian?:{stop,pause,open,close,middle,liga} }`。例:英文全角句號 .(U+FF0E)預設未分類,以 biaodian.stop:"." 歸為全寬句末標點即比照 。處理。向下兼容(不傳則行為不變、實例隔離);v1 為追加語義,不重分類內置;結構性 ASCII 不開放。
|
||||
|
||||
## v1 相容
|
||||
## v1 相容 — 已移除
|
||||
|
||||
`createCjkAutospace(opts).apply(root)` 經相容層映射至 createJuzhen,零改動可跑。
|
||||
v1 相容層(createCjkAutospace/IIFE 全域別名/autospaceClass)已隨最後一批 v1 消費端遷移完成而移除;請改用 createJuzhen(options).render(root)。v1 原始碼保存於 v1-autospace 分支。
|
||||
|
||||
MIT。借鑑 Han.css/Heti 思路與分類表,成品自含。
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user