diff --git a/CHANGELOG.md b/CHANGELOG.md index 84209c4..3bf2989 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,19 @@ 记录 OpenStats 应用的功能新增、样式调整和问题修复,按版本与日期倒序排列。 官网、构建与发布脚本等不影响应用本身的改动不在此记录。 +## 未发布 + +### 2026-09-16 + +#### 样式 + +- 主窗口在 macOS 26 上改用液态玻璃:侧边栏是一块浮在窗口里的玻璃面板,红绿灯按钮落在面板里,选中项垫一块淡蓝色圆角底并随切换滑动;顶栏右侧“在菜单栏显示”一组收进玻璃胶囊。主按钮是蓝色玻璃胶囊、次要按钮与图标按钮是透明玻璃,风扇模式等快捷切换是玻璃胶囊,开关换成系统开关(拖动时滑块变成玻璃)。设置分组与数据卡片保持实色,与系统设置一致。macOS 14、15 上保持原来的样式。 +- 分段切换改为苹果标准样式:灰色圆角底槽,各段等宽铺满,选中的一段是实心蓝色、白字,切换时蓝块滑过去;网络详情的 IPv4 / IPv6 切换同样是灰底蓝选中。 + +#### 新增 + +- 全局加载框:清理、卸载应用、导出诊断信息、修改 DNS 进行中,整个主窗口压暗,中间浮一块加载框写明正在做什么(macOS 26 上是液态玻璃),期间不能误点其他操作,完成后自动消失。 + ## 0.3.2 · 2026-09-16 ### 2026-09-16 diff --git a/Packages/OpenStatsKit/Sources/Localization/Translations.swift b/Packages/OpenStatsKit/Sources/Localization/Translations.swift index 296d67e..2e614a3 100644 --- a/Packages/OpenStatsKit/Sources/Localization/Translations.swift +++ b/Packages/OpenStatsKit/Sources/Localization/Translations.swift @@ -680,6 +680,7 @@ npm 缓存 npm Cache 正在同步… Syncing… 正在安装,完成后 OpenStats 会自动重启。 Installing. OpenStats will relaunch when it's done. 正在导出… Exporting… +正在导出诊断信息… Exporting diagnostics… 正在扫描应用… Scanning apps… 正在收集走势… Collecting trend… 正在本机生成… Generating on this Mac… diff --git a/Packages/OpenStatsKit/Sources/OpenStatsUI/Design/Components.swift b/Packages/OpenStatsKit/Sources/OpenStatsUI/Design/Components.swift index a11a1eb..c3d85ef 100644 --- a/Packages/OpenStatsKit/Sources/OpenStatsUI/Design/Components.swift +++ b/Packages/OpenStatsKit/Sources/OpenStatsUI/Design/Components.swift @@ -331,8 +331,31 @@ private struct DSButtonBody: View { let kind: DSButtonStyle.Kind @State private var hovering = false @Environment(\.isEnabled) private var isEnabled + @Environment(\.isInsideGlass) private var isInsideGlass var body: some View { + if DS.Glass.isAvailable { + glassBody + } else { + solidBody + } + } + + /// macOS 26:胶囊形玻璃按钮。主按钮是蓝色玻璃,次要按钮是透明玻璃,文字按钮不加玻璃 + private var glassBody: some View { + configuration.label + .dsFont(.sm, weight: .medium) + .foregroundStyle(foreground) + .padding(.horizontal, DS.Space.s4) + .frame(height: DS.Size.controlHeight) + .contentShape(Capsule()) + .modifier(GlassButtonSurface(kind: kind, active: active, isInsideGlass: isInsideGlass)) + .opacity(isEnabled ? 1 : 0.5) + .onHover { hovering = $0 } + .animation(DS.Motion.quick, value: hovering) + } + + private var solidBody: some View { configuration.label .dsFont(.sm, weight: .medium) .foregroundStyle(foreground) @@ -374,15 +397,18 @@ struct IconButton: View { let help: String let action: () -> Void @State private var hovering = false + @Environment(\.isInsideGlass) private var isInsideGlass var body: some View { + // macOS 26 上是圆形玻璃按钮(与系统设置的前进后退一致);已经在玻璃面板里时只有悬停底色 + let glass = DS.Glass.isAvailable && !isInsideGlass Button(action: action) { Image(systemName: systemName) .font(.system(size: DS.TextSize.sm.rawValue, weight: .medium)) - .foregroundStyle(hovering ? DS.Palette.primary : DS.Palette.textSecondary) + .foregroundStyle(hovering ? DS.Palette.primary : glass ? DS.Palette.textPrimary : DS.Palette.textSecondary) .frame(width: DS.Size.controlHeight, height: DS.Size.controlHeight) - .background(hovering ? DS.Palette.surfaceHover : .clear, in: RoundedRectangle(cornerRadius: DS.Radius.md)) - .contentShape(Rectangle()) + .modifier(IconButtonSurface(hovering: hovering, glass: glass)) + .contentShape(Circle()) } .buttonStyle(.plain) .onHover { hovering = $0 } @@ -391,6 +417,39 @@ struct IconButton: View { } } +/// 玻璃按钮的底:主按钮蓝色玻璃,次要按钮透明玻璃;文字按钮、或已在玻璃面板里的次要按钮只有悬停底色 +private struct GlassButtonSurface: ViewModifier { + let kind: DSButtonStyle.Kind + let active: Bool + let isInsideGlass: Bool + + func body(content: Content) -> some View { + switch kind { + case .primary: + content.dsGlass(in: Capsule(), tint: DS.Palette.primary, interactive: true) + case .secondary where !isInsideGlass: + content.dsGlass(in: Capsule(), interactive: true) + default: + content.background(active ? DS.Palette.surfaceHover : .clear, in: Capsule()) + } + } +} + +private struct IconButtonSurface: ViewModifier { + let hovering: Bool + let glass: Bool + + func body(content: Content) -> some View { + if glass { + content.dsGlass(in: Circle(), interactive: true) + } else if DS.Glass.isAvailable { + content.background(hovering ? DS.Palette.surfaceHover : .clear, in: Circle()) + } else { + content.background(hovering ? DS.Palette.surfaceHover : .clear, in: RoundedRectangle(cornerRadius: DS.Radius.md)) + } + } +} + // MARK: - 侧边栏 /// 侧边栏的一项。选中态不填色:由容器上的 `sidebarGlider()` 画左侧滑动的蓝色光条与淡淡的高亮 @@ -442,15 +501,32 @@ struct SidebarRowsKey: PreferenceKey { } extension View { - /// 放在包含 SidebarButton 的容器上:左侧一条两端渐隐的细线,选中项位置有一段发光的蓝色光条, - /// 切换时带回弹地滑过去,光条右侧拖出一片渐淡的蓝色高亮 + /// 放在包含 SidebarButton 的容器上,画出选中态。 + /// macOS 26:选中项垫一块淡蓝色圆角底,切换时滑过去(侧边栏本身是玻璃面板,里面不再叠玻璃); + /// 更早的系统:左侧一条两端渐隐的细线,选中项位置有一段发光的蓝色光条,带回弹地滑过去 + @ViewBuilder func sidebarGlider() -> some View { - overlayPreferenceValue(SidebarRowsKey.self) { rows in - GeometryReader { proxy in - SidebarGlider(frames: rows.map { proxy[$0.bounds] }, - selected: rows.first(where: \.isSelected).map { proxy[$0.bounds] }) + if DS.Glass.isAvailable { + backgroundPreferenceValue(SidebarRowsKey.self) { rows in + GeometryReader { proxy in + if let selected = rows.first(where: \.isSelected).map({ proxy[$0.bounds] }) { + RoundedRectangle(cornerRadius: DS.Radius.md, style: .continuous) + .fill(DS.Palette.sidebarSelected) + .frame(width: selected.width, height: selected.height) + .offset(x: selected.minX, y: selected.minY) + .animation(.spring(response: 0.35, dampingFraction: 0.8), value: selected) + } + } + .allowsHitTesting(false) + } + } else { + overlayPreferenceValue(SidebarRowsKey.self) { rows in + GeometryReader { proxy in + SidebarGlider(frames: rows.map { proxy[$0.bounds] }, + selected: rows.first(where: \.isSelected).map { proxy[$0.bounds] }) + } + .allowsHitTesting(false) } - .allowsHitTesting(false) } } } @@ -497,9 +573,14 @@ private struct SidebarGlider: View { // MARK: - 分段控件 +/// 分段切换(苹果标准样式,与系统设置一致):一条灰色圆角底槽,各段等宽铺满, +/// 选中的一段是实心品牌蓝、白字,切换时蓝块滑过去 struct SegmentedControl: View { @Binding var selection: Value let options: [(value: Value, title: String)] + @Namespace private var namespace + + private static var radius: CGFloat { DS.Radius.md - DS.Space.s1 / 2 } var body: some View { HStack(spacing: 0) { @@ -509,16 +590,17 @@ struct SegmentedControl: View { selection = option.value } label: { Text(option.title) - .dsFont(.sm, weight: selected ? .semibold : .regular) - .foregroundStyle(selected ? DS.Palette.textPrimary : DS.Palette.textSecondary) + .dsFont(.sm) + .foregroundStyle(selected ? DS.Palette.onPrimary : DS.Palette.textPrimary) .lineLimit(1) + .padding(.horizontal, DS.Space.s2) .frame(maxWidth: .infinity) - .frame(height: DS.Size.segmentHeight) + .frame(height: DS.Size.segmentHeight + DS.Space.s1) .background { if selected { - RoundedRectangle(cornerRadius: DS.Radius.md - DS.Space.s1) - .fill(DS.Palette.elevated) - .dsShadow(DS.Shadow.level1) + RoundedRectangle(cornerRadius: Self.radius, style: .continuous) + .fill(DS.Palette.primary) + .matchedGeometryEffect(id: "selection", in: namespace) } } .contentShape(Rectangle()) @@ -527,9 +609,8 @@ struct SegmentedControl: View { .accessibilityAddTraits(selected ? .isSelected : []) } } - .padding(DS.Space.s1) - .background(DS.Palette.track, in: RoundedRectangle(cornerRadius: DS.Radius.md)) - .animation(DS.Motion.quick, value: selection) + .background(DS.Palette.track, in: RoundedRectangle(cornerRadius: Self.radius, style: .continuous)) + .animation(.spring(response: 0.3, dampingFraction: 0.85), value: selection) } } @@ -538,8 +619,22 @@ struct SegmentedControl: View { struct DSToggle: View { @Binding var isOn: Bool var label: String = "" + @Environment(\.isSnapshot) private var isSnapshot var body: some View { + // 离屏截图时窗口不在前台,系统开关会画成灰色,截图里用自绘的蓝色开关 + if DS.Glass.isAvailable, !isSnapshot { + // macOS 26 的系统开关自带液态玻璃(拖动时滑块变成玻璃),颜色沿用品牌蓝 + Toggle(isOn: $isOn) { Text(label) } + .labelsHidden() + .toggleStyle(.switch) + .tint(DS.Palette.primary) + } else { + solidBody + } + } + + private var solidBody: some View { Button { isOn.toggle() } label: { @@ -785,6 +880,27 @@ struct ChipButton: View { @State private var hovering = false var body: some View { + if DS.Glass.isAvailable { + // macOS 26:每个选项是一颗玻璃胶囊,选中的是蓝色玻璃 + Button(action: action) { + Text(title) + .dsFont(.xs, weight: isSelected ? .semibold : .medium) + .foregroundStyle(isSelected ? DS.Palette.onPrimary : DS.Palette.textPrimary) + .lineLimit(1) + .frame(maxWidth: .infinity) + .frame(height: DS.Size.segmentHeight) + .contentShape(Capsule()) + .dsGlass(in: Capsule(), tint: isSelected ? DS.Palette.primary : nil, interactive: true) + } + .buttonStyle(.plain) + .animation(DS.Motion.quick, value: isSelected) + .accessibilityAddTraits(isSelected ? .isSelected : []) + } else { + solidBody + } + } + + private var solidBody: some View { Button(action: action) { Text(title) .dsFont(.xs, weight: isSelected ? .semibold : .medium) diff --git a/Packages/OpenStatsKit/Sources/OpenStatsUI/Design/LiquidGlass.swift b/Packages/OpenStatsKit/Sources/OpenStatsUI/Design/LiquidGlass.swift new file mode 100644 index 0000000..3f9de24 --- /dev/null +++ b/Packages/OpenStatsKit/Sources/OpenStatsUI/Design/LiquidGlass.swift @@ -0,0 +1,111 @@ +import Localization +import SwiftUI + +// MARK: - 液态玻璃 + +/// macOS 26 起,侧边栏、顶栏控件、按钮与切换控件用系统的液态玻璃;更早的系统退回原来的实色样式。 +/// 玻璃只给浮在内容之上的导航与控件用,卡片、设置分组这类内容本身保持实色(与系统设置一致)。 +extension EnvironmentValues { + /// 已经在一块玻璃里面(比如侧边栏面板):里面的控件不再叠一层玻璃,改用普通的悬停底色 + @Entry var isInsideGlass = false +} + +extension DS { + enum Glass { + /// 当前系统是否支持液态玻璃 + static var isAvailable: Bool { + if #available(macOS 26.0, *) { true } else { false } + } + } +} + +extension View { + /// 给控件加一层液态玻璃。`tint` 为品牌色时是蓝色玻璃(主按钮、选中项); + /// `interactive` 为真时按下会有玻璃的形变反馈。不支持玻璃的系统上、以及离屏截图时(拍不出玻璃)画 `fallback` 实色 + func dsGlass(in shape: S, tint: Color? = nil, interactive: Bool = false, + fallback: Color = DS.Palette.elevated) -> some View { + modifier(GlassSurface(shape: shape, tint: tint, interactive: interactive, fallback: fallback)) + } +} + +private struct GlassSurface: ViewModifier { + let shape: S + let tint: Color? + let interactive: Bool + let fallback: Color + @Environment(\.isSnapshot) private var isSnapshot + + func body(content: Content) -> some View { + if #available(macOS 26.0, *), !isSnapshot { + content.glassEffect(DS.Glass.style(tint: tint, interactive: interactive), in: shape) + } else { + content.background(tint ?? fallback, in: shape) + } + } +} + +@available(macOS 26.0, *) +extension DS.Glass { + static func style(tint: Color?, interactive: Bool) -> SwiftUI.Glass { + var glass = SwiftUI.Glass.regular + if let tint { glass = glass.tint(tint) } + if interactive { glass = glass.interactive() } + return glass + } +} + +// MARK: - 全局加载 + +/// 需要等待、期间不宜继续操作的任务(清理、卸载、导出诊断、修改 DNS): +/// 整个窗口压暗,中间浮一块玻璃加载框,写明正在做什么;任务结束自动消失 +struct LoadingHUD: View { + let message: String + + var body: some View { + ZStack { + // 压暗并拦住点击,避免任务进行中又触发别的操作 + Rectangle() + .fill(DS.Palette.scrim) + .contentShape(Rectangle()) + .onTapGesture {} + + HStack(spacing: DS.Space.s3) { + ProgressView().controlSize(.regular) + Text(verbatim: message) + .dsFont(.base, weight: .medium) + .foregroundStyle(DS.Palette.textPrimary) + .lineLimit(2) + .fixedSize(horizontal: false, vertical: true) + } + .padding(.horizontal, DS.Space.s8) + .padding(.vertical, DS.Space.s6) + .frame(minWidth: DS.Size.sidebarWidth + DS.Space.s16) + .modifier(HUDBackground()) + .accessibilityElement(children: .combine) + .accessibilityAddTraits(.updatesFrequently) + } + .transition(.opacity) + } +} + +private struct HUDBackground: ViewModifier { + func body(content: Content) -> some View { + if #available(macOS 26.0, *) { + content.glassEffect(.regular, in: RoundedRectangle(cornerRadius: DS.Radius.xl, style: .continuous)) + } else { + content + .background(DS.Palette.elevated, in: RoundedRectangle(cornerRadius: DS.Radius.xl, style: .continuous)) + .dsShadow(DS.Shadow.level2) + } + } +} + +extension View { + /// 有文字时在上方盖一层全局加载框 + func loadingHUD(_ message: String?) -> some View { + overlay { + if let message { LoadingHUD(message: message) } + } + .animation(DS.Motion.quick, value: message) + } +} diff --git a/Packages/OpenStatsKit/Sources/OpenStatsUI/Design/Tokens.swift b/Packages/OpenStatsKit/Sources/OpenStatsUI/Design/Tokens.swift index f6db19b..e046eb4 100644 --- a/Packages/OpenStatsKit/Sources/OpenStatsUI/Design/Tokens.swift +++ b/Packages/OpenStatsKit/Sources/OpenStatsUI/Design/Tokens.swift @@ -38,6 +38,10 @@ extension DS { static let neutral500 = Color.dynamic(light: 0x6B7280, dark: 0x4B5563) /// 浅一档的品牌蓝,用来区分同一图表里的第二类数据(如性能核) static let primarySoft = Color.dynamic(light: 0x93C5FD, dark: 0x60A5FA) + /// 侧边栏选中项的淡蓝底 + static let sidebarSelected = Color.dynamic(light: 0x2563EB, lightAlpha: 0.12, dark: 0x3B82F6, darkAlpha: 0.22) + /// 全局加载时压暗整个窗口 + static let scrim = Color.dynamic(light: 0x000000, lightAlpha: 0.12, dark: 0x000000, darkAlpha: 0.40) /// 核心类型配色:最高性能档为品牌蓝,其次浅蓝,能效核灰色 static func cluster(_ id: Int) -> Color { @@ -184,6 +188,7 @@ extension DS { } static let level1 = Level(color: .black.opacity(0.08), radius: 3, y: 1) + static let level2 = Level(color: .black.opacity(0.10), radius: 12, y: 4) } enum Motion { diff --git a/Packages/OpenStatsKit/Sources/OpenStatsUI/Panel/MainWindowView.swift b/Packages/OpenStatsKit/Sources/OpenStatsUI/Panel/MainWindowView.swift index 2f26806..505a546 100644 --- a/Packages/OpenStatsKit/Sources/OpenStatsUI/Panel/MainWindowView.swift +++ b/Packages/OpenStatsKit/Sources/OpenStatsUI/Panel/MainWindowView.swift @@ -3,8 +3,9 @@ import Localization import Metrics import SwiftUI -/// 主窗口:侧边栏、标题栏与页面在同一张底色上,不分栏着色,也不画分隔线。 -/// 左侧切换页面,右侧是仪表盘、各指标详情与工具页;窗口宽高都可调整 +/// 主窗口。左侧切换页面,右侧是仪表盘、各指标详情与工具页;窗口宽高都可调整。 +/// macOS 26 起侧边栏是一块浮在窗口里的液态玻璃面板,顶栏控件是玻璃胶囊; +/// 更早的系统上侧边栏、标题栏与页面在同一张底色上,不分栏着色,也不画分隔线 public struct MainWindowView: View { @Environment(AppModel.self) private var model @Environment(\.isSnapshot) private var isSnapshot @@ -19,55 +20,93 @@ public struct MainWindowView: View { .background(alignment: .top) { WindowDragArea().frame(height: DS.Size.windowHeader) } + .modifier(SidebarPanel()) - VStack(spacing: 0) { - // 页面的滚动视图会自动向上延伸到标题栏下面,与顶栏重叠;顶栏必须在它之上,否则开关收不到点击 - PageHeader() - .zIndex(1) - Group { - switch model.settings.panelTab { - case .overview: OverviewPage() - case .system: SystemInfoPage() - case .history: HistoryPage() - case .cpu: DetailPage { CPUPopover() } - case .gpu: DetailPage { GPUPopover() } - case .memory: DetailPage { MemoryPopover() } - case .disk: DiskPage() - case .network: - DetailPage { - NetworkPopover() - NetworkSettings() - } - case .thermal: ThermalPage() - case .battery: DetailPage { BatteryPopover() } - case .processes: ProcessesPage() - case .keepAwake: KeepAwakePage() - case .cleaner: CleanerPage() - case .uninstaller: UninstallerPage() - case .startupItems: StartupItemsPage() - case .settingsGeneral: SettingsTabPage { GeneralSettings() } - case .settingsMenuBar: SettingsTabPage { MenuBarSettings() } - case .settingsNotifications: SettingsTabPage { NotificationSettings() } - case .settingsAccount: SettingsTabPage { AccountSettings() } - case .settingsHelper: SettingsTabPage { HelperSettings() } - case .settingsAbout: SettingsTabPage { AboutSettings() } - } - } - .frame(maxWidth: .infinity, maxHeight: isSnapshot ? nil : .infinity, alignment: .top) - // 截图时页面取自身高度:侧边栏更高时,多出的高度不能分给页面里可伸展的卡片 - .fixedSize(horizontal: false, vertical: isSnapshot) - } - .frame(minWidth: DS.Size.panelWidth, maxWidth: .infinity) - .frame(maxHeight: isSnapshot ? nil : .infinity, alignment: .top) + content + .frame(minWidth: DS.Size.panelWidth, maxWidth: .infinity) + .frame(maxHeight: isSnapshot ? nil : .infinity, alignment: .top) } .frame(width: isSnapshot ? DS.Size.sidebarWidth + DS.Size.panelWidth : nil) .fixedSize(horizontal: false, vertical: isSnapshot) .background(DS.Palette.background) + // 需要等待的任务进行中,整个窗口压暗并显示加载框 + .loadingHUD(isSnapshot ? nil : busyMessage) // 内容延伸到透明标题栏下方,由顶栏高度留出红绿灯按钮的位置 .ignoresSafeArea() // 文案在各视图计算时翻译好,切换语言后整棵视图重建 .id(model.settings.language) } + + /// 顶栏在上、页面在下。没有用 macOS 26 的 safeAreaBar 让页面从顶栏下面滚过: + /// 它在 macOS 上会在交界处画一条分隔线,滚动边缘样式设成柔和或隐藏都去不掉 + private var content: some View { + VStack(spacing: 0) { + // 页面的滚动视图会自动向上延伸到标题栏下面,与顶栏重叠;顶栏必须在它之上,否则开关收不到点击 + PageHeader() + .zIndex(1) + page + } + } + + /// 需要等待、期间不宜继续操作的任务 + private var busyMessage: String? { + if model.cleaner.phase == .cleaning { return tr("正在清理…") } + if model.uninstaller.isRemoving { return tr("正在移除…") } + if model.diagnostics.phase == .collecting { return tr("正在导出诊断信息…") } + if model.maintenance.isApplyingDNS { return tr("正在修改 DNS…") } + return nil + } + + private var page: some View { + Group { + switch model.settings.panelTab { + case .overview: OverviewPage() + case .system: SystemInfoPage() + case .history: HistoryPage() + case .cpu: DetailPage { CPUPopover() } + case .gpu: DetailPage { GPUPopover() } + case .memory: DetailPage { MemoryPopover() } + case .disk: DiskPage() + case .network: + DetailPage { + NetworkPopover() + NetworkSettings() + } + case .thermal: ThermalPage() + case .battery: DetailPage { BatteryPopover() } + case .processes: ProcessesPage() + case .keepAwake: KeepAwakePage() + case .cleaner: CleanerPage() + case .uninstaller: UninstallerPage() + case .startupItems: StartupItemsPage() + case .settingsGeneral: SettingsTabPage { GeneralSettings() } + case .settingsMenuBar: SettingsTabPage { MenuBarSettings() } + case .settingsNotifications: SettingsTabPage { NotificationSettings() } + case .settingsAccount: SettingsTabPage { AccountSettings() } + case .settingsHelper: SettingsTabPage { HelperSettings() } + case .settingsAbout: SettingsTabPage { AboutSettings() } + } + } + .frame(maxWidth: .infinity, maxHeight: isSnapshot ? nil : .infinity, alignment: .top) + // 截图时页面取自身高度:侧边栏更高时,多出的高度不能分给页面里可伸展的卡片 + .fixedSize(horizontal: false, vertical: isSnapshot) + } +} + +/// macOS 26:侧边栏浮在窗口里,四周留一圈边距,整块是液态玻璃,红绿灯按钮落在面板里 +private struct SidebarPanel: ViewModifier { + @Environment(\.isSnapshot) private var isSnapshot + + func body(content: Content) -> some View { + if DS.Glass.isAvailable, !isSnapshot { + content + .environment(\.isInsideGlass, true) + .dsGlass(in: RoundedRectangle(cornerRadius: DS.Radius.xl, style: .continuous)) + .padding(DS.Space.s2) + } else { + content + } + } } /// 指标详情页:复用菜单栏弹窗的内容,显示全部区块 @@ -104,11 +143,13 @@ private struct MainSidebar: View { } .padding(.leading, DS.Space.s1) .padding(.trailing, DS.Space.s3) - // 顶部留出窗口红绿灯按钮的位置 - .padding(.top, DS.Size.windowHeader + DS.Space.s2) + // 顶部留出窗口红绿灯按钮的位置;玻璃面板本身离窗口顶边已有一圈边距 + .padding(.top, DS.Size.windowHeader + (usesGlassPanel ? 0 : DS.Space.s2)) .padding(.bottom, DS.Space.s3) } + private var usesGlassPanel: Bool { DS.Glass.isAvailable && !isSnapshot } + private var navigation: some View { VStack(alignment: .leading, spacing: DS.Space.s1) { group(tr("监控"), PanelTab.monitors) @@ -171,15 +212,19 @@ private struct PageHeader: View { // 每个监控页都能在这里开关自己的菜单栏项目;温度与风扇页有两项,各带一个小标签 let items = tab.menuBarItems if !items.isEmpty { - Text(tr("在菜单栏显示")).dsFont(.xs).foregroundStyle(DS.Palette.textSecondary) - ForEach(items) { item in - if items.count > 1 { - Text(item.popoverTitle).dsFont(.xs).foregroundStyle(DS.Palette.textTertiary) + // macOS 26 上这一组是一颗玻璃胶囊,与访达工具栏的分组一致 + HStack(spacing: DS.Space.s3) { + Text(tr("在菜单栏显示")).dsFont(.xs).foregroundStyle(DS.Palette.textSecondary) + ForEach(items) { item in + if items.count > 1 { + Text(item.popoverTitle).dsFont(.xs).foregroundStyle(DS.Palette.textTertiary) + } + DSToggle(isOn: Binding(get: { settings.isEnabled(item) }, set: { settings.setEnabled(item, $0) }), + label: tr("在菜单栏显示\(item.title)")) + .help(tr("开启后图标出现在菜单栏;按住 ⌘ 键拖动图标可以调整位置")) } - DSToggle(isOn: Binding(get: { settings.isEnabled(item) }, set: { settings.setEnabled(item, $0) }), - label: tr("在菜单栏显示\(item.title)")) - .help(tr("开启后图标出现在菜单栏;按住 ⌘ 键拖动图标可以调整位置")) } + .modifier(HeaderControlGroup()) } } .padding(.horizontal, DS.Space.s3 + DS.Space.s1) @@ -187,6 +232,21 @@ private struct PageHeader: View { } } +/// 顶栏右侧的一组控件:macOS 26 上收进一颗玻璃胶囊,更早的系统直接排开 +private struct HeaderControlGroup: ViewModifier { + func body(content: Content) -> some View { + if DS.Glass.isAvailable { + content + .padding(.leading, DS.Space.s4) + .padding(.trailing, DS.Space.s2) + .frame(height: DS.Size.controlHeight) + .dsGlass(in: Capsule()) + } else { + content + } + } +} + /// 在浅色与深色之间切换;当前跟随系统时,切到与系统相反的那一种 private struct ThemeToggle: View { @Environment(AppModel.self) private var model diff --git a/Packages/OpenStatsKit/Sources/OpenStatsUI/Popover/PopoverRootView.swift b/Packages/OpenStatsKit/Sources/OpenStatsUI/Popover/PopoverRootView.swift index 2e2e42b..87a6d3e 100644 --- a/Packages/OpenStatsKit/Sources/OpenStatsUI/Popover/PopoverRootView.swift +++ b/Packages/OpenStatsKit/Sources/OpenStatsUI/Popover/PopoverRootView.swift @@ -187,8 +187,8 @@ struct MiniIconButton: View { .font(.system(size: DS.TextSize.xs.rawValue, weight: .semibold)) .foregroundStyle(hovering ? DS.Palette.textPrimary : DS.Palette.textSecondary) .frame(width: DS.Size.segmentHeight, height: DS.Size.segmentHeight) - .background(hovering ? DS.Palette.surfaceHover : .clear, in: RoundedRectangle(cornerRadius: DS.Radius.sm)) - .contentShape(Rectangle()) + .modifier(MiniIconSurface(hovering: hovering)) + .contentShape(Circle()) } .buttonStyle(.plain) .onHover { hovering = $0 } @@ -197,6 +197,19 @@ struct MiniIconButton: View { } } +/// macOS 26 上是一颗小玻璃圆钮,更早的系统只有悬停底色 +private struct MiniIconSurface: ViewModifier { + let hovering: Bool + + func body(content: Content) -> some View { + if DS.Glass.isAvailable { + content.dsGlass(in: Circle(), interactive: true) + } else { + content.background(hovering ? DS.Palette.surfaceHover : .clear, in: RoundedRectangle(cornerRadius: DS.Radius.sm)) + } + } +} + /// 区块标题栏里的刷新按钮:查询进行中换成系统的转圈,查完自动变回图标 struct RefreshButton: View { var loading: Bool diff --git a/README.md b/README.md index 9444da0..364f30d 100644 --- a/README.md +++ b/README.md @@ -59,9 +59,23 @@ system language by default; switch it in Settings → General → Language. -Latest release **0.3.2** (2026-09-16) · [full changelog](CHANGELOG.md) (kept in Chinese) +Latest release **0.3.2** (2026-09-16) · **3** changes in development · [full changelog](CHANGELOG.md) (kept in Chinese)
+2026-09-16 · Unreleased · 2 style · 1 added + +**Style** + +- 主窗口在 macOS 26 上改用液态玻璃:侧边栏是一块浮在窗口里的玻璃面板,红绿灯按钮落在面板里,选中项垫一块淡蓝色圆角底并随切换滑动;顶栏右侧“在菜单栏显示”一组收进玻璃胶囊。主按钮是蓝色玻璃胶囊、次要按钮与图标按钮是透明玻璃,风扇模式等快捷切换是玻璃胶囊,开关换成系统开关(拖动时滑块变成玻璃)。设置分组与数据卡片保持实色,与系统设置一致。macOS 14、15 上保持原来的样式。 +- 分段切换改为苹果标准样式:灰色圆角底槽,各段等宽铺满,选中的一段是实心蓝色、白字,切换时蓝块滑过去;网络详情的 IPv4 / IPv6 切换同样是灰底蓝选中。 + +**Added** + +- 全局加载框:清理、卸载应用、导出诊断信息、修改 DNS 进行中,整个主窗口压暗,中间浮一块加载框写明正在做什么(macOS 26 上是液态玻璃),期间不能误点其他操作,完成后自动消失。 + +
+ +
2026-09-16 · 0.3.2 · 2 fixed **Fixed** @@ -81,49 +95,6 @@ Latest release **0.3.2** (2026-09-16) · [full changelog](CHANGELOG.md) (kept in
-
-2026-09-15 · 0.3.0 · 8 added · 14 changed · 3 fixed - -**Added** - -- 菜单栏新增“电池”项目:电量按九种风格显示(电量条、圆环、饼图、数字等),充电中带闪电,电量低于 20% 按“高负载时着色”变红。点击弹出电池详情:电量与剩余 / 充满时间、适配器功率、电池温度、最近 24 小时电量曲线、功耗、健康度与循环次数、已连接蓝牙设备的电量(AirPods 左右耳与充电盒、妙控键盘 / 鼠标 / 触控板),可跳到系统电池设置;主窗口也有对应的“电池”页,历史页多了电池电量曲线。没有电池的 Mac(mini、Studio、iMac)上这个项目改为显示电量最低的蓝牙设备,弹窗只列蓝牙设备。「设置 · 菜单栏」新增“蓝牙设备电量低时提示”:某个设备低于 20% 时在电池项目旁显示它的图标与电量。 -- 网络详情“接口”区块右上角新增重置按钮:把开机后下载 / 上传归零、从现在起重新累计,标题旁标出起算时间,两行改叫“重置后下载 / 上传”;重启后自动回到开机后累计,右键按钮可随时改回。 -- 磁盘页从展示页变成能动手的磁盘工具,新增四张卡片: - - 空间占用:统计家目录里每个文件夹占多少,并列出最大的 20 个文件(应用、照片图库等按整体算一个);可在访达中显示,家目录里、Library 之外的文件可以直接移到废纸篓。只读扫描,文件多时需要几十秒,可随时停止。 - - 文件系统检查:调用系统的 diskutil 对启动盘做一次只读检查,相当于“磁盘工具”的急救但不修改任何东西;记住上次检查的时间与结果,发现问题时提示去磁盘工具修复。 - - 本地快照:列出 Time Machine 留在本机的快照(它们计入“可清除”空间),可一键全部删除;辅助工具够新时由它执行,否则请求一次管理员授权。辅助工具协议升到第 4 版,已安装的旧版会提示重新安装。 - - 其他磁盘:列出外置硬盘、U 盘、镜像与网络共享的容量,可在访达中显示或直接推出;接入、拔出时自动刷新。 -- 网络详情新增「IP 纯净度」区块:CleanIP.io 的纯净度评分与等级画成占满整行的 F 到 A+ 六段色带,分数在色带上方、得分处有标记,同一行右侧是 cleanip.io 字标,点了打开这个 IP 的完整报告;下面列出风险评分、命中的风险标记(VPN、代理、Tor、机房、滥用记录等)与一句评价。IP 地址区块用 cleanip.io 样式的徽章标出原生 / 广播、住宅 / 机房与运营商类型(ISP 等),徽章样式作为通用组件收进设计系统。 -- 账号与设置同步(设置 · 账号与同步):用 GitHub、Google 或 Apple 登录后,菜单栏项目与风格、刷新频率、外观与语言、详情弹窗的区块、连接探测、通知、快捷键、风扇安全温度、合盖电量下限会保存到 getopenstats.com,换一台 Mac 登录即自动恢复。设置改动 2 秒后上传,启动、唤醒与每 15 分钟检查一次云端;第一次登录时本机与云端都有内容且不同,会让你选用哪一份,之后以最后写入为准。登录走系统的授权窗口,令牌存在钥匙串,云端只保存邮箱、姓名与设置文档,可随时退出或删除云端数据。不登录时应用行为不变。 -- CPU 走势时长可选 1 / 3 / 5 分钟:走势线上方一行小字切换,弹窗与主窗口共用;曲线按真实采样时间定位,最新一次采样固定在右边缘,睡眠等超过 10 秒没有采样的地方线条断开。 -- 菜单栏新增“磁盘”项目:显示启动磁盘已用占比,八种风格都可用;点击弹出磁盘详情(容量与可用空间、读写速度、SSD 健康、读写最多的应用),区块可在设置里逐个隐藏。主窗口磁盘页右上角同样可以直接开关,“温度与风扇”页右上角现在有温度、风扇两个开关。 -- CPU 详情新增“各核心占用”区块:主窗口里每个核心一个小圆环,中间写百分比,按“核心 1…N”编号并按类型分组,超过 60% 变橙、85% 变红;弹窗里是每核一根柱子的紧凑版,默认关闭,可在“设置 · 菜单栏”的 CPU 弹窗显示里打开。 - -**Changed** - -- 公网 IP 的归属地改用 cleanip.io:去掉本地 MaxMind GeoLite2 数据库(含下载、导入、自动更新的整个“IP 归属地数据库”设置区块);归属地、ASN、网络类型与纯净度都由这台 Mac 直接向 cleanip.io 查询、不经过我们的服务器,只发送公网地址。 -- 「设置 · 菜单栏」每个显示项目下面多了一行“菜单栏风格”:给这一项单独选一种风格(默认跟随整体,菜单里标出整体现在是哪种),旁边是这一项按当前风格、实时读数画出的预览,改了立刻能看到。以前这个选择藏在开关旁一个没有说明的下拉里,不容易发现。温度、风扇只提供文字类的三种风格;原“风格”分组改名“整体风格”并加了说明。 -- 菜单栏弹窗右上角的两个按钮:左边改为该指标自己的图标(网络是网络图标、磁盘是硬盘),点了直接进主窗口里这个指标的页面;右边的齿轮进总设置。 -- 公网 IP 双栈支持:IPv4 与 IPv6 各自查询归属地与纯净度、各自缓存;两族都有时 IP 地址区块用胶囊开关切换 IPv4 / IPv6。纯净度区块的标题一行排开:地址族徽章(只有一族时不标)、置信度徽章(高绿、中灰、低黄)、完整报告链接,右侧是刷新按钮;IP 地址区块也有同样的刷新按钮,都是忽略缓存立即重查,查询进行中按钮变成系统的转圈。 -- 公网 IP 的归属地结果保存在本机:点开网络详情立刻显示上次的结果,后台只向 Cloudflare 核对一下地址;地址没变且不满 7 天就不再查归属地,换了 IP、超过 7 天或点了刷新才重新查。 -- 磁盘页与磁盘弹窗的“读取 / 写入”两组数值各占一半宽度,数字长短变化时位置不再左右挪动。 -- 磁盘“读写最多的应用”与网络详情“高占用进程”改为稳定的 5 条榜单:按最近十几秒的平均速率排序和画条,数字仍是当前速率;刚安静下来的应用会在榜上停留几秒再退出,不再随每秒的波动忽隐忽现、上下乱跳。 -- 磁盘页与磁盘弹窗的容量改为分段条:已用、可清除、可用三段按比例排在一条里,段内直接标名称与百分比,下方图例给出各自的容量;可清除是系统随时可以腾出的缓存,访达的“可用”把它算在内。 -- 磁盘页“读写最多的应用”与 CPU、内存详情“按应用汇总”的排行条不再画灰色底槽,只保留代表相对占比的蓝色条。 -- 「设置 · 菜单栏」的显示项目上方加了一条说明:按住 ⌘ 拖动可以调整菜单栏图标的顺序,新开启的项目由系统安排位置;各页面右上角的开关悬停时也有同样的提示。官网常见问题同步补充。 -- 网络详情的 DNS 切换改为下拉菜单:“配置方式”一行直接是菜单,选 Cloudflare、Google 等预设立即切换,选“手动”展开输入框,自定义地址时旁边有铅笔按钮可再次修改;不再显示六个并排的按钮,省下两行。 -- 主窗口的 CPU 走势图加上 0–100% 刻度与起点、中点、现在三个时间标签。 -- CPU 详情的解释性文字不再常驻:热力图怎么看、每类核心是干什么的、平均负载怎么理解、按应用汇总为什么会超过 100%,都收进区块标题旁的 ⓘ,鼠标悬停 ⓘ 或卡片内容时才显示,界面上只留数据。 -- 核心类型说人话:核心分工的悬停说明写明每类核心是干什么的(最快的处理重活、更省电的负责后台和轻量任务,系统自动分配);各核心占用的分组标题、热力图的行标签都带上核心数量,热力图说明写明“每行一个核心(共 N 个),颜色越深越忙”;最忙的核心改为“核心 7(性能核)”这种统一编号。 - -**Fixed** - -- 菜单栏弹窗的内容整体偏左、右边留白更宽:接了鼠标时系统默认常驻滚动条,滚动区域给它预留了一条宽度。现在应用内一律用浮层滚动条,内容占满整个弹窗宽度,主窗口页面同样处理。 -- 网络详情里的归属地国旗画错:中国、乌兹别克斯坦等 63 面旗子的 SVG 用了嵌套引用,系统渲染器画成一大块白。现在国旗改为预先渲染好的 PNG,全部按参考图核对过。 -- 各监控页右上角的“在菜单栏显示”开关点不动:页面的滚动区域会自动向上延伸到顶栏底下,把开关的点击截走了;现在顶栏盖在滚动区域之上,GPU、磁盘等页面的开关可以正常点击。 - -
- ## Activity diff --git a/README.zh-CN.md b/README.zh-CN.md index ca73204..6337d68 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -55,9 +55,23 @@ CPU 不分性能核与能效核,部分功耗与频率读数可能不显示。 -最新版本 **0.3.2**(2026-09-16) · [完整更新日志](CHANGELOG.md) +最新版本 **0.3.2**(2026-09-16) · 开发中 **3** 项改动尚未发布 · [完整更新日志](CHANGELOG.md)
+2026-09-16 · 未发布 · 样式 2 · 新增 1 + +**样式** + +- 主窗口在 macOS 26 上改用液态玻璃:侧边栏是一块浮在窗口里的玻璃面板,红绿灯按钮落在面板里,选中项垫一块淡蓝色圆角底并随切换滑动;顶栏右侧“在菜单栏显示”一组收进玻璃胶囊。主按钮是蓝色玻璃胶囊、次要按钮与图标按钮是透明玻璃,风扇模式等快捷切换是玻璃胶囊,开关换成系统开关(拖动时滑块变成玻璃)。设置分组与数据卡片保持实色,与系统设置一致。macOS 14、15 上保持原来的样式。 +- 分段切换改为苹果标准样式:灰色圆角底槽,各段等宽铺满,选中的一段是实心蓝色、白字,切换时蓝块滑过去;网络详情的 IPv4 / IPv6 切换同样是灰底蓝选中。 + +**新增** + +- 全局加载框:清理、卸载应用、导出诊断信息、修改 DNS 进行中,整个主窗口压暗,中间浮一块加载框写明正在做什么(macOS 26 上是液态玻璃),期间不能误点其他操作,完成后自动消失。 + +
+ +
2026-09-16 · 0.3.2 · 修复 2 **修复** @@ -77,49 +91,6 @@ CPU 不分性能核与能效核,部分功耗与频率读数可能不显示。
-
-2026-09-15 · 0.3.0 · 新增 8 · 调整 14 · 修复 3 - -**新增** - -- 菜单栏新增“电池”项目:电量按九种风格显示(电量条、圆环、饼图、数字等),充电中带闪电,电量低于 20% 按“高负载时着色”变红。点击弹出电池详情:电量与剩余 / 充满时间、适配器功率、电池温度、最近 24 小时电量曲线、功耗、健康度与循环次数、已连接蓝牙设备的电量(AirPods 左右耳与充电盒、妙控键盘 / 鼠标 / 触控板),可跳到系统电池设置;主窗口也有对应的“电池”页,历史页多了电池电量曲线。没有电池的 Mac(mini、Studio、iMac)上这个项目改为显示电量最低的蓝牙设备,弹窗只列蓝牙设备。「设置 · 菜单栏」新增“蓝牙设备电量低时提示”:某个设备低于 20% 时在电池项目旁显示它的图标与电量。 -- 网络详情“接口”区块右上角新增重置按钮:把开机后下载 / 上传归零、从现在起重新累计,标题旁标出起算时间,两行改叫“重置后下载 / 上传”;重启后自动回到开机后累计,右键按钮可随时改回。 -- 磁盘页从展示页变成能动手的磁盘工具,新增四张卡片: - - 空间占用:统计家目录里每个文件夹占多少,并列出最大的 20 个文件(应用、照片图库等按整体算一个);可在访达中显示,家目录里、Library 之外的文件可以直接移到废纸篓。只读扫描,文件多时需要几十秒,可随时停止。 - - 文件系统检查:调用系统的 diskutil 对启动盘做一次只读检查,相当于“磁盘工具”的急救但不修改任何东西;记住上次检查的时间与结果,发现问题时提示去磁盘工具修复。 - - 本地快照:列出 Time Machine 留在本机的快照(它们计入“可清除”空间),可一键全部删除;辅助工具够新时由它执行,否则请求一次管理员授权。辅助工具协议升到第 4 版,已安装的旧版会提示重新安装。 - - 其他磁盘:列出外置硬盘、U 盘、镜像与网络共享的容量,可在访达中显示或直接推出;接入、拔出时自动刷新。 -- 网络详情新增「IP 纯净度」区块:CleanIP.io 的纯净度评分与等级画成占满整行的 F 到 A+ 六段色带,分数在色带上方、得分处有标记,同一行右侧是 cleanip.io 字标,点了打开这个 IP 的完整报告;下面列出风险评分、命中的风险标记(VPN、代理、Tor、机房、滥用记录等)与一句评价。IP 地址区块用 cleanip.io 样式的徽章标出原生 / 广播、住宅 / 机房与运营商类型(ISP 等),徽章样式作为通用组件收进设计系统。 -- 账号与设置同步(设置 · 账号与同步):用 GitHub、Google 或 Apple 登录后,菜单栏项目与风格、刷新频率、外观与语言、详情弹窗的区块、连接探测、通知、快捷键、风扇安全温度、合盖电量下限会保存到 getopenstats.com,换一台 Mac 登录即自动恢复。设置改动 2 秒后上传,启动、唤醒与每 15 分钟检查一次云端;第一次登录时本机与云端都有内容且不同,会让你选用哪一份,之后以最后写入为准。登录走系统的授权窗口,令牌存在钥匙串,云端只保存邮箱、姓名与设置文档,可随时退出或删除云端数据。不登录时应用行为不变。 -- CPU 走势时长可选 1 / 3 / 5 分钟:走势线上方一行小字切换,弹窗与主窗口共用;曲线按真实采样时间定位,最新一次采样固定在右边缘,睡眠等超过 10 秒没有采样的地方线条断开。 -- 菜单栏新增“磁盘”项目:显示启动磁盘已用占比,八种风格都可用;点击弹出磁盘详情(容量与可用空间、读写速度、SSD 健康、读写最多的应用),区块可在设置里逐个隐藏。主窗口磁盘页右上角同样可以直接开关,“温度与风扇”页右上角现在有温度、风扇两个开关。 -- CPU 详情新增“各核心占用”区块:主窗口里每个核心一个小圆环,中间写百分比,按“核心 1…N”编号并按类型分组,超过 60% 变橙、85% 变红;弹窗里是每核一根柱子的紧凑版,默认关闭,可在“设置 · 菜单栏”的 CPU 弹窗显示里打开。 - -**调整** - -- 公网 IP 的归属地改用 cleanip.io:去掉本地 MaxMind GeoLite2 数据库(含下载、导入、自动更新的整个“IP 归属地数据库”设置区块);归属地、ASN、网络类型与纯净度都由这台 Mac 直接向 cleanip.io 查询、不经过我们的服务器,只发送公网地址。 -- 「设置 · 菜单栏」每个显示项目下面多了一行“菜单栏风格”:给这一项单独选一种风格(默认跟随整体,菜单里标出整体现在是哪种),旁边是这一项按当前风格、实时读数画出的预览,改了立刻能看到。以前这个选择藏在开关旁一个没有说明的下拉里,不容易发现。温度、风扇只提供文字类的三种风格;原“风格”分组改名“整体风格”并加了说明。 -- 菜单栏弹窗右上角的两个按钮:左边改为该指标自己的图标(网络是网络图标、磁盘是硬盘),点了直接进主窗口里这个指标的页面;右边的齿轮进总设置。 -- 公网 IP 双栈支持:IPv4 与 IPv6 各自查询归属地与纯净度、各自缓存;两族都有时 IP 地址区块用胶囊开关切换 IPv4 / IPv6。纯净度区块的标题一行排开:地址族徽章(只有一族时不标)、置信度徽章(高绿、中灰、低黄)、完整报告链接,右侧是刷新按钮;IP 地址区块也有同样的刷新按钮,都是忽略缓存立即重查,查询进行中按钮变成系统的转圈。 -- 公网 IP 的归属地结果保存在本机:点开网络详情立刻显示上次的结果,后台只向 Cloudflare 核对一下地址;地址没变且不满 7 天就不再查归属地,换了 IP、超过 7 天或点了刷新才重新查。 -- 磁盘页与磁盘弹窗的“读取 / 写入”两组数值各占一半宽度,数字长短变化时位置不再左右挪动。 -- 磁盘“读写最多的应用”与网络详情“高占用进程”改为稳定的 5 条榜单:按最近十几秒的平均速率排序和画条,数字仍是当前速率;刚安静下来的应用会在榜上停留几秒再退出,不再随每秒的波动忽隐忽现、上下乱跳。 -- 磁盘页与磁盘弹窗的容量改为分段条:已用、可清除、可用三段按比例排在一条里,段内直接标名称与百分比,下方图例给出各自的容量;可清除是系统随时可以腾出的缓存,访达的“可用”把它算在内。 -- 磁盘页“读写最多的应用”与 CPU、内存详情“按应用汇总”的排行条不再画灰色底槽,只保留代表相对占比的蓝色条。 -- 「设置 · 菜单栏」的显示项目上方加了一条说明:按住 ⌘ 拖动可以调整菜单栏图标的顺序,新开启的项目由系统安排位置;各页面右上角的开关悬停时也有同样的提示。官网常见问题同步补充。 -- 网络详情的 DNS 切换改为下拉菜单:“配置方式”一行直接是菜单,选 Cloudflare、Google 等预设立即切换,选“手动”展开输入框,自定义地址时旁边有铅笔按钮可再次修改;不再显示六个并排的按钮,省下两行。 -- 主窗口的 CPU 走势图加上 0–100% 刻度与起点、中点、现在三个时间标签。 -- CPU 详情的解释性文字不再常驻:热力图怎么看、每类核心是干什么的、平均负载怎么理解、按应用汇总为什么会超过 100%,都收进区块标题旁的 ⓘ,鼠标悬停 ⓘ 或卡片内容时才显示,界面上只留数据。 -- 核心类型说人话:核心分工的悬停说明写明每类核心是干什么的(最快的处理重活、更省电的负责后台和轻量任务,系统自动分配);各核心占用的分组标题、热力图的行标签都带上核心数量,热力图说明写明“每行一个核心(共 N 个),颜色越深越忙”;最忙的核心改为“核心 7(性能核)”这种统一编号。 - -**修复** - -- 菜单栏弹窗的内容整体偏左、右边留白更宽:接了鼠标时系统默认常驻滚动条,滚动区域给它预留了一条宽度。现在应用内一律用浮层滚动条,内容占满整个弹窗宽度,主窗口页面同样处理。 -- 网络详情里的归属地国旗画错:中国、乌兹别克斯坦等 63 面旗子的 SVG 用了嵌套引用,系统渲染器画成一大块白。现在国旗改为预先渲染好的 PNG,全部按参考图核对过。 -- 各监控页右上角的“在菜单栏显示”开关点不动:页面的滚动区域会自动向上延伸到顶栏底下,把开关的点击截走了;现在顶栏盖在滚动区域之上,GPU、磁盘等页面的开关可以正常点击。 - -
- ## 活跃度 diff --git a/project.yml b/project.yml index 20bb1f3..ee609bd 100644 --- a/project.yml +++ b/project.yml @@ -13,7 +13,7 @@ settings: SWIFT_VERSION: "6.0" MARKETING_VERSION: "0.3.2" # 构建号四位数字,每次 make build 加一;营销版本只在发布时用 Scripts/version.sh patch / minor 修改 - CURRENT_PROJECT_VERSION: "0055" + CURRENT_PROJECT_VERSION: "0067" MACOSX_DEPLOYMENT_TARGET: "14.0" # 本地开发使用 ad-hoc 签名;发布时改为 Developer ID(见 docs/PLAN.md) CODE_SIGN_STYLE: Manual