feat: iOS appearance, iPad logo fix, demo seed, and landing hero polish

Add System/Light/Dark preference for iPhone and iPad, restore the iPad
sidebar brand mark, ship a DEBUG seed-demo URL for rich placeholder data,
and refresh GitHub Pages with the device composite, full-bleed pale-green
hero wash, and transparent Mac screenshot chrome.
This commit is contained in:
Rocky
2026-07-12 16:24:59 +08:00
parent a57bba9ea0
commit c598ba77e6
18 changed files with 424 additions and 220 deletions
+10
View File
@@ -8,6 +8,14 @@ import OSGKeyboardShared
struct OSGKeyboardApp: App {
@UIApplicationDelegateAdaptor(AppURLHandler.self) private var appURLHandler
/// App-local light / dark preference (Settings Preferences Appearance).
@AppStorage(AppearancePreference.storageKey)
private var appearanceRaw = AppearancePreference.system.rawValue
private var appearance: AppearancePreference {
AppearancePreference.fromStored(appearanceRaw)
}
init() {
MaterialIconsFont.registerIfNeeded()
if AppGroup.isAvailable {
@@ -21,10 +29,12 @@ struct OSGKeyboardApp: App {
ThemedRoot {
MainAppRoot()
}
.preferredColorScheme(appearance.colorScheme)
} else {
ThemedRoot {
AppGroupErrorView()
}
.preferredColorScheme(appearance.colorScheme)
}
}
}
+5
View File
@@ -80,6 +80,11 @@ struct MainAppRoot: View {
switch url.host {
case "startflow":
flowManager.startSession(coldStart: true)
#if DEBUG
case "seed-demo":
DemoDataSeeder.seedRichPlaceholderData()
config.reloadFromPersistedStorage()
#endif
default:
break
}
+8 -2
View File
@@ -54,13 +54,19 @@ struct MainSplitView: View {
private var brandHeader: some View {
HStack {
Image("osglogo")
// Match macOS sidebar: template wide mark tinted with accent so
// light / dark both stay readable (PNG `osglogo` can fail to
// show under NavigationSplitView chrome on some iPad sizes).
Image("OSGLogoWide")
.renderingMode(.template)
.resizable()
.scaledToFit()
.frame(height: 28)
.foregroundStyle(palette.accent)
.accessibilityLabel("OSGKeyboard")
Spacer()
Spacer(minLength: 0)
}
.frame(maxWidth: .infinity, minHeight: 28, alignment: .leading)
.padding(.leading, WideLayoutMetrics.sidebarContentInset)
.padding(.trailing, WideLayoutMetrics.sidebarInset)
.padding(.top, Spacing.lg)
+25
View File
@@ -156,6 +156,10 @@ struct SettingsView: View {
Divider().background(palette.divider)
AppearancePickerRow()
Divider().background(palette.divider)
LocalePickerRow(
locales: effectiveLocales,
selection: Binding(
@@ -495,6 +499,27 @@ private struct AppLanguagePickerRow: View {
}
}
// MARK: - Appearance picker row
private struct AppearancePickerRow: View {
@AppStorage(AppearancePreference.storageKey)
private var appearanceRaw = AppearancePreference.system.rawValue
private var options: [(id: String, label: String)] {
AppearancePreference.allCases.map { preference in
(preference.rawValue, AppL10n.string(preference.labelKey))
}
}
var body: some View {
PickerRow(
title: AppL10n.string("settings.appearance.title"),
options: options,
selection: $appearanceRaw
)
}
}
// MARK: - Flow inactivity picker row
private struct FlowInactivityPickerRow: View {
+4
View File
@@ -91,6 +91,10 @@
"settings.appLanguage.auto" = "Auto";
"settings.appLanguage.english" = "English";
"settings.appLanguage.chinese" = "Chinese";
"settings.appearance.title" = "Appearance";
"settings.appearance.system" = "System";
"settings.appearance.light" = "Light";
"settings.appearance.dark" = "Dark";
"settings.reset.title" = "Reset all settings?";
"settings.reset.message" = "API key, model, base URL, voice history, and cloud consent will be cleared.";
"settings.reset.confirm" = "Reset all settings";
@@ -91,6 +91,10 @@
"settings.appLanguage.auto" = "自动";
"settings.appLanguage.english" = "英文";
"settings.appLanguage.chinese" = "中文";
"settings.appearance.title" = "外观";
"settings.appearance.system" = "跟随系统";
"settings.appearance.light" = "浅色";
"settings.appearance.dark" = "深色";
"settings.reset.title" = "重置所有设置?";
"settings.reset.message" = "将清空 API Key、模型、接口地址、语音历史及云端确认状态。";
"settings.reset.confirm" = "重置所有设置";