Commit graph

73 commits

Author SHA1 Message Date
Erik
cc686da532 feat(midsummer): retire the theme out of season (holiday over)
Flip the seasonal master switch (SEASON_ACTIVE=false in useMidsummer) so the
Sma Grodorna theme is fully dormant — no rain/frogs/maypole/banner/palette,
regardless of any stored toggle preference — and remove the 🐸 toggle from the
sidebar. All theme code is kept; to bring it back next Midsummer, flip
SEASON_ACTIVE to true and re-add <FrogToggle /> in SidebarWindowButtons.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-24 08:12:00 +02:00
Erik
d86bc48862 feat(midsummer): rain of flowers/frogs/Swedish flags, dots become frogs, drop jingle
Per request: remove the WebAudio jingle (+ its 🔊 toggle and sound state);
replace the one-shot confetti with a continuous rain of 🌼🌸🐸🇸🇪🌿 over the
screen (MidsummerRain, gated by the theme, reduced-motion aware, leak-free);
and replace player-dot markers with frogs themselves (override the inline
dot color/border) instead of a flower-crown on top. Still toggled by the
🐸 Midsommar switch. Includes rebuilt static bundle.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-19 09:47:39 +02:00
Erik
1f86e7cc86 polish(midsummer): guard frog-hop against rapid re-click stacking
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-19 09:33:51 +02:00
Erik
3cd2165c15 feat(midsummer): WebAudio Sma grodorna jingle, plays once on first gesture 2026-06-19 09:30:00 +02:00
Erik
e896ef1f21 feat(midsummer): frog-hop easter egg replaces the rickroll 2026-06-19 09:29:07 +02:00
Erik
c4dd1b7ae7 feat(midsummer): glad midsommar banner + one-shot confetti 2026-06-19 09:28:34 +02:00
Erik
e7b0f11bb1 feat(midsummer): flower-crown dots, frog on selected 2026-06-19 09:27:40 +02:00
Erik
da0cc79def feat(midsummer): dancing maypole pinned to map centre 2026-06-19 09:27:26 +02:00
Erik
2fb6fd2f3e feat(midsummer): sidebar frog toggle + jingle toggle (sound stubbed) 2026-06-19 09:26:45 +02:00
Erik
580fd6fbc5 feat(midsummer): pond-green palette overlay for sidebar and map 2026-06-19 09:26:12 +02:00
Erik
568992d0f9 feat(midsummer): theme state provider + data-midsummer attribute 2026-06-19 09:25:54 +02:00
Erik
87e4f2ff62 fix(dashboard): table width:auto so Character column sizes to content only
With width:100%, the table stretched to fill the container — and the
Character column (the only one with stretchable text) absorbed all the
extra space, looking much wider than the longest name needed.

width:auto lets each column size to its content. Table now fits its
data; container still scrolls horizontally if content ever exceeds the
viewport.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-23 19:37:49 +02:00
Erik
5f43ddce93 feat(dashboard): click-to-highlight rows + character column auto-sizes
Two small UX improvements to the Player Dashboard table (works in both
the new-tab fullscreen page and the deprecated in-app window since they
share PlayerDashboardContent):

1. Row highlight: click anywhere on a row to highlight it (blue tint +
   thin outline). Click again to unhighlight. Single selection — useful
   for tracking one character down a long sorted list.

2. Character column no longer truncates: removed maxWidth/overflow/
   textOverflow on the name cell. Column now sizes to the longest
   character name (still no wrapping; container scrolls horizontally
   if names are extreme).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-23 19:35:09 +02:00
Erik
5bda2b64f4 feat(dashboard): open Player Dashboard in a new tab
The 👥 Dashboard button used to open the player table as a draggable
in-app window, which competed for screen space with the map. It now
opens in a separate browser tab as a fullscreen page so users can put
the dashboard on a second monitor.

How:
- App.tsx branches on ?view=dashboard → renders PlayerDashboardFullPage
  (new file in components/) instead of the default MapLayout.
