Compare commits

..
12 Commits
Author SHA1 Message Date
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
9 changed files with 186 additions and 41 deletions
+96
View File
@@ -0,0 +1,96 @@
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: |
cat > version.json << JSON
{
"version": "${{ steps.version.outputs.version }}",
"url": "https://github.com/${{ github.repository }}/releases/download/${{ github.ref_name }}/ClaudeChecker.zip",
"notes": "$(cat RELEASE_NOTES.md 2>/dev/null || echo 'Release ${{ steps.version.outputs.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
+10
View File
@@ -94,6 +94,10 @@ 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: .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() }
@@ -998,6 +1002,12 @@ struct UpdateBanner: View {
Button("Update") { showSheet = true } Button("Update") { showSheet = true }
.buttonStyle(.borderedProminent) .buttonStyle(.borderedProminent)
.controlSize(.small) .controlSize(.small)
Button { updater.updateAvailable = false } label: {
Image(systemName: "xmark")
.font(.system(size: 10, weight: .medium))
.foregroundColor(.secondary)
}
.buttonStyle(.plain)
} }
.padding(.horizontal, 16) .padding(.horizontal, 16)
.padding(.vertical, 10) .padding(.vertical, 10)
+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>1.0.0</string> <string>1.0.4</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>19</string> <string>23</string>
<key>LSMinimumSystemVersion</key> <key>LSMinimumSystemVersion</key>
<string>13.0</string> <string>13.0</string>
<key>LSUIElement</key> <key>LSUIElement</key>
+2
View File
@@ -87,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 {
+29 -6
View File
@@ -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,6 +157,8 @@ struct UpdateNotificationView: View {
} }
var body: some View { var body: some View {
ZStack(alignment: .topTrailing) {
Button(action: { onTap?() }) {
HStack(spacing: 12) { HStack(spacing: 12) {
ZStack(alignment: .bottomTrailing) { ZStack(alignment: .bottomTrailing) {
Image(nsImage: NSImage(named: "AppIcon") ?? NSImage()) Image(nsImage: NSImage(named: "AppIcon") ?? NSImage())
@@ -159,13 +176,21 @@ struct UpdateNotificationView: View {
VStack(alignment: .leading, spacing: 3) { VStack(alignment: .leading, spacing: 3) {
Text(effectiveTitle) Text(effectiveTitle)
.font(.system(size: 13, weight: .semibold)) .font(.system(size: 13, weight: .semibold))
.foregroundColor(.primary)
Text(subtitle) Text(subtitle)
.font(.system(size: 11)) .font(.system(size: 11))
.foregroundColor(.secondary) .foregroundColor(.secondary)
.fixedSize(horizontal: false, vertical: true) .fixedSize(horizontal: false, vertical: true)
} }
Spacer(minLength: 8) Spacer(minLength: 24)
}
.padding(.horizontal, 14)
.padding(.vertical, 14)
.frame(width: 340, alignment: .leading)
.contentShape(Rectangle())
}
.buttonStyle(.plain)
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))
+1
View File
@@ -310,4 +310,5 @@ extension Notification.Name {
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-1.0.0-orange?style=flat)](https://github.com/superdooper86/claudechecker/releases) [![Version](https://img.shields.io/badge/version-1.0.3-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.
--- ---
+9
View File
@@ -0,0 +1,9 @@
## What's new in v1.0.4
### Clickable notifications
Notifications are now interactive — tap to act, not just read:
- **Limit warnings & resets** (80%, 95%, reset) — tapping the notification opens the main ClaudeChecker window so you can see your current usage at a glance
- **Update available** — tapping the notification opens the main window with the update sheet already showing, so you can install in one click
Previously notifications were display-only and required manually clicking the menubar icon to take action. This removes that friction.
+3 -3
View File
@@ -1,5 +1,5 @@
{ {
"version": "1.0.0", "version": "1.0.3",
"url": "https://github.com/superdooper86/claudechecker/releases/download/v1.0.0/ClaudeChecker.zip", "url": "https://github.com/superdooper86/claudechecker/releases/download/v1.0.3/ClaudeChecker.zip",
"notes": "Initial release." "notes": "Release 1.0.3"
} }