@@ -287,23 +287,26 @@ final class AppState {
287 // If the active connection was edited, its URL or credentials may now point
287 // If the active connection was edited, its URL or credentials may now point
288 // at a different server, so swapping the client alone isn't enough — the
288 // at a different server, so swapping the client alone isn't enough — the
289 // displayed streams/selection/server-info would be stale and queries would
289 // displayed streams/selection/server-info would be stale and queries would
290+ // 404. The caller is responsible for the actual reconnect (re-validate
290− // 404. Reconnect end to end (re-validate health, reload streams/filters/
291+ // health, reload streams/filters/about); we only clear the now-possibly-
291− // about). Clear the stream selection first so a now-missing stream doesn't
292+ // stale UI state here, since `performConnect` skips its own cleanup when the
292− // keep driving detail-tab requests during the reconnect.
293+ // connection id is unchanged. We must NOT also spawn a connect here: the
294+ // sole caller (ConnectionSheet.saveAndConnect) already connects right after,
295+ // and a second connect would run two full reconnects and flash the UI twice.
293 if activeConnection?.id == connection.id {
296 if activeConnection?.id == connection.id {
294− // `performConnect` skips its stale-state cleanup because the id is
295− // unchanged, but the edited URL/credentials may point at a different
296− // server. Clear the stream-specific UI state here so a leftover sidebar
297+ // Clear the stream-specific UI state so a leftover sidebar search filter
297− // search filter doesn't hide the new server's streams and a
298+ // doesn't hide the new server's streams and a stream-specific tab
298− // stream-specific tab doesn't linger with no valid selection.
299+ // doesn't linger with no valid selection.
299 selectedStream = nil
300 selectedStream = nil
300 streamSearchText = ""
301 streamSearchText = ""
301 currentTab = .query
302 currentTab = .query
302− // Drop the previous server's cached `about` too — performConnect skips
303+ // Drop the previous server's cached `about`, stream list, and filters —
303− // its own clear (id unchanged), so without this ServerInfoView keeps
304+ // performConnect skips its own clear (id unchanged), so without this the
305+ // sidebar keeps rendering server A's streams/filters and ServerInfoView
304− // painting the old server's version/storage until the new /about lands.
306+ // paints the old version/storage until the new server's responses land.
305 serverAbout = nil
307 serverAbout = nil
306− Task { await connect(to: connection) }
308+ streams = []