P13: dice — crit/fumble animation + roll visibility

- Crit/fumble emphasis: RollTray (and the Dice page result) highlight a nat-20 /
  critical-success as "✦ Critical ✦" (gold pop + glow) and a nat-1 / critical-
  failure as "✦ Fumble ✦" (red shake). naturalD20() exported from notation.
- DM sees players' rolls: RollFeed now also renders on the Combat page (players'
  rolls already broadcast to the GM).
- Players see the DM's public rolls: new gmRoll message — a hosting GM's rolls
  (Dice page + rollAndShow) broadcast to the table as "GM".
- DM secret-roll toggle (rollStore.secret) on the Dice page suppresses the broadcast.
- Realtime e2e: GM public roll appears in the player's table feed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-08 16:35:48 +02:00
parent d4ae60f199
commit d94df41a95
12 changed files with 129 additions and 25 deletions
+6
View File
@@ -46,6 +46,12 @@ test('GM hosts a session; a player on another device sees live combat', async ({
await player.getByTestId('player-live').getByRole('link').click();
await expect(player.getByText('Lia the Brave').first()).toBeVisible();
// GM rolls publicly on the Dice page → the player sees it in the table feed.
await gm.getByLabel('Primary').getByRole('link', { name: 'Dice' }).click();
await gm.getByRole('button', { name: 'Roll', exact: true }).click();
await expect(player.getByText('Table rolls')).toBeVisible();
await expect(player.getByText('GM').first()).toBeVisible();
await gmCtx.close();
await playerCtx.close();
});