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:
Erik 2026-04-14 14:02:00 +02:00
parent 27caa21a56
commit 938421999a
31 changed files with 345 additions and 21 deletions

View file

@ -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 />

View file

@ -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' }}

View 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>
);
};

View 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>
);
};

View file

@ -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;
}

View file

@ -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

View 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};

View 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

View file

@ -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};

View file

@ -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

View file

@ -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>

File diff suppressed because one or more lines are too long

View 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};

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

View 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};

View 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

View 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};

View 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};

File diff suppressed because one or more lines are too long

View file

@ -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>