create-pixi-vn 2.1.5 → 2.1.7

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "create-pixi-vn",
3
3
  "description": "Create a new Pixi’VN project",
4
- "version": "2.1.5",
4
+ "version": "2.1.7",
5
5
  "type": "module",
6
6
  "license": "GPL-3.0",
7
7
  "author": "DRincs-Productions",
@@ -13,7 +13,7 @@ import { useGameProps } from "@/lib/hooks/props-hooks";
13
13
  import { useQueryLastSave } from "@/lib/query/save-query";
14
14
  import { InterfaceSettings } from "@/lib/stores/interface-settings-store";
15
15
  import { cn } from "@/lib/utils";
16
- import { loadSave } from "@/lib/utils/save-utility";
16
+ import { loadRefreshSave, loadSave } from "@/lib/utils/save-utility";
17
17
  import { canvas, Game, ImageSprite } from "@drincs/pixi-vn";
18
18
  import { useHotkeys } from "@tanstack/react-hotkeys";
19
19
  import { useQueryClient } from "@tanstack/react-query";
@@ -224,7 +224,7 @@ export function ContinueMenuButton({
224
224
  if (!lastSave) return;
225
225
  setLoading(true);
226
226
  onLoadingChange?.(true);
227
- loadSave(lastSave)
227
+ (hasRefreshSave ? loadRefreshSave() : loadSave(lastSave))
228
228
  .then(() =>
229
229
  queryClient.invalidateQueries({
230
230
  queryKey: [INTERFACE_DATA_USE_QUERY_KEY],
@@ -238,7 +238,7 @@ export function ContinueMenuButton({
238
238
  setLoading(false);
239
239
  onLoadingChange?.(false);
240
240
  });
241
- }, [lastSave, queryClient, t, onLoadingChange]);
241
+ }, [lastSave, hasRefreshSave, queryClient, t, onLoadingChange]);
242
242
 
243
243
  const isDisabled = (!isLoading && !lastSave) || loading || disabled;
244
244
 
@@ -13,7 +13,7 @@ import { useGameProps } from "@/lib/hooks/props-hooks";
13
13
  import { useQueryLastSave } from "@/lib/query/save-query";
14
14
  import { InterfaceSettings } from "@/lib/stores/interface-settings-store";
15
15
  import { cn } from "@/lib/utils";
16
- import { loadSave } from "@/lib/utils/save-utility";
16
+ import { loadRefreshSave, loadSave } from "@/lib/utils/save-utility";
17
17
  import { canvas, Game, ImageSprite } from "@drincs/pixi-vn";
18
18
  import { useHotkeys } from "@tanstack/react-hotkeys";
19
19
  import { useQueryClient } from "@tanstack/react-query";
@@ -224,7 +224,7 @@ export function ContinueMenuButton({
224
224
  if (!lastSave) return;
225
225
  setLoading(true);
226
226
  onLoadingChange?.(true);
227
- loadSave(lastSave)
227
+ (hasRefreshSave ? loadRefreshSave() : loadSave(lastSave))
228
228
  .then(() =>
229
229
  queryClient.invalidateQueries({
230
230
  queryKey: [INTERFACE_DATA_USE_QUERY_KEY],
@@ -238,7 +238,7 @@ export function ContinueMenuButton({
238
238
  setLoading(false);
239
239
  onLoadingChange?.(false);
240
240
  });
241
- }, [lastSave, queryClient, t, onLoadingChange]);
241
+ }, [lastSave, hasRefreshSave, queryClient, t, onLoadingChange]);
242
242
 
243
243
  const isDisabled = (!isLoading && !lastSave) || loading || disabled;
244
244
 
@@ -100,26 +100,89 @@ jobs:
100
100
  with open(manifest, 'w') as f:
101
101
  f.write(content)
102
102
 
103
- # Tauri may generate styles.xml or themes.xml depending on the version
103
+ # Tauri may generate styles.xml or themes.xml, and both a light (values/)
104
+ # and dark (values-night/) variant — patch every variant we find so the
105
+ # fullscreen flag doesn't silently no-op when the device is in dark mode.
104
106
  styles_candidates = (
105
- glob.glob('src-tauri/gen/android/app/src/main/res/values/styles.xml') +
106
- glob.glob('src-tauri/gen/android/app/src/main/res/values/themes.xml')
107
+ glob.glob('src-tauri/gen/android/app/src/main/res/values*/styles.xml') +
108
+ glob.glob('src-tauri/gen/android/app/src/main/res/values*/themes.xml')
107
109
  )
108
110
  if not styles_candidates:
109
111
  print('Warning: no styles.xml/themes.xml found, skipping fullscreen patch')
110
112
  else:
111
- styles = styles_candidates[0]
112
- with open(styles) as f:
113
+ for styles in styles_candidates:
114
+ with open(styles) as f:
115
+ content = f.read()
116
+ # Hide status bar (fullscreen) — kept as a best-effort theme hint,
117
+ # but on targetSdk 35+ (edge-to-edge is enforced by the OS) this
118
+ # alone has no effect; the real fix is the MainActivity.kt patch below.
119
+ content = content.replace(
120
+ '</style>',
121
+ ' <item name="android:windowFullscreen">true</item>\n </style>',
122
+ 1
123
+ )
124
+ with open(styles, 'w') as f:
125
+ f.write(content)
126
+ print(f'Patched {styles}')
127
+
128
+ # Since this project's Android template targets SDK 35+, edge-to-edge display
129
+ # is enforced by the OS and the theme-based windowFullscreen flag above no
130
+ # longer hides the status bar (it's always drawn, transparent, over the
131
+ # content). The only reliable way to hide it is to hide the system bars at
132
+ # runtime via WindowInsetsControllerCompat in MainActivity.kt.
133
+ activity_candidates = glob.glob(
134
+ 'src-tauri/gen/android/app/src/main/**/MainActivity.kt', recursive=True
135
+ )
136
+ if not activity_candidates:
137
+ print('WARNING: MainActivity.kt not found — status bar hiding NOT patched')
138
+ else:
139
+ activity = activity_candidates[0]
140
+ with open(activity) as f:
113
141
  content = f.read()
114
- # Hide status bar (fullscreen)
115
- content = content.replace(
116
- '</style>',
117
- ' <item name="android:windowFullscreen">true</item>\n </style>',
118
- 1
119
- )
120
- with open(styles, 'w') as f:
142
+
143
+ new_imports = [
144
+ 'androidx.core.view.WindowCompat',
145
+ 'androidx.core.view.WindowInsetsCompat',
146
+ 'androidx.core.view.WindowInsetsControllerCompat',
147
+ ]
148
+ import_lines = list(re.finditer(r'^import .+$', content, re.MULTILINE))
149
+ if import_lines:
150
+ insert_at = import_lines[-1].end()
151
+ addition = ''.join(
152
+ f'\nimport {imp}' for imp in new_imports if imp not in content
153
+ )
154
+ content = content[:insert_at] + addition + content[insert_at:]
155
+
156
+ if 'hideSystemBars' not in content:
157
+ content = content.replace(
158
+ 'super.onCreate(savedInstanceState)',
159
+ 'super.onCreate(savedInstanceState)\n hideSystemBars()',
160
+ 1
161
+ )
162
+ content = re.sub(
163
+ r'\n}\s*$',
164
+ '\n\n'
165
+ ' override fun onWindowFocusChanged(hasFocus: Boolean) {\n'
166
+ ' super.onWindowFocusChanged(hasFocus)\n'
167
+ ' if (hasFocus) {\n'
168
+ ' hideSystemBars()\n'
169
+ ' }\n'
170
+ ' }\n'
171
+ '\n'
172
+ ' private fun hideSystemBars() {\n'
173
+ ' WindowCompat.setDecorFitsSystemWindows(window, false)\n'
174
+ ' val controller = WindowInsetsControllerCompat(window, window.decorView)\n'
175
+ ' controller.hide(WindowInsetsCompat.Type.systemBars())\n'
176
+ ' controller.systemBarsBehavior =\n'
177
+ ' WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE\n'
178
+ ' }\n'
179
+ '}\n',
180
+ content,
181
+ count=1
182
+ )
183
+ with open(activity, 'w') as f:
121
184
  f.write(content)
122
- print(f'Patched {styles}')
185
+ print(f'Patched {activity} (hide system bars at runtime)')
123
186
  PYEOF
124
187
 
125
188
  - name: build Android app
@@ -14,7 +14,7 @@ import { useQuit } from "@/lib/hooks/quit-hooks";
14
14
  import { useQueryLastSave } from "@/lib/query/save-query";
15
15
  import { InterfaceSettings } from "@/lib/stores/interface-settings-store";
16
16
  import { cn } from "@/lib/utils";
17
- import { loadSave } from "@/lib/utils/save-utility";
17
+ import { loadRefreshSave, loadSave } from "@/lib/utils/save-utility";
18
18
  import { canvas, Game, ImageSprite } from "@drincs/pixi-vn";
19
19
  import { useHotkeys } from "@tanstack/react-hotkeys";
20
20
  import { useQueryClient } from "@tanstack/react-query";
@@ -239,7 +239,7 @@ export function ContinueMenuButton({
239
239
  if (!lastSave) return;
240
240
  setLoading(true);
241
241
  onLoadingChange?.(true);
242
- loadSave(lastSave)
242
+ (hasRefreshSave ? loadRefreshSave() : loadSave(lastSave))
243
243
  .then(() =>
244
244
  queryClient.invalidateQueries({
245
245
  queryKey: [INTERFACE_DATA_USE_QUERY_KEY],
@@ -253,7 +253,7 @@ export function ContinueMenuButton({
253
253
  setLoading(false);
254
254
  onLoadingChange?.(false);
255
255
  });
256
- }, [lastSave, queryClient, t, onLoadingChange]);
256
+ }, [lastSave, hasRefreshSave, queryClient, t, onLoadingChange]);
257
257
 
258
258
  const isDisabled = (!isLoading && !lastSave) || loading || disabled;
259
259
 
@@ -100,26 +100,89 @@ jobs:
100
100
  with open(manifest, 'w') as f:
101
101
  f.write(content)
102
102
 
103
- # Tauri may generate styles.xml or themes.xml depending on the version
103
+ # Tauri may generate styles.xml or themes.xml, and both a light (values/)
104
+ # and dark (values-night/) variant — patch every variant we find so the
105
+ # fullscreen flag doesn't silently no-op when the device is in dark mode.
104
106
  styles_candidates = (
105
- glob.glob('src-tauri/gen/android/app/src/main/res/values/styles.xml') +
106
- glob.glob('src-tauri/gen/android/app/src/main/res/values/themes.xml')
107
+ glob.glob('src-tauri/gen/android/app/src/main/res/values*/styles.xml') +
108
+ glob.glob('src-tauri/gen/android/app/src/main/res/values*/themes.xml')
107
109
  )
108
110
  if not styles_candidates:
109
111
  print('Warning: no styles.xml/themes.xml found, skipping fullscreen patch')
110
112
  else:
111
- styles = styles_candidates[0]
112
- with open(styles) as f:
113
+ for styles in styles_candidates:
114
+ with open(styles) as f:
115
+ content = f.read()
116
+ # Hide status bar (fullscreen) — kept as a best-effort theme hint,
117
+ # but on targetSdk 35+ (edge-to-edge is enforced by the OS) this
118
+ # alone has no effect; the real fix is the MainActivity.kt patch below.
119
+ content = content.replace(
120
+ '</style>',
121
+ ' <item name="android:windowFullscreen">true</item>\n </style>',
122
+ 1
123
+ )
124
+ with open(styles, 'w') as f:
125
+ f.write(content)
126
+ print(f'Patched {styles}')
127
+
128
+ # Since this project's Android template targets SDK 35+, edge-to-edge display
129
+ # is enforced by the OS and the theme-based windowFullscreen flag above no
130
+ # longer hides the status bar (it's always drawn, transparent, over the
131
+ # content). The only reliable way to hide it is to hide the system bars at
132
+ # runtime via WindowInsetsControllerCompat in MainActivity.kt.
133
+ activity_candidates = glob.glob(
134
+ 'src-tauri/gen/android/app/src/main/**/MainActivity.kt', recursive=True
135
+ )
136
+ if not activity_candidates:
137
+ print('WARNING: MainActivity.kt not found — status bar hiding NOT patched')
138
+ else:
139
+ activity = activity_candidates[0]
140
+ with open(activity) as f:
113
141
  content = f.read()
114
- # Hide status bar (fullscreen)
115
- content = content.replace(
116
- '</style>',
117
- ' <item name="android:windowFullscreen">true</item>\n </style>',
118
- 1
119
- )
120
- with open(styles, 'w') as f:
142
+
143
+ new_imports = [
144
+ 'androidx.core.view.WindowCompat',
145
+ 'androidx.core.view.WindowInsetsCompat',
146
+ 'androidx.core.view.WindowInsetsControllerCompat',
147
+ ]
148
+ import_lines = list(re.finditer(r'^import .+$', content, re.MULTILINE))
149
+ if import_lines:
150
+ insert_at = import_lines[-1].end()
151
+ addition = ''.join(
152
+ f'\nimport {imp}' for imp in new_imports if imp not in content
153
+ )
154
+ content = content[:insert_at] + addition + content[insert_at:]
155
+
156
+ if 'hideSystemBars' not in content:
157
+ content = content.replace(
158
+ 'super.onCreate(savedInstanceState)',
159
+ 'super.onCreate(savedInstanceState)\n hideSystemBars()',
160
+ 1
161
+ )
162
+ content = re.sub(
163
+ r'\n}\s*$',
164
+ '\n\n'
165
+ ' override fun onWindowFocusChanged(hasFocus: Boolean) {\n'
166
+ ' super.onWindowFocusChanged(hasFocus)\n'
167
+ ' if (hasFocus) {\n'
168
+ ' hideSystemBars()\n'
169
+ ' }\n'
170
+ ' }\n'
171
+ '\n'
172
+ ' private fun hideSystemBars() {\n'
173
+ ' WindowCompat.setDecorFitsSystemWindows(window, false)\n'
174
+ ' val controller = WindowInsetsControllerCompat(window, window.decorView)\n'
175
+ ' controller.hide(WindowInsetsCompat.Type.systemBars())\n'
176
+ ' controller.systemBarsBehavior =\n'
177
+ ' WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE\n'
178
+ ' }\n'
179
+ '}\n',
180
+ content,
181
+ count=1
182
+ )
183
+ with open(activity, 'w') as f:
121
184
  f.write(content)
122
- print(f'Patched {styles}')
185
+ print(f'Patched {activity} (hide system bars at runtime)')
123
186
  PYEOF
124
187
 
125
188
  - name: build Android app
@@ -14,7 +14,7 @@ import { useQuit } from "@/lib/hooks/quit-hooks";
14
14
  import { useQueryLastSave } from "@/lib/query/save-query";
15
15
  import { InterfaceSettings } from "@/lib/stores/interface-settings-store";
16
16
  import { cn } from "@/lib/utils";
17
- import { loadSave } from "@/lib/utils/save-utility";
17
+ import { loadRefreshSave, loadSave } from "@/lib/utils/save-utility";
18
18
  import { canvas, Game, ImageSprite } from "@drincs/pixi-vn";
19
19
  import { useHotkeys } from "@tanstack/react-hotkeys";
20
20
  import { useQueryClient } from "@tanstack/react-query";
@@ -239,7 +239,7 @@ export function ContinueMenuButton({
239
239
  if (!lastSave) return;
240
240
  setLoading(true);
241
241
  onLoadingChange?.(true);
242
- loadSave(lastSave)
242
+ (hasRefreshSave ? loadRefreshSave() : loadSave(lastSave))
243
243
  .then(() =>
244
244
  queryClient.invalidateQueries({
245
245
  queryKey: [INTERFACE_DATA_USE_QUERY_KEY],
@@ -253,7 +253,7 @@ export function ContinueMenuButton({
253
253
  setLoading(false);
254
254
  onLoadingChange?.(false);
255
255
  });
256
- }, [lastSave, queryClient, t, onLoadingChange]);
256
+ }, [lastSave, hasRefreshSave, queryClient, t, onLoadingChange]);
257
257
 
258
258
  const isDisabled = (!isLoading && !lastSave) || loading || disabled;
259
259
 
@@ -13,7 +13,7 @@ import { useGameProps } from "@/lib/hooks/props-hooks";
13
13
  import { useQueryLastSave } from "@/lib/query/save-query";
14
14
  import { InterfaceSettings } from "@/lib/stores/interface-settings-store";
15
15
  import { cn } from "@/lib/utils";
16
- import { loadSave } from "@/lib/utils/save-utility";
16
+ import { loadRefreshSave, loadSave } from "@/lib/utils/save-utility";
17
17
  import { canvas, Game, ImageSprite } from "@drincs/pixi-vn";
18
18
  import { useHotkeys } from "@tanstack/react-hotkeys";
19
19
  import { useQueryClient } from "@tanstack/react-query";
@@ -225,7 +225,7 @@ export function ContinueMenuButton({
225
225
  if (!lastSave) return;
226
226
  setLoading(true);
227
227
  onLoadingChange?.(true);
228
- loadSave(lastSave)
228
+ (hasRefreshSave ? loadRefreshSave() : loadSave(lastSave))
229
229
  .then(() =>
230
230
  queryClient.invalidateQueries({
231
231
  queryKey: [INTERFACE_DATA_USE_QUERY_KEY],
@@ -239,7 +239,7 @@ export function ContinueMenuButton({
239
239
  setLoading(false);
240
240
  onLoadingChange?.(false);
241
241
  });
242
- }, [lastSave, queryClient, t, onLoadingChange]);
242
+ }, [lastSave, hasRefreshSave, queryClient, t, onLoadingChange]);
243
243
 
244
244
  const isDisabled = (!isLoading && !lastSave) || loading || disabled;
245
245
 
@@ -13,7 +13,7 @@ import { useGameProps } from "@/lib/hooks/props-hooks";
13
13
  import { useQueryLastSave } from "@/lib/query/save-query";
14
14
  import { InterfaceSettings } from "@/lib/stores/interface-settings-store";
15
15
  import { cn } from "@/lib/utils";
16
- import { loadSave } from "@/lib/utils/save-utility";
16
+ import { loadRefreshSave, loadSave } from "@/lib/utils/save-utility";
17
17
  import { canvas, Game, ImageSprite } from "@drincs/pixi-vn";
18
18
  import { useHotkeys } from "@tanstack/react-hotkeys";
19
19
  import { useQueryClient } from "@tanstack/react-query";
@@ -225,7 +225,7 @@ export function ContinueMenuButton({
225
225
  if (!lastSave) return;
226
226
  setLoading(true);
227
227
  onLoadingChange?.(true);
228
- loadSave(lastSave)
228
+ (hasRefreshSave ? loadRefreshSave() : loadSave(lastSave))
229
229
  .then(() =>
230
230
  queryClient.invalidateQueries({
231
231
  queryKey: [INTERFACE_DATA_USE_QUERY_KEY],
@@ -239,7 +239,7 @@ export function ContinueMenuButton({
239
239
  setLoading(false);
240
240
  onLoadingChange?.(false);
241
241
  });
242
- }, [lastSave, queryClient, t, onLoadingChange]);
242
+ }, [lastSave, hasRefreshSave, queryClient, t, onLoadingChange]);
243
243
 
244
244
  const isDisabled = (!isLoading && !lastSave) || loading || disabled;
245
245
 
@@ -100,26 +100,89 @@ jobs:
100
100
  with open(manifest, 'w') as f:
101
101
  f.write(content)
102
102
 
103
- # Tauri may generate styles.xml or themes.xml depending on the version
103
+ # Tauri may generate styles.xml or themes.xml, and both a light (values/)
104
+ # and dark (values-night/) variant — patch every variant we find so the
105
+ # fullscreen flag doesn't silently no-op when the device is in dark mode.
104
106
  styles_candidates = (
105
- glob.glob('src-tauri/gen/android/app/src/main/res/values/styles.xml') +
106
- glob.glob('src-tauri/gen/android/app/src/main/res/values/themes.xml')
107
+ glob.glob('src-tauri/gen/android/app/src/main/res/values*/styles.xml') +
108
+ glob.glob('src-tauri/gen/android/app/src/main/res/values*/themes.xml')
107
109
  )
108
110
  if not styles_candidates:
109
111
  print('Warning: no styles.xml/themes.xml found, skipping fullscreen patch')
110
112
  else:
111
- styles = styles_candidates[0]
112
- with open(styles) as f:
113
+ for styles in styles_candidates:
114
+ with open(styles) as f:
115
+ content = f.read()
116
+ # Hide status bar (fullscreen) — kept as a best-effort theme hint,
117
+ # but on targetSdk 35+ (edge-to-edge is enforced by the OS) this
118
+ # alone has no effect; the real fix is the MainActivity.kt patch below.
119
+ content = content.replace(
120
+ '</style>',
121
+ ' <item name="android:windowFullscreen">true</item>\n </style>',
122
+ 1
123
+ )
124
+ with open(styles, 'w') as f:
125
+ f.write(content)
126
+ print(f'Patched {styles}')
127
+
128
+ # Since this project's Android template targets SDK 35+, edge-to-edge display
129
+ # is enforced by the OS and the theme-based windowFullscreen flag above no
130
+ # longer hides the status bar (it's always drawn, transparent, over the
131
+ # content). The only reliable way to hide it is to hide the system bars at
132
+ # runtime via WindowInsetsControllerCompat in MainActivity.kt.
133
+ activity_candidates = glob.glob(
134
+ 'src-tauri/gen/android/app/src/main/**/MainActivity.kt', recursive=True
135
+ )
136
+ if not activity_candidates:
137
+ print('WARNING: MainActivity.kt not found — status bar hiding NOT patched')
138
+ else:
139
+ activity = activity_candidates[0]
140
+ with open(activity) as f:
113
141
  content = f.read()
114
- # Hide status bar (fullscreen)
115
- content = content.replace(
116
- '</style>',
117
- ' <item name="android:windowFullscreen">true</item>\n </style>',
118
- 1
119
- )
120
- with open(styles, 'w') as f:
142
+
143
+ new_imports = [
144
+ 'androidx.core.view.WindowCompat',
145
+ 'androidx.core.view.WindowInsetsCompat',
146
+ 'androidx.core.view.WindowInsetsControllerCompat',
147
+ ]
148
+ import_lines = list(re.finditer(r'^import .+$', content, re.MULTILINE))
149
+ if import_lines:
150
+ insert_at = import_lines[-1].end()
151
+ addition = ''.join(
152
+ f'\nimport {imp}' for imp in new_imports if imp not in content
153
+ )
154
+ content = content[:insert_at] + addition + content[insert_at:]
155
+
156
+ if 'hideSystemBars' not in content:
157
+ content = content.replace(
158
+ 'super.onCreate(savedInstanceState)',
159
+ 'super.onCreate(savedInstanceState)\n hideSystemBars()',
160
+ 1
161
+ )
162
+ content = re.sub(
163
+ r'\n}\s*$',
164
+ '\n\n'
165
+ ' override fun onWindowFocusChanged(hasFocus: Boolean) {\n'
166
+ ' super.onWindowFocusChanged(hasFocus)\n'
167
+ ' if (hasFocus) {\n'
168
+ ' hideSystemBars()\n'
169
+ ' }\n'
170
+ ' }\n'
171
+ '\n'
172
+ ' private fun hideSystemBars() {\n'
173
+ ' WindowCompat.setDecorFitsSystemWindows(window, false)\n'
174
+ ' val controller = WindowInsetsControllerCompat(window, window.decorView)\n'
175
+ ' controller.hide(WindowInsetsCompat.Type.systemBars())\n'
176
+ ' controller.systemBarsBehavior =\n'
177
+ ' WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE\n'
178
+ ' }\n'
179
+ '}\n',
180
+ content,
181
+ count=1
182
+ )
183
+ with open(activity, 'w') as f:
121
184
  f.write(content)
122
- print(f'Patched {styles}')
185
+ print(f'Patched {activity} (hide system bars at runtime)')
123
186
  PYEOF
124
187
 
125
188
  - name: build Android app
@@ -10,8 +10,8 @@
10
10
  "license": "GPL-3.0",
11
11
  "dependencies": {
12
12
  "@base-ui/react": "^1.6.0",
13
- "@drincs/pixi-vn": "^1.8.21",
14
- "@drincs/pixi-vn-ink": "^1.1.0",
13
+ "@drincs/pixi-vn": "^1.8.25",
14
+ "@drincs/pixi-vn-ink": "^1.1.9",
15
15
  "@tailwindcss/vite": "^4.3.0",
16
16
  "@tanstack/hotkeys": "^0.8.0",
17
17
  "@tanstack/react-hotkeys": "^0.10.0",
@@ -2119,9 +2119,9 @@
2119
2119
  }
2120
2120
  },
2121
2121
  "node_modules/@drincs/pixi-vn": {
2122
- "version": "1.8.21",
2123
- "resolved": "https://registry.npmjs.org/@drincs/pixi-vn/-/pixi-vn-1.8.21.tgz",
2124
- "integrity": "sha512-20RRsCo7Gd/dJlLb/SplI+7XuCV1U/J1wUAF/cLOWC+DtcSA3moWCwMGdH+PgcQBsNCd0sQxaGYdvJLrTy/ylg==",
2122
+ "version": "1.8.25",
2123
+ "resolved": "https://registry.npmjs.org/@drincs/pixi-vn/-/pixi-vn-1.8.25.tgz",
2124
+ "integrity": "sha512-S+SwiYcKdx9hnK78vnj/TZhNrtnNxArPyBp+h82jPIrUMwytkPpSHFCvXltC6NyWEsIFqlpyXzPPw8rJDApR+Q==",
2125
2125
  "license": "LGPL-2.1",
2126
2126
  "workspaces": [
2127
2127
  "sandbox"
@@ -2146,15 +2146,15 @@
2146
2146
  }
2147
2147
  },
2148
2148
  "node_modules/@drincs/pixi-vn-ink": {
2149
- "version": "1.1.0",
2150
- "resolved": "https://registry.npmjs.org/@drincs/pixi-vn-ink/-/pixi-vn-ink-1.1.0.tgz",
2151
- "integrity": "sha512-LRlpBQXiq1piPALEG7aR+j+x+1Ei3fTEX4ndhjFLfP7+UOwR+TzI41vfjYuIp1W9bbZNd0PsppPfdUEJuGNnOw==",
2149
+ "version": "1.1.9",
2150
+ "resolved": "https://registry.npmjs.org/@drincs/pixi-vn-ink/-/pixi-vn-ink-1.1.9.tgz",
2151
+ "integrity": "sha512-GJdOg+W0gP7PRo1BmtJQns3Sw6FBQupRffi8tb9zozVtlYaQ9OnZAARI4qkGVvhfMNNR8g4C9rellq7FonNiRA==",
2152
2152
  "license": "LGPL-2.1",
2153
2153
  "workspaces": [
2154
2154
  "playground"
2155
2155
  ],
2156
2156
  "dependencies": {
2157
- "@drincs/pixi-vn-json": "^1.13.10"
2157
+ "@drincs/pixi-vn-json": "^1.13.15"
2158
2158
  },
2159
2159
  "funding": {
2160
2160
  "url": "https://github.com/DRincs-Productions/pixi-vn?sponsor=1"
@@ -2170,9 +2170,9 @@
2170
2170
  }
2171
2171
  },
2172
2172
  "node_modules/@drincs/pixi-vn-json": {
2173
- "version": "1.13.10",
2174
- "resolved": "https://registry.npmjs.org/@drincs/pixi-vn-json/-/pixi-vn-json-1.13.10.tgz",
2175
- "integrity": "sha512-9rY+FoiGuNYfObe2U8369c5JU30uTUE0/C+sZjyUoDk30A7/oaC+9AxkZu/DwVl4U5KvyBkvN5V3vgEFg3I2Hg==",
2173
+ "version": "1.13.17",
2174
+ "resolved": "https://registry.npmjs.org/@drincs/pixi-vn-json/-/pixi-vn-json-1.13.17.tgz",
2175
+ "integrity": "sha512-SIIdrMEUqOgIWRMk9Bx8XCg6Jngr5PMjkyYpQ6CM9S5GPk0Nl6RI1zctGuz6BOEssDuOmke5Q93UGb78EHYKoQ==",
2176
2176
  "license": "LGPL-2.1",
2177
2177
  "funding": {
2178
2178
  "url": "https://github.com/DRincs-Productions/pixi-vn?sponsor=1"
@@ -14,7 +14,7 @@ import { useQuit } from "@/lib/hooks/quit-hooks";
14
14
  import { useQueryLastSave } from "@/lib/query/save-query";
15
15
  import { InterfaceSettings } from "@/lib/stores/interface-settings-store";
16
16
  import { cn } from "@/lib/utils";
17
- import { loadSave } from "@/lib/utils/save-utility";
17
+ import { loadRefreshSave, loadSave } from "@/lib/utils/save-utility";
18
18
  import { canvas, Game, ImageSprite } from "@drincs/pixi-vn";
19
19
  import { useHotkeys } from "@tanstack/react-hotkeys";
20
20
  import { useQueryClient } from "@tanstack/react-query";
@@ -240,7 +240,7 @@ export function ContinueMenuButton({
240
240
  if (!lastSave) return;
241
241
  setLoading(true);
242
242
  onLoadingChange?.(true);
243
- loadSave(lastSave)
243
+ (hasRefreshSave ? loadRefreshSave() : loadSave(lastSave))
244
244
  .then(() =>
245
245
  queryClient.invalidateQueries({
246
246
  queryKey: [INTERFACE_DATA_USE_QUERY_KEY],
@@ -254,7 +254,7 @@ export function ContinueMenuButton({
254
254
  setLoading(false);
255
255
  onLoadingChange?.(false);
256
256
  });
257
- }, [lastSave, queryClient, t, onLoadingChange]);
257
+ }, [lastSave, hasRefreshSave, queryClient, t, onLoadingChange]);
258
258
 
259
259
  const isDisabled = (!isLoading && !lastSave) || loading || disabled;
260
260
 
@@ -100,26 +100,89 @@ jobs:
100
100
  with open(manifest, 'w') as f:
101
101
  f.write(content)
102
102
 
103
- # Tauri may generate styles.xml or themes.xml depending on the version
103
+ # Tauri may generate styles.xml or themes.xml, and both a light (values/)
104
+ # and dark (values-night/) variant — patch every variant we find so the
105
+ # fullscreen flag doesn't silently no-op when the device is in dark mode.
104
106
  styles_candidates = (
105
- glob.glob('src-tauri/gen/android/app/src/main/res/values/styles.xml') +
106
- glob.glob('src-tauri/gen/android/app/src/main/res/values/themes.xml')
107
+ glob.glob('src-tauri/gen/android/app/src/main/res/values*/styles.xml') +
108
+ glob.glob('src-tauri/gen/android/app/src/main/res/values*/themes.xml')
107
109
  )
108
110
  if not styles_candidates:
109
111
  print('Warning: no styles.xml/themes.xml found, skipping fullscreen patch')
110
112
  else:
111
- styles = styles_candidates[0]
112
- with open(styles) as f:
113
+ for styles in styles_candidates:
114
+ with open(styles) as f:
115
+ content = f.read()
116
+ # Hide status bar (fullscreen) — kept as a best-effort theme hint,
117
+ # but on targetSdk 35+ (edge-to-edge is enforced by the OS) this
118
+ # alone has no effect; the real fix is the MainActivity.kt patch below.
119
+ content = content.replace(
120
+ '</style>',
121
+ ' <item name="android:windowFullscreen">true</item>\n </style>',
122
+ 1
123
+ )
124
+ with open(styles, 'w') as f:
125
+ f.write(content)
126
+ print(f'Patched {styles}')
127
+
128
+ # Since this project's Android template targets SDK 35+, edge-to-edge display
129
+ # is enforced by the OS and the theme-based windowFullscreen flag above no
130
+ # longer hides the status bar (it's always drawn, transparent, over the
131
+ # content). The only reliable way to hide it is to hide the system bars at
132
+ # runtime via WindowInsetsControllerCompat in MainActivity.kt.
133
+ activity_candidates = glob.glob(
134
+ 'src-tauri/gen/android/app/src/main/**/MainActivity.kt', recursive=True
135
+ )
136
+ if not activity_candidates:
137
+ print('WARNING: MainActivity.kt not found — status bar hiding NOT patched')
138
+ else:
139
+ activity = activity_candidates[0]
140
+ with open(activity) as f:
113
141
  content = f.read()
114
- # Hide status bar (fullscreen)
115
- content = content.replace(
116
- '</style>',
117
- ' <item name="android:windowFullscreen">true</item>\n </style>',
118
- 1
119
- )
120
- with open(styles, 'w') as f:
142
+
143
+ new_imports = [
144
+ 'androidx.core.view.WindowCompat',
145
+ 'androidx.core.view.WindowInsetsCompat',
146
+ 'androidx.core.view.WindowInsetsControllerCompat',
147
+ ]
148
+ import_lines = list(re.finditer(r'^import .+$', content, re.MULTILINE))
149
+ if import_lines:
150
+ insert_at = import_lines[-1].end()
151
+ addition = ''.join(
152
+ f'\nimport {imp}' for imp in new_imports if imp not in content
153
+ )
154
+ content = content[:insert_at] + addition + content[insert_at:]
155
+
156
+ if 'hideSystemBars' not in content:
157
+ content = content.replace(
158
+ 'super.onCreate(savedInstanceState)',
159
+ 'super.onCreate(savedInstanceState)\n hideSystemBars()',
160
+ 1
161
+ )
162
+ content = re.sub(
163
+ r'\n}\s*$',
164
+ '\n\n'
165
+ ' override fun onWindowFocusChanged(hasFocus: Boolean) {\n'
166
+ ' super.onWindowFocusChanged(hasFocus)\n'
167
+ ' if (hasFocus) {\n'
168
+ ' hideSystemBars()\n'
169
+ ' }\n'
170
+ ' }\n'
171
+ '\n'
172
+ ' private fun hideSystemBars() {\n'
173
+ ' WindowCompat.setDecorFitsSystemWindows(window, false)\n'
174
+ ' val controller = WindowInsetsControllerCompat(window, window.decorView)\n'
175
+ ' controller.hide(WindowInsetsCompat.Type.systemBars())\n'
176
+ ' controller.systemBarsBehavior =\n'
177
+ ' WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE\n'
178
+ ' }\n'
179
+ '}\n',
180
+ content,
181
+ count=1
182
+ )
183
+ with open(activity, 'w') as f:
121
184
  f.write(content)
122
- print(f'Patched {styles}')
185
+ print(f'Patched {activity} (hide system bars at runtime)')
123
186
  PYEOF
124
187
 
125
188
  - name: build Android app
@@ -14,7 +14,7 @@ import { useQuit } from "@/lib/hooks/quit-hooks";
14
14
  import { useQueryLastSave } from "@/lib/query/save-query";
15
15
  import { InterfaceSettings } from "@/lib/stores/interface-settings-store";
16
16
  import { cn } from "@/lib/utils";
17
- import { loadSave } from "@/lib/utils/save-utility";
17
+ import { loadRefreshSave, loadSave } from "@/lib/utils/save-utility";
18
18
  import { canvas, Game, ImageSprite } from "@drincs/pixi-vn";
19
19
  import { useHotkeys } from "@tanstack/react-hotkeys";
20
20
  import { useQueryClient } from "@tanstack/react-query";
@@ -240,7 +240,7 @@ export function ContinueMenuButton({
240
240
  if (!lastSave) return;
241
241
  setLoading(true);
242
242
  onLoadingChange?.(true);
243
- loadSave(lastSave)
243
+ (hasRefreshSave ? loadRefreshSave() : loadSave(lastSave))
244
244
  .then(() =>
245
245
  queryClient.invalidateQueries({
246
246
  queryKey: [INTERFACE_DATA_USE_QUERY_KEY],
@@ -254,7 +254,7 @@ export function ContinueMenuButton({
254
254
  setLoading(false);
255
255
  onLoadingChange?.(false);
256
256
  });
257
- }, [lastSave, queryClient, t, onLoadingChange]);
257
+ }, [lastSave, hasRefreshSave, queryClient, t, onLoadingChange]);
258
258
 
259
259
  const isDisabled = (!isLoading && !lastSave) || loading || disabled;
260
260