Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,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, 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.
- XLSX export is now free for everyone. Removed the Pro gate from the Export dialog: the `(Pro)` suffix in the format Picker, the orange "XLSX export requires a Pro license." caption, the `Activate License...` link, and the `isExportDisabled` short-circuit on unlicensed Macs are gone. The supporting machinery (`proFormatIds` set, `isProGatedFormat(_:)` predicate, `showActivationSheet` `@State` and its `.sheet` modifier in `ExportDialog`, the `xlsxExport` case in `ProFeature` plus its three switch arms, and the docs Note in `import-export.mdx`) is removed too. License-state changes no longer affect Excel export at all.
- 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.
Expand Down
7 changes: 0 additions & 7 deletions TablePro/Models/Settings/ProFeature.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import Foundation
/// Features that require a Pro (active) license
internal enum ProFeature: String, CaseIterable {
case iCloudSync
case xlsxExport
case encryptedExport
case envVarReferences
case linkedFolders
Expand All @@ -19,8 +18,6 @@ internal enum ProFeature: String, CaseIterable {
switch self {
case .iCloudSync:
return String(localized: "iCloud Sync")
case .xlsxExport:
return String(localized: "XLSX Export")
case .encryptedExport:
return String(localized: "Encrypted Export")
case .envVarReferences:
Expand All @@ -34,8 +31,6 @@ internal enum ProFeature: String, CaseIterable {
switch self {
case .iCloudSync:
return "icloud"
case .xlsxExport:
return "tablecells"
case .encryptedExport:
return "lock.doc"
case .envVarReferences:
Expand All @@ -49,8 +44,6 @@ internal enum ProFeature: String, CaseIterable {
switch self {
case .iCloudSync:
return String(localized: "Sync connections, settings, and history across your Macs.")
case .xlsxExport:
return String(localized: "Export query results and tables to Excel format.")
case .encryptedExport:
return String(localized: "Export connections with encrypted credentials.")
case .envVarReferences:
Expand Down
44 changes: 0 additions & 44 deletions TablePro/Resources/Localizable.xcstrings
Original file line number Diff line number Diff line change
Expand Up @@ -51363,50 +51363,6 @@
}
}
},
"XLSX Export" : {
"localizations" : {
"tr" : {
"stringUnit" : {
"state" : "translated",
"value" : "XLSX Dışa Aktarma"
}
},
"vi" : {
"stringUnit" : {
"state" : "translated",
"value" : "Xuất XLSX"
}
},
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "XLSX 导出"
}
}
}
},
"XLSX export requires a Pro license." : {
"localizations" : {
"tr" : {
"stringUnit" : {
"state" : "translated",
"value" : "XLSX dışa aktarma için Pro lisans gereklidir."
}
},
"vi" : {
"stringUnit" : {
"state" : "translated",
"value" : "Xuất XLSX yêu cầu giấy phép Pro."
}
},
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "XLSX 导出需要 Pro 许可证。"
}
}
}
},
"XXXXX-XXXXX-XXXXX-XXXXX-XXXXX" : {
"localizations" : {
"tr" : {
Expand Down
29 changes: 3 additions & 26 deletions TablePro/Views/Export/ExportDialog.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ struct ExportDialog: View {
@State private var showProgressDialog = false
@State private var showSuccessDialog = false
@State private var exportedFileURL: URL?
@State private var showActivationSheet = false

// MARK: - User Preferences

Expand Down Expand Up @@ -94,9 +93,6 @@ struct ExportDialog: View {
}
.frame(width: dialogWidth)
.background(Color(nsColor: .windowBackgroundColor))
.sheet(isPresented: $showActivationSheet) {
LicenseActivationSheet()
}
.onAppear {
let available = availableFormats
if !available.contains(where: { type(of: $0).formatId == config.formatId }) {
Expand Down Expand Up @@ -281,11 +277,7 @@ struct ExportDialog: View {
Picker("", selection: $config.formatId) {
ForEach(availableFormatIds, id: \.self) { formatId in
if let plugin = PluginManager.shared.exportPlugin(forFormat: formatId) {
if isProGatedFormat(formatId) {
Text("\(type(of: plugin).formatDisplayName) (Pro)").tag(formatId)
} else {
Text(type(of: plugin).formatDisplayName).tag(formatId)
}
Text(type(of: plugin).formatDisplayName).tag(formatId)
}
}
}
Expand All @@ -302,18 +294,8 @@ struct ExportDialog: View {
}
}

// Selection count or Pro gate message
VStack(spacing: 2) {
if isProGatedFormat(config.formatId) {
Text(String(localized: "XLSX export requires a Pro license."))
.font(.subheadline)
.foregroundStyle(Color(nsColor: .systemOrange))
Button(String(localized: "Activate License...")) {
showActivationSheet = true
}
.font(.subheadline)
.buttonStyle(.link)
} else if case .streamingQuery = mode {
if case .streamingQuery = mode {
Text("All rows")
.font(.subheadline)
.foregroundStyle(.secondary)
Expand Down Expand Up @@ -450,7 +432,7 @@ struct ExportDialog: View {
}

private var isExportDisabled: Bool {
if isExporting || !isFileNameValid || availableFormats.isEmpty || isProGatedFormat(config.formatId) {
if isExporting || !isFileNameValid || availableFormats.isEmpty {
return true
}
if case .streamingQuery = mode {
Expand All @@ -463,7 +445,6 @@ struct ExportDialog: View {
}

private static let formatDisplayOrder = ["csv", "json", "sql", "xlsx", "mql"]
private static let proFormatIds: Set<String> = ["xlsx"]

private func formatDescription(for formatId: String) -> String {
switch formatId {
Expand All @@ -476,10 +457,6 @@ struct ExportDialog: View {
}
}

private func isProGatedFormat(_ formatId: String) -> Bool {
Self.proFormatIds.contains(formatId) && !LicenseManager.shared.isFeatureAvailable(.xlsxExport)
}

/// Windows reserved device names (case-insensitive)
private static let windowsReservedNames: Set<String> = [
"CON", "PRN", "AUX", "NUL",
Expand Down
4 changes: 0 additions & 4 deletions docs/features/import-export.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -134,10 +134,6 @@ Export data in five formats (CSV, JSON, SQL, MQL, XLSX), import SQL files with g
```
</Tab>
<Tab title="XLSX">
<Note>
XLSX export requires a **Pro license**.
</Note>

| Option | Default |
|--------|---------|
| Include headers (bold first row) | Yes |
Expand Down
Loading