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
28 changes: 16 additions & 12 deletions Deckard.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
432752F9187D2E815F617B96 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88AA91CDBC6DDE958543FB29 /* main.swift */; };
63A33F81DE38406C15D1259A /* SettingsWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD37F7C8CC5243DCF0A1346E /* SettingsWindow.swift */; };
TC100001TC100001TC100001 /* ThemeCardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = TC100002TC100002TC100002 /* ThemeCardView.swift */; };
70265FBBA52FB6E60AE4EFFE /* ProjectPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BE07F0C0F9D62925EF195F7 /* ProjectPicker.swift */; };
70265FBBA52FB6E60AE4EFFE /* WorkspacePicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BE07F0C0F9D62925EF195F7 /* WorkspacePicker.swift */; };
7E500DC8ECF3F7D073A098A4 /* ControlSocket.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08100E22096E7498D571A20 /* ControlSocket.swift */; };

A0FF9B9E99BC8B33FDCAB7E5 /* HookHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A2B375A023FEBCB272DCFD7 /* HookHandler.swift */; };
Expand Down Expand Up @@ -54,8 +54,8 @@
AA1C0001AA1C0001AA1C0001 /* DeckardHooksInstallerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA1C0002AA1C0002AA1C0002 /* DeckardHooksInstallerTests.swift */; };
AA1D0001AA1D0001AA1D0001 /* CrashReporterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA1D0002AA1D0002AA1D0002 /* CrashReporterTests.swift */; };
AA1E0001AA1E0001AA1E0001 /* ControlSocketTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA1E0002AA1E0002AA1E0002 /* ControlSocketTests.swift */; };
AA1F0001AA1F0001AA1F0001 /* SidebarFolderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA1F0002AA1F0002AA1F0002 /* SidebarFolderTests.swift */; };
AA200001AA200001AA200001 /* SidebarFolderViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA200002AA200002AA200002 /* SidebarFolderViewTests.swift */; };
AA1F0001AA1F0001AA1F0001 /* SidebarGroupTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA1F0002AA1F0002AA1F0002 /* SidebarGroupTests.swift */; };
AA200001AA200001AA200001 /* SidebarGroupViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA200002AA200002AA200002 /* SidebarGroupViewTests.swift */; };
QA200002QA200002QA200002 /* QuotaMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = QA200001QA200001QA200001 /* QuotaMonitor.swift */; };
QA200004QA200004QA200004 /* QuotaView.swift in Sources */ = {isa = PBXBuildFile; fileRef = QA200003QA200003QA200003 /* QuotaView.swift */; };
QA200006QA200006QA200006 /* QuotaMonitorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = QA200005QA200005QA200005 /* QuotaMonitorTests.swift */; };
Expand All @@ -66,13 +66,14 @@
CF000001CF000001CF000001 /* ClaudeCLIFlags.swift in Sources */ = {isa = PBXBuildFile; fileRef = CF000002CF000002CF000002 /* ClaudeCLIFlags.swift */; };
CAF00001CAF00001CAF00001 /* ClaudeArgsField.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAF00002CAF00002CAF00002 /* ClaudeArgsField.swift */; };
CF000003CF000003CF000003 /* ClaudeCLIFlagsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CF000004CF000004CF000004 /* ClaudeCLIFlagsTests.swift */; };
5C000001500000015C000001 /* ShortcutMigrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C000002500000025C000002 /* ShortcutMigrationTests.swift */; };
/* End PBXBuildFile section */

/* Begin PBXFileReference section */


