diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml
new file mode 100644
index 0000000..a65df8a
--- /dev/null
+++ b/.github/workflows/codeql.yml
@@ -0,0 +1,99 @@
+# For most projects, this workflow file will not need changing; you simply need
+# to commit it to your repository.
+#
+# You may wish to alter this file to override the set of languages analyzed,
+# or to provide custom queries or build logic.
+#
+# ******** NOTE ********
+# We have attempted to detect the languages in your repository. Please check
+# the `language` matrix defined below to confirm you have the correct set of
+# supported CodeQL languages.
+#
+name: "CodeQL Advanced"
+
+on:
+ push:
+ branches: [ "main", "beta", "alpha" ]
+ pull_request:
+ branches: [ "main", "beta", "alpha" ]
+ schedule:
+ - cron: '42 23 * * 3'
+
+jobs:
+ analyze:
+ name: Analyze (${{ matrix.language }})
+ # Runner size impacts CodeQL analysis time. To learn more, please see:
+ # - https://gh.io/recommended-hardware-resources-for-running-codeql
+ # - https://gh.io/supported-runners-and-hardware-resources
+ # - https://gh.io/using-larger-runners (GitHub.com only)
+ # Consider using larger runners or machines with greater resources for possible analysis time improvements.
+ runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }}
+ permissions:
+ # required for all workflows
+ security-events: write
+
+ # required to fetch internal or private CodeQL packs
+ packages: read
+
+ # only required for workflows in private repositories
+ actions: read
+ contents: read
+
+ strategy:
+ fail-fast: false
+ matrix:
+ include:
+ - language: actions
+ build-mode: none
+ # CodeQL supports the following values keywords for 'language': 'actions', 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'rust', 'swift'
+ # Use `c-cpp` to analyze code written in C, C++ or both
+ # Use 'java-kotlin' to analyze code written in Java, Kotlin or both
+ # Use 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both
+ # To learn more about changing the languages that are analyzed or customizing the build mode for your analysis,
+ # see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning.
+ # If you are analyzing a compiled language, you can modify the 'build-mode' for that language to customize how
+ # your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+
+ # Add any setup steps before running the `github/codeql-action/init` action.
+ # This includes steps like installing compilers or runtimes (`actions/setup-node`
+ # or others). This is typically only required for manual builds.
+ # - name: Setup runtime (example)
+ # uses: actions/setup-example@v1
+
+ # Initializes the CodeQL tools for scanning.
+ - name: Initialize CodeQL
+ uses: github/codeql-action/init@v4
+ with:
+ languages: ${{ matrix.language }}
+ build-mode: ${{ matrix.build-mode }}
+ # If you wish to specify custom queries, you can do so here or in a config file.
+ # By default, queries listed here will override any specified in a config file.
+ # Prefix the list here with "+" to use these queries and those in the config file.
+
+ # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
+ # queries: security-extended,security-and-quality
+
+ # If the analyze step fails for one of the languages you are analyzing with
+ # "We were unable to automatically build your code", modify the matrix above
+ # to set the build mode to "manual" for that language. Then modify this step
+ # to build your code.
+ # ℹ️ Command-line programs to run using the OS shell.
+ # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
+ - name: Run manual build steps
+ if: matrix.build-mode == 'manual'
+ shell: bash
+ run: |
+ echo 'If you are using a "manual" build mode for one or more of the' \
+ 'languages you are analyzing, replace this with the commands to build' \
+ 'your code, for example:'
+ echo ' make bootstrap'
+ echo ' make release'
+ exit 1
+
+ - name: Perform CodeQL Analysis
+ uses: github/codeql-action/analyze@v4
+ with:
+ category: "/language:${{matrix.language}}"
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/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml
index ad9bf76..c8bf401 100644
--- a/.github/workflows/dotnet.yml
+++ b/.github/workflows/dotnet.yml
@@ -7,9 +7,9 @@ permissions:
on:
push:
- branches: [ "main" ]
+ branches: [ "main", "beta", "alpha" ]
pull_request:
- branches: [ "main" ]
+ branches: [ "main", "beta", "alpha" ]
jobs:
build:
diff --git a/.scripts/Setup.ps1 b/.scripts/Setup.ps1
index 4171528..ee5587a 100644
--- a/.scripts/Setup.ps1
+++ b/.scripts/Setup.ps1
@@ -43,9 +43,34 @@ $projectsList = @(
@{
Module = "Core"
Projects = @(
- @{ Folder = "Core"; Name = "CatalystUI.Core" }
+ @{ 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.Debug" },
+ @{ Folder = "Core"; Name = "CatalystUI.Supplementary" }
)
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")
}
)
@@ -53,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/.editorconfig b/CatalystUI/.editorconfig
new file mode 100644
index 0000000..355e106
--- /dev/null
+++ b/CatalystUI/.editorconfig
@@ -0,0 +1,300 @@
+root = true
+
+# C# files
+[*.cs]
+
+#### Core EditorConfig Options ####
+
+# Indentation and spacing
+indent_size = 4
+indent_style = space
+tab_width = 4
+
+# New line preferences
+end_of_line = crlf
+insert_final_newline = false
+
+#### .NET Code Actions ####
+
+# Type members
+dotnet_hide_advanced_members = false
+dotnet_member_insertion_location = with_other_members_of_the_same_kind
+dotnet_property_generation_behavior = prefer_throwing_properties
+
+# Symbol search
+dotnet_search_reference_assemblies = true
+
+#### .NET Coding Conventions ####
+
+# Organize usings
+dotnet_separate_import_directive_groups = false
+dotnet_sort_system_directives_first = false
+file_header_template = -------------------------------------------------------------------------------------------------\nCatalystUI Framework for .NET Core - https://catalystui.org/\nCopyright (c) 2025 CatalystUI LLC. All rights reserved.\n\nThis file is part of CatalystUI and is provided as part of an early-access release.\nUnauthorized commercial use, distribution, or modification is strictly prohibited.\n\nThis software is not open source and is not publicly licensed.\nFor full terms, see the LICENSE and NOTICE files in the project root.\n-------------------------------------------------------------------------------------------------
+
+# this. and Me. preferences
+dotnet_style_qualification_for_event = false
+dotnet_style_qualification_for_field = false
+dotnet_style_qualification_for_method = false
+dotnet_style_qualification_for_property = false
+
+# Language keywords vs BCL types preferences
+dotnet_style_predefined_type_for_locals_parameters_members = true
+dotnet_style_predefined_type_for_member_access = true
+
+# Parentheses preferences
+dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity
+dotnet_style_parentheses_in_other_binary_operators = always_for_clarity
+dotnet_style_parentheses_in_other_operators = never_if_unnecessary
+dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity
+
+# Modifier preferences
+dotnet_style_require_accessibility_modifiers = for_non_interface_members
+
+# Expression-level preferences
+dotnet_prefer_system_hash_code = true
+dotnet_style_coalesce_expression = true
+dotnet_style_collection_initializer = true
+dotnet_style_explicit_tuple_names = true
+dotnet_style_namespace_match_folder = true
+dotnet_style_null_propagation = true
+dotnet_style_object_initializer = true
+dotnet_style_operator_placement_when_wrapping = beginning_of_line
+dotnet_style_prefer_auto_properties = true
+dotnet_style_prefer_collection_expression = when_types_loosely_match
+dotnet_style_prefer_compound_assignment = true
+dotnet_style_prefer_conditional_expression_over_assignment = true
+dotnet_style_prefer_conditional_expression_over_return = true
+dotnet_style_prefer_foreach_explicit_cast_in_source = when_strongly_typed
+dotnet_style_prefer_inferred_anonymous_type_member_names = true
+dotnet_style_prefer_inferred_tuple_names = true
+dotnet_style_prefer_is_null_check_over_reference_equality_method = true
+dotnet_style_prefer_simplified_boolean_expressions = true
+dotnet_style_prefer_simplified_interpolation = true
+
+# Field preferences
+dotnet_style_readonly_field = true
+
+# Parameter preferences
+dotnet_code_quality_unused_parameters = all
+
+# Suppression preferences
+dotnet_remove_unnecessary_suppression_exclusions = none
+
+# New line preferences
+dotnet_style_allow_multiple_blank_lines_experimental = true
+dotnet_style_allow_statement_immediately_after_block_experimental = true
+
+#### C# Coding Conventions ####
+
+# var preferences
+csharp_style_var_elsewhere = false
+csharp_style_var_for_built_in_types = false
+csharp_style_var_when_type_is_apparent = false
+
+# Expression-bodied members
+csharp_style_expression_bodied_accessors = true
+csharp_style_expression_bodied_constructors = false
+csharp_style_expression_bodied_indexers = true
+csharp_style_expression_bodied_lambdas = true
+csharp_style_expression_bodied_local_functions = false
+csharp_style_expression_bodied_methods = false
+csharp_style_expression_bodied_operators = false
+csharp_style_expression_bodied_properties = true
+
+# Pattern matching preferences
+csharp_style_pattern_matching_over_as_with_null_check = true
+csharp_style_pattern_matching_over_is_with_cast_check = true
+csharp_style_prefer_extended_property_pattern = true
+csharp_style_prefer_not_pattern = true
+csharp_style_prefer_pattern_matching = true
+csharp_style_prefer_switch_expression = true
+
+# Null-checking preferences
+csharp_style_conditional_delegate_call = true
+
+# Modifier preferences
+csharp_prefer_static_anonymous_function = true
+csharp_prefer_static_local_function = true
+csharp_preferred_modifier_order = public, private, protected, internal, file, static, extern, new, virtual, abstract, sealed, override, readonly, unsafe, required, volatile, async
+csharp_style_prefer_readonly_struct = true
+csharp_style_prefer_readonly_struct_member = true
+
+# Code-block preferences
+csharp_prefer_braces = true
+csharp_prefer_simple_using_statement = true
+csharp_prefer_system_threading_lock = true
+csharp_style_namespace_declarations = block_scoped
+csharp_style_prefer_method_group_conversion = true
+csharp_style_prefer_primary_constructors = true
+csharp_style_prefer_top_level_statements = true
+
+# Expression-level preferences
+csharp_prefer_simple_default_expression = true
+csharp_style_deconstructed_variable_declaration = true
+csharp_style_implicit_object_creation_when_type_is_apparent = true
+csharp_style_inlined_variable_declaration = true
+csharp_style_prefer_implicitly_typed_lambda_expression = true
+csharp_style_prefer_index_operator = true
+csharp_style_prefer_local_over_anonymous_function = true
+csharp_style_prefer_null_check_over_type_check = true
+csharp_style_prefer_range_operator = true
+csharp_style_prefer_tuple_swap = true
+csharp_style_prefer_unbound_generic_type_in_nameof = true
+csharp_style_prefer_utf8_string_literals = true
+csharp_style_throw_expression = true
+csharp_style_unused_value_assignment_preference = discard_variable
+csharp_style_unused_value_expression_statement_preference = discard_variable
+
+# 'using' directive preferences
+csharp_using_directive_placement = outside_namespace
+
+# New line preferences
+csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true
+csharp_style_allow_blank_line_after_token_in_arrow_expression_clause_experimental = true
+csharp_style_allow_blank_line_after_token_in_conditional_expression_experimental = true
+csharp_style_allow_blank_lines_between_consecutive_braces_experimental = true
+csharp_style_allow_embedded_statements_on_same_line_experimental = true
+
+#### C# Formatting Rules ####
+
+# New line preferences
+csharp_new_line_before_catch = false
+csharp_new_line_before_else = false
+csharp_new_line_before_finally = false
+csharp_new_line_before_members_in_anonymous_types = true
+csharp_new_line_before_members_in_object_initializers = true
+csharp_new_line_before_open_brace = none
+csharp_new_line_between_query_expression_clauses = true
+
+# Indentation preferences
+csharp_indent_block_contents = true
+csharp_indent_braces = false
+csharp_indent_case_contents = true
+csharp_indent_case_contents_when_block = true
+csharp_indent_labels = one_less_than_current
+csharp_indent_switch_labels = true
+
+# Space preferences
+csharp_space_after_cast = true
+csharp_space_after_colon_in_inheritance_clause = true
+csharp_space_after_comma = true
+csharp_space_after_dot = false
+csharp_space_after_keywords_in_control_flow_statements = true
+csharp_space_after_semicolon_in_for_statement = true
+csharp_space_around_binary_operators = before_and_after
+csharp_space_around_declaration_statements = false
+csharp_space_before_colon_in_inheritance_clause = true
+csharp_space_before_comma = false
+csharp_space_before_dot = false
+csharp_space_before_open_square_brackets = false
+csharp_space_before_semicolon_in_for_statement = false
+csharp_space_between_empty_square_brackets = false
+csharp_space_between_method_call_empty_parameter_list_parentheses = false
+csharp_space_between_method_call_name_and_opening_parenthesis = false
+csharp_space_between_method_call_parameter_list_parentheses = false
+csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
+csharp_space_between_method_declaration_name_and_open_parenthesis = false
+csharp_space_between_method_declaration_parameter_list_parentheses = false
+csharp_space_between_parentheses = false
+csharp_space_between_square_brackets = false
+
+# Wrapping preferences
+csharp_preserve_single_line_blocks = true
+csharp_preserve_single_line_statements = true
+
+#### Naming styles ####
+
+# Naming rules
+
+dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion
+dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface
+dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i
+
+dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion
+dotnet_naming_rule.types_should_be_pascal_case.symbols = types
+dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case
+
+dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion
+dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members
+dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case
+
+# Symbol specifications
+
+dotnet_naming_symbols.interface.applicable_kinds = interface
+dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
+dotnet_naming_symbols.interface.required_modifiers =
+
+dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum
+dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
+dotnet_naming_symbols.types.required_modifiers =
+
+dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
+dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
+dotnet_naming_symbols.non_field_members.required_modifiers =
+
+# Naming styles
+
+dotnet_naming_style.all_upper.required_prefix =
+dotnet_naming_style.all_upper.required_suffix =
+dotnet_naming_style.all_upper.word_separator = _
+dotnet_naming_style.all_upper.capitalization = all_upper
+
+dotnet_naming_style.pascal_case.required_prefix =
+dotnet_naming_style.pascal_case.required_suffix =
+dotnet_naming_style.pascal_case.word_separator =
+dotnet_naming_style.pascal_case.capitalization = pascal_case
+
+dotnet_naming_style.begins_with_i.required_prefix = I
+dotnet_naming_style.begins_with_i.required_suffix =
+dotnet_naming_style.begins_with_i.word_separator =
+dotnet_naming_style.begins_with_i.capitalization = pascal_case
+
+dotnet_naming_style.begins_with_underscore.required_prefix = _
+dotnet_naming_style.begins_with_underscore.required_suffix =
+dotnet_naming_style.begins_with_underscore.word_separator =
+dotnet_naming_style.begins_with_underscore.capitalization = camel_case
+
+# Naming rule for constant fields
+dotnet_naming_symbols.static_readonly_fields.applicable_kinds = field
+dotnet_naming_symbols.static_readonly_fields.applicable_accessibilities = public
+dotnet_naming_symbols.static_readonly_fields.required_modifiers = static, readonly
+dotnet_naming_rule.static_readonly_fields_should_be_upper_case.severity = suggestion
+dotnet_naming_rule.static_readonly_fields_should_be_upper_case.symbols = static_readonly_fields
+dotnet_naming_rule.static_readonly_fields_should_be_upper_case.style = all_upper
+dotnet_naming_symbols.const_fields.applicable_kinds = field
+dotnet_naming_symbols.const_fields.applicable_accessibilities = public
+dotnet_naming_symbols.const_fields.required_modifiers = const
+dotnet_naming_rule.const_fields_should_be_upper_case.severity = suggestion
+dotnet_naming_rule.const_fields_should_be_upper_case.symbols = const_fields
+dotnet_naming_rule.const_fields_should_be_upper_case.style = all_upper
+
+# Naming correction for enum members (only supports ReSharper; see https://github.com/dotnet/roslyn/issues/24209)
+resharper_csharp_naming_rule.enum_member = AaBb
+
+# Naming rule for instance fields
+dotnet_naming_symbols.private_fields.applicable_kinds = field
+dotnet_naming_symbols.private_fields.applicable_accessibilities = *
+dotnet_naming_symbols.private_fields.required_modifiers =
+dotnet_naming_rule.private_fields_should_be_begins_with_underscore.severity = suggestion
+dotnet_naming_rule.private_fields_should_be_begins_with_underscore.symbols = private_fields
+dotnet_naming_rule.private_fields_should_be_begins_with_underscore.style = begins_with_underscore
+
+# ReSharper properties
+resharper_blank_lines_after_multiline_statements = 0
+resharper_blank_lines_after_start_comment = 1
+resharper_blank_lines_after_using_list = 1
+resharper_blank_lines_around_auto_property = 1
+resharper_blank_lines_around_field = 1
+resharper_blank_lines_around_invocable = 1
+resharper_blank_lines_around_namespace = 1
+resharper_blank_lines_around_property = 1
+resharper_blank_lines_around_region = 1
+resharper_blank_lines_around_single_line_type = 0
+resharper_blank_lines_around_type = 1
+resharper_blank_lines_before_control_transfer_statements = 0
+resharper_blank_lines_inside_namespace = 1
+resharper_blank_lines_inside_type = 1
+resharper_csharp_remove_spaces_on_blank_lines = false
+resharper_space_within_array_access_brackets = false
+resharper_space_within_array_rank_brackets = false
+resharper_space_within_list_pattern_brackets = true
\ No newline at end of file
diff --git a/CatalystUI/CatalystUI.sln b/CatalystUI/CatalystUI.sln
index c3a3324..0b50ed1 100644
--- a/CatalystUI/CatalystUI.sln
+++ b/CatalystUI/CatalystUI.sln
@@ -4,6 +4,38 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Core", "Core", "{7EC51871-4
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CatalystUI.Core", "Core\CatalystUI.Core\CatalystUI.Core.csproj", "{68F496AC-9438-40F1-9DF8-97363033D661}"
EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tooling", "Tooling", "{5D38F696-8C11-4C9A-B50E-2C33AA7FAA6C}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CatalystUI.Profiling", "Tooling\CatalystUI.Profiling\CatalystUI.Profiling.csproj", "{10856DCF-AD1F-45C2-B995-E36CA4F8751B}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CatalystUI.Collections", "Core\CatalystUI.Collections\CatalystUI.Collections.csproj", "{9B36BF4B-52A9-4881-8D01-391627D51AB9}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CatalystUI.Threading", "Core\CatalystUI.Threading\CatalystUI.Threading.csproj", "{BFC8674D-AE56-4FF4-94B1-ACF5D6B2A4FA}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CatalystUI.Analyzers", "Tooling\CatalystUI.Analyzers\CatalystUI.Analyzers.csproj", "{A3936CB7-DC31-414B-9E40-CB9436391068}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CatalystUI.Attributes", "Core\CatalystUI.Attributes\CatalystUI.Attributes.csproj", "{44E8E3D2-FE47-49EA-A397-EB680E33AA2E}"
+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
@@ -14,8 +46,66 @@ Global
{68F496AC-9438-40F1-9DF8-97363033D661}.Debug|Any CPU.Build.0 = Debug|Any CPU
{68F496AC-9438-40F1-9DF8-97363033D661}.Release|Any CPU.ActiveCfg = Release|Any CPU
{68F496AC-9438-40F1-9DF8-97363033D661}.Release|Any CPU.Build.0 = Release|Any CPU
+ {10856DCF-AD1F-45C2-B995-E36CA4F8751B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {10856DCF-AD1F-45C2-B995-E36CA4F8751B}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {10856DCF-AD1F-45C2-B995-E36CA4F8751B}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {10856DCF-AD1F-45C2-B995-E36CA4F8751B}.Release|Any CPU.Build.0 = Release|Any CPU
+ {9B36BF4B-52A9-4881-8D01-391627D51AB9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {9B36BF4B-52A9-4881-8D01-391627D51AB9}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {9B36BF4B-52A9-4881-8D01-391627D51AB9}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {9B36BF4B-52A9-4881-8D01-391627D51AB9}.Release|Any CPU.Build.0 = Release|Any CPU
+ {BFC8674D-AE56-4FF4-94B1-ACF5D6B2A4FA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {BFC8674D-AE56-4FF4-94B1-ACF5D6B2A4FA}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {BFC8674D-AE56-4FF4-94B1-ACF5D6B2A4FA}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {BFC8674D-AE56-4FF4-94B1-ACF5D6B2A4FA}.Release|Any CPU.Build.0 = Release|Any CPU
+ {A3936CB7-DC31-414B-9E40-CB9436391068}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {A3936CB7-DC31-414B-9E40-CB9436391068}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {A3936CB7-DC31-414B-9E40-CB9436391068}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {A3936CB7-DC31-414B-9E40-CB9436391068}.Release|Any CPU.Build.0 = Release|Any CPU
+ {44E8E3D2-FE47-49EA-A397-EB680E33AA2E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {44E8E3D2-FE47-49EA-A397-EB680E33AA2E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {44E8E3D2-FE47-49EA-A397-EB680E33AA2E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {44E8E3D2-FE47-49EA-A397-EB680E33AA2E}.Release|Any CPU.Build.0 = Release|Any CPU
+ {E5319DB6-E93C-4A7D-9B3B-F219206BBC54}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {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}
+ {10856DCF-AD1F-45C2-B995-E36CA4F8751B} = {5D38F696-8C11-4C9A-B50E-2C33AA7FAA6C}
+ {9B36BF4B-52A9-4881-8D01-391627D51AB9} = {7EC51871-49A8-4991-BDAF-F43A4E9B8C9D}
+ {BFC8674D-AE56-4FF4-94B1-ACF5D6B2A4FA} = {7EC51871-49A8-4991-BDAF-F43A4E9B8C9D}
+ {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.Attributes/CatalystUI.Attributes.csproj b/CatalystUI/Core/CatalystUI.Attributes/CatalystUI.Attributes.csproj
new file mode 100644
index 0000000..f1c9654
--- /dev/null
+++ b/CatalystUI/Core/CatalystUI.Attributes/CatalystUI.Attributes.csproj
@@ -0,0 +1,32 @@
+
+
+
+
+
+ Catalyst.Attributes
+ Catalyst.Attributes
+
+
+ CatalystUI Attributes
+ 1.0.0
+ beta.2
+ CatalystUI LLC
+ Attributes API provided by the CatalystUI library.
+ CatalystUI,attributes
+
+
+
+
+ netstandard2.0
+ latest
+
+
+
+
+ false
+ false
+ false
+ none
+
+
+
\ No newline at end of file
diff --git a/CatalystUI/Core/CatalystUI.Attributes/Threading/CachedDelegateAttribute.cs b/CatalystUI/Core/CatalystUI.Attributes/Threading/CachedDelegateAttribute.cs
new file mode 100644
index 0000000..282cbac
--- /dev/null
+++ b/CatalystUI/Core/CatalystUI.Attributes/Threading/CachedDelegateAttribute.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 System;
+
+namespace Catalyst.Attributes.Threading {
+
+ ///
+ /// Generates a readonly, cached reference to the annotated method as a delegate.
+ ///
+ ///
+ ///
+ /// The name for the cached delegate field is prefixed with either _cachedAction or _cachedFunction,
+ /// depending on whether the method returns void or a value.
+ ///
+ ///
+ /// Valid signatures include:
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ [AttributeUsage(AttributeTargets.Method, Inherited = false)]
+ public sealed class CachedDelegateAttribute : Attribute {
+
+ // ...
+
+ }
+
+}
\ No newline at end of file
diff --git a/CatalystUI/Core/CatalystUI.Collections/CatalystUI.Collections.csproj b/CatalystUI/Core/CatalystUI.Collections/CatalystUI.Collections.csproj
new file mode 100644
index 0000000..111d7d0
--- /dev/null
+++ b/CatalystUI/Core/CatalystUI.Collections/CatalystUI.Collections.csproj
@@ -0,0 +1,18 @@
+
+
+
+
+
+ Catalyst.Collections
+ Catalyst.Collections
+
+
+ CatalystUI Collections
+ 1.0.0
+ beta.2
+ CatalystUI LLC
+ Collections API provided by the CatalystUI library.
+ CatalystUI,collections
+
+
+
\ No newline at end of file
diff --git a/CatalystUI/Core/CatalystUI.Collections/StaticArrayPool.cs b/CatalystUI/Core/CatalystUI.Collections/StaticArrayPool.cs
new file mode 100644
index 0000000..ebeb3d2
--- /dev/null
+++ b/CatalystUI/Core/CatalystUI.Collections/StaticArrayPool.cs
@@ -0,0 +1,212 @@
+// -------------------------------------------------------------------------------------------------
+// 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.Collections;
+using System.Collections.Generic;
+using System.ComponentModel;
+
+namespace Catalyst.Collections {
+
+ ///
+ /// A fixed-size pool implementation with zero memory
+ /// allocations after initialization.
+ ///
+ ///
+ ///
+ /// Allocations should be made using the method,
+ /// which returns a reference to the next available slot in the pool. Once
+ /// updated, the method should be fired, which
+ /// returns the index of the newly allocated element and increments
+ /// the count by one.
+ ///
+ ///
+ /// To release an allocated element, the
+ /// method should be called with the index of the element to release.
+ ///
+ ///
+ /// The type of elements stored in the pool.
+ public sealed class StaticArrayPool : IReadOnlyCollection where T : struct {
+
+ ///
+ /// The array that holds the elements of the pool.
+ ///
+ private readonly T[] _arr;
+
+ ///
+ /// A boolean array that indicates which elements in the pool are free.
+ ///
+ private readonly bool[] _free;
+
+ ///
+ /// A stack which holds the indices of allocated elements.
+ ///
+ private readonly StaticArrayStack _allocated;
+
+ ///
+ /// The current number of elements in the pool.
+ ///
+ private int _count;
+
+ ///
+ /// Gets the current number of elements in the pool.
+ ///
+ /// The pool's current element count.
+ public int Count => _count;
+
+ ///
+ /// Gets the total allocated capacity of the pool.
+ ///
+ /// The pool's total allocated capacity.
+ public int Capacity => _arr.Length;
+
+ ///
+ /// Gets the underlying array of the pool.
+ ///
+ ///
+ ///
+ /// INTERNAL USE ONLY.
+ ///
+ /// Version consistency is not guaranteed,
+ /// and the property may be changed without notice.
+ ///
+ ///
+ /// Warning: Fetching the underlying array
+ /// provides access to the raw data of the pool, meaning
+ /// the return value will provide both valid and invalid
+ /// instances of the underlying struct type. Prefer
+ /// enumeration via whenever
+ /// possible. This property is provided for advanced use cases
+ /// and/or disposal of the underlying structures, and should
+ /// be used with special care, attention, and caution.
+ ///
+ ///
+ [EditorBrowsable(EditorBrowsableState.Advanced)]
+ public T[] Items => _arr;
+
+ ///
+ /// Gets a flag indicating if the pool is empty.
+ ///
+ /// if the pool is empty; otherwise, .
+ public bool IsEmpty => _count == 0;
+
+ ///
+ /// Gets a flag indicating if the pool is full.
+ ///
+ /// if the pool is full; otherwise, .
+ public bool IsFull => _count == Capacity;
+
+ ///
+ /// Gets a reference to the element at the specified index.
+ ///
+ /// The index of the element to access.
+ /// Thrown if the pool is empty, the index is out of range, or the index is out of bounds.
+ public ref T this[int index] {
+ get {
+ if (IsEmpty) throw new IndexOutOfRangeException("Cannot access an empty pool.");
+ if (index < 0 || index >= Capacity) throw new IndexOutOfRangeException("Index is out of bounds of the pool.");
+ if (_free[index]) throw new IndexOutOfRangeException("Index is not allocated in the pool.");
+ return ref _arr[index];
+ }
+ }
+
+ ///
+ /// Constructs a new .
+ ///
+ /// The allocated capacity of the pool.
+ public StaticArrayPool(int capacity) {
+ _arr = new T[capacity];
+ _free = new bool[capacity];
+ _allocated = new(capacity);
+ for (int i = capacity - 1; i >= 0; i--) {
+ ref int index = ref _allocated.PeekPush();
+ index = i;
+ _allocated.Push();
+ _free[i] = true;
+ }
+ _count = 0;
+ }
+
+ ///
+ /// Peeks at the next available slot in the pool.
+ ///
+ /// A reference to the next available slot in the pool.
+ public ref T PeekNext() {
+ if (IsFull) throw new IndexOutOfRangeException("Cannot peek at next element in a full pool.");
+ return ref _arr[_allocated.PeekPop()];
+ }
+
+ ///
+ /// Increments the count of allocated elements and
+ /// returns the index of the newly allocated element.
+ ///
+ /// The index of the newly allocated element.
+ public int Allocate() {
+ if (IsFull) throw new IndexOutOfRangeException("Cannot allocate elements in a full pool.");
+ ref int index = ref _allocated.PeekPop();
+ _allocated.Pop();
+ _free[index] = false;
+ _count++;
+ return index;
+ }
+
+ ///
+ /// Releases an allocated element in the pool by its index.
+ ///
+ /// The index of the element to release.
+ public void Release(int index) {
+ if (IsEmpty) throw new IndexOutOfRangeException("Cannot release elements from an empty pool.");
+ ref int slot = ref _allocated.PeekPush();
+ slot = index;
+ _allocated.Push();
+ _free[index] = true;
+ _count--;
+ }
+
+ ///
+ /// Clears the queue by resetting all elements to their defaults.
+ ///
+ public void Clear() {
+ for (int i = 0; i < _arr.Length; i++) {
+ _arr[i] = default;
+ _free[i] = true;
+ }
+ _allocated.Clear();
+ for (int i = _arr.Length - 1; i >= 0; i--) {
+ ref int index = ref _allocated.PeekPush();
+ index = i;
+ _allocated.Push();
+ }
+ _count = 0;
+ }
+
+ ///
+ public IEnumerator GetEnumerator() {
+ if (IsEmpty) yield break;
+ if (IsFull) {
+ for (int i = 0; i < _arr.Length; i++) {
+ yield return _arr[i];
+ }
+ } else {
+ for (int i = 0; i < _arr.Length; i++) {
+ if (!_free[i]) yield return _arr[i];
+ }
+ }
+ }
+
+ ///
+ IEnumerator IEnumerable.GetEnumerator() {
+ return GetEnumerator();
+ }
+
+ }
+
+}
\ No newline at end of file
diff --git a/CatalystUI/Core/CatalystUI.Collections/StaticArrayQueue.cs b/CatalystUI/Core/CatalystUI.Collections/StaticArrayQueue.cs
new file mode 100644
index 0000000..7716578
--- /dev/null
+++ b/CatalystUI/Core/CatalystUI.Collections/StaticArrayQueue.cs
@@ -0,0 +1,211 @@
+// -------------------------------------------------------------------------------------------------
+// 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.Collections;
+using System.Collections.Generic;
+using System.ComponentModel;
+
+namespace Catalyst.Collections {
+
+ ///
+ /// A fixed-size queue implementation with zero memory
+ /// allocations after initialization.
+ ///
+ ///
+ ///
+ /// Allocations should be made using the method,
+ /// which returns a reference to the next available slot in the queue. Once
+ /// updated, the method should be fired to increment
+ /// the write counter by one.
+ ///
+ ///
+ /// Similarly, the method returns a reference to the
+ /// front-most enqueued slot in the queue. Once read, the
+ /// method should be called to increment the read counter by one.
+ ///
+ ///
+ /// The type of elements stored in the queue.
+ public sealed class StaticArrayQueue : IReadOnlyCollection where T : struct {
+
+ ///
+ /// The array used to store the queue elements.
+ ///
+ private readonly T[] _arr;
+
+ ///
+ /// The next index to write into the queue.
+ ///
+ private int _write;
+
+ ///
+ /// The next index to read from the queue.
+ ///
+ private int _read;
+
+ ///
+ /// The current number of elements in the queue.
+ ///
+ private int _count;
+
+ ///
+ /// Gets the current number of elements in the queue.
+ ///
+ /// The queue's current element count.
+ public int Count => _count;
+
+ ///
+ /// Gets the total allocated capacity of the queue.
+ ///
+ /// The queue's total allocated capacity.
+ public int Capacity => _arr.Length;
+
+ ///
+ /// Gets the underlying array of the queue.
+ ///
+ ///
+ ///
+ /// INTERNAL USE ONLY.
+ ///
+ /// Version consistency is not guaranteed,
+ /// and the property may be changed without notice.
+ ///
+ ///
+ /// Warning: Fetching the underlying array
+ /// provides access to the raw data of the queue, meaning
+ /// the return value will provide both valid and invalid
+ /// instances of the underlying struct type. Prefer
+ /// index-based access via the indexer
+ /// or enumeration via whenever
+ /// possible. This property is provided for advanced use cases
+ /// and/or disposal of the underlying structures, and should
+ /// be used with special care, attention, and caution.
+ ///
+ ///
+ [EditorBrowsable(EditorBrowsableState.Advanced)]
+ public T[] Items => _arr;
+
+ ///
+ /// Gets a flag indicating if the queue is empty.
+ ///
+ /// if the queue is empty; otherwise, .
+ public bool IsEmpty => _count == 0;
+
+ ///
+ /// Gets a flag indicating if the queue is full.
+ ///
+ /// if the queue is full; otherwise, .
+ public bool IsFull => _count == Capacity;
+
+ ///
+ /// Gets a reference to the element at the specified index.
+ ///
+ /// The index of the element to access.
+ /// Thrown if the queue is empty, the index is out of range, or the index is out of bounds.
+ public ref T this[int index] {
+ get {
+ if (IsEmpty) throw new IndexOutOfRangeException("Cannot access an empty queue.");
+ if (index < 0 || index >= _count) throw new IndexOutOfRangeException("Index is out of bounds of the queue.");
+ int adjustedIndex = (_read + index) % _arr.Length;
+ return ref _arr[adjustedIndex];
+ }
+ }
+
+ ///
+ /// Constructs a new .
+ ///
+ /// The allocated capacity of the queue.
+ public StaticArrayQueue(int capacity) {
+ if (capacity < 0) throw new ArgumentOutOfRangeException(nameof(capacity), "Capacity must be a number greater than zero!");
+ _arr = new T[capacity];
+ _write = 0;
+ _read = 0;
+ _count = 0;
+ }
+
+ ///
+ /// Peeks at the next available slot in the queue.
+ ///
+ /// A reference to the next available slot in the queue.
+ /// Thrown if the queue is full.
+ public ref T PeekEnqueue() {
+ if (IsFull) throw new IndexOutOfRangeException("Cannot peek the back of a full queue.");
+ return ref _arr[_write];
+ }
+
+ ///
+ /// Increments the write counter by one.
+ ///
+ /// Thrown if the queue is full.
+ public void Enqueue() {
+ if (IsFull) throw new IndexOutOfRangeException("Cannot enqueue to a full queue.");
+ _write = (_write + 1) % _arr.Length;
+ _count++;
+ }
+
+ ///
+ /// Peeks at the front-most element in the queue.
+ ///
+ /// A reference to the front-most element in the queue.
+ /// Thrown if the queue is empty.
+ public ref T PeekDequeue() {
+ if (IsEmpty) throw new IndexOutOfRangeException("Cannot peek the front of an empty queue.");
+ return ref _arr[_read];
+ }
+
+ ///
+ /// Increments the read counter by one.
+ ///
+ /// Thrown if the queue is empty.
+ public void Dequeue() {
+ if (IsEmpty) throw new IndexOutOfRangeException("Cannot dequeue from an empty queue.");
+ _read = (_read + 1) % _arr.Length;
+ _count--;
+ }
+
+ ///
+ /// Clears the queue by resetting all values to their defaults.
+ ///
+ public void Clear() {
+ for (int i = 0; i < _arr.Length; i++) {
+ ref T item = ref _arr[i];
+ item = default;
+ }
+ _write = 0;
+ _read = 0;
+ _count = 0;
+ }
+
+ ///
+ public IEnumerator GetEnumerator() {
+ if (IsEmpty) yield break;
+ if (IsFull) {
+ for (int i = 0; i < _arr.Length; i++) {
+ yield return _arr[i];
+ }
+ } else {
+ int remaining = _count;
+ int index = _read;
+ for (int i = 0; i < remaining; i++) {
+ yield return _arr[index];
+ index = (index + 1) % _arr.Length;
+ }
+ }
+ }
+
+ ///
+ IEnumerator IEnumerable.GetEnumerator() {
+ return GetEnumerator();
+ }
+
+ }
+
+}
\ No newline at end of file
diff --git a/CatalystUI/Core/CatalystUI.Collections/StaticArrayStack.cs b/CatalystUI/Core/CatalystUI.Collections/StaticArrayStack.cs
new file mode 100644
index 0000000..3b8e3e2
--- /dev/null
+++ b/CatalystUI/Core/CatalystUI.Collections/StaticArrayStack.cs
@@ -0,0 +1,182 @@
+// -------------------------------------------------------------------------------------------------
+// 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.Collections;
+using System.Collections.Generic;
+
+namespace Catalyst.Collections {
+
+ ///
+ /// A fixed-size stack implementation with zero memory
+ /// allocations after initialization.
+ ///
+ ///
+ ///
+ /// Allocations should be made using the method,
+ /// which returns a reference to the next available slot in the stack. Once
+ /// updated, the method should be fired to increment
+ /// the count by one.
+ ///
+ ///
+ /// Similarly, the method returns a reference to the
+ /// top-most element in the stack. Once read, the
+ /// method should be called to decrement the count by one.
+ ///
+ ///
+ /// The type of elements stored in the stack.
+ public sealed class StaticArrayStack : IReadOnlyCollection where T : struct {
+
+ ///
+ /// The array used to store the stack elements.
+ ///
+ private readonly T[] _arr;
+
+ ///
+ /// The current number of elements in the stack.
+ ///
+ private int _count;
+
+ ///
+ /// Gets the current number of elements in the stack.
+ ///
+ /// The stack's current element count.
+ public int Count => _count;
+
+ ///
+ /// Gets the total allocated capacity of the stack.
+ ///
+ /// The stack's total allocated capacity.
+ public int Capacity => _arr.Length;
+
+ ///
+ /// Gets the underlying array of the stack.
+ ///
+ ///
+ ///
+ /// INTERNAL USE ONLY.
+ ///
+ /// Version consistency is not guaranteed,
+ /// and the property may be changed without notice.
+ ///
+ ///
+ /// Warning: Fetching the underlying array
+ /// provides access to the raw data of the stack, meaning
+ /// the return value will provide both valid and invalid
+ /// instances of the underlying struct type. Prefer
+ /// enumeration via whenever
+ /// possible. This property is provided for advanced use cases
+ /// and/or disposal of the underlying structures, and should
+ /// be used with special care, attention, and caution.
+ ///
+ ///
+ public T[] Items => _arr;
+
+ ///
+ /// Gets a flag indicating whether the stack is empty.
+ ///
+ /// if the stack is empty; otherwise, .
+ public bool IsEmpty => _count == 0;
+
+ ///
+ /// Gets a flag indicating whether the stack is full.
+ ///
+ /// if the stack is full; otherwise, .
+ public bool IsFull => _count == _arr.Length;
+
+ ///
+ /// Gets a reference to the element at the specified index.
+ ///
+ /// The index of the element to access.
+ /// Thrown if the stack is empty, the index is out of range, or the index is out of bounds.
+ public ref T this[int index] {
+ get {
+ if (IsEmpty) throw new IndexOutOfRangeException("Cannot access an empty stack.");
+ if (index >= _count) throw new IndexOutOfRangeException("Index is out of bounds of the stack.");
+ return ref _arr[index];
+ }
+ }
+
+ ///
+ /// Constructs a new .
+ ///
+ /// The allocated capacity of the stack.
+ public StaticArrayStack(int capacity) {
+ if (capacity < 0) throw new ArgumentOutOfRangeException(nameof(capacity), "Capacity must be a number greater than zero!");
+ _arr = new T[capacity];
+ _count = 0;
+ }
+
+ ///
+ /// Peeks at the next available slot in the stack.
+ ///
+ /// A reference to the next available slot in the stack.
+ /// Thrown if the stack is full.
+ public ref T PeekPush() {
+ if (IsFull) throw new IndexOutOfRangeException("Stack is full, cannot peek next element!");
+ return ref _arr[_count];
+ }
+
+ ///
+ /// Increments the head counter by one.
+ ///
+ /// Thrown if the stack is full.
+ public void Push() {
+ if (IsFull) throw new IndexOutOfRangeException("Stack is full, cannot push new element!");
+ _count++;
+ }
+
+ ///
+ /// Peeks at the top element of the stack.
+ ///
+ /// A reference to the top element of the stack.
+ /// Thrown if the stack is empty.
+ public ref T PeekPop() {
+ if (IsEmpty) throw new IndexOutOfRangeException("Stack is empty, cannot peek top element!");
+ return ref _arr[_count - 1];
+ }
+
+ ///
+ /// Decrements the head counter by one.
+ ///
+ /// Thrown if the stack is empty.
+ public void Pop() {
+ if (IsEmpty) throw new IndexOutOfRangeException("Stack is empty, cannot pop top element!");
+ _count--;
+ }
+
+ ///
+ /// Clears the stack by resetting all values to their defaults.
+ ///
+ public void Clear() {
+ for (int i = 0; i < _count; i++) {
+ ref T item = ref _arr[i];
+ item = default;
+ }
+ _count = 0;
+ }
+
+ ///
+ public IEnumerator GetEnumerator() {
+ if (IsEmpty) yield break;
+ for (int i = _count - 1; i >= 0; i--) {
+ yield return _arr[i];
+ }
+ }
+
+ ///
+ IEnumerator IEnumerable.GetEnumerator() {
+ return GetEnumerator();
+ }
+
+ }
+
+}
\ No newline at end of file
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..09fdba0
--- /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 {
+
+ ///
+ /// This 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 0621225..f4acb4e 100644
--- a/CatalystUI/Core/CatalystUI.Core/CatalystUI.Core.csproj
+++ b/CatalystUI/Core/CatalystUI.Core/CatalystUI.Core.csproj
@@ -10,14 +10,19 @@
CatalystUI Core
1.0.0
beta.2
- FireController#1847
+ CatalystUI LLC
Core API provided by the CatalystUI library.
CatalystUI,core
-
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CatalystUI/Core/CatalystUI.Core/Connectors/IAdapterConnector.cs b/CatalystUI/Core/CatalystUI.Core/Connectors/IAdapterConnector.cs
new file mode 100644
index 0000000..445da68
--- /dev/null
+++ b/CatalystUI/Core/CatalystUI.Core/Connectors/IAdapterConnector.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;
+
+namespace Catalyst.Connectors {
+
+ ///
+ /// Represents the adapter connector in the CatalystUI model.
+ ///
+ ///
+ public interface IAdapterConnector : IConnector where TLayerLow : IFrameLayer where TLayerHigh : IComponentsLayer {
+
+ // ...
+
+ }
+
+}
\ No newline at end of file
diff --git a/CatalystUI/Core/CatalystUI.Core/Connectors/IBridgeConnector.cs b/CatalystUI/Core/CatalystUI.Core/Connectors/IBridgeConnector.cs
new file mode 100644
index 0000000..2b675b0
--- /dev/null
+++ b/CatalystUI/Core/CatalystUI.Core/Connectors/IBridgeConnector.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;
+
+namespace Catalyst.Connectors {
+
+ ///
+ /// Represents the bridge connector in the CatalystUI model.
+ ///
+ ///
+ public interface IBridgeConnector : IConnector where TLayerLow : IRendererLayer where TLayerHigh : IFrameLayer {
+
+ // ...
+
+ }
+
+}
\ No newline at end of file
diff --git a/CatalystUI/Core/CatalystUI.Core/Connectors/IDataConnector.cs b/CatalystUI/Core/CatalystUI.Core/Connectors/IDataConnector.cs
new file mode 100644
index 0000000..da20dbc
--- /dev/null
+++ b/CatalystUI/Core/CatalystUI.Core/Connectors/IDataConnector.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;
+
+namespace Catalyst.Connectors {
+
+ ///
+ /// Represents the data connector in the CatalystUI model.
+ ///
+ ///
+ public interface IDataConnector : IConnector where TLayerLow : ISemanticsLayer where TLayerHigh : IDataLayer {
+
+ // ...
+
+ }
+
+}
\ No newline at end of file
diff --git a/CatalystUI/Core/CatalystUI.Core/Connectors/INativeConnector.cs b/CatalystUI/Core/CatalystUI.Core/Connectors/INativeConnector.cs
new file mode 100644
index 0000000..b16e6ed
--- /dev/null
+++ b/CatalystUI/Core/CatalystUI.Core/Connectors/INativeConnector.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;
+
+namespace Catalyst.Connectors {
+
+ ///
+ /// Represents the native connector in the CatalystUI model.
+ ///
+ ///
+ public interface INativeConnector : IConnector where TLayerLow : ISystemLayer where TLayerHigh : IWindowLayer {
+
+ // ...
+
+ }
+
+}
\ No newline at end of file
diff --git a/CatalystUI/Core/CatalystUI.Core/Connectors/IParserConnector.cs b/CatalystUI/Core/CatalystUI.Core/Connectors/IParserConnector.cs
new file mode 100644
index 0000000..6d2e5fc
--- /dev/null
+++ b/CatalystUI/Core/CatalystUI.Core/Connectors/IParserConnector.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;
+
+namespace Catalyst.Connectors {
+
+ ///
+ /// Represents the parser connector in the CatalystUI model.
+ ///
+ ///
+ public interface IParserConnector : IConnector where TLayerLow : IComponentsLayer where TLayerHigh : ISemanticsLayer {
+
+ // ...
+
+ }
+
+}
\ No newline at end of file
diff --git a/CatalystUI/Core/CatalystUI.Core/Connectors/ISurfaceConnector.cs b/CatalystUI/Core/CatalystUI.Core/Connectors/ISurfaceConnector.cs
new file mode 100644
index 0000000..3116d6d
--- /dev/null
+++ b/CatalystUI/Core/CatalystUI.Core/Connectors/ISurfaceConnector.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;
+
+namespace Catalyst.Connectors {
+
+ ///
+ /// Represents the surface connector in the CatalystUI model.
+ ///
+ ///
+ public interface ISurfaceConnector : IConnector where TLayerLow : IWindowLayer where TLayerHigh : IRendererLayer {
+
+ // ...
+
+ }
+
+}
\ No newline at end of file
diff --git a/CatalystUI/Core/CatalystUI.Core/Debugging/CatalystDebug.cs b/CatalystUI/Core/CatalystUI.Core/Debugging/CatalystDebug.cs
new file mode 100644
index 0000000..d3dec4b
--- /dev/null
+++ b/CatalystUI/Core/CatalystUI.Core/Debugging/CatalystDebug.cs
@@ -0,0 +1,382 @@
+// -------------------------------------------------------------------------------------------------
+// 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.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
+using System.IO;
+using System.Linq;
+
+namespace Catalyst.Debugging {
+
+ ///
+ /// Container class for CatalystUI debugging utilities.
+ ///
+ public static class CatalystDebug {
+
+ ///
+ /// The environment variable prefix for CatalystUI debugging settings.
+ ///
+ public const string ENV_PREFIX = "CATALYST_DEBUG_";
+
+ ///
+ /// The name of the CatalystUI debugging configuration file.
+ ///
+ public const string CONFIG_FILE_NAME = "catdebug.ini";
+
+ ///
+ /// The name of the CatalystUI debugging output log file.
+ ///
+ public const string OUTPUT_FILE_NAME = "catdebug.log";
+
+ // The following is a list of primary sections and keys which
+ // are used in the configuration file for Catalyst debugging.
+#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
+ public const string DEBUG_SECTION_LOGGER = "Logger";
+ public const string DEBUG_KVP_LOGGER_MINIMUM_LEVEL = "MinimumLevel";
+ public const string DEBUG_KVP_LOGGER_SHOW_THREAD = "ShowThread";
+ public const string DEBUG_KVP_LOGGER_SHOW_FILENAME = "ShowFileName";
+ public const string DEBUG_KVP_LOGGER_SHOW_METHOD_NAME = "ShowMethodName";
+ public const string DEBUG_KVP_LOGGER_SHOW_LINE_NUMBER = "ShowLineNumber";
+ public const string DEBUG_KVP_LOGGER_SHOW_STACK_TRACE = "ShowStackTrace";
+ public const string DEBUG_SECTION_ENABLED_SCOPES = "EnabledScopes";
+#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member
+
+ ///
+ /// Gets or sets the current CatalystUI debugging configuration.
+ ///
+ /// The current instance.
+ public static CatalystDebugConfiguration? DebugConfiguration { get; private set; }
+
+ ///
+ /// Gets or sets the current CatalystUI debugging options.
+ ///
+ /// The current instance.
+ public static CatalystDebugOptions DebugOptions { get; private set; }
+
+ ///
+ /// A cache of configuration properties which
+ /// have been attempted to be loaded.
+ ///
+ private static readonly ConcurrentDictionary _configCache;
+
+ ///
+ /// A dictionary of enabled scopes for debugging.
+ ///
+ private static readonly ConcurrentDictionary _enabledScopes;
+
+ ///
+ /// A constructor for creating instances.
+ ///
+ private static Func? _debugContextConstructor;
+
+ ///
+ /// Static initializer for .
+ ///
+ static CatalystDebug() {
+ // Fields
+ _configCache = new();
+ _enabledScopes = new();
+ _debugContextConstructor = null;
+
+ // Properties
+ DebugConfiguration = LoadDebugConfiguration();
+ DebugOptions = LoadDebugOptions();
+
+ // Preload scopes
+ PreloadScopes();
+ }
+
+ ///
+ /// Loads the CatalystUI debugging configuration from file and environment variables.
+ ///
+ /// The loaded instance.
+ private static CatalystDebugConfiguration? LoadDebugConfiguration() {
+ try {
+ if (!File.Exists(CONFIG_FILE_NAME)) return null;
+ using FileStream stream = File.OpenRead(CONFIG_FILE_NAME);
+
+ // The following code is adapted from previous iterations
+ // of the CatalystUI Arcane project. To prevent interdependency,
+ // only the necessary code has been copied here.
+
+ // COPIED CODE START
+
+ // Read and parse the file into basic structures
+ Dictionary fileEntries = [ ];
+ Dictionary> fileSections = [ ];
+ using StreamReader reader = new(stream, leaveOpen: true);
+ string? section = null;
+ while (reader.ReadLine() is { } line) {
+ line = line.Trim();
+ if (string.IsNullOrWhiteSpace(line) || line.StartsWith(';')) continue; // Skip empty lines and comments
+
+ // Parse section tag
+ if (line.StartsWith('[') && line.EndsWith(']')) {
+ // New section
+ section = line[1..^1].Trim();
+ if (string.IsNullOrWhiteSpace(section)) section = null; // don't accept empty sections
+ continue;
+ }
+
+ // Split key-value pairs
+ int index = line.IndexOf('=');
+ if (index < 0) continue; // No key-value pair found
+ string key = line[..index].Trim();
+ string? value = line[(index + 1)..].Trim();
+ if (string.IsNullOrWhiteSpace(value)) value = null; // Treat empty values as null
+ if (value != null && (value = value.Trim()).Length >= 4) { // Check for "null" value
+ int idx = value.IndexOf("null", StringComparison.OrdinalIgnoreCase);
+ if (idx >= 0 &&
+ (idx == 0 || value[..idx].All(c => !char.IsLetterOrDigit(c))) &&
+ (idx + 4 == value.Length || value[(idx + 4)..].All(c => !char.IsLetterOrDigit(c))))
+ value = null;
+ }
+
+ // If we have a section, add to it; otherwise, add to global entries
+ if (section != null) {
+ if (!fileSections.TryGetValue(section, out Dictionary? sectionEntries)) {
+ sectionEntries = new();
+ fileSections[section] = sectionEntries;
+ }
+ sectionEntries[key] = value;
+ } else {
+ fileEntries[key] = value;
+ }
+ }
+
+ // COPIED CODE END
+
+ // Cast sections to read-only dictionaries
+ Dictionary> readonlySections =
+ fileSections.ToDictionary(
+ kvp => kvp.Key,
+ IReadOnlyDictionary (kvp) => kvp.Value
+ );
+
+ // Return the constructed configuration
+ return new CatalystDebugConfiguration(
+ globalEntries: fileEntries,
+ sections: readonlySections
+ );
+ } catch (Exception e) {
+ Trace.TraceWarning($"⚠️ [Catalyst.Debugging] Failed to load the debug configuration file. {e.Message}{Environment.NewLine}{e.StackTrace}");
+ return null;
+ }
+ }
+
+ ///
+ /// Gets a configuration value from the CatalystUI debugging configuration.
+ ///
+ /// The section in the configuration file to look for, or to use the global section.
+ /// The key to retrieve the value for.
+ /// The value retrieved from the configuration file or environment variable.
+ /// If , skips logging the retrieval of the configuration value.
+ /// if the value was found; otherwise, .
+ public static bool TryGetConfigValue(string? section, string key, [NotNullWhen(true)] out string? value, bool skipLog = false) {
+ // First, check the cache
+ if (_configCache.TryGetValue(key, out value)) {
+ return value != null; // the value is always found if it was cached
+ }
+
+ // First, check the environment variable
+ string env;
+ if (section != null) {
+ env = ENV_PREFIX + section.ToUpperInvariant() + "_" + key.ToUpperInvariant();
+ } else {
+ env = ENV_PREFIX + key.ToUpperInvariant();
+ }
+ value = Environment.GetEnvironmentVariable(env);
+ if (value != null) {
+ if (!skipLog) {
+ Trace.WriteLine($"ℹ️ [Catalyst.Debugging] Configuration value '{key}' found in environment variable '{env}' with value: {value}");
+ }
+ _configCache.TryAdd(key, value);
+ return true;
+ }
+
+ // Second, check the configuration file
+ if (DebugConfiguration != null) {
+ CatalystDebugConfiguration configFile = DebugConfiguration.Value;
+ if (!configFile.GlobalEntries.TryGetValue(key, out value) && section != null) {
+ if (configFile.Sections.TryGetValue(section, out IReadOnlyDictionary? sectionEntries)) {
+ _ = sectionEntries.TryGetValue(key, out value);
+ }
+ }
+ if (value != null) {
+ _configCache.TryAdd(key, value);
+ if (!skipLog) {
+ Trace.WriteLine($"ℹ️ [Catalyst.Debugging] Configuration value '{key}' found in section '{section ?? "Global"}' with value: {value}");
+ }
+ return true;
+ }
+ }
+
+ // Otherwise, we couldn't find it
+ value = null;
+ _configCache.TryAdd(key, null);
+ return false;
+ }
+
+ ///
+ /// Determines the log event level from a string representation.
+ ///
+ /// The string representation of the log level.
+ /// The corresponding log event level or if not recognized.
+ public static LogLevel? LogLevelFromString(string level) {
+ return level.Trim().ToLowerInvariant() switch {
+ "critical" => LogLevel.Critical,
+ "fatal" => LogLevel.Critical, // Alias for Critical
+ "error" => LogLevel.Error,
+ "exception" => LogLevel.Error, // Alias for Error
+ "warning" => LogLevel.Warning,
+ "information" => LogLevel.Info,
+ "info" => LogLevel.Info, // Alias for Information
+ "debug" => LogLevel.Debug,
+ "verbose" => LogLevel.Verbose,
+ _ => null
+ };
+ }
+
+ ///
+ /// Loads the CatalystUI debugging options from configuration.
+ ///
+ /// The loaded instance.
+ private static CatalystDebugOptions LoadDebugOptions() {
+ LogLevel level = LogLevel.Debug;
+ bool showThread = true;
+ bool showFileName = false;
+ bool showMethodName = false;
+ bool showLineNumber = false;
+ bool showStackTrace = false;
+
+ // Fetch each associated configuration value
+ if (TryGetConfigValue(DEBUG_SECTION_LOGGER, DEBUG_KVP_LOGGER_MINIMUM_LEVEL, out string? configLogLevel)) {
+ if (LogLevelFromString(configLogLevel) is LogLevel parsedLevel) {
+ level = parsedLevel;
+ }
+ }
+ if (TryGetConfigValue(DEBUG_SECTION_LOGGER, DEBUG_KVP_LOGGER_SHOW_THREAD, out string? configShowThread)) {
+ if (bool.TryParse(configShowThread, out bool parsedShowThread)) {
+ showThread = parsedShowThread;
+ }
+ }
+ if (TryGetConfigValue(DEBUG_SECTION_LOGGER, DEBUG_KVP_LOGGER_SHOW_FILENAME, out string? configShowFileName)) {
+ if (bool.TryParse(configShowFileName, out bool parsedShowFileName)) {
+ showFileName = parsedShowFileName;
+ }
+ }
+ if (TryGetConfigValue(DEBUG_SECTION_LOGGER, DEBUG_KVP_LOGGER_SHOW_METHOD_NAME, out string? configShowMethodName)) {
+ if (bool.TryParse(configShowMethodName, out bool parsedShowMethodName)) {
+ showMethodName = parsedShowMethodName;
+ }
+ }
+ if (TryGetConfigValue(DEBUG_SECTION_LOGGER, DEBUG_KVP_LOGGER_SHOW_LINE_NUMBER, out string? configShowLineNumber)) {
+ if (bool.TryParse(configShowLineNumber, out bool parsedShowLineNumber)) {
+ showLineNumber = parsedShowLineNumber;
+ }
+ }
+ if (TryGetConfigValue(DEBUG_SECTION_LOGGER, DEBUG_KVP_LOGGER_SHOW_STACK_TRACE, out string? configShowStackTrace)) {
+ if (bool.TryParse(configShowStackTrace, out bool parsedShowStackTrace)) {
+ showStackTrace = parsedShowStackTrace;
+ }
+ }
+
+ // Return the constructed options
+ return new(
+ minimumLogLevel: level,
+ showsThread: showThread,
+ showsFileName: showFileName,
+ showsMethodName: showMethodName,
+ showsLineNumber: showLineNumber,
+ showsStackTrace: showStackTrace
+ );
+ }
+
+ ///
+ /// Determines the log level for a given scope.
+ ///
+ /// The scope to determine the log level for.
+ /// The determined for the scope.
+ public static LogLevel DetermineScopeLevel(string scope) {
+ if (!_enabledScopes.TryGetValue(scope, out LogLevel level)) {
+ level = DebugOptions.MinimumLogLevel;
+ if (TryGetConfigValue(DEBUG_SECTION_ENABLED_SCOPES, scope, out string? configValue, true)) {
+ LogLevel? parsedResult = LogLevelFromString(configValue);
+ if (parsedResult == null) throw new InvalidOperationException($"Invalid log level '{configValue}' for scope '{scope}'. Using default level: {DebugOptions.MinimumLogLevel}");
+ level = parsedResult.Value;
+ _enabledScopes.TryAdd(scope, level);
+ } else {
+ 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($"ℹ️ [Catalyst.Debugging] Determined scope '{scope}' level: {level}");
+ }
+ }
+ return level;
+ }
+
+ ///
+ /// Preloads all existing scopes from the configuration.
+ ///
+ private static void PreloadScopes() {
+ // Scan through scopes and check if they are enabled
+ if (DebugConfiguration != null && DebugConfiguration.Value.Sections.TryGetValue(DEBUG_SECTION_ENABLED_SCOPES, out IReadOnlyDictionary? sectionEntries)) {
+ foreach (KeyValuePair entry in sectionEntries) {
+ // Determine the scope level
+ try {
+ DetermineScopeLevel(entry.Key);
+ } catch {
+ // ignore failed scope level determination
+ // special configuration values may use this section to store additional
+ // information for each scope, so we don't want to throw an error here
+ //
+ // an error is thrown in ForContext() if the scope level is invalid,
+ // however, since the property is expected to be used as a log level
+ // for the associated scope
+ }
+ }
+ }
+ }
+
+ ///
+ /// Injects a custom constructor for creating instances.
+ ///
+ ///
+ /// Custom implementations of can be used to provide custom logging or debugging behavior.
+ /// The constructor should return a new instance of or
+ /// an extended variety that overrides the default behavior.
+ ///
+ /// A function that constructs a new .
+ public static void InjectDebugContext(Func constructor) {
+ if (_debugContextConstructor != null) {
+ 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;
+ }
+
+ ///
+ /// Constructs a new debug context for the specified scope.
+ ///
+ /// The scope of the debug context.
+ /// A new instance of for the specified scope.
+ public static DebugContext ForContext(string scope) {
+ return _debugContextConstructor == null ? new NoopDebugContext(scope) : _debugContextConstructor(scope);
+ }
+
+ }
+
+}
\ No newline at end of file
diff --git a/CatalystUI/Core/CatalystUI.Core/Debugging/CatalystDebugConfiguration.cs b/CatalystUI/Core/CatalystUI.Core/Debugging/CatalystDebugConfiguration.cs
new file mode 100644
index 0000000..63a691c
--- /dev/null
+++ b/CatalystUI/Core/CatalystUI.Core/Debugging/CatalystDebugConfiguration.cs
@@ -0,0 +1,49 @@
+// -------------------------------------------------------------------------------------------------
+// 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.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
+
+namespace Catalyst.Debugging {
+
+ ///
+ /// The debug configuration file for CatalystUI.
+ ///
+ public readonly record struct CatalystDebugConfiguration {
+
+ ///
+ /// Gets a raw list of parsed global entries from the configuration file.
+ ///
+ /// The global entries dictionary.
+ public required IReadOnlyDictionary GlobalEntries { get; init; }
+
+ ///
+ /// Gets a raw list of parsed sections from the configuration file.
+ ///
+ /// The sections dictionary.
+ public required IReadOnlyDictionary> Sections { get; init; }
+
+ ///
+ /// Constructs a new .
+ ///
+ /// The global entries.
+ /// The sections.
+ [SetsRequiredMembers]
+ public CatalystDebugConfiguration(
+ IReadOnlyDictionary globalEntries,
+ IReadOnlyDictionary> sections) {
+ GlobalEntries = globalEntries;
+ Sections = sections;
+ }
+
+ }
+
+}
\ No newline at end of file
diff --git a/CatalystUI/Core/CatalystUI.Core/Debugging/CatalystDebugOptions.cs b/CatalystUI/Core/CatalystUI.Core/Debugging/CatalystDebugOptions.cs
new file mode 100644
index 0000000..b1f90ed
--- /dev/null
+++ b/CatalystUI/Core/CatalystUI.Core/Debugging/CatalystDebugOptions.cs
@@ -0,0 +1,93 @@
+// -------------------------------------------------------------------------------------------------
+// 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;
+
+namespace Catalyst.Debugging {
+
+ ///
+ /// Options which can be set to modify the way Catalyst debugging works.
+ ///
+ public readonly record struct CatalystDebugOptions {
+
+ ///
+ /// Gets the minimum logging level for Catalyst debugging.
+ ///
+ /// The minimum logging level.
+ public required LogLevel MinimumLogLevel { get; init; }
+
+ ///
+ /// Gets a flag indicating whether thread information should be shown in log entries.
+ ///
+ /// if thread information should be shown; otherwise, .
+ public required bool ShowsThread { get; init; }
+
+ ///
+ /// Gets a flag indicating whether file names should be shown in log entries.
+ ///
+ /// if file names should be shown; otherwise, .
+ public required bool ShowsFileName { get; init; }
+
+ ///
+ /// Gets a flag indicating whether method names should be shown in log entries.
+ ///
+ /// if method names should be shown; otherwise, .
+ public required bool ShowsMethodName { get; init; }
+
+ ///
+ /// Gets a flag indicating whether line numbers should be shown in log entries.
+ ///
+ /// if line numbers should be shown; otherwise, .
+ public required bool ShowsLineNumber { get; init; }
+
+ ///
+ /// Gets a flag indicating whether stack traces should be shown in log entries.
+ ///
+ /// if stack traces should be shown; otherwise, .
+ public required bool ShowsStackTrace { get; init; }
+
+ ///
+ /// Constructs a new with
+ /// default settings.
+ ///
+ [SetsRequiredMembers]
+ public CatalystDebugOptions() {
+ MinimumLogLevel = LogLevel.Debug;
+ ShowsThread = true;
+ ShowsFileName = false;
+ ShowsMethodName = false;
+ ShowsLineNumber = false;
+ ShowsStackTrace = false;
+ }
+
+ ///
+ /// Constructs a new with
+ /// the specified settings.
+ ///
+ [SetsRequiredMembers]
+ public CatalystDebugOptions(
+ LogLevel minimumLogLevel,
+ bool showsThread,
+ bool showsFileName,
+ bool showsMethodName,
+ bool showsLineNumber,
+ bool showsStackTrace) {
+ MinimumLogLevel = minimumLogLevel;
+ ShowsThread = showsThread;
+ ShowsFileName = showsFileName;
+ ShowsMethodName = showsMethodName;
+ ShowsLineNumber = showsLineNumber;
+ ShowsStackTrace = showsStackTrace;
+ }
+
+ }
+
+}
\ No newline at end of file
diff --git a/CatalystUI/Core/CatalystUI.Core/Debugging/DebugContext.cs b/CatalystUI/Core/CatalystUI.Core/Debugging/DebugContext.cs
new file mode 100644
index 0000000..e0d6606
--- /dev/null
+++ b/CatalystUI/Core/CatalystUI.Core/Debugging/DebugContext.cs
@@ -0,0 +1,143 @@
+// -------------------------------------------------------------------------------------------------
+// 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;
+
+namespace Catalyst.Debugging {
+
+ ///
+ /// A context for debugging operations within the CatalystUI framework.
+ ///
+ public abstract class DebugContext {
+
+ ///
+ /// Gets the scope of the debug context.
+ ///
+ /// The scope as a string.
+ public string Scope { get; }
+
+ ///
+ /// Gets or sets the prefix for debug messages.
+ ///
+ ///
+ /// No additional formatting is applied to the prefix.
+ ///
+ /// The prefix as a string.
+ public string? Prefix { get; protected set; }
+
+ ///
+ /// Gets or sets the log level for the debug context.
+ ///
+ /// The log level.
+ public LogLevel Level { get; protected set; }
+
+ ///
+ /// Constructs a new with
+ /// the specified scope.
+ ///
+ ///
+ protected DebugContext(string scope) {
+ Scope = scope;
+ Prefix = null;
+ Level = CatalystDebug.DebugOptions.MinimumLogLevel;
+ }
+
+ ///
+ /// Logs to the logger with the specified log level, message, and optional prefix and arguments.
+ ///
+ /// The log level for the message.
+ /// The message to log.
+ /// An optional prefix to prepend to the message.
+ /// Optional arguments to stringify at the end of the message.
+ [DebuggerHidden]
+ public virtual void Log(LogLevel level, string message, string? prefix = null, params object[] args) {
+ // no-op since internal library calls will not be filtered out by the compiler
+ // this is an unfortunate compromise to allow for debug logging from the library itself
+ // in addition to providing the debug functionality for the user
+ // any calls to the utility methods will be stripped by the compiler in release builds
+ // of both the library and the user's application, which is intended functionality
+ }
+
+ ///
+ /// Logs a fatal error with the specified message and optional arguments.
+ ///
+ ///
+ [Conditional("DEBUG")]
+ public void LogFatal(string message, params object[] args) {
+ if (Level < LogLevel.Critical) return;
+ Log(LogLevel.Critical, message, null, args);
+ }
+
+ ///
+ [Conditional("DEBUG")]
+ public void LogCritical(string message, params object[] args) => LogFatal(message, args);
+
+ ///
+ /// Logs an error with the specified message and optional arguments.
+ ///
+ ///
+ /// If an exception is provided as an argument, the exception's will be included in the log output.
+ /// If the message is null or empty, it will be replaced with the exception's
+ ///
+ ///
+ [Conditional("DEBUG")]
+ public void LogError(string message, params object[] args) {
+ if (Level < LogLevel.Error) return;
+ Log(LogLevel.Error, message, null, args);
+ }
+
+ ///
+ [Conditional("DEBUG")]
+ public void LogException(string message, params object[] args) => LogError(message, args);
+
+ ///
+ /// Logs a warning with the specified message and optional arguments.
+ ///
+ ///
+ [Conditional("DEBUG")]
+ public void LogWarning(string message, params object[] args) {
+ if (Level < LogLevel.Warning) return;
+ Log(LogLevel.Warning, message, null, args);
+ }
+
+ ///
+ /// Logs an informational message with the specified message and optional arguments.
+ ///
+ ///
+ [Conditional("DEBUG")]
+ public void LogInfo(string message, params object[] args) {
+ if (Level < LogLevel.Info) return;
+ Log(LogLevel.Info, message, null, args);
+ }
+
+ ///
+ /// Logs a debug message with the specified message and optional arguments.
+ ///
+ ///
+ [Conditional("DEBUG")]
+ public void LogDebug(string message, params object[] args) {
+ if (Level < LogLevel.Debug) return;
+ Log(LogLevel.Debug, message, null, args);
+ }
+
+ ///
+ /// Logs a verbose message with the specified message and optional arguments.
+ ///
+ ///
+ [Conditional("DEBUG")]
+ public void LogVerbose(string message, params object[] args) {
+ if (Level < LogLevel.Verbose) return;
+ Log(LogLevel.Verbose, message, null, args);
+ }
+
+ }
+
+}
\ No newline at end of file
diff --git a/CatalystUI/Core/CatalystUI.Core/Debugging/LogLevel.cs b/CatalystUI/Core/CatalystUI.Core/Debugging/LogLevel.cs
new file mode 100644
index 0000000..b4bbb8d
--- /dev/null
+++ b/CatalystUI/Core/CatalystUI.Core/Debugging/LogLevel.cs
@@ -0,0 +1,57 @@
+// -------------------------------------------------------------------------------------------------
+// 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.Debugging {
+
+ ///
+ /// A list of recognized log levels for debugging and logging within the CatalystUI framework.
+ ///
+ public enum LogLevel {
+
+ ///
+ /// No logs are emitted or processed.
+ ///
+ None = 0,
+
+ ///
+ /// Logs only critical errors that require immediate attention, such as application crashes or severe failures.
+ ///
+ Critical = 1,
+
+ ///
+ /// Logs critical errors and errors that occur but do not require immediate attention.
+ ///
+ Error = 2,
+
+ ///
+ /// Logs errors and additional warnings that may indicate potential issues.
+ ///
+ Warning = 3,
+
+ ///
+ /// Logs errors, warnings, and informational messages that provide insights into the application's state.
+ ///
+ Info = 4,
+
+ ///
+ /// Logs errors, warnings, informational messages, and detailed debugging information useful for diagnosing issues.
+ ///
+ Debug = 5,
+
+ ///
+ /// Logs all messages, including detailed debug information, performance metrics, and verbose output for in-depth analysis.
+ ///
+ Verbose = 6
+
+ }
+
+}
\ No newline at end of file
diff --git a/CatalystUI/Core/CatalystUI.Core/Debugging/NoopDebugContext.cs b/CatalystUI/Core/CatalystUI.Core/Debugging/NoopDebugContext.cs
new file mode 100644
index 0000000..1a1aa7d
--- /dev/null
+++ b/CatalystUI/Core/CatalystUI.Core/Debugging/NoopDebugContext.cs
@@ -0,0 +1,33 @@
+// -------------------------------------------------------------------------------------------------
+// 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.Debugging {
+
+ ///
+ /// Should be avoided whenever possible, a fallback debug context which is used
+ /// by the static class when no custom debug context
+ /// is provided.
+ ///
+ public sealed class NoopDebugContext : DebugContext {
+
+ ///
+ internal NoopDebugContext(string scope) : base(scope) {
+ // ...
+ }
+
+ ///
+ public override void Log(LogLevel level, string message, string? prefix = null, params object[] args) {
+ // no-op
+ }
+
+ }
+
+}
\ No newline at end of file
diff --git a/CatalystUI/Core/CatalystUI.Core/Domains/IAuditoryDomain.cs b/CatalystUI/Core/CatalystUI.Core/Domains/IAuditoryDomain.cs
new file mode 100644
index 0000000..6039fe7
--- /dev/null
+++ b/CatalystUI/Core/CatalystUI.Core/Domains/IAuditoryDomain.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.Domains {
+
+ ///
+ /// Represents the auditory domain in the CatalystUI model.
+ ///
+ public interface IAuditoryDomain : IDomain {
+
+ // ...
+
+ }
+
+}
\ No newline at end of file
diff --git a/CatalystUI/Core/CatalystUI.Core/Domains/IGustatoryDomain.cs b/CatalystUI/Core/CatalystUI.Core/Domains/IGustatoryDomain.cs
new file mode 100644
index 0000000..a62b5e4
--- /dev/null
+++ b/CatalystUI/Core/CatalystUI.Core/Domains/IGustatoryDomain.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.Domains {
+
+ ///
+ /// Represents the gustatory domain in the CatalystUI model.
+ ///
+ public interface IGustatoryDomain : IDomain {
+
+ // ...
+
+ }
+
+}
\ No newline at end of file
diff --git a/CatalystUI/Core/CatalystUI.Core/Domains/IMultisensoryDomain.cs b/CatalystUI/Core/CatalystUI.Core/Domains/IMultisensoryDomain.cs
new file mode 100644
index 0000000..673c826
--- /dev/null
+++ b/CatalystUI/Core/CatalystUI.Core/Domains/IMultisensoryDomain.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.Domains {
+
+ ///
+ /// Represents the multisensory domain in the CatalystUI model.
+ ///
+ public interface IMultisensoryDomain : IDomain {
+
+ // ...
+
+ }
+
+}
\ No newline at end of file
diff --git a/CatalystUI/Core/CatalystUI.Core/Domains/IOlfactoryDomain.cs b/CatalystUI/Core/CatalystUI.Core/Domains/IOlfactoryDomain.cs
new file mode 100644
index 0000000..e9af3f2
--- /dev/null
+++ b/CatalystUI/Core/CatalystUI.Core/Domains/IOlfactoryDomain.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.Domains {
+
+ ///
+ /// Represents the olfactory domain in the CatalystUI model.
+ ///
+ public interface IOlfactoryDomain : IDomain {
+
+ // ...
+
+ }
+
+}
\ No newline at end of file
diff --git a/CatalystUI/Core/CatalystUI.Core/Domains/ISymbolicDomain.cs b/CatalystUI/Core/CatalystUI.Core/Domains/ISymbolicDomain.cs
new file mode 100644
index 0000000..9b0ba52
--- /dev/null
+++ b/CatalystUI/Core/CatalystUI.Core/Domains/ISymbolicDomain.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.Domains {
+
+ ///
+ /// Represents the symbolic domain in the CatalystUI model.
+ ///
+ public interface ISymbolicDomain : IDomain {
+
+ // ...
+
+ }
+
+}
\ No newline at end of file
diff --git a/CatalystUI/Core/CatalystUI.Core/Domains/ITactileDomain.cs b/CatalystUI/Core/CatalystUI.Core/Domains/ITactileDomain.cs
new file mode 100644
index 0000000..408c636
--- /dev/null
+++ b/CatalystUI/Core/CatalystUI.Core/Domains/ITactileDomain.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.Domains {
+
+ ///
+ /// Represents the tactile domain in the CatalystUI model.
+ ///
+ public interface ITactileDomain : IDomain {
+
+ // ...
+
+ }
+
+}
\ No newline at end of file
diff --git a/CatalystUI/Core/CatalystUI.Core/Domains/IVisualDomain.cs b/CatalystUI/Core/CatalystUI.Core/Domains/IVisualDomain.cs
new file mode 100644
index 0000000..c7f657a
--- /dev/null
+++ b/CatalystUI/Core/CatalystUI.Core/Domains/IVisualDomain.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.Domains {
+
+ ///
+ /// Represents the visual domain in the CatalystUI model.
+ ///
+ public interface IVisualDomain : IDomain {
+
+ // ...
+
+ }
+
+}
\ No newline at end of file
diff --git a/CatalystUI/Core/CatalystUI.Core/IConnector.cs b/CatalystUI/Core/CatalystUI.Core/IConnector.cs
new file mode 100644
index 0000000..8fd590d
--- /dev/null
+++ b/CatalystUI/Core/CatalystUI.Core/IConnector.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 Catalyst.Domains;
+using Catalyst.Layers;
+using System;
+
+// ReSharper disable once CheckNamespace
+namespace Catalyst.Connectors {
+
+ ///
+ /// Represents a connector in the CatalystUI model.
+ ///
+ /// The layer type associated with the lower-level abstraction.
+ /// The layer type associated with the higher-level abstraction.
+ public interface IConnector where TLayerLow : ILayer where TLayerHigh : ILayer {
+
+ ///
+ /// Gets the type of the higher layer of the connector.
+ ///
+ /// The connector's high layer type.
+ static virtual Type HighLayer => typeof(TLayerHigh);
+
+ ///
+ /// Gets the type of the lower layer of the connector.
+ ///
+ /// The connector's low layer type.
+ static virtual Type LowLayer => typeof(TLayerLow);
+
+ }
+
+}
\ No newline at end of file
diff --git a/CatalystUI/Core/CatalystUI.Core/IDomain.cs b/CatalystUI/Core/CatalystUI.Core/IDomain.cs
new file mode 100644
index 0000000..669a223
--- /dev/null
+++ b/CatalystUI/Core/CatalystUI.Core/IDomain.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.Domains {
+
+ ///
+ /// Represents a domain in the CatalystUI model.
+ ///
+ public interface IDomain {
+
+ // ...
+
+ }
+
+}
\ No newline at end of file
diff --git a/CatalystUI/Core/CatalystUI.Core/ILayer.cs b/CatalystUI/Core/CatalystUI.Core/ILayer.cs
new file mode 100644
index 0000000..fa43891
--- /dev/null
+++ b/CatalystUI/Core/CatalystUI.Core/ILayer.cs
@@ -0,0 +1,32 @@
+// -------------------------------------------------------------------------------------------------
+// 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 System;
+
+// ReSharper disable once CheckNamespace
+namespace Catalyst.Layers {
+
+ ///
+ /// Represents a layer in the CatalystUI model.
+ ///
+ /// The domain type associated with the layer.
+ public interface ILayer where TDomain : IDomain {
+
+ ///
+ /// Gets the type of the domain of the layer.
+ ///
+ /// The layer's domain type.
+ static virtual Type Domain => typeof(TDomain);
+
+ }
+
+}
\ No newline at end of file
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/IInputDevice.cs b/CatalystUI/Core/CatalystUI.Core/Interactions/IInputDevice.cs
new file mode 100644
index 0000000..c8358c7
--- /dev/null
+++ b/CatalystUI/Core/CatalystUI.Core/Interactions/IInputDevice.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 originating input device for an interaction.
+ ///
+ public interface IInputDevice {
+
+ // ...
+
+ }
+
+}
\ No newline at end of file
diff --git a/CatalystUI/Core/CatalystUI.Core/Interactions/IInteraction.cs b/CatalystUI/Core/CatalystUI.Core/Interactions/IInteraction.cs
new file mode 100644
index 0000000..2fb777f
--- /dev/null
+++ b/CatalystUI/Core/CatalystUI.Core/Interactions/IInteraction.cs
@@ -0,0 +1,33 @@
+// -------------------------------------------------------------------------------------------------
+// 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 an interaction in the CatalystUI model.
+ ///
+ public interface IInteraction {
+
+ ///
+ /// Gets the input device that originated the interaction.
+ ///
+ /// 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/Interactions/Input/IHardwareDevice.cs b/CatalystUI/Core/CatalystUI.Core/Interactions/Input/IHardwareDevice.cs
new file mode 100644
index 0000000..097c8a7
--- /dev/null
+++ b/CatalystUI/Core/CatalystUI.Core/Interactions/Input/IHardwareDevice.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.
+// -------------------------------------------------------------------------------------------------
+
+namespace Catalyst.Interactions.Input {
+
+ ///
+ /// Represents a physical hardware input device,
+ /// such as a mouse, keyboard, or touch screen.
+ ///
+ public interface IHardwareDevice : IInputDevice {
+
+ // ...
+
+ }
+
+}
\ No newline at end of file
diff --git a/CatalystUI/Core/CatalystUI.Core/Interactions/Input/ILogicalDevice.cs b/CatalystUI/Core/CatalystUI.Core/Interactions/Input/ILogicalDevice.cs
new file mode 100644
index 0000000..ee1cd44
--- /dev/null
+++ b/CatalystUI/Core/CatalystUI.Core/Interactions/Input/ILogicalDevice.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.
+// -------------------------------------------------------------------------------------------------
+
+namespace Catalyst.Interactions.Input {
+
+ ///
+ /// Represents a logical input device, indicating the interaction
+ /// did not originate from a physical hardware device.
+ ///
+ public interface ILogicalDevice : IInputDevice {
+
+ // ...
+
+ }
+
+}
\ No newline at end of file
diff --git a/CatalystUI/Core/CatalystUI.Core/Layers/IComponentsLayer.cs b/CatalystUI/Core/CatalystUI.Core/Layers/IComponentsLayer.cs
new file mode 100644
index 0000000..d0a619a
--- /dev/null
+++ b/CatalystUI/Core/CatalystUI.Core/Layers/IComponentsLayer.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;
+
+namespace Catalyst.Layers {
+
+ ///
+ /// Represents the components layer in the CatalystUI model.
+ ///
+ ///
+ public interface IComponentsLayer : ILayer where TDomain : IDomain {
+
+ // ...
+
+ }
+
+}
\ No newline at end of file
diff --git a/CatalystUI/Core/CatalystUI.Core/Layers/IDataLayer.cs b/CatalystUI/Core/CatalystUI.Core/Layers/IDataLayer.cs
new file mode 100644
index 0000000..fc07364
--- /dev/null
+++ b/CatalystUI/Core/CatalystUI.Core/Layers/IDataLayer.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;
+
+namespace Catalyst.Layers {
+
+ ///
+ /// Represents the data layer in the CatalystUI model.
+ ///
+ ///
+ public interface IDataLayer : ILayer where TDomain : IDomain {
+
+ // ...
+
+ }
+
+}
\ No newline at end of file
diff --git a/CatalystUI/Core/CatalystUI.Core/Layers/IFrameLayer.cs b/CatalystUI/Core/CatalystUI.Core/Layers/IFrameLayer.cs
new file mode 100644
index 0000000..3dd29a4
--- /dev/null
+++ b/CatalystUI/Core/CatalystUI.Core/Layers/IFrameLayer.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;
+
+namespace Catalyst.Layers {
+
+ ///
+ /// Represents the frame layer in the CatalystUI model.
+ ///
+ ///
+ public interface IFrameLayer : ILayer where TDomain : IDomain {
+
+ // ...
+
+ }
+
+}
\ No newline at end of file
diff --git a/CatalystUI/Core/CatalystUI.Core/Layers/IRendererLayer.cs b/CatalystUI/Core/CatalystUI.Core/Layers/IRendererLayer.cs
new file mode 100644
index 0000000..c4f4e7f
--- /dev/null
+++ b/CatalystUI/Core/CatalystUI.Core/Layers/IRendererLayer.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.
+// -------------------------------------------------------------------------------------------------
+
+using Catalyst.Domains;
+
+namespace Catalyst.Layers {
+
+ ///
+ /// Represents the renderer layer in the CatalystUI model.
+ ///
+ ///
+ public interface IRendererLayer : ILayer where TDomain : IDomain {
+
+ // ...
+
+ }
+
+}
\ No newline at end of file
diff --git a/CatalystUI/Core/CatalystUI.Core/Layers/ISemanticsLayer.cs b/CatalystUI/Core/CatalystUI.Core/Layers/ISemanticsLayer.cs
new file mode 100644
index 0000000..158c2ce
--- /dev/null
+++ b/CatalystUI/Core/CatalystUI.Core/Layers/ISemanticsLayer.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.
+// -------------------------------------------------------------------------------------------------
+
+using Catalyst.Domains;
+
+namespace Catalyst.Layers {
+
+ ///
+ /// Represents the semantics layer in the CatalystUI model.
+ ///
+ ///
+ public interface ISemanticsLayer : ILayer where TDomain : IDomain {
+
+ // ...
+
+ }
+
+}
\ No newline at end of file
diff --git a/CatalystUI/Core/CatalystUI.Core/Layers/ISystemLayer.cs b/CatalystUI/Core/CatalystUI.Core/Layers/ISystemLayer.cs
new file mode 100644
index 0000000..a9367a4
--- /dev/null
+++ b/CatalystUI/Core/CatalystUI.Core/Layers/ISystemLayer.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.
+// -------------------------------------------------------------------------------------------------
+
+using Catalyst.Domains;
+
+namespace Catalyst.Layers {
+
+ ///
+ /// Represents the system layer in the CatalystUI model.
+ ///
+ ///
+ public interface ISystemLayer : ILayer where TDomain : IDomain {
+
+ // ...
+
+ }
+
+}
\ No newline at end of file
diff --git a/CatalystUI/Core/CatalystUI.Core/Layers/IWindowLayer.cs b/CatalystUI/Core/CatalystUI.Core/Layers/IWindowLayer.cs
new file mode 100644
index 0000000..a34a35d
--- /dev/null
+++ b/CatalystUI/Core/CatalystUI.Core/Layers/IWindowLayer.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.
+// -------------------------------------------------------------------------------------------------
+
+using Catalyst.Domains;
+
+namespace Catalyst.Layers {
+
+ ///
+ /// Represents the window layer in the CatalystUI model.
+ ///
+ ///
+ public interface IWindowLayer : ILayer where TDomain : IDomain {
+
+ // ...
+
+ }
+
+}
\ 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..ea5cdb8
--- /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 wrapped by this native API.
+ ///
+ /// 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..b9c28fd
--- /dev/null
+++ b/CatalystUI/Core/CatalystUI.Debug/Extensions/CatalystAppBuilderExtensions.cs
@@ -0,0 +1,51 @@
+// -------------------------------------------------------------------------------------------------
+// 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.
+ ///
+ ///
+ ///
+ /// This 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 this 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.
+ ///
+ ///
+ /// 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..fd3eb67
--- /dev/null
+++ b/CatalystUI/Core/CatalystUI.Debug/SerilogDebugContext.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 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 {
+
+ ///
+ /// The logger instance for this debug context.
+ ///
+ 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);
+ }
+
+ ///