Compare commits

..

70 Commits

Author SHA1 Message Date
Phil Scott
e4dda283bb Adds overloads for MarkUp methods without args
These methods don't require a string.format call so we'll directly call the Render method without a call to string.format.

Added bonus of a a couple fewer allocations too.
2021-03-08 15:04:55 +01:00
Phil Scott
da9c6ee4c2 Add IProgress<double> to ProgressTask.cs
Makes the Report method an explicit implementation to allow for better interoperability with standard .NET progress functionality while keeping backwards compatibility with existing ProgressTask functionality.

Closes #285
2021-03-07 09:24:44 +01:00
Phil Scott
855127f32a Changes progress task IsFinished to account for stopped tasks
Previous behavior was that the only way to get a task to a finished state was to artificially set the Value to MaxValue.

With this change StopTask() will also complete the task with the change that a task cannot be restarted.
2021-03-07 09:24:21 +01:00
stf
fa731070d8 update the docs
To improve discoveratbility of the new option
2021-03-04 07:47:00 -05:00
stf
ef08c5bf2b Improve the unit test around HideCompleted 2021-03-04 07:47:00 -05:00
stf
1c769c6610 Add Progress.HideCompleted 2021-03-04 07:47:00 -05:00
Phil Scott
1cd335e785 Serilog example for logging 2021-03-04 08:39:53 +01:00
Patrik Svensson
29e6e34f83 Support setting the static console 2021-03-01 15:22:35 -05:00
Phil Scott
bff3438a5a using loop instead of linq
In both of these loops context is captured preventing caching of the lambda. this results in a pretty significant amount of allocations especially with progress bars that constantly are remeasuring
2021-03-01 08:22:45 +01:00
Oskar Klintrot
c64884854f Make it possible to set Value directly 2021-02-25 11:56:32 +01:00
Phil Scott
3a42c0a119 Adds DotSettings and tweaks editoconfigs for tests
R# and Rider have quite a bit of noise related to documentation in the testing projects so this disables those warnings.

In the main projects, R# and Rider complain loudly about the namespaces not matching the file structure. The DotSettings file disables that warning.

Once you get rid of that noise there are quite a few opportunities for trimming out redundant code that R# points out especially with the nullable support enabled, plus there are some bugs related to multiple enumerations worth looking into I think.
2021-02-23 22:34:33 +01:00
Thomas Freudenberg
525b414ff8 Make alignment of TaskDescriptionColumn configurable 2021-02-16 23:33:47 +01:00
Thomas Freudenberg
ed0fb29be4 Make default answer for confirmation prompt configurable 2021-02-16 23:32:49 +01:00
Phil Scott
04d0e663d5 Extends maximum size of remaining and elapsed time displayed, plus a failsafe
Elapsed and remaining now support > 9 hours, and if a timespan can't be rendered in that size **:**:** will be displayed
2021-02-16 23:31:55 +01:00
Patrik Svensson
17ee8990f4 Update example image 2021-02-15 13:01:30 +01:00
Bastian Eicher
a1050fc676 Handle output to stderr 2021-02-14 18:08:42 +01:00
Phil Scott
9312663bde Adds text and Progress bar spinner column for tasks yet to be started 2021-02-14 18:03:57 +01:00
Patrik Svensson
102e2dc38d Allow formatting breakdown charts with lambda expr
Relates to #252
2021-02-13 17:09:51 +01:00
Patrik Svensson
28e9c14de4 Register the console lazily in CLI type registrar
This should fix a strange bug we're seeing in Cake on macOS.
2021-02-12 02:04:59 +01:00
Patrik Svensson
fd217ffc83 Update sponsor text 2021-02-11 23:22:43 +01:00
Patrik Svensson
95ec04df40 Add sponsor information 2021-02-11 23:20:11 +01:00
Patrik Svensson
705cf745ea Add formatting support for breakdown chart values
Closes #252
2021-02-05 11:53:55 +01:00
Patrik Svensson
b64e016e8c Add breakdown chart support
This also cleans up the bar chart code slightly and fixes
some minor bugs that were detected in related code.

