Files
harmony/fleet/harmony-fleet-operator/vendor/app.js
Jean-Gabriel Gill-Couture 15f908a542
All checks were successful
Run Check Script / check (pull_request) Successful in 2m34s
feat(fleet-operator): live-tail device logs via 2s frontend polling (Ch3)
Fake log streaming by re-fetching the podman-logs tail every 2s from the
frontend (HTMX `every 2s`) — the pragmatic stand-in until a real TTY/streaming
channel (v0.4 #13), per the agreed Ch3 scope.

- log_viewer polls `load, every 2s` with `hx-sync="this:drop"` so slow/offline
  devices can't pile up requests.
- Multi-deployment selector: the poll URL is driven by `hx-include` of the
  `<select>` rather than a baked deployment, so changing the selection is
  picked up by the next poll instead of polling a stale deployment's URL.
  Single/no deployment bakes it into the URL as before.
- app.js: tail-follow only when the user is already at the bottom, so the 2s
  poll doesn't yank them down while reading history.
- Render tests lock the polling trigger and the selector-driven URL behavior.

Deferred: the `LogQuery<T>` Score companion. It's meant to mirror the
smoke-test contract, which doesn't exist yet (Ch10) — building it now would be
a speculative abstraction at n=1. Revisit when the smoke companion lands.
2026-06-05 15:26:10 -04:00

40 lines
1.5 KiB
JavaScript

document.body.addEventListener('htmx:configRequest', (event) => {
event.detail.headers['x-csrf-token'] = '1';
});
// Open a modal dialog swapped into #modal-root. Lives here (not inline)
// because the production CSP forbids inline scripts/handlers.
document.body.addEventListener('htmx:afterSwap', (event) => {
if (!event.target || event.target.id !== 'modal-root') return;
const dialog = event.target.querySelector('dialog');
if (!dialog || typeof dialog.showModal !== 'function') return;
dialog.showModal();
// Backdrop click closes; closing clears the root so it can re-open.
dialog.addEventListener('click', (e) => {
if (e.target === dialog) dialog.close();
});
dialog.addEventListener('close', () => {
event.target.innerHTML = '';
});
});
// Log panels re-fetch every 2s. Tail-follow only when the user is already at
// the bottom, so polling doesn't yank them back down while they read history.
// Record "was pinned" before the swap; honor it after.
function logfeed(el) {
return el && el.classList && el.classList.contains('logfeed') ? el : null;
}
document.body.addEventListener('htmx:beforeSwap', (event) => {
const el = logfeed(event.target);
if (!el) return;
el.dataset.pinned =
el.scrollHeight - el.scrollTop - el.clientHeight < 40 ? '1' : '0';
});
document.body.addEventListener('htmx:afterSwap', (event) => {
const el = logfeed(event.target);
if (!el) return;
// Default (first load, no dataset) follows the tail.
if (el.dataset.pinned !== '0') el.scrollTop = el.scrollHeight;
});