First off, thank you for considering contributing to ProseFlow! 🎉
We're thrilled that you're interested in helping us build the best writing assistant. Every contribution, from a small bug fix to a major new feature, is valuable and greatly appreciated.
This document provides guidelines for contributing to the project to ensure a smooth and effective process for everyone involved. Please read it carefully to ensure a smooth and effective collaboration process.
- Code of Conduct
- How Can I Contribute?
- Development Setup
- Understanding the Architecture
- Coding Style & Conventions
- Submitting a Pull Request
- Where to Find Things (A Guided Tour)
We are committed to providing a friendly, safe, privacy-focused, and open-source software for all. By participating in this project, you agree to abide by our Code of Conduct. Please be respectful and considerate in all your interactions. Harassment or exclusionary behavior will not be tolerated.
There are many ways to contribute to ProseFlow:
- Reporting Bugs: If you find a bug, please create an issue in our GitHub repository. Describe the bug clearly, include steps to reproduce it, and mention your operating system and the app version.
- Suggesting Enhancements: Have an idea for a new feature or a UX improvement? Open an issue to discuss it. We love hearing new ideas!
- Writing Code: Help us fix bugs or implement new features. If you're looking for a place to start, check the Issues tab and look for issues tagged with
good first issueorhelp wanted. - Improving Documentation: Clear documentation is crucial. If you find parts of our documentation (including this guide!) that could be improved, feel free to submit a pull request.
To get ProseFlow running on your local machine, follow these steps.
Before you begin, make sure you have the following tools installed:
- .NET 8 SDK
- Git
- An IDE of your choice:
- Visual Studio 2022 (Windows)
- JetBrains Rider (Recommended for cross-platform development)
- Visual Studio Code with the C# Dev Kit and Avalonia extensions.
-
Fork and Clone the Repository:
git clone https://github.com/LSXPrime/ProseFlow.git cd ProseFlow -
Restore Workloads: Since this project is deploying to multiple platforms, we need to restore the workloads.
dotnet workload restore
-
Restore Dependencies: The first time you open the solution (
ProseFlow.sln) in your IDE, it should automatically restore all NuGet packages. You can also do this manually from the command line:dotnet restore
-
Set up the Database: ProseFlow uses Entity Framework Core with a SQLite database. The database file (
proseflow.db) is created automatically. You just need to apply the migrations.- Ensure you have the EF Core tools installed:
dotnet tool install --global dotnet-ef - From the root of the repository, run the following command to apply migrations:
dotnet ef database update --startup-project ProseFlow.UI
This will create and seed the database in the appropriate application data folder for your OS. The application uses a local SQLite database, which will be created and migrated automatically in your user's
AppDataor equivalent folder on its first run. - Ensure you have the EF Core tools installed:
-
Build and Run the Application:
- The main UI project is
ProseFlow.UI. You can run it from your IDE by setting it as the startup project and then building/running. - Alternatively, via the command line:
dotnet run --project ProseFlow.UI/ProseFlow.UI.csproj
The application should launch, and you will be greeted with the first-run onboarding window if the database was just created.
- The main UI project is
ProseFlow is built using Clean Architecture principles to ensure separation of concerns, maintainability, and testability. The solution is divided into four main projects. This approach ensures that dependencies flow inwards, from the outer layers (UI, Infrastructure) to the inner layers (Application, Core).
graph TD
%% Define styles for different component types
classDef layer fill:#f9f9f9,stroke:#333,stroke-width:2px;
classDef interface fill:#e6f3ff,stroke:#0066cc;
classDef concrete fill:#d4edda,stroke:#155724;
classDef ui fill:#f8d7da,stroke:#721c24;
classDef external fill:#fff3cd,stroke:#856404;
%% --- External Libraries ---
subgraph External_Libraries["External Libraries"]
LlmTornado[LlmTornado]:::external
LLamaSharp[LLamaSharp]:::external
EFCore[Entity Framework Core]:::external
Avalonia[Avalonia UI]:::external
SharpHook[SharpHook]:::external
end
%% --- Core Layer ---
subgraph Core["Core (ProseFlow.Core)"]
direction TB
Core_Interfaces["Interfaces (IAiProvider, IRepository, IUnitOfWork)"]:::interface
Core_Models["Models (Action, HistoryEntry, etc.)"]:::concrete
Enums[Enums]:::concrete
end
class Core layer
%% --- Application Layer ---
subgraph Application["Application (ProseFlow.Application)"]
direction TB
App_Interfaces["Interfaces (IDownloadManager, IUpdateService)"]:::interface
App_Services["Services (ActionOrchestrationService, etc.)"]:::concrete
DTOs["DTOs (ActionDto, etc.)"]:::concrete
AppEvents["AppEvents (Static Events)"]:::concrete
end
class Application layer
%% --- Infrastructure Layer ---
subgraph Infrastructure["Infrastructure (ProseFlow.Infrastructure)"]
direction TB
subgraph Data_Access["Data Access"]
AppDbContext[AppDbContext]:::concrete
Repositories["Repositories (ActionRepository, etc.)"]:::concrete
end
subgraph AI_Providers["AI Providers"]
CloudProvider[CloudProvider]:::concrete
LocalProvider[LocalProvider]:::concrete
end
subgraph OS_Services["OS Services"]
OsService[OsService]:::concrete
ActiveWindowTrackers["Platform-Specific Trackers"]:::concrete
end
ApiKeyProtector["Security (ApiKeyProtector)"]:::concrete
end
class Infrastructure layer
%% --- UI Layer ---
subgraph UI["UI (ProseFlow.UI)"]
direction TB
DI_Setup["Dependency Injection Setup (App.axaml.cs)"]:::ui
ViewModels["ViewModels (MVVM)"]:::ui
Views["Views (.axaml)"]:::ui
Services_UI["UI Services (DialogService, etc.)"]:::ui
end
class UI layer
%% --- Dependency Relationships ---
%% UI Layer Dependencies
Views -- "Binds to" --> ViewModels
ViewModels -- "Uses" --> App_Services
Services_UI -- "Used by" --> Views
Services_UI -- "Used by" --> ViewModels
DI_Setup -- "Registers" --> App_Services
DI_Setup -- "Registers" --> Repositories
DI_Setup -- "Registers" --> CloudProvider
DI_Setup -- "Registers" --> LocalProvider
DI_Setup -- "Registers" --> OsService
UI -- "Built with" --> Avalonia
%% Infrastructure Layer Dependencies
Repositories -- "Implements" --> Core_Interfaces
CloudProvider -- "Implements" --> Core_Interfaces
LocalProvider -- "Implements" --> Core_Interfaces
OsService -- "Implements" --> Core_Interfaces
ApiKeyProtector -- "Implements" --> App_Interfaces
Repositories -- "Uses" --> AppDbContext
AppDbContext -- "Uses" --> EFCore
CloudProvider -- "Uses" --> LlmTornado
LocalProvider -- "Uses" --> LLamaSharp
OsService -- "Uses" --> SharpHook
ActiveWindowTrackers -- "Used by" --> OsService
%% Application Layer Dependencies
App_Services -- "Depends on" --> Core_Interfaces
App_Services -- "Uses" --> Core_Models
App_Services -- "Uses" --> DTOs
App_Services -- "Raises" --> AppEvents
App_Services -- "Implements" --> App_Interfaces
%% Cross-layer Dependencies
ViewModels -- "Listens to" --> AppEvents
Infrastructure -- "Provides implementations for" --> Application
Infrastructure -- "Provides implementations for" --> Core
Application -- "Depends on abstractions from" --> Core
UI -- "Depends on" --> Application
%% Architecture Flow
UI --> Application
Application --> Core
Infrastructure --> Core
Infrastructure --> Application
This is the heart of the application. It contains the core domain models, business logic, and interfaces.
- Models: Plain C# objects representing the core concepts (e.g.,
Action,HistoryEntry,CloudProviderConfiguration). - Enums: Application-wide enumerations.
- Interfaces: Contracts for repositories (
IRepository,IActionRepository) and core services (e.g.,IUnitOfWork,IAiProvider,IOsService). - Key Rule: This project has zero dependencies on other layers or external frameworks.
This layer contains the application-specific business rules and orchestrates the core domain models.
- Services: Classes that implement the application's use cases (e.g.,
ActionOrchestrationService,DashboardService). They depend on interfaces fromProseFlow.Core. - DTOs (Data Transfer Objects): Simple records used to transfer data between layers, especially to and from the UI.
- Interfaces: Defines interfaces for infrastructure components that the application logic needs (e.g.,
IDownloadManager,IUpdateService). - Events: Static events (
AppEvents) for decoupled communication between the application layer and the UI. - Key Rule: Depends only on
ProseFlow.Core. It does not know about specific UI frameworks or infrastructure details.
This is the outermost layer that provides concrete implementations for the interfaces defined in the Application and Core layers.
- Data: Contains the
AppDbContext(Entity Framework Core), repository implementations, and theUnitOfWorkpattern for database transactions. - Services: Home to implementations that interact with the outside world:
AiProviders: ConcreteIAiProviderimplementations for both Cloud (usingLlmTornado) and Local (usingLLamaSharp) models.Os: Platform-specific services for hotkeys (SharpHook) and active window tracking.Security:ApiKeyProtectorfor encrypting sensitive data.
- Key Rule: Depends on
ProseFlow.ApplicationandProseFlow.Core. This is where all external libraries and platform-specific code reside.
The presentation layer, built with Avalonia UI and following the MVVM (Model-View-ViewModel) pattern.
- Views: The XAML files that define the user interface (
.axaml). They are kept as simple as possible, with minimal code-behind. - ViewModels: The logic that drives the Views. They are responsible for state management and user interaction commands. We use the CommunityToolkit.Mvvm library. ViewModels should inherit from
ViewModelBase. - Models: UI-specific models or DTOs.
- Services: UI-specific services like
DialogServiceandNotificationService. - Behaviors & Converters: Reusable components that extend Avalonia's functionality.
- Key Rule: Depends on
ProseFlow.ApplicationandProseFlow.Infrastructure. It wires up all the dependencies inApp.axaml.csusingMicrosoft.Extensions.DependencyInjection.
Key Design Patterns:
- Dependency Injection: Services are registered in
App.axaml.csand injected via constructors throughout the application. Prefer constructor injection. - Repository & Unit of Work: Decouples business logic from data access.
- Async/Await: All I/O and long-running operations are asynchronous using
async Task. Suffix allasyncmethods withAsync(e.g.,LoadSettingsAsync). Avoidasync voidexcept for event handlers.
Consistency is key. Please adhere to the following conventions:
- Language: We use C# 12 and .NET 8. Feel free to use modern language features where appropriate (e.g., file-scoped namespaces, primary constructors,
recordtypes for DTOs). - Formatting: We follow standard .NET coding conventions
- MVVM: We use the
CommunityToolkit.Mvvmlibrary.- Use
[ObservableProperty]for properties that need to notify the UI. - Use
[RelayCommand]for methods that will be bound to UI commands (e.g., button clicks). - Keep Views free of business logic.
- Use
- Naming:
- Use
PascalCasefor classes, methods, properties, and enums. - Use
_camelCasefor private fields. - UI controls in XAML should have a meaningful
x:Name.
- Use
- Comments:
- Write XML documentation (
/// <summary>...) for all public methods, properties, and classes. This helps with IntelliSense and maintainability. - Use
//for inline comments to clarify complex or non-obvious logic within a method.
- Write XML documentation (
- XAML:
- Use
StaticResourcefor converters, styles, and brushes to promote reusability. - Follow the existing structure and organization within View files.
- Our UI is built on the ShadUI theme for Avalonia. Please use its components (
Card,Buttonclasses, etc.) where appropriate to maintain a consistent look and feel.
- Use
Before you submit your PR, please ensure you have completed the following:
- Fork the repository on GitHub.
- Create a new branch for your changes from the
masterbranch. Name it descriptively, likefix/hotkey-conflictorfeat/diff-view(e.g.,git checkout -b feature/add-new-providerorgit checkout -b fix/history-view-crash). - Make your changes. Ensure your code adheres to the architecture and coding style outlined above.
- Commit your changes with a clear and concise commit message.
- Push your branch to your fork.
- Open a Pull Request (PR) against the
masterbranch of the official repository.
When opening your PR, please ensure:
- The project builds and runs without errors.
- Your code follows the style guidelines outlined above.
- You have added XML comments to any new public members.
- Your commit messages are clear and descriptive.
- You have written a clear description for your PR, explaining the "what" and "why" of your changes.
- You have linked the PR to any relevant issues (e.g.,
Fixes #123or "Closes #123"). - You have tested your changes in single or multiple environments.
Once your PR is submitted, a team member will review it. We may suggest some changes or improvements. We appreciate your patience and collaboration during this process.
Here are some common contribution scenarios and where to look in the code:
- Core: Add a new value to the
ProviderTypeenum inProseFlow.Core/Enums/ProviderType.cs. - Infrastructure: In
CloudProvider.cs, map your newProviderTypeto the correspondingLlmTornadoprovider. - UI: Add the new provider type to the UI dropdown in
CloudProviderEditorView.axaml.
- Application: Create a new ViewModel (e.g.,
MyNewPageViewModel.cs) that implements theIPageViewModelinterface fromViewModelBase.cs. - UI: Create a corresponding View (
MyNewPageView.axaml) in theViewsfolder. - Wiring Up:
- Register your new ViewModel in
App.axaml.cs(services.AddTransient<MyNewPageViewModel>()). - Add an instance of your ViewModel to the
PageViewModelscollection inMainViewModel.cs. - The
ViewLocator.cswill automatically connect the View and ViewModel at runtime.
- Register your new ViewModel in
- Core: Add the property to
GeneralSettings.cs. - Infrastructure: Create a new migration for the database change (
dotnet ef migrations add [MigrationName] -p ProseFlow.Infrastructure -s ProseFlow.UI). - UI:
- Add a control to
SettingsView.axamland bind it to your new property (e.g.,{Binding Settings.MyNewSetting}). - The
SettingsViewModelalready loads and saves the entireGeneralSettingsobject, so no ViewModel changes may be needed for simple properties.
- Add a control to
Thank you again for your interest in contributing to ProseFlow! We look forward to your pull requests. Happy coding!