diff --git a/bitchat/Localizable.xcstrings b/bitchat/Localizable.xcstrings index f34ed351..98673b09 100644 --- a/bitchat/Localizable.xcstrings +++ b/bitchat/Localizable.xcstrings @@ -3210,6 +3210,931 @@ } } }, + "app_info.appearance.nord" : { + "extractionState" : "manual", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "nord" + } + }, + "bn" : { + "stringUnit" : { + "state" : "translated", + "value" : "nord" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "nord" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "nord" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "nord" + } + }, + "fa" : { + "stringUnit" : { + "state" : "translated", + "value" : "nord" + } + }, + "fil" : { + "stringUnit" : { + "state" : "translated", + "value" : "nord" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "nord" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "nord" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "nord" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "nord" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "nord" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "nord" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "nord" + } + }, + "ms" : { + "stringUnit" : { + "state" : "translated", + "value" : "nord" + } + }, + "ne" : { + "stringUnit" : { + "state" : "translated", + "value" : "nord" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "nord" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "nord" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "nord" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "nord" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "nord" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "nord" + } + }, + "ta" : { + "stringUnit" : { + "state" : "translated", + "value" : "nord" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "nord" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "nord" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "nord" + } + }, + "ur" : { + "stringUnit" : { + "state" : "translated", + "value" : "nord" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "nord" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "nord" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "nord" + } + } + } + }, + "app_info.appearance.dracula" : { + "extractionState" : "manual", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "dracula" + } + }, + "bn" : { + "stringUnit" : { + "state" : "translated", + "value" : "dracula" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "dracula" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "dracula" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "dracula" + } + }, + "fa" : { + "stringUnit" : { + "state" : "translated", + "value" : "dracula" + } + }, + "fil" : { + "stringUnit" : { + "state" : "translated", + "value" : "dracula" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "dracula" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "dracula" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "dracula" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "dracula" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "dracula" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "dracula" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "dracula" + } + }, + "ms" : { + "stringUnit" : { + "state" : "translated", + "value" : "dracula" + } + }, + "ne" : { + "stringUnit" : { + "state" : "translated", + "value" : "dracula" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "dracula" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "dracula" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "dracula" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "dracula" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "dracula" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "dracula" + } + }, + "ta" : { + "stringUnit" : { + "state" : "translated", + "value" : "dracula" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "dracula" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "dracula" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "dracula" + } + }, + "ur" : { + "stringUnit" : { + "state" : "translated", + "value" : "dracula" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "dracula" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "dracula" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "dracula" + } + } + } + }, + "app_info.appearance.solarized_dark" : { + "extractionState" : "manual", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "solarized dark" + } + }, + "bn" : { + "stringUnit" : { + "state" : "translated", + "value" : "solarized dark" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "solarized dark" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "solarized dark" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "solarized dark" + } + }, + "fa" : { + "stringUnit" : { + "state" : "translated", + "value" : "solarized dark" + } + }, + "fil" : { + "stringUnit" : { + "state" : "translated", + "value" : "solarized dark" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "solarized dark" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "solarized dark" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "solarized dark" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "solarized dark" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "solarized dark" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "solarized dark" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "solarized dark" + } + }, + "ms" : { + "stringUnit" : { + "state" : "translated", + "value" : "solarized dark" + } + }, + "ne" : { + "stringUnit" : { + "state" : "translated", + "value" : "solarized dark" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "solarized dark" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "solarized dark" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "solarized dark" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "solarized dark" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "solarized dark" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "solarized dark" + } + }, + "ta" : { + "stringUnit" : { + "state" : "translated", + "value" : "solarized dark" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "solarized dark" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "solarized dark" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "solarized dark" + } + }, + "ur" : { + "stringUnit" : { + "state" : "translated", + "value" : "solarized dark" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "solarized dark" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "solarized dark" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "solarized dark" + } + } + } + }, + "app_info.appearance.tokyo_night" : { + "extractionState" : "manual", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "tokyo night" + } + }, + "bn" : { + "stringUnit" : { + "state" : "translated", + "value" : "tokyo night" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "tokyo night" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "tokyo night" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "tokyo night" + } + }, + "fa" : { + "stringUnit" : { + "state" : "translated", + "value" : "tokyo night" + } + }, + "fil" : { + "stringUnit" : { + "state" : "translated", + "value" : "tokyo night" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "tokyo night" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "tokyo night" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "tokyo night" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "tokyo night" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "tokyo night" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "tokyo night" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "tokyo night" + } + }, + "ms" : { + "stringUnit" : { + "state" : "translated", + "value" : "tokyo night" + } + }, + "ne" : { + "stringUnit" : { + "state" : "translated", + "value" : "tokyo night" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "tokyo night" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "tokyo night" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "tokyo night" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "tokyo night" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "tokyo night" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "tokyo night" + } + }, + "ta" : { + "stringUnit" : { + "state" : "translated", + "value" : "tokyo night" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "tokyo night" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "tokyo night" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "tokyo night" + } + }, + "ur" : { + "stringUnit" : { + "state" : "translated", + "value" : "tokyo night" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "tokyo night" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "tokyo night" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "tokyo night" + } + } + } + }, + "app_info.appearance.monochrome" : { + "extractionState" : "manual", + "localizations" : { + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "monochrome" + } + }, + "bn" : { + "stringUnit" : { + "state" : "translated", + "value" : "monochrome" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "monochrome" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "monochrome" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "monochrome" + } + }, + "fa" : { + "stringUnit" : { + "state" : "translated", + "value" : "monochrome" + } + }, + "fil" : { + "stringUnit" : { + "state" : "translated", + "value" : "monochrome" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "monochrome" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "monochrome" + } + }, + "hi" : { + "stringUnit" : { + "state" : "translated", + "value" : "monochrome" + } + }, + "id" : { + "stringUnit" : { + "state" : "translated", + "value" : "monochrome" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "monochrome" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "monochrome" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "monochrome" + } + }, + "ms" : { + "stringUnit" : { + "state" : "translated", + "value" : "monochrome" + } + }, + "ne" : { + "stringUnit" : { + "state" : "translated", + "value" : "monochrome" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "monochrome" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "monochrome" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "monochrome" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "monochrome" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "monochrome" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "monochrome" + } + }, + "ta" : { + "stringUnit" : { + "state" : "translated", + "value" : "monochrome" + } + }, + "th" : { + "stringUnit" : { + "state" : "translated", + "value" : "monochrome" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "monochrome" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "monochrome" + } + }, + "ur" : { + "stringUnit" : { + "state" : "translated", + "value" : "monochrome" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "monochrome" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "monochrome" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "monochrome" + } + } + } + }, "app_info.appearance.title" : { "extractionState" : "manual", "localizations" : { diff --git a/bitchat/Utils/Theme.swift b/bitchat/Utils/Theme.swift index ec0283e0..138f2eaf 100644 --- a/bitchat/Utils/Theme.swift +++ b/bitchat/Utils/Theme.swift @@ -12,6 +12,11 @@ import SwiftUI enum AppTheme: String, CaseIterable, Identifiable { case matrix case liquidGlass + case nord + case dracula + case solarizedDark + case tokyoNight + case monochrome var id: String { rawValue } @@ -22,6 +27,11 @@ enum AppTheme: String, CaseIterable, Identifiable { switch self { case .matrix: return "app_info.appearance.matrix" case .liquidGlass: return "app_info.appearance.liquid_glass" + case .nord: return "app_info.appearance.nord" + case .dracula: return "app_info.appearance.dracula" + case .solarizedDark: return "app_info.appearance.solarized_dark" + case .tokyoNight: return "app_info.appearance.tokyo_night" + case .monochrome: return "app_info.appearance.monochrome" } } @@ -31,13 +41,21 @@ enum AppTheme: String, CaseIterable, Identifiable { switch self { case .matrix: return .monospaced case .liquidGlass: return .default + case .nord: return .default + case .dracula: return .monospaced + case .solarizedDark: return .monospaced + case .tokyoNight: return .default + case .monochrome: return .default } } /// Whether chrome surfaces (header/composer bars, input field) render as /// translucent glass/material instead of the flat matrix background. var usesGlassChrome: Bool { - self == .liquidGlass + switch self { + case .liquidGlass: return true + case .matrix, .nord, .dracula, .solarizedDark, .tokyoNight, .monochrome: return false + } } /// Discriminator mixed into per-message formatting caches so cached @@ -47,16 +65,24 @@ enum AppTheme: String, CaseIterable, Identifiable { switch self { case .matrix: return "" case .liquidGlass: return "lg:" + case .nord: return "nd:" + case .dracula: return "dc:" + case .solarizedDark: return "sd:" + case .tokyoNight: return "tn:" + case .monochrome: return "mc:" } } /// Resolves the semantic color palette for this theme under the given color scheme. func palette(for colorScheme: ColorScheme) -> ThemePalette { switch self { - case .matrix: - return .matrix(colorScheme) - case .liquidGlass: - return .liquidGlass(colorScheme) + case .matrix: return .matrix(colorScheme) + case .liquidGlass: return .liquidGlass(colorScheme) + case .nord: return .nord(colorScheme) + case .dracula: return .dracula(colorScheme) + case .solarizedDark: return .solarizedDark(colorScheme) + case .tokyoNight: return .tokyoNight(colorScheme) + case .monochrome: return .monochrome(colorScheme) } } } @@ -109,6 +135,76 @@ struct ThemePalette { ) } + static func nord(_ colorScheme: ColorScheme) -> ThemePalette { + let isDark = colorScheme == .dark + return ThemePalette( + background: isDark ? Color(red: 0.18, green: 0.20, blue: 0.25) : Color(red: 0.93, green: 0.94, blue: 0.96), + primary: isDark ? Color(red: 0.93, green: 0.94, blue: 0.96) : Color(red: 0.18, green: 0.20, blue: 0.25), + secondary: isDark ? Color(red: 0.61, green: 0.66, blue: 0.74) : Color(red: 0.30, green: 0.34, blue: 0.40), + accent: isDark ? Color(red: 0.53, green: 0.75, blue: 0.82) : Color(red: 0.37, green: 0.51, blue: 0.67), + locationAccent: isDark ? Color(red: 0.64, green: 0.75, blue: 0.55) : Color(red: 0.40, green: 0.55, blue: 0.31), + accentBlue: isDark ? Color(red: 0.51, green: 0.63, blue: 0.76) : Color(red: 0.51, green: 0.63, blue: 0.76), + alertRed: isDark ? Color(red: 0.75, green: 0.38, blue: 0.42) : Color(red: 0.75, green: 0.38, blue: 0.42), + divider: isDark ? Color(red: 0.30, green: 0.34, blue: 0.40) : Color(red: 0.85, green: 0.87, blue: 0.91) + ) + } + + static func dracula(_ colorScheme: ColorScheme) -> ThemePalette { + let isDark = colorScheme == .dark + return ThemePalette( + background: isDark ? Color(red: 0.16, green: 0.16, blue: 0.21) : Color(red: 0.97, green: 0.97, blue: 0.95), + primary: isDark ? Color(red: 0.97, green: 0.97, blue: 0.95) : Color(red: 0.16, green: 0.16, blue: 0.21), + secondary: isDark ? Color(red: 0.38, green: 0.45, blue: 0.64) : Color(red: 0.38, green: 0.45, blue: 0.64), + accent: isDark ? Color(red: 1.0, green: 0.47, blue: 0.78) : Color(red: 0.80, green: 0.28, blue: 0.56), + locationAccent: isDark ? Color(red: 0.31, green: 0.98, blue: 0.48) : Color(red: 0.22, green: 0.60, blue: 0.33), + accentBlue: isDark ? Color(red: 0.55, green: 0.91, blue: 0.99) : Color(red: 0.27, green: 0.73, blue: 0.83), + alertRed: isDark ? Color(red: 1.0, green: 0.33, blue: 0.33) : Color(red: 0.82, green: 0.18, blue: 0.18), + divider: isDark ? Color(red: 0.27, green: 0.28, blue: 0.35) : Color(red: 0.88, green: 0.88, blue: 0.88) + ) + } + + static func solarizedDark(_ colorScheme: ColorScheme) -> ThemePalette { + let isDark = colorScheme == .dark + return ThemePalette( + background: isDark ? Color(red: 0.0, green: 0.17, blue: 0.21) : Color(red: 0.99, green: 0.96, blue: 0.89), + primary: isDark ? Color(red: 0.51, green: 0.58, blue: 0.59) : Color(red: 0.40, green: 0.48, blue: 0.51), + secondary: isDark ? Color(red: 0.40, green: 0.48, blue: 0.51) : Color(red: 0.51, green: 0.58, blue: 0.59), + accent: isDark ? Color(red: 0.16, green: 0.63, blue: 0.60) : Color(red: 0.16, green: 0.63, blue: 0.60), + locationAccent: isDark ? Color(red: 0.52, green: 0.60, blue: 0.0) : Color(red: 0.52, green: 0.60, blue: 0.0), + accentBlue: isDark ? Color(red: 0.15, green: 0.55, blue: 0.82) : Color(red: 0.15, green: 0.55, blue: 0.82), + alertRed: isDark ? Color(red: 0.86, green: 0.20, blue: 0.18) : Color(red: 0.86, green: 0.20, blue: 0.18), + divider: isDark ? Color(red: 0.03, green: 0.24, blue: 0.26) : Color(red: 0.93, green: 0.91, blue: 0.84) + ) + } + + static func tokyoNight(_ colorScheme: ColorScheme) -> ThemePalette { + let isDark = colorScheme == .dark + return ThemePalette( + background: isDark ? Color(red: 0.10, green: 0.11, blue: 0.15) : Color(red: 0.84, green: 0.84, blue: 0.86), + primary: isDark ? Color(red: 0.66, green: 0.69, blue: 0.84) : Color(red: 0.22, green: 0.38, blue: 0.75), + secondary: isDark ? Color(red: 0.47, green: 0.51, blue: 0.67) : Color(red: 0.36, green: 0.38, blue: 0.56), + accent: isDark ? Color(red: 0.49, green: 0.81, blue: 1.0) : Color(red: 0.18, green: 0.49, blue: 0.91), + locationAccent: isDark ? Color(red: 0.62, green: 0.81, blue: 0.42) : Color(red: 0.35, green: 0.46, blue: 0.22), + accentBlue: isDark ? Color(red: 0.48, green: 0.64, blue: 0.97) : Color(red: 0.18, green: 0.49, blue: 0.91), + alertRed: isDark ? Color(red: 0.97, green: 0.46, blue: 0.56) : Color(red: 0.78, green: 0.26, blue: 0.26), + divider: isDark ? Color(red: 0.18, green: 0.24, blue: 0.39) : Color(red: 0.71, green: 0.71, blue: 0.77) + ) + } + + static func monochrome(_ colorScheme: ColorScheme) -> ThemePalette { + let isDark = colorScheme == .dark + return ThemePalette( + background: isDark ? .black : .white, + primary: isDark ? .white : .black, + secondary: isDark ? Color(white: 0.65) : Color(white: 0.40), + accent: isDark ? .white : .black, + locationAccent: isDark ? Color(white: 0.75) : Color(white: 0.25), + accentBlue: isDark ? Color(white: 0.75) : Color(white: 0.25), + alertRed: isDark ? Color(red: 0.82, green: 0.32, blue: 0.32) : Color(red: 0.72, green: 0.18, blue: 0.18), + divider: isDark ? Color(white: 0.25) : Color(white: 0.80) + ) + } + private static var systemBackground: Color { #if os(iOS) Color(UIColor.systemBackground) diff --git a/bitchat/Views/AppInfoView.swift b/bitchat/Views/AppInfoView.swift index cb99fbac..43da991e 100644 --- a/bitchat/Views/AppInfoView.swift +++ b/bitchat/Views/AppInfoView.swift @@ -333,27 +333,30 @@ struct AppInfoView: View { @ViewBuilder private var settingsContent: some View { VStack(alignment: .leading, spacing: 24) { - // Appearance — single row: label left, theme chips right - HStack(spacing: 12) { + // Appearance — label above theme chips (scrollable for many themes) + VStack(alignment: .leading, spacing: 8) { SectionHeader(Strings.appearanceTitle) - Spacer() - ForEach(AppTheme.allCases) { theme in - Button { - appThemeRawValue = theme.rawValue - } label: { - Text(theme.displayNameKey) - .bitchatFont(size: 13, weight: selectedTheme == theme ? .semibold : .regular) - .foregroundColor(selectedTheme == theme ? palette.accent : secondaryTextColor) - .padding(.horizontal, 10) - .padding(.vertical, 6) - .background( - RoundedRectangle(cornerRadius: 8, style: .continuous) - .fill(selectedTheme == theme ? palette.accent.opacity(0.15) : Color.clear) - ) - .contentShape(Rectangle()) + ScrollView(.horizontal, showsIndicators: false) { + HStack(spacing: 12) { + ForEach(AppTheme.allCases) { theme in + Button { + appThemeRawValue = theme.rawValue + } label: { + Text(theme.displayNameKey) + .bitchatFont(size: 13, weight: selectedTheme == theme ? .semibold : .regular) + .foregroundColor(selectedTheme == theme ? palette.accent : secondaryTextColor) + .padding(.horizontal, 10) + .padding(.vertical, 6) + .background( + RoundedRectangle(cornerRadius: 8, style: .continuous) + .fill(selectedTheme == theme ? palette.accent.opacity(0.15) : Color.clear) + ) + .contentShape(Rectangle()) + } + .buttonStyle(.plain) + .accessibilityAddTraits(selectedTheme == theme ? .isSelected : []) + } } - .buttonStyle(.plain) - .accessibilityAddTraits(selectedTheme == theme ? .isSelected : []) } }