From be8e65277b6eadff8054e8cc35151ba0bd8af8b1 Mon Sep 17 00:00:00 2001 From: Simone Date: Sun, 26 Jul 2026 01:01:32 +0200 Subject: [PATCH] fix(web): afterSwap used the pre-swap, now-detached target for outerHTML polls htmx's outerHTML swap (every polling card, including Executions) removes the original target node from the DOM as part of the swap, but htmx:afterSwap's eventInfo.target still points at that dead node -- so restoreTimeToggles() was querying a detached subtree every poll and never touching the live buttons. Fall back to the native event target, which htmx dispatches on the freshly-inserted content, whenever detail.target is no longer connected. --- src/ci_orchestrator/web/static/app.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/ci_orchestrator/web/static/app.js b/src/ci_orchestrator/web/static/app.js index 7212718..d7a6041 100644 --- a/src/ci_orchestrator/web/static/app.js +++ b/src/ci_orchestrator/web/static/app.js @@ -241,6 +241,13 @@ document.body.addEventListener("htmx:afterSwap", function (evt) { var target = evt.detail && evt.detail.target; + if (!target || !target.isConnected) { + // eventInfo.target is the pre-swap node. Every polling card swaps + // outerHTML on itself, which detaches that exact node from the DOM -- + // detail.target is then a dead subtree forever. evt.target (the native + // dispatch target) is htmx's freshly-inserted element instead. + target = evt.target; + } if (target && target.id === "action-slot") { processSlot(target); }