From 9e4f92e366df3a5b16eed5670dd16a060ccc955d Mon Sep 17 00:00:00 2001 From: gentpan Date: Wed, 16 Sep 2026 18:32:38 +0500 Subject: [PATCH] =?UTF-8?q?=E8=BF=9E=E6=8E=A5=E6=8E=A2=E6=B5=8B=E5=8F=AA?= =?UTF-8?q?=E7=9C=8B=E9=80=9A=E4=B8=8D=E9=80=9A=EF=BC=9A=E7=BB=BF=E7=BA=A2?= =?UTF-8?q?=E4=B8=A4=E8=89=B2=2060=20=E6=A0=BC=EF=BC=8C=E5=8E=BB=E6=8E=89?= =?UTF-8?q?=E5=BB=B6=E8=BF=9F=E6=8A=96=E5=8A=A8=E4=B8=A2=E5=8C=85=EF=BC=8C?= =?UTF-8?q?=E5=89=8D=E5=8F=B0=E5=9B=BA=E5=AE=9A=E6=AF=8F=E7=A7=92=E6=8E=A2?= =?UTF-8?q?=E6=B5=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - ProbeGrid 只分绿(通)、红(超时或不可达)、灰(尚无数据),不再用橙色表示延迟偏高 - 探测历史固定 60 次:弹窗 20 列 3 行,主窗口 30 列 2 行;去掉格子下面的延迟、抖动、丢包与 NetworkController 里对应的统计 - 网络详情打开时固定每秒探测一次(NetworkController.foregroundProbeSeconds),移除 AppSettings.probeSeconds、设置里的“探测间隔”与同步文档字段;后台低频探测仍每 10 秒 - 译文、README、官网与隐私页、更新日志同步 --- CHANGELOG.md | 1 + .../Sources/Localization/Translations.swift | 11 ++------ .../OpenStatsUI/AppKit/AppController.swift | 3 +- .../Sources/OpenStatsUI/Design/Charts.swift | 9 +++--- .../OpenStatsUI/Popover/NetworkPopover.swift | 27 +++--------------- .../OpenStatsUI/Settings/SettingsPages.swift | 10 +------ .../Snapshot/SnapshotRenderer.swift | 1 - .../OpenStatsUI/State/AppSettings.swift | 7 ----- .../OpenStatsUI/State/Diagnostics.swift | 2 +- .../OpenStatsUI/State/NetworkController.swift | 28 ++++--------------- .../OpenStatsUI/State/SettingsDocument.swift | 3 -- README.md | 9 +++--- README.zh-CN.md | 9 +++--- project.yml | 2 +- web/index.html | 4 +-- web/privacy.html | 2 +- 16 files changed, 35 insertions(+), 93 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3bf2989..d9eba7c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ #### 样式 - 主窗口在 macOS 26 上改用液态玻璃:侧边栏是一块浮在窗口里的玻璃面板,红绿灯按钮落在面板里,选中项垫一块淡蓝色圆角底并随切换滑动;顶栏右侧“在菜单栏显示”一组收进玻璃胶囊。主按钮是蓝色玻璃胶囊、次要按钮与图标按钮是透明玻璃,风扇模式等快捷切换是玻璃胶囊,开关换成系统开关(拖动时滑块变成玻璃)。设置分组与数据卡片保持实色,与系统设置一致。macOS 14、15 上保持原来的样式。 +- 连接探测只用来看网络通不通:格子通了是绿色、不通是红色(不再用橙色表示延迟偏高),固定 60 格,弹窗排成 3 行、主窗口排成 2 行;去掉格子下面的延迟、抖动、丢包三项。打开网络详情时固定每秒探测一次,「设置 · 网络」里的“探测间隔”选项随之去掉,后台低频探测仍是每 10 秒。 - 分段切换改为苹果标准样式:灰色圆角底槽,各段等宽铺满,选中的一段是实心蓝色、白字,切换时蓝块滑过去;网络详情的 IPv4 / IPv6 切换同样是灰底蓝选中。 #### 新增 diff --git a/Packages/OpenStatsKit/Sources/Localization/Translations.swift b/Packages/OpenStatsKit/Sources/Localization/Translations.swift index 2e614a3..8b1f6bb 100644 --- a/Packages/OpenStatsKit/Sources/Localization/Translations.swift +++ b/Packages/OpenStatsKit/Sources/Localization/Translations.swift @@ -89,7 +89,7 @@ npm 缓存 npm Cache {} {} 英寸 {} {}-inch {} · {} 个 {} · {} {} · {} 核 {} · {} cores -{} · 每 {} 秒 {} · every {}s +{} · 每秒一次 {} · every second {} · 约 {} {} · about {} {} · 负载 {}/{} {} · load {}/{} {} 个 {} @@ -123,8 +123,8 @@ npm 缓存 npm Cache {}后充满 Full in {} {}电量低 {} battery low {}({}) {} ({}) -{},{} 秒,后台{} {}, {}s, background {} {},{},健康 {} {}, {}, health {} +{},后台{} {}, background {} {}:{} {}: {} ~/.npm/_cacache,安装依赖时自动重新下载 ~/.npm/_cacache; downloaded again when installing dependencies ~/Library/Caches 中各应用的缓存,删除后会按需重建 App caches in ~/Library/Caches; rebuilt as needed @@ -157,7 +157,6 @@ npm 缓存 npm Cache 不限 No limit 不限时 Indefinitely 与 30 秒前持平 Same as 30s ago -丢包 Loss 严重 Critical 中等负载 Moderate load 中等风险 Medium risk @@ -470,7 +469,6 @@ npm 缓存 npm Cache 应用缓存 App Caches 应用退出或断开连接时,自动恢复风扇与睡眠设置 Restores fan and sleep settings when the app quits or disconnects 废纸篓 Trash -延迟 Latency 开 On 开关防休眠 Toggle Keep Awake 开发构建不支持在线升级 Development builds can't update online @@ -523,7 +521,6 @@ npm 缓存 npm Cache 打开 OpenStats 主窗口 Open the OpenStats Window 打开 SMC 失败 ({}) Failed to open SMC ({}) 打开登录项设置 Open Login Items Settings -打开网络详情时使用 Used while network details are open 打开网络详情时向 Cloudflare(1.1.1.1)或 ipify 查询一次公网地址,10 分钟内不重复请求;归属地、ASN、网络类型与纯净度由这台 Mac 直接向 cleanip.io 查询,只发送公网地址 When network details open, asks Cloudflare (1.1.1.1) or ipify for your public address, at most once every 10 minutes; location, ASN, network type and cleanliness are looked up by this Mac directly at cleanip.io, sending only the public address 打开进程页 Show Processes 打开通知设置 Open Notification Settings @@ -532,7 +529,6 @@ npm 缓存 npm Cache 扫描中 Scanning 扫描中… Scanning… 扫描于 Scanned -抖动 Jitter 折线历史 Line History 折线是最近 30 次采样(约 1 分钟)的走势 The line shows the last 30 samples (about a minute) 拷贝 Copy @@ -550,7 +546,6 @@ npm 缓存 npm Cache 掌托 Palm rest 排队程度 Run Queue 探测目标 Probe Target -探测间隔 Probe Interval 接口 Interface 接通电源 Plugged in 推出 Eject @@ -762,7 +757,6 @@ npm 缓存 npm Cache 现在 Now 现在没有本地快照 No local snapshots right now 用 Apple 智能解释 Explain with Apple Intelligence -用 ping 测量延迟与丢包,在网络详情里以格子显示 Measures latency and loss with ping, shown as a grid in network details 用户 User 用户 {} User {} 用户资源库 User Library @@ -867,6 +861,7 @@ npm 缓存 npm Cache 网络已恢复 Network Restored 网络断开 Network Down 网络类型 Network type +网络详情打开时每秒 ping 一次,通了是绿格、不通是红格,显示最近 60 次 Pings once a second while network details are open: green when reachable, red when not, showing the last 60 网络运营方 Network operator 网络连接中断超过 20 秒,恢复后再提示一次 Network has been down for over 20 seconds; notifies again when it's back 网络连接已中断超过 20 秒。可以打开网络详情查看接口与路由器状态。 The network has been down for over 20 seconds. Open network details to check the interface and router. diff --git a/Packages/OpenStatsKit/Sources/OpenStatsUI/AppKit/AppController.swift b/Packages/OpenStatsKit/Sources/OpenStatsUI/AppKit/AppController.swift index 19d5ff2..4cf67c8 100644 --- a/Packages/OpenStatsKit/Sources/OpenStatsUI/AppKit/AppController.swift +++ b/Packages/OpenStatsKit/Sources/OpenStatsUI/AppKit/AppController.swift @@ -206,11 +206,10 @@ public final class AppController: NSObject, NSApplicationDelegate { } } - /// 探测目标、间隔变化时清空历史重新探测;关闭公网 IP 查询时清除已显示的结果 + /// 探测目标变化时清空历史重新探测;关闭公网 IP 查询时清除已显示的结果 private func observeProbeSettings() { withObservationTracking { _ = model.settings.probeTarget - _ = model.settings.probeSeconds _ = model.settings.publicIPLookup } onChange: { [weak self] in Task { @MainActor in diff --git a/Packages/OpenStatsKit/Sources/OpenStatsUI/Design/Charts.swift b/Packages/OpenStatsKit/Sources/OpenStatsUI/Design/Charts.swift index 1bf8ed3..61c0f22 100644 --- a/Packages/OpenStatsKit/Sources/OpenStatsUI/Design/Charts.swift +++ b/Packages/OpenStatsKit/Sources/OpenStatsUI/Design/Charts.swift @@ -464,13 +464,12 @@ struct MirroredRateChart: View { } } -/// 连接探测格子:按时间从左到右、从上到下排列,最新的一格在末尾。 -/// 绿色正常,橙色延迟偏高,红色超时,灰色尚无数据 +/// 连接探测格子:一格一次探测,从左上到右下按时间排,最新的在最后一格。 +/// 只表示通不通:通了是绿色,超时或不可达是红色,还没探测到的是灰色 struct ProbeGrid: View { let samples: [ProbeSample] var columns = 20 var rows = 3 - var slowThreshold: Double = 200 var body: some View { let capacity = columns * rows @@ -487,8 +486,8 @@ struct ProbeGrid: View { let color: Color if index < offset { color = DS.Palette.track - } else if let latency = recent[index - offset].latency { - color = latency >= slowThreshold ? DS.Palette.warning : DS.Palette.success + } else if recent[index - offset].latency != nil { + color = DS.Palette.success } else { color = DS.Palette.error } diff --git a/Packages/OpenStatsKit/Sources/OpenStatsUI/Popover/NetworkPopover.swift b/Packages/OpenStatsKit/Sources/OpenStatsUI/Popover/NetworkPopover.swift index 8b1f376..dab2833 100644 --- a/Packages/OpenStatsKit/Sources/OpenStatsUI/Popover/NetworkPopover.swift +++ b/Packages/OpenStatsKit/Sources/OpenStatsUI/Popover/NetworkPopover.swift @@ -100,17 +100,13 @@ private struct ProbeSection: View { SectionCard(title: PopoverSection.networkProbe.title, trailing: { if settings.probeEnabled { - Text(verbatim: tr("\(network.probeAddress ?? "—") · 每 \(settings.probeSeconds) 秒")) + Text(verbatim: tr("\(network.probeAddress ?? "—") · 每秒一次")) } }) { if settings.probeEnabled { - ProbeGrid(samples: network.probes.elements, columns: isDetailPage ? 40 : 20, rows: 3) - HStack(spacing: DS.Space.s3) { - stat(tr("延迟"), network.latency.map(milliseconds) ?? "—") - stat(tr("抖动"), network.jitter.map(milliseconds) ?? "—") - stat(tr("丢包"), network.lossRate.map { Format.percent($0) } ?? "—", - tone: (network.lossRate ?? 0) > 0.05 ? .error : .neutral) - } + // 只用来看网络通不通:60 格是最近 60 次探测,主窗口里更宽,排成两行 + let columns = isDetailPage ? 30 : 20 + ProbeGrid(samples: network.probes.elements, columns: columns, rows: NetworkController.probeCapacity / columns) } else { HStack { Text(tr("定时 ping 一个地址,记录网络是否通畅")).dsFont(.xs).foregroundStyle(DS.Palette.textSecondary) @@ -121,21 +117,6 @@ private struct ProbeSection: View { } } } - - private func milliseconds(_ value: Double) -> String { - value < 10 ? "\(value.formatted(.number.precision(.fractionLength(1)))) ms" : "\(Int(value.rounded())) ms" - } - - private func stat(_ label: String, _ value: String, tone: Tone = .neutral) -> some View { - VStack(alignment: .leading, spacing: 0) { - Text(label).dsFont(.xs).foregroundStyle(DS.Palette.textTertiary) - Text(verbatim: value) - .dsFont(.sm, weight: .semibold) - .monospacedDigit() - .foregroundStyle(tone == .neutral ? DS.Palette.textPrimary : tone.color) - } - .frame(maxWidth: .infinity, alignment: .leading) - } } // MARK: - 接口 diff --git a/Packages/OpenStatsKit/Sources/OpenStatsUI/Settings/SettingsPages.swift b/Packages/OpenStatsKit/Sources/OpenStatsUI/Settings/SettingsPages.swift index 2d4087c..01d381d 100644 --- a/Packages/OpenStatsKit/Sources/OpenStatsUI/Settings/SettingsPages.swift +++ b/Packages/OpenStatsKit/Sources/OpenStatsUI/Settings/SettingsPages.swift @@ -552,18 +552,10 @@ struct NetworkSettings: View { SettingsGroup(caption: tr("连接探测")) { GroupRow(showsDivider: false) { - SettingRow(title: tr("定时探测网络"), subtitle: tr("用 ping 测量延迟与丢包,在网络详情里以格子显示")) { + SettingRow(title: tr("定时探测网络"), subtitle: tr("网络详情打开时每秒 ping 一次,通了是绿格、不通是红格,显示最近 60 次")) { DSToggle(isOn: $settings.probeEnabled, label: tr("定时探测网络")) } } - GroupRow { - SettingRow(title: tr("探测间隔"), subtitle: tr("打开网络详情时使用")) { - SegmentedControl(selection: $settings.probeSeconds, - options: AppSettings.probeOptions.map { ($0, tr("\($0) 秒")) }) - .frame(width: DS.Size.sidebarWidth) - } - } - .disabled(!settings.probeEnabled) GroupRow { SettingRow(title: tr("探测目标"), subtitle: tr("国内网络建议选阿里云或腾讯;选路由器只检测本地连接")) { Picker(tr("探测目标"), selection: $settings.probeTarget) { diff --git a/Packages/OpenStatsKit/Sources/OpenStatsUI/Snapshot/SnapshotRenderer.swift b/Packages/OpenStatsKit/Sources/OpenStatsUI/Snapshot/SnapshotRenderer.swift index adf82a2..a919548 100644 --- a/Packages/OpenStatsKit/Sources/OpenStatsUI/Snapshot/SnapshotRenderer.swift +++ b/Packages/OpenStatsKit/Sources/OpenStatsUI/Snapshot/SnapshotRenderer.swift @@ -14,7 +14,6 @@ enum SnapshotRenderer { let defaults = UserDefaults(suiteName: "OpenStats.snapshot") ?? .standard let settings = AppSettings(defaults: defaults) settings.menuBarItems = [.cpu, .gpu, .memory, .network, .disk, .temperature, .battery] - settings.probeSeconds = 1 // 历史页用示例数据:最近 24 小时每分钟一条,中间留一段“睡眠”空档 let historyURL = FileManager.default.temporaryDirectory.appendingPathComponent("openstats-snapshot-history.sqlite") try? FileManager.default.removeItem(at: historyURL) diff --git a/Packages/OpenStatsKit/Sources/OpenStatsUI/State/AppSettings.swift b/Packages/OpenStatsKit/Sources/OpenStatsUI/State/AppSettings.swift index 2c1a270..eb6f9ca 100644 --- a/Packages/OpenStatsKit/Sources/OpenStatsUI/State/AppSettings.swift +++ b/Packages/OpenStatsKit/Sources/OpenStatsUI/State/AppSettings.swift @@ -435,9 +435,6 @@ public final class AppSettings { public var probeInBackground: Bool { didSet { defaults.set(probeInBackground, forKey: Keys.probeInBackground) } } - public var probeSeconds: Int { - didSet { defaults.set(probeSeconds, forKey: Keys.probeSeconds) } - } public var probeTarget: ProbeTarget { didSet { defaults.set(probeTarget.rawValue, forKey: Keys.probeTarget) } } @@ -491,7 +488,6 @@ public final class AppSettings { } public static let refreshOptions = [1, 2, 3, 5] - public static let probeOptions = [1, 2, 5] public static let batteryFloorOptions = [10, 20, 30, 40] public static let fanSafetyOptions = [85, 90, 95, 100] public static let alertTemperatureOptions = [85, 90, 95, 100] @@ -526,8 +522,6 @@ public final class AppSettings { .map { Set($0.compactMap(PopoverSection.init(rawValue:))) } ?? PopoverSection.hiddenByDefault probeEnabled = defaults.object(forKey: Keys.probeEnabled) as? Bool ?? true probeInBackground = defaults.object(forKey: Keys.probeInBackground) as? Bool ?? true - probeSeconds = Self.probeOptions.contains(defaults.integer(forKey: Keys.probeSeconds)) - ? defaults.integer(forKey: Keys.probeSeconds) : 2 probeTarget = defaults.string(forKey: Keys.probeTarget).flatMap(ProbeTarget.init(rawValue:)) ?? .cloudflare publicIPLookup = defaults.object(forKey: Keys.publicIPLookup) as? Bool ?? true autoCheckUpdates = defaults.object(forKey: Keys.autoCheckUpdates) as? Bool ?? true @@ -589,7 +583,6 @@ public final class AppSettings { static let menuBarLayout = "menuBarLayout" static let hiddenPopoverSections = "hiddenPopoverSections" static let probeEnabled = "probeEnabled" - static let probeSeconds = "probeSeconds" static let probeInBackground = "probeInBackground" static let probeTarget = "probeTarget" static let publicIPLookup = "publicIPLookup" diff --git a/Packages/OpenStatsKit/Sources/OpenStatsUI/State/Diagnostics.swift b/Packages/OpenStatsKit/Sources/OpenStatsUI/State/Diagnostics.swift index faa9d00..0a585e1 100644 --- a/Packages/OpenStatsKit/Sources/OpenStatsUI/State/Diagnostics.swift +++ b/Packages/OpenStatsKit/Sources/OpenStatsUI/State/Diagnostics.swift @@ -110,7 +110,7 @@ final class DiagnosticsExporter { row(tr("菜单栏风格"), settings.menuBarStyle.rawValue) row(tr("刷新间隔"), tr("\(settings.refreshSeconds) 秒")) row(tr("外观"), settings.appearance.rawValue) - row(tr("连接探测"), settings.probeEnabled ? tr("\(settings.probeTarget.rawValue),\(settings.probeSeconds) 秒,后台\(settings.probeInBackground ? tr("开启") : tr("关闭"))") : tr("关闭")) + row(tr("连接探测"), settings.probeEnabled ? tr("\(settings.probeTarget.rawValue),后台\(settings.probeInBackground ? tr("开启") : tr("关闭"))") : tr("关闭")) row(tr("公网 IP 查询"), settings.publicIPLookup ? tr("开启") : tr("关闭")) row(tr("风扇安全温度"), "\(settings.fanSafetyTemperature)°C") row(tr("合盖电量下限"), "\(settings.lidModeBatteryFloor)%") diff --git a/Packages/OpenStatsKit/Sources/OpenStatsUI/State/NetworkController.swift b/Packages/OpenStatsKit/Sources/OpenStatsUI/State/NetworkController.swift index ae141f7..3aa16d3 100644 --- a/Packages/OpenStatsKit/Sources/OpenStatsUI/State/NetworkController.swift +++ b/Packages/OpenStatsKit/Sources/OpenStatsUI/State/NetworkController.swift @@ -12,8 +12,8 @@ public struct ProbeSample: Sendable, Equatable { @MainActor @Observable public final class NetworkController { - /// 主窗口显示 120 次,弹窗显示最近 60 次 - public static let probeCapacity = 120 + /// 探测格子显示最近 60 次;前台每秒一次,也就是最近 60 秒 + public static let probeCapacity = 60 public private(set) var details: NetworkDetails? /// 公网 IP 信息按地址族各存一份:两份都含 IPv4 与 IPv6 地址,归属地、纯净度等是各自那个地址的 @@ -50,7 +50,8 @@ public final class NetworkController { /// 正在运行的探测是否为前台频率;前后台切换时重启循环,不必等完后台的长间隔 @ObservationIgnored private var probingForeground: Bool? - /// 详情关闭、只在菜单栏显示网速时的低频探测间隔 + /// 网络详情打开时每秒探测一次;详情关闭、只在菜单栏显示网速时低频探测 + static let foregroundProbeSeconds = 1 static let backgroundProbeSeconds = 10 /// 上次查到的公网 IP 信息:点开弹窗时先显示它,只有超过 7 天或公网 IP 变了才重新查归属地 @@ -113,24 +114,7 @@ public final class NetworkController { rate.totalUploaded >= baseline.upload ? rate.totalUploaded - baseline.upload : 0) } - // MARK: 统计 - - /// 最近一次成功探测的延迟 - var latency: Double? { probes.elements.last?.latency } - - /// 抖动:相邻两次成功探测的延迟差的平均值 - var jitter: Double? { - let values = probes.elements.suffix(20).compactMap(\.latency) - guard values.count > 2 else { return nil } - let differences = zip(values.dropFirst(), values).map { abs($0 - $1) } - return differences.reduce(0, +) / Double(differences.count) - } - - var lossRate: Double? { - let recent = probes.elements - guard !recent.isEmpty else { return nil } - return Double(recent.filter { $0.latency == nil }.count) / Double(recent.count) - } + // MARK: 探测目标 var probeAddress: String? { settings.probeTarget.address(router: details?.physical?.router) @@ -316,7 +300,7 @@ public final class NetworkController { let clock = ContinuousClock() while !Task.isCancelled { guard let self else { return } - let interval = Double(foreground ? self.settings.probeSeconds : Self.backgroundProbeSeconds) + let interval = Double(foreground ? Self.foregroundProbeSeconds : Self.backgroundProbeSeconds) if self.details == nil { await self.refreshDetails() } let started = clock.now guard let address = self.probeAddress else { diff --git a/Packages/OpenStatsKit/Sources/OpenStatsUI/State/SettingsDocument.swift b/Packages/OpenStatsKit/Sources/OpenStatsUI/State/SettingsDocument.swift index 9ba72e1..2439b93 100644 --- a/Packages/OpenStatsKit/Sources/OpenStatsUI/State/SettingsDocument.swift +++ b/Packages/OpenStatsKit/Sources/OpenStatsUI/State/SettingsDocument.swift @@ -21,7 +21,6 @@ public struct SettingsDocument: Codable, Equatable, Sendable { public var hiddenPopoverSections: [String]? public var probeEnabled: Bool? public var probeInBackground: Bool? - public var probeSeconds: Int? public var probeTarget: String? public var publicIPLookup: Bool? public var enabledAlerts: [String]? @@ -55,7 +54,6 @@ extension AppSettings { doc.hiddenPopoverSections = hiddenPopoverSections.map(\.rawValue).sorted() doc.probeEnabled = probeEnabled doc.probeInBackground = probeInBackground - doc.probeSeconds = probeSeconds doc.probeTarget = probeTarget.rawValue doc.publicIPLookup = publicIPLookup doc.enabledAlerts = enabledAlerts.map(\.rawValue).sorted() @@ -99,7 +97,6 @@ extension AppSettings { assign(\.hiddenPopoverSections, doc.hiddenPopoverSections.map { Set($0.compactMap(PopoverSection.init(rawValue:))) }) assign(\.probeEnabled, doc.probeEnabled) assign(\.probeInBackground, doc.probeInBackground) - assign(\.probeSeconds, option(doc.probeSeconds, in: Self.probeOptions)) assign(\.probeTarget, doc.probeTarget.flatMap(ProbeTarget.init(rawValue:))) assign(\.publicIPLookup, doc.publicIPLookup) assign(\.enabledAlerts, doc.enabledAlerts.map { Set($0.compactMap(AlertKind.init(rawValue:))) }) diff --git a/README.md b/README.md index 364f30d..a238f0c 100644 --- a/README.md +++ b/README.md @@ -59,14 +59,15 @@ system language by default; switch it in Settings → General → Language. -Latest release **0.3.2** (2026-09-16) · **3** changes in development · [full changelog](CHANGELOG.md) (kept in Chinese) +Latest release **0.3.2** (2026-09-16) · **4** changes in development · [full changelog](CHANGELOG.md) (kept in Chinese)
-2026-09-16 · Unreleased · 2 style · 1 added +2026-09-16 · Unreleased · 3 style · 1 added **Style** - 主窗口在 macOS 26 上改用液态玻璃:侧边栏是一块浮在窗口里的玻璃面板,红绿灯按钮落在面板里,选中项垫一块淡蓝色圆角底并随切换滑动;顶栏右侧“在菜单栏显示”一组收进玻璃胶囊。主按钮是蓝色玻璃胶囊、次要按钮与图标按钮是透明玻璃,风扇模式等快捷切换是玻璃胶囊,开关换成系统开关(拖动时滑块变成玻璃)。设置分组与数据卡片保持实色,与系统设置一致。macOS 14、15 上保持原来的样式。 +- 连接探测只用来看网络通不通:格子通了是绿色、不通是红色(不再用橙色表示延迟偏高),固定 60 格,弹窗排成 3 行、主窗口排成 2 行;去掉格子下面的延迟、抖动、丢包三项。打开网络详情时固定每秒探测一次,「设置 · 网络」里的“探测间隔”选项随之去掉,后台低频探测仍是每 10 秒。 - 分段切换改为苹果标准样式:灰色圆角底槽,各段等宽铺满,选中的一段是实心蓝色、白字,切换时蓝块滑过去;网络详情的 IPv4 / IPv6 切换同样是灰底蓝选中。 **Added** @@ -138,7 +139,7 @@ Choose which sections each popover shows in Settings. `Esc` closes it. queueing (load average per core, rising or falling); usage summed by app. - **Memory.** What is still available with a pressure timeline; a waterline bar; memory saved by compression and live swap I/O; usage summed by app. -- **Network.** Traffic history; a connection-probe grid (latency, jitter, loss); interface, +- **Network.** Traffic history; a connection-probe grid (green when reachable, red when not, last 60 pings); interface, Wi-Fi signal, VPN / proxy; local and public IPv4 / IPv6 with a flag, region and ASN; an IP cleanliness score with an F to A+ grade and risk flags (VPN, proxy, Tor, data center, abuse); DNS flush and one-click switching to Cloudflare, Google, Tencent, Alibaba Cloud or manual servers; @@ -244,7 +245,7 @@ check in Settings → About: come from `cleanip.io`, queried directly from each Mac with nothing but the public address and never through our servers; a result is kept for a week unless the address changes or you refresh. - **Connection probe**: an ICMP ping to the target you pick (Cloudflare, Google, Alibaba Cloud, - Tencent or your router) every 1, 2 or 5 seconds, only while the network item is in the menu bar + Tencent or your router) once a second while network details are open (every 10 seconds in the background), only while the network item is in the menu bar or network details are open. - **Update check**: at launch and once a day, the app reads a version manifest from getopenstats.com — the manifest only. When a new version is out it asks; nothing installs without your click. diff --git a/README.zh-CN.md b/README.zh-CN.md index 6337d68..6a45f88 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -55,14 +55,15 @@ CPU 不分性能核与能效核,部分功耗与频率读数可能不显示。 -最新版本 **0.3.2**(2026-09-16) · 开发中 **3** 项改动尚未发布 · [完整更新日志](CHANGELOG.md) +最新版本 **0.3.2**(2026-09-16) · 开发中 **4** 项改动尚未发布 · [完整更新日志](CHANGELOG.md)
-2026-09-16 · 未发布 · 样式 2 · 新增 1 +2026-09-16 · 未发布 · 样式 3 · 新增 1 **样式** - 主窗口在 macOS 26 上改用液态玻璃:侧边栏是一块浮在窗口里的玻璃面板,红绿灯按钮落在面板里,选中项垫一块淡蓝色圆角底并随切换滑动;顶栏右侧“在菜单栏显示”一组收进玻璃胶囊。主按钮是蓝色玻璃胶囊、次要按钮与图标按钮是透明玻璃,风扇模式等快捷切换是玻璃胶囊,开关换成系统开关(拖动时滑块变成玻璃)。设置分组与数据卡片保持实色,与系统设置一致。macOS 14、15 上保持原来的样式。 +- 连接探测只用来看网络通不通:格子通了是绿色、不通是红色(不再用橙色表示延迟偏高),固定 60 格,弹窗排成 3 行、主窗口排成 2 行;去掉格子下面的延迟、抖动、丢包三项。打开网络详情时固定每秒探测一次,「设置 · 网络」里的“探测间隔”选项随之去掉,后台低频探测仍是每 10 秒。 - 分段切换改为苹果标准样式:灰色圆角底槽,各段等宽铺满,选中的一段是实心蓝色、白字,切换时蓝块滑过去;网络详情的 IPv4 / IPv6 切换同样是灰底蓝选中。 **新增** @@ -129,7 +130,7 @@ CPU 不分性能核与能效核,部分功耗与频率读数可能不显示。 **详情弹窗**——每个指标一个菜单栏图标,点击弹出该项的窄详情,显示哪些区块可在设置里勾选;按 `Esc` 关闭。 - **CPU**:占用与状态、与 30 秒前的变化、CPU 温度与余量、1 / 3 / 5 分钟走势;核心热力图与此刻各核心占用;核心分工与频率;排队程度(每核平均负载与趋势);按应用汇总。 - **内存**:还可用多少与压力走势;内存水位条;压缩省下的内存与交换区读写;按应用汇总。 -- **网络**:流量历史;连接探测格子(延迟、抖动、丢包);接口、Wi-Fi 信号、VPN / 代理;本地与公网 IPv4 / IPv6, +- **网络**:流量历史;连接探测格子(通了是绿格、不通是红格,最近 60 次);接口、Wi-Fi 信号、VPN / 代理;本地与公网 IPv4 / IPv6, 归属地国旗、ASN;IP 纯净度评分与 F 到 A+ 等级、风险标记(VPN、代理、Tor、机房、滥用记录);DNS 一键刷新,一键切换 Cloudflare、Google、腾讯、阿里云或手动填写;各进程流量。 - **磁盘**:启动磁盘容量分段条(已用、可清除、可用);读写速度与 60 秒走势;SSD 健康;读写最多的应用。 - **GPU**、**温度与风扇**:使用历史、各组温度、风扇转速与快捷模式。 @@ -211,7 +212,7 @@ CPU 不分性能核与能效核,部分功耗与频率读数可能不显示。 会联网的功能都可以关闭。前两项在“设置 → 网络”中,检查更新在“设置 · 关于”中: - **公网 IP**:打开网络详情时向 Cloudflare `1.1.1.1`(回退 ipify)请求一次公网地址,10 分钟内不重复请求。归属地、ASN、网络类型与纯净度评分由这台 Mac 直接向 `cleanip.io` 查询,只发送公网地址、不经过我们的服务器;地址没变、不满 7 天就沿用上次结果,点刷新才重查。 -- **连接探测**:每 1、2 或 5 秒向你选择的目标(Cloudflare、Google、阿里云、腾讯或路由器)发送一次 ICMP ping,只在菜单栏显示网络项或打开网络详情时运行。 +- **连接探测**:打开网络详情时每秒(后台每 10 秒)向你选择的目标(Cloudflare、Google、阿里云、腾讯或路由器)发送一次 ICMP ping,只在菜单栏显示网络项或打开网络详情时运行。 - **检查更新**:启动时与之后每天向 getopenstats.com 读取一次版本清单,只下载清单本身;发现新版本时提示,由你决定是否安装。 账号是可选的,只在登录后同步偏好设置,不含任何监控数据。 diff --git a/project.yml b/project.yml index ee609bd..dce0561 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: "0067" + CURRENT_PROJECT_VERSION: "0068" MACOSX_DEPLOYMENT_TARGET: "14.0" # 本地开发使用 ad-hoc 签名;发布时改为 Developer ID(见 docs/PLAN.md) CODE_SIGN_STYLE: Manual diff --git a/web/index.html b/web/index.html index 3a70bcd..30bfe3d 100644 --- a/web/index.html +++ b/web/index.html @@ -248,7 +248,7 @@

