feat(ios): comprehensive iPhone+iPad UX polish — refined-native design system
Freeze a shared design system (DesignSystem/{Tokens,Typography,StatusStyle,
Primitives}.swift): indigo #7C8CFF accent (root .tint), semantic status colors,
2-4-8-12-16-20-24 spacing + sm8/md12/lg16 radii scale, SF Mono tabular numbers,
reduce-motion-gated animations, haptics, reusable StatusBadge/TelemetryChip/Card/
SectionHeader/DSButtonStyle/ContinueLastBanner. Every changed view consumes tokens
— no hardcoded colors/spacing.
Applied across all surfaces (visual only — zero behavior/logic change, all suites
green): session list rows + status system (shape+color, not color-alone) +
telemetry chips + thumbnail placeholders; terminal gate card (≥44pt approve/reject)
+ keybar + reconnect + quick-reply + digest + SwiftTerm accent theme; pairing hero
+ warning tiers + Projects cards + Timeline/Diff; nav chrome + iPad split placeholder
+ privacy shade + motion. Chinese gate copy. UX finding fixed: timeline class colors
now via DS.Palette (+timelineTool/timelineUser tokens).
Design review 8.5/10. Verified: iPhone 16 290 + iPad Pro 11 290 tests green;
packages + integration green; consistency audit ~clean; zero changes under
ios/Packages, src/, public/.
This commit is contained in:
@@ -12,11 +12,6 @@ import WireProtocol
|
||||
struct TimelineSheet: View {
|
||||
let viewModel: TimelineViewModel
|
||||
|
||||
private enum Metrics {
|
||||
static let rowSpacing: CGFloat = 10
|
||||
static let iconColumnWidth: CGFloat = 24
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
NavigationStack {
|
||||
content
|
||||
@@ -66,6 +61,7 @@ struct TimelineSheet: View {
|
||||
Task { await viewModel.load() }
|
||||
}
|
||||
.buttonStyle(.borderedProminent)
|
||||
.tint(DS.Palette.accent)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,21 +79,25 @@ struct TimelineSheet: View {
|
||||
}
|
||||
|
||||
private func row(_ event: TimelineEvent) -> some View {
|
||||
HStack(spacing: Metrics.rowSpacing) {
|
||||
HStack(spacing: DS.Space.md12) {
|
||||
// Timestamp — mono/tabular so HH:mm columns line up (direction).
|
||||
Text(TimelineRowFormat.timeLabel(atMs: event.at))
|
||||
.font(.caption.monospacedDigit())
|
||||
.foregroundStyle(.secondary)
|
||||
.font(DS.Typography.mono(.caption))
|
||||
.foregroundStyle(DS.Palette.textSecondary)
|
||||
// class → glyph + semantic color (frozen mapping; unit-tested).
|
||||
Text(verbatim: TimelineClassStyle.glyph(for: event.class))
|
||||
.font(.callout)
|
||||
.font(DS.Typography.callout)
|
||||
.foregroundStyle(TimelineClassStyle.color(for: event.class))
|
||||
.frame(width: Metrics.iconColumnWidth)
|
||||
.frame(width: DS.Space.xxl24)
|
||||
// Server-derived phrase — untrusted: verbatim (never
|
||||
// LocalizedStringKey/Markdown) + hard single-line truncation.
|
||||
Text(verbatim: event.label)
|
||||
.font(.subheadline)
|
||||
.font(DS.Typography.callout)
|
||||
.foregroundStyle(DS.Palette.textPrimary)
|
||||
.lineLimit(1)
|
||||
Spacer(minLength: 0)
|
||||
}
|
||||
.padding(.vertical, DS.Space.xs2)
|
||||
.accessibilityElement(children: .combine)
|
||||
}
|
||||
}
|
||||
@@ -125,13 +125,14 @@ enum TimelineClassStyle {
|
||||
}
|
||||
|
||||
static func color(for cls: String) -> Color {
|
||||
// All from the frozen design system — no raw SwiftUI colors (UX finding).
|
||||
switch cls {
|
||||
case "tool": return .blue
|
||||
case "waiting": return .orange
|
||||
case "done": return .green
|
||||
case "stuck": return .red
|
||||
case "user": return .purple
|
||||
default: return .secondary
|
||||
case "tool": return DS.Palette.timelineTool
|
||||
case "waiting": return DS.Palette.statusWaiting
|
||||
case "done": return DS.Palette.statusWorking
|
||||
case "stuck": return DS.Palette.statusStuck
|
||||
case "user": return DS.Palette.timelineUser
|
||||
default: return DS.Palette.textSecondary
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user