From 86d4f1c2341cfcaf459898e82f84bf0e25cd3a36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B8=D0=BA=D1=82=D0=BE=D1=80?= <78488229+viktor138irk@users.noreply.github.com> Date: Thu, 7 May 2026 21:22:53 +0900 Subject: [PATCH] =?UTF-8?q?=D0=92=D0=B8=D0=B7=D1=83=D0=B0=D0=BB=D1=8C?= =?UTF-8?q?=D0=BD=D1=8B=D0=B5=20=D0=B1=D0=BB=D0=BE=D0=BA=D0=B8=20VM=20?= =?UTF-8?q?=D0=B1=D1=8B=D0=BB=D0=B8=20=D0=BF=D0=BE=D0=B4=D0=BA=D0=BB=D1=8E?= =?UTF-8?q?=D1=87=D0=B5=D0=BD=D1=8B=20=D0=BA=20=D1=82=D0=B5=D1=85=D0=BD?= =?UTF-8?q?=D0=B8=D1=87=D0=B5=D1=81=D0=BA=D0=BE=D0=BC=D1=83=20=D0=B2=D1=8B?= =?UTF-8?q?=D0=B2=D0=BE=D0=B4=D1=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- web/static/panel.js | 121 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 121 insertions(+) diff --git a/web/static/panel.js b/web/static/panel.js index 39897c5..1932601 100644 --- a/web/static/panel.js +++ b/web/static/panel.js @@ -115,6 +115,127 @@ applyTheme(getTheme()); })(); +/* VM visual cards from raw virsh output */ +(() => { + function qs(selector, root = document) { return root.querySelector(selector); } + function qsa(selector, root = document) { return Array.from(root.querySelectorAll(selector)); } + function escapeHtml(value) { + return String(value ?? '').replace(/[&<>"]/g, (ch) => ({ '&': '&', '<': '<', '>': '>', '"': '"' }[ch])); + } + function rawPreBySummary(summaryText) { + const details = qsa('.raw-details').find((item) => (qs('summary', item)?.textContent || '').includes(summaryText)); + return qs('pre', details)?.textContent.trim() || ''; + } + function parseKeyValues(raw) { + const data = {}; + raw.split('\n').forEach((line) => { + const idx = line.indexOf(':'); + if (idx < 0) return; + const key = line.slice(0, idx).trim().toLowerCase(); + const value = line.slice(idx + 1).trim(); + if (key) data[key] = value; + }); + return data; + } + function sizeToGiB(value) { + const raw = String(value || '').trim(); + const n = Number(raw.replace(',', '.').replace(/[^0-9.]/g, '')); + if (!Number.isFinite(n) || n <= 0) return 0; + if (/kib|kb/i.test(raw)) return n / 1024 / 1024; + if (/mib|mb/i.test(raw)) return n / 1024; + if (/gib|gb/i.test(raw)) return n; + return n / 1024 / 1024; + } + function renderInfo() { + const box = qs('#vm-info-visual'); + if (!box) return; + const raw = rawPreBySummary('dominfo'); + const data = parseKeyValues(raw); + const cpu = data['cpu(s)'] || data['cpu'] || '—'; + const state = data.state || 'unknown'; + const osType = data['os type'] || data['os'] || '—'; + const maxMem = sizeToGiB(data['max memory']); + const usedMem = sizeToGiB(data['used memory']); + const memPct = maxMem > 0 ? Math.max(0, Math.min(100, Math.round((usedMem / maxMem) * 100))) : 0; + const cpuPct = Math.max(8, Math.min(100, Number(cpu) ? Number(cpu) * 12 : 18)); + const autostartText = (qsa('.top-meta .status').find((item) => item.textContent.toLowerCase().includes('autostart'))?.textContent || 'autostart: —').replace(/^autostart:\s*/i, ''); + box.innerHTML = ` +