Add state management, WebSocket polling, html.js templating, and refactor pages

- lib/state.py: per-subsystem collectors with versioned state store
- daemon/server.py: state refresh on request, batch routing updates
- webui/static/hoover/html.js: new html tag template helper via htm.js
- webui/static/hoover/websocket.js: real-time state change notifications
- webui/static/hoover/vdom.js: VDOM improvements for keyed diff
- All frontend pages refactored to use html templates
- Add tests for state management and polling
- Update docs and AGENTS.md
This commit is contained in:
2026-06-23 21:11:45 +00:00
parent 5025dfaf30
commit 5ba0f31767
26 changed files with 1193 additions and 495 deletions
+27 -3
View File
@@ -9,6 +9,20 @@
import { esc } from '../helpers.js?v=7';
import { att_esc } from '../helpers.js?v=7';
import { apiSubmit } from '../api.js?v=7';
import { createDom } from '../vdom.js?v=7';
/**
* Render Hoover VNodes into a modal content element.
* VDOM is not diffed across modal re-render — modals are transient and
* innerHTML is cleared/repainted each time (avoids lifecycle baggage).
*/
export function modalVNodes(inner, vnodes) {
inner.innerHTML = '';
const nodes = Array.isArray(vnodes) ? vnodes : [vnodes];
for (const vnode of nodes) {
if (vnode) inner.appendChild(createDom(vnode));
}
}
const _modalQueue = [];
@@ -35,10 +49,15 @@ function _renderModals() {
/**
* Open a modal dialog.
*
* @param {function} renderFn (contentEl, idx) => void, renders into contentEl
* @param {function|object} content Either:
* - renderFn(contentEl, idx) => void (legacy innerHTML path)
* - VNode / VNode[] (new VDOM path — uses modalVNodes)
*/
export function openModal(renderFn) {
_modalQueue.push({ renderFn, id: _modalQueue.length });
export function openModal(content) {
const entry = typeof content === 'function'
? { renderFn: content, id: _modalQueue.length }
: { id: _modalQueue.length, renderFn: (inner) => modalVNodes(inner, content) };
_modalQueue.push(entry);
_renderModals();
}
@@ -61,6 +80,11 @@ export function closeAllModals() {
_renderModals();
}
/** Re-render all open modals. Used by long-lived modals that update in place. */
export function refreshModals() {
_renderModals();
}
/**
* Render a standard modal layout: title, form fields, action buttons.
*