create-pixi-vn 2.0.12 → 2.0.13

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.
Files changed (114) hide show
  1. package/package.json +1 -1
  2. package/template-react-vite-muijoy-ink/.vscode/extensions.json +2 -1
  3. package/template-react-vite-muijoy-ink/.vscode/settings.json +6 -1
  4. package/template-react-vite-muijoy-ink/_gitignore +1 -0
  5. package/template-react-vite-muijoy-ink/ink/second_part.ink +571 -0
  6. package/template-react-vite-muijoy-ink/ink/start.ink +214 -0
  7. package/template-react-vite-muijoy-ink/package-lock.json +230 -4
  8. package/template-react-vite-muijoy-ink/package.json +3 -1
  9. package/template-react-vite-muijoy-ink/src/App.tsx +25 -25
  10. package/template-react-vite-muijoy-ink/src/assets/index.ts +2 -4
  11. package/template-react-vite-muijoy-ink/src/assets/ink-manifest.gen.json +4 -1
  12. package/template-react-vite-muijoy-ink/src/content/ink/hashtag-commands.ts +38 -0
  13. package/template-react-vite-muijoy-ink/src/content/ink/text-replaces.ts +19 -0
  14. package/template-react-vite-muijoy-ink/src/lib/hooks/ink-hooks.tsx +12 -0
  15. package/template-react-vite-muijoy-ink/src/lib/i18n.ts +22 -1
  16. package/template-react-vite-muijoy-ink/src/pixi-vn-keys.gen.d.ts +2 -0
  17. package/template-react-vite-muijoy-ink/src/routes/__root.tsx +4 -0
  18. package/template-react-vite-muijoy-ink/vite.config.ts +6 -0
  19. package/template-react-vite-muijoy-ink-tauri/.assetpack.ts +9 -0
  20. package/template-react-vite-muijoy-ink-tauri/.vscode/extensions.json +5 -1
  21. package/template-react-vite-muijoy-ink-tauri/.vscode/launch.json +11 -0
  22. package/template-react-vite-muijoy-ink-tauri/.vscode/settings.json +6 -1
  23. package/template-react-vite-muijoy-ink-tauri/.vscode/tasks.json +47 -0
  24. package/template-react-vite-muijoy-ink-tauri/README.md +3 -3
  25. package/template-react-vite-muijoy-ink-tauri/_github/workflows/desktop.yml +188 -0
  26. package/template-react-vite-muijoy-ink-tauri/_github/workflows/mobile.yml +270 -0
  27. package/template-react-vite-muijoy-ink-tauri/_gitignore +11 -0
  28. package/template-react-vite-muijoy-ink-tauri/ink/second_part.ink +571 -0
  29. package/template-react-vite-muijoy-ink-tauri/ink/start.ink +214 -0
  30. package/template-react-vite-muijoy-ink-tauri/package-lock.json +480 -4
  31. package/template-react-vite-muijoy-ink-tauri/package.json +13 -2
  32. package/template-react-vite-muijoy-ink-tauri/src/App.tsx +25 -25
  33. package/template-react-vite-muijoy-ink-tauri/src/assets/index.ts +2 -4
  34. package/template-react-vite-muijoy-ink-tauri/src/assets/ink-manifest.gen.json +4 -1
  35. package/template-react-vite-muijoy-ink-tauri/src/components/menus/main-menu.tsx +16 -1
  36. package/template-react-vite-muijoy-ink-tauri/src/components/menus/settings/quick-menus.tsx +17 -1
  37. package/template-react-vite-muijoy-ink-tauri/src/components/menus/settings/system-controls.tsx +11 -1
  38. package/template-react-vite-muijoy-ink-tauri/src/content/ink/hashtag-commands.ts +55 -0
  39. package/template-react-vite-muijoy-ink-tauri/src/content/ink/text-replaces.ts +19 -0
  40. package/template-react-vite-muijoy-ink-tauri/src/lib/hooks/ink-hooks.tsx +12 -0
  41. package/template-react-vite-muijoy-ink-tauri/src/lib/hooks/quit-hooks.ts +24 -0
  42. package/template-react-vite-muijoy-ink-tauri/src/lib/i18n.ts +22 -1
  43. package/template-react-vite-muijoy-ink-tauri/src/lib/query/settings-query.ts +7 -1
  44. package/template-react-vite-muijoy-ink-tauri/src/lib/steam.ts +143 -0
  45. package/template-react-vite-muijoy-ink-tauri/src/pixi-vn-keys.gen.d.ts +2 -0
  46. package/template-react-vite-muijoy-ink-tauri/src/routes/__root.tsx +4 -0
  47. package/template-react-vite-muijoy-ink-tauri/src-tauri/Cargo.toml +50 -0
  48. package/template-react-vite-muijoy-ink-tauri/src-tauri/build.rs +46 -0
  49. package/template-react-vite-muijoy-ink-tauri/src-tauri/capabilities/default.json +21 -0
  50. package/template-react-vite-muijoy-ink-tauri/src-tauri/icons/128x128.png +0 -0
  51. package/template-react-vite-muijoy-ink-tauri/src-tauri/icons/128x128@2x.png +0 -0
  52. package/template-react-vite-muijoy-ink-tauri/src-tauri/icons/32x32.png +0 -0
  53. package/template-react-vite-muijoy-ink-tauri/src-tauri/icons/Square107x107Logo.png +0 -0
  54. package/template-react-vite-muijoy-ink-tauri/src-tauri/icons/Square142x142Logo.png +0 -0
  55. package/template-react-vite-muijoy-ink-tauri/src-tauri/icons/Square150x150Logo.png +0 -0
  56. package/template-react-vite-muijoy-ink-tauri/src-tauri/icons/Square284x284Logo.png +0 -0
  57. package/template-react-vite-muijoy-ink-tauri/src-tauri/icons/Square30x30Logo.png +0 -0
  58. package/template-react-vite-muijoy-ink-tauri/src-tauri/icons/Square310x310Logo.png +0 -0
  59. package/template-react-vite-muijoy-ink-tauri/src-tauri/icons/Square44x44Logo.png +0 -0
  60. package/template-react-vite-muijoy-ink-tauri/src-tauri/icons/Square71x71Logo.png +0 -0
  61. package/template-react-vite-muijoy-ink-tauri/src-tauri/icons/Square89x89Logo.png +0 -0
  62. package/template-react-vite-muijoy-ink-tauri/src-tauri/icons/StoreLogo.png +0 -0
  63. package/template-react-vite-muijoy-ink-tauri/src-tauri/icons/icon.icns +0 -0
  64. package/template-react-vite-muijoy-ink-tauri/src-tauri/icons/icon.ico +0 -0
  65. package/template-react-vite-muijoy-ink-tauri/src-tauri/icons/icon.png +0 -0
  66. package/template-react-vite-muijoy-ink-tauri/src-tauri/src/lib.rs +64 -0
  67. package/template-react-vite-muijoy-ink-tauri/src-tauri/src/main.rs +6 -0
  68. package/template-react-vite-muijoy-ink-tauri/src-tauri/src/steam.rs +238 -0
  69. package/template-react-vite-muijoy-ink-tauri/src-tauri/tauri.conf.json +50 -0
  70. package/template-react-vite-muijoy-ink-tauri/vite.config.ts +30 -0
  71. package/template-react-vite-muijoy-tauri/.assetpack.ts +9 -0
  72. package/template-react-vite-muijoy-tauri/.vscode/extensions.json +4 -1
  73. package/template-react-vite-muijoy-tauri/.vscode/launch.json +11 -0
  74. package/template-react-vite-muijoy-tauri/.vscode/tasks.json +47 -0
  75. package/template-react-vite-muijoy-tauri/README.md +3 -3
  76. package/template-react-vite-muijoy-tauri/_github/workflows/desktop.yml +188 -0
  77. package/template-react-vite-muijoy-tauri/_github/workflows/mobile.yml +270 -0
  78. package/template-react-vite-muijoy-tauri/_gitignore +10 -0
  79. package/template-react-vite-muijoy-tauri/package-lock.json +250 -0
  80. package/template-react-vite-muijoy-tauri/package.json +10 -1
  81. package/template-react-vite-muijoy-tauri/src/components/menus/main-menu.tsx +16 -1
  82. package/template-react-vite-muijoy-tauri/src/components/menus/settings/quick-menus.tsx +17 -1
  83. package/template-react-vite-muijoy-tauri/src/components/menus/settings/system-controls.tsx +11 -1
  84. package/template-react-vite-muijoy-tauri/src/lib/hooks/quit-hooks.ts +24 -0
  85. package/template-react-vite-muijoy-tauri/src/lib/query/settings-query.ts +7 -1
  86. package/template-react-vite-muijoy-tauri/src/lib/steam.ts +143 -0
  87. package/template-react-vite-muijoy-tauri/src-tauri/Cargo.toml +50 -0
  88. package/template-react-vite-muijoy-tauri/src-tauri/build.rs +46 -0
  89. package/template-react-vite-muijoy-tauri/src-tauri/capabilities/default.json +21 -0
  90. package/template-react-vite-muijoy-tauri/src-tauri/icons/128x128.png +0 -0
  91. package/template-react-vite-muijoy-tauri/src-tauri/icons/128x128@2x.png +0 -0
  92. package/template-react-vite-muijoy-tauri/src-tauri/icons/32x32.png +0 -0
  93. package/template-react-vite-muijoy-tauri/src-tauri/icons/Square107x107Logo.png +0 -0
  94. package/template-react-vite-muijoy-tauri/src-tauri/icons/Square142x142Logo.png +0 -0
  95. package/template-react-vite-muijoy-tauri/src-tauri/icons/Square150x150Logo.png +0 -0
  96. package/template-react-vite-muijoy-tauri/src-tauri/icons/Square284x284Logo.png +0 -0
  97. package/template-react-vite-muijoy-tauri/src-tauri/icons/Square30x30Logo.png +0 -0
  98. package/template-react-vite-muijoy-tauri/src-tauri/icons/Square310x310Logo.png +0 -0
  99. package/template-react-vite-muijoy-tauri/src-tauri/icons/Square44x44Logo.png +0 -0
  100. package/template-react-vite-muijoy-tauri/src-tauri/icons/Square71x71Logo.png +0 -0
  101. package/template-react-vite-muijoy-tauri/src-tauri/icons/Square89x89Logo.png +0 -0
  102. package/template-react-vite-muijoy-tauri/src-tauri/icons/StoreLogo.png +0 -0
  103. package/template-react-vite-muijoy-tauri/src-tauri/icons/icon.icns +0 -0
  104. package/template-react-vite-muijoy-tauri/src-tauri/icons/icon.ico +0 -0
  105. package/template-react-vite-muijoy-tauri/src-tauri/icons/icon.png +0 -0
  106. package/template-react-vite-muijoy-tauri/src-tauri/src/lib.rs +64 -0
  107. package/template-react-vite-muijoy-tauri/src-tauri/src/main.rs +6 -0
  108. package/template-react-vite-muijoy-tauri/src-tauri/src/steam.rs +238 -0
  109. package/template-react-vite-muijoy-tauri/src-tauri/tauri.conf.json +50 -0
  110. package/template-react-vite-muijoy-tauri/vite.config.ts +24 -0
  111. package/template-react-vite-muijoy-ink/src/content/labels/second.label.ts +0 -1207
  112. package/template-react-vite-muijoy-ink/src/content/labels/start.label.ts +0 -566
  113. package/template-react-vite-muijoy-ink-tauri/src/content/labels/second.label.ts +0 -1207
  114. package/template-react-vite-muijoy-ink-tauri/src/content/labels/start.label.ts +0 -566
