feat(frontend): message search and session export entry points
Cyrene CI / backend (push) Has been cancelled
Cyrene CI / frontend (push) Has been cancelled

- SearchModal queries GET /messages/search and jumps to the matching
  session; opened from the sidebar button or Ctrl+K
- session items in the sidebar gain a hover export button that
  downloads the markdown export via an authenticated fetch + blob
  (plain links cannot send the JWT)
This commit is contained in:
Aska committed 2026-08-21 12:59:30 +08:00
1 parent 9d040fde0b
commit 26d65dd6bb
4 files changed
+143 -7

No files matched your search

+18 -2
View File
@@ -1,7 +1,8 @@
import { useEffect } from 'react'
import { useEffect, useState } from 'react'
import { useParams } from 'react-router-dom'
import { useChatStore } from '../../store/chatStore'
import ChatContainer from '../../components/chat/ChatContainer'
import SearchModal from '../../components/chat/SearchModal'
import Sidebar from '../../components/layout/Sidebar'
import { api } from '../../api/client'
import type { Message } from '../../types/chat'
@@ -9,6 +10,7 @@ import type { Message } from '../../types/chat'
export default function ChatPage() {
const { sessionId } = useParams()
const { setSessionId, setMessages } = useChatStore()
const [showSearch, setShowSearch] = useState(false)
useEffect(() => {
if (!sessionId) return
@@ -30,12 +32,26 @@ export default function ChatPage() {
return () => { cancelled = true }
}, [sessionId, setSessionId, setMessages])
// Ctrl+K opens message search
useEffect(() => {
const onKey = (e: KeyboardEvent) => {
if ((e.ctrlKey || e.metaKey) && e.key === 'k') {
e.preventDefault()
setShowSearch(true)
}
}
window.addEventListener('keydown', onKey)
return () => window.removeEventListener('keydown', onKey)
}, [])
return (
<div className="flex h-full">
<Sidebar />
<Sidebar onSearch={() => setShowSearch(true)} />
<div className="flex-1 min-w-0">
<ChatContainer />
</div>
{showSearch && <SearchModal onClose={() => setShowSearch(false)} />}
</div>
)
}