AI action panel improvements.
This commit is contained in:
@@ -33,7 +33,7 @@
|
||||
<PackageVersion Include="Microsoft.Identity.Client.Extensions.Msal" Version="4.82.1" />
|
||||
<PackageVersion Include="Microsoft.NETCore.UniversalWindowsPlatform" Version="6.2.14" />
|
||||
<PackageVersion Include="Microsoft.Xaml.Behaviors.WinUI.Managed" Version="3.0.1" />
|
||||
<PackageVersion Include="Wino.Mail.Contracts" Version="1.0.14" />
|
||||
<PackageVersion Include="Wino.Mail.Contracts" Version="1.0.15" />
|
||||
<PackageVersion Include="MimeKit" Version="4.15.1" />
|
||||
<PackageVersion Include="morelinq" Version="4.4.0" />
|
||||
<PackageVersion Include="Nito.AsyncEx" Version="5.1.2" />
|
||||
|
||||
@@ -67,6 +67,21 @@ public interface IPreferencesService : INotifyPropertyChanged
|
||||
/// </summary>
|
||||
bool IsWinoAccountButtonHidden { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Setting: Default target language code used for AI translation actions.
|
||||
/// </summary>
|
||||
string AiDefaultTranslationLanguageCode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Setting: Preferred target language code for AI summarize actions.
|
||||
/// </summary>
|
||||
string AiSummarizeLanguageCode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Setting: Preferred folder path used when saving AI summaries.
|
||||
/// </summary>
|
||||
string AiSummarySavePath { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Serializes the current syncable preferences snapshot.
|
||||
/// </summary>
|
||||
|
||||
@@ -19,7 +19,7 @@ public interface IWinoAccountApiClient
|
||||
Task<ApiEnvelope<JsonElement>> LogoutAsync(string refreshToken, CancellationToken cancellationToken = default);
|
||||
Task<ApiEnvelope<AuthUserDto>> GetCurrentUserAsync(CancellationToken cancellationToken = default);
|
||||
Task<ApiEnvelope<AiStatusResultDto>> GetAiStatusAsync(CancellationToken cancellationToken = default);
|
||||
Task<ApiEnvelope<AiTextResultDto>> SummarizeAsync(string html, CancellationToken cancellationToken = default);
|
||||
Task<ApiEnvelope<AiTextResultDto>> SummarizeAsync(string html, string targetLanguage, CancellationToken cancellationToken = default);
|
||||
Task<ApiEnvelope<AiTextResultDto>> TranslateAsync(string html, string targetLanguage, CancellationToken cancellationToken = default);
|
||||
Task<ApiEnvelope<AiTextResultDto>> RewriteAsync(string html, string mode, CancellationToken cancellationToken = default);
|
||||
Task<ApiEnvelope<WinoStoreCollectionsIdTicketInfo>> CreateCollectionsIdTicketAsync(CancellationToken cancellationToken = default);
|
||||
|
||||
@@ -24,7 +24,7 @@ public interface IWinoAccountProfileService
|
||||
Task<bool> HasActiveAccountAsync();
|
||||
Task<ApiEnvelope<AuthUserDto>> GetCurrentUserAsync(CancellationToken cancellationToken = default);
|
||||
Task<ApiEnvelope<AiStatusResultDto>> GetAiStatusAsync(CancellationToken cancellationToken = default);
|
||||
Task<ApiEnvelope<AiTextResultDto>> SummarizeAsync(string html, CancellationToken cancellationToken = default);
|
||||
Task<ApiEnvelope<AiTextResultDto>> SummarizeAsync(string html, string targetLanguage, CancellationToken cancellationToken = default);
|
||||
Task<ApiEnvelope<AiTextResultDto>> TranslateAsync(string html, string targetLanguage, CancellationToken cancellationToken = default);
|
||||
Task<ApiEnvelope<AiTextResultDto>> RewriteAsync(string html, string mode, CancellationToken cancellationToken = default);
|
||||
Task<ApiEnvelope<JsonElement>> SyncStoreEntitlementsAsync(CancellationToken cancellationToken = default);
|
||||
|
||||
@@ -724,6 +724,16 @@
|
||||
"SettingsAppPreferences_HideWinoAccountButton_Description": "Hide the title bar profile button that opens the Wino account flyout.",
|
||||
"SettingsAppPreferences_StoreUpdateNotifications_Title": "Store update notifications",
|
||||
"SettingsAppPreferences_StoreUpdateNotifications_Description": "Show notifications and footer actions when a Microsoft Store update is available.",
|
||||
"SettingsAppPreferences_AiActions_Title": "AI actions",
|
||||
"SettingsAppPreferences_AiActions_Description": "Choose default AI languages and where summaries should be saved.",
|
||||
"SettingsAppPreferences_AiDefaultTranslationLanguage_Title": "Default translation language",
|
||||
"SettingsAppPreferences_AiDefaultTranslationLanguage_Description": "Pick the default target language used by AI translate actions.",
|
||||
"SettingsAppPreferences_AiSummarizeLanguage_Title": "Summarize language",
|
||||
"SettingsAppPreferences_AiSummarizeLanguage_Description": "Choose the preferred summarize language for future AI summary output.",
|
||||
"SettingsAppPreferences_AiSummarySavePath_Title": "Default summary save path",
|
||||
"SettingsAppPreferences_AiSummarySavePath_Description": "Choose the folder Wino should use by default when saving AI summaries.",
|
||||
"SettingsAppPreferences_AiSummarySavePath_Placeholder": "Use the system default save location",
|
||||
"SettingsAppPreferences_AiSummarySavePath_InvalidHint": "This folder does not exist. The default save location will be used for summaries.",
|
||||
"SettingsAutoSelectNextItem_Description": "Select the next item after you delete or move a mail.",
|
||||
"SettingsAutoSelectNextItem_Title": "Auto select next item",
|
||||
"SettingsAvailableThemes_Description": "Select a theme from Wino's own collection for your taste or apply your own themes.",
|
||||
@@ -1071,7 +1081,7 @@
|
||||
"AiActions_SignedOutDescription": "Translate, rewrite, and summarize emails with AI after signing in to your Wino Account and activating the AI Pack add-on.",
|
||||
"AiActions_NoPackTitle": "AI Pack required",
|
||||
"AiActions_NoPackDescription": "You're signed in, but AI Pack is not active yet. Purchase it to use Wino's AI translation, rewrite, and summarize tools.",
|
||||
"AiActions_UsageSummary": "{0} of {1} requests used this month.",
|
||||
"AiActions_UsageSummary": "{0} of {1} credits used this month.",
|
||||
"Composer_AiRewritePolite": "Make it polite",
|
||||
"Composer_AiRewritePoliteDescription": "Softens the wording while keeping the same intent.",
|
||||
"Composer_AiRewriteAngry": "Make it angry",
|
||||
@@ -1243,7 +1253,7 @@
|
||||
"WinoAccount_Management_AiPackPromoTitle": "Unlock AI Pack",
|
||||
"WinoAccount_Management_AiPackPromoDescription": "Supercharge your email workflow with AI-powered tools. Translate messages into 50+ languages, rewrite for clarity and tone, and get instant summaries of long threads.",
|
||||
"WinoAccount_Management_AiPackPromoPrice": "$4.99 / mo",
|
||||
"WinoAccount_Management_AiPackPromoRequests": "1,000 requests",
|
||||
"WinoAccount_Management_AiPackPromoRequests": "1,000 credits",
|
||||
"WinoAccount_Management_AiPackGetButton": "Get AI Pack",
|
||||
"WinoAddOn_AI_PACK_Name": "Wino AI Pack",
|
||||
"WinoAddOn_AI_PACK_Description": "AI-powered tools for translate, rewrite, and summarize actions in Wino Mail.",
|
||||
@@ -1256,7 +1266,7 @@
|
||||
"WinoAccount_Management_StoreSyncFailed": "Your purchase finished, but Wino could not refresh your account benefits yet. Please try again in a moment.",
|
||||
"WinoAccount_Management_AiPackSubscriptionActive": "Your subscription is active",
|
||||
"WinoAccount_Management_AiPackRenews": "Renews {0:d}",
|
||||
"WinoAccount_Management_AiPackRequestsUsed": "Requests used this month",
|
||||
"WinoAccount_Management_AiPackRequestsUsed": "Credits used this month",
|
||||
"WinoAccount_Management_AiPackResets": "Resets {0:d}",
|
||||
"WinoAccount_Management_AiPackUsageLoadFailed": "We had issues loading your AI usage balance.",
|
||||
"WinoAccount_Management_AiPackFeatureTranslate": "Translate",
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using Wino.Core.Domain;
|
||||
using Wino.Core.Domain.Enums;
|
||||
using Wino.Core.Domain.Interfaces;
|
||||
using Wino.Core.Domain.Models.Ai;
|
||||
using Wino.Core.Domain.Models.Navigation;
|
||||
using Wino.Core.Domain.Models.Translations;
|
||||
|
||||
@@ -16,12 +18,14 @@ public partial class AppPreferencesPageViewModel : MailBaseViewModel
|
||||
IMailDialogService dialogService,
|
||||
IPreferencesService preferencesService,
|
||||
IStartupBehaviorService startupBehaviorService,
|
||||
ITranslationService translationService)
|
||||
ITranslationService translationService,
|
||||
IAiActionOptionsService aiActionOptionsService)
|
||||
{
|
||||
_dialogService = dialogService;
|
||||
PreferencesService = preferencesService;
|
||||
_startupBehaviorService = startupBehaviorService;
|
||||
_translationService = translationService;
|
||||
_aiActionOptionsService = aiActionOptionsService;
|
||||
|
||||
SearchModes =
|
||||
[
|
||||
@@ -40,6 +44,7 @@ public partial class AppPreferencesPageViewModel : MailBaseViewModel
|
||||
SelectedDefaultSearchMode = SearchModes[(int)PreferencesService.DefaultSearchMode];
|
||||
SelectedDefaultApplicationMode = ApplicationModes[(int)PreferencesService.DefaultApplicationMode];
|
||||
EmailSyncIntervalMinutes = PreferencesService.EmailSyncIntervalMinutes;
|
||||
SummarySavePath = PreferencesService.AiSummarySavePath;
|
||||
}
|
||||
|
||||
public IPreferencesService PreferencesService { get; }
|
||||
@@ -56,6 +61,21 @@ public partial class AppPreferencesPageViewModel : MailBaseViewModel
|
||||
[ObservableProperty]
|
||||
public partial AppLanguageModel SelectedLanguage { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
public partial List<AiTranslateLanguageOption> AvailableAiLanguages { get; set; } = [];
|
||||
|
||||
[ObservableProperty]
|
||||
public partial AiTranslateLanguageOption SelectedDefaultTranslationLanguage { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
public partial AiTranslateLanguageOption SelectedSummarizeLanguage { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
public partial string SummarySavePath { get; set; } = string.Empty;
|
||||
|
||||
[ObservableProperty]
|
||||
public partial bool HasInvalidSummarySavePath { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedFor(nameof(IsStartupBehaviorDisabled))]
|
||||
[NotifyPropertyChangedFor(nameof(IsStartupBehaviorEnabled))]
|
||||
@@ -64,7 +84,9 @@ public partial class AppPreferencesPageViewModel : MailBaseViewModel
|
||||
private readonly IMailDialogService _dialogService;
|
||||
private readonly IStartupBehaviorService _startupBehaviorService;
|
||||
private readonly ITranslationService _translationService;
|
||||
private readonly IAiActionOptionsService _aiActionOptionsService;
|
||||
private bool _isLanguageInitialized;
|
||||
private bool _isAiPreferencesInitialized;
|
||||
private int _emailSyncIntervalMinutes;
|
||||
private string _selectedDefaultSearchMode;
|
||||
private string _selectedDefaultApplicationMode;
|
||||
@@ -110,6 +132,31 @@ public partial class AppPreferencesPageViewModel : MailBaseViewModel
|
||||
_ = _translationService.InitializeLanguageAsync(value.Language);
|
||||
}
|
||||
|
||||
partial void OnSelectedDefaultTranslationLanguageChanged(AiTranslateLanguageOption value)
|
||||
{
|
||||
if (!_isAiPreferencesInitialized || value == null)
|
||||
return;
|
||||
|
||||
PreferencesService.AiDefaultTranslationLanguageCode = value.Code;
|
||||
}
|
||||
|
||||
partial void OnSelectedSummarizeLanguageChanged(AiTranslateLanguageOption value)
|
||||
{
|
||||
if (!_isAiPreferencesInitialized || value == null)
|
||||
return;
|
||||
|
||||
PreferencesService.AiSummarizeLanguageCode = value.Code;
|
||||
}
|
||||
|
||||
partial void OnSummarySavePathChanged(string value)
|
||||
{
|
||||
if (!_isAiPreferencesInitialized)
|
||||
return;
|
||||
|
||||
PreferencesService.AiSummarySavePath = value ?? string.Empty;
|
||||
RefreshSummarySavePathState();
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task ToggleStartupBehaviorAsync()
|
||||
{
|
||||
@@ -157,15 +204,56 @@ public partial class AppPreferencesPageViewModel : MailBaseViewModel
|
||||
}
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task BrowseSummarySavePathAsync()
|
||||
{
|
||||
var pickedPath = await _dialogService.PickWindowsFolderAsync();
|
||||
if (string.IsNullOrWhiteSpace(pickedPath))
|
||||
return;
|
||||
|
||||
await ExecuteUIThread(() => SummarySavePath = pickedPath);
|
||||
}
|
||||
|
||||
public override async void OnNavigatedTo(NavigationMode mode, object parameters)
|
||||
{
|
||||
base.OnNavigatedTo(mode, parameters);
|
||||
|
||||
AvailableLanguages = _translationService.GetAvailableLanguages();
|
||||
var availableLanguages = _translationService.GetAvailableLanguages();
|
||||
var availableAiLanguages = new List<AiTranslateLanguageOption>(_aiActionOptionsService.GetTranslateLanguageOptions());
|
||||
var startupBehaviorResult = await _startupBehaviorService.GetCurrentStartupBehaviorAsync();
|
||||
|
||||
await ExecuteUIThread(() =>
|
||||
{
|
||||
AvailableLanguages = availableLanguages;
|
||||
SelectedLanguage = AvailableLanguages.Find(language => language.Language == PreferencesService.CurrentLanguage)
|
||||
?? (AvailableLanguages.Count > 0 ? AvailableLanguages[0] : null);
|
||||
_isLanguageInitialized = true;
|
||||
|
||||
StartupBehaviorResult = await _startupBehaviorService.GetCurrentStartupBehaviorAsync();
|
||||
AvailableAiLanguages = availableAiLanguages;
|
||||
SelectedDefaultTranslationLanguage = FindAiLanguageOption(PreferencesService.AiDefaultTranslationLanguageCode)
|
||||
?? FindAiLanguageOption("en-US")
|
||||
?? (AvailableAiLanguages.Count > 0 ? AvailableAiLanguages[0] : null);
|
||||
SelectedSummarizeLanguage = FindAiLanguageOption(PreferencesService.AiSummarizeLanguageCode)
|
||||
?? FindAiLanguageOption("en-US")
|
||||
?? (AvailableAiLanguages.Count > 0 ? AvailableAiLanguages[0] : null);
|
||||
SummarySavePath = PreferencesService.AiSummarySavePath;
|
||||
RefreshSummarySavePathState();
|
||||
_isAiPreferencesInitialized = true;
|
||||
|
||||
StartupBehaviorResult = startupBehaviorResult;
|
||||
});
|
||||
}
|
||||
|
||||
private AiTranslateLanguageOption FindAiLanguageOption(string languageCode)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(languageCode))
|
||||
return null;
|
||||
|
||||
return AvailableAiLanguages.Find(option => option.Code == languageCode);
|
||||
}
|
||||
|
||||
private void RefreshSummarySavePathState()
|
||||
{
|
||||
HasInvalidSummarySavePath = !string.IsNullOrWhiteSpace(SummarySavePath) && !Directory.Exists(SummarySavePath);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,18 +143,16 @@
|
||||
Style="{StaticResource AccentButtonStyle}" />
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel
|
||||
<Grid
|
||||
x:Name="ReadyPanel"
|
||||
Spacing="8"
|
||||
ColumnSpacing="12"
|
||||
Visibility="Collapsed">
|
||||
<!-- Row 1: Action tabs + usage -->
|
||||
<Grid ColumnSpacing="12">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!-- Column 1: Action tabs -->
|
||||
<controls:Segmented
|
||||
x:Name="ActionSelector"
|
||||
Height="30"
|
||||
@@ -187,27 +185,8 @@
|
||||
</controls:SegmentedItem>
|
||||
</controls:Segmented>
|
||||
|
||||
<StackPanel
|
||||
Grid.Column="2"
|
||||
VerticalAlignment="Center"
|
||||
Orientation="Horizontal"
|
||||
Spacing="8">
|
||||
<ProgressBar
|
||||
x:Name="UsageProgressBar"
|
||||
Width="100"
|
||||
VerticalAlignment="Center"
|
||||
Maximum="1000"
|
||||
Value="0" />
|
||||
<TextBlock
|
||||
x:Name="UsageSummaryTextBlock"
|
||||
VerticalAlignment="Center"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
Style="{StaticResource CaptionTextBlockStyle}" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
<!-- Row 2: Action-specific options -->
|
||||
<Grid ColumnSpacing="8">
|
||||
<!-- Column 2: Action-specific options -->
|
||||
<Grid Grid.Column="1" ColumnSpacing="8">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
@@ -284,11 +263,24 @@
|
||||
x:Name="SummarizeOptionsPanel"
|
||||
Spacing="8"
|
||||
Visibility="Collapsed">
|
||||
|
||||
<StackPanel Orientation="Horizontal" Spacing="8">
|
||||
<TextBlock
|
||||
VerticalAlignment="Center"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
Text="{x:Bind domain:Translator.Composer_AiSummarizeDescription, Mode=OneWay}" />
|
||||
<StackPanel Orientation="Horizontal" Spacing="4">
|
||||
Text="{x:Bind domain:Translator.Composer_AiTranslateLanguage, Mode=OneWay}" />
|
||||
<ComboBox
|
||||
x:Name="SummarizeLanguageComboBox"
|
||||
MinWidth="120"
|
||||
SelectionChanged="SummarizeLanguageComboBox_SelectionChanged">
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate x:DataType="models:AiTranslateLanguageOption">
|
||||
<TextBlock Text="{x:Bind Label}" />
|
||||
</DataTemplate>
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
|
||||
<Button
|
||||
x:Name="RunSummarizeButton"
|
||||
HorizontalAlignment="Left"
|
||||
@@ -306,10 +298,30 @@
|
||||
ToolTipService.ToolTip="{x:Bind domain:Translator.Composer_AiSummarize, Mode=OneWay}"
|
||||
Visibility="Collapsed" />
|
||||
</StackPanel>
|
||||
|
||||
</StackPanel>
|
||||
|
||||
</Grid>
|
||||
|
||||
<!-- Column 3: Progress -->
|
||||
<StackPanel
|
||||
Grid.Column="2"
|
||||
VerticalAlignment="Center"
|
||||
Orientation="Horizontal"
|
||||
Spacing="8">
|
||||
<ProgressBar
|
||||
x:Name="UsageProgressBar"
|
||||
Width="100"
|
||||
VerticalAlignment="Center"
|
||||
Maximum="1000"
|
||||
Value="0" />
|
||||
<TextBlock
|
||||
x:Name="UsageSummaryTextBlock"
|
||||
VerticalAlignment="Center"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
Style="{StaticResource CaptionTextBlockStyle}" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
@@ -25,6 +25,7 @@ public sealed partial class AiActionsPanel : UserControl, IDisposable
|
||||
private readonly IStoreManagementService _storeManagementService = App.Current.Services.GetRequiredService<IStoreManagementService>();
|
||||
private readonly IMailDialogService _dialogService = App.Current.Services.GetRequiredService<IMailDialogService>();
|
||||
private readonly IAiActionOptionsService _optionsService = App.Current.Services.GetRequiredService<IAiActionOptionsService>();
|
||||
private readonly IPreferencesService _preferencesService = App.Current.Services.GetRequiredService<IPreferencesService>();
|
||||
|
||||
private bool _disposedValue;
|
||||
private bool _isRefreshing;
|
||||
@@ -43,6 +44,8 @@ public sealed partial class AiActionsPanel : UserControl, IDisposable
|
||||
|
||||
public AiTranslateLanguageOption? SelectedTranslateLanguageOption { get; set; }
|
||||
|
||||
public AiTranslateLanguageOption? SelectedSummarizeLanguageOption { get; set; }
|
||||
|
||||
public AiRewriteModeOption? SelectedRewriteModeOption { get; set; }
|
||||
|
||||
public AiActionsPanel()
|
||||
@@ -90,19 +93,33 @@ public sealed partial class AiActionsPanel : UserControl, IDisposable
|
||||
{
|
||||
// Save current selections before replacing ItemsSource (which clears SelectedItem).
|
||||
var previousTranslateCode = SelectedTranslateLanguageOption?.Code;
|
||||
var previousSummarizeCode = SelectedSummarizeLanguageOption?.Code;
|
||||
var previousRewriteMode = SelectedRewriteModeOption?.Mode;
|
||||
var preferredTranslateCode = string.IsNullOrWhiteSpace(previousTranslateCode)
|
||||
? _preferencesService.AiDefaultTranslationLanguageCode
|
||||
: previousTranslateCode;
|
||||
var preferredSummarizeCode = string.IsNullOrWhiteSpace(previousSummarizeCode)
|
||||
? _preferencesService.AiSummarizeLanguageCode
|
||||
: previousSummarizeCode;
|
||||
|
||||
_translateOptions = _optionsService.GetTranslateLanguageOptions();
|
||||
_rewriteOptions = _optionsService.GetRewriteModeOptions();
|
||||
|
||||
TranslateLanguageComboBox.ItemsSource = _translateOptions;
|
||||
SummarizeLanguageComboBox.ItemsSource = _translateOptions;
|
||||
RewriteModeComboBox.ItemsSource = _rewriteOptions;
|
||||
|
||||
// Restore selection by matching on value, falling back to first item.
|
||||
SelectedTranslateLanguageOption = FindOption(_translateOptions, o => o.Code == previousTranslateCode) ?? (_translateOptions.Count > 0 ? _translateOptions[0] : null);
|
||||
SelectedTranslateLanguageOption = FindOption(_translateOptions, o => o.Code == preferredTranslateCode)
|
||||
?? FindOption(_translateOptions, o => o.Code == "en-US")
|
||||
?? (_translateOptions.Count > 0 ? _translateOptions[0] : null);
|
||||
SelectedSummarizeLanguageOption = FindOption(_translateOptions, o => o.Code == preferredSummarizeCode)
|
||||
?? FindOption(_translateOptions, o => o.Code == "en-US")
|
||||
?? (_translateOptions.Count > 0 ? _translateOptions[0] : null);
|
||||
SelectedRewriteModeOption = FindOption(_rewriteOptions, o => o.Mode == previousRewriteMode) ?? (_rewriteOptions.Count > 0 ? _rewriteOptions[0] : null);
|
||||
|
||||
TranslateLanguageComboBox.SelectedItem = SelectedTranslateLanguageOption;
|
||||
SummarizeLanguageComboBox.SelectedItem = SelectedSummarizeLanguageOption;
|
||||
RewriteModeComboBox.SelectedItem = SelectedRewriteModeOption;
|
||||
UpdateRewriteOptionState();
|
||||
}
|
||||
@@ -293,6 +310,7 @@ public sealed partial class AiActionsPanel : UserControl, IDisposable
|
||||
BusyProgressBar.Visibility = isBusy ? Visibility.Visible : Visibility.Collapsed;
|
||||
ActionSelector.IsEnabled = !isBusy;
|
||||
TranslateLanguageComboBox.IsEnabled = !isBusy;
|
||||
SummarizeLanguageComboBox.IsEnabled = !isBusy;
|
||||
RewriteModeComboBox.IsEnabled = !isBusy;
|
||||
CustomRewriteTextBox.IsEnabled = !isBusy;
|
||||
RunTranslateButton.IsEnabled = !isBusy;
|
||||
@@ -426,6 +444,7 @@ public sealed partial class AiActionsPanel : UserControl, IDisposable
|
||||
if (TranslateLanguageComboBox.SelectedItem is AiTranslateLanguageOption option)
|
||||
{
|
||||
SelectedTranslateLanguageOption = option;
|
||||
_preferencesService.AiDefaultTranslationLanguageCode = option.Code;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -438,6 +457,15 @@ public sealed partial class AiActionsPanel : UserControl, IDisposable
|
||||
}
|
||||
}
|
||||
|
||||
private void SummarizeLanguageComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
if (SummarizeLanguageComboBox.SelectedItem is AiTranslateLanguageOption option)
|
||||
{
|
||||
SelectedSummarizeLanguageOption = option;
|
||||
_preferencesService.AiSummarizeLanguageCode = option.Code;
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateRewriteOptionState()
|
||||
{
|
||||
var isCustom = SelectedRewriteModeOption?.IsCustom ?? false;
|
||||
@@ -500,6 +528,12 @@ public sealed partial class AiActionsPanel : UserControl, IDisposable
|
||||
return;
|
||||
}
|
||||
|
||||
if (action == AiActionType.Summarize && SelectedSummarizeLanguageOption == null)
|
||||
{
|
||||
_dialogService.InfoBarMessage(Translator.Composer_AiErrorTitle, Translator.WinoAccount_Error_ValidationFailed, InfoBarMessageType.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
if (action == AiActionType.Translate)
|
||||
{
|
||||
var cachedTranslation = await HtmlHost.TryGetCachedTranslationHtmlAsync(SelectedTranslateLanguageOption?.Code ?? string.Empty, cancellationToken).ConfigureAwait(true);
|
||||
@@ -539,7 +573,7 @@ public sealed partial class AiActionsPanel : UserControl, IDisposable
|
||||
{
|
||||
AiActionType.Translate => await _profileService.TranslateAsync(html, SelectedTranslateLanguageOption?.Code ?? string.Empty, cancellationToken).ConfigureAwait(true),
|
||||
AiActionType.Rewrite => await _profileService.RewriteAsync(html, ResolveRewriteMode(), cancellationToken).ConfigureAwait(true),
|
||||
AiActionType.Summarize => await _profileService.SummarizeAsync(html, cancellationToken).ConfigureAwait(true),
|
||||
AiActionType.Summarize => await _profileService.SummarizeAsync(html, SelectedSummarizeLanguageOption?.Code ?? string.Empty, cancellationToken).ConfigureAwait(true),
|
||||
_ => ApiEnvelope<AiTextResultDto>.Failure(ApiErrorCodes.ValidationFailed)
|
||||
};
|
||||
|
||||
@@ -684,7 +718,11 @@ public sealed partial class AiActionsPanel : UserControl, IDisposable
|
||||
|
||||
try
|
||||
{
|
||||
var path = await _dialogService.PickFilePathAsync(HtmlHost.GetSuggestedSummaryFileName());
|
||||
var configuredSummarySavePath = _preferencesService.AiSummarySavePath;
|
||||
var path = !string.IsNullOrWhiteSpace(configuredSummarySavePath) && Directory.Exists(configuredSummarySavePath)
|
||||
? Path.Combine(configuredSummarySavePath, HtmlHost.GetSuggestedSummaryFileName())
|
||||
: await _dialogService.PickFilePathAsync(HtmlHost.GetSuggestedSummaryFileName());
|
||||
|
||||
if (string.IsNullOrWhiteSpace(path))
|
||||
{
|
||||
args.Cancel = true;
|
||||
|
||||
@@ -389,6 +389,24 @@ public class PreferencesService(IConfigurationService configurationService) : Ob
|
||||
set => SetPropertyAndSave(nameof(IsWinoAccountButtonHidden), value);
|
||||
}
|
||||
|
||||
public string AiDefaultTranslationLanguageCode
|
||||
{
|
||||
get => _configurationService.Get(nameof(AiDefaultTranslationLanguageCode), "en-US");
|
||||
set => SetPropertyAndSave(nameof(AiDefaultTranslationLanguageCode), string.IsNullOrWhiteSpace(value) ? "en-US" : value.Trim());
|
||||
}
|
||||
|
||||
public string AiSummarizeLanguageCode
|
||||
{
|
||||
get => _configurationService.Get(nameof(AiSummarizeLanguageCode), "en-US");
|
||||
set => SetPropertyAndSave(nameof(AiSummarizeLanguageCode), string.IsNullOrWhiteSpace(value) ? "en-US" : value.Trim());
|
||||
}
|
||||
|
||||
public string AiSummarySavePath
|
||||
{
|
||||
get => _configurationService.Get(nameof(AiSummarySavePath), string.Empty);
|
||||
set => SetPropertyAndSave(nameof(AiSummarySavePath), value?.Trim() ?? string.Empty);
|
||||
}
|
||||
|
||||
public WinoApplicationMode DefaultApplicationMode
|
||||
{
|
||||
get
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:domain="using:Wino.Core.Domain"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:models="using:Wino.Core.Domain.Models.Ai"
|
||||
xmlns:muxc="using:Microsoft.UI.Xaml.Controls"
|
||||
xmlns:translations="using:Wino.Core.Domain.Models.Translations"
|
||||
mc:Ignorable="d">
|
||||
@@ -74,6 +75,67 @@
|
||||
<FontIcon FontFamily="{StaticResource SymbolThemeFontFamily}" Glyph="" />
|
||||
</controls:SettingsCard.HeaderIcon>
|
||||
</controls:SettingsCard>
|
||||
|
||||
<controls:SettingsExpander
|
||||
Description="{x:Bind domain:Translator.SettingsAppPreferences_AiActions_Description}"
|
||||
Header="{x:Bind domain:Translator.SettingsAppPreferences_AiActions_Title}"
|
||||
IsExpanded="True">
|
||||
<controls:SettingsExpander.HeaderIcon>
|
||||
<FontIcon FontFamily="{StaticResource SymbolThemeFontFamily}" Glyph="" />
|
||||
</controls:SettingsExpander.HeaderIcon>
|
||||
<controls:SettingsExpander.Items>
|
||||
<controls:SettingsCard Description="{x:Bind domain:Translator.SettingsAppPreferences_AiDefaultTranslationLanguage_Description}" Header="{x:Bind domain:Translator.SettingsAppPreferences_AiDefaultTranslationLanguage_Title}">
|
||||
<ComboBox ItemsSource="{x:Bind ViewModel.AvailableAiLanguages, Mode=OneWay}" SelectedItem="{x:Bind ViewModel.SelectedDefaultTranslationLanguage, Mode=TwoWay}">
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate x:DataType="models:AiTranslateLanguageOption">
|
||||
<TextBlock Text="{x:Bind Label}" />
|
||||
</DataTemplate>
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
</controls:SettingsCard>
|
||||
|
||||
<controls:SettingsCard Description="{x:Bind domain:Translator.SettingsAppPreferences_AiSummarizeLanguage_Description}" Header="{x:Bind domain:Translator.SettingsAppPreferences_AiSummarizeLanguage_Title}">
|
||||
<ComboBox ItemsSource="{x:Bind ViewModel.AvailableAiLanguages, Mode=OneWay}" SelectedItem="{x:Bind ViewModel.SelectedSummarizeLanguage, Mode=TwoWay}">
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate x:DataType="models:AiTranslateLanguageOption">
|
||||
<TextBlock Text="{x:Bind Label}" />
|
||||
</DataTemplate>
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
</controls:SettingsCard>
|
||||
|
||||
<controls:SettingsCard
|
||||
Description="{x:Bind domain:Translator.SettingsAppPreferences_AiSummarySavePath_Description}"
|
||||
Header="{x:Bind domain:Translator.SettingsAppPreferences_AiSummarySavePath_Title}"
|
||||
HorizontalContentAlignment="Stretch"
|
||||
VerticalContentAlignment="Stretch"
|
||||
ContentAlignment="Vertical">
|
||||
<StackPanel Spacing="8">
|
||||
<Grid ColumnSpacing="12">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBox
|
||||
Grid.Column="0"
|
||||
IsReadOnly="True"
|
||||
PlaceholderText="{x:Bind domain:Translator.SettingsAppPreferences_AiSummarySavePath_Placeholder}"
|
||||
Text="{x:Bind ViewModel.SummarySavePath, Mode=OneWay}" />
|
||||
<Button
|
||||
Grid.Column="1"
|
||||
Command="{x:Bind ViewModel.BrowseSummarySavePathCommand}"
|
||||
Content="{x:Bind domain:Translator.Buttons_Browse}" />
|
||||
</Grid>
|
||||
<TextBlock
|
||||
Foreground="{ThemeResource SystemFillColorCautionBrush}"
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
Text="{x:Bind domain:Translator.SettingsAppPreferences_AiSummarySavePath_InvalidHint}"
|
||||
TextWrapping="WrapWholeWords"
|
||||
Visibility="{x:Bind ViewModel.HasInvalidSummarySavePath, Mode=OneWay}" />
|
||||
</StackPanel>
|
||||
</controls:SettingsCard>
|
||||
</controls:SettingsExpander.Items>
|
||||
</controls:SettingsExpander>
|
||||
</StackPanel>
|
||||
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
|
||||
@@ -108,11 +108,11 @@ public sealed class WinoAccountApiClient : IWinoAccountApiClient, IDisposable
|
||||
public Task<ApiEnvelope<AiStatusResultDto>> GetAiStatusAsync(CancellationToken cancellationToken = default)
|
||||
=> SendAuthorizedRequestAsync("api/v1/ai/status", WinoAccountApiJsonContext.Default.ApiEnvelopeAiStatusResultDto, cancellationToken);
|
||||
|
||||
public Task<ApiEnvelope<AiTextResultDto>> SummarizeAsync(string html, CancellationToken cancellationToken = default)
|
||||
public Task<ApiEnvelope<AiTextResultDto>> SummarizeAsync(string html, string targetLanguage, CancellationToken cancellationToken = default)
|
||||
=> SendAuthorizedRequestAsync(
|
||||
HttpMethod.Post,
|
||||
"api/v1/ai/summarize",
|
||||
new SummarizeRequest(html),
|
||||
new SummarizeRequest(html, targetLanguage),
|
||||
WinoAccountApiJsonContext.Default.SummarizeRequest,
|
||||
WinoAccountApiJsonContext.Default.ApiEnvelopeAiTextResultDto,
|
||||
cancellationToken);
|
||||
|
||||
@@ -213,8 +213,8 @@ public sealed class WinoAccountProfileService : BaseDatabaseService, IWinoAccoun
|
||||
return response;
|
||||
}
|
||||
|
||||
public async Task<ApiEnvelope<AiTextResultDto>> SummarizeAsync(string html, CancellationToken cancellationToken = default)
|
||||
=> await ExecuteAiOperationAsync(account => _apiClient.SummarizeAsync(html, cancellationToken), "summarize", cancellationToken).ConfigureAwait(false);
|
||||
public async Task<ApiEnvelope<AiTextResultDto>> SummarizeAsync(string html, string targetLanguage, CancellationToken cancellationToken = default)
|
||||
=> await ExecuteAiOperationAsync(account => _apiClient.SummarizeAsync(html, targetLanguage, cancellationToken), "summarize", cancellationToken).ConfigureAwait(false);
|
||||
|
||||
public async Task<ApiEnvelope<AiTextResultDto>> TranslateAsync(string html, string targetLanguage, CancellationToken cancellationToken = default)
|
||||
=> await ExecuteAiOperationAsync(account => _apiClient.TranslateAsync(html, targetLanguage, cancellationToken), "translate", cancellationToken).ConfigureAwait(false);
|
||||
|
||||
Reference in New Issue
Block a user