网络,一眼看到底

  • 上下镜像的流量历史,上传绿、下载蓝
  • -
  • 连接探测格子:每 1 / 2 / 5 秒 ping 一次,延迟、抖动、丢包一目了然
  • +
  • 连接探测格子:每秒 ping 一次,通了是绿格、不通是红格,最近 60 次一眼看清
  • 本地与公网 IPv4 / IPv6、路由器、Wi‑Fi 信号,点一下就拷贝
  • 归属地显示国旗、城市、ASN 与网络运营方,原生 / 广播、住宅 / 机房直接标出
  • IP 纯净度:CleanIP.io 评分与 F 到 A+ 等级色带,列出 VPN、代理、Tor、机房、滥用记录等风险标记
  • @@ -326,7 +326,7 @@
  • 统计上报没有
  • 公网 IP打开网络详情时向 Cloudflare 查询一次公网地址,10 分钟内不重复,可关闭
  • 归属地归属地、ASN、网络类型与纯净度评分来自 cleanip.io,这台 Mac 直接向它查自己的公网 IP,只发送这个地址
  • -
  • 连接探测按设定间隔 ping 你选择的目标,只在显示网络项时运行,可关闭
  • +
  • 连接探测打开网络详情时每秒 ping 一次你选择的目标(后台每 10 秒),只在显示网络项时运行,可关闭
  • 偏好设置保存在本机;登录后同步的那一份不含任何监控数据
diff --git a/web/privacy.html b/web/privacy.html index 29ca72d..510c9ce 100644 --- a/web/privacy.html +++ b/web/privacy.html @@ -35,7 +35,7 @@
  • 公网 IP打开网络详情时向 Cloudflare 查询一次公网地址,10 分钟内不重复,可在设置中关闭
  • 归属地归属地、ASN、网络类型与纯净度评分由 cleanip.io 提供:这台 Mac 直接向它查询自己的公网 IP,只发送这个地址、不带其他信息,不经过 OpenStats 的服务器
  • -
  • 连接探测按设定间隔 ping 你选择的目标,只在显示网络项时运行,可关闭
  • +
  • 连接探测打开网络详情时每秒 ping 一次你选择的目标(后台每 10 秒),只在显示网络项时运行,可关闭
  • 检查更新启动时与每天一次读取 getopenstats.com 上的版本清单,请求不带任何标识