create-pixi-vn 2.1.4 → 2.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/template-lib/package.json +3 -0
- package/template-nqtr-react-vite-muijoy/package.json +2 -2
- package/template-nqtr-react-vite-muijoy/src/assets/index.ts +2 -11
- package/template-nqtr-react-vite-muijoy/src/assets/manifest.gen.json +2 -7
- package/template-nqtr-react-vite-muijoy/src/components/menus/settings/index.tsx +15 -6
- package/template-nqtr-react-vite-muijoy/src/components/menus/settings/menus/diagnostics.tsx +266 -0
- package/template-nqtr-react-vite-muijoy/src/components/menus/settings/quick-menus.tsx +25 -1
- package/template-nqtr-react-vite-muijoy/src/lib/hooks/device-diagnostics-hooks.ts +68 -0
- package/template-nqtr-react-vite-muijoy/src/lib/hooks/hotkeys-hooks.ts +16 -0
- package/template-nqtr-react-vite-muijoy/src/lib/utils/device-diagnostics-utility.ts +382 -0
- package/template-nqtr-react-vite-muijoy/src/locales/en.json +50 -0
- package/template-nqtr-react-vite-muijoy/src/pixi-vn.keys.gen.ts +4 -7
- package/template-nqtr-react-vite-muijoy-ink/package.json +3 -3
- package/template-nqtr-react-vite-muijoy-ink/src/assets/index.ts +2 -11
- package/template-nqtr-react-vite-muijoy-ink/src/assets/manifest.gen.json +2 -7
- package/template-nqtr-react-vite-muijoy-ink/src/components/menus/settings/index.tsx +15 -6
- package/template-nqtr-react-vite-muijoy-ink/src/components/menus/settings/menus/diagnostics.tsx +266 -0
- package/template-nqtr-react-vite-muijoy-ink/src/components/menus/settings/quick-menus.tsx +25 -1
- package/template-nqtr-react-vite-muijoy-ink/src/lib/hooks/device-diagnostics-hooks.ts +68 -0
- package/template-nqtr-react-vite-muijoy-ink/src/lib/hooks/hotkeys-hooks.ts +16 -0
- package/template-nqtr-react-vite-muijoy-ink/src/lib/utils/device-diagnostics-utility.ts +382 -0
- package/template-nqtr-react-vite-muijoy-ink/src/locales/en.json +50 -0
- package/template-nqtr-react-vite-muijoy-ink/src/pixi-vn.keys.gen.ts +4 -7
- package/template-nqtr-react-vite-muijoy-ink-tauri/.vscode/launch.json +1 -1
- package/template-nqtr-react-vite-muijoy-ink-tauri/.vscode/tasks.json +1 -35
- package/template-nqtr-react-vite-muijoy-ink-tauri/package.json +3 -3
- package/template-nqtr-react-vite-muijoy-ink-tauri/src/assets/index.ts +2 -11
- package/template-nqtr-react-vite-muijoy-ink-tauri/src/assets/manifest.gen.json +2 -7
- package/template-nqtr-react-vite-muijoy-ink-tauri/src/components/menus/settings/index.tsx +15 -6
- package/template-nqtr-react-vite-muijoy-ink-tauri/src/components/menus/settings/menus/diagnostics.tsx +332 -0
- package/template-nqtr-react-vite-muijoy-ink-tauri/src/components/menus/settings/quick-menus.tsx +32 -1
- package/template-nqtr-react-vite-muijoy-ink-tauri/src/content/ink/hashtag-commands.ts +17 -0
- package/template-nqtr-react-vite-muijoy-ink-tauri/src/lib/hooks/device-diagnostics-hooks.ts +96 -0
- package/template-nqtr-react-vite-muijoy-ink-tauri/src/lib/hooks/hotkeys-hooks.ts +16 -0
- package/template-nqtr-react-vite-muijoy-ink-tauri/src/lib/system-info.ts +54 -0
- package/template-nqtr-react-vite-muijoy-ink-tauri/src/lib/utils/device-diagnostics-utility.ts +511 -0
- package/template-nqtr-react-vite-muijoy-ink-tauri/src/locales/en.json +66 -0
- package/template-nqtr-react-vite-muijoy-ink-tauri/src/pixi-vn.keys.gen.ts +4 -7
- package/template-nqtr-react-vite-muijoy-ink-tauri/src-tauri/Cargo.toml +1 -0
- package/template-nqtr-react-vite-muijoy-ink-tauri/src-tauri/src/lib.rs +34 -22
- package/template-nqtr-react-vite-muijoy-ink-tauri/src-tauri/src/system_info.rs +33 -0
- package/template-nqtr-react-vite-muijoy-tauri/.vscode/launch.json +1 -1
- package/template-nqtr-react-vite-muijoy-tauri/.vscode/tasks.json +1 -35
- package/template-nqtr-react-vite-muijoy-tauri/package.json +2 -2
- package/template-nqtr-react-vite-muijoy-tauri/src/assets/index.ts +2 -11
- package/template-nqtr-react-vite-muijoy-tauri/src/assets/manifest.gen.json +2 -7
- package/template-nqtr-react-vite-muijoy-tauri/src/components/menus/settings/index.tsx +15 -6
- package/template-nqtr-react-vite-muijoy-tauri/src/components/menus/settings/menus/diagnostics.tsx +332 -0
- package/template-nqtr-react-vite-muijoy-tauri/src/components/menus/settings/quick-menus.tsx +32 -1
- package/template-nqtr-react-vite-muijoy-tauri/src/lib/hooks/device-diagnostics-hooks.ts +96 -0
- package/template-nqtr-react-vite-muijoy-tauri/src/lib/hooks/hotkeys-hooks.ts +16 -0
- package/template-nqtr-react-vite-muijoy-tauri/src/lib/system-info.ts +54 -0
- package/template-nqtr-react-vite-muijoy-tauri/src/lib/utils/device-diagnostics-utility.ts +511 -0
- package/template-nqtr-react-vite-muijoy-tauri/src/locales/en.json +66 -0
- package/template-nqtr-react-vite-muijoy-tauri/src/pixi-vn.keys.gen.ts +4 -7
- package/template-nqtr-react-vite-muijoy-tauri/src-tauri/Cargo.toml +1 -0
- package/template-nqtr-react-vite-muijoy-tauri/src-tauri/src/lib.rs +32 -20
- package/template-nqtr-react-vite-muijoy-tauri/src-tauri/src/system_info.rs +33 -0
- package/template-react-vite-muijoy/package.json +1 -1
- package/template-react-vite-muijoy/src/assets/index.ts +55 -55
- package/template-react-vite-muijoy/src/assets/manifest.gen.json +2 -7
- package/template-react-vite-muijoy/src/components/menus/settings/index.tsx +15 -6
- package/template-react-vite-muijoy/src/components/menus/settings/menus/diagnostics.tsx +266 -0
- package/template-react-vite-muijoy/src/components/menus/settings/quick-menus.tsx +25 -1
- package/template-react-vite-muijoy/src/lib/hooks/device-diagnostics-hooks.ts +68 -0
- package/template-react-vite-muijoy/src/lib/hooks/hotkeys-hooks.ts +16 -0
- package/template-react-vite-muijoy/src/lib/utils/device-diagnostics-utility.ts +382 -0
- package/template-react-vite-muijoy/src/locales/en.json +50 -0
- package/template-react-vite-muijoy/src/pixi-vn.keys.gen.ts +2 -3
- package/template-react-vite-muijoy-ink/ink/second_part.ink +0 -1
- package/template-react-vite-muijoy-ink/ink/start.ink +4 -6
- package/template-react-vite-muijoy-ink/package.json +2 -2
- package/template-react-vite-muijoy-ink/src/assets/index.ts +55 -55
- package/template-react-vite-muijoy-ink/src/assets/manifest.gen.json +2 -7
- package/template-react-vite-muijoy-ink/src/components/menus/settings/index.tsx +15 -6
- package/template-react-vite-muijoy-ink/src/components/menus/settings/menus/diagnostics.tsx +266 -0
- package/template-react-vite-muijoy-ink/src/components/menus/settings/quick-menus.tsx +25 -1
- package/template-react-vite-muijoy-ink/src/lib/hooks/device-diagnostics-hooks.ts +68 -0
- package/template-react-vite-muijoy-ink/src/lib/hooks/hotkeys-hooks.ts +16 -0
- package/template-react-vite-muijoy-ink/src/lib/utils/device-diagnostics-utility.ts +382 -0
- package/template-react-vite-muijoy-ink/src/locales/en.json +50 -0
- package/template-react-vite-muijoy-ink/src/pixi-vn.keys.gen.ts +2 -3
- package/template-react-vite-muijoy-ink-tauri/.vscode/launch.json +1 -1
- package/template-react-vite-muijoy-ink-tauri/.vscode/tasks.json +1 -35
- package/template-react-vite-muijoy-ink-tauri/ink/second_part.ink +0 -1
- package/template-react-vite-muijoy-ink-tauri/ink/start.ink +4 -6
- package/template-react-vite-muijoy-ink-tauri/package.json +2 -2
- package/template-react-vite-muijoy-ink-tauri/src/assets/index.ts +55 -55
- package/template-react-vite-muijoy-ink-tauri/src/assets/manifest.gen.json +2 -7
- package/template-react-vite-muijoy-ink-tauri/src/components/menus/settings/index.tsx +15 -6
- package/template-react-vite-muijoy-ink-tauri/src/components/menus/settings/menus/diagnostics.tsx +332 -0
- package/template-react-vite-muijoy-ink-tauri/src/components/menus/settings/quick-menus.tsx +32 -1
- package/template-react-vite-muijoy-ink-tauri/src/lib/hooks/device-diagnostics-hooks.ts +96 -0
- package/template-react-vite-muijoy-ink-tauri/src/lib/hooks/hotkeys-hooks.ts +16 -0
- package/template-react-vite-muijoy-ink-tauri/src/lib/system-info.ts +54 -0
- package/template-react-vite-muijoy-ink-tauri/src/lib/utils/device-diagnostics-utility.ts +511 -0
- package/template-react-vite-muijoy-ink-tauri/src/locales/en.json +66 -0
- package/template-react-vite-muijoy-ink-tauri/src/pixi-vn.keys.gen.ts +2 -3
- package/template-react-vite-muijoy-ink-tauri/src-tauri/Cargo.toml +1 -0
- package/template-react-vite-muijoy-ink-tauri/src-tauri/src/lib.rs +32 -20
- package/template-react-vite-muijoy-ink-tauri/src-tauri/src/system_info.rs +33 -0
- package/template-react-vite-muijoy-tauri/.vscode/launch.json +1 -1
- package/template-react-vite-muijoy-tauri/.vscode/tasks.json +1 -35
- package/template-react-vite-muijoy-tauri/package.json +1 -1
- package/template-react-vite-muijoy-tauri/src/assets/index.ts +55 -55
- package/template-react-vite-muijoy-tauri/src/assets/manifest.gen.json +2 -7
- package/template-react-vite-muijoy-tauri/src/components/menus/settings/index.tsx +15 -6
- package/template-react-vite-muijoy-tauri/src/components/menus/settings/menus/diagnostics.tsx +332 -0
- package/template-react-vite-muijoy-tauri/src/components/menus/settings/quick-menus.tsx +32 -1
- package/template-react-vite-muijoy-tauri/src/lib/hooks/device-diagnostics-hooks.ts +96 -0
- package/template-react-vite-muijoy-tauri/src/lib/hooks/hotkeys-hooks.ts +16 -0
- package/template-react-vite-muijoy-tauri/src/lib/system-info.ts +54 -0
- package/template-react-vite-muijoy-tauri/src/lib/utils/device-diagnostics-utility.ts +511 -0
- package/template-react-vite-muijoy-tauri/src/locales/en.json +66 -0
- package/template-react-vite-muijoy-tauri/src/pixi-vn.keys.gen.ts +2 -3
- package/template-react-vite-muijoy-tauri/src-tauri/Cargo.toml +1 -0
- package/template-react-vite-muijoy-tauri/src-tauri/src/lib.rs +32 -20
- package/template-react-vite-muijoy-tauri/src-tauri/src/system_info.rs +33 -0
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
import { Button } from "@/components/ui/button";
|
|
2
|
+
import { ScrollArea } from "@/components/ui/scroll-area";
|
|
3
|
+
import { useDeviceDiagnostics } from "@/lib/hooks/device-diagnostics-hooks";
|
|
4
|
+
import {
|
|
5
|
+
buildDiagnosticsReportText,
|
|
6
|
+
downloadDiagnosticsReportJson,
|
|
7
|
+
} from "@/lib/utils/device-diagnostics-utility";
|
|
8
|
+
import { AlertTriangleIcon, CheckIcon, ClipboardCopyIcon, DownloadIcon, XIcon } from "lucide-react";
|
|
9
|
+
import type { ReactNode } from "react";
|
|
10
|
+
import { useTranslation } from "react-i18next";
|
|
11
|
+
import { toast } from "sonner";
|
|
12
|
+
|
|
13
|
+
function DiagnosticsSection({ title, children }: { title: string; children: ReactNode }) {
|
|
14
|
+
return (
|
|
15
|
+
<div className="flex flex-col gap-2">
|
|
16
|
+
<h3 className="text-sm font-semibold uppercase tracking-wide text-muted-foreground">
|
|
17
|
+
{title}
|
|
18
|
+
</h3>
|
|
19
|
+
<div className="flex flex-col gap-1 rounded-lg border bg-card/80 p-3">{children}</div>
|
|
20
|
+
</div>
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function DiagnosticRow({ label, value }: { label: string; value: ReactNode }) {
|
|
25
|
+
return (
|
|
26
|
+
<div className="flex items-start justify-between gap-4 text-sm">
|
|
27
|
+
<span className="text-muted-foreground">{label}</span>
|
|
28
|
+
<span className="text-right font-medium break-all">{value}</span>
|
|
29
|
+
</div>
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function CapabilityRow({
|
|
34
|
+
label,
|
|
35
|
+
supported,
|
|
36
|
+
hint,
|
|
37
|
+
}: {
|
|
38
|
+
label: string;
|
|
39
|
+
supported: boolean | null;
|
|
40
|
+
hint?: string;
|
|
41
|
+
}) {
|
|
42
|
+
const { t } = useTranslation(["ui"]);
|
|
43
|
+
return (
|
|
44
|
+
<div className="flex items-center justify-between gap-4 text-sm">
|
|
45
|
+
<div>
|
|
46
|
+
<p className="font-medium">{label}</p>
|
|
47
|
+
{hint && <p className="text-xs text-muted-foreground">{hint}</p>}
|
|
48
|
+
</div>
|
|
49
|
+
{supported === null ? (
|
|
50
|
+
<span className="shrink-0 text-xs text-muted-foreground">
|
|
51
|
+
{t("diagnostics_unknown")}
|
|
52
|
+
</span>
|
|
53
|
+
) : supported ? (
|
|
54
|
+
<CheckIcon className="size-4 shrink-0 text-green-600 dark:text-green-400" />
|
|
55
|
+
) : (
|
|
56
|
+
<XIcon className="size-4 shrink-0 text-red-600 dark:text-red-400" />
|
|
57
|
+
)}
|
|
58
|
+
</div>
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function DiagnosticsSettingsPage() {
|
|
63
|
+
const diagnostics = useDeviceDiagnostics();
|
|
64
|
+
const { t } = useTranslation(["ui"]);
|
|
65
|
+
const { webgl, capabilities, mobile, native, resolution, memory, fps } = diagnostics;
|
|
66
|
+
const yesNo = (value: boolean | null) =>
|
|
67
|
+
value === null
|
|
68
|
+
? t("diagnostics_unknown")
|
|
69
|
+
: value
|
|
70
|
+
? t("diagnostics_yes")
|
|
71
|
+
: t("diagnostics_no");
|
|
72
|
+
|
|
73
|
+
const handleCopyReport = async () => {
|
|
74
|
+
try {
|
|
75
|
+
await navigator.clipboard.writeText(buildDiagnosticsReportText(diagnostics));
|
|
76
|
+
toast.success(t("diagnostics_report_copied"));
|
|
77
|
+
} catch {
|
|
78
|
+
toast.error(t("diagnostics_report_copy_failed"));
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
const handleDownloadReport = () => {
|
|
83
|
+
downloadDiagnosticsReportJson(diagnostics);
|
|
84
|
+
toast.success(t("diagnostics_report_downloaded"));
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
return (
|
|
88
|
+
<ScrollArea className="flex-1 min-h-0">
|
|
89
|
+
<div className="mx-auto flex max-w-3xl flex-col gap-4 p-4">
|
|
90
|
+
<div className="flex justify-end gap-2">
|
|
91
|
+
<Button variant="outline" onClick={handleDownloadReport}>
|
|
92
|
+
<DownloadIcon />
|
|
93
|
+
{t("diagnostics_download_report")}
|
|
94
|
+
</Button>
|
|
95
|
+
<Button onClick={handleCopyReport}>
|
|
96
|
+
<ClipboardCopyIcon />
|
|
97
|
+
{t("diagnostics_copy_report")}
|
|
98
|
+
</Button>
|
|
99
|
+
</div>
|
|
100
|
+
|
|
101
|
+
{!webgl.webgl2Supported && (
|
|
102
|
+
<div className="flex items-start gap-3 rounded-lg border border-destructive/40 bg-destructive/10 p-3 text-sm text-destructive">
|
|
103
|
+
<AlertTriangleIcon className="mt-0.5 size-5 shrink-0" />
|
|
104
|
+
<p>{t("diagnostics_webgl2_missing_message")}</p>
|
|
105
|
+
</div>
|
|
106
|
+
)}
|
|
107
|
+
|
|
108
|
+
<DiagnosticsSection title={t("diagnostics_section_engine")}>
|
|
109
|
+
<DiagnosticRow
|
|
110
|
+
label={t("diagnostics_game_version")}
|
|
111
|
+
value={diagnostics.gameVersion}
|
|
112
|
+
/>
|
|
113
|
+
<DiagnosticRow
|
|
114
|
+
label={t("diagnostics_pixijs_version")}
|
|
115
|
+
value={diagnostics.pixiJsVersion}
|
|
116
|
+
/>
|
|
117
|
+
<DiagnosticRow
|
|
118
|
+
label={t("diagnostics_pixivn_version")}
|
|
119
|
+
value={diagnostics.pixiVnVersion}
|
|
120
|
+
/>
|
|
121
|
+
<DiagnosticRow
|
|
122
|
+
label={t("diagnostics_tonejs_version")}
|
|
123
|
+
value={diagnostics.toneJsVersion}
|
|
124
|
+
/>
|
|
125
|
+
<DiagnosticRow
|
|
126
|
+
label={t("diagnostics_motion_version")}
|
|
127
|
+
value={diagnostics.motionVersion}
|
|
128
|
+
/>
|
|
129
|
+
</DiagnosticsSection>
|
|
130
|
+
|
|
131
|
+
<DiagnosticsSection title={t("diagnostics_section_device")}>
|
|
132
|
+
<DiagnosticRow label={t("diagnostics_platform")} value={diagnostics.platform} />
|
|
133
|
+
<DiagnosticRow
|
|
134
|
+
label={t("diagnostics_browser_engine")}
|
|
135
|
+
value={diagnostics.browserEngine}
|
|
136
|
+
/>
|
|
137
|
+
<DiagnosticRow
|
|
138
|
+
label={t("diagnostics_engine_version")}
|
|
139
|
+
value={diagnostics.engineVersion ?? t("diagnostics_unknown")}
|
|
140
|
+
/>
|
|
141
|
+
<DiagnosticRow
|
|
142
|
+
label={t("diagnostics_user_agent")}
|
|
143
|
+
value={diagnostics.userAgent}
|
|
144
|
+
/>
|
|
145
|
+
</DiagnosticsSection>
|
|
146
|
+
|
|
147
|
+
<DiagnosticsSection title={t("diagnostics_section_native")}>
|
|
148
|
+
{native ? (
|
|
149
|
+
<>
|
|
150
|
+
<DiagnosticRow
|
|
151
|
+
label={t("diagnostics_os")}
|
|
152
|
+
value={`${native.osType} ${native.osVersion}`}
|
|
153
|
+
/>
|
|
154
|
+
<DiagnosticRow
|
|
155
|
+
label={t("diagnostics_os_bitness")}
|
|
156
|
+
value={native.bitness}
|
|
157
|
+
/>
|
|
158
|
+
<DiagnosticRow
|
|
159
|
+
label={t("diagnostics_os_architecture")}
|
|
160
|
+
value={native.architecture}
|
|
161
|
+
/>
|
|
162
|
+
<DiagnosticRow
|
|
163
|
+
label={t("diagnostics_webview_version")}
|
|
164
|
+
value={native.webviewVersion ?? t("diagnostics_unknown")}
|
|
165
|
+
/>
|
|
166
|
+
</>
|
|
167
|
+
) : (
|
|
168
|
+
<p className="text-sm text-muted-foreground">
|
|
169
|
+
{t("diagnostics_native_unavailable")}
|
|
170
|
+
</p>
|
|
171
|
+
)}
|
|
172
|
+
</DiagnosticsSection>
|
|
173
|
+
|
|
174
|
+
<DiagnosticsSection title={t("diagnostics_section_graphics")}>
|
|
175
|
+
<DiagnosticRow
|
|
176
|
+
label={t("diagnostics_webgl1")}
|
|
177
|
+
value={yesNo(webgl.webgl1Supported)}
|
|
178
|
+
/>
|
|
179
|
+
<DiagnosticRow
|
|
180
|
+
label={t("diagnostics_webgl2")}
|
|
181
|
+
value={yesNo(webgl.webgl2Supported)}
|
|
182
|
+
/>
|
|
183
|
+
<DiagnosticRow
|
|
184
|
+
label={t("diagnostics_hardware_acceleration")}
|
|
185
|
+
value={yesNo(webgl.hardwareAccelerated)}
|
|
186
|
+
/>
|
|
187
|
+
<DiagnosticRow
|
|
188
|
+
label={t("diagnostics_gpu_vendor")}
|
|
189
|
+
value={webgl.unmaskedVendor ?? webgl.vendor ?? t("diagnostics_unknown")}
|
|
190
|
+
/>
|
|
191
|
+
<DiagnosticRow
|
|
192
|
+
label={t("diagnostics_gpu_renderer")}
|
|
193
|
+
value={webgl.unmaskedRenderer ?? webgl.renderer ?? t("diagnostics_unknown")}
|
|
194
|
+
/>
|
|
195
|
+
<DiagnosticRow
|
|
196
|
+
label={t("diagnostics_gpu_vendor_brand")}
|
|
197
|
+
value={webgl.gpuVendorBrand}
|
|
198
|
+
/>
|
|
199
|
+
<DiagnosticRow
|
|
200
|
+
label={t("diagnostics_mesa_driver")}
|
|
201
|
+
value={webgl.mesaVersion ?? t("diagnostics_not_detected")}
|
|
202
|
+
/>
|
|
203
|
+
<DiagnosticRow
|
|
204
|
+
label={t("diagnostics_max_texture_size")}
|
|
205
|
+
value={webgl.maxTextureSize ?? t("diagnostics_unknown")}
|
|
206
|
+
/>
|
|
207
|
+
</DiagnosticsSection>
|
|
208
|
+
|
|
209
|
+
<DiagnosticsSection title={t("diagnostics_section_display")}>
|
|
210
|
+
<DiagnosticRow
|
|
211
|
+
label={t("diagnostics_resolution")}
|
|
212
|
+
value={`${resolution.innerWidth}x${resolution.innerHeight}`}
|
|
213
|
+
/>
|
|
214
|
+
<DiagnosticRow
|
|
215
|
+
label={t("diagnostics_device_pixel_ratio")}
|
|
216
|
+
value={resolution.devicePixelRatio}
|
|
217
|
+
/>
|
|
218
|
+
<DiagnosticRow
|
|
219
|
+
label={t("diagnostics_fps")}
|
|
220
|
+
value={fps ?? t("diagnostics_unknown")}
|
|
221
|
+
/>
|
|
222
|
+
</DiagnosticsSection>
|
|
223
|
+
|
|
224
|
+
<DiagnosticsSection title={t("diagnostics_section_mobile")}>
|
|
225
|
+
<DiagnosticRow
|
|
226
|
+
label={t("diagnostics_touch_support")}
|
|
227
|
+
value={
|
|
228
|
+
mobile.touchSupported
|
|
229
|
+
? `${t("diagnostics_yes")} (${mobile.maxTouchPoints})`
|
|
230
|
+
: t("diagnostics_no")
|
|
231
|
+
}
|
|
232
|
+
/>
|
|
233
|
+
<DiagnosticRow
|
|
234
|
+
label={t("diagnostics_device_memory")}
|
|
235
|
+
value={
|
|
236
|
+
mobile.deviceMemoryGb !== null
|
|
237
|
+
? `${mobile.deviceMemoryGb} GB`
|
|
238
|
+
: t("diagnostics_not_available")
|
|
239
|
+
}
|
|
240
|
+
/>
|
|
241
|
+
<DiagnosticRow
|
|
242
|
+
label={t("diagnostics_network_type")}
|
|
243
|
+
value={mobile.networkEffectiveType ?? t("diagnostics_not_available")}
|
|
244
|
+
/>
|
|
245
|
+
<DiagnosticRow
|
|
246
|
+
label={t("diagnostics_screen_orientation")}
|
|
247
|
+
value={mobile.screenOrientation ?? t("diagnostics_unknown")}
|
|
248
|
+
/>
|
|
249
|
+
</DiagnosticsSection>
|
|
250
|
+
|
|
251
|
+
<DiagnosticsSection title={t("diagnostics_section_memory")}>
|
|
252
|
+
{memory ? (
|
|
253
|
+
<DiagnosticRow
|
|
254
|
+
label={t("diagnostics_js_heap")}
|
|
255
|
+
value={`${(memory.usedJSHeapSize / 1024 / 1024).toFixed(1)} / ${(
|
|
256
|
+
memory.totalJSHeapSize / 1024 / 1024
|
|
257
|
+
).toFixed(1)} MB`}
|
|
258
|
+
/>
|
|
259
|
+
) : (
|
|
260
|
+
<p className="text-sm text-muted-foreground">
|
|
261
|
+
{t("diagnostics_js_heap_unavailable")}
|
|
262
|
+
</p>
|
|
263
|
+
)}
|
|
264
|
+
</DiagnosticsSection>
|
|
265
|
+
|
|
266
|
+
<DiagnosticsSection title={t("diagnostics_section_capabilities")}>
|
|
267
|
+
<CapabilityRow
|
|
268
|
+
label={t("diagnostics_webgl2")}
|
|
269
|
+
supported={webgl.webgl2Supported}
|
|
270
|
+
/>
|
|
271
|
+
<CapabilityRow
|
|
272
|
+
label={t("diagnostics_hardware_acceleration")}
|
|
273
|
+
supported={webgl.hardwareAccelerated}
|
|
274
|
+
/>
|
|
275
|
+
<CapabilityRow
|
|
276
|
+
label={t("diagnostics_gpu_supported")}
|
|
277
|
+
supported={webgl.webgl1Supported || webgl.webgl2Supported}
|
|
278
|
+
/>
|
|
279
|
+
<CapabilityRow
|
|
280
|
+
label={t("diagnostics_max_texture_size_check")}
|
|
281
|
+
supported={(webgl.maxTextureSize ?? 0) >= 8192}
|
|
282
|
+
/>
|
|
283
|
+
<CapabilityRow
|
|
284
|
+
label={t("diagnostics_float_textures")}
|
|
285
|
+
supported={webgl.floatTexturesSupported}
|
|
286
|
+
/>
|
|
287
|
+
<CapabilityRow label={t("diagnostics_msaa")} supported={webgl.msaaSupported} />
|
|
288
|
+
<CapabilityRow
|
|
289
|
+
label={t("diagnostics_shaders_compile")}
|
|
290
|
+
supported={webgl.shadersCompile}
|
|
291
|
+
/>
|
|
292
|
+
<CapabilityRow label={t("diagnostics_audio")} supported={capabilities.audio} />
|
|
293
|
+
<CapabilityRow
|
|
294
|
+
label={t("diagnostics_gamepad")}
|
|
295
|
+
supported={capabilities.gamepad}
|
|
296
|
+
/>
|
|
297
|
+
<CapabilityRow
|
|
298
|
+
label={t("diagnostics_clipboard")}
|
|
299
|
+
supported={capabilities.clipboard}
|
|
300
|
+
/>
|
|
301
|
+
<CapabilityRow
|
|
302
|
+
label={t("diagnostics_fullscreen")}
|
|
303
|
+
supported={capabilities.fullscreen}
|
|
304
|
+
/>
|
|
305
|
+
<CapabilityRow
|
|
306
|
+
label={t("diagnostics_local_storage")}
|
|
307
|
+
supported={capabilities.localStorage}
|
|
308
|
+
/>
|
|
309
|
+
<CapabilityRow
|
|
310
|
+
label={t("diagnostics_indexed_db")}
|
|
311
|
+
supported={capabilities.indexedDb}
|
|
312
|
+
/>
|
|
313
|
+
<CapabilityRow
|
|
314
|
+
label={t("diagnostics_steam_overlay")}
|
|
315
|
+
supported={capabilities.steamOverlayDetected}
|
|
316
|
+
hint={t("diagnostics_steam_overlay_hint")}
|
|
317
|
+
/>
|
|
318
|
+
</DiagnosticsSection>
|
|
319
|
+
|
|
320
|
+
<DiagnosticsSection title={t("diagnostics_section_extensions")}>
|
|
321
|
+
{webgl.keyExtensions.map((extension) => (
|
|
322
|
+
<CapabilityRow
|
|
323
|
+
key={extension.name}
|
|
324
|
+
label={extension.name}
|
|
325
|
+
supported={extension.supported}
|
|
326
|
+
/>
|
|
327
|
+
))}
|
|
328
|
+
</DiagnosticsSection>
|
|
329
|
+
</div>
|
|
330
|
+
</ScrollArea>
|
|
331
|
+
);
|
|
332
|
+
}
|
|
@@ -6,7 +6,14 @@ import { useSetSearchParamState } from "@/lib/hooks/navigation-hooks";
|
|
|
6
6
|
import { useQuit } from "@/lib/hooks/quit-hooks";
|
|
7
7
|
import { Game } from "@drincs/pixi-vn";
|
|
8
8
|
import { useLocation, useNavigate } from "@tanstack/react-router";
|
|
9
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
Gamepad2Icon,
|
|
11
|
+
HistoryIcon,
|
|
12
|
+
LogOutIcon,
|
|
13
|
+
PowerIcon,
|
|
14
|
+
SaveIcon,
|
|
15
|
+
StethoscopeIcon,
|
|
16
|
+
} from "lucide-react";
|
|
10
17
|
import { useTranslation } from "react-i18next";
|
|
11
18
|
|
|
12
19
|
export function QuickMenus() {
|
|
@@ -19,6 +26,7 @@ export function QuickMenus() {
|
|
|
19
26
|
{isInGame && <OpenHistorySettingButton />}
|
|
20
27
|
<SaveLoadMenuButton />
|
|
21
28
|
<OpenControlsListSettingButton />
|
|
29
|
+
<OpenDiagnosticsSettingButton />
|
|
22
30
|
</div>
|
|
23
31
|
{isInGame && <Separator />}
|
|
24
32
|
{isInGame && <ReturnMainMenuButton />}
|
|
@@ -51,6 +59,29 @@ export function OpenControlsListSettingButton() {
|
|
|
51
59
|
);
|
|
52
60
|
}
|
|
53
61
|
|
|
62
|
+
export function OpenDiagnosticsSettingButton() {
|
|
63
|
+
const { t } = useTranslation(["ui"]);
|
|
64
|
+
const setSettingsOpen = useSetSearchParamState<boolean>("settings");
|
|
65
|
+
const setSettingsTab = useSetSearchParamState<string>("settings_tab");
|
|
66
|
+
|
|
67
|
+
return (
|
|
68
|
+
<Button
|
|
69
|
+
variant="outline"
|
|
70
|
+
className="w-full justify-start"
|
|
71
|
+
onClick={() => {
|
|
72
|
+
setSettingsOpen(true);
|
|
73
|
+
setSettingsTab("menus/diagnostics");
|
|
74
|
+
}}
|
|
75
|
+
>
|
|
76
|
+
<StethoscopeIcon />
|
|
77
|
+
{t("diagnostics")}
|
|
78
|
+
<KbdGroup className="ml-auto">
|
|
79
|
+
<Kbd>F8</Kbd>
|
|
80
|
+
</KbdGroup>
|
|
81
|
+
</Button>
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
|
|
54
85
|
export function ReturnMainMenuButton() {
|
|
55
86
|
const navigate = useNavigate();
|
|
56
87
|
const { t } = useTranslation(["ui"]);
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { systemInfo } from "@/lib/system-info";
|
|
2
|
+
import {
|
|
3
|
+
type DeviceDiagnosticsSnapshot,
|
|
4
|
+
type NativeDiagnostics,
|
|
5
|
+
detectBrowserEngine,
|
|
6
|
+
detectPlatform,
|
|
7
|
+
getBrowserCapabilities,
|
|
8
|
+
getCurrentFps,
|
|
9
|
+
getEngineVersion,
|
|
10
|
+
getGameVersion,
|
|
11
|
+
getJsMemoryInfo,
|
|
12
|
+
getMobileDiagnostics,
|
|
13
|
+
getMotionVersion,
|
|
14
|
+
getPixiJsVersion,
|
|
15
|
+
getPixiVnVersion,
|
|
16
|
+
getResolutionInfo,
|
|
17
|
+
getToneJsVersion,
|
|
18
|
+
getWebglDiagnostics,
|
|
19
|
+
} from "@/lib/utils/device-diagnostics-utility";
|
|
20
|
+
import { useEffect, useMemo, useState } from "react";
|
|
21
|
+
|
|
22
|
+
const LIVE_REFRESH_INTERVAL_MS = 1000;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* WebGL support, browser capabilities, UA, platform and engine don't change during a session,
|
|
26
|
+
* so they're probed once. FPS, resolution and JS memory are polled since they change live.
|
|
27
|
+
*/
|
|
28
|
+
export function useDeviceDiagnostics(): DeviceDiagnosticsSnapshot {
|
|
29
|
+
const webgl = useMemo(() => getWebglDiagnostics(), []);
|
|
30
|
+
const capabilities = useMemo(() => getBrowserCapabilities(), []);
|
|
31
|
+
const platform = useMemo(() => detectPlatform(), []);
|
|
32
|
+
const browserEngine = useMemo(() => detectBrowserEngine(), []);
|
|
33
|
+
const engineVersion = useMemo(() => getEngineVersion(), []);
|
|
34
|
+
const gameVersion = useMemo(() => getGameVersion(), []);
|
|
35
|
+
const pixiJsVersion = useMemo(() => getPixiJsVersion(), []);
|
|
36
|
+
const pixiVnVersion = useMemo(() => getPixiVnVersion(), []);
|
|
37
|
+
const toneJsVersion = useMemo(() => getToneJsVersion(), []);
|
|
38
|
+
const motionVersion = useMemo(() => getMotionVersion(), []);
|
|
39
|
+
|
|
40
|
+
const [resolution, setResolution] = useState(() => getResolutionInfo());
|
|
41
|
+
const [mobile, setMobile] = useState(() => getMobileDiagnostics());
|
|
42
|
+
const [memory, setMemory] = useState(() => getJsMemoryInfo());
|
|
43
|
+
const [fps, setFps] = useState<number | null>(() => getCurrentFps());
|
|
44
|
+
// Only ever populated inside the Tauri app — stays null in the browser build.
|
|
45
|
+
const [native, setNative] = useState<NativeDiagnostics | null>(null);
|
|
46
|
+
|
|
47
|
+
useEffect(() => {
|
|
48
|
+
let cancelled = false;
|
|
49
|
+
Promise.all([systemInfo.getSystemInfo(), systemInfo.getWebviewVersion()]).then(
|
|
50
|
+
([info, webviewVersion]) => {
|
|
51
|
+
if (cancelled || !info) return;
|
|
52
|
+
setNative({ ...info, webviewVersion });
|
|
53
|
+
},
|
|
54
|
+
);
|
|
55
|
+
return () => {
|
|
56
|
+
cancelled = true;
|
|
57
|
+
};
|
|
58
|
+
}, []);
|
|
59
|
+
|
|
60
|
+
useEffect(() => {
|
|
61
|
+
// A resize also fires on orientation changes, so refresh both together.
|
|
62
|
+
const onResize = () => {
|
|
63
|
+
setResolution(getResolutionInfo());
|
|
64
|
+
setMobile(getMobileDiagnostics());
|
|
65
|
+
};
|
|
66
|
+
window.addEventListener("resize", onResize);
|
|
67
|
+
return () => window.removeEventListener("resize", onResize);
|
|
68
|
+
}, []);
|
|
69
|
+
|
|
70
|
+
useEffect(() => {
|
|
71
|
+
const interval = setInterval(() => {
|
|
72
|
+
setFps(getCurrentFps());
|
|
73
|
+
setMemory(getJsMemoryInfo());
|
|
74
|
+
}, LIVE_REFRESH_INTERVAL_MS);
|
|
75
|
+
return () => clearInterval(interval);
|
|
76
|
+
}, []);
|
|
77
|
+
|
|
78
|
+
return {
|
|
79
|
+
webgl,
|
|
80
|
+
capabilities,
|
|
81
|
+
mobile,
|
|
82
|
+
native,
|
|
83
|
+
resolution,
|
|
84
|
+
memory,
|
|
85
|
+
fps,
|
|
86
|
+
userAgent: navigator.userAgent,
|
|
87
|
+
platform,
|
|
88
|
+
browserEngine,
|
|
89
|
+
engineVersion,
|
|
90
|
+
gameVersion,
|
|
91
|
+
pixiJsVersion,
|
|
92
|
+
pixiVnVersion,
|
|
93
|
+
toneJsVersion,
|
|
94
|
+
motionVersion,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
@@ -169,6 +169,11 @@ export function useSettingsHotkeys(): null {
|
|
|
169
169
|
setSettingsTab("menus/controls");
|
|
170
170
|
}, [setSettingsOpen, setSettingsTab]);
|
|
171
171
|
|
|
172
|
+
const openDiagnosticsPage = useCallback(() => {
|
|
173
|
+
setSettingsOpen(true);
|
|
174
|
+
setSettingsTab("menus/diagnostics");
|
|
175
|
+
}, [setSettingsOpen, setSettingsTab]);
|
|
176
|
+
|
|
172
177
|
useHotkeys([
|
|
173
178
|
{
|
|
174
179
|
hotkey: "Escape",
|
|
@@ -191,6 +196,17 @@ export function useSettingsHotkeys(): null {
|
|
|
191
196
|
},
|
|
192
197
|
},
|
|
193
198
|
},
|
|
199
|
+
{
|
|
200
|
+
hotkey: "F8",
|
|
201
|
+
callback: openDiagnosticsPage,
|
|
202
|
+
options: {
|
|
203
|
+
enabled: !isAnyMenuOrDialogOpen,
|
|
204
|
+
meta: {
|
|
205
|
+
name: t("diagnostics"),
|
|
206
|
+
description: t("diagnostics_hotkey_description"),
|
|
207
|
+
},
|
|
208
|
+
},
|
|
209
|
+
},
|
|
194
210
|
]);
|
|
195
211
|
|
|
196
212
|
return null;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Native system info — thin wrappers around Tauri commands exposed by
|
|
3
|
+
* `src-tauri/src/system_info.rs`. Safe to call even when running outside
|
|
4
|
+
* Tauri (web mode): functions resolve to `null` instead of throwing.
|
|
5
|
+
*
|
|
6
|
+
* These expose OS/distro and WebView engine details that browsers
|
|
7
|
+
* deliberately don't surface to JS (to limit fingerprinting).
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { invoke } from "@tauri-apps/api/core";
|
|
11
|
+
|
|
12
|
+
const isTauri = typeof window !== "undefined" && "__TAURI__" in window;
|
|
13
|
+
|
|
14
|
+
export interface NativeSystemInfo {
|
|
15
|
+
/** Specific OS/distro name (e.g. "Ubuntu", "Windows", "Mac OS"), not just the generic platform. */
|
|
16
|
+
osType: string;
|
|
17
|
+
osVersion: string;
|
|
18
|
+
bitness: string;
|
|
19
|
+
architecture: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
interface RawSystemInfo {
|
|
23
|
+
os_type: string;
|
|
24
|
+
os_version: string;
|
|
25
|
+
bitness: string;
|
|
26
|
+
architecture: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async function call<T>(cmd: string): Promise<T | null> {
|
|
30
|
+
if (!isTauri) return null;
|
|
31
|
+
try {
|
|
32
|
+
return await invoke<T>(cmd);
|
|
33
|
+
} catch {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export namespace systemInfo {
|
|
39
|
+
export async function getSystemInfo(): Promise<NativeSystemInfo | null> {
|
|
40
|
+
const raw = await call<RawSystemInfo>("get_system_info");
|
|
41
|
+
if (!raw) return null;
|
|
42
|
+
return {
|
|
43
|
+
osType: raw.os_type,
|
|
44
|
+
osVersion: raw.os_version,
|
|
45
|
+
bitness: raw.bitness,
|
|
46
|
+
architecture: raw.architecture,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** Underlying WebView engine version (WebView2, WebKitGTK, WKWebView, Android System WebView). */
|
|
51
|
+
export async function getWebviewVersion(): Promise<string | null> {
|
|
52
|
+
return call<string>("get_webview_version");
|
|
53
|
+
}
|
|
54
|
+
}
|