Der Schriftzug gehört zum Aufmacher, nicht zu sich selbst

`AsyncImage` bringt seine eigene Einblendung mit und fängt erst an zu laden,
wenn es erscheint. Beim Überblenden des Aufmachers blendete sich der Schriftzug
des Films deshalb getrennt und später ein, während Titelzeile, Beschreibung und
Knöpfe längst mitgewandert waren — zwei Bewegungen, wo eine sein soll.

Jetzt lädt er über denselben Zwischenspeicher wie alle anderen Bilder, und das
Karussell holt ihn zusammen mit dem Hintergrundbild vor. Beim Erscheinen liegt
er bereit und macht mit, was der Aufmacher macht.

Dazu ein kleines Hilfsprogramm, das die Fensterkennung eines laufenden
Programms ausgibt, damit `screencapture -l` genau dessen Fenster fotografiert
und sonst nichts vom Bildschirm. Das System-Python bringt PyObjC nicht mehr
mit, und ohne Kennung gibt es keinen Weg dorthin.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Scarriffle
2026-09-13 17:25:32 +02:00
co-authored by Claude Opus 5
parent 7155f50bac
commit e5e01838f7
3 changed files with 93 additions and 26 deletions
+45
View File
@@ -0,0 +1,45 @@
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 ?? "Flixy"
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 }
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)