From 8e4f17068d1a6bbc493208005c4db862a063a604 Mon Sep 17 00:00:00 2001 From: Brian Krabach Date: Sun, 29 Mar 2026 22:28:20 -0700 Subject: [PATCH] refactor: remove command palette JavaScript --- muxplex/frontend/app.js | 222 +----------- ...t_frontend_js.cpython-312-pytest-9.0.2.pyc | Bin 0 -> 33121 bytes muxplex/tests/test_frontend_js.py | 330 ++++++++++++++++++ 3 files changed, 332 insertions(+), 220 deletions(-) create mode 100644 muxplex/tests/__pycache__/test_frontend_js.cpython-312-pytest-9.0.2.pyc create mode 100644 muxplex/tests/test_frontend_js.py diff --git a/muxplex/frontend/app.js b/muxplex/frontend/app.js index 0e21afe..4e65092 100644 --- a/muxplex/frontend/app.js +++ b/muxplex/frontend/app.js @@ -898,187 +898,14 @@ function _setViewingSession(name) { _viewingSession = name; } -// ─── Command palette state ──────────────────────────────────────────────────── -const PALETTE_MAX_ITEMS = 9; -let _paletteSelectedIndex = 0; -let _paletteFilteredSessions = []; -let _paletteOpen = false; -let _paletteInputListener = null; - -// ─── Command palette functions ──────────────────────────────────────────────── - -/** - * Render the filtered session list inside #palette-list. - * Shows up to 9 items. Each item is a
  • with index number, - * session name, optional bell emoji, and timestamp. - */ -function renderPaletteList() { - const list = $('palette-list'); - if (!list) return; - - const items = _paletteFilteredSessions.slice(0, PALETTE_MAX_ITEMS); - list.innerHTML = items - .map((session, i) => { - const isBell = sessionPriority(session) === 'bell'; - const bell = isBell ? ' 🔔' : ''; - const time = formatTimestamp(session.last_activity_at || null); - const name = escapeHtml(session.name || ''); - return `
  • ${i + 1} ${name}${bell} ${escapeHtml(time)}
  • `; - }) - .join(''); - - // Bind click handlers on each item - list.querySelectorAll('.palette-item').forEach((item) => { - on(item, 'click', () => { - const idx = parseInt(item.dataset.index, 10); - const session = _paletteFilteredSessions[idx]; - if (session) { - closePalette(); - openSession(session.name).catch((err) => console.error('[renderPaletteList]', err)); - } - }); - }); - - highlightPaletteItem(_paletteSelectedIndex); -} - -/** - * Toggle the palette-item--selected class on the item at `index`. - * @param {number} index - */ -function highlightPaletteItem(index) { - const list = $('palette-list'); - if (!list) return; - list.querySelectorAll('.palette-item').forEach((item, i) => { - if (i === index) { - item.classList.add('palette-item--selected'); - } else { - item.classList.remove('palette-item--selected'); - } - }); -} - -/** - * Open the command palette. - * Shows #command-palette, copies _currentSessions to _paletteFilteredSessions, - * renders the list, resets selection index, focuses #palette-input, and binds - * the input event listener. - */ -function openPalette() { - _paletteOpen = true; - _paletteFilteredSessions = _currentSessions.slice(); - _paletteSelectedIndex = 0; - - const palette = $('command-palette'); - if (palette) palette.classList.remove('hidden'); // palette starts with hidden class - - renderPaletteList(); - - const input = $('palette-input'); - if (input) { - input.value = ''; - input.focus(); - if (_paletteInputListener) { - input.removeEventListener('input', _paletteInputListener); - } - _paletteInputListener = onPaletteInput; - input.addEventListener('input', _paletteInputListener); - } -} - -/** - * Close the command palette. - * Hides #command-palette and removes the input event listener. - */ -function closePalette() { - _paletteOpen = false; - - const palette = $('command-palette'); - if (palette) palette.classList.add('hidden'); - - const input = $('palette-input'); - if (input && _paletteInputListener) { - input.removeEventListener('input', _paletteInputListener); - _paletteInputListener = null; - } -} - -/** - * Handle input events on #palette-input. - * Filters sessions by the current query, re-renders the list, resets selection. - * @param {Event} e - */ -function onPaletteInput(e) { - const query = e && e.target ? e.target.value : ''; - _paletteFilteredSessions = filterByQuery(_currentSessions, query); - _paletteSelectedIndex = 0; - renderPaletteList(); -} - -/** - * Handle keydown events inside the command palette. - * ArrowDown/Up moves selection, Enter opens selected session, - * Escape closes palette, G closes palette + returns to grid, - * number keys 1-9 jump directly to that item. - * @param {KeyboardEvent} e - * @returns {Promise} - */ -async function handlePaletteKeydown(e) { - const visibleCount = Math.min(_paletteFilteredSessions.length, PALETTE_MAX_ITEMS); - - if (e.key === 'Escape') { - e.preventDefault(); - closePalette(); - } else if (e.key === 'g' || e.key === 'G') { - e.preventDefault(); - closePalette(); - await closeSession(); - } else if (visibleCount > 0) { - if (e.key === 'ArrowDown') { - e.preventDefault(); - _paletteSelectedIndex = (_paletteSelectedIndex + 1) % visibleCount; - highlightPaletteItem(_paletteSelectedIndex); - } else if (e.key === 'ArrowUp') { - e.preventDefault(); - _paletteSelectedIndex = (_paletteSelectedIndex - 1 + visibleCount) % visibleCount; - highlightPaletteItem(_paletteSelectedIndex); - } else if (e.key === 'Enter') { - e.preventDefault(); - const session = _paletteFilteredSessions[_paletteSelectedIndex]; - if (session) { - closePalette(); - await openSession(session.name); - } - } else if (e.key >= '1' && e.key <= '9') { - const idx = parseInt(e.key, 10) - 1; - if (idx < visibleCount) { - e.preventDefault(); - const session = _paletteFilteredSessions[idx]; - closePalette(); - await openSession(session.name); - } - } - } -} - /** * Global keydown handler. - * When palette is open: delegates to handlePaletteKeydown. - * When in fullscreen with palette closed: backtick or Ctrl+K opens palette, - * Escape returns to grid. + * When in fullscreen: Escape returns to grid. * @param {KeyboardEvent} e */ function handleGlobalKeydown(e) { - if (_paletteOpen) { - handlePaletteKeydown(e).catch((err) => console.error('[handleGlobalKeydown]', err)); - return; - } - if (_viewMode === 'fullscreen') { - if (e.key === '`' || (e.ctrlKey && e.key === 'k')) { - e.preventDefault(); - openPalette(); - } else if (e.key === 'Escape') { + if (e.key === 'Escape') { e.preventDefault(); closeSession(); } @@ -1166,8 +993,6 @@ function bindStaticEventListeners() { 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); on($('session-pill'), 'click', openBottomSheet); on($('sheet-backdrop'), 'click', closeBottomSheet); @@ -1216,36 +1041,6 @@ function _setCurrentSessions(sessions) { _currentSessions = sessions; } -/** Test-only: set _paletteFilteredSessions directly. */ -function _setPaletteFilteredSessions(sessions) { - _paletteFilteredSessions = sessions; -} - -/** Test-only: get _paletteFilteredSessions. */ -function _getPaletteFilteredSessions() { - return _paletteFilteredSessions; -} - -/** Test-only: set _paletteSelectedIndex directly. */ -function _setPaletteSelectedIndex(index) { - _paletteSelectedIndex = index; -} - -/** Test-only: get _paletteSelectedIndex. */ -function _getPaletteSelectedIndex() { - return _paletteSelectedIndex; -} - -/** Test-only: set _paletteOpen directly. */ -function _setPaletteOpen(val) { - _paletteOpen = val; -} - -/** Test-only: get _paletteOpen. */ -function _isPaletteOpen() { - return _paletteOpen; -} - /** Test-only: set _viewMode directly. */ function _setViewMode(mode) { _viewMode = mode; @@ -1301,13 +1096,6 @@ if (typeof module !== 'undefined' && module.exports) { openSession, closeSession, _setViewingSession, - // Command palette - renderPaletteList, - highlightPaletteItem, - openPalette, - closePalette, - onPaletteInput, - handlePaletteKeydown, handleGlobalKeydown, bindStaticEventListeners, openBottomSheet, @@ -1323,12 +1111,6 @@ if (typeof module !== 'undefined' && module.exports) { hidePreview, // Test-only helpers _setCurrentSessions, - _setPaletteFilteredSessions, - _getPaletteFilteredSessions, - _setPaletteSelectedIndex, - _getPaletteSelectedIndex, - _setPaletteOpen, - _isPaletteOpen, _setViewMode, }; } diff --git a/muxplex/tests/__pycache__/test_frontend_js.cpython-312-pytest-9.0.2.pyc b/muxplex/tests/__pycache__/test_frontend_js.cpython-312-pytest-9.0.2.pyc new file mode 100644 index 0000000000000000000000000000000000000000..946886647489b43627ee125cdabb07692c4d61ef GIT binary patch literal 33121 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 None: + """PALETTE_MAX_ITEMS constant must be removed.""" + assert "PALETTE_MAX_ITEMS" not in _JS, ( + "PALETTE_MAX_ITEMS must be removed from app.js" + ) + + +def test_no_palette_selected_index_variable() -> None: + """_paletteSelectedIndex variable must be removed.""" + assert "_paletteSelectedIndex" not in _JS, ( + "_paletteSelectedIndex must be removed from app.js" + ) + + +def test_no_palette_filtered_sessions_variable() -> None: + """_paletteFilteredSessions variable must be removed.""" + assert "_paletteFilteredSessions" not in _JS, ( + "_paletteFilteredSessions must be removed from app.js" + ) + + +def test_no_palette_open_variable() -> None: + """_paletteOpen variable must be removed.""" + assert "_paletteOpen" not in _JS, ( + "_paletteOpen must be removed from app.js" + ) + + +def test_no_palette_input_listener_variable() -> None: + """_paletteInputListener variable must be removed.""" + assert "_paletteInputListener" not in _JS, ( + "_paletteInputListener must be removed from app.js" + ) + + +# ── Palette functions must be removed ──────────────────────────────────────── + +def test_no_render_palette_list_function() -> None: + """renderPaletteList function must be removed.""" + assert "renderPaletteList" not in _JS, ( + "renderPaletteList must be removed from app.js" + ) + + +def test_no_highlight_palette_item_function() -> None: + """highlightPaletteItem function must be removed.""" + assert "highlightPaletteItem" not in _JS, ( + "highlightPaletteItem must be removed from app.js" + ) + + +def test_no_open_palette_function() -> None: + """openPalette function must be removed.""" + assert "openPalette" not in _JS, ( + "openPalette must be removed from app.js" + ) + + +def test_no_close_palette_function() -> None: + """closePalette function must be removed.""" + assert "closePalette" not in _JS, ( + "closePalette must be removed from app.js" + ) + + +def test_no_on_palette_input_function() -> None: + """onPaletteInput function must be removed.""" + assert "onPaletteInput" not in _JS, ( + "onPaletteInput must be removed from app.js" + ) + + +def test_no_handle_palette_keydown_function() -> None: + """handlePaletteKeydown function must be removed.""" + assert "handlePaletteKeydown" not in _JS, ( + "handlePaletteKeydown must be removed from app.js" + ) + + +# ── handleGlobalKeydown must be simplified ─────────────────────────────────── + +def test_handle_global_keydown_exists() -> None: + """handleGlobalKeydown function must exist.""" + assert "function handleGlobalKeydown" in _JS, ( + "handleGlobalKeydown must still exist in app.js" + ) + + +def test_handle_global_keydown_no_palette_open_check() -> None: + """handleGlobalKeydown must not check _paletteOpen.""" + # Extract the function body + match = re.search( + r"function handleGlobalKeydown\s*\(e\)\s*\{(.*?)\n\}", + _JS, + re.DOTALL, + ) + assert match, "handleGlobalKeydown function not found" + body = match.group(1) + assert "_paletteOpen" not in body, ( + "handleGlobalKeydown must not reference _paletteOpen" + ) + + +def test_handle_global_keydown_no_open_palette_call() -> None: + """handleGlobalKeydown must not call openPalette.""" + match = re.search( + r"function handleGlobalKeydown\s*\(e\)\s*\{(.*?)\n\}", + _JS, + re.DOTALL, + ) + assert match, "handleGlobalKeydown function not found" + body = match.group(1) + assert "openPalette" not in body, ( + "handleGlobalKeydown must not call openPalette" + ) + + +def test_handle_global_keydown_handles_escape_in_fullscreen() -> None: + """handleGlobalKeydown must call closeSession() on Escape in fullscreen mode.""" + match = re.search( + r"function handleGlobalKeydown\s*\(e\)\s*\{(.*?)\n\}", + _JS, + re.DOTALL, + ) + assert match, "handleGlobalKeydown function not found" + body = match.group(1) + assert "fullscreen" in body, "handleGlobalKeydown must check for fullscreen mode" + assert "Escape" in body, "handleGlobalKeydown must handle Escape key" + assert "closeSession" in body, "handleGlobalKeydown must call closeSession" + + +# ── bindStaticEventListeners must have no palette references ───────────────── + +def test_bind_static_event_listeners_no_palette_trigger() -> None: + """bindStaticEventListeners must not bind palette-trigger click.""" + match = re.search( + r"function bindStaticEventListeners\s*\(\s*\)\s*\{(.*?)\n\}", + _JS, + re.DOTALL, + ) + assert match, "bindStaticEventListeners function not found" + body = match.group(1) + assert "palette-trigger" not in body, ( + "bindStaticEventListeners must not bind palette-trigger click" + ) + + +def test_bind_static_event_listeners_no_palette_backdrop() -> None: + """bindStaticEventListeners must not bind palette-backdrop click.""" + match = re.search( + r"function bindStaticEventListeners\s*\(\s*\)\s*\{(.*?)\n\}", + _JS, + re.DOTALL, + ) + assert match, "bindStaticEventListeners function not found" + body = match.group(1) + assert "palette-backdrop" not in body, ( + "bindStaticEventListeners must not bind palette-backdrop click" + ) + + +# ── Palette test-only helpers must be removed ───────────────────────────────── + +def test_no_set_palette_filtered_sessions_helper() -> None: + """_setPaletteFilteredSessions test helper must be removed.""" + assert "_setPaletteFilteredSessions" not in _JS, ( + "_setPaletteFilteredSessions must be removed from app.js" + ) + + +def test_no_get_palette_filtered_sessions_helper() -> None: + """_getPaletteFilteredSessions test helper must be removed.""" + assert "_getPaletteFilteredSessions" not in _JS, ( + "_getPaletteFilteredSessions must be removed from app.js" + ) + + +def test_no_set_palette_selected_index_helper() -> None: + """_setPaletteSelectedIndex test helper must be removed.""" + assert "_setPaletteSelectedIndex" not in _JS, ( + "_setPaletteSelectedIndex must be removed from app.js" + ) + + +def test_no_get_palette_selected_index_helper() -> None: + """_getPaletteSelectedIndex test helper must be removed.""" + assert "_getPaletteSelectedIndex" not in _JS, ( + "_getPaletteSelectedIndex must be removed from app.js" + ) + + +def test_no_set_palette_open_helper() -> None: + """_setPaletteOpen test helper must be removed.""" + assert "_setPaletteOpen" not in _JS, ( + "_setPaletteOpen must be removed from app.js" + ) + + +def test_no_is_palette_open_helper() -> None: + """_isPaletteOpen test helper must be removed.""" + assert "_isPaletteOpen" not in _JS, ( + "_isPaletteOpen must be removed from app.js" + ) + + +# ── module.exports must not include palette exports ─────────────────────────── + +def test_exports_no_render_palette_list() -> None: + """module.exports must not export renderPaletteList.""" + match = re.search( + r"module\.exports\s*=\s*\{(.*?)\};", + _JS, + re.DOTALL, + ) + assert match, "module.exports block not found" + exports = match.group(1) + assert "renderPaletteList" not in exports, ( + "module.exports must not export renderPaletteList" + ) + + +def test_exports_no_highlight_palette_item() -> None: + """module.exports must not export highlightPaletteItem.""" + match = re.search( + r"module\.exports\s*=\s*\{(.*?)\};", + _JS, + re.DOTALL, + ) + assert match, "module.exports block not found" + exports = match.group(1) + assert "highlightPaletteItem" not in exports, ( + "module.exports must not export highlightPaletteItem" + ) + + +def test_exports_no_open_palette() -> None: + """module.exports must not export openPalette.""" + match = re.search( + r"module\.exports\s*=\s*\{(.*?)\};", + _JS, + re.DOTALL, + ) + assert match, "module.exports block not found" + exports = match.group(1) + assert "openPalette" not in exports, ( + "module.exports must not export openPalette" + ) + + +def test_exports_no_close_palette() -> None: + """module.exports must not export closePalette.""" + match = re.search( + r"module\.exports\s*=\s*\{(.*?)\};", + _JS, + re.DOTALL, + ) + assert match, "module.exports block not found" + exports = match.group(1) + assert "closePalette" not in exports, ( + "module.exports must not export closePalette" + ) + + +def test_exports_no_on_palette_input() -> None: + """module.exports must not export onPaletteInput.""" + match = re.search( + r"module\.exports\s*=\s*\{(.*?)\};", + _JS, + re.DOTALL, + ) + assert match, "module.exports block not found" + exports = match.group(1) + assert "onPaletteInput" not in exports, ( + "module.exports must not export onPaletteInput" + ) + + +def test_exports_no_handle_palette_keydown() -> None: + """module.exports must not export handlePaletteKeydown.""" + match = re.search( + r"module\.exports\s*=\s*\{(.*?)\};", + _JS, + re.DOTALL, + ) + assert match, "module.exports block not found" + exports = match.group(1) + assert "handlePaletteKeydown" not in exports, ( + "module.exports must not export handlePaletteKeydown" + ) + + +def test_exports_still_has_handle_global_keydown() -> None: + """module.exports must still export handleGlobalKeydown.""" + match = re.search( + r"module\.exports\s*=\s*\{(.*?)\};", + _JS, + re.DOTALL, + ) + assert match, "module.exports block not found" + exports = match.group(1) + assert "handleGlobalKeydown" in exports, ( + "module.exports must still export handleGlobalKeydown" + ) + + +def test_exports_still_has_bind_static_event_listeners() -> None: + """module.exports must still export bindStaticEventListeners.""" + match = re.search( + r"module\.exports\s*=\s*\{(.*?)\};", + _JS, + re.DOTALL, + ) + assert match, "module.exports block not found" + exports = match.group(1) + assert "bindStaticEventListeners" in exports, ( + "module.exports must still export bindStaticEventListeners" + )