提示信息
import SwiftUI
// MARK: - Helpers (辅助扩展,用于支持 Token 解析)
extension Color {
init(hex: String) {
let hex = hex.trimmingCharacters(in: CharacterSet.alphanumerics.inverted)
var int: UInt64 = 0
Scanner(string: hex).scanHexInt64(&int)
let a, r, g, b: UInt64
switch hex.count {
case 3: // RGB (12-bit)
(a, r, g, b) = (255, (int >> 8) * 17, (int >> 4 & 0xF) * 17, (int & 0xF) * 17)
case 6: // RGB (24-bit)
(a, r, g, b) = (255, int >> 16, int >> 8 & 0xFF, int & 0xFF)
case 8: // ARGB (32-bit)
(a, r, g, b) = (int >> 24, int >> 16 & 0xFF, int >> 8 & 0xFF, int & 0xFF)
default:
(a, r, g, b) = (1, 1, 1, 0)
}
self.init(
.sRGB,
red: Double(r) / 255,
green: Double(g) / 255,
blue: Double(b) / 255,
opacity: Double(a) / 255
)
}
init(rgba r: Double, g: Double, b: Double, a: Double) {
self.init(.sRGB, red: r / 255, green: g / 255, blue: b / 255, opacity: a)
}
}
struct Shadow {
let color: Color
let radius: CGFloat
let x: CGFloat
let y: CGFloat
}
// MARK: - 1. 颜色系统(Color Tokens)
extension Color {
// MARK: Background Colors
static let colorBackground = Color(hex: "#0E0E0E") // 主页背景(HomeExplore)
static let colorSurface1 = Color(hex: "#131313") // 卡片/固定消息背景
static let colorSurface2 = Color(hex: "#1F1F1F") // 头像边框背景
static let colorSurface3 = Color(hex: "#27272A") // 群组头像背景、次级卡片
static let colorNavBackground = Color(rgba: 9, g: 9, b: 11, a: 0.60) // 顶栏/导航 backdrop
static let colorBottomNavBg = Color(rgba: 24, g: 24, b: 27, a: 0.60) // 底栏 backdrop
// MARK: Brand / Accent
static let colorBrandBlue = Color(hex: "#5F9EFF") // 主强调色(在线点、已读、badge 背景)
static let colorBrandBlueShadow = Color(hex: "#5F9EFF").opacity(0.80) // 蓝点光晕
static let colorBlueTint = Color(hex: "#5F9EFF").opacity(0.10) // 背景光晕
// MARK: Text Colors
static let colorTextPrimary = Color(hex: "#E5E5E5") // 主标题、消息正文(接收方)
static let colorTextHeading = Color(hex: "#F4F4F5") // 顶栏标题
static let colorTextSecondary = Color(hex: "#ABABAB") // 消息预览、次要文字
static let colorTextMuted = Color(hex: "#757575") // 时间戳、分类标签
static let colorTextLink = Color(hex: "#5F9EFF") // 群组发言人名称、链接
static let colorTextOnDark = Color(hex: "#3F4041") // 发送方气泡文字(浅色气泡上)
static let colorTextOnBadge = Color(hex: "#001F46") // Badge 数字(深蓝底白)
static let colorTextGray = Color(hex: "#A1A1AA") // Inactive 图标
// MARK: Bubble Colors
static let colorBubbleIncoming = LinearGradient(
colors: [Color(rgba:117,g:117,b:117,a:0.15), Color(rgba:117,g:117,b:117,a:0)],
startPoint: .topLeading, endPoint: .bottomTrailing
)
static let colorBubbleOutgoing = Color(hex: "#C6C6C7")
static let colorAvatarGradientJulian = LinearGradient(
colors: [Color(hex: "#6366F1"), Color(hex: "#9333EA")],
startPoint: .init(x: 0.26, y: 0),
endPoint: .init(x: 0.74, y: 1)
)
// MARK: Borders
static let colorBorderSubtle = Color.white.opacity(0.05) // 卡片、底栏外框
static let colorBorderMedium = Color(rgba:72,g:72,b:72,a:0.10) // 状态指示器边框
static let colorBorderNormal = Color(rgba:72,g:72,b:72,a:0.20) // 头像边框
static let colorBorderWhiteTop = Color.white.opacity(0.20) // 玻璃球顶部高光边
// MARK: Status
static let colorStatusOnline = Color(hex: "#5F9EFF") // 在线绿点(本设计用蓝色)
static let colorStatusBorder = Color(hex: "#131313") // 在线点外圈(匹配卡片背景)
}
// MARK: - 2. 字体系统(Typography Tokens)
enum AppFontFamily {
static let inter = "Inter"
static let manrope = "Manrope"
}
struct AppFont {
// ── 标题 ───────────────────────────────────────────────────
static let displayXL = Font.custom("Manrope-ExtraBold", size: 30)
static let displayL = Font.custom("Manrope-ExtraBold", size: 24)
static let headingL = Font.custom("Inter-SemiBold", size: 20)
static let headingM = Font.custom("Manrope-SemiBold", size: 16)
// ── 正文 ───────────────────────────────────────────────────
static let bodyM = Font.custom("Inter-Regular", size: 14)
static let bodyS = Font.custom("Inter-Regular", size: 12)
// ── 标签 / 辅助 ────────────────────────────────────────────
static let labelM = Font.custom("Inter-Medium", size: 11)
static let labelS = Font.custom("Inter-SemiBold", size: 10)
static let labelXS = Font.custom("Inter-Regular", size: 10)
// ── 状态 ───────────────────────────────────────────────────
static let statusTag = Font.custom("Inter-Regular", size: 10)
// ── Badge ──────────────────────────────────────────────────
static let badge = Font.custom("Inter-SemiBold", size: 10)
}
// MARK: - 3. 间距系统(Spacing Tokens)
enum Spacing {
static let xxs: CGFloat = 2 // 消息行间距
static let xs: CGFloat = 4 // 气泡时间戳上边距,头像徽章偏移
static let s: CGFloat = 8 // 分组标题水平内边距
static let m: CGFloat = 12 // 状态栏水平内边距(单侧)
static let l: CGFloat = 16 // 列表项内边距,气泡左内边距
static let xl: CGFloat = 24 // 页面水平外边距,顶栏内边距
static let xxl: CGFloat = 48 // 底栏图标间距
// 列表项间距
static let chatGap: CGFloat = 16 // 头像与文字间距
static let sectionGap: CGFloat = 24 // Pinned / Recent 区块间距
// 页面边距
static let pageTop: CGFloat = 96 // 列表页顶部(顶栏高度 64 + 32)
static let pageBottom: CGFloat = 128 // 列表页底部(底栏高度 + 余量)
static let pageH: CGFloat = 24 // 页面水平 padding
}
// MARK: - 4. 圆角系统(Border Radius Tokens)
enum Radius {
static let full: CGFloat = 9999 // 圆形:头像徽章、底栏、状态指示器、FAB
static let xl: CGFloat = 32 // 底部导航栏
static let l: CGFloat = 16 // 聊天列表卡片、头像、气泡(主)
static let m: CGFloat = 9999 // 发送方气泡(圆角矩形):bl-tr-tl 均为 16
}
// MARK: - 5. 阴影与光效(Shadow & Glow Tokens)
struct AppShadow {
static let topBar = Shadow(color: Color.black.opacity(0.36), radius: 32, x: 0, y: 8)
static let bottomNav = Shadow(color: Color.black.opacity(0.25), radius: 50, x: 0, y: 25)
static let bubble = Shadow(color: Color.black.opacity(0.36), radius: 32, x: 0, y: 8)
static let chatCard = [
Shadow(color: Color.black.opacity(0.10), radius: 15, x: 0, y: 10),
Shadow(color: Color.black.opacity(0.10), radius: 6, x: 0, y: 4)
]
static let onlineDot = Shadow(color: Color(hex: "#5F9EFF").opacity(0.80), radius: 8, x: 0, y: 0)
static let orbOuter = Shadow(color: Color(hex: "#5F9EFF").opacity(0.30), radius: 80, x: 0, y: 0)
static let fab = Shadow(color: Color.black.opacity(0.25), radius: 50, x: 0, y: 25)
static let avatar = Shadow(color: Color.black.opacity(0.25), radius: 50, x: 0, y: 25)
}
// MARK: - 6. 尺寸规格(Size Tokens)
enum AppSize {
static let topBarHeight: CGFloat = 64
static let bottomNavWidth: CGFloat = 351
static let bottomNavPaddingH: CGFloat = 45.83
static let bottomNavPaddingV: CGFloat = 13
static let bottomNavBottom: CGFloat = 31.5
static let navIconActive: CGFloat = 48
static let navIconTap: CGFloat = 48
static let topBarIcon: CGFloat = 18
static let topBarIconArea: CGFloat = 32
static let avatarList: CGFloat = 56
static let avatarProfile: CGFloat = 112
static let avatarTopBar: CGFloat = 32
static let onlineBadge: CGFloat = 16
static let onlineBadgeBorder: CGFloat = 4
static let badgeH: CGFloat = 20
static let badgeMinW: CGFloat = 20
static let badgePaddingH: CGFloat = 6.81
static let fabSize: CGFloat = 56
static let fabBottom: CGFloat = 112
static let orbSize: CGFloat = 256
static let orbInset: CGFloat = 16
static let statusDot: CGFloat = 6
static let statusPaddingH: CGFloat = 25
static let statusPaddingV: CGFloat = 13
}
// MARK: - 7. 模糊效果(Blur Tokens)
enum AppBlur {
static let topBar: CGFloat = 32
static let bottomNav: CGFloat = 32
static let status: CGFloat = 6
static let orb: CGFloat = 20
static let bgGlow1: CGFloat = 60
static let bgGlow2: CGFloat = 50
static let avatarAura: CGFloat = 12
}
// MARK: - 8. 图标 SVG 路径清单(Icon SVG Paths)
enum AppIconPath {
static let home = "M3 18C2.16667 18 1.45833 17.7083 0.875 17.125C0.291667 16.5417 0 15.8333 0 15V9C0 8.6 0.075 8.21667 0.225 7.85C0.375 7.48333 0.591667 7.15833 0.875 6.875L6.875 0.875C7.15833 0.575 7.4875 0.354167 7.8625 0.2125C8.2375 0.0708333 8.61667 0 9 0C9.38333 0 9.75833 0.0708333 10.125 0.2125C10.4917 0.354167 10.825 0.575 11.125 0.875L11.875 1.625L4 9.5V14H14V9.5L10.4 5.9L13.275 3.05L17.125 6.875C17.4083 7.15833 17.625 7.48333 17.775 7.85C17.925 8.21667 18 8.6 18 9V15C18 15.8333 17.7083 16.5417 17.125 17.125C16.5417 17.7083 15.8333 18 15 18H3V18"
static let chat = "M0 18.5384V1.80768C0 1.30255 0.174998 0.874992 0.524995 0.524995..."
static let profile = "M7.49996 6.99993C6.53459 6.99993..."
static let search = "M16.6 18L10.3 11.7C9.8 12.1..."
static let back = "M3.825 9L9.425 14.6L8 16L0 8L8 0L9.425 1.4L3.825 7H16V9H3.825V9"
static let edit = "M0 18V13.75L13.2 0.575C13.4 0.391667..."
static let send = "M8.25 15L5.875 9.125L0 6.75V5.58333L15 0L9.41667 15H8.25V15"
static let mic = "M7 12C6.16667 12 5.45833 11.7083..."
static let waveform = "M2.33333 9.33333V2.33333H3.5V9.33333..."
static let delivered = "M4.2375 9.01875L0 4.78125L1.06875 3.73125L4.25625 6.91875V6.91875L5.30625 7.96875L4.2375 9.01875V9.01875M8.475 9.01875L4.2375 4.78125L5.2875 3.7125L8.475 6.9L15.375 0L16.425 1.06875L8.475 9.01875V9.01875M8.475 4.78125L7.40625 3.73125L11.1187 0.01875L12.1875 1.06875L8.475 4.78125V4.78125"
static let sent = "M4.275 9.01875L0 4.74375L1.06875 3.675L4.275 6.88125L11.1562 0L12.225 1.06875L4.275 9.01875V9.01875"
static let pinArrow = "M5.83333 5.25L7 6.41667V7.58333H4.08333V11.0833L3.5 11.6667L2.91667 11.0833V7.58333H0V6.41667L1.16667 5.25V1.16667H0.583333V0H6.41667V1.16667H5.83333V5.25V5.25"
static let image = "M1.16667 10.5C0.845833 10.5..."
static let location = "M4.66667 5.83333C4.9875 5.83333..."
static let download = "M8 12L3 7L4.4 5.55L7 8.15V0H9V8.15L11.6 5.55L13 7L8 12V12M2 16..."
static let info = "M9 15H11V9H9V15V15M10 7C10.2833 7..."
static let emoji = "M8.33333 12.9167C9.27778 12.9167..."
static let lock = "M1.5064 16.2499C1.0908 16.2499..."
static let shield = "M8 20C5.68333 19.4167 3.77083 18.0875..."
static let help = "M7.90747 12.5961C8.14542 12.5961..."
static let group = "M0 15V13.0312C0 12.1354 0.458333 11.4062 1.375 10.8438..."
static let tag = "M4.08333 9.33333C3.89861 9.33333..."
}
// MARK: - 9. 图片资源(Image Asset Hashes)
enum AppAsset {
static let homeBg = "figma:asset/e8e66ffa39e264e52de9245842960b9a233ebd17.png"
static let avatarElena = "figma:asset/dc8821b476fe947423e5fb2cd62046f4e818f9fd.png"
static let avatarJulian = "figma:asset/6cc0c4fbb4ce2040af20fa06bac879790c8aa074.png"
static let avatarSarah = "figma:asset/13cae4271b313d5b448f0cdbeea04bf6357f0a7b.png"
static let avatarAlex = "figma:asset/d97369160aa38d6e3b068558878857312c966936.png"
static let avatarMaya = "figma:asset/2a5cfc7e5bd5ce17e626f498c069acaa5b650eb8.png"
static let userProfile = "figma:asset/dd8b05fc008fff28e36294461564a87295ec1ccc.png"
static let chatAvatar1 = "figma:asset/489f26146d5d74a9ad1f85f5041dc117130ca2de.png"
static let chatAvatar2 = "figma:asset/ded57fbcdfb2f9b801c284753caa83707f0d2d03.png"
static let chatAvatar3 = "figma:asset/2cc61fdaeccf636e4e3a4fcbb15f56e96a07bf47.png"
static let chatAvatar4 = "figma:asset/75f4bcf89c4678d6c8283e458894557b8d1e0a5e.png"
static let profileAvatar = "figma:asset/0fb0e93a45b9e0f0c701cd2cfc14ce9cf42190cd.png"
}
// MARK: - 10. 组件模式(Component Patterns)
struct BubbleSpec {
// 接收方(incoming)
static let incomingPaddingTop: CGFloat = 10.75
static let incomingPaddingBottom: CGFloat = 12.5
static let incomingPaddingLeft: CGFloat = 16
static let incomingPaddingRight: CGFloat = 23
// 注意:SwiftUI 原生在 iOS 16+ 支持 .clipShape(UnevenRoundedRectangle(...))
// 此处保留你的 Token 定义思路
static let incomingCorners = [16, 16, 16, 0] // tl, tr, br, bl
static let incomingMaxWidth: CGFloat = 331.5
// 发送方(outgoing)
static let outgoingPaddingTop: CGFloat = 10.75
static let outgoingPaddingBottom: CGFloat = 12.5
static let outgoingPaddingLeft: CGFloat = 16
static let outgoingPaddingRight: CGFloat = 75.25
static let outgoingCorners = [16, 16, 0, 16] // tl, tr, br, bl
static let outgoingBackground = Color(hex: "#C6C6C7")
static let outgoingTextColor = Color(hex: "#3F4041")
static let outgoingFont = Font.custom("Inter-Medium", size: 14)
}
struct ChatItemSpec {
static let height: CGFloat = 88
static let avatarSize: CGFloat = 56
static let avatarRadius: CGFloat = 16
static let innerPadding: CGFloat = 16
static let itemGap: CGFloat = 16
static let textGap: CGFloat = 2
static let pinnedBg = Color(hex: "#131313")
static let normalBg = Color(hex: "#000000")
}
// 玻璃球背景渐变
let orbMainGradient = AngularGradient(
gradient: Gradient(stops: [
.init(color: Color.white.opacity(0.05), location: 0),
.init(color: Color.white.opacity(0.00), location: 0.5),
.init(color: Color.white.opacity(0.02), location: 1)
]),
center: .center,
angle: .degrees(134.872)
)