Skip to content

Commit 7e7fa2d

Browse files
committed
perf(search): cap every result group so large workspaces never flood the palette
Workflows/files/chats/tables/KBs were rendered in full at the empty state, so a workspace with thousands of them dumped thousands of rows into the DOM. Factor the catalog cap into a shared filterAndCap() and apply it to all variable-size groups, bounding palette DOM to MAX_RESULTS_PER_GROUP per group regardless of workspace size.
1 parent e3855dc commit 7e7fa2d

1 file changed

Lines changed: 22 additions & 14 deletions

File tree

  • apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/search-modal

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

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -100,19 +100,27 @@ function toRecentRow(
100100
}
101101

102102
/**
103-
* Filters a catalog group for the global search view: empty until the user is
104-
* actually searching (`enabled`), then score-sorted and capped so a broad query
105-
* never floods the DOM. Single source of the gate-and-cap rule shared by the
106-
* blocks, tools, triggers, tool-operations, and docs groups.
103+
* Score-sorts a group and caps it to {@link MAX_RESULTS_PER_GROUP} so no single
104+
* group can flood the DOM — neither a broad query nor a large workspace (which
105+
* can hold thousands of workflows/files). Results are ranked, so the cap only
106+
* trims the low-relevance tail.
107+
*/
108+
function filterAndCap<T>(items: T[], toValue: (item: T) => string, search: string): T[] {
109+
return filterAndSort(items, toValue, search).slice(0, MAX_RESULTS_PER_GROUP)
110+
}
111+
112+
/**
113+
* {@link filterAndCap} for the catalog groups, which stay empty until the user
114+
* is actually searching (`enabled`). Single source of the gate-and-cap rule
115+
* shared by the blocks, tools, triggers, tool-operations, and docs groups.
107116
*/
108117
function cappedCatalog<T>(
109118
enabled: boolean,
110119
items: T[],
111120
toValue: (item: T) => string,
112121
search: string
113122
): T[] {
114-
if (!enabled) return []
115-
return filterAndSort(items, toValue, search).slice(0, MAX_RESULTS_PER_GROUP)
123+
return enabled ? filterAndCap(items, toValue, search) : []
116124
}
117125

118126
export type { SearchModalProps } from './utils'
@@ -801,29 +809,29 @@ export function SearchModal({
801809
])
802810

803811
const filteredTables = useMemo(
804-
() => filterAndSort(tables, (t) => t.name, deferredSearch),
812+
() => filterAndCap(tables, (t) => t.name, deferredSearch),
805813
[tables, deferredSearch]
806814
)
807815
const filteredFiles = useMemo(
808-
() => filterAndSort(files, (f) => `${f.name} ${f.folderPath?.join(' ') ?? ''}`, deferredSearch),
816+
() => filterAndCap(files, (f) => `${f.name} ${f.folderPath?.join(' ') ?? ''}`, deferredSearch),
809817
[files, deferredSearch]
810818
)
811819
const filteredKnowledgeBases = useMemo(
812-
() => filterAndSort(knowledgeBases, (kb) => kb.name, deferredSearch),
820+
() => filterAndCap(knowledgeBases, (kb) => kb.name, deferredSearch),
813821
[knowledgeBases, deferredSearch]
814822
)
815823

816824
const filteredWorkflows = useMemo(
817825
() =>
818-
filterAndSort(workflows, (w) => `${w.name} ${w.folderPath?.join(' ') ?? ''}`, deferredSearch),
826+
filterAndCap(workflows, (w) => `${w.name} ${w.folderPath?.join(' ') ?? ''}`, deferredSearch),
819827
[workflows, deferredSearch]
820828
)
821829
const filteredChats = useMemo(
822-
() => filterAndSort(chats, (t) => t.name, deferredSearch),
830+
() => filterAndCap(chats, (t) => t.name, deferredSearch),
823831
[chats, deferredSearch]
824832
)
825833
const filteredWorkspaces = useMemo(
826-
() => filterAndSort(workspaces, (w) => w.name, deferredSearch),
834+
() => filterAndCap(workspaces, (w) => w.name, deferredSearch),
827835
[workspaces, deferredSearch]
828836
)
829837
const filteredPages = useMemo(
@@ -834,13 +842,13 @@ export function SearchModal({
834842
/** Connected accounts: visible on the integrations page even with empty input. */
835843
const filteredConnectedAccounts = useMemo(() => {
836844
if (!isOnIntegrationsPage) return []
837-
return filterAndSort(connectedAccounts, (a) => a.name, deferredSearch)
845+
return filterAndCap(connectedAccounts, (a) => a.name, deferredSearch)
838846
}, [isOnIntegrationsPage, connectedAccounts, deferredSearch])
839847

840848
/** Catalog integrations: only shown once the user has typed something. */
841849
const filteredIntegrations = useMemo(() => {
842850
if (!isOnIntegrationsPage || !deferredSearch) return []
843-
return filterAndSort(integrations, (i) => i.name, deferredSearch)
851+
return filterAndCap(integrations, (i) => i.name, deferredSearch)
844852
}, [isOnIntegrationsPage, deferredSearch, integrations])
845853

846854
if (!mounted) return null

0 commit comments

Comments
 (0)