From 02e44a04d39e59b20116c5777950eef4930f45fe Mon Sep 17 00:00:00 2001 From: mwiegand Date: Wed, 20 May 2026 22:50:22 +0200 Subject: [PATCH] feat(console): scrollAutoscrollTargets walks ancestors; expose on window Co-Authored-By: Claude Opus 4.7 (1M context) --- l4d2web/l4d2web/static/js/console-history.js | 31 +++++++++++++++----- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/l4d2web/l4d2web/static/js/console-history.js b/l4d2web/l4d2web/static/js/console-history.js index 2af99bd..70e3907 100644 --- a/l4d2web/l4d2web/static/js/console-history.js +++ b/l4d2web/l4d2web/static/js/console-history.js @@ -156,24 +156,39 @@ function bindAllConsoleForms(root) { scope.forEach(bindConsoleForm); } -function scrollConsolesToBottom(root) { +function scrollAutoscrollTargets(root) { if (!root) return; - const scope = root.matches && root.matches("[data-autoscroll]") ? [root] : []; - if (root.querySelectorAll) { - root.querySelectorAll("[data-autoscroll]").forEach((el) => scope.push(el)); + const targets = []; + // Case 1: root itself opts in. + if (root.matches && root.matches("[data-autoscroll]")) { + targets.push(root); } - scope.forEach((el) => { + // Case 2: descendants opt in. + if (root.querySelectorAll) { + root.querySelectorAll("[data-autoscroll]").forEach((el) => targets.push(el)); + } + // Case 3: neither — walk up. Handles htmx:load firing with the inserted + // child as the root after hx-swap="beforeend" on a console line. + if (targets.length === 0 && root.closest) { + const up = root.closest("[data-autoscroll]"); + if (up) targets.push(up); + } + targets.forEach((el) => { el.scrollTop = el.scrollHeight; }); } +// Expose for tabs.js (and any future cross-module consumer). The script +// is `defer`red in base.html, so it runs before DOMContentLoaded and the +// global is defined by the time tabs.js's DCL-deferred initStrips runs. +window.scrollAutoscrollTargets = scrollAutoscrollTargets; + document.addEventListener("DOMContentLoaded", () => { - scrollConsolesToBottom(document); + scrollAutoscrollTargets(document); bindAllConsoleForms(document); }); -// Support HTMX-injected content (mirrors sse.js pattern). document.addEventListener("htmx:load", (event) => { - scrollConsolesToBottom(event.detail.elt); + scrollAutoscrollTargets(event.detail.elt); bindAllConsoleForms(event.detail.elt); });