Skip to content

Repository files navigation

Multi-file Editor + Typechecker Server

Local Python web server with a tabbed multi-file editor. Any file change triggers:

  1. Send all files to /api/analyze
  2. Rewrite all files into the persistent local staging directory shown in the UI
  3. Copy those files into a fresh disposable analysis directory and run uv sync
  4. Run the type checkers in that analysis directory:
    • python -m ty check .
    • optional: cargo run --bin ty -- check --project <temp-project-path> in your local Ruff clone
    • optional: uv run --frozen --with-editable 'mypy @ <local-mypy-checkout>' mypy --color-output --pretty .
    • optional: uv run --frozen --with-editable 'pycroscope @ <local-pycroscope-checkout>' pycroscope --output-format concise .
    • python -m pyright --outputjson . (then normalized to relative paths in UI)
    • python -m pyrefly check .
    • python -m mypy --color-output --pretty .
    • zuban check --pretty .
    • python -m pycroscope --output-format concise .
  5. Display output from each tool in the UI

The runtime pane is always present and is turned off and collapsed by default. Like the type-checker panes, its On/Off toggle controls whether it runs independently of whether its output is collapsed. When enabled, it runs main.py (or the first Python file when there is no main.py) with the selected Python version and the project's installed dependencies. The runtime interpreter installs typing.reveal_type as the built-in reveal_type when available, and provides the same built-in behavior without modifying typing on Python 3.10.

Run

uv run multiplay

Then open http://localhost:8000.

Or, to start the server and automatically open it in your default browser:

./run.sh

Docker

docker build -t multiplay .
docker run --rm -p 8000:8000 multiplay

Then open http://localhost:8000.

Notes

Pycroscope imports the code, and enabling the runtime pane executes it. Don't enter code in the playground that you don't want run on your system.

Each type checker run has a 10-second timeout during analyze; if exceeded, that tool returns a timeout error while others still complete.

Each checker card includes:

  • an On/Off toggle (disabled tools are skipped by the backend)
  • a Configure button to open/create the tool's config section in pyproject.toml
  • a Docs button linking to the tool's configuration documentation
  • a collapse/expand arrow to hide/show that checker's output panel
  • a drag grip for reordering panels

Optional local Ruff ty checker:

  • provide a local Ruff clone path in the "Local checker directories (optional)" section
  • this adds a ty (/path/to/checkout) checker card for side-by-side comparison
  • backend runs it from that repo with cargo run --bin ty -- check --project <temp-project-path>
  • this local-checkout run is not capped by the 10-second analyze timeout

Optional local Python checker checkouts:

  • open the "Local checker directories (optional)" section in the header
  • provide local checkout paths for mypy and/or pycroscope
  • backend runs those tools via uv run --frozen --with-editable '<tool> @ <checkout>' <tool> ... instead of the installed PyPI package
  • checker card titles include the local checkout path so you can confirm which build is active

On startup, the server primes tool installs and detects checker versions. For each analysis, it copies the submitted files from the persistent staging directory into a fresh disposable directory, runs uv sync, and executes the checkers there. The UI shows each detected version in the checker pane header.

Sharing

Click the Share button in the header to create a public GitHub Gist containing your current files and dependencies. The gist ID is copied to your clipboard.

To load a shared gist, paste the gist ID (or full URL) into the Gist ID or URL input in the header and click Load. This replaces the current files and dependencies with the contents of the gist. Before analyzing the loaded files, multiplay turns off and collapses both the runtime and pycroscope panes; re-enable them only after reviewing the gist contents.

Sharing uses the GitHub Gist API directly. It uses MULTIPLAY_GH_TOKEN if set (for example: export MULTIPLAY_GH_TOKEN=...), otherwise it falls back to gh auth token when the GitHub CLI is installed/authenticated. Loading public gists only requires network access.

Dependencies

The header has a dependency field (comma/newline separated). When non-empty, dependencies are added to pyproject.toml and installed into that analysis's venv with uv sync before the type checker tools run. All tools in one analysis use the same snapshot venv, so they can resolve imports from installed packages directly.

If dependency install fails, the UI shows a dedicated error panel with command, exit code, requested dependencies, and full installer output.

Dependency installs use a 2-day package cooldown, so newly uploaded package versions are ignored until they have been available for at least two days. The advanced options include a package exemption list for dependencies that should skip the cooldown; it defaults to ty.

Configuration

Because we are simply running the type checker CLIs, you can add ty.toml, mypy.ini, pyrightconfig.json etc files and they "just work". Output panes preserve ANSI terminal color codes when the tool emits them.

Project layout

  • Backend server/API: app.py
  • Frontend markup: static/index.html
  • Frontend styles: static/styles.css
  • Frontend behavior/highlighting: static/app.js

Requirements

  • Python 3.14+
  • uv available on PATH
  • GitHub auth for sharing gists: either MULTIPLAY_GH_TOKEN or authenticated gh CLI
  • Network access on first run so uv can fetch tool packages if not already cached

About

multi-typechecker playground

Resources

Code of conduct

Security policy

Stars

20 stars

Watchers

2 watching

Forks

Releases

Packages

Contributors

Languages