feat(levelmark): 塊級分類 CSS 鉤子 data-jz-block-level+增量塊粒度契約(下游建議七/說明八)

建議七:新增 levelMark pass(standalone,order 10,恆開)——render 時把 resolved
功能級別寫回「直接承載可處理文本」之塊級元素:data-jz-block-level="paragraph|text",
revert 全數移除(I6 之 data-jz-* 清單涵蓋)。消費端段落級 CSS(首要 text-align:
justify)自此與 jinze/orphan 共用單一真相源,免另立必然漂移之選擇器白名單。

- 僅標直接承載文本之塊:純容器(body/ul/table…,直接文本僅空白)不標——否則
  body 恆判 paragraph、justify 鉤子經繼承污染全頁;此集合恰為段落級 pass 實際
  作用之塊集。avoid 子樹/pill 內部文本不計,未被處理之塊一致不標。
- 輸入/輸出分離:作者輸入 data-jz-level 只讀不寫;輸出屬性本庫絕不讀取、亦不在
  C2 OBSERVE_ATTRS 白名單(外部改動不觸發);增量路徑 revert 即清、render 即重標。
- 實現:一次 eachTextNode 走訪+短程上攀定塊(per-run 快取),O(文本);levelFor
  已記憶化。上攀止於 ctx.root,不越界外標。

說明八:README/ARCHITECTURE §3.1 明載增量粒度契約——增量須以塊為粒度(換塊
內容、增刪塊、改分類屬性皆與全量重繪一致);不支持對聚珍已切分文本節點之外科式
characterData 直改(charify 以 fragment 替換原節點,舊引用已脫離文檔)——後處理
排版器之固有限制、非缺陷,以塊為單位替換內容即可。

測試 140→145(標記/純容器不標、text 解析三路(選擇器/屬性/祖先)、avoid/
pill 不標、revert 全清含 root、C2 class 翻轉自動刷新);tsc 聲明構建通過;
headless Chrome 整管線煙測零報錯、段落屬性實際落 DOM。
This commit is contained in:
2026-07-16 16:42:56 +08:00
parent 2f04ea4085
commit 8ee9895247
8 changed files with 345 additions and 5 deletions
+38
View File
@@ -91,6 +91,13 @@ jz.observe(document.body); // 之後 DOM 變動即自動增量恢復
> 須經 `revert``rerender``invalidate``observe` 任一使該子樹快取失效**,否則沿用舊分類。
> 新增/移除元素與純內容編輯無此顧慮(新元素重新計算、移除者自動回收)。
> **增量之粒度為「塊」**:換塊內容(`textContent``innerHTML`)、增刪塊、改分類屬性——皆與
> 全量重繪一致。**不支持**對聚珍已切分之文本節點做外科式 `characterData` 直改(如持有 render
> **前**之 `p.firstChild` 引用、事後直改其 `.data`):render 後原文本節點已被 `jz-*` 切分/
> 替換,該引用已非原整段,直改單一碎片會令 `revert` 合併出之內容偏離作者意圖。此為後處理排版
> 器之固有限制(任何外科式文本 patch 之框架皆會與注入後之 DOM 失步)、非缺陷——聚珍對當前
> DOM 之處理始終自洽。欲改文字,以塊為單位替換內容即可。
> **`revert` 之範圍**`revert` 乾淨移除聚珍注入之全部結構(`jz-*``data-jz-*`),DOM 回到**邏輯
> 等價**態;但**不還原** `spacing` 依契約規範化之贅餘作者空格(CJK↔西文吸收為 margin 間隙、
> CJK↔CJK 剝除;見上表「中西間隙」)。即 `render→revert` 對含 CJK↔西文作者空格之源,還原後語義
@@ -311,6 +318,37 @@ web component 等)採**嚴格白名單**、一律當**邊界**(語義同隔
createJuzhen({ level: { text: "h1,h2,h3,button,.ui" } }); // 選擇器一鍵標記
```
#### CSS 可查之 resolved 分類:`data-jz-block-level`(輸出屬性)
`render` 時,聚珍把 **resolved** 級別寫回每個「直接承載可處理文本」之塊級元素:
```html
<div class="callout" data-jz-block-level="paragraph"></div> <!-- 聚珍判為段落級 -->
<h2 data-jz-block-level="text"></h2> <!-- 聚珍判為文本級 -->
<ul> <!-- 純容器:不標 -->
<li data-jz-block-level="paragraph"></li>
</ul>
```
消費端之**段落級 CSS**(首要:CJK 兩端對齊)從此與聚珍的段落級 pass(禁則/垂懸)共用
**單一真相源**,免另行維護一份必然漂移之選擇器白名單:
```css
[data-jz-block-level="paragraph"] { text-align: justify; }
.marginalia[data-jz-block-level="paragraph"] { text-align: start; } /* 窄欄 opt-out */
```
規則:
- **僅標直接承載文本之塊**——純容器(`body``ul``table`…,直接文本僅空白)不標,
否則 `body` 恆被判段落級,justify 鉤子經繼承污染全頁。此集合恰為段落級 pass 實際
作用之塊集。avoid 子樹/pill 內部之文本不計,故未被處理之塊一致不標。
- **輸出專用、輸入輸出分離**:作者輸入屬性 `data-jz-level` 只讀不寫;本屬性由聚珍寫入、
聚珍**絕不讀取**(消費端手寫無效),`revert` 時全數移除。增量模式(`observe`)下分類
屬性變化自動刷新。
- 是否對齊仍由消費端 CSS 決定(justify 非段落之純函數——窄欄邊注可 opt-out);聚珍
只供分類、不介入樣式。
### 標點懸掛(`hanging`)— 已移除
原計劃之標點懸掛(行端標點凸出版心)經實測為**機制級死路、已移除**(負 margin 程式碼與