fix(ui): handle URL params in search page for command palette navigation
The search page now reads `q` and `mode` query parameters from the URL, enabling proper integration with the command palette's search/ask actions. Changes: - Read URL params using useSearchParams hook - Initialize form fields and active tab from URL params - Auto-trigger search/ask when arriving with URL params - Handle subsequent navigations while on the search page
This commit is contained in:
parent
728f036eeb
commit
33be80ab7c
1 changed files with 65 additions and 8 deletions
|
|
@ -1,6 +1,7 @@
|
||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import { useMemo, useState } from 'react'
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||||
|
import { useSearchParams } from 'next/navigation'
|
||||||
import { AppShell } from '@/components/layout/AppShell'
|
import { AppShell } from '@/components/layout/AppShell'
|
||||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
|
||||||
import { Input } from '@/components/ui/input'
|
import { Input } from '@/components/ui/input'
|
||||||
|
|
@ -23,14 +24,24 @@ import { AdvancedModelsDialog } from '@/components/search/AdvancedModelsDialog'
|
||||||
import { SaveToNotebooksDialog } from '@/components/search/SaveToNotebooksDialog'
|
import { SaveToNotebooksDialog } from '@/components/search/SaveToNotebooksDialog'
|
||||||
|
|
||||||
export default function SearchPage() {
|
export default function SearchPage() {
|
||||||
|
// URL params
|
||||||
|
const searchParams = useSearchParams()
|
||||||
|
const urlQuery = searchParams.get('q') || ''
|
||||||
|
const urlMode = searchParams.get('mode') || 'ask'
|
||||||
|
|
||||||
|
// Tab state (controlled)
|
||||||
|
const [activeTab, setActiveTab] = useState<'ask' | 'search'>(
|
||||||
|
urlMode === 'search' ? 'search' : 'ask'
|
||||||
|
)
|
||||||
|
|
||||||
// Search state
|
// Search state
|
||||||
const [searchQuery, setSearchQuery] = useState('')
|
const [searchQuery, setSearchQuery] = useState(urlMode === 'search' ? urlQuery : '')
|
||||||
const [searchType, setSearchType] = useState<'text' | 'vector'>('text')
|
const [searchType, setSearchType] = useState<'text' | 'vector'>('text')
|
||||||
const [searchSources, setSearchSources] = useState(true)
|
const [searchSources, setSearchSources] = useState(true)
|
||||||
const [searchNotes, setSearchNotes] = useState(true)
|
const [searchNotes, setSearchNotes] = useState(true)
|
||||||
|
|
||||||
// Ask state
|
// Ask state
|
||||||
const [askQuestion, setAskQuestion] = useState('')
|
const [askQuestion, setAskQuestion] = useState(urlMode === 'ask' ? urlQuery : '')
|
||||||
|
|
||||||
// Advanced models dialog
|
// Advanced models dialog
|
||||||
const [showAdvancedModels, setShowAdvancedModels] = useState(false)
|
const [showAdvancedModels, setShowAdvancedModels] = useState(false)
|
||||||
|
|
@ -64,7 +75,11 @@ export default function SearchPage() {
|
||||||
|
|
||||||
const hasEmbeddingModel = !!modelDefaults?.default_embedding_model
|
const hasEmbeddingModel = !!modelDefaults?.default_embedding_model
|
||||||
|
|
||||||
const handleSearch = () => {
|
// Track if we've already auto-triggered from URL params
|
||||||
|
const hasAutoTriggeredRef = useRef(false)
|
||||||
|
const lastUrlParamsRef = useRef({ q: '', mode: '' })
|
||||||
|
|
||||||
|
const handleSearch = useCallback(() => {
|
||||||
if (!searchQuery.trim()) return
|
if (!searchQuery.trim()) return
|
||||||
|
|
||||||
searchMutation.mutate({
|
searchMutation.mutate({
|
||||||
|
|
@ -75,7 +90,7 @@ export default function SearchPage() {
|
||||||
search_notes: searchNotes,
|
search_notes: searchNotes,
|
||||||
minimum_score: 0.2
|
minimum_score: 0.2
|
||||||
})
|
})
|
||||||
}
|
}, [searchQuery, searchType, searchSources, searchNotes, searchMutation])
|
||||||
|
|
||||||
const handleKeyPress = (e: React.KeyboardEvent) => {
|
const handleKeyPress = (e: React.KeyboardEvent) => {
|
||||||
if (e.key === 'Enter') {
|
if (e.key === 'Enter') {
|
||||||
|
|
@ -83,7 +98,7 @@ export default function SearchPage() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleAsk = () => {
|
const handleAsk = useCallback(() => {
|
||||||
if (!askQuestion.trim() || !modelDefaults?.default_chat_model) return
|
if (!askQuestion.trim() || !modelDefaults?.default_chat_model) return
|
||||||
|
|
||||||
const models = customModels || {
|
const models = customModels || {
|
||||||
|
|
@ -93,14 +108,56 @@ export default function SearchPage() {
|
||||||
}
|
}
|
||||||
|
|
||||||
ask.sendAsk(askQuestion, models)
|
ask.sendAsk(askQuestion, models)
|
||||||
}
|
}, [askQuestion, modelDefaults, customModels, ask])
|
||||||
|
|
||||||
|
// Auto-trigger search/ask when arriving with URL params
|
||||||
|
useEffect(() => {
|
||||||
|
// Skip if already triggered or no query
|
||||||
|
if (hasAutoTriggeredRef.current || !urlQuery) return
|
||||||
|
|
||||||
|
// Wait for models to load before triggering ask
|
||||||
|
if (urlMode === 'ask' && modelsLoading) return
|
||||||
|
|
||||||
|
hasAutoTriggeredRef.current = true
|
||||||
|
|
||||||
|
if (urlMode === 'search') {
|
||||||
|
handleSearch()
|
||||||
|
} else if (urlMode === 'ask' && modelDefaults?.default_chat_model) {
|
||||||
|
handleAsk()
|
||||||
|
}
|
||||||
|
}, [urlQuery, urlMode, modelsLoading, modelDefaults, handleSearch, handleAsk])
|
||||||
|
|
||||||
|
// Handle URL param changes while on page (e.g., from command palette again)
|
||||||
|
useEffect(() => {
|
||||||
|
const currentQ = searchParams.get('q') || ''
|
||||||
|
const currentMode = searchParams.get('mode') || 'ask'
|
||||||
|
|
||||||
|
// Check if URL params have changed
|
||||||
|
if (currentQ !== lastUrlParamsRef.current.q || currentMode !== lastUrlParamsRef.current.mode) {
|
||||||
|
lastUrlParamsRef.current = { q: currentQ, mode: currentMode }
|
||||||
|
|
||||||
|
if (currentQ) {
|
||||||
|
// Update state based on mode
|
||||||
|
if (currentMode === 'search') {
|
||||||
|
setSearchQuery(currentQ)
|
||||||
|
setActiveTab('search')
|
||||||
|
// Reset trigger flag so we auto-trigger with new params
|
||||||
|
hasAutoTriggeredRef.current = false
|
||||||
|
} else {
|
||||||
|
setAskQuestion(currentQ)
|
||||||
|
setActiveTab('ask')
|
||||||
|
hasAutoTriggeredRef.current = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [searchParams])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AppShell>
|
<AppShell>
|
||||||
<div className="p-4 md:p-6">
|
<div className="p-4 md:p-6">
|
||||||
<h1 className="text-xl md:text-2xl font-bold mb-4 md:mb-6">Ask and Search</h1>
|
<h1 className="text-xl md:text-2xl font-bold mb-4 md:mb-6">Ask and Search</h1>
|
||||||
|
|
||||||
<Tabs defaultValue="ask" className="w-full space-y-6">
|
<Tabs value={activeTab} onValueChange={(v) => setActiveTab(v as 'ask' | 'search')} className="w-full space-y-6">
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<p className="text-xs font-semibold uppercase tracking-wide text-muted-foreground">Choose a mode</p>
|
<p className="text-xs font-semibold uppercase tracking-wide text-muted-foreground">Choose a mode</p>
|
||||||
<TabsList aria-label="Ask or search your knowledge base" className="w-full max-w-xl">
|
<TabsList aria-label="Ask or search your knowledge base" className="w-full max-w-xl">
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue