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>
This commit is contained in:
parent
27caa21a56
commit
938421999a
31 changed files with 345 additions and 21 deletions
|
|
@ -85,7 +85,6 @@ export const Sidebar: React.FC<Props> = ({
|
|||
<a href="/inventory.html" target="_blank" className="ml-tool-link">🔍 Inv Search</a>
|
||||
<a href="/suitbuilder.html" target="_blank" className="ml-tool-link">🛡️ Suitbuilder</a>
|
||||
<a href="/debug.html" target="_blank" className="ml-tool-link">🐛 Debug</a>
|
||||
<a href="/quest-status.html" target="_blank" className="ml-tool-link">📜 Quests</a>
|
||||
</div>
|
||||
<SidebarWindowButtons />
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,10 @@ export const SidebarWindowButtons: React.FC = () => {
|
|||
|
||||
return (
|
||||
<div className="ml-tool-links">
|
||||
<span className="ml-tool-link" style={{ cursor: 'pointer' }}
|
||||
onClick={() => openWindow('playerdash', 'Player Dashboard')}>👥 Dashboard</span>
|
||||
<span className="ml-tool-link" style={{ cursor: 'pointer' }}
|
||||
onClick={() => openWindow('queststatus', 'Quest Status')}>📜 Quests</span>
|
||||
<span className="ml-tool-link" style={{ cursor: 'pointer' }}
|
||||
onClick={() => openWindow('issues', 'Issues Board')}>📋 Issues</span>
|
||||
<span className="ml-tool-link" style={{ cursor: 'pointer' }}
|
||||
|
|
|
|||
113
frontend/src/components/windows/PlayerDashboardWindow.tsx
Normal file
113
frontend/src/components/windows/PlayerDashboardWindow.tsx
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
import React, { useState, useMemo } from 'react';
|
||||
import { DraggableWindow } from './DraggableWindow';
|
||||
import type { CharacterState } from '../../types';
|
||||
|
||||
interface Props { id: string; zIndex: number; characters: Map<string, CharacterState>; }
|
||||
|
||||
type SortCol = 'name' | 'kills' | 'kph' | 'rares' | 'deaths' | 'uptime' | 'state';
|
||||
|
||||
export const PlayerDashboardWindow: React.FC<Props> = ({ id, zIndex, characters }) => {
|
||||
const [sortCol, setSortCol] = useState<SortCol>('kph');
|
||||
const [sortAsc, setSortAsc] = useState(false);
|
||||
|
||||
const players = useMemo(() => {
|
||||
const list = Array.from(characters.values()).filter(c => c.telemetry).map(c => {
|
||||
const t = c.telemetry!;
|
||||
return {
|
||||
name: c.name,
|
||||
kills: t.kills ?? 0,
|
||||
kph: parseInt(t.kills_per_hour) || 0,
|
||||
totalKills: t.total_kills ?? 0,
|
||||
rares: t.total_rares ?? 0,
|
||||
sessionRares: t.session_rares ?? 0,
|
||||
deaths: parseInt(t.deaths as string) || 0,
|
||||
totalDeaths: parseInt(t.total_deaths as string) || 0,
|
||||
uptime: t.onlinetime?.replace(/^00\./, '') ?? '',
|
||||
state: t.vt_state ?? 'idle',
|
||||
tapers: parseInt(t.prismatic_taper_count as string) || 0,
|
||||
hp: c.vitals?.health_percentage ?? 0,
|
||||
};
|
||||
});
|
||||
|
||||
list.sort((a, b) => {
|
||||
let cmp = 0;
|
||||
switch (sortCol) {
|
||||
case 'name': cmp = a.name.localeCompare(b.name); break;
|
||||
case 'kills': cmp = a.kills - b.kills; break;
|
||||
case 'kph': cmp = a.kph - b.kph; break;
|
||||
case 'rares': cmp = a.rares - b.rares; break;
|
||||
case 'deaths': cmp = a.totalDeaths - b.totalDeaths; break;
|
||||
case 'uptime': cmp = a.uptime.localeCompare(b.uptime); break;
|
||||
case 'state': cmp = a.state.localeCompare(b.state); break;
|
||||
}
|
||||
return sortAsc ? cmp : -cmp;
|
||||
});
|
||||
return list;
|
||||
}, [characters, sortCol, sortAsc]);
|
||||
|
||||
const toggleSort = (col: SortCol) => {
|
||||
if (sortCol === col) setSortAsc(!sortAsc);
|
||||
else { setSortCol(col); setSortAsc(false); }
|
||||
};
|
||||
|
||||
const thStyle = (col: SortCol): React.CSSProperties => ({
|
||||
padding: '4px 6px', cursor: 'pointer', userSelect: 'none',
|
||||
color: sortCol === col ? '#6af' : '#888',
|
||||
fontSize: '0.65rem', fontWeight: 600, whiteSpace: 'nowrap',
|
||||
borderBottom: '1px solid #444',
|
||||
});
|
||||
|
||||
const arrow = (col: SortCol) => sortCol === col ? (sortAsc ? ' ▲' : ' ▼') : '';
|
||||
|
||||
return (
|
||||
<DraggableWindow id={id} title="Player Dashboard" zIndex={zIndex} width={850} height={500}>
|
||||
<div style={{ flex: 1, overflow: 'auto', fontSize: '0.73rem' }}>
|
||||
<table style={{ width: '100%', borderCollapse: 'collapse' }}>
|
||||
<thead>
|
||||
<tr style={{ position: 'sticky', top: 0, background: '#1a1a1a', zIndex: 1 }}>
|
||||
<th style={{ ...thStyle('name'), textAlign: 'left' }} onClick={() => toggleSort('name')}>Character{arrow('name')}</th>
|
||||
<th style={{ ...thStyle('state'), textAlign: 'center' }} onClick={() => toggleSort('state')}>State{arrow('state')}</th>
|
||||
<th style={{ ...thStyle('kph'), textAlign: 'right' }} onClick={() => toggleSort('kph')}>KPH{arrow('kph')}</th>
|
||||
<th style={{ ...thStyle('kills'), textAlign: 'right' }} onClick={() => toggleSort('kills')}>Session{arrow('kills')}</th>
|
||||
<th style={{ textAlign: 'right', padding: '4px 6px', color: '#888', fontSize: '0.65rem', fontWeight: 600, borderBottom: '1px solid #444' }}>Total</th>
|
||||
<th style={{ ...thStyle('rares'), textAlign: 'right' }} onClick={() => toggleSort('rares')}>Rares{arrow('rares')}</th>
|
||||
<th style={{ ...thStyle('deaths'), textAlign: 'right' }} onClick={() => toggleSort('deaths')}>Deaths{arrow('deaths')}</th>
|
||||
<th style={{ ...thStyle('uptime'), textAlign: 'right' }} onClick={() => toggleSort('uptime')}>Uptime{arrow('uptime')}</th>
|
||||
<th style={{ textAlign: 'right', padding: '4px 6px', color: '#888', fontSize: '0.65rem', fontWeight: 600, borderBottom: '1px solid #444' }}>HP%</th>
|
||||
<th style={{ textAlign: 'right', padding: '4px 6px', color: '#888', fontSize: '0.65rem', fontWeight: 600, borderBottom: '1px solid #444' }}>Tapers</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{players.map(p => {
|
||||
const stateLC = p.state.toLowerCase();
|
||||
const isActive = stateLC === 'combat' || stateLC === 'hunt';
|
||||
return (
|
||||
<tr key={p.name} style={{ borderBottom: '1px solid #1a1a1a' }}>
|
||||
<td style={{ padding: '3px 6px', color: '#ccc', fontWeight: 500, maxWidth: 180, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{p.name}</td>
|
||||
<td style={{ textAlign: 'center', padding: '3px 6px' }}>
|
||||
<span style={{ fontSize: '0.6rem', padding: '1px 6px', borderRadius: 3,
|
||||
background: isActive ? 'rgba(68,204,68,0.15)' : stateLC === 'idle' || stateLC === 'default' ? 'rgba(100,100,100,0.2)' : 'rgba(204,68,68,0.15)',
|
||||
color: isActive ? '#4c4' : stateLC === 'idle' || stateLC === 'default' ? '#888' : '#c44',
|
||||
}}>{p.state}</span>
|
||||
</td>
|
||||
<td style={{ textAlign: 'right', padding: '3px 6px', color: '#4c4', fontVariantNumeric: 'tabular-nums' }}>{p.kph.toLocaleString()}</td>
|
||||
<td style={{ textAlign: 'right', padding: '3px 6px', color: '#ccc', fontVariantNumeric: 'tabular-nums' }}>{p.kills.toLocaleString()}</td>
|
||||
<td style={{ textAlign: 'right', padding: '3px 6px', color: '#888', fontVariantNumeric: 'tabular-nums' }}>{p.totalKills.toLocaleString()}</td>
|
||||
<td style={{ textAlign: 'right', padding: '3px 6px', color: '#fc0', fontVariantNumeric: 'tabular-nums' }}>{p.rares}{p.sessionRares > 0 ? ` (${p.sessionRares})` : ''}</td>
|
||||
<td style={{ textAlign: 'right', padding: '3px 6px', color: p.totalDeaths > 0 ? '#c66' : '#555', fontVariantNumeric: 'tabular-nums' }}>{p.totalDeaths}</td>
|
||||
<td style={{ textAlign: 'right', padding: '3px 6px', color: '#888', fontVariantNumeric: 'tabular-nums' }}>{p.uptime}</td>
|
||||
<td style={{ textAlign: 'right', padding: '3px 6px', fontVariantNumeric: 'tabular-nums',
|
||||
color: p.hp > 80 ? '#4c4' : p.hp > 40 ? '#ca0' : '#c44' }}>{p.hp.toFixed(0)}%</td>
|
||||
<td style={{ textAlign: 'right', padding: '3px 6px', color: '#888', fontVariantNumeric: 'tabular-nums' }}>{p.tapers.toLocaleString()}</td>
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
{players.length === 0 && (
|
||||
<div style={{ padding: 20, color: '#666', textAlign: 'center' }}>No characters online</div>
|
||||
)}
|
||||
</div>
|
||||
</DraggableWindow>
|
||||
);
|
||||
};
|
||||
84
frontend/src/components/windows/QuestStatusWindow.tsx
Normal file
84
frontend/src/components/windows/QuestStatusWindow.tsx
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
import React, { useEffect, useState } from 'react';
|
||||
import { DraggableWindow } from './DraggableWindow';
|
||||
import { apiFetch } from '../../api/client';
|
||||
|
||||
interface Props { id: string; zIndex: number; }
|
||||
|
||||
interface QuestData {
|
||||
quest_data: Record<string, Record<string, string>>;
|
||||
tracked_quests: string[];
|
||||
player_count: number;
|
||||
}
|
||||
|
||||
export const QuestStatusWindow: React.FC<Props> = ({ id, zIndex }) => {
|
||||
const [data, setData] = useState<QuestData | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const fetch = async () => {
|
||||
try { setData(await apiFetch<QuestData>('/quest-status')); } catch {}
|
||||
};
|
||||
fetch();
|
||||
const iv = setInterval(fetch, 30000);
|
||||
return () => clearInterval(iv);
|
||||
}, []);
|
||||
|
||||
const characters = data ? Object.keys(data.quest_data).sort() : [];
|
||||
// Collect ALL unique quest names across all characters
|
||||
const allQuests = new Set<string>();
|
||||
if (data) {
|
||||
for (const quests of Object.values(data.quest_data)) {
|
||||
for (const q of Object.keys(quests)) allQuests.add(q);
|
||||
}
|
||||
}
|
||||
const questNames = Array.from(allQuests).sort();
|
||||
|
||||
return (
|
||||
<DraggableWindow id={id} title="Quest Status" zIndex={zIndex} width={780} height={500}>
|
||||
<div style={{ flex: 1, overflow: 'auto', fontSize: '0.72rem' }}>
|
||||
{!data ? (
|
||||
<div style={{ padding: 20, color: '#666', textAlign: 'center' }}>Loading quest data...</div>
|
||||
) : characters.length === 0 ? (
|
||||
<div style={{ padding: 20, color: '#666', textAlign: 'center' }}>No quest data available</div>
|
||||
) : (
|
||||
<table style={{ width: '100%', borderCollapse: 'collapse' }}>
|
||||
<thead>
|
||||
<tr style={{ position: 'sticky', top: 0, background: '#1a1a1a', zIndex: 1 }}>
|
||||
<th style={{ textAlign: 'left', padding: '4px 8px', borderBottom: '1px solid #444', color: '#888', fontSize: '0.65rem', fontWeight: 600, minWidth: 140 }}>Character</th>
|
||||
{questNames.map(q => (
|
||||
<th key={q} style={{ textAlign: 'center', padding: '4px 6px', borderBottom: '1px solid #444', color: '#888', fontSize: '0.6rem', fontWeight: 600, maxWidth: 120, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}
|
||||
title={q}>
|
||||
{q.replace(' Timer', '').replace(' Pickup', '')}
|
||||
</th>
|
||||
))}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{characters.map(char => {
|
||||
const quests = data.quest_data[char] || {};
|
||||
return (
|
||||
<tr key={char} style={{ borderBottom: '1px solid #222' }}>
|
||||
<td style={{ padding: '3px 8px', color: '#ccc', fontWeight: 500, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis', maxWidth: 160 }}>{char}</td>
|
||||
{questNames.map(q => {
|
||||
const val = quests[q];
|
||||
const isReady = val === 'READY';
|
||||
return (
|
||||
<td key={q} style={{
|
||||
textAlign: 'center', padding: '3px 6px',
|
||||
color: isReady ? '#4c4' : val ? '#ca0' : '#333',
|
||||
fontWeight: isReady ? 600 : 400,
|
||||
fontSize: isReady ? '0.7rem' : '0.68rem',
|
||||
}}>
|
||||
{val || '\u2014'}
|
||||
</td>
|
||||
);
|
||||
})}
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
)}
|
||||
</div>
|
||||
</DraggableWindow>
|
||||
);
|
||||
};
|
||||
|
|
@ -9,6 +9,8 @@ const CombatStatsWindow = lazy(() => import('./CombatStatsWindow').then(m => ({
|
|||
const CombatPickerWindow = lazy(() => import('./CombatPickerWindow').then(m => ({ default: m.CombatPickerWindow })));
|
||||
const IssuesWindow = lazy(() => import('./IssuesWindow').then(m => ({ default: m.IssuesWindow })));
|
||||
const VitalSharingWindow = lazy(() => import('./VitalSharingWindow').then(m => ({ default: m.VitalSharingWindow })));
|
||||
const QuestStatusWindow = lazy(() => import('./QuestStatusWindow').then(m => ({ default: m.QuestStatusWindow })));
|
||||
const PlayerDashboardWindow = lazy(() => import('./PlayerDashboardWindow').then(m => ({ default: m.PlayerDashboardWindow })));
|
||||
import type { CharacterState } from '../../types';
|
||||
|
||||
interface Props {
|
||||
|
|
@ -52,6 +54,10 @@ export const WindowRenderer: React.FC<Props> = React.memo(({ characters, chatMes
|
|||
return <IssuesWindow key={w.id} id={w.id} zIndex={w.zIndex} />;
|
||||
case 'vitalsharing':
|
||||
return <VitalSharingWindow key={w.id} id={w.id} zIndex={w.zIndex} />;
|
||||
case 'queststatus':
|
||||
return <QuestStatusWindow key={w.id} id={w.id} zIndex={w.zIndex} />;
|
||||
case 'playerdash':
|
||||
return <PlayerDashboardWindow key={w.id} id={w.id} zIndex={w.zIndex} characters={characters} />;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -1 +1 @@
|
|||
import{u as c,j as r,D as d}from"./index-ahPE6Fpe.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};
|
||||
import{u as c,j as r,D as d}from"./index-D3oSGfL8.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};
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
static/assets/PlayerDashboardWindow-DchADNHm.js
Normal file
1
static/assets/PlayerDashboardWindow-DchADNHm.js
Normal file
|
|
@ -0,0 +1 @@
|
|||
import{r as d,j as t,D as y}from"./index-D3oSGfL8.js";import"./react-DlyoauG8.js";const b=({id:m,zIndex:u,characters:h})=>{const[o,k]=d.useState("kph"),[c,x]=d.useState(!1),p=d.useMemo(()=>{const e=Array.from(h.values()).filter(s=>s.telemetry).map(s=>{var l,g;const r=s.telemetry;return{name:s.name,kills:r.kills??0,kph:parseInt(r.kills_per_hour)||0,totalKills:r.total_kills??0,rares:r.total_rares??0,sessionRares:r.session_rares??0,deaths:parseInt(r.deaths)||0,totalDeaths:parseInt(r.total_deaths)||0,uptime:((l=r.onlinetime)==null?void 0:l.replace(/^00\./,""))??"",state:r.vt_state??"idle",tapers:parseInt(r.prismatic_taper_count)||0,hp:((g=s.vitals)==null?void 0:g.health_percentage)??0}});return e.sort((s,r)=>{let l=0;switch(o){case"name":l=s.name.localeCompare(r.name);break;case"kills":l=s.kills-r.kills;break;case"kph":l=s.kph-r.kph;break;case"rares":l=s.rares-r.rares;break;case"deaths":l=s.totalDeaths-r.totalDeaths;break;case"uptime":l=s.uptime.localeCompare(r.uptime);break;case"state":l=s.state.localeCompare(r.state);break}return c?l:-l}),e},[h,o,c]),a=e=>{o===e?x(!c):(k(e),x(!1))},i=e=>({padding:"4px 6px",cursor:"pointer",userSelect:"none",color:o===e?"#6af":"#888",fontSize:"0.65rem",fontWeight:600,whiteSpace:"nowrap",borderBottom:"1px solid #444"}),n=e=>o===e?c?" ▲":" ▼":"";return t.jsx(y,{id:m,title:"Player Dashboard",zIndex:u,width:850,height:500,children:t.jsxs("div",{style:{flex:1,overflow:"auto",fontSize:"0.73rem"},children:[t.jsxs("table",{style:{width:"100%",borderCollapse:"collapse"},children:[t.jsx("thead",{children:t.jsxs("tr",{style:{position:"sticky",top:0,background:"#1a1a1a",zIndex:1},children:[t.jsxs("th",{style:{...i("name"),textAlign:"left"},onClick:()=>a("name"),children:["Character",n("name")]}),t.jsxs("th",{style:{...i("state"),textAlign:"center"},onClick:()=>a("state"),children:["State",n("state")]}),t.jsxs("th",{style:{...i("kph"),textAlign:"right"},onClick:()=>a("kph"),children:["KPH",n("kph")]}),t.jsxs("th",{style:{...i("kills"),textAlign:"right"},onClick:()=>a("kills"),children:["Session",n("kills")]}),t.jsx("th",{style:{textAlign:"right",padding:"4px 6px",color:"#888",fontSize:"0.65rem",fontWeight:600,borderBottom:"1px solid #444"},children:"Total"}),t.jsxs("th",{style:{...i("rares"),textAlign:"right"},onClick:()=>a("rares"),children:["Rares",n("rares")]}),t.jsxs("th",{style:{...i("deaths"),textAlign:"right"},onClick:()=>a("deaths"),children:["Deaths",n("deaths")]}),t.jsxs("th",{style:{...i("uptime"),textAlign:"right"},onClick:()=>a("uptime"),children:["Uptime",n("uptime")]}),t.jsx("th",{style:{textAlign:"right",padding:"4px 6px",color:"#888",fontSize:"0.65rem",fontWeight:600,borderBottom:"1px solid #444"},children:"HP%"}),t.jsx("th",{style:{textAlign:"right",padding:"4px 6px",color:"#888",fontSize:"0.65rem",fontWeight:600,borderBottom:"1px solid #444"},children:"Tapers"})]})}),t.jsx("tbody",{children:p.map(e=>{const s=e.state.toLowerCase(),r=s==="combat"||s==="hunt";return t.jsxs("tr",{style:{borderBottom:"1px solid #1a1a1a"},children:[t.jsx("td",{style:{padding:"3px 6px",color:"#ccc",fontWeight:500,maxWidth:180,overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap"},children:e.name}),t.jsx("td",{style:{textAlign:"center",padding:"3px 6px"},children:t.jsx("span",{style:{fontSize:"0.6rem",padding:"1px 6px",borderRadius:3,background:r?"rgba(68,204,68,0.15)":s==="idle"||s==="default"?"rgba(100,100,100,0.2)":"rgba(204,68,68,0.15)",color:r?"#4c4":s==="idle"||s==="default"?"#888":"#c44"},children:e.state})}),t.jsx("td",{style:{textAlign:"right",padding:"3px 6px",color:"#4c4",fontVariantNumeric:"tabular-nums"},children:e.kph.toLocaleString()}),t.jsx("td",{style:{textAlign:"right",padding:"3px 6px",color:"#ccc",fontVariantNumeric:"tabular-nums"},children:e.kills.toLocaleString()}),t.jsx("td",{style:{textAlign:"right",padding:"3px 6px",color:"#888",fontVariantNumeric:"tabular-nums"},children:e.totalKills.toLocaleString()}),t.jsxs("td",{style:{textAlign:"right",padding:"3px 6px",color:"#fc0",fontVariantNumeric:"tabular-nums"},children:[e.rares,e.sessionRares>0?` (${e.sessionRares})`:""]}),t.jsx("td",{style:{textAlign:"right",padding:"3px 6px",color:e.totalDeaths>0?"#c66":"#555",fontVariantNumeric:"tabular-nums"},children:e.totalDeaths}),t.jsx("td",{style:{textAlign:"right",padding:"3px 6px",color:"#888",fontVariantNumeric:"tabular-nums"},children:e.uptime}),t.jsxs("td",{style:{textAlign:"right",padding:"3px 6px",fontVariantNumeric:"tabular-nums",color:e.hp>80?"#4c4":e.hp>40?"#ca0":"#c44"},children:[e.hp.toFixed(0),"%"]}),t.jsx("td",{style:{textAlign:"right",padding:"3px 6px",color:"#888",fontVariantNumeric:"tabular-nums"},children:e.tapers.toLocaleString()})]},e.name)})})]}),p.length===0&&t.jsx("div",{style:{padding:20,color:"#666",textAlign:"center"},children:"No characters online"})]})})};export{b as PlayerDashboardWindow};
|
||||
1
static/assets/QuestStatusWindow-CnP4D88H.js
Normal file
1
static/assets/QuestStatusWindow-CnP4D88H.js
Normal file
|
|
@ -0,0 +1 @@
|
|||
import{r as c,j as t,D as u,a as f}from"./index-D3oSGfL8.js";import"./react-DlyoauG8.js";const j=({id:p,zIndex:x})=>{const[s,h]=c.useState(null);c.useEffect(()=>{const e=async()=>{try{h(await f("/quest-status"))}catch{}};e();const o=setInterval(e,3e4);return()=>clearInterval(o)},[]);const i=s?Object.keys(s.quest_data).sort():[],l=new Set;if(s)for(const e of Object.values(s.quest_data))for(const o of Object.keys(e))l.add(o);const n=Array.from(l).sort();return t.jsx(u,{id:p,title:"Quest Status",zIndex:x,width:780,height:500,children:t.jsx("div",{style:{flex:1,overflow:"auto",fontSize:"0.72rem"},children:s?i.length===0?t.jsx("div",{style:{padding:20,color:"#666",textAlign:"center"},children:"No quest data available"}):t.jsxs("table",{style:{width:"100%",borderCollapse:"collapse"},children:[t.jsx("thead",{children:t.jsxs("tr",{style:{position:"sticky",top:0,background:"#1a1a1a",zIndex:1},children:[t.jsx("th",{style:{textAlign:"left",padding:"4px 8px",borderBottom:"1px solid #444",color:"#888",fontSize:"0.65rem",fontWeight:600,minWidth:140},children:"Character"}),n.map(e=>t.jsx("th",{style:{textAlign:"center",padding:"4px 6px",borderBottom:"1px solid #444",color:"#888",fontSize:"0.6rem",fontWeight:600,maxWidth:120,overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap"},title:e,children:e.replace(" Timer","").replace(" Pickup","")},e))]})}),t.jsx("tbody",{children:i.map(e=>{const o=s.quest_data[e]||{};return t.jsxs("tr",{style:{borderBottom:"1px solid #222"},children:[t.jsx("td",{style:{padding:"3px 8px",color:"#ccc",fontWeight:500,whiteSpace:"nowrap",overflow:"hidden",textOverflow:"ellipsis",maxWidth:160},children:e}),n.map(d=>{const r=o[d],a=r==="READY";return t.jsx("td",{style:{textAlign:"center",padding:"3px 6px",color:a?"#4c4":r?"#ca0":"#333",fontWeight:a?600:400,fontSize:a?"0.7rem":"0.68rem"},children:r||"—"},d)})]},e)})})]}):t.jsx("div",{style:{padding:20,color:"#666",textAlign:"center"},children:"Loading quest data..."})})})};export{j as QuestStatusWindow};
|
||||
File diff suppressed because one or more lines are too long
|
|
@ -1 +1 @@
|
|||
import{r as o,j as t,D as d}from"./index-ahPE6Fpe.js";import"./react-DlyoauG8.js";const c=[{title:"Kills per Hour",id:1},{title:"Memory (MB)",id:2},{title:"CPU (%)",id:3},{title:"Mem Handles",id:4}],m=[{label:"1H",value:"now-1h"},{label:"6H",value:"now-6h"},{label:"24H",value:"now-24h"},{label:"7D",value:"now-7d"}],v=({id:s,charName:a,zIndex:i})=>{const[l,r]=o.useState("now-24h"),n=e=>`/grafana/d-solo/dereth-tracker/dereth-tracker-dashboard?panelId=${e}&var-character=${encodeURIComponent(a)}&from=${l}&to=now&theme=light`;return t.jsxs(d,{id:s,title:`Stats: ${a}`,zIndex:i,width:750,height:480,children:[t.jsx("div",{className:"ml-stats-controls",children:m.map(e=>t.jsx("button",{className:`ml-stats-range-btn ${l===e.value?"active":""}`,onClick:()=>r(e.value),children:e.label},e.value))}),t.jsx("div",{className:"ml-stats-grid",children:c.map(e=>t.jsx("div",{className:"ml-stats-panel",children:t.jsx("iframe",{src:n(e.id),width:"100%",height:"100%",frameBorder:"0",title:e.title})},e.id))})]})};export{v as StatsWindow};
|
||||
import{r as o,j as t,D as d}from"./index-D3oSGfL8.js";import"./react-DlyoauG8.js";const c=[{title:"Kills per Hour",id:1},{title:"Memory (MB)",id:2},{title:"CPU (%)",id:3},{title:"Mem Handles",id:4}],m=[{label:"1H",value:"now-1h"},{label:"6H",value:"now-6h"},{label:"24H",value:"now-24h"},{label:"7D",value:"now-7d"}],v=({id:s,charName:a,zIndex:i})=>{const[l,r]=o.useState("now-24h"),n=e=>`/grafana/d-solo/dereth-tracker/dereth-tracker-dashboard?panelId=${e}&var-character=${encodeURIComponent(a)}&from=${l}&to=now&theme=light`;return t.jsxs(d,{id:s,title:`Stats: ${a}`,zIndex:i,width:750,height:480,children:[t.jsx("div",{className:"ml-stats-controls",children:m.map(e=>t.jsx("button",{className:`ml-stats-range-btn ${l===e.value?"active":""}`,onClick:()=>r(e.value),children:e.label},e.value))}),t.jsx("div",{className:"ml-stats-grid",children:c.map(e=>t.jsx("div",{className:"ml-stats-panel",children:t.jsx("iframe",{src:n(e.id),width:"100%",height:"100%",frameBorder:"0",title:e.title})},e.id))})]})};export{v as StatsWindow};
|
||||
|
|
@ -1 +1 @@
|
|||
import{r as n,j as t,D as x,a as m}from"./index-ahPE6Fpe.js";import"./react-DlyoauG8.js";const v=({id:o,zIndex:c})=>{const[a,d]=n.useState([]);n.useEffect(()=>{const e=async()=>{try{const s=await m("/vital-sharing/peers");d(s.peers??[])}catch{}};e();const l=setInterval(e,5e3);return()=>clearInterval(l)},[]);const h=(e,l)=>l>0?Math.min(100,e/l*100):0;return t.jsx(x,{id:o,title:"Vital Sharing Network",zIndex:c,width:520,height:450,children:t.jsx("div",{style:{flex:1,overflowY:"auto",padding:6,fontSize:"0.75rem"},children:a.length===0?t.jsx("div",{style:{padding:16,color:"#666",textAlign:"center"},children:"No vital-sharing peers connected"}):a.map(e=>{var l,s,r;return t.jsxs("div",{style:{padding:"6px 8px",marginBottom:4,background:"#1f1f1f",borderRadius:3,border:"1px solid #333"},children:[t.jsxs("div",{style:{display:"flex",alignItems:"center",gap:6,marginBottom:3},children:[t.jsx("span",{style:{color:e.plugin_connected?"#4c4":"#a33",fontSize:"0.8rem"},children:"●"}),t.jsx("strong",{style:{flex:1},children:e.character_name}),e.subscribed&&t.jsx("span",{style:{color:"#6bf",fontSize:"0.65rem"},children:"[subscribed]"})]}),t.jsxs("div",{style:{color:"#666",fontSize:"0.68rem",marginBottom:3},children:["tags: ",((l=e.tags)==null?void 0:l.join(", "))||"none"]}),e.vitals&&e.vitals.max_health>0&&t.jsx("div",{style:{display:"flex",flexDirection:"column",gap:2},children:[{label:"HP",cur:e.vitals.current_health,max:e.vitals.max_health,bg:"#330000",fill:"#c44"},{label:"STA",cur:e.vitals.current_stamina,max:e.vitals.max_stamina,bg:"#331a00",fill:"#ca0"},{label:"MANA",cur:e.vitals.current_mana,max:e.vitals.max_mana,bg:"#001433",fill:"#48f"}].map(i=>t.jsxs("div",{style:{display:"flex",alignItems:"center",gap:4},children:[t.jsx("span",{style:{width:32,color:"#888",fontSize:"0.65rem"},children:i.label}),t.jsx("div",{style:{flex:1,height:6,background:i.bg,borderRadius:3,overflow:"hidden"},children:t.jsx("div",{style:{width:`${h(i.cur,i.max)}%`,height:"100%",background:i.fill,borderRadius:3}})}),t.jsxs("span",{style:{width:60,textAlign:"right",fontSize:"0.65rem",color:"#888"},children:[i.cur,"/",i.max]})]},i.label))}),e.position&&t.jsxs("div",{style:{color:"#555",fontSize:"0.65rem",marginTop:2},children:[(s=e.position.ns)==null?void 0:s.toFixed(1),"N, ",(r=e.position.ew)==null?void 0:r.toFixed(1),"E"]})]},e.character_name)})})})};export{v as VitalSharingWindow};
|
||||
import{r as n,j as t,D as x,a as m}from"./index-D3oSGfL8.js";import"./react-DlyoauG8.js";const v=({id:o,zIndex:c})=>{const[a,d]=n.useState([]);n.useEffect(()=>{const e=async()=>{try{const s=await m("/vital-sharing/peers");d(s.peers??[])}catch{}};e();const l=setInterval(e,5e3);return()=>clearInterval(l)},[]);const h=(e,l)=>l>0?Math.min(100,e/l*100):0;return t.jsx(x,{id:o,title:"Vital Sharing Network",zIndex:c,width:520,height:450,children:t.jsx("div",{style:{flex:1,overflowY:"auto",padding:6,fontSize:"0.75rem"},children:a.length===0?t.jsx("div",{style:{padding:16,color:"#666",textAlign:"center"},children:"No vital-sharing peers connected"}):a.map(e=>{var l,s,r;return t.jsxs("div",{style:{padding:"6px 8px",marginBottom:4,background:"#1f1f1f",borderRadius:3,border:"1px solid #333"},children:[t.jsxs("div",{style:{display:"flex",alignItems:"center",gap:6,marginBottom:3},children:[t.jsx("span",{style:{color:e.plugin_connected?"#4c4":"#a33",fontSize:"0.8rem"},children:"●"}),t.jsx("strong",{style:{flex:1},children:e.character_name}),e.subscribed&&t.jsx("span",{style:{color:"#6bf",fontSize:"0.65rem"},children:"[subscribed]"})]}),t.jsxs("div",{style:{color:"#666",fontSize:"0.68rem",marginBottom:3},children:["tags: ",((l=e.tags)==null?void 0:l.join(", "))||"none"]}),e.vitals&&e.vitals.max_health>0&&t.jsx("div",{style:{display:"flex",flexDirection:"column",gap:2},children:[{label:"HP",cur:e.vitals.current_health,max:e.vitals.max_health,bg:"#330000",fill:"#c44"},{label:"STA",cur:e.vitals.current_stamina,max:e.vitals.max_stamina,bg:"#331a00",fill:"#ca0"},{label:"MANA",cur:e.vitals.current_mana,max:e.vitals.max_mana,bg:"#001433",fill:"#48f"}].map(i=>t.jsxs("div",{style:{display:"flex",alignItems:"center",gap:4},children:[t.jsx("span",{style:{width:32,color:"#888",fontSize:"0.65rem"},children:i.label}),t.jsx("div",{style:{flex:1,height:6,background:i.bg,borderRadius:3,overflow:"hidden"},children:t.jsx("div",{style:{width:`${h(i.cur,i.max)}%`,height:"100%",background:i.fill,borderRadius:3}})}),t.jsxs("span",{style:{width:60,textAlign:"right",fontSize:"0.65rem",color:"#888"},children:[i.cur,"/",i.max]})]},i.label))}),e.position&&t.jsxs("div",{style:{color:"#555",fontSize:"0.65rem",marginTop:2},children:[(s=e.position.ns)==null?void 0:s.toFixed(1),"N, ",(r=e.position.ew)==null?void 0:r.toFixed(1),"E"]})]},e.character_name)})})})};export{v as VitalSharingWindow};
|
||||
File diff suppressed because one or more lines are too long
|
|
@ -8,7 +8,7 @@
|
|||
<link rel="preload" as="image" href="/dereth.png" />
|
||||
<link rel="preload" as="image" href="/icons/0600127E.png" />
|
||||
<link rel="preload" as="fetch" href="/dungeon_tiles.json" crossorigin="anonymous" />
|
||||
<script type="module" crossorigin src="/assets/index-ahPE6Fpe.js"></script>
|
||||
<script type="module" crossorigin src="/assets/index-D3oSGfL8.js"></script>
|
||||
<link rel="modulepreload" crossorigin href="/assets/react-DlyoauG8.js">
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-CjQam_eg.css">
|
||||
</head>
|
||||
|
|
|
|||
1
static/v2/assets/CharacterWindow-CtTJptvX.js
Normal file
1
static/v2/assets/CharacterWindow-CtTJptvX.js
Normal file
File diff suppressed because one or more lines are too long
1
static/v2/assets/CombatPickerWindow-CrwAqIf6.js
Normal file
1
static/v2/assets/CombatPickerWindow-CrwAqIf6.js
Normal file
|
|
@ -0,0 +1 @@
|
|||
import{u as c,j as r,D as d}from"./index-D3oSGfL8.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};
|
||||
1
static/v2/assets/CombatStatsWindow-DK6NDxgo.js
Normal file
1
static/v2/assets/CombatStatsWindow-DK6NDxgo.js
Normal file
File diff suppressed because one or more lines are too long
72
static/v2/assets/DashboardView-CByhGUl4.js
Normal file
72
static/v2/assets/DashboardView-CByhGUl4.js
Normal file
File diff suppressed because one or more lines are too long
1
static/v2/assets/InventoryWindow-BnPRil_F.js
Normal file
1
static/v2/assets/InventoryWindow-BnPRil_F.js
Normal file
File diff suppressed because one or more lines are too long
1
static/v2/assets/IssuesWindow-CCHfda8u.js
Normal file
1
static/v2/assets/IssuesWindow-CCHfda8u.js
Normal file
File diff suppressed because one or more lines are too long
1
static/v2/assets/PlayerDashboardWindow-DchADNHm.js
Normal file
1
static/v2/assets/PlayerDashboardWindow-DchADNHm.js
Normal file
|
|
@ -0,0 +1 @@
|
|||
import{r as d,j as t,D as y}from"./index-D3oSGfL8.js";import"./react-DlyoauG8.js";const b=({id:m,zIndex:u,characters:h})=>{const[o,k]=d.useState("kph"),[c,x]=d.useState(!1),p=d.useMemo(()=>{const e=Array.from(h.values()).filter(s=>s.telemetry).map(s=>{var l,g;const r=s.telemetry;return{name:s.name,kills:r.kills??0,kph:parseInt(r.kills_per_hour)||0,totalKills:r.total_kills??0,rares:r.total_rares??0,sessionRares:r.session_rares??0,deaths:parseInt(r.deaths)||0,totalDeaths:parseInt(r.total_deaths)||0,uptime:((l=r.onlinetime)==null?void 0:l.replace(/^00\./,""))??"",state:r.vt_state??"idle",tapers:parseInt(r.prismatic_taper_count)||0,hp:((g=s.vitals)==null?void 0:g.health_percentage)??0}});return e.sort((s,r)=>{let l=0;switch(o){case"name":l=s.name.localeCompare(r.name);break;case"kills":l=s.kills-r.kills;break;case"kph":l=s.kph-r.kph;break;case"rares":l=s.rares-r.rares;break;case"deaths":l=s.totalDeaths-r.totalDeaths;break;case"uptime":l=s.uptime.localeCompare(r.uptime);break;case"state":l=s.state.localeCompare(r.state);break}return c?l:-l}),e},[h,o,c]),a=e=>{o===e?x(!c):(k(e),x(!1))},i=e=>({padding:"4px 6px",cursor:"pointer",userSelect:"none",color:o===e?"#6af":"#888",fontSize:"0.65rem",fontWeight:600,whiteSpace:"nowrap",borderBottom:"1px solid #444"}),n=e=>o===e?c?" ▲":" ▼":"";return t.jsx(y,{id:m,title:"Player Dashboard",zIndex:u,width:850,height:500,children:t.jsxs("div",{style:{flex:1,overflow:"auto",fontSize:"0.73rem"},children:[t.jsxs("table",{style:{width:"100%",borderCollapse:"collapse"},children:[t.jsx("thead",{children:t.jsxs("tr",{style:{position:"sticky",top:0,background:"#1a1a1a",zIndex:1},children:[t.jsxs("th",{style:{...i("name"),textAlign:"left"},onClick:()=>a("name"),children:["Character",n("name")]}),t.jsxs("th",{style:{...i("state"),textAlign:"center"},onClick:()=>a("state"),children:["State",n("state")]}),t.jsxs("th",{style:{...i("kph"),textAlign:"right"},onClick:()=>a("kph"),children:["KPH",n("kph")]}),t.jsxs("th",{style:{...i("kills"),textAlign:"right"},onClick:()=>a("kills"),children:["Session",n("kills")]}),t.jsx("th",{style:{textAlign:"right",padding:"4px 6px",color:"#888",fontSize:"0.65rem",fontWeight:600,borderBottom:"1px solid #444"},children:"Total"}),t.jsxs("th",{style:{...i("rares"),textAlign:"right"},onClick:()=>a("rares"),children:["Rares",n("rares")]}),t.jsxs("th",{style:{...i("deaths"),textAlign:"right"},onClick:()=>a("deaths"),children:["Deaths",n("deaths")]}),t.jsxs("th",{style:{...i("uptime"),textAlign:"right"},onClick:()=>a("uptime"),children:["Uptime",n("uptime")]}),t.jsx("th",{style:{textAlign:"right",padding:"4px 6px",color:"#888",fontSize:"0.65rem",fontWeight:600,borderBottom:"1px solid #444"},children:"HP%"}),t.jsx("th",{style:{textAlign:"right",padding:"4px 6px",color:"#888",fontSize:"0.65rem",fontWeight:600,borderBottom:"1px solid #444"},children:"Tapers"})]})}),t.jsx("tbody",{children:p.map(e=>{const s=e.state.toLowerCase(),r=s==="combat"||s==="hunt";return t.jsxs("tr",{style:{borderBottom:"1px solid #1a1a1a"},children:[t.jsx("td",{style:{padding:"3px 6px",color:"#ccc",fontWeight:500,maxWidth:180,overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap"},children:e.name}),t.jsx("td",{style:{textAlign:"center",padding:"3px 6px"},children:t.jsx("span",{style:{fontSize:"0.6rem",padding:"1px 6px",borderRadius:3,background:r?"rgba(68,204,68,0.15)":s==="idle"||s==="default"?"rgba(100,100,100,0.2)":"rgba(204,68,68,0.15)",color:r?"#4c4":s==="idle"||s==="default"?"#888":"#c44"},children:e.state})}),t.jsx("td",{style:{textAlign:"right",padding:"3px 6px",color:"#4c4",fontVariantNumeric:"tabular-nums"},children:e.kph.toLocaleString()}),t.jsx("td",{style:{textAlign:"right",padding:"3px 6px",color:"#ccc",fontVariantNumeric:"tabular-nums"},children:e.kills.toLocaleString()}),t.jsx("td",{style:{textAlign:"right",padding:"3px 6px",color:"#888",fontVariantNumeric:"tabular-nums"},children:e.totalKills.toLocaleString()}),t.jsxs("td",{style:{textAlign:"right",padding:"3px 6px",color:"#fc0",fontVariantNumeric:"tabular-nums"},children:[e.rares,e.sessionRares>0?` (${e.sessionRares})`:""]}),t.jsx("td",{style:{textAlign:"right",padding:"3px 6px",color:e.totalDeaths>0?"#c66":"#555",fontVariantNumeric:"tabular-nums"},children:e.totalDeaths}),t.jsx("td",{style:{textAlign:"right",padding:"3px 6px",color:"#888",fontVariantNumeric:"tabular-nums"},children:e.uptime}),t.jsxs("td",{style:{textAlign:"right",padding:"3px 6px",fontVariantNumeric:"tabular-nums",color:e.hp>80?"#4c4":e.hp>40?"#ca0":"#c44"},children:[e.hp.toFixed(0),"%"]}),t.jsx("td",{style:{textAlign:"right",padding:"3px 6px",color:"#888",fontVariantNumeric:"tabular-nums"},children:e.tapers.toLocaleString()})]},e.name)})})]}),p.length===0&&t.jsx("div",{style:{padding:20,color:"#666",textAlign:"center"},children:"No characters online"})]})})};export{b as PlayerDashboardWindow};
|
||||
1
static/v2/assets/QuestStatusWindow-CnP4D88H.js
Normal file
1
static/v2/assets/QuestStatusWindow-CnP4D88H.js
Normal file
|
|
@ -0,0 +1 @@
|
|||
import{r as c,j as t,D as u,a as f}from"./index-D3oSGfL8.js";import"./react-DlyoauG8.js";const j=({id:p,zIndex:x})=>{const[s,h]=c.useState(null);c.useEffect(()=>{const e=async()=>{try{h(await f("/quest-status"))}catch{}};e();const o=setInterval(e,3e4);return()=>clearInterval(o)},[]);const i=s?Object.keys(s.quest_data).sort():[],l=new Set;if(s)for(const e of Object.values(s.quest_data))for(const o of Object.keys(e))l.add(o);const n=Array.from(l).sort();return t.jsx(u,{id:p,title:"Quest Status",zIndex:x,width:780,height:500,children:t.jsx("div",{style:{flex:1,overflow:"auto",fontSize:"0.72rem"},children:s?i.length===0?t.jsx("div",{style:{padding:20,color:"#666",textAlign:"center"},children:"No quest data available"}):t.jsxs("table",{style:{width:"100%",borderCollapse:"collapse"},children:[t.jsx("thead",{children:t.jsxs("tr",{style:{position:"sticky",top:0,background:"#1a1a1a",zIndex:1},children:[t.jsx("th",{style:{textAlign:"left",padding:"4px 8px",borderBottom:"1px solid #444",color:"#888",fontSize:"0.65rem",fontWeight:600,minWidth:140},children:"Character"}),n.map(e=>t.jsx("th",{style:{textAlign:"center",padding:"4px 6px",borderBottom:"1px solid #444",color:"#888",fontSize:"0.6rem",fontWeight:600,maxWidth:120,overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap"},title:e,children:e.replace(" Timer","").replace(" Pickup","")},e))]})}),t.jsx("tbody",{children:i.map(e=>{const o=s.quest_data[e]||{};return t.jsxs("tr",{style:{borderBottom:"1px solid #222"},children:[t.jsx("td",{style:{padding:"3px 8px",color:"#ccc",fontWeight:500,whiteSpace:"nowrap",overflow:"hidden",textOverflow:"ellipsis",maxWidth:160},children:e}),n.map(d=>{const r=o[d],a=r==="READY";return t.jsx("td",{style:{textAlign:"center",padding:"3px 6px",color:a?"#4c4":r?"#ca0":"#333",fontWeight:a?600:400,fontSize:a?"0.7rem":"0.68rem"},children:r||"—"},d)})]},e)})})]}):t.jsx("div",{style:{padding:20,color:"#666",textAlign:"center"},children:"Loading quest data..."})})})};export{j as QuestStatusWindow};
|
||||
1
static/v2/assets/RadarWindow-GGqyTOAq.js
Normal file
1
static/v2/assets/RadarWindow-GGqyTOAq.js
Normal file
File diff suppressed because one or more lines are too long
1
static/v2/assets/StatsWindow--fpGlal-.js
Normal file
1
static/v2/assets/StatsWindow--fpGlal-.js
Normal file
|
|
@ -0,0 +1 @@
|
|||
import{r as o,j as t,D as d}from"./index-D3oSGfL8.js";import"./react-DlyoauG8.js";const c=[{title:"Kills per Hour",id:1},{title:"Memory (MB)",id:2},{title:"CPU (%)",id:3},{title:"Mem Handles",id:4}],m=[{label:"1H",value:"now-1h"},{label:"6H",value:"now-6h"},{label:"24H",value:"now-24h"},{label:"7D",value:"now-7d"}],v=({id:s,charName:a,zIndex:i})=>{const[l,r]=o.useState("now-24h"),n=e=>`/grafana/d-solo/dereth-tracker/dereth-tracker-dashboard?panelId=${e}&var-character=${encodeURIComponent(a)}&from=${l}&to=now&theme=light`;return t.jsxs(d,{id:s,title:`Stats: ${a}`,zIndex:i,width:750,height:480,children:[t.jsx("div",{className:"ml-stats-controls",children:m.map(e=>t.jsx("button",{className:`ml-stats-range-btn ${l===e.value?"active":""}`,onClick:()=>r(e.value),children:e.label},e.value))}),t.jsx("div",{className:"ml-stats-grid",children:c.map(e=>t.jsx("div",{className:"ml-stats-panel",children:t.jsx("iframe",{src:n(e.id),width:"100%",height:"100%",frameBorder:"0",title:e.title})},e.id))})]})};export{v as StatsWindow};
|
||||
1
static/v2/assets/VitalSharingWindow-DvDmvLb0.js
Normal file
1
static/v2/assets/VitalSharingWindow-DvDmvLb0.js
Normal file
|
|
@ -0,0 +1 @@
|
|||
import{r as n,j as t,D as x,a as m}from"./index-D3oSGfL8.js";import"./react-DlyoauG8.js";const v=({id:o,zIndex:c})=>{const[a,d]=n.useState([]);n.useEffect(()=>{const e=async()=>{try{const s=await m("/vital-sharing/peers");d(s.peers??[])}catch{}};e();const l=setInterval(e,5e3);return()=>clearInterval(l)},[]);const h=(e,l)=>l>0?Math.min(100,e/l*100):0;return t.jsx(x,{id:o,title:"Vital Sharing Network",zIndex:c,width:520,height:450,children:t.jsx("div",{style:{flex:1,overflowY:"auto",padding:6,fontSize:"0.75rem"},children:a.length===0?t.jsx("div",{style:{padding:16,color:"#666",textAlign:"center"},children:"No vital-sharing peers connected"}):a.map(e=>{var l,s,r;return t.jsxs("div",{style:{padding:"6px 8px",marginBottom:4,background:"#1f1f1f",borderRadius:3,border:"1px solid #333"},children:[t.jsxs("div",{style:{display:"flex",alignItems:"center",gap:6,marginBottom:3},children:[t.jsx("span",{style:{color:e.plugin_connected?"#4c4":"#a33",fontSize:"0.8rem"},children:"●"}),t.jsx("strong",{style:{flex:1},children:e.character_name}),e.subscribed&&t.jsx("span",{style:{color:"#6bf",fontSize:"0.65rem"},children:"[subscribed]"})]}),t.jsxs("div",{style:{color:"#666",fontSize:"0.68rem",marginBottom:3},children:["tags: ",((l=e.tags)==null?void 0:l.join(", "))||"none"]}),e.vitals&&e.vitals.max_health>0&&t.jsx("div",{style:{display:"flex",flexDirection:"column",gap:2},children:[{label:"HP",cur:e.vitals.current_health,max:e.vitals.max_health,bg:"#330000",fill:"#c44"},{label:"STA",cur:e.vitals.current_stamina,max:e.vitals.max_stamina,bg:"#331a00",fill:"#ca0"},{label:"MANA",cur:e.vitals.current_mana,max:e.vitals.max_mana,bg:"#001433",fill:"#48f"}].map(i=>t.jsxs("div",{style:{display:"flex",alignItems:"center",gap:4},children:[t.jsx("span",{style:{width:32,color:"#888",fontSize:"0.65rem"},children:i.label}),t.jsx("div",{style:{flex:1,height:6,background:i.bg,borderRadius:3,overflow:"hidden"},children:t.jsx("div",{style:{width:`${h(i.cur,i.max)}%`,height:"100%",background:i.fill,borderRadius:3}})}),t.jsxs("span",{style:{width:60,textAlign:"right",fontSize:"0.65rem",color:"#888"},children:[i.cur,"/",i.max]})]},i.label))}),e.position&&t.jsxs("div",{style:{color:"#555",fontSize:"0.65rem",marginTop:2},children:[(s=e.position.ns)==null?void 0:s.toFixed(1),"N, ",(r=e.position.ew)==null?void 0:r.toFixed(1),"E"]})]},e.character_name)})})})};export{v as VitalSharingWindow};
|
||||
34
static/v2/assets/index-D3oSGfL8.js
Normal file
34
static/v2/assets/index-D3oSGfL8.js
Normal file
File diff suppressed because one or more lines are too long
|
|
@ -8,7 +8,7 @@
|
|||
<link rel="preload" as="image" href="/dereth.png" />
|
||||
<link rel="preload" as="image" href="/icons/0600127E.png" />
|
||||
<link rel="preload" as="fetch" href="/dungeon_tiles.json" crossorigin="anonymous" />
|
||||
<script type="module" crossorigin src="/assets/index-ahPE6Fpe.js"></script>
|
||||
<script type="module" crossorigin src="/assets/index-D3oSGfL8.js"></script>
|
||||
<link rel="modulepreload" crossorigin href="/assets/react-DlyoauG8.js">
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-CjQam_eg.css">
|
||||
</head>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue