Skip to main content

Disposable Anki profile, add-on install, Docker/Xvfb, and GUI workbench tooling for add-on and deck development.

Project description

๐Ÿ”ฌ anki-addon-workbench

Headless, disposable-profile testing & cross-platform GUI tooling for Anki add-on and deck development.

PyPI Python versions CI License: MIT Platforms

Disposable Anki profile, add-on install, Docker/Xvfb, smoke-test, and GUI workbench tooling for Anki add-on and deck development.

It helps an agent or developer launch Anki away from a real profile, install the add-on under test, run a probe add-on, capture marked screenshots, and send mouse or keyboard input โ€” locally on macOS/Linux or inside a virtual display.

The first target users are add-on authors who want to make GUI work less manual without risking their daily Anki profile, and deck authors who need to see how cards actually render inside Anki (where JavaScript and styling can behave differently than in a browser) without manually closing and reopening Anki โ€” which triggers a sync round-trip each time.

How it works

        โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”                        โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
        โ”‚    live Anki     โ”‚  move ยท click ยท type   โ”‚    screenshot    โ”‚
        โ”‚    disposable    โ”‚ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ถ โ”‚  cursor-marked   โ”‚
        โ”‚     profile      โ”‚      (pyautogui)       โ”‚   .png + .json   โ”‚
        โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜                        โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                  โ–ฒ                                           โ”‚
                  โ”‚           agent reads & decides           โ”‚
                  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

A disposable Anki the agent drives, screenshots, and adjusts โ€” no profile risk, no sync churn.

Install

From PyPI:

# core smoke/profile/Docker tooling only (dependency-light)
uv add anki-addon-workbench

# include the GUI primitives (pyautogui + Pillow) for screenshot/move/click/type
uv add 'anki-addon-workbench[gui]'

(pip install 'anki-addon-workbench[gui]' works too.)

The GUI extra is optional: the core smoke, launch (without screenshots), profile, and dockerfile tooling has no heavy dependencies. The screenshot/pointer/keyboard commands lazy-load pyautogui + Pillow and fail with a clear install hint if the [gui] extra is missing.

Platform notes. GUI primitives are cross-platform via pyautogui:

  • macOS โ€” works against your local /Applications/Anki.app; grant Terminal (or your runner) Screen Recording and Accessibility permission once.
  • Linux/Xvfb โ€” works headless; pyautogui uses scrot + python-xlib (installed by the bundled Docker image).

Configure An Add-On

Add this to the add-on project's pyproject.toml:

[tool.anki-addon-workbench]
project_name = "My Add-on"
addon_package = "my_addon"
source_root = "."
include = ["__init__.py", "manifest.json", "README.md"]
exclude = ["tests", "user_files", ".git", "__pycache__"]
seed_apkgs = []
probe_addon = "tests/gui_smoke/probe_addon"
anki_version = "25.09"
profile = "User 1"
docker_image = "my-addon-anki-gui"
# Optional. Override when dogfooding an unreleased workbench build.
docker_workbench_spec = "anki-addon-workbench[gui]"

If the project does not have a pyproject.toml, place the same keys in anki-workbench.toml.

For deck-only projects, omit addon_package and point seed_apkgs at one or more generated .apkg files:

[tool.anki-addon-workbench]
project_name = "My Deck"
seed_apkgs = ["out/my-deck.apkg"]
anki_version = "25.09"
docker_image = "my-deck-anki-gui"

The workbench imports those packages into the disposable profile before Anki's GUI starts. If seed_apkgs is configured and no custom probe_addon is set, smoke automatically installs a built-in deck probe that verifies the collection has cards, lists the imported decks and note types, and renders a small card sample. Use a custom probe only when the deck needs project-specific assertions.

CLI

anki-workbench doctor
anki-workbench smoke
anki-workbench launch --xvfb --pointer 500,180 --keep
anki-workbench screenshot --out .tmp/shot.png --meta .tmp/shot.json
anki-workbench move 500 180
anki-workbench click
anki-workbench key Escape
anki-workbench type "search text"
anki-workbench dockerfile --out tests/gui_smoke/Dockerfile
anki-workbench docker-smoke-local --uv-command "sfw uv"
anki-workbench init-probe --out tests/gui_smoke/probe_addon

smoke installs the configured add-on and optional probe add-on into a disposable base folder, seeds the profile so the first-run language dialog does not appear, imports any configured APKGs, launches Anki, waits for the probe to write JSON, prints the JSON, and removes the base folder unless --keep is passed. Deck-only projects with seed_apkgs get the built-in deck smoke probe automatically when no custom probe is configured.

launch starts disposable Anki without the probe add-on so an agent can inspect and interact with the live GUI. It can start Xvfb, wait for the Anki window, move the pointer, and capture a cursor-marked screenshot.

All commands print JSON. The screenshot command draws a high-contrast synthetic marker at the pointer (headless screenshots do not include the real cursor), which is far more reliable than depending on the display server to render it.

type is ASCII-oriented. It uses pyautogui's keystroke synthesis, which reliably types ASCII and common keys but cannot reliably type non-ASCII text (e.g. CJK, accented characters) โ€” those need an input method the synthetic keystrokes bypass. For non-Latin search/input, set the field value through Anki/AnkiConnect rather than type. (This is a regression from the old xdotool type backend, traded for cross-platform support.)

The Probe Contract

smoke works by installing a small probe add-on alongside the add-on under test. When Anki finishes starting, the probe writes a JSON result to the path in the ANKI_ADDON_WORKBENCH_RESULT environment variable and then exits Anki. The runner reads that file, prints it, and uses ok for its exit status.

