From 121ac1825030ee8f333d103a22db48d337b8143f Mon Sep 17 00:00:00 2001 From: D Stephenson Date: Wed, 13 May 2026 17:22:19 +0000 Subject: [PATCH] Enable template auto-reload and fix scan log UI TEMPLATES_AUTO_RELOAD=True so index.html volume-mount hot-reloads on browser refresh without a container restart. Also: scan log persists after scan with dismiss button when failures exist; slider renamed from Sessions to Parallel with clarifying tooltip. Co-Authored-By: Claude Sonnet 4.6 --- app.py | 1 + index.html | 44 ++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 41 insertions(+), 4 deletions(-) diff --git a/app.py b/app.py index 67d0014..d5f1553 100644 --- a/app.py +++ b/app.py @@ -254,4 +254,5 @@ def api_export_png(): if __name__ == "__main__": + app.config['TEMPLATES_AUTO_RELOAD'] = True app.run(host="0.0.0.0", port=5000, debug=False) diff --git a/index.html b/index.html index e9e2ad3..ecf952b 100644 --- a/index.html +++ b/index.html @@ -416,6 +416,17 @@ var Xr=function(e){if(!(this instanceof Xr))return new Xr(e);this.id="Thenable/1 } .scan-log-line.ok { color: #3fb950; } .scan-log-line.fail { color: #f85149; } + .scan-log-dismiss { + margin-left: auto; + background: none; + border: none; + color: #484f58; + cursor: pointer; + font-size: 14px; + padding: 0 4px; + line-height: 1; + } + .scan-log-dismiss:hover { color: #8b949e; } .spinner { width: 14px; height: 14px; border: 2px solid rgba(79,142,247,.3); @@ -485,7 +496,7 @@ var Xr=function(e){if(!(this instanceof Xr))return new Xr(e);this.id="Thenable/1
- Sessions + Parallel 5
@@ -585,8 +596,9 @@ var Xr=function(e){if(!(this instanceof Xr))return new Xr(e);this.id="Thenable/1
-
+
Scanning... +
@@ -936,6 +948,8 @@ async function pollStatus() { document.getElementById('clearScanBtn').disabled = true; progressWrap.classList.add('visible'); statusBar.classList.add('visible'); + document.getElementById('scanSpinner').style.display = ''; + document.getElementById('scanLogDismiss').style.display = 'none'; const pct = s.total > 0 ? Math.round((s.done / s.total) * 100) : 0; progressBar.style.width = pct + '%'; @@ -964,8 +978,31 @@ async function pollStatus() { const positions = cy.nodes().map(n => ({ id: n.id(), x: n.position('x'), y: n.position('y') })); await fetch('/api/layout', { method: 'POST', headers: {'Content-Type':'application/json'}, body: JSON.stringify({ positions }) }); }, 1000); + + // Update log panel to show completed state + const spinner = document.getElementById('scanSpinner'); + const dismissBtn = document.getElementById('scanLogDismiss'); + const logBody = document.getElementById('scanLogBody'); + const lines = s.log_lines || []; + logBody.innerHTML = lines.map(l => + `
${l.ts}  ${l.text}
` + ).join(''); + logBody.scrollTop = logBody.scrollHeight; + + if (s.fail > 0) { + // Keep panel open so user can read errors + spinner.style.display = 'none'; + statusText.innerHTML = + `Scan complete  —  ` + + `✓ ${s.ok}  ` + + `✗ ${s.fail} failed`; + dismissBtn.style.display = 'block'; + } else { + statusBar.classList.remove('visible'); + spinner.style.display = ''; + dismissBtn.style.display = 'none'; + } } - // Do NOT call loadTopology here on every poll tick scanBtn.disabled = false; document.getElementById('scanElecBtn').disabled = false; @@ -976,7 +1013,6 @@ async function pollStatus() { progressWrap.classList.remove('visible'); progressBar.style.width = '0%'; }, 800); - statusBar.classList.remove('visible'); clearInterval(pollInterval); pollInterval = null;