diff --git a/.editorconfig b/.editorconfig
index 3f02430..34ef06c 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -1,158 +1,163 @@
-# EditorConfig is awesome: https://EditorConfig.org
-
-# top-most EditorConfig file
root = true
-# Don't use tabs for indentation.
+# All files
[*]
indent_style = space
-# (Please don't specify an indent_size here; that has too many unintended consequences.)
-
-# Code files
-[*.{cs,csx,vb,vbx}]
-indent_size = 4
-insert_final_newline = true
-charset = utf-8
-# XML project files
-[*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj}]
+# Xml files
+[*.xml]
indent_size = 2
-# XML config files
-[*.{props,targets,ruleset,config,nuspec,resx,vsixmanifest,vsct}]
-indent_size = 2
-
-# JSON files
-[*.json]
+# Xml project files
+[*.{csproj,fsproj,vbproj,proj,slnx}]
indent_size = 2
-# Powershell files
-[*.ps1]
+# Xml config files
+[*.{props,targets,config,nuspec}]
indent_size = 2
-# Shell script files
-[*.sh]
-end_of_line = lf
+[*.json]
indent_size = 2
-# Dotnet code style settings:
-[*.{cs,vb}]
-
-# Sort using and Import directives with System.* appearing first
-dotnet_sort_system_directives_first = true
-dotnet_separate_import_directive_groups = false
-# Avoid "this." and "Me." if not necessary
-dotnet_style_qualification_for_field = false:refactoring
-dotnet_style_qualification_for_property = false:refactoring
-dotnet_style_qualification_for_method = false:refactoring
-dotnet_style_qualification_for_event = false:refactoring
-
-# Use language keywords instead of framework type names for type references
-dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion
-dotnet_style_predefined_type_for_member_access = true:suggestion
-
-# Suggest more modern language features when available
-dotnet_style_object_initializer = true:suggestion
-dotnet_style_collection_initializer = true:suggestion
-dotnet_style_coalesce_expression = true:suggestion
-dotnet_style_null_propagation = true:suggestion
-dotnet_style_explicit_tuple_names = true:suggestion
-
-# Whitespace options
-dotnet_style_allow_multiple_blank_lines_experimental = false
-
-# Non-private static fields are PascalCase
-dotnet_naming_rule.non_private_static_fields_should_be_pascal_case.severity = suggestion
-dotnet_naming_rule.non_private_static_fields_should_be_pascal_case.symbols = non_private_static_fields
-dotnet_naming_rule.non_private_static_fields_should_be_pascal_case.style = non_private_static_field_style
-
-dotnet_naming_symbols.non_private_static_fields.applicable_kinds = field
-dotnet_naming_symbols.non_private_static_fields.applicable_accessibilities = public, protected, internal, protected_internal, private_protected
-dotnet_naming_symbols.non_private_static_fields.required_modifiers = static
-
-dotnet_naming_style.non_private_static_field_style.capitalization = pascal_case
-
-# Non-private readonly fields are PascalCase
-dotnet_naming_rule.non_private_readonly_fields_should_be_pascal_case.severity = suggestion
-dotnet_naming_rule.non_private_readonly_fields_should_be_pascal_case.symbols = non_private_readonly_fields
-dotnet_naming_rule.non_private_readonly_fields_should_be_pascal_case.style = non_private_readonly_field_style
-
-dotnet_naming_symbols.non_private_readonly_fields.applicable_kinds = field
-dotnet_naming_symbols.non_private_readonly_fields.applicable_accessibilities = public, protected, internal, protected_internal, private_protected
-dotnet_naming_symbols.non_private_readonly_fields.required_modifiers = readonly
-
-dotnet_naming_style.non_private_readonly_field_style.capitalization = pascal_case
-
-# Constants are PascalCase
-dotnet_naming_rule.constants_should_be_pascal_case.severity = suggestion
-dotnet_naming_rule.constants_should_be_pascal_case.symbols = constants
-dotnet_naming_rule.constants_should_be_pascal_case.style = constant_style
-
-dotnet_naming_symbols.constants.applicable_kinds = field, local
-dotnet_naming_symbols.constants.required_modifiers = const
-
-dotnet_naming_style.constant_style.capitalization = pascal_case
+# C# files
+[*.cs]
-# Static fields are camelCase and start with s_
-dotnet_naming_rule.static_fields_should_be_camel_case.severity = suggestion
-dotnet_naming_rule.static_fields_should_be_camel_case.symbols = static_fields
-dotnet_naming_rule.static_fields_should_be_camel_case.style = static_field_style
+#### Core EditorConfig Options ####
-dotnet_naming_symbols.static_fields.applicable_kinds = field
-dotnet_naming_symbols.static_fields.required_modifiers = static
+# Indentation and spacing
+indent_size = 4
+tab_width = 4
-dotnet_naming_style.static_field_style.capitalization = camel_case
-dotnet_naming_style.static_field_style.required_prefix = s_
+# New line preferences
+insert_final_newline = false
-# Instance fields are camelCase and start with _
-dotnet_naming_rule.instance_fields_should_be_camel_case.severity = suggestion
-dotnet_naming_rule.instance_fields_should_be_camel_case.symbols = instance_fields
-dotnet_naming_rule.instance_fields_should_be_camel_case.style = instance_field_style
+#### .NET Coding Conventions ####
+[*.{cs,vb}]
-dotnet_naming_symbols.instance_fields.applicable_kinds = field
+# Organize usings
+dotnet_separate_import_directive_groups = true
+dotnet_sort_system_directives_first = true
+file_header_template = unset
-dotnet_naming_style.instance_field_style.capitalization = camel_case
-dotnet_naming_style.instance_field_style.required_prefix = _
+# this. and Me. preferences
+dotnet_style_qualification_for_event = false:silent
+dotnet_style_qualification_for_field = false:silent
+dotnet_style_qualification_for_method = false:silent
+dotnet_style_qualification_for_property = false:silent
-# Locals and parameters are camelCase
-dotnet_naming_rule.locals_should_be_camel_case.severity = suggestion
-dotnet_naming_rule.locals_should_be_camel_case.symbols = locals_and_parameters
-dotnet_naming_rule.locals_should_be_camel_case.style = camel_case_style
+# Language keywords vs BCL types preferences
+dotnet_style_predefined_type_for_locals_parameters_members = true:silent
+dotnet_style_predefined_type_for_member_access = true:silent
-dotnet_naming_symbols.locals_and_parameters.applicable_kinds = parameter, local
+# Parentheses preferences
+dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent
+dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent
+dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent
+dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent
-dotnet_naming_style.camel_case_style.capitalization = camel_case
+# Modifier preferences
+dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent
-# Local functions are PascalCase
-dotnet_naming_rule.local_functions_should_be_pascal_case.severity = suggestion
-dotnet_naming_rule.local_functions_should_be_pascal_case.symbols = local_functions
-dotnet_naming_rule.local_functions_should_be_pascal_case.style = local_function_style
+# Expression-level preferences
+dotnet_style_coalesce_expression = true:suggestion
+dotnet_style_collection_initializer = true:suggestion
+dotnet_style_explicit_tuple_names = true:suggestion
+dotnet_style_namespace_match_folder = true:suggestion
+dotnet_style_null_propagation = true:suggestion
+dotnet_style_object_initializer = true:suggestion
+dotnet_style_operator_placement_when_wrapping = beginning_of_line
+dotnet_style_prefer_auto_properties = true:suggestion
+dotnet_style_prefer_collection_expression = when_types_loosely_match:suggestion
+dotnet_style_prefer_compound_assignment = true:suggestion
+dotnet_style_prefer_conditional_expression_over_assignment = true:suggestion
+dotnet_style_prefer_conditional_expression_over_return = true:suggestion
+dotnet_style_prefer_foreach_explicit_cast_in_source = when_strongly_typed:suggestion
+dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion
+dotnet_style_prefer_inferred_tuple_names = true:suggestion
+dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion
+dotnet_style_prefer_simplified_boolean_expressions = true:suggestion
+dotnet_style_prefer_simplified_interpolation = true:suggestion
+
+# Field preferences
+dotnet_style_readonly_field = true:warning
+
+# Parameter preferences
+dotnet_code_quality_unused_parameters = all:suggestion
+
+# Suppression preferences
+dotnet_remove_unnecessary_suppression_exclusions = none
+
+#### C# Coding Conventions ####
+[*.cs]
-dotnet_naming_symbols.local_functions.applicable_kinds = local_function
+# var preferences
+csharp_style_var_elsewhere = false:silent
+csharp_style_var_for_built_in_types = false:silent
+csharp_style_var_when_type_is_apparent = false:silent
+
+# Expression-bodied members
+csharp_style_expression_bodied_accessors = true:silent
+csharp_style_expression_bodied_constructors = false:silent
+csharp_style_expression_bodied_indexers = true:silent
+csharp_style_expression_bodied_lambdas = true:suggestion
+csharp_style_expression_bodied_local_functions = false:silent
+csharp_style_expression_bodied_methods = false:silent
+csharp_style_expression_bodied_operators = false:silent
+csharp_style_expression_bodied_properties = true:silent
+
+# Pattern matching preferences
+csharp_style_pattern_matching_over_as_with_null_check = true:suggestion
+csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion
+csharp_style_prefer_extended_property_pattern = true:suggestion
+csharp_style_prefer_not_pattern = true:suggestion
+csharp_style_prefer_pattern_matching = true:silent
+csharp_style_prefer_switch_expression = true:suggestion
-dotnet_naming_style.local_function_style.capitalization = pascal_case
+# Null-checking preferences
+csharp_style_conditional_delegate_call = true:suggestion
-# By default, name items with PascalCase
-dotnet_naming_rule.members_should_be_pascal_case.severity = suggestion
-dotnet_naming_rule.members_should_be_pascal_case.symbols = all_members
-dotnet_naming_rule.members_should_be_pascal_case.style = pascal_case_style
+# Modifier preferences
+csharp_prefer_static_anonymous_function = true:suggestion
+csharp_prefer_static_local_function = true:warning
+csharp_preferred_modifier_order = public,private,protected,internal,file,const,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,required,volatile,async:suggestion
+csharp_style_prefer_readonly_struct = true:suggestion
+csharp_style_prefer_readonly_struct_member = true:suggestion
-dotnet_naming_symbols.all_members.applicable_kinds = *
+# Code-block preferences
+csharp_prefer_braces = true:silent
+csharp_prefer_simple_using_statement = true:suggestion
+csharp_style_namespace_declarations = file_scoped:suggestion
+csharp_style_prefer_method_group_conversion = true:silent
+csharp_style_prefer_primary_constructors = true:suggestion
+csharp_style_prefer_top_level_statements = true:silent
+
+# Expression-level preferences
+csharp_prefer_simple_default_expression = true:suggestion
+csharp_style_deconstructed_variable_declaration = true:suggestion
+csharp_style_implicit_object_creation_when_type_is_apparent = true:suggestion
+csharp_style_inlined_variable_declaration = true:suggestion
+csharp_style_prefer_index_operator = true:suggestion
+csharp_style_prefer_local_over_anonymous_function = true:suggestion
+csharp_style_prefer_null_check_over_type_check = true:suggestion
+csharp_style_prefer_range_operator = true:suggestion
+csharp_style_prefer_tuple_swap = true:suggestion
+csharp_style_prefer_utf8_string_literals = true:suggestion
+csharp_style_throw_expression = true:suggestion
+csharp_style_unused_value_assignment_preference = discard_variable:suggestion
+csharp_style_unused_value_expression_statement_preference = discard_variable:silent
-dotnet_naming_style.pascal_case_style.capitalization = pascal_case
+# 'using' directive preferences
+csharp_using_directive_placement = outside_namespace:silent
-# RS0016: Only enable if API files are present
-dotnet_public_api_analyzer.require_api_files = true
+#### C# Formatting Rules ####
-# CSharp code style settings:
-[*.cs]
-# Newline settings
-csharp_new_line_before_open_brace = all
-csharp_new_line_before_else = true
+# New line preferences
csharp_new_line_before_catch = true
+csharp_new_line_before_else = true
csharp_new_line_before_finally = true
-csharp_new_line_before_members_in_object_initializers = true
csharp_new_line_before_members_in_anonymous_types = true
+csharp_new_line_before_members_in_object_initializers = true
+csharp_new_line_before_open_brace = all
csharp_new_line_between_query_expression_clauses = true
# Indentation preferences
@@ -160,38 +165,8 @@ 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
-csharp_indent_labels = flush_left
-
-# Whitespace options
-csharp_style_allow_embedded_statements_on_same_line_experimental = false
-csharp_style_allow_blank_lines_between_consecutive_braces_experimental = false
-csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = false
-csharp_style_allow_blank_line_after_token_in_conditional_expression_experimental = false
-csharp_style_allow_blank_line_after_token_in_arrow_expression_clause_experimental = false
-
-# Prefer "var" everywhere
-csharp_style_var_for_built_in_types = true:suggestion
-csharp_style_var_when_type_is_apparent = true:suggestion
-csharp_style_var_elsewhere = true:suggestion
-
-# Prefer method-like constructs to have a block body
-csharp_style_expression_bodied_methods = false:none
-csharp_style_expression_bodied_constructors = false:none
-csharp_style_expression_bodied_operators = false:none
-
-# Prefer property-like constructs to have an expression-body
-csharp_style_expression_bodied_properties = true:none
-csharp_style_expression_bodied_indexers = true:none
-csharp_style_expression_bodied_accessors = true:none
-
-# Suggest more modern language features when available
-csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion
-csharp_style_pattern_matching_over_as_with_null_check = true:suggestion
-csharp_style_inlined_variable_declaration = true:suggestion
-csharp_style_throw_expression = true:suggestion
-csharp_style_conditional_delegate_call = true:suggestion
-csharp_style_prefer_extended_property_pattern = true:suggestion
# Space preferences
csharp_space_after_cast = false
@@ -201,7 +176,7 @@ 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 = do_not_ignore
+csharp_space_around_declaration_statements = false
csharp_space_before_colon_in_inheritance_clause = true
csharp_space_before_comma = false
csharp_space_before_dot = false
@@ -217,63 +192,197 @@ csharp_space_between_method_declaration_parameter_list_parentheses = false
csharp_space_between_parentheses = false
csharp_space_between_square_brackets = false
-# Blocks are allowed
-csharp_prefer_braces = true:silent
+# Wrapping preferences
csharp_preserve_single_line_blocks = true
csharp_preserve_single_line_statements = true
-# IDE0060: Remove unused parameter
-dotnet_diagnostic.IDE0060.severity = warning
+#### Naming styles ####
+[*.{cs,vb}]
+
+# Naming rules
+
+dotnet_naming_rule.types_and_namespaces_should_be_pascalcase.severity = suggestion
+dotnet_naming_rule.types_and_namespaces_should_be_pascalcase.symbols = types_and_namespaces
+dotnet_naming_rule.types_and_namespaces_should_be_pascalcase.style = pascalcase
+
+dotnet_naming_rule.interfaces_should_be_ipascalcase.severity = suggestion
+dotnet_naming_rule.interfaces_should_be_ipascalcase.symbols = interfaces
+dotnet_naming_rule.interfaces_should_be_ipascalcase.style = ipascalcase
+
+dotnet_naming_rule.type_parameters_should_be_tpascalcase.severity = suggestion
+dotnet_naming_rule.type_parameters_should_be_tpascalcase.symbols = type_parameters
+dotnet_naming_rule.type_parameters_should_be_tpascalcase.style = tpascalcase
+
+dotnet_naming_rule.methods_should_be_pascalcase.severity = suggestion
+dotnet_naming_rule.methods_should_be_pascalcase.symbols = methods
+dotnet_naming_rule.methods_should_be_pascalcase.style = pascalcase
-[src/{Compilers,ExpressionEvaluator,Scripting}/**Test**/*.{cs,vb}]
+dotnet_naming_rule.properties_should_be_pascalcase.severity = suggestion
+dotnet_naming_rule.properties_should_be_pascalcase.symbols = properties
+dotnet_naming_rule.properties_should_be_pascalcase.style = pascalcase
-# IDE0060: Remove unused parameter
-dotnet_diagnostic.IDE0060.severity = none
+dotnet_naming_rule.events_should_be_pascalcase.severity = suggestion
+dotnet_naming_rule.events_should_be_pascalcase.symbols = events
+dotnet_naming_rule.events_should_be_pascalcase.style = pascalcase
-[src/{Analyzers,CodeStyle,Features,Workspaces,EditorFeatures,VisualStudio}/**/*.{cs,vb}]
+dotnet_naming_rule.local_variables_should_be_camelcase.severity = suggestion
+dotnet_naming_rule.local_variables_should_be_camelcase.symbols = local_variables
+dotnet_naming_rule.local_variables_should_be_camelcase.style = camelcase
-# IDE0011: Add braces
-csharp_prefer_braces = when_multiline:warning
-# NOTE: We need the below severity entry for Add Braces due to https://github.com/dotnet/roslyn/issues/44201
-dotnet_diagnostic.IDE0011.severity = warning
+dotnet_naming_rule.local_constants_should_be_camelcase.severity = suggestion
+dotnet_naming_rule.local_constants_should_be_camelcase.symbols = local_constants
+dotnet_naming_rule.local_constants_should_be_camelcase.style = camelcase
-# IDE0040: Add accessibility modifiers
-dotnet_diagnostic.IDE0040.severity = warning
+dotnet_naming_rule.parameters_should_be_camelcase.severity = suggestion
+dotnet_naming_rule.parameters_should_be_camelcase.symbols = parameters
+dotnet_naming_rule.parameters_should_be_camelcase.style = camelcase
-# IDE0052: Remove unread private member
-dotnet_diagnostic.IDE0052.severity = warning
+dotnet_naming_rule.public_fields_should_be_pascalcase.severity = suggestion
+dotnet_naming_rule.public_fields_should_be_pascalcase.symbols = public_fields
+dotnet_naming_rule.public_fields_should_be_pascalcase.style = pascalcase
-# IDE0059: Unnecessary assignment to a value
-dotnet_diagnostic.IDE0059.severity = warning
+dotnet_naming_rule.private_fields_should_be__camelcase.severity = suggestion
+dotnet_naming_rule.private_fields_should_be__camelcase.symbols = private_fields
+dotnet_naming_rule.private_fields_should_be__camelcase.style = _camelcase
-# CA1012: Abstract types should not have public constructors
-dotnet_diagnostic.CA1012.severity = warning
+dotnet_naming_rule.private_static_fields_should_be_s_camelcase.severity = suggestion
+dotnet_naming_rule.private_static_fields_should_be_s_camelcase.symbols = private_static_fields
+dotnet_naming_rule.private_static_fields_should_be_s_camelcase.style = s_camelcase
-# CA1822: Make member static
-dotnet_diagnostic.CA1822.severity = warning
+dotnet_naming_rule.public_constant_fields_should_be_pascalcase.severity = suggestion
+dotnet_naming_rule.public_constant_fields_should_be_pascalcase.symbols = public_constant_fields
+dotnet_naming_rule.public_constant_fields_should_be_pascalcase.style = pascalcase
-# Prefer "var" everywhere
-dotnet_diagnostic.IDE0007.severity = warning
-csharp_style_var_for_built_in_types = true:warning
-csharp_style_var_when_type_is_apparent = true:warning
-csharp_style_var_elsewhere = true:warning
+dotnet_naming_rule.private_constant_fields_should_be_pascalcase.severity = suggestion
+dotnet_naming_rule.private_constant_fields_should_be_pascalcase.symbols = private_constant_fields
+dotnet_naming_rule.private_constant_fields_should_be_pascalcase.style = pascalcase
-# csharp_style_allow_embedded_statements_on_same_line_experimental
-dotnet_diagnostic.IDE2001.severity = warning
+dotnet_naming_rule.public_static_readonly_fields_should_be_pascalcase.severity = suggestion
+dotnet_naming_rule.public_static_readonly_fields_should_be_pascalcase.symbols = public_static_readonly_fields
+dotnet_naming_rule.public_static_readonly_fields_should_be_pascalcase.style = pascalcase
-# csharp_style_allow_blank_lines_between_consecutive_braces_experimental
-dotnet_diagnostic.IDE2002.severity = warning
+dotnet_naming_rule.private_static_readonly_fields_should_be_pascalcase.severity = suggestion
+dotnet_naming_rule.private_static_readonly_fields_should_be_pascalcase.symbols = private_static_readonly_fields
+dotnet_naming_rule.private_static_readonly_fields_should_be_pascalcase.style = pascalcase
-# csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental
-dotnet_diagnostic.IDE2004.severity = warning
+dotnet_naming_rule.enums_should_be_pascalcase.severity = suggestion
+dotnet_naming_rule.enums_should_be_pascalcase.symbols = enums
+dotnet_naming_rule.enums_should_be_pascalcase.style = pascalcase
-# csharp_style_allow_blank_line_after_token_in_conditional_expression_experimental
-dotnet_diagnostic.IDE2005.severity = warning
+dotnet_naming_rule.local_functions_should_be_pascalcase.severity = suggestion
+dotnet_naming_rule.local_functions_should_be_pascalcase.symbols = local_functions
+dotnet_naming_rule.local_functions_should_be_pascalcase.style = pascalcase
-# csharp_style_allow_blank_line_after_token_in_arrow_expression_clause_experimental
-dotnet_diagnostic.IDE2006.severity = warning
+dotnet_naming_rule.non_field_members_should_be_pascalcase.severity = suggestion
+dotnet_naming_rule.non_field_members_should_be_pascalcase.symbols = non_field_members
+dotnet_naming_rule.non_field_members_should_be_pascalcase.style = pascalcase
-[src/{VisualStudio}/**/*.{cs,vb}]
-# CA1822: Make member static
-# There is a risk of accidentally breaking an internal API that partners rely on though IVT.
-dotnet_code_quality.CA1822.api_surface = private
+# Symbol specifications
+
+dotnet_naming_symbols.interfaces.applicable_kinds = interface
+dotnet_naming_symbols.interfaces.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
+dotnet_naming_symbols.interfaces.required_modifiers =
+
+dotnet_naming_symbols.enums.applicable_kinds = enum
+dotnet_naming_symbols.enums.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
+dotnet_naming_symbols.enums.required_modifiers =
+
+dotnet_naming_symbols.events.applicable_kinds = event
+dotnet_naming_symbols.events.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
+dotnet_naming_symbols.events.required_modifiers =
+
+dotnet_naming_symbols.methods.applicable_kinds = method
+dotnet_naming_symbols.methods.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
+dotnet_naming_symbols.methods.required_modifiers =
+
+dotnet_naming_symbols.properties.applicable_kinds = property
+dotnet_naming_symbols.properties.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
+dotnet_naming_symbols.properties.required_modifiers =
+
+dotnet_naming_symbols.public_fields.applicable_kinds = field
+dotnet_naming_symbols.public_fields.applicable_accessibilities = public, internal
+dotnet_naming_symbols.public_fields.required_modifiers =
+
+dotnet_naming_symbols.private_fields.applicable_kinds = field
+dotnet_naming_symbols.private_fields.applicable_accessibilities = private, protected, protected_internal, private_protected
+dotnet_naming_symbols.private_fields.required_modifiers =
+
+dotnet_naming_symbols.private_static_fields.applicable_kinds = field
+dotnet_naming_symbols.private_static_fields.applicable_accessibilities = private, protected, protected_internal, private_protected
+dotnet_naming_symbols.private_static_fields.required_modifiers = static
+
+dotnet_naming_symbols.types_and_namespaces.applicable_kinds = namespace, class, struct, interface, enum
+dotnet_naming_symbols.types_and_namespaces.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
+dotnet_naming_symbols.types_and_namespaces.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 =
+
+dotnet_naming_symbols.type_parameters.applicable_kinds = namespace
+dotnet_naming_symbols.type_parameters.applicable_accessibilities = *
+dotnet_naming_symbols.type_parameters.required_modifiers =
+
+dotnet_naming_symbols.private_constant_fields.applicable_kinds = field
+dotnet_naming_symbols.private_constant_fields.applicable_accessibilities = private, protected, protected_internal, private_protected
+dotnet_naming_symbols.private_constant_fields.required_modifiers = const
+
+dotnet_naming_symbols.local_variables.applicable_kinds = local
+dotnet_naming_symbols.local_variables.applicable_accessibilities = local
+dotnet_naming_symbols.local_variables.required_modifiers =
+
+dotnet_naming_symbols.local_constants.applicable_kinds = local
+dotnet_naming_symbols.local_constants.applicable_accessibilities = local
+dotnet_naming_symbols.local_constants.required_modifiers = const
+
+dotnet_naming_symbols.parameters.applicable_kinds = parameter
+dotnet_naming_symbols.parameters.applicable_accessibilities = *
+dotnet_naming_symbols.parameters.required_modifiers =
+
+dotnet_naming_symbols.public_constant_fields.applicable_kinds = field
+dotnet_naming_symbols.public_constant_fields.applicable_accessibilities = public, internal
+dotnet_naming_symbols.public_constant_fields.required_modifiers = const
+
+dotnet_naming_symbols.public_static_readonly_fields.applicable_kinds = field
+dotnet_naming_symbols.public_static_readonly_fields.applicable_accessibilities = public, internal
+dotnet_naming_symbols.public_static_readonly_fields.required_modifiers = readonly, static
+
+dotnet_naming_symbols.private_static_readonly_fields.applicable_kinds = field
+dotnet_naming_symbols.private_static_readonly_fields.applicable_accessibilities = private, protected, protected_internal, private_protected
+dotnet_naming_symbols.private_static_readonly_fields.required_modifiers = readonly, static
+
+dotnet_naming_symbols.local_functions.applicable_kinds = local_function
+dotnet_naming_symbols.local_functions.applicable_accessibilities = *
+dotnet_naming_symbols.local_functions.required_modifiers =
+
+# Naming styles
+
+dotnet_naming_style.pascalcase.required_prefix =
+dotnet_naming_style.pascalcase.required_suffix =
+dotnet_naming_style.pascalcase.word_separator =
+dotnet_naming_style.pascalcase.capitalization = pascal_case
+
+dotnet_naming_style.ipascalcase.required_prefix = I
+dotnet_naming_style.ipascalcase.required_suffix =
+dotnet_naming_style.ipascalcase.word_separator =
+dotnet_naming_style.ipascalcase.capitalization = pascal_case
+
+dotnet_naming_style.tpascalcase.required_prefix = T
+dotnet_naming_style.tpascalcase.required_suffix =
+dotnet_naming_style.tpascalcase.word_separator =
+dotnet_naming_style.tpascalcase.capitalization = pascal_case
+
+dotnet_naming_style._camelcase.required_prefix = _
+dotnet_naming_style._camelcase.required_suffix =
+dotnet_naming_style._camelcase.word_separator =
+dotnet_naming_style._camelcase.capitalization = camel_case
+
+dotnet_naming_style.camelcase.required_prefix =
+dotnet_naming_style.camelcase.required_suffix =
+dotnet_naming_style.camelcase.word_separator =
+dotnet_naming_style.camelcase.capitalization = camel_case
+
+dotnet_naming_style.s_camelcase.required_prefix = s_
+dotnet_naming_style.s_camelcase.required_suffix =
+dotnet_naming_style.s_camelcase.word_separator =
+dotnet_naming_style.s_camelcase.capitalization = camel_case
\ No newline at end of file
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index ad7cf42..696d1c7 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -19,12 +19,12 @@ jobs:
env:
NUGET_PACKAGES: ${{ github.workspace }}/.nuget/packages
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v6
with:
fetch-depth: 0
- - uses: actions/setup-dotnet@v4
+ - uses: actions/setup-dotnet@v5
with:
- dotnet-version: 9.x
+ dotnet-version: 10.x
cache: true
cache-dependency-path: '**/packages.lock.json'
- run: dotnet restore --locked-mode
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
deleted file mode 100644
index e1fc603..0000000
--- a/.github/workflows/codeql-analysis.yml
+++ /dev/null
@@ -1,74 +0,0 @@
-# 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"
-
-on:
- push:
- branches: [ main ]
- pull_request:
- # The branches below must be a subset of the branches above
- branches: [ main ]
- paths:
- - '**/*.cs'
- schedule:
- - cron: '37 14 * * 6'
-
-jobs:
- analyze:
- name: Analyze
- runs-on: ubuntu-latest
- permissions:
- actions: read
- contents: read
- security-events: write
-
- strategy:
- fail-fast: false
- matrix:
- language: [ 'csharp' ]
- # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
- # Learn more about CodeQL language support at https://git.io/codeql-language-support
-
- steps:
- - name: Checkout repository
- uses: actions/checkout@v4
- with:
- submodules: true
-
- # Initializes the CodeQL tools for scanning.
- - name: Initialize CodeQL
- uses: github/codeql-action/init@v3
- with:
- languages: ${{ matrix.language }}
- # 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.
- # queries: ./path/to/local/query, your-org/your-repo/queries@main
- queries: security-and-quality
- # Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
- # If this step fails, then you should remove it and run the build manually (see below)
- - name: Autobuild
- uses: github/codeql-action/autobuild@v3
-
- # âšī¸ Command-line programs to run using the OS shell.
- # đ https://git.io/JvXDl
-
- # âī¸ If the Autobuild fails above, remove it and uncomment the following three lines
- # and modify them (or add more) to build your code if your project
- # uses a compiled language
-
- #- run: |
- # make bootstrap
- # make release
-
- - name: Perform CodeQL Analysis
- uses: github/codeql-action/analyze@v3
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 80ec6c6..f705fcf 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -1,17 +1,19 @@
+exclude: '(?i)(?:^|[\/])external(?:[\/]|$)'
+
ci:
skip: [ dotnet-format ]
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
- rev: v5.0.0
+ rev: v6.0.0
hooks:
- id: check-added-large-files
- id: check-json
- id: check-merge-conflict
- id: check-toml
- id: check-yaml
- - id: end-of-file-fixer
- id: trailing-whitespace
+ args: [--markdown-linebreak-ext=md]
- id: check-xml
- id: detect-private-key
- id: fix-byte-order-marker
@@ -22,5 +24,5 @@ repos:
- id: dotnet-format
name: dotnet-format
language: system
- entry: dotnet format --severity info --exclude 'external/' --include
+ entry: dotnet format --severity info --include
types_or: [ "c#", "vb" ]
diff --git a/Directory.Packages.props b/Directory.Packages.props
index 12e9d95..3eba1b2 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -1,26 +1,26 @@
true
- NU1507
- 11.2.3
+ 11.3.11
-
-
+
+
-
+
+
-
-
+
+
all
runtime; build; native; contentfiles; analyzers
-
+
all
runtime; build; native; contentfiles; analyzers
@@ -28,9 +28,9 @@
all
runtime; build; native; contentfiles; analyzers
-
-
+
+
-
+
-
+
\ No newline at end of file
diff --git a/SFP.sln b/SFP.sln
deleted file mode 100644
index c21eda1..0000000
--- a/SFP.sln
+++ /dev/null
@@ -1,57 +0,0 @@
-Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio Version 17
-VisualStudioVersion = 17.1.32104.313
-MinimumVisualStudioVersion = 10.0.40219.1
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SFP_UI", "SFP_UI\SFP_UI.csproj", "{55EC0EB9-96FC-4CB5-B8AB-3F4A904AC97C}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SFP", "SFP\SFP.csproj", "{2801B18E-31FC-4B99-AAD7-7259CD197D8D}"
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{50AF4859-D9C1-465A-97E5-66B7B944FE7D}"
- ProjectSection(SolutionItems) = preProject
- .editorconfig = .editorconfig
- Directory.Packages.props = Directory.Packages.props
- SFPCommon.props = SFPCommon.props
- README.md = README.md
- createpublishedzip.ps1 = createpublishedzip.ps1
- .pre-commit-config.yaml = .pre-commit-config.yaml
- EndProjectSection
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{D50C3657-84AF-4631-9291-21EEF38F94B6}"
- ProjectSection(SolutionItems) = preProject
- .github\FUNDING.yml = .github\FUNDING.yml
- .github\dependabot.yml = .github\dependabot.yml
- EndProjectSection
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{DED8434F-5A66-487D-A851-DC06DF32242D}"
- ProjectSection(SolutionItems) = preProject
- .github\workflows\publish.yml = .github\workflows\publish.yml
- .github\workflows\codeql-analysis.yml = .github\workflows\codeql-analysis.yml
- .github\workflows\build.yml = .github\workflows\build.yml
- EndProjectSection
-EndProject
-Global
- GlobalSection(SolutionConfigurationPlatforms) = preSolution
- Debug|Any CPU = Debug|Any CPU
- Release|Any CPU = Release|Any CPU
- EndGlobalSection
- GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {55EC0EB9-96FC-4CB5-B8AB-3F4A904AC97C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {55EC0EB9-96FC-4CB5-B8AB-3F4A904AC97C}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {55EC0EB9-96FC-4CB5-B8AB-3F4A904AC97C}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {55EC0EB9-96FC-4CB5-B8AB-3F4A904AC97C}.Release|Any CPU.Build.0 = Release|Any CPU
- {2801B18E-31FC-4B99-AAD7-7259CD197D8D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {2801B18E-31FC-4B99-AAD7-7259CD197D8D}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {2801B18E-31FC-4B99-AAD7-7259CD197D8D}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {2801B18E-31FC-4B99-AAD7-7259CD197D8D}.Release|Any CPU.Build.0 = Release|Any CPU
- EndGlobalSection
- GlobalSection(SolutionProperties) = preSolution
- HideSolutionNode = FALSE
- EndGlobalSection
- GlobalSection(ExtensibilityGlobals) = postSolution
- SolutionGuid = {6467C57A-09C8-44F5-88AC-95F87E14ED73}
- EndGlobalSection
- GlobalSection(NestedProjects) = preSolution
- {D50C3657-84AF-4631-9291-21EEF38F94B6} = {50AF4859-D9C1-465A-97E5-66B7B944FE7D}
- {DED8434F-5A66-487D-A851-DC06DF32242D} = {D50C3657-84AF-4631-9291-21EEF38F94B6}
- EndGlobalSection
-EndGlobal
diff --git a/SFP.sln.DotSettings b/SFP.sln.DotSettings
deleted file mode 100644
index 88257cc..0000000
--- a/SFP.sln.DotSettings
+++ /dev/null
@@ -1,6 +0,0 @@
-
- True
- True
- True
- True
- True
diff --git a/SFP.slnx b/SFP.slnx
new file mode 100644
index 0000000..2fb4f1a
--- /dev/null
+++ b/SFP.slnx
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/SFP/Models/Injection/BrowserEndpoint.cs b/SFP/Models/Injection/BrowserEndpoint.cs
index afc9174..617444f 100644
--- a/SFP/Models/Injection/BrowserEndpoint.cs
+++ b/SFP/Models/Injection/BrowserEndpoint.cs
@@ -1,8 +1,10 @@
#region
using System.Text.Json.Serialization;
+
using Flurl;
using Flurl.Http;
+
using SFP.Properties;
#endregion
@@ -47,4 +49,4 @@ internal static async Task GetBrowserEndpointAsync()
throw;
}
}
-}
+}
\ No newline at end of file
diff --git a/SFP/Models/Injection/Config/MillenniumConfig.cs b/SFP/Models/Injection/Config/MillenniumConfig.cs
index ccf522e..38c3985 100644
--- a/SFP/Models/Injection/Config/MillenniumConfig.cs
+++ b/SFP/Models/Injection/Config/MillenniumConfig.cs
@@ -26,4 +26,4 @@ public class Patch
public class MillenniumConfig
{
[JsonPropertyName("patch")] public IEnumerable Patch { get; init; } = [];
-}
+}
\ No newline at end of file
diff --git a/SFP/Models/Injection/Config/SfpConfig.cs b/SFP/Models/Injection/Config/SfpConfig.cs
index 4aa9a89..f0c9a08 100644
--- a/SFP/Models/Injection/Config/SfpConfig.cs
+++ b/SFP/Models/Injection/Config/SfpConfig.cs
@@ -1,6 +1,5 @@
#region
-using System.Diagnostics.CodeAnalysis;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Text.RegularExpressions;
@@ -11,21 +10,15 @@ namespace SFP.Models.Injection.Config;
public class PatchEntry : IEquatable
{
- [JsonIgnore] private Regex? _matchRegex;
-
public string MatchRegexString { get; init; } = string.Empty;
public string TargetCss { get; init; } = string.Empty;
public string TargetJs { get; init; } = string.Empty;
- [JsonIgnore] public Regex MatchRegex => _matchRegex ??= new Regex(MatchRegexString, RegexOptions.Compiled);
+ [field: JsonIgnore][JsonIgnore] public Regex MatchRegex => field ??= new Regex(MatchRegexString, RegexOptions.Compiled);
public bool Equals(PatchEntry? entry)
{
- if (entry == null)
- {
- return false;
- }
- return MatchRegexString == entry.MatchRegexString && TargetCss == entry.TargetCss &&
+ return entry != null && MatchRegexString == entry.MatchRegexString && TargetCss == entry.TargetCss &&
TargetJs == entry.TargetJs;
}
@@ -43,75 +36,75 @@ public override int GetHashCode()
public class SfpConfig
{
[JsonIgnore]
- private static readonly IReadOnlyCollection s_defaultPatches =
+ private static readonly IReadOnlyCollection DefaultPatches =
[
- new PatchEntry
+ new()
{
MatchRegexString = "https://.*.steampowered.com", TargetCss = "webkit.css", TargetJs = "webkit.js"
},
- new PatchEntry { MatchRegexString = "https://steamcommunity.com", TargetCss = "webkit.css", TargetJs = "webkit.js" },
- new PatchEntry
+ new() { MatchRegexString = "https://steamcommunity.com", TargetCss = "webkit.css", TargetJs = "webkit.js" },
+ new()
{
MatchRegexString = "^Steam$", TargetCss = "libraryroot.custom.css", TargetJs = "libraryroot.custom.js"
},
- new PatchEntry
+ new()
{
MatchRegexString = "^OverlayBrowser_Browser$",
TargetCss = "libraryroot.custom.css",
TargetJs = "libraryroot.custom.js"
},
- new PatchEntry
+ new()
{
MatchRegexString = "^SP Overlay:", TargetCss = "libraryroot.custom.css", TargetJs = "libraryroot.custom.js"
},
- new PatchEntry
+ new()
{
- MatchRegexString = @"Supernav$", TargetCss = "libraryroot.custom.css", TargetJs = "libraryroot.custom.js"
+ MatchRegexString = "Supernav$", TargetCss = "libraryroot.custom.css", TargetJs = "libraryroot.custom.js"
},
- new PatchEntry
+ new()
{
- MatchRegexString = @"^notificationtoasts_",
+ MatchRegexString = "^notificationtoasts_",
TargetCss = "notifications.custom.css",
TargetJs = "notifications.custom.js"
},
- new PatchEntry
+ new()
{
- MatchRegexString = @"^SteamBrowser_Find$",
+ MatchRegexString = "^SteamBrowser_Find$",
TargetCss = "libraryroot.custom.css",
TargetJs = "libraryroot.custom.js"
},
- new PatchEntry
+ new()
{
MatchRegexString = @"^OverlayTab\d+_Find$",
TargetCss = "libraryroot.custom.css",
TargetJs = "libraryroot.custom.js"
},
- new PatchEntry
+ new()
{
MatchRegexString = "^Steam Big Picture Mode$",
TargetCss = "bigpicture.custom.css",
TargetJs = "bigpicture.custom.js"
},
- new PatchEntry
+ new()
{
MatchRegexString = "^QuickAccess_", TargetCss = "bigpicture.custom.css", TargetJs = "bigpicture.custom.js"
},
- new PatchEntry
+ new()
{
MatchRegexString = "^MainMenu_", TargetCss = "bigpicture.custom.css", TargetJs = "bigpicture.custom.js"
},
// Friends List and Chat
- new PatchEntry
+ new()
{
- MatchRegexString = @".friendsui-container", TargetCss = "friends.custom.css", TargetJs = "friends.custom.js"
+ MatchRegexString = ".friendsui-container", TargetCss = "friends.custom.css", TargetJs = "friends.custom.js"
},
- new PatchEntry { MatchRegexString = "Menu$", TargetCss = "libraryroot.custom.css", TargetJs = "libraryroot.custom.js" },
- new PatchEntry
+ new() { MatchRegexString = "Menu$", TargetCss = "libraryroot.custom.css", TargetJs = "libraryroot.custom.js" },
+ new()
{
// Steam Dialog popups (Settings, Game Properties, etc)
MatchRegexString = ".ModalDialogPopup", TargetCss = "libraryroot.custom.css", TargetJs = "libraryroot.custom.js"
},
- new PatchEntry
+ new()
{
// Sign In Page
MatchRegexString = ".FullModalOverlay", TargetCss = "libraryroot.custom.css", TargetJs = "libraryroot.custom.js"
@@ -120,15 +113,14 @@ public class SfpConfig
[JsonIgnore] private static SfpConfig? s_sfpConfig;
- [JsonIgnore] public bool _isFromMillennium;
+ [JsonIgnore] public bool IsFromMillennium;
public bool UseDefaultPatches { get; init; }
public IEnumerable Patches { get; init; } = GetDefaultPatches();
[JsonIgnore] public static SfpConfig DefaultConfig { get; } = new();
- [ExcludeFromCodeCoverage]
private static IEnumerable GetDefaultPatches()
{
- return s_defaultPatches;
+ return DefaultPatches;
}
public static SfpConfig GetConfig(bool overWrite = false)
@@ -175,7 +167,7 @@ public static SfpConfig GetConfig(bool overWrite = false)
{
json = FromMillenniumConfig(millenniumConfig);
s_sfpConfig = json;
- s_sfpConfig._isFromMillennium = true;
+ s_sfpConfig.IsFromMillennium = true;
Log.Logger.Info("Using config.json from Millennium skin");
}
}
@@ -200,10 +192,10 @@ public static SfpConfig FromMillenniumConfig(MillenniumConfig millenniumConfig)
return new SfpConfig
{
UseDefaultPatches = false,
- Patches = millenniumConfig.Patch
+ Patches = [.. millenniumConfig.Patch
.Where(p => !string.IsNullOrWhiteSpace(p.Url) &&
(!string.IsNullOrWhiteSpace(p.Css) || !string.IsNullOrWhiteSpace(p.Js)))
- .Select(p => new PatchEntry { MatchRegexString = p.Url, TargetCss = p.Css, TargetJs = p.Js }).ToArray()
+ .Select(p => new PatchEntry { MatchRegexString = p.Url, TargetCss = p.Css, TargetJs = p.Js })]
};
}
-}
+}
\ No newline at end of file
diff --git a/SFP/Models/Injection/Injector.cs b/SFP/Models/Injection/Injector.cs
index c96ee74..b71d645 100644
--- a/SFP/Models/Injection/Injector.cs
+++ b/SFP/Models/Injection/Injector.cs
@@ -2,7 +2,9 @@
using System.Text;
using System.Text.RegularExpressions;
+
using PuppeteerSharp;
+
using SFP.Models.Injection.Config;
using SFP.Properties;
@@ -13,10 +15,10 @@ namespace SFP.Models.Injection;
public static partial class Injector
{
private static IBrowser? s_browser;
- private static bool s_isInjected;
private static bool s_manualDisconnect;
- private static readonly SemaphoreSlim s_semaphore = new(1, 1);
- public static bool IsInjected => s_isInjected && s_browser != null;
+ private static readonly SemaphoreSlim Semaphore = new(1, 1);
+ private static bool s_webkitReloaded;
+ public static bool IsInjected { get => field && s_browser != null; private set; }
public static event EventHandler? InjectionStateChanged;
@@ -33,7 +35,7 @@ public static partial class Injector
"SystemAccentColorDark3"
];
- public static string ColorsCss { get; private set; } = string.Empty;
+ private static string ColorsCss { get; set; } = string.Empty;
public static async Task StartInjectionAsync(bool noError = false)
{
@@ -43,7 +45,7 @@ public static async Task StartInjectionAsync(bool noError = false)
return;
}
- if (!await s_semaphore.WaitAsync(TimeSpan.Zero))
+ if (!await Semaphore.WaitAsync(TimeSpan.Zero))
{
Log.Logger.Warn("Injection already in progress, skipping injection");
return;
@@ -68,17 +70,18 @@ public static async Task StartInjectionAsync(bool noError = false)
BrowserWSEndpoint = browserEndpoint,
DefaultViewport = null,
EnqueueAsyncMessages = true,
- EnqueueTransportMessages = true,
+ EnqueueTransportMessages = true
};
Log.Logger.Info("Connecting to " + browserEndpoint);
+ s_webkitReloaded = false;
s_browser = await Puppeteer.ConnectAsync(options);
s_browser.Disconnected += OnDisconnected;
Log.Logger.Info("Connected");
s_browser.TargetCreated += Browser_TargetUpdate;
s_browser.TargetChanged += Browser_TargetUpdate;
await InjectAsync();
- s_isInjected = true;
+ IsInjected = true;
InjectionStateChanged?.Invoke(null, EventArgs.Empty);
Log.Logger.Info("Initial injection finished");
}
@@ -94,7 +97,7 @@ public static async Task StartInjectionAsync(bool noError = false)
}
finally
{
- s_semaphore.Release();
+ Semaphore.Release();
}
}
@@ -121,15 +124,16 @@ public static void StopInjection()
{
Log.Logger.Info("Disconnecting from Steam instance");
}
- s_isInjected = false;
+ IsInjected = false;
s_manualDisconnect = true;
s_browser?.Disconnect();
s_browser = null;
+ s_webkitReloaded = false;
InjectionStateChanged?.Invoke(null, EventArgs.Empty);
}
// injection after reload occurs before content is fully loaded, needs investigation
- public static async void Reload()
+ public static async Task Reload()
{
if (s_browser == null)
{
@@ -156,27 +160,39 @@ public static async void Reload()
}
}
+#pragma warning disable EPC27
private static async void OnDisconnected(object? sender, EventArgs e)
+#pragma warning restore EPC27
{
- Log.Logger.Info("Disconnected from Steam instance");
- var manualDisconnect = s_manualDisconnect;
- StopInjection();
- if (manualDisconnect)
+ try
{
- s_manualDisconnect = false;
- return;
- }
+ Log.Logger.Info("Disconnected from Steam instance");
+ var manualDisconnect = s_manualDisconnect;
+ StopInjection();
+ if (manualDisconnect)
+ {
+ s_manualDisconnect = false;
+ return;
+ }
- await Task.Delay(500);
- if (!Steam.IsSteamWebHelperRunning)
+ await Task.Delay(500);
+ if (!Steam.IsSteamWebHelperRunning)
+ {
+ return;
+ }
+ Log.Logger.Warn("Unexpected disconnect, trying to reconnect to Steam instance");
+ await Steam.TryInject();
+ }
+ catch (Exception ex)
{
- return;
+ Log.Logger.Error("Error in OnDisconnected event handler");
+ Log.Logger.Debug(ex);
}
- Log.Logger.Warn("Unexpected disconnect, trying to reconnect to Steam instance");
- await Steam.TryInject();
}
+#pragma warning disable EPC27
private static async void Browser_TargetUpdate(object? sender, TargetChangedArgs e)
+#pragma warning restore EPC27
{
try
{
@@ -193,6 +209,11 @@ private static async void Browser_TargetUpdate(object? sender, TargetChangedArgs
Log.Logger.Warn("Puppeteer exception when trying to get page");
Log.Logger.Debug(err);
}
+ catch (Exception err)
+ {
+ Log.Logger.Error("Unexpected error in Browser_TargetUpdate event handler");
+ Log.Logger.Debug(err);
+ }
}
private static async Task ProcessPage(IPage? page)
@@ -217,7 +238,7 @@ private static async Task ProcessPage(IPage? page)
private static async Task ProcessFrame(IFrame frame)
{
var config = SfpConfig.GetConfig();
- var patches = config.Patches as PatchEntry[] ?? config.Patches.ToArray();
+ var patches = config.Patches as PatchEntry[] ?? [.. config.Patches];
if (!IsFrameWebkit(frame))
{
@@ -269,7 +290,7 @@ private static async Task ProcessFrame(IFrame frame)
}
else
{
- switch (config._isFromMillennium)
+ switch (config.IsFromMillennium)
{
case false when patch.MatchRegex.IsMatch(title):
case true when regex == title:
@@ -286,10 +307,10 @@ private static async Task ProcessFrame(IFrame frame)
await SetBypassCsp(frame);
var url = GetDomainRegex().Match(frame.Url).Groups[1].Value;
await DumpFrame(frame, url);
- if (!config._isFromMillennium)
+ if (!config.IsFromMillennium)
{
var httpPatches = patches.Where(p => p.MatchRegexString.StartsWith("http", StringComparison.CurrentCultureIgnoreCase));
- var patchEntries = httpPatches as PatchEntry[] ?? httpPatches.ToArray();
+ var patchEntries = httpPatches as PatchEntry[] ?? [.. httpPatches];
var patch = patchEntries.FirstOrDefault(p => p.MatchRegex.IsMatch(frame.Url));
if (patch != null)
{
@@ -360,9 +381,19 @@ private static async Task SetBypassCsp(IFrame frame)
}
}
+#pragma warning disable EPC27
private static async void Frame_Navigate(object? sender, FrameEventArgs e)
+#pragma warning restore EPC27
{
- await ProcessFrame(e.Frame);
+ try
+ {
+ await ProcessFrame(e.Frame);
+ }
+ catch (Exception ex)
+ {
+ Log.Logger.Error("Error in Frame_Navigate event handler");
+ Log.Logger.Debug(ex);
+ }
}
private static async Task InjectAsync(IFrame frame, PatchEntry patch, string tabFriendlyName)
@@ -404,27 +435,36 @@ private static async Task InjectResourceAsync(IFrame frame, string fileRelativeP
fileRelativePath = $"{relativeSkinDir}{fileRelativePath}";
var injectString =
- $@"function inject() {{
- if (document.getElementById('{frame.Id}{resourceType}') !== null) return;
- const element = document.createElement('{(resourceType == "css" ? "link" : "script")}');
- element.id = '{frame.Id}{resourceType}';
- {(resourceType == "css" ? "element.rel = 'stylesheet';" : "")}
- element.type = '{(resourceType == "css" ? "text/css" : "module")}';
- element.{(resourceType == "css" ? "href" : "src")} = 'https://steamloopback.host/{fileRelativePath}';
- document.head.append(element);
- }}
- if ((document.readyState === 'loading') && '{IsFrameWebkit(frame)}' === 'True') {{
- addEventListener('DOMContentLoaded', inject);
- }} else {{
- inject();
- }}
- ";
+ $$"""
+ function inject() {
+ if ('{{IsFrameWebkit(frame)}}' === 'True' && '{{s_webkitReloaded}}' === 'False') {
+ location.reload();
+ }
+ if (document.getElementById('{{frame.Id}}{{resourceType}}') !== null) return;
+ const element = document.createElement('{{(resourceType == "css" ? "link" : "script")}}');
+ element.id = '{{frame.Id}}{{resourceType}}';
+ {{(resourceType == "css" ? "element.rel = 'stylesheet';" : "")}}
+ element.type = '{{(resourceType == "css" ? "text/css" : "module")}}';
+ element.{{(resourceType == "css" ? "href" : "src")}} = 'https://steamloopback.host/{{fileRelativePath}}';
+ document.head.append(element);
+ }
+ if ((document.readyState === 'loading') && '{{IsFrameWebkit(frame)}}' === 'True') {
+ addEventListener('DOMContentLoaded', inject);
+ } else {
+ inject();
+ }
+ """;
try
{
if (!IsFrameWebkit(frame) && resourceType == "js")
{
await Task.Delay(500);
}
+
+ if (!s_webkitReloaded && IsFrameWebkit(frame))
+ {
+ s_webkitReloaded = true;
+ }
await frame.EvaluateExpressionAsync(injectString);
Log.Logger.Info($"Injected {Path.GetFileName(fileRelativePath)} into {tabFriendlyName} from patch {patchName}");
}
@@ -455,7 +495,7 @@ private static async Task UpdateColorInPage(IPage page)
}
}
- public static async void UpdateColorScheme(string? colorScheme = null)
+ public static async Task UpdateColorScheme(string? colorScheme = null)
{
if (s_browser == null || !Settings.Default.UseAppTheme && colorScheme == null)
{
@@ -483,7 +523,7 @@ public static void SetColorScheme(string themeVariant)
public static void SetAccentColors(IEnumerable colors)
{
- var colorsArr = colors as string[] ?? colors.ToArray();
+ var colorsArr = colors as string[] ?? [.. colors];
var colorsCss = new StringBuilder();
colorsCss.Append(":root { ");
for (var i = 0; i < 7; i++)
@@ -494,7 +534,7 @@ public static void SetAccentColors(IEnumerable colors)
ColorsCss = colorsCss.ToString();
}
- public static async void UpdateSystemAccentColors(bool useAccentColors = true)
+ public static async Task UpdateSystemAccentColors(bool useAccentColors = true)
{
if (s_browser == null || !Settings.Default.UseAppTheme && useAccentColors)
{
@@ -507,18 +547,20 @@ public static async void UpdateSystemAccentColors(bool useAccentColors = true)
: pages.Select(async page =>
{
var injectString =
- $@"function injectAcc() {{
- var element = document.getElementById('SystemAccentColorInjection');
- if (element) {{
- element.parentNode.removeChild(element);
- }}
- }}
- if ((document.readyState === 'loading') && '{IsFrameWebkit(page.MainFrame)}' === 'True') {{
- addEventListener('DOMContentLoaded', injectAcc);
- }} else {{
- injectAcc();
- }}
- ";
+ $$"""
+ function injectAcc() {
+ var element = document.getElementById('SystemAccentColorInjection');
+ if (element) {
+ element.parentNode.removeChild(element);
+ }
+ }
+ if ((document.readyState === 'loading') && '{{IsFrameWebkit(page.MainFrame)}}' === 'True') {
+ addEventListener('DOMContentLoaded', injectAcc);
+ } else {
+ injectAcc();
+ }
+
+ """;
await page.EvaluateExpressionAsync(injectString);
});
await Task.WhenAll(processTasks);
@@ -527,25 +569,27 @@ public static async void UpdateSystemAccentColors(bool useAccentColors = true)
private static async Task UpdateSystemAccentColorsInPage(IPage page)
{
var injectString =
- $@"function injectAcc() {{
- var element = document.getElementById('SystemAccentColorInjection');
- if (element) {{
- element.parentNode.removeChild(element);
- }}
- element = document.createElement('style');
- element.id = 'SystemAccentColorInjection';
- element.innerHTML = `{ColorsCss}`;
- document.head.append(element);
- }}
- if ((document.readyState === 'loading') && '{IsFrameWebkit(page.MainFrame)}' === 'True') {{
- addEventListener('DOMContentLoaded', injectAcc);
- }} else {{
- injectAcc();
- }}
- ";
+ $$"""
+ function injectAcc() {
+ var element = document.getElementById('SystemAccentColorInjection');
+ if (element) {
+ element.parentNode.removeChild(element);
+ }
+ element = document.createElement('style');
+ element.id = 'SystemAccentColorInjection';
+ element.innerHTML = `{{ColorsCss}}`;
+ document.head.append(element);
+ }
+ if ((document.readyState === 'loading') && '{{IsFrameWebkit(page.MainFrame)}}' === 'True') {
+ addEventListener('DOMContentLoaded', injectAcc);
+ } else {
+ injectAcc();
+ }
+
+ """;
await page.EvaluateExpressionAsync(injectString);
}
[GeneratedRegex(@"^(?:https?:\/\/)?(?:[^@\/\n]+@)?(?:www\.)?([^:\/?\n]+)")]
private static partial Regex GetDomainRegex();
-}
+}
\ No newline at end of file
diff --git a/SFP/Models/Log.cs b/SFP/Models/Log.cs
index 76e51d4..1777e10 100644
--- a/SFP/Models/Log.cs
+++ b/SFP/Models/Log.cs
@@ -9,4 +9,4 @@ namespace SFP.Models;
public static class Log
{
public static readonly Logger Logger = LogManager.GetCurrentClassLogger();
-}
+}
\ No newline at end of file
diff --git a/SFP/Models/Steam.cs b/SFP/Models/Steam.cs
index 3bef3ad..1740100 100644
--- a/SFP/Models/Steam.cs
+++ b/SFP/Models/Steam.cs
@@ -3,7 +3,9 @@
using System.ComponentModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
+
using FileWatcherEx;
+
using SFP.Models.Injection;
using SFP.Properties;
@@ -16,19 +18,18 @@ public static class Steam
private static FileSystemWatcherEx? s_watcher;
private static Process? s_steamProcess;
private static bool s_injectOnce;
- private static readonly SemaphoreSlim s_semaphore = new(1, 1);
+ private static readonly SemaphoreSlim Semaphore = new(1, 1);
- private static readonly int s_processAmount = OperatingSystem.IsWindows() ? 3 : OperatingSystem.IsMacOS() ? 4 : 6;
- public static bool IsSteamWebHelperRunning => SteamWebHelperProcesses.Length > s_processAmount;
+ private static readonly int ProcessAmount = OperatingSystem.IsWindows() ? 3 : OperatingSystem.IsMacOS() ? 4 : 6;
+ public static bool IsSteamWebHelperRunning => SteamWebHelperProcesses.Length > ProcessAmount;
public static bool IsSteamRunning => SteamProcess is not null;
- private static Process[] SteamWebHelperProcesses => Process.GetProcessesByName(SteamWebHelperProcName)
- .Where(p => p.ProcessName.Equals(SteamWebHelperProcName, StringComparison.OrdinalIgnoreCase)).ToArray();
+ private static Process[] SteamWebHelperProcesses => [.. Process.GetProcessesByName(SteamWebHelperProcName).Where(p => p.ProcessName.Equals(SteamWebHelperProcName, StringComparison.OrdinalIgnoreCase))];
private static Process? SteamProcess => Process.GetProcessesByName(SteamProcName)
.FirstOrDefault(p => p.ProcessName.Equals(SteamProcName, StringComparison.OrdinalIgnoreCase));
- private static string SteamWebHelperProcName => OperatingSystem.IsMacOS() ? "Steam Helper" : @"steamwebhelper";
+ private static string SteamWebHelperProcName => OperatingSystem.IsMacOS() ? "Steam Helper" : "steamwebhelper";
private static string SteamProcName => OperatingSystem.IsMacOS() ? "steam_osx" : "steam";
@@ -54,17 +55,11 @@ public static class Steam
private static string? GetSteamRootDir()
{
- if (OperatingSystem.IsWindows())
- {
- return SteamDir;
- }
-
- if (OperatingSystem.IsLinux())
- {
- return Path.Join(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".steam");
- }
-
- return OperatingSystem.IsMacOS()
+ return OperatingSystem.IsWindows()
+ ? SteamDir
+ : OperatingSystem.IsLinux()
+ ? Path.Join(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".steam")
+ : OperatingSystem.IsMacOS()
? Path.Join(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "Library",
"Application Support", "Steam")
: null;
@@ -73,17 +68,11 @@ public static class Steam
private static string? GetSteamDir()
{
var dir = Settings.Default.SteamDirectory;
- if (!string.IsNullOrWhiteSpace(dir) && Directory.Exists(dir))
- {
- return dir;
- }
-
- if (OperatingSystem.IsWindows())
- {
- return GetRegistryData(@"SOFTWARE\Valve\Steam", "SteamPath")?.ToString()?.Replace(@"/", @"\");
- }
-
- return OperatingSystem.IsLinux()
+ return !string.IsNullOrWhiteSpace(dir) && Directory.Exists(dir)
+ ? dir
+ : OperatingSystem.IsWindows()
+ ? (GetRegistryData(@"SOFTWARE\Valve\Steam", "SteamPath")?.ToString()?.Replace("/", @"\"))
+ : OperatingSystem.IsLinux()
? Path.Join(SteamRootDir, "steam")
:
// OSX
@@ -204,7 +193,7 @@ public static Task StartSteam(string? args = null)
return Task.CompletedTask;
}
- public static async void RunRestartSteam()
+ public static async Task RunRestartSteam()
{
await Task.Run(() => RestartSteam());
}
@@ -234,11 +223,21 @@ private static async Task RestartSteam(string? args = null)
}
}
+#pragma warning disable EPC27
private static async void OnSteamExited(object? sender, EventArgs e)
+#pragma warning restore EPC27
{
- s_steamProcess?.Dispose();
- s_steamProcess = null;
- await StartSteam();
+ try
+ {
+ s_steamProcess?.Dispose();
+ s_steamProcess = null;
+ await StartSteam();
+ }
+ catch (Exception ex)
+ {
+ Log.Logger.Error("Error in OnSteamExited event handler");
+ Log.Logger.Debug(ex);
+ }
}
public static void StartMonitorSteam()
@@ -272,15 +271,25 @@ public static void StartMonitorSteam()
}
}
+#pragma warning disable EPC27
private static async void OnCrashFileCreated(object? sender, FileChangedEvent e)
+#pragma warning restore EPC27
{
- SteamStarted?.Invoke(null, EventArgs.Empty);
- if (!Settings.Default.InjectOnSteamStart && !s_injectOnce)
+ try
{
- return;
+ SteamStarted?.Invoke(null, EventArgs.Empty);
+ if (!Settings.Default.InjectOnSteamStart && !s_injectOnce)
+ {
+ return;
+ }
+ s_injectOnce = false;
+ await TryInject();
+ }
+ catch (Exception ex)
+ {
+ Log.Logger.Error("Error in OnCrashFileCreated event handler");
+ Log.Logger.Debug(ex);
}
- s_injectOnce = false;
- await TryInject();
}
private static void OnCrashFileDeleted(object? sender, FileChangedEvent e)
@@ -293,7 +302,7 @@ public static List GetCommandLine()
return Utils.GetCommandLine(SteamProcess);
}
- public static async void RunTryInject()
+ public static async Task RunTryInject()
{
Log.Logger.Info("Starting injection...");
await Task.Run(TryInject);
@@ -301,7 +310,7 @@ public static async void RunTryInject()
public static async Task TryInject()
{
- if (!await s_semaphore.WaitAsync(TimeSpan.Zero))
+ if (!await Semaphore.WaitAsync(TimeSpan.Zero))
{
Log.Logger.Warn("Injection already in progress");
return;
@@ -345,7 +354,7 @@ public static async Task TryInject()
}
finally
{
- s_semaphore.Release();
+ Semaphore.Release();
}
}
@@ -388,26 +397,26 @@ private static async Task CheckForMissingArgumentsAsync()
private static void AppendArgs(ref string args)
{
- const string DebuggingString = @"-cef-enable-debugging";
- const string BootstrapString = @"-skipinitialbootstrap";
- const string PortString = @"-devtools-port";
+ const string debuggingString = "-cef-enable-debugging";
+ const string bootstrapString = "-skipinitialbootstrap";
+ const string portString = "-devtools-port";
var argsList = args.Split(' ', StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries).ToList();
- if (!argsList.Contains(DebuggingString))
+ if (!argsList.Contains(debuggingString))
{
- argsList.Add(DebuggingString);
+ argsList.Add(debuggingString);
}
- if (OperatingSystem.IsMacOS() && !argsList.Contains(BootstrapString))
+ if (OperatingSystem.IsMacOS() && !argsList.Contains(bootstrapString))
{
- argsList.Add(BootstrapString);
+ argsList.Add(bootstrapString);
}
- if (!argsList.Contains(PortString))
+ if (!argsList.Contains(portString))
{
- argsList.Add(PortString + " " + Settings.Default.SteamCefPort);
+ argsList.Add(portString + " " + Settings.Default.SteamCefPort);
}
args = string.Join(" ", argsList);
}
-}
+}
\ No newline at end of file
diff --git a/SFP/Models/Unix/Utils.cs b/SFP/Models/Unix/Utils.cs
index 4600cc3..32d3b20 100644
--- a/SFP/Models/Unix/Utils.cs
+++ b/SFP/Models/Unix/Utils.cs
@@ -50,4 +50,4 @@ private static string RunCommand(string command)
return output;
}
-}
+}
\ No newline at end of file
diff --git a/SFP/Models/Utils.cs b/SFP/Models/Utils.cs
index 62c1cbc..c09dd70 100644
--- a/SFP/Models/Utils.cs
+++ b/SFP/Models/Utils.cs
@@ -25,7 +25,9 @@ public static void OpenUrl(string url)
return;
}
- url = $@"""{url}""";
+ url = $"""
+ "{url}"
+ """;
if (OperatingSystem.IsLinux())
{
@@ -45,29 +47,24 @@ public static void OpenUrl(string url)
public static List GetCommandLine(Process? process)
{
- if (process == null)
+ if (process != null)
{
- Log.Logger.Warn("Could not get command line, process does not exist.");
- return [];
- }
-
- if (OperatingSystem.IsWindows())
- {
- return Windows.Utils.GetCommandLine(process);
- }
-
- if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS())
- {
- return Unix.Utils.GetCommandLine(process);
+ return OperatingSystem.IsWindows()
+ ? Windows.Utils.GetCommandLine(process)
+ : OperatingSystem.IsLinux() || OperatingSystem.IsMacOS()
+ ? Unix.Utils.GetCommandLine(process)
+ : [];
}
+ Log.Logger.Warn("Could not get command line, process does not exist.");
return [];
+
}
// ReSharper disable once InconsistentNaming
public static string ConvertARGBtoRGBA(string argb)
{
- if (!argb.StartsWith("#"))
+ if (!argb.StartsWith('#'))
{
var color = Color.FromName(argb);
if (color is { A: 0, R: 0, G: 0, B: 0 })
@@ -89,4 +86,4 @@ public static string ConvertARGBtoRGBA(string argb)
return "#" + rgb + alpha;
}
-}
+}
\ No newline at end of file
diff --git a/SFP/Models/Windows/Utils.cs b/SFP/Models/Windows/Utils.cs
index d0ad2df..0e0ab69 100644
--- a/SFP/Models/Windows/Utils.cs
+++ b/SFP/Models/Windows/Utils.cs
@@ -2,8 +2,11 @@
using System.Diagnostics;
using System.Runtime.Versioning;
+
using Microsoft.Win32;
+
using WindowsShortcutFactory;
+
using WmiLight;
#endregion
@@ -36,7 +39,8 @@ public static void SetAppRunOnLaunch(bool runOnLaunch)
return;
}
- using WindowsShortcut shortcut = new() { Path = processPath };
+ using WindowsShortcut shortcut = new();
+ shortcut.Path = processPath;
shortcut.Save(shortcutAddress);
}
@@ -62,4 +66,4 @@ public static List GetCommandLine(Process process)
return value;
}
-}
+}
\ No newline at end of file
diff --git a/SFP/packages.lock.json b/SFP/packages.lock.json
index 433a5d2..a323590 100644
--- a/SFP/packages.lock.json
+++ b/SFP/packages.lock.json
@@ -1,18 +1,18 @@
{
"version": 2,
"dependencies": {
- "net8.0": {
+ "net10.0": {
"BepInEx.AssemblyPublicizer.MSBuild": {
"type": "Direct",
- "requested": "[0.4.2, )",
- "resolved": "0.4.2",
- "contentHash": "AwGR6zOQxhWPWmcMdmhCaawC2qYdYHttkGmlXxyvhB1EIUAtuILEle8kKSkzElnbFGZjvw6iabpFi+AYUG4IuA=="
+ "requested": "[0.4.3, )",
+ "resolved": "0.4.3",
+ "contentHash": "Z8Qs3JTTHZG1q0DCqY+HFJ8xp92tkEXWyQ7roeWgUvn20tjv0ZNfAdyy13XrRS1jeEoN/weJc81747TbvvwaDg=="
},
"ErrorProne.NET.CoreAnalyzers": {
"type": "Direct",
- "requested": "[0.7.0-beta.1, )",
- "resolved": "0.7.0-beta.1",
- "contentHash": "c+Tcp88i2Z+nrUYqEEHroE3tyj57n6F9CB4DDRsPJatWn2RNB2mAmAVVXVg8PcE7QPlUCHD2QURJzKluDQlW5Q=="
+ "requested": "[0.8.2-beta.1, )",
+ "resolved": "0.8.2-beta.1",
+ "contentHash": "8zTBD0fGHSZzwUCqm7b9DykXDBWA8swQ71mB9Jli2n8MYThLrqnJL9xK3rNol21oGFn0q/JfaGYJbSheTXqeYA=="
},
"ErrorProne.NET.Structs": {
"type": "Direct",
@@ -22,9 +22,9 @@
},
"FileWatcherEx": {
"type": "Direct",
- "requested": "[2.6.0, )",
- "resolved": "2.6.0",
- "contentHash": "zHDfVCmqguDYzcRhUmsAzUVBMm5G3nvkD5+p3/f5TjqPHcxFvDoEfSKvxZ4/Mid6fVDZJOU+7xxittk+AIE2Bw=="
+ "requested": "[2.7.0, )",
+ "resolved": "2.7.0",
+ "contentHash": "gw4isJWR2mr6UDSHUOGY3lR1fh4OsSRbJOwQz9Wz2V1JMQ6I8OyHB1CG3ekgrTnj+Ik4uXdMzmlPdOfXq2FApQ=="
},
"Flurl.Http": {
"type": "Direct",
@@ -37,15 +37,15 @@
},
"MinVer": {
"type": "Direct",
- "requested": "[6.0.0, )",
- "resolved": "6.0.0",
- "contentHash": "+/SsmiySsXJlvQLCGBqaZKNVt3s/Y/HbAdwtop7Km2CnuZbaScoqkWJEBQ5Cy9ebkn6kCYKrHsXgwrFdTgcb3g=="
+ "requested": "[7.0.0, )",
+ "resolved": "7.0.0",
+ "contentHash": "2lMTCQl5bGP4iv0JNkockPnyllC6eHLz+CoK2ICvalvHod+exXSxueu9hq+zNkU7bZBJf8wMfeRC/Edn8AGmEg=="
},
"NLog": {
"type": "Direct",
- "requested": "[5.3.4, )",
- "resolved": "5.3.4",
- "contentHash": "gLy7+O1hEYJXIlcTr1/VWjGXrZTQFZzYNO18IWasD64pNwz0BreV+nHLxWKXWZzERRzoKnsk2XYtwLkTVk7J1A=="
+ "requested": "[6.1.0, )",
+ "resolved": "6.1.0",
+ "contentHash": "K4UdxS4fuJeCM0sm+JTXWoJgOtcXX7X5kvE94bR07UB8JOB6A2+ski7q1tf2qdv+N0H3gsoWtteScJCHa3+zpw=="
},
"PortableJsonSettingsProvider": {
"type": "Direct",
@@ -67,8 +67,7 @@
"Microsoft.Bcl.AsyncInterfaces": "1.1.0",
"Microsoft.Extensions.Logging": "2.0.2",
"Newtonsoft.Json": "13.0.1",
- "SharpZipLib": "1.3.3",
- "System.Text.Encodings.Web": "6.0.0"
+ "SharpZipLib": "1.3.3"
}
},
"WindowsShortcutFactory": {
@@ -79,9 +78,9 @@
},
"WmiLight": {
"type": "Direct",
- "requested": "[6.9.1, )",
- "resolved": "6.9.1",
- "contentHash": "TDQm/8wtenGNZw4NzSdOZuo+ox32Ix6/f1mxIaYEBaTJ+h8RjRhCQeAXpM0dXCOPTYKnlPpbuDZ5VeCL/hxiDA=="
+ "requested": "[7.1.0, )",
+ "resolved": "7.1.0",
+ "contentHash": "kWwFsCm9Xu68hhmpI+2BXGPvMoXqNMok0d74GE1td4AEzas+J3k9dFOhtksZ4J5egVZeebXbp5zYMujzPslW6A=="
},
"Flurl": {
"type": "Transitive",
@@ -93,8 +92,7 @@
"resolved": "2.2.0",
"contentHash": "9ErxAAKaDzxXASB/b5uLEkLgUWv1QbeVxyJYEHQwMaxXOeFFVkQxiq8RyfVcifLU7NR0QY0p3acqx4ZpYfhHDg==",
"dependencies": {
- "Microsoft.Net.Http.Headers": "2.2.0",
- "System.Text.Encodings.Web": "4.5.0"
+ "Microsoft.Net.Http.Headers": "2.2.0"
}
},
"Microsoft.Bcl.AsyncInterfaces": {
@@ -134,19 +132,14 @@
"Microsoft.Extensions.Primitives": {
"type": "Transitive",
"resolved": "2.2.0",
- "contentHash": "azyQtqbm4fSaDzZHD/J+V6oWMFaf2tWP4WEGIYePLCMw3+b2RQdj9ybgbQyjCshcitQKQ4lEDOZjmSlTTrHxUg==",
- "dependencies": {
- "System.Memory": "4.5.1",
- "System.Runtime.CompilerServices.Unsafe": "4.5.1"
- }
+ "contentHash": "azyQtqbm4fSaDzZHD/J+V6oWMFaf2tWP4WEGIYePLCMw3+b2RQdj9ybgbQyjCshcitQKQ4lEDOZjmSlTTrHxUg=="
},
"Microsoft.Net.Http.Headers": {
"type": "Transitive",
"resolved": "2.2.0",
"contentHash": "iZNkjYqlo8sIOI0bQfpsSoMTmB/kyvmV2h225ihyZT33aTp48ZpF6qYnXxzSXmHt8DpBAwBTX+1s1UFLbYfZKg==",
"dependencies": {
- "Microsoft.Extensions.Primitives": "2.2.0",
- "System.Buffers": "4.5.0"
+ "Microsoft.Extensions.Primitives": "2.2.0"
}
},
"Microsoft.Win32.SystemEvents": {
@@ -164,11 +157,6 @@
"resolved": "1.3.3",
"contentHash": "N8+hwhsKZm25tDJfWpBSW7EGhH/R7EMuiX+KJ4C4u+fCWVc1lJ5zg1u3S1RPPVYgTqhx/C3hxrqUpi6RwK5+Tg=="
},
- "System.Buffers": {
- "type": "Transitive",
- "resolved": "4.5.0",
- "contentHash": "pL2ChpaRRWI/p4LXyy4RgeWlYF2sgfj/pnVMvBqwNFr5cXg7CXNnWZWxrOONLg8VGdFB8oB+EG2Qw4MLgTOe+A=="
- },
"System.Configuration.ConfigurationManager": {
"type": "Transitive",
"resolved": "7.0.0",
@@ -192,16 +180,6 @@
"Microsoft.Win32.SystemEvents": "7.0.0"
}
},
- "System.Memory": {
- "type": "Transitive",
- "resolved": "4.5.1",
- "contentHash": "sDJYJpGtTgx+23Ayu5euxG5mAXWdkDb4+b0rD0Cab0M1oQS9H0HXGPriKcqpXuiJDTV7fTp/d+fMDJmnr6sNvA=="
- },
- "System.Runtime.CompilerServices.Unsafe": {
- "type": "Transitive",
- "resolved": "6.0.0",
- "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg=="
- },
"System.Security.Cryptography.ProtectedData": {
"type": "Transitive",
"resolved": "7.0.0",
@@ -215,14 +193,6 @@
"System.Windows.Extensions": "7.0.0"
}
},
- "System.Text.Encodings.Web": {
- "type": "Transitive",
- "resolved": "6.0.0",
- "contentHash": "Vg8eB5Tawm1IFqj4TVK1czJX89rhFxJo9ELqc/Eiq0eXy13RK00eubyU6TJE6y+GQXjyV5gSfiewDUZjQgSE0w==",
- "dependencies": {
- "System.Runtime.CompilerServices.Unsafe": "6.0.0"
- }
- },
"System.Windows.Extensions": {
"type": "Transitive",
"resolved": "7.0.0",
@@ -232,12 +202,12 @@
}
}
},
- "net8.0/linux-x64": {
+ "net10.0/linux-x64": {
"WmiLight": {
"type": "Direct",
- "requested": "[6.9.1, )",
- "resolved": "6.9.1",
- "contentHash": "TDQm/8wtenGNZw4NzSdOZuo+ox32Ix6/f1mxIaYEBaTJ+h8RjRhCQeAXpM0dXCOPTYKnlPpbuDZ5VeCL/hxiDA=="
+ "requested": "[7.1.0, )",
+ "resolved": "7.1.0",
+ "contentHash": "kWwFsCm9Xu68hhmpI+2BXGPvMoXqNMok0d74GE1td4AEzas+J3k9dFOhtksZ4J5egVZeebXbp5zYMujzPslW6A=="
},
"Microsoft.Win32.SystemEvents": {
"type": "Transitive",
@@ -262,14 +232,6 @@
"resolved": "7.0.0",
"contentHash": "xSPiLNlHT6wAHtugASbKAJwV5GVqQK351crnILAucUioFqqieDN79evO1rku1ckt/GfjIn+b17UaSskoY03JuA=="
},
- "System.Text.Encodings.Web": {
- "type": "Transitive",
- "resolved": "6.0.0",
- "contentHash": "Vg8eB5Tawm1IFqj4TVK1czJX89rhFxJo9ELqc/Eiq0eXy13RK00eubyU6TJE6y+GQXjyV5gSfiewDUZjQgSE0w==",
- "dependencies": {
- "System.Runtime.CompilerServices.Unsafe": "6.0.0"
- }
- },
"System.Windows.Extensions": {
"type": "Transitive",
"resolved": "7.0.0",
@@ -279,12 +241,12 @@
}
}
},
- "net8.0/osx-x64": {
+ "net10.0/osx-x64": {
"WmiLight": {
"type": "Direct",
- "requested": "[6.9.1, )",
- "resolved": "6.9.1",
- "contentHash": "TDQm/8wtenGNZw4NzSdOZuo+ox32Ix6/f1mxIaYEBaTJ+h8RjRhCQeAXpM0dXCOPTYKnlPpbuDZ5VeCL/hxiDA=="
+ "requested": "[7.1.0, )",
+ "resolved": "7.1.0",
+ "contentHash": "kWwFsCm9Xu68hhmpI+2BXGPvMoXqNMok0d74GE1td4AEzas+J3k9dFOhtksZ4J5egVZeebXbp5zYMujzPslW6A=="
},
"Microsoft.Win32.SystemEvents": {
"type": "Transitive",
@@ -309,14 +271,6 @@
"resolved": "7.0.0",
"contentHash": "xSPiLNlHT6wAHtugASbKAJwV5GVqQK351crnILAucUioFqqieDN79evO1rku1ckt/GfjIn+b17UaSskoY03JuA=="
},
- "System.Text.Encodings.Web": {
- "type": "Transitive",
- "resolved": "6.0.0",
- "contentHash": "Vg8eB5Tawm1IFqj4TVK1czJX89rhFxJo9ELqc/Eiq0eXy13RK00eubyU6TJE6y+GQXjyV5gSfiewDUZjQgSE0w==",
- "dependencies": {
- "System.Runtime.CompilerServices.Unsafe": "6.0.0"
- }
- },
"System.Windows.Extensions": {
"type": "Transitive",
"resolved": "7.0.0",
@@ -326,12 +280,12 @@
}
}
},
- "net8.0/win-x64": {
+ "net10.0/win-x64": {
"WmiLight": {
"type": "Direct",
- "requested": "[6.9.1, )",
- "resolved": "6.9.1",
- "contentHash": "TDQm/8wtenGNZw4NzSdOZuo+ox32Ix6/f1mxIaYEBaTJ+h8RjRhCQeAXpM0dXCOPTYKnlPpbuDZ5VeCL/hxiDA=="
+ "requested": "[7.1.0, )",
+ "resolved": "7.1.0",
+ "contentHash": "kWwFsCm9Xu68hhmpI+2BXGPvMoXqNMok0d74GE1td4AEzas+J3k9dFOhtksZ4J5egVZeebXbp5zYMujzPslW6A=="
},
"Microsoft.Win32.SystemEvents": {
"type": "Transitive",
@@ -356,14 +310,6 @@
"resolved": "7.0.0",
"contentHash": "xSPiLNlHT6wAHtugASbKAJwV5GVqQK351crnILAucUioFqqieDN79evO1rku1ckt/GfjIn+b17UaSskoY03JuA=="
},
- "System.Text.Encodings.Web": {
- "type": "Transitive",
- "resolved": "6.0.0",
- "contentHash": "Vg8eB5Tawm1IFqj4TVK1czJX89rhFxJo9ELqc/Eiq0eXy13RK00eubyU6TJE6y+GQXjyV5gSfiewDUZjQgSE0w==",
- "dependencies": {
- "System.Runtime.CompilerServices.Unsafe": "6.0.0"
- }
- },
"System.Windows.Extensions": {
"type": "Transitive",
"resolved": "7.0.0",
@@ -374,4 +320,4 @@
}
}
}
-}
+}
\ No newline at end of file
diff --git a/SFPCommon.props b/SFPCommon.props
index 644fbf1..4b147b5 100644
--- a/SFPCommon.props
+++ b/SFPCommon.props
@@ -1,12 +1,10 @@
- net8.0
+ net10.0
win-x64;linux-x64;osx-x64
enable
enable
-
-
- false
+ true
true
embedded
diff --git a/SFP_UI/App.axaml b/SFP_UI/App.axaml
index 8348c8f..f977d1b 100644
--- a/SFP_UI/App.axaml
+++ b/SFP_UI/App.axaml
@@ -26,18 +26,18 @@
+ Command="{Binding RunInjectCommand}"/>
+ Command="{Binding RunSteamCommand}"/>
+ Command="{Binding ShowSettingsCommand}"/>
-
+ Command="{Binding ShowWindowCommand}"/>
+
-
+
\ No newline at end of file
diff --git a/SFP_UI/App.axaml.cs b/SFP_UI/App.axaml.cs
index f9033f5..6075f6c 100644
--- a/SFP_UI/App.axaml.cs
+++ b/SFP_UI/App.axaml.cs
@@ -1,16 +1,20 @@
#region
-using System.Diagnostics.CodeAnalysis;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Markup.Xaml;
using Avalonia.Styling;
using Avalonia.Threading;
+
using FluentAvalonia.Styling;
+
+using JetBrains.Annotations;
+
using SFP.Models;
using SFP.Models.Injection;
using SFP.Properties;
+
using SFP_UI.Models;
using SFP_UI.ViewModels;
using SFP_UI.Views;
@@ -31,51 +35,53 @@ public override void Initialize()
AvaloniaXamlLoader.Load(this);
}
+#pragma warning disable EPC27
public override async void OnFrameworkInitializationCompleted()
+#pragma warning restore EPC27
{
- if (!Settings.Default.StartMinimized || !Settings.Default.MinimizeToTray)
+ try
{
- StartMainWindow();
- }
+ if (!Settings.Default.StartMinimized || !Settings.Default.MinimizeToTray)
+ {
+ StartMainWindow();
+ }
- base.OnFrameworkInitializationCompleted();
+ base.OnFrameworkInitializationCompleted();
- if (Design.IsDesignMode)
- {
- return;
- }
+ if (Design.IsDesignMode)
+ {
+ return;
+ }
- SetIconsState(Settings.Default.ShowTrayIcon);
+ SetIconsState(Settings.Default.ShowTrayIcon);
- Injector.SetColorScheme(ActualThemeVariant.ToString());
- Injector.SetAccentColors(GetColorValues());
- ActualThemeVariantChanged += (_, _) =>
- {
Injector.SetColorScheme(ActualThemeVariant.ToString());
- Injector.UpdateColorScheme();
- };
- if (Current?.PlatformSettings != null)
- {
- Current.PlatformSettings.ColorValuesChanged += (_, _) =>
+ Injector.SetAccentColors(GetColorValues());
+ ActualThemeVariantChanged += (_, _) => _ = OnActualThemeVariantChangedAsync();
+ if (Current?.PlatformSettings != null)
{
- Injector.SetAccentColors(GetColorValues());
- Injector.UpdateSystemAccentColors();
- };
- }
- else
- {
- Log.Logger.Warn("PlatformSettings is null, can't update system accent colors");
- }
+ Current.PlatformSettings.ColorValuesChanged += (_, _) => _ = OnColorValuesChangedAsync();
+ }
+ else
+ {
+ Log.Logger.Warn("PlatformSettings is null, can't update system accent colors");
+ }
- await HandleStartupTasks();
+ await HandleStartupTasks();
- if (Settings.Default.CheckForUpdates)
+ if (Settings.Default.CheckForUpdates)
+ {
+ Dispatcher.UIThread.Post(() => _ = UpdateChecker.CheckForUpdates());
+ }
+ }
+ catch (Exception ex)
{
- Dispatcher.UIThread.Post(() => _ = UpdateChecker.CheckForUpdates());
+ Log.Logger.Error("Error in OnFrameworkInitializationCompleted event handler");
+ Log.Logger.Debug(ex);
}
}
- private static IEnumerable GetColorValues()
+ private static string[] GetColorValues()
{
if (Current!.Styles[0] is not FluentAvaloniaTheme faTheme)
{
@@ -129,10 +135,7 @@ public static void SetIconsState(bool state)
public static void SetApplicationTheme(string themeVariantString)
{
var faTheme = Current?.Styles.OfType().FirstOrDefault();
- if (faTheme != null)
- {
- faTheme.PreferSystemTheme = themeVariantString == "System Default";
- }
+ faTheme?.PreferSystemTheme = themeVariantString == "System Default";
Current!.RequestedThemeVariant = themeVariantString switch
{
@@ -166,9 +169,41 @@ public static void QuitApplication()
}
}
- [SuppressMessage("ReSharper", "UnusedParameter.Local")]
+ [UsedImplicitly]
private void TrayIcon_OnClicked(object? sender, EventArgs e)
{
MainWindow.ShowWindow();
}
-}
+
+ private static async Task OnActualThemeVariantChangedAsync()
+ {
+ try
+ {
+ Injector.SetColorScheme(Current!.ActualThemeVariant.ToString());
+ await Injector.UpdateColorScheme();
+ }
+ catch (Exception ex)
+ {
+ Log.Logger.Error("Error updating color scheme on theme variant changed");
+ Log.Logger.Debug(ex);
+ }
+ }
+
+ private static async Task OnColorValuesChangedAsync()
+ {
+ try
+ {
+ if (Current == null)
+ {
+ return;
+ }
+ Injector.SetAccentColors(GetColorValues());
+ await Injector.UpdateSystemAccentColors();
+ }
+ catch (Exception ex)
+ {
+ Log.Logger.Error("Error updating system accent colors");
+ Log.Logger.Debug(ex);
+ }
+ }
+}
\ No newline at end of file
diff --git a/SFP_UI/FodyWeavers.xml b/SFP_UI/FodyWeavers.xml
deleted file mode 100644
index e344bfb..0000000
--- a/SFP_UI/FodyWeavers.xml
+++ /dev/null
@@ -1,3 +0,0 @@
-
-
-
diff --git a/SFP_UI/Models/OpenFileModel.cs b/SFP_UI/Models/OpenFileModel.cs
index 1daa2db..81bb6b3 100644
--- a/SFP_UI/Models/OpenFileModel.cs
+++ b/SFP_UI/Models/OpenFileModel.cs
@@ -1,14 +1,54 @@
-using System.Reactive;
using Avalonia.Controls;
-using ReactiveUI;
+
+using ReactiveUI.SourceGenerators;
+
using SFP.Models;
using SFP.Models.Injection.Config;
using SFP.Properties;
namespace SFP_UI.Models;
-public class OpenFileModel
+public partial class FileCommands
{
+ [ReactiveCommand]
+ private static async Task OpenFile(string relativeFilePath) => await OpenPath(relativeFilePath, false);
+
+ [ReactiveCommand]
+ private static void OpenDir(string relativeDirPath) => OpenPath(relativeDirPath, true).Wait();
+
+ private static async Task OpenPath(string path, bool isDirectory)
+ {
+ try
+ {
+ if (isDirectory)
+ {
+ if (!Directory.Exists(path))
+ {
+ Directory.CreateDirectory(path);
+ }
+ }
+ else
+ {
+ if (!File.Exists(path))
+ {
+ await File.Create(path).DisposeAsync();
+ }
+ }
+
+ Utils.OpenUrl(path);
+ }
+ catch (Exception e)
+ {
+ Log.Logger.Warn("Could not open " + path);
+ Log.Logger.Debug(e);
+ }
+ }
+}
+
+public abstract class OpenFileModel
+{
+ private static readonly FileCommands FileCommands = new();
+
public static void PopulateOpenFileDropDownButton(object itemsCollection)
{
@@ -36,86 +76,43 @@ public static void PopulateOpenFileDropDownButton(object itemsCollection)
case IList nativeMenuItems:
{
nativeMenuItems.Clear();
- nativeMenuItems.Add(new NativeMenuItem { Header = "steamui/skins/", Command = OpenDir, CommandParameter = Steam.SkinsDir });
- nativeMenuItems.Add(new NativeMenuItem { Header = "Active Skin: " + Settings.Default.SelectedSkin + '/', Command = OpenDir, CommandParameter = Steam.SkinDir });
+ nativeMenuItems.Add(new NativeMenuItem { Header = "steamui/skins/", Command = FileCommands.OpenDirCommand, CommandParameter = Steam.SkinsDir });
+ nativeMenuItems.Add(new NativeMenuItem { Header = "Active Skin: " + Settings.Default.SelectedSkin + '/', Command = FileCommands.OpenDirCommand, CommandParameter = Steam.SkinDir });
nativeMenuItems.Add(new NativeMenuItemSeparator());
foreach (var cssFile in targetCssFiles)
{
- nativeMenuItems.Add(new NativeMenuItem { Header = Path.GetFileName(cssFile), Command = OpenFile, CommandParameter = Path.Join(skinDir, cssFile) });
+ nativeMenuItems.Add(new NativeMenuItem { Header = Path.GetFileName(cssFile), Command = FileCommands.OpenFileCommand, CommandParameter = Path.Join(skinDir, cssFile) });
}
nativeMenuItems.Add(new NativeMenuItemSeparator());
foreach (var jsFile in targetJsFiles)
{
- nativeMenuItems.Add(new NativeMenuItem { Header = Path.GetFileName(jsFile), Command = OpenFile, CommandParameter = Path.Join(skinDir, jsFile) });
+ nativeMenuItems.Add(new NativeMenuItem { Header = Path.GetFileName(jsFile), Command = FileCommands.OpenFileCommand, CommandParameter = Path.Join(skinDir, jsFile) });
}
break;
}
case ItemCollection avaloniaItems:
{
avaloniaItems.Clear();
- avaloniaItems.Add(new MenuItem { Header = "steamui/skins/", Command = OpenDir, CommandParameter = Steam.SkinsDir });
- avaloniaItems.Add(new MenuItem { Header = "Active Skin: " + Settings.Default.SelectedSkin + '/', Command = OpenDir, CommandParameter = Steam.SkinDir });
+ avaloniaItems.Add(new MenuItem { Header = "steamui/skins/", Command = FileCommands.OpenDirCommand, CommandParameter = Steam.SkinsDir });
+ avaloniaItems.Add(new MenuItem { Header = "Active Skin: " + Settings.Default.SelectedSkin + '/', Command = FileCommands.OpenDirCommand, CommandParameter = Steam.SkinDir });
avaloniaItems.Add(new Separator());
foreach (var cssFile in targetCssFiles)
{
- avaloniaItems.Add(new MenuItem { Header = Path.GetFileName(cssFile), Command = OpenFile, CommandParameter = Path.Join(skinDir, cssFile) });
+ avaloniaItems.Add(new MenuItem { Header = Path.GetFileName(cssFile), Command = FileCommands.OpenFileCommand, CommandParameter = Path.Join(skinDir, cssFile) });
}
avaloniaItems.Add(new Separator());
foreach (var jsFile in targetJsFiles)
{
- avaloniaItems.Add(new MenuItem { Header = Path.GetFileName(jsFile), Command = OpenFile, CommandParameter = Path.Join(skinDir, jsFile) });
+ avaloniaItems.Add(new MenuItem { Header = Path.GetFileName(jsFile), Command = FileCommands.OpenFileCommand, CommandParameter = Path.Join(skinDir, jsFile) });
}
break;
}
}
}
-
- private static ReactiveCommand OpenFile { get; } = ReactiveCommand.CreateFromTask(OpenFileImpl);
- private static ReactiveCommand OpenDir { get; } = ReactiveCommand.Create(OpenDirImpl);
-
-
- private static async Task OpenPath(string path, bool isDirectory)
- {
- try
- {
- if (isDirectory)
- {
- if (!Directory.Exists(path))
- {
- Directory.CreateDirectory(path);
- }
- }
- else
- {
- if (!File.Exists(path))
- {
- await File.Create(path).DisposeAsync();
- }
- }
-
- Utils.OpenUrl(path);
- }
- catch (Exception e)
- {
- Log.Logger.Warn("Could not open " + path);
- Log.Logger.Debug(e);
- }
- }
-
- private static async Task OpenFileImpl(string relativeFilePath)
- {
- await OpenPath(relativeFilePath, false);
- }
-
- private static void OpenDirImpl(string relativeDirPath)
- {
- OpenPath(relativeDirPath, true).Wait();
- }
-
-}
+}
\ No newline at end of file
diff --git a/SFP_UI/Models/UpdateChecker.cs b/SFP_UI/Models/UpdateChecker.cs
index 97949fd..3005a41 100644
--- a/SFP_UI/Models/UpdateChecker.cs
+++ b/SFP_UI/Models/UpdateChecker.cs
@@ -2,9 +2,15 @@
using System.Reflection;
using System.Text.Json.Serialization;
+
+using JetBrains.Annotations;
+
using Semver;
+
using SFP_UI.ViewModels;
+
using SFP.Models;
+
#if RELEASE
using System.Net.Http.Headers;
using Flurl.Http;
@@ -12,6 +18,8 @@
#if DEBUG
using System.Text.Json;
// ReSharper disable ConditionalAccessQualifierIsNonNullableAccordingToAPIContract
+// ReSharper disable HeuristicUnreachableCode
+// ReSharper disable UnusedParameter.Local
#endif
#endregion
@@ -85,8 +93,7 @@ private static async Task GetLatestVersionAsync(bool preRelease)
}
}
-internal struct Release
+internal readonly struct Release
{
- // ReSharper disable once UnusedAutoPropertyAccessor.Global
- [JsonPropertyName("tag_name")] public string TagName { get; set; }
-}
+ [JsonPropertyName("tag_name")] public string TagName { get; [UsedImplicitly] init; }
+}
\ No newline at end of file
diff --git a/SFP_UI/Pages/MainPage.axaml b/SFP_UI/Pages/MainPage.axaml
index c7a48b7..bc1d86f 100644
--- a/SFP_UI/Pages/MainPage.axaml
+++ b/SFP_UI/Pages/MainPage.axaml
@@ -11,7 +11,7 @@
CloseButtonContent="Dismiss"
PreferredPlacement="Center"
Name="UpdateNotification"
- ActionButtonCommand="{Binding UpdateNotificationView}"
+ ActionButtonCommand="{Binding UpdateNotificationViewCommand}"
ActionButtonCommandParameter="https://github.com/PhantomGamers/SFP/releases"
Content="{Binding UpdateNotificationContent}"
IsOpen="{Binding UpdateNotificationIsOpen}" />
@@ -21,7 +21,7 @@