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>
Backend: new weapon_type query parameter on /search/items.
Uses skill ID from IntValues[218103840] for melee types (Heavy=44,
Light=45, Finesse=46, TwoHanded=41) and name matching for missile
sub-types (bow, crossbow, thrown). Caster = ObjectClass 31.
Frontend: dropdown appears when "Weapons" radio selected, hidden otherwise.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add Weapons and Clothing equipment type radio options
- Add Weapon/Combat Stats filter card: damage, attack bonus, crit resist, tinks
- Add Item Properties filter card: material, level req, workmanship, value, burden
- Add Item State filter card: spell text search, bonded, attuned, rare checkboxes
- Add Status column (Equipped/Inventory) to results table
- Remove dead Slot View button and section (no JS handlers existed)
- Update clearAllFields() for all new inputs
- All changes frontend-only — suitbuilder and backend API untouched
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Two issues caused objects to float relative to dungeon map:
1. rotAngle used (heading + PI) but canvas uses (PI - heading)
- these differ: sin(x+PI) = -sin(x) vs sin(PI-x) = sin(x)
2. Object dy was negated, but 180° heading offset already handles N/S
- double N/S flip caused objects to drift with heading changes
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
UB applies HeadingToQuaternion(heading - 180) for dungeon rendering.
The layer has Y increasing downward, and the 180° offset flips N/S.
Reverted coordinates to Version 1 (X mirror, Y direct) which had
correct tile connections, and added the 180° heading offset that UB
uses to fix N/S orientation.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Version 1 (X mirror, Y direct) had correct tile connections but N/S
flipped. Previous attempts to fix N/S also changed rotation handling
which broke tiles. This commit reverts tile processing (no flip),
reverts rotation (exact UB values), and only adds Y negation to
Version 1's working coordinates. No other changes.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
UB's tiles were authored for a mirrored-X rendering space. Instead of
mirroring coordinates (which breaks positioning), flip each tile image
horizontally during pre-processing and negate the cell rotation angles
to compensate. Cell and object positions remain direct (no mirroring).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
UB mirrors both cell positions and player position within layer space,
so relative offset is direct (mirrors cancel out). Removed X negation.
Added float tolerance for rotation quaternion W component comparison
(was using strict equality which fails on floating point values).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Negate Y delta for dungeon tiles and objects so north points up
(AC Y+ = north, canvas Y+ = down)
- Wrap early COM calls in try/catch to prevent RPC_E_SERVERFAULT
during state transitions (portal loading, etc.)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
UB uses mirrored X: x = -(cell.X - playerX), direct Y: y = cell.Y - playerY.
Applied same transform to tile rendering, object positioning, and
entity list distance calculations in dungeon mode.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Process each tile through canvas pixel manipulation:
- White pixels → transparent (same as UB's MakeTransparent)
- Remap UB's 5 source colors (walls, inner walls, ramps, floors, stairs)
to readable display colors on dark background
- Black outlines made semi-transparent
- Current floor at 85% opacity, other floors at 12%
- Non-current floors drawn first, current floor on top
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
361 of 614 tile files have .bmp extension but are actually PNG.
Now detects magic bytes and sets correct MIME type per tile.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Extract 614 UB tile BMPs into dungeon_tiles.json (287KB base64 bundle).
Frontend loads tiles once, then draws them rotated per-cell using
environment IDs. Falls back to colored rectangles if tiles not loaded.
Current floor at 70% opacity, other floors at 15%.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Backend: dungeon_map event handler with permanent in-memory cache
by landblock ID, request_dungeon_map for late-joining browsers
- Frontend: render dungeon cells as colored rectangles when in dungeon,
multi-level Z support (current floor bright, others dimmed),
automatic overworld/dungeon switching based on is_dungeon flag,
raw physics coordinate positioning for dungeon objects
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Draws a rotated/scaled slice of the overworld map behind radar dots.
Map clips to the radar circle, rotates with heading-up mode, and
renders at 40% opacity so dots remain visible on top.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace dereth.png with Thwargle's highres_colorCorrect.png (2041x2041).
Same coordinate bounds (-102.1 to 102.1), dimensions read dynamically.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Replace range dropdown with smooth scroll-wheel zoom on canvas
- Click dot on canvas to select it (white ring + name label)
- Click row in entity list to select on canvas
- Click again to deselect
- Selected row highlighted with blue accent in list
- Auto-scrolls list to keep selected row visible
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
AC heading is standard clockwise (0=N, 90=E). Both compass labels
and object rotation need positive heading — previous negation broke
the compass while fixing objects. Now both use the same sign.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
AC's Actions.Heading uses opposite rotation from standard math
angles (confirmed by UtilityBelt's HeadingToQuaternion negation).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Compass is now always heading-up: player facing direction is fixed
pointing up, N/E/S/W labels rotate around the edge as player turns
- Fix: reopening a closed radar window now resends start_radar command
so streaming resumes without needing a full page refresh
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Browser can open a radar window per character that streams nearby
monsters, players, NPCs, portals, and other objects in real-time.
On-demand activation via start_radar/stop_radar commands through
the existing WebSocket command channel.
- Backend: nearby_objects event handler with in-memory cache and broadcast
- Frontend: canvas mini-map + entity list table in draggable window
- Radar button added to player list alongside Chat/Stats/Inventory/Char
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>