Compare commits

..

124 Commits

Author SHA1 Message Date
tk
37a04f3a74 * Support command description localization
eg.
 [CommandOption("-a|--args")]
 [Description(nameof(Str.GitArgs))]
 [Localization(typeof(Str))]
 public string Args { get; set; }
2025-07-03 11:00:59 +08:00
f32f80dc57 Fix resizing of Live views with reduced size. (#1840) 2025-06-21 12:30:18 +02:00
7f3ebe02c4 Reduce memory usage for rune width cache. (#1756) 2025-06-20 12:49:45 +02:00
d77bfb6391 chore: Update dependency AngleSharp to 1.3.0 (#1809)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-18 18:05:15 +02:00
7819f0693d chore: Update dependency SixLabors.ImageSharp to 3.1.10 (#1812)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-18 18:01:14 +02:00
465be9391b chore: Update dependency Microsoft.Playwright to 1.52.0 (#1816)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-18 18:00:41 +02:00
7e5ddb1efe chore: Update dependency Verify.Xunit to v30 (#1818)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-18 17:57:36 +02:00
aabe8eeaf8 chore: Update dependency Scriban to 6.2.1 (#1808)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-18 17:56:33 +02:00
108b23fca8 chore: Update dependency xunit.runner.visualstudio to 3.1.1 (#1820)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-18 17:55:15 +02:00
7051bc9e2d chore: Update dependency Microsoft.NET.Test.Sdk to 17.14.1 (#1822)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-18 17:52:31 +02:00
65bab890f2 Update System.Memory dependency 2025-06-05 10:34:24 +02:00
bd0e2d3e22 Fixed link not dispalyed in markup in Style.cs and added unit test cases (#1750) 2025-06-02 10:19:53 +02:00
9efc426eb9 Added documentation for align widget 2025-06-01 13:54:15 +02:00
2570202990 Fix build errors 2025-05-25 16:42:43 +02:00
e4b5b56d93 Update help output for required options 2025-05-25 16:42:43 +02:00
67c3909bbb Add support for required options 2025-05-25 16:42:43 +02:00
d836ad1805 Add ShowRowSeparators in Table Widget docs (#1807) 2025-04-14 18:34:09 +02:00
57dd8ee410 #1718 TestConsole can now be configured and accessed in CommandAppTester (#1803)
* TestConsole can now be configured and accessed in CommandAppTester
* Add test with mocked user inputs for interactive command
* Add documentation for using the CommandAppTester

Co-authored-by: Patrik Svensson <patriksvensson@users.noreply.github.com>
Co-authored-by: Marek Magath <Marek.Magath@solarwinds.com>
2025-04-14 10:38:03 +02:00
6105ee2a86 Fix IndexOutOfRangeException
Fix `IndexOutOfRangeException` if an exception does not have an associated stack trace.

Resolves #1798.
2025-04-09 12:36:53 +02:00
b5c839030c Blog post: Fix broken PR links 2025-04-08 20:45:50 +02:00
b08ca1c4d7 Add blog post 2025-04-08 18:29:25 +02:00
68fcfe0de4 Update dependencies and .NET SDK 2025-04-08 17:30:03 +02:00
b0f82d787d Documentation improvements for 1.0 release (#1620)
* Rewrote the Dependency Injection section for better clarity and readability.
* Syntax fix: Should reference interfaces, not implementation
* Renamed TypeRegistrar to MyTypeRegistrar, making it more obvious it is a custom class the user must provide.
2025-04-08 17:01:17 +02:00
1dabf25e1c Add testing documentation (#1631) 2025-04-08 16:58:25 +02:00
958820dd66 Tighten up when to show/hide the application "-v|--version" option. 2025-04-08 16:53:43 +02:00
c4a97f3c89 Improved unit test coverage using Spectre.Console.Tests.Data.VersionCommand 2025-04-08 16:53:43 +02:00
4ac88b5d3f Help writer unit tests; including coverage of when the -v|--version should (and shouldn't) appear in the help output 2025-04-08 16:53:43 +02:00
c937c8800a Updated test names for better self-documentation 2025-04-08 16:43:49 +02:00
349eac1e22 Bug fix: Tests with remaining args should not be expected to pass strict parsing 2025-04-08 16:43:49 +02:00
2f8a38f169 Add strict parsing to all version tests, an obvious omission in main 2025-04-08 16:43:49 +02:00
e9f9f56189 Check if the command has a version option on its setting class 2025-04-08 16:43:49 +02:00
cefb51df7b Unit tests to ensure VersionCommand executes when -v|--version is specified, rather than showing the ApplicationVersion number 2025-04-08 16:43:49 +02:00
75b3b83210 Removed unnecessary using directive 2025-04-08 16:43:49 +02:00
dfdd129dd0 Display the application version (if set) when a version flag is the first argument, even if a default command has been set. 2025-04-08 16:43:49 +02:00
17c7a4f7d6 Cover -v and --version options in unit tests 2025-04-08 16:43:49 +02:00
520efe07e2 Significant improvement to the command line parsing 2025-04-08 16:38:37 +02:00
c81bc5fe1d Fix tests with incorrect validation that were in the main branch
Tests had incorrect expectations for the parsing of branch + default command arguments
2025-04-08 16:38:37 +02:00
edf7f23957 Add strict parsing to almost all branches tests, an obvious omission 2025-04-08 16:38:37 +02:00
80a8b0e406 Cosmetic: Remove ugly looking variable naming 2025-04-08 16:38:37 +02:00
dca67da8cd New unit tests to ensure unknown flags are added to remaining args 2025-04-08 16:38:37 +02:00
78272e62e6 Merge pull request #1779 from Moustafaa91/bug/1776 2025-03-24 00:05:14 +01:00
540fb0195f Upgrade SixLabors.ImageSharp to 3.1.7 2025-03-23 13:44:18 +00:00
93668e92b6 Changed IConfigurator to return IConfigurator instead of void for (#1762) 2025-02-24 20:57:23 +00:00
11a320c7c9 Conditionally trim trailing periods of argument and option descriptions (#1740) 2025-02-11 21:13:30 +00:00
c1eb94c1db Merge pull request #1755 from 0xced/fix-generic-exception-formatting 2025-02-05 18:39:38 +01:00
9d8d3c1d6d Fix generic exception formatting with shortened types
Fixes #1754
2025-02-05 18:20:21 +01:00
7e1142df58 Add tests for generic exception formatting 2025-02-05 18:20:21 +01:00
a6b96e9297 Include resource files for additional cultures in HelpProvider. (#1717)
* Creation of Resource files for HelpProvider in the following cultures: it, ja, ko, pt, ru, and zh-Hans.
* Add unit test files and update InlineData for new cultures.
* Include --version option in CommandAppTests.Help.cs and update related output files
2025-01-31 21:23:40 +00:00
f1f633cc72 Merge pull request #1747 from phil-scott-78/spinner-extension 2025-01-28 18:41:34 +01:00
05ce33615e Add async spinner extension methods and related documentation 2025-01-28 10:20:24 -05:00
97715f2553 Merge pull request #1743 from spectreconsole/FrankRay78-disable-blank-issue 2025-01-25 20:20:59 +01:00
039553efbb Disable the GitHub 'Blank issue' option offered to users 2025-01-25 18:30:02 +00:00
f704f2a0e8 Merge pull request #1739 from FrankRay78/1738-CommandAppTester-is-trimming-TestConsole-output 2025-01-21 10:33:25 +01:00
8c5264d117 Trimming of TestConsole output by CommandAppTester is configurable 2025-01-21 08:39:31 +00:00
58bf89a56a Implement 3 digit hex parsing (#1708) 2024-12-04 16:31:21 +01:00
29ab313bb9 Async overloads for AnsiConsole Prompt/Ask/Confirm. (#1194)
* Add async overloads for Prompt/Ask/Confirm.

* Added unit test coverage - AskAsync, ConfirmAsync

* Reordered methods to group non-async/async of the same tests together

---------

Co-authored-by: Frank Ray <52075808+FrankRay78@users.noreply.github.com>
2024-11-28 10:56:19 +00:00
92daeb739d Update Docs SDK version to 9.0.100, disable NuGetAudit temporarily, and upgrade Scriban to version 5.12.0 for generator 2024-11-23 21:05:36 +01:00
43e9669395 Update documentation packages to latest versions
* HighlightService was modified to support GetClassifiedSpansAsync method
* Updated Program.cs to pull in the proper location of the Json and ImageSharp extension projects
* Statiq must have changed how they are doing xrefs, so I adjusted those to be more specific with a few links
2024-11-23 14:48:46 +01:00
ab8384acf6 Update PolySharp to version 1.15.0 and remove Nullable package references 2024-11-23 14:47:01 +01:00
b2689be3ed Enhance ExceptionFormatter for AOT compatibility by adding fallback handling for stack frames. 2024-11-22 22:55:01 +01:00
835143d95f Add Native AOT support documentation and update project files for AOT compatibility 2024-11-22 22:55:01 +01:00
8f2a859087 Fallback to using GetConverter if GetIntrinsicConverter doesn't find one. 2024-11-22 22:55:01 +01:00
2be8e8da4e Adds fallback for ExceptionFormatter in AOT 2024-11-22 22:55:01 +01:00
2a8810affd Explicitly marks Spectre.Console.Cli as not trimmable and not appropriate for AOT scenarios. Additionally adds a warning to CommandApp for users who may try it. 2024-11-22 22:55:01 +01:00
e1d21e7e61 Adding polysharp properly 2024-11-22 22:55:01 +01:00
c7c3ebdf57 Fix assembly name retrieval for FSharp.Core in TypeNameHelper to work better in AOT scenarios 2024-11-22 22:55:01 +01:00
b67af32423 Add RequiresDynamicCode attribute to exception formatter to indicate incompatability with AOT 2024-11-22 22:55:01 +01:00
10773a5625 Refactor enum value retrieval to use EnumUtils for better compatibility with NetStandard 2.0 and AOT 2024-11-22 22:55:01 +01:00
4802751357 Add AOT compatibility and PolySharp package support 2024-11-22 22:55:01 +01:00
4515d89705 ProgressTask.GetPercentage() returns 100 when max value is 0 (#1694) 2024-11-22 12:44:00 +01:00
be45494d6e Merge pull request #1691 from phil-scott-78/emoji-perf 2024-11-21 00:30:24 +01:00
69689d2ba1 Changes Emoji dictionary to OrdinalIgnoreCase for performance
This code is ran on startup, and instantiating a new InvariantCultureIgnoreCase was taking up 11ms, which was half the time in AOT.
2024-11-20 17:55:33 -05:00
aa9e5c48c6 Adding TransferSpeedColumn configuration to display bits/bytes + binary/decimal prefixes (#904)
* Adding configuration to TransferSpeedColumn to be able to display in both bytes/bits, as well as using binary/decimal prefix definitions.

---------

Co-authored-by: Frank Ray <52075808+FrankRay78@users.noreply.github.com>
2024-11-19 15:41:49 +00:00
8d06daf355 Fixed typo in Segment description (#1687) 2024-11-19 06:09:22 +01:00
cecfdc386c Fix typos (#1684) 2024-11-13 15:00:21 +01:00
375a708c43 Add net9.0 support, remove legacy TFM:s (#1682)
* Update .NET SDK to 9.0.100
* Remove net6.0
* Remove net7.0
* Update dependencies
* Install correct .NET SDK:s in GitHub Actions
2024-11-13 14:54:01 +01:00
574ead6d46 Added hex color conversion (#1432)
* Added hex color conversion

---------

Co-authored-by: Frank Ray <52075808+FrankRay78@users.noreply.github.com>
Co-authored-by: Nils Andresen <nils@nils-andresen.de>
2024-11-13 13:35:26 +01:00
a87277e859 Adding Enricher for Azure Pipelines (#1675) 2024-11-04 16:30:35 +01:00
fdc03f2081 Merge pull request #1514 from BlazeFace/blz/issues/1390 2024-10-30 01:36:05 +01:00
3eb620a44e Fix typo 2024-10-26 07:27:31 -07:00
bc88da8056 Added last file and used fluent style, relocated test 2024-10-26 07:25:38 -07:00
00b9aecb4f Add new test files 2024-10-26 07:18:52 -07:00
444dbed259 Merge remote-tracking branch 'upstream/main' into blz/issues/1390
# Conflicts:
#	src/Tests/Spectre.Console.Tests/Expectations/Widgets/Layout/Render_Layout_With_Three_And_One_Columns.Output.verified.txt
2024-10-25 17:47:02 -07:00
a32dc8030b Fixed docs examples URLs redirecting to the examples repository 2024-10-18 11:33:23 +02:00
75547b2436 chore: Update dependency Verify.Xunit to 26.4.5 2024-09-14 14:47:38 +02:00
22d1cbe01f chore: Update dependency Roslynator.Analyzers to 4.12.5 2024-09-14 14:36:40 +02:00
7f8ed509bb Fix strange sentence in docs 2024-09-14 14:15:32 +02:00
c70a8b8fc5 Improve exception if a (multi)selection prompt is used incorrectly
Before this commit, the selection prompt would throw an `InvalidOperationException` (Sequence contains no elements) and the multi selection prompt would throw an `ArgumentOutOfRangeException` (Index was out of range. Must be non-negative and less than the size of the collection.)

Both would occur because the prompts were never meant to be empty.
2024-09-11 10:16:21 +02:00
b470af11f7 chore: Update dependency Microsoft.NET.Test.Sdk to 17.11.1 (#1630)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-11 09:00:27 +02:00
23b160a3f5 chore: Update dependency Verify.Xunit to 26.4.4 (#1624)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-11 08:57:27 +02:00
1345a6347a chore: Update dependency MinVer to v6 (#1629)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-11 08:52:25 +02:00
78f3f80b17 Update documentation: add example for the Text Prompt usage (#1636) 2024-09-10 09:03:20 +02:00
a55b80220d Enhance the style of the checkboxes for multi-selection (#1244)
* Enhance the checkboxes' style for multi-selection by applying the selected style, if available, to the checkboxes currently selected.
2024-09-09 16:35:01 +02:00
f8a4b2271d Add unit test to ensure code coverage 2024-09-09 14:28:13 +02:00
dba7ad0875 Fix SA1513 line 38 Tree.cs 2024-09-09 14:28:13 +02:00
322ed2efbb Fix error SA1300 in full property creation 2024-09-09 14:28:13 +02:00
156d254208 Fix issue 1153 on expanded tree 2024-09-09 14:28:13 +02:00
3437130bf0 Removed blank line 2024-09-09 10:21:00 +02:00
32384f7b8d Add custom highligh style for single calendar event 2024-09-09 10:21:00 +02:00
32361d3f15 Cleanup prompt tests (#1635)
* Move Should_Search_In_Remapped_Result into the SelectionPromptTests class where it belongs
* Use file sealed class for CustomItem, like it's done with CustomSelectionItem in the selection prompt tests
2024-09-07 15:19:55 +01:00
b9d2d2df6d Add spanish translation for help strings 2024-09-06 20:36:30 +02:00
8e44a83737 Simplify InternalsVisibleTo
If the $(PublicKey) property is used, the SDK targets will automatically use it for the assembly attributes.

See https://github.com/dotnet/sdk/pull/3439

Simplifies https://github.com/spectreconsole/spectre.console/pull/1623
2024-09-06 20:31:18 +02:00
fd69ad0b01 Fix search bug in prompt related to custom item types
Closes #1626
2024-09-03 00:46:18 +02:00
753894de94 Bump Spectre.Verify.Extensions to strong-named version
Remove NoWarn now that Spectre.Verify.Extensions is strong-named
2024-09-02 02:49:21 +02:00
dc2cb40b79 chore: Update rickstaa/top-issues-action action to v1.3.101 2024-08-30 09:40:24 +02:00
511f798f0f chore: Update dependency Microsoft.NET.Test.Sdk to 17.11.0 2024-08-30 09:34:15 +02:00
2081c0fd9a chore: Update dependency dotnet-sdk to v8.0.401 2024-08-30 09:30:53 +02:00
96512f353f Fix regression in Razor syntax 2024-08-30 09:18:07 +02:00
56feea11a1 Fix the workflow. Third time the charm? 2024-08-30 09:01:52 +02:00
45c24055fa (Hopefully) fix workflow 2024-08-30 09:01:03 +02:00
d56139756c Lock SDK for documentation for now 2024-08-30 08:59:15 +02:00
7af1eedca7 Merge pull request #1623 from KirillOsenkov/dev/kirillo/snk 2024-08-30 07:45:19 +02:00
6116af3844 Strong name the assemblies 2024-08-29 16:52:55 -07:00
2cc6c457ad Merge pull request #1603 from spectreconsole/renovate/verify.xunit-26.x
chore: Update dependency Verify.Xunit to v26.2.0
2024-08-10 13:07:40 +02:00
f02b46107e chore: Update dependency Verify.Xunit to v26.2.0 2024-08-10 10:41:25 +00:00
42fd801876 Preparations for the 1.0 release
* Less cluttered solution layout.
* Move examples to a repository of its own.
* Move Roslyn analyzer to a repository of its own.
* Enable central package management.
* Clean up csproj files.
* Add README file to NuGet packages.
2024-08-06 15:38:28 +02:00
bb72b44d60 Delete based on character width when pressing Backspace. 2024-08-01 11:07:16 +02:00
d79e6adc5f Updated test expectations to match changes in HtmlEncoder 2024-07-25 23:13:06 +02:00
64b9ef582d Fixed incorrect decoration check from Bold to Italic in HtmlEncoder.BuildCss 2024-07-25 23:13:06 +02:00
a19c84e25a chore: Update dependency SixLabors.ImageSharp to v3.1.5 [SECURITY] 2024-07-22 19:50:33 +02:00
d52d14e848 Undo debug change and add method group 2024-04-13 17:23:26 -07:00
a62e79992b Undo debug changes 2024-04-13 17:20:12 -07:00
4f22f5b7c3 Add Reference File 2024-04-13 17:19:19 -07:00
20a2f727f7 Updating test and Ratio calculation 2024-04-09 06:03:45 -07:00
829 changed files with 6006 additions and 6974 deletions

1
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View File

@ -0,0 +1 @@
blank_issues_enabled: false

View File

@ -26,9 +26,8 @@ jobs:
uses: actions/setup-dotnet@v4 uses: actions/setup-dotnet@v4
with: with:
dotnet-version: | dotnet-version: |
6.0.x
7.0.x
8.0.x 8.0.x
9.0.x
- name: Build - name: Build
shell: bash shell: bash

View File

@ -32,9 +32,8 @@ jobs:
uses: actions/setup-dotnet@v4 uses: actions/setup-dotnet@v4
with: with:
dotnet-version: | dotnet-version: |
6.0.x
7.0.x
8.0.x 8.0.x
9.0.x
- name: Publish - name: Publish
shell: bash shell: bash

View File

@ -9,7 +9,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Top Issues action - name: Top Issues action
uses: rickstaa/top-issues-action@v1.3.100 uses: rickstaa/top-issues-action@v1.3.101
env: env:
github_token: ${{ secrets.GITHUB_TOKEN }} github_token: ${{ secrets.GITHUB_TOKEN }}
with: with:

View File

@ -44,30 +44,13 @@ https://spectreconsole.net/
<h2 id="examples">مثال‌ها</h2> <h2 id="examples">مثال‌ها</h2>
برای بررسی `Spectre.Console` در عمل، ابزار سراسری To see `Spectre.Console` in action, please see the
[dotnet-example](https://github.com/patriksvensson/dotnet-example) [examples repository](https://github.com/spectreconsole/examples).
را نصب کنید.
<pre dir="ltr">
> dotnet tool restore
</pre>
حالا شما می‌توانید مثال‌های موجود در این مخزن را لیست کنید:
<pre dir="ltr">
> dotnet example
</pre>
و برای اجرای مثال:
<pre dir="ltr">
> dotnet example tables
</pre>
<h2 id="license">مجوز</h2> <h2 id="license">مجوز</h2>
<div dir="ltr"> <div dir="ltr">
Copyright © Patrik Svensson, Phil Scott Copyright © Patrik Svensson, Phil Scott, Nils Andresen, Cédric Luthi, Frank Ray
</div> </div>
همانطور که Spectre.Console تحت مجوز MIT ارائه شده است؛ برای کسب اطلاعات بیشتر به مجوز مراجعه کنید. همانطور که Spectre.Console تحت مجوز MIT ارائه شده است؛ برای کسب اطلاعات بیشتر به مجوز مراجعه کنید.

View File

@ -49,25 +49,8 @@ https://spectreconsole.net
## Examples ## Examples
To see `Spectre.Console` in action, install the To see `Spectre.Console` in action, please see the
[dotnet-example](https://github.com/patriksvensson/dotnet-example) [examples repository](https://github.com/spectreconsole/examples).
global tool.
```
> dotnet tool restore
```
Now you can list available examples in this repository:
```
> dotnet example
```
And to run an example:
```
> dotnet example tables
```
## Sponsors ## Sponsors
@ -100,7 +83,7 @@ This project is supported by the [.NET Foundation](https://dotnetfoundation.org)
## License ## License
Copyright © Patrik Svensson, Phil Scott, Nils Andresen Copyright © Patrik Svensson, Phil Scott, Nils Andresen, Cédric Luthi, Frank Ray
`Spectre.Console` is provided as-is under the MIT license. For more information see LICENSE. `Spectre.Console` is provided as-is under the MIT license. For more information see LICENSE.

View File

@ -43,24 +43,8 @@ https://spectreconsole.net/
## Exemplos ## Exemplos
Para ver o `Spectre.Console` em ação, instale a ferramenta global To see `Spectre.Console` in action, please see the
[dotnet-example](https://github.com/patriksvensson/dotnet-example). [examples repository](https://github.com/spectreconsole/examples).
```
> dotnet tool restore
```
Agora você pode listar os exemplos disponíveis neste repositório:
```
> dotnet example
```
E para executar um exemplo:
```
> dotnet example tables
```
## Patrocinadores ## Patrocinadores
@ -83,7 +67,7 @@ Eu estou muito agradecido.
## Licença ## Licença
Copyright © Patrik Svensson, Phil Scott, Nils Andresen Copyright © Patrik Svensson, Phil Scott, Nils Andresen, Cédric Luthi, Frank Ray
Spectre.Console é fornecido no estado em que se encontra sob a licença do MIT. Para obter mais informações, consulte o arquivo [LICENSE](LICENSE.md). Spectre.Console é fornecido no estado em que se encontra sob a licença do MIT. Para obter mais informações, consulte o arquivo [LICENSE](LICENSE.md).

View File

@ -39,23 +39,8 @@ https://spectreconsole.net/
## 例子 ## 例子
如果想直接运行`Spectre.Console`的例子,则需要安装[dotnet-example](https://github.com/patriksvensson/dotnet-example)工具。 To see `Spectre.Console` in action, please see the
[examples repository](https://github.com/spectreconsole/examples).
```
> dotnet tool restore
```
然后你可以列出仓库里的所有例子:
```
> dotnet example
```
跑一个看看效果:
```
> dotnet example tables
```
## Sponsors ## Sponsors
@ -77,7 +62,7 @@ https://spectreconsole.net/
## 开源许可 ## 开源许可
版权所有 © Patrik Svensson, Phil Scott, Nils Andresen 版权所有 © Patrik Svensson, Phil Scott, Nils Andresen, Cédric Luthi, Frank Ray
Spectre.Console 基于 MIT 协议提供。查看 LICENSE 文件了解更多信息。 Spectre.Console 基于 MIT 协议提供。查看 LICENSE 文件了解更多信息。

View File

@ -35,41 +35,11 @@ Task("Build")
}); });
}); });
Task("Build-Analyzer")
.IsDependentOn("Build")
.Does(context =>
{
DotNetBuild("./src/Spectre.Console.Analyzer.sln", new DotNetBuildSettings {
Configuration = configuration,
Verbosity = DotNetVerbosity.Minimal,
NoLogo = true,
NoIncremental = context.HasArgument("rebuild"),
MSBuildSettings = new DotNetMSBuildSettings()
.TreatAllWarningsAs(MSBuildTreatAllWarningsAs.Error)
});
});
Task("Build-Examples")
.IsDependentOn("Build")
.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)
});
});
Task("Test") Task("Test")
.IsDependentOn("Build") .IsDependentOn("Build")
.IsDependentOn("Build-Analyzer")
.IsDependentOn("Build-Examples")
.Does(context => .Does(context =>
{ {
DotNetTest("./test/Spectre.Console.Tests/Spectre.Console.Tests.csproj", new DotNetTestSettings { DotNetTest("./src/Tests/Spectre.Console.Tests/Spectre.Console.Tests.csproj", new DotNetTestSettings {
Configuration = configuration, Configuration = configuration,
Verbosity = DotNetVerbosity.Minimal, Verbosity = DotNetVerbosity.Minimal,
NoLogo = true, NoLogo = true,
@ -77,15 +47,7 @@ Task("Test")
NoBuild = true, NoBuild = true,
}); });
DotNetTest("./test/Spectre.Console.Cli.Tests/Spectre.Console.Cli.Tests.csproj", new DotNetTestSettings { DotNetTest("./src/Tests/Spectre.Console.Cli.Tests/Spectre.Console.Cli.Tests.csproj", new DotNetTestSettings {
Configuration = configuration,
Verbosity = DotNetVerbosity.Minimal,
NoLogo = true,
NoRestore = true,
NoBuild = true,
});
DotNetTest("./test/Spectre.Console.Analyzer.Tests/Spectre.Console.Analyzer.Tests.csproj", new DotNetTestSettings {
Configuration = configuration, Configuration = configuration,
Verbosity = DotNetVerbosity.Minimal, Verbosity = DotNetVerbosity.Minimal,
NoLogo = true, NoLogo = true,
@ -108,17 +70,6 @@ Task("Package")
MSBuildSettings = new DotNetMSBuildSettings() MSBuildSettings = new DotNetMSBuildSettings()
.TreatAllWarningsAs(MSBuildTreatAllWarningsAs.Error) .TreatAllWarningsAs(MSBuildTreatAllWarningsAs.Error)
}); });
// context.DotNetPack($"./src/Spectre.Console.Analyzer.sln", new DotNetPackSettings {
// Configuration = configuration,
// Verbosity = DotNetVerbosity.Minimal,
// NoLogo = true,
// NoRestore = true,
// NoBuild = true,
// OutputDirectory = "./.artifacts",
// MSBuildSettings = new DotNetMSBuildSettings()
// .TreatAllWarningsAs(MSBuildTreatAllWarningsAs.Error)
// });
}); });
Task("Publish-NuGet") Task("Publish-NuGet")
@ -132,7 +83,7 @@ Task("Publish-NuGet")
} }
// Publish to GitHub Packages // Publish to GitHub Packages
foreach(var file in context.GetFiles("./.artifacts/*.NS.*.nupkg")) foreach(var file in context.GetFiles("./.artifacts/*.nupkg"))
{ {
context.Information("Publishing {0}...", file.GetFilename().FullPath); context.Information("Publishing {0}...", file.GetFilename().FullPath);
DotNetNuGetPush(file.FullPath, new DotNetNuGetPushSettings DotNetNuGetPush(file.FullPath, new DotNetNuGetPushSettings

View File

@ -7,6 +7,11 @@
<DefaultItemExcludes>$(DefaultItemExcludes);output\**;.gitignore</DefaultItemExcludes> <DefaultItemExcludes>$(DefaultItemExcludes);output\**;.gitignore</DefaultItemExcludes>
<NoWarn>MVC1000</NoWarn> <NoWarn>MVC1000</NoWarn>
<MinVerSkip>true</MinVerSkip> <MinVerSkip>true</MinVerSkip>
<!--
Disable NuGetAudit for now, there is an in progress PR with Statiq regarding these packages,
but since since this is just a generator we are safe to ignore this for now.
-->
<NuGetAudit>false</NuGetAudit>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
@ -33,13 +38,13 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.Playwright" Version="1.19.0" /> <PackageReference Include="Microsoft.Playwright" Version="1.52.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="Statiq.CodeAnalysis" Version="1.0.0-beta.58" /> <PackageReference Include="Statiq.CodeAnalysis" Version="1.0.0-beta.72" />
<PackageReference Include="Statiq.Common" Version="1.0.0-beta.58" /> <PackageReference Include="Statiq.Common" Version="1.0.0-beta.72" />
<PackageReference Include="Statiq.Web" Version="1.0.0-beta.44" /> <PackageReference Include="Statiq.Web" Version="1.0.0-beta.60" />
<PackageReference Include="MinVer" PrivateAssets="All" Version="2.5.0" /> <PackageReference Include="MinVer" PrivateAssets="All" Version="6.0.0" />
<PackageReference Include="Statiq.Web.Netlify" Version="1.0.0-beta.44" /> <PackageReference Include="Statiq.Web.Netlify" Version="1.0.0-beta.60" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -23,8 +23,9 @@ namespace Docs
{ {
"../../src/Spectre.Console/**/{!bin,!obj,!packages,!*.Tests,}/**/*.cs", "../../src/Spectre.Console/**/{!bin,!obj,!packages,!*.Tests,}/**/*.cs",
"../../src/Spectre.Console.Cli/**/{!bin,!obj,!packages,!*.Tests,}/**/*.cs", "../../src/Spectre.Console.Cli/**/{!bin,!obj,!packages,!*.Tests,}/**/*.cs",
"../../src/Spectre.Console.ImageSharp/**/{!bin,!obj,!packages,!*.Tests,}/**/*.cs", "../../src/Spectre.Console.Testing/**/{!bin,!obj,!packages,!*.Tests,}/**/*.cs",
"../../src/Spectre.Console.Json/**/{!bin,!obj,!packages,!*.Tests,}/**/*.cs" "../../src/Extensions/Spectre.Console.ImageSharp/**/{!bin,!obj,!packages,!*.Tests,}/**/*.cs",
"../../src/Extensions/Spectre.Console.Json/**/{!bin,!obj,!packages,!*.Tests,}/**/*.cs"
}) })
.AddSetting(Constants.ExampleSourceFiles, new List<string> .AddSetting(Constants.ExampleSourceFiles, new List<string>
{ {

View File

@ -3,7 +3,7 @@
"isRoot": true, "isRoot": true,
"tools": { "tools": {
"microsoft.playwright.cli": { "microsoft.playwright.cli": {
"version": "1.2.2", "version": "1.2.3",
"commands": [ "commands": [
"playwright" "playwright"
] ]

View File

@ -1,6 +1,6 @@
{ {
"sdk": { "sdk": {
"version": "8.0.100", "version": "9.0.202",
"rollForward": "latestFeature" "rollForward": "latestFeature"
} }
} }

View File

@ -10,7 +10,7 @@ For all available spinners, see https://jsfiddle.net/sindresorhus/2eLtsbey/embed
## Usage ## Usage
Spinners can be used with [Progress](xref:progress) and [Status](xref:status). Spinners can be used with [Progress](xref:live-progress) and [Status](xref:live-status).
```csharp ```csharp
AnsiConsole.Status() AnsiConsole.Status()

View File

@ -6,6 +6,7 @@ Highlights:
- Dim, Invert - Dim, Invert
- Conceal, slowblink, rapidblink - Conceal, slowblink, rapidblink
- Links - Links
Xref: appendix-styles
--- ---
Note that what styles that can be used is defined by the system or your terminal software, and may not appear as they should. Note that what styles that can be used is defined by the system or your terminal software, and may not appear as they should.
@ -48,7 +49,7 @@ Note that what styles that can be used is defined by the system or your terminal
<td>Shows text with a horizontal line through the center</td> <td>Shows text with a horizontal line through the center</td>
</tr> </tr>
<tr> <tr>
<td><code>link</link></td> <td><code>link</code></td>
<td>Creates a clickable link within text</td> <td>Creates a clickable link within text</td>
</tr> </tr>
</table> </table>

View File

@ -0,0 +1,2 @@
{"version": 2, "width": 40, "height": 3, "timestamp": 1667342769, "env": {"SHELL": "/bin/bash", "TERM": "xterm-256color"}}
[0.0, "o", "\u001b[H\u001b[2B\u001b[38;5;9;48;5;0mSpectre!\u001b[0m"]

View File

@ -0,0 +1,332 @@
{"version": 2, "width": 42, "height": 4, "title": "await-spinner (plain)", "env": {"TERM": "Spectre.Console"}}
[0, "o", "Loading the rocket ship "]
[0, "o", "\u001B[?25l"]
[0, "o", "\u280B"]
[0, "o", "\u001B[1D"]
[0.094, "o", "\u001B[?25l"]
[0.094, "o", "\u2819"]
[0.094, "o", "\u001B[1D"]
[0.188, "o", "\u001B[?25l"]
[0.188, "o", "\u2839"]
[0.188, "o", "\u001B[1D"]
[0.266, "o", "\u001B[?25l"]
[0.266, "o", "\u2838"]
[0.266, "o", "\u001B[1D"]
[0.36, "o", "\u001B[?25l"]
[0.36, "o", "\u283C"]
[0.36, "o", "\u001B[1D"]
[0.453, "o", "\u001B[?25l"]
[0.453, "o", "\u2834"]
[0.453, "o", "\u001B[1D"]
[0.563, "o", "\u001B[?25l"]
[0.563, "o", "\u2826"]
[0.563, "o", "\u001B[1D"]
[0.656, "o", "\u001B[?25l"]
[0.656, "o", "\u2827"]
[0.656, "o", "\u001B[1D"]
[0.75, "o", "\u001B[?25l"]
[0.75, "o", "\u2807"]
[0.75, "o", "\u001B[1D"]
[0.844, "o", "\u001B[?25l"]
[0.844, "o", "\u280F"]
[0.844, "o", "\u001B[1D"]
[0.922, "o", "\u001B[?25l"]
[0.922, "o", "\u280B"]
[0.922, "o", "\u001B[1D"]
[1.016, "o", "\u001B[?25l"]
[1.016, "o", "\u2819"]
[1.016, "o", "\u001B[1D"]
[1.11, "o", "\u001B[?25l"]
[1.11, "o", "\u2839"]
[1.11, "o", "\u001B[1D"]
[1.203, "o", "\u001B[?25l"]
[1.203, "o", "\u2838"]
[1.203, "o", "\u001B[1D"]
[1.297, "o", "\u001B[?25l"]
[1.297, "o", "\u283C"]
[1.297, "o", "\u001B[1D"]
[1.391, "o", "\u001B[?25l"]
[1.391, "o", "\u2834"]
[1.391, "o", "\u001B[1D"]
[1.485, "o", "\u001B[?25l"]
[1.485, "o", "\u2826"]
[1.485, "o", "\u001B[1D"]
[1.578, "o", "\u001B[?25l"]
[1.578, "o", "\u2827"]
[1.578, "o", "\u001B[1D"]
[1.672, "o", "\u001B[?25l"]
[1.672, "o", "\u2807"]
[1.672, "o", "\u001B[1D"]
[1.75, "o", "\u001B[?25l"]
[1.75, "o", "\u280F"]
[1.75, "o", "\u001B[1D"]
[1.828, "o", "\u001B[?25l"]
[1.828, "o", "\u280B"]
[1.828, "o", "\u001B[1D"]
[1.906, "o", "\u001B[?25l"]
[1.906, "o", "\u2819"]
[1.906, "o", "\u001B[1D"]
[2, "o", "\u001B[?25l"]
[2, "o", "\u2839"]
[2, "o", "\u001B[1D"]
[2.11, "o", "\u001B[?25l"]
[2.11, "o", "\u2838"]
[2.11, "o", "\u001B[1D"]
[2.203, "o", "\u001B[?25l"]
[2.203, "o", "\u283C"]
[2.203, "o", "\u001B[1D"]
[2.297, "o", "\u001B[?25l"]
[2.297, "o", "\u2834"]
[2.297, "o", "\u001B[1D"]
[2.391, "o", "\u001B[?25l"]
[2.391, "o", "\u2826"]
[2.391, "o", "\u001B[1D"]
[2.485, "o", "\u001B[?25l"]
[2.485, "o", "\u2827"]
[2.485, "o", "\u001B[1D"]
[2.578, "o", "\u001B[?25l"]
[2.578, "o", "\u2807"]
[2.578, "o", "\u001B[1D"]
[2.656, "o", "\u001B[?25l"]
[2.656, "o", "\u280F"]
[2.656, "o", "\u001B[1D"]
[2.735, "o", "\u001B[?25l"]
[2.735, "o", "\u280B"]
[2.735, "o", "\u001B[1D"]
[2.828, "o", "\u001B[?25l"]
[2.828, "o", "\u2819"]
[2.828, "o", "\u001B[1D"]
[2.922, "o", "\u001B[?25l"]
[2.922, "o", "\u2839"]
[2.922, "o", "\u001B[1D"]
[3.016, "o", "\u001B[?25l"]
[3.016, "o", "\u2838"]
[3.016, "o", "\u001B[1D"]
[3.094, "o", "\u001B[?25l"]
[3.094, "o", "\u283C"]
[3.094, "o", "\u001B[1D"]
[3.188, "o", "\u001B[?25l"]
[3.188, "o", "\u2834"]
[3.188, "o", "\u001B[1D"]
[3.281, "o", "\u001B[?25l"]
[3.281, "o", "\u2826"]
[3.281, "o", "\u001B[1D"]
[3.375, "o", "\u001B[?25l"]
[3.375, "o", "\u2827"]
[3.375, "o", "\u001B[1D"]
[3.453, "o", "\u001B[?25l"]
[3.453, "o", "\u2807"]
[3.453, "o", "\u001B[1D"]
[3.516, "o", " "]
[3.516, "o", "\u001B[1D"]
[3.516, "o", "\u001B[?25h"]
[3.516, "o", "\u001B[32mDone\u001B[0m\r\n"]
[3.516, "o", "Firing up the engines "]
[3.516, "o", "\u001B[?25l"]
[3.516, "o", "[ ]"]
[3.516, "o", "\u001B[6D"]
[3.61, "o", "\u001B[?25l"]
[3.61, "o", "[= ]"]
[3.61, "o", "\u001B[6D"]
[3.703, "o", "\u001B[?25l"]
[3.703, "o", "[== ]"]
[3.703, "o", "\u001B[6D"]
[3.797, "o", "\u001B[?25l"]
[3.797, "o", "[=== ]"]
[3.797, "o", "\u001B[6D"]
[3.875, "o", "\u001B[?25l"]
[3.875, "o", "[ ===]"]
[3.875, "o", "\u001B[6D"]
[3.953, "o", "\u001B[?25l"]
[3.953, "o", "[ ==]"]
[3.953, "o", "\u001B[6D"]
[4.063, "o", "\u001B[?25l"]
[4.063, "o", "[ =]"]
[4.063, "o", "\u001B[6D"]
[4.156, "o", "\u001B[?25l"]
[4.156, "o", "[ ]"]
[4.156, "o", "\u001B[6D"]
[4.25, "o", "\u001B[?25l"]
[4.25, "o", "[ =]"]
[4.25, "o", "\u001B[6D"]
[4.328, "o", "\u001B[?25l"]
[4.328, "o", "[ ==]"]
[4.328, "o", "\u001B[6D"]
[4.406, "o", "\u001B[?25l"]
[4.406, "o", "[ ===]"]
[4.406, "o", "\u001B[6D"]
[4.5, "o", "\u001B[?25l"]
[4.5, "o", "[====]"]
[4.5, "o", "\u001B[6D"]
[4.594, "o", "\u001B[?25l"]
[4.594, "o", "[=== ]"]
[4.594, "o", "\u001B[6D"]
[4.688, "o", "\u001B[?25l"]
[4.688, "o", "[== ]"]
[4.688, "o", "\u001B[6D"]
[4.781, "o", "\u001B[?25l"]
[4.781, "o", "[= ]"]
[4.781, "o", "\u001B[6D"]
[4.86, "o", "\u001B[?25l"]
[4.86, "o", "[ ]"]
[4.86, "o", "\u001B[6D"]
[4.953, "o", "\u001B[?25l"]
[4.953, "o", "[= ]"]
[4.953, "o", "\u001B[6D"]
[5.031, "o", "\u001B[?25l"]
[5.031, "o", "[== ]"]
[5.031, "o", "\u001B[6D"]
[5.125, "o", "\u001B[?25l"]
[5.125, "o", "[=== ]"]
[5.125, "o", "\u001B[6D"]
[5.219, "o", "\u001B[?25l"]
[5.219, "o", "[ ===]"]
[5.219, "o", "\u001B[6D"]
[5.313, "o", "\u001B[?25l"]
[5.313, "o", "[ ==]"]
[5.313, "o", "\u001B[6D"]
[5.422, "o", "\u001B[?25l"]
[5.422, "o", "[ =]"]
[5.422, "o", "\u001B[6D"]
[5.5, "o", "\u001B[?25l"]
[5.5, "o", "[ ]"]
[5.5, "o", "\u001B[6D"]
[5.594, "o", "\u001B[?25l"]
[5.594, "o", "[ =]"]
[5.594, "o", "\u001B[6D"]
[5.672, "o", "\u001B[?25l"]
[5.672, "o", "[ ==]"]
[5.672, "o", "\u001B[6D"]
[5.766, "o", "\u001B[?25l"]
[5.766, "o", "[ ===]"]
[5.766, "o", "\u001B[6D"]
[5.86, "o", "\u001B[?25l"]
[5.86, "o", "[====]"]
[5.86, "o", "\u001B[6D"]
[5.953, "o", "\u001B[?25l"]
[5.969, "o", "[=== ]"]
[5.969, "o", "\u001B[6D"]
[6.063, "o", "\u001B[?25l"]
[6.063, "o", "[== ]"]
[6.063, "o", "\u001B[6D"]
[6.156, "o", "\u001B[?25l"]
[6.156, "o", "[= ]"]
[6.156, "o", "\u001B[6D"]
[6.25, "o", "\u001B[?25l"]
[6.25, "o", "[ ]"]
[6.25, "o", "\u001B[6D"]
[6.328, "o", "\u001B[?25l"]
[6.328, "o", "[= ]"]
[6.328, "o", "\u001B[6D"]
[6.422, "o", "\u001B[?25l"]
[6.422, "o", "[== ]"]
[6.422, "o", "\u001B[6D"]
[6.516, "o", "\u001B[?25l"]
[6.516, "o", "[=== ]"]
[6.516, "o", "\u001B[6D"]
[6.61, "o", "\u001B[?25l"]
[6.61, "o", "[ ===]"]
[6.61, "o", "\u001B[6D"]
[6.703, "o", "\u001B[?25l"]
[6.703, "o", "[ ==]"]
[6.703, "o", "\u001B[6D"]
[6.797, "o", "\u001B[?25l"]
[6.797, "o", "[ =]"]
[6.797, "o", "\u001B[6D"]
[6.891, "o", "\u001B[?25l"]
[6.891, "o", "[ ]"]
[6.891, "o", "\u001B[6D"]
[6.922, "o", " "]
[6.922, "o", "\u001B[6D"]
[6.922, "o", "\u001B[?25h"]
[6.922, "o", "\u001B[32mDone\u001B[0m\r\n"]
[6.922, "o", "Blasting into orbit "]
[6.922, "o", "\u001B[?25l"]
[6.922, "o", "\u2631"]
[6.922, "o", "\u001B[1D"]
[7.031, "o", "\u001B[?25l"]
[7.031, "o", "\u2632"]
[7.031, "o", "\u001B[1D"]
[7.141, "o", "\u001B[?25l"]
[7.141, "o", "\u2634"]
[7.141, "o", "\u001B[1D"]
[7.25, "o", "\u001B[?25l"]
[7.25, "o", "\u2631"]
[7.25, "o", "\u001B[1D"]
[7.36, "o", "\u001B[?25l"]
[7.36, "o", "\u2632"]
[7.36, "o", "\u001B[1D"]
[7.485, "o", "\u001B[?25l"]
[7.485, "o", "\u2634"]
[7.485, "o", "\u001B[1D"]
[7.594, "o", "\u001B[?25l"]
[7.594, "o", "\u2631"]
[7.594, "o", "\u001B[1D"]
[7.703, "o", "\u001B[?25l"]
[7.703, "o", "\u2632"]
[7.703, "o", "\u001B[1D"]
[7.813, "o", "\u001B[?25l"]
[7.813, "o", "\u2634"]
[7.813, "o", "\u001B[1D"]
[7.922, "o", "\u001B[?25l"]
[7.922, "o", "\u2631"]
[7.922, "o", "\u001B[1D"]
[8.031, "o", "\u001B[?25l"]
[8.031, "o", "\u2632"]
[8.031, "o", "\u001B[1D"]
[8.141, "o", "\u001B[?25l"]
[8.141, "o", "\u2634"]
[8.141, "o", "\u001B[1D"]
[8.25, "o", "\u001B[?25l"]
[8.25, "o", "\u2631"]
[8.25, "o", "\u001B[1D"]
[8.375, "o", "\u001B[?25l"]
[8.375, "o", "\u2632"]
[8.375, "o", "\u001B[1D"]
[8.485, "o", "\u001B[?25l"]
[8.485, "o", "\u2634"]
[8.485, "o", "\u001B[1D"]
[8.594, "o", "\u001B[?25l"]
[8.594, "o", "\u2631"]
[8.594, "o", "\u001B[1D"]
[8.703, "o", "\u001B[?25l"]
[8.703, "o", "\u2632"]
[8.703, "o", "\u001B[1D"]
[8.813, "o", "\u001B[?25l"]
[8.813, "o", "\u2634"]
[8.813, "o", "\u001B[1D"]
[8.938, "o", "\u001B[?25l"]
[8.938, "o", "\u2631"]
[8.938, "o", "\u001B[1D"]
[9.047, "o", "\u001B[?25l"]
[9.047, "o", "\u2632"]
[9.047, "o", "\u001B[1D"]
[9.156, "o", "\u001B[?25l"]
[9.156, "o", "\u2634"]
[9.156, "o", "\u001B[1D"]
[9.266, "o", "\u001B[?25l"]
[9.266, "o", "\u2631"]
[9.266, "o", "\u001B[1D"]
[9.375, "o", "\u001B[?25l"]
[9.375, "o", "\u2632"]
[9.375, "o", "\u001B[1D"]
[9.485, "o", "\u001B[?25l"]
[9.485, "o", "\u2634"]
[9.485, "o", "\u001B[1D"]
[9.594, "o", "\u001B[?25l"]
[9.594, "o", "\u2631"]
[9.594, "o", "\u001B[1D"]
[9.719, "o", "\u001B[?25l"]
[9.719, "o", "\u2632"]
[9.719, "o", "\u001B[1D"]
[9.828, "o", "\u001B[?25l"]
[9.828, "o", "\u2634"]
[9.828, "o", "\u001B[1D"]
[9.938, "o", "\u001B[?25l"]
[9.938, "o", "\u2631"]
[9.938, "o", "\u001B[1D"]
[9.953, "o", " "]
[9.953, "o", "\u001B[1D"]
[9.953, "o", "\u001B[?25h"]
[9.953, "o", "\u001B[31mOh no\u001B[0m\r\n"]

View File

@ -0,0 +1,326 @@
{"version": 2, "width": 42, "height": 4, "title": "await-spinner (rich)", "env": {"TERM": "Spectre.Console"}}
[0, "o", "Loading the rocket ship "]
[0, "o", "\u001B[?25l"]
[0, "o", "\u280B"]
[0, "o", "\u001B[1D"]
[0.094, "o", "\u001B[?25l"]
[0.094, "o", "\u2819"]
[0.094, "o", "\u001B[1D"]
[0.172, "o", "\u001B[?25l"]
[0.172, "o", "\u2839"]
[0.172, "o", "\u001B[1D"]
[0.266, "o", "\u001B[?25l"]
[0.266, "o", "\u2838"]
[0.266, "o", "\u001B[1D"]
[0.36, "o", "\u001B[?25l"]
[0.36, "o", "\u283C"]
[0.36, "o", "\u001B[1D"]
[0.453, "o", "\u001B[?25l"]
[0.453, "o", "\u2834"]
[0.453, "o", "\u001B[1D"]
[0.547, "o", "\u001B[?25l"]
[0.547, "o", "\u2826"]
[0.547, "o", "\u001B[1D"]
[0.641, "o", "\u001B[?25l"]
[0.641, "o", "\u2827"]
[0.641, "o", "\u001B[1D"]
[0.719, "o", "\u001B[?25l"]
[0.719, "o", "\u2807"]
[0.719, "o", "\u001B[1D"]
[0.797, "o", "\u001B[?25l"]
[0.797, "o", "\u280F"]
[0.797, "o", "\u001B[1D"]
[0.891, "o", "\u001B[?25l"]
[0.891, "o", "\u280B"]
[0.891, "o", "\u001B[1D"]
[0.969, "o", "\u001B[?25l"]
[0.969, "o", "\u2819"]
[0.969, "o", "\u001B[1D"]
[1.078, "o", "\u001B[?25l"]
[1.078, "o", "\u2839"]
[1.094, "o", "\u001B[1D"]
[1.25, "o", "\u001B[?25l"]
[1.25, "o", "\u2838"]
[1.266, "o", "\u001B[1D"]
[1.407, "o", "\u001B[?25l"]
[1.407, "o", "\u283C"]
[1.422, "o", "\u001B[1D"]
[1.532, "o", "\u001B[?25l"]
[1.532, "o", "\u2834"]
[1.532, "o", "\u001B[1D"]
[1.61, "o", "\u001B[?25l"]
[1.61, "o", "\u2826"]
[1.61, "o", "\u001B[1D"]
[1.703, "o", "\u001B[?25l"]
[1.703, "o", "\u2827"]
[1.703, "o", "\u001B[1D"]
[1.782, "o", "\u001B[?25l"]
[1.782, "o", "\u2807"]
[1.782, "o", "\u001B[1D"]
[1.86, "o", "\u001B[?25l"]
[1.86, "o", "\u280F"]
[1.86, "o", "\u001B[1D"]
[1.953, "o", "\u001B[?25l"]
[1.953, "o", "\u280B"]
[1.953, "o", "\u001B[1D"]
[2.047, "o", "\u001B[?25l"]
[2.047, "o", "\u2819"]
[2.047, "o", "\u001B[1D"]
[2.125, "o", "\u001B[?25l"]
[2.125, "o", "\u2839"]
[2.125, "o", "\u001B[1D"]
[2.219, "o", "\u001B[?25l"]
[2.219, "o", "\u2838"]
[2.219, "o", "\u001B[1D"]
[2.313, "o", "\u001B[?25l"]
[2.313, "o", "\u283C"]
[2.313, "o", "\u001B[1D"]
[2.407, "o", "\u001B[?25l"]
[2.407, "o", "\u2834"]
[2.407, "o", "\u001B[1D"]
[2.5, "o", "\u001B[?25l"]
[2.5, "o", "\u2826"]
[2.5, "o", "\u001B[1D"]
[2.594, "o", "\u001B[?25l"]
[2.594, "o", "\u2827"]
[2.594, "o", "\u001B[1D"]
[2.688, "o", "\u001B[?25l"]
[2.688, "o", "\u2807"]
[2.688, "o", "\u001B[1D"]
[2.782, "o", "\u001B[?25l"]
[2.782, "o", "\u280F"]
[2.782, "o", "\u001B[1D"]
[2.875, "o", "\u001B[?25l"]
[2.875, "o", "\u280B"]
[2.875, "o", "\u001B[1D"]
[2.969, "o", "\u001B[?25l"]
[2.969, "o", "\u2819"]
[2.969, "o", "\u001B[1D"]
[3.063, "o", "\u001B[?25l"]
[3.063, "o", "\u2839"]
[3.063, "o", "\u001B[1D"]
[3.157, "o", "\u001B[?25l"]
[3.157, "o", "\u2838"]
[3.157, "o", "\u001B[1D"]
[3.25, "o", "\u001B[?25l"]
[3.25, "o", "\u283C"]
[3.25, "o", "\u001B[1D"]
[3.344, "o", "\u001B[?25l"]
[3.344, "o", "\u2834"]
[3.344, "o", "\u001B[1D"]
[3.469, "o", "\u001B[?25l"]
[3.469, "o", "\u2826"]
[3.469, "o", "\u001B[1D"]
[3.563, "o", "\u001B[?25l"]
[3.563, "o", "\u2827"]
[3.563, "o", "\u001B[1D"]
[3.563, "o", " "]
[3.563, "o", "\u001B[1D"]
[3.563, "o", "\u001B[?25h"]
[3.563, "o", "\u001B[38;5;2mDone\u001B[0m\r\n"]
[3.563, "o", "Firing up the engines "]
[3.563, "o", "\u001B[?25l"]
[3.563, "o", "[ ]"]
[3.563, "o", "\u001B[6D"]
[3.672, "o", "\u001B[?25l"]
[3.672, "o", "[= ]"]
[3.672, "o", "\u001B[6D"]
[3.75, "o", "\u001B[?25l"]
[3.75, "o", "[== ]"]
[3.75, "o", "\u001B[6D"]
[3.844, "o", "\u001B[?25l"]
[3.844, "o", "[=== ]"]
[3.844, "o", "\u001B[6D"]
[3.953, "o", "\u001B[?25l"]
[3.953, "o", "[ ===]"]
[3.953, "o", "\u001B[6D"]
[4.047, "o", "\u001B[?25l"]
[4.047, "o", "[ ==]"]
[4.047, "o", "\u001B[6D"]
[4.157, "o", "\u001B[?25l"]
[4.157, "o", "[ =]"]
[4.157, "o", "\u001B[6D"]
[4.25, "o", "\u001B[?25l"]
[4.25, "o", "[ ]"]
[4.25, "o", "\u001B[6D"]
[4.344, "o", "\u001B[?25l"]
[4.344, "o", "[ =]"]
[4.344, "o", "\u001B[6D"]
[4.438, "o", "\u001B[?25l"]
[4.438, "o", "[ ==]"]
[4.438, "o", "\u001B[6D"]
[4.532, "o", "\u001B[?25l"]
[4.532, "o", "[ ===]"]
[4.532, "o", "\u001B[6D"]
[4.625, "o", "\u001B[?25l"]
[4.625, "o", "[====]"]
[4.625, "o", "\u001B[6D"]
[4.719, "o", "\u001B[?25l"]
[4.719, "o", "[=== ]"]
[4.719, "o", "\u001B[6D"]
[4.828, "o", "\u001B[?25l"]
[4.844, "o", "[== ]"]
[4.86, "o", "\u001B[6D"]
[4.953, "o", "\u001B[?25l"]
[4.953, "o", "[= ]"]
[4.953, "o", "\u001B[6D"]
[5.047, "o", "\u001B[?25l"]
[5.047, "o", "[ ]"]
[5.047, "o", "\u001B[6D"]
[5.141, "o", "\u001B[?25l"]
[5.141, "o", "[= ]"]
[5.141, "o", "\u001B[6D"]
[5.235, "o", "\u001B[?25l"]
[5.235, "o", "[== ]"]
[5.235, "o", "\u001B[6D"]
[5.328, "o", "\u001B[?25l"]
[5.328, "o", "[=== ]"]
[5.328, "o", "\u001B[6D"]
[5.422, "o", "\u001B[?25l"]
[5.422, "o", "[ ===]"]
[5.422, "o", "\u001B[6D"]
[5.532, "o", "\u001B[?25l"]
[5.532, "o", "[ ==]"]
[5.532, "o", "\u001B[6D"]
[5.625, "o", "\u001B[?25l"]
[5.625, "o", "[ =]"]
[5.625, "o", "\u001B[6D"]
[5.719, "o", "\u001B[?25l"]
[5.719, "o", "[ ]"]
[5.719, "o", "\u001B[6D"]
[5.813, "o", "\u001B[?25l"]
[5.813, "o", "[ =]"]
[5.813, "o", "\u001B[6D"]
[5.907, "o", "\u001B[?25l"]
[5.907, "o", "[ ==]"]
[5.907, "o", "\u001B[6D"]
[6, "o", "\u001B[?25l"]
[6, "o", "[ ===]"]
[6, "o", "\u001B[6D"]
[6.094, "o", "\u001B[?25l"]
[6.094, "o", "[====]"]
[6.094, "o", "\u001B[6D"]
[6.188, "o", "\u001B[?25l"]
[6.188, "o", "[=== ]"]
[6.188, "o", "\u001B[6D"]
[6.282, "o", "\u001B[?25l"]
[6.282, "o", "[== ]"]
[6.282, "o", "\u001B[6D"]
[6.375, "o", "\u001B[?25l"]
[6.375, "o", "[= ]"]
[6.375, "o", "\u001B[6D"]
[6.453, "o", "\u001B[?25l"]
[6.453, "o", "[ ]"]
[6.453, "o", "\u001B[6D"]
[6.547, "o", "\u001B[?25l"]
[6.547, "o", "[= ]"]
[6.547, "o", "\u001B[6D"]
[6.625, "o", "\u001B[?25l"]
[6.625, "o", "[== ]"]
[6.625, "o", "\u001B[6D"]
[6.703, "o", "\u001B[?25l"]
[6.703, "o", "[=== ]"]
[6.703, "o", "\u001B[6D"]
[6.797, "o", "\u001B[?25l"]
[6.797, "o", "[ ===]"]
[6.797, "o", "\u001B[6D"]
[6.891, "o", "\u001B[?25l"]
[6.891, "o", "[ ==]"]
[6.891, "o", "\u001B[6D"]
[6.969, "o", " "]
[6.969, "o", "\u001B[6D"]
[6.969, "o", "\u001B[?25h"]
[6.969, "o", "\u001B[38;5;2mDone\u001B[0m\r\n"]
[6.969, "o", "Blasting into orbit "]
[6.969, "o", "\u001B[?25l"]
[6.969, "o", "\u2631"]
[6.969, "o", "\u001B[1D"]
[7.078, "o", "\u001B[?25l"]
[7.078, "o", "\u2632"]
[7.078, "o", "\u001B[1D"]
[7.172, "o", "\u001B[?25l"]
[7.172, "o", "\u2634"]
[7.172, "o", "\u001B[1D"]
[7.282, "o", "\u001B[?25l"]
[7.282, "o", "\u2631"]
[7.282, "o", "\u001B[1D"]
[7.375, "o", "\u001B[?25l"]
[7.375, "o", "\u2632"]
[7.375, "o", "\u001B[1D"]
[7.485, "o", "\u001B[?25l"]
[7.485, "o", "\u2634"]
[7.485, "o", "\u001B[1D"]
[7.594, "o", "\u001B[?25l"]
[7.594, "o", "\u2631"]
[7.594, "o", "\u001B[1D"]
[7.703, "o", "\u001B[?25l"]
[7.703, "o", "\u2632"]
[7.703, "o", "\u001B[1D"]
[7.813, "o", "\u001B[?25l"]
[7.813, "o", "\u2634"]
[7.813, "o", "\u001B[1D"]
[7.922, "o", "\u001B[?25l"]
[7.922, "o", "\u2631"]
[7.922, "o", "\u001B[1D"]
[8.032, "o", "\u001B[?25l"]
[8.032, "o", "\u2632"]
[8.032, "o", "\u001B[1D"]
[8.125, "o", "\u001B[?25l"]
[8.125, "o", "\u2634"]
[8.125, "o", "\u001B[1D"]
[8.235, "o", "\u001B[?25l"]
[8.235, "o", "\u2631"]
[8.235, "o", "\u001B[1D"]
[8.344, "o", "\u001B[?25l"]
[8.344, "o", "\u2632"]
[8.344, "o", "\u001B[1D"]
[8.453, "o", "\u001B[?25l"]
[8.453, "o", "\u2634"]
[8.453, "o", "\u001B[1D"]
[8.563, "o", "\u001B[?25l"]
[8.563, "o", "\u2631"]
[8.563, "o", "\u001B[1D"]
[8.657, "o", "\u001B[?25l"]
[8.657, "o", "\u2632"]
[8.657, "o", "\u001B[1D"]
[8.766, "o", "\u001B[?25l"]
[8.766, "o", "\u2634"]
[8.766, "o", "\u001B[1D"]
[8.86, "o", "\u001B[?25l"]
[8.86, "o", "\u2631"]
[8.86, "o", "\u001B[1D"]
[8.969, "o", "\u001B[?25l"]
[8.969, "o", "\u2632"]
[8.969, "o", "\u001B[1D"]
[9.078, "o", "\u001B[?25l"]
[9.078, "o", "\u2634"]
[9.078, "o", "\u001B[1D"]
[9.203, "o", "\u001B[?25l"]
[9.203, "o", "\u2631"]
[9.203, "o", "\u001B[1D"]
[9.313, "o", "\u001B[?25l"]
[9.313, "o", "\u2632"]
[9.313, "o", "\u001B[1D"]
[9.407, "o", "\u001B[?25l"]
[9.407, "o", "\u2634"]
[9.407, "o", "\u001B[1D"]
[9.516, "o", "\u001B[?25l"]
[9.516, "o", "\u2631"]
[9.516, "o", "\u001B[1D"]
[9.625, "o", "\u001B[?25l"]
[9.625, "o", "\u2632"]
[9.625, "o", "\u001B[1D"]
[9.719, "o", "\u001B[?25l"]
[9.719, "o", "\u2634"]
[9.735, "o", "\u001B[1D"]
[9.828, "o", "\u001B[?25l"]
[9.828, "o", "\u2631"]
[9.828, "o", "\u001B[1D"]
[9.938, "o", "\u001B[?25l"]
[9.938, "o", "\u2632"]
[9.938, "o", "\u001B[1D"]
[10, "o", " "]
[10, "o", "\u001B[1D"]
[10, "o", "\u001B[?25h"]
[10, "o", "\u001B[38;5;9mOh no\u001B[0m\r\n"]

View File

@ -85,10 +85,8 @@ on the main thread.
### Unit Testing Best Practices ### Unit Testing Best Practices
For testing of console output, Spectre.Console has [`IAnsiConsole`](xref:T:Spectre.Console.IAnsiConsole) that can be For testing of console output, Spectre.Console has [`IAnsiConsole`](xref:T:Spectre.Console.IAnsiConsole) that can be
injected into your application. injected into your application. The [Spectre.Console.Test](https://www.nuget.org/packages/Spectre.Console.Testing/)
The [Spectre.Console.Test](https://www.nuget.org/packages/Spectre.Console.Testing/) contains a set of utilities for NuGet package contains utilities for capturing the console output for verification. See the [Unit Testing](cli/unit-testing) page for further guidance.
capturing the output for verification, either manually or via a tool such
as [Verify](https://github.com/VerifyTests/Verify).
### Analyzer for Best Practices ### Analyzer for Best Practices
@ -96,6 +94,31 @@ Spectre.Console has an [analyzer](https://www.nuget.org/packages/Spectre.Console
common errors in writing console output from above such as using multiple live rendering widgets simultaneously, common errors in writing console output from above such as using multiple live rendering widgets simultaneously,
or using the static `AnsiConsole` class when `IAnsiConsole` is available. or using the static `AnsiConsole` class when `IAnsiConsole` is available.
### Native AOT Support
Publishing your app as Native AOT with Spectre.Console produces an app that's self-contained and has been ahead-of-time (AOT) compiled to native code. Native AOT apps have faster startup time and smaller memory footprints. These apps can run on machines that don't have the .NET runtime installed.
To enable AOT support on your application, Add `<PublishAot>true</PublishAot>` to your project file.
```xml
<PropertyGroup>
<PublishAot>true</PublishAot>
</PropertyGroup>
```
Current Spectre.Console support for AOT:
* &#9745;&#65039; Spectre.Console
* &#10060; Spectre.Console.Cli
* &#9745;&#65039; Spectre.Console.Json
* &#9745;&#65039; Spectre.Console.ImageSharp
Spectre.Console.Cli relies on reflection and discovering types at runtime, preventing it from currently supporting AOT.
Spectre.Console supports AOT, but with the following limitations
* `WriteException` will output a simple stacktrace and ignore any `ExceptionFormats` set.
### Configuring the Windows Terminal For Unicode and Emoji Support ### Configuring the Windows Terminal For Unicode and Emoji Support
Windows Terminal supports Unicode and Emoji. However, the shells such as Powershell and cmd.exe do not. Windows Terminal supports Unicode and Emoji. However, the shells such as Powershell and cmd.exe do not.

View File

@ -26,9 +26,9 @@ New features have been added, such as the ability to show separators between tab
## Rendering ## Rendering
* Add .NET 8 support by [@patriksvensson](https://github.com/patriksvensson) in [#1367](https://github.com/spectreconsole/spectre.console/pull/1367) * Add .NET 8 support by [@patriksvensson](https://github.com/patriksvensson) in [#1367](https://github.com/spectreconsole/spectre.console/pull/1367)
* Fixed render issue where writeline inside status caused corrupt output #415 #694 by [@fredrikbentzen](https://github.com/fredrikbentzen) in [#1132]([#1132](https://github.com/spectreconsole/spectre.console/pull/1132)) * Fixed render issue where writeline inside status caused corrupt output #415 #694 by [@fredrikbentzen](https://github.com/fredrikbentzen) in [#1132](https://github.com/spectreconsole/spectre.console/pull/1132)
* Relax the SDK requirements by rolling forward to the latest feature by [@0xced](https://github.com/0xced) in [#1237]([#1237](https://github.com/spectreconsole/spectre.console/pull/1237)) * Relax the SDK requirements by rolling forward to the latest feature by [@0xced](https://github.com/0xced) in [#1237](https://github.com/spectreconsole/spectre.console/pull/1237)
* Add fix to avoid exception on rows with no children by [@jeppevammenkristensen](https://github.com/jeppevammenkristensen) in [#1241]([#1241](https://github.com/spectreconsole/spectre.console/pull/1241)) * Add fix to avoid exception on rows with no children by [@jeppevammenkristensen](https://github.com/jeppevammenkristensen) in [#1241](https://github.com/spectreconsole/spectre.console/pull/1241)
* Set `end_of_line` to `LF` instead of `CRLF` by [@0xced](https://github.com/0xced) in [#1256](https://github.com/spectreconsole/spectre.console/pull/1256) * Set `end_of_line` to `LF` instead of `CRLF` by [@0xced](https://github.com/0xced) in [#1256](https://github.com/spectreconsole/spectre.console/pull/1256)
* Fix `Rule` widget docs by [@tomaszprasolek](https://github.com/tomaszprasolek) in [#1257](https://github.com/spectreconsole/spectre.console/pull/1257) * Fix `Rule` widget docs by [@tomaszprasolek](https://github.com/tomaszprasolek) in [#1257](https://github.com/spectreconsole/spectre.console/pull/1257)
* Added the missing columns-cast by [@nils](https://github.com/nils)-a in [#1294](https://github.com/spectreconsole/spectre.console/pull/1294) * Added the missing columns-cast by [@nils](https://github.com/nils)-a in [#1294](https://github.com/spectreconsole/spectre.console/pull/1294)
@ -46,7 +46,7 @@ New features have been added, such as the ability to show separators between tab
## CLI ## CLI
* Add async command unit tests by [@FrankRay78](https://github.com/FrankRay78) in [#1228](https://github.com/spectreconsole/spectre.console/pull/1228) * Add async command unit tests by [@FrankRay78](https://github.com/FrankRay78) in [#1228](https://github.com/spectreconsole/spectre.console/pull/1228)
* Add support for async delegate by [@icalvo](https://github.com/icalvo) in [#1215]([#1215](https://github.com/spectreconsole/spectre.console/pull/1215)) * Add support for async delegate by [@icalvo](https://github.com/icalvo) in [#1215](https://github.com/spectreconsole/spectre.console/pull/1215)
* Remove unnecessary `[NotNull]` attributes by [@0xced](https://github.com/0xced) in [#1255](https://github.com/spectreconsole/spectre.console/pull/1255) * Remove unnecessary `[NotNull]` attributes by [@0xced](https://github.com/0xced) in [#1255](https://github.com/spectreconsole/spectre.console/pull/1255)
* Allow custom help providers by [@FrankRay78](https://github.com/FrankRay78) in [#1259](https://github.com/spectreconsole/spectre.console/pull/1259) * Allow custom help providers by [@FrankRay78](https://github.com/FrankRay78) in [#1259](https://github.com/spectreconsole/spectre.console/pull/1259)
* Specified details for settings for the argument vector by [@nils](https://github.com/nils)-a in [#1301](https://github.com/spectreconsole/spectre.console/pull/1301) * Specified details for settings for the argument vector by [@nils](https://github.com/nils)-a in [#1301](https://github.com/spectreconsole/spectre.console/pull/1301)

View File

@ -0,0 +1,71 @@
Title: Spectre.Console 0.50 released!
Description: Now with 25% less lead!
Published: 2025-04-08
Category: Release Notes
Excluded: false
---
Version 0.50 of Spectre.Console has been released!
## New Contributors
* [@Kissaki](https://github.com/Kissaki) made their first contribution in [#1575](https://github.com/spectreconsole/spectre.console/pull/1575)
* [@z4ryy](https://github.com/z4ryy) made their first contribution in [#1590](https://github.com/spectreconsole/spectre.console/pull/1590)
* [@TonWin618](https://github.com/TonWin618) made their first contribution in [#1595](https://github.com/spectreconsole/spectre.console/pull/1595)
* [@KirillOsenkov](https://github.com/KirillOsenkov) made their first contribution in [#1623](https://github.com/spectreconsole/spectre.console/pull/1623)
* [@davide-pi](https://github.com/davide-pi) made their first contribution in [#1246](https://github.com/spectreconsole/spectre.console/pull/1246)
* [@armanossiloko](https://github.com/armanossiloko) made their first contribution in [#1668](https://github.com/spectreconsole/spectre.console/pull/1668)
* [@PascalSenn](https://github.com/PascalSenn) made their first contribution in [#1687](https://github.com/spectreconsole/spectre.console/pull/1687)
* [@tpill90](https://github.com/tpill90) made their first contribution in [#904](https://github.com/spectreconsole/spectre.console/pull/904)
* [@tmds](https://github.com/tmds) made their first contribution in [#1194](https://github.com/spectreconsole/spectre.console/pull/1194)
* [@TheMarteh](https://github.com/TheMarteh) made their first contribution in [#1708](https://github.com/spectreconsole/spectre.console/pull/1708)
* [@Tolitech](https://github.com/Tolitech) made their first contribution in [#1717](https://github.com/spectreconsole/spectre.console/pull/1717)
* [@TheTonttu](https://github.com/TheTonttu) made their first contribution in [#1740](https://github.com/spectreconsole/spectre.console/pull/1740)
* [@byte2pixel](https://github.com/byte2pixel) made their first contribution in [#1762](https://github.com/spectreconsole/spectre.console/pull/1762)
* [@Moustafaa91](https://github.com/Moustafaa91) made their first contribution in [#1779](https://github.com/spectreconsole/spectre.console/pull/1779)
### General
* Strong name the assemblies by [@KirillOsenkov](https://github.com/KirillOsenkov) in [#1623](https://github.com/spectreconsole/spectre.console/pull/1623)
* Update MSDN link to learn.microsoft.com by [@Kissaki](https://github.com/Kissaki) in [#1575](https://github.com/spectreconsole/spectre.console/pull/1575)
* Add spanish translation for help strings by [@kzu](https://github.com/kzu) in [#1597](https://github.com/spectreconsole/spectre.console/pull/1597)
* Update documentation: add example for the Text Prompt usage by [@davide-pi](https://github.com/davide-pi) in [#1636](https://github.com/spectreconsole/spectre.console/pull/1636)
* Fix typos xml docs by [@devlead](https://github.com/devlead) in [#1684](https://github.com/spectreconsole/spectre.console/pull/1684)
* Upgrade SixLabors.ImageSharp to 3.1.7 by [@Moustafaa91](https://github.com/Moustafaa91) in [#1779](https://github.com/spectreconsole/spectre.console/pull/1779)
### Console
* AOT Support for Spectre.Console by [@phil-scott-78](https://github.com/phil-scott-78) in [#1690](https://github.com/spectreconsole/spectre.console/pull/1690)
* Make method reference to Markup.Escape more obvious by [@Kissaki](https://github.com/Kissaki) in [#1574](https://github.com/spectreconsole/spectre.console/pull/1574)
* Fix `HtmlEncoder` Incorrectly Applying Italics to Bold Text by [@z4ryy](https://github.com/z4ryy) in [#1590](https://github.com/spectreconsole/spectre.console/pull/1590)
* Fix Console Display Issue with Deleting Wide Characters by [@TonWin618](https://github.com/TonWin618) in [#1595](https://github.com/spectreconsole/spectre.console/pull/1595)
* Fix search bug in prompt related to custom item types by [@patriksvensson](https://github.com/patriksvensson) in [#1627](https://github.com/spectreconsole/spectre.console/pull/1627)
* Cleanup the prompt tests by [@0xced](https://github.com/0xced) in [#1635](https://github.com/spectreconsole/spectre.console/pull/1635)
* Add custom style for each calendar event by [@davide-pi](https://github.com/davide-pi) in [#1246](https://github.com/spectreconsole/spectre.console/pull/1246)
* Fix tree expansion bug by [@davide-pi](https://github.com/davide-pi) in [#1245](https://github.com/spectreconsole/spectre.console/pull/1245)
* Enhance the style of the checkboxes for multi-selection by [@davide-pi](https://github.com/davide-pi) in [#1244](https://github.com/spectreconsole/spectre.console/pull/1244)
* Improve exception if a (multi)selection prompt is used incorrectly by [@0xced](https://github.com/0xced) in [#1637](https://github.com/spectreconsole/spectre.console/pull/1637)
* Fix incorrect panel height calculation in complex layout by [@BlazeFace](https://github.com/BlazeFace) in [#1514](https://github.com/spectreconsole/spectre.console/pull/1514)
* Adding Enricher for Azure Pipelines by [@BlazeFace](https://github.com/BlazeFace) in [#1675](https://github.com/spectreconsole/spectre.console/pull/1675)
* Added hex color conversion by [@jsheely](https://github.com/jsheely) in [#1432](https://github.com/spectreconsole/spectre.console/pull/1432)
* Fixed type in Segment description by [@PascalSenn](https://github.com/PascalSenn) in [#1687](https://github.com/spectreconsole/spectre.console/pull/1687)
* Adding TransferSpeedColumn configuration to display bits/bytes + binary/decimal prefixes by [@tpill90](https://github.com/tpill90) in [#904](https://github.com/spectreconsole/spectre.console/pull/904)
* Changes Emoji dictionary to OrdinalIgnoreCase for performance by [@phil-scott-78](https://github.com/phil-scott-78) in [#1691](https://github.com/spectreconsole/spectre.console/pull/1691)
* ProgressTask.GetPercentage() returns 100 when max value is 0 by [@FrankRay78](https://github.com/FrankRay78) in [#1694](https://github.com/spectreconsole/spectre.console/pull/1694)
* Async overloads for AnsiConsole Prompt/Ask/Confirm. by [@tmds](https://github.com/tmds) in [#1194](https://github.com/spectreconsole/spectre.console/pull/1194)
* Support 3-digit hex codes in markup by [@TheMarteh](https://github.com/TheMarteh) in [#1708](https://github.com/spectreconsole/spectre.console/pull/1708)
* Add async spinner extension methods and related documentation by [@phil-scott-78](https://github.com/phil-scott-78) in [#1747](https://github.com/spectreconsole/spectre.console/pull/1747)
* Fix generic exception formatting by [@0xced](https://github.com/0xced) in [#1755](https://github.com/spectreconsole/spectre.console/pull/1755)
### CLI
* Remove redundant explain settings ctor by [@gitfool](https://github.com/gitfool) in [#1534](https://github.com/spectreconsole/spectre.console/pull/1534)
* Trim trailing comma in settings by [@devlead](https://github.com/devlead) in [#1550](https://github.com/spectreconsole/spectre.console/pull/1550)
* Consider -? as an alias to -h by [@kzu](https://github.com/kzu) in [#1552](https://github.com/spectreconsole/spectre.console/pull/1552)
* Trimming of TestConsole output by CommandAppTester is user configurable. by [@FrankRay78](https://github.com/FrankRay78) in [#1739](https://github.com/spectreconsole/spectre.console/pull/1739)
* Include resource files for additional cultures in HelpProvider. by [@Tolitech](https://github.com/Tolitech) in [#1717](https://github.com/spectreconsole/spectre.console/pull/1717)
* Conditionally trim trailing periods of argument and option descriptions by [@TheTonttu](https://github.com/TheTonttu) in [#1740](https://github.com/spectreconsole/spectre.console/pull/1740)
* Changed IConfigurator to return IConfigurator instead of void by [@byte2pixel](https://github.com/byte2pixel) in [#1762](https://github.com/spectreconsole/spectre.console/pull/1762)
* Add parsed unknown flag to remaining arguments for a branch with a default command by [@FrankRay78](https://github.com/FrankRay78) in [#1660](https://github.com/spectreconsole/spectre.console/pull/1660)
* Correctly show application version; execution of command with version option by [@FrankRay78](https://github.com/FrankRay78) in [#1663](https://github.com/spectreconsole/spectre.console/pull/1663)
* Help output correctly decides when to show the version option by [@FrankRay78](https://github.com/FrankRay78) in [#1664](https://github.com/spectreconsole/spectre.console/pull/1664)

View File

@ -1,3 +1,3 @@
@{ @{
Layout = @$"_layout.cshtml"; Layout = "_layout.cshtml";
} }

View File

@ -41,7 +41,7 @@ Removing all styling from help text is also possible, a good choice for ensuring
config.Settings.HelpProviderStyles = null; config.Settings.HelpProviderStyles = null;
``` ```
See [Markup](../markup) for information about the use of markup in Spectre.Console, and [Styles](xref:styles) for a listing of supported styles. See [Markup](../markup) for information about the use of markup in Spectre.Console, and [Styles](xref:appendix-styles) for a listing of supported styles.
## Custom help providers ## Custom help providers
@ -71,5 +71,5 @@ public static class Program
} }
``` ```
There is a working [example of a custom help provider](https://github.com/spectreconsole/spectre.console/tree/main/examples/Cli/Help) demonstrating this. There is a working [example of a custom help provider](https://github.com/spectreconsole/examples/tree/main/examples/Cli/Help) demonstrating this.

View File

@ -67,7 +67,7 @@ registrations.AddSingleton<IGreeter, HelloWorldGreeter>();
// Create a type registrar and register any dependencies. // Create a type registrar and register any dependencies.
// A type registrar is an adapter for a DI framework. // A type registrar is an adapter for a DI framework.
var registrar = new TypeRegistrar(registrations); var registrar = new MyTypeRegistrar(registrations);
// Create a new command app with the registrar // Create a new command app with the registrar
// and run it with the provided arguments. // and run it with the provided arguments.
@ -75,10 +75,13 @@ var app = new CommandApp<DefaultCommand>(registrar);
return app.Run(args); return app.Run(args);
``` ```
`TypeRegistrar` is a custom class that must be created by the user. This [example using `Microsoft.Extensions.DependencyInjection` as the container](https://github.com/spectreconsole/spectre.console/tree/main/examples/Cli/Injection) provides an example `TypeRegistrar` and `TypeResolver` that can be added to your application with small adjustments for your DI container. <?# Alert ?>
`MyTypeRegistrar` is a custom class that implements [ITypeRegistrar](xref:T:Spectre.Console.Cli.ITypeRegistrar) and must be provided by the user.
<?#/ Alert ?>
Hint: If you do write your own implementation of `TypeRegistrar` and `TypeResolver` and you have some form of unit tests in place for your project, There is a working [example of dependency injection](https://github.com/spectreconsole/examples/tree/main/examples/Cli/Injection) that uses `Microsoft.Extensions.DependencyInjection` as the container. Example implementations of `ITypeRegistrar` and `ITypeResolver` are provided, which you can copy and paste to your application for dependency injection.
there is a utility `TypeRegistrarBaseTests` available that can be used to ensure your implementations adhere to the required implementation. Simply call `TypeRegistrarBaseTests.RunAllTests()` and expect no `TypeRegistrarBaseTests.TestFailedException` to be thrown.
Unit testing your `ITypeRegistrar` and `ITypeResolver` implementations is done using the utility `TypeRegistrarBaseTests` included in `Spectre.Console.Testing`. Simply call `TypeRegistrarBaseTests.RunAllTests()` and expect no `TypeRegistrarBaseTests.TestFailedException` to be thrown.
## Interception ## Interception
Interceptors can be registered with the `TypeRegistrar` (or with a custom DI-Container). Alternatively, `CommandApp` also provides a `SetInterceptor` configuration. Interceptors can be registered with the `TypeRegistrar` (or with a custom DI-Container). Alternatively, `CommandApp` also provides a `SetInterceptor` configuration.
@ -89,4 +92,4 @@ This provides an opportunity to modify the result and also to tear down any infr
The `Intercept`-Method of each interceptor is run before the command is executed and the `InterceptResult`-Method is run after it. These are typically used for configuring logging or other infrastructure concerns. The `Intercept`-Method of each interceptor is run before the command is executed and the `InterceptResult`-Method is run after it. These are typically used for configuring logging or other infrastructure concerns.
For an example of using the interceptor to configure logging, see the [Serilog demo](https://github.com/spectreconsole/spectre.console/tree/main/examples/Cli/Logging). For an example of using the interceptor to configure logging, see the [Serilog demo](https://github.com/spectreconsole/examples/tree/main/examples/Cli/Logging)

View File

@ -1,8 +1,8 @@
Title: Composing Commands Title: Composing Commands
RedirectFrom: introduction RedirectFrom: introduction
Order: 8 Order: 8
Description: "The underlying philosophy behind *Spectre.Console.Cli* is to rely on the .NET type system to Description: The underlying philosophy behind *Spectre.Console.Cli* is
declare the commands, but tie everything together via composition." to rely on the .NET type system to declare the commands, but tie everything together via composition.
--- ---
The underlying philosophy behind `Spectre.Console.Cli` is to rely on the .NET type system to The underlying philosophy behind `Spectre.Console.Cli` is to rely on the .NET type system to

View File

@ -1,8 +1,8 @@
Title: Introduction Title: Introduction
Order: 1 Order: 1
Description: "*Spectre.Console.Cli* is a modern library for parsing command line arguments. While it's extremely Description: "*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 opinionated in what it does, it tries to follow established industry conventions, and draws
its inspiration from applications you use everyday." its inspiration from applications you use everyday."
--- ---
`Spectre.Console.Cli` is a modern library for parsing command line arguments. While it's extremely `Spectre.Console.Cli` is a modern library for parsing command line arguments. While it's extremely

View File

@ -0,0 +1,199 @@
Title: Unit Testing
Order: 14
Description: Instructions for unit testing a Spectre.Console application.
Reference:
- T:Spectre.Console.Testing.CommandAppTester
- T:Spectre.Console.Testing.TestConsole
- T:Spectre.Console.Testing.TestConsoleInput
---
`Spectre.Console` has a separate project that contains test harnesses for unit testing your own console applications.
The fastest way of getting started is to install the `Spectre.Console.Testing` NuGet package.
```text
> dotnet add package Spectre.Console.Testing
```
`Spectre.Console.Testing` is also the namespace containing the test classes.
## Testing a CommandApp
The `CommandAppTester` is a test implementation of `CommandApp` that's configured in a similar manner but designed for unit testing.
The following example validates the exit code and terminal output of a `Spectre.Console` command:
```csharp
/// <summary>
/// A Spectre.Console Command
/// </summary>
public class HelloWorldCommand : Command
{
private readonly IAnsiConsole _console;
public HelloWorldCommand(IAnsiConsole console)
{
// nb. AnsiConsole should not be called directly by the command
// since this doesn't play well with testing. Instead,
// the command should inject a IAnsiConsole and use that.
_console = console;
}
public override int Execute(CommandContext context)
{
_console.WriteLine("Hello world.");
return 0;
}
}
[TestMethod]
public void Should_Output_Hello_World()
{
// Given
var app = new CommandAppTester();
app.SetDefaultCommand<HelloWorldCommand>();
// When
var result = app.Run();
// Then
Assert.AreEqual(result.ExitCode, 0);
Assert.AreEqual(result.Output, "Hello world.");
}
```
The following example demonstrates how to mock user inputs for an interactive command.
This test (InteractiveCommand_WithMockedUserInputs_ProducesExpectedOutput) simulates user interactions by pushing predefined inputs to the console, then verifies that the resulting output is as expected.
```csharp
public sealed class InteractiveCommandTests
{
private sealed class InteractiveCommand : Command
{
private readonly IAnsiConsole _console;
public InteractiveCommand(IAnsiConsole console)
{
_console = console;
}
public override int Execute(CommandContext context)
{
var fruits = _console.Prompt(
new MultiSelectionPrompt<string>()
.Title("What are your [green]favorite fruits[/]?")
.NotRequired() // Not required to have a favorite fruit
.PageSize(10)
.MoreChoicesText("[grey](Move up and down to reveal more fruits)[/]")
.InstructionsText(
"[grey](Press [blue]<space>[/] to toggle a fruit, " +
"[green]<enter>[/] to accept)[/]")
.AddChoices(new[] {
"Apple", "Apricot", "Avocado",
"Banana", "Blackcurrant", "Blueberry",
"Cherry", "Cloudberry", "Coconut",
}));
var fruit = _console.Prompt(
new SelectionPrompt<string>()
.Title("What's your [green]favorite fruit[/]?")
.PageSize(10)
.MoreChoicesText("[grey](Move up and down to reveal more fruits)[/]")
.AddChoices(new[] {
"Apple", "Apricot", "Avocado",
"Banana", "Blackcurrant", "Blueberry",
"Cherry", "Cloudberry", "Cocunut",
}));
var name = _console.Ask<string>("What's your name?");
_console.WriteLine($"[{string.Join(',', fruits)};{fruit};{name}]");
return 0;
}
}
[Fact]
public void InteractiveCommand_WithMockedUserInputs_ProducesExpectedOutput()
{
// Given
TestConsole console = new();
console.Interactive();
// Your mocked inputs must always end with "Enter" for each prompt!
// Multi selection prompt: Choose first option
console.Input.PushKey(ConsoleKey.Spacebar);
console.Input.PushKey(ConsoleKey.Enter);
// Selection prompt: Choose second option
console.Input.PushKey(ConsoleKey.DownArrow);
console.Input.PushKey(ConsoleKey.Enter);
// Ask text prompt: Enter name
console.Input.PushTextWithEnter("Spectre Console");
var app = new CommandAppTester(null, new CommandAppTesterSettings(), console);
app.SetDefaultCommand<InteractiveCommand>();
// When
var result = app.Run();
// Then
result.ExitCode.ShouldBe(0);
result.Output.EndsWith("[Apple;Apricot;Spectre Console]");
}
}
```
## Testing console behaviour
`TestConsole` and `TestConsoleInput` are testable implementations of `IAnsiConsole` and `IAnsiConsoleInput`, allowing you fine-grain control over testing console output and interactivity.
The following example renders some widgets before then validating the console output:
```csharp
[TestMethod]
public void Should_Render_Panel()
{
// Given
var console = new TestConsole();
// When
console.Write(new Panel(new Text("Hello World")));
// Then
Assert.AreEqual(console.Output, """"
┌─────────────┐
Hello World
└─────────────┘
"""");
}
```
While `Assert` is fine for validating simple output, more complex output may benefit from a tool like [Verify](https://github.com/VerifyTests/Verify).
The following example prompts the user for input before then validating the expected choice was made:
```csharp
[TestMethod]
public void Should_Select_Orange()
{
// Given
var console = new TestConsole();
console.Input.PushTextWithEnter("Orange");
// When
console.Prompt(
new TextPrompt<string>("Favorite fruit?")
.AddChoice("Banana")
.AddChoice("Orange"));
// Then
Assert.AreEqual(console.Output, "Favorite fruit? [Banana/Orange]: Orange\n");
}
```
`CommandAppTester` uses `TestConsole` internally, which in turn uses `TestConsoleInput`, offering a fully testable harness for `Spectre.Console` widgets, prompts and commands.

View File

@ -6,7 +6,7 @@ Order: 0
Spectre.Console is a `.NET` library that makes it easier Spectre.Console is a `.NET` library that makes it easier
to create beautiful console applications. to create beautiful console applications.
## Spectre.Console.AnsiConsole Features ## Spectre.Console.AnsiConsole
* Easily output text with different colors and even styles such as bold, italic and blinking with a Rich inspired [markup language](markup). * Easily output text with different colors and even styles such as bold, italic and blinking with a Rich inspired [markup language](markup).
* Supports `3`/`4`/`8`/`24`-bit colors in the terminal with auto-detection of the current terminal's capabilities. * Supports `3`/`4`/`8`/`24`-bit colors in the terminal with auto-detection of the current terminal's capabilities.
@ -14,16 +14,19 @@ to create beautiful console applications.
* Display progress for long running tasks with live displays of [progress](live/progress) and [status](live/status) controls. * Display progress for long running tasks with live displays of [progress](live/progress) and [status](live/status) controls.
* Prompt user input with strongly typed [text input](prompts/text) or via [single-item select](prompts/selection) and [multiple item select](prompts/multiselection) controls. * Prompt user input with strongly typed [text input](prompts/text) or via [single-item select](prompts/selection) and [multiple item select](prompts/multiselection) controls.
* Format .NET [exceptions](exceptions) with custom color coded themes and styles. * Format .NET [exceptions](exceptions) with custom color coded themes and styles.
* Written with unit testing in mind.
Spectre.Console.AnsiConsole has been heavily inspired Spectre.Console.AnsiConsole has been heavily inspired by the excellent [Rich](https://github.com/willmcgugan/rich) library for Python written by Will McGugan.
by the excellent [Rich](https://github.com/willmcgugan/rich) library
for Python written by Will McGugan.
## Spectre.Console.Cli ## Spectre.Console.Cli
* Create strongly typed settings and commands for parsing `args[]` to create complex command line applications like `git`, `gh`, or `dotnet` * Create strongly typed settings and commands for parsing `args[]` to create complex command line applications like `git`, `gh`, or `dotnet`
## Spectre.Console.Testing
* Spectre.Console has been developed with unit testing in mind. The Spectre.Console library itself is covered by an extensive test suite, project maintainers require test coverage for all new commits, and the same extension points and test harnesses used internally for testing are available to you.
* The [Unit Testing](cli/unit-testing) page provides instructions for testing a Spectre.Console application.
## Examples ## Examples
![Sample of Spectre.Console output](./assets/images/example.png) ![Sample of Spectre.Console output](./assets/images/example.png)
@ -36,3 +39,4 @@ for Python written by Will McGugan.
Sorry, your browser doesn't support embedded videos. Sorry, your browser doesn't support embedded videos.
</video> </video>
The Spectre.Console [examples repository](https://github.com/spectreconsole/examples) contains many other examples.

73
docs/input/live/async.md Normal file
View File

@ -0,0 +1,73 @@
Title: Async Extensions
Order: 11
Description: "Async Extensions provides extension methods for running tasks with an inline animations."
Highlights:
- Extension methods for running tasks with spinner animations
- Support for both void and generic Task types
- Customizable spinner styles and console output
Reference:
- T:Spectre.Console.Extensions.SpinnerExtensions
Xref: spinner-extensions
---
The Async Spinner Extension provides convenient extension methods for running tasks with an inline spinner animations in the console.
<?# AsciiCast cast="await-spinner" /?>
<?# Alert ?>
The spinner animation is not thread safe, and using it together with other interactive
components such as prompts, progress displays or other status displays is not supported.
<?#/ Alert ?>
## Usage
The extension methods allow you to easily add spinner animations to any Task execution:
```csharp
// Basic usage with void Task
await someTask.Spinner();
// With generic Task<T>
var result = await someTaskWithResult.Spinner(
Spinner.Known.Star,
new Style(foreground: Color.Green));
// With custom console
await someTask.Spinner(
Spinner.Known.Dots,
style: Style.Plain,
ansiConsole: customConsole);
```
## Features
The spinner extensions provide:
- Support for both void Tasks and Tasks with return values
- Customizable spinner animations using any Spectre.Console Spinner
- Optional styling for the spinner animation
- Ability to specify a custom IAnsiConsole instance
## Examples
Here's a more complete example showing different ways to use the spinner extensions:
```csharp
// Basic spinner with default settings
await Task.Delay(1000)
.Spinner(Spinner.Known.Dots);
// Customized spinner with style
var result = await CalculateSomething()
.Spinner(
Spinner.Known.Star,
new Style(foreground: Color.Green));
// Using with a custom console
await ProcessData()
.Spinner(
new Spinner(new[] { "⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏" }, 80),
new Style(foreground: Color.Blue),
customConsole);
```

View File

@ -9,6 +9,7 @@ Highlights:
Reference: Reference:
- T:Spectre.Console.Progress - T:Spectre.Console.Progress
- M:Spectre.Console.AnsiConsole.Progress - M:Spectre.Console.AnsiConsole.Progress
Xref: live-progress
--- ---
Spectre.Console can display information about long running tasks in the console. Spectre.Console can display information about long running tasks in the console.
@ -85,6 +86,8 @@ AnsiConsole.Progress()
new PercentageColumn(), // Percentage new PercentageColumn(), // Percentage
new RemainingTimeColumn(), // Remaining time new RemainingTimeColumn(), // Remaining time
new SpinnerColumn(), // Spinner new SpinnerColumn(), // Spinner
new DownloadedColumn(), // Downloaded
new TransferSpeedColumn(), // Transfer speed
}) })
.Start(ctx => .Start(ctx =>
{ {

View File

@ -8,6 +8,7 @@ Highlights:
Reference: Reference:
- T:Spectre.Console.Status - T:Spectre.Console.Status
- M:Spectre.Console.AnsiConsole.Status - M:Spectre.Console.AnsiConsole.Status
Xref: live-status
--- ---
Spectre.Console can display information about long running tasks in the console. Spectre.Console can display information about long running tasks in the console.

View File

@ -27,6 +27,32 @@ you can use the `Prompt<TResult>`.
Run prompt example? [y/n] (y): _ Run prompt example? [y/n] (y): _
``` ```
### Usage
```csharp
// Ask the user to confirm
var confirmation = AnsiConsole.Prompt(
new TextPrompt<bool>("Run prompt example?")
.AddChoice(true)
.AddChoice(false)
.DefaultValue(true)
.WithConverter(choice => choice ? "y" : "n"));
// Echo the confirmation back to the terminal
Console.WriteLine(confirmation ? "Confirmed" : "Declined");
```
Otherwise it is possible to use the `ConfirmationPrompt`
```csharp
// Ask the user to confirm
var confirmation = AnsiConsole.Prompt(
new ConfirmationPrompt("Run prompt example?"));
// Echo the confirmation back to the terminal
Console.WriteLine(confirmation ? "Confirmed" : "Declined");
```
## Simple ## Simple
<?# Example symbol="M:Prompt.Program.AskName" project="Prompt" /?> <?# Example symbol="M:Prompt.Program.AskName" project="Prompt" /?>
@ -36,6 +62,30 @@ What's your name? Patrik
What's your age? 37 What's your age? 37
``` ```
### Usage
```csharp
// Ask the user a couple of simple questions
var name = AnsiConsole.Prompt(
new TextPrompt<string>("What's your name?"));
var age = AnsiConsole.Prompt(
new TextPrompt<int>("What's your age?"));
// Echo the name and age back to the terminal
AnsiConsole.WriteLine($"So you're {name} and you're {age} years old");
```
Otherwise it is possible to use the `Ask` method
```csharp
// Ask the user a couple of simple questions
var name = AnsiConsole.Ask<string>("What's your name?");
var age = AnsiConsole.Ask<int>("What's your age?");
// Echo the name and age back to the terminal
AnsiConsole.WriteLine($"So you're {name} and you're {age} years old");
```
## Choices ## Choices
<?# Example symbol="M:Prompt.Program.AskFruit" project="Prompt" /?> <?# Example symbol="M:Prompt.Program.AskFruit" project="Prompt" /?>
@ -44,6 +94,19 @@ What's your age? 37
What's your favorite fruit? [Apple/Banana/Orange] (Orange): _ What's your favorite fruit? [Apple/Banana/Orange] (Orange): _
``` ```
### Usage
```csharp
// Ask for the user's favorite fruit
var fruit = AnsiConsole.Prompt(
new TextPrompt<string>("What's your favorite fruit?")
.AddChoices(["Apple", "Banana", "Orange"])
.DefaultValue("Orange"));
// Echo the fruit back to the terminal
Console.WriteLine($"I agree. {fruit} is tasty!");
```
## Validation ## Validation
<?# Example symbol="M:Prompt.Program.AskAge" project="Prompt" /?> <?# Example symbol="M:Prompt.Program.AskAge" project="Prompt" /?>
@ -56,6 +119,23 @@ Too high
What's the secret number? _ What's the secret number? _
``` ```
### Usage
```csharp
// Ask the user to guess the secret number
var number = AnsiConsole.Prompt(
new TextPrompt<int>("What's the secret number?")
.Validate((n) => n switch
{
< 50 => ValidationResult.Error("Too low"),
50 => ValidationResult.Success(),
> 50 => ValidationResult.Error("Too high"),
}));
// Echo the user's success back to the terminal
Console.WriteLine($"Correct! The secret number is {number}.");
```
## Secrets ## Secrets
<?# Example symbol="M:Prompt.Program.AskPassword" project="Prompt" /?> <?# Example symbol="M:Prompt.Program.AskPassword" project="Prompt" /?>
@ -65,6 +145,18 @@ What's the secret number? _
Enter password: ************_ Enter password: ************_
``` ```
### Usage
```csharp
// Ask the user to enter the password
var password = AnsiConsole.Prompt(
new TextPrompt<string>("Enter password:")
.Secret());
// Echo the password back to the terminal
Console.WriteLine($"Your password is {password}");
```
## Masks ## Masks
<?# Example symbol="M:Prompt.Program.AskPasswordWithCustomMask" project="Prompt" /?> <?# Example symbol="M:Prompt.Program.AskPasswordWithCustomMask" project="Prompt" /?>
@ -82,10 +174,36 @@ You can utilize a null character to completely hide input.
Enter password: _ Enter password: _
``` ```
### Usage
```csharp
// Ask the user to enter the password
var password = AnsiConsole.Prompt(
new TextPrompt<string>("Enter password:")
.Secret('-'));
// Echo the password back to the terminal
Console.WriteLine($"Your password is {password}");
```
## Optional ## Optional
<?# Example symbol="M:Prompt.Program.AskColor" project="Prompt" /?> <?# Example symbol="M:Prompt.Program.AskColor" project="Prompt" /?>
```text ```text
[Optional] Favorite color? _ [Optional] Favorite color? _
```
### Usage
```csharp
// Ask the user to enter the password
var color = AnsiConsole.Prompt(
new TextPrompt<string>("[[Optional]] Favorite color?")
.AllowEmpty());
// Echo the color back to the terminal
Console.WriteLine(string.IsNullOrWhiteSpace(color)
? "You're right, all colors are beautiful"
: $"I agree. {color} is a very beautiful color");
``` ```

View File

@ -17,7 +17,7 @@ and the ascii terminal player.
/* temp styling for alerts */ /* temp styling for alerts */
.alert-warning { .alert-warning {
@apply p-4 border border-yellow-300 bg-yellow-100 text-yellow-800 dark:border-orange-700/50 dark:bg-orange-800/50 dark:text-orange-300/90 rounded shadow-sm text-sm; @apply p-4 border border-yellow-300 bg-yellow-100 text-yellow-800 dark:border-red-700/50 dark:bg-red-800/50 dark:text-red-100/90 rounded shadow-sm text-sm;
} }
.alert-warning p { .alert-warning p {

View File

@ -0,0 +1,66 @@
Title: Align
Description: "Use **Align** to render and position widgets in the console."
Highlights:
- Custom colors
- Labels
- Use your own data with a converter.
Reference: T:Spectre.Console.Align
---
Use `Align` to render and position widgets in the console.
<?# AsciiCast cast="align" /?>
## Usage
### Basic usage
```csharp
// Render an item and align it in the bottom-left corner of the console
AnsiConsole.Write(new Align(
new Text("Spectre!"),
HorizontalAlignment.Left,
VerticalAlignment.Bottom
));
```
### Align items from an IEnumerable
```csharp
// Create a list of items
var alignItems = new List<Text>(){
new Text("Spectre"),
new Text("Console"),
new Text("Is Awesome!")
};
// Render the items in the middle-right of the console
AnsiConsole.Write(new Align(
alignItems,
HorizontalAlignment.Right,
VerticalAlignment.Middle
));
```
### Dynamically align with different widgets
```csharp
// Create a table
var table = new Table()
.AddColumn("ID")
.AddColumn("Methods")
.AddColumn("Purpose")
.AddRow("1", "Center()", "Initializes a new instance that is center aligned")
.AddRow("2", "Measure()", "Measures the renderable object")
.AddRow("3", "Right()", "Initializes a new instance that is right aligned.");
// Create a panel
var panel = new Panel(table)
.Header("Other Align Methods")
.Border(BoxBorder.Double);
// Renders the panel in the top-center of the console
AnsiConsole.Write(new Align(panel, HorizontalAlignment.Center, VerticalAlignment.Top));
```

View File

@ -137,4 +137,11 @@ table.Columns[0].NoWrap();
```csharp ```csharp
// Set the column width // Set the column width
table.Columns[0].Width(15); table.Columns[0].Width(15);
```
### Show row separators
```csharp
// Shows separator between each row
table.ShowRowSeparators();
``` ```

282
docs/package-lock.json generated
View File

@ -123,12 +123,13 @@
} }
}, },
"node_modules/@babel/runtime": { "node_modules/@babel/runtime": {
"version": "7.17.0", "version": "7.27.0",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.0.tgz", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.0.tgz",
"integrity": "sha512-etcO/ohMNaNA2UBdaXBBSX/3aEzFMRrVfaPv8Ptc0k+cWpWW0QFiGZ2XnVqQZI1Cf734LbPGmqBKWESfW4x/dQ==", "integrity": "sha512-VtPOkrdPHZsKc/clNqyi9WUA8TINkZ4cGk63UUE3u4pmB2k+ZMQRDuIOagv8UVd6j7k0T3+RRIb7beKTebNbcw==",
"dev": true, "dev": true,
"license": "MIT",
"dependencies": { "dependencies": {
"regenerator-runtime": "^0.13.4" "regenerator-runtime": "^0.14.0"
}, },
"engines": { "engines": {
"node": ">=6.9.0" "node": ">=6.9.0"
@ -315,12 +316,13 @@
} }
}, },
"node_modules/braces": { "node_modules/braces": {
"version": "3.0.2", "version": "3.0.3",
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
"integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
"dev": true, "dev": true,
"license": "MIT",
"dependencies": { "dependencies": {
"fill-range": "^7.0.1" "fill-range": "^7.1.1"
}, },
"engines": { "engines": {
"node": ">=8" "node": ">=8"
@ -475,10 +477,11 @@
} }
}, },
"node_modules/cross-spawn": { "node_modules/cross-spawn": {
"version": "7.0.3", "version": "7.0.6",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
"integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
"dev": true, "dev": true,
"license": "MIT",
"dependencies": { "dependencies": {
"path-key": "^3.1.0", "path-key": "^3.1.0",
"shebang-command": "^2.0.0", "shebang-command": "^2.0.0",
@ -500,6 +503,13 @@
"node": ">=4" "node": ">=4"
} }
}, },
"node_modules/csstype": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
"dev": true,
"license": "MIT"
},
"node_modules/defined": { "node_modules/defined": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz",
@ -587,10 +597,11 @@
} }
}, },
"node_modules/fill-range": { "node_modules/fill-range": {
"version": "7.0.1", "version": "7.1.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
"integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
"dev": true, "dev": true,
"license": "MIT",
"dependencies": { "dependencies": {
"to-regex-range": "^5.0.1" "to-regex-range": "^5.0.1"
}, },
@ -737,6 +748,7 @@
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
"dev": true, "dev": true,
"license": "MIT",
"engines": { "engines": {
"node": ">=0.12.0" "node": ">=0.12.0"
} }
@ -802,13 +814,14 @@
} }
}, },
"node_modules/micromatch": { "node_modules/micromatch": {
"version": "4.0.4", "version": "4.0.8",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
"integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
"dev": true, "dev": true,
"license": "MIT",
"dependencies": { "dependencies": {
"braces": "^3.0.1", "braces": "^3.0.3",
"picomatch": "^2.2.3" "picomatch": "^2.3.1"
}, },
"engines": { "engines": {
"node": ">=8.6" "node": ">=8.6"
@ -824,16 +837,27 @@
} }
}, },
"node_modules/minimist": { "node_modules/minimist": {
"version": "1.2.5", "version": "1.2.8",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
"dev": true "dev": true,
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
}, },
"node_modules/nanoid": { "node_modules/nanoid": {
"version": "3.2.0", "version": "3.3.11",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.2.0.tgz", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
"integrity": "sha512-fmsZYa9lpn69Ad5eDn7FMcnnSR+8R34W9qJEijxYhTbfOWzr22n1QxCMzXLK+ODyW2973V3Fux959iQoUxzUIA==", "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
"dev": true, "dev": true,
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"license": "MIT",
"bin": { "bin": {
"nanoid": "bin/nanoid.cjs" "nanoid": "bin/nanoid.cjs"
}, },
@ -931,10 +955,11 @@
} }
}, },
"node_modules/picocolors": { "node_modules/picocolors": {
"version": "1.0.0", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
"integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
"dev": true "dev": true,
"license": "ISC"
}, },
"node_modules/picomatch": { "node_modules/picomatch": {
"version": "2.3.1", "version": "2.3.1",
@ -949,21 +974,32 @@
} }
}, },
"node_modules/postcss": { "node_modules/postcss": {
"version": "8.4.6", "version": "8.5.3",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.6.tgz", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz",
"integrity": "sha512-OovjwIzs9Te46vlEx7+uXB0PLijpwjXGKXjVGGPIGubGpq7uh5Xgf6D6FiJ/SzJMBosHDp6a2hiXOS97iBXcaA==", "integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==",
"dev": true, "dev": true,
"funding": [
{
"type": "opencollective",
"url": "https://opencollective.com/postcss/"
},
{
"type": "tidelift",
"url": "https://tidelift.com/funding/github/npm/postcss"
},
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"license": "MIT",
"dependencies": { "dependencies": {
"nanoid": "^3.2.0", "nanoid": "^3.3.8",
"picocolors": "^1.0.0", "picocolors": "^1.1.1",
"source-map-js": "^1.0.2" "source-map-js": "^1.2.1"
}, },
"engines": { "engines": {
"node": "^10 || ^12 || >=14" "node": "^10 || ^12 || >=14"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/postcss/"
} }
}, },
"node_modules/postcss-js": { "node_modules/postcss-js": {
@ -1093,10 +1129,11 @@
} }
}, },
"node_modules/regenerator-runtime": { "node_modules/regenerator-runtime": {
"version": "0.13.9", "version": "0.14.1",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
"integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==", "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==",
"dev": true "dev": true,
"license": "MIT"
}, },
"node_modules/resolve": { "node_modules/resolve": {
"version": "1.22.0", "version": "1.22.0",
@ -1157,6 +1194,29 @@
"queue-microtask": "^1.2.2" "queue-microtask": "^1.2.2"
} }
}, },
"node_modules/seroval": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/seroval/-/seroval-1.2.1.tgz",
"integrity": "sha512-yBxFFs3zmkvKNmR0pFSU//rIsYjuX418TnlDmc2weaq5XFDqDIV/NOMPBoLrbxjLH42p4UzRuXHryXh9dYcKcw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=10"
}
},
"node_modules/seroval-plugins": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/seroval-plugins/-/seroval-plugins-1.2.1.tgz",
"integrity": "sha512-H5vs53+39+x4Udwp4J5rNZfgFuA+Lt+uU+09w1gYBVWomtAl98B+E9w7yC05Xc81/HgLvJdlyqJbU0fJCKCmdw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=10"
},
"peerDependencies": {
"seroval": "^1.0"
}
},
"node_modules/shebang-command": { "node_modules/shebang-command": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
@ -1179,16 +1239,23 @@
} }
}, },
"node_modules/solid-js": { "node_modules/solid-js": {
"version": "1.3.5", "version": "1.9.5",
"resolved": "https://registry.npmjs.org/solid-js/-/solid-js-1.3.5.tgz", "resolved": "https://registry.npmjs.org/solid-js/-/solid-js-1.9.5.tgz",
"integrity": "sha512-PUom2cCARfvvgxI7cwOhfXMrZZZxjp+vIrb5fzVNBFyICy8A30wTqExwfUv457eJYgKpii2D3qStW9ILtKnShw==", "integrity": "sha512-ogI3DaFcyn6UhYhrgcyRAMbu/buBJitYQASZz5WzfQVPP10RD2AbCoRZ517psnezrasyCbWzIxZ6kVqet768xw==",
"dev": true "dev": true,
"license": "MIT",
"dependencies": {
"csstype": "^3.1.0",
"seroval": "^1.1.0",
"seroval-plugins": "^1.1.0"
}
}, },
"node_modules/source-map-js": { "node_modules/source-map-js": {
"version": "1.0.2", "version": "1.2.1",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
"integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
"dev": true, "dev": true,
"license": "BSD-3-Clause",
"engines": { "engines": {
"node": ">=0.10.0" "node": ">=0.10.0"
} }
@ -1274,6 +1341,7 @@
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
"dev": true, "dev": true,
"license": "MIT",
"dependencies": { "dependencies": {
"is-number": "^7.0.0" "is-number": "^7.0.0"
}, },
@ -1407,12 +1475,12 @@
} }
}, },
"@babel/runtime": { "@babel/runtime": {
"version": "7.17.0", "version": "7.27.0",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.0.tgz", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.0.tgz",
"integrity": "sha512-etcO/ohMNaNA2UBdaXBBSX/3aEzFMRrVfaPv8Ptc0k+cWpWW0QFiGZ2XnVqQZI1Cf734LbPGmqBKWESfW4x/dQ==", "integrity": "sha512-VtPOkrdPHZsKc/clNqyi9WUA8TINkZ4cGk63UUE3u4pmB2k+ZMQRDuIOagv8UVd6j7k0T3+RRIb7beKTebNbcw==",
"dev": true, "dev": true,
"requires": { "requires": {
"regenerator-runtime": "^0.13.4" "regenerator-runtime": "^0.14.0"
} }
}, },
"@nodelib/fs.scandir": { "@nodelib/fs.scandir": {
@ -1547,12 +1615,12 @@
"dev": true "dev": true
}, },
"braces": { "braces": {
"version": "3.0.2", "version": "3.0.3",
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
"integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
"dev": true, "dev": true,
"requires": { "requires": {
"fill-range": "^7.0.1" "fill-range": "^7.1.1"
} }
}, },
"browserslist": { "browserslist": {
@ -1652,9 +1720,9 @@
} }
}, },
"cross-spawn": { "cross-spawn": {
"version": "7.0.3", "version": "7.0.6",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
"integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
"dev": true, "dev": true,
"requires": { "requires": {
"path-key": "^3.1.0", "path-key": "^3.1.0",
@ -1668,6 +1736,12 @@
"integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
"dev": true "dev": true
}, },
"csstype": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
"dev": true
},
"defined": { "defined": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz",
@ -1743,9 +1817,9 @@
} }
}, },
"fill-range": { "fill-range": {
"version": "7.0.1", "version": "7.1.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
"integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
"dev": true, "dev": true,
"requires": { "requires": {
"to-regex-range": "^5.0.1" "to-regex-range": "^5.0.1"
@ -1905,13 +1979,13 @@
"dev": true "dev": true
}, },
"micromatch": { "micromatch": {
"version": "4.0.4", "version": "4.0.8",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
"integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
"dev": true, "dev": true,
"requires": { "requires": {
"braces": "^3.0.1", "braces": "^3.0.3",
"picomatch": "^2.2.3" "picomatch": "^2.3.1"
} }
}, },
"mini-svg-data-uri": { "mini-svg-data-uri": {
@ -1921,15 +1995,15 @@
"dev": true "dev": true
}, },
"minimist": { "minimist": {
"version": "1.2.5", "version": "1.2.8",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
"dev": true "dev": true
}, },
"nanoid": { "nanoid": {
"version": "3.2.0", "version": "3.3.11",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.2.0.tgz", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
"integrity": "sha512-fmsZYa9lpn69Ad5eDn7FMcnnSR+8R34W9qJEijxYhTbfOWzr22n1QxCMzXLK+ODyW2973V3Fux959iQoUxzUIA==", "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
"dev": true "dev": true
}, },
"node-releases": { "node-releases": {
@ -1998,9 +2072,9 @@
"dev": true "dev": true
}, },
"picocolors": { "picocolors": {
"version": "1.0.0", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
"integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
"dev": true "dev": true
}, },
"picomatch": { "picomatch": {
@ -2010,14 +2084,14 @@
"dev": true "dev": true
}, },
"postcss": { "postcss": {
"version": "8.4.6", "version": "8.5.3",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.6.tgz", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz",
"integrity": "sha512-OovjwIzs9Te46vlEx7+uXB0PLijpwjXGKXjVGGPIGubGpq7uh5Xgf6D6FiJ/SzJMBosHDp6a2hiXOS97iBXcaA==", "integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==",
"dev": true, "dev": true,
"requires": { "requires": {
"nanoid": "^3.2.0", "nanoid": "^3.3.8",
"picocolors": "^1.0.0", "picocolors": "^1.1.1",
"source-map-js": "^1.0.2" "source-map-js": "^1.2.1"
} }
}, },
"postcss-js": { "postcss-js": {
@ -2086,9 +2160,9 @@
} }
}, },
"regenerator-runtime": { "regenerator-runtime": {
"version": "0.13.9", "version": "0.14.1",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
"integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==", "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==",
"dev": true "dev": true
}, },
"resolve": { "resolve": {
@ -2123,6 +2197,19 @@
"queue-microtask": "^1.2.2" "queue-microtask": "^1.2.2"
} }
}, },
"seroval": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/seroval/-/seroval-1.2.1.tgz",
"integrity": "sha512-yBxFFs3zmkvKNmR0pFSU//rIsYjuX418TnlDmc2weaq5XFDqDIV/NOMPBoLrbxjLH42p4UzRuXHryXh9dYcKcw==",
"dev": true
},
"seroval-plugins": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/seroval-plugins/-/seroval-plugins-1.2.1.tgz",
"integrity": "sha512-H5vs53+39+x4Udwp4J5rNZfgFuA+Lt+uU+09w1gYBVWomtAl98B+E9w7yC05Xc81/HgLvJdlyqJbU0fJCKCmdw==",
"dev": true,
"requires": {}
},
"shebang-command": { "shebang-command": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
@ -2139,15 +2226,20 @@
"dev": true "dev": true
}, },
"solid-js": { "solid-js": {
"version": "1.3.5", "version": "1.9.5",
"resolved": "https://registry.npmjs.org/solid-js/-/solid-js-1.3.5.tgz", "resolved": "https://registry.npmjs.org/solid-js/-/solid-js-1.9.5.tgz",
"integrity": "sha512-PUom2cCARfvvgxI7cwOhfXMrZZZxjp+vIrb5fzVNBFyICy8A30wTqExwfUv457eJYgKpii2D3qStW9ILtKnShw==", "integrity": "sha512-ogI3DaFcyn6UhYhrgcyRAMbu/buBJitYQASZz5WzfQVPP10RD2AbCoRZ517psnezrasyCbWzIxZ6kVqet768xw==",
"dev": true "dev": true,
"requires": {
"csstype": "^3.1.0",
"seroval": "^1.1.0",
"seroval-plugins": "^1.1.0"
}
}, },
"source-map-js": { "source-map-js": {
"version": "1.0.2", "version": "1.2.1",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
"integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
"dev": true "dev": true
}, },
"supports-color": { "supports-color": {

View File

@ -99,8 +99,7 @@ public class Api : Pipeline
new ConcatDocuments(nameof(Code)), new ConcatDocuments(nameof(Code)),
new CacheDocuments( new CacheDocuments(
new AnalyzeCSharp() new AnalyzeCSharp()
.WhereNamespaces(ns => ns.StartsWith("Spectre.Console") && !ns.Contains("Analyzer") && .WhereNamespaces(ns => ns.StartsWith("Spectre.Console") && !ns.Contains("Analyzer") && !ns.Contains("Examples"))
!ns.Contains("Testing") && !ns.Contains("Examples"))
.WherePublic(true) .WherePublic(true)
.WithCssClasses("code", "cs") .WithCssClasses("code", "cs")
.WithDestinationPrefix("api") .WithDestinationPrefix("api")

View File

@ -15,7 +15,7 @@
<div id="container"> <div id="container">
<div id="console"> <div id="console">
<div class="line"><span style="color:var(--brightBlack)">╭─</span><span style="color:var(--folder)">&#xe0b2;</span><span style="background-color:var(--folder);color:var(--black)"> ~/spectre.console</span><span style="color:var(--folder);background-color:var(--dotnet)">&#xe0b0;</span><span style="background-color:var(--blue)"> .NET 8.0 </span><span style="color:var(--dotnet);background-color:var(--git)">&#xe0b0;</span><span style="background-color:var(--git);color:var(--background)"> &#xe0a0; main </span><span style="color:var(--git)">&#xe0b4;</span></div> <div class="line"><span style="color:var(--brightBlack)">╭─</span><span style="color:var(--folder)">&#xe0b2;</span><span style="background-color:var(--folder);color:var(--black)"> ~/spectre.console</span><span style="color:var(--folder);background-color:var(--dotnet)">&#xe0b0;</span><span style="background-color:var(--blue)"> .NET 9.0 </span><span style="color:var(--dotnet);background-color:var(--git)">&#xe0b0;</span><span style="background-color:var(--git);color:var(--background)"> &#xe0a0; main </span><span style="color:var(--git)">&#xe0b4;</span></div>
<div class="line"><span style="color:var(--brightBlack)">╰─</span> dotnet run</div> <div class="line"><span style="color:var(--brightBlack)">╰─</span> dotnet run</div>
<div class="line"></div> <div class="line"></div>
<div class="line">╭────────────────────────────────────────────────────────╮</div> <div class="line">╭────────────────────────────────────────────────────────╮</div>

View File

@ -58,21 +58,31 @@ internal static class HighlightService
} }
var text = await syntaxReference.SyntaxTree.GetTextAsync(); var text = await syntaxReference.SyntaxTree.GetTextAsync();
// we need a workspace, but it seems it is only used to resolve a few services and nothing else so an empty one will suffice
return HighlightElement(_emptyWorkspace, model, text, textSpan, indent); // we need a document for the syntax highlighter, so create a temporary solution and project to hold it.
var workspace = new AdhocWorkspace();
var solution = workspace.CurrentSolution
.AddProject("TempProject", "TempProject", "C#")
.AddDocument("TempDocument", await syntaxReference.SyntaxTree.GetTextAsync());
var document = solution.Project.Documents.First();
var highlightElement = await HighlightElement(document, text, textSpan, indent);
return highlightElement;
} }
private static int GetIndent(SyntaxTriviaList leadingTrivia) private static int GetIndent(SyntaxTriviaList leadingTrivia)
{ {
var whitespace = leadingTrivia.FirstOrDefault(i => i.Kind() == SyntaxKind.WhitespaceTrivia); var whitespace = leadingTrivia.FirstOrDefault(i => i.IsKind(SyntaxKind.WhitespaceTrivia));
return whitespace == default ? 0 : whitespace.Span.Length; return whitespace == default ? 0 : whitespace.Span.Length;
} }
private static string HighlightElement(Workspace workspace, SemanticModel semanticModel, SourceText fullSourceText, private static async Task<string> HighlightElement(Document document,
SourceText fullSourceText,
TextSpan textSpan, int indent) TextSpan textSpan, int indent)
{ {
var classifiedSpans = Classifier.GetClassifiedSpans(semanticModel, textSpan, workspace); var classifiedSpans = await Classifier.GetClassifiedSpansAsync(document, textSpan);
return HighlightElement(classifiedSpans, fullSourceText, indent); return HighlightElement(classifiedSpans, fullSourceText, indent);
} }

View File

@ -3,17 +3,11 @@
"isRoot": true, "isRoot": true,
"tools": { "tools": {
"cake.tool": { "cake.tool": {
"version": "4.0.0", "version": "5.0.0",
"commands": [ "commands": [
"dotnet-cake" "dotnet-cake"
] ]
}, },
"dotnet-example": {
"version": "3.1.0",
"commands": [
"dotnet-example"
]
},
"verify.tool": { "verify.tool": {
"version": "0.6.0", "version": "0.6.0",
"commands": [ "commands": [

View File

@ -1,15 +0,0 @@
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; }
}
}

View File

@ -1,17 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.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="..\..\Shared\Shared.csproj" />
</ItemGroup>
</Project>

View File

@ -1,61 +0,0 @@
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<BarSettings>("bar", Bar)
.WithDescription("Bars the foos");
config.AddAsyncDelegate("fooAsync", FooAsync)
.WithDescription("Foos the bars asynchronously");
config.AddAsyncDelegate<BarSettings>("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<int> FooAsync(CommandContext context)
{
AnsiConsole.WriteLine("Foo");
return Task.FromResult(0);
}
private static Task<int> BarAsync(CommandContext context, BarSettings settings)
{
for (var index = 0; index < settings.Count; index++)
{
AnsiConsole.WriteLine("Bar");
}
return Task.FromResult(0);
}
}

View File

@ -1,46 +0,0 @@
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<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

@ -1,29 +0,0 @@
using System.ComponentModel;
using Demo.Utilities;
using Spectre.Console.Cli;
namespace Demo.Commands.Add;
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

@ -1,11 +0,0 @@
using System.ComponentModel;
using Spectre.Console.Cli;
namespace Demo.Commands.Add;
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

@ -1,69 +0,0 @@
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<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

@ -1,40 +0,0 @@
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<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

@ -1,17 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.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.Cli\Spectre.Console.Cli.csproj" />
</ItemGroup>
</Project>

View File

@ -1,39 +0,0 @@
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<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("serve", "-o", "firefox")
.WithExample("serve", "--port", "80", "-o", "firefox");
});
return app.Run(args);
}
}

View File

@ -1,28 +0,0 @@
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);
}
}

View File

@ -1,53 +0,0 @@
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

@ -1,17 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.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="..\..\Shared\Shared.csproj" />
</ItemGroup>
</Project>

View File

@ -1,20 +0,0 @@
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;
}
}

View File

@ -1,23 +0,0 @@
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

@ -1,30 +0,0 @@
using System.Linq;
using Spectre.Console;
using Spectre.Console.Cli;
using Spectre.Console.Cli.Help;
using Spectre.Console.Rendering;
namespace Help;
/// <summary>
/// Example showing how to extend the built-in Spectre.Console help provider
/// by rendering a custom banner at the top of the help information
/// </summary>
internal class CustomHelpProvider : HelpProvider
{
public CustomHelpProvider(ICommandAppSettings settings)
: base(settings)
{
}
public override IEnumerable<IRenderable> GetHeader(ICommandModel model, ICommandInfo? command)
{
return new[]
{
new Text("--------------------------------------"), Text.NewLine,
new Text("--- CUSTOM HELP PROVIDER ---"), Text.NewLine,
new Text("--------------------------------------"), Text.NewLine,
Text.NewLine,
};
}
}

View File

@ -1,20 +0,0 @@
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;
}
}

View File

@ -1,18 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<ExampleName>Help</ExampleName>
<ExampleDescription>Demonstrates how to extend the built-in Spectre.Console help provider to render a custom banner at the top of the help information.</ExampleDescription>
<ExampleGroup>Cli</ExampleGroup>
<ExampleVisible>false</ExampleVisible>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\Shared\Shared.csproj" />
</ItemGroup>
</Project>

View File

@ -1,23 +0,0 @@
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<DefaultCommand>();
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);
}
}

View File

@ -1,29 +0,0 @@
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

@ -1,16 +0,0 @@
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}!");
}
}

View File

@ -1,40 +0,0 @@
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<object> func)
{
if (func is null)
{
throw new ArgumentNullException(nameof(func));
}
_builder.AddSingleton(service, (provider) => func());
}
}

View File

@ -1,32 +0,0 @@
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();
}
}
}

View File

@ -1,21 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.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="8.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Shared\Shared.csproj" />
</ItemGroup>
</Project>

View File

@ -1,23 +0,0 @@
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<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

@ -1,34 +0,0 @@
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

@ -1,55 +0,0 @@
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

@ -1,19 +0,0 @@
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;
}
}
}

View File

@ -1,37 +0,0 @@
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);
}
}

View File

@ -1,40 +0,0 @@
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<object> func)
{
if (func is null)
{
throw new ArgumentNullException(nameof(func));
}
_builder.AddSingleton(service, _ => func());
}
}

View File

@ -1,24 +0,0 @@
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);
}
}

View File

@ -1,26 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.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="8.0.0" />
<PackageReference Include="Serilog" Version="3.1.1" />
<PackageReference Include="Serilog.Extensions.Logging" Version="8.0.0" />
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
<PackageReference Include="Serilog.Sinks.Map" Version="1.0.2" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Shared\Shared.csproj" />
</ItemGroup>
</Project>

View File

@ -1,54 +0,0 @@
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<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>net8.0</TargetFramework>
<ExampleTitle>Screens</ExampleTitle>
<ExampleDescription>Demonstrates how to use alternate screens.</ExampleDescription>
<ExampleGroup>Widgets</ExampleGroup>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\Shared\Shared.csproj" />
</ItemGroup>
</Project>

View File

@ -1,26 +0,0 @@
// 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);
});

View File

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

View File

@ -1,86 +0,0 @@
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();
}
}

View File

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

View File

@ -1,18 +0,0 @@
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));
}
}

View File

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

View File

@ -1,43 +0,0 @@
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);
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

View File

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

View File

@ -1,42 +0,0 @@
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));
}
}

View File

@ -1,15 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.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="..\..\Shared\Shared.csproj" />
</ItemGroup>
</Project>

View File

@ -1,105 +0,0 @@
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));
}
}
}

View File

@ -1,19 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ExampleTitle>Columns</ExampleTitle>
<ExampleDescription>Demonstrates how to render data into columns.</ExampleDescription>
<ExampleGroup>Widgets</ExampleGroup>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Shared\Shared.csproj" />
</ItemGroup>
</Project>

View File

@ -1,30 +0,0 @@
using System.Collections.Generic;
using Spectre.Console;
namespace Columns;
public static class Program
{
public static void Main()
{
var cards = new List<Panel>();
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}[/]";
}
}

View File

@ -1,88 +0,0 @@
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<User> LoadUsers()
{
return new List<User>
{
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",
},
};
}
}

View File

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

View File

@ -1,19 +0,0 @@
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");
}
}

View File

@ -1,15 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ExampleTitle>Decorations</ExampleTitle>
<ExampleDescription>Demonstrates how to [italic]use[/] [bold]decorations[/] [dim]in[/] the console.</ExampleDescription>
<ExampleGroup>Misc</ExampleGroup>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\Shared\Shared.csproj" />
</ItemGroup>
</Project>

View File

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

View File

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

View File

@ -1,23 +0,0 @@
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());
}
}

View File

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

View File

@ -1,101 +0,0 @@
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<string>();
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<int>(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<string> result)
{
try
{
CheckCredentials(foo, bar);
result = new List<string>();
}
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>(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<bool> CheckCredentialsAsync<T>(List<T> qux, T[] otherArray, string[,] corgi)
{
await Task.Delay(0);
throw new InvalidCredentialException("The credentials are invalid.");
}
}

View File

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

View File

@ -1,13 +0,0 @@
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));
}
}

View File

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

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