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/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.
|
||||||
Reference in New Issue
Block a user