mirror of
https://github.com/ksyasuda/dotfiles.git
synced 2026-02-27 12:22:43 -08:00
update
This commit is contained in:
126
.agents/skills/screenshot/scripts/macos_window_info.swift
Normal file
126
.agents/skills/screenshot/scripts/macos_window_info.swift
Normal file
@@ -0,0 +1,126 @@
|
||||
import AppKit
|
||||
import CoreGraphics
|
||||
import Foundation
|
||||
|
||||
struct Bounds: Encodable {
|
||||
let x: Int
|
||||
let y: Int
|
||||
let width: Int
|
||||
let height: Int
|
||||
}
|
||||
|
||||
struct WindowInfo: Encodable {
|
||||
let id: Int
|
||||
let owner: String
|
||||
let name: String
|
||||
let layer: Int
|
||||
let bounds: Bounds
|
||||
let area: Int
|
||||
}
|
||||
|
||||
struct Response: Encodable {
|
||||
let count: Int
|
||||
let selected: WindowInfo?
|
||||
let windows: [WindowInfo]?
|
||||
}
|
||||
|
||||
func value(for flag: String) -> String? {
|
||||
guard let idx = CommandLine.arguments.firstIndex(of: flag) else {
|
||||
return nil
|
||||
}
|
||||
let next = CommandLine.arguments.index(after: idx)
|
||||
guard next < CommandLine.arguments.endIndex else {
|
||||
return nil
|
||||
}
|
||||
return CommandLine.arguments[next]
|
||||
}
|
||||
|
||||
let frontmostFlag = CommandLine.arguments.contains("--frontmost")
|
||||
let explicitApp = value(for: "--app")
|
||||
let frontmostName = frontmostFlag ? NSWorkspace.shared.frontmostApplication?.localizedName : nil
|
||||
if frontmostFlag && frontmostName == nil {
|
||||
fputs("{\"count\":0}\n", stderr)
|
||||
exit(1)
|
||||
}
|
||||
let appFilter = (explicitApp ?? frontmostName)?.lowercased()
|
||||
let nameFilter = value(for: "--window-name")?.lowercased()
|
||||
let includeList = CommandLine.arguments.contains("--list")
|
||||
|
||||
let options: CGWindowListOption = [.optionOnScreenOnly, .excludeDesktopElements]
|
||||
guard let raw = CGWindowListCopyWindowInfo(options, kCGNullWindowID) as? [[String: Any]] else {
|
||||
fputs("{\"count\":0}\n", stderr)
|
||||
exit(1)
|
||||
}
|
||||
|
||||
var exactMatches: [WindowInfo] = []
|
||||
var partialMatches: [WindowInfo] = []
|
||||
exactMatches.reserveCapacity(raw.count)
|
||||
partialMatches.reserveCapacity(raw.count)
|
||||
|
||||
for entry in raw {
|
||||
guard let owner = entry[kCGWindowOwnerName as String] as? String else { continue }
|
||||
let ownerLower = owner.lowercased()
|
||||
if let appFilter, !ownerLower.contains(appFilter) { continue }
|
||||
|
||||
let name = (entry[kCGWindowName as String] as? String) ?? ""
|
||||
if let nameFilter, !name.lowercased().contains(nameFilter) { continue }
|
||||
|
||||
guard let number = entry[kCGWindowNumber as String] as? Int else { continue }
|
||||
let layer = (entry[kCGWindowLayer as String] as? Int) ?? 0
|
||||
|
||||
guard let boundsDict = entry[kCGWindowBounds as String] as? [String: Any] else { continue }
|
||||
let x = Int((boundsDict["X"] as? Double) ?? 0)
|
||||
let y = Int((boundsDict["Y"] as? Double) ?? 0)
|
||||
let width = Int((boundsDict["Width"] as? Double) ?? 0)
|
||||
let height = Int((boundsDict["Height"] as? Double) ?? 0)
|
||||
if width <= 0 || height <= 0 { continue }
|
||||
|
||||
let bounds = Bounds(x: x, y: y, width: width, height: height)
|
||||
let area = width * height
|
||||
let info = WindowInfo(id: number, owner: owner, name: name, layer: layer, bounds: bounds, area: area)
|
||||
if let appFilter, ownerLower == appFilter {
|
||||
exactMatches.append(info)
|
||||
} else {
|
||||
partialMatches.append(info)
|
||||
}
|
||||
}
|
||||
|
||||
let windows: [WindowInfo]
|
||||
if appFilter != nil && !exactMatches.isEmpty {
|
||||
windows = exactMatches
|
||||
} else {
|
||||
windows = partialMatches
|
||||
}
|
||||
|
||||
func rank(_ window: WindowInfo) -> (Int, Int) {
|
||||
// Prefer normal-layer windows, then larger area.
|
||||
let layerScore = window.layer == 0 ? 0 : 1
|
||||
return (layerScore, -window.area)
|
||||
}
|
||||
|
||||
let ordered: [WindowInfo]
|
||||
if frontmostFlag {
|
||||
ordered = windows
|
||||
} else {
|
||||
ordered = windows.sorted { rank($0) < rank($1) }
|
||||
}
|
||||
let selected = ordered.first
|
||||
|
||||
let list: [WindowInfo]?
|
||||
if includeList {
|
||||
list = ordered
|
||||
} else {
|
||||
list = nil
|
||||
}
|
||||
|
||||
let response = Response(count: windows.count, selected: selected, windows: list)
|
||||
let encoder = JSONEncoder()
|
||||
encoder.outputFormatting = [.sortedKeys]
|
||||
|
||||
if let data = try? encoder.encode(response),
|
||||
let json = String(data: data, encoding: .utf8) {
|
||||
print(json)
|
||||
} else {
|
||||
fputs("{\"count\":\(windows.count)}\n", stderr)
|
||||
exit(1)
|
||||
}
|
||||
Reference in New Issue
Block a user