Skip to main content
Glama
WindowCommand.swift40.3 kB
import AppKit import Commander import CoreGraphics import Foundation import PeekabooCore import PeekabooFoundation // Logger for window command debugging /// Manipulate application windows with various actions @MainActor struct WindowCommand: ParsableCommand { static let commandDescription = CommandDescription( commandName: "window", abstract: "Manipulate application windows", discussion: """ SYNOPSIS: peekaboo window SUBCOMMAND [OPTIONS] DESCRIPTION: Provides window manipulation capabilities including closing, minimizing, maximizing, moving, resizing, and focusing windows. EXAMPLES: # Close a window peekaboo window close --app Safari peekaboo window close --app Safari --window-title "GitHub" # Minimize/maximize windows peekaboo window minimize --app Finder peekaboo window maximize --app Terminal # Move and resize windows peekaboo window move --app TextEdit --x 100 --y 100 peekaboo window resize --app Safari --width 1200 --height 800 peekaboo window set-bounds --app Chrome --x 50 --y 50 --width 1024 --height 768 # Focus a window peekaboo window focus --app "Visual Studio Code" peekaboo window focus --app Safari --window-title "Apple" # List windows (convenience shortcut) peekaboo window list --app Safari SUBCOMMANDS: close Close a window minimize Minimize a window to the Dock maximize Maximize a window (full screen) move Move a window to a new position resize Resize a window set-bounds Set window position and size in one operation focus Bring a window to the foreground list List windows for an application OUTPUT FORMAT: Default output is human-readable text. Use --json-output for machine-readable JSON format. """, subcommands: [ CloseSubcommand.self, MinimizeSubcommand.self, MaximizeSubcommand.self, MoveSubcommand.self, ResizeSubcommand.self, SetBoundsSubcommand.self, FocusSubcommand.self, WindowListSubcommand.self, ], showHelpOnEmptyInvocation: true ) } // MARK: - Common Options @MainActor struct WindowIdentificationOptions: CommanderParsable, ApplicationResolvable { @Option(name: .long, help: "Target application name, bundle ID, or 'PID:12345'") var app: String? @Option(name: .long, help: "Target application by process ID") var pid: Int32? @Option(name: .long, help: "Target window by title (partial match supported)") var windowTitle: String? @Option(name: .long, help: "Target window by index (0-based, frontmost is 0)") var windowIndex: Int? enum CodingKeys: String, CodingKey { case app case pid case windowTitle case windowIndex } init() {} init(from decoder: any Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) self.app = try container.decodeIfPresent(String.self, forKey: .app) self.pid = try container.decodeIfPresent(Int32.self, forKey: .pid) self.windowTitle = try container.decodeIfPresent(String.self, forKey: .windowTitle) self.windowIndex = try container.decodeIfPresent(Int.self, forKey: .windowIndex) } func validate() throws { // Ensure we have some way to identify the window if self.app == nil && self.pid == nil { throw ValidationError("Either --app or --pid must be specified") } } /// Convert to WindowTarget for service layer func toWindowTarget() throws -> WindowTarget { // Convert to WindowTarget for service layer let appIdentifier = try self.resolveApplicationIdentifier() if let index = windowIndex { return .index(app: appIdentifier, index: index) } else if self.windowTitle != nil { // For title matching, we still need the app context // The service will handle finding the right window return .application(appIdentifier) } else { // Default to app's frontmost window return .application(appIdentifier) } } } // MARK: - Helper Functions private func createWindowActionResult( action: String, success: Bool, windowInfo: ServiceWindowInfo?, appName: String? = nil ) -> WindowActionResult { let bounds: WindowBounds? = if let windowInfo { WindowBounds( x: Int(windowInfo.bounds.origin.x), y: Int(windowInfo.bounds.origin.y), width: Int(windowInfo.bounds.size.width), height: Int(windowInfo.bounds.size.height) ) } else { nil } return WindowActionResult( action: action, success: success, app_name: appName ?? "Unknown", window_title: windowInfo?.title, new_bounds: bounds ) } private func logWindowAction( action: String, appName: String?, windowInfo: ServiceWindowInfo? ) { let title = windowInfo?.title ?? "Unknown" let boundsDescription: String if let windowBounds = windowInfo?.bounds { let origin = "bounds=(\(Int(windowBounds.origin.x)),\(Int(windowBounds.origin.y)))" let size = "x(\(Int(windowBounds.size.width)),\(Int(windowBounds.size.height)))" boundsDescription = "\(origin)\(size)" } else { boundsDescription = "bounds=unknown" } AutomationEventLogger.log( .window, "\(action) app=\(appName ?? "Unknown") title=\(title) \(boundsDescription)" ) } // MARK: - Subcommands extension WindowCommand { @MainActor struct CloseSubcommand: ErrorHandlingCommand, OutputFormattable { @OptionGroup var windowOptions: WindowIdentificationOptions @RuntimeStorage private var runtime: CommandRuntime? private var resolvedRuntime: CommandRuntime { guard let runtime else { preconditionFailure("CommandRuntime must be configured before accessing runtime resources") } return runtime } private var services: any PeekabooServiceProviding { self.resolvedRuntime.services } private var logger: Logger { self.resolvedRuntime.logger } var outputLogger: Logger { self.logger } var jsonOutput: Bool { self.resolvedRuntime.configuration.jsonOutput } /// Resolve the target window, close it, and surface the outcome in JSON or text form. @MainActor mutating func run(using runtime: CommandRuntime) async throws { self.runtime = runtime self.logger.setJsonOutputMode(self.jsonOutput) do { try self.windowOptions.validate() let target = self.windowOptions.createTarget() // Get window info before action let windows = try await WindowServiceBridge.listWindows( windows: self.services.windows, target: self.windowOptions.toWindowTarget() ) let windowInfo = self.windowOptions.selectWindow(from: windows) let appName = self.windowOptions.app ?? "Unknown" // Perform the action try await WindowServiceBridge.closeWindow(windows: self.services.windows, target: target) logWindowAction( action: "close", appName: appName, windowInfo: windowInfo ) let data = createWindowActionResult( action: "close", success: true, windowInfo: windowInfo, appName: appName ) output(data) { print("Successfully closed window '\(windowInfo?.title ?? "Untitled")' of \(appName)") } } catch { handleError(error) throw ExitCode(1) } } } @MainActor struct MinimizeSubcommand: ErrorHandlingCommand, OutputFormattable { @OptionGroup var windowOptions: WindowIdentificationOptions @RuntimeStorage private var runtime: CommandRuntime? private var resolvedRuntime: CommandRuntime { guard let runtime else { preconditionFailure("CommandRuntime must be configured before accessing runtime resources") } return runtime } private var services: any PeekabooServiceProviding { self.resolvedRuntime.services } private var logger: Logger { self.resolvedRuntime.logger } var outputLogger: Logger { self.logger } var jsonOutput: Bool { self.resolvedRuntime.configuration.jsonOutput } /// Resolve the target window, minimize it to the Dock, and report the action. @MainActor mutating func run(using runtime: CommandRuntime) async throws { self.runtime = runtime self.logger.setJsonOutputMode(self.jsonOutput) do { try self.windowOptions.validate() let target = self.windowOptions.createTarget() // Get window info before action let windows = try await WindowServiceBridge.listWindows( windows: self.services.windows, target: self.windowOptions.toWindowTarget() ) let windowInfo = self.windowOptions.selectWindow(from: windows) let appName = self.windowOptions.app ?? "Unknown" // Perform the action try await WindowServiceBridge.minimizeWindow(windows: self.services.windows, target: target) logWindowAction( action: "minimize", appName: appName, windowInfo: windowInfo ) let data = createWindowActionResult( action: "minimize", success: true, windowInfo: windowInfo, appName: appName ) output(data) { print("Successfully minimized window '\(windowInfo?.title ?? "Untitled")' of \(appName)") } } catch { handleError(error) throw ExitCode(1) } } } @MainActor struct MaximizeSubcommand: ErrorHandlingCommand, OutputFormattable { @OptionGroup var windowOptions: WindowIdentificationOptions @RuntimeStorage private var runtime: CommandRuntime? private var resolvedRuntime: CommandRuntime { guard let runtime else { preconditionFailure("CommandRuntime must be configured before accessing runtime resources") } return runtime } private var services: any PeekabooServiceProviding { self.resolvedRuntime.services } private var logger: Logger { self.resolvedRuntime.logger } var outputLogger: Logger { self.logger } var jsonOutput: Bool { self.resolvedRuntime.configuration.jsonOutput } /// Expand the resolved window to fill the available screen real estate and share the updated frame. @MainActor mutating func run(using runtime: CommandRuntime) async throws { self.runtime = runtime self.logger.setJsonOutputMode(self.jsonOutput) do { try self.windowOptions.validate() let target = self.windowOptions.createTarget() // Get window info before action let windows = try await WindowServiceBridge.listWindows( windows: self.services.windows, target: self.windowOptions.toWindowTarget() ) let windowInfo = self.windowOptions.selectWindow(from: windows) let appName = self.windowOptions.app ?? "Unknown" // Perform the action try await WindowServiceBridge.maximizeWindow(windows: self.services.windows, target: target) logWindowAction( action: "maximize", appName: appName, windowInfo: windowInfo ) let data = createWindowActionResult( action: "maximize", success: true, windowInfo: windowInfo, appName: appName ) output(data) { print("Successfully maximized window '\(windowInfo?.title ?? "Untitled")' of \(appName)") } } catch { handleError(error) throw ExitCode(1) } } } @MainActor struct FocusSubcommand: ErrorHandlingCommand, OutputFormattable { @OptionGroup var windowOptions: WindowIdentificationOptions @OptionGroup var focusOptions: FocusCommandOptions @RuntimeStorage private var runtime: CommandRuntime? private var resolvedRuntime: CommandRuntime { guard let runtime else { preconditionFailure("CommandRuntime must be configured before accessing runtime resources") } return runtime } private var services: any PeekabooServiceProviding { self.resolvedRuntime.services } private var logger: Logger { self.resolvedRuntime.logger } var outputLogger: Logger { self.logger } var jsonOutput: Bool { self.resolvedRuntime.configuration.jsonOutput } /// Focus the targeted window, handling Space switches or relocation according to the provided options. @MainActor mutating func run(using runtime: CommandRuntime) async throws { self.runtime = runtime self.logger.debug("FocusSubcommand.run() called") self.logger.setJsonOutputMode(self.jsonOutput) do { self.logger.debug("About to validate window options") try self.windowOptions.validate() self.logger.debug("Window options validated") let target = self.windowOptions.createTarget() self.logger.debug("Target created: \(target)") // Get window info before action let windows = try await WindowServiceBridge.listWindows( windows: self.services.windows, target: self.windowOptions.toWindowTarget() ) self.logger.debug("Found \(windows.count) windows") let windowInfo = self.windowOptions.selectWindow(from: windows) let appName = self.windowOptions.app ?? "Unknown" // Check if we found any windows guard !windows.isEmpty else { throw PeekabooError.windowNotFound(criteria: "No windows found for \(appName)") } // Use enhanced focus with space support if let windowID = windowInfo?.windowID { try await ensureFocused( windowID: CGWindowID(windowID), applicationName: self.windowOptions.app, windowTitle: self.windowOptions.windowTitle, options: self.focusOptions.asFocusOptions, services: self.services ) } else { // Fallback to regular focus if no window ID try await WindowServiceBridge.focusWindow(windows: self.services.windows, target: target) } let refreshedWindowInfo = await self.windowOptions.refetchWindowInfo( services: self.services, logger: self.logger, context: "window-focus" ) let finalWindowInfo = refreshedWindowInfo ?? windowInfo logWindowAction( action: "focus", appName: appName, windowInfo: finalWindowInfo ) let data = createWindowActionResult( action: "focus", success: true, windowInfo: finalWindowInfo, appName: appName ) output(data) { var message = "Successfully focused window '\(finalWindowInfo?.title ?? "Untitled")' of \(appName)" if self.focusOptions.bringToCurrentSpace { message += " (moved to current Space)" } print(message) } } catch { handleError(error) throw ExitCode(1) } } } // MARK: - Move Command @MainActor struct MoveSubcommand: ErrorHandlingCommand, OutputFormattable { @OptionGroup var windowOptions: WindowIdentificationOptions @Option(name: .customShort("x", allowingJoined: false), help: "New X coordinate") var x: Int @Option(name: .customShort("y", allowingJoined: false), help: "New Y coordinate") var y: Int @RuntimeStorage private var runtime: CommandRuntime? private var resolvedRuntime: CommandRuntime { guard let runtime else { preconditionFailure("CommandRuntime must be configured before accessing runtime resources") } return runtime } private var services: any PeekabooServiceProviding { self.resolvedRuntime.services } private var logger: Logger { self.resolvedRuntime.logger } var outputLogger: Logger { self.logger } var jsonOutput: Bool { self.resolvedRuntime.configuration.jsonOutput } /// Move the window to the absolute screen coordinates provided by the user. @MainActor mutating func run(using runtime: CommandRuntime) async throws { self.runtime = runtime self.logger.setJsonOutputMode(self.jsonOutput) do { try self.windowOptions.validate() let target = self.windowOptions.createTarget() // Get window info let windows = try await WindowServiceBridge.listWindows( windows: self.services.windows, target: self.windowOptions.toWindowTarget() ) let windowInfo = self.windowOptions.selectWindow(from: windows) let appName = self.windowOptions.app ?? "Unknown" // Move the window let newOrigin = CGPoint(x: x, y: y) try await WindowServiceBridge.moveWindow(windows: self.services.windows, target: target, to: newOrigin) // Create result with new bounds let updatedInfo = windowInfo.map { info in ServiceWindowInfo( windowID: info.windowID, title: info.title, bounds: CGRect(origin: newOrigin, size: info.bounds.size), isMinimized: info.isMinimized, isMainWindow: info.isMainWindow, windowLevel: info.windowLevel, alpha: info.alpha, index: info.index ) } let refreshedWindowInfo = await self.windowOptions.refetchWindowInfo( services: self.services, logger: self.logger, context: "window-move" ) let finalWindowInfo = refreshedWindowInfo ?? updatedInfo ?? windowInfo logWindowAction( action: "move", appName: appName, windowInfo: finalWindowInfo ) let data = createWindowActionResult( action: "move", success: true, windowInfo: finalWindowInfo, appName: appName ) output(data) { print( "Successfully moved window '\(finalWindowInfo?.title ?? "Untitled")' to (\(self.x), \(self.y))" ) } } catch { handleError(error) throw ExitCode(1) } } } // MARK: - Resize Command @MainActor struct ResizeSubcommand: ErrorHandlingCommand, OutputFormattable { @OptionGroup var windowOptions: WindowIdentificationOptions @Option(name: .customShort("w", allowingJoined: false), help: "New width") var width: Int @Option(name: .long, help: "New height") var height: Int @RuntimeStorage private var runtime: CommandRuntime? private var resolvedRuntime: CommandRuntime { guard let runtime else { preconditionFailure("CommandRuntime must be configured before accessing runtime resources") } return runtime } private var services: any PeekabooServiceProviding { self.resolvedRuntime.services } private var logger: Logger { self.resolvedRuntime.logger } var outputLogger: Logger { self.logger } var jsonOutput: Bool { self.resolvedRuntime.configuration.jsonOutput } /// Resize the window to the supplied dimensions, preserving its origin. @MainActor mutating func run(using runtime: CommandRuntime) async throws { self.runtime = runtime self.logger.setJsonOutputMode(self.jsonOutput) do { try self.windowOptions.validate() let target = self.windowOptions.createTarget() // Get window info let windows = try await WindowServiceBridge.listWindows( windows: self.services.windows, target: self.windowOptions.toWindowTarget() ) let windowInfo = self.windowOptions.selectWindow(from: windows) let appName = self.windowOptions.app ?? "Unknown" // Resize the window let newSize = CGSize(width: width, height: height) try await WindowServiceBridge.resizeWindow(windows: self.services.windows, target: target, to: newSize) let refreshedWindowInfo = await self.windowOptions.refetchWindowInfo( services: self.services, logger: self.logger, context: "window-resize" ) let finalWindowInfo = refreshedWindowInfo ?? windowInfo logWindowAction( action: "resize", appName: appName, windowInfo: finalWindowInfo ) let data = createWindowActionResult( action: "resize", success: true, windowInfo: finalWindowInfo, appName: appName ) output(data) { let title = finalWindowInfo?.title ?? "Untitled" print("Successfully resized window '\(title)' to \(self.width)x\(self.height)") } } catch { handleError(error) throw ExitCode(1) } } } // MARK: - Set Bounds Command @MainActor struct SetBoundsSubcommand: ErrorHandlingCommand, OutputFormattable { @OptionGroup var windowOptions: WindowIdentificationOptions @Option(name: .customShort("x", allowingJoined: false), help: "New X coordinate") var x: Int @Option(name: .customShort("y", allowingJoined: false), help: "New Y coordinate") var y: Int @Option(name: .customShort("w", allowingJoined: false), help: "New width") var width: Int @Option(name: .long, help: "New height") var height: Int @RuntimeStorage private var runtime: CommandRuntime? private var resolvedRuntime: CommandRuntime { guard let runtime else { preconditionFailure("CommandRuntime must be configured before accessing runtime resources") } return runtime } private var services: any PeekabooServiceProviding { self.resolvedRuntime.services } private var logger: Logger { self.resolvedRuntime.logger } var outputLogger: Logger { self.logger } var jsonOutput: Bool { self.resolvedRuntime.configuration.jsonOutput } /// Set both position and size for the window in a single operation, then confirm the new bounds. @MainActor mutating func run(using runtime: CommandRuntime) async throws { self.runtime = runtime self.logger.setJsonOutputMode(self.jsonOutput) do { try self.windowOptions.validate() let target = self.windowOptions.createTarget() // Get window info let windows = try await WindowServiceBridge.listWindows( windows: self.services.windows, target: self.windowOptions.toWindowTarget() ) let windowInfo = self.windowOptions.selectWindow(from: windows) let appName = self.windowOptions.app ?? "Unknown" // Set bounds let newBounds = CGRect(x: x, y: y, width: width, height: height) try await WindowServiceBridge.setWindowBounds( windows: self.services.windows, target: target, bounds: newBounds ) let refreshedWindowInfo = await self.windowOptions.refetchWindowInfo( services: self.services, logger: self.logger, context: "window-set-bounds" ) let finalWindowInfo = refreshedWindowInfo ?? windowInfo logWindowAction( action: "set-bounds", appName: appName, windowInfo: finalWindowInfo ) let data = createWindowActionResult( action: "set-bounds", success: true, windowInfo: finalWindowInfo, appName: appName ) output(data) { let title = finalWindowInfo?.title ?? "Untitled" let boundsDescription = "(\(self.x), \(self.y)) \(self.width)x\(self.height)" print("Successfully set window '\(title)' bounds to \(boundsDescription)") } } catch { handleError(error) throw ExitCode(1) } } } // MARK: - List Command @MainActor struct WindowListSubcommand: ErrorHandlingCommand, OutputFormattable, ApplicationResolvable { @Option(name: .long, help: "Target application name, bundle ID, or 'PID:12345'") var app: String? @Option(name: .long, help: "Target application by process ID") var pid: Int32? @RuntimeStorage private var runtime: CommandRuntime? private var resolvedRuntime: CommandRuntime { guard let runtime else { preconditionFailure("CommandRuntime must be configured before accessing runtime resources") } return runtime } private var services: any PeekabooServiceProviding { self.resolvedRuntime.services } private var logger: Logger { self.resolvedRuntime.logger } var outputLogger: Logger { self.logger } var jsonOutput: Bool { self.resolvedRuntime.configuration.jsonOutput } @Flag(name: .long, help: "Group windows by Space (virtual desktop)") var groupBySpace = false /// List windows for the target application and optionally organize them by Space. @MainActor mutating func run(using runtime: CommandRuntime) async throws { self.runtime = runtime self.logger.setJsonOutputMode(self.jsonOutput) do { let appIdentifier = try self.resolveApplicationIdentifier() // First find the application to get its info let appInfo = try await self.services.applications.findApplication(identifier: appIdentifier) let target = WindowTarget.application(appIdentifier) let rawWindows = try await WindowServiceBridge.listWindows( windows: self.services.windows, target: target ) let windows = WindowFilterHelper.filter( windows: rawWindows, appIdentifier: appIdentifier, mode: .list, logger: self.logger ) // Convert ServiceWindowInfo to WindowInfo for consistency let windowInfos = windows.map { window in WindowInfo( window_title: window.title, window_id: UInt32(window.windowID), window_index: window.index, bounds: WindowBounds( x: Int(window.bounds.origin.x), y: Int(window.bounds.origin.y), width: Int(window.bounds.size.width), height: Int(window.bounds.size.height) ), is_on_screen: window.isOnScreen ) } // Use PeekabooCore's WindowListData let data = WindowListData( windows: windowInfos, target_application_info: TargetApplicationInfo( app_name: appInfo.name, bundle_id: appInfo.bundleIdentifier, pid: appInfo.processIdentifier ) ) output(data) { print("\(data.target_application_info.app_name) has \(data.windows.count) window(s):") if self.groupBySpace { // Group windows by space var windowsBySpace: [UInt64?: [(window: ServiceWindowInfo, index: Int)]] = [:] for window in windows { let spaceID = window.spaceID windowsBySpace[spaceID, default: []].append((window, window.index)) } // Sort spaces by ID (nil first for windows not on any space) let sortedSpaces = windowsBySpace.keys.sorted { a, b in switch (a, b) { case (nil, nil): false case (nil, _): true case (_, nil): false case let (a?, b?): a < b } } // Print grouped windows for spaceID in sortedSpaces { if let spaceID { let spaceName = windowsBySpace[spaceID]?.first?.window.spaceName ?? "Space \(spaceID)" print("\n Space: \(spaceName) [ID: \(spaceID)]") } else { print("\n No Space:") } for (window, index) in windowsBySpace[spaceID] ?? [] { let status = window.isMinimized ? " [minimized]" : "" print(" [\(index)] \"\(window.title)\"\(status)") let origin = window.bounds.origin print(" Position: (\(Int(origin.x)), \(Int(origin.y)))") print( " Size: \(Int(window.bounds.size.width))x\(Int(window.bounds.size.height))" ) } } } else { // Original flat list for window in data.windows { let index = window.window_index ?? 0 let status = (window.is_on_screen == false) ? " [minimized]" : "" print(" [\(index)] \"\(window.window_title)\"\(status)") if let bounds = window.bounds { print(" Position: (\(bounds.x), \(bounds.y))") print(" Size: \(bounds.width)x\(bounds.height)") } } } } } catch { handleError(error) throw ExitCode(1) } } } } // MARK: - Response Types struct WindowActionResult: Codable { let action: String let success: Bool let app_name: String let window_title: String? let new_bounds: WindowBounds? } // Using PeekabooCore.WindowListData for consistency // MARK: - Subcommand Conformances @MainActor extension WindowCommand.MoveSubcommand: ParsableCommand { nonisolated(unsafe) static var commandDescription: CommandDescription { MainActorCommandDescription.describe { CommandDescription(commandName: "move", abstract: "Move a window to a new position") } } } extension WindowCommand.MoveSubcommand: AsyncRuntimeCommand {} @MainActor extension WindowCommand.ResizeSubcommand: ParsableCommand { nonisolated(unsafe) static var commandDescription: CommandDescription { MainActorCommandDescription.describe { CommandDescription(commandName: "resize", abstract: "Resize a window") } } } extension WindowCommand.ResizeSubcommand: AsyncRuntimeCommand {} @MainActor extension WindowCommand.SetBoundsSubcommand: ParsableCommand { nonisolated(unsafe) static var commandDescription: CommandDescription { MainActorCommandDescription.describe { CommandDescription(commandName: "set-bounds", abstract: "Set window position and size in one operation") } } } extension WindowCommand.SetBoundsSubcommand: AsyncRuntimeCommand {} @MainActor extension WindowCommand.WindowListSubcommand: ParsableCommand { nonisolated(unsafe) static var commandDescription: CommandDescription { MainActorCommandDescription.describe { CommandDescription(commandName: "list", abstract: "List windows for an application") } } } extension WindowCommand.WindowListSubcommand: AsyncRuntimeCommand {} @MainActor extension WindowCommand.CloseSubcommand: ParsableCommand { nonisolated(unsafe) static var commandDescription: CommandDescription { MainActorCommandDescription.describe { CommandDescription(commandName: "close", abstract: "Close a window") } } } extension WindowCommand.CloseSubcommand: AsyncRuntimeCommand {} @MainActor extension WindowCommand.MinimizeSubcommand: ParsableCommand { nonisolated(unsafe) static var commandDescription: CommandDescription { MainActorCommandDescription.describe { CommandDescription(commandName: "minimize", abstract: "Minimize a window to the Dock") } } } extension WindowCommand.MinimizeSubcommand: AsyncRuntimeCommand {} @MainActor extension WindowCommand.MaximizeSubcommand: ParsableCommand { nonisolated(unsafe) static var commandDescription: CommandDescription { MainActorCommandDescription.describe { CommandDescription(commandName: "maximize", abstract: "Maximize a window (full screen)") } } } extension WindowCommand.MaximizeSubcommand: AsyncRuntimeCommand {} @MainActor extension WindowCommand.FocusSubcommand: ParsableCommand { nonisolated(unsafe) static var commandDescription: CommandDescription { MainActorCommandDescription.describe { CommandDescription( commandName: "focus", abstract: "Bring a window to the foreground", discussion: """ Focus brings a window to the foreground and activates its application. Space Support: By default, if the window is on a different Space (virtual desktop), the focus command will switch to that Space. You can control this behavior with the --space-switch and --move-here options. Examples: peekaboo window focus --app Safari peekaboo window focus --app "Visual Studio Code" --window-title "main.swift" peekaboo window focus --app Terminal --no-space-switch peekaboo window focus --app Finder --move-here """ ) } } } extension WindowCommand.FocusSubcommand: AsyncRuntimeCommand {} // MARK: - Commander Binding @MainActor extension WindowCommand.CloseSubcommand: CommanderBindableCommand { mutating func applyCommanderValues(_ values: CommanderBindableValues) throws { self.windowOptions = try values.makeWindowOptions() } } @MainActor extension WindowCommand.MinimizeSubcommand: CommanderBindableCommand { mutating func applyCommanderValues(_ values: CommanderBindableValues) throws { self.windowOptions = try values.makeWindowOptions() } } @MainActor extension WindowCommand.MaximizeSubcommand: CommanderBindableCommand { mutating func applyCommanderValues(_ values: CommanderBindableValues) throws { self.windowOptions = try values.makeWindowOptions() } } @MainActor extension WindowCommand.FocusSubcommand: CommanderBindableCommand { mutating func applyCommanderValues(_ values: CommanderBindableValues) throws { self.windowOptions = try values.makeWindowOptions() self.focusOptions = try values.makeFocusOptions() } } @MainActor extension WindowCommand.MoveSubcommand: CommanderBindableCommand { mutating func applyCommanderValues(_ values: CommanderBindableValues) throws { self.windowOptions = try values.makeWindowOptions() self.x = try values.requireOption("x", as: Int.self) self.y = try values.requireOption("y", as: Int.self) } } @MainActor extension WindowCommand.ResizeSubcommand: CommanderBindableCommand { mutating func applyCommanderValues(_ values: CommanderBindableValues) throws { self.windowOptions = try values.makeWindowOptions() self.width = try values.requireOption("width", as: Int.self) self.height = try values.requireOption("height", as: Int.self) } } @MainActor extension WindowCommand.SetBoundsSubcommand: CommanderBindableCommand { mutating func applyCommanderValues(_ values: CommanderBindableValues) throws { self.windowOptions = try values.makeWindowOptions() self.x = try values.requireOption("x", as: Int.self) self.y = try values.requireOption("y", as: Int.self) self.width = try values.requireOption("width", as: Int.self) self.height = try values.requireOption("height", as: Int.self) } } @MainActor extension WindowCommand.WindowListSubcommand: CommanderBindableCommand { mutating func applyCommanderValues(_ values: CommanderBindableValues) throws { self.app = values.singleOption("app") self.pid = try values.decodeOption("pid", as: Int32.self) } } // MARK: - Commander Helpers

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/steipete/Peekaboo'

If you have feedback or need assistance with the MCP directory API, please join our Discord server