- SidebarWindowButtons.tsx: 👥 Dashboard onClick now does
  window.open('/?view=dashboard', '_blank', 'noopener'). Label shows
  '↗' so users know it's an external open.
- PlayerDashboardWindow.tsx refactored: extracted the sortable table
  body into a reusable PlayerDashboardContent component. The old window
  shell stays registered in WindowRenderer for backward compat — just
  no longer reachable from the default sidebar.
- map-layout.css: new .ml-dashboard-page rules for fullscreen layout.

Each tab gets its own useLiveData + WebSocket connection (server
already handles multiple browser clients). The new tab inherits the
session cookie from the original tab — no re-login.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-23 19:31:26 +02:00
Erik
1c1c43d28b feat(dashboard): logout button + admin user-management window
Logout: new sidebar link 'Log out (username)' that POSTs /api/logout
(clears session cookie) and navigates to /login. Visible to everyone.
Replaces 'no logout functionality' state where users could only get
out by deleting cookies manually.

Admin window: new 'Admin · Users' window (only shown when current
user.is_admin) lists all users in a table with:
  - Add user (username + password + admin checkbox)
  - Reset password inline per row
  - Toggle admin per row
  - Delete user per row (blocked for self)
Wraps the existing /api-admin/users CRUD endpoints in main.py.

Plumbing: useCurrentUser hook fetches /me on mount; apiPatch+apiDelete
helpers added to api/client.ts; new endpoint wrappers exported from
api/endpoints.ts; AdminUsersWindow.tsx registered in WindowRenderer
under id prefix 'adminusers'; CSS for admin table/form/buttons and
the muted-red logout link.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-15 20:10:10 +02:00
Erik
79cf88d3f7 feat(agent): Phase 1 — chat-window AI assistant via Claude Code subprocess
Adds an in-dashboard AI assistant that answers questions about live game
state. Designed reactively (no background loops) — every user message in
the chat window or via /api/agent/ask runs one `claude -p` invocation.

Architecture:
- New host-side FastAPI service (agent/) on 127.0.0.1:8767, OUTSIDE the
  dereth-tracker Docker container because `claude` and ~/.claude
  credentials live on the host.
