Skip to content

Development And Contributing

errorsallthethings edited this page Jun 29, 2026 · 8 revisions

This page is for developers and contributors who want to run Local VTT locally, understand the project structure, test changes, or prepare releases.

For user-facing setup, see Getting Started.

Local-VTT-VS-Code

Project Overview

Local VTT is an Electron desktop app with a React renderer and Canvas 2D scene rendering.

The app is local-first and stores campaign data in portable local folders.

The runtime is split into:

  • Electron main process code.
  • A secure preload bridge.
  • Shared TypeScript models and validation.
  • React GM View.
  • React Player View.
  • Canvas-based scene rendering.

Local Development

Requirements

Development requires:

  • Node.js.
  • npm.
  • Git.

The release workflow currently uses Node 24 in GitHub Actions.

Install Dependencies

From the project root, install dependencies:

npm install

After install, Local VTT runs scripts/ensure-electron.mjs through postinstall to verify or repair the Electron binary. This should create node_modules/electron/path.txt when npm lifecycle scripts are enabled.

If Electron did not install correctly, run:

npm run electron:install

If path.txt is still missing, check whether npm lifecycle scripts are disabled:

npm config get ignore-scripts

npm install may report deprecation warnings from transitive Electron packaging dependencies, such as rimraf, inflight, glob, or boolean. To see why one is installed, run:

npm explain rimraf
npm explain inflight
npm explain glob
npm explain boolean

Run In Development

Start the app in development mode:

npm run dev

This builds the Electron files, starts the Vite dev server, waits for the local dev URL, and launches Electron.

Linux Graphics Options

Linux developers can test Electron's Ozone platform and Vulkan flags when diagnosing Wayland, X11, compositor, or GPU driver behavior:

LOCALVTT_OZONE_PLATFORM=wayland npm run dev
LOCALVTT_OZONE_PLATFORM=x11 npm run dev
LOCALVTT_OZONE_PLATFORM=auto npm run dev
LOCALVTT_ENABLE_VULKAN=1 npm run dev
LOCALVTT_DISABLE_VULKAN=1 npm run dev

These options are opt-in because the best combination depends on the compositor, GPU driver, and Electron/Chromium version. LOCALVTT_DISABLE_VULKAN=1 takes precedence when both Vulkan variables are set.

Build The App

Build renderer and Electron files:

npm run build

This runs TypeScript checks, builds Electron files, and builds the Vite renderer output.

Run Lint

Run ESLint:

npm run lint

Run Tests

Run the Vitest suite:

npm test

Run The Full Local Check

Run the full local verification pass:

npm run check

This runs TypeScript typechecking, ESLint, and Vitest tests.

Electron Smoke Test

After a production build, run the Electron smoke test:

npm run smoke:electron

The smoke test builds the app, launches the production Electron entrypoint, verifies the GM preload bridge, opens Player View through IPC, sends a Player View idle state, and checks Electron display enumeration.

Package For Windows

To create a Windows package:

npm run package:win

Successful Windows packaging creates output in the release/ folder, including the installer, blockmap, update metadata, and unpacked app.

Package For macOS

To create a macOS package:

npm run package:mac

macOS signing and notarization are not currently configured.

Package For Linux

To create Linux packages:

npm run package:linux

Linux packaging produces AppImage, Debian package, and RPM artifacts.

Project Architecture

Project Structure

Important project areas include:

electron/
  main.ts
  preload.ts
  persistenceCodecs.ts
  assetProtocol.ts

src/
  shared/
  renderer/

src/renderer/
  views/
  components/
  canvas/
  hooks/
  lib/
  styles/

docs/
  architecture.md
  canvas-performance-budget.md
  codebase-audit.md
  layer-ownership-rules.md
  release-process.md

tests/
  electron/
  renderer/
  shared/

Electron Main Process

The Electron main process handles:

  • Application lifecycle.
  • Secure window creation.
  • Campaign folder IO.
  • Asset import and copy operations.
  • Metadata backups.
  • Player View window control.
  • Native filesystem interactions.

Key files include:

  • electron/main.ts
  • electron/assets.ts
  • electron/assetProtocol.ts
  • electron/persistenceCodecs.ts
  • electron/metadataErrors.ts

Preload Bridge

The preload bridge exposes a typed API to the renderer through Electron's contextBridge.

The renderer does not receive unrestricted filesystem access.

Key file:

  • electron/preload.ts

Shared Models

Shared TypeScript code defines campaign and scene models, validation, defaults, schema normalization, and Player View-safe scene projection.

Key area:

  • src/shared/

Campaign and scene data include schema versions so future migrations have an explicit upgrade path.

Only bump schema versions when the saved campaign or scene JSON shape changes.

Renderer

The renderer contains the React GM View, React Player View, canvas rendering code, hooks, feature libraries, and styles.

