Development

Setup and checks

Clone the repository and create .venv as in Install, then:

python -m pip install -e '.[dev,web]'
npm ci

Node.js 22+ is used only for checks of the browser code. Tests use unittest; the web extra enables the HTTP tests, and rendering tests need an installed sans-serif font or JAMSHOOT_FONT.

Every pull request must pass:

python -m unittest discover -s tests -v
python -m ruff check .
python -m ruff format --check .
python -m black --check .
python -m mypy
python -m pyright
python -m pylint src tests
npm run check

Ruff and Black share one style; both run so either formatter keeps it. npm run check runs Biome (lint and format), the Node unit tests and Fallow (dead code and complexity); npm run format applies safe fixes. The Node tests cover editor state, history, shortcuts, color math and the API client without a browser.

Packaging changes also need:

python -m build --outdir ./dist-check
python tests/check_distribution.py ./dist-check

Use an empty dist-check: the check expects one wheel and one sdist. CI installs the built wheel and runs the tests on macOS, Linux and Windows with Python 3.11 and 3.14. Before a release, check create, edit, save, export, ZIP transfer, Undo/Redo and error recovery by hand at desktop and mobile widths, in the light and dark themes.

Code organization

src/jamshoot/
  core/       project rules: schema, styles, planning, starter projects
  render/     Pillow renderer: background, text, device mockups, export
  server/     Studio's local HTTP server
  web/        Studio's browser code: pages/, features/, shared/
  data/       schema, catalog and settings shipped with the package
  cli.py      the jamshoot command
  *.py        catalog, project library, settings and font lookup
tests/        Python and Node tests
docs/         this documentation (MkDocs)
studio        the ./studio launcher

The CLI and the server share project rules and the renderer; core and render never import the server or browser code. Runtime files load through importlib.resources, so an installed wheel behaves like the checkout.

Browser code is native ES modules without a build step. Each page keeps its HTML, JavaScript and CSS together; features are grouped by what they do; each reusable UI component has its own folder. Pages use features, features use shared code, and shared code depends on neither. Import from the owning module, load component styles before feature and page overrides, and keep CSS layers. HTML is served through page routes and other files through an allowlist.

Tests must run against an installed wheel. Architecture and design tests check module boundaries, CSS tokens, contrast and lint suppressions.

Documentation

docs/ is rendered by MkDocs with mkdocs.yml. Preview with live reload at http://127.0.0.1:8000:

python -m mkdocs serve

The Publish documentation workflow runs python -m mkdocs build --strict, which fails on broken links, and deploys site/ to GitHub Pages (Pages source: GitHub Actions).

Releases

The version is in src/jamshoot/__init__.py; archive contents are listed in pyproject.toml. Record user-facing changes in CHANGELOG.md under Unreleased and move them under ## [VERSION] - YYYY-MM-DD for a release.

GitHub workflows run only when started by hand: CI, Publish documentation and Create tag and release. The release workflow checks that the version and dated changelog entry match, runs every check, then creates the vVERSION tag and a GitHub Release with the wheel and sdist. It refuses an existing tag and does not publish to PyPI. If the release fails after the tag was pushed, delete the tag with git push origin :refs/tags/vVERSION before retrying.