feat(feedback): 添加错误报告与邮件反馈入口

This commit is contained in:
ysicing committed 2026-09-27 20:09:00 +08:00
1 parent 3c90b079d3
commit 3acc79472e
12 files changed
+132 -1

No files matched your search

+31
View File
@@ -0,0 +1,31 @@
---
name: 问题反馈 / Bug report
about: 反馈 XStats 的故障或异常 / Report an XStats bug
title: "[Bug] "
---
## 问题描述 / Description
请简要描述遇到的问题。Describe the problem.
## 复现步骤 / Steps to reproduce
1.
2.
3.
## 预期结果 / Expected behavior
## 实际结果 / Actual behavior
## 环境 / Environment
- XStats 版本与构建号 / version and build:
- macOS 版本 / version:
## 截图与日志 / Screenshots and logs
如有帮助,可在 XStats 的“设置 → 关于”中导出诊断信息并附上。请先检查附件,避免公开密码、令牌等敏感信息。
If useful, export diagnostics from XStats Settings → About and attach them after reviewing the archive for sensitive information.
@@ -397,6 +397,7 @@ uv 下载的 Python 包与构建缓存,由 uv 自行清理 ذاكرة حزم P
发现问题:{} وُجدت مشكلة: {}
发生这些状况时发送系统通知,点通知打开对应页面 إرسال إشعار عند حدوث هذه الحالات؛ انقر عليه لفتح الصفحة المعنية
发送 إرسال
发送反馈邮件 إرسال ملاحظات عبر البريد الإلكتروني
发送测试通知 إرسال إشعار اختبار
取消 إلغاء
受保护 محمي
@@ -467,6 +468,7 @@ uv 下载的 Python 包与构建缓存,由 uv 自行清理 ذاكرة حزم P
图标 أيقونة
圆环 حلقة
圆环表示当前占用比例 تعرض الحلقة الاستخدام الحالي
在 GitHub 提交问题 الإبلاغ عن مشكلة على GitHub
在 cleanip.io 查看这个 IP 的完整报告 فتح التقرير الكامل لعنوان IP هذا في cleanip.io
在主窗口打开“{}” فتح «{}» في النافذة الرئيسية
在程序坞显示图标 عرض الرمز في Dock
1 # Copyright (C) 2026 ysicing
397 发现问题:{}
398 发生这些状况时发送系统通知,点通知打开对应页面
399 发送
400 发送反馈邮件
401 发送测试通知
402 取消
403 受保护
468 图标
469 圆环
470 圆环表示当前占用比例
471 在 GitHub 提交问题
472 在 cleanip.io 查看这个 IP 的完整报告
473 在主窗口打开“{}”
474 在程序坞显示图标
@@ -397,6 +397,7 @@ uv 下载的 Python 包与构建缓存,由 uv 自行清理 Von uv geladener Py
发现问题:{} Problem gefunden: {}
发生这些状况时发送系统通知,点通知打开对应页面 Bei diesen Ereignissen eine Mitteilung senden; ein Klick öffnet die entsprechende Seite
发送 Senden
发送反馈邮件 Feedback per E-Mail senden
发送测试通知 Testmitteilung senden
取消 Abbrechen
受保护 Geschützt
@@ -467,6 +468,7 @@ uv 下载的 Python 包与构建缓存,由 uv 自行清理 Von uv geladener Py
图标 Symbol
圆环 Ring
圆环表示当前占用比例 Ein Ring zeigt die aktuelle Auslastung
在 GitHub 提交问题 Problem auf GitHub melden
在 cleanip.io 查看这个 IP 的完整报告 Vollständigen Bericht zu dieser IP auf cleanip.io öffnen
在主窗口打开“{}” „{}“ im Hauptfenster öffnen
在程序坞显示图标 Symbol im Dock anzeigen
1 # Copyright (C) 2026 ysicing
397 发现问题:{}
398 发生这些状况时发送系统通知,点通知打开对应页面
399 发送
400 发送反馈邮件
401 发送测试通知
402 取消
403 受保护
468 图标
469 圆环
470 圆环表示当前占用比例
471 在 GitHub 提交问题
472 在 cleanip.io 查看这个 IP 的完整报告
473 在主窗口打开“{}”
474 在程序坞显示图标
@@ -397,6 +397,7 @@ uv 下载的 Python 包与构建缓存,由 uv 自行清理 Cachés de paquetes
发现问题:{} Problema encontrado: {}
发生这些状况时发送系统通知,点通知打开对应页面 Enviar una notificación cuando ocurra; púlsala para abrir la página correspondiente
发送 Enviar
发送反馈邮件 Enviar correo de comentarios
发送测试通知 Enviar notificación de prueba
取消 Cancelar
受保护 Protegido
@@ -467,6 +468,7 @@ uv 下载的 Python 包与构建缓存,由 uv 自行清理 Cachés de paquetes
图标 Icono
圆环 Anillo
圆环表示当前占用比例 Un anillo muestra el uso actual
在 GitHub 提交问题 Informar de un problema en GitHub
在 cleanip.io 查看这个 IP 的完整报告 Abrir el informe completo de esta IP en cleanip.io
在主窗口打开“{}” Abrir «{}» en la ventana principal
在程序坞显示图标 Mostrar icono en el Dock
1 # Copyright (C) 2026 ysicing
397 发现问题:{}
398 发生这些状况时发送系统通知,点通知打开对应页面
399 发送
400 发送反馈邮件
401 发送测试通知
402 取消
403 受保护
468 图标
469 圆环
470 圆环表示当前占用比例
471 在 GitHub 提交问题
472 在 cleanip.io 查看这个 IP 的完整报告
473 在主窗口打开“{}”
474 在程序坞显示图标
@@ -397,6 +397,7 @@ uv 下载的 Python 包与构建缓存,由 uv 自行清理 Caches de paquets P
发现问题:{} Problème détecté : {}
发生这些状况时发送系统通知,点通知打开对应页面 Envoyer une notification lors de ces événements ; cliquer dessus ouvre la page correspondante
发送 Envoyer
发送反馈邮件 Envoyer un retour par e-mail
发送测试通知 Envoyer une notification de test
取消 Annuler
受保护 Protégé
@@ -467,6 +468,7 @@ uv 下载的 Python 包与构建缓存,由 uv 自行清理 Caches de paquets P
图标 Icône
圆环 Anneau
圆环表示当前占用比例 Un anneau représente l’utilisation actuelle
在 GitHub 提交问题 Signaler un problème sur GitHub
在 cleanip.io 查看这个 IP 的完整报告 Ouvrir le rapport complet de cette IP sur cleanip.io
在主窗口打开“{}” Ouvrir « {} » dans la fenêtre principale
在程序坞显示图标 Afficher l’icône dans le Dock
1 # Copyright (C) 2026 ysicing
397 发现问题:{}
398 发生这些状况时发送系统通知,点通知打开对应页面
399 发送
400 发送反馈邮件
401 发送测试通知
402 取消
403 受保护
468 图标
469 圆环
470 圆环表示当前占用比例
471 在 GitHub 提交问题
472 在 cleanip.io 查看这个 IP 的完整报告
473 在主窗口打开“{}”
474 在程序坞显示图标
@@ -397,6 +397,7 @@ uv 下载的 Python 包与构建缓存,由 uv 自行清理 uvのPythonパッ
发现问题:{} 問題が見つかりました:{}
发生这些状况时发送系统通知,点通知打开对应页面 これらの状態で通知を送信します。通知をクリックすると関連ページが開きます
发送 送信
发送反馈邮件 フィードバックをメールで送信
发送测试通知 テスト通知を送信
取消 キャンセル
受保护 保護されています
@@ -467,6 +468,7 @@ uv 下载的 Python 包与构建缓存,由 uv 自行清理 uvのPythonパッ
图标 アイコン
圆环 リング
圆环表示当前占用比例 リングは現在の使用率を示します
在 GitHub 提交问题 GitHubで問題を報告
在 cleanip.io 查看这个 IP 的完整报告 cleanip.ioでこのIPの詳細レポートを開く
在主窗口打开“{}” メインウインドウで「{}」を開く
在程序坞显示图标 Dockにアイコンを表示
1 # Copyright (C) 2026 ysicing
397 发现问题:{}
398 发生这些状况时发送系统通知,点通知打开对应页面
399 发送
400 发送反馈邮件
401 发送测试通知
402 取消
403 受保护
468 图标
469 圆环
470 圆环表示当前占用比例
471 在 GitHub 提交问题
472 在 cleanip.io 查看这个 IP 的完整报告
473 在主窗口打开“{}”
474 在程序坞显示图标
@@ -397,6 +397,7 @@ uv 下载的 Python 包与构建缓存,由 uv 自行清理 uv의 Python 패키
发现问题:{} 문제 발견: {}
发生这些状况时发送系统通知,点通知打开对应页面 이러한 상황에서 알림을 보냅니다. 알림을 클릭하면 관련 페이지가 열립니다
发送 보내기
发送反馈邮件 피드백 이메일 보내기
发送测试通知 테스트 알림 보내기
取消 취소
受保护 보호됨
@@ -467,6 +468,7 @@ uv 下载的 Python 包与构建缓存,由 uv 自行清理 uv의 Python 패키
图标 아이콘
圆环 링
圆环表示当前占用比例 링은 현재 사용률을 표시합니다
在 GitHub 提交问题 GitHub에 문제 신고
在 cleanip.io 查看这个 IP 的完整报告 cleanip.io에서 이 IP의 전체 보고서 열기
在主窗口打开“{}” 주 윈도우에서 “{}” 열기
在程序坞显示图标 Dock에 아이콘 표시
1 # Copyright (C) 2026 ysicing
397 发现问题:{}
398 发生这些状况时发送系统通知,点通知打开对应页面
399 发送
400 发送反馈邮件
401 发送测试通知
402 取消
403 受保护
468 图标
469 圆环
470 圆环表示当前占用比例
471 在 GitHub 提交问题
472 在 cleanip.io 查看这个 IP 的完整报告
473 在主窗口打开“{}”
474 在程序坞显示图标
@@ -398,6 +398,7 @@ uv 下载的 Python 包与构建缓存,由 uv 自行清理 uv 下載的 Python
发现问题:{} 發現問題:{}
发生这些状况时发送系统通知,点通知打开对应页面 發生這些狀況時發送系統通知,點通知打開對應頁面
发送 發送
发送反馈邮件 傳送回饋郵件
发送测试通知 發送測試通知
取消 取消
受保护 受保護
@@ -468,6 +469,7 @@ uv 下载的 Python 包与构建缓存,由 uv 自行清理 uv 下載的 Python
图标 圖標
圆环 圓環
圆环表示当前占用比例 圓環表示當前佔用比例
在 GitHub 提交问题 在 GitHub 提交問題
在 cleanip.io 查看这个 IP 的完整报告 在 cleanip.io 查看這個 IP 的完整報告
在主窗口打开“{}” 在主窗口打開“{}”
在程序坞显示图标 在程序塢顯示圖標
1 # Copyright (c) 2026 GiantAccel, LLC
398 发现问题:{}
399 发生这些状况时发送系统通知,点通知打开对应页面
400 发送
401 发送反馈邮件
402 发送测试通知
403 取消
404 受保护
469 图标
470 圆环
471 圆环表示当前占用比例
472 在 GitHub 提交问题
473 在 cleanip.io 查看这个 IP 的完整报告
474 在主窗口打开“{}”
475 在程序坞显示图标
@@ -518,6 +518,7 @@ uv 下载的 Python 包与构建缓存,由 uv 自行清理 Python package and
发现问题:{} Problem found: {}
发生这些状况时发送系统通知,点通知打开对应页面 Send a notification when these happen; click it to open the related page
发送 Send
发送反馈邮件 Send Feedback Email
发送测试通知 Send Test Notification
取消 Cancel
受保护 Protected
@@ -588,6 +589,7 @@ uv 下载的 Python 包与构建缓存,由 uv 自行清理 Python package and
图标 Icon
圆环 Ring
圆环表示当前占用比例 A ring shows current usage
在 GitHub 提交问题 Report a Problem on GitHub
在 cleanip.io 查看这个 IP 的完整报告 Open this IP's full report on cleanip.io
在主窗口打开“{}” Open “{}” in the main window
在程序坞显示图标 Show Icon in Dock
@@ -477,7 +477,7 @@ private struct GlassButtonSurface: ViewModifier {
}
}
private struct IconButtonSurface: ViewModifier {
struct IconButtonSurface: ViewModifier {
let hovering: Bool
let glass: Bool
@@ -153,6 +153,7 @@ private struct MainSidebar: View {
HStack(spacing: DS.Space.s1) {
ThemeToggle()
FeedbackMenu()
Spacer(minLength: 0)
IconButton(systemName: "power", help: tr("退出 XStats")) { model.quit() }
}
@@ -208,6 +209,61 @@ private struct MainSidebar: View {
}
}
private struct FeedbackMenu: View {
@Environment(\.isInsideGlass) private var isInsideGlass
@State private var hovering = false
var body: some View {
let glass = DS.Glass.isAvailable && !isInsideGlass
Menu {
Button {
NSWorkspace.shared.open(FeedbackLinks.githubBugReport)
} label: {
Label(tr("在 GitHub 提交问题"), systemImage: "ladybug")
}
Button {
let version = Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String ?? tr("开发版")
let build = Bundle.main.object(forInfoDictionaryKey: "CFBundleVersion") as? String
let systemVersion = ProcessInfo.processInfo.operatingSystemVersionString
if let url = FeedbackLinks.email(version: version, build: build, systemVersion: systemVersion) {
NSWorkspace.shared.open(url)
}
} label: {
Label(tr("发送反馈邮件"), systemImage: "envelope")
}
} label: {
Image(systemName: "ladybug")
.font(.system(size: DS.TextSize.sm.rawValue, weight: .medium))
.foregroundStyle(hovering ? DS.Palette.primary : glass ? DS.Palette.textPrimary : DS.Palette.textSecondary)
.frame(width: DS.Size.controlHeight, height: DS.Size.controlHeight)
.modifier(IconButtonSurface(hovering: hovering, glass: glass))
.contentShape(Circle())
}
.menuStyle(.borderlessButton)
.menuIndicator(.hidden)
.frame(width: DS.Size.controlHeight)
.onHover { hovering = $0 }
.help(tr("反馈问题"))
.accessibilityLabel(tr("反馈问题"))
}
}
enum FeedbackLinks {
static let githubBugReport = URL(string: "https://github.com/ysicing/XStats/issues/new?template=bug_report.md")!
static func email(version: String, build: String?, systemVersion: String) -> URL? {
let appVersion = build.map { "\(version) (\($0))" } ?? version
var components = URLComponents()
components.scheme = "mailto"
components.path = "i@xiai.me"
components.queryItems = [
URLQueryItem(name: "subject", value: tr("反馈问题")),
URLQueryItem(name: "body", value: "XStats \(appVersion)\nmacOS \(systemVersion)\n\n"),
]
return components.url
}
}
private struct PageHeader: View {
@Environment(AppModel.self) private var model
@@ -0,0 +1,28 @@
// Copyright (C) 2026 ysicing
// SPDX-License-Identifier: AGPL-3.0-or-later
import Foundation
import Testing
@testable import XStatsUI
@Suite struct FeedbackLinksTests {
@Test func githubOpensTheBugReportTemplate() {
let url = FeedbackLinks.githubBugReport
let components = URLComponents(url: url, resolvingAgainstBaseURL: false)
#expect(url.scheme == "https")
#expect(url.host == "github.com")
#expect(url.path == "/ysicing/XStats/issues/new")
#expect(components?.queryItems?.first(where: { $0.name == "template" })?.value == "bug_report.md")
}
@Test func emailPrefillsAppBuildAndSystemVersion() throws {
let url = try #require(FeedbackLinks.email(version: "0.10.0", build: "119",
systemVersion: "Version 26.0 (Build 25A123)"))
let components = try #require(URLComponents(url: url, resolvingAgainstBaseURL: false))
#expect(components.scheme == "mailto")
#expect(components.path == "i@xiai.me")
#expect(components.queryItems?.first(where: { $0.name == "subject" })?.value?.isEmpty == false)
#expect(components.queryItems?.first(where: { $0.name == "body" })?.value
== "XStats 0.10.0 (119)\nmacOS Version 26.0 (Build 25A123)\n\n")
}
}