release(ios): 0.2.2 构建 9 修复消息发送遮挡并中性化石墨灰阶

- 消息中心发送入口从被标签栏盖住的底部操作条移到导航栏右侧,发信恢复可用
- 停用态主按钮保留主色淡底与同色描边,与次级按钮拉开层级
- 消息行删除改安静线性图标,去掉一屏红块
- 缺数据读数统一为短横,卡片延迟补空格单位
- 中性灰阶改为严格 R=G=B,浅色主题不再读出冷调;图标石墨底同步去蓝
- 详情页延迟量表改用与卡片同一算法,身份信息值改为右对齐
- 标签加号按钮与输入框同高同圆角
This commit is contained in:
chick
2026-09-15 08:19:18 +08:00
parent 9ad0c46088
commit c3d17b3daf
21 changed files with 203 additions and 125 deletions
@@ -21,10 +21,11 @@ public struct AIButtonStyle: ButtonStyle {
}
func backgroundColor(isEnabled: Bool) -> Color {
switch self {
case .primary, .secondary, .danger:
return isEnabled ? kindBackground : AITheme.control
}
if isEnabled { return kindBackground }
// 禁用态:实心主色按钮保留主色淡版,其余退回中性灰。
return self == .primary
? AITheme.primary.opacity(AITheme.disabledFillOpacity)
: AITheme.control
}
private var kindBackground: Color {
@@ -34,6 +35,13 @@ public struct AIButtonStyle: ButtonStyle {
case .danger: return AITheme.danger
}
}
/// 禁用态下主按钮必须还能看出"这是主行动"。
/// 之前三种 kind 禁用后都塌成同一块 control 灰,表单里"取消"和"保存"长得一模一样,
/// 用户只能靠文字猜哪个是提交。这里让实心主色按钮改用同色相淡填充 + 描边,灰底次按钮不套。
var usesTintedDisabledTreatment: Bool {
self == .primary
}
}
public enum Size: Hashable {
@@ -74,6 +82,8 @@ public struct AIButtonStyle: ButtonStyle {
case .toolbarInline: AITheme.toolbarButtonHeight
}
let isTintedDisabled = kind.usesTintedDisabledTreatment && !isEnabled
configuration.label
.labelStyle(.titleAndIcon)
.font(
@@ -94,7 +104,7 @@ public struct AIButtonStyle: ButtonStyle {
AnyShapeStyle(kind.backgroundColor(isEnabled: isEnabled))
)
.shadow(
color: (kind == .secondary || !isEnabled) ? .clear : .black.opacity(0.10),
color: (kind == .secondary || isTintedDisabled) ? .clear : .black.opacity(0.10),
radius: 12,
x: 0,
y: 5
@@ -111,9 +121,11 @@ public struct AIButtonStyle: ButtonStyle {
style: .continuous
)
.strokeBorder(
size == .toolbarInline && kind == .primary
? AITheme.primary.opacity(0.28)
: (kind == .secondary || !isEnabled ? AITheme.line : .clear)
isTintedDisabled
? AITheme.primary.opacity(AITheme.disabledStrokeOpacity)
: size == .toolbarInline && kind == .primary
? AITheme.primary.opacity(0.28)
: (kind == .secondary || !isEnabled ? AITheme.line : .clear)
)
}
.scaleEffect(configuration.isPressed ? Self.pressedScale : 1)
@@ -820,17 +820,22 @@ public struct AISymbolAction: View {
private let title: String
private let systemImage: String
private let tint: Color
/// 安静样式:只留着色图标与点击区,不再铺色块。
/// 卡片列表里每行都放一块彩色方块会把整页染成"警告",删除这类低频动作更适合安静样式。
private let isQuiet: Bool
private let action: () -> Void
public init(
_ title: String,
systemImage: String,
tint: Color = AITheme.muted,
quiet: Bool = false,
action: @escaping () -> Void
) {
self.title = title
self.systemImage = systemImage
self.tint = tint
self.isQuiet = quiet
self.action = action
}
@@ -842,11 +847,13 @@ public struct AISymbolAction: View {
.font(.system(size: 14, weight: .semibold))
.foregroundStyle(tint)
.frame(width: AITheme.iconButtonSize, height: AITheme.iconButtonSize)
.background(tint.opacity(0.10))
.background(isQuiet ? Color.clear : tint.opacity(0.10))
.clipShape(RoundedRectangle(cornerRadius: AITheme.iconButtonRadius, style: .continuous))
.overlay {
RoundedRectangle(cornerRadius: AITheme.iconButtonRadius, style: .continuous)
.strokeBorder(tint.opacity(0.18))
if isQuiet == false {
RoundedRectangle(cornerRadius: AITheme.iconButtonRadius, style: .continuous)
.strokeBorder(tint.opacity(0.18))
}
}
.frame(width: 44, height: 44)
.contentShape(Rectangle())
@@ -28,76 +28,77 @@ private enum AIPalette {
dynamicColor(light: light, dark: dark)
}
// 石墨灰阶:所有中性色 R≈G≈B,不带蓝调;主色让位给黑/白,颜色只用来表达状态。
static let canvas = color(light: rgba(244, 245, 245, 1), dark: rgba(8, 9, 9, 1))
static let surface = color(light: rgba(255, 255, 255, 1), dark: rgba(19, 21, 21, 1))
static let surfaceRaised = color(light: rgba(255, 255, 255, 1), dark: rgba(27, 29, 30, 1))
static let control = color(light: rgba(238, 239, 239, 1), dark: rgba(36, 39, 40, 1))
static let tile = color(light: rgba(246, 247, 247, 1), dark: rgba(255, 255, 255, 0.055))
static let tileLine = color(light: rgba(231, 233, 233, 1), dark: rgba(255, 255, 255, 0.09))
static let chrome = color(light: rgba(252, 252, 252, 0.92), dark: rgba(11, 12, 12, 0.88))
static let ink = color(light: rgba(17, 19, 20, 1), dark: rgba(240, 242, 242, 1))
static let muted = color(light: rgba(88, 94, 97, 1), dark: rgba(158, 165, 168, 1))
static let faint = color(light: rgba(126, 132, 135, 1), dark: rgba(112, 118, 121, 1))
static let line = color(light: rgba(223, 226, 226, 1), dark: rgba(255, 255, 255, 0.11))
static let brand = color(light: rgba(18, 20, 21, 1), dark: rgba(8, 9, 9, 1))
static let primary = color(light: rgba(21, 24, 25, 1), dark: rgba(238, 241, 241, 1))
static let onPrimary = color(light: rgba(255, 255, 255, 1), dark: rgba(12, 14, 14, 1))
// 中性石墨灰阶:所有中性色严格 R=G=B,既不冷到读出蓝,也不暖到读出米色;主色让位给
// 黑/白,颜色只用来表达状态。原先 B 比 R 高 3~10 级,浅色下大面积文字与描边会蒙一层冷调。
static let canvas = color(light: rgba(245, 245, 245, 1), dark: rgba(10, 10, 10, 1))
static let surface = color(light: rgba(255, 255, 255, 1), dark: rgba(21, 21, 21, 1))
static let surfaceRaised = color(light: rgba(255, 255, 255, 1), dark: rgba(29, 29, 29, 1))
static let control = color(light: rgba(239, 239, 239, 1), dark: rgba(39, 39, 39, 1))
static let tile = color(light: rgba(247, 247, 247, 1), dark: rgba(255, 255, 255, 0.055))
static let tileLine = color(light: rgba(232, 232, 232, 1), dark: rgba(255, 255, 255, 0.09))
static let chrome = color(light: rgba(252, 252, 252, 0.92), dark: rgba(13, 13, 13, 0.88))
static let ink = color(light: rgba(18, 18, 18, 1), dark: rgba(242, 242, 242, 1))
static let muted = color(light: rgba(92, 92, 92, 1), dark: rgba(164, 164, 164, 1))
static let faint = color(light: rgba(130, 130, 130, 1), dark: rgba(118, 118, 118, 1))
static let line = color(light: rgba(226, 226, 226, 1), dark: rgba(255, 255, 255, 0.11))
static let brand = color(light: rgba(18, 18, 18, 1), dark: rgba(9, 9, 9, 1))
static let primary = color(light: rgba(23, 23, 23, 1), dark: rgba(240, 240, 240, 1))
static let onPrimary = color(light: rgba(255, 255, 255, 1), dark: rgba(13, 13, 13, 1))
static let danger = color(light: rgba(193, 46, 40, 1), dark: rgba(247, 113, 105, 1))
static let onDanger = color(light: rgba(255, 255, 255, 1), dark: rgba(24, 8, 7, 1))
static let warning = color(light: rgba(170, 92, 15, 1), dark: rgba(233, 166, 60, 1))
static let success = color(light: rgba(16, 122, 86, 1), dark: rgba(66, 199, 132, 1))
static let segmentTrack = color(light: rgba(234, 236, 236, 1), dark: rgba(26, 29, 29, 1))
static let segmentSelected = color(light: rgba(255, 255, 255, 1), dark: rgba(54, 58, 59, 1))
static let cardShadow = color(light: rgba(17, 22, 25, 0.06), dark: rgba(0, 0, 0, 0.60))
static let segmentTrack = color(light: rgba(235, 235, 235, 1), dark: rgba(28, 28, 28, 1))
static let segmentSelected = color(light: rgba(255, 255, 255, 1), dark: rgba(58, 58, 58, 1))
static let cardShadow = color(light: rgba(18, 18, 18, 0.06), dark: rgba(0, 0, 0, 0.60))
#elseif canImport(AppKit)
static let canvas = Color(red: 0.957, green: 0.961, blue: 0.961)
static let canvas = Color(red: 0.961, green: 0.961, blue: 0.961)
static let surface = Color.white
static let control = Color(red: 0.933, green: 0.937, blue: 0.937)
static let ink = Color(red: 0.067, green: 0.075, blue: 0.078)
static let muted = Color(red: 0.345, green: 0.369, blue: 0.380)
static let line = Color(red: 0.875, green: 0.886, blue: 0.886)
static let brand = Color(red: 0.071, green: 0.078, blue: 0.082)
static let control = Color(red: 0.937, green: 0.937, blue: 0.937)
static let ink = Color(red: 0.071, green: 0.071, blue: 0.071)
static let muted = Color(red: 0.361, green: 0.361, blue: 0.361)
static let line = Color(red: 0.886, green: 0.886, blue: 0.886)
static let brand = Color(red: 0.071, green: 0.071, blue: 0.071)
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.082, green: 0.094, blue: 0.098)
static let primary = Color(red: 0.090, green: 0.090, blue: 0.090)
static let danger = Color(red: 0.757, green: 0.180, blue: 0.157)
static let warning = Color(red: 0.667, green: 0.361, blue: 0.059)
static let success = Color(red: 0.063, green: 0.478, blue: 0.337)
static let tile = Color(red: 0.965, green: 0.969, blue: 0.969)
static let tileLine = Color(red: 0.906, green: 0.914, blue: 0.914)
static let faint = Color(red: 0.494, green: 0.518, blue: 0.529)
static let segmentTrack = Color(red: 0.918, green: 0.925, blue: 0.925)
static let tile = Color(red: 0.969, green: 0.969, blue: 0.969)
static let tileLine = Color(red: 0.910, green: 0.910, blue: 0.910)
static let faint = Color(red: 0.510, green: 0.510, blue: 0.510)
static let segmentTrack = Color(red: 0.922, green: 0.922, blue: 0.922)
static let segmentSelected = Color.white
static let cardShadow = Color(red: 0.067, green: 0.086, blue: 0.098).opacity(0.06)
static let cardShadow = Color(red: 0.071, green: 0.071, blue: 0.071).opacity(0.06)
#else
private static func color(_ red: CGFloat, _ green: CGFloat, _ blue: CGFloat) -> Color {
Color(red: red / 255, green: green / 255, blue: blue / 255)
}
static let canvas = color(243, 245, 248)
static let canvas = color(245, 245, 245)
static let surface = color(255, 255, 255)
static let control = color(247, 249, 251)
static let ink = color(28, 39, 56)
static let muted = color(111, 124, 143)
static let line = color(224, 229, 235)
static let brand = color(19, 30, 48)
static let surfaceRaised = color(251, 252, 253)
static let control = color(239, 239, 239)
static let ink = color(18, 18, 18)
static let muted = color(92, 92, 92)
static let line = color(226, 226, 226)
static let brand = color(18, 18, 18)
static let surfaceRaised = color(255, 255, 255)
static let chrome = color(255, 255, 255, 0.82)
static let onPrimary = color(255, 255, 255)
static let onDanger = color(255, 255, 255)
static let primary = color(17, 20, 25)
static let primary = color(23, 23, 23)
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 tile = color(247, 247, 247)
static let tileLine = color(232, 232, 232)
static let faint = color(130, 130, 130)
static let segmentTrack = color(235, 235, 235)
static let segmentSelected = color(255, 255, 255)
static let cardShadow = color(15, 30, 60)
static let cardShadow = color(18, 18, 18)
#endif
}
@@ -125,6 +126,9 @@ public enum AITheme {
public static let cardShadow = AIPalette.cardShadow
public static let buttonHeight: CGFloat = 48
// 停用态主按钮保留一层主色淡底,避免和次级按钮长得一模一样,层级读不出来。
public static let disabledFillOpacity: Double = 0.14
public static let disabledStrokeOpacity: Double = 0.22
public static let compactButtonHeight: CGFloat = 44
public static let largeButtonHeight: CGFloat = 56
public static let toolbarButtonHeight: CGFloat = 44