Commit Graph

87 Commits

Author SHA1 Message Date
david raistrick 17245dfa1b Reapply "M2: gate storage init on STORAGE mode (firebase vs ws/memory)"
This reverts commit e843acdf8a.
2026-06-29 12:36:16 -04:00
david raistrick e843acdf8a Revert "M2: gate storage init on STORAGE mode (firebase vs ws/memory)"
This reverts commit 3e84f28325.
2026-06-29 12:03:57 -04:00
david raistrick 74b4c2c42d Revert "M2: fix ws adapter for browser WebSocket + firebase path prefix"
This reverts commit e743d40e8d.
2026-06-29 12:03:57 -04:00
david raistrick d1ee69a70a Revert "M3: stub db sentinel in ws/memory mode so legacy guards pass"
This reverts commit a5a4df78f0.
2026-06-29 12:03:57 -04:00
david raistrick a5a4df78f0 M3: stub db sentinel in ws/memory mode so legacy guards pass
ws/memory storage mode never set the module-level `db` variable. 24 handlers
guarded with `if (!db) return` early-exited, silently dropping all writes
(create campaign, add encounter, participant CRUD, combat, logs).

db stays a truthy sentinel object { __localStub: true } in non-firebase mode.
All real reads/writes route through storage.*; db only used by guards.

56 frontend tests green. Verified via headed browser: create campaign flow
works end-to-end (modal closes, campaign appears via WS realtime push).
2026-06-29 11:30:08 -04:00
david raistrick b095e37bfe M3: remove project dev-serve skill (moving to generic global skill) 2026-06-29 11:25:13 -04:00
david raistrick 54e8df9ffa M3: add dev-serve skill (boot stack + headed browser for human testing)
- pkill stale procs, boot backend + frontend in background
- STORAGE=ws default (firebase opt-in)
- agent_browser --headed fresh session for user-visible window
- troubleshooting table + teardown

Skill loads via /skill:dev-serve
2026-06-29 11:24:23 -04:00
david raistrick e743d40e8d M2: fix ws adapter for browser WebSocket + firebase path prefix
- WebSocketImpl: native browser WebSocket if present, else ws pkg (node/jest)
- ensureWs: use onopen/onmessage/onerror/onclose property handlers (browser API)
  instead of ws.on('open') EventEmitter (node-only) — was silent no-op in browser
- norm(): strip 'artifacts/{APP_ID}/public/data/' prefix from all paths
  App passes firebase-prefixed paths; backend uses canonical campaigns/...
- apply norm() to getDoc/getCollection/setDoc/updateDoc/deleteDoc/addDoc/
  subscribeDoc/subscribeCollection/changeTypesForDocPath/changeTypesForCollPath

Verified: STORAGE=ws boots, WS subscribe fires, backend broadcast -> browser
live update (curl POST campaign -> appears without reload). Cross-device sync
confirmed end-to-end.
2026-06-28 22:43:30 -04:00
david raistrick 3e84f28325 M2: gate storage init on STORAGE mode (firebase vs ws/memory)
- STORAGE_MODE = getStorageMode()
- initializeStorage(): firebase = real SDK init; ws/memory = stub auth
- App auth flow: ws/memory skip signInAnonymously, unblock UI
- error screen mode-aware message

56 tests green. STORAGE=ws now boots past config error.
2026-06-28 21:11:56 -04:00
david raistrick 812298fa73 M2: refactor all firebase write sites to storage adapter
- 37 call sites: setDoc/updateDoc/deleteDoc/addDoc/getDocs/writeBatch -> storage.*
- adapter wraps SDK, path-string interface
- storage instance app-wide (getStorage)
- firebase.js: static imports (getDoc/getDocs alias), no dynamic import

56 frontend tests green. STORAGE=firebase = identical behavior.
2026-06-28 21:05:39 -04:00
david raistrick 5bb9e5fc19 M2: refactor hooks to storage adapter (subscribe)
- src/storage/index.js: getStorage() factory + SDK re-exports
- App.js: useFirestoreDocument/Collection call storage.subscribeDoc/Collection
- getStorage import added

