SwiftUI port of the Android app under ios/, with CarPlay browse and Now Playing, Siri play-media intents, and a build-ipa.sh script that makes an unsigned IPA for sideloading. README gets an iOS section and the vibecoded disclaimer. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
81 lines
3.2 KiB
Swift
81 lines
3.2 KiB
Swift
import SwiftUI
|
|
|
|
extension Color {
|
|
init(hex: UInt32, alpha: Double = 1) {
|
|
self.init(.sRGB, red: Double((hex >> 16) & 0xFF) / 255, green: Double((hex >> 8) & 0xFF) / 255,
|
|
blue: Double(hex & 0xFF) / 255, opacity: alpha)
|
|
}
|
|
}
|
|
|
|
/// The three themes' colours. Black is true black for OLED; no gradients anywhere.
|
|
struct Palette {
|
|
var dark: Bool
|
|
var bg: Color
|
|
var groupedBg: Color
|
|
var cell: Color
|
|
var fill: Color
|
|
var fillStrong: Color
|
|
var label: Color
|
|
var label2: Color
|
|
var label3: Color
|
|
var separator: Color
|
|
var placeholder: Color
|
|
var accent: Color
|
|
var red: Color
|
|
|
|
static func of(_ mode: ThemeMode, _ accent: Accent) -> Palette {
|
|
switch mode {
|
|
case .Black:
|
|
Palette(dark: true, bg: Color(hex: 0x000000), groupedBg: Color(hex: 0x000000), cell: Color(hex: 0x1C1C1E),
|
|
fill: Color(hex: 0x1C1C1E), fillStrong: Color(hex: 0x3A3A3C), label: .white, label2: Color(hex: 0x8D8D93),
|
|
label3: Color(hex: 0x48484A), separator: Color(hex: 0x2C2C2E), placeholder: Color(hex: 0x1C1C1E),
|
|
accent: Color(hex: accent.dark), red: Color(hex: 0xFF453A))
|
|
case .Dark:
|
|
Palette(dark: true, bg: Color(hex: 0x1C1C1E), groupedBg: Color(hex: 0x1C1C1E), cell: Color(hex: 0x2C2C2E),
|
|
fill: Color(hex: 0x2C2C2E), fillStrong: Color(hex: 0x48484A), label: .white, label2: Color(hex: 0x98989D),
|
|
label3: Color(hex: 0x5A5A5E), separator: Color(hex: 0x3D3D40), placeholder: Color(hex: 0x3A3A3C),
|
|
accent: Color(hex: accent.dark), red: Color(hex: 0xFF453A))
|
|
case .White:
|
|
Palette(dark: false, bg: Color(hex: 0xFFFFFF), groupedBg: Color(hex: 0xF2F2F7), cell: Color(hex: 0xFFFFFF),
|
|
fill: Color(hex: 0xEFEFF0), fillStrong: Color(hex: 0xE3E3E8), label: .black, label2: Color(hex: 0x8A8A8E),
|
|
label3: Color(hex: 0xC4C4C7), separator: Color(hex: 0xD8D8DC), placeholder: Color(hex: 0xE5E5EA),
|
|
accent: Color(hex: accent.light), red: Color(hex: 0xFF3B30))
|
|
}
|
|
}
|
|
}
|
|
|
|
private struct PaletteKey: EnvironmentKey {
|
|
static let defaultValue = Palette.of(.Black, .Music)
|
|
}
|
|
|
|
extension EnvironmentValues {
|
|
var palette: Palette {
|
|
get { self[PaletteKey.self] }
|
|
set { self[PaletteKey.self] = newValue }
|
|
}
|
|
}
|
|
|
|
extension View {
|
|
/// A list on the theme's background instead of the system one.
|
|
func themedList(grouped: Bool = true) -> some View { modifier(ThemedList(grouped: grouped)) }
|
|
|
|
/// Rows on the theme's cell colour.
|
|
func themedRow(grouped: Bool = true) -> some View { modifier(ThemedRow(grouped: grouped)) }
|
|
}
|
|
|
|
private struct ThemedList: ViewModifier {
|
|
@Environment(\.palette) private var p
|
|
let grouped: Bool
|
|
func body(content: Content) -> some View {
|
|
content.scrollContentBackground(.hidden).background(grouped ? p.groupedBg : p.bg)
|
|
}
|
|
}
|
|
|
|
private struct ThemedRow: ViewModifier {
|
|
@Environment(\.palette) private var p
|
|
let grouped: Bool
|
|
func body(content: Content) -> some View {
|
|
content.listRowBackground(grouped ? p.cell : p.bg).listRowSeparatorTint(p.separator)
|
|
}
|
|
}
|