Skip to content

refactor(sidebar): connection-scoped favorites + multi-window tab persistence fix#1000

Merged
datlechin merged 4 commits intomainfrom
refactor/connection-scoped-sidebar-state
May 5, 2026
Merged

refactor(sidebar): connection-scoped favorites + multi-window tab persistence fix#1000
datlechin merged 4 commits intomainfrom
refactor/connection-scoped-sidebar-state

Conversation

@datlechin
Copy link
Copy Markdown
Member

Summary

  • Sidebar refactor: favorites tree, folders, linked-files, and selection are now per-connection rather than per-window, so opening a second native tab on the same connection no longer flashes a spinner or reloads from SQLite. Mirrors Apple's pattern (Notes / Mail / Music) of binding sidebar state to data identity.
  • Per-window favorites search: each window of the same connection can now filter favorites independently. Tables search remains connection-shared (intentional — separate scope).
  • Multi-window tab persistence fix: closes a long-standing race where Cmd+Q only restored one tab per connection on relaunch. Three save paths each wrote single-window tabs and clobbered the canonical willTerminate aggregate save.

Architecture (commit 1: refactor)

Three new types under TablePro/:

  • ViewModels/ConnectionDataCache@MainActor @Observable per-connection singleton holding folders, favorites, linkedFolders, linkedFilesByFolderId. Subscribes to .sqlFavoritesDidUpdate and .linkedSQLFoldersDidUpdate. scheduleRefresh() cancels in-flight tasks so notification storms coalesce.
  • ViewModels/ConnectionSidebarState — per-connection holder for selectedFavoriteNodeId, persisted to UserDefaults via didSet so cross-window selection sync and across-launch persistence share one code path.
  • Models/UI/WindowSidebarState — per-window holder for favoritesSearchText, owned by MainContentCoordinator.

FavoritesSidebarViewModel becomes a thin facade over the cache: nodes is a computed property, loadFavorites() / isLoading / observer plumbing removed. The sidebar's single NSSearchField (in SidebarContainerViewController) routes to SharedSidebarState.searchText on Tables and WindowSidebarState.favoritesSearchText on Favorites based on the active tab.

Two render-timing fixes for the new-tab mount path: .transaction { animation = nil } on the SidebarView root (matching the existing pattern in buildDetailView), and view.layoutSubtreeIfNeeded() + view.display() in MainSplitViewController.viewWillAppear so SwiftUI evaluates and renders before AppKit's tab-slide animation completes.

Persistence fix (commit 2)

Three save paths each saved only tabManager.tabs (current window) instead of aggregated tabs across all windows of the connection. On Cmd+Q, AppKit closes windows LIFO and the last per-window save overwrote the willTerminate observer's aggregated save with a single-tab payload.

  • MainContentView+EventHandlers.handleTabSelectionChange now writes MainContentCoordinator.aggregatedTabs(for:) instead of tabManager.tabs. Tab-switch in window 1 no longer drops windows 2/3 from the saved file.
  • MainContentCommandActions.setupWindowObservers skips during isAppTerminating and routes through saveOrClearAggregated() otherwise.
  • MainContentCoordinator+WindowLifecycle.handleWindowWillClose skips its save during quit; the willTerminate observer's aggregate save is now the only quit-time write.

Test plan

  • Connect to any DB, open Favorites tab, confirm tree renders.
  • Double-click a linked favorite — second native tab opens with no spinner / no flicker on the favorites pane.
  • Click a favorite in window A — confirm same row highlights in window B.
  • Type a filter in Favorites search of window A — confirm window B's filter is independent (per-window).
  • Type a filter in Tables search of window A — confirm window B's tables filter mirrors (still per-connection).
  • Quit, relaunch, reopen the same connection — selected favorite stays highlighted (UserDefaults persistence).
  • Edit a linked .sql file externally — both windows refresh within ~1.5 s via SQLFolderWatcher notification.
  • Persistence regression: open 3+ linked-favorite tabs across native windows, Cmd+Q, relaunch — all 3 tabs restore (was 1 before).
  • swiftlint lint --strict passes.

@chatgpt-codex-connector
Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@datlechin datlechin merged commit 7b4012a into main May 5, 2026
2 checks passed
@datlechin datlechin deleted the refactor/connection-scoped-sidebar-state branch May 5, 2026 12:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant