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>
1 line
810 B
JavaScript
1 line
810 B
JavaScript
import{u as c,j as r,D as d}from"./index-BfJ04YaG.js";import"./react-DlyoauG8.js";const p=({id:n,zIndex:i,characters:a})=>{const{openWindow:s}=c(),e=Array.from(a.keys()).sort();return r.jsx(d,{id:n,title:"Combat Stats — Select Character",zIndex:i,width:300,height:400,children:r.jsx("div",{style:{flex:1,overflowY:"auto",padding:6},children:e.length===0?r.jsx("div",{style:{padding:12,color:"#666",textAlign:"center",fontSize:"0.8rem"},children:"No characters online"}):e.map(o=>r.jsx("div",{style:{padding:"5px 8px",cursor:"pointer",borderBottom:"1px solid #222",color:"#ccc",fontSize:"0.82rem"},onMouseEnter:t=>t.currentTarget.style.background="#2a2a2a",onMouseLeave:t=>t.currentTarget.style.background="",onClick:()=>s(`combat-${o}`,`Combat: ${o}`,o),children:o},o))})})};export{p as CombatPickerWindow};
|