Compare commits
55
Commits
v1.0.9
...
v1.1.3-beta.4
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ae736a7e4a | ||
|
|
a1bb186af2 | ||
|
|
e93a4e6df5 | ||
|
|
a3fbce3f0e | ||
|
|
4b4280630c | ||
|
|
90acbcb968 | ||
|
|
f2f2be1305 | ||
|
|
f17694622c | ||
|
|
aa10f77776 | ||
|
|
cb00ddc806 | ||
|
|
1f357b9123 | ||
|
|
9dee674ff0 | ||
|
|
0025690a74 | ||
|
|
7b03909c55 | ||
|
|
c3ea2be809 | ||
|
|
6f6a7361fe | ||
|
|
d9d65da6b7 | ||
|
|
c18054a810 | ||
|
|
032951882f | ||
|
|
ed186af2c6 | ||
|
|
d3dabc7843 | ||
|
|
987bd36137 | ||
|
|
cfcb74f32b | ||
|
|
273e10566e | ||
|
|
76a8f433a7 | ||
|
|
65a171f296 | ||
|
|
26312290ac | ||
|
|
63cbd70f38 | ||
|
|
235ab81efb | ||
|
|
12d7855ceb | ||
|
|
d1ab5206bd | ||
|
|
997e820be8 | ||
|
|
7fc78d5a83 | ||
|
|
818d4aa6c2 | ||
|
|
6aa74fcabc | ||
|
|
2a01d6f5b8 | ||
|
|
af920eda83 | ||
|
|
6b6802eaea | ||
|
|
e9d29db2ee | ||
|
|
9be5069aac | ||
|
|
cc9e2aaba1 | ||
|
|
19bb6459ed | ||
|
|
abf05b04fc | ||
|
|
849e94c942 | ||
|
|
d76c7496bb | ||
|
|
45a3925f65 | ||
|
|
113e91583d | ||
|
|
c60fb93e1a | ||
|
|
b16c24cb44 | ||
|
|
89a79c6f13 | ||
|
|
aa529835f7 | ||
|
|
b5c3cc6379 | ||
|
|
c8adaf1baf | ||
|
|
3e99e1417e | ||
|
|
e41d3d0c0f |
@@ -0,0 +1,79 @@
|
||||
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: Update beta badge in README
|
||||
run: |
|
||||
shields_version=$(echo "${{ steps.version.outputs.version }}" | sed 's/-/--/g')
|
||||
tag="${{ github.ref_name }}"
|
||||
export BADGE="[](https://github.com/${{ github.repository }}/releases/tag/${tag}) <!-- BETA_BADGE -->"
|
||||
python3 -c "
|
||||
import os
|
||||
badge = os.environ['BADGE']
|
||||
lines = open('README.md').readlines()
|
||||
out = []
|
||||
for line in lines:
|
||||
out.append(badge + chr(10) if '<!-- BETA_BADGE -->' in line else line)
|
||||
open('README.md', 'w').writelines(out)
|
||||
"
|
||||
|
||||
- name: Commit version-beta.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-beta.json README.md
|
||||
git commit -m "Beta release ${{ github.ref_name }}"
|
||||
git pull --rebase origin main
|
||||
git push origin HEAD:main
|
||||
@@ -7,6 +7,7 @@ on:
|
||||
|
||||
jobs:
|
||||
release:
|
||||
if: ${{ !contains(github.ref_name, '-') }}
|
||||
runs-on: macos-latest
|
||||
permissions:
|
||||
contents: write
|
||||
@@ -65,10 +66,24 @@ jobs:
|
||||
../homebrew-tap/Casks/claudechecker.rb 2>/dev/null || true
|
||||
|
||||
- name: Update README badge
|
||||
env:
|
||||
NEW_VERSION: ${{ steps.version.outputs.version }}
|
||||
run: |
|
||||
sed -i '' \
|
||||
's/version-[0-9][^-]*-orange/version-${{ steps.version.outputs.version }}-orange/' \
|
||||
README.md
|
||||
python3 -c "
|
||||
import os, re
|
||||
v = os.environ['NEW_VERSION']
|
||||
content = open('README.md').read()
|
||||
content = re.sub(r'version-[0-9][^-]*-orange', 'version-' + v + '-orange', content)
|
||||
lines = content.splitlines(keepends=True)
|
||||
out = []
|
||||
for line in lines:
|
||||
if '<!-- BETA_BADGE -->' in line:
|
||||
continue
|
||||
out.append(line)
|
||||
if 'license-MIT-blue' in line:
|
||||
out.append('<!-- BETA_BADGE -->' + chr(10))
|
||||
open('README.md', 'w').writelines(out)
|
||||
"
|
||||
|
||||
- name: Commit version.json and README
|
||||
run: |
|
||||
@@ -76,6 +91,7 @@ jobs:
|
||||
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 pull --rebase origin main
|
||||
git push origin HEAD:main
|
||||
|
||||
- name: Update Homebrew tap
|
||||
|
||||
@@ -40,14 +40,15 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
||||
|
||||
// Popover
|
||||
popover = NSPopover()
|
||||
popover?.contentSize = NSSize(width: 480, height: 640)
|
||||
popover?.behavior = .transient
|
||||
popover?.animates = true
|
||||
popover?.contentViewController = NSHostingController(
|
||||
let hc = NSHostingController(
|
||||
rootView: ContentView()
|
||||
.environmentObject(usageViewModel)
|
||||
.environmentObject(updateManager)
|
||||
)
|
||||
hc.sizingOptions = .preferredContentSize
|
||||
popover?.contentViewController = hc
|
||||
|
||||
// React to limits or menubar toggle changes
|
||||
usageViewModel.$limits
|
||||
@@ -60,6 +61,11 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
||||
.sink { [weak self] _ in self?.updateStatusItem() }
|
||||
.store(in: &cancellables)
|
||||
|
||||
updateManager.$updateAvailable
|
||||
.receive(on: DispatchQueue.main)
|
||||
.sink { [weak self] _ in self?.updateStatusItem() }
|
||||
.store(in: &cancellables)
|
||||
|
||||
// Refresh timer
|
||||
scheduleTimer(interval: usageViewModel.refreshInterval)
|
||||
|
||||
@@ -166,7 +172,13 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
||||
let sdStr = sd.map { "\(Int($0.usedPercent.rounded()))%" } ?? "—"
|
||||
button.image = nil
|
||||
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)
|
||||
} else {
|
||||
button.title = ""
|
||||
@@ -199,6 +211,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
||||
@objc func showPopover() {
|
||||
guard let button = statusItem?.button, let popover else { return }
|
||||
if !popover.isShown {
|
||||
NotificationCenter.default.post(name: .popoverWillOpen, object: nil)
|
||||
popover.show(relativeTo: button.bounds, of: button, preferredEdge: .minY)
|
||||
NSApp.activate(ignoringOtherApps: true)
|
||||
}
|
||||
@@ -209,6 +222,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
||||
if popover.isShown {
|
||||
popover.performClose(nil)
|
||||
} else {
|
||||
NotificationCenter.default.post(name: .popoverWillOpen, object: nil)
|
||||
popover.show(relativeTo: button.bounds, of: button, preferredEdge: .minY)
|
||||
NSApp.activate(ignoringOtherApps: true)
|
||||
}
|
||||
@@ -223,3 +237,4 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -47,13 +47,29 @@ struct ContentView: View {
|
||||
EmptyStateView()
|
||||
.padding(.vertical, 40)
|
||||
} else {
|
||||
VStack(spacing: 1) {
|
||||
VStack(spacing: 8) {
|
||||
ForEach(vm.limits) { limit in
|
||||
LimitCard(limit: limit)
|
||||
VStack(spacing: 0) {
|
||||
HStack {
|
||||
Text(limit.window == .fiveHour ? "5 Hour 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)
|
||||
.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
|
||||
if let extra = vm.extraUsage, extra.isEnabled {
|
||||
@@ -94,6 +110,9 @@ struct ContentView: View {
|
||||
}
|
||||
.frame(width: 480)
|
||||
.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
|
||||
@@ -193,7 +212,7 @@ struct LimitCard: View {
|
||||
AgentIconView(agent: .claude, size: 14)
|
||||
Text("Claude")
|
||||
.font(.system(size: 13, weight: .semibold))
|
||||
Text("· \(vm.planLabel) · \(limit.window.label)")
|
||||
Text("· \(vm.planLabel)")
|
||||
.font(.system(size: 11))
|
||||
.foregroundColor(.secondary)
|
||||
Spacer()
|
||||
@@ -410,7 +429,9 @@ struct SparklineView: View {
|
||||
|
||||
var body: some View {
|
||||
GeometryReader { geo in
|
||||
if data.count > 1 {
|
||||
let plotData = data.count == 1 ? [data[0], data[0]] : data
|
||||
if plotData.count > 1 {
|
||||
let data = plotData
|
||||
let w = geo.size.width
|
||||
let h = geo.size.height
|
||||
let minV = data.min() ?? 0
|
||||
@@ -770,25 +791,23 @@ struct SettingsView: View {
|
||||
.padding(.vertical, 1)
|
||||
.background(Color.primary.opacity(0.06))
|
||||
.cornerRadius(4)
|
||||
if updater.updateAvailable {
|
||||
Text("v\(updater.latestVersion) available")
|
||||
.font(.system(size: 10, weight: .medium))
|
||||
.foregroundColor(.white)
|
||||
.padding(.horizontal, 6)
|
||||
.padding(.vertical, 2)
|
||||
.background(Color.blue)
|
||||
.cornerRadius(4)
|
||||
.fixedSize()
|
||||
} else if updater.betaAvailable {
|
||||
Text("v\(updater.latestBetaVersion) available")
|
||||
.font(.system(size: 10, weight: .medium))
|
||||
.foregroundColor(.white)
|
||||
.padding(.horizontal, 6)
|
||||
.padding(.vertical, 2)
|
||||
.background(Color.orange)
|
||||
.cornerRadius(4)
|
||||
.fixedSize()
|
||||
}
|
||||
}
|
||||
if updater.updateAvailable {
|
||||
Text("v\(updater.latestVersion) available")
|
||||
.font(.system(size: 10, weight: .medium))
|
||||
.foregroundColor(.white)
|
||||
.padding(.horizontal, 6)
|
||||
.padding(.vertical, 2)
|
||||
.background(Color.blue)
|
||||
.cornerRadius(4)
|
||||
} else if updater.betaAvailable {
|
||||
Text("v\(updater.latestBetaVersion) available")
|
||||
.font(.system(size: 10, weight: .medium))
|
||||
.foregroundColor(.white)
|
||||
.padding(.horizontal, 6)
|
||||
.padding(.vertical, 2)
|
||||
.background(Color.orange)
|
||||
.cornerRadius(4)
|
||||
}
|
||||
Text("Built by superdooper86 & claude")
|
||||
.font(.system(size: 11))
|
||||
@@ -1021,25 +1040,20 @@ struct UpdateBanner: View {
|
||||
VStack(alignment: .leading, spacing: 1) {
|
||||
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("Tap Update to view what's new & install.")
|
||||
.font(.system(size: 11))
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
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)
|
||||
.background(Color.blue.opacity(0.08))
|
||||
.overlay(Divider(), alignment: .bottom)
|
||||
.onTapGesture { showSheet = true }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1157,3 +1171,5 @@ struct UpdateSheet: View {
|
||||
.frame(width: 380)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -15,9 +15,9 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0.9</string>
|
||||
<string>1.1.3-beta.4</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>28</string>
|
||||
<string>38</string>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>13.0</string>
|
||||
<key>LSUIElement</key>
|
||||
@@ -45,3 +45,5 @@
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
|
||||
|
||||
@@ -24,21 +24,27 @@ struct LoginWebView: NSViewRepresentable {
|
||||
|
||||
class Coordinator: NSObject, WKNavigationDelegate {
|
||||
let onAuthenticated: () -> Void
|
||||
var didAuthenticate = false
|
||||
|
||||
init(onAuthenticated: @escaping () -> Void) {
|
||||
self.onAuthenticated = onAuthenticated
|
||||
}
|
||||
|
||||
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
|
||||
let hasSession = cookies.contains {
|
||||
$0.domain.contains("claude.ai") &&
|
||||
($0.name == "sessionKey" || $0.name == "__Secure-next-auth.session-token")
|
||||
}
|
||||
if hasSession {
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
|
||||
self.onAuthenticated()
|
||||
}
|
||||
guard hasSession, !self.didAuthenticate else { return }
|
||||
self.didAuthenticate = true
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
|
||||
self.onAuthenticated()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -58,9 +64,18 @@ struct LoginSheetView: View {
|
||||
Text(authenticated ? "✓ Signed in — loading data…" : "Sign in to Claude")
|
||||
.font(.system(size: 13, weight: .semibold))
|
||||
Spacer()
|
||||
Button("Cancel") { isPresented = false }
|
||||
.buttonStyle(.bordered)
|
||||
if authenticated {
|
||||
Button("Done") {
|
||||
isPresented = false
|
||||
onDone()
|
||||
}
|
||||
.buttonStyle(.borderedProminent)
|
||||
.controlSize(.small)
|
||||
} else {
|
||||
Button("Cancel") { isPresented = false }
|
||||
.buttonStyle(.bordered)
|
||||
.controlSize(.small)
|
||||
}
|
||||
}
|
||||
.padding(.horizontal, 14)
|
||||
.padding(.vertical, 10)
|
||||
|
||||
@@ -318,13 +318,31 @@ class UpdateManager: ObservableObject {
|
||||
}
|
||||
|
||||
private func isNewer(_ version: String, than current: String) -> Bool {
|
||||
let toInts = { (v: String) in v.split(separator: ".").compactMap { Int($0) } }
|
||||
let a = toInts(version), b = toInts(current)
|
||||
for i in 0..<max(a.count, b.count) {
|
||||
let av = i < a.count ? a[i] : 0
|
||||
let bv = i < b.count ? b[i] : 0
|
||||
func parse(_ v: String) -> (base: [Int], pre: [Int]?) {
|
||||
guard !v.isEmpty else { return ([], nil) }
|
||||
let halves = v.split(separator: "-", maxSplits: 1)
|
||||
let base = halves[0].split(separator: ".").compactMap { Int($0) }
|
||||
let pre = halves.count > 1 ? String(halves[1]).split(separator: ".").compactMap { Int($0) } : nil
|
||||
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 }
|
||||
}
|
||||
// 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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,6 +37,9 @@ class UsageViewModel: ObservableObject {
|
||||
let saved = UserDefaults.standard.double(forKey: "refresh_interval")
|
||||
refreshInterval = saved > 0 ? saved : 60
|
||||
showInMenuBar = UserDefaults.standard.object(forKey: "show_in_menubar") as? Bool ?? true
|
||||
if let saved = UserDefaults.standard.object(forKey: "burn_history") as? [String: [Double]] {
|
||||
burnHistoryStore = saved
|
||||
}
|
||||
loadPlaceholderData()
|
||||
}
|
||||
|
||||
@@ -84,8 +87,9 @@ class UsageViewModel: ObservableObject {
|
||||
history.append(limits[i].usedPercent)
|
||||
if history.count > maxHistorySamples { history.removeFirst() }
|
||||
burnHistoryStore[key] = history
|
||||
if history.count > 1 { limits[i].burnHistory = history }
|
||||
limits[i].burnHistory = history
|
||||
}
|
||||
UserDefaults.standard.set(burnHistoryStore, forKey: "burn_history")
|
||||
checkLimitNotifications(for: limits)
|
||||
} catch AppError.notAuthenticated {
|
||||
isNotAuthenticated = true
|
||||
@@ -307,8 +311,10 @@ extension Notification.Name {
|
||||
static let refreshIntervalChanged = Notification.Name("refreshIntervalChanged")
|
||||
static let closePopover = Notification.Name("closePopover")
|
||||
static let showPopover = Notification.Name("showPopover")
|
||||
static let popoverWillOpen = Notification.Name("popoverWillOpen")
|
||||
static let updateDetected = Notification.Name("updateDetected")
|
||||
static let limitWarning = Notification.Name("limitWarning")
|
||||
static let limitReset = Notification.Name("limitReset")
|
||||
static let openUpdateSheet = Notification.Name("openUpdateSheet")
|
||||
}
|
||||
|
||||
|
||||
@@ -8,8 +8,9 @@
|
||||
|
||||
[](https://www.apple.com/macos/)
|
||||
[](https://swift.org)
|
||||
[](https://github.com/superdooper86/claudechecker/releases)
|
||||
[](https://github.com/superdooper86/claudechecker/releases)
|
||||
[](LICENSE)
|
||||
[](https://github.com/superdooper86/claudechecker/releases/tag/v1.1.3-beta.3) <!-- BETA_BADGE -->
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
+10
-3
@@ -1,4 +1,11 @@
|
||||
## What's new in v1.0.9
|
||||
## What's new in v1.1.3
|
||||
|
||||
### 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.
|
||||
### UI polish
|
||||
- Limit headers now read "5 Hour Limit" and "7 Day Limit" instead of "5hr"
|
||||
- Tapping anywhere on the blue update banner now opens the update window
|
||||
|
||||
### Session Diary fix
|
||||
- Burn history is now persisted across app launches — the sparkline populates immediately on first open
|
||||
|
||||
### Window sizing fix
|
||||
- The popover now resizes correctly when switching between the main view and settings — no more empty space below the main view
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"version": "1.1.3-beta.3",
|
||||
"url": "https://github.com/superdooper86/claudechecker/releases/download/v1.1.3-beta.3/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\" instead of \"5hr\"\n- Tapping anywhere on the blue update banner now opens the update window\n\n### Session Diary fix\n- Burn history is now persisted across app launches — the sparkline populates immediately on first open instead of requiring a manual refresh"
|
||||
}
|
||||
+3
-3
@@ -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": "1.1.2",
|
||||
"url": "https://github.com/superdooper86/claudechecker/releases/download/v1.1.2/ClaudeChecker.zip",
|
||||
"notes": "## What's new in v1.1.2\n\n### Cleaner usage cards\nRemoved the redundant \"5h\" / \"7d\" labels from each card row — the section headers already make it clear which limit is which.\n\n### Beta update support\nThe app now correctly detects and compares beta versions (e.g. v1.1.2-beta.2 > v1.1.2-beta.1) when the beta channel is enabled in Settings. Stable releases also correctly supersede beta builds of the same version — so users on a beta are prompted to install the final release.\n\n### Fixed crash when enabling beta channel\nEnabling the beta toggle could crash the app if no stable update was available. This is now fixed.\n\n### Fixed settings panel truncation\nThe app name and version number no longer get cut off when an update badge is displayed in Settings."
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user