diff --git a/static/js/chat.js b/static/js/chat.js index 10f7e05..6e75dab 100644 --- a/static/js/chat.js +++ b/static/js/chat.js @@ -4037,8 +4037,11 @@ import createResearchSynapse from './researchSynapse.js'; const clickedIndex = allMsgs.indexOf(msgElement); if (clickedIndex < 0) return; + // No early-out on a missing session: an output shown before any model was + // selected (issue #1428) has no session/persisted rows, but its "x" must + // still remove it. We only need the session id for the server-side delete + // below; without one we fall back to removing the DOM. const sessionId = sessionModule.getCurrentSessionId(); - if (!sessionId) return; const clickedIsUser = msgElement.classList.contains('msg-user'); @@ -4114,8 +4117,10 @@ import createResearchSynapse from './researchSynapse.js'; } } - if (!msgIds.length) { - // Fallback: just remove DOM elements if no DB IDs available + if (!msgIds.length || !sessionId) { + // No persisted rows to delete (no DB IDs, or no session at all — e.g. an + // error output shown before a model was selected, #1428). Just remove the + // DOM so the "x" works regardless. domToRemove.forEach(el => el.remove()); if (uiModule) uiModule.showToast('Message deleted'); return; diff --git a/tests/test_delete_message_no_session.py b/tests/test_delete_message_no_session.py new file mode 100644 index 0000000..1ce1cf1 --- /dev/null +++ b/tests/test_delete_message_no_session.py @@ -0,0 +1,34 @@ +"""Regression guard for issue #1428 — the "x" on a chat output did nothing when +no model/API was selected. + +deleteMessage() bailed at `if (!sessionId) return;`. An output shown before a +model is picked has no session and no persisted rows, so the early-out meant the +"x" never even removed the bubble from the DOM. The delete now falls through to +DOM removal when there's no session / no DB ids. + +chat.js pulls in browser globals so it can't run under node; guard at the source. +""" +import re +from pathlib import Path + +SRC = Path(__file__).resolve().parent.parent / "static/js/chat.js" + + +def _delete_message_body() -> str: + text = SRC.read_text(encoding="utf-8") + start = text.index("export async function deleteMessage(") + rest = text[start:] + m = re.search(r"\n export (async )?function ", rest[1:]) + return rest[: m.start() + 1] if m else rest + + +def test_delete_does_not_early_return_on_missing_session(): + body = _delete_message_body() + # The bug was an unconditional early-out when no session existed. + assert not re.search(r"if\s*\(\s*!sessionId\s*\)\s*return\s*;", body), ( + "deleteMessage must not early-return on a missing session (#1428)" + ) + # The DOM-removal fallback must also fire when there's no session. + assert re.search(r"!msgIds\.length\s*\|\|\s*!sessionId", body), ( + "DOM-removal fallback should cover the no-session case" + )