Surface the existing pin (keep-from-cull) and per-file delete actions as visible inline buttons on each offline cache row instead of context-menu-only: a star toggles protection from auto-cull (and restore-if-missing), a trash culls that file early. Aligns wording/icons to the star metaphor. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
47 lines
No EOL
2 KiB
Swift
47 lines
No EOL
2 KiB
Swift
import Foundation
|
|
|
|
/// One macOS audio output endpoint — the HDMI sink that pairs with an external
|
|
/// display, or the built-in speakers on the laptop.
|
|
public struct AudioOutputInfo: Identifiable, Sendable, Equatable, Codable {
|
|
public var id: UInt32 { deviceId }
|
|
/// CoreAudio `AudioDeviceID` — persisted as VLC's `auhal-audio-device`.
|
|
public let deviceId: UInt32
|
|
public let name: String
|
|
public let isBuiltIn: Bool
|
|
|
|
public init(deviceId: UInt32, name: String, isBuiltIn: Bool) {
|
|
self.deviceId = deviceId
|
|
self.name = name
|
|
self.isBuiltIn = isBuiltIn
|
|
}
|
|
|
|
public var shortLabel: String {
|
|
if isBuiltIn { return "Built-in Speakers" }
|
|
return name.isEmpty ? "External Audio" : name
|
|
}
|
|
|
|
/// Heuristic: laptop speakers / built-in headphone jack vs HDMI/DisplayPort sinks.
|
|
public static func inferBuiltIn(name: String) -> Bool {
|
|
let n = name.lowercased()
|
|
return n.contains("built-in") || n.hasSuffix("speakers")
|
|
}
|
|
|
|
/// Pick the audio sink that belongs with `display`. External displays are
|
|
/// matched by name (e.g. SAMSUNG display → SAMSUNG HDMI); built-in falls back
|
|
/// to the laptop speakers rather than the system-default output device.
|
|
public static func pick(for display: DisplayInfo, from devices: [AudioOutputInfo]) -> AudioOutputInfo? {
|
|
guard !devices.isEmpty else { return nil }
|
|
if display.isBuiltIn {
|
|
return devices.first(where: \.isBuiltIn) ?? devices.first
|
|
}
|
|
let dn = display.name.trimmingCharacters(in: .whitespacesAndNewlines).lowercased()
|
|
if !dn.isEmpty {
|
|
if let exact = devices.first(where: { $0.name.lowercased() == dn }) { return exact }
|
|
if let partial = devices.first(where: {
|
|
let n = $0.name.lowercased()
|
|
return n.contains(dn) || dn.contains(n)
|
|
}) { return partial }
|
|
}
|
|
return devices.first(where: { !$0.isBuiltIn }) ?? devices.first
|
|
}
|
|
} |