fix: warm sheet header, welcome state for AI chat, consistent colors
All checks were successful
Security Checks / dependency-audit (push) Successful in 13s
Security Checks / secret-scanning (push) Successful in 4s
Security Checks / dockerfile-lint (push) Successful in 3s

- Custom sheet header with warm canvas background (no white bar)
- Tab picker styled as warm pills (not default segmented control)
- AI Chat welcome state: sparkles icon + 'What did you eat?' prompt
- All backgrounds use canvas color consistently

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Yusuf Suleman
2026-04-03 09:18:21 -05:00
parent 39d7628c9e
commit b9d80fb6b6
2 changed files with 70 additions and 15 deletions

View File

@@ -63,28 +63,62 @@ struct MainTabView: View {
}
struct AssistantSheetView: View {
@Environment(\.dismiss) private var dismiss
@State private var selectedMode = 0
var body: some View {
NavigationStack {
VStack(spacing: 0) {
Picker("Mode", selection: $selectedMode) {
Text("AI Chat").tag(0)
Text("Quick Add").tag(1)
}
.pickerStyle(.segmented)
.padding(.horizontal)
// Custom header warm background
VStack(spacing: 12) {
// Drag handle
Capsule()
.fill(Color.textTertiary.opacity(0.3))
.frame(width: 36, height: 5)
.padding(.top, 8)
Text("Add Food")
.font(.headline)
.foregroundStyle(Color.textPrimary)
// Tab picker warm styled
HStack(spacing: 4) {
tabButton("AI Chat", icon: "sparkles", index: 0)
tabButton("Quick Add", icon: "magnifyingglass", index: 1)
}
.padding(4)
.background(Color.textTertiary.opacity(0.08))
.clipShape(Capsule())
.padding(.horizontal, 40)
}
.padding(.bottom, 12)
.background(Color.canvas)
// Content
if selectedMode == 0 {
AssistantChatView()
} else {
FoodSearchView(isSheet: true)
}
}
.navigationTitle("Add Food")
.navigationBarTitleDisplayMode(.inline)
}
.background(Color.canvas)
.presentationDetents([.large])
}
private func tabButton(_ title: String, icon: String, index: Int) -> some View {
Button {
withAnimation(.easeInOut(duration: 0.2)) { selectedMode = index }
} label: {
HStack(spacing: 5) {
Image(systemName: icon)
.font(.caption2)
Text(title)
.font(.subheadline.weight(selectedMode == index ? .semibold : .regular))
}
.foregroundStyle(selectedMode == index ? Color.textPrimary : Color.textTertiary)
.padding(.horizontal, 16)
.padding(.vertical, 8)
.background(selectedMode == index ? Color.surfaceCard : Color.clear)
.clipShape(Capsule())
}
}
}

View File

@@ -10,6 +10,27 @@ struct AssistantChatView: View {
ScrollViewReader { proxy in
ScrollView {
LazyVStack(spacing: 8) {
// Welcome state when no messages
if vm.messages.isEmpty {
VStack(spacing: 16) {
Spacer(minLength: 60)
Image(systemName: "sparkles")
.font(.system(size: 40))
.foregroundStyle(Color.accentWarm.opacity(0.4))
Text("What did you eat?")
.font(.title3.weight(.semibold))
.foregroundStyle(Color.textPrimary)
Text("Describe your food naturally.\n\"2 eggs and toast for breakfast\"\nor snap a photo of your meal.")
.font(.subheadline)
.foregroundStyle(Color.textSecondary)
.multilineTextAlignment(.center)
.lineSpacing(4)
Spacer(minLength: 60)
}
.frame(maxWidth: .infinity)
.padding(.horizontal, 32)
}
ForEach(vm.messages) { message in
chatBubble(message)
.id(message.id)