From a0fb38eb6bea9c9989e8bd4edfbcd18eaed3c5ed Mon Sep 17 00:00:00 2001 From: Ngo Quoc Dat Date: Fri, 1 May 2026 19:23:25 +0700 Subject: [PATCH 1/2] refactor: align keyboard shortcuts with macOS HIG --- CHANGELOG.md | 11 +++ .../TextViewController+FindPanelTarget.swift | 12 +++ .../Infrastructure/MainWindowToolbar.swift | 2 +- .../Models/UI/KeyboardShortcutModels.swift | 52 ++++++++---- TablePro/TableProApp.swift | 82 ++++++++++++++++--- TablePro/Views/Editor/EditorEventRouter.swift | 20 +++++ .../Views/Editor/SQLEditorCoordinator.swift | 25 ++++++ .../Main/MainContentCommandActions.swift | 24 ++++++ .../Views/Toolbar/TableProToolbarView.swift | 2 +- docs/features/keyboard-shortcuts.mdx | 20 +++-- 10 files changed, 210 insertions(+), 40 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 64f6307c3..c0cf63864 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,9 +22,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Copy rows writes TSV, HTML table, and plain text to the clipboard for richer paste in spreadsheet apps - Row drag adds TSV and HTML representations alongside the internal drag type - AI provider settings allow manually entering a model name when the provider does not return one +- Edit menu has a Find submenu with Find, Find Next (`Cmd+G`), Find Previous (`Cmd+Shift+G`), Use Selection for Find (`Cmd+E`), and Jump to Selection (`Cmd+J`) +- File menu has a New Window item (`Cmd+Ctrl+N`) that opens a fresh editor window for the active connection ### Changed +- `Cmd+N` now opens the New Connection form. Manage Connections moves to the File menu without a default shortcut. +- `Cmd+D` now duplicates the selected row. Save as Favorite moves to `Cmd+Shift+D`. +- Removed default shortcuts that conflicted with system reservations: `Cmd+Y` (Quick Look), `Cmd+Option+Delete` (Empty Trash), `Cmd+Ctrl+C` (Color Picker), `Cmd+L` (URL bar / Add Link). +- Show History moves from `Cmd+Y` to `Cmd+Option+H` to mirror the other panel toggles. +- Truncate Table no longer has a default shortcut; the action stays in the Edit menu and the sidebar context menu. +- Switch Connection no longer has a default shortcut; the menu entry remains and users can rebind in Settings > Keyboard. +- Explain with AI no longer has a default shortcut; the menu entry remains and users can rebind in Settings > Keyboard. +- File menu "Save Changes" renamed to "Save". +- View menu Show/Hide labels now flip based on panel state (Show Sidebar / Hide Sidebar, Show Inspector / Hide Inspector, Show Filters / Hide Filters, Show History / Hide History, Show Results / Hide Results). - Storage and sync singletons accept dependencies via init for test isolation, matching Apple's URLSession and UserDefaults convention. Production callers using `.shared` are unchanged. `SQLFavoriteStorage` is now an actor so its first access no longer blocks the main thread on SQLite setup. - Create Database dialog is now driver-driven. Each driver discovers its own valid options (PostgreSQL queries `pg_collation` and `pg_database`, MySQL/MariaDB query `information_schema.character_sets`/`collations`). The hardcoded macOS-flavored locale list is gone. Engines that don't support creation hide the Create button instead of failing on click. - Introduced TableRows, Row, and Delta value types in TablePro/Models/Query/ as the foundation for the data grid row model rewrite. No callers migrated yet (Phase C.1 of the DataGrid refactor). diff --git a/LocalPackages/CodeEditSourceEditor/Sources/CodeEditSourceEditor/Controller/TextViewController+FindPanelTarget.swift b/LocalPackages/CodeEditSourceEditor/Sources/CodeEditSourceEditor/Controller/TextViewController+FindPanelTarget.swift index 9631b3bf7..467c46f3b 100644 --- a/LocalPackages/CodeEditSourceEditor/Sources/CodeEditSourceEditor/Controller/TextViewController+FindPanelTarget.swift +++ b/LocalPackages/CodeEditSourceEditor/Sources/CodeEditSourceEditor/Controller/TextViewController+FindPanelTarget.swift @@ -35,4 +35,16 @@ public extension TextViewController { _ = textView.resignFirstResponder() findViewController?.showFindPanel() } + + func findNextMatch() { + findViewController?.viewModel.moveToNextMatch() + } + + func findPreviousMatch() { + findViewController?.viewModel.moveToPreviousMatch() + } + + func setFindText(_ text: String) { + findViewController?.viewModel.findText = text + } } diff --git a/TablePro/Core/Services/Infrastructure/MainWindowToolbar.swift b/TablePro/Core/Services/Infrastructure/MainWindowToolbar.swift index 38efcee31..1d3bfbdc6 100644 --- a/TablePro/Core/Services/Infrastructure/MainWindowToolbar.swift +++ b/TablePro/Core/Services/Infrastructure/MainWindowToolbar.swift @@ -457,7 +457,7 @@ private struct HistoryToolbarButton: View { } label: { Label("History", systemImage: "clock") } - .help(String(localized: "Toggle Query History (⌘Y)")) + .help(String(localized: "Toggle Query History (⌥⌘H)")) } } diff --git a/TablePro/Models/UI/KeyboardShortcutModels.swift b/TablePro/Models/UI/KeyboardShortcutModels.swift index caa67dc57..077aed1e1 100644 --- a/TablePro/Models/UI/KeyboardShortcutModels.swift +++ b/TablePro/Models/UI/KeyboardShortcutModels.swift @@ -36,6 +36,8 @@ enum ShortcutCategory: String, Codable, CaseIterable, Identifiable { /// All customizable keyboard shortcut actions enum ShortcutAction: String, Codable, CaseIterable, Identifiable { // File + case newConnection + case newWindow case manageConnections case newTab case openDatabase @@ -70,6 +72,11 @@ enum ShortcutAction: String, Codable, CaseIterable, Identifiable { case delete case selectAll case clearSelection + case find + case findNext + case findPrevious + case useSelectionForFind + case jumpToSelection case addRow case duplicateRow case truncateTable @@ -98,14 +105,15 @@ enum ShortcutAction: String, Codable, CaseIterable, Identifiable { var category: ShortcutCategory { switch self { - case .manageConnections, .newTab, .openDatabase, .openFile, .switchConnection, - .saveChanges, .saveAs, .previewSQL, .closeTab, .refresh, + case .newConnection, .newWindow, .manageConnections, .newTab, .openDatabase, .openFile, + .switchConnection, .saveChanges, .saveAs, .previewSQL, .closeTab, .refresh, .executeQuery, .explainQuery, .formatQuery, .export, .importData, .quickSwitcher, .previousPage, .nextPage, .saveAsFavorite, .openTerminal: return .file case .undo, .redo, .cut, .copy, .copyWithHeaders, .copyAsJson, .paste, - .delete, .selectAll, .clearSelection, .addRow, - .duplicateRow, .truncateTable, .previewFKReference: + .delete, .selectAll, .clearSelection, + .find, .findNext, .findPrevious, .useSelectionForFind, .jumpToSelection, + .addRow, .duplicateRow, .truncateTable, .previewFKReference: return .edit case .toggleTableBrowser, .toggleInspector, .toggleFilters, .toggleHistory, .toggleResults, .previousResultTab, .nextResultTab, .closeResultTab: @@ -119,13 +127,15 @@ enum ShortcutAction: String, Codable, CaseIterable, Identifiable { var displayName: String { switch self { + case .newConnection: return String(localized: "New Connection") + case .newWindow: return String(localized: "New Window") case .manageConnections: return String(localized: "Manage Connections") case .executeQuery: return String(localized: "Execute Query") case .newTab: return String(localized: "New Tab") case .openDatabase: return String(localized: "Open Database") case .openFile: return String(localized: "Open File") case .switchConnection: return String(localized: "Switch Connection") - case .saveChanges: return String(localized: "Save Changes") + case .saveChanges: return String(localized: "Save") case .saveAs: return String(localized: "Save As") case .previewSQL: return String(localized: "Preview SQL") case .closeTab: return String(localized: "Close Tab") @@ -148,16 +158,21 @@ enum ShortcutAction: String, Codable, CaseIterable, Identifiable { case .delete: return String(localized: "Delete") case .selectAll: return String(localized: "Select All") case .clearSelection: return String(localized: "Clear Selection") + case .find: return String(localized: "Find") + case .findNext: return String(localized: "Find Next") + case .findPrevious: return String(localized: "Find Previous") + case .useSelectionForFind: return String(localized: "Use Selection for Find") + case .jumpToSelection: return String(localized: "Jump to Selection") case .addRow: return String(localized: "Add Row") case .duplicateRow: return String(localized: "Duplicate Row") case .truncateTable: return String(localized: "Truncate Table") case .previewFKReference: return String(localized: "Preview FK Reference") case .saveAsFavorite: return String(localized: "Save as Favorite") - case .toggleTableBrowser: return String(localized: "Toggle Table Browser") - case .toggleInspector: return String(localized: "Toggle Inspector") - case .toggleFilters: return String(localized: "Toggle Filters") - case .toggleHistory: return String(localized: "Toggle History") - case .toggleResults: return String(localized: "Toggle Results") + case .toggleTableBrowser: return String(localized: "Show Sidebar") + case .toggleInspector: return String(localized: "Show Inspector") + case .toggleFilters: return String(localized: "Show Filters") + case .toggleHistory: return String(localized: "Show History") + case .toggleResults: return String(localized: "Show Results") case .previousResultTab: return String(localized: "Previous Result") case .nextResultTab: return String(localized: "Next Result") case .closeResultTab: return String(localized: "Close Result Tab") @@ -457,12 +472,12 @@ struct KeyboardSettings: Codable, Equatable { /// Default shortcuts — applied when user has no overrides static let defaultShortcuts: [ShortcutAction: KeyCombo] = [ // File - .manageConnections: KeyCombo(key: "n", command: true), + .newConnection: KeyCombo(key: "n", command: true), + .newWindow: KeyCombo(key: "n", command: true, control: true), .executeQuery: KeyCombo(key: "return", command: true, isSpecialKey: true), .newTab: KeyCombo(key: "t", command: true), .openDatabase: KeyCombo(key: "k", command: true), .openFile: KeyCombo(key: "o", command: true), - .switchConnection: KeyCombo(key: "c", command: true, control: true), .saveChanges: KeyCombo(key: "s", command: true), .saveAs: KeyCombo(key: "s", command: true, shift: true), .previewSQL: KeyCombo(key: "p", command: true, shift: true), @@ -488,17 +503,21 @@ struct KeyboardSettings: Codable, Equatable { .delete: KeyCombo(key: "delete", command: true, isSpecialKey: true), .selectAll: KeyCombo(key: "a", command: true), .clearSelection: KeyCombo(key: "escape", isSpecialKey: true), + .find: KeyCombo(key: "f", command: true), + .findNext: KeyCombo(key: "g", command: true), + .findPrevious: KeyCombo(key: "g", command: true, shift: true), + .useSelectionForFind: KeyCombo(key: "e", command: true), + .jumpToSelection: KeyCombo(key: "j", command: true), .addRow: KeyCombo(key: "n", command: true, shift: true), - .duplicateRow: KeyCombo(key: "d", command: true, shift: true), - .truncateTable: KeyCombo(key: "delete", option: true, isSpecialKey: true), + .duplicateRow: KeyCombo(key: "d", command: true), .previewFKReference: KeyCombo(key: "space", isSpecialKey: true), - .saveAsFavorite: KeyCombo(key: "d", command: true), + .saveAsFavorite: KeyCombo(key: "d", command: true, shift: true), // View .toggleTableBrowser: KeyCombo(key: "0", command: true), .toggleInspector: KeyCombo(key: "i", command: true, option: true), .toggleFilters: KeyCombo(key: "f", command: true, shift: true), - .toggleHistory: KeyCombo(key: "y", command: true), + .toggleHistory: KeyCombo(key: "h", command: true, option: true), .toggleResults: KeyCombo(key: "r", command: true, option: true), .previousResultTab: KeyCombo(key: "[", command: true, option: true), .nextResultTab: KeyCombo(key: "]", command: true, option: true), @@ -509,7 +528,6 @@ struct KeyboardSettings: Codable, Equatable { .showNextTab: KeyCombo(key: "]", command: true, shift: true), // AI - .aiExplainQuery: KeyCombo(key: "l", command: true), .aiOptimizeQuery: KeyCombo(key: "l", command: true, option: true), ] } diff --git a/TablePro/TableProApp.swift b/TablePro/TableProApp.swift index b5d18fdf0..9b9a672cd 100644 --- a/TablePro/TableProApp.swift +++ b/TablePro/TableProApp.swift @@ -122,6 +122,18 @@ struct AppMenuCommands: Commands { settingsManager.keyboard.keyboardShortcut(for: action) } + private func openNewMainWindow() { + let connectionId = actions?.connectionId + ?? NSApp.keyWindow.flatMap { WindowLifecycleMonitor.shared.connectionId(forWindow: $0) } + guard let connectionId else { + WelcomeWindowFactory.openOrFront() + return + } + WindowManager.shared.openTab( + payload: EditorTabPayload(connectionId: connectionId, intent: .newEmptyTab) + ) + } + /// Prefers the focused scene value; falls back to the coordinator back-reference /// so Cmd+W still routes through `closeTab()` (with its unsaved-changes dialog) /// when focus is inside an AppKit subview and `@FocusedValue` has not resolved. @@ -195,10 +207,15 @@ struct AppMenuCommands: Commands { // File menu CommandGroup(replacing: .newItem) { - Button("Manage Connections") { - NotificationCenter.default.post(name: .newConnection, object: nil) + Button(String(localized: "New Connection...")) { + WindowOpener.shared.openWindow?(id: "connection-form") } - .optionalKeyboardShortcut(shortcut(for: .manageConnections)) + .optionalKeyboardShortcut(shortcut(for: .newConnection)) + + Button(String(localized: "New Window")) { + openNewMainWindow() + } + .optionalKeyboardShortcut(shortcut(for: .newWindow)) } CommandGroup(after: .newItem) { @@ -213,6 +230,11 @@ struct AppMenuCommands: Commands { } .disabled(!(actions?.isConnected ?? false) || actions?.isReadOnly ?? false) + Button(String(localized: "Manage Connections...")) { + NotificationCenter.default.post(name: .openWelcomeWindow, object: nil) + } + .optionalKeyboardShortcut(shortcut(for: .manageConnections)) + Button("Open Database...") { actions?.openDatabaseSwitcher() } @@ -227,7 +249,7 @@ struct AppMenuCommands: Commands { Divider() - Button("Save Changes") { + Button(String(localized: "Save")) { actions?.saveChanges() } .optionalKeyboardShortcut(shortcut(for: .saveChanges)) @@ -423,14 +445,38 @@ struct AppMenuCommands: Commands { // Edit menu - pasteboard commands with FocusedValue support PasteboardCommands(settingsManager: settingsManager) - // Edit menu - Find + row operations (after pasteboard) + // Edit menu - Find submenu + row operations (after pasteboard) CommandGroup(after: .pasteboard) { Divider() - Button(String(localized: "Find...")) { - EditorEventRouter.shared.showFindPanelForKeyWindow() + Menu(String(localized: "Find")) { + Button(String(localized: "Find...")) { + EditorEventRouter.shared.showFindPanelForKeyWindow() + } + .optionalKeyboardShortcut(shortcut(for: .find)) + + Button(String(localized: "Find Next")) { + EditorEventRouter.shared.findNextForKeyWindow() + } + .optionalKeyboardShortcut(shortcut(for: .findNext)) + + Button(String(localized: "Find Previous")) { + EditorEventRouter.shared.findPreviousForKeyWindow() + } + .optionalKeyboardShortcut(shortcut(for: .findPrevious)) + + Divider() + + Button(String(localized: "Use Selection for Find")) { + EditorEventRouter.shared.useSelectionForFindForKeyWindow() + } + .optionalKeyboardShortcut(shortcut(for: .useSelectionForFind)) + + Button(String(localized: "Jump to Selection")) { + EditorEventRouter.shared.jumpToSelectionForKeyWindow() + } + .optionalKeyboardShortcut(shortcut(for: .jumpToSelection)) } - .keyboardShortcut("f", modifiers: .command) Divider() @@ -458,12 +504,16 @@ struct AppMenuCommands: Commands { // View menu CommandGroup(after: .sidebar) { - Button(String(localized: "Toggle Sidebar")) { + Button(actions?.isSidebarVisible == true + ? String(localized: "Hide Sidebar") + : String(localized: "Show Sidebar")) { NSApp.sendAction(#selector(NSSplitViewController.toggleSidebar(_:)), to: nil, from: nil) } .optionalKeyboardShortcut(shortcut(for: .toggleTableBrowser)) - Button("Toggle Inspector") { + Button(actions?.isInspectorVisible == true + ? String(localized: "Hide Inspector") + : String(localized: "Show Inspector")) { actions?.toggleRightSidebar() } .optionalKeyboardShortcut(shortcut(for: .toggleInspector)) @@ -471,13 +521,17 @@ struct AppMenuCommands: Commands { Divider() - Button("Toggle Filters") { + Button(actions?.isFilterPanelVisible == true + ? String(localized: "Hide Filters") + : String(localized: "Show Filters")) { actions?.toggleFilterPanel() } .optionalKeyboardShortcut(shortcut(for: .toggleFilters)) .disabled(!(actions?.isConnected ?? false) || !(actions?.isTableTab ?? false)) - Button("Toggle History") { + Button(actions?.isHistoryPanelVisible == true + ? String(localized: "Hide History") + : String(localized: "Show History")) { actions?.toggleHistoryPanel() } .optionalKeyboardShortcut(shortcut(for: .toggleHistory)) @@ -485,7 +539,9 @@ struct AppMenuCommands: Commands { Divider() - Button("Toggle Results") { + Button(actions?.isResultsVisible == true + ? String(localized: "Hide Results") + : String(localized: "Show Results")) { actions?.toggleResults() } .optionalKeyboardShortcut(shortcut(for: .toggleResults)) diff --git a/TablePro/Views/Editor/EditorEventRouter.swift b/TablePro/Views/Editor/EditorEventRouter.swift index da81d58f0..36e80f852 100644 --- a/TablePro/Views/Editor/EditorEventRouter.swift +++ b/TablePro/Views/Editor/EditorEventRouter.swift @@ -95,6 +95,26 @@ internal final class EditorEventRouter { coordinator.showFindPanel() } + internal func findNextForKeyWindow() { + guard let (coordinator, _) = editor(for: NSApp.keyWindow) else { return } + coordinator.findNextMatch() + } + + internal func findPreviousForKeyWindow() { + guard let (coordinator, _) = editor(for: NSApp.keyWindow) else { return } + coordinator.findPreviousMatch() + } + + internal func useSelectionForFindForKeyWindow() { + guard let (coordinator, _) = editor(for: NSApp.keyWindow) else { return } + coordinator.useSelectionForFind() + } + + internal func jumpToSelectionForKeyWindow() { + guard let (coordinator, _) = editor(for: NSApp.keyWindow) else { return } + coordinator.jumpToSelection() + } + // MARK: - Lookup private func editor(for window: NSWindow?) -> (SQLEditorCoordinator, TextView)? { diff --git a/TablePro/Views/Editor/SQLEditorCoordinator.swift b/TablePro/Views/Editor/SQLEditorCoordinator.swift index 3214a2dd0..a29e7ca96 100644 --- a/TablePro/Views/Editor/SQLEditorCoordinator.swift +++ b/TablePro/Views/Editor/SQLEditorCoordinator.swift @@ -526,6 +526,31 @@ final class SQLEditorCoordinator: TextViewCoordinator, TextViewDelegate { controller?.showFindPanel() } + func findNextMatch() { + controller?.findNextMatch() + } + + func findPreviousMatch() { + controller?.findPreviousMatch() + } + + func useSelectionForFind() { + guard let controller, let textView = controller.textView else { return } + let range = textView.selectedRange() + guard range.length > 0 else { return } + let selected = (textView.string as NSString).substring(with: range) + guard !selected.isEmpty else { return } + controller.setFindText(selected) + controller.showFindPanel() + } + + func jumpToSelection() { + guard let controller, let textView = controller.textView else { return } + let range = textView.selectedRange() + guard range.location != NSNotFound else { return } + textView.scrollToRange(range) + } + // MARK: - CodeEditSourceEditor Workarounds /// Reorder FindViewController's subviews so the find panel is on top for hit testing. diff --git a/TablePro/Views/Main/MainContentCommandActions.swift b/TablePro/Views/Main/MainContentCommandActions.swift index 694bbf9d6..efa36bb8d 100644 --- a/TablePro/Views/Main/MainContentCommandActions.swift +++ b/TablePro/Views/Main/MainContentCommandActions.swift @@ -251,6 +251,8 @@ final class MainContentCommandActions { var currentDatabaseType: DatabaseType { connection.type } + var connectionId: UUID { connection.id } + var supportsDatabaseSwitching: Bool { PluginManager.shared.supportsDatabaseSwitching(for: connection.type) } @@ -292,6 +294,28 @@ final class MainContentCommandActions { coordinator?.toolbarState.hasStructureChanges ?? false } + var isSidebarVisible: Bool { + guard let collapsed = coordinator?.splitViewController?.isSidebarCollapsed else { return false } + return !collapsed + } + + var isInspectorVisible: Bool { + coordinator?.inspectorProxy?.isInspectorVisible ?? false + } + + var isFilterPanelVisible: Bool { + filterStateManager.isVisible + } + + var isHistoryPanelVisible: Bool { + coordinator?.toolbarState.isHistoryPanelVisible ?? false + } + + var isResultsVisible: Bool { + guard let collapsed = coordinator?.toolbarState.isResultsCollapsed else { return false } + return !collapsed + } + // MARK: - Unsaved Changes Check private var hasUnsavedChanges: Bool { diff --git a/TablePro/Views/Toolbar/TableProToolbarView.swift b/TablePro/Views/Toolbar/TableProToolbarView.swift index df95683d6..4d8650623 100644 --- a/TablePro/Views/Toolbar/TableProToolbarView.swift +++ b/TablePro/Views/Toolbar/TableProToolbarView.swift @@ -208,7 +208,7 @@ struct TableProToolbar: ViewModifier { } label: { Label("History", systemImage: "clock") } - .help(String(localized: "Toggle Query History (⌘Y)")) + .help(String(localized: "Toggle Query History (⌥⌘H)")) Button { actions?.exportTables() diff --git a/docs/features/keyboard-shortcuts.mdx b/docs/features/keyboard-shortcuts.mdx index fdddcbeed..40edb5c93 100644 --- a/docs/features/keyboard-shortcuts.mdx +++ b/docs/features/keyboard-shortcuts.mdx @@ -15,7 +15,8 @@ TablePro is keyboard-driven. Most actions have shortcuts, and most menu shortcut |--------|----------| | Execute query | `Cmd+Enter` | | New connection | `Cmd+N` | -| Open history | `Cmd+Y` | +| New window | `Cmd+Ctrl+N` | +| Show history | `Cmd+Option+H` | | Settings | `Cmd+,` | | Quick Switcher | `Cmd+Shift+O` | | Close window | `Cmd+W` | @@ -71,9 +72,10 @@ TablePro is keyboard-driven. Most actions have shortcuts, and most menu shortcut | Action | Shortcut | |--------|----------| | Find | `Cmd+F` | -| Find and replace | `Cmd+Option+F` | | Find next | `Cmd+G` | | Find previous | `Cmd+Shift+G` | +| Use Selection for Find | `Cmd+E` | +| Jump to Selection | `Cmd+J` | ### Selection @@ -104,7 +106,7 @@ TablePro is keyboard-driven. Most actions have shortcuts, and most menu shortcut | Preview FK reference | `Space` | | Cancel edit | `Escape` | | Add row | `Cmd+Shift+N` | -| Duplicate row | `Cmd+Shift+D` | +| Duplicate row | `Cmd+D` | | Delete row | `Delete` or `Backspace` | | Commit changes | `Cmd+S` | @@ -159,7 +161,9 @@ TablePro is keyboard-driven. Most actions have shortcuts, and most menu shortcut | Action | Shortcut | |--------|----------| | New connection | `Cmd+N` | -| Switch connection | `Cmd+Control+C` | +| New window | `Cmd+Ctrl+N` | +| Manage connections | None (menu only) | +| Switch connection | None (menu only) | | Refresh connection | `Cmd+R` | | Delete selected connections | `Cmd+Delete` | @@ -176,9 +180,9 @@ TablePro is keyboard-driven. Most actions have shortcuts, and most menu shortcut | Action | Shortcut | |--------|----------| -| Query History | `Cmd+Y` | -| Toggle Cell Inspector | `Cmd+Option+I` | -| Toggle Results | `Cmd+Option+R` | +| Show / Hide History | `Cmd+Option+H` | +| Show / Hide Inspector | `Cmd+Option+I` | +| Show / Hide Results | `Cmd+Option+R` | | Open Terminal | `Ctrl+Cmd+`` | | Settings | `Cmd+,` | @@ -194,7 +198,7 @@ TablePro is keyboard-driven. Most actions have shortcuts, and most menu shortcut | Action | Shortcut | Description | |--------|----------|-------------| -| Explain with AI | `Cmd+L` | Send current query to AI for explanation | +| Explain with AI | None (menu only) | Send current query to AI for explanation | | Optimize with AI | `Cmd+Option+L` | Send current query to AI for optimization | ## ER Diagram From 0f73af978ad3b307f3176abad6aa6fb55b19ae19 Mon Sep 17 00:00:00 2001 From: Ngo Quoc Dat Date: Fri, 1 May 2026 19:46:19 +0700 Subject: [PATCH 2/2] fix(shortcuts): rebind Show History to Cmd+Shift+Y (Cmd+Option+H is system Hide Others) --- CHANGELOG.md | 2 +- TablePro/Core/Services/Infrastructure/MainWindowToolbar.swift | 2 +- TablePro/Models/UI/KeyboardShortcutModels.swift | 2 +- TablePro/Views/Toolbar/TableProToolbarView.swift | 2 +- docs/features/keyboard-shortcuts.mdx | 4 ++-- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index eb0591e32..0800a9c09 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,7 +37,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `Cmd+N` now opens the New Connection form. Manage Connections moves to the File menu without a default shortcut. - `Cmd+D` now duplicates the selected row. Save as Favorite moves to `Cmd+Shift+D`. - Removed default shortcuts that conflicted with system reservations: `Cmd+Y` (Quick Look), `Cmd+Option+Delete` (Empty Trash), `Cmd+Ctrl+C` (Color Picker), `Cmd+L` (URL bar / Add Link). -- Show History moves from `Cmd+Y` to `Cmd+Option+H` to mirror the other panel toggles. +- Show History moves from `Cmd+Y` (system Quick Look) to `Cmd+Shift+Y` to free the system shortcut while keeping the Y mnemonic. (`Cmd+Option+H` is reserved by macOS for Hide Others, so we avoid that chord too.) - Truncate Table no longer has a default shortcut; the action stays in the Edit menu and the sidebar context menu. - Switch Connection no longer has a default shortcut; the menu entry remains and users can rebind in Settings > Keyboard. - Explain with AI no longer has a default shortcut; the menu entry remains and users can rebind in Settings > Keyboard. diff --git a/TablePro/Core/Services/Infrastructure/MainWindowToolbar.swift b/TablePro/Core/Services/Infrastructure/MainWindowToolbar.swift index 726f497d1..6ecaa259d 100644 --- a/TablePro/Core/Services/Infrastructure/MainWindowToolbar.swift +++ b/TablePro/Core/Services/Infrastructure/MainWindowToolbar.swift @@ -454,7 +454,7 @@ private struct HistoryToolbarButton: View { } label: { Label("History", systemImage: "clock") } - .help(String(localized: "Toggle Query History (⌥⌘H)")) + .help(String(localized: "Toggle Query History (⇧⌘Y)")) } } diff --git a/TablePro/Models/UI/KeyboardShortcutModels.swift b/TablePro/Models/UI/KeyboardShortcutModels.swift index 077aed1e1..825bd1850 100644 --- a/TablePro/Models/UI/KeyboardShortcutModels.swift +++ b/TablePro/Models/UI/KeyboardShortcutModels.swift @@ -517,7 +517,7 @@ struct KeyboardSettings: Codable, Equatable { .toggleTableBrowser: KeyCombo(key: "0", command: true), .toggleInspector: KeyCombo(key: "i", command: true, option: true), .toggleFilters: KeyCombo(key: "f", command: true, shift: true), - .toggleHistory: KeyCombo(key: "h", command: true, option: true), + .toggleHistory: KeyCombo(key: "y", command: true, shift: true), .toggleResults: KeyCombo(key: "r", command: true, option: true), .previousResultTab: KeyCombo(key: "[", command: true, option: true), .nextResultTab: KeyCombo(key: "]", command: true, option: true), diff --git a/TablePro/Views/Toolbar/TableProToolbarView.swift b/TablePro/Views/Toolbar/TableProToolbarView.swift index 92f310e95..8e58c5d55 100644 --- a/TablePro/Views/Toolbar/TableProToolbarView.swift +++ b/TablePro/Views/Toolbar/TableProToolbarView.swift @@ -207,7 +207,7 @@ struct TableProToolbar: ViewModifier { } label: { Label("History", systemImage: "clock") } - .help(String(localized: "Toggle Query History (⌥⌘H)")) + .help(String(localized: "Toggle Query History (⇧⌘Y)")) Button { actions?.exportTables() diff --git a/docs/features/keyboard-shortcuts.mdx b/docs/features/keyboard-shortcuts.mdx index ecfbfa0da..58773dda1 100644 --- a/docs/features/keyboard-shortcuts.mdx +++ b/docs/features/keyboard-shortcuts.mdx @@ -16,7 +16,7 @@ TablePro is keyboard-driven. Most actions have shortcuts, and most menu shortcut | Execute query | `Cmd+Enter` | | New connection | `Cmd+N` | | New window | `Cmd+Ctrl+N` | -| Show history | `Cmd+Option+H` | +| Show history | `Cmd+Shift+Y` | | Settings | `Cmd+,` | | Quick Switcher | `Cmd+Shift+O` | | Close window | `Cmd+W` | @@ -180,7 +180,7 @@ TablePro is keyboard-driven. Most actions have shortcuts, and most menu shortcut | Action | Shortcut | |--------|----------| -| Show / Hide History | `Cmd+Option+H` | +| Show / Hide History | `Cmd+Shift+Y` | | Show / Hide Inspector | `Cmd+Option+I` | | Show / Hide Results | `Cmd+Option+R` | | Open Terminal | `Ctrl+Cmd+`` |