Compare commits

..
50 Commits
Author SHA1 Message Date
superdooper86 02440c5340 Add fake beta version 3.1.0-beta for testing 2026-05-07 16:06:23 +02:00
superdooper86 6fe3e9722b v3.0.0 — built-by credit fix, version bump 2026-05-07 16:03:23 +02:00
superdooper86 f5114a31f8 README: add notification details, split update section, remove internal beta notes 2026-05-07 15:59:28 +02:00
superdooper86 4f260a3d4b v2.0.10 — limit notifications, fix positioning, add notification screenshots 2026-05-07 15:57:06 +02:00
superdooper86 3d1bae3d0a v2.0.9 — limit notifications using custom floating window (same style as update notifications) 2026-05-07 15:44:38 +02:00
superdooper86 b95102cb1b v2.0.9 — limit notifications at 80%/95% and on reset 2026-05-07 15:40:13 +02:00
superdooper86 2b48114996 Rename screenshots to bust CDN cache 2026-05-07 15:29:47 +02:00
superdooper86 6a4bb73c4f v2.0.8 — Fix data source endpoints in settings, update screenshots 2026-05-07 15:28:08 +02:00
superdooper86 d2815d65ce v2.0.7 — Show account email in settings auth section 2026-05-07 15:21:35 +02:00
superdooper86 a1c3c0b314 v2.0.6 — Auth section shows sign-in status, re-authenticate and sign out buttons 2026-05-07 15:17:11 +02:00
superdooper86 d3ec3f5dd7 revert: restore v2.0.5 after testing 2026-05-07 14:47:04 +02:00
superdooper86 5fee0af18b temp: bump fake version to 9.9.10 2026-05-07 14:37:39 +02:00
superdooper86 4edca2962c temp: fake update for testing 2026-05-07 14:33:51 +02:00
superdooper86 ef2cdd8bf3 v2.0.5 — Fixed window size on open, update banner now clears when up to date 2026-05-07 14:28:33 +02:00
superdooper86 2da3eeaf72 Improve screenshots layout with grid table 2026-05-07 14:24:36 +02:00
superdooper86 d229581de1 Add screenshots to README 2026-05-07 14:21:26 +02:00
superdooper86 169cfd8950 revert: restore v2.0.4 after screenshot 2026-05-07 14:17:37 +02:00
superdooper86 a5218fa44f temp: bump version for screenshot 2026-05-07 14:16:08 +02:00
superdooper86 1f7afab61c Update Data Sources section with new billing endpoints 2026-05-07 14:06:46 +02:00
superdooper86 1507c6490f v2.0.4 — Show spent, limit and balance from dedicated billing endpoints 2026-05-07 14:04:19 +02:00
superdooper86 ccf3468621 v2.0.3 — Clean up extra usage display, remove non-existent balance field 2026-05-07 13:56:31 +02:00
superdooper86 3199b228a8 v2.0.2 — Show spent, limit, and balance in extra usage section 2026-05-07 13:46:36 +02:00
superdooper86 255f3f6cd5 v2.0.2 — Fix unlimited monthly limit display in extra usage section 2026-05-07 13:44:05 +02:00
superdooper86 773fcb1a46 Bump to v2.0.1 2026-05-07 13:39:18 +02:00
superdooper86 464884e88f Fix crash when extra_usage fields are null 2026-05-07 13:36:23 +02:00
superdooper86 edd8d42c8d Bump CFBundleShortVersionString to 2.0 2026-05-07 13:33:16 +02:00
superdooper86 e123b48812 Bump to v2.0 2026-05-07 13:22:06 +02:00
superdooper86 603d405c87 Fix icon URL in README to raw GitHub path 2026-05-07 13:19:25 +02:00
superdooper86 f976a90875 Add Xcode project source for v1.6 2026-05-07 13:18:00 +02:00
SuperDooper c79bd41504 Bump version to 1.6 and update download URL 2026-05-07 12:53:37 +02:00
SuperDooper dfbd276616 Remove instructions from README
Removed instructions
2026-05-07 12:38:27 +02:00
SuperDooper dbf14bdd70 Update contributors in README.md 2026-05-07 12:37:08 +02:00
SuperDooper 6c894c32a4 Create README.md for ClaudeChecker app
Add README for ClaudeChecker macOS app with features and installation instructions.
2026-05-07 12:36:35 +02:00
SuperDooper 345150e894 Update version to 1.5 with notification fixes 2026-05-07 12:32:57 +02:00
SuperDooper d724622533 Downgrade version from 1.5 to 1.4 2026-05-07 12:21:37 +02:00
SuperDooper 8ecb5f3670 Bump version to 1.5 and update release notes 2026-05-07 12:17:08 +02:00
SuperDooper cf6f9184e3 Remove beta version information from JSON 2026-05-07 12:16:49 +02:00
SuperDooper d734fec836 Update version to 1.4 and change download URL 2026-05-07 12:08:18 +02:00
SuperDooper b6542e099a Update version to 1.3 and change download URL 2026-05-07 11:56:24 +02:00
SuperDooper c4e798b484 Update download URL in version.json 2026-05-07 11:47:15 +02:00
SuperDooper 0dd1ac3a8f Update version.json for release 1.2
Updated the version URL and release notes for version 1.2.
2026-05-07 11:45:28 +02:00
SuperDooper 2d8a3629da Rename version-beta to version-beta.json 2026-05-07 11:35:10 +02:00
SuperDooper e07af519a6 Rename version to version.json 2026-05-07 11:35:00 +02:00
SuperDooper 6b1f85dbaf Update version to 1.2 2026-05-07 11:31:12 +02:00
SuperDooper 74fe785898 Add version information for beta release 2026-05-07 11:26:21 +02:00
SuperDooper 3de4e09876 Create version-beta 2026-05-07 11:24:20 +02:00
SuperDooper 43dbb155f5 Delete version-beta.json 2026-05-07 11:24:08 +02:00
SuperDooper 79209eee6f Create version-beta.json 2026-05-07 11:23:50 +02:00
SuperDooper d9598a70ea Add version file with release information 2026-05-07 10:59:12 +02:00
SuperDooper c8cae1827f Initial commit 2026-05-07 10:58:23 +02:00
10 changed files with 58 additions and 229 deletions
-95
View File
@@ -1,95 +0,0 @@
name: Release
on:
push:
tags:
- 'v*'
jobs:
release:
runs-on: macos-latest
permissions:
contents: write
steps:
- name: Checkout
uses: actions/checkout@v4
with:
token: ${{ secrets.GITHUB_TOKEN }}
- name: Get version from tag
id: version
run: echo "version=${GITHUB_REF_NAME#v}" >> $GITHUB_OUTPUT
- name: Build
run: |
xcodebuild \
-project ClaudeChecker.xcodeproj \
-scheme ClaudeChecker \
-configuration Release \
-derivedDataPath build \
clean build
- name: Zip app
run: |
ditto -c -k --keepParent \
build/Build/Products/Release/ClaudeChecker.app \
ClaudeChecker.zip
- name: Compute SHA256
id: sha
run: echo "sha256=$(shasum -a 256 ClaudeChecker.zip | awk '{print $1}')" >> $GITHUB_OUTPUT
- name: Create GitHub release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh release create "$GITHUB_REF_NAME" ClaudeChecker.zip \
--title "$GITHUB_REF_NAME" \
--notes "$(cat RELEASE_NOTES.md 2>/dev/null || echo 'Release ${{ steps.version.outputs.version }}')"
- name: Update version.json
run: |
notes=$(cat RELEASE_NOTES.md 2>/dev/null || echo "Release ${{ steps.version.outputs.version }}")
jq -n \
--arg version "${{ steps.version.outputs.version }}" \
--arg url "https://github.com/${{ github.repository }}/releases/download/${{ github.ref_name }}/ClaudeChecker.zip" \
--arg notes "$notes" \
'{version: $version, url: $url, notes: $notes}' > version.json
- name: Update Homebrew cask
run: |
sed -i '' \
-e 's/version "[^"]*"/version "${{ steps.version.outputs.version }}"/' \
-e 's/sha256 "[^"]*"/sha256 "${{ steps.sha.outputs.sha256 }}"/' \
../homebrew-tap/Casks/claudechecker.rb 2>/dev/null || true
- name: Update README badge
run: |
sed -i '' \
's/version-[0-9][^-]*-orange/version-${{ steps.version.outputs.version }}-orange/' \
README.md
- name: Commit version.json and README
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add version.json README.md
git commit -m "Release ${{ github.ref_name }}"
git push origin HEAD:main
- name: Update Homebrew tap
env:
TAP_TOKEN: ${{ secrets.TAP_TOKEN }}
run: |
git clone https://x-access-token:${{ secrets.TAP_TOKEN }}@github.com/${{ github.repository_owner }}/homebrew-tap /tmp/homebrew-tap
sed -i '' \
-e 's/version "[^"]*"/version "${{ steps.version.outputs.version }}"/' \
-e 's/sha256 "[^"]*"/sha256 "${{ steps.sha.outputs.sha256 }}"/' \
/tmp/homebrew-tap/Casks/claudechecker.rb
cd /tmp/homebrew-tap
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add Casks/claudechecker.rb
git commit -m "Update claudechecker to ${{ github.ref_name }}"
git push
+8 -45
View File
@@ -94,10 +94,6 @@ struct ContentView: View {
}
.frame(width: 480)
.animation(.easeInOut(duration: 0.2), value: showSettings)
.onReceive(NotificationCenter.default.publisher(for: .openUpdateSheet)) { _ in
showSettings = false
showUpdateSheet = true
}
.sheet(isPresented: $showLogin) {
LoginSheetView(isPresented: $showLogin) {
Task { await vm.refresh() }
@@ -778,16 +774,14 @@ struct SettingsView: View {
.padding(.vertical, 2)
.background(Color.blue)
.cornerRadius(4)
.fixedSize()
} else if updater.betaAvailable {
Text("v\(updater.latestBetaVersion) available")
Text("v\(updater.latestBetaVersion) beta")
.font(.system(size: 10, weight: .medium))
.foregroundColor(.white)
.padding(.horizontal, 6)
.padding(.vertical, 2)
.background(Color.orange)
.cornerRadius(4)
.fixedSize()
}
}
Text("Built by superdooper86 & claude")
@@ -977,36 +971,6 @@ struct UpdateSuccessBanner: View {
}
}
// MARK: - Markdown notes renderer
struct ReleaseNotesText: View {
let text: String
var font: Font = .system(size: 12)
var body: some View {
let processed = text
.split(separator: "\n", omittingEmptySubsequences: false)
.map { line -> String in
let s = String(line)
if s.hasPrefix("### ") { return "**\(s.dropFirst(4))**" }
if s.hasPrefix("## ") { return "**\(s.dropFirst(3))**" }
if s.hasPrefix("# ") { return "**\(s.dropFirst(2))**" }
if s.hasPrefix("- ") { return "\(s.dropFirst(2))" }
return s
}
.joined(separator: "\n")
if let attr = try? AttributedString(markdown: processed,
options: .init(interpretedSyntax: .inlineOnlyPreservingWhitespace)) {
Text(attr).font(font).foregroundColor(.secondary)
.fixedSize(horizontal: false, vertical: true)
} else {
Text(text).font(font).foregroundColor(.secondary)
.fixedSize(horizontal: false, vertical: true)
}
}
}
// MARK: - Update Banner
struct UpdateBanner: View {
@@ -1022,19 +986,16 @@ struct UpdateBanner: View {
Text("Update available — v\(updater.latestVersion)")
.font(.system(size: 12, weight: .semibold))
if !updater.releaseNotes.isEmpty {
ReleaseNotesText(text: updater.releaseNotes, font: .system(size: 11))
Text(updater.releaseNotes)
.font(.system(size: 11))
.foregroundColor(.secondary)
.fixedSize(horizontal: false, vertical: true)
}
}
Spacer()
Button("Update") { showSheet = true }
.buttonStyle(.borderedProminent)
.controlSize(.small)
Button { updater.updateAvailable = false } label: {
Image(systemName: "xmark")
.font(.system(size: 10, weight: .medium))
.foregroundColor(.secondary)
}
.buttonStyle(.plain)
}
.padding(.horizontal, 16)
.padding(.vertical, 10)
@@ -1088,7 +1049,9 @@ struct UpdateSheet: View {
.foregroundColor(.blue)
}
if !updater.releaseNotes.isEmpty {
ReleaseNotesText(text: updater.releaseNotes)
Text(updater.releaseNotes)
.font(.system(size: 12))
.foregroundColor(.secondary)
}
}
Spacer()
+2 -2
View File
@@ -15,9 +15,9 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0.9</string>
<string>3.0.0</string>
<key>CFBundleVersion</key>
<string>28</string>
<string>18</string>
<key>LSMinimumSystemVersion</key>
<string>13.0</string>
<key>LSUIElement</key>
+3 -15
View File
@@ -46,8 +46,6 @@ class UpdateManager: ObservableObject {
@Published var updateComplete = false
@Published var justUpdated = false
private var notifiedVersion: String = ""
@Published var betaChannel: Bool = false {
didSet {
UserDefaults.standard.set(betaChannel, forKey: "beta_channel")
@@ -72,13 +70,14 @@ class UpdateManager: ObservableObject {
if let stable = await stableResult {
print("[UpdateManager] Stable remote=\(stable.version), isNewer=\(isNewer(stable.version, than: currentVersion))")
if isNewer(stable.version, than: currentVersion) {
let wasAvailable = updateAvailable
latestVersion = stable.version
releaseNotes = stable.notes ?? ""
downloadURL = stable.url
updateAvailable = true
print("[UpdateManager] updateAvailable set to true")
if notifiedVersion != stable.version {
notifiedVersion = stable.version
// Only notify once when first detected
if !wasAvailable {
NotificationCenter.default.post(name: .updateDetected, object: stable.version)
}
} else {
@@ -87,8 +86,6 @@ class UpdateManager: ObservableObject {
}
} else {
print("[UpdateManager] Stable fetch returned nil")
updateAvailable = false
latestVersion = ""
}
if let beta = await betaResult {
@@ -101,19 +98,10 @@ class UpdateManager: ObservableObject {
releaseNotes = beta.notes ?? ""
downloadURL = beta.url
updateAvailable = true
if notifiedVersion != beta.version {
notifiedVersion = beta.version
NotificationCenter.default.post(name: .updateDetected, object: beta.version)
}
}
} else {
betaAvailable = false
latestBetaVersion = ""
}
} else {
print("[UpdateManager] Beta fetch returned nil")
betaAvailable = false
latestBetaVersion = ""
}
}
+28 -51
View File
@@ -14,7 +14,7 @@ class UpdateNotificationWindowController: NSWindowController {
// Called when a new update is detected while running
static func showUpdate(version: String, notes: String, near statusItem: NSStatusItem?) {
showWindow(version: version, subtitle: "Tap to view release notes & update now!", near: statusItem, isUpdate: true)
showWindow(version: version, subtitle: notes.isEmpty ? "Tap to update now." : notes, near: statusItem, isUpdate: true)
}
static func showLimitWarning(windowName: String, percent: Int, near statusItem: NSStatusItem?) {
@@ -39,12 +39,7 @@ class UpdateNotificationWindowController: NSWindowController {
sharedLimit?.close()
let content = UpdateNotificationView(
version: "", subtitle: subtitle, isUpdate: false,
titleText: title, badgeIcon: badgeIcon, badgeColor: badgeColor,
onTap: {
sharedLimit?.close()
sharedLimit = nil
NotificationCenter.default.post(name: .showPopover, object: nil)
}
titleText: title, badgeIcon: badgeIcon, badgeColor: badgeColor
) {
sharedLimit?.close()
sharedLimit = nil
@@ -58,16 +53,7 @@ class UpdateNotificationWindowController: NSWindowController {
private static func showWindow(version: String, subtitle: String, near statusItem: NSStatusItem?, isUpdate: Bool) {
shared?.close()
let content = UpdateNotificationView(version: version, subtitle: subtitle, isUpdate: isUpdate,
onTap: {
shared?.close()
shared = nil
NotificationCenter.default.post(name: .showPopover, object: nil)
if isUpdate {
NotificationCenter.default.post(name: .openUpdateSheet, object: nil)
}
}
) {
let content = UpdateNotificationView(version: version, subtitle: subtitle, isUpdate: isUpdate) {
shared?.close()
shared = nil
}
@@ -143,7 +129,6 @@ struct UpdateNotificationView: View {
var titleText: String? = nil
var badgeIcon: String? = nil
var badgeColor: Color? = nil
var onTap: (() -> Void)? = nil
let onDismiss: () -> Void
private var effectiveTitle: String {
@@ -157,40 +142,30 @@ struct UpdateNotificationView: View {
}
var body: some View {
ZStack(alignment: .topTrailing) {
Button(action: { onTap?() }) {
HStack(spacing: 12) {
ZStack(alignment: .bottomTrailing) {
Image(nsImage: NSImage(named: "AppIcon") ?? NSImage())
.resizable()
.frame(width: 44, height: 44)
.cornerRadius(10)
HStack(spacing: 12) {
ZStack(alignment: .bottomTrailing) {
Image(nsImage: NSImage(named: "AppIcon") ?? NSImage())
.resizable()
.frame(width: 44, height: 44)
.cornerRadius(10)
Image(systemName: effectiveBadgeIcon)
.font(.system(size: 16, weight: .semibold))
.foregroundColor(effectiveBadgeColor)
.background(Color(nsColor: .windowBackgroundColor).clipShape(Circle()))
.offset(x: 4, y: 4)
}
VStack(alignment: .leading, spacing: 3) {
Text(effectiveTitle)
.font(.system(size: 13, weight: .semibold))
.foregroundColor(.primary)
Text(subtitle)
.font(.system(size: 11))
.foregroundColor(.secondary)
.fixedSize(horizontal: false, vertical: true)
}
Spacer(minLength: 24)
}
.padding(.horizontal, 14)
.padding(.vertical, 14)
.frame(width: 340, alignment: .leading)
.contentShape(Rectangle())
Image(systemName: effectiveBadgeIcon)
.font(.system(size: 16, weight: .semibold))
.foregroundColor(effectiveBadgeColor)
.background(Color(nsColor: .windowBackgroundColor).clipShape(Circle()))
.offset(x: 4, y: 4)
}
.buttonStyle(.plain)
VStack(alignment: .leading, spacing: 3) {
Text(effectiveTitle)
.font(.system(size: 13, weight: .semibold))
Text(subtitle)
.font(.system(size: 11))
.foregroundColor(.secondary)
.fixedSize(horizontal: false, vertical: true)
}
Spacer(minLength: 8)
Button(action: onDismiss) {
Image(systemName: "xmark")
@@ -198,8 +173,10 @@ struct UpdateNotificationView: View {
.foregroundColor(.secondary)
}
.buttonStyle(.plain)
.padding(14)
}
.padding(.horizontal, 14)
.padding(.vertical, 14)
.frame(width: 340, alignment: .leading)
.background(
RoundedRectangle(cornerRadius: 13)
.fill(Color(nsColor: .windowBackgroundColor))
-1
View File
@@ -310,5 +310,4 @@ extension Notification.Name {
static let updateDetected = Notification.Name("updateDetected")
static let limitWarning = Notification.Name("limitWarning")
static let limitReset = Notification.Name("limitReset")
static let openUpdateSheet = Notification.Name("openUpdateSheet")
}
+9 -13
View File
@@ -8,7 +8,7 @@
[![macOS](https://img.shields.io/badge/macOS-13.0+-000000?style=flat&logo=apple&logoColor=white)](https://www.apple.com/macos/)
[![Swift](https://img.shields.io/badge/Swift-5.9-F05138?style=flat&logo=swift&logoColor=white)](https://swift.org)
[![Version](https://img.shields.io/badge/version-1.0.8-orange?style=flat)](https://github.com/superdooper86/claudechecker/releases)
[![Version](https://img.shields.io/badge/version-3.0.0-orange?style=flat)](https://github.com/superdooper86/claudechecker/releases)
[![License](https://img.shields.io/badge/license-MIT-blue?style=flat)](LICENSE)
</div>
@@ -113,29 +113,25 @@ No API key required. It reads directly from your `claude.ai` session using a bui
## Requirements
- macOS 13 Ventura or later
- Xcode 15+ (to build from source)
- A claude.ai account
---
## Installation
### Homebrew (easiest)
```bash
brew tap superdooper86/tap
brew install --cask claudechecker
```
### Download
### Download (recommended)
1. Go to [Releases](https://github.com/superdooper86/claudechecker/releases)
2. Download `ClaudeChecker.zip` from the latest release
3. Unzip and drag `ClaudeChecker.app` to `/Applications`
4. Open from Applications — it will appear in your menubar
> **macOS security prompt?** If macOS blocks the app on first launch, run this in Terminal:
> ```bash
> xattr -cr /Applications/ClaudeChecker.app
> ```
> This is not needed when installing via Homebrew.
### Build from source
```bash
git clone https://github.com/superdooper86/claudechecker.git
open ClaudeChecker/ClaudeChecker.xcodeproj
```
Then in Xcode: set your Team under Signing & Capabilities, then **⌘R** to run.
---
-4
View File
@@ -1,4 +0,0 @@
## What's new in v1.0.9
### Fixed update notification popup
The notification popup now correctly shows **"Tap to view release notes & update now!"** instead of the raw markdown release notes text.
+5
View File
@@ -0,0 +1,5 @@
{
"version": "3.1.0-beta",
"url": "https://github.com/superdooper86/claudechecker/releases/download/v3.0.0/ClaudeChecker.zip",
"notes": "Beta test build."
}
+3 -3
View File
@@ -1,5 +1,5 @@
{
"version": "1.0.8",
"url": "https://github.com/superdooper86/claudechecker/releases/download/v1.0.8/ClaudeChecker.zip",
"notes": "## What's new in v1.0.8\n\n### Release notes now render correctly\nFixed markdown rendering in the update banner and update sheet — headers, **bold text**, and bullet points now display properly instead of showing raw `##` and `**` characters."
"version": "3.0.0",
"url": "https://github.com/superdooper86/claudechecker/releases/download/v3.0.0/ClaudeChecker.zip",
"notes": "Limit notifications for 80% and 95% usage, and on quota reset."
}