mirror of
https://github.com/cverinc/tugtile.git
synced 2026-07-22 07:42:19 +00:00
Release 0.1.10 — keyboard-complete board + tested/documented CJK
- The full create → sort → edit → archive loop now works without the mouse: Space / A / D check, archive, or delete a focused card; arrow keys tug a tile within or across lanes; Tab to a lane header and arrow-reorder the whole lane; new / rename lane are rebindable commands. - IME composition is guarded on every keyboard surface (the editor, card submit, and the board's move keys), so a Chinese / Japanese / Korean candidate-confirm is never mistaken for a shortcut. - Long-card collapse is width-aware for CJK (fullwidth chars count as two). - New CJK.md documents the six CJK guarantees and how each is verified. - README: keyboard + CJK highlights refreshed across all four languages. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
5a1483968a
commit
46c8c89376
5 changed files with 416 additions and 17 deletions
111
CJK.md
Normal file
111
CJK.md
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
# CJK support in tugtile
|
||||
|
||||
tugtile is built to be a first-class kanban for Chinese, Japanese, and Korean boards — not
|
||||
latin-first with CJK bolted on. This document turns that claim into something you can check:
|
||||
every guarantee below states **what it promises** and **how it is verified** — an automated test
|
||||
where the logic is pure, a short manual repro where a live browser / IME is required.
|
||||
|
||||
The pure logic lives in the engine block (`plugin.src.js`, the `CORE` region: `homeKey`,
|
||||
`displayWidth`, `parseFile`, `serializeFile`, …). It has no Obsidian dependency, so it runs in
|
||||
plain Node and is unit-tested in `test/cjk.test.cjs`.
|
||||
|
||||
## The 6 guarantees
|
||||
|
||||
### 1. Round-trip fidelity for CJK content
|
||||
**Promise:** A card whose body is Chinese / Japanese / Korean text survives `parse → serialize`
|
||||
byte-for-byte. We never re-encode, normalise, or "clean up" your characters — `tile.raw` is the
|
||||
exact markdown from your file and is what we write back.
|
||||
|
||||
**Verified by:** automated — `test/cjk.test.cjs` (`raw line preserved verbatim`,
|
||||
`parse→serialize keeps the fullwidth checkbox line`, `round-trip idempotent`) and the broader
|
||||
round-trip suites in `test/core.test.cjs` / `test/archive-home.test.cjs`, which exercise CJK
|
||||
boards (`## 進行中`, `卡片內容`, `%%tg-home:進行中%%`).
|
||||
|
||||
### 2. Fullwidth / non-ASCII checkbox tolerance
|
||||
**Promise:** The board parser does not assume the checkbox mark is ASCII. A card written
|
||||
`- [X] …` (fullwidth X) — or any single non-ASCII mark inside the `- [ ]` brackets — parses
|
||||
without crashing, and the mark is captured and preserved verbatim.
|
||||
|
||||
**Verified by:** automated — `test/cjk.test.cjs`
|
||||
(`parseFile tolerates fullwidth checkbox mark (no crash)`,
|
||||
`fullwidth checkbox mark captured verbatim`). The parser's matcher is `/^- \[(.)\]/`, capturing
|
||||
any single code point rather than a fixed `[ xX]` set.
|
||||
|
||||
### 3. Display-width awareness (long-card collapse)
|
||||
**Promise:** The "this card is long, collapse it to its title" decision reasons in **visual
|
||||
columns**, not UTF-16 code units. A CJK character occupies ~2 columns, so a 30-character Chinese
|
||||
title (~60 columns wide) collapses at the same *visual* length as a 60-character latin title —
|
||||
neither language is treated as "shorter" than it looks.
|
||||
|
||||
**How it works:** `displayWidth(str)` counts East-Asian wide / fullwidth code points as 2 and
|
||||
everything else as 1, iterating by Unicode code point (so surrogate-pair / astral CJK characters
|
||||
are measured once). The host collapse check is `displayWidth(meta.clean) > 60` — the threshold is
|
||||
**60 columns**, deliberately *not* doubled: 60 latin chars = width 60 (identical to the previous
|
||||
`.length > 60` behaviour), while ~30 CJK chars = width 60.
|
||||
|
||||
**Verified by:** automated — `test/cjk.test.cjs`. `displayWidth` cases: latin-only (`"hello"→5`),
|
||||
CJK (`"理牌"→4`), mixed (`"a理b"→4`), fullwidth punctuation (`":,"→4`), empty (`→0`), kana /
|
||||
hangul / ideographic-space, and a surrogate-pair char (`"𠀀"→2`, not 4). Boundary cases assert
|
||||
latin behaviour is unchanged (60→not long, 61→long) and the CJK collapse point
|
||||
(29 CJK = width 58 → not long; 31 CJK = width 62 → collapses).
|
||||
|
||||
### 4. Fullwidth-space tolerant lane matching
|
||||
**Promise:** A lane named with a fullwidth ideographic space (U+3000, common when typing with an
|
||||
IME) still matches its own archive-restore token. `homeKey()` collapses all whitespace — including
|
||||
U+3000 — to a single ASCII space, on both the writing and the matching side, so an archived card
|
||||
restores to the right lane.
|
||||
|
||||
**Verified by:** automated — `test/cjk.test.cjs` (`homeKey collapses fullwidth space U+3000`,
|
||||
`homeKey fullwidth-space == ascii-space twin`) and the end-to-end archive restore in
|
||||
`test/archive-restore-e2e.test.cjs`.
|
||||
|
||||
### 5. IME composition guard (keyboard surfaces)
|
||||
**Promise:** While you are mid-composition with an IME — picking among Chinese/Japanese candidate
|
||||
characters — pressing Enter or an arrow key confirms / navigates candidates and **never** fires a
|
||||
tugtile shortcut. This is guarded consistently in **all three** keyboard surfaces:
|
||||
- the full-screen **editor** (`editor-core.js` — `compositionstart`/`compositionend` gate the
|
||||
re-highlight, and `Enter`/`Tab` check `!e.isComposing` / `keyCode !== 229`),
|
||||
- **card submit** (`plugin.src.js` — the inline-editor Enter handler checks
|
||||
`!(e.isComposing || e.keyCode === 229)`),
|
||||
- the **board card keydown / arrow handler** (`plugin.src.js` — bails on
|
||||
`e.isComposing || e.keyCode === 229` before any bare-key card action).
|
||||
|
||||
Because composition state is browser/IME-driven, this guarantee is verified by hand, not in Node.
|
||||
|
||||
**Manual repro:**
|
||||
1. Open a board, focus a card's inline editor (or the full-screen editor), and switch your OS to a
|
||||
Chinese or Japanese IME.
|
||||
2. Type a syllable so candidate characters appear (composition is active), then press **Enter** to
|
||||
confirm a candidate — the candidate is inserted; the card must **not** submit/close.
|
||||
3. With a card focused on the board (not its input), start composing and press the **arrow keys** —
|
||||
candidate navigation should work and the card must **not** move between lanes. Releasing the IME
|
||||
and pressing the same keys then behaves normally.
|
||||
|
||||
### 6. CJK line-wrap inside cards
|
||||
**Promise:** A long Chinese / Japanese / Korean title wraps onto multiple lines *inside* the card
|
||||
rather than overflowing or being clipped, so the full title stays readable on the board.
|
||||
|
||||
Wrapping is layout behaviour in a live browser, so it is verified by hand.
|
||||
|
||||
**Manual repro:**
|
||||
1. Create a card with a long single-line CJK title (e.g. 40+ Han characters, no spaces).
|
||||
2. On the board, confirm the title wraps within the card's width and every character is visible
|
||||
(the card grows in height; text is not cut off horizontally).
|
||||
3. Collapse/expand the card (guarantee 3) and confirm the collapsed title line behaves the same.
|
||||
|
||||
## Known limitation (honest disclosure)
|
||||
|
||||
**Table / row view truncates CJK at ~30 visible characters.** The compact row view caps each row's
|
||||
text at `max-width: 60ch` (`styles.css`, the `.tugtile__row-text` rule) with an ellipsis. Because
|
||||
`ch` is the width of the `0` glyph, a CJK character (~2× that width) consumes roughly two `ch`, so
|
||||
CJK row text is visually truncated at about **30 characters** where latin truncates at ~60. This is
|
||||
a **cosmetic** limit of the dense table view only — the full title is always intact in the card
|
||||
view and on disk (round-trip fidelity, guarantee 1, is unaffected). It is documented here rather
|
||||
than hidden; a future fix would switch that cap to a display-width-aware measure.
|
||||
|
||||
## Running the tests
|
||||
|
||||
```
|
||||
node test/cjk.test.cjs # the CJK-specific engine tests
|
||||
bash scripts/test.sh # full suite: syntax + i18n + builds + every test/*.cjs
|
||||
```
|
||||
37
README.md
37
README.md
|
|
@ -32,12 +32,19 @@ whole table was easier than patching someone else's. Written from scratch (clean
|
|||
- **Never lose a card** — built-in undo / redo, a backup before the first write, and
|
||||
overwrite protection: if the file changes elsewhere, tugtile reloads instead of
|
||||
clobbering it. (Backed by a data-safety test suite.)
|
||||
- **Hands never leave the keyboard** — arrow keys tug a tile within or across lanes;
|
||||
Space / A / D check, archive, or delete it; Tab to a lane header and arrow-reorder the
|
||||
whole lane; new / rename lane are rebindable commands. Sort a whole board without
|
||||
reaching for the mouse.
|
||||
- **An editor that survives phones** — two-row toolbar, virtual-keyboard-aware layout on
|
||||
iOS/iPad; tug lanes & tiles to reorder, dates/times, tags, stash (archive).
|
||||
- **Your data stays Markdown** — human-readable, diff-friendly, portable. Board and
|
||||
Table views over the same note, plus a raw-editor button when you want the text itself.
|
||||
- **CJK-first, concretely** — re-highlighting waits while your IME is composing, so CJK
|
||||
input is never interrupted; `#tags` accept CJK; sorting is locale-aware.
|
||||
- **Your language isn't an edge case** — every keyboard surface (the editor, card submit,
|
||||
*and* the board's move keys) pauses while your IME is composing, so a Chinese / Japanese
|
||||
/ Korean candidate-confirm is never mistaken for a shortcut; card collapse is
|
||||
width-aware; `#tags` accept CJK. The six CJK guarantees — and how each is verified —
|
||||
live in [CJK.md](./CJK.md).
|
||||
- **No network, no telemetry** — your boards never leave your vault.
|
||||
|
||||
### Install
|
||||
|
|
@ -83,12 +90,17 @@ tugtile の前身は、obsidian-kanban のために書いた plugin の山(plu
|
|||
|
||||
- **カードを失わない**:取り消し/やり直しを内蔵、最初の書き込み前にバックアップ、
|
||||
ファイルが外部で変わったら上書きせず再読み込み。(データ安全テストで保証)
|
||||
- **手はキーボードから離れない**:矢印キーでタイルを同じ列/隣の列へ動かし、Space/A/D
|
||||
で完了・アーカイブ・削除、Tab で列見出しへ移って矢印キーで列ごと並べ替え。列の新規作成
|
||||
/名前変更は割り当て可能なコマンド。ボード一枚、マウスに触れずに片づきます。
|
||||
- **スマホで生き残るエディタ**:2 段ツールバー、iOS/iPad の仮想キーボードに追従する
|
||||
レイアウト。列もタイルもつまんで並べ替え、日付/時刻、タグ、アーカイブ。
|
||||
- **データはずっと Markdown**:人間が読めて、diff しやすく、持ち運べます。同じノートを
|
||||
ボード/表の 2 ビューで。テキストそのものを触りたいときは raw エディタボタンも。
|
||||
- **CJK ファースト、具体的に**:IME の変換中は再ハイライトを待機し、日本語入力を決して
|
||||
中断しません。`#タグ` も CJK 対応、並べ替えはロケールを認識します。
|
||||
- **あなたの言語は、例外じゃない**:すべてのキーボード操作(エディタ、カード送信、そして
|
||||
ボードの移動キー)は IME の変換中は待機するので、日本語・中国語・韓国語の変換確定が
|
||||
ショートカットと取り違えられることはありません。長いカードの折りたたみは文字幅を考慮し、
|
||||
`#タグ` も CJK 対応。6 つの CJK 保証と、その検証方法は [CJK.md](./CJK.md) に。
|
||||
- **ネットワークなし、テレメトリなし**:ボードが vault の外に出ることはありません。
|
||||
|
||||
### インストール
|
||||
|
|
@ -134,13 +146,18 @@ tugtile은 원래 obsidian-kanban을 위해 쓴 플러그인들(플러그인의
|
|||
|
||||
- **카드를 잃지 않습니다**: 실행 취소/다시 실행 내장, 첫 쓰기 전 백업, 파일이 외부에서
|
||||
바뀌면 덮어쓰지 않고 다시 읽기.(데이터 안전 테스트로 보장)
|
||||
- **손이 키보드를 떠나지 않습니다**: 화살표 키로 타일을 같은 열이나 옆 열로 옮기고,
|
||||
Space/A/D로 완료・보관・삭제, Tab으로 열 머리글로 이동해 화살표 키로 열 전체를 재정렬.
|
||||
열 추가/이름 변경은 단축키를 지정할 수 있는 명령입니다. 보드 하나를 마우스 없이 정리합니다.
|
||||
- **휴대폰에서 살아남는 에디터**: 2단 툴바, iOS/iPad 가상 키보드를 따라가는 레이아웃,
|
||||
열과 타일을 집어서 정렬, 날짜/시간, 태그, 보관함.
|
||||
- **데이터는 언제나 Markdown**: 사람이 읽을 수 있고, diff 친화적이며, 어디로든 가져갈 수
|
||||
있습니다. 같은 노트를 보드/표 두 가지 뷰로, 텍스트를 직접 만지고 싶을 때는 raw 에디터
|
||||
버튼도 있습니다.
|
||||
- **CJK 우선, 구체적으로**: IME 조합 중에는 재하이라이트를 멈춰 한국어 입력이 끊기지
|
||||
않습니다. `#태그`도 CJK 지원, 정렬은 로케일 인식 비교를 사용합니다.
|
||||
- **여러분의 언어는 예외가 아닙니다**: 모든 키보드 입력(에디터, 카드 전송, 그리고 보드의
|
||||
이동 키)이 IME 조합 중에는 대기하므로, 한국어・중국어・일본어의 변환 확정이 단축키로
|
||||
잘못 인식되지 않습니다. 긴 카드 접기는 글자 너비를 고려하고, `#태그`도 CJK 지원. 여섯
|
||||
가지 CJK 보증과 각각의 검증 방법은 [CJK.md](./CJK.md)에 있습니다.
|
||||
- **네트워크 없음, 텔레메트리 없음**: 보드는 vault 밖으로 나가지 않습니다.
|
||||
|
||||
### 설치
|
||||
|
|
@ -182,12 +199,16 @@ tugtile 的前身正是幫 obsidian-kanban 寫的一堆 plugin(plugin 的 plug
|
|||
|
||||
- **絕不掉牌**:內建復原/重做、首次寫入前先備份、外部改檔時自動重載而非覆寫
|
||||
(由資料安全測試套件守護)。
|
||||
- **雙手永遠在鍵盤上**:方向鍵把一張牌拖到同列或隔壁列,`Space`/`A`/`D` 勾選/封存/
|
||||
刪牌,`Tab` 到牌列標題再用方向鍵搬整條牌列;新增/改名牌列是可自綁熱鍵的指令。整桌牌
|
||||
理完,不用碰滑鼠。
|
||||
- **在手機上活得下來的編輯器**:雙排工具列、iOS/iPad 虛擬鍵盤自動避讓;牌列與牌都能
|
||||
拖曳排序,加上日期/時間、標籤、收牌(牌庫)。
|
||||
- **資料永遠是 Markdown**:人類可讀、好 diff、帶得走。同一份筆記有牌桌/牌表兩種檢視,
|
||||
想直接碰文字時還有 raw 編輯器按鈕。
|
||||
- **CJK 優先,而且講得出細節**:IME 組字期間暫停重新上色,中文輸入絕不被打斷;`#標籤`
|
||||
支援 CJK;排序使用 locale 感知比較。
|
||||
- **你的語言,不是 edge case**:每一個鍵盤入口(編輯器、送出、連同牌桌的搬牌鍵)都在
|
||||
IME 組字期間讓位,中日韓的選字確認絕不被誤判成快捷鍵;長牌收合是寬度感知的;`#標籤`
|
||||
支援 CJK。六項 CJK 保證與各自的驗證方式都寫在 [CJK.md](./CJK.md)。
|
||||
- **不連網、零遙測**:你的牌桌永遠不會離開 vault。
|
||||
|
||||
### 安裝
|
||||
|
|
|
|||
281
main.js
281
main.js
File diff suppressed because one or more lines are too long
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"id": "tugtile",
|
||||
"name": "tugtile",
|
||||
"version": "0.1.8",
|
||||
"version": "0.1.10",
|
||||
"minAppVersion": "1.4.0",
|
||||
"description": "A card table for your Markdown notes — tug tiles to reorder. Reads your existing kanban boards. CJK-friendly.",
|
||||
"author": "CVER Inc.",
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
{
|
||||
"0.1.8": "1.4.0"
|
||||
"0.1.10": "1.4.0"
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue