Compare commits

..
41 Commits
Author SHA1 Message Date
james d1ab5206bd Set bundle version to 1.1.2-beta.3 2026-05-07 22:50:59 +02:00
github-actions[bot] 997e820be8 Beta release v1.1.2-beta.2 2026-05-07 20:46:59 +00:00
james 7fc78d5a83 Set bundle version to 1.1.2-beta.2 2026-05-07 22:46:06 +02:00
james 818d4aa6c2 Set bundle version to 1.1.2-beta.1 2026-05-07 22:45:39 +02:00
github-actions[bot] 6aa74fcabc Beta release v1.1.2-beta.1 2026-05-07 20:43:45 +00:00
james 2a01d6f5b8 Handle semver pre-release versions in isNewer; restore full version string in beta workflow 2026-05-07 22:42:35 +02:00
github-actions[bot] af920eda83 Beta release v1.1.2-beta.1 2026-05-07 20:41:57 +00:00
james 6b6802eaea Fix beta version format in version-beta.json — strip pre-release suffix for isNewer compatibility 2026-05-07 22:40:34 +02:00
github-actions[bot] e9d29db2ee Beta release v1.1.2-beta.1 2026-05-07 20:36:45 +00:00
james 9be5069aac v1.1.2-beta.1 — remove redundant window labels from card rows 2026-05-07 22:36:02 +02:00
james cc9e2aaba1 Add beta release workflow, restrict stable to non-prerelease tags 2026-05-07 22:33:47 +02:00
github-actions[bot] 19bb6459ed Release v1.1.1 2026-05-07 20:28:16 +00:00
James Bone abf05b04fc v1.1.1 — clearer limit labels and login window fix 2026-05-07 22:25:26 +02:00
github-actions[bot] 849e94c942 Release v1.1.0 2026-05-07 16:34:12 +00:00
superdooper86 d76c7496bb Release v1.1.0 2026-05-07 18:33:04 +02:00
github-actions[bot] 45a3925f65 Release v1.0.12 2026-05-07 16:29:13 +00:00
superdooper86 113e91583d Release v1.0.12 2026-05-07 18:28:11 +02:00
github-actions[bot] c60fb93e1a Release v1.0.11 2026-05-07 16:24:21 +00:00
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
13 changed files with 255 additions and 71 deletions
+63
View File
@@ -0,0 +1,63 @@
name: Release Beta
on:
push:
tags:
- 'v*-beta*'
jobs:
release-beta:
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: Create GitHub pre-release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh release create "$GITHUB_REF_NAME" ClaudeChecker.zip \
--title "$GITHUB_REF_NAME" \
--prerelease \
--notes "$(cat RELEASE_NOTES.md 2>/dev/null || echo 'Beta release ${{ steps.version.outputs.version }}')"
- name: Update version-beta.json
run: |
notes=$(cat RELEASE_NOTES.md 2>/dev/null || echo "Beta 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-beta.json
- name: Commit version-beta.json
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add version-beta.json
git commit -m "Beta release ${{ github.ref_name }}"
git push origin HEAD:main
+7 -7
View File
@@ -4,6 +4,7 @@ on:
push: push:
tags: tags:
- 'v*' - 'v*'
- '!v*-*'
jobs: jobs:
release: release:
@@ -50,13 +51,12 @@ jobs:
- name: Update version.json - name: Update version.json
run: | run: |
cat > version.json << JSON notes=$(cat RELEASE_NOTES.md 2>/dev/null || echo "Release ${{ steps.version.outputs.version }}")
{ jq -n \
"version": "${{ steps.version.outputs.version }}", --arg version "${{ steps.version.outputs.version }}" \
"url": "https://github.com/${{ github.repository }}/releases/download/${{ github.ref_name }}/ClaudeChecker.zip", --arg 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 }}')" --arg notes "$notes" \
} '{version: $version, url: $url, notes: $notes}' > version.json
JSON
- name: Update Homebrew cask - name: Update Homebrew cask
run: | run: |
+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)
} }
+59 -17
View File
@@ -47,13 +47,29 @@ struct ContentView: View {
EmptyStateView() EmptyStateView()
.padding(.vertical, 40) .padding(.vertical, 40)
} else { } else {
VStack(spacing: 1) { VStack(spacing: 8) {
ForEach(vm.limits) { limit in ForEach(vm.limits) { limit in
VStack(spacing: 0) {
HStack {
Text(limit.window == .fiveHour ? "5hr Limit" : "7 Day Limit")
.font(.system(size: 11, weight: .semibold))
.foregroundColor(.secondary)
.textCase(.uppercase)
Spacer()
}
.padding(.horizontal, 16)
.padding(.top, 10)
.padding(.bottom, 4)
LimitCard(limit: limit) LimitCard(limit: limit)
.background(Color(nsColor: .controlBackgroundColor))
.cornerRadius(8)
.overlay(RoundedRectangle(cornerRadius: 8).stroke(Color.primary.opacity(0.08), lineWidth: 0.5))
.padding(.horizontal, 8)
} }
} }
.background(Color.primary.opacity(0.05)) }
.padding(.vertical, 1) .padding(.bottom, 4)
// Extra credits row // Extra credits row
if let extra = vm.extraUsage, extra.isEnabled { if let extra = vm.extraUsage, extra.isEnabled {
@@ -94,6 +110,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() }
@@ -189,7 +212,7 @@ struct LimitCard: View {
AgentIconView(agent: .claude, size: 14) AgentIconView(agent: .claude, size: 14)
Text("Claude") Text("Claude")
.font(.system(size: 13, weight: .semibold)) .font(.system(size: 13, weight: .semibold))
Text("· \(vm.planLabel) · \(limit.window.label)") Text("· \(vm.planLabel)")
.font(.system(size: 11)) .font(.system(size: 11))
.foregroundColor(.secondary) .foregroundColor(.secondary)
Spacer() Spacer()
@@ -973,6 +996,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 {
@@ -988,22 +1041,13 @@ 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()
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)
@@ -1057,9 +1101,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>1.0.2</string> <string>1.1.2-beta.3</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>21</string> <string>35</string>
<key>LSMinimumSystemVersion</key> <key>LSMinimumSystemVersion</key>
<string>13.0</string> <string>13.0</string>
<key>LSUIElement</key> <key>LSUIElement</key>
+17 -2
View File
@@ -24,18 +24,25 @@ struct LoginWebView: NSViewRepresentable {
class Coordinator: NSObject, WKNavigationDelegate { class Coordinator: NSObject, WKNavigationDelegate {
let onAuthenticated: () -> Void let onAuthenticated: () -> Void
var didAuthenticate = false
init(onAuthenticated: @escaping () -> Void) { init(onAuthenticated: @escaping () -> Void) {
self.onAuthenticated = onAuthenticated self.onAuthenticated = onAuthenticated
} }
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) { func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
guard !didAuthenticate else { return }
// Don't fire on the login/auth pages themselves
if let url = webView.url?.absoluteString,
url.contains("/login") || url.contains("/auth") { return }
WKWebsiteDataStore.default().httpCookieStore.getAllCookies { cookies in WKWebsiteDataStore.default().httpCookieStore.getAllCookies { cookies in
let hasSession = cookies.contains { let hasSession = cookies.contains {
$0.domain.contains("claude.ai") && $0.domain.contains("claude.ai") &&
($0.name == "sessionKey" || $0.name == "__Secure-next-auth.session-token") ($0.name == "sessionKey" || $0.name == "__Secure-next-auth.session-token")
} }
if hasSession { guard hasSession, !self.didAuthenticate else { return }
self.didAuthenticate = true
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
self.onAuthenticated() self.onAuthenticated()
} }
@@ -43,7 +50,6 @@ struct LoginWebView: NSViewRepresentable {
} }
} }
} }
}
// MARK: - Login Sheet View // MARK: - Login Sheet View
@@ -58,10 +64,19 @@ struct LoginSheetView: View {
Text(authenticated ? "✓ Signed in — loading data…" : "Sign in to Claude") Text(authenticated ? "✓ Signed in — loading data…" : "Sign in to Claude")
.font(.system(size: 13, weight: .semibold)) .font(.system(size: 13, weight: .semibold))
Spacer() Spacer()
if authenticated {
Button("Done") {
isPresented = false
onDone()
}
.buttonStyle(.borderedProminent)
.controlSize(.small)
} else {
Button("Cancel") { isPresented = false } Button("Cancel") { isPresented = false }
.buttonStyle(.bordered) .buttonStyle(.bordered)
.controlSize(.small) .controlSize(.small)
} }
}
.padding(.horizontal, 14) .padding(.horizontal, 14)
.padding(.vertical, 10) .padding(.vertical, 10)
.background(Color(nsColor: .windowBackgroundColor)) .background(Color(nsColor: .windowBackgroundColor))
+22 -5
View File
@@ -318,13 +318,30 @@ class UpdateManager: ObservableObject {
} }
private func isNewer(_ version: String, than current: String) -> Bool { private func isNewer(_ version: String, than current: String) -> Bool {
let toInts = { (v: String) in v.split(separator: ".").compactMap { Int($0) } } func parse(_ v: String) -> (base: [Int], pre: [Int]?) {
let a = toInts(version), b = toInts(current) let halves = v.split(separator: "-", maxSplits: 1)
for i in 0..<max(a.count, b.count) { let base = halves[0].split(separator: ".").compactMap { Int($0) }
let av = i < a.count ? a[i] : 0 let pre = halves.count > 1 ? String(halves[1]).split(separator: ".").compactMap { Int($0) } : nil
let bv = i < b.count ? b[i] : 0 return (base, pre)
}
let a = parse(version), b = parse(current)
// Compare base version numbers
for i in 0..<max(a.base.count, b.base.count) {
let av = i < a.base.count ? a.base[i] : 0
let bv = i < b.base.count ? b.base[i] : 0
if av != bv { return av > bv } if av != bv { return av > bv }
} }
// Base equal: stable beats pre-release
if a.pre == nil && b.pre != nil { return true }
if a.pre != nil && b.pre == nil { return false }
// Both pre-release: compare pre-release numbers
if let ap = a.pre, let bp = b.pre {
for i in 0..<max(ap.count, bp.count) {
let av = i < ap.count ? ap[i] : 0
let bv = i < bp.count ? bp[i] : 0
if av != bv { return av > bv }
}
}
return false return false
} }
} }
+30 -7
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,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))
+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")
} }
+1 -1
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.1-orange?style=flat)](https://github.com/superdooper86/claudechecker/releases) [![Version](https://img.shields.io/badge/version-1.1.1-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>
+4
View File
@@ -0,0 +1,4 @@
## What's new in v1.1.2
### Cleaner card headers
Removed the redundant "5h" / "7d" window labels from each card row — the section headers already make it clear which limit is which.
+5
View File
@@ -0,0 +1,5 @@
{
"version": "1.1.2-beta.2",
"url": "https://github.com/superdooper86/claudechecker/releases/download/v1.1.2-beta.2/ClaudeChecker.zip",
"notes": "## What's new in v1.1.2\n\n### Cleaner card headers\nRemoved the redundant \"5h\" / \"7d\" window labels from each card row — the section headers already make it clear which limit is which."
}
+3 -3
View File
@@ -1,5 +1,5 @@
{ {
"version": "1.0.1", "version": "1.1.1",
"url": "https://github.com/superdooper86/claudechecker/releases/download/v1.0.1/ClaudeChecker.zip", "url": "https://github.com/superdooper86/claudechecker/releases/download/v1.1.1/ClaudeChecker.zip",
"notes": "Update banner can now be dismissed. Fixes update state not clearing correctly." "notes": "## What's new in v1.1.1\n\n### Clearer limit labels\nThe 5hr and 7 Day usage cards now have clear section headers so it's immediately obvious which limit is which.\n\n### Login window fix\nThe sign-in sheet now reliably closes after a successful login. A **Done** button also appears as a fallback once authentication is detected — no more being stuck with only a Cancel button."
} }