admin
|
c5a503580c
|
perf+feat: 掃描/佈局提速(中間/S4+S1)+增量恢復(Tier3 C1/C3/C2)
承 Tier1(S2+S3,已於 main 168e9e7)。本批為效能第二層+第三層,全程獨立複驗(差分/往返/
增量≡全量/進程隔離測速/真瀏覽器)+4 份真人撰寫真實文檔驗收,行為保真、提速真實。
掃描層(無狀態、保行為):
- 中間發現+S4:insidePill/isAvoided 之「逐文本節點沿祖先鏈」以 ancestorOrSelfPill/…Avoided
迭代回填記憶化(每元素恰算一次、兄弟共用祖先鏈快取),O(文本×depth)→O(元素)。實測掃描層
el.matches() 累計約降五成;深嵌套 render 進程隔離墻鐘 pre-Tier1 707ms→254ms(2.8×)。
佈局層(僅瀏覽器、正確性敏感):
- S1 gapTrim 對齊門:尾隨 margin 僅在右緣 flush 之對齊可見,唯 left/start(ltr) 之 ragged 右緣
可整批跳過,省其 O(gaps) 次同步重排。實測非兩端對齊內容 gapTrim 數千 ms→數十 ms;justify/
center/right/RTL 保留完整逐一定案、行為不變(真瀏覽器差分:justify trim 集指紋逐位相同)。
增量恢復(第三層,向下兼容 API):
- C1 持久化 Finder:跨 render 復用元素鍵快取(category/level/feature/祖先游走)。正確性靠
失效:新增 finder.invalidate(el) 清整棵子樹快取(祖先屬性變則後代快取變脏),revert 自動失效
其子樹(安全預設)。
- C3 rerender(root)=revert+render;局部恢復(實測編輯 400 段中一段 766×、matches 1441×)。
- C2 observe/disconnect:MutationObserver 自動增量——內容/分類屬性變化重處理最近塊、新增之塊
直接 render;render/revert 期間暫停觀察,自身注入絕不自觸發。
- append 修復(IV-3 差分複驗發現、單測漏掉):向葉塊(帶 jz-orphan)追加子塊使其轉非葉,須撤
容器陳舊 orphan——追加塊時若塊級父有 jz-orphan 直接子則改重處理該父。
測試 130→140(+S2 非葉塊、C1/C3、C2 觀察/失效/append、不自觸發)。基準設施:bench-incremental
(增量恢復)、bench-layout(版面層,headless Chrome)。dist 重建。
|
2026-07-12 02:20:20 +08:00 |
|
admin
|
1fae8ebf25
|
test(bench): 掃描層效能基準(jsdom,確定性 el.matches() 計數+計時)
Tier 2 基準設施:test/bench/bench-scan.mjs。建寬(多兄弟段落)+深(巢狀塊測 orphan、巢狀行內
測祖先游走)壓力結構,度量 render 之 el.matches() 呼叫數(確定性)與壁鐘中位數,供逐層提速實測。
正確補 Element.prototype.matches(非 HTMLBodyElement.prototype)。jsdom 無版面故僅覆蓋掃描層;
版面層(S1)另備 headless Chrome 基準。
|
2026-07-11 20:04:58 +08:00 |
|
admin
|
168e9e73f0
|
perf(finder,orphan): 第一層掃描提速——category 記憶化(S3)+orphan 葉塊預計算(S2),保行為
下游效能訴求一(掃描疑 O(n²))之第一層:無狀態、保行為、無公開 API 變更之確定收益。
S3 category 記憶化:finder.category 以 categoryCache(WeakMap<Element, InlineCategory>)記憶化,砍
除 collectRuns/jiyaAdjacency/jinze/split 跨 pass 對同一元素之重複 el.matches()(pill/isolate/
avoid 選擇器)。安全:單次 render 內一元素之分類恆定(class/skip 屬性/nodeName 不因 render 變更;
jz-* 包裹按 nodeName 分類亦恆定),且 Finder 隨 makeCtx 每次 render 重建 → 快取僅存活單次 render、
無跨 render 失效(持久化屬 C1,另議)。亲驗(正確補 Element.prototype.matches):pill/isolate 密集
頁面 el.matches() 8204→5842(約降三成);暫禁快取則 computeCategory 2322 vs 1041(命中率 55%)。
S2 orphan 葉塊預計算:以一趟上攀預計算「非葉塊」Set 取代逐塊 block.querySelector(sel) 掃整棵子樹之
O(Σ塊子樹)(深塊嵌套下 O(n²));某塊為另一塊之最近塊祖先 ⟺ 含塊級後代,scope 無涉、與原
querySelector 同口徑,行為等價。sel 亦外提出迴圈。
測試 129→130(+1 鎖定 S2 非葉塊等價:含巢狀塊之容器跳過、僅內層葉塊綁定);全綠、輸出不變。
文檔:ARCHITECTURE §4.1(category 記憶化)+§6.5.1(orphan 葉塊預計算)。
新發現(待議、非本層):剩餘 el.matches() 之最大來源為 insidePill 之「逐文本節點沿祖先鏈 pillMatches」
(eachTextNode 每文本節點一趟、未記憶化)——可作下一項提速機會。
|
2026-07-11 19:56:53 +08:00 |
|
admin
|
3fbb2904ef
|
fix(split): 攀升對稱守衛——切分前驗整條祖先鏈可切,opaque 中間祖先不再被克隆肢解
下游 .selection-bar-message 病例:段末實義字落於 opaque <button>(巢於可切 <span>),
splitElementAt 僅驗頂層 span 可切;splitTreeAfter 逐層克隆 textNode→top 祖先鏈時,中間
opaque button 從未複查即被克隆肢解(複製出 splitoff button、破壞互動元素)。firstText/
lastText 守下潛(遇非可切回 null),但攀升無對稱守衛——此即真正病根,A7 之 per-instance
.selection-bar-message 迴避僅治標。
修復:splitTreeAfter 切分前先驗「textNode.parentNode…top(含 top)」整條祖先鏈皆 canSplit,
任一層 opaque/pill/isolate/帶 id 即回 null → 呼叫端綁整個 top(退化但安全,與下潛對稱)。
純新增守衛、無新克隆邏輯;jinze 之 isolateBoundaryToken 因下潛已守,行為不變。附帶亦杜絕
中間祖先帶 id 之克隆重複 id。
測試(129 全綠,+2):末實義字在 opaque button(巢於可切 span)→ 綁整 span、無 splitoff、
button 完整無缺;退化路徑 render→revert 還原一致。亲驗:暫禁守衛則該測試於「無 splitoff」
斷言失敗(證明非空通過、病根屬實)。
文檔:split.ts 檔頭+splitTreeAfter JSDoc+ARCHITECTURE §6.5.1 補「canSplit 須對整條祖先鏈
成立、下潛+攀升雙向守衛」之不變量。dist 重建。
|
2026-07-11 13:31:42 +08:00 |
|
admin
|
efad638a04
|
test+docs: 逐元素標點樣式覆寫——回歸測試(6 例)+ README/ARCHITECTURE 說明
承 c00fb3e(data-jz-style/lang.stylePolicy 之設置格式已推送)。補 e2e 回歸與文檔。
測試(6 例,127 全綠):屬性 banjiao/選擇器 banjiao/屬性優先於選擇器/最近祖先勝/非法值
回退 lang/revert 還原(作者屬性保留)。
文檔:README 設置速查補 lang.stylePolicy + data-jz-style 兩行、新增「逐元素標點樣式」小節
(非段落用半角之選擇器與屬性兩範式);ARCHITECTURE §4.3 補 resolveStyle 優先序(逐元素覆寫
> policy > style > 內建)。
|
2026-07-11 01:46:43 +08:00 |
|
admin
|
e3296104a6
|
refactor: 專案整理階段二——透明判定單一真相源+跨透明邊界避頭尾+avoid 排除歸一
承階段一之下游審計。核心貫穿問題:「透明穿越」判定實有兩套口徑散落各處,抵觸 finder
檔頭「所有遍歷共用同一判定」之聲明。用戶抉擇:**嚴格白名單**(未知行內標籤=邊界,
透明須顯式登記 styleInlines)。
透明判定單一真相源(2a):
・transparentInline 提升為公開 isTransparent,作邏輯文本流之單一透明判定。
・collectRuns 改由 isTransparent 判定——未在 styleInlines 之未知行內標籤(如 <button>/自訂
元素)當隔離式邊界(斷 run、內部自成脈絡);isolate 與未知行內併為同一分支。
・jiyaAdjacency 同改用 finder.isTransparent;adjacentLogicalChar 沿用(僅隨改名)。
・分層澄清(承 #4 教訓,不強行一統):邏輯文本流統一為 isTransparent;另兩層各有正當之
不同邊界、刻意不併入——結構切分安全(split canSplit:更窄白名單,<a>/ABBR 不可切)與
物理版面(lineedge:getClientRects 物理相鄰,須跨 isolate 找真實鄰接)。檔頭改分層表述。
跨透明元素邊界避頭尾綁定(2b,修下游審計 #3):
・避頭類標點若落於透明元素邊緣(「你好<strong>。世界</strong>」之 。位於 strong 首位、與
「好」不同 DOM parent),舊版 forbiddenBreak 僅認直接 jz-char、render 僅處理 jz-char 之直接
父(strong),外層 p 之「好↔strong」綁定從不被評估 → 。漏綁、可誤置行首。
・偵測:edgeCharEl 經 finder.isTransparent 穿越透明元素定位邊緣 jz-char。
・父級處理:render 沿透明元素邊緣上攀納入外層 parent,父集深者先處理。綁定端沿用
isolateBoundaryToken(可切則最小綁定;邊界字為已 charify 之 jz-char 無法越過時退化整綁,
安全、與 <a> 同型)。
avoid 排除機制歸一(2c):
・isAvoided 改單一組合入口(自身即邊界 或 祖先鏈任一邊界),免除「avoidsSelf ‖ isAvoided」
易漏一半之誤用(階段一 #2 即此類);avoidsSelf 保留供遍歷原語逐元素下探自判(免 O(n²))。
・processPills/orphan/spacing root 收斂為 isAvoided(x);doc 補齊七種 avoid 來源與優先序。
一致性小項(2e):scope.include 型別去多餘 `| null`;PASSES 陣列補註(執行序由 pass.order
分群排序決定);gapTrim 補 featureEnabledFor("spacing"),與 lineEdge relayout 逐節點閘對稱。
評估後不做(2d):五處幂等判斷(jiya 祖先/longword 父/spacing 父/orphan 子/jinze 父)檢查
不同位置關係、無單一 isJz 形狀可全覆蓋;真正缺陷(jinze 二次 render 巢狀)已於階段一以 isJz
修復,其餘各自正確,強行歸一徒增風險(承 #4 教訓:正當差異不硬併)。
新增契約/回歸測試(button 嚴格白名單、跨透明邊界避頭);121 測試全綠、構建乾淨。
|
2026-07-07 21:05:25 +08:00 |
|
admin
|
30a11b5d6e
|
refactor: 專案整理階段一——確認缺陷修復(jinze 冪等/pill 自身 avoid)+死代碼清理
下游審計(4 路並行測繪 + 逐條源碼複驗)之階段一:落地已複驗確認之缺陷與低風險
清理;架構統一(透明判定單一真相源)與文檔重整留待階段二/三。
修復(複驗確認):
・jinze 二次 render(未 revert)產生巢狀 jz-jinze、違反 I6 冪等——守衛改判 parent
自身即本 pass 產物(isJz(parent, PASS)),已綁定內容不再重包(實測 jz-jinze 3→6 已修)。
・processPills 漏檢 avoidsSelf——自身命中 avoid(如 <code data-jz-skip>)之 pill 仍被補
間隙(isAvoided 只查祖先、不含自身,同「下游問題六」型)。processPills + root 自處理
分支補 avoidsSelf 檢查。
・三分類互斥:CODE/KBD 自 DEFAULT_SKIP_TAGS 移除——二者已由預設 pillSelector
"code, kbd" 以 pill 機制涵蓋(內部不可見+兩端補隙+斷 run/相鄰),再列 skipTags 致
同元素既 pill 又 avoid、pill 兩端間隙被 avoid 抹掉(違反互斥,且令上一項修復誤傷普通
code)。移除後普通 code 為純 pill、正常補隙;唯自身 data-jz-skip 者不補(即上項修復)。
清理:
・刪零引用死代碼 createMarker/ANY_CJK/BD_ALL/BD_CLASS_NAMES/CreateOptions.attrs;
isJz 由 jinze 冪等守衛啟用、脫離死代碼。
・死旗標 biaodian/emphasis/ruby 補 @reserved 註記(比照 hanging;點明與
charClass.biaodian 撞名異義,避免混淆)。
・JzKind 補註 data-jz-kind 第三值 "splitoff"(split.ts 直接設、不經 createJz);
longWord/orphan 冗餘 ===false 特判簡化(feature() 已對 false 回 false)。
複驗後重新定性(不照單全收):
・jinze 跨透明元素邊界避頭尾漏綁(你好<strong>。世界</strong> 之句末點號漏綁)——**真
缺陷**,但正解須改 jinze 之父級處理模型(穿越透明包裹上攀評估跨層綁定),與階段二
「透明判定單一真相源」一併處理更穩;forbiddenBreak 下探草稿已還原、缺口以註解標記。
・lineedge edgeRect 攀穿 isolate——**非缺陷**:edgeRect 為物理版面測量(getClientRects),
isolate 斷的是邏輯 run/相鄰,其內部字元之物理行首/行尾判定須跨 <mark> 找真實相鄰
內容(否則「文字<mark>「引」</mark>」之 「 誤判行首而錯收左半)。初評誤判、已還原並補註。
新增 3 回歸測試(jinze 冪等、預設 code 為 pill 非 skipTags、自身 avoid 之 pill 不補隙);
119 測試全綠、tsc + esbuild 構建乾淨。
|
2026-07-07 20:28:11 +08:00 |
|
admin
|
6c57461b61
|
fix: 標點擠壓句界例外——句末點號接開類保留右空白(。「 全角/開明皆 1.5em)
機制 A 行內連續擠壓原對 (L,R) 兩側獨立收:L 有右空白即收,不看 R 是誰,致句末
點號接開類(。「/?「)兩側皆收、合占 1em,丟失句界視覺。修為:L 為句末點號
(bd-stop)且 R 為開類(bd-open)時保留 L 右空白、僅收開類左側 → 全角式與開明式
皆 1.5em,對稱於 。」。例外僅限句末點號+開類;句內點號 ,「 與雙括號 」「/》《
仍兩側皆收 1em。
半角式不回歸:句末點號於 banjiao 由 baseline(機制 C)已收半形,守衛僅跳過
adjacency 之重複追加、不移除 baseline 之 jz-half;實測 32 可壓縮標點全半形、句界
組合句末點號仍半形(0 回歸)。唯非可壓縮 ·・‧/…— 各式皆全寬,屬設計。
唯一改動點 jiyaAdjacencyPass.squeeze() 加 stopBeforeOpen 守衛;lineedge/baseline
/jinze/spacing 不波及。新增 3 契約測試(116 全綠);README/ARCHITECTURE §6.3
同步句界例外。
|
2026-06-13 15:35:03 +08:00 |
|
admin
|
65a9df561e
|
test: 三分類優先序契約——pill 行為覆蓋 isolate(兩端補隙同口徑、雙命中 pill 勝出)
實證後鎖定契約:pill 與 isolate 之邊界口徑完全相同(同一段 processPills 邊界處理,
兩端補隙/斷 run/斷相鄰/不可切分),差異僅在內部(pill 不可見、isolate 照常);
同一元素同時命中兩選擇器時 pill 語義勝出(collectRuns/jiyaAdjacency 均 pill 先判)。
新增契約測試(113 全綠);README 三分類要點+types.ts 選項註記補優先序說明。
|
2026-06-12 17:23:11 +08:00 |
|
admin
|
c4e8c6cfd5
|
fix: 行內元素三分類(透明/隔離/整體)——jinze 下潛肢解 KaTeX 修復(下游問題六);v1 相容層移除
下游問題六:行內公式緊鄰禁則標點時,jinze 為綁鄰字透明下潛 .katex(全樹皆 SPAN,
僅憑標籤名判透明),把公式內側末字連同完整殼層逐層克隆切出(splitoff),上標漂移
錯位;pillSelector/skipAttribute/scope.avoid 三層防護全被刺穿。復現另揭兩向量:
相鄰 pill 時 descend 刺穿致 jz-hws 插入公式內部;pill 內部標點被 jiya charify。
確立三分類(§4.1,所有遍歷原語共用同一判定):
透明(styleInlines+jz-*)— 穿越/run 延續/可克隆切分;
隔離(isolateSelector,預設 "mark",新增選項)— 邊界斷 run/斷相鄰/不可切分、
兩端補隙(同 pill),內部照常處理(自成脈絡);
整體(pillSelector)— 內部對一切 pass 不可見(insidePill 過濾),禁則時作為
原子鄰字單位整體綁入 jz-jinze。
機制修復:
finder — transparentInline 改以元素判定(先排除 pill/isolate/avoid 再查標籤集);
avoidsSelf 自身判定(isAvoided 僅查祖先,自身命中 avoid 曾被穿越);descend 三態
(邊界字/無內容續找兄弟/blocked 終止——不可越界跳找更遠之字);eachTextNode
過濾 pill 內部;collectRuns 隔離元素內部自成 run。
core/split — canSplit 諮詢 finder(pill/isolate/avoid 不可切 → 退化整綁);
MARK 移出 SPLITTABLE(背景盒切分有可見接縫)。
jiyaAdjacency — pill/avoid(自身判定)斷相鄰不下探;isolate 斷而內部自成脈絡。
spacing Pass B — 邊界選擇器擴為 pill ∪ isolate;巢於 pill 內者跳過。
orphan — avoidsSelf 邊界;avoid 塊不綁(舊漏判:avoid 子樹內裸文本曾被計數包綁)。
v1 相容層移除(消費端已全數遷移 v2 API):src/compat/v1.ts、IIFE 全域別名
createCjkAutospace、死選項 autospaceClass(經查從未被任何 pass 讀取)一併刪;
I8 退役。dist/compat 殘留聲明清除、dist/core/split.d.ts 補入庫(前次遺漏)。
測試 112 全綠(問題六 ×7、isolate ×6、compat 移除 ×1);headless Chrome 實證
公式完整(splitoff=0、內部無 jz-hws/jz-char)、mark 與公式整體入 jz-jinze。
文檔:README 三分類節+選項示例+功能表(hanging 行補「已移除」);ARCHITECTURE
§4.1 三分類/§3.5 相容層移除/§6.1/§6.3/§6.4/I8 退役。
|
2026-06-12 17:15:24 +08:00 |
|
admin
|
a3c94ddee5
|
refactor: 移除標點懸掛(機制級死路)——負 margin 程式碼/CSS/型別一併刪
標點懸掛經實測為**機制級死路**(純 CSS 之寬度守恆矛盾):行端標點盒保持非零寬則墨色
出去多少版心內就空多少(行末實義字無法齊邊);收為零寬則釋放一整字寬、斷行器把下一字
拉上本行與標點重疊(且 justifyAtoms 預設之 inline-block 下負 margin 被 justify 吸收)。
CSS hack 接觸不到斷行器,此恒等式無解。唯一正解為原生 hanging-punctuation(僅 Safari
支援、且 jz-char 為 inline-block 原子非文本,原生懸掛不作用),屬遠期「原生管線」新功能、
與此處無共享,故不保留休眠碼。
移除:
lineedge.ts — jz-hang-* class、HANG_* 常量、applyEdge hang 分支、modeForChar、
HANG_ENABLED 總開關;行端僅餘 squeeze(半形)。
_jiya.css — jz-hang-* 三條規則(margin 半形 fallback 完整保留)。
types.ts — LineEndMode 型別、ResolvedOptions.lineEnd、features.hanging。
index.ts — lineEnd 鎖。
hanging 公開選項保留為 @deprecated no-op(API 相容)。
文檔:README「標點懸掛」節、ARCHITECTURE §6.5(開頭/§6.5.2/§6.5.4/§6.5.5/§6.5.6)、
demo(showcase §三/resize-check)全部由「停用/休眠」改為「已移除」+寬度守恆論證。
測試:防逃逸測試改寫為「hanging 為 no-op、輸出與不傳一致、各路徑不產生 jz-hang-*」。
全綠 101。dist 已無 jz-hang/lineEnd/HANG 殘留。
|
2026-06-11 16:16:01 +08:00 |
|
admin
|
2890549f44
|
fix: 行內排版透明性——pill 前標點誤隙+行內元素整體掉行+審計(Bug1/Bug2/T4-T6)
統一「行內元素對排版透明」原則在各遍歷原語之口徑,修復兩個下游 Bug 及三項同根隱患。
Bug1(pill 前標點誤加間隙):
isTransparentInline 補 JZ-CHAR/JZ-INNER。`漢字。<code>` 經禁則綁定後,
adjacentLogicalChar 之 descend 遇不透明 jz-char 既不進入亦不終止、而跳過它越過
標點取到「字」誤補 jz-hws;透明後回傳標點「。」(非 pill 鄰字)→ 不補隙,與
collectRuns 既有透明遞迴對齊。
Bug2(行內樣式元素整體掉行):
新增 core/split.ts 之 isolateBoundaryToken/splitElementAt——沿祖先鏈逐層把邊界
詞元剝入淺克隆(保留標籤/class/style/lang、去 id;標 data-jz-kind="splitoff"),
jinze/orphan 改最小綁定:`<strong>長串</strong>,` 只綁末字+逗號(非整串入 nowrap
inline-block 致行末整體掉行);段末長 strong 不再整綁溢出容器。revertPass 增延後
階段把 splitoff 併回原元素(先解包 wrap、後併 splitoff),render→revert 還原一致。
純樣式標籤白名單可切,a/abbr/ruby/帶 id 退化整綁。
審計(同根隱患):
T4 lineedge edgeRect 攀爬集改由 options.finder.styleInlines 派生(棄硬編碼子集,
免遺漏 ins/del/sub 等致行端誤判)。
T5 jiyaAdjacency 遇 pill(pillMatches)斷相鄰、不穿越(內容對排版不可見)。
T6 spacing processPills 補 per-node 功能閘(data-juzhen-off=spacing 區域內不補隙)。
文檔:README 切片表(spacing/jinze/orphan 三行)+ARCHITECTURE §4.1 透明性原則、
§4.4 splitoff 還原、§6.4 行內元素最小綁定、§5 jz-orphan 詞彙。
測試 90→102(Bug1×4、Bug2×6、T5/T6×2)。headless Chrome 實證 Bug2:段末 strong
最小切分、orphan 寬 60px 不溢出版心。
|
2026-06-11 16:06:17 +08:00 |
|
admin
|
04f2b25e4c
|
feat: lineedge 行端模式(squeeze);標點懸掛實作後全面停用(§6.5.2)
行端 lineedge 升級為可配模式(squeeze|hang|none),抽出共用行端標點偵測;
squeeze(現有行端半形)保留並修正「行歸屬判定」bug:jz-char 為 inline-block,其行盒
top 與相鄰純文本字形 top 因基線差約 5px、遠小於行高卻超舊容差(EPS=4px),致同行誤判
為下一行;改判「垂直區間」(下一行=b.top≥a.bottom、上一行=b.bottom≤a.top,容 2px)。
標點懸掛 hang 模式經 headless + 真實瀏覽器實測**確認不可行、全面停用**(純負 margin):
1. 句號墨色偏框左下,安全凸出量(<1em)只推出空白半框、墨色仍在版心內(視覺不懸掛);
2. 凸出滿 1em 釋放一整字寬、把下一字拉上本行致重疊;
3. 與 inline-block 對齊原子互斥(justifyAtoms:true 下 margin 被 justify 吸收);
4. ResizeObserver 重排時兩階段標記錯亂。
真正的解為原生 hanging-punctuation,惟 Chrome 不支援、且與本庫原子結構衝突。
**三重防逃逸**(下游以任何方式皆無法啟用):① index 鎖 lineEnd:"squeeze"(忽略 hanging
選項);② lineedge.modeForChar 無條件降 squeeze;③ applyEdge 之 HANG_ENABLED=false 總開關。
hang 之 lineedge 程式碼、CSS(jz-hang-*)、LineEndMode 型別保留休眠,俟原生支援再議。
hanging 選項仍被接受(API 相容)但無效果。
單元測試 90 通過,含防逃逸覆蓋(hanging:true/data-juzhen/blocks 各路徑皆不產生 jz-hang-*)。
|
2026-06-09 23:25:47 +08:00 |
|
admin
|
754b00da42
|
feat: 垂懸字避免(方案 C,斷行層無測量,order 70)(§6.5.1)
段末行 CJK 實義字數 ≥ N(預設 2;標點/符號不計)。standalone 段落級 pass:
自段末向前數 N 個實義字,把「第 N 字起至段末」(含其間/尾隨標點)包入
<jz-orphan>(white-space:nowrap)。該整體不可斷 → 末行恒含之 ⇒ 末行實義字 ≥ N,
任意寬度/字型/瀏覽器成立,無需量測 layout。
要點:
- 邊界字定位跨 jz-jinze/行內元素攀爬:實義字落於行內元素內時綁定整個塊級
直接子(不切割 nowrap 群、revert 乾淨);僅裸文本直接子才精確切分(最小綁定)。
- 僅處理葉段落(無塊級後代);段落實義字 < N 不綁;單行段方案 C 本就無害。
- 選項 orphan?: boolean | { chars? };**預設開 chars=2**(§6.5.6 建議);
下游可 orphan:false 關閉。段落級,data-jz-level=text 子樹自動跳過。
- order=70:jinze(40)/spacing(50)/longWord(60) 後、layout pass(90/92) 前。
新增 _orphan.css(jz-orphan{white-space:nowrap});JzTag 加 jz-orphan。
9 項單元測試(末 N+尾標點、chars 選項、標點不計、實義字<N 不綁、跨 jinze、
關閉、冪等、revert、段落級閘)。共 85 測試全綠。
窄容器拖動「末行恒 ≥N」之 layout 驗證留待 demo(Step 4-5,瀏覽器)。
|
2026-06-09 17:00:04 +08:00 |
|
admin
|
c1fbabf5b0
|
feat: 功能分級(文本級 vs 段落級)+ data-jz-level 標記(§6.5.0a)
整體計劃 §6.5 之前置基礎設施。把已實作功能切為兩級:
- 文本級(spacing 中西間隙、jiya 標點擠壓/鄰接):任意文本片段皆適用。
- 段落級(jinze 禁則、longWord 斷詞、lineEdge 行端、gapTrim):需行/段布局。
標記 API:
- 預設塊級元素 → 段落級(全功能)。
- 元素屬性 data-jz-level="text"|"paragraph"(最近祖先勝)。
- 選項 level:{ text: "選擇器" }(如標題/按鈕一鍵降為文本級)。
實作:
- 每個 pass 加 level 元數據;Finder.levelFor/levelAllows(含快取)。
- charify 段落級 pass(longWord)於共享走訪受閘;standalone 段落級 pass
(jinze/lineEdge/gapTrim)逐元素受閘。文本級不受限。
- spacing 補單元素 root 支援:root 為行內/單一非塊元素時亦處理(標題等
之中西間隙、標點半形生效)。
6 項新單元測試(text-level 跳段落級、選擇器標記、巢狀最近勝、單元素 spacing、
預設段落級回歸)。共 76 測試全綠。
|
2026-06-09 16:52:00 +08:00 |
|
admin
|
fdf071c5bb
|
feat: 全角句點 . 納入句末挤压 + 連續標點挤压規則(雙側獨立+透明穿越)
本會話標點挤压一致性兩項(改內置默認;皆以 Noto CJK + 真實 Chromium 量測驗證)。
一、全角句點 .(U+FF0E)納入句末標點
- BD_STOP 由 。!? 擴為 。.!?——納入全角句點 .(異於表意句號 。U+3002)。實測
Noto CJK SC/TC 之 halt(GPOS SinglePos)皆覆蓋 .、挤压收半形 advance 0.5em,與 。
一致。inkBias 同步補 .(簡 left/繁 center),margin 後備亦可挤。
- 政策行為與 。!? 同:全角式與開明式句末保持全角 1em,banjiao(或鄰接被收)挤半。
- 設計原則:halt 即字型之「挤压意願」——字型不提供某標點 halt(字形占滿、不宜挤,如某些
字型之全角 ?、〈〉)則信任字型、保持全角不強挤;margin 後備僅用於整體無 halt 之字型。
二、連續標點挤压規則:雙側獨立 + 透明穿越(修正既有不一致)
- 規則(用戶確定):相鄰兩標點,**對每一標點**獨立判斷——若其「有空白之側」緊鄰另一標點,
即挤掉該側空白(收半形)。原 jiyaAdjacencyPass 為「二選一只收一側」,致:
· ,「(pause 右空白 + open 左空白)原只收 ,→ 全角式 1.5em;應雙側皆收 → 1em。
改 else-if 為雙獨立 if。修後:,「 全角式與開明式皆 1em;。」 全角式 1.5em/開明式 1em
(」左為墨不收);·— 兩式皆 2em(不宜挤之例外)。
- 透明穿越:原以「同 parent 直接兄弟」掃描,漏掉跨 strong/em 等行內邊界之連續標點(致
跨邊界標點對未挤、達 2em)。改為從 root 之透明遍歷(穿越樣式行內;block 邊界與 avoid
子樹斷相鄰;jz-char 為原子不下探),跨 inline 邊界之連續標點亦正確配對。
- src/core/unicode.ts:BD_STOP +.;inkBias 表 +.。
- src/typeset/jiya.ts:jiyaAdjacencyPass 重寫(雙側收 + root 透明遍歷)。
- test:. 納入+連續挤压(雙側/句末單側/跨 inline/多標點鏈)回歸,70 測試通過。
- README/ARCHITECTURE:. 與連續挤压規則、halt 挤压意願原則;**涉長度處標明全角式/
開明式**(。」 全角 1.5em/開明 1em 等)。dist 重建(僅 JS,juzhen.css 不變)。
|
2026-06-09 15:19:35 +08:00 |
|
admin
|
b821cd1134
|
feat: 字符分類規則集抽象(charClass,可配置;向下兼容)
把散落於各 pass 之硬編碼字符分類(中文 CJK/西文 ANS/全寬標點 BD_* 子類)統一抽象為
規則集 Ruleset:內置默認即現狀值,消費端經 options.charClass 擴充,以覆寫 spacing/
jiya/jinze 對特定字符之分類判定。
動機:英文全角句號 .(U+FF0E,異於中文句號 。U+3002)實證為**完全未分類**——不被
charify、不挤壓、與 CJK 間不補隙。與其每次硬編碼補字符(如先前 € 之西文集),不如給
消費端可配置之分類鉤子。
API(追加語義):
charClass?: {
cjk?: string; // 追加為中文
western?: string; // 追加為西文(半寬標點亦歸此)
biaodian?: { stop?, pause?, open?, close?, middle?, liga? }; // 追加為全寬標點,按子類
}
實作:
- core/unicode.ts:新增 Ruleset/CharClassOverride/BdSubclass 型別、DEFAULT_RULESET、
resolveRuleset(追加合併);classifyBiaodian(ch, biaodian) 改吃規則集(預設參數向下兼容)。
- types.ts:JuzhenOptions.charClass;ResolvedOptions.ruleset;index 再匯出公開型別。
- index.ts:resolveRuleset(opts.charClass) → ResolvedOptions.ruleset。
- typeset/spacing.ts:所有 CJK/ANS 依賴正則移入 makeRules(ruleset),per-instance 編譯
(非每 block);ALNUM/ALPHA/運算符等結構性 ASCII 仍為固定常數。
- typeset/jiya.ts:classifyBiaodian(ch, ruleset.biaodian)。
- typeset/jinze.ts:詞元邊界之 anyCjk 由 ruleset.cjk 建。
向下兼容:不傳 charClass 則 ruleset === DEFAULT_RULESET、行為與重構前完全一致。
v1 為追加語義,不支援重分類/移除內置字符(範圍式集合難挖洞、需求罕見,列後續)。
驗證:實 Chromium end-to-end——charClass.biaodian.stop="." → . charify 為 bd-stop
(开明式句末維持全角,比照 。);western="★"/cjk="〇" → 與對側正確補/不補中西間隙。
測試 66 通過(含 9 條 charClass 新測——默認未分類/各子類 charify/實例隔離不可變
(resolveRuleset 不污染 DEFAULT)/追加不破壞內置/多子類組合;57 既有不變)。
- README「自定義字符分類」節+ARCHITECTURE §4.2 規則集抽象。dist 重建。
|
2026-06-04 16:22:14 +08:00 |
|
admin
|
5b9c6b5fa1
|
refactor: 字型移出聚珍管理(移除 _lang.css),交消費端;半形顯式保留 locl
下游回饋之字型樹需求。決策:字型屬消費端領域,聚珍不應內建字型堆疊(原 _lang.css
之 Noto 後備係越權,且曾蓋掉消費端字型釀 bug)。採「完全移除、交消費端 CSS」(A 案),
否決「以參數管字型」(B 案——JS 管字型須注入 CSS、違反 JS 注入結構/CSS 給視覺之分工;
而缺 halt 字型須逐字型微調 margin 之指引,由既有 jiya.halfWidth/jiya.bias +文檔承擔)。
移除:
- 刪 src/css/_lang.css,juzhen.css 移除其 @import。dist/juzhen.css 現零 font-family
規則(3.1→2.5kb)。
聚珍保留之三項非管理性本分(確保不礙消費端字型與 locl):
- _normalize.css `jz-* { font: inherit }`:原子繼承消費端字型、不強加。
- _jiya.css 半形 font-feature-settings 顯式改 "locl" 1, "halt" 1(預設+halt-island 兩
處):確保泛 CJK 字型(思源/Noto CJK)之地域標點字形不因設 halt 而失。
- render 只讀不改 lang。body 文本之 locl 全由消費端字型+正確 lang 決定。
驗證(真實 Chromium):
- halt 模式:消費端逐 lang 字型生效並繼承至 jz-inner,無強制 Noto。
- margin 後備:半形盒 0.5em、字身依墨色偏側位移正確、jz-inner 繼承消費端字型、
feature normal(locl 既定開),皆不受字型移除影響。
- demo/index.html、resize-check.html 改示範「消費端逐 lang 設 font-family」之推薦範式;
fallback.html 更新註解。
- README 新增「字型與 locl」節+改寫 CSS 基礎設定字型項;ARCHITECTURE §6.9 改寫
(A 案決策、否決 B 案、三項本分)+檔案樹。
- test:新增 CSS 回歸(不內建 font-family、半形保留 locl)。57 測試通過。
- dist/ 重建(僅 juzhen.css 變動,JS 不變)。
|
2026-06-03 02:04:34 +08:00 |
|
admin
|
79fee8481f
|
fix: 中西間隙下游回饋批次(B 省略號/C+E gapTrim 自我應驗/D 西文集/標點稽核)
實 Chromium 定位並修復下游回饋(Peek.nvim,commit 96f5ac3 基線)之全部間隙問題,
另完成現代漢語標點之誤隙稽核。jsdom 不可信(TreeWalker/offset 失真),結論均以
真實 Chromium 量測。
B 全角省略號誤隙:DOTS_CJK 之 alternation `|…` 誤把全角省略號 U+2026 納入,致
`……` 後接 CJK 冒出中西隙。移除 `|…`,僅留 ASCII `...`。
C+E gapTrim 自我應驗誤判(容器寬相關「最後一個間隙消失」、pill 跨行右隙消失):
間隙之 margin-right 參與斷行;依「後字是否在更低行」判行末,而後字落點又被
margin 推擠 → 間隙自身 margin 把後字擠到次行 → 判行末 → trim 去 margin → 後字
回同行,自相矛盾、卡死於「已 trim、後字同行、間隙消失」。污染雙向:下游間隙之
margin 亦令夾在中間之內容放不下其行。修法:trimGaps 改「起始全部 trim、由左至右
逐一定案」——量測時前方已定案、自身與其後皆 trim,後字落點不受任何 margin 污染。
以 lastRect 為準兼顧跨行 pill。實測 8–22em(ragged/justify)偽 trim 歸零、真行末
仍 trim(justify 齊右不變)。
D 西文字集完整化(列舉法,2016/Windows 10 相容、\uXXXX 無需 flag):補入貨幣
U+20A0–20BF(€ 等)、拉丁擴充 A/B(拼音聲調 ā ǎ ē)、西里爾、數學運算符
U+2200–22FF;並把希臘由原 U+0370–037D 補全至整塊(原漏 α-ω 本身)。€ 鄰接 CJK
之手工空格遂正規化、邊界補隙。
標點稽核:實 Chromium 全部常用中文標點 × 簡繁共 104 例,CJK 間皆 0 誤隙。修正
唯一偏例——收尾彎引號 `”`(pangu QUOTE_AN 令其後接拉丁/數字補隙,為唯一會補隙
之收尾標點,與 」』)》】〕 不一致;clreq 下全角收尾標點字身已含右側留白)。移除
QUOTE_AN。
- src/core/unicode.ts:WEST_SHARED 列舉補全(\uXXXX、區塊註記)。
- src/typeset/spacing.ts:DOTS_CJK 去 `|…`;移除 QUOTE_AN。
- src/typeset/lineedge.ts:trimGaps 起始全 trim、左→右逐一定案;新增 lastRect。
- test/juzhen.test.mjs:B/D/標點稽核/彎引號 回歸(56 測試通過)。
- demo/resize-check.html:可拖曳容器檢查頁(各寬度間隙穩定性、標點無誤隙)。
- README.md 目視驗證、ARCHITECTURE.md §6.1/§6.1a 自我應驗/§6.1b 標點稽核。
- dist/ 重建(juzhen.css 不變,僅 JS 邏輯)。
|
2026-06-03 00:57:40 +08:00 |
|
admin
|
96f5ac32e1
|
fix: 原子透明——段落繼承屬性漏溢修復(text-indent/hanging-punctuation/text-decoration)
下游回報:段落設 text-indent 時,標點附近冒出巨大間距。根因為聚珍把標點拆成
inline-block 原子,繼承屬性被每個原子重複套用(過套用),且裝飾盒跳過原子(欠套用)。
以「同類問題整體界定」原則統一在庫內修復,使原子對消費端段落樣式透明。
範圍(headless Chrome 逐屬性實測界定,非逐案補丁):
過套用(繼承屬性被 inline-block 原子各自重套),於原子歸零:
- text-indent: 0——段落首行縮排傳入後每原子內部各縮排一次,shrink-to-fit 寬膨脹為
縮排量+字形。實測:歸零後段落首行縮排保持 2em、原子寬由 ~3em 回到 0.5em/1em。
- hanging-punctuation: none——同屬區塊容器首/末行機制;Chrome 未實作、Safari 觸發,
且與規劃中之標點懸掛相衝,按機制一併歸零。
欠套用(text-decoration 非繼承、由段落裝飾盒繪製並跳過原子致下劃線斷缺),令原子
inherit 自繪接續:
- jz-char/jz-jinze 設 text-decoration: inherit;margin 模式 jz-inner 亦為
inline-block,於 _jiya.css 補一條。像素掃描實測:halt/margin 兩模式下劃線
coverage 由 ~0.25 回到 1.0,且 margin 下劃線隨半形收為 0.5em advance。
不處理者(實測不洩漏或本應逐字穿透):letter-spacing/word-spacing/text-align 系/
text-transform/line-height/color/font/word-break/overflow-wrap/hyphens/
white-space/tab-size/text-emphasis/text-shadow/font-variant/font-stretch。
- src/css/_normalize.css:過套用歸零 + text-decoration 透明(含原則與實測依據注釋)。
- src/css/_jiya.css:margin 模式 jz-inner 裝飾接續。
- test/juzhen.test.mjs:三項回歸斷言(51 測試通過)。
- README.md CSS 基礎設定第 8 項、ARCHITECTURE.md「原子透明原則」一節。
- dist/ 重建。
|
2026-06-02 17:18:41 +08:00 |
|
admin
|
7b4a051a1f
|
feat: 標點半形 margin 後備模式+下游回饋修復
margin 後備(供缺 OpenType halt 之字型):
- jiya.halfWidth: "halt"(預設)|"margin"。margin 以 width:0.5em+字身依墨色
偏側溢出定位,於 CSS 盒模型重現 halt 之半形 advance(真 0.5em;相鄰標點各
0.5em、合佔 1em,無負 margin 之鄰接疊加/單側重疊)。模式由 data-jz-halfwidth
子樹屬性決定(可逐區塊/逐 lang,含 halt-island 反置)。
- 偏側 inkBias 依字身墨色中心給 left/center/right/full;參考表據方正書宋 GBK
實測,consumer 可經 jiya.bias 逐 lang 逐字覆寫。
- _lang.css 字型堆疊僅作後備(全包 :where 零特異度、只命中帶 lang 屬性之元素、
不直接覆寫 jz-inner)→ 消費端 font-family 恆優先,標點隨之以消費端字型渲染。
- 新增 tools/measure-bias.py(量 glyph ink 中心、產偏側建議表)+ README
「字型適配工作流」。demo/fallback.html:方正書宋 GBK 對照 halt 失效 vs margin。
下游回饋修復:
- 1a:adjacentLogicalChar 透明穿越 jz-jinze(同 jz-hws/樣式行內)→ pill 緊鄰
CJK 標點時,左緣間隙與作者空格剝除復原(jinze 先於 spacing 之交互)。
- 1b:CJK↔CJK 贅餘作者空格(含跨 inline 標籤)剝除、不留隙,與 CJK↔Latin 之
邊界正規化一致(DEL 哨符,全形空格 U+3000 不動)。
- 問題二:新增 justifyAtoms 選項;false → jz-char/jz-jinze 改 display:inline,
相容分頁器(Paged.js)對齊引擎(halt 半形與禁則 nowrap 保留)。
測試 43→49 全通過;dist 重建;README/ARCHITECTURE 同步更新契約。
|
2026-06-02 01:21:52 +08:00 |
|
admin
|
2d42c7ecf2
|
feat: 聚珍切片 1 — 中西間隙/標點擠壓/禁則 + v1 相容層
從零重寫之第一垂直切片(依 ARCHITECTURE.md 契約),涵蓋並超越 v1 全部功能。
核心基座(src/core):unicode 字元表+標點子類+字形側位;locale 語言與
全角/開明政策(全域 lang.style +逐 lang policy);dom(jz-* 工廠+可還原
標記);finder 遍歷引擎(avoid/scope.include/邏輯 run/pill 鄰字/分塊閘);
pass 聚合管線。
功能 pass(src/typeset):
- spacing:中西間隙採 margin 模型——包裹邊界左側字、以 margin-right 留隙,
不注入空白字元(textContent 完全不變、複製緊湊、justify 不暴增、不撐框、
行首乾淨)。含跨樣式邊界、pill、原檔空格移除。
- gapTrim(lineedge):layout pass,行末去隙,修正 justify 右緣內縮。
- longword:長英文詞 <span lang> + hyphens。
- jiya:標點 charify + 全角/開明/半角(全域 + 逐 lang);半形以字型 OpenType
halt 渲染(按字形正確定位、不重疊,居中字形 !? 與繁體 locl 句逗點號皆正確);
行內連續擠壓。
- jinze:行首行末避頭尾(閉類/點號綁前字、開類綁後字,以詞元為邊界、長引文
中段可斷);亦為 justify 綁定載體。
- lineedge:行端擠壓 layout pass(括號引號;ResizeObserver 重算,瀏覽器專用)。
§8 justify:jz-char inline-block 原子,句末標點單份間距。
v1 相容層(compat/v1):createCjkAutospace().apply 映射,IIFE 全域別名。
構建:esbuild → ESM/IIFE/CSS;tsc → d.ts。測試:39 個 jsdom 案例。
demo:簡繁並列+justify+強制繁體開明+標點寬度量測,載 Noto webfont 作參考
(擠壓依賴字型 halt)。間隔號 · 不觸發中西間隙;200% 等詞元不被禁則切斷。
|
2026-06-01 21:31:07 +08:00 |
|