Some checks failed
ClawSweeper Dispatch / dispatch (push) Has been cancelled
CodeQL / Security High (actions) (push) Has been cancelled
CodeQL / Security High (channel-runtime-boundary) (push) Has been cancelled
CodeQL / Security High (core-auth-secrets) (push) Has been cancelled
CodeQL / Security High (mcp-process-tool-boundary) (push) Has been cancelled
CodeQL / Security High (network-ssrf-boundary) (push) Has been cancelled
CodeQL / Security High (plugin-trust-boundary) (push) Has been cancelled
CodeQL / Security High (process-exec-boundary) (push) Has been cancelled
Docs Sync Publish Repo / sync-publish-repo (push) Has been cancelled
Docs / docs (push) Has been cancelled
OpenClaw Stable Main Closeout / Resolve stable release closeout inputs (push) Has been cancelled
OpenClaw Stable Main Closeout / Verify stable main closeout (push) Has been cancelled
Workflow Sanity / no-tabs (push) Has been cancelled
Workflow Sanity / actionlint (push) Has been cancelled
Workflow Sanity / generated-doc-baselines (push) Has been cancelled
CI / runner-admission (push) Has been cancelled
CI / preflight (push) Has been cancelled
CI / security-fast (push) Has been cancelled
CI / pnpm-store-warmup (push) Has been cancelled
CI / build-artifacts (push) Has been cancelled
CI / native-i18n (push) Has been cancelled
CI / ${{ matrix.check_name }} (push) Has been cancelled
CI / ${{ matrix.checkName }} (push) Has been cancelled
CI / checks-node-compat-node22 (push) Has been cancelled
CI / check-bundled-channel-config-metadata (push) Has been cancelled
CI / check-dependencies (push) Has been cancelled
CI / check-guards (push) Has been cancelled
CI / check-lint (push) Has been cancelled
CI / check-prod-types (push) Has been cancelled
CI / check-shrinkwrap (push) Has been cancelled
CI / check-test-types (push) Has been cancelled
CI / check-additional-boundaries-a (push) Has been cancelled
CI / check-additional-boundaries-bcd (push) Has been cancelled
CI / check-additional-extension-bundled (push) Has been cancelled
CI / check-additional-extension-channels (push) Has been cancelled
CI / check-additional-extension-package-boundary (push) Has been cancelled
CI / check-additional-runtime-topology-architecture (push) Has been cancelled
CI / check-session-accessor-boundary (push) Has been cancelled
CI / check-session-transcript-reader-boundary (push) Has been cancelled
CI / check-docs (push) Has been cancelled
CI / skills-python (push) Has been cancelled
CI / macos-swift (push) Has been cancelled
CI / ios-build (push) Has been cancelled
CI / ci-timings-summary (push) Has been cancelled
Native App Locale Refresh / Refresh native fa (push) Has been cancelled
Native App Locale Refresh / Refresh native fr (push) Has been cancelled
Native App Locale Refresh / Refresh native hi (push) Has been cancelled
Native App Locale Refresh / Refresh native id (push) Has been cancelled
Native App Locale Refresh / Refresh native it (push) Has been cancelled
Native App Locale Refresh / Refresh native ja-JP (push) Has been cancelled
Control UI Locale Refresh / plan (push) Has been cancelled
Control UI Locale Refresh / Refresh ${{ matrix.locale }} (push) Has been cancelled
Control UI Locale Refresh / Commit control UI locale refresh (push) Has been cancelled
Live Media Runner Image / Build live media runner image (push) Has been cancelled
Native App Locale Refresh / Refresh native ar (push) Has been cancelled
Native App Locale Refresh / Refresh native de (push) Has been cancelled
Native App Locale Refresh / Refresh native es (push) Has been cancelled
Native App Locale Refresh / Refresh native ko (push) Has been cancelled
Native App Locale Refresh / Refresh native nl (push) Has been cancelled
Native App Locale Refresh / Refresh native pl (push) Has been cancelled
Native App Locale Refresh / Refresh native pt-BR (push) Has been cancelled
Native App Locale Refresh / Refresh native ru (push) Has been cancelled
Native App Locale Refresh / Refresh native sv (push) Has been cancelled
Native App Locale Refresh / Refresh native th (push) Has been cancelled
Native App Locale Refresh / Refresh native tr (push) Has been cancelled
Native App Locale Refresh / Refresh native uk (push) Has been cancelled
Native App Locale Refresh / Refresh native vi (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-CN (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-TW (push) Has been cancelled
Native App Locale Refresh / Commit native locale refresh (push) Has been cancelled
Plugin Init Scaffold Validation / Validate provider scaffold (push) Has been cancelled
Plugin NPM Release / preview_plugins_npm (push) Has been cancelled
Plugin NPM Release / Validate release publish approval (push) Has been cancelled
Plugin NPM Release / preview_plugin_pack (push) Has been cancelled
Plugin NPM Release / publish_plugins_npm (push) Has been cancelled
Sandbox Common Smoke / sandbox-common-smoke (push) Has been cancelled
Website Installer Sync / static (push) Has been cancelled
Website Installer Sync / linux-docker (push) Has been cancelled
Website Installer Sync / macos-installer (push) Has been cancelled
Website Installer Sync / windows-installer (push) Has been cancelled
Website Installer Sync / sync-website (push) Has been cancelled
Adolf is a fork/vendored clone of github.com/openclaw/openclaw (v2026.6.11), free to diverge. Tree copied sans upstream .git; upstream remote added for future syncs. Node pinned to 24 (.nvmrc); engines already require >=22.19. Preserves docs/ARCHITECTURE.md. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LeqyaxJF2nbRXJtae2kNB2
448 lines
15 KiB
Swift
448 lines
15 KiB
Swift
import CoreText
|
|
import SwiftUI
|
|
import UIKit
|
|
|
|
enum OpenClawType {
|
|
// MARK: - Display — Red Hat Display
|
|
|
|
static var title1: Font {
|
|
scaledDisplay(weight: Display.heavyTitle, size: 34, relativeTo: .largeTitle)
|
|
}
|
|
|
|
static var title2: Font {
|
|
scaledDisplay(weight: Display.heavyTitle, size: 28, relativeTo: .title1)
|
|
}
|
|
|
|
static var title3: Font {
|
|
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(weight: Display.opticalSemiBold, size: 17, relativeTo: .headline)
|
|
}
|
|
|
|
static var headlineBold: Font {
|
|
scaledDisplay(weight: Display.opticalBold, size: 17, relativeTo: .headline)
|
|
}
|
|
|
|
// MARK: - Body — Inter
|
|
|
|
static var body: Font {
|
|
scaledBody(weight: Body.regular, size: 17, relativeTo: .body)
|
|
}
|
|
|
|
static var callout: Font {
|
|
scaledBody(weight: Body.regular, size: 16, relativeTo: .callout)
|
|
}
|
|
|
|
static var subhead: Font {
|
|
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(weight: Display.opticalSemiBold, size: 15, relativeTo: .subheadline)
|
|
}
|
|
|
|
static var subheadBold: Font {
|
|
scaledDisplay(weight: Display.opticalBold, size: 15, relativeTo: .subheadline)
|
|
}
|
|
|
|
static var footnote: Font {
|
|
scaledBody(weight: Body.regular, size: 13, relativeTo: .footnote)
|
|
}
|
|
|
|
static var footnoteMedium: Font {
|
|
scaledBody(weight: Body.medium, size: 13, relativeTo: .footnote)
|
|
}
|
|
|
|
static var footnoteSemiBold: Font {
|
|
scaledBody(weight: Body.semiBold, size: 13, relativeTo: .footnote)
|
|
}
|
|
|
|
static var caption: Font {
|
|
scaledBody(weight: Body.regular, size: 12, relativeTo: .caption1)
|
|
}
|
|
|
|
static var captionMedium: Font {
|
|
scaledBody(weight: Body.medium, size: 12, relativeTo: .caption1)
|
|
}
|
|
|
|
static var captionSemiBold: Font {
|
|
scaledBody(weight: Body.semiBold, size: 12, relativeTo: .caption1)
|
|
}
|
|
|
|
static var captionBold: Font {
|
|
scaledBody(weight: Body.bold, size: 12, relativeTo: .caption1)
|
|
}
|
|
|
|
static func avatar(size: CGFloat) -> Font {
|
|
self.scaledBody(weight: Body.bold, size: size, relativeTo: .caption1)
|
|
}
|
|
|
|
static var caption2: Font {
|
|
scaledBody(weight: Body.regular, size: 11, relativeTo: .caption2)
|
|
}
|
|
|
|
static var caption2Medium: Font {
|
|
scaledBody(weight: Body.medium, size: 11, relativeTo: .caption2)
|
|
}
|
|
|
|
static var caption2SemiBold: Font {
|
|
scaledBody(weight: Body.semiBold, size: 11, relativeTo: .caption2)
|
|
}
|
|
|
|
static var caption2Bold: Font {
|
|
scaledDisplay(weight: Display.opticalBold, size: 11, relativeTo: .caption2)
|
|
}
|
|
|
|
static var title2SemiBold: Font {
|
|
scaledDisplay(weight: Display.opticalSemiBold, size: 28, relativeTo: .title1)
|
|
}
|
|
|
|
// MARK: - Mono — JetBrains Mono
|
|
|
|
static var mono: Font {
|
|
scaledMono(name: Mono.regular, size: 14, relativeTo: .body)
|
|
}
|
|
|
|
static var monoSmall: Font {
|
|
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)
|
|
}
|
|
|
|
static var monoHeadline: Font {
|
|
scaledMono(name: Mono.medium, size: 17, relativeTo: .headline)
|
|
}
|
|
|
|
/// PostScript names for bundled fonts. Keep aligned with `UIAppFonts` in `project.yml`.
|
|
static let registeredPostScriptNames: [String] = [
|
|
Display.postScriptName,
|
|
Body.postScriptName,
|
|
Body.italicPostScriptName,
|
|
Mono.regular,
|
|
Mono.medium,
|
|
Mono.semiBold,
|
|
]
|
|
|
|
@MainActor
|
|
static func installUIKitAppearance() {
|
|
self.applyUIKitAppearance(self.makeUIKitAppearanceFonts())
|
|
}
|
|
|
|
@MainActor
|
|
static func refreshUIKitAppearance(in windows: [UIWindow]) {
|
|
let fonts = self.makeUIKitAppearanceFonts()
|
|
self.applyUIKitAppearance(fonts)
|
|
for window in windows {
|
|
self.applyUIKitTypography(fonts, to: window)
|
|
}
|
|
}
|
|
|
|
private static func makeUIKitAppearanceFonts() -> UIKitAppearanceFonts {
|
|
UIKitAppearanceFonts(
|
|
inlineNavigationTitleFont: self.scaledDisplayUIFont(
|
|
weight: Display.opticalSemiBold,
|
|
size: 17,
|
|
relativeTo: .headline,
|
|
maximumPointSize: 22),
|
|
largeNavigationTitleFont: self.scaledDisplayUIFont(
|
|
weight: Display.heavyTitle,
|
|
size: 34,
|
|
relativeTo: .largeTitle,
|
|
maximumPointSize: 44),
|
|
tabBarNormalFont: self.scaledBodyUIFont(
|
|
weight: Body.medium,
|
|
size: 11,
|
|
relativeTo: .caption2,
|
|
maximumPointSize: 13),
|
|
tabBarSelectedFont: self.scaledBodyUIFont(
|
|
weight: Body.semiBold,
|
|
size: 11,
|
|
relativeTo: .caption2,
|
|
maximumPointSize: 13),
|
|
segmentedNormalFont: self.scaledBodyUIFont(
|
|
weight: Body.medium,
|
|
size: 13,
|
|
relativeTo: .footnote,
|
|
maximumPointSize: 16),
|
|
segmentedSelectedFont: self.scaledBodyUIFont(
|
|
weight: Body.semiBold,
|
|
size: 13,
|
|
relativeTo: .footnote,
|
|
maximumPointSize: 16),
|
|
barButtonFont: self.scaledBodyUIFont(
|
|
weight: Body.semiBold,
|
|
size: 17,
|
|
relativeTo: .body,
|
|
maximumPointSize: 22),
|
|
disabledBarButtonFont: self.scaledBodyUIFont(
|
|
weight: Body.regular,
|
|
size: 17,
|
|
relativeTo: .body,
|
|
maximumPointSize: 22),
|
|
textInputFont: self.scaledBodyUIFont(
|
|
weight: Body.regular,
|
|
size: 17,
|
|
relativeTo: .body,
|
|
maximumPointSize: 22))
|
|
}
|
|
|
|
@MainActor
|
|
private static func applyUIKitAppearance(_ fonts: UIKitAppearanceFonts) {
|
|
let navigationBar = UINavigationBar.appearance()
|
|
self.applyNavigationBarTitleAttributes(fonts, to: navigationBar)
|
|
|
|
let tabBarItem = UITabBarItem.appearance()
|
|
self.applyTabBarItemAttributes(fonts, to: tabBarItem)
|
|
|
|
let segmentedControl = UISegmentedControl.appearance()
|
|
self.applySegmentedControlAttributes(fonts, to: segmentedControl)
|
|
|
|
let barButtonItem = UIBarButtonItem.appearance()
|
|
self.applyBarButtonItemAttributes(fonts, to: barButtonItem)
|
|
|
|
UITextField.appearance().font = fonts.textInputFont
|
|
UITextView.appearance().font = fonts.textInputFont
|
|
UISearchTextField.appearance().font = fonts.textInputFont
|
|
}
|
|
|
|
@MainActor
|
|
private static func applyUIKitTypography(_ fonts: UIKitAppearanceFonts, to view: UIView) {
|
|
switch view {
|
|
case let navigationBar as UINavigationBar:
|
|
self.applyNavigationBarTitleAttributes(fonts, to: navigationBar)
|
|
for item in navigationBar.items ?? [] {
|
|
self.applyNavigationItemBarButtonAttributes(fonts, to: item)
|
|
}
|
|
if let topItem = navigationBar.topItem {
|
|
self.applyNavigationItemBarButtonAttributes(fonts, to: topItem)
|
|
}
|
|
case let tabBar as UITabBar:
|
|
tabBar.items?.forEach { self.applyTabBarItemAttributes(fonts, to: $0) }
|
|
case let segmentedControl as UISegmentedControl:
|
|
self.applySegmentedControlAttributes(fonts, to: segmentedControl)
|
|
case let toolbar as UIToolbar:
|
|
toolbar.items?.forEach { self.applyBarButtonItemAttributes(fonts, to: $0) }
|
|
case let searchTextField as UISearchTextField:
|
|
searchTextField.font = fonts.textInputFont
|
|
default:
|
|
break
|
|
}
|
|
|
|
for subview in view.subviews {
|
|
self.applyUIKitTypography(fonts, to: subview)
|
|
}
|
|
}
|
|
|
|
@MainActor
|
|
private static func applyNavigationBarTitleAttributes(
|
|
_ fonts: UIKitAppearanceFonts,
|
|
to navigationBar: UINavigationBar)
|
|
{
|
|
var titleAttributes = navigationBar.titleTextAttributes ?? [:]
|
|
titleAttributes[.font] = fonts.inlineNavigationTitleFont
|
|
navigationBar.titleTextAttributes = titleAttributes
|
|
|
|
var largeTitleAttributes = navigationBar.largeTitleTextAttributes ?? [:]
|
|
largeTitleAttributes[.font] = fonts.largeNavigationTitleFont
|
|
navigationBar.largeTitleTextAttributes = largeTitleAttributes
|
|
}
|
|
|
|
@MainActor
|
|
private static func applyTabBarItemAttributes(_ fonts: UIKitAppearanceFonts, to tabBarItem: UITabBarItem) {
|
|
tabBarItem.setTitleTextAttributes([.font: fonts.tabBarNormalFont], for: .normal)
|
|
tabBarItem.setTitleTextAttributes([.font: fonts.tabBarSelectedFont], for: .selected)
|
|
}
|
|
|
|
@MainActor
|
|
private static func applySegmentedControlAttributes(
|
|
_ fonts: UIKitAppearanceFonts,
|
|
to segmentedControl: UISegmentedControl)
|
|
{
|
|
segmentedControl.setTitleTextAttributes([.font: fonts.segmentedNormalFont], for: .normal)
|
|
segmentedControl.setTitleTextAttributes([.font: fonts.segmentedSelectedFont], for: .selected)
|
|
}
|
|
|
|
@MainActor
|
|
private static func applyBarButtonItemAttributes(_ fonts: UIKitAppearanceFonts, to barButtonItem: UIBarButtonItem) {
|
|
barButtonItem.setTitleTextAttributes([.font: fonts.barButtonFont], for: .normal)
|
|
barButtonItem.setTitleTextAttributes([.font: fonts.barButtonFont], for: .highlighted)
|
|
barButtonItem.setTitleTextAttributes([.font: fonts.barButtonFont], for: .selected)
|
|
barButtonItem.setTitleTextAttributes([.font: fonts.disabledBarButtonFont], for: .disabled)
|
|
}
|
|
|
|
@MainActor
|
|
private static func applyNavigationItemBarButtonAttributes(
|
|
_ fonts: UIKitAppearanceFonts,
|
|
to item: UINavigationItem)
|
|
{
|
|
let barButtonItems = [
|
|
item.backBarButtonItem,
|
|
item.leftBarButtonItem,
|
|
item.rightBarButtonItem,
|
|
] + (item.leftBarButtonItems ?? []) + (item.rightBarButtonItems ?? [])
|
|
barButtonItems.compactMap(\.self).forEach { self.applyBarButtonItemAttributes(fonts, to: $0) }
|
|
}
|
|
|
|
private struct UIKitAppearanceFonts {
|
|
let inlineNavigationTitleFont: UIFont
|
|
let largeNavigationTitleFont: UIFont
|
|
let tabBarNormalFont: UIFont
|
|
let tabBarSelectedFont: UIFont
|
|
let segmentedNormalFont: UIFont
|
|
let segmentedSelectedFont: UIFont
|
|
let barButtonFont: UIFont
|
|
let disabledBarButtonFont: UIFont
|
|
let textInputFont: UIFont
|
|
}
|
|
|
|
private enum Display {
|
|
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 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 {
|
|
static let regular = "JetBrainsMono-Regular"
|
|
static let medium = "JetBrainsMono-Medium"
|
|
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(
|
|
weight: CGFloat,
|
|
size: CGFloat,
|
|
relativeTo textStyle: UIFont.TextStyle) -> Font
|
|
{
|
|
Font(
|
|
self.scaledDisplayUIFont(
|
|
weight: weight,
|
|
size: size,
|
|
relativeTo: textStyle))
|
|
}
|
|
|
|
private static func scaledBody(
|
|
weight: CGFloat,
|
|
size: CGFloat,
|
|
relativeTo textStyle: UIFont.TextStyle) -> Font
|
|
{
|
|
Font(
|
|
self.scaledBodyUIFont(
|
|
weight: weight,
|
|
size: size,
|
|
relativeTo: textStyle))
|
|
}
|
|
|
|
private static func scaledMono(
|
|
name: String,
|
|
size: CGFloat,
|
|
relativeTo textStyle: UIFont.TextStyle) -> Font
|
|
{
|
|
self.scaledFont(name: name, size: size, relativeTo: textStyle)
|
|
}
|
|
|
|
private static func scaledDisplayUIFont(
|
|
weight: CGFloat,
|
|
size: CGFloat,
|
|
relativeTo textStyle: UIFont.TextStyle,
|
|
maximumPointSize: CGFloat? = nil) -> UIFont
|
|
{
|
|
self.scaledVariableUIFont(
|
|
name: Display.postScriptName,
|
|
size: size,
|
|
relativeTo: textStyle,
|
|
maximumPointSize: maximumPointSize,
|
|
variations: [self.fontWeightAxis: weight])
|
|
}
|
|
|
|
private static func scaledBodyUIFont(
|
|
weight: CGFloat,
|
|
size: CGFloat,
|
|
relativeTo textStyle: UIFont.TextStyle,
|
|
maximumPointSize: CGFloat? = nil) -> UIFont
|
|
{
|
|
self.scaledVariableUIFont(
|
|
name: Body.postScriptName,
|
|
size: size,
|
|
relativeTo: textStyle,
|
|
maximumPointSize: maximumPointSize,
|
|
variations: [
|
|
self.fontWeightAxis: weight,
|
|
self.opticalSizeAxis: min(max(size, 14), 32),
|
|
])
|
|
}
|
|
|
|
private static func scaledVariableUIFont(
|
|
name: String,
|
|
size: CGFloat,
|
|
relativeTo textStyle: UIFont.TextStyle,
|
|
maximumPointSize: CGFloat? = nil,
|
|
variations: [NSNumber: CGFloat]) -> UIFont
|
|
{
|
|
guard UIFont(name: name, size: size) != nil else {
|
|
let fallback = UIFont.systemFont(ofSize: size)
|
|
return self.scaledUIFont(fallback, relativeTo: textStyle, maximumPointSize: maximumPointSize)
|
|
}
|
|
|
|
let descriptor = UIFontDescriptor(fontAttributes: [
|
|
.name: name,
|
|
kCTFontVariationAttribute as UIFontDescriptor.AttributeName: variations,
|
|
])
|
|
let base = UIFont(descriptor: descriptor, size: size)
|
|
return self.scaledUIFont(base, relativeTo: textStyle, maximumPointSize: maximumPointSize)
|
|
}
|
|
|
|
private static func scaledFont(
|
|
name: String,
|
|
size: CGFloat,
|
|
relativeTo textStyle: UIFont.TextStyle) -> Font
|
|
{
|
|
let base = UIFont(name: name, size: size) ?? UIFont.systemFont(ofSize: size)
|
|
let scaled = UIFontMetrics(forTextStyle: textStyle).scaledFont(for: base)
|
|
return Font(scaled)
|
|
}
|
|
|
|
private static func scaledUIFont(
|
|
_ base: UIFont,
|
|
relativeTo textStyle: UIFont.TextStyle,
|
|
maximumPointSize: CGFloat?) -> UIFont
|
|
{
|
|
let metrics = UIFontMetrics(forTextStyle: textStyle)
|
|
if let maximumPointSize {
|
|
return metrics.scaledFont(for: base, maximumPointSize: maximumPointSize)
|
|
}
|
|
return metrics.scaledFont(for: base)
|
|
}
|
|
}
|