56 frontend tests green. Hooks now impl-agnostic (firebase vs ws).
2026-06-28 19:03:44 -04:00
david raistrick 35b5a1d238 test: logs + deathSave characterization (6 tests)
- Logs.characterization.test.js: logAction (write + undo payload), clearLogs batch delete, undo (updateDoc encounter + mark undone), deathSave increment + isDying
- mock firestore getDocs: return .ref.path on docs (batch.delete support)
- mock addDoc: record full doc path not collection path

All write sites characterized. 56 frontend tests green.
2026-06-28 19:00:08 -04:00
david raistrick d581e60ba3 test: combat characterization (9 tests)
- Combat.characterization.test.js: startEncounter (state + activeDisplay), nextTurn, round wrap, pause, resume, endEncounter (reset + clear activeDisplay), toggleHidePlayerHp

Locks combat control write paths.
2026-06-28 18:52:49 -04:00
david raistrick 4158a1634d test: participant characterization (9 tests)
- Participant.characterization.test.js: addMonster (shape, initiative range, NPC), deleteParticipant, toggleActive, applyDamage, damage-to-0, heal revive, toggleCondition
- testHelpers.js: getParticipantForm (scoped), addMonsterViaUI, setupReady, startCombatViaUI

Locks participant write paths + payload shapes. Refactor guard.
2026-06-28 18:50:42 -04:00
david raistrick 0c1196aee1 test: encounter characterization (6 tests)
- Encounter.characterization.test.js: createEncounter, path nesting, togglePlayerDisplay on/off, deleteEncounter + clears activeDisplay
- testHelpers.js: createEncounterViaUI, selectEncounterByName

Locks encounter write paths + payload shapes.
2026-06-28 18:30:57 -04:00
david raistrick 672f042b60 chore: alphabetize package.json deps after install/uninstall churn
trivial reorder, no version changes
2026-06-28 18:29:09 -04:00
david raistrick b6555648ee test: campaign characterization (7 tests)
- src/testHelpers.js: renderApp, createCampaignViaUI, selectCampaignByName
- App.characterization.test.js: createCampaign, addCharacter, updateCharacter, deleteCharacter, deleteCampaign + path namespace + bg url
- mock firestore writeBatch sync (was async, app no-await)

