diff --git a/.github/workflows/dotnet-manual.yml b/.github/workflows/dotnet-manual.yml
new file mode 100644
index 0000000..abe8f29
--- /dev/null
+++ b/.github/workflows/dotnet-manual.yml
@@ -0,0 +1,52 @@
+# This workflow will build a .NET project
+# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-net
+
+name: .NET (Manual Build)
+permissions:
+ contents: read
+
+on:
+ workflow_dispatch: # Manual trigger only
+ inputs:
+ branch:
+ description: 'Branch to build'
+ required: false
+ default: ''
+ type: string
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+ with:
+ ref: ${{ github.event.inputs.branch || github.ref_name }}
+
+ - name: Setup .NET
+ uses: actions/setup-dotnet@v4
+ with:
+ dotnet-version: 9.0.x
+
+ - name: Install Mono
+ run: |
+ sudo apt-get update
+ sudo apt-get install -y mono-devel
+
+ - name: Run Configure setup
+ shell: pwsh
+ run: |
+ Write-Host "Running Configure.ps1..."
+ echo "1" | pwsh ./Configure.ps1 setup -Verbose
+
+ - name: Restore dependencies
+ working-directory: CatalystUI
+ run: dotnet restore
+
+ - name: Build
+ working-directory: CatalystUI
+ run: dotnet build --no-restore
+
+ - name: Test
+ working-directory: CatalystUI
+ run: dotnet test --no-build --verbosity normal
diff --git a/.scripts/Setup.ps1 b/.scripts/Setup.ps1
index 4bfa893..ee5587a 100644
--- a/.scripts/Setup.ps1
+++ b/.scripts/Setup.ps1
@@ -45,12 +45,32 @@ $projectsList = @(
Projects = @(
@{ Folder = "Core"; Name = "CatalystUI.Attributes" },
@{ Folder = "Core"; Name = "CatalystUI.Collections" },
+ @{ Folder = "Core"; Name = "CatalystUI.Mathematics" },
@{ Folder = "Core"; Name = "CatalystUI.Threading" },
@{ Folder = "Tooling"; Name = "CatalystUI.Analyzers" },
@{ Folder = "Tooling"; Name = "CatalystUI.CodeFix" },
- @{ Folder = "Core"; Name = "CatalystUI.Core" }
+ @{ Folder = "Core"; Name = "CatalystUI.Core" },
+ @{ Folder = "Core"; Name = "CatalystUI.Debug" },
+ @{ Folder = "Core"; Name = "CatalystUI.Supplementary" }
)
- PromptIgnore = $true
+ PromptIgnore = $false
+ Depends = @()
+ },
+ @{
+ Module = "Arcane"
+ Projects = @(
+ @{ Folder = "Modules/Arcane"; Name = "CatalystUI.Modules.Arcane.Core" }
+ )
+ PromptIgnore = $false
+ Depends = @("Core")
+ },
+ @{
+ Module = "Arcane.Ini"
+ Project = @(
+ @{ Folder = "Modules/Arcane"; Name = "CatalystUI.Modules.Arcane.Ini" }
+ )
+ PromptIgnore = $false
+ Depends = @("Arcane")
}
)
@@ -58,8 +78,7 @@ $projectsList = @(
$promptOptions = @("All") + (
$projectsList |
Where-Object { -not $_.PromptIgnore -and $_.Module -ne "All" } |
- Select-Object -ExpandProperty Module -Unique |
- Sort-Object
+ Select-Object -ExpandProperty Module -Unique
)
# Prompt user for module selection
diff --git a/CatalystUI/CatalystUI.sln b/CatalystUI/CatalystUI.sln
index 956b7d8..0b50ed1 100644
--- a/CatalystUI/CatalystUI.sln
+++ b/CatalystUI/CatalystUI.sln
@@ -18,6 +18,24 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CatalystUI.Attributes", "Co
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CatalystUI.CodeFix", "Tooling\CatalystUI.CodeFix\CatalystUI.CodeFix.csproj", "{E5319DB6-E93C-4A7D-9B3B-F219206BBC54}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CatalystUI.Debug", "Core\CatalystUI.Debug\CatalystUI.Debug.csproj", "{39CD2850-CB5B-4F3C-81AB-9430506F3BD7}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CatalystUI.Supplementary", "Core\CatalystUI.Supplementary\CatalystUI.Supplementary.csproj", "{33B1D211-9C3A-4AA8-95DE-75D9122CC968}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CatalystUI.Mathematics", "Core\CatalystUI.Mathematics\CatalystUI.Mathematics.csproj", "{E2B3A13C-9AE6-44D8-8456-58723ABBC343}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Modules", "Modules", "{9C3F6A00-82F5-4900-9D6C-07ACBBAAE823}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Crystal", "Crystal", "{41BEF490-7005-4D10-9958-22D636F9DE38}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Arcane", "Arcane", "{C8B02B42-826B-4EDE-B72F-F4F97C1A088D}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Veilstone", "Veilstone", "{0E1F2B64-37D9-4C24-9CED-9A44D7CDBB8C}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CatalystUI.Modules.Arcane.Ini", "Modules\Arcane\CatalystUI.Modules.Arcane.Ini\CatalystUI.Modules.Arcane.Ini.csproj", "{C02600D7-087B-4190-9B47-F15184C19B2D}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CatalystUI.Modules.Arcane.Core", "Modules\Arcane\CatalystUI.Modules.Arcane.Core\CatalystUI.Modules.Arcane.Core.csproj", "{047744B0-87DC-4808-99C4-5AC1F8A1EB4D}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -52,6 +70,26 @@ Global
{E5319DB6-E93C-4A7D-9B3B-F219206BBC54}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E5319DB6-E93C-4A7D-9B3B-F219206BBC54}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E5319DB6-E93C-4A7D-9B3B-F219206BBC54}.Release|Any CPU.Build.0 = Release|Any CPU
+ {39CD2850-CB5B-4F3C-81AB-9430506F3BD7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {39CD2850-CB5B-4F3C-81AB-9430506F3BD7}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {39CD2850-CB5B-4F3C-81AB-9430506F3BD7}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {39CD2850-CB5B-4F3C-81AB-9430506F3BD7}.Release|Any CPU.Build.0 = Release|Any CPU
+ {33B1D211-9C3A-4AA8-95DE-75D9122CC968}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {33B1D211-9C3A-4AA8-95DE-75D9122CC968}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {33B1D211-9C3A-4AA8-95DE-75D9122CC968}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {33B1D211-9C3A-4AA8-95DE-75D9122CC968}.Release|Any CPU.Build.0 = Release|Any CPU
+ {E2B3A13C-9AE6-44D8-8456-58723ABBC343}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {E2B3A13C-9AE6-44D8-8456-58723ABBC343}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {E2B3A13C-9AE6-44D8-8456-58723ABBC343}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {E2B3A13C-9AE6-44D8-8456-58723ABBC343}.Release|Any CPU.Build.0 = Release|Any CPU
+ {C02600D7-087B-4190-9B47-F15184C19B2D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {C02600D7-087B-4190-9B47-F15184C19B2D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {C02600D7-087B-4190-9B47-F15184C19B2D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {C02600D7-087B-4190-9B47-F15184C19B2D}.Release|Any CPU.Build.0 = Release|Any CPU
+ {047744B0-87DC-4808-99C4-5AC1F8A1EB4D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {047744B0-87DC-4808-99C4-5AC1F8A1EB4D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {047744B0-87DC-4808-99C4-5AC1F8A1EB4D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {047744B0-87DC-4808-99C4-5AC1F8A1EB4D}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{68F496AC-9438-40F1-9DF8-97363033D661} = {7EC51871-49A8-4991-BDAF-F43A4E9B8C9D}
@@ -61,5 +99,13 @@ Global
{A3936CB7-DC31-414B-9E40-CB9436391068} = {5D38F696-8C11-4C9A-B50E-2C33AA7FAA6C}
{44E8E3D2-FE47-49EA-A397-EB680E33AA2E} = {7EC51871-49A8-4991-BDAF-F43A4E9B8C9D}
{E5319DB6-E93C-4A7D-9B3B-F219206BBC54} = {5D38F696-8C11-4C9A-B50E-2C33AA7FAA6C}
+ {39CD2850-CB5B-4F3C-81AB-9430506F3BD7} = {7EC51871-49A8-4991-BDAF-F43A4E9B8C9D}
+ {33B1D211-9C3A-4AA8-95DE-75D9122CC968} = {7EC51871-49A8-4991-BDAF-F43A4E9B8C9D}
+ {E2B3A13C-9AE6-44D8-8456-58723ABBC343} = {7EC51871-49A8-4991-BDAF-F43A4E9B8C9D}
+ {41BEF490-7005-4D10-9958-22D636F9DE38} = {9C3F6A00-82F5-4900-9D6C-07ACBBAAE823}
+ {C8B02B42-826B-4EDE-B72F-F4F97C1A088D} = {9C3F6A00-82F5-4900-9D6C-07ACBBAAE823}
+ {0E1F2B64-37D9-4C24-9CED-9A44D7CDBB8C} = {9C3F6A00-82F5-4900-9D6C-07ACBBAAE823}
+ {C02600D7-087B-4190-9B47-F15184C19B2D} = {C8B02B42-826B-4EDE-B72F-F4F97C1A088D}
+ {047744B0-87DC-4808-99C4-5AC1F8A1EB4D} = {C8B02B42-826B-4EDE-B72F-F4F97C1A088D}
EndGlobalSection
EndGlobal
diff --git a/CatalystUI/Core/CatalystUI.Core/Builders/CatalystAppBuilder.cs b/CatalystUI/Core/CatalystUI.Core/Builders/CatalystAppBuilder.cs
new file mode 100644
index 0000000..5216e92
--- /dev/null
+++ b/CatalystUI/Core/CatalystUI.Core/Builders/CatalystAppBuilder.cs
@@ -0,0 +1,146 @@
+// -------------------------------------------------------------------------------------------------
+// CatalystUI Framework for .NET Core - https://catalystui.org/
+// Copyright (c) 2025 CatalystUI LLC. All rights reserved.
+//
+// This file is part of CatalystUI and is provided as part of an early-access release.
+// Unauthorized commercial use, distribution, or modification is strictly prohibited.
+//
+// This software is not open source and is not publicly licensed.
+// For full terms, see the LICENSE and NOTICE files in the project root.
+// -------------------------------------------------------------------------------------------------
+
+using Catalyst.Connectors;
+using Catalyst.Domains;
+using Catalyst.Layers;
+using System;
+using System.ComponentModel;
+using System.Threading.Tasks;
+
+namespace Catalyst.Builders {
+
+ ///
+ /// Builder class for creating a Catalyst application.
+ ///
+ public sealed class CatalystAppBuilder {
+
+ ///
+ /// Constructs a new .
+ ///
+ public CatalystAppBuilder() {
+ // ...
+ }
+
+ ///
+ /// Makes a layer available for use in the CatalystUI application.
+ ///
+ ///
+ ///
+ /// The added layer is registered with the ,
+ /// which allows it to be used globally across the application.
+ ///
+ ///
+ /// Layers are the building blocks of the CatalystUI application,
+ /// providing a way to organize and manage different parts of the application.
+ /// Inserting a layer into the application allows it to add additional functionality
+ /// such as data handling, UI components, or other features. To connect the functionality
+ /// of multiple layers, you can use connectors.
+ ///
+ ///
+ ///
+ /// The layer to add.
+ /// The type of the layer to add.
+ /// The current instance of the .
+ public CatalystAppBuilder AddLayer(TLayer layer) where TLayer : ILayer {
+ ModelRegistry.RegisterLayer(layer);
+ return this;
+ }
+
+ ///
+ /// Makes a connector available for use in the CatalystUI application.
+ ///
+ ///
+ ///
+ /// The added connector is registered with the ,
+ /// which allows it to be used globally across the application.
+ ///
+ ///
+ /// Connectors allow different layers to communicate with each other,
+ /// and for their underlying domains to interact seamlessly. To add
+ /// new functionality to the application, you can insert a layer
+ /// and connect it to existing layers using connectors.
+ ///
+ ///
+ ///
+ /// The connector to add.
+ /// The type of the connector to add.
+ /// The current instance of the .
+ public CatalystAppBuilder AddConnector(TConnector connector) where TConnector : IConnector, ILayer> {
+ ModelRegistry.RegisterConnector(connector);
+ return this;
+ }
+
+ ///
+ /// Builds the CatalystUI Application.
+ ///
+ ///
+ ///
+ /// Building a Catalyst application will capture the thread on which it is called.
+ /// This thread should be the main thread of the application to ensure proper operation.
+ /// Failure to do so may result in unexpected behavior and occasional crashes.
+ ///
+ ///
+ /// The resulting run method will be executed on a separate thread. If the caller needs
+ /// to perform work on the main thread, it should use the
+ /// to schedule work on the main thread.
+ ///
+ ///
+ /// The application will not exit until the run method completes or the method is called.
+ /// To keep an application running indefinitely, you would do as you would in a typical
+ /// application, such as using a loop or waiting for user input.
+ ///
+ ///
+ /// The method to run when the application starts.
+ /// A new instance of a CatalystUI application.
+ public void Build(Func runMethod) {
+ _ = new CatalystApp(runMethod);
+ }
+
+ ///
+ public void Build(Action runMethod) {
+ _ = new CatalystApp(app => {
+ runMethod(app);
+ return 0; // Default exit code
+ });
+ }
+
+ ///
+ public void Build(Func runMethod) {
+ _ = new CatalystApp(app => {
+ runMethod(app).GetAwaiter().GetResult();
+ return 0; // Default exit code
+ });
+ }
+
+ ///
+ public void Build(Func> runMethod) {
+ _ = new CatalystApp(app => runMethod(app).GetAwaiter().GetResult());
+ }
+
+ ///
+ /// When building a Catalyst application, it is recommended to use the overloads that accept a or as the run method,
+ /// which allows the application to capture the main thread and prevent issues during asynchronous operations.
+ /// This method is provided for advanced use cases only and should be used with caution,
+ /// as some functionality may cause unexpected behavior if the main thread is not captured correctly.
+ /// MacOS and Linux seem to be particularly sensitive to this, as they require the main thread to be
+ /// captured for proper system-level operations and UI interactions.
+ ///
+ ///
+ [Obsolete("Use Build(Action) or Build(Func) whenever possible. This method is provided for advanced use cases only.")]
+ [EditorBrowsable(EditorBrowsableState.Advanced)]
+ public void Build() {
+ _ = new CatalystApp();
+ }
+
+ }
+
+}
\ No newline at end of file
diff --git a/CatalystUI/Core/CatalystUI.Core/Builders/Extensions/CatalystAppBuilderExtensions.cs b/CatalystUI/Core/CatalystUI.Core/Builders/Extensions/CatalystAppBuilderExtensions.cs
new file mode 100644
index 0000000..784d544
--- /dev/null
+++ b/CatalystUI/Core/CatalystUI.Core/Builders/Extensions/CatalystAppBuilderExtensions.cs
@@ -0,0 +1,26 @@
+// -------------------------------------------------------------------------------------------------
+// CatalystUI Framework for .NET Core - https://catalystui.org/
+// Copyright (c) 2025 CatalystUI LLC. All rights reserved.
+//
+// This file is part of CatalystUI and is provided as part of an early-access release.
+// Unauthorized commercial use, distribution, or modification is strictly prohibited.
+//
+// This software is not open source and is not publicly licensed.
+// For full terms, see the LICENSE and NOTICE files in the project root.
+// -------------------------------------------------------------------------------------------------
+
+namespace Catalyst.Builders.Extensions {
+
+ ///
+ /// The following class doesn't provide any functionality itself,
+ /// but it provides the namespace for extension methods,
+ /// so conditional compilation doesn't yell at end-users
+ /// if they need it during a debug build but not a release build.
+ ///
+ public static class CatalystAppBuilderExtensions {
+
+ // ...
+
+ }
+
+}
\ No newline at end of file
diff --git a/CatalystUI/Core/CatalystUI.Core/CatalystApp.cs b/CatalystUI/Core/CatalystUI.Core/CatalystApp.cs
new file mode 100644
index 0000000..c58b100
--- /dev/null
+++ b/CatalystUI/Core/CatalystUI.Core/CatalystApp.cs
@@ -0,0 +1,109 @@
+// -------------------------------------------------------------------------------------------------
+// CatalystUI Framework for .NET Core - https://catalystui.org/
+// Copyright (c) 2025 CatalystUI LLC. All rights reserved.
+//
+// This file is part of CatalystUI and is provided as part of an early-access release.
+// Unauthorized commercial use, distribution, or modification is strictly prohibited.
+//
+// This software is not open source and is not publicly licensed.
+// For full terms, see the LICENSE and NOTICE files in the project root.
+// -------------------------------------------------------------------------------------------------
+
+using Catalyst.Debugging;
+using Catalyst.Threading;
+using System;
+using System.Threading;
+
+namespace Catalyst {
+
+ ///
+ /// An application utilizing the CatalystUI framework.
+ ///
+ public class CatalystApp {
+
+ ///
+ /// The dispatcher used to handle threading operations in the CatalystUI framework.
+ ///
+ ///
+ /// In most situations, the dispatcher will be the one used to capture the main thread of the application.
+ /// A and associated should
+ /// always be created on the main thread of the application. Failure to do so may result in
+ /// unexpected behavior and occasional crashes.
+ ///
+ public ThreadDelegateDispatcher Dispatcher { get; private set; } = null!; // set in the captured main thread
+
+ ///
+ /// The debug context for the application.
+ ///
+ protected readonly DebugContext _debug;
+
+ ///
+ /// The exit code of the application.
+ ///
+ private volatile int _exitCode;
+
+ ///
+ /// A flag indicating whether the application is exiting.
+ ///
+ private volatile bool _isExiting;
+
+ ///
+ /// A lock used to ensure thread safety when performing Catalyst
+ /// synchronizational operations, such as exiting the application.
+ ///
+ private readonly Lock _lock;
+
+ ///
+ /// Constructs a new .
+ /// This method will not return.
+ ///
+ internal CatalystApp(Func? runMethod = null) {
+ // Fields
+ _debug = CatalystDebug.ForContext("Application");
+ _exitCode = 0;
+ _isExiting = false;
+ _lock = new();
+
+ // Properties
+ _debug.Log(LogLevel.Verbose, "Capturing main thread dispatcher and running application...");
+ ThreadDelegateDispatcher.Capture(dispatcher => {
+ // Assign the main-thread dispatcher to the app.
+ Dispatcher = dispatcher;
+ Thread.CurrentThread.Name = "MainThread";
+ _debug.Log(LogLevel.Verbose, "Main thread dispatcher captured successfully.");
+
+ // Run the caller's method if provided.
+ if (runMethod != null) {
+ _debug.Log(LogLevel.Info, "CatalystApp initialized, initializing worker thread. Application will run until the provided run method exits.");
+ ThreadDelegateDispatcher workerThread = ThreadDelegateDispatcher.New("CallerThread");
+ workerThread.Execute(() => {
+ Exit(runMethod(this));
+ });
+ } else {
+ _debug.Log(LogLevel.Info, "CatalystApp initialized. No run method provided, application will run until exit.");
+ }
+ }, isMainThread: true);
+
+ // Set the exit code and let the application return gracefully.
+ Environment.ExitCode = _exitCode;
+ }
+
+ ///
+ /// Kills the main-thread dispatcher and exits the application with the specified exit code.
+ ///
+ /// The exit code to use when exiting the application.
+ public void Exit(int code = 0) {
+ if (_isExiting) return;
+ _lock.Enter();
+ try {
+ _exitCode = code;
+ Dispatcher.Dispose();
+ _isExiting = true;
+ } finally {
+ _lock.Exit();
+ }
+ }
+
+ }
+
+}
\ No newline at end of file
diff --git a/CatalystUI/Core/CatalystUI.Core/CatalystUI.Core.csproj b/CatalystUI/Core/CatalystUI.Core/CatalystUI.Core.csproj
index 339ec99..f4acb4e 100644
--- a/CatalystUI/Core/CatalystUI.Core/CatalystUI.Core.csproj
+++ b/CatalystUI/Core/CatalystUI.Core/CatalystUI.Core.csproj
@@ -19,8 +19,9 @@
-
+
+
diff --git a/CatalystUI/Core/CatalystUI.Core/Debugging/CatalystDebug.cs b/CatalystUI/Core/CatalystUI.Core/Debugging/CatalystDebug.cs
index e4d4f8b..d3dec4b 100644
--- a/CatalystUI/Core/CatalystUI.Core/Debugging/CatalystDebug.cs
+++ b/CatalystUI/Core/CatalystUI.Core/Debugging/CatalystDebug.cs
@@ -317,12 +317,12 @@ public static LogLevel DetermineScopeLevel(string scope) {
level = parsedResult.Value;
_enabledScopes.TryAdd(scope, level);
} else {
- Trace.WriteLine($"⚠️ Scope '{scope}' is not defined in the configuration file. Using default level: {DebugOptions.MinimumLogLevel}");
+ Trace.WriteLine($"⚠️ [Catalyst.Debugging] Scope '{scope}' is not defined in the configuration file. Using default level: {DebugOptions.MinimumLogLevel}");
}
// Scope output
if (DebugOptions.MinimumLogLevel >= LogLevel.Debug) {
- Trace.WriteLine($"ℹ️ Determined scope '{scope}' level: {level}");
+ Trace.WriteLine($"ℹ️ [Catalyst.Debugging] Determined scope '{scope}' level: {level}");
}
}
return level;
@@ -362,7 +362,7 @@ private static void PreloadScopes() {
/// A function that constructs a new .
public static void InjectDebugContext(Func constructor) {
if (_debugContextConstructor != null) {
- Trace.WriteLine("⚠️ A debug constructor has already been set. The previous constructor will be replaced with the new one.");
+ Trace.WriteLine("⚠️ [Catalyst.Debugging] A debug constructor has already been set. The previous constructor will be replaced with the new one.");
Trace.WriteLine(constructor.GetType().GetGenericArguments()[1].FullName);
}
_debugContextConstructor = constructor;
diff --git a/CatalystUI/Core/CatalystUI.Core/Interactions/IInputData.cs b/CatalystUI/Core/CatalystUI.Core/Interactions/IInputData.cs
new file mode 100644
index 0000000..eb1b390
--- /dev/null
+++ b/CatalystUI/Core/CatalystUI.Core/Interactions/IInputData.cs
@@ -0,0 +1,23 @@
+// -------------------------------------------------------------------------------------------------
+// CatalystUI Framework for .NET Core - https://catalystui.org/
+// Copyright (c) 2025 CatalystUI LLC. All rights reserved.
+//
+// This file is part of CatalystUI and is provided as part of an early-access release.
+// Unauthorized commercial use, distribution, or modification is strictly prohibited.
+//
+// This software is not open source and is not publicly licensed.
+// For full terms, see the LICENSE and NOTICE files in the project root.
+// -------------------------------------------------------------------------------------------------
+
+namespace Catalyst.Interactions {
+
+ ///
+ /// Represents the data passed from the input device to the interaction.
+ ///
+ public interface IInputData {
+
+ // ...
+
+ }
+
+}
\ No newline at end of file
diff --git a/CatalystUI/Core/CatalystUI.Core/Interactions/IInteraction.cs b/CatalystUI/Core/CatalystUI.Core/Interactions/IInteraction.cs
index b5a1d22..2fb777f 100644
--- a/CatalystUI/Core/CatalystUI.Core/Interactions/IInteraction.cs
+++ b/CatalystUI/Core/CatalystUI.Core/Interactions/IInteraction.cs
@@ -22,6 +22,12 @@ public interface IInteraction {
/// The input device, or if the origin is unknown.
IInputDevice? InputDevice { get; }
+ ///
+ /// Gets the input data associated with the interaction.
+ ///
+ /// The input data, or if no data is associated.
+ IInputData? InputData { get; }
+
}
}
\ No newline at end of file
diff --git a/CatalystUI/Core/CatalystUI.Core/ModelRegistry.cs b/CatalystUI/Core/CatalystUI.Core/ModelRegistry.cs
new file mode 100644
index 0000000..e1f987b
--- /dev/null
+++ b/CatalystUI/Core/CatalystUI.Core/ModelRegistry.cs
@@ -0,0 +1,272 @@
+// -------------------------------------------------------------------------------------------------
+// CatalystUI Framework for .NET Core - https://catalystui.org/
+// Copyright (c) 2025 CatalystUI LLC. All rights reserved.
+//
+// This file is part of CatalystUI and is provided as part of an early-access release.
+// Unauthorized commercial use, distribution, or modification is strictly prohibited.
+//
+// This software is not open source and is not publicly licensed.
+// For full terms, see the LICENSE and NOTICE files in the project root.
+// -------------------------------------------------------------------------------------------------
+
+using Catalyst.Connectors;
+using Catalyst.Debugging;
+using Catalyst.Domains;
+using Catalyst.Layers;
+using System;
+using System.Collections.Generic;
+using System.Threading;
+using System.Linq;
+
+using LayerSet = System.Collections.Generic.HashSet>;
+using LayerMap = System.Collections.Generic.Dictionary>>;
+using DomainMap = System.Collections.Generic.Dictionary>>>;
+
+using ConnectorSet = System.Collections.Generic.HashSet, Catalyst.Layers.ILayer>>;
+using LowLayerConnectorMap = System.Collections.Generic.Dictionary, Catalyst.Layers.ILayer>>>;
+using HighLayerLowLayerMap = System.Collections.Generic.Dictionary, Catalyst.Layers.ILayer>>>>;
+
+namespace Catalyst {
+
+ ///
+ /// Provides registration services for implementations of the CatalystUI model in .NET Core applications.
+ ///
+ public static class ModelRegistry {
+
+ ///
+ /// A dictionary that maps domain types to their corresponding layers.
+ ///
+ private static readonly DomainMap _layers;
+
+ ///
+ /// A dictionary that maps two layer types to their corresponding connector.
+ ///
+ private static readonly HighLayerLowLayerMap _connectors;
+
+ ///
+ /// The debug context for the model registry.
+ ///
+ private static readonly DebugContext _debug;
+
+ ///
+ /// A lock used for thread-safety.
+ ///
+ private static readonly Lock _lock;
+
+ ///
+ /// Static constructor for .
+ ///
+ static ModelRegistry() {
+ _layers = [];
+ _connectors = [];
+ _debug = CatalystDebug.ForContext("ModelRegistry");
+ _lock = new();
+ }
+
+ ///
+ /// Registers a layer instance with the model registry.
+ ///
+ /// The layer instance to register.
+ /// The type of the layer instance being registered.
+ /// Thrown if is null.
+ /// Thrown if a layer of the same type is already registered for the same domain.
+ public static void RegisterLayer(TLayer layer) where TLayer : ILayer {
+ if (layer == null) throw new ArgumentNullException(nameof(layer), "Layer cannot be null.");
+ _lock.Enter();
+ try {
+ Type domainType = TLayer.Domain;
+ Type layerType = typeof(TLayer);
+
+ // Ensure the layer map for the domain exists
+ if (!_layers.TryGetValue(domainType, out LayerMap? layerMap)) {
+ layerMap = [];
+ _layers[domainType] = layerMap;
+ }
+
+ // Ensure the layer instances set for the layer type exists
+ if (!layerMap.TryGetValue(layerType, out LayerSet? layerSet)) {
+ layerSet = [];
+ layerMap[layerType] = layerSet;
+ }
+
+ // Register the layer instance
+ if (!layerSet.Add(layer)) {
+ throw new InvalidOperationException($"The specified layer of type {layerType.FullName} is already registered for domain {domainType.FullName}.");
+ }
+ _debug.Log(LogLevel.Info, $"Registered layer of type {layerType.FullName} for domain {domainType.FullName}.");
+ } finally {
+ _lock.Exit();
+ }
+ }
+
+ ///
+ /// Registers a connector instance with the model registry.
+ ///
+ /// The connector instance to register.
+ /// The type of the connector instance being registered.
+ /// Thrown if is null.
+ /// Thrown if a connector of the same type is already registered between the same layers.
+ public static void RegisterConnector(TConnector connector) where TConnector : IConnector, ILayer> {
+ if (connector == null) throw new ArgumentNullException(nameof(connector), "Connector cannot be null.");
+ _lock.Enter();
+ try {
+ Type highLayerType = TConnector.HighLayer;
+ Type lowLayerType = TConnector.LowLayer;
+
+ // Ensure the low layer map for the high layer exists
+ if (!_connectors.TryGetValue(highLayerType, out LowLayerConnectorMap? lowLayerMap)) {
+ lowLayerMap = [];
+ _connectors[highLayerType] = lowLayerMap;
+ }
+
+ // Ensure the connector set for the low layer type exists
+ if (!lowLayerMap.TryGetValue(lowLayerType, out ConnectorSet? connectorSet)) {
+ connectorSet = [];
+ lowLayerMap[lowLayerType] = connectorSet;
+ }
+
+ // Register the connector instance
+ if (!connectorSet.Add(connector)) {
+ throw new InvalidOperationException($"The specified connector of type {typeof(TConnector).FullName} is already registered between layers {highLayerType.FullName} and {lowLayerType.FullName}.");
+ }
+ _debug.Log(LogLevel.Info, $"Registered connector of type {typeof(TConnector).FullName} between layers {highLayerType.FullName} and {lowLayerType.FullName}.");
+ } finally {
+ _lock.Exit();
+ }
+ }
+
+ ///
+ /// Requests for the first registered layer instance of the specified type.
+ ///
+ ///
+ /// When multiple instances of the same layer type are registered, returns the first instance found.
+ ///
+ /// The type of layer being requested.
+ /// The first registered instance of the specified layer type.
+ /// Thrown if no layer of the specified type is registered.
+ public static TLayer RequestLayer() where TLayer : ILayer {
+ _lock.Enter();
+ try {
+ Type domainType = TLayer.Domain;
+ Type layerType = typeof(TLayer);
+
+ // First, check for an exact type match and return the first instance found
+ if (_layers.TryGetValue(domainType, out LayerMap? layerMap)) {
+ if (layerMap.TryGetValue(layerType, out LayerSet? layerSet) && layerSet.Count > 0) {
+ return (TLayer) layerSet.First();
+ }
+ }
+
+ // Otherwise, search for assignable types and return the first instance found
+ foreach (KeyValuePair kvp in _layers) {
+ if (domainType.IsAssignableFrom(kvp.Key)) {
+ foreach (KeyValuePair innerKvp in kvp.Value) {
+ if (layerType.IsAssignableFrom(innerKvp.Key) && innerKvp.Value.Count > 0) {
+ return (TLayer) innerKvp.Value.First();
+ }
+ }
+ }
+ }
+
+ // If no layer is found, throw an exception
+ throw new InvalidOperationException($"No registered layer of type {layerType.FullName} found for domain {domainType.FullName}.");
+ } finally {
+ _lock.Exit();
+ }
+ }
+
+ ///
+ /// Requests all registered layer instances of the specified type.
+ ///
+ /// The type of layer being requested.
+ /// An enumerable for all registered instances of the specified layer type.
+ public static IEnumerable RequestLayers() where TLayer : ILayer {
+ _lock.Enter();
+ try {
+ Type domainType = TLayer.Domain;
+ Type layerType = typeof(TLayer);
+ foreach (KeyValuePair kvp in _layers) {
+ if (domainType.IsAssignableFrom(kvp.Key)) {
+ foreach (KeyValuePair innerKvp in kvp.Value) {
+ if (layerType.IsAssignableFrom(innerKvp.Key) && innerKvp.Value.Count > 0) {
+ foreach (ILayer layer in innerKvp.Value) {
+ yield return (TLayer) layer;
+ }
+ }
+ }
+ }
+ }
+ } finally {
+ _lock.Exit();
+ }
+ }
+
+ ///
+ /// Requests for the first registered connector instance of the specified type.
+ ///
+ ///
+ /// When multiple instances of the same connector type are registered, returns the first instance found.
+ ///
+ /// The type of connector being requested.
+ /// The first registered instance of the specified connector type.
+ /// Thrown if no connector of the specified type is registered.
+ public static TConnector RequestConnector() where TConnector : IConnector, ILayer> {
+ _lock.Enter();
+ try {
+ Type highLayerType = TConnector.HighLayer;
+ Type lowLayerType = TConnector.LowLayer;
+
+ // First, check for an exact type match and return the first instance found
+ if (_connectors.TryGetValue(highLayerType, out LowLayerConnectorMap? lowLayerMap)) {
+ if (lowLayerMap.TryGetValue(lowLayerType, out ConnectorSet? connectorSet) && connectorSet.Count > 0) {
+ return (TConnector) connectorSet.First();
+ }
+ }
+
+ // Otherwise, search for assignable types and return the first instance found
+ foreach (KeyValuePair kvp in _connectors) {
+ if (highLayerType.IsAssignableFrom(kvp.Key)) {
+ foreach (KeyValuePair innerKvp in kvp.Value) {
+ if (lowLayerType.IsAssignableFrom(innerKvp.Key) && innerKvp.Value.Count > 0) {
+ return (TConnector) innerKvp.Value.First();
+ }
+ }
+ }
+ }
+
+ // If no connector is found, throw an exception
+ throw new InvalidOperationException($"No registered connector of type {typeof(TConnector).FullName} found between layers {highLayerType.FullName} and {lowLayerType.FullName}.");
+ } finally {
+ _lock.Exit();
+ }
+ }
+
+ ///
+ /// Requests all registered connector instances of the specified type.
+ ///
+ /// The type of connector being requested.
+ /// An enumerable for all registered instances of the specified connector type.
+ public static IEnumerable RequestConnectors() where TConnector : IConnector, ILayer> {
+ _lock.Enter();
+ try {
+ Type highLayerType = TConnector.HighLayer;
+ Type lowLayerType = TConnector.LowLayer;
+ foreach (KeyValuePair kvp in _connectors) {
+ if (highLayerType.IsAssignableFrom(kvp.Key)) {
+ foreach (KeyValuePair innerKvp in kvp.Value) {
+ if (lowLayerType.IsAssignableFrom(innerKvp.Key) && innerKvp.Value.Count > 0) {
+ foreach (IConnector, ILayer> connector in innerKvp.Value) {
+ yield return (TConnector) connector;
+ }
+ }
+ }
+ }
+ }
+ } finally {
+ _lock.Exit();
+ }
+ }
+
+ }
+
+}
\ No newline at end of file
diff --git a/CatalystUI/Core/CatalystUI.Core/Native/INativeApi.cs b/CatalystUI/Core/CatalystUI.Core/Native/INativeApi.cs
new file mode 100644
index 0000000..6ad2be3
--- /dev/null
+++ b/CatalystUI/Core/CatalystUI.Core/Native/INativeApi.cs
@@ -0,0 +1,37 @@
+// -------------------------------------------------------------------------------------------------
+// CatalystUI Framework for .NET Core - https://catalystui.org/
+// Copyright (c) 2025 CatalystUI LLC. All rights reserved.
+//
+// This file is part of CatalystUI and is provided as part of an early-access release.
+// Unauthorized commercial use, distribution, or modification is strictly prohibited.
+//
+// This software is not open source and is not publicly licensed.
+// For full terms, see the LICENSE and NOTICE files in the project root.
+// -------------------------------------------------------------------------------------------------
+
+using System;
+
+namespace Catalyst.Native {
+
+ ///
+ /// Represents a wrapper for an API which provides native access using the Singleton pattern.
+ ///
+ /// A reference to the type of the API instance which is generated.
+ /// A reference to the type of the wrapped API instance, if applicable.
+ public interface INativeApi : IDisposable where TSelf : INativeApi {
+
+ ///
+ /// Gets the API instance which is being wrapped.
+ ///
+ /// The wrapped API instance.
+ TApi Api { get; }
+
+ ///
+ /// Requests an instance of the API.
+ ///
+ /// The instance of the API.
+ static abstract TSelf GetInstance();
+
+ }
+
+}
\ No newline at end of file
diff --git a/CatalystUI/Core/CatalystUI.Core/Native/NativeException.cs b/CatalystUI/Core/CatalystUI.Core/Native/NativeException.cs
new file mode 100644
index 0000000..25b52f6
--- /dev/null
+++ b/CatalystUI/Core/CatalystUI.Core/Native/NativeException.cs
@@ -0,0 +1,48 @@
+// -------------------------------------------------------------------------------------------------
+// CatalystUI Framework for .NET Core - https://catalystui.org/
+// Copyright (c) 2025 CatalystUI LLC. All rights reserved.
+//
+// This file is part of CatalystUI and is provided as part of an early-access release.
+// Unauthorized commercial use, distribution, or modification is strictly prohibited.
+//
+// This software is not open source and is not publicly licensed.
+// For full terms, see the LICENSE and NOTICE files in the project root.
+// -------------------------------------------------------------------------------------------------
+
+using System;
+
+namespace Catalyst.Native {
+
+ ///
+ /// Represents an error that occurs during native operations.
+ ///
+ ///
+ public class NativeException : Exception {
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public NativeException() : base() {
+ // ...
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// with a specified error message.
+ ///
+ public NativeException(string message) : base(message) {
+ // ...
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// with a specified error message and a reference to the inner exception
+ /// that is the cause of this exception.
+ ///
+ public NativeException(string message, Exception innerException) : base(message, innerException) {
+ // ...
+ }
+
+ }
+
+}
\ No newline at end of file
diff --git a/CatalystUI/Core/CatalystUI.Debug/CatalystSerilogDebug.cs b/CatalystUI/Core/CatalystUI.Debug/CatalystSerilogDebug.cs
new file mode 100644
index 0000000..3dc2b16
--- /dev/null
+++ b/CatalystUI/Core/CatalystUI.Debug/CatalystSerilogDebug.cs
@@ -0,0 +1,165 @@
+// -------------------------------------------------------------------------------------------------
+// CatalystUI Framework for .NET Core - https://catalystui.org/
+// Copyright (c) 2025 CatalystUI LLC. All rights reserved.
+//
+// This file is part of CatalystUI and is provided as part of an early-access release.
+// Unauthorized commercial use, distribution, or modification is strictly prohibited.
+//
+// This software is not open source and is not publicly licensed.
+// For full terms, see the LICENSE and NOTICE files in the project root.
+// -------------------------------------------------------------------------------------------------
+
+using Serilog;
+using Serilog.Events;
+using Serilog.Templates;
+using System;
+using System.Diagnostics;
+using System.IO;
+
+namespace Catalyst.Debugging {
+
+ ///
+ /// Serilog utilities for debugging within the CatalystUI.Debug framework.
+ ///
+ public static class CatalystSerilogDebug {
+
+ ///
+ /// Gets the current logger instance for debugging purposes.
+ ///
+ private static ILogger? _logger;
+
+ ///
+ /// Static constructor for .
+ ///
+ static CatalystSerilogDebug() {
+ // Configure the logger
+ ConfigureLogger();
+ }
+
+ ///
+ /// Configures the global logger for debugging.
+ ///
+ private static void ConfigureLogger() {
+ LoggerConfiguration config = new();
+
+ // Apply requested minimum level to the logger configuration
+ switch (CatalystDebug.DebugOptions.MinimumLogLevel) {
+ case LogLevel.Critical:
+ config.MinimumLevel.Fatal();
+ break;
+ case LogLevel.Error:
+ config.MinimumLevel.Error();
+ break;
+ case LogLevel.Warning:
+ config.MinimumLevel.Warning();
+ break;
+ case LogLevel.Info:
+ config.MinimumLevel.Information();
+ break;
+ case LogLevel.Debug:
+ config.MinimumLevel.Debug();
+ break;
+ case LogLevel.Verbose:
+ config.MinimumLevel.Verbose();
+ break;
+ case LogLevel.None:
+ default:
+ config.MinimumLevel.Debug();
+ break;
+ }
+
+ // Add an async sink
+ config.WriteTo.Async(writeTo => {
+ // Create the logging template
+ string levelmap = @"
+ if @l = 'Verbose' then 'VERBOSE'
+ else if @l = 'Debug' then 'DEBUG'
+ else if @l = 'Information' then 'INFO'
+ else if @l = 'Warning' then 'WARN'
+ else if @l = 'Error' then 'ERROR'
+ else if @l = 'Fatal' then 'CRITICAL'
+ else 'UNKNOWN'
+ ";
+ string thread = $@"
+ if ThreadName is not null then ThreadName
+ else if ThreadId = {Environment.CurrentManagedThreadId} then 'MainThread'
+ else Concat('Thread ', ToString(ThreadId))
+ ";
+ string threadTemplate = CatalystDebug.DebugOptions.ShowsThread ? "<{" + thread + "}> " : string.Empty;
+ string template = threadTemplate + "[{SourceContext}] [{@t:HH:mm:ss:fff}] [{" + levelmap + "}] {@m}{if @x is not null then '" + Environment.NewLine + "' + @x else ''}";
+ ExpressionTemplate formatter = new(template + Environment.NewLine);
+
+ // Add a debug sink
+ writeTo.Debug(formatter);
+
+ // Add a file sink
+ try {
+ if (File.Exists(CatalystDebug.OUTPUT_FILE_NAME))
+ File.Delete(CatalystDebug.OUTPUT_FILE_NAME);
+ } catch {
+ // eh
+ }
+ writeTo.File(
+ path: CatalystDebug.OUTPUT_FILE_NAME,
+ retainedFileCountLimit: 1,
+ formatter: formatter
+ );
+ });
+
+ // Construct the logger
+ _logger = config
+ .Enrich.WithThreadId()
+ .Enrich.WithThreadName()
+ .CreateLogger()
+ .ForContext("SourceContext", "");
+ Log.Logger = _logger; // Set the global logger to the configured logger
+
+ // Log initialization
+ Trace.WriteLine($"ℹ️ [Catalyst.Debugging] Catalyst debugging initialized with minimum log level: {CatalystDebug.DebugOptions.MinimumLogLevel}");
+ }
+
+ ///
+ /// Converts a Serilog to a .
+ ///
+ /// The Serilog log event level to convert.
+ /// A corresponding to the provided Serilog log event level.
+ public static LogLevel FromLogEventLevel(LogEventLevel level) {
+ return level switch {
+ LogEventLevel.Verbose => LogLevel.Verbose,
+ LogEventLevel.Debug => LogLevel.Debug,
+ LogEventLevel.Information => LogLevel.Info,
+ LogEventLevel.Warning => LogLevel.Warning,
+ LogEventLevel.Error => LogLevel.Error,
+ LogEventLevel.Fatal => LogLevel.Critical,
+ _ => LogLevel.None
+ };
+ }
+
+ ///
+ /// Converts a to a Serilog .
+ ///
+ /// The log level to convert.
+ /// A Serilog corresponding to the provided log level.
+ public static LogEventLevel ToLogEventLevel(LogLevel logLevel) {
+ return logLevel switch {
+ LogLevel.Verbose => LogEventLevel.Verbose,
+ LogLevel.Debug => LogEventLevel.Debug,
+ LogLevel.Info => LogEventLevel.Information,
+ LogLevel.Warning => LogEventLevel.Warning,
+ LogLevel.Error => LogEventLevel.Error,
+ LogLevel.Critical => LogEventLevel.Fatal,
+ _ => LogEventLevel.Debug // Default to Debug if None
+ };
+ }
+
+ ///
+ /// Requests a logger for the specified scope.
+ ///
+ internal static (ILogger, LogLevel)? RequestLogger(string scope) {
+ if (_logger == null) return null; // how?
+ return (_logger.ForContext("SourceContext", scope), CatalystDebug.DetermineScopeLevel(scope));
+ }
+
+ }
+
+}
\ No newline at end of file
diff --git a/CatalystUI/Core/CatalystUI.Debug/CatalystUI.Debug.csproj b/CatalystUI/Core/CatalystUI.Debug/CatalystUI.Debug.csproj
new file mode 100644
index 0000000..e4d4cbc
--- /dev/null
+++ b/CatalystUI/Core/CatalystUI.Debug/CatalystUI.Debug.csproj
@@ -0,0 +1,29 @@
+
+
+
+
+
+ Catalyst.Debugging
+ Catalyst.Debug
+
+
+ CatalystUI Debugging
+ 1.0.0
+ beta.2
+ CatalystUI LLC
+ Debugging API provided by the CatalystUI library.
+ CatalystUI,debug,debugging
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CatalystUI/Core/CatalystUI.Debug/Extensions/CatalystAppBuilderExtensions.cs b/CatalystUI/Core/CatalystUI.Debug/Extensions/CatalystAppBuilderExtensions.cs
new file mode 100644
index 0000000..012662d
--- /dev/null
+++ b/CatalystUI/Core/CatalystUI.Debug/Extensions/CatalystAppBuilderExtensions.cs
@@ -0,0 +1,64 @@
+// -------------------------------------------------------------------------------------------------
+// CatalystUI Framework for .NET Core - https://catalystui.org/
+// Copyright (c) 2025 CatalystUI LLC. All rights reserved.
+//
+// This file is part of CatalystUI and is provided as part of an early-access release.
+// Unauthorized commercial use, distribution, or modification is strictly prohibited.
+//
+// This software is not open source and is not publicly licensed.
+// For full terms, see the LICENSE and NOTICE files in the project root.
+// -------------------------------------------------------------------------------------------------
+
+using Catalyst.Debugging;
+
+// ReSharper disable once CheckNamespace
+namespace Catalyst.Builders.Extensions {
+
+ ///
+ /// Builder extensions for the .
+ ///
+ public static class CatalystAppBuilderExtensions {
+
+ ///
+ /// Instructs the instance to use the Catalyst.Debug
+ /// library for debugging operations.
+ ///
+ ///
+ ///
+ /// must be the first method called on the
+ /// instance, as it sets up the debug context for the entire application
+ /// and any subsequent calls to the methods,
+ /// including in various static classes and methods.
+ ///
+ ///
+ /// 99.99% of the time, you will want to wrap the method in #if DEBUG
+ /// preprocessor directives to ensure that it is only included in debug builds.
+ /// The same is true for the inclusion of the Catalyst.Debug library itself.
+ /// Failure to do so will result in a large amount of bloat added to the
+ /// executable built for release, as well as a significant performance
+ /// impact due to the overhead of the debug logging system.
+ ///
+ ///
+ ///
+ ///
+ /// public static void Main(string[] args) {
+ /// new CatalystAppBuilder()
+ /// #if DEBUG
+ /// .UseCatalystDebug()
+ /// #endif
+ /// .AddArcaneIniModule()
+ /// .AddCrystalGlfw3Module()
+ /// .Build(Run);
+ /// }
+ ///
+ ///
+ /// The instance.
+ /// The modified instance.
+ public static CatalystAppBuilder UseCatalystDebug(this CatalystAppBuilder builder) {
+ CatalystDebug.InjectDebugContext(scope => new SerilogDebugContext(scope));
+ return builder;
+ }
+
+ }
+
+}
\ No newline at end of file
diff --git a/CatalystUI/Core/CatalystUI.Debug/SerilogDebugContext.cs b/CatalystUI/Core/CatalystUI.Debug/SerilogDebugContext.cs
new file mode 100644
index 0000000..875c2ef
--- /dev/null
+++ b/CatalystUI/Core/CatalystUI.Debug/SerilogDebugContext.cs
@@ -0,0 +1,155 @@
+// -------------------------------------------------------------------------------------------------
+// CatalystUI Framework for .NET Core - https://catalystui.org/
+// Copyright (c) 2025 CatalystUI LLC. All rights reserved.
+//
+// This file is part of CatalystUI and is provided as part of an early-access release.
+// Unauthorized commercial use, distribution, or modification is strictly prohibited.
+//
+// This software is not open source and is not publicly licensed.
+// For full terms, see the LICENSE and NOTICE files in the project root.
+// -------------------------------------------------------------------------------------------------
+
+using Serilog;
+using System;
+using System.Diagnostics;
+using System.IO;
+using System.Text;
+
+namespace Catalyst.Debugging {
+
+ ///
+ /// A debug context for Serilog integration within the CatalystUI framework.
+ ///
+ public sealed class SerilogDebugContext : DebugContext {
+
+ ///
+ /// Gets the Serilog logger instance for the debug context.
+ ///
+ /// A Serilog instance, or if not provided.
+ public ILogger? Logger { get; }
+
+ ///
+ public SerilogDebugContext(string scope) : base(scope) {
+ (ILogger Logger, LogLevel LogLevel)? result = CatalystSerilogDebug.RequestLogger(scope);
+ if (result == null) {
+ Logger = null;
+ } else {
+ Logger = result.Value.Logger;
+ Level = result.Value.LogLevel;
+ }
+ }
+
+ ///
+ public override void Log(LogLevel level, string message, string? prefix = null, params object[] args) {
+ if (Level < level) return;
+ if (Logger == null) return;
+ StringBuilder sb = new();
+ string? stackTrace = null;
+ try {
+ bool needsStackTrace = CatalystDebug.DebugOptions.ShowsFileName || CatalystDebug.DebugOptions.ShowsMethodName || CatalystDebug.DebugOptions.ShowsLineNumber || CatalystDebug.DebugOptions.ShowsStackTrace;
+ if (needsStackTrace) {
+ StackTrace trace = new(fNeedFileInfo: true, skipFrames: 2);
+ if (CatalystDebug.DebugOptions.ShowsStackTrace) stackTrace = trace.ToString();
+ StackFrame? frame = trace.GetFrame(0);
+ DiagnosticMethodInfo? method = frame != null ? DiagnosticMethodInfo.Create(frame) : null;
+ if (frame != null && method != null) {
+ bool needsContinuation = false;
+ bool needsComma = false;
+ if (CatalystDebug.DebugOptions.ShowsFileName) {
+ string? file = frame.GetFileName();
+ string? typeName = method.Name;
+ string? assemblyName = method.DeclaringAssemblyName;
+ sb.Append('<');
+ if (!string.IsNullOrEmpty(file)) {
+ sb.Append(Path.GetFileName(file));
+ } else if (!string.IsNullOrEmpty(typeName)) {
+ sb.Append(typeName);
+ } else if (!string.IsNullOrEmpty(assemblyName)) {
+ sb.Append(assemblyName);
+ } else {
+ sb.Append("Object");
+ }
+ needsContinuation = true;
+ needsComma = true;
+ }
+ if (CatalystDebug.DebugOptions.ShowsMethodName) {
+ if (needsComma) {
+ sb.Append('#');
+ } else {
+ sb.Append('<');
+ }
+ sb.Append(method.Name);
+ needsContinuation = true;
+ needsComma = true;
+ }
+ if (CatalystDebug.DebugOptions.ShowsLineNumber) {
+ if (needsComma) {
+ sb.Append('(');
+ } else {
+ sb.Append('<');
+ }
+ sb.Append(frame.GetFileLineNumber());
+ if (needsComma) sb.Append(')');
+ needsContinuation = true;
+ needsComma = true;
+ }
+ if (needsContinuation) {
+ sb.Append('>').Append(' ');
+ }
+ }
+ }
+ } catch {
+ // not supported probably
+ }
+ sb.Append(message).Append(' ');
+ if (args.Length > 0) {
+ if (args[0] is Exception e) {
+ stackTrace = e.StackTrace;
+ if (e.StackTrace == null) {
+ stackTrace = e.ToString();
+ // Remove the first two lines which are the exception message and type
+ stackTrace = stackTrace[(stackTrace.IndexOf('\n') + 1)..];
+ stackTrace = stackTrace[(stackTrace.IndexOf('\n') + 1)..];
+ }
+ } else {
+ for (int i = 0; i < args.Length; i++) {
+ sb.Append(args[i]?.ToString());
+ if (i < args.Length - 1) {
+ sb.Append(' ');
+ }
+ }
+ }
+ }
+ if (stackTrace != null) {
+ sb.AppendLine().Append(stackTrace);
+ }
+ switch (level) {
+ case LogLevel.Critical:
+ Logger.Fatal(sb.ToString());
+ break;
+ case LogLevel.Error:
+ Logger.Error(sb.ToString());
+ break;
+ case LogLevel.Warning:
+ Logger.Warning(sb.ToString());
+ break;
+ case LogLevel.Info:
+ Logger.Information(sb.ToString());
+ break;
+ case LogLevel.Debug:
+ Logger.Debug(sb.ToString());
+ break;
+ case LogLevel.Verbose:
+ Logger.Verbose(sb.ToString());
+ break;
+ case LogLevel.None:
+ break; // No logging for None level
+ default:
+ Logger.Debug(sb.ToString()); // Fall back to Debug for unknown levels
+ break;
+ }
+ }
+
+ }
+
+}
\ No newline at end of file
diff --git a/CatalystUI/Core/CatalystUI.Mathematics/CatalystUI.Mathematics.csproj b/CatalystUI/Core/CatalystUI.Mathematics/CatalystUI.Mathematics.csproj
new file mode 100644
index 0000000..75a29c1
--- /dev/null
+++ b/CatalystUI/Core/CatalystUI.Mathematics/CatalystUI.Mathematics.csproj
@@ -0,0 +1,18 @@
+
+
+
+
+
+ Catalyst.Mathematics
+ Catalyst.Mathematics
+
+
+ CatalystUI Mathematics
+ 1.0.0
+ beta.2
+ CatalystUI LLC
+ Mathematics API provided by the CatalystUI library.
+ CatalystUI,math,mathematics
+
+
+
\ No newline at end of file
diff --git a/CatalystUI/Core/CatalystUI.Mathematics/Geometry/Angle.cs b/CatalystUI/Core/CatalystUI.Mathematics/Geometry/Angle.cs
new file mode 100644
index 0000000..0f7d5c1
--- /dev/null
+++ b/CatalystUI/Core/CatalystUI.Mathematics/Geometry/Angle.cs
@@ -0,0 +1,253 @@
+// -------------------------------------------------------------------------------------------------
+// CatalystUI Framework for .NET Core - https://catalystui.org/
+// Copyright (c) 2025 CatalystUI LLC. All rights reserved.
+//
+// This file is part of CatalystUI and is provided as part of an early-access release.
+// Unauthorized commercial use, distribution, or modification is strictly prohibited.
+//
+// This software is not open source and is not publicly licensed.
+// For full terms, see the LICENSE and NOTICE files in the project root.
+// -------------------------------------------------------------------------------------------------
+
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+namespace Catalyst.Mathematics.Geometry {
+
+ ///
+ /// A programmatic representation of an angle.
+ ///
+ [StructLayout(LayoutKind.Sequential)]
+ public readonly record struct Angle {
+
+ ///
+ /// The underlying angle in radians.
+ ///
+ private readonly double _radians;
+
+ ///
+ /// Gets the angle in degrees.
+ ///
+ /// The angle in degrees.
+ public double Degrees => RadiansToDegrees(_radians);
+
+ ///
+ /// Gets the angle in radians.
+ ///
+ /// The angle in radians.
+ public double Radians => _radians;
+
+ ///
+ /// Gets the angle in gradians (also known as gon).
+ ///
+ /// The angle is gradians (gon).
+ public double Gradians => RadiansToGradians(_radians);
+
+ ///
+ /// Constructs a new .
+ ///
+ /// The angle in radians.
+ private Angle(double radians) {
+ _radians = radians;
+ }
+
+ ///
+ /// Converts an angle from radians to an instance.
+ ///
+ /// The angle in radians.
+ /// An instance representing the angle in radians.
+ public static Angle FromRadians(double radians) {
+ return new(radians);
+ }
+
+ ///
+ /// Converts an angle from degrees to an instance.
+ ///
+ /// The angle in degrees.
+ /// An instance representing the angle in degrees.
+ public static Angle FromDegrees(double degrees) {
+ return new(DegreesToRadians(degrees));
+ }
+
+ ///
+ /// Converts an angle from gradians (gon) to an instance.
+ ///
+ /// The angle in gradians (gon).
+ /// An instance representing the angle in gradians.
+ public static Angle FromGradians(double gradians) {
+ return new(GradiansToRadians(gradians));
+ }
+
+ ///
+ /// Normalizes the angle to a value between 0 and 2π radians (0 and 360 degrees).
+ ///
+ /// The normalized angle.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Angle Normalize() {
+ return new((_radians % (2 * Math.PI) + 2 * Math.PI) % (2 * Math.PI));
+ }
+
+ ///
+ /// Gets the quadrant of the angle.
+ ///
+ /// The quadrant of the angle.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Quadrant ToQuadrant() {
+ return (Quadrant) ((int) (Normalize()._radians / (Math.PI / 2)) + 1);
+ }
+
+ ///
+ /// Converts an angle from radians to degrees.
+ ///
+ /// The angle in radians.
+ /// The angle in degrees.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static double RadiansToDegrees(double radians) {
+ return radians * (180.0 / Math.PI);
+ }
+
+ ///
+ /// Converts an angle from radians to gradians (gon).
+ ///
+ /// The angle in radians.
+ /// The angle in gradians.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static double RadiansToGradians(double radians) {
+ return radians * (200.0 / Math.PI);
+ }
+
+ ///
+ /// Converts an angle from degrees to radians.
+ ///
+ /// The angle in degrees.
+ /// The angle in radians.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static double DegreesToRadians(double degrees) {
+ return degrees * (Math.PI / 180.0);
+ }
+
+ ///
+ /// Converts an angle from degrees to gradians (gon).
+ ///
+ /// The angle in degrees.
+ /// The angle in gradians.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static double DegreesToGradians(double degrees) {
+ return degrees * (10.0 / 9.0);
+ }
+
+ ///
+ /// Converts an angle from gradians (gon) to radians.
+ ///
+ /// The angle in gradians.
+ /// The angle in radians.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static double GradiansToRadians(double gradians) {
+ return gradians * (Math.PI / 200.0);
+ }
+
+ ///
+ /// Converts an angle from gradians (gon) to degrees.
+ ///
+ /// The angle in gradians.
+ /// The angle in degrees.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static double GradiansToDegrees(double gradians) {
+ return gradians * (9.0 / 1.0);
+ }
+
+ ///
+ /// Returns the value of the operand angle without modification.
+ ///
+ /// The angle to return.
+ /// The same angle instance.
+ public static Angle operator +(Angle angle) {
+ return angle;
+ }
+
+ ///
+ /// Returns the negated value of the operand angle.
+ ///
+ /// The angle to negate.
+ /// The negated angle instance.
+ public static Angle operator -(Angle angle) {
+ return new(-angle._radians);
+ }
+
+ ///
+ /// Increments the angle by 1 degree (approximately 0.0174533 radians).
+ ///
+ /// The angle to increment.
+ /// A new angle instance incremented by 1 degree.
+ public static Angle operator ++(Angle angle) {
+ return new(angle._radians + DegreesToRadians(1));
+ }
+
+ ///
+ /// Decrements the angle by 1 degree (approximately 0.0174533 radians).
+ ///
+ /// The angle to decrement.
+ /// A new angle instance decremented by 1 degree.
+ public static Angle operator --(Angle angle) {
+ return new(angle._radians - DegreesToRadians(1));
+ }
+
+ ///
+ /// Adds two angles together, resulting in a new angle.
+ ///
+ /// The first angle to add.
+ /// The second angle to add.
+ /// A new angle that is the sum of the two angles.
+ public static Angle operator +(Angle left, Angle right) {
+ return new(left._radians + right._radians);
+ }
+
+ ///
+ /// Subtracts one angle from another, resulting in a new angle.
+ ///
+ /// The angle to subtract from.
+ /// The angle to subtract.
+ /// A new angle that is the difference of the two angles.
+ public static Angle operator -(Angle left, Angle right) {
+ return new(left._radians - right._radians);
+ }
+
+ ///
+ /// Multiplies an angle by a scalar value, resulting in a new angle.
+ ///
+ /// The angle to multiply.
+ /// The scalar value to multiply the angle by.
+ /// A new angle that is the product of the angle and the scalar.
+ public static Angle operator *(Angle angle, double scalar) {
+ return new(angle._radians * scalar);
+ }
+
+ ///
+ /// Divides an angle by a scalar value, resulting in a new angle.
+ ///
+ /// The angle to divide.
+ /// The scalar value to divide the angle by.
+ /// A new angle that is the quotient of the angle and the scalar.
+ public static Angle operator /(Angle angle, double scalar) {
+ return new(angle._radians / scalar);
+ }
+
+ ///
+ /// Modulo operation on an angle with a scalar value, resulting in a new angle.
+ ///
+ /// The angle to apply the modulo operation on.
+ /// The scalar value to apply the modulo operation with.
+ /// A new angle that is the result of the modulo operation.
+ public static Angle operator %(Angle angle, double scalar) {
+ return new(angle._radians % scalar);
+ }
+
+ ///
+ public override string ToString() {
+ return $"{Degrees:F1}°";
+ }
+
+ }
+
+}
\ No newline at end of file
diff --git a/CatalystUI/Core/CatalystUI.Mathematics/Geometry/Quadrant.cs b/CatalystUI/Core/CatalystUI.Mathematics/Geometry/Quadrant.cs
new file mode 100644
index 0000000..0d4c7b8
--- /dev/null
+++ b/CatalystUI/Core/CatalystUI.Mathematics/Geometry/Quadrant.cs
@@ -0,0 +1,45 @@
+// -------------------------------------------------------------------------------------------------
+// CatalystUI Framework for .NET Core - https://catalystui.org/
+// Copyright (c) 2025 CatalystUI LLC. All rights reserved.
+//
+// This file is part of CatalystUI and is provided as part of an early-access release.
+// Unauthorized commercial use, distribution, or modification is strictly prohibited.
+//
+// This software is not open source and is not publicly licensed.
+// For full terms, see the LICENSE and NOTICE files in the project root.
+// -------------------------------------------------------------------------------------------------
+
+namespace Catalyst.Mathematics.Geometry {
+
+ ///
+ /// A list of quadrants in a Cartesian coordinate system.
+ ///
+ public enum Quadrant {
+
+ ///
+ /// The first quadrant (Q1) or top-right quadrant, where both x and y coordinates are positive.
+ ///
+ /// 1
+ First = 1,
+
+ ///
+ /// The second quadrant (Q2) or top-left quadrant, where x is negative and y is positive.
+ ///
+ /// 2
+ Second = 2,
+
+ ///
+ /// The third quadrant (Q3) or bottom-left quadrant, where both x and y coordinates are negative.
+ ///
+ /// 3
+ Third = 3,
+
+ ///
+ /// The fourth quadrant (Q4) or bottom-right quadrant, where x is positive and y is negative.
+ ///
+ /// 4
+ Fourth = 4
+
+ }
+
+}
\ No newline at end of file
diff --git a/CatalystUI/Core/CatalystUI.Mathematics/Range.cs b/CatalystUI/Core/CatalystUI.Mathematics/Range.cs
new file mode 100644
index 0000000..46a60db
--- /dev/null
+++ b/CatalystUI/Core/CatalystUI.Mathematics/Range.cs
@@ -0,0 +1,154 @@
+// -------------------------------------------------------------------------------------------------
+// CatalystUI Framework for .NET Core - https://catalystui.org/
+// Copyright (c) 2025 CatalystUI LLC. All rights reserved.
+//
+// This file is part of CatalystUI and is provided as part of an early-access release.
+// Unauthorized commercial use, distribution, or modification is strictly prohibited.
+//
+// This software is not open source and is not publicly licensed.
+// For full terms, see the LICENSE and NOTICE files in the project root.
+// -------------------------------------------------------------------------------------------------
+
+using System;
+using System.Diagnostics.CodeAnalysis;
+using System.Numerics;
+using System.Text;
+
+namespace Catalyst.Mathematics {
+
+ ///
+ /// Represents a range of numeric values.
+ ///
+ /// The numeric type of the range values.
+ public readonly record struct Range where TNumber : struct, INumber {
+
+ ///
+ /// The zero range, where both minimum and maximum are zero and inclusive.
+ ///
+ public static readonly Range ZERO = new(RangeValue.ZERO_INCLUSIVE, RangeValue.ZERO_INCLUSIVE);
+
+ // Backing Fields
+ private readonly RangeValue _minimum;
+ private readonly RangeValue _maximum;
+
+ ///
+ /// Gets the minimum value of the range.
+ ///
+ /// The range's minimum value.
+ public required RangeValue Minimum {
+ get => _minimum;
+ init {
+ _minimum = value;
+ if (_maximum != default) Validate();
+ }
+ }
+
+ ///
+ /// Gets the maximum value of the range.
+ ///
+ /// The range's maximum value.
+ public required RangeValue Maximum {
+ get => _maximum;
+ init {
+ _maximum = value;
+ if (_minimum != default) Validate();
+ }
+ }
+
+ ///
+ /// Constructs a new
+ /// with the specified minimum and maximum values.
+ ///
+ /// The minimum value of the range.
+ /// The maximum value of the range.
+ [SetsRequiredMembers]
+ public Range(RangeValue minimum, RangeValue maximum) {
+ Minimum = minimum;
+ Maximum = maximum;
+ Validate();
+ }
+
+ ///
+ /// Determines if the specified number is within the range.
+ ///
+ /// The number to check.
+ /// if the number is within the range; otherwise, .
+ public bool Within(TNumber number) {
+ bool aboveMinimum = _minimum.Exclusive ? number > _minimum.Value : number >= _minimum.Value;
+ bool belowMaximum = _maximum.Exclusive ? number < _maximum.Value : number <= _maximum.Value;
+ return aboveMinimum && belowMaximum;
+ }
+
+ ///
+ /// Validates the range values.
+ ///
+ /// Thrown if the maximum value is less than the minimum value.
+ private void Validate() {
+ if (_minimum.Value > _maximum.Value) throw new ArgumentOutOfRangeException(nameof(_maximum), "The maximum value must be greater than or equal to the minimum value.");
+ if (_minimum.Value == _maximum.Value && (_minimum.Exclusive || _maximum.Exclusive)) throw new ArgumentOutOfRangeException(nameof(_maximum), "The maximum value must be greater than the minimum value when either bound is exclusive.");
+ }
+
+ ///
+ public override string ToString() {
+ StringBuilder sb = new();
+ sb.Append(Minimum.Exclusive ? '(' : '[');
+ sb.Append(Minimum.Value);
+ sb.Append(',').Append(' ');
+ sb.Append(Maximum.Value);
+ sb.Append(Maximum.Exclusive ? ')' : ']');
+ return sb.ToString();
+ }
+
+ }
+
+ ///
+ /// Represents a number value within a specified range.
+ ///
+ /// The numeric type of the range value.
+ public readonly record struct RangeValue where TNumber : struct, INumber {
+
+ ///
+ /// A zero value which is inclusive within the range.
+ ///
+ public static readonly RangeValue ZERO_INCLUSIVE = new(TNumber.Zero, false);
+
+ ///
+ /// A zero value which is exclusive outside the range.
+ ///
+ public static readonly RangeValue ZERO_EXCLUSIVE = new(TNumber.Zero, true);
+
+ ///
+ /// Gets the underlying value.
+ ///
+ /// The underlying value.
+ public required TNumber Value { get; init; }
+
+ ///
+ /// Gets a flag indicating whether the value is exclusive outside the range.
+ ///
+ /// if the value is exclusive; otherwise, .
+ public required bool Exclusive { get; init; }
+
+ ///
+ /// Constructs a new .
+ ///
+ /// The underlying value.
+ /// A flag indicating whether the value is exclusive outside the range.
+ [SetsRequiredMembers]
+ public RangeValue(TNumber value, bool exclusive) {
+ Value = value;
+ Exclusive = exclusive;
+ }
+
+ ///
+ /// Implicitly converts a range value to its underlying numeric type.
+ ///
+ /// The range value to convert.
+ /// The underlying numeric value.
+ public static implicit operator TNumber(RangeValue rv) {
+ return rv.Value;
+ }
+
+ }
+
+}
\ No newline at end of file
diff --git a/CatalystUI/Core/CatalystUI.Mathematics/Vector2.cs b/CatalystUI/Core/CatalystUI.Mathematics/Vector2.cs
new file mode 100644
index 0000000..429fbb4
--- /dev/null
+++ b/CatalystUI/Core/CatalystUI.Mathematics/Vector2.cs
@@ -0,0 +1,304 @@
+// -------------------------------------------------------------------------------------------------
+// CatalystUI Framework for .NET Core - https://catalystui.org/
+// Copyright (c) 2025 CatalystUI LLC. All rights reserved.
+//
+// This file is part of CatalystUI and is provided as part of an early-access release.
+// Unauthorized commercial use, distribution, or modification is strictly prohibited.
+//
+// This software is not open source and is not publicly licensed.
+// For full terms, see the LICENSE and NOTICE files in the project root.
+// -------------------------------------------------------------------------------------------------
+
+using System;
+using System.Diagnostics.CodeAnalysis;
+using System.Numerics;
+using System.Runtime.InteropServices;
+
+namespace Catalyst.Mathematics {
+
+ ///
+ /// A vector containing two numeric values.
+ ///
+ /// The numeric type of the vector values.
+ [StructLayout(LayoutKind.Sequential)]
+ public readonly record struct Vector2 where TNumber : struct, INumber {
+
+ ///
+ /// The common zero vector (0, 0).
+ ///
+ public static readonly Vector2 ZERO = new(TNumber.Zero);
+
+ ///
+ /// The common unit vector (1, 1).
+ ///
+ public static readonly Vector2 UNIT = new(TNumber.One);
+
+ ///
+ /// Gets the X value of the vector.
+ ///
+ /// The vector's X value.
+ public required TNumber X { get; init; }
+
+ ///
+ /// Gets the Y value of the vector.
+ ///
+ /// The vector's Y value.
+ public required TNumber Y { get; init; }
+
+ ///
+ /// Gets the R value of the vector (alias for X).
+ ///
+ /// The vector's R value.
+ public TNumber R => X;
+
+ ///
+ /// Gets the G value of the vector (alias for Y).
+ ///
+ /// The vector's G value.
+ public TNumber G => Y;
+
+ ///
+ /// Gets the S value of the vector (alias for X).
+ ///
+ /// The vector's S value.
+ public TNumber S => X;
+
+ ///
+ /// Gets the T value of the vector (alias for Y).
+ ///
+ /// The vector's T value.
+ public TNumber T => Y;
+
+ ///
+ /// Constructs a new
+ /// using the specified X and Y values.
+ ///
+ /// The X value of the vector.
+ /// The Y value of the vector.
+ [SetsRequiredMembers]
+ public Vector2(TNumber x, TNumber y) {
+ X = x;
+ Y = y;
+ }
+
+ ///
+ /// Constructs a new
+ /// using the specified value for both X and Y.
+ ///
+ /// The value for both X and Y of the vector.
+ [SetsRequiredMembers]
+ public Vector2(TNumber xy) : this(xy, xy) {
+ // ...
+ }
+
+ ///
+ /// Normalizes the vector by preserving direction
+ /// and setting its length to 1.
+ ///
+ ///
+ /// When calculating a normalized vector,
+ /// the value is first converted to a
+ /// double-precision floating point number,
+ /// which allows the necessary mathematical
+ /// operations to be performed. It is then
+ /// converted back to the original numeric
+ /// type.
+ ///
+ /// A new vector with the same direction and a length of 1.
+ public Vector2 Normalize() {
+ TNumber lengthSquared = X * X + Y * Y;
+ if (lengthSquared == TNumber.Zero) return ZERO;
+ double lengthInverted = 1.0 / Math.Sqrt(double.CreateChecked(lengthSquared));
+ TNumber lengthConverted = TNumber.CreateChecked(lengthInverted);
+ return new(X * lengthConverted, Y * lengthConverted);
+ }
+
+ ///
+ /// Converts a from
+ /// into a from .
+ ///
+ /// The newly created .
+ public Vector2 ToVector2() {
+ return new(
+ float.CreateChecked(X),
+ float.CreateChecked(Y)
+ );
+ }
+
+ ///
+ /// Converts a from
+ /// into a from .
+ ///
+ /// The to convert.
+ /// The newly created .
+ public static Vector2 FromVector2(Vector2 vector2) {
+ return new(
+ TNumber.CreateChecked(vector2.X),
+ TNumber.CreateChecked(vector2.Y)
+ );
+ }
+
+ ///
+ /// Converts the vector to a different numeric type.
+ ///
+ /// The vector to convert.
+ /// The numeric type to convert to.
+ /// The newly created vector with the specified numeric type.
+ public static Vector2 ConvertTo(Vector2 vector) where TToNumber : struct, INumber {
+ return new(
+ TToNumber.CreateChecked(vector.X),
+ TToNumber.CreateChecked(vector.Y)
+ );
+ }
+
+ ///
+ /// Linearly interpolates between two vectors.
+ ///
+ /// The starting vector.
+ /// The ending vector.
+ /// The interpolation position, typically between 0 and 1.
+ /// The interpolated vector.
+ public static Vector2 Lerp(Vector2 v1, Vector2 v2, TNumber position) {
+ return new(
+ v1.X + (v2.X - v1.X) * position,
+ v1.Y + (v2.Y - v1.Y) * position
+ );
+ }
+
+ ///
+ /// Calculates the dot product of two vectors.
+ ///
+ /// The first vector.
+ /// The second vector.
+ /// The dot product of the two vectors.
+ public static TNumber Dot(Vector2 v1, Vector2 v2) {
+ return v1.X * v2.X + v1.Y * v2.Y;
+ }
+
+ ///
+ /// Calculates the distance between two vectors.
+ ///
+ /// The first vector.
+ /// The second vector.
+ /// The distance between the two vectors.
+ public static TNumber Distance(Vector2 v1, Vector2 v2) {
+ TNumber deltaX = v2.X - v1.X;
+ TNumber deltaY = v2.Y - v1.Y;
+ double distance = Math.Sqrt(double.CreateChecked(deltaX * deltaX + deltaY * deltaY));
+ return TNumber.CreateChecked(distance);
+ }
+
+ ///
+ /// Compares two vectors to determine if the left vector is less than the right vector.
+ ///
+ /// The left vector.
+ /// The right vector.
+ /// if the left vector is less than the right vector; otherwise, .
+ public static bool operator <(Vector2 left, Vector2 right) => left.X < right.X && left.Y < right.Y;
+
+ ///
+ /// Compares two vectors to determine if the left vector is less than or equal to the right vector.
+ ///
+ /// The left vector.
+ /// The right vector.
+ /// if the left vector is less than or equal to the right vector; otherwise, .
+ public static bool operator <=(Vector2 left, Vector2 right) => left.X <= right.X && left.Y <= right.Y;
+
+ ///
+ /// Compares two vectors to determine if the left vector is greater than the right vector.
+ ///
+ /// The left vector.
+ /// The right vector.
+ /// if the left vector is greater than the right vector; otherwise, .
+ public static bool operator >(Vector2 left, Vector2 right) => left.X > right.X && left.Y > right.Y;
+
+ ///
+ /// Compares two vectors to determine if the left vector is greater than or equal to the right vector.
+ ///
+ /// The left vector.
+ /// The right vector.
+ /// if the left vector is greater than or equal to the right vector; otherwise, .
+ public static bool operator >=(Vector2 left, Vector2 right) => left.X >= right.X && left.Y >= right.Y;
+
+ ///
+ /// Unary plus operator.
+ ///
+ /// The vector to return.
+ /// The vector unchanged.
+ public static Vector2 operator +(Vector2 vector) => vector;
+
+ ///
+ /// Unary negation operator.
+ ///
+ /// The vector to negate.
+ /// The vector with both X and Y negated.
+ public static Vector2 operator -(Vector2 vector) => new(-vector.X, -vector.Y);
+
+ ///
+ /// Finds the sum of two vectors.
+ ///
+ /// The left vector.
+ /// The right vector.
+ /// The sum of the two vectors.
+ public static Vector2 operator +(Vector2 left, Vector2 right) => new(left.X + right.X, left.Y + right.Y);
+
+ ///
+ /// Finds the difference between two vectors.
+ ///
+ /// The left vector.
+ /// The right vector.
+ /// The difference of the two vectors.
+ public static Vector2 operator -(Vector2 left, Vector2 right) => new(left.X - right.X, left.Y - right.Y);
+
+ ///
+ /// Increments the vector by one.
+ ///
+ /// The vector to increment.
+ /// The vector with both X and Y incremented by one.
+ public static Vector2 operator ++(Vector2 vector) => new(vector.X + TNumber.One, vector.Y + TNumber.One);
+
+ ///
+ /// Decrements the vector by one.
+ ///
+ /// The vector to decrement.
+ /// The vector with both X and Y decremented by one.
+ public static Vector2 operator --(Vector2 vector) => new(vector.X - TNumber.One, vector.Y - TNumber.One);
+
+ ///
+ /// Multiplies the vector by a scalar value.
+ ///
+ /// The vector to multiply.
+ /// The scalar value to multiply by.
+ /// The vector scaled by the scalar value.
+ public static Vector2 operator *(Vector2 vector, TNumber scalar) => new(vector.X * scalar, vector.Y * scalar);
+
+ ///
+ public static Vector2 operator *(TNumber scalar, Vector2 vector) => new(vector.X * scalar, vector.Y * scalar);
+
+ ///
+ /// Multiplies two vectors together.
+ ///
+ /// The left vector.
+ /// The right vector.
+ /// The vector with each value multiplied together.
+ public static Vector2 operator *(Vector2 left, Vector2 right) => new(left.X * right.X, left.Y * right.Y);
+
+ ///
+ /// Divides the vector by a scalar value.
+ ///
+ /// The vector to divide.
+ /// The scalar value to divide by.
+ /// The vector divided by the scalar value.
+ public static Vector2 operator /(Vector2 vector, TNumber scalar) => new(vector.X / scalar, vector.Y / scalar);
+
+ ///
+ /// Finds the quotient of two vectors.
+ ///
+ /// The left vector.
+ /// The right vector.
+ /// The vector with each value divided.
+ public static Vector2 operator /(Vector2 left, Vector2 right) => new(left.X / right.X, left.Y / right.Y);
+
+ }
+
+}
\ No newline at end of file
diff --git a/CatalystUI/Core/CatalystUI.Mathematics/Vector3.cs b/CatalystUI/Core/CatalystUI.Mathematics/Vector3.cs
new file mode 100644
index 0000000..147132b
--- /dev/null
+++ b/CatalystUI/Core/CatalystUI.Mathematics/Vector3.cs
@@ -0,0 +1,291 @@
+// -------------------------------------------------------------------------------------------------
+// CatalystUI Framework for .NET Core - https://catalystui.org/
+// Copyright (c) 2025 CatalystUI LLC. All rights reserved.
+//
+// This file is part of CatalystUI and is provided as part of an early-access release.
+// Unauthorized commercial use, distribution, or modification is strictly prohibited.
+//
+// This software is not open source and is not publicly licensed.
+// For full terms, see the LICENSE and NOTICE files in the project root.
+// -------------------------------------------------------------------------------------------------
+
+using System;
+using System.Diagnostics.CodeAnalysis;
+using System.Numerics;
+using System.Runtime.InteropServices;
+
+namespace Catalyst.Mathematics {
+
+ ///
+ /// A vector containing three numeric values.
+ ///
+ /// The numeric type of the vector values.
+ [StructLayout(LayoutKind.Sequential)]
+ public readonly record struct Vector3 where TNumber : struct, INumber {
+
+ ///
+ /// The common zero vector (0, 0, 0).
+ ///
+ public static readonly Vector3 ZERO = new(TNumber.Zero);
+
+ ///
+ /// The common unit vector (1, 1, 1).
+ ///
+ public static readonly Vector3 UNIT = new(TNumber.One);
+
+ ///
+ /// Gets the X value of the vector.
+ ///
+ /// The vector's X value.
+ public required TNumber X { get; init; }
+
+ ///
+ /// Gets the Y value of the vector.
+ ///
+ /// The vector's Y value.
+ public required TNumber Y { get; init; }
+
+ ///
+ /// Gets the Z value of the vector.
+ ///
+ /// The vector's Z value.
+ public required TNumber Z { get; init; }
+
+ ///
+ /// Gets the R value of the vector (alias for X).
+ ///
+ /// The vector's R value.
+ public TNumber R => X;
+
+ ///
+ /// Gets the G value of the vector (alias for Y).
+ ///
+ /// The vector's G value.
+ public TNumber G => Y;
+
+ ///
+ /// Gets the B value of the vector (alias for Z).
+ ///
+ /// The vector's B value.
+ public TNumber B => Z;
+
+ ///
+ /// Gets the S value of the vector (alias for X).
+ ///
+ /// The vector's S value.
+ public TNumber S => X;
+
+ ///
+ /// Gets the T value of the vector (alias for Y).
+ ///
+ /// The vector's T value.
+ public TNumber T => Y;
+
+ ///
+ /// Gets the P value of the vector (alias for Z).
+ ///
+ /// The vector's P value.
+ public TNumber P => Z;
+
+ ///
+ /// Constructs a new
+ /// using the specified X, Y, and Z values.
+ ///
+ /// The X value of the vector.
+ /// The Y value of the vector.
+ /// The Z value of the vector.
+ [SetsRequiredMembers]
+ public Vector3(TNumber x, TNumber y, TNumber z) {
+ X = x;
+ Y = y;
+ Z = z;
+ }
+
+ ///
+ /// Constructs a new
+ /// using the specified value for X, Y, and Z.
+ ///
+ /// The value for all components of the vector.
+ [SetsRequiredMembers]
+ public Vector3(TNumber xyz) : this(xyz, xyz, xyz) {
+ // ...
+ }
+
+ ///
+ /// Normalizes the vector by preserving direction
+ /// and setting its length to 1.
+ ///
+ ///
+ /// When calculating a normalized vector,
+ /// the value is first converted to a
+ /// double-precision floating point number,
+ /// which allows the necessary mathematical
+ /// operations to be performed. It is then
+ /// converted back to the original numeric
+ /// type.
+ ///
+ /// A new vector with the same direction and a length of 1.
+ public Vector3 Normalize() {
+ TNumber lengthSquared = X * X + Y * Y + Z * Z;
+ if (lengthSquared == TNumber.Zero) return ZERO;
+ double lengthInverted = 1.0 / Math.Sqrt(double.CreateChecked(lengthSquared));
+ TNumber lengthConverted = TNumber.CreateChecked(lengthInverted);
+ return new(X * lengthConverted, Y * lengthConverted, Z * lengthConverted);
+ }
+
+ ///
+ /// Converts a from
+ /// into a from .
+ ///
+ /// The newly created .
+ public Vector3 ToVector3() {
+ return new(
+ float.CreateChecked(X),
+ float.CreateChecked(Y),
+ float.CreateChecked(Z)
+ );
+ }
+
+ ///
+ /// Converts a from
+ /// into a from .
+ ///
+ /// The to convert.
+ /// The newly created .
+ public static Vector3 FromVector3(Vector3 vector3) {
+ return new(
+ TNumber.CreateChecked(vector3.X),
+ TNumber.CreateChecked(vector3.Y),
+ TNumber.CreateChecked(vector3.Z)
+ );
+ }
+
+ ///
+ /// Converts the vector to a different numeric type.
+ ///
+ /// The vector to convert.
+ /// The numeric type to convert to.
+ /// The newly created vector with the specified numeric type.
+ public static Vector3 ConvertTo(Vector3 vector) where TToNumber : struct, INumber {
+ return new(
+ TToNumber.CreateChecked(vector.X),
+ TToNumber.CreateChecked(vector.Y),
+ TToNumber.CreateChecked(vector.Z)
+ );
+ }
+
+ ///
+ /// Linearly interpolates between two vectors.
+ ///
+ /// The starting vector.
+ /// The ending vector.
+ /// The interpolation position, typically between 0 and 1.
+ /// The interpolated vector.
+ public static Vector3 Lerp(Vector3 v1, Vector3 v2, TNumber position) {
+ return new(
+ v1.X + (v2.X - v1.X) * position,
+ v1.Y + (v2.Y - v1.Y) * position,
+ v1.Z + (v2.Z - v1.Z) * position
+ );
+ }
+
+ ///
+ /// Calculates the dot product of two vectors.
+ ///
+ /// The first vector.
+ /// The second vector.
+ /// The dot product of the two vectors.
+ public static TNumber Dot(Vector3 v1, Vector3 v2) {
+ return v1.X * v2.X + v1.Y * v2.Y + v1.Z * v2.Z;
+ }
+
+ ///
+ /// Calculates the distance between two vectors.
+ ///
+ /// The first vector.
+ /// The second vector.
+ /// The distance between the two vectors.
+ public static TNumber Distance(Vector3 v1, Vector3 v2) {
+ TNumber deltaX = v2.X - v1.X;
+ TNumber deltaY = v2.Y - v1.Y;
+ TNumber deltaZ = v2.Z - v1.Z;
+ double distance = Math.Sqrt(double.CreateChecked(deltaX * deltaX + deltaY * deltaY + deltaZ * deltaZ));
+ return TNumber.CreateChecked(distance);
+ }
+
+ ///
+ /// Compares two vectors to determine if the left vector is less than the right vector.
+ ///
+ public static bool operator <(Vector3 left, Vector3 right) => left.X < right.X && left.Y < right.Y && left.Z < right.Z;
+
+ ///
+ /// Compares two vectors to determine if the left vector is less than or equal to the right vector.
+ ///
+ public static bool operator <=(Vector3 left, Vector3 right) => left.X <= right.X && left.Y <= right.Y && left.Z <= right.Z;
+
+ ///
+ /// Compares two vectors to determine if the left vector is greater than the right vector.
+ ///
+ public static bool operator >(Vector3 left, Vector3 right) => left.X > right.X && left.Y > right.Y && left.Z > right.Z;
+
+ ///
+ /// Compares two vectors to determine if the left vector is greater than or equal to the right vector.
+ ///
+ public static bool operator >=(Vector3 left, Vector3 right) => left.X >= right.X && left.Y >= right.Y && left.Z >= right.Z;
+
+ ///
+ /// Unary plus operator.
+ ///
+ public static Vector3 operator +(Vector3 vector) => vector;
+
+ ///
+ /// Unary negation operator.
+ ///
+ public static Vector3 operator -(Vector3 vector) => new(-vector.X, -vector.Y, -vector.Z);
+
+ ///
+ /// Finds the sum of two vectors.
+ ///
+ public static Vector3 operator +(Vector3 left, Vector3 right) => new(left.X + right.X, left.Y + right.Y, left.Z + right.Z);
+
+ ///
+ /// Finds the difference between two vectors.
+ ///
+ public static Vector3 operator -(Vector3 left, Vector3 right) => new(left.X - right.X, left.Y - right.Y, left.Z - right.Z);
+
+ ///
+ /// Increments the vector by one.
+ ///
+ public static Vector3 operator ++(Vector3 vector) => new(vector.X + TNumber.One, vector.Y + TNumber.One, vector.Z + TNumber.One);
+
+ ///
+ /// Decrements the vector by one.
+ ///
+ public static Vector3 operator --(Vector3 vector) => new(vector.X - TNumber.One, vector.Y - TNumber.One, vector.Z - TNumber.One);
+
+ ///
+ /// Multiplies the vector by a scalar value.
+ ///
+ public static Vector3 operator *(Vector3 vector, TNumber scalar) => new(vector.X * scalar, vector.Y * scalar, vector.Z * scalar);
+
+ ///
+ public static Vector3 operator *(TNumber scalar, Vector3 vector) => new(vector.X * scalar, vector.Y * scalar, vector.Z * scalar);
+
+ ///
+ /// Multiplies two vectors together.
+ ///
+ public static Vector3 operator *(Vector3 left, Vector3 right) => new(left.X * right.X, left.Y * right.Y, left.Z * right.Z);
+
+ ///
+ /// Divides the vector by a scalar value.
+ ///
+ public static Vector3 operator /(Vector3 vector, TNumber scalar) => new(vector.X / scalar, vector.Y / scalar, vector.Z / scalar);
+
+ ///
+ /// Finds the quotient of two vectors.
+ ///
+ public static Vector3 operator /(Vector3 left, Vector3 right) => new(left.X / right.X, left.Y / right.Y, left.Z / right.Z);
+
+ }
+
+}
\ No newline at end of file
diff --git a/CatalystUI/Core/CatalystUI.Mathematics/Vector4.cs b/CatalystUI/Core/CatalystUI.Mathematics/Vector4.cs
new file mode 100644
index 0000000..002c16e
--- /dev/null
+++ b/CatalystUI/Core/CatalystUI.Mathematics/Vector4.cs
@@ -0,0 +1,272 @@
+// -------------------------------------------------------------------------------------------------
+// CatalystUI Framework for .NET Core - https://catalystui.org/
+// Copyright (c) 2025 CatalystUI LLC. All rights reserved.
+//
+// This file is part of CatalystUI and is provided as part of an early-access release.
+// Unauthorized commercial use, distribution, or modification is strictly prohibited.
+//
+// This software is not open source and is not publicly licensed.
+// For full terms, see the LICENSE and NOTICE files in the project root.
+// -------------------------------------------------------------------------------------------------
+
+using System;
+using System.Diagnostics.CodeAnalysis;
+using System.Numerics;
+using System.Runtime.InteropServices;
+
+namespace Catalyst.Mathematics {
+
+ ///
+ /// A vector containing four numeric values.
+ ///
+ /// The numeric type of the vector values.
+ [StructLayout(LayoutKind.Sequential)]
+ public readonly record struct Vector4 where TNumber : struct, INumber {
+
+ ///
+ /// The common zero vector (0, 0, 0, 0).
+ ///
+ public static readonly Vector4 ZERO = new(TNumber.Zero);
+
+ ///
+ /// The common unit vector (1, 1, 1, 1).
+ ///
+ public static readonly Vector4 UNIT = new(TNumber.One);
+
+ ///
+ /// Gets the X value of the vector.
+ ///
+ /// The X component of the vector.
+ public required TNumber X { get; init; }
+
+ ///
+ /// Gets the Y value of the vector.
+ ///
+ /// The Y component of the vector.
+ public required TNumber Y { get; init; }
+
+ ///
+ /// Gets the Z value of the vector.
+ ///
+ /// The Z component of the vector.
+ public required TNumber Z { get; init; }
+
+ ///
+ /// Gets the W value of the vector.
+ ///
+ /// The W component of the vector.
+ public required TNumber W { get; init; }
+
+ ///
+ /// Gets the R value of the vector (alias for X).
+ ///
+ public TNumber R => X;
+
+ ///
+ /// Gets the G value of the vector (alias for Y).
+ ///
+ public TNumber G => Y;
+
+ ///
+ /// Gets the B value of the vector (alias for Z).
+ ///
+ public TNumber B => Z;
+
+ ///
+ /// Gets the A value of the vector (alias for W).
+ ///
+ public TNumber A => W;
+
+ ///
+ /// Gets the S value of the vector (alias for X).
+ ///
+ public TNumber S => X;
+
+ ///
+ /// Gets the T value of the vector (alias for Y).
+ ///
+ public TNumber T => Y;
+
+ ///
+ /// Gets the P value of the vector (alias for Z).
+ ///
+ public TNumber P => Z;
+
+ ///
+ /// Gets the Q value of the vector (alias for W).
+ ///
+ public TNumber Q => W;
+
+ ///
+ /// Constructs a new using the specified X, Y, Z, and W values.
+ ///
+ [SetsRequiredMembers]
+ public Vector4(TNumber x, TNumber y, TNumber z, TNumber w) {
+ X = x;
+ Y = y;
+ Z = z;
+ W = w;
+ }
+
+ ///
+ /// Constructs a new using the specified value for all components.
+ ///
+ [SetsRequiredMembers]
+ public Vector4(TNumber xyzw) : this(xyzw, xyzw, xyzw, xyzw) {
+ // ...
+ }
+
+ ///
+ /// Normalizes the vector by preserving direction and setting its length to 1.
+ ///
+ public Vector4 Normalize() {
+ TNumber lengthSquared = X * X + Y * Y + Z * Z + W * W;
+ if (lengthSquared == TNumber.Zero) return ZERO;
+ double lengthInverted = 1.0 / Math.Sqrt(double.CreateChecked(lengthSquared));
+ TNumber lengthConverted = TNumber.CreateChecked(lengthInverted);
+ return new(X * lengthConverted, Y * lengthConverted, Z * lengthConverted, W * lengthConverted);
+ }
+
+ ///
+ /// Converts a from into a from .
+ ///
+ public Vector4 ToVector4() {
+ return new(
+ float.CreateChecked(X),
+ float.CreateChecked(Y),
+ float.CreateChecked(Z),
+ float.CreateChecked(W)
+ );
+ }
+
+ ///
+ /// Converts a from into a from .
+ ///
+ public static Vector4 FromVector4(Vector4 vector4) {
+ return new(
+ TNumber.CreateChecked(vector4.X),
+ TNumber.CreateChecked(vector4.Y),
+ TNumber.CreateChecked(vector4.Z),
+ TNumber.CreateChecked(vector4.W)
+ );
+ }
+
+ ///
+ /// Converts the vector to a different numeric type.
+ ///
+ public static Vector4 ConvertTo(Vector4 vector) where TToNumber : struct, INumber {
+ return new(
+ TToNumber.CreateChecked(vector.X),
+ TToNumber.CreateChecked(vector.Y),
+ TToNumber.CreateChecked(vector.Z),
+ TToNumber.CreateChecked(vector.W)
+ );
+ }
+
+ ///
+ /// Linearly interpolates between two vectors.
+ ///
+ public static Vector4 Lerp(Vector4 v1, Vector4 v2, TNumber position) {
+ return new(
+ v1.X + (v2.X - v1.X) * position,
+ v1.Y + (v2.Y - v1.Y) * position,
+ v1.Z + (v2.Z - v1.Z) * position,
+ v1.W + (v2.W - v1.W) * position
+ );
+ }
+
+ ///
+ /// Calculates the dot product of two vectors.
+ ///
+ public static TNumber Dot(Vector4 v1, Vector4 v2) {
+ return v1.X * v2.X + v1.Y * v2.Y + v1.Z * v2.Z + v1.W * v2.W;
+ }
+
+ ///
+ /// Calculates the distance between two vectors.
+ ///
+ public static TNumber Distance(Vector4 v1, Vector4 v2) {
+ TNumber dx = v2.X - v1.X;
+ TNumber dy = v2.Y - v1.Y;
+ TNumber dz = v2.Z - v1.Z;
+ TNumber dw = v2.W - v1.W;
+ double distance = Math.Sqrt(double.CreateChecked(dx * dx + dy * dy + dz * dz + dw * dw));
+ return TNumber.CreateChecked(distance);
+ }
+
+ ///
+ /// Compares two vectors to determine if the left vector is less than the right vector.
+ ///
+ public static bool operator <(Vector4 left, Vector4 right) => left.X < right.X && left.Y < right.Y && left.Z < right.Z && left.W < right.W;
+
+ ///
+ /// Compares two vectors to determine if the left vector is less than or equal to the right vector.
+ ///
+ public static bool operator <=(Vector4 left, Vector4 right) => left.X <= right.X && left.Y <= right.Y && left.Z <= right.Z && left.W <= right.W;
+
+ ///
+ /// Compares two vectors to determine if the left vector is greater than the right vector.
+ ///
+ public static bool operator >(Vector4 left, Vector4 right) => left.X > right.X && left.Y > right.Y && left.Z > right.Z && left.W > right.W;
+
+ ///
+ /// Compares two vectors to determine if the left vector is greater than or equal to the right vector.
+ ///
+ public static bool operator >=(Vector4 left, Vector4 right) => left.X >= right.X && left.Y >= right.Y && left.Z >= right.Z && left.W >= right.W;
+
+ ///
+ /// Unary plus operator.
+ ///
+ public static Vector4 operator +(Vector4 vector) => vector;
+
+ ///
+ /// Unary negation operator.
+ ///
+ public static Vector4 operator -(Vector4 vector) => new(-vector.X, -vector.Y, -vector.Z, -vector.W);
+
+ ///
+ /// Finds the sum of two vectors.
+ ///
+ public static Vector4 operator +(Vector4 left, Vector4 right) => new(left.X + right.X, left.Y + right.Y, left.Z + right.Z, left.W + right.W);
+
+ ///
+ /// Finds the difference between two vectors.
+ ///
+ public static Vector4 operator -(Vector4 left, Vector4 right) => new(left.X - right.X, left.Y - right.Y, left.Z - right.Z, left.W - right.W);
+
+ ///
+ /// Increments the vector by one.
+ ///
+ public static Vector4 operator ++(Vector4 vector) => new(vector.X + TNumber.One, vector.Y + TNumber.One, vector.Z + TNumber.One, vector.W + TNumber.One);
+
+ ///
+ /// Decrements the vector by one.
+ ///
+ public static Vector4 operator --(Vector4 vector) => new(vector.X - TNumber.One, vector.Y - TNumber.One, vector.Z - TNumber.One, vector.W - TNumber.One);
+
+ ///
+ /// Multiplies the vector by a scalar value.
+ ///
+ public static Vector4 operator *(Vector4 vector, TNumber scalar) => new(vector.X * scalar, vector.Y * scalar, vector.Z * scalar, vector.W * scalar);
+
+ ///
+ public static Vector4 operator *(TNumber scalar, Vector4 vector) => new(vector.X * scalar, vector.Y * scalar, vector.Z * scalar, vector.W * scalar);
+
+ ///
+ /// Multiplies two vectors together.
+ ///
+ public static Vector4 operator *(Vector4 left, Vector4 right) => new(left.X * right.X, left.Y * right.Y, left.Z * right.Z, left.W * right.W);
+
+ ///
+ /// Divides the vector by a scalar value.
+ ///
+ public static Vector4 operator /(Vector4 vector, TNumber scalar) => new(vector.X / scalar, vector.Y / scalar, vector.Z / scalar, vector.W / scalar);
+
+ ///
+ /// Finds the quotient of two vectors.
+ ///
+ public static Vector4 operator /(Vector4 left, Vector4 right) => new(left.X / right.X, left.Y / right.Y, left.Z / right.Z, left.W / right.W);
+
+ }
+
+}
\ No newline at end of file
diff --git a/CatalystUI/Core/CatalystUI.Supplementary/CatalystUI.Supplementary.csproj b/CatalystUI/Core/CatalystUI.Supplementary/CatalystUI.Supplementary.csproj
new file mode 100644
index 0000000..061d8fa
--- /dev/null
+++ b/CatalystUI/Core/CatalystUI.Supplementary/CatalystUI.Supplementary.csproj
@@ -0,0 +1,23 @@
+
+
+
+
+
+ Catalyst.Supplementary
+ Catalyst.Supplementary
+
+
+ CatalystUI Supplementary
+ 1.0.0
+ beta.2
+ FireController#1847
+ Supplementary API provided by the CatalystUI library.
+ CatalystUI,supplementary,addons,additional,extra
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CatalystUI/Core/CatalystUI.Supplementary/Connectors/DataConnector/BindingContract.cs b/CatalystUI/Core/CatalystUI.Supplementary/Connectors/DataConnector/BindingContract.cs
new file mode 100644
index 0000000..00672da
--- /dev/null
+++ b/CatalystUI/Core/CatalystUI.Supplementary/Connectors/DataConnector/BindingContract.cs
@@ -0,0 +1,107 @@
+// -------------------------------------------------------------------------------------------------
+// CatalystUI Framework for .NET Core - https://catalystui.org/
+// Copyright (c) 2025 CatalystUI LLC. All rights reserved.
+//
+// This file is part of CatalystUI and is provided as part of an early-access release.
+// Unauthorized commercial use, distribution, or modification is strictly prohibited.
+//
+// This software is not open source and is not publicly licensed.
+// For full terms, see the LICENSE and NOTICE files in the project root.
+// -------------------------------------------------------------------------------------------------
+
+
+
+using System.Threading;
+// ReSharper disable once CheckNamespace
+using System;
+
+namespace Catalyst.Supplementary {
+
+ ///
+ /// Represents a contract for binding data to a specific instance or context.
+ ///
+ ///
+ /// Should be disposed of when no longer needed to allow proper resource cleanup.
+ ///
+ // ReSharper disable once ClassNeverInstantiated.Global
+ public sealed class BindingContract : IDisposable {
+
+ ///
+ /// Fired when managed resources should be disposed of.
+ ///
+ public event Action? DisposeManaged;
+
+ ///
+ /// Fired when unmanaged resources should be disposed of.
+ ///
+ public event Action? DisposeUnmanaged;
+
+ ///
+ /// The unique identifier for the instance to which the data is bound.
+ ///
+ public Guid InstanceId { get; }
+
+ ///
+ /// A flag indicating whether the object has been disposed of.
+ ///
+ private bool _disposed;
+
+ ///
+ /// A lock used to ensure thread-safe access to the object.
+ ///
+ private readonly Lock _lock;
+
+ ///
+ /// Constructs a new .
+ ///
+ public BindingContract() {
+ // Fields
+ _disposed = false;
+ _lock = new();
+
+ // Properties
+ InstanceId = Guid.NewGuid();
+ }
+
+ ///
+ /// Disposes of the .
+ ///
+ ~BindingContract() {
+ // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
+ Dispose(disposing: false);
+ }
+
+ ///
+ /// Disposes of the .
+ ///
+ public void Dispose() {
+ // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
+ Dispose(disposing: true);
+ GC.SuppressFinalize(this);
+ }
+
+ /// if disposal is being performed by the garbage collector, otherwise
+ ///
+ private void Dispose(bool disposing) {
+ _lock.Enter();
+ try {
+ if (_disposed) return;
+
+ // Dispose managed state (managed objects)
+ if (disposing) {
+ DisposeManaged?.Invoke(this);
+ }
+
+ // Dispose unmanaged state (unmanaged objects)
+ DisposeUnmanaged?.Invoke(this);
+
+ // Indicate disposal completion
+ _disposed = true;
+ } finally {
+ _lock.Exit();
+ }
+ }
+
+ }
+
+}
\ No newline at end of file
diff --git a/CatalystUI/Core/CatalystUI.Supplementary/Connectors/DataConnector/FileDataConnectorBase.cs b/CatalystUI/Core/CatalystUI.Supplementary/Connectors/DataConnector/FileDataConnectorBase.cs
new file mode 100644
index 0000000..f12fe37
--- /dev/null
+++ b/CatalystUI/Core/CatalystUI.Supplementary/Connectors/DataConnector/FileDataConnectorBase.cs
@@ -0,0 +1,157 @@
+// -------------------------------------------------------------------------------------------------
+// CatalystUI Framework for .NET Core - https://catalystui.org/
+// Copyright (c) 2025 CatalystUI LLC. All rights reserved.
+//
+// This file is part of CatalystUI and is provided as part of an early-access release.
+// Unauthorized commercial use, distribution, or modification is strictly prohibited.
+//
+// This software is not open source and is not publicly licensed.
+// For full terms, see the LICENSE and NOTICE files in the project root.
+// -------------------------------------------------------------------------------------------------
+
+using Catalyst.Domains;
+using Catalyst.Layers;
+using System;
+using System.Diagnostics.CodeAnalysis;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.IO;
+
+// ReSharper disable once CheckNamespace
+namespace Catalyst.Supplementary {
+
+ ///
+ /// A base implementation of .
+ ///
+ ///
+ public abstract class FileDataConnectorBase : IFileDataConnector where TLayerLow : ISemanticsLayer {
+
+ ///
+ /// A concurrent dictionary mapping binding contracts to their associated file information.
+ ///
+ protected readonly ConcurrentDictionary _boundContracts;
+
+ ///
+ /// A concurrent dictionary mapping file information to their associated file streams.
+ ///
+ protected readonly ConcurrentDictionary _boundStreams;
+
+ ///
+ /// The file mode to use when opening files.
+ ///
+ protected readonly FileMode _fileMode;
+
+ ///
+ /// The file access level to use when opening files.
+ ///
+ protected readonly FileAccess _fileAccess;
+
+ ///
+ /// The file sharing mode to use when opening files.
+ ///
+ protected readonly FileShare _fileShare;
+
+ ///
+ /// A flag indicating whether to enforce exclusive access to files.
+ ///
+ protected readonly bool _enforceExclusivity;
+
+ ///
+ /// Constructs a new .
+ ///
+ /// The file mode to use when opening files.
+ /// The file access level to use when opening files.
+ /// The file sharing mode to use when opening files.
+ /// Whether to enforce exclusive access to files.
+ protected FileDataConnectorBase(FileMode fileMode = FileMode.OpenOrCreate, FileAccess fileAccess = FileAccess.ReadWrite, FileShare fileShare = FileShare.Read, bool enforceExclusivity = false) {
+ // Fields
+ _boundContracts = [];
+ _boundStreams = [];
+ _fileMode = fileMode;
+ _fileAccess = fileAccess;
+ _fileShare = fileShare;
+ _enforceExclusivity = enforceExclusivity;
+ }
+
+ ///
+ public abstract bool TryRead(FileInfo fileInfo, [NotNullWhen(true)] out TSemantic? semantic);
+
+ ///
+ public bool TryBind(FileInfo fileInfo, [NotNullWhen(true)] out BindingContract? contract) {
+ // If exclusivity is enforced, check if the file is already bound
+ if (_enforceExclusivity && _boundStreams.ContainsKey(fileInfo)) {
+ throw new InvalidOperationException($"File '{fileInfo.FullName}' is already bound to this handler.");
+ }
+
+ // Create the file stream and the bound file info
+ FileStream fileStream = fileInfo.Open(_fileMode, _fileAccess, _fileShare);
+ if (!_boundStreams.TryAdd(fileInfo, fileStream)) {
+ contract = null;
+ return false;
+ }
+
+ // Create the binding contract
+ contract = new();
+ contract.DisposeUnmanaged += OnBindingDisposed;
+ if (!_boundContracts.TryAdd(contract, fileInfo)) {
+ // If the binding contract could not be added, clean up the file stream
+ _boundStreams.TryRemove(fileInfo, out _);
+ fileStream.Dispose();
+ contract = null; // Clear the binding
+ return false;
+ }
+
+ // Fire the file bound event
+ OnFileBound(fileInfo, fileStream);
+ return true; // success
+ }
+
+ ///
+ public abstract bool TryWrite(FileInfo fileInfo, TSemantic semantic);
+
+ ///
+ /// Fired immediately after a file is bound to the handler.
+ ///
+ ///
+ /// Can be overridden to handle additional logic when a file is bound.
+ ///
+ /// The file information that was bound.
+ /// The file stream that was opened for the file.
+ protected virtual void OnFileBound(FileInfo fileInfo, FileStream fileStream) {
+ // ...
+ }
+
+ ///
+ /// Fired immediately before a file stream is closed from the handler.
+ ///
+ ///
+ /// Can be overridden to handle additional logic when a file is unbound.
+ ///
+ /// The file information that was unbound.
+ /// The file stream that was closed.
+ protected virtual void OnFileUnbound(FileInfo fileInfo, FileStream fileStream) {
+ // ...
+ }
+
+ ///
+ /// Fired when a binding is disposed.
+ ///
+ /// The binding contract that was disposed.
+ protected void OnBindingDisposed(BindingContract binding) {
+ binding.DisposeUnmanaged -= OnBindingDisposed;
+
+ // Find the bound file info associated with the binding
+ if (_boundContracts.TryRemove(binding, out FileInfo? fileInfo)) {
+ if (_boundStreams.TryGetValue(fileInfo, out FileStream? fileStream)) {
+ OnFileUnbound(fileInfo, fileStream);
+ _boundStreams.Remove(fileInfo, out _);
+ fileStream.Dispose();
+ }
+ } else {
+ throw new InvalidOperationException("Failed to remove binding contract from the handler.");
+ }
+ }
+
+ }
+
+}
\ No newline at end of file
diff --git a/CatalystUI/Core/CatalystUI.Supplementary/Connectors/DataConnector/IFileDataConnector.cs b/CatalystUI/Core/CatalystUI.Supplementary/Connectors/DataConnector/IFileDataConnector.cs
new file mode 100644
index 0000000..b2e2c42
--- /dev/null
+++ b/CatalystUI/Core/CatalystUI.Supplementary/Connectors/DataConnector/IFileDataConnector.cs
@@ -0,0 +1,75 @@
+// -------------------------------------------------------------------------------------------------
+// CatalystUI Framework for .NET Core - https://catalystui.org/
+// Copyright (c) 2025 CatalystUI LLC. All rights reserved.
+//
+// This file is part of CatalystUI and is provided as part of an early-access release.
+// Unauthorized commercial use, distribution, or modification is strictly prohibited.
+//
+// This software is not open source and is not publicly licensed.
+// For full terms, see the LICENSE and NOTICE files in the project root.
+// -------------------------------------------------------------------------------------------------
+
+using Catalyst.Layers;
+using Catalyst.Connectors;
+using Catalyst.Domains;
+using System;
+using System.Diagnostics.CodeAnalysis;
+using System.IO;
+
+// ReSharper disable once CheckNamespace
+namespace Catalyst.Supplementary {
+
+#pragma warning disable CS1712 // Type parameter has no matching typeparam tag in the XML comment (but other type parameters do)
+ ///
+ /// Represents a subset of the data connector specifically designed for handling file data.
+ ///
+ /// The semantic type will represent the file data.
+ ///
+ public interface IFileDataConnector : IDataConnector> where TLayerLow : ISemanticsLayer {
+#pragma warning restore CS1712 // Type parameter has no matching typeparam tag in the XML comment (but other type parameters do)
+
+ ///
+ /// Attempts to read and parse the file information into the specified semantic type.
+ ///
+ /// The file information to be read.
+ /// A new instance of if the read operation is successful; otherwise, .
+ /// if the read operation is successful; otherwise, .
+ /// Thrown if is .
+ bool TryRead(FileInfo fileInfo, [NotNullWhen(true)] out TSemantic? semantic);
+
+ ///
+ /// Attempts to bind the file information to the connector.
+ ///
+ ///
+ ///
+ /// Binding to a file allows the data connector to have active control over the file's data,
+ /// rather than performing stateless reads and writes. Typically, a data connector will then
+ /// use an existing to perform reads and writes, which can be
+ /// significantly faster than stateless operations, which would otherwise require re-reading
+ /// or completely overwriting the file for every operation. By binding to a file, the connector
+ /// can potentially determine a difference between the file's current semantic and a provided
+ /// semantic, significantly improving performance for large files or frequent updates.
+ ///
+ ///
+ /// The outputted is used to manage the lifecycle of the binding,
+ /// and should be disposed of when no longer needed, allowing the data connector to safely
+ /// clean up resources and release the file for other operations.
+ ///
+ ///
+ /// The file information to be bound.
+ /// A new if the bind operation is successful; otherwise, .
+ /// if the bind operation is successful; otherwise, .
+ bool TryBind(FileInfo fileInfo, [NotNullWhen(true)] out BindingContract? contract);
+
+ ///
+ /// Attempts to write the specified semantic data back to the file.
+ ///
+ /// The file information where the data will be written.
+ /// The semantic data to be written.
+ /// if the write operation is successful; otherwise, .
+ /// Thrown if or are .
+ bool TryWrite(FileInfo fileInfo, TSemantic semantic);
+
+ }
+
+}
\ No newline at end of file
diff --git a/CatalystUI/Core/CatalystUI.Supplementary/Interactions/Common/IDigitalInputData.cs b/CatalystUI/Core/CatalystUI.Supplementary/Interactions/Common/IDigitalInputData.cs
new file mode 100644
index 0000000..88f910e
--- /dev/null
+++ b/CatalystUI/Core/CatalystUI.Supplementary/Interactions/Common/IDigitalInputData.cs
@@ -0,0 +1,30 @@
+// -------------------------------------------------------------------------------------------------
+// CatalystUI Framework for .NET Core - https://catalystui.org/
+// Copyright (c) 2025 CatalystUI LLC. All rights reserved.
+//
+// This file is part of CatalystUI and is provided as part of an early-access release.
+// Unauthorized commercial use, distribution, or modification is strictly prohibited.
+//
+// This software is not open source and is not publicly licensed.
+// For full terms, see the LICENSE and NOTICE files in the project root.
+// -------------------------------------------------------------------------------------------------
+
+using Catalyst.Interactions;
+
+// ReSharper disable once CheckNamespace
+namespace Catalyst.Interactions {
+
+ ///
+ /// Represents input data for digital input devices (e.g., buttons, keys).
+ ///
+ public interface IDigitalInputData : IInputData {
+
+ ///
+ /// Gets a value indicating whether the digital input is currently activated (pressed) or not.
+ ///
+ /// if activated; otherwise, .
+ bool Activated { get; }
+
+ }
+
+}
\ No newline at end of file
diff --git a/CatalystUI/Core/CatalystUI.Supplementary/Interactions/Common/IPositionedInputData.cs b/CatalystUI/Core/CatalystUI.Supplementary/Interactions/Common/IPositionedInputData.cs
new file mode 100644
index 0000000..4aa7aca
--- /dev/null
+++ b/CatalystUI/Core/CatalystUI.Supplementary/Interactions/Common/IPositionedInputData.cs
@@ -0,0 +1,30 @@
+// -------------------------------------------------------------------------------------------------
+// CatalystUI Framework for .NET Core - https://catalystui.org/
+// Copyright (c) 2025 CatalystUI LLC. All rights reserved.
+//
+// This file is part of CatalystUI and is provided as part of an early-access release.
+// Unauthorized commercial use, distribution, or modification is strictly prohibited.
+//
+// This software is not open source and is not publicly licensed.
+// For full terms, see the LICENSE and NOTICE files in the project root.
+// -------------------------------------------------------------------------------------------------
+
+using Catalyst.Mathematics;
+
+// ReSharper disable once CheckNamespace
+namespace Catalyst.Interactions {
+
+ ///
+ /// Represents input data that includes positional information.
+ ///
+ public interface IPositionedInputData : IInputData {
+
+ ///
+ /// Gets the position associated with the input data.
+ ///
+ /// A representing the position.
+ Vector2 Position { get; }
+
+ }
+
+}
\ No newline at end of file
diff --git a/CatalystUI/Core/CatalystUI.Supplementary/Interactions/Common/IScrollInputData.cs b/CatalystUI/Core/CatalystUI.Supplementary/Interactions/Common/IScrollInputData.cs
new file mode 100644
index 0000000..855057e
--- /dev/null
+++ b/CatalystUI/Core/CatalystUI.Supplementary/Interactions/Common/IScrollInputData.cs
@@ -0,0 +1,34 @@
+// -------------------------------------------------------------------------------------------------
+// CatalystUI Framework for .NET Core - https://catalystui.org/
+// Copyright (c) 2025 CatalystUI LLC. All rights reserved.
+//
+// This file is part of CatalystUI and is provided as part of an early-access release.
+// Unauthorized commercial use, distribution, or modification is strictly prohibited.
+//
+// This software is not open source and is not publicly licensed.
+// For full terms, see the LICENSE and NOTICE files in the project root.
+// -------------------------------------------------------------------------------------------------
+
+using Catalyst.Mathematics;
+
+// ReSharper disable once CheckNamespace
+namespace Catalyst.Interactions {
+
+ ///
+ /// Represents input data for scroll input devices (e.g., mouse wheels, touchpad scrolls).
+ ///
+ public interface IScrollInputData : IInputData {
+
+ ///
+ /// Gets the offset of the scroll input in both horizontal and vertical directions.
+ ///
+ ///
+ /// Positive values typically indicate scrolling down/right,
+ /// while negative values indicate scrolling up/left.
+ ///
+ /// A representing the scroll offset.
+ Vector2 Offset { get; }
+
+ }
+
+}
\ No newline at end of file
diff --git a/CatalystUI/Core/CatalystUI.Supplementary/Interactions/Common/ITextInputData.cs b/CatalystUI/Core/CatalystUI.Supplementary/Interactions/Common/ITextInputData.cs
new file mode 100644
index 0000000..0e98b3a
--- /dev/null
+++ b/CatalystUI/Core/CatalystUI.Supplementary/Interactions/Common/ITextInputData.cs
@@ -0,0 +1,34 @@
+// -------------------------------------------------------------------------------------------------
+// CatalystUI Framework for .NET Core - https://catalystui.org/
+// Copyright (c) 2025 CatalystUI LLC. All rights reserved.
+//
+// This file is part of CatalystUI and is provided as part of an early-access release.
+// Unauthorized commercial use, distribution, or modification is strictly prohibited.
+//
+// This software is not open source and is not publicly licensed.
+// For full terms, see the LICENSE and NOTICE files in the project root.
+// -------------------------------------------------------------------------------------------------
+
+// ReSharper disable once CheckNamespace
+namespace Catalyst.Interactions {
+
+ ///
+ /// Represents input data for text input devices (e.g., keyboards, on-screen keyboards).
+ ///
+ ///
+ /// Text input is handled independently of a device since
+ /// it can originate from various sources. It's preferred
+ /// for input detection over a device-specific approach
+ /// because of Dead Keys.
+ ///
+ public interface ITextInputData : IInputData {
+
+ ///
+ /// Gets the text input received from the input device.
+ ///
+ /// The text input as a .
+ string Text { get; }
+
+ }
+
+}
\ No newline at end of file
diff --git a/CatalystUI/Core/CatalystUI.Supplementary/Interactions/Common/Impl/DigitalInputData.cs b/CatalystUI/Core/CatalystUI.Supplementary/Interactions/Common/Impl/DigitalInputData.cs
new file mode 100644
index 0000000..ccecac2
--- /dev/null
+++ b/CatalystUI/Core/CatalystUI.Supplementary/Interactions/Common/Impl/DigitalInputData.cs
@@ -0,0 +1,34 @@
+// -------------------------------------------------------------------------------------------------
+// CatalystUI Framework for .NET Core - https://catalystui.org/
+// Copyright (c) 2025 CatalystUI LLC. All rights reserved.
+//
+// This file is part of CatalystUI and is provided as part of an early-access release.
+// Unauthorized commercial use, distribution, or modification is strictly prohibited.
+//
+// This software is not open source and is not publicly licensed.
+// For full terms, see the LICENSE and NOTICE files in the project root.
+// -------------------------------------------------------------------------------------------------
+
+// ReSharper disable once CheckNamespace
+namespace Catalyst.Interactions {
+
+ ///
+ /// A structure conveying digital input data.
+ ///
+ public readonly record struct DigitalInputData : IDigitalInputData {
+
+ ///
+ public required bool Activated { get; init; }
+
+ ///
+ /// Constructs a new with
+ /// the specified activation state.
+ ///
+ /// The activation state of the digital input.
+ public DigitalInputData(bool activated) {
+ Activated = activated;
+ }
+
+ }
+
+}
\ No newline at end of file
diff --git a/CatalystUI/Core/CatalystUI.Supplementary/Interactions/Common/Impl/PositionedInputData.cs b/CatalystUI/Core/CatalystUI.Supplementary/Interactions/Common/Impl/PositionedInputData.cs
new file mode 100644
index 0000000..9e488c1
--- /dev/null
+++ b/CatalystUI/Core/CatalystUI.Supplementary/Interactions/Common/Impl/PositionedInputData.cs
@@ -0,0 +1,36 @@
+// -------------------------------------------------------------------------------------------------
+// CatalystUI Framework for .NET Core - https://catalystui.org/
+// Copyright (c) 2025 CatalystUI LLC. All rights reserved.
+//
+// This file is part of CatalystUI and is provided as part of an early-access release.
+// Unauthorized commercial use, distribution, or modification is strictly prohibited.
+//
+// This software is not open source and is not publicly licensed.
+// For full terms, see the LICENSE and NOTICE files in the project root.
+// -------------------------------------------------------------------------------------------------
+
+using Catalyst.Mathematics;
+
+// ReSharper disable once CheckNamespace
+namespace Catalyst.Interactions {
+
+ ///
+ /// A structure conveying positioned input data.
+ ///
+ public readonly record struct PositionedInputData : IPositionedInputData {
+
+ ///
+ public required Vector2 Position { get; init; }
+
+ ///
+ /// Constructs a new with
+ /// the specified position.
+ ///
+ /// The position associated with the input data.
+ public PositionedInputData(Vector2 position) {
+ Position = position;
+ }
+
+ }
+
+}
\ No newline at end of file
diff --git a/CatalystUI/Core/CatalystUI.Supplementary/Interactions/Common/Impl/ScrollInputData.cs b/CatalystUI/Core/CatalystUI.Supplementary/Interactions/Common/Impl/ScrollInputData.cs
new file mode 100644
index 0000000..1857640
--- /dev/null
+++ b/CatalystUI/Core/CatalystUI.Supplementary/Interactions/Common/Impl/ScrollInputData.cs
@@ -0,0 +1,38 @@
+// -------------------------------------------------------------------------------------------------
+// CatalystUI Framework for .NET Core - https://catalystui.org/
+// Copyright (c) 2025 CatalystUI LLC. All rights reserved.
+//
+// This file is part of CatalystUI and is provided as part of an early-access release.
+// Unauthorized commercial use, distribution, or modification is strictly prohibited.
+//
+// This software is not open source and is not publicly licensed.
+// For full terms, see the LICENSE and NOTICE files in the project root.
+// -------------------------------------------------------------------------------------------------
+
+using Catalyst.Mathematics;
+using System.Diagnostics.CodeAnalysis;
+
+// ReSharper disable once CheckNamespace
+namespace Catalyst.Interactions {
+
+ ///
+ /// A structure conveying scroll input data.
+ ///
+ public readonly record struct ScrollInputData : IScrollInputData {
+
+ ///
+ public required Vector2 Offset { get; init; }
+
+ ///
+ /// Constructs a new with
+ /// the specified offset.
+ ///
+ /// The scroll offset.
+ [SetsRequiredMembers]
+ public ScrollInputData(Vector2 offset) {
+ Offset = offset;
+ }
+
+ }
+
+}
\ No newline at end of file
diff --git a/CatalystUI/Core/CatalystUI.Supplementary/Interactions/Common/Impl/TextInputData.cs b/CatalystUI/Core/CatalystUI.Supplementary/Interactions/Common/Impl/TextInputData.cs
new file mode 100644
index 0000000..ae6b655
--- /dev/null
+++ b/CatalystUI/Core/CatalystUI.Supplementary/Interactions/Common/Impl/TextInputData.cs
@@ -0,0 +1,34 @@
+// -------------------------------------------------------------------------------------------------
+// CatalystUI Framework for .NET Core - https://catalystui.org/
+// Copyright (c) 2025 CatalystUI LLC. All rights reserved.
+//
+// This file is part of CatalystUI and is provided as part of an early-access release.
+// Unauthorized commercial use, distribution, or modification is strictly prohibited.
+//
+// This software is not open source and is not publicly licensed.
+// For full terms, see the LICENSE and NOTICE files in the project root.
+// -------------------------------------------------------------------------------------------------
+
+// ReSharper disable once CheckNamespace
+namespace Catalyst.Interactions {
+
+ ///
+ /// A structure conveying text input data.
+ ///
+ public readonly record struct TextInputData : ITextInputData {
+
+ ///
+ public required string Text { get; init; }
+
+ ///
+ /// Constructs a new with
+ /// the specified text.
+ ///
+ /// The text associated with the input data.
+ public TextInputData(string text) {
+ Text = text;
+ }
+
+ }
+
+}
\ No newline at end of file
diff --git a/CatalystUI/Core/CatalystUI.Supplementary/Interactions/Devices/Keyboard/IKeyboardInputData.cs b/CatalystUI/Core/CatalystUI.Supplementary/Interactions/Devices/Keyboard/IKeyboardInputData.cs
new file mode 100644
index 0000000..90a9aa3
--- /dev/null
+++ b/CatalystUI/Core/CatalystUI.Supplementary/Interactions/Devices/Keyboard/IKeyboardInputData.cs
@@ -0,0 +1,40 @@
+// -------------------------------------------------------------------------------------------------
+// CatalystUI Framework for .NET Core - https://catalystui.org/
+// Copyright (c) 2025 CatalystUI LLC. All rights reserved.
+//
+// This file is part of CatalystUI and is provided as part of an early-access release.
+// Unauthorized commercial use, distribution, or modification is strictly prohibited.
+//
+// This software is not open source and is not publicly licensed.
+// For full terms, see the LICENSE and NOTICE files in the project root.
+// -------------------------------------------------------------------------------------------------
+
+// ReSharper disable once CheckNamespace
+namespace Catalyst.Interactions.Devices {
+
+ ///
+ /// Represents keyboard input data.
+ ///
+ public interface IKeyboardInputData : IDigitalInputData {
+
+ ///
+ /// Gets the scan code of the key.
+ ///
+ /// The scan code of the key.
+ int ScanCode { get; }
+
+ ///
+ /// Gets the interpreted key from the keyboard input.
+ ///
+ /// The interpreted key.
+ KeyboardInputKey Key { get; }
+
+ ///
+ /// Gets the modifier keys active during the keyboard input.
+ ///
+ /// The modifier keys.
+ KeyboardInputModifiers Modifiers { get; }
+
+ }
+
+}
\ No newline at end of file
diff --git a/CatalystUI/Core/CatalystUI.Supplementary/Interactions/Devices/Keyboard/IKeyboardInputDevice.cs b/CatalystUI/Core/CatalystUI.Supplementary/Interactions/Devices/Keyboard/IKeyboardInputDevice.cs
new file mode 100644
index 0000000..7e8c8db
--- /dev/null
+++ b/CatalystUI/Core/CatalystUI.Supplementary/Interactions/Devices/Keyboard/IKeyboardInputDevice.cs
@@ -0,0 +1,24 @@
+// -------------------------------------------------------------------------------------------------
+// CatalystUI Framework for .NET Core - https://catalystui.org/
+// Copyright (c) 2025 CatalystUI LLC. All rights reserved.
+//
+// This file is part of CatalystUI and is provided as part of an early-access release.
+// Unauthorized commercial use, distribution, or modification is strictly prohibited.
+//
+// This software is not open source and is not publicly licensed.
+// For full terms, see the LICENSE and NOTICE files in the project root.
+// -------------------------------------------------------------------------------------------------
+
+// ReSharper disable once CheckNamespace
+namespace Catalyst.Interactions.Devices {
+
+ ///
+ /// Represents a keyboard input device.
+ ///
+ public interface IKeyboardInputDevice : IInputDevice {
+
+ // ...
+
+ }
+
+}
\ No newline at end of file
diff --git a/CatalystUI/Core/CatalystUI.Supplementary/Interactions/Devices/Keyboard/KeyboardInputData.cs b/CatalystUI/Core/CatalystUI.Supplementary/Interactions/Devices/Keyboard/KeyboardInputData.cs
new file mode 100644
index 0000000..37f5f5b
--- /dev/null
+++ b/CatalystUI/Core/CatalystUI.Supplementary/Interactions/Devices/Keyboard/KeyboardInputData.cs
@@ -0,0 +1,52 @@
+// -------------------------------------------------------------------------------------------------
+// CatalystUI Framework for .NET Core - https://catalystui.org/
+// Copyright (c) 2025 CatalystUI LLC. All rights reserved.
+//
+// This file is part of CatalystUI and is provided as part of an early-access release.
+// Unauthorized commercial use, distribution, or modification is strictly prohibited.
+//
+// This software is not open source and is not publicly licensed.
+// For full terms, see the LICENSE and NOTICE files in the project root.
+// -------------------------------------------------------------------------------------------------
+
+using System.Diagnostics.CodeAnalysis;
+
+// ReSharper disable once CheckNamespace
+namespace Catalyst.Interactions.Devices {
+
+ ///
+ /// A structure conveying keyboard input data.
+ ///
+ public readonly record struct KeyboardInputData : IKeyboardInputData {
+
+ ///
+ public required bool Activated { get; init; }
+
+ ///
+ public required int ScanCode { get; init; }
+
+ ///
+ public required KeyboardInputKey Key { get; init; }
+
+ ///
+ public required KeyboardInputModifiers Modifiers { get; init; }
+
+ ///
+ /// Constructs a new with
+ /// the specified activation state, scan code, key, and modifiers.
+ ///
+ /// Whether the key is activated (pressed) or not.
+ /// The scan code of the key.
+ /// The interpreted key.
+ /// The modifier keys.
+ [SetsRequiredMembers]
+ public KeyboardInputData(bool activated, int scanCode, KeyboardInputKey key, KeyboardInputModifiers modifiers) {
+ Activated = activated;
+ ScanCode = scanCode;
+ Key = key;
+ Modifiers = modifiers;
+ }
+
+ }
+
+}
\ No newline at end of file
diff --git a/CatalystUI/Core/CatalystUI.Supplementary/Interactions/Devices/Keyboard/KeyboardInputKey.cs b/CatalystUI/Core/CatalystUI.Supplementary/Interactions/Devices/Keyboard/KeyboardInputKey.cs
new file mode 100644
index 0000000..6bec0b0
--- /dev/null
+++ b/CatalystUI/Core/CatalystUI.Supplementary/Interactions/Devices/Keyboard/KeyboardInputKey.cs
@@ -0,0 +1,206 @@
+// -------------------------------------------------------------------------------------------------
+// CatalystUI Framework for .NET Core - https://catalystui.org/
+// Copyright (c) 2025 CatalystUI LLC. All rights reserved.
+//
+// This file is part of CatalystUI and is provided as part of an early-access release.
+// Unauthorized commercial use, distribution, or modification is strictly prohibited.
+//
+// This software is not open source and is not publicly licensed.
+// For full terms, see the LICENSE and NOTICE files in the project root.
+// -------------------------------------------------------------------------------------------------
+
+// ReSharper disable once CheckNamespace
+namespace Catalyst.Interactions.Devices {
+
+ ///
+ /// A list of keyboard keys.
+ ///
+ ///
+ ///
+ /// The design of the enum is to support
+ /// as many keys as possible, but there will
+ /// be limitations due to the nature of different
+ /// languages and keyboard formats.
+ ///
+ ///
+ /// The focus is to map keys to their associated
+ /// Unicode value under an English, QWERTY U.S. Layout.
+ /// However, implementing the actual translation
+ /// to characters will depend on the user's implementation.
+ ///
+ ///
+ /// Some keys, such as the F1-F24 keys, are mapped
+ /// to the private use Unicode space. If an unsupported
+ /// key is encountered, it should be mapped to the
+ /// 0xF900-0xF9FF range. CatalystUI reserves the
+ /// space of 0xF700-0xF8FF for future use.
+ ///
+ ///
+ public enum KeyboardInputKey {
+
+#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
+
+ // NULL
+ Null = 0,
+
+ // Function keys (F1-F24)
+ F1 = 0xF700,
+ F2 = 0xF701,
+ F3 = 0xF702,
+ F4 = 0xF703,
+ F5 = 0xF704,
+ F6 = 0xF705,
+ F7 = 0xF706,
+ F8 = 0xF707,
+ F9 = 0xF708,
+ F10 = 0xF709,
+ F11 = 0xF70A,
+ F12 = 0xF70B,
+ F13 = 0xF70C,
+ F14 = 0xF70D,
+ F15 = 0xF70E,
+ F16 = 0xF70F,
+ F17 = 0xF710,
+ F18 = 0xF711,
+ F19 = 0xF712,
+ F20 = 0xF713,
+ F21 = 0xF714,
+ F22 = 0xF715,
+ F23 = 0xF716,
+ F24 = 0xF717,
+
+ // Number keys
+ Zero = 0x0030,
+ One = 0x0031,
+ Two = 0x0032,
+ Three = 0x0033,
+ Four = 0x0034,
+ Five = 0x0035,
+ Six = 0x0036,
+ Seven = 0x0037,
+ Eight = 0x0038,
+ Nine = 0x0039,
+
+ // Alphanumeric keys
+ A = 0x0041,
+ B = 0x0042,
+ C = 0x0043,
+ D = 0x0044,
+ E = 0x0045,
+ F = 0x0046,
+ G = 0x0047,
+ H = 0x0048,
+ I = 0x0049,
+ J = 0x004A,
+ K = 0x004B,
+ L = 0x004C,
+ M = 0x004D,
+ N = 0x004E,
+ O = 0x004F,
+ P = 0x0050,
+ Q = 0x0051,
+ R = 0x0052,
+ S = 0x0053,
+ T = 0x0054,
+ U = 0x0055,
+ V = 0x0056,
+ W = 0x0057,
+ X = 0x0058,
+ Y = 0x0059,
+ Z = 0x005A,
+
+ // Symbols
+ Space = 0x0020,
+ Apostrophe = 0x0027,
+ Comma = 0x002C,
+ Dash = 0x002D,
+ Period = 0x002E,
+ Slash = 0x002F,
+ Semicolon = 0x003B,
+ Equals = 0x003D,
+ LeftBracket = 0x005B,
+ Backslash = 0x005C,
+ RightBracket = 0x005D,
+ GraveAccent = 0x0060,
+
+ // Special characters (with Unicode mappings)
+ Backspace = 0x0008,
+ Tab = 0x0009,
+ Enter = 0x000D,
+ Escape = 0x001B,
+ Delete = 0x007F,
+
+ // Special characters (without Unicode mappings)
+ PrintScreen = 0xF800,
+ ScrollLock = 0xF801,
+ PauseBreak = 0xF802,
+ Insert = 0xF803,
+ CapsLock = 0xF804,
+ Home = 0xF805,
+ End = 0xF806,
+ PageUp = 0xF807,
+ PageDown = 0xF808,
+ NumLock = 0xF809,
+
+ // Arrow keys
+ ArrowLeft = 0x2190,
+ ArrowUp = 0x2191,
+ ArrowRight = 0x2192,
+ ArrowDown = 0x2193,
+
+ // Modifier keys
+ LeftShift = 0xF820,
+ LeftControl = 0xF821,
+ LeftSuper = 0xF822,
+ LeftWindows = LeftSuper,
+ LeftCommand = LeftSuper,
+ LeftAlt = 0xF823,
+ RightShift = 0xF830,
+ RightControl = 0xF831,
+ RightSuper = 0xF832,
+ RightWindows = RightSuper,
+ RightCommand = RightSuper,
+ RightAlt = 0xF833,
+ Function = 0xF834,
+ Menu = 0xF835,
+
+ // Number pad
+ NumpadDivide = 0xF850,
+ NumpadMultiply = 0xF851,
+ NumpadSubtract = 0xF852,
+ NumpadAdd = 0xF853,
+ NumpadEnter = 0xF854,
+ NumpadDecimal = 0xF855,
+ NumpadZero = 0xF860,
+ NumpadOne = 0xF861,
+ NumpadTwo = 0xF862,
+ NumpadThree = 0xF863,
+ NumpadFour = 0xF864,
+ NumpadFive = 0xF865,
+ NumpadSix = 0xF866,
+ NumpadSeven = 0xF867,
+ NumpadEight = 0xF868,
+ NumpadNine = 0xF869,
+
+ // Media controls
+ MediaPlay = 0xF880,
+ MediaPause = 0xF881,
+ MediaStop = 0xF882,
+ MediaNext = 0xF883,
+ MediaPrevious = 0xF884,
+ VolumeMute = 0xF885,
+ VolumeUp = 0xF886,
+ VolumeDown = 0xF887,
+
+ // Computer controls
+ Help = 0xF890,
+ Print = 0xF891,
+ Power = 0xF892,
+ Sleep = 0xF893,
+ Wake = 0xF894,
+
+#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member
+
+ }
+
+}
\ No newline at end of file
diff --git a/CatalystUI/Core/CatalystUI.Supplementary/Interactions/Devices/Keyboard/KeyboardInputModifiers.cs b/CatalystUI/Core/CatalystUI.Supplementary/Interactions/Devices/Keyboard/KeyboardInputModifiers.cs
new file mode 100644
index 0000000..a7899f5
--- /dev/null
+++ b/CatalystUI/Core/CatalystUI.Supplementary/Interactions/Devices/Keyboard/KeyboardInputModifiers.cs
@@ -0,0 +1,60 @@
+// -------------------------------------------------------------------------------------------------
+// CatalystUI Framework for .NET Core - https://catalystui.org/
+// Copyright (c) 2025 CatalystUI LLC. All rights reserved.
+//
+// This file is part of CatalystUI and is provided as part of an early-access release.
+// Unauthorized commercial use, distribution, or modification is strictly prohibited.
+//
+// This software is not open source and is not publicly licensed.
+// For full terms, see the LICENSE and NOTICE files in the project root.
+// -------------------------------------------------------------------------------------------------
+
+using System;
+
+// ReSharper disable once CheckNamespace
+namespace Catalyst.Interactions.Devices {
+
+ ///
+ /// A list of keyboard modifier keys.
+ ///
+ [Flags]
+ public enum KeyboardInputModifiers {
+
+ ///
+ /// No modifier keys.
+ ///
+ None = 0,
+
+ ///
+ /// Shift modifier key.
+ ///
+ Shift = 1 << 1,
+
+ ///
+ /// Control modifier key.
+ ///
+ Control = 1 << 2,
+
+ ///
+ /// Alt modifier key.
+ ///
+ Alt = 1 << 3,
+
+ ///
+ /// (Linux/Other) Super modifier key.
+ ///
+ Super = 1 << 4,
+
+ ///
+ /// (Windows) Windows modifier key.
+ ///
+ Windows = Super,
+
+ ///
+ /// (MacOS) Command modifier key.
+ ///
+ Command = Super
+
+ }
+
+}
\ No newline at end of file
diff --git a/CatalystUI/Core/CatalystUI.Supplementary/Interactions/Devices/Mouse/IMouseInputDevice.cs b/CatalystUI/Core/CatalystUI.Supplementary/Interactions/Devices/Mouse/IMouseInputDevice.cs
new file mode 100644
index 0000000..a11b863
--- /dev/null
+++ b/CatalystUI/Core/CatalystUI.Supplementary/Interactions/Devices/Mouse/IMouseInputDevice.cs
@@ -0,0 +1,24 @@
+// -------------------------------------------------------------------------------------------------
+// CatalystUI Framework for .NET Core - https://catalystui.org/
+// Copyright (c) 2025 CatalystUI LLC. All rights reserved.
+//
+// This file is part of CatalystUI and is provided as part of an early-access release.
+// Unauthorized commercial use, distribution, or modification is strictly prohibited.
+//
+// This software is not open source and is not publicly licensed.
+// For full terms, see the LICENSE and NOTICE files in the project root.
+// -------------------------------------------------------------------------------------------------
+
+// ReSharper disable once CheckNamespace
+namespace Catalyst.Interactions.Devices {
+
+ ///
+ /// Represents a mouse input device.
+ ///
+ public interface IMouseInputDevice : IInputDevice {
+
+ // ...
+
+ }
+
+}
\ No newline at end of file
diff --git a/CatalystUI/Core/CatalystUI.Supplementary/Interactions/Devices/Mouse/IMouseMovedInputData.cs b/CatalystUI/Core/CatalystUI.Supplementary/Interactions/Devices/Mouse/IMouseMovedInputData.cs
new file mode 100644
index 0000000..b52cf36
--- /dev/null
+++ b/CatalystUI/Core/CatalystUI.Supplementary/Interactions/Devices/Mouse/IMouseMovedInputData.cs
@@ -0,0 +1,24 @@
+// -------------------------------------------------------------------------------------------------
+// CatalystUI Framework for .NET Core - https://catalystui.org/
+// Copyright (c) 2025 CatalystUI LLC. All rights reserved.
+//
+// This file is part of CatalystUI and is provided as part of an early-access release.
+// Unauthorized commercial use, distribution, or modification is strictly prohibited.
+//
+// This software is not open source and is not publicly licensed.
+// For full terms, see the LICENSE and NOTICE files in the project root.
+// -------------------------------------------------------------------------------------------------
+
+// ReSharper disable once CheckNamespace
+namespace Catalyst.Interactions.Devices {
+
+ ///
+ /// Represents mouse-move input data.
+ ///
+ public interface IMouseMovedInputData : IPositionedInputData {
+
+ // ...
+
+ }
+
+}
\ No newline at end of file
diff --git a/CatalystUI/Core/CatalystUI.Supplementary/Interactions/Devices/Mouse/IMousePressedInputData.cs b/CatalystUI/Core/CatalystUI.Supplementary/Interactions/Devices/Mouse/IMousePressedInputData.cs
new file mode 100644
index 0000000..fcb247e
--- /dev/null
+++ b/CatalystUI/Core/CatalystUI.Supplementary/Interactions/Devices/Mouse/IMousePressedInputData.cs
@@ -0,0 +1,40 @@
+// -------------------------------------------------------------------------------------------------
+// CatalystUI Framework for .NET Core - https://catalystui.org/
+// Copyright (c) 2025 CatalystUI LLC. All rights reserved.
+//
+// This file is part of CatalystUI and is provided as part of an early-access release.
+// Unauthorized commercial use, distribution, or modification is strictly prohibited.
+//
+// This software is not open source and is not publicly licensed.
+// For full terms, see the LICENSE and NOTICE files in the project root.
+// -------------------------------------------------------------------------------------------------
+
+// ReSharper disable once CheckNamespace
+namespace Catalyst.Interactions.Devices {
+
+ ///
+ /// Represents mouse-press input data.
+ ///
+ public interface IMousePressedInputData : IPositionedInputData, IDigitalInputData {
+
+ ///
+ /// Gets the mouse button that was pressed.
+ ///
+ /// The mouse button.
+ MouseInputButton Button { get; }
+
+ ///
+ /// Gets the identifier of the button that was pressed.
+ ///
+ /// The button identifier.
+ int ButtonId { get; }
+
+ ///
+ /// Gets the modifier keys active during the mouse press.
+ ///
+ /// The modifier keys.
+ KeyboardInputModifiers Modifiers { get; }
+
+ }
+
+}
\ No newline at end of file
diff --git a/CatalystUI/Core/CatalystUI.Supplementary/Interactions/Devices/Mouse/IMouseScrollInputData.cs b/CatalystUI/Core/CatalystUI.Supplementary/Interactions/Devices/Mouse/IMouseScrollInputData.cs
new file mode 100644
index 0000000..867221f
--- /dev/null
+++ b/CatalystUI/Core/CatalystUI.Supplementary/Interactions/Devices/Mouse/IMouseScrollInputData.cs
@@ -0,0 +1,24 @@
+// -------------------------------------------------------------------------------------------------
+// CatalystUI Framework for .NET Core - https://catalystui.org/
+// Copyright (c) 2025 CatalystUI LLC. All rights reserved.
+//
+// This file is part of CatalystUI and is provided as part of an early-access release.
+// Unauthorized commercial use, distribution, or modification is strictly prohibited.
+//
+// This software is not open source and is not publicly licensed.
+// For full terms, see the LICENSE and NOTICE files in the project root.
+// -------------------------------------------------------------------------------------------------
+
+// ReSharper disable once CheckNamespace
+namespace Catalyst.Interactions.Devices {
+
+ ///
+ /// Represents mouse-scroll input data.
+ ///
+ public interface IMouseScrollInputData : IPositionedInputData, IScrollInputData {
+
+ // ...
+
+ }
+
+}
\ No newline at end of file
diff --git a/CatalystUI/Core/CatalystUI.Supplementary/Interactions/Devices/Mouse/MouseInputButton.cs b/CatalystUI/Core/CatalystUI.Supplementary/Interactions/Devices/Mouse/MouseInputButton.cs
new file mode 100644
index 0000000..7bc3ec6
--- /dev/null
+++ b/CatalystUI/Core/CatalystUI.Supplementary/Interactions/Devices/Mouse/MouseInputButton.cs
@@ -0,0 +1,42 @@
+// -------------------------------------------------------------------------------------------------
+// CatalystUI Framework for .NET Core - https://catalystui.org/
+// Copyright (c) 2025 CatalystUI LLC. All rights reserved.
+//
+// This file is part of CatalystUI and is provided as part of an early-access release.
+// Unauthorized commercial use, distribution, or modification is strictly prohibited.
+//
+// This software is not open source and is not publicly licensed.
+// For full terms, see the LICENSE and NOTICE files in the project root.
+// -------------------------------------------------------------------------------------------------
+
+// ReSharper disable once CheckNamespace
+namespace Catalyst.Interactions.Devices {
+
+ ///
+ /// A list of mouse buttons.
+ ///
+ public enum MouseInputButton {
+
+ ///
+ /// The primary mouse button (typically left click).
+ ///
+ PrimaryButton,
+
+ ///
+ /// The secondary mouse button (typically right click).
+ ///
+ SecondaryButton,
+
+ ///
+ /// The tertiary mouse button (typically middle click).
+ ///
+ TertiaryButton,
+
+ ///
+ /// Any other button on the mouse.
+ ///
+ OtherButton
+
+ }
+
+}
\ No newline at end of file
diff --git a/CatalystUI/Core/CatalystUI.Supplementary/Interactions/Devices/Mouse/MouseMovedInputData.cs b/CatalystUI/Core/CatalystUI.Supplementary/Interactions/Devices/Mouse/MouseMovedInputData.cs
new file mode 100644
index 0000000..36a56ef
--- /dev/null
+++ b/CatalystUI/Core/CatalystUI.Supplementary/Interactions/Devices/Mouse/MouseMovedInputData.cs
@@ -0,0 +1,40 @@
+// -------------------------------------------------------------------------------------------------
+// CatalystUI Framework for .NET Core - https://catalystui.org/
+// Copyright (c) 2025 CatalystUI LLC. All rights reserved.
+//
+// This file is part of CatalystUI and is provided as part of an early-access release.
+// Unauthorized commercial use, distribution, or modification is strictly prohibited.
+//
+// This software is not open source and is not publicly licensed.
+// For full terms, see the LICENSE and NOTICE files in the project root.
+// -------------------------------------------------------------------------------------------------
+
+
+
+using System.Diagnostics.CodeAnalysis;
+using Catalyst.Mathematics;
+
+// ReSharper disable once CheckNamespace
+namespace Catalyst.Interactions.Devices {
+
+ ///
+ /// A structure conveying mouse moved input data.
+ ///
+ public readonly record struct MouseMovedInputData : IMouseMovedInputData {
+
+ ///
+ public required Vector2 Position { get; init; }
+
+ ///
+ /// Constructs a new with
+ /// the specified position.
+ ///
+ /// The position of the mouse.
+ [SetsRequiredMembers]
+ public MouseMovedInputData(Vector2 position) {
+ Position = position;
+ }
+
+ }
+
+}
\ No newline at end of file
diff --git a/CatalystUI/Core/CatalystUI.Supplementary/Interactions/Devices/Mouse/MousePressedInputData.cs b/CatalystUI/Core/CatalystUI.Supplementary/Interactions/Devices/Mouse/MousePressedInputData.cs
new file mode 100644
index 0000000..9403a3d
--- /dev/null
+++ b/CatalystUI/Core/CatalystUI.Supplementary/Interactions/Devices/Mouse/MousePressedInputData.cs
@@ -0,0 +1,58 @@
+// -------------------------------------------------------------------------------------------------
+// CatalystUI Framework for .NET Core - https://catalystui.org/
+// Copyright (c) 2025 CatalystUI LLC. All rights reserved.
+//
+// This file is part of CatalystUI and is provided as part of an early-access release.
+// Unauthorized commercial use, distribution, or modification is strictly prohibited.
+//
+// This software is not open source and is not publicly licensed.
+// For full terms, see the LICENSE and NOTICE files in the project root.
+// -------------------------------------------------------------------------------------------------
+
+using Catalyst.Mathematics;
+using System.Diagnostics.CodeAnalysis;
+
+// ReSharper disable once CheckNamespace
+namespace Catalyst.Interactions.Devices {
+
+ ///
+ /// A structure conveying mouse pressed input data.
+ ///
+ public readonly record struct MousePressedInputData : IMousePressedInputData {
+
+ ///
+ public required Vector2 Position { get; init; }
+
+ ///
+ public required bool Activated { get; init; }
+
+ ///
+ public required MouseInputButton Button { get; init; }
+
+ ///
+ public required int ButtonId { get; init; }
+
+ ///
+ public required KeyboardInputModifiers Modifiers { get; init; }
+
+ ///
+ /// Constructs a new with
+ /// the specified position, activation state, button, button ID, and modifiers.
+ ///
+ /// The position of the mouse.
+ /// The activation state of the button.
+ /// The mouse button.
+ /// The button identifier.
+ /// The modifier keys.
+ [SetsRequiredMembers]
+ public MousePressedInputData(Vector2 position, bool activated, MouseInputButton button, int buttonId, KeyboardInputModifiers modifiers) {
+ Position = position;
+ Activated = activated;
+ Button = button;
+ ButtonId = buttonId;
+ Modifiers = modifiers;
+ }
+
+ }
+
+}
\ No newline at end of file
diff --git a/CatalystUI/Core/CatalystUI.Supplementary/Interactions/Devices/Mouse/MouseScrollInputData.cs b/CatalystUI/Core/CatalystUI.Supplementary/Interactions/Devices/Mouse/MouseScrollInputData.cs
new file mode 100644
index 0000000..955c05e
--- /dev/null
+++ b/CatalystUI/Core/CatalystUI.Supplementary/Interactions/Devices/Mouse/MouseScrollInputData.cs
@@ -0,0 +1,43 @@
+// -------------------------------------------------------------------------------------------------
+// CatalystUI Framework for .NET Core - https://catalystui.org/
+// Copyright (c) 2025 CatalystUI LLC. All rights reserved.
+//
+// This file is part of CatalystUI and is provided as part of an early-access release.
+// Unauthorized commercial use, distribution, or modification is strictly prohibited.
+//
+// This software is not open source and is not publicly licensed.
+// For full terms, see the LICENSE and NOTICE files in the project root.
+// -------------------------------------------------------------------------------------------------
+
+using Catalyst.Mathematics;
+using System.Diagnostics.CodeAnalysis;
+
+// ReSharper disable once CheckNamespace
+namespace Catalyst.Interactions.Devices {
+
+ ///
+ /// A structure conveying mouse scroll input data.
+ ///
+ public readonly record struct MouseScrollInputData : IMouseScrollInputData {
+
+ ///
+ public required Vector2 Position { get; init; }
+
+ ///
+ public required Vector2 Offset { get; init; }
+
+ ///
+ /// Constructs a new with
+ /// the specified position and offset.
+ ///
+ /// The position of the mouse.
+ /// The scroll offset.
+ [SetsRequiredMembers]
+ public MouseScrollInputData(Vector2 position, Vector2 offset) {
+ Position = position;
+ Offset = offset;
+ }
+
+ }
+
+}
\ No newline at end of file
diff --git a/CatalystUI/Core/CatalystUI.Supplementary/Layers/DataLayer/IFileInfoDataLayer.cs b/CatalystUI/Core/CatalystUI.Supplementary/Layers/DataLayer/IFileInfoDataLayer.cs
new file mode 100644
index 0000000..0cdca4e
--- /dev/null
+++ b/CatalystUI/Core/CatalystUI.Supplementary/Layers/DataLayer/IFileInfoDataLayer.cs
@@ -0,0 +1,28 @@
+// -------------------------------------------------------------------------------------------------
+// CatalystUI Framework for .NET Core - https://catalystui.org/
+// Copyright (c) 2025 CatalystUI LLC. All rights reserved.
+//
+// This file is part of CatalystUI and is provided as part of an early-access release.
+// Unauthorized commercial use, distribution, or modification is strictly prohibited.
+//
+// This software is not open source and is not publicly licensed.
+// For full terms, see the LICENSE and NOTICE files in the project root.
+// -------------------------------------------------------------------------------------------------
+
+using Catalyst.Domains;
+using Catalyst.Layers;
+
+// ReSharper disable once CheckNamespace
+namespace Catalyst.Supplementary {
+
+ ///
+ /// Represents a subset of the data layer specifically for handling file information.
+ ///
+ ///
+ public interface IFileInfoDataLayer : IDataLayer where TDomain : IDomain {
+
+ // ...
+
+ }
+
+}
\ No newline at end of file
diff --git a/CatalystUI/Core/CatalystUI.Supplementary/Layers/SystemLayer/ILinuxSystemLayer.cs b/CatalystUI/Core/CatalystUI.Supplementary/Layers/SystemLayer/ILinuxSystemLayer.cs
new file mode 100644
index 0000000..dada472
--- /dev/null
+++ b/CatalystUI/Core/CatalystUI.Supplementary/Layers/SystemLayer/ILinuxSystemLayer.cs
@@ -0,0 +1,27 @@
+// -------------------------------------------------------------------------------------------------
+// CatalystUI Framework for .NET Core - https://catalystui.org/
+// Copyright (c) 2025 CatalystUI LLC. All rights reserved.
+//
+// This file is part of CatalystUI and is provided as part of an early-access release.
+// Unauthorized commercial use, distribution, or modification is strictly prohibited.
+//
+// This software is not open source and is not publicly licensed.
+// For full terms, see the LICENSE and NOTICE files in the project root.
+// -------------------------------------------------------------------------------------------------
+
+using Catalyst.Layers;
+using Catalyst.Domains;
+
+// ReSharper disable once CheckNamespace
+namespace Catalyst.Supplementary {
+
+ ///
+ /// Represents a system layer for Linux-based systems.
+ ///
+ public interface ILinuxSystemLayer : ISystemLayer {
+
+ // ...
+
+ }
+
+}
\ No newline at end of file
diff --git a/CatalystUI/Core/CatalystUI.Supplementary/Layers/SystemLayer/IMacSystemLayer.cs b/CatalystUI/Core/CatalystUI.Supplementary/Layers/SystemLayer/IMacSystemLayer.cs
new file mode 100644
index 0000000..d7e7d49
--- /dev/null
+++ b/CatalystUI/Core/CatalystUI.Supplementary/Layers/SystemLayer/IMacSystemLayer.cs
@@ -0,0 +1,27 @@
+// -------------------------------------------------------------------------------------------------
+// CatalystUI Framework for .NET Core - https://catalystui.org/
+// Copyright (c) 2025 CatalystUI LLC. All rights reserved.
+//
+// This file is part of CatalystUI and is provided as part of an early-access release.
+// Unauthorized commercial use, distribution, or modification is strictly prohibited.
+//
+// This software is not open source and is not publicly licensed.
+// For full terms, see the LICENSE and NOTICE files in the project root.
+// -------------------------------------------------------------------------------------------------
+
+using Catalyst.Layers;
+using Catalyst.Domains;
+
+// ReSharper disable once CheckNamespace
+namespace Catalyst.Supplementary {
+
+ ///
+ /// Represents a system layer for Apple's macOS-based systems.
+ ///
+ public interface IMacSystemLayer : ISystemLayer {
+
+ // ...
+
+ }
+
+}
\ No newline at end of file
diff --git a/CatalystUI/Core/CatalystUI.Supplementary/Layers/SystemLayer/IWindowsSystemLayer.cs b/CatalystUI/Core/CatalystUI.Supplementary/Layers/SystemLayer/IWindowsSystemLayer.cs
new file mode 100644
index 0000000..1fce9b9
--- /dev/null
+++ b/CatalystUI/Core/CatalystUI.Supplementary/Layers/SystemLayer/IWindowsSystemLayer.cs
@@ -0,0 +1,27 @@
+// -------------------------------------------------------------------------------------------------
+// CatalystUI Framework for .NET Core - https://catalystui.org/
+// Copyright (c) 2025 CatalystUI LLC. All rights reserved.
+//
+// This file is part of CatalystUI and is provided as part of an early-access release.
+// Unauthorized commercial use, distribution, or modification is strictly prohibited.
+//
+// This software is not open source and is not publicly licensed.
+// For full terms, see the LICENSE and NOTICE files in the project root.
+// -------------------------------------------------------------------------------------------------
+
+using Catalyst.Domains;
+using Catalyst.Layers;
+
+// ReSharper disable once CheckNamespace
+namespace Catalyst.Supplementary {
+
+ ///
+ /// Represents a system layer for Microsoft's Windows-based systems.
+ ///
+ public interface IWindowsSystemLayer : ISystemLayer {
+
+ // ...
+
+ }
+
+}
\ No newline at end of file
diff --git a/CatalystUI/Core/CatalystUI.Supplementary/Utilities/SystemDetector.cs b/CatalystUI/Core/CatalystUI.Supplementary/Utilities/SystemDetector.cs
new file mode 100644
index 0000000..a1ae07a
--- /dev/null
+++ b/CatalystUI/Core/CatalystUI.Supplementary/Utilities/SystemDetector.cs
@@ -0,0 +1,60 @@
+// -------------------------------------------------------------------------------------------------
+// CatalystUI Framework for .NET Core - https://catalystui.org/
+// Copyright (c) 2025 CatalystUI LLC. All rights reserved.
+//
+// This file is part of CatalystUI and is provided as part of an early-access release.
+// Unauthorized commercial use, distribution, or modification is strictly prohibited.
+//
+// This software is not open source and is not publicly licensed.
+// For full terms, see the LICENSE and NOTICE files in the project root.
+// -------------------------------------------------------------------------------------------------
+
+using Catalyst.Domains;
+using Catalyst.Layers;
+using System;
+using System.Runtime.InteropServices;
+
+// ReSharper disable once CheckNamespace
+namespace Catalyst.Supplementary {
+
+ ///
+ /// A set of utilities to determine the current operating system and environment.
+ ///
+ public static class SystemDetector {
+
+ ///
+ /// Gets the detected operating system type.
+ ///
+ public static Type? DetectedSystemType { get; }
+
+ ///
+ /// Static constructor for .
+ ///
+ static SystemDetector() {
+ // TODO: Is this sufficient? Or should the detection be more advanced?
+ // Maybe in the future the system layers could provide system information,
+ // such as versioning or platform-specific features, and then we could
+ // discern between them here.
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) {
+ DetectedSystemType = typeof(IWindowsSystemLayer);
+ } else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) {
+ DetectedSystemType = typeof(IMacSystemLayer);
+ } else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) {
+ DetectedSystemType = typeof(ILinuxSystemLayer);
+ } else {
+ DetectedSystemType = null;
+ }
+ }
+
+ ///
+ /// Determines if the current system is of the specified type.
+ ///
+ /// The type of the system to check.
+ /// if the current system is of the specified type; otherwise, .
+ public static bool IsSystem() where TSystem : ISystemLayer {
+ return DetectedSystemType != null && typeof(TSystem).IsAssignableFrom(DetectedSystemType);
+ }
+
+ }
+
+}
\ No newline at end of file
diff --git a/CatalystUI/Modules/Arcane/CatalystUI.Modules.Arcane.Core/CatalystUI.Modules.Arcane.Core.csproj b/CatalystUI/Modules/Arcane/CatalystUI.Modules.Arcane.Core/CatalystUI.Modules.Arcane.Core.csproj
new file mode 100644
index 0000000..dd54f63
--- /dev/null
+++ b/CatalystUI/Modules/Arcane/CatalystUI.Modules.Arcane.Core/CatalystUI.Modules.Arcane.Core.csproj
@@ -0,0 +1,24 @@
+
+
+
+
+
+