WeChat Reading client: shelf, notebook, highlights, store search and statistics
app
  • Emacs Lisp 68.2%
  • JavaScript 18.8%
  • Python 12.6%
  • Makefile 0.4%
Find a file
Kinneyzhang 020f739b74
Some checks failed
Repository structure / structure (push) Has been cancelled
chore(release): version 0.2.5
Ship the current sources as an immutable archive version.

Validation: `make -C etaf-weread check` before this header-only change; the
archive install gate re-installs every target afterwards.
2026-10-24 22:59:57 +08:00
.githooks feat: add the WeChat Reading client 2026-10-01 20:54:14 +08:00
.github/workflows chore(scripts): sync shared workspace tooling 2026-10-19 12:49:34 +08:00
backends/weread docs(weread): point the Chrome-cookie failure at the built-in login 2026-10-20 20:35:20 +08:00
docs feat(reader): offer the standard 亮/随/暗 switch to its panel 2026-10-24 20:34:18 +08:00
examples refactor(weread): ship the backend inside the package 2026-10-17 21:24:11 +08:00
lisp feat(reader): offer the standard 亮/随/暗 switch to its panel 2026-10-24 20:34:18 +08:00
scripts chore(scripts): sync shared workspace tooling 2026-10-22 13:16:12 +08:00
tests feat(reader): offer the standard 亮/随/暗 switch to its panel 2026-10-24 20:34:18 +08:00
.editorconfig feat: add the WeChat Reading client 2026-10-01 20:54:14 +08:00
.gitignore feat: add the WeChat Reading client 2026-10-01 20:54:14 +08:00
CHANGELOG.md feat(reader): offer the standard 亮/随/暗 switch to its panel 2026-10-24 20:34:18 +08:00
etaf-weread.el chore(release): version 0.2.5 2026-10-24 22:59:57 +08:00
LICENSE chore: add the GPL-3.0 license 2026-10-13 00:49:01 +08:00
Makefile feat(weread)!: remove the local-book reading mode 2026-10-20 18:13:50 +08:00
README.md feat(reader): offer the standard 亮/随/暗 switch to its panel 2026-10-24 20:34:18 +08:00

ETAF Weread

ETAF Weread is a WeChat Reading (微信读书) client for Emacs. It renders the signed-in account's shelf, notebook and reading statistics as one ETAF view: a section sidebar, a paginated book list, a side pane with the book card, table of contents and highlights, and a local SQLite index of every highlight through ETAF DB.

The reader at a wide frame:

The reader at a wide frame

At a medium frame the sidebar becomes an icon rail and the side pane turns into tabs:

The reader at a medium frame

At a narrow frame the sections become a chip strip and the panes stack under tabs:

The reader at a narrow frame

Capabilities

  • Shelf: every book with progress, reading time and finished state, filtered by all, reading and finished.
  • Notebook: the books that have highlights or thoughts, with per-book counts.
  • Book detail: cover initial, author, category, rating, word count, progress meter, reading time, note counts, introduction-free card and the full table of contents.
  • Highlights: quotes grouped by chapter with the attached thought below each quote, orphan thoughts collected separately.
  • Store search: find books in the WeChat Reading store and open their detail.
  • Reading statistics: finished and reading counts, total reading time, notes and the most-read books.
  • Local highlight index: reading a book's highlights stores them in a SQLite database, and the index section searches the whole notebook offline; I walks and indexes the notebook in the background.
  • Export: the selected book's highlights and thoughts are written as Markdown by the bundled weread backend.
  • Open in the browser: the selected book opens in the WeChat Reading web reader.
  • Three palettes (dark, light and sepia) on one key, published as :ui-* theme tokens so shared ETAF UI controls follow the same colours; inside a floating panel the title bar offers the standard 亮/随/暗 switch, and 随 follows the panel's palette.
  • Window layouts: three columns on a wide frame, a glyph-only section rail with one pane on a medium one, and a phone-like stacked layout on a narrow one.
  • Headless sessions for scripts and tests: no buffer, no timer and a scripted client are all supported.

Prerequisites

Requirement Why Check
Emacs 29.1 or newer the ETAF runtime and the built-in SQLite support emacs --version
Node.js 22 or newer runs the backend client node --version
etaf, etaf-ui, etaf-db, ebox, ecss the framework; each root entry must be on load-path every checkout is readable
Bundled weread backend shelf, notes, login ships inside the package; only Node.js is required
Chrome, Edge, Brave or Chromium opens the QR login page and reads its cookies; every read-only command works without it WEREAD_BROWSER=/path/to/browser overrides the binary

The reader displays what the backend provides: the bundled weread backend owns the account, the cookie and the highlight index, and the app keeps a local SQLite mirror of every highlight it reads.