- nginx routes /api/agent/* to the host service.
- The same browser session cookie the tracker issues authenticates
  agent requests (shared SECRET_KEY).
- The agent shells out to `claude -p --session-id <uuid>` with
  cwd=/home/erik/MosswartOverlord. Sessions persist as JSONL on disk
  via Claude Code's built-in machinery.
- An MCP stdio server (agent/mcp_overlord.py) exposes tools to Claude:
  get_live_players, get_recent_rares, query_telemetry_db (read-only,
  parsed by sqlglot to reject DML/DDL), get_player_state, get_inventory,
  get_inventory_search, get_combat_stats, get_equipment_cantrips,
  get_quest_status, get_server_health, suitbuilder_search.
- Read-only PG role (overlord_agent_ro) is the second line of defense
  on the SQL tool — even a parser bypass can't mutate.

Frontend:
- AgentWindow.tsx — draggable chat window with localStorage-pinned
  session UUID, "New Chat" button, on-mount rehydration from
  /agent/sessions/{id}/history (parses Claude Code's JSONL).
- Wired into WindowRenderer + Sidebar (🤖 Assistant button).

Operational:
- systemd unit (overlord-agent.service) + install.sh.
- agent/README.md documents env vars, deploy flow, smoke tests.
- nginx/overlord.conf gets a new /api/agent/ location with 180s timeout.
- CLAUDE.md gets an "Overlord Assistant Mode" section briefing the
  agent on which tools to use and how to behave.

NOT YET DEPLOYED — server still needs:
1. Apply agent/sql/0001_overlord_agent_ro.sql + ALTER ROLE password
2. Add AGENT_DB_DSN to /home/erik/MosswartOverlord/.env
3. bash agent/install.sh (creates venv, installs unit, starts service)
4. sudo cp /home/erik/MosswartOverlord/nginx/overlord.conf to nginx + reload

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-25 20:43:59 +02:00
Erik
f7f04d6a84 Revert floating badge, remove debug logs
The floating version badge scrolled awkwardly and wasn't necessary
now that the bind-mount/deploy issue is fixed. The existing ml-version
inside the Sidebar is sufficient.

Also removed the temporary [INV_DEBUG] console logs from useLiveData
and InventoryWindow — the inventory live-update bug is confirmed fixed.
Kept the per-character inventoryVersions fix and the cache-buster on
the refetch URL.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 19:20:24 +02:00
Erik
a5ff228d4f Add floating version badge in top-left corner
Small yellow badge fixed at position (4, 4) showing the running build
version. Helps visually confirm which bundle a browser is loading when
diagnosing cache issues.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 19:14:43 +02:00
Erik
0ff396cd0e Add debug logging for inventory live-update tracing + cache-bust fetch
Temporary instrumentation to diagnose why InventoryWindow doesn't refresh
on inventory_delta. Three log points:
- useLiveData: logs when inventory_delta arrives and version bump
- InventoryWindow effect: logs every run with state
- InventoryWindow fetch: logs when debounce fires and result count

Also added cache-buster (_t=timestamp) to the refetch URL in case HTTP
caching is masking fresh data.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 19:05:51 +02:00
Erik
d26f1f725c Fix inventory window never refreshing live (per-character version)
The inventoryVersion counter in useLiveData was a single global value that
bumped on every inventory_delta for any character. With 60+ active chars
all generating deltas, the global counter advanced multiple times per
second.

InventoryWindow's debounce effect watched this global counter, so every
bump reset its 2-second fetch timer. Since bumps arrived faster than 2s,
the fetch never fired — the window appeared frozen until the user closed
and reopened it (which triggered the initial-fetch effect).

Fix: make inventoryVersions a Map<string, number> keyed by character name.
Each inventory_delta now only bumps its own character's counter, so an
open window's debounce correctly fires 2s after its character's last
delta, ignoring unrelated traffic.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 18:57:55 +02:00
Erik
475c7aba03 feat: harder shake then spin 🌀🎵
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 23:25:13 +02:00
Erik
30c4067c99 feat: easter egg 🎵
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 23:22:44 +02:00
Erik
adb9d5feab feat: major cleanup + death alerts + idle detection + Discord webhooks
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>
2026-04-14 16:32:14 +02:00
Erik
d2c30b610b fix(v2): character window now updates live from WebSocket
The CharacterWindow only fetched once from API on mount and never
updated. Now:
- character_stats WS messages are tracked in useLiveData via ref
- Passed through WindowRenderer to CharacterWindow as liveStats prop
- Window uses live WS data when available, falls back to API fetch
- Attributes, skills, vitals base values, properties (augmentations,
  ratings, etc.), allegiance all update in real-time

Also: vitals bars in the character window use live WS vitals data
(health_percentage etc.) for real-time HP/Stamina/Mana display.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 16:02:49 +02:00
Erik
a5bd659876 feat(v2): remove old dashboard, add vitae + resizable windows
- Removed old Recharts dashboard view entirely (no more viewMode
  toggle, DashboardView lazy import, Ctrl+D shortcut)
- Recharts chunk eliminated from build — bundle size reduced
- Player Dashboard window: added Vitae column (red when > 0%)
- ALL windows now resizable: drag bottom-right corner handle
  (min 300×200px). Subtle diagonal line grip indicator.
- Sidebar: removed 📊 Dashboard toggle link, removed broken
  /quest-status.html external link (replaced by 📜 Quests window)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 15:33:07 +02:00
Erik
938421999a feat(v2): Quest Status + Player Dashboard as React windows
Quest Status window (📜 Quests in sidebar):
- Fetches GET /quest-status API (polls every 30s)
- Grid: characters as rows × all unique quests as columns
- "READY" shown in green, countdowns in yellow, missing as dash
- Quest names shortened (removes "Timer", "Pickup" suffixes)
- Sticky header row, scrollable body
- Replaces broken quest-status.html link

Player Dashboard window (👥 Dashboard in sidebar):
- Sortable table of all online characters
- Columns: Character, State, KPH, Session kills, Total kills,
  Rares (total + session), Deaths, Uptime, HP%, Tapers
- Click column headers to sort (ascending/descending toggle)
- State badges: green=combat/hunt, red=other, gray=idle
- KPH in green, rares in gold, deaths in red (if > 0)
- HP% color-coded: green >80%, yellow >40%, red below

Sidebar changes:
- Removed broken /quest-status.html external link
- Added 👥 Dashboard + 📜 Quests as window opener buttons
- Both lazy-loaded (only fetched when first opened)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 14:02:00 +02:00
Erik
27caa21a56 style(v2): hide sidebar scrollbar between player column and map
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 13:58:09 +02:00
Erik
1a7300df37 style(v2): hide scrollbar on dashboard main area
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 13:56:25 +02:00
Erik
666af817a2 fix: add missing useRef import to InventoryWindow
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 13:54:51 +02:00
Erik
4638e60043 fix(v2): inventory no longer flickers — debounced re-fetch, no loading flash
inventory_delta WS messages were triggering immediate full re-fetch
with setLoading(true), causing content to flash blank. Now:
- Initial load shows loading state (once)
- Subsequent deltas debounced to 2s (batches rapid changes)
- Re-fetch runs silently without clearing existing items

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 13:54:06 +02:00
Erik
0112c59514 feat(v2): 13 improvements — functional, visual, UX, backend
Functional:
1. Chat: "▼ New messages below" indicator when scrolled up, click to jump
2. Combat stats: "Clear Session" button (red, with confirm dialog)
3. Inventory: live updates via inventory_delta WS (re-fetches on change)
4. Inventory: real mana time from equipment_cantrip_state WS (live
   countdown with state dot: green=active, red=inactive, yellow=unknown)

Visual:
5. Thin separator line between tool links and sort buttons
6. Selected player row highlighted with darker background (#2a3344)
7. Scroll-to-top button (▲) appears when scrolled past 200px in player list

UX:
8. Double-click player dot on map opens their chat window
9. Right-click player dot shows context menu (Chat/Stats/Inv/Char/Combat/Radar)
10. Ctrl+D keyboard shortcut toggles between map and dashboard views
11. Sound notification on rare drops (880Hz sine beep via Web Audio API)

Backend:
12. Deep-merge lifetime offense/defense per element — accumulates
    total_attacks, failed_attacks, crits, damage per AttackType×Element
    instead of overwriting with latest session data
13. Startup cleanup: deletes stale combat_stats records from before
    the lifetime fix (pre-2026-04-14T09:00Z)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 13:49:40 +02:00
Erik
0b64c6ccff feat(v2): chat command history + smart auto-scroll
Command history:
- Up/Down arrow keys browse sent command history (like bash/console)
- 50 commands stored per character in localStorage
- Persists across page reloads and browser sessions
- Current input preserved when browsing (restored on Down past end)
- Duplicates kept (matches user preference)

Smart auto-scroll:
- New messages only auto-scroll if user is already at the bottom
- If user has scrolled up to read history, it stays put
- Sending a message snaps back to bottom
- 30px threshold for "at bottom" detection

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 12:51:45 +02:00
Erik
8a2d0e1a72 style(v2): amber/yellow meta states now show red instead
Non-active, non-idle VTank states (nav, turn_in_quests, etc.) now
display in red instead of amber/yellow in both:
- Map sidebar: .ml-meta-pill.other (red background + text)
- Dashboard cards: .badge-other (red background + text)

Green = combat/hunt, Red = nav/other states, Gray = idle/default

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 12:36:23 +02:00
Erik
9f7686681b feat: v2 React frontend is now primary at /
- 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>
2026-04-14 12:17:23 +02:00
Erik
69678a9426 perf(v2): 8 optimizations — 24% smaller bundle, fewer re-renders
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>
2026-04-14 12:11:08 +02:00
Erik
19d95a370f chore: update tsconfig build cache — working baseline
All features functional: map view, sidebar, player dots/trails/heatmap/portals,
draggable windows (chat/stats/inventory/character/radar/combat/issues/vitals),
session+lifetime combat stats, 60-color palette, rare notifications, dungeon
radar, version display. Performance: code-split Recharts, direct DOM pan/zoom,
deferred player list, memoized derived data.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 12:06:50 +02:00
Erik
9611868266 fix(v2): remove nav links from dashboard header, move Map View button left
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 12:01:31 +02:00
Erik
1e125f7653 fix(v2): tool links open in new tab (target=_blank)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 11:57:00 +02:00
Erik
2cd68d0368 fix(v2): move Dashboard to tool links + fix Combat sidebar button
- Dashboard toggle moved from sidebar header to tool links area
  alongside Suitbuilder, Inv Search, Debug, Quests
- Combat sidebar button now opens a character picker window
  (combatpicker prefix) that lists all online characters — click
  one to open their full combat stats window

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 10:19:57 +02:00
Erik
2095f54d79 fix(v2): player zoom no longer locks the map
The zoom-to-player effect was re-triggering on every telemetry
update (every 2s) because selectedPlayer stayed set and players
array kept changing. Now tracks lastZoomedRef — zoom only fires
once per selection. Map is immediately free to pan/zoom after.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 10:07:19 +02:00
Erik
869507a3ef fix(v2): version display — top-left inside sidebar above header
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 13:32:25 +02:00
Erik
6b0b26c287 fix(v2): version display — correct URL path (/api/api-version)
apiFetch adds /api prefix, so /api-version became /api/api-version
which was wrong. Use raw fetch with correct path.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 13:30:04 +02:00
Erik
a59296867d fix(v2): player zoom + dot blink + version + sidebar links + dungeon radar + rare emojis
1. Player click → zoom: clicking a player in sidebar or on map dot
   zooms to their position at 3x zoom, centered on screen. Click
   again to deselect. Uses direct DOM transform (no React state).

2. Selected dot blink: selected player dot gets 10px size + blink
   animation (0.6s step-end infinite) matching v1's .dot.highlight.

3. Version display: fetches /api-version on mount, shows "vX.Y.Z"
   in small text positioned just right of sidebar (fixed, top: 6px).

4. Missing sidebar buttons: added Combat Stats (⚔️) alongside
   existing Issues (📋) and Vital Sharing (🤝) in SidebarWindowButtons.

5. Rare notification: added 🎆 emojis to "LEGENDARY RARE!" title
   matching v1's notification text.

6. Dungeon map in radar — verbatim port from v1 lines 3596-3930:
   - loadDungeonTiles(): fetches dungeon_tiles.json, processes each
     tile image (color remap: UB source colors → display colors,
     white → transparent, black → semi-transparent)
   - cellRotation(): maps rotation values to radians (v1's exact logic)
   - Dungeon rendering: sorts z_levels (current floor on top at 85%
     opacity, others at 12%), draws each cell with per-cell rotation,
     uses processed tile canvases or colored rectangle fallback
   - Requests dungeon map via WebSocket when radar detects dungeon
   - Caches dungeon maps on window.__dungeonMapCache
   - Overworld map: fixed srcSize calculation to use range * pixPerCoord

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 12:04:27 +02:00
Erik
76baec33e7 style(v2): hide player list scrollbar (still scrollable)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 23:38:08 +02:00
Erik
85dce15d8b perf(v2): comprehensive performance optimizations
1. Map pan/zoom via direct DOM mutation (bypass React state)
   - txRef stores {scale, offX, offY}, applyTransform() writes
     directly to groupRef.style.transform
   - Zero React re-renders during pan/zoom — smooth 60fps
   - Removed MapTransformContext dependency (dead code now)

2. Code-split Recharts via React.lazy()
   - DashboardView (with all Recharts components) is a separate chunk
   - Main bundle: 274KB (was 694KB — 60% reduction)
   - Dashboard chunk: 425KB (loaded only on demand)
   - Map view loads instantly without Recharts overhead

3. useDeferredValue for player list
   - Kill counters, KPH, rares in sidebar use deferred rendering
   - React prioritizes map interactions over stat text updates
   - Reduces unnecessary re-renders during WS message bursts

4. useMemo for derived data in MapLayout
   - players array and vitalsMap memoized on characters ref
   - Prevents child component re-renders when Map identity changes
     but content is the same

5. Removed MapTransformProvider wrapper (no longer needed)

Total impact: ~60% smaller initial load, ~10x fewer re-renders
during active WebSocket streaming, zero-latency pan/zoom.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 22:46:54 +02:00
Erik
851fc5f7cd fix(v2): issues board — full v1 feature parity
Now matches v1's Issues Board exactly:

- Category badges with v1's exact colors (Plugin=#8844cc, Overlord=#4488cc,
  Nav=#44aa44, Macro=#cc8844, Other=#888888)
- Author name + date per issue
- Action buttons:
  - Unresolved: ✓ Resolve + ✎ Edit
  - Resolved: ↻ Reopen + 🗑 Delete (with confirm dialog) + ✎ Edit
- Inline edit form: editable title + category dropdown + description
  textarea + Save/Cancel buttons (toggle with ✎ Edit click)
- Comments section per issue: always visible inline
  - Comment list with author (blue), date, text
  - Add comment input with Post button (Enter key supported)
  - "No comments yet" placeholder
- Add issue form at bottom: title input + category select + description
  textarea + Add button
- Resolved issues dimmed to 55% opacity, sorted below unresolved
- All API calls use /api prefix with credentials

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 22:41:13 +02:00
Erik
b00c386d77 fix(v2): character window — verbatim property ID maps from v1
Property ID maps were wrong (made-up IDs 360-390). Now uses the
exact same IDs as v1 script.js lines 1843-1876:

- TS_AUGMENTATIONS: IDs 218-328 (30 augmentations)
- TS_AURAS: IDs 333-365 (11 luminance auras)
- TS_RATINGS: IDs 370-379 (8 ratings)
- TS_SOCIETY: IDs 287-289 (3 societies)
- TS_MASTERIES: IDs 354-362 with TS_MASTERY_NAMES lookup
- TS_GENERAL: IDs 181-390 (chess, fishing, total augs, aetheria, enlightenment)
- societyRank() function matching v1's _tsSocietyRank()

Other tab now shows General + Masteries + Society sections (was
only showing allegiance). Each section has its own header matching
v1's ts-section-title styling.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 22:35:30 +02:00
Erik
bd8ad863d1 fix(v2): pack capacity from enhanced_properties.ItemSlots_Decal
The inventory service doesn't return items_capacity directly — it's
in enhanced_properties.ItemSlots_Decal. Updated normalizer to read
from there. Also defaults to 24 (standard AC pack size) with ||
instead of ?? to catch 0/undefined/null. Removed debug console.logs.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 22:29:46 +02:00
Erik
8e77274316 fix(v2): pack fill — count children directly from items array
Instead of relying on the packItems Map (which may have key matching
issues), count pack children directly by filtering the normalized
items array for items whose container_id matches the pack's item_id.
Also removed debug console.log spam from WindowRenderer.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 22:26:57 +02:00
Erik
f9ae3d6b96 debug(v2): log WindowRenderer to check if windows mount
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 22:23:38 +02:00