14 Commits

Author SHA1 Message Date
b1121651dc v0.2.5: 1Hz tick keeps timer counting when no envelope event fires
Bug: timeSinceStart only updated when an envelope event fired
(combatStart, attack-roll, etc.). If the combat was idle — a
player thinking about their turn, between turns — the timer froze.

Fix: 1Hz self-rescheduling setTimeout in the constructor that
bumps timeSinceStart and triggers a throttled render while
_state.isActive is true. Stopped on unwireHooks (combat end).

TDD: Section O (5 assertions) added BEFORE the fix.
- O.1 initial timeSinceStart reflects _combatStartedAt
- O.2 advances without an envelope event
- O.3 tick interval is ~1s
- O.4 timer does not tick when combat is inactive
- O.5 timer resumes when combat becomes active again

Tests: 65/65 passing in ~2s. Playwright 31/31.
2026-06-22 19:21:30 -04:00
86687e4b7a test:turn-update-live: live verification of the two v0.2.4 fixes
- Requires GM slot (skipped in CI / when user has GM open).
- Test 1: create/start combat with 2+ combatants, advance one turn,
  assert HUD shows new combatant as current.
- Test 2: reload the page mid-combat, assert HUD auto-opens with
  the resumed encounter state.
- npm run test:turn-update-live
2026-06-22 18:34:13 -04:00
e9e550b190 v0.2.4: current-turn updates on combatTurn + session-open auto-open
Bug 1: current-turn indicator not updated on combatant switch.
Foundry's combatTurn hook fires BEFORE the new state is committed,
so game.combat.combatant is the OLD combatant at that moment. The
event-translation layer now resolves the new combatant from
combat.turns[newTurn] and passes combatantId + combatantName +
combatantTokenId through. The HUD stashes these on _state._latestTurn
and uses it as a tiebreaker when the encounter's combatantId is
stale (which battle-focus's encounter always is, since bf doesn't
update currentTurn on turn events).

Bug 2: HUD doesn't open on session-open with a pending/active combat.
At ready, check battle-focus.api.getActiveEncounter(); if it's
live (not endedAt), open the HUD and populate from the encounter.
This handles browser-refresh mid-combat and 'pending combat'
(tracker exists but Start Combat not yet clicked).

Bonus: encounter.combatantId key resolution. The encounter's
combatantId is the Foundry combatant document id, but the
combatants map may be keyed by tokenId, actorId, or combatantId.
The currentTurn resolver now tries all three.

Tests: 60/60 passing in <1s. Sections M (turn event) + N
(session-open) added.
2026-06-22 18:32:05 -04:00
95666fdcf9 test:matrix: 8-cell matrix + audit report + npm script
- tests/verify-hub-e2e-matrix.mjs: 8 cells (full install, each
  single-module removal, solo chh, solo ia, all off). 53/53 pass.
  Restores full install at the end.
- package.json: add test:matrix script.
- tests/AUDIT_REPORT.md: per-feature reachability audit covering
  all 4 modules + cross-module integration claims. Verdict:
  SHIP-READY. Out-of-scope items documented for follow-up.
2026-06-22 18:19:29 -04:00
92c56e6691 test:matrix: 8-cell add/remove module matrix (53/53)
verify-hub-e2e-matrix.mjs verifies that adding or removing any of
the 4 modules from the active world doesn't break the others'
lifecycle or the hub's behavior. Each cell sets
core.moduleConfiguration, reloads, and runs a focused subset of
assertions.

Cells:
  0. baseline (all 4 on)
  1. remove hooks-lib
  2. remove battle-focus
  3. remove its-achievable
  4. remove combat-hud-hub
  5. only combat-hud-hub
  6. only its-achievable
  7. all off (control)

All 53 assertions pass. combat-hud-hub's core sections correctly
register iff battle-focus is active. pinned-achievements section
correctly registers iff its-achievable is active. No thrown
errors in any cell.
2026-06-22 18:18:26 -04:00
e468771156 test:foundry: pass with full-install world (31/31)
- Wait for api.isReady() before checking section list.
- Auto-create a combat + place tokens if no encounter exists.
- Force a render after pushFeedEntry so the section's render fn
  actually fires.
- Capture full-install screenshot at tests/screenshots/.
- Screenshot shows: Round 1, Fire Giant (only actor in demo world),
  Dice Streak 0, Pinned Achievements 'None yet' — its-achievable
  integration with combat-hud-hub working end-to-end.
