From 71fbb30c70e4c1dfea9c51968e55d0266e1e159b Mon Sep 17 00:00:00 2001 From: ysicing Date: Wed, 23 Sep 2026 17:21:51 +0800 Subject: [PATCH] =?UTF-8?q?feat(legal):=20=E6=B7=BB=E5=8A=A0=E6=9C=8D?= =?UTF-8?q?=E5=8A=A1=E6=9D=A1=E6=AC=BE=E4=B8=8E=E9=9A=90=E7=A7=81=E6=94=BF?= =?UTF-8?q?=E7=AD=96=E5=B9=B6=E4=BC=98=E5=8C=96=E9=98=85=E8=AF=BB=E6=8E=92?= =?UTF-8?q?=E7=89=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Packages/XStatsKit/Package.swift | 2 +- .../Sources/Localization/Resources/ar.tsv | 6 + .../Sources/Localization/Resources/de.tsv | 6 + .../Sources/Localization/Resources/es.tsv | 6 + .../Sources/Localization/Resources/fr.tsv | 6 + .../Sources/Localization/Resources/ja.tsv | 6 + .../Sources/Localization/Resources/ko.tsv | 6 + .../Localization/Resources/zh-Hant.tsv | 6 + .../Sources/Localization/Translations.swift | 6 + .../XStatsUI/Resources/Legal/Privacy.en.md | 41 +++++ .../Resources/Legal/Privacy.zh-Hans.md | 41 +++++ .../XStatsUI/Resources/Legal/Terms.en.md | 29 ++++ .../XStatsUI/Resources/Legal/Terms.zh-Hans.md | 29 ++++ .../Settings/LegalDocumentSheet.swift | 146 ++++++++++++++++++ .../XStatsUI/Settings/SettingsPages.swift | 20 +++ .../XStatsUITests/LegalDocumentTests.swift | 26 ++++ README.en.md | 8 +- README.ja.md | 2 +- README.ko.md | 2 +- README.md | 8 +- server/api/dashboard.html | 2 +- 21 files changed, 394 insertions(+), 10 deletions(-) create mode 100644 Packages/XStatsKit/Sources/XStatsUI/Resources/Legal/Privacy.en.md create mode 100644 Packages/XStatsKit/Sources/XStatsUI/Resources/Legal/Privacy.zh-Hans.md create mode 100644 Packages/XStatsKit/Sources/XStatsUI/Resources/Legal/Terms.en.md create mode 100644 Packages/XStatsKit/Sources/XStatsUI/Resources/Legal/Terms.zh-Hans.md create mode 100644 Packages/XStatsKit/Sources/XStatsUI/Settings/LegalDocumentSheet.swift create mode 100644 Packages/XStatsKit/Tests/XStatsUITests/LegalDocumentTests.swift diff --git a/Packages/XStatsKit/Package.swift b/Packages/XStatsKit/Package.swift index 6ead3a1..c1c66bd 100644 --- a/Packages/XStatsKit/Package.swift +++ b/Packages/XStatsKit/Package.swift @@ -30,7 +30,7 @@ let package = Package( .target(name: "WebDAVSync", dependencies: ["Localization"]), .target(name: "XStatsUI", dependencies: ["AIUsage", "Localization", "Metrics", "SMC", "HelperShared", "Cleaner", "Updates", "WebDAVSync", .product(name: "Tyme4Swift", package: "tyme4swift")], - resources: [.copy("Resources/Flags"), .copy("Resources/Logos")]), + resources: [.copy("Resources/Flags"), .copy("Resources/Logos"), .copy("Resources/Legal")]), .testTarget(name: "MetricsTests", dependencies: ["Metrics", "SMC"]), .testTarget(name: "CleanerTests", dependencies: ["Cleaner"]), .testTarget(name: "HelperSharedTests", dependencies: ["HelperShared"]), diff --git a/Packages/XStatsKit/Sources/Localization/Resources/ar.tsv b/Packages/XStatsKit/Sources/Localization/Resources/ar.tsv index 79ce62e..11075fe 100644 --- a/Packages/XStatsKit/Sources/Localization/Resources/ar.tsv +++ b/Packages/XStatsKit/Sources/Localization/Resources/ar.tsv @@ -1192,6 +1192,12 @@ Apple 智能本机模型当前不可用。 نموذج Apple Intelligence على 降温 تبريد 隐私 الخصوصية 隐私政策 سياسة الخصوصية +法律与隐私 الشروط والخصوصية +服务条款 شروط الخدمة +了解 XStats 的使用条件与责任边界 اطّلع على شروط استخدام XStats وحدود المسؤولية +了解本机数据、联网功能与信息处理方式 تعرّف على معالجة البيانات المحلية وميزات الشبكة +无法读取法律文件 تعذّر تحميل المستند القانوني +关闭文档 إغلاق 隐藏 XStats إخفاء XStats 隐藏序列号 إخفاء الرقم التسلسلي 需要 macOS 26 及以上,并在系统设置中开启 Apple 智能。 يتطلب macOS 26 أو أحدث مع تفعيل Apple Intelligence في إعدادات النظام. diff --git a/Packages/XStatsKit/Sources/Localization/Resources/de.tsv b/Packages/XStatsKit/Sources/Localization/Resources/de.tsv index b03465a..a64c6ea 100644 --- a/Packages/XStatsKit/Sources/Localization/Resources/de.tsv +++ b/Packages/XStatsKit/Sources/Localization/Resources/de.tsv @@ -1192,6 +1192,12 @@ Apple 智能本机模型当前不可用。 Das lokale Apple Intelligence-Modell 降温 Abkühlung 隐私 Datenschutz 隐私政策 Datenschutzerklärung +法律与隐私 Rechtliches & Datenschutz +服务条款 Nutzungsbedingungen +了解 XStats 的使用条件与责任边界 Nutzungsbedingungen und Verantwortlichkeiten von XStats +了解本机数据、联网功能与信息处理方式 Umgang mit lokalen Daten und Online-Funktionen +无法读取法律文件 Rechtliches Dokument konnte nicht geladen werden +关闭文档 Schließen 隐藏 XStats XStats ausblenden 隐藏序列号 Seriennummer ausblenden 需要 macOS 26 及以上,并在系统设置中开启 Apple 智能。 Benötigt macOS 26 oder neuer und aktiviertes Apple Intelligence in den Systemeinstellungen. diff --git a/Packages/XStatsKit/Sources/Localization/Resources/es.tsv b/Packages/XStatsKit/Sources/Localization/Resources/es.tsv index 9b01832..beb89ae 100644 --- a/Packages/XStatsKit/Sources/Localization/Resources/es.tsv +++ b/Packages/XStatsKit/Sources/Localization/Resources/es.tsv @@ -1192,6 +1192,12 @@ Apple 智能本机模型当前不可用。 El modelo de Apple Intelligence en el 降温 Refrigeración 隐私 Privacidad 隐私政策 Política de privacidad +法律与隐私 Avisos legales y privacidad +服务条款 Términos del servicio +了解 XStats 的使用条件与责任边界 Condiciones de uso y responsabilidades de XStats +了解本机数据、联网功能与信息处理方式 Cómo se tratan los datos locales y las funciones en línea +无法读取法律文件 No se pudo cargar el documento legal +关闭文档 Cerrar 隐藏 XStats Ocultar XStats 隐藏序列号 Ocultar número de serie 需要 macOS 26 及以上,并在系统设置中开启 Apple 智能。 Requiere macOS 26 o posterior con Apple Intelligence activado en Ajustes del Sistema. diff --git a/Packages/XStatsKit/Sources/Localization/Resources/fr.tsv b/Packages/XStatsKit/Sources/Localization/Resources/fr.tsv index 3d3a7e0..251a8b0 100644 --- a/Packages/XStatsKit/Sources/Localization/Resources/fr.tsv +++ b/Packages/XStatsKit/Sources/Localization/Resources/fr.tsv @@ -1192,6 +1192,12 @@ Apple 智能本机模型当前不可用。 Le modèle Apple Intelligence sur l 降温 Refroidissement 隐私 Confidentialité 隐私政策 Politique de confidentialité +法律与隐私 Mentions légales et confidentialité +服务条款 Conditions d’utilisation +了解 XStats 的使用条件与责任边界 Conditions d’utilisation et responsabilités de XStats +了解本机数据、联网功能与信息处理方式 Traitement des données locales et des fonctions en ligne +无法读取法律文件 Impossible de charger le document juridique +关闭文档 Fermer 隐藏 XStats Masquer XStats 隐藏序列号 Masquer le numéro de série 需要 macOS 26 及以上,并在系统设置中开启 Apple 智能。 Nécessite macOS 26 ou ultérieur et Apple Intelligence activé dans Réglages Système. diff --git a/Packages/XStatsKit/Sources/Localization/Resources/ja.tsv b/Packages/XStatsKit/Sources/Localization/Resources/ja.tsv index 7d0df20..f65d771 100644 --- a/Packages/XStatsKit/Sources/Localization/Resources/ja.tsv +++ b/Packages/XStatsKit/Sources/Localization/Resources/ja.tsv @@ -1192,6 +1192,12 @@ Apple 智能本机模型当前不可用。 Apple Intelligenceのオンデバイ 降温 冷却 隐私 プライバシー 隐私政策 プライバシーポリシー +法律与隐私 法的情報とプライバシー +服务条款 利用規約 +了解 XStats 的使用条件与责任边界 XStats の利用条件と責任範囲を確認 +了解本机数据、联网功能与信息处理方式 ローカルデータとネットワーク機能の取り扱いを確認 +无法读取法律文件 法的文書を読み込めません +关闭文档 閉じる 隐藏 XStats XStatsを隠す 隐藏序列号 シリアル番号を隠す 需要 macOS 26 及以上,并在系统设置中开启 Apple 智能。 macOS 26以降が必要です。システム設定でApple Intelligenceをオンにしてください。 diff --git a/Packages/XStatsKit/Sources/Localization/Resources/ko.tsv b/Packages/XStatsKit/Sources/Localization/Resources/ko.tsv index a5dcbd5..ef0fd45 100644 --- a/Packages/XStatsKit/Sources/Localization/Resources/ko.tsv +++ b/Packages/XStatsKit/Sources/Localization/Resources/ko.tsv @@ -1192,6 +1192,12 @@ Apple 智能本机模型当前不可用。 Apple Intelligence 온디바이스 降温 냉각 隐私 개인정보 보호 隐私政策 개인정보 처리방침 +法律与隐私 법률 및 개인정보 +服务条款 서비스 약관 +了解 XStats 的使用条件与责任边界 XStats 이용 조건과 책임 범위 확인 +了解本机数据、联网功能与信息处理方式 로컬 데이터와 네트워크 기능의 정보 처리 방식 확인 +无法读取法律文件 법률 문서를 불러올 수 없습니다 +关闭文档 닫기 隐藏 XStats XStats 숨기기 隐藏序列号 일련 번호 숨기기 需要 macOS 26 及以上,并在系统设置中开启 Apple 智能。 macOS 26 이상이 필요하며 시스템 설정에서 Apple Intelligence를 켜야 합니다. diff --git a/Packages/XStatsKit/Sources/Localization/Resources/zh-Hant.tsv b/Packages/XStatsKit/Sources/Localization/Resources/zh-Hant.tsv index 8bdfe05..1ebe5bd 100644 --- a/Packages/XStatsKit/Sources/Localization/Resources/zh-Hant.tsv +++ b/Packages/XStatsKit/Sources/Localization/Resources/zh-Hant.tsv @@ -1193,6 +1193,12 @@ Apple 智能本机模型当前不可用。 Apple 智能本機模型目前無法 降温 降溫 隐私 隱私 隐私政策 隱私政策 +法律与隐私 法律與隱私 +服务条款 服務條款 +了解 XStats 的使用条件与责任边界 了解 XStats 的使用條件與責任範圍 +了解本机数据、联网功能与信息处理方式 了解本機資料、連網功能與資訊處理方式 +无法读取法律文件 無法讀取法律文件 +关闭文档 關閉文件 隐藏 XStats 隱藏 XStats 隐藏序列号 隱藏序列號 需要 macOS 26 及以上,并在系统设置中开启 Apple 智能。 需要 macOS 26 及以上,並在系統設置中開啓 Apple 智能。 diff --git a/Packages/XStatsKit/Sources/Localization/Translations.swift b/Packages/XStatsKit/Sources/Localization/Translations.swift index 447be71..3041125 100644 --- a/Packages/XStatsKit/Sources/Localization/Translations.swift +++ b/Packages/XStatsKit/Sources/Localization/Translations.swift @@ -1188,6 +1188,12 @@ Apple 智能本机模型当前不可用。 The on-device Apple Intelligence mode 降温 Cooling 隐私 Privacy 隐私政策 Privacy policy +法律与隐私 Legal & Privacy +服务条款 Terms of Service +了解 XStats 的使用条件与责任边界 Read XStats's terms of use and responsibilities +了解本机数据、联网功能与信息处理方式 Learn how local data and online features are handled +无法读取法律文件 Unable to load legal document +关闭文档 Close 隐藏 XStats Hide XStats 隐藏序列号 Hide Serial Number 需要 macOS 26 及以上,并在系统设置中开启 Apple 智能。 Requires macOS 26 or later with Apple Intelligence turned on in System Settings. diff --git a/Packages/XStatsKit/Sources/XStatsUI/Resources/Legal/Privacy.en.md b/Packages/XStatsKit/Sources/XStatsUI/Resources/Legal/Privacy.en.md new file mode 100644 index 0000000..2ae661b --- /dev/null +++ b/Packages/XStatsKit/Sources/XStatsUI/Resources/Legal/Privacy.en.md @@ -0,0 +1,41 @@ +# XStats Privacy Policy + +Effective date: September 23, 2026 + +This policy explains what XStats processes on your Mac and which features contact external services. XStats-operated services retain only the update-statistics aggregates described in section 2; the app's other persisted data stays on your Mac and is not uploaded to XStats-operated servers. Only if you configure and manually use WebDAV sync is a settings backup stored on the server you configured. Network tests also send requests needed for the test to independent providers, as described in sections 3 and 4. Contact: i@xiai.me. + +## 1. Data processed on your Mac + +CPU, GPU, memory, network, temperature, fan, and process metrics come from local system interfaces. Those metrics, their history, hardware serial numbers, and process lists are processed on your Mac and are not uploaded to the XStats update service. Preferences are stored locally; selected preferences become a remote backup only when you manually use WebDAV sync. + +AI Usage is off by default. When enabled, it reads local Codex / Claude Code session logs to summarize token usage by model. It does not read login credentials, query subscription quotas, or transmit those logs. Apple Intelligence process explanations run on-device and do not send process information to another AI service. + +You choose whether and where to export diagnostics; they are not uploaded automatically. An exported archive may contain a settings summary, the last three days of system logs, cleanup records, and crash reports. Review it before sharing. + +## 2. Update checks and installation statistics + +When automatic update checks are enabled, XStats sends its current version and the SHA-256 hash of a random installation identifier to the XStats update service after launch and approximately daily thereafter. Manual checks send the same information. China-region devices try `x-stats.china.12306.work` first; others try `xstats-apps.12306.work` first. The other endpoint is tried only if the preferred one fails. The original random value stays in local preferences and is excluded from WebDAV backups. + +The update service's application database stores the hash, current version, first and most recent check times, and cumulative check count. These aggregate records support deduplicated installation counts, version distribution, and activity statistics. They have no automatic expiry. The update service does not create a separate record for every request or store request IP addresses in its application database. It uses IP addresses in memory for one-minute rate limiting. Network infrastructure may process connection metadata under its own rules. + +You can turn off automatic checks in Settings → About. You decide whether to download and install an available update. To request access to or deletion of an associated aggregate record, email us below; we may need the hash of your local installation identifier to locate it. + +## 3. Other network features + +- When you open network details with public-IP lookup enabled, XStats contacts Cloudflare `1.1.1.1`, falling back to ipify. It sends your public IP address to `cleanip.io` for location, ASN, network type, and cleanliness information. Results are cached locally. +- Connection probing sends ICMP packets to your selected target when enabled. Speed tests, egress checks, and DNS lookups contact selected test nodes, websites, or DNS services only when you use those features. Those services can observe connection metadata and the data needed for the test. +- Globalping receives the target domain you enter. Measurement parameters and results can be retrieved by anyone with the measurement ID; do not enter a target you want to keep private. + +These third-party services operate independently and process requests under their own policies. Except for update checks, these requests are not relayed through XStats servers. + +## 4. WebDAV settings sync + +You enter and save the WebDAV server address, username, and password yourself; XStats does not provide or preconfigure a WebDAV sync server. The app contacts that server over HTTPS only when you manually upload or download. The uploaded JSON contains preferences, not monitoring data, history, the original installation identifier, or WebDAV connection details. Your WebDAV password stays in the local Keychain. XStats does not add encryption to the backup file stored on your server. You and your chosen provider control retention and deletion there. + +## 5. Retention, controls, and contact + +You and macOS control local data. Uninstalling the app may leave local preferences, logs, or diagnostic archives you exported. You can disable optional features to stop future requests and delete local or WebDAV data yourself. + +The update service retains hashed aggregate records without automatic expiry, as described in section 2. If you email us, we use your message to handle the request and keep it only as long as needed for that purpose and applicable obligations. Contact i@xiai.me to ask about, correct, or delete an associated record we can locate, or to raise a privacy concern. + +If our processing changes materially, we will update this policy and its effective date and make the new version available in the app or project page. diff --git a/Packages/XStatsKit/Sources/XStatsUI/Resources/Legal/Privacy.zh-Hans.md b/Packages/XStatsKit/Sources/XStatsUI/Resources/Legal/Privacy.zh-Hans.md new file mode 100644 index 0000000..4611cf2 --- /dev/null +++ b/Packages/XStatsKit/Sources/XStatsUI/Resources/Legal/Privacy.zh-Hans.md @@ -0,0 +1,41 @@ +# XStats 隐私政策 + +生效日期:2026 年 9 月 23 日 + +本政策说明 XStats 在 macOS 上处理哪些信息,以及哪些功能会连接外部服务。XStats 自有服务仅保存第 2 节所述的更新统计汇总;应用自身的其他持久化数据保存在本机,不上传到 XStats 自有服务器。只有你自行配置并主动使用 WebDAV 同步时,设置备份才会保存在你配置的服务器;联网测试也会向相应第三方发送完成测试所需的请求,详见第 3、4 节。联系邮箱:i@xiai.me。 + +## 1. 本机处理的数据 + +CPU、GPU、内存、网络、温度、风扇、进程等监控数据来自本机系统接口,其指标、历史记录、硬件序列号和进程列表仅在本机处理,不上传到 XStats 更新服务。设置保存在本机应用偏好中;仅在你主动使用 WebDAV 同步时,所选设置会形成远端备份。 + +“AI 使用统计”默认关闭;开启后只读取本机 Codex / Claude Code 会话日志,按模型汇总 Token 用量,不读取登录凭据,不查询订阅额度,也不发送这些日志。Apple 智能进程解释在本机运行,不把进程信息发送给其他 AI 服务。 + +诊断信息由你主动导出到指定位置,不会自动上传。导出的压缩包可能包含应用设置摘要、最近三天的系统日志、清理记录和崩溃报告;分享前请自行检查内容。 + +## 2. 更新检查与安装统计 + +启用自动检查更新时,应用启动后及此后约每天向 XStats 更新服务发送当前版本和随机安装标识的 SHA-256 哈希,并获取版本清单;手动检查时也会发送。中国地区优先连接 `x-stats.china.12306.work`,其他地区优先连接 `xstats-apps.12306.work`,首选失败时才尝试另一端。原始随机值仅保存在本机偏好设置中,不在 WebDAV 备份中。 + +更新服务的业务数据库保存哈希、当前版本、首次及最近检查时间、累计检查次数,用于去重统计安装量、版本分布和活跃情况。此汇总记录不设自动到期清理;更新服务不建立逐次请求明细,应用服务不将请求 IP 写入业务数据库。请求 IP 会在内存中用于一分钟限流。网络基础设施可能按其自身规则处理连接元数据。 + +你可以在“设置 → 关于”关闭自动检查更新;发现新版本后,由你决定是否下载安装。若希望查询或删除关联的服务端汇总记录,请通过下方邮箱联系我们;为了定位记录,可能需要你提供本机安装标识的哈希。 + +## 3. 其他联网功能 + +- 打开网络详情且启用公网 IP 查询时,应用直接访问 Cloudflare `1.1.1.1`,失败时回退到 ipify;归属地、ASN、网络类型和纯净度查询会把公网 IP 发送给 `cleanip.io`。结果在本机缓存。 +- 启用连接探测时,应用向所选目标发送 ICMP 探测。测速、出口检测和 DNS 查询仅在你使用相应功能时,直接连接所选测试节点、网站或 DNS 服务;对方可看到连接元数据及测试所需内容。 +- 使用全球探针时,应用会把你输入的目标域名提交给 Globalping。测量参数和结果可通过测量 ID 被他人查询,请勿输入不希望公开的目标。 + +上述第三方服务不由 XStats 控制,其对请求信息的处理以各自政策为准。除更新服务外,这些请求不经 XStats 服务器转发。 + +## 4. WebDAV 设置同步 + +WebDAV 服务器地址、用户名和密码均由你自行填写并保存;XStats 不提供或预设 WebDAV 同步服务器。只有你主动上传或下载时,应用才会通过 HTTPS 与该服务器通信。上传的 JSON 文件包含偏好设置,不包含监控数据、历史记录、原始安装标识或 WebDAV 连接信息。WebDAV 密码保存在本机钥匙串;备份文件在服务器上不由 XStats 额外加密。远端文件的保存与删除由你及所选服务商管理。 + +## 5. 保存、控制与联系我们 + +本机数据由你和 macOS 管理;卸载应用不一定自动删除本机偏好、日志或你已导出的诊断包。可通过关闭相应功能停止后续查询,并在本机或 WebDAV 服务端删除相关数据。 + +更新服务的哈希汇总记录按第 2 节说明保留,不自动过期。通过邮件联系我们时,我们仅将邮件用于处理你的请求,并按处理请求及适用义务所需的期限保存。你可通过 i@xiai.me 请求了解、更正或删除我们可定位的关联记录,或提出隐私问题。 + +如处理方式发生重大变化,我们会更新本政策及生效日期,并在应用或项目页面提供新版本。 diff --git a/Packages/XStatsKit/Sources/XStatsUI/Resources/Legal/Terms.en.md b/Packages/XStatsKit/Sources/XStatsUI/Resources/Legal/Terms.en.md new file mode 100644 index 0000000..d7b6b9d --- /dev/null +++ b/Packages/XStatsKit/Sources/XStatsUI/Resources/Legal/Terms.en.md @@ -0,0 +1,29 @@ +# XStats Terms of Service + +Effective date: September 23, 2026 + +These terms cover the XStats macOS app and its update-check service. Contact: i@xiai.me. Please also read the Privacy Policy under Settings → About in the app. + +## 1. Software and open-source licenses + +XStats provides local system monitoring, network tests, settings sync, cleanup, and related features. The licenses for XStats-authored or modified source are described in [LICENSE](https://github.com/ysicing/XStats/blob/main/LICENSE) and [LICENSING.md](https://github.com/ysicing/XStats/blob/main/LICENSING.md). Upstream and third-party components retain their respective licenses. These terms do not replace, narrow, or add restrictions to applicable open-source licenses. + +## 2. Actions under your control + +Some features change local state, including file cleanup, app uninstallation, fan control, startup-item management, and update installation. The app presents relevant choices, permission requests, or confirmations, but you should still review targets, keep appropriate backups, and decide whether an action suits your device. Items moved to Trash may be recoverable only while Trash remains unemptied and macOS permits recovery. + +You configure the WebDAV server address and account yourself; XStats does not provide or preconfigure a sync server. You also start each sync manually. Uploading may replace the remote settings file; downloading and applying may overwrite corresponding local preferences. Manage server access, backups, and credentials accordingly. + +## 3. Network features and third parties + +Update checks, network details, speed tests, egress checks, global probes, and WebDAV may connect to external services when used. Some services are run by independent third parties; their availability, accuracy, limits, and terms can change. Do not submit a target to Globalping if it must stay private. See the Privacy Policy for details. + +Finding an update does not install it automatically. You can review the changes and decide whether to upgrade. + +## 4. Availability and responsibility + +Metrics, network results, and cleanup suggestions can be affected by macOS version, permissions, hardware, network conditions, and third-party services. They are informational. We work to improve XStats but cannot guarantee uninterrupted or error-free software or network services. These terms do not exclude or limit rights or responsibilities that applicable law does not allow us to exclude or limit. + +## 5. Changes and contact + +If these terms change materially, we will update the effective date and make the revised version available in the app or project page. Send questions, feedback, or rights requests to i@xiai.me. diff --git a/Packages/XStatsKit/Sources/XStatsUI/Resources/Legal/Terms.zh-Hans.md b/Packages/XStatsKit/Sources/XStatsUI/Resources/Legal/Terms.zh-Hans.md new file mode 100644 index 0000000..f14ed91 --- /dev/null +++ b/Packages/XStatsKit/Sources/XStatsUI/Resources/Legal/Terms.zh-Hans.md @@ -0,0 +1,29 @@ +# XStats 服务条款 + +生效日期:2026 年 9 月 23 日 + +本条款适用于 XStats macOS 应用及其更新检查服务。联系邮箱:i@xiai.me。使用前也请阅读应用“设置 → 关于”中的隐私政策。 + +## 1. 软件与开源许可 + +XStats 提供本机系统监控、网络测试、设置同步及清理等功能。应用源码中 XStats 自行编写和修改部分的许可,以项目的 [LICENSE](https://github.com/ysicing/XStats/blob/main/LICENSE) 和 [LICENSING.md](https://github.com/ysicing/XStats/blob/main/LICENSING.md) 为准;上游及第三方组件保留各自许可。本条款不取代、缩减或额外限制适用的开源许可。 + +## 2. 你控制的操作 + +部分功能会修改本机状态,例如清理文件、卸载应用、控制风扇、管理启动项或安装更新。应用会在相应步骤显示选择、权限要求或确认,但你仍应核对目标、保留必要备份,并确认操作适合自己的设备。移到废纸篓的内容只有在废纸篓未清空且系统允许时才可能恢复。 + +WebDAV 服务器地址和账号由你自行配置,XStats 不提供或预设同步服务器;同步也由你主动发起。上传可能替换远端同名设置文件,下载并应用可能覆盖本机相应设置;请管理好服务器权限、备份和凭据。 + +## 3. 联网功能与第三方服务 + +检查更新、网络详情、测速、出口检测、全球探针及 WebDAV 等功能按实际使用情形连接外部服务。部分服务由第三方提供,不受 XStats 控制;其可用性、准确性、限额及条款可能变化。请勿把不应公开的目标提交给全球探针。详见隐私政策。 + +发现更新不代表应用会自动安装;由你查看更新内容并决定是否升级。 + +## 4. 可用性与责任 + +监控指标、网络测试结果和清理建议可能受系统版本、权限、硬件、网络及第三方服务影响,仅供参考。XStats 会持续改进,但不保证软件或联网服务始终无错误或不中断。本条款不排除或限制适用法律不得排除或限制的权利与责任。 + +## 5. 条款变更与联系 + +如果条款发生重要变化,我们会更新生效日期,并在应用或项目页面提供新版本。问题、反馈或权利请求请发送至 i@xiai.me。 diff --git a/Packages/XStatsKit/Sources/XStatsUI/Settings/LegalDocumentSheet.swift b/Packages/XStatsKit/Sources/XStatsUI/Settings/LegalDocumentSheet.swift new file mode 100644 index 0000000..4121ceb --- /dev/null +++ b/Packages/XStatsKit/Sources/XStatsUI/Settings/LegalDocumentSheet.swift @@ -0,0 +1,146 @@ +// Copyright (C) 2026 ysicing +// SPDX-License-Identifier: AGPL-3.0-or-later + +import Foundation +import Localization +import SwiftUI + +struct LegalBlock: Equatable { + enum Kind: Equatable { case metadata, section, paragraph, bullet } + + let kind: Kind + let text: String +} + +enum LegalDocument: String, Identifiable { + case terms = "Terms" + case privacy = "Privacy" + + var id: String { rawValue } + var title: String { self == .terms ? tr("服务条款") : tr("隐私政策") } + + func blocks(for language: AppLanguage) -> [LegalBlock]? { + let documentLanguage = language.isChinese ? "zh-Hans" : "en" + guard let url = Bundle.module.url(forResource: "\(rawValue).\(documentLanguage)", withExtension: "md", subdirectory: "Legal"), + let source = try? String(contentsOf: url, encoding: .utf8) else { return nil } + return Self.parse(source) + } + + /// SwiftUI 的单个 Text 会压平 Markdown 块级结构;按块拆开,交给布局控制章节和段落间距。 + static func parse(_ source: String) -> [LegalBlock] { + var blocks: [LegalBlock] = [] + var paragraph: [String] = [] + + func flushParagraph() { + guard !paragraph.isEmpty else { return } + blocks.append(LegalBlock(kind: blocks.isEmpty ? .metadata : .paragraph, + text: paragraph.joined(separator: " "))) + paragraph.removeAll(keepingCapacity: true) + } + + for line in source.components(separatedBy: .newlines).map({ $0.trimmingCharacters(in: .whitespaces) }) { + if line.isEmpty { + flushParagraph() + } else if line.hasPrefix("# ") { + flushParagraph() // 标题由弹窗顶栏显示,不在正文重复。 + } else if line.hasPrefix("## ") { + flushParagraph() + blocks.append(LegalBlock(kind: .section, text: String(line.dropFirst(3)))) + } else if line.hasPrefix("- ") { + flushParagraph() + blocks.append(LegalBlock(kind: .bullet, text: String(line.dropFirst(2)))) + } else { + paragraph.append(line) + } + } + flushParagraph() + return blocks + } +} + +struct LegalDocumentSheet: View { + @Environment(\.dismiss) private var dismiss + let document: LegalDocument + + var body: some View { + VStack(alignment: .leading, spacing: 0) { + HStack(spacing: DS.Space.s3) { + Button { dismiss() } label: { + Image(systemName: "xmark") + .font(.system(size: 11, weight: .semibold)) + .foregroundStyle(DS.Palette.textSecondary) + .frame(width: 28, height: 28) + .background(DS.Palette.surface, in: Circle()) + } + .buttonStyle(.plain) + .accessibilityLabel(tr("关闭文档")) + .keyboardShortcut(.cancelAction) + Text(document.title) + .dsFont(.lg, weight: .semibold) + .foregroundStyle(DS.Palette.textPrimary) + Spacer() + } + .padding(DS.Space.s4) + + HairlineDivider() + + ScrollView { + if let blocks = document.blocks(for: L10n.language) { + VStack(alignment: .leading, spacing: 0) { + ForEach(Array(blocks.enumerated()), id: \.offset) { _, block in + blockView(block) + } + } + .frame(maxWidth: 640, alignment: .leading) + .padding(DS.Space.s6) + .frame(maxWidth: .infinity) + } else { + Text(tr("无法读取法律文件")) + .foregroundStyle(DS.Palette.textSecondary) + .padding(DS.Space.s4) + } + } + .overlayScrollers() + } + .frame(width: 720, height: 600) + .background(DS.Palette.background) + } + + @ViewBuilder + private func blockView(_ block: LegalBlock) -> some View { + switch block.kind { + case .metadata: + Text(block.text) + .dsFont(.xs) + .foregroundStyle(DS.Palette.textSecondary) + .padding(.bottom, DS.Space.s6) + case .section: + Text(block.text) + .dsFont(.base, weight: .semibold) + .foregroundStyle(DS.Palette.textPrimary) + .padding(.top, DS.Space.s4) + .padding(.bottom, DS.Space.s3) + case .paragraph: + richText(block.text) + .dsFont(.sm) + .lineSpacing(5) + .textSelection(.enabled) + .fixedSize(horizontal: false, vertical: true) + .padding(.bottom, DS.Space.s4) + case .bullet: + HStack(alignment: .firstTextBaseline, spacing: DS.Space.s2) { + Text("•").frame(width: DS.Space.s3, alignment: .leading) + richText(block.text) + .dsFont(.sm) + .lineSpacing(5) + .textSelection(.enabled) + .fixedSize(horizontal: false, vertical: true) + } + .padding(.bottom, DS.Space.s2) + } + } + + private func richText(_ source: String) -> Text { + Text((try? AttributedString(markdown: source)) ?? AttributedString(source)) + } +} diff --git a/Packages/XStatsKit/Sources/XStatsUI/Settings/SettingsPages.swift b/Packages/XStatsKit/Sources/XStatsUI/Settings/SettingsPages.swift index 136e5df..95ace45 100644 --- a/Packages/XStatsKit/Sources/XStatsUI/Settings/SettingsPages.swift +++ b/Packages/XStatsKit/Sources/XStatsUI/Settings/SettingsPages.swift @@ -761,6 +761,8 @@ private struct CapabilityLine: View { // MARK: - 关于 struct AboutSettings: View { + @State private var legalDocument: LegalDocument? + var body: some View { let version = Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String ?? tr("开发版") let build = Bundle.main.object(forInfoDictionaryKey: "CFBundleVersion") as? String @@ -790,6 +792,21 @@ struct AboutSettings: View { UpdateSettings() DiagnosticsSettings() + SettingsGroup(caption: tr("法律与隐私")) { + GroupRow(showsDivider: false) { + SettingRow(title: tr("服务条款"), subtitle: tr("了解 XStats 的使用条件与责任边界")) { + Button(tr("查看")) { legalDocument = .terms } + .buttonStyle(DSButtonStyle(kind: .secondary)) + } + } + GroupRow { + SettingRow(title: tr("隐私政策"), subtitle: tr("了解本机数据、联网功能与信息处理方式")) { + Button(tr("查看")) { legalDocument = .privacy } + .buttonStyle(DSButtonStyle(kind: .secondary)) + } + } + } + SettingsGroup(caption: tr("致谢")) { GroupRow { SettingRow(title: "gentpan/OpenStats", subtitle: tr("XStats 基于 OpenStats 开发,感谢原项目的开源贡献 · MIT License")) { @@ -808,5 +825,8 @@ struct AboutSettings: View { } } } + .sheet(item: $legalDocument) { document in + LegalDocumentSheet(document: document) + } } } diff --git a/Packages/XStatsKit/Tests/XStatsUITests/LegalDocumentTests.swift b/Packages/XStatsKit/Tests/XStatsUITests/LegalDocumentTests.swift new file mode 100644 index 0000000..bf0d417 --- /dev/null +++ b/Packages/XStatsKit/Tests/XStatsUITests/LegalDocumentTests.swift @@ -0,0 +1,26 @@ +// Copyright (C) 2026 ysicing +// SPDX-License-Identifier: AGPL-3.0-or-later + +import Localization +import Testing +@testable import XStatsUI + +@Suite struct LegalDocumentTests { + @Test(arguments: [LegalDocument.terms, .privacy], [AppLanguage.chinese, .english]) + func bundledDocumentsCanBeRead(document: LegalDocument, language: AppLanguage) throws { + let blocks = try #require(document.blocks(for: language)) + #expect(blocks.contains { $0.kind == .section }) + #expect(blocks.contains { $0.kind == .paragraph && $0.text.contains("i@xiai.me") }) + #expect(blocks.allSatisfy { !$0.text.hasPrefix("#") }) + if document == .privacy { + #expect(blocks.filter { $0.kind == .bullet }.count >= 3) + } + } + + @Test func markdownBlockBoundariesArePreserved() { + let source = "# Title\n\nDate\n\nIntro\n\n## Section\n\nFirst paragraph\ncontinued line\n\n- One\n- Two\n" + let blocks = LegalDocument.parse(source) + #expect(blocks.map(\.kind) == [.metadata, .paragraph, .section, .paragraph, .bullet, .bullet]) + #expect(blocks[3].text == "First paragraph continued line") + } +} diff --git a/README.en.md b/README.en.md index 4111146..917deb8 100644 --- a/README.en.md +++ b/README.en.md @@ -19,7 +19,7 @@ CPU load, GPU, memory pressure, network speed, disk, battery, temperatures and f lets you act on it: spin the fans up, keep the Mac awake with the lid closed, clear caches, fully uninstall apps and disable startup items. Network details also check how clean your public IP is — whether it is flagged as a VPN, proxy, data center or for abuse. -All system-monitoring metrics are read on your own Mac and are never uploaded. No XStats account is required: manually back up and restore preferences through your own WebDAV server. Update checks send the current version and the SHA-256 of a random installation ID for anonymous installation and version-distribution statistics; the original random value remains in local preferences, is not a credential, and is excluded from WebDAV sync. Other optional network features include a public-IP and cleanliness lookup, a ping probe, and a daily update check. +All system-monitoring metrics are read on your own Mac and are never uploaded. No XStats account is required: configure a WebDAV server yourself to manually back up and restore preferences; XStats does not provide or preconfigure a sync server. Update checks send the current version and the SHA-256 of a random installation ID for deduplicated installation and version-distribution statistics; the original random value remains in local preferences, is not a credential, and is excluded from WebDAV sync. Other optional network features include a public-IP and cleanliness lookup, a ping probe, and a daily update check. [Download](https://github.com/ysicing/xstats/releases) · [Changelog](CHANGELOG.md) · @@ -205,7 +205,7 @@ unclean exit restores them at the next boot. ## Your data Metrics come from the kernel (`host_processor_info`, `host_statistics64`, `sysctl`), IOKit and the -SMC on your own Mac. Preferences live in the app's user defaults and contain nothing personal. +SMC on your own Mac. Preferences live in the app's user defaults. Apart from update-statistics aggregates and settings backups you manually upload to WebDAV, the app's other persisted data stays on your Mac. Network tools still send the requests needed for a lookup or test to the relevant third party. Monitoring metrics, history, the hardware serial number and process lists are never uploaded. Every feature that touches the network can be turned off. Public IP and connection probes live on the Network page, update checks in Settings → About: @@ -227,7 +227,9 @@ Every feature that touches the network can be turned off. Public IP and connecti **AI usage** is entirely local: it reads Codex / Claude Code session logs on this Mac, never reads credentials, never queries subscription quotas and makes no network requests at all. Off by default. -WebDAV sync sends only preferences to your configured server. It excludes monitoring data, history and WebDAV credentials. +WebDAV sync sends only preferences when you configure a server and start a transfer manually. It excludes monitoring data, history and WebDAV credentials. + +Read the [Privacy Policy](Packages/XStatsKit/Sources/XStatsUI/Resources/Legal/Privacy.en.md) and [Terms of Service](Packages/XStatsKit/Sources/XStatsUI/Resources/Legal/Terms.en.md) in the repository or under Settings → About in the app. ### WebDAV settings sync diff --git a/README.ja.md b/README.ja.md index e755ba2..057466b 100644 --- a/README.ja.md +++ b/README.ja.md @@ -16,7 +16,7 @@ XStats は、Mac の各 CPU コア、GPU、メモリ負荷、通信速度、デ 状態の確認に加えて、ファンの回転数調整、蓋を閉じた状態でのスリープ防止、キャッシュ削除、アプリと関連ファイルの削除、起動項目の管理ができます。 ネットワーク画面では、公開 IP が VPN、プロキシ、データセンター、不正利用などに関連付けられているかも確認できます。Codex / Claude Code のローカルログからモデル別 Token 使用量も表示できます。 -監視指標は Mac 内だけで処理され、アップロードされません。XStats のアカウントは不要で、自分の WebDAV サーバーを使って設定を手動でバックアップ・復元できます。更新確認では、匿名のインストール数とバージョン分布を集計するため、現在のバージョンとランダムなインストール ID の SHA-256 を送信します。元のランダム値はローカルのキーチェーンにのみ保存されます。 +監視指標は Mac 内だけで処理され、アップロードされません。XStats のアカウントは不要で、自分の WebDAV サーバーを使って設定を手動でバックアップ・復元できます。更新確認では、インストール数の重複を除いてバージョン分布を集計するため、現在のバージョンとランダムなインストール ID の SHA-256 を送信します。元のランダム値はローカルの設定にのみ保存され、キーチェーンには保存されません。 公開 IP の照会、接続テスト、更新確認などのネットワーク機能は任意です。Codex 使用統計はローカル処理です。 [ダウンロード](https://github.com/ysicing/xstats/releases) · [変更履歴(中国語)](CHANGELOG.md) · [開発ガイド(中国語)](DEVELOPMENT.md) diff --git a/README.ko.md b/README.ko.md index f935a43..f681896 100644 --- a/README.ko.md +++ b/README.ko.md @@ -16,7 +16,7 @@ XStats는 CPU 코어별 사용량, GPU, 메모리 압력, 네트워크 속도, 팬 속도 조절, 덮개를 닫은 상태에서 실행 유지, 캐시 정리, 앱과 관련 파일 제거, 시작 항목 관리도 지원합니다. 네트워크 화면에서는 공인 IP가 VPN, 프록시, 데이터 센터 또는 악용 기록과 관련되어 있는지 확인할 수 있습니다. 로컬 Codex / Claude Code 로그로 모델별 토큰 사용량도 표시합니다. -모니터링 지표는 Mac 안에서만 처리하며 업로드하지 않습니다. XStats 계정 없이 자신의 WebDAV 서버로 설정을 수동 백업하고 복원할 수 있습니다. 업데이트 확인 시 익명 설치 수와 버전 분포를 집계하기 위해 현재 버전과 무작위 설치 ID의 SHA-256을 전송하며, 원본 무작위 값은 로컬 키체인에만 저장합니다. +모니터링 지표는 Mac 안에서만 처리하며 업로드하지 않습니다. XStats 계정 없이 자신의 WebDAV 서버로 설정을 수동 백업하고 복원할 수 있습니다. 업데이트 확인 시 중복을 제외한 설치 수와 버전 분포를 집계하기 위해 현재 버전과 무작위 설치 ID의 SHA-256을 전송하며, 원본 무작위 값은 키체인이 아닌 로컬 설정에만 저장합니다. 공인 IP 조회, 연결 테스트, 업데이트 확인 등의 네트워크 기능은 선택 사항입니다. Codex 사용 통계는 로컬에서 처리합니다. [다운로드](https://github.com/ysicing/xstats/releases) · [변경 기록(중국어)](CHANGELOG.md) · [개발 안내(중국어)](DEVELOPMENT.md) diff --git a/README.md b/README.md index f60ef9c..fbb0a81 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ XStats 是一款 macOS 菜单栏应用,实时显示 Mac 正在做什么:各核心 CPU 负载、GPU、内存压力、 网速、磁盘、电池、温度和风扇,也可以按需查看 Codex / Claude Code 本机模型使用统计;并且可以直接处理:给风扇提速、合盖后继续运行、清理缓存、彻底卸载应用、停用启动项; 网络详情还能检测公网 IP 的纯净度,看出出口是否被标记为 VPN、代理、机房或有滥用记录。 -所有系统监控指标都在你自己的 Mac 上读取,不会上传。不需要 XStats 账号,可通过自己的 WebDAV 服务器手动备份和恢复偏好设置。检查更新时会发送当前版本和随机安装标识的 SHA-256,用于匿名安装与版本分布统计;原始随机值只保存在本机偏好设置中,不作为凭据,也不参与 WebDAV 同步。其他可关闭的联网功能包括:打开网络详情时查询公网 IP 与纯净度、定时 ping 你选择的探测目标、每天检查一次新版本。 +所有系统监控指标都在你自己的 Mac 上读取,不会上传。不需要 XStats 账号,可自行配置 WebDAV 服务器,手动备份和恢复偏好设置;XStats 不提供或预设同步服务器。检查更新时会发送当前版本和随机安装标识的 SHA-256,用于去重安装与版本分布统计;原始随机值只保存在本机偏好设置中,不作为凭据,也不参与 WebDAV 同步。其他可关闭的联网功能包括:打开网络详情时查询公网 IP 与纯净度、定时 ping 你选择的探测目标、每天检查一次新版本。 [下载](https://github.com/ysicing/xstats/releases) · [更新日志](CHANGELOG.md) · @@ -216,8 +216,10 @@ XStats 暂无官网。自动更新与现有 GeoIP 服务保持不变;账号登 ## 你的数据 +详细说明见[隐私政策](Packages/XStatsKit/Sources/XStatsUI/Resources/Legal/Privacy.zh-Hans.md)和[服务条款](Packages/XStatsKit/Sources/XStatsUI/Resources/Legal/Terms.zh-Hans.md),也可在应用的“设置 → 关于”中阅读。 + 指标来自本机的内核接口(`host_processor_info`、`host_statistics64`、`sysctl`)、IOKit 与 SMC。 -偏好设置保存在应用自己的 user defaults 中,不含个人信息。监控指标、历史记录、硬件序列号和进程列表不会上传。 +偏好设置保存在应用自己的 user defaults 中。除更新统计汇总及你主动上传的 WebDAV 设置备份外,应用自身的其他持久化数据留在本机;联网工具仍会向相应第三方发送完成查询或测试所需的请求。监控指标、历史记录、硬件序列号和进程列表不会上传。 会联网的功能都可以关闭。公网 IP 与连接探测在网络页中,检查更新在“设置 · 关于”中: @@ -227,7 +229,7 @@ XStats 暂无官网。自动更新与现有 GeoIP 服务保持不变;账号登 **AI 使用统计**完全在本机完成:只读取 Codex / Claude Code 的本机会话日志,不读取登录凭据、不查询订阅配额,也不发送任何网络请求。默认关闭。 -WebDAV 只向你配置的服务器传输偏好设置,不上传监控数据、历史记录或 WebDAV 凭据。 +WebDAV 仅在你自行配置服务器并手动同步时传输偏好设置,不上传监控数据、历史记录或 WebDAV 凭据。 ### WebDAV 设置同步 diff --git a/server/api/dashboard.html b/server/api/dashboard.html index abceee6..ee17847 100644 --- a/server/api/dashboard.html +++ b/server/api/dashboard.html @@ -134,7 +134,7 @@ SPDX-License-Identifier: AGPL-3.0-or-later