Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b16c24cb44 | ||
|
|
89a79c6f13 | ||
|
|
aa529835f7 | ||
|
|
b5c3cc6379 | ||
|
|
c8adaf1baf | ||
|
|
3e99e1417e | ||
|
|
e41d3d0c0f | ||
|
|
1078951c43 | ||
|
|
ed6a5e1b51 | ||
|
|
8701fb1326 | ||
|
|
247b5fc202 | ||
|
|
255de797f8 | ||
|
|
0046c53283 | ||
|
|
8d222d63f0 | ||
|
|
5720370649 | ||
|
|
0843730ae1 | ||
|
|
9c030fcdd5 | ||
|
|
dd68ff39d6 |
@@ -50,13 +50,12 @@ jobs:
|
||||
|
||||
- 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
|
||||
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: |
|
||||
|
||||
@@ -60,6 +60,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 +171,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 +210,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 +221,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)
|
||||
}
|
||||
|
||||
@@ -94,6 +94,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
|
||||
@@ -977,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
|
||||
|
||||
struct UpdateBanner: View {
|
||||
@@ -992,22 +1025,13 @@ struct UpdateBanner: View {
|
||||
Text("Update available — v\(updater.latestVersion)")
|
||||
.font(.system(size: 12, weight: .semibold))
|
||||
if !updater.releaseNotes.isEmpty {
|
||||
Text(updater.releaseNotes)
|
||||
.font(.system(size: 11))
|
||||
.foregroundColor(.secondary)
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
ReleaseNotesText(text: updater.releaseNotes, font: .system(size: 11))
|
||||
}
|
||||
}
|
||||
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)
|
||||
@@ -1061,9 +1085,7 @@ struct UpdateSheet: View {
|
||||
.foregroundColor(.blue)
|
||||
}
|
||||
if !updater.releaseNotes.isEmpty {
|
||||
Text(updater.releaseNotes)
|
||||
.font(.system(size: 12))
|
||||
.foregroundColor(.secondary)
|
||||
ReleaseNotesText(text: updater.releaseNotes)
|
||||
}
|
||||
}
|
||||
Spacer()
|
||||
|
||||
@@ -15,9 +15,9 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0.4</string>
|
||||
<string>1.0.11</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>23</string>
|
||||
<string>31</string>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>13.0</string>
|
||||
<key>LSUIElement</key>
|
||||
|
||||
@@ -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: 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?) {
|
||||
|
||||
@@ -307,6 +307,7 @@ 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")
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
[](https://www.apple.com/macos/)
|
||||
[](https://swift.org)
|
||||
[](https://github.com/superdooper86/claudechecker/releases)
|
||||
[](https://github.com/superdooper86/claudechecker/releases)
|
||||
[](LICENSE)
|
||||
|
||||
</div>
|
||||
|
||||
+5
-7
@@ -1,9 +1,7 @@
|
||||
## What's new in v1.0.4
|
||||
## What's new in v1.0.11
|
||||
|
||||
### Clickable notifications
|
||||
Notifications are now interactive — tap to act, not just read:
|
||||
### 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.
|
||||
|
||||
- **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.
|
||||
### 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.
|
||||
|
||||
+3
-3
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"version": "1.0.3",
|
||||
"url": "https://github.com/superdooper86/claudechecker/releases/download/v1.0.3/ClaudeChecker.zip",
|
||||
"notes": "Release 1.0.3"
|
||||
"version": "1.1.1",
|
||||
"url": "https://github.com/superdooper86/claudechecker/releases/download/v1.0.10/ClaudeChecker.zip",
|
||||
"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."
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user