8.7 KiB
8.7 KiB
Copilot Instructions for Wino-Mail Project
Project Overview
Wino Mail is a native Windows mail client targeting Windows 10 1809+ and Windows 11. The project is transitioning from UWP to WinUI 3 - always work with WinUI projects (Wino.Mail.WinUI, Wino.Core.WinUI), never edit the old Wino.Mail UWP project.
Key Technologies
- WinUI 3 for UI (previously UWP/WinUI 2)
- MVVM Toolkit (CommunityToolkit.Mvvm) for ViewModels with source generators
- Messenger pattern (WeakReferenceMessenger.Default) for event pub-sub throughout the codebase
- SQLite database stored in publisher cache folder (not local storage)
- WebView2 for mail rendering/composition with custom HTML/JavaScript editors
- MimeKit/MailKit for IMAP/SMTP operations
- Microsoft Graph SDK for Outlook synchronization
- Gmail API for Gmail synchronization
Solution Structure
Wino.Core.Domain → Entities, interfaces, translations, enums (shared contracts)
Wino.Core → Synchronization engine, authenticators, request processing
Wino.Services → Database, mail, folder, account services
Wino.Mail.ViewModels → Mail-specific ViewModels
Wino.Core.ViewModels → Shared ViewModels (settings, personalization)
Wino.Mail.WinUI → **Active WinUI 3 UI project** (use this)
Wino.Mail → **Deprecated UWP project** (DO NOT EDIT)
Architecture Patterns
Mail Synchronization Flow
- WinoRequestDelegator → Validates and delegates user actions (mark read, delete, move)
- WinoRequestProcessor → Batches requests using RequestComparer, queues to synchronizers
- Synchronizers (OutlookSynchronizer, GmailSynchronizer, ImapSynchronizer) → Execute batched operations
- ChangeProcessors (OutlookChangeProcessor, etc.) → Apply changes to local database
- Database updates trigger Messenger events (MailAddedMessage, MailUpdatedMessage, etc.)
Queue-Based Sync (New Pattern - See QUEUE_SYNC_IMPLEMENTATION.md)
- Initial sync now queues mail IDs first (MailItemQueue table), downloads metadata only (no MIME)
- MIME content downloaded on-demand when user opens mail
- Synchronizers override
QueueMailIdsForInitialSyncAsync(),DownloadMailsFromQueueAsync(),CreateMinimalMailCopyAsync() - Check
MailItemFolder.IsInitialSyncCompletedto determine sync state
Dependency Injection Setup
Services registered in extension methods across projects:
RegisterCoreServices()in Wino.Core/CoreContainerSetup.csRegisterSharedServices()in Wino.Services/ServicesContainerSetup.csRegisterCoreUWPServices()in CoreUWPContainerSetup.cs- ViewModels registered in App.xaml.cs with AddTransient/AddSingleton
Messenger Pattern (Event Pub-Sub)
- All ViewModels inherit from CoreBaseViewModel or MailBaseViewModel which implement IRecipient
- Register/unregister message handlers in
RegisterRecipients()/UnregisterRecipients() - Send messages via
WeakReferenceMessenger.Default.Send(new MessageType(...)) - Common messages: MailAddedMessage, MailUpdatedMessage, NavigationRequested, ThemeChanged
ViewModels Development Guidelines
Observable Properties - Critical Pattern
- ALWAYS use
public partialobservable properties with MVVM Toolkit source generators - NEVER use private fields with
[ObservableProperty]attribute - Correct:
[ObservableProperty] public partial string SearchQuery { get; set; } = string.Empty; - Incorrect:
[ObservableProperty] private string searchQuery = string.Empty; // WRONG - will not work
ViewModels Structure
- Inherit from MailBaseViewModel (for mail features) or CoreBaseViewModel (for shared features)
- Use
[RelayCommand]for command methods - source generator creates Command properties - Implement IRecipient for message handlers
- Use
IMailDialogServicefor Mail-related dialogs,IDialogServiceBasefor core dialogs - Call
RegisterRecipients()in constructor/OnNavigatedTo,UnregisterRecipients()in OnNavigatedFrom
Localization System
Translation Workflow (Custom T4-based System)
- Add English strings ONLY to
Wino.Core.Domain/Translations/en_US/resources.json - Build the project - source generators automatically create Translator properties
- Use
Translator.{PropertyName}in ViewModels, XAML (with x:Bind, OneTime mode) - NEVER edit other language files - Crowdin manages translations automatically
- NEVER hardcode user-facing strings
Usage Examples
// ViewModel
_dialogService.InfoBarMessage(Translator.Info_MissingFolderTitle, message);
// XAML
<TextBlock Text="{x:Bind Translator.Settings_Title, Mode=OneTime}" />
UI Data Binding and Converters
WinUI 3 Automatic Conversions
- NEVER create IValueConverter classes or add them to Converters.xaml
- NEVER use BoolToVisibilityConverter - WinUI 3 SDK automatically converts bool to Visibility
- Direct binding:
Visibility="{x:Bind IsVisible, Mode=OneWay}" - Register control events (for example
Loaded,Unloaded,SizeChanged,PointerEntered) in XAML markup, not with+=in.xaml.cs.
XamlHelpers for Complex Conversions
- ALWAYS use XamlHelpers static methods instead of converters
- Add xmlns:
xmlns:helpers="using:Wino.Helpers" - Usage:
{x:Bind helpers:XamlHelpers.ReverseBoolToVisibilityConverter(PropertyName), Mode=OneWay} - Available methods: ReverseBoolToVisibilityConverter, CountToBooleanConverter, BoolToSelectionMode, Base64ToBitmapImage
- Add new methods to XamlHelpers.cs when needed, don't create converters
WebView2 Mail Rendering
Architecture
- reader.html (Wino.Mail.WinUI/JS/) for reading mails
- editor.html for composing mails (uses Jodit editor, not Quill as originally planned)
- WebView2 uses virtual host mapping:
https://wino.mail/reader.html - JavaScript interop via
ExecuteScriptFunctionAsync()to call functions likeRenderHTML() - MIME content downloaded on-demand, not during sync
Key Patterns
- Set environment variables for WebView2 before initialization (overlay scrollbars, cache)
- Wait for DOMContentLoaded event before script execution
- Handle theme changes by updating editor CSS dynamically
- Cancel external navigation, open in browser via Launcher.LaunchUriAsync()
File Structure and Project Organization
Critical Rules
- NEVER edit files in Wino.Mail (UWP) project - it's deprecated
- ALWAYS work with Wino.Mail.WinUI for UI components
- Place ViewModels in Wino.Mail.ViewModels (mail-specific) or Wino.Core.ViewModels (shared)
- Create abstract base classes in Views/Abstract folders
- Mail-specific dialog services go in Wino.Mail.WinUI/Services
Database and Storage
- SQLite database in publisher cache folder (not app local storage)
- EML files stored in app local storage, referenced by MailCopy.FileId
- Paths resolved via MimeFileService.GetMimeMessagePath()
- Database entities in Wino.Core.Domain/Entities
Error Handling and User Feedback
Exception Handling Patterns
try {
await operation();
} catch (UnavailableSpecialFolderException ex) {
_dialogService.InfoBarMessage(title, message, InfoBarMessageType.Warning, buttonText, action);
} catch (NotImplementedException) {
_dialogService.ShowNotSupportedMessage();
}
Dialog Service Methods
InfoBarMessage()- simple notifications with optional action buttonShowConfirmationDialogAsync()- yes/no dialogsPickFilesAsync()- file selection- Always check for null/empty results from dialog operations
Code Style and Best Practices
- Use
varwhere type is obvious from right side - String interpolation over string.Format for simple cases
- Keep methods focused and single-responsibility
- Add XML documentation for public APIs
- Avoid introducing new NuGet packages - maximize use of existing libraries
- Wrap async operations in try-catch blocks
- Log errors via IWinoLogger but don't expose technical details to users
Development Workflow
Building and Running
- Open WinoMail.slnx in Visual Studio 2022+
- Target platforms: x86, x64, ARM64 (ARM32 being phased out)
- Minimum: Windows 10 1809, Target: Windows 11 22H2
- Set Wino.Mail.WinUI as startup project
Testing
- Test suite in Wino.Core.Tests
- Manual testing required for UI/WebView2 interactions
- Test synchronization with real accounts when modifying synchronizers
Common Pitfalls
- Forgetting to register ViewModels in App.xaml.cs RegisterViewModels()
- Not calling RegisterRecipients() for message handlers
- Using private fields with [ObservableProperty] (won't work - must be public partial)
- Creating IValueConverter classes instead of using XamlHelpers
- Editing UWP project files instead of WinUI equivalents
- Hardcoding strings instead of using Translator
- Forgetting to unregister Messenger recipients (memory leaks)