create-pixi-vn 2.1.3 → 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/.assetpack.ts +82 -2
- 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/.assetpack.ts +82 -2
- 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/.assetpack.ts +82 -2
- 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/.assetpack.ts +82 -2
- 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/.assetpack.ts +82 -2
- 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/.assetpack.ts +82 -2
- 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/.assetpack.ts +82 -2
- 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/.assetpack.ts +82 -2
- 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
package/package.json
CHANGED
|
@@ -1,5 +1,83 @@
|
|
|
1
|
-
import type { AssetPackConfig } from "@assetpack/core";
|
|
1
|
+
import type { AssetPackConfig, AssetPipe } from "@assetpack/core";
|
|
2
|
+
import { path } from "@assetpack/core";
|
|
2
3
|
import { pixiPipes } from "@assetpack/core/pixi";
|
|
4
|
+
import fs from "node:fs/promises";
|
|
5
|
+
|
|
6
|
+
const manifestOutput = "src/assets/manifest.gen.json";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* AssetPack only creates a manifest bundle for folders explicitly tagged
|
|
10
|
+
* with "{m}" (see pixiManifest's `manifest` tag); everything else lands in
|
|
11
|
+
* the single "default" bundle. This splits the "default" bundle into one
|
|
12
|
+
* bundle per top-level folder under the assets entry, named after that
|
|
13
|
+
* folder, so bundles mirror the folder structure without needing to tag
|
|
14
|
+
* every folder manually. Files directly at the entry root (no folder) stay
|
|
15
|
+
* in "default". Runs after the manifest has been written to disk, so it
|
|
16
|
+
* re-reads and rewrites the file.
|
|
17
|
+
*/
|
|
18
|
+
function groupBundlesByFolder(options: { output: string }): AssetPipe<{ output: string }> {
|
|
19
|
+
return {
|
|
20
|
+
name: "group-bundles-by-folder",
|
|
21
|
+
defaultOptions: options,
|
|
22
|
+
async finish(_asset, opts) {
|
|
23
|
+
const manifest = JSON.parse(await fs.readFile(opts.output, "utf-8"));
|
|
24
|
+
|
|
25
|
+
const bundles = new Map<string, { name: string; assets: unknown[] }>();
|
|
26
|
+
for (const bundle of manifest.bundles) {
|
|
27
|
+
if (bundle.name !== "default") {
|
|
28
|
+
bundles.set(bundle.name, bundle);
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
for (const asset of bundle.assets) {
|
|
32
|
+
const folder = path.dirname(asset.src[0]).split("/")[0];
|
|
33
|
+
const bundleName = folder === "." ? "default" : folder;
|
|
34
|
+
const target = bundles.get(bundleName) ?? { name: bundleName, assets: [] };
|
|
35
|
+
target.assets.push(asset);
|
|
36
|
+
bundles.set(bundleName, target);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
manifest.bundles = [...bundles.values()];
|
|
40
|
+
|
|
41
|
+
await fs.writeFile(opts.output, JSON.stringify(manifest, null, 2));
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Removes file extensions from the aliases generated by the pixi manifest
|
|
48
|
+
* pipe and replaces path separators with underscores (e.g.
|
|
49
|
+
* "yard/yard1.png" -> "yard_yard1"). Runs after the manifest has been
|
|
50
|
+
* written to disk, so it re-reads and rewrites the file.
|
|
51
|
+
*
|
|
52
|
+
* If two assets end up with the same normalized alias, the later one is
|
|
53
|
+
* prefixed with its bundle name (e.g. "yard_yard_yard1") to keep aliases
|
|
54
|
+
* unique across the whole manifest.
|
|
55
|
+
*/
|
|
56
|
+
function normalizeAliases(options: { output: string }): AssetPipe<{ output: string }> {
|
|
57
|
+
return {
|
|
58
|
+
name: "normalize-aliases",
|
|
59
|
+
defaultOptions: options,
|
|
60
|
+
async finish(_asset, opts) {
|
|
61
|
+
const manifest = JSON.parse(await fs.readFile(opts.output, "utf-8"));
|
|
62
|
+
|
|
63
|
+
const seen = new Set<string>();
|
|
64
|
+
for (const bundle of manifest.bundles) {
|
|
65
|
+
for (const asset of bundle.assets) {
|
|
66
|
+
asset.alias = asset.alias.map((alias: string) => {
|
|
67
|
+
const normalized = path.trimExt(alias).replaceAll("/", "_");
|
|
68
|
+
const unique = seen.has(normalized)
|
|
69
|
+
? `${bundle.name}_${normalized}`
|
|
70
|
+
: normalized;
|
|
71
|
+
seen.add(normalized);
|
|
72
|
+
return unique;
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
await fs.writeFile(opts.output, JSON.stringify(manifest, null, 2));
|
|
78
|
+
},
|
|
79
|
+
};
|
|
80
|
+
}
|
|
3
81
|
|
|
4
82
|
const config: AssetPackConfig = {
|
|
5
83
|
entry: "./src/assets",
|
|
@@ -8,10 +86,12 @@ const config: AssetPackConfig = {
|
|
|
8
86
|
pipes: [
|
|
9
87
|
...pixiPipes({
|
|
10
88
|
manifest: {
|
|
11
|
-
output:
|
|
89
|
+
output: manifestOutput,
|
|
12
90
|
createShortcuts: true,
|
|
13
91
|
},
|
|
14
92
|
}),
|
|
93
|
+
groupBundlesByFolder({ output: manifestOutput }),
|
|
94
|
+
normalizeAliases({ output: manifestOutput }),
|
|
15
95
|
],
|
|
16
96
|
};
|
|
17
97
|
|
|
@@ -19,8 +19,8 @@
|
|
|
19
19
|
},
|
|
20
20
|
"dependencies": {
|
|
21
21
|
"@base-ui/react": "^1.6.0",
|
|
22
|
-
"@drincs/nqtr": "^1.0.
|
|
23
|
-
"@drincs/pixi-vn": "^1.8.
|
|
22
|
+
"@drincs/nqtr": "^1.0.3",
|
|
23
|
+
"@drincs/pixi-vn": "^1.8.25",
|
|
24
24
|
"@tailwindcss/vite": "^4.3.0",
|
|
25
25
|
"@tanstack/hotkeys": "^0.8.0",
|
|
26
26
|
"@tanstack/react-hotkeys": "^0.10.0",
|
|
@@ -14,16 +14,7 @@ export const manifest: AssetsManifest = {
|
|
|
14
14
|
...generatedManifestJson.bundles,
|
|
15
15
|
{
|
|
16
16
|
name: AUDIO_BUNDLE_NAME,
|
|
17
|
-
assets: [
|
|
18
|
-
{
|
|
19
|
-
alias: "bgm_cheerful",
|
|
20
|
-
src: "https://raw.githubusercontent.com/DRincs-Productions/pixi-vn-bucket/refs/heads/main/audio/bgm_cheerful.wav",
|
|
21
|
-
},
|
|
22
|
-
{
|
|
23
|
-
alias: "sfx_whoosh",
|
|
24
|
-
src: "https://raw.githubusercontent.com/DRincs-Productions/pixi-vn-bucket/refs/heads/main/audio/sfx_whoosh.wav",
|
|
25
|
-
},
|
|
26
|
-
],
|
|
17
|
+
assets: [],
|
|
27
18
|
},
|
|
28
19
|
// screens
|
|
29
20
|
{
|
|
@@ -31,7 +22,7 @@ export const manifest: AssetsManifest = {
|
|
|
31
22
|
assets: [
|
|
32
23
|
{
|
|
33
24
|
alias: "background_main_menu",
|
|
34
|
-
src: "https://
|
|
25
|
+
src: "https://pub-72ff059a2c6642fb9eab15df80fb3b45.r2.dev/main-menu.png",
|
|
35
26
|
},
|
|
36
27
|
],
|
|
37
28
|
},
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { About } from "@/components/menus/settings/about";
|
|
2
2
|
import { DialoguesControls } from "@/components/menus/settings/dialogues-controls";
|
|
3
3
|
import { ControlsListSettingsPage } from "@/components/menus/settings/menus/controls";
|
|
4
|
+
import { DiagnosticsSettingsPage } from "@/components/menus/settings/menus/diagnostics";
|
|
4
5
|
import { HistoryListSettingsPage } from "@/components/menus/settings/menus/history";
|
|
5
6
|
import { SaveLoadSettingsPage } from "@/components/menus/settings/menus/save-load";
|
|
6
7
|
import { QuickMenus } from "@/components/menus/settings/quick-menus";
|
|
@@ -23,7 +24,7 @@ import { ArrowLeftIcon } from "lucide-react";
|
|
|
23
24
|
import { Fragment } from "react";
|
|
24
25
|
import { useTranslation } from "react-i18next";
|
|
25
26
|
|
|
26
|
-
type SettingsTabPath = "menus/controls" | "menus/history" | "menus/save-load";
|
|
27
|
+
type SettingsTabPath = "menus/controls" | "menus/history" | "menus/save-load" | "menus/diagnostics";
|
|
27
28
|
|
|
28
29
|
type BreadcrumbEntry = {
|
|
29
30
|
id: string;
|
|
@@ -38,7 +39,8 @@ export function Settings() {
|
|
|
38
39
|
const normalizedTab: SettingsTabPath | undefined =
|
|
39
40
|
currentTab === "menus/controls" ||
|
|
40
41
|
currentTab === "menus/history" ||
|
|
41
|
-
currentTab === "menus/save-load"
|
|
42
|
+
currentTab === "menus/save-load" ||
|
|
43
|
+
currentTab === "menus/diagnostics"
|
|
42
44
|
? (currentTab as SettingsTabPath)
|
|
43
45
|
: undefined;
|
|
44
46
|
|
|
@@ -55,7 +57,8 @@ export function Settings() {
|
|
|
55
57
|
if (
|
|
56
58
|
normalizedTab === "menus/controls" ||
|
|
57
59
|
normalizedTab === "menus/history" ||
|
|
58
|
-
normalizedTab === "menus/save-load"
|
|
60
|
+
normalizedTab === "menus/save-load" ||
|
|
61
|
+
normalizedTab === "menus/diagnostics"
|
|
59
62
|
) {
|
|
60
63
|
trail.push({ id: "menus", label: t("menus") });
|
|
61
64
|
trail.push({
|
|
@@ -64,13 +67,17 @@ export function Settings() {
|
|
|
64
67
|
? "menus-controls"
|
|
65
68
|
: normalizedTab === "menus/history"
|
|
66
69
|
? "menus-history"
|
|
67
|
-
: "menus
|
|
70
|
+
: normalizedTab === "menus/save-load"
|
|
71
|
+
? "menus-save-load"
|
|
72
|
+
: "menus-diagnostics",
|
|
68
73
|
label:
|
|
69
74
|
normalizedTab === "menus/controls"
|
|
70
75
|
? t("hotkeys_menu")
|
|
71
76
|
: normalizedTab === "menus/history"
|
|
72
77
|
? t("history")
|
|
73
|
-
:
|
|
78
|
+
: normalizedTab === "menus/save-load"
|
|
79
|
+
? `${t("save")}/${t("load")}`
|
|
80
|
+
: t("diagnostics"),
|
|
74
81
|
});
|
|
75
82
|
}
|
|
76
83
|
return trail;
|
|
@@ -112,7 +119,8 @@ export function Settings() {
|
|
|
112
119
|
if (
|
|
113
120
|
normalizedTab === "menus/controls" ||
|
|
114
121
|
normalizedTab === "menus/history" ||
|
|
115
|
-
normalizedTab === "menus/save-load"
|
|
122
|
+
normalizedTab === "menus/save-load" ||
|
|
123
|
+
normalizedTab === "menus/diagnostics"
|
|
116
124
|
) {
|
|
117
125
|
return (
|
|
118
126
|
<>
|
|
@@ -130,6 +138,7 @@ export function Settings() {
|
|
|
130
138
|
{normalizedTab === "menus/controls" ? <ControlsListSettingsPage /> : null}
|
|
131
139
|
{normalizedTab === "menus/history" ? <HistoryListSettingsPage /> : null}
|
|
132
140
|
{normalizedTab === "menus/save-load" ? <SaveLoadSettingsPage /> : null}
|
|
141
|
+
{normalizedTab === "menus/diagnostics" ? <DiagnosticsSettingsPage /> : null}
|
|
133
142
|
</>
|
|
134
143
|
);
|
|
135
144
|
}
|
|
@@ -0,0 +1,266 @@
|
|
|
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, 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_user_agent")}
|
|
139
|
+
value={diagnostics.userAgent}
|
|
140
|
+
/>
|
|
141
|
+
</DiagnosticsSection>
|
|
142
|
+
|
|
143
|
+
<DiagnosticsSection title={t("diagnostics_section_graphics")}>
|
|
144
|
+
<DiagnosticRow
|
|
145
|
+
label={t("diagnostics_webgl1")}
|
|
146
|
+
value={yesNo(webgl.webgl1Supported)}
|
|
147
|
+
/>
|
|
148
|
+
<DiagnosticRow
|
|
149
|
+
label={t("diagnostics_webgl2")}
|
|
150
|
+
value={yesNo(webgl.webgl2Supported)}
|
|
151
|
+
/>
|
|
152
|
+
<DiagnosticRow
|
|
153
|
+
label={t("diagnostics_hardware_acceleration")}
|
|
154
|
+
value={yesNo(webgl.hardwareAccelerated)}
|
|
155
|
+
/>
|
|
156
|
+
<DiagnosticRow
|
|
157
|
+
label={t("diagnostics_gpu_vendor")}
|
|
158
|
+
value={webgl.unmaskedVendor ?? webgl.vendor ?? t("diagnostics_unknown")}
|
|
159
|
+
/>
|
|
160
|
+
<DiagnosticRow
|
|
161
|
+
label={t("diagnostics_gpu_renderer")}
|
|
162
|
+
value={webgl.unmaskedRenderer ?? webgl.renderer ?? t("diagnostics_unknown")}
|
|
163
|
+
/>
|
|
164
|
+
<DiagnosticRow
|
|
165
|
+
label={t("diagnostics_max_texture_size")}
|
|
166
|
+
value={webgl.maxTextureSize ?? t("diagnostics_unknown")}
|
|
167
|
+
/>
|
|
168
|
+
</DiagnosticsSection>
|
|
169
|
+
|
|
170
|
+
<DiagnosticsSection title={t("diagnostics_section_display")}>
|
|
171
|
+
<DiagnosticRow
|
|
172
|
+
label={t("diagnostics_resolution")}
|
|
173
|
+
value={`${resolution.innerWidth}x${resolution.innerHeight}`}
|
|
174
|
+
/>
|
|
175
|
+
<DiagnosticRow
|
|
176
|
+
label={t("diagnostics_device_pixel_ratio")}
|
|
177
|
+
value={resolution.devicePixelRatio}
|
|
178
|
+
/>
|
|
179
|
+
<DiagnosticRow
|
|
180
|
+
label={t("diagnostics_fps")}
|
|
181
|
+
value={fps ?? t("diagnostics_unknown")}
|
|
182
|
+
/>
|
|
183
|
+
</DiagnosticsSection>
|
|
184
|
+
|
|
185
|
+
<DiagnosticsSection title={t("diagnostics_section_memory")}>
|
|
186
|
+
{memory ? (
|
|
187
|
+
<DiagnosticRow
|
|
188
|
+
label={t("diagnostics_js_heap")}
|
|
189
|
+
value={`${(memory.usedJSHeapSize / 1024 / 1024).toFixed(1)} / ${(
|
|
190
|
+
memory.totalJSHeapSize / 1024 / 1024
|
|
191
|
+
).toFixed(1)} MB`}
|
|
192
|
+
/>
|
|
193
|
+
) : (
|
|
194
|
+
<p className="text-sm text-muted-foreground">
|
|
195
|
+
{t("diagnostics_js_heap_unavailable")}
|
|
196
|
+
</p>
|
|
197
|
+
)}
|
|
198
|
+
</DiagnosticsSection>
|
|
199
|
+
|
|
200
|
+
<DiagnosticsSection title={t("diagnostics_section_capabilities")}>
|
|
201
|
+
<CapabilityRow
|
|
202
|
+
label={t("diagnostics_webgl2")}
|
|
203
|
+
supported={webgl.webgl2Supported}
|
|
204
|
+
/>
|
|
205
|
+
<CapabilityRow
|
|
206
|
+
label={t("diagnostics_hardware_acceleration")}
|
|
207
|
+
supported={webgl.hardwareAccelerated}
|
|
208
|
+
/>
|
|
209
|
+
<CapabilityRow
|
|
210
|
+
label={t("diagnostics_gpu_supported")}
|
|
211
|
+
supported={webgl.webgl1Supported || webgl.webgl2Supported}
|
|
212
|
+
/>
|
|
213
|
+
<CapabilityRow
|
|
214
|
+
label={t("diagnostics_max_texture_size_check")}
|
|
215
|
+
supported={(webgl.maxTextureSize ?? 0) >= 8192}
|
|
216
|
+
/>
|
|
217
|
+
<CapabilityRow
|
|
218
|
+
label={t("diagnostics_float_textures")}
|
|
219
|
+
supported={webgl.floatTexturesSupported}
|
|
220
|
+
/>
|
|
221
|
+
<CapabilityRow label={t("diagnostics_msaa")} supported={webgl.msaaSupported} />
|
|
222
|
+
<CapabilityRow
|
|
223
|
+
label={t("diagnostics_shaders_compile")}
|
|
224
|
+
supported={webgl.shadersCompile}
|
|
225
|
+
/>
|
|
226
|
+
<CapabilityRow label={t("diagnostics_audio")} supported={capabilities.audio} />
|
|
227
|
+
<CapabilityRow
|
|
228
|
+
label={t("diagnostics_gamepad")}
|
|
229
|
+
supported={capabilities.gamepad}
|
|
230
|
+
/>
|
|
231
|
+
<CapabilityRow
|
|
232
|
+
label={t("diagnostics_clipboard")}
|
|
233
|
+
supported={capabilities.clipboard}
|
|
234
|
+
/>
|
|
235
|
+
<CapabilityRow
|
|
236
|
+
label={t("diagnostics_fullscreen")}
|
|
237
|
+
supported={capabilities.fullscreen}
|
|
238
|
+
/>
|
|
239
|
+
<CapabilityRow
|
|
240
|
+
label={t("diagnostics_local_storage")}
|
|
241
|
+
supported={capabilities.localStorage}
|
|
242
|
+
/>
|
|
243
|
+
<CapabilityRow
|
|
244
|
+
label={t("diagnostics_indexed_db")}
|
|
245
|
+
supported={capabilities.indexedDb}
|
|
246
|
+
/>
|
|
247
|
+
<CapabilityRow
|
|
248
|
+
label={t("diagnostics_steam_overlay")}
|
|
249
|
+
supported={capabilities.steamOverlayDetected}
|
|
250
|
+
hint={t("diagnostics_steam_overlay_hint")}
|
|
251
|
+
/>
|
|
252
|
+
</DiagnosticsSection>
|
|
253
|
+
|
|
254
|
+
<DiagnosticsSection title={t("diagnostics_section_extensions")}>
|
|
255
|
+
{webgl.keyExtensions.map((extension) => (
|
|
256
|
+
<CapabilityRow
|
|
257
|
+
key={extension.name}
|
|
258
|
+
label={extension.name}
|
|
259
|
+
supported={extension.supported}
|
|
260
|
+
/>
|
|
261
|
+
))}
|
|
262
|
+
</DiagnosticsSection>
|
|
263
|
+
</div>
|
|
264
|
+
</ScrollArea>
|
|
265
|
+
);
|
|
266
|
+
}
|
|
@@ -5,7 +5,7 @@ import { Separator } from "@/components/ui/separator";
|
|
|
5
5
|
import { useSetSearchParamState } from "@/lib/hooks/navigation-hooks";
|
|
6
6
|
import { Game } from "@drincs/pixi-vn";
|
|
7
7
|
import { useLocation, useNavigate } from "@tanstack/react-router";
|
|
8
|
-
import { Gamepad2Icon, HistoryIcon, LogOutIcon, SaveIcon } from "lucide-react";
|
|
8
|
+
import { Gamepad2Icon, HistoryIcon, LogOutIcon, SaveIcon, StethoscopeIcon } from "lucide-react";
|
|
9
9
|
import { useTranslation } from "react-i18next";
|
|
10
10
|
|
|
11
11
|
export function QuickMenus() {
|
|
@@ -18,6 +18,7 @@ export function QuickMenus() {
|
|
|
18
18
|
{isInGame && <OpenHistorySettingButton />}
|
|
19
19
|
<SaveLoadMenuButton />
|
|
20
20
|
<OpenControlsListSettingButton />
|
|
21
|
+
<OpenDiagnosticsSettingButton />
|
|
21
22
|
</div>
|
|
22
23
|
{isInGame && <Separator />}
|
|
23
24
|
{isInGame && <ReturnMainMenuButton />}
|
|
@@ -49,6 +50,29 @@ export function OpenControlsListSettingButton() {
|
|
|
49
50
|
);
|
|
50
51
|
}
|
|
51
52
|
|
|
53
|
+
export function OpenDiagnosticsSettingButton() {
|
|
54
|
+
const { t } = useTranslation(["ui"]);
|
|
55
|
+
const setSettingsOpen = useSetSearchParamState<boolean>("settings");
|
|
56
|
+
const setSettingsTab = useSetSearchParamState<string>("settings_tab");
|
|
57
|
+
|
|
58
|
+
return (
|
|
59
|
+
<Button
|
|
60
|
+
variant="outline"
|
|
61
|
+
className="w-full justify-start"
|
|
62
|
+
onClick={() => {
|
|
63
|
+
setSettingsOpen(true);
|
|
64
|
+
setSettingsTab("menus/diagnostics");
|
|
65
|
+
}}
|
|
66
|
+
>
|
|
67
|
+
<StethoscopeIcon />
|
|
68
|
+
{t("diagnostics")}
|
|
69
|
+
<KbdGroup className="ml-auto">
|
|
70
|
+
<Kbd>F8</Kbd>
|
|
71
|
+
</KbdGroup>
|
|
72
|
+
</Button>
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
|
|
52
76
|
export function ReturnMainMenuButton() {
|
|
53
77
|
const navigate = useNavigate();
|
|
54
78
|
const { t } = useTranslation(["ui"]);
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type DeviceDiagnosticsSnapshot,
|
|
3
|
+
detectBrowserEngine,
|
|
4
|
+
detectPlatform,
|
|
5
|
+
getBrowserCapabilities,
|
|
6
|
+
getCurrentFps,
|
|
7
|
+
getGameVersion,
|
|
8
|
+
getJsMemoryInfo,
|
|
9
|
+
getMotionVersion,
|
|
10
|
+
getPixiJsVersion,
|
|
11
|
+
getPixiVnVersion,
|
|
12
|
+
getResolutionInfo,
|
|
13
|
+
getToneJsVersion,
|
|
14
|
+
getWebglDiagnostics,
|
|
15
|
+
} from "@/lib/utils/device-diagnostics-utility";
|
|
16
|
+
import { useEffect, useMemo, useState } from "react";
|
|
17
|
+
|
|
18
|
+
const LIVE_REFRESH_INTERVAL_MS = 1000;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* WebGL support, browser capabilities, UA, platform and engine don't change during a session,
|
|
22
|
+
* so they're probed once. FPS, resolution and JS memory are polled since they change live.
|
|
23
|
+
*/
|
|
24
|
+
export function useDeviceDiagnostics(): DeviceDiagnosticsSnapshot {
|
|
25
|
+
const webgl = useMemo(() => getWebglDiagnostics(), []);
|
|
26
|
+
const capabilities = useMemo(() => getBrowserCapabilities(), []);
|
|
27
|
+
const platform = useMemo(() => detectPlatform(), []);
|
|
28
|
+
const browserEngine = useMemo(() => detectBrowserEngine(), []);
|
|
29
|
+
const gameVersion = useMemo(() => getGameVersion(), []);
|
|
30
|
+
const pixiJsVersion = useMemo(() => getPixiJsVersion(), []);
|
|
31
|
+
const pixiVnVersion = useMemo(() => getPixiVnVersion(), []);
|
|
32
|
+
const toneJsVersion = useMemo(() => getToneJsVersion(), []);
|
|
33
|
+
const motionVersion = useMemo(() => getMotionVersion(), []);
|
|
34
|
+
|
|
35
|
+
const [resolution, setResolution] = useState(() => getResolutionInfo());
|
|
36
|
+
const [memory, setMemory] = useState(() => getJsMemoryInfo());
|
|
37
|
+
const [fps, setFps] = useState<number | null>(() => getCurrentFps());
|
|
38
|
+
|
|
39
|
+
useEffect(() => {
|
|
40
|
+
const onResize = () => setResolution(getResolutionInfo());
|
|
41
|
+
window.addEventListener("resize", onResize);
|
|
42
|
+
return () => window.removeEventListener("resize", onResize);
|
|
43
|
+
}, []);
|
|
44
|
+
|
|
45
|
+
useEffect(() => {
|
|
46
|
+
const interval = setInterval(() => {
|
|
47
|
+
setFps(getCurrentFps());
|
|
48
|
+
setMemory(getJsMemoryInfo());
|
|
49
|
+
}, LIVE_REFRESH_INTERVAL_MS);
|
|
50
|
+
return () => clearInterval(interval);
|
|
51
|
+
}, []);
|
|
52
|
+
|
|
53
|
+
return {
|
|
54
|
+
webgl,
|
|
55
|
+
capabilities,
|
|
56
|
+
resolution,
|
|
57
|
+
memory,
|
|
58
|
+
fps,
|
|
59
|
+
userAgent: navigator.userAgent,
|
|
60
|
+
platform,
|
|
61
|
+
browserEngine,
|
|
62
|
+
gameVersion,
|
|
63
|
+
pixiJsVersion,
|
|
64
|
+
pixiVnVersion,
|
|
65
|
+
toneJsVersion,
|
|
66
|
+
motionVersion,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
@@ -175,6 +175,11 @@ export function useSettingsHotkeys(): null {
|
|
|
175
175
|
setSettingsTab("menus/controls");
|
|
176
176
|
}, [setSettingsOpen, setSettingsTab]);
|
|
177
177
|
|
|
178
|
+
const openDiagnosticsPage = useCallback(() => {
|
|
179
|
+
setSettingsOpen(true);
|
|
180
|
+
setSettingsTab("menus/diagnostics");
|
|
181
|
+
}, [setSettingsOpen, setSettingsTab]);
|
|
182
|
+
|
|
178
183
|
useHotkeys([
|
|
179
184
|
{
|
|
180
185
|
hotkey: "Escape",
|
|
@@ -197,6 +202,17 @@ export function useSettingsHotkeys(): null {
|
|
|
197
202
|
},
|
|
198
203
|
},
|
|
199
204
|
},
|
|
205
|
+
{
|
|
206
|
+
hotkey: "F8",
|
|
207
|
+
callback: openDiagnosticsPage,
|
|
208
|
+
options: {
|
|
209
|
+
enabled: !isAnyMenuOrDialogOpen,
|
|
210
|
+
meta: {
|
|
211
|
+
name: t("diagnostics"),
|
|
212
|
+
description: t("diagnostics_hotkey_description"),
|
|
213
|
+
},
|
|
214
|
+
},
|
|
215
|
+
},
|
|
200
216
|
]);
|
|
201
217
|
|
|
202
218
|
return null;
|