diff --git a/ChatBox.sln b/ChatBox.sln index 7c7638c..801f5bd 100644 --- a/ChatBox.sln +++ b/ChatBox.sln @@ -15,6 +15,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ChatBox.Logger", "src\ChatB EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ChatBox.Desktop.Macos", "src\ChatBox.Desktop.Macos\ChatBox.Desktop.Macos.csproj", "{39B84C7C-FAB8-4066-B5C4-AFB7FD81031C}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ChatBox.IOS", "src\ChatBox.IOS\ChatBox.IOS.csproj", "{0B3D5DA7-732D-4919-A755-E15633E5FB31}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -41,6 +43,10 @@ Global {39B84C7C-FAB8-4066-B5C4-AFB7FD81031C}.Debug|Any CPU.Build.0 = Debug|Any CPU {39B84C7C-FAB8-4066-B5C4-AFB7FD81031C}.Release|Any CPU.ActiveCfg = Release|Any CPU {39B84C7C-FAB8-4066-B5C4-AFB7FD81031C}.Release|Any CPU.Build.0 = Release|Any CPU + {0B3D5DA7-732D-4919-A755-E15633E5FB31}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0B3D5DA7-732D-4919-A755-E15633E5FB31}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0B3D5DA7-732D-4919-A755-E15633E5FB31}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0B3D5DA7-732D-4919-A755-E15633E5FB31}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -51,6 +57,7 @@ Global {908453E7-9DAC-42C1-8B41-5199ECB5E785} = {40D887DE-CAB8-452F-90FE-BB3CF2B2613C} {14CDEB6A-DB37-4A22-B0AC-B5117EF57DDB} = {40D887DE-CAB8-452F-90FE-BB3CF2B2613C} {39B84C7C-FAB8-4066-B5C4-AFB7FD81031C} = {40D887DE-CAB8-452F-90FE-BB3CF2B2613C} + {0B3D5DA7-732D-4919-A755-E15633E5FB31} = {40D887DE-CAB8-452F-90FE-BB3CF2B2613C} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {9A2982A7-F084-41A2-ACD5-4C6DDA6F03C4} diff --git a/src/ChatBox.Desktop.Macos/AppDelegate.cs b/src/ChatBox.Desktop.Macos/AppDelegate.cs index 94ab2e1..3d799de 100644 --- a/src/ChatBox.Desktop.Macos/AppDelegate.cs +++ b/src/ChatBox.Desktop.Macos/AppDelegate.cs @@ -6,6 +6,7 @@ using ChatBox.Constant; using ChatBox.Service; using Foundation; +using Microsoft.Extensions.DependencyInjection; namespace ChatBox.Desktop; @@ -22,8 +23,7 @@ public override void OpenUrls(NSApplication application, NSUrl[] urls) var token = CrossPlatformCustomProtocolHelper.ParseCustomProtocolArgs(urls.Select(u => u.AbsoluteString).Where(x=>!string.IsNullOrWhiteSpace(x)).ToArray()!); if (!string.IsNullOrWhiteSpace(token)) { - var settingService = new SettingService(); - + var settingService = HostApplication.Services.GetRequiredService(); settingService.InitSetting(token); } } diff --git a/src/ChatBox.IOS/AppDelegate.cs b/src/ChatBox.IOS/AppDelegate.cs new file mode 100644 index 0000000..2017e68 --- /dev/null +++ b/src/ChatBox.IOS/AppDelegate.cs @@ -0,0 +1,48 @@ +using System; +using Foundation; +using UIKit; +using Avalonia; +using Avalonia.Controls.ApplicationLifetimes; +using Avalonia.iOS; +using ChatBox.Desktop; +using ChatBox.Service; +using Microsoft.Extensions.DependencyInjection; + +namespace ChatBox.iOS; + +// The UIApplicationDelegate for the application. This class is responsible for launching the +// User Interface of the application, as well as listening (and optionally responding) to +// application events from iOS. +[Register("AppDelegate")] +#pragma warning disable CA1711 // Identifiers should not have incorrect suffix +public partial class AppDelegate : AvaloniaAppDelegate +#pragma warning restore CA1711 // Identifiers should not have incorrect suffix +{ + public AppDelegate() + { + // 订阅 Activated 事件 + ((IAvaloniaAppDelegate)this).Activated += OnActivated; + } + + private void OnActivated(object? sender, ActivatedEventArgs e) + { + // 检查是否是 URL 协议激活 + if (e is ProtocolActivatedEventArgs protocolArgs) + { + var uri = protocolArgs.Uri.ToString(); + var token = CrossPlatformCustomProtocolHelper.ParseCustomProtocolArgs([uri]); + if (!string.IsNullOrWhiteSpace(token)) + { + var settingService = HostApplication.Services.GetRequiredService(); + settingService.InitSetting(token); + } + } + } + + protected override AppBuilder CustomizeAppBuilder(AppBuilder builder) + { + return base.CustomizeAppBuilder(builder) + .WithInterFont() + .LogToTrace(); + } +} \ No newline at end of file diff --git a/src/ChatBox.IOS/ChatBox.IOS.csproj b/src/ChatBox.IOS/ChatBox.IOS.csproj new file mode 100644 index 0000000..527d32c --- /dev/null +++ b/src/ChatBox.IOS/ChatBox.IOS.csproj @@ -0,0 +1,28 @@ + + + Exe + net9.0-ios + 13.0 + enable + + + + + + + + + + + + + Resources\i18n\ChatBoxMarkdown.zh-Hant.xml + Always + + + Resources\models.json + Always + + + + diff --git a/src/ChatBox.IOS/Entitlements.plist b/src/ChatBox.IOS/Entitlements.plist new file mode 100644 index 0000000..0c67376 --- /dev/null +++ b/src/ChatBox.IOS/Entitlements.plist @@ -0,0 +1,5 @@ + + + + + diff --git a/src/ChatBox.IOS/Info.plist b/src/ChatBox.IOS/Info.plist new file mode 100644 index 0000000..ce9e6d6 --- /dev/null +++ b/src/ChatBox.IOS/Info.plist @@ -0,0 +1,54 @@ + + + + + CFBundleDisplayName + ChatBox + CFBundleIdentifier + com.aidotnet.chatbox + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1.0.0 + LSRequiresIPhoneOS + + MinimumOSVersion + 13.0 + UIDeviceFamily + + 1 + 2 + + UILaunchStoryboardName + LaunchScreen + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + CFBundleURLTypes + + + CFBundleURLName + com.aidotnet.chatbox + CFBundleURLSchemes + + chatbox + + + + + diff --git a/src/ChatBox.IOS/Main.cs b/src/ChatBox.IOS/Main.cs new file mode 100644 index 0000000..d88df71 --- /dev/null +++ b/src/ChatBox.IOS/Main.cs @@ -0,0 +1,16 @@ +using Microsoft.Extensions.DependencyInjection; +using UIKit; + +namespace ChatBox.iOS; + +public class Application +{ + // This is the main entry point of the application. + static void Main(string[] args) + { + // if you want to use a different Application Delegate class from "AppDelegate" + // you can specify it here. + HostApplication.Builder(); + UIApplication.Main(args, null, typeof(AppDelegate)); + } +} \ No newline at end of file diff --git a/src/ChatBox.IOS/Resources/LaunchScreen.xib b/src/ChatBox.IOS/Resources/LaunchScreen.xib new file mode 100644 index 0000000..a308c70 --- /dev/null +++ b/src/ChatBox.IOS/Resources/LaunchScreen.xib @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/ChatBox/App.axaml.cs b/src/ChatBox/App.axaml.cs index b29c8c2..d7bfffe 100644 --- a/src/ChatBox/App.axaml.cs +++ b/src/ChatBox/App.axaml.cs @@ -50,7 +50,7 @@ public override void OnFrameworkInitializationCompleted() } else if (ApplicationLifetime is ISingleViewApplicationLifetime singleViewPlatform) { - singleViewPlatform.MainView = HostApplication.Services.GetRequiredService(); + singleViewPlatform.MainView = HostApplication.Services.GetRequiredService(); } base.OnFrameworkInitializationCompleted(); diff --git a/src/ChatBox/ChatBox.csproj b/src/ChatBox/ChatBox.csproj index fc01fca..5f61204 100644 --- a/src/ChatBox/ChatBox.csproj +++ b/src/ChatBox/ChatBox.csproj @@ -4,6 +4,7 @@ enable latest true + $(InterceptorsNamespaces);Dapper.AOT diff --git a/src/ChatBox/Controls/ChatInput.axaml.cs b/src/ChatBox/Controls/ChatInput.axaml.cs index 2fcf394..7b93597 100644 --- a/src/ChatBox/Controls/ChatInput.axaml.cs +++ b/src/ChatBox/Controls/ChatInput.axaml.cs @@ -54,12 +54,7 @@ public ChatInput() protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) { base.OnAttachedToVisualTree(e); - _notificationManager = new WindowNotificationManager(HostApplication.Services.GetService()) - { - Position = NotificationPosition.TopRight, - MaxItems = 4, - Margin = new Thickness(0, 0, 15, 40) - }; + _notificationManager = HostApplication.Services.GetService(); } @@ -254,19 +249,18 @@ private async void DeleteButton_Click(object? sender, RoutedEventArgs e) private async void OpenFile(object? sender, RoutedEventArgs e) { - var _target = HostApplication.Services.GetService(); - var files = await _target.StorageProvider.OpenFilePickerAsync(new FilePickerOpenOptions() + var files = await TopLevel.GetTopLevel(this).StorageProvider.OpenFilePickerAsync(new FilePickerOpenOptions() { Title = "请选择文件", AllowMultiple = true, FileTypeFilter = new[] { CodeAll, FilePickerFileTypes.TextPlain } }); - + if (files.Count == 0) { return; } - + foreach (var file in files) { ViewModel.Files.Add(new FileModel() diff --git a/src/ChatBox/Controls/ChatRender.axaml.cs b/src/ChatBox/Controls/ChatRender.axaml.cs index 1f0aa44..1f0ff1e 100644 --- a/src/ChatBox/Controls/ChatRender.axaml.cs +++ b/src/ChatBox/Controls/ChatRender.axaml.cs @@ -36,12 +36,7 @@ protected override void OnApplyTemplate(TemplateAppliedEventArgs e) { base.OnApplyTemplate(e); - _notificationManager = new WindowNotificationManager(HostApplication.Services.GetService()) - { - Position = NotificationPosition.TopRight, - MaxItems = 4, - Margin = new Thickness(0, 0, 15, 40) - }; + _notificationManager = HostApplication.Services.GetService(); } private void DeleteButton_Click(object sender, RoutedEventArgs e) @@ -138,8 +133,7 @@ private Button CreateCopyButton(string codeContent) copyButton.Click += (sender, e) => { - var window = HostApplication.Services.GetService(); - window.Clipboard?.SetTextAsync(codeContent); + TopLevel.GetTopLevel(HostApplication.Services.GetService())!.Clipboard?.SetTextAsync(codeContent); _notificationManager?.Show(new Notification( "已复制", "代码已复制到剪贴板", diff --git a/src/ChatBox/Controls/WorkspaceConversation.axaml.cs b/src/ChatBox/Controls/WorkspaceConversation.axaml.cs index 7c78b66..777087e 100644 --- a/src/ChatBox/Controls/WorkspaceConversation.axaml.cs +++ b/src/ChatBox/Controls/WorkspaceConversation.axaml.cs @@ -29,12 +29,7 @@ public WorkspaceConversation() protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) { base.OnAttachedToVisualTree(e); - _notificationManager = new WindowNotificationManager(HostApplication.Services.GetService()) - { - Position = NotificationPosition.TopRight, - MaxItems = 3, - Margin = new Thickness(0, 0, 15, 40) - }; + _notificationManager = HostApplication.Services.GetService(); } protected override async void OnDataContextChanged(EventArgs e) diff --git a/src/ChatBox/DataAccess/ChatMessageRepository.cs b/src/ChatBox/DataAccess/ChatMessageRepository.cs index a47eee2..e50ded0 100644 --- a/src/ChatBox/DataAccess/ChatMessageRepository.cs +++ b/src/ChatBox/DataAccess/ChatMessageRepository.cs @@ -4,6 +4,7 @@ namespace ChatBox.DataAccess; +[DapperAot] public class ChatMessageRepository(Lazy dbContext) { public async Task InsertAsync(ChatMessage message) diff --git a/src/ChatBox/HostApplication.cs b/src/ChatBox/HostApplication.cs index b6d9f9d..bc70a79 100644 --- a/src/ChatBox/HostApplication.cs +++ b/src/ChatBox/HostApplication.cs @@ -1,4 +1,6 @@ -using ChatBox.AI; +using Avalonia; +using Avalonia.Controls.Notifications; +using ChatBox.AI; using ChatBox.Logger; using ChatBox.Pages; using ChatBox.Service; @@ -10,98 +12,118 @@ namespace ChatBox; public class HostApplication { - private static IServiceProvider _serviceProvider = null!; - - /// - /// 退出登录 - /// - /// - public static Action Logout { get; set; } = null!; - - public static IServiceProvider Services => _serviceProvider; - - public static void Infonrmation(string message) - { - var logger = _serviceProvider.GetService>(); - logger.LogInformation(message); - } - - public static void Error(string message) - { - var logger = _serviceProvider.GetService>(); - logger.LogError(message); - } - - public static void Warning(string message) - { - var logger = _serviceProvider.GetService>(); - logger.LogWarning(message); - } - - public static void Debug(string message) - { - var logger = _serviceProvider.GetService>(); - logger.LogDebug(message); - } - - public static void Infonrmation(string message, Exception exception) - { - var logger = _serviceProvider.GetService>(); - logger.LogInformation(exception, message); - } - - public static void Error(string message, Exception exception) - { - var logger = _serviceProvider.GetService>(); - logger.LogError(exception, message); - } - - public static void Warning(string message, Exception exception) - { - var logger = _serviceProvider.GetService>(); - logger.LogWarning(exception, message); - } - - public static IHost Builder() - { - var host = new HostBuilder() - .ConfigureServices((hostContext, services) => - { - services.AddSingleton(); - services.AddSingleton(); - - services.AddSingleton(); - services.AddSingleton(); - - services.AddSingleton(); - services.AddSingleton(); - - services.AddSingleton(); - - services.AddSingleton>((provider => - { - return new Lazy(() => new DbContext()); - })); - - services.AddSingleton(); - - services.AddSingleton(); - - - services.AddSingleton(); - services.AddSingleton(); - - services.AddSingleton(); - }).ConfigureLogging(logging => - { - logging.ClearProviders(); - logging.AddChatBoxLogger(); - }) - - .Build(); - - _serviceProvider = host.Services; - - return host; - } + private static IServiceProvider _serviceProvider = null!; + + /// + /// 退出登录 + /// + /// + public static Action Logout { get; set; } = null!; + + public static IServiceProvider Services => _serviceProvider; + + public static void Infonrmation(string message) + { + var logger = _serviceProvider.GetService>(); + logger.LogInformation(message); + } + + public static void Error(string message) + { + var logger = _serviceProvider.GetService>(); + logger.LogError(message); + } + + public static void Warning(string message) + { + var logger = _serviceProvider.GetService>(); + logger.LogWarning(message); + } + + public static void Debug(string message) + { + var logger = _serviceProvider.GetService>(); + logger.LogDebug(message); + } + + public static void Infonrmation(string message, Exception exception) + { + var logger = _serviceProvider.GetService>(); + logger.LogInformation(exception, message); + } + + public static void Error(string message, Exception exception) + { + var logger = _serviceProvider.GetService>(); + logger.LogError(exception, message); + } + + public static void Warning(string message, Exception exception) + { + var logger = _serviceProvider.GetService>(); + logger.LogWarning(exception, message); + } + + public static IHost Builder() + { + var host = new HostBuilder() + .ConfigureServices((hostContext, services) => + { + services.AddSingleton(); + services.AddSingleton(); + + if (OperatingSystem.IsWindows() || OperatingSystem.IsMacOS() || + OperatingSystem.IsMacCatalyst() || OperatingSystem.IsLinux()) + { + services.AddSingleton(); + services.AddSingleton(s => new WindowNotificationManager(s.GetRequiredService()) + { + Position = NotificationPosition.TopRight, + MaxItems = 4, + Margin = new Thickness(0, 0, 15, 40) + }); + } + else + { + services.AddSingleton(); + services.AddSingleton(s => new WindowNotificationManager(TopLevel.GetTopLevel(s.GetRequiredService())) + { + Position = NotificationPosition.TopRight, + MaxItems = 4, + Margin = new Thickness(0, 0, 15, 40) + }); + } + + services.AddSingleton(); + + services.AddSingleton(); + services.AddSingleton(); + + services.AddSingleton(); + + services.AddSingleton>((provider => + { + return new Lazy(() => new DbContext()); + })); + + services.AddSingleton(); + + services.AddSingleton(); + + + services.AddSingleton(); + services.AddSingleton(); + + services.AddSingleton(); + }).ConfigureLogging(logging => + { + logging.ClearProviders(); + logging.AddChatBoxLogger(); + }) + .Build(); + + _serviceProvider = host.Services; + + return host; + } } \ No newline at end of file diff --git a/src/ChatBox/Internal/CrossPlatformCustomProtocolHelper.cs b/src/ChatBox/Internal/CrossPlatformCustomProtocolHelper.cs index 0a12ec2..c0f74aa 100644 --- a/src/ChatBox/Internal/CrossPlatformCustomProtocolHelper.cs +++ b/src/ChatBox/Internal/CrossPlatformCustomProtocolHelper.cs @@ -1,5 +1,7 @@ using System; using System.IO; +using Avalonia.Platform.Storage; +using ChatBox.Views; namespace ChatBox.Desktop; @@ -38,11 +40,8 @@ public static void OpenCustomProtocolUrl() var url = CustomProtocol + "://callback"; // 打开https://api.token-ai.cn/login?redirect_uri=chatbox://callback // 会调用默认浏览器打开 URL - var process = new System.Diagnostics.Process(); - // 使用默认浏览器打开 - process.StartInfo.UseShellExecute = true; - process.StartInfo.FileName = "https://api.token-ai.cn/login?redirect_uri=" + url; - process.Start(); + var launcher = TopLevel.GetTopLevel(HostApplication.Services.GetRequiredService())!.Launcher; + launcher.LaunchUriAsync(new Uri("https://api.token-ai.cn/login?redirect_uri=" + url)); } /// diff --git a/src/ChatBox/Pages/Setting.axaml.cs b/src/ChatBox/Pages/Setting.axaml.cs index 6803952..42c69c0 100644 --- a/src/ChatBox/Pages/Setting.axaml.cs +++ b/src/ChatBox/Pages/Setting.axaml.cs @@ -32,12 +32,7 @@ protected override void OnDataContextChanged(EventArgs e) protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) { base.OnAttachedToVisualTree(e); - _notificationManager = new WindowNotificationManager(HostApplication.Services.GetService()) - { - Position = NotificationPosition.TopRight, - MaxItems = 4, - Margin = new Thickness(0, 0, 15, 40) - }; + _notificationManager = HostApplication.Services.GetService(); } private SettingViewModel ViewModel => (SettingViewModel)DataContext; diff --git a/src/ChatBox/Service/SettingService.cs b/src/ChatBox/Service/SettingService.cs index a4c4b8b..415f28f 100644 --- a/src/ChatBox/Service/SettingService.cs +++ b/src/ChatBox/Service/SettingService.cs @@ -8,36 +8,15 @@ namespace ChatBox.Service; public class SettingService { private readonly string _settingConfig = ConstantPath.ChatSettingPath; - + private Action? _settingCallback; + /// /// 监听_settingConfig文件的变化 /// /// public void FileChange(Action settingModel) { - var path = new FileInfo(_settingConfig); - - if (path.Directory?.Exists == false) - { - path.Directory.Create(); - } - - // 监听文件变化 - var watcher = new FileSystemWatcher(path.DirectoryName ?? throw new InvalidOperationException(), path.Name) - { - EnableRaisingEvents = true, - NotifyFilter = NotifyFilters.LastWrite, - Filter = path.Name - }; - - watcher.Changed += (sender, args) => - { - settingModel.Invoke(); - - watcher.EnableRaisingEvents = false; - - watcher.Dispose(); - }; + _settingCallback = settingModel; } public void SaveSetting(SettingModel settingModel) @@ -52,6 +31,8 @@ public void SaveSetting(SettingModel settingModel) var content = JsonSerializer.Serialize(settingModel, AppJsonSerialize.SerializerOptions); File.WriteAllText(_settingConfig, content); + + _settingCallback?.Invoke(); } public void InitSetting(string token) diff --git a/src/ChatBox/Views/MainView.axaml.cs b/src/ChatBox/Views/MainView.axaml.cs index ee2b2ff..a985686 100644 --- a/src/ChatBox/Views/MainView.axaml.cs +++ b/src/ChatBox/Views/MainView.axaml.cs @@ -20,7 +20,7 @@ namespace ChatBox.Views; public partial class MainView : UserControl { private readonly SettingService _settingService; - private WindowNotificationManager _notificationManager; + private WindowNotificationManager? _notificationManager; public MainView() { @@ -44,12 +44,7 @@ protected override void OnApplyTemplate(TemplateAppliedEventArgs e) { base.OnApplyTemplate(e); - _notificationManager = new WindowNotificationManager(HostApplication.Services.GetRequiredService()) - { - Position = NotificationPosition.TopRight, - MaxItems = 4, - Margin = new Thickness(0, 0, 15, 40) - }; + _notificationManager = HostApplication.Services.GetService(); var setting = _settingService.LoadSetting();