ReadAware

Build a plugin

Start from the public TypeScript template, declare the smallest capability set, and exercise the built package in the ReadAware desktop app. The host owns lifecycle, permissions, presentation, and rollback; your plugin owns its behavior and private data.

Prerequisites

Create the package

  1. Copy template/ to plugins/<your-plugin-id>/.
  2. Keep the folder name, manifest id, and runtime namespace identical.
  3. Edit manifest.json and src/main.ts.
  4. Delete template contributions you do not use and remove their permissions.
  5. Build the self-contained main.js that ReadAware loads.
bash
bun run build
bun run typecheck
bun test
bun run validate

Design the manifest before the implementation

Review the manifest in this order:

  1. Identity — stable ID, name, package version, author, and minimum app version.
  2. Data — positive integer schemaVersion and migration path.
  3. Compatibility — a semver range in requires for every API and schema used.
  4. Authority — semantic permissions and exact settingsAccess grants.
  5. Declarations — settings, schedules, themes, fonts, and entry module.

Use the capability browser and permission preview before installing. Requirements are compatibility claims, not user authority; permission-free capabilities still belong in requires when your plugin depends on their contract.

Choose the right capability

  1. Use a Domain for state or behavior ReadAware owns.
  2. Use a Contribution to supply a choice, action, or provider.
  3. Use a Service for a bounded host operation.
  4. Use plugin storage only for plugin-owned data.
  5. Request a new typed host capability when no existing shape fits.

Do not mirror books, progress, annotations, Settings, or memory into plugin storage. Shadow state bypasses product invariants, committed events, projection rebuilds, sync semantics, and agent context.

Keep activation declarative

During activate(ctx), inspect the environment and register actions, commands, providers, subscriptions, and schedules. Do not perform business writes or external work. The host stages every registration until activation RPCs finish and the Worker answers a health ping.

Start runtime work from a registered handler after promotion. If a handler returns a promise, let the host present loading and failure states. Keep references to external resources only when your optional deactivate() must close them; host registrations and subscriptions are disposed automatically.

Version private data explicitly

schemaVersion versions plugin KV and document collections; it is independent of the package version. Change it only when the private data shape changes. Export migrate(storageCtx, change) for every supported upgrade and downgrade after a schema has been committed.

  • Migrations receive storage only: no domains, Settings, secrets, network, UI, LLM, or contributions.
  • Make each transition deterministic and idempotent.
  • Test a failure after partial writes; the host must restore KV, documents, files, and schema metadata exactly.
  • Do not use a package-version check as a substitute for the data schema.

Install the working folder

  1. Run the build and checks.
  2. Open ReadAware → Settings → Plugins → Install plugin.
  3. Select the built plugin folder and inspect the consent summary.
  4. Exercise the real feature in the desktop app.
  5. Rebuild and reinstall to test an update.

A plain browser cannot verify plugin installation, Worker IPC, SQLite persistence, raw book access, reader integration, or rollback. Test the shipping Tauri app.

Test the lifecycle, not only the happy path

  • Fresh install, enable, disable, and re-enable without restarting.
  • Successful update and downgrade across real data.
  • Activation timeout, handler rejection, migration failure, and exact rollback.
  • Uninstall cleanup: no surviving action, listener, schedule, provider, or Worker.
  • Permission removal and permission expansion during an update.
  • Long labels, empty states, keyboard navigation, and all host themes.

Know the current limits

Schedules run while ReadAware is open, at least at their declared cadence, with launch catch-up when overdue. They are not durable jobs: there is no execution while the app is closed, persisted queue, retry/backoff contract, or crash-resume guarantee.

UI is available only at existing typed contribution points. A missing placement requires a host-owned contribution and consumer; arbitrary HTML or a generic native invoke API will not be added as a shortcut.

Next

Keep the API reference beside your editor, then read Publishing before preparing a registry pull request.