Stop showing slash commands in general Go to Anything search (#29012)

This commit is contained in:
yyh 2025-12-02 14:24:21 +08:00 committed by GitHub
parent a85afe4d07
commit 8e5cb86409
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 29 additions and 12 deletions

View File

@ -214,8 +214,12 @@ export const searchAnything = async (
actionItem?: ActionItem, actionItem?: ActionItem,
dynamicActions?: Record<string, ActionItem>, dynamicActions?: Record<string, ActionItem>,
): Promise<SearchResult[]> => { ): Promise<SearchResult[]> => {
const trimmedQuery = query.trim()
if (actionItem) { if (actionItem) {
const searchTerm = query.replace(actionItem.key, '').replace(actionItem.shortcut, '').trim() const escapeRegExp = (value: string) => value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
const prefixPattern = new RegExp(`^(${escapeRegExp(actionItem.key)}|${escapeRegExp(actionItem.shortcut)})\\s*`)
const searchTerm = trimmedQuery.replace(prefixPattern, '').trim()
try { try {
return await actionItem.search(query, searchTerm, locale) return await actionItem.search(query, searchTerm, locale)
} }
@ -225,10 +229,12 @@ export const searchAnything = async (
} }
} }
if (query.startsWith('@') || query.startsWith('/')) if (trimmedQuery.startsWith('@') || trimmedQuery.startsWith('/'))
return [] return []
const globalSearchActions = Object.values(dynamicActions || Actions) const globalSearchActions = Object.values(dynamicActions || Actions)
// Exclude slash commands from general search results
.filter(action => action.key !== '/')
// Use Promise.allSettled to handle partial failures gracefully // Use Promise.allSettled to handle partial failures gracefully
const searchPromises = globalSearchActions.map(async (action) => { const searchPromises = globalSearchActions.map(async (action) => {

View File

@ -177,31 +177,42 @@ const GotoAnything: FC<Props> = ({
} }
}, [router]) }, [router])
const dedupedResults = useMemo(() => {
const seen = new Set<string>()
return searchResults.filter((result) => {
const key = `${result.type}-${result.id}`
if (seen.has(key))
return false
seen.add(key)
return true
})
}, [searchResults])
// Group results by type // Group results by type
const groupedResults = useMemo(() => searchResults.reduce((acc, result) => { const groupedResults = useMemo(() => dedupedResults.reduce((acc, result) => {
if (!acc[result.type]) if (!acc[result.type])
acc[result.type] = [] acc[result.type] = []
acc[result.type].push(result) acc[result.type].push(result)
return acc return acc
}, {} as { [key: string]: SearchResult[] }), }, {} as { [key: string]: SearchResult[] }),
[searchResults]) [dedupedResults])
useEffect(() => { useEffect(() => {
if (isCommandsMode) if (isCommandsMode)
return return
if (!searchResults.length) if (!dedupedResults.length)
return return
const currentValueExists = searchResults.some(result => `${result.type}-${result.id}` === cmdVal) const currentValueExists = dedupedResults.some(result => `${result.type}-${result.id}` === cmdVal)
if (!currentValueExists) if (!currentValueExists)
setCmdVal(`${searchResults[0].type}-${searchResults[0].id}`) setCmdVal(`${dedupedResults[0].type}-${dedupedResults[0].id}`)
}, [isCommandsMode, searchResults, cmdVal]) }, [isCommandsMode, dedupedResults, cmdVal])
const emptyResult = useMemo(() => { const emptyResult = useMemo(() => {
if (searchResults.length || !searchQuery.trim() || isLoading || isCommandsMode) if (dedupedResults.length || !searchQuery.trim() || isLoading || isCommandsMode)
return null return null
const isCommandSearch = searchMode !== 'general' const isCommandSearch = searchMode !== 'general'
@ -246,7 +257,7 @@ const GotoAnything: FC<Props> = ({
</div> </div>
</div> </div>
) )
}, [searchResults, searchQuery, Actions, searchMode, isLoading, isError, isCommandsMode]) }, [dedupedResults, searchQuery, Actions, searchMode, isLoading, isError, isCommandsMode])
const defaultUI = useMemo(() => { const defaultUI = useMemo(() => {
if (searchQuery.trim()) if (searchQuery.trim())
@ -430,14 +441,14 @@ const GotoAnything: FC<Props> = ({
{/* Always show footer to prevent height jumping */} {/* Always show footer to prevent height jumping */}
<div className='border-t border-divider-subtle bg-components-panel-bg-blur px-4 py-2 text-xs text-text-tertiary'> <div className='border-t border-divider-subtle bg-components-panel-bg-blur px-4 py-2 text-xs text-text-tertiary'>
<div className='flex min-h-[16px] items-center justify-between'> <div className='flex min-h-[16px] items-center justify-between'>
{(!!searchResults.length || isError) ? ( {(!!dedupedResults.length || isError) ? (
<> <>
<span> <span>
{isError ? ( {isError ? (
<span className='text-red-500'>{t('app.gotoAnything.someServicesUnavailable')}</span> <span className='text-red-500'>{t('app.gotoAnything.someServicesUnavailable')}</span>
) : ( ) : (
<> <>
{t('app.gotoAnything.resultCount', { count: searchResults.length })} {t('app.gotoAnything.resultCount', { count: dedupedResults.length })}
{searchMode !== 'general' && ( {searchMode !== 'general' && (
<span className='ml-2 opacity-60'> <span className='ml-2 opacity-60'>
{t('app.gotoAnything.inScope', { scope: searchMode.replace('@', '') })} {t('app.gotoAnything.inScope', { scope: searchMode.replace('@', '') })}