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:
maksarsanjeev
2026-10-06 22:27:26 +03:00
parent a4d5032959
commit ae394370bf
4 changed files with 59 additions and 24 deletions
+54 -15
View File
@@ -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)
+1 -1
View File
@@ -104,6 +104,6 @@ enum PlanTime {
let f = DateFormatter()
f.locale = Locale(identifier: "ru_RU")
f.dateFormat = date.timeIntervalSince(now) < 6 * 86400 ? "EE HH:mm" : "d MMM HH:mm"
return f.string(from: date).replacingOccurrences(of: ".", with: "")
return f.string(from: date).replacingOccurrences(of: ".", with: "").lowercased()
}
}
+2 -6
View File
@@ -552,12 +552,8 @@ struct IslandHeader: View {
// Right: plan pill (GitHub build, home view only) + action icons
HStack(spacing: 8) {
#if !APPSTORE
if (state.view == .overview || state.view == .empty) && state.showPlanInNotch && state.planRelayInstalled {
ClaudePlanHeaderPill(state: state)
}
if (state.view == .overview || state.view == .empty), let w = state.codexWeekly {
ChatGPTLimitPill(window: w)
}
// Plan limits live inside the Claude / ChatGPT pills (AppGridView):
// here they ran under the camera.
#endif
HStack(spacing: 14) {
Button(action: {