diff --git a/apps/decodex-app/Sources/DecodexApp/AccountPanelView.swift b/apps/decodex-app/Sources/DecodexApp/AccountPanelView.swift index 341f7ecd..61b7e04a 100644 --- a/apps/decodex-app/Sources/DecodexApp/AccountPanelView.swift +++ b/apps/decodex-app/Sources/DecodexApp/AccountPanelView.swift @@ -90,7 +90,7 @@ enum PanelPalette { static func glassStroke(_ colorScheme: ColorScheme) -> Color { colorScheme == .dark ? Color.white.opacity(0.1) - : Color.white.opacity(0.5) + : Color(red: 0.34, green: 0.42, blue: 0.52).opacity(0.18) } static func glassInnerShadow(_ colorScheme: ColorScheme) -> Color { @@ -1526,8 +1526,11 @@ private struct AccountListScrollIndicatorView: View { } private enum AccountRunChipLayout { - static let height: CGFloat = 21 - static let cornerRadius: CGFloat = 10.5 + static let height: CGFloat = 18.5 + static let cornerRadius: CGFloat = 9.25 + static let horizontalPadding: CGFloat = 6.5 + static let iconWidth: CGFloat = 9.5 + static let spacing: CGFloat = 4 } struct AccountRunChipView: View { @@ -1537,21 +1540,21 @@ struct AccountRunChipView: View { @State private var showsPopover = false var body: some View { - HStack(spacing: 5) { + HStack(spacing: AccountRunChipLayout.spacing) { Image(systemName: symbol) - .font(PanelFont.summaryIcon) + .font(PanelFont.runChipIcon) .foregroundStyle(tint.opacity(colorScheme == .dark ? 0.88 : 0.76)) - .frame(width: 11) + .frame(width: AccountRunChipLayout.iconWidth) Text(run.compactTitle) - .font(PanelFont.metricValue) + .font(PanelFont.runChipTitle) .foregroundStyle(PanelPalette.primaryText(colorScheme).opacity(0.92)) .lineLimit(1) .truncationMode(.middle) .fixedSize(horizontal: true, vertical: false) } .frame(height: AccountRunChipLayout.height) - .padding(.horizontal, 8) + .padding(.horizontal, AccountRunChipLayout.horizontalPadding) .background { RoundedRectangle(cornerRadius: AccountRunChipLayout.cornerRadius, style: .continuous) .fill(isHovered ? tint.opacity(colorScheme == .dark ? 0.09 : 0.07) : Color.clear) @@ -1761,31 +1764,21 @@ struct AccountPoolUsageEstimateView: View { @Environment(\.colorScheme) private var colorScheme var body: some View { - VStack(alignment: .leading, spacing: 4) { - HStack(spacing: 0) { - AccountPoolUsageMetricView( - title: "Pool used", - value: formatUsagePercent(estimate.totalUsedOfCapacityPercent), - tint: poolUsageTint - ) - - usageDivider - - AccountPoolUsageMetricView( - title: "Day Δ", - value: dayDeltaText, - tint: dayDeltaTint - ) - - usageDivider + VStack(alignment: .leading, spacing: 3) { + HStack(spacing: 5) { + ForEach(Array(metrics.enumerated()), id: \.offset) { index, metric in + AccountPoolUsageMetricView( + title: metric.title, + value: metric.value, + tint: metric.tint + ) - AccountPoolUsageMetricView( - title: "Daily avg", - value: formatDailyUsageRate(estimate.averageDailyPoolPercent), - tint: PanelPalette.secondaryText(colorScheme) - ) + if index < metrics.count - 1 { + Spacer(minLength: 3) + } + } } - .frame(height: 30) + .frame(height: 16) if estimate.accountEstimateCount < estimate.accountCount { Text("\(estimate.accountEstimateCount)/\(estimate.accountCount) accounts measured") @@ -1801,10 +1794,20 @@ struct AccountPoolUsageEstimateView: View { .accessibilityLabel(accessibilityLabel) } - private var usageDivider: some View { - Rectangle() - .fill(PanelPalette.separator(colorScheme).opacity(colorScheme == .dark ? 0.72 : 0.9)) - .frame(width: 0.5, height: 20) + private var metrics: [(title: String, value: String, tint: Color)] { + [ + ( + "Pool used", + formatUsagePercent(estimate.totalUsedOfCapacityPercent), + poolUsageTint + ), + ("Day Δ", dayDeltaText, dayDeltaTint), + ( + "Daily avg", + formatDailyUsageRate(estimate.averageDailyPoolPercent), + PanelPalette.secondaryText(colorScheme) + ), + ] } private var accessibilityLabel: String { @@ -1922,21 +1925,20 @@ struct AccountPoolUsageMetricView: View { @Environment(\.colorScheme) private var colorScheme var body: some View { - VStack(alignment: .leading, spacing: 1) { + HStack(alignment: .firstTextBaseline, spacing: 3) { Text(title) - .font(PanelFont.metricLabel) - .foregroundStyle(PanelPalette.secondaryText(colorScheme)) + .font(PanelFont.usageLabel) + .foregroundStyle(PanelPalette.secondaryText(colorScheme).opacity(0.82)) .lineLimit(1) Text(value) - .font(PanelFont.metricValue) + .font(PanelFont.usageValue) .foregroundStyle(tint.opacity(colorScheme == .dark ? 0.94 : 0.78)) .monospacedDigit() .lineLimit(1) .minimumScaleFactor(0.72) } - .frame(maxWidth: .infinity, alignment: .leading) - .padding(.horizontal, 5) + .lineLimit(1) } } @@ -2460,17 +2462,17 @@ struct OperatorStatusStripView: View { @Environment(\.colorScheme) private var colorScheme var body: some View { - VStack(alignment: .leading, spacing: 5) { - HStack(spacing: 0) { + VStack(alignment: .leading, spacing: 4) { + HStack(spacing: 5) { ForEach(Array(metrics.enumerated()), id: \.element.id) { index, metric in - if index > 0 { - flowDivider - } - OperatorFlowMetricView(metric: metric) + + if index < metrics.count - 1 { + Spacer(minLength: 3) + } } } - .frame(height: 32) + .frame(height: 16) if let warning = snapshot.warningSummary { HStack(spacing: 5) { @@ -2501,12 +2503,6 @@ struct OperatorStatusStripView: View { .modernGlassSurface(cornerRadius: 10, depth: .section) } - private var flowDivider: some View { - Rectangle() - .fill(PanelPalette.separator(colorScheme).opacity(colorScheme == .dark ? 0.72 : 0.9)) - .frame(width: 0.5, height: 21) - } - private var metrics: [OperatorFlowMetric] { [ OperatorFlowMetric( @@ -3007,6 +3003,10 @@ struct OperatorFlowMetric: Identifiable { var unit: String { value == 1 ? unitSingular : unitPlural } + + var fullText: String { + "\(title) \(value) \(unit)" + } } struct OperatorFlowMetricView: View { @@ -3014,21 +3014,22 @@ struct OperatorFlowMetricView: View { @Environment(\.colorScheme) private var colorScheme var body: some View { - VStack(alignment: .leading, spacing: 1) { + HStack(alignment: .firstTextBaseline, spacing: 3) { Text(metric.title) - .font(PanelFont.metricLabel) - .foregroundStyle(PanelPalette.secondaryText(colorScheme)) + .font(PanelFont.usageLabel) + .foregroundStyle(PanelPalette.secondaryText(colorScheme).opacity(0.82)) .lineLimit(1) - Text("\(metric.value) \(metric.unit)") - .font(PanelFont.metricValue) + Text("\(metric.value)") + .font(PanelFont.usageValue) .foregroundStyle(valueTint) .monospacedDigit() .lineLimit(1) + .minimumScaleFactor(0.72) } - .frame(maxWidth: .infinity, alignment: .leading) - .padding(.horizontal, 5) - .help(metric.title) + .lineLimit(1) + .help(metric.fullText) + .accessibilityLabel(metric.fullText) } private var valueTint: Color { @@ -3630,6 +3631,9 @@ struct ModernGlassSurfaceModifier: ViewModifier { if #available(macOS 26.0, *) { content + .background { + shape.fill(surfaceFill) + } .glassEffect( configuredGlass, in: shape @@ -3649,6 +3653,7 @@ struct ModernGlassSurfaceModifier: ViewModifier { content .background { shape.fill(materialStyle) + shape.fill(surfaceFill) } .overlay { shape @@ -3708,6 +3713,27 @@ struct ModernGlassSurfaceModifier: ViewModifier { } } + private var surfaceFill: Color { + switch depth { + case .panel: + return colorScheme == .dark + ? Color(red: 0.18, green: 0.22, blue: 0.28).opacity(0.08) + : Color(red: 0.95, green: 0.97, blue: 0.99).opacity(0.38) + case .section: + return colorScheme == .dark + ? Color(red: 0.22, green: 0.26, blue: 0.32).opacity(0.15) + : Color(red: 0.86, green: 0.9, blue: 0.95).opacity(0.66) + case .row: + return colorScheme == .dark + ? Color(red: 0.2, green: 0.24, blue: 0.3).opacity(0.12) + : Color(red: 0.87, green: 0.91, blue: 0.96).opacity(0.56) + case .control: + return colorScheme == .dark + ? Color(red: 0.24, green: 0.29, blue: 0.36).opacity(0.18) + : Color(red: 0.8, green: 0.85, blue: 0.91).opacity(0.72) + } + } + private var surfaceStroke: Color { switch depth { case .panel: diff --git a/apps/decodex-app/Sources/DecodexApp/PanelTypography.swift b/apps/decodex-app/Sources/DecodexApp/PanelTypography.swift index 986315a1..c3f320c1 100644 --- a/apps/decodex-app/Sources/DecodexApp/PanelTypography.swift +++ b/apps/decodex-app/Sources/DecodexApp/PanelTypography.swift @@ -18,7 +18,7 @@ enum PanelFont { static let emptyBody = text(10.9, weight: .regular) static let notice = text(10.6, weight: .regular) - static let accountName = text(13.1, weight: .semibold) + static let accountName = text(12.6, weight: .semibold) static let accountDetail = text(10.9, weight: .medium) static let summaryIcon = text(10.4, weight: .medium) @@ -36,6 +36,8 @@ enum PanelFont { static let lanePopoverValue = text(11.0, weight: .semibold) static let lanePopoverMeta = text(10.6, weight: .medium) static let lanePopoverChip = text(10.5, weight: .semibold) + static let runChipIcon = text(9.5, weight: .semibold) + static let runChipTitle = text(10.6, weight: .semibold) static let iconButton = text(11.2, weight: .semibold) }