diff --git a/l4d2web/static/js/console-history.js b/l4d2web/static/js/console-history.js index 34015e0..2af99bd 100644 --- a/l4d2web/static/js/console-history.js +++ b/l4d2web/static/js/console-history.js @@ -18,10 +18,12 @@ function bindConsoleForm(form) { // Entries are stored newest-first to match the API response shape. let cache = []; let cacheLoaded = false; + let loadingPromise = null; let cursor = -1; // -1 = "not in history" (at the live input) let snapshot = ""; // saved input value for restoring via ArrowDown let oldestId = null; // id of the oldest cached entry, used for pagination let exhausted = false; // true when we've fetched all available history + let pendingCommand = null; // captured before HTMX submit; used by afterRequest async function loadHistory(params) { const url = new URL(`/servers/${serverId}/console/history`, location.origin); @@ -40,15 +42,20 @@ function bindConsoleForm(form) { async function ensureLoaded() { if (cacheLoaded) return; - cacheLoaded = true; - const entries = await loadHistory(); - cache = entries; // newest-first from API - if (cache.length > 0) { - oldestId = cache[cache.length - 1].id; - } - if (entries.length < 50) { - exhausted = true; + if (!loadingPromise) { + loadingPromise = (async () => { + const entries = await loadHistory(); + cache = entries; // newest-first from API + if (cache.length > 0) { + oldestId = cache[cache.length - 1].id; + } + if (entries.length < 50) { + exhausted = true; + } + cacheLoaded = true; + })(); } + await loadingPromise; } async function fetchOlderPage() { @@ -116,13 +123,23 @@ function bindConsoleForm(form) { } }); + // Capture the command value before HTMX submits the form; avoids relying on + // event.detail.requestConfig.parameters which changed between HTMX 1.x / 2.x. + form.addEventListener("htmx:beforeRequest", () => { + const commandInput = form.querySelector("input[name='command']"); + pendingCommand = commandInput ? commandInput.value : null; + }); + // After a successful HTMX POST: prepend the sent command to cache. form.addEventListener("htmx:afterRequest", (event) => { if (!event.detail.successful) return; - const params = event.detail.requestConfig && event.detail.requestConfig.parameters; - const command = (params && params.command) || snapshot || ""; + // Use the value captured before the request; fall back to snapshot. + const command = pendingCommand || snapshot || ""; + pendingCommand = null; if (!command) return; - // Prepend as the newest entry (id=null is a placeholder). + // Prepend as the newest entry. + // id=null is safe here: pagination uses oldestId (the real persisted-row id) + // for ?before= queries, so a synthetic null-id entry doesn't break paging. cache.unshift({ id: null, command }); // Reset cursor so ArrowUp immediately recalls this command. cursor = -1;