From 8cdcbb2f3337aedd14fd18362de9d0a010215598 Mon Sep 17 00:00:00 2001 From: Ngo Quoc Dat Date: Wed, 6 May 2026 20:12:37 +0700 Subject: [PATCH 1/6] feat(toolbar): lead identity with connection name and color dot --- CHANGELOG.md | 1 + .../Views/Toolbar/ConnectionStatusView.swift | 51 ++++++++++++------- 2 files changed, 33 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b7212daf6..588866b2a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,6 +37,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- Toolbar connection identity now leads with the connection name and a small color dot, so multiple windows targeting the same database (e.g. `prod-safe`, `prod-unsafe`, `staging`, `local` against the same Postgres) are distinguishable at a glance instead of all reading "PostgreSQL 16.x". The colored dot follows the macOS HIG pattern used by Reminders / Calendar / Notes (small color shape next to a primary-colored title), so the color stays an augment to the text rather than the only signal. Database vendor and version move to the hover tooltip and the VoiceOver label, which now reads "Connection: prod-safe, PostgreSQL 16.1". Color falls back to the database type's brand color when no custom Connection Color is set, matching the existing `displayColor` resolver. Fixes #1044. - All Safe Mode levels are now free. **Safe Mode** (Touch ID), **Safe Mode (Full)**, and **Read Only** no longer require a Pro license. Removed the Pro gate from `SafeModeGuard` (no more silent downgrade to Silent on unlicensed Macs), the `(Pro)` suffix and license prompt from the Customization pane Picker and toolbar `SafeModeBadgeView`, and the `safeMode` case from `ProFeature`. The `requiresPro` computed property on `SafeModeLevel` is gone along with the unused `showSafeModeProAlert` / `showActivationSheet` flags on `CustomizationPaneViewModel`. - Favorites sidebar state is now connection-scoped, not window-scoped. Opening a second native tab for the same connection no longer reloads the favorites tree from SQLite or flashes a spinner. The folders/favorites/linked-files cache (`ConnectionDataCache`) is shared across windows of the same connection and refreshes on `.sqlFavoritesDidUpdate` and `.linkedSQLFoldersDidUpdate`. Favorite selection (`ConnectionSidebarState.selectedFavoriteNodeId`) is also shared, so highlighting a favorite in window A reflects in window B and persists across launches via UserDefaults. Favorites search text remains per-window (`WindowSidebarState.favoritesSearchText`), matching Mail/Notes patterns where each window can search independently. The single sidebar `NSSearchField` routes to the connection-shared text on Tables and to the window-local text on Favorites based on the active tab. - Connection Form rebuilt around macOS HIG sidebar navigation. The old segmented-tab form (~2200 lines across five files) is replaced by a `NavigationSplitView` with five sidebar panes (General, SSH Tunnel, SSL/TLS, Customization, Advanced). State previously held in 30+ flat `@State` vars is now split across six `@Observable` per-pane view models behind a `ConnectionFormCoordinator`. Plugin-driven additional fields auto-route to the right pane by their declared `FieldSection`. The toolbar exposes Cancel, Save, and Save & Connect natively; Test Connection lives inline in the General pane as a Status row. Each sidebar item shows a red warning triangle when its pane has missing required fields. diff --git a/TablePro/Views/Toolbar/ConnectionStatusView.swift b/TablePro/Views/Toolbar/ConnectionStatusView.swift index 88fc775c5..7158f4f38 100644 --- a/TablePro/Views/Toolbar/ConnectionStatusView.swift +++ b/TablePro/Views/Toolbar/ConnectionStatusView.swift @@ -22,15 +22,12 @@ struct ConnectionStatusView: View { var body: some View { HStack(spacing: 10) { - // Database type icon + version - databaseInfoSection + connectionIdentitySection - // Vertical separator - Divider() - .frame(height: 12) - - // Database name (clickable to switch databases) if !databaseName.isEmpty { + Divider() + .frame(height: 12) + databaseNameSection } } @@ -38,18 +35,26 @@ struct ConnectionStatusView: View { // MARK: - Subviews - /// Database type and version info - private var databaseInfoSection: some View { - Text(formattedDatabaseInfo) - .font(.system(.subheadline, design: .monospaced)) - .foregroundStyle(ThemeEngine.shared.colors.toolbar.secondaryTextSwiftUI) - .lineLimit(1) - .truncationMode(.middle) - .frame(maxWidth: 280) - .accessibilityLabel( - String(format: String(localized: "Database type: %@"), formattedDatabaseInfo) - ) - .help("Database: \(formattedDatabaseInfo)") + private var connectionIdentitySection: some View { + HStack(spacing: 6) { + Circle() + .fill(displayColor) + .frame(width: 8, height: 8) + .overlay( + Circle() + .strokeBorder(Color.primary.opacity(0.12), lineWidth: 0.5) + ) + + Text(connectionName) + .font(.callout.weight(.medium)) + .foregroundStyle(.primary) + .lineLimit(1) + .truncationMode(.tail) + .frame(maxWidth: 280, alignment: .leading) + } + .help(connectionTooltip) + .accessibilityElement(children: .combine) + .accessibilityLabel(connectionAccessibilityLabel) } /// Database name (clickable to open database switcher, plain label for SQLite) @@ -91,6 +96,14 @@ struct ConnectionStatusView: View { } return databaseType.rawValue } + + private var connectionTooltip: String { + String(format: String(localized: "%@ • %@"), connectionName, formattedDatabaseInfo) + } + + private var connectionAccessibilityLabel: String { + String(format: String(localized: "Connection: %@, %@"), connectionName, formattedDatabaseInfo) + } } // MARK: - Preview From f8a6f02654aeff349e9442d8dbb4766c638eca64 Mon Sep 17 00:00:00 2001 From: Ngo Quoc Dat Date: Wed, 6 May 2026 20:18:28 +0700 Subject: [PATCH 2/6] fix(toolbar): bump dot to 10pt and stop SwiftUI from compressing identity text --- CHANGELOG.md | 2 +- TablePro/Views/Toolbar/ConnectionStatusView.swift | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 588866b2a..085498f45 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,7 +37,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed -- Toolbar connection identity now leads with the connection name and a small color dot, so multiple windows targeting the same database (e.g. `prod-safe`, `prod-unsafe`, `staging`, `local` against the same Postgres) are distinguishable at a glance instead of all reading "PostgreSQL 16.x". The colored dot follows the macOS HIG pattern used by Reminders / Calendar / Notes (small color shape next to a primary-colored title), so the color stays an augment to the text rather than the only signal. Database vendor and version move to the hover tooltip and the VoiceOver label, which now reads "Connection: prod-safe, PostgreSQL 16.1". Color falls back to the database type's brand color when no custom Connection Color is set, matching the existing `displayColor` resolver. Fixes #1044. +- Toolbar connection identity now leads with the connection name and a 10pt color dot (sized to match Reminders / Calendar / Notes), so multiple windows targeting the same database (e.g. `prod-safe`, `prod-unsafe`, `staging`, `local` against the same Postgres) are distinguishable at a glance instead of all reading "PostgreSQL 16.x". Color stays an augment to the text rather than the only signal. Database vendor and version move to the hover tooltip and the VoiceOver label, which now reads "Connection: prod-safe, PostgreSQL 16.1". Color falls back to the database type's brand color when no custom Connection Color is set, matching the existing `displayColor` resolver. Both the connection-name and database-name `Text` labels now use `.fixedSize(horizontal: true, vertical: false)` so SwiftUI no longer compresses them inside the `NSHostingController` principal item. Previously a long database name would render truncated as `sep...` even in a 1900pt-wide window because SwiftUI compressed flexible text before NSToolbar got a chance to evaluate intrinsic width. Fixes #1044. - All Safe Mode levels are now free. **Safe Mode** (Touch ID), **Safe Mode (Full)**, and **Read Only** no longer require a Pro license. Removed the Pro gate from `SafeModeGuard` (no more silent downgrade to Silent on unlicensed Macs), the `(Pro)` suffix and license prompt from the Customization pane Picker and toolbar `SafeModeBadgeView`, and the `safeMode` case from `ProFeature`. The `requiresPro` computed property on `SafeModeLevel` is gone along with the unused `showSafeModeProAlert` / `showActivationSheet` flags on `CustomizationPaneViewModel`. - Favorites sidebar state is now connection-scoped, not window-scoped. Opening a second native tab for the same connection no longer reloads the favorites tree from SQLite or flashes a spinner. The folders/favorites/linked-files cache (`ConnectionDataCache`) is shared across windows of the same connection and refreshes on `.sqlFavoritesDidUpdate` and `.linkedSQLFoldersDidUpdate`. Favorite selection (`ConnectionSidebarState.selectedFavoriteNodeId`) is also shared, so highlighting a favorite in window A reflects in window B and persists across launches via UserDefaults. Favorites search text remains per-window (`WindowSidebarState.favoritesSearchText`), matching Mail/Notes patterns where each window can search independently. The single sidebar `NSSearchField` routes to the connection-shared text on Tables and to the window-local text on Favorites based on the active tab. - Connection Form rebuilt around macOS HIG sidebar navigation. The old segmented-tab form (~2200 lines across five files) is replaced by a `NavigationSplitView` with five sidebar panes (General, SSH Tunnel, SSL/TLS, Customization, Advanced). State previously held in 30+ flat `@State` vars is now split across six `@Observable` per-pane view models behind a `ConnectionFormCoordinator`. Plugin-driven additional fields auto-route to the right pane by their declared `FieldSection`. The toolbar exposes Cancel, Save, and Save & Connect natively; Test Connection lives inline in the General pane as a Status row. Each sidebar item shows a red warning triangle when its pane has missing required fields. diff --git a/TablePro/Views/Toolbar/ConnectionStatusView.swift b/TablePro/Views/Toolbar/ConnectionStatusView.swift index 7158f4f38..1fd246eda 100644 --- a/TablePro/Views/Toolbar/ConnectionStatusView.swift +++ b/TablePro/Views/Toolbar/ConnectionStatusView.swift @@ -39,7 +39,7 @@ struct ConnectionStatusView: View { HStack(spacing: 6) { Circle() .fill(displayColor) - .frame(width: 8, height: 8) + .frame(width: 10, height: 10) .overlay( Circle() .strokeBorder(Color.primary.opacity(0.12), lineWidth: 0.5) @@ -50,7 +50,7 @@ struct ConnectionStatusView: View { .foregroundStyle(.primary) .lineLimit(1) .truncationMode(.tail) - .frame(maxWidth: 280, alignment: .leading) + .fixedSize(horizontal: true, vertical: false) } .help(connectionTooltip) .accessibilityElement(children: .combine) @@ -85,6 +85,8 @@ struct ConnectionStatusView: View { Text(databaseName) .font(.callout.weight(.medium)) .foregroundStyle(.primary) + .lineLimit(1) + .fixedSize(horizontal: true, vertical: false) } } From eda2056c747e952f0e2ff406be01a9b25caf505a Mon Sep 17 00:00:00 2001 From: Ngo Quoc Dat Date: Wed, 6 May 2026 20:24:09 +0700 Subject: [PATCH 3/6] fix(toolbar): show engine icon tinted with connection color in identity --- CHANGELOG.md | 2 +- TablePro/Views/Toolbar/ConnectionStatusView.swift | 13 ++++++------- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 085498f45..68ed0403a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,7 +37,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed -- Toolbar connection identity now leads with the connection name and a 10pt color dot (sized to match Reminders / Calendar / Notes), so multiple windows targeting the same database (e.g. `prod-safe`, `prod-unsafe`, `staging`, `local` against the same Postgres) are distinguishable at a glance instead of all reading "PostgreSQL 16.x". Color stays an augment to the text rather than the only signal. Database vendor and version move to the hover tooltip and the VoiceOver label, which now reads "Connection: prod-safe, PostgreSQL 16.1". Color falls back to the database type's brand color when no custom Connection Color is set, matching the existing `displayColor` resolver. Both the connection-name and database-name `Text` labels now use `.fixedSize(horizontal: true, vertical: false)` so SwiftUI no longer compresses them inside the `NSHostingController` principal item. Previously a long database name would render truncated as `sep...` even in a 1900pt-wide window because SwiftUI compressed flexible text before NSToolbar got a chance to evaluate intrinsic width. Fixes #1044. +- Toolbar connection identity now leads with the database engine icon (tinted with the connection's color) and the connection name, so multiple windows targeting the same database (e.g. `prod-safe`, `prod-unsafe`, `staging`, `local` against the same Postgres) are distinguishable at a glance instead of all reading "PostgreSQL 16.x". Single visual element does double duty: shape encodes engine type (Postgres elephant, MySQL dolphin, etc.), tint encodes connection identity. Pattern matches the existing `ConnectionSidebarHeader` and `WelcomeConnectionRow` rendering, so the same connection looks consistent across sidebar, welcome screen, and toolbar. Engine icon scales with Dynamic Type via `@ScaledMetric`. The verbose `"PostgreSQL 16.1"` text moves to the hover tooltip and the VoiceOver label, which reads "Connection: prod-safe, PostgreSQL 16.1". Color falls back to the database type's brand color when no custom Connection Color is set, matching the existing `displayColor` resolver. Both the connection-name and database-name `Text` labels now use `.fixedSize(horizontal: true, vertical: false)` so SwiftUI no longer compresses them inside the `NSHostingController` principal item. Previously a long database name would render truncated as `sep...` even in a 1900pt-wide window because SwiftUI compressed flexible text before NSToolbar got a chance to evaluate intrinsic width. Fixes #1044. - All Safe Mode levels are now free. **Safe Mode** (Touch ID), **Safe Mode (Full)**, and **Read Only** no longer require a Pro license. Removed the Pro gate from `SafeModeGuard` (no more silent downgrade to Silent on unlicensed Macs), the `(Pro)` suffix and license prompt from the Customization pane Picker and toolbar `SafeModeBadgeView`, and the `safeMode` case from `ProFeature`. The `requiresPro` computed property on `SafeModeLevel` is gone along with the unused `showSafeModeProAlert` / `showActivationSheet` flags on `CustomizationPaneViewModel`. - Favorites sidebar state is now connection-scoped, not window-scoped. Opening a second native tab for the same connection no longer reloads the favorites tree from SQLite or flashes a spinner. The folders/favorites/linked-files cache (`ConnectionDataCache`) is shared across windows of the same connection and refreshes on `.sqlFavoritesDidUpdate` and `.linkedSQLFoldersDidUpdate`. Favorite selection (`ConnectionSidebarState.selectedFavoriteNodeId`) is also shared, so highlighting a favorite in window A reflects in window B and persists across launches via UserDefaults. Favorites search text remains per-window (`WindowSidebarState.favoritesSearchText`), matching Mail/Notes patterns where each window can search independently. The single sidebar `NSSearchField` routes to the connection-shared text on Tables and to the window-local text on Favorites based on the active tab. - Connection Form rebuilt around macOS HIG sidebar navigation. The old segmented-tab form (~2200 lines across five files) is replaced by a `NavigationSplitView` with five sidebar panes (General, SSH Tunnel, SSL/TLS, Customization, Advanced). State previously held in 30+ flat `@State` vars is now split across six `@Observable` per-pane view models behind a `ConnectionFormCoordinator`. Plugin-driven additional fields auto-route to the right pane by their declared `FieldSection`. The toolbar exposes Cancel, Save, and Save & Connect natively; Test Connection lives inline in the General pane as a Status row. Each sidebar item shows a red warning triangle when its pane has missing required fields. diff --git a/TablePro/Views/Toolbar/ConnectionStatusView.swift b/TablePro/Views/Toolbar/ConnectionStatusView.swift index 1fd246eda..aeb1b780a 100644 --- a/TablePro/Views/Toolbar/ConnectionStatusView.swift +++ b/TablePro/Views/Toolbar/ConnectionStatusView.swift @@ -20,6 +20,8 @@ struct ConnectionStatusView: View { var safeModeLevel: SafeModeLevel = .silent var onSwitchDatabase: (() -> Void)? + @ScaledMetric private var engineIconSize: CGFloat = 14 + var body: some View { HStack(spacing: 10) { connectionIdentitySection @@ -37,13 +39,10 @@ struct ConnectionStatusView: View { private var connectionIdentitySection: some View { HStack(spacing: 6) { - Circle() - .fill(displayColor) - .frame(width: 10, height: 10) - .overlay( - Circle() - .strokeBorder(Color.primary.opacity(0.12), lineWidth: 0.5) - ) + databaseType.iconImage + .renderingMode(.template) + .foregroundStyle(displayColor) + .frame(width: engineIconSize, height: engineIconSize) Text(connectionName) .font(.callout.weight(.medium)) From 1dd0f40113eeb5e4f49816fc17b24bcebad85a43 Mon Sep 17 00:00:00 2001 From: Ngo Quoc Dat Date: Wed, 6 May 2026 20:27:35 +0700 Subject: [PATCH 4/6] fix(toolbar): keep engine icon in brand color, dot for connection identity --- CHANGELOG.md | 2 +- TablePro/Views/Toolbar/ConnectionStatusView.swift | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 68ed0403a..acecb7576 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,7 +37,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed -- Toolbar connection identity now leads with the database engine icon (tinted with the connection's color) and the connection name, so multiple windows targeting the same database (e.g. `prod-safe`, `prod-unsafe`, `staging`, `local` against the same Postgres) are distinguishable at a glance instead of all reading "PostgreSQL 16.x". Single visual element does double duty: shape encodes engine type (Postgres elephant, MySQL dolphin, etc.), tint encodes connection identity. Pattern matches the existing `ConnectionSidebarHeader` and `WelcomeConnectionRow` rendering, so the same connection looks consistent across sidebar, welcome screen, and toolbar. Engine icon scales with Dynamic Type via `@ScaledMetric`. The verbose `"PostgreSQL 16.1"` text moves to the hover tooltip and the VoiceOver label, which reads "Connection: prod-safe, PostgreSQL 16.1". Color falls back to the database type's brand color when no custom Connection Color is set, matching the existing `displayColor` resolver. Both the connection-name and database-name `Text` labels now use `.fixedSize(horizontal: true, vertical: false)` so SwiftUI no longer compresses them inside the `NSHostingController` principal item. Previously a long database name would render truncated as `sep...` even in a 1900pt-wide window because SwiftUI compressed flexible text before NSToolbar got a chance to evaluate intrinsic width. Fixes #1044. +- Toolbar connection identity now leads with the database engine icon, a connection-color dot, and the connection name, so multiple windows targeting the same database (e.g. `prod-safe`, `prod-unsafe`, `staging`, `local` against the same Postgres) are distinguishable at a glance instead of all reading "PostgreSQL 16.x". Engine icon stays in its brand color (`databaseType.themeColor`, e.g. Postgres blue, MySQL teal) since the engine's identity does not change with the connection. The 10pt color dot beside it carries the connection-level color (`displayColor`), matching the Finder pattern where the file icon stays system blue and tag dots carry the user-chosen color. Both icon and dot scale with Dynamic Type via `@ScaledMetric`. The verbose "PostgreSQL 16.1" text moves to the hover tooltip and the VoiceOver label, which reads "Connection: prod-safe, PostgreSQL 16.1". Both the connection-name and database-name `Text` labels now use `.fixedSize(horizontal: true, vertical: false)` so SwiftUI no longer compresses them inside the `NSHostingController` principal item. Previously a long database name would render truncated as `sep...` even in a 1900pt-wide window because SwiftUI compressed flexible text before NSToolbar got a chance to evaluate intrinsic width. Fixes #1044. - All Safe Mode levels are now free. **Safe Mode** (Touch ID), **Safe Mode (Full)**, and **Read Only** no longer require a Pro license. Removed the Pro gate from `SafeModeGuard` (no more silent downgrade to Silent on unlicensed Macs), the `(Pro)` suffix and license prompt from the Customization pane Picker and toolbar `SafeModeBadgeView`, and the `safeMode` case from `ProFeature`. The `requiresPro` computed property on `SafeModeLevel` is gone along with the unused `showSafeModeProAlert` / `showActivationSheet` flags on `CustomizationPaneViewModel`. - Favorites sidebar state is now connection-scoped, not window-scoped. Opening a second native tab for the same connection no longer reloads the favorites tree from SQLite or flashes a spinner. The folders/favorites/linked-files cache (`ConnectionDataCache`) is shared across windows of the same connection and refreshes on `.sqlFavoritesDidUpdate` and `.linkedSQLFoldersDidUpdate`. Favorite selection (`ConnectionSidebarState.selectedFavoriteNodeId`) is also shared, so highlighting a favorite in window A reflects in window B and persists across launches via UserDefaults. Favorites search text remains per-window (`WindowSidebarState.favoritesSearchText`), matching Mail/Notes patterns where each window can search independently. The single sidebar `NSSearchField` routes to the connection-shared text on Tables and to the window-local text on Favorites based on the active tab. - Connection Form rebuilt around macOS HIG sidebar navigation. The old segmented-tab form (~2200 lines across five files) is replaced by a `NavigationSplitView` with five sidebar panes (General, SSH Tunnel, SSL/TLS, Customization, Advanced). State previously held in 30+ flat `@State` vars is now split across six `@Observable` per-pane view models behind a `ConnectionFormCoordinator`. Plugin-driven additional fields auto-route to the right pane by their declared `FieldSection`. The toolbar exposes Cancel, Save, and Save & Connect natively; Test Connection lives inline in the General pane as a Status row. Each sidebar item shows a red warning triangle when its pane has missing required fields. diff --git a/TablePro/Views/Toolbar/ConnectionStatusView.swift b/TablePro/Views/Toolbar/ConnectionStatusView.swift index aeb1b780a..9d3b80a76 100644 --- a/TablePro/Views/Toolbar/ConnectionStatusView.swift +++ b/TablePro/Views/Toolbar/ConnectionStatusView.swift @@ -21,6 +21,7 @@ struct ConnectionStatusView: View { var onSwitchDatabase: (() -> Void)? @ScaledMetric private var engineIconSize: CGFloat = 14 + @ScaledMetric private var connectionDotSize: CGFloat = 10 var body: some View { HStack(spacing: 10) { @@ -41,9 +42,13 @@ struct ConnectionStatusView: View { HStack(spacing: 6) { databaseType.iconImage .renderingMode(.template) - .foregroundStyle(displayColor) + .foregroundStyle(databaseType.themeColor) .frame(width: engineIconSize, height: engineIconSize) + Circle() + .fill(displayColor) + .frame(width: connectionDotSize, height: connectionDotSize) + Text(connectionName) .font(.callout.weight(.medium)) .foregroundStyle(.primary) From 43929644eda18febee809cfe313823e29db6f555 Mon Sep 17 00:00:00 2001 From: Ngo Quoc Dat Date: Wed, 6 May 2026 20:32:38 +0700 Subject: [PATCH 5/6] fix(toolbar): drop dot, tint engine icon with connection color (Calendar pattern) --- CHANGELOG.md | 2 +- TablePro/Views/Toolbar/ConnectionStatusView.swift | 7 +------ 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index acecb7576..76d153730 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,7 +37,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed -- Toolbar connection identity now leads with the database engine icon, a connection-color dot, and the connection name, so multiple windows targeting the same database (e.g. `prod-safe`, `prod-unsafe`, `staging`, `local` against the same Postgres) are distinguishable at a glance instead of all reading "PostgreSQL 16.x". Engine icon stays in its brand color (`databaseType.themeColor`, e.g. Postgres blue, MySQL teal) since the engine's identity does not change with the connection. The 10pt color dot beside it carries the connection-level color (`displayColor`), matching the Finder pattern where the file icon stays system blue and tag dots carry the user-chosen color. Both icon and dot scale with Dynamic Type via `@ScaledMetric`. The verbose "PostgreSQL 16.1" text moves to the hover tooltip and the VoiceOver label, which reads "Connection: prod-safe, PostgreSQL 16.1". Both the connection-name and database-name `Text` labels now use `.fixedSize(horizontal: true, vertical: false)` so SwiftUI no longer compresses them inside the `NSHostingController` principal item. Previously a long database name would render truncated as `sep...` even in a 1900pt-wide window because SwiftUI compressed flexible text before NSToolbar got a chance to evaluate intrinsic width. Fixes #1044. +- Toolbar connection identity now leads with the database engine icon tinted with the connection's color, followed by the connection name, so multiple windows targeting the same database (e.g. `prod-safe`, `prod-unsafe`, `staging`, `local` against the same Postgres) are distinguishable at a glance instead of all reading "PostgreSQL 16.x". The pattern matches Calendar.app and Reminders.app, where a single icon carries both the type (shape) and the user-chosen identity (color). When no custom Connection Color is set, the icon falls back to the database type's brand color via the existing `displayColor` resolver, so the brand identity is preserved by default. The icon scales with Dynamic Type via `@ScaledMetric`. The verbose "PostgreSQL 16.1" text moves to the hover tooltip and the VoiceOver label, which reads "Connection: prod-safe, PostgreSQL 16.1". Both the connection-name and database-name `Text` labels now use `.fixedSize(horizontal: true, vertical: false)` so SwiftUI no longer compresses them inside the `NSHostingController` principal item. Previously a long database name would render truncated as `sep...` even in a 1900pt-wide window because SwiftUI compressed flexible text before NSToolbar got a chance to evaluate intrinsic width. Fixes #1044. - All Safe Mode levels are now free. **Safe Mode** (Touch ID), **Safe Mode (Full)**, and **Read Only** no longer require a Pro license. Removed the Pro gate from `SafeModeGuard` (no more silent downgrade to Silent on unlicensed Macs), the `(Pro)` suffix and license prompt from the Customization pane Picker and toolbar `SafeModeBadgeView`, and the `safeMode` case from `ProFeature`. The `requiresPro` computed property on `SafeModeLevel` is gone along with the unused `showSafeModeProAlert` / `showActivationSheet` flags on `CustomizationPaneViewModel`. - Favorites sidebar state is now connection-scoped, not window-scoped. Opening a second native tab for the same connection no longer reloads the favorites tree from SQLite or flashes a spinner. The folders/favorites/linked-files cache (`ConnectionDataCache`) is shared across windows of the same connection and refreshes on `.sqlFavoritesDidUpdate` and `.linkedSQLFoldersDidUpdate`. Favorite selection (`ConnectionSidebarState.selectedFavoriteNodeId`) is also shared, so highlighting a favorite in window A reflects in window B and persists across launches via UserDefaults. Favorites search text remains per-window (`WindowSidebarState.favoritesSearchText`), matching Mail/Notes patterns where each window can search independently. The single sidebar `NSSearchField` routes to the connection-shared text on Tables and to the window-local text on Favorites based on the active tab. - Connection Form rebuilt around macOS HIG sidebar navigation. The old segmented-tab form (~2200 lines across five files) is replaced by a `NavigationSplitView` with five sidebar panes (General, SSH Tunnel, SSL/TLS, Customization, Advanced). State previously held in 30+ flat `@State` vars is now split across six `@Observable` per-pane view models behind a `ConnectionFormCoordinator`. Plugin-driven additional fields auto-route to the right pane by their declared `FieldSection`. The toolbar exposes Cancel, Save, and Save & Connect natively; Test Connection lives inline in the General pane as a Status row. Each sidebar item shows a red warning triangle when its pane has missing required fields. diff --git a/TablePro/Views/Toolbar/ConnectionStatusView.swift b/TablePro/Views/Toolbar/ConnectionStatusView.swift index 9d3b80a76..aeb1b780a 100644 --- a/TablePro/Views/Toolbar/ConnectionStatusView.swift +++ b/TablePro/Views/Toolbar/ConnectionStatusView.swift @@ -21,7 +21,6 @@ struct ConnectionStatusView: View { var onSwitchDatabase: (() -> Void)? @ScaledMetric private var engineIconSize: CGFloat = 14 - @ScaledMetric private var connectionDotSize: CGFloat = 10 var body: some View { HStack(spacing: 10) { @@ -42,13 +41,9 @@ struct ConnectionStatusView: View { HStack(spacing: 6) { databaseType.iconImage .renderingMode(.template) - .foregroundStyle(databaseType.themeColor) + .foregroundStyle(displayColor) .frame(width: engineIconSize, height: engineIconSize) - Circle() - .fill(displayColor) - .frame(width: connectionDotSize, height: connectionDotSize) - Text(connectionName) .font(.callout.weight(.medium)) .foregroundStyle(.primary) From c14fa0e006796f6bde09c30aed75f8ee4a326094 Mon Sep 17 00:00:00 2001 From: Ngo Quoc Dat Date: Wed, 6 May 2026 20:37:37 +0700 Subject: [PATCH 6/6] refactor(toolbar): drop dead tagName and connectionState props --- .../Views/Toolbar/ConnectionStatusView.swift | 29 ++++++------------- .../Views/Toolbar/TableProToolbarView.swift | 2 -- 2 files changed, 9 insertions(+), 22 deletions(-) diff --git a/TablePro/Views/Toolbar/ConnectionStatusView.swift b/TablePro/Views/Toolbar/ConnectionStatusView.swift index aeb1b780a..fa6d9f27a 100644 --- a/TablePro/Views/Toolbar/ConnectionStatusView.swift +++ b/TablePro/Views/Toolbar/ConnectionStatusView.swift @@ -14,9 +14,7 @@ struct ConnectionStatusView: View { let databaseVersion: String? let databaseName: String let connectionName: String - let connectionState: ToolbarConnectionState let displayColor: Color - let tagName: String? var safeModeLevel: SafeModeLevel = .silent var onSwitchDatabase: (() -> Void)? @@ -56,7 +54,6 @@ struct ConnectionStatusView: View { .accessibilityLabel(connectionAccessibilityLabel) } - /// Database name (clickable to open database switcher, plain label for SQLite) @ViewBuilder private var databaseNameSection: some View { if !PluginManager.shared.supportsDatabaseSwitching(for: databaseType) { @@ -109,58 +106,50 @@ struct ConnectionStatusView: View { // MARK: - Preview -#Preview("Connected") { +#Preview("MariaDB") { ConnectionStatusView( databaseType: .mariadb, databaseVersion: "11.1.2", databaseName: "production_db", connectionName: "Production Database", - connectionState: .connected, - displayColor: .cyan, - tagName: "production" + displayColor: .cyan ) .padding() .background(Color(nsColor: .windowBackgroundColor)) } -#Preview("Executing - No Duplicate") { +#Preview("MySQL") { ConnectionStatusView( databaseType: .mysql, databaseVersion: "8.0.35", databaseName: "dev_db", connectionName: "Development", - connectionState: .executing, - displayColor: .orange, - tagName: "local" + displayColor: .orange ) .padding() .background(Color(nsColor: .windowBackgroundColor)) } -#Preview("No Tag") { +#Preview("PostgreSQL Dark") { ConnectionStatusView( databaseType: .postgresql, databaseVersion: "16.1", databaseName: "analytics", connectionName: "Analytics DB", - connectionState: .connected, - displayColor: .blue, - tagName: nil + displayColor: .blue ) .padding() .background(Color(nsColor: .windowBackgroundColor)) .preferredColorScheme(.dark) } -#Preview("Duplicate Name") { +#Preview("Empty Database") { ConnectionStatusView( databaseType: .mysql, databaseVersion: "9.5.0", - databaseName: "laravel", + databaseName: "", connectionName: "Local", - connectionState: .connected, - displayColor: .green, - tagName: "local" + displayColor: .green ) .padding() .background(Color(nsColor: .windowBackgroundColor)) diff --git a/TablePro/Views/Toolbar/TableProToolbarView.swift b/TablePro/Views/Toolbar/TableProToolbarView.swift index 7972bc777..4e045781b 100644 --- a/TablePro/Views/Toolbar/TableProToolbarView.swift +++ b/TablePro/Views/Toolbar/TableProToolbarView.swift @@ -34,9 +34,7 @@ struct ToolbarPrincipalContent: View { databaseVersion: state.databaseVersion, databaseName: state.databaseName, connectionName: state.connectionName, - connectionState: state.connectionState, displayColor: state.displayColor, - tagName: tag?.name, safeModeLevel: state.safeModeLevel, onSwitchDatabase: onSwitchDatabase )