Compare commits

..

6 Commits

Author SHA1 Message Date
Josh McKinney
5431a70c0c Make ratatui dev-dependecy in ratatui-termwiz just a path ref 2024-11-27 14:04:02 -08:00
Josh McKinney
65666905e6 Fix missing underline-color feature mapping to core flag for -crossterm 2024-11-27 14:03:05 -08:00
Josh McKinney
9703083288 Make ratatui dev-dependecy in ratatui-crossterm just a path ref 2024-11-27 14:01:02 -08:00
Josh McKinney
c079cafe4d Make ratatui dev-dependecy in ratatui-widgets just a path ref 2024-11-27 13:59:04 -08:00
Josh McKinney
421adbe5dd Add sub crate versions to workspace manifest 2024-11-27 13:55:35 -08:00
Josh McKinney
35e790cb0d chore: prepare alpha modularization release 2024-11-27 13:31:06 -08:00
401 changed files with 9743 additions and 25449 deletions

View File

@@ -1,115 +0,0 @@
# skip entries from <https://github.com/ratatui/ratatui/pull/1652>
50af9a5d80ed5446f3e6cc554911f606580edde9
272e9709c6eed45cd7e0c183624b7898f4e0ae69
adc8fdc35aa57d6dad2ae8dd30ec2e9256576c09
31711dbf82a4c7bb3b78692da34d9f469725dd6e
b6356aa7a529a491d63dd6628b8985adae337f16
885558b6f89df642317d39c5b44c94c742d1e0c8
6440eb9d76379340953420629ab0a3d9039d6c48
3870583ea868452191857f9bda97a3d5c35d0a4f
487edc8399683fb8a9a66359729c015780d248f2
250c222cc4aaab09184a28efc68f75ca03133794
590a392ab11c1a215767614931036781f4cf6a29
6443f7408af4a8834bc68cd35d2ba9be47e45f38
8339cce10a51c9c951b3c9750d527d80168626eb
ac342231c344e893f2f630ee38167aab28c736a6
0fb103680028a6e26a1923f87b60bde51acaec4f
4335a90a00aeeedb69a92442c7f2711727944017
71480242a926f98e9081ed6e2dc8c381757b3a42
e1a31db55913bd690bcaef380e9dbc3b6a5dc175
38490ff8da6f11e309ba1bcb3e88a562f7c953c5
a4bb143e4767137d6a9e9927d3da66562611f86f
bc73f2dcbe5ea48fc4d1555a8e931f40d7b0e03f
77dea441e5637b1c428c2aa71ea67fb3aac20c12
06af14107e70e49a4cba3babb8ab0a0c57b4bdf8
9acdab32df69517b93dd2b861b85586d47c71540
2e684c6500be61fbe69744d183c8086564ac0051
b7f8ec0ff9659473d936eae53a57cd9de38cec1f
31a2c4548c304270a8c852f19baa7a4eaac5e75c
57b681b053c019b66e0ed92959638997fea731b1
131b9ec41751163d43d94564363247b60f031486
8b32f82b4dd526580d00fa13f053bf507e8ea933
76d1e5b1733d47a7f05acf563db26cb1a66b540d
57a0a34f924e0d488c9e9f917900e677c3488dc4
12aa58601ddd0704256c56019bd2c7139d41f7a2
2dc81833c60951d16f9bd60f3da003edfc9a11f5
20e41f1d1da6db8abbc2504814531d4d97bdf94b
68b55a12a29e70ebfcc063c2d5d5845de3b5a27e
693003314a25e792ffc5d53146b28bfb6a4582e3
63441e259bc38b56e0369197bed14788b2cb3d54
fa88152c808eeb6c9d9b3662361aab1e57e1b1bf
30d9daa59b1843786cde00e25c3e69cfe818b80b
92540b2f6ab25f3a5400aebb28af3c498ac793a4
29edc3a7a38c512611a80cf5d8d42027558419b2
819499d6ffc0e8453ed3220067645933a4882a74
4756526829a4e849d9e256b6cf821eb66afe3ade
8f35437d5ad78d31cd45c4af888f20f0b5ab4196
39bd72b1f702dadb1ebbaf4e77ad2fada166ac49
c6d2cee3e967c9234176c5229858512b0c79d6a9
bd90e3d928e0f9f0b915933ebbc32c2256fe8cfa
b820c0c7e4c576c1e39b5e482a8aac08076a039c
74194759756bb111b4da3e9a5cdb968275a2fab0
38b2f27efe0e1829bc503df7fd64b94b7bb80d97
21aa3232d762d6e3f81f15fc5b66ba462385ac05
903bb0ae32d22393783edfda96db900739864f0a
5cee13ab6d9c49751cf9283d9099e37f0cc3632c
0a0997702dd4cf2217160f5652f5c39cbd4a1010
778f2f5ec511bef431b54157242b91d083ea9840
7d23bd2ceaf96e81972b5f746fdcba0d17f6391f
2c02a56bce31519386303571e0b66b7d4beb378e
f33d51e7d9ccf9fe52ec3289d04d97c722d9ee17
91cd81aaa032887bb2327bc3fe3cad6b3c9fbacf
5d5a1ccb0b4e2f293f215ce026fba33f1c069689
6b9417db5f2adbdc60e9dd8dc5acbbe2a1f77ba9
dce1e4b138eb1333c9e773bacd579a8cdddd73fb
b4aacb045e2200896b0d0136a2b8688b47828d73
dcba0bcd5d5d6e33ddc1fa94ebb94819fdda600e
6f52350ecfb62e3a5bac16f0824e74b757cc6cd2
2c6f324b9aa5034771e00758b143fd8df94d859d
bf0210602948f8d26ae323996fe7b22fb218a446
07aff91b015b5e7e0504680c12edbce70d7dba1e
f6d49dde14af73ed467d75d8f6ec0f502db2908f
9a7467b30576d5cb7491ea6e09efcae97eadf9bb
a0c35f1d7bcce10e092582b95f5b0a3f20ad7bf3
d24747d46982192b575a40b8cc18d1c948fac3d7
8060f7bc578b29dde6ca0c4c64569f9c73218f46
0dc5b2d2e0aa6438ffc1b3965b1ab31c721adbcc
8ecdd892f53d7db95bbb53a61700d36e3fcefdd4
570c35868147a2400a13331e85d562d1ef96a011
3855c3a84a77037aeee40dbe9e52454fb1f9afee
93372f35c1669da0138ca776890f3ff3d38a6539
6cf08d4a2f0398856fd593f50bf077fd59b08230
f78d3bfec32d07c1124eee8d0249477ce3fb0884
204307fa50aaaa373946342084f7fd3af39f3cd3
c50b01d098e5ab405a50c3e14e858da27d606e8f
f71d1ac73e8290f37d55a67d6a6507a3653ec174
ae2868c0e0b1ac8b5126fd43269383fa533d87b5
be8def963956c605bca28bcd8df673bd7ec3740b
4ac4d9d3ab97176d71e287bcdd6d41e66f2f7ccb
fafabb8dab84e9460a076199ea646262e51c855b
2f97d35bd8618e8c0cc006cb1d4a9b151c1b9b4a
39d5a745acbbd3510de707d4e7c471c17e02ae59
a1acdcdc4c002390a76f01699cfa006a36cf3f56
ad54cf29ad1a4335ba208fb94a8fc5dfbba260e3
c7649575e7b199794be4252f79da80aaecdcee28
8913e2ce1f40d451ddb4527f08ec75f198d5063c
1b9e310300f22bfc72364f027a9caeddadf61a99
89d7dd46031511f0556b2d29ab34035f42e3a24c
ca4fa0b9bf5ba707aa0447ba7c38fbacdadb7eec
8cecfdf2f6dd5b0de507f79b469517cc0fb42add
7eeb6afb3dbc56e52f9387a74f826b186cd19137
d0f75eb371a96f8d5f174e23de074efd840e9e44
f8a70ea9da8e6df2bf7a5f74cce45615fc292afe
f28b9730061bffadb9d87ad63edf7d10b245d2c1
afc5cf2140f22fea6bd6933dd0f9c302229a1980
b75df78cdca58d5dca0c51fb8e106067aa6cb752
28f5a6dbd4091aa3efa86eed6767eeb44a655f0f
345e6a1ebd853858463a33953585ce407a60378c
c45a4de47c601554f6b981d211181468b4798e41
bbaa9a5432ff6ad5518344123c3b56f349347e99
f804c90f96221f334371ccd01b0e6df7b1cfc1e8
16ba867c5877d8c97968987ecb5f8bff966d0a82
38a1474ca12aa6a796afc1e277882d997a999e14
92c4078413fc79fcc83f5d3d8708abb58696ff1a
d4415204e1eb3aed2a74a722aeaaa274975dd2d7
e48bcf5f21f14acb27996fdc02231c140f5b817c

View File

@@ -32,17 +32,17 @@ schema_pattern = "(build|chore|ci|docs|feat|fix|perf|refactor|revert|style|test)
type = "list"
name = "change_type"
choices = [
{ value = "build", name = "build: Changes that affect the build system or external dependencies (example scopes: pip, docker, npm)", key = "b" },
{ value = "chore", name = "chore: A modification that generally does not fall into any other category", key = "c" },
{ value = "ci", name = "ci: Changes to our CI configuration files and scripts (example scopes: GitLabCI)", key = "i" },
{ value = "docs", name = "docs: Documentation only changes", key = "d" },
{ value = "feat", name = "feat: A new feature.", key = "f" },
{ value = "fix", name = "fix: A bug fix.", key = "x" },
{ value = "perf", name = "perf: A code change that improves performance", key = "p" },
{ value = "refactor", name = "refactor: A code change that neither fixes a bug nor adds a feature", key = "r" },
{ value = "revert", name = "revert: Revert previous commits", key = "v" },
{ value = "style", name = "style: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)", key = "s" },
{ value = "test", name = "test: Adding missing or correcting existing tests", key = "t" },
{ value = "build", name = "build: Changes that affect the build system or external dependencies (example scopes: pip, docker, npm)", key = "b" },
{ value = "chore", name = "chore: A modification that generally does not fall into any other category", key = "c" },
{ value = "ci", name = "ci: Changes to our CI configuration files and scripts (example scopes: GitLabCI)", key = "i" },
{ value = "docs", name = "docs: Documentation only changes", key = "d" },
{ value = "feat", name = "feat: A new feature.", key = "f" },
{ value = "fix", name = "fix: A bug fix.", key = "x" },
{ value = "perf", name = "perf: A code change that improves performance", key = "p" },
{ value = "refactor", name = "refactor: A code change that neither fixes a bug nor adds a feature", key = "r" },
{ value = "revert", name = "revert: Revert previous commits", key = "v" },
{ value = "style", name = "style: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)", key = "s" },
{ value = "test", name = "test: Adding missing or correcting existing tests", key = "t" },
]
message = "Select the type of change you are committing"

View File

