Skip to content

Commit daa2416

Browse files
authored
improvement(search-modal): remove matched-character bolding and move SearchHighlight into knowledge (#5822)
1 parent 6f96d4a commit daa2416

11 files changed

Lines changed: 40 additions & 220 deletions

File tree

apps/sim/app/workspace/[workspaceId]/knowledge/[id]/[documentId]/document.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import { truncate } from '@sim/utils/string'
88
import { ChevronDown, ChevronUp, FileText, Pencil, Tag } from 'lucide-react'
99
import { useParams, useRouter } from 'next/navigation'
1010
import { useQueryStates } from 'nuqs'
11-
import { SearchHighlight } from '@/components/ui/search-highlight'
1211
import type { ChunkData } from '@/lib/knowledge/types'
1312
import { formatTokenCount } from '@/lib/tokenization'
1413
import type {
@@ -38,7 +37,7 @@ import {
3837
documentParsers,
3938
documentUrlKeys,
4039
} from '@/app/workspace/[workspaceId]/knowledge/[id]/[documentId]/search-params'
41-
import { ActionBar } from '@/app/workspace/[workspaceId]/knowledge/[id]/components'
40+
import { ActionBar, SearchHighlight } from '@/app/workspace/[workspaceId]/knowledge/[id]/components'
4241
import { getDocumentIcon } from '@/app/workspace/[workspaceId]/knowledge/components'
4342
import { useUserPermissionsContext } from '@/app/workspace/[workspaceId]/providers/workspace-permissions-provider'
4443
import { useContextMenu } from '@/app/workspace/[workspaceId]/w/components/sidebar/hooks'

apps/sim/app/workspace/[workspaceId]/knowledge/[id]/base.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ import { AlertCircle, Pencil, Plus, Tag, X } from 'lucide-react'
3131
import { useParams, useRouter } from 'next/navigation'
3232
import { useQueryState, useQueryStates } from 'nuqs'
3333
import { usePostHog } from 'posthog-js/react'
34-
import { SearchHighlight } from '@/components/ui/search-highlight'
3534
import { ALL_TAG_SLOTS, type AllTagSlot, getFieldTypeForSlot } from '@/lib/knowledge/constants'
3635
import type { DocumentSortField, SortOrder } from '@/lib/knowledge/documents/types'
3736
import { type FilterFieldType, getOperatorsForFieldType } from '@/lib/knowledge/filters/types'
@@ -59,6 +58,7 @@ import {
5958
ConnectorsSection,
6059
DocumentContextMenu,
6160
RenameDocumentModal,
61+
SearchHighlight,
6262
} from '@/app/workspace/[workspaceId]/knowledge/[id]/components'
6363
import {
6464
addConnectorParam,

apps/sim/app/workspace/[workspaceId]/knowledge/[id]/components/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@ export { ConnectorsSection } from './connectors-section'
66
export { DocumentContextMenu } from './document-context-menu'
77
export { EditConnectorModal } from './edit-connector-modal'
88
export { RenameDocumentModal } from './rename-document-modal'
9+
export { SearchHighlight } from './search-highlight'
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { SearchHighlight } from './search-highlight'

apps/sim/components/ui/search-highlight.tsx renamed to apps/sim/app/workspace/[workspaceId]/knowledge/[id]/components/search-highlight/search-highlight.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@ interface SearchHighlightProps {
44
className?: string
55
}
66

7+
/**
8+
* Renders `text` with substrings matching any whitespace-separated term of
9+
* `searchQuery` wrapped in the highlight-match colors. Falls back to plain
10+
* text when the query is empty.
11+
*/
712
export function SearchHighlight({ text, searchQuery, className = '' }: SearchHighlightProps) {
813
if (!searchQuery.trim()) {
914
return <span className={className}>{text}</span>

apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/search-modal/components/command-items/command-items.tsx

Lines changed: 15 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -6,56 +6,9 @@ import { cn } from '@sim/emcn'
66
import { File, Workflow } from '@sim/emcn/icons'
77
import { Command } from 'cmdk'
88
import type { CommandItemProps } from '@/app/workspace/[workspaceId]/w/components/sidebar/components/search-modal/utils'
9-
import {
10-
COMMAND_ITEM_CLASSNAME,
11-
fuzzyMatch,
12-
} from '@/app/workspace/[workspaceId]/w/components/sidebar/components/search-modal/utils'
9+
import { COMMAND_ITEM_CLASSNAME } from '@/app/workspace/[workspaceId]/w/components/sidebar/components/search-modal/utils'
1310
import { getTileIconColorClass } from '@/blocks/icon-color'
1411

15-
interface Segment {
16-
text: string
17-
hit: boolean
18-
}
19-
20-
function buildSegments(text: string, positions: readonly number[]): Segment[] {
21-
const hits = new Set(positions)
22-
const segments: Segment[] = []
23-
for (let i = 0; i < text.length; i++) {
24-
const hit = hits.has(i)
25-
const last = segments[segments.length - 1]
26-
if (last && last.hit === hit) last.text += text[i]
27-
else segments.push({ text: text[i], hit })
28-
}
29-
return segments
30-
}
31-
32-
/**
33-
* Renders `text` with the characters that match `query` emphasized. Falls back
34-
* to plain text when there is no query or no positional match against the
35-
* display text (e.g. the row matched on a hidden id rather than its label).
36-
*/
37-
export const HighlightedText = memo(
38-
function HighlightedText({ text, query }: { text: string; query?: string }) {
39-
if (!query) return <>{text}</>
40-
const { positions } = fuzzyMatch(text, query)
41-
if (positions.length === 0) return <>{text}</>
42-
return (
43-
<>
44-
{buildSegments(text, positions).map((segment, index) =>
45-
segment.hit ? (
46-
<span key={index} className='font-medium'>
47-
{segment.text}
48-
</span>
49-
) : (
50-
<span key={index}>{segment.text}</span>
51-
)
52-
)}
53-
</>
54-
)
55-
},
56-
(prev, next) => prev.text === next.text && prev.query === next.query
57-
)
58-
5912
export const MemoizedCommandItem = memo(
6013
function CommandItem({
6114
value,
@@ -64,7 +17,6 @@ export const MemoizedCommandItem = memo(
6417
bgColor,
6518
showColoredIcon,
6619
label,
67-
query,
6820
}: CommandItemProps) {
6921
return (
7022
<Command.Item value={value} onSelect={onSelect} className={COMMAND_ITEM_CLASSNAME}>
@@ -81,9 +33,7 @@ export const MemoizedCommandItem = memo(
8133
)}
8234
/>
8335
</div>
84-
<span className='truncate text-[var(--text-body)]'>
85-
<HighlightedText text={label} query={query} />
86-
</span>
36+
<span className='truncate text-[var(--text-body)]'>{label}</span>
8737
</Command.Item>
8838
)
8939
},
@@ -92,8 +42,7 @@ export const MemoizedCommandItem = memo(
9242
prev.icon === next.icon &&
9343
prev.bgColor === next.bgColor &&
9444
prev.showColoredIcon === next.showColoredIcon &&
95-
prev.label === next.label &&
96-
prev.query === next.query
45+
prev.label === next.label
9746
)
9847

9948
export const MemoizedActionItem = memo(
@@ -103,21 +52,17 @@ export const MemoizedActionItem = memo(
10352
icon: Icon,
10453
name,
10554
shortcut,
106-
query,
10755
}: {
10856
value: string
10957
onSelect: () => void
11058
icon: ComponentType<{ className?: string }>
11159
name: string
11260
shortcut?: string
113-
query?: string
11461
}) {
11562
return (
11663
<Command.Item value={value} onSelect={onSelect} className={COMMAND_ITEM_CLASSNAME}>
11764
<Icon className='size-[16px] flex-shrink-0 text-[var(--text-icon)]' />
118-
<span className='truncate text-[var(--text-body)]'>
119-
<HighlightedText text={name} query={query} />
120-
</span>
65+
<span className='truncate text-[var(--text-body)]'>{name}</span>
12166
{shortcut && (
12267
<span className='ml-auto flex-shrink-0 text-[var(--text-subtle)] text-small'>
12368
{shortcut}
@@ -130,8 +75,7 @@ export const MemoizedActionItem = memo(
13075
prev.value === next.value &&
13176
prev.icon === next.icon &&
13277
prev.name === next.name &&
133-
prev.shortcut === next.shortcut &&
134-
prev.query === next.query
78+
prev.shortcut === next.shortcut
13579
)
13680

13781
export const MemoizedWorkflowItem = memo(
@@ -141,24 +85,20 @@ export const MemoizedWorkflowItem = memo(
14185
name,
14286
folderPath,
14387
isCurrent,
144-
query,
14588
}: {
14689
value: string
14790
onSelect: () => void
14891
name: string
14992
folderPath?: string[]
15093
isCurrent?: boolean
151-
query?: string
15294
}) {
15395
return (
15496
<Command.Item value={value} onSelect={onSelect} className={COMMAND_ITEM_CLASSNAME}>
15597
<div className='relative flex size-[16px] flex-shrink-0 items-center justify-center'>
15698
<Workflow className='size-[14px] text-[var(--text-icon)]' />
15799
</div>
158100
<span className='flex min-w-0 max-w-[75%] flex-shrink-0 text-[var(--text-body)]'>
159-
<span className='truncate'>
160-
<HighlightedText text={name} query={query} />
161-
</span>
101+
<span className='truncate'>{name}</span>
162102
{isCurrent && <span className='flex-shrink-0 whitespace-pre'> (current)</span>}
163103
</span>
164104
{folderPath && folderPath.length > 0 && (
@@ -181,7 +121,6 @@ export const MemoizedWorkflowItem = memo(
181121
prev.value === next.value &&
182122
prev.name === next.name &&
183123
prev.isCurrent === next.isCurrent &&
184-
prev.query === next.query &&
185124
(prev.folderPath === next.folderPath ||
186125
(prev.folderPath?.length === next.folderPath?.length &&
187126
(prev.folderPath ?? []).every((segment, i) => segment === next.folderPath?.[i])))
@@ -193,23 +132,19 @@ export const MemoizedFileItem = memo(
193132
onSelect,
194133
name,
195134
folderPath,
196-
query,
197135
}: {
198136
value: string
199137
onSelect: () => void
200138
name: string
201139
folderPath?: string[]
202-
query?: string
203140
}) {
204141
return (
205142
<Command.Item value={value} onSelect={onSelect} className={COMMAND_ITEM_CLASSNAME}>
206143
<div className='relative flex size-[16px] flex-shrink-0 items-center justify-center'>
207144
<File className='size-[14px] text-[var(--text-icon)]' />
208145
</div>
209146
<span className='flex min-w-0 max-w-[75%] flex-shrink-0 font-base text-[var(--text-body)]'>
210-
<span className='truncate'>
211-
<HighlightedText text={name} query={query} />
212-
</span>
147+
<span className='truncate'>{name}</span>
213148
</span>
214149
{folderPath && folderPath.length > 0 && (
215150
<span className='ml-auto flex min-w-0 pl-2 font-base text-[var(--text-subtle)] text-small'>
@@ -230,7 +165,6 @@ export const MemoizedFileItem = memo(
230165
(prev, next) =>
231166
prev.value === next.value &&
232167
prev.name === next.name &&
233-
prev.query === next.query &&
234168
(prev.folderPath === next.folderPath ||
235169
(prev.folderPath?.length === next.folderPath?.length &&
236170
(prev.folderPath ?? []).every((segment, i) => segment === next.folderPath?.[i])))
@@ -241,22 +175,18 @@ export const MemoizedTaskItem = memo(
241175
value,
242176
onSelect,
243177
name,
244-
query,
245178
}: {
246179
value: string
247180
onSelect: () => void
248181
name: string
249-
query?: string
250182
}) {
251183
return (
252184
<Command.Item value={value} onSelect={onSelect} className={COMMAND_ITEM_CLASSNAME}>
253-
<span className='truncate text-[var(--text-body)]'>
254-
<HighlightedText text={name} query={query} />
255-
</span>
185+
<span className='truncate text-[var(--text-body)]'>{name}</span>
256186
</Command.Item>
257187
)
258188
},
259-
(prev, next) => prev.value === next.value && prev.name === next.name && prev.query === next.query
189+
(prev, next) => prev.value === next.value && prev.name === next.name
260190
)
261191

262192
export const MemoizedWorkspaceItem = memo(
@@ -265,30 +195,23 @@ export const MemoizedWorkspaceItem = memo(
265195
onSelect,
266196
name,
267197
isCurrent,
268-
query,
269198
}: {
270199
value: string
271200
onSelect: () => void
272201
name: string
273202
isCurrent?: boolean
274-
query?: string
275203
}) {
276204
return (
277205
<Command.Item value={value} onSelect={onSelect} className={COMMAND_ITEM_CLASSNAME}>
278206
<span className='flex min-w-0 text-[var(--text-body)]'>
279-
<span className='truncate'>
280-
<HighlightedText text={name} query={query} />
281-
</span>
207+
<span className='truncate'>{name}</span>
282208
{isCurrent && <span className='flex-shrink-0 whitespace-pre'> (current)</span>}
283209
</span>
284210
</Command.Item>
285211
)
286212
},
287213
(prev, next) =>
288-
prev.value === next.value &&
289-
prev.name === next.name &&
290-
prev.isCurrent === next.isCurrent &&
291-
prev.query === next.query
214+
prev.value === next.value && prev.name === next.name && prev.isCurrent === next.isCurrent
292215
)
293216

294217
export const MemoizedPageItem = memo(
@@ -298,21 +221,17 @@ export const MemoizedPageItem = memo(
298221
icon: Icon,
299222
name,
300223
shortcut,
301-
query,
302224
}: {
303225
value: string
304226
onSelect: () => void
305227
icon: ComponentType<{ className?: string }>
306228
name: string
307229
shortcut?: string
308-
query?: string
309230
}) {
310231
return (
311232
<Command.Item value={value} onSelect={onSelect} className={COMMAND_ITEM_CLASSNAME}>
312233
<Icon className='size-[16px] flex-shrink-0 text-[var(--text-icon)]' />
313-
<span className='truncate text-[var(--text-body)]'>
314-
<HighlightedText text={name} query={query} />
315-
</span>
234+
<span className='truncate text-[var(--text-body)]'>{name}</span>
316235
{shortcut && (
317236
<span className='ml-auto flex-shrink-0 text-[var(--text-subtle)] text-small'>
318237
{shortcut}
@@ -325,8 +244,7 @@ export const MemoizedPageItem = memo(
325244
prev.value === next.value &&
326245
prev.icon === next.icon &&
327246
prev.name === next.name &&
328-
prev.shortcut === next.shortcut &&
329-
prev.query === next.query
247+
prev.shortcut === next.shortcut
330248
)
331249

332250
export const MemoizedIconItem = memo(
@@ -335,26 +253,18 @@ export const MemoizedIconItem = memo(
335253
onSelect,
336254
name,
337255
icon: Icon,
338-
query,
339256
}: {
340257
value: string
341258
onSelect: () => void
342259
name: string
343260
icon: ComponentType<{ className?: string }>
344-
query?: string
345261
}) {
346262
return (
347263
<Command.Item value={value} onSelect={onSelect} className={COMMAND_ITEM_CLASSNAME}>
348264
<Icon className='size-[16px] flex-shrink-0 text-[var(--text-icon)]' />
349-
<span className='truncate text-[var(--text-body)]'>
350-
<HighlightedText text={name} query={query} />
351-
</span>
265+
<span className='truncate text-[var(--text-body)]'>{name}</span>
352266
</Command.Item>
353267
)
354268
},
355-
(prev, next) =>
356-
prev.value === next.value &&
357-
prev.name === next.name &&
358-
prev.icon === next.icon &&
359-
prev.query === next.query
269+
(prev, next) => prev.value === next.value && prev.name === next.name && prev.icon === next.icon
360270
)

apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/search-modal/components/command-items/index.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
export {
2-
HighlightedText,
32
MemoizedActionItem,
43
MemoizedCommandItem,
54
MemoizedFileItem,

0 commit comments

Comments
 (0)