Cleanup:
- Removed 109 stale asset files from static/assets/ (was 122, now 13)
- Removed static/v2/ entirely (was duplicate of root assets)
- Removed dead dashboard code: DashboardView, Layout, GlobalStats,
CharacterCard, CharacterGrid, VitalBar, TabContainer, CombatTab,
RaresTab, MapTab, InventoryTab, global.css, MapTransformContext
- Removed recharts dependency (425KB chunk eliminated)
- CSS reduced from 17KB to 10KB
- Added deploy-frontend.sh script for one-command build+deploy
- Updated CLAUDE.md with combat_stats, share_*, dungeon_map events
and React frontend architecture
Death alerts (frontend + backend):
- Frontend: DeathNotification component with red banner + sawtooth
sound when vitae goes from 0 to >0
- Backend: detects vitae transition in vitals handler, sends Discord
webhook to #aclog with "☠️ CHARACTER died! (vitae: X%)"
- Rate-limited: max 1 Discord alert per character per 5 minutes
Idle detection (backend):
- Background task runs every 60 seconds
- Detects: vt_state "default"/"idle" OR kph=0 while in combat/hunt
- Sends Discord webhook: "⚠️ CHARACTER appears idle (state: X, KPH: 0)"
- Auto-clears alert when character becomes active again
- No duplicate alerts for same idle period
Discord integration:
- DISCORD_ACLOG_WEBHOOK env var for webhook URL
- Used by both death alerts and idle detection
- Graceful fallback when not configured
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- v1 vanilla JS frontend moved to /classic (static/classic/)
- v2 React app now serves at / (root)
- Vite base changed from /v2/ to /
- Assets at /assets/, service worker at /sw.js
- /classic still works — all v1 files preserved with relative paths
- /v2 still works as before (build output unchanged)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1. React.memo on WindowRenderer — prevents re-renders when parent
state changes but no windows are affected
2. Coordinate display via direct DOM ref — no React state updates
on mouse move (was triggering re-renders on every pixel)
3. useDeferredValue for sidebar vitals + player list — React
prioritizes map interactions over stat text updates
4. Chat messages in ref — stores in useRef instead of useState,
only bumps a version counter for re-render. Eliminates a
new Map() allocation on every chat message.
5. Lazy-load 8 window components — InventoryWindow, CharacterWindow,
RadarWindow, CombatStatsWindow, IssuesWindow, VitalSharingWindow,
StatsWindow, CombatPickerWindow all loaded on first open.
Main bundle dropped from 278KB to 211KB (24% reduction).
6. Preload critical assets — dereth.png, backpack icon, dungeon_tiles.json
via <link rel="preload"> in index.html for instant map render.
7. Bundle splitting — React runtime extracted to separate 12KB chunk
(cached independently). Window components split into 8 chunks.
Total: 13 chunks vs previous 2.
8. Service worker — caches map images, icon sprites, and dungeon tiles.
Icon images cached on first fetch. Repeat page loads serve from
cache instantly. Auto-cleans old cache versions.
Net result:
- Initial load: 211KB main + 17KB CSS (was 278KB + 17KB)
- React cached separately: 12KB
- Windows load on demand: 1-15KB each
- Dashboard with Recharts: 425KB (unchanged, still lazy)
- Map images/icons: cached by service worker after first load
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
New modern dashboard at /v2 running alongside the existing UI at /.
Same backend, same APIs, same WebSocket — zero backend changes.
Stack: React 19 + Vite + TypeScript + Recharts
Source: frontend/ — build output: static/v2/
Phase 1 delivers:
- Character overview cards in a responsive CSS Grid
- Live HP/Stamina/Mana bars via WebSocket vitals
- Kills/hr, total kills, deaths, session uptime
- VTank state badge (Combat/Nav/Idle)
- Location coordinates
- Click to expand: combat stats, prismatic count, CPU/RAM
- Global stats header: active chars, total kills, total rares, server health
- WebSocket hook with auto-reconnect
- HTTP poll fallback for initial load + server health
- Mobile responsive (single column on narrow screens)
- Dark theme matching the MosswartOverlord palette
Build: cd frontend && npm run build
Access: /v2 (served by existing NoCacheStaticFiles mount)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>