fix(v2): all reported issues — missing windows, broken features, layouts
Missing features (now added): 1. Vital Sharing window — polls /vital-sharing/peers, shows peer vitals with HP/STA/MANA bars, connection status, position, tags 2. Combat Stats window — full Mag-Tools style with monster list (left), damage breakdown grid (right), session/lifetime toggle, element matrix 3. Issues Board window — CRUD with categories, resolve/reopen, comments 4. Quest Status — links to /quest-status.html (separate page like v1) 5. Sidebar: added Issues + Vitals buttons, Quest link, Combat button per player row (6 buttons now: Chat/Stats/Inv/Char/Combat/Radar) Fixed functionality: 6. Radar — fixed command to "start_radar"/"stop_radar" (was wrong path) 7. Character window — redesigned with v1-style tabbed layout: Left tabs: Attributes (vitals bars + attribute grid) | Skills (specialized/trained grouped) | Titles Right tabs: Augs | Ratings | Other (allegiance) Header: level, race, gender, XP, luminance, deaths, skill credits 8. Stats window — proper Grafana iframe grid (4 panels 2x2) with time range selector (1H/6H/24H/7D) Color palette: expanded to 60 distinct colors (was 30) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
b77450b6eb
commit
52e1bcd6b8
12 changed files with 710 additions and 226 deletions
|
|
@ -1,52 +1,65 @@
|
|||
import React, { useEffect } from 'react';
|
||||
import { DraggableWindow } from './DraggableWindow';
|
||||
|
||||
interface NearbyObject {
|
||||
id: number;
|
||||
name: string;
|
||||
type: string;
|
||||
distance: number;
|
||||
bearing?: number;
|
||||
}
|
||||
|
||||
interface Props {
|
||||
id: string;
|
||||
charName: string;
|
||||
zIndex: number;
|
||||
socket: WebSocket | null;
|
||||
nearbyObjects: any[];
|
||||
nearbyObjects: NearbyObject[];
|
||||
}
|
||||
|
||||
export const RadarWindow: React.FC<Props> = ({ id, charName, zIndex, socket, nearbyObjects }) => {
|
||||
// Send start_radar when window opens, stop_radar when it closes
|
||||
// Send start_radar when window opens, stop_radar on close
|
||||
useEffect(() => {
|
||||
if (socket && socket.readyState === WebSocket.OPEN) {
|
||||
socket.send(JSON.stringify({ player_name: charName, command: '/mm radar start' }));
|
||||
socket.send(JSON.stringify({ player_name: charName, command: 'start_radar' }));
|
||||
}
|
||||
return () => {
|
||||
if (socket && socket.readyState === WebSocket.OPEN) {
|
||||
socket.send(JSON.stringify({ player_name: charName, command: '/mm radar stop' }));
|
||||
socket.send(JSON.stringify({ player_name: charName, command: 'stop_radar' }));
|
||||
}
|
||||
};
|
||||
}, [charName, socket]);
|
||||
|
||||
const objects = nearbyObjects || [];
|
||||
const sorted = [...objects].sort((a, b) => (a.distance ?? 999) - (b.distance ?? 999));
|
||||
|
||||
return (
|
||||
<DraggableWindow id={id} title={`Radar: ${charName}`} zIndex={zIndex} width={450} height={400}>
|
||||
<DraggableWindow id={id} title={`Radar: ${charName}`} zIndex={zIndex} width={480} height={420}>
|
||||
<div style={{ padding: '4px 8px', fontSize: '0.7rem', color: '#888', borderBottom: '1px solid #333' }}>
|
||||
Range: ~120m · {objects.length} objects nearby
|
||||
</div>
|
||||
<div style={{ flex: 1, overflowY: 'auto', fontSize: '0.73rem' }}>
|
||||
{objects.length === 0 ? (
|
||||
<div style={{ padding: 16, color: '#666', textAlign: 'center' }}>
|
||||
Waiting for nearby objects data...<br/>
|
||||
<span style={{ fontSize: '0.65rem' }}>The plugin will start sending radar data shortly.</span>
|
||||
<div style={{ padding: 20, color: '#555', textAlign: 'center' }}>
|
||||
Waiting for radar data from plugin...
|
||||
</div>
|
||||
) : (
|
||||
<table style={{ width: '100%', borderCollapse: 'collapse' }}>
|
||||
<thead>
|
||||
<tr style={{ borderBottom: '1px solid #444', color: '#888', fontSize: '0.65rem' }}>
|
||||
<th style={{ textAlign: 'left', padding: '3px 6px' }}>Name</th>
|
||||
<th style={{ textAlign: 'left', padding: '3px 4px' }}>Type</th>
|
||||
<th style={{ textAlign: 'right', padding: '3px 4px' }}>Dist</th>
|
||||
<tr style={{ borderBottom: '1px solid #444', color: '#777', fontSize: '0.65rem', textTransform: 'uppercase' }}>
|
||||
<th style={{ textAlign: 'left', padding: '4px 6px' }}>Name</th>
|
||||
<th style={{ textAlign: 'left', padding: '4px 4px' }}>Type</th>
|
||||
<th style={{ textAlign: 'right', padding: '4px 6px' }}>Distance</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{objects.map((obj: any, i: number) => (
|
||||
<tr key={i} style={{ borderBottom: '1px solid #1a1a1a', color: '#ccc' }}>
|
||||
<td style={{ padding: '2px 6px' }}>{obj.name}</td>
|
||||
<td style={{ padding: '2px 4px', color: '#888' }}>{obj.type || obj.object_class || ''}</td>
|
||||
<td style={{ textAlign: 'right', padding: '2px 4px' }}>{obj.distance ? `${Math.round(obj.distance)}m` : ''}</td>
|
||||
{sorted.map((obj, i) => (
|
||||
<tr key={obj.id ?? i} style={{ borderBottom: '1px solid #1a1a1a', color: '#ccc' }}>
|
||||
<td style={{ padding: '3px 6px', fontWeight: 500 }}>{obj.name}</td>
|
||||
<td style={{ padding: '3px 4px', color: '#888', fontSize: '0.68rem' }}>{obj.type || ''}</td>
|
||||
<td style={{ textAlign: 'right', padding: '3px 6px', fontVariantNumeric: 'tabular-nums' }}>
|
||||
{obj.distance != null ? `${Math.round(obj.distance)}m` : ''}
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue