From bdb3f069e5ba1ea87964b533d88a2df1a974b383 Mon Sep 17 00:00:00 2001 From: Brian Krabach Date: Wed, 1 Apr 2026 12:48:38 -0700 Subject: [PATCH] fix: delete active session returns to dashboard, sidebar/tile delete doesn't navigate Bug 1: killSession() now checks if _viewingSession === name and calls closeSession() before pollSessions(). Previously the user was stuck in the expanded terminal view for a dead session after deleting it. Bug 2: Sidebar item click handler now ignores clicks on .sidebar-delete buttons. Tile click handler now ignores clicks on .tile-delete buttons. Previously stopPropagation() in the document-level delete handler fired too late (after the sidebar-item/tile handlers had already called openSession), causing the session to be navigated to before being killed. Tests added: - killSession closes active session and returns to dashboard - sidebar click handler ignores clicks on delete button - tile click handler ignores clicks on tile-delete button 321 JS tests pass, 760 Python tests pass. --- muxplex/frontend/app.js | 14 ++++++++++++-- muxplex/frontend/tests/test_app.mjs | 27 +++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/muxplex/frontend/app.js b/muxplex/frontend/app.js index 9492e41..b038c13 100644 --- a/muxplex/frontend/app.js +++ b/muxplex/frontend/app.js @@ -733,7 +733,9 @@ function renderSidebar(sessions, currentSession) { list.querySelectorAll('.sidebar-item').forEach((item) => { const name = item.dataset.session; const sourceUrl = item.dataset.sourceUrl || ''; - on(item, 'click', () => { + on(item, 'click', (e) => { + // Don't navigate when clicking the delete button inside the item + if (e.target.closest && e.target.closest('.sidebar-delete')) return; if (name !== currentSession) openSession(name, { sourceUrl }); }); }); @@ -992,7 +994,11 @@ function renderGrid(sessions) { // Bind interaction handlers on each tile document.querySelectorAll('.session-tile').forEach(function(tile) { - on(tile, 'click', () => openSession(tile.dataset.session, { sourceUrl: tile.dataset.sourceUrl || '' })); + on(tile, 'click', (e) => { + // Don't navigate when clicking the delete button inside the tile + if (e.target.closest && e.target.closest('.tile-delete')) return; + openSession(tile.dataset.session, { sourceUrl: tile.dataset.sourceUrl || '' }); + }); on(tile, 'keydown', (e) => { if (e.key === 'Enter' || e.key === ' ') { openSession(tile.dataset.session, { sourceUrl: tile.dataset.sourceUrl || '' }); @@ -2237,6 +2243,10 @@ function killSession(name) { api('DELETE', '/api/sessions/' + name) .then(function() { showToast('Session \'' + name + '\' killed'); + // If we deleted the session we're currently viewing, return to dashboard + if (_viewingSession === name) { + closeSession(); + } pollSessions(); }) .catch(function(err) { diff --git a/muxplex/frontend/tests/test_app.mjs b/muxplex/frontend/tests/test_app.mjs index 1459b0c..3598fc2 100644 --- a/muxplex/frontend/tests/test_app.mjs +++ b/muxplex/frontend/tests/test_app.mjs @@ -4406,3 +4406,30 @@ test('buildSidebarHTML trim happens BEFORE slice in source (structural order che 'trailing blank trim (.pop()) must appear BEFORE .slice() in buildSidebarHTML — trim the full snapshot first, then slice the last 20 lines', ); }); + +// --- Bug fixes: delete UX --- + +test('killSession closes active session and returns to dashboard', () => { + const source = fs.readFileSync(new URL('../app.js', import.meta.url), 'utf8'); + // Find killSession function body + const fnStart = source.indexOf('function killSession'); + const fnBody = source.substring(fnStart, fnStart + 500); + assert.ok(fnBody.includes('_viewingSession'), 'killSession must check if deleted session is the active one'); + assert.ok(fnBody.includes('closeSession'), 'killSession must call closeSession when deleting the active session'); +}); + +test('sidebar click handler ignores clicks on delete button', () => { + const source = fs.readFileSync(new URL('../app.js', import.meta.url), 'utf8'); + assert.ok( + source.includes("closest('.sidebar-delete')"), + "sidebar click handler must guard against clicks on .sidebar-delete button" + ); +}); + +test('tile click handler ignores clicks on tile-delete button', () => { + const source = fs.readFileSync(new URL('../app.js', import.meta.url), 'utf8'); + assert.ok( + source.includes("closest('.tile-delete')"), + "tile click handler must guard against clicks on .tile-delete button" + ); +});