2026-06-22 17:10:14 -04:00
bf9b8cd1cc v0.2.3: add isReady() + 3-place version rule
- New api.isReady() returns true after the ready hook fires (and
  core sections have registered). Tests now wait for isReady()
  before asserting on the section list.
- 3-place version rule (module.json + package.json + main.js
  MODULE_VERSION) — v0.2.2 was missing the source bump; corrected
  by going to v0.2.3 across all three places.
- Tests: 50/50 passing in <1s.
2026-06-22 17:08:24 -04:00
e6531ec569 v0.2.2: bump MODULE_VERSION in main.js to match module.json/package.json
Missed the 3-place version rule on the v0.2.2 bump. MODULE_VERSION
was still v0.2.0 in scripts/main.js, so Foundry's init log showed
the old version and the live system didn't have getFeed/clearFeed.
Caught during the e2e Playwright run when Foundry loaded v0.2.0
of the main.js but v0.2.2 of the module.json.
2026-06-22 17:06:35 -04:00
23c2deeac5 v0.2.2: add getFeed + clearFeed read accessors
- api.getFeed(sectionId) returns a shallow copy of the section's
  feed entries. Useful for tests + consumer introspection.
- api.clearFeed(sectionId) empties a section's feed.
- Smoke: 50/50 passing. Section C extended with new assertions.
- This unblocks the its-achievable v0.3.0 Playwright test, which
  needs getFeed to verify chatBubble → pushToHubFeed → chh feed.
2026-06-22 16:15:45 -04:00
f752d15805 v0.2.1: add Playwright test + tests/PLAN.md
- New tests/verify-combat-hud-hub-foundry.mjs (12 assertion sections).
  Covers live Foundry v14: API surface, soft-dep presence, section
  registry round-trip, pushFeedEntry, HUD open on combatStart, core
  section rendering, current-turn indicator, screenshot, no errors.
- New tests/PLAN.md (per-repo test plan).
- .gitignore: tests/screenshots/ excluded.
2026-06-22 16:09:23 -04:00
f304db49cc v0.2.1: current-turn indicator + encounter-as-source-of-truth
- Combatants row whose tokenId matches encounter's current combatant
  gets data-chh-current-turn=true, an accent-color border + tint, an
  outlined portrait, and a '▶' marker before the name.
- Header section prepends '▶' before the turn name.
- buildRenderContext now resolves currentTurn from the encounter
  singleton (encounter.currentTurn / encounter.combatantId) instead
  of reading game.combat directly. battle-focus owns the encounter.
- Tests: 46/46 in <1s, new section L covers the indicator.
2026-06-22 16:05:14 -04:00
04008acc66 v0.2.0: port HUD from its-achievable as a section-based host
- Ported scripts/hud.js, scripts/event-translation.js,
  templates/hud.html, styles/hud.css from its-achievable v0.2.0.
- Renamed bf-hud-* CSS prefix to chh-* (function-over-branding).
- HUD now renders via addSection({ id, label, render }); no internal
  pinned-achievements feed. its-achievable (v0.3.0) registers its own
  pinned-achievements section.
- Built-in core sections: core-header, core-combatants, core-dice-streak.
- New hudPosition setting (per-user, top/bottom/left/right).
- Throttled render (1Hz).
- ApplicationV2 surface: open, close, isOpen, getState, getView,
  forceRender, wireHooks, unwireHooks.
- Tests: 42/42 passing in <1s covering PLAN sections A-K.
2026-06-22 12:37:12 -04:00
9c4e4c86fe v0.1.0: scaffold combat-hud-hub with public API + soft-dep wiring
- Public API: addSection, removeSection, pushFeedEntry, listSections,
  getHud, openHud, closeHud.
- Soft-dep on foundry-hooks-lib (envelope subscription) + battle-focus
  (encounter seam).
- Built-in core sections (round, current turn, per-PC damage, dice
  streak) register only when both deps are present at ready.
- Smoke tests: 22/22 in <1s covering sections A-G of tests/PLAN.md.
- HUD instance is a stub in v0.1.0; ApplicationV2 mounting in v0.2.0.
2026-06-22 12:04:43 -04:00
6adc55c9f9 Initial commit 2026-06-22 15:57:54 +00:00