The logs page white-screened when deployed against Firestore. LogsView
paginates with the neutral offset() constraint, which the server/sqlite
adapter pushes into SQL — but the firebase adapter's subscribeCollection
had no case for it and passed the raw {__type:'offset'} object through
to the SDK's query(), which throws on non-QueryConstraint values. The
throw fired inside useFirestoreCollection's effect on first render,
unmounting the whole tree. Dev never hit it because dev runs the server
adapter.
Fix: shared toSdkConstraints() translator in the firebase adapter,
used by getCollection and subscribeCollection. The client SDK has no
offset, so emulate it — widen limit to offset+limit and slice the
leading docs off the snapshot (same read cost as the admin SDK's
native offset, which also bills skipped docs). Unknown constraint
types are now dropped instead of passed through, so a future neutral
builder can't reintroduce the crash. This also fixes getCollection
silently skipping offset, which returned page 1 for every page.
Server/sqlite adapter unchanged; offset still runs in SQL. Two new
shared contract tests (offset pagination + offset past end) run
against both adapters and pass: firebase mock 33, live sqlite 40.
Also bump better-sqlite3 ^11.3.0 -> ^12.0.0: v11 fails to compile
against Node 26's V8 (GetPrototype removed), so server tests could
not run at all on Node 26. v12.11.1 builds clean; full server suite
passes.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The rework-backend merge added an optional self-hosted Express/ws/SQLite
backend (npm workspaces, single-container Docker deployment) alongside
the existing Firebase default. Bump APP_VERSION and refresh README to
cover both storage modes and the new repo layout.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
npm workspaces needs shared/package.json and server/package.json present
at install time to link @ttrpg/shared, otherwise the build stage fails
with "Module not found: Error: Can't resolve '@ttrpg/shared'".
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Firestore rejects writes containing undefined values. The pause/resume
and end-combat undo snapshots read encounter fields that may not yet
exist in Firestore, so add ?? false / ?? null / ?? 0 fallbacks to
match the pattern already used in the start-combat undo path.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When a participant was activated mid-combat, computeTurnOrderAfterAddition
appended them to the end of turnOrderIds. The visual display sorted by
initiative (putting them at the top), but the turn pointer followed the
append order, making it look like the top-initiative participants were
skipped when the round wrapped.
Fix: at the round boundary in handleNextTurn, rebuild turnOrderIds from
all active participants sorted by initiative. Mid-round additions go last
in round 1 (standard D&D ruling), then slot into proper initiative order
from round 2 onwards. Also adds turnOrderIds to the next-turn undo snapshot.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Each logged action now stores a Firestore snapshot of the affected
encounter state. The /logs page shows an ↩ Undo button on any entry
with undo data; clicking it restores the encounter to its pre-action
state and marks the entry as rolled back (greyed out, strikethrough).
Covered actions: damage/heal, condition toggle, activate/deactivate,
add/remove participant, next turn, start/pause/resume/end combat.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Documents conditions list (now 22), combat log at /logs, hide player HP
toggle, inactive monster hiding, fullscreen button, and wake lock toggle.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Inactive monsters are now filtered out of the DisplayView so DMs can
pre-stage summoned/reserve monsters without spoiling them for players.
Inactive characters remain visible since their inactive state is
player-relevant.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
onAuthStateChanged fires with null before signInAnonymously completes,
causing DisplayView to query Firestore unauthenticated. Now only marks
auth ready when an actual user is present; auth failures are handled in
the catch block to avoid hanging the UI.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Instruments 9 handlers (combat start/end/pause/resume, next turn,
participant add/remove/toggle, HP changes, conditions) to write
timestamped entries to a Firestore logs collection. New LogsView
at /logs shows entries newest-first with encounter context, and
includes a Clear Log button. Adds a View Logs link in the header.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add DM toggle (default on) to hide player HP bars on player display;
persisted in activeDisplay Firestore doc for real-time sync
- Add Alchemist Fire and Bardic Inspiration conditions; sort all
conditions alphabetically
- Fix turn order skipping when participants are deleted, deactivated,
or killed mid-combat: turnOrderIds was never updated, causing
handleNextTurn to resolve currentIndex as -1 and snap back to the
first participant. Now all mutation paths (delete, toggle active,
HP death/resurrection) keep turnOrderIds in sync and advance the
turn pointer correctly when the current participant is removed.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>