Installation

1. System tools

brew install node                 # Node.js 22 or newer

Emacs 29.1 or newer is required; its built-in SQLite support stores the local highlight index (no external database is needed).

2. The bundled backend and its one-time login

The account, cookie and highlight backend ships inside this package under backends/weread/; nothing else has to be installed or cloned. Sign in once, from Emacs:

(require 'etaf-weread)
M-x etaf-weread-login

The command reuses its own signed-in browser, otherwise reads your local Chrome session, and otherwise opens a browser window at the WeChat Reading login page and waits for the QR scan. WeChat Reading only offers QR login, so this step needs you once; etaf-weread-login-timeout (default 300 seconds) is how long it waits. The browser is a separate profile that ships with the package — any of Chrome, Edge, Brave or Chromium works, and WEREAD_BROWSER points at another binary — so no Pi skill or external tool is involved. The cookie is stored in the backend's data directory, and later reads go straight over HTTP.

M-x etaf-weread-logout deletes that stored login and clears the account data from the open reader; the WeChat Reading account itself is unchanged. The backend script remains available for a manual cookie without Emacs:

BACKEND="$(dirname "$(locate-library etaf-weread)")/backends/weread"
node "$BACKEND/weread.mjs" status                                 # reports a missing login
node "$BACKEND/weread.mjs" login --from-chrome                    # macOS: reads the Chrome session
# or paste the two cookie values from a signed-in browser:
node "$BACKEND/weread.mjs" login --cookie "wr_vid=…; wr_skey=…"
node "$BACKEND/weread.mjs" logout                                 # forget the stored login

M-x etaf-weread-open reports a missing or expired login in its status line and names M-x etaf-weread-login; g retries once you have signed in.

3. The Emacs package

(add-to-list 'load-path "/path/to/etaf")
(add-to-list 'load-path "/path/to/etaf-ui")
(add-to-list 'load-path "/path/to/etaf-db")
(add-to-list 'load-path "/path/to/ebox")
(add-to-list 'load-path "/path/to/ecss")
(add-to-list 'load-path "/path/to/etaf-weread")
(require 'etaf-weread)

(global-set-key (kbd "C-c r") #'etaf-weread-open)

M-x etaf-weread-open mounts the reader in the selected window; M-x etaf-weread-close closes it. etaf-weread-index-file selects the local SQLite file and etaf-weread-theme-mode the palette.

Quick start for an agent

A non-interactive path to a working reader (the QR scan needs the user once):

command -v node || brew install node
BACKEND="$(dirname "$(locate-library etaf-weread)")/backends/weread"
test -f "$BACKEND/weread.mjs" || {
  echo "weread backend not found: reinstall etaf-weread" >&2; exit 1; }
node "$BACKEND/weread.mjs" status || node "$BACKEND/weread.mjs" login --from-chrome
;; After adding the repositories to `load-path':
(require 'etaf-weread)
(etaf-weread-open)               ; real backend; returns the reader buffer

A missing client or an expired cookie leaves the reader mounted with the backend error in its message line; sign in again through the bundled backend (see "The bundled backend and its one-time login") and press g to retry.

Minimal example

(require 'etaf-weread)

(let* ((reader (etaf-weread-create))        ; headless session
       (client (etaf-weread-make-client)))
  (plist-put reader :client client)
  (etaf-weread-refresh reader)              ; shelf, account and stats
  (while (plist-get (etaf-weread-state reader) :loading)
    (sit-for 0.05))
  (etaf-weread-move-selection reader 3)
  (etaf-weread-activate-selection reader)   ; the fourth book's detail
  (plist-get (etaf-weread-state reader) :detail))

Runnable demo

M-x etaf-weread-demo-open mounts the complete reader on a scripted account, so it needs no network, login or external backend. M-x etaf-weread-demo-live opens the same reader against the installed backend. From a checkout with sibling providers:

cd etaf-weread
emacs -Q -L ../ecss -L ../ebox -L ../etaf -L ../etaf-ui \
  -L ../etaf-db -L . -l examples/etaf-weread-demo.el -f etaf-weread-demo-open

Documentation

  • The manual: keys, sections, the local index, theming, scripting, custom backends and troubleshooting.
  • Architecture: module responsibilities, data flow and invariants.
  • The root entry Commentary: the authoritative list of supported functions, variables, the app Component and the error type.

Checks

make check runs the structure checker, byte compilation with warnings as errors, the acceptance suite and the API boundary checker. The acceptance scenarios drive a scripted backend and a temporary SQLite database, so they need no network, login or frame:

make compile   # byte compilation, warnings are errors
make test      # the public acceptance scenarios
make check     # structure + compile + acceptance + API boundary