release(ios): 发布 0.2.1 并重构图标与界面细节
- 应用图标改用与 Web favicon 同源的扁平标记,金色 SIM 卡体加三格递增信号,29pt 下仍可读 - 标签统一为胶囊形状,提高深色主题下的描边权重,避免标签在暗色下消失 - 消息卡片与详情分区卡片收紧行距,单屏信息密度明显提升 - 详情头部指标精简为 4 行,数据模块改为单条读数 - 批量选择勾选框移出名称区域,工具栏控件统一为等高 34pt - 分组编辑器改为点选即生效,移除说明文字与常驻禁用按钮
This commit is contained in:
@@ -89,15 +89,9 @@ public struct AIButtonStyle: ButtonStyle {
|
||||
.frame(maxWidth: fillsWidth ? .infinity : nil)
|
||||
.frame(width: fixedWidth)
|
||||
.background(
|
||||
kind == .primary && isEnabled && size != .toolbarInline
|
||||
? AnyShapeStyle(
|
||||
LinearGradient(
|
||||
colors: [AITheme.primaryTop, AITheme.primaryBottom],
|
||||
startPoint: .top,
|
||||
endPoint: .bottom
|
||||
)
|
||||
)
|
||||
: AnyShapeStyle(kind.backgroundColor(isEnabled: isEnabled))
|
||||
// 主色面用纯色。之前的 primaryTop/primaryBottom 竖向渐变在系统里会被合成成灰紫,
|
||||
// 实测 #3B82F6 掉到 #6B81CA,整片按钮发脏。
|
||||
AnyShapeStyle(kind.backgroundColor(isEnabled: isEnabled))
|
||||
)
|
||||
.shadow(
|
||||
color: (kind == .secondary || !isEnabled) ? .clear : .black.opacity(0.10),
|
||||
@@ -107,13 +101,13 @@ public struct AIButtonStyle: ButtonStyle {
|
||||
)
|
||||
.clipShape(
|
||||
RoundedRectangle(
|
||||
cornerRadius: size == .toolbarInline ? AITheme.toolbarButtonHeight / 2 : AITheme.buttonRadius,
|
||||
cornerRadius: cornerRadius,
|
||||
style: .continuous
|
||||
)
|
||||
)
|
||||
.overlay {
|
||||
RoundedRectangle(
|
||||
cornerRadius: size == .toolbarInline ? AITheme.toolbarButtonHeight / 2 : AITheme.buttonRadius,
|
||||
cornerRadius: cornerRadius,
|
||||
style: .continuous
|
||||
)
|
||||
.strokeBorder(
|
||||
@@ -126,6 +120,15 @@ public struct AIButtonStyle: ButtonStyle {
|
||||
.opacity(configuration.isPressed ? 0.82 : 1)
|
||||
}
|
||||
|
||||
/// 工具栏里的图标按钮需要更小的圆角,否则 34pt 方块看起来像被切过。
|
||||
private var cornerRadius: CGFloat {
|
||||
switch size {
|
||||
case .toolbarInline: return AITheme.toolbarButtonHeight / 2
|
||||
case .icon: return AITheme.iconButtonRadius
|
||||
default: return AITheme.buttonRadius
|
||||
}
|
||||
}
|
||||
|
||||
private var minimumWidth: CGFloat {
|
||||
switch size {
|
||||
case .compact: return AITheme.compactButtonMinWidth
|
||||
|
||||
@@ -25,7 +25,7 @@ public struct AICard<Content: View>: View {
|
||||
)
|
||||
)
|
||||
.clipShape(RoundedRectangle(cornerRadius: AITheme.cardRadius, style: .continuous))
|
||||
.shadow(color: .black.opacity(0.07), radius: 18, x: 0, y: 8)
|
||||
.shadow(color: AITheme.cardShadow, radius: 14, x: 0, y: 6)
|
||||
.overlay {
|
||||
RoundedRectangle(cornerRadius: AITheme.cardRadius, style: .continuous)
|
||||
.strokeBorder(AITheme.line)
|
||||
@@ -61,12 +61,14 @@ public struct AITag: View {
|
||||
.lineLimit(1)
|
||||
.truncationMode(.middle)
|
||||
.padding(.horizontal, 10)
|
||||
.frame(minHeight: 30)
|
||||
.background(tint.opacity(0.10))
|
||||
.clipShape(RoundedRectangle(cornerRadius: 10, style: .continuous))
|
||||
// 与节点卡片上的标签胶囊同一形状,同一高度;深色底上 10% 色雾几乎看不见,
|
||||
// 所以描边权重必须够,不能让标签在暗色主题里消失。
|
||||
.frame(height: 28)
|
||||
.background(tint.opacity(0.14))
|
||||
.clipShape(Capsule(style: .continuous))
|
||||
.overlay {
|
||||
RoundedRectangle(cornerRadius: 10, style: .continuous)
|
||||
.strokeBorder(tint.opacity(0.16))
|
||||
Capsule(style: .continuous)
|
||||
.strokeBorder(tint.opacity(0.30))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -127,7 +129,6 @@ public struct AISectionTitle: View {
|
||||
.accessibilityHidden(true)
|
||||
Text(title)
|
||||
.font(.headline.weight(.semibold))
|
||||
.kerning(-0.1)
|
||||
.foregroundStyle(AITheme.ink)
|
||||
.lineLimit(1)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
@@ -158,6 +159,41 @@ public struct AIFieldLabel: View {
|
||||
}
|
||||
}
|
||||
|
||||
extension View {
|
||||
/// 卡片内的二级面板:极浅底色 + 更浅的描边,只做"分组",不抢主信息的对比度。
|
||||
func aiInsetSurface(cornerRadius: CGFloat = 12) -> some View {
|
||||
self
|
||||
.background(AITheme.tile)
|
||||
.clipShape(RoundedRectangle(cornerRadius: cornerRadius, style: .continuous))
|
||||
.overlay {
|
||||
RoundedRectangle(cornerRadius: cornerRadius, style: .continuous)
|
||||
.strokeBorder(AITheme.tileLine)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 带语义色的内嵌指标块:底色/描边都由 tint 决定,浅色下是一层很淡的色洗。
|
||||
struct AITintTile<Content: View>: View {
|
||||
private let tint: Color
|
||||
private let content: Content
|
||||
|
||||
init(tint: Color, @ViewBuilder content: () -> Content) {
|
||||
self.tint = tint
|
||||
self.content = content()
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
content
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.background(tint.opacity(0.09))
|
||||
.clipShape(RoundedRectangle(cornerRadius: AITheme.tileRadius, style: .continuous))
|
||||
.overlay {
|
||||
RoundedRectangle(cornerRadius: AITheme.tileRadius, style: .continuous)
|
||||
.strokeBorder(tint.opacity(0.20))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public struct AIStatCard: View {
|
||||
private let title: String
|
||||
private let value: String
|
||||
@@ -180,33 +216,36 @@ public struct AIStatCard: View {
|
||||
}
|
||||
|
||||
public var body: some View {
|
||||
AICard(padding: 12) {
|
||||
HStack(alignment: .center, spacing: 10) {
|
||||
if let systemImage {
|
||||
Image(systemName: systemImage)
|
||||
.font(.system(size: 14, weight: .semibold))
|
||||
.foregroundStyle(tint)
|
||||
.frame(width: 32, height: 32)
|
||||
.background(tint.opacity(0.10))
|
||||
.clipShape(RoundedRectangle(cornerRadius: 10, style: .continuous))
|
||||
.accessibilityHidden(true)
|
||||
}
|
||||
VStack(alignment: .leading, spacing: 2) {
|
||||
Text(title)
|
||||
.font(.caption2.weight(.semibold))
|
||||
.lineLimit(1)
|
||||
.minimumScaleFactor(0.85)
|
||||
.foregroundStyle(AITheme.muted)
|
||||
Text(value)
|
||||
.font(.headline.weight(.semibold))
|
||||
.foregroundStyle(value == "0" || value.hasPrefix("0/") ? AITheme.muted : tint)
|
||||
.lineLimit(1)
|
||||
.minimumScaleFactor(0.75)
|
||||
}
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
// 卡内不再叠一张卡:双层阴影 + 双层描边是浅色发灰的主因。
|
||||
HStack(alignment: .center, spacing: 10) {
|
||||
if let systemImage {
|
||||
Image(systemName: systemImage)
|
||||
.font(.system(size: 13, weight: .semibold))
|
||||
.foregroundStyle(tint)
|
||||
.frame(width: 30, height: 30)
|
||||
.background(tint.opacity(0.12))
|
||||
.clipShape(RoundedRectangle(cornerRadius: 9, style: .continuous))
|
||||
.accessibilityHidden(true)
|
||||
}
|
||||
.accessibilityElement(children: .combine)
|
||||
VStack(alignment: .leading, spacing: 2) {
|
||||
Text(title)
|
||||
.font(.caption2.weight(.semibold))
|
||||
.lineLimit(1)
|
||||
.minimumScaleFactor(0.85)
|
||||
.foregroundStyle(AITheme.muted)
|
||||
Text(value)
|
||||
.font(.headline.weight(.semibold))
|
||||
.monospacedDigit()
|
||||
.foregroundStyle(value == "0" || value.hasPrefix("0/") ? AITheme.muted : tint)
|
||||
.lineLimit(1)
|
||||
.minimumScaleFactor(0.75)
|
||||
}
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
}
|
||||
.padding(12)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.aiInsetSurface(cornerRadius: AITheme.tileRadius)
|
||||
.accessibilityElement(children: .combine)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -247,11 +286,34 @@ public struct AIEmptyState: View {
|
||||
private let systemImage: String
|
||||
private let title: String
|
||||
private let message: String
|
||||
private let actionTitle: String?
|
||||
private let actionSystemImage: String
|
||||
private let action: (() -> Void)?
|
||||
|
||||
public init(systemImage: String, title: String, message: String) {
|
||||
self.systemImage = systemImage
|
||||
self.title = title
|
||||
self.message = message
|
||||
self.actionTitle = nil
|
||||
self.actionSystemImage = "arrow.clockwise"
|
||||
self.action = nil
|
||||
}
|
||||
|
||||
/// 失败/空态带一个就地重试入口,避免"红色横幅 + 暂无数据"互相矛盾。
|
||||
public init(
|
||||
systemImage: String,
|
||||
title: String,
|
||||
message: String,
|
||||
actionTitle: String,
|
||||
actionSystemImage: String = "arrow.clockwise",
|
||||
action: @escaping () -> Void
|
||||
) {
|
||||
self.systemImage = systemImage
|
||||
self.title = title
|
||||
self.message = message
|
||||
self.actionTitle = actionTitle
|
||||
self.actionSystemImage = actionSystemImage
|
||||
self.action = action
|
||||
}
|
||||
|
||||
public var body: some View {
|
||||
@@ -271,6 +333,13 @@ public struct AIEmptyState: View {
|
||||
.foregroundStyle(AITheme.muted)
|
||||
.multilineTextAlignment(.center)
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
if let actionTitle, let action {
|
||||
Button(action: action) {
|
||||
Label(actionTitle, systemImage: actionSystemImage)
|
||||
}
|
||||
.buttonStyle(AIButtonStyle(kind: .secondary, size: .compact))
|
||||
.padding(.top, 4)
|
||||
}
|
||||
}
|
||||
.frame(maxWidth: .infinity)
|
||||
.padding(.vertical, 16)
|
||||
@@ -375,7 +444,7 @@ public struct AILoadingCard: View {
|
||||
)
|
||||
)
|
||||
.clipShape(RoundedRectangle(cornerRadius: AITheme.cardRadius, style: .continuous))
|
||||
.shadow(color: .black.opacity(0.07), radius: 18, x: 0, y: 8)
|
||||
.shadow(color: AITheme.brand.opacity(0.08), radius: 20, x: 0, y: 10)
|
||||
.overlay {
|
||||
RoundedRectangle(cornerRadius: AITheme.cardRadius, style: .continuous)
|
||||
.strokeBorder(AITheme.line)
|
||||
@@ -460,11 +529,13 @@ public struct AIKeyValueRow: View {
|
||||
private let title: String
|
||||
private let value: String
|
||||
private let valueTint: Color
|
||||
private let monospaced: Bool
|
||||
|
||||
public init(_ title: String, value: String, tint: Color = AITheme.ink) {
|
||||
public init(_ title: String, value: String, tint: Color = AITheme.ink, monospaced: Bool = false) {
|
||||
self.title = title
|
||||
self.value = value
|
||||
self.valueTint = tint
|
||||
self.monospaced = monospaced
|
||||
}
|
||||
|
||||
public var body: some View {
|
||||
@@ -476,7 +547,12 @@ public struct AIKeyValueRow: View {
|
||||
.layoutPriority(1)
|
||||
Spacer(minLength: 12)
|
||||
Text(value)
|
||||
.font(.subheadline.weight(.medium))
|
||||
.font(
|
||||
monospaced
|
||||
? Font.system(.subheadline, design: .monospaced, weight: .medium)
|
||||
: Font.subheadline.weight(.medium)
|
||||
)
|
||||
.monospacedDigit()
|
||||
.foregroundStyle(valueTint)
|
||||
.multilineTextAlignment(.trailing)
|
||||
.lineLimit(2)
|
||||
@@ -505,7 +581,7 @@ public struct AISegmentedControl<Value: Hashable>: View {
|
||||
}
|
||||
|
||||
public var body: some View {
|
||||
HStack(spacing: 4) {
|
||||
HStack(spacing: 3) {
|
||||
ForEach(options, id: \.value) { option in
|
||||
let isSelected = selection == option.value
|
||||
|
||||
@@ -513,22 +589,16 @@ public struct AISegmentedControl<Value: Hashable>: View {
|
||||
selection = option.value
|
||||
} label: {
|
||||
Text(option.title)
|
||||
.font(.body.weight(isSelected ? .semibold : .medium))
|
||||
.font(.subheadline.weight(isSelected ? .semibold : .medium))
|
||||
.lineLimit(1)
|
||||
.minimumScaleFactor(0.75)
|
||||
.foregroundStyle(isSelected ? AITheme.onPrimary : AITheme.muted)
|
||||
.foregroundStyle(isSelected ? AITheme.ink : AITheme.muted)
|
||||
.frame(maxWidth: .infinity, minHeight: AITheme.segmentedControlHeight)
|
||||
// 分段控件是"视图切换"而不是主操作:抬起的中性块,避免和标签筛选抢注意力。
|
||||
.background {
|
||||
if isSelected {
|
||||
RoundedRectangle(cornerRadius: 12, style: .continuous)
|
||||
.fill(
|
||||
LinearGradient(
|
||||
colors: [AITheme.primaryTop, AITheme.primaryBottom],
|
||||
startPoint: .top,
|
||||
endPoint: .bottom
|
||||
)
|
||||
)
|
||||
}
|
||||
RoundedRectangle(cornerRadius: 11, style: .continuous)
|
||||
.fill(isSelected ? AITheme.segmentSelected : Color.clear)
|
||||
.shadow(color: isSelected ? AITheme.cardShadow : .clear, radius: 3, x: 0, y: 1)
|
||||
}
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
@@ -537,12 +607,12 @@ public struct AISegmentedControl<Value: Hashable>: View {
|
||||
.accessibilityAddTraits(isSelected ? .isSelected : [])
|
||||
}
|
||||
}
|
||||
.padding(4)
|
||||
.background(AITheme.control)
|
||||
.clipShape(RoundedRectangle(cornerRadius: 16, style: .continuous))
|
||||
.padding(3)
|
||||
.background(AITheme.segmentTrack)
|
||||
.clipShape(RoundedRectangle(cornerRadius: 14, style: .continuous))
|
||||
.overlay {
|
||||
RoundedRectangle(cornerRadius: 16, style: .continuous)
|
||||
.strokeBorder(AITheme.line)
|
||||
RoundedRectangle(cornerRadius: 14, style: .continuous)
|
||||
.strokeBorder(AITheme.tileLine)
|
||||
}
|
||||
.animation(.easeOut(duration: 0.16), value: selection)
|
||||
.accessibilityElement(children: .contain)
|
||||
@@ -567,15 +637,18 @@ public struct AISearchField: View {
|
||||
private let placeholder: String
|
||||
@Binding private var text: String
|
||||
private var onSubmit: (() -> Void)?
|
||||
private let minHeight: CGFloat
|
||||
@FocusState private var isFocused: Bool
|
||||
|
||||
public init(
|
||||
_ placeholder: String,
|
||||
text: Binding<String>,
|
||||
minHeight: CGFloat = AITheme.buttonHeight,
|
||||
onSubmit: (() -> Void)? = nil
|
||||
) {
|
||||
self.placeholder = placeholder
|
||||
self._text = text
|
||||
self.minHeight = minHeight
|
||||
self.onSubmit = onSubmit
|
||||
}
|
||||
|
||||
@@ -607,7 +680,7 @@ public struct AISearchField: View {
|
||||
.accessibilityLabel("清除搜索")
|
||||
}
|
||||
}
|
||||
.aiInputSurface(minHeight: AITheme.buttonHeight, isFocused: isFocused)
|
||||
.aiInputSurface(minHeight: minHeight, isFocused: isFocused)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -651,14 +724,14 @@ extension View {
|
||||
}
|
||||
}
|
||||
|
||||
func aiSelectSurface() -> some View {
|
||||
func aiSelectSurface(compact: Bool = false) -> some View {
|
||||
self
|
||||
.tint(AITheme.ink)
|
||||
.font(.subheadline.weight(.semibold))
|
||||
.font(compact ? .footnote.weight(.semibold) : .subheadline.weight(.semibold))
|
||||
.foregroundStyle(AITheme.ink)
|
||||
.labelsHidden()
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.aiInputSurface()
|
||||
.aiInputSurface(minHeight: compact ? AITheme.compactControlHeight : AITheme.inputHeight)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -727,3 +800,45 @@ public struct AIIconButton: View {
|
||||
.accessibilityLabel(title)
|
||||
}
|
||||
}
|
||||
|
||||
/// 列表行内的轻量图标动作:同色淡底小方块,触区仍补足 44pt。
|
||||
/// 行内操作用实心药丸会把一行读成"按钮墙",这里让颜色只表达危险程度。
|
||||
public struct AISymbolAction: View {
|
||||
private let title: String
|
||||
private let systemImage: String
|
||||
private let tint: Color
|
||||
private let action: () -> Void
|
||||
|
||||
public init(
|
||||
_ title: String,
|
||||
systemImage: String,
|
||||
tint: Color = AITheme.muted,
|
||||
action: @escaping () -> Void
|
||||
) {
|
||||
self.title = title
|
||||
self.systemImage = systemImage
|
||||
self.tint = tint
|
||||
self.action = action
|
||||
}
|
||||
|
||||
public var body: some View {
|
||||
Button {
|
||||
action()
|
||||
} label: {
|
||||
Image(systemName: systemImage)
|
||||
.font(.system(size: 14, weight: .semibold))
|
||||
.foregroundStyle(tint)
|
||||
.frame(width: AITheme.iconButtonSize, height: AITheme.iconButtonSize)
|
||||
.background(tint.opacity(0.10))
|
||||
.clipShape(RoundedRectangle(cornerRadius: AITheme.iconButtonRadius, style: .continuous))
|
||||
.overlay {
|
||||
RoundedRectangle(cornerRadius: AITheme.iconButtonRadius, style: .continuous)
|
||||
.strokeBorder(tint.opacity(0.18))
|
||||
}
|
||||
.frame(width: 44, height: 44)
|
||||
.contentShape(Rectangle())
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
.accessibilityLabel(title)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,41 +28,50 @@ private enum AIPalette {
|
||||
dynamicColor(light: light, dark: dark)
|
||||
}
|
||||
|
||||
static let canvas = color(light: rgba(245, 246, 248, 1), dark: rgba(6, 7, 9, 1))
|
||||
static let surface = color(light: rgba(255, 255, 255, 1), dark: rgba(18, 20, 24, 1))
|
||||
static let surfaceRaised = color(light: rgba(251, 252, 253, 1), dark: rgba(27, 30, 35, 1))
|
||||
static let control = color(light: rgba(243, 245, 247, 1), dark: rgba(26, 29, 34, 1))
|
||||
static let chrome = color(light: rgba(255, 255, 255, 0.82), dark: rgba(11, 12, 15, 0.82))
|
||||
static let ink = color(light: rgba(15, 18, 22, 1), dark: rgba(243, 245, 247, 1))
|
||||
static let muted = color(light: rgba(98, 107, 119, 1), dark: rgba(151, 160, 172, 1))
|
||||
static let line = color(light: rgba(227, 230, 234, 1), dark: rgba(255, 255, 255, 0.09))
|
||||
static let brand = color(light: rgba(13, 15, 18, 1), dark: rgba(8, 9, 11, 1))
|
||||
static let primary = color(light: rgba(17, 20, 25, 1), dark: rgba(220, 178, 111, 1))
|
||||
static let primaryTop = color(light: rgba(43, 49, 58, 1), dark: rgba(240, 209, 154, 1))
|
||||
static let primaryBottom = color(light: rgba(15, 18, 22, 1), dark: rgba(198, 154, 92, 1))
|
||||
static let onPrimary = color(light: rgba(255, 255, 255, 1), dark: rgba(12, 14, 17, 1))
|
||||
static let danger = color(light: rgba(190, 65, 57, 1), dark: rgba(242, 124, 113, 1))
|
||||
static let onDanger = color(light: rgba(255, 255, 255, 1), dark: rgba(22, 9, 8, 1))
|
||||
static let warning = color(light: rgba(170, 111, 23, 1), dark: rgba(226, 166, 65, 1))
|
||||
static let success = color(light: rgba(50, 150, 101, 1), dark: rgba(103, 198, 148, 1))
|
||||
// 浅色:中性冷灰底 + 纯白卡 + 极浅内嵌面,避免"灰蓝蒙层"把层级糊成一团。
|
||||
static let canvas = color(light: rgba(242, 244, 248, 1), dark: rgba(7, 10, 16, 1))
|
||||
static let surface = color(light: rgba(255, 255, 255, 1), dark: rgba(20, 26, 36, 1))
|
||||
static let surfaceRaised = color(light: rgba(255, 255, 255, 1), dark: rgba(26, 35, 49, 1))
|
||||
static let control = color(light: rgba(237, 241, 247, 1), dark: rgba(34, 44, 60, 1))
|
||||
static let tile = color(light: rgba(247, 249, 252, 1), dark: rgba(255, 255, 255, 0.05))
|
||||
static let tileLine = color(light: rgba(233, 239, 247, 1), dark: rgba(255, 255, 255, 0.08))
|
||||
static let chrome = color(light: rgba(255, 255, 255, 0.90), dark: rgba(12, 17, 25, 0.86))
|
||||
static let ink = color(light: rgba(15, 23, 41, 1), dark: rgba(241, 245, 249, 1))
|
||||
static let muted = color(light: rgba(92, 106, 128, 1), dark: rgba(154, 167, 184, 1))
|
||||
static let faint = color(light: rgba(141, 152, 170, 1), dark: rgba(112, 127, 148, 1))
|
||||
static let line = color(light: rgba(224, 231, 240, 1), dark: rgba(255, 255, 255, 0.10))
|
||||
static let brand = color(light: rgba(11, 27, 51, 1), dark: rgba(7, 10, 16, 1))
|
||||
static let primary = color(light: rgba(18, 96, 214, 1), dark: rgba(96, 165, 250, 1))
|
||||
static let onPrimary = color(light: rgba(255, 255, 255, 1), dark: rgba(5, 22, 43, 1))
|
||||
static let danger = color(light: rgba(199, 44, 44, 1), dark: rgba(248, 113, 113, 1))
|
||||
static let onDanger = color(light: rgba(255, 255, 255, 1), dark: rgba(24, 7, 7, 1))
|
||||
static let warning = color(light: rgba(176, 84, 12, 1), dark: rgba(245, 158, 11, 1))
|
||||
static let success = color(light: rgba(16, 124, 90, 1), dark: rgba(74, 222, 128, 1))
|
||||
static let segmentTrack = color(light: rgba(236, 240, 246, 1), dark: rgba(12, 17, 25, 1))
|
||||
static let segmentSelected = color(light: rgba(255, 255, 255, 1), dark: rgba(48, 61, 81, 1))
|
||||
static let cardShadow = color(light: rgba(15, 30, 60, 0.07), dark: rgba(0, 0, 0, 0.55))
|
||||
#elseif canImport(AppKit)
|
||||
static let canvas = Color(red: 0.953, green: 0.961, blue: 0.973)
|
||||
static let canvas = Color(red: 0.914, green: 0.933, blue: 0.961)
|
||||
static let surface = Color.white
|
||||
static let control = Color(red: 0.969, green: 0.976, blue: 0.984)
|
||||
static let ink = Color(red: 0.110, green: 0.153, blue: 0.220)
|
||||
static let muted = Color(red: 0.435, green: 0.486, blue: 0.561)
|
||||
static let line = Color(red: 0.878, green: 0.898, blue: 0.922)
|
||||
static let brand = Color(red: 0.075, green: 0.118, blue: 0.188)
|
||||
static let surfaceRaised = Color(red: 0.984, green: 0.988, blue: 0.992)
|
||||
static let chrome = Color.white.opacity(0.82)
|
||||
static let control = Color(red: 0.941, green: 0.957, blue: 0.980)
|
||||
static let ink = Color(red: 0.035, green: 0.086, blue: 0.169)
|
||||
static let muted = Color(red: 0.275, green: 0.341, blue: 0.435)
|
||||
static let line = Color(red: 0.827, green: 0.867, blue: 0.918)
|
||||
static let brand = Color(red: 0.035, green: 0.086, blue: 0.169)
|
||||
static let surfaceRaised = Color.white
|
||||
static let chrome = Color.white.opacity(0.86)
|
||||
static let onPrimary = Color.white
|
||||
static let onDanger = Color.white
|
||||
static let primary = Color(red: 0.067, green: 0.078, blue: 0.098)
|
||||
static let primaryTop = Color(red: 0.17, green: 0.19, blue: 0.23)
|
||||
static let primaryBottom = Color(red: 0.06, green: 0.07, blue: 0.09)
|
||||
static let danger = Color(red: 0.745, green: 0.255, blue: 0.224)
|
||||
static let warning = Color(red: 0.667, green: 0.435, blue: 0.09)
|
||||
static let success = Color(red: 0.196, green: 0.588, blue: 0.396)
|
||||
static let primary = Color(red: 0.145, green: 0.388, blue: 0.922)
|
||||
static let danger = Color(red: 0.784, green: 0.118, blue: 0.118)
|
||||
static let warning = Color(red: 0.706, green: 0.325, blue: 0.035)
|
||||
static let success = Color(red: 0.071, green: 0.502, blue: 0.361)
|
||||
static let tile = Color(red: 0.969, green: 0.976, blue: 0.988)
|
||||
static let tileLine = Color(red: 0.914, green: 0.937, blue: 0.969)
|
||||
static let faint = Color(red: 0.553, green: 0.596, blue: 0.667)
|
||||
static let segmentTrack = Color(red: 0.925, green: 0.941, blue: 0.965)
|
||||
static let segmentSelected = Color.white
|
||||
static let cardShadow = Color(red: 0.059, green: 0.118, blue: 0.235).opacity(0.07)
|
||||
#else
|
||||
private static func color(_ red: CGFloat, _ green: CGFloat, _ blue: CGFloat) -> Color {
|
||||
Color(red: red / 255, green: green / 255, blue: blue / 255)
|
||||
@@ -80,11 +89,15 @@ private enum AIPalette {
|
||||
static let onPrimary = color(255, 255, 255)
|
||||
static let onDanger = color(255, 255, 255)
|
||||
static let primary = color(17, 20, 25)
|
||||
static let primaryTop = color(43, 49, 58)
|
||||
static let primaryBottom = color(15, 18, 22)
|
||||
static let danger = color(190, 65, 57)
|
||||
static let warning = color(170, 111, 23)
|
||||
static let success = color(50, 150, 101)
|
||||
static let tile = color(247, 249, 252)
|
||||
static let tileLine = color(233, 239, 247)
|
||||
static let faint = color(141, 152, 170)
|
||||
static let segmentTrack = color(236, 240, 246)
|
||||
static let segmentSelected = color(255, 255, 255)
|
||||
static let cardShadow = color(15, 30, 60)
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -93,30 +106,39 @@ public enum AITheme {
|
||||
public static let surface = AIPalette.surface
|
||||
public static let surfaceRaised = AIPalette.surfaceRaised
|
||||
public static let control = AIPalette.control
|
||||
public static let tile = AIPalette.tile
|
||||
public static let tileLine = AIPalette.tileLine
|
||||
public static let chrome = AIPalette.chrome
|
||||
public static let ink = AIPalette.ink
|
||||
public static let muted = AIPalette.muted
|
||||
public static let faint = AIPalette.faint
|
||||
public static let line = AIPalette.line
|
||||
public static let brand = AIPalette.brand
|
||||
public static let primary = AIPalette.primary
|
||||
public static let primaryTop = AIPalette.primaryTop
|
||||
public static let primaryBottom = AIPalette.primaryBottom
|
||||
public static let onPrimary = AIPalette.onPrimary
|
||||
public static let danger = AIPalette.danger
|
||||
public static let onDanger = AIPalette.onDanger
|
||||
public static let warning = AIPalette.warning
|
||||
public static let success = AIPalette.success
|
||||
public static let segmentTrack = AIPalette.segmentTrack
|
||||
public static let segmentSelected = AIPalette.segmentSelected
|
||||
public static let cardShadow = AIPalette.cardShadow
|
||||
|
||||
public static let buttonHeight: CGFloat = 48
|
||||
public static let compactButtonHeight: CGFloat = 44
|
||||
public static let largeButtonHeight: CGFloat = 56
|
||||
public static let toolbarButtonHeight: CGFloat = 44
|
||||
public static let iconButtonSize: CGFloat = 44
|
||||
// 导航栏只给约 38pt 高度,44pt 的方块会被上下裁切,所以工具栏图标按钮单独取 34。
|
||||
public static let iconButtonSize: CGFloat = 34
|
||||
public static let iconButtonRadius: CGFloat = 11
|
||||
public static let buttonRadius: CGFloat = 16
|
||||
public static let cardRadius: CGFloat = 22
|
||||
public static let inputRadius: CGFloat = 16
|
||||
public static let inputHeight: CGFloat = 52
|
||||
public static let segmentedControlHeight: CGFloat = 44
|
||||
public static let tileRadius: CGFloat = 14
|
||||
public static let chipHeight: CGFloat = 34
|
||||
public static let compactControlHeight: CGFloat = 44
|
||||
public static let compactButtonMinWidth: CGFloat = 72
|
||||
public static let regularButtonMinWidth: CGFloat = 88
|
||||
public static let toolbarButtonMinWidth: CGFloat = 96
|
||||
|
||||
Reference in New Issue
Block a user