From 52263b1a7c734a8b7c1686d68fce9fcf62be2825 Mon Sep 17 00:00:00 2001 From: anna <54419064+jadetheda@users.noreply.github.com> Date: Thu, 30 Jul 2026 15:56:24 -0400 Subject: [PATCH 1/3] Add bsky themes to ColorSet.swift --- .../Sources/DesignSystem/ColorSet.swift | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/Packages/DesignSystem/Sources/DesignSystem/ColorSet.swift b/Packages/DesignSystem/Sources/DesignSystem/ColorSet.swift index 65299f881..f95ec7e9e 100644 --- a/Packages/DesignSystem/Sources/DesignSystem/ColorSet.swift +++ b/Packages/DesignSystem/Sources/DesignSystem/ColorSet.swift @@ -9,6 +9,7 @@ public let availableColorsSets: [ColorSetCouple] = .init(light: MediumLight(), dark: MediumDark()), .init(light: ConstellationLight(), dark: ConstellationDark()), .init(light: ThreadsLight(), dark: ThreadsDark()), + .init(light: BlueskyLight(), dark: BlueskyDark()), ] public protocol ColorSet: Sendable { @@ -39,6 +40,8 @@ public enum ColorSetName: String, Sendable { case constellationDark = "Constellation - Dark" case threadsLight = "Threads - Light" case threadsDark = "Threads - Dark" + case blueskyLight = "Bluesky - Light" + case blueskyDark = "Bluesky - Dark" } public struct ColorSetCouple: Identifiable, Sendable { @@ -203,3 +206,27 @@ public struct ThreadsLight: ColorSet { public init() {} } + +public struct BlueskyDark: ColorSet { + public var name: ColorSetName = .blueskyDark + public var scheme: ColorScheme = .dark + public var tintColor: Color = .init(hex: 0x4D97FF) + public var primaryBackgroundColor: Color = .init(hex: 0x151D28) + public var secondaryBackgroundColor: Color = .init(hex: 0x151D28) + public var labelColor: Color = .white +} + + + public init() {} +} + +public struct BlueskyLight: ColorSet { + public var name: ColorSetName = .blueskyLight + public var scheme: ColorScheme = .light + public var tintColor: Color = .init(hex: 0x006AFF) + public var primaryBackgroundColor: Color = .init(hex: 0xFFFFFF) + public var secondaryBackgroundColor: Color = .init(hex: 0xFFFFFF) + public var labelColor: Color = .black + + public init() {} +} From f324969139dd183d6975ff218ce480215a7db49d Mon Sep 17 00:00:00 2001 From: anna <54419064+jadetheda@users.noreply.github.com> Date: Thu, 30 Jul 2026 15:59:23 -0400 Subject: [PATCH 2/3] Add bsky themes to Theme.swift --- .../Sources/DesignSystem/Theme.swift | 125 +++++++++++++++++- 1 file changed, 124 insertions(+), 1 deletion(-) diff --git a/Packages/DesignSystem/Sources/DesignSystem/Theme.swift b/Packages/DesignSystem/Sources/DesignSystem/Theme.swift index 06674636f..999bde561 100644 --- a/Packages/DesignSystem/Sources/DesignSystem/Theme.swift +++ b/Packages/DesignSystem/Sources/DesignSystem/Theme.swift @@ -1,5 +1,7 @@ import Combine import SwiftUI +import CoreText +import CoreGraphics @MainActor @Observable @@ -52,6 +54,7 @@ public final class Theme { case openDyslexic case hyperLegible case SFRounded + case inter case custom public var title: LocalizedStringKey { @@ -64,6 +67,8 @@ public final class Theme { "Hyper Legible" case .SFRounded: "SF Rounded" + case .inter: + "Inter (Bluesky)" case .custom: "settings.display.font.custom" } @@ -351,8 +356,8 @@ public final class Theme { compactLayoutPadding = themeStorage.compactLayoutPadding avatarAnimated = themeStorage.avatarAnimated selectedSet = storedSet - computeContrastingTintColor() + registerCustomFonts() } public static var allColorSet: [ColorSet] { @@ -371,6 +376,8 @@ public final class Theme { ConstellationDark(), ThreadsLight(), ThreadsDark(), + BlueskyLight(), + BlueskyDark(), ] } @@ -379,6 +386,122 @@ public final class Theme { setColor(withName: set) } + public func registerCustomFonts() { + let fileManager = FileManager.default + guard let appSupportURL = fileManager.urls(for: .applicationSupportDirectory, in: .userDomainMask).first else { + return + } + let customFontsDir = appSupportURL.appendingPathComponent("CustomFonts", isDirectory: true) + guard fileManager.fileExists(atPath: customFontsDir.path) else { return } + + do { + let fileURLs = try fileManager.contentsOfDirectory(at: customFontsDir, includingPropertiesForKeys: nil) + for fileURL in fileURLs { + if ["ttf", "otf"].contains(fileURL.pathExtension.lowercased()) { + var error: Unmanaged? + CTFontManagerRegisterFontsForURL(fileURL as CFURL, .process, &error) + } + } + } catch { + print("Failed to register custom fonts: \(error)") + } + } + + public func importFont(from url: URL) -> String? { + let fileManager = FileManager.default + guard let appSupportURL = fileManager.urls(for: .applicationSupportDirectory, in: .userDomainMask).first else { + return nil + } + let customFontsDir = appSupportURL.appendingPathComponent("CustomFonts", isDirectory: true) + + do { + if !fileManager.fileExists(atPath: customFontsDir.path) { + try fileManager.createDirectory(at: customFontsDir, withIntermediateDirectories: true, attributes: nil) + } + + guard let fontDataProvider = CGDataProvider(url: url as CFURL), + let cgFont = CGFont(fontDataProvider), + let postScriptName = cgFont.postScriptName as String? else { + return nil + } + + let destinationURL = customFontsDir.appendingPathComponent("\(postScriptName).\(url.pathExtension)") + + if fileManager.fileExists(atPath: destinationURL.path) { + try? fileManager.removeItem(at: destinationURL) + } + + try fileManager.copyItem(at: url, to: destinationURL) + + var error: Unmanaged? + if CTFontManagerRegisterFontsForURL(destinationURL as CFURL, .process, &error) { + return postScriptName + } else { + // If error is "already registered" it's fine, otherwise we probably should still return it + // since it was successfully copied, but logging is better. + if let err = error?.takeRetainedValue() { + print("Font registration warning/error: \(err)") + } + return postScriptName + } + } catch { + print("Failed to import font: \(error)") + return nil + } + } + + public func getImportedFonts() -> [String] { + let fileManager = FileManager.default + guard let appSupportURL = fileManager.urls(for: .applicationSupportDirectory, in: .userDomainMask).first else { + return [] + } + let customFontsDir = appSupportURL.appendingPathComponent("CustomFonts", isDirectory: true) + guard fileManager.fileExists(atPath: customFontsDir.path) else { return [] } + + do { + let fileURLs = try fileManager.contentsOfDirectory(at: customFontsDir, includingPropertiesForKeys: nil) + var fontNames: [String] = [] + for fileURL in fileURLs { + if ["ttf", "otf"].contains(fileURL.pathExtension.lowercased()) { + if let fontDataProvider = CGDataProvider(url: fileURL as CFURL), + let cgFont = CGFont(fontDataProvider), + let postScriptName = cgFont.postScriptName as String? { + fontNames.append(postScriptName) + } + } + } + return fontNames.sorted() + } catch { + return [] + } + } + + public func deleteImportedFont(name: String) { + let fileManager = FileManager.default + guard let appSupportURL = fileManager.urls(for: .applicationSupportDirectory, in: .userDomainMask).first else { + return + } + let customFontsDir = appSupportURL.appendingPathComponent("CustomFonts", isDirectory: true) + guard fileManager.fileExists(atPath: customFontsDir.path) else { return } + + do { + let fileURLs = try fileManager.contentsOfDirectory(at: customFontsDir, includingPropertiesForKeys: nil) + for fileURL in fileURLs { + if ["ttf", "otf"].contains(fileURL.pathExtension.lowercased()) { + if let fontDataProvider = CGDataProvider(url: fileURL as CFURL), + let cgFont = CGFont(fontDataProvider), + let postScriptName = cgFont.postScriptName as String?, + postScriptName == name { + var error: Unmanaged? + CTFontManagerUnregisterFontsForURL(fileURL as CFURL, .process, &error) + try fileManager.removeItem(at: fileURL) + break + } + } + } + } catch {} + } + public func setColor(withName name: ColorSetName) { let colorSet = Theme.allColorSet.filter { $0.name == name }.first ?? IceCubeDark() selectedScheme = colorSet.scheme From ea88c659a9afc6d88f40742d5fd268ae74aa5b50 Mon Sep 17 00:00:00 2001 From: anna <54419064+jadetheda@users.noreply.github.com> Date: Thu, 30 Jul 2026 17:49:14 -0400 Subject: [PATCH 3/3] Remove unrelated guff from --- .../Sources/DesignSystem/ColorSet.swift | 2 - .../Sources/DesignSystem/Theme.swift | 123 +----------------- 2 files changed, 1 insertion(+), 124 deletions(-) diff --git a/Packages/DesignSystem/Sources/DesignSystem/ColorSet.swift b/Packages/DesignSystem/Sources/DesignSystem/ColorSet.swift index f95ec7e9e..7511f08bb 100644 --- a/Packages/DesignSystem/Sources/DesignSystem/ColorSet.swift +++ b/Packages/DesignSystem/Sources/DesignSystem/ColorSet.swift @@ -214,8 +214,6 @@ public struct BlueskyDark: ColorSet { public var primaryBackgroundColor: Color = .init(hex: 0x151D28) public var secondaryBackgroundColor: Color = .init(hex: 0x151D28) public var labelColor: Color = .white -} - public init() {} } diff --git a/Packages/DesignSystem/Sources/DesignSystem/Theme.swift b/Packages/DesignSystem/Sources/DesignSystem/Theme.swift index 999bde561..68cbcaade 100644 --- a/Packages/DesignSystem/Sources/DesignSystem/Theme.swift +++ b/Packages/DesignSystem/Sources/DesignSystem/Theme.swift @@ -1,7 +1,5 @@ import Combine import SwiftUI -import CoreText -import CoreGraphics @MainActor @Observable @@ -54,7 +52,6 @@ public final class Theme { case openDyslexic case hyperLegible case SFRounded - case inter case custom public var title: LocalizedStringKey { @@ -67,8 +64,6 @@ public final class Theme { "Hyper Legible" case .SFRounded: "SF Rounded" - case .inter: - "Inter (Bluesky)" case .custom: "settings.display.font.custom" } @@ -356,8 +351,8 @@ public final class Theme { compactLayoutPadding = themeStorage.compactLayoutPadding avatarAnimated = themeStorage.avatarAnimated selectedSet = storedSet + computeContrastingTintColor() - registerCustomFonts() } public static var allColorSet: [ColorSet] { @@ -386,122 +381,6 @@ public final class Theme { setColor(withName: set) } - public func registerCustomFonts() { - let fileManager = FileManager.default - guard let appSupportURL = fileManager.urls(for: .applicationSupportDirectory, in: .userDomainMask).first else { - return - } - let customFontsDir = appSupportURL.appendingPathComponent("CustomFonts", isDirectory: true) - guard fileManager.fileExists(atPath: customFontsDir.path) else { return } - - do { - let fileURLs = try fileManager.contentsOfDirectory(at: customFontsDir, includingPropertiesForKeys: nil) - for fileURL in fileURLs { - if ["ttf", "otf"].contains(fileURL.pathExtension.lowercased()) { - var error: Unmanaged? - CTFontManagerRegisterFontsForURL(fileURL as CFURL, .process, &error) - } - } - } catch { - print("Failed to register custom fonts: \(error)") - } - } - - public func importFont(from url: URL) -> String? { - let fileManager = FileManager.default - guard let appSupportURL = fileManager.urls(for: .applicationSupportDirectory, in: .userDomainMask).first else { - return nil - } - let customFontsDir = appSupportURL.appendingPathComponent("CustomFonts", isDirectory: true) - - do { - if !fileManager.fileExists(atPath: customFontsDir.path) { - try fileManager.createDirectory(at: customFontsDir, withIntermediateDirectories: true, attributes: nil) - } - - guard let fontDataProvider = CGDataProvider(url: url as CFURL), - let cgFont = CGFont(fontDataProvider), - let postScriptName = cgFont.postScriptName as String? else { - return nil - } - - let destinationURL = customFontsDir.appendingPathComponent("\(postScriptName).\(url.pathExtension)") - - if fileManager.fileExists(atPath: destinationURL.path) { - try? fileManager.removeItem(at: destinationURL) - } - - try fileManager.copyItem(at: url, to: destinationURL) - - var error: Unmanaged? - if CTFontManagerRegisterFontsForURL(destinationURL as CFURL, .process, &error) { - return postScriptName - } else { - // If error is "already registered" it's fine, otherwise we probably should still return it - // since it was successfully copied, but logging is better. - if let err = error?.takeRetainedValue() { - print("Font registration warning/error: \(err)") - } - return postScriptName - } - } catch { - print("Failed to import font: \(error)") - return nil - } - } - - public func getImportedFonts() -> [String] { - let fileManager = FileManager.default - guard let appSupportURL = fileManager.urls(for: .applicationSupportDirectory, in: .userDomainMask).first else { - return [] - } - let customFontsDir = appSupportURL.appendingPathComponent("CustomFonts", isDirectory: true) - guard fileManager.fileExists(atPath: customFontsDir.path) else { return [] } - - do { - let fileURLs = try fileManager.contentsOfDirectory(at: customFontsDir, includingPropertiesForKeys: nil) - var fontNames: [String] = [] - for fileURL in fileURLs { - if ["ttf", "otf"].contains(fileURL.pathExtension.lowercased()) { - if let fontDataProvider = CGDataProvider(url: fileURL as CFURL), - let cgFont = CGFont(fontDataProvider), - let postScriptName = cgFont.postScriptName as String? { - fontNames.append(postScriptName) - } - } - } - return fontNames.sorted() - } catch { - return [] - } - } - - public func deleteImportedFont(name: String) { - let fileManager = FileManager.default - guard let appSupportURL = fileManager.urls(for: .applicationSupportDirectory, in: .userDomainMask).first else { - return - } - let customFontsDir = appSupportURL.appendingPathComponent("CustomFonts", isDirectory: true) - guard fileManager.fileExists(atPath: customFontsDir.path) else { return } - - do { - let fileURLs = try fileManager.contentsOfDirectory(at: customFontsDir, includingPropertiesForKeys: nil) - for fileURL in fileURLs { - if ["ttf", "otf"].contains(fileURL.pathExtension.lowercased()) { - if let fontDataProvider = CGDataProvider(url: fileURL as CFURL), - let cgFont = CGFont(fontDataProvider), - let postScriptName = cgFont.postScriptName as String?, - postScriptName == name { - var error: Unmanaged? - CTFontManagerUnregisterFontsForURL(fileURL as CFURL, .process, &error) - try fileManager.removeItem(at: fileURL) - break - } - } - } - } catch {} - } - public func setColor(withName name: ColorSetName) { let colorSet = Theme.allColorSet.filter { $0.name == name }.first ?? IceCubeDark() selectedScheme = colorSet.scheme