4A2B375A023FEBCB272DCFD7 /* HookHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HookHandler.swift; sourceTree = "<group>"; };
4BE07F0C0F9D62925EF195F7 /* ProjectPicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProjectPicker.swift; sourceTree = "<group>"; };
4BE07F0C0F9D62925EF195F7 /* WorkspacePicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WorkspacePicker.swift; sourceTree = "<group>"; };
6C2CC70A6932271D0EE46154 /* Deckard-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Deckard-Bridging-Header.h"; sourceTree = "<group>"; };
7BE2AA0719D32ACCD1549184 /* SessionState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionState.swift; sourceTree = "<group>"; };
7F505D5B7348F40C2CBD6F83 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -116,8 +117,8 @@
AA1C0002AA1C0002AA1C0002 /* DeckardHooksInstallerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeckardHooksInstallerTests.swift; sourceTree = "<group>"; };
AA1D0002AA1D0002AA1D0002 /* CrashReporterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CrashReporterTests.swift; sourceTree = "<group>"; };
AA1E0002AA1E0002AA1E0002 /* ControlSocketTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ControlSocketTests.swift; sourceTree = "<group>"; };
AA1F0002AA1F0002AA1F0002 /* SidebarFolderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SidebarFolderTests.swift; sourceTree = "<group>"; };
AA200002AA200002AA200002 /* SidebarFolderViewTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SidebarFolderViewTests.swift; sourceTree = "<group>"; };
AA1F0002AA1F0002AA1F0002 /* SidebarGroupTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SidebarGroupTests.swift; sourceTree = "<group>"; };
AA200002AA200002AA200002 /* SidebarGroupViewTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SidebarGroupViewTests.swift; sourceTree = "<group>"; };
QA200001QA200001QA200001 /* QuotaMonitor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuotaMonitor.swift; sourceTree = "<group>"; };
QA200003QA200003QA200003 /* QuotaView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuotaView.swift; sourceTree = "<group>"; };
QA200005QA200005QA200005 /* QuotaMonitorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuotaMonitorTests.swift; sourceTree = "<group>"; };
Expand All @@ -127,6 +128,7 @@
SE00000ASE00000ASE00000A /* SessionExplorerTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionExplorerTimelineView.swift; sourceTree = "<group>"; };
CF000002CF000002CF000002 /* ClaudeCLIFlags.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClaudeCLIFlags.swift; sourceTree = "<group>"; };
CF000004CF000004CF000004 /* ClaudeCLIFlagsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClaudeCLIFlagsTests.swift; sourceTree = "<group>"; };
5C000002500000025C000002 /* ShortcutMigrationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShortcutMigrationTests.swift; sourceTree = "<group>"; };
CAF00002CAF00002CAF00002 /* ClaudeArgsField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClaudeArgsField.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */

