feat(health): Little Blue health band — avatar, grouped service tiles, local icons

This commit is contained in:
root
2026-06-02 23:01:08 +10:00
parent b0d54a24cc
commit 9c125cedba
5 changed files with 83 additions and 2 deletions

View File

@@ -0,0 +1,27 @@
import { el, mount } from '../dom.js';
import { api } from '../api.js';
import { littleblueAvatar } from '../components/littleblue_avatar.js';
import { serviceTile } from '../components/service_tile.js';
const TITLE = { agents: 'Agents', infrastructure: 'Infrastructure', media: 'Media', other: 'Other' };
let host, timer;
async function load() {
if (!host) return;
try {
const groups = await api.get('/api/health/services');
const sections = groups.map(g =>
el('div', { class: 'lb-section' },
el('div', { class: 'lb-group' },
el('span', { class: 'gname' }, TITLE[g.category] || g.category),
el('span', { class: 'gcount' }, `${g.healthy}/${g.total} healthy`),
el('span', { class: 'line' })),
el('div', { class: 'tiles' }, g.services.map(serviceTile))));
mount(host,
el('div', { class: 'lbwrap' }, littleblueAvatar(),
el('div', {}, el('div', { class: 'lb-name' }, 'Little Blue'),
el('div', { class: 'lb-sub' }, 'Health & Uptime of the lab'))),
sections);
} catch { mount(host, el('span', { class: 'muted' }, 'Health band unavailable')); }
}
export function renderHealthBand(el_) { host = el_; load(); timer = setInterval(load, 60000); }
export function stopHealthBand() { clearInterval(timer); host = null; }

View File

@@ -1,5 +1,6 @@
import { el, mount } from '../dom.js';
import { api } from '../api.js';
import { renderHealthBand, stopHealthBand } from './health_band.js';
import { svCard } from '../components/sv_card.js';
import { attachReorder } from '../components/sv_reorder.js';
import { orderCards } from './cards/registry.js';
@@ -15,7 +16,7 @@ const CARD_MODULES = [clock, weather, hostPerf, jobs, inbox, search, speedtest];
let active = []; // mounted cards needing stop()
export async function render(main) {
active.forEach(c => c.stop && c.stop()); active = [];
active.forEach(c => c.stop && c.stop()); active = []; stopHealthBand();
mount(main,
el('h1', { class: 'view-h1' }, 'Sacred Valley'),
el('p', { class: 'view-sub' }, 'The homelab, at a glance.'),
@@ -43,5 +44,5 @@ export async function render(main) {
try { await api.put('/api/dashboard/layout', { ...layout, card_order: newOrder }); layout.card_order = newOrder; }
catch (e) { console.error('save layout', e); }
});
// health band wiring arrives in Task 22.
renderHealthBand(document.getElementById('sv-health'));
}