Merge pull request #181 from MervinPraison/claude/issue-178-20260708-120000

fix: expose Ctrl+K command palette globally + guard init idempotency
This commit is contained in:
praisonai-triage-agent[bot] authored and GitHub committed 2026-07-09 16:47:38 +00:00
commit 949a1862da
3 files changed
+89

No files matched your search

@@ -1087,7 +1087,11 @@ function closeCommandPalette() {
_paletteResults = [];
}
let _paletteInitialized = false;
function initCommandPalette() {
if (_paletteInitialized) return;
_paletteInitialized = true;
document.addEventListener('keydown', (e) => {
if ((e.ctrlKey || e.metaKey) && (e.key === 'k' || e.key === 'K')) {
e.preventDefault();
@@ -1096,6 +1100,11 @@ function initCommandPalette() {
});
}
window.aiui = window.aiui || {};
window.aiui.openCommandPalette = openCommandPalette;
window.aiui.closeCommandPalette = closeCommandPalette;
window.openCommandPalette = openCommandPalette;
// ── Session Search Palette ──────────────────────────────────────────
// Vanilla-JS port of the React SessionSearch / useSessionSearch behavior so
// that the dashboard style reaches C-01 parity. The palette lists sessions
+67
View File
@@ -0,0 +1,67 @@
"""Regression tests for #178 — Ctrl+K command palette exposure and idempotency.
The dashboard command palette (initCommandPalette / openCommandPalette /
.db-cmdk-overlay) is implemented in the shipped bundle, but the acceptance
criteria require openCommandPalette to be reachable from the global scope
(``typeof openCommandPalette === 'function'``) and require that repeated
initialisation (HMR / full reload) does not double-register the keydown
handler. These source-parsing tests mirror ``test_dashboard_shell_order.py``.
"""
from __future__ import annotations
import re
from pathlib import Path
FRONTEND = (
Path(__file__).resolve().parents[2]
/ "src" / "praisonaiui" / "templates" / "frontend" / "plugins"
)
DASHBOARD_JS = FRONTEND / "dashboard.js"
class TestCommandPalette:
def _src(self) -> str:
return DASHBOARD_JS.read_text()
def test_open_command_palette_exposed_globally(self):
src = self._src()
assert "window.openCommandPalette = openCommandPalette" in src, (
"openCommandPalette must be exposed on window for the global "
"'typeof openCommandPalette === function' acceptance criterion"
)
def test_open_command_palette_on_aiui_namespace(self):
src = self._src()
assert "window.aiui.openCommandPalette = openCommandPalette" in src
def test_init_command_palette_guarded_against_double_register(self):
src = self._src()
m = re.search(
r"function initCommandPalette\([^)]*\)\s*\{(.*?)^}",
src, re.DOTALL | re.MULTILINE,
)
assert m, "initCommandPalette not found"
body = m.group(1)
assert "_paletteInitialized" in body, (
"initCommandPalette must guard against duplicate registration"
)
assert "return" in body, (
"initCommandPalette must early-return when already initialised"
)
def test_init_command_palette_called_after_build(self):
src = self._src()
build_idx = src.find("await buildDashboard()")
init_idx = src.find("initCommandPalette();")
assert build_idx != -1, "buildDashboard call not found"
assert init_idx != -1, "initCommandPalette call not found"
assert build_idx < init_idx, (
"initCommandPalette must run after buildDashboard so the shell exists"
)
def test_ctrl_and_meta_k_bound(self):
src = self._src()
assert re.search(
r"e\.ctrlKey \|\| e\.metaKey", src
), "Ctrl+K / Cmd+K must both open the palette"
@@ -31,6 +31,9 @@ class TestDashboardSessionSearch:
# keydown listener physically lives.
src = self._src()
assert "initSessionSearch" in src
# Ctrl+K / Cmd+K ownership lives in the command palette
# (initCommandPalette), which exposes a "Search sessions…" entry that
# calls openSessionSearch. initSessionSearch only owns Escape-to-close.
m = re.search(
r"function initCommandPalette\(\)\s*\{(.*?)^}",
src, re.DOTALL | re.MULTILINE,
@@ -45,6 +48,16 @@ class TestDashboardSessionSearch:
assert "openSessionSearch()" in src
assert "action: 'sessions'" in src or 'action: "sessions"' in src
# initSessionSearch owns only Escape-to-close for the session palette.
m = re.search(
r"function initSessionSearch\(\)\s*\{(.*?)^}",
src, re.DOTALL | re.MULTILINE,
)
assert m, "initSessionSearch not found"
assert "Escape" in m.group(1), (
"initSessionSearch must own Escape-to-close for the session palette"
)
def test_palette_fetches_sessions_endpoint(self):
src = self._src()
m = re.search(