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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions TablePro/Models/Connection/SafeModeLevel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
}
30 changes: 9 additions & 21 deletions TablePro/Views/AIChat/AIChatCodeBlockView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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()
Expand Down Expand Up @@ -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 {
Expand All @@ -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 {
Expand Down Expand Up @@ -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)
)
}
Expand Down
2 changes: 1 addition & 1 deletion TablePro/Views/AIChat/AIChatMessageView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
7 changes: 4 additions & 3 deletions TablePro/Views/AIChat/AIChatPanelView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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)
Expand Down Expand Up @@ -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"))
Expand Down
6 changes: 3 additions & 3 deletions TablePro/Views/Components/PaginationControlsView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down Expand Up @@ -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"
Expand All @@ -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)
}
}
Expand Down
2 changes: 1 addition & 1 deletion TablePro/Views/Components/SearchFieldView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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))
}
}
8 changes: 4 additions & 4 deletions TablePro/Views/Connection/ConnectionExportOptionsSheet.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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)
Expand All @@ -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)
Expand All @@ -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)
}
}
Expand Down
4 changes: 2 additions & 2 deletions TablePro/Views/Connection/ConnectionFormView+Footer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -106,7 +106,7 @@ extension ConnectionFormView {
if let urlParseError {
Text(urlParseError)
.font(.caption)
.foregroundStyle(.red)
.foregroundStyle(Color(nsColor: .systemRed))
}

HStack {
Expand Down
8 changes: 4 additions & 4 deletions TablePro/Views/Connection/ConnectionFormView+Helpers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
Expand Down
22 changes: 11 additions & 11 deletions TablePro/Views/Connection/ConnectionImportSheet.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -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)
Expand All @@ -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)
}
Expand Down Expand Up @@ -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()
Expand All @@ -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)

Expand All @@ -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)
}

Expand All @@ -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()
Expand Down
7 changes: 5 additions & 2 deletions TablePro/Views/Connection/ConnectionSSHTunnelView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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") {
Expand Down Expand Up @@ -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"))
}
}
}
Expand Down
6 changes: 3 additions & 3 deletions TablePro/Views/Connection/ConnectionSidebarHeader.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion TablePro/Views/Connection/OnboardingContentView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
Loading
Loading