Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 42 additions & 29 deletions apps/sim/app/o/[organizationId]/search/search.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@ import { Chip, ComposerActionButton, cn } from '@sim/emcn'
import { ArrowUp, Plus, Search, Sparkles, StopFilled } from '@sim/emcn/icons'
import { generateId } from '@sim/utils/id'
import { useIsFetching, useQueryClient } from '@tanstack/react-query'
import { usePathname, useRouter } from 'next/navigation'
import { usePathname, useRouter, useSearchParams } from 'next/navigation'
import { useQueryStates } from 'nuqs'
import { HEADER_ACTION_CLUSTER } from '@/components/page-header-bar'
import type { WorkspaceSearchFilters } from '@/lib/api/contracts/knowledge'
import { resourceScopeKey } from '@/lib/core/resource-scope'
import { BrowserStorage } from '@/lib/core/utils/browser-storage'
import { organizationRoutes } from '@/lib/navigation/paths'
import type { OrganizationSummaryRequest } from '@/app/o/[organizationId]/components/organization-conversation'
import { SearchInputBar } from '@/app/o/[organizationId]/components/search-input-bar'
Expand Down Expand Up @@ -166,17 +167,17 @@ function OrganizationSearchContent({ userId, chatId }: OrganizationSearchProps)
const { recordQuery } = useSearchHistoryActions()
const router = useRouter()
const pathname = usePathname()
const hasSearchParams = useSearchParams().size > 0
const routes = organizationRoutes(organization.id)
const hasConversation = Boolean(chatId) || pathname.startsWith(`${routes.search}/chat/`)
const queryClient = useQueryClient()
const [{ q, mode }, setParams] = useQueryStates(
organizationSearchParsers,
organizationSearchUrlKeys
)
const activeMode =
mothershipAvailable && (mode ?? (hasConversation ? 'assistant' : 'search')) === 'assistant'
? 'assistant'
: 'search'
const routeMode = mode ?? (hasConversation ? 'assistant' : hasSearchParams ? 'search' : null)
const activeMode = mothershipAvailable ? routeMode : 'search'
const preferenceKey = `organization-search-mode:${userId}:${organization.id}`
const [assistantOpened, setAssistantOpened] = useState(activeMode === 'assistant')
const [conversationInstance, setConversationInstance] = useState(0)
const [isStartingNew, startNewTransition] = useTransition()
Expand All @@ -185,8 +186,20 @@ function OrganizationSearchContent({ userId, chatId }: OrganizationSearchProps)
(id: string) => setSummaries((current) => current.filter((request) => request.id !== id)),
[]
)
useEffect(() => {
if (!mothershipAvailable) return
if (routeMode) {
BrowserStorage.setItem(preferenceKey, routeMode)
return
}
const savedMode = BrowserStorage.getItem<unknown>(preferenceKey, null)
// Pin the mode to this history entry so Back survives a later preference change.
void setParams({ mode: savedMode === 'assistant' ? 'assistant' : 'search' })
}, [mothershipAvailable, preferenceKey, routeMode, setParams])

if (activeMode === 'assistant' && !assistantOpened) setAssistantOpened(true)

