Skip to content

feat(sidebar): show all databases on the server as a tree (#139)#1473

Merged
datlechin merged 19 commits into
mainfrom
feat/139-sidebar-database-tree
May 29, 2026
Merged

feat(sidebar): show all databases on the server as a tree (#139)#1473
datlechin merged 19 commits into
mainfrom
feat/139-sidebar-database-tree

Conversation

@datlechin

Copy link
Copy Markdown
Member

Summary

  • Sidebar now lists every database on the server as an expandable tree (feat: show all databases in sidebar tree #139). Browse tables of any database without leaving the one you're working in; double-click or right-click → "Use as Active" to switch context.
  • Applies to MySQL, MariaDB, PostgreSQL, MSSQL, ClickHouse, Redshift, and other network engines with database switching. SQLite, Redis, MongoDB, BigQuery keep their existing sidebar.
  • Non-active databases load lazily through a per-connection metadata pool (read-only secondary drivers, 15s connect timeout, in-flight tracking on eviction).
  • Active-database routing consolidated: view talks to a single DatabaseTreeMetadataService facade which delegates to SchemaService internally for the active database, eliminating two-sources-of-truth drift.
  • System databases (mysql, information_schema, etc.) hidden by default; active database/schema shown via bold + tint (no iOS checkmark icon); set-active via right-click context menu; opening a non-active database's table auto-switches first.

Architecture changes

  • New: DatabaseTreeView, DatabaseTreeMetadataService, MetadataConnectionPool.
  • SchemaPickerFooter hidden when the tree is in use to avoid dual schema-switch controls.
  • SchemaService gains a per-connection generation counter so SidebarViewModel filter cache fingerprints are O(1) instead of [TableInfo].hashValue O(n).
  • SidebarViewModel.shared(connectionId:databaseType:bindings:) registry: VM survives SidebarView rebuilds across rebuildPanes/AnyView wrapping; cleanup hook in disconnectSession.
  • SidebarContainerViewController replaces recursive withObservationTracking re-registration with a Task-based await loop.

Sidebar polish + bug fixes

  • FavoritesTabView recursive AnyView(ForEach) replaced with named recursive FavoriteNodeRowsView struct.
  • expandedSchemas / expandedDatabases / expandedTreeDatabaseSchemas moved from @State to WindowSidebarState so expansion survives view recreation.
  • SidebarTreeView search load throttled (300ms debounce, skips already-loaded schemas).
  • TableRowView ZStack badge → .overlay(alignment: .bottomTrailing); monospaced font replaced with system sans-serif (matches Xcode/Finder).
  • SchemaPickerFooter schemaVersion notification hack removed (uses @Bindable DatabaseManager.shared native observation).
  • Eight SidebarContextMenu button literals + SidebarTreeView / RedisKeyTreeView / Keys header / FavoritesTabView empty-state strings localized.
  • Tree row click uses native List.contextMenu(forSelectionType:) primaryAction: (Mail/Finder/Xcode pattern). DatabaseTreeView holds its own selection state to avoid the legacy MainContentView.onChange(selectedTables) auto-open bypass that skipped switchDatabase.
  • .task(id:) on .idle/.loading rows auto-triggers load so ex-active databases don't get stuck on "Loading…" after a switch.

Test plan

  • MySQL connection with ≥2 databases: tree shows all databases, system DBs hidden, active database auto-expanded.
  • Expand non-active database → tables load via secondary metadata connection (verify in Activity Monitor / pg_stat_activity).
  • Right-click a database → "Use as Active Database" switches without reconnect (MySQL) or reconnects cleanly (Postgres).
  • Double-click table in non-active database → switches active first, opens tab, query runs against correct DB (no Table 'old_db.x' doesn't exist errors).
  • Switch active database via Cmd+K → ex-active database section in tree reloads automatically (no perpetual "Loading…").
  • Single click on row body (text, icon, edge) all select; double-click anywhere opens.
  • Disconnect → secondary metadata connections close (no leaked SSH tunnels).
  • SQLite / Redis / MongoDB / BigQuery: sidebar unchanged.

@datlechin datlechin force-pushed the feat/139-sidebar-database-tree branch from a0c45b7 to 96ff6fa Compare May 29, 2026 01:07
@datlechin datlechin force-pushed the feat/139-sidebar-database-tree branch from 96ff6fa to 04b5f3e Compare May 29, 2026 01:08

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a0c45b737f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +116 to +117
if candidates.contains(where: { $0.id == target.id }) {
return (db.name, schema)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Include the database in tree table identity

When two databases contain the same schema/table/type, TableInfo.id and Hashable do not include the database, so this lookup treats the row selected in the later database as a match for the first visible database with the same table. In the new multi-database tree, double-clicking db2.public.users can therefore switch/open db1.public.users instead, because locateTable returns the first matching id.

Useful? React with 👍 / 👎.

Comment on lines +111 to +113
func routines(connectionId: UUID, database: String, schema: String?) -> [RoutineInfo] {
guard database == activeDatabase(for: connectionId) else { return [] }
return SchemaService.shared.routines(for: connectionId)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Filter active routines by schema

For schema-level databases, this returns every routine from the active database regardless of the schema argument, even though RoutineInfo carries its schema. As a result, when the active PostgreSQL/MSSQL/Oracle database is expanded, the same procedures/functions are rendered under every schema instead of only under their own schema.

Useful? React with 👍 / 👎.

Comment on lines +122 to +124
let idleEntries = mine.filter { $0.value.inFlightCount == 0 }
let pool = idleEntries.isEmpty ? mine : idleEntries
guard let oldest = pool.min(by: { $0.value.lastUsed < $1.value.lastUsed }) else { return }

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Avoid evicting in-flight metadata connections

If a user expands or searches enough databases to start metadata loads for more than four databases at once, idleEntries can be empty and this falls back to evicting from mine, then disconnects a driver whose withDriver body is still running. That can abort an otherwise valid schema/table fetch and leave the tree in a failed/loading state just because concurrent loads hit the pool cap.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e7f293cc53

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

private var visibleDatabases: [DatabaseMetadata] {
let nonSystem = databases.filter { !$0.isSystemDatabase }
guard !searchText.isEmpty else { return nonSystem }
return nonSystem.filter { databaseMatchesSearch($0) }

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Load metadata before filtering tree search results

When the search text is a schema/table/routine name in a database that has not already been expanded, this filter drops that database while its schema/table state is still idle, and no task is scheduled to load its contents. For example, searching for users only finds databases whose contents were previously expanded; unexpanded databases whose names do not contain users never render schemasContent/tablesContent, so their .task loaders never run.

Useful? React with 👍 / 👎.

Comment on lines +110 to +116
SidebarContextMenu(
clickedTable: selection.first,
selectedTables: selection,
isReadOnly: coordinator?.safeModeLevel.blocksAllWrites ?? false,
onBatchToggleTruncate: { viewModel.batchToggleTruncate(tableNames: $0) },
onBatchToggleDelete: { viewModel.batchToggleDelete(tableNames: $0) },
coordinator: coordinator

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Resolve the database before showing table context actions

In the database tree, the context menu receives only the selected TableInfo, while the menu actions call coordinator APIs that operate in the current active database (or pass only table names for export/delete/truncate). Unlike the primary action below, this path never calls locateTable or switches to the row's database, so right-clicking a table under a non-active database can show structure or queue destructive/export operations against the active database's table with the same name instead.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c88cff546a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +503 to +506
if let schema,
schema != coordinator?.toolbarState.currentSchema,
PluginManager.shared.supportsSchemaSwitching(for: databaseType) {
await coordinator?.switchSchema(to: schema)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Switch schema after changing databases

When opening a table in another database, this comparison still uses the toolbar schema value from the previous database; switchDatabase(to:) updates the database first but the session is reset to the default schema, so if the target schema has the same name as the old toolbar schema (for example moving from db1.sales to db2.sales), this branch skips switchSchema and opens the table against the wrong schema in the new database.

Useful? React with 👍 / 👎.

Signed-off-by: Ngô Quốc Đạt <datlechin@gmail.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 50e90a834c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +155 to +156
try await group.next()
group.cancelAll()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Avoid waiting on timed-out metadata connects

When the timeout child wins, this withThrowingTaskGroup still waits for the driver.connect() child to finish as the group scope exits; cancelAll() only requests cancellation. For drivers whose connect call is blocked in client/network code and does not promptly observe Swift task cancellation, expanding an unreachable database can hang well past the intended 15-second metadata timeout instead of surfacing the timeout error.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 25cee6a451

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

.contextMenu(forSelectionType: DatabaseTreeTableRef.self) { selection in
SidebarContextMenu(
clickedTable: selection.first?.table,
selectedTables: Set(selection.map(\.table)),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Preserve database context for batched tree actions

When a tree selection spans more than one database, this conversion drops the database/schema from each DatabaseTreeTableRef while activateBeforeAction switches only to selection.first. A Command-click selection like db1.users plus db2.orders followed by Truncate/Delete therefore queues both table names against whichever database happens to be first, so one selected table can be operated on in the wrong database.

Useful? React with 👍 / 👎.

Comment on lines +90 to +91
get: { localSelection },
set: { localSelection = $0 }

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Sync tree selection with command actions

In tree layout this binding keeps selections only in localSelection, while app/menu commands such as Copy and Truncate Table check MainContentCommandActions.hasTableSelection, which reads windowState.selectedTables. Selecting a table in the new database tree therefore leaves those commands disabled (or no-op) even though a sidebar row is selected.

Useful? React with 👍 / 👎.

PluginManager.shared.supportsSchemaSwitching(for: databaseType) {
await coordinator?.switchSchema(to: schema)
}
coordinator?.openTableTab(table)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Stop opening after a failed context switch

If switching to the target database or schema fails, switchDatabase/switchSchema handle the error internally and return, so this still opens the selected TableInfo in the previous active context. For a database that is listed but later fails to switch (for example reconnect/permission failure), double-clicking its table can open or query the same-named table in the old database after the error alert.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 82323b797e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +22 to +29
if let existing = registry[connectionId] {
existing.updateBindings(
selectedTables: selectedTables,
pendingTruncates: pendingTruncates,
pendingDeletes: pendingDeletes,
tableOperationOptions: tableOperationOptions
)
return existing

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Avoid sharing window-bound sidebar bindings across windows

When a second window for the same connection constructs a sidebar, this branch returns the existing SidebarViewModel after replacing its selectedTables/pending-operation bindings with the new window's WindowSidebarState bindings. The first window still holds the same shared view model, so table selections, copy/delete/truncate enablement, and pending operation state in that window now read and write the second window's sidebar state instead of its own. This only shows up with multiple windows on one connection, but it breaks the window-scoped state separation introduced by WindowSidebarState.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 635b620f79

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

}

private var visibleDatabases: [DatabaseMetadata] {
let nonSystem = databases.filter { !$0.isSystemDatabase }

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep the active system database visible

When the current database is one of the configured system databases (for example PostgreSQL's default postgres or SQL Server's master), this filter removes it before rendering, so the active database and its tables disappear from the tree; if it is the only database returned, the sidebar becomes a blank list even though the connection is usable. The tree should still include the active database (or otherwise provide a way to show it) while hiding inactive system databases by default.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 397f380a88

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +544 to +545
if schema != coordinator?.toolbarState.currentSchema {
await coordinator?.switchSchema(to: schema)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Always switch schema after changing databases

When “Use as Active Schema” is invoked for a schema in a different database, this check still compares against the toolbar schema from the previous database after switchDatabase returns. On reconnecting engines such as PostgreSQL, switching databases clears/resets the session schema, so choosing db2.sales while currently on db1.sales skips switchSchema(to: "sales") and leaves the new database on its default/no schema instead of the requested schema.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 186f2cc51f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +305 to +306
RoutineContextMenu(routine: routine) { selected in
coordinator?.showRoutineDDL(selected)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Resolve the database before routine DDL actions

When a procedure/function row belongs to a non-active database, this context menu still calls showRoutineDDL on the current active driver without switching to the row's database or passing any database identity. Since RoutineInfo only carries the schema/name, asking for DDL on db2.public.fn while db1 is active will fetch the same-named routine from db1 (or fail) instead of the routine shown in the tree.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c951b76436

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +117 to +119
func routines(connectionId: UUID, database: String, schema: String?) -> [RoutineInfo] {
routineLists[Self.tableKey(connectionId: connectionId, database: database, schema: schema)] ?? []
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Read active routines from SchemaService

When the active database's tables are already loaded by SchemaService before this tree renders, reconcileLoads sees the active table state as .loaded and never calls loadTables, so routineLists remains empty even though SchemaService may already have procedures/functions. In that common path (or after refreshDatabase on the active DB, which only refreshes SchemaService), the tree shows active tables but drops the active database's routines until some later tree-specific load happens; delegate active-database routine reads to SchemaService or keep this cache synchronized.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: fba22726d0

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

private func expansionBinding(for schema: String) -> Binding<Bool> {
Binding(
get: { !searchText.isEmpty || expandedSchemas.contains(schema) },
get: { !searchText.isEmpty || windowState.expandedTreeSchemas.contains(schema) },

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Load persisted expanded schemas

When a BigQuery-style schema tree keeps a dataset expanded in windowState.expandedTreeSchemas, this getter reopens the section without calling loadTables(for:); the only load trigger remains the binding setter. After a reconnect, schema refresh, or reopening this view with an already-expanded dataset whose per-schema state is idle, the expanded section renders the “Loading tables…” row indefinitely until the user collapses and expands it again.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 740f455693

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +331 to +332
isPendingTruncate: pendingTruncates.contains(ref.table.name),
isPendingDelete: pendingDeletes.contains(ref.table.name)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Scope pending badges to the tree table ref

When two visible databases or schemas contain the same table name, these checks mark every matching row as pending because the pending sets are keyed only by table.name. For example, scheduling truncate for db1.public.users will also show the truncate badge on db2.public.users, which makes the tree indicate that the wrong table is queued even before any operation is run.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4071b5f06d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +734 to +737
await DatabaseTreeMetadataService.shared.loadDatabases(
connectionId: connectionId,
databaseType: connection.type
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Skip tree metadata preload when tree is unavailable

For connections where supportsDatabaseTree is false, this still awaits fetchDatabases() during every schema load even though the database tree cannot be shown. That adds an unnecessary startup/reload API call for flat-only or unsupported drivers (for example API/file-based connections) and can block initializeView() on listing all databases before post-schema reconciliation completes; guard this call with the same tree-support/layout conditions used by the sidebar.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 78c33bc145

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +138 to +142
async let procedures = driver.fetchProcedures(schema: normalizedSchema)
async let functions = driver.fetchFunctions(schema: normalizedSchema)
return SchemaObjects(
tables: try await tables,
routines: try await procedures + functions

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Do not fail table loads when routine metadata fails

When a database allows fetchTables but denies or errors on procedure/function metadata, these async let calls make the whole object load throw and render the schema/database as failed, hiding tables that could otherwise be shown. The flat SchemaService path already treats routine fetches as best-effort via fetchRoutinesSafely, so the new tree path should similarly load tables independently and only omit routines when those calls fail.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 569aacfdc7

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

EmptyView()
.contextMenu(forSelectionType: TableInfo.self) { selection in
SidebarContextMenu(
clickedTable: selection.first,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve the row target for context actions

When more than one table is selected in the flat sidebar, this passes selection.first from an unordered Set as the clicked table, so row-specific actions like Show Structure, Edit View Definition, and Maintenance can act on an arbitrary selected table rather than the row whose context menu the user opened. The previous per-row menu supplied the actual row as clickedTable, so multi-selecting tables and right-clicking one of them can now open or maintain the wrong object while batch actions still use the full selection.

Useful? React with 👍 / 👎.

@datlechin datlechin merged commit 634c3cb into main May 29, 2026
2 checks passed
@datlechin datlechin deleted the feat/139-sidebar-database-tree branch May 29, 2026 13:29
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