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
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,4 @@ run: build-app
open $(APP_PATH)

run-isolated: build-isolated
open -n --env SHELLRAISER_APP_SUPPORT_SUBDIRECTORY=$(ISOLATED_APP_SUPPORT_SUBDIRECTORY) $(ISOLATED_APP_PATH)
open --env SHELLRAISER_APP_SUPPORT_SUBDIRECTORY=$(ISOLATED_APP_SUPPORT_SUBDIRECTORY) $(ISOLATED_APP_PATH)
23 changes: 22 additions & 1 deletion Sources/Shellraiser/App/ShellraiserApp.swift
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,17 @@ final class ShellraiserAppDelegate: NSObject, NSApplicationDelegate {
ShellraiserScriptingController.shared.insertSurfaceConfiguration(configuration)
}

/// Brings the main window to front when the dock icon is clicked with no visible windows.
func applicationShouldHandleReopen(_ sender: NSApplication, hasVisibleWindows flag: Bool) -> Bool {
if !flag {
for window in sender.windows where window.canBecomeMain {
window.makeKeyAndOrderFront(nil)
break
}
}
return true
}

/// Intercepts standard app termination and requires explicit user confirmation.
func applicationShouldTerminate(_ sender: NSApplication) -> NSApplication.TerminateReply {
guard confirmQuit() else {
Expand All @@ -112,15 +123,25 @@ struct ShellraiserApp: App {
@StateObject private var manager: WorkspaceManager

/// Disables native macOS window tabbing so the app's own pane tabs remain the only tab UI.
/// Exits immediately if another instance with the same bundle ID is already running,
/// activating the existing instance instead — guards against Launch Services conflicts.
init() {
if let bundleID = Bundle.main.bundleIdentifier {
let others = NSRunningApplication.runningApplications(withBundleIdentifier: bundleID)
.filter { $0 != .current }
if let existing = others.first {
existing.activate()
exit(0)
}
}
NSWindow.allowsAutomaticWindowTabbing = false
let manager = WorkspaceManager()
_manager = StateObject(wrappedValue: manager)
ShellraiserScriptingController.shared.install(workspaceManager: manager)
}

var body: some Scene {
WindowGroup {
Window("Shellraiser", id: "main") {
ContentView(manager: manager)
.frame(minWidth: 1100, minHeight: 760)
}
Expand Down
16 changes: 16 additions & 0 deletions Tests/ShellraiserTests/ShellraiserAppTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,20 @@ final class ShellraiserAppTests: WorkspaceTestCase {
XCTAssertEqual(reply, .terminateCancel)
XCTAssertFalse(manager.isTerminating)
}

/// Verifies the delegate returns true (allow reopen handling) when no windows are visible,
/// so AppKit proceeds to make a window key — required for dock-click restoration.
func testApplicationShouldHandleReopenReturnsTrueWithNoVisibleWindows() {
let delegate = ShellraiserAppDelegate()
let result = delegate.applicationShouldHandleReopen(NSApplication.shared, hasVisibleWindows: false)
XCTAssertTrue(result)
}

/// Verifies the delegate returns true even when windows are already visible,
/// keeping reopen handling consistent regardless of window visibility state.
func testApplicationShouldHandleReopenReturnsTrueWithVisibleWindows() {
let delegate = ShellraiserAppDelegate()
let result = delegate.applicationShouldHandleReopen(NSApplication.shared, hasVisibleWindows: true)
XCTAssertTrue(result)
}
}
Loading