Locks path + payload shape per action. Refactor guard.
2026-06-28 18:12:27 -04:00
david raistrick 84dd17e174 test: Firebase mock harness + createCampaign characterization
- src/__mocks__/firebase/*: jest manual mocks (app/auth/firestore)
- src/__mocks__/firebase/_mock-db.js: in-memory DB + call recorder
- src/setupTests.js: jest-dom, env stubs, crypto polyfill, DB reset
- src/App.characterization.test.js: createCampaign -> setDoc path/payload locked
- src/storage/contract.js (renamed from .test.js, helper not suite)

21 tests green (memory 19 + createCampaign 2).
2026-06-28 17:59:50 -04:00
david raistrick 12b24eb707 M2 (C): storage barrel re-export, App.js imports swapped
- src/storage/index.js: re-exports Firebase SDK
- App.js: imports from ./storage (was firebase/* direct)
- STORAGE=firebase = identical behavior
- dev server compiles clean

Safe refactor proof. Next: per-call-site path-based rewrite for ws adapter.
2026-06-28 17:51:39 -04:00
david raistrick 2ee2bba93b M2 (TDD): storage contract test + memory impl
- src/storage/contract.test.js: storage interface spec (19 assertions)
- src/storage/memory.js: in-process impl (Map + EventEmitter)
- src/storage/storage.test.js: runner, memory first

TDD: contract RED first, memory built to satisfy, 19/19 green.
Next impls (ws, firebase) run same contract.
2026-06-28 17:18:14 -04:00
david raistrick 9457f48b23 ci: local pre-push hook instead of GH Actions (private repo)
- remove .github/workflows/ci.yml
- add .githooks/pre-push: runs npm run test:all
- git config core.hooksPath .githooks (set)
- docs/DEVELOPMENT.md: document local pipeline

Private repo = no free Actions. Revisit when public.
2026-06-28 17:16:23 -04:00
david raistrick fa19913e23 ci: add GitHub Actions workflow + dev docs + test:all script
- .github/workflows/ci.yml: runs shared + server tests on push/PR
- docs/DEVELOPMENT.md: setup, run, test, architecture, status
- package.json: test:all script (shared + server suites)
2026-06-28 17:14:51 -04:00
david raistrick 0e76fb2fc7 M1: backend (Express+ws+better-sqlite3) + integration tests
- server/db.js: SQLite schema mirroring Firestore doc tree
- server/handlers.js: action -> shared turn fn -> tx persist -> broadcast
- server/index.js: REST endpoints + WebSocket real-time push
- server/server.test.js: 7 integration tests (REST CRUD + combat flow)
- --forceExit for jest (open WS handles)

Backend boots, serves state, persists to SQLite.
2026-06-28 17:01:53 -04:00
david raistrick e06adaa081 M1: shared turn logic + characterization tests (39 green)
- npm workspaces: shared/, server/
- shared/turn.js: port turn logic verbatim from App.js (bugs preserved)
- 39 characterization tests lock current behavior
- gitignore: sqlite data, logs
2026-06-28 16:57:43 -04:00
david raistrick d679c9d1e9 docs: restore approved milestone plan (backend=M1, drop FSM-extract milestone) 2026-06-28 16:53:18 -04:00
david raistrick ad7979d8fd docs: add rework plan (backend-first, test-ecosystem baseline) 2026-06-28 16:47:48 -04:00
robert 33b27775b4 Bump version to v0.3
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
v0.3
2026-06-27 20:15:20 -04:00
robert d96d3a6cf2 Guard against undefined values in combat log undo payloads
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>
v.03
2026-06-27 17:19:52 -04:00
robert 439f48871e Fix mid-round activation causing participants to be skipped in round 2+
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>
2026-06-27 16:57:08 -04:00
robert 35990f588e Add per-entry undo buttons to combat log, bump to v0.2.5
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>
2026-06-27 16:45:07 -04:00
robert b11fbe4715 Update README to reflect current feature set
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>
2026-06-27 11:59:36 -04:00
robert 7b52480329 Add wake lock toggle to prevent screen sleep on player display
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-27 11:56:11 -04:00
robert 58cc588726 Add fullscreen button to player display view
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-27 11:53:56 -04:00
robert e200f59b7e Hide inactive monsters from player display view
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>
2026-06-26 14:39:59 -04:00
robert bb65709e26 Added shield to list of conditions. 2026-05-16 15:29:27 -04:00
robert 33d831af54 Fix race condition causing permissions error on /display page
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>
2026-05-16 10:53:03 -04:00
robert 4150267925 Add combat action log at /logs
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>
2026-05-16 10:25:17 -04:00
robert e23cea205a Add HP toggle, new conditions, fix turn order sync bug
- 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>
2026-05-16 10:00:17 -04:00
robert 6cd25dadaa Alchemist Fire and Bardic Inspiration conditions, sort alphabetically. 2026-05-16 09:37:11 -04:00
robert 90cfb36b56 Adds a 'Hide player HP' toggle in the Combat Controls panel (on by default) that suppresses health bars for player characters in the DisplayView. Setting is persisted in the activeDisplay Firestore doc so it survives page reloads and takes effect in real time on the player screen. 2026-05-16 09:30:35 -04:00
robert 7676751a5b Added weapon conditions 2026-04-26 17:12:29 -04:00
robert 16118dd958 Updated README.md and added CLAUDE.md 2026-04-26 11:42:22 -04:00
robert 33c93ab86b Added conditions. 2026-04-26 10:37:25 -04:00
robert 451151628c fixing build and nginx 2026-04-25 20:35:52 -04:00
robert 1e0df31cd4 More text improvements. 2026-04-25 20:25:34 -04:00
robert 3be9b0a921 Updated look of site. 2026-04-25 18:37:55 -04:00
robert 895fa06227 updated fix for adding combatants mid fight and added some death effects. 2025-12-13 19:15:26 -05:00
robert 82f45e60f0 Updated readme, and added a license. 2025-05-28 18:58:38 -04:00
robert ceba7632f8 Update README.md
Just a little clean up.
2025-05-28 15:33:44 -04:00
robert 8cf3a49a76 Added collapsible Characters. v0.2.1 2025-05-28 14:58:54 -04:00