From a3cc9f5f72e5e6c45227b3007d7aa740e50de6f7 Mon Sep 17 00:00:00 2001 From: Patrik Svensson Date: Mon, 5 Aug 2024 21:20:18 +0200 Subject: [PATCH] Initial commits --- .editorconfig | 178 ++++++ .gitignore | 93 +++ CODE_OF_CONDUCT.md | 132 +++++ LICENSE.md | 21 + NuGet.Config | 6 + README.md | 21 + build.cake | 36 ++ dotnet-tools.json | 18 + examples/Cli/Delegates/BarSettings.cs | 15 + examples/Cli/Delegates/Delegates.csproj | 17 + examples/Cli/Delegates/Program.cs | 61 ++ .../Demo/Commands/Add/AddPackageCommand.cs | 46 ++ .../Demo/Commands/Add/AddReferenceCommand.cs | 29 + examples/Cli/Demo/Commands/Add/AddSettings.cs | 11 + examples/Cli/Demo/Commands/Run/RunCommand.cs | 69 +++ .../Cli/Demo/Commands/Serve/ServeCommand.cs | 40 ++ examples/Cli/Demo/Demo.csproj | 17 + examples/Cli/Demo/Program.cs | 39 ++ examples/Cli/Demo/Utilities/SettingsDumper.cs | 28 + examples/Cli/Demo/Verbosity.cs | 53 ++ examples/Cli/Dynamic/Dynamic.csproj | 17 + examples/Cli/Dynamic/MyCommand.cs | 20 + examples/Cli/Dynamic/Program.cs | 23 + examples/Cli/Help/CustomHelpProvider.cs | 30 + examples/Cli/Help/DefaultCommand.cs | 20 + examples/Cli/Help/Help.csproj | 18 + examples/Cli/Help/Program.cs | 23 + .../Cli/Injection/Commands/DefaultCommand.cs | 29 + examples/Cli/Injection/IGreeter.cs | 16 + .../Injection/Infrastructure/TypeRegistrar.cs | 40 ++ .../Injection/Infrastructure/TypeResolver.cs | 32 ++ examples/Cli/Injection/Injection.csproj | 21 + examples/Cli/Injection/Program.cs | 23 + examples/Cli/Logging/Commands/HelloCommand.cs | 34 ++ .../Logging/Commands/LogCommandSettings.cs | 55 ++ .../Logging/Infrastructure/LogInterceptor.cs | 19 + .../Logging/Infrastructure/LoggingEnricher.cs | 37 ++ .../Logging/Infrastructure/TypeRegistrar.cs | 40 ++ .../Logging/Infrastructure/TypeResolver.cs | 24 + examples/Cli/Logging/Logging.csproj | 26 + examples/Cli/Logging/Program.cs | 54 ++ .../AlternateScreen/AlternateScreen.csproj | 15 + examples/Console/AlternateScreen/Program.cs | 26 + examples/Console/Borders/Borders.csproj | 15 + examples/Console/Borders/Program.cs | 86 +++ examples/Console/Calendars/Calendars.csproj | 15 + examples/Console/Calendars/Program.cs | 18 + examples/Console/Canvas/Canvas.csproj | 21 + examples/Console/Canvas/Program.cs | 43 ++ examples/Console/Canvas/cake.png | Bin 0 -> 52832 bytes examples/Console/Charts/Charts.csproj | 15 + examples/Console/Charts/Program.cs | 42 ++ examples/Console/Colors/Colors.csproj | 15 + examples/Console/Colors/Program.cs | 105 ++++ examples/Console/Columns/Columns.csproj | 19 + examples/Console/Columns/Program.cs | 30 + examples/Console/Columns/User.cs | 88 +++ examples/Console/Cursor/Cursor.csproj | 15 + examples/Console/Cursor/Program.cs | 19 + .../Console/Decorations/Decorations.csproj | 15 + examples/Console/Decorations/Program.cs | 29 + examples/Console/Emojis/Emojis.csproj | 15 + examples/Console/Emojis/Program.cs | 23 + examples/Console/Exceptions/Exceptions.csproj | 15 + examples/Console/Exceptions/Program.cs | 101 ++++ examples/Console/Figlet/Figlet.csproj | 15 + examples/Console/Figlet/Program.cs | 13 + examples/Console/Grids/Grids.csproj | 15 + examples/Console/Grids/Program.cs | 23 + examples/Console/Info/Info.csproj | 15 + examples/Console/Info/Program.cs | 33 ++ examples/Console/Json/Json.csproj | 15 + examples/Console/Json/Program.cs | 36 ++ examples/Console/Layout/Layout.csproj | 15 + examples/Console/Layout/Program.cs | 60 ++ examples/Console/Links/Links.csproj | 15 + examples/Console/Links/Program.cs | 20 + examples/Console/Live/Live.csproj | 15 + examples/Console/Live/Program.cs | 81 +++ examples/Console/LiveTable/LiveTable.csproj | 15 + examples/Console/LiveTable/Program.cs | 86 +++ examples/Console/Minimal/GlobalUsings.cs | 2 + examples/Console/Minimal/Minimal.csproj | 16 + examples/Console/Minimal/Program.cs | 12 + examples/Console/Panels/Panels.csproj | 15 + examples/Console/Panels/Program.cs | 41 ++ examples/Console/Paths/Paths.csproj | 15 + examples/Console/Paths/Program.cs | 67 +++ .../Console/Progress/DescriptionGenerator.cs | 44 ++ examples/Console/Progress/Program.cs | 121 ++++ examples/Console/Progress/Progress.csproj | 19 + examples/Console/Prompt/Program.cs | 182 ++++++ examples/Console/Prompt/Prompt.csproj | 16 + examples/Console/Rules/Program.cs | 42 ++ examples/Console/Rules/Rules.csproj | 15 + .../Console/Showcase/ExceptionGenerator.cs | 29 + examples/Console/Showcase/Program.cs | 154 +++++ examples/Console/Showcase/Showcase.csproj | 15 + examples/Console/Status/Program.cs | 69 +++ examples/Console/Status/Status.csproj | 19 + examples/Console/Tables/Program.cs | 46 ++ examples/Console/Tables/Tables.csproj | 15 + examples/Console/Trees/Program.cs | 44 ++ examples/Console/Trees/Trees.csproj | 15 + examples/Directory.Build.props | 5 + examples/Examples.sln | 540 ++++++++++++++++++ examples/Shared/ColorBox.cs | 123 ++++ examples/Shared/Extensions/ColorExtensions.cs | 14 + examples/Shared/Shared.csproj | 16 + global.json | 7 + 110 files changed, 4506 insertions(+) create mode 100644 .editorconfig create mode 100644 .gitignore create mode 100644 CODE_OF_CONDUCT.md create mode 100644 LICENSE.md create mode 100644 NuGet.Config create mode 100644 README.md create mode 100644 build.cake create mode 100644 dotnet-tools.json create mode 100644 examples/Cli/Delegates/BarSettings.cs create mode 100644 examples/Cli/Delegates/Delegates.csproj create mode 100644 examples/Cli/Delegates/Program.cs create mode 100644 examples/Cli/Demo/Commands/Add/AddPackageCommand.cs create mode 100644 examples/Cli/Demo/Commands/Add/AddReferenceCommand.cs create mode 100644 examples/Cli/Demo/Commands/Add/AddSettings.cs create mode 100644 examples/Cli/Demo/Commands/Run/RunCommand.cs create mode 100644 examples/Cli/Demo/Commands/Serve/ServeCommand.cs create mode 100644 examples/Cli/Demo/Demo.csproj create mode 100644 examples/Cli/Demo/Program.cs create mode 100644 examples/Cli/Demo/Utilities/SettingsDumper.cs create mode 100644 examples/Cli/Demo/Verbosity.cs create mode 100644 examples/Cli/Dynamic/Dynamic.csproj create mode 100644 examples/Cli/Dynamic/MyCommand.cs create mode 100644 examples/Cli/Dynamic/Program.cs create mode 100644 examples/Cli/Help/CustomHelpProvider.cs create mode 100644 examples/Cli/Help/DefaultCommand.cs create mode 100644 examples/Cli/Help/Help.csproj create mode 100644 examples/Cli/Help/Program.cs create mode 100644 examples/Cli/Injection/Commands/DefaultCommand.cs create mode 100644 examples/Cli/Injection/IGreeter.cs create mode 100644 examples/Cli/Injection/Infrastructure/TypeRegistrar.cs create mode 100644 examples/Cli/Injection/Infrastructure/TypeResolver.cs create mode 100644 examples/Cli/Injection/Injection.csproj create mode 100644 examples/Cli/Injection/Program.cs create mode 100644 examples/Cli/Logging/Commands/HelloCommand.cs create mode 100644 examples/Cli/Logging/Commands/LogCommandSettings.cs create mode 100644 examples/Cli/Logging/Infrastructure/LogInterceptor.cs create mode 100644 examples/Cli/Logging/Infrastructure/LoggingEnricher.cs create mode 100644 examples/Cli/Logging/Infrastructure/TypeRegistrar.cs create mode 100644 examples/Cli/Logging/Infrastructure/TypeResolver.cs create mode 100644 examples/Cli/Logging/Logging.csproj create mode 100644 examples/Cli/Logging/Program.cs create mode 100644 examples/Console/AlternateScreen/AlternateScreen.csproj create mode 100644 examples/Console/AlternateScreen/Program.cs create mode 100644 examples/Console/Borders/Borders.csproj create mode 100644 examples/Console/Borders/Program.cs create mode 100644 examples/Console/Calendars/Calendars.csproj create mode 100644 examples/Console/Calendars/Program.cs create mode 100644 examples/Console/Canvas/Canvas.csproj create mode 100644 examples/Console/Canvas/Program.cs create mode 100644 examples/Console/Canvas/cake.png create mode 100644 examples/Console/Charts/Charts.csproj create mode 100644 examples/Console/Charts/Program.cs create mode 100644 examples/Console/Colors/Colors.csproj create mode 100644 examples/Console/Colors/Program.cs create mode 100644 examples/Console/Columns/Columns.csproj create mode 100644 examples/Console/Columns/Program.cs create mode 100644 examples/Console/Columns/User.cs create mode 100644 examples/Console/Cursor/Cursor.csproj create mode 100644 examples/Console/Cursor/Program.cs create mode 100644 examples/Console/Decorations/Decorations.csproj create mode 100644 examples/Console/Decorations/Program.cs create mode 100644 examples/Console/Emojis/Emojis.csproj create mode 100644 examples/Console/Emojis/Program.cs create mode 100644 examples/Console/Exceptions/Exceptions.csproj create mode 100644 examples/Console/Exceptions/Program.cs create mode 100644 examples/Console/Figlet/Figlet.csproj create mode 100644 examples/Console/Figlet/Program.cs create mode 100644 examples/Console/Grids/Grids.csproj create mode 100644 examples/Console/Grids/Program.cs create mode 100644 examples/Console/Info/Info.csproj create mode 100644 examples/Console/Info/Program.cs create mode 100644 examples/Console/Json/Json.csproj create mode 100644 examples/Console/Json/Program.cs create mode 100644 examples/Console/Layout/Layout.csproj create mode 100644 examples/Console/Layout/Program.cs create mode 100644 examples/Console/Links/Links.csproj create mode 100644 examples/Console/Links/Program.cs create mode 100644 examples/Console/Live/Live.csproj create mode 100644 examples/Console/Live/Program.cs create mode 100644 examples/Console/LiveTable/LiveTable.csproj create mode 100644 examples/Console/LiveTable/Program.cs create mode 100644 examples/Console/Minimal/GlobalUsings.cs create mode 100644 examples/Console/Minimal/Minimal.csproj create mode 100644 examples/Console/Minimal/Program.cs create mode 100644 examples/Console/Panels/Panels.csproj create mode 100644 examples/Console/Panels/Program.cs create mode 100644 examples/Console/Paths/Paths.csproj create mode 100644 examples/Console/Paths/Program.cs create mode 100644 examples/Console/Progress/DescriptionGenerator.cs create mode 100644 examples/Console/Progress/Program.cs create mode 100644 examples/Console/Progress/Progress.csproj create mode 100644 examples/Console/Prompt/Program.cs create mode 100644 examples/Console/Prompt/Prompt.csproj create mode 100644 examples/Console/Rules/Program.cs create mode 100644 examples/Console/Rules/Rules.csproj create mode 100644 examples/Console/Showcase/ExceptionGenerator.cs create mode 100644 examples/Console/Showcase/Program.cs create mode 100644 examples/Console/Showcase/Showcase.csproj create mode 100644 examples/Console/Status/Program.cs create mode 100644 examples/Console/Status/Status.csproj create mode 100644 examples/Console/Tables/Program.cs create mode 100644 examples/Console/Tables/Tables.csproj create mode 100644 examples/Console/Trees/Program.cs create mode 100644 examples/Console/Trees/Trees.csproj create mode 100644 examples/Directory.Build.props create mode 100644 examples/Examples.sln create mode 100644 examples/Shared/ColorBox.cs create mode 100644 examples/Shared/Extensions/ColorExtensions.cs create mode 100644 examples/Shared/Shared.csproj create mode 100644 global.json diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..2d99c04 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,178 @@ +root = true + +[*] +charset = utf-8 +end_of_line = LF +indent_style = space +indent_size = 4 +insert_final_newline = false +trim_trailing_whitespace = true + +[*.sln] +indent_style = tab + +[*.{csproj,vbproj,vcxproj,vcxproj.filters}] +indent_size = 2 + +[*.{xml,config,props,targets,nuspec,ruleset}] +indent_size = 2 + +[*.{yml,yaml}] +indent_size = 2 + +[*.json] +indent_size = 2 + +[*.md] +trim_trailing_whitespace = false + +[*.cs] +# Prefer file scoped namespace declarations +csharp_style_namespace_declarations = file_scoped:warning + +# 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 + +# 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 + +# 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 +dotnet_naming_symbols.instance_fields.applicable_kinds = field +dotnet_naming_style.instance_field_style.capitalization = camel_case +dotnet_naming_style.instance_field_style.required_prefix = _ + +# 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 +dotnet_naming_symbols.locals_and_parameters.applicable_kinds = parameter, local +dotnet_naming_style.camel_case_style.capitalization = camel_case + +# 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 +dotnet_naming_symbols.local_functions.applicable_kinds = local_function +dotnet_naming_style.local_function_style.capitalization = pascal_case + +# 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 +dotnet_naming_symbols.all_members.applicable_kinds = * +dotnet_naming_style.pascal_case_style.capitalization = pascal_case + +# Newline settings +csharp_new_line_before_open_brace = all +csharp_new_line_before_else = true +csharp_new_line_before_catch = 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_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_switch_labels = true +csharp_indent_labels = flush_left + +# 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 + +# Space preferences +csharp_space_after_cast = false +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 = do_not_ignore +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 + +# Blocks are allowed +csharp_prefer_braces = true:silent +csharp_preserve_single_line_blocks = true +csharp_preserve_single_line_statements = true + +# warning RS0037: PublicAPI.txt is missing '#nullable enable' +dotnet_diagnostic.RS0037.severity = none \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..664051d --- /dev/null +++ b/.gitignore @@ -0,0 +1,93 @@ +# Misc folders +[Bb]in/ +[Oo]bj/ +[Tt]emp/ +[Pp]ackages/ +/.artifacts/ +/[Tt]ools/ +.idea +.DS_Store + +# Cakeup +cakeup-x86_64-latest.exe + +# .NET Core CLI +/.dotnet/ +/.packages/ +dotnet-install.sh* +*.lock.json + +# Visual Studio +.vs/ +.vscode/ +launchSettings.json +*.sln.ide/ + +# Rider +src/.idea/**/workspace.xml +src/.idea/**/tasks.xml +src/.idea/dictionaries +src/.idea/**/dataSources/ +src/.idea/**/dataSources.ids +src/.idea/**/dataSources.xml +src/.idea/**/dataSources.local.xml +src/.idea/**/sqlDataSources.xml +src/.idea/**/dynamic.xml +src/.idea/**/uiDesigner.xml + +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# User-specific files +*.suo +*.user +*.sln.docstates +*.userprefs +*.GhostDoc.xml +*StyleCop.Cache + +# Build results +[Dd]ebug/ +[Rr]elease/ +x64/ +*_i.c +*_p.c +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.log +*.vspscc +*.vssscc +.builds + +# Visual Studio profiler +*.psess +*.vsp +*.vspx + +# ReSharper is a .NET coding add-in +_ReSharper* + +# NCrunch +.*crunch*.local.xml +_NCrunch_* + +# NuGet Packages Directory +packages + +# Windows +Thumbs.db + +*.received.* + +node_modules \ No newline at end of file diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..7b01ef2 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,132 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, caste, color, religion, or sexual +identity and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the overall + community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or advances of + any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email address, + without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official e-mail address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement at +conduct@dotnetfoundation.org. +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series of +actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or permanent +ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within the +community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.1, available at +[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. + +Community Impact Guidelines were inspired by +[Mozilla's code of conduct enforcement ladder][Mozilla CoC]. + +For answers to common questions about this code of conduct, see the FAQ at +[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at +[https://www.contributor-covenant.org/translations][translations]. + +[homepage]: https://www.contributor-covenant.org +[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html +[Mozilla CoC]: https://github.com/mozilla/diversity +[FAQ]: https://www.contributor-covenant.org/faq +[translations]: https://www.contributor-covenant.org/translations \ No newline at end of file diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..1af3613 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Patrik Svensson, Phil Scott, Nils Andresen, Cédric Luthi, Frank Ray + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/NuGet.Config b/NuGet.Config new file mode 100644 index 0000000..2c70539 --- /dev/null +++ b/NuGet.Config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..367783d --- /dev/null +++ b/README.md @@ -0,0 +1,21 @@ +# `Spectre.Console` examples + +To see `Spectre.Console` in action, install the +[dotnet-example](https://github.com/patriksvensson/dotnet-example) +global tool. + +``` +> dotnet tool restore +``` + +Now you can list available examples in this repository: + +``` +> dotnet example +``` + +And to run an example: + +``` +> dotnet example tables +``` diff --git a/build.cake b/build.cake new file mode 100644 index 0000000..fd89c8d --- /dev/null +++ b/build.cake @@ -0,0 +1,36 @@ +var target = Argument("target", "Default"); +var configuration = Argument("configuration", "Release"); + +//////////////////////////////////////////////////////////////// +// Tasks + +Task("Clean") + .Does(context => +{ + context.CleanDirectory("./.artifacts"); +}); + +Task("Build") + .IsDependentOn("Clean") + .Does(context => +{ + DotNetBuild("./examples/Examples.sln", new DotNetBuildSettings { + Configuration = configuration, + Verbosity = DotNetVerbosity.Minimal, + NoLogo = true, + NoIncremental = context.HasArgument("rebuild"), + MSBuildSettings = new DotNetMSBuildSettings() + .TreatAllWarningsAs(MSBuildTreatAllWarningsAs.Error) + }); +}); + +//////////////////////////////////////////////////////////////// +// Targets + +Task("Default") + .IsDependentOn("Package"); + +//////////////////////////////////////////////////////////////// +// Execution + +RunTarget(target) \ No newline at end of file diff --git a/dotnet-tools.json b/dotnet-tools.json new file mode 100644 index 0000000..b0036d8 --- /dev/null +++ b/dotnet-tools.json @@ -0,0 +1,18 @@ +{ + "version": 1, + "isRoot": true, + "tools": { + "cake.tool": { + "version": "4.0.0", + "commands": [ + "dotnet-cake" + ] + }, + "dotnet-example": { + "version": "3.1.0", + "commands": [ + "dotnet-example" + ] + } + } +} \ No newline at end of file diff --git a/examples/Cli/Delegates/BarSettings.cs b/examples/Cli/Delegates/BarSettings.cs new file mode 100644 index 0000000..3c89484 --- /dev/null +++ b/examples/Cli/Delegates/BarSettings.cs @@ -0,0 +1,15 @@ +using System.ComponentModel; +using Spectre.Console.Cli; + +namespace Delegates; + +public static partial class Program +{ + public sealed class BarSettings : CommandSettings + { + [CommandOption("--count")] + [Description("The number of bars to print")] + [DefaultValue(3)] + public int Count { get; set; } + } +} diff --git a/examples/Cli/Delegates/Delegates.csproj b/examples/Cli/Delegates/Delegates.csproj new file mode 100644 index 0000000..233fbdf --- /dev/null +++ b/examples/Cli/Delegates/Delegates.csproj @@ -0,0 +1,17 @@ + + + + Exe + net8.0 + false + Delegates + Demonstrates how to specify commands as delegates. + Cli + false + + + + + + + diff --git a/examples/Cli/Delegates/Program.cs b/examples/Cli/Delegates/Program.cs new file mode 100644 index 0000000..a91df34 --- /dev/null +++ b/examples/Cli/Delegates/Program.cs @@ -0,0 +1,61 @@ +using System.Threading.Tasks; +using Spectre.Console; +using Spectre.Console.Cli; + +namespace Delegates; + +public static partial class Program +{ + public static int Main(string[] args) + { + var app = new CommandApp(); + app.Configure(config => + { + config.AddDelegate("foo", Foo) + .WithDescription("Foos the bars"); + + config.AddDelegate("bar", Bar) + .WithDescription("Bars the foos"); + + config.AddAsyncDelegate("fooAsync", FooAsync) + .WithDescription("Foos the bars asynchronously"); + + config.AddAsyncDelegate("barAsync", BarAsync) + .WithDescription("Bars the foos asynchronously"); + }); + + return app.Run(args); + } + + private static int Foo(CommandContext context) + { + AnsiConsole.WriteLine("Foo"); + return 0; + } + + private static int Bar(CommandContext context, BarSettings settings) + { + for (var index = 0; index < settings.Count; index++) + { + AnsiConsole.WriteLine("Bar"); + } + + return 0; + } + + private static Task FooAsync(CommandContext context) + { + AnsiConsole.WriteLine("Foo"); + return Task.FromResult(0); + } + + private static Task BarAsync(CommandContext context, BarSettings settings) + { + for (var index = 0; index < settings.Count; index++) + { + AnsiConsole.WriteLine("Bar"); + } + + return Task.FromResult(0); + } +} diff --git a/examples/Cli/Demo/Commands/Add/AddPackageCommand.cs b/examples/Cli/Demo/Commands/Add/AddPackageCommand.cs new file mode 100644 index 0000000..1166d08 --- /dev/null +++ b/examples/Cli/Demo/Commands/Add/AddPackageCommand.cs @@ -0,0 +1,46 @@ +using System.ComponentModel; +using Demo.Utilities; +using Spectre.Console.Cli; + +namespace Demo.Commands.Add; + +[Description("Add a NuGet package reference to the project.")] +public sealed class AddPackageCommand : Command +{ + public sealed class Settings : AddSettings + { + [CommandArgument(0, "")] + [Description("The package reference to add.")] + public string PackageName { get; set; } + + [CommandOption("-v|--version ")] + [Description("The version of the package to add.")] + public string Version { get; set; } + + [CommandOption("-f|--framework ")] + [Description("Add the reference only when targeting a specific framework.")] + public string Framework { get; set; } + + [CommandOption("--no-restore")] + [Description("Add the reference without performing restore preview and compatibility check.")] + public bool NoRestore { get; set; } + + [CommandOption("--source ")] + [Description("The NuGet package source to use during the restore.")] + public string Source { get; set; } + + [CommandOption("--package-directory ")] + [Description("The directory to restore packages to.")] + public string PackageDirectory { get; set; } + + [CommandOption("--interactive")] + [Description("Allows the command to stop and wait for user input or action (for example to complete authentication).")] + public bool Interactive { get; set; } + } + + public override int Execute(CommandContext context, Settings settings) + { + SettingsDumper.Dump(settings); + return 0; + } +} diff --git a/examples/Cli/Demo/Commands/Add/AddReferenceCommand.cs b/examples/Cli/Demo/Commands/Add/AddReferenceCommand.cs new file mode 100644 index 0000000..33e2b8a --- /dev/null +++ b/examples/Cli/Demo/Commands/Add/AddReferenceCommand.cs @@ -0,0 +1,29 @@ +using System.ComponentModel; +using Demo.Utilities; +using Spectre.Console.Cli; + +namespace Demo.Commands.Add; + +public sealed class AddReferenceCommand : Command +{ + public sealed class Settings : AddSettings + { + [CommandArgument(0, "")] + [Description("The package reference to add.")] + public string ProjectPath { get; set; } + + [CommandOption("-f|--framework ")] + [Description("Add the reference only when targeting a specific framework.")] + public string Framework { get; set; } + + [CommandOption("--interactive")] + [Description("Allows the command to stop and wait for user input or action (for example to complete authentication).")] + public bool Interactive { get; set; } + } + + public override int Execute(CommandContext context, Settings settings) + { + SettingsDumper.Dump(settings); + return 0; + } +} diff --git a/examples/Cli/Demo/Commands/Add/AddSettings.cs b/examples/Cli/Demo/Commands/Add/AddSettings.cs new file mode 100644 index 0000000..c8f02ec --- /dev/null +++ b/examples/Cli/Demo/Commands/Add/AddSettings.cs @@ -0,0 +1,11 @@ +using System.ComponentModel; +using Spectre.Console.Cli; + +namespace Demo.Commands.Add; + +public abstract class AddSettings : CommandSettings +{ + [CommandArgument(0, "")] + [Description("The project file to operate on. If a file is not specified, the command will search the current directory for one.")] + public string Project { get; set; } +} diff --git a/examples/Cli/Demo/Commands/Run/RunCommand.cs b/examples/Cli/Demo/Commands/Run/RunCommand.cs new file mode 100644 index 0000000..a1aa675 --- /dev/null +++ b/examples/Cli/Demo/Commands/Run/RunCommand.cs @@ -0,0 +1,69 @@ +using System.ComponentModel; +using Demo.Utilities; +using Spectre.Console.Cli; + +namespace Demo.Commands.Run; + +[Description("Build and run a .NET project output.")] +public sealed class RunCommand : Command +{ + public sealed class Settings : CommandSettings + { + [CommandOption("-c|--configuration ")] + [Description("The configuration to run for. The default for most projects is '[grey]Debug[/]'.")] + [DefaultValue("Debug")] + public string Configuration { get; set; } + + [CommandOption("-f|--framework ")] + [Description("The target framework to run for. The target framework must also be specified in the project file.")] + public string Framework { get; set; } + + [CommandOption("-r|--runtime ")] + [Description("The target runtime to run for.")] + public string RuntimeIdentifier { get; set; } + + [CommandOption("-p|--project ")] + [Description("The path to the project file to run (defaults to the current directory if there is only one project).")] + public string ProjectPath { get; set; } + + [CommandOption("--launch-profile ")] + [Description("The name of the launch profile (if any) to use when launching the application.")] + public string LaunchProfile { get; set; } + + [CommandOption("--no-launch-profile")] + [Description("Do not attempt to use [grey]launchSettings.json[/] to configure the application.")] + public bool NoLaunchProfile { get; set; } + + [CommandOption("--no-build")] + [Description("Do not build the project before running. Implies [grey]--no-restore[/].")] + public bool NoBuild { get; set; } + + [CommandOption("--interactive")] + [Description("Allows the command to stop and wait for user input or action (for example to complete authentication).")] + public string Interactive { get; set; } + + [CommandOption("--no-restore")] + [Description("Do not restore the project before building.")] + public bool NoRestore { get; set; } + + [CommandOption("--verbosity ")] + [Description("Set the MSBuild verbosity level. Allowed values are q[grey]uiet[/], m[grey]inimal[/], n[grey]ormal[/], d[grey]etailed[/], and diag[grey]nostic[/].")] + [TypeConverter(typeof(VerbosityConverter))] + [DefaultValue(Verbosity.Normal)] + public Verbosity Verbosity { get; set; } + + [CommandOption("--no-dependencies")] + [Description("Do not restore project-to-project references and only restore the specified project.")] + public bool NoDependencies { get; set; } + + [CommandOption("--force")] + [Description("Force all dependencies to be resolved even if the last restore was successful. This is equivalent to deleting [grey]project.assets.json[/].")] + public bool Force { get; set; } + } + + public override int Execute(CommandContext context, Settings settings) + { + SettingsDumper.Dump(settings); + return 0; + } +} diff --git a/examples/Cli/Demo/Commands/Serve/ServeCommand.cs b/examples/Cli/Demo/Commands/Serve/ServeCommand.cs new file mode 100644 index 0000000..58d1271 --- /dev/null +++ b/examples/Cli/Demo/Commands/Serve/ServeCommand.cs @@ -0,0 +1,40 @@ +using System; +using System.ComponentModel; +using Demo.Utilities; +using Spectre.Console.Cli; + +namespace Demo.Commands.Serve; + +[Description("Launches a web server in the current working directory and serves all files in it.")] +public sealed class ServeCommand : Command +{ + public sealed class Settings : CommandSettings + { + [CommandOption("-p|--port ")] + [Description("Port to use. Defaults to [grey]8080[/]. Use [grey]0[/] for a dynamic port.")] + public int Port { get; set; } + + [CommandOption("-o|--open-browser [BROWSER]")] + [Description("Open a web browser when the server starts. You can also specify which browser to use. If none is specified, the default one will be used.")] + public FlagValue OpenBrowser { get; set; } + } + + public override int Execute(CommandContext context, Settings settings) + { + if (settings.OpenBrowser.IsSet) + { + var browser = settings.OpenBrowser.Value; + if (browser != null) + { + Console.WriteLine($"Open in {browser}"); + } + else + { + Console.WriteLine($"Open in default browser."); + } + } + + SettingsDumper.Dump(settings); + return 0; + } +} diff --git a/examples/Cli/Demo/Demo.csproj b/examples/Cli/Demo/Demo.csproj new file mode 100644 index 0000000..5d8c15a --- /dev/null +++ b/examples/Cli/Demo/Demo.csproj @@ -0,0 +1,17 @@ + + + + Exe + net8.0 + false + Demo + Demonstrates the most common use cases of Spectre.Cli. + Cli + false + + + + + + + diff --git a/examples/Cli/Demo/Program.cs b/examples/Cli/Demo/Program.cs new file mode 100644 index 0000000..f6c3403 --- /dev/null +++ b/examples/Cli/Demo/Program.cs @@ -0,0 +1,39 @@ +using Demo.Commands; +using Demo.Commands.Add; +using Demo.Commands.Run; +using Demo.Commands.Serve; +using Spectre.Console.Cli; + +namespace Demo; + +public static class Program +{ + public static int Main(string[] args) + { + var app = new CommandApp(); + app.Configure(config => + { + config.SetApplicationName("fake-dotnet"); + config.ValidateExamples(); + config.AddExample("run", "--no-build"); + + // Run + config.AddCommand("run"); + + // Add + config.AddBranch("add", add => + { + add.SetDescription("Add a package or reference to a .NET project"); + add.AddCommand("package"); + add.AddCommand("reference"); + }); + + // Serve + config.AddCommand("serve") + .WithExample("serve", "-o", "firefox") + .WithExample("serve", "--port", "80", "-o", "firefox"); + }); + + return app.Run(args); + } +} diff --git a/examples/Cli/Demo/Utilities/SettingsDumper.cs b/examples/Cli/Demo/Utilities/SettingsDumper.cs new file mode 100644 index 0000000..4445927 --- /dev/null +++ b/examples/Cli/Demo/Utilities/SettingsDumper.cs @@ -0,0 +1,28 @@ +using Spectre.Console; +using Spectre.Console.Cli; + +namespace Demo.Utilities; + +public static class SettingsDumper +{ + public static void Dump(CommandSettings settings) + { + var table = new Table().RoundedBorder(); + table.AddColumn("[grey]Name[/]"); + table.AddColumn("[grey]Value[/]"); + + var properties = settings.GetType().GetProperties(); + foreach (var property in properties) + { + var value = property.GetValue(settings) + ?.ToString() + ?.Replace("[", "[["); + + table.AddRow( + property.Name, + value ?? "[grey]null[/]"); + } + + AnsiConsole.Write(table); + } +} diff --git a/examples/Cli/Demo/Verbosity.cs b/examples/Cli/Demo/Verbosity.cs new file mode 100644 index 0000000..84fc3b4 --- /dev/null +++ b/examples/Cli/Demo/Verbosity.cs @@ -0,0 +1,53 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Globalization; + +namespace Demo; + +public enum Verbosity +{ + Quiet, + Minimal, + Normal, + Detailed, + Diagnostic +} + +public sealed class VerbosityConverter : TypeConverter +{ + private readonly Dictionary _lookup; + + public VerbosityConverter() + { + _lookup = new Dictionary(StringComparer.OrdinalIgnoreCase) + { + { "q", Verbosity.Quiet }, + { "quiet", Verbosity.Quiet }, + { "m", Verbosity.Minimal }, + { "minimal", Verbosity.Minimal }, + { "n", Verbosity.Normal }, + { "normal", Verbosity.Normal }, + { "d", Verbosity.Detailed }, + { "detailed", Verbosity.Detailed }, + { "diag", Verbosity.Diagnostic }, + { "diagnostic", Verbosity.Diagnostic } + }; + } + + public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) + { + if (value is string stringValue) + { + var result = _lookup.TryGetValue(stringValue, out var verbosity); + if (!result) + { + const string format = "The value '{0}' is not a valid verbosity."; + var message = string.Format(CultureInfo.InvariantCulture, format, value); + throw new InvalidOperationException(message); + } + return verbosity; + } + throw new NotSupportedException("Can't convert value to verbosity."); + } +} diff --git a/examples/Cli/Dynamic/Dynamic.csproj b/examples/Cli/Dynamic/Dynamic.csproj new file mode 100644 index 0000000..739192f --- /dev/null +++ b/examples/Cli/Dynamic/Dynamic.csproj @@ -0,0 +1,17 @@ + + + + Exe + net8.0 + false + Dynamic + Demonstrates how to define dynamic commands. + Cli + false + + + + + + + diff --git a/examples/Cli/Dynamic/MyCommand.cs b/examples/Cli/Dynamic/MyCommand.cs new file mode 100644 index 0000000..60128cb --- /dev/null +++ b/examples/Cli/Dynamic/MyCommand.cs @@ -0,0 +1,20 @@ +using System; +using Spectre.Console; +using Spectre.Console.Cli; + +namespace Dynamic; + +public sealed class MyCommand : Command +{ + public override int Execute(CommandContext context) + { + if (!(context.Data is int data)) + { + throw new InvalidOperationException("Command has no associated data."); + + } + + AnsiConsole.WriteLine("Value = {0}", data); + return 0; + } +} diff --git a/examples/Cli/Dynamic/Program.cs b/examples/Cli/Dynamic/Program.cs new file mode 100644 index 0000000..9dc916f --- /dev/null +++ b/examples/Cli/Dynamic/Program.cs @@ -0,0 +1,23 @@ +using System.Linq; +using Spectre.Console.Cli; + +namespace Dynamic; + +public static class Program +{ + public static int Main(string[] args) + { + var app = new CommandApp(); + app.Configure(config => + { + foreach (var index in Enumerable.Range(1, 10)) + { + config.AddCommand($"c{index}") + .WithDescription($"Prints the number {index}") + .WithData(index); + } + }); + + return app.Run(args); + } +} diff --git a/examples/Cli/Help/CustomHelpProvider.cs b/examples/Cli/Help/CustomHelpProvider.cs new file mode 100644 index 0000000..fd9c01f --- /dev/null +++ b/examples/Cli/Help/CustomHelpProvider.cs @@ -0,0 +1,30 @@ +using System.Linq; +using Spectre.Console; +using Spectre.Console.Cli; +using Spectre.Console.Cli.Help; +using Spectre.Console.Rendering; + +namespace Help; + +/// +/// Example showing how to extend the built-in Spectre.Console help provider +/// by rendering a custom banner at the top of the help information +/// +internal class CustomHelpProvider : HelpProvider +{ + public CustomHelpProvider(ICommandAppSettings settings) + : base(settings) + { + } + + public override IEnumerable GetHeader(ICommandModel model, ICommandInfo? command) + { + return new[] + { + new Text("--------------------------------------"), Text.NewLine, + new Text("--- CUSTOM HELP PROVIDER ---"), Text.NewLine, + new Text("--------------------------------------"), Text.NewLine, + Text.NewLine, + }; + } +} \ No newline at end of file diff --git a/examples/Cli/Help/DefaultCommand.cs b/examples/Cli/Help/DefaultCommand.cs new file mode 100644 index 0000000..71613d0 --- /dev/null +++ b/examples/Cli/Help/DefaultCommand.cs @@ -0,0 +1,20 @@ +using Spectre.Console; +using Spectre.Console.Cli; + +namespace Help; + +public sealed class DefaultCommand : Command +{ + private IAnsiConsole _console; + + public DefaultCommand(IAnsiConsole console) + { + _console = console; + } + + public override int Execute(CommandContext context) + { + _console.WriteLine("Hello world"); + return 0; + } +} \ No newline at end of file diff --git a/examples/Cli/Help/Help.csproj b/examples/Cli/Help/Help.csproj new file mode 100644 index 0000000..d93c525 --- /dev/null +++ b/examples/Cli/Help/Help.csproj @@ -0,0 +1,18 @@ + + + + Exe + net8.0 + enable + enable + Help + Demonstrates how to extend the built-in Spectre.Console help provider to render a custom banner at the top of the help information. + Cli + false + + + + + + + diff --git a/examples/Cli/Help/Program.cs b/examples/Cli/Help/Program.cs new file mode 100644 index 0000000..aba42b0 --- /dev/null +++ b/examples/Cli/Help/Program.cs @@ -0,0 +1,23 @@ +using Spectre.Console.Cli; +using Spectre.Console.Cli.Help; + +namespace Help; + +public static class Program +{ + public static int Main(string[] args) + { + var app = new CommandApp(); + + app.Configure(config => + { + // Register the custom help provider + config.SetHelpProvider(new CustomHelpProvider(config.Settings)); + + // Render an unstyled help text for maximum accessibility + config.Settings.HelpProviderStyles = null; + }); + + return app.Run(args); + } +} diff --git a/examples/Cli/Injection/Commands/DefaultCommand.cs b/examples/Cli/Injection/Commands/DefaultCommand.cs new file mode 100644 index 0000000..b31cc8a --- /dev/null +++ b/examples/Cli/Injection/Commands/DefaultCommand.cs @@ -0,0 +1,29 @@ +using System; +using System.ComponentModel; +using Spectre.Console.Cli; + +namespace Injection.Commands; + +public sealed class DefaultCommand : Command +{ + private readonly IGreeter _greeter; + + public sealed class Settings : CommandSettings + { + [CommandOption("-n|--name ")] + [Description("The person or thing to greet.")] + [DefaultValue("World")] + public string Name { get; set; } + } + + public DefaultCommand(IGreeter greeter) + { + _greeter = greeter ?? throw new ArgumentNullException(nameof(greeter)); + } + + public override int Execute(CommandContext context, Settings settings) + { + _greeter.Greet(settings.Name); + return 0; + } +} diff --git a/examples/Cli/Injection/IGreeter.cs b/examples/Cli/Injection/IGreeter.cs new file mode 100644 index 0000000..1ff49fb --- /dev/null +++ b/examples/Cli/Injection/IGreeter.cs @@ -0,0 +1,16 @@ +using Spectre.Console; + +namespace Injection; + +public interface IGreeter +{ + void Greet(string name); +} + +public sealed class HelloWorldGreeter : IGreeter +{ + public void Greet(string name) + { + AnsiConsole.WriteLine($"Hello {name}!"); + } +} diff --git a/examples/Cli/Injection/Infrastructure/TypeRegistrar.cs b/examples/Cli/Injection/Infrastructure/TypeRegistrar.cs new file mode 100644 index 0000000..6651e67 --- /dev/null +++ b/examples/Cli/Injection/Infrastructure/TypeRegistrar.cs @@ -0,0 +1,40 @@ +using System; +using Microsoft.Extensions.DependencyInjection; +using Spectre.Console.Cli; + +namespace Injection.Infrastructure; + +public sealed class TypeRegistrar : ITypeRegistrar +{ + private readonly IServiceCollection _builder; + + public TypeRegistrar(IServiceCollection builder) + { + _builder = builder; + } + + public ITypeResolver Build() + { + return new TypeResolver(_builder.BuildServiceProvider()); + } + + public void Register(Type service, Type implementation) + { + _builder.AddSingleton(service, implementation); + } + + public void RegisterInstance(Type service, object implementation) + { + _builder.AddSingleton(service, implementation); + } + + public void RegisterLazy(Type service, Func func) + { + if (func is null) + { + throw new ArgumentNullException(nameof(func)); + } + + _builder.AddSingleton(service, (provider) => func()); + } +} diff --git a/examples/Cli/Injection/Infrastructure/TypeResolver.cs b/examples/Cli/Injection/Infrastructure/TypeResolver.cs new file mode 100644 index 0000000..f3cf618 --- /dev/null +++ b/examples/Cli/Injection/Infrastructure/TypeResolver.cs @@ -0,0 +1,32 @@ +using System; +using Spectre.Console.Cli; + +namespace Injection.Infrastructure; + +public sealed class TypeResolver : ITypeResolver, IDisposable +{ + private readonly IServiceProvider _provider; + + public TypeResolver(IServiceProvider provider) + { + _provider = provider ?? throw new ArgumentNullException(nameof(provider)); + } + + public object Resolve(Type type) + { + if (type == null) + { + return null; + } + + return _provider.GetService(type); + } + + public void Dispose() + { + if (_provider is IDisposable disposable) + { + disposable.Dispose(); + } + } +} diff --git a/examples/Cli/Injection/Injection.csproj b/examples/Cli/Injection/Injection.csproj new file mode 100644 index 0000000..a07bc9f --- /dev/null +++ b/examples/Cli/Injection/Injection.csproj @@ -0,0 +1,21 @@ + + + + Exe + net8.0 + false + Injection + Demonstrates how to use dependency injection with Spectre.Cli. + Cli + false + + + + + + + + + + + diff --git a/examples/Cli/Injection/Program.cs b/examples/Cli/Injection/Program.cs new file mode 100644 index 0000000..813560d --- /dev/null +++ b/examples/Cli/Injection/Program.cs @@ -0,0 +1,23 @@ +using Injection.Commands; +using Injection.Infrastructure; +using Microsoft.Extensions.DependencyInjection; +using Spectre.Console.Cli; + +namespace Injection; + +public class Program +{ + public static int Main(string[] args) + { + // Create a type registrar and register any dependencies. + // A type registrar is an adapter for a DI framework. + var registrations = new ServiceCollection(); + registrations.AddSingleton(); + var registrar = new TypeRegistrar(registrations); + + // Create a new command app with the registrar + // and run it with the provided arguments. + var app = new CommandApp(registrar); + return app.Run(args); + } +} diff --git a/examples/Cli/Logging/Commands/HelloCommand.cs b/examples/Cli/Logging/Commands/HelloCommand.cs new file mode 100644 index 0000000..21e8244 --- /dev/null +++ b/examples/Cli/Logging/Commands/HelloCommand.cs @@ -0,0 +1,34 @@ +using Microsoft.Extensions.Logging; +using Spectre.Console; +using Spectre.Console.Cli; + +namespace Logging.Commands; + +public class HelloCommand : Command +{ + private ILogger _logger; + private IAnsiConsole _console; + + public HelloCommand(IAnsiConsole console, ILogger logger) + { + _console = console; + _logger = logger; + _logger.LogDebug("{0} initialized", nameof(HelloCommand)); + } + + public class Settings : LogCommandSettings + { + [CommandArgument(0, "[Name]")] + public string Name { get; set; } + } + + + public override int Execute(CommandContext context, Settings settings) + { + _logger.LogInformation("Starting my command"); + AnsiConsole.MarkupLine($"Hello, [blue]{settings.Name}[/]"); + _logger.LogInformation("Completed my command"); + + return 0; + } +} diff --git a/examples/Cli/Logging/Commands/LogCommandSettings.cs b/examples/Cli/Logging/Commands/LogCommandSettings.cs new file mode 100644 index 0000000..890953d --- /dev/null +++ b/examples/Cli/Logging/Commands/LogCommandSettings.cs @@ -0,0 +1,55 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Globalization; +using Serilog.Events; +using Spectre.Console.Cli; + +namespace Logging.Commands; + +public class LogCommandSettings : CommandSettings +{ + [CommandOption("--logFile")] + [Description("Path and file name for logging")] + public string LogFile { get; set; } + + [CommandOption("--logLevel")] + [Description("Minimum level for logging")] + [TypeConverter(typeof(VerbosityConverter))] + [DefaultValue(LogEventLevel.Information)] + public LogEventLevel LogLevel { get; set; } +} + +public sealed class VerbosityConverter : TypeConverter +{ + private readonly Dictionary _lookup; + + public VerbosityConverter() + { + _lookup = new Dictionary(StringComparer.OrdinalIgnoreCase) + { + {"d", LogEventLevel.Debug}, + {"v", LogEventLevel.Verbose}, + {"i", LogEventLevel.Information}, + {"w", LogEventLevel.Warning}, + {"e", LogEventLevel.Error}, + {"f", LogEventLevel.Fatal} + }; + } + + public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) + { + if (value is string stringValue) + { + var result = _lookup.TryGetValue(stringValue, out var verbosity); + if (!result) + { + const string format = "The value '{0}' is not a valid verbosity."; + var message = string.Format(CultureInfo.InvariantCulture, format, value); + throw new InvalidOperationException(message); + } + return verbosity; + } + throw new NotSupportedException("Can't convert value to verbosity."); + } +} diff --git a/examples/Cli/Logging/Infrastructure/LogInterceptor.cs b/examples/Cli/Logging/Infrastructure/LogInterceptor.cs new file mode 100644 index 0000000..54e2653 --- /dev/null +++ b/examples/Cli/Logging/Infrastructure/LogInterceptor.cs @@ -0,0 +1,19 @@ +using Logging.Commands; +using Serilog.Core; +using Spectre.Console.Cli; + +namespace Logging.Infrastructure; + +public class LogInterceptor : ICommandInterceptor +{ + public static readonly LoggingLevelSwitch LogLevel = new(); + + public void Intercept(CommandContext context, CommandSettings settings) + { + if (settings is LogCommandSettings logSettings) + { + LoggingEnricher.Path = logSettings.LogFile ?? "application.log"; + LogLevel.MinimumLevel = logSettings.LogLevel; + } + } +} diff --git a/examples/Cli/Logging/Infrastructure/LoggingEnricher.cs b/examples/Cli/Logging/Infrastructure/LoggingEnricher.cs new file mode 100644 index 0000000..266260d --- /dev/null +++ b/examples/Cli/Logging/Infrastructure/LoggingEnricher.cs @@ -0,0 +1,37 @@ +using Serilog.Core; +using Serilog.Events; + +namespace Logging.Infrastructure; + +internal class LoggingEnricher : ILogEventEnricher +{ + private string _cachedLogFilePath; + private LogEventProperty _cachedLogFilePathProperty; + + // this path and level will be set by the LogInterceptor.cs after parsing the settings + public static string Path = string.Empty; + + public const string LogFilePathPropertyName = "LogFilePath"; + + public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory) + { + // the settings might not have a path or we might not be within a command in which case + // we won't have the setting so a default value for the log file will be required + LogEventProperty logFilePathProperty; + + if (_cachedLogFilePathProperty != null && Path.Equals(_cachedLogFilePath)) + { + // Path hasn't changed, so let's use the cached property + logFilePathProperty = _cachedLogFilePathProperty; + } + else + { + // We've got a new path for the log. Let's create a new property + // and cache it for future log events to use + _cachedLogFilePath = Path; + _cachedLogFilePathProperty = logFilePathProperty = propertyFactory.CreateProperty(LogFilePathPropertyName, Path); + } + + logEvent.AddPropertyIfAbsent(logFilePathProperty); + } +} diff --git a/examples/Cli/Logging/Infrastructure/TypeRegistrar.cs b/examples/Cli/Logging/Infrastructure/TypeRegistrar.cs new file mode 100644 index 0000000..f1169fc --- /dev/null +++ b/examples/Cli/Logging/Infrastructure/TypeRegistrar.cs @@ -0,0 +1,40 @@ +using System; +using Microsoft.Extensions.DependencyInjection; +using Spectre.Console.Cli; + +namespace Logging.Infrastructure; + +public sealed class TypeRegistrar : ITypeRegistrar +{ + private readonly IServiceCollection _builder; + + public TypeRegistrar(IServiceCollection builder) + { + _builder = builder; + } + + public ITypeResolver Build() + { + return new TypeResolver(_builder.BuildServiceProvider()); + } + + public void Register(Type service, Type implementation) + { + _builder.AddSingleton(service, implementation); + } + + public void RegisterInstance(Type service, object implementation) + { + _builder.AddSingleton(service, implementation); + } + + public void RegisterLazy(Type service, Func func) + { + if (func is null) + { + throw new ArgumentNullException(nameof(func)); + } + + _builder.AddSingleton(service, _ => func()); + } +} diff --git a/examples/Cli/Logging/Infrastructure/TypeResolver.cs b/examples/Cli/Logging/Infrastructure/TypeResolver.cs new file mode 100644 index 0000000..bd2b5f7 --- /dev/null +++ b/examples/Cli/Logging/Infrastructure/TypeResolver.cs @@ -0,0 +1,24 @@ +using System; +using Spectre.Console.Cli; + +namespace Logging.Infrastructure; + +public sealed class TypeResolver : ITypeResolver +{ + private readonly IServiceProvider _provider; + + public TypeResolver(IServiceProvider provider) + { + _provider = provider ?? throw new ArgumentNullException(nameof(provider)); + } + + public object Resolve(Type type) + { + if (type == null) + { + return null; + } + + return _provider.GetService(type); + } +} diff --git a/examples/Cli/Logging/Logging.csproj b/examples/Cli/Logging/Logging.csproj new file mode 100644 index 0000000..d7710f7 --- /dev/null +++ b/examples/Cli/Logging/Logging.csproj @@ -0,0 +1,26 @@ + + + + Exe + net8.0 + false + Logging + Demonstrates how to dynamically configure Serilog for logging using parameters from a command. + Cli + false + disable + + + + + + + + + + + + + + + diff --git a/examples/Cli/Logging/Program.cs b/examples/Cli/Logging/Program.cs new file mode 100644 index 0000000..bfab659 --- /dev/null +++ b/examples/Cli/Logging/Program.cs @@ -0,0 +1,54 @@ +using Logging.Commands; +using Logging.Infrastructure; +using Microsoft.Extensions.DependencyInjection; +using Serilog; +using Spectre.Console.Cli; + +/* + * Dynamically control serilog configuration via command line parameters + * + * This works around the chicken and egg situation with configuring serilog via the command line. + * The logger needs to be configured prior to executing the parser, but the logger needs the parsed values + * to be configured. By using serilog.sinks.map we can defer configuration. We use a LogLevelSwitch to control the + * logging levels dynamically, and then we use a serilog enricher that has its state populated via a + * Spectre.Console CommandInterceptor + */ + +namespace Logging; + +public class Program +{ + static int Main(string[] args) + { + // to retrieve the log file name, we must first parse the command settings + // this will require us to delay setting the file path for the file writer. + // With serilog we can use an enricher and Serilog.Sinks.Map to dynamically + // pull this setting. + var serviceCollection = new ServiceCollection() + .AddLogging(configure => + configure.AddSerilog(new LoggerConfiguration() + // log level will be dynamically be controlled by our log interceptor upon running + .MinimumLevel.ControlledBy(LogInterceptor.LogLevel) + // the log enricher will add a new property with the log file path from the settings + // that we can use to set the path dynamically + .Enrich.With() + // serilog.sinks.map will defer the configuration of the sink to be ondemand + // allowing us to look at the properties set by the enricher to set the path appropriately + .WriteTo.Map(LoggingEnricher.LogFilePathPropertyName, + (logFilePath, wt) => wt.File($"{logFilePath}"), 1) + .CreateLogger() + ) + ); + + var registrar = new TypeRegistrar(serviceCollection); + var app = new CommandApp(registrar); + + app.Configure(config => + { + config.SetInterceptor(new LogInterceptor()); // add the interceptor + config.AddCommand("hello"); + }); + + return app.Run(args); + } +} diff --git a/examples/Console/AlternateScreen/AlternateScreen.csproj b/examples/Console/AlternateScreen/AlternateScreen.csproj new file mode 100644 index 0000000..e048827 --- /dev/null +++ b/examples/Console/AlternateScreen/AlternateScreen.csproj @@ -0,0 +1,15 @@ + + + + Exe + net8.0 + Screens + Demonstrates how to use alternate screens. + Widgets + + + + + + + diff --git a/examples/Console/AlternateScreen/Program.cs b/examples/Console/AlternateScreen/Program.cs new file mode 100644 index 0000000..4415ab0 --- /dev/null +++ b/examples/Console/AlternateScreen/Program.cs @@ -0,0 +1,26 @@ +// Check if we can use alternate screen buffers +using Spectre.Console; + +if (!AnsiConsole.Profile.Capabilities.AlternateBuffer) +{ + AnsiConsole.MarkupLine( + "[red]Alternate screen buffers are not supported " + + "by your terminal[/] [yellow]:([/]"); + + return; +} + +// Write to the terminal +AnsiConsole.Write(new Rule("[yellow]Normal universe[/]")); +AnsiConsole.Write(new Panel("Hello World!")); +AnsiConsole.MarkupLine("[grey]Press a key to continue[/]"); +AnsiConsole.Console.Input.ReadKey(true); + +AnsiConsole.AlternateScreen(() => +{ + // Now we're in another terminal screen buffer + AnsiConsole.Write(new Rule("[red]Mirror universe[/]")); + AnsiConsole.Write(new Panel("[red]Welcome to the upside down![/]")); + AnsiConsole.MarkupLine("[grey]Press a key to return[/]"); + AnsiConsole.Console.Input.ReadKey(true); +}); \ No newline at end of file diff --git a/examples/Console/Borders/Borders.csproj b/examples/Console/Borders/Borders.csproj new file mode 100644 index 0000000..4030715 --- /dev/null +++ b/examples/Console/Borders/Borders.csproj @@ -0,0 +1,15 @@ + + + + Exe + net8.0 + Borders + Demonstrates the different kind of borders. + Widgets + + + + + + + diff --git a/examples/Console/Borders/Program.cs b/examples/Console/Borders/Program.cs new file mode 100644 index 0000000..2b755ae --- /dev/null +++ b/examples/Console/Borders/Program.cs @@ -0,0 +1,86 @@ +using Spectre.Console; +using Spectre.Console.Rendering; + +namespace Borders; + +public static class Program +{ + public static void Main() + { + // Render panel borders + HorizontalRule("PANEL BORDERS"); + PanelBorders(); + + // Render table borders + HorizontalRule("TABLE BORDERS"); + TableBorders(); + } + + private static void PanelBorders() + { + static IRenderable CreatePanel(string name, BoxBorder border) + { + return + new Panel($"This is a panel with\nthe [yellow]{name}[/] border.") + .Header($" [blue]{name}[/] ", Justify.Center) + .Border(border) + .BorderStyle(Style.Parse("grey")); + } + + var items = new[] + { + CreatePanel("Ascii", BoxBorder.Ascii), + CreatePanel("Square", BoxBorder.Square), + CreatePanel("Rounded", BoxBorder.Rounded), + CreatePanel("Heavy", BoxBorder.Heavy), + CreatePanel("Double", BoxBorder.Double), + CreatePanel("None", BoxBorder.None), + }; + + AnsiConsole.Write( + new Padder( + new Columns(items).PadRight(2), + new Padding(2, 0, 0, 0))); + } + + private static void TableBorders() + { + static IRenderable CreateTable(string name, TableBorder border) + { + var table = new Table().Border(border); + table.ShowRowSeparators(); + table.AddColumn("[yellow]Header 1[/]", c => c.Footer("[grey]Footer 1[/]")); + table.AddColumn("[yellow]Header 2[/]", col => col.Footer("[grey]Footer 2[/]").RightAligned()); + table.AddRow("Cell", "Cell"); + table.AddRow("Cell", "Cell"); + + return new Panel(table) + .Header($" [blue]{name}[/] ", Justify.Center) + .PadBottom(1) + .NoBorder(); + } + + var items = new[] + { + CreateTable("Ascii", TableBorder.Ascii), CreateTable("Ascii2", TableBorder.Ascii2), + CreateTable("AsciiDoubleHead", TableBorder.AsciiDoubleHead), + CreateTable("Horizontal", TableBorder.Horizontal), CreateTable("Simple", TableBorder.Simple), + CreateTable("SimpleHeavy", TableBorder.SimpleHeavy), CreateTable("Minimal", TableBorder.Minimal), + CreateTable("MinimalHeavyHead", TableBorder.MinimalHeavyHead), + CreateTable("MinimalDoubleHead", TableBorder.MinimalDoubleHead), + CreateTable("Square", TableBorder.Square), CreateTable("Rounded", TableBorder.Rounded), + CreateTable("Heavy", TableBorder.Heavy), CreateTable("HeavyEdge", TableBorder.HeavyEdge), + CreateTable("HeavyHead", TableBorder.HeavyHead), CreateTable("Double", TableBorder.Double), + CreateTable("DoubleEdge", TableBorder.DoubleEdge), CreateTable("Markdown", TableBorder.Markdown), + }; + + AnsiConsole.Write(new Columns(items).Collapse()); + } + + private static void HorizontalRule(string title) + { + AnsiConsole.WriteLine(); + AnsiConsole.Write(new Rule($"[white bold]{title}[/]").RuleStyle("grey").LeftJustified()); + AnsiConsole.WriteLine(); + } +} \ No newline at end of file diff --git a/examples/Console/Calendars/Calendars.csproj b/examples/Console/Calendars/Calendars.csproj new file mode 100644 index 0000000..c888117 --- /dev/null +++ b/examples/Console/Calendars/Calendars.csproj @@ -0,0 +1,15 @@ + + + + Exe + net8.0 + Calendars + Demonstrates how to render calendars. + Widgets + + + + + + + diff --git a/examples/Console/Calendars/Program.cs b/examples/Console/Calendars/Program.cs new file mode 100644 index 0000000..d270115 --- /dev/null +++ b/examples/Console/Calendars/Program.cs @@ -0,0 +1,18 @@ +using Spectre.Console; + +namespace Calendars; + +public static class Program +{ + public static void Main(string[] args) + { + AnsiConsole.WriteLine(); + AnsiConsole.Write(new Calendar(2020, 10) + .RoundedBorder() + .HighlightStyle(Style.Parse("red")) + .HeaderStyle(Style.Parse("yellow")) + .AddCalendarEvent("An event", 2020, 9, 22) + .AddCalendarEvent("Another event", 2020, 10, 2) + .AddCalendarEvent("A third event", 2020, 10, 13)); + } +} diff --git a/examples/Console/Canvas/Canvas.csproj b/examples/Console/Canvas/Canvas.csproj new file mode 100644 index 0000000..c9b1c38 --- /dev/null +++ b/examples/Console/Canvas/Canvas.csproj @@ -0,0 +1,21 @@ + + + + Exe + net8.0 + Canvas + Demonstrates how to render pixels and images. + Widgets + + + + + + + + + PreserveNewest + + + + diff --git a/examples/Console/Canvas/Program.cs b/examples/Console/Canvas/Program.cs new file mode 100644 index 0000000..3e537dd --- /dev/null +++ b/examples/Console/Canvas/Program.cs @@ -0,0 +1,43 @@ +using System.Diagnostics; +using System.Reflection; +using SixLabors.ImageSharp.Processing; +using Spectre.Console; +using Spectre.Console.Rendering; + +namespace Canvas; + +public static class Program +{ + public static void Main() + { + // Draw an image using CanvasImage powered by ImageSharp. + // This requires the "Spectre.Console.ImageSharp" NuGet package. + var image = new CanvasImage("cake.png"); + image.BilinearResampler(); + image.MaxWidth(16); + Render(image, "Image from file (16 wide)"); + + // Draw image again, but without max width + image.NoMaxWidth(); + image.Mutate(ctx => ctx.Grayscale().Rotate(-45).EntropyCrop()); + Render(image, "Image from file (fit, greyscale, rotated)"); + + // Draw image again, but load from embedded resource rather than file + using (var fileStream = Assembly.GetExecutingAssembly().GetManifestResourceStream("Canvas.cake.png")) + { + Debug.Assert(fileStream != null); + var embeddedImage = new CanvasImage(fileStream); + embeddedImage.BilinearResampler(); + embeddedImage.MaxWidth(16); + Render(embeddedImage, "Image from embedded resource (16 wide)"); + } + } + + private static void Render(IRenderable canvas, string title) + { + AnsiConsole.WriteLine(); + AnsiConsole.Write(new Rule($"[yellow]{title}[/]").LeftJustified().RuleStyle("grey")); + AnsiConsole.WriteLine(); + AnsiConsole.Write(canvas); + } +} diff --git a/examples/Console/Canvas/cake.png b/examples/Console/Canvas/cake.png new file mode 100644 index 0000000000000000000000000000000000000000..f11d285c9463d14b1c4250457fe2a358678392a7 GIT binary patch literal 52832 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%0AR{LQqrz*;V=tn8)n&-xSpEi6st^OJ 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 literal 0 HcmV?d00001 diff --git a/examples/Console/Charts/Charts.csproj b/examples/Console/Charts/Charts.csproj new file mode 100644 index 0000000..f08d5b9 --- /dev/null +++ b/examples/Console/Charts/Charts.csproj @@ -0,0 +1,15 @@ + + + + Exe + net8.0 + Charts + Demonstrates how to render charts in a console. + Widgets + + + + + + + diff --git a/examples/Console/Charts/Program.cs b/examples/Console/Charts/Program.cs new file mode 100644 index 0000000..5aadfa8 --- /dev/null +++ b/examples/Console/Charts/Program.cs @@ -0,0 +1,42 @@ +using Spectre.Console; +using Spectre.Console.Rendering; + +namespace Charts; + +public static class Program +{ + public static void Main() + { + // Render a bar chart + AnsiConsole.WriteLine(); + Render("Fruits per month", new BarChart() + .Width(60) + .Label("[green bold underline]Number of fruits[/]") + .CenterLabel() + .AddItem("Apple", 12, Color.Yellow) + .AddItem("Orange", 54, Color.Green) + .AddItem("Banana", 33, Color.Red)); + + // Render a breakdown chart + AnsiConsole.WriteLine(); + Render("Languages used", new BreakdownChart() + .FullSize() + .Width(60) + .ShowPercentage() + .WithValueColor(Color.Orange1) + .AddItem("SCSS", 37, Color.Red) + .AddItem("HTML", 28.3, Color.Blue) + .AddItem("C#", 22.6, Color.Green) + .AddItem("JavaScript", 6, Color.Yellow) + .AddItem("Ruby", 6, Color.LightGreen) + .AddItem("Shell", 0.1, Color.Aqua)); + } + + private static void Render(string title, IRenderable chart) + { + AnsiConsole.Write( + new Panel(chart) + .Padding(1, 1) + .Header(title)); + } +} diff --git a/examples/Console/Colors/Colors.csproj b/examples/Console/Colors/Colors.csproj new file mode 100644 index 0000000..0258570 --- /dev/null +++ b/examples/Console/Colors/Colors.csproj @@ -0,0 +1,15 @@ + + + + Exe + net8.0 + Colors + Demonstrates how to use [yellow]c[/][red]o[/][green]l[/][blue]o[/][aqua]r[/][lime]s[/] in the console. + Misc + + + + + + + diff --git a/examples/Console/Colors/Program.cs b/examples/Console/Colors/Program.cs new file mode 100644 index 0000000..946cb3d --- /dev/null +++ b/examples/Console/Colors/Program.cs @@ -0,0 +1,105 @@ +using Spectre.Console; +using Spectre.Console.Examples; + +namespace Colors; + +public static class Program +{ + public static void Main() + { + ///////////////////////////////////////////////////////////////// + // No colors + ///////////////////////////////////////////////////////////////// + if (AnsiConsole.Profile.Capabilities.ColorSystem == ColorSystem.NoColors) + { + AnsiConsole.WriteLine("No colors are supported."); + return; + } + + ///////////////////////////////////////////////////////////////// + // 3-BIT + ///////////////////////////////////////////////////////////////// + if (AnsiConsole.Profile.Supports(ColorSystem.Legacy)) + { + AnsiConsole.ResetColors(); + AnsiConsole.WriteLine(); + AnsiConsole.Write(new Rule("[yellow bold underline]3-bit Colors[/]").RuleStyle("grey").LeftJustified()); + AnsiConsole.WriteLine(); + + for (var i = 0; i < 8; i++) + { + AnsiConsole.Background = Color.FromInt32(i); + AnsiConsole.Foreground = AnsiConsole.Background.GetInvertedColor(); + AnsiConsole.Write(string.Format(" {0,-9}", AnsiConsole.Background.ToString())); + AnsiConsole.ResetColors(); + if ((i + 1) % 8 == 0) + { + AnsiConsole.WriteLine(); + } + } + } + + ///////////////////////////////////////////////////////////////// + // 4-BIT + ///////////////////////////////////////////////////////////////// + if (AnsiConsole.Profile.Supports(ColorSystem.Standard)) + { + AnsiConsole.ResetColors(); + AnsiConsole.WriteLine(); + AnsiConsole.Write(new Rule("[yellow bold underline]4-bit Colors[/]").RuleStyle("grey").LeftJustified()); + AnsiConsole.WriteLine(); + + for (var i = 0; i < 16; i++) + { + AnsiConsole.Background = Color.FromInt32(i); + AnsiConsole.Foreground = AnsiConsole.Background.GetInvertedColor(); + AnsiConsole.Write(string.Format(" {0,-9}", AnsiConsole.Background.ToString())); + AnsiConsole.ResetColors(); + if ((i + 1) % 8 == 0) + { + AnsiConsole.WriteLine(); + } + } + } + + ///////////////////////////////////////////////////////////////// + // 8-BIT + ///////////////////////////////////////////////////////////////// + if (AnsiConsole.Profile.Supports(ColorSystem.EightBit)) + { + AnsiConsole.ResetColors(); + AnsiConsole.WriteLine(); + AnsiConsole.Write(new Rule("[yellow bold underline]8-bit Colors[/]").RuleStyle("grey").LeftJustified()); + AnsiConsole.WriteLine(); + + for (var i = 0; i < 16; i++) + { + for (var j = 0; j < 16; j++) + { + var number = i * 16 + j; + AnsiConsole.Background = Color.FromInt32(number); + AnsiConsole.Foreground = AnsiConsole.Background.GetInvertedColor(); + AnsiConsole.Write(string.Format(" {0,-4}", number)); + AnsiConsole.ResetColors(); + if ((number + 1) % 16 == 0) + { + AnsiConsole.WriteLine(); + } + } + } + } + + ///////////////////////////////////////////////////////////////// + // 24-BIT + ///////////////////////////////////////////////////////////////// + if (AnsiConsole.Profile.Supports(ColorSystem.TrueColor)) + { + AnsiConsole.ResetColors(); + AnsiConsole.WriteLine(); + AnsiConsole.Write(new Rule("[yellow bold underline]24-bit Colors[/]").RuleStyle("grey").LeftJustified()); + AnsiConsole.WriteLine(); + + AnsiConsole.Write(new ColorBox(width: 80, height: 15)); + } + } +} diff --git a/examples/Console/Columns/Columns.csproj b/examples/Console/Columns/Columns.csproj new file mode 100644 index 0000000..471c83b --- /dev/null +++ b/examples/Console/Columns/Columns.csproj @@ -0,0 +1,19 @@ + + + + Exe + net8.0 + Columns + Demonstrates how to render data into columns. + Widgets + + + + + + + + + + + diff --git a/examples/Console/Columns/Program.cs b/examples/Console/Columns/Program.cs new file mode 100644 index 0000000..3a93ef4 --- /dev/null +++ b/examples/Console/Columns/Program.cs @@ -0,0 +1,30 @@ +using System.Collections.Generic; +using Spectre.Console; + +namespace Columns; + +public static class Program +{ + public static void Main() + { + var cards = new List(); + foreach (var user in User.LoadUsers()) + { + cards.Add( + new Panel(GetCardContent(user)) + .Header($"{user.Country}") + .RoundedBorder().Expand()); + } + + // Render all cards in columns + AnsiConsole.Write(new Spectre.Console.Columns(cards)); + } + + private static string GetCardContent(User user) + { + var name = $"{user.FirstName} {user.LastName}"; + var city = $"{user.City}"; + + return $"[b]{name}[/]\n[yellow]{city}[/]"; + } +} diff --git a/examples/Console/Columns/User.cs b/examples/Console/Columns/User.cs new file mode 100644 index 0000000..f2e7681 --- /dev/null +++ b/examples/Console/Columns/User.cs @@ -0,0 +1,88 @@ +using System.Collections.Generic; + +namespace Columns; + +public sealed class User +{ + public string FirstName { get; set; } + public string LastName { get; set; } + public string City { get; set; } + public string Country { get; set; } + + public static List LoadUsers() + { + return new List + { + new User + { + FirstName = "Andrea", + LastName = "Johansen", + City = "Hornbæk", + Country = "Denmark", + }, + new User + { + FirstName = "Phil", + LastName = "Scott", + City = "Dayton", + Country = "United States", + }, + new User + { + FirstName = "Patrik", + LastName = "Svensson", + City = "Stockholm", + Country = "Sweden", + }, + new User + { + FirstName = "Freya", + LastName = "Thompson", + City = "Rotorua", + Country = "New Zealand", + }, + new User + { + FirstName = "طاها", + LastName = "رضایی", + City = "اهواز", + Country = "Iran", + }, + new User + { + FirstName = "Yara", + LastName = "Simon", + City = "Develier", + Country = "Switzerland", + }, + new User + { + FirstName = "Giray", + LastName = "Erbay", + City = "Karabük", + Country = "Turkey", + }, + new User + { + FirstName = "Miodrag", + LastName = "Schaffer", + City = "Möckern", + Country = "Germany", + }, + new User + { + FirstName = "Carmela", + LastName = "Lo Castro", + City = "Firenze", + Country = "Italy", + }, + new User + { + FirstName = "Roberto", + LastName = "Sims", + City = "Mallow", + Country = "Ireland", + }, + }; + } +} diff --git a/examples/Console/Cursor/Cursor.csproj b/examples/Console/Cursor/Cursor.csproj new file mode 100644 index 0000000..0f3a3ea --- /dev/null +++ b/examples/Console/Cursor/Cursor.csproj @@ -0,0 +1,15 @@ + + + + Exe + net8.0 + Cursor + Demonstrates how to move the cursor. + Misc + + + + + + + diff --git a/examples/Console/Cursor/Program.cs b/examples/Console/Cursor/Program.cs new file mode 100644 index 0000000..3ea7785 --- /dev/null +++ b/examples/Console/Cursor/Program.cs @@ -0,0 +1,19 @@ +using Spectre.Console; + +namespace Cursor; + +public static class Program +{ + public static void Main(string[] args) + { + AnsiConsole.Write("Hello"); + + // Move the cursor 3 cells to the right + AnsiConsole.Cursor.Move(CursorDirection.Right, 3); + AnsiConsole.Write("World"); + + // Move the cursor 5 cells to the left. + AnsiConsole.Cursor.Move(CursorDirection.Left, 5); + AnsiConsole.WriteLine("Universe"); + } +} diff --git a/examples/Console/Decorations/Decorations.csproj b/examples/Console/Decorations/Decorations.csproj new file mode 100644 index 0000000..8541965 --- /dev/null +++ b/examples/Console/Decorations/Decorations.csproj @@ -0,0 +1,15 @@ + + + + Exe + net8.0 + Decorations + Demonstrates how to [italic]use[/] [bold]decorations[/] [dim]in[/] the console. + Misc + + + + + + + diff --git a/examples/Console/Decorations/Program.cs b/examples/Console/Decorations/Program.cs new file mode 100644 index 0000000..e689df5 --- /dev/null +++ b/examples/Console/Decorations/Program.cs @@ -0,0 +1,29 @@ +using Spectre.Console; + +namespace Colors; + +public static class Program +{ + public static void Main() + { + AnsiConsole.ResetDecoration(); + AnsiConsole.WriteLine(); + + if (AnsiConsole.Profile.Capabilities.Ansi) + { + AnsiConsole.Write(new Rule("[bold green]ANSI Decorations[/]")); + } + else + { + AnsiConsole.Write(new Rule("[bold red]Legacy Decorations (unsupported)[/]")); + } + + var decorations = System.Enum.GetValues(typeof(Decoration)); + foreach (var decoration in decorations) + { + var name = System.Enum.GetName(typeof(Decoration),decoration); + AnsiConsole.Write(name + ": "); + AnsiConsole.Write(new Markup(name+"\n", new Style(decoration: (Decoration)decoration))); + } + } +} diff --git a/examples/Console/Emojis/Emojis.csproj b/examples/Console/Emojis/Emojis.csproj new file mode 100644 index 0000000..f4def1b --- /dev/null +++ b/examples/Console/Emojis/Emojis.csproj @@ -0,0 +1,15 @@ + + + + Exe + net8.0 + Emojis + Demonstrates how to render emojis. + Misc + + + + + + + diff --git a/examples/Console/Emojis/Program.cs b/examples/Console/Emojis/Program.cs new file mode 100644 index 0000000..3fe6e0f --- /dev/null +++ b/examples/Console/Emojis/Program.cs @@ -0,0 +1,23 @@ +using Spectre.Console; + +namespace Emojis; + +public static class Program +{ + public static void Main(string[] args) + { + // Show a known emoji + RenderEmoji(); + + // Show a remapped emoji + Emoji.Remap("globe_showing_europe_africa", Emoji.Known.GrinningFaceWithSmilingEyes); + RenderEmoji(); + } + + private static void RenderEmoji() + { + AnsiConsole.Write( + new Panel("[yellow]Hello :globe_showing_europe_africa:![/]") + .RoundedBorder()); + } +} diff --git a/examples/Console/Exceptions/Exceptions.csproj b/examples/Console/Exceptions/Exceptions.csproj new file mode 100644 index 0000000..7ce802e --- /dev/null +++ b/examples/Console/Exceptions/Exceptions.csproj @@ -0,0 +1,15 @@ + + + + Exe + net8.0 + Exceptions + Demonstrates how to render formatted exceptions. + Misc + + + + + + + diff --git a/examples/Console/Exceptions/Program.cs b/examples/Console/Exceptions/Program.cs new file mode 100644 index 0000000..032908a --- /dev/null +++ b/examples/Console/Exceptions/Program.cs @@ -0,0 +1,101 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Security.Authentication; +using System.Threading.Tasks; +using Spectre.Console; + +namespace Exceptions; + +public static class Program +{ + public static async Task Main(string[] args) + { + try + { + var foo = new List(); + DoMagic(42, null, ref foo); + } + catch (Exception ex) + { + AnsiConsole.WriteLine(); + AnsiConsole.Write(new Rule("Default").LeftJustified()); + AnsiConsole.WriteLine(); + AnsiConsole.WriteException(ex); + + AnsiConsole.WriteLine(); + AnsiConsole.Write(new Rule("Compact").LeftJustified()); + AnsiConsole.WriteLine(); + AnsiConsole.WriteException(ex, ExceptionFormats.ShortenEverything | ExceptionFormats.ShowLinks); + + AnsiConsole.WriteLine(); + AnsiConsole.Write(new Rule("Compact + Custom colors").LeftJustified()); + AnsiConsole.WriteLine(); + AnsiConsole.WriteException(ex, new ExceptionSettings + { + Format = ExceptionFormats.ShortenEverything | ExceptionFormats.ShowLinks, + Style = new ExceptionStyle + { + Exception = new Style().Foreground(Color.Grey), + Message = new Style().Foreground(Color.White), + NonEmphasized = new Style().Foreground(Color.Cornsilk1), + Parenthesis = new Style().Foreground(Color.Cornsilk1), + Method = new Style().Foreground(Color.Red), + ParameterName = new Style().Foreground(Color.Cornsilk1), + ParameterType = new Style().Foreground(Color.Red), + Path = new Style().Foreground(Color.Red), + LineNumber = new Style().Foreground(Color.Cornsilk1), + } + }); + } + + try + { + await DoMagicAsync(42, null); + } + catch (Exception ex) + { + AnsiConsole.WriteLine(); + AnsiConsole.Write(new Rule("Async").LeftJustified()); + AnsiConsole.WriteLine(); + AnsiConsole.WriteException(ex, ExceptionFormats.ShortenPaths); + } + } + + private static void DoMagic(int foo, string[,] bar, ref List result) + { + try + { + CheckCredentials(foo, bar); + result = new List(); + } + catch (Exception ex) + { + throw new InvalidOperationException("Whaaat?", ex); + } + } + + private static bool CheckCredentials(int? qux, string[,] corgi) + { + throw new InvalidCredentialException("The credentials are invalid."); + } + + private static async Task DoMagicAsync(T foo, string[,] bar) + { + try + { + await CheckCredentialsAsync(new[] { foo }.ToList(), new []{ foo }, bar); + } + catch (Exception ex) + { + throw new InvalidOperationException("Whaaat?", ex); + } + } + + private static async Task CheckCredentialsAsync(List qux, T[] otherArray, string[,] corgi) + { + await Task.Delay(0); + throw new InvalidCredentialException("The credentials are invalid."); + } +} + diff --git a/examples/Console/Figlet/Figlet.csproj b/examples/Console/Figlet/Figlet.csproj new file mode 100644 index 0000000..c44b008 --- /dev/null +++ b/examples/Console/Figlet/Figlet.csproj @@ -0,0 +1,15 @@ + + + + Exe + net8.0 + Figlet + Demonstrates how to render FIGlet text. + Widgets + + + + + + + diff --git a/examples/Console/Figlet/Program.cs b/examples/Console/Figlet/Program.cs new file mode 100644 index 0000000..a831014 --- /dev/null +++ b/examples/Console/Figlet/Program.cs @@ -0,0 +1,13 @@ +using Spectre.Console; + +namespace Figlet; + +public static class Program +{ + public static void Main(string[] args) + { + AnsiConsole.Write(new FigletText("Left aligned").LeftJustified().Color(Color.Red)); + AnsiConsole.Write(new FigletText("Centered").Centered().Color(Color.Green)); + AnsiConsole.Write(new FigletText("Right aligned").RightJustified().Color(Color.Blue)); + } +} diff --git a/examples/Console/Grids/Grids.csproj b/examples/Console/Grids/Grids.csproj new file mode 100644 index 0000000..f7c5527 --- /dev/null +++ b/examples/Console/Grids/Grids.csproj @@ -0,0 +1,15 @@ + + + + Exe + net8.0 + Grids + Demonstrates how to render grids in a console. + Widgets + + + + + + + diff --git a/examples/Console/Grids/Program.cs b/examples/Console/Grids/Program.cs new file mode 100644 index 0000000..ee9d0e9 --- /dev/null +++ b/examples/Console/Grids/Program.cs @@ -0,0 +1,23 @@ +using Spectre.Console; + +namespace Grids; + +public static class Program +{ + public static void Main() + { + AnsiConsole.WriteLine(); + AnsiConsole.MarkupLine("Usage: [grey]dotnet [blue]run[/] [[options]] [[[[--]] ...]]]][/]"); + AnsiConsole.WriteLine(); + + var grid = new Grid(); + grid.AddColumn(new GridColumn().NoWrap()); + grid.AddColumn(new GridColumn().PadLeft(2)); + grid.AddRow("Options:"); + grid.AddRow(" [blue]-h[/], [blue]--help[/]", "Show command line help."); + grid.AddRow(" [blue]-c[/], [blue]--configuration[/] ", "The configuration to run for."); + grid.AddRow(" [blue]-v[/], [blue]--verbosity[/] ", "Set the [grey]MSBuild[/] verbosity level."); + + AnsiConsole.Write(grid); + } +} diff --git a/examples/Console/Info/Info.csproj b/examples/Console/Info/Info.csproj new file mode 100644 index 0000000..c06d19a --- /dev/null +++ b/examples/Console/Info/Info.csproj @@ -0,0 +1,15 @@ + + + + Exe + net8.0 + Info + Displays the capabilities of the current console. + Misc + + + + + + + diff --git a/examples/Console/Info/Program.cs b/examples/Console/Info/Program.cs new file mode 100644 index 0000000..8bd5177 --- /dev/null +++ b/examples/Console/Info/Program.cs @@ -0,0 +1,33 @@ +using Spectre.Console; + +namespace Info; + +public static class Program +{ + public static void Main() + { + var grid = new Grid() + .AddColumn(new GridColumn().NoWrap().PadRight(4)) + .AddColumn() + .AddRow("[b]Enrichers[/]", string.Join(", ", AnsiConsole.Profile.Enrichers)) + .AddRow("[b]Color system[/]", $"{AnsiConsole.Profile.Capabilities.ColorSystem}") + .AddRow("[b]Unicode?[/]", $"{YesNo(AnsiConsole.Profile.Capabilities.Unicode)}") + .AddRow("[b]Supports ansi?[/]", $"{YesNo(AnsiConsole.Profile.Capabilities.Ansi)}") + .AddRow("[b]Supports links?[/]", $"{YesNo(AnsiConsole.Profile.Capabilities.Links)}") + .AddRow("[b]Legacy console?[/]", $"{YesNo(AnsiConsole.Profile.Capabilities.Legacy)}") + .AddRow("[b]Interactive?[/]", $"{YesNo(AnsiConsole.Profile.Capabilities.Interactive)}") + .AddRow("[b]Terminal?[/]", $"{YesNo(AnsiConsole.Profile.Out.IsTerminal)}") + .AddRow("[b]Buffer width[/]", $"{AnsiConsole.Console.Profile.Width}") + .AddRow("[b]Buffer height[/]", $"{AnsiConsole.Console.Profile.Height}") + .AddRow("[b]Encoding[/]", $"{AnsiConsole.Console.Profile.Encoding.EncodingName}"); + + AnsiConsole.Write( + new Panel(grid) + .Header("Information")); + } + + private static string YesNo(bool value) + { + return value ? "Yes" : "No"; + } +} diff --git a/examples/Console/Json/Json.csproj b/examples/Console/Json/Json.csproj new file mode 100644 index 0000000..43145c7 --- /dev/null +++ b/examples/Console/Json/Json.csproj @@ -0,0 +1,15 @@ + + + + Exe + net8.0 + Json + Demonstrates how to print syntax highlighted JSON. + Widgets + + + + + + + diff --git a/examples/Console/Json/Program.cs b/examples/Console/Json/Program.cs new file mode 100644 index 0000000..2dc4df6 --- /dev/null +++ b/examples/Console/Json/Program.cs @@ -0,0 +1,36 @@ +using Spectre.Console; +using Spectre.Console.Json; + +namespace Json; + +public static class Program +{ + public static void Main() + { + var json = new JsonText( + """ + { + "hello": 32, + "world": { + "foo": 21, + "bar": 255, + "baz": [ + 0.32, 0.33e-32, + 0.42e32, 0.55e+32, + { + "hello": "world", + "lol": null + } + ] + } + } + """); + + AnsiConsole.Write( + new Panel(json) + .Header("Some JSON in a panel") + .Collapse() + .RoundedBorder() + .BorderColor(Color.Yellow)); + } +} diff --git a/examples/Console/Layout/Layout.csproj b/examples/Console/Layout/Layout.csproj new file mode 100644 index 0000000..344ec91 --- /dev/null +++ b/examples/Console/Layout/Layout.csproj @@ -0,0 +1,15 @@ + + + + Exe + net8.0 + Layout + Demonstrates how to use layouts. + Widgets + + + + + + + diff --git a/examples/Console/Layout/Program.cs b/examples/Console/Layout/Program.cs new file mode 100644 index 0000000..835b614 --- /dev/null +++ b/examples/Console/Layout/Program.cs @@ -0,0 +1,60 @@ +using System; +using Spectre.Console; + +namespace Layouts; + +public static class Program +{ + public static void Main() + { + var layout = CreateLayout(); + AnsiConsole.Write(layout); + + Console.ReadKey(true); + } + + private static Layout CreateLayout() + { + var layout = new Layout(); + + layout.SplitRows( + new Layout("Top") + .SplitColumns( + new Layout("Left") + .SplitRows( + new Layout("LeftTop"), + new Layout("LeftBottom")), + new Layout("Right").Ratio(2), + new Layout("RightRight").Size(3)), + new Layout("Bottom")); + + layout["LeftBottom"].Update( + new Panel("[blink]PRESS ANY KEY TO QUIT[/]") + .Expand() + .BorderColor(Color.Yellow) + .Padding(0, 0)); + + layout["Right"].Update( + new Panel( + new Table() + .AddColumns("[blue]Qux[/]", "[green]Corgi[/]") + .AddRow("9", "8") + .AddRow("7", "6") + .Expand()) + .Header("A [yellow]Table[/] in a [blue]Panel[/] (Ratio=2)") + .Expand()); + + layout["RightRight"].Update( + new Panel("Explicit-size-is-[yellow]3[/]") + .BorderColor(Color.Yellow) + .Padding(0, 0)); + + layout["Bottom"].Update( + new Panel( + new FigletText("Hello World")) + .Header("Some [green]Figlet[/] text") + .Expand()); + + return layout; + } +} diff --git a/examples/Console/Links/Links.csproj b/examples/Console/Links/Links.csproj new file mode 100644 index 0000000..2165300 --- /dev/null +++ b/examples/Console/Links/Links.csproj @@ -0,0 +1,15 @@ + + + + Exe + net8.0 + Links + Demonstrates how to render links in a console. + Misc + + + + + + + diff --git a/examples/Console/Links/Program.cs b/examples/Console/Links/Program.cs new file mode 100644 index 0000000..6b1a459 --- /dev/null +++ b/examples/Console/Links/Program.cs @@ -0,0 +1,20 @@ +using Spectre.Console; + +namespace Links; + +public static class Program +{ + public static void Main() + { + if (AnsiConsole.Profile.Capabilities.Links) + { + AnsiConsole.MarkupLine("[link=https://patriksvensson.se]Click to visit my blog[/]!"); + } + else + { + AnsiConsole.MarkupLine("[red]It looks like your terminal doesn't support links[/]"); + AnsiConsole.WriteLine(); + AnsiConsole.MarkupLine("[yellow](╯°□°)╯[/]︵ [blue]┻━┻[/]"); + } + } +} diff --git a/examples/Console/Live/Live.csproj b/examples/Console/Live/Live.csproj new file mode 100644 index 0000000..c69b546 --- /dev/null +++ b/examples/Console/Live/Live.csproj @@ -0,0 +1,15 @@ + + + + Exe + net8.0 + Live + Demonstrates how to do live updates. + Live + + + + + + + diff --git a/examples/Console/Live/Program.cs b/examples/Console/Live/Program.cs new file mode 100644 index 0000000..4d5bdbc --- /dev/null +++ b/examples/Console/Live/Program.cs @@ -0,0 +1,81 @@ +using System; +using System.Threading; +using Spectre.Console; + +namespace Live; + +public static class Program +{ + public static void Main() + { + var table = new Table().Centered(); + + // Animate + AnsiConsole.Live(table) + .AutoClear(false) + .Overflow(VerticalOverflow.Ellipsis) + .Cropping(VerticalOverflowCropping.Top) + .Start(ctx => + { + void Update(int delay, Action action) + { + action(); + ctx.Refresh(); + Thread.Sleep(delay); + } + + // Columns + Update(230, () => table.AddColumn("Release date")); + Update(230, () => table.AddColumn("Title")); + Update(230, () => table.AddColumn("Budget")); + Update(230, () => table.AddColumn("Opening Weekend")); + Update(230, () => table.AddColumn("Box office")); + + // Rows + Update(70, () => table.AddRow("May 25, 1977", "[yellow]Star Wars[/] [grey]Ep.[/] [u]IV[/]", "$11,000,000", "$1,554,475", "$775,398,007")); + Update(70, () => table.AddRow("May 21, 1980", "[yellow]Star Wars[/] [grey]Ep.[/] [u]V[/]", "$18,000,000", "$4,910,483", "$547,969,004")); + Update(70, () => table.AddRow("May 25, 1983", "[yellow]Star Wars[/] [grey]Ep.[/] [u]VI[/]", "$32,500,000", "$23,019,618", "$475,106,177")); + Update(70, () => table.AddRow("May 19, 1999", "[yellow]Star Wars[/] [grey]Ep.[/] [u]I[/]", "$115,000,000", "$64,810,870", "$1,027,044,677")); + Update(70, () => table.AddRow("May 16, 2002", "[yellow]Star Wars[/] [grey]Ep.[/] [u]II[/]", "$115,000,000", "$80,027,814", "$649,436,358")); + Update(70, () => table.AddRow("May 19, 2005", "[yellow]Star Wars[/] [grey]Ep.[/] [u]III[/]", "$113,000,000", "$108,435,841", "$850,035,635")); + Update(70, () => table.AddRow("Dec 18, 2015", "[yellow]Star Wars[/] [grey]Ep.[/] [u]VII[/]", "$245,000,000", "$247,966,675", "$2,068,223,624")); + Update(70, () => table.AddRow("Dec 15, 2017", "[yellow]Star Wars[/] [grey]Ep.[/] [u]VIII[/]", "$317,000,000", "$220,009,584", "$1,333,539,889")); + Update(70, () => table.AddRow("Dec 20, 2019", "[yellow]Star Wars[/] [grey]Ep.[/] [u]IX[/]", "$245,000,000", "$177,383,864", "$1,074,114,248")); + + // Column footer + Update(230, () => table.Columns[2].Footer("$1,633,000,000")); + Update(230, () => table.Columns[3].Footer("$928,119,224")); + Update(400, () => table.Columns[4].Footer("$10,318,030,576")); + + // Column alignment + Update(230, () => table.Columns[2].RightAligned()); + Update(230, () => table.Columns[3].RightAligned()); + Update(400, () => table.Columns[4].RightAligned()); + + // Column titles + Update(70, () => table.Columns[0].Header("[bold]Release date[/]")); + Update(70, () => table.Columns[1].Header("[bold]Title[/]")); + Update(70, () => table.Columns[2].Header("[red bold]Budget[/]")); + Update(70, () => table.Columns[3].Header("[green bold]Opening Weekend[/]")); + Update(400, () => table.Columns[4].Header("[blue bold]Box office[/]")); + + // Footers + Update(70, () => table.Columns[2].Footer("[red bold]$1,633,000,000[/]")); + Update(70, () => table.Columns[3].Footer("[green bold]$928,119,224[/]")); + Update(400, () => table.Columns[4].Footer("[blue bold]$10,318,030,576[/]")); + + // Title + Update(500, () => table.Title("Star Wars Movies")); + Update(400, () => table.Title("[[ [yellow]Star Wars Movies[/] ]]")); + + // Borders + Update(230, () => table.BorderColor(Color.Yellow)); + Update(230, () => table.MinimalBorder()); + Update(230, () => table.SimpleBorder()); + Update(230, () => table.SimpleHeavyBorder()); + + // Caption + Update(400, () => table.Caption("[[ [blue]THE END[/] ]]")); + }); + } +} diff --git a/examples/Console/LiveTable/LiveTable.csproj b/examples/Console/LiveTable/LiveTable.csproj new file mode 100644 index 0000000..56272a4 --- /dev/null +++ b/examples/Console/LiveTable/LiveTable.csproj @@ -0,0 +1,15 @@ + + + + Exe + net8.0 + LiveTable + Demonstrates how to do live updates in a table. + Live + + + + + + + diff --git a/examples/Console/LiveTable/Program.cs b/examples/Console/LiveTable/Program.cs new file mode 100644 index 0000000..b3fc253 --- /dev/null +++ b/examples/Console/LiveTable/Program.cs @@ -0,0 +1,86 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using Spectre.Console; + +namespace LiveTable; + +public static class Program +{ + private const int NumberOfRows = 10; + + private static readonly Random _random = new(); + private static readonly string[] _exchanges = new string[] + { + "SGD", "SEK", "PLN", + "MYR", "EUR", "USD", + "AUD", "JPY", "CNH", + "HKD", "CAD", "INR", + "DKK", "GBP", "RUB", + "NZD", "MXN", "IDR", + "TWD", "THB", "VND", + }; + + public static async Task Main(string[] args) + { + var table = new Table().Expand().BorderColor(Color.Grey); + table.AddColumn("[yellow]Source currency[/]"); + table.AddColumn("[yellow]Destination currency[/]"); + table.AddColumn("[yellow]Exchange rate[/]"); + + AnsiConsole.MarkupLine("Press [yellow]CTRL+C[/] to exit"); + + await AnsiConsole.Live(table) + .AutoClear(false) + .Overflow(VerticalOverflow.Ellipsis) + .Cropping(VerticalOverflowCropping.Bottom) + .StartAsync(async ctx => + { + // Add some initial rows + foreach (var _ in Enumerable.Range(0, NumberOfRows)) + { + AddExchangeRateRow(table); + } + + // Continously update the table + while (true) + { + // More rows than we want? + if (table.Rows.Count > NumberOfRows) + { + // Remove the first one + table.Rows.RemoveAt(0); + } + + // Add a new row + AddExchangeRateRow(table); + + // Refresh and wait for a while + ctx.Refresh(); + await Task.Delay(400); + } + }); + } + + private static void AddExchangeRateRow(Table table) + { + var (source, destination, rate) = GetExchangeRate(); + table.AddRow( + source, destination, + _random.NextDouble() > 0.35D ? $"[green]{rate}[/]" : $"[red]{rate}[/]"); + } + + private static (string Source, string Destination, double Rate) GetExchangeRate() + { + var source = _exchanges[_random.Next(0, _exchanges.Length)]; + var dest = _exchanges[_random.Next(0, _exchanges.Length)]; + var rate = 200 / ((_random.NextDouble() * 320) + 1); + + while (source == dest) + { + dest = _exchanges[_random.Next(0, _exchanges.Length)]; + } + + return (source, dest, rate); + } +} diff --git a/examples/Console/Minimal/GlobalUsings.cs b/examples/Console/Minimal/GlobalUsings.cs new file mode 100644 index 0000000..6c76d1f --- /dev/null +++ b/examples/Console/Minimal/GlobalUsings.cs @@ -0,0 +1,2 @@ +global using Spectre.Console; +global using static Spectre.Console.AnsiConsole; \ No newline at end of file diff --git a/examples/Console/Minimal/Minimal.csproj b/examples/Console/Minimal/Minimal.csproj new file mode 100644 index 0000000..b0f2cfe --- /dev/null +++ b/examples/Console/Minimal/Minimal.csproj @@ -0,0 +1,16 @@ + + + + Exe + net8.0 + enable + Minimal + Demonstrates a minimal console application. + Live + + + + + + + diff --git a/examples/Console/Minimal/Program.cs b/examples/Console/Minimal/Program.cs new file mode 100644 index 0000000..6de47a3 --- /dev/null +++ b/examples/Console/Minimal/Program.cs @@ -0,0 +1,12 @@ +// Write a markup line to the console +MarkupLine("[yellow]Hello[/], [blue]World[/]!"); + +// Write text to the console +WriteLine("Hello, World!"); + +// Write a table to the console +Write(new Table() + .RoundedBorder() + .AddColumns("[red]Greeting[/]", "[red]Subject[/]") + .AddRow("[yellow]Hello[/]", "World") + .AddRow("[green]Oh hi[/]", "[blue u]Mark[/]")); \ No newline at end of file diff --git a/examples/Console/Panels/Panels.csproj b/examples/Console/Panels/Panels.csproj new file mode 100644 index 0000000..ceab0e6 --- /dev/null +++ b/examples/Console/Panels/Panels.csproj @@ -0,0 +1,15 @@ + + + + Exe + net8.0 + Panels + Demonstrates how to render items in panels. + Widgets + + + + + + + diff --git a/examples/Console/Panels/Program.cs b/examples/Console/Panels/Program.cs new file mode 100644 index 0000000..dc9ecb8 --- /dev/null +++ b/examples/Console/Panels/Program.cs @@ -0,0 +1,41 @@ +using Spectre.Console; + +namespace Panels; + +public static class Program +{ + public static void Main() + { + var content = new Markup( + "[underline]I[/] heard [underline on blue]you[/] like panels\n\n\n\n" + + "So I put a panel in a panel").Centered(); + + AnsiConsole.Write( + new Panel( + new Panel(content) + .Border(BoxBorder.Rounded))); + + // Left adjusted panel with text + AnsiConsole.Write( + new Panel(new Text("Left adjusted\nLeft").LeftJustified()) + .Expand() + .SquareBorder() + .Header("[red]Left[/]")); + + // Centered ASCII panel with text + AnsiConsole.Write( + new Panel(new Text("Centered\nCenter").Centered()) + .Expand() + .AsciiBorder() + .Header("[green]Center[/]") + .HeaderAlignment(Justify.Center)); + + // Right adjusted, rounded panel with text + AnsiConsole.Write( + new Panel(new Text("Right adjusted\nRight").RightJustified()) + .Expand() + .RoundedBorder() + .Header("[blue]Right[/]") + .HeaderAlignment(Justify.Right)); + } +} diff --git a/examples/Console/Paths/Paths.csproj b/examples/Console/Paths/Paths.csproj new file mode 100644 index 0000000..cad7fcc --- /dev/null +++ b/examples/Console/Paths/Paths.csproj @@ -0,0 +1,15 @@ + + + + Exe + net8.0 + Paths + Demonstrates how to render paths. + Widgets + + + + + + + diff --git a/examples/Console/Paths/Program.cs b/examples/Console/Paths/Program.cs new file mode 100644 index 0000000..ff88429 --- /dev/null +++ b/examples/Console/Paths/Program.cs @@ -0,0 +1,67 @@ +using System; +using System.Threading; +using Spectre.Console; + +namespace Paths; + +public static class Program +{ + public static void Main() + { + var windowsPath = @"C:\This is\A\Super Long\Windows\Path\That\Goes\On And On\And\Never\Seems\To\Stop\But\At\Some\Point\It\Must\I\Guess.txt"; + var unixPath = @"//This is/A/Super Long/Unix/Path/That/Goes/On And On/And/Never/Seems/To/Stop/But/At/Some/Point/It/Must/I/Guess.txt"; + + AnsiConsole.WriteLine(); + WritePlain(windowsPath, unixPath); + + AnsiConsole.WriteLine(); + WriteColorized(windowsPath, unixPath); + + AnsiConsole.WriteLine(); + WriteAligned(windowsPath); + } + + private static void WritePlain(string windowsPath, string unixPath) + { + var table = new Table().BorderColor(Color.Grey).Title("Plain").RoundedBorder(); + table.AddColumns("[grey]OS[/]", "[grey]Path[/]"); + table.AddRow(new Text("Windows"), new TextPath(windowsPath)); + table.AddRow(new Text("Unix"), new TextPath(unixPath)); + + AnsiConsole.Write(table); + } + + private static void WriteColorized(string windowsPath, string unixPath) + { + var table = new Table().BorderColor(Color.Grey).Title("Colorized").RoundedBorder(); + table.AddColumns("[grey]OS[/]", "[grey]Path[/]"); + + table.AddRow(new Text("Windows"), + new TextPath(windowsPath) + .RootColor(Color.Blue) + .SeparatorColor(Color.Yellow) + .StemColor(Color.Red) + .LeafColor(Color.Green)); + + table.AddRow(new Text("Unix"), + new TextPath(unixPath) + .RootColor(Color.Blue) + .SeparatorColor(Color.Yellow) + .StemColor(Color.Red) + .LeafColor(Color.Green)); + + AnsiConsole.Write(table); + } + + private static void WriteAligned(string path) + { + var table = new Table().BorderColor(Color.Grey).Title("Aligned").RoundedBorder(); + table.AddColumns("[grey]Alignment[/]", "[grey]Path[/]"); + + table.AddRow(new Text("Left"), new TextPath(path).LeftJustified()); + table.AddRow(new Text("Center"), new TextPath(path).Centered()); + table.AddRow(new Text("Right"), new TextPath(path).RightJustified()); + + AnsiConsole.Write(table); + } +} diff --git a/examples/Console/Progress/DescriptionGenerator.cs b/examples/Console/Progress/DescriptionGenerator.cs new file mode 100644 index 0000000..0bdb064 --- /dev/null +++ b/examples/Console/Progress/DescriptionGenerator.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; + +namespace Progress; + +public static class DescriptionGenerator +{ + private static readonly string[] _verbs = new[] { "Downloading", "Rerouting", "Retriculating", "Collapsing", "Folding", "Solving", "Colliding", "Measuring" }; + private static readonly string[] _nouns = new[] { "internet", "splines", "space", "capacitators", "quarks", "algorithms", "data structures", "spacetime" }; + + private static readonly Random _random; + private static readonly HashSet _used; + + static DescriptionGenerator() + { + _random = new Random(DateTime.Now.Millisecond); + _used = new HashSet(); + } + + public static bool TryGenerate(out string name) + { + var iterations = 0; + while (iterations < 25) + { + name = Generate(); + if (!_used.Contains(name)) + { + _used.Add(name); + return true; + } + + iterations++; + } + + name = Generate(); + return false; + } + + public static string Generate() + { + return _verbs[_random.Next(0, _verbs.Length)] + + " " + _nouns[_random.Next(0, _nouns.Length)]; + } +} diff --git a/examples/Console/Progress/Program.cs b/examples/Console/Progress/Program.cs new file mode 100644 index 0000000..268c826 --- /dev/null +++ b/examples/Console/Progress/Program.cs @@ -0,0 +1,121 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using Spectre.Console; +using Spectre.Console.Rendering; + +namespace Progress; + +public static class Program +{ + public static void Main() + { + AnsiConsole.MarkupLine("[yellow]Initializing warp drive[/]..."); + + // Show progress + AnsiConsole.Progress() + .AutoClear(false) + .Columns(new ProgressColumn[] + { + new TaskDescriptionColumn(), // Task description + new ProgressBarColumn(), // Progress bar + new PercentageColumn(), // Percentage + new RemainingTimeColumn(), // Remaining time + new SpinnerColumn(), // Spinner + }) + .UseRenderHook((renderable, tasks) => RenderHook(tasks, renderable)) + .Start(ctx => + { + var random = new Random(DateTime.Now.Millisecond); + + // Create some tasks + var tasks = CreateTasks(ctx, random); + var warpTask = ctx.AddTask("Going to warp", autoStart: false).IsIndeterminate(); + + // Wait for all tasks (except the indeterminate one) to complete + while (!ctx.IsFinished) + { + // Increment progress + foreach (var (task, increment) in tasks) + { + task.Increment(random.NextDouble() * increment); + } + + // Write some random things to the terminal + if (random.NextDouble() < 0.1) + { + WriteLogMessage(); + } + + // Simulate some delay + Thread.Sleep(100); + } + + // Now start the "warp" task + warpTask.StartTask(); + warpTask.IsIndeterminate(false); + while (!ctx.IsFinished) + { + warpTask.Increment(12 * random.NextDouble()); + + // Simulate some delay + Thread.Sleep(100); + } + }); + + // Done + AnsiConsole.MarkupLine("[green]Done![/]"); + } + + private static IRenderable RenderHook(IReadOnlyList tasks, IRenderable renderable) + { + var header = new Panel("Going on a :rocket:, we're going to the :crescent_moon:").Expand().RoundedBorder(); + var footer = new Rows( + new Rule(), + new Markup( + $"[blue]{tasks.Count}[/] total tasks. [green]{tasks.Count(i => i.IsFinished)}[/] complete.") + ); + + const string ESC = "\u001b"; + string escapeSequence; + if (tasks.All(i => i.IsFinished)) + { + escapeSequence = $"{ESC}]]9;4;0;100{ESC}\\"; + } + else + { + var total = tasks.Sum(i => i.MaxValue); + var done = tasks.Sum(i => i.Value); + var percent = (int)(done / total * 100); + escapeSequence = $"{ESC}]]9;4;1;{percent}{ESC}\\"; + } + + var middleContent = new Grid().AddColumns(new GridColumn(), new GridColumn().Width(20)); + middleContent.AddRow(renderable, new FigletText(tasks.Count(i => i.IsFinished == false).ToString())); + + return new Rows(header, middleContent, footer, new ControlCode(escapeSequence)); + } + + private static List<(ProgressTask Task, int Delay)> CreateTasks(ProgressContext progress, Random random) + { + var tasks = new List<(ProgressTask, int)>(); + while (tasks.Count < 5) + { + if (DescriptionGenerator.TryGenerate(out var name)) + { + tasks.Add((progress.AddTask(name), random.Next(2, 10))); + } + } + + return tasks; + } + + private static void WriteLogMessage() + { + AnsiConsole.MarkupLine( + "[grey]LOG:[/] " + + DescriptionGenerator.Generate() + + "[grey]...[/]"); + } +} diff --git a/examples/Console/Progress/Progress.csproj b/examples/Console/Progress/Progress.csproj new file mode 100644 index 0000000..d231a4b --- /dev/null +++ b/examples/Console/Progress/Progress.csproj @@ -0,0 +1,19 @@ + + + + Exe + net8.0 + Progress + Demonstrates how to show progress bars. + Status + + + + + + + + + + + diff --git a/examples/Console/Prompt/Program.cs b/examples/Console/Prompt/Program.cs new file mode 100644 index 0000000..216d029 --- /dev/null +++ b/examples/Console/Prompt/Program.cs @@ -0,0 +1,182 @@ +using Spectre.Console; + +namespace Prompt +{ + public static class Program + { + public static void Main(string[] args) + { + // Check if we can accept key strokes + if (!AnsiConsole.Profile.Capabilities.Interactive) + { + AnsiConsole.MarkupLine("[red]Environment does not support interaction.[/]"); + return; + } + + // Confirmation + if (!AskConfirmation()) + { + return; + } + + // Ask the user for some different things + WriteDivider("Strings"); + var name = AskName(); + + WriteDivider("Lists"); + var fruit = AskFruit(); + + WriteDivider("Choices"); + var sport = AskSport(); + + WriteDivider("Integers"); + var age = AskAge(); + + WriteDivider("Secrets"); + var password = AskPassword(); + + WriteDivider("Mask"); + var mask = AskPasswordWithCustomMask(); + + WriteDivider("Null Mask"); + var nullMask = AskPasswordWithNullMask(); + + WriteDivider("Optional"); + var color = AskColor(); + + // Summary + AnsiConsole.WriteLine(); + AnsiConsole.Write(new Rule("[yellow]Results[/]").RuleStyle("grey").LeftJustified()); + AnsiConsole.Write(new Table().AddColumns("[grey]Question[/]", "[grey]Answer[/]") + .RoundedBorder() + .BorderColor(Color.Grey) + .AddRow("[grey]Name[/]", name) + .AddRow("[grey]Favorite fruit[/]", fruit) + .AddRow("[grey]Favorite sport[/]", sport) + .AddRow("[grey]Age[/]", age.ToString()) + .AddRow("[grey]Password[/]", password) + .AddRow("[grey]Mask[/]", mask) + .AddRow("[grey]Null Mask[/]", nullMask) + .AddRow("[grey]Favorite color[/]", string.IsNullOrEmpty(color) ? "Unknown" : color)); + } + + private static void WriteDivider(string text) + { + AnsiConsole.WriteLine(); + AnsiConsole.Write(new Rule($"[yellow]{text}[/]").RuleStyle("grey").LeftJustified()); + } + + public static bool AskConfirmation() + { + if (!AnsiConsole.Confirm("Run prompt example?")) + { + AnsiConsole.MarkupLine("Ok... :("); + return false; + } + + return true; + } + + public static string AskName() + { + var name = AnsiConsole.Ask("What's your [green]name[/]?"); + return name; + } + + public static string AskFruit() + { + var favorites = AnsiConsole.Prompt( + new MultiSelectionPrompt() + .PageSize(10) + .Title("What are your [green]favorite fruits[/]?") + .MoreChoicesText("[grey](Move up and down to reveal more fruits)[/]") + .InstructionsText("[grey](Press [blue][/] to toggle a fruit, [green][/] to accept)[/]") + .AddChoiceGroup("Berries", new[] + { + "Blackcurrant", "Blueberry", "Cloudberry", + "Elderberry", "Honeyberry", "Mulberry" + }) + .AddChoices(new[] + { + "Apple", "Apricot", "Avocado", "Banana", + "Cherry", "Cocunut", "Date", "Dragonfruit", "Durian", + "Egg plant", "Fig", "Grape", "Guava", + "Jackfruit", "Jambul", "Kiwano", "Kiwifruit", "Lime", "Lylo", + "Lychee", "Melon", "Nectarine", "Orange", "Olive" + })); + + var fruit = favorites.Count == 1 ? favorites[0] : null; + if (string.IsNullOrWhiteSpace(fruit)) + { + fruit = AnsiConsole.Prompt( + new SelectionPrompt() + .EnableSearch() + .Title("Ok, but if you could only choose [green]one[/]?") + .MoreChoicesText("[grey](Move up and down to reveal more fruits)[/]") + .AddChoices(favorites)); + } + + AnsiConsole.MarkupLine("You selected: [yellow]{0}[/]", fruit); + return fruit; + } + + public static string AskSport() + { + return AnsiConsole.Prompt( + new TextPrompt("What's your [green]favorite sport[/]?") + .InvalidChoiceMessage("[red]That's not a sport![/]") + .DefaultValue("Sport?") + .AddChoice("Soccer") + .AddChoice("Hockey") + .AddChoice("Basketball")); + } + + public static int AskAge() + { + return AnsiConsole.Prompt( + new TextPrompt("How [green]old[/] are you?") + .PromptStyle("green") + .ValidationErrorMessage("[red]That's not a valid age[/]") + .Validate(age => + { + return age switch + { + <= 0 => ValidationResult.Error("[red]You must at least be 1 years old[/]"), + >= 123 => ValidationResult.Error("[red]You must be younger than the oldest person alive[/]"), + _ => ValidationResult.Success(), + }; + })); + } + + public static string AskPassword() + { + return AnsiConsole.Prompt( + new TextPrompt("Enter [green]password[/]?") + .PromptStyle("red") + .Secret()); + } + + public static string AskPasswordWithCustomMask() + { + return AnsiConsole.Prompt( + new TextPrompt("Enter [green]password[/]?") + .PromptStyle("red") + .Secret('-')); + } + + public static string AskPasswordWithNullMask() + { + return AnsiConsole.Prompt( + new TextPrompt("Enter [green]password[/]?") + .PromptStyle("red") + .Secret(null)); + } + + public static string AskColor() + { + return AnsiConsole.Prompt( + new TextPrompt("[grey][[Optional]][/] What is your [green]favorite color[/]?") + .AllowEmpty()); + } + } +} \ No newline at end of file diff --git a/examples/Console/Prompt/Prompt.csproj b/examples/Console/Prompt/Prompt.csproj new file mode 100644 index 0000000..137ee61 --- /dev/null +++ b/examples/Console/Prompt/Prompt.csproj @@ -0,0 +1,16 @@ + + + + Exe + net8.0 + 9 + Prompt + Demonstrates how to get input from a user. + Misc + + + + + + + diff --git a/examples/Console/Rules/Program.cs b/examples/Console/Rules/Program.cs new file mode 100644 index 0000000..442ed3f --- /dev/null +++ b/examples/Console/Rules/Program.cs @@ -0,0 +1,42 @@ +using Spectre.Console; + +namespace Rules; + +public static class Program +{ + public static void Main(string[] args) + { + // No title + Render( + new Rule() + .RuleStyle(Style.Parse("yellow")) + .AsciiBorder() + .LeftJustified()); + + // Left aligned title + Render( + new Rule("[blue]Left aligned[/]") + .RuleStyle(Style.Parse("red")) + .DoubleBorder() + .LeftJustified()); + + // Centered title + Render( + new Rule("[green]Centered[/]") + .RuleStyle(Style.Parse("green")) + .HeavyBorder() + .Centered()); + + // Right aligned title + Render( + new Rule("[red]Right aligned[/]") + .RuleStyle(Style.Parse("blue")) + .RightJustified()); + } + + private static void Render(Rule rule) + { + AnsiConsole.Write(rule); + AnsiConsole.WriteLine(); + } +} diff --git a/examples/Console/Rules/Rules.csproj b/examples/Console/Rules/Rules.csproj new file mode 100644 index 0000000..8d0660f --- /dev/null +++ b/examples/Console/Rules/Rules.csproj @@ -0,0 +1,15 @@ + + + + Exe + net8.0 + Rules + Demonstrates how to render horizontal rules (lines). + Widgets + + + + + + + diff --git a/examples/Console/Showcase/ExceptionGenerator.cs b/examples/Console/Showcase/ExceptionGenerator.cs new file mode 100644 index 0000000..f3ddd99 --- /dev/null +++ b/examples/Console/Showcase/ExceptionGenerator.cs @@ -0,0 +1,29 @@ +using System; + +namespace Showcase; + +public static class ExceptionGenerator +{ + public static Exception GenerateException() + { + try + { + SomeOperation(); + throw new InvalidOperationException(); + } + catch (Exception ex) + { + return ex; + } + } + + private static void SomeOperation() + { + SomeOperationGoingWrong(); + } + + private static void SomeOperationGoingWrong() + { + throw new InvalidOperationException("Something went very wrong!"); + } +} diff --git a/examples/Console/Showcase/Program.cs b/examples/Console/Showcase/Program.cs new file mode 100644 index 0000000..abb1e23 --- /dev/null +++ b/examples/Console/Showcase/Program.cs @@ -0,0 +1,154 @@ +using Spectre.Console; +using Spectre.Console.Examples; +using Spectre.Console.Rendering; + +namespace Showcase; + +public static partial class Program +{ + public static void Main() + { + var table = new Table().HideHeaders().NoBorder(); + table.Title("[u][yellow]Spectre.Console[/] [b]Features[/][/]"); + table.AddColumn("Feature", c => c.NoWrap().RightAligned().Width(10).PadRight(3)); + table.AddColumn("Demonstration", c => c.PadRight(0)); + table.AddEmptyRow(); + + // Colors + table.AddRow( + new Markup("[red]Colors[/]"), + GetColorTable()); + + // Styles + table.AddEmptyRow(); + table.AddRow( + new Markup("[red]OS[/]"), + new Grid().Expand().AddColumns(3) + .AddRow( + "[bold green]Windows[/]", + "[bold blue]macOS[/]", + "[bold yellow]Linux[/]")); + + // Styles + table.AddEmptyRow(); + table.AddRow( + "[red]Styles[/]", + "All ansi styles: [bold]bold[/], [dim]dim[/], [italic]italic[/], [underline]underline[/], " + + "[strikethrough]strikethrough[/], [reverse]reverse[/], and even [blink]blink[/]."); + + // Text + table.AddEmptyRow(); + table.AddRow( + new Markup("[red]Text[/]"), + new Markup("Word wrap text. Justify [green]left[/], [yellow]center[/] or [blue]right[/].")); + + table.AddEmptyRow(); + table.AddRow( + Text.Empty, + GetTextGrid()); + + // Markup + table.AddEmptyRow(); + table.AddRow( + "[red]Markup[/]", + "[bold purple]Spectre.Console[/] supports a simple [i]bbcode[/] like " + + "[b]markup[/] for [yellow]color[/], [underline]style[/], and emoji! " + + ":thumbs_up: :red_apple: :ant: :bear: :baguette_bread: :bus:"); + + // Trees and tables + table.AddEmptyRow(); + table.AddRow( + new Markup("[red]Tables and Trees[/]"), + GetTreeTable()); + + // Charts + table.AddRow( + new Markup("[red]Charts[/]"), + new Grid().Collapse().AddColumns(2).AddRow( + new Panel(GetBreakdownChart()).BorderColor(Color.Grey), + new Panel(GetBarChart()).BorderColor(Color.Grey))); + + + // Exceptions + table.AddEmptyRow(); + table.AddRow( + new Markup("[red]Exceptions[/]"), + ExceptionGenerator.GenerateException().GetRenderable()); + + // Much more + table.AddEmptyRow(); + table.AddRow( + "[red]+ Much more![/]", + "Tables, Grids, Trees, Progress bars, Status, Bar charts, Calendars, Figlet, Images, Text prompts, " + + "List boxes, Separators, Pretty exceptions, Canvas, CLI parsing"); + table.AddEmptyRow(); + + // Render the table + AnsiConsole.WriteLine(); + AnsiConsole.Write(table); + } + + private static IRenderable GetColorTable() + { + var colorTable = new Table().Collapse().HideHeaders().NoBorder(); + colorTable.AddColumn("Desc", c => c.PadRight(3)).AddColumn("Colors", c => c.PadRight(0)); + colorTable.AddRow( + new Markup( + "✓ [bold grey]NO_COLOR support[/]\n" + + "✓ [bold green]3-bit color[/]\n" + + "✓ [bold blue]4-bit color[/]\n" + + "✓ [bold purple]8-bit color[/]\n" + + "✓ [bold yellow]Truecolor (16.7 million)[/]\n" + + "✓ [bold aqua]Automatic color conversion[/]"), + new ColorBox(height: 6)); + + return colorTable; + } + + private static IRenderable GetTextGrid() + { + var loremTable = new Grid(); + var lorem = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque in metus sed sapien ultricies pretium a at justo. Maecenas luctus velit et auctor maximus."; + loremTable.AddColumn(new GridColumn().LeftAligned()); + loremTable.AddColumn(new GridColumn().Centered()); + loremTable.AddColumn(new GridColumn().RightAligned()); + loremTable.AddRow($"[green]{lorem}[/]", $"[yellow]{lorem}[/]", $"[blue]{lorem}[/]"); + return loremTable; + } + + private static IRenderable GetTreeTable() + { + var tree = new Tree("📁 src"); + tree.AddNode("📁 foo").AddNode("📄 bar.cs"); + tree.AddNode("📁 baz").AddNode("📁 qux").AddNode("📄 corgi.txt"); + tree.AddNode("📄 waldo.xml"); + + var table = new Table().SimpleBorder().BorderColor(Color.Grey); + table.AddColumn(new TableColumn("Overview")); + table.AddColumn(new TableColumn("").Footer("[grey]3 Files, 225 KiB[/]")); + table.AddRow(new Markup("[yellow]Files[/]"), tree); + + return new Table().RoundedBorder().Collapse().BorderColor(Color.Yellow) + .AddColumn("Foo").AddColumn("Bar") + .AddRow(new Text("Baz"), table) + .AddRow("Qux", "Corgi"); + } + + private static IRenderable GetBarChart() + { + return new BarChart() + .AddItem("Apple", 32, Color.Green) + .AddItem("Oranges", 13, Color.Orange1) + .AddItem("Bananas", 22, Color.Yellow); + } + + private static IRenderable GetBreakdownChart() + { + return new BreakdownChart() + .ShowPercentage() + .FullSize() + .AddItem("C#", 82, Color.Green) + .AddItem("PowerShell", 13, Color.Red) + .AddItem("Bash", 5, Color.Blue); + } +} diff --git a/examples/Console/Showcase/Showcase.csproj b/examples/Console/Showcase/Showcase.csproj new file mode 100644 index 0000000..f624fbe --- /dev/null +++ b/examples/Console/Showcase/Showcase.csproj @@ -0,0 +1,15 @@ + + + + Exe + net8.0 + Showcase + Demonstration of Spectre.Console. + Misc + + + + + + + diff --git a/examples/Console/Status/Program.cs b/examples/Console/Status/Program.cs new file mode 100644 index 0000000..ee28916 --- /dev/null +++ b/examples/Console/Status/Program.cs @@ -0,0 +1,69 @@ +using System.Threading; +using Spectre.Console; + +namespace Status; + +public static class Program +{ + public static void Main() + { + AnsiConsole.Status() + .AutoRefresh(true) + .Spinner(Spinner.Known.Default) + .Start("[yellow]Initializing warp drive[/]", ctx => + { + // Initialize + Thread.Sleep(3000); + WriteLogMessage("Starting gravimetric field displacement manifold"); + Thread.Sleep(1000); + WriteLogMessage("Warming up deuterium chamber"); + Thread.Sleep(2000); + WriteLogMessage("Generating antideuterium"); + + // Warp nacelles + Thread.Sleep(3000); + ctx.Spinner(Spinner.Known.BouncingBar); + ctx.Status("[bold blue]Unfolding warp nacelles[/]"); + WriteLogMessage("Unfolding left warp nacelle"); + Thread.Sleep(2000); + WriteLogMessage("Left warp nacelle [green]online[/]"); + WriteLogMessage("Unfolding right warp nacelle"); + Thread.Sleep(1000); + WriteLogMessage("Right warp nacelle [green]online[/]"); + + // Warp bubble + Thread.Sleep(3000); + ctx.Spinner(Spinner.Known.Star2); + ctx.Status("[bold blue]Generating warp bubble[/]"); + Thread.Sleep(3000); + ctx.Spinner(Spinner.Known.Star); + ctx.Status("[bold blue]Stabilizing warp bubble[/]"); + + // Safety + ctx.Spinner(Spinner.Known.Monkey); + ctx.Status("[bold blue]Performing safety checks[/]"); + WriteLogMessage("Enabling interior dampening"); + Thread.Sleep(2000); + WriteLogMessage("Interior dampening [green]enabled[/]"); + + // Warp! + Thread.Sleep(3000); + ctx.Spinner(Spinner.Known.Moon); + WriteLogMessage("Preparing for warp"); + Thread.Sleep(1000); + for (var warp = 1; warp < 10; warp++) + { + ctx.Status($"[bold blue]Warp {warp}[/]"); + Thread.Sleep(500); + } + }); + + // Done + AnsiConsole.MarkupLine("[bold green]Crusing at Warp 9.8[/]"); + } + + private static void WriteLogMessage(string message) + { + AnsiConsole.MarkupLine($"[grey]LOG:[/] {message}[grey]...[/]"); + } +} diff --git a/examples/Console/Status/Status.csproj b/examples/Console/Status/Status.csproj new file mode 100644 index 0000000..df43d6c --- /dev/null +++ b/examples/Console/Status/Status.csproj @@ -0,0 +1,19 @@ + + + + Exe + net8.0 + Status + Demonstrates how to show status updates. + Status + + + + + + + + + + + diff --git a/examples/Console/Tables/Program.cs b/examples/Console/Tables/Program.cs new file mode 100644 index 0000000..751477c --- /dev/null +++ b/examples/Console/Tables/Program.cs @@ -0,0 +1,46 @@ +using Spectre.Console; + +namespace Tables; + +public static class Program +{ + public static void Main() + { + AnsiConsole.Write(CreateTable()); + } + + private static Table CreateTable() + { + var simple = new Table() + .Border(TableBorder.Square) + .BorderColor(Color.Red) + .AddColumn(new TableColumn("[u]CDE[/]").Footer("EDC").Centered()) + .AddColumn(new TableColumn("[u]FED[/]").Footer("DEF")) + .AddColumn(new TableColumn("[u]IHG[/]").Footer("GHI")) + .AddRow("Hello", "[red]World![/]", "") + .AddRow("[blue]Bonjour[/]", "[white]le[/]", "[red]monde![/]") + .AddRow("[blue]Hej[/]", "[yellow]Världen![/]", ""); + + var second = new Table() + .Border(TableBorder.Rounded) + .BorderColor(Color.Green) + .AddColumn(new TableColumn("[u]Foo[/]")) + .AddColumn(new TableColumn("[u]Bar[/]")) + .AddColumn(new TableColumn("[u]Baz[/]")) + .AddRow("Hello", "[red]World![/]", "") + .AddRow(simple, new Text("Whaaat"), new Text("Lolz")) + .AddRow("[blue]Hej[/]", "[yellow]Världen![/]", ""); + + return new Table() + .Centered() + .Border(TableBorder.DoubleEdge) + .Title("TABLE [yellow]TITLE[/]") + .Caption("TABLE [yellow]CAPTION[/]") + .AddColumn(new TableColumn(new Panel("[u]ABC[/]").BorderColor(Color.Red)).Footer("[u]FOOTER 1[/]")) + .AddColumn(new TableColumn(new Panel("[u]DEF[/]").BorderColor(Color.Green)).Footer("[u]FOOTER 2[/]")) + .AddColumn(new TableColumn(new Panel("[u]GHI[/]").BorderColor(Color.Blue)).Footer("[u]FOOTER 3[/]")) + .AddRow(new Text("Hello").Centered(), new Markup("[red]World![/]"), Text.Empty) + .AddRow(second, new Text("Whaaat"), new Text("Lol")) + .AddRow(new Markup("[blue]Hej[/]").Centered(), new Markup("[yellow]Världen![/]"), Text.Empty); + } +} diff --git a/examples/Console/Tables/Tables.csproj b/examples/Console/Tables/Tables.csproj new file mode 100644 index 0000000..9dd65f3 --- /dev/null +++ b/examples/Console/Tables/Tables.csproj @@ -0,0 +1,15 @@ + + + + Exe + net8.0 + Tables + Demonstrates how to render tables in a console. + Widgets + + + + + + + diff --git a/examples/Console/Trees/Program.cs b/examples/Console/Trees/Program.cs new file mode 100644 index 0000000..7e55066 --- /dev/null +++ b/examples/Console/Trees/Program.cs @@ -0,0 +1,44 @@ +using Spectre.Console; + +namespace Trees; + +public static class Program +{ + public static void Main() + { + AnsiConsole.WriteLine(); + + // Render the tree + var tree = BuildTree(); + AnsiConsole.Write(tree); + } + + private static Tree BuildTree() + { + // Create the tree + var tree = new Tree("Root") + .Style(Style.Parse("red")) + .Guide(TreeGuide.Line); + + // Add some nodes + var foo = tree.AddNode("[yellow]Foo[/]"); + var table = foo.AddNode(new Table() + .RoundedBorder() + .AddColumn("First") + .AddColumn("Second") + .AddRow("1", "2") + .AddRow("3", "4") + .AddRow("5", "6")); + + table.AddNode("[blue]Baz[/]"); + foo.AddNode("Qux"); + + var bar = tree.AddNode("[yellow]Bar[/]"); + bar.AddNode(new Calendar(2020, 12) + .AddCalendarEvent(2020, 12, 12) + .HideHeader()); + + // Return the tree + return tree; + } +} diff --git a/examples/Console/Trees/Trees.csproj b/examples/Console/Trees/Trees.csproj new file mode 100644 index 0000000..9011411 --- /dev/null +++ b/examples/Console/Trees/Trees.csproj @@ -0,0 +1,15 @@ + + + + Exe + net8.0 + Trees + Demonstrates how to render trees in a console. + Widgets + + + + + + + diff --git a/examples/Directory.Build.props b/examples/Directory.Build.props new file mode 100644 index 0000000..3080093 --- /dev/null +++ b/examples/Directory.Build.props @@ -0,0 +1,5 @@ + + + false + + \ No newline at end of file diff --git a/examples/Examples.sln b/examples/Examples.sln new file mode 100644 index 0000000..136e2a8 --- /dev/null +++ b/examples/Examples.sln @@ -0,0 +1,540 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.31903.59 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shared", "Shared", "{2571F1BD-6556-4F96-B27B-B6190E1BF13A}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Cli", "Cli", "{4682E9B7-B54C-419D-B92F-470DA4E5674C}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Delegates", "Cli\Delegates\Delegates.csproj", "{E2C9023A-D24F-4F5D-8411-5E2FEA642FEF}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Demo", "Cli\Demo\Demo.csproj", "{7FC2594D-55D6-4688-AB7D-C9C903414448}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dynamic", "Cli\Dynamic\Dynamic.csproj", "{6EA8F935-A230-4171-8618-FDFABA553292}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Injection", "Cli\Injection\Injection.csproj", "{DEB88B47-658E-4FCB-BD1D-05C99E7EFA58}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Logging", "Cli\Logging\Logging.csproj", "{37024E79-A857-4EB2-9B50-F724ED34E5EB}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Shared", "Shared\Shared.csproj", "{DD8EC1B0-F50C-44E4-8399-2D560F95E572}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Borders", "Console\Borders\Borders.csproj", "{036D547D-1C9B-4E6F-B7E6-2E375E4CAF01}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Calendars", "Console\Calendars\Calendars.csproj", "{C64ADBA3-AED2-4938-8E26-8D6679C395CB}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Canvas", "Console\Canvas\Canvas.csproj", "{B3393919-26F1-4200-88CD-B71EEAF0EA51}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Charts", "Console\Charts\Charts.csproj", "{0CF6EE4B-4015-4DE0-9D48-1EADCDE296E6}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Colors", "Console\Colors\Colors.csproj", "{18562660-BF70-4EF3-A765-22713BDE2EB1}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Columns", "Console\Columns\Columns.csproj", "{264EBC5A-B897-4DA8-9C9E-9A445B1E368C}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cursor", "Console\Cursor\Cursor.csproj", "{95BE948B-6102-4453-982D-C7B21E51B582}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Emojis", "Console\Emojis\Emojis.csproj", "{6272C7ED-432E-4286-914C-898304A1880E}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Exceptions", "Console\Exceptions\Exceptions.csproj", "{20766F18-AEC7-4382-BDE2-0C846F2857AA}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Figlet", "Console\Figlet\Figlet.csproj", "{1C374C51-6C82-4E71-9701-5F378BC0356C}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Grids", "Console\Grids\Grids.csproj", "{6F7AB11D-D2AE-4D1C-AFEE-7FBA922D6C26}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Info", "Console\Info\Info.csproj", "{FBCCBA12-9DBF-403C-9FA4-222CBB080955}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Links", "Console\Links\Links.csproj", "{B9414195-69A2-40E6-B071-751DCCE95AF0}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Live", "Console\Live\Live.csproj", "{4AE3AC67-A927-42CC-B286-E0A0735DC7A2}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Panels", "Console\Panels\Panels.csproj", "{12E7F2CE-E91B-446F-A269-F88FB546B6A8}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Progress", "Console\Progress\Progress.csproj", "{4554BDF3-0723-4076-B054-616D830942D4}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Prompt", "Console\Prompt\Prompt.csproj", "{85A37D38-54D9-4FA2-B311-CA48BD7AF916}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Rules", "Console\Rules\Rules.csproj", "{305E2CE7-9D64-43D7-A26C-11036ACE2A89}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Showcase", "Console\Showcase\Showcase.csproj", "{5BEE8076-59A0-47BB-9CC6-222EC978AE16}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Status", "Console\Status\Status.csproj", "{FCE2405B-A215-434B-A47C-32053E27A907}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tables", "Console\Tables\Tables.csproj", "{3D16490A-9EBA-488C-9B3E-6A11FC1E0300}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Trees", "Console\Trees\Trees.csproj", "{2BD88288-E05D-4978-B045-17937078E63C}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LiveTable", "Console\LiveTable\LiveTable.csproj", "{E5FAAFB4-1D0F-4E29-A94F-A647D64AE64E}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Minimal", "Console\Minimal\Minimal.csproj", "{1780A30A-397A-4CC3-B2A0-A385D9081FA2}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AlternateScreen", "Console\AlternateScreen\AlternateScreen.csproj", "{8A3B636E-5828-438B-A8F4-83811D2704CD}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Paths", "Console\Paths\Paths.csproj", "{65CB00B0-A3AE-4E8F-A990-4C8C1A232FE2}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Layout", "Console\Layout\Layout.csproj", "{A9FDE73A-8452-4CA3-B366-3F900597E132}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Json", "Console\Json\Json.csproj", "{ABE3E734-0756-4D5A-B28A-E6E526D9927D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Help", "Cli\Help\Help.csproj", "{BAB490D6-FF8D-462B-B2B0-933384D629DB}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Decorations", "Console\Decorations\Decorations.csproj", "{FC5852F1-E01F-4DF7-9B49-CA19A9EE670F}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {E2C9023A-D24F-4F5D-8411-5E2FEA642FEF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E2C9023A-D24F-4F5D-8411-5E2FEA642FEF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E2C9023A-D24F-4F5D-8411-5E2FEA642FEF}.Debug|x64.ActiveCfg = Debug|Any CPU + {E2C9023A-D24F-4F5D-8411-5E2FEA642FEF}.Debug|x64.Build.0 = Debug|Any CPU + {E2C9023A-D24F-4F5D-8411-5E2FEA642FEF}.Debug|x86.ActiveCfg = Debug|Any CPU + {E2C9023A-D24F-4F5D-8411-5E2FEA642FEF}.Debug|x86.Build.0 = Debug|Any CPU + {E2C9023A-D24F-4F5D-8411-5E2FEA642FEF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E2C9023A-D24F-4F5D-8411-5E2FEA642FEF}.Release|Any CPU.Build.0 = Release|Any CPU + {E2C9023A-D24F-4F5D-8411-5E2FEA642FEF}.Release|x64.ActiveCfg = Release|Any CPU + {E2C9023A-D24F-4F5D-8411-5E2FEA642FEF}.Release|x64.Build.0 = Release|Any CPU + {E2C9023A-D24F-4F5D-8411-5E2FEA642FEF}.Release|x86.ActiveCfg = Release|Any CPU + {E2C9023A-D24F-4F5D-8411-5E2FEA642FEF}.Release|x86.Build.0 = Release|Any CPU + {7FC2594D-55D6-4688-AB7D-C9C903414448}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7FC2594D-55D6-4688-AB7D-C9C903414448}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7FC2594D-55D6-4688-AB7D-C9C903414448}.Debug|x64.ActiveCfg = Debug|Any CPU + {7FC2594D-55D6-4688-AB7D-C9C903414448}.Debug|x64.Build.0 = Debug|Any CPU + {7FC2594D-55D6-4688-AB7D-C9C903414448}.Debug|x86.ActiveCfg = Debug|Any CPU + {7FC2594D-55D6-4688-AB7D-C9C903414448}.Debug|x86.Build.0 = Debug|Any CPU + {7FC2594D-55D6-4688-AB7D-C9C903414448}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7FC2594D-55D6-4688-AB7D-C9C903414448}.Release|Any CPU.Build.0 = Release|Any CPU + {7FC2594D-55D6-4688-AB7D-C9C903414448}.Release|x64.ActiveCfg = Release|Any CPU + {7FC2594D-55D6-4688-AB7D-C9C903414448}.Release|x64.Build.0 = Release|Any CPU + {7FC2594D-55D6-4688-AB7D-C9C903414448}.Release|x86.ActiveCfg = Release|Any CPU + {7FC2594D-55D6-4688-AB7D-C9C903414448}.Release|x86.Build.0 = Release|Any CPU + {6EA8F935-A230-4171-8618-FDFABA553292}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6EA8F935-A230-4171-8618-FDFABA553292}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6EA8F935-A230-4171-8618-FDFABA553292}.Debug|x64.ActiveCfg = Debug|Any CPU + {6EA8F935-A230-4171-8618-FDFABA553292}.Debug|x64.Build.0 = Debug|Any CPU + {6EA8F935-A230-4171-8618-FDFABA553292}.Debug|x86.ActiveCfg = Debug|Any CPU + {6EA8F935-A230-4171-8618-FDFABA553292}.Debug|x86.Build.0 = Debug|Any CPU + {6EA8F935-A230-4171-8618-FDFABA553292}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6EA8F935-A230-4171-8618-FDFABA553292}.Release|Any CPU.Build.0 = Release|Any CPU + {6EA8F935-A230-4171-8618-FDFABA553292}.Release|x64.ActiveCfg = Release|Any CPU + {6EA8F935-A230-4171-8618-FDFABA553292}.Release|x64.Build.0 = Release|Any CPU + {6EA8F935-A230-4171-8618-FDFABA553292}.Release|x86.ActiveCfg = Release|Any CPU + {6EA8F935-A230-4171-8618-FDFABA553292}.Release|x86.Build.0 = Release|Any CPU + {DEB88B47-658E-4FCB-BD1D-05C99E7EFA58}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DEB88B47-658E-4FCB-BD1D-05C99E7EFA58}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DEB88B47-658E-4FCB-BD1D-05C99E7EFA58}.Debug|x64.ActiveCfg = Debug|Any CPU + {DEB88B47-658E-4FCB-BD1D-05C99E7EFA58}.Debug|x64.Build.0 = Debug|Any CPU + {DEB88B47-658E-4FCB-BD1D-05C99E7EFA58}.Debug|x86.ActiveCfg = Debug|Any CPU + {DEB88B47-658E-4FCB-BD1D-05C99E7EFA58}.Debug|x86.Build.0 = Debug|Any CPU + {DEB88B47-658E-4FCB-BD1D-05C99E7EFA58}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DEB88B47-658E-4FCB-BD1D-05C99E7EFA58}.Release|Any CPU.Build.0 = Release|Any CPU + {DEB88B47-658E-4FCB-BD1D-05C99E7EFA58}.Release|x64.ActiveCfg = Release|Any CPU + {DEB88B47-658E-4FCB-BD1D-05C99E7EFA58}.Release|x64.Build.0 = Release|Any CPU + {DEB88B47-658E-4FCB-BD1D-05C99E7EFA58}.Release|x86.ActiveCfg = Release|Any CPU + {DEB88B47-658E-4FCB-BD1D-05C99E7EFA58}.Release|x86.Build.0 = Release|Any CPU + {37024E79-A857-4EB2-9B50-F724ED34E5EB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {37024E79-A857-4EB2-9B50-F724ED34E5EB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {37024E79-A857-4EB2-9B50-F724ED34E5EB}.Debug|x64.ActiveCfg = Debug|Any CPU + {37024E79-A857-4EB2-9B50-F724ED34E5EB}.Debug|x64.Build.0 = Debug|Any CPU + {37024E79-A857-4EB2-9B50-F724ED34E5EB}.Debug|x86.ActiveCfg = Debug|Any CPU + {37024E79-A857-4EB2-9B50-F724ED34E5EB}.Debug|x86.Build.0 = Debug|Any CPU + {37024E79-A857-4EB2-9B50-F724ED34E5EB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {37024E79-A857-4EB2-9B50-F724ED34E5EB}.Release|Any CPU.Build.0 = Release|Any CPU + {37024E79-A857-4EB2-9B50-F724ED34E5EB}.Release|x64.ActiveCfg = Release|Any CPU + {37024E79-A857-4EB2-9B50-F724ED34E5EB}.Release|x64.Build.0 = Release|Any CPU + {37024E79-A857-4EB2-9B50-F724ED34E5EB}.Release|x86.ActiveCfg = Release|Any CPU + {37024E79-A857-4EB2-9B50-F724ED34E5EB}.Release|x86.Build.0 = Release|Any CPU + {DD8EC1B0-F50C-44E4-8399-2D560F95E572}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DD8EC1B0-F50C-44E4-8399-2D560F95E572}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DD8EC1B0-F50C-44E4-8399-2D560F95E572}.Debug|x64.ActiveCfg = Debug|Any CPU + {DD8EC1B0-F50C-44E4-8399-2D560F95E572}.Debug|x64.Build.0 = Debug|Any CPU + {DD8EC1B0-F50C-44E4-8399-2D560F95E572}.Debug|x86.ActiveCfg = Debug|Any CPU + {DD8EC1B0-F50C-44E4-8399-2D560F95E572}.Debug|x86.Build.0 = Debug|Any CPU + {DD8EC1B0-F50C-44E4-8399-2D560F95E572}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DD8EC1B0-F50C-44E4-8399-2D560F95E572}.Release|Any CPU.Build.0 = Release|Any CPU + {DD8EC1B0-F50C-44E4-8399-2D560F95E572}.Release|x64.ActiveCfg = Release|Any CPU + {DD8EC1B0-F50C-44E4-8399-2D560F95E572}.Release|x64.Build.0 = Release|Any CPU + {DD8EC1B0-F50C-44E4-8399-2D560F95E572}.Release|x86.ActiveCfg = Release|Any CPU + {DD8EC1B0-F50C-44E4-8399-2D560F95E572}.Release|x86.Build.0 = Release|Any CPU + {036D547D-1C9B-4E6F-B7E6-2E375E4CAF01}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {036D547D-1C9B-4E6F-B7E6-2E375E4CAF01}.Debug|Any CPU.Build.0 = Debug|Any CPU + {036D547D-1C9B-4E6F-B7E6-2E375E4CAF01}.Debug|x64.ActiveCfg = Debug|Any CPU + {036D547D-1C9B-4E6F-B7E6-2E375E4CAF01}.Debug|x64.Build.0 = Debug|Any CPU + {036D547D-1C9B-4E6F-B7E6-2E375E4CAF01}.Debug|x86.ActiveCfg = Debug|Any CPU + {036D547D-1C9B-4E6F-B7E6-2E375E4CAF01}.Debug|x86.Build.0 = Debug|Any CPU + {036D547D-1C9B-4E6F-B7E6-2E375E4CAF01}.Release|Any CPU.ActiveCfg = Release|Any CPU + {036D547D-1C9B-4E6F-B7E6-2E375E4CAF01}.Release|Any CPU.Build.0 = Release|Any CPU + {036D547D-1C9B-4E6F-B7E6-2E375E4CAF01}.Release|x64.ActiveCfg = Release|Any CPU + {036D547D-1C9B-4E6F-B7E6-2E375E4CAF01}.Release|x64.Build.0 = Release|Any CPU + {036D547D-1C9B-4E6F-B7E6-2E375E4CAF01}.Release|x86.ActiveCfg = Release|Any CPU + {036D547D-1C9B-4E6F-B7E6-2E375E4CAF01}.Release|x86.Build.0 = Release|Any CPU + {C64ADBA3-AED2-4938-8E26-8D6679C395CB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C64ADBA3-AED2-4938-8E26-8D6679C395CB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C64ADBA3-AED2-4938-8E26-8D6679C395CB}.Debug|x64.ActiveCfg = Debug|Any CPU + {C64ADBA3-AED2-4938-8E26-8D6679C395CB}.Debug|x64.Build.0 = Debug|Any CPU + {C64ADBA3-AED2-4938-8E26-8D6679C395CB}.Debug|x86.ActiveCfg = Debug|Any CPU + {C64ADBA3-AED2-4938-8E26-8D6679C395CB}.Debug|x86.Build.0 = Debug|Any CPU + {C64ADBA3-AED2-4938-8E26-8D6679C395CB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C64ADBA3-AED2-4938-8E26-8D6679C395CB}.Release|Any CPU.Build.0 = Release|Any CPU + {C64ADBA3-AED2-4938-8E26-8D6679C395CB}.Release|x64.ActiveCfg = Release|Any CPU + {C64ADBA3-AED2-4938-8E26-8D6679C395CB}.Release|x64.Build.0 = Release|Any CPU + {C64ADBA3-AED2-4938-8E26-8D6679C395CB}.Release|x86.ActiveCfg = Release|Any CPU + {C64ADBA3-AED2-4938-8E26-8D6679C395CB}.Release|x86.Build.0 = Release|Any CPU + {B3393919-26F1-4200-88CD-B71EEAF0EA51}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B3393919-26F1-4200-88CD-B71EEAF0EA51}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B3393919-26F1-4200-88CD-B71EEAF0EA51}.Debug|x64.ActiveCfg = Debug|Any CPU + {B3393919-26F1-4200-88CD-B71EEAF0EA51}.Debug|x64.Build.0 = Debug|Any CPU + {B3393919-26F1-4200-88CD-B71EEAF0EA51}.Debug|x86.ActiveCfg = Debug|Any CPU + {B3393919-26F1-4200-88CD-B71EEAF0EA51}.Debug|x86.Build.0 = Debug|Any CPU + {B3393919-26F1-4200-88CD-B71EEAF0EA51}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B3393919-26F1-4200-88CD-B71EEAF0EA51}.Release|Any CPU.Build.0 = Release|Any CPU + {B3393919-26F1-4200-88CD-B71EEAF0EA51}.Release|x64.ActiveCfg = Release|Any CPU + {B3393919-26F1-4200-88CD-B71EEAF0EA51}.Release|x64.Build.0 = Release|Any CPU + {B3393919-26F1-4200-88CD-B71EEAF0EA51}.Release|x86.ActiveCfg = Release|Any CPU + {B3393919-26F1-4200-88CD-B71EEAF0EA51}.Release|x86.Build.0 = Release|Any CPU + {0CF6EE4B-4015-4DE0-9D48-1EADCDE296E6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0CF6EE4B-4015-4DE0-9D48-1EADCDE296E6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0CF6EE4B-4015-4DE0-9D48-1EADCDE296E6}.Debug|x64.ActiveCfg = Debug|Any CPU + {0CF6EE4B-4015-4DE0-9D48-1EADCDE296E6}.Debug|x64.Build.0 = Debug|Any CPU + {0CF6EE4B-4015-4DE0-9D48-1EADCDE296E6}.Debug|x86.ActiveCfg = Debug|Any CPU + {0CF6EE4B-4015-4DE0-9D48-1EADCDE296E6}.Debug|x86.Build.0 = Debug|Any CPU + {0CF6EE4B-4015-4DE0-9D48-1EADCDE296E6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0CF6EE4B-4015-4DE0-9D48-1EADCDE296E6}.Release|Any CPU.Build.0 = Release|Any CPU + {0CF6EE4B-4015-4DE0-9D48-1EADCDE296E6}.Release|x64.ActiveCfg = Release|Any CPU + {0CF6EE4B-4015-4DE0-9D48-1EADCDE296E6}.Release|x64.Build.0 = Release|Any CPU + {0CF6EE4B-4015-4DE0-9D48-1EADCDE296E6}.Release|x86.ActiveCfg = Release|Any CPU + {0CF6EE4B-4015-4DE0-9D48-1EADCDE296E6}.Release|x86.Build.0 = Release|Any CPU + {18562660-BF70-4EF3-A765-22713BDE2EB1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {18562660-BF70-4EF3-A765-22713BDE2EB1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {18562660-BF70-4EF3-A765-22713BDE2EB1}.Debug|x64.ActiveCfg = Debug|Any CPU + {18562660-BF70-4EF3-A765-22713BDE2EB1}.Debug|x64.Build.0 = Debug|Any CPU + {18562660-BF70-4EF3-A765-22713BDE2EB1}.Debug|x86.ActiveCfg = Debug|Any CPU + {18562660-BF70-4EF3-A765-22713BDE2EB1}.Debug|x86.Build.0 = Debug|Any CPU + {18562660-BF70-4EF3-A765-22713BDE2EB1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {18562660-BF70-4EF3-A765-22713BDE2EB1}.Release|Any CPU.Build.0 = Release|Any CPU + {18562660-BF70-4EF3-A765-22713BDE2EB1}.Release|x64.ActiveCfg = Release|Any CPU + {18562660-BF70-4EF3-A765-22713BDE2EB1}.Release|x64.Build.0 = Release|Any CPU + {18562660-BF70-4EF3-A765-22713BDE2EB1}.Release|x86.ActiveCfg = Release|Any CPU + {18562660-BF70-4EF3-A765-22713BDE2EB1}.Release|x86.Build.0 = Release|Any CPU + {264EBC5A-B897-4DA8-9C9E-9A445B1E368C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {264EBC5A-B897-4DA8-9C9E-9A445B1E368C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {264EBC5A-B897-4DA8-9C9E-9A445B1E368C}.Debug|x64.ActiveCfg = Debug|Any CPU + {264EBC5A-B897-4DA8-9C9E-9A445B1E368C}.Debug|x64.Build.0 = Debug|Any CPU + {264EBC5A-B897-4DA8-9C9E-9A445B1E368C}.Debug|x86.ActiveCfg = Debug|Any CPU + {264EBC5A-B897-4DA8-9C9E-9A445B1E368C}.Debug|x86.Build.0 = Debug|Any CPU + {264EBC5A-B897-4DA8-9C9E-9A445B1E368C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {264EBC5A-B897-4DA8-9C9E-9A445B1E368C}.Release|Any CPU.Build.0 = Release|Any CPU + {264EBC5A-B897-4DA8-9C9E-9A445B1E368C}.Release|x64.ActiveCfg = Release|Any CPU + {264EBC5A-B897-4DA8-9C9E-9A445B1E368C}.Release|x64.Build.0 = Release|Any CPU + {264EBC5A-B897-4DA8-9C9E-9A445B1E368C}.Release|x86.ActiveCfg = Release|Any CPU + {264EBC5A-B897-4DA8-9C9E-9A445B1E368C}.Release|x86.Build.0 = Release|Any CPU + {95BE948B-6102-4453-982D-C7B21E51B582}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {95BE948B-6102-4453-982D-C7B21E51B582}.Debug|Any CPU.Build.0 = Debug|Any CPU + {95BE948B-6102-4453-982D-C7B21E51B582}.Debug|x64.ActiveCfg = Debug|Any CPU + {95BE948B-6102-4453-982D-C7B21E51B582}.Debug|x64.Build.0 = Debug|Any CPU + {95BE948B-6102-4453-982D-C7B21E51B582}.Debug|x86.ActiveCfg = Debug|Any CPU + {95BE948B-6102-4453-982D-C7B21E51B582}.Debug|x86.Build.0 = Debug|Any CPU + {95BE948B-6102-4453-982D-C7B21E51B582}.Release|Any CPU.ActiveCfg = Release|Any CPU + {95BE948B-6102-4453-982D-C7B21E51B582}.Release|Any CPU.Build.0 = Release|Any CPU + {95BE948B-6102-4453-982D-C7B21E51B582}.Release|x64.ActiveCfg = Release|Any CPU + {95BE948B-6102-4453-982D-C7B21E51B582}.Release|x64.Build.0 = Release|Any CPU + {95BE948B-6102-4453-982D-C7B21E51B582}.Release|x86.ActiveCfg = Release|Any CPU + {95BE948B-6102-4453-982D-C7B21E51B582}.Release|x86.Build.0 = Release|Any CPU + {6272C7ED-432E-4286-914C-898304A1880E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6272C7ED-432E-4286-914C-898304A1880E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6272C7ED-432E-4286-914C-898304A1880E}.Debug|x64.ActiveCfg = Debug|Any CPU + {6272C7ED-432E-4286-914C-898304A1880E}.Debug|x64.Build.0 = Debug|Any CPU + {6272C7ED-432E-4286-914C-898304A1880E}.Debug|x86.ActiveCfg = Debug|Any CPU + {6272C7ED-432E-4286-914C-898304A1880E}.Debug|x86.Build.0 = Debug|Any CPU + {6272C7ED-432E-4286-914C-898304A1880E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6272C7ED-432E-4286-914C-898304A1880E}.Release|Any CPU.Build.0 = Release|Any CPU + {6272C7ED-432E-4286-914C-898304A1880E}.Release|x64.ActiveCfg = Release|Any CPU + {6272C7ED-432E-4286-914C-898304A1880E}.Release|x64.Build.0 = Release|Any CPU + {6272C7ED-432E-4286-914C-898304A1880E}.Release|x86.ActiveCfg = Release|Any CPU + {6272C7ED-432E-4286-914C-898304A1880E}.Release|x86.Build.0 = Release|Any CPU + {20766F18-AEC7-4382-BDE2-0C846F2857AA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {20766F18-AEC7-4382-BDE2-0C846F2857AA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {20766F18-AEC7-4382-BDE2-0C846F2857AA}.Debug|x64.ActiveCfg = Debug|Any CPU + {20766F18-AEC7-4382-BDE2-0C846F2857AA}.Debug|x64.Build.0 = Debug|Any CPU + {20766F18-AEC7-4382-BDE2-0C846F2857AA}.Debug|x86.ActiveCfg = Debug|Any CPU + {20766F18-AEC7-4382-BDE2-0C846F2857AA}.Debug|x86.Build.0 = Debug|Any CPU + {20766F18-AEC7-4382-BDE2-0C846F2857AA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {20766F18-AEC7-4382-BDE2-0C846F2857AA}.Release|Any CPU.Build.0 = Release|Any CPU + {20766F18-AEC7-4382-BDE2-0C846F2857AA}.Release|x64.ActiveCfg = Release|Any CPU + {20766F18-AEC7-4382-BDE2-0C846F2857AA}.Release|x64.Build.0 = Release|Any CPU + {20766F18-AEC7-4382-BDE2-0C846F2857AA}.Release|x86.ActiveCfg = Release|Any CPU + {20766F18-AEC7-4382-BDE2-0C846F2857AA}.Release|x86.Build.0 = Release|Any CPU + {1C374C51-6C82-4E71-9701-5F378BC0356C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1C374C51-6C82-4E71-9701-5F378BC0356C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1C374C51-6C82-4E71-9701-5F378BC0356C}.Debug|x64.ActiveCfg = Debug|Any CPU + {1C374C51-6C82-4E71-9701-5F378BC0356C}.Debug|x64.Build.0 = Debug|Any CPU + {1C374C51-6C82-4E71-9701-5F378BC0356C}.Debug|x86.ActiveCfg = Debug|Any CPU + {1C374C51-6C82-4E71-9701-5F378BC0356C}.Debug|x86.Build.0 = Debug|Any CPU + {1C374C51-6C82-4E71-9701-5F378BC0356C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1C374C51-6C82-4E71-9701-5F378BC0356C}.Release|Any CPU.Build.0 = Release|Any CPU + {1C374C51-6C82-4E71-9701-5F378BC0356C}.Release|x64.ActiveCfg = Release|Any CPU + {1C374C51-6C82-4E71-9701-5F378BC0356C}.Release|x64.Build.0 = Release|Any CPU + {1C374C51-6C82-4E71-9701-5F378BC0356C}.Release|x86.ActiveCfg = Release|Any CPU + {1C374C51-6C82-4E71-9701-5F378BC0356C}.Release|x86.Build.0 = Release|Any CPU + {6F7AB11D-D2AE-4D1C-AFEE-7FBA922D6C26}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6F7AB11D-D2AE-4D1C-AFEE-7FBA922D6C26}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6F7AB11D-D2AE-4D1C-AFEE-7FBA922D6C26}.Debug|x64.ActiveCfg = Debug|Any CPU + {6F7AB11D-D2AE-4D1C-AFEE-7FBA922D6C26}.Debug|x64.Build.0 = Debug|Any CPU + {6F7AB11D-D2AE-4D1C-AFEE-7FBA922D6C26}.Debug|x86.ActiveCfg = Debug|Any CPU + {6F7AB11D-D2AE-4D1C-AFEE-7FBA922D6C26}.Debug|x86.Build.0 = Debug|Any CPU + {6F7AB11D-D2AE-4D1C-AFEE-7FBA922D6C26}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6F7AB11D-D2AE-4D1C-AFEE-7FBA922D6C26}.Release|Any CPU.Build.0 = Release|Any CPU + {6F7AB11D-D2AE-4D1C-AFEE-7FBA922D6C26}.Release|x64.ActiveCfg = Release|Any CPU + {6F7AB11D-D2AE-4D1C-AFEE-7FBA922D6C26}.Release|x64.Build.0 = Release|Any CPU + {6F7AB11D-D2AE-4D1C-AFEE-7FBA922D6C26}.Release|x86.ActiveCfg = Release|Any CPU + {6F7AB11D-D2AE-4D1C-AFEE-7FBA922D6C26}.Release|x86.Build.0 = Release|Any CPU + {FBCCBA12-9DBF-403C-9FA4-222CBB080955}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FBCCBA12-9DBF-403C-9FA4-222CBB080955}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FBCCBA12-9DBF-403C-9FA4-222CBB080955}.Debug|x64.ActiveCfg = Debug|Any CPU + {FBCCBA12-9DBF-403C-9FA4-222CBB080955}.Debug|x64.Build.0 = Debug|Any CPU + {FBCCBA12-9DBF-403C-9FA4-222CBB080955}.Debug|x86.ActiveCfg = Debug|Any CPU + {FBCCBA12-9DBF-403C-9FA4-222CBB080955}.Debug|x86.Build.0 = Debug|Any CPU + {FBCCBA12-9DBF-403C-9FA4-222CBB080955}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FBCCBA12-9DBF-403C-9FA4-222CBB080955}.Release|Any CPU.Build.0 = Release|Any CPU + {FBCCBA12-9DBF-403C-9FA4-222CBB080955}.Release|x64.ActiveCfg = Release|Any CPU + {FBCCBA12-9DBF-403C-9FA4-222CBB080955}.Release|x64.Build.0 = Release|Any CPU + {FBCCBA12-9DBF-403C-9FA4-222CBB080955}.Release|x86.ActiveCfg = Release|Any CPU + {FBCCBA12-9DBF-403C-9FA4-222CBB080955}.Release|x86.Build.0 = Release|Any CPU + {B9414195-69A2-40E6-B071-751DCCE95AF0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B9414195-69A2-40E6-B071-751DCCE95AF0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B9414195-69A2-40E6-B071-751DCCE95AF0}.Debug|x64.ActiveCfg = Debug|Any CPU + {B9414195-69A2-40E6-B071-751DCCE95AF0}.Debug|x64.Build.0 = Debug|Any CPU + {B9414195-69A2-40E6-B071-751DCCE95AF0}.Debug|x86.ActiveCfg = Debug|Any CPU + {B9414195-69A2-40E6-B071-751DCCE95AF0}.Debug|x86.Build.0 = Debug|Any CPU + {B9414195-69A2-40E6-B071-751DCCE95AF0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B9414195-69A2-40E6-B071-751DCCE95AF0}.Release|Any CPU.Build.0 = Release|Any CPU + {B9414195-69A2-40E6-B071-751DCCE95AF0}.Release|x64.ActiveCfg = Release|Any CPU + {B9414195-69A2-40E6-B071-751DCCE95AF0}.Release|x64.Build.0 = Release|Any CPU + {B9414195-69A2-40E6-B071-751DCCE95AF0}.Release|x86.ActiveCfg = Release|Any CPU + {B9414195-69A2-40E6-B071-751DCCE95AF0}.Release|x86.Build.0 = Release|Any CPU + {4AE3AC67-A927-42CC-B286-E0A0735DC7A2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4AE3AC67-A927-42CC-B286-E0A0735DC7A2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4AE3AC67-A927-42CC-B286-E0A0735DC7A2}.Debug|x64.ActiveCfg = Debug|Any CPU + {4AE3AC67-A927-42CC-B286-E0A0735DC7A2}.Debug|x64.Build.0 = Debug|Any CPU + {4AE3AC67-A927-42CC-B286-E0A0735DC7A2}.Debug|x86.ActiveCfg = Debug|Any CPU + {4AE3AC67-A927-42CC-B286-E0A0735DC7A2}.Debug|x86.Build.0 = Debug|Any CPU + {4AE3AC67-A927-42CC-B286-E0A0735DC7A2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4AE3AC67-A927-42CC-B286-E0A0735DC7A2}.Release|Any CPU.Build.0 = Release|Any CPU + {4AE3AC67-A927-42CC-B286-E0A0735DC7A2}.Release|x64.ActiveCfg = Release|Any CPU + {4AE3AC67-A927-42CC-B286-E0A0735DC7A2}.Release|x64.Build.0 = Release|Any CPU + {4AE3AC67-A927-42CC-B286-E0A0735DC7A2}.Release|x86.ActiveCfg = Release|Any CPU + {4AE3AC67-A927-42CC-B286-E0A0735DC7A2}.Release|x86.Build.0 = Release|Any CPU + {12E7F2CE-E91B-446F-A269-F88FB546B6A8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {12E7F2CE-E91B-446F-A269-F88FB546B6A8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {12E7F2CE-E91B-446F-A269-F88FB546B6A8}.Debug|x64.ActiveCfg = Debug|Any CPU + {12E7F2CE-E91B-446F-A269-F88FB546B6A8}.Debug|x64.Build.0 = Debug|Any CPU + {12E7F2CE-E91B-446F-A269-F88FB546B6A8}.Debug|x86.ActiveCfg = Debug|Any CPU + {12E7F2CE-E91B-446F-A269-F88FB546B6A8}.Debug|x86.Build.0 = Debug|Any CPU + {12E7F2CE-E91B-446F-A269-F88FB546B6A8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {12E7F2CE-E91B-446F-A269-F88FB546B6A8}.Release|Any CPU.Build.0 = Release|Any CPU + {12E7F2CE-E91B-446F-A269-F88FB546B6A8}.Release|x64.ActiveCfg = Release|Any CPU + {12E7F2CE-E91B-446F-A269-F88FB546B6A8}.Release|x64.Build.0 = Release|Any CPU + {12E7F2CE-E91B-446F-A269-F88FB546B6A8}.Release|x86.ActiveCfg = Release|Any CPU + {12E7F2CE-E91B-446F-A269-F88FB546B6A8}.Release|x86.Build.0 = Release|Any CPU + {4554BDF3-0723-4076-B054-616D830942D4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4554BDF3-0723-4076-B054-616D830942D4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4554BDF3-0723-4076-B054-616D830942D4}.Debug|x64.ActiveCfg = Debug|Any CPU + {4554BDF3-0723-4076-B054-616D830942D4}.Debug|x64.Build.0 = Debug|Any CPU + {4554BDF3-0723-4076-B054-616D830942D4}.Debug|x86.ActiveCfg = Debug|Any CPU + {4554BDF3-0723-4076-B054-616D830942D4}.Debug|x86.Build.0 = Debug|Any CPU + {4554BDF3-0723-4076-B054-616D830942D4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4554BDF3-0723-4076-B054-616D830942D4}.Release|Any CPU.Build.0 = Release|Any CPU + {4554BDF3-0723-4076-B054-616D830942D4}.Release|x64.ActiveCfg = Release|Any CPU + {4554BDF3-0723-4076-B054-616D830942D4}.Release|x64.Build.0 = Release|Any CPU + {4554BDF3-0723-4076-B054-616D830942D4}.Release|x86.ActiveCfg = Release|Any CPU + {4554BDF3-0723-4076-B054-616D830942D4}.Release|x86.Build.0 = Release|Any CPU + {85A37D38-54D9-4FA2-B311-CA48BD7AF916}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {85A37D38-54D9-4FA2-B311-CA48BD7AF916}.Debug|Any CPU.Build.0 = Debug|Any CPU + {85A37D38-54D9-4FA2-B311-CA48BD7AF916}.Debug|x64.ActiveCfg = Debug|Any CPU + {85A37D38-54D9-4FA2-B311-CA48BD7AF916}.Debug|x64.Build.0 = Debug|Any CPU + {85A37D38-54D9-4FA2-B311-CA48BD7AF916}.Debug|x86.ActiveCfg = Debug|Any CPU + {85A37D38-54D9-4FA2-B311-CA48BD7AF916}.Debug|x86.Build.0 = Debug|Any CPU + {85A37D38-54D9-4FA2-B311-CA48BD7AF916}.Release|Any CPU.ActiveCfg = Release|Any CPU + {85A37D38-54D9-4FA2-B311-CA48BD7AF916}.Release|Any CPU.Build.0 = Release|Any CPU + {85A37D38-54D9-4FA2-B311-CA48BD7AF916}.Release|x64.ActiveCfg = Release|Any CPU + {85A37D38-54D9-4FA2-B311-CA48BD7AF916}.Release|x64.Build.0 = Release|Any CPU + {85A37D38-54D9-4FA2-B311-CA48BD7AF916}.Release|x86.ActiveCfg = Release|Any CPU + {85A37D38-54D9-4FA2-B311-CA48BD7AF916}.Release|x86.Build.0 = Release|Any CPU + {305E2CE7-9D64-43D7-A26C-11036ACE2A89}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {305E2CE7-9D64-43D7-A26C-11036ACE2A89}.Debug|Any CPU.Build.0 = Debug|Any CPU + {305E2CE7-9D64-43D7-A26C-11036ACE2A89}.Debug|x64.ActiveCfg = Debug|Any CPU + {305E2CE7-9D64-43D7-A26C-11036ACE2A89}.Debug|x64.Build.0 = Debug|Any CPU + {305E2CE7-9D64-43D7-A26C-11036ACE2A89}.Debug|x86.ActiveCfg = Debug|Any CPU + {305E2CE7-9D64-43D7-A26C-11036ACE2A89}.Debug|x86.Build.0 = Debug|Any CPU + {305E2CE7-9D64-43D7-A26C-11036ACE2A89}.Release|Any CPU.ActiveCfg = Release|Any CPU + {305E2CE7-9D64-43D7-A26C-11036ACE2A89}.Release|Any CPU.Build.0 = Release|Any CPU + {305E2CE7-9D64-43D7-A26C-11036ACE2A89}.Release|x64.ActiveCfg = Release|Any CPU + {305E2CE7-9D64-43D7-A26C-11036ACE2A89}.Release|x64.Build.0 = Release|Any CPU + {305E2CE7-9D64-43D7-A26C-11036ACE2A89}.Release|x86.ActiveCfg = Release|Any CPU + {305E2CE7-9D64-43D7-A26C-11036ACE2A89}.Release|x86.Build.0 = Release|Any CPU + {5BEE8076-59A0-47BB-9CC6-222EC978AE16}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5BEE8076-59A0-47BB-9CC6-222EC978AE16}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5BEE8076-59A0-47BB-9CC6-222EC978AE16}.Debug|x64.ActiveCfg = Debug|Any CPU + {5BEE8076-59A0-47BB-9CC6-222EC978AE16}.Debug|x64.Build.0 = Debug|Any CPU + {5BEE8076-59A0-47BB-9CC6-222EC978AE16}.Debug|x86.ActiveCfg = Debug|Any CPU + {5BEE8076-59A0-47BB-9CC6-222EC978AE16}.Debug|x86.Build.0 = Debug|Any CPU + {5BEE8076-59A0-47BB-9CC6-222EC978AE16}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5BEE8076-59A0-47BB-9CC6-222EC978AE16}.Release|Any CPU.Build.0 = Release|Any CPU + {5BEE8076-59A0-47BB-9CC6-222EC978AE16}.Release|x64.ActiveCfg = Release|Any CPU + {5BEE8076-59A0-47BB-9CC6-222EC978AE16}.Release|x64.Build.0 = Release|Any CPU + {5BEE8076-59A0-47BB-9CC6-222EC978AE16}.Release|x86.ActiveCfg = Release|Any CPU + {5BEE8076-59A0-47BB-9CC6-222EC978AE16}.Release|x86.Build.0 = Release|Any CPU + {FCE2405B-A215-434B-A47C-32053E27A907}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FCE2405B-A215-434B-A47C-32053E27A907}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FCE2405B-A215-434B-A47C-32053E27A907}.Debug|x64.ActiveCfg = Debug|Any CPU + {FCE2405B-A215-434B-A47C-32053E27A907}.Debug|x64.Build.0 = Debug|Any CPU + {FCE2405B-A215-434B-A47C-32053E27A907}.Debug|x86.ActiveCfg = Debug|Any CPU + {FCE2405B-A215-434B-A47C-32053E27A907}.Debug|x86.Build.0 = Debug|Any CPU + {FCE2405B-A215-434B-A47C-32053E27A907}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FCE2405B-A215-434B-A47C-32053E27A907}.Release|Any CPU.Build.0 = Release|Any CPU + {FCE2405B-A215-434B-A47C-32053E27A907}.Release|x64.ActiveCfg = Release|Any CPU + {FCE2405B-A215-434B-A47C-32053E27A907}.Release|x64.Build.0 = Release|Any CPU + {FCE2405B-A215-434B-A47C-32053E27A907}.Release|x86.ActiveCfg = Release|Any CPU + {FCE2405B-A215-434B-A47C-32053E27A907}.Release|x86.Build.0 = Release|Any CPU + {3D16490A-9EBA-488C-9B3E-6A11FC1E0300}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3D16490A-9EBA-488C-9B3E-6A11FC1E0300}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3D16490A-9EBA-488C-9B3E-6A11FC1E0300}.Debug|x64.ActiveCfg = Debug|Any CPU + {3D16490A-9EBA-488C-9B3E-6A11FC1E0300}.Debug|x64.Build.0 = Debug|Any CPU + {3D16490A-9EBA-488C-9B3E-6A11FC1E0300}.Debug|x86.ActiveCfg = Debug|Any CPU + {3D16490A-9EBA-488C-9B3E-6A11FC1E0300}.Debug|x86.Build.0 = Debug|Any CPU + {3D16490A-9EBA-488C-9B3E-6A11FC1E0300}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3D16490A-9EBA-488C-9B3E-6A11FC1E0300}.Release|Any CPU.Build.0 = Release|Any CPU + {3D16490A-9EBA-488C-9B3E-6A11FC1E0300}.Release|x64.ActiveCfg = Release|Any CPU + {3D16490A-9EBA-488C-9B3E-6A11FC1E0300}.Release|x64.Build.0 = Release|Any CPU + {3D16490A-9EBA-488C-9B3E-6A11FC1E0300}.Release|x86.ActiveCfg = Release|Any CPU + {3D16490A-9EBA-488C-9B3E-6A11FC1E0300}.Release|x86.Build.0 = Release|Any CPU + {2BD88288-E05D-4978-B045-17937078E63C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2BD88288-E05D-4978-B045-17937078E63C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2BD88288-E05D-4978-B045-17937078E63C}.Debug|x64.ActiveCfg = Debug|Any CPU + {2BD88288-E05D-4978-B045-17937078E63C}.Debug|x64.Build.0 = Debug|Any CPU + {2BD88288-E05D-4978-B045-17937078E63C}.Debug|x86.ActiveCfg = Debug|Any CPU + {2BD88288-E05D-4978-B045-17937078E63C}.Debug|x86.Build.0 = Debug|Any CPU + {2BD88288-E05D-4978-B045-17937078E63C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2BD88288-E05D-4978-B045-17937078E63C}.Release|Any CPU.Build.0 = Release|Any CPU + {2BD88288-E05D-4978-B045-17937078E63C}.Release|x64.ActiveCfg = Release|Any CPU + {2BD88288-E05D-4978-B045-17937078E63C}.Release|x64.Build.0 = Release|Any CPU + {2BD88288-E05D-4978-B045-17937078E63C}.Release|x86.ActiveCfg = Release|Any CPU + {2BD88288-E05D-4978-B045-17937078E63C}.Release|x86.Build.0 = Release|Any CPU + {E5FAAFB4-1D0F-4E29-A94F-A647D64AE64E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E5FAAFB4-1D0F-4E29-A94F-A647D64AE64E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E5FAAFB4-1D0F-4E29-A94F-A647D64AE64E}.Debug|x64.ActiveCfg = Debug|Any CPU + {E5FAAFB4-1D0F-4E29-A94F-A647D64AE64E}.Debug|x64.Build.0 = Debug|Any CPU + {E5FAAFB4-1D0F-4E29-A94F-A647D64AE64E}.Debug|x86.ActiveCfg = Debug|Any CPU + {E5FAAFB4-1D0F-4E29-A94F-A647D64AE64E}.Debug|x86.Build.0 = Debug|Any CPU + {E5FAAFB4-1D0F-4E29-A94F-A647D64AE64E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E5FAAFB4-1D0F-4E29-A94F-A647D64AE64E}.Release|Any CPU.Build.0 = Release|Any CPU + {E5FAAFB4-1D0F-4E29-A94F-A647D64AE64E}.Release|x64.ActiveCfg = Release|Any CPU + {E5FAAFB4-1D0F-4E29-A94F-A647D64AE64E}.Release|x64.Build.0 = Release|Any CPU + {E5FAAFB4-1D0F-4E29-A94F-A647D64AE64E}.Release|x86.ActiveCfg = Release|Any CPU + {E5FAAFB4-1D0F-4E29-A94F-A647D64AE64E}.Release|x86.Build.0 = Release|Any CPU + {1780A30A-397A-4CC3-B2A0-A385D9081FA2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1780A30A-397A-4CC3-B2A0-A385D9081FA2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1780A30A-397A-4CC3-B2A0-A385D9081FA2}.Debug|x64.ActiveCfg = Debug|Any CPU + {1780A30A-397A-4CC3-B2A0-A385D9081FA2}.Debug|x64.Build.0 = Debug|Any CPU + {1780A30A-397A-4CC3-B2A0-A385D9081FA2}.Debug|x86.ActiveCfg = Debug|Any CPU + {1780A30A-397A-4CC3-B2A0-A385D9081FA2}.Debug|x86.Build.0 = Debug|Any CPU + {1780A30A-397A-4CC3-B2A0-A385D9081FA2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1780A30A-397A-4CC3-B2A0-A385D9081FA2}.Release|Any CPU.Build.0 = Release|Any CPU + {1780A30A-397A-4CC3-B2A0-A385D9081FA2}.Release|x64.ActiveCfg = Release|Any CPU + {1780A30A-397A-4CC3-B2A0-A385D9081FA2}.Release|x64.Build.0 = Release|Any CPU + {1780A30A-397A-4CC3-B2A0-A385D9081FA2}.Release|x86.ActiveCfg = Release|Any CPU + {1780A30A-397A-4CC3-B2A0-A385D9081FA2}.Release|x86.Build.0 = Release|Any CPU + {8A3B636E-5828-438B-A8F4-83811D2704CD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8A3B636E-5828-438B-A8F4-83811D2704CD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8A3B636E-5828-438B-A8F4-83811D2704CD}.Debug|x64.ActiveCfg = Debug|Any CPU + {8A3B636E-5828-438B-A8F4-83811D2704CD}.Debug|x64.Build.0 = Debug|Any CPU + {8A3B636E-5828-438B-A8F4-83811D2704CD}.Debug|x86.ActiveCfg = Debug|Any CPU + {8A3B636E-5828-438B-A8F4-83811D2704CD}.Debug|x86.Build.0 = Debug|Any CPU + {8A3B636E-5828-438B-A8F4-83811D2704CD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8A3B636E-5828-438B-A8F4-83811D2704CD}.Release|Any CPU.Build.0 = Release|Any CPU + {8A3B636E-5828-438B-A8F4-83811D2704CD}.Release|x64.ActiveCfg = Release|Any CPU + {8A3B636E-5828-438B-A8F4-83811D2704CD}.Release|x64.Build.0 = Release|Any CPU + {8A3B636E-5828-438B-A8F4-83811D2704CD}.Release|x86.ActiveCfg = Release|Any CPU + {8A3B636E-5828-438B-A8F4-83811D2704CD}.Release|x86.Build.0 = Release|Any CPU + {65CB00B0-A3AE-4E8F-A990-4C8C1A232FE2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {65CB00B0-A3AE-4E8F-A990-4C8C1A232FE2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {65CB00B0-A3AE-4E8F-A990-4C8C1A232FE2}.Debug|x64.ActiveCfg = Debug|Any CPU + {65CB00B0-A3AE-4E8F-A990-4C8C1A232FE2}.Debug|x64.Build.0 = Debug|Any CPU + {65CB00B0-A3AE-4E8F-A990-4C8C1A232FE2}.Debug|x86.ActiveCfg = Debug|Any CPU + {65CB00B0-A3AE-4E8F-A990-4C8C1A232FE2}.Debug|x86.Build.0 = Debug|Any CPU + {65CB00B0-A3AE-4E8F-A990-4C8C1A232FE2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {65CB00B0-A3AE-4E8F-A990-4C8C1A232FE2}.Release|Any CPU.Build.0 = Release|Any CPU + {65CB00B0-A3AE-4E8F-A990-4C8C1A232FE2}.Release|x64.ActiveCfg = Release|Any CPU + {65CB00B0-A3AE-4E8F-A990-4C8C1A232FE2}.Release|x64.Build.0 = Release|Any CPU + {65CB00B0-A3AE-4E8F-A990-4C8C1A232FE2}.Release|x86.ActiveCfg = Release|Any CPU + {65CB00B0-A3AE-4E8F-A990-4C8C1A232FE2}.Release|x86.Build.0 = Release|Any CPU + {A9FDE73A-8452-4CA3-B366-3F900597E132}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A9FDE73A-8452-4CA3-B366-3F900597E132}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A9FDE73A-8452-4CA3-B366-3F900597E132}.Debug|x64.ActiveCfg = Debug|Any CPU + {A9FDE73A-8452-4CA3-B366-3F900597E132}.Debug|x64.Build.0 = Debug|Any CPU + {A9FDE73A-8452-4CA3-B366-3F900597E132}.Debug|x86.ActiveCfg = Debug|Any CPU + {A9FDE73A-8452-4CA3-B366-3F900597E132}.Debug|x86.Build.0 = Debug|Any CPU + {A9FDE73A-8452-4CA3-B366-3F900597E132}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A9FDE73A-8452-4CA3-B366-3F900597E132}.Release|Any CPU.Build.0 = Release|Any CPU + {A9FDE73A-8452-4CA3-B366-3F900597E132}.Release|x64.ActiveCfg = Release|Any CPU + {A9FDE73A-8452-4CA3-B366-3F900597E132}.Release|x64.Build.0 = Release|Any CPU + {A9FDE73A-8452-4CA3-B366-3F900597E132}.Release|x86.ActiveCfg = Release|Any CPU + {A9FDE73A-8452-4CA3-B366-3F900597E132}.Release|x86.Build.0 = Release|Any CPU + {ABE3E734-0756-4D5A-B28A-E6E526D9927D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {ABE3E734-0756-4D5A-B28A-E6E526D9927D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {ABE3E734-0756-4D5A-B28A-E6E526D9927D}.Debug|x64.ActiveCfg = Debug|Any CPU + {ABE3E734-0756-4D5A-B28A-E6E526D9927D}.Debug|x64.Build.0 = Debug|Any CPU + {ABE3E734-0756-4D5A-B28A-E6E526D9927D}.Debug|x86.ActiveCfg = Debug|Any CPU + {ABE3E734-0756-4D5A-B28A-E6E526D9927D}.Debug|x86.Build.0 = Debug|Any CPU + {ABE3E734-0756-4D5A-B28A-E6E526D9927D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {ABE3E734-0756-4D5A-B28A-E6E526D9927D}.Release|Any CPU.Build.0 = Release|Any CPU + {ABE3E734-0756-4D5A-B28A-E6E526D9927D}.Release|x64.ActiveCfg = Release|Any CPU + {ABE3E734-0756-4D5A-B28A-E6E526D9927D}.Release|x64.Build.0 = Release|Any CPU + {ABE3E734-0756-4D5A-B28A-E6E526D9927D}.Release|x86.ActiveCfg = Release|Any CPU + {ABE3E734-0756-4D5A-B28A-E6E526D9927D}.Release|x86.Build.0 = Release|Any CPU + {BAB490D6-FF8D-462B-B2B0-933384D629DB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BAB490D6-FF8D-462B-B2B0-933384D629DB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BAB490D6-FF8D-462B-B2B0-933384D629DB}.Debug|x64.ActiveCfg = Debug|Any CPU + {BAB490D6-FF8D-462B-B2B0-933384D629DB}.Debug|x64.Build.0 = Debug|Any CPU + {BAB490D6-FF8D-462B-B2B0-933384D629DB}.Debug|x86.ActiveCfg = Debug|Any CPU + {BAB490D6-FF8D-462B-B2B0-933384D629DB}.Debug|x86.Build.0 = Debug|Any CPU + {BAB490D6-FF8D-462B-B2B0-933384D629DB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BAB490D6-FF8D-462B-B2B0-933384D629DB}.Release|Any CPU.Build.0 = Release|Any CPU + {BAB490D6-FF8D-462B-B2B0-933384D629DB}.Release|x64.ActiveCfg = Release|Any CPU + {BAB490D6-FF8D-462B-B2B0-933384D629DB}.Release|x64.Build.0 = Release|Any CPU + {BAB490D6-FF8D-462B-B2B0-933384D629DB}.Release|x86.ActiveCfg = Release|Any CPU + {BAB490D6-FF8D-462B-B2B0-933384D629DB}.Release|x86.Build.0 = Release|Any CPU + {FC5852F1-E01F-4DF7-9B49-CA19A9EE670F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FC5852F1-E01F-4DF7-9B49-CA19A9EE670F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FC5852F1-E01F-4DF7-9B49-CA19A9EE670F}.Debug|x64.ActiveCfg = Debug|Any CPU + {FC5852F1-E01F-4DF7-9B49-CA19A9EE670F}.Debug|x64.Build.0 = Debug|Any CPU + {FC5852F1-E01F-4DF7-9B49-CA19A9EE670F}.Debug|x86.ActiveCfg = Debug|Any CPU + {FC5852F1-E01F-4DF7-9B49-CA19A9EE670F}.Debug|x86.Build.0 = Debug|Any CPU + {FC5852F1-E01F-4DF7-9B49-CA19A9EE670F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FC5852F1-E01F-4DF7-9B49-CA19A9EE670F}.Release|Any CPU.Build.0 = Release|Any CPU + {FC5852F1-E01F-4DF7-9B49-CA19A9EE670F}.Release|x64.ActiveCfg = Release|Any CPU + {FC5852F1-E01F-4DF7-9B49-CA19A9EE670F}.Release|x64.Build.0 = Release|Any CPU + {FC5852F1-E01F-4DF7-9B49-CA19A9EE670F}.Release|x86.ActiveCfg = Release|Any CPU + {FC5852F1-E01F-4DF7-9B49-CA19A9EE670F}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {E2C9023A-D24F-4F5D-8411-5E2FEA642FEF} = {4682E9B7-B54C-419D-B92F-470DA4E5674C} + {7FC2594D-55D6-4688-AB7D-C9C903414448} = {4682E9B7-B54C-419D-B92F-470DA4E5674C} + {6EA8F935-A230-4171-8618-FDFABA553292} = {4682E9B7-B54C-419D-B92F-470DA4E5674C} + {DEB88B47-658E-4FCB-BD1D-05C99E7EFA58} = {4682E9B7-B54C-419D-B92F-470DA4E5674C} + {37024E79-A857-4EB2-9B50-F724ED34E5EB} = {4682E9B7-B54C-419D-B92F-470DA4E5674C} + {DD8EC1B0-F50C-44E4-8399-2D560F95E572} = {2571F1BD-6556-4F96-B27B-B6190E1BF13A} + {BAB490D6-FF8D-462B-B2B0-933384D629DB} = {4682E9B7-B54C-419D-B92F-470DA4E5674C} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {3EE724C5-CAB4-410D-AC63-8D4260EF83ED} + EndGlobalSection +EndGlobal diff --git a/examples/Shared/ColorBox.cs b/examples/Shared/ColorBox.cs new file mode 100644 index 0000000..7438c9d --- /dev/null +++ b/examples/Shared/ColorBox.cs @@ -0,0 +1,123 @@ +using System; +using System.Collections.Generic; +using Spectre.Console.Rendering; + +namespace Spectre.Console.Examples; + +public sealed class ColorBox : Renderable +{ + private readonly int _height; + private int? _width; + + public ColorBox(int height) + { + _height = height; + } + + public ColorBox(int width, int height) + : this(height) + { + _width = width; + } + + protected override Measurement Measure(RenderOptions options, int maxWidth) + { + return new Measurement(1, GetWidth(maxWidth)); + } + + protected override IEnumerable Render(RenderOptions options, int maxWidth) + { + maxWidth = GetWidth(maxWidth); + + for (var y = 0; y < _height; y++) + { + for (var x = 0; x < maxWidth; x++) + { + var h = x / (float)maxWidth; + var l = 0.1f + ((y / (float)_height) * 0.7f); + var (r1, g1, b1) = ColorFromHSL(h, l, 1.0f); + var (r2, g2, b2) = ColorFromHSL(h, l + (0.7f / 10), 1.0f); + + var background = new Color((byte)(r1 * 255), (byte)(g1 * 255), (byte)(b1 * 255)); + var foreground = new Color((byte)(r2 * 255), (byte)(g2 * 255), (byte)(b2 * 255)); + + yield return new Segment("▄", new Style(foreground, background)); + } + + yield return Segment.LineBreak; + } + } + + private int GetWidth(int maxWidth) + { + var width = maxWidth; + if (_width != null) + { + width = Math.Min(_width.Value, width); + } + + return width; + } + + private static (float, float, float) ColorFromHSL(double h, double l, double s) + { + double r = 0, g = 0, b = 0; + if (l != 0) + { + if (s == 0) + { + r = g = b = l; + } + else + { + double temp2; + if (l < 0.5) + { + temp2 = l * (1.0 + s); + } + else + { + temp2 = l + s - (l * s); + } + + var temp1 = 2.0 * l - temp2; + + r = GetColorComponent(temp1, temp2, h + 1.0 / 3.0); + g = GetColorComponent(temp1, temp2, h); + b = GetColorComponent(temp1, temp2, h - 1.0 / 3.0); + } + } + + return ((float)r, (float)g, (float)b); + + } + + private static double GetColorComponent(double temp1, double temp2, double temp3) + { + if (temp3 < 0.0) + { + temp3 += 1.0; + } + else if (temp3 > 1.0) + { + temp3 -= 1.0; + } + + if (temp3 < 1.0 / 6.0) + { + return temp1 + (temp2 - temp1) * 6.0 * temp3; + } + else if (temp3 < 0.5) + { + return temp2; + } + else if (temp3 < 2.0 / 3.0) + { + return temp1 + ((temp2 - temp1) * ((2.0 / 3.0) - temp3) * 6.0); + } + else + { + return temp1; + } + } +} diff --git a/examples/Shared/Extensions/ColorExtensions.cs b/examples/Shared/Extensions/ColorExtensions.cs new file mode 100644 index 0000000..cdc6657 --- /dev/null +++ b/examples/Shared/Extensions/ColorExtensions.cs @@ -0,0 +1,14 @@ +namespace Spectre.Console.Examples; + +public static class ColorExtensions +{ + public static Color GetInvertedColor(this Color color) + { + return GetLuminance(color) < 140 ? Color.White : Color.Black; + } + + private static float GetLuminance(this Color color) + { + return (float)((0.2126 * color.R) + (0.7152 * color.G) + (0.0722 * color.B)); + } +} diff --git a/examples/Shared/Shared.csproj b/examples/Shared/Shared.csproj new file mode 100644 index 0000000..a6dcbd2 --- /dev/null +++ b/examples/Shared/Shared.csproj @@ -0,0 +1,16 @@ + + + + net8.0 + false + enable + + + + + + + + + + diff --git a/global.json b/global.json new file mode 100644 index 0000000..af3eb74 --- /dev/null +++ b/global.json @@ -0,0 +1,7 @@ +{ + "$schema": "http://json.schemastore.org/global", + "sdk": { + "version": "8.0.204", + "rollForward": "latestFeature" + } +}