diff --git a/CHANGELOG.md b/CHANGELOG.md index fe7b2a350..9ba82d97e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Fixed + +- Fix macOS HIG compliance: system colors, accessibility labels, theme tokens, localization + ## [0.28.0] - 2026-04-07 ### Added diff --git a/TablePro/Models/Connection/SafeModeLevel.swift b/TablePro/Models/Connection/SafeModeLevel.swift index 91d3c2ffa..72e92bb5f 100644 --- a/TablePro/Models/Connection/SafeModeLevel.swift +++ b/TablePro/Models/Connection/SafeModeLevel.swift @@ -74,8 +74,8 @@ internal extension SafeModeLevel { var badgeColor: Color { switch self { case .silent: return .secondary - case .alert, .alertFull: return .orange - case .safeMode, .safeModeFull, .readOnly: return .red + case .alert, .alertFull: return Color(nsColor: .systemOrange) + case .safeMode, .safeModeFull, .readOnly: return Color(nsColor: .systemRed) } } } diff --git a/TablePro/Views/AIChat/AIChatCodeBlockView.swift b/TablePro/Views/AIChat/AIChatCodeBlockView.swift index 1de908614..134ade103 100644 --- a/TablePro/Views/AIChat/AIChatCodeBlockView.swift +++ b/TablePro/Views/AIChat/AIChatCodeBlockView.swift @@ -14,8 +14,6 @@ struct AIChatCodeBlockView: View { let language: String? @State private var isCopied: Bool = false - @State private var cachedHighlight: AttributedString? - @State private var cachedCodeLength: Int = 0 @FocusedValue(\.commandActions) private var actions var body: some View { @@ -37,7 +35,7 @@ struct AIChatCodeBlockView: View { .padding(.horizontal, 6) .padding(.vertical, 2) .background(Color(nsColor: .separatorColor)) - .clipShape(RoundedRectangle(cornerRadius: 4)) + .clipShape(RoundedRectangle(cornerRadius: ThemeEngine.shared.activeTheme.cornerRadius.small)) } Spacer() @@ -78,8 +76,12 @@ struct AIChatCodeBlockView: View { private var codeContent: some View { ScrollView(.horizontal, showsIndicators: false) { - if isSQL || isMongoDB { - Text(cachedHighlight ?? AttributedString(code)) + if isSQL { + Text(highlightedSQL(code)) + .textSelection(.enabled) + .padding(10) + } else if isMongoDB { + Text(highlightedJavaScript(code)) .textSelection(.enabled) .padding(10) } else if isRedis { @@ -94,20 +96,6 @@ struct AIChatCodeBlockView: View { .padding(10) } } - .onAppear { - recomputeHighlight() - } - .onChange(of: code) { - let newLen = (code as NSString).length - if newLen - cachedCodeLength >= 50 { - recomputeHighlight() - } - } - } - - private func recomputeHighlight() { - cachedHighlight = isSQL ? highlightedSQL(code) : highlightedJavaScript(code) - cachedCodeLength = (code as NSString).length } private var isSQL: Bool { @@ -369,9 +357,9 @@ private struct CodeBlockGroupBoxStyle: GroupBoxStyle { configuration.content } .background(Color(nsColor: .controlBackgroundColor)) - .clipShape(RoundedRectangle(cornerRadius: 8)) + .clipShape(RoundedRectangle(cornerRadius: ThemeEngine.shared.activeTheme.cornerRadius.large)) .overlay( - RoundedRectangle(cornerRadius: 8) + RoundedRectangle(cornerRadius: ThemeEngine.shared.activeTheme.cornerRadius.large) .stroke(Color(nsColor: .separatorColor), lineWidth: 1) ) } diff --git a/TablePro/Views/AIChat/AIChatMessageView.swift b/TablePro/Views/AIChat/AIChatMessageView.swift index 7adcdfeb2..ec19b44b8 100644 --- a/TablePro/Views/AIChat/AIChatMessageView.swift +++ b/TablePro/Views/AIChat/AIChatMessageView.swift @@ -51,7 +51,7 @@ struct AIChatMessageView: View { } .padding(8) .background(Color.accentColor.opacity(0.06)) - .clipShape(RoundedRectangle(cornerRadius: 8)) + .clipShape(RoundedRectangle(cornerRadius: ThemeEngine.shared.activeTheme.cornerRadius.large)) } else { // Assistant: role header above content roleHeader diff --git a/TablePro/Views/AIChat/AIChatPanelView.swift b/TablePro/Views/AIChat/AIChatPanelView.swift index b588c2303..e45c1ee6c 100644 --- a/TablePro/Views/AIChat/AIChatPanelView.swift +++ b/TablePro/Views/AIChat/AIChatPanelView.swift @@ -244,7 +244,7 @@ struct AIChatPanelView: View { private func errorBanner(_ message: String) -> some View { HStack(spacing: 6) { Image(systemName: "exclamationmark.triangle.fill") - .foregroundStyle(.yellow) + .foregroundStyle(Color(nsColor: .systemYellow)) Text(message) .font(.caption) .foregroundStyle(.secondary) @@ -254,10 +254,11 @@ struct AIChatPanelView: View { viewModel.errorMessage = nil } label: { Image(systemName: "xmark") - .font(.caption2) + .frame(width: 24, height: 24) .foregroundStyle(.secondary) } .buttonStyle(.plain) + .accessibilityLabel(String(localized: "Dismiss error")) } .padding(.horizontal, 12) .padding(.vertical, 6) @@ -289,7 +290,7 @@ struct AIChatPanelView: View { viewModel.cancelStream() } label: { Image(systemName: "stop.circle.fill") - .foregroundStyle(.red) + .foregroundStyle(Color(nsColor: .systemRed)) } .buttonStyle(.plain) .help(String(localized: "Stop Generating")) diff --git a/TablePro/Views/Components/PaginationControlsView.swift b/TablePro/Views/Components/PaginationControlsView.swift index 28269f329..3db273cf4 100644 --- a/TablePro/Views/Components/PaginationControlsView.swift +++ b/TablePro/Views/Components/PaginationControlsView.swift @@ -35,7 +35,7 @@ struct PaginationControlsView: View { .frame(width: 24, height: 24) } .buttonStyle(.borderless) - .help("Pagination Settings") + .help(String(localized: "Pagination Settings")) .popover(isPresented: $showSettings, arrowEdge: .top) { settingsPopover } @@ -64,7 +64,7 @@ struct PaginationControlsView: View { } .buttonStyle(.borderless) .disabled(!pagination.hasPreviousPage || pagination.isLoading) - .help("Previous Page (⌘[)") + .help(String(localized: "Previous Page (⌘[)")) .keyboardShortcut("[", modifiers: .command) // Page indicator: "1 of 25" @@ -86,7 +86,7 @@ struct PaginationControlsView: View { } .buttonStyle(.borderless) .disabled(!pagination.hasNextPage || pagination.isLoading) - .help("Next Page (⌘])") + .help(String(localized: "Next Page (⌘])")) .keyboardShortcut("]", modifiers: .command) } } diff --git a/TablePro/Views/Components/SearchFieldView.swift b/TablePro/Views/Components/SearchFieldView.swift index 5b9770e4c..8384fb90e 100644 --- a/TablePro/Views/Components/SearchFieldView.swift +++ b/TablePro/Views/Components/SearchFieldView.swift @@ -32,6 +32,6 @@ struct SearchFieldView: View { .padding(.horizontal, 8) .padding(.vertical, 6) .background(Color(nsColor: .controlBackgroundColor)) - .clipShape(RoundedRectangle(cornerRadius: 6)) + .clipShape(RoundedRectangle(cornerRadius: ThemeEngine.shared.activeTheme.cornerRadius.medium)) } } diff --git a/TablePro/Views/Connection/ConnectionExportOptionsSheet.swift b/TablePro/Views/Connection/ConnectionExportOptionsSheet.swift index 8e8f540b0..43d867298 100644 --- a/TablePro/Views/Connection/ConnectionExportOptionsSheet.swift +++ b/TablePro/Views/Connection/ConnectionExportOptionsSheet.swift @@ -30,7 +30,7 @@ struct ConnectionExportOptionsSheet: View { var body: some View { VStack(alignment: .leading, spacing: 16) { Text(String(localized: "Export Options")) - .font(.system(size: 13, weight: .semibold)) + .font(.system(size: ThemeEngine.shared.activeTheme.typography.body, weight: .semibold)) .frame(maxWidth: .infinity, alignment: .leading) VStack(alignment: .leading, spacing: 8) { @@ -41,7 +41,7 @@ struct ConnectionExportOptionsSheet: View { if !isProAvailable { Text("Pro") - .font(.system(size: 10, weight: .medium)) + .font(.system(size: ThemeEngine.shared.activeTheme.typography.caption, weight: .medium)) .foregroundStyle(.white) .padding(.horizontal, 5) .padding(.vertical, 1) @@ -54,7 +54,7 @@ struct ConnectionExportOptionsSheet: View { if includeCredentials { Text("Passwords will be encrypted with the passphrase you provide.") - .font(.system(size: 11)) + .font(.system(size: ThemeEngine.shared.activeTheme.typography.small)) .foregroundStyle(.secondary) SecureField("Passphrase (8+ characters)", text: $passphrase) @@ -65,7 +65,7 @@ struct ConnectionExportOptionsSheet: View { if !passphrase.isEmpty && !confirmPassphrase.isEmpty && passphrase != confirmPassphrase { Text("Passphrases do not match") - .font(.system(size: 11)) + .font(.system(size: ThemeEngine.shared.activeTheme.typography.small)) .foregroundStyle(.red) } } diff --git a/TablePro/Views/Connection/ConnectionFormView+Footer.swift b/TablePro/Views/Connection/ConnectionFormView+Footer.swift index f61267d66..b4b4a64dc 100644 --- a/TablePro/Views/Connection/ConnectionFormView+Footer.swift +++ b/TablePro/Views/Connection/ConnectionFormView+Footer.swift @@ -22,7 +22,7 @@ extension ConnectionFormView { .controlSize(.small) } else if testSucceeded { Image(systemName: "checkmark.circle.fill") - .foregroundStyle(.green) + .foregroundStyle(Color(nsColor: .systemGreen)) } else { Image(systemName: "bolt.horizontal") .foregroundStyle(.secondary) @@ -106,7 +106,7 @@ extension ConnectionFormView { if let urlParseError { Text(urlParseError) .font(.caption) - .foregroundStyle(.red) + .foregroundStyle(Color(nsColor: .systemRed)) } HStack { diff --git a/TablePro/Views/Connection/ConnectionFormView+Helpers.swift b/TablePro/Views/Connection/ConnectionFormView+Helpers.swift index d4d41464a..789526558 100644 --- a/TablePro/Views/Connection/ConnectionFormView+Helpers.swift +++ b/TablePro/Views/Connection/ConnectionFormView+Helpers.swift @@ -698,28 +698,28 @@ extension ConnectionFormView { String(localized: "~/.pgpass not found"), systemImage: "exclamationmark.triangle.fill" ) - .foregroundStyle(.yellow) + .foregroundStyle(Color(nsColor: .systemYellow)) .font(.caption) case .badPermissions: Label( String(localized: "~/.pgpass has incorrect permissions (needs chmod 0600)"), systemImage: "xmark.circle.fill" ) - .foregroundStyle(.red) + .foregroundStyle(Color(nsColor: .systemRed)) .font(.caption) case .matchFound: Label( String(localized: "~/.pgpass found — matching entry exists"), systemImage: "checkmark.circle.fill" ) - .foregroundStyle(.green) + .foregroundStyle(Color(nsColor: .systemGreen)) .font(.caption) case .noMatch: Label( String(localized: "~/.pgpass found — no matching entry"), systemImage: "exclamationmark.triangle.fill" ) - .foregroundStyle(.yellow) + .foregroundStyle(Color(nsColor: .systemYellow)) .font(.caption) } } diff --git a/TablePro/Views/Connection/ConnectionImportSheet.swift b/TablePro/Views/Connection/ConnectionImportSheet.swift index 67b744b11..c2ff45a43 100644 --- a/TablePro/Views/Connection/ConnectionImportSheet.swift +++ b/TablePro/Views/Connection/ConnectionImportSheet.swift @@ -83,9 +83,9 @@ struct ConnectionImportSheet: View { private func header(_ preview: ConnectionImportPreview) -> some View { HStack { Text(String(localized: "Import Connections")) - .font(.system(size: 13, weight: .semibold)) + .font(.system(size: ThemeEngine.shared.activeTheme.typography.body, weight: .semibold)) Text("(\(fileURL.lastPathComponent))") - .font(.system(size: 13)) + .font(.system(size: ThemeEngine.shared.activeTheme.typography.body)) .foregroundStyle(.secondary) Spacer() Toggle(String(localized: "Select All"), isOn: Binding( @@ -139,11 +139,11 @@ struct ConnectionImportSheet: View { VStack(alignment: .leading, spacing: 1) { HStack(spacing: 4) { Text(item.connection.name) - .font(.system(size: 13)) + .font(.system(size: ThemeEngine.shared.activeTheme.typography.body)) .lineLimit(1) if case .duplicate = item.status { Text(String(localized: "duplicate")) - .font(.system(size: 10)) + .font(.system(size: ThemeEngine.shared.activeTheme.typography.caption)) .foregroundStyle(.secondary) .padding(.horizontal, 4) .padding(.vertical, 1) @@ -157,7 +157,7 @@ struct ConnectionImportSheet: View { Text("\(item.connection.host):\(String(item.connection.port))") warningText(for: item.status) } - .font(.system(size: 11)) + .font(.system(size: ThemeEngine.shared.activeTheme.typography.small)) .foregroundStyle(.secondary) .lineLimit(1) } @@ -191,11 +191,11 @@ struct ConnectionImportSheet: View { switch status { case .ready: Image(systemName: "checkmark.circle.fill") - .font(.system(size: 12)) + .font(.system(size: ThemeEngine.shared.activeTheme.typography.medium)) .foregroundStyle(.green) case .warnings: Image(systemName: "exclamationmark.triangle.fill") - .font(.system(size: 12)) + .font(.system(size: ThemeEngine.shared.activeTheme.typography.medium)) .foregroundStyle(.yellow) case .duplicate: EmptyView() @@ -221,10 +221,10 @@ struct ConnectionImportSheet: View { .foregroundStyle(.secondary) Text("This file is encrypted") - .font(.system(size: 13, weight: .semibold)) + .font(.system(size: ThemeEngine.shared.activeTheme.typography.body, weight: .semibold)) Text("Enter the passphrase to decrypt and import connections.") - .font(.system(size: 12)) + .font(.system(size: ThemeEngine.shared.activeTheme.typography.medium)) .foregroundStyle(.secondary) .multilineTextAlignment(.center) @@ -235,7 +235,7 @@ struct ConnectionImportSheet: View { if let passphraseError { Text(passphraseError) - .font(.system(size: 11)) + .font(.system(size: ThemeEngine.shared.activeTheme.typography.small)) .foregroundStyle(.red) } @@ -260,7 +260,7 @@ struct ConnectionImportSheet: View { private func footer(_ preview: ConnectionImportPreview) -> some View { HStack { Text("\(selectedIds.count) of \(preview.items.count) selected") - .font(.system(size: 11)) + .font(.system(size: ThemeEngine.shared.activeTheme.typography.small)) .foregroundStyle(.secondary) Spacer() diff --git a/TablePro/Views/Connection/ConnectionSSHTunnelView.swift b/TablePro/Views/Connection/ConnectionSSHTunnelView.swift index 8e033f4b8..c9f104212 100644 --- a/TablePro/Views/Connection/ConnectionSSHTunnelView.swift +++ b/TablePro/Views/Connection/ConnectionSSHTunnelView.swift @@ -49,7 +49,7 @@ struct ConnectionSSHTunnelView: View { Section { HStack { Image(systemName: "exclamationmark.triangle.fill") - .foregroundStyle(.yellow) + .foregroundStyle(Color(nsColor: .systemYellow)) Text("Selected SSH profile no longer exists.") } Button("Switch to Inline Configuration") { @@ -309,9 +309,12 @@ struct ConnectionSSHTunnelView: View { let idToRemove = jumpHost.id withAnimation { jumpHosts.removeAll { $0.id == idToRemove } } } label: { - Image(systemName: "minus.circle.fill").foregroundStyle(.red) + Image(systemName: "minus.circle.fill") + .frame(width: 24, height: 24) + .foregroundStyle(Color(nsColor: .systemRed)) } .buttonStyle(.plain) + .accessibilityLabel(String(localized: "Remove jump host")) } } } diff --git a/TablePro/Views/Connection/ConnectionSidebarHeader.swift b/TablePro/Views/Connection/ConnectionSidebarHeader.swift index a9772a84f..4ee731865 100644 --- a/TablePro/Views/Connection/ConnectionSidebarHeader.swift +++ b/TablePro/Views/Connection/ConnectionSidebarHeader.swift @@ -158,13 +158,13 @@ struct ConnectionSidebarHeader: View { private func statusColor(for session: ConnectionSession) -> Color { switch session.status { case .connected: - return .green + return Color(nsColor: .systemGreen) case .connecting: - return .orange + return Color(nsColor: .systemOrange) case .disconnected: return .gray case .error: - return .red + return Color(nsColor: .systemRed) } } } diff --git a/TablePro/Views/Connection/OnboardingContentView.swift b/TablePro/Views/Connection/OnboardingContentView.swift index ed27ee92f..c4b0286c1 100644 --- a/TablePro/Views/Connection/OnboardingContentView.swift +++ b/TablePro/Views/Connection/OnboardingContentView.swift @@ -162,7 +162,7 @@ struct OnboardingContentView: View { VStack(spacing: ThemeEngine.shared.activeTheme.spacing.md) { Image(systemName: "checkmark.circle.fill") .font(.system(size: 48)) - .foregroundStyle(.green) + .foregroundStyle(Color(nsColor: .systemGreen)) Text("You're all set!") .font(.system(size: 22, weight: .bold, design: .rounded)) diff --git a/TablePro/Views/Connection/SSHProfileEditorView.swift b/TablePro/Views/Connection/SSHProfileEditorView.swift index 96797c3b7..e4dc7b704 100644 --- a/TablePro/Views/Connection/SSHProfileEditorView.swift +++ b/TablePro/Views/Connection/SSHProfileEditorView.swift @@ -269,9 +269,12 @@ struct SSHProfileEditorView: View { let idToRemove = jumpHost.id withAnimation { jumpHosts.removeAll { $0.id == idToRemove } } } label: { - Image(systemName: "minus.circle.fill").foregroundStyle(.red) + Image(systemName: "minus.circle.fill") + .frame(width: 24, height: 24) + .foregroundStyle(Color(nsColor: .systemRed)) } .buttonStyle(.plain) + .accessibilityLabel(String(localized: "Remove jump host")) } } } @@ -326,7 +329,7 @@ struct SSHProfileEditorView: View { .controlSize(.small) } else { Image(systemName: testSucceeded ? "checkmark.circle.fill" : "bolt.horizontal") - .foregroundStyle(testSucceeded ? .green : .secondary) + .foregroundStyle(testSucceeded ? Color(nsColor: .systemGreen) : .secondary) } Text("Test Connection") } diff --git a/TablePro/Views/Connection/WelcomeConnectionRow.swift b/TablePro/Views/Connection/WelcomeConnectionRow.swift index d48a88434..efb5b3c57 100644 --- a/TablePro/Views/Connection/WelcomeConnectionRow.swift +++ b/TablePro/Views/Connection/WelcomeConnectionRow.swift @@ -40,7 +40,7 @@ struct WelcomeConnectionRow: View { .padding(.horizontal, ThemeEngine.shared.activeTheme.spacing.xxs) .padding(.vertical, ThemeEngine.shared.activeTheme.spacing.xxxs) .background( - RoundedRectangle(cornerRadius: 4).fill( + RoundedRectangle(cornerRadius: ThemeEngine.shared.activeTheme.cornerRadius.small).fill( tag.color.color.opacity(0.15))) } } diff --git a/TablePro/Views/Connection/WelcomeLeftPanel.swift b/TablePro/Views/Connection/WelcomeLeftPanel.swift index b2bec2328..90ca22056 100644 --- a/TablePro/Views/Connection/WelcomeLeftPanel.swift +++ b/TablePro/Views/Connection/WelcomeLeftPanel.swift @@ -100,7 +100,7 @@ struct WelcomeButtonStyle: ButtonStyle { .padding(.vertical, ThemeEngine.shared.activeTheme.spacing.sm) .frame(maxWidth: .infinity, alignment: .leading) .background( - RoundedRectangle(cornerRadius: 8) + RoundedRectangle(cornerRadius: ThemeEngine.shared.activeTheme.cornerRadius.large) .fill( Color( nsColor: configuration.isPressed diff --git a/TablePro/Views/Connection/WelcomeWindowView.swift b/TablePro/Views/Connection/WelcomeWindowView.swift index a0c9a90ae..c91a2e00a 100644 --- a/TablePro/Views/Connection/WelcomeWindowView.swift +++ b/TablePro/Views/Connection/WelcomeWindowView.swift @@ -132,7 +132,7 @@ struct WelcomeWindowView: View { ) } .buttonStyle(.plain) - .help("New Connection (⌘N)") + .help(String(localized: "New Connection (⌘N)")) Button(action: { vm.pendingMoveToNewGroup = []; vm.activeSheet = .newGroup(parentId: nil) }) { Image(systemName: "folder.badge.plus") diff --git a/TablePro/Views/DatabaseSwitcher/DatabaseSwitcherSheet.swift b/TablePro/Views/DatabaseSwitcher/DatabaseSwitcherSheet.swift index 24e791ebe..da7c11bff 100644 --- a/TablePro/Views/DatabaseSwitcher/DatabaseSwitcherSheet.swift +++ b/TablePro/Views/DatabaseSwitcher/DatabaseSwitcherSheet.swift @@ -171,7 +171,7 @@ struct DatabaseSwitcherSheet: View { .frame(width: 24, height: 24) } .buttonStyle(.borderless) - .help("Refresh database list") + .help(String(localized: "Refresh database list")) // Create (only for non-SQLite) if databaseType != .sqlite && !isSchemaMode { @@ -180,7 +180,7 @@ struct DatabaseSwitcherSheet: View { .frame(width: 24, height: 24) } .buttonStyle(.borderless) - .help("Create new database") + .help(String(localized: "Create new database")) } } .padding(.horizontal, 12) diff --git a/TablePro/Views/Editor/ExplainResultView.swift b/TablePro/Views/Editor/ExplainResultView.swift index c10194b52..36350030c 100644 --- a/TablePro/Views/Editor/ExplainResultView.swift +++ b/TablePro/Views/Editor/ExplainResultView.swift @@ -30,6 +30,7 @@ struct ExplainResultView: View { Image(systemName: "textformat.size.smaller") .frame(width: 24, height: 24) } + .accessibilityLabel(String(localized: "Decrease font size")) Text("\(Int(fontSize))") .font(.caption) .foregroundStyle(.secondary) @@ -38,6 +39,7 @@ struct ExplainResultView: View { Image(systemName: "textformat.size.larger") .frame(width: 24, height: 24) } + .accessibilityLabel(String(localized: "Increase font size")) } .buttonStyle(.borderless) @@ -52,7 +54,7 @@ struct ExplainResultView: View { if showCopyConfirmation { HStack { Image(systemName: "checkmark.circle.fill") - .foregroundStyle(.green) + .foregroundStyle(Color(nsColor: .systemGreen)) Text("Copied!") } .transition(.opacity) diff --git a/TablePro/Views/Editor/HistoryPanelView.swift b/TablePro/Views/Editor/HistoryPanelView.swift index 180794c82..4cf8755d9 100644 --- a/TablePro/Views/Editor/HistoryPanelView.swift +++ b/TablePro/Views/Editor/HistoryPanelView.swift @@ -81,6 +81,7 @@ private extension HistoryPanelView { .buttonStyle(.borderless) .disabled(entries.isEmpty) .help(String(localized: "Clear all history")) + .accessibilityLabel(String(localized: "Clear all history")) Picker("", selection: $dateFilter) { ForEach(UIDateFilter.allCases, id: \.self) { filter in diff --git a/TablePro/Views/Editor/QueryEditorView.swift b/TablePro/Views/Editor/QueryEditorView.swift index 1d20499c5..b367f290a 100644 --- a/TablePro/Views/Editor/QueryEditorView.swift +++ b/TablePro/Views/Editor/QueryEditorView.swift @@ -84,7 +84,7 @@ struct QueryEditorView: View { .frame(width: 24, height: 24) } .buttonStyle(.borderless) - .help("Clear Query") + .help(String(localized: "Clear Query")) // Format button Button(action: formatQuery) { @@ -92,7 +92,7 @@ struct QueryEditorView: View { .frame(width: 24, height: 24) } .buttonStyle(.borderless) - .help("Format Query (⌥⌘F)") + .help(String(localized: "Format Query (⌥⌘F)")) .keyboardShortcut("f", modifiers: [.option, .command]) Divider() diff --git a/TablePro/Views/Editor/QuerySuccessView.swift b/TablePro/Views/Editor/QuerySuccessView.swift index b1f7bface..37d885f91 100644 --- a/TablePro/Views/Editor/QuerySuccessView.swift +++ b/TablePro/Views/Editor/QuerySuccessView.swift @@ -20,7 +20,7 @@ struct QuerySuccessView: View { // Success icon Image(systemName: "checkmark.circle.fill") .font(.system(size: 64)) - .foregroundStyle(.green) + .foregroundStyle(Color(nsColor: .systemGreen)) // Success message Text("Query executed successfully") diff --git a/TablePro/Views/Editor/VimModeIndicatorView.swift b/TablePro/Views/Editor/VimModeIndicatorView.swift index 8d3d87c60..2ca3f33d2 100644 --- a/TablePro/Views/Editor/VimModeIndicatorView.swift +++ b/TablePro/Views/Editor/VimModeIndicatorView.swift @@ -19,7 +19,7 @@ struct VimModeIndicatorView: View { .padding(.horizontal, 6) .padding(.vertical, 2) .background(backgroundColor) - .clipShape(RoundedRectangle(cornerRadius: 4)) + .clipShape(RoundedRectangle(cornerRadius: ThemeEngine.shared.activeTheme.cornerRadius.small)) } else { Text(mode.displayLabel) .font(.system(size: 10, weight: .semibold, design: .monospaced)) @@ -27,7 +27,7 @@ struct VimModeIndicatorView: View { .padding(.horizontal, 6) .padding(.vertical, 2) .background(backgroundColor) - .clipShape(RoundedRectangle(cornerRadius: 4)) + .clipShape(RoundedRectangle(cornerRadius: ThemeEngine.shared.activeTheme.cornerRadius.small)) } } diff --git a/TablePro/Views/Export/ExportDialog.swift b/TablePro/Views/Export/ExportDialog.swift index 4db09e90b..17a8f9be4 100644 --- a/TablePro/Views/Export/ExportDialog.swift +++ b/TablePro/Views/Export/ExportDialog.swift @@ -288,7 +288,7 @@ struct ExportDialog: View { if isProGatedFormat(config.formatId) { Text(String(localized: "XLSX export requires a Pro license.")) .font(.system(size: ThemeEngine.shared.activeTheme.typography.small)) - .foregroundStyle(.orange) + .foregroundStyle(Color(nsColor: .systemOrange)) Button(String(localized: "Activate License...")) { showActivationSheet = true } @@ -306,7 +306,7 @@ struct ExportDialog: View { if let plugin = currentPlugin, !type(of: plugin).perTableOptionColumns.isEmpty, exportableCount < selectedCount { Text("\(selectedCount - exportableCount) skipped (no options)") .font(.system(size: ThemeEngine.shared.activeTheme.typography.small)) - .foregroundStyle(.orange) + .foregroundStyle(Color(nsColor: .systemOrange)) } } } @@ -356,7 +356,7 @@ struct ExportDialog: View { if let validationError = fileNameValidationError { Text(validationError) .font(.system(size: ThemeEngine.shared.activeTheme.typography.small)) - .foregroundStyle(.red) + .foregroundStyle(Color(nsColor: .systemRed)) } } .padding(16) diff --git a/TablePro/Views/Export/ExportProgressView.swift b/TablePro/Views/Export/ExportProgressView.swift index 7e21c72b2..a856be0e0 100644 --- a/TablePro/Views/Export/ExportProgressView.swift +++ b/TablePro/Views/Export/ExportProgressView.swift @@ -24,7 +24,7 @@ struct ExportProgressView: View { Text(totalTables > 1 ? String(localized: "Export multiple tables") : String(localized: "Export table")) - .font(.system(size: 15, weight: .semibold)) + .font(.system(size: ThemeEngine.shared.activeTheme.typography.title3, weight: .semibold)) // Table info and row count VStack(spacing: 8) { @@ -32,11 +32,11 @@ struct ExportProgressView: View { // Show status message if set, otherwise show table name if !statusMessage.isEmpty { Text(statusMessage) - .font(.system(size: 13)) + .font(.system(size: ThemeEngine.shared.activeTheme.typography.body)) .foregroundStyle(.secondary) } else { Text("\(tableName) (\(tableIndex)/\(totalTables))") - .font(.system(size: 13)) + .font(.system(size: ThemeEngine.shared.activeTheme.typography.body)) .lineLimit(1) .truncationMode(.middle) } @@ -45,7 +45,7 @@ struct ExportProgressView: View { if statusMessage.isEmpty { Text("\(processedRows.formatted())/\(totalRows.formatted()) rows") - .font(.system(size: 13, design: .monospaced)) + .font(.system(size: ThemeEngine.shared.activeTheme.typography.body, design: .monospaced)) .foregroundStyle(.secondary) } } diff --git a/TablePro/Views/Export/ExportSuccessView.swift b/TablePro/Views/Export/ExportSuccessView.swift index c82552079..beaaa1437 100644 --- a/TablePro/Views/Export/ExportSuccessView.swift +++ b/TablePro/Views/Export/ExportSuccessView.swift @@ -30,10 +30,10 @@ struct ExportSuccessView: View { // Title and message VStack(spacing: 6) { Text("Success") - .font(.system(size: 15, weight: .semibold)) + .font(.system(size: ThemeEngine.shared.activeTheme.typography.title3, weight: .semibold)) Text("Export completed successfully") - .font(.system(size: 13)) + .font(.system(size: ThemeEngine.shared.activeTheme.typography.body)) .foregroundStyle(.secondary) } @@ -57,7 +57,7 @@ struct ExportSuccessView: View { // Don't show again checkbox Toggle("Don't show this again", isOn: $localDontShowAgain) .toggleStyle(.checkbox) - .font(.system(size: 12)) + .font(.system(size: ThemeEngine.shared.activeTheme.typography.medium)) .foregroundStyle(.secondary) } .padding(24) diff --git a/TablePro/Views/Export/ExportTableTreeView.swift b/TablePro/Views/Export/ExportTableTreeView.swift index 447afb072..b433e2b84 100644 --- a/TablePro/Views/Export/ExportTableTreeView.swift +++ b/TablePro/Views/Export/ExportTableTreeView.swift @@ -71,10 +71,10 @@ struct ExportTableTreeView: View { Image(systemName: "cylinder") .foregroundStyle(.blue) - .font(.system(size: 13)) + .font(.system(size: ThemeEngine.shared.activeTheme.typography.body)) Text(database.name) - .font(.system(size: 13)) + .font(.system(size: ThemeEngine.shared.activeTheme.typography.body)) .lineLimit(1) .truncationMode(.middle) } @@ -107,10 +107,10 @@ struct ExportTableTreeView: View { Image(systemName: table.wrappedValue.type == .view ? "eye" : "tablecells") .foregroundStyle(table.wrappedValue.type == .view ? .purple : .gray) - .font(.system(size: 13)) + .font(.system(size: ThemeEngine.shared.activeTheme.typography.body)) Text(table.wrappedValue.name) - .font(.system(size: 13)) + .font(.system(size: ThemeEngine.shared.activeTheme.typography.body)) .lineLimit(1) .truncationMode(.middle) diff --git a/TablePro/Views/Filter/FilterPanelView.swift b/TablePro/Views/Filter/FilterPanelView.swift index 957dac008..95fd08a3e 100644 --- a/TablePro/Views/Filter/FilterPanelView.swift +++ b/TablePro/Views/Filter/FilterPanelView.swift @@ -66,7 +66,7 @@ struct FilterPanelView: View { .pickerStyle(.segmented) .frame(width: 80) .accessibilityLabel(String(localized: "Filter logic mode")) - .help("Match ALL filters (AND) or ANY filter (OR)") + .help(String(localized: "Match ALL filters (AND) or ANY filter (OR)")) } Spacer() @@ -94,8 +94,8 @@ struct FilterPanelView: View { .padding(.vertical, ThemeEngine.shared.activeTheme.spacing.xs) .background(Color(nsColor: .controlBackgroundColor)) .contentShape(Rectangle()) - .alert("Save Filter Preset", isPresented: $showSavePresetAlert) { - TextField("Preset Name", text: $newPresetName) + .alert(String(localized: "Save Filter Preset"), isPresented: $showSavePresetAlert) { + TextField(String(localized: "Preset Name"), text: $newPresetName) Button("Cancel", role: .cancel) {} Button("Save") { guard !newPresetName.isEmpty else { return } diff --git a/TablePro/Views/Import/ImportDialog.swift b/TablePro/Views/Import/ImportDialog.swift index dcbf4d3a0..702a08893 100644 --- a/TablePro/Views/Import/ImportDialog.swift +++ b/TablePro/Views/Import/ImportDialog.swift @@ -159,7 +159,7 @@ struct ImportDialog: View { selectFile() } .buttonStyle(.link) - .font(.system(size: 12)) + .font(.system(size: ThemeEngine.shared.activeTheme.typography.medium)) } HStack(spacing: 16) { @@ -167,7 +167,7 @@ struct ImportDialog: View { ByteCountFormatter.string(fromByteCount: fileSize, countStyle: .file), systemImage: "chart.bar.doc.horizontal" ) - .font(.system(size: 12)) + .font(.system(size: ThemeEngine.shared.activeTheme.typography.medium)) .foregroundStyle(.secondary) if isCountingStatements { @@ -175,12 +175,12 @@ struct ImportDialog: View { ProgressView() .controlSize(.small) Text("Counting...") - .font(.system(size: 12)) + .font(.system(size: ThemeEngine.shared.activeTheme.typography.medium)) .foregroundStyle(.secondary) } } else if statementCount > 0 { Label("\(statementCount) statements", systemImage: "list.bullet") - .font(.system(size: 12)) + .font(.system(size: ThemeEngine.shared.activeTheme.typography.medium)) .foregroundStyle(.secondary) } } @@ -192,7 +192,7 @@ struct ImportDialog: View { private var formatPickerView: some View { HStack(spacing: 8) { Text("Format:") - .font(.system(size: 13)) + .font(.system(size: ThemeEngine.shared.activeTheme.typography.body)) .frame(width: 80, alignment: .leading) Picker("", selection: $selectedFormatId) { @@ -210,14 +210,14 @@ struct ImportDialog: View { private var filePreviewView: some View { VStack(alignment: .leading, spacing: 6) { Text("Preview") - .font(.system(size: 12, weight: .semibold)) + .font(.system(size: ThemeEngine.shared.activeTheme.typography.medium, weight: .semibold)) .foregroundStyle(.primary) SQLCodePreview(text: $filePreview) .frame(height: availableFormats.count > 1 ? 220 : 280) - .cornerRadius(6) + .clipShape(RoundedRectangle(cornerRadius: ThemeEngine.shared.activeTheme.cornerRadius.medium)) .overlay( - RoundedRectangle(cornerRadius: 6) + RoundedRectangle(cornerRadius: ThemeEngine.shared.activeTheme.cornerRadius.medium) .stroke(Color(nsColor: .separatorColor), lineWidth: 1) ) } @@ -226,14 +226,14 @@ struct ImportDialog: View { private var optionsView: some View { VStack(alignment: .leading, spacing: 14) { Text("Options") - .font(.system(size: 12, weight: .semibold)) + .font(.system(size: ThemeEngine.shared.activeTheme.typography.medium, weight: .semibold)) .foregroundStyle(.primary) VStack(alignment: .leading, spacing: 12) { // Encoding picker (always shown, independent of plugin) HStack(spacing: 8) { Text("Encoding:") - .font(.system(size: 13)) + .font(.system(size: ThemeEngine.shared.activeTheme.typography.body)) .frame(width: 80, alignment: .leading) Picker("", selection: $selectedEncoding) { diff --git a/TablePro/Views/Import/ImportErrorView.swift b/TablePro/Views/Import/ImportErrorView.swift index 6fca06521..1204e2a75 100644 --- a/TablePro/Views/Import/ImportErrorView.swift +++ b/TablePro/Views/Import/ImportErrorView.swift @@ -20,29 +20,29 @@ struct ImportErrorView: View { VStack(spacing: 6) { Text("Import Failed") - .font(.system(size: 15, weight: .semibold)) + .font(.system(size: ThemeEngine.shared.activeTheme.typography.title3, weight: .semibold)) if let pluginError = error as? PluginImportError, case .statementFailed(let statement, let line, let underlyingError) = pluginError { Text("Failed at line \(line)") - .font(.system(size: 13)) + .font(.system(size: ThemeEngine.shared.activeTheme.typography.body)) .foregroundStyle(.secondary) ScrollView { VStack(alignment: .leading, spacing: 8) { Text("Statement:") - .font(.system(size: 12, weight: .medium)) + .font(.system(size: ThemeEngine.shared.activeTheme.typography.medium, weight: .medium)) Text(statement) - .font(.system(size: 11, design: .monospaced)) + .font(.system(size: ThemeEngine.shared.activeTheme.typography.small, design: .monospaced)) .textSelection(.enabled) .frame(maxWidth: .infinity, alignment: .leading) Text("Error:") - .font(.system(size: 12, weight: .medium)) + .font(.system(size: ThemeEngine.shared.activeTheme.typography.medium, weight: .medium)) .padding(.top, 8) Text(underlyingError.localizedDescription) - .font(.system(size: 11)) + .font(.system(size: ThemeEngine.shared.activeTheme.typography.small)) .foregroundStyle(.red) .frame(maxWidth: .infinity, alignment: .leading) } @@ -51,10 +51,10 @@ struct ImportErrorView: View { .frame(height: 150) .padding(8) .background(Color(nsColor: .textBackgroundColor)) - .cornerRadius(4) + .clipShape(RoundedRectangle(cornerRadius: ThemeEngine.shared.activeTheme.cornerRadius.small)) } else { Text(error?.localizedDescription ?? String(localized: "Unknown error")) - .font(.system(size: 13)) + .font(.system(size: ThemeEngine.shared.activeTheme.typography.body)) .foregroundStyle(.secondary) .multilineTextAlignment(.center) } diff --git a/TablePro/Views/Import/ImportProgressView.swift b/TablePro/Views/Import/ImportProgressView.swift index 35a57c329..0eb8aa05e 100644 --- a/TablePro/Views/Import/ImportProgressView.swift +++ b/TablePro/Views/Import/ImportProgressView.swift @@ -16,17 +16,17 @@ struct ImportProgressView: View { var body: some View { VStack(spacing: 20) { Text("Importing...") - .font(.system(size: 15, weight: .semibold)) + .font(.system(size: ThemeEngine.shared.activeTheme.typography.title3, weight: .semibold)) VStack(spacing: 8) { HStack { if !statusMessage.isEmpty { Text(statusMessage) - .font(.system(size: 13)) + .font(.system(size: ThemeEngine.shared.activeTheme.typography.body)) .foregroundStyle(.secondary) } else { Text("Executed \(processedStatements) statements") - .font(.system(size: 13)) + .font(.system(size: ThemeEngine.shared.activeTheme.typography.body)) Spacer() } diff --git a/TablePro/Views/Import/ImportSuccessView.swift b/TablePro/Views/Import/ImportSuccessView.swift index d726da945..602b092a8 100644 --- a/TablePro/Views/Import/ImportSuccessView.swift +++ b/TablePro/Views/Import/ImportSuccessView.swift @@ -20,16 +20,16 @@ struct ImportSuccessView: View { VStack(spacing: 6) { Text("Import Successful") - .font(.system(size: 15, weight: .semibold)) + .font(.system(size: ThemeEngine.shared.activeTheme.typography.title3, weight: .semibold)) if let result { Text("\(result.executedStatements) statements executed") - .font(.system(size: 13)) + .font(.system(size: ThemeEngine.shared.activeTheme.typography.body)) .foregroundStyle(.secondary) let formattedTime = String(format: "%.2f", result.executionTime) Text(String(format: String(localized: "%@ seconds"), formattedTime)) - .font(.system(size: 12)) + .font(.system(size: ThemeEngine.shared.activeTheme.typography.medium)) .foregroundStyle(.secondary) } } diff --git a/TablePro/Views/Main/Child/MainEditorContentView.swift b/TablePro/Views/Main/Child/MainEditorContentView.swift index 47acb6225..bb12a01d2 100644 --- a/TablePro/Views/Main/Child/MainEditorContentView.swift +++ b/TablePro/Views/Main/Child/MainEditorContentView.swift @@ -761,7 +761,7 @@ struct MainEditorContentView: View { .padding(.horizontal, 6) .padding(.vertical, 2) .background( - RoundedRectangle(cornerRadius: 4) + RoundedRectangle(cornerRadius: ThemeEngine.shared.activeTheme.cornerRadius.small) .fill(Color(nsColor: .quaternaryLabelColor)) ) Text( @@ -787,7 +787,7 @@ struct MainEditorContentView: View { .padding(.horizontal, 6) .padding(.vertical, 2) .background( - RoundedRectangle(cornerRadius: 4) + RoundedRectangle(cornerRadius: ThemeEngine.shared.activeTheme.cornerRadius.small) .fill(Color(nsColor: .quaternaryLabelColor)) ) Text("Switch Database") diff --git a/TablePro/Views/QuickSwitcher/QuickSwitcherView.swift b/TablePro/Views/QuickSwitcher/QuickSwitcherView.swift index 2ecbcf385..e4288463a 100644 --- a/TablePro/Views/QuickSwitcher/QuickSwitcherView.swift +++ b/TablePro/Views/QuickSwitcher/QuickSwitcherView.swift @@ -169,14 +169,14 @@ internal struct QuickSwitcherSheet: View { .padding(.horizontal, 6) .padding(.vertical, 2) .background( - RoundedRectangle(cornerRadius: 4) + RoundedRectangle(cornerRadius: ThemeEngine.shared.activeTheme.cornerRadius.small) .fill(isSelected ? Color(nsColor: .alternateSelectedControlTextColor).opacity(0.15) : Color(nsColor: .quaternaryLabelColor)) ) } .padding(.vertical, 4) .contentShape(Rectangle()) .listRowBackground( - RoundedRectangle(cornerRadius: 4) + RoundedRectangle(cornerRadius: ThemeEngine.shared.activeTheme.cornerRadius.small) .fill(isSelected ? Color(nsColor: .selectedContentBackgroundColor) : Color.clear) .padding(.horizontal, 4) ) diff --git a/TablePro/Views/Results/EnumPopoverContentView.swift b/TablePro/Views/Results/EnumPopoverContentView.swift index 07220a57b..f325715dd 100644 --- a/TablePro/Views/Results/EnumPopoverContentView.swift +++ b/TablePro/Views/Results/EnumPopoverContentView.swift @@ -37,7 +37,7 @@ struct EnumPopoverContentView: View { VStack(spacing: 0) { TextField("Search...", text: $searchText) .textFieldStyle(.roundedBorder) - .font(.system(size: 13)) + .font(.system(size: ThemeEngine.shared.activeTheme.typography.body)) .padding(.horizontal, 8) .padding(.vertical, 8) @@ -70,19 +70,19 @@ struct EnumPopoverContentView: View { private func rowLabel(for value: String) -> some View { if value == enumNullMarker { Text(value) - .font(.system(size: 12, design: .monospaced).italic()) + .font(.system(size: ThemeEngine.shared.activeTheme.typography.medium, design: .monospaced).italic()) .foregroundStyle(.secondary) .lineLimit(1) .truncationMode(.tail) } else if value == currentValue { Text(value) - .font(.system(size: 12, design: .monospaced)) + .font(.system(size: ThemeEngine.shared.activeTheme.typography.medium, design: .monospaced)) .foregroundStyle(.tint) .lineLimit(1) .truncationMode(.tail) } else { Text(value) - .font(.system(size: 12, design: .monospaced)) + .font(.system(size: ThemeEngine.shared.activeTheme.typography.medium, design: .monospaced)) .foregroundStyle(.primary) .lineLimit(1) .truncationMode(.tail) diff --git a/TablePro/Views/Results/ForeignKeyPopoverContentView.swift b/TablePro/Views/Results/ForeignKeyPopoverContentView.swift index b1a2a355e..d8de3dee5 100644 --- a/TablePro/Views/Results/ForeignKeyPopoverContentView.swift +++ b/TablePro/Views/Results/ForeignKeyPopoverContentView.swift @@ -43,7 +43,7 @@ struct ForeignKeyPopoverContentView: View { VStack(spacing: 0) { TextField("Search...", text: $searchText) .textFieldStyle(.roundedBorder) - .font(.system(size: 13)) + .font(.system(size: ThemeEngine.shared.activeTheme.typography.body)) .padding(.horizontal, 8) .padding(.vertical, 8) @@ -56,7 +56,7 @@ struct ForeignKeyPopoverContentView: View { } else if filteredValues.isEmpty { Text("No values found") .foregroundStyle(.secondary) - .font(.system(size: 12)) + .font(.system(size: ThemeEngine.shared.activeTheme.typography.medium)) .frame(maxWidth: .infinity, alignment: .leading) .frame(height: 60) } else { @@ -94,13 +94,13 @@ struct ForeignKeyPopoverContentView: View { private func rowLabel(for value: FKValue) -> some View { if value.id == currentValue { Text(value.display) - .font(.system(size: 12, design: .monospaced)) + .font(.system(size: ThemeEngine.shared.activeTheme.typography.medium, design: .monospaced)) .foregroundStyle(.tint) .lineLimit(1) .truncationMode(.tail) } else { Text(value.display) - .font(.system(size: 12, design: .monospaced)) + .font(.system(size: ThemeEngine.shared.activeTheme.typography.medium, design: .monospaced)) .foregroundStyle(.primary) .lineLimit(1) .truncationMode(.tail) diff --git a/TablePro/Views/Results/ForeignKeyPreviewView.swift b/TablePro/Views/Results/ForeignKeyPreviewView.swift index e0c7e687a..b43126cb3 100644 --- a/TablePro/Views/Results/ForeignKeyPreviewView.swift +++ b/TablePro/Views/Results/ForeignKeyPreviewView.swift @@ -42,7 +42,7 @@ struct ForeignKeyPreviewView: View { private var header: some View { HStack { Text("\(fkInfo.column) → \(fkInfo.referencedTable).\(fkInfo.referencedColumn)") - .font(.system(size: 11, design: .monospaced)) + .font(.system(size: ThemeEngine.shared.activeTheme.typography.small, design: .monospaced)) .foregroundStyle(.secondary) Spacer() } @@ -57,7 +57,7 @@ struct ForeignKeyPreviewView: View { if cellValue == nil { Text("NULL — no referenced row") .foregroundStyle(.secondary) - .font(.system(size: 12)) + .font(.system(size: ThemeEngine.shared.activeTheme.typography.medium)) .frame(maxWidth: .infinity, alignment: .center) .frame(height: 60) } else if isLoading { @@ -67,12 +67,12 @@ struct ForeignKeyPreviewView: View { } else if let errorMessage { Text(errorMessage) .foregroundStyle(.red) - .font(.system(size: 12)) + .font(.system(size: ThemeEngine.shared.activeTheme.typography.medium)) .padding(10) } else if values.isEmpty { Text("Referenced row not found") .foregroundStyle(.secondary) - .font(.system(size: 12)) + .font(.system(size: ThemeEngine.shared.activeTheme.typography.medium)) .frame(maxWidth: .infinity, alignment: .center) .frame(height: 60) } else { @@ -82,20 +82,20 @@ struct ForeignKeyPreviewView: View { let (col, value) = pair HStack(alignment: .top, spacing: 8) { Text(col) - .font(.system(size: 11, design: .monospaced)) + .font(.system(size: ThemeEngine.shared.activeTheme.typography.small, design: .monospaced)) .foregroundStyle(.secondary) .frame(width: 120, alignment: .trailing) .lineLimit(1) if let val = value { Text(val) - .font(.system(size: 12, design: .monospaced)) + .font(.system(size: ThemeEngine.shared.activeTheme.typography.medium, design: .monospaced)) .foregroundStyle(.primary) .lineLimit(3) .textSelection(.enabled) } else { Text("NULL") - .font(.system(size: 12, design: .monospaced)) + .font(.system(size: ThemeEngine.shared.activeTheme.typography.medium, design: .monospaced)) .foregroundStyle(.tertiary) .italic() } diff --git a/TablePro/Views/Results/InlineErrorBanner.swift b/TablePro/Views/Results/InlineErrorBanner.swift index deb83dfd8..59a18d8c5 100644 --- a/TablePro/Views/Results/InlineErrorBanner.swift +++ b/TablePro/Views/Results/InlineErrorBanner.swift @@ -31,6 +31,7 @@ struct InlineErrorBanner: View { } .buttonStyle(.plain) .help(String(localized: "Copy error message")) + .accessibilityLabel(String(localized: "Copy error message")) if let onDismiss { Button { onDismiss() } label: { Image(systemName: "xmark") @@ -38,6 +39,7 @@ struct InlineErrorBanner: View { .foregroundStyle(.secondary) } .buttonStyle(.plain) + .accessibilityLabel(String(localized: "Dismiss error")) } } .padding(.horizontal, 12) diff --git a/TablePro/Views/Results/ResultTabBar.swift b/TablePro/Views/Results/ResultTabBar.swift index 99fb65ee9..9bb9ab72b 100644 --- a/TablePro/Views/Results/ResultTabBar.swift +++ b/TablePro/Views/Results/ResultTabBar.swift @@ -35,7 +35,7 @@ struct ResultTabBar: View { .foregroundStyle(.secondary) } Text(rs.label) - .font(.system(size: 11)) + .font(.system(size: ThemeEngine.shared.activeTheme.typography.small)) .lineLimit(1) if !rs.isPinned { Button { onClose?(rs.id) } label: { @@ -49,7 +49,7 @@ struct ResultTabBar: View { .padding(.horizontal, 10) .padding(.vertical, 6) .background( - RoundedRectangle(cornerRadius: 4) + RoundedRectangle(cornerRadius: ThemeEngine.shared.activeTheme.cornerRadius.small) .fill(isActive ? Color(nsColor: .selectedControlColor) : Color.clear) ) .contentShape(Rectangle()) diff --git a/TablePro/Views/RightSidebar/EditableFieldView.swift b/TablePro/Views/RightSidebar/EditableFieldView.swift index 026ad5ada..5141eef82 100644 --- a/TablePro/Views/RightSidebar/EditableFieldView.swift +++ b/TablePro/Views/RightSidebar/EditableFieldView.swift @@ -91,10 +91,10 @@ internal struct FieldDetailView: View { if isTruncated && !isLoadingFullValue { Text("truncated") .font(.system(size: ThemeEngine.shared.activeTheme.typography.tiny, weight: .medium)) - .foregroundStyle(.orange) + .foregroundStyle(Color(nsColor: .systemOrange)) .padding(.horizontal, 5) .padding(.vertical, 1) - .background(.orange.opacity(0.15)) + .background(Color(nsColor: .systemOrange).opacity(0.15)) .clipShape(Capsule()) } } diff --git a/TablePro/Views/RightSidebar/FieldEditors/BlobHexEditorView.swift b/TablePro/Views/RightSidebar/FieldEditors/BlobHexEditorView.swift index f889f0cd5..b9ab364b2 100644 --- a/TablePro/Views/RightSidebar/FieldEditors/BlobHexEditorView.swift +++ b/TablePro/Views/RightSidebar/FieldEditors/BlobHexEditorView.swift @@ -60,7 +60,7 @@ internal struct BlobHexEditorView: View { if BlobFormattingService.shared.parseHex(hexEditText) == nil, !hexEditText.isEmpty { Text("Invalid hex") .font(.system(size: ThemeEngine.shared.activeTheme.typography.tiny)) - .foregroundStyle(.red) + .foregroundStyle(Color(nsColor: .systemRed)) } } } diff --git a/TablePro/Views/RightSidebar/FieldEditors/FieldMenuView.swift b/TablePro/Views/RightSidebar/FieldEditors/FieldMenuView.swift index ed8f97aaa..5798afd5b 100644 --- a/TablePro/Views/RightSidebar/FieldEditors/FieldMenuView.swift +++ b/TablePro/Views/RightSidebar/FieldEditors/FieldMenuView.swift @@ -59,7 +59,7 @@ internal struct FieldMenuView: View { } } label: { Image(systemName: "chevron.down") - .font(.system(size: 10)) + .font(.system(size: ThemeEngine.shared.activeTheme.typography.caption)) .frame(width: 20, height: 20) .contentShape(Rectangle()) } diff --git a/TablePro/Views/Settings/AISettingsView.swift b/TablePro/Views/Settings/AISettingsView.swift index c482709e2..06e76ae5b 100644 --- a/TablePro/Views/Settings/AISettingsView.swift +++ b/TablePro/Views/Settings/AISettingsView.swift @@ -86,6 +86,7 @@ struct AISettingsView: View { } .buttonStyle(.borderless) .disabled(selectedProviderID == nil) + .accessibilityLabel(String(localized: "Remove provider")) Divider() .frame(height: 16) @@ -97,6 +98,7 @@ struct AISettingsView: View { .frame(width: 24, height: 24) } .buttonStyle(.borderless) + .accessibilityLabel(String(localized: "Add provider")) Spacer() @@ -111,6 +113,7 @@ struct AISettingsView: View { } .buttonStyle(.borderless) .disabled(selectedProviderID == nil) + .accessibilityLabel(String(localized: "Edit provider")) } .padding(.horizontal, 4) .padding(.vertical, 2) @@ -454,7 +457,7 @@ private struct AIProviderEditorSheet: View { HStack { Text(error) .font(.caption) - .foregroundStyle(.red) + .foregroundStyle(Color(nsColor: .systemRed)) Button { fetchModels() } label: { @@ -493,11 +496,11 @@ private struct AIProviderEditorSheet: View { if case .success = testResult { Text(String(localized: "Connection successful")) .font(.caption) - .foregroundStyle(.green) + .foregroundStyle(Color(nsColor: .systemGreen)) } else if case .failure(let message) = testResult { Text(message) .font(.caption) - .foregroundStyle(.red) + .foregroundStyle(Color(nsColor: .systemRed)) .lineLimit(2) } } @@ -597,8 +600,8 @@ private struct AIProviderEditorSheet: View { private var testResultColor: Color { switch testResult { - case .success: return .green - case .failure: return .red + case .success: return Color(nsColor: .systemGreen) + case .failure: return Color(nsColor: .systemRed) case .none: return .secondary } } diff --git a/TablePro/Views/Settings/Appearance/ThemeEditorView.swift b/TablePro/Views/Settings/Appearance/ThemeEditorView.swift index c5fc3c1de..4e379b907 100644 --- a/TablePro/Views/Settings/Appearance/ThemeEditorView.swift +++ b/TablePro/Views/Settings/Appearance/ThemeEditorView.swift @@ -36,7 +36,7 @@ internal struct ThemeEditorView: View { var body: some View { VStack(spacing: 0) { Text(theme.name) - .font(.system(size: 15, weight: .semibold)) + .font(.system(size: ThemeEngine.shared.activeTheme.typography.title3, weight: .semibold)) .frame(maxWidth: .infinity, alignment: .leading) .padding(.horizontal, 16) .padding(.top, 12) @@ -97,11 +97,11 @@ internal struct ThemeEditorView: View { Text(theme.isBuiltIn ? String(localized: "This is a built-in theme.") : String(localized: "This is a registry theme.")) - .font(.system(size: 13)) + .font(.system(size: ThemeEngine.shared.activeTheme.typography.body)) .foregroundStyle(.secondary) Text(String(localized: "Duplicate it to customize colors and layout.")) - .font(.system(size: 11)) + .font(.system(size: ThemeEngine.shared.activeTheme.typography.small)) .foregroundStyle(.tertiary) Button(String(localized: "Duplicate Theme")) { diff --git a/TablePro/Views/Settings/Appearance/ThemeListRowView.swift b/TablePro/Views/Settings/Appearance/ThemeListRowView.swift index 6f838a76d..41ca623ef 100644 --- a/TablePro/Views/Settings/Appearance/ThemeListRowView.swift +++ b/TablePro/Views/Settings/Appearance/ThemeListRowView.swift @@ -9,7 +9,7 @@ internal struct ThemeListRowView: View { VStack(alignment: .leading, spacing: 2) { Text(theme.name) - .font(.system(size: 12)) + .font(.system(size: ThemeEngine.shared.activeTheme.typography.medium)) .lineLimit(1) Text(theme.isBuiltIn @@ -17,7 +17,7 @@ internal struct ThemeListRowView: View { : theme.isRegistry ? String(localized: "Registry") : String(localized: "Custom")) - .font(.system(size: 10)) + .font(.system(size: ThemeEngine.shared.activeTheme.typography.caption)) .foregroundStyle(.secondary) } } diff --git a/TablePro/Views/Settings/AppearanceSettingsView.swift b/TablePro/Views/Settings/AppearanceSettingsView.swift index ae9dece50..906aaa1b3 100644 --- a/TablePro/Views/Settings/AppearanceSettingsView.swift +++ b/TablePro/Views/Settings/AppearanceSettingsView.swift @@ -53,7 +53,7 @@ struct AppearanceSettingsView: View { VStack(spacing: 0) { HStack(spacing: 12) { Text("Appearance") - .font(.system(size: 12)) + .font(.system(size: ThemeEngine.shared.activeTheme.typography.medium)) .foregroundStyle(.secondary) Picker("", selection: $settings.appearanceMode) { diff --git a/TablePro/Views/Settings/GeneralSettingsView.swift b/TablePro/Views/Settings/GeneralSettingsView.swift index 97f05346d..04d93f627 100644 --- a/TablePro/Views/Settings/GeneralSettingsView.swift +++ b/TablePro/Views/Settings/GeneralSettingsView.swift @@ -54,7 +54,7 @@ struct GeneralSettingsView: View { Text("\(seconds) seconds").tag(seconds) } } - .help("Maximum time to wait for a query to complete. Set to 0 for no limit. Applied to new connections.") + .help(String(localized: "Maximum time to wait for a query to complete. Set to 0 for no limit. Applied to new connections.")) } Section("Software Update") { diff --git a/TablePro/Views/Settings/LicenseSettingsView.swift b/TablePro/Views/Settings/LicenseSettingsView.swift index 5ecc96e28..26bd6dabb 100644 --- a/TablePro/Views/Settings/LicenseSettingsView.swift +++ b/TablePro/Views/Settings/LicenseSettingsView.swift @@ -53,7 +53,7 @@ struct LicenseSettingsView: View { .controlSize(.small) } .padding(12) - .background(.orange.opacity(0.1), in: RoundedRectangle(cornerRadius: 8)) + .background(.orange.opacity(0.1), in: RoundedRectangle(cornerRadius: ThemeEngine.shared.activeTheme.cornerRadius.large)) } Section("License") { diff --git a/TablePro/Views/Settings/LinkedFoldersSection.swift b/TablePro/Views/Settings/LinkedFoldersSection.swift index 535fb6baf..13f3e4acb 100644 --- a/TablePro/Views/Settings/LinkedFoldersSection.swift +++ b/TablePro/Views/Settings/LinkedFoldersSection.swift @@ -85,10 +85,12 @@ struct LinkedFoldersSection: View { removeFolder(folder) } label: { Image(systemName: "trash") + .frame(width: 24, height: 24) .foregroundStyle(.secondary) } .buttonStyle(.plain) .help(String(localized: "Remove Folder")) + .accessibilityLabel(String(localized: "Remove folder")) } } @@ -131,6 +133,6 @@ private struct ProBadge: View { .foregroundStyle(.white) .padding(.horizontal, 4) .padding(.vertical, 1) - .background(.orange, in: RoundedRectangle(cornerRadius: 3)) + .background(Color(nsColor: .systemOrange), in: RoundedRectangle(cornerRadius: 3)) } } diff --git a/TablePro/Views/Settings/Plugins/BrowsePluginsView.swift b/TablePro/Views/Settings/Plugins/BrowsePluginsView.swift index dfa4f177a..3de2642d3 100644 --- a/TablePro/Views/Settings/Plugins/BrowsePluginsView.swift +++ b/TablePro/Views/Settings/Plugins/BrowsePluginsView.swift @@ -30,7 +30,7 @@ struct BrowsePluginsView: View { } await downloadCountService.fetchCounts(for: registryClient.manifest) } - .alert("Installation Failed", isPresented: $showErrorAlert) { + .alert(String(localized: "Installation Failed"), isPresented: $showErrorAlert) { Button("OK") {} } message: { Text(errorMessage) diff --git a/TablePro/Views/Settings/SyncSettingsView.swift b/TablePro/Views/Settings/SyncSettingsView.swift index c29352b1a..3682b94ff 100644 --- a/TablePro/Views/Settings/SyncSettingsView.swift +++ b/TablePro/Views/Settings/SyncSettingsView.swift @@ -151,7 +151,7 @@ struct SyncSettingsView: View { .controlSize(.small) } .padding(12) - .background(.ultraThinMaterial, in: RoundedRectangle(cornerRadius: 8)) + .background(.ultraThinMaterial, in: RoundedRectangle(cornerRadius: ThemeEngine.shared.activeTheme.cornerRadius.large)) .padding() Spacer() diff --git a/TablePro/Views/Settings/ThemePreviewCard.swift b/TablePro/Views/Settings/ThemePreviewCard.swift index 30cad10e7..f65ec7395 100644 --- a/TablePro/Views/Settings/ThemePreviewCard.swift +++ b/TablePro/Views/Settings/ThemePreviewCard.swift @@ -63,9 +63,9 @@ struct ThemePreviewCard: View { private var compactCard: some View { thumbnail .frame(width: 72, height: 45) - .clipShape(RoundedRectangle(cornerRadius: 4)) + .clipShape(RoundedRectangle(cornerRadius: ThemeEngine.shared.activeTheme.cornerRadius.small)) .overlay( - RoundedRectangle(cornerRadius: 4) + RoundedRectangle(cornerRadius: ThemeEngine.shared.activeTheme.cornerRadius.small) .strokeBorder(isActive ? Color.accentColor : Color.clear, lineWidth: 0.5) ) } diff --git a/TablePro/Views/Sidebar/FavoriteEditDialog.swift b/TablePro/Views/Sidebar/FavoriteEditDialog.swift index b7229f2d9..fa2138258 100644 --- a/TablePro/Views/Sidebar/FavoriteEditDialog.swift +++ b/TablePro/Views/Sidebar/FavoriteEditDialog.swift @@ -77,9 +77,9 @@ internal struct FavoriteEditDialog: View { .scrollContentBackground(.hidden) .padding(4) .background(Color(nsColor: .textBackgroundColor)) - .clipShape(RoundedRectangle(cornerRadius: 4)) + .clipShape(RoundedRectangle(cornerRadius: ThemeEngine.shared.activeTheme.cornerRadius.small)) .overlay( - RoundedRectangle(cornerRadius: 4) + RoundedRectangle(cornerRadius: ThemeEngine.shared.activeTheme.cornerRadius.small) .stroke(Color(nsColor: .separatorColor), lineWidth: 0.5) ) } diff --git a/TablePro/Views/Sidebar/FavoriteRowView.swift b/TablePro/Views/Sidebar/FavoriteRowView.swift index f12f9d3be..e3ac4f5c2 100644 --- a/TablePro/Views/Sidebar/FavoriteRowView.swift +++ b/TablePro/Views/Sidebar/FavoriteRowView.swift @@ -23,7 +23,7 @@ internal struct FavoriteRowView: View { if let keyword = favorite.keyword, !keyword.isEmpty { Text(keyword) - .font(.system(size: 10, weight: .medium, design: .monospaced)) + .font(.system(size: ThemeEngine.shared.activeTheme.typography.caption, weight: .medium, design: .monospaced)) .foregroundStyle(.secondary) .padding(.horizontal, 5) .padding(.vertical, 1) diff --git a/TablePro/Views/Sidebar/FavoritesTabView.swift b/TablePro/Views/Sidebar/FavoritesTabView.swift index 78880a0a4..ed3bb0fba 100644 --- a/TablePro/Views/Sidebar/FavoritesTabView.swift +++ b/TablePro/Views/Sidebar/FavoritesTabView.swift @@ -211,11 +211,11 @@ internal struct FavoritesTabView: View { .foregroundStyle(Color(nsColor: .tertiaryLabelColor)) Text("No Favorites") - .font(.system(size: 13, weight: .medium)) + .font(.system(size: ThemeEngine.shared.activeTheme.typography.body, weight: .medium)) .foregroundStyle(Color(nsColor: .secondaryLabelColor)) Text("Save frequently used queries\nfor quick access.") - .font(.system(size: 11)) + .font(.system(size: ThemeEngine.shared.activeTheme.typography.small)) .foregroundStyle(Color(nsColor: .tertiaryLabelColor)) .multilineTextAlignment(.center) @@ -238,7 +238,7 @@ internal struct FavoritesTabView: View { .foregroundStyle(Color(nsColor: .tertiaryLabelColor)) Text("No Matching Favorites") - .font(.system(size: 13, weight: .medium)) + .font(.system(size: ThemeEngine.shared.activeTheme.typography.body, weight: .medium)) .foregroundStyle(Color(nsColor: .secondaryLabelColor)) } .frame(maxWidth: .infinity, maxHeight: .infinity) diff --git a/TablePro/Views/Sidebar/SidebarView.swift b/TablePro/Views/Sidebar/SidebarView.swift index 8b301837e..52cacce88 100644 --- a/TablePro/Views/Sidebar/SidebarView.swift +++ b/TablePro/Views/Sidebar/SidebarView.swift @@ -191,11 +191,11 @@ struct SidebarView: View { .foregroundStyle(Color(nsColor: .tertiaryLabelColor)) Text(noItemsLabel) - .font(.system(size: 13, weight: .medium)) + .font(.system(size: ThemeEngine.shared.activeTheme.typography.body, weight: .medium)) .foregroundStyle(Color(nsColor: .secondaryLabelColor)) Text(noItemsDetail) - .font(.system(size: 11)) + .font(.system(size: ThemeEngine.shared.activeTheme.typography.small)) .foregroundStyle(Color(nsColor: .tertiaryLabelColor)) } .frame(maxWidth: .infinity, maxHeight: .infinity) diff --git a/TablePro/Views/Sidebar/TableRowView.swift b/TablePro/Views/Sidebar/TableRowView.swift index 72607180a..5a7787180 100644 --- a/TablePro/Views/Sidebar/TableRowView.swift +++ b/TablePro/Views/Sidebar/TableRowView.swift @@ -22,14 +22,14 @@ enum TableRowLogic { } static func iconColor(table: TableInfo, isPendingDelete: Bool, isPendingTruncate: Bool) -> Color { - if isPendingDelete { return .red } - if isPendingTruncate { return .orange } - return table.type == .view ? .purple : .blue + if isPendingDelete { return Color(nsColor: .systemRed) } + if isPendingTruncate { return Color(nsColor: .systemOrange) } + return table.type == .view ? Color(nsColor: .systemPurple) : Color(nsColor: .systemBlue) } static func textColor(isPendingDelete: Bool, isPendingTruncate: Bool) -> Color { - if isPendingDelete { return .red } - if isPendingTruncate { return .orange } + if isPendingDelete { return Color(nsColor: .systemRed) } + if isPendingTruncate { return Color(nsColor: .systemOrange) } return .primary } } @@ -53,12 +53,12 @@ struct TableRow: View { if isPendingDelete { Image(systemName: "minus.circle.fill") .font(.system(size: ThemeEngine.shared.activeTheme.typography.caption)) - .foregroundStyle(.red) + .foregroundStyle(Color(nsColor: .systemRed)) .offset(x: 4, y: 4) } else if isPendingTruncate { Image(systemName: "exclamationmark.circle.fill") .font(.system(size: ThemeEngine.shared.activeTheme.typography.caption)) - .foregroundStyle(.orange) + .foregroundStyle(Color(nsColor: .systemOrange)) .offset(x: 4, y: 4) } } diff --git a/TablePro/Views/Structure/ClickHousePartsView.swift b/TablePro/Views/Structure/ClickHousePartsView.swift index 1eeb8c8fc..1b13b17bb 100644 --- a/TablePro/Views/Structure/ClickHousePartsView.swift +++ b/TablePro/Views/Structure/ClickHousePartsView.swift @@ -27,7 +27,7 @@ struct ClickHousePartsView: View { VStack(spacing: 8) { Image(systemName: "exclamationmark.triangle") .font(.largeTitle) - .foregroundStyle(.orange) + .foregroundStyle(Color(nsColor: .systemOrange)) Text(error) .foregroundStyle(.secondary) } @@ -68,7 +68,7 @@ struct ClickHousePartsView: View { .width(min: 100, ideal: 160) TableColumn("Active") { part in Image(systemName: part.active ? "checkmark.circle.fill" : "xmark.circle") - .foregroundStyle(part.active ? .green : .secondary) + .foregroundStyle(part.active ? Color(nsColor: .systemGreen) : .secondary) } .width(min: 50, ideal: 60) } diff --git a/TablePro/Views/Structure/SchemaPreviewSheet.swift b/TablePro/Views/Structure/SchemaPreviewSheet.swift index 6c4a62d75..98e1021ec 100644 --- a/TablePro/Views/Structure/SchemaPreviewSheet.swift +++ b/TablePro/Views/Structure/SchemaPreviewSheet.swift @@ -54,7 +54,7 @@ struct SchemaPreviewSheet: View { // Footer HStack { Toggle("Don't show this again", isOn: $skipPreview) - .help("You can re-enable this in Settings") + .help(String(localized: "You can re-enable this in Settings")) Spacer() @@ -122,7 +122,7 @@ struct SchemaPreviewSheet: View { .font(.caption) } .buttonStyle(.borderless) - .help("Copy this statement to clipboard") + .help(String(localized: "Copy this statement to clipboard")) } } diff --git a/TablePro/Views/Structure/TableStructureView+Schema.swift b/TablePro/Views/Structure/TableStructureView+Schema.swift index ef48879fe..04f9ebdab 100644 --- a/TablePro/Views/Structure/TableStructureView+Schema.swift +++ b/TablePro/Views/Structure/TableStructureView+Schema.swift @@ -121,6 +121,7 @@ extension TableStructureView { Image(systemName: "textformat.size.smaller") .frame(width: 24, height: 24) } + .accessibilityLabel(String(localized: "Decrease font size")) Text("\(Int(ddlFontSize))") .font(.caption) .foregroundStyle(.secondary) @@ -129,6 +130,7 @@ extension TableStructureView { Image(systemName: "textformat.size.larger") .frame(width: 24, height: 24) } + .accessibilityLabel(String(localized: "Increase font size")) } .buttonStyle(.borderless) @@ -137,7 +139,7 @@ extension TableStructureView { if showCopyConfirmation { HStack { Image(systemName: "checkmark.circle.fill") - .foregroundStyle(.green) + .foregroundStyle(Color(nsColor: .systemGreen)) Text("Copied!") } .transition(.opacity) diff --git a/TablePro/Views/Structure/TableStructureView.swift b/TablePro/Views/Structure/TableStructureView.swift index 14e000ff6..0639980af 100644 --- a/TablePro/Views/Structure/TableStructureView.swift +++ b/TablePro/Views/Structure/TableStructureView.swift @@ -235,7 +235,7 @@ struct TableStructureView: View { VStack(spacing: 8) { Image(systemName: "exclamationmark.triangle") .font(.largeTitle) - .foregroundStyle(.orange) + .foregroundStyle(Color(nsColor: .systemOrange)) Text(message) .foregroundStyle(.secondary) } diff --git a/TablePro/Views/Toolbar/ConnectionSwitcherPopover.swift b/TablePro/Views/Toolbar/ConnectionSwitcherPopover.swift index a14a6c102..ccaccd5bf 100644 --- a/TablePro/Views/Toolbar/ConnectionSwitcherPopover.swift +++ b/TablePro/Views/Toolbar/ConnectionSwitcherPopover.swift @@ -66,7 +66,7 @@ struct ConnectionSwitcherPopover: View { } .buttonStyle(.plain) .listRowBackground( - RoundedRectangle(cornerRadius: 4) + RoundedRectangle(cornerRadius: ThemeEngine.shared.activeTheme.cornerRadius.small) .fill( index == selectedIndex ? Color(nsColor: .selectedContentBackgroundColor) @@ -100,7 +100,7 @@ struct ConnectionSwitcherPopover: View { } .buttonStyle(.plain) .listRowBackground( - RoundedRectangle(cornerRadius: 4) + RoundedRectangle(cornerRadius: ThemeEngine.shared.activeTheme.cornerRadius.small) .fill( itemIndex == selectedIndex ? Color(nsColor: .selectedContentBackgroundColor) diff --git a/TablePro/Views/Toolbar/ExecutionIndicatorView.swift b/TablePro/Views/Toolbar/ExecutionIndicatorView.swift index 8e010ca92..29b48c355 100644 --- a/TablePro/Views/Toolbar/ExecutionIndicatorView.swift +++ b/TablePro/Views/Toolbar/ExecutionIndicatorView.swift @@ -35,7 +35,7 @@ struct ExecutionIndicatorView: View { .font(.system(size: ThemeEngine.shared.activeTheme.typography.small, weight: .regular, design: .monospaced)) .foregroundStyle(ThemeEngine.shared.colors.toolbar.tertiaryTextSwiftUI) .accessibilityLabel(String(format: String(localized: "Last query: %@"), chProgress.formattedSummary)) - .help("Last query execution summary") + .help(String(localized: "Last query execution summary")) } else if let duration = lastDuration { Text(formattedDuration(duration)) .font(.system(size: ThemeEngine.shared.activeTheme.typography.small, weight: .regular, design: .monospaced)) @@ -43,13 +43,13 @@ struct ExecutionIndicatorView: View { .accessibilityLabel( String(format: String(localized: "Last query took %@"), formattedDuration(duration)) ) - .help("Last query execution time") + .help(String(localized: "Last query execution time")) } else { Text("--") .font(.system(size: ThemeEngine.shared.activeTheme.typography.small, weight: .regular, design: .monospaced)) .foregroundStyle(.quaternary) .accessibilityLabel(String(localized: "No query executed yet")) - .help("Run a query to see execution time") + .help(String(localized: "Run a query to see execution time")) } } .padding(.trailing, ThemeEngine.shared.activeTheme.spacing.xs) diff --git a/TablePro/Views/Toolbar/SafeModeBadgeView.swift b/TablePro/Views/Toolbar/SafeModeBadgeView.swift index 969a7f023..4692a30ec 100644 --- a/TablePro/Views/Toolbar/SafeModeBadgeView.swift +++ b/TablePro/Views/Toolbar/SafeModeBadgeView.swift @@ -25,6 +25,7 @@ struct SafeModeBadgeView: View { } .buttonStyle(.plain) .help(String(format: String(localized: "Safe Mode: %@"), safeModeLevel.displayName)) + .accessibilityLabel(String(format: String(localized: "Safe Mode: %@"), safeModeLevel.displayName)) .popover(isPresented: $showPopover) { VStack(alignment: .leading, spacing: 8) { Text("Safe Mode") @@ -47,6 +48,7 @@ struct SafeModeBadgeView: View { } .padding() .frame(width: 220) + .onExitCommand { showPopover = false } } .onChange(of: safeModeLevel) { oldValue, newValue in if newValue.requiresPro && !isProUnlocked { diff --git a/TablePro/Views/Toolbar/TableProToolbarView.swift b/TablePro/Views/Toolbar/TableProToolbarView.swift index b150a00d8..7e17794f4 100644 --- a/TablePro/Views/Toolbar/TableProToolbarView.swift +++ b/TablePro/Views/Toolbar/TableProToolbarView.swift @@ -68,7 +68,7 @@ struct TableProToolbar: ViewModifier { } label: { Label("Connection", systemImage: "network") } - .help("Switch Connection (⌘⌥C)") + .help(String(localized: "Switch Connection (⌘⌥C)")) .popover(isPresented: $showConnectionSwitcher) { ConnectionSwitcherPopover { showConnectionSwitcher = false @@ -83,7 +83,7 @@ struct TableProToolbar: ViewModifier { } label: { Label("Database", systemImage: "cylinder") } - .help("Open Database (⌘K)") + .help(String(localized: "Open Database (⌘K)")) .disabled( state.connectionState != .connected || PluginManager.shared.connectionMode(for: state.databaseType) == .fileBased) @@ -96,7 +96,7 @@ struct TableProToolbar: ViewModifier { } label: { Label("Refresh", systemImage: "arrow.clockwise") } - .help("Refresh (⌘R)") + .help(String(localized: "Refresh (⌘R)")) .disabled(state.connectionState != .connected) Button { @@ -104,7 +104,7 @@ struct TableProToolbar: ViewModifier { } label: { Label("Save Changes", systemImage: "checkmark.circle.fill") } - .help("Save Changes (⌘S)") + .help(String(localized: "Save Changes (⌘S)")) .disabled(!state.hasPendingChanges || state.connectionState != .connected) .tint(.accentColor) } @@ -123,7 +123,7 @@ struct TableProToolbar: ViewModifier { } label: { Label("Quick Switcher", systemImage: "magnifyingglass") } - .help("Quick Switcher (⌘P)") + .help(String(localized: "Quick Switcher (⌘P)")) .disabled(state.connectionState != .connected) Button { @@ -131,7 +131,7 @@ struct TableProToolbar: ViewModifier { } label: { Label("New Tab", systemImage: "plus.rectangle") } - .help("New Query Tab (⌘T)") + .help(String(localized: "New Query Tab (⌘T)")) } ToolbarItem(placement: .primaryAction) { @@ -140,7 +140,7 @@ struct TableProToolbar: ViewModifier { } label: { Label("Filters", systemImage: "line.3.horizontal.decrease.circle") } - .help("Toggle Filters (⌘F)") + .help(String(localized: "Toggle Filters (⌘F)")) .disabled(state.connectionState != .connected || !state.isTableTab) } @@ -151,7 +151,7 @@ struct TableProToolbar: ViewModifier { let langName = PluginManager.shared.queryLanguageName(for: state.databaseType) Label("Preview \(langName)", systemImage: "eye") } - .help("Preview \(PluginManager.shared.queryLanguageName(for: state.databaseType)) (⌘⇧P)") + .help(String(format: String(localized: "Preview %@ (⌘⇧P)"), PluginManager.shared.queryLanguageName(for: state.databaseType))) .disabled(!state.hasDataPendingChanges || state.connectionState != .connected) .popover(isPresented: $state.showSQLReviewPopover) { SQLReviewPopover(statements: state.previewStatements, databaseType: state.databaseType) @@ -179,7 +179,7 @@ struct TableProToolbar: ViewModifier { } label: { Label("Inspector", systemImage: "sidebar.trailing") } - .help("Toggle Inspector (⌘⌥B)") + .help(String(localized: "Toggle Inspector (⌘⌥B)")) } // MARK: - Secondary Action (Overflow) @@ -190,14 +190,14 @@ struct TableProToolbar: ViewModifier { } label: { Label("History", systemImage: "clock") } - .help("Toggle Query History (⌘Y)") + .help(String(localized: "Toggle Query History (⌘Y)")) Button { actions?.exportTables() } label: { Label("Export", systemImage: "square.and.arrow.up") } - .help("Export Data (⌘⇧E)") + .help(String(localized: "Export Data (⌘⇧E)")) .disabled(state.connectionState != .connected) if PluginManager.shared.supportsImport(for: state.databaseType) { @@ -206,7 +206,7 @@ struct TableProToolbar: ViewModifier { } label: { Label("Import", systemImage: "square.and.arrow.down") } - .help("Import Data (⌘⇧I)") + .help(String(localized: "Import Data (⌘⇧I)")) .disabled(state.connectionState != .connected || state.safeModeLevel.blocksAllWrites) } } diff --git a/TablePro/Views/Toolbar/TagBadgeView.swift b/TablePro/Views/Toolbar/TagBadgeView.swift index 2ad1b0754..c1c67ba8f 100644 --- a/TablePro/Views/Toolbar/TagBadgeView.swift +++ b/TablePro/Views/Toolbar/TagBadgeView.swift @@ -30,7 +30,7 @@ struct TagBadgeView: View { .fill(tag.color.color.opacity(0.2)) ) .padding(.leading, ThemeEngine.shared.activeTheme.spacing.xs) - .help("Tag: \(tag.name)") + .help(String(format: String(localized: "Tag: %@"), tag.name)) .accessibilityLabel("Tag: \(tag.name)") } }