Zheng Tally

approvedunreviewed

by yunmin

Count with traditional five-stroke Zheng tallies inline in Obsidian and resume them later.

★ 1 stars↓ 340 downloadsUpdated 4d agoMIT

Zheng Tally

A native-feeling 正 tally counter for Obsidian. Count stroke by stroke, keep it inline, and resume anytime.

Release CI Obsidian desktop License: MIT

Zheng Tally demo

Press Alt+Z, tap Space to count, Enter to commit. The tally renders as a real inline 正 built from canonical stroke vectors, and it stays that way: committed tallies remain vector chips you can click to resume.

Why Zheng Tally

Counting with 正 tallies in Markdown usually means typing characters by hand and losing count halfway. Zheng Tally turns it into a tight loop: start anywhere in a note, watch each stroke appear, commit a portable plain-text tally, and pick it back up later without retyping. The editor shows vectors; the file stays readable text.

How it works

  1. Alt+Z drops an inline counter at the cursor.
  2. Each Space adds one canonical stroke (一 → … → 正), grouped in fives.
  3. Enter writes 正正正·3<!--zt:18--> — visible text plus a count marker.
  4. The note keeps showing a vector chip, derived from that text on every load.
  5. Click the chip (or caret + Alt+Z) to resume from 18. Esc never writes.

Features

  • Canonical 5-stroke 正 vectors at every step — never a substituted character
  • True inline widget with font-matched size and baseline
  • Persistent, resumable tally objects with restart-safe recovery
  • Readable storage: notes stay meaningful with the plugin off
  • Compact large-count preview that never drops the in-progress group
  • Conservative hover/count badge for legacy tallies, zero layout shift

Installation

From a release:

  1. Download main.js and manifest.json from Releases.
  2. Place both in <vault>/.obsidian/plugins/zheng-tally/.
  3. Enable the plugin under Settings → Community plugins.

From source:

git clone https://github.com/yunmin311/zheng-tally-obsidian.git
cd zheng-tally-obsidian
npm install
npm run build

Copy dist/main.js, manifest.json, and styles.css into your vault's plugin folder. (styles.css carries the chip and hover-badge styling.)

Usage

  1. Open a Markdown note (Obsidian desktop).
  2. Alt+Z to start tally mode at the cursor.
  3. Space / + to count up, Backspace / - to count down.
  4. Enter to commit a persistent vector chip.
  5. Click the chip (or caret + Alt+Z) to resume; Esc cancels cleanly.

One session at a time; switching leaves ends it without writing.

Keyboard shortcuts

KeyAction
Alt+ZStart, or resume the tally under the caret
Space / + / NumpadAdd / Shift+=+1
Backspace / - / _ / NumpadSubtract−1 (floors at 0)
EnterCommit (single edit)
EscCancel, zero document changes

Ctrl/Alt/Meta chords pass through untouched.

Persistent & resumable tallies

Committed tallies keep rendering as vector chips — same strokes, same metrics, quieter chrome — across note reopens, plugin reloads, and restarts, derived purely from the note text. This holds in reading view too (src/reading-tally.ts): a registerMarkdownPostProcessor pass renders the chip there, because the editor's CodeMirror decorations never reach Obsidian's MarkdownRenderer. Reading-view chips are read-only; counting stays an editor action.

Reading rendering verifies ownership against the current Markdown section, including tallies in the middle of a paragraph. Ordinary unmarked 正 text and code are left untouched. If another renderer changes the section so its source cannot be safely matched, the tally remains readable plain text. Reading rendering never rewrites the note or your settings.

阅读模式按当前区块的源码核对计数归属,也支持段落中间的计数。 没有标记的普通「正」字和代码不受影响。若其他渲染器改变了内容、无法安全核对, 则保留可读的原文字,不猜测计数归属,也不改写笔记或设置。

  • Resume from the stored total (18 → 19), never from zero.
  • Resume + Enter rewrites text + marker in one edit (正正正正·3<!--zt:23-->).
  • Resume + Esc changes nothing; the original chip returns.
  • Resume to 0 + Enter removes the tally and its marker.
  • Ambiguous caret between two adjacent tallies resumes neither.
  • The persisted total stays hidden until hover or caret reaches the chip, reserving its space so nothing shifts.

Persisted tally

Resumed tally

Storage format

CountStored
1·1<!--zt:1-->
5正<!--zt:5-->
18正正正·3<!--zt:18-->

The <!--zt:N--> comment is the integer source of truth and ownership marker. A marker counts only when it re-serializes to exactly the visible text; mismatches fail closed. Unmarked legacy tallies (正正正·3) still get a conservative hover badge and can be resumed once, upgrading on next Enter. A lone 正 never auto-resumes.

Large-count behavior

Up to four group slots render in full; beyond that the preview compacts while always keeping the most-recent finished group and the live partial slot:

18 -> 正 正 正 [state3]
20 -> 正 正 正 正
21 -> 正 正 … 正 [state1]
83 -> 正 正 … 正 [state3]

The right-hand total is always exact. Active and persisted chips share one grouping algorithm, so committing never jumps visually.

Progressive strokes

Large count

Architecture

  • src/zheng-strokes.ts — vendored canonical 5-stroke vectors (Arphic data)
  • src/renderer.ts — native-measured glyph cells + tally chips
  • src/cm6-widget.ts — live inline Decoration.widget
  • src/persistent-tally.ts — marker replace decorations + resume lookup
  • src/editor-session.ts — keyboard capture, lifecycle, single-edit commits
  • src/tally-hover.ts — legacy parser + transient count badge
  • src/tally-state.ts — counts plus stable/marked serialization
  • src/editor-host.ts — the single CodeMirror access point

Compatibility / limitations

  • Obsidian desktop only; no mobile support.
  • One active session at a time; no history or statistics.
  • Detection stays conservative by design: in-word matches and lone 正 never badge or resume.

Development

npm install       # install dependencies
npm run dev       # watch mode build
npm run build     # production build (dist/main.js + dist/manifest.json)
npm test          # 143 passing
npm run lint      # eslint
npm run typecheck # tsc --noEmit

License & data attribution

  • Plugin source code: MIT — see LICENSE.
  • Canonical 正 stroke vectors: Arphic Public License, not MIT — via Hanzi Writer Data / Make Me a Hanzi, extracted from Arphic Technology fonts. Full text: vendor/ARPHICPL.txt, notes: vendor/README.md.

For plugin developers

Search results and similarity scores are powered by semantic analysis of your plugin's README. If your plugin isn't appearing for searches you'd expect, try updating your README to clearly describe your plugin's purpose, features, and use cases.