mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-12 21:53:00 -06:00
fix(ios): align branded typography licenses
This commit is contained in:
+262
-214
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
|
||||
|
||||
+5
-10
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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.
@@ -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>
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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
@@ -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)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user