初始:cjk-autospace 工廠函式 + TS 宣告 + README

雙 pass 架構:
- Pass A — 對每個 BLOCK 走 DFS 收集邏輯文本(樣式標籤透明、PILL
  與子 BLOCK 中斷 run),套 panguSpace,把 MARK 反映射回
  textNode / offset 並插入 .cjk-autospace marker span
- Pass B — 對每個 PILL(pillSelector)透過樣式鏈遞迴找邏輯鄰字,
  按 0 / 1 / 多 + 特殊規則處理空白

工廠 createCjkAutospace(options) 接受 pillSelector / styleInlines /
blockTags / skipTags / autospaceClass / skipAttribute / isSkipped 等
配置。UMD-ish export 同時支援 ESM、CommonJS、browser global 與
inline script 形式。

規則表 port 自 Pangu.js v7.2.1(MIT),加上跨樣式邊界透明與行內塊
邊界兩 pass 為原創設計。
This commit is contained in:
2026-05-22 22:18:45 +08:00
commit c3defc4054
4 changed files with 593 additions and 0 deletions
+62
View File
@@ -0,0 +1,62 @@
# cjk-autospace
CJK ↔ Latin / 數字 / 標點 / 引號 / 括號 / 運算子等邊界之自動間距 shim。Pangu.js v7.2.1 規則之 ES5 端口,加上「跨樣式標籤透明 + 行內塊邊界」兩 pass 架構。工廠模式,由消費端注入配置。
## API
```js
const autospace = createCjkAutospace({
pillSelector: "code, kbd, .your-pill-class",
});
autospace.apply(document.body);
```
回傳 `{ apply(root?) }`。冪等:在已注入 marker 之 DOM 上重跑會跳過既有 `.cjk-autospace`。完整選項見 `cjk-autospace.d.ts`
## CSS 需求
```css
.cjk-autospace {
-webkit-user-select: none;
user-select: none;
/* 視覺寬度由消費端決定,例如 0.25em via inline-block + width */
}
```
`user-select: none` 讓選取與剪貼板跳過 markermarker 內含真實 ASCII spacelayout 視為 whitespace:行末 collapse、容許換行、justify 可拉伸)。
## 標籤三分類
- **STYLE_INLINES**`<strong>` / `<em>` / `<a>` / `<b>` / `<i>` / `<u>` / `<s>` / `<mark>` / `<ins>` / `<del>` / `<sub>` / `<sup>` / `<small>` / `<abbr>` / `<cite>` / `<dfn>` / `<q>` / `<span>` / `<var>` / `<samp>` / `<time>` / `<bdo>` / `<bdi>` / `<ruby>` / `<rb>` / `<rt>` / `<rp>` / `<wbr>`)— shim 透明走過。跨樣式之 Pangu 邊界(如 `<strong>是</strong> Neovim`)由 Pass A 在剝離後識別。
- **PILL**(由 `pillSelector` 指定,預設 `code, kbd`)— 行內塊。邊界由 Pass B 處理,與內容鄰接(CJK / 字母數字)即插 marker。
- **BLOCK**`<p>` / `<div>` / `<h1>``<h6>` / `<li>` / `<pre>` / `<table>` / `<br>` / `<hr>` / …)— 段落級。Pass A 把它們當作 run 邊界,子 block 各自一次。
## 兩 pass
- **Pass A — per-block Pangu on tag-stripped text**:對每個 BLOCK 走 DFS 收集邏輯文本(STYLE 透明、PILL 與子 BLOCK 中斷 run、既有 marker 也中斷),整段套 `panguSpace`,把 MARK 反映射回 textNode/offset。MARK 對應原文若是單 ASCII space 則 strip 後插 marker,否則純插。actions 倒序執行確保前面偏移不被後面動作影響。
- **Pass B — pill boundary**:對每個 PILL 透過 STYLE 鏈遞迴找邏輯鄰字(`adjacentLogicalChar`),鄰字若是 CJK / 字母數字直接插 marker;若是 1 個 ASCII space 且另一側為內容字則 strip 後插 marker;其餘(標點 / 多空格 / 換行 / 既有 marker)跳過。
## 配置選項
| 選項 | 預設 | 說明 |
|---|---|---|
| `pillSelector` | `"code, kbd"` | 行內塊 CSS 選擇器 |
| `styleInlines` | 預設樣式標籤集 | tagName 大寫之陣列 |
| `blockTags` | 預設段落標籤集 | tagName 大寫之陣列 |
| `skipTags` | mono / form / script / style | tagName 大寫之陣列;整 subtree skip |
| `autospaceClass` | `"cjk-autospace"` | marker span class 名 |
| `skipAttribute` | `"data-md-key"` | 任一祖先帶此屬性即整 subtree skip |
| `isSkipped` | (無) | 自訂 skip callback;先於內建判定檢查 |
## 模組形式
UMD-ish
- **ES module / TypeScript**:直接 `import { createCjkAutospace } from "./cjk-autospace.js"`(搭配 `cjk-autospace.d.ts`
- **CommonJS / Node**`module.exports.createCjkAutospace`
- **Browser global**`globalThis.createCjkAutospace`
- **Inline `<script>`**:函式宣告即註冊於 enclosing scope
## 授權
基於 Pangu.js v7.2.1 的規則表 port[原始項目](https://github.com/vinta/pangu.js) MIT)。