From b62bd3e36168cefdcbf78627641e1e47b94878ec Mon Sep 17 00:00:00 2001 From: Brian Krabach Date: Fri, 27 Mar 2026 17:49:07 -0700 Subject: [PATCH] feat: add overlay click-away handler for sidebar on narrow screens --- frontend/app.js | 25 +++++++++++++++++++++++++ frontend/tests/test_app.mjs | 23 +++++++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/frontend/app.js b/frontend/app.js index e438e24..20bad99 100644 --- a/frontend/app.js +++ b/frontend/app.js @@ -448,6 +448,29 @@ function toggleSidebar() { } } +/** + * Bind a click-away handler on #terminal-container that collapses the sidebar + * when the user taps outside of it in overlay mode (window.innerWidth < 960). + * Returns early without collapsing if: + * - the screen is wide enough that the sidebar is not in overlay mode (>= 960px) + * - the sidebar element is missing + * - the sidebar is already collapsed + */ +function bindSidebarClickAway() { + const container = $('terminal-container'); + if (!container) return; + container.addEventListener('click', () => { + if (window.innerWidth >= SIDEBAR_NARROW_THRESHOLD) return; + const sidebar = $('sidebar'); + if (!sidebar) return; + if (sidebar.classList.contains('sidebar--collapsed')) return; + sidebar.classList.add('sidebar--collapsed'); + try { + localStorage.setItem(SIDEBAR_KEY, JSON.stringify(false)); + } catch (_) { /* blocked — ok */ } + }); +} + /** * Render the session grid. Shows empty state when no sessions exist. * On mobile, sorts sessions by priority before rendering. @@ -972,6 +995,7 @@ function bindStaticEventListeners() { on($('back-btn'), 'click', closeSession); on($('sidebar-toggle-btn'), 'click', toggleSidebar); on($('sidebar-collapse-btn'), 'click', toggleSidebar); + bindSidebarClickAway(); on($('palette-trigger'), 'click', openPalette); on($('palette-backdrop'), 'click', closePalette); document.addEventListener('keydown', handleGlobalKeydown); @@ -1058,6 +1082,7 @@ if (typeof module !== 'undefined' && module.exports) { renderSidebar, initSidebar, toggleSidebar, + bindSidebarClickAway, renderGrid, requestNotificationPermission, handleBellTransitions, diff --git a/frontend/tests/test_app.mjs b/frontend/tests/test_app.mjs index 189ca4a..7c8b161 100644 --- a/frontend/tests/test_app.mjs +++ b/frontend/tests/test_app.mjs @@ -2001,3 +2001,26 @@ test('bindStaticEventListeners binds sidebar-toggle-btn and sidebar-collapse-btn globalThis.document.getElementById = origGetById; globalThis.document.addEventListener = origDocAddListener; }); + +// --- bindSidebarClickAway --- + +test('bindSidebarClickAway is exported and callable', () => { + assert.strictEqual(typeof app.bindSidebarClickAway, 'function', 'bindSidebarClickAway should be a function'); +}); + +test('bindSidebarClickAway registers click listener on terminal-container', () => { + let addEventListenerCalledWith = null; + const mockTerminalContainer = { + addEventListener: (ev, fn) => { addEventListenerCalledWith = ev; }, + }; + const origGetById = globalThis.document.getElementById; + globalThis.document.getElementById = (id) => { + if (id === 'terminal-container') return mockTerminalContainer; + return null; + }; + + app.bindSidebarClickAway(); + + assert.strictEqual(addEventListenerCalledWith, 'click', 'bindSidebarClickAway should register a click listener on terminal-container'); + globalThis.document.getElementById = origGetById; +});