@@ -1,81 +0,0 @@
# GitHub Copilot Code Review Instructions
## General Review Principles
### Pull Request Size and Scope
- **Flag large PRs**: Comment if a PR changes more than 500 lines or touches many unrelated areas
- **Suggest splitting**: Recommend breaking large changes into smaller, focused PRs
- **Question scope creep**: Ask about unrelated changes that seem outside the PR's stated purpose
### Code Quality and Style
- **Check adherence to style guidelines**: Verify changes follow the project's Rust conventions in [CONTRIBUTING.md](https://github.com/ratatui/ratatui/blob/main/CONTRIBUTING.md#code-formatting)
- **Verify xtask compliance**: Ensure `cargo xtask format` and `cargo xtask lint` would pass
- **Look for AI-generated patterns**: Be suspicious of verbose, overly-commented, or non-idiomatic code
### Architectural Considerations
- **Reference ARCHITECTURE.md**: Point to [ARCHITECTURE.md](https://github.com/ratatui/ratatui/blob/main/ARCHITECTURE.md) for changes affecting crate boundaries
- **Question fundamental changes**: Flag modifications to core configuration, linting rules, or build setup without clear justification
- **Verify appropriate crate placement**: Ensure changes are in the correct crate per the modular structure
### Breaking Changes and Deprecation
- **Require deprecation**: Insist on deprecation warnings rather than immediate removal of public APIs
- **Ask for migration path**: Request clear upgrade instructions for breaking changes
- **Suggest feature flags**: Recommend feature flags for experimental or potentially disruptive changes
- **Reference versioning policy**: Point to the requirement of at least one version notice before removal
### Testing and Documentation
- **Verify test coverage**: Ensure new functionality includes appropriate tests
- **Check for test removal**: Question any removal of existing tests without clear justification
- **Require documentation**: Ensure public APIs are documented with examples
- **Validate examples**: Check that code examples are minimal and follow project style
### Specific Areas of Concern
#### Configuration Changes
- **Lint configuration**: Question changes to `.clippy.toml`, `rustfmt.toml`, or CI configuration
- **Cargo.toml modifications**: Scrutinize dependency changes or workspace modifications
- **Build system changes**: Require justification for xtask or build process modifications
#### Large Code Additions
- **Question necessity**: Ask if large code additions could be implemented more simply
- **Check for duplication**: Look for code that duplicates existing functionality
- **Verify integration**: Ensure new code integrates well with existing patterns
#### File Organization
- **Validate module structure**: Ensure new modules follow the project's organization
- **Check import organization**: Verify imports follow the std/external/local grouping pattern
- **Review file placement**: Confirm files are in appropriate locations per ARCHITECTURE.md
## Comment Templates
### For Large PRs
```
This PR seems quite large with changes across multiple areas. Consider splitting it into smaller, focused PRs:
- Core functionality changes
- Documentation updates
- Test additions
- Configuration changes
See our [contribution guidelines](https://github.com/ratatui/ratatui/blob/main/CONTRIBUTING.md#keep-prs-small-intentional-and-focused) for more details.
```
### For Breaking Changes
```
This appears to introduce breaking changes. Please consider:
- Adding deprecation warnings instead of immediate removal
- Providing a clear migration path in the PR description
- Following our [deprecation policy](https://github.com/ratatui/ratatui/blob/main/CONTRIBUTING.md#deprecation-notice)
```
### For Configuration Changes
```
Changes to project configuration (linting, formatting, build) should be discussed first. Please explain:
- Why this change is necessary
- What problem it solves
- Whether it affects contributor workflow
```
### For Style Issues
```
Please run `cargo xtask format` and `cargo xtask lint` to ensure code follows our style guidelines. See [CONTRIBUTING.md](https://github.com/ratatui/ratatui/blob/main/CONTRIBUTING.md#code-formatting) for details.
```

25
.github/workflows/bench_base.yml vendored Normal file
View File

@@ -0,0 +1,25 @@
name: Run Benchmarks
on:
push:
branches:
- main
jobs:
benchmark_base_branch:
name: Continuous Benchmarking with Bencher
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- uses: bencherdev/bencher@main
- name: Track base branch benchmarks with Bencher
run: |
bencher run \
--project ratatui-org \
--token '${{ secrets.BENCHER_API_TOKEN }}' \
--branch main \
--testbed ubuntu-latest \
--adapter rust_criterion \
--err \
cargo bench

25
.github/workflows/bench_run_fork_pr.yml vendored Normal file
View File

@@ -0,0 +1,25 @@
name: Run and Cache Benchmarks
on:
pull_request:
types: [opened, reopened, edited, synchronize]
jobs:
benchmark_fork_pr_branch:
name: Run Fork PR Benchmarks
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Run Benchmarks
run: cargo bench > benchmark_results.txt
- name: Upload Benchmark Results
uses: actions/upload-artifact@v4
with:
name: benchmark_results.txt
path: ./benchmark_results.txt
- name: Upload GitHub Pull Request Event
uses: actions/upload-artifact@v4
with:
name: event.json
path: ${{ github.event_path }}

View File

@@ -0,0 +1,56 @@
name: Track Benchmarks with Bencher
on:
workflow_run:
workflows: [Run and Cache Benchmarks]
types: [completed]
permissions:
contents: read
pull-requests: write
jobs:
track_fork_pr_branch:
if: github.event.workflow_run.conclusion == 'success'
runs-on: ubuntu-latest
env:
BENCHMARK_RESULTS: benchmark_results.txt
PR_EVENT: event.json
steps:
- name: Download Benchmark Results
uses: dawidd6/action-download-artifact@v6
with:
name: ${{ env.BENCHMARK_RESULTS }}
run_id: ${{ github.event.workflow_run.id }}
- name: Download PR Event
uses: dawidd6/action-download-artifact@v6
with:
name: ${{ env.PR_EVENT }}
run_id: ${{ github.event.workflow_run.id }}
- name: Export PR Event Data
uses: actions/github-script@v7
with:
script: |
let fs = require('fs');
let prEvent = JSON.parse(fs.readFileSync(process.env.PR_EVENT, {encoding: 'utf8'}));
core.exportVariable("PR_HEAD", prEvent.pull_request.head.ref);
core.exportVariable("PR_BASE", prEvent.pull_request.base.ref);
core.exportVariable("PR_BASE_SHA", prEvent.pull_request.base.sha);
core.exportVariable("PR_NUMBER", prEvent.number);
- uses: bencherdev/bencher@main
- name: Track Benchmarks with Bencher
run: |
bencher run \
--project ratatui-org \
--token '${{ secrets.BENCHER_API_TOKEN }}' \
--branch "$PR_HEAD" \
--start-point "$PR_BASE" \
--start-point-hash "$PR_BASE_SHA" \
--start-point-clone-thresholds \
--start-point-reset \
--testbed ubuntu-latest \
--adapter rust_criterion \
--err \
--github-actions '${{ secrets.GITHUB_TOKEN }}' \
--ci-number "$PR_NUMBER" \
--file "$BENCHMARK_RESULTS"

View File

@@ -0,0 +1,52 @@
#!/bin/bash
# Exit on error. Append "|| true" if you expect an error.
set -o errexit
# Exit on error inside any functions or subshells.
set -o errtrace
# Do not allow use of undefined vars. Use ${VAR:-} to use an undefined VAR
set -o nounset
# Catch the error in case mysqldump fails (but gzip succeeds) in `mysqldump |gzip`
set -o pipefail
# Turn on traces, useful while debugging but commented out by default
# set -o xtrace
last_release="$(git tag --sort=committerdate | grep -P "v0+\.\d+\.\d+$" | tail -1)"
echo "🐭 Last release: ${last_release}"
# detect breaking changes
if [ -n "$(git log --oneline ${last_release}..HEAD | grep '!:')" ]; then
echo "🐭 Breaking changes detected since ${last_release}"
git log --oneline ${last_release}..HEAD | grep '!:'
# increment the minor version
minor="${last_release##v0.}"
minor="${minor%.*}"
next_minor="$((minor + 1))"
next_release="v0.${next_minor}.0"
else
# increment the patch version
patch="${last_release##*.}"
next_patch="$((patch + 1))"
next_release="${last_release/%${patch}/${next_patch}}"
fi
echo "🐭 Next release: ${next_release}"
suffix="alpha"
last_tag="$(git tag --sort=committerdate | tail -1)"
if [[ "${last_tag}" = "${next_release}-${suffix}"* ]]; then
echo "🐭 Last alpha release: ${last_tag}"
# increment the alpha version
# e.g. v0.22.1-alpha.12 -> v0.22.1-alpha.13
alpha="${last_tag##*-${suffix}.}"
next_alpha="$((alpha + 1))"
next_tag="${last_tag/%${alpha}/${next_alpha}}"
else
# increment the patch and start the alpha version from 0
# e.g. v0.22.0 -> v0.22.1-alpha.0
next_tag="${next_release}-${suffix}.0"
fi
# update the crate version
msg="# crate version"
sed -E -i "s/^version = .* ${msg}$/version = \"${next_tag#v}\" ${msg}/" Cargo.toml
echo "NEXT_TAG=${next_tag}" >> $GITHUB_ENV
echo "🐭 Next alpha release: ${next_tag}"

View File

@@ -1,13 +1,6 @@
name: Check Pull Requests
# Set the permissions of the github token to the minimum and only enable what is needed
# See https://docs.github.com/en/actions/security-guides/automatic-token-authentication#permissions
permissions: {}
on:
# this workflow is required to be run on pull_request_target as it modifies the PR comments
# care should be taken that the jobs do not run any untrusted input
# zizmor: ignore[dangerous-triggers]
pull_request_target:
types:
- opened
@@ -15,21 +8,23 @@ on:
- synchronize
- labeled
- unlabeled
merge_group:
permissions:
pull-requests: write
jobs:
check-title:
permissions:
pull-requests: write
runs-on: ubuntu-latest
steps:
- name: Check PR title
if: github.event_name == 'pull_request_target'
uses: amannn/action-semantic-pull-request@48f256284bd46cdaab1048c3721360e808335d50 # v5
uses: amannn/action-semantic-pull-request@v5
id: check_pr_title
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# Add comment indicating we require pull request titles to follow conventional commits specification
- uses: marocchino/sticky-pull-request-comment@773744901bac0e8cbb5a0dc842800d45e9b2b405 # v2
- uses: marocchino/sticky-pull-request-comment@v2
if: always() && (steps.check_pr_title.outputs.error_message != null)
with:
header: pr-title-lint-error
@@ -44,42 +39,40 @@ jobs:
# Delete a previous comment when the issue has been resolved
- if: ${{ steps.check_pr_title.outputs.error_message == null }}
uses: marocchino/sticky-pull-request-comment@773744901bac0e8cbb5a0dc842800d45e9b2b405 # v2
uses: marocchino/sticky-pull-request-comment@v2
with:
header: pr-title-lint-error
delete: true
check-breaking-change-label:
permissions:
pull-requests: write
runs-on: ubuntu-latest
env:
# use an environment variable to pass untrusted input to the script
# see https://securitylab.github.com/research/github-actions-untrusted-input/
PR_TITLE: ${{ github.event.pull_request.title }}
steps:
- name: Check breaking change label
id: check_breaking_change
run: |
pattern='^(build|chore|ci|docs|feat|fix|perf|refactor|revert|style|test)(\(\w+\))?!:'
# Check if pattern matches
if echo "${PR_TITLE}" | grep -qE "$pattern"; then
echo "breaking_change=true" >> $GITHUB_OUTPUT
else
echo "breaking_change=false" >> $GITHUB_OUTPUT
fi
- name: Add label
if: steps.check_breaking_change.outputs.breaking_change == 'true'
uses: actions/github-script@v8
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
github.rest.issues.addLabels({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
labels: ['Type: Breaking Change']
})
- name: Check breaking change label
id: check_breaking_change
run: |
pattern='^(build|chore|ci|docs|feat|fix|perf|refactor|revert|style|test)(\(\w+\))?!:'
# Check if pattern matches
if echo "${PR_TITLE}" | grep -qE "$pattern"; then
echo "breaking_change=true" >> $GITHUB_OUTPUT
else
echo "breaking_change=false" >> $GITHUB_OUTPUT
fi
- name: Add label
if: steps.check_breaking_change.outputs.breaking_change == 'true'
uses: actions/github-script@v7
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
github.rest.issues.addLabels({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
labels: ['Type: Breaking Change']
})
do-not-merge:
if: ${{ contains(github.event.*.labels.*.name, 'do not merge') }}

View File

@@ -1,9 +1,5 @@
name: Check Semver
# Set the permissions of the github token to the minimum and only enable what is needed
# See https://docs.github.com/en/actions/security-guides/automatic-token-authentication#permissions
permissions: {}
on:
pull_request:
branches:
@@ -15,8 +11,6 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout the repository
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
persist-credentials: false
uses: actions/checkout@v4
- name: Check semver
uses: obi1kenobi/cargo-semver-checks-action@5b298c9520f7096a4683c0bd981a7ac5a7e249ae # v2
uses: obi1kenobi/cargo-semver-checks-action@v2

View File

@@ -1,9 +1,5 @@
name: Continuous Integration
# Set the permissions of the github token to the minimum and only enable what is needed
# See https://docs.github.com/en/actions/security-guides/automatic-token-authentication#permissions
permissions: {}
on:
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
@@ -29,18 +25,10 @@ jobs:
name: Check Formatting
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
persist-credentials: false
- uses: dtolnay/rust-toolchain@6d653acede28d24f02e3cd41383119e8b1b35921 # master
with:
toolchain: nightly
components: rustfmt
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2
- uses: taiki-e/install-action@44c6d64aa62cd779e873306675c7a58e86d6d532 # v2
with:
tool: taplo-cli
- run: cargo xtask format --check
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@nightly
with: { components: rustfmt }
- run: cargo xtask lint-formatting
# Check for typos in the codebase.
# See <https://github.com/crate-ci/typos/>
@@ -48,10 +36,8 @@ jobs:
name: Check Typos
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
persist-credentials: false
- uses: crate-ci/typos@07d900b8fa1097806b8adb6391b0d3e0ac2fdea7 # master
- uses: actions/checkout@v4
- uses: crate-ci/typos@master
# Check for any disallowed dependencies in the codebase due to license / security issues.
# See <https://github.com/EmbarkStudios/cargo-deny>
@@ -59,16 +45,8 @@ jobs:
name: Check Dependencies
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
persist-credentials: false
- uses: dtolnay/rust-toolchain@6d653acede28d24f02e3cd41383119e8b1b35921 # master
with:
toolchain: stable
- uses: taiki-e/install-action@44c6d64aa62cd779e873306675c7a58e86d6d532 # v2
with:
tool: cargo-deny
- run: cargo deny --log-level info --all-features check
- uses: actions/checkout@v4
- uses: EmbarkStudios/cargo-deny-action@v2
# Check for any unused dependencies in the codebase.
# See <https://github.com/bnjbvr/cargo-machete/>
@@ -76,46 +54,28 @@ jobs:
name: Check Unused Dependencies
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
persist-credentials: false
- uses: bnjbvr/cargo-machete@7959c845782fed02ee69303126d4a12d64f1db18 # v0.9.1
- uses: actions/checkout@v4
- uses: Swatinem/rust-cache@v2
- uses: bnjbvr/cargo-machete@v0.7.0
# Run cargo clippy.
#
# We check for clippy warnings on beta, but these are not hard failures. They should often be
# fixed to prevent clippy failing on the next stable release, but don't block PRs on them unless
# they are introduced by the PR.
lint-clippy:
name: Check Clippy
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
toolchain: ["stable", "beta"]
continue-on-error: ${{ matrix.toolchain == 'beta' }}
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
persist-credentials: false
- uses: dtolnay/rust-toolchain@6d653acede28d24f02e3cd41383119e8b1b35921 # master
with:
toolchain: ${{ matrix.toolchain }}
components: clippy
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2
- run: cargo xtask clippy
env:
RUSTUP_TOOLCHAIN: ${{ matrix.toolchain }}
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
with: { components: clippy }
- uses: Swatinem/rust-cache@v2
- run: cargo xtask lint-clippy
# Run markdownlint on all markdown files in the repository.
lint-markdown:
name: Check Markdown
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
persist-credentials: false
- uses: DavidAnson/markdownlint-cli2-action@992badcdf24e3b8eb7e87ff9287fe931bcb00c6e # v20
- uses: actions/checkout@v4
- uses: DavidAnson/markdownlint-cli2-action@v18
with:
globs: |
'**/*.md'
@@ -127,19 +87,14 @@ jobs:
name: Coverage Report
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
with:
persist-credentials: false
- uses: dtolnay/rust-toolchain@6d653acede28d24f02e3cd41383119e8b1b35921 # master
with:
toolchain: stable
components: llvm-tools
- uses: taiki-e/install-action@44c6d64aa62cd779e873306675c7a58e86d6d532 # v2
with:
tool: cargo-llvm-cov
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2
- uses: taiki-e/install-action@cargo-llvm-cov
- uses: Swatinem/rust-cache@v2
- run: cargo xtask coverage
- uses: codecov/codecov-action@5a1091511ad55cbe89839c7260b706298ca349f7 # v5
- uses: codecov/codecov-action@v5
with:
token: ${{ secrets.CODECOV_TOKEN }}
fail_ci_if_error: true
@@ -151,58 +106,25 @@ jobs:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
toolchain: ["1.85.0", "stable"]
toolchain: ["1.74.0", "stable"]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
persist-credentials: false
- uses: dtolnay/rust-toolchain@6d653acede28d24f02e3cd41383119e8b1b35921 # master
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ matrix.toolchain }}
- uses: taiki-e/install-action@44c6d64aa62cd779e873306675c7a58e86d6d532 # v2
with:
tool: cargo-hack
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2
- run: cargo xtask check --all-features
env:
RUSTUP_TOOLCHAIN: ${{ matrix.toolchain }}
build-no-std:
name: Build No-Std
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
persist-credentials: false
- uses: dtolnay/rust-toolchain@6d653acede28d24f02e3cd41383119e8b1b35921 # master
with:
toolchain: stable
targets: x86_64-unknown-none
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2
# This makes it easier to debug the exact versions of the dependencies
- run: cargo tree --target x86_64-unknown-none -p ratatui-core
- run: cargo tree --target x86_64-unknown-none -p ratatui-widgets
- run: cargo tree --target x86_64-unknown-none -p ratatui-macros
- run: cargo tree --target x86_64-unknown-none -p ratatui --no-default-features
- run: cargo build --target x86_64-unknown-none -p ratatui-core
- run: cargo build --target x86_64-unknown-none -p ratatui-widgets
- run: cargo build --target x86_64-unknown-none -p ratatui-macros
- run: cargo build --target x86_64-unknown-none -p ratatui --no-default-features
- uses: Swatinem/rust-cache@v2
- run: cargo xtask check
# Check if README.md is up-to-date with the crate's documentation.
check-readme:
name: Check README
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
persist-credentials: false
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2
- uses: taiki-e/install-action@44c6d64aa62cd779e873306675c7a58e86d6d532 # v2
with:
tool: cargo-hack,cargo-rdme
- run: cargo xtask readme --check
- uses: actions/checkout@v4
- uses: Swatinem/rust-cache@v2
- uses: taiki-e/install-action@cargo-rdme
- run: cargo xtask check-readme
# Run cargo rustdoc with the same options that would be used by docs.rs, taking into account the
# package.metadata.docs.rs configured in Cargo.toml. https://github.com/dtolnay/cargo-docs-rs
@@ -212,20 +134,11 @@ jobs:
env:
RUSTDOCFLAGS: -Dwarnings
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
persist-credentials: false
- uses: dtolnay/rust-toolchain@6d653acede28d24f02e3cd41383119e8b1b35921 # master
with:
toolchain: nightly
- uses: dtolnay/install@74f735cdf643820234e37ae1c4089a08fd266d8a # master
with:
crate: cargo-docs-rs
- uses: taiki-e/install-action@44c6d64aa62cd779e873306675c7a58e86d6d532 # v2
with:
tool: cargo-hack
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2
- run: cargo xtask docs
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@nightly
- uses: dtolnay/install@cargo-docs-rs
- uses: Swatinem/rust-cache@v2
- run: cargo xtask lint-docs
# Run cargo test on the documentation of the crate. This will catch any code examples that don't
# compile, or any other issues in the documentation.
@@ -233,16 +146,9 @@ jobs:
name: Test Docs
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
persist-credentials: false
- uses: dtolnay/rust-toolchain@6d653acede28d24f02e3cd41383119e8b1b35921 # master
with:
toolchain: stable
- uses: taiki-e/install-action@44c6d64aa62cd779e873306675c7a58e86d6d532 # v2
with:
tool: cargo-hack
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
- run: cargo xtask test-docs
# Run cargo test on the libraries of the crate.
@@ -252,18 +158,11 @@ jobs:
strategy:
fail-fast: false
matrix:
toolchain: ["1.85.0", "stable"]
toolchain: ["1.74.0", "stable"]
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
persist-credentials: false
- uses: dtolnay/rust-toolchain@6d653acede28d24f02e3cd41383119e8b1b35921 # master
with:
toolchain: stable
- uses: taiki-e/install-action@44c6d64aa62cd779e873306675c7a58e86d6d532 # v2
with:
tool: cargo-hack
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
- run: cargo xtask test-libs
# Run cargo test on all the backends.
@@ -280,11 +179,7 @@ jobs:
- os: windows-latest
backend: termion
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
persist-credentials: false
- uses: dtolnay/rust-toolchain@6d653acede28d24f02e3cd41383119e8b1b35921 # master
with:
toolchain: stable
- uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
- run: cargo xtask test-backend ${{ matrix.backend }}

48
.github/workflows/release-alpha.yml vendored Normal file
View File

@@ -0,0 +1,48 @@
name: Release alpha version
on:
workflow_dispatch:
schedule:
# At 00:00 on Saturday
# https://crontab.guru/#0_0_*_*_6
- cron: "0 0 * * 6"
defaults:
run:
shell: bash
jobs:
publish-alpha:
name: Create an alpha release
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Checkout the repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Calculate the next release
run: .github/workflows/calculate-alpha-release.bash
- name: Install Rust stable
uses: dtolnay/rust-toolchain@stable
- name: Publish
run: cargo publish --allow-dirty --token ${{ secrets.CARGO_TOKEN }}
- name: Generate a changelog
uses: orhun/git-cliff-action@v4
with:
config: cliff.toml
args: --unreleased --tag ${{ env.NEXT_TAG }} --strip header
env:
OUTPUT: BODY.md
- name: Publish on GitHub
uses: ncipollo/release-action@v1
with:
tag: ${{ env.NEXT_TAG }}
prerelease: true
bodyFile: BODY.md

View File

@@ -1,71 +0,0 @@
name: Release-plz
# Set the permissions of the github token to the minimum and only enable what is needed
# See https://docs.github.com/en/actions/security-guides/automatic-token-authentication#permissions
permissions: {}
on:
push:
branches:
- main
workflow_dispatch:
jobs:
# Release unpublished packages.
release-plz-release:
name: Release-plz release
environment: release
permissions:
pull-requests: write
contents: write
id-token: write
runs-on: ubuntu-latest
if: ${{ github.repository_owner == 'ratatui' }}
steps:
- name: Checkout repository
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
fetch-depth: 0
persist-credentials: false
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@6d653acede28d24f02e3cd41383119e8b1b35921 # master
with:
toolchain: stable
- uses: rust-lang/crates-io-auth-action@b7e9a28eded4986ec6b1fa40eeee8f8f165559ec # v1
id: auth
- name: Run release-plz
uses: release-plz/action@d529f731ae3e89610ada96eda34e5c6ba3b12214 # v0.5
with:
command: release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CARGO_REGISTRY_TOKEN: ${{ steps.auth.outputs.token }}
# Create a PR with the new versions and changelog, preparing the next release.
release-plz-pr:
name: Release-plz PR
permissions:
pull-requests: write
contents: write
runs-on: ubuntu-latest
if: ${{ github.repository_owner == 'ratatui' }}
concurrency:
group: release-plz-${{ github.ref }}
cancel-in-progress: false
steps:
- name: Checkout repository
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
fetch-depth: 0
persist-credentials: false
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@6d653acede28d24f02e3cd41383119e8b1b35921 # master
with:
toolchain: stable
- name: Run release-plz
uses: release-plz/action@d529f731ae3e89610ada96eda34e5c6ba3b12214 # v0.5
with:
command: release-pr
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_TOKEN }}

45
.github/workflows/release-stable.yml vendored Normal file
View File

@@ -0,0 +1,45 @@
name: Release stable version
on:
push:
tags:
- "v*.*.*"
jobs:
publish-stable:
name: Create an stable release
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Checkout the repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Generate a changelog
uses: orhun/git-cliff-action@v4
with:
config: cliff.toml
args: --latest --strip header
env:
OUTPUT: BODY.md
- name: Publish on GitHub
uses: ncipollo/release-action@v1
with:
prerelease: false
bodyFile: BODY.md
publish-crate:
name: Publish crate
runs-on: ubuntu-latest
steps:
- name: Checkout the repository
uses: actions/checkout@v4
- name: Install Rust stable
uses: dtolnay/rust-toolchain@stable
- name: Publish
run: cargo publish --token ${{ secrets.CARGO_TOKEN }}

View File

@@ -1,26 +0,0 @@
name: GitHub Actions Security Analysis with zizmor 🌈
# docs https://docs.zizmor.sh/integrations/#github-actions
on:
push:
branches: ["main"]
pull_request:
branches: ["**"]
permissions: {}
jobs:
zizmor:
name: Run zizmor 🌈
runs-on: ubuntu-latest
permissions:
security-events: write
steps:
- name: Checkout repository
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
persist-credentials: false
- name: Run zizmor 🌈
uses: zizmorcore/zizmor-action@e673c3917a1aef3c65c972347ed84ccd013ecda4 # v0.2.0

1
.gitignore vendored
View File

@@ -3,4 +3,3 @@ target
*.rs.rustfmt
.gdb_history
.idea/
.env

View File

@@ -1,12 +0,0 @@
include = ["**/*.toml"]
[formatting]
column_width = 100
reorder_keys = false
[[rule]]
include = ["**/Cargo.toml"]
keys = ["dependencies", "dev-dependencies", "build-dependencies", "workspace.dependencies"]
[rule.formatting]
reorder_keys = true

View File

