[Service] New UI

This commit is contained in:
2026-09-13 21:12:44 -07:00
parent e473d00f4f
commit 1b1ca16fdd
45 changed files with 2726 additions and 205 deletions
@@ -118,6 +118,7 @@ public final class PortalClient: @unchecked Sendable {
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Accept")
PortalClientIdentity.apply(to: &request)
let data: Data
let response: URLResponse
@@ -190,6 +191,7 @@ public final class PortalClient: @unchecked Sendable {
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.setValue("application/json", forHTTPHeaderField: "Accept")
PortalClientIdentity.apply(to: &request)
let payload: [String: String] = [
"pairingId": session.pairingId,
@@ -447,6 +449,7 @@ public final class PortalClient: @unchecked Sendable {
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
PortalClientIdentity.apply(to: &request)
let payload: [String: String] = [
"pairingId": session.pairingId,
@@ -0,0 +1,58 @@
//
// PortalClientIdentity.swift
// PortalKit
//
// Identity headers sent on pairing (and available for other Portal requests).
//
import Foundation
import Darwin
/// Values for `X-Client-Name`, `X-Client-Version`, `X-Device-Model`, and `User-Agent`.
public enum PortalClientIdentity {
public static var clientName: String {
if let name = Bundle.main.object(forInfoDictionaryKey: "CFBundleDisplayName") as? String,
!name.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
return name
}
if let name = Bundle.main.object(forInfoDictionaryKey: "CFBundleName") as? String,
!name.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
return name
}
return "PortalCam"
}
public static var clientVersion: String {
if let v = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String,
!v.isEmpty {
return v
}
return PortalKitVersion.version
}
/// Hardware model identifier, e.g. `MacBookPro18,1`.
public static var deviceModel: String {
hwModel() ?? "Mac"
}
public static var userAgent: String {
"PortalCam/\(clientVersion) (macOS; \(deviceModel))"
}
/// Apply identity headers used by Portal TV pairing.
public static func apply(to request: inout URLRequest) {
request.setValue(clientName, forHTTPHeaderField: "X-Client-Name")
request.setValue(clientVersion, forHTTPHeaderField: "X-Client-Version")
request.setValue(deviceModel, forHTTPHeaderField: "X-Device-Model")
request.setValue(userAgent, forHTTPHeaderField: "User-Agent")
}
private static func hwModel() -> String? {
var size = 0
guard sysctlbyname("hw.model", nil, &size, nil, 0) == 0, size > 0 else { return nil }
var buf = [CChar](repeating: 0, count: size)
guard sysctlbyname("hw.model", &buf, &size, nil, 0) == 0 else { return nil }
let s = String(cString: buf).trimmingCharacters(in: .whitespacesAndNewlines)
return s.isEmpty ? nil : s
}
}