@@ -140,14 +140,16 @@ class FileBrowserViewModel(private val container: AppContainer) : ViewModel() {
141 // The search view replaces the directory listing; clear loading so the spinner
141 // The search view replaces the directory listing; clear loading so the spinner
142 // from a cancelled open() doesn't persist into the search results view.
142 // from a cancelled open() doesn't persist into the search results view.
143+ // Set searching=true immediately (not after the debounce) so the spinner shows
144+ // during the debounce delay — otherwise the UI briefly renders SearchResults
145+ // with an empty list, flashing "no matches" before the search even starts.
143− _state.update { it.copy(loading = false) }
146+ _state.update { it.copy(loading = false, searching = true) }
144 val client = api ?: run {
147 val client = api ?: run {
145 _state.update { it.copy(searching = false, error = container.string(R.string.not_connected)) }
148 _state.update { it.copy(searching = false, error = container.string(R.string.not_connected)) }
148 searchJob = viewModelScope.launch {
151 searchJob = viewModelScope.launch {
149 delay(NetworkConfig.fileSearchDebounceMs) // debounce
152 delay(NetworkConfig.fileSearchDebounceMs) // debounce
150− _state.update { it.copy(searching = true) }
151 runCatchingCancellable { client.findFiles(query) }
153 runCatchingCancellable { client.findFiles(query) }
152 .onSuccess { results -> _state.update { it.copy(results = results, searching = false) } }
154 .onSuccess { results -> _state.update { it.copy(results = results, searching = false) } }
153 .onFailure { e -> _state.update { it.copy(searching = false, error = container.friendlyError(e)) } }
155 .onFailure { e -> _state.update { it.copy(searching = false, error = container.friendlyError(e)) } }