fix(mac): render context bar reliably

This commit is contained in:
Peter Steinberger
2025-12-13 00:13:33 +00:00
parent 072ad8d371
commit 9b9fa009d1
2 changed files with 59 additions and 31 deletions

View File

@@ -24,21 +24,19 @@ struct ContextUsageBar: View {
}
var body: some View {
GeometryReader { proxy in
let fillWidth = proxy.size.width * self.clampedFractionUsed
ZStack(alignment: .leading) {
Capsule()
.fill(Color.secondary.opacity(0.25))
Capsule()
.fill(self.tint)
.frame(width: max(1, fillWidth))
}
// Prefer the native progress indicator in menus; `GeometryReader` can get wonky
// inside `MenuBarExtra`-backed menus (often receiving zero width).
ZStack {
Capsule()
.fill(Color.secondary.opacity(0.25))
ProgressView(value: self.clampedFractionUsed, total: 1)
.progressViewStyle(.linear)
.tint(self.tint)
.clipShape(Capsule())
}
.frame(maxWidth: .infinity)
.frame(height: self.height)
.accessibilityLabel("Context usage")
.accessibilityValue(self.accessibilityValue)
.drawingGroup()
}
private var accessibilityValue: String {

View File

@@ -321,26 +321,7 @@ enum SessionLoader {
return nil
}
guard let text = try? String(contentsOf: logURL, encoding: .utf8) else { return nil }
var lastUsage: [String: Any]?
for line in text.split(whereSeparator: \.isNewline) {
let trimmedLine = line.trimmingCharacters(in: .whitespacesAndNewlines)
if trimmedLine.isEmpty { continue }
guard let data = trimmedLine.data(using: .utf8) else { continue }
guard let obj = try? JSONSerialization.jsonObject(with: data) as? [String: Any] else { continue }
if let message = obj["message"] as? [String: Any], let usage = message["usage"] as? [String: Any] {
lastUsage = usage
continue
}
if let usage = obj["usage"] as? [String: Any] {
lastUsage = usage
continue
}
}
guard let lastUsage else { return nil }
guard let lastUsage = self.readLastUsageFromJsonl(logURL) else { return nil }
let input = self.number(from: lastUsage["input"]) ?? 0
let output = self.number(from: lastUsage["output"]) ?? 0
@@ -354,6 +335,55 @@ enum SessionLoader {
return nil
}
private static func readLastUsageFromJsonl(_ url: URL) -> [String: Any]? {
// Logs can contain huge toolResult payloads (base64 images). Avoid parsing the whole file:
// read a tail window and scan backwards for the last JSON line that contains a usage blob.
let handle: FileHandle
do {
handle = try FileHandle(forReadingFrom: url)
} catch {
return nil
}
defer { try? handle.close() }
let fileSize: UInt64
do {
fileSize = try handle.seekToEnd()
} catch {
return nil
}
let window: UInt64 = 512 * 1024
let start = fileSize > window ? fileSize - window : 0
do {
try handle.seek(toOffset: start)
} catch {
return nil
}
let data = (try? handle.readToEnd()) ?? Data()
guard let text = String(data: data, encoding: .utf8) else { return nil }
let lines = text.split(whereSeparator: \.isNewline)
for line in lines.reversed() {
let trimmedLine = line.trimmingCharacters(in: .whitespacesAndNewlines)
if trimmedLine.isEmpty { continue }
// Cheap prefilter before JSON parsing.
if !trimmedLine.contains("\"usage\"") { continue }
guard let lineData = trimmedLine.data(using: .utf8) else { continue }
guard let obj = try? JSONSerialization.jsonObject(with: lineData) as? [String: Any] else { continue }
if let message = obj["message"] as? [String: Any], let usage = message["usage"] as? [String: Any] {
return usage
}
if let usage = obj["usage"] as? [String: Any] {
return usage
}
}
return nil
}
private static func number(from raw: Any?) -> Int? {
switch raw {
case let v as Int: v