fix(ios): apply typography to native chrome

This commit is contained in:
joshavant
2026-07-03 13:03:36 -05:00
committed by Josh Avant
parent ea3c6dc5f7
commit b4c5cd511a
11 changed files with 101 additions and 35 deletions

View File

@@ -65,8 +65,8 @@ struct AgentProDreamingDestination: View {
OpenClawAdaptiveHeaderRow(
title: "Dreaming",
subtitle: self.dreamingDetail,
titleFont: .title3.weight(.semibold),
subtitleFont: .callout)
titleFont: OpenClawType.title3SemiBold,
subtitleFont: OpenClawType.subheadMedium)
{
OpenClawSidebarHeaderLeadingSlot(action: headerLeadingAction)
} accessory: {

View File

@@ -39,8 +39,8 @@ struct AgentProNodesDestination: View {
OpenClawAdaptiveHeaderRow(
title: "Instances",
subtitle: self.instancesDetail,
titleFont: .title3.weight(.semibold),
subtitleFont: .callout)
titleFont: OpenClawType.title3SemiBold,
subtitleFont: OpenClawType.subheadMedium)
{
OpenClawSidebarHeaderLeadingSlot(action: headerLeadingAction)
} accessory: {

View File

@@ -172,8 +172,8 @@ extension AgentProTab {
OpenClawAdaptiveHeaderRow(
title: title,
subtitle: subtitle,
titleFont: .title3.weight(.semibold),
subtitleFont: .callout)
titleFont: OpenClawType.title3SemiBold,
subtitleFont: OpenClawType.subheadMedium)
{
OpenClawSidebarHeaderLeadingSlot(action: headerLeadingAction)
} accessory: {

View File

@@ -8,8 +8,8 @@ extension AgentProTab {
OpenClawAdaptiveHeaderRow(
title: self.headerTitle,
subtitle: "\(self.sortedAgents.count) total",
titleFont: .system(size: 28, weight: .bold),
subtitleFont: .subheadline,
titleFont: OpenClawType.title2SemiBold,
subtitleFont: OpenClawType.subheadMedium,
subtitleLineLimit: 1)
{
if let headerLeadingAction {
@@ -64,7 +64,9 @@ extension AgentProTab {
HStack(spacing: 10) {
Picker("Agent status", selection: self.$agentRosterFilter) {
ForEach(AgentRosterFilter.allCases) { filter in
Text(filter.title).tag(filter)
Text(filter.title)
.font(OpenClawType.captionSemiBold)
.tag(filter)
}
}
.pickerStyle(.segmented)

View File

@@ -120,8 +120,8 @@ struct CommandCenterTab: View {
OpenClawAdaptiveHeaderRow(
title: self.headerTitle,
subtitle: self.gatewaySubtitle,
titleFont: .title3.weight(.semibold),
subtitleFont: .caption,
titleFont: OpenClawType.title3SemiBold,
subtitleFont: OpenClawType.caption,
subtitleLineLimit: 1)
{
if let headerLeadingAction {

View File

@@ -51,8 +51,8 @@ struct OpenClawDocsScreen: View {
OpenClawAdaptiveHeaderRow(
title: "Docs",
subtitle: "Gateway setup, pairing, channels, and mobile node reference.",
titleFont: .headline,
subtitleFont: .caption)
titleFont: OpenClawType.headline,
subtitleFont: OpenClawType.caption)
{
HStack(alignment: .top, spacing: 12) {
if let headerLeadingAction {

View File

@@ -151,6 +151,39 @@ enum OpenClawType {
Mono.semiBold,
]
@MainActor
static func installUIKitAppearance() {
let inlineNavigationTitleFont = self.scaledDisplayUIFont(
weight: Display.opticalSemiBold,
size: 17,
relativeTo: .headline)
let largeNavigationTitleFont = self.scaledDisplayUIFont(
weight: Display.heavyTitle,
size: 34,
relativeTo: .largeTitle)
let tabBarNormalFont = self.scaledBodyUIFont(weight: Body.medium, size: 11, relativeTo: .caption2)
let tabBarSelectedFont = self.scaledBodyUIFont(weight: Body.semiBold, size: 11, relativeTo: .caption2)
let segmentedNormalFont = self.scaledBodyUIFont(weight: Body.medium, size: 13, relativeTo: .footnote)
let segmentedSelectedFont = self.scaledBodyUIFont(weight: Body.semiBold, size: 13, relativeTo: .footnote)
let navigationBar = UINavigationBar.appearance()
var titleAttributes = navigationBar.titleTextAttributes ?? [:]
titleAttributes[.font] = inlineNavigationTitleFont
navigationBar.titleTextAttributes = titleAttributes
var largeTitleAttributes = navigationBar.largeTitleTextAttributes ?? [:]
largeTitleAttributes[.font] = largeNavigationTitleFont
navigationBar.largeTitleTextAttributes = largeTitleAttributes
let tabBarItem = UITabBarItem.appearance()
tabBarItem.setTitleTextAttributes([.font: tabBarNormalFont], for: .normal)
tabBarItem.setTitleTextAttributes([.font: tabBarSelectedFont], for: .selected)
let segmentedControl = UISegmentedControl.appearance()
segmentedControl.setTitleTextAttributes([.font: segmentedNormalFont], for: .normal)
segmentedControl.setTitleTextAttributes([.font: segmentedSelectedFont], for: .selected)
}
private enum Display {
static let postScriptName = "RedHatDisplay-Regular"
static let opticalSemiBold: CGFloat = 650
@@ -181,11 +214,11 @@ enum OpenClawType {
size: CGFloat,
relativeTo textStyle: UIFont.TextStyle) -> Font
{
self.scaledVariableFont(
name: Display.postScriptName,
size: size,
relativeTo: textStyle,
variations: [self.fontWeightAxis: weight])
Font(
self.scaledDisplayUIFont(
weight: weight,
size: size,
relativeTo: textStyle))
}
private static func scaledBody(
@@ -193,14 +226,11 @@ enum OpenClawType {
size: CGFloat,
relativeTo textStyle: UIFont.TextStyle) -> Font
{
self.scaledVariableFont(
name: Body.postScriptName,
size: size,
relativeTo: textStyle,
variations: [
self.fontWeightAxis: weight,
self.opticalSizeAxis: min(max(size, 14), 32),
])
Font(
self.scaledBodyUIFont(
weight: weight,
size: size,
relativeTo: textStyle))
}
private static func scaledMono(
@@ -211,16 +241,42 @@ enum OpenClawType {
self.scaledFont(name: name, size: size, relativeTo: textStyle)
}
private static func scaledVariableFont(
private static func scaledDisplayUIFont(
weight: CGFloat,
size: CGFloat,
relativeTo textStyle: UIFont.TextStyle) -> UIFont
{
self.scaledVariableUIFont(
name: Display.postScriptName,
size: size,
relativeTo: textStyle,
variations: [self.fontWeightAxis: weight])
}
private static func scaledBodyUIFont(
weight: CGFloat,
size: CGFloat,
relativeTo textStyle: UIFont.TextStyle) -> UIFont
{
self.scaledVariableUIFont(
name: Body.postScriptName,
size: size,
relativeTo: textStyle,
variations: [
self.fontWeightAxis: weight,
self.opticalSizeAxis: min(max(size, 14), 32),
])
}
private static func scaledVariableUIFont(
name: String,
size: CGFloat,
relativeTo textStyle: UIFont.TextStyle,
variations: [NSNumber: CGFloat]) -> Font
variations: [NSNumber: CGFloat]) -> UIFont
{
guard UIFont(name: name, size: size) != nil else {
let fallback = UIFont.systemFont(ofSize: size)
let scaledFallback = UIFontMetrics(forTextStyle: textStyle).scaledFont(for: fallback)
return Font(scaledFallback)
return UIFontMetrics(forTextStyle: textStyle).scaledFont(for: fallback)
}
let descriptor = UIFontDescriptor(fontAttributes: [
@@ -228,8 +284,7 @@ enum OpenClawType {
kCTFontVariationAttribute as UIFontDescriptor.AttributeName: variations,
])
let base = UIFont(descriptor: descriptor, size: size)
let scaled = UIFontMetrics(forTextStyle: textStyle).scaledFont(for: base)
return Font(scaled)
return UIFontMetrics(forTextStyle: textStyle).scaledFont(for: base)
}
private static func scaledFont(

View File

@@ -33,6 +33,8 @@ struct RootTabsPhoneControlHub: View {
} header: {
if let title = self.sectionTitle(for: group) {
Text(title)
.font(OpenClawType.captionSemiBold)
.foregroundStyle(.secondary)
}
}
}

View File

@@ -145,7 +145,7 @@ extension SettingsProTab {
route: .voice)
}
Section("Device") {
Section {
self.appearanceRow
self.settingsListRow(
icon: "stethoscope",
@@ -166,6 +166,10 @@ extension SettingsProTab {
icon: "info.circle",
title: "About",
route: .about)
} header: {
Text("Device")
.font(OpenClawType.captionSemiBold)
.foregroundStyle(.secondary)
}
Section {

View File

@@ -628,6 +628,7 @@ struct OpenClawApp: App {
init() {
Self.installUncaughtExceptionLogger()
GatewaySettingsStore.bootstrapPersistence()
OpenClawType.installUIKitAppearance()
let appModel = NodeAppModel()
#if DEBUG
if ProcessInfo.processInfo.arguments.contains("--openclaw-reset-onboarding") {
@@ -658,6 +659,7 @@ struct OpenClawApp: App {
WindowGroup {
RootTabs()
.tint(OpenClawBrand.accent)
.font(OpenClawType.body)
.preferredColorScheme(self.appearancePreference.colorScheme)
.environment(self.appModel)
.environment(self.appModel.voiceWake)

View File

@@ -244,7 +244,7 @@ struct RootTabsSourceGuardTests {
from: "var appearanceRow: some View",
to: "var appearanceRowLabel: some View")
#expect(gatewayStatus.contains("HStack(spacing: 6)"))
#expect(gatewayStatus.contains("OpenClawStatusBadge(label: self.title, tone: self.tone)"))
#expect(!gatewayStatus.contains("ProCapsule("))
#expect(!gatewayStatus.contains("Capsule()"))
#expect(agentDestinationsSource.contains("List {"))
@@ -259,7 +259,8 @@ struct RootTabsSourceGuardTests {
#expect(!talkSource.contains("conversationCard"))
#expect(!talkSource.contains("voiceModeCard"))
#expect(!talkSource.contains("statusChip"))
#expect(settingsList.contains("Section(\"Device\")"))
#expect(settingsList.contains("Text(\"Device\")"))
#expect(settingsList.contains(".font(OpenClawType.captionSemiBold)"))
#expect(!settingsList.contains("ProCard("))
#expect(settingsRow.contains("NavigationLink(value: route)"))
#expect(!settingsRow.contains("chevron.right"))