Files
peardata/index.html
T
Raven Scott a7648dc090
CI / test (push) Successful in 1m1s
Release rolling / release (push) Successful in 6m59s
Updates
2026-07-18 20:32:05 -04:00

411 lines
18 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="en" data-theme="dark">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>PearData — P2P Fleet Monitoring</title>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500&display=swap"
rel="stylesheet"
/>
<script>
try {
var raw = localStorage.getItem('peardata.settings.v1') || localStorage.getItem('peardata-ui-boot') || '{}'
var s = JSON.parse(raw)
var theme = s.theme || (s.settings && s.settings.theme)
if (theme) document.documentElement.dataset.theme = theme
} catch (e) {}
</script>
<link rel="stylesheet" href="./ui/styles.css" />
</head>
<body data-accent="teal" data-density="comfortable" data-reduce-motion="0">
<div id="titlebar" role="banner">
<div class="titlebar-left">
<pear-ctrl></pear-ctrl>
<div class="app-brand" aria-hidden="true">
<div class="app-brand-mark"></div>
<div class="app-brand-text">
PearData <span>P2P observability</span>
</div>
</div>
</div>
<div class="titlebar-right">
<span id="active-peer-chip" class="chip chip-muted">no agent</span>
<span id="role-badge" class="badge"></span>
<div id="status-chip" class="chip offline">offline</div>
</div>
</div>
<aside id="sidebar">
<nav id="main-nav" aria-label="Main">
<div class="nav-group-label">Monitor</div>
<button type="button" class="nav-link active" data-view="overview">
<span class="nav-ico"></span><span class="nav-label">Overview</span>
</button>
<button type="button" class="nav-link" data-view="charts">
<span class="nav-ico"></span><span class="nav-label">Charts</span>
</button>
<button type="button" class="nav-link" data-view="alerts">
<span class="nav-ico"></span><span class="nav-label">Alerts</span>
</button>
<div class="nav-group-label">Fleet</div>
<button type="button" class="nav-link" data-view="fleet">
<span class="nav-ico"></span><span class="nav-label">Agents</span>
</button>
<button type="button" class="nav-link" data-view="connect">
<span class="nav-ico"></span><span class="nav-label">Connect</span>
</button>
<div class="nav-group-label">App</div>
<button type="button" class="nav-link" data-view="settings">
<span class="nav-ico"></span><span class="nav-label">Settings</span>
</button>
</nav>
<div class="sidebar-footer">
<button type="button" id="collapse-sidebar-btn" class="ghost compact" title="Collapse sidebar">
</button>
<div id="conn-meta" class="meta muted">Ready</div>
</div>
</aside>
<div id="content">
<div id="offline-banner" class="offline-banner hidden" role="status">
Agent offline — showing last-known samples. Reconnecting…
</div>
<div id="restoring-banner" class="offline-banner restoring-banner hidden" role="status">
Restoring saved agents…
</div>
<!-- Overview -->
<section id="overview-view" class="view">
<header class="page-header">
<div>
<p class="dash-kicker">Live</p>
<h1 class="dash-title">Overview</h1>
<p class="page-subtitle">Real-time host metrics from the active agent</p>
</div>
<div class="page-actions">
<button type="button" id="btn-refresh-meta" class="ghost">Refresh</button>
</div>
</header>
<section class="dash-kpis">
<div class="dash-kpi" data-stat="cpu">
<span class="stat-label">CPU</span>
<strong id="stat-cpu"></strong>
</div>
<div class="dash-kpi" data-stat="ram">
<span class="stat-label">RAM used</span>
<strong id="stat-ram"></strong>
</div>
<div class="dash-kpi" data-stat="load">
<span class="stat-label">Load 1m</span>
<strong id="stat-load"></strong>
</div>
<div class="dash-kpi" data-stat="net">
<span class="stat-label">Net RX</span>
<strong id="stat-net"></strong>
</div>
<div class="dash-kpi" data-stat="health" id="kpi-health">
<span class="stat-label">Health</span>
<strong id="stat-health"></strong>
</div>
</section>
<section id="fleet-strip" class="fleet-strip dash-card hidden" aria-live="polite">
<header>
<h3>Fleet</h3>
<span id="fleet-summary" class="muted"></span>
</header>
<ul id="fleet-children" class="fleet-children"></ul>
</section>
<section class="charts-grid">
<article class="dash-card chart-panel" data-chart="system.cpu" id="panel-cpu">
<header>
<h3>CPU</h3>
<span class="muted" id="chart-cpu-label">system.cpu</span>
</header>
<canvas id="chart-cpu" height="160"></canvas>
<div class="chart-legend" id="legend-cpu"></div>
</article>
<article class="dash-card chart-panel" data-chart="mem.available" id="panel-ram">
<header>
<h3>Memory</h3>
<span class="muted">system.ram</span>
</header>
<canvas id="chart-ram" height="160"></canvas>
</article>
<article class="dash-card chart-panel" data-chart="system.net" id="panel-net">
<header>
<h3>Network</h3>
<span class="muted">system.net</span>
</header>
<canvas id="chart-net" height="160"></canvas>
</article>
<article class="dash-card chart-panel" data-chart="system.io" id="panel-io">
<header>
<h3>Disk I/O</h3>
<span class="muted">system.io</span>
</header>
<canvas id="chart-io" height="160"></canvas>
</article>
<article class="dash-card chart-panel" data-chart="system.load" id="panel-load">
<header>
<h3>Load</h3>
<span class="muted">system.load</span>
</header>
<canvas id="chart-load" height="160"></canvas>
</article>
<article class="dash-card chart-panel" data-chart="explore" id="panel-explore">
<header>
<h3>Spotlight</h3>
<label class="chart-picker">
<select id="explore-chart" aria-label="Spotlight chart">
<option value="system.io">system.io</option>
</select>
</label>
</header>
<canvas id="chart-explore" height="160"></canvas>
</article>
</section>
</section>
<!-- Master metrics wall -->
<section id="charts-view" class="view hidden">
<header class="page-header metrics-header">
<div>
<p class="dash-kicker">Metrics</p>
<h1 class="dash-title">Charts</h1>
<p class="page-subtitle">Every collected context, sectioned and live · <kbd>Space</kbd> play · <kbd>/</kbd> search · <kbd>15</kbd> window</p>
</div>
<div class="metrics-timebar" id="metrics-timebar">
<button type="button" id="metrics-play" class="ghost" aria-pressed="true">Pause</button>
<button type="button" id="metrics-force-play" class="ghost" aria-pressed="false" title="Keep live updating (wallboard)">Force</button>
<button type="button" id="metrics-board" class="ghost" aria-pressed="false" title="Show only pinned charts">Board</button>
<button type="button" id="metrics-reset" class="ghost" title="Reset to live 5m">Reset</button>
<div id="metrics-presets" class="metrics-presets" role="group" aria-label="Time window">
<button type="button" class="ghost" data-preset="1m">1m</button>
<button type="button" class="ghost active" data-preset="5m">5m</button>
<button type="button" class="ghost" data-preset="15m">15m</button>
<button type="button" class="ghost" data-preset="1h">1h</button>
<button type="button" class="ghost" data-preset="6h">6h</button>
</div>
<label class="metrics-group-label muted">
Group
<select id="metrics-group" aria-label="Downsample aggregation">
<option value="average">average</option>
<option value="min">min</option>
<option value="max">max</option>
<option value="sum">sum</option>
</select>
</label>
<button type="button" id="metrics-dim-sort" class="ghost">Sort: name</button>
<span id="metrics-meta" class="muted metrics-meta"></span>
<span id="metrics-hover" class="muted metrics-hover"></span>
</div>
</header>
<div id="metrics-related" class="dash-card related-panel hidden"></div>
<div class="metrics-shell">
<aside class="metrics-toc dash-card">
<input id="chart-search" type="search" placeholder="Filter by id, title, family…" autocomplete="off" />
<nav id="metrics-toc" class="metrics-toc-nav" aria-label="Chart sections"></nav>
<!-- legacy list kept hidden for compatibility -->
<ul id="chart-catalog-list" class="catalog-list hidden" hidden></ul>
</aside>
<div id="metrics-wall" class="metrics-wall" tabindex="0"></div>
</div>
<!-- Detail strip for deep-dive (dblclick a card) -->
<article id="chart-detail-panel" class="dash-card chart-detail metrics-detail hidden">
<header>
<h3 id="chart-detail-title">Select a chart</h3>
<span id="chart-detail-meta" class="muted"></span>
</header>
<canvas id="chart-detail" height="220"></canvas>
<pre id="chart-detail-dims" class="dim-list muted"></pre>
</article>
</section>
<!-- Alerts -->
<section id="alerts-view" class="view hidden">
<header class="page-header">
<div>
<p class="dash-kicker">Signals</p>
<h1 class="dash-title">Alerts</h1>
<p class="page-subtitle">Anomalies and threshold events from the agent</p>
</div>
<label class="check-row">
<input type="checkbox" id="notify-desktop" checked />
Desktop notifications
</label>
</header>
<div class="dash-card">
<ul id="anomaly-list" class="event-list"></ul>
</div>
</section>
<!-- Fleet / agents -->
<section id="fleet-view" class="view hidden">
<header class="page-header">
<div>
<p class="dash-kicker">Peers</p>
<h1 class="dash-title">Agents</h1>
<p class="page-subtitle">Saved bookmarks and live connections</p>
</div>
<label class="check-row">
<input type="checkbox" id="compare-toggle" />
Compare CPU overlay
</label>
</header>
<div class="fleet-layout">
<div class="dash-card">
<h3 class="rail-sub">Saved</h3>
<ul id="bookmark-list" class="peer-list bookmark-list"></ul>
</div>
<div class="dash-card">
<h3 class="rail-sub">Connected</h3>
<ul id="peer-list" class="peer-list"></ul>
<div class="admin-block">
<button id="btn-invite" class="ghost" disabled>Mint invite</button>
<pre id="invite-out" class="invite-out hidden"></pre>
</div>
</div>
</div>
</section>
<!-- Connect -->
<section id="connect-view" class="view hidden">
<header class="page-header">
<div>
<p class="dash-kicker">Dial</p>
<h1 class="dash-title">Connect</h1>
<p class="page-subtitle">Public key or <code>pd1.</code> invite</p>
</div>
</header>
<div class="dash-card connect-card">
<label>
Agent / invite
<textarea id="connect-input" rows="3" placeholder="64-hex key or pd1.…"></textarea>
</label>
<label>
Alias (optional)
<input id="peer-alias" type="text" maxlength="64" placeholder="homelab-1" autocomplete="off" />
</label>
<label>
Admin seed (optional)
<input id="admin-seed" type="password" autocomplete="off" placeholder="SERVER_SEED" />
</label>
<div class="row">
<button id="btn-connect" class="primary">Connect</button>
<button id="btn-disconnect" class="ghost" disabled>Disconnect</button>
</div>
</div>
</section>
<!-- Settings -->
<section id="settings-view" class="view hidden">
<header class="page-header">
<div>
<p class="dash-kicker">Preferences</p>
<h1 class="dash-title">Settings</h1>
<p class="page-subtitle">Appearance, charts, and desktop behaviour</p>
</div>
</header>
<div class="settings-subtabs" id="settings-tabs" role="tablist">
<button type="button" class="settings-tab active" data-settings-tab="appearance">Appearance</button>
<button type="button" class="settings-tab" data-settings-tab="charts">Charts</button>
<button type="button" class="settings-tab" data-settings-tab="connections">Connections</button>
<button type="button" class="settings-tab" data-settings-tab="notifications">Notifications</button>
<button type="button" class="settings-tab" data-settings-tab="about">About</button>
</div>
<div class="settings-panel active" data-settings-panel="appearance">
<div class="dash-card settings-section">
<h3>Theme</h3>
<div class="seg">
<button type="button" class="seg-btn" data-theme-set="dark">Dark</button>
<button type="button" class="seg-btn" data-theme-set="light">Light</button>
</div>
<h3>Accent</h3>
<div class="accent-swatches" id="accent-swatches">
<button type="button" data-accent="teal" class="swatch" title="Teal"></button>
<button type="button" data-accent="emerald" class="swatch" title="Emerald"></button>
<button type="button" data-accent="sky" class="swatch" title="Sky"></button>
<button type="button" data-accent="amber" class="swatch" title="Amber"></button>
<button type="button" data-accent="rose" class="swatch" title="Rose"></button>
</div>
<h3>Density</h3>
<div class="seg">
<button type="button" class="seg-btn" data-density-set="compact">Compact</button>
<button type="button" class="seg-btn" data-density-set="comfortable">Comfortable</button>
<button type="button" class="seg-btn" data-density-set="spacious">Spacious</button>
</div>
<label class="check-row">
<input type="checkbox" id="setting-reduce-motion" />
Reduce motion
</label>
</div>
</div>
<div class="settings-panel" data-settings-panel="charts">
<div class="dash-card settings-section">
<h3>History window</h3>
<label>
Points per chart
<input id="setting-chart-points" type="number" min="30" max="300" step="10" value="90" />
</label>
<label>
Default spotlight chart
<input id="setting-default-explore" type="text" value="system.io" />
</label>
</div>
</div>
<div class="settings-panel" data-settings-panel="connections">
<div class="dash-card settings-section">
<h3>Multi-agent</h3>
<label class="check-row">
<input type="checkbox" id="setting-auto-restore" checked />
Restore saved agents on launch
</label>
<label>
Max reconnect attempts
<input id="setting-reconnect-max" type="number" min="0" max="100" step="1" value="20" />
</label>
<p class="hint">
Saved agents live in <code>~/.config/peardata/cache/peers.json</code>.
Last active agent is restored when online. Reconnect never steals the active host.
</p>
<button type="button" id="btn-reset-peers" class="ghost">Forget all saved agents</button>
</div>
</div>
<div class="settings-panel" data-settings-panel="notifications">
<div class="dash-card settings-section">
<h3>Desktop</h3>
<label class="check-row">
<input type="checkbox" id="setting-notify" checked />
Enable desktop notifications for alerts
</label>
<p class="hint">Agent webhooks are configured on the server via environment variables.</p>
</div>
</div>
<div class="settings-panel" data-settings-panel="about">
<div class="dash-card settings-section">
<h3>PearData</h3>
<p class="hint">Decentralized P2P real-time host monitoring over HyperDHT.</p>
<pre id="server-info" class="server-info muted">Not connected</pre>
<h3>Log</h3>
<pre id="log" class="log"></pre>
</div>
</div>
</section>
</div>
<script type="module" src="./app.js"></script>
</body>
</html>