Skip to content

Development And Contributing

errorsallthethings edited this page Jul 10, 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, release metadata validation, ESLint, and Vitest tests.

Before opening or marking a feature PR ready for merge, also run the Electron smoke pass when the branch changes Electron startup, Player View, canvas rendering, table tools, dice scene rolls, effects, maps, or release behavior:

npm run smoke

Electron And Visual Smoke Tests

Run the full smoke pass before sharing a build or after changes that affect Electron, Player View, canvas rendering, media playback, app windows, or local protocol behavior:

npm run smoke

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

Run an individual smoke check when narrowing a failure:

npm run smoke:electron
npm run smoke:visual

Run smoke commands sequentially. Both smoke paths launch Electron and exercise Player View IPC, so running them at the same time can make a smoke run observe the wrong Player View state.

The visual smoke test sends a deterministic scene and test pattern to Player View, verifies that scene rendering is nonblank, checks dice, turn order, and player seat overlays, and writes screenshots to the OS temp folder for failure evidence. Automated video-map visual coverage is deferred, so video maps still need a manual smoke pass.

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

Local VTT uses a React-friendly, Electron-friendly version of MVC rather than strict classic MVC:

  • Model / domain: src/shared, src/renderer/lib, and focused Electron helpers own schemas, migrations, validation, projections, pure state transitions, persistence codecs, and reusable file/path rules.
  • View: React components in src/renderer/components and app-level view composition in src/renderer/views render UI and expose typed user actions.
  • Controller / workflow coordination: React hooks in src/renderer/hooks and Electron IPC registration modules coordinate app workflows, async work, and state transitions between views, domain helpers, and platform services.
  • Platform adapters: Electron services in electron/ own windows, dialogs, filesystem access, protocols, thumbnail/media integration, and packaging/runtime checks.

The goal is separation of concerns, not pattern purity. New code should keep domain rules, React rendering, workflow orchestration, persistence, and native platform access in their existing lanes.

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
  diagrams/
  layer-ownership-rules.md
  project-structure.md
  release-process.md

tests/
  electron/
  renderer/
    scene/
  shared/

Scene Component Structure

The scene-canvas component area is split by responsibility so scene authoring code does not drift back into one flat folder:

  • src/renderer/components/scene/context-menu/: context menu rendering, opening state, target detection, and action routing.
  • src/renderer/components/scene/hooks/: React hooks that coordinate canvas input, rendering, lifecycle, selection, assets, and viewport reporting.
  • src/renderer/components/scene/input/: pointer, keyboard, hover, drag, token, drawing, fog, ruler, weather, and effect interaction helpers.
  • src/renderer/components/scene/map/: map calibration, video map elements, map readiness, and video-map viewport policy.
  • src/renderer/components/scene/overlays/: Player View turn/seat overlays, scene status strips, and tool status overlays.
  • src/renderer/components/scene/state/: scene-canvas state transitions shared by hooks and tests.

Scene-canvas tests live in tests/renderer/scene/ and should keep the source filename in the test filename when possible.

Folder Growth Guidance

Avoid mass-moving files only to make directory counts look smaller. File moves should happen when they improve ownership, reduce noisy imports during active work, or make a feature easier to test.

Good future destinations, when work already touches the relevant files, include:

  • src/renderer/hooks/campaign/
  • src/renderer/hooks/gm/
  • src/renderer/hooks/player-view/
  • src/renderer/hooks/scene-canvas/
  • tests/renderer/canvas/
  • tests/renderer/lib/
  • tests/renderer/components/

Do not create empty folders just to reserve names. Move files into them as part of reviewed feature or cleanup bundles.

Architecture Diagrams

Wiki-ready PlantUML source diagrams live in the main repo:

  • docs/diagrams/README.md
  • docs/diagrams/plantuml.md

The diagrams cover:

  • System context.
  • Container boundaries.
  • React/Electron MVC-style ownership.
  • Renderer components.
  • Scene Canvas pipeline.
  • Electron main-process components.
  • Campaign persistence flow.
  • Player View trust boundary.
  • Asset import and token crop flow.
  • Asset deletion safety flow.
  • Smoke verification flow.

Update those diagrams when runtime boundaries, persistence flows, Player View projection, asset import/delete behavior, or smoke coverage changes.

Electron Main Process

The Electron main process handles:

  • Application lifecycle.
  • Secure window creation.
  • Service registration.
  • Player View window control.
  • Native filesystem interactions.

Feature-specific filesystem workflows, such as campaign persistence, asset import, backups, health checks, thumbnail generation, and asset maintenance, should live in focused Electron services or IPC modules instead of growing electron/main.ts.

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.
  • File/path safety and write-before-delete recovery.

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

When user-facing workflows change, update the matching Wiki pages before the release is tagged. Common release pages include Scenes And Maps, Table Tools, Effects Layer, Dice And Turn Order, Player View Setup, and Troubleshooting.

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

For targeted smoke reruns while narrowing a failure:

npm run smoke:electron
npm run smoke:visual

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/diagrams/README.md
  • docs/diagrams/plantuml.md
  • docs/layer-ownership-rules.md
  • docs/project-structure.md
  • docs/release-process.md

These are useful references for architectural decisions, release flow, feature placement, canvas performance expectations, and Wiki-ready architecture diagrams.

Related Pages

Clone this wiki locally