Compare commits

..
30 Commits
Author SHA1 Message Date
superdooper86 b16c24cb44 Release v1.0.11 — update indicator in menubar, undismissable banner
- Menubar shows ↑ Update alongside limits when an update is available
- Removed × dismiss button from update banner; it stays until installed
2026-05-07 18:23:26 +02:00
superdooper86 89a79c6f13 Temporary: bump version.json to 1.1.1 for update testing 2026-05-07 18:14:08 +02:00
github-actions[bot] aa529835f7 Release v1.0.10 2026-05-07 16:13:13 +00:00
superdooper86 b5c3cc6379 Rerelease as v1.0.10 (was incorrectly versioned as v1.1.0) 2026-05-07 18:11:45 +02:00
github-actions[bot] c8adaf1baf Release v1.1.0 2026-05-07 16:02:51 +00:00
superdooper86 3e99e1417e Release v1.1.0 — always open to main window
Post popoverWillOpen notification on show; ContentView resets showSettings
on receive so reopening always lands on the main window, not Settings.
2026-05-07 18:02:04 +02:00
github-actions[bot] e41d3d0c0f Release v1.0.9 2026-05-07 15:56:24 +00:00
superdooper86 1078951c43 Release v1.0.9 — fix update notification popup showing raw markdown
Sync UpdateNotificationWindow.swift: showUpdate now always uses the
static subtitle "Tap to view release notes & update now!" instead of
passing the full release notes text to the popup.
2026-05-07 17:55:05 +02:00
github-actions[bot] ed6a5e1b51 Release v1.0.8 2026-05-07 15:49:47 +00:00
superdooper86 8701fb1326 Release v1.0.8 2026-05-07 17:48:32 +02:00
github-actions[bot] 247b5fc202 Release v1.0.7 2026-05-07 15:45:02 +00:00
superdooper86 255de797f8 Release v1.0.7 — fix markdown rendering in release notes
Add ReleaseNotesText view that pre-processes headers (##, ###) to bold
and converts - bullets to • before passing to AttributedString, which
only supports inline markdown. Replaces both Text(.init()) call sites.
2026-05-07 17:43:46 +02:00
github-actions[bot] 0046c53283 Release v1.0.6 2026-05-07 15:39:24 +00:00
superdooper86 8d222d63f0 Release v1.0.6 — improve update notification subtitle
Change notification prompt to "Tap to view release notes & update now!"
2026-05-07 17:38:34 +02:00
github-actions[bot] 5720370649 Release v1.0.5 2026-05-07 15:31:00 +00:00
superdooper86 0843730ae1 Release v1.0.5 — fix markdown rendering in release notes
Use Text(.init()) to parse markdown in update sheet and banner.
Notification popup now shows a fixed subtitle instead of raw notes.
2026-05-07 17:29:29 +02:00
superdooper86 9c030fcdd5 Fix version.json containing invalid JSON (literal newlines in notes)
Use jq to build version.json so multiline release notes are properly
escaped as \n sequences. Also fix current version.json for v1.0.4.
2026-05-07 17:22:57 +02:00
github-actions[bot] dd68ff39d6 Release v1.0.4 2026-05-07 15:16:04 +00:00
superdooper86 b76bdf3bc1 Release v1.0.4 — clickable notifications
Notifications are now interactive: limit warnings and resets open the
main window on tap; update notifications open the update sheet directly.

Bump version to 1.0.4 (build 23).
2026-05-07 17:14:29 +02:00
github-actions[bot] c31950fd76 Release v1.0.3 2026-05-07 15:09:05 +00:00
superdooper86 4e6ae21825 Bump version to 1.0.3 2026-05-07 17:08:12 +02:00
superdooper86 c0bf2e4873 Clickable notifications — limit opens popover, update opens update sheet 2026-05-07 17:07:17 +02:00
github-actions[bot] 081d4804ab Release v1.0.2 2026-05-07 15:02:07 +00:00
superdooper86 be3899e188 Bump version to 1.0.2 2026-05-07 17:01:02 +02:00
superdooper86 286761e51a Use TAP_TOKEN for Homebrew tap updates 2026-05-07 16:59:21 +02:00
superdooper86 8e0d66468c Add automated release workflow 2026-05-07 16:54:49 +02:00
superdooper86 ed64bcc9e5 Add xattr workaround note for Gatekeeper on manual install 2026-05-07 16:50:03 +02:00
superdooper86 b2275a74d3 Remove build from source section and Xcode requirement 2026-05-07 16:47:53 +02:00
superdooper86 3b05188a88 Add Homebrew install method as primary option 2026-05-07 16:47:01 +02:00
superdooper86 ea77454c10 ClaudeChecker v1.0.1 2026-05-07 16:42:46 +02:00
11 changed files with 244 additions and 59 deletions
+95
View File
@@ -0,0 +1,95 @@
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
+14 -1
View File
@@ -60,6 +60,11 @@ class AppDelegate: NSObject, NSApplicationDelegate {
.sink { [weak self] _ in self?.updateStatusItem() } .sink { [weak self] _ in self?.updateStatusItem() }
.store(in: &cancellables) .store(in: &cancellables)
updateManager.$updateAvailable
.receive(on: DispatchQueue.main)
.sink { [weak self] _ in self?.updateStatusItem() }
.store(in: &cancellables)
// Refresh timer // Refresh timer
scheduleTimer(interval: usageViewModel.refreshInterval) scheduleTimer(interval: usageViewModel.refreshInterval)
@@ -166,7 +171,13 @@ class AppDelegate: NSObject, NSApplicationDelegate {
let sdStr = sd.map { "\(Int($0.usedPercent.rounded()))%" } ?? "" let sdStr = sd.map { "\(Int($0.usedPercent.rounded()))%" } ?? ""
button.image = nil button.image = nil
button.attributedTitle = NSAttributedString(string: "") button.attributedTitle = NSAttributedString(string: "")
button.title = "\(fhStr) \(sdStr)" let updatePrefix = updateManager.updateAvailable ? "↑ Update " : ""
button.title = "\(updatePrefix)\(fhStr) \(sdStr)"
button.font = NSFont.monospacedDigitSystemFont(ofSize: 12, weight: .medium)
} else if updateManager.updateAvailable {
button.image = nil
button.attributedTitle = NSAttributedString(string: "")
button.title = "↑ Update"
button.font = NSFont.monospacedDigitSystemFont(ofSize: 12, weight: .medium) button.font = NSFont.monospacedDigitSystemFont(ofSize: 12, weight: .medium)
} else { } else {
button.title = "" button.title = ""
@@ -199,6 +210,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
@objc func showPopover() { @objc func showPopover() {
guard let button = statusItem?.button, let popover else { return } guard let button = statusItem?.button, let popover else { return }
if !popover.isShown { if !popover.isShown {
NotificationCenter.default.post(name: .popoverWillOpen, object: nil)
popover.show(relativeTo: button.bounds, of: button, preferredEdge: .minY) popover.show(relativeTo: button.bounds, of: button, preferredEdge: .minY)
NSApp.activate(ignoringOtherApps: true) NSApp.activate(ignoringOtherApps: true)
} }
@@ -209,6 +221,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
if popover.isShown { if popover.isShown {
popover.performClose(nil) popover.performClose(nil)
} else { } else {
NotificationCenter.default.post(name: .popoverWillOpen, object: nil)
popover.show(relativeTo: button.bounds, of: button, preferredEdge: .minY) popover.show(relativeTo: button.bounds, of: button, preferredEdge: .minY)
NSApp.activate(ignoringOtherApps: true) NSApp.activate(ignoringOtherApps: true)
} }
+42 -8
View File
@@ -94,6 +94,13 @@ struct ContentView: View {
} }
.frame(width: 480) .frame(width: 480)
.animation(.easeInOut(duration: 0.2), value: showSettings) .animation(.easeInOut(duration: 0.2), value: showSettings)
.onReceive(NotificationCenter.default.publisher(for: .popoverWillOpen)) { _ in
showSettings = false
}
.onReceive(NotificationCenter.default.publisher(for: .openUpdateSheet)) { _ in
showSettings = false
showUpdateSheet = true
}
.sheet(isPresented: $showLogin) { .sheet(isPresented: $showLogin) {
LoginSheetView(isPresented: $showLogin) { LoginSheetView(isPresented: $showLogin) {
Task { await vm.refresh() } Task { await vm.refresh() }
@@ -774,14 +781,16 @@ struct SettingsView: View {
.padding(.vertical, 2) .padding(.vertical, 2)
.background(Color.blue) .background(Color.blue)
.cornerRadius(4) .cornerRadius(4)
.fixedSize()
} else if updater.betaAvailable { } else if updater.betaAvailable {
Text("v\(updater.latestBetaVersion) beta") Text("v\(updater.latestBetaVersion) available")
.font(.system(size: 10, weight: .medium)) .font(.system(size: 10, weight: .medium))
.foregroundColor(.white) .foregroundColor(.white)
.padding(.horizontal, 6) .padding(.horizontal, 6)
.padding(.vertical, 2) .padding(.vertical, 2)
.background(Color.orange) .background(Color.orange)
.cornerRadius(4) .cornerRadius(4)
.fixedSize()
} }
} }
Text("Built by superdooper86 & claude") Text("Built by superdooper86 & claude")
@@ -971,6 +980,36 @@ 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 // MARK: - Update Banner
struct UpdateBanner: View { struct UpdateBanner: View {
@@ -986,10 +1025,7 @@ struct UpdateBanner: View {
Text("Update available — v\(updater.latestVersion)") Text("Update available — v\(updater.latestVersion)")
.font(.system(size: 12, weight: .semibold)) .font(.system(size: 12, weight: .semibold))
if !updater.releaseNotes.isEmpty { if !updater.releaseNotes.isEmpty {
Text(updater.releaseNotes) ReleaseNotesText(text: updater.releaseNotes, font: .system(size: 11))
.font(.system(size: 11))
.foregroundColor(.secondary)
.fixedSize(horizontal: false, vertical: true)
} }
} }
Spacer() Spacer()
@@ -1049,9 +1085,7 @@ struct UpdateSheet: View {
.foregroundColor(.blue) .foregroundColor(.blue)
} }
if !updater.releaseNotes.isEmpty { if !updater.releaseNotes.isEmpty {
Text(updater.releaseNotes) ReleaseNotesText(text: updater.releaseNotes)
.font(.system(size: 12))
.foregroundColor(.secondary)
} }
} }
Spacer() Spacer()
+2 -2
View File
@@ -15,9 +15,9 @@
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
<string>APPL</string> <string>APPL</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>3.0.0</string> <string>1.0.11</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>18</string> <string>31</string>
<key>LSMinimumSystemVersion</key> <key>LSMinimumSystemVersion</key>
<string>13.0</string> <string>13.0</string>
<key>LSUIElement</key> <key>LSUIElement</key>
+15 -3
View File
@@ -46,6 +46,8 @@ class UpdateManager: ObservableObject {
@Published var updateComplete = false @Published var updateComplete = false
@Published var justUpdated = false @Published var justUpdated = false
private var notifiedVersion: String = ""
@Published var betaChannel: Bool = false { @Published var betaChannel: Bool = false {
didSet { didSet {
UserDefaults.standard.set(betaChannel, forKey: "beta_channel") UserDefaults.standard.set(betaChannel, forKey: "beta_channel")
@@ -70,14 +72,13 @@ class UpdateManager: ObservableObject {
if let stable = await stableResult { if let stable = await stableResult {
print("[UpdateManager] Stable remote=\(stable.version), isNewer=\(isNewer(stable.version, than: currentVersion))") print("[UpdateManager] Stable remote=\(stable.version), isNewer=\(isNewer(stable.version, than: currentVersion))")
if isNewer(stable.version, than: currentVersion) { if isNewer(stable.version, than: currentVersion) {
let wasAvailable = updateAvailable
latestVersion = stable.version latestVersion = stable.version
releaseNotes = stable.notes ?? "" releaseNotes = stable.notes ?? ""
downloadURL = stable.url downloadURL = stable.url
updateAvailable = true updateAvailable = true
print("[UpdateManager] updateAvailable set to true") print("[UpdateManager] updateAvailable set to true")
// Only notify once when first detected if notifiedVersion != stable.version {
if !wasAvailable { notifiedVersion = stable.version
NotificationCenter.default.post(name: .updateDetected, object: stable.version) NotificationCenter.default.post(name: .updateDetected, object: stable.version)
} }
} else { } else {
@@ -86,6 +87,8 @@ class UpdateManager: ObservableObject {
} }
} else { } else {
print("[UpdateManager] Stable fetch returned nil") print("[UpdateManager] Stable fetch returned nil")
updateAvailable = false
latestVersion = ""
} }
if let beta = await betaResult { if let beta = await betaResult {
@@ -98,10 +101,19 @@ class UpdateManager: ObservableObject {
releaseNotes = beta.notes ?? "" releaseNotes = beta.notes ?? ""
downloadURL = beta.url downloadURL = beta.url
updateAvailable = true updateAvailable = true
if notifiedVersion != beta.version {
notifiedVersion = beta.version
NotificationCenter.default.post(name: .updateDetected, object: beta.version)
}
} }
} else {
betaAvailable = false
latestBetaVersion = ""
} }
} else { } else {
print("[UpdateManager] Beta fetch returned nil") print("[UpdateManager] Beta fetch returned nil")
betaAvailable = false
latestBetaVersion = ""
} }
} }
+51 -28
View File
@@ -14,7 +14,7 @@ class UpdateNotificationWindowController: NSWindowController {
// Called when a new update is detected while running // Called when a new update is detected while running
static func showUpdate(version: String, notes: String, near statusItem: NSStatusItem?) { static func showUpdate(version: String, notes: String, near statusItem: NSStatusItem?) {
showWindow(version: version, subtitle: notes.isEmpty ? "Tap to update now." : notes, near: statusItem, isUpdate: true) showWindow(version: version, subtitle: "Tap to view release notes & update now!", near: statusItem, isUpdate: true)
} }
static func showLimitWarning(windowName: String, percent: Int, near statusItem: NSStatusItem?) { static func showLimitWarning(windowName: String, percent: Int, near statusItem: NSStatusItem?) {
@@ -39,7 +39,12 @@ class UpdateNotificationWindowController: NSWindowController {
sharedLimit?.close() sharedLimit?.close()
let content = UpdateNotificationView( let content = UpdateNotificationView(
version: "", subtitle: subtitle, isUpdate: false, version: "", subtitle: subtitle, isUpdate: false,
titleText: title, badgeIcon: badgeIcon, badgeColor: badgeColor titleText: title, badgeIcon: badgeIcon, badgeColor: badgeColor,
onTap: {
sharedLimit?.close()
sharedLimit = nil
NotificationCenter.default.post(name: .showPopover, object: nil)
}
) { ) {
sharedLimit?.close() sharedLimit?.close()
sharedLimit = nil sharedLimit = nil
@@ -53,7 +58,16 @@ class UpdateNotificationWindowController: NSWindowController {
private static func showWindow(version: String, subtitle: String, near statusItem: NSStatusItem?, isUpdate: Bool) { private static func showWindow(version: String, subtitle: String, near statusItem: NSStatusItem?, isUpdate: Bool) {
shared?.close() shared?.close()
let content = UpdateNotificationView(version: version, subtitle: subtitle, isUpdate: isUpdate) { 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)
}
}
) {
shared?.close() shared?.close()
shared = nil shared = nil
} }
@@ -129,6 +143,7 @@ struct UpdateNotificationView: View {
var titleText: String? = nil var titleText: String? = nil
var badgeIcon: String? = nil var badgeIcon: String? = nil
var badgeColor: Color? = nil var badgeColor: Color? = nil
var onTap: (() -> Void)? = nil
let onDismiss: () -> Void let onDismiss: () -> Void
private var effectiveTitle: String { private var effectiveTitle: String {
@@ -142,30 +157,40 @@ struct UpdateNotificationView: View {
} }
var body: some View { var body: some View {
HStack(spacing: 12) { ZStack(alignment: .topTrailing) {
ZStack(alignment: .bottomTrailing) { Button(action: { onTap?() }) {
Image(nsImage: NSImage(named: "AppIcon") ?? NSImage()) HStack(spacing: 12) {
.resizable() ZStack(alignment: .bottomTrailing) {
.frame(width: 44, height: 44) Image(nsImage: NSImage(named: "AppIcon") ?? NSImage())
.cornerRadius(10) .resizable()
.frame(width: 44, height: 44)
.cornerRadius(10)
Image(systemName: effectiveBadgeIcon) Image(systemName: effectiveBadgeIcon)
.font(.system(size: 16, weight: .semibold)) .font(.system(size: 16, weight: .semibold))
.foregroundColor(effectiveBadgeColor) .foregroundColor(effectiveBadgeColor)
.background(Color(nsColor: .windowBackgroundColor).clipShape(Circle())) .background(Color(nsColor: .windowBackgroundColor).clipShape(Circle()))
.offset(x: 4, y: 4) .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())
} }
.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) { Button(action: onDismiss) {
Image(systemName: "xmark") Image(systemName: "xmark")
@@ -173,10 +198,8 @@ struct UpdateNotificationView: View {
.foregroundColor(.secondary) .foregroundColor(.secondary)
} }
.buttonStyle(.plain) .buttonStyle(.plain)
.padding(14)
} }
.padding(.horizontal, 14)
.padding(.vertical, 14)
.frame(width: 340, alignment: .leading)
.background( .background(
RoundedRectangle(cornerRadius: 13) RoundedRectangle(cornerRadius: 13)
.fill(Color(nsColor: .windowBackgroundColor)) .fill(Color(nsColor: .windowBackgroundColor))
+2
View File
@@ -307,7 +307,9 @@ extension Notification.Name {
static let refreshIntervalChanged = Notification.Name("refreshIntervalChanged") static let refreshIntervalChanged = Notification.Name("refreshIntervalChanged")
static let closePopover = Notification.Name("closePopover") static let closePopover = Notification.Name("closePopover")
static let showPopover = Notification.Name("showPopover") static let showPopover = Notification.Name("showPopover")
static let popoverWillOpen = Notification.Name("popoverWillOpen")
static let updateDetected = Notification.Name("updateDetected") static let updateDetected = Notification.Name("updateDetected")
static let limitWarning = Notification.Name("limitWarning") static let limitWarning = Notification.Name("limitWarning")
static let limitReset = Notification.Name("limitReset") static let limitReset = Notification.Name("limitReset")
static let openUpdateSheet = Notification.Name("openUpdateSheet")
} }
+13 -9
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/) [![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) [![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-3.0.0-orange?style=flat)](https://github.com/superdooper86/claudechecker/releases) [![Version](https://img.shields.io/badge/version-1.0.10-orange?style=flat)](https://github.com/superdooper86/claudechecker/releases)
[![License](https://img.shields.io/badge/license-MIT-blue?style=flat)](LICENSE) [![License](https://img.shields.io/badge/license-MIT-blue?style=flat)](LICENSE)
</div> </div>
@@ -113,25 +113,29 @@ No API key required. It reads directly from your `claude.ai` session using a bui
## Requirements ## Requirements
- macOS 13 Ventura or later - macOS 13 Ventura or later
- Xcode 15+ (to build from source)
- A claude.ai account - A claude.ai account
--- ---
## Installation ## Installation
### Download (recommended) ### Homebrew (easiest)
```bash
brew tap superdooper86/tap
brew install --cask claudechecker
```
### Download
1. Go to [Releases](https://github.com/superdooper86/claudechecker/releases) 1. Go to [Releases](https://github.com/superdooper86/claudechecker/releases)
2. Download `ClaudeChecker.zip` from the latest release 2. Download `ClaudeChecker.zip` from the latest release
3. Unzip and drag `ClaudeChecker.app` to `/Applications` 3. Unzip and drag `ClaudeChecker.app` to `/Applications`
4. Open from Applications — it will appear in your menubar 4. Open from Applications — it will appear in your menubar
### Build from source > **macOS security prompt?** If macOS blocks the app on first launch, run this in Terminal:
```bash > ```bash
git clone https://github.com/superdooper86/claudechecker.git > xattr -cr /Applications/ClaudeChecker.app
open ClaudeChecker/ClaudeChecker.xcodeproj > ```
``` > This is not needed when installing via Homebrew.
Then in Xcode: set your Team under Signing & Capabilities, then **⌘R** to run.
--- ---
+7
View File
@@ -0,0 +1,7 @@
## What's new in v1.0.11
### Update indicator in menubar
When an update is available, the menubar now shows **↑ Update** alongside your usage percentages — so you can see there's an update without opening the app.
### Update banner can't be dismissed
The × close button has been removed from the update banner. The banner stays visible until you install the update.
-5
View File
@@ -1,5 +0,0 @@
{
"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": "3.0.0", "version": "1.1.1",
"url": "https://github.com/superdooper86/claudechecker/releases/download/v3.0.0/ClaudeChecker.zip", "url": "https://github.com/superdooper86/claudechecker/releases/download/v1.0.10/ClaudeChecker.zip",
"notes": "Limit notifications for 80% and 95% usage, and on quota reset." "notes": "## What's new in v1.0.10\\n\\n### Main window always opens on click\\nClosing the app while Settings was open would cause the next click to reopen straight to Settings. The app now always returns to the main window when reopened."
} }