diff --git a/Plugin/unraid/pages/ai.php b/Plugin/unraid/pages/ai.php
index 1410962..18a7394 100644
--- a/Plugin/unraid/pages/ai.php
+++ b/Plugin/unraid/pages/ai.php
@@ -341,10 +341,22 @@
+ `
${esc(text)}
`));
scroll();
}
+ let pendingTimer = null;
function addPending() {
const n = el(``);
+ + ``
+ + `starting…`
+ + `
`);
chat().appendChild(n); scroll();
+ // An elapsed counter distinguishes "working" from "wedged" at a glance. Without it a stalled
+ // turn and a slow one look identical, and the slow case here is legitimately ~40s.
+ const t0 = Date.now();
+ clearInterval(pendingTimer);
+ pendingTimer = setInterval(() => {
+ const e = $('vv-ai-elapsed');
+ if (!e) { clearInterval(pendingTimer); return; }
+ e.textContent = Math.round((Date.now() - t0) / 1000) + 's';
+ }, 1000);
}
function phase(t) { const p = $('vv-ai-phase'); if (p) p.textContent = t; }
@@ -389,7 +401,16 @@
// ── Ask / poll ──────────────────────────────────────────────────────────
function send() {
- if (busy) return;
+ // Never fail silently on a stuck flag. A turn that ends without finish() — a throw, a poll
+ // loop that stopped, a tab left open across a deploy — would otherwise make every later
+ // click a no-op with the previous "starting…" still on screen, which reads as a hang that
+ // produces no request and therefore no server-side trace at all. That cost a diagnosis
+ // session; it now says so and offers the way out.
+ if (busy) {
+ addError('A previous question is still marked in-flight, so this one was not sent. '
+ + 'Reload the tab to reset it.');
+ return;
+ }
const q = $('vv-ai-input').value.trim();
if (!q) return;
@@ -439,7 +460,11 @@
.catch(e => { addError('Request failed: ' + (e && e.message ? e.message : e)); finish(); });
}
- function finish() { busy = false; $('vv-ai-send').disabled = false; }
+ function finish() {
+ busy = false;
+ $('vv-ai-send').disabled = false;
+ clearInterval(pendingTimer);
+ }
function poll(token, started) {
if (Date.now() - started > POLL_CEIL) {