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" + ); +});