diff --git a/BetterGenshinImpact/App.xaml b/BetterGenshinImpact/App.xaml
index 3aedc8a1..78242db4 100644
--- a/BetterGenshinImpact/App.xaml
+++ b/BetterGenshinImpact/App.xaml
@@ -11,7 +11,9 @@
+
+
/Assets/Fonts/MiSans-Regular.ttf#MiSans
/Assets/Fonts/deluge-led.ttf#Deluge LED
diff --git a/BetterGenshinImpact/BetterGenshinImpact.csproj b/BetterGenshinImpact/BetterGenshinImpact.csproj
index ce9ed196..0988c397 100644
--- a/BetterGenshinImpact/BetterGenshinImpact.csproj
+++ b/BetterGenshinImpact/BetterGenshinImpact.csproj
@@ -48,6 +48,7 @@
+
diff --git a/BetterGenshinImpact/Core/Script/ScriptRepoUpdater.cs b/BetterGenshinImpact/Core/Script/ScriptRepoUpdater.cs
index 1add05b0..3cbf7654 100644
--- a/BetterGenshinImpact/Core/Script/ScriptRepoUpdater.cs
+++ b/BetterGenshinImpact/Core/Script/ScriptRepoUpdater.cs
@@ -17,6 +17,8 @@ using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using System.Windows;
+using BetterGenshinImpact.View.Windows;
+using LibGit2Sharp;
using Vanara.PInvoke;
using Wpf.Ui.Violeta.Controls;
@@ -34,15 +36,15 @@ public class ScriptRepoUpdater : Singleton
// 仓储临时目录 用于下载与解压
public static readonly string ReposTempPath = Path.Combine(ReposPath, "Temp");
- // 中央仓库信息地址
- public static readonly List CenterRepoInfoUrls =
- [
- "https://raw.githubusercontent.com/babalae/bettergi-scripts-list/refs/heads/main/repo.json",
- "https://r2-script.bettergi.com/github_mirror/repo.json",
- ];
+ // // 中央仓库信息地址
+ // public static readonly List CenterRepoInfoUrls =
+ // [
+ // "https://raw.githubusercontent.com/babalae/bettergi-scripts-list/refs/heads/main/repo.json",
+ // "https://r2-script.bettergi.com/github_mirror/repo.json",
+ // ];
// 中央仓库解压后文件夹名
- public static readonly string CenterRepoUnzipName = "bettergi-scripts-list-main";
+ public static readonly string CenterRepoUnzipName = "bettergi-scripts-list-git";
public static readonly string CenterRepoPath = Path.Combine(ReposPath, CenterRepoUnzipName);
@@ -56,103 +58,211 @@ public class ScriptRepoUpdater : Singleton
private WebpageWindow? _webWindow;
- public void AutoUpdate()
+ // [Obsolete]
+ // public void AutoUpdate()
+ // {
+ // var scriptConfig = TaskContext.Instance().Config.ScriptConfig;
+ //
+ // if (!Directory.Exists(ReposPath))
+ // {
+ // Directory.CreateDirectory(ReposPath);
+ // }
+ //
+ // // 判断更新周期是否到达
+ // if (DateTime.Now - scriptConfig.LastUpdateScriptRepoTime >=
+ // TimeSpan.FromDays(scriptConfig.AutoUpdateScriptRepoPeriod))
+ // {
+ // // 更新仓库
+ // Task.Run(async () =>
+ // {
+ // try
+ // {
+ // var (repoPath, updated) = await UpdateCenterRepo();
+ // Debug.WriteLine($"脚本仓库更新完成,路径:{repoPath}");
+ // scriptConfig.LastUpdateScriptRepoTime = DateTime.Now;
+ // if (updated)
+ // {
+ // scriptConfig.ScriptRepoHintDotVisible = true;
+ // }
+ // }
+ // catch (Exception e)
+ // {
+ // _logger.LogDebug(e, $"脚本仓库更新失败:{e.Message}");
+ // }
+ // });
+ // }
+ // }
+
+
+ public async Task<(string, bool)> UpdateCenterRepoByGit(string repoUrl)
{
- var scriptConfig = TaskContext.Instance().Config.ScriptConfig;
-
- if (!Directory.Exists(ReposPath))
+ if (string.IsNullOrEmpty(repoUrl))
{
- Directory.CreateDirectory(ReposPath);
+ throw new ArgumentException("仓库URL不能为空", nameof(repoUrl));
}
- // 判断更新周期是否到达
- if (DateTime.Now - scriptConfig.LastUpdateScriptRepoTime >= TimeSpan.FromDays(scriptConfig.AutoUpdateScriptRepoPeriod))
- {
- // 更新仓库
- Task.Run(async () =>
- {
- try
- {
- var (repoPath, updated) = await UpdateCenterRepo();
- Debug.WriteLine($"脚本仓库更新完成,路径:{repoPath}");
- scriptConfig.LastUpdateScriptRepoTime = DateTime.Now;
- if (updated)
- {
- scriptConfig.ScriptRepoHintDotVisible = true;
- }
- }
- catch (Exception e)
- {
- _logger.LogDebug(e, $"脚本仓库更新失败:{e.Message}");
- }
- });
- }
- }
-
- public async Task<(string, bool)> UpdateCenterRepo()
- {
- // 测速并获取信息
- var (fastUrl, jsonString) = await ProxySpeedTester.GetFastestUrlAsync(CenterRepoInfoUrls);
- if (string.IsNullOrEmpty(jsonString))
- {
- throw new Exception("从互联网下载最新的仓库信息失败");
- }
-
- var (time, url, file) = ParseJson(jsonString);
-
+ var repoPath = Path.Combine(ReposPath, "bettergi-scripts-list-git");
var updated = false;
- // 检查仓库是否存在,不存在则下载
- var needDownload = false;
- if (Directory.Exists(CenterRepoPath))
+ await Task.Run(() =>
{
- var p = Directory.GetFiles(CenterRepoPath, "repo.json", SearchOption.AllDirectories).FirstOrDefault();
- if (p is null)
+ try
{
- needDownload = true;
+ if (!Directory.Exists(repoPath))
+ {
+ // 如果仓库不存在,执行浅克隆操作
+ _logger.LogInformation($"浅克隆仓库: {repoUrl} 到 {repoPath}");
+
+ // 使用浅克隆选项
+ var options = new CloneOptions
+ {
+ Checkout = true,
+ IsBare = false,
+ RecurseSubmodules = false, // 不递归克隆子模块
+ };
+ options.FetchOptions.Depth = 1; // 浅克隆,只获取最新的提交
+ // 克隆仓库
+ Repository.Clone(repoUrl, repoPath, options);
+ updated = true;
+ }
+ else
+ {
+ // 仓库已经存在,执行拉取更新
+ using var repo = new Repository(repoPath);
+
+ // 检查远程URL是否需要更新
+ var origin = repo.Network.Remotes["origin"];
+ if (origin.Url != repoUrl)
+ {
+ // 远程URL已更改,需要更新
+ _logger.LogInformation($"更新远程URL: 从 {origin.Url} 到 {repoUrl}");
+ repo.Network.Remotes.Update("origin", r => r.Url = repoUrl);
+ }
+
+ // 获取远程分支信息
+ var remote = repo.Network.Remotes["origin"];
+ var refSpecs = remote.FetchRefSpecs.Select(x => x.Specification);
+
+ // 使用浅拉取选项
+ // var fetchOptions = new FetchOptions
+ // {
+ // Depth = 1 // 浅拉取,只获取最新的提交
+ // };
+
+ Commands.Fetch(repo, remote.Name, refSpecs, null, "拉取最新更新");
+
+ // 获取当前分支
+ var branch = repo.Branches["main"] ?? repo.Branches["master"];
+ if (branch == null)
+ {
+ throw new Exception("未找到main或master分支");
+ }
+
+ // 如果是本地分支,需要设置上游分支
+ if (!branch.IsRemote)
+ {
+ var trackingBranch = repo.Branches[$"origin/{branch.FriendlyName}"];
+ if (trackingBranch != null && branch.TrackedBranch == null)
+ {
+ branch = repo.Branches.Update(branch,
+ b => b.TrackedBranch = trackingBranch.CanonicalName);
+ }
+ }
+
+ // 检查是否有更新
+ var currentCommitSha = repo.Head.Tip.Sha;
+
+ // 合并或重置到最新
+ if (branch.TrackedBranch != null)
+ {
+ var trackingBranch = branch.TrackedBranch;
+ var mergeResult = Commands.Pull(
+ repo,
+ new Signature("BetterGI", "auto@bettergi.com", DateTimeOffset.Now),
+ new PullOptions());
+
+ // 检查是否有更新
+ updated = currentCommitSha != repo.Head.Tip.Sha;
+ }
+ }
}
- }
- else
- {
- needDownload = true;
- }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "Git仓库更新失败");
+ throw;
+ }
+ });
- if (needDownload)
- {
- await DownloadRepoAndUnzip(url);
- updated = true;
- }
-
- // 搜索本地的 repo.json
- var localRepoJsonPath = Directory.GetFiles(CenterRepoPath, file, SearchOption.AllDirectories).FirstOrDefault();
- if (localRepoJsonPath is null)
- {
- throw new Exception("本地仓库缺少 repo.json");
- }
-
- var (time2, url2, file2) = ParseJson(await File.ReadAllTextAsync(localRepoJsonPath));
-
- // 检查是否需要更新
- if (long.Parse(time) > long.Parse(time2))
- {
- await DownloadRepoAndUnzip(url2);
- updated = true;
- }
-
- // 获取与 localRepoJsonPath 同名(无扩展名)的文件夹路径
- var folderName = Path.GetFileNameWithoutExtension(localRepoJsonPath);
- var folderPath = Path.Combine(Path.GetDirectoryName(localRepoJsonPath)!, folderName);
- if (!Directory.Exists(folderPath))
- {
- throw new Exception("本地仓库文件夹不存在");
- }
-
- return (folderPath, updated);
+ return (repoPath, updated);
}
+
+
+ // [Obsolete]
+ // public async Task<(string, bool)> UpdateCenterRepo()
+ // {
+ // // 测速并获取信息
+ // var (fastUrl, jsonString) = await ProxySpeedTester.GetFastestUrlAsync(CenterRepoInfoUrls);
+ // if (string.IsNullOrEmpty(jsonString))
+ // {
+ // throw new Exception("从互联网下载最新的仓库信息失败");
+ // }
+ //
+ // var (time, url, file) = ParseJson(jsonString);
+ //
+ // var updated = false;
+ //
+ // // 检查仓库是否存在,不存在则下载
+ // var needDownload = false;
+ // if (Directory.Exists(CenterRepoPath))
+ // {
+ // var p = Directory.GetFiles(CenterRepoPath, "repo.json", SearchOption.AllDirectories).FirstOrDefault();
+ // if (p is null)
+ // {
+ // needDownload = true;
+ // }
+ // }
+ // else
+ // {
+ // needDownload = true;
+ // }
+ //
+ // if (needDownload)
+ // {
+ // await DownloadRepoAndUnzip(url);
+ // updated = true;
+ // }
+ //
+ // // 搜索本地的 repo.json
+ // var localRepoJsonPath = Directory.GetFiles(CenterRepoPath, file, SearchOption.AllDirectories).FirstOrDefault();
+ // if (localRepoJsonPath is null)
+ // {
+ // throw new Exception("本地仓库缺少 repo.json");
+ // }
+ //
+ // var (time2, url2, file2) = ParseJson(await File.ReadAllTextAsync(localRepoJsonPath));
+ //
+ // // 检查是否需要更新
+ // if (long.Parse(time) > long.Parse(time2))
+ // {
+ // await DownloadRepoAndUnzip(url2);
+ // updated = true;
+ // }
+ //
+ // // 获取与 localRepoJsonPath 同名(无扩展名)的文件夹路径
+ // var folderName = Path.GetFileNameWithoutExtension(localRepoJsonPath);
+ // var folderPath = Path.Combine(Path.GetDirectoryName(localRepoJsonPath)!, folderName);
+ // if (!Directory.Exists(folderPath))
+ // {
+ // throw new Exception("本地仓库文件夹不存在");
+ // }
+ //
+ // return (folderPath, updated);
+ // }
public string FindCenterRepoPath()
{
- var localRepoJsonPath = Directory.GetFiles(CenterRepoPath, "repo.json", SearchOption.AllDirectories).FirstOrDefault();
+ var localRepoJsonPath = Directory.GetFiles(CenterRepoPath, "repo.json", SearchOption.AllDirectories)
+ .FirstOrDefault();
if (localRepoJsonPath is null)
{
throw new Exception("本地仓库缺少 repo.json");
@@ -197,7 +307,9 @@ public class ScriptRepoUpdater : Singleton
// 获取文件名
var contentDisposition = res.Content.Headers.ContentDisposition;
- var fileName = contentDisposition is { FileName: not null } ? contentDisposition.FileName.Trim('"') : "temp.zip";
+ var fileName = contentDisposition is { FileName: not null }
+ ? contentDisposition.FileName.Trim('"')
+ : "temp.zip";
// 创建临时目录
if (!Directory.Exists(ReposTempPath))
@@ -250,7 +362,8 @@ public class ScriptRepoUpdater : Singleton
var uiMessageBox = new Wpf.Ui.Controls.MessageBox
{
Title = "脚本订阅",
- Content = $"检测到{(formClipboard ? "剪切板上存在" : "")}脚本订阅链接,解析后需要导入的脚本为:{pathJson}。\n是否导入并覆盖此文件或者文件夹下的脚本?",
+ Content =
+ $"检测到{(formClipboard ? "剪切板上存在" : "")}脚本订阅链接,解析后需要导入的脚本为:{pathJson}。\n是否导入并覆盖此文件或者文件夹下的脚本?",
CloseButtonText = "关闭",
PrimaryButtonText = "确认导入",
Owner = Application.Current.MainWindow,
@@ -322,28 +435,13 @@ public class ScriptRepoUpdater : Singleton
scriptConfig.SubscribedScriptPaths.AddRange(paths);
Toast.Information("获取最新仓库信息中...");
-
- // 更新仓库
+
string repoPath;
try
{
- (repoPath, _) = await Task.Run(UpdateCenterRepo);
+ repoPath = FindCenterRepoPath();
}
- catch (Exception e)
- {
- Toast.Warning("获取最新仓库信息失败,尝试使用本地已有仓库信息");
- try
- {
- repoPath = FindCenterRepoPath();
- }
- catch
- {
- await MessageBox.ErrorAsync("本地无仓库信息,请至少成功更新一次脚本仓库信息!");
- return;
- }
- }
-
- if (string.IsNullOrEmpty(repoPath))
+ catch
{
await MessageBox.ErrorAsync("本地无仓库信息,请至少成功更新一次脚本仓库信息!");
return;
@@ -474,10 +572,13 @@ public class ScriptRepoUpdater : Singleton
}
// 使用路径分隔符分割路径
- string[] parts = path.Split(new char[] { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar }, StringSplitOptions.RemoveEmptyEntries);
+ string[] parts = path.Split(new char[] { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar },
+ StringSplitOptions.RemoveEmptyEntries);
// 返回第一个文件夹和剩余路径
- return parts.Length > 0 ? (parts[0], string.Join(Path.DirectorySeparatorChar, parts.Skip(1))) : (string.Empty, string.Empty);
+ return parts.Length > 0
+ ? (parts[0], string.Join(Path.DirectorySeparatorChar, parts.Skip(1)))
+ : (string.Empty, string.Empty);
}
public void OpenLocalRepoInWebView()
@@ -493,7 +594,8 @@ public class ScriptRepoUpdater : Singleton
_webWindow.Closed += (s, e) => _webWindow = null;
_webWindow.Panel!.DownloadFolderPath = MapPathingViewModel.PathJsonPath;
_webWindow.NavigateToFile(Global.Absolute(@"Assets\Web\ScriptRepo\index.html"));
- _webWindow.Panel!.OnWebViewInitializedAction = () => _webWindow.Panel!.WebView.CoreWebView2.AddHostObjectToScript("repoWebBridge", new RepoWebBridge());
+ _webWindow.Panel!.OnWebViewInitializedAction = () =>
+ _webWindow.Panel!.WebView.CoreWebView2.AddHostObjectToScript("repoWebBridge", new RepoWebBridge());
_webWindow.Show();
}
else
@@ -502,6 +604,12 @@ public class ScriptRepoUpdater : Singleton
}
}
+ public void OpenScriptRepoWindow()
+ {
+ var scriptRepoWindow = new ScriptRepoWindow { Owner = Application.Current.MainWindow };
+ scriptRepoWindow.ShowDialog();
+ }
+
///
/// 处理带有 icon.ico 和 desktop.ini 的文件夹
///
diff --git a/BetterGenshinImpact/Core/Script/WebView/RepoWebBridge.cs b/BetterGenshinImpact/Core/Script/WebView/RepoWebBridge.cs
index 1c211e6c..2023a7ae 100644
--- a/BetterGenshinImpact/Core/Script/WebView/RepoWebBridge.cs
+++ b/BetterGenshinImpact/Core/Script/WebView/RepoWebBridge.cs
@@ -20,15 +20,6 @@ public class RepoWebBridge
{
public async Task GetRepoJson()
{
- try
- {
- await ScriptRepoUpdater.Instance.UpdateCenterRepo();
- }
- catch (Exception e)
- {
- Toast.Warning($"更新仓库信息失败:{e.Message}");
- }
-
try
{
if (!Directory.Exists(ScriptRepoUpdater.CenterRepoPath))
diff --git a/BetterGenshinImpact/GameTask/Common/Job/SwitchPartyTask.cs b/BetterGenshinImpact/GameTask/Common/Job/SwitchPartyTask.cs
index 12faf95d..d513fa34 100644
--- a/BetterGenshinImpact/GameTask/Common/Job/SwitchPartyTask.cs
+++ b/BetterGenshinImpact/GameTask/Common/Job/SwitchPartyTask.cs
@@ -53,9 +53,9 @@ public class SwitchPartyTask
// 考虑加载时间 2s,共检查 2.5s,如果失败则抛出异常
- for (int i = 0; i < 3; i++) // 检查 3 次
+ for (int i = 0; i < 10; i++) // 检查 3 次
{
- await Delay(850, ct);
+ await Delay(600, ct);
using var raCheck = CaptureToRectArea();
if (Bv.IsInPartyViewUi(raCheck))
{
@@ -68,11 +68,6 @@ public class SwitchPartyTask
{
break; // 页面已打开,跳出循环
}
-
- if (attempt < maxAttempts)
- {
- Logger.LogWarning("尝试打开队伍配置页面失败,正在重试...");
- }
}
if (!isOpened)
diff --git a/BetterGenshinImpact/View/Controls/Drawer/CustomDrawer.cs b/BetterGenshinImpact/View/Controls/Drawer/CustomDrawer.cs
new file mode 100644
index 00000000..c3e8fac4
--- /dev/null
+++ b/BetterGenshinImpact/View/Controls/Drawer/CustomDrawer.cs
@@ -0,0 +1,410 @@
+using System;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Media;
+using System.Windows.Media.Animation;
+using System.ComponentModel;
+
+namespace BetterGenshinImpact.View.Controls.Drawer;
+
+public class CustomDrawer : ContentControl
+{
+ #region 依赖属性
+
+ public static readonly DependencyProperty IsOpenProperty =
+ DependencyProperty.Register(nameof(IsOpen), typeof(bool), typeof(CustomDrawer),
+ new PropertyMetadata(false, OnIsOpenChanged));
+
+ public static readonly DependencyProperty DrawerPositionProperty =
+ DependencyProperty.Register(nameof(DrawerPosition), typeof(DrawerPosition), typeof(CustomDrawer),
+ new PropertyMetadata(DrawerPosition.Right, OnDrawerPositionChanged));
+
+ public static readonly DependencyProperty OpenWidthProperty =
+ DependencyProperty.Register(nameof(OpenWidth), typeof(double), typeof(CustomDrawer),
+ new PropertyMetadata(400.0));
+
+ public static readonly DependencyProperty OpenHeightProperty =
+ DependencyProperty.Register(nameof(OpenHeight), typeof(double), typeof(CustomDrawer),
+ new PropertyMetadata(300.0));
+
+ public static readonly DependencyProperty AnimationDurationProperty =
+ DependencyProperty.Register(nameof(AnimationDuration), typeof(TimeSpan), typeof(CustomDrawer),
+ new PropertyMetadata(TimeSpan.FromMilliseconds(200)));
+
+ public static readonly DependencyProperty BackgroundOpacityProperty =
+ DependencyProperty.Register(nameof(BackgroundOpacity), typeof(double), typeof(CustomDrawer),
+ new PropertyMetadata(0.6));
+
+ public static readonly DependencyProperty DrawerBackgroundProperty =
+ DependencyProperty.Register(nameof(DrawerBackground), typeof(Brush), typeof(CustomDrawer),
+ new PropertyMetadata(Brushes.Black));
+
+ #endregion
+
+ #region 事件
+
+ ///
+ /// 抽屉打开后触发的事件
+ ///
+ public event EventHandler Opened;
+
+ ///
+ /// 抽屉关闭前触发的事件,可以取消关闭操作
+ ///
+ public event CancelEventHandler Closing;
+
+ ///
+ /// 引发 Opened 事件
+ ///
+ protected virtual void OnOpened()
+ {
+ Opened?.Invoke(this, EventArgs.Empty);
+ }
+
+ ///
+ /// 引发 Closing 事件
+ ///
+ /// 如果取消关闭,则返回 true;否则返回 false
+ protected virtual bool OnClosing()
+ {
+ if (Closing != null)
+ {
+ CancelEventArgs args = new CancelEventArgs();
+ Closing(this, args);
+ return args.Cancel;
+ }
+ return false;
+ }
+
+ #endregion
+
+ #region 属性
+
+ public bool IsOpen
+ {
+ get => (bool)GetValue(IsOpenProperty);
+ set => SetValue(IsOpenProperty, value);
+ }
+
+ public DrawerPosition DrawerPosition
+ {
+ get => (DrawerPosition)GetValue(DrawerPositionProperty);
+ set => SetValue(DrawerPositionProperty, value);
+ }
+
+ public double OpenWidth
+ {
+ get => (double)GetValue(OpenWidthProperty);
+ set => SetValue(OpenWidthProperty, value);
+ }
+
+ public double OpenHeight
+ {
+ get => (double)GetValue(OpenHeightProperty);
+ set => SetValue(OpenHeightProperty, value);
+ }
+
+ public TimeSpan AnimationDuration
+ {
+ get => (TimeSpan)GetValue(AnimationDurationProperty);
+ set => SetValue(AnimationDurationProperty, value);
+ }
+
+ public double BackgroundOpacity
+ {
+ get => (double)GetValue(BackgroundOpacityProperty);
+ set => SetValue(BackgroundOpacityProperty, value);
+ }
+
+ public Brush DrawerBackground
+ {
+ get => (Brush)GetValue(DrawerBackgroundProperty);
+ set => SetValue(DrawerBackgroundProperty, value);
+ }
+
+ #endregion
+
+ private Border _backgroundOverlay;
+ private Border _drawerContainer;
+ private Grid _mainGrid;
+
+ static CustomDrawer()
+ {
+ DefaultStyleKeyProperty.OverrideMetadata(typeof(CustomDrawer),
+ new FrameworkPropertyMetadata(typeof(CustomDrawer)));
+ }
+
+ public CustomDrawer()
+ {
+ this.Loaded += CustomDrawer_Loaded;
+ }
+
+ public override void OnApplyTemplate()
+ {
+ base.OnApplyTemplate();
+
+ _mainGrid = GetTemplateChild("PART_MainGrid") as Grid;
+ _backgroundOverlay = GetTemplateChild("PART_BackgroundOverlay") as Border;
+ _drawerContainer = GetTemplateChild("PART_DrawerContainer") as Border;
+
+ if (_backgroundOverlay != null)
+ {
+ _backgroundOverlay.MouseDown += BackgroundOverlay_MouseDown;
+ }
+
+ UpdateDrawerPosition();
+ UpdateOpenState(false);
+ }
+
+ private void CustomDrawer_Loaded(object sender, RoutedEventArgs e)
+ {
+ UpdateOpenState(false);
+ }
+
+ private void BackgroundOverlay_MouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
+ {
+ CloseDrawer();
+ }
+
+ private static void OnIsOpenChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ {
+ if (d is CustomDrawer drawer)
+ {
+ bool newValue = (bool)e.NewValue;
+ bool oldValue = (bool)e.OldValue;
+
+ // 如果是从打开到关闭状态,需要触发关闭前事件
+ if (oldValue && !newValue)
+ {
+ bool cancel = drawer.OnClosing();
+ if (cancel)
+ {
+ // 如果取消关闭,则恢复 IsOpen 为 true
+ drawer.IsOpen = true;
+ return;
+ }
+ }
+
+ // 更新抽屉状态(动画)
+ drawer.UpdateOpenState(true);
+
+ }
+ }
+
+ private static void OnDrawerPositionChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+ {
+ if (d is CustomDrawer drawer)
+ {
+ drawer.UpdateDrawerPosition();
+ drawer.UpdateOpenState(false);
+ }
+ }
+
+ private void UpdateDrawerPosition()
+ {
+ if (_drawerContainer == null) return;
+
+ switch (DrawerPosition)
+ {
+ case DrawerPosition.Left:
+ _drawerContainer.HorizontalAlignment = HorizontalAlignment.Left;
+ _drawerContainer.VerticalAlignment = VerticalAlignment.Stretch;
+ _drawerContainer.Width = OpenWidth;
+ _drawerContainer.Height = double.NaN;
+ break;
+ case DrawerPosition.Right:
+ _drawerContainer.HorizontalAlignment = HorizontalAlignment.Right;
+ _drawerContainer.VerticalAlignment = VerticalAlignment.Stretch;
+ _drawerContainer.Width = OpenWidth;
+ _drawerContainer.Height = double.NaN;
+ break;
+ case DrawerPosition.Top:
+ _drawerContainer.HorizontalAlignment = HorizontalAlignment.Stretch;
+ _drawerContainer.VerticalAlignment = VerticalAlignment.Top;
+ _drawerContainer.Width = double.NaN;
+ _drawerContainer.Height = OpenHeight;
+ break;
+ case DrawerPosition.Bottom:
+ _drawerContainer.HorizontalAlignment = HorizontalAlignment.Stretch;
+ _drawerContainer.VerticalAlignment = VerticalAlignment.Bottom;
+ _drawerContainer.Width = double.NaN;
+ _drawerContainer.Height = OpenHeight;
+ break;
+ }
+ }
+
+ private void UpdateOpenState(bool animate)
+ {
+ if (_drawerContainer == null || _backgroundOverlay == null) return;
+
+ _backgroundOverlay.IsHitTestVisible = IsOpen;
+
+ _drawerContainer.Opacity = 1;
+
+ if (IsOpen)
+ {
+ Visibility = Visibility.Visible;
+ }
+
+ // 每次更新状态时重新应用宽高
+ switch (DrawerPosition)
+ {
+ case DrawerPosition.Left:
+ case DrawerPosition.Right:
+ _drawerContainer.Width = OpenWidth;
+ _drawerContainer.Height = double.NaN;
+ break;
+ case DrawerPosition.Top:
+ case DrawerPosition.Bottom:
+ _drawerContainer.Width = double.NaN;
+ _drawerContainer.Height = OpenHeight;
+ break;
+ }
+
+ if (animate)
+ {
+ // 动画背景遮罩
+ DoubleAnimation backgroundAnimation = new DoubleAnimation
+ {
+ To = IsOpen ? BackgroundOpacity : 0,
+ Duration = AnimationDuration
+ };
+ _backgroundOverlay.BeginAnimation(OpacityProperty, backgroundAnimation);
+
+ // 确保RenderTransform已设置
+ if (_drawerContainer.RenderTransform == null || !(_drawerContainer.RenderTransform is TranslateTransform))
+ {
+ _drawerContainer.RenderTransform = new TranslateTransform();
+ }
+
+ TranslateTransform transform = (TranslateTransform)_drawerContainer.RenderTransform;
+
+ // 动画抽屉
+ DoubleAnimation drawerAnimation = new DoubleAnimation
+ {
+ Duration = AnimationDuration,
+ // 弹性动画效果
+ // EasingFunction = IsOpen
+ // ? new BackEase { EasingMode = EasingMode.EaseOut, Amplitude = 0.3 }
+ // : new ExponentialEase { EasingMode = EasingMode.EaseIn, Exponent = 6 }
+ };
+
+ // 如果是打开操作,在动画完成时触发Opened事件
+ if (IsOpen)
+ {
+ drawerAnimation.Completed += (s, e) => OnOpened();
+ }
+
+ switch (DrawerPosition)
+ {
+ case DrawerPosition.Left:
+ // 打开时,先设置初始位置
+ if (IsOpen)
+ {
+ transform.X = -OpenWidth;
+ }
+
+ drawerAnimation.To = IsOpen ? 0 : -OpenWidth;
+ transform.BeginAnimation(TranslateTransform.XProperty, drawerAnimation);
+ break;
+ case DrawerPosition.Right:
+ // 打开时,先设置初始位置
+ if (IsOpen)
+ {
+ transform.X = OpenWidth;
+ }
+
+ drawerAnimation.To = IsOpen ? 0 : OpenWidth;
+ transform.BeginAnimation(TranslateTransform.XProperty, drawerAnimation);
+ break;
+ case DrawerPosition.Top:
+ // 打开时,先设置初始位置
+ if (IsOpen)
+ {
+ transform.Y = -OpenHeight;
+ }
+
+ drawerAnimation.To = IsOpen ? 0 : -OpenHeight;
+ transform.BeginAnimation(TranslateTransform.YProperty, drawerAnimation);
+ break;
+ case DrawerPosition.Bottom:
+ // 打开时,先设置初始位置
+ if (IsOpen)
+ {
+ transform.Y = OpenHeight;
+ }
+
+ drawerAnimation.To = IsOpen ? 0 : OpenHeight;
+ transform.BeginAnimation(TranslateTransform.YProperty, drawerAnimation);
+ break;
+ }
+
+ if (!IsOpen)
+ {
+ drawerAnimation.Completed += (s, e) =>
+ {
+ if (!IsOpen)
+ {
+ Visibility = Visibility.Collapsed;
+ }
+ };
+ }
+ }
+ else
+ {
+ // 无动画直接设置
+ _backgroundOverlay.Opacity = IsOpen ? BackgroundOpacity : 0;
+
+ TranslateTransform transform = new TranslateTransform();
+ _drawerContainer.RenderTransform = transform;
+
+ switch (DrawerPosition)
+ {
+ case DrawerPosition.Left:
+ transform.X = IsOpen ? 0 : -OpenWidth;
+ break;
+ case DrawerPosition.Right:
+ transform.X = IsOpen ? 0 : OpenWidth;
+ break;
+ case DrawerPosition.Top:
+ transform.Y = IsOpen ? 0 : -OpenHeight;
+ break;
+ case DrawerPosition.Bottom:
+ transform.Y = IsOpen ? 0 : OpenHeight;
+ break;
+ }
+
+ Visibility = IsOpen ? Visibility.Visible : Visibility.Collapsed;
+
+ // 如果是无动画模式且正在打开,立即触发Opened事件
+ if (IsOpen)
+ {
+ OnOpened();
+ }
+ }
+ }
+
+ ///
+ /// 关闭抽屉的方法,会触发 Closing 事件并可能被取消
+ ///
+ /// 如果成功关闭返回 true,如果被取消返回 false
+ public bool CloseDrawer()
+ {
+ if (!IsOpen)
+ return true;
+
+ if (OnClosing())
+ return false;
+
+ IsOpen = false;
+ return true;
+ }
+}
+
+public enum DrawerPosition
+{
+ Left,
+ Right,
+ Top,
+ Bottom
+}
\ No newline at end of file
diff --git a/BetterGenshinImpact/View/Controls/Drawer/DrawerStyles.xaml b/BetterGenshinImpact/View/Controls/Drawer/DrawerStyles.xaml
new file mode 100644
index 00000000..947b7809
--- /dev/null
+++ b/BetterGenshinImpact/View/Controls/Drawer/DrawerStyles.xaml
@@ -0,0 +1,27 @@
+
+
+
+
\ No newline at end of file
diff --git a/BetterGenshinImpact/View/Controls/Drawer/DrawerViewModel.cs b/BetterGenshinImpact/View/Controls/Drawer/DrawerViewModel.cs
new file mode 100644
index 00000000..820e7bf8
--- /dev/null
+++ b/BetterGenshinImpact/View/Controls/Drawer/DrawerViewModel.cs
@@ -0,0 +1,71 @@
+using CommunityToolkit.Mvvm.ComponentModel;
+using CommunityToolkit.Mvvm.Input;
+using System;
+using System.ComponentModel;
+using System.Windows.Input;
+
+namespace BetterGenshinImpact.View.Controls.Drawer;
+
+public partial class DrawerViewModel : ObservableObject
+{
+ [ObservableProperty]
+ private bool _isDrawerOpen;
+
+ [ObservableProperty]
+ private object? _drawerContent;
+
+ [ObservableProperty]
+ private DrawerPosition _drawerPosition = DrawerPosition.Right;
+
+ [ObservableProperty]
+ private double _drawerWidth = 400;
+
+ [ObservableProperty]
+ private double _drawerHeight = 300;
+
+ [ObservableProperty]
+ private RelayCommand _onDrawerOpenedCommand;
+
+ [ObservableProperty]
+ private RelayCommand _onDrawerClosingCommand;
+
+ public void setDrawerOpenedAction(Action action)
+ {
+ OnDrawerOpenedCommand = new RelayCommand(action!);
+ }
+
+ public void SetDrawerClosingAction(Action action)
+ {
+ OnDrawerClosingCommand = new RelayCommand(action!);
+ }
+
+ [RelayCommand]
+ public void OpenDrawer(object content)
+ {
+ DrawerContent = content;
+ IsDrawerOpen = true;
+ }
+
+ [RelayCommand]
+ public void CloseDrawer()
+ {
+ IsDrawerOpen = false;
+ }
+
+ [RelayCommand]
+ public void ToggleDrawer(object? content = null)
+ {
+ if (IsDrawerOpen)
+ {
+ CloseDrawer();
+ }
+ else
+ {
+ if (content != null)
+ {
+ DrawerContent = content;
+ }
+ IsDrawerOpen = true;
+ }
+ }
+}
diff --git a/BetterGenshinImpact/View/Controls/Webview/WebpagePanel.cs b/BetterGenshinImpact/View/Controls/Webview/WebpagePanel.cs
index dfb50fcf..394e5556 100644
--- a/BetterGenshinImpact/View/Controls/Webview/WebpagePanel.cs
+++ b/BetterGenshinImpact/View/Controls/Webview/WebpagePanel.cs
@@ -4,11 +4,13 @@ using Microsoft.Web.WebView2.Wpf;
using System;
using System.Diagnostics;
using System.IO;
+using System.Net;
using System.Security.AccessControl;
using System.Text;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
+using BetterGenshinImpact.Helpers;
namespace BetterGenshinImpact.View.Controls.Webview;
@@ -23,6 +25,8 @@ public class WebpagePanel : UserControl
public string? DownloadFolderPath { get; set; }
public Action? OnWebViewInitializedAction { get; set; }
+
+ public Action? OnNavigationCompletedAction { get; set; }
public WebpagePanel()
{
@@ -45,10 +49,37 @@ public class WebpagePanel : UserControl
};
_webView.CoreWebView2InitializationCompleted += WebView_CoreWebView2InitializationCompleted;
_webView.NavigationStarting += NavigationStarting_CancelNavigation;
+ _webView.NavigationCompleted += WebView_NavigationCompleted;
+
Content = _webView;
}
}
-
+
+ // public WebpagePanel(WebView2 webView2)
+ // {
+ // if (!IsWebView2Available())
+ // {
+ // Content = CreateDownloadButton();
+ // }
+ // else
+ // {
+ // EnsureWebView2DataFolder();
+ // _webView = webView2;
+ // webView2.CreationProperties = new CoreWebView2CreationProperties
+ // {
+ // UserDataFolder = Path.Combine(new FileInfo(Environment.ProcessPath!).DirectoryName!, @"WebView2Data\\"),
+ // };
+ // webView2.CoreWebView2InitializationCompleted += WebView_CoreWebView2InitializationCompleted;
+ // webView2.NavigationStarting += NavigationStarting_CancelNavigation;
+ // Content = webView2;
+ // }
+ // }
+ private void WebView_NavigationCompleted(object? sender, CoreWebView2NavigationCompletedEventArgs e)
+ {
+ // 调用外部设置的导航完成 Action
+ OnNavigationCompletedAction?.Invoke(e);
+ }
+
private void WebView_CoreWebView2InitializationCompleted(object? sender, CoreWebView2InitializationCompletedEventArgs e)
{
if (e.IsSuccess)
@@ -143,6 +174,15 @@ public class WebpagePanel : UserControl
}));
}
+ public void NavigateToMd(string md, string backgroundColor = "#2b2b2b")
+ {
+ md = WebUtility.HtmlEncode(md);
+ string md2Html = ResourceHelper.GetString($"pack://application:,,,/Assets/Strings/md2html.html",
+ Encoding.UTF8);
+ var html = md2Html.Replace("{{content}}", md).Replace("#202020", backgroundColor);
+ NavigateToHtml(html);
+ }
+
private void NavigationStarting_CancelNavigation(object? sender, CoreWebView2NavigationStartingEventArgs e)
{
if (e.Uri.StartsWith("data:")) // when using NavigateToString
diff --git a/BetterGenshinImpact/View/Pages/JsListPage.xaml b/BetterGenshinImpact/View/Pages/JsListPage.xaml
index 30322c99..b8613ad5 100644
--- a/BetterGenshinImpact/View/Pages/JsListPage.xaml
+++ b/BetterGenshinImpact/View/Pages/JsListPage.xaml
@@ -7,6 +7,7 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:pages="clr-namespace:BetterGenshinImpact.ViewModel.Pages"
xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml"
+ xmlns:drawer="clr-namespace:BetterGenshinImpact.View.Controls.Drawer"
d:DataContext="{d:DesignInstance Type=pages:JsListViewModel}"
d:DesignHeight="600"
d:DesignWidth="800"
@@ -22,82 +23,93 @@
-
-
-
-
-
-
-
-
-
-
-
- 可以通过 Javascript 调用 BetterGI 在原神中的各项能力。请在调度器中使用!
- 点击查看 Javascript 脚本使用与编写教程
-
-
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
- 脚本仓库
-
-
-
-
+
+
+ 可以通过 Javascript 调用 BetterGI 在原神中的各项能力。请在调度器中使用!
+
+ 点击查看 Javascript 脚本使用与编写教程
+
+
-
+
+
+
+
+
+ 脚本仓库
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/BetterGenshinImpact/View/Pages/MapPathingPage.xaml b/BetterGenshinImpact/View/Pages/MapPathingPage.xaml
index 0f893f25..64d0e092 100644
--- a/BetterGenshinImpact/View/Pages/MapPathingPage.xaml
+++ b/BetterGenshinImpact/View/Pages/MapPathingPage.xaml
@@ -10,6 +10,7 @@
xmlns:pages="clr-namespace:BetterGenshinImpact.ViewModel.Pages"
xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml"
xmlns:vio="http://schemas.lepo.co/wpfui/2022/xaml/violeta"
+ xmlns:drawer="clr-namespace:BetterGenshinImpact.View.Controls.Drawer"
d:DataContext="{d:DesignInstance Type=pages:MapPathingViewModel}"
d:DesignHeight="600"
d:DesignWidth="800"
@@ -25,102 +26,135 @@
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
-
-
- 可以实现自动采集、自动挖矿、自动锄地等功能。请在调度器中使用!
- 点击查看地图追踪与录制使用教程
-
-
+
+
+ 可以实现自动采集、自动挖矿、自动锄地等功能。请在调度器中使用!
+
+ 点击查看地图追踪与录制使用教程
+
+
-
-
-
-
- 脚本仓库
-
+
+
+
+
+ 脚本仓库
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/BetterGenshinImpact/View/Windows/ScriptRepoWindow.xaml b/BetterGenshinImpact/View/Windows/ScriptRepoWindow.xaml
new file mode 100644
index 00000000..cb194d67
--- /dev/null
+++ b/BetterGenshinImpact/View/Windows/ScriptRepoWindow.xaml
@@ -0,0 +1,86 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/BetterGenshinImpact/View/Windows/ScriptRepoWindow.xaml.cs b/BetterGenshinImpact/View/Windows/ScriptRepoWindow.xaml.cs
new file mode 100644
index 00000000..7ae22deb
--- /dev/null
+++ b/BetterGenshinImpact/View/Windows/ScriptRepoWindow.xaml.cs
@@ -0,0 +1,126 @@
+using System;
+using System.Collections.ObjectModel;
+using System.IO;
+using System.Threading.Tasks;
+using System.Windows;
+using CommunityToolkit.Mvvm.ComponentModel;
+using CommunityToolkit.Mvvm.Input;
+using BetterGenshinImpact.Core.Config;
+using BetterGenshinImpact.Core.Script;
+using BetterGenshinImpact.GameTask;
+using BetterGenshinImpact.Helpers;
+using Wpf.Ui.Violeta.Controls;
+
+namespace BetterGenshinImpact.View.Windows;
+
+[ObservableObject]
+public partial class ScriptRepoWindow
+{
+ // 更新渠道类
+ public class RepoChannel
+ {
+ public string Name { get; set; }
+ public string Url { get; set; }
+
+ public RepoChannel(string name, string url)
+ {
+ Name = name;
+ Url = url;
+ }
+ }
+
+ // 渠道列表
+ private ObservableCollection _repoChannels;
+ public ObservableCollection RepoChannels => _repoChannels;
+
+ // 选中的渠道
+ [ObservableProperty] private RepoChannel _selectedRepoChannel;
+
+ public ScriptRepoWindow()
+ {
+ InitializeRepoChannels();
+ InitializeComponent();
+ DataContext = this;
+ }
+
+ private void InitializeRepoChannels()
+ {
+ _repoChannels = new ObservableCollection
+ {
+ new RepoChannel("CNB", "https://cnb.cool/bettergi/bettergi-scripts-list"),
+ new RepoChannel("GitCode", "https://gitcode.com/huiyadanli/bettergi-scripts-list"),
+ new RepoChannel("Gitee", "https://gitee.com/babalae/bettergi-scripts-list"),
+ new RepoChannel("GitHub", "https://github.com/babalae/bettergi-scripts-list"),
+ };
+ SelectedRepoChannel = _repoChannels[0];
+ }
+
+ [RelayCommand]
+ private async Task UpdateRepo()
+ {
+ try
+ {
+ // 使用选定渠道的URL进行更新
+ string repoUrl = SelectedRepoChannel.Url;
+
+ // 显示更新中提示
+ Toast.Information("正在更新脚本仓库...");
+
+ // 执行更新
+ var (repoPath, updated) = await ScriptRepoUpdater.Instance.UpdateCenterRepoByGit(repoUrl);
+
+ // 更新结果提示
+ if (updated)
+ {
+ Toast.Success("脚本仓库更新成功,有新内容");
+ }
+ else
+ {
+ Toast.Success("脚本仓库已是最新");
+ }
+ }
+ catch (Exception ex)
+ {
+ Toast.Error($"更新失败: {ex.Message}");
+ }
+ }
+
+ [RelayCommand]
+ private void OpenLocalScriptRepo()
+ {
+ TaskContext.Instance().Config.ScriptConfig.ScriptRepoHintDotVisible = false;
+ ScriptRepoUpdater.Instance.OpenLocalRepoInWebView();
+ Close();
+ }
+
+ [RelayCommand]
+ private async Task ResetRepo()
+ {
+ // 添加确认对话框
+ var result = await MessageBox.ShowAsync(
+ "确定要重置脚本仓库吗?无法正常更新时候可以使用本功能,重置后请重新更新脚本仓库。",
+ "确认重置",
+ MessageBoxButton.YesNo,
+ MessageBoxImage.Warning);
+
+ if (result == MessageBoxResult.Yes)
+ {
+ try
+ {
+ if (Directory.Exists(ScriptRepoUpdater.CenterRepoPath))
+ {
+ DirectoryHelper.DeleteReadOnlyDirectory(ScriptRepoUpdater.CenterRepoPath);
+ Toast.Success("脚本仓库已重置,请重新更新脚本仓库。");
+ }
+ else
+ {
+ Toast.Information("脚本仓库不存在,无需重置");
+ }
+ }
+ catch (Exception ex)
+ {
+ Toast.Error($"重置失败: {ex.Message}");
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/BetterGenshinImpact/ViewModel/MainWindowViewModel.cs b/BetterGenshinImpact/ViewModel/MainWindowViewModel.cs
index 1e139318..21d7d4ca 100644
--- a/BetterGenshinImpact/ViewModel/MainWindowViewModel.cs
+++ b/BetterGenshinImpact/ViewModel/MainWindowViewModel.cs
@@ -149,7 +149,7 @@ public partial class MainWindowViewModel : ObservableObject, IViewModel
}
// 更新仓库
- ScriptRepoUpdater.Instance.AutoUpdate();
+ // ScriptRepoUpdater.Instance.AutoUpdate();
// 清理临时目录
TempManager.CleanUp();
diff --git a/BetterGenshinImpact/ViewModel/Pages/JsListViewModel.cs b/BetterGenshinImpact/ViewModel/Pages/JsListViewModel.cs
index c5528bb6..889b6417 100644
--- a/BetterGenshinImpact/ViewModel/Pages/JsListViewModel.cs
+++ b/BetterGenshinImpact/ViewModel/Pages/JsListViewModel.cs
@@ -9,15 +9,25 @@ using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
+using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Media;
using BetterGenshinImpact.Helpers;
+using BetterGenshinImpact.View.Controls.Drawer;
+using BetterGenshinImpact.View.Controls.Webview;
using BetterGenshinImpact.ViewModel.Message;
using CommunityToolkit.Mvvm.Messaging;
+using Microsoft.Web.WebView2.Wpf;
using Wpf.Ui;
-using Wpf.Ui.Controls;
using Wpf.Ui.Violeta.Controls;
+using Button = Wpf.Ui.Controls.Button;
+using StackPanel = Wpf.Ui.Controls.StackPanel;
+using TextBlock = Wpf.Ui.Controls.TextBlock;
+using TextBox = Wpf.Ui.Controls.TextBox;
namespace BetterGenshinImpact.ViewModel.Pages;
@@ -26,13 +36,21 @@ public partial class JsListViewModel : ViewModel
private readonly ILogger _logger = App.GetLogger();
private readonly string scriptPath = Global.ScriptPath();
- [ObservableProperty]
- private ObservableCollection _scriptItems = [];
+ [ObservableProperty] private ObservableCollection _scriptItems = [];
private readonly IScriptService _scriptService;
public AllConfig Config { get; set; }
+ public DrawerViewModel DrawerVm { get; } = new DrawerViewModel();
+
+ private WebView2? _webView2;
+
+ private WebpagePanel? _mdWebpagePanel;
+
+ private TaskCompletionSource? _navigationCompletionSource;
+ private const int NavigationTimeoutMs = 10000; // 10秒超时
+
public JsListViewModel(IScriptService scriptService, IConfigService configService)
{
_scriptService = scriptService;
@@ -114,13 +132,214 @@ public partial class JsListViewModel : ViewModel
[RelayCommand]
public void OnGoToJsScriptUrl()
{
- Process.Start(new ProcessStartInfo("https://bettergi.com/feats/autos/jsscript.html") { UseShellExecute = true });
+ Process.Start(new ProcessStartInfo("https://bettergi.com/feats/autos/jsscript.html")
+ { UseShellExecute = true });
}
[RelayCommand]
public void OnOpenLocalScriptRepo()
{
Config.ScriptConfig.ScriptRepoHintDotVisible = false;
- ScriptRepoUpdater.Instance.OpenLocalRepoInWebView();
+ ScriptRepoUpdater.Instance.OpenScriptRepoWindow();
}
-}
+
+ [RelayCommand]
+ private void OpenScriptDetailDrawer(object? scriptItem)
+ {
+ if (scriptItem == null)
+ {
+ return;
+ }
+
+ if (scriptItem is ScriptProject scriptProject)
+ {
+ // 检查是否存在README.md或其他md文件
+ var mdFilePath = FindMdFilePath(scriptProject);
+
+ // 设置抽屉位置和大小
+ DrawerVm.DrawerPosition = DrawerPosition.Right;
+
+ if (!string.IsNullOrEmpty(mdFilePath))
+ {
+ DrawerVm.DrawerWidth = 450;
+ // 注册抽屉关闭前事件
+ DrawerVm.SetDrawerClosingAction(args =>
+ {
+ if (_mdWebpagePanel != null)
+ {
+ _mdWebpagePanel.Visibility = Visibility.Hidden;
+ }
+ });
+ DrawerVm.setDrawerOpenedAction(async () =>
+ {
+ if (_mdWebpagePanel != null)
+ {
+ // 等待导航完成或超时
+ try
+ {
+ await WaitForNavigationCompletedWithTimeout();
+ _mdWebpagePanel.Visibility = Visibility.Visible;
+ _mdWebpagePanel.WebView.Focus();
+ Debug.WriteLine("Navigation completed successfully");
+ // 导航成功完成后执行其他操作
+ }
+ catch (TimeoutException)
+ {
+ Toast.Error("Markdown内容加载超时");
+ }
+ }
+ });
+ }
+ else
+ {
+ DrawerVm.SetDrawerClosingAction(_ => { });
+ DrawerVm.setDrawerOpenedAction(() => { });
+ DrawerVm.DrawerWidth = 300;
+ }
+
+ // 创建要在抽屉中显示的内容
+ var content = CreateScriptDetailContent(scriptProject, mdFilePath);
+
+ // 打开抽屉
+ DrawerVm.OpenDrawer(content);
+ }
+ }
+
+ private async Task WaitForNavigationCompletedWithTimeout()
+ {
+ var completedTask = await Task.WhenAny(
+ _navigationCompletionSource!.Task,
+ Task.Delay(NavigationTimeoutMs)
+ );
+
+ if (completedTask != _navigationCompletionSource.Task)
+ {
+ throw new TimeoutException("Navigation did not complete within the timeout period");
+ }
+ }
+
+ private object CreateScriptDetailContent(ScriptProject scriptProject, string? mdFilePath)
+ {
+ // 创建显示脚本详情的控件
+ var border = new Border
+ {
+ Background = new SolidColorBrush(Color.FromRgb(0x2B, 0x2B, 0x2B)),
+ Padding = new Thickness(20)
+ };
+ var panel = new StackPanel();
+ border.Child = panel;
+
+ // 假设scriptItem是你的脚本对象,根据实际类型进行调整
+ panel.Children.Add(new TextBlock
+ {
+ Text = scriptProject.Manifest.Name,
+ FontSize = 20,
+ FontWeight = FontWeights.Bold,
+ Margin = new Thickness(0, 0, 0, 10)
+ });
+
+
+ // 如果找到md文件,使用WebpagePanel显示
+ if (!string.IsNullOrEmpty(mdFilePath))
+ {
+ // 使用Grid作为容器来实现填充效果
+ var grid = new Grid
+ {
+ Margin = new Thickness(0, 0, 0, 15)
+ };
+
+ _mdWebpagePanel = new WebpagePanel
+ {
+ Margin = new Thickness(0),
+ Visibility = Visibility.Hidden,
+ VerticalAlignment = VerticalAlignment.Stretch,
+ HorizontalAlignment = HorizontalAlignment.Stretch
+ };
+
+ _navigationCompletionSource = new TaskCompletionSource();
+ _mdWebpagePanel.OnNavigationCompletedAction = (_) =>
+ {
+ // 导航完成时设置任务结果
+ _navigationCompletionSource.TrySetResult(true);
+ };
+ _mdWebpagePanel.NavigateToMd(File.ReadAllText(mdFilePath));
+
+ grid.Children.Add(_mdWebpagePanel);
+ panel.Children.Add(grid);
+
+ // 设置Grid高度以占满剩余空间
+ panel.SizeChanged += (sender, args) =>
+ {
+ // 计算其他元素使用的高度
+ double otherElementsHeight = 0;
+ foreach (var child in panel.Children)
+ {
+ if (child != grid)
+ {
+ var frameworkElement = child as FrameworkElement;
+ if (frameworkElement != null)
+ {
+ otherElementsHeight += frameworkElement.ActualHeight + frameworkElement.Margin.Top + frameworkElement.Margin.Bottom;
+ }
+ }
+ }
+
+ // 设置Grid高度为剩余空间
+ grid.Height = Math.Max(400, panel.ActualHeight - otherElementsHeight - 15); // 设置最小高度为400
+ };
+ }
+ else
+ {
+ panel.Children.Add(new TextBlock
+ {
+ Text = $"版本: {scriptProject.Manifest.Version}",
+ Margin = new Thickness(0, 5, 0, 5)
+ });
+
+ panel.Children.Add(new TextBlock
+ {
+ Text = scriptProject.Manifest.Description,
+ TextWrapping = TextWrapping.Wrap,
+ Margin = new Thickness(0, 5, 0, 15)
+ });
+ }
+
+ // 添加操作按钮
+ // var buttonPanel = new StackPanel { Orientation = Orientation.Horizontal };
+ //
+ // var runButton = new Button
+ // {
+ // Content = "执行脚本",
+ // Margin = new Thickness(0, 0, 10, 0)
+ // };
+ // runButton.Click += async (s, e) => await OnStartRun(script);
+ // buttonPanel.Children.Add(runButton);
+ //
+ // var openFolderButton = new Button { Content = "打开目录" };
+ // openFolderButton.Click += (s, e) => OnOpenScriptProjectFolder(script);
+ // buttonPanel.Children.Add(openFolderButton);
+
+ // panel.Children.Add(buttonPanel);
+
+
+ return border;
+ }
+
+ private static string? FindMdFilePath(ScriptProject script)
+ {
+ string[] possibleMdFiles = { "README.md", "readme.md" };
+ string mdFilePath = null;
+
+ foreach (var mdFile in possibleMdFiles)
+ {
+ string fullPath = Path.Combine(script.ProjectPath, mdFile);
+ if (File.Exists(fullPath))
+ {
+ mdFilePath = fullPath;
+ break;
+ }
+ }
+
+ return mdFilePath;
+ }
+}
\ No newline at end of file
diff --git a/BetterGenshinImpact/ViewModel/Pages/KeyMouseRecordPageViewModel.cs b/BetterGenshinImpact/ViewModel/Pages/KeyMouseRecordPageViewModel.cs
index 393d5ca0..23b9ea27 100644
--- a/BetterGenshinImpact/ViewModel/Pages/KeyMouseRecordPageViewModel.cs
+++ b/BetterGenshinImpact/ViewModel/Pages/KeyMouseRecordPageViewModel.cs
@@ -231,6 +231,6 @@ public partial class KeyMouseRecordPageViewModel : ViewModel
public void OnOpenLocalScriptRepo()
{
Config.ScriptConfig.ScriptRepoHintDotVisible = false;
- ScriptRepoUpdater.Instance.OpenLocalRepoInWebView();
+ ScriptRepoUpdater.Instance.OpenScriptRepoWindow();
}
}
diff --git a/BetterGenshinImpact/ViewModel/Pages/MapPathingViewModel.cs b/BetterGenshinImpact/ViewModel/Pages/MapPathingViewModel.cs
index cee4d6f6..2e9ce69e 100644
--- a/BetterGenshinImpact/ViewModel/Pages/MapPathingViewModel.cs
+++ b/BetterGenshinImpact/ViewModel/Pages/MapPathingViewModel.cs
@@ -1,4 +1,5 @@
-using BetterGenshinImpact.Core.Config;
+using System;
+using BetterGenshinImpact.Core.Config;
using BetterGenshinImpact.Core.Script.Group;
using BetterGenshinImpact.Core.Script.Project;
using BetterGenshinImpact.GameTask.AutoPathing;
@@ -21,6 +22,14 @@ using Wpf.Ui.Violeta.Controls;
using BetterGenshinImpact.View.Pages.View;
using BetterGenshinImpact.ViewModel.Pages.View;
using Wpf.Ui.Violeta.Win32;
+using BetterGenshinImpact.View.Controls.Drawer;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Media;
+using System.ComponentModel;
+using BetterGenshinImpact.View.Controls.Webview;
+using Microsoft.Web.WebView2.Wpf;
+using BetterGenshinImpact.Helpers;
namespace BetterGenshinImpact.ViewModel.Pages;
@@ -39,6 +48,15 @@ public partial class MapPathingViewModel : ViewModel
private readonly IScriptService _scriptService;
public AllConfig Config { get; set; }
+
+ // 添加抽屉ViewModel
+ public DrawerViewModel DrawerVm { get; } = new DrawerViewModel();
+
+ // 添加WebView2相关成员变量
+ private WebView2? _webView2;
+ private WebpagePanel? _mdWebpagePanel;
+ private TaskCompletionSource? _navigationCompletionSource;
+ private const int NavigationTimeoutMs = 10000; // 10秒超时
///
public MapPathingViewModel(IScriptService scriptService, IConfigService configService)
@@ -179,6 +197,222 @@ public partial class MapPathingViewModel : ViewModel
public void OnOpenLocalScriptRepo()
{
Config.ScriptConfig.ScriptRepoHintDotVisible = false;
- ScriptRepoUpdater.Instance.OpenLocalRepoInWebView();
+ ScriptRepoUpdater.Instance.OpenScriptRepoWindow();
}
-}
+
+ [RelayCommand]
+ public void OnOpenPathingDetail()
+ {
+ var item = SelectNode;
+ if (item == null)
+ {
+ return;
+ }
+
+ // 如果是目录,检查是否存在README.md
+ string? mdFilePath = null;
+ if (item.IsDirectory && !string.IsNullOrEmpty(item.FilePath))
+ {
+ mdFilePath = FindMdFilePath(item.FilePath);
+ }
+
+ // 设置抽屉位置和大小
+ DrawerVm.DrawerPosition = DrawerPosition.Right;
+
+ if (!string.IsNullOrEmpty(mdFilePath))
+ {
+ DrawerVm.DrawerWidth = 450;
+ // 注册抽屉关闭前事件
+ DrawerVm.SetDrawerClosingAction(args =>
+ {
+ if (_mdWebpagePanel != null)
+ {
+ _mdWebpagePanel.Visibility = Visibility.Hidden;
+ }
+ });
+ DrawerVm.setDrawerOpenedAction(async () =>
+ {
+ if (_mdWebpagePanel != null)
+ {
+ // 等待导航完成或超时
+ try
+ {
+ await WaitForNavigationCompletedWithTimeout();
+ _mdWebpagePanel.Visibility = Visibility.Visible;
+ _mdWebpagePanel.WebView.Focus();
+ Debug.WriteLine("Navigation completed successfully");
+ }
+ catch (TimeoutException)
+ {
+ Toast.Error("Markdown内容加载超时");
+ }
+ }
+ });
+ }
+ else
+ {
+ DrawerVm.DrawerWidth = 350;
+ DrawerVm.SetDrawerClosingAction(_ => { });
+ DrawerVm.setDrawerOpenedAction(() => { });
+ }
+
+ // 创建要在抽屉中显示的内容
+ var content = CreatePathingDetailContent(item, mdFilePath);
+
+ // 打开抽屉
+ if (content != null)
+ {
+ DrawerVm.OpenDrawer(content);
+ }
+ }
+
+ private async Task WaitForNavigationCompletedWithTimeout()
+ {
+ var completedTask = await Task.WhenAny(
+ _navigationCompletionSource!.Task,
+ Task.Delay(NavigationTimeoutMs)
+ );
+
+ if (completedTask != _navigationCompletionSource.Task)
+ {
+ throw new TimeoutException("Navigation did not complete within the timeout period");
+ }
+ }
+
+ private string? FindMdFilePath(string dirPath)
+ {
+ string[] possibleMdFiles = { "README.md", "readme.md" };
+
+ foreach (var mdFile in possibleMdFiles)
+ {
+ string fullPath = Path.Combine(dirPath, mdFile);
+ if (File.Exists(fullPath))
+ {
+ return fullPath;
+ }
+ }
+
+ return null;
+ }
+
+ private object? CreatePathingDetailContent(FileTreeNode node, string? mdFilePath = null)
+ {
+ // 创建显示路径任务详情的控件
+ var border = new Border
+ {
+ Background = new SolidColorBrush(Color.FromRgb(0x2B, 0x2B, 0x2B)),
+ Padding = new Thickness(20)
+ };
+
+ var panel = new StackPanel();
+ border.Child = panel;
+
+ // 添加标题
+ panel.Children.Add(new TextBlock
+ {
+ Text = node.FileName,
+ FontSize = 20,
+ FontWeight = FontWeights.Bold,
+ Margin = new Thickness(0, 0, 0, 10)
+ });
+
+ // 如果找到md文件,使用WebpagePanel显示
+ if (!string.IsNullOrEmpty(mdFilePath))
+ {
+ // 使用Grid作为容器来实现填充效果
+ var grid = new Grid
+ {
+ Margin = new Thickness(0, 0, 0, 15)
+ };
+
+ _mdWebpagePanel = new WebpagePanel
+ {
+ Margin = new Thickness(0),
+ Visibility = Visibility.Hidden,
+ VerticalAlignment = VerticalAlignment.Stretch,
+ HorizontalAlignment = HorizontalAlignment.Stretch
+ };
+
+ _navigationCompletionSource = new TaskCompletionSource();
+ _mdWebpagePanel.OnNavigationCompletedAction = (_) =>
+ {
+ // 导航完成时设置任务结果
+ _navigationCompletionSource.TrySetResult(true);
+ };
+ _mdWebpagePanel.NavigateToMd(File.ReadAllText(mdFilePath));
+
+ grid.Children.Add(_mdWebpagePanel);
+ panel.Children.Add(grid);
+
+ // 设置Grid高度以占满剩余空间
+ panel.SizeChanged += (sender, args) =>
+ {
+ // 计算其他元素使用的高度
+ double otherElementsHeight = 0;
+ foreach (var child in panel.Children)
+ {
+ if (child != grid)
+ {
+ var frameworkElement = child as FrameworkElement;
+ if (frameworkElement != null)
+ {
+ otherElementsHeight += frameworkElement.ActualHeight + frameworkElement.Margin.Top + frameworkElement.Margin.Bottom;
+ }
+ }
+ }
+
+ // 设置Grid高度为剩余空间
+ grid.Height = Math.Max(400, panel.ActualHeight - otherElementsHeight - 15); // 设置最小高度为400
+ };
+ }
+ else if (!node.IsDirectory && !string.IsNullOrEmpty(node.FilePath))
+ {
+ // 如果是文件而不是目录,显示更多详情
+ try
+ {
+ if (string.IsNullOrEmpty(node.Value?.Info.Description))
+ {
+ return null;
+ }
+
+ panel.Children.Add(new TextBlock
+ {
+ Text = $"{node.Value?.Info.Description}",
+ TextWrapping = TextWrapping.Wrap,
+ Margin = new Thickness(0, 5, 0, 5)
+ });
+ }
+ catch (Exception ex)
+ {
+ panel.Children.Add(new TextBlock
+ {
+ Text = $"读取文件信息时出错: {ex.Message}",
+ TextWrapping = TextWrapping.Wrap,
+ Margin = new Thickness(0, 5, 0, 5)
+ });
+ }
+ }
+ else
+ {
+ // 显示目录信息
+ panel.Children.Add(new TextBlock
+ {
+ Text = "这是一个目录,包含多个地图追踪任务。",
+ TextWrapping = TextWrapping.Wrap,
+ Margin = new Thickness(0, 5, 0, 15)
+ });
+
+ // 添加子项信息
+ if (node.Children.Count > 0)
+ {
+ panel.Children.Add(new TextBlock
+ {
+ Text = $"包含 {node.Children.Count} 个子项",
+ Margin = new Thickness(0, 5, 0, 5)
+ });
+ }
+ }
+
+ return border;
+ }
+}
\ No newline at end of file
diff --git a/BetterGenshinImpact/ViewModel/Pages/ScriptControlViewModel.cs b/BetterGenshinImpact/ViewModel/Pages/ScriptControlViewModel.cs
index 34234995..81ea51a3 100644
--- a/BetterGenshinImpact/ViewModel/Pages/ScriptControlViewModel.cs
+++ b/BetterGenshinImpact/ViewModel/Pages/ScriptControlViewModel.cs
@@ -451,7 +451,7 @@ public partial class ScriptControlViewModel : ViewModel
public void OnOpenLocalScriptRepo()
{
TaskContext.Instance().Config.ScriptConfig.ScriptRepoHintDotVisible = false;
- ScriptRepoUpdater.Instance.OpenLocalRepoInWebView();
+ ScriptRepoUpdater.Instance.OpenScriptRepoWindow();
}
[RelayCommand]
diff --git a/BetterGenshinImpact/ViewModel/Pages/View/AutoFightViewModel.cs b/BetterGenshinImpact/ViewModel/Pages/View/AutoFightViewModel.cs
index 428271f4..d9e8f3f1 100644
--- a/BetterGenshinImpact/ViewModel/Pages/View/AutoFightViewModel.cs
+++ b/BetterGenshinImpact/ViewModel/Pages/View/AutoFightViewModel.cs
@@ -79,7 +79,7 @@ public partial class AutoFightViewModel : ObservableObject, IViewModel
public void OnOpenLocalScriptRepo()
{
Config.ScriptConfig.ScriptRepoHintDotVisible = false;
- ScriptRepoUpdater.Instance.OpenLocalRepoInWebView();
+ ScriptRepoUpdater.Instance.OpenScriptRepoWindow();
}
[RelayCommand]