fix: address review feedback on session search palette

- useSessionSearch: add `enabled` param so global Ctrl+K is only
  intercepted when the palette is rendered; case-insensitive key check
- ChatLayout: only enable the shortcut in fullscreen mode
- AgentUILayout: pass currentSessionId/onSessionChange to ChatArea so
  session selection is functional
- SessionSearch: guard async fetch with an active flag, hoist date
  objects out of the reduce loop, use a div for the loading state
  (CommandEmpty only renders post-filter)
- tests: patch async _datastore with AsyncMock and build the app via
  create_app() so the /sessions endpoint tests actually run

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-authored-by: Mervin Praison <[email protected]>
This commit is contained in:
praisonai-triage-agent[bot]andMervin Praison committed 2026-06-23 11:04:04 +00:00
1 parent 92e257306f
commit ff54833c84
5 files changed
+49 -27

No files matched your search

+29 -14
View File
@@ -30,23 +30,33 @@ export function SessionSearch({ open, onOpenChange, onSessionSelect, currentSess
// Fetch sessions when dialog opens
useEffect(() => {
if (!open) return
let active = true
const fetchSessions = async () => {
try {
setLoading(true)
const res = await fetch('/sessions')
if (!res.ok) throw new Error(`HTTP ${res.status}`)
const data = await res.json()
setSessions(data.sessions || [])
if (active) {
setSessions(data.sessions || [])
}
} catch (err) {
console.error('Failed to fetch sessions:', err)
setSessions([])
if (active) {
setSessions([])
}
} finally {
setLoading(false)
if (active) {
setLoading(false)
}
}
}
fetchSessions()
return () => {
active = false
}
}, [open])
const handleSelect = useCallback((sessionId: string) => {
@@ -73,23 +83,28 @@ export function SessionSearch({ open, onOpenChange, onSessionSelect, currentSess
}
// Group sessions by date
const today = new Date()
const yesterday = new Date(today)
yesterday.setDate(yesterday.getDate() - 1)
const lastWeek = new Date(today)
lastWeek.setDate(lastWeek.getDate() - 7)
const todayStr = today.toDateString()
const yesterdayStr = yesterday.toDateString()
const groupedSessions = sessions.reduce((groups, session) => {
const date = new Date(session.updated_at)
const today = new Date()
const yesterday = new Date(today)
yesterday.setDate(yesterday.getDate() - 1)
const lastWeek = new Date(today)
lastWeek.setDate(lastWeek.getDate() - 7)
const dateStr = date.toDateString()
let group = 'Older'
if (date.toDateString() === today.toDateString()) {
if (dateStr === todayStr) {
group = 'Today'
} else if (date.toDateString() === yesterday.toDateString()) {
} else if (dateStr === yesterdayStr) {
group = 'Yesterday'
} else if (date > lastWeek) {
group = 'This Week'
}
if (!groups[group]) groups[group] = []
groups[group].push(session)
return groups
@@ -110,7 +125,7 @@ export function SessionSearch({ open, onOpenChange, onSessionSelect, currentSess
/>
<CommandList>
{loading ? (
<CommandEmpty>Loading sessions...</CommandEmpty>
<div className="py-6 text-center text-sm text-muted-foreground">Loading sessions...</div>
) : sessions.length === 0 ? (
<CommandEmpty>No sessions found. Start a chat to create one.</CommandEmpty>
) : (
+5 -3
View File
@@ -1,12 +1,14 @@
import { useEffect, useState, useCallback } from 'react'
export function useSessionSearch() {
export function useSessionSearch(enabled: boolean = true) {
const [open, setOpen] = useState(false)
useEffect(() => {
if (!enabled) return
const handleKeyDown = (e: KeyboardEvent) => {
// Check for Ctrl+K (Windows/Linux) or Cmd+K (Mac)
if ((e.ctrlKey || e.metaKey) && e.key === 'k') {
if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === 'k') {
e.preventDefault()
setOpen((prev) => !prev)
}
@@ -14,7 +16,7 @@ export function useSessionSearch() {
document.addEventListener('keydown', handleKeyDown)
return () => document.removeEventListener('keydown', handleKeyDown)
}, [])
}, [enabled])
const onOpenChange = useCallback((open: boolean) => {
setOpen(open)
+6 -1
View File
@@ -155,7 +155,12 @@ export function AgentUILayout({ config, title }: AgentUILayoutProps) {
</header>
)}
<div className="flex-1 overflow-hidden">
<ChatArea config={config} className="h-full" />
<ChatArea
config={config}
className="h-full"
sessionId={currentSessionId}
onSessionChange={handleSessionSelect}
/>
</div>
</main>
</div>
+1 -1
View File
@@ -18,7 +18,7 @@ export function ChatLayout({ config, layout, title }: ChatLayoutProps) {
const [sessionListKey, setSessionListKey] = useState(0)
// Session search palette (Ctrl+K)
const { open: sessionSearchOpen, onOpenChange: onSessionSearchChange } = useSessionSearch()
const { open: sessionSearchOpen, onOpenChange: onSessionSearchChange } = useSessionSearch(mode === 'fullscreen')
const handleSessionSelect = useCallback((sessionId: string) => {
setCurrentSessionId(sessionId)
+8 -8
View File
@@ -1,6 +1,6 @@
"""Unit tests for session search functionality."""
from unittest.mock import patch
from unittest.mock import AsyncMock, patch
import pytest
from starlette.testclient import TestClient
@@ -9,8 +9,8 @@ from starlette.testclient import TestClient
def test_sessions_endpoint_returns_list(test_client):
"""Test that /sessions endpoint returns a list of sessions."""
# Mock the session manager to return test sessions
with patch("praisonaiui.server._session_manager") as mock_manager:
mock_manager.list_sessions.return_value = [
with patch("praisonaiui.server._datastore") as mock_datastore:
mock_datastore.list_sessions = AsyncMock(return_value=[
{
"id": "session-1",
"title": "Test Session 1",
@@ -25,7 +25,7 @@ def test_sessions_endpoint_returns_list(test_client):
"updated_at": "2024-01-02T01:00:00Z",
"message_count": 10,
},
]
])
response = test_client.get("/sessions")
assert response.status_code == 200
@@ -39,8 +39,8 @@ def test_sessions_endpoint_returns_list(test_client):
def test_sessions_endpoint_empty_list(test_client):
"""Test that /sessions endpoint returns empty list when no sessions."""
with patch("praisonaiui.server._session_manager") as mock_manager:
mock_manager.list_sessions.return_value = []
with patch("praisonaiui.server._datastore") as mock_datastore:
mock_datastore.list_sessions = AsyncMock(return_value=[])
response = test_client.get("/sessions")
assert response.status_code == 200
@@ -93,6 +93,6 @@ def test_session_search_filters_by_id():
@pytest.fixture
def test_client():
"""Create a test client for the app."""
from praisonaiui.server import app
from praisonaiui.server import create_app
return TestClient(app)
return TestClient(create_app())