this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Add screenshot command for capturing simulator display

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

onevcat 485b7ea5 b4d64e68

+109 -9
+99
Sources/AXe/Commands/Screenshot.swift
··· 1 + import ArgumentParser 2 + import Foundation 3 + import FBSimulatorControl 4 + @preconcurrency import FBControlCore 5 + 6 + struct Screenshot: AsyncParsableCommand { 7 + static let configuration = CommandConfiguration( 8 + commandName: "screenshot", 9 + abstract: "Capture a screenshot from the simulator display and save it as a PNG file" 10 + ) 11 + 12 + @Option(name: .customLong("udid"), help: "The UDID of the simulator.") 13 + var simulatorUDID: String 14 + 15 + @Option(help: "Output PNG file path. Defaults to 'Simulator Screenshot - <device name> - <timestamp>.png' in the current directory.") 16 + var output: String? 17 + 18 + func run() async throws { 19 + let logger = AxeLogger() 20 + try await performGlobalSetup(logger: logger) 21 + 22 + let trimmedUDID = simulatorUDID.trimmingCharacters(in: .whitespacesAndNewlines) 23 + guard !trimmedUDID.isEmpty else { 24 + throw CLIError(errorDescription: "Simulator UDID cannot be empty. Use --udid to specify a simulator.") 25 + } 26 + 27 + let simulatorSet = try await getSimulatorSet(deviceSetPath: nil, logger: logger, reporter: EmptyEventReporter.shared) 28 + guard let targetSimulator = simulatorSet.allSimulators.first(where: { $0.udid == trimmedUDID }) else { 29 + throw CLIError(errorDescription: "Simulator with UDID \(trimmedUDID) not found.") 30 + } 31 + 32 + guard targetSimulator.state == .booted else { 33 + let stateDescription = FBiOSTargetStateStringFromState(targetSimulator.state) 34 + throw CLIError(errorDescription: "Simulator \(trimmedUDID) is not booted. Current state: \(stateDescription)") 35 + } 36 + 37 + let outputURL = try prepareOutputURL(simulator: targetSimulator) 38 + 39 + let screenshotData = try await VideoFrameUtilities.captureScreenshotData(from: targetSimulator) 40 + 41 + try screenshotData.write(to: outputURL) 42 + 43 + FileHandle.standardError.write(Data("Screenshot saved to \(outputURL.path)\n".utf8)) 44 + print(outputURL.path) 45 + } 46 + 47 + private func prepareOutputURL(simulator: FBSimulator) throws -> URL { 48 + let fileManager = FileManager.default 49 + 50 + let providedPath = output?.trimmingCharacters(in: .whitespacesAndNewlines) 51 + let resolvedPath: String 52 + if let providedPath, !providedPath.isEmpty { 53 + resolvedPath = (providedPath as NSString).expandingTildeInPath 54 + } else { 55 + let timestamp = Self.formatTimestamp(Date()) 56 + resolvedPath = "Simulator Screenshot - \(simulator.name) - \(timestamp).png" 57 + } 58 + 59 + let baseURL: URL 60 + if resolvedPath.hasPrefix("/") { 61 + baseURL = URL(fileURLWithPath: resolvedPath) 62 + } else { 63 + baseURL = URL(fileURLWithPath: fileManager.currentDirectoryPath).appendingPathComponent(resolvedPath) 64 + } 65 + 66 + var isDirectory: ObjCBool = false 67 + if fileManager.fileExists(atPath: baseURL.path, isDirectory: &isDirectory), isDirectory.boolValue { 68 + let timestamp = Self.formatTimestamp(Date()) 69 + let filename = "Simulator Screenshot - \(simulator.name) - \(timestamp).png" 70 + let directoryURL = baseURL 71 + if !fileManager.fileExists(atPath: directoryURL.path) { 72 + try fileManager.createDirectory(at: directoryURL, withIntermediateDirectories: true, attributes: nil) 73 + } 74 + return directoryURL.appendingPathComponent(filename) 75 + } 76 + 77 + let directoryURL = baseURL.deletingLastPathComponent() 78 + if !fileManager.fileExists(atPath: directoryURL.path) { 79 + try fileManager.createDirectory(at: directoryURL, withIntermediateDirectories: true, attributes: nil) 80 + } 81 + 82 + if fileManager.fileExists(atPath: baseURL.path) { 83 + var existingIsDirectory: ObjCBool = false 84 + fileManager.fileExists(atPath: baseURL.path, isDirectory: &existingIsDirectory) 85 + if existingIsDirectory.boolValue { 86 + throw CLIError(errorDescription: "Output path \(baseURL.path) is a directory. Provide a file name or point to a different location.") 87 + } 88 + try fileManager.removeItem(at: baseURL) 89 + } 90 + 91 + return baseURL 92 + } 93 + 94 + private static func formatTimestamp(_ date: Date) -> String { 95 + let formatter = DateFormatter() 96 + formatter.dateFormat = "yyyy-MM-dd 'at' HH.mm.ss" 97 + return formatter.string(from: date) 98 + } 99 + }
+10 -9
Sources/AXe/main.swift
··· 14 14 abstract: "A utility to interact with iOS Simulators and extract accessibility information.", 15 15 version: VERSION, 16 16 subcommands: [ 17 - DescribeUI.self, 18 - ListSimulators.self, 19 - Tap.self, 20 - Type.self, 21 - Swipe.self, 22 - Button.self, 23 - Key.self, 24 - KeySequence.self, 17 + DescribeUI.self, 18 + ListSimulators.self, 19 + Tap.self, 20 + Type.self, 21 + Swipe.self, 22 + Button.self, 23 + Key.self, 24 + KeySequence.self, 25 25 Touch.self, 26 26 Gesture.self, 27 27 StreamVideo.self, 28 - RecordVideo.self 28 + RecordVideo.self, 29 + Screenshot.self 29 30 ] 30 31 ) 31 32 }