For deck-only projects, the built-in APKG probe is usually enough. It is enabled automatically when seed_apkgs is non-empty and probe_addon is omitted. Set deck_smoke_render_limit = 10 if you want it to render more than the default five sample cards.

Scaffold one in one command โ€” you rarely need to write a probe by hand:

anki-workbench init-probe --out tests/gui_smoke/probe_addon

This writes a documented, self-contained __init__.py with a run_checks() function to edit. Then point probe_addon at that directory in your config.

The result contract:

Field Type Meaning
ok bool Required. Whether the probe's checks passed.
checks list Optional. [{"name": str, "ok": bool, ...}] per-check detail.
anything else json Optional. Echoed back verbatim in the smoke output.

The runner validates that ok is present and boolean; if a probe forgets it (or never writes / never exits, which times out), the smoke output says exactly what went wrong and points you back to init-probe. A probe may also read ANKI_ADDON_WORKBENCH_SCREENSHOT for a path to capture a screenshot to.

Docker/Xvfb

Render the reusable Anki launcher image template in the add-on project:

anki-workbench dockerfile --out tests/gui_smoke/Dockerfile

Then build it and run it with the add-on project mounted:

docker build -f my-addon/tests/gui_smoke/Dockerfile -t my-addon-anki-gui my-addon
docker run --rm -v "$PWD":/workspace -w /workspace/my-addon my-addon-anki-gui

The image installs the Anki Linux launcher, Xvfb, xdotool, scrot, QtWebEngine runtime libraries, Python, and the configured workbench package spec (anki-addon-workbench[gui] by default). The Anki binary path is provided via the ANKI_BIN environment variable (set in the image), not hardcoded in the library. The workbench itself is installed into the image, so generated Dockerfiles are standalone and do not require a sibling source checkout.

When dogfooding an unreleased workbench build, either set docker_workbench_spec in [tool.anki-addon-workbench] or pass an override:

anki-workbench dockerfile \
  --out tests/gui_smoke/Dockerfile \
  --workbench-spec "anki-addon-workbench[gui] @ https://github.com/elvis-sik/anki-addon-workbench/archive/<commit>.zip"

For workbench maintainers, the higher-level local-wheel path avoids publishing or pushing before testing:

anki-workbench docker-smoke-local --uv-command "sfw uv"

It builds a wheel from --workbench-source (default: .), creates a small Docker build context containing that wheel, renders a Dockerfile that installs the wheel with [gui], builds the configured image with a -local suffix, and runs the configured smoke test with the project mounted at /workspace. Artifacts and command logs are written under .tmp/anki-workbench-local.

Development

make check
make docker-smoke-local

Tests run on pytest (make test) and cover config loading, add-on copy filtering, profile seeding, command construction, Dockerfile rendering, probe scaffolding, and the GUI marker rendering. Docker GUI smoke tests are kept behind explicit CI commands because Anki launcher downloads and QtWebEngine startup are comparatively slow. make docker-smoke-local is the maintainer confidence check for unreleased workbench changes: it installs the local wheel into the generated Docker image instead of pulling the published PyPI package.

Releasing

Releases publish to PyPI via GitHub Actions using Trusted Publishing (OIDC) โ€” no API token is stored. To cut a release:

# 1. bump `version` in pyproject.toml, commit
# 2. tag and push โ€” the tag must match the version
git tag v0.3.1
git push origin v0.3.1

The release.yml workflow checks that the tag matches pyproject.toml, builds the sdist + wheel with uv build, and publishes.

One-time setup on PyPI (Account โ†’ Publishing โ†’ Add a pending publisher):

  • PyPI Project Name: anki-addon-workbench
  • Owner / Repository: your GitHub org/repo
  • Workflow name: release.yml
  • Environment name: pypi

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

anki_addon_workbench-0.4.0.tar.gz (72.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

anki_addon_workbench-0.4.0-py3-none-any.whl (35.1 kB view details)

Uploaded Python 3

File details

Details for the file anki_addon_workbench-0.4.0.tar.gz.

File metadata

  • Download URL: anki_addon_workbench-0.4.0.tar.gz
  • Upload date:
  • Size: 72.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for anki_addon_workbench-0.4.0.tar.gz
Algorithm Hash digest
SHA256 f1e2b7f2beda0fe880eb3575be89cd75f9ccd12c929117d8d5180ff1bd2637bb
MD5 b246548bd1c09241d0571ddd920b1f79
BLAKE2b-256 5f376e2a2beedcde0b25961763be3211d7efa342476fc1368e0c7ba7e48af6ba

See more details on using hashes here.

Provenance

The following attestation bundles were made for anki_addon_workbench-0.4.0.tar.gz:

Publisher: release.yml on elvis-sik/anki-addon-workbench

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file anki_addon_workbench-0.4.0-py3-none-any.whl.

File metadata

File hashes

Hashes for anki_addon_workbench-0.4.0-py3-none-any.whl
Algorithm Hash digest
SHA256 8d86971d2c3181b33ffdd4c7005165d05913a93c6de7681538fad5dff8c6fbb2
MD5 721135f312c96ba851291a0fccf31d41
BLAKE2b-256 15f41fe0f0ffcd243e3265e6070b5d9c72b3f7261861caea22646979c6f0e5bf

See more details on using hashes here.

Provenance

The following attestation bundles were made for anki_addon_workbench-0.4.0-py3-none-any.whl:

Publisher: release.yml on elvis-sik/anki-addon-workbench

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page