Closes #244
2021-02-01 01:03:39 +01:00
Patrik Svensson
58400fe74e Fix code generation
Previous changes introduced some bugs to the
code generation scripts and templates, which
now have been fixed.
2021-01-29 21:46:08 +01:00
Patrik Svensson
e20f6284f9 Clean up profile enrichment 2021-01-29 20:16:52 +01:00
David Butler
953008b5e3 Implemented buffer/stream constructors for CanvasImage (#246)
* Implemented buffer/stream constructors for CanvasImage and added section to Canvas example

Signed-off-by: David Butler <mail@davidbutlerdesign.co.uk>
2021-01-27 18:12:22 +01:00
Milosz Krajewski
ad49b6aa67 GH-242: Fix version retrieval for single file applications (#245) 2021-01-26 00:45:38 +01:00
Patrik Svensson
31a5e17a45 Remove InteractivityDetector 2021-01-19 18:12:15 +01:00
Patrik Svensson
f06dc7e7d8 GitHub actions should use default width (for now) 2021-01-19 18:12:15 +01:00
Patrik Svensson
a23bec4082 Add profile support
Closes #231
2021-01-19 17:53:03 +01:00
Patrik Svensson
913a7b1e37 Add support for default choice in selection prompt
Closes #234
2021-01-15 17:05:11 +01:00
Nick
63bae278a9 Add support for selection prompt highlighting 2021-01-15 15:23:09 +01:00
Eslami Sepehr
1a747696a8 Add Persian README 2021-01-15 09:08:37 +01:00
Matt Constable
994540d97f Add cycle detection to tree rendering 2021-01-14 18:37:22 +01:00
Thomas Freudenberg
dee3c01629 mask default value when prompt is a secret 2021-01-14 17:44:18 +01:00
Mattias Karlsson
a3e11b24e5 (GH-226) Switch ParameterValidationAttribute check to IsNullOrWhiteSpace
* fixes #226
2021-01-13 20:31:33 +01:00
Jay Turpin
35568ab823 Updated Commands with with new Execute() method signature 2021-01-13 19:48:15 +01:00
Patrik Svensson
07db28bb6f Add enhancements to progress widget
* Adds TransferSpeedColumn
* Adds DownloadedColumn
* Adds ElapsedTimeColumn
* Minor enhancements to existing columns
2021-01-12 14:10:07 +01:00
ριтєя мαяχ
d87d8e4422 Update exceptions.md 2021-01-10 20:45:44 +01:00
Patrik Svensson
a977fdadff Fix tree node collection type 2021-01-10 16:59:40 +01:00
Patrik Svensson
8261b25e5c Fix tree rendering
Fixes some tree rendering problems where lines were not properly drawn
at some levels during some circumstances.

* Change the API back to only allow one root.
* Now uses a stack based approach to rendering instead of recursion.
* Removes the need for measuring the whole tree in advance.
  Leave this up to each child to render.
2021-01-10 15:55:11 +01:00
Patrik Svensson
0e0f4b4220 Add interactive prompts for selecting values
* Adds SelectionPrompt
* Adds MultiSelectionPrompt

Closes #210
2021-01-09 09:37:28 +01:00
Patrik Svensson
3a593857c8 Add progress and status result overloads 2021-01-06 09:54:45 +01:00
Patrik Svensson
11e192e750 Update Canvas tests 2021-01-06 09:54:45 +01:00
Thomas Freudenberg
8901450283 Allow returning a result from Progress.StartAsync/Status.StartAsync 2021-01-06 08:08:38 +01:00
Patrik Svensson
0796bad598 Add contributing guidelines 2021-01-05 14:38:29 +01:00
Matt Constable
5b553a4106 Added canvas unit tests & fix canvas scaling bug 2021-01-05 14:00:04 +01:00
Patrik Svensson
1bb0b9ccc6 Fix expectation structure 2021-01-05 11:09:35 +01:00
Matt Constable
9ad5f2daeb Fix console detection for Cygwin/WSL-Shell on Windows 2021-01-04 18:24:56 +01:00
Patrik Svensson
1f211d3e1f Add convenience methods for tree nodes 2021-01-03 23:28:55 +01:00
Patrik Svensson
87e6b42409 Add tree example 2021-01-03 23:28:55 +01:00
Patrik Svensson
1aa958ced3 Add support for multiple tree roots 2021-01-03 23:28:55 +01:00
Patrik Svensson
4bfb24bfcb Streamline tree API a bit 2021-01-03 23:28:55 +01:00
Matt Constable
b136d0299b Add tree widget 2021-01-02 10:01:16 +01:00
Patrik Svensson
179e243214 Clean up table rendering a bit 2021-01-02 09:33:22 +01:00
Patrik Svensson
c6210f75ca Prevent endless loop in tokenization
Closes #198
2021-01-01 23:01:51 +01:00
Patrik Svensson
b81739567b Fix argument order in help
Closes #188
2021-01-01 13:43:28 +01:00
Patrik Svensson
5cf41725a5 Do not split remaining args on space
Closes #186
2021-01-01 12:19:37 +01:00
Matthew Constable
f561d71e4e Remove unused Segment.TruncateWithEllipsis method 2020-12-31 17:29:50 +01:00
Simon Cropp
e71db7f78c fix some nullability issues 2020-12-31 11:09:03 +01:00
Patrik Svensson
79742ce9e3 Parse quoted strings correctly
When parsing quoted strings, space was not handled
properly in remaining arguments.

Fixes #186
2020-12-30 18:43:29 +01:00
Patrik Svensson
241423dd16 Do not truncate command table
Temporary fix for commands not showing up if they
are missing a description. This is really a bug in the table
rendering and should be fixed there at some point.

Closes #192
2020-12-30 18:43:29 +01:00
Patrik Svensson
4e2251fd62 Add migration guide for Spectre.Cli 2020-12-29 10:29:26 +01:00
Patrik Svensson
0ae419326d Add Spectre.Cli to Spectre.Console
* Renames Spectre.Cli to Spectre.Console.Cli.
* Now uses Verify with Spectre.Console.Cli tests.
* Removes some duplicate definitions.

Closes #168
2020-12-28 17:28:03 +01:00
Thomas Freudenberg
0bbf9b81a9 Adds optional function to get the display string for TextPrompt choices 2020-12-26 18:17:46 +01:00
Simon Cropp
041aea2ae5 spelling 2020-12-26 18:16:41 +01:00
Simon Cropp
f417e297cd update test refs
the latest of shouldy has better nullable attributes
2020-12-26 18:13:35 +01:00
Simon Cropp
5045b8b959 remove pdb AllowedOutputExtensionsInPackageBuildOutputFolder 2020-12-26 18:12:19 +01:00
Patrik Svensson
7dccb310f3 Add support for bar charts
Closes #103
2020-12-23 10:05:08 +01:00
Patrik Svensson
1cf30f62fc Add autocomplete for text prompt
Closes #166
2020-12-22 11:31:22 +01:00
766 changed files with 22112 additions and 3178 deletions

1
.gitignore vendored
View File

@@ -79,7 +79,6 @@ x64/
_ReSharper*
# NCrunch
*.ncrunch*
.*crunch*.local.xml
_NCrunch_*

161
CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,161 @@
# Contribution Guidelines
* [Prerequisites](#prerequisites)
* [Definition of trivial contributions](#definition-of-trivial-contributions)
* [Code](#code)
* [Code style](#code-style)
* [Dependencies](#dependencies)
* [Unit tests](#unit-tests)
* [Contributing process](#contributing-process)
* [Get buyoff or find open community issues or features](#get-buyoff-or-find-open-community-issues-or-features)
* [Set up your environment](#Set-up-your-environment)
* [Prepare commits](#prepare-commits)
* [Submit pull request](#Submit-pull-request)
* [Respond to feedback on pull request](#respond-to-feedback-on-pull-request)
* [Other general information](#other-general-information)
* [Acknowledgement](#acknowledgement)
## Prerequisites
By contributing to Spectre.Console, you assert that:
* The contribution is your own original work.
* You have the right to assign the copyright for the work (it is not owned by your employer, or
you have been given copyright assignment in writing).
* You [license](https://github.com/spectresystems/spectre.console/blob/main/LICENSE) the contribution under the terms applied to the rest of the Spectre.Console project.
* You agree to follow the [code of conduct](https://github.com/spectresystems/spectre.console/blob/main/CODE_OF_CONDUCT.md).
## Definition of trivial contributions
It's hard to define what is a trivial contribution. Sometimes even a 1 character change can be considered significant.
Unfortunately because it can be subjective, the decision on what is trivial comes from the maintainers of the project
and not from folks contributing to the project.
What is generally considered trivial:
* Fixing a typo.
* Documentation changes.
## Code
### Code style
Normal .NET coding guidelines apply.
See the [Framework Design Guidelines](https://msdn.microsoft.com/en-us/library/ms229042%28v=vs.110%29.aspx) for more information.
### Dependencies
The assembly `Spectre.Console` should have no dependencies except the .NET BCL library.
### Unit tests
Make sure to run all unit tests before creating a pull request.
Any new code should also have reasonable unit test coverage.
## Contributing process
### Get buyoff or find open community issues or features
* Through GitHub, or through the [GitHub discussions](https://github.com/spectresystems/spectre.console/discussions) (preferred),
you talk about a feature you would like to see (or a bug), and why it should be in Spectre.Console.
* If approved through the GitHub discussions, ensure an accompanying GitHub issue is created with
information and a link back to the discussion.
* Once you get a nod from someone in the Spectre.Console Team, you can start on the feature.
* Alternatively, if a feature is on the issues list with the
[Up For Grabs](https://github.com/spectresystems/spectre.console/labels/up-for-grabs) label,
it is open for a community member (contributor) to patch. You should comment that you are signing up for it on
the issue so someone else doesn't also sign up for the work.
### Set up your environment
* You create, or update, a fork of `spectresystems/spectre.console` under your GitHub account.
* From there you create a branch named specific to the feature.
* In the branch you do work specific to the feature.
* Please also observe the following:
* No reformatting
* No changing files that are not specific to the feature.
* More covered below in the **Prepare commits** section.
* Test your changes and please help us out by updating and implementing some automated tests.
It is recommended that all contributors spend some time looking over the tests in the source code.
You can't go wrong emulating one of the existing tests and then changing it specific to the behavior you are testing.
* Please do not update your branch from the main branch unless we ask you to. See the responding to feedback section below.
### Prepare commits
This section serves to help you understand what makes a good commit.
A commit should observe the following:
* A commit is a small logical unit that represents a change.
* Should include new or changed tests relevant to the changes you are making.
* No unnecessary whitespace. Check for whitespace with `git diff --check` and `git diff --cached --check` before commit.
* You can stage parts of a file for commit.
### Submit pull request
Prerequisites:
* You are making commits in a feature branch.
* All code should compile without errors or warnings.
* All tests should be passing.
Submitting PR:
* Once you feel it is ready, submit the pull request to the `spectresystems/spectre.console` repository against the `main` branch
unless specifically requested to submit it against another branch.
* In the case of a larger change that is going to require more discussion,
please submit a PR sooner. Waiting until you are ready may mean more changes than you are
interested in if the changes are taking things in a direction the maintainers do not want to go.
* In the pull request, outline what you did and point to specific conversations (as in URLs)
and issues that you are resolving. This is a tremendous help for us in evaluation and acceptance.
* Once the pull request is in, please do not delete the branch or close the pull request
(unless something is wrong with it).
* One of the Spectre.Console team members, or one of the maintainers, will evaluate it within a
reasonable time period (which is to say usually within 1-3 weeks). Some things get evaluated
faster or fast tracked. We are human and we have active lives outside of open source so don't
fret if you haven't seen any activity on your pull request within a month or two.
We don't have a Service Level Agreement (SLA) for pull requests.
Just know that we will evaluate your pull request.
### Respond to feedback on pull request
We may have feedback for you to fix or change some things. We generally like to see that pushed against
the same topic branch (it will automatically update the Pull Request). You can also fix/squash/rebase
commits and push the same topic branch with `--force` (it's generally acceptable to do this on topic
branches not in the main repository, it is generally unacceptable and should be avoided at all costs
against the main repository).
If we have comments or questions when we do evaluate it and receive no response, it will probably
lessen the chance of getting accepted. Eventually, this means it will be closed if it is not accepted.
Please know this doesn't mean we don't value your contribution, just that things go stale. If in the
future you want to pick it back up, feel free to address our concerns/questions/feedback and reopen
the issue/open a new PR (referencing old one).
Sometimes we may need you to rebase your commit against the latest code before we can review it further.
If this happens, you can do the following:
* `git fetch upstream` (upstream remote would be `spectresystems/spectre.console`)
* `git checkout main`
* `git rebase upstream/main`
* `git checkout your-branch`
* `git rebase main`
* Fix any merge conflicts
* `git push origin your-branch` (origin would be your GitHub repo or `your-github-username/spectre.console` in this case).
You may need to `git push origin your-branch --force` to get the commits pushed.
This is generally acceptable with topic branches not in the mainstream repository.
The only reasons a pull request should be closed and resubmitted are as follows:
* When the pull request is targeting the wrong branch (this doesn't happen as often).
* When there are updates made to the original by someone other than the original contributor.
Then the old branch is closed with a note on the newer branch this supersedes #github_number.
## Other general information
If you reformat code or hit core functionality without an approval from a person on the Spectre.Console Team,
it's likely that no matter how awesome it looks afterwards, it will probably not get accepted.
Reformatting code makes it harder for us to evaluate exactly what was changed.
If you do these things, it will be make evaluation and acceptance easy.
Now if you stray outside of the guidelines we have above, it doesn't mean we are going to ignore
your pull request. It will just make things harder for us.
Harder for us roughly translates to a longer SLA for your pull request.
## Acknowledgement
This contribution guide was taken from the [Chocolatey project](https://chocolatey.org/)
with permission and was edited to follow Spectre.Console's conventions and processes.

77
README.fa.md Normal file
View File

@@ -0,0 +1,77 @@
# `Spectre.Console`
_[![Spectre.Console NuGet Version](https://img.shields.io/nuget/v/spectre.console.svg?style=flat&label=NuGet%3A%20Spectre.Console)](https://www.nuget.org/packages/spectre.console)_
<div dir="rtl">
یک کتابخانه NET Standard 2.0/.NET 5. که ایجاد Console Applicationهای زیبا و cross platform را آسان‌تر می‌کند.
از کتابخانه عالی [Rich](https://github.com/willmcgugan/rich) برای پایتون، بسیار الهام گرفته شده است.
## فهرست
1. [امکانات](#features)
2. [نصب](#installing)
3. [مستندات](#documentation)
4. [مثال‌ها](#examples)
5. [مجوز](#license)
<h2 id="features">امکانات</h2>
* با در نظر گرفتن تست واحد نوشته شده است.
* جداول، چارچوب‌ها، پنل‌ها و یک زبان نشانه گذاری که از [rich](https://github.com/willmcgugan/rich) الهام گرفته شده است را پشتیبانی می‌کند.
* از رایج ترین پارامترهای SRG در هنگام فرم دهی متن مانند پررنگ، کم نور، اریب، زیرخط، خط زدن و چشمک زدن پشتیبانی می‌کند.
* پشتیبانی از رنگ‌های 28/8/4/3-بیت در ترمینال.
این کتابخانه توانایی ترمینال فعلی را تشخیص داده و در صورت لزوم رنگ‌ها را کاهش می‌دهد.
![Example](docs/input/assets/images/example.png)
<h2 id="installing">نصب</h2>
سریع ترین راه برای شروع `Spectre.Console` نصب از طریق NuGet Package می‌باشد.
<pre dir="ltr">
dotnet add package Spectre.Console
</pre>
<h2 id="documentation">مستندات</h2>
مستندات `Spectre.Console` را در اینجا می‌توایند پیدا کنید:
<div dir="ltr">
https://spectresystems.github.io/spectre.console/
</div>
<h2 id="examples">مثال‌ها</h2>
برای بررسی `Spectre.Console` در عمل، ابزار سراسری
[dotnet-example](https://github.com/patriksvensson/dotnet-example)
را نصب کنید.
<pre dir="ltr">
> dotnet tool restore
</pre>
حالا شما می‌توانید مثال‌های موجود در این مخزن را لیست کنید:
<pre dir="ltr">
> dotnet example
</pre>
و برای اجرای مثال:
<pre dir="ltr">
> dotnet example tables
</pre>
<h2 id="license">مجوز</h2>
<div dir="ltr">
Copyright © Spectre Systems.
</div>
همانطور که Spectre.Console تحت مجوز MIT ارائه شده است؛ برای کسب اطلاعات بیشتر به مجوز مراجعه کنید.
* برای SixLabors.ImageSharp، مشاهده کنید: https://github.com/SixLabors/ImageSharp/blob/master/LICENSE
</div>

View File

@@ -29,7 +29,7 @@ Python用の素晴らしい[Rich ライブラリ](https://github.com/willmcgugan
## 例
![Example](resources/gfx/screenshots/example.png)
![Example](docs/input/assets/images/example.png)
## 使用方法
@@ -111,6 +111,7 @@ Spectre.Consoleでできることを見るために、
│ Panels │ examples/Panels/Panels.csproj │ Demonstrates how to render items in panels. │
│ Rules │ examples/Rules/Rules.csproj │ Demonstrates how to render horizontal rules (lines). │
│ Tables │ examples/Tables/Tables.csproj │ Demonstrates how to render tables in a console. │
│ Trees │ examples/Trees/Trees.csproj │ Demonstrates how to render trees in a console. │
╰────────────┴───────────────────────────────────────┴──────────────────────────────────────────────────────╯
```

View File

@@ -12,6 +12,7 @@ for Python.
2. [Installing](#installing)
3. [Documentation](#documentation)
4. [Examples](#examples)
5. [Sponsors](#sponsors)
5. [License](#license)
## Features
@@ -26,7 +27,7 @@ for Python.
and downgrade colors as needed.
![Example](resources/gfx/screenshots/example.png)
![Example](docs/input/assets/images/example.png)
## Installing
@@ -63,6 +64,24 @@ And to run an example:
> dotnet example tables
```
## Sponsors
The following people are [sponsoring](https://github.com/sponsors/patriksvensson)
Spectre.Console to show their support and to ensure the longevity of the project.
* [Rodney Littles II](https://github.com/RLittlesII)
* [Martin Björkström](https://github.com/bjorkstromm)
* [Dave Glick](https://github.com/daveaglick)
* [Kim Gunanrsson](https://github.com/kimgunnarsson)
* [Andrew McClenaghan](https://github.com/andymac4182)
* [C. Augusto Proiete](https://github.com/augustoproiete)
* [Viktor Elofsson](https://github.com/vktr)
* [Steven Knox](https://github.com/stevenknox)
* [David Pendray](https://github.com/dpen2000)
I really appreciate it.
**Thank you very much!**
## License
Copyright © Spectre Systems.

View File

@@ -7,7 +7,7 @@ To start contributing to the [Spectre.Console](https://github.com/spectresystems
The documentation site uses [Statiq](https://statiq.dev), a static site generator. To build the documentation site run the following in a command-line terminal.
```
> dotnet run preview --virtual-dir "spectre.console"
> Preview.ps1
```
After the build is complete, you can navigate to [http://localhost:5080/spectre.console](http://localhost:5080/spectre.console).
@@ -29,13 +29,7 @@ Layout and styling can also be found in the [input](./input) directory. Look for
## Custom Build Features
The documentation site has custom enhancements to Statiq located under the [./src](./src) directory. Enhancements to the build process include:
- [Extension Methods](./src/Extensions)
- [Models](./src/Models)
- [Pipelines](./src/Pipelines)
- [Shortcodes](./src/Shortcodes)
- [Utilities](./src/Utilities)
The documentation site has custom enhancements to Statiq located under the [./src](./src) directory.
## License

View File

@@ -1,5 +1,5 @@
Title: Appendix
Order: 10
Order: 100
---
<h1>Sections</h1>

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 219 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 229 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 208 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

View File

@@ -0,0 +1,12 @@
Title: CLI
Order: 10
---
<h1>Sections</h1>
<ul>
@foreach (IDocument child in OutputPages.GetChildrenOf(Document))
{
<li>@Html.DocumentLink(child)</li>
}
</ul>

View File

@@ -0,0 +1,119 @@
Title: Introduction
Order: 1
---
`Spectre.Console.Cli` is a modern library for parsing command line arguments. While it's extremely
opinionated in what it does, it tries to follow established industry conventions, and draws
its inspiration from applications you use everyday.
# How does it work?
The underlying philosophy behind `Spectre.Console.Cli` is to rely on the .NET type system to
declare the commands, but tie everything together via composition.
Imagine the following command structure:
* dotnet *(executable)*
* add `[PROJECT]`
* package `<PACKAGE_NAME>` --version `<VERSION>`
* reference `<PROJECT_REFERENCE>`
For this I would like to implement the commands (the different levels in the tree that
executes something) separately from the settings (the options, flags and arguments),
which I want to be able to inherit from each other.
## Specify the settings
We start by creating some settings that represents the options, flags and arguments
that we want to act upon.
```csharp
public class AddSettings : CommandSettings
{
[CommandArgument(0, "[PROJECT]")]
public string Project { get; set; }
}
public class AddPackageSettings : AddSettings
{
[CommandArgument(0, "<PACKAGE_NAME>")]
public string PackageName { get; set; }
[CommandOption("-v|--version <VERSION>")]
public string Version { get; set; }
}
public class AddReferenceSettings : AddSettings
{
[CommandArgument(0, "<PROJECT_REFERENCE>")]
public string ProjectReference { get; set; }
}
```
## Specify the commands
Now it's time to specify the commands that act on the settings we created
in the previous step.
```csharp
public class AddPackageCommand : Command<AddPackageSettings>
{
public override int Execute(CommandContext context, AddPackageSettings settings)
{
// Omitted
return 0;
}
}
public class AddReferenceCommand : Command<AddReferenceSettings>
{
public override int Execute(CommandContext context, AddReferenceSettings settings)
{
// Omitted
return 0;
}
}
```
## Let's tie it together
Now when we have our commands and settings implemented, we can compose a command tree
that tells the parser how to interpret user input.
```csharp
using Spectre.Console.Cli;
namespace MyApp
{
public static class Program
{
public static int Main(string[] args)
{
var app = new CommandApp();
app.Configure(config =>
{
config.AddBranch<AddSettings>("add", add =>
{
add.AddCommand<AddPackageCommand>("package");
add.AddCommand<AddReferenceCommand>("reference");
});
});
return app.Run(args);
}
}
}
```
# So why this way?
Now you might wonder, why do things like this? Well, for starters the different parts
of the application are separated, while still having the option to share things like options,
flags and arguments between them.
This make the resulting code very clean and easy to navigate, not to mention to unit test.
And most importantly at all, the type system guides me to do the right thing. I can't configure
commands in non-compatible ways, and if I want to add a new top-level `add-package` command
(or move the command completely), it's just a single line to change. This makes it easy to
experiment and makes the CLI experience a first class citizen of your application.

View File

@@ -0,0 +1,47 @@
Title: Migrate from Spectre.Cli
Order: 2
---
The functionality in `Spectre.Cli` has been moved into the `Spectre.Console`
library. If you're using `Spectre.Cli`, you will need to migrate to ensure
that you get updates or fixes.
## 1. Remove Spectre.Cli NuGet package
Start with removing the `Spectre.Cli` package reference from your project(s).
```text
> dotnet remove package Spectre.Cli
```
## 2. Add Spectre.Console NuGet package
Add the [Spectre.Console](https://www.nuget.org/packages/spectre.console) NuGet package to your project(s).
```text
> dotnet add package Spectre.Console
```
## 3. Change using statements
Change all using statements from `Spectre.Cli`
to `Spectre.Console.CLi`.
```diff
- using Spectre.Cli;
+ using Spectre.Console.Cli;
```
# Breaking Changes
In the process of moving `Spectre.Cli`, there have been some minor breaking changes.
## Spectre.Cli.Exceptions namespace moved
All exceptions have been moved from the `Spectre.Cli.Exceptions` namespace to
the `Spectre.Console.Cli` namespace.
```diff
- using Spectre.Cli.Exceptions;
+ using Spectre.Console.Cli;
```

View File

@@ -2,7 +2,7 @@ Title: Exceptions
Order: 3
---
Exceptions isn't always readable when viewed in the terminal.
Exceptions aren't always readable when viewed in the terminal.
You can make exception a bit more readable by using the `WriteException` method.
```csharp

View File

@@ -22,5 +22,5 @@ for Python written by Will McGugan.
## Examples
<img src="assets/images/table.gif" style="max-width: 100%; margin-top: 15px; margin-bottom: 25px;" />
<img src="https://github.com/spectresystems/spectre.console/raw/main/resources/gfx/screenshots/example.png" style="max-width: 100%;" />
<img src="./assets/images/example.png" style="max-width: 100%; margin-top: 15px; margin-bottom: 25px;" />
<img src="./assets/images/table.gif" style="max-width: 100%;" />

View File

@@ -63,6 +63,7 @@ await AnsiConsole.Progress()
AnsiConsole.Progress()
.AutoRefresh(false) // Turn off auto refresh
.AutoClear(false) // Do not remove the task list when done
.HideCompleted(false) // Hide tasks as they are completed
.Columns(new ProgressColumn[]
{
new TaskDescriptionColumn(), // Task description

View File

@@ -0,0 +1,12 @@
Title: Prompts
Order: 5
---
<h1>Sections</h1>
<ul>
@foreach (IDocument child in OutputPages.GetChildrenOf(Document))
{
<li>@Html.DocumentLink(child)</li>
}
</ul>

View File

@@ -0,0 +1,31 @@
Title: Multi Selection
Order: 3
---
The `MultiSelectionPrompt` can be used when you want the user to select
one or many items from a provided list.
<img src="../assets/images/multiselection.gif" style="width: 100%;" />
# Usage
```csharp
// Ask for the user's favorite fruits
var fruits = AnsiConsole.Prompt(
new MultiSelectionPrompt<string>()
.Title("What are your [green]favorite fruits[/]?")
.NotRequired() // Not required to have a favorite fruit
.PageSize(10)
.AddChoice("Apple")
.AddChoices(new[] {
"Apricot", "Avocado",
"Banana", "Blackcurrant", "Blueberry",
"Cherry", "Cloudberry", "Cocunut",
}));
// Write the selected fruits to the terminal
foreach (string fruit in fruits)
{
AnsiConsole.WriteLine(fruit);
}
```

View File

@@ -0,0 +1,27 @@
Title: Selection
Order: 1
---
The `SelectionPrompt` can be used when you want the user to select
a single item from a provided list.
<img src="../assets/images/selection.gif" style="width: 100%;" />
# Usage
```csharp
// Ask for the user's favorite fruit
var fruit = AnsiConsole.Prompt(
new SelectionPrompt<string>()
.Title("What's your [green]favorite fruit[/]?")
.PageSize(10)
.AddChoice("Apple")
.AddChoices(new[] {
"Apricot", "Avocado",
"Banana", "Blackcurrant", "Blueberry",
"Cherry", "Cloudberry", "Cocunut",
}));
// Echo the fruit back to the terminal
AnsiConsole.WriteLine($"I agree. {fruit} is tasty!");
```

View File

@@ -1,5 +1,6 @@
Title: Prompt
Order: 4
Title: Text
Order: 0
RedirectFrom: prompt
---
Sometimes you want to get some input from the user, and for this

19
docs/input/sponsors.md Normal file
View File

@@ -0,0 +1,19 @@
Title: Sponsors
Order: 0
---
The following people are [sponsoring](https://github.com/sponsors/patriksvensson)
Spectre.Console to show their support and to ensure the longevity of the project.
* [Rodney Littles II](https://github.com/RLittlesII)
* [Martin Björkström](https://github.com/bjorkstromm)
* [Dave Glick](https://github.com/daveaglick)
* [Kim Gunanrsson](https://github.com/kimgunnarsson)
* [Andrew McClenaghan](https://github.com/andymac4182)
* [C. Augusto Proiete](https://github.com/augustoproiete)
* [Viktor Elofsson](https://github.com/vktr)
* [Steven Knox](https://github.com/stevenknox)
* [David Pendray](https://github.com/dpen2000)
I really appreciate it.
**Thank you very much!**

View File

@@ -0,0 +1,75 @@
Title: Bar Chart
Order: 20
---
Use `BarChart` to render bar charts to the console.
<img src="../assets/images/barchart.png" style="width: 100%;" />
# Usage
## Basic usage
```csharp
AnsiConsole.Render(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));
```
## Add items with converter
```csharp
// Create a list of fruits
var items = new List<(string Label, double Value)>
{
("Apple", 12),
("Orange", 54),
("Banana", 33),
};
// Render bar chart
AnsiConsole.Render(new BarChart()
.Width(60)
.Label("[green bold underline]Number of fruits[/]")
.CenterLabel()
.AddItems(items, (item) => new BarChartItem(
item.Label, item.Value, Color.Yellow)));
```
## Add items implementing IBarChartItem
```csharp
public sealed class Fruit : IBarChartItem
{
public string Label { get; set; }
public double Value { get; set; }
public Color? Color { get; set; }
public Fruit(string label, double value, Color? color = null)
{
Label = label;
Value = value;
Color = color;
}
}
// Create a list of fruits
var items = new List<Fruit>
{
new Fruit("Apple", 12, Color.Yellow),
new Fruit("Orange", 54, Color.Red),
new Fruit("Banana", 33, Color.Green),
};
// Render bar chart
AnsiConsole.Render(new BarChart()
.Width(60)
.Label("[green bold underline]Number of fruits[/]")
.CenterLabel()
.AddItem(new Fruit("Mango", 3))
.AddItems(items));
```

View File

@@ -1,5 +1,5 @@
Title: Calendar
Order: 2
Order: 40
RedirectFrom: calendar
---

View File

@@ -1,5 +1,5 @@
Title: Canvas Image
Order: 5
Order: 70
---
To add [ImageSharp](https://github.com/SixLabors/ImageSharp) superpowers to

View File

@@ -1,5 +1,5 @@
Title: Canvas
Order: 4
Order: 60
---
`Canvas` is a widget that allows you to render arbitrary "pixels"

View File

@@ -1,5 +1,5 @@
Title: Figlet
Order: 3
Order: 50
RedirectFrom: figlet
---

View File

@@ -1,5 +1,5 @@
Title: Rule
Order: 1
Order: 30
RedirectFrom: rule
---

View File

@@ -0,0 +1,70 @@
Title: Tree
Order: 10
---
The `Tree` widget can be used to render hierarchical data.
<img src="../assets/images/tree.png" style="width: 100%;" />
# Usage
```csharp
// Create the tree
var tree = new Tree("Root");
// 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());
// Render the tree
AnsiConsole.Render(root);
```
# Collapsing nodes
```csharp
root.AddNode("Label").Collapsed();
```
# Appearance
## Style
```csharp
var root = new Tree("Root")
.Style("white on red");
```
## Guide lines
```csharp
// ASCII guide lines
var root = new Tree("Root")
.Guide(TreeGuide.Ascii);
// Default guide lines
var root = new Tree("Root")
.Guide(TreeGuide.Line);
// Double guide lines
var root = new Tree("Root")
.Guide(TreeGuide.DoubleLine);
// Bold guide lines
var root = new Tree("Root")
.Guide(TreeGuide.BoldLine);
```

View File

@@ -3,7 +3,7 @@
"isRoot": true,
"tools": {
"cake.tool": {
"version": "1.0.0-rc0001",
"version": "1.0.0-rc0002",
"commands": [
"dotnet-cake"
]
@@ -15,7 +15,7 @@
]
},
"dotnet-example": {
"version": "1.1.0",
"version": "1.2.0",
"commands": [
"dotnet-example"
]

View File

@@ -1,15 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<IsPackable>false</IsPackable>
<Title>Borders</Title>
<Description>Demonstrates the different kind of borders.</Description>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Spectre.Console\Spectre.Console.csproj" />
</ItemGroup>
</Project>

View File

@@ -1,15 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<IsPackable>false</IsPackable>
<Title>Calendars</Title>
<Description>Demonstrates how to render calendars.</Description>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Spectre.Console\Spectre.Console.csproj" />
</ItemGroup>
</Project>

View File

@@ -1,22 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
<IsPackable>false</IsPackable>
<Title>Canvas</Title>
<Description>Demonstrates how to render pixels and images.</Description>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Spectre.Console.ImageSharp\Spectre.Console.ImageSharp.csproj" />
<ProjectReference Include="..\..\src\Spectre.Console\Spectre.Console.csproj" />
</ItemGroup>
<ItemGroup>
<None Update="cake.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,16 @@
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(1)]
public int Count { get; set; }
}
}
}

View File

@@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<IsPackable>false</IsPackable>
<ExampleName>Delegates</ExampleName>
<ExampleDescription>Demonstrates how to specify commands as delegates.</ExampleDescription>
<ExampleGroup>Cli</ExampleGroup>
<ExampleVisible>false</ExampleVisible>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\src\Spectre.Console\Spectre.Console.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,39 @@
using System;
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<BarSettings>("bar", Bar)
.WithDescription("Bars the foos"); ;
});
return app.Run(args);
}
private static int Foo(CommandContext context)
{
Console.WriteLine("Foo");
return 0;
}
private static int Bar(CommandContext context, BarSettings settings)
{
for (var index = 0; index < settings.Count; index++)
{
Console.WriteLine("Bar");
}
return 0;
}
}
}

View File

@@ -0,0 +1,47 @@
using System.ComponentModel;
using Demo.Utilities;
using Spectre.Console.Cli;
namespace Demo.Commands
{
[Description("Add a NuGet package reference to the project.")]
public sealed class AddPackageCommand : Command<AddPackageCommand.Settings>
{
public sealed class Settings : AddSettings
{
[CommandArgument(0, "<PACKAGENAME>")]
[Description("The package reference to add.")]
public string PackageName { get; set; }
[CommandOption("-v|--version <VERSION>")]
[Description("The version of the package to add.")]
public string Version { get; set; }
[CommandOption("-f|--framework <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 <SOURCE>")]
[Description("The NuGet package source to use during the restore.")]
public string Source { get; set; }
[CommandOption("--package-directory <PACKAGEDIR>")]
[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;
}
}
}

View File

@@ -0,0 +1,30 @@
using System.ComponentModel;
using Demo.Utilities;
using Spectre.Console.Cli;
namespace Demo.Commands
{
public sealed class AddReferenceCommand : Command<AddReferenceCommand.Settings>
{
public sealed class Settings : AddSettings
{
[CommandArgument(0, "<PROJECTPATH>")]
[Description("The package reference to add.")]
public string ProjectPath { get; set; }
[CommandOption("-f|--framework <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;
}
}
}

View File

@@ -0,0 +1,12 @@
using System.ComponentModel;
using Spectre.Console.Cli;
namespace Demo.Commands
{
public abstract class AddSettings : CommandSettings
{
[CommandArgument(0, "<PROJECT>")]
[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; }
}
}

View File

@@ -0,0 +1,70 @@
using System.ComponentModel;
using Demo.Utilities;
using Spectre.Console.Cli;
namespace Demo.Commands
{
[Description("Build and run a .NET project output.")]
public sealed class RunCommand : Command<RunCommand.Settings>
{
public sealed class Settings : CommandSettings
{
[CommandOption("-c|--configuration <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 <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 <RUNTIMEIDENTIFIER>")]
[Description("The target runtime to run for.")]
public string RuntimeIdentifier { get; set; }
[CommandOption("-p|--project <PROJECTPATH>")]
[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 <LAUNCHPROFILE>")]
[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 <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;
}
}
}

View File

@@ -0,0 +1,41 @@
using System;
using System.ComponentModel;
using Demo.Utilities;
using Spectre.Console.Cli;
namespace Demo.Commands
{
[Description("Launches a web server in the current working directory and serves all files in it.")]
public sealed class ServeCommand : Command<ServeCommand.Settings>
{
public sealed class Settings : CommandSettings
{
[CommandOption("-p|--port <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<string> 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;
}
}
}

View File

@@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<IsPackable>false</IsPackable>
<ExampleName>Demo</ExampleName>
<ExampleDescription>Demonstrates the most common use cases of Spectre.Cli.</ExampleDescription>
<ExampleGroup>Cli</ExampleGroup>
<ExampleVisible>false</ExampleVisible>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\src\Spectre.Console\Spectre.Console.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,37 @@
using Demo.Commands;
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(new[] { "run", "--no-build" });
// Run
config.AddCommand<RunCommand>("run");
// Add
config.AddBranch<AddSettings>("add", add =>
{
add.SetDescription("Add a package or reference to a .NET project");
add.AddCommand<AddPackageCommand>("package");
add.AddCommand<AddReferenceCommand>("reference");
});
// Serve
config.AddCommand<ServeCommand>("serve")
.WithExample(new[] { "serve", "-o", "firefox" })
.WithExample(new[] { "serve", "--port", "80", "-o", "firefox" });
});
return app.Run(args);
}
}
}

View File

@@ -0,0 +1,29 @@
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.Render(table);
}
}
}

View File

@@ -0,0 +1,54 @@
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<string, Verbosity> _lookup;
public VerbosityConverter()
{
_lookup = new Dictionary<string, Verbosity>(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.");
}
}
}

View File

@@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<IsPackable>false</IsPackable>
<ExampleName>Dynamic</ExampleName>
<ExampleDescription>Demonstrates how to define dynamic commands.</ExampleDescription>
<ExampleGroup>Cli</ExampleGroup>
<ExampleVisible>false</ExampleVisible>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\src\Spectre.Console\Spectre.Console.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,20 @@
using System;
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.");
}
Console.WriteLine("Value = {0}", data);
return 0;
}
}
}

View File

@@ -0,0 +1,24 @@
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<MyCommand>($"c{index}")
.WithDescription($"Prints the number {index}")
.WithData(index);
}
});
return app.Run(args);
}
}
}

View File

@@ -0,0 +1,30 @@
using System;
using System.ComponentModel;
using Spectre.Console.Cli;
namespace Injection.Commands
{
public sealed class DefaultCommand : Command<DefaultCommand.Settings>
{
private readonly IGreeter _greeter;
public sealed class Settings : CommandSettings
{
[CommandOption("-n|--name <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;
}
}
}

View File

@@ -0,0 +1,17 @@
using System;
namespace Injection
{
public interface IGreeter
{
void Greet(string name);
}
public sealed class HelloWorldGreeter : IGreeter
{
public void Greet(string name)
{
Console.WriteLine($"Hello {name}!");
}
}
}

View File

@@ -0,0 +1,41 @@
using System;
using Microsoft.Extensions.DependencyInjection;
using Spectre.Console.Cli;
namespace Injection
{
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<object> func)
{
if (func is null)
{
throw new ArgumentNullException(nameof(func));
}
_builder.AddSingleton(service, (provider) => func());
}
}
}

View File

@@ -0,0 +1,21 @@
using System;
using Microsoft.Extensions.DependencyInjection;
using Spectre.Console.Cli;
namespace Injection
{
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)
{
return _provider.GetRequiredService(type);
}
}
}

View File

@@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<IsPackable>false</IsPackable>
<ExampleName>Injection</ExampleName>
<ExampleDescription>Demonstrates how to use dependency injection with Spectre.Cli.</ExampleDescription>
<ExampleGroup>Cli</ExampleGroup>
<ExampleVisible>false</ExampleVisible>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="3.1.8" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\src\Spectre.Console\Spectre.Console.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,23 @@
using Injection.Commands;
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<IGreeter, HelloWorldGreeter>();
var registrar = new TypeRegistrar(registrations);
// Create a new command app with the registrar
// and run it with the provided arguments.
var app = new CommandApp<DefaultCommand>(registrar);
return app.Run(args);
}
}
}

View File

@@ -0,0 +1,35 @@
using Microsoft.Extensions.Logging;
using Spectre.Console;
using Spectre.Console.Cli;
namespace Logging.Commands
{
public class HelloCommand : Command<HelloCommand.Settings>
{
private ILogger<HelloCommand> _logger;
private IAnsiConsole _console;
public HelloCommand(IAnsiConsole console, ILogger<HelloCommand> 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;
}
}
}

View File

@@ -0,0 +1,56 @@
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<string, LogEventLevel> _lookup;
public VerbosityConverter()
{
_lookup = new Dictionary<string, LogEventLevel>(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.");
}
}
}

View File

@@ -0,0 +1,20 @@
using Logging.Commands;
using Serilog.Core;
using Spectre.Console.Cli;
namespace Logging
{
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;
}
}
}
}

View File

@@ -0,0 +1,38 @@
using Serilog.Core;
using Serilog.Events;
namespace Logging
{
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);
}
}
}

View File

@@ -0,0 +1,41 @@
using System;
using Microsoft.Extensions.DependencyInjection;
using Spectre.Console.Cli;
namespace Logging
{
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<object> func)
{
if (func is null)
{
throw new ArgumentNullException(nameof(func));
}
_builder.AddSingleton(service, _ => func());
}
}
}

View File

@@ -0,0 +1,21 @@
using System;
using Microsoft.Extensions.DependencyInjection;
using Spectre.Console.Cli;
namespace Logging
{
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)
{
return _provider.GetRequiredService(type);
}
}
}

View File

@@ -0,0 +1,26 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<IsPackable>false</IsPackable>
<ExampleName>Logging</ExampleName>
<ExampleDescription>Demonstrates how to dynamically configure Serilog for logging using parameters from a command.</ExampleDescription>
<ExampleGroup>Cli</ExampleGroup>
<ExampleVisible>false</ExampleVisible>
<Nullable>disable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="5.0.1" />
<PackageReference Include="Serilog" Version="2.10.0" />
<PackageReference Include="Serilog.Extensions.Logging" Version="3.0.1" />
<PackageReference Include="Serilog.Sinks.File" Version="4.1.0" />
<PackageReference Include="Serilog.Sinks.Map" Version="1.0.2" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\src\Spectre.Console\Spectre.Console.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,54 @@
using Logging.Commands;
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 it's 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<LoggingEnricher>()
// 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<HelloCommand>("hello");
});
return app.Run(args);
}
}
}

View File

@@ -1,15 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<IsPackable>false</IsPackable>
<Title>Colors</Title>
<Description>Demonstrates how to use [yellow]c[/][red]o[/][green]l[/][blue]o[/][aqua]r[/][lime]s[/] in the console.</Description>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Spectre.Console\Spectre.Console.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<ExampleTitle>Borders</ExampleTitle>
<ExampleDescription>Demonstrates the different kind of borders.</ExampleDescription>
<ExampleGroup>Widgets</ExampleGroup>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\src\Spectre.Console\Spectre.Console.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<ExampleTitle>Calendars</ExampleTitle>
<ExampleDescription>Demonstrates how to render calendars.</ExampleDescription>
<ExampleGroup>Widgets</ExampleGroup>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\src\Spectre.Console\Spectre.Console.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<ExampleTitle>Canvas</ExampleTitle>
<ExampleDescription>Demonstrates how to render pixels and images.</ExampleDescription>
<ExampleGroup>Widgets</ExampleGroup>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\src\Spectre.Console.ImageSharp\Spectre.Console.ImageSharp.csproj" />
<ProjectReference Include="..\..\..\src\Spectre.Console\Spectre.Console.csproj" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="cake.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</EmbeddedResource>
</ItemGroup>
</Project>

View File

@@ -1,3 +1,5 @@
using System.Diagnostics;
using System.Reflection;
using SixLabors.ImageSharp.Processing;
using Spectre.Console;
using Spectre.Console.Rendering;
@@ -23,6 +25,16 @@ namespace CanvasExample
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)

View File

Before

Width:  |  Height:  |  Size: 52 KiB

After

Width:  |  Height:  |  Size: 52 KiB

View File

@@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<ExampleTitle>Charts</ExampleTitle>
<ExampleDescription>Demonstrates how to render charts in a console.</ExampleDescription>
<ExampleGroup>Widgets</ExampleGroup>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\src\Spectre.Console\Spectre.Console.csproj" />
</ItemGroup>
</Project>

View File

@@ -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()
.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.Render(
new Panel(chart)
.Padding(1, 1)
.Header(title));
}
}
}

View File

@@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<ExampleTitle>Colors</ExampleTitle>
<ExampleDescription>Demonstrates how to use [yellow]c[/][red]o[/][green]l[/][blue]o[/][aqua]r[/][lime]s[/] in the console.</ExampleDescription>
<ExampleGroup>Misc</ExampleGroup>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\src\Spectre.Console\Spectre.Console.csproj" />
</ItemGroup>
</Project>

View File

@@ -6,7 +6,7 @@ namespace ColorExample
{
public static void Main()
{
if (AnsiConsole.Capabilities.ColorSystem == ColorSystem.NoColors)
if (AnsiConsole.Profile.ColorSystem == ColorSystem.NoColors)
{
/////////////////////////////////////////////////////////////////
// No colors
@@ -16,7 +16,7 @@ namespace ColorExample
return;
}
if (AnsiConsole.Capabilities.Supports(ColorSystem.Legacy))
if (AnsiConsole.Profile.Supports(ColorSystem.Legacy))
{
/////////////////////////////////////////////////////////////////
// 3-BIT
@@ -39,7 +39,7 @@ namespace ColorExample
}
}
if (AnsiConsole.Capabilities.Supports(ColorSystem.Standard))
if (AnsiConsole.Profile.Supports(ColorSystem.Standard))
{
/////////////////////////////////////////////////////////////////
// 4-BIT
@@ -62,7 +62,7 @@ namespace ColorExample
}
}
if (AnsiConsole.Capabilities.Supports(ColorSystem.EightBit))
if (AnsiConsole.Profile.Supports(ColorSystem.EightBit))
{
/////////////////////////////////////////////////////////////////
// 8-BIT
@@ -89,7 +89,7 @@ namespace ColorExample
}
}
if (AnsiConsole.Capabilities.Supports(ColorSystem.TrueColor))
if (AnsiConsole.Profile.Supports(ColorSystem.TrueColor))
{
/////////////////////////////////////////////////////////////////
// 24-BIT

View File

@@ -3,9 +3,9 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<IsPackable>false</IsPackable>
<Title>Columns</Title>
<Description>Demonstrates how to render data into columns.</Description>
<ExampleTitle>Columns</ExampleTitle>
<ExampleDescription>Demonstrates how to render data into columns.</ExampleDescription>
<ExampleGroup>Widgets</ExampleGroup>
</PropertyGroup>
<ItemGroup>
@@ -13,7 +13,7 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Spectre.Console\Spectre.Console.csproj" />
<ProjectReference Include="..\..\..\src\Spectre.Console\Spectre.Console.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<ExampleTitle>Cursor</ExampleTitle>
<ExampleDescription>Demonstrates how to move the cursor.</ExampleDescription>
<ExampleGroup>Misc</ExampleGroup>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\src\Spectre.Console\Spectre.Console.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<ExampleTitle>Emojis</ExampleTitle>
<ExampleDescription>Demonstrates how to render emojis.</ExampleDescription>
<ExampleGroup>Misc</ExampleGroup>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\src\Spectre.Console\Spectre.Console.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<ExampleTitle>Exceptions</ExampleTitle>
<ExampleDescription>Demonstrates how to render formatted exceptions.</ExampleDescription>
<ExampleGroup>Misc</ExampleGroup>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\src\Spectre.Console\Spectre.Console.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<ExampleTitle>Figlet</ExampleTitle>
<ExampleDescription>Demonstrates how to render FIGlet text.</ExampleDescription>
<ExampleGroup>Widgets</ExampleGroup>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\src\Spectre.Console\Spectre.Console.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<ExampleTitle>Grids</ExampleTitle>
<ExampleDescription>Demonstrates how to render grids in a console.</ExampleDescription>
<ExampleGroup>Widgets</ExampleGroup>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\src\Spectre.Console\Spectre.Console.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<ExampleTitle>Info</ExampleTitle>
<ExampleDescription>Displays the capabilities of the current console.</ExampleDescription>
<ExampleGroup>Misc</ExampleGroup>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\src\Spectre.Console\Spectre.Console.csproj" />
</ItemGroup>
</Project>

View File

@@ -9,12 +9,16 @@ namespace InfoExample
var grid = new Grid()
.AddColumn(new GridColumn().NoWrap().PadRight(4))
.AddColumn()
.AddRow("[b]Color system[/]", $"{AnsiConsole.Capabilities.ColorSystem}")
.AddRow("[b]Supports ansi?[/]", $"{YesNo(AnsiConsole.Capabilities.SupportsAnsi)}")
.AddRow("[b]Legacy console?[/]", $"{YesNo(AnsiConsole.Capabilities.LegacyConsole)}")
.AddRow("[b]Interactive?[/]", $"{YesNo(AnsiConsole.Capabilities.SupportsInteraction)}")
.AddRow("[b]Buffer width[/]", $"{AnsiConsole.Console.Width}")
.AddRow("[b]Buffer height[/]", $"{AnsiConsole.Console.Height}");
.AddRow("[b]Enrichers[/]", string.Join(", ", AnsiConsole.Profile.Enrichers))
.AddRow("[b]Color system[/]", $"{AnsiConsole.Profile.ColorSystem}")
.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]TTY?[/]", $"{YesNo(AnsiConsole.Profile.Capabilities.Tty)}")
.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.Render(
new Panel(grid)

View File

@@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<ExampleTitle>Links</ExampleTitle>
<ExampleDescription>Demonstrates how to render links in a console.</ExampleDescription>
<ExampleGroup>Misc</ExampleGroup>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\src\Spectre.Console\Spectre.Console.csproj" />
</ItemGroup>
</Project>

View File

@@ -6,7 +6,7 @@ namespace LinkExample
{
public static void Main()
{
if (AnsiConsole.Capabilities.SupportLinks)
if (AnsiConsole.Profile.Capabilities.Links)
{
AnsiConsole.MarkupLine("[link=https://patriksvensson.se]Click to visit my blog[/]!");
}

View File

@@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<ExampleTitle>Panels</ExampleTitle>
<ExampleDescription>Demonstrates how to render items in panels.</ExampleDescription>
<ExampleGroup>Widgets</ExampleGroup>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\src\Spectre.Console\Spectre.Console.csproj" />
</ItemGroup>
</Project>

View File

@@ -3,9 +3,9 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<IsPackable>false</IsPackable>
<Title>Progress</Title>
<Description>Demonstrates how to show progress bars.</Description>
<ExampleTitle>Progress</ExampleTitle>
<ExampleDescription>Demonstrates how to show progress bars.</ExampleDescription>
<ExampleGroup>Status</ExampleGroup>
</PropertyGroup>
<ItemGroup>
@@ -13,7 +13,7 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Spectre.Console\Spectre.Console.csproj" />
<ProjectReference Include="..\..\..\src\Spectre.Console\Spectre.Console.csproj" />
</ItemGroup>
</Project>

View File

@@ -7,7 +7,7 @@ namespace Cursor
public static void Main(string[] args)
{
// Check if we can accept key strokes
if (!AnsiConsole.Capabilities.SupportsInteraction)
if (!AnsiConsole.Profile.Capabilities.Interactive)
{
AnsiConsole.MarkupLine("[red]Environment does not support interaction.[/]");
return;
@@ -20,26 +20,87 @@ namespace Cursor
return;
}
// String
// Ask the user for some different things
var name = AskName();
var fruit = AskFruit();
var sport = AskSport();
var age = AskAge();
var password = AskPassword();
var color = AskColor();
// Summary
AnsiConsole.WriteLine();
AnsiConsole.Render(new Rule("[yellow]Results[/]").RuleStyle("grey").LeftAligned());
AnsiConsole.Render(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]Favorite color[/]", string.IsNullOrEmpty(color) ? "Unknown" : color));
}
private static string AskName()
{
AnsiConsole.WriteLine();
AnsiConsole.Render(new Rule("[yellow]Strings[/]").RuleStyle("grey").LeftAligned());
var name = AnsiConsole.Ask<string>("What's your [green]name[/]?");
return name;
}
// String with choices
private static string AskFruit()
{
AnsiConsole.WriteLine();
AnsiConsole.Render(new Rule("[yellow]Lists[/]").RuleStyle("grey").LeftAligned());
var favorites = AnsiConsole.Prompt(
new MultiSelectionPrompt<string>()
.PageSize(10)
.Title("What are your [green]favorite fruits[/]?")
.AddChoices(new[]
{
"Apple", "Apricot", "Avocado", "Banana", "Blackcurrant", "Blueberry",
"Cherry", "Cloudberry", "Cocunut", "Date", "Dragonfruit", "Durian",
"Egg plant", "Elderberry", "Fig", "Grape", "Guava", "Honeyberry",
"Jackfruit", "Jambul", "Kiwano", "Kiwifruit", "Lime", "Lylo",
"Lychee", "Melon", "Mulberry", "Nectarine", "Orange", "Olive"
}));
var fruit = favorites.Count == 1 ? favorites[0] : null;
if (string.IsNullOrWhiteSpace(fruit))
{
fruit = AnsiConsole.Prompt(
new SelectionPrompt<string>()
.Title("Ok, but if you could only choose [green]one[/]?")
.AddChoices(favorites));
}
AnsiConsole.MarkupLine("Your selected: [yellow]{0}[/]", fruit);
return fruit;
}
private static string AskSport()
{
AnsiConsole.WriteLine();
AnsiConsole.Render(new Rule("[yellow]Choices[/]").RuleStyle("grey").LeftAligned());
var fruit = AnsiConsole.Prompt(
new TextPrompt<string>("What's your [green]favorite fruit[/]?")
.InvalidChoiceMessage("[red]That's not a valid fruit[/]")
.DefaultValue("Orange")
.AddChoice("Apple")
.AddChoice("Banana")
.AddChoice("Orange"));
// Integer
return AnsiConsole.Prompt(
new TextPrompt<string>("What's your [green]favorite sport[/]?")
.InvalidChoiceMessage("[red]That's not a valid fruit[/]")
.DefaultValue("Lol")
.AddChoice("Soccer")
.AddChoice("Hockey")
.AddChoice("Basketball"));
}
private static int AskAge()
{
AnsiConsole.WriteLine();
AnsiConsole.Render(new Rule("[yellow]Integers[/]").RuleStyle("grey").LeftAligned());
var age = AnsiConsole.Prompt(
return AnsiConsole.Prompt(
new TextPrompt<int>("How [green]old[/] are you?")
.PromptStyle("green")
.ValidationErrorMessage("[red]That's not a valid age[/]")
@@ -52,33 +113,27 @@ namespace Cursor
_ => ValidationResult.Success(),
};
}));
}
// Secret
private static string AskPassword()
{
AnsiConsole.WriteLine();
AnsiConsole.Render(new Rule("[yellow]Secrets[/]").RuleStyle("grey").LeftAligned());
var password = AnsiConsole.Prompt(
return AnsiConsole.Prompt(
new TextPrompt<string>("Enter [green]password[/]?")
.PromptStyle("red")
.Secret());
}
// Optional
private static string AskColor()
{
AnsiConsole.WriteLine();
AnsiConsole.Render(new Rule("[yellow]Optional[/]").RuleStyle("grey").LeftAligned());
var color = AnsiConsole.Prompt(
return AnsiConsole.Prompt(
new TextPrompt<string>("[grey][[Optional]][/] What is your [green]favorite color[/]?")
.AllowEmpty());
// Summary
AnsiConsole.WriteLine();
AnsiConsole.Render(new Rule("[yellow]Results[/]").RuleStyle("grey").LeftAligned());
AnsiConsole.Render(new Table().AddColumns("[grey]Question[/]", "[grey]Answer[/]")
.RoundedBorder()
.BorderColor(Color.Grey)
.AddRow("[grey]Name[/]", name)
.AddRow("[grey]Favorite fruit[/]", fruit)
.AddRow("[grey]Age[/]", age.ToString())
.AddRow("[grey]Password[/]", password)
.AddRow("[grey]Favorite color[/]", string.IsNullOrEmpty(color) ? "Unknown" : color));
}
}
}

Some files were not shown because too many files have changed in this diff Show More