Compare commits
28
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
73b251a1e9 | ||
|
|
7bab6a0df0 | ||
|
|
db6380fa66 | ||
|
|
47a148fa67 | ||
|
|
85ac329d36 | ||
|
|
a0fb6eabcf | ||
|
|
b778d03323 | ||
|
|
13387888b2 | ||
|
|
44be1649a1 | ||
|
|
1160712e1b | ||
|
|
e4f55c80ee | ||
|
|
526752dc36 | ||
|
|
a2d983b555 | ||
|
|
0d807c2285 | ||
|
|
1c42433145 | ||
|
|
c0ac2e5a66 | ||
|
|
405e65f9f7 | ||
|
|
1c7433ebea | ||
|
|
a39ef800e8 | ||
|
|
76dec8b85d | ||
|
|
2f04f8581c | ||
|
|
788c066a1e | ||
|
|
31280d4ab2 | ||
|
|
7b1c5bbf8e | ||
|
|
f2ba44bbaa | ||
|
|
de1fffe0e3 | ||
|
|
e80f07d1b9 | ||
|
|
e9ca8a7efc |
@@ -40,8 +40,7 @@ struct ContentView: View {
|
|||||||
.environmentObject(updater)
|
.environmentObject(updater)
|
||||||
.transition(.move(edge: .trailing).combined(with: .opacity))
|
.transition(.move(edge: .trailing).combined(with: .opacity))
|
||||||
} else {
|
} else {
|
||||||
ScrollView(.vertical, showsIndicators: false) {
|
VStack(spacing: 0) {
|
||||||
VStack(spacing: 0) {
|
|
||||||
// Cards grid
|
// Cards grid
|
||||||
if vm.limits.isEmpty {
|
if vm.limits.isEmpty {
|
||||||
EmptyStateView()
|
EmptyStateView()
|
||||||
@@ -96,7 +95,6 @@ struct ContentView: View {
|
|||||||
.padding(.bottom, 8)
|
.padding(.bottom, 8)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.transition(.opacity)
|
.transition(.opacity)
|
||||||
|
|
||||||
@@ -398,12 +396,11 @@ struct DiaryRow: View {
|
|||||||
var body: some View {
|
var body: some View {
|
||||||
VStack(alignment: .leading, spacing: 5) {
|
VStack(alignment: .leading, spacing: 5) {
|
||||||
HStack {
|
HStack {
|
||||||
AgentIconView(agent: .claude, size: 12)
|
Text("\(totalSaved) samples")
|
||||||
Text("Claude")
|
.font(.system(size: 10.5))
|
||||||
.font(.system(size: 12, weight: .medium))
|
.foregroundColor(.secondary)
|
||||||
.foregroundColor(color)
|
|
||||||
Spacer()
|
Spacer()
|
||||||
Text("\(totalSaved) samples · Avg Burn Rate: +\(String(format: "%.1f", burnRate))%/h")
|
Text("Avg burn rate: \(String(format: "%.1f", burnRate))%/h")
|
||||||
.font(.system(size: 10.5))
|
.font(.system(size: 10.5))
|
||||||
.foregroundColor(.secondary)
|
.foregroundColor(.secondary)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,9 +15,9 @@
|
|||||||
<key>CFBundlePackageType</key>
|
<key>CFBundlePackageType</key>
|
||||||
<string>APPL</string>
|
<string>APPL</string>
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
<string>1.1.4-beta.5</string>
|
<string>1.2.1-beta.1</string>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>45</string>
|
<string>49</string>
|
||||||
<key>LSMinimumSystemVersion</key>
|
<key>LSMinimumSystemVersion</key>
|
||||||
<string>13.0</string>
|
<string>13.0</string>
|
||||||
<key>LSUIElement</key>
|
<key>LSUIElement</key>
|
||||||
|
|||||||
@@ -38,6 +38,16 @@ class UsageViewModel: ObservableObject {
|
|||||||
burnHistoryStore = saved
|
burnHistoryStore = saved
|
||||||
}
|
}
|
||||||
loadPlaceholderData()
|
loadPlaceholderData()
|
||||||
|
Task { await checkInitialSignInState() }
|
||||||
|
}
|
||||||
|
|
||||||
|
private func checkInitialSignInState() async {
|
||||||
|
let cookies = await WKWebsiteDataStore.default().httpCookieStore.allCookies()
|
||||||
|
let hasSession = cookies.contains {
|
||||||
|
$0.domain.contains("claude.ai") &&
|
||||||
|
($0.name == "sessionKey" || $0.name == "__Secure-next-auth.session-token")
|
||||||
|
}
|
||||||
|
if hasSession { isSignedIn = true }
|
||||||
}
|
}
|
||||||
|
|
||||||
func signOut() async {
|
func signOut() async {
|
||||||
@@ -140,11 +150,22 @@ class UsageViewModel: ObservableObject {
|
|||||||
let memberships = (account?["memberships"] ?? json["memberships"]) as? [[String: Any]]
|
let memberships = (account?["memberships"] ?? json["memberships"]) as? [[String: Any]]
|
||||||
let firstOrg = memberships?.first?["organization"] as? [String: Any]
|
let firstOrg = memberships?.first?["organization"] as? [String: Any]
|
||||||
|
|
||||||
// org ID — primary path then flat-list fallback
|
// org ID — primary path then flat-list fallback then dedicated endpoint
|
||||||
var orgId: String? = firstOrg?["uuid"] as? String
|
var orgId: String? = firstOrg?["uuid"] as? String
|
||||||
if orgId == nil {
|
if orgId == nil {
|
||||||
orgId = (json["organizations"] as? [[String: Any]])?.first?["uuid"] as? String
|
orgId = (json["organizations"] as? [[String: Any]])?.first?["uuid"] as? String
|
||||||
}
|
}
|
||||||
|
if orgId == nil, let cookie = await claudeCookieHeader() {
|
||||||
|
// Final fallback: fetch /api/organizations directly
|
||||||
|
var orgsReq = URLRequest(url: URL(string: "https://claude.ai/api/organizations")!)
|
||||||
|
orgsReq.setValue("application/json", forHTTPHeaderField: "accept")
|
||||||
|
orgsReq.setValue(cookie, forHTTPHeaderField: "Cookie")
|
||||||
|
if let (orgsData, orgsResp) = try? await URLSession.shared.data(for: orgsReq),
|
||||||
|
let orgsHttp = orgsResp as? HTTPURLResponse, orgsHttp.statusCode == 200,
|
||||||
|
let orgs = try? JSONSerialization.jsonObject(with: orgsData) as? [[String: Any]] {
|
||||||
|
orgId = orgs.first?["uuid"] as? String
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let email = account?["email_address"] as? String
|
let email = account?["email_address"] as? String
|
||||||
|
|
||||||
|
|||||||
@@ -8,9 +8,9 @@
|
|||||||
|
|
||||||
[](https://www.apple.com/macos/)
|
[](https://www.apple.com/macos/)
|
||||||
[](https://swift.org)
|
[](https://swift.org)
|
||||||
[](https://github.com/superdooper86/claudechecker/releases)
|
[](https://github.com/superdooper86/claudechecker/releases)
|
||||||
[](LICENSE)
|
[](LICENSE)
|
||||||
[](https://github.com/superdooper86/claudechecker/releases/tag/v1.1.4-beta.4) <!-- BETA_BADGE -->
|
<!-- BETA_BADGE -->
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
+6
-3
@@ -1,4 +1,7 @@
|
|||||||
## What's new in v1.1.4-beta.5
|
## What's new in v1.2.1
|
||||||
|
|
||||||
### Improvements
|
### Bug fixes
|
||||||
- Bootstrap API call consolidated — org ID, email, and plan name now fetched in a single request per refresh
|
- Fixed "Not signed in" showing incorrectly on launch when the session was already active
|
||||||
|
- Sign-in state is now detected immediately from stored cookies on startup, before the first data refresh completes
|
||||||
|
- Added `/api/organizations` as a final fallback for org ID resolution when the bootstrap API response doesn't include it
|
||||||
|
- Session cookie detection now checks both `sessionKey` and `__Secure-next-auth.session-token` cookie names
|
||||||
|
|||||||
+3
-3
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"version": "1.1.4-beta.4",
|
"version": "1.1.4-beta.7",
|
||||||
"url": "https://github.com/superdooper86/claudechecker/releases/download/v1.1.4-beta.4/ClaudeChecker.zip",
|
"url": "https://github.com/superdooper86/claudechecker/releases/download/v1.1.4-beta.7/ClaudeChecker.zip",
|
||||||
"notes": "## What's new in v1.1.4-beta.4\n\n### Bug fix\n- Plan name now updates correctly on every refresh (was only set on first launch)"
|
"notes": "## What's new in v1.1.4-beta.7\n\n### Bug fix\n- Main panel no longer scrolls — popover now auto-sizes to fit content"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"version": "0.0.1-beta.35",
|
"version": "0.0.1-beta.49",
|
||||||
"url": "https://github.com/superdooper86/claudechecker/releases/download/win-v0.0.1-beta.35/ClaudeChecker-Windows.zip",
|
"url": "https://github.com/superdooper86/claudechecker/releases/download/win-v0.0.1-beta.49/ClaudeChecker-Windows.zip",
|
||||||
"notes": "## What's new in beta.10\r\n\r\n### Bug fixes\r\n- Limits now update correctly on each refresh cycle\r\n- Session expiry is detected and prompts re-authentication instead of silently showing stale data\r\n- Plan name (e.g. Pro, Max) is now read from the API instead of being hardcoded"
|
"notes": "## What's new in beta.36\r\n\r\n### Bug fixes\r\n- Settings no longer incorrectly shows \"Not signed in\" when limits are working\r\n- Session Diary now shows correctly after the first successful refresh\r\n- Extra Usage Credits section now restored from cache on startup\r\n- Plan name, overage, and prepaid credits are now cached and shown immediately on launch\r\n- App now loads cached state instantly on startup before the background refresh completes"
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-3
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"version": "1.1.3",
|
"version": "1.2.0",
|
||||||
"url": "https://github.com/superdooper86/claudechecker/releases/download/v1.1.3/ClaudeChecker.zip",
|
"url": "https://github.com/superdooper86/claudechecker/releases/download/v1.2.0/ClaudeChecker.zip",
|
||||||
"notes": "## What's new in v1.1.3\n\n### UI polish\n- Limit headers now read \"5 Hour Limit\" and \"7 Day Limit\"\n- Tapping anywhere on the blue update banner opens the update window\n\n### Session Diary fix\n- Burn history is persisted across app launches — the sparkline populates immediately on first open instead of requiring a manual refresh\n\n### Window sizing fixes\n- The popover now resizes correctly when switching between the main view and settings\n- Background colour no longer shows through during the resize animation"
|
"notes": "## What's new in v1.2.0\n\n### Bug fixes\n- App now works for all users — org ID is fetched dynamically from the API instead of being hardcoded\n- Plan name (e.g. Pro, Max) now updates correctly on every refresh\n\n### Improvements\n- Plan name is read from the API rather than hardcoded\n- Bootstrap API call consolidated — org ID, email, and plan name fetched in a single request per refresh\n- Main panel no longer scrolls — popover auto-sizes to fit content\n- Session Diary card redesigned — sample count and avg burn rate shown left/right above the sparkline, Claude header removed"
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user