diff --git a/src/Snap.Hutao/Snap.Hutao/Context/FileSystem/CacheContext.cs b/src/Snap.Hutao/Snap.Hutao/Context/FileSystem/CacheContext.cs
new file mode 100644
index 00000000..6a5c1b89
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Context/FileSystem/CacheContext.cs
@@ -0,0 +1,64 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+using Snap.Hutao.Context.FileSystem.Location;
+using Windows.Storage;
+
+namespace Snap.Hutao.Context.FileSystem;
+
+///
+/// 缓存目录上下文
+///
+[Injection(InjectAs.Transient)]
+internal class CacheContext : FileSystemContext
+{
+ ///
+ /// 构造一个新的缓存目录上下文
+ ///
+ /// 缓存位置
+ public CacheContext(Cache cache)
+ : base(cache)
+ {
+ }
+
+ ///
+ /// 获取缓存文件夹
+ ///
+ public static StorageFolder Folder
+ {
+ get => ApplicationData.Current.TemporaryFolder;
+ }
+
+ ///
+ /// 获取缓存文件的名称
+ ///
+ /// uri
+ /// 缓存文件的名称
+ public static string GetCacheFileName(Uri uri)
+ {
+ return CreateHash64(uri.ToString()).ToString();
+ }
+
+ ///
+ /// 获取缓存文件的名称
+ ///
+ /// url
+ /// 缓存文件的名称
+ public static string GetCacheFileName(string url)
+ {
+ return CreateHash64(url).ToString();
+ }
+
+ private static ulong CreateHash64(string str)
+ {
+ byte[] utf8 = System.Text.Encoding.UTF8.GetBytes(str);
+
+ ulong value = (ulong)utf8.Length;
+ for (int n = 0; n < utf8.Length; n++)
+ {
+ value += (ulong)utf8[n] << ((n * 5) % 56);
+ }
+
+ return value;
+ }
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Context/FileSystem/Location/Cache.cs b/src/Snap.Hutao/Snap.Hutao/Context/FileSystem/Location/Cache.cs
new file mode 100644
index 00000000..315cdbfa
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Context/FileSystem/Location/Cache.cs
@@ -0,0 +1,26 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+using System.IO;
+
+namespace Snap.Hutao.Context.FileSystem.Location;
+
+///
+/// 缓存位置
+///
+[Injection(InjectAs.Transient)]
+internal class Cache : IFileSystemLocation
+{
+ private string? path;
+
+ ///
+ public string GetPath()
+ {
+ if (string.IsNullOrEmpty(path))
+ {
+ path = Windows.Storage.ApplicationData.Current.TemporaryFolder.Path;
+ }
+
+ return path;
+ }
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Context/FileSystem/Location/Metadata.cs b/src/Snap.Hutao/Snap.Hutao/Context/FileSystem/Location/Metadata.cs
index 7110d6a8..5f967ce4 100644
--- a/src/Snap.Hutao/Snap.Hutao/Context/FileSystem/Location/Metadata.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Context/FileSystem/Location/Metadata.cs
@@ -9,7 +9,7 @@ namespace Snap.Hutao.Context.FileSystem.Location;
/// 我的文档位置
///
[Injection(InjectAs.Transient)]
-public class Metadata : IFileSystemLocation
+internal class Metadata : IFileSystemLocation
{
private string? path;
diff --git a/src/Snap.Hutao/Snap.Hutao/Context/FileSystem/Location/MyDocument.cs b/src/Snap.Hutao/Snap.Hutao/Context/FileSystem/Location/MyDocument.cs
index 1dfa1a24..5bb9e34f 100644
--- a/src/Snap.Hutao/Snap.Hutao/Context/FileSystem/Location/MyDocument.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Context/FileSystem/Location/MyDocument.cs
@@ -9,7 +9,7 @@ namespace Snap.Hutao.Context.FileSystem.Location;
/// 我的文档位置
///
[Injection(InjectAs.Transient)]
-public class MyDocument : IFileSystemLocation
+internal class MyDocument : IFileSystemLocation
{
private string? path;
diff --git a/src/Snap.Hutao/Snap.Hutao/Context/FileSystem/MetadataContext.cs b/src/Snap.Hutao/Snap.Hutao/Context/FileSystem/MetadataContext.cs
index f50a29ca..da417975 100644
--- a/src/Snap.Hutao/Snap.Hutao/Context/FileSystem/MetadataContext.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Context/FileSystem/MetadataContext.cs
@@ -16,4 +16,4 @@ internal class MetadataContext : FileSystemContext
: base(metadata)
{
}
-}
\ No newline at end of file
+}
diff --git a/src/Snap.Hutao/Snap.Hutao/Control/Image/Gradient.cs b/src/Snap.Hutao/Snap.Hutao/Control/Image/Gradient.cs
new file mode 100644
index 00000000..2f3d9c31
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Control/Image/Gradient.cs
@@ -0,0 +1,140 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+using CommunityToolkit.WinUI.Helpers;
+using CommunityToolkit.WinUI.UI.Animations;
+using Microsoft.UI;
+using Microsoft.UI.Composition;
+using Microsoft.UI.Xaml;
+using Microsoft.UI.Xaml.Hosting;
+using Microsoft.UI.Xaml.Media;
+using Microsoft.UI.Xaml.Media.Animation;
+using Snap.Hutao.Context.FileSystem;
+using Snap.Hutao.Core;
+using Snap.Hutao.Core.Threading;
+using Snap.Hutao.Extension;
+using System.Numerics;
+using Windows.Graphics.Imaging;
+using Windows.Storage;
+using Windows.Storage.Streams;
+
+namespace Snap.Hutao.Control.Image;
+
+///
+/// 支持渐变的图像
+///
+public class Gradient : Microsoft.UI.Xaml.Controls.Control
+{
+ private static readonly DependencyProperty SourceProperty = Property.Depend(nameof(Source), string.Empty, OnSourceChanged);
+ private static readonly ConcurrentCancellationTokenSource ImageLoading = new();
+ private SpriteVisual? spriteVisual;
+ private double imageAspectRatio;
+
+ ///
+ /// 构造一个新的渐变图像
+ ///
+ public Gradient()
+ {
+ SizeChanged += OnSizeChanged;
+ }
+
+ ///
+ /// 源
+ ///
+ public string Source
+ {
+ get => (string)GetValue(SourceProperty);
+ set => SetValue(SourceProperty, value);
+ }
+
+ private static async Task GetCachedFileAsync(string url, CancellationToken token)
+ {
+ string fileName = CacheContext.GetCacheFileName(url);
+ CacheContext cacheContext = Ioc.Default.GetRequiredService();
+
+ StorageFile storageFile;
+ if (!cacheContext.FileExists(fileName))
+ {
+ storageFile = await CacheContext.Folder.CreateFileAsync(fileName).AsTask(token);
+ await StreamHelper.GetHttpStreamToStorageFileAsync(new(url), storageFile);
+ }
+ else
+ {
+ storageFile = await CacheContext.Folder.GetFileAsync(fileName).AsTask(token);
+ }
+
+ return storageFile;
+ }
+
+ private static void OnSourceChanged(DependencyObject sender, DependencyPropertyChangedEventArgs arg)
+ {
+ Gradient gradient = (Gradient)sender;
+ string url = (string)arg.NewValue;
+
+ gradient.ApplyImageAsync(url, ImageLoading.Register(gradient)).SafeForget();
+ }
+
+ private void OnSizeChanged(object sender, SizeChangedEventArgs e)
+ {
+ if (e.NewSize != e.PreviousSize && spriteVisual is not null)
+ {
+ UpdateVisual(spriteVisual);
+ }
+ }
+
+ private void UpdateVisual(SpriteVisual spriteVisual)
+ {
+ if (spriteVisual is not null)
+ {
+ double width = ActualWidth;
+ double height = Math.Clamp(width * imageAspectRatio, 0, MaxHeight);
+
+ spriteVisual.Size = new Vector2((float)width, (float)height);
+ Height = height;
+ }
+ }
+
+ private async Task ApplyImageAsync(string url, CancellationToken token)
+ {
+ await AnimationBuilder
+ .Create()
+ .Opacity(0, 1)
+ .StartAsync(this, token);
+
+ StorageFile storageFile = await GetCachedFileAsync(url, token);
+
+ Compositor compositor = ElementCompositionPreview.GetElementVisual(this).Compositor;
+
+ LoadedImageSurface imageSurface = await LoadImageSurfaceAsync(storageFile, token);
+
+ CompositionSurfaceBrush imageSurfaceBrush = compositor.CompositeSurfaceBrush(imageSurface, stretch: CompositionStretch.UniformToFill, vRatio: 0f);
+
+ CompositionLinearGradientBrush backgroundBrush = compositor.CompositeLinearGradientBrush(new(1f, 0), Vector2.UnitY, new(0, Colors.White), new(1, Colors.Black));
+ CompositionLinearGradientBrush foregroundBrush = compositor.CompositeLinearGradientBrush(Vector2.Zero, Vector2.UnitY, new(0, Colors.White), new(0.95f, Colors.Black));
+
+ CompositionEffectBrush gradientEffectBrush = compositor.CompositeBlendEffectBrush(backgroundBrush, foregroundBrush);
+ CompositionEffectBrush opacityMaskEffectBrush = compositor.CompositeLuminanceToAlphaEffectBrush(gradientEffectBrush);
+ CompositionEffectBrush alphaMaskEffectBrush = compositor.CompositeAlphaMaskEffectBrush(imageSurfaceBrush, opacityMaskEffectBrush);
+
+ spriteVisual = compositor.CompositeSpriteVisual(alphaMaskEffectBrush);
+ UpdateVisual(spriteVisual);
+
+ ElementCompositionPreview.SetElementChildVisual(this, spriteVisual);
+
+ await AnimationBuilder
+ .Create()
+ .Opacity(1, 0)
+ .StartAsync(this, token);
+ }
+
+ private async Task LoadImageSurfaceAsync(StorageFile storageFile, CancellationToken token)
+ {
+ using (IRandomAccessStream imageStream = await storageFile.OpenAsync(FileAccessMode.Read).AsTask(token))
+ {
+ BitmapDecoder decoder = await BitmapDecoder.CreateAsync(imageStream).AsTask(token);
+ imageAspectRatio = (double)decoder.PixelHeight / decoder.PixelWidth;
+
+ return LoadedImageSurface.StartLoadFromStream(imageStream);
+ }
+ }
+}
diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Logging/EventIds.cs b/src/Snap.Hutao/Snap.Hutao/Core/Logging/EventIds.cs
index 2a1d39c6..dfb6412e 100644
--- a/src/Snap.Hutao/Snap.Hutao/Core/Logging/EventIds.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Core/Logging/EventIds.cs
@@ -22,4 +22,9 @@ internal static class EventIds
/// Forget任务执行异常
///
public static readonly EventId TaskException = new(100002, nameof(TaskException));
+
+ ///
+ /// Forget任务执行异常
+ ///
+ public static readonly EventId AsyncCommandException = new(100003, nameof(AsyncCommandException));
}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Threading/ConcurrentCancellationTokenSource.cs b/src/Snap.Hutao/Snap.Hutao/Core/Threading/ConcurrentCancellationTokenSource.cs
new file mode 100644
index 00000000..a0cd7879
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Core/Threading/ConcurrentCancellationTokenSource.cs
@@ -0,0 +1,33 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+using System.Collections.Concurrent;
+
+namespace Snap.Hutao.Core.Threading;
+
+///
+/// 并发
+///
+/// 项类型
+internal class ConcurrentCancellationTokenSource
+ where TItem : notnull
+{
+ private readonly ConcurrentDictionary waitingItems = new();
+
+ ///
+ /// 未某个项注册取消令牌
+ ///
+ /// 项
+ /// 取消令牌
+ public CancellationToken Register(TItem item)
+ {
+ if (waitingItems.TryRemove(item, out CancellationTokenSource? prevSource))
+ {
+ prevSource.Cancel();
+ }
+
+ CancellationTokenSource current = waitingItems.GetOrAdd(item, new CancellationTokenSource());
+
+ return current.Token;
+ }
+}
diff --git a/src/Snap.Hutao/Snap.Hutao/Extension/CompositionExtensions.cs b/src/Snap.Hutao/Snap.Hutao/Extension/CompositionExtensions.cs
new file mode 100644
index 00000000..9d9c8c22
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Extension/CompositionExtensions.cs
@@ -0,0 +1,154 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+using Microsoft.Graphics.Canvas.Effects;
+using Microsoft.UI.Composition;
+using System.Numerics;
+
+namespace Snap.Hutao.Extension;
+
+///
+/// 合成扩展
+///
+internal static class CompositionExtensions
+{
+ ///
+ /// 创建拼合图视觉对象
+ ///
+ /// 合成器
+ /// 画刷
+ /// 拼合图视觉对象
+ public static SpriteVisual CompositeSpriteVisual(this Compositor compositor, CompositionBrush brush)
+ {
+ SpriteVisual spriteVisual = compositor.CreateSpriteVisual();
+ spriteVisual.Brush = brush;
+ return spriteVisual;
+ }
+
+ ///
+ /// 创建混合效果画刷
+ ///
+ /// 合成器
+ /// 前景
+ /// 背景
+ /// 混合模式
+ /// 合成效果画刷
+ public static CompositionEffectBrush CompositeBlendEffectBrush(
+ this Compositor compositor,
+ CompositionBrush background,
+ CompositionBrush foreground,
+ BlendEffectMode blendEffectMode = BlendEffectMode.Multiply)
+ {
+ BlendEffect effect = new()
+ {
+ Background = new CompositionEffectSourceParameter("Background"),
+ Foreground = new CompositionEffectSourceParameter("Foreground"),
+ Mode = blendEffectMode,
+ };
+
+ CompositionEffectBrush brush = compositor.CreateEffectFactory(effect).CreateBrush();
+
+ brush.SetSourceParameter("Background", background);
+ brush.SetSourceParameter("Foreground", foreground);
+
+ return brush;
+ }
+
+ ///
+ /// 创建亮度转不透明度效果画刷
+ ///
+ /// 合成器
+ /// 源
+ /// 合成效果画刷
+ public static CompositionEffectBrush CompositeLuminanceToAlphaEffectBrush(this Compositor compositor, CompositionBrush sourceBrush)
+ {
+ LuminanceToAlphaEffect effect = new()
+ {
+ Source = new CompositionEffectSourceParameter("Source"),
+ };
+
+ CompositionEffectBrush brush = compositor.CreateEffectFactory(effect).CreateBrush();
+
+ brush.SetSourceParameter("Source", sourceBrush);
+
+ return brush;
+ }
+
+ ///
+ /// 创建不透明度蒙版效果画刷
+ ///
+ /// 合成器
+ /// 源
+ /// 不透明度蒙版
+ /// 合成效果画刷
+ public static CompositionEffectBrush CompositeAlphaMaskEffectBrush(
+ this Compositor compositor,
+ CompositionBrush sourceBrush,
+ CompositionBrush alphaMask)
+ {
+ AlphaMaskEffect maskEffect = new()
+ {
+ AlphaMask = new CompositionEffectSourceParameter("AlphaMask"),
+ Source = new CompositionEffectSourceParameter("Source"),
+ };
+
+ CompositionEffectBrush brush = compositor.CreateEffectFactory(maskEffect).CreateBrush();
+
+ brush.SetSourceParameter("AlphaMask", alphaMask);
+ brush.SetSourceParameter("Source", sourceBrush);
+
+ return brush;
+ }
+
+ ///
+ /// 创建一个表面画刷
+ ///
+ /// 合成器
+ /// 合成表面
+ /// 拉伸方法
+ /// 水平对齐比
+ /// 垂直对齐比
+ /// 合成表面画刷
+ public static CompositionSurfaceBrush CompositeSurfaceBrush(
+ this Compositor compositor,
+ ICompositionSurface surface,
+ CompositionStretch stretch = CompositionStretch.None,
+ float hRatio = 0.5f,
+ float vRatio = 0.5f)
+ {
+ CompositionSurfaceBrush brush = compositor.CreateSurfaceBrush(surface);
+ brush.Stretch = stretch;
+ brush.VerticalAlignmentRatio = vRatio;
+ brush.HorizontalAlignmentRatio = hRatio;
+
+ return brush;
+ }
+
+ ///
+ /// 创建一个线性渐变画刷
+ ///
+ /// 合成器
+ /// 起点
+ /// 终点
+ /// 锚点
+ /// 线性渐变画刷
+ public static CompositionLinearGradientBrush CompositeLinearGradientBrush(
+ this Compositor compositor,
+ Vector2 start,
+ Vector2 end,
+ params GradientStop[] stops)
+ {
+ CompositionLinearGradientBrush brush = compositor.CreateLinearGradientBrush();
+ brush.StartPoint = start;
+ brush.EndPoint = end;
+
+ foreach (GradientStop stop in stops)
+ {
+ brush.ColorStops.Add(compositor.CreateColorGradientStop(stop.Offset, stop.Color));
+ }
+
+ return brush;
+ }
+
+ public record struct GradientStop(float Offset, Windows.UI.Color Color);
+}
diff --git a/src/Snap.Hutao/Snap.Hutao/Extension/TaskExtensions.cs b/src/Snap.Hutao/Snap.Hutao/Extension/TaskExtensions.cs
index 8e6f1826..8296a7af 100644
--- a/src/Snap.Hutao/Snap.Hutao/Extension/TaskExtensions.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Extension/TaskExtensions.cs
@@ -24,6 +24,9 @@ public static class TaskExtensions
{
await task.ConfigureAwait(continueOnCapturedContext);
}
+ catch (TaskCanceledException)
+ {
+ }
catch (Exception e)
{
logger?.LogError(EventIds.TaskException, e, "{caller}:{exception}", nameof(SafeForget), e.GetBaseException());
diff --git a/src/Snap.Hutao/Snap.Hutao/Factory/AsyncRelayCommandFactory.cs b/src/Snap.Hutao/Snap.Hutao/Factory/AsyncRelayCommandFactory.cs
index 99d5393b..e9aaf1de 100644
--- a/src/Snap.Hutao/Snap.Hutao/Factory/AsyncRelayCommandFactory.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Factory/AsyncRelayCommandFactory.cs
@@ -2,7 +2,7 @@
// Licensed under the MIT license.
using CommunityToolkit.Mvvm.Input;
-using Microsoft.AppCenter.Crashes;
+using Snap.Hutao.Core.Logging;
using Snap.Hutao.Factory.Abstraction;
namespace Snap.Hutao.Factory;
@@ -93,8 +93,7 @@ internal class AsyncRelayCommandFactory : IAsyncRelayCommandFactory
if (asyncRelayCommand.ExecutionTask?.Exception is AggregateException exception)
{
Exception baseException = exception.GetBaseException();
- logger.LogError(baseException, "异步命令发生了错误");
- Crashes.TrackError(baseException);
+ logger.LogError(EventIds.AsyncCommandException, baseException, "异步命令发生了错误");
}
}
}
diff --git a/src/Snap.Hutao/Snap.Hutao/IocHttpClientConfiguration.cs b/src/Snap.Hutao/Snap.Hutao/IocHttpClientConfiguration.cs
index 436cf0e9..908bbade 100644
--- a/src/Snap.Hutao/Snap.Hutao/IocHttpClientConfiguration.cs
+++ b/src/Snap.Hutao/Snap.Hutao/IocHttpClientConfiguration.cs
@@ -31,10 +31,10 @@ internal static class IocHttpClientConfiguration
services.AddHttpClient(DefaultConfiguration);
// normal clients
- services.AddHttpClient(DefaultConfiguration);
services.AddHttpClient(DefaultConfiguration);
- services.AddHttpClient(DefaultConfiguration);
services.AddHttpClient(DefaultConfiguration);
+ services.AddHttpClient(DefaultConfiguration);
+ services.AddHttpClient(DefaultConfiguration);
// x-rpc clients
services.AddHttpClient(XRpcConfiguration);
diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Converter/AvatarNameCardPicConverter.cs b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Converter/AvatarNameCardPicConverter.cs
new file mode 100644
index 00000000..11c66cc4
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Converter/AvatarNameCardPicConverter.cs
@@ -0,0 +1,33 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+using Microsoft.UI.Xaml.Data;
+
+namespace Snap.Hutao.Model.Metadata.Converter;
+
+///
+/// 角色名片转换器
+///
+internal class AvatarNameCardPicConverter : IValueConverter
+{
+ private const string BaseUrl = "https://static.snapgenshin.com/NameCardPic/UI_NameCardPic_{0}_P.png";
+
+ ///
+ public object Convert(object value, Type targetType, object parameter, string language)
+ {
+ if (value == null)
+ {
+ return null!;
+ }
+
+ Avatar.Avatar avatar = (Avatar.Avatar)value;
+ string avatarName = avatar.Icon[14..];
+ return new Uri(string.Format(BaseUrl, avatarName));
+ }
+
+ ///
+ public object ConvertBack(object value, Type targetType, object parameter, string language)
+ {
+ throw Must.NeverHappen();
+ }
+}
\ 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 1fc6f2d8..80469ad8 100644
--- a/src/Snap.Hutao/Snap.Hutao/Package.appxmanifest
+++ b/src/Snap.Hutao/Snap.Hutao/Package.appxmanifest
@@ -9,7 +9,7 @@
+ Version="1.0.13.0" />
胡桃
diff --git a/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj b/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj
index a96303ce..6c1daf5b 100644
--- a/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj
+++ b/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj
@@ -71,10 +71,9 @@
-
-
-
-
+
+
+
diff --git a/src/Snap.Hutao/Snap.Hutao/View/Page/WikiAvatarPage.xaml b/src/Snap.Hutao/Snap.Hutao/View/Page/WikiAvatarPage.xaml
index 5c4e6ffe..6acb29ac 100644
--- a/src/Snap.Hutao/Snap.Hutao/View/Page/WikiAvatarPage.xaml
+++ b/src/Snap.Hutao/Snap.Hutao/View/Page/WikiAvatarPage.xaml
@@ -5,7 +5,6 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:cwuc="using:CommunityToolkit.WinUI.UI.Controls"
- xmlns:cwum="using:CommunityToolkit.WinUI.UI.Media"
xmlns:mxic="using:Microsoft.Xaml.Interactions.Core"
xmlns:mxi="using:Microsoft.Xaml.Interactivity"
xmlns:shc="using:Snap.Hutao.Control"
@@ -27,6 +26,7 @@
+
@@ -144,44 +144,48 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -391,7 +395,7 @@
-
+
-
+
-
+
@@ -494,7 +498,7 @@
-
-
-
+
+
+
\ No newline at end of file