Skip to content

About

Novel reader plugin for hoardodile.

Topics

Resources

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

@hoardodile/plugin-novel

Novel reader content plugin: column-paginated text with chapter navigation, per-paragraph comments and typography settings. Reads txt (auto-detected UTF-8 / GB18030 / UTF-16, plus explicit Shift_JIS / Big5), markdown, html/xhtml, epub, docx and fb2 — including chapter folders (every file a chapter) and .fb2.zip containers.

Architecture

The plugin is split into a pure core layer (src/core/) and a thin render layer (src/render/):

  • charset.ts — byte → text decoding (BOM, strict UTF-8, GB18030, explicit encodings)
  • markup.ts — tolerant XML/HTML tokenizer (namespaces, entities, raw-text elements)
  • text.ts — html/fb2/docx → prose units, plus metadata extraction
  • document.ts — units → NovelDocument (paragraphs + chapter table)
  • epub.ts — container.xml / OPF spine parsing
  • format.ts — source-shape classification (single file vs folder)

The server hooks (main.ts) classify the resource and list the text units — archives surface as virtual container entries (book.epub!OEBPS/text/ch1.xhtml) the client reads through container addressing, so no zip parsing ever runs in the iframe. All decoding is pure and Node-testable without a DOM; the render layer only reads bytes and calls logic functions.

Dev loop

This plugin is developed entirely through the plugin toolchain — no hoardodile server, no web app:

pnpm build          # bundle manifest + client + server hooks into dist/
pnpm dev            # watch-build and serve the workbench (http://127.0.0.1:5199)
pnpm detect:smoke   # run detect against testdata/text through the real sandbox
pnpm test           # vitest (logic + render)
pnpm testdata       # regenerate the committed fixture volumes
pnpm testdata:real  # fetch real books, then verify hooks + logic against them

pnpm dev captures the server-side hook results from the real worker sandbox and feeds them to the workbench, so the iframe receives the same context the app would push. Point --data at another testdata/ volume (or run hoardodile plugin run listFiles testdata/epub --plugin-dir dist) to work on a specific format.

testdata/

Committed fixture volumes, generated rather than copied from anywhere; pnpm testdata regenerates them. Every volume is a flat directory — a resource holds exactly one book, so each format gets its own:

  • text/ — the synthetic folio (.txt with regex-detectable chapters
    • a .md sidecar)
  • folder/ — a multi-file chapter folder (卷一.txt … plus html)
  • epub/ — a two-chapter EPUB with real container/OPF/spine layout
  • docx/ — a Word document with core properties
  • fb2/ — nested sections + title-info metadata
  • html/ — a single chapter page
  • gbk/ — the folio re-encoded as GB18030

Real-world samples live in testdata-real/ (git-ignored; not required for CI — the committed fixtures cover the formats offline). pnpm testdata:real downloads public-domain books (Gutenberg, the Internet Archive, Aozora Bunko), re-encodes the Chinese classic to GB18030 and UTF-16LE, and verifies every sample through the built plugin sandbox and the pure logic layer — format correctness is proven from bytes alone, no screen required.

SDK packages

@hoardodile/* dependencies resolve from the npm registry (^0.1.9): pnpm install pulls the published SDK directly — no tarballs, no pnpm-workspace.yaml overrides.

Licensing

  • The plugin is MIT licensed (see LICENSE).
  • Committed fixtures under testdata/ are synthetic (regenerated by pnpm testdata). Real-world samples fetched by pnpm testdata:real are public-domain works (Project Gutenberg, the Internet Archive, Aozora Bunko) and live only in the gitignored testdata-real/ directory.

Requirements

  • hoardodile ≥ 0.1.9 — the built-in plugin marketplace (Settings → Marketplace) and the batched asset-download API (download([…])) when the manifest declares "download": true. The marketplace's Readme and Release notes tabs and release readme assets are read by newer builds; older builds still list and install the plugin normally.
  • "minAppVersion" in manifest.json declares the lowest hoardodile release this plugin runs on. Hosts below it refuse to install or update the plugin (the marketplace gates the install/update entries and zip uploads are blocked with an explanation), so bump it only when the plugin really needs a newer app. Omit it for plugins that support every release.
  • This plugin's version is independent of the hoardodile release version; bump it on user-visible changes.
  • Dev loop: Node ≥ 24, pnpm 11.

Deploying

Publish to the marketplace with one command:

# 1. Add the repository address to your registry repo's registry.json:
#    { "version": 1, "plugins": ["https://github.com/<owner>/<repo>"] }

# 2. One-click release — release-it bumps the version in package.json AND
#    manifest.json, writes CHANGELOG.md from Conventional Commits, commits,
#    tags `v<version>` and pushes. The tag-triggered `.github/workflows/
#    release.yml` then builds, packages (`release/<id>-<version>.zip` +
#    `.sha256`) and publishes the GitHub release. No local `gh` CLI or token.
pnpm release <version>

On main, with a clean working tree. The tag must match the manifest version (v<manifest.version>) — the workflow fails otherwise. Then paste the registry repo address once in Settings → Marketplace. The app reads the registry, each plugin's manifest and its latest release — names, versions, permissions and release notes come straight from GitHub, so the list never needs editing again. The zip asset is <id>-<version>.zip (produced by hoardodile plugin package); before the first release the plugin shows up with a "no release" state.

Prefer pushing a tag manually (no release-it)? It still works — the workflow does the same build/package/publish:

git tag v<version> && git push origin v<version>

Local installs (zip upload in Settings → Plugins) still work for private packages.

Publishing a readme

The marketplace detail view shows a per-release Readme tab. Ship the readme markdown in the readme/ folder as a bare README.md fallback, plus one README.<locale>.md file per extra language (e.g. readme/README.md, readme/README.zh.md) — release.yml uploads the whole folder alongside the zip, so each release carries its own readme and every version shows independent notes. README.md is the fallback the app shows for any language without a specific file, so English normally lives there and you do not need a README.en.md. Use the app's supported language codes for the extra files (zh, ja, de, es) — a region-coded name like README.zh-CN.md only matches a UI language resolved to that exact code, so README.zh.md is what Chinese users see.

pnpm readme:check (run by release.yml before publishing) gates this: it fails the release if readme/ is absent-and-required, is not flat, ships no README.md / README.<locale>.md, or references an image by a nested/missing path.

The app resolves the readme for the user's UI language (exact locale → base language → the README.md fallback); the release body always shows in the Release notes tab.

About

Novel reader plugin for hoardodile.

Topics

Resources

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages