Skip to content

Commit d6968e4

Browse files
committed
refactor(search): apply the cleanup passes to the Sim Search surface
Drop three memos over cheap derivations, make the search setter's callers honest about their dependencies, ignore a whitespace-only query from a link, give a dropdown setup field its hint, and drop the query from the URL when the composer leaves Search.
1 parent 6b621bd commit d6968e4

7 files changed

Lines changed: 50 additions & 34 deletions

File tree

apps/sim/app/workspace/[workspaceId]/home/components/knowledge-search-results/knowledge-search-results.tsx

Lines changed: 11 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -118,14 +118,10 @@ export function KnowledgeSearchResults({
118118
* The list also carries the viewer's legacy personal bases, which have no
119119
* workspace; a search names one workspace and refuses a base outside it.
120120
*/
121-
const knowledgeBaseIds = useMemo(
122-
() =>
123-
knowledgeBases
124-
.filter((kb) => kb.workspaceId === workspaceId)
125-
.slice(0, MAX_SEARCHED_KNOWLEDGE_BASES)
126-
.map((kb) => kb.id),
127-
[knowledgeBases, workspaceId]
128-
)
121+
const knowledgeBaseIds = knowledgeBases
122+
.filter((kb) => kb.workspaceId === workspaceId)
123+
.slice(0, MAX_SEARCHED_KNOWLEDGE_BASES)
124+
.map((kb) => kb.id)
129125
const {
130126
data: results,
131127
isPending,
@@ -134,16 +130,13 @@ export function KnowledgeSearchResults({
134130
} = useWorkspaceKnowledgeSearch(workspaceId, knowledgeBaseIds, query)
135131
const { data: memberConnectors = [] } = useWorkspaceMemberConnectors(workspaceId)
136132
/** Every per-member connector still indexing for the viewer, in any base the search spans. */
137-
const indexing = useMemo(
138-
() => [
139-
...new Set(
140-
memberConnectors
141-
.filter(isIndexing)
142-
.map((connection) => connectorName(connection.connectorType))
143-
),
144-
],
145-
[memberConnectors]
146-
)
133+
const indexing = [
134+
...new Set(
135+
memberConnectors
136+
.filter(isIndexing)
137+
.map((connection) => connectorName(connection.connectorType))
138+
),
139+
]
147140
const documents = useMemo(() => groupResultsByDocument(results ?? []), [results])
148141
const sourceTypes = useMemo(
149142
() => [...new Set(documents.map((result) => result.connectorType ?? 'upload'))],

apps/sim/app/workspace/[workspaceId]/home/components/search-sources/search-sources.tsx

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -169,14 +169,11 @@ export function SearchSources({ workspaceId }: SearchSourcesProps) {
169169
})
170170
const [setupConnector, setSetupConnector] = useState<SearchConnector | null>(null)
171171

172-
/** Connected sources first, then the rest alphabetically. */
173-
const ordered = useMemo(() => {
174-
const rank = (connector: SearchConnector) =>
175-
connectionByType.get(connector.type)?.viewerMembership === 'connected' ? 0 : 1
176-
return [...PERSONAL_SEARCH_CONNECTORS].sort(
177-
(a, b) => rank(a) - rank(b) || a.meta.name.localeCompare(b.meta.name)
178-
)
179-
}, [connectionByType])
172+
const rank = (connector: SearchConnector) =>
173+
connectionByType.get(connector.type)?.viewerMembership === 'connected' ? 0 : 1
174+
const ordered = [...PERSONAL_SEARCH_CONNECTORS].sort(
175+
(a, b) => rank(a) - rank(b) || a.meta.name.localeCompare(b.meta.name)
176+
)
180177

181178
const startConnect = (connector: SearchConnector) => {
182179
const connection = connectionByType.get(connector.type)

apps/sim/app/workspace/[workspaceId]/home/components/search-sources/source-setup-modal.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ export function SourceSetupModal({
5757
label: option.label,
5858
}))}
5959
placeholder={field.placeholder}
60+
hint={field.description}
6061
required
6162
/>
6263
) : (

apps/sim/app/workspace/[workspaceId]/home/components/user-input/components/mode-switcher/mode-switcher.test.tsx

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,15 @@ import { act } from 'react'
55
import { createRoot, type Root } from 'react-dom/client'
66
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
77

8-
const { mockCaptureEvent } = vi.hoisted(() => ({ mockCaptureEvent: vi.fn() }))
8+
const { mockCaptureEvent, mockSetSearchQuery } = vi.hoisted(() => ({
9+
mockCaptureEvent: vi.fn(),
10+
mockSetSearchQuery: vi.fn(),
11+
}))
912

1013
vi.mock('next/navigation', () => ({
1114
useParams: () => ({ workspaceId: 'workspace-1' }),
1215
}))
16+
vi.mock('nuqs', () => ({ useQueryState: () => [null, mockSetSearchQuery] }))
1317
vi.mock('posthog-js/react', () => ({ usePostHog: () => null }))
1418
vi.mock('@/lib/posthog/client', () => ({ captureEvent: mockCaptureEvent }))
1519

@@ -94,6 +98,20 @@ describe('ModeSwitcher', () => {
9498
workspace_id: 'workspace-1',
9599
mode: 'search',
96100
})
101+
expect(mockSetSearchQuery).not.toHaveBeenCalled()
102+
})
103+
104+
it('drops the search query from the URL when leaving Search', () => {
105+
useMothershipModeStore.getState().setMode('search')
106+
mount()
107+
openMenu()
108+
109+
act(() => {
110+
items()[0].dispatchEvent(new MouseEvent('click', { bubbles: true, button: 0 }))
111+
})
112+
113+
expect(useMothershipModeStore.getState().mode).toBe('build')
114+
expect(mockSetSearchQuery).toHaveBeenCalledWith(null, { history: 'replace', scroll: false })
97115
})
98116

99117
it('does not report re-selecting the active mode', () => {

apps/sim/app/workspace/[workspaceId]/home/components/user-input/components/mode-switcher/mode-switcher.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,10 @@ import {
1111
} from '@sim/emcn'
1212
import { Check } from '@sim/emcn/icons'
1313
import { useParams } from 'next/navigation'
14+
import { useQueryState } from 'nuqs'
1415
import { usePostHog } from 'posthog-js/react'
1516
import { captureEvent } from '@/lib/posthog/client'
17+
import { searchQueryParam } from '@/app/workspace/[workspaceId]/home/search-params'
1618
import {
1719
MOTHERSHIP_MODES,
1820
type MothershipMode,
@@ -37,9 +39,13 @@ export const ModeSwitcher = memo(function ModeSwitcher() {
3739
const mode = useMothershipModeStore((state) => state.mode)
3840
const setMode = useMothershipModeStore((state) => state.setMode)
3941

42+
const [, setSearchQueryParam] = useQueryState(searchQueryParam.key, searchQueryParam.parser)
43+
44+
/** Leaving Search drops the query from the URL, so a clean URL always means no search is showing. */
4045
const handleSelect = (next: MothershipMode) => {
4146
if (next === mode) return
4247
setMode(next)
48+
if (next !== 'search') void setSearchQueryParam(null, { history: 'replace', scroll: false })
4349
captureEvent(posthog, 'chat_mode_changed', { workspace_id: workspaceId, mode: next })
4450
}
4551

apps/sim/app/workspace/[workspaceId]/home/home.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -475,11 +475,11 @@ export function Home({ chatId, userName, userId }: HomeProps) {
475475
prepareResourceViewForAgentTurn()
476476
sendMessage(trimmed || 'Analyze the attached file(s).', fileAttachments, contexts)
477477
},
478-
[workspaceId, chatId, prepareResourceViewForAgentTurn, sendMessage]
478+
[workspaceId, chatId, prepareResourceViewForAgentTurn, sendMessage, setSearchQuery]
479479
)
480480

481481
/** An emptied search box returns to the sources; nothing else reads the cleared query. */
482-
const clearSearch = useCallback(() => setSearchQuery(''), [])
482+
const clearSearch = useCallback(() => setSearchQuery(''), [setSearchQuery])
483483

484484
/** Summarize on a result: hand the document to the agent in Build mode. */
485485
const handleSummarize = useCallback(
@@ -488,7 +488,7 @@ export function Home({ chatId, userName, userId }: HomeProps) {
488488
setSearchQuery('')
489489
handleSubmit(prompt)
490490
},
491-
[handleSubmit]
491+
[handleSubmit, setSearchQuery]
492492
)
493493
/**
494494
* A chat that already exists opens in Build: its transcript is a
@@ -498,7 +498,7 @@ export function Home({ chatId, userName, userId }: HomeProps) {
498498
useEffect(() => {
499499
if (chatId) useMothershipModeStore.getState().setMode('build')
500500
}, [chatId])
501-
const showSearchResults = composerMode === 'search' && searchQuery.length > 0
501+
const showSearchResults = composerMode === 'search' && searchQuery.trim().length > 0
502502
const searchResults = showSearchResults ? (
503503
<KnowledgeSearchResults
504504
workspaceId={workspaceId}

apps/sim/app/workspace/[workspaceId]/home/search-params.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,10 @@ export const resourceUrlKeys = {
2828

2929
/**
3030
* `q` is the composer's Search-mode query, so a search is a shareable,
31-
* bookmarkable link the way a search page's is. Present only while a search
32-
* is showing; the composer reads it once on mount to restore the query and
33-
* the Search mode. Filter-like, so it replaces the history entry.
31+
* bookmarkable link. Present only while a search is showing: it is dropped
32+
* when the box empties, on Summarize, and when the mode leaves Search. The
33+
* composer reads it once on mount to restore the query and the Search mode.
34+
* Filter-like, so it replaces the history entry.
3435
*/
3536
export const searchQueryParam = {
3637
key: 'q',

0 commit comments

Comments
 (0)