@@ -1,203 +0,0 @@
# Ratatui Architecture
This document provides a comprehensive overview of Ratatui's architecture and crate
organization, introduced in version 0.30.0.
## Overview
Starting with Ratatui 0.30.0, the project was reorganized from a single monolithic crate into
a modular workspace consisting of multiple specialized crates. This architectural decision was
made to improve modularity, reduce compilation times, enable more flexible dependency
management, and provide better API stability for third-party widget libraries.
## Crate Organization
The Ratatui project is now organized as a Cargo workspace containing the following crates:
### Core Crates
#### `ratatui` (Main Crate)
- **Purpose**: The main entry point that most applications should use
- **Contents**: Re-exports everything from other crates for convenience, plus experimental features
- **Target Users**: Application developers building terminal UIs
- **Key Features**:
- Complete widget ecosystem
- Backend implementations
- Layout system
- Terminal management
- Experimental `WidgetRef` and `StatefulWidgetRef` traits
#### `ratatui-core`
- **Purpose**: Foundational types and traits for the Ratatui ecosystem
- **Contents**: Core widget traits, text types, buffer, layout, style, and symbols
- **Target Users**: Widget library authors, minimalist projects
- **Key Features**:
- `Widget` and `StatefulWidget` traits
- Text rendering (`Text`, `Line`, `Span`)
- Buffer management
- Layout system
- Style and color definitions
- Symbol collections
#### `ratatui-widgets`
- **Purpose**: Built-in widget implementations
- **Contents**: All standard widgets like `Block`, `Paragraph`, `List`, `Chart`, etc.
- **Target Users**: Applications needing standard widgets, widget library authors
- **Key Features**:
- Complete set of built-in widgets
- Optimized implementations
- Comprehensive documentation and examples
### Backend Crates
#### `ratatui-crossterm`
- **Purpose**: Crossterm backend implementation
- **Contents**: Cross-platform terminal backend using the `crossterm` crate
- **Target Users**: Applications targeting multiple platforms
#### `ratatui-termion`
- **Purpose**: Termion backend implementation
- **Contents**: Unix-specific terminal backend using the `termion` crate
- **Target Users**: Unix-specific applications requiring low-level control
#### `ratatui-termwiz`
- **Purpose**: Termwiz backend implementation
- **Contents**: Terminal backend using the `termwiz` crate
- **Target Users**: Applications needing advanced terminal features
### Utility Crates
#### `ratatui-macros`
- **Purpose**: Declarative macros for common patterns and boilerplate reduction
- **Contents**: Macros for common patterns and boilerplate reduction
- **Target Users**: Applications and libraries wanting macro support
## Dependency Relationships
```text
ratatui
├── ratatui-core
├── ratatui-widgets → ratatui-core
├── ratatui-crossterm → ratatui-core
├── ratatui-termion → ratatui-core
├── ratatui-termwiz → ratatui-core
└── ratatui-macros
```
### Key Dependencies
- **ratatui-core**: Foundation for all other crates
- **ratatui-widgets**: Depends on `ratatui-core` for widget traits and types
- **Backend crates**: Each depends on `ratatui-core` for backend traits and types
- **ratatui**: Depends on all other crates and re-exports their public APIs
## Design Principles
### Stability and Compatibility
The modular architecture provides different levels of API stability:
- **ratatui-core**: Designed for maximum stability to minimize breaking changes for widget
libraries
- **ratatui-widgets**: Focused on widget implementations with moderate stability requirements
- **Backend crates**: Isolated from core changes, allowing backend-specific updates
- **ratatui**: Main crate that can evolve more freely while maintaining backward compatibility
through re-exports
### Compilation Performance
The split architecture enables:
- **Reduced compilation times**: Widget libraries only need to compile core types
- **Parallel compilation**: Different crates can be compiled in parallel
- **Selective compilation**: Applications can exclude unused backends or widgets
### Ecosystem Benefits
- **Widget Library Authors**: Can depend on stable `ratatui-core` without frequent updates
- **Application Developers**: Can use the convenient `ratatui` crate with everything included
- **Minimalist Projects**: Can use only `ratatui-core` for lightweight applications
## Migration Guide
### For Application Developers
Most applications should continue using the main `ratatui` crate with minimal changes:
```rust
// No changes needed - everything is re-exported
use ratatui::{
widgets::{Block, Paragraph},
layout::{Layout, Constraint},
Terminal,
};
```
### For Widget Library Authors
Consider migrating to `ratatui-core` for better stability:
```rust
// Before (0.29.x and earlier)
use ratatui::{
widgets::{Widget, StatefulWidget},
buffer::Buffer,
layout::Rect,
};
// After (0.30.0+)
use ratatui_core::{
widgets::{Widget, StatefulWidget},
buffer::Buffer,
layout::Rect,
};
```
### Backwards Compatibility
All existing code using the `ratatui` crate will continue to work unchanged, as the main crate
re-exports all public APIs from the specialized crates.
## Future Considerations
### Potential Enhancements
- **Widget-specific crates**: Further split widgets into individual crates for even more
granular dependencies
- **Plugin system**: Enable dynamic widget loading and third-party widget ecosystems
- **Feature flags**: More granular feature flags for compile-time customization
### Version Synchronization
Currently, all crates are versioned together for simplicity. Future versions may adopt
independent versioning once the API stabilizes further.
## Related Issues and PRs
This architecture was developed through extensive discussion and implementation across multiple
PRs:
- [Issue #1388](https://github.com/ratatui/ratatui/issues/1388): Original RFC for modularization
- [PR #1459](https://github.com/ratatui/ratatui/pull/1459): Move ratatui crate into workspace
folder
- [PR #1460](https://github.com/ratatui/ratatui/pull/1460): Move core types to ratatui-core
- [PR #1474](https://github.com/ratatui/ratatui/pull/1474): Move widgets into ratatui-widgets
crate
## Contributing
When contributing to the Ratatui project, please consider:
- **Core changes**: Submit PRs against `ratatui-core` for fundamental improvements
- **Widget changes**: Submit PRs against `ratatui-widgets` for widget-specific improvements
- **Backend changes**: Submit PRs against the appropriate backend crate
- **Integration changes**: Submit PRs against the main `ratatui` crate
See the [CONTRIBUTING.md](CONTRIBUTING.md) guide for more details on the contribution process.

View File

@@ -10,26 +10,10 @@ GitHub with a [breaking change] label.
This is a quick summary of the sections below:
- [v0.30.0 Unreleased](#v0300-unreleased)
- `Flex::SpaceAround` now mirrors flexbox: space between items is twice the size of the outer gaps
are twice the size of first and last elements
- `block::Title` no longer exists
- [Unreleased](#unreleased)
- The `From` impls for backend types are now replaced with more specific traits
- `FrameExt` trait for `unstable-widget-ref` feature
- `List::highlight_symbol` now accepts `Into<Line>` instead of `&str`
- 'layout::Alignment' is renamed to 'layout::HorizontalAlignment'
- The MSRV is now 1.85.0
- `Backend` now requires an associated `Error` type and `clear_region` method
- `TestBackend` now uses `core::convert::Infallible` for error handling instead of `std::io::Error`
- Disabling `default-features` will now disable layout cache, which can have a negative impact on performance
- `Layout::init_cache` and `Layout::DEFAULT_CACHE_SIZE` are now only available if `layout-cache`
feature is enabled
- Disabling `default-features` suppresses the error message if `show_cursor()` fails when dropping
`Terminal`
- Support a broader range for `unicode-width` version
- [v0.29.0](#v0290)
- `Sparkline::data` takes `IntoIterator<Item = SparklineBar>` instead of `&[u64]` and is no longer
const
- `Sparkline::data` takes `IntoIterator<Item = SparklineBar>` instead of `&[u64]` and is no longer const
- Removed public fields from `Rect` iterators
- `Line` now implements `From<Cow<str>`
- `Table::highlight_style` is now `Table::row_highlight_style`
@@ -90,205 +74,7 @@ This is a quick summary of the sections below:
- MSRV is now 1.63.0
- `List` no longer ignores empty strings
## v0.30.0 Unreleased
### `Flex::SpaceAround` now mirrors flexbox: space between items is twice the size of the outer gaps ([#1952])
[#1952]: https://github.com/ratatui/ratatui/pull/1952
The old `Flex::SpaceAround` behavior has been changed to distribute space evenly around each
element, with the middle spacers being twice the size of the first and last one. The old
behavior can be achieved by using `Flex::SpaceEvenly` instead.
```diff
- let rects = Layout::horizontal([Length(1), Length(2)]).flex(Flex::SpaceAround).split(area);
+ let rects = Layout::horizontal([Length(1), Length(2)]).flex(Flex::SpaceEvenly).split(area);
```
### `block::Title` no longer exists ([#1926])
[#1926]: https://github.com/ratatui/ratatui/pull/1926
The title alignment is better expressed in the `Line` as this fits more coherently with the rest of
the library.
- `widgets::block` is no longer exported
- `widgets::block::Title` no longer exists
- `widgets::block::Position` is now `widgets::TitlePosition`
- `Block::title()` now accepts `Into::<Line>` instead of `Into<Title>`
- `BlockExt` is now exported at widgets::`BlockExt` instead of `widgets::block::BlockExt`
```diff
- use ratatui::widgets::{Block, block::{Title, Position}};
+ use ratatui::widgets::{Block, TitlePosition};
let block = Block::default()
- .title(Title::from("Hello"))
- .title(Title::from("Hello").position(Position::Bottom).alignment(Alignment::Center))
- .title_position(Position::Bottom);
+ .title(Line::from("Hello"))
+ .title_bottom(Line::from("Hello").centered());
+ .title_position(TitlePosition::Bottom);
- use ratatui::widgets::block::BlockExt;
+ use ratatui::widgets::BlockExt;
struct MyWidget {
block: Option<Block>,
}
impl Widget for &MyWidget {
fn render(self, area: Rect, buf: &mut Buffer) {
self.block.as_ref().render(area, buf);
let area = self.block.inner_if_some();
// ...
}
}
```
### `Style` no longer implements `Styled` ([#1572])
[#1572]: https://github.com/ratatui/ratatui/pull/1572
Any calls to methods implemented by the blanket implementation of `Stylize` are now defined directly
on `Style`. Remove the `Stylize` import if it is no longer used by your code.
```diff
- use ratatui::style::Stylize;
let style = Style::new().red();
```
The `reset()` method does not have a direct replacement, as it clashes with the existing `reset()`
method. Use the `Style::reset()` method instead.
```diff
- some_style.reset();
+ Style::reset();
```
### Disabling `default-features` suppresses the error message if `show_cursor()` fails when dropping `Terminal` ([#1794])
[#1794]: https://github.com/ratatui/ratatui/pull/1794
Since disabling `default-features` disables `std`, printing to stderr is not possible. It is
recommended to re-enable `std` when not using Ratatui in `no_std` environment.
### `Layout::init_cache` and `Layout::DEFAULT_CACHE_SIZE` are now only available if `layout-cache` feature is enabled ([#1795])
[#1795]: https://github.com/ratatui/ratatui/pull/1795
Previously, `Layout::init_cache` and `Layout::DEFAULT_CACHE_SIZE` were available independently of
enabled feature flags.
### Disabling `default-features` will now disable layout cache, which can have a negative impact on performance ([#1795])
Layout cache is now opt-in in `ratatui-core` and enabled by default in `ratatui`. If app doesn't
make use of `no_std`-compatibility, and disables `default-feature`, it is recommended to explicitly
re-enable layout cache. Not doing so may impact performance.
```diff
- ratatui = { version = "0.29.0", default-features = false }
+ ratatui = { version = "0.30.0", default-features = false, features = ["layout-cache"] }
```
### `TestBackend` now uses `core::convert::Infallible` for error handling instead of `std::io::Error` ([#1823])
[#1823]: https://github.com/ratatui/ratatui/pull/1823
Since `TestBackend` never fails, it now uses `Infallible` as associated `Error`. This may require
changes in test cases that use `TestBackend`.
### `Backend` now requires an associated `Error` type and `clear_region` method ([#1778])
[#1778]: https://github.com/ratatui/ratatui/pull/1778
Custom `Backend` implementations must now define an associated `Error` type for method `Result`s
and implement the `clear_region` method, which no longer has a default implementation.
This change was made to provide greater flexibility for custom backends, particularly to remove the
explicit dependency on `std::io` for backends that want to support `no_std` targets.
If your app or library uses the `Backend` trait directly - for example, by providing a generic
implementation for many backends - you may need to update the referenced error type.
```diff
- fn run<B: Backend>(mut terminal: Terminal<B>) -> io::Result<()> {
+ fn run<B: Backend>(mut terminal: Terminal<B>) -> Result<(), B::Error> {
```
Alternatively, you can explicitly require the associated error to be `std::io::Error`. This approach
may require fewer changes in user code but is generally not recommended, as it limits compatibility
with third-party backends. Additionally, the error type used by built-in backends may or may not
change in the future, making this approach less future-proof compared to the previous one.
```diff
- fn run<B: Backend>(mut terminal: Terminal<B>) -> io::Result<()> {
+ fn run<B: Backend<Error = io::Error>>(mut terminal: Terminal<B>) -> io::Result<()> {
```
If your application uses a concrete backend implementation, prefer specifying it explicitly
instead.
```diff
- fn run<B: Backend>(mut terminal: Terminal<B>) -> io::Result<()> {
+ fn run(mut terminal: DefaultTerminal) -> io::Result<()> {
```
### The MSRV is now 1.85.0 ([#1860])
[#1860]: https://github.com/ratatui/ratatui/pull/1860
The minimum supported Rust version (MSRV) is now 1.85.0.
### `layout::Alignment` is renamed to `layout::HorizontalAlignment` ([#1735])
[#1735]: https://github.com/ratatui/ratatui/pull/1691
The `Alignment` enum has been renamed to `HorizontalAlignment` to better reflect its purpose. A type
alias has been added to maintain backwards compatibility, however there are some cases where type
aliases are not enough to maintain backwards compatibility. E.g. when using glob imports to import
all the enum variants.
We don't expect to remove or deprecate the type alias in the near future, but it is recommended to
update your imports to use the new name.
```diff
- use ratatui::layout::Alignment;
+ use ratatui::layout::HorizontalAlignment;
- use Alignment::*;
+ use HorizontalAlignment::*;
```
### `List::highlight_symbol` accepts `Into<Line>` ([#1595])
[#1595]: https://github.com/ratatui/ratatui/pull/1595
Previously `List::highlight_symbol` accepted `&str`. Any code that uses conversion methods will need
to be rewritten. Since `Into::into` is not const, this function cannot be called in const context.
### `FrameExt` trait for `unstable-widget-ref` feature ([#1530])
[#1530]: https://github.com/ratatui/ratatui/pull/1530
To call `Frame::render_widget_ref()` or `Frame::render_stateful_widget_ref()` you now need to:
1. Import the `FrameExt` trait from `ratatui::widgets`.
2. Enable the `unstable-widget-ref` feature.
For example:
```rust
use ratatui::{
layout::Rect,
widgets::{Block, FrameExt},
};
let block = Block::new();
let area = Rect::new(0, 0, 5, 5);
frame.render_widget_ref(&block, area);
```
## Unreleased (0.30.0)
### `WidgetRef` no longer has a blanket implementation of Widget
@@ -369,26 +155,6 @@ for `Bar::text_value()`:
+ Bar::default().text_value("foobar");
```
### `termwiz` is upgraded to 0.23.0 ([#1682])
[#1682]: https://github.com/ratatui/ratatui/pull/1682
The `termwiz` backend is upgraded from 0.22.0 to 0.23.0.
This release has a few fixes for hyperlinks and input handling, plus some dependency updates.
See the [commits](https://github.com/wezterm/wezterm/commits/main/termwiz) for more details.
### Support a broader range for `unicode-width` version ([#1999])
[#1999]: https://github.com/ratatui/ratatui/pull/1999
Ratatui's dependency on `unicode-width`, previously pinned to 0.2.0, has
expanded to allow version 0.2.1. This comes with 2 behavior changes described in
[unicode-width#61] and [unicode-width#74].
[unicode-width#61]: https://github.com/unicode-rs/unicode-width/pull/61
[unicode-width#74]: https://github.com/unicode-rs/unicode-width/pull/74
## [v0.29.0](https://github.com/ratatui/ratatui/releases/tag/v0.29.0)
### `Sparkline::data` takes `IntoIterator<Item = SparklineBar>` instead of `&[u64]` and is no longer const ([#1326])

File diff suppressed because it is too large Load Diff

View File

@@ -6,23 +6,6 @@ If your contribution is not straightforward, please first discuss the change you
creating a new issue before making the change, or starting a discussion on
[discord](https://discord.gg/pMCEU9hNEj).
## AI Generated Content
We welcome high quality PRs, whether they are human generated or made with the assistance of AI
tools, but we ask that you follow these guidelines:
- **Attribution**: Tell us about your use of AI tools, don't make us guess whether you're using it.
- **Review**: Make sure you review every line of AI generated content for correctness and relevance.
- **Quality**: AI-generated content should meet the same quality standards as human-written content.
- **Quantity**: Avoid submitting large amounts of AI generated content in a single PR. Remember that
quality is usually more important than quantity.
- **License**: Ensure that the AI-generated content is compatible with Ratatui's
[License](./LICENSE).
> [!IMPORTANT]
> Remember that AI tools can assist in generating content, but you are responsible for the final
> quality and accuracy of the contributions and communicating about it.
## Reporting issues
Before reporting an issue on the [issue tracker](https://github.com/ratatui/ratatui/issues),
@@ -39,48 +22,17 @@ on the crate or its users should ideally be discussed in a "Feature Request" iss
### Keep PRs small, intentional, and focused
Try to do one pull request per change. The time taken to review a PR grows exponentially with the size
of the change. Small focused PRs will generally be much faster to review. PRs that include both
Try to do one pull request per change. The time taken to review a PR grows exponential with the size
of the change. Small focused PRs will generally be much more faster to review. PRs that include both
refactoring (or reformatting) with actual changes are more difficult to review as every line of the
change becomes a place where a bug may have been introduced. Consider splitting refactoring /
reformatting changes into a separate PR from those that make a behavioral change, as the tests help
guarantee that the behavior is unchanged.
Guidelines for PR size:
- Aim for PRs under 500 lines of changes when possible.
- Split large features into incremental PRs that build on each other.
- Separate refactoring, formatting, and functional changes into different PRs.
- If a large PR is unavoidable, clearly explain why in the PR description.
### Breaking changes and backwards compatibility
We prioritize maintaining backwards compatibility and minimizing disruption to users:
- **Prefer deprecation over removal**: Add deprecation warnings rather than immediately removing
public APIs
- **Provide migration paths**: Include clear upgrade instructions for any breaking changes
- **Follow our deprecation policy**: Wait at least two versions before removing deprecated items
- **Consider feature flags**: Use feature flags for experimental or potentially disruptive changes
- **Document breaking changes**: Clearly mark breaking changes in commit messages and PR descriptions
See our [deprecation notice policy](#deprecation-notice) for more details.
### Code formatting
Run `cargo xtask format` before committing to ensure that code is consistently formatted with
rustfmt. Configuration is in [`rustfmt.toml`](./rustfmt.toml). We use some unstable formatting
options as they lead to subjectively better formatting. These require a nightly version of Rust
to be installed when running rustfmt. You can install the nightly version of Rust using
[`rustup`](https://rustup.rs/):
```shell
rustup install nightly
```
> [!IMPORTANT]
> Do not modify formatting configuration (`rustfmt.toml`, `.clippy.toml`) without
> prior discussion. These changes affect all contributors and should be carefully considered.
rustfmt. Configuration is in [`rustfmt.toml`](./rustfmt.toml).
### Search `tui-rs` for similar work
@@ -108,15 +60,6 @@ Running `cargo xtask ci` before pushing will perform the same checks that we do
It's not mandatory to do this before pushing, however it may save you time to do so instead of
waiting for GitHub to run the checks.
Available xtask commands:
- `cargo xtask ci` - Run all CI checks
- `cargo xtask format` - Format code
- `cargo xtask lint` - Run linting checks
- `cargo xtask test` - Run all tests
Run `cargo xtask --help` to see all available commands.
### Sign your commits
We use commit signature verification, which will block commits from being merged via the UI unless
@@ -124,25 +67,6 @@ they are signed. To set up your machine to sign commits, see [managing commit si
verification](https://docs.github.com/en/authentication/managing-commit-signature-verification/about-commit-signature-verification)
in GitHub docs.
### Configuration and build system changes
Changes to project configuration files require special consideration:
- Linting configuration (`.clippy.toml`, `rustfmt.toml`): Affects all contributors.
- CI configuration (`.github/workflows/`): Affects build and deployment.
- Build system (`xtask/`, `Cargo.toml` workspace config): Affects development workflow.
- Dependencies: Consider MSRV compatibility and licensing.
Please discuss these changes in an issue before implementing them.
### Collaborative development
We may occasionally make changes directly to your branch—such as force-pushes—to help move a PR
forward, speed up review, or ensure it meets our quality standards. If you would prefer we do not do
this, or if your workflow depends on us avoiding force-pushes (for example, if your app points to
your branch in `Cargo.toml`), please mention this in your PR description and we will respect your
preference.
## Implementation Guidelines
### Setup
@@ -160,20 +84,6 @@ cd ratatui
cargo xtask build
```
### Architecture
For an understanding of the crate organization and design decisions, see [ARCHITECTURE.md]. This
document explains the modular workspace structure introduced in version 0.30.0 and provides
guidance on which crate to use for different use cases.
When making changes, consider:
- Which crate should contain your changes per the modular structure,
- Whether your changes affect the public API of `ratatui-core` (requires extra care),
- And how your changes fit into the overall architecture.
[ARCHITECTURE.md]: https://github.com/ratatui/ratatui/blob/main/ARCHITECTURE.md
### Tests
The [test coverage](https://app.codecov.io/gh/ratatui/ratatui) of the crate is reasonably
@@ -181,7 +91,7 @@ good, but this can always be improved. Focus on keeping the tests simple and obv
tests for all new or modified code. Beside the usual doc and unit tests, one of the most valuable
test you can write for Ratatui is a test against the `TestBackend`. It allows you to assert the
content of the output buffer that would have been flushed to the terminal after a given draw call.
See `widgets_block_renders` in [ratatui/tests/widgets_block.rs](./ratatui/tests/widgets_block.rs) for an example.
See `widgets_block_renders` in [tests/widgets_block.rs](./tests/widget_block.rs) for an example.
When writing tests, generally prefer to write unit tests and doc tests directly in the code file
being tested rather than integration tests in the `tests/` folder.
@@ -190,10 +100,6 @@ If an area that you're making a change in is not tested, write tests to characte
behavior before changing it. This helps ensure that we don't introduce bugs to existing software
using Ratatui (and helps make it easy to migrate apps still using `tui-rs`).
> [!IMPORTANT]
> Do not remove existing tests without clear justification. If tests need to be
> modified due to API changes, explain why in your PR description.
For coverage, we have two [bacon](https://dystroy.org/bacon/) jobs (one for all tests, and one for
unit tests, keyboard shortcuts `v` and `u` respectively) that run
[cargo-llvm-cov](https://github.com/taiki-e/cargo-llvm-cov) to report the coverage. Several plugins
@@ -261,14 +167,6 @@ We generally want to wait at least two versions before removing deprecated items
time to update. However, if a deprecation is blocking for us to implement a new feature we may
*consider* removing it in a one version notice.
Deprecation process:
1. Add `#[deprecated]` attribute with a clear message.
2. Update documentation to point to the replacement.
3. Add an entry to `BREAKING-CHANGES.md` if applicable.
4. Wait at least two versions before removal.
5. Consider the impact on the ecosystem before removing.
### Use of unsafe for optimization purposes
We don't currently use any unsafe code in Ratatui, and would like to keep it that way. However, there

2272
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,17 +1,14 @@
[workspace]
resolver = "2"
members = ["ratatui", "ratatui-*", "xtask", "examples/apps/*", "examples/concepts/*"]
members = ["ratatui", "ratatui-*", "xtask"]
default-members = [
"ratatui",
"ratatui-core",
"ratatui-crossterm",
# this is not included as it doesn't compile on windows
# "ratatui-termion",
"ratatui-macros",
"ratatui-termwiz",
"ratatui-widgets",
"examples/apps/*",
"examples/concepts/*",
"ratatui",
"ratatui-core",
"ratatui-crossterm",
# this is not included as it doesn't compile on windows
# "ratatui-termion",
"ratatui-termwiz",
"ratatui-widgets",
]
[workspace.package]
@@ -24,100 +21,35 @@ categories = ["command-line-interface"]
readme = "README.md"
license = "MIT"
exclude = ["assets/*", ".github", "Makefile.toml", "CONTRIBUTING.md", "*.log", "tags"]
edition = "2024"
rust-version = "1.85.0"
edition = "2021"
rust-version = "1.74.0"
[workspace.dependencies]
anstyle = "1"
bitflags = "2.10"
clap = { version = "4.5", features = ["derive"] }
color-eyre = "0.6"
compact_str = { version = "0.9", default-features = false }
criterion = { version = "0.7", features = ["html_reports"] }
crossterm = "0.29"
document-features = "0.2"
fakeit = "1"
futures = "0.3"
hashbrown = "0.16"
indoc = "2"
instability = "0.3"
itertools = { version = "0.14", default-features = false, features = ["use_alloc"] }
kasuari = { version = "0.4", default-features = false }
line-clipping = "0.3"
lru = "0.16"
octocrab = "0.47"
palette = "0.7"
pretty_assertions = "1"
rand = "0.9"
rand_chacha = "0.9"
ratatui = { path = "ratatui", version = "0.30.0-beta.0" }
ratatui-core = { path = "ratatui-core", version = "0.1.0-beta.0" }
ratatui-crossterm = { path = "ratatui-crossterm", version = "0.1.0-beta.0" }
ratatui-macros = { path = "ratatui-macros", version = "0.7.0-beta.0" }
ratatui-termion = { path = "ratatui-termion", version = "0.1.0-beta.0" }
ratatui-termwiz = { path = "ratatui-termwiz", version = "0.1.0-beta.0" }
ratatui-widgets = { path = "ratatui-widgets", version = "0.3.0-beta.0" }
rstest = "0.26"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
strum = { version = "0.27", default-features = false, features = ["derive"] }
termion = "4"
termwiz = "0.23"
thiserror = { version = "2", default-features = false }
time = { version = "0.3", default-features = false }
tokio = "1"
tokio-stream = "0.1"
tracing = "0.1"
tracing-appender = "0.2"
tracing-subscriber = "0.3"
trybuild = "1"
unicode-segmentation = "1"
unicode-truncate = { version = "2", default-features = false }
bitflags = "2.6.0"
color-eyre = "0.6.3"
crossterm = "0.28.1"
document-features = "0.2.7"
indoc = "2.0.5"
instability = "0.3.3"
itertools = "0.13.0"
pretty_assertions = "1.4.1"
ratatui = { path = "ratatui", version = "0.30.0-alpha.0" }
ratatui-core = { path = "ratatui-core", version = "0.1.0-alpha.0" }
ratatui-crossterm = { path = "ratatui-crossterm", version = "0.1.0-alpha.0" }
ratatui-termion = { path = "ratatui-termion", version = "0.1.0-alpha.0" }
ratatui-termwiz = { path = "ratatui-termwiz", version = "0.1.0-alpha.0" }
ratatui-widgets = { path = "ratatui-widgets", version = "0.3.0-alpha.0" }
rstest = "0.23.0"
serde = { version = "1.0.215", features = ["derive"] }
serde_json = "1.0.133"
strum = { version = "0.26.3", features = ["derive"] }
termwiz = { version = "0.22.0" }
unicode-segmentation = "1.12.0"
# See <https://github.com/ratatui/ratatui/issues/1271> for information about why we pin unicode-width
unicode-width = ">=0.2.0, <=0.2.2"
unicode-width = "=0.2.0"
termion = "4.0.0"
# Improve benchmark consistency
[profile.bench]
codegen-units = 1
lto = true
[workspace.lints.rust]
unsafe_code = "forbid"
[workspace.lints.clippy]
pedantic = { level = "warn", priority = -1 }
cast_possible_truncation = "allow"
cast_possible_wrap = "allow"
cast_precision_loss = "allow"
cast_sign_loss = "allow"
missing_errors_doc = "allow"
missing_panics_doc = "allow"
module_name_repetitions = "allow"
must_use_candidate = "allow"
# we often split up a module into multiple files with the main type in a file named after the
# module, so we want to allow this pattern
module_inception = "allow"
# nursery or restricted
as_underscore = "warn"
deref_by_slicing = "warn"
else_if_without_else = "warn"
empty_line_after_doc_comments = "warn"
equatable_if_let = "warn"
fn_to_numeric_cast_any = "warn"
format_push_string = "warn"
implicit_clone = "warn"
map_err_ignore = "warn"
missing_const_for_fn = "warn"
mixed_read_write_in_expression = "warn"
mod_module_files = "warn"
needless_pass_by_ref_mut = "warn"
needless_raw_strings = "warn"
or_fun_call = "warn"
redundant_type_annotations = "warn"
rest_pat_in_fully_bound_structs = "warn"
string_lit_chars_any = "warn"
string_slice = "warn"
unnecessary_self_imports = "warn"
use_self = "warn"

View File

@@ -1,7 +1,7 @@
The MIT License (MIT)
Copyright (c) 2016-2022 Florian Dehau
Copyright (c) 2023-2025 The Ratatui Developers
Copyright (c) 2023-2024 The Ratatui Developers
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@@ -5,7 +5,7 @@ This file documents current and past maintainers.
- [orhun](https://github.com/orhun)
- [joshka](https://github.com/joshka)
- [kdheepak](https://github.com/kdheepak)
- [j-g00da](https://github.com/j-g00da)
- [Valentin271](https://github.com/Valentin271)
## Past Maintainers
@@ -13,4 +13,3 @@ This file documents current and past maintainers.
- [mindoodoo](https://github.com/mindoodoo)
- [sayanarijit](https://github.com/sayanarijit)
- [EdJoPaTo](https://github.com/EdJoPaTo)
- [Valentin271](https://github.com/Valentin271)

422
README.md
View File

@@ -1,182 +1,380 @@
<details>
<summary>Table of Contents</summary>
- [Quickstart](#quickstart)
- [Documentation](#documentation)
- [Templates](#templates)
- [Built with Ratatui](#built-with-ratatui)
- [Alternatives](#alternatives)
- [Contributing](#contributing)
- [Acknowledgements](#acknowledgements)
- [License](#license)
- [Ratatui](#ratatui)
- [Quick Start](#quickstart)
- [Other documentation](#other-documentation)
- [Introduction](#introduction)
- [Quickstart](#quickstart)
- [Initialize and restore the terminal](#initialize-and-restore-the-terminal)
- [Drawing the UI](#drawing-the-ui)
- [Handling events](#handling-events)
- [Layout](#layout)
- [Text and styling](#text-and-styling)
- [Status of this fork](#status-of-this-fork)
- [Widgets](#widgets)
- [Built in](#built-in)
- [Third-party libraries, bootstrapping templates and widgets](#third-party-libraries-bootstrapping-templates-and-widgets)
- [Apps](#apps)
- [Alternatives](#alternatives)
- [Acknowledgments](#acknowledgments)
- [License](#license)
</details>
![Release header](https://github.com/ratatui/ratatui/blob/b23480adfa9430697071c906c7ba4d4f9bd37a73/assets/release-header.png?raw=true)
<!-- cargo-rdme start -->
![Demo](https://github.com/ratatui/ratatui/blob/87ae72dbc756067c97f6400d3e2a58eeb383776e/examples/demo2-destroy.gif?raw=true)
<div align="center">
[![Crate Badge]][Crate] [![Repo Badge]][Repo] [![Docs Badge]][Docs] [![License Badge]][License] \
[![CI Badge]][CI] [![Deps Badge]][Deps] [![Codecov Badge]][Codecov] [![Sponsors Badge]][Sponsors] \
[Ratatui Website] · [Docs] · [Widget Examples] · [App Examples] · [Changelog] \
[Breaking Changes] · [Contributing] · [Report a bug] · [Request a Feature]
[![Crate Badge]][Crate] [![Docs Badge]][API Docs] [![CI Badge]][CI Workflow] [![Deps.rs
Badge]][Deps.rs]<br> [![Codecov Badge]][Codecov] [![License Badge]](./LICENSE) [![Sponsors
Badge]][GitHub Sponsors]<br> [![Discord Badge]][Discord Server] [![Matrix Badge]][Matrix]
[![Forum Badge]][Forum]<br>
[Ratatui Website] · [API Docs] · [Examples] · [Changelog] · [Breaking Changes]<br>
[Contributing] · [Report a bug] · [Request a Feature] · [Create a Pull Request]
</div>
[Ratatui][Ratatui Website] (_ˌræ.təˈtu.i_) is a Rust crate for cooking up terminal user interfaces
(TUIs). It provides a simple and flexible way to create text-based user interfaces in the terminal,
which can be used for command-line applications, dashboards, and other interactive console programs.
# Ratatui
[Ratatui][Ratatui Website] is a crate for cooking up terminal user interfaces in Rust. It is a
lightweight library that provides a set of widgets and utilities to build complex Rust TUIs.
Ratatui was forked from the [tui-rs] crate in 2023 in order to continue its development.
## Quickstart
Ratatui has [templates] available to help you get started quickly. You can use the
[`cargo-generate`] command to create a new project with Ratatui:
Add `ratatui` and `crossterm` as dependencies to your cargo.toml:
```shell
cargo install --locked cargo-generate
cargo generate ratatui/templates
cargo add ratatui crossterm
```
Selecting the Hello World template produces the following application:
Then you can create a simple "Hello World" application:
```rust
use color_eyre::Result;
use crossterm::event::{self, Event};
use ratatui::{DefaultTerminal, Frame};
use ratatui::{text::Text, Frame};
fn main() -> Result<()> {
color_eyre::install()?;
let terminal = ratatui::init();
let result = run(terminal);
fn main() {
let mut terminal = ratatui::init();
loop {
terminal.draw(draw).expect("failed to draw frame");
if matches!(event::read().expect("failed to read event"), Event::Key(_)) {
break;
}
}
ratatui::restore();
}
fn draw(frame: &mut Frame) {
let text = Text::raw("Hello World!");
frame.render_widget(text, frame.area());
}
```
The full code for this example which contains a little more detail is in the [Examples]
directory. For more guidance on different ways to structure your application see the
[Application Patterns] and [Hello World tutorial] sections in the [Ratatui Website] and the
various [Examples]. There are also several starter templates available in the [templates]
repository.
## Other documentation
- [Ratatui Website] - explains the library's concepts and provides step-by-step tutorials
- [Ratatui Forum][Forum] - a place to ask questions and discuss the library
- [API Docs] - the full API documentation for the library on docs.rs.
- [Examples] - a collection of examples that demonstrate how to use the library.
- [Contributing] - Please read this if you are interested in contributing to the project.
- [Changelog] - generated by [git-cliff] utilizing [Conventional Commits].
- [Breaking Changes] - a list of breaking changes in the library.
You can also watch the [FOSDEM 2024 talk] about Ratatui which gives a brief introduction to
terminal user interfaces and showcases the features of Ratatui, along with a hello world demo.
## Introduction
Ratatui is based on the principle of immediate rendering with intermediate buffers. This means
that for each frame, your app must render all widgets that are supposed to be part of the UI.
This is in contrast to the retained mode style of rendering where widgets are updated and then
automatically redrawn on the next frame. See the [Rendering] section of the [Ratatui Website]
for more info.
Ratatui uses [Crossterm] by default as it works on most platforms. See the [Installation]
section of the [Ratatui Website] for more details on how to use other backends ([Termion] /
[Termwiz]).
Every application built with `ratatui` needs to implement the following steps:
- Initialize the terminal
- A main loop that:
- Draws the UI
- Handles input events
- Restore the terminal state
### Initialize and restore the terminal
The [`Terminal`] type is the main entry point for any Ratatui application. It is generic over a
a choice of [`Backend`] implementations that each provide functionality to draw frames, clear
the screen, hide the cursor, etc. There are backend implementations for [Crossterm], [Termion]
and [Termwiz].
The simplest way to initialize the terminal is to use the [`init`] function which returns a
[`DefaultTerminal`] instance with the default options, enters the Alternate Screen and Raw mode
and sets up a panic hook that restores the terminal in case of panic. This instance can then be
used to draw frames and interact with the terminal state. (The [`DefaultTerminal`] instance is a
type alias for a terminal with the [`crossterm`] backend.) The [`restore`] function restores the
terminal to its original state.
```rust
fn main() -> std::io::Result<()> {
let mut terminal = ratatui::init();
let result = run(&mut terminal);
ratatui::restore();
result
}
```
fn run(mut terminal: DefaultTerminal) -> Result<()> {
See the [`backend` module] and the [Backends] section of the [Ratatui Website] for more info on
the alternate screen and raw mode.
### Drawing the UI
Drawing the UI is done by calling the [`Terminal::draw`] method on the terminal instance. This
method takes a closure that is called with a [`Frame`] instance. The [`Frame`] provides the size
of the area to draw to and allows the app to render any [`Widget`] using the provided
[`render_widget`] method. After this closure returns, a diff is performed and only the changes
are drawn to the terminal. See the [Widgets] section of the [Ratatui Website] for more info.
The closure passed to the [`Terminal::draw`] method should handle the rendering of a full frame.
```rust
use ratatui::{widgets::Paragraph, Frame};
fn run(terminal: &mut ratatui::DefaultTerminal) -> std::io::Result<()> {
loop {
terminal.draw(render)?;
if matches!(event::read()?, Event::Key(_)) {
terminal.draw(|frame| draw(frame))?;
if handle_events()? {
break Ok(());
}
}
}
fn render(frame: &mut Frame) {
frame.render_widget("hello world", frame.area());
fn draw(frame: &mut Frame) {
let text = Paragraph::new("Hello World!");
frame.render_widget(text, frame.area());
}
```
## Documentation
### Handling events
- [Docs] - the full API documentation for the library on docs.rs.
- [Ratatui Website] - explains the library's concepts and provides step-by-step tutorials.
- [Ratatui Forum] - a place to ask questions and discuss the library.
- [Widget Examples] - a collection of examples that demonstrate how to use the library.
- [App Examples] - a collection of more complex examples that demonstrate how to build apps.
- [ARCHITECTURE.md] - explains the crate organization and modular workspace structure.
- [Changelog] - generated by [git-cliff] utilizing [Conventional Commits].
- [Breaking Changes] - a list of breaking changes in the library.
Ratatui does not include any input handling. Instead event handling can be implemented by
calling backend library methods directly. See the [Handling Events] section of the [Ratatui
Website] for more info. For example, if you are using [Crossterm], you can use the
[`crossterm::event`] module to handle events.
You can also watch the [EuroRust 2024 talk] to learn about common concepts in Ratatui and what's
possible to build with it.
```rust
use crossterm::event::{self, Event, KeyCode, KeyEvent, KeyEventKind};
## Templates
If you're looking to get started quickly, you can use one of the available templates from the
[templates] repository using [`cargo-generate`]:
```shell
cargo generate ratatui/templates
fn handle_events() -> std::io::Result<bool> {
match event::read()? {
Event::Key(key) if key.kind == KeyEventKind::Press => match key.code {
KeyCode::Char('q') => return Ok(true),
// handle other key events
_ => {}
},
// handle other events
_ => {}
}
Ok(false)
}
```
## Built with Ratatui
## Layout
[![Awesome](https://awesome.re/badge-flat2.svg)][awesome-ratatui]
The library comes with a basic yet useful layout management object called [`Layout`] which
allows you to split the available space into multiple areas and then render widgets in each
area. This lets you describe a responsive terminal UI by nesting layouts. See the [Layout]
section of the [Ratatui Website] for more info.
Check out the [showcase] section of the website, or the [awesome-ratatui] repository for a curated
list of awesome apps and libraries built with Ratatui!
```rust
use ratatui::{
layout::{Constraint, Layout},
widgets::Block,
Frame,
};
## Alternatives
fn draw(frame: &mut Frame) {
use Constraint::{Fill, Length, Min};
- [Cursive](https://crates.io/crates/cursive) - a ncurses-based TUI library.
- [iocraft](https://crates.io/crates/iocraft) - a declarative TUI library.
let vertical = Layout::vertical([Length(1), Min(0), Length(1)]);
let [title_area, main_area, status_area] = vertical.areas(frame.area());
let horizontal = Layout::horizontal([Fill(1); 2]);
let [left_area, right_area] = horizontal.areas(main_area);
## Contributing
[![Discord Badge]][Discord Server] [![Matrix Badge]][Matrix] [![Forum Badge]][Ratatui Forum]
Feel free to join our [Discord server](https://discord.gg/pMCEU9hNEj) for discussions and questions!
There is also a [Matrix](https://matrix.org/) bridge available at
[#ratatui:matrix.org](https://matrix.to/#/#ratatui:matrix.org). We have also recently launched the
[Ratatui Forum].
We rely on GitHub for [bugs][Report a bug] and [feature requests][Request a Feature].
Please make sure you read the [contributing](./CONTRIBUTING.md) guidelines before [creating a pull
request][Create a Pull Request]. We accept AI generated code, but please read the [AI Contributions]
guidelines to ensure compliance.
If you'd like to show your support, you can add the Ratatui badge to your project's README:
```md
[![Built With Ratatui](https://img.shields.io/badge/Built_With_Ratatui-000?logo=ratatui&logoColor=fff)](https://ratatui.rs/)
frame.render_widget(Block::bordered().title("Title Bar"), title_area);
frame.render_widget(Block::bordered().title("Status Bar"), status_area);
frame.render_widget(Block::bordered().title("Left"), left_area);
frame.render_widget(Block::bordered().title("Right"), right_area);
}
```
[![Built With Ratatui](https://img.shields.io/badge/Built_With_Ratatui-000?logo=ratatui&logoColor=fff)](https://ratatui.rs/)
Running this example produces the following output:
## Acknowledgements
```text
Title Bar───────────────────────────────────
┌Left────────────────┐┌Right───────────────┐
│ ││ │
└────────────────────┘└────────────────────┘
Status Bar──────────────────────────────────
```
Ratatui was forked from the [tui-rs] crate in 2023 in order to continue its development. None of
this could be possible without [Florian Dehau] who originally created [tui-rs] which inspired many
Rust TUIs.
## Text and styling
Special thanks to [Pavel Fomchenkov] for his work in designing an awesome logo for the Ratatui
project and organization.
The [`Text`], [`Line`] and [`Span`] types are the building blocks of the library and are used in
many places. [`Text`] is a list of [`Line`]s and a [`Line`] is a list of [`Span`]s. A [`Span`]
is a string with a specific style.
## License
The [`style` module] provides types that represent the various styling options. The most
important one is [`Style`] which represents the foreground and background colors and the text
attributes of a [`Span`]. The [`style` module] also provides a [`Stylize`] trait that allows
short-hand syntax to apply a style to widgets and text. See the [Styling Text] section of the
[Ratatui Website] for more info.
This project is licensed under the [MIT License][License].
```rust
use ratatui::{
layout::{Constraint, Layout},
style::{Color, Modifier, Style, Stylize},
text::{Line, Span},
widgets::{Block, Paragraph},
Frame,
};
fn draw(frame: &mut Frame) {
let areas = Layout::vertical([Constraint::Length(1); 4]).split(frame.area());
let line = Line::from(vec![
Span::raw("Hello "),
Span::styled(
"World",
Style::new()
.fg(Color::Green)
.bg(Color::White)
.add_modifier(Modifier::BOLD),
),
"!".red().on_light_yellow().italic(),
]);
frame.render_widget(line, areas[0]);
// using the short-hand syntax and implicit conversions
let paragraph = Paragraph::new("Hello World!".red().on_white().bold());
frame.render_widget(paragraph, areas[1]);
// style the whole widget instead of just the text
let paragraph = Paragraph::new("Hello World!").style(Style::new().red().on_white());
frame.render_widget(paragraph, areas[2]);
// use the simpler short-hand syntax
let paragraph = Paragraph::new("Hello World!").blue().on_yellow();
frame.render_widget(paragraph, areas[3]);
}
```
[Repo]: https://github.com/ratatui/ratatui
[Ratatui Website]: https://ratatui.rs/
[Ratatui Forum]: https://forum.ratatui.rs
[Docs]: https://docs.rs/ratatui
[Widget Examples]: https://github.com/ratatui/ratatui/tree/main/ratatui-widgets/examples
[App Examples]: https://github.com/ratatui/ratatui/tree/main/examples
[ARCHITECTURE.md]: https://github.com/ratatui/ratatui/blob/main/ARCHITECTURE.md
[Changelog]: https://github.com/ratatui/ratatui/blob/main/CHANGELOG.md
[git-cliff]: https://git-cliff.org
[Conventional Commits]: https://www.conventionalcommits.org
[Breaking Changes]: https://github.com/ratatui/ratatui/blob/main/BREAKING-CHANGES.md
[EuroRust 2024 talk]: https://www.youtube.com/watch?v=hWG51Mc1DlM
[Installation]: https://ratatui.rs/installation/
[Rendering]: https://ratatui.rs/concepts/rendering/
[Application Patterns]: https://ratatui.rs/concepts/application-patterns/
[Hello World tutorial]: https://ratatui.rs/tutorials/hello-world/
[Backends]: https://ratatui.rs/concepts/backends/
[Widgets]: https://ratatui.rs/how-to/widgets/
[Handling Events]: https://ratatui.rs/concepts/event-handling/
[Layout]: https://ratatui.rs/how-to/layout/
[Styling Text]: https://ratatui.rs/how-to/render/style-text/
[templates]: https://github.com/ratatui/templates/
[Examples]: https://github.com/ratatui/ratatui/tree/main/ratatui/examples/README.md
[Report a bug]: https://github.com/ratatui/ratatui/issues/new?labels=bug&projects=&template=bug_report.md
[Request a Feature]: https://github.com/ratatui/ratatui/issues/new?labels=enhancement&projects=&template=feature_request.md
[Create a Pull Request]: https://github.com/ratatui/ratatui/compare
[git-cliff]: https://git-cliff.org
[Conventional Commits]: https://www.conventionalcommits.org
[API Docs]: https://docs.rs/ratatui
[Changelog]: https://github.com/ratatui/ratatui/blob/main/CHANGELOG.md
[Contributing]: https://github.com/ratatui/ratatui/blob/main/CONTRIBUTING.md
[AI Contributions]: https://github.com/ratatui/ratatui/blob/main/CONTRIBUTING.md#ai-generated-content
[Breaking Changes]: https://github.com/ratatui/ratatui/blob/main/BREAKING-CHANGES.md
[FOSDEM 2024 talk]: https://www.youtube.com/watch?v=NU0q6NOLJ20
[`Frame`]: terminal::Frame
[`render_widget`]: terminal::Frame::render_widget
[`Widget`]: widgets::Widget
[`Layout`]: layout::Layout
[`Text`]: text::Text
[`Line`]: text::Line
[`Span`]: text::Span
[`Style`]: style::Style
[`style` module]: style
[`Stylize`]: style::Stylize
[`Backend`]: backend::Backend
[`backend` module]: backend
[`crossterm::event`]: https://docs.rs/crossterm/latest/crossterm/event/index.html
[Crate]: https://crates.io/crates/ratatui
[Crossterm]: https://crates.io/crates/crossterm
[Termion]: https://crates.io/crates/termion
[Termwiz]: https://crates.io/crates/termwiz
[tui-rs]: https://crates.io/crates/tui
[Sponsors]: https://github.com/sponsors/ratatui
[Crate Badge]: https://img.shields.io/crates/v/ratatui?logo=rust&style=flat-square&color=E05D44
[Repo Badge]: https://img.shields.io/badge/repo-ratatui/ratatui-1370D3?style=flat-square&logo=github
[GitHub Sponsors]: https://github.com/sponsors/ratatui
[Crate Badge]: https://img.shields.io/crates/v/ratatui?logo=rust&style=flat-square&logoColor=E05D44&color=E05D44
[License Badge]: https://img.shields.io/crates/l/ratatui?style=flat-square&color=1370D3
[CI Badge]: https://img.shields.io/github/actions/workflow/status/ratatui/ratatui/ci.yml?style=flat-square&logo=github
[CI]: https://github.com/ratatui/ratatui/actions/workflows/ci.yml
[Codecov Badge]: https://img.shields.io/codecov/c/github/ratatui/ratatui?logo=codecov&style=flat-square&token=BAQ8SOKEST&color=C43AC3
[CI Workflow]: https://github.com/ratatui/ratatui/actions/workflows/ci.yml
[Codecov Badge]: https://img.shields.io/codecov/c/github/ratatui/ratatui?logo=codecov&style=flat-square&token=BAQ8SOKEST&color=C43AC3&logoColor=C43AC3
[Codecov]: https://app.codecov.io/gh/ratatui/ratatui
[Deps Badge]: https://deps.rs/repo/github/ratatui/ratatui/status.svg?path=ratatui&style=flat-square
[Deps]: https://deps.rs/repo/github/ratatui/ratatui?path=ratatui
[Deps.rs Badge]: https://deps.rs/repo/github/ratatui/ratatui/status.svg?style=flat-square
[Deps.rs]: https://deps.rs/repo/github/ratatui/ratatui
[Discord Badge]: https://img.shields.io/discord/1070692720437383208?label=discord&logo=discord&style=flat-square&color=1370D3&logoColor=1370D3
[Discord Server]: https://discord.gg/pMCEU9hNEj
[Docs Badge]: https://img.shields.io/badge/docs-ratatui-1370D3?style=flat-square&logo=rust
[Docs Badge]: https://img.shields.io/docsrs/ratatui?logo=rust&style=flat-square&logoColor=E05D44
[Matrix Badge]: https://img.shields.io/matrix/ratatui-general%3Amatrix.org?style=flat-square&logo=matrix&label=Matrix&color=C43AC3
[Matrix]: https://matrix.to/#/#ratatui:matrix.org
[Forum Badge]: https://img.shields.io/discourse/likes?server=https%3A%2F%2Fforum.ratatui.rs&style=flat-square&logo=discourse&label=forum&color=C43AC3
[Forum]: https://forum.ratatui.rs
[Sponsors Badge]: https://img.shields.io/github/sponsors/ratatui?logo=github&style=flat-square&color=1370D3
[templates]: https://github.com/ratatui/templates/
[showcase]: https://ratatui.rs/showcase/
[awesome-ratatui]: https://github.com/ratatui/awesome-ratatui
[Pavel Fomchenkov]: https://github.com/nawok
[Florian Dehau]: https://github.com/fdehau
[`cargo-generate`]: https://crates.io/crates/cargo-generate
[License]: ./LICENSE
<!-- cargo-rdme end -->
## Contributing
In order to organize ourselves, we currently use a [Discord server](https://discord.gg/pMCEU9hNEj),
feel free to join and come chat! There is also a [Matrix](https://matrix.org/) bridge available at
[#ratatui:matrix.org](https://matrix.to/#/#ratatui:matrix.org).
We have also recently launched the [Ratatui Forum][Forum], For bugs and features, we rely on GitHub.
Please [Report a bug], [Request a Feature] or [Create a Pull Request].
Please make sure you read the [contributing](./CONTRIBUTING.md) guidelines, especially if you are
interested in working on a PR or issue opened in the previous repository.
## Built with Ratatui
Ratatui has a number of built-in [widgets](https://docs.rs/ratatui/latest/ratatui/widgets/), as well
as many contributed by external contributors. Check out the [Showcase](https://ratatui.rs/showcase/)
section of the website, or the [awesome-ratatui](https://github.com/ratatui/awesome-ratatui) repo
for a curated list of awesome apps/libraries built with `ratatui`!
## Alternatives
You might want to checkout [Cursive](https://github.com/gyscos/Cursive) or
[iocraft](https://github.com/ccbrown/iocraft/) for an alternative solutions
to build text user interfaces in Rust.
## Acknowledgments
None of this could be possible without [**Florian Dehau**](https://github.com/fdehau) who originally
created [tui-rs] which inspired many Rust TUIs.
Special thanks to [**Pavel Fomchenkov**](https://github.com/nawok) for his work in designing **an
awesome logo** for the ratatui project and ratatui organization.
## License
[MIT](./LICENSE)

View File

@@ -36,7 +36,7 @@ actions](.github/workflows/cd.yml) and triggered by pushing a tag.
## Alpha Releases
Alpha releases are automatically released every Saturday via [cd.yml](./.github/workflows/cd.yml)
and can be manually created when necessary by triggering the [Continuous
and can be manually be created when necessary by triggering the [Continuous
Deployment](https://github.com/ratatui/ratatui/actions/workflows/cd.yml) workflow.
We automatically release an alpha release with a patch level bump + alpha.num weekly (and when we

View File

@@ -1 +0,0 @@
<svg role="img" viewBox="0 0 50 50" xmlns="http://www.w3.org/2000/svg"><title>Ratatui</title><path d="M17 29h1v1h1v1h1v1h1v1h1v1h1v1h1v1h1v1h1v1h1v1h1v1h1v1h1v1h1v1h1v1h1v1h1v1h1v1h1v1h1v1h1v1H5v-1H4v-1H3v-1H2v-1H1v-1H0v-2h1v-1h1v-1h1v-1h1v-1h1v-1h1v-1h1v-1h1v-1h1v-1h1v-1h1v-1h1v-1h1v-1h1v-1h1v-1h2zm-2 14h1v1h1v1h1v-2h-1v-1h-1v-1h-1zm0-6h-3v1h1v1h4v-4h-1v-1h-1zm35-21h-1v4h-1v1h-1v1h-1v1h-2v1h-1v1h-1v1h-1v1h-2v9h5v1h1v9h-1v1h-1v2h-1v1h-1v-1h-1v-1h-1v-1h1v-1h1v-1h1v-5h-1v1h-3v1h-1v3h-1v-1h-1v-1h-1v-1h-1v-1h-1v-1h-1v-1h-1v-1h-1v-1h-1v-1h-1v-1h-1v-1h-1v-1h-1v-2h1v-2h1v-1h-2v1h-4v-1h-1v-1h-1v-1h-1v-2h1v-1h1v-1h2v-1h7v-1h1v-1h1v-1h1v-1h1v-1h1v-1h4v-1h3v-1h10zm-17 2h-1v2h1v1h2v-1h1v-2h-1v-1h-2zM29 1h1v9h1v1h1v2h1v2h-1v1h-1v1h-1v1h-1v1h-1v1h-3v-1h-1v-1h-2v-1h-1v-1h-1v-1h-6v-1h-1V9h1V8h1V7h1V6h1V5h1V4h1V3h1V2h1V1h2V0h6z"/></svg>

Before

Width:  |  Height:  |  Size: 831 B

View File

@@ -1 +0,0 @@
<svg role="img" viewBox="0 0 50 50" xmlns="http://www.w3.org/2000/svg"><title>Ratatui</title><path d="M50 16h-1v4h-1v1h-1v1h-1v1h-2v1h-1v1h-1v1h-1v1h-2v9h5v1h1v9h-1v1h-1v2h-1v1h-1v-1h-1v1h-2v-1h-1v-1h-1v-1h-1v-1h-1v-1h-1v-1h-1v-1h-1v-1h-1v-1h-1v-1h-1v-1h-1v-1h-1v-1h-1v-1h-1v-1h-1v-1h-1v-1h-1v-1h-1v-1h-1v-1h-1v-1h-2v1h-1v1h-1v1h-1v1h-1v1h-1v1h-1v1H9v1H8v1H7v1H6v1H5v1H4v1H3v1H2v2h1v1h1v1h1v1h1v1h1v1H5v-1H4v-1H3v-1H2v-1H1v-1H0v-2h1v-1h1v-1h1v-1h1v-1h1v-1h1v-1h1v-1h1v-1h1v-1h1v-1h1v-1h1v-1h1v-1h1v-1h1v-1h1v-1h2v1h1v-1h-1v-1h-1v-2h1v-1h1v-1h2v-1h7v-1h1v-1h1v-1h1v-1h1v-1h1v-1h4v-1h3v-1h10zM39 49h1v-1h-1zm2-8h-3v1h-1v3h-1v-1h-1v1h1v1h1v1h1v1h1v-1h1v-1h1v-1h1v-5h-1zm-7 3h1v-1h-1zm-1-1h1v-1h-1zm-1-1h1v-1h-1zm-1-1h1v-1h-1zm-1-1h1v-1h-1zm-1-1h1v-1h-1zm-1-1h1v-1h-1zm-1-1h1v-1h-1zm-1-1h1v-1h-1zm-1-1h1v-1h-1zm-1-1h1v-1h-1zm-5-5h1v1h1v1h1v1h1v1h1v-2h1v-2h1v-1h-2v1h-4v-1h-1zm14-11h-1v2h1v1h2v-1h1v-2h-1v-1h-2zM18 40h1v1h1v2h-1v-1h-1v-1h-1v-2h1zm0-7h1v4h-4v-1h-1v-1h3v-3h1zM29 1h1v9h1v1h1v2h1v2h-1v1h-1v1h-1v1h-1v1h-1v1h-3v-1h-1v-1h-2v-1h-1v-1h-1v-1h-6v-1h-1V9h1V8h1V7h1V6h1V5h1V4h1V3h1V2h1V1h2V0h6z"/></svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -8,73 +8,119 @@
default_job = "check"
[jobs.check]
command = ["cargo", "xtask", "check"]
command = ["cargo", "check", "--all-features"]
need_stdout = false
[jobs.check-all]
command = ["cargo", "xtask", "check", "--all-features"]
command = ["cargo", "check", "--all-targets", "--all-features"]
need_stdout = false
[jobs.check-crossterm]
command = ["cargo", "xtask", "check-backend", "crossterm"]
command = [
"cargo",
"check",
"--all-targets",
"--no-default-features",
"--features",
"crossterm",
]
need_stdout = false
[jobs.check-termion]
command = ["cargo", "xtask", "check-backend", "termion"]
command = [
"cargo",
"check",
"--all-targets",
"--no-default-features",
"--features",
"termion",
]
need_stdout = false
[jobs.check-termwiz]
command = ["cargo", "xtask", "check-backend", "termwiz"]
command = [
"cargo",
"check",
"--all-targets",
"--no-default-features",
"--features",
"termwiz",
]
need_stdout = false
[jobs.clippy-all]
command = ["cargo", "xtask", "clippy"]
[jobs.clippy]
command = ["cargo", "clippy", "--all-targets"]
need_stdout = false
[jobs.test]
command = ["cargo", "xtask", "test"]
command = ["cargo", "test", "--all-features"]
need_stdout = true
[jobs.test-unit]
command = ["cargo", "xtask", "test-libs"]
command = ["cargo", "test", "--lib", "--all-features"]
need_stdout = true
[jobs.doc]
command = ["cargo", "xtask", "docs"]
command = [
"cargo",
"+nightly",
"doc",
"-Zunstable-options",
"-Zrustdoc-scrape-examples",
"--all-features",
"--no-deps",
]
env.RUSTDOCFLAGS = "--cfg docsrs"
need_stdout = false
# If the doc compiles, then it opens in your browser and bacon switches
# to the previous job
[jobs.doc-open]
command = ["cargo", "xtask", "docs", "--open"]
on_success = "job:doc"
command = [
"cargo",
"+nightly",
"doc",
"-Zunstable-options",
"-Zrustdoc-scrape-examples",
"--all-features",
"--no-deps",
"--open",
]
env.RUSTDOCFLAGS = "--cfg docsrs"
need_stdout = false
on_success = "job:doc" # so that we don't open the browser at each change
[jobs.coverage]
command = ["cargo", "xtask", "coverage"]
command = [
"cargo",
"llvm-cov",
"--lcov",
"--output-path",
"target/lcov.info",
"--all-features",
]
[jobs.coverage-unit-tests-only]
command = ["cargo", "xtask", "coverage", "--lib"]
command = [
"cargo",
"llvm-cov",
"--lcov",
"--output-path",
"target/lcov.info",
"--lib",
"--all-features",
]
[jobs.hack]
command = ["cargo", "xtask", "hack"]
[jobs.format]
command = ["cargo", "xtask", "format"]
[jobs.zizmor-offline]
# zizmor checks the workflow files for security issues. The offline version is generally faster, but
# checks for fewer issues.
command = ["zizmor", "--color", "always", ".github/workflows", "--offline"]
need_stdout = true
default_watch = false
watch = [".github/workflows/"]
[jobs.zizmor-online]
# zizmor checks the workflow files for security issues. The online version is a bit slower, but it
# checks for more issues
command = ["zizmor", "--color", "always", ".github/workflows"]
need_stdout = true
default_watch = false
watch = [".github/workflows/"]
command = [
"cargo",
"hack",
"test",
"--lib",
"--each-feature",
# "--all-targets",
"--workspace",
]
# You may define here keybindings that would be specific to
# a project, for example a shortcut to launch a specific job.
@@ -89,6 +135,3 @@ v = "job:coverage"
ctrl-v = "job:coverage-unit-tests-only"
u = "job:test-unit"
n = "job:nextest"
f = "job:format"
z = "job:zizmor-offline"
shift-z = "job:zizmor-online"

View File

@@ -24,11 +24,7 @@ body = """
{%- if not version %}
## [unreleased]
{% else -%}
{%- if package -%} {# release-plz specific variable #}
## {{ package }} - [{{ version }}]({{ release_link }}) - {{ timestamp | date(format="%Y-%m-%d") }}
{%- else -%}
## [{{ version }}]({{ self::remote_url() }}/releases/tag/{{ version }}) - {{ timestamp | date(format="%Y-%m-%d") }}
{%- endif %}
## [{{ version }}](https://github.com/ratatui/ratatui/releases/tag/{{ version }}) - {{ timestamp | date(format="%Y-%m-%d") }}
{% endif -%}
{% macro commit(commit) -%}
@@ -37,16 +33,6 @@ body = """
{% if commit.remote.username %} by @{{ commit.remote.username }}{%- endif -%}\
{% if commit.remote.pr_number %} in [#{{ commit.remote.pr_number }}]({{ self::remote_url() }}/pull/{{ commit.remote.pr_number }}){%- endif %}\
{%- if commit.breaking %} [**breaking**]{% endif %}
{%- if commit.body %}\n\n{{ commit.body | indent(prefix=" > ", first=true, blank=true) }}
{%- endif %}
{%- for footer in commit.footers %}\n
{%- if footer.token != "Signed-off-by" and footer.token != "Co-authored-by" %}
>
{{ footer.token | indent(prefix=" > ", first=true, blank=true) }}
{{- footer.separator }}
{{- footer.value| indent(prefix=" > ", first=false, blank=true) }}
{%- endif %}
{%- endfor %}
{% endmacro -%}
{% for group, commits in commits | group_by(attribute="group") %}
@@ -61,7 +47,6 @@ body = """
{%- endfor -%}
{%- endfor %}
{%- if not release_link -%}
{% if github.contributors | filter(attribute="is_first_time", value=true) | length != 0 %}
### New Contributors
{%- endif %}\
@@ -71,31 +56,27 @@ body = """
[#{{ contributor.pr_number }}]({{ self::remote_url() }}/pull/{{ contributor.pr_number }}) \
{%- endif %}
{%- endfor -%}
{%- endif -%}
{% if version %}
{% if previous.version %}
{%- if release_link -%}
**Full Changelog**: {{ release_link }} {# release-plz specific variable #}
{%- else -%}
**Full Changelog**: {{ self::remote_url() }}/compare/{{ previous.version }}...{{ version }}
{% endif %}
{% endif %}
{% else -%}
{% raw %}\n{% endraw %}
{% endif %}
{%- macro remote_url() -%}
{%- if remote.owner -%} {# release-plz specific variable #}
https://github.com/{{ remote.owner }}/{{ remote.repo }}\
{%- else -%}
https://github.com/{{ remote.github.owner }}/{{ remote.github.repo }}\
{%- endif -%}
{% endmacro %}
"""
# remove the leading and trailing whitespace from the template
trim = false
# postprocessors for the changelog body
# changelog footer
footer = """
<!-- generated by git-cliff -->
"""
postprocessors = [
{ pattern = '<!-- Please read CONTRIBUTING.md before submitting any pull request. -->', replace = "" },
{ pattern = '>---+\n', replace = '' },
@@ -116,20 +97,9 @@ commit_preprocessors = [
{ pattern = '(Clarify README.md)', replace = "docs(readme): ${1}" },
{ pattern = '(Update README.md)', replace = "docs(readme): ${1}" },
{ pattern = '(fix typos|Fix typos)', replace = "fix: ${1}" },
# Typos that squeaked through and which would otherwise trigger the typos linter.
# Regex obsfucation is to avoid triggering the linter in this file until there's a per file config
# See https://github.com/crate-ci/typos/issues/724
{ pattern = '\<[d]eatil\>', replace = "detail" },
{ pattern = '\<[f]eatuers\>', replace = "features" },
{ pattern = '\<[s]pecically\>', replace = "specially" },
{ pattern = '\<[g]ague\>', replace = "gauge" },
{ pattern = '\<[i]ntructions\>', replace = "instructions" },
{ pattern = '\<[i]mplementated\>', replace = "implemented" },
]
# regex for parsing and grouping commits
commit_parsers = [
# release-plz adds 000000 as a placeholder for release commits
{ field = "id", pattern = "0000000", skip = true },
{ message = "^feat", group = "<!-- 00 -->Features" },
{ message = "^[fF]ix", group = "<!-- 01 -->Bug Fixes" },
{ message = "^refactor", group = "<!-- 02 -->Refactor" },
@@ -138,16 +108,13 @@ commit_parsers = [
{ message = "^style", group = "<!-- 05 -->Styling" },
{ message = "^test", group = "<!-- 06 -->Testing" },
{ message = "^chore\\(release\\): prepare for", skip = true },
{ message = "^chore: release", skip = true },
{ message = "^chore\\(pr\\)", skip = true },
{ message = "^chore\\(pull\\)", skip = true },
{ message = "^chore\\(deps\\)", skip = true },
{ message = "^chore\\(changelog\\)", skip = true },
{ message = "^[cC]hore", group = "<!-- 07 -->Miscellaneous Tasks" },
{ message = "^build\\(deps\\)", skip = true },
{ message = "^build\\(release\\)", skip = true },
{ message = "^build", group = "<!-- 08 -->Build" },
{ body = ".*security", group = "<!-- 09 -->Security" },
{ body = ".*security", group = "<!-- 08 -->Security" },
{ message = "^build", group = "<!-- 09 -->Build" },
{ message = "^ci", group = "<!-- 10 -->Continuous Integration" },
{ message = "^revert", group = "<!-- 11 -->Reverted Commits" },
# handle some old commits styles from pre 0.4
@@ -162,9 +129,9 @@ filter_commits = false
# glob pattern for matching git tags
tag_pattern = "v[0-9]*"
# regex for skipping tags
skip_tags = "beta|alpha|v0.1.0-rc.1"
skip_tags = "v0.1.0-rc.1"
# regex for ignoring tags
ignore_tags = "rc"
ignore_tags = "alpha"
# sort the tags topologically
topo_order = false
# sort the commits inside sections by oldest/newest order

View File

@@ -1 +1,24 @@
avoid-breaking-exported-api = false
# https://rust-lang.github.io/rust-clippy/master/index.html#/multiple_crate_versions
# ratatui -> bitflags v2.3
# termwiz -> wezterm-blob-leases -> mac_address -> nix -> bitflags v1.3.2
# (also, memoffset, syn, nix, strsim, windows-sys
# crossterm -> all the windows- deps https://github.com/ratatui/ratatui/pull/1064#issuecomment-2078848980
allowed-duplicate-crates = [
"bitflags",
"memoffset",
"nix",
"strsim",
"syn",
"windows-sys",
"windows-targets",
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
"unicode-width",
]

View File

@@ -4,15 +4,14 @@
version = 2
confidence-threshold = 0.8
allow = [
"Apache-2.0",
"BSD-2-Clause",
"BSD-3-Clause",
"ISC",
"MIT",
"Unicode-3.0",
"Unicode-DFS-2016",
"WTFPL",
"Zlib",
"Apache-2.0",
"BSD-2-Clause",
"BSD-3-Clause",
"ISC",
"MIT",
"Unicode-DFS-2016",
"WTFPL",
"Zlib",
]
[advisories]
@@ -25,15 +24,3 @@ multiple-versions = "allow"
unknown-registry = "deny"
unknown-git = "warn"
allow-registry = ["https://github.com/rust-lang/crates.io-index"]
[[licenses.clarify]]
crate = "ring"
# SPDX considers OpenSSL to encompass both the OpenSSL and SSLeay licenses
# https://spdx.org/licenses/OpenSSL.html
# ISC - Both BoringSSL and ring use this for their new files
# MIT - "Files in third_party/ have their own licenses, as described therein. The MIT
# license, for third_party/fiat, which, unlike other third_party directories, is
# compiled into non-test libraries, is included below."
# OpenSSL - Obviously
expression = "ISC AND MIT AND OpenSSL"
license-files = [{ path = "LICENSE", hash = 0xbd0eed23 }]

View File

@@ -1,275 +0,0 @@
# Examples
This folder contains examples that are more application focused.
> [!TIP]
> There are also [widget examples] in `ratatui-widgets`.
[widget examples]: ../ratatui-widgets/examples
You can run these examples using:
```shell
cargo run -p example-name
```
> [!NOTE]
> This folder might use unreleased code. Consider viewing the examples in the `latest` branch instead
> of the `main` branch for code which is guaranteed to work with the released Ratatui version.
> [!WARNING]
>
> There may be backwards incompatible changes in these examples, as they are designed to compile
> against the `main` branch.
>
> There are a few workaround for this problem:
>
> - View the examples as they were when the latest version was release by selecting the tag that
> matches that version. E.g. <https://github.com/ratatui/ratatui/tree/v0.26.1/examples>.
> - If you're viewing this file on GitHub, there is a combo box at the top of this page which
> allows you to select any previous tagged version.
> - To view the code locally, checkout the tag. E.g. `git switch --detach v0.26.1`.
> - Use the latest [alpha version of Ratatui] in your app. These are released weekly on Saturdays.
> - Compile your code against the main branch either locally by adding e.g. `path = "../ratatui"` to
> the dependency, or remotely by adding `git = "https://github.com/ratatui/ratatui"`
>
> For a list of unreleased breaking changes, see [BREAKING-CHANGES.md].
>
> We don't keep the CHANGELOG updated with unreleased changes, check the git commit history or run
> `git-cliff -u` against a cloned version of this repository.
## Design choices
The examples contain some opinionated choices in order to make it easier for newer rustaceans to
easily be productive in creating applications:
- Each example has an `App` struct, with methods that implement a main loop, handle events and drawing
the UI.
- We use `color_eyre` for handling errors and panics. See [How to use color-eyre with Ratatui] on the
website for more information about this.
- Common code is not extracted into a separate file. This makes each example self-contained and easy
to read as a whole.
[How to use color-eyre with Ratatui]: https://ratatui.rs/recipes/apps/color-eyre/
## Demo
This is the original demo example from the main README. It is available for each of the backends.
[Source](./apps/demo/).
![Demo](demo.gif)
## Demo2
This is the demo example from the main README and crate page. [Source](./apps/demo2/).
![Demo2](demo2.gif)
## Async GitHub
Shows how to fetch data from GitHub API asynchronously. [Source](./apps/async-github/).
![Async GitHub demo][async-github.gif]
## Calendar Explorer
Shows how to render a calendar with different styles. [Source](./apps/calendar-explorer/).
![Calendar explorer demo][calendar-explorer.gif]
## Canvas
Shows how to render a canvas with different shapes. [Source](./apps/canvas/).
![Canvas demo][canvas.gif]
## Chart
Shows how to render line, bar, and scatter charts. [Source](./apps/chart/).
![Chart demo][chart.gif]
## Color Explorer
Shows how to handle the supported colors. [Source](./apps/color-explorer/).
![Color explorer demo][color-explorer.gif]
## Colors-RGB demo
Shows the full range of RGB colors in an animation. [Source](./apps/colors-rgb/).
![Colors-RGB demo][colors-rgb.gif]
## Constraint Explorer
Shows how different constraints can be used to layout widgets. [Source](./apps/constraint-explorer/).
![Constraint Explorer demo][constraint-explorer.gif]
## Constraints
Shows different types of constraints. [Source](./apps/constraints/).
![Constraints demo][constraints.gif]
## Custom Widget
Shows how to create a custom widget that can be interacted with the mouse. [Source](./apps/custom-widget/).
![Custom widget demo][custom-widget.gif]
## Hyperlink
Shows how to render hyperlinks in a terminal using [OSC
8](https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda). [Source](./apps/hyperlink/).
![Hyperlink demo][hyperlink.gif]
## Flex
Shows how to use the flex layouts. [Source](./apps/flex/).
![Flex demo][flex.gif]
## Hello World
Shows how to create a simple TUI with a text. [Source](./apps/hello-world/).
![Hello World demo][hello-world.gif]
## Gauge
Shows different types of gauges. [Source](./apps/gauge/).
## Inline
Shows how to use the inlined viewport to render in a specific area of the screen. [Source](./apps/inline/).
![Inline demo][inline.gif]
## Input Form
Shows how to render a form with input fields. [Source](./apps/input-form/).
## Modifiers
Shows different types of modifiers. [Source](./apps/modifiers/).
![Modifiers demo][modifiers.gif]
## Mouse Drawing
Shows how to handle mouse events. [Source](./apps/mouse-drawing/).
## Minimal
Shows how to create a minimal application. [Source](./apps/minimal/).
![Minimal demo][minimal.gif]
## Panic
Shows how to handle panics. [Source](./apps/panic/).
![Panic demo][panic.gif]
## Popup
Shows how to handle popups. [Source](./apps/popup/).
![Popup demo][popup.gif]
## Scrollbar
Shows how to render different types of scrollbars. [Source](./apps/scrollbar/).
![Scrollbar demo][scrollbar.gif]
## Table
Shows how to create an interactive table. [Source](./apps/table/).
![Table demo][table.gif]
## Todo List
Shows how to create a simple todo list application. [Source](./apps/todo-list/).
![Todo List demo][todo-list.gif]
## Tracing
Shows how to use the [tracing](https://crates.io/crates/tracing) crate to log to a file. [Source](./apps/tracing/).
![Tracing demo][tracing.gif]
## User Input
Shows how to handle user input. [Source](./apps/user-input/). [Source](./apps/user-input/).
![User input demo][user-input.gif]
## Weather
Shows how to render weather data using barchart widget. [Source](./apps/weather/).
## WidgetRef Container
Shows how to use [`WidgetRef`](https://docs.rs/ratatui/latest/ratatui/widgets/trait.WidgetRef.html) to store widgets in a container. [Source](./apps/widget-ref-container/).
## Advanced Widget Implementation
Shows how to render the `Widget` trait in different ways.
![Advanced widget impl demo][advanced-widget-impl.gif]
---
<details>
<summary>How to update these examples?</summary>
These gifs were created using [VHS](https://github.com/charmbracelet/vhs). Each example has a
corresponding `.tape` file that holds instructions for how to generate the images. Note that the
images themselves are stored in a separate `images` git branch to avoid bloating the `main`
branch.
<!--
Links to images to make them easier to update in bulk. Use the following script to update and upload
the examples to the images branch. (Requires push access to the branch).
```shell
vhs/generate.bash
```
-->
</details>
[advanced-widget-impl.gif]: https://github.com/ratatui/ratatui/blob/images/examples/advanced-widget-impl.gif?raw=true
[async-github.gif]: https://github.com/ratatui/ratatui/blob/images/examples/async-github.gif?raw=true
[calendar-explorer.gif]: https://github.com/ratatui/ratatui/blob/images/examples/calendar-explorer.gif?raw=true
[canvas.gif]: https://github.com/ratatui/ratatui/blob/images/examples/canvas.gif?raw=true
[chart.gif]: https://github.com/ratatui/ratatui/blob/images/examples/chart.gif?raw=true
[color-explorer.gif]: https://github.com/ratatui/ratatui/blob/images/examples/color-explorer.gif?raw=true
[colors-rgb.gif]: https://github.com/ratatui/ratatui/blob/images/examples/colors-rgb.gif?raw=true
[constraint-explorer.gif]: https://github.com/ratatui/ratatui/blob/images/examples/constraint-explorer.gif?raw=true
[constraints.gif]: https://github.com/ratatui/ratatui/blob/images/examples/constraints.gif?raw=true
[custom-widget.gif]: https://github.com/ratatui/ratatui/blob/images/examples/custom-widget.gif?raw=true
[demo2-destroy.gif]: https://github.com/ratatui/ratatui/blob/images/examples/demo2-destroy.gif?raw=true
[demo2-social.gif]: https://github.com/ratatui/ratatui/blob/images/examples/demo2-social.gif?raw=true
[demo2.gif]: https://github.com/ratatui/ratatui/blob/images/examples/demo2.gif?raw=true
[demo.gif]: https://github.com/ratatui/ratatui/blob/images/examples/demo.gif?raw=true
[flex.gif]: https://github.com/ratatui/ratatui/blob/images/examples/flex.gif?raw=true
[hello-world.gif]: https://github.com/ratatui/ratatui/blob/images/examples/hello-world.gif?raw=true
[hyperlink.gif]: https://github.com/ratatui/ratatui/blob/images/examples/hyperlink.gif?raw=true
[inline.gif]: https://github.com/ratatui/ratatui/blob/images/examples/inline.gif?raw=true
[minimal.gif]: https://github.com/ratatui/ratatui/blob/images/examples/minimal.gif?raw=true
[modifiers.gif]: https://github.com/ratatui/ratatui/blob/images/examples/modifiers.gif?raw=true
[panic.gif]: https://github.com/ratatui/ratatui/blob/images/examples/panic.gif?raw=true
[popup.gif]: https://github.com/ratatui/ratatui/blob/images/examples/popup.gif?raw=true
[scrollbar.gif]: https://github.com/ratatui/ratatui/blob/images/examples/scrollbar.gif?raw=true
[table.gif]: https://github.com/ratatui/ratatui/blob/images/examples/table.gif?raw=true
[todo-list.gif]: https://github.com/ratatui/ratatui/blob/images/examples/todo-list.gif?raw=true
[tracing.gif]: https://github.com/ratatui/ratatui/blob/images/examples/tracing.gif?raw=true
[user-input.gif]: https://github.com/ratatui/ratatui/blob/images/examples/user-input.gif?raw=true

View File

@@ -1,14 +0,0 @@
[package]
name = "advanced-widget-impl"
publish = false
license.workspace = true
edition.workspace = true
rust-version.workspace = true
[dependencies]
color-eyre.workspace = true
crossterm.workspace = true
ratatui = { workspace = true, features = ["unstable-widget-ref"] }
[lints]
workspace = true

View File

@@ -1,9 +0,0 @@
# Advanced Widget Implementation demo
This example shows how to render the `Widget` trait in different ways.
To run this demo:
```shell
cargo run -p advanced-widget-impl
```

View File

@@ -1,22 +0,0 @@
[package]
name = "async-github"
publish = false
authors.workspace = true
documentation.workspace = true
repository.workspace = true
homepage.workspace = true
keywords.workspace = true
categories.workspace = true
readme.workspace = true
license.workspace = true
exclude.workspace = true
edition.workspace = true
rust-version.workspace = true
[dependencies]
color-eyre.workspace = true
crossterm = { workspace = true, features = ["event-stream"] }
octocrab.workspace = true
ratatui.workspace = true
tokio = { workspace = true, features = ["rt-multi-thread", "macros"] }
tokio-stream.workspace = true

View File

@@ -1,9 +0,0 @@
# Async GitHub demo
This example demonstrates how to use Ratatui with widgets that fetch data from GitHub API asynchronously.
To run this demo:
```shell
cargo run -p async-github
```

View File

@@ -1,15 +0,0 @@
[package]
name = "calendar-explorer"
publish = false
license.workspace = true
edition.workspace = true
rust-version.workspace = true
[dependencies]
color-eyre.workspace = true
crossterm.workspace = true
ratatui.workspace = true
time = { workspace = true, features = ["formatting", "parsing"] }
[lints]
workspace = true

View File

@@ -1,9 +0,0 @@
# Calendar explorer demo
This example shows how to render a calendar with different styles.
To run this demo:
```shell
cargo run -p calendar-explorer
```

View File

@@ -1,236 +0,0 @@
//! A Ratatui example that demonstrates how to render calendar with different styles.
//!
//! Marks the holidays and seasons on the calendar.
//!
//! This example runs with the Ratatui library code in the branch that you are currently reading.
//! See the [`latest`] branch for the code which works with the most recent Ratatui release.
//!
//! [`latest`]: https://github.com/ratatui/ratatui/tree/latest
//! [`BarChart`]: https://docs.rs/ratatui/latest/ratatui/widgets/struct.BarChart.html
use std::fmt;
use color_eyre::Result;
use crossterm::event::{self, KeyCode};
use ratatui::layout::{Constraint, Layout, Margin, Rect};
use ratatui::style::{Color, Modifier, Style, Stylize};
use ratatui::text::{Line, Text};
use ratatui::widgets::calendar::{CalendarEventStore, Monthly};
use ratatui::{DefaultTerminal, Frame};
use time::ext::NumericalDuration;
use time::{Date, Month, OffsetDateTime};
fn main() -> Result<()> {
color_eyre::install()?;
ratatui::run(run)
}
/// Run the application.
fn run(terminal: &mut DefaultTerminal) -> Result<()> {
let mut selected_date = OffsetDateTime::now_local()?.date();
let mut calendar_style = StyledCalendar::Default;
loop {
terminal.draw(|frame| render(frame, calendar_style, selected_date))?;
if let Some(key) = event::read()?.as_key_press_event() {
match key.code {
KeyCode::Char('q') => break Ok(()),
KeyCode::Char('s') => calendar_style = calendar_style.next(),
KeyCode::Char('n') | KeyCode::Tab => selected_date = next_month(selected_date),
KeyCode::Char('p') | KeyCode::BackTab => selected_date = prev_month(selected_date),
KeyCode::Char('h') | KeyCode::Left => selected_date -= 1.days(),
KeyCode::Char('j') | KeyCode::Down => selected_date += 1.weeks(),
KeyCode::Char('k') | KeyCode::Up => selected_date -= 1.weeks(),
KeyCode::Char('l') | KeyCode::Right => selected_date += 1.days(),
_ => {}
}
}
}
}
fn next_month(date: Date) -> Date {
if date.month() == Month::December {
date.replace_month(Month::January)
.unwrap()
.replace_year(date.year() + 1)
.unwrap()
} else {
date.replace_month(date.month().next()).unwrap()
}
}
fn prev_month(date: Date) -> Date {
if date.month() == Month::January {
date.replace_month(Month::December)
.unwrap()
.replace_year(date.year() - 1)
.unwrap()
} else {
date.replace_month(date.month().previous()).unwrap()
}
}
/// Render the UI with a calendar.
fn render(frame: &mut Frame, calendar_style: StyledCalendar, selected_date: Date) {
let header = Text::from_iter([
Line::from("Calendar Example".bold()),
Line::from(
"<q> Quit | <s> Change Style | <n> Next Month | <p> Previous Month, <hjkl> Move",
),
Line::from(format!(
"Current date: {selected_date} | Current style: {calendar_style}"
)),
]);
let [text_area, area] = frame.area().layout(&Layout::vertical([
Constraint::Length(header.height() as u16),
Constraint::Fill(1),
]));
frame.render_widget(header.centered(), text_area);
calendar_style
.render_year(frame, area, selected_date)
.unwrap();
}
#[derive(Debug, Clone, Copy)]
enum StyledCalendar {
Default,
Surrounding,
WeekdaysHeader,
SurroundingAndWeekdaysHeader,
MonthHeader,
MonthAndWeekdaysHeader,
}
impl StyledCalendar {
// Cycle through the different styles.
const fn next(self) -> Self {
match self {
Self::Default => Self::Surrounding,
Self::Surrounding => Self::WeekdaysHeader,
Self::WeekdaysHeader => Self::SurroundingAndWeekdaysHeader,
Self::SurroundingAndWeekdaysHeader => Self::MonthHeader,
Self::MonthHeader => Self::MonthAndWeekdaysHeader,
Self::MonthAndWeekdaysHeader => Self::Default,
}
}
}
impl fmt::Display for StyledCalendar {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Default => write!(f, "Default"),
Self::Surrounding => write!(f, "Show Surrounding"),
Self::WeekdaysHeader => write!(f, "Show Weekdays Header"),
Self::SurroundingAndWeekdaysHeader => write!(f, "Show Surrounding and Weekdays Header"),
Self::MonthHeader => write!(f, "Show Month Header"),
Self::MonthAndWeekdaysHeader => write!(f, "Show Month Header and Weekdays Header"),
}
}
}
impl StyledCalendar {
fn render_year(self, frame: &mut Frame, area: Rect, date: Date) -> Result<()> {
let events = events(date)?;
let vertical = Layout::vertical([Constraint::Ratio(1, 3); 3]);
let horizontal = &Layout::horizontal([Constraint::Ratio(1, 4); 4]);
let areas = area
.inner(Margin::new(1, 1))
.layout_vec(&vertical)
.into_iter()
.flat_map(|row| row.layout_vec(horizontal));
for (i, area) in areas.enumerate() {
let month = date
.replace_day(1)
.unwrap()
.replace_month(Month::try_from(i as u8 + 1).unwrap())
.unwrap();
self.render_month(frame, area, month, &events);
}
Ok(())
}
fn render_month(self, frame: &mut Frame, area: Rect, date: Date, events: &CalendarEventStore) {
let calendar = match self {
Self::Default => Monthly::new(date, events)
.default_style(Style::new().bold().bg(Color::Rgb(50, 50, 50)))
.show_month_header(Style::default()),
Self::Surrounding => Monthly::new(date, events)
.default_style(Style::new().bold().bg(Color::Rgb(50, 50, 50)))
.show_month_header(Style::default())
.show_surrounding(Style::new().dim()),
Self::WeekdaysHeader => Monthly::new(date, events)
.default_style(Style::new().bold().bg(Color::Rgb(50, 50, 50)))
.show_month_header(Style::default())
.show_weekdays_header(Style::new().bold().green()),
Self::SurroundingAndWeekdaysHeader => Monthly::new(date, events)
.default_style(Style::new().bold().bg(Color::Rgb(50, 50, 50)))
.show_month_header(Style::default())
.show_surrounding(Style::new().dim())
.show_weekdays_header(Style::new().bold().green()),
Self::MonthHeader => Monthly::new(date, events)
.default_style(Style::new().bold().bg(Color::Rgb(50, 50, 50)))
.show_month_header(Style::default())
.show_month_header(Style::new().bold().green()),
Self::MonthAndWeekdaysHeader => Monthly::new(date, events)
.default_style(Style::new().bold().bg(Color::Rgb(50, 50, 50)))
.show_month_header(Style::default())
.show_weekdays_header(Style::new().bold().dim().light_yellow()),
};
frame.render_widget(calendar, area);
}
}
/// Makes a list of dates for the current year.
fn events(selected_date: Date) -> Result<CalendarEventStore> {
const SELECTED: Style = Style::new()
.fg(Color::White)
.bg(Color::Red)
.add_modifier(Modifier::BOLD);
const HOLIDAY: Style = Style::new()
.fg(Color::Red)
.add_modifier(Modifier::UNDERLINED);
const SEASON: Style = Style::new()
.fg(Color::Green)
.bg(Color::Black)
.add_modifier(Modifier::UNDERLINED);
let mut list = CalendarEventStore::today(
Style::default()
.add_modifier(Modifier::BOLD)
.bg(Color::Blue),
);
let y = selected_date.year();
// new year's
list.add(Date::from_calendar_date(y, Month::January, 1)?, HOLIDAY);
// next new_year's for December "show surrounding"
list.add(Date::from_calendar_date(y + 1, Month::January, 1)?, HOLIDAY);
// groundhog day
list.add(Date::from_calendar_date(y, Month::February, 2)?, HOLIDAY);
// april fool's
list.add(Date::from_calendar_date(y, Month::April, 1)?, HOLIDAY);
// earth day
list.add(Date::from_calendar_date(y, Month::April, 22)?, HOLIDAY);
// star wars day
list.add(Date::from_calendar_date(y, Month::May, 4)?, HOLIDAY);
// festivus
list.add(Date::from_calendar_date(y, Month::December, 23)?, HOLIDAY);
// new year's eve
list.add(Date::from_calendar_date(y, Month::December, 31)?, HOLIDAY);
// seasons
// spring equinox
list.add(Date::from_calendar_date(y, Month::March, 22)?, SEASON);
// summer solstice
list.add(Date::from_calendar_date(y, Month::June, 21)?, SEASON);
// fall equinox
list.add(Date::from_calendar_date(y, Month::September, 22)?, SEASON);
// winter solstice
list.add(Date::from_calendar_date(y, Month::December, 21)?, SEASON);
// selected date
list.add(selected_date, SELECTED);
Ok(list)
}

View File

@@ -1,15 +0,0 @@
[package]
name = "canvas"
publish = false
license.workspace = true
edition.workspace = true
rust-version.workspace = true
[dependencies]
color-eyre.workspace = true
crossterm.workspace = true
itertools.workspace = true
ratatui.workspace = true
[lints]
workspace = true

View File

@@ -1,9 +0,0 @@
# Canvas demo
This example shows how to render various shapes and a map on a canvas.
To run this demo:
```shell
cargo run -p canvas
```

View File

@@ -1,14 +0,0 @@
[package]
name = "chart"
publish = false
license.workspace = true
edition.workspace = true
rust-version.workspace = true
[dependencies]
color-eyre.workspace = true
crossterm.workspace = true
ratatui.workspace = true
[lints]
workspace = true

View File

@@ -1,9 +0,0 @@
# Chart demo
This example shows how to render line, bar, and scatter charts.
To run this demo:
```shell
cargo run -p chart
```

View File

@@ -1,15 +0,0 @@
[package]
name = "color-explorer"
publish = false
license.workspace = true
edition.workspace = true
rust-version.workspace = true
[dependencies]
color-eyre.workspace = true
crossterm.workspace = true
itertools.workspace = true
ratatui.workspace = true
[lints]
workspace = true

View File

@@ -1,9 +0,0 @@
# Color explorer demo
This example shows how to handle the supported colors.
To run this demo:
```shell
cargo run -p color-explorer
```

View File

@@ -1,15 +0,0 @@
[package]
name = "colors-rgb"
publish = false
license.workspace = true
edition.workspace = true
rust-version.workspace = true
[dependencies]
color-eyre.workspace = true
crossterm.workspace = true
palette.workspace = true
ratatui.workspace = true
[lints]
workspace = true

View File

@@ -1,9 +0,0 @@
# Colors-RGB demo
This example shows the full range of RGB colors in an animation.
To run this demo:
```shell
cargo run -p colors-rgb
```

View File

@@ -1,16 +0,0 @@
[package]
name = "constraint-explorer"
publish = false
license.workspace = true
edition.workspace = true
rust-version.workspace = true
[dependencies]
color-eyre.workspace = true
crossterm.workspace = true
itertools.workspace = true
ratatui.workspace = true
strum.workspace = true
[lints]
workspace = true

View File

@@ -1,9 +0,0 @@
# Constraint explorer demo
This interactive example shows how different constraints can be used to layout widgets.
To run this demo:
```shell
cargo run -p constraint-explorer
```

View File

@@ -1,15 +0,0 @@
[package]
name = "constraints"
publish = false
license.workspace = true
edition.workspace = true
rust-version.workspace = true
[dependencies]
color-eyre.workspace = true
crossterm.workspace = true
ratatui.workspace = true
strum.workspace = true
[lints]
workspace = true

View File

@@ -1,9 +0,0 @@
# Constraints demo
This example shows different types of constraints.
To run this demo:
```shell
cargo run -p constraints
```

View File

@@ -1,14 +0,0 @@
[package]
name = "custom-widget"
publish = false
license.workspace = true
edition.workspace = true
rust-version.workspace = true
[dependencies]
color-eyre.workspace = true
crossterm.workspace = true
ratatui.workspace = true
[lints]
workspace = true

View File

@@ -1,9 +0,0 @@
# Custom widget demo
This example shows how to create a custom widget that can be interacted with the mouse.
To run this demo:
```shell
cargo run -p custom-widget
```

View File

@@ -1,22 +0,0 @@
[package]
name = "demo"
publish = false
license.workspace = true
edition.workspace = true
rust-version.workspace = true
[features]
default = ["crossterm"]
crossterm = ["ratatui/crossterm", "dep:crossterm"]
termion = ["ratatui/termion", "dep:termion"]
termwiz = ["ratatui/termwiz", "dep:termwiz"]
[dependencies]
clap.workspace = true
crossterm = { workspace = true, optional = true }
rand.workspace = true
ratatui.workspace = true
termwiz = { workspace = true, optional = true }
[target.'cfg(not(windows))'.dependencies]
termion = { workspace = true, optional = true }

View File

@@ -1,25 +0,0 @@
# Demo example
This is the original demo that was developed for Tui-rs (the library that Ratatui was forked from).
![demo.gif](https://github.com/ratatui/ratatui/blob/images/examples/demo.gif?raw=true)
This example is available for each backend. To run it:
## crossterm
```shell
cargo run -p demo
```
## termion
```shell
cargo run -p demo --no-default-features --features termion
```
## termwiz
```shell
cargo run -p demo --no-default-features --features termwiz
```

View File

@@ -1,19 +0,0 @@
[package]
name = "demo2"
publish = false
license.workspace = true
edition.workspace = true
rust-version.workspace = true
[dependencies]
color-eyre.workspace = true
crossterm.workspace = true
indoc.workspace = true
itertools.workspace = true
palette.workspace = true
rand.workspace = true
rand_chacha.workspace = true
ratatui = { workspace = true, features = ["all-widgets"] }
strum.workspace = true
time.workspace = true
unicode-width.workspace = true

View File

@@ -1,9 +0,0 @@
## Demo2
This is the demo example from the main README and crate page. Source: [demo2](./demo2/).
```shell
cargo run -p demo2
```
![Demo2](https://github.com/ratatui/ratatui/blob/images/examples/demo2.gif?raw=true)

View File

@@ -1,73 +0,0 @@
use ratatui::buffer::Buffer;
use ratatui::layout::{Alignment, Constraint, Layout, Margin, Rect};
use ratatui::widgets::{
Block, Borders, Clear, MascotEyeColor, Padding, Paragraph, RatatuiMascot, Widget, Wrap,
};
use crate::{RgbSwatch, THEME};
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct AboutTab {
row_index: usize,
}
impl AboutTab {
pub fn prev_row(&mut self) {
self.row_index = self.row_index.saturating_sub(1);
}
pub fn next_row(&mut self) {
self.row_index = self.row_index.saturating_add(1);
}
}
impl Widget for AboutTab {
fn render(self, area: Rect, buf: &mut Buffer) {
RgbSwatch.render(area, buf);
let layout = Layout::horizontal([Constraint::Length(34), Constraint::Min(0)]);
let [logo_area, description] = area.layout(&layout);
render_crate_description(description, buf);
let eye_state = if self.row_index % 2 == 0 {
MascotEyeColor::Default
} else {
MascotEyeColor::Red
};
RatatuiMascot::default().set_eye(eye_state).render(
logo_area.inner(Margin {
vertical: 0,
horizontal: 2,
}),
buf,
);
}
}
fn render_crate_description(area: Rect, buf: &mut Buffer) {
let area = area.inner(Margin {
vertical: 4,
horizontal: 2,
});
Clear.render(area, buf); // clear out the color swatches
Block::new().style(THEME.content).render(area, buf);
let area = area.inner(Margin {
vertical: 1,
horizontal: 2,
});
let text = "- cooking up terminal user interfaces -
Ratatui is a Rust crate that provides widgets (e.g. Paragraph, Table) and draws them to the \
screen efficiently every frame.";
Paragraph::new(text)
.style(THEME.description)
.block(
Block::new()
.title(" Ratatui ")
.title_alignment(Alignment::Center)
.borders(Borders::TOP)
.border_style(THEME.description_title)
.padding(Padding::new(0, 0, 0, 0)),
)
.wrap(Wrap { trim: true })
.scroll((0, 0))
.render(area, buf);
}

View File

@@ -1,15 +0,0 @@
[package]
name = "flex"
publish = false
license.workspace = true
edition.workspace = true
rust-version.workspace = true
[dependencies]
color-eyre.workspace = true
crossterm.workspace = true
ratatui.workspace = true
strum.workspace = true
[lints]
workspace = true

View File

@@ -1,9 +0,0 @@
# Flex demo
This interactive example shows how to use the flex layouts.
To run this demo:
```shell
cargo run -p flex
```

View File

@@ -1,14 +0,0 @@
[package]
name = "gauge"
publish = false
license.workspace = true
edition.workspace = true
rust-version.workspace = true
[dependencies]
color-eyre.workspace = true
crossterm.workspace = true
ratatui.workspace = true
[lints]
workspace = true

View File

@@ -1,9 +0,0 @@
# Gauge demo
This example shows different types of gauges.
To run this demo:
```shell
cargo run -p gauge
```

View File

@@ -1,14 +0,0 @@
[package]
name = "hello-world"
publish = false
license.workspace = true
edition.workspace = true
rust-version.workspace = true
[dependencies]
color-eyre.workspace = true
crossterm.workspace = true
ratatui.workspace = true
[lints]
workspace = true

View File

@@ -1,9 +0,0 @@
# Hello World demo
This example shows how to create a simple TUI with a text.
To run this demo:
```shell
cargo run -p hello-world
```

View File

@@ -1,15 +0,0 @@
[package]
name = "hyperlink"
publish = false
license.workspace = true
edition.workspace = true
rust-version.workspace = true
[dependencies]
color-eyre.workspace = true
crossterm.workspace = true
itertools.workspace = true
ratatui.workspace = true
[lints]
workspace = true

View File

@@ -1,9 +0,0 @@
# Hyperlink demo
This example shows how to render hyperlinks in a terminal using [OSC 8](https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda).
To run this demo:
```shell
cargo run -p hyperlink
```

View File

@@ -1,72 +0,0 @@
/// A Ratatui example that how to create hyperlinks in the terminal using [OSC 8].
///
/// This example runs with the Ratatui library code in the branch that you are currently
/// reading. See the [`latest`] branch for the code which works with the most recent Ratatui
/// release.
///
/// [`latest`]: https://github.com/ratatui/ratatui/tree/latest
/// [OSC 8]: https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda
use color_eyre::Result;
use crossterm::event;
use itertools::Itertools;
use ratatui::buffer::Buffer;
use ratatui::layout::Rect;
use ratatui::style::Stylize;
use ratatui::text::{Line, Text};
use ratatui::widgets::Widget;
fn main() -> Result<()> {
color_eyre::install()?;
let text = Line::from(vec!["Example ".into(), "hyperlink".blue()]);
let hyperlink = Hyperlink::new(text, "https://example.com");
ratatui::run(|terminal| {
loop {
terminal.draw(|frame| frame.render_widget(&hyperlink, frame.area()))?;
if event::read()?.is_key_press() {
break Ok(());
}
}
})
}
/// A hyperlink widget that renders a hyperlink in the terminal using [OSC 8].
///
/// [OSC 8]: https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda
struct Hyperlink<'content> {
text: Text<'content>,
url: String,
}
impl<'content> Hyperlink<'content> {
fn new(text: impl Into<Text<'content>>, url: impl Into<String>) -> Self {
Self {
text: text.into(),
url: url.into(),
}
}
}
impl Widget for &Hyperlink<'_> {
fn render(self, area: Rect, buffer: &mut Buffer) {
(&self.text).render(area, buffer);
// this is a hacky workaround for https://github.com/ratatui/ratatui/issues/902, a bug
// in the terminal code that incorrectly calculates the width of ANSI escape sequences. It
// works by rendering the hyperlink as a series of 2-character chunks, which is the
// calculated width of the hyperlink text.
for (i, two_chars) in self
.text
.to_string()
.chars()
.chunks(2)
.into_iter()
.enumerate()
{
let text = two_chars.collect::<String>();
let hyperlink = format!("\x1B]8;;{}\x07{}\x1B]8;;\x07", self.url, text);
buffer[(area.x + i as u16 * 2, area.y)].set_symbol(hyperlink.as_str());
}
}
}

View File

@@ -1,15 +0,0 @@
[package]
name = "inline"
publish = false
license.workspace = true
edition.workspace = true
rust-version.workspace = true
[dependencies]
color-eyre.workspace = true
crossterm.workspace = true
rand.workspace = true
ratatui.workspace = true
[lints]
workspace = true

View File

@@ -1,9 +0,0 @@
# Inline demo
This example shows how to use the inlined viewport to render in a specific area of the screen.
To run this demo:
```shell
cargo run -p inline
```

View File

@@ -1,16 +0,0 @@
[package]
name = "input-form"
publish = false
license.workspace = true
edition.workspace = true
rust-version.workspace = true
[dependencies]
color-eyre.workspace = true
crossterm.workspace = true
ratatui.workspace = true
serde.workspace = true
serde_json.workspace = true
[lints]
workspace = true

View File

@@ -1,27 +0,0 @@
# Input Form example
This example demonstrates how to handle input across several form fields (2 strings and an number).
It uses an enum to track the focused field, and sends keyboard events to one which is current.
Run this example with:
```shell
cargo run -p input-form
```
This example does not handle things like cursor movement within the line (just keys and backspace).
Most apps would benefit from using the following crates for text input rather than directly using
strings:
- [`tui-input`](https://crates.io/crates/tui-input)
- [`tui-prompts`](https://crates.io/crates/tui-prompts)
- [`tui-textarea`](https://crates.io/crates/tui-textarea)
- [`rat-salsa`](https://crates.io/crates/rat-salsa)
Some more ideas for handling focus can be found in:
- [`focusable`](https://crates.io/crates/focusable) (see also [Ratatui forum
post](https://forum.ratatui.rs/t/focusable-crate-manage-focus-state-for-your-widgets/73))
- [`rat-focus`](https://crates.io/crates/rat-focus)
- A useful [`Bevy` discussion](https://github.com/bevyengine/bevy/discussions/15374) about focus
more generally.

View File

@@ -1,263 +0,0 @@
//! A Ratatui example that demonstrates how to handle input form focus
//!
//! This example demonstrates how to handle cursor and input focus between multiple fields in a
//! form. You can navigate between fields using the Tab key.
//!
//! This does not handle cursor movement etc. This is just a simple example. In a real application,
//! consider using [`tui-input`], or [`tui-prompts`], or [`tui-textarea`].
//!
//! This example runs with the Ratatui library code in the branch that you are currently reading.
//! See the [`latest`] branch for the code which works with the most recent Ratatui release.
//!
//! [`latest`]: https://github.com/ratatui/ratatui/tree/latest
//! [`tui-input`]: https://crates.io/crates/tui-input
//! [`tui-prompts`]: https://crates.io/crates/tui-prompts
//! [`tui-textarea`]: https://crates.io/crates/tui-textarea
use color_eyre::Result;
use crossterm::event::{self, KeyCode, KeyEvent};
use ratatui::buffer::Buffer;
use ratatui::layout::{Constraint, Layout, Offset, Rect};
use ratatui::style::Stylize;
use ratatui::text::Line;
use ratatui::widgets::Widget;
use ratatui::{DefaultTerminal, Frame};
use serde::Serialize;
fn main() -> Result<()> {
color_eyre::install()?;
// serialize the form to JSON if the user submitted it, otherwise print "Canceled"
match ratatui::run(|terminal| App::default().run(terminal)) {
Ok(Some(form)) => println!("{}", serde_json::to_string_pretty(&form)?),
Ok(None) => println!("Canceled"),
Err(err) => eprintln!("{err}"),
}
Ok(())
}
#[derive(Default)]
struct App {
state: AppState,
form: InputForm,
}
#[derive(Default, PartialEq, Eq)]
enum AppState {
#[default]
Running,
Cancelled,
Submitted,
}
impl App {
fn run(mut self, terminal: &mut DefaultTerminal) -> Result<Option<InputForm>> {
while self.state == AppState::Running {
terminal.draw(|frame| self.render(frame))?;
self.handle_events()?;
}
match self.state {
AppState::Cancelled => Ok(None),
AppState::Submitted => Ok(Some(self.form)),
AppState::Running => unreachable!(),
}
}
fn render(&self, frame: &mut Frame) {
self.form.render(frame);
}
fn handle_events(&mut self) -> Result<()> {
if let Some(key) = event::read()?.as_key_press_event() {
match key.code {
KeyCode::Esc => self.state = AppState::Cancelled,
KeyCode::Enter => self.state = AppState::Submitted,
_ => self.form.on_key_press(key),
}
}
Ok(())
}
}
#[derive(Serialize)]
struct InputForm {
#[serde(skip)]
focus: Focus,
first_name: StringField,
last_name: StringField,
age: AgeField,
}
impl Default for InputForm {
fn default() -> Self {
Self {
focus: Focus::FirstName,
first_name: StringField::new("First Name"),
last_name: StringField::new("Last Name"),
age: AgeField::new("Age"),
}
}
}
impl InputForm {
// Handle focus navigation or pass the event to the focused field.
fn on_key_press(&mut self, event: KeyEvent) {
match event.code {
KeyCode::Tab => self.focus = self.focus.next(),
_ => match self.focus {
Focus::FirstName => self.first_name.on_key_press(event),
Focus::LastName => self.last_name.on_key_press(event),
Focus::Age => self.age.on_key_press(event),
},
}
}
/// Render the form with the current focus.
///
/// The cursor is placed at the end of the focused field.
fn render(&self, frame: &mut Frame) {
let layout = Layout::vertical(Constraint::from_lengths([1, 1, 1]));
let [first_name_area, last_name_area, age_area] = frame.area().layout(&layout);
frame.render_widget(&self.first_name, first_name_area);
frame.render_widget(&self.last_name, last_name_area);
frame.render_widget(&self.age, age_area);
let cursor_position = match self.focus {
Focus::FirstName => first_name_area.offset(self.first_name.cursor_offset()),
Focus::LastName => last_name_area.offset(self.last_name.cursor_offset()),
Focus::Age => age_area.offset(self.age.cursor_offset()),
};
frame.set_cursor_position(cursor_position);
}
}
#[derive(Default, PartialEq, Eq)]
enum Focus {
#[default]
FirstName,
LastName,
Age,
}
impl Focus {
// Round-robin focus order.
const fn next(&self) -> Self {
match self {
Self::FirstName => Self::LastName,
Self::LastName => Self::Age,
Self::Age => Self::FirstName,
}
}
}
/// A new-type representing a string field with a label.
#[derive(Debug, Serialize)]
struct StringField {
#[serde(skip)]
label: &'static str,
value: String,
}
impl StringField {
const fn new(label: &'static str) -> Self {
Self {
label,
value: String::new(),
}
}
/// Handle input events for the string input.
fn on_key_press(&mut self, event: KeyEvent) {
match event.code {
KeyCode::Char(c) => self.value.push(c),
KeyCode::Backspace => {
self.value.pop();
}
_ => {}
}
}
fn cursor_offset(&self) -> Offset {
let x = (self.label.len() + self.value.len() + 2) as i32;
Offset::new(x, 0)
}
}
impl Widget for &StringField {
fn render(self, area: Rect, buf: &mut Buffer) {
let layout = Layout::horizontal([
Constraint::Length(self.label.len() as u16 + 2),
Constraint::Fill(1),
]);
let [label_area, value_area] = area.layout(&layout);
let label = Line::from_iter([self.label, ": "]).bold();
label.render(label_area, buf);
self.value.clone().render(value_area, buf);
}
}
/// A new-type representing a person's age in years (0-130).
#[derive(Default, Clone, Copy, Serialize)]
struct AgeField {
#[serde(skip)]
label: &'static str,
value: u8,
}
impl AgeField {
const MAX: u8 = 130;
const fn new(label: &'static str) -> Self {
Self { label, value: 0 }
}
/// Handle input events for the age input.
///
/// Digits are accepted as input, with any input which would exceed the maximum age being
/// ignored. The up/down arrow keys and 'j'/'k' keys can be used to increment/decrement the
/// age.
fn on_key_press(&mut self, event: KeyEvent) {
match event.code {
KeyCode::Char(digit @ '0'..='9') => {
let value = self
.value
.saturating_mul(10)
.saturating_add(digit as u8 - b'0');
if value <= Self::MAX {
self.value = value;
}
}
KeyCode::Backspace => self.value /= 10,
KeyCode::Up | KeyCode::Char('k') => self.increment(),
KeyCode::Down | KeyCode::Char('j') => self.decrement(),
_ => {}
}
}
fn increment(&mut self) {
self.value = self.value.saturating_add(1).min(Self::MAX);
}
const fn decrement(&mut self) {
self.value = self.value.saturating_sub(1);
}
fn cursor_offset(&self) -> Offset {
let x = (self.label.len() + self.value.to_string().len() + 2) as i32;
Offset::new(x, 0)
}
}
impl Widget for &AgeField {
fn render(self, area: Rect, buf: &mut Buffer) {
let layout = Layout::horizontal([
Constraint::Length(self.label.len() as u16 + 2),
Constraint::Fill(1),
]);
let [label_area, value_area] = area.layout(&layout);
let label = Line::from_iter([self.label, ": "]).bold();
let value = self.value.to_string();
label.render(label_area, buf);
value.render(value_area, buf);
}
}

View File

@@ -1,13 +0,0 @@
[package]
name = "minimal"
publish = false
license.workspace = true
edition.workspace = true
rust-version.workspace = true
[dependencies]
crossterm.workspace = true
ratatui.workspace = true
[lints]
workspace = true

View File

@@ -1,9 +0,0 @@
# Minimal demo
This example shows how to create a minimal application.
To run this demo:
```shell
cargo run -p minimal
```

View File

@@ -1,23 +0,0 @@
//! A minimal example of a Ratatui application.
//!
//! This is a bare minimum example. There are many approaches to running an application loop,
//! so this is not meant to be prescriptive. See the [examples] folder for more complete
//! examples. In particular, the [hello-world] example is a good starting point.
//!
//! This example runs with the Ratatui library code in the branch that you are currently
//! reading. See the [`latest`] branch for the code which works with the most recent Ratatui
//! release.
//!
//! [`latest`]: https://github.com/ratatui/ratatui/tree/latest
//! [examples]: https://github.com/ratatui/ratatui/blob/main/examples
//! [hello-world]: https://github.com/ratatui/ratatui/blob/main/examples/apps/hello-world
fn main() -> Result<(), Box<dyn std::error::Error>> {
ratatui::run(|terminal| {
loop {
terminal.draw(|frame| frame.render_widget("Hello World!", frame.area()))?;
if crossterm::event::read()?.is_key_press() {
break Ok(());
}
}
})
}

View File

@@ -1,15 +0,0 @@
[package]
name = "modifiers"
publish = false
license.workspace = true
edition.workspace = true
rust-version.workspace = true
[dependencies]
color-eyre.workspace = true
crossterm.workspace = true
itertools.workspace = true
ratatui.workspace = true
[lints]
workspace = true

View File

@@ -1,9 +0,0 @@
# Modifiers demo
This example shows different types of modifiers.
To run this demo:
```shell
cargo run -p modifiers
```

View File

@@ -1,17 +0,0 @@
[package]
name = "mouse-drawing"
publish = false
license.workspace = true
edition.workspace = true
rust-version.workspace = true
[dependencies]
color-eyre.workspace = true
crossterm.workspace = true
## a collection of line drawing algorithms (e.g. Bresenham's line algorithm)
line_drawing = "1"
rand.workspace = true
ratatui.workspace = true
[lints]
workspace = true

View File

@@ -1,9 +0,0 @@
# Mouse drawing demo
This example shows how to receive mouse and handle mouse events.
To run this demo:
```shell
cargo run -p mouse-drawing
```

View File

@@ -1,118 +0,0 @@
/// A Ratatui example that demonstrates how to handle mouse events.
///
/// This example demonstrates how to handle mouse events in Ratatui. You can draw lines by
/// clicking and dragging the mouse.
///
/// This example runs with the Ratatui library code in the branch that you are currently
/// reading. See the [`latest`] branch for the code which works with the most recent Ratatui
/// release.
///
/// [`latest`]: https://github.com/ratatui/ratatui/tree/latest
use color_eyre::Result;
use crossterm::event::{
self, DisableMouseCapture, EnableMouseCapture, Event, KeyCode, KeyEvent, MouseEvent,
MouseEventKind,
};
use crossterm::execute;
use ratatui::layout::{Position, Rect, Size};
use ratatui::style::{Color, Stylize};
use ratatui::text::Line;
use ratatui::{DefaultTerminal, Frame, symbols};
fn main() -> Result<()> {
color_eyre::install()?;
ratatui::run(|terminal| MouseDrawingApp::default().run(terminal))
}
#[derive(Default)]
struct MouseDrawingApp {
// Whether the app should exit
pub should_exit: bool,
// The last known mouse position
pub mouse_position: Option<Position>,
// The points that have been clicked / drawn by dragging the mouse
pub points: Vec<(Position, Color)>,
// The color to draw with
pub current_color: Color,
}
impl MouseDrawingApp {
fn run(mut self, terminal: &mut DefaultTerminal) -> Result<()> {
execute!(std::io::stdout(), EnableMouseCapture)?;
while !self.should_exit {
terminal.draw(|frame| self.render(frame))?;
self.handle_events()?;
}
execute!(std::io::stdout(), DisableMouseCapture)?;
Ok(())
}
fn handle_events(&mut self) -> Result<()> {
match event::read()? {
Event::Key(event) => self.on_key_event(event),
Event::Mouse(event) => self.on_mouse_event(event),
_ => {}
}
Ok(())
}
/// Quit the app if the user presses 'q' or 'Esc'
fn on_key_event(&mut self, key: KeyEvent) {
if !key.is_press() {
return;
}
match key.code {
KeyCode::Char(' ') => {
self.current_color = Color::Rgb(rand::random(), rand::random(), rand::random());
}
KeyCode::Char('q') | KeyCode::Esc => self.should_exit = true,
_ => {}
}
}
/// Adds any points which were clicked or dragged to the `points` vector.
fn on_mouse_event(&mut self, event: MouseEvent) {
let position = Position::new(event.column, event.row);
match event.kind {
MouseEventKind::Down(_) => self.points.push((position, self.current_color)),
MouseEventKind::Drag(_) => self.draw_line(position),
_ => {}
}
self.mouse_position = Some(position);
}
/// Draw a line between the last point and the given position
fn draw_line(&mut self, position: Position) {
if let Some(start) = self.points.last() {
let (x0, y0) = (i32::from(start.0.x), i32::from(start.0.y));
let (x1, y1) = (i32::from(position.x), i32::from(position.y));
for (x, y) in line_drawing::Bresenham::new((x0, y0), (x1, y1)) {
let point = (Position::new(x as u16, y as u16), self.current_color);
self.points.push(point);
}
}
}
fn render(&self, frame: &mut Frame) {
// call order is important here as later elements are drawn on top of earlier elements
self.render_points(frame);
self.render_mouse_cursor(frame);
let value = "Mouse Example ('Esc' to quit. Click / drag to draw. 'Space' to change color)";
let title = Line::from(value).centered();
frame.render_widget(title, frame.area());
}
fn render_points(&self, frame: &mut Frame<'_>) {
for (position, color) in &self.points {
let area = Rect::from((*position, Size::new(1, 1))).clamp(frame.area());
frame.render_widget(symbols::block::FULL.fg(*color), area);
}
}
fn render_mouse_cursor(&self, frame: &mut Frame<'_>) {
if let Some(position) = self.mouse_position {
let area = Rect::from((position, Size::new(1, 1))).clamp(frame.area());
frame.render_widget("".bg(self.current_color), area);
}
}
}

View File

@@ -1,14 +0,0 @@
[package]
name = "panic"
publish = false
license.workspace = true
edition.workspace = true
rust-version.workspace = true
[dependencies]
color-eyre.workspace = true
crossterm.workspace = true
ratatui.workspace = true
[lints]
workspace = true

View File

@@ -1,9 +0,0 @@
# Panic demo
This example shows how to handle panics in your application.
To run this demo:
```shell
cargo run -p panic
```

View File

@@ -1,88 +0,0 @@
/// A Ratatui example that demonstrates how to handle panics in your application.
///
/// Prior to Ratatui 0.28.1, a panic hook had to be manually set up to ensure that the terminal
/// was reset when a panic occurred. This was necessary because a panic would interrupt the
/// normal control flow and leave the terminal in a distorted state.
///
/// Starting with Ratatui 0.28.1, the panic hook is automatically set up by the new
/// `ratatui::init` function, so you no longer need to manually set up the panic hook. This
/// example now demonstrates how the panic hook acts when it is enabled by default.
///
/// When exiting normally or when handling `Result::Err`, we can reset the terminal manually at
/// the end of `main` just before we print the error.
///
/// Because a panic interrupts the normal control flow, manually resetting the terminal at the
/// end of `main` won't do us any good. Instead, we need to make sure to set up a panic hook
/// that first resets the terminal before handling the panic. This both reuses the standard
/// panic hook to ensure a consistent panic handling UX and properly resets the terminal to not
/// distort the output.
///
/// That's why this example is set up to show both situations, with and without the panic hook,
/// to see the difference.
///
/// For more information on how to set this up manually, see the [Color Eyre recipe] in the
/// Ratatui website.
///
/// This example runs with the Ratatui library code in the branch that you are currently
/// reading. See the [`latest`] branch for the code which works with the most recent Ratatui
/// release.
///
/// [`latest`]: https://github.com/ratatui/ratatui/tree/latest
/// [Color Eyre recipe]: https://ratatui.rs/recipes/apps/color-eyre
use color_eyre::{Result, eyre::bail};
use crossterm::event::{self, KeyCode};
use ratatui::Frame;
use ratatui::text::Line;
use ratatui::widgets::{Block, Paragraph};
#[derive(Debug)]
enum PanicHandlerState {
Enabled,
Disabled,
}
fn main() -> Result<()> {
color_eyre::install()?;
let mut panic_hook_state = PanicHandlerState::Enabled;
ratatui::run(|terminal| {
loop {
terminal.draw(|frame| render(frame, &panic_hook_state))?;
if let Some(key) = event::read()?.as_key_press_event() {
match key.code {
KeyCode::Char('p') => panic!("intentional demo panic"),
KeyCode::Char('e') => bail!("intentional demo error"),
KeyCode::Char('h') => {
let _ = std::panic::take_hook();
panic_hook_state = PanicHandlerState::Disabled;
}
KeyCode::Char('q') => return Ok(()),
_ => {}
}
}
}
})
}
fn render(frame: &mut Frame, state: &PanicHandlerState) {
let text = vec![
Line::from(format!("Panic hook is currently: {state:?}")),
Line::from(""),
Line::from("Press `p` to cause a panic"),
Line::from("Press `e` to cause an error"),
Line::from("Press `h` to disable the panic hook"),
Line::from("Press `q` to quit"),
Line::from(""),
Line::from("When your app panics without a panic hook, you will likely have to"),
Line::from("reset your terminal afterwards with the `reset` command"),
Line::from(""),
Line::from("Try first with the panic handler enabled, and then with it disabled"),
Line::from("to see the difference"),
];
let paragraph = Paragraph::new(text)
.block(Block::bordered().title("Panic Handler Demo"))
.centered();
frame.render_widget(paragraph, frame.area());
}

View File

@@ -1,14 +0,0 @@
[package]
name = "popup"
publish = false
license.workspace = true
edition.workspace = true
rust-version.workspace = true
[dependencies]
color-eyre.workspace = true
crossterm.workspace = true
ratatui.workspace = true
[lints]
workspace = true

View File

@@ -1,9 +0,0 @@
# Popup demo
This example shows how to handle popups.
To run this demo:
```shell
cargo run -p popup
```

View File

@@ -1,70 +0,0 @@
//! A Ratatui example that demonstrates how to handle popups.
//! See also:
//! - <https://github.com/joshka/tui-popup> and
//! - <https://github.com/sephiroth74/tui-confirm-dialog>
//!
//! This example runs with the Ratatui library code in the branch that you are currently
//! reading. See the [`latest`] branch for the code which works with the most recent Ratatui
//! release.
//!
//! [`latest`]: https://github.com/ratatui/ratatui/tree/latest
use color_eyre::Result;
use crossterm::event::{self, KeyCode};
use ratatui::Frame;
use ratatui::layout::{Constraint, Flex, Layout, Rect};
use ratatui::style::Stylize;
use ratatui::text::Line;
use ratatui::widgets::{Block, Clear};
fn main() -> Result<()> {
color_eyre::install()?;
// This flag will be toggled when the user presses 'p'. This could be stored in an app struct
// if you have more state to manage than just this flag.
let mut show_popup = false;
ratatui::run(|terminal| {
loop {
terminal.draw(|frame| render(frame, show_popup))?;
if let Some(key) = event::read()?.as_key_press_event() {
match key.code {
KeyCode::Char('q') => return Ok(()),
KeyCode::Char('p') => show_popup = !show_popup,
_ => {}
}
}
}
})
}
fn render(frame: &mut Frame, show_popup: bool) {
let area = frame.area();
let layout = Layout::vertical([Constraint::Length(1), Constraint::Fill(1)]);
let [instructions, content] = area.layout(&layout);
frame.render_widget(
Line::from("Press 'p' to toggle popup, 'q' to quit").centered(),
instructions,
);
frame.render_widget(Block::bordered().title("Content").on_blue(), content);
if show_popup {
let popup = Block::bordered().title("Popup");
let popup_area = centered_area(area, 60, 20);
// clears out any background in the area before rendering the popup
frame.render_widget(Clear, popup_area);
frame.render_widget(popup, popup_area);
}
}
/// Create a centered rect using up certain percentage of the available rect
fn centered_area(area: Rect, percent_x: u16, percent_y: u16) -> Rect {
let vertical = Layout::vertical([Constraint::Percentage(percent_y)]).flex(Flex::Center);
let horizontal = Layout::horizontal([Constraint::Percentage(percent_x)]).flex(Flex::Center);
let [area] = area.layout(&vertical);
let [area] = area.layout(&horizontal);
area
}

View File

@@ -1,13 +0,0 @@
[package]
name = "release-header"
publish = false
license.workspace = true
edition.workspace = true
rust-version.workspace = true
[dependencies]
color-eyre.workspace = true
ratatui.workspace = true
[lints]
workspace = true

View File

@@ -1,13 +0,0 @@
# Release Header
Generates a banner for Ratatui releases featuring a Ratatui logo, version info, and a list of crates.
Used for README.md, documentation, and release materials. Updated for every release starting with v0.30.0 "Bryndza".
## Usage
```bash
cargo run --p release-header
```
Press any key to exit. Creates a fixed 68x16 terminal viewport.

View File

@@ -1,171 +0,0 @@
//! Generates a terminal banner for Ratatui releases featuring a Ratatui logo, version info, and
//! a list of crates.
//!
//! Used for README.md, documentation, and release materials. Updated for every release starting
//! with v0.30.0 "Bryndza".
//!
//! This example runs with the Ratatui library code in the branch that you are currently
//! reading. See the [`latest`] branch for the code which works with the most recent Ratatui
//! release.
//!
//! [`latest`]: https://github.com/ratatui/ratatui/tree/latest
use std::io::stdout;
use std::iter::zip;
use ratatui::crossterm::terminal::{EnterAlternateScreen, LeaveAlternateScreen};
use ratatui::crossterm::{event, execute};
use ratatui::layout::{Constraint, Flex, Layout, Margin, Rect, Spacing};
use ratatui::style::{Color, Stylize};
use ratatui::symbols::merge::MergeStrategy;
use ratatui::text::Line;
use ratatui::widgets::{Block, BorderType, Padding, Paragraph, RatatuiLogo};
use ratatui::{DefaultTerminal, Frame, TerminalOptions, Viewport};
const SEMVER: &str = "0.30.0";
const RELEASE_NAME: &str = "Bryndza";
const MAIN_DISHES: [&str; 4] = [
"> ratatui",
"> ratatui-core",
"> ratatui-widgets",
"> ratatui-macros",
];
const BACKENDS: [&str; 3] = [
"> ratatui-crossterm",
"> ratatui-termion",
"> ratatui-termwiz",
];
const FG_COLOR: Color = Color::Rgb(246, 214, 187); // #F6D6BB
const BG_COLOR: Color = Color::Rgb(20, 20, 50); // #141432
const MENU_BORDER_COLOR: Color = Color::Rgb(255, 255, 160); // #FFFFA0
enum Rainbow {
Red,
Orange,
Yellow,
Green,
Blue,
Indigo,
Violet,
}
fn main() -> color_eyre::Result<()> {
color_eyre::install()?;
let viewport = Viewport::Fixed(Rect::new(0, 0, 68, 16));
let terminal = ratatui::init_with_options(TerminalOptions { viewport });
execute!(stdout(), EnterAlternateScreen).expect("failed to enter alternate screen");
let result = run(terminal);
execute!(stdout(), LeaveAlternateScreen).expect("failed to leave alternate screen");
ratatui::restore();
result
}
fn run(mut terminal: DefaultTerminal) -> color_eyre::Result<()> {
loop {
terminal.draw(render)?;
if event::read()?.is_key_press() {
break Ok(());
}
}
}
fn render(frame: &mut Frame) {
let area = frame.area();
frame.buffer_mut().set_style(area, (FG_COLOR, BG_COLOR));
let logo_width = 29;
let menu_width = 23;
let padding = 2; // Padding between logo and menu
let menu_borders = 3;
let height = MAIN_DISHES.len() as u16 + BACKENDS.len() as u16 + menu_borders;
let width = logo_width + menu_width + padding;
let center_area = area.centered(Constraint::Length(width), Constraint::Length(height));
let layout = Layout::horizontal(Constraint::from_lengths([logo_width, padding, menu_width]));
let [logo_area, _, menu_area] = center_area.layout(&layout);
render_logo(frame, logo_area);
render_menu(frame, menu_area);
}
fn render_logo(frame: &mut Frame, area: Rect) {
let area = area.inner(Margin::new(1, 0));
let layout = Layout::vertical(Constraint::from_lengths([6, 2, 1])).flex(Flex::End);
let [shadow_area, logo_area, version_area] = area.layout(&layout);
// Divide the logo into letter sections for individual coloring, then render a block for each
// letter with a color based on the row index.
let letter_layout = Layout::horizontal(Constraint::from_lengths([5, 4, 4, 4, 4, 5, 1]));
for (row_index, row) in shadow_area.rows().enumerate() {
for (rainbow, letter_area) in zip(Rainbow::ROYGBIV, row.layout_vec(&letter_layout)) {
let color = rainbow.gradient_color(row_index);
frame.render_widget(Block::new().style(color), letter_area);
}
// Render the Ratatui logo truncated.
frame.render_widget(RatatuiLogo::small(), row);
}
frame.render_widget(Block::new().style(FG_COLOR), logo_area);
frame.render_widget(RatatuiLogo::small(), logo_area);
frame.render_widget(format!("v{SEMVER} \"{RELEASE_NAME}\"").dim(), version_area);
}
impl Rainbow {
const RED_GRADIENT: [u8; 6] = [41, 43, 50, 68, 104, 156];
const GREEN_GRADIENT: [u8; 6] = [24, 30, 41, 65, 105, 168];
const BLUE_GRADIENT: [u8; 6] = [55, 57, 62, 78, 113, 166];
const AMBIENT_GRADIENT: [u8; 6] = [17, 18, 20, 25, 40, 60];
const ROYGBIV: [Self; 7] = [
Self::Red,
Self::Orange,
Self::Yellow,
Self::Green,
Self::Blue,
Self::Indigo,
Self::Violet,
];
fn gradient_color(&self, row: usize) -> Color {
let ambient = Self::AMBIENT_GRADIENT[row];
let red = Self::RED_GRADIENT[row];
let green = Self::GREEN_GRADIENT[row];
let blue = Self::BLUE_GRADIENT[row];
let blue_sat = Self::AMBIENT_GRADIENT[row].saturating_mul(6 - row as u8);
let (r, g, b) = match self {
Self::Red => (red, ambient, blue_sat),
Self::Orange => (red, green / 2, blue_sat),
Self::Yellow => (red, green, blue_sat),
Self::Green => (ambient, green, blue_sat),
Self::Blue => (ambient, ambient, blue.max(blue_sat)),
Self::Indigo => (blue, ambient, blue.max(blue_sat)),
Self::Violet => (red, ambient, blue.max(blue_sat)),
};
Color::Rgb(r, g, b)
}
}
fn render_menu(frame: &mut Frame, area: Rect) {
let layout = Layout::vertical(Constraint::from_lengths([
MAIN_DISHES.len() as u16 + 2,
BACKENDS.len() as u16 + 2,
]))
.spacing(Spacing::Overlap(1)); // Overlap to merge borders
let [main_dishes_area, backends_area] = area.layout(&layout);
render_menu_block(frame, main_dishes_area, "Main Courses", &MAIN_DISHES);
render_menu_block(frame, backends_area, "Pairings", &BACKENDS);
}
fn render_menu_block(frame: &mut Frame, area: Rect, title: &str, menu_items: &[&str]) {
let menu_block = Block::bordered()
.border_type(BorderType::Rounded)
.border_style(MENU_BORDER_COLOR)
.padding(Padding::horizontal(1))
.merge_borders(MergeStrategy::Fuzzy)
.title(title);
let menu_lines: Vec<Line> = menu_items.iter().map(|&item| Line::from(item)).collect();
frame.render_widget(Paragraph::new(menu_lines).block(menu_block), area);
}

View File

@@ -1,14 +0,0 @@
[package]
name = "scrollbar"
publish = false
license.workspace = true
edition.workspace = true
rust-version.workspace = true
[dependencies]
color-eyre.workspace = true
crossterm.workspace = true
ratatui.workspace = true
[lints]
workspace = true

View File

@@ -1,9 +0,0 @@
# Scrollbar demo
This example shows how to render different types of scrollbars.
To run this demo:
```shell
cargo run -p scrollbar
```

View File

@@ -1,17 +0,0 @@
[package]
name = "table"
publish = false
license.workspace = true
edition.workspace = true
rust-version.workspace = true
[dependencies]
color-eyre.workspace = true
crossterm.workspace = true
fakeit.workspace = true
itertools.workspace = true
ratatui.workspace = true
unicode-width.workspace = true
[lints]
workspace = true

View File

@@ -1,9 +0,0 @@
# Table demo
This example shows how to create an interactive table.
To run this demo:
```shell
cargo run -p table
```

View File

@@ -1,14 +0,0 @@
[package]
name = "todo-list"
publish = false
license.workspace = true
edition.workspace = true
rust-version.workspace = true
[dependencies]
color-eyre.workspace = true
crossterm.workspace = true
ratatui.workspace = true
[lints]
workspace = true

View File

@@ -1,9 +0,0 @@
# Todo list demo
This example demonstrates a simple todo list application.
To run this demo:
```shell
cargo run -p todo-list
```

View File

@@ -1,17 +0,0 @@
[package]
name = "tracing"
publish = false
license.workspace = true
edition.workspace = true
rust-version.workspace = true
[dependencies]
color-eyre.workspace = true
crossterm.workspace = true
ratatui.workspace = true
tracing.workspace = true
tracing-appender.workspace = true
tracing-subscriber = { workspace = true, features = ["env-filter"] }
[lints]
workspace = true

View File

@@ -1,9 +0,0 @@
# Tracing demo
This example demonstrates how to use the [tracing](https://crates.io/crates/tracing) crate to log to a file.
To run this demo:
```shell
cargo run -p tracing
```

View File

@@ -1,14 +0,0 @@
[package]
name = "user-input"
publish = false
license.workspace = true
edition.workspace = true
rust-version.workspace = true
[dependencies]
color-eyre.workspace = true
crossterm.workspace = true
ratatui.workspace = true
[lints]
workspace = true

View File

@@ -1,9 +0,0 @@
# User input demo
This example shows how to handle user input.
To run this demo:
```shell
cargo run -p user-input
```

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