commit a3cc9f5f72e5e6c45227b3007d7aa740e50de6f7 Author: Patrik Svensson Date: Mon Aug 5 21:20:18 2024 +0200 Initial commits 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 0000000..f11d285 Binary files /dev/null and b/examples/Console/Canvas/cake.png differ 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" + } +}