diff --git a/.github/workflows/build-validation.yml b/.github/workflows/build-validation.yml
index e88baa6a05..a136aa68c8 100644
--- a/.github/workflows/build-validation.yml
+++ b/.github/workflows/build-validation.yml
@@ -20,6 +20,8 @@ jobs:
- name: Checkout code
uses: actions/checkout@v6
+ with:
+ fetch-depth: 0 # GitVersion.MsBuild needs full history
- name: Setup .NET Core
uses: actions/setup-dotnet@v5
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
index ff3f13d3e0..55277f0aa6 100644
--- a/.github/workflows/codeql-analysis.yml
+++ b/.github/workflows/codeql-analysis.yml
@@ -32,9 +32,7 @@ jobs:
- name: Checkout repository
uses: actions/checkout@v6
with:
- # We must fetch at least the immediate parents so that if this is
- # a pull request then we can checkout the head.
- fetch-depth: 2
+ fetch-depth: 0 # GitVersion.MsBuild needs full history
# If this run was triggered by a pull request event, then checkout
# the head of the pull request instead of the merge commit.
diff --git a/.github/workflows/finalize-release.yml b/.github/workflows/finalize-release.yml
new file mode 100644
index 0000000000..1ae78d9b74
--- /dev/null
+++ b/.github/workflows/finalize-release.yml
@@ -0,0 +1,120 @@
+name: Finalize Release
+
+# Runs automatically when a release PR is merged into main.
+# Creates the git tag, GitHub Release, and opens a back-merge PR to develop.
+
+on:
+ pull_request:
+ types: [closed]
+ branches: [main]
+
+jobs:
+ finalize-release:
+ name: Tag, Release, and Back-merge
+ # Only run when a release PR is actually merged (not just closed)
+ if: github.event.pull_request.merged == true && startsWith(github.event.pull_request.head.ref, 'release/')
+ runs-on: ubuntu-latest
+ permissions:
+ contents: write
+ pull-requests: write
+
+ steps:
+ - name: Checkout main
+ uses: actions/checkout@v6
+ with:
+ ref: main
+ fetch-depth: 0
+ token: ${{ secrets.GITHUB_TOKEN }}
+
+ - name: Configure Git
+ run: |
+ git config user.name "github-actions[bot]"
+ git config user.email "github-actions[bot]@users.noreply.github.com"
+
+ - name: Extract release info from branch name
+ id: release
+ run: |
+ BRANCH="${{ github.event.pull_request.head.ref }}"
+ # Branch name is release/vX.Y.Z or release/vX.Y.Z-beta
+ TAG="${BRANCH#release/}"
+ SEMVER="${TAG#v}"
+
+ # Determine if this is a pre-release
+ if echo "$SEMVER" | grep -q '-'; then
+ PRERELEASE="true"
+ else
+ PRERELEASE="false"
+ fi
+
+ echo "tag=${TAG}" >> $GITHUB_OUTPUT
+ echo "semver=${SEMVER}" >> $GITHUB_OUTPUT
+ echo "prerelease=${PRERELEASE}" >> $GITHUB_OUTPUT
+
+ - name: Check if tag already exists
+ run: |
+ if git rev-parse "${{ steps.release.outputs.tag }}" >/dev/null 2>&1; then
+ echo "::error::Tag ${{ steps.release.outputs.tag }} already exists. Skipping."
+ exit 1
+ fi
+
+ - name: Create annotated tag
+ run: |
+ TAG="${{ steps.release.outputs.tag }}"
+ git tag -a "$TAG" -m "Release $TAG"
+ git push origin "$TAG"
+ echo "Created and pushed tag: $TAG"
+
+ - name: Create GitHub Release
+ env:
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ TAG: ${{ steps.release.outputs.tag }}
+ SEMVER: ${{ steps.release.outputs.semver }}
+ PRERELEASE: ${{ steps.release.outputs.prerelease }}
+ run: |
+ PRERELEASE_FLAG=""
+ if [ "$PRERELEASE" = "true" ]; then
+ PRERELEASE_FLAG="--prerelease"
+ fi
+
+ gh release create "$TAG" \
+ --title "$TAG" \
+ --generate-notes \
+ $PRERELEASE_FLAG
+
+ - name: Delete release branch
+ run: |
+ BRANCH="${{ github.event.pull_request.head.ref }}"
+ git push origin --delete "$BRANCH" || true
+ echo "Deleted branch: $BRANCH"
+
+ - name: Create back-merge PR (main → develop)
+ env:
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ TAG: ${{ steps.release.outputs.tag }}
+ run: |
+ BACKMERGE_BRANCH="backmerge/${TAG}"
+
+ # Create a branch from main for the back-merge
+ git checkout -b "$BACKMERGE_BRANCH" main
+ git push origin "$BACKMERGE_BRANCH"
+
+ # Create the PR
+ gh pr create \
+ --base develop \
+ --head "$BACKMERGE_BRANCH" \
+ --title "Back-merge ${TAG} from main into develop" \
+ --body "Automatic back-merge after release ${TAG}. Merge this to keep \`develop\` in sync with \`main\`."
+
+ - name: Summary
+ run: |
+ echo "## Release Finalized :rocket:" >> $GITHUB_STEP_SUMMARY
+ echo "" >> $GITHUB_STEP_SUMMARY
+ echo "- **Tag:** ${{ steps.release.outputs.tag }}" >> $GITHUB_STEP_SUMMARY
+ echo "- **Version:** ${{ steps.release.outputs.semver }}" >> $GITHUB_STEP_SUMMARY
+ echo "- **Pre-release:** ${{ steps.release.outputs.prerelease }}" >> $GITHUB_STEP_SUMMARY
+ echo "" >> $GITHUB_STEP_SUMMARY
+ echo "### What happened" >> $GITHUB_STEP_SUMMARY
+ echo "1. ✅ Tag \`${{ steps.release.outputs.tag }}\` created" >> $GITHUB_STEP_SUMMARY
+ echo "2. ✅ GitHub Release created with auto-generated notes" >> $GITHUB_STEP_SUMMARY
+ echo "3. ✅ Publish workflow triggered (NuGet)" >> $GITHUB_STEP_SUMMARY
+ echo "4. ✅ Back-merge PR opened to develop" >> $GITHUB_STEP_SUMMARY
diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml
index 49848aee8a..3514c44a3f 100644
--- a/.github/workflows/integration-tests.yml
+++ b/.github/workflows/integration-tests.yml
@@ -24,6 +24,8 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v6
+ with:
+ fetch-depth: 0 # GitVersion.MsBuild needs full history
- name: Setup .NET Core
uses: actions/setup-dotnet@v5
diff --git a/.github/workflows/prepare-release.yml b/.github/workflows/prepare-release.yml
new file mode 100644
index 0000000000..3adcdca274
--- /dev/null
+++ b/.github/workflows/prepare-release.yml
@@ -0,0 +1,184 @@
+name: Prepare Release
+
+# Creates a release PR from develop → main.
+# Maintainers trigger this from Actions → Prepare Release → Run workflow.
+# After reviewing, merge the PR — the "Finalize Release" workflow handles the rest.
+
+on:
+ workflow_dispatch:
+ inputs:
+ release_type:
+ description: 'Release type (controls the pre-release label on main)'
+ required: true
+ type: choice
+ options:
+ - beta
+ - rc
+ - stable
+ default: 'beta'
+ version_override:
+ description: 'Version override (optional, e.g., 2.0.0). Leave blank to let GitVersion calculate it.'
+ required: false
+ type: string
+
+jobs:
+ prepare-release:
+ name: Create Release PR
+ runs-on: ubuntu-latest
+ permissions:
+ contents: write
+ pull-requests: write
+
+ steps:
+ - name: Checkout develop
+ uses: actions/checkout@v6
+ with:
+ ref: develop
+ fetch-depth: 0
+ token: ${{ secrets.GITHUB_TOKEN }}
+
+ - name: Configure Git
+ run: |
+ git config user.name "github-actions[bot]"
+ git config user.email "github-actions[bot]@users.noreply.github.com"
+
+ - name: Install GitVersion
+ uses: gittools/actions/gitversion/setup@v4.5.0
+ with:
+ versionSpec: '6.x'
+
+ - name: Determine Version
+ uses: gittools/actions/gitversion/execute@v4.5.0
+ with:
+ useConfigFile: true
+ id: gitversion
+
+ - name: Compute release version
+ id: version
+ run: |
+ if [ -n "${{ github.event.inputs.version_override }}" ]; then
+ VERSION="${{ github.event.inputs.version_override }}"
+ else
+ VERSION="${{ steps.gitversion.outputs.MajorMinorPatch }}"
+ fi
+
+ RELEASE_TYPE="${{ github.event.inputs.release_type }}"
+
+ if [ "$RELEASE_TYPE" = "stable" ]; then
+ SEMVER="${VERSION}"
+ TAG="v${VERSION}"
+ else
+ SEMVER="${VERSION}-${RELEASE_TYPE}"
+ TAG="v${VERSION}-${RELEASE_TYPE}"
+ fi
+
+ echo "version=${VERSION}" >> $GITHUB_OUTPUT
+ echo "semver=${SEMVER}" >> $GITHUB_OUTPUT
+ echo "tag=${TAG}" >> $GITHUB_OUTPUT
+ echo "release_type=${RELEASE_TYPE}" >> $GITHUB_OUTPUT
+
+ - name: Check if tag already exists
+ run: |
+ if git rev-parse "${{ steps.version.outputs.tag }}" >/dev/null 2>&1; then
+ echo "::error::Tag ${{ steps.version.outputs.tag }} already exists."
+ echo "::error::Choose a different version or delete the existing tag first."
+ exit 1
+ fi
+
+ - name: Create release branch
+ run: |
+ BRANCH="release/${{ steps.version.outputs.tag }}"
+ git checkout -b "$BRANCH"
+ echo "branch=${BRANCH}" >> $GITHUB_OUTPUT
+ id: branch
+
+ - name: Update GitVersion.yml label for release type
+ run: |
+ RELEASE_TYPE="${{ steps.version.outputs.release_type }}"
+
+ if [ "$RELEASE_TYPE" = "stable" ]; then
+ NEW_LABEL=" label: ''"
+ else
+ NEW_LABEL=" label: ${RELEASE_TYPE}"
+ fi
+
+ # Update the label line that follows the main branch regex.
+ # The main section has 'regex: ^main$' followed by a comment then 'label: ...'
+ # Use sed to replace the label line after the main regex match.
+ sed -i "/regex: \^main/,/^ [a-z]/{s/^ label: .*/ label: ${RELEASE_TYPE}/}" GitVersion.yml
+
+ # For stable, label must be empty string
+ if [ "$RELEASE_TYPE" = "stable" ]; then
+ sed -i "/regex: \^main/,/^ [a-z]/{s/^ label: .*/ label: ''/}" GitVersion.yml
+ fi
+
+ echo "Updated GitVersion.yml main label to: '${RELEASE_TYPE}'"
+ grep -A 5 "^ main:" GitVersion.yml
+
+ - name: Commit label change
+ run: |
+ if git diff --quiet GitVersion.yml; then
+ echo "No label change needed"
+ else
+ git add GitVersion.yml
+ git commit -m "Set release label to '${{ steps.version.outputs.release_type }}' for ${{ steps.version.outputs.tag }}"
+ fi
+
+ - name: Push release branch
+ run: |
+ BRANCH="release/${{ steps.version.outputs.tag }}"
+ git push origin "$BRANCH"
+
+ - name: Create Pull Request
+ env:
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ SEMVER: ${{ steps.version.outputs.semver }}
+ TAG: ${{ steps.version.outputs.tag }}
+ RELEASE_TYPE: ${{ steps.version.outputs.release_type }}
+ run: |
+ BRANCH="release/${TAG}"
+
+ if [ "$RELEASE_TYPE" = "stable" ]; then
+ PRERELEASE_NOTE=""
+ else
+ PRERELEASE_NOTE="This is a **${RELEASE_TYPE}** pre-release."
+ fi
+
+ cat > /tmp/pr_body.md << EOF
+ ## Release ${TAG}
+
+ ${PRERELEASE_NOTE}
+
+ **Version:** \`${SEMVER}\`
+ **NuGet Package:** \`Terminal.Gui ${SEMVER}\`
+
+ ### What happens when this PR is merged
+
+ 1. ✅ The **Finalize Release** workflow will automatically create tag \`${TAG}\`
+ 2. ✅ The **Publish** workflow will build and push to [NuGet.org](https://www.nuget.org/packages/Terminal.Gui)
+ 3. ✅ A **GitHub Release** will be created with auto-generated notes
+ 4. ✅ A **back-merge PR** from \`main\` → \`develop\` will be opened
+
+ ### Checklist
+
+ - [ ] CI passes on this PR
+ - [ ] Version looks correct: \`${SEMVER}\`
+ - [ ] Release notes reviewed (will be auto-generated on merge)
+ EOF
+
+ gh pr create \
+ --base main \
+ --head "$BRANCH" \
+ --title "Release ${TAG}" \
+ --body-file /tmp/pr_body.md
+
+ - name: Summary
+ run: |
+ echo "## Release PR Created :rocket:" >> $GITHUB_STEP_SUMMARY
+ echo "" >> $GITHUB_STEP_SUMMARY
+ echo "- **Version:** ${{ steps.version.outputs.semver }}" >> $GITHUB_STEP_SUMMARY
+ echo "- **Tag:** ${{ steps.version.outputs.tag }}" >> $GITHUB_STEP_SUMMARY
+ echo "- **Type:** ${{ steps.version.outputs.release_type }}" >> $GITHUB_STEP_SUMMARY
+ echo "- **Branch:** release/${{ steps.version.outputs.tag }}" >> $GITHUB_STEP_SUMMARY
+ echo "" >> $GITHUB_STEP_SUMMARY
+ echo "Review and merge the PR to trigger the release." >> $GITHUB_STEP_SUMMARY
diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml
index 7f7e5abd17..186c66b5a1 100644
--- a/.github/workflows/publish.yml
+++ b/.github/workflows/publish.yml
@@ -19,6 +19,9 @@ jobs:
with:
fetch-depth: 0 # fetch-depth is needed for GitVersion https://github.com/GitTools/actions/blob/main/docs/cloning.md
+ # GitVersion.MsBuild (in Terminal.Gui.csproj) automatically computes the version
+ # from git history + GitVersion.yml during build. We still run the CLI here to
+ # capture the SemVer for use in subsequent steps (push, template dispatch).
- name: Install GitVersion
uses: gittools/actions/gitversion/setup@v4.5.0
with:
@@ -28,7 +31,6 @@ jobs:
uses: gittools/actions/gitversion/execute@v4.5.0
with:
useConfigFile: true
- updateAssemblyInfo: true
id: gitversion # step id used as reference for output values
- name: Setup dotnet
@@ -41,7 +43,7 @@ jobs:
run: dotnet build Terminal.Gui/Terminal.Gui.csproj --no-incremental --nologo --force --configuration Release
- name: Pack Release Terminal.Gui ${{ steps.gitversion.outputs.SemVer }} for Nuget
- run: dotnet pack Terminal.Gui/Terminal.Gui.csproj -c Release --include-symbols -p:Version='${{ steps.gitversion.outputs.SemVer }}'
+ run: dotnet pack Terminal.Gui/Terminal.Gui.csproj -c Release --include-symbols --no-build
# - name: Test to generate Code Coverage Report
# run: |
diff --git a/.github/workflows/stress-tests.yml b/.github/workflows/stress-tests.yml
index 0660308e30..481741c6fd 100644
--- a/.github/workflows/stress-tests.yml
+++ b/.github/workflows/stress-tests.yml
@@ -22,6 +22,8 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v6
+ with:
+ fetch-depth: 0 # GitVersion.MsBuild needs full history
- name: Setup .NET Core
uses: actions/setup-dotnet@v5
@@ -65,6 +67,8 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v6
+ with:
+ fetch-depth: 0 # GitVersion.MsBuild needs full history
- name: Setup .NET Core
uses: actions/setup-dotnet@v5
diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml
index b466926f52..a73ca887de 100644
--- a/.github/workflows/unit-tests.yml
+++ b/.github/workflows/unit-tests.yml
@@ -26,6 +26,8 @@ jobs:
- name: Checkout code
uses: actions/checkout@v6
+ with:
+ fetch-depth: 0 # GitVersion.MsBuild needs full history
- name: Setup .NET Core
uses: actions/setup-dotnet@v5
@@ -84,6 +86,8 @@ jobs:
- name: Checkout code
uses: actions/checkout@v6
+ with:
+ fetch-depth: 0 # GitVersion.MsBuild needs full history
- name: Setup .NET Core
uses: actions/setup-dotnet@v5
diff --git a/Directory.Packages.props b/Directory.Packages.props
index 6718b0de22..43bd0c219f 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -47,6 +47,7 @@
+
diff --git a/Examples/AI/AI.csproj b/Examples/AI/AI.csproj
index 751ecad327..6361cd36df 100644
--- a/Examples/AI/AI.csproj
+++ b/Examples/AI/AI.csproj
@@ -1,10 +1,6 @@
Exe
- 2.0
- 2.0
- 2.0
- 2.0
enable
latest
diff --git a/Examples/Example/Example.csproj b/Examples/Example/Example.csproj
index a734c759e3..ed4cca28a1 100644
--- a/Examples/Example/Example.csproj
+++ b/Examples/Example/Example.csproj
@@ -1,13 +1,6 @@
Exe
-
-
-
- 2.0
- 2.0
- 2.0
- 2.0
enable
latest
diff --git a/Examples/InlineCLI/InlineCLI.csproj b/Examples/InlineCLI/InlineCLI.csproj
index d5931053e5..963226eeae 100644
--- a/Examples/InlineCLI/InlineCLI.csproj
+++ b/Examples/InlineCLI/InlineCLI.csproj
@@ -1,13 +1,6 @@
Exe
-
-
-
- 2.0
- 2.0
- 2.0
- 2.0
enable
latest
diff --git a/Examples/InlineColorPicker/InlineColorPicker.csproj b/Examples/InlineColorPicker/InlineColorPicker.csproj
index 38c47ab3fc..46d92f7faf 100644
--- a/Examples/InlineColorPicker/InlineColorPicker.csproj
+++ b/Examples/InlineColorPicker/InlineColorPicker.csproj
@@ -1,13 +1,6 @@
Exe
-
-
-
- 2.0
- 2.0
- 2.0
- 2.0
enable
latest
diff --git a/Examples/InlineSelect/InlineSelect.csproj b/Examples/InlineSelect/InlineSelect.csproj
index 38c47ab3fc..46d92f7faf 100644
--- a/Examples/InlineSelect/InlineSelect.csproj
+++ b/Examples/InlineSelect/InlineSelect.csproj
@@ -1,13 +1,6 @@
Exe
-
-
-
- 2.0
- 2.0
- 2.0
- 2.0
enable
latest
diff --git a/Examples/README.md b/Examples/README.md
new file mode 100644
index 0000000000..0f6aa383ff
--- /dev/null
+++ b/Examples/README.md
@@ -0,0 +1,3 @@
+# Terminal.Gui Examples
+
+This directory contains example applications demonstrating Terminal.Gui. Each example has its own README with usage details and documentation.
diff --git a/Examples/ReactiveExample/ReactiveExample.csproj b/Examples/ReactiveExample/ReactiveExample.csproj
index f639a03d79..e3052b9b61 100644
--- a/Examples/ReactiveExample/ReactiveExample.csproj
+++ b/Examples/ReactiveExample/ReactiveExample.csproj
@@ -1,13 +1,6 @@
Exe
-
-
-
- 2.0
- 2.0
- 2.0
- 2.0
diff --git a/Examples/ScenarioRunner/ScenarioRunner.csproj b/Examples/ScenarioRunner/ScenarioRunner.csproj
index 1030734925..b9f3a1dfef 100644
--- a/Examples/ScenarioRunner/ScenarioRunner.csproj
+++ b/Examples/ScenarioRunner/ScenarioRunner.csproj
@@ -1,13 +1,6 @@
Exe
-
-
-
- 2.0
- 2.0
- 2.0
- 2.0
TRACE
diff --git a/Examples/UICatalog/README.md b/Examples/UICatalog/README.md
index 042bc8a945..007733bd8c 100644
--- a/Examples/UICatalog/README.md
+++ b/Examples/UICatalog/README.md
@@ -1,92 +1,77 @@
# Terminal.Gui UI Catalog
-UI Catalog is a comprehensive sample library for Terminal.Gui. It attempts to satisfy the following goals:
+UI Catalog is a comprehensive sample library for Terminal.Gui. It provides:
-1. Be an easy-to-use showcase for Terminal.Gui concepts and features.
-2. Provide sample code that illustrates how to properly implement
-said concepts & features.
-3. Make it easy for contributors to add additional samples in a structured way.
+1. An easy-to-use, interactive, showcase for Terminal.Gui concepts and features.
+2. Sample code that illustrates how to properly implement said concepts & features.
+3. A structured way for contributors to add additional samples.
-
-
-## Motivation
-
-The original `demo.cs` sample app for Terminal.Gui is neither good to showcase, nor does it explain different concepts. In addition, because it is built on a single source file, it has proven to cause friction when multiple contributors are simultaneously working on different aspects of Terminal.Gui.
-See [Issue #368](https://github.com/giu-cs/Terminal.Gui/issues/368) for more background.
+
## How To Use
-Build and run UI Catalog by typing `dotnet run` from the `UI Catalog` folder or by using the `Terminal.Gui` Visual Studio solution.
-
-`Program.cs` is the main **UI Catalog** app and provides a UI for selecting and running **Scenarios**. Each **Scenario* is implemented as a class derived from `Scenario` and `Program.cs` uses reflection to dynamically build the UI.
-
-**Scenarios** are tagged with categories using the `[ScenarioCategory]` attribute. The left pane of the main screen lists the categories. Clicking on a category shows all the scenarios in that category.
-
-**Scenarios** can be run either from the **UICatalog.exe** app UI or by being specified on the command line:
-
```
-UICatalog.exe
+dotnet run --project ./Examples/UICatalog/UICatalog.csproj
```
-e.g.
+or
```
-UICatalog.exe Buttons
+dotnet build
+./Examples/UICatalog/bin/Debug/net10.0/UICatalog.exe
```
-Hitting ENTER on a selected Scenario or double-clicking on a Scenario runs that scenario as though it were a stand-alone Terminal.Gui app.
-
-When a **Scenario** is run, it runs as though it were a standalone `Terminal.Gui` app. However, scaffolding is provided (in the `Scenario` base class) that (optionally) takes care of `Terminal.Gui` initialization.
+## Implementing a Scenario
-## Contributing by Adding Scenarios
+**Scenarios** are tagged with categories using the `[ScenarioCategory]` attribute. The left pane of the main screen lists the categories. Clicking on a category shows all the scenarios in that category.
-To add a new **Scenario** simply:
+To add a new **Scenario**:
-1. Create a new `.cs` file in the `Scenarios` directory that derives from `Scenario`.
-2. Add a `[ScenarioMetaData]` attribute to the class specifying the scenario's name and description.
-3. Add one or more `[ScenarioCategory]` attributes to the class specifying which categories the sceanrio belongs to. If you don't specify a category the sceanrio will show up in "All".
+1. Create a new `.cs` file in `./Examples/UICatalog/Scenarios` that derives from `Scenario`.
+2. Add a `[ScenarioMetaData]` attribute specifying the scenario's name and description.
+3. Add one or more `[ScenarioCategory]` attributes specifying which categories the scenario belongs to. If you don't specify a category, the scenario will show up in "All".
4. Implement the `Setup` override which will be called when a user selects the scenario to run.
5. Optionally, implement the `Init` and/or `Run` overrides to provide a custom implementation.
-The sample below is provided in the `.\UICatalog\Scenarios` directory as a generic sample that can be copied and re-named:
-
-```csharp
-
-namespace UICatalog {
- [ScenarioMetadata (Name: "Generic", Description: "Generic sample - A template for creating new Scenarios")]
- [ScenarioCategory ("Controls")]
- class MyScenario : Scenario {
- public override void Setup ()
- {
- // Put your scenario code here, e.g.
- Win.Add (new Button () {
-Text = "Press me!",
- X = Pos.Center (),
- Y = Pos.Center (),
- Clicked = () => MessageBox.Query (20, 7, "Hi", "Neat?", Strings.btnNo, Strings.btnYes)
- });
- }
- }
-}
-```
+See `./Examples/UICatalog/Scenarios/Generic.cs` for a starting point.
+
+### Contribution Guidelines
-`Scenario` provides `Win`, a `Window` object that provides a canvas for the Scenario to operate.
+- Provide a terse, descriptive `Name` for `Scenarios`. Keep them short.
+- Provide a clear `Description`.
+- Comment `Scenario` code to describe to others why it's a useful `Scenario`.
+- Annotate `Scenarios` with `[ScenarioCategory]` attributes. Minimize the number of new categories created.
-The default `Window` shows the Scenario name and supports exiting the Scenario through the `Esc` key.
-
+## Command Line Arguments
-To build a more advanced scenario, where control of the `Runnable` and `Window` is needed (e.g. for scenarios using `MenuBar` or `StatusBar`), simply use `Application.Top` per normal Terminal.Gui programming, as seen in the `Notepad` scenario.
+Usage: `UICatalog [] [options]`
-For complete control, the `Init` and `Run` overrides can be implemented. The `base.Init` creates `Win`. The `base.Run` simply calls `Application.Run(Application.Top)`.
+| Option | Description |
+|--------|-------------|
+| `` | The name of the Scenario to run. If not provided, the interactive UI will be shown. |
+| `-dl`, `--debug-log-level` | The log level (`Trace`, `Debug`, `Information`, `Warning`, `Error`, `Critical`, `None`). Default: `Warning`. |
+| `-b`, `--benchmark` | Enables benchmarking. If a Scenario is specified, just that Scenario will be benchmarked. |
+| `-t`, `--timeout ` | Max time in milliseconds per benchmark. Default: `2500`. |
+| `-f`, `--file ` | File to save benchmark results to. If omitted, results are displayed in a `TableView`. |
+| `-d`, `--driver ` | The `IDriver` to use (`ansi`, `dotnet`, `windows`). |
+| `-dcm`, `--disable-cm` | Disables Configuration Management. Only `ConfigLocations.HardCoded` settings will be loaded. |
+| `-16`, `--force-16-colors` | Forces 16-color mode instead of TrueColor. |
+| `--help` | Show help (renders this README as formatted markdown). |
+| `--version` | Show version information. |
-## Contribution Guidelines
+### Examples
-- Provide a terse, descriptive `Name` for `Scenarios`. Keep them short.
-- Provide a clear `Description`.
-- Comment `Scenario` code to describe to others why it's a useful `Scenario`.
-- Annotate `Scenarios` with `[ScenarioCategory]` attributes. Minimize the number of new categories created.
-- Use the `Bug Repo` Category for `Scenarios` that reproduce bugs.
- - Include the Github Issue # in the Description.
- - Once the bug has been fixed in `develop` submit another PR to remove the `Scenario` (or modify it to provide a good regression test/sample).
-- Tag bugs or suggestions for `UI Catalog` as [`Terminal.Gui` Github Issues](https://github.com/gui-cs/Terminal.Gui/issues) with "UICatalog: ".
+```bash
+# Show formatted help
+./Examples/UICatalog/bin/Debug/net10.0/UICatalog.exe --help
+
+# Run a specific scenario
+./Examples/UICatalog/bin/Debug/net10.0/UICatalog.exe "All Views Tester"
+
+# Benchmark all scenarios
+./Examples/UICatalog/bin/Debug/net10.0/UICatalog.exe --benchmark
+
+# Run with a specific driver and debug logging
+./Examples/UICatalog/bin/Debug/net10.0/UICatalog.exe -d ansi -dl Debug
+```
\ No newline at end of file
diff --git a/Examples/UICatalog/UICatalog.cs b/Examples/UICatalog/UICatalog.cs
index 1945190ddf..b7ea221c03 100644
--- a/Examples/UICatalog/UICatalog.cs
+++ b/Examples/UICatalog/UICatalog.cs
@@ -12,6 +12,7 @@
global using Terminal.Gui.Resources;
using System.CommandLine;
using System.CommandLine.Help;
+using System.CommandLine.Invocation;
using System.CommandLine.Parsing;
using System.Diagnostics;
using System.Globalization;
@@ -22,6 +23,7 @@
using Serilog;
using Serilog.Core;
using Serilog.Events;
+using TextMateSharp.Grammars;
using ILogger = Microsoft.Extensions.Logging.ILogger;
#nullable enable
@@ -175,7 +177,7 @@ private static int Main (string [] args)
DontEnableConfigurationManagement = context.GetRequiredValue (disableConfigManagement),
Benchmark = context.GetRequiredValue (benchmarkFlag),
BenchmarkTimeout = context.GetRequiredValue (benchmarkTimeout),
- ResultsFile = context.GetRequiredValue (resultsFile) ?? string.Empty,
+ ResultsFile = context.GetRequiredValue (resultsFile),
DebugLogLevel = context.GetRequiredValue (debugLogLevel),
// Only set Force16Colors if explicitly specified on command line
@@ -186,31 +188,52 @@ private static int Main (string [] args)
Options = options;
});
- var helpShown = false;
+ // Capture terminal dimensions before any driver changes them
+ int terminalWidth;
+ int terminalHeight;
- ParseResult parseResult = rootCommand.Parse (args);
-
- // Check if the analysis results indicate that help should be displayed
- if (parseResult.Errors.Count == 0 && parseResult.Action is HelpAction)
+ try
{
- helpShown = true;
+ terminalWidth = Console.WindowWidth;
+ terminalHeight = Console.WindowHeight;
}
-
- parseResult.Invoke ();
-
- if (helpShown)
+ catch (IOException)
{
- return 0;
+ // Console dimensions unavailable (e.g. output is piped)
+ terminalWidth = 120;
+ terminalHeight = 40;
}
+ // Override --help to render the embedded README.md as formatted markdown
+ HelpOption helpOption = rootCommand.Options.OfType ().First ();
+ helpOption.Action = new MarkdownHelpAction (() => RenderMarkdown (ReadEmbeddedReadme (), terminalWidth, terminalHeight));
+
+ // Override --version to show the Terminal.Gui library version
+ VersionOption versionOption = rootCommand.Options.OfType ().First ();
+ versionOption.Action = new VersionAction (() => Console.WriteLine (GetLibraryVersion ()));
+
+ ParseResult parseResult = rootCommand.Parse (args);
+
+ // If there are parse errors, show README then print diagnostics underneath
if (parseResult.Errors.Count > 0)
{
+ RenderMarkdown (ReadEmbeddedReadme (), terminalWidth, terminalHeight);
+
foreach (ParseError error in parseResult.Errors)
{
Console.Error.WriteLine (error.Message);
}
- return 1; // Non-zero exit code for error
+ return 1;
+ }
+
+ parseResult.Invoke ();
+
+ // If our SetAction callback wasn't invoked (--help, --version, etc.),
+ // Options won't have been initialized — return early.
+ if (Options.DebugLogLevel is null)
+ {
+ return 0;
}
Scenario.BenchmarkTimeout = Options.BenchmarkTimeout;
@@ -324,4 +347,104 @@ private static void UICatalogMain (UICatalogCommandLineOptions options)
runner.RunInteractive (!Options.DontEnableConfigurationManagement);
}
+
+ ///
+ /// Renders markdown content to the terminal using the ANSI driver and exits.
+ ///
+ private static void RenderMarkdown (string markdown, int width, int height)
+ {
+ // Prevent the ANSI driver from trying to read/write real terminal size or capabilities
+ Environment.SetEnvironmentVariable ("DisableRealDriverIO", "1");
+ IApplication app = Application.Create ();
+ app.Init (DriverRegistry.Names.ANSI);
+
+ app.Driver?.SetScreenSize (width, height);
+
+ Markdown markdownView = new ()
+ {
+ App = app,
+ UseThemeBackground = true,
+ ShowCopyButtons = false,
+ Width = Dim.Fill (),
+ Height = Dim.Fill (),
+ SyntaxHighlighter = new TextMateSyntaxHighlighter (ThemeName.Dracula),
+ Text = markdown
+ };
+
+ // Layout to get natural content height
+ markdownView.SetRelativeLayout (app.Screen.Size);
+ markdownView.Layout ();
+
+ // Resize to the full content height but keep the terminal width
+ int contentHeight = markdownView.GetContentHeight ();
+ app.Driver?.SetScreenSize (width, contentHeight);
+ markdownView.SetRelativeLayout (app.Screen.Size);
+
+ markdownView.Frame = app.Screen with { X = 0, Y = 0 };
+ markdownView.Layout ();
+
+ app.Driver?.ClearContents ();
+ markdownView.Draw ();
+ Console.WriteLine (app.Driver?.ToAnsi ());
+ }
+
+ ///
+ /// Gets the Terminal.Gui library version from its assembly metadata.
+ ///
+ public static string GetLibraryVersion ()
+ {
+ Assembly libAssembly = typeof (View).Assembly;
+
+ string? informationalVersion = libAssembly.GetCustomAttribute ()?.InformationalVersion;
+
+ if (informationalVersion is { })
+ {
+ // Strip build metadata (everything after '+') for a terse SemVer
+ int plusIndex = informationalVersion.IndexOf ('+');
+
+ return plusIndex >= 0 ? informationalVersion [..plusIndex] : informationalVersion;
+ }
+
+ return libAssembly.GetName ().Version?.ToString () ?? "unknown";
+ }
+
+ ///
+ /// Reads the embedded README.md resource from the assembly.
+ ///
+ private static string ReadEmbeddedReadme ()
+ {
+ Assembly assembly = Assembly.GetExecutingAssembly ();
+ string resourceName = assembly.GetManifestResourceNames ().First (n => n.EndsWith ("README.md", StringComparison.Ordinal));
+
+ using Stream stream = assembly.GetManifestResourceStream (resourceName)!;
+ using StreamReader reader = new (stream);
+
+ return reader.ReadToEnd ();
+ }
+}
+
+///
+/// Custom help action that renders the embedded README.md as formatted markdown.
+///
+internal sealed class MarkdownHelpAction (Action renderHelp) : SynchronousCommandLineAction
+{
+ public override int Invoke (ParseResult parseResult)
+ {
+ renderHelp ();
+
+ return 0;
+ }
+}
+
+///
+/// Custom version action that displays the Terminal.Gui library version.
+///
+internal sealed class VersionAction (Action printVersion) : SynchronousCommandLineAction
+{
+ public override int Invoke (ParseResult parseResult)
+ {
+ printVersion ();
+
+ return 0;
+ }
}
diff --git a/Examples/UICatalog/UICatalog.csproj b/Examples/UICatalog/UICatalog.csproj
index 88e916f9cf..1dc8d9f303 100644
--- a/Examples/UICatalog/UICatalog.csproj
+++ b/Examples/UICatalog/UICatalog.csproj
@@ -2,13 +2,7 @@
UICatalog.UICatalog
Exe
-
-
-
- 2.0
- 2.0
- 2.0
- 2.0
+
Linux
@@ -22,6 +16,7 @@
+
diff --git a/Examples/UICatalog/UICatalogRunnable.cs b/Examples/UICatalog/UICatalogRunnable.cs
index aa57f3599f..4dc1b1137b 100644
--- a/Examples/UICatalog/UICatalogRunnable.cs
+++ b/Examples/UICatalog/UICatalogRunnable.cs
@@ -913,7 +913,7 @@ private void ShowAboutDialog ()
{
Width = Dim.Auto (),
Height = Dim.Auto (),
- Text = "v2 - Beta",
+ Text = $"Terminal.Gui {UICatalog.GetLibraryVersion ()}",
X = Pos.Center (),
Y = Pos.Bottom (logo) + 1
};
diff --git a/Examples/mdv/mdv.csproj b/Examples/mdv/mdv.csproj
index f465e91566..98e6fca9b7 100644
--- a/Examples/mdv/mdv.csproj
+++ b/Examples/mdv/mdv.csproj
@@ -1,13 +1,6 @@
Exe
-
-
-
- 2.0
- 2.0
- 2.0
- 2.0
enable
latest
diff --git a/GitVersion.yml b/GitVersion.yml
index ddfe73b07c..f10138884e 100644
--- a/GitVersion.yml
+++ b/GitVersion.yml
@@ -70,20 +70,28 @@ branches:
# source-branches: ['v1_develop']
# Pull Request Branches
- # Configures versioning for PRs (e.g., 2.0.0-pr.feature-123.1)
+ # Configures versioning for PRs (e.g., 2.0.0-PullRequest5009.1)
pull-request:
- # Matches typical PR branch names
- regex: ^(pull|pull\-requests|pr)[/-]
- # Uses 'pr' prefix with branch name in the label (e.g., pr.feature-123)
- label: pr.{BranchName}
- # Inherits increment strategy from source branch
+ regex: "^(pull|pull\\-requests|pr)[\\/-](?\\d+)"
+ label: "PullRequest{Number}"
increment: Inherit
source-branches:
- develop
- main
- # High weight ensures PR versions sort after regular pre-releases
+ - feature
pre-release-weight: 30000
+ # Feature / Fix / Topic Branches
+ # Matches branches like feature/*, fix/*, issue/*, etc.
+ # Inherits version from develop and uses the branch name as pre-release label.
+ # e.g., if develop is at 2.2.0, fix/foobar produces 2.2.0-fix-foobar.1
+ feature:
+ regex: "^(feature|fix|issue|dependabot|copilot)[\\/-](?.+)"
+ label: "{BranchName}"
+ increment: Inherit
+ source-branches:
+ - develop
+
# Ignore specific commits if needed (currently empty)
ignore:
sha: []
\ No newline at end of file
diff --git a/Terminal.Gui/README.md b/Terminal.Gui/README.md
index fbc18bafd8..abbbfffb41 100644
--- a/Terminal.Gui/README.md
+++ b/Terminal.Gui/README.md
@@ -1,126 +1,161 @@
-# Terminal.Gui Project
+# Terminal.Gui Library — Maintainer Guide
-**Terminal.Gui** is a cross-platform UI toolkit for creating console-based graphical user interfaces in .NET. This repository contains all files required to build the **Terminal.Gui** library and NuGet package, enabling developers to create rich terminal applications with ease.
+This directory contains the core **Terminal.Gui** library source code. This README documents how to maintain and release the library. For contribution guidelines, see [CONTRIBUTING.md](../CONTRIBUTING.md). For building apps with Terminal.Gui, see [the documentation](https://gui-cs.github.io/Terminal.Gui).
-## Project Overview
+## Versioning
-**Terminal.Gui** provides a comprehensive framework for building interactive console applications with support for keyboard and mouse input, customizable views, and a robust event system. It is designed to work across Windows, macOS, and Linux, leveraging platform-specific console capabilities where available.
+Versions are computed automatically by [GitVersion 6.x](https://gitversion.net) using the [GitFlow](https://gitversion.net/docs/learn/branching-strategies/gitflow/) branching strategy. Configuration is in [`GitVersion.yml`](../GitVersion.yml).
-## Project Folder Structure
+The [GitVersion.MsBuild](https://www.nuget.org/packages/GitVersion.MsBuild) NuGet package is included in `Terminal.Gui.csproj` and automatically sets `Version`, `AssemblyVersion`, `FileVersion`, and `InformationalVersion` from git history at build time. No manual version management is needed.
-This directory contains the core **Terminal.Gui** library source code. For a detailed repository structure, see [CONTRIBUTING.md - Repository Structure](../CONTRIBUTING.md#repository-structure).
+### How Versions Are Computed
-## Getting Started
+| Branch | Example Version | Increment | Notes |
+|--------|----------------|-----------|-------|
+| `main` (pre-release) | `2.0.0-beta.3` | Patch | Label set in `GitVersion.yml` (`label: beta`) |
+| `main` (stable) | `2.0.0` | Patch | Set `label: ''` for stable release |
+| `develop` | `2.1.0-develop.42` | Minor | Always carries `-develop` pre-release label |
+| `feature/*`, `fix/*`, etc. | `2.1.0-my-feature.1` | Inherit | Inherits from `develop`; branch name becomes label |
+| `pull-request/*` | `2.0.0-pr.123.1` | Inherit | PR number in label |
-For instructions on how to start using **Terminal.Gui**, refer to the [Getting Started Guide](https://gui-cs.github.io/Terminal.Gui/docs/getting-started.html) in our documentation.
+### Checking Versions Locally
-## Documentation
+```powershell
+# Install the CLI tool (one-time)
+dotnet tool install --global GitVersion.Tool
-Comprehensive documentation is available at [gui-cs.github.io/Terminal.Gui](https://gui-cs.github.io/Terminal.Gui).
+# Show what version would be computed for the current branch
+dotnet-gitversion
+```
-For information on generating and updating the API documentation locally, refer to the [DocFX README](../docfx/README.md).
+The version is also embedded in every build. For example, `UICatalog --version` displays the terse SemVer (build metadata after `+` is stripped).
-## Versioning
+### Pre-Release Label Progression on `main`
-Version information for Terminal.Gui is managed by [gitversion](https://gitversion.net). To install `gitversion`:
+To change the pre-release stage, edit the `label` field under `main` in `GitVersion.yml`:
-```powershell
-dotnet tool install --global GitVersion.Tool
-dotnet-gitversion
-```
+| Stage | `label` value | Example Output |
+|-------|--------------|----------------|
+| Beta | `beta` | `2.0.0-beta.1` |
+| Release Candidate | `rc` | `2.0.0-rc.1` |
+| Stable Release | `''` (empty) | `2.0.0` |
-The project version (used in the NuGet package and `Terminal.Gui.dll`) is determined from the latest `git tag`. The format of version numbers is `major.minor.patch.build.height` and follows [Semantic Versioning](https://semver.org/) rules.
+## Publishing a Release
-To define a new version, tag a commit using `git tag`:
+Releases follow [Semantic Versioning](https://semver.org/): **MAJOR** for breaking changes, **MINOR** for new features, **PATCH** for bug fixes.
-```powershell
-git tag v2.1.0-beta.1 -a -m "Release v2.1.0 Beta 1"
-dotnet-gitversion /updateprojectfiles
-dotnet build -c Release
-```
+### Automated Release Workflow (Preferred)
-**DO NOT COMMIT AFTER USING `/updateprojectfiles`!** Doing so will update the `.csproj` files in your branch with version info, which we do not want.
+Two workflows handle the release lifecycle:
-## Publishing a Release of Terminal.Gui
+**Step 1 — [Prepare Release](../.github/workflows/prepare-release.yml)** (manual trigger):
+1. Go to **Actions → Prepare Release → Run workflow**.
+2. Pick the release type (`beta`, `rc`, `stable`) and optionally override the version.
+3. The workflow creates a `release/vX.Y.Z` branch from `develop`, updates the `GitVersion.yml` label, and opens a PR into `main`.
+4. Review the PR — CI runs, branch protections apply.
-To release a new version, follow these steps based on [Semantic Versioning](https://semver.org/) rules:
+**Step 2 — [Finalize Release](../.github/workflows/finalize-release.yml)** (automatic on PR merge):
+When the release PR is merged into `main`:
+1. Creates an annotated tag (`vX.Y.Z` or `vX.Y.Z-beta`)
+2. Creates a GitHub Release with auto-generated notes
+3. [Publish](../.github/workflows/publish.yml) fires automatically → NuGet package published
+4. Opens a back-merge PR (`main` → `develop`) to keep branches in sync
-- **MAJOR** version for incompatible API changes.
-- **MINOR** version for backwards-compatible functionality additions.
-- **PATCH** version for backwards-compatible bug fixes.
+### Manual Release Steps
-### Steps for Release:
+If you need to release manually:
-1. **Verify the `develop` branch is ready for release**:
- - Ensure all changes are committed and pushed to the `develop` branch.
- - Ensure your local `develop` branch is up-to-date with `upstream/develop`.
+1. **Ensure `develop` is ready**: all changes committed, CI passing.
-2. **Create a pull request for the release in the `develop` branch**:
- - Title the PR as "Release vX.Y.Z".
- ```powershell
- git checkout develop
- git pull upstream develop
- git checkout -b vX_Y_Z
- git add .
- git commit -m "Release vX.Y.Z"
- git push
- ```
- - Go to the link printed by `git push` and fill out the Pull Request.
+2. **Merge `develop` into `main`**:
+ ```powershell
+ git checkout main
+ git pull upstream main
+ git checkout develop
+ git pull upstream develop
+ git checkout main
+ git merge develop
+ # Fix any merge conflicts
+ ```
-3. **On github.com, verify the build action worked on your fork, then merge the PR**.
+3. **Update the pre-release label** in `GitVersion.yml` if changing stages (e.g., `beta` → `rc` → `''`).
-4. **Pull the merged `develop` from `upstream`**:
- ```powershell
- git checkout develop
- git pull upstream develop
- ```
+4. **Tag the release on `main`**:
+ ```powershell
+ git tag vX.Y.Z -a -m "Release vX.Y.Z"
+ ```
-5. **Merge `develop` into `main`**:
- ```powershell
- git checkout main
- git pull upstream main
- git merge develop
- ```
- - Fix any merge errors.
+5. **Push atomically**:
+ ```powershell
+ git push --atomic upstream main vX.Y.Z
+ ```
-6. **Create a new annotated tag for the release on `main`**:
- ```powershell
- git tag vX.Y.Z -a -m "Release vX.Y.Z"
- ```
+6. **Monitor CI**: the [Publish workflow](https://github.com/gui-cs/Terminal.Gui/actions) builds and pushes to [NuGet](https://www.nuget.org/packages/Terminal.Gui). It also triggers an update to [Terminal.Gui.templates](https://github.com/gui-cs/Terminal.Gui.templates) for stable releases.
-7. **Push the new tag to `main` on `upstream`**:
- ```powershell
- git push --atomic upstream main vX.Y.Z
- ```
+7. **Create a GitHub Release** at [Releases](https://github.com/gui-cs/Terminal.Gui/releases) with auto-generated release notes.
-8. **Monitor Github Actions to ensure the NuGet publishing worked**:
- - Check [GitHub Actions](https://github.com/gui-cs/Terminal.Gui/actions).
+8. **Merge `main` back into `develop`**:
+ ```powershell
+ git checkout develop
+ git pull upstream develop
+ git merge main
+ git push upstream develop
+ ```
-9. **Check NuGet to see the new package version (wait a few minutes)**:
- - Visit [NuGet Package](https://www.nuget.org/packages/Terminal.Gui).
+## CI/CD Workflows
-10. **Add a new Release in Github**:
- - Go to [GitHub Releases](https://github.com/gui-cs/Terminal.Gui/releases) and generate release notes with the list of PRs since the last release.
+All workflows are in [`.github/workflows/`](../.github/workflows/):
-11. **Update the `develop` branch with the new version**:
- ```powershell
- git checkout develop
- git pull upstream develop
- git merge main
- git push upstream develop
- ```
+| Workflow | Trigger | Purpose |
+|----------|---------|---------|
+| **[prepare-release.yml](../.github/workflows/prepare-release.yml)** | Manual dispatch | Creates a release PR from `develop` → `main` with label updates |
+| **[finalize-release.yml](../.github/workflows/finalize-release.yml)** | Release PR merged to `main` | Creates tag, GitHub Release, back-merge PR to `develop` |
+| **[publish.yml](../.github/workflows/publish.yml)** | Push to `main` or `develop`, version tags | Builds Release config, packs, and publishes to NuGet.org |
+| **[release.yml](../.github/workflows/release.yml)** | Manual dispatch | **(Legacy)** Direct tag-and-release on `main`; superseded by prepare/finalize |
+| **[build-validation.yml](../.github/workflows/build-validation.yml)** | Push/PR to `main` or `develop` | Builds all configurations to validate compilation |
+| **[unit-tests.yml](../.github/workflows/unit-tests.yml)** | Push/PR to `main` or `develop` | Runs all unit tests |
+| **[api-docs.yml](../.github/workflows/api-docs.yml)** | Push to `develop` | Builds and deploys API docs to GitHub Pages |
+| **[codeql-analysis.yml](../.github/workflows/codeql-analysis.yml)** | Push/PR to `main` or `develop` | CodeQL security analysis |
+| **[integration-tests.yml](../.github/workflows/integration-tests.yml)** | Push/PR to `main` or `develop` | Integration tests |
+| **[stress-tests.yml](../.github/workflows/stress-tests.yml)** | Push/PR to `main` or `develop` | Stress tests |
-## NuGet
+## V1 Legacy Branches
-The official NuGet package for Terminal.Gui is available at [https://www.nuget.org/packages/Terminal.Gui](https://www.nuget.org/packages/Terminal.Gui). When a new version tag is defined and merged into `main`, a NuGet package is automatically generated by a GitHub Action. Pre-release versions (e.g., `2.0.0-beta.5`) are tagged as pre-release on NuGet.
+Terminal.Gui V1 (latest: `v1.19.0`) is maintained on separate branches:
-## Contributing
+| Branch | Purpose |
+|--------|---------|
+| `v1_release` | V1 stable releases (equivalent of `main` for V1) |
+| `v1_develop` | V1 development (equivalent of `develop` for V1) |
-We welcome contributions from the community. For complete contribution guidelines, including:
-- Build and test instructions
-- Coding conventions and style rules
-- Testing requirements and patterns
-- Pull request guidelines
-- CI/CD workflows
+V1 follows the same GitFlow model as V2 but is in maintenance-only mode. The V1 NuGet package is `Terminal.Gui` 1.x. V1 API docs are published from `v1_release` to a separate GitHub Pages path.
+
+These branches are **not** configured in `GitVersion.yml` (the config was removed to avoid interference with V2 versioning). V1 releases are tagged manually (e.g., `v1.19.0`).
+
+## NuGet Package
+
+- **Package**: [nuget.org/packages/Terminal.Gui](https://www.nuget.org/packages/Terminal.Gui)
+- **Auto-published** on every push to `main` or `develop` (pre-release versions from `develop`, release versions from `main`)
+- Pre-release versions (e.g., `2.0.0-beta.5`) are marked as pre-release on NuGet
+
+### Local Package Development
+
+When building in `Release` configuration, the `.csproj` automatically:
+1. Copies `.nupkg` and `.snupkg` to `../local_packages/`
+2. Pushes the package to the local NuGet cache
+
+Use the `local_packages` folder as a local NuGet source to test packages before publishing:
+```powershell
+dotnet build Terminal.Gui/Terminal.Gui.csproj -c Release
+# Package is now in local_packages/ and your global NuGet cache
+```
+
+## Documentation
+
+- **Live docs**: [gui-cs.github.io/Terminal.Gui](https://gui-cs.github.io/Terminal.Gui)
+- **DocFX source**: [`docfx/`](../docfx/) — see [`docfx/README.md`](../docfx/README.md) for local generation
+- **API docs** are auto-deployed to GitHub Pages on every push to `develop`
+
+## Contributing
-Please refer to [CONTRIBUTING.md](../CONTRIBUTING.md) in the repository root.
+See [CONTRIBUTING.md](../CONTRIBUTING.md) for build/test instructions, coding conventions, and PR guidelines.
diff --git a/Terminal.Gui/Terminal.Gui.csproj b/Terminal.Gui/Terminal.Gui.csproj
index a91b4c6d6f..893d3143e0 100644
--- a/Terminal.Gui/Terminal.Gui.csproj
+++ b/Terminal.Gui/Terminal.Gui.csproj
@@ -1,13 +1,8 @@
-
-
-
+
-
- 2.0.0
-
@@ -45,7 +40,6 @@
true
-
@@ -76,6 +70,9 @@
+
+
+
@@ -209,6 +206,6 @@
-
+
diff --git a/Tests/UnitTests.Legacy/UnitTests.Legacy.csproj b/Tests/UnitTests.Legacy/UnitTests.Legacy.csproj
index 98cce1be6b..9eb7fa9818 100644
--- a/Tests/UnitTests.Legacy/UnitTests.Legacy.csproj
+++ b/Tests/UnitTests.Legacy/UnitTests.Legacy.csproj
@@ -1,13 +1,4 @@
-
-
-
-
- 2.0
- 2.0
- 2.0
- 2.0
-
UnitTests.Legacy
Exe
diff --git a/Tests/UnitTests.NonParallelizable/UnitTests.NonParallelizable.csproj b/Tests/UnitTests.NonParallelizable/UnitTests.NonParallelizable.csproj
index 76f1c2b6bf..b414931854 100644
--- a/Tests/UnitTests.NonParallelizable/UnitTests.NonParallelizable.csproj
+++ b/Tests/UnitTests.NonParallelizable/UnitTests.NonParallelizable.csproj
@@ -1,13 +1,4 @@
-
-
-
-
- 2.0
- 2.0
- 2.0
- 2.0
-
UnitTests.NonParallelizable
Exe
diff --git a/Tests/UnitTestsParallelizable/UnitTests.Parallelizable.csproj b/Tests/UnitTestsParallelizable/UnitTests.Parallelizable.csproj
index f590d74bd6..7da1c0c2dd 100644
--- a/Tests/UnitTestsParallelizable/UnitTests.Parallelizable.csproj
+++ b/Tests/UnitTestsParallelizable/UnitTests.Parallelizable.csproj
@@ -1,13 +1,4 @@
-
-
-
-
- 2.0
- 2.0
- 2.0
- 2.0
-
Exe
true
diff --git a/docfx/images/uicatalog.gif b/docfx/images/uicatalog.gif
new file mode 100644
index 0000000000..c91292922d
Binary files /dev/null and b/docfx/images/uicatalog.gif differ