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
+53
View File
@@ -261,6 +261,59 @@ h('#comp', { component: MyPage, key: '/dashboard' }, [])
**Children flattening:** `null`, `undefined`, and `false` children are filtered out. String and number primitives are automatically converted to text VNodes.
### HTM (Tagged HTML Templates)
Hoover ships with **htm** for JSX-like template syntax using tagged template literals. Import and use:
```javascript
import { html, Badge, ConfirmDelete } from '/static/hoover/index.js';
// Instead of:
h('div', { class: 'card' },
h('h3', { style: 'color:red' }, 'Title'),
h('button', { 'on:click': handler }, 'Click')
)
// Write:
html`<div class="card">
<h3 style="color:red">Title</h3>
<button onClick=${handler}>Click</button>
</div>`
```
**Event naming:** Use camelCase `onClick=${fn}` — the adapter translates events to Hoover's `on:click` convention. Any attribute starting with `on` followed by a capital letter (e.g., `onSubmit`, `onChange`) is converted.
**Component syntax:** Use `<${Component}>` syntax for inline components:
```javascript
html`<${Badge} text=${val} variant="info" />`
html`<${ConfirmDelete} url=${url} message=${msg} success="Deleted" refresh="firewall" />`
```
**Interpolation:** Values are interpolated with `${...}`. Use `esc()` for user-controlled text:
```javascript
html`<tr key=${item.id}>
<td>${esc(item.name)}</td>
<td>${item.value}</td>
</tr>`
```
**Spread attributes:** Use `...${props}` to spread an object as props:
```javascript
html`<${Badge} ...${badgeProps} />`
```
**Boolean attributes:** Use `html`<${Badge} readonly />`` for boolean attributes.
**Coexistence with `h()`:** Both `h` and `html` are exported from the barrel. Use whichever is clearer for the given context. Simple elements are often shorter with `h()`, while complex nested structures benefit from `html`.
**Limitations:**
- No `<Badge>...</Badge>` closing syntax — must use self-closing `<${Badge} ... />` or full `<${Badge} ... ></${Badge}>` syntax
- No control flow (`if/for`) in templates — use JavaScript conditionals and `.map()` before interpolation
- `esc()` is still required for user-controlled text to prevent XSS
### Props
| Prop | Behavior |