const changeMode = (next: 'search' | 'assistant') => {
if (next === 'assistant') setAssistantOpened(true)
void setParams({ mode: next }, { history: 'push' })
}
const startNew = () => {
Expand All @@ -201,7 +214,7 @@ function OrganizationSearchContent({ userId, chatId }: OrganizationSearchProps)
}
startNewTransition(() => {
setConversationInstance((current) => current + 1)
router.push(activeMode === 'assistant' ? `${routes.search}?mode=assistant` : routes.search, {
router.push(mothershipAvailable ? `${routes.search}?mode=${activeMode}` : routes.search, {
scroll: false,
})
})
Expand Down Expand Up @@ -263,28 +276,28 @@ function OrganizationSearchContent({ userId, chatId }: OrganizationSearchProps)
return (
<div className='h-full min-h-0 min-w-0 bg-[var(--bg)] pt-[var(--workspace-content-title-bar-inset)]'>
<div className='flex h-full min-h-0 min-w-0 flex-col [--workspace-content-title-bar-inset:0px]'>
{(assistantOpened || hasConversation || activeMode === 'assistant') &&
mothershipAvailable && (
<div
inert={activeMode !== 'assistant'}
className={cn(
'min-h-0 min-w-0 flex-1 flex-col',
activeMode === 'assistant' ? 'flex' : 'hidden'
)}
>
<Suspense fallback={<HomeFallback header={header} />}>
<OrganizationConversation
key={conversationInstance}
requestMode='assistant'
chatId={chatId}
header={header}
active={activeMode === 'assistant'}
summaryRequest={summaries[0]}
onSummaryConsumed={consumeSummary}
/>
</Suspense>
</div>
)}
{(assistantOpened || hasConversation) && mothershipAvailable && (
<div
inert={activeMode !== 'assistant'}
className={cn(
'min-h-0 min-w-0 flex-1 flex-col',
activeMode === 'assistant' ? 'flex' : 'hidden'
)}
>
<Suspense fallback={<HomeFallback header={header} />}>
<OrganizationConversation
key={conversationInstance}
requestMode='assistant'
chatId={chatId}
header={header}
active={activeMode === 'assistant'}
summaryRequest={summaries[0]}
onSummaryConsumed={consumeSummary}
/>
</Suspense>
</div>
)}
{!activeMode && <HomeFallback />}
{activeMode === 'search' && (
<>
<div className={cn(RESOURCE_HEADER_CLASSES.layout, RESOURCE_HEADER_CLASSES.content)}>
Expand Down
2 changes: 1 addition & 1 deletion apps/sim/scripts/test-assistant-actions-e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1488,7 +1488,7 @@ try {
await reviewCards.getByRole('button', { name: 'Cancel', exact: true }).focus()
await page.keyboard.press('Enter')
await expect.poll(() => decisionPersisted).toBe(true)
await page.goto(`${baseUrl.origin}${root}/search`)
await page.goto(`${baseUrl.origin}${root}/search?mode=search`)
const search = page.getByRole('textbox', { name: 'Search your sources', exact: true })
await search.fill('Continue searching')
releaseResponse.resolve()
Expand Down
19 changes: 17 additions & 2 deletions apps/sim/scripts/test-organization-search-e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -412,6 +412,21 @@ try {
await expect(assistantInput).toHaveValue('Summarize launch readiness')
}
)
await check('Returning to Search restores the last selected view', async () => {
await page.goto(`${baseUrl.origin}${root}/search`)
const views = page.getByRole('navigation', { name: 'Search views' })
for (const mode of ['Assistant', 'Search'] as const) {
const tab = views.getByRole('button', { name: mode, exact: true })
await tab.click()
await expect(tab).toHaveAttribute('aria-current', 'page')
await page.getByRole('link', { name: 'Home', exact: true }).click()
await expect(page).toHaveURL(`${baseUrl.origin}${root}/home`)
await page.getByRole('link', { name: 'Search', exact: true }).click()
await expect(tab).toHaveAttribute('aria-current', 'page')
await page.goto(`${baseUrl.origin}${root}/search`)
await expect(tab).toHaveAttribute('aria-current', 'page')
}
})
await check(
'Traditional search submits the organization query and keeps results in Search',
async () => {
Expand Down Expand Up @@ -750,7 +765,7 @@ try {
await input.fill('Edited launch query')
await expect(input).toHaveValue('Edited launch query')
await page.getByRole('button', { name: 'New search', exact: true }).click()
await expect(page).toHaveURL(`${baseUrl.origin}${root}/search`)
await expect(page).toHaveURL(`${baseUrl.origin}${root}/search?mode=search`)
await expect(input).toHaveValue('')
await page.goBack()
await expect(input).toHaveValue('Edited launch query')
Expand Down Expand Up @@ -1001,7 +1016,7 @@ try {
values (${generateId()}, ${organizationId}, 'Search only',
${sql.json({ disableWorkspaceCreation: true })}, ${userId}, true)`
await page.goto(`${baseUrl.origin}${root}/home`)
await expect(page).toHaveURL(`${baseUrl.origin}${root}/search`)
await expect(page).toHaveURL((url) => url.pathname === `${root}/search`)
await page.goto(`${baseUrl.origin}${root}/search/chat/${assistantId}`)
await expect(page.getByText(answer, { exact: true })).toBeVisible()
}
Expand Down
Loading