Translation caching. New ai actions panel.

This commit is contained in:
Burak Kaan Köse
2026-04-03 11:56:25 +02:00
parent 8f16f553f5
commit 27e91316d3
20 changed files with 1150 additions and 23 deletions
+22 -1
View File
@@ -160,6 +160,17 @@
Visibility="{x:Bind ViewModel.IsDraftBusy, Mode=OneWay}">
<ProgressRing IsActive="True" />
</AppBarButton>
<AppBarToggleButton
x:Name="ComposeAiActionsToggleButton"
Checked="ComposeAiActionsToggleButton_Checked"
MinWidth="40"
HorizontalContentAlignment="Center"
LabelPosition="Collapsed"
ToolTipService.ToolTip="{x:Bind domain:Translator.Composer_AiActions}">
<AppBarToggleButton.Icon>
<FontIcon FontFamily="{StaticResource SymbolThemeFontFamily}" Glyph="&#xE945;" />
</AppBarToggleButton.Icon>
</AppBarToggleButton>
<AppBarButton Click="ToggleEditorThemeClicked" ToolTipService.ToolTip="{x:Bind GetEditorThemeToolTip(WebViewEditor.IsEditorDarkMode), Mode=OneWay}">
<AppBarButton.Icon>
<coreControls:WinoFontIcon Icon="{x:Bind GetEditorThemeIcon(WebViewEditor.IsEditorDarkMode), Mode=OneWay}" />
@@ -307,6 +318,7 @@
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
@@ -445,10 +457,19 @@
BorderThickness="0"
Text="{x:Bind ViewModel.Subject, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<coreControls:AiActionsPanel
x:Name="ComposeAiActionsPanel"
Grid.Row="5"
Grid.ColumnSpan="2"
Margin="0,8,0,0"
AvailableActions="Rewrite"
HtmlHost="{x:Bind}"
Visibility="{x:Bind GetAiActionsPanelVisibility(ComposeAiActionsToggleButton.IsChecked), Mode=OneWay}" />
<!-- Attachments -->
<ListView
x:Name="AttachmentsListView"
Grid.Row="5"
Grid.Row="6"
Grid.ColumnSpan="2"
ui:ListViewExtensions.Command="{x:Bind ViewModel.OpenAttachmentCommand}"
x:Load="{x:Bind helpers:XamlHelpers.CountToBooleanConverter(ViewModel.IncludedAttachments.Count), Mode=OneWay}"
@@ -32,12 +32,15 @@ using Wino.Views.Abstract;
namespace Wino.Views.Mail;
public sealed partial class ComposePage : ComposePageAbstract,
IAiHtmlActionHost,
IRecipient<CreateNewComposeMailRequested>,
IRecipient<ApplicationThemeChanged>,
IRecipient<ReaderItemRefreshRequestedEvent>
{
public WebView2 GetWebView() => WebViewEditor.GetUnderlyingWebView();
public Visibility GetAiActionsPanelVisibility(bool? isChecked) => isChecked == true ? Visibility.Visible : Visibility.Collapsed;
private readonly List<IDisposable> _disposables = [];
public ComposePage()
@@ -283,6 +286,11 @@ public sealed partial class ComposePage : ComposePageAbstract,
ViewModel.IsCCBCCVisible = true;
}
private async void ComposeAiActionsToggleButton_Checked(object sender, RoutedEventArgs e)
{
await ComposeAiActionsPanel.RefreshAvailabilityAsync();
}
private async void TokenItemAdding(TokenizingTextBox sender, TokenItemAddingEventArgs args)
{
// Check is valid email.
@@ -410,11 +418,39 @@ public sealed partial class ComposePage : ComposePageAbstract,
base.OnNavigatingFrom(e);
FocusManager.GotFocus -= GlobalFocusManagerGotFocus;
ComposeAiActionsPanel.CancelPendingOperation();
await ViewModel.UpdateMimeChangesAsync();
DisposeDisposables();
}
public async Task<string?> GetCurrentHtmlAsync(CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
var html = await WebViewEditor.GetHtmlBodyAsync();
cancellationToken.ThrowIfCancellationRequested();
return html;
}
public async Task ApplyHtmlResultAsync(string html, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
await WebViewEditor.RenderHtmlAsync(html);
cancellationToken.ThrowIfCancellationRequested();
}
public Task<string?> TryGetCachedTranslationHtmlAsync(string languageCode, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
return Task.FromResult<string?>(null);
}
public Task SaveCachedTranslationHtmlAsync(string languageCode, string html, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
return Task.CompletedTask;
}
private void OpenAttachment_Click(object sender, RoutedEventArgs e)
{
if (sender is MenuFlyoutItem item && item.CommandParameter is MailAttachmentViewModel attachment)
@@ -13,6 +13,7 @@
xmlns:local="using:Wino.Behaviors"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:muxc="using:Microsoft.UI.Xaml.Controls"
xmlns:coreControls="using:Wino.Mail.WinUI.Controls"
xmlns:toolkit="using:CommunityToolkit.WinUI.Controls"
xmlns:viewModelData="using:Wino.Mail.ViewModels.Data"
x:Name="root"
@@ -181,6 +182,7 @@
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid Grid.Row="0" Margin="5,0">
<Grid.ColumnDefinitions>
@@ -273,13 +275,16 @@
IsDynamicOverflowEnabled="True"
OverflowButtonVisibility="Auto">
<interactivity:Interaction.Behaviors>
<local:BindableCommandBarBehavior ItemClickedCommand="{x:Bind ViewModel.OperationClickedCommand}" PrimaryCommands="{x:Bind ViewModel.MenuItems, Mode=OneWay}" />
<local:BindableCommandBarBehavior
ItemClickedCommand="{x:Bind ViewModel.OperationClickedCommand}"
PrimaryCommands="{x:Bind ViewModel.MenuItems, Mode=OneWay}" />
</interactivity:Interaction.Behaviors>
<CommandBar.Content>
<Grid Padding="0,5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
@@ -317,6 +322,19 @@
<TextBlock FontSize="12" Text="{x:Bind helpers:XamlHelpers.GetCreationDateString(ViewModel.CreationDate, ViewModel.PreferencesService.Prefer24HourTimeFormat), Mode=OneWay}" />
</StackPanel>
</Grid>
<AppBarToggleButton
x:Name="ReaderAiActionsToggleButton"
Grid.Column="2"
Checked="ReaderAiActionsToggleButton_Checked"
MinWidth="40"
HorizontalContentAlignment="Center"
LabelPosition="Collapsed"
ToolTipService.ToolTip="{x:Bind domain:Translator.Composer_AiActions}">
<AppBarToggleButton.Icon>
<FontIcon FontFamily="{StaticResource SymbolThemeFontFamily}" Glyph="&#xE945;" />
</AppBarToggleButton.Icon>
</AppBarToggleButton>
</Grid>
</CommandBar.Content>
</CommandBar>
@@ -409,8 +427,16 @@
</Grid>
</ScrollViewer>
<coreControls:AiActionsPanel
x:Name="ReaderAiActionsPanel"
Grid.Row="3"
Margin="0,8,0,0"
AvailableActions="Translate, Summarize"
HtmlHost="{x:Bind}"
Visibility="{x:Bind GetAiActionsPanelVisibility(ReaderAiActionsToggleButton.IsChecked), Mode=OneWay}" />
<!-- Attachments -->
<Grid Grid.Row="3">
<Grid Grid.Row="4">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
@@ -442,7 +468,7 @@
<InfoBar
x:Name="ImageLoadingDisabledMessage"
Grid.Row="4"
Grid.Row="5"
HorizontalContentAlignment="Stretch"
x:Load="{x:Bind ViewModel.IsImageRenderingDisabled, Mode=OneWay}"
IsOpen="True"
@@ -458,7 +484,7 @@
<ProgressBar
x:Name="DownloadingProgressBar"
Grid.Row="3"
Grid.Row="4"
Margin="12,1"
HorizontalAlignment="Stretch"
VerticalAlignment="Top"
@@ -1,6 +1,7 @@
using System;
using System.Linq;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using CommunityToolkit.Mvvm.Messaging;
using Microsoft.Extensions.DependencyInjection;
@@ -16,6 +17,7 @@ using Wino.Core.Domain.Interfaces;
using Wino.Core.Domain.Models.Printing;
using Wino.Mail.ViewModels.Data;
using Wino.Mail.WinUI;
using Wino.Mail.WinUI.Controls;
using Wino.Mail.WinUI.Extensions;
using Wino.Messaging.Client.Mails;
using Wino.Messaging.Client.Shell;
@@ -24,19 +26,24 @@ using Wino.Views.Abstract;
namespace Wino.Views.Mail;
public sealed partial class MailRenderingPage : MailRenderingPageAbstract,
IAiHtmlActionHost,
IRecipient<HtmlRenderingRequested>,
IRecipient<CancelRenderingContentRequested>,
IRecipient<ApplicationThemeChanged>
{
private readonly IPreferencesService _preferencesService = App.Current.Services.GetService<IPreferencesService>()!;
private readonly IMailDialogService _dialogService = App.Current.Services.GetService<IMailDialogService>()!;
private readonly IMimeFileService _mimeFileService = App.Current.Services.GetRequiredService<IMimeFileService>();
private bool isRenderingInProgress = false;
private bool? _lastAppliedDarkTheme;
private TaskCompletionSource<bool> DOMLoadedTask = new TaskCompletionSource<bool>();
private string _currentRenderedHtml = string.Empty;
public WebView2 GetWebView() => Chromium;
public Visibility GetAiActionsPanelVisibility(bool? isChecked) => isChecked == true ? Visibility.Visible : Visibility.Collapsed;
public MailRenderingPage()
{
InitializeComponent();
@@ -82,6 +89,7 @@ public sealed partial class MailRenderingPage : MailRenderingPageAbstract,
private async Task RenderInternalAsync(string htmlBody)
{
isRenderingInProgress = true;
_currentRenderedHtml = htmlBody ?? string.Empty;
await DOMLoadedTask.Task;
@@ -141,10 +149,63 @@ public sealed partial class MailRenderingPage : MailRenderingPageAbstract,
ViewModel.SaveHTMLasPDFFunc = null;
ViewModel.DirectPrintFuncAsync = null;
_currentRenderedHtml = string.Empty;
ReaderAiActionsPanel.CancelPendingOperation();
DisposeWebView2();
}
public Task<string?> GetCurrentHtmlAsync(CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
return Task.FromResult<string?>(_currentRenderedHtml);
}
public async Task ApplyHtmlResultAsync(string html, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
await RenderInternalAsync(html);
cancellationToken.ThrowIfCancellationRequested();
}
private async void ReaderAiActionsToggleButton_Checked(object sender, RoutedEventArgs e)
{
await ReaderAiActionsPanel.RefreshAvailabilityAsync();
}
public async Task<string?> TryGetCachedTranslationHtmlAsync(string languageCode, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
if (!ViewModel.CurrentMailAccountId.HasValue || !ViewModel.CurrentMailFileId.HasValue || string.IsNullOrWhiteSpace(languageCode))
{
return null;
}
return await _mimeFileService.GetTranslatedHtmlAsync(
ViewModel.CurrentMailAccountId.Value,
ViewModel.CurrentMailFileId.Value,
languageCode,
cancellationToken).ConfigureAwait(false);
}
public async Task SaveCachedTranslationHtmlAsync(string languageCode, string html, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
if (!ViewModel.CurrentMailAccountId.HasValue || !ViewModel.CurrentMailFileId.HasValue || string.IsNullOrWhiteSpace(languageCode))
{
return;
}
await _mimeFileService.SaveTranslatedHtmlAsync(
ViewModel.CurrentMailAccountId.Value,
ViewModel.CurrentMailFileId.Value,
languageCode,
html,
cancellationToken).ConfigureAwait(false);
}
private void DisposeWebView2()
{
if (Chromium == null) return;