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>
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>
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>
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>
Increased bar width to 7px with #222 background and #666 border for
better contrast. Added tooltip showing "X% full" on hover. Minimum
2px fill height when non-empty so even nearly-empty packs show a sliver.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The pack fill bars were always empty because items weren't mapping
to container key 0 (main backpack). The bodyContainerId detection
failed when the inventory service doesn't include container_id on
wielded items. Now falls back to using the largest non-container
item group as the main backpack if key 0 is empty.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1. Pack capacity bars: gave the fill bar div explicit height (30px)
instead of relying on alignItems:stretch which produced 0 height.
Bar now visibly fills green/orange/red beside each pack icon.
2. Mana panel: added 20px item icons back to each mana row, between
the status dot and item name. Uses the same ItemIcon component
with 3-layer compositing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replaced the cramped 3-column CSS Grid mana layout with simple
flexbox rows matching the player sidebar style:
- Status dot (green/red) + item name + mana current/max + time remaining
- Font sizes use rem units (0.65-0.72rem) matching sidebar buttons/stats
- tabular-nums for aligned numbers
- Time column has min-width so it doesn't get clipped
- No more horizontal scrolling or cut-off text
- Empty state message when no mana items equipped
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1. Item normalization: normalizeItem() handles ALL formats:
- Inventory service (snake_case): current_wielded_location, object_class
- Plugin raw (PascalCase): CurrentWieldedLocation, ObjectClass
- Plugin IntValues: IntValues['10'] for wielded, ['5'] for burden
- Sentinel filtering: -1 values properly excluded
2. Equipment slots: armor (object_class=2) fills ALL matching slots.
Non-armor uses exact mask match first, then first bit overlap.
Body container ID detected to separate worn from pack items.
3. Slot colors: per-slot-type backgrounds matching v1:
purple (#3a2555) for jewelry, blue (#1e2e55) for armor,
teal (#1e3e3e) for clothing, dark blue (#142040) for weapons
4. Burden: fetches /character-stats/{name} for burden_units and
encumbrance_capacity. Shows percentage when available, raw burden
otherwise. Bar fills 0-200% mapped to 0-100% height with
green/orange/red thresholds.
5. Mana panel: shows equipped items with current/max mana + estimated
time remaining. State dot green/red. Sorted by mana ascending.
6. Fonts: switched to system font stack (-apple-system etc.) instead
of Palatino for crisp rendering.
7. Tooltip: proper system font, larger text (13px), structured sections
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1. Equipment slots: armor (object_class=2) now renders in ALL matching
slots via bitmask, not just the first. E.g. a chest piece covering
upper arm + chest + abdomen appears in all 3 slots. Non-armor items
still use first-match. Matches v1's exact logic.
2. Pack fill bars: changed from horizontal-below to vertical-right of
each pack icon. 4px wide bar with fill from bottom, color-coded:
green <70%, orange 70-90%, red >90%.
3. Burden: removed garbled percentage (was dividing by 10). Now shows
"Burden" label with total burden in tooltip. Bar shows 50% as
placeholder until character_stats provides encumbrance_capacity.
4. PackIcon component: reusable for main backpack + sub-packs, shows
game icon + vertical fill bar + green active glow + gold ▶ arrow.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Rebuilt inventory window to match v1 pixel-for-pixel:
Left column (316px):
- Equipment grid: 6×7 slots at 44px spacing, beveled 3D borders,
cyan glow (#00ffff) when equipped, faded ghost icon when empty
- Item grid: 6-column CSS Grid with purple gradient cells,
minimum 24 empty cells to fill grid
Center sidebar (38px):
- Burden bar: 14×40px vertical bar, green/orange/red thresholds,
percentage label, tooltip with burden units
- Pack icons: 32×32px with actual game icon images (not emoji)
- Active pack: green border + glow + gold ▶ arrow indicator
- Fill indicator: 4px green bar below each pack showing capacity %
- Main backpack (icon 0600127E) + sub-packs with actual container icons
Right panel (flex):
- Mana panel: header + equipped items with mana tracking
- Per-item: 16px icon, name, mana state dot (green/red),
current/max mana values in v1's grid layout
Hover tooltip:
- Follows mouse cursor (fixed position)
- Shows: name (gold), value, burden, material (green), armor level,
max damage, damage range/type, attack/defense bonuses as %,
skill requirements (orange), imbue, set, tinks, workmanship,
ratings, spellcraft, mana, spell list (blue)
- Black semi-transparent background matching v1's inventory-tooltip
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Character Window — now matches v1 exactly:
- Navy blue background (#000022) with gold/bronze borders (#af7a30)
- Two side-by-side 320px tab containers
- Left tabs: Attributes (vital bars with gold borders + attribute
table with green/blue cell backgrounds + vitals base + skill
credits) | Skills (specialized=purple gradient, trained=teal
gradient, grouped and sorted) | Titles
- Right tabs: Augmentations (with auras section) | Ratings | Other
(allegiance with followers)
- Active tab: green tint background with gold top/side borders
- Header: large name + level (gold, right-floated) + race/gender
- XP grid: total, unassigned, luminance earned/total, deaths
- Live vital bars from WebSocket vitals data
- Augmentation/aura/rating property ID maps from v1
Radar — passes full radarData message (not just objects array)
so canvas can render map background + entity positions properly
WindowRenderer — passes live vitals to CharacterWindow
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Radar — now pixel-accurate reproduction of v1:
- 300×300 canvas with dark circular background
- Semi-transparent dereth.png map overlay (heading-rotated)
- 4 range rings + crosshair lines
- Compass labels (N=red, E/S/W=gray) rotating with heading
- Facing direction indicator line
- Entity dots color-coded by type (Monster=red, Player=blue,
NPC=green, Portal=purple, Corpse=orange, Container=yellow)
- Player dot: gold center with white border
- Heading-up rotation for all entity positions
- Click to select entity (white selection ring)
- Scroll to zoom (0.02-5.0 AC units range)
- Entity list with color dot, name, type, distance, compass direction
- Selected entity highlighted with blue left border
Inventory — v1-style icon composites + slot styling:
- 3-layer icon composite: underlay → base → overlay images
using portal.dat offset formula + icon_overlay_id/IntValues
- Equipment slots: 3D beveled border + cyan glow when equipped
(matching v1's outset border + #00ffff shadow)
- Pack item cells: purple gradient background (v1's #3d007a)
- Proper 36×36px icon rendering with pixelated scaling
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Radar:
- nearby_objects WebSocket messages now tracked in useLiveData state
- Passed through MapLayout → WindowRenderer → RadarWindow
- Objects list updates live as radar data streams in
Inventory:
- Items now render actual game icons via /icons/{hexId}.png
using the portal.dat offset formula (iconRaw + 0x06000000)
- Hover tooltip shows: name, material, AL, damage, workmanship,
tinks, set, imbue (multi-line)
- Equipment grid slots show item icons instead of text names
- Pack item grid shows item icons with proper tooltips
- Fallback icon (06000133.png) on load error
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Primary buttons: soft blue translucent background with blue text,
glows slightly on hover. Secondary buttons: dark subtle with gray
text. Cleaner, more modern feel vs the old solid #88f blocks.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Stats now use a 3-column CSS Grid so values align across rows
- Fixed icons: ☠️ for deaths (was 💀), prismatic-taper-icon.png
for tapers (was wrong emoji 🔮), 🕐 for time (was 🕑)
- Added action buttons row (Chat, Stats, Inv, Char, Radar) matching
v1's button bar — accent-colored for primary actions
- Buttons are present but not wired to windows yet (Phase 3)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Stats were bare numbers with no visual hint what they represent.
Now each stat has the same emoji prefix as v1:
⚔️ session kills, 🏆 total kills, KPH suffix
💎 rares (session/total), 📊 KPR suffix
🕑 online time, 💀 deaths, 🔮 prismatic tapers
Meta state pill still color-coded (green=active, gray=idle).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Rebuilds the v1 map-centric experience in React:
Layout:
- 400px sidebar on left, interactive map on right (flex, 100vh)
- Exact same proportions and dark theme as v1
Sidebar (top→bottom):
- Header with active player count + Dashboard toggle button
- Server status dot (Coldeve online/offline with pulse)
- Aggregate counters: Rares (gold), Server KPH (blue glow), Kills (red)
- 6 sort buttons (Name, KPH, S.Kills, S.Rares, T.Kills, KPR)
- Player name filter
- Scrollable player list with per-row:
- Name + coordinates
- HP/Stamina/Mana vital bars (red/orange/blue gradients)
- Session kills, total kills, KPH
- Session rares, total rares, VTank meta state pill
- Online time, deaths, prismatic tapers
- Color-coded left border per player
Map:
- dereth.png with CSS transform pan (drag) + zoom (wheel, 1.1x factor, max 20x)
- Player dots (6px circles, color-matched to sidebar)
- Hover tooltip (name, coords, kph, kills)
- World coordinate display at cursor position
- Fit-to-window on first load
View toggle: Map View ↔ Dashboard with localStorage persistence.
All v1 CSS ported under ml-* prefix, scoped via map-layout.css.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Below the character cards grid, adds four tabbed analytics sections:
Combat Tab (Recharts):
- Kills per hour horizontal bar chart (all characters, sorted)
- Total damage session bar chart
- Damage by element pie chart (aggregated across all characters)
Rares Tab:
- Summary cards: total rares, total kills, drop rate (1 in N)
- Recent rare drops timeline (from WebSocket events)
- Rares per character lifetime bar chart
Map Tab:
- Dereth map (dereth_highres.png) with SVG overlay
- Character position dots (green=hunting, yellow=other)
- Hover to see character name + coordinates
- Responsive, maintains aspect ratio
Inventory Tab:
- Cross-character item search with debounced input
- Results table: character, item, type, material, set, workmanship
- Powered by existing /search/items API
All tabs lazy-rendered (only active tab mounts). Horizontal scroll
tab bar on mobile. Dark theme consistent with cards.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
vt_state values from plugins include "Hunt", "combat", "Default",
"turn_in_quests" etc. Previously only "combat" showed as green badge,
everything else was "Idle". Now Hunt shows green, unknown states show
their actual name.
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>
The element breakdown grid previously only showed damage RECEIVED
(defense) in the Mel/Msl and Magic columns, which was mostly empty
for characters who evade/resist everything. Now shows both:
- Given M/M + Given Mag: damage dealt by element (offense)
- Recv M/M + Recv Mag: damage taken by element (defense)
This makes the element breakdown immediately useful — you can see
that you're dealing Slash damage via melee, for example.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Peers who unsubscribed or disconnected from vital sharing were lingering
forever in the Vital Sharing browser window because nothing ever deleted
them from the server-side state or told the browser to drop them.
Backend:
- share_unsubscribe now pops the character from _vital_sharing_peer_state
(not just flips connected=false) and broadcasts a share_peer_removed
envelope to browser clients.
- On real plugin disconnect, do the same: pop the state entry and
broadcast share_peer_removed so the NetworkUI updates immediately.
Frontend:
- New removeVitalSharingPeer(name) deletes from the local
vitalSharingPeers dict and re-renders.
- socket.onmessage now routes share_peer_removed to it.
- refreshVitalSharingPeers() reconciles against the server's list and
prunes any local entries the server no longer knows about, catching
any race where the broadcast was missed.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Previously the NetworkUI window used #c44/#4c4/#46c which rendered
stamina as green. Updated to the gradients used in .vital-fill.* in
style.css (#ff4444, #ffaa00, #4488ff) so the network UI matches the
player sidebar.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Accepts new share_subscribe / share_unsubscribe / share_* WebSocket
messages from MM plugin clients and fans them out to other opted-in
plugin clients (excluding origin) and to browser clients for the
NetworkUI window.
- main.py: _vital_sharing_subscribers set, _vital_sharing_peer_state
snapshot, _broadcast_share_to_plugin_clients relay, disconnect
cleanup, GET /vital-sharing/peers endpoint.
- static/index.html: new sidebar link for Vital Sharing window.
- static/script.js: showVitalSharingWindow with live HP/STA/MANA bars,
per-peer status dot/tags/position, 5s /vital-sharing/peers poll, and
share_* routing through the existing browser WebSocket handler.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace Nginx basic auth with proper user accounts:
- Session cookies via itsdangerous (30-day expiry, httponly, secure)
- Password hashing with bcrypt via passlib
- Login page with AC-themed UI
- Admin page for user management (CRUD)
- AuthMiddleware exempts plugin WS and browser WS endpoints
- Issues/comments author auto-populated from session
- Sidebar shows logged-in username, admin link, and logout
- Seed users: erik (admin), alex, lundberg
- SECRET_KEY env var for cookie signing
- New PATCH /issues/{id} endpoint to toggle resolved flag
- Add resolved:false to new issues
- Frontend: click "✓ Resolve" marks issue green with strikethrough
- Resolved issues show "↺ Reopen" and "🗑 Delete" buttons
- Delete requires confirmation
- Sort: unresolved first, then resolved
- Issues persist until explicitly deleted
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add buttons as 6th grid row instead of extra element below grid
- Reduce stat pill padding and font size (4px→2px, 0.75rem→0.7rem)
- Reduce grid gap (6px→4px row, 12px→8px col) and item padding
- Unify all 5 button styles (Chat/Stats/Inventory/Char/Radar) with
shared compact sizing, individual colors preserved
- flex-wrap on buttons container as safety for narrow sidebars
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Version: CalVer + git hash shown in top-right corner of main page.
Built via Docker ARG BUILD_VERSION at build time. Served via /api-version.
Issues Board: shared notepad window for tracking issues with plugin,
overlord, nav files, macros. Stored in openissues.json on server.
- GET/POST/DELETE /issues endpoints
- Draggable window matching Chat/Radar pattern
- Category tags (plugin, overlord, nav, macro, other) with colors
- Add/resolve issues through the UI
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Extract spell effect mappings from Dictionaries.cs into spell_effects.json.
During item ingestion, compute_base_values() reverses active enchantment
effects to get true base stats:
- base_armor_level: armor without Impenetrability buffs
- base_max_damage: damage without Blood Drinker buffs
- base_attack_bonus: attack without Heart Seeker buffs
- base_melee_defense_bonus: defense without Defender buffs
- base_elemental_damage_vs_monsters: elemental without Spirit Drinker
- base_mana_conversion_bonus: mana conv without Hermetic Link
New columns in ItemCombatStats, exposed in search CTEs.
Frontend: Base Armor and Base Dmg columns (hidden by default, toggle on).
Requires ALTER TABLE migration before deploy.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sort listener was on .th-label span only, making clickable area too
small. Moved back to full .sortable th element. The × hide button
already uses stopPropagation so it won't trigger sort.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Some unidentified items have IntValues[218103835] = 67108882 which is
a bitmask/flags value, not weapon speed. Cap at 100 in extraction and
filter >100 in frontend display. AC weapon speed is typically 0-50.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace separate checkbox bar with inline × buttons in each column
header. Click × to hide a column. Hidden columns appear in a bar
above the table with + buttons to restore them. Sort by clicking
the column label text. Persisted to localStorage.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Unclosed string on line 570 prevented entire inventory.js from loading,
breaking character list and all search functionality.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Backend: add weapon_time and melee_defense_bonus to search CTE.
Frontend: show Speed, Attack Bonus (+%), Melee Def (+%) columns
visible by default. Material/Workmanship hidden by default.
Attack bonus and melee defense shown as percentage offset from 1.0.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
New columns: Max Damage, Attack Bonus, Material, Workmanship.
All columns now driven by RESULT_COLUMNS config array.
Column visibility toggles bar above results — checkboxes to
show/hide any column, persisted to localStorage.
Coverage column hidden by default.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>