Key areas include:

  • src/renderer/views/
  • src/renderer/components/
  • src/renderer/canvas/
  • src/renderer/hooks/
  • src/renderer/lib/
  • src/renderer/styles/

Canvas Rendering

Local VTT uses Canvas 2D for most map and scene rendering.

Canvas rendering covers:

  • Static maps.
  • Video maps.
  • Pan and zoom.
  • Grids.
  • Fog of war.
  • Tokens.
  • Drawings.
  • Templates.
  • Weather.
  • Animated effects.
  • Ruler measurement.
  • Scene overlays.

Three.js is used where 3D rendering is needed, such as dice.

Layer Ownership

Layer responsibilities are intentionally narrow so tools, rendering, visibility, and persistence stay predictable.

For detailed layer rules, see:

  • docs/layer-ownership-rules.md

General guidance:

  • Put visibility and reveal logic in Fog of War.
  • Put environmental visuals in Effects.
  • Put annotations and templates in Drawings.
  • Put movable map markers in Tokens.
  • Keep GM-only prep content private.
  • Do not overload reserved future layers.

Player View Projection

Player View receives a filtered scene payload.

Player-safe projection should prevent GM-only data from leaking into the Player View.

When adding features, make sure Player View visibility rules are handled centrally and tested.

Testing Guidance

Use focused tests for small changes and broader tests for cross-cutting behavior.

Important test areas include:

  • Campaign actions.
  • Persistence normalization.
  • Player View sync.
  • Scene editing.
  • Token behavior.
  • Fog and visibility.
  • Map calibration.
  • Measurement.
  • Dice.
  • Turn order.
  • Effects.
  • Electron metadata and asset behavior.

Tests live in:

  • tests/shared/
  • tests/renderer/
  • tests/electron/

Vitest discovery is intentionally limited to tests/**/*.test.ts so ignored build output such as dist-electron/ is not counted as source test coverage.

Release Workflow

Icons

Application icons live in the build/ folder:

build/
  icon.svg
  icon.ico
  icon.icns

The editable icon source is build/icon.svg.

To regenerate icon files after changing the source artwork:

npm run generate:icon

Windows packaging uses build/icon.ico.

macOS packaging uses build/icon.icns.

Release Process

Release builds are created by GitHub Actions when a version tag matching v*.*.* is pushed.

A typical release flow:

  1. Create or update a release branch.
  2. Bump the app version.
  3. Update CHANGELOG.md.
  4. Run local verification.
  5. Commit release metadata.
  6. Push the release branch.
  7. Create and push the version tag.
  8. Confirm the GitHub Actions release build succeeds.
  9. Check the GitHub Release assets.

For detailed steps, see:

  • docs/release-process.md

Version Numbers

The app version is stored in:

  • package.json
  • package-lock.json

Use npm to update both:

npm version 0.1.9 --no-git-tag-version

Only bump campaign or scene schema versions when saved data shape changes.

Schema constants live in:

  • src/shared/localvtt.ts

GitHub Release Assets

Published GitHub release assets may be immutable.

If a release has already been published for a version tag, create a new patch version and tag rather than trying to replace the existing release assets.

For example, if v0.1.8 is already published, use v0.1.9 for a corrected build.

The release workflow validates expected Windows, macOS, and Linux artifacts before publishing. If validation fails before a GitHub Release is created, fix the release branch, update or recreate the unpublished tag on the corrected commit, and rerun the tag workflow. If the GitHub Release already exists, do not retry the same tag.

Installer Upgrade And Downgrade Checks

Before publishing a Windows release, test installing the candidate over the previous release and confirm existing campaign folders, Desktop shortcuts, and Start Menu shortcuts still work.

Also attempt installing the previous release over the candidate release. Downgrades are not a supported recovery path unless a release explicitly says otherwise, so record whether the installer blocks, warns, or allows the downgrade and confirm campaign folders are not corrupted.

For the detailed checklist, see docs/release-process.md.

Pull Request Checklist

Before opening a pull request, run:

npm run check
npm run build

For changes affecting Electron, packaging, Player View, or release behavior, also run:

npm run smoke:electron

For packaging changes, test the relevant package command:

npm run package:win

or:

npm run package:mac

Contributor Notes

When contributing:

  • Keep changes focused.
  • Follow existing project structure.
  • Add or update tests for behavior changes.
  • Avoid moving unrelated files.
  • Keep Player View visibility rules in mind.
  • Preserve local-first campaign portability.
  • Do not introduce unrestricted renderer filesystem access.
  • Update docs when workflows or user-facing behavior changes.

Useful Documentation

Project docs include:

  • docs/architecture.md
  • docs/canvas-performance-budget.md
  • docs/codebase-audit.md
  • docs/layer-ownership-rules.md
  • docs/release-process.md

These are useful references for architectural decisions, release flow, and feature placement.

Related Pages

Clone this wiki locally