Files
wehub-resource-sync 869b84f27c
Build / build (push) Waiting to run
chore: import upstream snapshot with attribution
2026-07-13 12:29:30 +08:00

130 lines
4.7 KiB
Swift

import SwiftUI
import AppKit
/// Zero-size helper that captures SwiftUI's `openWindow` action into
/// `WindowOpener.shared` when the main window appears, so the AppKit menu-bar
/// popover can reopen the window after it's been closed.
struct WindowOpenerCapture: View {
@Environment(\.openWindow) private var openWindow
var body: some View {
Color.clear
.frame(width: 0, height: 0)
.onAppear { WindowOpener.shared.open = { id in openWindow(id: id) } }
}
}
/// Drop-down panel hosted in the menu-bar `NSPopover` (via `NSHostingController`)
/// with live CPU / memory / disk meters and quick actions. Kept self-contained
/// so the menu bar surface stays decoupled from the main window's `AppState`.
struct MenuBarMonitorView: View {
@ObservedObject private var monitor = SystemMonitor.shared
var body: some View {
VStack(alignment: .leading, spacing: 12) {
Text("System Monitor")
.font(.system(size: 13, weight: .semibold))
.foregroundStyle(.primary)
VStack(spacing: 10) {
MeterRow(title: "CPU", tint: Tint.blue,
fraction: monitor.cpuUsage,
detail: "\(Int((monitor.cpuUsage * 100).rounded()))%")
MeterRow(title: "Memory", tint: Tint.purple,
fraction: monitor.memoryFraction,
detail: byteDetail(monitor.memoryUsed, monitor.memoryTotal))
MeterRow(title: "Disk", tint: Tint.green,
fraction: monitor.diskFraction,
detail: byteDetail(monitor.diskUsed, monitor.diskTotal))
}
Divider()
HStack {
Button {
openMainWindow()
} label: {
Text("Open PureMac")
}
.buttonStyle(.borderedProminent)
.controlSize(.small)
Spacer()
Button {
NSApp.terminate(nil)
} label: {
Text("Quit PureMac")
}
.buttonStyle(.bordered)
.controlSize(.small)
}
}
.padding(14)
.frame(width: 248)
.onAppear { monitor.start() }
.onDisappear { monitor.stop() }
}
private func byteDetail(_ used: Int64, _ total: Int64) -> String {
let u = ByteCountFormatter.string(fromByteCount: used, countStyle: .memory)
let t = ByteCountFormatter.string(fromByteCount: total, countStyle: .memory)
return "\(u) / \(t)"
}
/// Bring the app forward and surface the main window. The app stays alive
/// after its window closes only while the monitor is enabled (see
/// `AppDelegate.applicationShouldTerminateAfterLastWindowClosed`), so this
/// reopens a fresh window when none is left, otherwise just focuses it.
private func openMainWindow() {
NSApp.activate(ignoringOtherApps: true)
// Exclude the menu-bar popover's own panel; a real content window is
// titled and can become main.
if let existing = NSApp.windows.first(where: {
$0.canBecomeMain && $0.styleMask.contains(.titled)
}) {
existing.makeKeyAndOrderFront(nil)
} else {
// No content window left — reopen via the captured openWindow action
// (the popover has no working openWindow environment of its own).
WindowOpener.shared.open?("main")
}
}
}
/// One labeled meter: title on the left, a thin tinted progress bar, and a
/// trailing numeric detail. Mirrors the restrained chrome used elsewhere.
private struct MeterRow: View {
let title: LocalizedStringKey
let tint: Color
let fraction: Double
let detail: String
private var clamped: Double { max(0, min(1, fraction)) }
var body: some View {
VStack(spacing: 4) {
HStack {
Text(title)
.font(.system(size: 11, weight: .medium))
.foregroundStyle(.secondary)
Spacer()
Text(detail)
.font(.system(size: 11, weight: .semibold))
.monospacedDigit()
.foregroundStyle(.primary)
}
GeometryReader { geo in
ZStack(alignment: .leading) {
Capsule()
.fill(Color.primary.opacity(0.08))
Capsule()
.fill(tint)
.frame(width: max(2, geo.size.width * clamped))
}
}
.frame(height: 5)
}
}
}