主窗口改用液态玻璃(macOS 26),分段切换改为苹果标准样式,新增全局加载框

- 新增 Design/LiquidGlass.swift:dsGlass 修饰器(macOS 26 用 glassEffect,更早系统与离屏截图退回实色)、isInsideGlass 环境值避免玻璃叠玻璃、LoadingHUD 全局加载框
- 侧边栏是浮在窗口里的玻璃面板,选中项为滑动的淡蓝圆角底;顶栏“在菜单栏显示”一组收进玻璃胶囊
- DSButtonStyle:主按钮蓝色玻璃胶囊、次要按钮透明玻璃;IconButton / MiniIconButton 为圆形玻璃;ChipButton 为玻璃胶囊;DSToggle 在 macOS 26 用系统开关
- SegmentedControl 统一为苹果标准样式:灰色圆角底槽、等宽分段、选中段实心品牌蓝白字并滑动;PillSwitch 同样灰底
- 清理、卸载、导出诊断、修改 DNS 进行中显示全局加载框
- 没有用 safeAreaBar 让页面从顶栏下滚过:macOS 上交界处会画一条分隔线,柔和或隐藏滚动边缘都去不掉
This commit is contained in:
gentpan committed 2026-09-16 18:26:09 +05:00
1 parent f3c0f860cb
commit d68fbce42a
10 files changed
+421 -160

No files matched your search

+13
View File
@@ -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
@@ -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…
@@ -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<Value: Hashable>: 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<Value: Hashable>: 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<Value: Hashable>: 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<Value: Hashable>: 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)
@@ -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<S: InsettableShape>(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<S: InsettableShape>: 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)
}
}
@@ -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 {
@@ -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
@@ -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
+15 -44
View File
@@ -59,9 +59,23 @@ system language by default; switch it in Settings → General → Language.
<!-- changelog:start -->
<!-- Generated from CHANGELOG.md by Scripts/sync_changelog.py. Do not edit by hand. -->
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)
<details open>
<summary><b>2026-09-16</b> · Unreleased · 2 style · 1 added</summary>
**Style**
- 主窗口在 macOS 26 上改用液态玻璃:侧边栏是一块浮在窗口里的玻璃面板,红绿灯按钮落在面板里,选中项垫一块淡蓝色圆角底并随切换滑动;顶栏右侧“在菜单栏显示”一组收进玻璃胶囊。主按钮是蓝色玻璃胶囊、次要按钮与图标按钮是透明玻璃,风扇模式等快捷切换是玻璃胶囊,开关换成系统开关(拖动时滑块变成玻璃)。设置分组与数据卡片保持实色,与系统设置一致。macOS 14、15 上保持原来的样式。
- 分段切换改为苹果标准样式:灰色圆角底槽,各段等宽铺满,选中的一段是实心蓝色、白字,切换时蓝块滑过去;网络详情的 IPv4 / IPv6 切换同样是灰底蓝选中。
**Added**
- 全局加载框:清理、卸载应用、导出诊断信息、修改 DNS 进行中,整个主窗口压暗,中间浮一块加载框写明正在做什么(macOS 26 上是液态玻璃),期间不能误点其他操作,完成后自动消失。
</details>
<details>
<summary><b>2026-09-16</b> · 0.3.2 · 2 fixed</summary>
**Fixed**
@@ -81,49 +95,6 @@ Latest release **0.3.2** (2026-09-16) · [full changelog](CHANGELOG.md) (kept in
</details>
<details>
<summary><b>2026-09-15</b> · 0.3.0 · 8 added · 14 changed · 3 fixed</summary>
**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、磁盘等页面的开关可以正常点击。
</details>
<!-- changelog:end -->
## Activity
+15 -44
View File
@@ -55,9 +55,23 @@ CPU 不分性能核与能效核,部分功耗与频率读数可能不显示。
<!-- changelog:start -->
<!-- 由 Scripts/sync_changelog.py 从 CHANGELOG.md 生成,请勿手改。 -->
最新版本 **0.3.2**(2026-09-16) · [完整更新日志](CHANGELOG.md)
最新版本 **0.3.2**(2026-09-16) · 开发中 **3** 项改动尚未发布 · [完整更新日志](CHANGELOG.md)
<details open>
<summary><b>2026-09-16</b> · 未发布 · 样式 2 · 新增 1</summary>
**样式**
- 主窗口在 macOS 26 上改用液态玻璃:侧边栏是一块浮在窗口里的玻璃面板,红绿灯按钮落在面板里,选中项垫一块淡蓝色圆角底并随切换滑动;顶栏右侧“在菜单栏显示”一组收进玻璃胶囊。主按钮是蓝色玻璃胶囊、次要按钮与图标按钮是透明玻璃,风扇模式等快捷切换是玻璃胶囊,开关换成系统开关(拖动时滑块变成玻璃)。设置分组与数据卡片保持实色,与系统设置一致。macOS 14、15 上保持原来的样式。
- 分段切换改为苹果标准样式:灰色圆角底槽,各段等宽铺满,选中的一段是实心蓝色、白字,切换时蓝块滑过去;网络详情的 IPv4 / IPv6 切换同样是灰底蓝选中。
**新增**
- 全局加载框:清理、卸载应用、导出诊断信息、修改 DNS 进行中,整个主窗口压暗,中间浮一块加载框写明正在做什么(macOS 26 上是液态玻璃),期间不能误点其他操作,完成后自动消失。
</details>
<details>
<summary><b>2026-09-16</b> · 0.3.2 · 修复 2</summary>
**修复**
@@ -77,49 +91,6 @@ CPU 不分性能核与能效核,部分功耗与频率读数可能不显示。
</details>
<details>
<summary><b>2026-09-15</b> · 0.3.0 · 新增 8 · 调整 14 · 修复 3</summary>
**新增**
- 菜单栏新增“电池”项目:电量按九种风格显示(电量条、圆环、饼图、数字等),充电中带闪电,电量低于 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、磁盘等页面的开关可以正常点击。
</details>
<!-- changelog:end -->
## 活跃度
+1 -1
View File
@@ -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