diff --git a/src/Snap.Hutao/Snap.Hutao.Installer/Package.en-us.wxl b/src/Snap.Hutao/Snap.Hutao.Installer/Package.en-us.wxl
index 7fa02fa..968a817 100644
--- a/src/Snap.Hutao/Snap.Hutao.Installer/Package.en-us.wxl
+++ b/src/Snap.Hutao/Snap.Hutao.Installer/Package.en-us.wxl
@@ -4,5 +4,8 @@ This file contains the declaration of all the localizable strings.
+
+
+
diff --git a/src/Snap.Hutao/Snap.Hutao.Installer/Package.wxs b/src/Snap.Hutao/Snap.Hutao.Installer/Package.wxs
index 77afbb1..40f0c13 100644
--- a/src/Snap.Hutao/Snap.Hutao.Installer/Package.wxs
+++ b/src/Snap.Hutao/Snap.Hutao.Installer/Package.wxs
@@ -1,21 +1,32 @@
-
+
-
+
+
+
+
-
-
+
-
-
+
+
+
+
+
+
+
diff --git a/src/Snap.Hutao/Snap.Hutao.Installer/Package.zh-cn.wxl b/src/Snap.Hutao/Snap.Hutao.Installer/Package.zh-cn.wxl
new file mode 100644
index 0000000..ef545db
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao.Installer/Package.zh-cn.wxl
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/Snap.Hutao/Snap.Hutao.Installer/Snap.Hutao.Installer.wixproj b/src/Snap.Hutao/Snap.Hutao.Installer/Snap.Hutao.Installer.wixproj
index ee8d9b2..f388f64 100644
--- a/src/Snap.Hutao/Snap.Hutao.Installer/Snap.Hutao.Installer.wixproj
+++ b/src/Snap.Hutao/Snap.Hutao.Installer/Snap.Hutao.Installer.wixproj
@@ -4,6 +4,8 @@
x64
net10.0-windows10.0.26100.0
Release
+ zh-CN
+ zh-CN;en-US
@@ -11,7 +13,7 @@
-
+
MainAppComponents
INSTALLFOLDER
true
@@ -19,6 +21,13 @@
true
-
+
+
+
+
+
+
+
+
diff --git a/src/Snap.Hutao/Snap.Hutao/Core/HutaoRuntime.cs b/src/Snap.Hutao/Snap.Hutao/Core/HutaoRuntime.cs
index 44943b1..4b1cb93 100644
--- a/src/Snap.Hutao/Snap.Hutao/Core/HutaoRuntime.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Core/HutaoRuntime.cs
@@ -33,6 +33,8 @@ internal static class HutaoRuntime
public static WebView2Version WebView2Version { get; } = InitializeWebView2();
+ public static string WebView2UserDataDirectory { get; } = InitializeWebView2UserDataDirectory();
+
// ⚠️ 延迟初始化以避免循环依赖
private static readonly Lazy LazyIsProcessElevated = new(GetIsProcessElevated);
@@ -144,6 +146,13 @@ internal static class HutaoRuntime
return cacheDir;
}
+ private static string InitializeWebView2UserDataDirectory()
+ {
+ string directory = Path.Combine(LocalCacheDirectory, "WebView2");
+ Directory.CreateDirectory(directory);
+ return directory;
+ }
+
private static bool CheckAppNotificationEnabled()
{
try
@@ -226,4 +235,4 @@ internal static class HutaoRuntime
return new(string.Empty, SH.CoreWebView2HelperVersionUndetected, false);
}
}
-}
\ No newline at end of file
+}
diff --git a/src/Snap.Hutao/Snap.Hutao/Package.appxmanifest b/src/Snap.Hutao/Snap.Hutao/Package.appxmanifest
index e5ba27d..5a2cc27 100644
--- a/src/Snap.Hutao/Snap.Hutao/Package.appxmanifest
+++ b/src/Snap.Hutao/Snap.Hutao/Package.appxmanifest
@@ -13,7 +13,7 @@
+ Version="1.18.2.0" />
Snap Hutao
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Game/Launching/Invoker/AbstractLaunchExecutionInvoker.cs b/src/Snap.Hutao/Snap.Hutao/Service/Game/Launching/Invoker/AbstractLaunchExecutionInvoker.cs
index 7e649e9..10ebc07 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/Game/Launching/Invoker/AbstractLaunchExecutionInvoker.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/Game/Launching/Invoker/AbstractLaunchExecutionInvoker.cs
@@ -21,6 +21,8 @@ internal abstract class AbstractLaunchExecutionInvoker
private bool invoked;
protected ImmutableArray Handlers { get; init; }
+ protected virtual bool ShouldWaitForProcessExit { get => true; }
+ protected virtual bool ShouldSpinWaitGameExitAfterInvoke { get => true; }
public static bool Invoking()
{
@@ -40,7 +42,7 @@ internal abstract class AbstractLaunchExecutionInvoker
finally
{
Invokers.TryRemove(this, out _);
- if (!Invoking())
+ if (!Invoking() && ShouldSpinWaitGameExitAfterInvoke)
{
await GameLifeCycle.SpinWaitGameExitAsync(taskContext).ConfigureAwait(false);
}
@@ -132,7 +134,7 @@ internal abstract class AbstractLaunchExecutionInvoker
}
// 只有在没有启用Island且进程存在时才等待退出
- if (process is { IsRunning: true })
+ if (ShouldWaitForProcessExit && process is { IsRunning: true })
{
progress.Report(new(SH.ServiceGameLaunchPhaseWaitingProcessExit));
try
@@ -148,7 +150,7 @@ internal abstract class AbstractLaunchExecutionInvoker
return;
}
}
- else if (beforeContext.LaunchOptions.IsIslandEnabled.Value)
+ else if (ShouldWaitForProcessExit && beforeContext.LaunchOptions.IsIslandEnabled.Value)
{
progress.Report(new(SH.ServiceGameLaunchPhaseWaitingProcessExit));
await taskContext.SwitchToBackgroundAsync();
@@ -170,4 +172,4 @@ internal abstract class AbstractLaunchExecutionInvoker
}
}
}
-}
\ No newline at end of file
+}
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Game/Launching/Invoker/ConvertOnlyLaunchExecutionInvoker.cs b/src/Snap.Hutao/Snap.Hutao/Service/Game/Launching/Invoker/ConvertOnlyLaunchExecutionInvoker.cs
index 39f5200..4eb9557 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/Game/Launching/Invoker/ConvertOnlyLaunchExecutionInvoker.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/Game/Launching/Invoker/ConvertOnlyLaunchExecutionInvoker.cs
@@ -9,6 +9,9 @@ namespace Snap.Hutao.Service.Game.Launching.Invoker;
internal sealed class ConvertOnlyLaunchExecutionInvoker : AbstractLaunchExecutionInvoker
{
+ protected override bool ShouldWaitForProcessExit { get => false; }
+ protected override bool ShouldSpinWaitGameExitAfterInvoke { get => false; }
+
public ConvertOnlyLaunchExecutionInvoker()
{
Handlers =
@@ -24,4 +27,4 @@ internal sealed class ConvertOnlyLaunchExecutionInvoker : AbstractLaunchExecutio
// Since this invoker is only for conversion, we do not actually need the process.
return default;
}
-}
\ No newline at end of file
+}
diff --git a/src/Snap.Hutao/Snap.Hutao/UI/Xaml/Control/ScopedPage.cs b/src/Snap.Hutao/Snap.Hutao/UI/Xaml/Control/ScopedPage.cs
index bf1ec1b..0fe8470 100644
--- a/src/Snap.Hutao/Snap.Hutao/UI/Xaml/Control/ScopedPage.cs
+++ b/src/Snap.Hutao/Snap.Hutao/UI/Xaml/Control/ScopedPage.cs
@@ -20,6 +20,8 @@ internal partial class ScopedPage : Page
protected ScopedPage()
{
+ // Allow a small set of recent pages to be cached to reduce navigation stutter.
+ NavigationCacheMode = NavigationCacheMode.Enabled;
// Events/Override Methods order
// ----------------------------------------------------------------------
// Page Navigation methods:
@@ -103,6 +105,13 @@ internal partial class ScopedPage : Page
private void OnUnloaded(object sender, RoutedEventArgs e)
{
+ // When navigation cache is enabled, the page instance is reused.
+ // Do not tear down DataContext/scope here to avoid invalid state on return.
+ if (NavigationCacheMode != NavigationCacheMode.Disabled)
+ {
+ return;
+ }
+
// Cancel all tasks executed by the view model
viewCts.Cancel();
@@ -140,4 +149,4 @@ internal partial class ScopedPage : Page
Unloaded -= OnUnloaded;
}
-}
\ No newline at end of file
+}
diff --git a/src/Snap.Hutao/Snap.Hutao/UI/Xaml/View/MainView.xaml b/src/Snap.Hutao/Snap.Hutao/UI/Xaml/View/MainView.xaml
index 2ab72fb..d007f5a 100644
--- a/src/Snap.Hutao/Snap.Hutao/UI/Xaml/View/MainView.xaml
+++ b/src/Snap.Hutao/Snap.Hutao/UI/Xaml/View/MainView.xaml
@@ -264,10 +264,13 @@
-
+
-
\ No newline at end of file
+
diff --git a/src/Snap.Hutao/Snap.Hutao/UI/Xaml/View/Window/WebView2/CompactWebView2Window.xaml.cs b/src/Snap.Hutao/Snap.Hutao/UI/Xaml/View/Window/WebView2/CompactWebView2Window.xaml.cs
index 67ddaa4..82552e2 100644
--- a/src/Snap.Hutao/Snap.Hutao/UI/Xaml/View/Window/WebView2/CompactWebView2Window.xaml.cs
+++ b/src/Snap.Hutao/Snap.Hutao/UI/Xaml/View/Window/WebView2/CompactWebView2Window.xaml.cs
@@ -6,6 +6,7 @@ using Microsoft.UI.Windowing;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Input;
using Microsoft.Web.WebView2.Core;
+using Snap.Hutao.Core;
using Snap.Hutao.Core.Logging;
using Snap.Hutao.Core.Setting;
using Snap.Hutao.UI.Input.LowLevel;
@@ -337,7 +338,8 @@ internal sealed partial class CompactWebView2Window : Microsoft.UI.Xaml.Window,
{
AdditionalBrowserArguments = "--do-not-de-elevate --autoplay-policy=no-user-gesture-required",
};
- CoreWebView2Environment environment = await CoreWebView2Environment.CreateWithOptionsAsync(null, null, options);
+ string userDataFolder = HutaoRuntime.WebView2UserDataDirectory;
+ CoreWebView2Environment environment = await CoreWebView2Environment.CreateWithOptionsAsync(null, userDataFolder, options);
await WebView.EnsureCoreWebView2Async(environment);
}
catch (SEHException ex)
@@ -430,4 +432,4 @@ internal sealed partial class CompactWebView2Window : Microsoft.UI.Xaml.Window,
RefreshButton.Command = RefreshCommand;
ProgressRing.Visibility = Visibility.Collapsed;
}
-}
\ No newline at end of file
+}
diff --git a/src/Snap.Hutao/Snap.Hutao/UI/Xaml/View/Window/WebView2/WebView2Window.xaml.cs b/src/Snap.Hutao/Snap.Hutao/UI/Xaml/View/Window/WebView2/WebView2Window.xaml.cs
index 211f179..d0ab950 100644
--- a/src/Snap.Hutao/Snap.Hutao/UI/Xaml/View/Window/WebView2/WebView2Window.xaml.cs
+++ b/src/Snap.Hutao/Snap.Hutao/UI/Xaml/View/Window/WebView2/WebView2Window.xaml.cs
@@ -5,6 +5,7 @@ using Microsoft.UI;
using Microsoft.UI.Windowing;
using Microsoft.UI.Xaml;
using Microsoft.Web.WebView2.Core;
+using Snap.Hutao.Core;
using Snap.Hutao.Core.Logging;
using Snap.Hutao.UI.Windowing;
using Snap.Hutao.UI.Windowing.Abstraction;
@@ -154,7 +155,8 @@ internal sealed partial class WebView2Window : Microsoft.UI.Xaml.Window,
{
AdditionalBrowserArguments = "--do-not-de-elevate",
};
- CoreWebView2Environment environment = await CoreWebView2Environment.CreateWithOptionsAsync(null, null, options);
+ string userDataFolder = HutaoRuntime.WebView2UserDataDirectory;
+ CoreWebView2Environment environment = await CoreWebView2Environment.CreateWithOptionsAsync(null, userDataFolder, options);
await WebView.EnsureCoreWebView2Async(environment);
}
catch (SEHException)
@@ -213,4 +215,4 @@ internal sealed partial class WebView2Window : Microsoft.UI.Xaml.Window,
{
contentProvider.ActualTheme = sender.ActualTheme;
}
-}
\ No newline at end of file
+}