Skip to content
Closed
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 ora/Common/Constants/KeyboardShortcuts.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ enum KeyboardShortcuts {
static let reopenClosed = KeyboardShortcut("t", modifiers: [.command, .shift])
static let next = KeyboardShortcut(.tab, modifiers: [.control])
static let previous = KeyboardShortcut(.tab, modifiers: [.control, .shift])
static let nextAlt = KeyboardShortcut("]", modifiers: [.command, .shift])
static let previousAlt = KeyboardShortcut("[", modifiers: [.command, .shift])
static let moveRight = KeyboardShortcut(.rightArrow, modifiers: [.option, .command])
static let moveLeft = KeyboardShortcut(.leftArrow, modifiers: [.option, .command])
static let pin = KeyboardShortcut("d", modifiers: [.command])
Expand Down Expand Up @@ -89,6 +91,8 @@ extension KeyboardShortcuts {
.init(category: "Tabs", name: "Reopen Closed", display: "⇧⌘T"),
.init(category: "Tabs", name: "Next Tab", display: "^⇥"),
.init(category: "Tabs", name: "Previous Tab", display: "^⇧⇥"),
.init(category: "Tabs", name: "Next Tab (Alt)", display: "⇧⌘]"),
.init(category: "Tabs", name: "Previous Tab (Alt)", display: "⇧⌘["),
.init(category: "Tabs", name: "Move Tab Right", display: "⌥⌘→"),
.init(category: "Tabs", name: "Move Tab Left", display: "⌥⌘←"),
.init(category: "Tabs", name: "Pin Tab", display: "⌘D"),
Expand Down
42 changes: 42 additions & 0 deletions ora/Services/TabManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,48 @@ class TabManager: ObservableObject {
try? modelContext.save()
}

func switchToNextTab() {
guard let container = activeContainer else { return }

// Get all available tabs, preferring ready ones but falling back to all
let readyTabs = container.tabs.filter(\.isWebViewReady)
let tabs = readyTabs.isEmpty ? Array(container.tabs) : readyTabs

guard tabs.count > 1, let currentTab = activeTab else { return }

// Sort by order for consistent navigation
let sortedTabs = tabs.sorted { $0.order < $1.order }

if let currentIndex = sortedTabs.firstIndex(where: { $0.id == currentTab.id }) {
let nextIndex = (currentIndex + 1) % sortedTabs.count
activateTab(sortedTabs[nextIndex])
} else {
// Current tab not found, activate first tab
activateTab(sortedTabs[0])
}
}

func switchToPreviousTab() {
guard let container = activeContainer else { return }

// Get all available tabs, preferring ready ones but falling back to all
let readyTabs = container.tabs.filter(\.isWebViewReady)
let tabs = readyTabs.isEmpty ? Array(container.tabs) : readyTabs

guard tabs.count > 1, let currentTab = activeTab else { return }

// Sort by order for consistent navigation
let sortedTabs = tabs.sorted { $0.order < $1.order }

if let currentIndex = sortedTabs.firstIndex(where: { $0.id == currentTab.id }) {
let previousIndex = (currentIndex - 1 + sortedTabs.count) % sortedTabs.count
activateTab(sortedTabs[previousIndex])
} else {
// Current tab not found, activate last tab
activateTab(sortedTabs[sortedTabs.count - 1])
}
}

private func fetchContainers() -> [TabContainer] {
do {
let descriptor = FetchDescriptor<TabContainer>(sortBy: [SortDescriptor(\.lastAccessedAt, order: .reverse)])
Expand Down
45 changes: 42 additions & 3 deletions ora/oraApp.swift
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,32 @@ struct OraApp: App {

if event.keyCode == 48 { // Tab key
if event.modifierFlags.contains(.control) {
if event.modifierFlags.contains(.shift) {
// Ctrl+Shift+Tab - Previous tab
DispatchQueue.main.async {
tabManager.switchToPreviousTab()
}
} else {
// Ctrl+Tab - Next tab
DispatchQueue.main.async {
tabManager.switchToNextTab()
}
}
return true
}
} else if event.keyCode == 33 { // ] key
if event.modifierFlags.contains([.command, .shift]) {
// Cmd+Shift+] - Next tab
DispatchQueue.main.async {
appState.isFloatingTabSwitchVisible = true
tabManager.switchToNextTab()
}
return true
}
} else if event.keyCode == 30 { // [ key
if event.modifierFlags.contains([.command, .shift]) {
// Cmd+Shift+[ - Previous tab
DispatchQueue.main.async {
tabManager.switchToPreviousTab()
}
return true
}
Expand Down Expand Up @@ -222,12 +246,27 @@ struct OraApp: App {
Divider()

Button("Next Tab") {
appState.isFloatingTabSwitchVisible = true
tabManager.switchToNextTab()
}
.keyboardShortcut(KeyboardShortcuts.Tabs.next)

Button("Previous Tab") {
appState.isFloatingTabSwitchVisible = true
tabManager.switchToPreviousTab()
}
.keyboardShortcut(KeyboardShortcuts.Tabs.previous)

// Alternative shortcuts for tab switching
Button("") {
tabManager.switchToNextTab()
}
.keyboardShortcut(KeyboardShortcuts.Tabs.nextAlt)
.hidden()

Button("") {
tabManager.switchToPreviousTab()
}
.keyboardShortcut(KeyboardShortcuts.Tabs.previousAlt)
.hidden()
}
}
Settings {
Expand Down