Files
Sybil-2/ios/Packages/Sybil/Sources/Sybil/SybilTheme.swift

213 lines
6.8 KiB
Swift
Raw Normal View History

2026-05-02 16:23:00 -07:00
import CoreText
import Foundation
2026-02-20 00:09:02 -08:00
import SwiftUI
2026-05-02 17:10:32 -07:00
import UIKit
2026-02-20 00:09:02 -08:00
2026-05-02 16:23:00 -07:00
enum SybilFontRegistry {
static func registerIfNeeded() {
_ = registeredFonts
}
private static let registeredFonts: Void = {
2026-05-03 22:11:29 -07:00
for fontName in ["Inter", "Orbitron", "StalinistOne-Regular"] {
2026-05-02 16:23:00 -07:00
guard let url = Bundle.main.url(forResource: fontName, withExtension: "ttf", subdirectory: "Fonts") ??
Bundle.main.url(forResource: fontName, withExtension: "ttf")
else {
continue
}
CTFontManagerRegisterFontsForURL(url as CFURL, .process, nil)
}
}()
}
extension Font {
static func sybil(_ textStyle: Font.TextStyle, weight: Font.Weight = .regular) -> Font {
SybilFontRegistry.registerIfNeeded()
return .custom("Inter", size: Self.sybilPointSize(for: textStyle), relativeTo: textStyle)
.weight(weight)
}
static func sybil(size: CGFloat, weight: Font.Weight = .regular) -> Font {
SybilFontRegistry.registerIfNeeded()
return .custom("Inter", size: size)
.weight(weight)
}
private static func sybilPointSize(for textStyle: Font.TextStyle) -> CGFloat {
switch textStyle {
case .largeTitle:
return 34
case .title:
return 28
case .title2:
return 22
case .title3:
return 20
case .headline:
return 17
case .subheadline:
return 15
case .callout:
return 16
case .caption:
return 12
case .caption2:
return 11
case .footnote:
return 13
case .body:
return 17
@unknown default:
return 17
}
}
}
2026-02-20 00:09:02 -08:00
enum SybilTheme {
2026-05-02 16:23:00 -07:00
static let background = Color(red: 0.02, green: 0.02, blue: 0.05)
static let surface = Color(red: 0.05, green: 0.04, blue: 0.10)
static let surfaceStrong = Color(red: 0.07, green: 0.06, blue: 0.14)
static let card = Color(red: 0.06, green: 0.05, blue: 0.12)
static let border = Color(red: 0.24, green: 0.20, blue: 0.38)
static let primary = Color(red: 0.57, green: 0.38, blue: 0.96)
static let primarySoft = Color(red: 0.43, green: 0.25, blue: 0.76)
static let accent = Color(red: 0.31, green: 0.88, blue: 0.95)
static let text = Color(red: 0.96, green: 0.94, blue: 1.0)
static let textMuted = Color(red: 0.65, green: 0.62, blue: 0.76)
static let searchCard = Color(red: 0.07, green: 0.06, blue: 0.14)
static let userBubble = Color(red: 0.29, green: 0.13, blue: 0.65)
static let danger = Color(red: 0.96, green: 0.32, blue: 0.40)
2026-02-20 00:09:02 -08:00
2026-05-02 17:10:32 -07:00
@MainActor static func applySystemAppearance() {
let navAppearance = UINavigationBarAppearance()
navAppearance.configureWithOpaqueBackground()
navAppearance.backgroundColor = UIColor(red: 0.02, green: 0.02, blue: 0.05, alpha: 1)
navAppearance.shadowColor = UIColor(red: 0.24, green: 0.20, blue: 0.38, alpha: 0.9)
navAppearance.titleTextAttributes = [
.foregroundColor: UIColor(red: 0.96, green: 0.94, blue: 1.0, alpha: 1)
]
navAppearance.largeTitleTextAttributes = navAppearance.titleTextAttributes
UINavigationBar.appearance().prefersLargeTitles = false
UINavigationBar.appearance().standardAppearance = navAppearance
UINavigationBar.appearance().compactAppearance = navAppearance
UINavigationBar.appearance().scrollEdgeAppearance = navAppearance
UINavigationBar.appearance().compactScrollEdgeAppearance = navAppearance
}
2026-02-20 00:09:02 -08:00
static var backgroundGradient: LinearGradient {
LinearGradient(
colors: [
2026-05-02 16:23:00 -07:00
Color(red: 0.12, green: 0.08, blue: 0.28),
Color(red: 0.04, green: 0.03, blue: 0.09),
background
2026-02-20 00:09:02 -08:00
],
startPoint: .top,
endPoint: .bottom
)
}
2026-05-02 16:23:00 -07:00
static var brandGradient: LinearGradient {
LinearGradient(
colors: [
Color(red: 1.0, green: 0.55, blue: 0.97),
Color(red: 0.60, green: 0.43, blue: 1.0),
Color(red: 0.40, green: 0.87, blue: 1.0)
],
startPoint: .leading,
endPoint: .trailing
)
}
static var panelGradient: LinearGradient {
LinearGradient(
colors: [
Color(red: 0.07, green: 0.06, blue: 0.15).opacity(0.94),
Color(red: 0.03, green: 0.03, blue: 0.08).opacity(0.96)
],
startPoint: .top,
endPoint: .bottom
)
}
static var primaryGradient: LinearGradient {
LinearGradient(
colors: [
Color(red: 0.43, green: 0.27, blue: 0.92),
Color(red: 0.45, green: 0.09, blue: 0.63)
],
startPoint: .topLeading,
endPoint: .bottomTrailing
)
}
static var selectedRowGradient: LinearGradient {
LinearGradient(
colors: [
primary.opacity(0.56),
primarySoft.opacity(0.48)
],
startPoint: .topLeading,
endPoint: .bottomTrailing
)
}
static var userBubbleGradient: LinearGradient {
LinearGradient(
colors: [
Color(red: 0.38, green: 0.16, blue: 0.80),
Color(red: 0.25, green: 0.08, blue: 0.55)
],
startPoint: .topLeading,
endPoint: .bottomTrailing
)
}
static var composerGradient: LinearGradient {
LinearGradient(
colors: [
Color(red: 0.04, green: 0.04, blue: 0.10).opacity(0.98),
Color(red: 0.09, green: 0.06, blue: 0.16).opacity(0.94)
],
startPoint: .topLeading,
endPoint: .bottomTrailing
)
}
static var toolCallGradient: LinearGradient {
LinearGradient(
colors: [
Color(red: 0.01, green: 0.15, blue: 0.17).opacity(0.70),
Color(red: 0.03, green: 0.09, blue: 0.15).opacity(0.78)
],
startPoint: .leading,
endPoint: .trailing
)
}
static var failedToolCallGradient: LinearGradient {
LinearGradient(
colors: [
danger.opacity(0.18),
Color(red: 0.15, green: 0.03, blue: 0.07).opacity(0.72)
],
startPoint: .leading,
endPoint: .trailing
)
}
}
struct SybilWordmark: View {
var size: CGFloat = 30
var body: some View {
Text("SYBIL")
2026-05-03 22:11:29 -07:00
.font(.custom("Stalinist One", size: size))
2026-05-02 16:23:00 -07:00
.fontWeight(.black)
.tracking(0)
.foregroundStyle(SybilTheme.brandGradient)
.accessibilityLabel("Sybil")
}
2026-02-20 00:09:02 -08:00
}