Files
Flaro/Tools/WindowShot/main.swift
T
ScarriffleandClaude Opus 5 4b9a1e7b67 Flixy heißt ab hier Halo
Reine Umbenennung, kein Verhalten geändert — bewusst als eigener Commit,
damit „umbenannt" und „umgebaut" im Nachhinein auseinanderzuhalten sind.

712 mal `Flixy`, 778 mal `flixy`, 7 mal `FLIXY` in 169 Dateien, dazu 25
Pfade. Ziele: `Halo`, `HaloTV`, `HaloTopShelf`, `HaloUITests`, `HaloKit` mit
`HaloDesign`, `HaloDownloads`, `HaloPlayback` und `HaloProbe`. `MediaKit`,
`JellyfinKit` und `DownloadWidget` tragen den Namen nicht und bleiben.

`docs/superpowers/specs/` ist ausgenommen. Datierte Entwurfsdokumente sind
ein Protokoll getroffener Entscheidungen; sie nachträglich umzuschreiben
hiesse, die Vergangenheit zu glätten.

**Die Bundle-ID wandert mit** — ausdrücklich so gewollt, mit bekanntem Preis:
`com.scarriffleservices.halo` ist für iOS eine *andere* App. Die installierte
Fassung bleibt daneben stehen, mit Downloads und Anmeldung in ihrem Container;
Halo startet leer. Einmal neu anmelden, heruntergeladene Folgen einmal neu
laden. Dasselbe gilt für die App-Gruppe, die sich TV-App und Top Shelf teilen.

**Eine Folge davon, die beim Entscheiden noch nicht auf dem Tisch lag:** der
Aufräumschritt aus dem vorigen Commit kommt an die alten Plex-Token nicht mehr
heran. Der Schlüsselbund grenzt nach Team und Bundle-Kennung ab — was unter
`…flixy` liegt, ist für `…halo` unerreichbar. Erledigt wird es trotzdem, nur
anders: **die alte App löschen.** iOS räumt deren Schlüsselbund-Einträge beim
Deinstallieren mit ab. Der Code bleibt stehen, weil er für jeden Stand vor
dieser Umbenennung richtig ist und im Jahr einen Startvergleich kostet.

Die Projektdatei ist aus `project.yml` neu erzeugt, der alte Ordner gelöscht
statt umbenannt — sie steht ohnehin nicht im Repo.

Das Verzeichnis des Repos selbst heißt weiter `Flixy`; das umzubenennen ist
ein Handgriff ausserhalb des Projekts.

368 Tests grün. iOS und tvOS bauen.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-03 11:12:32 +02:00

50 lines
2.1 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import CoreGraphics
import Foundation
// Sucht das größte sichtbare Fenster eines Programms und gibt seine Kennung
// aus. Damit lässt sich `screencapture -l<kennung>` aufrufen — das fotografiert
// genau dieses eine Fenster und sonst nichts vom Bildschirm.
//
// Gebaut, weil es sonst keinen Weg dorthin gibt: das System-Python bringt
// PyObjC nicht mehr mit, und `screencapture` kennt nur Kennungen, keine Namen.
let name = CommandLine.arguments.dropFirst().first ?? "Halo"
/// Zweites Argument: die Prozesskennung. Nötig, sobald zwei Bauarten
/// desselben Programms gleichzeitig laufen — sonst trifft es die falsche.
let wantedPID = CommandLine.arguments.dropFirst(2).first.flatMap(Int.init)
guard let windows = CGWindowListCopyWindowInfo(.optionOnScreenOnly, kCGNullWindowID) as? [[String: Any]] else {
FileHandle.standardError.write(Data("Fensterliste nicht lesbar\n".utf8))
exit(2)
}
let candidates = windows.filter { window in
guard let owner = window[kCGWindowOwnerName as String] as? String, owner == name else { return false }
// Ebene 0 ist das normale Fenster; alles darüber sind Menüs und Schatten.
guard (window[kCGWindowLayer as String] as? Int) == 0 else { return false }
if let wantedPID, (window[kCGWindowOwnerPID as String] as? Int) != wantedPID { return false }
return true
}
let largest = candidates.max { a, b in
func area(_ w: [String: Any]) -> Double {
guard let bounds = w[kCGWindowBounds as String] as? [String: Any],
let width = bounds["Width"] as? Double,
let height = bounds["Height"] as? Double
else { return 0 }
return width * height
}
return area(a) < area(b)
}
guard let window = largest, let id = window[kCGWindowNumber as String] as? Int else {
FileHandle.standardError.write(Data("Kein sichtbares Fenster von „\(name)“\n".utf8))
exit(1)
}
if let bounds = window[kCGWindowBounds as String] as? [String: Any],
let width = bounds["Width"] as? Double, let height = bounds["Height"] as? Double {
FileHandle.standardError.write(Data("\(name): \(Int(width))×\(Int(height))\n".utf8))
}
print(id)