Give the Monitor tab the help panel the Scheduler already had

This commit is contained in:
Gmer4Lfe
2026-08-09 00:06:28 -04:00
parent 3a45c18555
commit a011cd5e3b
2 changed files with 183 additions and 0 deletions
+68
View File
@@ -86,7 +86,12 @@
// ═══════════════════════════════════════════════════════════════════════════════════════════════ // ═══════════════════════════════════════════════════════════════════════════════════════════════
require_once dirname(__DIR__) . '/include/monitor.php'; require_once dirname(__DIR__) . '/include/monitor.php';
require_once dirname(__DIR__) . '/include/ai_chat.php'; require_once dirname(__DIR__) . '/include/ai_chat.php';
require_once dirname(__DIR__) . '/include/docs.php';
if (vv_ai_ui_on()) vv_ai_chat_assets(); if (vv_ai_ui_on()) vv_ai_chat_assets();
// Live values for the `$VAR` markers in pages/readme/monitor-readme.md. Same arrangement the
// Scheduler tab uses: the doc names a conf variable and the reader sees this host's value.
$_vv_doc_vars = array_merge(vv_conf_vars(), ['SCRIPTS_DIR' => SCRIPTS_DIR]);
?> ?>
<style> <style>
@keyframes vvRsPulse { @keyframes vvRsPulse {
@@ -348,6 +353,33 @@ if (vv_ai_ui_on()) vv_ai_chat_assets();
</div> </div>
<!-- ── How to read this page ────────────────────────────────────────────────────
Rendered from pages/readme/monitor-readme.md, not maintained here. That file is also what
the AI tab retrieves, so the panel you read and the answer the assistant gives are the same
text and cannot drift.
Below the grid and collapsed by default: this page is watched, not read, and help that
pushed the cards down would be in the way every time you opened the tab for the reason you
usually open it. Two views of one file — the brief list is one line per card, "More info"
swaps in the full document for the parts that need explaining. -->
<div class="vv-sug-block" data-save-key="monitor-howto" style="margin-top:12px;">
<div class="vv-sug-header" onclick="vvMonToggleHelp(this)">
<span class="vv-sug-chevron">▸</span>
<span class="vv-sug-title">How to read this page</span>
<button class="vv-more-btn" id="vv-mon-howto-more" type="button"
onclick="event.stopPropagation(); vvMonToggleMore(this)">More info</button>
</div>
<div class="vv-sug-body vv-info-body" style="display:none">
<ul class="vv-info-cols" id="vv-mon-howto-brief">
<?= vv_docs_brief('Plugin/unraid/pages/readme/monitor-readme.md', $_vv_doc_vars,
fn($h) => is_string($h) && str_starts_with($h, 'Reference —')) ?>
</ul>
<div class="vv-doc" id="vv-mon-howto-full" style="display:none">
<?= vv_docs_render('Plugin/unraid/pages/readme/monitor-readme.md', $_vv_doc_vars) ?>
</div>
</div>
</div>
<script> <script>
// ── Shared helpers ──────────────────────────────────────────────────────────── // ── Shared helpers ────────────────────────────────────────────────────────────
@@ -2601,6 +2633,42 @@ document.addEventListener('click', () => {
// Removed rather than left as an unreachable handler for the platform's three most destructive // Removed rather than left as an unreachable handler for the platform's three most destructive
// operations. The endpoint stays; see its header for why it is kept unwired. // operations. The endpoint stays; see its header for why it is kept unwired.
// ── Help panel ───────────────────────────────────────────────────────────────
// Collapse state and depth are both remembered. Which one you want is a preference, not a
// per-visit decision — and on a page you leave open, re-collapsing help you deliberately opened
// would be its own small annoyance.
function vvMonToggleHelp(header) {
const body = header.nextElementSibling;
const chevron = header.querySelector('.vv-sug-chevron');
const open = body.style.display !== 'none';
body.style.display = open ? 'none' : '';
chevron.textContent = open ? '▸' : '▾';
localStorage.setItem('vv-sug-monitor-howto', open ? '0' : '1');
}
function vvMonToggleMore(btn) {
const brief = document.getElementById('vv-mon-howto-brief');
const full = document.getElementById('vv-mon-howto-full');
if (!brief || !full) return;
const showFull = full.style.display === 'none';
full.style.display = showFull ? '' : 'none';
brief.style.display = showFull ? 'none' : '';
btn.textContent = showFull ? 'Less' : 'More info';
btn.classList.toggle('vv-more-on', showFull);
localStorage.setItem('vv-mon-howto-more', showFull ? '1' : '0');
}
(function vvMonRestoreHelp() {
if (localStorage.getItem('vv-sug-monitor-howto') === '1') {
const h = document.querySelector('[data-save-key="monitor-howto"] .vv-sug-header');
if (h) vvMonToggleHelp(h);
}
if (localStorage.getItem('vv-mon-howto-more') === '1') {
const b = document.getElementById('vv-mon-howto-more');
if (b) vvMonToggleMore(b);
}
})();
// ── AI row ─────────────────────────────────────────────────────────────────── // ── AI row ───────────────────────────────────────────────────────────────────
// Constructed only when the row rendered. The card markup is behind vv_ai_ui_on(), so on any // Constructed only when the row rendered. The card markup is behind vv_ai_ui_on(), so on any
// other host these ids do not exist and the factories are never called — the row is absent // other host these ids do not exist and the factories are never called — the row is absent
@@ -0,0 +1,115 @@
# Monitor
Everything this host is doing right now, plus each partner. Nothing on this tab changes the
system except the container controls — the rest is observation.
Two poll rates, deliberately split: CPU, memory and network refresh every second; everything
else every five. The slower payload is served from a cache the background writer refreshes once
a minute, so polling faster could not make it newer — it only decides how soon the page notices.
**Cards you do not have do not appear.** No GPU, no UPS, no VMs, no partner — the card is absent
rather than showing zeros. An empty card would be a permanent reminder of nothing.
---
## Reference — System, power, CPU, memory, network
| Card | What it tells you |
|---|---|
| System | Hostname, Unraid version, uptime, array state |
| Power | UPS load, battery and runtime. Absent when no UPS is configured |
| CPU | Per-core utilisation with a 2-minute history, plus package temperature |
| Memory | Used / cached / free, broken out rather than a single percentage |
| Network | Live rx/tx with a 2-minute history |
## Reference — Jobs, fallback, partner, containers
| Card | What it tells you |
|---|---|
| Scripts | What ran recently and how it exited. Click a row to open its log |
| Fallback | `NORMAL`, `FALLBACK`, `NO_INTERNET` or `DARK` — which tier this host is in |
| Partner | Each partner's uptime, load, memory and array state, over the mesh |
| Containers & VMs | Grouped by folder. Start and stop from here; stopping asks first |
## Reference — Sync, GPUs, transcodes, streams
| Card | What it tells you |
|---|---|
| Rsync | Last run per share, duration and outcome |
| GPU | Utilisation, VRAM and temperature, one card per GPU |
| Transcode | Ramdisk usage, SSD fallback, and the sessions currently running |
| Streams | What is playing now across Emby, Jellyfin and Plex |
## Reference — Health and storage
| Card | What it tells you |
|---|---|
| Watchdog | Roll-up across every watchdog. Healthy requires *every* strike set to be clear |
| Parity | Last check, its result, and when the next one is due |
| Pools | Each cache pool with its per-disk breakdown and live I/O |
| Array | Every array disk, its temperature against the warning thresholds, and live I/O |
## Reference — AI
Present only on the AI host with `$AI_ENABLED` true. Absent everywhere else, on the same rule
as the GPU and UPS cards.
| Card | What it tells you |
|---|---|
| AI | Model residency, context size, VRAM, index size and staleness, tokens used today |
| Conversations | The last `$AI_CHAT_HISTORY_MAX` chats. Click to reopen, × to delete |
| Assistant | Ask a question without leaving the dashboard |
---
## The AI card leads with offload, not with size
`GPU offload` is the number worth watching. At 100% the model is fully resident and answers at
roughly 60 tokens per second on this hardware; with even a few layers pushed to CPU that falls
to about a quarter of it. Nothing else in the WebGUI surfaces it, and a model that has quietly
fallen back to partial offload is invisible until answers merely *feel* slow.
`Index` reports chunks and whether the index is stale. Stale means a tracked file is newer than
the last build — the assistant will still answer confidently, out of code that has since changed.
That is the one failure a grounded answer cannot reveal on its own, so it is stated rather than
implied.
These figures come from the same one-minute collection the AI tab's banner uses, so the two
cannot disagree about whether the model is loaded.
## The assistant here starts on General Chat
The AI tab starts on Varaverk Assistant, which answers only from this installation's
documentation and cites its sources. This card starts on General Chat instead, because the jobs
are different: the tab is where you go to interrogate the system, this is the box you type an
idle question into while watching the dashboard.
Nothing is lost by starting loose. A question that is genuinely about this installation is handed
to the strict profile automatically, and handed back if the documentation turns out not to cover
it. Starting strict would refuse ordinary questions to guard against a mistake the server already
prevents.
Switching profile with the buttons keeps what is on screen but stops sending earlier turns to the
model. Carrying cited, retrieval-grounded answers into a mode with no retrieval makes the model
keep referring to sources it can no longer see.
## Conversations are shared with the AI tab
The same store backs both. Start a thread here, finish it on the AI tab, or the reverse — it is
one conversation, not two. Chats save themselves when a turn completes; there is no Save button,
because a conversation worth keeping is not reliably one you knew was worth keeping while you
were having it. The oldest drops off past `$AI_CHAT_HISTORY_MAX`.
Reopening a chat shows the turns as plain text. Sources, reasoning and timings describe one
generation and are not stored — redrawing them beside a transcript you may continue under a
different profile would be citing evidence for an answer that is no longer being made.
## What this tab will not do
It will not clear a watchdog strike, reset a counter, or start a parity check. Strike state
belongs to the watchdog that owns it, and a page that cleared strikes would silently undo an
escalation something was deliberately building toward.
Stopping a container asks first. Starting one does not — and either way the endpoint validates
the request against real inventory, so a stale page cannot act on a container that no longer
exists.