LukasandClaude Opus 5 90eb39b227 Restore green merge gate: pnpm settings, deps, unbound-method
pnpm 10.32 stopped reading the "pnpm" field from package.json, so the
undici/picomatch overrides and the three GHSA suppressions had silently
stopped applying — only the stale lockfile still held undici at 7.24.8.
Move the surviving picomatch override to pnpm-workspace.yaml.

With the config live again, two high advisories surfaced:
- postcss was stuck at 8.5.15 (GHSA-r28c-9q8g-f849, patched in 8.5.18);
  nothing pinned it, so refresh to 8.5.25 within vite's range.
- undici GHSA-4cwx-7wf7-3272 needed >=7.29.0, but our ~7.24.0 pin existed
  because jsdom 29 crashed on undici 7.28+. jsdom 30 moved to undici ^8.9,
  so bump jsdom and drop both the pin and all three suppressions.

15 vulnerabilities (5 high, 3 suppressed) down to 1 moderate (smol-toml
via knip, below the --audit-level=high gate).

Separately, ports.ts declared its members with method shorthand, which
TypeScript treats as this-dependent, so oxlint's unbound-method fired
wherever a port was passed by reference (use-bestiary.ts:236). The ports
are bags of plain module functions, so declare them as readonly function
properties instead of suppressing the one call site. This makes parameter
types contravariant rather than bivariant; typecheck passes unchanged.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-05 13:24:13 +02:00

Initiative

A local-first initiative tracker and encounter manager for tabletop RPGs (D&D 5e / 2024). Runs entirely in the browser — no server, no account, no data leaves your machine.

What it does

  • Initiative tracking — add combatants (batch-add from bestiary, custom creatures with optional stats), roll initiative (manual or d20), cycle turns and rounds
  • Encounter state — HP, AC, conditions, concentration tracking with visual status indicators
  • Bestiary integration — import bestiary JSON sources, search creatures, and view full stat blocks
  • Player characters — create reusable player character templates with name, AC, HP, level, color, and icon; search and add them to encounters with pre-filled stats; manage (edit/delete) from a dedicated panel
  • Encounter difficulty — live 3-bar indicator in the top bar showing encounter difficulty (Trivial/Low/Moderate/High) based on the 2024 5.5e XP budget system; automatically derived from PC levels and bestiary creature CRs
  • Undo/redo — reverse any encounter action with Undo/Redo buttons or keyboard shortcuts (Ctrl+Z / Ctrl+Shift+Z, Cmd on Mac); history persists across page reloads
  • Import/export — export the full encounter state (combatants, undo/redo history, player characters) as a JSON file or copy to clipboard; import from file upload or pasted JSON with validation and confirmation
  • Persistent — encounters survive page reloads via localStorage; bestiary data cached in IndexedDB; player characters stored independently

Prerequisites

  • Node.js 22+
  • pnpm 10.6+

Getting Started

pnpm install
pnpm --filter web dev

Open http://localhost:5173.

Scripts

Command Description
pnpm --filter web dev Start the dev server
pnpm --filter web build Production build
pnpm test Run all tests (Vitest)
pnpm test:watch Tests in watch mode
pnpm vitest run path/to/test.ts Run a single test file
pnpm typecheck TypeScript type checking
pnpm lint Biome lint
pnpm format Biome format (writes changes)
pnpm check Full merge gate (see below)

Merge gate (pnpm check)

All of these run at pre-commit via Lefthook (in parallel where possible):

  • pnpm audit — security audit
  • knip — unused code detection
  • biome check — formatting + linting
  • oxlint — type-aware linting (complements Biome)
  • Custom scripts — lint-ignore caps, className enforcement, component prop limits
  • tsc --build — TypeScript strict mode
  • vitest run — tests with per-path coverage thresholds
  • jscpd + jsinspect — copy-paste and structural duplication detection

Tech Stack

  • TypeScript 5.8 (strict mode), React 19, Vite 6
  • Tailwind CSS v4 (dark/light theme)
  • Biome 2.4 (formatting + linting), oxlint (type-aware linting)
  • Vitest (testing, v8 coverage), Lefthook (pre-commit hooks)
  • Knip (unused code), jscpd + jsinspect (duplication detection)

Project Structure

apps/web/                  React 19 + Vite — UI components, hooks, adapters
packages/domain/           Pure functions — state transitions, types, validation
packages/application/      Use cases — orchestrates domain via port interfaces
data/bestiary/             Pre-built bestiary search index (~10k creatures)
scripts/                   Build tooling (layer checks, index generation)
specs/                     Feature specifications (spec → plan → tasks)

Architecture

Strict layered architecture with enforced dependency direction:

apps/web (adapters)  →  packages/application (use cases)  →  packages/domain (pure logic)
  • Domain — pure functions, no I/O, no randomness, no framework imports. Errors returned as values (DomainError), never thrown.
  • Application — orchestrates domain calls via port interfaces (EncounterStore, PlayerCharacterStore, etc.). No business logic.
  • Web — React adapter. Implements ports using hooks/state. All UI components, persistence, and external data access live here.

Layer boundaries are enforced by automated import checks that run as part of the test suite.

Contributing

Workflow

Development is spec-driven. Feature specs live in specs/NNN-feature-name/ and are managed through Claude Code skills (see CLAUDE.md for full details).

Scope What to do
Bug fix / CSS tweak Fix it, run pnpm check, commit. Optionally use /browser-interactive-testing for visual verification.
Change to existing feature Update the feature spec, then implement
Larger change to existing feature Update the spec → /rpi-research/rpi-plan/rpi-implement
New feature /speckit.specify/speckit.clarify/speckit.plan/speckit.tasks/speckit.implement

Use /write-issue to create well-structured Gitea issues, and /integrate-issue to pull an existing issue's requirements into the relevant feature spec.

Before committing

Run pnpm check — Lefthook runs this automatically at pre-commit, but running it manually first saves time. All checks must pass.

Conventions

  • Biome for formatting and linting — tab indentation, 80-char lines
  • TypeScript strict mode with verbatimModuleSyntax (type-only imports must use import type)
  • Max 8 props per component interface — use React context for shared state
  • Tests in __tests__/ directories — test pure functions directly, use renderHook for hooks

See CLAUDE.md for the full conventions and project constitution.

Bestiary Index

The bestiary search index (data/bestiary/index.json) is pre-built and checked into the repo. To regenerate it (e.g., after a new source book release):

  1. Clone 5etools-mirror-3/5etools-src locally
  2. Run node scripts/generate-bestiary-index.mjs /path/to/5etools-src

The script extracts creature names, stats, and source info into a compact search index.

S
Description
Initiative Tracker
Readme
2.3 MiB
Languages
TypeScript 90.8%
Shell 4.3%
Python 2.3%
JavaScript 2%
CSS 0.5%