Skip to content
This repository was archived by the owner on Nov 16, 2025. It is now read-only.

Commit ec0659b

Browse files
committed
perf: improve UI responsiveness during Claude log processing
- Batch progress updates to every 10 files instead of every file - Add 0.5s debouncing to UI updates in ClaudeUsageDataLoader - Increase default RealTimeMonitor interval from 30s to 60s - Prevent excessive UI refreshes during log file processing This significantly improves app responsiveness when processing hundreds of Claude log files by reducing main thread UI updates.
1 parent 4c9ffd3 commit ec0659b

File tree

3 files changed

+25
-8
lines changed

3 files changed

+25
-8
lines changed

VibeMeter/Core/Services/ClaudeLogProcessor.swift

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,10 @@ actor ClaudeLogProcessor {
7878
await collector.incrementProcessedCount()
7979
}
8080

81-
// Send progress update if handler provided
82-
if let progressHandler {
81+
// Send progress update every 10 files or on last file
82+
let shouldUpdateProgress = processedCount % 10 == 0 || processedCount == fileURLs.count
83+
84+
if let progressHandler, shouldUpdateProgress {
8385
let (currentDailyUsage, _, currentFilesProcessed) = await collector.getResults()
8486
await progressHandler(currentFilesProcessed, currentDailyUsage)
8587
}

VibeMeter/Core/Services/RealTimeMonitor.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ public final class RealTimeMonitor: ObservableObject {
2828
public let providers: [ServiceProvider]
2929

3030
public static let `default` = Configuration(
31-
updateInterval: 30, // 30 seconds
31+
updateInterval: 60, // 60 seconds - reduced from 30 for better performance
3232
enableAnomalyDetection: true,
3333
enablePredictiveAlerts: true,
3434
burnRateThreshold: 10000, // tokens/hour

VibeMeter/Presentation/Views/ClaudeUsageReportModels.swift

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,8 @@ final class ClaudeUsageDataLoader {
157157
var availableProjects: [String] = []
158158

159159
private let claudeLogManager = ClaudeLogManager.shared
160+
private var updateTimer: Timer?
161+
private var pendingDailyUsage: [Date: [ClaudeLogEntry]]?
160162

161163
func loadData(forceRefresh: Bool = false) {
162164
guard !isLoading else { return }
@@ -208,18 +210,31 @@ extension ClaudeUsageDataLoader: ClaudeLogProgressDelegate {
208210

209211
func logProcessingDidUpdate(filesProcessed: Int, dailyUsage: [Date: [ClaudeLogEntry]]) {
210212
self.filesProcessed = filesProcessed
211-
self.dailyUsage = dailyUsage
213+
self.pendingDailyUsage = dailyUsage
212214

213215
let percentage = totalFiles > 0 ? Int((Double(filesProcessed) / Double(totalFiles)) * 100) : 0
214216
self.loadingMessage = "Processing files... \(percentage)% (\(filesProcessed)/\(totalFiles))"
215217

216-
// Update available projects
217-
updateAvailableProjects()
218-
219-
// Keep isLoading true - it will be set to false in logProcessingDidComplete
218+
// Cancel existing timer
219+
updateTimer?.invalidate()
220+
221+
// Debounce the UI update by 0.5 seconds
222+
updateTimer = Timer.scheduledTimer(withTimeInterval: 0.5, repeats: false) { [weak self] _ in
223+
Task { @MainActor [weak self] in
224+
guard let self = self, let pendingData = self.pendingDailyUsage else { return }
225+
self.dailyUsage = pendingData
226+
self.updateAvailableProjects()
227+
self.pendingDailyUsage = nil
228+
}
229+
}
220230
}
221231

222232
func logProcessingDidComplete(dailyUsage: [Date: [ClaudeLogEntry]]) {
233+
// Cancel any pending timer
234+
updateTimer?.invalidate()
235+
updateTimer = nil
236+
pendingDailyUsage = nil
237+
223238
self.dailyUsage = dailyUsage
224239
self.isLoading = false
225240
self.loadingMessage = ""

0 commit comments

Comments
 (0)