@@ -0,0 +1,46 @@
1
+ use std::{env, fs, path::PathBuf};
2
+
3
+ fn main() {
4
+ if env::var("CARGO_FEATURE_STEAM").is_ok() {
5
+ copy_steam_lib();
6
+ }
7
+ tauri_build::build()
8
+ }
9
+
10
+ // Copies the Steamworks redistributable library from steamworks-sys's OUT_DIR
11
+ // into src-tauri/ so that Tauri's bundler can pick it up as a resource.
12
+ fn copy_steam_lib() {
13
+ let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
14
+ let manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
15
+
16
+ let lib_name = match env::var("CARGO_CFG_TARGET_OS").as_deref() {
17
+ Ok("windows") => "steam_api64.dll",
18
+ Ok("macos") => "libsteam_api.dylib",
19
+ _ => "libsteam_api.so",
20
+ };
21
+
22
+ // OUT_DIR layout: .../target/[profile]/build/[pkg-hash]/out
23
+ // nth(2) walks up to .../target/[profile]/build/
24
+ let build_dir = out_dir
25
+ .ancestors()
26
+ .nth(2)
27
+ .expect("unexpected OUT_DIR layout");
28
+
29
+ if let Ok(entries) = fs::read_dir(build_dir) {
30
+ for entry in entries.flatten() {
31
+ let candidate = entry.path().join("out").join(lib_name);
32
+ if candidate.exists() {
33
+ let dest = manifest_dir.join(lib_name);
34
+ fs::copy(&candidate, &dest)
35
+ .unwrap_or_else(|e| panic!("failed to copy {lib_name}: {e}"));
36
+ println!("cargo:warning=Steam: copied {lib_name} → src-tauri/");
37
+ return;
38
+ }
39
+ }
40
+ }
41
+
42
+ panic!(
43
+ "Steam: could not find `{lib_name}` in build artifacts. \
44
+ Ensure `--features steam` is active and the crate compiled successfully."
45
+ );
46
+ }
@@ -0,0 +1,21 @@
1
+ {
2
+ "$schema": "../gen/schemas/desktop-schema.json",
3
+ "identifier": "default",
4
+ "description": "Capability for the main window",
5
+ "windows": ["main"],
6
+ "permissions": [
7
+ "core:default",
8
+ "core:window:allow-close",
9
+ "core:window:allow-is-fullscreen",
10
+ "core:window:allow-set-fullscreen",
11
+ "core:window:allow-minimize",
12
+ "core:window:allow-maximize",
13
+ "core:window:allow-unminimize",
14
+ "core:window:allow-set-title",
15
+ "opener:default",
16
+ "process:default",
17
+ "window-state:allow-filename",
18
+ "window-state:allow-restore-state",
19
+ "window-state:allow-save-window-state"
20
+ ]
21
+ }
@@ -0,0 +1,64 @@
1
+ use tauri::Manager;
2
+ #[cfg(not(mobile))]
3
+ use tauri_plugin_window_state::{AppHandleExt, StateFlags};
4
+
5
+ // Steam is only compiled on desktop targets when the "steam" feature is on.
6
+ #[cfg(all(feature = "steam", not(target_os = "ios"), not(target_os = "android")))]
7
+ mod steam;
8
+
9
+ #[cfg_attr(mobile, tauri::mobile_entry_point)]
10
+ pub fn run() {
11
+ #[allow(unused_mut)]
12
+ let mut builder = tauri::Builder::default()
13
+ .plugin(tauri_plugin_process::init())
14
+ .plugin(tauri_plugin_opener::init())
15
+ .on_page_load(on_page_load);
16
+
17
+ #[cfg(not(mobile))]
18
+ let mut builder = builder
19
+ .plugin(tauri_plugin_window_state::Builder::new().build())
20
+ .on_window_event(on_window_event);
21
+
22
+ // ── Steam ─────────────────────────────────────────────────────────────────
23
+ // Enabled only when `--features steam` is passed (or `default = ["steam"]`
24
+ // is set in Cargo.toml). Steam is not supported on iOS / Android.
25
+ #[cfg(all(feature = "steam", not(target_os = "ios"), not(target_os = "android")))]
26
+ {
27
+ builder = builder
28
+ .manage(steam::SteamClient {
29
+ client: std::sync::Mutex::new(steam::try_init()),
30
+ })
31
+ .invoke_handler(tauri::generate_handler![
32
+ steam::steam_is_available,
33
+ steam::steam_get_player_name,
34
+ steam::steam_get_app_id,
35
+ steam::steam_unlock_achievement,
36
+ steam::steam_is_achievement_unlocked,
37
+ steam::steam_clear_achievement,
38
+ steam::steam_set_stat_int,
39
+ steam::steam_get_stat_int,
40
+ steam::steam_set_stat_float,
41
+ steam::steam_get_stat_float,
42
+ steam::steam_store_stats,
43
+ steam::steam_is_dlc_installed,
44
+ steam::steam_open_overlay,
45
+ steam::steam_open_store,
46
+ ]);
47
+ }
48
+
49
+ builder
50
+ .run(tauri::generate_context!())
51
+ .expect("error while running tauri application");
52
+ }
53
+
54
+ fn on_page_load(window: &tauri::Webview, _: &tauri::webview::PageLoadPayload<'_>) {
55
+ #[cfg(not(debug_assertions))]
56
+ let _ = window.eval("document.addEventListener('contextmenu', e => e.preventDefault())");
57
+ }
58
+
59
+ #[cfg(not(mobile))]
60
+ fn on_window_event(window: &tauri::Window, event: &tauri::WindowEvent) {
61
+ if let tauri::WindowEvent::CloseRequested { .. } = event {
62
+ let _ = window.app_handle().save_window_state(StateFlags::all());
63
+ }
64
+ }
@@ -0,0 +1,6 @@
1
+ // Prevents additional console window on Windows in release, DO NOT REMOVE!!
2
+ #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
3
+
4
+ fn main() {
5
+ my_app_lib::run()
6
+ }
@@ -0,0 +1,238 @@
1
+ use std::sync::Mutex;
2
+ use steamworks::{AppId, Client, OverlayToStoreFlag};
3
+ use tauri::State;
4
+
5
+ pub struct SteamClient {
6
+ pub client: Mutex<Option<Client>>,
7
+ }
8
+
9
+ /// Try to initialise Steam. Returns `None` when Steam is not running or the
10
+ /// game is launched outside Steam (e.g. during CI builds).
11
+ ///
12
+ /// A background thread is spawned to pump Steam callbacks every 100 ms so
13
+ /// that overlay notifications, stat uploads, and other async operations work.
14
+ pub fn try_init() -> Option<Client> {
15
+ match Client::init() {
16
+ Ok(client) => {
17
+ // Client is Clone + Send + Sync in steamworks 0.13 — safe to move into a thread.
18
+ let ticker = client.clone();
19
+ std::thread::spawn(move || loop {
20
+ ticker.run_callbacks();
21
+ std::thread::sleep(std::time::Duration::from_millis(100));
22
+ });
23
+
24
+ eprintln!("[Steam] Initialised — App ID {}", client.utils().app_id().0);
25
+ Some(client)
26
+ }
27
+ Err(e) => {
28
+ eprintln!("[Steam] Not available: {e}");
29
+ None
30
+ }
31
+ }
32
+ }
33
+
34
+ // ── Commands ──────────────────────────────────────────────────────────────────
35
+
36
+ /// Returns `true` when Steam was initialised successfully.
37
+ #[tauri::command]
38
+ pub fn steam_is_available(state: State<'_, SteamClient>) -> bool {
39
+ state.client.lock().map_or(false, |g| g.is_some())
40
+ }
41
+
42
+ /// Steam display name of the current user (`null` when Steam unavailable).
43
+ #[tauri::command]
44
+ pub fn steam_get_player_name(state: State<'_, SteamClient>) -> Option<String> {
45
+ state
46
+ .client
47
+ .lock()
48
+ .ok()
49
+ .and_then(|g| g.as_ref().map(|c| c.friends().name()))
50
+ }
51
+
52
+ /// Numeric Steam App ID of the running application.
53
+ #[tauri::command]
54
+ pub fn steam_get_app_id(state: State<'_, SteamClient>) -> Option<u32> {
55
+ state
56
+ .client
57
+ .lock()
58
+ .ok()
59
+ .and_then(|g| g.as_ref().map(|c| c.utils().app_id().0))
60
+ }
61
+
62
+ // ── Achievements ──────────────────────────────────────────────────────────────
63
+
64
+ /// Unlock an achievement and persist it to Steam immediately.
65
+ ///
66
+ /// `achievement_id` must match the API Name defined in Steamworks Partner.
67
+ #[tauri::command]
68
+ pub fn steam_unlock_achievement(
69
+ state: State<'_, SteamClient>,
70
+ achievement_id: String,
71
+ ) -> Result<(), String> {
72
+ let guard = state.client.lock().map_err(|e| e.to_string())?;
73
+ let client = guard.as_ref().ok_or("Steam not available")?;
74
+ let stats = client.user_stats();
75
+ stats
76
+ .achievement(&achievement_id)
77
+ .set()
78
+ .map_err(|_| format!("Achievement '{}' not found or stats not ready", achievement_id))?;
79
+ stats
80
+ .store_stats()
81
+ .map_err(|_| "Failed to store stats".to_string())
82
+ }
83
+
84
+ /// Returns `true` if the current user has already unlocked the achievement.
85
+ ///
86
+ /// Stats are fetched automatically when Steam initialises. Allow a few seconds
87
+ /// after launch before calling this for the most reliable result.
88
+ #[tauri::command]
89
+ pub fn steam_is_achievement_unlocked(
90
+ state: State<'_, SteamClient>,
91
+ achievement_id: String,
92
+ ) -> Result<bool, String> {
93
+ let guard = state.client.lock().map_err(|e| e.to_string())?;
94
+ let client = guard.as_ref().ok_or("Steam not available")?;
95
+ client
96
+ .user_stats()
97
+ .achievement(&achievement_id)
98
+ .get()
99
+ .map_err(|_| format!("Achievement '{}' not found or stats not ready", achievement_id))
100
+ }
101
+
102
+ /// Reset an achievement (useful during development / QA).
103
+ #[tauri::command]
104
+ pub fn steam_clear_achievement(
105
+ state: State<'_, SteamClient>,
106
+ achievement_id: String,
107
+ ) -> Result<(), String> {
108
+ let guard = state.client.lock().map_err(|e| e.to_string())?;
109
+ let client = guard.as_ref().ok_or("Steam not available")?;
110
+ let stats = client.user_stats();
111
+ stats
112
+ .achievement(&achievement_id)
113
+ .clear()
114
+ .map_err(|_| format!("Achievement '{}' not found or stats not ready", achievement_id))?;
115
+ stats
116
+ .store_stats()
117
+ .map_err(|_| "Failed to store stats".to_string())
118
+ }
119
+
120
+ // ── Stats ─────────────────────────────────────────────────────────────────────
121
+
122
+ /// Set an integer stat. Call `steam_store_stats` to persist.
123
+ #[tauri::command]
124
+ pub fn steam_set_stat_int(
125
+ state: State<'_, SteamClient>,
126
+ name: String,
127
+ value: i32,
128
+ ) -> Result<(), String> {
129
+ let guard = state.client.lock().map_err(|e| e.to_string())?;
130
+ let client = guard.as_ref().ok_or("Steam not available")?;
131
+ client
132
+ .user_stats()
133
+ .set_stat_i32(&name, value)
134
+ .map_err(|_| format!("Failed to set stat '{}'", name))
135
+ }
136
+
137
+ /// Read an integer stat.
138
+ #[tauri::command]
139
+ pub fn steam_get_stat_int(
140
+ state: State<'_, SteamClient>,
141
+ name: String,
142
+ ) -> Result<i32, String> {
143
+ let guard = state.client.lock().map_err(|e| e.to_string())?;
144
+ let client = guard.as_ref().ok_or("Steam not available")?;
145
+ client
146
+ .user_stats()
147
+ .get_stat_i32(&name)
148
+ .map_err(|_| format!("Failed to get stat '{}'", name))
149
+ }
150
+
151
+ /// Set a float stat. Call `steam_store_stats` to persist.
152
+ #[tauri::command]
153
+ pub fn steam_set_stat_float(
154
+ state: State<'_, SteamClient>,
155
+ name: String,
156
+ value: f32,
157
+ ) -> Result<(), String> {
158
+ let guard = state.client.lock().map_err(|e| e.to_string())?;
159
+ let client = guard.as_ref().ok_or("Steam not available")?;
160
+ client
161
+ .user_stats()
162
+ .set_stat_f32(&name, value)
163
+ .map_err(|_| format!("Failed to set stat '{}'", name))
164
+ }
165
+
166
+ /// Read a float stat.
167
+ #[tauri::command]
168
+ pub fn steam_get_stat_float(
169
+ state: State<'_, SteamClient>,
170
+ name: String,
171
+ ) -> Result<f32, String> {
172
+ let guard = state.client.lock().map_err(|e| e.to_string())?;
173
+ let client = guard.as_ref().ok_or("Steam not available")?;
174
+ client
175
+ .user_stats()
176
+ .get_stat_f32(&name)
177
+ .map_err(|_| format!("Failed to get stat '{}'", name))
178
+ }
179
+
180
+ /// Commit all pending stat changes to Steam servers.
181
+ /// Achievement commands already call this automatically.
182
+ #[tauri::command]
183
+ pub fn steam_store_stats(state: State<'_, SteamClient>) -> Result<(), String> {
184
+ let guard = state.client.lock().map_err(|e| e.to_string())?;
185
+ let client = guard.as_ref().ok_or("Steam not available")?;
186
+ client
187
+ .user_stats()
188
+ .store_stats()
189
+ .map_err(|_| "Failed to store stats".to_string())
190
+ }
191
+
192
+ // ── DLC ───────────────────────────────────────────────────────────────────────
193
+
194
+ /// Returns `true` if the user owns and has the specified DLC installed.
195
+ #[tauri::command]
196
+ pub fn steam_is_dlc_installed(state: State<'_, SteamClient>, app_id: u32) -> bool {
197
+ state.client.lock().map_or(false, |g| {
198
+ g.as_ref()
199
+ .map_or(false, |c| c.apps().is_dlc_installed(AppId(app_id)))
200
+ })
201
+ }
202
+
203
+ // ── Overlay ───────────────────────────────────────────────────────────────────
204
+
205
+ /// Open the Steam overlay to a specific dialog.
206
+ ///
207
+ /// Valid `dialog` values:
208
+ /// "achievements", "community", "friends", "players",
209
+ /// "settings", "officialgamegroup", "stats"
210
+ #[tauri::command]
211
+ pub fn steam_open_overlay(state: State<'_, SteamClient>, dialog: String) -> bool {
212
+ let Ok(guard) = state.client.lock() else {
213
+ return false;
214
+ };
215
+ let Some(client) = guard.as_ref() else {
216
+ return false;
217
+ };
218
+ client.friends().activate_game_overlay(&dialog);
219
+ true
220
+ }
221
+
222
+ /// Open the Steam store page for this game (or a specific `app_id`).
223
+ #[tauri::command]
224
+ pub fn steam_open_store(state: State<'_, SteamClient>, app_id: Option<u32>) -> bool {
225
+ let Ok(guard) = state.client.lock() else {
226
+ return false;
227
+ };
228
+ let Some(client) = guard.as_ref() else {
229
+ return false;
230
+ };
231
+ let target = app_id
232
+ .map(AppId)
233
+ .unwrap_or_else(|| client.utils().app_id());
234
+ client
235
+ .friends()
236
+ .activate_game_overlay_to_store(target, OverlayToStoreFlag::None);
237
+ true
238
+ }
@@ -0,0 +1,50 @@
1
+ {
2
+ "$schema": "https://schema.tauri.app/config/2",
3
+ "productName": "my-app-package-name",
4
+ "version": "0.1.0",
5
+ "identifier": "com.my-app-identifier.app",
6
+ "build": {
7
+ "beforeDevCommand": "npm run dev",
8
+ "devUrl": "http://localhost:5173",
9
+ "beforeBuildCommand": "npm run build",
10
+ "frontendDist": "../dist"
11
+ },
12
+ "app": {
13
+ "withGlobalTauri": true,
14
+ "windows": [
15
+ {
16
+ "title": "my-app-project-name",
17
+ "backgroundColor": "#303030",
18
+ "theme": "Dark",
19
+ "width": 1280,
20
+ "height": 720,
21
+ "minWidth": 640,
22
+ "minHeight": 360,
23
+ "center": true,
24
+ "fullscreen": false,
25
+ "resizable": true,
26
+ "dragDropEnabled": false,
27
+ "zoomHotkeysEnabled": false
28
+ }
29
+ ],
30
+ "security": {
31
+ "csp": null
32
+ }
33
+ },
34
+ "bundle": {
35
+ "active": true,
36
+ "targets": "all",
37
+ "icon": [
38
+ "icons/32x32.png",
39
+ "icons/128x128.png",
40
+ "icons/128x128@2x.png",
41
+ "icons/icon.icns",
42
+ "icons/icon.ico"
43
+ ],
44
+ "windows": {
45
+ "nsis": {
46
+ "installerIcon": "icons/icon.ico"
47
+ }
48
+ }
49
+ }
50
+ }
@@ -18,6 +18,8 @@ import assetPackConfig from "./.assetpack.ts";
18
18
  */
