mirror of
https://github.com/samsonjs/Peekaboo.git
synced 2026-04-27 15:07:41 +00:00
- Enhanced CaptureError types to include underlying system errors - Added comprehensive error logging in debug_logs for troubleshooting - Fixed duplicate error output from ApplicationFinder - Improved error details for app not found to show available applications - Updated test expectations to match new error message formats This ensures that errors from deep within ScreenCaptureKit and file operations are properly surfaced to users with full context in the debug logs. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
194 lines
5.7 KiB
Swift
194 lines
5.7 KiB
Swift
import ArgumentParser
|
|
import Foundation
|
|
|
|
// MARK: - Image Capture Models
|
|
|
|
struct SavedFile: Codable {
|
|
let path: String
|
|
let item_label: String?
|
|
let window_title: String?
|
|
let window_id: UInt32?
|
|
let window_index: Int?
|
|
let mime_type: String
|
|
}
|
|
|
|
struct ImageCaptureData: Codable {
|
|
let saved_files: [SavedFile]
|
|
}
|
|
|
|
enum CaptureMode: String, CaseIterable, ExpressibleByArgument {
|
|
case screen
|
|
case window
|
|
case multi
|
|
}
|
|
|
|
enum ImageFormat: String, CaseIterable, ExpressibleByArgument {
|
|
case png
|
|
case jpg
|
|
}
|
|
|
|
enum CaptureFocus: String, CaseIterable, ExpressibleByArgument {
|
|
case background
|
|
case auto
|
|
case foreground
|
|
}
|
|
|
|
// MARK: - Application & Window Models
|
|
|
|
struct ApplicationInfo: Codable {
|
|
let app_name: String
|
|
let bundle_id: String
|
|
let pid: Int32
|
|
let is_active: Bool
|
|
let window_count: Int
|
|
}
|
|
|
|
struct ApplicationListData: Codable {
|
|
let applications: [ApplicationInfo]
|
|
}
|
|
|
|
struct WindowInfo: Codable {
|
|
let window_title: String
|
|
let window_id: UInt32?
|
|
let window_index: Int?
|
|
let bounds: WindowBounds?
|
|
let is_on_screen: Bool?
|
|
}
|
|
|
|
struct WindowBounds: Codable {
|
|
let xCoordinate: Int
|
|
let yCoordinate: Int
|
|
let width: Int
|
|
let height: Int
|
|
}
|
|
|
|
struct TargetApplicationInfo: Codable {
|
|
let app_name: String
|
|
let bundle_id: String?
|
|
let pid: Int32
|
|
}
|
|
|
|
struct WindowListData: Codable {
|
|
let windows: [WindowInfo]
|
|
let target_application_info: TargetApplicationInfo
|
|
}
|
|
|
|
// MARK: - Window Specifier
|
|
|
|
enum WindowSpecifier {
|
|
case title(String)
|
|
case index(Int)
|
|
}
|
|
|
|
// MARK: - Window Details Options
|
|
|
|
enum WindowDetailOption: String, CaseIterable {
|
|
case off_screen
|
|
case bounds
|
|
case ids
|
|
}
|
|
|
|
// MARK: - Window Management
|
|
|
|
struct WindowData {
|
|
let windowId: UInt32
|
|
let title: String
|
|
let bounds: CGRect
|
|
let isOnScreen: Bool
|
|
let windowIndex: Int
|
|
}
|
|
|
|
// MARK: - Error Types
|
|
|
|
enum CaptureError: Error, LocalizedError {
|
|
case noDisplaysAvailable
|
|
case screenRecordingPermissionDenied
|
|
case accessibilityPermissionDenied
|
|
case invalidDisplayID
|
|
case captureCreationFailed(Error?)
|
|
case windowNotFound
|
|
case windowCaptureFailed(Error?)
|
|
case fileWriteError(String, Error?)
|
|
case appNotFound(String)
|
|
case invalidWindowIndex(Int)
|
|
case invalidArgument(String)
|
|
case unknownError(String)
|
|
case noWindowsFound(String)
|
|
|
|
var errorDescription: String? {
|
|
switch self {
|
|
case .noDisplaysAvailable:
|
|
return "No displays available for capture."
|
|
case .screenRecordingPermissionDenied:
|
|
return "Screen recording permission is required. " +
|
|
"Please grant it in System Settings > Privacy & Security > Screen Recording."
|
|
case .accessibilityPermissionDenied:
|
|
return "Accessibility permission is required for some operations. " +
|
|
"Please grant it in System Settings > Privacy & Security > Accessibility."
|
|
case .invalidDisplayID:
|
|
return "Invalid display ID provided."
|
|
case let .captureCreationFailed(underlyingError):
|
|
var message = "Failed to create the screen capture."
|
|
if let error = underlyingError {
|
|
message += " \(error.localizedDescription)"
|
|
}
|
|
return message
|
|
case .windowNotFound:
|
|
return "The specified window could not be found."
|
|
case let .windowCaptureFailed(underlyingError):
|
|
var message = "Failed to capture the specified window."
|
|
if let error = underlyingError {
|
|
message += " \(error.localizedDescription)"
|
|
}
|
|
return message
|
|
case let .fileWriteError(path, underlyingError):
|
|
var message = "Failed to write capture file to path: \(path)."
|
|
|
|
if let error = underlyingError {
|
|
let errorString = error.localizedDescription
|
|
if errorString.lowercased().contains("permission") {
|
|
message += " Permission denied - check that the directory is " +
|
|
"writable and the application has necessary permissions."
|
|
} else if errorString.lowercased().contains("no such file") {
|
|
message += " Directory does not exist - ensure the parent directory exists."
|
|
} else if errorString.lowercased().contains("no space") {
|
|
message += " Insufficient disk space available."
|
|
} else {
|
|
message += " \(errorString)"
|
|
}
|
|
} else {
|
|
message += " This may be due to insufficient permissions, missing directory, or disk space issues."
|
|
}
|
|
|
|
return message
|
|
case let .appNotFound(identifier):
|
|
return "Application with identifier '\(identifier)' not found or is not running."
|
|
case let .invalidWindowIndex(index):
|
|
return "Invalid window index: \(index)."
|
|
case let .invalidArgument(message):
|
|
return "Invalid argument: \(message)"
|
|
case let .unknownError(message):
|
|
return "An unexpected error occurred: \(message)"
|
|
case let .noWindowsFound(appName):
|
|
return "The '\(appName)' process is running, but no capturable windows were found."
|
|
}
|
|
}
|
|
|
|
var exitCode: Int32 {
|
|
switch self {
|
|
case .noDisplaysAvailable: 10
|
|
case .screenRecordingPermissionDenied: 11
|
|
case .accessibilityPermissionDenied: 12
|
|
case .invalidDisplayID: 13
|
|
case .captureCreationFailed: 14
|
|
case .windowNotFound: 15
|
|
case .windowCaptureFailed: 16
|
|
case .fileWriteError: 17
|
|
case .appNotFound: 18
|
|
case .invalidWindowIndex: 19
|
|
case .invalidArgument: 20
|
|
case .unknownError: 1
|
|
case .noWindowsFound: 7
|
|
}
|
|
}
|
|
}
|