Files
OpenStats/Packages/OpenStatsKit/Sources/OpenStatsUI/State/AppSettings.swift
T
gentpan 1205483b5e 新增清理与系统维护,修复面板抖动,更换应用图标
- 清理:应用缓存、日志、浏览器缓存、Xcode/模拟器/npm 缓存、下载残留、安装包、废纸篓;
  白名单目录 + 安全守卫,运行中的应用自动跳过,执行前逐项复核,操作日志
- 系统维护:刷新 DNS 缓存、释放内存(辅助工具或管理员授权)
- 面板:去掉打开后的二次调整高度与窗口高度动画,内容放得下时不回弹,
  修复点击菜单栏图标关闭时被重新打开
- 更换应用图标,面板与关于页使用应用图标作为 Logo
- 清理未使用的组件、格式化方法、设计 token 与辅助工具接口
2026-09-13 03:03:08 +05:00

206 lines
7.2 KiB
Swift

import Foundation
import Observation
public enum MenuBarItem: String, CaseIterable, Identifiable, Sendable {
case cpu, memory, network, gpu, temperature, fan
public var id: String { rawValue }
var title: String {
switch self {
case .cpu: "CPU"
case .memory: "内存"
case .network: "网速"
case .gpu: "GPU"
case .temperature: "CPU 温度"
case .fan: "风扇转速"
}
}
var subtitle: String {
switch self {
case .cpu: "总占用,可附带迷你负载图"
case .memory: "已用内存占比"
case .network: "两行显示:绿点上传、蓝点下载"
case .gpu: "GPU 占用"
case .temperature: "CPU 核心最高温度"
case .fan: "转速最高的风扇"
}
}
/// 菜单栏上方的小标签
var menuBarLabel: String {
switch self {
case .cpu: "CPU"
case .memory: "RAM"
case .network: "NET"
case .gpu: "GPU"
case .temperature: "TEMP"
case .fan: "FAN"
}
}
/// 百分比类指标可选择图形样式
var supportsGaugeStyle: Bool { self == .cpu || self == .gpu || self == .memory }
var symbol: String {
switch self {
case .cpu: "cpu"
case .memory: "memorychip"
case .network: "arrow.up.arrow.down"
case .gpu: "square.3.layers.3d"
case .temperature: "thermometer.medium"
case .fan: "fan"
}
}
}
public enum GaugeStyle: String, CaseIterable, Identifiable, Sendable {
case value, bars, ring, pie, barsAndValue
public var id: String { rawValue }
var title: String {
switch self {
case .value: "数字"
case .bars: "柱状图"
case .ring: "圆环"
case .pie: "饼图"
case .barsAndValue: "柱状+数字"
}
}
}
public enum AppearanceMode: String, CaseIterable, Identifiable, Sendable {
case system, light, dark
public var id: String { rawValue }
var title: String {
switch self {
case .system: "跟随系统"
case .light: "浅色"
case .dark: "深色"
}
}
}
public enum PanelTab: String, CaseIterable, Identifiable, Sendable {
case overview, processes, thermal, keepAwake, cleaner
public var id: String { rawValue }
var title: String {
switch self {
case .overview: "概览"
case .processes: "进程"
case .thermal: "散热"
case .keepAwake: "防休眠"
case .cleaner: "清理"
}
}
}
/// UserDefaults 持久化的偏好设置
@MainActor
@Observable
public final class AppSettings {
@ObservationIgnored private let defaults: UserDefaults
public var menuBarItems: Set<MenuBarItem> {
didSet { defaults.set(menuBarItems.map(\.rawValue).sorted(), forKey: Keys.menuBarItems) }
}
/// CPU / GPU / 内存各自的菜单栏样式
public var gaugeStyles: [MenuBarItem: GaugeStyle] {
didSet {
defaults.set(Dictionary(uniqueKeysWithValues: gaugeStyles.map { ($0.key.rawValue, $0.value.rawValue) }),
forKey: Keys.gaugeStyles)
}
}
public var refreshSeconds: Int {
didSet { defaults.set(refreshSeconds, forKey: Keys.refreshSeconds) }
}
public var colorizeHighLoad: Bool {
didSet { defaults.set(colorizeHighLoad, forKey: Keys.colorizeHighLoad) }
}
public var useFahrenheit: Bool {
didSet { defaults.set(useFahrenheit, forKey: Keys.useFahrenheit) }
}
/// 电量低于该值时自动关闭“合盖运行”
public var lidModeBatteryFloor: Int {
didSet { defaults.set(lidModeBatteryFloor, forKey: Keys.lidModeBatteryFloor) }
}
/// 自定义风扇模式下 CPU 达到该温度时恢复系统控制
public var fanSafetyTemperature: Int {
didSet { defaults.set(fanSafetyTemperature, forKey: Keys.fanSafetyTemperature) }
}
public var panelTab: PanelTab {
didSet { defaults.set(panelTab.rawValue, forKey: Keys.panelTab) }
}
public var appearance: AppearanceMode {
didSet { defaults.set(appearance.rawValue, forKey: Keys.appearance) }
}
/// 可再生的缓存也先移到废纸篓(可恢复,但不会立即释放空间)
public var cleanPrefersTrash: Bool {
didSet { defaults.set(cleanPrefersTrash, forKey: Keys.cleanPrefersTrash) }
}
public static let refreshOptions = [1, 2, 3, 5]
public static let batteryFloorOptions = [10, 20, 30, 40]
public static let fanSafetyOptions = [85, 90, 95, 100]
public init(defaults: UserDefaults = .standard) {
self.defaults = defaults
let items = defaults.stringArray(forKey: Keys.menuBarItems)?.compactMap(MenuBarItem.init(rawValue:))
menuBarItems = Set(items ?? [.cpu])
let storedStyles = defaults.dictionary(forKey: Keys.gaugeStyles) as? [String: String] ?? [:]
gaugeStyles = Dictionary(uniqueKeysWithValues: storedStyles.compactMap { key, value in
guard let item = MenuBarItem(rawValue: key), let style = GaugeStyle(rawValue: value) else { return nil }
return (item, style)
})
refreshSeconds = Self.refreshOptions.contains(defaults.integer(forKey: Keys.refreshSeconds))
? defaults.integer(forKey: Keys.refreshSeconds) : 2
colorizeHighLoad = defaults.bool(forKey: Keys.colorizeHighLoad)
useFahrenheit = defaults.bool(forKey: Keys.useFahrenheit)
lidModeBatteryFloor = Self.batteryFloorOptions.contains(defaults.integer(forKey: Keys.lidModeBatteryFloor))
? defaults.integer(forKey: Keys.lidModeBatteryFloor) : 20
fanSafetyTemperature = Self.fanSafetyOptions.contains(defaults.integer(forKey: Keys.fanSafetyTemperature))
? defaults.integer(forKey: Keys.fanSafetyTemperature) : 95
panelTab = defaults.string(forKey: Keys.panelTab).flatMap(PanelTab.init(rawValue:)) ?? .overview
appearance = defaults.string(forKey: Keys.appearance).flatMap(AppearanceMode.init(rawValue:)) ?? .system
cleanPrefersTrash = defaults.bool(forKey: Keys.cleanPrefersTrash)
}
/// 按固定顺序返回已启用的菜单栏项目
var orderedMenuBarItems: [MenuBarItem] {
MenuBarItem.allCases.filter(menuBarItems.contains)
}
func isEnabled(_ item: MenuBarItem) -> Bool { menuBarItems.contains(item) }
func gaugeStyle(for item: MenuBarItem) -> GaugeStyle {
gaugeStyles[item] ?? (item == .cpu ? .barsAndValue : .value)
}
func setGaugeStyle(_ style: GaugeStyle, for item: MenuBarItem) {
gaugeStyles[item] = style
}
func setEnabled(_ item: MenuBarItem, _ enabled: Bool) {
if enabled { menuBarItems.insert(item) } else { menuBarItems.remove(item) }
}
private enum Keys {
static let menuBarItems = "menuBarItems"
static let gaugeStyles = "gaugeStyles"
static let refreshSeconds = "refreshSeconds"
static let colorizeHighLoad = "colorizeHighLoad"
static let useFahrenheit = "useFahrenheit"
static let lidModeBatteryFloor = "lidModeBatteryFloor"
static let fanSafetyTemperature = "fanSafetyTemperature"
static let panelTab = "panelTab"
static let appearance = "appearance"
static let cleanPrefersTrash = "cleanPrefersTrash"
}
}