Add Xcode project source for v1.6
This commit is contained in:
@@ -0,0 +1,120 @@
|
||||
import SwiftUI
|
||||
import AppKit
|
||||
|
||||
// MARK: - Compact update notification window (shown on relaunch after update)
|
||||
|
||||
class UpdateNotificationWindowController: NSWindowController {
|
||||
static var shared: UpdateNotificationWindowController?
|
||||
|
||||
// Called after successful install + relaunch
|
||||
static func show(version: String, notes: String, near statusItem: NSStatusItem?) {
|
||||
showWindow(version: version, subtitle: notes.isEmpty ? "ClaudeChecker is up to date." : notes, near: statusItem, isUpdate: false)
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
private static func showWindow(version: String, subtitle: String, near statusItem: NSStatusItem?, isUpdate: Bool) {
|
||||
shared?.close()
|
||||
|
||||
let content = UpdateNotificationView(version: version, subtitle: subtitle, isUpdate: isUpdate) {
|
||||
shared?.close()
|
||||
shared = nil
|
||||
}
|
||||
|
||||
let hosting = NSHostingController(rootView: content)
|
||||
|
||||
let window = NSWindow(
|
||||
contentRect: NSRect(x: 0, y: 0, width: 340, height: 80),
|
||||
styleMask: [.borderless, .nonactivatingPanel],
|
||||
backing: .buffered,
|
||||
defer: false
|
||||
)
|
||||
window.isOpaque = false
|
||||
window.backgroundColor = .clear
|
||||
window.level = .statusBar
|
||||
window.hasShadow = false
|
||||
window.contentViewController = hosting
|
||||
|
||||
if let button = statusItem?.button,
|
||||
let buttonWindow = button.window,
|
||||
let screen = buttonWindow.screen {
|
||||
let buttonFrame = buttonWindow.convertToScreen(button.frame)
|
||||
let x = buttonFrame.midX - 170
|
||||
let y = buttonFrame.minY - 80 - 8
|
||||
let clampedX = max(8, min(x, screen.frame.maxX - 348))
|
||||
window.setFrameOrigin(NSPoint(x: clampedX, y: y))
|
||||
} else {
|
||||
window.center()
|
||||
}
|
||||
|
||||
let controller = UpdateNotificationWindowController(window: window)
|
||||
shared = controller
|
||||
controller.showWindow(nil)
|
||||
|
||||
// Auto-dismiss after 8 seconds
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 8) {
|
||||
NSAnimationContext.runAnimationGroup { ctx in
|
||||
ctx.duration = 0.3
|
||||
window.animator().alphaValue = 0
|
||||
} completionHandler: {
|
||||
controller.close()
|
||||
if shared === controller { shared = nil }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Notification View
|
||||
|
||||
struct UpdateNotificationView: View {
|
||||
let version: String
|
||||
let subtitle: String
|
||||
let isUpdate: Bool
|
||||
let onDismiss: () -> Void
|
||||
|
||||
var body: some View {
|
||||
HStack(spacing: 12) {
|
||||
ZStack(alignment: .bottomTrailing) {
|
||||
Image(nsImage: NSImage(named: "AppIcon") ?? NSImage())
|
||||
.resizable()
|
||||
.frame(width: 44, height: 44)
|
||||
.cornerRadius(10)
|
||||
|
||||
Image(systemName: isUpdate ? "arrow.down.circle.fill" : "checkmark.circle.fill")
|
||||
.font(.system(size: 16, weight: .semibold))
|
||||
.foregroundColor(isUpdate ? .blue : .green)
|
||||
.background(Color(nsColor: .windowBackgroundColor).clipShape(Circle()))
|
||||
.offset(x: 4, y: 4)
|
||||
}
|
||||
|
||||
VStack(alignment: .leading, spacing: 3) {
|
||||
Text(isUpdate ? "Update available — v\(version)" : "Updated to v\(version) ✓")
|
||||
.font(.system(size: 13, weight: .semibold))
|
||||
Text(subtitle)
|
||||
.font(.system(size: 11))
|
||||
.foregroundColor(.secondary)
|
||||
.lineLimit(1)
|
||||
}
|
||||
|
||||
Spacer()
|
||||
|
||||
Button(action: onDismiss) {
|
||||
Image(systemName: "xmark")
|
||||
.font(.system(size: 10, weight: .medium))
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
}
|
||||
.padding(.horizontal, 14)
|
||||
.padding(.vertical, 14)
|
||||
.frame(width: 340, height: 80)
|
||||
.background(
|
||||
RoundedRectangle(cornerRadius: 13)
|
||||
.fill(Color(nsColor: .windowBackgroundColor))
|
||||
.shadow(color: .black.opacity(0.25), radius: 16, x: 0, y: 6)
|
||||
)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user