Files
oai-swift/oAI/Views/Main/FooterView.swift

163 lines
4.5 KiB
Swift

//
// FooterView.swift
// oAI
//
// Footer bar with session summary
//
import SwiftUI
struct FooterView: View {
let stats: SessionStats
var body: some View {
HStack(spacing: 20) {
// Session summary
HStack(spacing: 16) {
FooterItem(
icon: "message",
label: "Messages",
value: "\(stats.messageCount)"
)
FooterItem(
icon: "chart.bar.xaxis",
label: "Tokens",
value: "\(stats.totalTokens) (\(stats.totalInputTokens) in, \(stats.totalOutputTokens) out)"
)
FooterItem(
icon: "dollarsign.circle",
label: "Cost",
value: stats.totalCostDisplay
)
// Git sync status (if enabled)
if SettingsService.shared.syncEnabled && SettingsService.shared.syncAutoSave {
SyncStatusFooter()
}
}
Spacer()
// Shortcuts hint
#if os(macOS)
Text("⌘M Model • ⌘K Clear • ⌘S Stats")
.font(.caption2)
.foregroundColor(.oaiSecondary)
#endif
}
.padding(.horizontal, 16)
.padding(.vertical, 8)
.background(.ultraThinMaterial)
.overlay(
Rectangle()
.fill(Color.oaiBorder.opacity(0.5))
.frame(height: 1),
alignment: .top
)
}
}
struct FooterItem: View {
let icon: String
let label: String
let value: String
private let guiSize = SettingsService.shared.guiTextSize
var body: some View {
HStack(spacing: 6) {
Image(systemName: icon)
.font(.system(size: guiSize - 2))
.foregroundColor(.oaiSecondary)
Text(label + ":")
.font(.system(size: guiSize - 2))
.foregroundColor(.oaiSecondary)
Text(value)
.font(.system(size: guiSize - 2, weight: .medium))
.foregroundColor(.oaiPrimary)
}
}
}
struct SyncStatusFooter: View {
private let gitSync = GitSyncService.shared
private let guiSize = SettingsService.shared.guiTextSize
@State private var syncText = "Not Synced"
@State private var syncColor: Color = .secondary
var body: some View {
HStack(spacing: 6) {
Image(systemName: "arrow.triangle.2.circlepath")
.font(.system(size: guiSize - 2))
.foregroundColor(syncColor)
Text(syncText)
.font(.system(size: guiSize - 2, weight: .medium))
.foregroundColor(syncColor)
}
.onAppear {
updateSyncStatus()
}
.onChange(of: gitSync.syncStatus.lastSyncTime) {
updateSyncStatus()
}
.onChange(of: gitSync.lastSyncError) {
updateSyncStatus()
}
.onChange(of: gitSync.isSyncing) {
updateSyncStatus()
}
}
private func updateSyncStatus() {
if let error = gitSync.lastSyncError {
syncText = "Error With Sync"
syncColor = .red
} else if gitSync.isSyncing {
syncText = "Syncing..."
syncColor = .orange
} else if let lastSync = gitSync.syncStatus.lastSyncTime {
syncText = "Last Sync: \(timeAgo(lastSync))"
syncColor = .green
} else if gitSync.syncStatus.isCloned {
syncText = "Not Synced"
syncColor = .secondary
} else {
syncText = "Not Configured"
syncColor = .secondary
}
}
private func timeAgo(_ date: Date) -> String {
let seconds = Int(Date().timeIntervalSince(date))
if seconds < 60 {
return "just now"
} else if seconds < 3600 {
let minutes = seconds / 60
return "\(minutes)m ago"
} else if seconds < 86400 {
let hours = seconds / 3600
return "\(hours)h ago"
} else {
let days = seconds / 86400
return "\(days)d ago"
}
}
}
#Preview {
VStack {
Spacer()
FooterView(stats: SessionStats(
totalInputTokens: 1250,
totalOutputTokens: 3420,
totalCost: 0.0152,
messageCount: 12
))
}
.background(Color.oaiBackground)
}