Keep command palette empty state stable while typing

This commit is contained in:
Lawrence Chen 2026-03-17 16:29:35 -07:00 committed by Lawrence Chen
parent 018510d4ef
commit 4162eccf66
2 changed files with 95 additions and 1 deletions

View file

@ -1404,6 +1404,7 @@ struct ContentView: View {
@State private var commandPaletteResolvedSearchRequestID: UInt64 = 0
@State private var commandPaletteResolvedSearchScope: CommandPaletteListScope?
@State private var commandPaletteResolvedSearchFingerprint: Int?
@State private var commandPaletteResolvedMatchingQuery = ""
@State private var isCommandPaletteSearchPending = false
@State private var commandPalettePendingActivation: CommandPalettePendingActivation?
@State private var commandPaletteResultsRevision: UInt64 = 0
@ -3257,7 +3258,7 @@ struct ContentView: View {
// stale switcher rows cannot linger above command-mode results.
VStack(spacing: 0) {
if visibleResults.isEmpty {
if commandPaletteHasCurrentResolvedResults {
if commandPaletteShouldShowEmptyState {
Text(commandPaletteEmptyStateText)
.font(.system(size: 13, weight: .regular))
.foregroundStyle(.secondary)
@ -4106,6 +4107,27 @@ struct ContentView: View {
!hasVisibleResultsForScope
}
static func commandPaletteShouldPreserveEmptyStateWhileSearchPending(
isSearchPending: Bool,
visibleResultsScopeMatches: Bool,
resolvedSearchScopeMatches: Bool,
resolvedSearchFingerprintMatches: Bool,
resolvedResultsAreEmpty: Bool,
currentMatchingQuery: String,
resolvedMatchingQuery: String
) -> Bool {
guard isSearchPending,
visibleResultsScopeMatches,
resolvedSearchScopeMatches,
resolvedSearchFingerprintMatches,
resolvedResultsAreEmpty else {
return false
}
return currentMatchingQuery == resolvedMatchingQuery
|| currentMatchingQuery.hasPrefix(resolvedMatchingQuery)
}
private func scheduleCommandPaletteResultsRefresh(
query: String? = nil,
forceSearchCorpusRefresh: Bool = false
@ -4152,6 +4174,7 @@ struct ContentView: View {
commandPaletteResolvedSearchRequestID = requestID
commandPaletteResolvedSearchScope = scope
commandPaletteResolvedSearchFingerprint = fingerprint
commandPaletteResolvedMatchingQuery = matchingQuery
isCommandPaletteSearchPending = false
setCommandPaletteVisibleResults(
cachedCommandPaletteResults,
@ -4210,6 +4233,7 @@ struct ContentView: View {
commandPaletteResolvedSearchRequestID = requestID
commandPaletteResolvedSearchScope = scope
commandPaletteResolvedSearchFingerprint = fingerprint
commandPaletteResolvedMatchingQuery = matchingQuery
isCommandPaletteSearchPending = false
setCommandPaletteVisibleResults(
cachedCommandPaletteResults,
@ -6096,6 +6120,23 @@ struct ContentView: View {
!isCommandPaletteSearchPending && commandPaletteResolvedSearchRequestID == commandPaletteSearchRequestID
}
private var commandPaletteShouldShowEmptyState: Bool {
guard commandPaletteVisibleResults.isEmpty else { return false }
if commandPaletteHasCurrentResolvedResults {
return true
}
return Self.commandPaletteShouldPreserveEmptyStateWhileSearchPending(
isSearchPending: isCommandPaletteSearchPending,
visibleResultsScopeMatches: commandPaletteVisibleResultsScope == commandPaletteListScope,
resolvedSearchScopeMatches: commandPaletteResolvedSearchScope == commandPaletteListScope,
resolvedSearchFingerprintMatches: commandPaletteResolvedSearchFingerprint == commandPaletteVisibleResultsFingerprint,
resolvedResultsAreEmpty: cachedCommandPaletteResults.isEmpty,
currentMatchingQuery: commandPaletteQueryForMatching,
resolvedMatchingQuery: commandPaletteResolvedMatchingQuery
)
}
private func runCommandPaletteResolvedActivation(_ activation: CommandPaletteResolvedActivation) {
switch activation {
case .command(let commandID):