Limits move into the Claude/ChatGPT pills (header ran under the camera); balanced home margins
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -28,6 +28,26 @@ enum DesktopApps {
|
||||
struct AppGridView: View {
|
||||
@ObservedObject var state: AppState
|
||||
|
||||
/// "9% · сброс через 1 д 18 ч" — the Claude window closest to its limit.
|
||||
private var claudeLimit: PillLimit? {
|
||||
guard state.planRelayInstalled, let usage = state.claudePlanUsage,
|
||||
let pct = ClaudePlanGauge.dominantPct(usage) else { return nil }
|
||||
var text = "\(Int(pct.rounded()))%"
|
||||
if let w = ClaudePlanGauge.dominantWindow(usage), w.resetsAt > Date() {
|
||||
text += " · сброс через \(PlanTime.untilReset(w.resetsAt))"
|
||||
}
|
||||
return PillLimit(text: text, color: ClaudePlanGauge.color(for: pct))
|
||||
}
|
||||
|
||||
/// "23% · сброс сб 15:22" — ChatGPT weekly limit from the Codex logs.
|
||||
private var chatGPTLimit: PillLimit? {
|
||||
guard let w = state.codexWeekly else { return nil }
|
||||
let pct = ClaudePlanGauge.effectivePct(w)
|
||||
var text = "\(Int(pct.rounded()))%"
|
||||
if w.resetsAt > Date() { text += " · сброс \(PlanTime.resetMoment(w.resetsAt))" }
|
||||
return PillLimit(text: text, color: ClaudePlanGauge.color(for: pct))
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
ZStack {
|
||||
VStack(spacing: 8) {
|
||||
@@ -36,12 +56,12 @@ struct AppGridView: View {
|
||||
EmptyPill()
|
||||
}
|
||||
HStack(spacing: 8) {
|
||||
AppPill(app: DesktopApps.claude)
|
||||
AppPill(app: DesktopApps.chatGPT)
|
||||
AppPill(app: DesktopApps.claude, limit: claudeLimit)
|
||||
AppPill(app: DesktopApps.chatGPT, limit: chatGPTLimit)
|
||||
}
|
||||
}
|
||||
.padding(.leading, 108)
|
||||
.padding(.trailing, 14)
|
||||
.padding(.leading, 104)
|
||||
.padding(.trailing, 20)
|
||||
.padding(.vertical, 6)
|
||||
|
||||
#if !APPSTORE
|
||||
@@ -62,8 +82,14 @@ struct AppGridView: View {
|
||||
|
||||
// MARK: - App pill
|
||||
|
||||
struct PillLimit {
|
||||
let text: String
|
||||
let color: String
|
||||
}
|
||||
|
||||
struct AppPill: View {
|
||||
let app: DesktopAppSlot
|
||||
var limit: PillLimit? = nil
|
||||
@State private var appURL: URL?
|
||||
@State private var isRunning = false
|
||||
@State private var isHovered = false
|
||||
@@ -78,23 +104,36 @@ struct AppPill: View {
|
||||
HStack(spacing: 10) {
|
||||
icon
|
||||
.frame(width: 26, height: 26)
|
||||
VStack(alignment: .leading, spacing: 1) {
|
||||
Text(app.name)
|
||||
.font(.system(size: 13, weight: .semibold))
|
||||
.foregroundColor(Color(hex: "#F5F6F8"))
|
||||
VStack(alignment: .leading, spacing: 2) {
|
||||
HStack(spacing: 6) {
|
||||
Text(app.name)
|
||||
.font(.system(size: 13, weight: .semibold))
|
||||
.foregroundColor(Color(hex: "#F5F6F8"))
|
||||
Spacer(minLength: 4)
|
||||
Image(systemName: "arrow.up.right")
|
||||
.font(.system(size: 9, weight: .semibold))
|
||||
.foregroundColor(isHovered ? Color(hex: app.color) : Color(hex: "#5F646D"))
|
||||
}
|
||||
HStack(spacing: 5) {
|
||||
Circle()
|
||||
.fill(isRunning ? Color(hex: "#34D399") : Color(hex: "#5F646D"))
|
||||
.frame(width: 5, height: 5)
|
||||
Text(status)
|
||||
.font(.system(size: 10.5))
|
||||
.foregroundColor(Color(hex: "#8E939C"))
|
||||
// With a limit shown, the green dot alone says "running".
|
||||
if limit == nil || appURL == nil {
|
||||
Text(status)
|
||||
.font(.system(size: 10.5))
|
||||
.foregroundColor(Color(hex: "#8E939C"))
|
||||
.lineLimit(1)
|
||||
}
|
||||
if let limit, appURL != nil {
|
||||
Text(limit.text)
|
||||
.font(.system(size: 10.5, weight: .semibold))
|
||||
.foregroundColor(Color(hex: limit.color))
|
||||
.lineLimit(1)
|
||||
.minimumScaleFactor(0.8)
|
||||
}
|
||||
}
|
||||
}
|
||||
Spacer(minLength: 4)
|
||||
Image(systemName: "arrow.up.right")
|
||||
.font(.system(size: 9, weight: .semibold))
|
||||
.foregroundColor(isHovered ? Color(hex: app.color) : Color(hex: "#5F646D"))
|
||||
}
|
||||
.padding(.horizontal, 12)
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||
|
||||
Reference in New Issue
Block a user