# Cross-Platform Abstraction Layer **Referenced Files in This Document** - [IDispatcher.cs](file://ClassicAssist.Shared/IDispatcher.cs) - [IUIInvoker.cs](file://ClassicAssist.Shared/IUIInvoker.cs) - [WPFDispatcher.cs](file://ClassicAssist/WPFDispatcher.cs) - [WPFUIInvoker.cs](file://ClassicAssist/WPFUIInvoker.cs) - [AvaloniaDispatcher.cs](file://ClassicAssist.Avalonia/AvaloniaDispatcher.cs) - [AvaloniaUIInvoker.cs](file://ClassicAssist.Avalonia/AvaloniaUIInvoker.cs) - [Engine.cs (Shared)](file://ClassicAssist.Shared/Engine.cs) - [Engine.cs (WPF)](file://ClassicAssist/Engine.cs) - [Engine.cs (Avalonia)](file://ClassicAssist.Avalonia/Engine.cs) - [Program.cs (Avalonia)](file://ClassicAssist.Avalonia/Program.cs) - [App.axaml.cs (Avalonia)](file://ClassicAssist.Avalonia/App.axaml.cs) - [MessageBoxProvider.cs](file://ClassicAssist.Shared/Misc/MessageBoxProvider.cs) - [AvaloniaMessageBoxProvider.cs](file://ClassicAssist.Avalonia/Misc/AvaloniaMessageBoxProvider.cs) - [WPFMessageBoxProvider.cs](file://ClassicAssist/UI/Misc/WPFMessageBoxProvider.cs) - [NativeMethods.cs](file://ClassicAssist.Shared/Misc/NativeMethods.cs) - [PlatformDetector.cs](file://ClassicAssist.Shared/Engine/PlatformDetector.cs) ## Table of Contents 1. [Introduction](#introduction) 2. [Project Structure](#project-structure) 3. [Core Components](#core-components) 4. [Architecture Overview](#architecture-overview) 5. [Detailed Component Analysis](#detailed-component-analysis) 6. [Dependency Analysis](#dependency-analysis) 7. [Performance Considerations](#performance-considerations) 8. [Troubleshooting Guide](#troubleshooting-guide) 9. [Conclusion](#conclusion) 10. [Appendices](#appendices) ## Introduction This document explains the cross-platform abstraction layer that enables ClassicAssist to run on Windows, Linux, and macOS. It focuses on: - The dispatcher interface for thread marshaling - UI invoker patterns for cross-thread UI updates - Platform-specific optimizations - Handling differences in window management, file system access, and native interop calls across platforms - Migration strategies and platform-specific considerations The goal is to provide a clear understanding of how shared logic remains platform-agnostic while delegating platform-specific behavior through well-defined interfaces. ## Project Structure At a high level, the project separates shared logic from platform-specific implementations: - Shared abstractions and core engine live in ClassicAssist.Shared - WPF-based implementation lives in ClassicAssist - Avalonia-based implementation lives in ClassicAssist.Avalonia - Launcher and updater are separate applications with their own UI frameworks ```mermaid graph TB subgraph "Shared" S_ID["IDispatcher.cs"] S_UI["IUIInvoker.cs"] S_Eng["Engine.cs (Shared)"] S_MsgProv["MessageBoxProvider.cs"] S_Native["NativeMethods.cs"] S_Platform["PlatformDetector.cs"] end subgraph "WPF" W_Dispatch["WPFDispatcher.cs"] W_UIInv["WPFUIInvoker.cs"] W_Eng["Engine.cs (WPF)"] W_MsgProv["WPFMessageBoxProvider.cs"] end subgraph "Avalonia" A_Dispatch["AvaloniaDispatcher.cs"] A_UIInv["AvaloniaUIInvoker.cs"] A_Eng["Engine.cs (Avalonia)"] A_Program["Program.cs (Avalonia)"] A_App["App.axaml.cs (Avalonia)"] A_MsgProv["AvaloniaMessageBoxProvider.cs"] end S_ID --> W_Dispatch S_ID --> A_Dispatch S_UI --> W_UIInv S_UI --> A_UIInv S_Eng --> W_Eng S_Eng --> A_Eng S_MsgProv --> W_MsgProv S_MsgProv --> A_MsgProv S_Native --> W_Eng S_Native --> A_Eng S_Platform --> W_Eng S_Platform --> A_Eng A_Program --> A_Eng A_App --> A_Eng ``` **Diagram sources** - [IDispatcher.cs](file://ClassicAssist.Shared/IDispatcher.cs) - [IUIInvoker.cs](file://ClassicAssist.Shared/IUIInvoker.cs) - [WPFDispatcher.cs](file://ClassicAssist/WPFDispatcher.cs) - [WPFUIInvoker.cs](file://ClassicAssist/WPFUIInvoker.cs) - [AvaloniaDispatcher.cs](file://ClassicAssist.Avalonia/AvaloniaDispatcher.cs) - [AvaloniaUIInvoker.cs](file://ClassicAssist.Avalonia/AvaloniaUIInvoker.cs) - [Engine.cs (Shared)](file://ClassicAssist.Shared/Engine.cs) - [Engine.cs (WPF)](file://ClassicAssist/Engine.cs) - [Engine.cs (Avalonia)](file://ClassicAssist.Avalonia/Engine.cs) - [Program.cs (Avalonia)](file://ClassicAssist.Avalonia/Program.cs) - [App.axaml.cs (Avalonia)](file://ClassicAssist.Avalonia/App.axaml.cs) - [MessageBoxProvider.cs](file://ClassicAssist.Shared/Misc/MessageBoxProvider.cs) - [WPFMessageBoxProvider.cs](file://ClassicAssist/UI/Misc/WPFMessageBoxProvider.cs) - [AvaloniaMessageBoxProvider.cs](file://ClassicAssist.Avalonia/Misc/AvaloniaMessageBoxProvider.cs) - [NativeMethods.cs](file://ClassicAssist.Shared/Misc/NativeMethods.cs) - [PlatformDetector.cs](file://ClassicAssist.Shared/Engine/PlatformDetector.cs) **Section sources** - [IDispatcher.cs](file://ClassicAssist.Shared/IDispatcher.cs) - [IUIInvoker.cs](file://ClassicAssist.Shared/IUIInvoker.cs) - [Engine.cs (Shared)](file://ClassicAssist.Shared/Engine.cs) - [Engine.cs (WPF)](file://ClassicAssist/Engine.cs) - [Engine.cs (Avalonia)](file://ClassicAssist.Avalonia/Engine.cs) - [Program.cs (Avalonia)](file://ClassicAssist.Avalonia/Program.cs) - [App.axaml.cs (Avalonia)](file://ClassicAssist.Avalonia/App.axaml.cs) - [MessageBoxProvider.cs](file://ClassicAssist.Shared/Misc/MessageBoxProvider.cs) - [WPFMessageBoxProvider.cs](file://ClassicAssist/UI/Misc/WPFMessageBoxProvider.cs) - [AvaloniaMessageBoxProvider.cs](file://ClassicAssist.Avalonia/Misc/AvaloniaMessageBoxProvider.cs) - [NativeMethods.cs](file://ClassicAssist.Shared/Misc/NativeMethods.cs) - [PlatformDetector.cs](file://ClassicAssist.Shared/Engine/PlatformDetector.cs) ## Core Components The cross-platform abstraction centers around two primary interfaces: - Dispatcher interface for thread marshaling - UI Invoker interface for cross-thread UI updates These interfaces are implemented per platform (WPF and Avalonia), allowing shared code to remain framework-agnostic. Key responsibilities: - Marshal work onto the UI thread safely - Provide a consistent API for invoking UI operations from background threads - Abstract away platform-specific threading models and message pumps **Section sources** - [IDispatcher.cs](file://ClassicAssist.Shared/IDispatcher.cs) - [IUIInvoker.cs](file://ClassicAssist.Shared/IUIInvoker.cs) - [WPFDispatcher.cs](file://ClassicAssist/WPFDispatcher.cs) - [WPFUIInvoker.cs](file://ClassicAssist/WPFUIInvoker.cs) - [AvaloniaDispatcher.cs](file://ClassicAssist.Avalonia/AvaloniaDispatcher.cs) - [AvaloniaUIInvoker.cs](file://ClassicAssist.Avalonia/AvaloniaUIInvoker.cs) ## Architecture Overview The architecture uses dependency injection or static initialization to select the correct platform implementation at runtime. Shared components depend only on interfaces, not concrete classes. ```mermaid classDiagram class IDispatcher { + "Dispatch(action)" + "BeginInvoke(action)" + "Invoke(action)" } class IUIInvoker { + "InvokeOnUiThread(action)" + "IsOnUIThread()" } class WPFDispatcher class WPFUIInvoker class AvaloniaDispatcher class AvaloniaUIInvoker IDispatcher <|.. WPFDispatcher : "implements" IDispatcher <|.. AvaloniaDispatcher : "implements" IUIInvoker <|.. WPFUIInvoker : "implements" IUIInvoker <|.. AvaloniaUIInvoker : "implements" ``` **Diagram sources** - [IDispatcher.cs](file://ClassicAssist.Shared/IDispatcher.cs) - [IUIInvoker.cs](file://ClassicAssist.Shared/IUIInvoker.cs) - [WPFDispatcher.cs](file://ClassicAssist/WPFDispatcher.cs) - [WPFUIInvoker.cs](file://ClassicAssist/WPFUIInvoker.cs) - [AvaloniaDispatcher.cs](file://ClassicAssist.Avalonia/AvaloniaDispatcher.cs) - [AvaloniaUIInvoker.cs](file://ClassicAssist.Avalonia/AvaloniaUIInvoker.cs) ## Detailed Component Analysis ### Dispatcher Interface and Implementations Purpose: - Provide a unified way to schedule work on the UI thread regardless of the underlying UI framework - Support both synchronous and asynchronous dispatching patterns Implementation highlights: - WPFDispatcher bridges to WPF’s dispatcher model - AvaloniaDispatcher bridges to Avalonia’s dispatcher model - Both expose methods to enqueue actions and ensure they execute on the UI thread ```mermaid sequenceDiagram participant BG as "Background Thread" participant Shared as "Shared Code" participant Disp as "IDispatcher" participant Impl as "WPFDispatcher / AvaloniaDispatcher" participant UIThread as "UI Thread" BG->>Shared : "Perform long-running task" Shared->>Disp : "BeginInvoke(action)" Disp->>Impl : "Forward call" Impl->>UIThread : "Marshal action" UIThread-->>Impl : "Execute action" Impl-->>Shared : "Return" ``` **Diagram sources** - [IDispatcher.cs](file://ClassicAssist.Shared/IDispatcher.cs) - [WPFDispatcher.cs](file://ClassicAssist/WPFDispatcher.cs) - [AvaloniaDispatcher.cs](file://ClassicAssist.Avalonia/AvaloniaDispatcher.cs) **Section sources** - [IDispatcher.cs](file://ClassicAssist.Shared/IDispatcher.cs) - [WPFDispatcher.cs](file://ClassicAssist/WPFDispatcher.cs) - [AvaloniaDispatcher.cs](file://ClassicAssist.Avalonia/AvaloniaDispatcher.cs) ### UI Invoker Interface and Implementations Purpose: - Encapsulate UI framework-specific invocation semantics - Offer IsOnUIThread checks and safe InvokeOnUiThread helpers Implementation highlights: - WPFUIInvoker uses WPF’s thread affinity APIs - AvaloniaUIInvoker uses Avalonia’s thread context APIs - Shared code calls IUIInvoker without knowing the underlying framework ```mermaid flowchart TD Start(["Start"]) --> Check["Check if currently on UI thread"] Check --> |Yes| ExecuteDirect["Execute UI update directly"] Check --> |No| Enqueue["Enqueue action via dispatcher"] Enqueue --> Wait["Wait for completion if needed"] ExecuteDirect --> End(["End"]) Wait --> End ``` **Diagram sources** - [IUIInvoker.cs](file://ClassicAssist.Shared/IUIInvoker.cs) - [WPFUIInvoker.cs](file://ClassicAssist/WPFUIInvoker.cs) - [AvaloniaUIInvoker.cs](file://ClassicAssist.Avalonia/AvaloniaUIInvoker.cs) **Section sources** - [IUIInvoker.cs](file://ClassicAssist.Shared/IUIInvoker.cs) - [WPFUIInvoker.cs](file://ClassicAssist/WPFUIInvoker.cs) - [AvaloniaUIInvoker.cs](file://ClassicAssist.Avalonia/AvaloniaUIInvoker.cs) ### Engine Initialization and Platform Selection Purpose: - Initialize the correct platform-specific dispatcher and UI invoker - Configure application lifecycle and entry points per platform Implementation highlights: - Shared Engine defines common startup logic and configuration - WPF Engine initializes WPF-specific resources and sets up WPFDispatcher/WPFUIInvoker - Avalonia Engine initializes Avalonia-specific resources and sets up AvaloniaDispatcher/AvaloniaUIInvoker - Avalonia Program and App coordinate bootstrapping and DI setup ```mermaid sequenceDiagram participant OS as "Operating System" participant Entry as "Program.cs (Avalonia)" participant App as "App.axaml.cs (Avalonia)" participant Eng as "Engine.cs (Avalonia)" participant Disp as "AvaloniaDispatcher" participant UIInv as "AvaloniaUIInvoker" OS->>Entry : "Launch process" Entry->>App : "Initialize Avalonia" App->>Eng : "Create and configure Engine" Eng->>Disp : "Register dispatcher" Eng->>UIInv : "Register UI invoker" Eng-->>App : "Ready" ``` **Diagram sources** - [Program.cs (Avalonia)](file://ClassicAssist.Avalonia/Program.cs) - [App.axaml.cs (Avalonia)](file://ClassicAssist.Avalonia/App.axaml.cs) - [Engine.cs (Avalonia)](file://ClassicAssist.Avalonia/Engine.cs) - [Engine.cs (WPF)](file://ClassicAssist/Engine.cs) - [Engine.cs (Shared)](file://ClassicAssist.Shared/Engine.cs) **Section sources** - [Engine.cs (Shared)](file://ClassicAssist.Shared/Engine.cs) - [Engine.cs (WPF)](file://ClassicAssist/Engine.cs) - [Engine.cs (Avalonia)](file://ClassicAssist.Avalonia/Engine.cs) - [Program.cs (Avalonia)](file://ClassicAssist.Avalonia/Program.cs) - [App.axaml.cs (Avalonia)](file://ClassicAssist.Avalonia/App.axaml.cs) ### Window Management Abstraction Purpose: - Provide a consistent API for creating, showing, and managing windows across platforms - Hide differences between WPF Window and Avalonia Window lifecycles Approach: - Use platform-specific providers registered by each Engine implementation - Expose simple Create/Show/Close operations from shared code Considerations: - Ensure UI thread affinity when manipulating window state - Handle platform differences in modal dialogs and owner relationships [No sources needed since this section doesn't analyze specific files] ### File System Access Abstraction Purpose: - Normalize path separators, case sensitivity, and special directories across platforms - Provide helper utilities for reading/writing configuration and data files Approach: - Use shared utility functions that adapt to platform specifics - Centralize path resolution and normalization Considerations: - Respect user profile locations and sandboxing constraints - Handle permission differences on Linux/macOS vs Windows [No sources needed since this section doesn't analyze specific files] ### Native Interop Calls Purpose: - Provide a single point for platform-specific native calls - Abstract away differences in P/Invoke signatures and availability Approach: - Define conditional compilation or runtime detection for available APIs - Wrap native calls behind shared interfaces where possible Considerations: - Guard against missing symbols on non-Windows platforms - Prefer managed alternatives when available **Section sources** - [NativeMethods.cs](file://ClassicAssist.Shared/Misc/NativeMethods.cs) - [PlatformDetector.cs](file://ClassicAssist.Shared/Engine/PlatformDetector.cs) ### Message Box Provider Abstraction Purpose: - Standardize user-facing dialog interactions across platforms - Allow shared code to show messages without depending on UI framework details Implementation highlights: - MessageBoxProvider defines the shared contract - WPFMessageBoxProvider implements it using WPF dialogs - AvaloniaMessageBoxProvider implements it using Avalonia dialogs ```mermaid classDiagram class MessageBoxProvider { <> + "Show(message)" } class WPFMessageBoxProvider class AvaloniaMessageBoxProvider MessageBoxProvider <|.. WPFMessageBoxProvider : "implements" MessageBoxProvider <|.. AvaloniaMessageBoxProvider : "implements" ``` **Diagram sources** - [MessageBoxProvider.cs](file://ClassicAssist.Shared/Misc/MessageBoxProvider.cs) - [WPFMessageBoxProvider.cs](file://ClassicAssist/UI/Misc/WPFMessageBoxProvider.cs) - [AvaloniaMessageBoxProvider.cs](file://ClassicAssist.Avalonia/Misc/AvaloniaMessageBoxProvider.cs) **Section sources** - [MessageBoxProvider.cs](file://ClassicAssist.Shared/Misc/MessageBoxProvider.cs) - [WPFMessageBoxProvider.cs](file://ClassicAssist/UI/Misc/WPFMessageBoxProvider.cs) - [AvaloniaMessageBoxProvider.cs](file://ClassicAssist.Avalonia/Misc/AvaloniaMessageBoxProvider.cs) ## Dependency Analysis The following diagram shows how shared interfaces are implemented by platform-specific classes and how engines wire them together. ```mermaid graph LR Shared["Shared Interfaces
IDispatcher, IUIInvoker"] --> WPFImpl["WPF Implementations
WPFDispatcher, WPFUIInvoker"] Shared --> AvaloniaImpl["Avalonia Implementations
AvaloniaDispatcher, AvaloniaUIInvoker"] SharedEng["Shared Engine"] --> WPFEng["WPF Engine"] SharedEng --> AvaloniaEng["Avalonia Engine"] WPFEng --> WPFImpl AvaloniaEng --> AvaloniaImpl ``` **Diagram sources** - [IDispatcher.cs](file://ClassicAssist.Shared/IDispatcher.cs) - [IUIInvoker.cs](file://ClassicAssist.Shared/IUIInvoker.cs) - [WPFDispatcher.cs](file://ClassicAssist/WPFDispatcher.cs) - [WPFUIInvoker.cs](file://ClassicAssist/WPFUIInvoker.cs) - [AvaloniaDispatcher.cs](file://ClassicAssist.Avalonia/AvaloniaDispatcher.cs) - [AvaloniaUIInvoker.cs](file://ClassicAssist.Avalonia/AvaloniaUIInvoker.cs) - [Engine.cs (Shared)](file://ClassicAssist.Shared/Engine.cs) - [Engine.cs (WPF)](file://ClassicAssist/Engine.cs) - [Engine.cs (Avalonia)](file://ClassicAssist.Avalonia/Engine.cs) **Section sources** - [IDispatcher.cs](file://ClassicAssist.Shared/IDispatcher.cs) - [IUIInvoker.cs](file://ClassicAssist.Shared/IUIInvoker.cs) - [WPFDispatcher.cs](file://ClassicAssist/WPFDispatcher.cs) - [WPFUIInvoker.cs](file://ClassicAssist/WPFUIInvoker.cs) - [AvaloniaDispatcher.cs](file://ClassicAssist.Avalonia/AvaloniaDispatcher.cs) - [AvaloniaUIInvoker.cs](file://ClassicAssist.Avalonia/AvaloniaUIInvoker.cs) - [Engine.cs (Shared)](file://ClassicAssist.Shared/Engine.cs) - [Engine.cs (WPF)](file://ClassicAssist/Engine.cs) - [Engine.cs (Avalonia)](file://ClassicAssist.Avalonia/Engine.cs) ## Performance Considerations - Minimize cross-thread marshals: batch UI updates when possible to reduce dispatcher overhead - Avoid blocking the UI thread: use BeginInvoke-style patterns for long-running tasks - Prefer lightweight invocations: keep actions small and focused to reduce contention - Cache frequently accessed platform info: avoid repeated platform detection calls - Be mindful of memory pressure: avoid capturing large objects in dispatched closures [No sources needed since this section provides general guidance] ## Troubleshooting Guide Common issues and resolutions: - Cross-thread operation exceptions: ensure all UI updates go through IUIInvoker.InvokeOnUiThread - Deadlocks on UI thread: avoid calling synchronous Invoke from the UI thread; prefer asynchronous patterns - Missing platform features: guard native calls with platform detection and fallbacks - Dialog visibility problems: verify that dialogs are shown on the correct UI thread and have proper owners **Section sources** - [IUIInvoker.cs](file://ClassicAssist.Shared/IUIInvoker.cs) - [WPFUIInvoker.cs](file://ClassicAssist/WPFUIInvoker.cs) - [AvaloniaUIInvoker.cs](file://ClassicAssist.Avalonia/AvaloniaUIInvoker.cs) - [PlatformDetector.cs](file://ClassicAssist.Shared/Engine/PlatformDetector.cs) ## Conclusion The cross-platform abstraction layer cleanly separates shared logic from platform-specific concerns using well-defined interfaces for dispatching and UI invocation. By centralizing platform selection in the Engine implementations and providing consistent APIs for window management, file system access, and native interop, ClassicAssist achieves robust support across Windows, Linux, and macOS. Following the migration strategies and performance guidelines outlined here will help maintain stability and portability as the codebase evolves. [No sources needed since this section summarizes without analyzing specific files] ## Appendices ### Migration Strategies - Introduce new platform-specific feature: - Add an interface method to the relevant shared interface if necessary - Implement the method in WPF and Avalonia providers - Update Engines to register the new provider - Replace direct UI framework calls: - Refactor to use IUIInvoker and IDispatcher - Remove direct references to WPF/Avalonia types from shared code - Handle native interop: - Wrap calls in shared utilities with platform guards - Provide no-op or alternative implementations for unsupported platforms [No sources needed since this section doesn't analyze specific files]