Files
combat-hud-hub/CHANGELOG.md
Kaysser Taylor 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

4.9 KiB

Changelog

All notable changes to Combat HUD Hub are documented here.

[0.2.5] — 2026-06-22 (timer auto-tick)

  • 1Hz tick keeps the timer counting. The HUD's timeSinceStart field 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. Added a 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).
  • Tests: 65/65 passing in ~2s. Section O covers initial value, no-event advance, tick interval, idle pause, and resume.

[0.2.4] — 2026-06-22 (current-turn updates on combatTurn + session-open auto-open)

  • Current-turn updates instantly on combatant switch. The event-translation layer now resolves the new combatant from combat.turns[newTurn] (because Foundry fires combatTurn BEFORE the new state is committed, so game.combat.combatant is still the OLD combatant at that moment). The HUD stashes the new turn info on _state._latestTurn and uses it as a tiebreaker when the encounter's combatantId is stale.
  • Session-open auto-opens HUD with active encounter. At ready, if battle-focus.api.getActiveEncounter() returns a live (not endedAt) encounter, the HUD opens and populates from the encounter. This handles browser-refresh mid-combat and "pending combat" (combat tracker exists but Start Combat not yet clicked) — the HUD no longer stays invisible until the next event fires.
  • 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.

[0.2.3] — 2026-06-22 (isReady() + 3-place version fix)

  • New api.isReady() returns true after the ready hook has fired (and core sections have been registered). Lets consumers and tests gate on "ready" instead of polling the section list.
  • 3-place version rule enforced. v0.2.2's source MODULE_VERSION was missed on the previous bump; corrected and now bumped to v0.2.3 across module.json + package.json + main.js.
  • Tests: 50/50 passing in <1s.

[0.2.2] — 2026-06-22 (getFeed + clearFeed)

  • New api.getFeed(sectionId) — read accessor that returns a shallow copy of the section's feed entries. Useful for consumers (and tests) that need to inspect feed state.
  • New api.clearFeed(sectionId) — empties a section's feed.
  • Tests: 50/50 passing in <1s. Section C extended with getFeed/clearFeed assertions.

[0.2.1] — 2026-06-22 (current-turn indicator)

  • Current-turn indicator. The combatants row whose tokenId matches the encounter's current combatant gets data-chh-current-turn="true", a .chh-section-combatants-row--current class, an accent-color left border + subtle background tint, an outlined portrait, and a "▶" marker before the name. The header section also prepends "▶" before the turn name.
  • Encounter-as-source-of-truth for currentTurn. buildRenderContext now resolves the current turn from the encounter singleton (encounter.currentTurn or encounter.combatantId → encounter.combatants.get(...)) instead of reading game.combat directly. battle-focus owns the encounter; the hub reads it.
  • Tests: 46/46 passing in <1s covering sections A-L.

[0.2.0] — 2026-06-20 (port HUD from its-achievable)

  • Ported scripts/hud.js, scripts/event-translation.js, templates/hud.html, styles/hud.css from its-achievable v0.2.0.
  • Renamed bf-hud-* CSS class prefix to chh-* (function-over-branding).
  • Refactored HUD to render via addSection({ id, label, render }) API. The HUD itself no longer owns a pinnedAchievements feed — that becomes a section registered by its-achievable (v0.3.0).
  • Built-in core sections now: core-header (round + current turn + portrait), core-combatants (per-PC rows), core-dice-streak.
  • New hudPosition setting (top/bottom/left/right, per-user) replaces the previous its-achievable-owned setting.
  • Throttled render (1Hz, same as its-achievable v0.2.0).
  • ApplicationV2 surface: open, close, isOpen, getState, getView({ isGM, character }), forceRender, wireHooks, unwireHooks.
  • Smoke tests: 42/42 passing in <1s covering sections A-K of tests/PLAN.md.

[0.1.0] — 2026-06-20 (initial scaffolding)

  • New module extracted from its-achievable v0.2.0.
  • Public API: addSection, removeSection, pushFeedEntry, listSections, getHud, openHud, closeHud.
  • Soft-dep wiring for foundry-hooks-lib (envelope subscription) and battle-focus (encounter seam).
  • Built-in core sections register only when both soft-deps are present at ready.
  • Smoke test: 22/22 passing in <1s.
  • HUD instance is a stub; real ApplicationV2 mounting deferred.