New version v2.3.6

This commit is contained in:
2026-03-04 10:19:16 +01:00
parent 65a35cd508
commit 49f842f119
52 changed files with 14034 additions and 358 deletions

View File

@@ -47,19 +47,19 @@ struct FooterView: View {
HStack(spacing: 16) {
FooterItem(
icon: "message",
label: "Messages",
label: "Messages:",
value: "\(stats.messageCount)"
)
FooterItem(
icon: "chart.bar.xaxis",
label: "Tokens",
label: "Tokens:",
value: "\(stats.totalTokens) (\(stats.totalInputTokens) in, \(stats.totalOutputTokens) out)"
)
FooterItem(
icon: "dollarsign.circle",
label: "Cost",
label: "Cost:",
value: stats.totalCostDisplay
)
@@ -134,7 +134,7 @@ struct SaveIndicator: View {
return .secondary
}
private var tooltip: String {
private var tooltip: LocalizedStringKey {
if isModified { return "Click to re-save \"\(conversationName ?? "")\"" }
if isSaved { return "Saved — no changes" }
return "Not saved — use /save <name>"
@@ -162,7 +162,7 @@ struct SaveIndicator: View {
struct FooterItem: View {
let icon: String
let label: String
let label: LocalizedStringKey
let value: String
private let guiSize = SettingsService.shared.guiTextSize
@@ -172,7 +172,7 @@ struct FooterItem: View {
.font(.system(size: guiSize - 2))
.foregroundColor(.oaiSecondary)
Text(label + ":")
Text(label)
.font(.system(size: guiSize - 2))
.foregroundColor(.oaiSecondary)
@@ -187,9 +187,24 @@ struct SyncStatusFooter: View {
private let gitSync = GitSyncService.shared
private let settings = SettingsService.shared
private let guiSize = SettingsService.shared.guiTextSize
@State private var syncText = "Not Synced"
private enum SyncState { case off, notInitialized, ready, syncing, error, synced(Date) }
@State private var syncState: SyncState = .off
@State private var syncColor: Color = .secondary
private var syncText: LocalizedStringKey {
switch syncState {
case .off: return "Sync: Off"
case .notInitialized: return "Sync: Not Initialized"
case .ready: return "Sync: Ready"
case .syncing: return "Syncing..."
case .error: return "Sync Error"
case .synced(let date):
let rel = RelativeDateTimeFormatter().localizedString(for: date, relativeTo: .now)
return "Last Sync: \(rel)"
}
}
var body: some View {
HStack(spacing: 6) {
Image(systemName: "arrow.triangle.2.circlepath")
@@ -200,61 +215,27 @@ struct SyncStatusFooter: View {
.font(.system(size: guiSize - 2, weight: .medium))
.foregroundColor(syncColor)
}
.onAppear {
updateSyncStatus()
}
.onChange(of: gitSync.syncStatus.lastSyncTime) {
updateSyncStatus()
}
.onChange(of: gitSync.syncStatus.isCloned) {
updateSyncStatus()
}
.onChange(of: gitSync.lastSyncError) {
updateSyncStatus()
}
.onChange(of: gitSync.isSyncing) {
updateSyncStatus()
}
.onChange(of: settings.syncConfigured) {
updateSyncStatus()
}
.onAppear { updateSyncStatus() }
.onChange(of: gitSync.syncStatus.lastSyncTime) { updateSyncStatus() }
.onChange(of: gitSync.syncStatus.isCloned) { updateSyncStatus() }
.onChange(of: gitSync.lastSyncError) { updateSyncStatus() }
.onChange(of: gitSync.isSyncing) { updateSyncStatus() }
.onChange(of: settings.syncConfigured) { updateSyncStatus() }
}
private func updateSyncStatus() {
if gitSync.lastSyncError != nil {
syncText = "Sync Error"
syncColor = .red
syncState = .error; syncColor = .red
} else if gitSync.isSyncing {
syncText = "Syncing..."
syncColor = .orange
syncState = .syncing; syncColor = .orange
} else if let lastSync = gitSync.syncStatus.lastSyncTime {
syncText = "Last Sync: \(timeAgo(lastSync))"
syncColor = .green
syncState = .synced(lastSync); syncColor = .green
} else if gitSync.syncStatus.isCloned {
syncText = "Sync: Ready"
syncColor = .secondary
syncState = .ready; syncColor = .secondary
} else if settings.syncConfigured {
syncText = "Sync: Not Initialized"
syncColor = .orange
syncState = .notInitialized; syncColor = .orange
} else {
syncText = "Sync: Off"
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"
syncState = .off; syncColor = .secondary
}
}
}