Expand Down Expand Up @@ -214,7 +216,7 @@
QA200003QA200003QA200003 /* QuotaView.swift */,
SV000002SV000002SV000002 /* SidebarViews.swift */,
TV000002TV000002TV000002 /* TabBarViews.swift */,
4BE07F0C0F9D62925EF195F7 /* ProjectPicker.swift */,
4BE07F0C0F9D62925EF195F7 /* WorkspacePicker.swift */,
DD37F7C8CC5243DCF0A1346E /* SettingsWindow.swift */,
TC100002TC100002TC100002 /* ThemeCardView.swift */,
CAF00002CAF00002CAF00002 /* ClaudeArgsField.swift */,
Expand Down Expand Up @@ -280,15 +282,16 @@
AA180002AA180002AA180002 /* ProcessMonitorTests.swift */,
QA200005QA200005QA200005 /* QuotaMonitorTests.swift */,
AA130002AA130002AA130002 /* SessionStateTests.swift */,
AA1F0002AA1F0002AA1F0002 /* SidebarFolderTests.swift */,
AA200002AA200002AA200002 /* SidebarFolderViewTests.swift */,
AA1F0002AA1F0002AA1F0002 /* SidebarGroupTests.swift */,
AA200002AA200002AA200002 /* SidebarGroupViewTests.swift */,
TE000001TE000001TE000001 /* SmokeTests.swift */,
AA120002AA120002AA120002 /* TerminalColorSchemeTests.swift */,
AA1A0002AA1A0002AA1A0002 /* TerminalSurfaceTests.swift */,
AA110002AA110002AA110002 /* ThemeColorsTests.swift */,
AA140002AA140002AA140002 /* ThemeManagerTests.swift */,
AA1B0002AA1B0002AA1B0002 /* WindowControllerLogicTests.swift */,
CF000004CF000004CF000004 /* ClaudeCLIFlagsTests.swift */,
5C000002500000025C000002 /* ShortcutMigrationTests.swift */,
);
path = Tests;
sourceTree = "<group>";
Expand Down Expand Up @@ -426,7 +429,7 @@
SV000001SV000001SV000001 /* SidebarViews.swift in Sources */,
TV000001TV000001TV000001 /* TabBarViews.swift in Sources */,
A0FF9B9E99BC8B33FDCAB7E5 /* HookHandler.swift in Sources */,
70265FBBA52FB6E60AE4EFFE /* ProjectPicker.swift in Sources */,
70265FBBA52FB6E60AE4EFFE /* WorkspacePicker.swift in Sources */,
2D322E22D0679FD39D688539 /* SessionState.swift in Sources */,
63A33F81DE38406C15D1259A /* SettingsWindow.swift in Sources */,
TC100001TC100001TC100001 /* ThemeCardView.swift in Sources */,
Expand Down Expand Up @@ -471,10 +474,11 @@
AA1C0001AA1C0001AA1C0001 /* DeckardHooksInstallerTests.swift in Sources */,
AA1D0001AA1D0001AA1D0001 /* CrashReporterTests.swift in Sources */,
AA1E0001AA1E0001AA1E0001 /* ControlSocketTests.swift in Sources */,
AA1F0001AA1F0001AA1F0001 /* SidebarFolderTests.swift in Sources */,
AA200001AA200001AA200001 /* SidebarFolderViewTests.swift in Sources */,
AA1F0001AA1F0001AA1F0001 /* SidebarGroupTests.swift in Sources */,
AA200001AA200001AA200001 /* SidebarGroupViewTests.swift in Sources */,
QA200006QA200006QA200006 /* QuotaMonitorTests.swift in Sources */,
CF000003CF000003CF000003 /* ClaudeCLIFlagsTests.swift in Sources */,
5C000001500000015C000001 /* ShortcutMigrationTests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down
90 changes: 47 additions & 43 deletions Sources/App/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ class AppDelegate: NSObject, NSApplicationDelegate {
ThemeManager.shared.applySavedTheme()
log.log("startup", "Loaded \(ThemeManager.shared.availableThemes.count) themes, current: \(ThemeManager.shared.currentThemeName ?? "default")")

// Migrate any user shortcut overrides from old identifier names before
// anything reads from KeyboardShortcuts.
DeckardShortcutMigration.migrate()

// Set up the main menu.
log.log("startup", "Setting up main menu...")
setupMainMenu()
Expand Down Expand Up @@ -72,7 +76,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
if TerminalSurface.tmuxAvailable {
let savedState = SessionManager.shared.load()
let activeSessions = Set(
(savedState?.projects ?? []).flatMap(\.tabs).compactMap(\.tmuxSessionName)
(savedState?.workspaces ?? []).flatMap(\.tabs).compactMap(\.tmuxSessionName)
)
DispatchQueue.global(qos: .utility).async {
TerminalSurface.cleanupOrphanedTmuxSessions(activeSessions: activeSessions)
Expand Down Expand Up @@ -152,11 +156,11 @@ class AppDelegate: NSObject, NSApplicationDelegate {
}

@objc private func handleNewTab() {
windowController?.addTabToCurrentProject(kind: .claude)
windowController?.addTabToCurrentWorkspace(kind: .claude)
}

@objc private func handleNewCodexTab() {
windowController?.addTabToCurrentProject(kind: .codex)
windowController?.addTabToCurrentWorkspace(kind: .codex)
}

@objc private func handleCloseTab() {
Expand Down Expand Up @@ -192,8 +196,8 @@ class AppDelegate: NSObject, NSApplicationDelegate {
let fileMenuItem = NSMenuItem()
let fileMenu = NSMenu(title: "File")

let openItem = NSMenuItem(title: "Open Folder...", action: #selector(openProject), keyEquivalent: "")
openItem.setShortcut(for: .openFolder)
let openItem = NSMenuItem(title: "Open Workspace...", action: #selector(openWorkspace), keyEquivalent: "")
openItem.setShortcut(for: .openWorkspace)
fileMenu.addItem(openItem)
fileMenu.addItem(.separator())

Expand All @@ -215,23 +219,23 @@ class AppDelegate: NSObject, NSApplicationDelegate {
closeItem.setShortcut(for: .closeTab)
fileMenu.addItem(closeItem)

let newFolderItem = NSMenuItem(title: "New Sidebar Folder", action: #selector(createNewSidebarFolder), keyEquivalent: "")
newFolderItem.setShortcut(for: .newSidebarFolder)
newFolderItem.target = self
fileMenu.addItem(newFolderItem)
let newGroupItem = NSMenuItem(title: "New Group", action: #selector(createNewSidebarGroup), keyEquivalent: "")
newGroupItem.setShortcut(for: .newGroup)
newGroupItem.target = self
fileMenu.addItem(newGroupItem)

let moveOutItem = NSMenuItem(title: "Move Out of Folder", action: #selector(moveCurrentProjectOutOfFolder), keyEquivalent: "")
moveOutItem.setShortcut(for: .moveOutOfFolder)
let moveOutItem = NSMenuItem(title: "Move Out of Group", action: #selector(moveCurrentWorkspaceOutOfGroup), keyEquivalent: "")
moveOutItem.setShortcut(for: .moveOutOfGroup)
moveOutItem.target = self
fileMenu.addItem(moveOutItem)

let exploreSessionsItem = NSMenuItem(title: "Explore Sessions", action: #selector(exploreSessions), keyEquivalent: "")
exploreSessionsItem.setShortcut(for: .exploreSessions)
fileMenu.addItem(exploreSessionsItem)

let closeProjectItem = NSMenuItem(title: "Close Folder", action: #selector(closeCurrentProject), keyEquivalent: "")
closeProjectItem.setShortcut(for: .closeFolder)
fileMenu.addItem(closeProjectItem)
let closeWorkspaceItem = NSMenuItem(title: "Close Workspace", action: #selector(closeCurrentWorkspace), keyEquivalent: "")
closeWorkspaceItem.setShortcut(for: .closeWorkspace)
fileMenu.addItem(closeWorkspaceItem)
fileMenu.addItem(.separator())

let nextTabItem = NSMenuItem(title: "Next Tab", action: #selector(selectNextTab), keyEquivalent: "")
Expand All @@ -242,19 +246,19 @@ class AppDelegate: NSObject, NSApplicationDelegate {
prevTabItem.setShortcut(for: .previousTab)
fileMenu.addItem(prevTabItem)

let nextProjectItem = NSMenuItem(title: "Next Project", action: #selector(selectNextProject), keyEquivalent: "")
nextProjectItem.setShortcut(for: .nextProject)
fileMenu.addItem(nextProjectItem)
let nextWorkspaceItem = NSMenuItem(title: "Next Workspace", action: #selector(selectNextWorkspace), keyEquivalent: "")
nextWorkspaceItem.setShortcut(for: .nextWorkspace)
fileMenu.addItem(nextWorkspaceItem)

let prevProjectItem = NSMenuItem(title: "Previous Project", action: #selector(selectPrevProject), keyEquivalent: "")
prevProjectItem.setShortcut(for: .previousProject)
fileMenu.addItem(prevProjectItem)
let prevWorkspaceItem = NSMenuItem(title: "Previous Workspace", action: #selector(selectPrevWorkspace), keyEquivalent: "")
prevWorkspaceItem.setShortcut(for: .previousWorkspace)
fileMenu.addItem(prevWorkspaceItem)
fileMenu.addItem(.separator())

// Cmd+1-9, Cmd+0 for direct project access
// Cmd+1-9, Cmd+0 for direct workspace access
for i in 0..<tabShortcutNames.count {
let displayNum = i + 1
let item = NSMenuItem(title: "Project \(displayNum)", action: #selector(selectTabByNumber(_:)), keyEquivalent: "")
let item = NSMenuItem(title: "Workspace \(displayNum)", action: #selector(selectTabByNumber(_:)), keyEquivalent: "")
item.tag = i
item.setShortcut(for: tabShortcutNames[i])
fileMenu.addItem(item)
Expand Down Expand Up @@ -296,29 +300,29 @@ class AppDelegate: NSObject, NSApplicationDelegate {

// MARK: - Actions

private let projectPicker = ProjectPicker()
private let workspacePicker = WorkspacePicker()

func openProjectPicker() {
openProject()
func openWorkspacePicker() {
openWorkspace()
}

@objc private func openProject() {
projectPicker.show(relativeTo: windowController?.window) { [weak self] path in
@objc private func openWorkspace() {
workspacePicker.show(relativeTo: windowController?.window) { [weak self] path in
guard let path = path else { return }
self?.windowController?.openProject(path: path)
self?.windowController?.openWorkspace(path: path)
}
}

@objc private func newClaudeTab() {
windowController?.addTabToCurrentProject(kind: .claude)
windowController?.addTabToCurrentWorkspace(kind: .claude)
}

@objc private func newCodexTab() {
windowController?.addTabToCurrentProject(kind: .codex)
windowController?.addTabToCurrentWorkspace(kind: .codex)
}

@objc private func newTerminalTab() {
windowController?.addTabToCurrentProject(kind: .terminal)
windowController?.addTabToCurrentWorkspace(kind: .terminal)
}

@objc private func closeCurrentTab() {
Expand All @@ -331,20 +335,20 @@ class AppDelegate: NSObject, NSApplicationDelegate {
}

@objc private func exploreSessions() {
windowController?.exploreCurrentProjectSessions()
windowController?.exploreCurrentWorkspaceSessions()
}

@objc private func moveCurrentProjectOutOfFolder() {
windowController?.moveCurrentProjectOutOfFolder()
@objc private func moveCurrentWorkspaceOutOfGroup() {
windowController?.moveCurrentWorkspaceOutOfGroup()
}

@objc private func closeCurrentProject() {
@objc private func closeCurrentWorkspace() {
if let keyWindow = NSApp.keyWindow,
keyWindow != windowController?.window {
keyWindow.performClose(nil)
return
}
windowController?.closeCurrentProject()
windowController?.closeCurrentWorkspace()
}

@objc private func selectNextTab() {
Expand All @@ -355,20 +359,20 @@ class AppDelegate: NSObject, NSApplicationDelegate {
windowController?.selectPrevTab()
}

@objc private func selectNextProject() {
windowController?.selectNextProject()
@objc private func selectNextWorkspace() {
windowController?.selectNextWorkspace()
}

@objc private func selectPrevProject() {
windowController?.selectPrevProject()
@objc private func selectPrevWorkspace() {
windowController?.selectPrevWorkspace()
}

@objc private func selectTabByNumber(_ sender: NSMenuItem) {
windowController?.selectProject(byNumber: sender.tag)
windowController?.selectWorkspace(byNumber: sender.tag)
}

@objc private func createNewSidebarFolder() {
windowController?.createSidebarFolder()
@objc private func createNewSidebarGroup() {
windowController?.createSidebarGroup()
}

@objc private func toggleSidebar() {
Expand Down
4 changes: 2 additions & 2 deletions Sources/App/ClaudeCLIFlags.swift
Original file line number Diff line number Diff line change
Expand Up @@ -264,8 +264,8 @@ final class CodexCLIFlags {
/// Flags Deckard manages internally — excluded from suggestions.
static let blocklist: Set<String> = [
"--help", "--version",
// Deckard launches Codex in the project directory already; suggesting
// --cd as a persistent default would make tabs ignore their project root.
// Deckard launches Codex in the workspace directory already; suggesting
// --cd as a persistent default would make tabs ignore their workspace root.
"--cd",
]

Expand Down
Loading
Loading