Give the Monitor tab the help panel the Scheduler already had
This commit is contained in:
@@ -86,7 +86,12 @@
|
||||
// ═══════════════════════════════════════════════════════════════════════════════════════════════
|
||||
require_once dirname(__DIR__) . '/include/monitor.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();
|
||||
|
||||
// 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>
|
||||
@keyframes vvRsPulse {
|
||||
@@ -348,6 +353,33 @@ if (vv_ai_ui_on()) vv_ai_chat_assets();
|
||||
|
||||
</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>
|
||||
// ── Shared helpers ────────────────────────────────────────────────────────────
|
||||
|
||||
@@ -2601,6 +2633,42 @@ document.addEventListener('click', () => {
|
||||
// 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.
|
||||
|
||||
// ── 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 ───────────────────────────────────────────────────────────────────
|
||||
// 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
|
||||
|
||||
@@ -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.
|
||||
Reference in New Issue
Block a user