fix(ios): align branded typography licenses

This commit is contained in:
joshavant
2026-07-03 02:00:44 -05:00
committed by Josh Avant
parent 9184a68ab8
commit ea3c6dc5f7
46 changed files with 578 additions and 387 deletions
File diff suppressed because it is too large Load Diff
@@ -1,17 +1,14 @@
DM Sans
Inter
Copyright 2014 The DM Sans Project Authors
Copyright 2016 The Inter Project Authors
Source: https://github.com/google/fonts/tree/main/ofl/dmsans
Version: 2.100, static instances extracted from variable fonts
Source: https://github.com/google/fonts/tree/main/ofl/inter
Version: Google Fonts variable release
License: SIL Open Font License 1.1
Bundled files:
- DMSans-Light.ttf
- DMSans-Regular.ttf
- DMSans-Italic.ttf
- DMSans-Medium.ttf
- DMSans-SemiBold.ttf
- Inter[opsz,wght].ttf
- Inter-Italic[opsz,wght].ttf
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
@@ -1,18 +1,13 @@
Plus Jakarta Sans
Red Hat Display
Copyright 2020 The Plus Jakarta Sans Project Authors
Copyright 2024 The Red Hat Project Authors (https://github.com/RedHatOfficial/RedHatFont)
Source: https://github.com/tokotype/PlusJakartaSans
Version: 2.7.1
Source: https://github.com/google/fonts/tree/main/ofl/redhatdisplay
Version: Google Fonts variable release
License: SIL Open Font License 1.1
Bundled files:
- PlusJakartaSans-Light.ttf
- PlusJakartaSans-Regular.ttf
- PlusJakartaSans-Medium.ttf
- PlusJakartaSans-SemiBold.ttf
- PlusJakartaSans-Bold.ttf
- PlusJakartaSans-ExtraBold.ttf
- RedHatDisplay[wght].ttf
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
@@ -77,7 +77,7 @@ extension AgentProTab {
}
} label: {
Image(systemName: "xmark.circle.fill")
.font(OpenClawType.title3)
.font(.title3)
.foregroundStyle(.secondary)
.frame(width: 44, height: 44)
.contentShape(Circle())
@@ -313,7 +313,7 @@ extension AgentProTab {
func agentAvatar(_ agent: AgentSummary, state: AgentRosterState) -> some View {
ZStack(alignment: .bottomTrailing) {
Text(self.agentBadge(for: agent))
.font(.system(size: self.agentBadge(for: agent).count > 2 ? 14 : 18, weight: .bold, design: .rounded))
.font(OpenClawType.avatar(size: self.agentBadge(for: agent).count > 2 ? 14 : 18))
.foregroundStyle(.white)
.minimumScaleFactor(0.62)
.lineLimit(1)
@@ -540,7 +540,7 @@ extension AgentProTab {
self.detailMetric(label: "Source", value: self.normalized(skill.source) ?? "unknown")
if let filePath = self.normalized(skill.filePath) {
Text(filePath)
.font(OpenClawType.monoSmall)
.font(OpenClawType.monoCaption2)
.foregroundStyle(.secondary)
.textSelection(.enabled)
}
+1 -1
View File
@@ -129,7 +129,7 @@ struct ChatProTab: View {
private var headerIdentityBadge: some View {
if self.showsAgentBadge {
Text(self.agentBadge)
.font(.system(size: self.agentBadge.count > 2 ? 13 : 16, weight: .bold, design: .rounded))
.font(OpenClawType.avatar(size: self.agentBadge.count > 2 ? 13 : 16))
.foregroundStyle(.white)
.minimumScaleFactor(0.6)
.lineLimit(1)
@@ -97,7 +97,7 @@ struct CommandSessionRow: View {
struct CommandViewMoreRow: View {
var body: some View {
Label("View More", systemImage: "chevron.right")
.font(OpenClawType.subheadSemiBold)
.font(OpenClawType.subheadBold)
.foregroundStyle(OpenClawBrand.accent)
.frame(maxWidth: .infinity)
.padding(.vertical, 10)
@@ -113,7 +113,7 @@ struct CommandEmptyStateRow: View {
var body: some View {
HStack(spacing: 10) {
Image(systemName: self.icon)
.font(OpenClawType.captionSemiBold)
.font(OpenClawType.captionBold)
.foregroundStyle(OpenClawBrand.ok)
.frame(width: 30, height: 30)
.background {
@@ -673,7 +673,7 @@ struct CommandSessionsScreen: View {
VStack(spacing: 0) {
HStack(spacing: 8) {
Text("Recent sessions")
.font(OpenClawType.subheadSemiBold)
.font(OpenClawType.subheadBold)
Spacer(minLength: 8)
if self.isLoading {
ProgressView()
@@ -103,9 +103,9 @@ private struct IPadWorkboardCompactRowsPreview: View {
private var previewHeader: some View {
VStack(alignment: .leading, spacing: 4) {
Text("Phone queue")
.font(.headline)
.font(OpenClawType.headline)
Text("Tap for detail, swipe or long-press for card actions.")
.font(.caption)
.font(OpenClawType.caption)
.foregroundStyle(.secondary)
}
}
@@ -161,9 +161,9 @@ private struct IPadSkillWorkshopCompactRowsPreview: View {
private var previewHeader: some View {
VStack(alignment: .leading, spacing: 4) {
Text("Phone proposals")
.font(.headline)
.font(OpenClawType.headline)
Text("Tap for detail, swipe or long-press for proposal actions.")
.font(.caption)
.font(OpenClawType.caption)
.foregroundStyle(.secondary)
}
}
@@ -277,7 +277,7 @@ private struct IPadActivityStatesPreview: View {
private func previewHeader(_ title: String) -> some View {
Text(title)
.font(.caption.weight(.semibold))
.font(OpenClawType.captionSemiBold)
.foregroundStyle(.secondary)
.textCase(.uppercase)
}
@@ -423,7 +423,7 @@ private struct IPadWorkboardStatesPreview: View {
private func previewHeader(_ title: String) -> some View {
Text(title)
.font(.caption.weight(.semibold))
.font(OpenClawType.captionSemiBold)
.foregroundStyle(.secondary)
.textCase(.uppercase)
}
@@ -525,7 +525,7 @@ private struct IPadSkillWorkshopStatesPreview: View {
private func previewHeader(_ title: String) -> some View {
Text(title)
.font(.subheadline.weight(.semibold))
.font(OpenClawType.subheadSemiBold)
.foregroundStyle(.secondary)
}
@@ -593,9 +593,9 @@ private struct IPadSkillWorkshopKanbanPreview: View {
private var previewHeader: some View {
VStack(alignment: .leading, spacing: 4) {
Text("iPad kanban")
.font(.headline)
.font(OpenClawType.headline)
Text("Wide layout with populated, empty, held, and custom proposal lanes.")
.font(.caption)
.font(OpenClawType.caption)
.foregroundStyle(.secondary)
}
.padding(.horizontal, OpenClawProMetric.pagePadding)
@@ -34,7 +34,7 @@ struct IPadSidebarScreenChrome<Content: View>: View {
OpenClawAdaptiveHeaderRow(
title: self.title,
subtitle: self.subtitle,
titleFont: self.isCompactHeight ? .headline.weight(.semibold) : .title2.weight(.semibold),
titleFont: self.isCompactHeight ? OpenClawType.headline : OpenClawType.title2SemiBold,
subtitleLineLimit: self.isCompactHeight ? 1 : 2)
{
if let headerLeadingAction {
@@ -448,7 +448,7 @@ struct IPadSkillWorkshopScreen: View {
.font(OpenClawType.subheadSemiBold)
ForEach(proposal.supportFiles, id: \.path) { file in
Text(file.path)
.font(OpenClawType.monoSmall)
.font(OpenClawType.monoCaption2)
.foregroundStyle(.secondary)
.lineLimit(1)
}
@@ -1099,7 +1099,7 @@ struct IPadWorkboardQueueRow: View {
self.actionMenuItems
} label: {
Image(systemName: self.isBusy ? "hourglass" : "ellipsis.circle")
.font(OpenClawType.headline)
.font(.system(size: 19, weight: .semibold))
.frame(width: 36, height: 36)
.contentShape(Rectangle())
}
@@ -75,7 +75,7 @@ struct LicenseDocumentDetailView: View {
var body: some View {
ScrollView {
Text(verbatim: self.document.body)
.font(.system(.footnote, design: .monospaced))
.font(OpenClawType.monoFootnote)
.foregroundStyle(.primary)
.textSelection(.enabled)
.accessibilityIdentifier("licenses-detail-text")
@@ -120,7 +120,7 @@ struct OpenClawDocsScreen: View {
}
Spacer(minLength: 8)
Image(systemName: "arrow.up.right")
.font(OpenClawType.captionSemiBold)
.font(OpenClawType.captionBold)
.foregroundStyle(.secondary)
}
.padding(.horizontal, 14)
@@ -274,7 +274,7 @@ struct OpenClawSidebarRevealButton: View {
var body: some View {
let button = Button(action: headerAction.action) {
Image(systemName: self.headerAction.systemName)
.font(OpenClawType.subhead)
.font(OpenClawType.subheadSemiBold)
.frame(
width: OpenClawProMetric.compactControlSize,
height: OpenClawProMetric.compactControlSize)
@@ -389,7 +389,7 @@ struct OpenClawNoticeBanner: View {
.fixedSize(horizontal: false, vertical: true)
case let .requestID(value):
Text("Request ID: \(value)")
.font(OpenClawType.monoSmall)
.font(OpenClawType.monoSmallMedium)
.foregroundStyle(.secondary)
.textSelection(.enabled)
}
@@ -400,7 +400,7 @@ struct OpenClawNoticeBanner: View {
struct OpenClawAdaptiveHeaderRow<Leading: View, Accessory: View>: View {
let title: String
let subtitle: String?
var titleFont: Font = OpenClawType.title3
var titleFont: Font = OpenClawType.title3SemiBold
var subtitleFont: Font = OpenClawType.subhead
var subtitleLineLimit: Int? = 2
@ViewBuilder let leading: Leading
@@ -409,7 +409,7 @@ struct OpenClawAdaptiveHeaderRow<Leading: View, Accessory: View>: View {
init(
title: String,
subtitle: String? = nil,
titleFont: Font = OpenClawType.title3,
titleFont: Font = OpenClawType.title3SemiBold,
subtitleFont: Font = OpenClawType.subhead,
subtitleLineLimit: Int? = 2,
@ViewBuilder leading: () -> Leading,
@@ -710,7 +710,7 @@ struct ProMetricTile: View {
VStack(alignment: .leading, spacing: 2) {
Text(self.value)
.font(OpenClawType.headline)
.font(OpenClawType.headlineBold)
.lineLimit(1)
.minimumScaleFactor(0.72)
Text(self.title)
+114 -47
View File
@@ -1,85 +1,118 @@
import CoreText
import SwiftUI
import UIKit
enum OpenClawType {
// MARK: - Display Plus Jakarta Sans
// MARK: - Display Red Hat Display
static var title1: Font {
scaledDisplay(name: Display.extraBold, size: 34, relativeTo: .largeTitle)
scaledDisplay(weight: Display.heavyTitle, size: 34, relativeTo: .largeTitle)
}
static var title2: Font {
scaledDisplay(name: Display.bold, size: 28, relativeTo: .title1)
scaledDisplay(weight: Display.heavyTitle, size: 28, relativeTo: .title1)
}
static var title3: Font {
scaledDisplay(name: Display.bold, size: 22, relativeTo: .title2)
scaledDisplay(weight: Display.opticalBold, size: 22, relativeTo: .title2)
}
static var title3SemiBold: Font {
scaledDisplay(weight: Display.opticalSemiBold, size: 22, relativeTo: .title2)
}
static var headline: Font {
scaledDisplay(name: Display.semiBold, size: 17, relativeTo: .headline)
scaledDisplay(weight: Display.opticalSemiBold, size: 17, relativeTo: .headline)
}
// MARK: - Body DM Sans
static var headlineBold: Font {
scaledDisplay(weight: Display.opticalBold, size: 17, relativeTo: .headline)
}
static func display(size: CGFloat, weight: CGFloat, relativeTo textStyle: UIFont.TextStyle) -> Font {
self.scaledDisplay(weight: weight, size: size, relativeTo: textStyle)
}
// MARK: - Body Inter
static var body: Font {
scaledBody(name: Body.regular, size: 17, relativeTo: .body)
scaledBody(weight: Body.regular, size: 17, relativeTo: .body)
}
static var callout: Font {
scaledBody(name: Body.regular, size: 16, relativeTo: .callout)
scaledBody(weight: Body.regular, size: 16, relativeTo: .callout)
}
static var subhead: Font {
scaledBody(name: Body.medium, size: 15, relativeTo: .subheadline)
scaledBody(weight: Body.regular, size: 15, relativeTo: .subheadline)
}
static var subheadMedium: Font {
scaledBody(weight: Body.medium, size: 15, relativeTo: .subheadline)
}
static var subheadSemiBold: Font {
scaledDisplay(name: Display.semiBold, size: 15, relativeTo: .subheadline)
scaledDisplay(weight: Display.opticalSemiBold, size: 15, relativeTo: .subheadline)
}
static var subheadBold: Font {
scaledDisplay(weight: Display.opticalBold, size: 15, relativeTo: .subheadline)
}
static var footnote: Font {
scaledBody(name: Body.regular, size: 13, relativeTo: .footnote)
scaledBody(weight: Body.regular, size: 13, relativeTo: .footnote)
}
static var footnoteMedium: Font {
scaledBody(name: Body.medium, size: 13, relativeTo: .footnote)
scaledBody(weight: Body.medium, size: 13, relativeTo: .footnote)
}
static var footnoteSemiBold: Font {
scaledBody(name: Body.semiBold, size: 13, relativeTo: .footnote)
scaledBody(weight: Body.semiBold, size: 13, relativeTo: .footnote)
}
static var caption: Font {
scaledBody(name: Body.regular, size: 12, relativeTo: .caption1)
scaledBody(weight: Body.regular, size: 12, relativeTo: .caption1)
}
static var captionMedium: Font {
scaledBody(name: Body.medium, size: 12, relativeTo: .caption1)
scaledBody(weight: Body.medium, size: 12, relativeTo: .caption1)
}
static var captionSemiBold: Font {
scaledBody(name: Body.semiBold, size: 12, relativeTo: .caption1)
scaledBody(weight: Body.semiBold, size: 12, relativeTo: .caption1)
}
static var captionBold: Font {
scaledBody(weight: Body.bold, size: 12, relativeTo: .caption1)
}
static func body(size: CGFloat, weight: CGFloat, relativeTo textStyle: UIFont.TextStyle) -> Font {
self.scaledBody(weight: weight, size: size, relativeTo: textStyle)
}
static func avatar(size: CGFloat) -> Font {
self.scaledBody(weight: Body.bold, size: size, relativeTo: .caption1)
}
static var caption2: Font {
scaledBody(name: Body.regular, size: 11, relativeTo: .caption2)
scaledBody(weight: Body.regular, size: 11, relativeTo: .caption2)
}
static var caption2Medium: Font {
scaledBody(name: Body.medium, size: 11, relativeTo: .caption2)
scaledBody(weight: Body.medium, size: 11, relativeTo: .caption2)
}
static var caption2SemiBold: Font {
scaledBody(name: Body.semiBold, size: 11, relativeTo: .caption2)
scaledBody(weight: Body.semiBold, size: 11, relativeTo: .caption2)
}
static var caption2Bold: Font {
scaledDisplay(name: Display.bold, size: 11, relativeTo: .caption2)
scaledDisplay(weight: Display.opticalBold, size: 11, relativeTo: .caption2)
}
static var title2SemiBold: Font {
scaledDisplay(name: Display.semiBold, size: 28, relativeTo: .title1)
scaledDisplay(weight: Display.opticalSemiBold, size: 28, relativeTo: .title1)
}
// MARK: - Mono JetBrains Mono
@@ -92,6 +125,14 @@ enum OpenClawType {
scaledMono(name: Mono.regular, size: 12, relativeTo: .caption1)
}
static var monoSmallMedium: Font {
scaledMono(name: Mono.medium, size: 12, relativeTo: .caption1)
}
static var monoCaption2: Font {
scaledMono(name: Mono.regular, size: 11, relativeTo: .caption2)
}
static var monoFootnote: Font {
scaledMono(name: Mono.regular, size: 13, relativeTo: .footnote)
}
@@ -102,37 +143,28 @@ enum OpenClawType {
/// PostScript names for bundled fonts. Keep aligned with `UIAppFonts` in `project.yml`.
static let registeredPostScriptNames: [String] = [
Display.light,
Display.regular,
Display.medium,
Display.semiBold,
Display.bold,
Display.extraBold,
Body.light,
Body.regular,
Body.italic,
Body.medium,
Body.semiBold,
Display.postScriptName,
Body.postScriptName,
Body.italicPostScriptName,
Mono.regular,
Mono.medium,
Mono.semiBold,
]
private enum Display {
static let light = "PlusJakartaSans-Light"
static let regular = "PlusJakartaSans-Regular"
static let medium = "PlusJakartaSans-Medium"
static let semiBold = "PlusJakartaSans-SemiBold"
static let bold = "PlusJakartaSans-Bold"
static let extraBold = "PlusJakartaSans-ExtraBold"
static let postScriptName = "RedHatDisplay-Regular"
static let opticalSemiBold: CGFloat = 650
static let opticalBold: CGFloat = 750
static let heavyTitle: CGFloat = 800
}
private enum Body {
static let light = "DMSans-Light"
static let regular = "DMSans-Regular"
static let italic = "DMSans-Italic"
static let medium = "DMSans-Medium"
static let semiBold = "DMSans-SemiBold"
static let postScriptName = "Inter-Regular"
static let italicPostScriptName = "Inter-Italic"
static let regular: CGFloat = 400
static let medium: CGFloat = 500
static let semiBold: CGFloat = 600
static let bold: CGFloat = 700
}
private enum Mono {
@@ -141,20 +173,34 @@ enum OpenClawType {
static let semiBold = "JetBrainsMono-SemiBold"
}
private static let fontWeightAxis = NSNumber(value: 2_003_265_652) // "wght"
private static let opticalSizeAxis = NSNumber(value: 1_869_640_570) // "opsz"
private static func scaledDisplay(
name: String,
weight: CGFloat,
size: CGFloat,
relativeTo textStyle: UIFont.TextStyle) -> Font
{
self.scaledFont(name: name, size: size, relativeTo: textStyle)
self.scaledVariableFont(
name: Display.postScriptName,
size: size,
relativeTo: textStyle,
variations: [self.fontWeightAxis: weight])
}
private static func scaledBody(
name: String,
weight: CGFloat,
size: CGFloat,
relativeTo textStyle: UIFont.TextStyle) -> Font
{
self.scaledFont(name: name, size: size, relativeTo: textStyle)
self.scaledVariableFont(
name: Body.postScriptName,
size: size,
relativeTo: textStyle,
variations: [
self.fontWeightAxis: weight,
self.opticalSizeAxis: min(max(size, 14), 32),
])
}
private static func scaledMono(
@@ -165,6 +211,27 @@ enum OpenClawType {
self.scaledFont(name: name, size: size, relativeTo: textStyle)
}
private static func scaledVariableFont(
name: String,
size: CGFloat,
relativeTo textStyle: UIFont.TextStyle,
variations: [NSNumber: CGFloat]) -> Font
{
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)
}
let descriptor = UIFontDescriptor(fontAttributes: [
.name: name,
kCTFontVariationAttribute as UIFontDescriptor.AttributeName: variations,
])
let base = UIFont(descriptor: descriptor, size: size)
let scaled = UIFontMetrics(forTextStyle: textStyle).scaledFont(for: base)
return Font(scaled)
}
private static func scaledFont(
name: String,
size: CGFloat,
@@ -60,10 +60,10 @@ struct RootTabsPhoneControlHub: View {
color: self.gatewayStateColor)
VStack(alignment: .leading, spacing: 2) {
Text("Gateway")
.font(.subheadline.weight(.semibold))
.font(OpenClawType.subheadSemiBold)
.foregroundStyle(.primary)
Text(self.sidebarActiveAgentTitle)
.font(.footnote)
.font(OpenClawType.footnote)
.foregroundStyle(.secondary)
.lineLimit(1)
}
@@ -71,10 +71,10 @@ struct RootTabsPhoneControlHub: View {
HStack(spacing: 6) {
ProStatusDot(color: self.gatewayStateColor)
Text(self.gatewayStateText)
.font(.footnote.weight(.semibold))
.font(OpenClawType.footnoteSemiBold)
.foregroundStyle(self.gatewayStateColor)
Image(systemName: "chevron.right")
.font(.caption.weight(.semibold))
.font(OpenClawType.captionSemiBold)
.foregroundStyle(.secondary)
}
}
@@ -95,7 +95,7 @@ struct RootTabsPhoneControlHub: View {
self.rowLabel(destination)
Spacer(minLength: 8)
Image(systemName: "chevron.right")
.font(.caption.weight(.semibold))
.font(OpenClawType.captionSemiBold)
.foregroundStyle(.secondary)
}
.contentShape(Rectangle())
@@ -111,6 +111,8 @@ struct RootTabsPhoneControlHub: View {
private func rowLabel(_ destination: RootTabs.SidebarDestination) -> some View {
Label {
Text(destination.title)
.font(OpenClawType.subheadSemiBold)
.foregroundStyle(.primary)
} icon: {
ProIconBadge(systemName: destination.systemImage, color: .secondary)
}
+1 -1
View File
@@ -288,7 +288,7 @@ struct HostedPushRelayDisclosureSheet: View {
.font(OpenClawType.title2SemiBold)
.foregroundStyle(OpenClawBrand.accentForeground)
Text("Enable OpenClaw Hosted Push Relay?")
.font(OpenClawType.title3)
.font(OpenClawType.title3SemiBold)
Text(self.message)
.font(OpenClawType.body)
.foregroundStyle(.secondary)
@@ -42,16 +42,16 @@ extension SettingsProTab {
color: .secondary)
Text("Appearance")
.font(.subheadline.weight(.semibold))
.font(OpenClawType.subheadSemiBold)
.foregroundStyle(.primary)
Spacer(minLength: 8)
HStack(spacing: 5) {
Text(self.currentAppearancePreference.label)
.font(.subheadline.weight(.semibold))
.font(OpenClawType.subheadSemiBold)
Image(systemName: "chevron.up.chevron.down")
.font(.caption2.weight(.bold))
.font(OpenClawType.caption2Bold)
}
.foregroundStyle(OpenClawBrand.accent)
}
@@ -576,9 +576,9 @@ extension SettingsProTab {
ProIconBadge(systemName: "doc.text", color: .secondary)
VStack(alignment: .leading, spacing: 3) {
Text("No licenses bundled")
.font(.subheadline.weight(.semibold))
.font(OpenClawType.subheadSemiBold)
Text("License files are not available in this build.")
.font(.caption)
.font(OpenClawType.caption)
.foregroundStyle(.secondary)
.lineLimit(2)
}
@@ -589,7 +589,7 @@ extension SettingsProTab {
let lastDocumentID = documents.last?.id
Text("OpenClaw appreciates its partners in the open-source community.")
.font(.footnote)
.font(OpenClawType.footnote)
.foregroundStyle(.secondary)
.multilineTextAlignment(.center)
.fixedSize(horizontal: false, vertical: true)
@@ -619,11 +619,11 @@ extension SettingsProTab {
HStack(spacing: 12) {
ProIconBadge(systemName: "doc.text", color: .secondary)
Text(document.title)
.font(.subheadline.weight(.semibold))
.font(OpenClawType.subheadSemiBold)
.foregroundStyle(.primary)
Spacer(minLength: 8)
Image(systemName: "chevron.right")
.font(.caption.weight(.semibold))
.font(OpenClawType.captionSemiBold)
.foregroundStyle(.tertiary)
}
.padding(.horizontal, 14)
@@ -36,7 +36,7 @@ struct SettingsApprovalRow: View {
var body: some View {
HStack(spacing: 10) {
Image(systemName: self.item.icon)
.font(OpenClawType.captionSemiBold)
.font(OpenClawType.captionBold)
.foregroundStyle(.white)
.frame(width: 30, height: 30)
.background {
@@ -54,7 +54,7 @@ struct SettingsApprovalRow: View {
}
Spacer(minLength: 8)
Text(self.item.priority)
.font(OpenClawType.captionSemiBold)
.font(OpenClawType.captionBold)
.foregroundStyle(self.item.color)
.padding(.horizontal, 9)
.padding(.vertical, 5)
+4 -4
View File
@@ -109,8 +109,8 @@ struct TalkProTab: View {
OpenClawAdaptiveHeaderRow(
title: "Talk",
subtitle: self.headerSubtitle,
titleFont: .system(size: 30, weight: .bold),
subtitleFont: .caption.weight(.medium),
titleFont: OpenClawType.display(size: 30, weight: 750, relativeTo: .title1),
subtitleFont: OpenClawType.captionMedium,
subtitleLineLimit: 1)
{
if let headerLeadingAction {
@@ -137,14 +137,14 @@ struct TalkProTab: View {
.font(OpenClawType.title3)
.multilineTextAlignment(.center)
Text(self.heroSubtitle)
.font(OpenClawType.subhead)
.font(OpenClawType.subheadMedium)
.foregroundStyle(.secondary)
.multilineTextAlignment(.center)
}
Button(action: self.handlePrimaryAction) {
Label(self.state.primaryButtonTitle, systemImage: self.state.primaryButtonIcon)
.font(OpenClawType.subheadSemiBold)
.font(OpenClawType.subheadBold)
.frame(maxWidth: .infinity)
.frame(height: 50)
}
@@ -43,7 +43,7 @@ struct TalkRuntimeIssueDetailsSheet: View {
Section {
VStack(alignment: .leading, spacing: 10) {
Text(self.issue.fallbackBannerTitle)
.font(OpenClawType.title3)
.font(OpenClawType.title3SemiBold)
Text(self.issue.fallbackBannerMessage)
.font(OpenClawType.body)
.foregroundStyle(.secondary)
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+3 -11
View File
@@ -90,17 +90,9 @@
<string>$(OPENCLAW_PUSH_RELAY_BASE_URL)</string>
<key>UIAppFonts</key>
<array>
<string>PlusJakartaSans-Light.ttf</string>
<string>PlusJakartaSans-Regular.ttf</string>
<string>PlusJakartaSans-Medium.ttf</string>
<string>PlusJakartaSans-SemiBold.ttf</string>
<string>PlusJakartaSans-Bold.ttf</string>
<string>PlusJakartaSans-ExtraBold.ttf</string>
<string>DMSans-Light.ttf</string>
<string>DMSans-Regular.ttf</string>
<string>DMSans-Italic.ttf</string>
<string>DMSans-Medium.ttf</string>
<string>DMSans-SemiBold.ttf</string>
<string>RedHatDisplay[wght].ttf</string>
<string>Inter[opsz,wght].ttf</string>
<string>Inter-Italic[opsz,wght].ttf</string>
<string>JetBrainsMono-Regular.ttf</string>
<string>JetBrainsMono-Medium.ttf</string>
<string>JetBrainsMono-SemiBold.ttf</string>
+3 -3
View File
@@ -306,7 +306,7 @@ struct RootTabs: View {
VStack(alignment: .leading, spacing: 2) {
Text("OpenClaw")
.font(.headline.weight(.semibold))
.font(OpenClawType.headline)
.foregroundStyle(.primary)
.lineLimit(1)
@@ -317,7 +317,7 @@ struct RootTabs: View {
Text(self.sidebarGatewayStatusTitle)
.lineLimit(1)
}
.font(.caption.weight(.medium))
.font(OpenClawType.captionMedium)
.foregroundStyle(.secondary)
}
@@ -608,7 +608,7 @@ struct RootTabs: View {
self.hideSidebar()
} label: {
Image(systemName: self.isSidebarDrawerLayout ? "xmark" : "sidebar.left")
.font(.system(size: 15, weight: .semibold))
.font(OpenClawType.subheadSemiBold)
}
.frame(width: 44, height: 44)
.contentShape(Rectangle())
+22 -2
View File
@@ -1,3 +1,4 @@
import CoreText
import Testing
import UIKit
@testable import OpenClaw
@@ -10,8 +11,8 @@ struct OpenClawTypographyTests {
}
@Test func `dynamic type scales display fonts`() {
guard let base = UIFont(name: "PlusJakartaSans-ExtraBold", size: 34) else {
Issue.record("PlusJakartaSans-ExtraBold should be bundled")
guard let base = UIFont(name: "RedHatDisplay-Regular", size: 34) else {
Issue.record("RedHatDisplay-Regular should be bundled")
return
}
@@ -24,4 +25,23 @@ struct OpenClawTypographyTests {
#expect(largeSize > defaultSize)
}
@Test func `display variable weight axis instantiates heavy weights`() {
guard UIFont(name: "RedHatDisplay-Regular", size: 15) != nil else {
Issue.record("RedHatDisplay-Regular should be bundled")
return
}
let weightAxis = NSNumber(value: 2_003_265_652) // "wght"
let descriptor = UIFontDescriptor(fontAttributes: [
.name: "RedHatDisplay-Regular",
kCTFontVariationAttribute as UIFontDescriptor.AttributeName: [weightAxis: 900],
])
let font = UIFont(descriptor: descriptor, size: 15)
let variations = font.fontDescriptor.object(
forKey: kCTFontVariationAttribute as UIFontDescriptor.AttributeName) as? [NSNumber: Any]
let weightValue = variations?[weightAxis] as? NSNumber
#expect(weightValue?.doubleValue == 900)
}
}
+3 -11
View File
@@ -179,17 +179,9 @@ targets:
NSSupportsLiveActivities: true
ITSAppUsesNonExemptEncryption: false
UIAppFonts:
- PlusJakartaSans-Light.ttf
- PlusJakartaSans-Regular.ttf
- PlusJakartaSans-Medium.ttf
- PlusJakartaSans-SemiBold.ttf
- PlusJakartaSans-Bold.ttf
- PlusJakartaSans-ExtraBold.ttf
- DMSans-Light.ttf
- DMSans-Regular.ttf
- DMSans-Italic.ttf
- DMSans-Medium.ttf
- DMSans-SemiBold.ttf
- RedHatDisplay[wght].ttf
- Inter[opsz,wght].ttf
- Inter-Italic[opsz,wght].ttf
- JetBrainsMono-Regular.ttf
- JetBrainsMono-Medium.ttf
- JetBrainsMono-SemiBold.ttf
@@ -217,7 +217,7 @@ struct OpenClawChatComposer: View {
{
ForEach(self.viewModel.sessionChoices, id: \.key) { session in
Text(session.displayName ?? session.key)
.font(.system(.caption, design: .monospaced))
.font(OpenClawChatTypography.mono(size: 12, relativeTo: .caption))
.tag(session.key)
}
}
@@ -287,7 +287,7 @@ struct OpenClawChatComposer: View {
private var compactAttachmentLabel: some View {
Image(systemName: "paperclip")
.font(.system(size: 15, weight: .semibold))
.font(OpenClawChatTypography.display(size: 15, weight: .semibold, relativeTo: .subheadline))
.foregroundStyle(.secondary)
.frame(width: self.cleanControlHeight, height: self.cleanControlHeight)
.contentShape(Rectangle())
@@ -407,9 +407,9 @@ struct OpenClawChatComposer: View {
} label: {
HStack(spacing: 6) {
Image(systemName: talkControl.isEnabled ? "stop.fill" : "waveform")
.font(.caption.weight(.semibold))
.font(OpenClawChatTypography.captionSemiBold)
Text(talkControl.isEnabled ? "Stop" : "Talk")
.font(.caption.weight(.semibold))
.font(OpenClawChatTypography.captionSemiBold)
.lineLimit(1)
}
.foregroundStyle(talkControl.isEnabled ? .white : .primary)
@@ -437,7 +437,7 @@ struct OpenClawChatComposer: View {
talkControl.toggle(self.viewModel.sessionKey)
} label: {
Image(systemName: talkControl.isEnabled ? "stop.fill" : "waveform")
.font(.system(size: 14, weight: .semibold))
.font(OpenClawChatTypography.display(size: 14, weight: .semibold, relativeTo: .subheadline))
.foregroundStyle(talkControl.isEnabled ? .white : .secondary)
.frame(width: self.cleanIconControlSize, height: self.cleanIconControlSize)
.background {
@@ -500,7 +500,7 @@ struct OpenClawChatComposer: View {
.fill(self.connectionOK ? .green : .orange)
.frame(width: 7, height: 7)
Text(self.connectionStatusText)
.font(.caption2)
.font(OpenClawChatTypography.caption2)
.foregroundStyle(.secondary)
}
.padding(.horizontal, self.composerChrome == .clean ? 0 : 8)
@@ -523,7 +523,7 @@ struct OpenClawChatComposer: View {
ZStack(alignment: self.editorOverlayAlignment) {
if self.viewModel.input.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
Text(self.placeholderText)
.font(.body)
.font(OpenClawChatTypography.body)
.foregroundStyle(.tertiary)
.padding(.horizontal, self.cleanFieldTextInset)
.padding(.vertical, self.composerChrome == .clean ? 0 : 4)
@@ -549,7 +549,7 @@ struct OpenClawChatComposer: View {
"",
text: self.$viewModel.input,
axis: .vertical)
.font(.body)
.font(OpenClawChatTypography.body)
.textFieldStyle(.plain)
.lineLimit(1...4)
.fixedSize(horizontal: false, vertical: true)
@@ -576,7 +576,7 @@ struct OpenClawChatComposer: View {
ProgressView().controlSize(.mini)
} else {
Image(systemName: "stop.fill")
.font(.system(size: 13, weight: .semibold))
.font(OpenClawChatTypography.display(size: 13, weight: .semibold, relativeTo: .caption))
}
}
.buttonStyle(.plain)
@@ -597,7 +597,7 @@ struct OpenClawChatComposer: View {
ProgressView().controlSize(.mini)
} else {
Image(systemName: "arrow.up")
.font(.system(size: 13, weight: .semibold))
.font(OpenClawChatTypography.display(size: 13, weight: .semibold, relativeTo: .caption))
}
}
.buttonStyle(.plain)
@@ -233,7 +233,7 @@ private struct InlineImageList: View {
.strokeBorder(Color.white.opacity(0.12), lineWidth: 1))
} else {
Text(item.label.isEmpty ? "Image" : item.label)
.font(.footnote)
.font(OpenClawChatTypography.footnote)
.foregroundStyle(.secondary)
}
}
@@ -15,7 +15,7 @@ struct ChatAgentAvatar: View {
var body: some View {
Text(self.displayText)
.font(.system(size: self.fontSize, weight: .bold, design: .rounded))
.font(OpenClawChatTypography.avatar(size: self.fontSize))
.foregroundStyle(.white)
.minimumScaleFactor(0.6)
.lineLimit(1)
@@ -468,7 +468,7 @@ private struct AttachmentRow: View {
HStack(spacing: 8) {
Image(systemName: "paperclip")
Text(self.att.fileName ?? "Attachment")
.font(.footnote)
.font(OpenClawChatTypography.footnote)
.lineLimit(1)
.foregroundStyle(self.isUser ? OpenClawChatTheme.userText : OpenClawChatTheme.assistantText)
Spacer()
@@ -487,13 +487,13 @@ private struct ToolCallCard: View {
VStack(alignment: .leading, spacing: 6) {
HStack(spacing: 6) {
Text(self.toolName)
.font(.footnote.weight(.semibold))
.font(OpenClawChatTypography.footnoteSemiBold)
Spacer(minLength: 0)
}
if let summary = self.summary, !summary.isEmpty {
Text(summary)
.font(.footnote.monospaced())
.font(OpenClawChatTypography.mono(size: 13, relativeTo: .footnote))
.foregroundStyle(.secondary)
.lineLimit(2)
}
@@ -532,12 +532,12 @@ private struct ToolResultCard: View {
VStack(alignment: .leading, spacing: 8) {
HStack(spacing: 6) {
Text(self.title)
.font(.footnote.weight(.semibold))
.font(OpenClawChatTypography.footnoteSemiBold)
Spacer(minLength: 0)
}
Text(self.displayText)
.font(.footnote.monospaced())
.font(OpenClawChatTypography.mono(size: 13, relativeTo: .footnote))
.foregroundStyle(self.isUser ? OpenClawChatTheme.userText : OpenClawChatTheme.assistantText)
.lineLimit(self.expanded ? nil : Self.previewLineLimit)
@@ -546,7 +546,7 @@ private struct ToolResultCard: View {
self.expanded.toggle()
}
.buttonStyle(.plain)
.font(.caption)
.font(OpenClawChatTypography.caption)
.foregroundStyle(.secondary)
}
}
@@ -602,7 +602,7 @@ struct ChatTypingIndicatorBubble: View {
HStack(spacing: 9) {
TypingDots()
Text("Writing")
.font(.caption.weight(.semibold))
.font(OpenClawChatTypography.captionSemiBold)
.foregroundStyle(.secondary)
}
.padding(.vertical, self.isClean ? 5 : (self.style == .standard ? 10 : 9))
@@ -694,7 +694,7 @@ struct ChatPendingToolsBubble: View {
var body: some View {
VStack(alignment: .leading, spacing: 8) {
Label("Running tools…", systemImage: "hammer")
.font(.caption)
.font(OpenClawChatTypography.caption)
.foregroundStyle(.secondary)
ForEach(self.toolCalls) { call in
@@ -702,14 +702,14 @@ struct ChatPendingToolsBubble: View {
VStack(alignment: .leading, spacing: 4) {
HStack(alignment: .firstTextBaseline, spacing: 8) {
Text("\(display.emoji) \(display.label)")
.font(.footnote.monospaced())
.font(OpenClawChatTypography.mono(size: 13, relativeTo: .footnote))
.lineLimit(1)
Spacer(minLength: 0)
ProgressView().controlSize(.mini)
}
if let detail = display.detailLine, !detail.isEmpty {
Text(detail)
.font(.caption.monospaced())
.font(OpenClawChatTypography.mono(size: 12, relativeTo: .caption))
.foregroundStyle(.secondary)
.lineLimit(2)
}
@@ -15,13 +15,13 @@ struct ChatSessionsSheet: View {
} label: {
VStack(alignment: .leading, spacing: 4) {
Text(session.displayName ?? session.key)
.font(.system(.body, design: .monospaced))
.font(OpenClawChatTypography.mono(size: 17, relativeTo: .body))
.lineLimit(1)
if let updatedAt = session.updatedAt, updatedAt > 0 {
Text(Date(timeIntervalSince1970: updatedAt / 1000).formatted(
date: .abbreviated,
time: .shortened))
.font(.caption)
.font(OpenClawChatTypography.caption)
.foregroundStyle(.secondary)
}
}
@@ -0,0 +1,78 @@
import Foundation
import SwiftUI
enum OpenClawChatTypography {
static var title3: Font {
display(size: 22, weight: .bold, relativeTo: .title2)
}
static var title3SemiBold: Font {
display(size: 22, weight: .semibold, relativeTo: .title2)
}
static var headline: Font {
display(size: 17, weight: .semibold, relativeTo: .headline)
}
static var callout: Font {
body(size: 16, weight: .regular, relativeTo: .callout)
}
static var body: Font {
body(size: 17, weight: .regular, relativeTo: .body)
}
static var footnote: Font {
body(size: 13, weight: .regular, relativeTo: .footnote)
}
static var footnoteSemiBold: Font {
body(size: 13, weight: .semibold, relativeTo: .footnote)
}
static var caption: Font {
body(size: 12, weight: .regular, relativeTo: .caption)
}
static var captionSemiBold: Font {
body(size: 12, weight: .semibold, relativeTo: .caption)
}
static var caption2: Font {
body(size: 11, weight: .regular, relativeTo: .caption2)
}
static func avatar(size: CGFloat) -> Font {
body(size: size, weight: .bold, relativeTo: .caption)
}
static func body(size: CGFloat, weight: Font.Weight, relativeTo textStyle: Font.TextStyle) -> Font {
#if os(iOS)
Font.custom(Self.bodyPostScriptName, size: size, relativeTo: textStyle).weight(weight)
#else
Font.system(size: size, weight: weight)
#endif
}
static func display(size: CGFloat, weight: Font.Weight, relativeTo textStyle: Font.TextStyle) -> Font {
#if os(iOS)
Font.custom(Self.displayPostScriptName, size: size, relativeTo: textStyle).weight(weight)
#else
Font.system(size: size, weight: weight)
#endif
}
static func mono(size: CGFloat, weight: Font.Weight = .regular, relativeTo textStyle: Font.TextStyle) -> Font {
#if os(iOS)
let name = weight == .semibold ? Self.monoSemiBoldPostScriptName : Self.monoPostScriptName
return Font.custom(name, size: size, relativeTo: textStyle)
#else
return Font.system(size: size, weight: weight, design: .monospaced)
#endif
}
private static let displayPostScriptName = "RedHatDisplay-Regular"
private static let bodyPostScriptName = "Inter-Regular"
private static let monoPostScriptName = "JetBrainsMono-Regular"
private static let monoSemiBoldPostScriptName = "JetBrainsMono-SemiBold"
}
@@ -829,12 +829,12 @@ private struct ChatAssistantIntroCard: View {
var body: some View {
VStack(alignment: .leading, spacing: 8) {
Image(systemName: "sparkles")
.font(.title3.weight(.medium))
.font(OpenClawChatTypography.title3)
.foregroundStyle(OpenClawChatTheme.accent)
.accessibilityHidden(true)
Text(self.text)
.font(.title3.weight(.semibold))
.font(OpenClawChatTypography.title3SemiBold)
.foregroundStyle(OpenClawChatTheme.assistantText)
.multilineTextAlignment(.leading)
}
@@ -851,7 +851,7 @@ private struct ChatLoadingBubble: View {
ProgressView()
.controlSize(.small)
Text("Loading chat")
.font(.caption.weight(.semibold))
.font(OpenClawChatTypography.captionSemiBold)
.foregroundStyle(.secondary)
}
.padding(.vertical, 9)
@@ -874,17 +874,17 @@ private struct ChatNoticeCard: View {
var body: some View {
HStack(alignment: .center, spacing: 14) {
Image(systemName: self.systemImage)
.font(.system(size: 19, weight: .semibold))
.font(OpenClawChatTypography.display(size: 19, weight: .semibold, relativeTo: .headline))
.foregroundStyle(self.tint)
.frame(width: 42, height: 42)
.background(self.tint.opacity(0.14), in: Circle())
VStack(alignment: .leading, spacing: 4) {
Text(self.title)
.font(.headline)
.font(OpenClawChatTypography.headline)
Text(self.message)
.font(.callout)
.font(OpenClawChatTypography.callout)
.foregroundStyle(.secondary)
.multilineTextAlignment(.leading)
.lineLimit(3)
@@ -920,16 +920,16 @@ private struct ChatNoticeBanner: View {
var body: some View {
HStack(alignment: .top, spacing: 10) {
Image(systemName: self.systemImage)
.font(.system(size: 15, weight: .semibold))
.font(OpenClawChatTypography.display(size: 15, weight: .semibold, relativeTo: .subheadline))
.foregroundStyle(self.tint)
.padding(.top, 1)
VStack(alignment: .leading, spacing: 3) {
Text(self.title)
.font(.caption.weight(.semibold))
.font(OpenClawChatTypography.captionSemiBold)
Text(self.message)
.font(.caption)
.font(OpenClawChatTypography.caption)
.foregroundStyle(.secondary)
.lineLimit(2)
}