19
19
  const CACHED_EXTERNAL_HOSTNAMES: string[] = ["raw.githubusercontent.com"];
20
20
 
21
+ const host = process.env.TAURI_DEV_HOST;
22
+
21
23
  // https://vite.dev/config/
22
24
  export default defineConfig(({ mode }) => ({
23
25
  plugins: [
@@ -112,6 +114,28 @@ export default defineConfig(({ mode }) => ({
112
114
  },
113
115
  },
114
116
  },
117
+
118
+ // Vite options tailored for Tauri development and only applied in `tauri dev` or `tauri build`
119
+ //
120
+ // 1. prevent vite from obscuring rust errors
121
+ clearScreen: false,
122
+ // 2. tauri expects a fixed port, fail if that port is not available
123
+ server: {
124
+ port: 5173,
125
+ strictPort: true,
126
+ host: host || false,
127
+ hmr: host
128
+ ? {
129
+ protocol: "ws",
130
+ host,
131
+ port: 1421,
132
+ }
133
+ : undefined,
134
+ watch: {
135
+ // 3. tell vite to ignore watching `src-tauri`
136
+ ignored: ["**/src-tauri/**"],
137
+ },
138
+ },
115
139
  }));
116
140
 
117
141
  function assetpackPlugin(): Plugin {