Compare commits

..

186 Commits

Author SHA1 Message Date
Josh McKinney
e7831aedd4 chore: release 0.30.0-alpha.1 (#1618)
Versions:

```
ratatui = { path = "ratatui", version = "0.30.0-alpha.1" }
ratatui-core = { path = "ratatui-core", version = "0.1.0-alpha.2" }
ratatui-crossterm = { path = "ratatui-crossterm", version = "0.1.0-alpha.1" }
ratatui-termion = { path = "ratatui-termion", version = "0.1.0-alpha.1" }
ratatui-termwiz = { path = "ratatui-termwiz", version = "0.1.0-alpha.1" }
ratatui-widgets = { path = "ratatui-widgets", version = "0.3.0-alpha.1" }
```
2025-01-15 15:31:38 -08:00
Josh McKinney
4a871f993e ci: refactor xtask / toml formatting (#1602)
- format toml files using taplo
- add toml formatting check to CI
- use xtask consistently from bacon
- refactor xtask commands to take params instead of multiple commands
2025-01-14 14:20:05 -08:00
Josh McKinney
da05957fa0 docs: add widget-ref-container example (#1603)
Implements ideas alluded to by
<https://discord.com/channels/1070692720437383208/1072907135664529508/1323061053990637640>
and followup conversations.
2025-01-14 14:05:58 -08:00
dependabot[bot]
41d883da7a chore(deps): bump serde_json from 1.0.134 to 1.0.135 (#1614)
Bumps [serde_json](https://github.com/serde-rs/json) from 1.0.134 to
1.0.135.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/serde-rs/json/releases">serde_json's
releases</a>.</em></p>
<blockquote>
<h2>v1.0.135</h2>
<ul>
<li>Add serde_json::Map::into_values method (<a
href="https://redirect.github.com/serde-rs/json/issues/1226">#1226</a>,
thanks <a
href="https://github.com/tisonkun"><code>@​tisonkun</code></a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="9802c08d4e"><code>9802c08</code></a>
Release 1.0.135</li>
<li><a
href="b97935fde2"><code>b97935f</code></a>
Merge pull request <a
href="https://redirect.github.com/serde-rs/json/issues/1226">#1226</a>
from tisonkun/map-into-values</li>
<li><a
href="d48c224d12"><code>d48c224</code></a>
Add Map::into_values method</li>
<li><a
href="1e77cac742"><code>1e77cac</code></a>
Resolve precedence clippy lint</li>
<li>See full diff in <a
href="https://github.com/serde-rs/json/compare/v1.0.134...v1.0.135">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=serde_json&package-manager=cargo&previous-version=1.0.134&new-version=1.0.135)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-14 20:31:20 +03:00
dependabot[bot]
0552223511 chore(deps): bump rstest from 0.23.0 to 0.24.0 (#1610)
Bumps [rstest](https://github.com/la10736/rstest) from 0.23.0 to 0.24.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/la10736/rstest/releases">rstest's
releases</a>.</em></p>
<blockquote>
<h2>0.24.0</h2>
<h2>What's Changed</h2>
<ul>
<li>refactor: use <code>core</code> instead of <code>std</code> by <a
href="https://github.com/rnbguy"><code>@​rnbguy</code></a> in <a
href="https://redirect.github.com/la10736/rstest/pull/283">la10736/rstest#283</a></li>
<li>Fix msrv and complete no_std support by <a
href="https://github.com/la10736"><code>@​la10736</code></a> in <a
href="https://redirect.github.com/la10736/rstest/pull/285">la10736/rstest#285</a></li>
<li>replace futures with futures-util by <a
href="https://github.com/mati865"><code>@​mati865</code></a> in <a
href="https://redirect.github.com/la10736/rstest/pull/288">la10736/rstest#288</a></li>
<li>Introduce Context by <a
href="https://github.com/la10736"><code>@​la10736</code></a> in <a
href="https://redirect.github.com/la10736/rstest/pull/289">la10736/rstest#289</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a href="https://github.com/rnbguy"><code>@​rnbguy</code></a> made
their first contribution in <a
href="https://redirect.github.com/la10736/rstest/pull/283">la10736/rstest#283</a></li>
<li><a href="https://github.com/mati865"><code>@​mati865</code></a> made
their first contribution in <a
href="https://redirect.github.com/la10736/rstest/pull/288">la10736/rstest#288</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/la10736/rstest/compare/v0.23.0...v0.24.0">https://github.com/la10736/rstest/compare/v0.23.0...v0.24.0</a></p>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/la10736/rstest/blob/master/CHANGELOG.md">rstest's
changelog</a>.</em></p>
<blockquote>
<h2>[0.24.0] 2025/1/1</h2>
<h3>Changed</h3>
<ul>
<li>MSRV to 1.70.0 (see <a
href="https://redirect.github.com/la10736/rstest/issues/284">#284</a>
thanks to <a
href="https://github.com/rnbguy"><code>@​rnbguy</code></a>)</li>
</ul>
<h3>Add</h3>
<ul>
<li><code>#![no_std]</code> support: now you can use <code>rstest</code>
also in <code>no_std</code> lib
(see <a
href="https://redirect.github.com/la10736/rstest/issues/282">#282</a>
thanks to <a
href="https://github.com/rnbguy"><code>@​rnbguy</code></a>)</li>
<li><code>#[context]</code> to have test function name and other useful
thighs on
the tip of your fingers (see <a
href="https://redirect.github.com/la10736/rstest/issues/177">#177</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="d57af90f16"><code>d57af90</code></a>
Prepare release</li>
<li><a
href="dfc4ff0e42"><code>dfc4ff0</code></a>
Make clippy happy</li>
<li><a
href="01140c13a1"><code>01140c1</code></a>
Docs and changelog</li>
<li><a
href="4a9763f2fe"><code>4a9763f</code></a>
1. Test also matrix case</li>
<li><a
href="d77d7c6a48"><code>d77d7c6</code></a>
Base implementation</li>
<li><a
href="7792a9b7bc"><code>7792a9b</code></a>
Add the ability to run CI test on demand</li>
<li><a
href="11869b587c"><code>11869b5</code></a>
Don't add channel if the env is not provided explicitly.</li>
<li><a
href="9a78bfb78f"><code>9a78bfb</code></a>
replace futures with futures-util</li>
<li><a
href="88f9c2ef6f"><code>88f9c2e</code></a>
<a href="https://redirect.github.com/la10736/rstest/issues/282">#282</a>
: add e2e test, cover also the once fixture case and add cheange log
lines</li>
<li><a
href="cb5db35f26"><code>cb5db35</code></a>
Close <a
href="https://redirect.github.com/la10736/rstest/issues/284">#284</a>
and add a test to check msrv on rendered code.</li>
<li>Additional commits viewable in <a
href="https://github.com/la10736/rstest/compare/v0.23.0...v0.24.0">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=rstest&package-manager=cargo&previous-version=0.23.0&new-version=0.24.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-14 20:31:03 +03:00
dependabot[bot]
21a561b1b3 chore(deps): bump tokio from 1.42.0 to 1.43.0 (#1613)
Bumps [tokio](https://github.com/tokio-rs/tokio) from 1.42.0 to 1.43.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/tokio-rs/tokio/releases">tokio's
releases</a>.</em></p>
<blockquote>
<h2>Tokio v1.43.0</h2>
<h1>1.43.0 (Jan 8th, 2025)</h1>
<h3>Added</h3>
<ul>
<li>net: add <code>UdpSocket::peek</code> methods (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/7068">#7068</a>)</li>
<li>net: add support for Haiku OS (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/7042">#7042</a>)</li>
<li>process: add <code>Command::into_std()</code> (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/7014">#7014</a>)</li>
<li>signal: add <code>SignalKind::info</code> on illumos (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/6995">#6995</a>)</li>
<li>signal: add support for realtime signals on illumos (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/7029">#7029</a>)</li>
</ul>
<h3>Fixed</h3>
<ul>
<li>io: don't call <code>set_len</code> before initializing vector in
<code>Blocking</code> (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/7054">#7054</a>)</li>
<li>macros: suppress <code>clippy::needless_return</code> in
<code>#[tokio::main]</code> (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/6874">#6874</a>)</li>
<li>runtime: fix thread parking on WebAssembly (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/7041">#7041</a>)</li>
</ul>
<h3>Changes</h3>
<ul>
<li>chore: use unsync loads for <code>unsync_load</code> (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/7073">#7073</a>)</li>
<li>io: use <code>Buf::put_bytes</code> in <code>Repeat</code> read impl
(<a
href="https://redirect.github.com/tokio-rs/tokio/issues/7055">#7055</a>)</li>
<li>task: drop the join waker of a task eagerly (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/6986">#6986</a>)</li>
</ul>
<h3>Changes to unstable APIs</h3>
<ul>
<li>metrics: improve flexibility of H2Histogram Configuration (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/6963">#6963</a>)</li>
<li>taskdump: add accessor methods for backtrace (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/6975">#6975</a>)</li>
</ul>
<h3>Documented</h3>
<ul>
<li>io: clarify <code>ReadBuf::uninit</code> allows initialized buffers
as well (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/7053">#7053</a>)</li>
<li>net: fix ambiguity in <code>TcpStream::try_write_vectored</code>
docs (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/7067">#7067</a>)</li>
<li>runtime: fix <code>LocalRuntime</code> doc links (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/7074">#7074</a>)</li>
<li>sync: extend documentation for
<code>watch::Receiver::wait_for</code> (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/7038">#7038</a>)</li>
<li>sync: fix typos in <code>OnceCell</code> docs (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/7047">#7047</a>)</li>
</ul>
<p><a
href="https://redirect.github.com/tokio-rs/tokio/issues/6874">#6874</a>:
<a
href="https://redirect.github.com/tokio-rs/tokio/pull/6874">tokio-rs/tokio#6874</a>
<a
href="https://redirect.github.com/tokio-rs/tokio/issues/6963">#6963</a>:
<a
href="https://redirect.github.com/tokio-rs/tokio/pull/6963">tokio-rs/tokio#6963</a>
<a
href="https://redirect.github.com/tokio-rs/tokio/issues/6975">#6975</a>:
<a
href="https://redirect.github.com/tokio-rs/tokio/pull/6975">tokio-rs/tokio#6975</a>
<a
href="https://redirect.github.com/tokio-rs/tokio/issues/6986">#6986</a>:
<a
href="https://redirect.github.com/tokio-rs/tokio/pull/6986">tokio-rs/tokio#6986</a>
<a
href="https://redirect.github.com/tokio-rs/tokio/issues/6995">#6995</a>:
<a
href="https://redirect.github.com/tokio-rs/tokio/pull/6995">tokio-rs/tokio#6995</a>
<a
href="https://redirect.github.com/tokio-rs/tokio/issues/7014">#7014</a>:
<a
href="https://redirect.github.com/tokio-rs/tokio/pull/7014">tokio-rs/tokio#7014</a>
<a
href="https://redirect.github.com/tokio-rs/tokio/issues/7029">#7029</a>:
<a
href="https://redirect.github.com/tokio-rs/tokio/pull/7029">tokio-rs/tokio#7029</a>
<a
href="https://redirect.github.com/tokio-rs/tokio/issues/7038">#7038</a>:
<a
href="https://redirect.github.com/tokio-rs/tokio/pull/7038">tokio-rs/tokio#7038</a>
<a
href="https://redirect.github.com/tokio-rs/tokio/issues/7041">#7041</a>:
<a
href="https://redirect.github.com/tokio-rs/tokio/pull/7041">tokio-rs/tokio#7041</a>
<a
href="https://redirect.github.com/tokio-rs/tokio/issues/7042">#7042</a>:
<a
href="https://redirect.github.com/tokio-rs/tokio/pull/7042">tokio-rs/tokio#7042</a>
<a
href="https://redirect.github.com/tokio-rs/tokio/issues/7047">#7047</a>:
<a
href="https://redirect.github.com/tokio-rs/tokio/pull/7047">tokio-rs/tokio#7047</a>
<a
href="https://redirect.github.com/tokio-rs/tokio/issues/7053">#7053</a>:
<a
href="https://redirect.github.com/tokio-rs/tokio/pull/7053">tokio-rs/tokio#7053</a>
<a
href="https://redirect.github.com/tokio-rs/tokio/issues/7054">#7054</a>:
<a
href="https://redirect.github.com/tokio-rs/tokio/pull/7054">tokio-rs/tokio#7054</a>
<a
href="https://redirect.github.com/tokio-rs/tokio/issues/7055">#7055</a>:
<a
href="https://redirect.github.com/tokio-rs/tokio/pull/7055">tokio-rs/tokio#7055</a></p>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="5f3296df77"><code>5f3296d</code></a>
chore: prepare Tokio v1.43.0 (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/7079">#7079</a>)</li>
<li><a
href="cc974a646b"><code>cc974a6</code></a>
chore: prepare tokio-macros v2.5.0 (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/7078">#7078</a>)</li>
<li><a
href="15495fd883"><code>15495fd</code></a>
metrics: improve flexibility of H2Histogram Configuration (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/6963">#6963</a>)</li>
<li><a
href="ad4183412a"><code>ad41834</code></a>
io: don't call <code>set_len</code> before initializing vector in
<code>Blocking</code> (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/7054">#7054</a>)</li>
<li><a
href="bd3e857737"><code>bd3e857</code></a>
runtime: move <code>is_join_waker_set</code> assertion in
<code>unset_waker</code> (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/7072">#7072</a>)</li>
<li><a
href="15f73666f1"><code>15f7366</code></a>
runtime: fix <code>LocalRuntime</code> doc links (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/7074">#7074</a>)</li>
<li><a
href="fd2048dad1"><code>fd2048d</code></a>
ci: split miri jobs into unit and integration tests (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/7071">#7071</a>)</li>
<li><a
href="e8f39157b6"><code>e8f3915</code></a>
chore: use unsync loads for <code>unsync_load</code> (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/7073">#7073</a>)</li>
<li><a
href="67f127769b"><code>67f1277</code></a>
net: fix ambiguity in <code>TcpStream::try_write_vectored</code> docs
(<a
href="https://redirect.github.com/tokio-rs/tokio/issues/7067">#7067</a>)</li>
<li><a
href="463502cbaf"><code>463502c</code></a>
io: clarify <code>ReadBuf::uninit</code> allows initialized buffers as
well (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/7053">#7053</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/tokio-rs/tokio/compare/tokio-1.42.0...tokio-1.43.0">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=tokio&package-manager=cargo&previous-version=1.42.0&new-version=1.43.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-13 13:18:16 -08:00
dependabot[bot]
079d74ce14 chore(deps): bump bitflags from 2.6.0 to 2.7.0 (#1615)
Bumps [bitflags](https://github.com/bitflags/bitflags) from 2.6.0 to
2.7.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/bitflags/bitflags/releases">bitflags's
releases</a>.</em></p>
<blockquote>
<h2>2.7.0</h2>
<h2>What's Changed</h2>
<ul>
<li>Fix <code>clippy::doc_lazy_continuation</code> lints by <a
href="https://github.com/waywardmonkeys"><code>@​waywardmonkeys</code></a>
in <a
href="https://redirect.github.com/bitflags/bitflags/pull/414">bitflags/bitflags#414</a></li>
<li>Run clippy on extra features in CI. by <a
href="https://github.com/waywardmonkeys"><code>@​waywardmonkeys</code></a>
in <a
href="https://redirect.github.com/bitflags/bitflags/pull/415">bitflags/bitflags#415</a></li>
<li>Fix CI: trybuild refresh, allow some clippy restrictions. by <a
href="https://github.com/waywardmonkeys"><code>@​waywardmonkeys</code></a>
in <a
href="https://redirect.github.com/bitflags/bitflags/pull/417">bitflags/bitflags#417</a></li>
<li>Update zerocopy version in example by <a
href="https://github.com/KodrAus"><code>@​KodrAus</code></a> in <a
href="https://redirect.github.com/bitflags/bitflags/pull/422">bitflags/bitflags#422</a></li>
<li>Add method to check if unknown bits are set by <a
href="https://github.com/wysiwys"><code>@​wysiwys</code></a> in <a
href="https://redirect.github.com/bitflags/bitflags/pull/426">bitflags/bitflags#426</a></li>
<li>Update error messages by <a
href="https://github.com/KodrAus"><code>@​KodrAus</code></a> in <a
href="https://redirect.github.com/bitflags/bitflags/pull/427">bitflags/bitflags#427</a></li>
<li>Add <code>truncate(&amp;mut self)</code> method to unset unknown
bits by <a href="https://github.com/wysiwys"><code>@​wysiwys</code></a>
in <a
href="https://redirect.github.com/bitflags/bitflags/pull/428">bitflags/bitflags#428</a></li>
<li>Update error messages by <a
href="https://github.com/KodrAus"><code>@​KodrAus</code></a> in <a
href="https://redirect.github.com/bitflags/bitflags/pull/429">bitflags/bitflags#429</a></li>
<li>Prepare for 2.7.0 release by <a
href="https://github.com/KodrAus"><code>@​KodrAus</code></a> in <a
href="https://redirect.github.com/bitflags/bitflags/pull/430">bitflags/bitflags#430</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a href="https://github.com/wysiwys"><code>@​wysiwys</code></a> made
their first contribution in <a
href="https://redirect.github.com/bitflags/bitflags/pull/426">bitflags/bitflags#426</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/bitflags/bitflags/compare/2.6.0...2.7.0">https://github.com/bitflags/bitflags/compare/2.6.0...2.7.0</a></p>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/bitflags/bitflags/blob/main/CHANGELOG.md">bitflags's
changelog</a>.</em></p>
<blockquote>
<h1>2.7.0</h1>
<h2>What's Changed</h2>
<ul>
<li>Fix <code>clippy::doc_lazy_continuation</code> lints by <a
href="https://github.com/waywardmonkeys"><code>@​waywardmonkeys</code></a>
in <a
href="https://redirect.github.com/bitflags/bitflags/pull/414">bitflags/bitflags#414</a></li>
<li>Run clippy on extra features in CI. by <a
href="https://github.com/waywardmonkeys"><code>@​waywardmonkeys</code></a>
in <a
href="https://redirect.github.com/bitflags/bitflags/pull/415">bitflags/bitflags#415</a></li>
<li>Fix CI: trybuild refresh, allow some clippy restrictions. by <a
href="https://github.com/waywardmonkeys"><code>@​waywardmonkeys</code></a>
in <a
href="https://redirect.github.com/bitflags/bitflags/pull/417">bitflags/bitflags#417</a></li>
<li>Update zerocopy version in example by <a
href="https://github.com/KodrAus"><code>@​KodrAus</code></a> in <a
href="https://redirect.github.com/bitflags/bitflags/pull/422">bitflags/bitflags#422</a></li>
<li>Add method to check if unknown bits are set by <a
href="https://github.com/wysiwys"><code>@​wysiwys</code></a> in <a
href="https://redirect.github.com/bitflags/bitflags/pull/426">bitflags/bitflags#426</a></li>
<li>Update error messages by <a
href="https://github.com/KodrAus"><code>@​KodrAus</code></a> in <a
href="https://redirect.github.com/bitflags/bitflags/pull/427">bitflags/bitflags#427</a></li>
<li>Add <code>truncate(&amp;mut self)</code> method to unset unknown
bits by <a href="https://github.com/wysiwys"><code>@​wysiwys</code></a>
in <a
href="https://redirect.github.com/bitflags/bitflags/pull/428">bitflags/bitflags#428</a></li>
<li>Update error messages by <a
href="https://github.com/KodrAus"><code>@​KodrAus</code></a> in <a
href="https://redirect.github.com/bitflags/bitflags/pull/429">bitflags/bitflags#429</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a href="https://github.com/wysiwys"><code>@​wysiwys</code></a> made
their first contribution in <a
href="https://redirect.github.com/bitflags/bitflags/pull/426">bitflags/bitflags#426</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/bitflags/bitflags/compare/2.6.0...2.7.0">https://github.com/bitflags/bitflags/compare/2.6.0...2.7.0</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="10b9fd3a9e"><code>10b9fd3</code></a>
Merge pull request <a
href="https://redirect.github.com/bitflags/bitflags/issues/430">#430</a>
from bitflags/cargo/2.7.0</li>
<li><a
href="137485a2c2"><code>137485a</code></a>
prepare for 2.7.0 release</li>
<li><a
href="6c0dfa4a0c"><code>6c0dfa4</code></a>
Merge pull request <a
href="https://redirect.github.com/bitflags/bitflags/issues/429">#429</a>
from bitflags/fix/err-msgs-2</li>
<li><a
href="451fdf4778"><code>451fdf4</code></a>
update rust before building</li>
<li><a
href="f5ac5b14e5"><code>f5ac5b1</code></a>
update error messages</li>
<li><a
href="30901e6dee"><code>30901e6</code></a>
Merge pull request <a
href="https://redirect.github.com/bitflags/bitflags/issues/428">#428</a>
from wysiwys/main</li>
<li><a
href="7b9ecab84d"><code>7b9ecab</code></a>
add <code>truncate(&amp;mut self)</code> method to unset unknown
bits</li>
<li><a
href="85983a4b1c"><code>85983a4</code></a>
Merge pull request <a
href="https://redirect.github.com/bitflags/bitflags/issues/427">#427</a>
from bitflags/ci/error-msgs</li>
<li><a
href="f00779337d"><code>f007793</code></a>
remove match case</li>
<li><a
href="6ebcd190a2"><code>6ebcd19</code></a>
add test case for match statement</li>
<li>Additional commits viewable in <a
href="https://github.com/bitflags/bitflags/compare/2.6.0...2.7.0">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=bitflags&package-manager=cargo&previous-version=2.6.0&new-version=2.7.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-13 13:17:41 -08:00
dependabot[bot]
22ec4f7414 chore(deps): bump instability from 0.3.6 to 0.3.7 (#1616)
Bumps [instability](https://github.com/ratatui-org/instability) from
0.3.6 to 0.3.7.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/ratatui-org/instability/releases">instability's
releases</a>.</em></p>
<blockquote>
<h2>instability-example-v0.3.7</h2>
<h3>Other</h3>
<ul>
<li>Add #[allow(unused_imports)] lint to unstable reexports (<a
href="https://redirect.github.com/ratatui/instability/pull/21">#21</a>)</li>
</ul>
<h2>instability-v0.3.7</h2>
<h3>Other</h3>
<ul>
<li>Add #[allow(unused_imports)] lint to unstable reexports (<a
href="https://redirect.github.com/ratatui/instability/pull/21">#21</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/ratatui/instability/blob/main/CHANGELOG.md">instability's
changelog</a>.</em></p>
<blockquote>
<h2><a
href="https://github.com/ratatui/instability/compare/instability-v0.3.6...instability-v0.3.7">0.3.7</a>
- 2025-01-10</h2>
<h3>Other</h3>
<ul>
<li>Add #[allow(unused_imports)] lint to unstable reexports (<a
href="https://redirect.github.com/ratatui/instability/pull/21">#21</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="c9ae88731b"><code>c9ae887</code></a>
chore: release v0.3.7 (<a
href="https://redirect.github.com/ratatui-org/instability/issues/22">#22</a>)</li>
<li><a
href="cf3e49aa16"><code>cf3e49a</code></a>
Add #[allow(unused_imports)] lint to unstable reexports (<a
href="https://redirect.github.com/ratatui-org/instability/issues/21">#21</a>)</li>
<li>See full diff in <a
href="https://github.com/ratatui-org/instability/compare/instability-v0.3.6...instability-v0.3.7">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=instability&package-manager=cargo&previous-version=0.3.6&new-version=0.3.7)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-13 13:16:24 -08:00
Josh McKinney
6f213191ef chore: rename examples with clashing names (#1597)
These will eventually be moved / consolidated elsewhere, but this clears
the warnings while building for now.
2025-01-11 00:10:54 +03:00
Josh McKinney
088aac136d docs(readme): tweak links and badges (#1598) 2025-01-11 00:06:52 +03:00
Josh McKinney
a692a6e371 fix(lint): apply rust 1.84 clippy suggestions (#1612)
The canvas map constants are now statics instead.
Fixes
https://rust-lang.github.io/rust-clippy/master/index.html\#large_const_arrays
2025-01-10 23:17:07 +03:00
dawe
1798512e94 docs: fix wording in user_input example (#1611)
Fix wording in `user_input.rs` example.
2025-01-07 19:25:47 +03:00
dependabot[bot]
32f3833a6d chore(deps): bump clap-cargo from 0.14.1 to 0.15.1 (#1608)
Bumps [clap-cargo](https://github.com/crate-ci/clap-cargo) from 0.14.1
to 0.15.1.
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/crate-ci/clap-cargo/blob/master/CHANGELOG.md">clap-cargo's
changelog</a>.</em></p>
<blockquote>
<h2>[0.15.1] - 2025-01-03</h2>
<h3>Fix</h3>
<ul>
<li>Reduce <code>Cargo.lock</code> content</li>
</ul>
<h2>[0.15.0] - 2025-01-02</h2>
<h3>Breaking Changes</h3>
<ul>
<li>Upgraded <code>cargo_metadata</code> to 0.19</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="3eadd0f476"><code>3eadd0f</code></a>
chore: Release clap-cargo version 0.15.1</li>
<li><a
href="427af69642"><code>427af69</code></a>
docs: Update changelog</li>
<li><a
href="a37548f05b"><code>a37548f</code></a>
Merge pull request <a
href="https://redirect.github.com/crate-ci/clap-cargo/issues/79">#79</a>
from dtolnay-contrib/deps</li>
<li><a
href="80914f3015"><code>80914f3</code></a>
chore: Omit serde/serde_json from lockfiles not using
cargo_metadata</li>
<li><a
href="9b76d52f24"><code>9b76d52</code></a>
chore: Release clap-cargo version 0.15.0</li>
<li><a
href="bb2590c3af"><code>bb2590c</code></a>
docs: Update changelog</li>
<li><a
href="b44569a3ce"><code>b44569a</code></a>
Merge pull request <a
href="https://redirect.github.com/crate-ci/clap-cargo/issues/78">#78</a>
from epage/renovate/cargo_metadata-0.x</li>
<li><a
href="28a8c00d3f"><code>28a8c00</code></a>
chore(deps)!: Update Rust crate cargo_metadata to 0.19</li>
<li><a
href="4f485ee5de"><code>4f485ee</code></a>
Merge pull request <a
href="https://redirect.github.com/crate-ci/clap-cargo/issues/77">#77</a>
from crate-ci/renovate/stable-1.x</li>
<li><a
href="cfe5cacab3"><code>cfe5cac</code></a>
chore(deps): Update dependency STABLE to v1.83.0</li>
<li>Additional commits viewable in <a
href="https://github.com/crate-ci/clap-cargo/compare/v0.14.1...v0.15.1">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=clap-cargo&package-manager=cargo&previous-version=0.14.1&new-version=0.15.1)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-06 18:25:23 +03:00
dependabot[bot]
2ccc40e116 chore(deps): bump instability from 0.3.5 to 0.3.6 (#1609)
Bumps [instability](https://github.com/ratatui-org/instability) from
0.3.5 to 0.3.6.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/ratatui-org/instability/releases">instability's
releases</a>.</em></p>
<blockquote>
<h2>instability-example-v0.3.6</h2>
<h3>Added</h3>
<ul>
<li>Allow marking impl blocks unstable/stable (<a
href="https://redirect.github.com/ratatui-org/instability/issues/15">#15</a>)</li>
<li>add stable macro (<a
href="https://redirect.github.com/ratatui-org/instability/issues/14">#14</a>)</li>
<li>use doc(cfg)</li>
<li>allow use statements to be marked unstable (<a
href="https://redirect.github.com/ratatui-org/instability/issues/3">#3</a>)</li>
</ul>
<h3>Other</h3>
<ul>
<li>prepare instability-example for publish (<a
href="https://redirect.github.com/ratatui-org/instability/issues/18">#18</a>)</li>
<li>bump msrv to 1.63</li>
<li>tweak doc wording and formatting (<a
href="https://redirect.github.com/ratatui-org/instability/issues/4">#4</a>)</li>
<li>fork and change name to instabilty</li>
<li>Apply visibility restriction to struct fields (<a
href="https://redirect.github.com/ratatui/instability/pull/10">#10</a>)</li>
<li>Update to Rust edition 2021 (<a
href="https://redirect.github.com/ratatui/instability/pull/9">#9</a>)</li>
<li>Add issue tracking</li>
<li>Initial version</li>
</ul>
<h2>instability-v0.3.6</h2>
<h3>Other</h3>
<ul>
<li>Move pretty_assertions to dev-dependencies (<a
href="https://redirect.github.com/ratatui/instability/pull/19">#19</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/ratatui/instability/blob/main/CHANGELOG.md">instability's
changelog</a>.</em></p>
<blockquote>
<h2><a
href="https://github.com/ratatui/instability/compare/instability-v0.3.5...instability-v0.3.6">0.3.6</a>
- 2025-01-04</h2>
<h3>Other</h3>
<ul>
<li>Move pretty_assertions to dev-dependencies (<a
href="https://redirect.github.com/ratatui/instability/pull/19">#19</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="cf84fbe505"><code>cf84fbe</code></a>
chore: release v0.3.6 (<a
href="https://redirect.github.com/ratatui-org/instability/issues/20">#20</a>)</li>
<li><a
href="5eca2ee0d1"><code>5eca2ee</code></a>
Move pretty_assertions to dev-dependencies (<a
href="https://redirect.github.com/ratatui-org/instability/issues/19">#19</a>)</li>
<li>See full diff in <a
href="https://github.com/ratatui-org/instability/compare/instability-v0.3.5...instability-v0.3.6">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=instability&package-manager=cargo&previous-version=0.3.5&new-version=0.3.6)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-06 18:24:20 +03:00
dependabot[bot]
019e34e006 chore(deps): bump serde from 1.0.216 to 1.0.217 (#1600)
Bumps [serde](https://github.com/serde-rs/serde) from 1.0.216 to
1.0.217.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/serde-rs/serde/releases">serde's
releases</a>.</em></p>
<blockquote>
<h2>v1.0.217</h2>
<ul>
<li>Support serializing externally tagged unit variant inside flattened
field (<a
href="https://redirect.github.com/serde-rs/serde/issues/2786">#2786</a>,
thanks <a
href="https://github.com/Mingun"><code>@​Mingun</code></a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="930401b0dd"><code>930401b</code></a>
Release 1.0.217</li>
<li><a
href="cb6eaea151"><code>cb6eaea</code></a>
Fix roundtrip inconsistency:</li>
<li><a
href="b6f339ca36"><code>b6f339c</code></a>
Resolve repr_packed_without_abi clippy lint in tests</li>
<li><a
href="2a5caea1a8"><code>2a5caea</code></a>
Merge pull request <a
href="https://redirect.github.com/serde-rs/serde/issues/2872">#2872</a>
from dtolnay/ehpersonality</li>
<li><a
href="b9f93f99aa"><code>b9f93f9</code></a>
Add no-std CI on stable compiler</li>
<li><a
href="eb5cd476ba"><code>eb5cd47</code></a>
Drop #[lang = &quot;eh_personality&quot;] from no-std test</li>
<li><a
href="8478a3b7dd"><code>8478a3b</code></a>
Merge pull request <a
href="https://redirect.github.com/serde-rs/serde/issues/2871">#2871</a>
from dtolnay/nostdstart</li>
<li><a
href="dbb909136e"><code>dbb9091</code></a>
Replace #[start] with extern fn main</li>
<li>See full diff in <a
href="https://github.com/serde-rs/serde/compare/v1.0.216...v1.0.217">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=serde&package-manager=cargo&previous-version=1.0.216&new-version=1.0.217)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-30 22:24:53 +03:00
Josh McKinney
11cbb2ba87 chore: use cargo xtask for bacon clippy command (#1592) 2024-12-29 11:49:41 -08:00
n4n5
50ba96518f feat: add a new RatatuiMascot widget (#1584)
Move the Mascot from Demo2 into a new widget.
Make the Rat grey and adjust the other colors.

```rust
frame.render_widget(RatatuiMascot::default(), frame.area());
```
2024-12-26 04:32:38 -08:00
Josh McKinney
904b0aa723 refactor: move symbols to modules (#1594) 2024-12-26 04:05:05 -08:00
Josh McKinney
b544e394c9 chore: use clap instead of argh for demo example (#1591) 2024-12-24 22:45:39 -08:00
Josh McKinney
1d28c89fe5 feat: add conversions for anstyle (#1581)
https://crates.io/crates/anstyle makes it possible to define colors in
an interoperable way. This makes it possible for applications to easily
load colors from a variety of formats.

This is gated by the anstyle feature flag which is disabled by default.

---------

Co-authored-by: Orhun Parmaksız <orhun@archlinux.org>
2024-12-24 12:03:14 -08:00
dependabot[bot]
1d2882636e chore(deps): bump serde_json from 1.0.133 to 1.0.134 (#1586)
Bumps [serde_json](https://github.com/serde-rs/json) from 1.0.133 to
1.0.134.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/serde-rs/json/releases">serde_json's
releases</a>.</em></p>
<blockquote>
<h2>v1.0.134</h2>
<ul>
<li>Add <code>RawValue</code> associated constants for literal
<code>null</code>, <code>true</code>, <code>false</code> (<a
href="https://redirect.github.com/serde-rs/json/issues/1221">#1221</a>,
thanks <a
href="https://github.com/bheylin"><code>@​bheylin</code></a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="b2a1415aad"><code>b2a1415</code></a>
Release 1.0.134</li>
<li><a
href="9875785f24"><code>9875785</code></a>
Tweak wording of NULL/TRUE/FALSE documentation</li>
<li><a
href="4aa05b9143"><code>4aa05b9</code></a>
Merge pull request <a
href="https://redirect.github.com/serde-rs/json/issues/1222">#1222</a>
from dtolnay/rawvalueassoc</li>
<li><a
href="f42c7c760b"><code>f42c7c7</code></a>
Move RawValue associated constants into same impl block as public
functions</li>
<li><a
href="96576bad53"><code>96576ba</code></a>
Merge pull request <a
href="https://redirect.github.com/serde-rs/json/issues/1221">#1221</a>
from bheylin/add-const-raw-values-for-null-and-bools</li>
<li><a
href="4db66fb0b2"><code>4db66fb</code></a>
Add <code>'static</code> lifetime to <code>const</code>'s</li>
<li><a
href="9c9aa1f380"><code>9c9aa1f</code></a>
Add literal 'null', 'true' and 'false' consts to <code>RawValue</code>
struct.</li>
<li>See full diff in <a
href="https://github.com/serde-rs/json/compare/v1.0.133...v1.0.134">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=serde_json&package-manager=cargo&previous-version=1.0.133&new-version=1.0.134)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-24 11:39:39 -08:00
dependabot[bot]
157cb3401b chore(deps): bump clap-verbosity-flag from 3.0.1 to 3.0.2 (#1587)
Bumps
[clap-verbosity-flag](https://github.com/clap-rs/clap-verbosity-flag)
from 3.0.1 to 3.0.2.
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/clap-rs/clap-verbosity-flag/blob/master/CHANGELOG.md">clap-verbosity-flag's
changelog</a>.</em></p>
<blockquote>
<h2>[3.0.2] - 2024-12-16</h2>
<h3>Features</h3>
<ul>
<li>Add a <code>Copy</code> impl to <code>Verbosity</code></li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="8a65b98d9b"><code>8a65b98</code></a>
chore: Release clap-verbosity-flag version 3.0.2</li>
<li><a
href="6f634c99b2"><code>6f634c9</code></a>
docs: Update changelog</li>
<li><a
href="bcfc9e88f6"><code>bcfc9e8</code></a>
Merge pull request <a
href="https://redirect.github.com/clap-rs/clap-verbosity-flag/issues/133">#133</a>
from joshka/jm/verbosity-copy</li>
<li><a
href="9191996bed"><code>9191996</code></a>
feat: Impl Copy for Verbosity</li>
<li><a
href="9f72d6c034"><code>9f72d6c</code></a>
Merge pull request <a
href="https://redirect.github.com/clap-rs/clap-verbosity-flag/issues/131">#131</a>
from clap-rs/renovate/stable-1.x</li>
<li><a
href="3c00cdad77"><code>3c00cda</code></a>
chore(deps): Update compatible (dev) (<a
href="https://redirect.github.com/clap-rs/clap-verbosity-flag/issues/130">#130</a>)</li>
<li><a
href="061da5a9ef"><code>061da5a</code></a>
chore(deps): Update dependency STABLE to v1.83.0</li>
<li>See full diff in <a
href="https://github.com/clap-rs/clap-verbosity-flag/compare/v3.0.1...v3.0.2">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=clap-verbosity-flag&package-manager=cargo&previous-version=3.0.1&new-version=3.0.2)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-24 11:39:07 -08:00
dependabot[bot]
3d0c96a838 chore(deps): bump instability from 0.3.3 to 0.3.5 (#1588)
Bumps [instability](https://github.com/ratatui-org/instability) from
0.3.3 to 0.3.5.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/ratatui-org/instability/releases">instability's
releases</a>.</em></p>
<blockquote>
<h2>instability-example-v0.3.5</h2>
<h3>Added</h3>
<ul>
<li>Allow marking impl blocks unstable/stable (<a
href="https://redirect.github.com/ratatui-org/instability/issues/15">#15</a>)</li>
<li>add stable macro (<a
href="https://redirect.github.com/ratatui-org/instability/issues/14">#14</a>)</li>
<li>use doc(cfg)</li>
<li>allow use statements to be marked unstable (<a
href="https://redirect.github.com/ratatui-org/instability/issues/3">#3</a>)</li>
</ul>
<h3>Other</h3>
<ul>
<li>prepare instability-example for publish (<a
href="https://redirect.github.com/ratatui-org/instability/issues/18">#18</a>)</li>
<li>bump msrv to 1.63</li>
<li>tweak doc wording and formatting (<a
href="https://redirect.github.com/ratatui-org/instability/issues/4">#4</a>)</li>
<li>fork and change name to instabilty</li>
<li>Apply visibility restriction to struct fields (<a
href="https://redirect.github.com/ratatui/instability/pull/10">#10</a>)</li>
<li>Update to Rust edition 2021 (<a
href="https://redirect.github.com/ratatui/instability/pull/9">#9</a>)</li>
<li>Add issue tracking</li>
<li>Initial version</li>
</ul>
<h2>instability-v0.3.5</h2>
<h3>Other</h3>
<ul>
<li>prepare instability-example for publish (<a
href="https://redirect.github.com/ratatui-org/instability/issues/18">#18</a>)</li>
<li>clippy --fix</li>
</ul>
<h2>instability-example-v0.3.4</h2>
<p>No release notes provided.</p>
<h2>instability-v0.3.4</h2>
<h3>Added</h3>
<ul>
<li>Allow marking impl blocks unstable/stable (<a
href="https://redirect.github.com/ratatui/instability/pull/15">#15</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/ratatui/instability/blob/main/CHANGELOG.md">instability's
changelog</a>.</em></p>
<blockquote>
<h2><a
href="https://github.com/ratatui/instability/compare/instability-v0.3.4...instability-v0.3.5">0.3.5</a>
- 2024-12-21</h2>
<h3>Other</h3>
<ul>
<li>prepare instability-example for publish (<a
href="https://redirect.github.com/ratatui-org/instability/issues/18">#18</a>)</li>
<li>clippy --fix</li>
</ul>
<h2><a
href="https://github.com/ratatui/instability/compare/instability-v0.3.3...instability-v0.3.4">0.3.4</a>
- 2024-12-21</h2>
<h3>Added</h3>
<ul>
<li>Allow marking impl blocks unstable/stable (<a
href="https://redirect.github.com/ratatui/instability/pull/15">#15</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="6420472e5b"><code>6420472</code></a>
chore: release v0.3.5 (<a
href="https://redirect.github.com/ratatui-org/instability/issues/17">#17</a>)</li>
<li><a
href="308c590e9e"><code>308c590</code></a>
docs: prepare instability-example for publish (<a
href="https://redirect.github.com/ratatui-org/instability/issues/18">#18</a>)</li>
<li><a
href="7655288648"><code>7655288</code></a>
chore: clippy --fix</li>
<li><a
href="0762e064c2"><code>0762e06</code></a>
chore: release v0.3.4 (<a
href="https://redirect.github.com/ratatui-org/instability/issues/16">#16</a>)</li>
<li><a
href="596e13c00d"><code>596e13c</code></a>
feat: Allow marking impl blocks unstable/stable (<a
href="https://redirect.github.com/ratatui-org/instability/issues/15">#15</a>)</li>
<li>See full diff in <a
href="https://github.com/ratatui-org/instability/compare/instability-v0.3.3...instability-v0.3.5">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=instability&package-manager=cargo&previous-version=0.3.3&new-version=0.3.5)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-24 11:37:27 -08:00
dawe
03066d81bf docs: fix punctuation in canvas.rs documentation (#1583)
Fix end of sentence punctuation in canvas.rs docs.
2024-12-21 03:49:44 -05:00
Orhun Parmaksız
5f57d35234 chore(examples): add colors explorer demo app (#1580)
related #1512 

Moves the colors examples to apps

Co-authored-by: Josh McKinney <joshka@users.noreply.github.com>
2024-12-18 20:09:17 -08:00
Orhun Parmaksız
5c021bf344 chore(examples): add chart demo app (#1579)
related #1512 

Moves the chart example to apps

Co-authored-by: Josh McKinney <joshka@users.noreply.github.com>
2024-12-18 20:08:16 -08:00
Orhun Parmaksız
9721300a47 chore(examples): add canvas demo app (#1578)
related #1512 

This moves the canvas example to the apps and adds some interactivity
via changing the marker by pressing enter.
2024-12-18 20:06:12 -08:00
Orhun Parmaksız
9a541981b8 chore: make source files non-executable (#1577)
```bash
find . -type f -not -path './.git/*' -not -name '*.bash' -exec chmod 644 {} +
```

fixes #1576
2024-12-17 17:33:06 +03:00
Orhun Parmaksız
a6a1368250 chore(examples): add calendar explorer demo app (#1571)
Related to #1512 

As discussed, this moves the calendar example from ratatui to app
examples as an "explorer" example. It also adds interactivity where you
can press s to toggle between different styles of calendars.

---------

Co-authored-by: Josh McKinney <joshka@users.noreply.github.com>
2024-12-16 11:41:41 -08:00
dependabot[bot]
1fcca6369e chore(deps): bump tokio from 1.41.1 to 1.42.0 (#1574)
Bumps [tokio](https://github.com/tokio-rs/tokio) from 1.41.1 to 1.42.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/tokio-rs/tokio/releases">tokio's
releases</a>.</em></p>
<blockquote>
<h2>Tokio v1.42.0</h2>
<h1>1.42.0 (Dec 3rd, 2024)</h1>
<h3>Added</h3>
<ul>
<li>io: add <code>AsyncFd::{try_io, try_io_mut}</code> (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/6967">#6967</a>)</li>
</ul>
<h3>Fixed</h3>
<ul>
<li>io: avoid <code>ptr-&gt;ref-&gt;ptr</code> roundtrip in
RegistrationSet (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/6929">#6929</a>)</li>
<li>runtime: do not defer <code>yield_now</code> inside
<code>block_in_place</code> (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/6999">#6999</a>)</li>
</ul>
<h3>Changes</h3>
<ul>
<li>io: simplify io readiness logic (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/6966">#6966</a>)</li>
</ul>
<h3>Documented</h3>
<ul>
<li>net: fix docs for <code>tokio::net::unix::{pid_t, gid_t,
uid_t}</code> (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/6791">#6791</a>)</li>
<li>time: fix a typo in <code>Instant</code> docs (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/6982">#6982</a>)</li>
</ul>
<p><a
href="https://redirect.github.com/tokio-rs/tokio/issues/6791">#6791</a>:
<a
href="https://redirect.github.com/tokio-rs/tokio/pull/6791">tokio-rs/tokio#6791</a>
<a
href="https://redirect.github.com/tokio-rs/tokio/issues/6929">#6929</a>:
<a
href="https://redirect.github.com/tokio-rs/tokio/pull/6929">tokio-rs/tokio#6929</a>
<a
href="https://redirect.github.com/tokio-rs/tokio/issues/6966">#6966</a>:
<a
href="https://redirect.github.com/tokio-rs/tokio/pull/6966">tokio-rs/tokio#6966</a>
<a
href="https://redirect.github.com/tokio-rs/tokio/issues/6967">#6967</a>:
<a
href="https://redirect.github.com/tokio-rs/tokio/pull/6967">tokio-rs/tokio#6967</a>
<a
href="https://redirect.github.com/tokio-rs/tokio/issues/6982">#6982</a>:
<a
href="https://redirect.github.com/tokio-rs/tokio/pull/6982">tokio-rs/tokio#6982</a>
<a
href="https://redirect.github.com/tokio-rs/tokio/issues/6999">#6999</a>:
<a
href="https://redirect.github.com/tokio-rs/tokio/pull/6999">tokio-rs/tokio#6999</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="bb9d57017e"><code>bb9d570</code></a>
chore: prepare Tokio v1.42.0 (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/7005">#7005</a>)</li>
<li><a
href="af9c683d52"><code>af9c683</code></a>
tests: fix typo in build test instructions (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/7004">#7004</a>)</li>
<li><a
href="4bc5a1a058"><code>4bc5a1a</code></a>
ci: allow Unicode-3.0 license for unicode-ident (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/7006">#7006</a>)</li>
<li><a
href="f8948ea021"><code>f8948ea</code></a>
runtime: do not defer <code>yield_now</code> inside
<code>block_in_place</code> (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/6999">#6999</a>)</li>
<li><a
href="bce9780dd3"><code>bce9780</code></a>
time: use <code>array::from_fn</code> instead of manually creating array
(<a
href="https://redirect.github.com/tokio-rs/tokio/issues/7000">#7000</a>)</li>
<li><a
href="38151f30cb"><code>38151f3</code></a>
readme: unlist 1.32.x as LTS release (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/6997">#6997</a>)</li>
<li><a
href="5dda72d338"><code>5dda72d</code></a>
ci: pin valgrind to rustc 1.82 (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/6998">#6998</a>)</li>
<li><a
href="c07257f99f"><code>c07257f</code></a>
io: simplify io readiness logic (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/6966">#6966</a>)</li>
<li><a
href="d08578fc9a"><code>d08578f</code></a>
time: fix a typo in <code>Instant</code> docs (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/6982">#6982</a>)</li>
<li><a
href="4047d7962a"><code>4047d79</code></a>
miri: add annotations for tests with miri ignore (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/6981">#6981</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/tokio-rs/tokio/compare/tokio-1.41.1...tokio-1.42.0">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=tokio&package-manager=cargo&previous-version=1.41.1&new-version=1.42.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-16 10:26:49 -08:00
dependabot[bot]
694c788c24 chore(deps): bump serde from 1.0.215 to 1.0.216 (#1575)
Bumps [serde](https://github.com/serde-rs/serde) from 1.0.215 to
1.0.216.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/serde-rs/serde/releases">serde's
releases</a>.</em></p>
<blockquote>
<h2>v1.0.216</h2>
<ul>
<li>Mark all generated impls with #[automatically_derived] to exclude
from code coverage (<a
href="https://redirect.github.com/serde-rs/serde/issues/2866">#2866</a>,
<a
href="https://redirect.github.com/serde-rs/serde/issues/2868">#2868</a>,
thanks <a
href="https://github.com/tdittr"><code>@​tdittr</code></a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="ad8dd4148b"><code>ad8dd41</code></a>
Release 1.0.216</li>
<li><a
href="f91d2ed9ae"><code>f91d2ed</code></a>
Merge pull request <a
href="https://redirect.github.com/serde-rs/serde/issues/2868">#2868</a>
from dtolnay/automaticallyderived</li>
<li><a
href="9497463718"><code>9497463</code></a>
Mark all generated trait impls as #[automatically_derived]</li>
<li><a
href="46e9ecfcdd"><code>46e9ecf</code></a>
Merge pull request <a
href="https://redirect.github.com/serde-rs/serde/issues/2866">#2866</a>
from tdittr/mark-visitors-as-generated</li>
<li><a
href="e9c399c822"><code>e9c399c</code></a>
Mark generated <code>impl de::Visitor</code> blocks as
<code>#[automatically_derived]</code></li>
<li><a
href="b9dbfcb4ac"><code>b9dbfcb</code></a>
Switch out fnv in favor of foldhash in test</li>
<li><a
href="c270e27a4d"><code>c270e27</code></a>
Use BuildHasher instead of Hasher in collection macros</li>
<li><a
href="0307f604ea"><code>0307f60</code></a>
Resolve question_mark clippy lint in build script</li>
<li>See full diff in <a
href="https://github.com/serde-rs/serde/compare/v1.0.215...v1.0.216">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=serde&package-manager=cargo&previous-version=1.0.215&new-version=1.0.216)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-16 10:23:50 -08:00
Orhun Parmaksız
6e436725e4 docs(readme): reimagine README.md (#1569)
This is the result of the re-imagination of a more suitable README.md. It
is simpler and shorter: not giving more information to the user than
they actually need.

Also updates the quickstart code with the up-to-date version and adds
link to templates which was missing.

---------

Co-authored-by: Josh McKinney <joshka@users.noreply.github.com>
2024-12-14 22:22:33 +03:00
Orhun Parmaksız
dafb716f9d docs(widgets): add example for grouped barchart (#1566)
related #1512

---------

Co-authored-by: Josh McKinney <joshka@users.noreply.github.com>
2024-12-12 20:42:41 +03:00
Orhun Parmaksız
819e92cd44 chore(examples): add weather demo app (#1567)
related to #1512
2024-12-11 11:25:56 +03:00
dependabot[bot]
a38066d2d1 chore(deps): bump dawidd6/action-download-artifact from 6 to 7 (#1560)
Bumps
[dawidd6/action-download-artifact](https://github.com/dawidd6/action-download-artifact)
from 6 to 7.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/dawidd6/action-download-artifact/releases">dawidd6/action-download-artifact's
releases</a>.</em></p>
<blockquote>
<h2>v7</h2>
<h2>What's Changed</h2>
<ul>
<li>build(deps): bump fast-xml-parser from 4.4.0 to 4.4.1 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/dawidd6/action-download-artifact/pull/299">dawidd6/action-download-artifact#299</a></li>
<li>build(deps): bump <code>@​actions/artifact</code> from 2.1.7 to
2.1.9 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/dawidd6/action-download-artifact/pull/300">dawidd6/action-download-artifact#300</a></li>
<li>build(deps): bump adm-zip from 0.5.14 to 0.5.15 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/dawidd6/action-download-artifact/pull/301">dawidd6/action-download-artifact#301</a></li>
<li>build(deps): bump adm-zip from 0.5.15 to 0.5.16 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/dawidd6/action-download-artifact/pull/306">dawidd6/action-download-artifact#306</a></li>
<li>build(deps): bump path-to-regexp from 6.2.2 to 6.3.0 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/dawidd6/action-download-artifact/pull/307">dawidd6/action-download-artifact#307</a></li>
<li>build(deps): bump <code>@​actions/artifact</code> from 2.1.9 to
2.1.10 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/dawidd6/action-download-artifact/pull/311">dawidd6/action-download-artifact#311</a></li>
<li>build(deps): bump <code>@​actions/core</code> from 1.10.1 to 1.11.0
by <a href="https://github.com/dependabot"><code>@​dependabot</code></a>
in <a
href="https://redirect.github.com/dawidd6/action-download-artifact/pull/310">dawidd6/action-download-artifact#310</a></li>
<li>build(deps): bump <code>@​actions/core</code> from 1.11.0 to 1.11.1
by <a href="https://github.com/dependabot"><code>@​dependabot</code></a>
in <a
href="https://redirect.github.com/dawidd6/action-download-artifact/pull/312">dawidd6/action-download-artifact#312</a></li>
<li>build(deps): bump <code>@​actions/artifact</code> from 2.1.10 to
2.1.11 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/dawidd6/action-download-artifact/pull/313">dawidd6/action-download-artifact#313</a></li>
<li>build(deps): Fix cross-spawn &gt;=7.0.0 &lt;= 7.0.5 vulnerability by
<a href="https://github.com/alexcouret"><code>@​alexcouret</code></a> in
<a
href="https://redirect.github.com/dawidd6/action-download-artifact/pull/317">dawidd6/action-download-artifact#317</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a
href="https://github.com/alexcouret"><code>@​alexcouret</code></a> made
their first contribution in <a
href="https://redirect.github.com/dawidd6/action-download-artifact/pull/317">dawidd6/action-download-artifact#317</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/dawidd6/action-download-artifact/compare/v6...v7">https://github.com/dawidd6/action-download-artifact/compare/v6...v7</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="80620a5d27"><code>80620a5</code></a>
node_modules: update</li>
<li><a
href="b15e003f46"><code>b15e003</code></a>
node_modules: install</li>
<li><a
href="1ee9a455fd"><code>1ee9a45</code></a>
build(deps): Fix cross-spawn &gt;=7.0.0 &lt;= 7.0.5 vulnerability (<a
href="https://redirect.github.com/dawidd6/action-download-artifact/issues/317">#317</a>)</li>
<li><a
href="b2f2706ac4"><code>b2f2706</code></a>
build(deps): bump <code>@​actions/artifact</code> from 2.1.10 to 2.1.11
(<a
href="https://redirect.github.com/dawidd6/action-download-artifact/issues/313">#313</a>)</li>
<li><a
href="fdbeba027c"><code>fdbeba0</code></a>
build(deps): bump <code>@​actions/core</code> from 1.11.0 to 1.11.1 (<a
href="https://redirect.github.com/dawidd6/action-download-artifact/issues/312">#312</a>)</li>
<li><a
href="a74b42987a"><code>a74b429</code></a>
build(deps): bump <code>@​actions/core</code> from 1.10.1 to 1.11.0 (<a
href="https://redirect.github.com/dawidd6/action-download-artifact/issues/310">#310</a>)</li>
<li><a
href="24e807a70c"><code>24e807a</code></a>
build(deps): bump <code>@​actions/artifact</code> from 2.1.9 to 2.1.10
(<a
href="https://redirect.github.com/dawidd6/action-download-artifact/issues/311">#311</a>)</li>
<li><a
href="9592e3c4ab"><code>9592e3c</code></a>
build(deps): bump path-to-regexp from 6.2.2 to 6.3.0 (<a
href="https://redirect.github.com/dawidd6/action-download-artifact/issues/307">#307</a>)</li>
<li><a
href="5f966b63eb"><code>5f966b6</code></a>
build(deps): bump adm-zip from 0.5.15 to 0.5.16 (<a
href="https://redirect.github.com/dawidd6/action-download-artifact/issues/306">#306</a>)</li>
<li><a
href="db9477a3eb"><code>db9477a</code></a>
build(deps): bump adm-zip from 0.5.14 to 0.5.15 (<a
href="https://redirect.github.com/dawidd6/action-download-artifact/issues/301">#301</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/dawidd6/action-download-artifact/compare/v6...v7">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=dawidd6/action-download-artifact&package-manager=github_actions&previous-version=6&new-version=7)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-09 15:26:49 -08:00
dependabot[bot]
3646c97840 chore(deps): bump time from 0.3.36 to 0.3.37 (#1561)
Bumps [time](https://github.com/time-rs/time) from 0.3.36 to 0.3.37.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/time-rs/time/releases">time's
releases</a>.</em></p>
<blockquote>
<h2>v0.3.37</h2>
<p>See the <a
href="https://github.com/time-rs/time/blob/main/CHANGELOG.md">changelog</a>
for details.</p>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/time-rs/time/blob/main/CHANGELOG.md">time's
changelog</a>.</em></p>
<blockquote>
<h2>0.3.37 [2024-12-03]</h2>
<h3>Added</h3>
<ul>
<li><code>Time::MAX</code>, equivalent to
<code>time!(23:59:59.999999999)</code></li>
<li><code>[year repr:century]</code> is now supported in format
descriptions. When used in conjunction with
<code>[year repr:last_two]</code>, there is sufficient information to
parse a date. Note that with the
<code>large-date</code> feature enabled, there is an ambiguity when
parsing the two back-to-back.</li>
<li>Parsing of <code>strftime</code>-style format descriptions, located
at
<code>time::format_description::parse_strftime_borrowed</code> and
<code>time::format_description::parse_strftime_owned</code></li>
<li><code>time::util::refresh_tz</code> and
<code>time::util::refresh_tz_unchecked</code>, which updates information
obtained via the <code>TZ</code> environment variable. This is
equivalent to the <code>tzset</code> syscall on Unix-like
systems, with and without built-in soundness checks, respectively.</li>
<li><code>Month::length</code> and <code>util::days_in_month</code>,
replacing <code>util::days_in_year_month</code>.</li>
<li>Expressions are permitted in
<code>time::serde::format_description!</code> rather than only paths.
This also
drastically improves diagnostics when an invalid value is provided.</li>
</ul>
<h3>Changed</h3>
<ul>
<li>
<p>Obtaining the system UTC offset on Unix-like systems should now
succeed when multi-threaded.
However, if the <code>TZ</code> environment variable is altered, the
program will not be aware of this until
<code>time::util::refresh_tz</code> or
<code>time::util::refresh_tz_unchecked</code> is called.
<code>refresh_tz</code> has the
same soundness requirements as obtaining the system UTC offset
previously did, with the
requirements still being automatically enforced.
<code>refresh_tz_unchecked</code> does not enforce these
requirements at the expense of being <code>unsafe</code>. Most programs
should not need to call either
function.</p>
<p>Due to this change, the <code>time::util::local_offset</code> module
has been deprecated in its entirety. The
<code>get_soundness</code> and <code>set_soundness</code> functions are
now no-ops.</p>
<p>Note that while calls <em>should</em> succeed, success is not
guaranteed in any situation. Downstream
users should always be prepared to handle the error case.</p>
</li>
</ul>
<h3>Fixed</h3>
<ul>
<li>Floating point values are truncated, not rounded, when
formatting.</li>
<li>RFC3339 allows arbitrary separators between the date and time
components.</li>
<li>Serialization of negative <code>Duration</code>s less than one
second is now correct. It previously omitted
the negative sign.</li>
<li><code>From&lt;js_sys::Date&gt; for OffsetDateTime</code> now ensures
sub-millisecond values are not erroneously
returned.</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="d4e39b306d"><code>d4e39b3</code></a>
v0.3.37 release</li>
<li><a
href="09439970e5"><code>0943997</code></a>
Fix CI failure</li>
<li><a
href="8b50f04ee0"><code>8b50f04</code></a>
Update lints</li>
<li><a
href="56f1db6dfa"><code>56f1db6</code></a>
Add <code>Month::length</code>, <code>days_in_month</code></li>
<li><a
href="03bcfe9f28"><code>03bcfe9</code></a>
Skip formatting some macros, update UI tests</li>
<li><a
href="4404638fe2"><code>4404638</code></a>
Permit exprs in <code>serde::format_description!</code></li>
<li><a
href="6b43b44060"><code>6b43b44</code></a>
strftime implementation</li>
<li><a
href="98569ffe5b"><code>98569ff</code></a>
Hide deprecations from docs</li>
<li><a
href="febf3a10de"><code>febf3a1</code></a>
Obtain local offset in multi-threaded situations</li>
<li><a
href="1e19827c5a"><code>1e19827</code></a>
Update rstest and rstest_reuse; bump MSRV to 1.67.1 (<a
href="https://redirect.github.com/time-rs/time/issues/716">#716</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/time-rs/time/compare/v0.3.36...v0.3.37">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=time&package-manager=cargo&previous-version=0.3.36&new-version=0.3.37)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-09 15:26:18 -08:00
dependabot[bot]
1c3b698b82 chore(deps): bump cargo_metadata from 0.19.0 to 0.19.1 (#1562)
[//]: # (dependabot-start)
⚠️  **Dependabot is rebasing this PR** ⚠️ 

Rebasing might not happen immediately, so don't worry if this takes some
time.

Note: if you make any changes to this PR yourself, they will take
precedence over the rebase.

---

[//]: # (dependabot-end)

Bumps [cargo_metadata](https://github.com/oli-obk/cargo_metadata) from
0.19.0 to 0.19.1.
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/oli-obk/cargo_metadata/blob/main/CHANGELOG.md">cargo_metadata's
changelog</a>.</em></p>
<blockquote>
<h1>Changelog</h1>
<h2>Unreleased</h2>
<ul>
<li>n/a</li>
</ul>
<h3>Added</h3>
<ul>
<li>n/a</li>
</ul>
<h3>Changed</h3>
<ul>
<li>Updated dependencies:
<ul>
<li><code>thiserror</code> from <code>1.0.31</code> to
<code>2.0.3</code></li>
<li><code>derive_builder</code> from <code>0.12</code> to
<code>0.20</code></li>
</ul>
</li>
</ul>
<h3>Removed</h3>
<ul>
<li>n/a</li>
</ul>
<h3>Fixed</h3>
<ul>
<li>n/a</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="8f4c3a8e6d"><code>8f4c3a8</code></a>
Merge pull request <a
href="https://redirect.github.com/oli-obk/cargo_metadata/issues/276">#276</a>
from sunshowers/fromstr-display</li>
<li><a
href="e6c0f06160"><code>e6c0f06</code></a>
Merge pull request <a
href="https://redirect.github.com/oli-obk/cargo_metadata/issues/278">#278</a>
from oli-obk/push-qxuollxmvlzn</li>
<li><a
href="61ceb9aabc"><code>61ceb9a</code></a>
Pacify clippy</li>
<li><a
href="eae17fb2da"><code>eae17fb</code></a>
Bump msrv to keep in sync with our dependencies</li>
<li><a
href="2983f519a2"><code>2983f51</code></a>
add FromStr and Display impls for TargetKind and CrateType</li>
<li><a
href="834207b821"><code>834207b</code></a>
Merge pull request <a
href="https://redirect.github.com/oli-obk/cargo_metadata/issues/273">#273</a>
from regexident/update-dependencies</li>
<li><a
href="437339616a"><code>4373396</code></a>
Update dependencies</li>
<li>See full diff in <a
href="https://github.com/oli-obk/cargo_metadata/compare/0.19.0...0.19.1">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=cargo_metadata&package-manager=cargo&previous-version=0.19.0&new-version=0.19.1)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Josh McKinney <joshka@users.noreply.github.com>
2024-12-09 15:25:59 -08:00
dependabot[bot]
c767f6bc3c chore(deps): bump tokio-stream from 0.1.16 to 0.1.17 (#1563)
Bumps [tokio-stream](https://github.com/tokio-rs/tokio) from 0.1.16 to
0.1.17.
<details>
<summary>Commits</summary>
<ul>
<li><a
href="67355c6d23"><code>67355c6</code></a>
chore: prepare tokio-stream v0.1.17 (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/7020">#7020</a>)</li>
<li><a
href="405d746d38"><code>405d746</code></a>
signal: remove oneshot channels from tests (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/7015">#7015</a>)</li>
<li><a
href="e0d1293fac"><code>e0d1293</code></a>
ci: add instructions that explain how to fix spellcheck errors (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/7016">#7016</a>)</li>
<li><a
href="480c010b01"><code>480c010</code></a>
signal: add <code>SignalKind::info</code> on illumos (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/6995">#6995</a>)</li>
<li><a
href="c032ea0203"><code>c032ea0</code></a>
ci: detect trailing whitespace (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/7013">#7013</a>)</li>
<li><a
href="0b31c2f73d"><code>0b31c2f</code></a>
chore: prepare tokio-util v0.7.13 (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/7012">#7012</a>)</li>
<li><a
href="129f9fc0c8"><code>129f9fc</code></a>
codec: fix incorrect handling of invalid utf-8 in
<code>LinesCodec::decode_eof</code> (#...</li>
<li><a
href="b5c227d51f"><code>b5c227d</code></a>
tracing: move tracing instrumentation tests into tokio tests (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/7007">#7007</a>)</li>
<li><a
href="dcae2b9eb8"><code>dcae2b9</code></a>
ci: unfreeze FreeBSD from rustc 1.81 (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/7009">#7009</a>)</li>
<li><a
href="bb9d57017e"><code>bb9d570</code></a>
chore: prepare Tokio v1.42.0 (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/7005">#7005</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/tokio-rs/tokio/compare/tokio-stream-0.1.16...tokio-stream-0.1.17">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=tokio-stream&package-manager=cargo&previous-version=0.1.16&new-version=0.1.17)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-09 15:25:05 -08:00
dependabot[bot]
08ea837753 chore(deps): bump octocrab from 0.42.0 to 0.42.1 (#1564)
Bumps [octocrab](https://github.com/XAMPPRocky/octocrab) from 0.42.0 to
0.42.1.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/XAMPPRocky/octocrab/releases">octocrab's
releases</a>.</em></p>
<blockquote>
<h2>v0.42.1</h2>
<h3>Other</h3>
<ul>
<li>Secret scanning alert locations API (<a
href="https://redirect.github.com/XAMPPRocky/octocrab/pull/735">#735</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/XAMPPRocky/octocrab/blob/main/CHANGELOG.md">octocrab's
changelog</a>.</em></p>
<blockquote>
<h2><a
href="https://github.com/XAMPPRocky/octocrab/compare/v0.42.0...v0.42.1">0.42.1</a>
- 2024-11-22</h2>
<h3>Other</h3>
<ul>
<li>Secret scanning alert locations API (<a
href="https://redirect.github.com/XAMPPRocky/octocrab/pull/735">#735</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="653814c434"><code>653814c</code></a>
chore: release v0.42.1 (<a
href="https://redirect.github.com/XAMPPRocky/octocrab/issues/736">#736</a>)</li>
<li><a
href="592f165801"><code>592f165</code></a>
Secret scanning alert locations API (<a
href="https://redirect.github.com/XAMPPRocky/octocrab/issues/735">#735</a>)</li>
<li>See full diff in <a
href="https://github.com/XAMPPRocky/octocrab/compare/v0.42.0...v0.42.1">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=octocrab&package-manager=cargo&previous-version=0.42.0&new-version=0.42.1)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-09 15:24:47 -08:00
dependabot[bot]
a8aca0ec12 chore(deps): bump clap from 4.5.21 to 4.5.23 (#1565)
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.21 to 4.5.23.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/clap-rs/clap/releases">clap's
releases</a>.</em></p>
<blockquote>
<h2>v4.5.23</h2>
<h2>[4.5.23] - 2024-12-05</h2>
<h3>Fixes</h3>
<ul>
<li><em>(parser)</em> When check <code>allow_negative_numbers</code>,
allow <code>E</code> again</li>
</ul>
<h2>v4.5.22</h2>
<h2>[4.5.22] - 2024-12-03</h2>
<h3>Fixes</h3>
<ul>
<li><em>(assert)</em> Catch bugs with arguments requiring themself</li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/clap-rs/clap/blob/master/CHANGELOG.md">clap's
changelog</a>.</em></p>
<blockquote>
<h2>[4.5.23] - 2024-12-05</h2>
<h3>Fixes</h3>
<ul>
<li><em>(parser)</em> When check <code>allow_negative_numbers</code>,
allow <code>E</code> again</li>
</ul>
<h2>[4.5.22] - 2024-12-03</h2>
<h3>Fixes</h3>
<ul>
<li><em>(assert)</em> Catch bugs with arguments requiring themself</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="7916028a90"><code>7916028</code></a>
chore: Release</li>
<li><a
href="804498d3b1"><code>804498d</code></a>
docs: Update changelog</li>
<li><a
href="f9721f1435"><code>f9721f1</code></a>
Merge pull request <a
href="https://redirect.github.com/clap-rs/clap/issues/5695">#5695</a>
from epage/value</li>
<li><a
href="b04bf72436"><code>b04bf72</code></a>
fix(complete)!: Be consistent in value language</li>
<li><a
href="0e28259e55"><code>0e28259</code></a>
chore: Release</li>
<li><a
href="e55c986309"><code>e55c986</code></a>
docs: Update changelog</li>
<li><a
href="c7157dfe1d"><code>c7157df</code></a>
Merge pull request <a
href="https://redirect.github.com/clap-rs/clap/issues/5692">#5692</a>
from epage/self</li>
<li><a
href="84252b7123"><code>84252b7</code></a>
fix(complete): Allow completing '.'</li>
<li><a
href="eded64a13b"><code>eded64a</code></a>
test(complete): Verify PathCompleter::dir</li>
<li>See full diff in <a
href="https://github.com/clap-rs/clap/compare/clap_complete-v4.5.21...clap_complete-v4.5.23">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=clap&package-manager=cargo&previous-version=4.5.21&new-version=4.5.23)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-09 15:24:05 -08:00
Orhun Parmaksız
ed5dd73084 docs(widgets): add example for tabs (#1559)
related #1512 

Also removes the tabs example from ratatui crate since it overlaps with
this new example in terms of functionality and it was not following the
general theme of other examples.
2024-12-09 15:22:03 -08:00
Josh McKinney
b5f7e44183 chore(examples): move async example to apps (#1503)
Move async example to examples/apps/async as full project.
Simplify a little by removing the need for the github api token.

Co-authored-by: Orhun Parmaksız <orhunparmaksiz@gmail.com>
2024-12-09 18:30:37 +03:00
Orhun Parmaksız
fab532171d docs(widgets): add example for scrollbar (#1545)
Related to: #1512
2024-12-08 02:28:18 -08:00
Orhun Parmaksız
898aef6e2f docs(widgets): add example for list (#1542)
Related to: #1512
2024-12-08 02:27:19 -08:00
EdJoPaTo
f57b696fdc fix(span): dont render control characters (#1312) 2024-12-08 02:10:58 -08:00
Orhun Parmaksız
452366aa9e docs(widgets): add example for sparkline (#1556)
related #1512

Also removes the sparkline example from ratatui crate since this example
is a simplified and easier to understand version of that
2024-12-07 18:35:45 +03:00
Orhun Parmaksız
ff729b7607 feat(scrollbar): support retrieving the current position of state (#1552)
As of now it is possible to change the position of the Scrollbar but not
possible to retrieve the position for further use. e.g.

```rust
let mut state = ScrollbarState::default();
state.next();
```

This commit adds a new method "`current_position`" (since `position` is
already taken by the fluent setter) for that purpose:

```rust
let index = state.get_position(); // yay
```

See #1545 for the concrete usage of this.
2024-12-07 18:23:22 +03:00
Orhun Parmaksız
6ddde0e8a8 docs(widgets): add example for table (#1557)
related #1512
2024-12-07 18:21:01 +03:00
Orhun Parmaksız
93ad6b828c docs(widgets): update values in chart example (#1558)
better stonks
2024-12-07 18:20:49 +03:00
Josh McKinney
e411d9ec3e docs: add input form example (#1551)
Demonstrates how to manage the cursor and input focus with a simple
form. Uses an enum to track the current input field and passes the
input events to the active field.

This is similar to the json tutorial on the website, but a bit simpler
2024-12-05 14:12:40 -08:00
Josh McKinney
a0979d6871 chore(build): remove cargo lint (#1549)
Duplicate crate lint is too noisy and sensitive to upstream changes
2024-12-05 21:35:06 +03:00
Josh McKinney
ed071f3723 docs: add mouse-drawing example (#1546)
Demonstrates how to handle mouse events
2024-12-04 13:34:12 -08:00
Josh McKinney
9275d3421c feat(layout): add Offset::new() constructor (#1547) 2024-12-04 12:57:38 -08:00
Orhun Parmaksız
15f442a71e docs(widgets): add example for paragraph (#1544)
related #1512 

Also removes the paragraph example from `ratatui` since these examples
are more or less the same.
2024-12-04 21:16:04 +03:00
Martin Sivák
18e70d3d51 fix(crossterm): terminal should keep Bold when removing Dim (#1541)
The Dim removal should behave the same as the logic for Bold removal
that sends NormalIntensity sequence and then restores Dim when needed.

Signed-off-by: Martin Sivak <mars@montik.net>
2024-12-03 13:21:59 -08:00
Orhun Parmaksız
17bba14540 docs(widgets): move the logo example to widgets (#1543)
related #1512 

Also updates the code to make it consistent with the other examples
2024-12-03 22:30:35 +03:00
Orhun Parmaksız
ce4856a65f feat(widgets): add the missing constructor to canvas types (#1538)
Allows constructing `Rectangle`, `Points` and `Circle` using the `new`
method instead of initializing with the public fields directly.
2024-12-03 12:04:53 +03:00
Orhun Parmaksız
f2451e7f1e docs(widgets): add example for gauge (#1539)
related #1512
2024-12-03 12:04:29 +03:00
raylu
7c8573f575 refactor: rearrange selection_spacing code (#1540) 2024-12-03 00:11:47 -08:00
Orhun Parmaksız
4f0a8b21af docs(widgets): add example for canvas (#1533)
related #1512
2024-12-02 17:21:53 +03:00
Orhun Parmaksız
91147c4d75 docs(widgets): add example for chart (#1536)
stonks
2024-12-01 20:34:57 +03:00
Orhun Parmaksız
6dd25a3111 docs(widgets): add example for calendar (#1532)
related #1512
2024-12-01 14:12:06 +03:00
Josh McKinney
35eba76b4d chore(example): move demo2 to top level folder (#1524) 2024-12-01 13:46:26 +03:00
Josh McKinney
357ae7e251 chore: move terminal types to ratatui-core (#1530)
- Move Terminal, TerminalOptions, ViewPort, CompletedFrame, Frame to
  ratatui-core crate
- Move render_widget_ref() and render_stateful_widget_ref() to extension
  trait (FrameExt) due as the Ref types are unstable and kept in the
  main lib instead of -core
- Fix rustdoc errors / feature config issues

BREAKING CHANGE: to call `Frame::render_widget_ref()` or
`Frame::render_stateful_widget_ref()` you now need to import the
FrameExt trait from `ratatui::widgets` and enable the
`unstable-widget-ref` feature.

Co-authored-by: Orhun Parmaksız <orhunparmaksiz@gmail.com>
2024-11-30 17:11:17 -08:00
Raffaele Cataldo
3b13240728 fix(scrollbar): check for area.is_empty() before rendering (#1529)
This adds the `area.is_empty()` back into the scrollbar render method.
Without it, the widget panics if the height is 0.

Co-authored-by: Josh McKinney <joshka@users.noreply.github.com>
2024-11-30 16:36:49 -08:00
Orhun Parmaksız
d201b8e5dd chore(xtask): check lints for only library targets (#1531)
Makes it possible to filter workspace packages by their targets.
(e.g. when we want to retrieve all the binary targets / examples, etc.)
2024-11-30 15:41:27 -08:00
Josh McKinney
21e62d84c2 chore: Move the demo example to main folder (#1523)
Add a top level examples folder for more app-ish examples
Move the demo example into the top level folder.

---------

Co-authored-by: Orhun Parmaksız <orhunparmaksiz@gmail.com>
2024-11-30 03:14:19 -08:00
Josh McKinney
d3f01ebf6e chore(lint): ensure lint config is correct (#1528)
- Move lints to workspace manifest
- Add lint config to backend crates
- Fix one small lint error
2024-11-30 11:13:50 +03:00
Josh McKinney
2892bddce6 fix: rust 1.83 clippy lints (#1527)
https://rust-lang.github.io/rust-clippy/master/index.html#needless_lifetimes
2024-11-29 10:49:12 +03:00
Josh McKinney
fbf6050c86 chore: prepare alpha modularization release (#1525)
This is the first modularization -alpha release. It captures the changes
necessary to manual publish. And ensures all the crates are properly
setup and to set a baseline for comparison in future release checks etc.

This does not update / check the git-cliff setup / changelog

Part of: #1388
2024-11-28 16:47:43 -08:00
Orhun Parmaksız
2b7ec5cb7f chore(widgets): enable calendar widget as default (#1521)
We now expect that you disable the default features if you want less
dependencies
2024-11-27 11:06:09 +03:00
Orhun Parmaksız
d291042e69 docs(block): revise the block example (#1520)
- Moves the block example from `ratatui` to `ratatui-widgets`
- Simplifies the example (bordered, styled, custom borders)

see #1512
2024-11-26 22:38:40 +03:00
dependabot[bot]
5c1c97d5a2 chore(deps): bump cargo_metadata from 0.18.1 to 0.19.0 (#1517)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Josh McKinney <joshka@users.noreply.github.com>
2024-11-25 19:52:03 -08:00
dependabot[bot]
f51d1ccc07 chore(deps): bump clap-verbosity-flag from 2.2.3 to 3.0.0 (#1516)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Josh McKinney <joshka@users.noreply.github.com>
2024-11-25 19:51:09 -08:00
dependabot[bot]
d137456ca1 chore(deps): bump octocrab from 0.42.0 to 0.42.1 (#1515)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Josh McKinney <joshka@users.noreply.github.com>
2024-11-25 19:50:46 -08:00
dependabot[bot]
881fe3eff1 chore(deps): bump rustls from 0.23.15 to 0.23.18 (#1518)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-25 19:23:03 -08:00
Josh McKinney
99ac005b06 docs(widgets): Add simple barchart example (#1511) 2024-11-24 01:32:54 -08:00
Josh McKinney
f132fa1715 refactor(table): small readability improvements (#1510) 2024-11-24 01:32:44 -08:00
Emirhan TALA
369b18eef2 feat(barchart): reduce barchart creation verbosity (#1453)
Adds constructor methods for BarChart, BarGroup, and Bar
2024-11-24 00:06:16 -08:00
Eric Lunderberg
2ce958e38c fix(table): Allow display of additional table row, if row height > 1 (#1452) 2024-11-23 18:40:22 -08:00
Orhun Parmaksız
217c57cd60 refactor: modularize backends (#1508)
Backend code is now moved to `ratatui-crossterm`, `ratatui-termion` and
`ratatui-termwiz`. This should be backwards compatible with existing code.

Co-authored-by: Josh McKinney <joshka@users.noreply.github.com>
2024-11-23 15:23:40 -08:00
Orhun Parmaksız
3ae6bf1d6f docs(contributing): use cargo-xtask for intructions (#1509)
- Updates `CONTRIBUTING.md` about the usage of `xtask`
- Removes `Makefile.toml`
2024-11-23 10:53:40 -08:00
Josh McKinney
ec30390446 fix(canvas): round coordinates to nearest grid cell (#1507)
Previously the canvas coordinates were rounded towards zero, which
causes the rendering to be off by one pixel in some cases. It also meant
that pixels at the extreme edges of the canvas can only be drawn if the
point was exactly on the edge of the canvas. This commit rounds the
coordinates to the nearest integer instead. This may change the output
for some apps using Canvas / Charts.
2024-11-22 14:00:55 -08:00
Emirhan TALA
56d5e05762 feat(bar)!: update label and text_value to accept Into<> (#1471)
BREAKING CHANGE: label and text_value now accept `Into<>` types, which
breaks type inference.

```diff
- Bar::default().label("foo".into());
+ Bar::default().label("foo");
```

```diff
- Bar::default().text_value("bar".into());
+ Bar::default().text_value("bar");
```
2024-11-21 19:12:24 -08:00
Emirhan TALA
b76ad3b02e feat(bar): impl Styled for Bar (#1476)
Related: https://github.com/ratatui/ratatui/issues/683
2024-11-21 17:30:08 -08:00
Ivan Smoliakov
afd1ce179b fix(canvas): Lines that start outside the visible grid are now drawn (#1501)
Previously lines with points that were outside the canvas bounds were
not drawn at all. Now they are clipped to the bounds of the canvas so
that the portion of the line within the canvas is draw.

To facilitate this, a new `Painter::bounds()` method which returns the
bounds of the canvas is added.

Fixes: https://github.com/ratatui/ratatui/issues/1489
2024-11-21 16:16:11 -08:00
Ho Kim
8f282473b2 docs(readme): correct examples links (#1484) 2024-11-20 06:59:01 -08:00
Josh McKinney
36e2d1bda1 fix: add feature(doc_cfg) when generating docs (#1506) 2024-11-20 04:17:13 -08:00
dependabot[bot]
9d5aba69e9 chore(deps): bump serde from 1.0.214 to 1.0.215 (#1495)
Bumps [serde](https://github.com/serde-rs/serde) from 1.0.214 to
1.0.215.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/serde-rs/serde/releases">serde's
releases</a>.</em></p>
<blockquote>
<h2>v1.0.215</h2>
<ul>
<li>Produce warning when multiple fields or variants have the same
deserialization name (<a
href="https://redirect.github.com/serde-rs/serde/issues/2855">#2855</a>,
<a
href="https://redirect.github.com/serde-rs/serde/issues/2856">#2856</a>,
<a
href="https://redirect.github.com/serde-rs/serde/issues/2857">#2857</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="8939af48fe"><code>8939af4</code></a>
Release 1.0.215</li>
<li><a
href="fa5d58cd00"><code>fa5d58c</code></a>
Use ui test syntax that does not interfere with rustfmt</li>
<li><a
href="1a3cf4b3c1"><code>1a3cf4b</code></a>
Update PR 2562 ui tests</li>
<li><a
href="7d96352e96"><code>7d96352</code></a>
Merge pull request <a
href="https://redirect.github.com/serde-rs/serde/issues/2857">#2857</a>
from dtolnay/collide</li>
<li><a
href="111ecc5d8c"><code>111ecc5</code></a>
Update ui tests for warning on colliding aliases</li>
<li><a
href="edd6fe954b"><code>edd6fe9</code></a>
Revert &quot;Add checks for conflicts for aliases&quot;</li>
<li><a
href="a20e9249c5"><code>a20e924</code></a>
Revert &quot;pacify clippy&quot;</li>
<li><a
href="b1353a99cd"><code>b1353a9</code></a>
Merge pull request <a
href="https://redirect.github.com/serde-rs/serde/issues/2856">#2856</a>
from dtolnay/dename</li>
<li><a
href="c59e876bb3"><code>c59e876</code></a>
Produce a separate warning for every colliding name</li>
<li><a
href="7f1e697c0d"><code>7f1e697</code></a>
Merge pull request <a
href="https://redirect.github.com/serde-rs/serde/issues/2855">#2855</a>
from dtolnay/namespan</li>
<li>Additional commits viewable in <a
href="https://github.com/serde-rs/serde/compare/v1.0.214...v1.0.215">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=serde&package-manager=cargo&previous-version=1.0.214&new-version=1.0.215)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-19 20:56:43 -08:00
dependabot[bot]
1b0d6b473b chore(deps): bump clap from 4.5.20 to 4.5.21 (#1496)
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.20 to 4.5.21.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/clap-rs/clap/releases">clap's
releases</a>.</em></p>
<blockquote>
<h2>v4.5.21</h2>
<h2>[4.5.21] - 2024-11-13</h2>
<h3>Fixes</h3>
<ul>
<li><em>(parser)</em> Ensure defaults are filled in on error with
<code>ignore_errors(true)</code></li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/clap-rs/clap/blob/master/CHANGELOG.md">clap's
changelog</a>.</em></p>
<blockquote>
<h2>[4.5.21] - 2024-11-13</h2>
<h3>Fixes</h3>
<ul>
<li><em>(parser)</em> Ensure defaults are filled in on error with
<code>ignore_errors(true)</code></li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="03d722625a"><code>03d7226</code></a>
chore: Release</li>
<li><a
href="3df70fb2b6"><code>3df70fb</code></a>
docs: Update changelog</li>
<li><a
href="3266c36abf"><code>3266c36</code></a>
Merge pull request <a
href="https://redirect.github.com/clap-rs/clap/issues/5691">#5691</a>
from epage/custom</li>
<li><a
href="951762db57"><code>951762d</code></a>
feat(complete): Allow any OsString-compatible type to be a
CompletionCandidate</li>
<li><a
href="bb6493e890"><code>bb6493e</code></a>
feat(complete): Offer - as a path option</li>
<li><a
href="27b348dbcb"><code>27b348d</code></a>
refactor(complete): Simplify ArgValueCandidates code</li>
<li><a
href="49b8108f8c"><code>49b8108</code></a>
feat(complete): Add PathCompleter</li>
<li><a
href="82a360aa54"><code>82a360a</code></a>
feat(complete): Add ArgValueCompleter</li>
<li><a
href="47aedc6906"><code>47aedc6</code></a>
fix(complete): Ensure paths are sorted</li>
<li><a
href="431e2bc931"><code>431e2bc</code></a>
test(complete): Ensure ArgValueCandidates get filtered</li>
<li>Additional commits viewable in <a
href="https://github.com/clap-rs/clap/compare/clap_complete-v4.5.20...clap_complete-v4.5.21">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=clap&package-manager=cargo&previous-version=4.5.20&new-version=4.5.21)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-19 20:56:02 -08:00
dependabot[bot]
c8339494a8 chore(deps): bump clap-verbosity-flag from 2.2.2 to 2.2.3 (#1494)
Bumps
[clap-verbosity-flag](https://github.com/clap-rs/clap-verbosity-flag)
from 2.2.2 to 2.2.3.
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/clap-rs/clap-verbosity-flag/blob/master/CHANGELOG.md">clap-verbosity-flag's
changelog</a>.</em></p>
<blockquote>
<h2>[2.2.3] - 2024-11-16</h2>
<h3>Features</h3>
<ul>
<li>Add <code>DebugLevel</code> and <code>TraceLevel</code> for
exploratory programming</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="99c0859e7b"><code>99c0859</code></a>
chore: Release clap-verbosity-flag version 2.2.3</li>
<li><a
href="9966b2552c"><code>9966b25</code></a>
docs: Update changelog</li>
<li><a
href="cdd29017d5"><code>cdd2901</code></a>
Merge pull request <a
href="https://redirect.github.com/clap-rs/clap-verbosity-flag/issues/123">#123</a>
from joshka/jm/additional-log-levels</li>
<li><a
href="2192330889"><code>2192330</code></a>
docs: Add example for setting default log level</li>
<li><a
href="e628191254"><code>e628191</code></a>
chore: Remove unnecessary clippy allow directives</li>
<li><a
href="6e5fc6a6be"><code>6e5fc6a</code></a>
feat: Add additional log levels</li>
<li><a
href="a7e305e8b0"><code>a7e305e</code></a>
Merge pull request <a
href="https://redirect.github.com/clap-rs/clap-verbosity-flag/issues/120">#120</a>
from clap-rs/renovate/stable-1.x</li>
<li><a
href="3e3368c681"><code>3e3368c</code></a>
chore(deps): Update Rust crate clap to v4.5.20 (<a
href="https://redirect.github.com/clap-rs/clap-verbosity-flag/issues/119">#119</a>)</li>
<li><a
href="f1c663cba5"><code>f1c663c</code></a>
chore(deps): Update dependency STABLE to v1.82.0</li>
<li><a
href="42bed96a45"><code>42bed96</code></a>
Merge pull request <a
href="https://redirect.github.com/clap-rs/clap-verbosity-flag/issues/118">#118</a>
from clap-rs/renovate/stable-1.x</li>
<li>Additional commits viewable in <a
href="https://github.com/clap-rs/clap-verbosity-flag/compare/v2.2.2...v2.2.3">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=clap-verbosity-flag&package-manager=cargo&previous-version=2.2.2&new-version=2.2.3)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-19 20:05:42 -08:00
dependabot[bot]
3ef1face9a chore(deps): bump octocrab from 0.41.2 to 0.42.0 (#1498)
Bumps [octocrab](https://github.com/XAMPPRocky/octocrab) from 0.41.2 to
0.42.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/XAMPPRocky/octocrab/releases">octocrab's
releases</a>.</em></p>
<blockquote>
<h2>v0.42.0</h2>
<h3>Added</h3>
<ul>
<li>added ssh_signing_keys ops (<a
href="https://redirect.github.com/XAMPPRocky/octocrab/pull/725">#725</a>)</li>
</ul>
<h3>Other</h3>
<ul>
<li>Secrets and Code scanning alerts API (<a
href="https://redirect.github.com/XAMPPRocky/octocrab/pull/730">#730</a>)</li>
<li>add support for custom executors (<a
href="https://redirect.github.com/XAMPPRocky/octocrab/pull/728">#728</a>)</li>
<li>Fixup route in api/issues/update (<a
href="https://redirect.github.com/XAMPPRocky/octocrab/pull/732">#732</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/XAMPPRocky/octocrab/blob/main/CHANGELOG.md">octocrab's
changelog</a>.</em></p>
<blockquote>
<h2><a
href="https://github.com/XAMPPRocky/octocrab/compare/v0.41.2...v0.42.0">0.42.0</a>
- 2024-11-13</h2>
<h3>Added</h3>
<ul>
<li>added ssh_signing_keys ops (<a
href="https://redirect.github.com/XAMPPRocky/octocrab/pull/725">#725</a>)</li>
</ul>
<h3>Other</h3>
<ul>
<li>Secrets and Code scanning alerts API (<a
href="https://redirect.github.com/XAMPPRocky/octocrab/pull/730">#730</a>)</li>
<li>add support for custom executors (<a
href="https://redirect.github.com/XAMPPRocky/octocrab/pull/728">#728</a>)</li>
<li>Fixup route in api/issues/update (<a
href="https://redirect.github.com/XAMPPRocky/octocrab/pull/732">#732</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="9c39eaac75"><code>9c39eaa</code></a>
chore: release v0.42.0 (<a
href="https://redirect.github.com/XAMPPRocky/octocrab/issues/733">#733</a>)</li>
<li><a
href="5f6b085a62"><code>5f6b085</code></a>
Secrets and Code scanning alerts API (<a
href="https://redirect.github.com/XAMPPRocky/octocrab/issues/730">#730</a>)</li>
<li><a
href="6f30dc5cf1"><code>6f30dc5</code></a>
imp: add support for custom executors (<a
href="https://redirect.github.com/XAMPPRocky/octocrab/issues/728">#728</a>)</li>
<li><a
href="57681d9940"><code>57681d9</code></a>
Fixup route in api/issues/update (<a
href="https://redirect.github.com/XAMPPRocky/octocrab/issues/732">#732</a>)</li>
<li><a
href="3958f288a4"><code>3958f28</code></a>
feat: added ssh_signing_keys ops (<a
href="https://redirect.github.com/XAMPPRocky/octocrab/issues/725">#725</a>)</li>
<li><a
href="c043d063f0"><code>c043d06</code></a>
chore: release v0.41.2 (<a
href="https://redirect.github.com/XAMPPRocky/octocrab/issues/721">#721</a>)</li>
<li>See full diff in <a
href="https://github.com/XAMPPRocky/octocrab/compare/v0.41.2...v0.42.0">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=octocrab&package-manager=cargo&previous-version=0.41.2&new-version=0.42.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-19 20:00:48 -08:00
dependabot[bot]
f4cbab4101 chore(deps): bump codecov/codecov-action from 4 to 5 (#1499)
Bumps
[codecov/codecov-action](https://github.com/codecov/codecov-action) from
4 to 5.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/codecov/codecov-action/releases">codecov/codecov-action's
releases</a>.</em></p>
<blockquote>
<h2>v5.0.0</h2>
<h2>v5 Release</h2>
<p><code>v5</code> of the Codecov GitHub Action will use the <a
href="https://github.com/codecov/wrapper">Codecov Wrapper</a> to
encapsulate the <a
href="https://github.com/codecov/codecov-cli">CLI</a>. This will help
ensure that the Action gets updates quicker.</p>
<h3>Migration Guide</h3>
<p>The <code>v5</code> release also coincides with the opt-out feature
for tokens for public repositories. In the <code>Global Upload
Token</code> section of the settings page of an organization in
codecov.io, you can set the ability for Codecov to receive a coverage
reports from any source. This will allow contributors or other members
of a repository to upload without needing access to the Codecov token.
For more details see <a
href="https://docs.codecov.com/docs/codecov-tokens#uploading-without-a-token">how
to upload without a token</a>.</p>
<blockquote>
<p>[!WARNING]<br />
<strong>The following arguments have been changed</strong></p>
<ul>
<li><code>file</code> (this has been deprecated in favor of
<code>files</code>)</li>
<li><code>plugin</code> (this has been deprecated in favor of
<code>plugins</code>)</li>
</ul>
</blockquote>
<p>The following arguments have been added:</p>
<ul>
<li><code>binary</code></li>
<li><code>gcov_args</code></li>
<li><code>gcov_executable</code></li>
<li><code>gcov_ignore</code></li>
<li><code>gcov_include</code></li>
<li><code>report_type</code></li>
<li><code>skip_validation</code></li>
<li><code>swift_project</code></li>
</ul>
<p>You can see their usage in the <code>action.yml</code> <a
href="https://github.com/codecov/codecov-action/blob/main/action.yml">file</a>.</p>
<h2>What's Changed</h2>
<ul>
<li>chore(deps): bump to eslint9+ and remove eslint-config-google by <a
href="https://github.com/thomasrockhu-codecov"><code>@​thomasrockhu-codecov</code></a>
in <a
href="https://redirect.github.com/codecov/codecov-action/pull/1591">codecov/codecov-action#1591</a></li>
<li>build(deps-dev): bump <code>@​octokit/webhooks-types</code> from
7.5.1 to 7.6.1 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/codecov/codecov-action/pull/1595">codecov/codecov-action#1595</a></li>
<li>build(deps-dev): bump typescript from 5.6.2 to 5.6.3 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/codecov/codecov-action/pull/1604">codecov/codecov-action#1604</a></li>
<li>build(deps-dev): bump <code>@​typescript-eslint/parser</code> from
8.8.0 to 8.8.1 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/codecov/codecov-action/pull/1601">codecov/codecov-action#1601</a></li>
<li>build(deps): bump <code>@​actions/core</code> from 1.11.0 to 1.11.1
by <a href="https://github.com/dependabot"><code>@​dependabot</code></a>
in <a
href="https://redirect.github.com/codecov/codecov-action/pull/1597">codecov/codecov-action#1597</a></li>
<li>build(deps): bump github/codeql-action from 3.26.9 to 3.26.11 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/codecov/codecov-action/pull/1596">codecov/codecov-action#1596</a></li>
<li>build(deps-dev): bump <code>@​typescript-eslint/eslint-plugin</code>
from 8.8.0 to 8.8.1 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/codecov/codecov-action/pull/1600">codecov/codecov-action#1600</a></li>
<li>build(deps-dev): bump eslint from 9.11.1 to 9.12.0 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/codecov/codecov-action/pull/1598">codecov/codecov-action#1598</a></li>
<li>build(deps): bump github/codeql-action from 3.26.11 to 3.26.12 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/codecov/codecov-action/pull/1609">codecov/codecov-action#1609</a></li>
<li>build(deps): bump actions/checkout from 4.2.0 to 4.2.1 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/codecov/codecov-action/pull/1608">codecov/codecov-action#1608</a></li>
<li>build(deps): bump actions/upload-artifact from 4.4.0 to 4.4.3 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/codecov/codecov-action/pull/1607">codecov/codecov-action#1607</a></li>
<li>build(deps-dev): bump <code>@​typescript-eslint/parser</code> from
8.8.1 to 8.9.0 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/codecov/codecov-action/pull/1612">codecov/codecov-action#1612</a></li>
<li>build(deps-dev): bump <code>@​typescript-eslint/eslint-plugin</code>
from 8.8.1 to 8.9.0 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/codecov/codecov-action/pull/1611">codecov/codecov-action#1611</a></li>
<li>build(deps-dev): bump <code>@​typescript-eslint/eslint-plugin</code>
from 8.9.0 to 8.10.0 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/codecov/codecov-action/pull/1615">codecov/codecov-action#1615</a></li>
<li>build(deps-dev): bump eslint from 9.12.0 to 9.13.0 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/codecov/codecov-action/pull/1618">codecov/codecov-action#1618</a></li>
<li>build(deps): bump github/codeql-action from 3.26.12 to 3.26.13 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/codecov/codecov-action/pull/1617">codecov/codecov-action#1617</a></li>
<li>build(deps-dev): bump <code>@​typescript-eslint/parser</code> from
8.9.0 to 8.10.0 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/codecov/codecov-action/pull/1614">codecov/codecov-action#1614</a></li>
<li>build(deps-dev): bump <code>@​typescript-eslint/eslint-plugin</code>
from 8.10.0 to 8.11.0 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/codecov/codecov-action/pull/1620">codecov/codecov-action#1620</a></li>
<li>build(deps-dev): bump <code>@​typescript-eslint/parser</code> from
8.10.0 to 8.11.0 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/codecov/codecov-action/pull/1619">codecov/codecov-action#1619</a></li>
<li>build(deps-dev): bump <code>@​types/jest</code> from 29.5.13 to
29.5.14 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/codecov/codecov-action/pull/1622">codecov/codecov-action#1622</a></li>
<li>build(deps): bump actions/checkout from 4.2.1 to 4.2.2 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/codecov/codecov-action/pull/1625">codecov/codecov-action#1625</a></li>
<li>build(deps): bump github/codeql-action from 3.26.13 to 3.27.0 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/codecov/codecov-action/pull/1624">codecov/codecov-action#1624</a></li>
<li>build(deps-dev): bump <code>@​typescript-eslint/eslint-plugin</code>
from 8.11.0 to 8.12.1 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/codecov/codecov-action/pull/1626">codecov/codecov-action#1626</a></li>
<li>build(deps-dev): bump <code>@​typescript-eslint/eslint-plugin</code>
from 8.12.1 to 8.12.2 by <a
href="https://github.com/dependabot"><code>@​dependabot</code></a> in <a
href="https://redirect.github.com/codecov/codecov-action/pull/1629">codecov/codecov-action#1629</a></li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md">codecov/codecov-action's
changelog</a>.</em></p>
<blockquote>
<h2>4.0.0-beta.2</h2>
<h3>Fixes</h3>
<ul>
<li><a
href="https://redirect.github.com/codecov/codecov-action/issues/1085">#1085</a>
not adding -n if empty to do-upload command</li>
</ul>
<h2>4.0.0-beta.1</h2>
<p><code>v4</code> represents a move from the <a
href="https://github.com/codecov/uploader">universal uploader</a> to the
<a href="https://github.com/codecov/codecov-cli">Codecov CLI</a>.
Although this will unlock new features for our users, the CLI is not yet
at feature parity with the universal uploader.</p>
<h3>Breaking Changes</h3>
<ul>
<li>No current support for <code>aarch64</code> and <code>alpine</code>
architectures.</li>
<li>Tokenless uploading is unsuported</li>
<li>Various arguments to the Action have been removed</li>
</ul>
<h2>3.1.4</h2>
<h3>Fixes</h3>
<ul>
<li><a
href="https://redirect.github.com/codecov/codecov-action/issues/967">#967</a>
Fix typo in README.md</li>
<li><a
href="https://redirect.github.com/codecov/codecov-action/issues/971">#971</a>
fix: add back in working dir</li>
<li><a
href="https://redirect.github.com/codecov/codecov-action/issues/969">#969</a>
fix: CLI option names for uploader</li>
</ul>
<h3>Dependencies</h3>
<ul>
<li><a
href="https://redirect.github.com/codecov/codecov-action/issues/970">#970</a>
build(deps-dev): bump <code>@​types/node</code> from 18.15.12 to
18.16.3</li>
<li><a
href="https://redirect.github.com/codecov/codecov-action/issues/979">#979</a>
build(deps-dev): bump <code>@​types/node</code> from 20.1.0 to
20.1.2</li>
<li><a
href="https://redirect.github.com/codecov/codecov-action/issues/981">#981</a>
build(deps-dev): bump <code>@​types/node</code> from 20.1.2 to
20.1.4</li>
</ul>
<h2>3.1.3</h2>
<h3>Fixes</h3>
<ul>
<li><a
href="https://redirect.github.com/codecov/codecov-action/issues/960">#960</a>
fix: allow for aarch64 build</li>
</ul>
<h3>Dependencies</h3>
<ul>
<li><a
href="https://redirect.github.com/codecov/codecov-action/issues/957">#957</a>
build(deps-dev): bump jest-junit from 15.0.0 to 16.0.0</li>
<li><a
href="https://redirect.github.com/codecov/codecov-action/issues/958">#958</a>
build(deps): bump openpgp from 5.7.0 to 5.8.0</li>
<li><a
href="https://redirect.github.com/codecov/codecov-action/issues/959">#959</a>
build(deps-dev): bump <code>@​types/node</code> from 18.15.10 to
18.15.12</li>
</ul>
<h2>3.1.2</h2>
<h3>Fixes</h3>
<ul>
<li><a
href="https://redirect.github.com/codecov/codecov-action/issues/718">#718</a>
Update README.md</li>
<li><a
href="https://redirect.github.com/codecov/codecov-action/issues/851">#851</a>
Remove unsupported path_to_write_report argument</li>
<li><a
href="https://redirect.github.com/codecov/codecov-action/issues/898">#898</a>
codeql-analysis.yml</li>
<li><a
href="https://redirect.github.com/codecov/codecov-action/issues/901">#901</a>
Update README to contain correct information - inputs and negate
feature</li>
<li><a
href="https://redirect.github.com/codecov/codecov-action/issues/955">#955</a>
fix: add in all the extra arguments for uploader</li>
</ul>
<h3>Dependencies</h3>
<ul>
<li><a
href="https://redirect.github.com/codecov/codecov-action/issues/819">#819</a>
build(deps): bump openpgp from 5.4.0 to 5.5.0</li>
<li><a
href="https://redirect.github.com/codecov/codecov-action/issues/835">#835</a>
build(deps): bump node-fetch from 3.2.4 to 3.2.10</li>
<li><a
href="https://redirect.github.com/codecov/codecov-action/issues/840">#840</a>
build(deps): bump ossf/scorecard-action from 1.1.1 to 2.0.4</li>
<li><a
href="https://redirect.github.com/codecov/codecov-action/issues/841">#841</a>
build(deps): bump <code>@​actions/core</code> from 1.9.1 to 1.10.0</li>
<li><a
href="https://redirect.github.com/codecov/codecov-action/issues/843">#843</a>
build(deps): bump <code>@​actions/github</code> from 5.0.3 to 5.1.1</li>
<li><a
href="https://redirect.github.com/codecov/codecov-action/issues/869">#869</a>
build(deps): bump node-fetch from 3.2.10 to 3.3.0</li>
<li><a
href="https://redirect.github.com/codecov/codecov-action/issues/872">#872</a>
build(deps-dev): bump jest-junit from 13.2.0 to 15.0.0</li>
<li><a
href="https://redirect.github.com/codecov/codecov-action/issues/879">#879</a>
build(deps): bump decode-uri-component from 0.2.0 to 0.2.2</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="5c47607acb"><code>5c47607</code></a>
fix: override commit and pr values for PR cases (<a
href="https://redirect.github.com/codecov/codecov-action/issues/1657">#1657</a>)</li>
<li><a
href="3b1354a6c4"><code>3b1354a</code></a>
chore(release): 5.0.1 (<a
href="https://redirect.github.com/codecov/codecov-action/issues/1656">#1656</a>)</li>
<li><a
href="2e2a9c6d58"><code>2e2a9c6</code></a>
fix: update tokenless branch logic (<a
href="https://redirect.github.com/codecov/codecov-action/issues/1650">#1650</a>)</li>
<li><a
href="cfc521b7a1"><code>cfc521b</code></a>
Update README.md</li>
<li><a
href="06425412c8"><code>0642541</code></a>
fix: use marketplace v5 badge (<a
href="https://redirect.github.com/codecov/codecov-action/issues/1646">#1646</a>)</li>
<li><a
href="968872560f"><code>9688725</code></a>
Update README.md</li>
<li><a
href="2112eaec1b"><code>2112eae</code></a>
chore(deps): bump wrapper to 0.0.23 (<a
href="https://redirect.github.com/codecov/codecov-action/issues/1644">#1644</a>)</li>
<li><a
href="193421c5b3"><code>193421c</code></a>
fixL use the correct source (<a
href="https://redirect.github.com/codecov/codecov-action/issues/1642">#1642</a>)</li>
<li><a
href="6018df70b0"><code>6018df7</code></a>
fix: update container builds (<a
href="https://redirect.github.com/codecov/codecov-action/issues/1640">#1640</a>)</li>
<li><a
href="eff1a643d6"><code>eff1a64</code></a>
fix: add missing vars (<a
href="https://redirect.github.com/codecov/codecov-action/issues/1638">#1638</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/codecov/codecov-action/compare/v4...v5">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=codecov/codecov-action&package-manager=github_actions&previous-version=4&new-version=5)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-19 19:52:52 -08:00
dependabot[bot]
ae6a8501ee chore(deps): bump DavidAnson/markdownlint-cli2-action from 17 to 18 (#1500)
Bumps
[DavidAnson/markdownlint-cli2-action](https://github.com/davidanson/markdownlint-cli2-action)
from 17 to 18.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/davidanson/markdownlint-cli2-action/releases">DavidAnson/markdownlint-cli2-action's
releases</a>.</em></p>
<blockquote>
<h2>Update markdownlint version (markdownlint-cli2 v0.15.0, markdownlint
v0.36.1).</h2>
<p>No release notes provided.</p>
<h2>Update markdownlint version (markdownlint-cli2 v0.14.0, markdownlint
v0.35.0).</h2>
<p>No release notes provided.</p>
<h2>Update markdownlint version (markdownlint-cli2 v0.13.0, markdownlint
v0.34.0).</h2>
<p>No release notes provided.</p>
<p>Update markdownlint version (markdownlint-cli2 v0.12.1, markdownlint
v0.33.0).</p>
<h2>Update markdownlint version (markdownlint-cli2 v0.11.0, markdownlint
v0.32.1), remove deprecated &quot;command&quot; input.</h2>
<p>No release notes provided.</p>
<h2>Update markdownlint version (markdownlint-cli2 v0.10.0, markdownlint
v0.31.1).</h2>
<p>No release notes provided.</p>
<h2>Update markdownlint version (markdownlint-cli2 v0.9.2, markdownlint
v0.30.0).</h2>
<p>No release notes provided.</p>
<h2>Update markdownlint version (markdownlint-cli2 v0.8.1, markdownlint
v0.29.0), add &quot;config&quot; and &quot;fix&quot; inputs, deprecate
&quot;command&quot; input.</h2>
<p>No release notes provided.</p>
<h2>Update markdownlint version (markdownlint-cli2 v0.7.1, markdownlint
v0.28.2).</h2>
<p>No release notes provided.</p>
<h2>Update markdownlint version (markdownlint-cli2 v0.7.0, markdownlint
v0.28.1), include link to rule information in title of annotations
(clickable in GitHub).</h2>
<p>No release notes provided.</p>
<h2>Update markdownlint version (markdownlint-cli2 v0.6.0, markdownlint
v0.27.0).</h2>
<p>No release notes provided.</p>
<h2>Update markdownlint version (markdownlint-cli2 v0.5.1, markdownlint
v0.26.2).</h2>
<p>No release notes provided.</p>
<h2>Update markdownlint version (markdownlint-cli2 v0.4.0, markdownlint
v0.25.1)</h2>
<p>No release notes provided.</p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="eb5ca3ab41"><code>eb5ca3a</code></a>
Update to version 18.0.0.</li>
<li><a
href="bd47e95879"><code>bd47e95</code></a>
Freshen generated index.js file.</li>
<li><a
href="ad0fecff0d"><code>ad0fecf</code></a>
Bump markdownlint-cli2 from 0.14.0 to 0.15.0</li>
<li><a
href="079995c6c3"><code>079995c</code></a>
Bump eslint-plugin-n from 17.13.0 to 17.13.1</li>
<li><a
href="4163a2f0f6"><code>4163a2f</code></a>
Bump eslint-plugin-n from 17.12.0 to 17.13.0</li>
<li><a
href="8b67109ec6"><code>8b67109</code></a>
Bump eslint from 9.13.0 to 9.14.0</li>
<li><a
href="f6d787a2fc"><code>f6d787a</code></a>
Bump <code>@​eslint/js</code> from 9.13.0 to 9.14.0</li>
<li><a
href="c05e13d409"><code>c05e13d</code></a>
Bump <code>@​stylistic/eslint-plugin</code> from 2.10.0 to 2.10.1</li>
<li><a
href="6d12e16c76"><code>6d12e16</code></a>
Bump eslint-plugin-n from 17.11.1 to 17.12.0</li>
<li><a
href="0f558ed3a6"><code>0f558ed</code></a>
Bump <code>@​stylistic/eslint-plugin</code> from 2.9.0 to 2.10.0</li>
<li>Additional commits viewable in <a
href="https://github.com/davidanson/markdownlint-cli2-action/compare/v17...v18">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=DavidAnson/markdownlint-cli2-action&package-manager=github_actions&previous-version=17&new-version=18)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-19 19:52:23 -08:00
dependabot[bot]
1bb41e7165 chore(deps): bump instability from 0.3.2 to 0.3.3 (#1497)
Bumps [instability](https://github.com/ratatui-org/instability) from
0.3.2 to 0.3.3.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/ratatui-org/instability/releases">instability's
releases</a>.</em></p>
<blockquote>
<h2>instability-v0.3.3</h2>
<h3>Added</h3>
<ul>
<li>add stable macro (<a
href="https://redirect.github.com/ratatui/instability/pull/14">#14</a>)</li>
<li>use doc(cfg)</li>
</ul>
<h3>Fixed</h3>
<ul>
<li>tests (<a
href="https://redirect.github.com/ratatui/instability/pull/13">#13</a>)</li>
<li>change master to main in lib.rs</li>
</ul>
<h3>Other</h3>
<ul>
<li>bump msrv to 1.63</li>
<li>use proc_macro2 and add tests</li>
<li>use darling instead of manual parsing for better error messages on
attributes</li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/ratatui/instability/blob/main/CHANGELOG.md">instability's
changelog</a>.</em></p>
<blockquote>
<h2><a
href="https://github.com/ratatui/instability/compare/instability-v0.3.2...instability-v0.3.3">0.3.3</a>
- 2024-11-12</h2>
<h3>Added</h3>
<ul>
<li>add stable macro (<a
href="https://redirect.github.com/ratatui/instability/pull/14">#14</a>)</li>
<li>use doc(cfg)</li>
</ul>
<h3>Fixed</h3>
<ul>
<li>tests (<a
href="https://redirect.github.com/ratatui/instability/pull/13">#13</a>)</li>
<li>change master to main in lib.rs</li>
</ul>
<h3>Other</h3>
<ul>
<li>bump msrv to 1.63</li>
<li>use proc_macro2 and add tests</li>
<li>use darling instead of manual parsing for better error messages on
attributes</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="e365305716"><code>e365305</code></a>
chore: release v0.3.3 (<a
href="https://redirect.github.com/ratatui-org/instability/issues/9">#9</a>)</li>
<li><a
href="14f2993a8f"><code>14f2993</code></a>
feat: add stable macro (<a
href="https://redirect.github.com/ratatui-org/instability/issues/14">#14</a>)</li>
<li><a
href="f833f10e01"><code>f833f10</code></a>
fix: tests (<a
href="https://redirect.github.com/ratatui-org/instability/issues/13">#13</a>)</li>
<li><a
href="261fdfd252"><code>261fdfd</code></a>
build: bump msrv to 1.63</li>
<li><a
href="402d37064c"><code>402d370</code></a>
feat: use doc(cfg)</li>
<li><a
href="731685a257"><code>731685a</code></a>
refactor: use proc_macro2 and add tests</li>
<li><a
href="82d15c721c"><code>82d15c7</code></a>
chore: use darling instead of manual parsing for better error messages
on att...</li>
<li><a
href="47e02c2009"><code>47e02c2</code></a>
fix: change master to main in lib.rs</li>
<li>See full diff in <a
href="https://github.com/ratatui-org/instability/compare/instability-v0.3.2...instability-v0.3.3">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=instability&package-manager=cargo&previous-version=0.3.2&new-version=0.3.3)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-19 19:31:38 -08:00
thscharler
4d7704fba5 fix: Make StatefulWidget and Ref work with unsized State (#1505)
StatefulWidget::State and StatefulWidgetRef::State are now ?Sized.

This allows implementations of the traits to use unsized types for the
State associated type. This is turn is useful when doing things like
boxing different stateful widget types with State which implements
`Any`, are slices or any other dynamically sized type.

Co-authored-by: Josh McKinney <joshka@users.noreply.github.com>
2024-11-19 17:56:26 -08:00
Josh McKinney
e4e95bcecf chore: remove --color always flags from bacon.toml (#1502)
No longer necessary as of bacon 3.3
2024-11-19 21:21:12 +03:00
Josh McKinney
a41c97b413 chore: move unstable widget refs to ratatui (#1491)
These are less stable than the non-ref traits as we have not yet
committed to the exact API. This change moves them to ratatui from
ratatui-core.

To facilitate this:
- implementations of WidgetRef for all internal widgets are removed and
  replaced with implementations of Widget for references to those
  widgets.
- Widget is now implemented for Option<W> where W: Widget, allowing for
  rendering of optional widgets.
- The blanket implementation of Widget for WidgetRef is reversed, to be
  a blanket implementation of WidgetRef for all &W where W: Widget.

BREAKING CHANGE: implementations of WidgetRef no longer have a blanket
implementation of Widget, so Widgets should generally implement the
Widget trait on a reference to the widget rather than implementing
WidgetRef directly. This has the advantage of not requiring unstable
features to be enabled.

Part of: https://github.com/ratatui/ratatui/issues/1388

Co-authored-by: Orhun Parmaksız <orhunparmaksiz@gmail.com>
2024-11-18 14:19:21 -08:00
Orhun Parmaksız
46902f5587 docs: improve docs for workspace crates (#1490)
Overall makes improvements in the documentation of the workspace crates and checking them.
2024-11-18 02:03:44 +03:00
Josh McKinney
e7085e3a3e chore: move widgets into ratatui-widgets crate (#1474)
All the widgets now live in their own ratatui-widgets crate, but are re-exported in the main ratatui crate.
This makes it easier to use portions of the ratatui library and is part of the effort to modularize

Part of: #1388

---------

Co-authored-by: Orhun Parmaksız <orhun@archlinux.org>
Co-authored-by: Orhun Parmaksız <orhunparmaksiz@gmail.com>
2024-11-15 19:42:07 -08:00
Nils Martel
9f90f7495f docs(readme): fix broken link (#1485) 2024-11-13 12:52:40 +03:00
Dheepak Krishnamurthy
260af68a34 docs(readme): include iocraft as an alternative (#1483) 2024-11-12 18:41:43 +03:00
Josh McKinney
e461b724a6 refactor: move {Stateful,}Widget{,Ref} types into individual files (#1479)
This is a preparatory refactoring for modularization. No user visible
changes.
2024-11-12 15:34:48 +03:00
dependabot[bot]
02c8c9373e chore(deps): bump unicode-truncate from 1.1.0 to 2.0.0 (#1481)
Bumps [unicode-truncate](https://github.com/Aetf/unicode-truncate) from
1.1.0 to 2.0.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/Aetf/unicode-truncate/releases">unicode-truncate's
releases</a>.</em></p>
<blockquote>
<h2>v2.0.0</h2>
<h3>Fixed</h3>
<ul>
<li><em>(deps)</em> update rust crate unicode-width to 0.2</li>
</ul>
<h3>Other</h3>
<ul>
<li>make release-plz use github app token</li>
<li>[<strong>breaking</strong>] bump MSRV to 1.66</li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/Aetf/unicode-truncate/blob/master/CHANGELOG.md">unicode-truncate's
changelog</a>.</em></p>
<blockquote>
<h2><a
href="https://github.com/Aetf/unicode-truncate/compare/v1.1.0...v2.0.0">2.0.0</a>
- 2024-11-10</h2>
<h3>Fixed</h3>
<ul>
<li><em>(deps)</em> update rust crate unicode-width to 0.2</li>
</ul>
<h3>Other</h3>
<ul>
<li>make release-plz use github app token</li>
<li>[<strong>breaking</strong>] bump MSRV to 1.66</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="95b4d26103"><code>95b4d26</code></a>
chore: release v2.0.0</li>
<li><a
href="4c0b472226"><code>4c0b472</code></a>
ci: make release-plz use github app token</li>
<li><a
href="2812162513"><code>2812162</code></a>
build!: bump MSRV to 1.66</li>
<li><a
href="8984d1529a"><code>8984d15</code></a>
fix(deps): update rust crate unicode-width to 0.2</li>
<li><a
href="abd6d916b9"><code>abd6d91</code></a>
fix(deps): update rust crate unicode-segmentation to v1.12.0</li>
<li>See full diff in <a
href="https://github.com/Aetf/unicode-truncate/compare/v1.1.0...v2.0.0">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=unicode-truncate&package-manager=cargo&previous-version=1.1.0&new-version=2.0.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-12 03:42:47 -08:00
dependabot[bot]
f40fa787d1 chore(deps): bump tokio from 1.40.0 to 1.41.1 (#1482)
Bumps [tokio](https://github.com/tokio-rs/tokio) from 1.40.0 to 1.41.1.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/tokio-rs/tokio/releases">tokio's
releases</a>.</em></p>
<blockquote>
<h2>Tokio v1.41.1</h2>
<h1>1.41.1 (Nov 7th, 2024)</h1>
<h3>Fixed</h3>
<ul>
<li>metrics: fix bug with wrong number of buckets for the histogram (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/6957">#6957</a>)</li>
<li>net: display <code>net</code> requirement for
<code>net::UdpSocket</code> in docs (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/6938">#6938</a>)</li>
<li>net: fix typo in <code>TcpStream</code> internal comment (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/6944">#6944</a>)</li>
</ul>
<p><a
href="https://redirect.github.com/tokio-rs/tokio/issues/6957">#6957</a>:
<a
href="https://redirect.github.com/tokio-rs/tokio/pull/6957">tokio-rs/tokio#6957</a>
<a
href="https://redirect.github.com/tokio-rs/tokio/issues/6938">#6938</a>:
<a
href="https://redirect.github.com/tokio-rs/tokio/pull/6938">tokio-rs/tokio#6938</a>
<a
href="https://redirect.github.com/tokio-rs/tokio/issues/6944">#6944</a>:
<a
href="https://redirect.github.com/tokio-rs/tokio/pull/6944">tokio-rs/tokio#6944</a></p>
<h2>Tokio v1.41.0</h2>
<h1>1.41.0 (Oct 22th, 2024)</h1>
<h3>Added</h3>
<ul>
<li>metrics: stabilize <code>global_queue_depth</code> (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/6854">#6854</a>,
<a
href="https://redirect.github.com/tokio-rs/tokio/issues/6918">#6918</a>)</li>
<li>net: add conversions for unix <code>SocketAddr</code> (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/6868">#6868</a>)</li>
<li>sync: add <code>watch::Sender::sender_count</code> (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/6836">#6836</a>)</li>
<li>sync: add <code>mpsc::Receiver::blocking_recv_many</code> (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/6867">#6867</a>)</li>
<li>task: stabilize <code>Id</code> apis (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/6793">#6793</a>,
<a
href="https://redirect.github.com/tokio-rs/tokio/issues/6891">#6891</a>)</li>
</ul>
<h3>Added (unstable)</h3>
<ul>
<li>metrics: add H2 Histogram option to improve histogram granularity
(<a
href="https://redirect.github.com/tokio-rs/tokio/issues/6897">#6897</a>)</li>
<li>metrics: rename some histogram apis (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/6924">#6924</a>)</li>
<li>runtime: add <code>LocalRuntime</code> (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/6808">#6808</a>)</li>
</ul>
<h3>Changed</h3>
<ul>
<li>runtime: box futures larger than 16k on release mode (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/6826">#6826</a>)</li>
<li>sync: add <code>#[must_use]</code> to <code>Notified</code> (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/6828">#6828</a>)</li>
<li>sync: make <code>watch</code> cooperative (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/6846">#6846</a>)</li>
<li>sync: make <code>broadcast::Receiver</code> cooperative (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/6870">#6870</a>)</li>
<li>task: add task size to tracing instrumentation (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/6881">#6881</a>)</li>
<li>wasm: enable <code>cfg_fs</code> for <code>wasi</code> target (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/6822">#6822</a>)</li>
</ul>
<h3>Fixed</h3>
<ul>
<li>net: fix regression of abstract socket path in unix socket (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/6838">#6838</a>)</li>
</ul>
<h3>Documented</h3>
<ul>
<li>io: recommend <code>OwnedFd</code> with <code>AsyncFd</code> (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/6821">#6821</a>)</li>
<li>io: document cancel safety of <code>AsyncFd</code> methods (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/6890">#6890</a>)</li>
<li>macros: render more comprehensible documentation for
<code>join</code> and <code>try_join</code> (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/6814">#6814</a>,
<a
href="https://redirect.github.com/tokio-rs/tokio/issues/6841">#6841</a>)</li>
<li>net: fix swapped examples for <code>TcpSocket::set_nodelay</code>
and <code>TcpSocket::nodelay</code> (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/6840">#6840</a>)</li>
<li>sync: document runtime compatibility (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/6833">#6833</a>)</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="bb7ca7507b"><code>bb7ca75</code></a>
chore: prepare Tokio v1.41.1 (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/6959">#6959</a>)</li>
<li><a
href="4a34b77af5"><code>4a34b77</code></a>
metrics: fix bug with wrong number of buckets for the histogram (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/6957">#6957</a>)</li>
<li><a
href="8897885425"><code>8897885</code></a>
docs: fix mismatched backticks in CONTRIBUTING.md (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/6951">#6951</a>)</li>
<li><a
href="0dbdd196b6"><code>0dbdd19</code></a>
ci: update cargo-check-external-types to 0.1.13 (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/6949">#6949</a>)</li>
<li><a
href="94e55c092b"><code>94e55c0</code></a>
net: fix typo in <code>TcpStream</code> internal comment (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/6944">#6944</a>)</li>
<li><a
href="4468f27c31"><code>4468f27</code></a>
metrics: fixed flaky <code>worker_steal_count</code> test (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/6932">#6932</a>)</li>
<li><a
href="070a825999"><code>070a825</code></a>
metrics: removed race condition from global_queue_depth_multi_thread
test (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/6">#6</a>...</li>
<li><a
href="946401c345"><code>946401c</code></a>
net: display <code>net</code> requirement for
<code>net::UdpSocket</code> in docs (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/6938">#6938</a>)</li>
<li><a
href="0c01fd23b4"><code>0c01fd2</code></a>
ci: use patched version of cargo-check-external-types to fix CI failure
(<a
href="https://redirect.github.com/tokio-rs/tokio/issues/6937">#6937</a>)</li>
<li><a
href="ebe241647e"><code>ebe2416</code></a>
ci: use cargo deny (<a
href="https://redirect.github.com/tokio-rs/tokio/issues/6931">#6931</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/tokio-rs/tokio/compare/tokio-1.40.0...tokio-1.41.1">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=tokio&package-manager=cargo&previous-version=1.40.0&new-version=1.41.1)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-12 03:42:08 -08:00
Marco Ieni
7b875091e1 fix: typo (#1480) 2024-11-09 19:10:31 -08:00
Orhun Parmaksız
17316ec5d0 chore(github): enable sponsorship button (#1478) 2024-11-08 16:34:03 +03:00
Orhun Parmaksız
eaa403856e chore(ci): install pre-built binaries for cargo-rdme (#1477)
`install-action` uses `cargo-binstall` to install the pre-built binaries
of `cargo-rdme` (which was released in
https://github.com/orium/cargo-rdme/releases/tag/v1.4.7).

This will make the `check-readme` step faster in CI (now takes only 10
seconds).
2024-11-08 16:33:48 +03:00
Orhun Parmaksız
e5e2316451 chore(ci): add check for keeping README.md up-to-date (#1473) 2024-11-07 09:45:14 +03:00
Josh McKinney
98df774d7f chore(core): move core types to ratatui-core (#1460)
The buffer, layout, style, symbols, text, and the top level of widgets
modules are moved to ratatui-core. This is the first step in
modularizing the library so that the core types can be used in other
projects without the need for the backend / widgets types.

This helps reduce the need for updating other crates as often due to
semver changes outside of the core types.

---------

Co-authored-by: Orhun Parmaksız <orhun@archlinux.org>
Co-authored-by: Orhun Parmaksız <orhunparmaksiz@gmail.com>
2024-11-06 09:10:49 +03:00
Everett Pompeii
0a47ebd94b chore(bencher): update bencher CLI usage (#1470) 2024-11-06 00:37:23 +03:00
Josh McKinney
abe2f27328 chore(backend): change From<T> impls to new backend specific IntoBackend and FromBackend traits (#1464)
Adds two traits `IntoCrossterm` and `FromCrossterm` for converting
between ratatui and crossterm types. This is necessary in order to avoid
the orphan rule when implementing `From` for crossterm types once the
crossterm types are moved to a separate crate.

Similarly Termwiz and Termwiz gain FromTermion, IntoTermion, FromTermwiz
and IntoTermwiz traits.

BREAKING CHANGE: The `From` and `Into` impls for backend types are now
replaced
with specific backend traits.

```diff
+ use ratatui::backend::{FromCrossterm, IntoCrossterm};

let crossterm_color = crossterm::style::Color::Black;
- let ratatui_color = crossterm_color.into();
- let ratatui_color = ratatui::style::Color::from(crossterm_color);
+ let ratatui_color = ratatui::style::Color::from_crossterm(crossterm_color);
- let crossterm_color = ratatui_color.into();
- let crossterm_color = crossterm::style::Color::from(ratatui_color);
+ let crossterm_color = ratatui_color.into_crossterm();

let crossterm_attribute = crossterm::style::types::Attribute::Bold;
- let ratatui_modifier = crossterm_attribute.into();
- let ratatui_modifier = ratatui::style::Modifier::from(crossterm_attribute);
+ let ratatui_modifier = ratatui::style::Modifier::from_crossterm(crossterm_attribute);
- let crossterm_attribute = ratatui_modifier.into();
- let crossterm_attribute = crossterm::style::types::Attribute::from(ratatui_modifier);
+ let crossterm_attribute = ratatui_modifier.into_crossterm();
```

Similar conversions for `ContentStyle` -> `Style` and `Attributes` ->
`Modifier` exist for Crossterm,
and all the Termion and Termwiz types.

---------

Co-authored-by: Orhun Parmaksız <orhun@archlinux.org>
2024-11-03 13:43:24 -08:00
Orhun Parmaksız
fcde9cb9c3 docs(changelog): fix typo (#1463) 2024-11-02 04:28:37 -07:00
Josh McKinney
2ef3583eff chore(ci): replace cargo-make with a custom cargo-xtask (#1461)
This removes the need for cargo-make and replaces it with a custom xtask
binary. See <https://github.com/matklad/cargo-xtask> for info.

Rearranges the CI workflow to use the new xtask and simplify which
workflows that run.

---------

Co-authored-by: Orhun Parmaksız <orhun@archlinux.org>
2024-11-02 10:03:44 +03:00
Thomas TACQUET
a6b579223f docs: fix example link in readme (#1462) 2024-11-01 18:08:07 -07:00
Josh McKinney
f1d0a18375 chore: move ratatui crate into workspace folder (#1459)
This is the first step towards modularization. Handling the move
as a separate step rather than combining it should make it easier
to rebase other PRs when necessary.

---------

Co-authored-by: Orhun Parmaksız <orhunparmaksiz@gmail.com>
2024-10-31 14:59:17 -07:00
Josh McKinney
55fb2d2e56 chore: update repo links to ratatui instead of ratatui-org (#1458) 2024-10-30 03:37:04 -07:00
dependabot[bot]
836634734f chore(deps): bump serde from 1.0.210 to 1.0.213 (#1455)
Bumps [serde](https://github.com/serde-rs/serde) from 1.0.210 to
1.0.213.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/serde-rs/serde/releases">serde's
releases</a>.</em></p>
<blockquote>
<h2>v1.0.213</h2>
<ul>
<li>Fix support for macro-generated <code>with</code> attributes inside
a newtype struct (<a
href="https://redirect.github.com/serde-rs/serde/issues/2847">#2847</a>)</li>
</ul>
<h2>v1.0.212</h2>
<ul>
<li>Fix hygiene of macro-generated local variable accesses in
serde(with) wrappers (<a
href="https://redirect.github.com/serde-rs/serde/issues/2845">#2845</a>)</li>
</ul>
<h2>v1.0.211</h2>
<ul>
<li>Improve error reporting about mismatched signature in
<code>with</code> and <code>default</code> attributes (<a
href="https://redirect.github.com/serde-rs/serde/issues/2558">#2558</a>,
thanks <a
href="https://github.com/Mingun"><code>@​Mingun</code></a>)</li>
<li>Show variant aliases in error message when variant deserialization
fails (<a
href="https://redirect.github.com/serde-rs/serde/issues/2566">#2566</a>,
thanks <a
href="https://github.com/Mingun"><code>@​Mingun</code></a>)</li>
<li>Improve binary size of untagged enum and internally tagged enum
deserialization by about 12% (<a
href="https://redirect.github.com/serde-rs/serde/issues/2821">#2821</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="58a8d22931"><code>58a8d22</code></a>
Release 1.0.213</li>
<li><a
href="ef0ed22593"><code>ef0ed22</code></a>
Merge pull request <a
href="https://redirect.github.com/serde-rs/serde/issues/2847">#2847</a>
from dtolnay/newtypewith</li>
<li><a
href="79925ac394"><code>79925ac</code></a>
Ignore dead_code warning in regression test</li>
<li><a
href="b60e4092ec"><code>b60e409</code></a>
Hygiene for macro-generated newtype struct deserialization with 'with'
attr</li>
<li><a
href="fdc36e5c06"><code>fdc36e5</code></a>
Add regression test for issue 2846</li>
<li><a
href="49e11ce1ba"><code>49e11ce</code></a>
Ignore trivially_copy_pass_by_ref pedantic clippy lint in test</li>
<li><a
href="7ae1b5f8f3"><code>7ae1b5f</code></a>
Release 1.0.212</li>
<li><a
href="1ac054b34a"><code>1ac054b</code></a>
Merge pull request <a
href="https://redirect.github.com/serde-rs/serde/issues/2845">#2845</a>
from dtolnay/withlocal</li>
<li><a
href="1e36ef551d"><code>1e36ef5</code></a>
Fix hygiene of macro-generated local variable accesses in serde(with)
wrappers</li>
<li><a
href="0058c7226e"><code>0058c72</code></a>
Add regression test for issue 2844</li>
<li>Additional commits viewable in <a
href="https://github.com/serde-rs/serde/compare/v1.0.210...v1.0.213">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=serde&package-manager=cargo&previous-version=1.0.210&new-version=1.0.213)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-28 23:10:57 +03:00
Josh McKinney
860e48b0f0 fix(buffer): buffer::get_pos() now correctly handles index > u16::MAX (#1447)
Previously this function wrapped the index pass u16::MAX which caused
problems rendering.

Fixes: <https://github.com/ratatui/ratatui/issues/1441>
2024-10-22 03:46:27 -07:00
miro
04e1b32cd2 docs(layout): rename cassowary-rs references to cassowary (#1448)
closes #1423
2024-10-22 12:15:36 +03:00
Orhun Parmaksız
28732176e1 chore(release): prepare for 0.29.0 (#1444)
🧀
2024-10-21 13:35:36 +03:00
Josh McKinney
6515097434 chore(cargo): check in Cargo.lock (#1434)
When kept up to date, this makes it possible to build any git version
with the same versions of crates that were used for any version, without
it, you can only use the current versions. This makes bugs in semver
compatible code difficult to detect.

The Cargo.lock file is not used by downstream consumers of the crate, so
it is safe to include it in the repository (and recommended by the Rust
docs).

See:
- https://doc.rust-lang.org/cargo/faq.html#why-have-cargolock-in-version-control
- https://blog.rust-lang.org/2023/08/29/committing-lockfiles.html
- https://github.com/rust-lang/cargo/issues/8728

Co-authored-by: Orhun Parmaksız <orhun@archlinux.org>
2024-10-20 11:59:07 +03:00
Orhun Parmaksız
4c4851ca3d feat(example): add drawing feature to the canvas example (#1429)
![rec_20241018T235208](https://github.com/user-attachments/assets/cfb2f9f8-773b-4599-9312-29625ff2ca60)


fun fact: I had to do [35
pushups](https://www.youtube.com/watch?v=eS92stzBYXA) for this...

---------

Co-authored-by: Josh McKinney <joshka@users.noreply.github.com>
2024-10-20 01:41:40 -07:00
Josh McKinney
4f5503dbf6 fix(color)!: hsl and hsluv are now clamped before conversion (#1436)
The `from_hsl` and `from_hsluv` functions now clamp the HSL and HSLuv
values before converting them to RGB. This ensures that the input values
are within the expected range before conversion.

Also note that the ranges of Saturation and Lightness values have been
aligned to be consisten with the palette crate. Saturation and Lightness
for `from_hsl` are now in the range [0.0..1.0] while `from_hsluv` are
in the range [0.0..100.0].

Refs:
- <https://github.com/Ogeon/palette/discussions/253>
- <https://docs.rs/palette/latest/palette/struct.Hsl.html>
- <https://docs.rs/palette/latest/palette/struct.Hsluv.html>

Fixes: <https://github.com/ratatui/ratatui/issues/1433>
2024-10-20 00:46:04 -07:00
Josh McKinney
611086eba4 fix: sparkline docs / doc tests (#1437) 2024-10-20 09:24:38 +03:00
Robert Hensing
514d273875 fix(terminal): use the latest, resized area when clearing (#1427) 2024-10-19 20:17:12 -07:00
FujiApple
60cc15bbb0 feat!: add support for empty bar style to Sparkline (#1326)
- distingiush between empty bars and bars with a value of 0
- provide custom styling for empty bars
- provide custom styling for individual bars
- inverts the rendering algorithm to be item first

Closes: #1325 

BREAKING CHANGE: `Sparkline::data` takes `IntoIterator<Item = SparklineBar>`
instead of `&[u64]` and is no longer const

Co-authored-by: Josh McKinney <joshka@users.noreply.github.com>
2024-10-19 19:49:05 -07:00
Eric Lunderberg
a52ee82fc7 fix(text): truncate based on alignment (#1432)
This is a follow-up PR to https://github.com/ratatui/ratatui/pull/987,
which implemented alignment-aware truncation for the `Line` widget.
However, the truncation only checked the `Line::alignment` field, and
any alignment inherited from a parent's `Text::alignment` field would
not be used.

This commit updates the truncation of `Line` to depend both on the
individual `Line::alignment`, and on any alignment inherited from the
parent's `Text::alignment`.

Co-authored-by: Josh McKinney <joshka@users.noreply.github.com>
2024-10-19 09:16:44 -07:00
Josh McKinney
381ec75329 docs(readme): reduce the length (#1431)
Motivation for this is that there's a bunch of stuff at the bottom of the Readme that we don't really keep up to date. Instead it's better to link to the places that we do keep this info.
2024-10-19 16:28:04 +03:00
Josh McKinney
f6f7794dd7 chore: remove leftover prelude refs / glob imports from example code (#1430)
Fixes: <https://github.com/ratatui/ratatui/issues/1150>

Co-authored-by: Dheepak Krishnamurthy <1813121+kdheepak@users.noreply.github.com>
2024-10-19 02:54:34 -07:00
Dheepak Krishnamurthy
453a308b46 feat!: Add overlap to layout (#1398)
This PR adds a new feature for the existing `Layout::spacing` method,
and introducing a `Spacing` enum.

Now `Layout::spacing` is generic and can take

- zero or positive numbers, e.g. `Layout::spacing(1)` (current
functionality)
- negative number, e.g. `Layout::spacing(-1)` (new)
- variant of the `Spacing` (new)

This allows creating layouts with a shared pixel for segments. When
`spacing(negative_value)` is used, spacing is ignored and all segments
will be adjacent and have pixels overlapping.
`spacing(zero_or_positive_value)` behaves the same as before. These are
internally converted to `Spacing::Overlap` or `Spacing::Space`.

Here's an example output to illustrate the layout solve from this PR:

```rust
#[test]
fn test_layout() {
    use crate::layout::Constraint::*;
    let mut terminal = crate::Terminal::new(crate::backend::TestBackend::new(50, 4)).unwrap();
    terminal
        .draw(|frame| {
            let [upper, lower] = Layout::vertical([Fill(1), Fill(1)]).areas(frame.area());

            let (segments, spacers) = Layout::horizontal([Length(10), Length(10), Length(10)])
                .flex(Flex::Center)
                .split_with_spacers(upper);

            for segment in segments.iter() {
                frame.render_widget(
                    crate::widgets::Block::bordered()
                        .border_set(crate::symbols::border::DOUBLE),
                    *segment,
                );
            }
            for spacer in spacers.iter() {
                frame.render_widget(crate::widgets::Block::bordered(), *spacer);
            }

            let (segments, spacers) = Layout::horizontal([Length(10), Length(10), Length(10)])
                .flex(Flex::Center)
                .spacing(-1) // new feature
                .split_with_spacers(lower);

            for segment in segments.iter() {
                frame.render_widget(
                    crate::widgets::Block::bordered()
                        .border_set(crate::symbols::border::DOUBLE),
                    *segment,
                );
            }
            for spacer in spacers.iter() {
                frame.render_widget(crate::widgets::Block::bordered(), *spacer);
            }
        })
        .unwrap();
    dbg!(terminal.backend());
}
```


```plain
┌────────┐╔════════╗╔════════╗╔════════╗┌────────┐
└────────┘╚════════╝╚════════╝╚════════╝└────────┘
┌─────────┐╔════════╔════════╔════════╗┌─────────┐
└─────────┘╚════════╚════════╚════════╝└─────────┘
```

Currently drawing a border on top of an existing border overwrites it.
Future PRs will allow for making the border drawing handle overlaps
better.

---------

Co-authored-by: Josh McKinney <joshka@users.noreply.github.com>
2024-10-16 05:41:08 -04:00
Josh McKinney
9fd1beedb2 chore!: make Positions iterator fields private (#1424)
BREAKING CHANGE: The Rect Positions iterator no longer has public
fields. The `rect` and `current_position` fields have been made private
as they were not intended to be accessed directly.
2024-10-15 21:38:53 -07:00
Tayfun Bocek
8db7a9a44a perf: implement size hints for Rect iterators (#1420)
Co-authored-by: Josh McKinney <joshka@users.noreply.github.com>
2024-10-15 18:48:17 -07:00
Josh McKinney
b7e488507d fix(color): fix doc test for from_hsl (#1421) 2024-10-15 13:08:52 +03:00
Josh McKinney
4728f0e68b docs: tweak readme (#1419)
Fixes: <https://github.com/ratatui/ratatui/issues/1417>
2024-10-15 03:00:03 -07:00
Orhun Parmaksız
6db16d67fc refactor(color)!: use palette types for Hsl/Hsluv conversions (#1418)
BREAKING-CHANGE: Previously `Color::from_hsl` accepted components
as individual f64 parameters. It now accepts a single `palette::Hsl`
value
and is gated behind a `palette` feature flag.

```diff
- Color::from_hsl(360.0, 100.0, 100.0)
+ Color::from_hsl(Hsl::new(360.0, 100.0, 100.0))
```

Fixes: <https://github.com/ratatui/ratatui/issues/1414>

---------

Co-authored-by: Josh McKinney <joshka@users.noreply.github.com>
2024-10-14 19:15:05 -07:00
Orhun Parmaksız
cc7497532a chore(deps)!: pin unicode-width to 0.2.0 (#1403)
We pin unicode-width to avoid breaking applications when there are breaking changes in the library.

Discussion in #1271

Fixes: #1385

Co-authored-by: Josh McKinney <joshka@users.noreply.github.com>
2024-10-14 19:05:43 -07:00
Neal Fachan
d72968d86b feat(scrolling-regions)!: use terminal scrolling regions to stop Terminal::insert_before from flickering (#1341)
The current implementation of Terminal::insert_before causes the
viewport to flicker. This is described in #584 .

This PR removes that flickering by using terminal scrolling regions
(sometimes called "scroll regions"). A terminal can have its scrolling
region set to something other than the whole screen. When a scroll ANSI
sequence is sent to the terminal and it has a non-default scrolling
region, the terminal will scroll just inside of that region.

We use scrolling regions to implement insert_before. We create a region
on the screen above the viewport, scroll that up to make room for the
newly inserted lines, and then draw the new lines. We may need to repeat
this process depending on how much space there is and how many lines we
need to draw.

When the viewport takes up the entire screen, we take a modified
approach. We create a scrolling region of just the top line (could be
more) of the viewport, then use that to draw the lines we want to
output. When we're done, we scroll it up by one line, into the
scrollback history, and then redraw the top line from the viewport.

A final edge case is when the viewport hasn't yet reached the bottom of
the screen. This case, we set up a different scrolling region, where the
top is the top of the viewport, and the bottom is the viewport's bottom
plus the number of lines we want to scroll by. We then scroll this
region down to open up space above the viewport for drawing the inserted
lines.

Regardless of what we do, we need to reset the scrolling region. This PR
takes the approach of always resetting the scrolling region after every
operation. So the Backend gets new scroll_region_up and
scroll_region_down methods instead of set_scrolling_region, scroll_up,
scroll_down, and reset_scrolling_region methods. We chose that approach
for two reasons. First, we don't want Ratatui to have to remember that
state and then reset the scrolling region when tearing down. Second, the
pre-Windows-10 console code doesn't support scrolling regio

This PR:
- Adds a new scrolling-regions feature.
- Adds two new Backend methods: scroll_region_up and scroll_region_down.
- Implements those Backend methods on all backends in the codebase.
- The crossterm and termion implementations use raw ANSI escape
sequences. I'm trying to merge changes into those two projects
separately to support these functions.
- Adds code to Terminal::insert_before to choose between
insert_before_scrolling_regions and insert_before_no_scrolling_regions.
The latter is the old implementation.
- Adds lots of tests to the TestBackend to for the
scrolling-region-related Backend methods.
- Adds versions of terminal tests that show that insert_before doesn't
clobber the viewport. This is a change in behavior from before.
2024-10-14 15:46:13 -07:00
FujiApple
7bdccce3d5 feat!: add an impl of DoubleEndedIterator for Columns and Rows (#1358)
BREAKING-CHANGE: The `pub` modifier has been removed from fields on the
`layout::rect::Columns` and `layout::rect::Rows` iterators. These fields
were not intended to be public and should not have been accessed
directly.

Fixes: #1357
2024-10-14 15:41:39 -07:00
Josh McKinney
3df685e114 fix(rect)!: Rect::area now returns u32 and Rect::new() no longer clamps area to u16::MAX (#1378)
This change fixes the unexpected behavior of the Rect::new() function to
be more intuitive. The Rect::new() function now clamps the width and
height of the rectangle to keep each bound within u16::MAX. The
Rect::area() function now returns a u32 instead of a u16 to allow for
larger areas to be calculated.

Previously, the Rect::new() function would clamp the total area of the
rectangle to u16::MAX, by preserving the aspect ratio of the rectangle.

BREAKING CHANGE: Rect::area() now returns a u32 instead of a u16.

Fixes: <https://github.com/ratatui/ratatui/issues/1375>

Co-authored-by: Orhun Parmaksız <orhunparmaksiz@gmail.com>
2024-10-14 15:04:56 -07:00
Josh McKinney
4069aa8274 docs: fix missing breaking changes link (#1416) 2024-10-14 17:46:23 +03:00
Josh McKinney
e5a7609588 feat(line)!: impl From<Cow<str>> for Line (#1373)
BREAKING-CHANGES: `Line` now implements `From<Cow<str>`

As this adds an extra conversion, ambiguous infered values may no longer
compile.

```rust
// given:
struct Foo { ... }
impl From<Foo> for String { ... }
impl From<Foo> for Cow<str> { ... }

let foo = Foo { ... };
let line = Line::from(foo); // now fails due to ambiguous type inference
// replace with
let line = Line::from(String::from(foo));
```

Fixes: https://github.com/ratatui/ratatui/issues/1367

---------

Co-authored-by: Orhun Parmaksız <orhunparmaksiz@gmail.com>
2024-10-14 17:02:57 +03:00
Orhun Parmaksız
69e0cd2fc4 chore(deny): allow Zlib license in cargo-deny configuration (#1411) 2024-10-14 02:48:30 -07:00
Josh McKinney
ab6b1feaec feat(tabs)!: allow tabs to be deselected (#1413)
`Tabs::select()` now accepts `Into<Option<usize>>` instead of `usize`.
This allows tabs to be deselected by passing `None`.

`Tabs::default()` is now also implemented manually instead of deriving
`Default`, and a new method `Tabs::titles()` is added to set the titles
of the tabs.

Fixes: <https://github.com/ratatui/ratatui/pull/1412>

BREAKING CHANGE: `Tabs::select()` now accepts `Into<Option<usize>>`
which breaks any code already using parameter type inference:

```diff
let selected = 1u8;
- let tabs = Tabs::new(["A", "B"]).select(selected.into())
+ let tabs = Tabs::new(["A", "B"]).select(selected as usize)
```
2024-10-14 02:44:58 -07:00
du-ob
3a43274881 feat(color): add hsluv support (#1333)
closes #763

---------

Co-authored-by: Orhun Parmaksız <orhun@archlinux.org>
2024-10-13 14:08:20 +03:00
Tayfun Bocek
dc8d0587ec feat(table)!: add support for selecting column and cell (#1331)
Fixes https://github.com/ratatui-org/ratatui/issues/1250

Adds support for selecting a column and cell in `TableState`. The
selected column, and cells style can be set by
`Table::column_highlight_style` and `Table::cell_highlight_style`
respectively.

The table example has also been updated to display the new
functionality:


https://github.com/user-attachments/assets/e5fd2858-4931-4ce1-a2f6-a5ea1eacbecc

BREAKING CHANGE: The Serialized output of the state will now include the
"selected_column" field. Software that manually parse the serialized the
output (with anything other than the `Serialize` implementation on
`TableState`) may have to be refactored if the "selected_column" field
is not accounted for. This does not affect users who rely on the
`Deserialize`, or `Serialize` implementation on the state.

BREAKING CHANGE: The `Table::highlight_style` is now deprecated in favor
of `Table::row_highlight_style`.

---------

Co-authored-by: Orhun Parmaksız <orhunparmaksiz@gmail.com>
2024-10-13 14:06:29 +03:00
Josh McKinney
23c0d52c29 feat(text): improve concise debug view for Span,Line,Text,Style (#1410)
Improves https://github.com/ratatui/ratatui/pull/1383

The following now round trips when formatted for debug.
This will make it easier to use insta when testing text related views of
widgets.

```rust
Text::from_iter([
    Line::from("Hello, world!"),
    Line::from("How are you?").bold().left_aligned(),
    Line::from_iter([
        Span::from("I'm "),
        Span::from("doing ").italic(),
        Span::from("great!").bold(),
    ]),
]).on_blue().italic().centered()
```
2024-10-08 09:08:21 +03:00
Tayfun Bocek
c32baa7cd8 chore: add benchmark for Table (#1408) 2024-10-07 12:32:05 -07:00
Raffaele Cataldo
1153a9ebaf refactor: Consistent result expected in layout tests (#1406)
Fixes #1399 
I've looked through all the `assert_eq` and made sure that they follow
the `expected, result` pattern. I wasn't sure if it was desired to
actually pass result and expected as variables to the assert_eq
statements, so I've left everything that seems to have followed the
pattern as is.
2024-10-07 00:09:42 -04:00
Josh McKinney
2805dddf05 feat(logo): Add a Ratatui logo widget
This is a simple logo widget that can be used to render the Ratatui logo
in the terminal. It is used in the `examples/ratatui-logo.rs` example,
and may be used in your applications' help or about screens.

```rust
use ratatui::{Frame, widgets::RatatuiLogo};

fn draw(frame: &mut Frame) {
    frame.render_widget(RatatuiLogo::tiny(), frame.area());
}
```
2024-10-05 17:35:43 -07:00
dependabot[bot]
baf047f556 chore(deps): update rstest requirement from 0.22.0 to 0.23.0 (#1394)
Updates the requirements on [rstest](https://github.com/la10736/rstest)
to permit the latest version.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/la10736/rstest/releases">rstest's
releases</a>.</em></p>
<blockquote>
<h2>0.23.0</h2>
<h2>What's Changed</h2>
<ul>
<li>Upgrade to async-std v1.13 by <a
href="https://github.com/jayvdb"><code>@​jayvdb</code></a> in <a
href="https://redirect.github.com/la10736/rstest/pull/274">la10736/rstest#274</a></li>
<li>Allow environment variables in #[files] attributes by <a
href="https://github.com/hansl"><code>@​hansl</code></a> in <a
href="https://redirect.github.com/la10736/rstest/pull/277">la10736/rstest#277</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a href="https://github.com/jayvdb"><code>@​jayvdb</code></a> made
their first contribution in <a
href="https://redirect.github.com/la10736/rstest/pull/274">la10736/rstest#274</a></li>
<li><a href="https://github.com/hansl"><code>@​hansl</code></a> made
their first contribution in <a
href="https://redirect.github.com/la10736/rstest/pull/277">la10736/rstest#277</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/la10736/rstest/compare/v0.22.0...v0.23.0">https://github.com/la10736/rstest/compare/v0.22.0...v0.23.0</a></p>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/la10736/rstest/blob/master/CHANGELOG.md">rstest's
changelog</a>.</em></p>
<blockquote>
<h2>[0.23.0] 2024/9/29</h2>
<h3>Add</h3>
<ul>
<li>You can now use environment variables in <code>#[files]</code> with
an optional default value (see <a
href="https://redirect.github.com/la10736/rstest/pull/277">#277</a>).</li>
<li>You can now set a base_dir for <code>#[files]</code> with the
<code>$[base_dir = &quot;...&quot;]</code> attribute (see <a
href="https://redirect.github.com/la10736/rstest/pull/277">#277</a>).</li>
</ul>
<h2>[0.22.0] 2024/8/4</h2>
<h3>Changed</h3>
<ul>
<li>Now it's possible destructuring input values both for cases, values
and fixtures. See <a
href="https://redirect.github.com/la10736/rstest/issues/231">#231</a>
for details</li>
</ul>
<h3>Add</h3>
<ul>
<li>Implemented <code>#[ignore]</code> attribute to ignore test
parameters during fixtures resolution/injection. See <a
href="https://redirect.github.com/la10736/rstest/issues/228">#228</a>
for details</li>
</ul>
<h3>Fixed</h3>
<ul>
<li>Lot of typo in code</li>
</ul>
<h2>[0.21.0] 2024/6/1</h2>
<h3>Changed</h3>
<ul>
<li>Add feature <code>crate-name</code> enabled by default to opt-in
crate rename
support. See <a
href="https://redirect.github.com/la10736/rstest/issues/258">#258</a></li>
</ul>
<h2>[0.20.0] 2024/5/30</h2>
<h3>Add</h3>
<ul>
<li>Implemented <code>#[by_ref]</code> attribute to take get a local
lifetime for test arguments.
See <a
href="https://redirect.github.com/la10736/rstest/issues/241">#241</a>
for more details. Thanks to
<a href="https://github.com/narpfel"><code>@​narpfel</code></a> for
suggesting it and useful discussions.</li>
<li>Support for import <code>rstest</code> with another name. See <a
href="https://redirect.github.com/la10736/rstest/issues/221">#221</a></li>
</ul>
<h3>Fixed</h3>
<ul>
<li>Don't remove Lifetimes from test function if any. See <a
href="https://redirect.github.com/la10736/rstest/issues/230">#230</a>
<a href="https://redirect.github.com/la10736/rstest/issues/241">#241</a>
for more details.</li>
<li><a
href="https://doc.rust-lang.org/std/path/struct.PathBuf.html"><code>PathBuf</code></a>
does no longer need to be
in scope when using <code>#[files]</code> (see <a
href="https://redirect.github.com/la10736/rstest/pull/242">#242</a>)</li>
<li><code>#[from(now::accept::also::path::for::fixture)]</code> See <a
href="https://redirect.github.com/la10736/rstest/issues/246">#246</a>
for more details</li>
</ul>
<h2>[0.19.0] 2024/4/9</h2>
<h3>Changed</h3>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="da11d4e48a"><code>da11d4e</code></a>
Update dependency and prepare the release</li>
<li><a
href="0c6e203b53"><code>0c6e203</code></a>
Update checkout list</li>
<li><a
href="20e8858611"><code>20e8858</code></a>
Make clippy happy</li>
<li><a
href="57a93425a5"><code>57a9342</code></a>
Playground should use dev package</li>
<li><a
href="8a04803580"><code>8a04803</code></a>
Removed the useless build rerun variable</li>
<li><a
href="8c232bce88"><code>8c232bc</code></a>
Add a test for invalid base_dir value</li>
<li><a
href="3520861ca5"><code>3520861</code></a>
Add tests for replace_env_vars</li>
<li><a
href="465a4012af"><code>465a401</code></a>
Add unit tests for parsing attributes, and add base_dir</li>
<li><a
href="a970613f43"><code>a970613</code></a>
Add a test for declared environment variables</li>
<li><a
href="c65d1c002c"><code>c65d1c0</code></a>
Add tests and #[ignore_missing_env_vars]</li>
<li>Additional commits viewable in <a
href="https://github.com/la10736/rstest/compare/v0.22.0...v0.23.0">compare
view</a></li>
</ul>
</details>
<br />


Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-05 23:49:25 +03:00
dependabot[bot]
6745a10508 chore(deps): update octocrab requirement from 0.40.0 to 0.41.0 (#1393)
Updates the requirements on
[octocrab](https://github.com/XAMPPRocky/octocrab) to permit the latest
version.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/XAMPPRocky/octocrab/releases">octocrab's
releases</a>.</em></p>
<blockquote>
<h2>v0.41.0</h2>
<h3>Added</h3>
<ul>
<li>Implement getting users and reopos by their respective IDs (<a
href="https://redirect.github.com/XAMPPRocky/octocrab/pull/690">#690</a>)</li>
</ul>
<h3>Fixed</h3>
<ul>
<li><em>(installation)</em> [<strong>breaking</strong>] Return Result
instead of panicking in <code>Octocrab::installation</code> (<a
href="https://redirect.github.com/XAMPPRocky/octocrab/pull/687">#687</a>)</li>
</ul>
<h3>Other</h3>
<ul>
<li>Update tower-http requirement from 0.5.1 to 0.6.1 (<a
href="https://redirect.github.com/XAMPPRocky/octocrab/pull/701">#701</a>)</li>
<li>add additional webhook model fields (<a
href="https://redirect.github.com/XAMPPRocky/octocrab/pull/696">#696</a>)</li>
<li>Bump hyper-rustls version. (<a
href="https://redirect.github.com/XAMPPRocky/octocrab/pull/699">#699</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/XAMPPRocky/octocrab/blob/main/CHANGELOG.md">octocrab's
changelog</a>.</em></p>
<blockquote>
<h2><a
href="https://github.com/XAMPPRocky/octocrab/compare/v0.40.0...v0.41.0">0.41.0</a>
- 2024-09-30</h2>
<h3>Added</h3>
<ul>
<li>Implement getting users and reopos by their respective IDs (<a
href="https://redirect.github.com/XAMPPRocky/octocrab/pull/690">#690</a>)</li>
</ul>
<h3>Fixed</h3>
<ul>
<li><em>(installation)</em> [<strong>breaking</strong>] Return Result
instead of panicking in <code>Octocrab::installation</code> (<a
href="https://redirect.github.com/XAMPPRocky/octocrab/pull/687">#687</a>)</li>
</ul>
<h3>Other</h3>
<ul>
<li>Update tower-http requirement from 0.5.1 to 0.6.1 (<a
href="https://redirect.github.com/XAMPPRocky/octocrab/pull/701">#701</a>)</li>
<li>add additional webhook model fields (<a
href="https://redirect.github.com/XAMPPRocky/octocrab/pull/696">#696</a>)</li>
<li>Bump hyper-rustls version. (<a
href="https://redirect.github.com/XAMPPRocky/octocrab/pull/699">#699</a>)</li>
</ul>
<h2><a
href="https://github.com/XAMPPRocky/octocrab/compare/v0.39.0...v0.40.0">0.40.0</a>
- 2024-09-22</h2>
<h3>Added</h3>
<ul>
<li>Support <code>remove_assignees</code> on issue API (<a
href="https://redirect.github.com/XAMPPRocky/octocrab/pull/686">#686</a>)</li>
<li>add missing fields in <code>CreateForkBuilder</code> (<a
href="https://redirect.github.com/XAMPPRocky/octocrab/pull/682">#682</a>)</li>
<li>Add <code>Gist::public</code> field (<a
href="https://redirect.github.com/XAMPPRocky/octocrab/pull/678">#678</a>)</li>
</ul>
<h3>Fixed</h3>
<ul>
<li><em>(refs)</em> [<strong>breaking</strong>] remove
<code>Reference::Commit</code> variant (<a
href="https://redirect.github.com/XAMPPRocky/octocrab/pull/697">#697</a>)</li>
</ul>
<h3>Other</h3>
<ul>
<li>Fix typo in cfg_attr statement (<a
href="https://redirect.github.com/XAMPPRocky/octocrab/pull/693">#693</a>)</li>
<li>Handle empty author object in pr_commits (<a
href="https://redirect.github.com/XAMPPRocky/octocrab/pull/656">#656</a>)</li>
<li>Add <code>DeviceCodes::poll_until_available</code> method (<a
href="https://redirect.github.com/XAMPPRocky/octocrab/pull/679">#679</a>)</li>
<li>Uncomment pr_commits function (<a
href="https://redirect.github.com/XAMPPRocky/octocrab/pull/680">#680</a>)</li>
<li>Only add base_path if req_pandq does not contain it (<a
href="https://redirect.github.com/XAMPPRocky/octocrab/pull/684">#684</a>)</li>
<li>Update code scanning alert (<a
href="https://redirect.github.com/XAMPPRocky/octocrab/pull/673">#673</a>)</li>
<li>Added <code>merged_by</code> and <code>closed_by</code> fields (<a
href="https://redirect.github.com/XAMPPRocky/octocrab/pull/674">#674</a>)</li>
<li>Update and Fixes to the Code Scanning Models &amp; Webhooks (<a
href="https://redirect.github.com/XAMPPRocky/octocrab/pull/675">#675</a>)</li>
</ul>
<h2><a
href="https://github.com/XAMPPRocky/octocrab/compare/v0.38.0...v0.39.0">0.39.0</a>
- 2024-07-30</h2>
<h3>Added</h3>
<ul>
<li>support permission on list_collaborators (<a
href="https://redirect.github.com/XAMPPRocky/octocrab/pull/630">#630</a>)</li>
<li>add check run pull requests and list parameters (<a
href="https://redirect.github.com/XAMPPRocky/octocrab/pull/494">#494</a>)</li>
<li>implement hook deliveries (<a
href="https://redirect.github.com/XAMPPRocky/octocrab/pull/668">#668</a>)</li>
<li>allow sending non String payload with execute (<a
href="https://redirect.github.com/XAMPPRocky/octocrab/pull/665">#665</a>)</li>
<li>added /user/blocks functionality (<a
href="https://redirect.github.com/XAMPPRocky/octocrab/pull/657">#657</a>)</li>
<li>add method to create repo webhook (<a
href="https://redirect.github.com/XAMPPRocky/octocrab/pull/640">#640</a>)</li>
</ul>
<h3>Fixed</h3>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="8f7929f120"><code>8f7929f</code></a>
chore: release (<a
href="https://redirect.github.com/XAMPPRocky/octocrab/issues/700">#700</a>)</li>
<li><a
href="4164ea9ec6"><code>4164ea9</code></a>
fix(installation)!: Return Result instead of panicking in
`Octocrab::installa...</li>
<li><a
href="6ca41409d5"><code>6ca4140</code></a>
feat: Implement getting users and reopos by their respective IDs (<a
href="https://redirect.github.com/XAMPPRocky/octocrab/issues/690">#690</a>)</li>
<li><a
href="5f1ee03a9b"><code>5f1ee03</code></a>
Update tower-http requirement from 0.5.1 to 0.6.1 (<a
href="https://redirect.github.com/XAMPPRocky/octocrab/issues/701">#701</a>)</li>
<li><a
href="5ffd5313ee"><code>5ffd531</code></a>
add additional webhook model fields (<a
href="https://redirect.github.com/XAMPPRocky/octocrab/issues/696">#696</a>)</li>
<li><a
href="53e3d4f403"><code>53e3d4f</code></a>
Bump hyper-rustls version. (<a
href="https://redirect.github.com/XAMPPRocky/octocrab/issues/699">#699</a>)</li>
<li>See full diff in <a
href="https://github.com/XAMPPRocky/octocrab/compare/v0.40.0...v0.41.0">compare
view</a></li>
</ul>
</details>
<br />


Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-05 23:47:42 +03:00
dependabot[bot]
7799f4ff5b chore(deps): bump bnjbvr/cargo-machete from 0.6.2 to 0.7.0 (#1392)
Bumps [bnjbvr/cargo-machete](https://github.com/bnjbvr/cargo-machete)
from 0.6.2 to 0.7.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/bnjbvr/cargo-machete/releases">bnjbvr/cargo-machete's
releases</a>.</em></p>
<blockquote>
<h2>v0.7.0</h2>
<ul>
<li>Breaking change: Don't search in ignored files (those specified in
.ignore/.gitignore) by default. It's possible to use
<code>--no-ignore</code> to search in these directories by default (<a
href="https://redirect.github.com/bnjbvr/cargo-machete/issues/137">#137</a>).</li>
<li>Improved: fix false positives for multi dependencies single use
statements (<a
href="https://redirect.github.com/bnjbvr/cargo-machete/issues/120">#120</a>).
This improves precision at the cost of a small performance hit.</li>
<li>Improved: make usage of <code>--with-metadata</code> more accurate
(<a
href="https://redirect.github.com/bnjbvr/cargo-machete/issues/122">#122</a>,
<a
href="https://redirect.github.com/bnjbvr/cargo-machete/issues/132">#132</a>).</li>
<li>Improved: instead of displaying <code>.</code> for the current
directory, <code>cargo-machete</code> will now display <code>this
directory</code> (<a
href="https://redirect.github.com/bnjbvr/cargo-machete/issues/109">#109</a>).</li>
<li>Added: There's now an automated docker image build that publishes to
the <a
href="https://github.com/bnjbvr/cargo-machete/pkgs/container/cargo-machete">github
repository</a> (<a
href="https://redirect.github.com/bnjbvr/cargo-machete/issues/121">#121</a>).</li>
<li>Added: <code>--ignore</code> flag which make cargo-machete respect
.ignore and .gitignore files when searching for files (<a
href="https://redirect.github.com/bnjbvr/cargo-machete/issues/95">#95</a>).</li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/bnjbvr/cargo-machete/blob/main/CHANGELOG.md">bnjbvr/cargo-machete's
changelog</a>.</em></p>
<blockquote>
<h1>0.7.0 (released on 2024-09-25)</h1>
<ul>
<li>Breaking change: Don't search in ignored files (those specified in
.ignore/.gitignore) by default. It's possible to use
<code>--no-ignore</code> to search in these directories by default (<a
href="https://redirect.github.com/bnjbvr/cargo-machete/issues/137">#137</a>).</li>
<li>Improved: fix false positives for multi dependencies single use
statements (<a
href="https://redirect.github.com/bnjbvr/cargo-machete/issues/120">#120</a>).
This improves precision at the cost of a small performance hit.</li>
<li>Improved: make usage of <code>--with-metadata</code> more accurate
(<a
href="https://redirect.github.com/bnjbvr/cargo-machete/issues/122">#122</a>,
<a
href="https://redirect.github.com/bnjbvr/cargo-machete/issues/132">#132</a>).</li>
<li>Improved: instead of displaying <code>.</code> for the current
directory, <code>cargo-machete</code> will now display <code>this
directory</code> (<a
href="https://redirect.github.com/bnjbvr/cargo-machete/issues/109">#109</a>).</li>
<li>Added: There's now an automated docker image build that publishes to
the <a
href="https://github.com/bnjbvr/cargo-machete/pkgs/container/cargo-machete">github
repository</a> (<a
href="https://redirect.github.com/bnjbvr/cargo-machete/issues/121">#121</a>).</li>
<li>Added: <code>--ignore</code> flag which make cargo-machete respect
.ignore and .gitignore files when searching for files (<a
href="https://redirect.github.com/bnjbvr/cargo-machete/issues/95">#95</a>).</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="5eaad10acf"><code>5eaad10</code></a>
ci: update OS versions in release task</li>
<li><a
href="a881c7261d"><code>a881c72</code></a>
Update CHANGELOG.md: address typo (thanks <a
href="https://github.com/signez"><code>@​signez</code></a>)</li>
<li><a
href="37beb07f34"><code>37beb07</code></a>
Release 0.7.0</li>
<li><a
href="ad0fea4b0b"><code>ad0fea4</code></a>
bump dependencies</li>
<li><a
href="426119c8f0"><code>426119c</code></a>
Fix typo.</li>
<li><a
href="a14e71ad3d"><code>a14e71a</code></a>
Don't search in ignored files by default</li>
<li><a
href="a767940ee9"><code>a767940</code></a>
Fix false positive for multi dep single use statements (<a
href="https://redirect.github.com/bnjbvr/cargo-machete/issues/120">#120</a>)</li>
<li><a
href="16e3e931d0"><code>16e3e93</code></a>
move regex-try to its own repository</li>
<li><a
href="bea9e7361a"><code>bea9e73</code></a>
nump dependencies</li>
<li><a
href="94a21474d2"><code>94a2147</code></a>
remove unnecessary clones, and some minor changes</li>
<li>Additional commits viewable in <a
href="https://github.com/bnjbvr/cargo-machete/compare/v0.6.2...v0.7.0">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=bnjbvr/cargo-machete&package-manager=github_actions&previous-version=0.6.2&new-version=0.7.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-05 23:43:00 +03:00
Dheepak Krishnamurthy
edcdc8a814 refactor(layout): rename element to segment in layout (#1397)
This PR renames `element` to `segment` in a couple of functions in the
layout calculations for clarity. `element` can refer to `segment`s or
`spacer`s and functions that take only `segment`s should use `segment`
as the variable names.
2024-10-02 22:34:07 +03:00
Josh McKinney
5ad623c29b chore: remove usage of prelude (#1390)
This helps make the doc examples more explicit about what is being used.
It will also makes it a bit easier to do future refactoring of Ratatui,
into several crates, as the ambiguity of where types are coming from
will be reduced.

Additionally, several doc examples have been simplified to use Stylize,
and necessary imports are no longer hidden.

This doesn't remove the prelude. Only the internal usages.
2024-09-28 11:47:45 -07:00
Josh McKinney
bc10af5931 chore(style): make Debug output for Text/Line/Span/Style more concise (#1383)
Given:

```rust
Text::from_iter([
    Line::from("without line fields"),
    Line::from("with line fields").bold().centered(),
    Line::from_iter([
        Span::from("without span fields"),
        Span::from("with span fields")
            .green()
            .on_black()
            .italic()
            .not_dim(),
    ]),
])
```

Debug:
```
Text [Line [Span("without line fields")], Line { style: Style::new().add_modifier(Modifier::BOLD), alignment: Some(Center), spans: [Span("with line fields")] }, Line [Span("without span fields"), Span { style: Style::new().green().on_black().add_modifier(Modifier::ITALIC).remove_modifier(Modifier::DIM), content: "with span fields" }]]
```

Fixes: https://github.com/ratatui/ratatui/issues/1382
---------

Co-authored-by: Orhun Parmaksız <orhun@archlinux.org>
Co-authored-by: Orhun Parmaksız <orhunparmaksiz@gmail.com>
2024-09-26 20:38:23 +03:00
dependabot[bot]
784f67a912 chore(deps): update octocrab requirement from 0.39.0 to 0.40.0 (#1386)
Updates the requirements on
[octocrab](https://github.com/XAMPPRocky/octocrab) to permit the latest
version.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/XAMPPRocky/octocrab/releases">octocrab's
releases</a>.</em></p>
<blockquote>
<h2>v0.40.0</h2>
<h3>Added</h3>
<ul>
<li>Support <code>remove_assignees</code> on issue API (<a
href="https://redirect.github.com/XAMPPRocky/octocrab/pull/686">#686</a>)</li>
<li>add missing fields in <code>CreateForkBuilder</code> (<a
href="https://redirect.github.com/XAMPPRocky/octocrab/pull/682">#682</a>)</li>
<li>Add <code>Gist::public</code> field (<a
href="https://redirect.github.com/XAMPPRocky/octocrab/pull/678">#678</a>)</li>
</ul>
<h3>Fixed</h3>
<ul>
<li><em>(refs)</em> [<strong>breaking</strong>] remove
<code>Reference::Commit</code> variant (<a
href="https://redirect.github.com/XAMPPRocky/octocrab/pull/697">#697</a>)</li>
</ul>
<h3>Other</h3>
<ul>
<li>Fix typo in cfg_attr statement (<a
href="https://redirect.github.com/XAMPPRocky/octocrab/pull/693">#693</a>)</li>
<li>Handle empty author object in pr_commits (<a
href="https://redirect.github.com/XAMPPRocky/octocrab/pull/656">#656</a>)</li>
<li>Add <code>DeviceCodes::poll_until_available</code> method (<a
href="https://redirect.github.com/XAMPPRocky/octocrab/pull/679">#679</a>)</li>
<li>Uncomment pr_commits function (<a
href="https://redirect.github.com/XAMPPRocky/octocrab/pull/680">#680</a>)</li>
<li>Only add base_path if req_pandq does not contain it (<a
href="https://redirect.github.com/XAMPPRocky/octocrab/pull/684">#684</a>)</li>
<li>Update code scanning alert (<a
href="https://redirect.github.com/XAMPPRocky/octocrab/pull/673">#673</a>)</li>
<li>Added <code>merged_by</code> and <code>closed_by</code> fields (<a
href="https://redirect.github.com/XAMPPRocky/octocrab/pull/674">#674</a>)</li>
<li>Update and Fixes to the Code Scanning Models &amp; Webhooks (<a
href="https://redirect.github.com/XAMPPRocky/octocrab/pull/675">#675</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/XAMPPRocky/octocrab/blob/main/CHANGELOG.md">octocrab's
changelog</a>.</em></p>
<blockquote>
<h2><a
href="https://github.com/XAMPPRocky/octocrab/compare/v0.39.0...v0.40.0">0.40.0</a>
- 2024-09-22</h2>
<h3>Added</h3>
<ul>
<li>Support <code>remove_assignees</code> on issue API (<a
href="https://redirect.github.com/XAMPPRocky/octocrab/pull/686">#686</a>)</li>
<li>add missing fields in <code>CreateForkBuilder</code> (<a
href="https://redirect.github.com/XAMPPRocky/octocrab/pull/682">#682</a>)</li>
<li>Add <code>Gist::public</code> field (<a
href="https://redirect.github.com/XAMPPRocky/octocrab/pull/678">#678</a>)</li>
</ul>
<h3>Fixed</h3>
<ul>
<li><em>(refs)</em> [<strong>breaking</strong>] remove
<code>Reference::Commit</code> variant (<a
href="https://redirect.github.com/XAMPPRocky/octocrab/pull/697">#697</a>)</li>
</ul>
<h3>Other</h3>
<ul>
<li>Fix typo in cfg_attr statement (<a
href="https://redirect.github.com/XAMPPRocky/octocrab/pull/693">#693</a>)</li>
<li>Handle empty author object in pr_commits (<a
href="https://redirect.github.com/XAMPPRocky/octocrab/pull/656">#656</a>)</li>
<li>Add <code>DeviceCodes::poll_until_available</code> method (<a
href="https://redirect.github.com/XAMPPRocky/octocrab/pull/679">#679</a>)</li>
<li>Uncomment pr_commits function (<a
href="https://redirect.github.com/XAMPPRocky/octocrab/pull/680">#680</a>)</li>
<li>Only add base_path if req_pandq does not contain it (<a
href="https://redirect.github.com/XAMPPRocky/octocrab/pull/684">#684</a>)</li>
<li>Update code scanning alert (<a
href="https://redirect.github.com/XAMPPRocky/octocrab/pull/673">#673</a>)</li>
<li>Added <code>merged_by</code> and <code>closed_by</code> fields (<a
href="https://redirect.github.com/XAMPPRocky/octocrab/pull/674">#674</a>)</li>
<li>Update and Fixes to the Code Scanning Models &amp; Webhooks (<a
href="https://redirect.github.com/XAMPPRocky/octocrab/pull/675">#675</a>)</li>
</ul>
<h2><a
href="https://github.com/XAMPPRocky/octocrab/compare/v0.38.0...v0.39.0">0.39.0</a>
- 2024-07-30</h2>
<h3>Added</h3>
<ul>
<li>support permission on list_collaborators (<a
href="https://redirect.github.com/XAMPPRocky/octocrab/pull/630">#630</a>)</li>
<li>add check run pull requests and list parameters (<a
href="https://redirect.github.com/XAMPPRocky/octocrab/pull/494">#494</a>)</li>
<li>implement hook deliveries (<a
href="https://redirect.github.com/XAMPPRocky/octocrab/pull/668">#668</a>)</li>
<li>allow sending non String payload with execute (<a
href="https://redirect.github.com/XAMPPRocky/octocrab/pull/665">#665</a>)</li>
<li>added /user/blocks functionality (<a
href="https://redirect.github.com/XAMPPRocky/octocrab/pull/657">#657</a>)</li>
<li>add method to create repo webhook (<a
href="https://redirect.github.com/XAMPPRocky/octocrab/pull/640">#640</a>)</li>
</ul>
<h3>Fixed</h3>
<ul>
<li>use put instead of get for set_thread_subscription (<a
href="https://redirect.github.com/XAMPPRocky/octocrab/pull/661">#661</a>)</li>
<li><em>(builder)</em> Change add_retry_config signature to match others
in OctocrabBuilder (<a
href="https://redirect.github.com/XAMPPRocky/octocrab/pull/643">#643</a>)</li>
</ul>
<h3>Other</h3>
<ul>
<li>getting Code Scanning (<a
href="https://redirect.github.com/XAMPPRocky/octocrab/pull/669">#669</a>)</li>
<li>added missing /repos/{owner}/{repo}/pulls/... handlers (<a
href="https://redirect.github.com/XAMPPRocky/octocrab/pull/546">#546</a>)
(<a
href="https://redirect.github.com/XAMPPRocky/octocrab/pull/605">#605</a>)</li>
<li>Properly mark feature-gated functionality in docs (<a
href="https://redirect.github.com/XAMPPRocky/octocrab/pull/662">#662</a>)</li>
<li>repos/releases improvements (<a
href="https://redirect.github.com/XAMPPRocky/octocrab/pull/666">#666</a>)</li>
<li>Add AutoRebaseEnabled to models.rs (<a
href="https://redirect.github.com/XAMPPRocky/octocrab/pull/660">#660</a>)</li>
<li>cargo fmt (<a
href="https://redirect.github.com/XAMPPRocky/octocrab/pull/658">#658</a>)</li>
<li>Fix issue <a
href="https://redirect.github.com/XAMPPRocky/octocrab/pull/635">#635</a>
(<a
href="https://redirect.github.com/XAMPPRocky/octocrab/pull/637">#637</a>)</li>
<li>Update issues.rs (<a
href="https://redirect.github.com/XAMPPRocky/octocrab/pull/634">#634</a>)</li>
<li>Add head repo to create pr (<a
href="https://redirect.github.com/XAMPPRocky/octocrab/pull/636">#636</a>)</li>
<li>Added support for make_latest in UpdateReleaseBuilder (<a
href="https://redirect.github.com/XAMPPRocky/octocrab/pull/646">#646</a>)</li>
<li>Changing the user name from required to optional parameter (<a
href="https://redirect.github.com/XAMPPRocky/octocrab/pull/650">#650</a>)</li>
<li>Update models.rs (<a
href="https://redirect.github.com/XAMPPRocky/octocrab/pull/651">#651</a>)</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="bd8b648282"><code>bd8b648</code></a>
chore: release (<a
href="https://redirect.github.com/XAMPPRocky/octocrab/issues/685">#685</a>)</li>
<li><a
href="a8bcee4c29"><code>a8bcee4</code></a>
fix(refs)!: remove <code>Reference::Commit</code> variant (<a
href="https://redirect.github.com/XAMPPRocky/octocrab/issues/697">#697</a>)</li>
<li><a
href="3167540eb0"><code>3167540</code></a>
Fix typo in cfg_attr statement (<a
href="https://redirect.github.com/XAMPPRocky/octocrab/issues/693">#693</a>)</li>
<li><a
href="01767f30eb"><code>01767f3</code></a>
Handle empty author object in pr_commits (<a
href="https://redirect.github.com/XAMPPRocky/octocrab/issues/656">#656</a>)</li>
<li><a
href="689ee43ab0"><code>689ee43</code></a>
feat: Support <code>remove_assignees</code> on issue API (<a
href="https://redirect.github.com/XAMPPRocky/octocrab/issues/686">#686</a>)</li>
<li><a
href="8aa113e079"><code>8aa113e</code></a>
Add <code>DeviceCodes::poll_until_available</code> method (<a
href="https://redirect.github.com/XAMPPRocky/octocrab/issues/679">#679</a>)</li>
<li><a
href="501ef7439d"><code>501ef74</code></a>
feat: add missing fields in <code>CreateForkBuilder</code> (<a
href="https://redirect.github.com/XAMPPRocky/octocrab/issues/682">#682</a>)</li>
<li><a
href="807cc7592d"><code>807cc75</code></a>
Uncomment pr_commits function (<a
href="https://redirect.github.com/XAMPPRocky/octocrab/issues/680">#680</a>)</li>
<li><a
href="0459e31986"><code>0459e31</code></a>
Only add base_path if req_pandq does not contain it (<a
href="https://redirect.github.com/XAMPPRocky/octocrab/issues/684">#684</a>)</li>
<li><a
href="70e9b9e49f"><code>70e9b9e</code></a>
feat: Add <code>Gist::public</code> field (<a
href="https://redirect.github.com/XAMPPRocky/octocrab/issues/678">#678</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/XAMPPRocky/octocrab/compare/v0.39.0...v0.40.0">compare
view</a></li>
</ul>
</details>
<br />


Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-23 21:21:17 +03:00
Josh McKinney
f4880b40cc chore: lock unicode-width version to 0.1.13 (#1384)
0.1.14 contains breaking changes which we'll need to investigate.
This commit puts a lock on the current version for now.

Changes
https://github.com/unicode-rs/unicode-width/compare/v0.1.13...v0.1.14
2024-09-21 02:58:29 -07:00
Josh McKinney
67c0ea243b chore(block): deprecate block::Title (#1372)
`ratatui::widgets::block::Title` is deprecated in favor of using `Line`
to represent titles.
This removes an unnecessary layer of wrapping (string -> Span -> Line ->
Title).

This struct will be removed in a future release of Ratatui (likely
0.31).
For more information see:
<https://github.com/ratatui/ratatui/issues/738>

To update your code:
```rust
Block::new().title(Title::from("foo"));
// becomes any of
Block::new().title("foo");
Block::new().title(Line::from("foo"));

Block::new().title(Title::from("foo").position(Position::TOP));
// becomes any of
Block::new().title_top("foo");
Block::new().title_top(Line::from("foo"));

Block::new().title(Title::from("foo").position(Position::BOTTOM));
// becomes any of
Block::new().title_bottom("foo");
Block::new().title_bottom(Line::from("foo"));
```
2024-09-20 10:21:26 +03:00
Anthony Rodgers
b9653ba05a fix: prevent calender render panic when terminal height is small (#1380)
Fixes: #1379
2024-09-19 06:02:26 -07:00
dependabot[bot]
9875d9facc chore(deps): bump DavidAnson/markdownlint-cli2-action from 16 to 17 (#1376)
Bumps
[DavidAnson/markdownlint-cli2-action](https://github.com/davidanson/markdownlint-cli2-action)
from 16 to 17.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/davidanson/markdownlint-cli2-action/releases">DavidAnson/markdownlint-cli2-action's
releases</a>.</em></p>
<blockquote>
<h2>Update markdownlint version (markdownlint-cli2 v0.14.0, markdownlint
v0.35.0).</h2>
<p>No release notes provided.</p>
<h2>Update markdownlint version (markdownlint-cli2 v0.13.0, markdownlint
v0.34.0).</h2>
<p>No release notes provided.</p>
<p>Update markdownlint version (markdownlint-cli2 v0.12.1, markdownlint
v0.33.0).</p>
<h2>Update markdownlint version (markdownlint-cli2 v0.11.0, markdownlint
v0.32.1), remove deprecated &quot;command&quot; input.</h2>
<p>No release notes provided.</p>
<h2>Update markdownlint version (markdownlint-cli2 v0.10.0, markdownlint
v0.31.1).</h2>
<p>No release notes provided.</p>
<h2>Update markdownlint version (markdownlint-cli2 v0.9.2, markdownlint
v0.30.0).</h2>
<p>No release notes provided.</p>
<h2>Update markdownlint version (markdownlint-cli2 v0.8.1, markdownlint
v0.29.0), add &quot;config&quot; and &quot;fix&quot; inputs, deprecate
&quot;command&quot; input.</h2>
<p>No release notes provided.</p>
<h2>Update markdownlint version (markdownlint-cli2 v0.7.1, markdownlint
v0.28.2).</h2>
<p>No release notes provided.</p>
<h2>Update markdownlint version (markdownlint-cli2 v0.7.0, markdownlint
v0.28.1), include link to rule information in title of annotations
(clickable in GitHub).</h2>
<p>No release notes provided.</p>
<h2>Update markdownlint version (markdownlint-cli2 v0.6.0, markdownlint
v0.27.0).</h2>
<p>No release notes provided.</p>
<h2>Update markdownlint version (markdownlint-cli2 v0.5.1, markdownlint
v0.26.2).</h2>
<p>No release notes provided.</p>
<h2>Update markdownlint version (markdownlint-cli2 v0.4.0, markdownlint
v0.25.1)</h2>
<p>No release notes provided.</p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="db43aef879"><code>db43aef</code></a>
Update to version 17.0.0.</li>
<li><a
href="c0decc52d0"><code>c0decc5</code></a>
Bump <code>@​stylistic/eslint-plugin</code> from 2.7.2 to 2.8.0</li>
<li><a
href="dd2171bb17"><code>dd2171b</code></a>
Bump eslint from 9.9.1 to 9.10.0</li>
<li><a
href="85b2286968"><code>85b2286</code></a>
Bump <code>@​eslint/js</code> from 9.9.1 to 9.10.0</li>
<li><a
href="95aa6ed6ed"><code>95aa6ed</code></a>
Freshen generated index.js file.</li>
<li><a
href="476aead54e"><code>476aead</code></a>
Bump markdownlint-cli2 from 0.13.0 to 0.14.0</li>
<li><a
href="da0291d977"><code>da0291d</code></a>
Freshen generated index.js file.</li>
<li><a
href="235535bdb7"><code>235535b</code></a>
Add <code>@​stylistic/eslint-plugin</code> to ESLint configuration.</li>
<li><a
href="20384985f1"><code>2038498</code></a>
Bump eslint from 9.9.0 to 9.9.1</li>
<li><a
href="ea9d2c1e2e"><code>ea9d2c1</code></a>
Bump <code>@​eslint/js</code> from 9.9.0 to 9.9.1</li>
<li>Additional commits viewable in <a
href="https://github.com/davidanson/markdownlint-cli2-action/compare/v16...v17">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=DavidAnson/markdownlint-cli2-action&package-manager=github_actions&previous-version=16&new-version=17)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-16 09:24:38 -07:00
Josh McKinney
b88717b65f docs(constraint): add note about percentages (#1368)
Co-authored-by: Orhun Parmaksız <orhun@archlinux.org>
2024-09-13 14:42:52 +03:00
Vitalii Kryvenko
5635b930c7 ci: add cargo-machete and remove unused deps (#1362)
https://github.com/bnjbvr/cargo-machete
2024-09-08 18:54:33 -07:00
Hossein Nedaee
870bc6a64a docs: use Frame::area() instead of size() in examples (#1361)
`Frame::size()` is deprecated
2024-09-08 13:20:59 -07:00
FujiApple
da821b431e fix: clippy lints from rust 1.81.0 (#1356) 2024-09-06 22:54:36 -07:00
Patryk Wychowaniec
68886d1787 fix: add unstable-backend-writer feature (#1352)
https://github.com/ratatui/ratatui/pull/991 created a new unstable
feature, but forgot to add it to Cargo.toml, making it impossible to use
on newer versions of rustc - this commit fixes it.
2024-09-06 15:33:14 -07:00
Patryk Wychowaniec
0f48239778 fix(terminal): resize() now resizes fixed viewports (#1353)
`Terminal::resize()` on a fixed viewport used to do nothing due to
an accidentally shadowed variable. This now works as intended.
2024-09-04 12:28:30 -07:00
Valentin271
b13e2f9473 docs(backend): added link to stdio FAQ (#1349) 2024-09-01 15:30:13 -07:00
Orhun Parmaksız
c777beb658 chore(ci): bump git-cliff-action to v4 (#1350)
See: https://github.com/orhun/git-cliff-action/releases/tag/v4.0.0
2024-09-01 16:23:35 +02:00
Mo
20c88aaa5b refactor: Avoid unneeded allocations (#1345) 2024-08-26 13:59:30 -07:00
Orhun Parmaksız
e02947be61 style(example): update panic message in minimal template (#1344) 2024-08-25 04:45:34 -07:00
Orhun Parmaksız
3a90e2a761 chore(release): prepare for 0.28.1 (#1343)
🧀 

The current release steps in reference to #1337

- Bump version in `Cargo.toml`
- `git cliff -u -p CHANGELOG.md -t v0.28.1`
- Merge the PR
- `git tag v0.28.1`
- `git push origin v0.28.1`

We can probably automate away most of these with `release-plz` when it
fully supports `git-cliff`'s GitHub integration.
2024-08-25 12:23:26 +03:00
Orhun Parmaksız
65da535745 chore(ci): update release strategy (#1337)
closes #1232 

Now we can trigger point releases by pushing a tag (follow the
instructions in `RELEASE.md`). This will create a release with generated
changelog.

There is still a lack of automation (e.g. updating `CHANGELOG.md`), but
this PR is a good start towards improving that.
2024-08-25 11:02:29 +03:00
Tayfun Bocek
9ed85fd1dd docs(table): fix incorrect backticks in TableState docs (#1342) 2024-08-24 14:26:37 -07:00
Neal Fachan
aed60b9839 fix(terminal): Terminal::insert_before would crash when called while the viewport filled the screen (#1329)
Reimplement Terminal::insert_before. The previous implementation would
insert the new lines in chunks into the area between the top of the
screen and the top of the (new) viewport. If the viewport filled the
screen, there would be no area in which to insert lines, and the
function would crash.

The new implementation uses as much of the screen as it needs to, all
the way up to using the whole screen.

This commit:
- adds a scrollback buffer to the `TestBackend` so that tests can
inspect and assert the state of the scrollback buffer in addition to the
screen
- adds functions to `TestBackend` to assert the state of the scrollback
- adds and updates `TestBackend` tests to test the behavior of the
scrollback and the new asserting functions
- reimplements `Terminal::insert_before`, including adding two new
helper functions `Terminal::draw_lines` and `Terminal::scroll_up`.
- updates the documentation for `Terminal::insert_before` to clarify
some of the edge cases
- updates terminal tests to assert the state of the scrollback buffer
- adds a new test for the condition that causes the bug
- adds a conversion constructor `Cell::from(char)`

Fixes: https://github.com/ratatui/ratatui/issues/999
2024-08-23 15:27:54 -07:00
Josh McKinney
3631b34f53 docs(examples): add widget implementation example (#1147)
This new example documents the various ways to implement widgets in
Ratatui. It demonstrates how to implement the `Widget` trait on a type,
a reference, and a mutable reference. It also shows how to use the
`WidgetRef` trait to render boxed widgets.
2024-08-23 14:30:23 -07:00
Mo
0d5f3c091f test: Avoid unneeded allocations in assertions (#1335)
A vector can be compared to an array.
2024-08-22 09:14:16 -07:00
Josh McKinney
ed51c4b342 feat(terminal): Add ratatui::init() and restore() methods (#1289)
These are simple opinionated methods for creating a terminal that is
useful to use in most apps. The new init method creates a crossterm
backend writing to stdout, enables raw mode, enters the alternate
screen, and sets a panic handler that restores the terminal on panic.

A minimal hello world now looks a bit like:

```rust
use ratatui::{
    crossterm::event::{self, Event},
    text::Text,
    Frame,
};

fn main() {
    let mut terminal = ratatui::init();
    loop {
        terminal
            .draw(|frame: &mut Frame| frame.render_widget(Text::raw("Hello World!"), frame.area()))
            .expect("Failed to draw");
        if matches!(event::read().expect("failed to read event"), Event::Key(_)) {
            break;
        }
    }
    ratatui::restore();
}
```

A type alias `DefaultTerminal` is added to represent this terminal
type and to simplify any cases where applications need to pass this
terminal around. It is equivalent to:
`Terminal<CrosstermBackend<Stdout>>`

We also added `ratatui::try_init()` and `try_restore()`, for situations
where you might want to handle initialization errors yourself instead
of letting the panic handler fire and cleanup. Simple Apps should
prefer the `init` and `restore` functions over these functions.

Corresponding functions to allow passing a `TerminalOptions` with
a `Viewport` (e.g. inline, fixed) are also available
(`init_with_options`,
and `try_init_with_options`).

The existing code to create a backend and terminal will remain and
is not deprecated by this approach. This just provides a simple one
line initialization using the common options.

---------

Co-authored-by: Orhun Parmaksız <orhunparmaksiz@gmail.com>
2024-08-22 15:16:35 +03:00
Josh McKinney
23516bce76 chore: rename ratatui-org to ratatui (#1334)
All urls updated to point at https://github.com/ratatui

To update your repository remotes, you can run the following commands:

```shell
git remote set-url origin https://github.com/ratatui/ratatui
```
2024-08-21 11:35:08 -07:00
Matt Armstrong
6d1bd99544 docs: minor grammar fixes (#1330) 2024-08-17 16:17:42 -07:00
Neal Fachan
2fb0b8a741 fix: fix u16 overflow in Terminal::insert_before. (#1323)
If the amount of characters in the screen above the viewport was greater
than u16::MAX, a multiplication would overflow. The multiply was used to
compute the maximum chunk size. The fix is to just do the multiplication
as a usize and also do the subsequent division as a usize.

There is currently another outstanding issue that limits the amount of
characters that can be inserted when calling Terminal::insert_before to
u16::MAX. However, this bug can still occur even if the viewport and the
amount of characters being inserted are both less than u16::MAX, since
it's dependant on how large the screen is above the viewport.

Fixes #1322
2024-08-13 21:06:49 -07:00
Josh McKinney
0256269a7f build: simplify Windows build (#1317)
Termion is not supported on Windows, so we need to avoid building it.

Adds a conditional dependency to the Cargo.toml file to only include
termion when the target is not Windows. This allows contributors to
build using the `--all-features` flag on Windows rather than needing
to specify the features individually.
2024-08-13 10:09:46 -07:00
Lucas Pickering
fdd5d8c092 fix(text): remove trailing newline from single-line Display trait impl (#1320) 2024-08-11 20:30:36 -07:00
EdJoPaTo
8b624f5952 chore(maintainers): remove EdJoPaTo (#1314) 2024-08-11 20:27:11 -07:00
Josh McKinney
57d8b742e5 chore(ci): use cargo-docs-rs to lint docs (#1318) 2024-08-11 20:09:57 -07:00
Josh McKinney
d5477b50d5 docs(examples): use ratatui::crossterm in examples (#1315) 2024-08-10 17:43:13 -07:00
310 changed files with 25712 additions and 13872 deletions

2
.cargo/config.toml Normal file
View File

@@ -0,0 +1,2 @@
[alias]
xtask = "run --package xtask --"

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"

9
.github/CODEOWNERS vendored
View File

@@ -1,8 +1,11 @@
# See https://help.github.com/articles/about-codeowners/
# See <https://help.github.com/articles/about-codeowners/>
# for more info about CODEOWNERS file
# It uses the same pattern rule for gitignore file
# https://git-scm.com/docs/gitignore#_pattern_format
# <https://git-scm.com/docs/gitignore#_pattern_format>
# Maintainers
* @ratatui-org/maintainers
* @ratatui/maintainers

2
.github/FUNDING.yml vendored Normal file
View File

@@ -0,0 +1,2 @@
github: ratatui
open_collective: ratatui

View File

@@ -18,43 +18,22 @@ jobs:
PR_EVENT: event.json
steps:
- name: Download Benchmark Results
uses: actions/github-script@v7
uses: dawidd6/action-download-artifact@v7
with:
script: |
async function downloadArtifact(artifactName) {
let allArtifacts = await github.rest.actions.listWorkflowRunArtifacts({
owner: context.repo.owner,
repo: context.repo.repo,
run_id: context.payload.workflow_run.id,
});
let matchArtifact = allArtifacts.data.artifacts.filter((artifact) => {
return artifact.name == artifactName
})[0];
if (!matchArtifact) {
core.setFailed(`Failed to find artifact: ${artifactName}`);
}
let download = await github.rest.actions.downloadArtifact({
owner: context.repo.owner,
repo: context.repo.repo,
artifact_id: matchArtifact.id,
archive_format: 'zip',
});
let fs = require('fs');
fs.writeFileSync(`${process.env.GITHUB_WORKSPACE}/${artifactName}.zip`, Buffer.from(download.data));
}
await downloadArtifact(process.env.BENCHMARK_RESULTS);
await downloadArtifact(process.env.PR_EVENT);
- name: Unzip Benchmark Results
run: |
unzip $BENCHMARK_RESULTS.zip
unzip $PR_EVENT.zip
name: ${{ env.BENCHMARK_RESULTS }}
run_id: ${{ github.event.workflow_run.id }}
- name: Download PR Event
uses: dawidd6/action-download-artifact@v7
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.number}/merge`);
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);
@@ -64,12 +43,14 @@ jobs:
bencher run \
--project ratatui-org \
--token '${{ secrets.BENCHER_API_TOKEN }}' \
--branch '${{ env.PR_HEAD }}' \
--branch-start-point '${{ env.PR_BASE }}' \
--branch-start-point-hash '${{ env.PR_BASE_SHA }}' \
--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 '${{ env.PR_NUMBER }}' \
--file "$BENCHMARK_RESULTS"
--ci-number "$PR_NUMBER" \
--file "$BENCHMARK_RESULTS"

View File

@@ -9,7 +9,6 @@ on:
pull_request:
branches:
- main
merge_group:
# ensure that the workflow is only triggered once per PR, subsequent pushes to the PR will cancel
# and restart the workflow. See https://docs.github.com/en/actions/using-jobs/using-concurrency
@@ -17,82 +16,95 @@ concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
env:
# don't install husky hooks during CI as they are only needed for for pre-push
CARGO_HUSKY_DONT_INSTALL_HOOKS: true
# lint, clippy and coveraget jobs are intentionally early in the workflow to catch simple
# formatting, typos, and missing tests as early as possible. This allows us to fix these and
# resubmit the PR without having to wait for the comprehensive matrix of tests to complete.
# lint, clippy and coverage jobs are intentionally early in the workflow to catch simple formatting,
# typos, and missing tests as early as possible. This allows us to fix these and resubmit the PR
# without having to wait for the comprehensive matrix of tests to complete.
jobs:
rustfmt:
# Lint the formatting of the codebase.
lint-formatting:
name: Check Formatting
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Rust nightly
uses: dtolnay/rust-toolchain@nightly
- uses: dtolnay/rust-toolchain@nightly
with: { components: rustfmt }
- uses: Swatinem/rust-cache@v2
- uses: taiki-e/install-action@v2
with:
components: rustfmt
- run: cargo +nightly fmt --all --check
tool: taplo-cli
- run: cargo xtask format --check
typos:
# Check for typos in the codebase.
# See <https://github.com/crate-ci/typos/>
lint-typos:
name: Check Typos
runs-on: ubuntu-latest
steps:
- 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>
dependencies:
name: Check Dependencies
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: EmbarkStudios/cargo-deny-action@v2
clippy:
# Check for any unused dependencies in the codebase.
# See <https://github.com/bnjbvr/cargo-machete/>
cargo-machete:
name: Check Unused Dependencies
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Rust stable
uses: dtolnay/rust-toolchain@stable
with:
components: clippy
- name: Install cargo-make
uses: taiki-e/install-action@cargo-make
- uses: Swatinem/rust-cache@v2
- run: cargo make clippy
- uses: bnjbvr/cargo-machete@v0.7.0
markdownlint:
# Run cargo clippy.
lint-clippy:
name: Check Clippy
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Lint markdown
uses: DavidAnson/markdownlint-cli2-action@v16
- uses: dtolnay/rust-toolchain@stable
with: { components: clippy }
- uses: Swatinem/rust-cache@v2
- run: cargo xtask clippy
# Run markdownlint on all markdown files in the repository.
lint-markdown:
name: Check Markdown
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: DavidAnson/markdownlint-cli2-action@v18
with:
globs: |
'**/*.md'
'!target'
# Run cargo coverage. This will generate a coverage report and upload it to codecov.
# <https://app.codecov.io/gh/ratatui/ratatui>
coverage:
name: Coverage Report
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Rust stable
uses: dtolnay/rust-toolchain@stable
- uses: dtolnay/rust-toolchain@stable
with:
components: llvm-tools
- name: Install cargo-llvm-cov and cargo-make
uses: taiki-e/install-action@v2
with:
tool: cargo-llvm-cov,cargo-make
- uses: taiki-e/install-action@cargo-llvm-cov
- uses: Swatinem/rust-cache@v2
- name: Generate coverage
run: cargo make coverage
- name: Upload to codecov.io
uses: codecov/codecov-action@v4
- run: cargo xtask coverage
- uses: codecov/codecov-action@v5
with:
token: ${{ secrets.CODECOV_TOKEN }}
fail_ci_if_error: true
# Run cargo check. This is a fast way to catch any obvious errors in the code.
check:
name: Check ${{ matrix.os }} ${{ matrix.toolchain }}
strategy:
fail-fast: false
matrix:
@@ -101,69 +113,76 @@ jobs:
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- name: Install Rust {{ matrix.toolchain }}
uses: dtolnay/rust-toolchain@master
- uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ matrix.toolchain }}
- name: Install cargo-make
uses: taiki-e/install-action@cargo-make
- uses: Swatinem/rust-cache@v2
- run: cargo make check
env:
RUST_BACKTRACE: full
- run: cargo xtask check --all-features
lint-docs:
# 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@v4
- name: Install Rust nightly
uses: dtolnay/rust-toolchain@nightly
- name: Install cargo-make
uses: taiki-e/install-action@cargo-make
- uses: Swatinem/rust-cache@v2
- run: cargo make lint-docs
- uses: taiki-e/install-action@cargo-rdme
- run: cargo xtask readme --check
test-doc:
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
runs-on: ${{ matrix.os }}
# 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
lint-docs:
name: Check Docs
runs-on: ubuntu-latest
env:
RUSTDOCFLAGS: -Dwarnings
steps:
- uses: actions/checkout@v4
- name: Install Rust stable
uses: dtolnay/rust-toolchain@stable
- name: Install cargo-make
uses: taiki-e/install-action@cargo-make
- uses: dtolnay/rust-toolchain@nightly
- uses: dtolnay/install@cargo-docs-rs
- uses: Swatinem/rust-cache@v2
- name: Test docs
run: cargo make test-doc
env:
RUST_BACKTRACE: full
- run: cargo xtask docs
test:
# 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.
test-docs:
name: Test Docs
runs-on: ubuntu-latest
steps:
- 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.
test-libs:
name: Test Libs ${{ matrix.toolchain }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
toolchain: ["1.74.0", "stable"]
steps:
- 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.
test-backends:
name: Test ${{matrix.backend}} on ${{ matrix.os }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
toolchain: ["1.74.0", "stable"]
backend: [crossterm, termion, termwiz]
exclude:
# termion is not supported on windows
- os: windows-latest
backend: termion
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- name: Install Rust ${{ matrix.toolchain }}}
uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ matrix.toolchain }}
- name: Install cargo-make
uses: taiki-e/install-action@cargo-make
- name: Install cargo-nextest
uses: taiki-e/install-action@nextest
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
- run: cargo make test-backend ${{ matrix.backend }}
env:
RUST_BACKTRACE: full
- run: cargo xtask test-backend ${{ matrix.backend }}

View File

@@ -1,4 +1,4 @@
name: Continuous Deployment
name: Release alpha version
on:
workflow_dispatch:
@@ -6,9 +6,6 @@ on:
# At 00:00 on Saturday
# https://crontab.guru/#0_0_*_*_6
- cron: "0 0 * * 6"
push:
tags:
- "v*.*.*"
defaults:
run:
@@ -20,7 +17,6 @@ jobs:
runs-on: ubuntu-latest
permissions:
contents: write
if: ${{ !startsWith(github.event.ref, 'refs/tags/v') }}
steps:
- name: Checkout the repository
uses: actions/checkout@v4
@@ -30,14 +26,14 @@ jobs:
- name: Calculate the next release
run: .github/workflows/calculate-alpha-release.bash
- name: Publish on crates.io
uses: actions-rs/cargo@v1
with:
command: publish
args: --allow-dirty --token ${{ secrets.CARGO_TOKEN }}
- 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@v3
uses: orhun/git-cliff-action@v4
with:
config: cliff.toml
args: --unreleased --tag ${{ env.NEXT_TAG }} --strip header
@@ -50,17 +46,3 @@ jobs:
tag: ${{ env.NEXT_TAG }}
prerelease: true
bodyFile: BODY.md
publish-stable:
name: Create a stable release
runs-on: ubuntu-latest
if: ${{ startsWith(github.event.ref, 'refs/tags/v') }}
steps:
- name: Checkout the repository
uses: actions/checkout@v4
- name: Publish on crates.io
uses: actions-rs/cargo@v1
with:
command: publish
args: --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 }}

1
.gitignore vendored
View File

@@ -1,5 +1,4 @@
target
Cargo.lock
*.log
*.rs.rustfmt
.gdb_history

12
.taplo.toml Normal file
View File

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

@@ -4,14 +4,24 @@ This document contains a list of breaking changes in each version and some notes
between versions. It is compiled manually from the commit history and changelog. We also tag PRs on
GitHub with a [breaking change] label.
[breaking change]: (https://github.com/ratatui-org/ratatui/issues?q=label%3A%22breaking+change%22)
[breaking change]: (https://github.com/ratatui/ratatui/issues?q=label%3A%22breaking+change%22)
## Summary
This is a quick summary of the sections below:
- [Unreleased](#unreleased)
- The `From` impls for backend types are now replaced with more specific traits
- `FrameExt` trait for `unstable-widget-ref` feature
- [v0.29.0](#v0290)
- `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`
- `Tabs::select` now accepts `Into<Option<usize>>`
- `Color::from_hsl` is now behind the `palette` feature
- [v0.28.0](#v0280)
`Backend::size` returns `Size` instead of `Rect`
- `Backend::size` returns `Size` instead of `Rect`
- `Backend` trait migrates to `get/set_cursor_position`
- Ratatui now requires Crossterm 0.28.0
- `Axis::labels` now accepts `IntoIterator<Into<Line>>`
@@ -65,18 +75,222 @@ This is a quick summary of the sections below:
- MSRV is now 1.63.0
- `List` no longer ignores empty strings
## v0.28.0
## Unreleased (0.30.0)
### `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);
```
### `WidgetRef` no longer has a blanket implementation of Widget
Previously there was a blanket implementation of Widget for WidgetRef. This has been reversed to
instead be a blanket implementation of WidgetRef for all &W where W: Widget. Any widgets that
previously implemented WidgetRef directly should now instead implement Widget for a reference to the
type.
```diff
-impl WidgetRef for Foo {
- fn render_ref(&self, area: Rect, buf: &mut Buffer)
+impl Widget for &Foo {
+ fn render(self, area: Rect, buf: &mut Buffer)
}
```
### The `From` impls for backend types are now replaced with more specific traits [#1464]
[#1464]: https://github.com/ratatui/ratatui/pull/1464
Crossterm gains `ratatui::backend::crossterm::{FromCrossterm, IntoCrossterm}`
Termwiz gains `ratatui::backend::termwiz::{FromTermwiz, IntoTermwiz}`
This is necessary in order to avoid the orphan rule when implementing `From` for crossterm types
once the crossterm types are moved to a separate crate.
```diff
+ use ratatui::backend::crossterm::{FromCrossterm, IntoCrossterm};
let crossterm_color = crossterm::style::Color::Black;
- let ratatui_color = crossterm_color.into();
- let ratatui_color = ratatui::style::Color::from(crossterm_color);
+ let ratatui_color = ratatui::style::Color::from_crossterm(crossterm_color);
- let crossterm_color = ratatui_color.into();
- let crossterm_color = crossterm::style::Color::from(ratatui_color);
+ let crossterm_color = ratatui_color.into_crossterm();
let crossterm_attribute = crossterm::style::types::Attribute::Bold;
- let ratatui_modifier = crossterm_attribute.into();
- let ratatui_modifier = ratatui::style::Modifier::from(crossterm_attribute);
+ let ratatui_modifier = ratatui::style::Modifier::from_crossterm(crossterm_attribute);
- let crossterm_attribute = ratatui_modifier.into();
- let crossterm_attribute = crossterm::style::types::Attribute::from(ratatui_modifier);
+ let crossterm_attribute = ratatui_modifier.into_crossterm();
```
Similar conversions for `ContentStyle` -> `Style` and `Attributes` -> `Modifier` exist for
Crossterm and the various Termion and Termwiz types as well.
### `Bar::label()` and `BarGroup::label()` now accepts `Into<Line<'a>>`. ([#1471])
[#1471]: https://github.com/ratatui/ratatui/pull/1471
Previously `Bar::label()` and `BarGroup::label()` accepted `Line<'a>`, but they now accepts `Into<Line<'a>>`.
for `Bar::label()`:
```diff
- Bar::default().label("foo".into());
+ Bar::default().label("foo");
```
for `BarGroup::label()`:
```diff
- BarGroup::default().label("bar".into());
+ BarGroup::default().label("bar");
```
### `Bar::text_value` now accepts `Into<String>` ([#1471])
Previously `Bar::text_value` accepted `String`, but now it accepts `Into<String>`.
for `Bar::text_value()`:
```diff
- Bar::default().text_value("foobar".into());
+ Bar::default().text_value("foobar");
```
## [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])
[#1326]: https://github.com/ratatui/ratatui/pull/1326
The `Sparkline::data` method has been modified to accept `IntoIterator<Item = SparklineBar>`
instead of `&[u64]`.
`SparklineBar` is a struct that contains an `Option<u64>` value, which represents an possible
_absent_ value, as distinct from a `0` value. This change allows the `Sparkline` to style
data points differently, depending on whether they are present or absent.
`SparklineBar` also contains an `Option<Style>` that will be used to apply a style the bar in
addition to any other styling applied to the `Sparkline`.
Several `From` implementations have been added to `SparklineBar` to support existing callers who
provide `&[u64]` and other types that can be converted to `SparklineBar`, such as `Option<u64>`.
If you encounter any type inference issues, you may need to provide an explicit type for the data
passed to `Sparkline::data`. For example, if you are passing a single value, you may need to use
`into()` to convert it to form that can be used as a `SparklineBar`:
```diff
let value = 1u8;
- Sparkline::default().data(&[value.into()]);
+ Sparkline::default().data(&[u64::from(value)]);
```
As a consequence of this change, the `data` method is no longer a `const fn`.
### `Color::from_hsl` is now behind the `palette` feature and accepts `palette::Hsl` ([#1418])
[#1418]: https://github.com/ratatui/ratatui/pull/1418
Previously `Color::from_hsl` accepted components as individual f64 parameters. It now accepts a
single `palette::Hsl` value and is gated behind a `palette` feature flag.
```diff
- Color::from_hsl(360.0, 100.0, 100.0)
+ Color::from_hsl(Hsl::new(360.0, 100.0, 100.0))
```
### Removed public fields from `Rect` iterators ([#1358], [#1424])
[#1358]: https://github.com/ratatui/ratatui/pull/1358
[#1424]: https://github.com/ratatui/ratatui/pull/1424
The `pub` modifier has been removed from fields on the `Columns`,`Rows`, and `Positions` iterators.
These fields were not intended to be public and should not have been accessed directly.
### `Rect::area()` now returns u32 instead of u16 ([#1378])
[#1378]: https://github.com/ratatui/ratatui/pull/1378
This is likely to impact anything which relies on `Rect::area` maxing out at u16::MAX. It can now
return up to u16::MAX * u16::MAX (2^32 - 2^17 + 1).
### `Line` now implements `From<Cow<str>` ([#1373])
[#1373]: https://github.com/ratatui/ratatui/pull/1373
As this adds an extra conversion, ambiguous inferred expressions may no longer compile.
```rust
// given:
struct Foo { ... }
impl From<Foo> for String { ... }
impl From<Foo> for Cow<str> { ... }
let foo = Foo { ... };
let line = Line::from(foo); // now fails due to now ambiguous inferred type
// replace with e.g.
let line = Line::from(String::from(foo));
```
### `Tabs::select()` now accepts `Into<Option<usize>>` ([#1413])
[#1413]: https://github.com/ratatui/ratatui/pull/1413
Previously `Tabs::select()` accepted `usize`, but it now accepts `Into<Option<usize>>`. This breaks
any code already using parameter type inference:
```diff
let selected = 1u8;
- let tabs = Tabs::new(["A", "B"]).select(selected.into())
+ let tabs = Tabs::new(["A", "B"]).select(selected as usize)
```
### `Table::highlight_style` is now `Table::row_highlight_style` ([#1331])
[#1331]: https://github.com/ratatui/ratatui/pull/1331
The `Table::highlight_style` is now deprecated in favor of `Table::row_highlight_style`.
Also, the serialized output of the `TableState` will now include the "selected_column" field.
Software that manually parse the serialized the output (with anything other than the `Serialize`
implementation on `TableState`) may have to be refactored if the "selected_column" field is not
accounted for. This does not affect users who rely on the `Deserialize`, or `Serialize`
implementation on the state.
## [v0.28.0](https://github.com/ratatui/ratatui/releases/tag/v0.28.0)
### `Backend::size` returns `Size` instead of `Rect` ([#1254])
[#1254]: https://github.com/ratatui-org/ratatui/pull/1254
[#1254]: https://github.com/ratatui/ratatui/pull/1254
The `Backend::size` method returns a `Size` instead of a `Rect`.
There is no need for the position here as it was always 0,0.
### `Backend` trait migrates to `get/set_cursor_position` ([#1284])
[#1284]: https://github.com/ratatui-org/ratatui/pull/1284
[#1284]: https://github.com/ratatui/ratatui/pull/1284
If you just use the types implementing the `Backend` trait, you will see deprecation hints but
nothing is a breaking change for you.
@@ -87,7 +301,7 @@ and a default implementation for them exists.
### Ratatui now requires Crossterm 0.28.0 ([#1278])
[#1278]: https://github.com/ratatui-org/ratatui/pull/1278
[#1278]: https://github.com/ratatui/ratatui/pull/1278
Crossterm is updated to version 0.28.0, which is a semver incompatible version with the previous
version (0.27.0). Ratatui re-exports the version of crossterm that it is compatible with under
@@ -95,8 +309,8 @@ version (0.27.0). Ratatui re-exports the version of crossterm that it is compati
### `Axis::labels()` now accepts `IntoIterator<Into<Line>>` ([#1273] and [#1283])
[#1273]: https://github.com/ratatui-org/ratatui/pull/1173
[#1283]: https://github.com/ratatui-org/ratatui/pull/1283
[#1273]: https://github.com/ratatui/ratatui/pull/1173
[#1283]: https://github.com/ratatui/ratatui/pull/1283
Previously Axis::labels accepted `Vec<Span>`. Any code that uses conversion methods that infer the
type will need to be rewritten as the compiler cannot infer the correct type.
@@ -108,7 +322,7 @@ type will need to be rewritten as the compiler cannot infer the correct type.
### `Layout::init_cache` no longer returns bool and takes a `NonZeroUsize` instead of `usize` ([#1245])
[#1245]: https://github.com/ratatui-org/ratatui/pull/1245
[#1245]: https://github.com/ratatui/ratatui/pull/1245
```diff
- let is_initialized = Layout::init_cache(100);
@@ -117,7 +331,7 @@ type will need to be rewritten as the compiler cannot infer the correct type.
### `ratatui::terminal` module is now private ([#1160])
[#1160]: https://github.com/ratatui-org/ratatui/pull/1160
[#1160]: https://github.com/ratatui/ratatui/pull/1160
The `terminal` module is now private and can not be used directly. The types under this module are
exported from the root of the crate. This reduces clashes with other modules in the backends that
@@ -130,21 +344,21 @@ are also named terminal, and confusion about module exports for newer Rust users
### `ToText` no longer has a lifetime ([#1234])
[#1234]: https://github.com/ratatui-org/ratatui/pull/1234
[#1234]: https://github.com/ratatui/ratatui/pull/1234
This change simplifies the trait and makes it easier to implement.
### `Frame::size` is deprecated and renamed to `Frame::area`
### `Frame::size` is deprecated and renamed to `Frame::area` ([#1293])
[#1293]: https://github.com/ratatui-org/ratatui/pull/1293
[#1293]: https://github.com/ratatui/ratatui/pull/1293
`Frame::size` is renamed to `Frame::area` as its the more correct name.
`Frame::size` is renamed to `Frame::area` as it's the more correct name.
## [v0.27.0](https://github.com/ratatui-org/ratatui/releases/tag/v0.27.0)
## [v0.27.0](https://github.com/ratatui/ratatui/releases/tag/v0.27.0)
### List no clamps the selected index to list ([#1159])
[#1149]: https://github.com/ratatui-org/ratatui/pull/1149
[#1149]: https://github.com/ratatui/ratatui/pull/1149
The `List` widget now clamps the selected index to the bounds of the list when navigating with
`first`, `last`, `previous`, and `next`, as well as when setting the index directly with `select`.
@@ -205,11 +419,11 @@ A change is only necessary if you were matching on all variants of the `MouseEve
wildcard. In this case, you need to either handle the two new variants, `MouseLeft` and
`MouseRight`, or add a wildcard.
[#1106]: https://github.com/ratatui-org/ratatui/pull/1106
[#1106]: https://github.com/ratatui/ratatui/pull/1106
### `Rect::inner` takes `Margin` directly instead of reference ([#1008])
[#1008]: https://github.com/ratatui-org/ratatui/pull/1008
[#1008]: https://github.com/ratatui/ratatui/pull/1008
`Margin` needs to be passed without reference now.
@@ -223,7 +437,7 @@ wildcard. In this case, you need to either handle the two new variants, `MouseLe
### `Buffer::filled` takes `Cell` directly instead of reference ([#1148])
[#1148]: https://github.com/ratatui-org/ratatui/pull/1148
[#1148]: https://github.com/ratatui/ratatui/pull/1148
`Buffer::filled` moves the `Cell` instead of taking a reference.
@@ -234,14 +448,14 @@ wildcard. In this case, you need to either handle the two new variants, `MouseLe
### `Stylize::bg()` now accepts `Into<Color>` ([#1103])
[#1103]: https://github.com/ratatui-org/ratatui/pull/1103
[#1103]: https://github.com/ratatui/ratatui/pull/1103
Previously, `Stylize::bg()` accepted `Color` but now accepts `Into<Color>`. This allows more
flexible types from calling scopes, though it can break some type inference in the calling scope.
### Remove deprecated `List::start_corner` and `layout::Corner` ([#759])
[#759]: https://github.com/ratatui-org/ratatui/pull/759
[#759]: https://github.com/ratatui/ratatui/pull/759
`List::start_corner` was deprecated in v0.25. Use `List::direction` and `ListDirection` instead.
@@ -264,7 +478,7 @@ flexible types from calling scopes, though it can break some type inference in t
### `LineGauge::gauge_style` is deprecated ([#565])
[#565]: https://github.com/ratatui-org/ratatui/pull/1148
[#565]: https://github.com/ratatui/ratatui/pull/1148
`LineGauge::gauge_style` is deprecated and replaced with `LineGauge::filled_style` and `LineGauge::unfilled_style`:
@@ -275,11 +489,11 @@ let gauge = LineGauge::default()
+ .unfilled_style(Style::default().fg(Color::White));
```
## [v0.26.0](https://github.com/ratatui-org/ratatui/releases/tag/v0.26.0)
## [v0.26.0](https://github.com/ratatui/ratatui/releases/tag/v0.26.0)
### `Flex::Start` is the new default flex mode for `Layout` ([#881])
[#881]: https://github.com/ratatui-org/ratatui/pull/881
[#881]: https://github.com/ratatui/ratatui/pull/881
Previously, constraints would stretch to fill all available space, violating constraints if
necessary.
@@ -300,7 +514,7 @@ existing layouts with `Flex::Start`. However, to get old behavior, use `Flex::Le
### `Table::new()` now accepts `IntoIterator<Item: Into<Row<'a>>>` ([#774])
[#774]: https://github.com/ratatui-org/ratatui/pull/774
[#774]: https://github.com/ratatui/ratatui/pull/774
Previously, `Table::new()` accepted `IntoIterator<Item=Row<'a>>`. The argument change to
`IntoIterator<Item: Into<Row<'a>>>`, This allows more flexible types from calling scopes, though it
@@ -317,7 +531,7 @@ This can be resolved either by providing an explicit type (e.g. `Vec::<Row>::new
### `Tabs::new()` now accepts `IntoIterator<Item: Into<Line<'a>>>` ([#776])
[#776]: https://github.com/ratatui-org/ratatui/pull/776
[#776]: https://github.com/ratatui/ratatui/pull/776
Previously, `Tabs::new()` accepted `Vec<T>` where `T: Into<Line<'a>>`. This allows more flexible
types from calling scopes, though it can break some type inference in the calling scope.
@@ -333,7 +547,7 @@ by removing the call to `.collect()`.
### Table::default() now sets segment_size to None and column_spacing to ([#751])
[#751]: https://github.com/ratatui-org/ratatui/pull/751
[#751]: https://github.com/ratatui/ratatui/pull/751
The default() implementation of Table now sets the column_spacing field to 1 and the segment_size
field to `SegmentSize::None`. This will affect the rendering of a small amount of apps.
@@ -343,7 +557,7 @@ To use the previous default values, call `table.segment_size(Default::default())
### `patch_style` & `reset_style` now consumes and returns `Self` ([#754])
[#754]: https://github.com/ratatui-org/ratatui/pull/754
[#754]: https://github.com/ratatui/ratatui/pull/754
Previously, `patch_style` and `reset_style` in `Text`, `Line` and `Span` were using a mutable
reference to `Self`. To be more consistent with the rest of `ratatui`, which is using fluent
@@ -369,7 +583,7 @@ The following example shows how to migrate for `Line`, but the same applies for
### `Block` style methods cannot be used in a const context ([#720])
[#720]: https://github.com/ratatui-org/ratatui/pull/720
[#720]: https://github.com/ratatui/ratatui/pull/720
Previously the `style()`, `border_style()` and `title_style()` methods could be used to create a
`Block` in a constant context. These now accept `Into<Style>` instead of `Style`. These methods no
@@ -377,7 +591,7 @@ longer can be called from a constant context.
### `Line` now has a `style` field that applies to the entire line ([#708])
[#708]: https://github.com/ratatui-org/ratatui/pull/708
[#708]: https://github.com/ratatui/ratatui/pull/708
Previously the style of a `Line` was stored in the `Span`s that make up the line. Now the `Line`
itself has a `style` field, which can be set with the `Line::styled` method. Any code that creates
@@ -401,11 +615,11 @@ the `Span::style` field.
.alignment(Alignment::Left);
```
## [v0.25.0](https://github.com/ratatui-org/ratatui/releases/tag/v0.25.0)
## [v0.25.0](https://github.com/ratatui/ratatui/releases/tag/v0.25.0)
### Removed `Axis::title_style` and `Buffer::set_background` ([#691])
[#691]: https://github.com/ratatui-org/ratatui/pull/691
[#691]: https://github.com/ratatui/ratatui/pull/691
These items were deprecated since 0.10.
@@ -419,7 +633,7 @@ These items were deprecated since 0.10.
### `List::new()` now accepts `IntoIterator<Item = Into<ListItem<'a>>>` ([#672])
[#672]: https://github.com/ratatui-org/ratatui/pull/672
[#672]: https://github.com/ratatui/ratatui/pull/672
Previously `List::new()` took `Into<Vec<ListItem<'a>>>`. This change will throw a compilation
error for `IntoIterator`s with an indeterminate item (e.g. empty vecs).
@@ -434,7 +648,7 @@ E.g.
### The default `Tabs::highlight_style` is now `Style::new().reversed()` ([#635])
[#635]: https://github.com/ratatui-org/ratatui/pull/635
[#635]: https://github.com/ratatui/ratatui/pull/635
Previously the default highlight style for tabs was `Style::default()`, which meant that a `Tabs`
widget in the default configuration would not show any indication of the selected tab.
@@ -446,7 +660,7 @@ widget in the default configuration would not show any indication of the selecte
### `Table::new()` now requires specifying the widths of the columns ([#664])
[#664]: https://github.com/ratatui-org/ratatui/pull/664
[#664]: https://github.com/ratatui/ratatui/pull/664
Previously `Table`s could be constructed without `widths`. In almost all cases this is an error.
A new `widths` parameter is now mandatory on `Table::new()`. Existing code of the form:
@@ -472,7 +686,7 @@ or complex, it may be convenient to replace `Table::new` with `Table::default().
### `Table::widths()` now accepts `IntoIterator<Item = AsRef<Constraint>>` ([#663])
[#663]: https://github.com/ratatui-org/ratatui/pull/663
[#663]: https://github.com/ratatui/ratatui/pull/663
Previously `Table::widths()` took a slice (`&'a [Constraint]`). This change will introduce clippy
`needless_borrow` warnings for places where slices are passed to this method. To fix these, remove
@@ -488,7 +702,7 @@ E.g.
### Layout::new() now accepts direction and constraint parameters ([#557])
[#557]: https://github.com/ratatui-org/ratatui/pull/557
[#557]: https://github.com/ratatui/ratatui/pull/557
Previously layout new took no parameters. Existing code should either use `Layout::default()` or
the new constructor.
@@ -505,18 +719,18 @@ let layout = layout::default()
let layout = layout::new(Direction::Vertical, [Constraint::Min(1), Constraint::Max(2)]);
```
## [v0.24.0](https://github.com/ratatui-org/ratatui/releases/tag/v0.24.0)
## [v0.24.0](https://github.com/ratatui/ratatui/releases/tag/v0.24.0)
### `ScrollbarState` field type changed from `u16` to `usize` ([#456])
[#456]: https://github.com/ratatui-org/ratatui/pull/456
[#456]: https://github.com/ratatui/ratatui/pull/456
In order to support larger content lengths, the `position`, `content_length` and
`viewport_content_length` methods on `ScrollbarState` now take `usize` instead of `u16`
### `BorderType::line_symbols` renamed to `border_symbols` ([#529])
[#529]: https://github.com/ratatui-org/ratatui/issues/529
[#529]: https://github.com/ratatui/ratatui/issues/529
Applications can now set custom borders on a `Block` by calling `border_set()`. The
`BorderType::line_symbols()` is renamed to `border_symbols()` and now returns a new struct
@@ -530,7 +744,7 @@ Applications can now set custom borders on a `Block` by calling `border_set()`.
### Generic `Backend` parameter removed from `Frame` ([#530])
[#530]: https://github.com/ratatui-org/ratatui/issues/530
[#530]: https://github.com/ratatui/ratatui/issues/530
`Frame` is no longer generic over Backend. Code that accepted `Frame<Backend>` will now need to
accept `Frame`. To migrate existing code, remove any generic parameters from code that uses an
@@ -544,7 +758,7 @@ instance of a Frame. E.g.:
### `Stylize` shorthands now consume rather than borrow `String` ([#466])
[#466]: https://github.com/ratatui-org/ratatui/issues/466
[#466]: https://github.com/ratatui/ratatui/issues/466
In order to support using `Stylize` shorthands (e.g. `"foo".red()`) on temporary `String` values, a
new implementation of `Stylize` was added that returns a `Span<'static>`. This causes the value to
@@ -562,7 +776,7 @@ longer compile. E.g.
### Deprecated `Spans` type removed (replaced with `Line`) ([#426])
[#426]: https://github.com/ratatui-org/ratatui/issues/426
[#426]: https://github.com/ratatui/ratatui/issues/426
`Spans` was replaced with `Line` in 0.21.0. `Buffer::set_spans` was replaced with
`Buffer::set_line`.
@@ -575,11 +789,11 @@ longer compile. E.g.
+ buffer.set_line(0, 0, line, 10);
```
## [v0.23.0](https://github.com/ratatui-org/ratatui/releases/tag/v0.23.0)
## [v0.23.0](https://github.com/ratatui/ratatui/releases/tag/v0.23.0)
### `Scrollbar::track_symbol()` now takes an `Option<&str>` instead of `&str` ([#360])
[#360]: https://github.com/ratatui-org/ratatui/issues/360
[#360]: https://github.com/ratatui/ratatui/issues/360
The track symbol of `Scrollbar` is now optional, this method now takes an optional value.
@@ -591,7 +805,7 @@ The track symbol of `Scrollbar` is now optional, this method now takes an option
### `Scrollbar` symbols moved to `symbols::scrollbar` and `widgets::scrollbar` module is private ([#330])
[#330]: https://github.com/ratatui-org/ratatui/issues/330
[#330]: https://github.com/ratatui/ratatui/issues/330
The symbols for defining scrollbars have been moved to the `symbols` module from the
`widgets::scrollbar` module which is no longer public. To update your code update any imports to the
@@ -605,31 +819,31 @@ new module locations. E.g.:
### MSRV updated to 1.67 ([#361])
[#361]: https://github.com/ratatui-org/ratatui/issues/361
[#361]: https://github.com/ratatui/ratatui/issues/361
The MSRV of ratatui is now 1.67 due to an MSRV update in a dependency (`time`).
## [v0.22.0](https://github.com/ratatui-org/ratatui/releases/tag/v0.22.0)
## [v0.22.0](https://github.com/ratatui/ratatui/releases/tag/v0.22.0)
### `bitflags` updated to 2.3 ([#205])
[#205]: https://github.com/ratatui-org/ratatui/issues/205
[#205]: https://github.com/ratatui/ratatui/issues/205
The `serde` representation of `bitflags` has changed. Any existing serialized types that have
Borders or Modifiers will need to be re-serialized. This is documented in the [`bitflags`
changelog](https://github.com/bitflags/bitflags/blob/main/CHANGELOG.md#200-rc2)..
## [v0.21.0](https://github.com/ratatui-org/ratatui/releases/tag/v0.21.0)
## [v0.21.0](https://github.com/ratatui/ratatui/releases/tag/v0.21.0)
### MSRV is 1.65.0 ([#171])
[#171]: https://github.com/ratatui-org/ratatui/issues/171
[#171]: https://github.com/ratatui/ratatui/issues/171
The minimum supported rust version is now 1.65.0.
### `Terminal::with_options()` stabilized to allow configuring the viewport ([#114])
[#114]: https://github.com/ratatui-org/ratatui/issues/114
[#114]: https://github.com/ratatui/ratatui/issues/114
In order to support inline viewports, the unstable method `Terminal::with_options()` was stabilized
and `ViewPort` was changed from a struct to an enum.
@@ -646,7 +860,7 @@ let terminal = Terminal::with_options(backend, TerminalOptions {
### Code that binds `Into<Text<'a>>` now requires type annotations ([#168])
[#168]: https://github.com/ratatui-org/ratatui/issues/168
[#168]: https://github.com/ratatui/ratatui/issues/168
A new type `Masked` was introduced that implements `From<Text<'a>>`. This causes any code that
previously did not need to use type annotations to fail to compile. To fix this, annotate or call
@@ -660,7 +874,7 @@ previously did not need to use type annotations to fail to compile. To fix this,
### `Marker::Block` now renders as a block rather than a bar character ([#133])
[#133]: https://github.com/ratatui-org/ratatui/issues/133
[#133]: https://github.com/ratatui/ratatui/issues/133
Code using the `Block` marker that previously rendered using a half block character (`'▀'``) now
renders using the full block character (`'█'`). A new marker variant`Bar` is introduced to replace
@@ -672,20 +886,20 @@ the existing code.
+ let canvas = Canvas::default().marker(Marker::Bar);
```
## [v0.20.0](https://github.com/ratatui-org/ratatui/releases/tag/v0.20.0)
## [v0.20.0](https://github.com/ratatui/ratatui/releases/tag/v0.20.0)
v0.20.0 was the first release of Ratatui - versions prior to this were release as tui-rs. See the
[Changelog](./CHANGELOG.md) for more details.
### MSRV is update to 1.63.0 ([#80])
[#80]: https://github.com/ratatui-org/ratatui/issues/80
[#80]: https://github.com/ratatui/ratatui/issues/80
The minimum supported rust version is 1.63.0
### List no longer ignores empty string in items ([#42])
[#42]: https://github.com/ratatui-org/ratatui/issues/42
[#42]: https://github.com/ratatui/ratatui/issues/42
The following code now renders 3 items instead of 2. Code which relies on the previous behavior will
need to manually filter empty items prior to display.

File diff suppressed because it is too large Load Diff

View File

@@ -8,7 +8,7 @@ creating a new issue before making the change, or starting a discussion on
## Reporting issues
Before reporting an issue on the [issue tracker](https://github.com/ratatui-org/ratatui/issues),
Before reporting an issue on the [issue tracker](https://github.com/ratatui/ratatui/issues),
please check that it has not already been reported by searching for some related keywords. Please
also check [`tui-rs` issues](https://github.com/fdehau/tui-rs/issues/) and link any related issues
found.
@@ -31,7 +31,7 @@ guarantee that the behavior is unchanged.
### Code formatting
Run `cargo make format` before committing to ensure that code is consistently formatted with
Run `cargo xtask format` before committing to ensure that code is consistently formatted with
rustfmt. Configuration is in [`rustfmt.toml`](./rustfmt.toml).
### Search `tui-rs` for similar work
@@ -56,7 +56,7 @@ documented.
### Run CI tests before pushing a PR
Running `cargo make ci` before pushing will perform the same checks that we do in the CI process.
Running `cargo xtask ci` before pushing will perform the same checks that we do in the CI process.
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.
@@ -71,22 +71,22 @@ in GitHub docs.
### Setup
Clone the repo and build it using [cargo-make](https://sagiegurari.github.io/cargo-make/)
TL;DR: Clone the repo and build it using `cargo xtask`.
Ratatui is an ordinary Rust project where common tasks are managed with
[cargo-make](https://github.com/sagiegurari/cargo-make/). It wraps common `cargo` commands with sane
[cargo-xtask](https://github.com/matklad/cargo-xtask). It wraps common `cargo` commands with sane
defaults depending on your platform of choice. Building the project should be as easy as running
`cargo make build`.
`cargo xtask build`.
```shell
git clone https://github.com/ratatui-org/ratatui.git
git clone https://github.com/ratatui/ratatui.git
cd ratatui
cargo make build
cargo xtask build
```
### Tests
The [test coverage](https://app.codecov.io/gh/ratatui-org/ratatui) of the crate is reasonably
The [test coverage](https://app.codecov.io/gh/ratatui/ratatui) of the crate is reasonably
good, but this can always be improved. Focus on keeping the tests simple and obvious and write unit
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
@@ -171,7 +171,7 @@ time to update. However, if a deprecation is blocking for us to implement a new
We don't currently use any unsafe code in Ratatui, and would like to keep it that way. However, there
may be specific cases that this becomes necessary in order to avoid slowness. Please see [this
discussion](https://github.com/ratatui-org/ratatui/discussions/66) for more about the decision.
discussion](https://github.com/ratatui/ratatui/discussions/66) for more about the decision.
## Continuous Integration
@@ -182,7 +182,7 @@ We use GitHub Actions for the CI where we perform the following checks:
- The code should conform to the default format enforced by `rustfmt`.
- The code should not contain common style issues `clippy`.
You can also check most of those things yourself locally using `cargo make ci` which will offer you
You can also check most of those things yourself locally using `cargo xtask ci` which will offer you
a shorter feedback loop than pushing to github.
## Relationship with `tui-rs`
@@ -196,7 +196,7 @@ it is useful to refer to when contributing code, documentation, or issues with R
We imported all the PRs from the original repository, implemented many of the smaller ones, and
made notes on the leftovers. These are marked as draft PRs and labelled as [imported from
tui](https://github.com/ratatui-org/ratatui/pulls?q=is%3Apr+is%3Aopen+label%3A%22imported+from+tui%22).
tui](https://github.com/ratatui/ratatui/pulls?q=is%3Apr+is%3Aopen+label%3A%22imported+from+tui%22).
We have documented the current state of those PRs, and anyone is welcome to pick them up and
continue the work on them.

4100
Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,80 +1,64 @@
[package]
name = "ratatui"
version = "0.28.0" # crate version
[workspace]
resolver = "2"
members = ["ratatui", "ratatui-*", "xtask", "examples/apps/*"]
default-members = [
"ratatui",
"ratatui-core",
"ratatui-crossterm",
# this is not included as it doesn't compile on windows
# "ratatui-termion",
"ratatui-termwiz",
"ratatui-widgets",
"examples/apps/*",
]
[workspace.package]
authors = ["Florian Dehau <work@fdehau.com>", "The Ratatui Developers"]
description = "A library that's all about cooking up terminal user interfaces"
documentation = "https://docs.rs/ratatui/latest/ratatui/"
repository = "https://github.com/ratatui-org/ratatui"
repository = "https://github.com/ratatui/ratatui"
homepage = "https://ratatui.rs"
keywords = ["tui", "terminal", "dashboard"]
categories = ["command-line-interface"]
readme = "README.md"
license = "MIT"
exclude = [
"assets/*",
".github",
"Makefile.toml",
"CONTRIBUTING.md",
"*.log",
"tags",
]
exclude = ["assets/*", ".github", "Makefile.toml", "CONTRIBUTING.md", "*.log", "tags"]
edition = "2021"
rust-version = "1.74.0"
[badges]
[workspace.dependencies]
bitflags = "2.7.0"
color-eyre = "0.6.3"
crossterm = "0.28.1"
document-features = "0.2.7"
indoc = "2.0.5"
instability = "0.3.7"
itertools = "0.13.0"
pretty_assertions = "1.4.1"
ratatui = { path = "ratatui", version = "0.30.0-alpha.1" }
ratatui-core = { path = "ratatui-core", version = "0.1.0-alpha.2" }
ratatui-crossterm = { path = "ratatui-crossterm", version = "0.1.0-alpha.1" }
ratatui-termion = { path = "ratatui-termion", version = "0.1.0-alpha.1" }
ratatui-termwiz = { path = "ratatui-termwiz", version = "0.1.0-alpha.1" }
ratatui-widgets = { path = "ratatui-widgets", version = "0.3.0-alpha.1" }
rstest = "0.24.0"
serde = { version = "1.0.217", features = ["derive"] }
serde_json = "1.0.135"
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
termion = "4.0.0"
unicode-width = "=0.2.0"
[dependencies]
bitflags = "2.3"
cassowary = "0.3"
compact_str = "0.8.0"
crossterm = { version = "0.28.1", optional = true }
document-features = { version = "0.2.7", optional = true }
instability = "0.3.1"
itertools = "0.13"
lru = "0.12.0"
paste = "1.0.2"
palette = { version = "0.7.6", optional = true }
serde = { version = "1", optional = true, features = ["derive"] }
strum = { version = "0.26", features = ["derive"] }
strum_macros = { version = "0.26.3" }
termion = { version = "4.0.0", optional = true }
termwiz = { version = "0.22.0", optional = true }
time = { version = "0.3.11", optional = true, features = ["local-offset"] }
unicode-segmentation = "1.10"
unicode-truncate = "1"
unicode-width = "0.1.13"
# Improve benchmark consistency
[profile.bench]
codegen-units = 1
lto = true
[dev-dependencies]
argh = "0.1.12"
color-eyre = "0.6.2"
criterion = { version = "0.5.1", features = ["html_reports"] }
crossterm = { version = "0.28.1", features = ["event-stream"] }
derive_builder = "0.20.0"
fakeit = "1.1"
font8x8 = "0.3.1"
futures = "0.3.30"
indoc = "2"
octocrab = "0.39.0"
pretty_assertions = "1.4.0"
rand = "0.8.5"
rand_chacha = "0.3.1"
rstest = "0.22.0"
serde_json = "1.0.109"
tokio = { version = "1.39.2", features = [
"rt",
"macros",
"time",
"rt-multi-thread",
] }
tracing = "0.1.40"
tracing-appender = "0.2.3"
tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }
[lints.rust]
[workspace.lints.rust]
unsafe_code = "forbid"
[lints.clippy]
cargo = { level = "warn", priority = -1 }
[workspace.lints.clippy]
pedantic = { level = "warn", priority = -1 }
cast_possible_truncation = "allow"
cast_possible_wrap = "allow"
@@ -111,259 +95,3 @@ string_slice = "warn"
string_to_string = "warn"
unnecessary_self_imports = "warn"
use_self = "warn"
[features]
#! The crate provides a set of optional features that can be enabled in your `cargo.toml` file.
#!
## By default, we enable the crossterm backend as this is a reasonable choice for most applications
## as it is supported on Linux/Mac/Windows systems. We also enable the `underline-color` feature
## which allows you to set the underline color of text.
default = ["crossterm", "underline-color"]
#! Generally an application will only use one backend, so you should only enable one of the following features:
## enables the [`CrosstermBackend`](backend::CrosstermBackend) backend and adds a dependency on [`crossterm`].
crossterm = ["dep:crossterm"]
## enables the [`TermionBackend`](backend::TermionBackend) backend and adds a dependency on [`termion`].
termion = ["dep:termion"]
## enables the [`TermwizBackend`](backend::TermwizBackend) backend and adds a dependency on [`termwiz`].
termwiz = ["dep:termwiz"]
#! The following optional features are available for all backends:
## enables serialization and deserialization of style and color types using the [`serde`] crate.
## This is useful if you want to save themes to a file.
serde = ["dep:serde", "bitflags/serde", "compact_str/serde"]
## enables the [`border!`] macro.
macros = []
## enables conversions from colors in the [`palette`] crate to [`Color`](crate::style::Color).
palette = ["dep:palette"]
## enables all widgets.
all-widgets = ["widget-calendar"]
#! Widgets that add dependencies are gated behind feature flags to prevent unused transitive
#! dependencies. The available features are:
## enables the [`calendar`](widgets::calendar) widget module and adds a dependency on [`time`].
widget-calendar = ["dep:time"]
#! The following optional features are only available for some backends:
## enables the backend code that sets the underline color.
## Underline color is only supported by the [`CrosstermBackend`](backend::CrosstermBackend) backend,
## and is not supported on Windows 7.
underline-color = ["dep:crossterm"]
#! The following features are unstable and may change in the future:
## Enable all unstable features.
unstable = ["unstable-rendered-line-info", "unstable-widget-ref"]
## Enables the [`Paragraph::line_count`](widgets::Paragraph::line_count)
## [`Paragraph::line_width`](widgets::Paragraph::line_width) methods
## which are experimental and may change in the future.
## See [Issue 293](https://github.com/ratatui-org/ratatui/issues/293) for more details.
unstable-rendered-line-info = []
## Enables the [`WidgetRef`](widgets::WidgetRef) and [`StatefulWidgetRef`](widgets::StatefulWidgetRef) traits which are experimental and may change in
## the future.
unstable-widget-ref = []
[package.metadata.docs.rs]
all-features = true
# see https://doc.rust-lang.org/nightly/rustdoc/scraped-examples.html
cargo-args = ["-Zunstable-options", "-Zrustdoc-scrape-examples"]
rustdoc-args = ["--cfg", "docsrs"]
# Improve benchmark consistency
[profile.bench]
codegen-units = 1
lto = true
[lib]
bench = false
[[bench]]
name = "main"
harness = false
[[example]]
name = "async"
required-features = ["crossterm"]
doc-scrape-examples = true
[[example]]
name = "barchart"
required-features = ["crossterm"]
doc-scrape-examples = true
[[example]]
name = "barchart-grouped"
required-features = ["crossterm"]
doc-scrape-examples = true
[[example]]
name = "block"
required-features = ["crossterm"]
doc-scrape-examples = true
[[example]]
name = "calendar"
required-features = ["crossterm", "widget-calendar"]
doc-scrape-examples = true
[[example]]
name = "canvas"
required-features = ["crossterm"]
doc-scrape-examples = true
[[example]]
name = "chart"
required-features = ["crossterm"]
doc-scrape-examples = true
[[example]]
name = "colors"
required-features = ["crossterm"]
# this example is a bit verbose, so we don't want to include it in the docs
doc-scrape-examples = false
[[example]]
name = "colors_rgb"
required-features = ["crossterm", "palette"]
doc-scrape-examples = true
[[example]]
name = "constraint-explorer"
required-features = ["crossterm"]
doc-scrape-examples = true
[[example]]
name = "constraints"
required-features = ["crossterm"]
doc-scrape-examples = false
[[example]]
name = "custom_widget"
required-features = ["crossterm"]
doc-scrape-examples = true
[[example]]
name = "demo"
# this runs for all of the terminal backends, so it can't be built using --all-features or scraped
doc-scrape-examples = false
[[example]]
name = "demo2"
required-features = ["crossterm", "palette", "widget-calendar"]
doc-scrape-examples = true
[[example]]
name = "docsrs"
required-features = ["crossterm"]
doc-scrape-examples = false
[[example]]
name = "flex"
required-features = ["crossterm"]
doc-scrape-examples = true
[[example]]
name = "gauge"
required-features = ["crossterm"]
doc-scrape-examples = true
[[example]]
name = "hello_world"
required-features = ["crossterm"]
doc-scrape-examples = true
[[example]]
name = "inline"
required-features = ["crossterm"]
doc-scrape-examples = true
[[example]]
name = "layout"
required-features = ["crossterm"]
doc-scrape-examples = true
[[example]]
name = "line_gauge"
required-features = ["crossterm"]
doc-scrape-examples = true
[[example]]
name = "hyperlink"
required-features = ["crossterm", "unstable-widget-ref"]
doc-scrape-examples = true
[[example]]
name = "list"
required-features = ["crossterm"]
doc-scrape-examples = true
[[example]]
name = "minimal"
required-features = ["crossterm"]
# prefer to show the more featureful examples in the docs
doc-scrape-examples = false
[[example]]
name = "modifiers"
required-features = ["crossterm"]
# this example is a bit verbose, so we don't want to include it in the docs
doc-scrape-examples = false
[[example]]
name = "panic"
required-features = ["crossterm"]
doc-scrape-examples = true
[[example]]
name = "paragraph"
required-features = ["crossterm"]
doc-scrape-examples = true
[[example]]
name = "popup"
required-features = ["crossterm"]
doc-scrape-examples = true
[[example]]
name = "ratatui-logo"
required-features = ["crossterm"]
doc-scrape-examples = true
[[example]]
name = "scrollbar"
required-features = ["crossterm"]
doc-scrape-examples = true
[[example]]
name = "sparkline"
required-features = ["crossterm"]
doc-scrape-examples = true
[[example]]
name = "table"
required-features = ["crossterm"]
doc-scrape-examples = true
[[example]]
name = "tabs"
required-features = ["crossterm"]
doc-scrape-examples = true
[[example]]
name = "tracing"
required-features = ["crossterm"]
doc-scrape-examples = true
[[example]]
name = "user_input"
required-features = ["crossterm"]
doc-scrape-examples = true
[[test]]
name = "state_serde"
required-features = ["serde"]

View File

@@ -6,10 +6,10 @@ This file documents current and past maintainers.
- [joshka](https://github.com/joshka)
- [kdheepak](https://github.com/kdheepak)
- [Valentin271](https://github.com/Valentin271)
- [EdJoPaTo](https://github.com/EdJoPaTo)
## Past Maintainers
- [fdehau](https://github.com/fdehau)
- [mindoodoo](https://github.com/mindoodoo)
- [sayanarijit](https://github.com/sayanarijit)
- [EdJoPaTo](https://github.com/EdJoPaTo)

View File

@@ -1,180 +0,0 @@
# configuration for https://github.com/sagiegurari/cargo-make
[config]
skip_core_tasks = true
[env]
# all features except the backend ones
ALL_FEATURES = "all-widgets,macros,serde"
[env.ALL_FEATURES_FLAG]
# Windows does not support building termion, so this avoids the build failure by providing two
# sets of flags, one for Windows and one for other platforms.
source = "${CARGO_MAKE_RUST_TARGET_OS}"
default_value = "--features=all-widgets,macros,serde,crossterm,termion,termwiz,underline-color,unstable"
mapping = { "windows" = "--features=all-widgets,macros,serde,crossterm,termwiz,underline-color,unstable" }
[tasks.default]
alias = "ci"
[tasks.ci]
description = "Run continuous integration tasks"
dependencies = ["lint", "clippy", "check", "test"]
[tasks.lint]
description = "Lint code style (formatting, typos, docs, markdown)"
dependencies = ["lint-format", "lint-typos", "lint-docs", "lint-markdown"]
[tasks.lint-format]
description = "Lint code formatting"
toolchain = "nightly"
command = "cargo"
args = ["fmt", "--all", "--check"]
[tasks.format]
description = "Fix code formatting"
toolchain = "nightly"
command = "cargo"
args = ["fmt", "--all"]
[tasks.lint-typos]
description = "Run typo checks"
install_crate = { crate_name = "typos-cli", binary = "typos", test_arg = "--version" }
command = "typos"
[tasks.lint-docs]
description = "Check documentation for errors and warnings"
toolchain = "nightly"
command = "cargo"
args = [
"rustdoc",
"--no-default-features",
"${ALL_FEATURES_FLAG}",
"--",
"-Zunstable-options",
"--check",
"-Dwarnings",
]
[tasks.lint-markdown]
description = "Check markdown files for errors and warnings"
command = "markdownlint-cli2"
args = ["**/*.md", "!target"]
[tasks.check]
description = "Check code for errors and warnings"
command = "cargo"
args = [
"check",
"--all-targets",
"--no-default-features",
"${ALL_FEATURES_FLAG}",
]
[tasks.build]
description = "Compile the project"
command = "cargo"
args = [
"build",
"--all-targets",
"--no-default-features",
"${ALL_FEATURES_FLAG}",
]
[tasks.clippy]
description = "Run Clippy for linting"
command = "cargo"
args = [
"clippy",
"--all-targets",
"--tests",
"--benches",
"--no-default-features",
"${ALL_FEATURES_FLAG}",
"--",
"-D",
"warnings",
]
[tasks.install-nextest]
description = "Install cargo-nextest"
install_crate = { crate_name = "cargo-nextest", binary = "cargo-nextest", test_arg = "--help" }
[tasks.test]
description = "Run tests"
run_task = { name = ["test-lib", "test-doc"] }
[tasks.test-lib]
description = "Run default tests"
dependencies = ["install-nextest"]
command = "cargo"
args = [
"nextest",
"run",
"--all-targets",
"--no-default-features",
"${ALL_FEATURES_FLAG}",
]
[tasks.test-doc]
description = "Run documentation tests"
command = "cargo"
args = ["test", "--doc", "--no-default-features", "${ALL_FEATURES_FLAG}"]
[tasks.test-backend]
# takes a command line parameter to specify the backend to test (e.g. "crossterm")
description = "Run backend-specific tests"
dependencies = ["install-nextest"]
command = "cargo"
args = [
"nextest",
"run",
"--all-targets",
"--no-default-features",
"--features",
"${ALL_FEATURES},${@}",
]
[tasks.coverage]
description = "Generate code coverage report"
command = "cargo"
args = [
"llvm-cov",
"--lcov",
"--output-path",
"target/lcov.info",
"--no-default-features",
"${ALL_FEATURES_FLAG}",
]
[tasks.run-example]
private = true
condition = { env_set = ["TUI_EXAMPLE_NAME"] }
command = "cargo"
args = [
"run",
"--release",
"--example",
"${TUI_EXAMPLE_NAME}",
"--features",
"all-widgets",
]
[tasks.build-examples]
description = "Compile project examples"
command = "cargo"
args = ["build", "--examples", "--release", "--features", "all-widgets"]
[tasks.run-examples]
description = "Run project examples"
dependencies = ["build-examples"]
script = '''
#!@duckscript
files = glob_array ./examples/*.rs
for file in ${files}
name = basename ${file}
name = substring ${name} -3
set_env TUI_EXAMPLE_NAME ${name}
cm_run_task run-example
end
'''

507
README.md
View File

@@ -1,429 +1,170 @@
<details>
<summary>Table of Contents</summary>
- [Ratatui](#ratatui)
- [Installation](#installation)
- [Introduction](#introduction)
- [Other Documentation](#other-documentation)
- [Quickstart](#quickstart)
- [Status of this fork](#status-of-this-fork)
- [Rust version requirements](#rust-version-requirements)
- [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)
- [Quickstart](#quickstart)
- [Documentation](#documentation)
- [Templates](#templates)
- [Built with Ratatui](#built-with-ratatui)
- [Alternatives](#alternatives)
- [Contributing](#contributing)
- [Acknowledgements](#acknowledgements)
- [License](#license)
</details>
<!-- cargo-rdme start -->
![Demo](https://github.com/ratatui-org/ratatui/blob/87ae72dbc756067c97f6400d3e2a58eeb383776e/examples/demo2-destroy.gif?raw=true)
![Demo](https://github.com/ratatui/ratatui/blob/87ae72dbc756067c97f6400d3e2a58eeb383776e/examples/demo2-destroy.gif?raw=true)
<div align="center">
[![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]
[![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]
</div>
# 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.
## Installation
Add `ratatui` as a dependency to your cargo.toml:
```shell
cargo add ratatui
```
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]).
## 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.
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.
## 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.
[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.
## Quickstart
The following example demonstrates the minimal amount of code necessary to setup a terminal and
render "Hello World!". 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.
Ratatui has [templates] available to help you get started quickly. You can use the
[`cargo-generate`] command to create a new project with Ratatui:
Every application built with `ratatui` needs to implement the following steps:
```shell
cargo install --locked cargo-generate
cargo generate ratatui/templates
```
- Initialize the terminal
- A main loop to:
- Handle input events
- Draw the UI
- Restore the terminal state
The library contains a [`prelude`] module that re-exports the most commonly used traits and
types for convenience. Most examples in the documentation will use this instead of showing the
full path of each type.
### Initialize and restore the terminal
The [`Terminal`] type is the main entry point for any Ratatui application. It is a light
abstraction over a choice of [`Backend`] implementations that provides functionality to draw
each frame, clear the screen, hide the cursor, etc. It is parametrized over any type that
implements the [`Backend`] trait which has implementations for [Crossterm], [Termion] and
[Termwiz].
Most applications should enter the Alternate Screen when starting and leave it when exiting and
also enable raw mode to disable line buffering and enable reading key events. See the [`backend`
module] and the [Backends] section of the [Ratatui Website] for more info.
### Drawing the UI
The drawing logic is delegated to a closure that takes a [`Frame`] instance as argument. 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.
### Handling events
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.
### Example
Selecting the Hello World template produces the following application:
```rust
use std::io::{self, stdout};
use color_eyre::Result;
use crossterm::event::{self, Event};
use ratatui::{DefaultTerminal, Frame};
use ratatui::{
backend::CrosstermBackend,
crossterm::{
event::{self, Event, KeyCode},
terminal::{
disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen,
},
ExecutableCommand,
},
widgets::{Block, Paragraph},
Frame, Terminal,
};
fn main() -> io::Result<()> {
enable_raw_mode()?;
stdout().execute(EnterAlternateScreen)?;
let mut terminal = Terminal::new(CrosstermBackend::new(stdout()))?;
let mut should_quit = false;
while !should_quit {
terminal.draw(ui)?;
should_quit = handle_events()?;
}
disable_raw_mode()?;
stdout().execute(LeaveAlternateScreen)?;
Ok(())
fn main() -> Result<()> {
color_eyre::install()?;
let terminal = ratatui::init();
let result = run(terminal);
ratatui::restore();
result
}
fn handle_events() -> io::Result<bool> {
if event::poll(std::time::Duration::from_millis(50))? {
if let Event::Key(key) = event::read()? {
if key.kind == event::KeyEventKind::Press && key.code == KeyCode::Char('q') {
return Ok(true);
}
fn run(mut terminal: DefaultTerminal) -> Result<()> {
loop {
terminal.draw(render)?;
if matches!(event::read()?, Event::Key(_)) {
break Ok(());
}
}
Ok(false)
}
fn ui(frame: &mut Frame) {
frame.render_widget(
Paragraph::new("Hello World!").block(Block::bordered().title("Greeting")),
frame.size(),
);
fn render(frame: &mut Frame) {
frame.render_widget("hello world", frame.area());
}
```
Running this example produces the following output:
## Documentation
![docsrs-hello]
- [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.
- [Changelog] - generated by [git-cliff] utilizing [Conventional Commits].
- [Breaking Changes] - a list of breaking changes in the library.
## Layout
You can also watch the [EuroRust 2024 talk] to learn about common concepts in Ratatui and what's
possible to build with it.
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.
## Templates
```rust
use ratatui::{
layout::{Constraint, Layout},
widgets::Block,
Frame,
};
If you're looking to get started quickly, you can use one of the available templates from the
[templates] repository using [`cargo-generate`]:
fn ui(frame: &mut Frame) {
let [title_area, main_area, status_area] = Layout::vertical([
Constraint::Length(1),
Constraint::Min(0),
Constraint::Length(1),
])
.areas(frame.size());
let [left_area, right_area] =
Layout::horizontal([Constraint::Percentage(50), Constraint::Percentage(50)])
.areas(main_area);
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);
}
```shell
cargo generate ratatui/templates
```
Running this example produces the following output:
## Built with Ratatui
![docsrs-layout]
[![Awesome](https://awesome.re/badge-flat2.svg)][awesome-ratatui]
## Text and styling
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.
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.
```rust
use ratatui::{
layout::{Constraint, Layout},
style::{Color, Modifier, Style, Stylize},
text::{Line, Span},
widgets::{Block, Paragraph},
Frame,
};
fn ui(frame: &mut Frame) {
let areas = Layout::vertical([Constraint::Length(1); 4]).split(frame.size());
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]);
}
```
Running this example produces the following output:
![docsrs-styling]
[Ratatui Website]: https://ratatui.rs/
[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-org/templates/
[Examples]: https://github.com/ratatui-org/ratatui/tree/main/examples/README.md
[Report a bug]: https://github.com/ratatui-org/ratatui/issues/new?labels=bug&projects=&template=bug_report.md
[Request a Feature]: https://github.com/ratatui-org/ratatui/issues/new?labels=enhancement&projects=&template=feature_request.md
[Create a Pull Request]: https://github.com/ratatui-org/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-org/ratatui/blob/main/CHANGELOG.md
[Contributing]: https://github.com/ratatui-org/ratatui/blob/main/CONTRIBUTING.md
[Breaking Changes]: https://github.com/ratatui-org/ratatui/blob/main/BREAKING-CHANGES.md
[FOSDEM 2024 talk]: https://www.youtube.com/watch?v=NU0q6NOLJ20
[docsrs-hello]: https://github.com/ratatui-org/ratatui/blob/c3c3c289b1eb8d562afb1931adb4dc719cd48490/examples/docsrs-hello.png?raw=true
[docsrs-layout]: https://github.com/ratatui-org/ratatui/blob/c3c3c289b1eb8d562afb1931adb4dc719cd48490/examples/docsrs-layout.png?raw=true
[docsrs-styling]: https://github.com/ratatui-org/ratatui/blob/c3c3c289b1eb8d562afb1931adb4dc719cd48490/examples/docsrs-styling.png?raw=true
[`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
[GitHub Sponsors]: https://github.com/sponsors/ratatui-org
[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-org/ratatui/ci.yml?style=flat-square&logo=github
[CI Workflow]: https://github.com/ratatui-org/ratatui/actions/workflows/ci.yml
[Codecov Badge]: https://img.shields.io/codecov/c/github/ratatui-org/ratatui?logo=codecov&style=flat-square&token=BAQ8SOKEST&color=C43AC3&logoColor=C43AC3
[Codecov]: https://app.codecov.io/gh/ratatui-org/ratatui
[Deps.rs Badge]: https://deps.rs/repo/github/ratatui-org/ratatui/status.svg?style=flat-square
[Deps.rs]: https://deps.rs/repo/github/ratatui-org/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/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-org?logo=github&style=flat-square&color=1370D3
<!-- cargo-rdme end -->
## Status of this fork
In response to the original maintainer [**Florian Dehau**](https://github.com/fdehau)'s issue
regarding the [future of `tui-rs`](https://github.com/fdehau/tui-rs/issues/654), several members of
the community forked the project and created this crate. We look forward to continuing the work
started by Florian 🚀
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).
While we do utilize Discord for coordinating, it's not essential for contributing. We have recently
launched the [Ratatui Forum][Forum], and our primary open-source workflow is centered around GitHub.
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 updated [contributing](./CONTRIBUTING.md) guidelines, especially if
you are interested in working on a PR or issue opened in the previous repository.
## Widgets
### Built in
The library comes with the following
[widgets](https://docs.rs/ratatui/latest/ratatui/widgets/index.html):
- [BarChart](https://docs.rs/ratatui/latest/ratatui/widgets/struct.BarChart.html)
- [Block](https://docs.rs/ratatui/latest/ratatui/widgets/block/struct.Block.html)
- [Calendar](https://docs.rs/ratatui/latest/ratatui/widgets/calendar/index.html)
- [Canvas](https://docs.rs/ratatui/latest/ratatui/widgets/canvas/struct.Canvas.html) which allows
rendering [points, lines, shapes and a world
map](https://docs.rs/ratatui/latest/ratatui/widgets/canvas/index.html)
- [Chart](https://docs.rs/ratatui/latest/ratatui/widgets/struct.Chart.html)
- [Clear](https://docs.rs/ratatui/latest/ratatui/widgets/struct.Clear.html)
- [Gauge](https://docs.rs/ratatui/latest/ratatui/widgets/struct.Gauge.html)
- [List](https://docs.rs/ratatui/latest/ratatui/widgets/struct.List.html)
- [Paragraph](https://docs.rs/ratatui/latest/ratatui/widgets/struct.Paragraph.html)
- [Scrollbar](https://docs.rs/ratatui/latest/ratatui/widgets/scrollbar/struct.Scrollbar.html)
- [Sparkline](https://docs.rs/ratatui/latest/ratatui/widgets/struct.Sparkline.html)
- [Table](https://docs.rs/ratatui/latest/ratatui/widgets/struct.Table.html)
- [Tabs](https://docs.rs/ratatui/latest/ratatui/widgets/struct.Tabs.html)
Each widget has an associated example which can be found in the [Examples] folder. Run each example
with cargo (e.g. to run the gauge example `cargo run --example gauge`), and quit by pressing `q`.
You can also run all examples by running `cargo make run-examples` (requires `cargo-make` that can
be installed with `cargo install cargo-make`).
### Third-party libraries, bootstrapping templates and widgets
- [ansi-to-tui](https://github.com/uttarayan21/ansi-to-tui) — Convert ansi colored text to
`ratatui::text::Text`
- [color-to-tui](https://github.com/uttarayan21/color-to-tui) — Parse hex colors to
`ratatui::style::Color`
- [templates](https://github.com/ratatui-org/templates) — Starter templates for
bootstrapping a Rust TUI application with Ratatui & crossterm
- [tui-builder](https://github.com/jkelleyrtp/tui-builder) — Batteries-included MVC framework for
Tui-rs + Crossterm apps
- [tui-clap](https://github.com/kegesch/tui-clap-rs) — Use clap-rs together with Tui-rs
- [tui-log](https://github.com/kegesch/tui-log-rs) — Example of how to use logging with Tui-rs
- [tui-logger](https://github.com/gin66/tui-logger) — Logger and Widget for Tui-rs
- [tui-realm](https://github.com/veeso/tui-realm) — Tui-rs framework to build stateful applications
with a React/Elm inspired approach
- [tui-realm-treeview](https://github.com/veeso/tui-realm-treeview) — Treeview component for
Tui-realm
- [tui-rs-tree-widgets](https://github.com/EdJoPaTo/tui-rs-tree-widget) — Widget for tree data
structures.
- [tui-windows](https://github.com/markatk/tui-windows-rs) — Tui-rs abstraction to handle multiple
windows and their rendering
- [tui-textarea](https://github.com/rhysd/tui-textarea) — Simple yet powerful multi-line text editor
widget supporting several key shortcuts, undo/redo, text search, etc.
- [tui-input](https://github.com/sayanarijit/tui-input) — TUI input library supporting multiple
backends and tui-rs.
- [tui-term](https://github.com/a-kenji/tui-term) — A pseudoterminal widget library
that enables the rendering of terminal applications as ratatui widgets.
## Apps
Check out [awesome-ratatui](https://github.com/ratatui-org/awesome-ratatui) for a curated list of
awesome apps/libraries built with `ratatui`!
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!
## Alternatives
You might want to checkout [Cursive](https://github.com/gyscos/Cursive) for an alternative solution
to build text user interfaces in Rust.
- [Cursive](https://crates.io/crates/cursive) - a ncurses-based TUI library.
- [iocraft](https://crates.io/crates/iocraft) - a declarative TUI library.
## Acknowledgments
## Contributing
Special thanks to [**Pavel Fomchenkov**](https://github.com/nawok) for his work in designing **an
awesome logo** for the ratatui project and ratatui-org organization.
[![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].
## Acknowledgements
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.
Special thanks to [Pavel Fomchenkov] for his work in designing an awesome logo for the Ratatui
project and organization.
## License
[MIT](./LICENSE)
This project is licensed under the [MIT License][License].
[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
[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
[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
[Contributing]: https://github.com/ratatui/ratatui/blob/main/CONTRIBUTING.md
[Crate]: https://crates.io/crates/ratatui
[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
[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
[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
[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
[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
[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

View File

@@ -1,5 +1,12 @@
# Creating a Release
Our release strategy is:
> Release major versions with detailed summaries when necessary, while releasing minor versions
> weekly or as needed without extensive announcements.
>
> Versioning scheme being `0.x.y`, where `x` is the major version and `y` is the minor version.
[crates.io](https://crates.io/crates/ratatui) releases are automated via [GitHub
actions](.github/workflows/cd.yml) and triggered by pushing a tag.
@@ -12,7 +19,7 @@ actions](.github/workflows/cd.yml) and triggered by pushing a tag.
```
1. Switch branches to the images branch and copy demo2.gif to examples/, commit, and push.
1. Grab the permalink from <https://github.com/ratatui-org/ratatui/blob/images/examples/demo2.gif> and
1. Grab the permalink from <https://github.com/ratatui/ratatui/blob/images/examples/demo2.gif> and
append `?raw=true` to redirect to the actual image url. Then update the link in the main README.
Avoid adding the gif to the git repo as binary files tend to bloat repositories.
@@ -21,16 +28,16 @@ actions](.github/workflows/cd.yml) and triggered by pushing a tag.
can be used for generating the entries.
1. Ensure that any breaking changes are documented in [BREAKING-CHANGES.md](./BREAKING-CHANGES.md)
1. Commit and push the changes.
1. Create a new tag: `git tag -a v[X.Y.Z]`
1. Create a new tag: `git tag -a v[0.x.y]`
1. Push the tag: `git push --tags`
1. Wait for [Continuous Deployment](https://github.com/ratatui-org/ratatui/actions) workflow to
1. Wait for [Continuous Deployment](https://github.com/ratatui/ratatui/actions) workflow to
finish.
## Alpha Releases
Alpha releases are automatically released every Saturday via [cd.yml](./.github/workflows/cd.yml)
and can be manually be created when necessary by triggering the [Continuous
Deployment](https://github.com/ratatui-org/ratatui/actions/workflows/cd.yml) workflow.
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
need to manually). E.g. the last release was 0.22.0, and the most recent alpha release is
@@ -40,5 +47,5 @@ These releases will have whatever happened to be in main at the time of release,
for apps that need to get releases from crates.io, but may contain more bugs and be generally less
tested than normal releases.
See [#147](https://github.com/ratatui-org/ratatui/issues/147) and
[#359](https://github.com/ratatui-org/ratatui/pull/359) for more info on the alpha release process.
See [#147](https://github.com/ratatui/ratatui/issues/147) and
[#359](https://github.com/ratatui/ratatui/pull/359) for more info on the alpha release process.

View File

@@ -6,4 +6,4 @@ We only support the latest version of this crate.
## Reporting a Vulnerability
To report secuirity vulnerability, please use the form at <https://github.com/ratatui-org/ratatui/security/advisories/new>
To report secuirity vulnerability, please use the form at <https://github.com/ratatui/ratatui/security/advisories/new>

View File

@@ -8,101 +8,64 @@
default_job = "check"
[jobs.check]
command = ["cargo", "check", "--all-features", "--color", "always"]
command = ["cargo", "xtask", "check"]
need_stdout = false
[jobs.check-all]
command = ["cargo", "check", "--all-targets", "--all-features", "--color", "always"]
command = ["cargo", "xtask", "check", "--all-features"]
need_stdout = false
[jobs.check-crossterm]
command = ["cargo", "check", "--color", "always", "--all-targets", "--no-default-features", "--features", "crossterm"]
command = ["cargo", "xtask", "check-crossterm"]
need_stdout = false
[jobs.check-termion]
command = ["cargo", "check", "--color", "always", "--all-targets", "--no-default-features", "--features", "termion"]
command = ["cargo", "xtask", "check-termion"]
need_stdout = false
[jobs.check-termwiz]
command = ["cargo", "check", "--color", "always", "--all-targets", "--no-default-features", "--features", "termwiz"]
command = ["cargo", "xtask", "check-termwiz"]
need_stdout = false
[jobs.clippy]
command = [
"cargo", "clippy",
"--all-targets",
"--color", "always",
]
[jobs.clippy-all]
command = ["cargo", "xtask", "clippy"]
need_stdout = false
[jobs.test]
command = [
"cargo", "test",
"--all-features",
"--color", "always",
"--", "--color", "always", # see https://github.com/Canop/bacon/issues/124
]
command = ["cargo", "xtask", "test"]
need_stdout = true
[jobs.test-unit]
command = [
"cargo", "test",
"--lib",
"--all-features",
"--color", "always",
"--", "--color", "always", # see https://github.com/Canop/bacon/issues/124
]
command = ["cargo", "xtask", "test-libs"]
need_stdout = true
[jobs.doc]
command = [
"cargo", "+nightly", "doc",
"-Zunstable-options", "-Zrustdoc-scrape-examples",
"--all-features",
"--color", "always",
"--no-deps",
]
env.RUSTDOCFLAGS = "--cfg docsrs"
command = ["cargo", "xtask", "docs"]
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", "+nightly", "doc",
"-Zunstable-options", "-Zrustdoc-scrape-examples",
"--all-features",
"--color", "always",
"--no-deps",
"--open",
]
env.RUSTDOCFLAGS = "--cfg docsrs"
command = ["cargo", "xtask", "docs", "--open"]
on_success = "job:doc"
need_stdout = false
on_success = "job:doc" # so that we don't open the browser at each change
[jobs.coverage]
command = [
"cargo", "llvm-cov",
"--lcov", "--output-path", "target/lcov.info",
"--all-features",
"--color", "always",
]
command = ["cargo", "xtask", "coverage"]
[jobs.coverage-unit-tests-only]
command = [
"cargo", "llvm-cov",
"--lcov", "--output-path", "target/lcov.info",
"--lib",
"--all-features",
"--color", "always",
]
command = ["cargo", "xtask", "coverage-unit"]
[jobs.hack]
command = ["cargo", "xtask", "hack"]
[jobs.format]
command = ["cargo", "xtask", "format"]
# You may define here keybindings that would be specific to
# a project, for example a shortcut to launch a specific job.
# Shortcuts to internal functions (scrolling, toggling, etc.)
# should go in your personal global prefs.toml file instead.
[keybindings]
# alt-m = "job:my-job"
ctrl-h = "job:hack"
ctrl-c = "job:check-crossterm"
ctrl-t = "job:check-termion"
ctrl-w = "job:check-termwiz"
@@ -110,3 +73,4 @@ v = "job:coverage"
ctrl-v = "job:coverage-unit-tests-only"
u = "job:test-unit"
n = "job:nextest"
f = "job:format"

View File

@@ -1,24 +0,0 @@
use criterion::{black_box, criterion_group, BenchmarkId, Criterion};
use ratatui::layout::Rect;
fn rect_rows_benchmark(c: &mut Criterion) {
let rect_sizes = vec![
Rect::new(0, 0, 1, 16),
Rect::new(0, 0, 1, 1024),
Rect::new(0, 0, 1, 65535),
];
let mut group = c.benchmark_group("rect_rows");
for rect in rect_sizes {
group.bench_with_input(BenchmarkId::new("rows", rect.height), &rect, |b, rect| {
b.iter(|| {
for row in rect.rows() {
// Perform any necessary operations on each row
black_box(row);
}
});
});
}
group.finish();
}
criterion_group!(benches, rect_rows_benchmark);

View File

@@ -2,7 +2,7 @@
# https://git-cliff.org/docs/configuration
[remote.github]
owner = "ratatui-org"
owner = "ratatui"
repo = "ratatui"
[changelog]
@@ -24,25 +24,15 @@ body = """
{%- if not version %}
## [unreleased]
{% else -%}
## [{{ version }}](https://github.com/ratatui-org/ratatui/releases/tag/{{ version }}) - {{ timestamp | date(format="%Y-%m-%d") }}
## [{{ version }}](https://github.com/ratatui/ratatui/releases/tag/{{ version }}) - {{ timestamp | date(format="%Y-%m-%d") }}
{% endif -%}
{% macro commit(commit) -%}
- [{{ commit.id | truncate(length=7, end="") }}]({{ "https://github.com/ratatui-org/ratatui/commit/" ~ commit.id }}) \
- [{{ commit.id | truncate(length=7, end="") }}]({{ "https://github.com/ratatui/ratatui/commit/" ~ commit.id }}) \
*({{commit.scope | default(value = "uncategorized") | lower }})* {{ commit.message | upper_first | trim }}\
{% if commit.github.username %} by @{{ commit.github.username }}{%- endif -%}\
{% if commit.github.pr_number %} in [#{{ commit.github.pr_number }}]({{ self::remote_url() }}/pull/{{ commit.github.pr_number }}){%- endif %}\
{% 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") %}

View File

@@ -1,17 +1 @@
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
# crossterm -> all the windows- deps https://github.com/ratatui-org/ratatui/pull/1064#issuecomment-2078848980
allowed-duplicate-crates = [
"bitflags",
"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",
]

View File

@@ -4,13 +4,16 @@
version = 2
confidence-threshold = 0.8
allow = [
"Apache-2.0",
"BSD-2-Clause",
"BSD-3-Clause",
"ISC",
"MIT",
"Unicode-DFS-2016",
"WTFPL",
"Apache-2.0",
"BSD-2-Clause",
"BSD-3-Clause",
"ISC",
"MIT",
"OpenSSL",
"Unicode-3.0",
"Unicode-DFS-2016",
"WTFPL",
"Zlib",
]
[advisories]
@@ -23,3 +26,15 @@ 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,6 +1,19 @@
# Examples
This folder might use unreleased code. View the examples for the latest release instead.
This folder contains examples that are more app focused. There are also other examples in example
folders under each crate folder e.g. [ratatui examples], [ratatui-widgets examples].
[ratatui examples]: ../ratatui/examples
[ratatui-widgets examples]: ../ratatui-widgets/examples
You can run these examples using:
```shell
cargo run -p example-name
```
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]
>
@@ -10,469 +23,44 @@ This folder might use unreleased code. View the examples for the latest release
> 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-org/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
> 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-org/ratatui"`
> 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
## Demo
The examples contain some opinionated choices in order to make it easier for newer rustaceans to
easily be productive in creating applications:
This is the original demo example from the main README. It is available for each of the backends.
[Source](./apps/demo/).
- 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.
Not every example has been updated with all these points in mind yet, however over time they will
be. None of the above choices are strictly necessary for Ratatui apps, but these choices make
examples easier to run, maintain and explain. These choices are designed to help newer users fall
into the pit of success when incorporating example code into their own apps. We may also eventually
move some of these design choices into the core of Ratatui to simplify apps.
[How to use color-eyre with Ratatui]: https://ratatui.rs/how-to/develop-apps/color_eyre/
![Demo](https://github.com/ratatui/ratatui/blob/images/examples/demo.gif?raw=true)
## Demo2
This is the demo example from the main README and crate page. Source: [demo2](./demo2/).
This is the demo example from the main README and crate page. [Source](./apps/demo2/).
```shell
cargo run --example=demo2 --features="crossterm widget-calendar"
```
![Demo2](https://github.com/ratatui/ratatui/blob/images/examples/demo2.gif?raw=true)
![Demo2][demo2.gif]
## Mouse Drawing demo
## Demo
Shows how to handle mouse events. [Source](./apps/mouse-drawing/).
This is the previous demo example from the main README. It is available for each of the backends. Source:
[demo.rs](./demo/).
## Async GitHub demo
```shell
cargo run --example=demo --features=crossterm
cargo run --example=demo --no-default-features --features=termion
cargo run --example=demo --no-default-features --features=termwiz
```
Shows how to fetch data from GitHub API asynchronously. [Source](./apps/async-github/).
![Demo][demo.gif]
## Weather demo
## Hello World
Shows how to render weather data using barchart widget. [Source](./apps/weather/).
This is a pretty boring example, but it contains some good documentation
on writing tui apps. Source: [hello_world.rs](./hello_world.rs).
## Calendar explorer demo
```shell
cargo run --example=hello_world --features=crossterm
```
![Hello World][hello_world.gif]
## Barchart
Demonstrates the [`BarChart`](https://docs.rs/ratatui/latest/ratatui/widgets/struct.BarChart.html)
widget. Source: [barchart.rs](./barchart.rs).
```shell
cargo run --example=barchart --features=crossterm
```
![Barchart][barchart.gif]
## Barchart (Grouped)
Demonstrates the [`BarChart`](https://docs.rs/ratatui/latest/ratatui/widgets/struct.BarChart.html)
widget with groups. Source: [barchart-grouped.rs](./barchart-grouped.rs).
```shell
cargo run --example=barchart-grouped --features=crossterm
```
![Barchart Grouped][barchart-grouped.gif]
## Block
Demonstrates the [`Block`](https://docs.rs/ratatui/latest/ratatui/widgets/block/struct.Block.html)
widget. Source: [block.rs](./block.rs).
```shell
cargo run --example=block --features=crossterm
```
![Block][block.gif]
## Calendar
Demonstrates the [`Calendar`](https://docs.rs/ratatui/latest/ratatui/widgets/calendar/index.html)
widget. Source: [calendar.rs](./calendar.rs).
```shell
cargo run --example=calendar --features="crossterm widget-calendar"
```
![Calendar][calendar.gif]
## Canvas
Demonstrates the [`Canvas`](https://docs.rs/ratatui/latest/ratatui/widgets/canvas/index.html) widget
and related shapes in the
[`canvas`](https://docs.rs/ratatui/latest/ratatui/widgets/canvas/index.html) module. Source:
[canvas.rs](./canvas.rs).
```shell
cargo run --example=canvas --features=crossterm
```
![Canvas][canvas.gif]
## Chart
Demonstrates the [`Chart`](https://docs.rs/ratatui/latest/ratatui/widgets/struct.Chart.html) widget.
Source: [chart.rs](./chart.rs).
```shell
cargo run --example=chart --features=crossterm
```
![Chart][chart.gif]
## Colors
Demonstrates the available [`Color`](https://docs.rs/ratatui/latest/ratatui/style/enum.Color.html)
options. These can be used in any style field. Source: [colors.rs](./colors.rs).
```shell
cargo run --example=colors --features=crossterm
```
![Colors][colors.gif]
## Colors (RGB)
Demonstrates the available RGB
[`Color`](https://docs.rs/ratatui/latest/ratatui/style/enum.Color.html) options. These can be used
in any style field. Source: [colors_rgb.rs](./colors_rgb.rs). Uses a half block technique to render
two square-ish pixels in the space of a single rectangular terminal cell.
```shell
cargo run --example=colors_rgb --features=crossterm
```
Note: VHs renders full screen animations poorly, so this is a screen capture rather than the output
of the VHS tape.
<https://github.com/ratatui-org/ratatui/assets/381361/485e775a-e0b5-4133-899b-1e8aeb56e774>
## Constraint Explorer
Demonstrates the behaviour of each
[`Constraint`](https://docs.rs/ratatui/latest/ratatui/layout/enum.Constraint.html) option with
respect to each other across different `Flex` modes.
```shell
cargo run --example=constraint-explorer --features=crossterm
```
![Constraint Explorer][constraint-explorer.gif]
## Constraints
Demonstrates how to use
[`Constraint`](https://docs.rs/ratatui/latest/ratatui/layout/enum.Constraint.html) options for
defining layout element sizes.
![Constraints][constraints.gif]
```shell
cargo run --example=constraints --features=crossterm
```
## Custom Widget
Demonstrates how to implement the
[`Widget`](https://docs.rs/ratatui/latest/ratatui/widgets/trait.Widget.html) trait. Also shows mouse
interaction. Source: [custom_widget.rs](./custom_widget.rs).
```shell
cargo run --example=custom_widget --features=crossterm
```
![Custom Widget][custom_widget.gif]
## Gauge
Demonstrates the [`Gauge`](https://docs.rs/ratatui/latest/ratatui/widgets/struct.Gauge.html) widget.
Source: [gauge.rs](./gauge.rs).
```shell
cargo run --example=gauge --features=crossterm
```
![Gauge][gauge.gif]
## Flex
Demonstrates the different [`Flex`](https://docs.rs/ratatui/latest/ratatui/layout/enum.Flex.html)
modes for controlling layout space distribution.
```shell
cargo run --example=flex --features=crossterm
```
![Flex][flex.gif]
## Line Gauge
Demonstrates the [`Line
Gauge`](https://docs.rs/ratatui/latest/ratatui/widgets/struct.LineGauge.html) widget. Source:
[line_gauge.rs](./line_gauge.rs).
```shell
cargo run --example=line_gauge --features=crossterm
```
![LineGauge][line_gauge.gif]
## Hyperlink
Demonstrates how to use OSC 8 to create hyperlinks in the terminal.
```shell
cargo run --example=hyperlink --features="crossterm unstable-widget-ref"
```
![Hyperlink][hyperlink.gif]
## Inline
Demonstrates how to use the
[`Inline`](https://docs.rs/ratatui/latest/ratatui/terminal/enum.Viewport.html#variant.Inline)
Viewport mode for ratatui apps. Source: [inline.rs](./inline.rs).
```shell
cargo run --example=inline --features=crossterm
```
![Inline][inline.gif]
## Layout
Demonstrates the [`Layout`](https://docs.rs/ratatui/latest/ratatui/layout/struct.Layout.html) and
interaction between each constraint. Source: [layout.rs](./layout.rs).
```shell
cargo run --example=layout --features=crossterm
```
![Layout][layout.gif]
## List
Demonstrates the [`List`](https://docs.rs/ratatui/latest/ratatui/widgets/struct.List.html) widget.
Source: [list.rs](./list.rs).
```shell
cargo run --example=list --features=crossterm
```
![List][list.gif]
## Modifiers
Demonstrates the style
[`Modifiers`](https://docs.rs/ratatui/latest/ratatui/style/struct.Modifier.html). Source:
[modifiers.rs](./modifiers.rs).
```shell
cargo run --example=modifiers --features=crossterm
```
![Modifiers][modifiers.gif]
## Minimal
Demonstrates how to create a minimal `Hello World!` program.
```shell
cargo run --example=minimal --features=crossterm
```
![Minimal][minimal.gif]
## Panic
Demonstrates how to handle panics by ensuring that panic messages are written correctly to the
screen. Source: [panic.rs](./panic.rs).
```shell
cargo run --example=panic --features=crossterm
```
![Panic][panic.gif]
## Paragraph
Demonstrates the [`Paragraph`](https://docs.rs/ratatui/latest/ratatui/widgets/struct.Paragraph.html)
widget. Source: [paragraph.rs](./paragraph.rs)
```shell
cargo run --example=paragraph --features=crossterm
```
![Paragraph][paragraph.gif]
## Popup
Demonstrates how to render a widget over the top of previously rendered widgets using the
[`Clear`](https://docs.rs/ratatui/latest/ratatui/widgets/struct.Clear.html) widget. Source:
[popup.rs](./popup.rs).
>
```shell
cargo run --example=popup --features=crossterm
```
![Popup][popup.gif]
## Ratatui-logo
A fun example of using half blocks to render graphics Source:
[ratatui-logo.rs](./ratatui-logo.rs).
>
```shell
cargo run --example=ratatui-logo --features=crossterm
```
![Ratatui Logo][ratatui-logo.gif]
## Scrollbar
Demonstrates the [`Scrollbar`](https://docs.rs/ratatui/latest/ratatui/widgets/struct.Scrollbar.html)
widget. Source: [scrollbar.rs](./scrollbar.rs).
```shell
cargo run --example=scrollbar --features=crossterm
```
![Scrollbar][scrollbar.gif]
## Sparkline
Demonstrates the [`Sparkline`](https://docs.rs/ratatui/latest/ratatui/widgets/struct.Sparkline.html)
widget. Source: [sparkline.rs](./sparkline.rs).
```shell
cargo run --example=sparkline --features=crossterm
```
![Sparkline][sparkline.gif]
## Table
Demonstrates the [`Table`](https://docs.rs/ratatui/latest/ratatui/widgets/struct.Table.html) widget.
Source: [table.rs](./table.rs).
```shell
cargo run --example=table --features=crossterm
```
![Table][table.gif]
## Tabs
Demonstrates the [`Tabs`](https://docs.rs/ratatui/latest/ratatui/widgets/struct.Tabs.html) widget.
Source: [tabs.rs](./tabs.rs).
```shell
cargo run --example=tabs --features=crossterm
```
![Tabs][tabs.gif]
## Tracing
Demonstrates how to use the [tracing crate](https://crates.io/crates/tracing) for logging. Creates
a file named `tracing.log` in the current directory.
```shell
cargo run --example=tracing --features=crossterm
```
![Tracing][tracing.gif]
## User Input
Demonstrates one approach to accepting user input. Source [user_input.rs](./user_input.rs).
> [!NOTE]
> Consider using [`tui-textarea`](https://crates.io/crates/tui-textarea) or
> [`tui-input`](https://crates.io/crates/tui-input) crates for more functional text entry UIs.
```shell
cargo run --example=user_input --features=crossterm
```
![User Input][user_input.gif]
## How to update these examples
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
repository.
<!--
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
examples/vhs/generate.bash
```
-->
[barchart.gif]: https://github.com/ratatui-org/ratatui/blob/images/examples/barchart.gif?raw=true
[barchart-grouped.gif]: https://github.com/ratatui-org/ratatui/blob/images/examples/barchart-grouped.gif?raw=true
[block.gif]: https://github.com/ratatui-org/ratatui/blob/images/examples/block.gif?raw=true
[calendar.gif]: https://github.com/ratatui-org/ratatui/blob/images/examples/calendar.gif?raw=true
[canvas.gif]: https://github.com/ratatui-org/ratatui/blob/images/examples/canvas.gif?raw=true
[chart.gif]: https://github.com/ratatui-org/ratatui/blob/images/examples/chart.gif?raw=true
[colors.gif]: https://github.com/ratatui-org/ratatui/blob/images/examples/colors.gif?raw=true
[constraint-explorer.gif]: https://github.com/ratatui-org/ratatui/blob/images/examples/constraint-explorer.gif?raw=true
[constraints.gif]: https://github.com/ratatui-org/ratatui/blob/images/examples/constraints.gif?raw=true
[custom_widget.gif]: https://github.com/ratatui-org/ratatui/blob/images/examples/custom_widget.gif?raw=true
[demo.gif]: https://github.com/ratatui-org/ratatui/blob/images/examples/demo.gif?raw=true
[demo2.gif]: https://github.com/ratatui-org/ratatui/blob/images/examples/demo2.gif?raw=true
[flex.gif]: https://github.com/ratatui-org/ratatui/blob/images/examples/flex.gif?raw=true
[gauge.gif]: https://github.com/ratatui-org/ratatui/blob/images/examples/gauge.gif?raw=true
[hello_world.gif]: https://github.com/ratatui-org/ratatui/blob/images/examples/hello_world.gif?raw=true
[hyperlink.gif]: https://github.com/ratatui-org/ratatui/blob/images/examples/hyperlink.gif?raw=true
[inline.gif]: https://github.com/ratatui-org/ratatui/blob/images/examples/inline.gif?raw=true
[layout.gif]: https://github.com/ratatui-org/ratatui/blob/images/examples/layout.gif?raw=true
[list.gif]: https://github.com/ratatui-org/ratatui/blob/images/examples/list.gif?raw=true
[line_gauge.gif]: https://github.com/ratatui-org/ratatui/blob/images/examples/line_gauge.gif?raw=true
[minimal.gif]: https://github.com/ratatui-org/ratatui/blob/images/examples/minimal.gif?raw=true
[modifiers.gif]: https://github.com/ratatui-org/ratatui/blob/images/examples/modifiers.gif?raw=true
[panic.gif]: https://github.com/ratatui-org/ratatui/blob/images/examples/panic.gif?raw=true
[paragraph.gif]: https://github.com/ratatui-org/ratatui/blob/images/examples/paragraph.gif?raw=true
[popup.gif]: https://github.com/ratatui-org/ratatui/blob/images/examples/popup.gif?raw=true
[ratatui-logo.gif]: https://github.com/ratatui-org/ratatui/blob/images/examples/ratatui-logo.gif?raw=true
[scrollbar.gif]: https://github.com/ratatui-org/ratatui/blob/images/examples/scrollbar.gif?raw=true
[sparkline.gif]: https://github.com/ratatui-org/ratatui/blob/images/examples/sparkline.gif?raw=true
[table.gif]: https://vhs.charm.sh/vhs-6njXBytDf0rwPufUtmSSpI.gif
[tabs.gif]: https://github.com/ratatui-org/ratatui/blob/images/examples/tabs.gif?raw=true
[tracing.gif]: https://github.com/ratatui-org/ratatui/blob/images/examples/tracing.gif?raw=true
[user_input.gif]: https://github.com/ratatui-org/ratatui/blob/images/examples/user_input.gif?raw=true
[alpha version of Ratatui]: https://crates.io/crates/ratatui/versions
[BREAKING-CHANGES.md]: https://github.com/ratatui-org/ratatui/blob/main/BREAKING-CHANGES.md
Shows how to render a calendar with different styles. [Source](./apps/calendar-explorer/).

View File

@@ -0,0 +1,22 @@
[package]
name = "async-github"
version = "0.1.0"
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 = "0.6.3"
crossterm = { workspace = true, features = ["event-stream"] }
octocrab = "0.42.1"
ratatui.workspace = true
tokio = { version = "1.43.0", features = ["rt-multi-thread", "macros"] }
tokio-stream = "0.1.17"

View File

@@ -0,0 +1,9 @@
# 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,9 +1,7 @@
//! # [Ratatui] Async example
//!
//! This example demonstrates how to use Ratatui with widgets that fetch data asynchronously. It
//! uses the `octocrab` crate to fetch a list of pull requests from the GitHub API. You will need an
//! environment variable named `GITHUB_TOKEN` with a valid GitHub personal access token. The token
//! does not need any special permissions.
//! uses the `octocrab` crate to fetch a list of pull requests from the GitHub API.
//!
//! <https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens#creating-a-fine-grained-personal-access-token>
//! <https://github.com/settings/tokens/new> to create a new token (select classic, and no scopes)
@@ -26,98 +24,81 @@
//! See the [examples readme] for more information on finding examples that match the version of the
//! library you are using.
//!
//! [Ratatui]: https://github.com/ratatui-org/ratatui
//! [examples]: https://github.com/ratatui-org/ratatui/blob/main/examples
//! [examples readme]: https://github.com/ratatui-org/ratatui/blob/main/examples/README.md
//! [Ratatui]: https://github.com/ratatui/ratatui
//! [examples]: https://github.com/ratatui/ratatui/blob/main/examples
//! [examples readme]: https://github.com/ratatui/ratatui/blob/main/examples/README.md
use std::{
sync::{Arc, RwLock},
time::Duration,
};
use color_eyre::{eyre::Context, Result, Section};
use futures::StreamExt;
use color_eyre::Result;
use octocrab::{
params::{pulls::Sort, Direction},
OctocrabBuilder, Page,
Page,
};
use ratatui::{
crossterm::event::{Event, EventStream, KeyCode},
layout::Offset,
prelude::{Buffer, Constraint, Line, Modifier, Rect, Stylize},
widgets::{
Block, BorderType, HighlightSpacing, Row, StatefulWidget, Table, TableState, Widget,
},
buffer::Buffer,
crossterm::event::{Event, EventStream, KeyCode, KeyEventKind},
layout::{Constraint, Layout, Rect},
style::{Style, Stylize},
text::Line,
widgets::{Block, HighlightSpacing, Row, StatefulWidget, Table, TableState, Widget},
DefaultTerminal, Frame,
};
use self::terminal::Terminal;
use tokio_stream::StreamExt;
#[tokio::main]
async fn main() -> Result<()> {
color_eyre::install()?;
init_octocrab()?;
let terminal = terminal::init()?;
let terminal = ratatui::init();
let app_result = App::default().run(terminal).await;
terminal::restore();
ratatui::restore();
app_result
}
fn init_octocrab() -> Result<()> {
let token = std::env::var("GITHUB_TOKEN")
.wrap_err("The GITHUB_TOKEN environment variable was not found")
.suggestion(
"Go to https://github.com/settings/tokens/new to create a token, and re-run:
GITHUB_TOKEN=ghp_... cargo run --example async --features crossterm",
)?;
let crab = OctocrabBuilder::new().personal_token(token).build()?;
octocrab::initialise(crab);
Ok(())
}
#[derive(Debug, Default)]
struct App {
should_quit: bool,
pulls: PullRequestsWidget,
pull_requests: PullRequestListWidget,
}
impl App {
const FRAMES_PER_SECOND: f32 = 60.0;
pub async fn run(mut self, mut terminal: Terminal) -> Result<()> {
self.pulls.run();
pub async fn run(mut self, mut terminal: DefaultTerminal) -> Result<()> {
self.pull_requests.run();
let mut interval =
tokio::time::interval(Duration::from_secs_f32(1.0 / Self::FRAMES_PER_SECOND));
let period = Duration::from_secs_f32(1.0 / Self::FRAMES_PER_SECOND);
let mut interval = tokio::time::interval(period);
let mut events = EventStream::new();
while !self.should_quit {
tokio::select! {
_ = interval.tick() => self.draw(&mut terminal)?,
Some(Ok(event)) = events.next() => self.handle_event(&event),
_ = interval.tick() => { terminal.draw(|frame| self.draw(frame))?; },
Some(Ok(event)) = events.next() => self.handle_event(&event),
}
}
Ok(())
}
fn draw(&self, terminal: &mut Terminal) -> Result<()> {
terminal.draw(|frame| {
let area = frame.area();
frame.render_widget(
Line::from("ratatui async example").centered().cyan().bold(),
area,
);
let area = area.offset(Offset { x: 0, y: 1 }).intersection(area);
frame.render_widget(&self.pulls, area);
})?;
Ok(())
fn draw(&self, frame: &mut Frame) {
let vertical = Layout::vertical([Constraint::Length(1), Constraint::Fill(1)]);
let [title_area, body_area] = vertical.areas(frame.area());
let title = Line::from("Ratatui async example").centered().bold();
frame.render_widget(title, title_area);
frame.render_widget(&self.pull_requests, body_area);
}
fn handle_event(&mut self, event: &Event) {
if let Event::Key(event) = event {
match event.code {
KeyCode::Char('q') => self.should_quit = true,
KeyCode::Char('j') => self.pulls.scroll_down(),
KeyCode::Char('k') => self.pulls.scroll_up(),
_ => {}
if let Event::Key(key) = event {
if key.kind == KeyEventKind::Press {
match key.code {
KeyCode::Char('q') | KeyCode::Esc => self.should_quit = true,
KeyCode::Char('j') | KeyCode::Down => self.pull_requests.scroll_down(),
KeyCode::Char('k') | KeyCode::Up => self.pull_requests.scroll_up(),
_ => {}
}
}
}
}
@@ -126,19 +107,19 @@ impl App {
/// A widget that displays a list of pull requests.
///
/// This is an async widget that fetches the list of pull requests from the GitHub API. It contains
/// an inner `Arc<RwLock<PullRequests>>` that holds the state of the widget. Cloning the widget
/// will clone the Arc, so you can pass it around to other threads, and this is used to spawn a
/// background task to fetch the pull requests.
/// an inner `Arc<RwLock<PullRequestListState>>` that holds the state of the widget. Cloning the
/// widget will clone the Arc, so you can pass it around to other threads, and this is used to spawn
/// a background task to fetch the pull requests.
#[derive(Debug, Clone, Default)]
struct PullRequestsWidget {
inner: Arc<RwLock<PullRequests>>,
selected_index: usize, // no need to lock this since it's only accessed by the main thread
struct PullRequestListWidget {
state: Arc<RwLock<PullRequestListState>>,
}
#[derive(Debug, Default)]
struct PullRequests {
pulls: Vec<PullRequest>,
struct PullRequestListState {
pull_requests: Vec<PullRequest>,
loading_state: LoadingState,
table_state: TableState,
}
#[derive(Debug, Clone)]
@@ -157,7 +138,7 @@ enum LoadingState {
Error(String),
}
impl PullRequestsWidget {
impl PullRequestListWidget {
/// Start fetching the pull requests in the background.
///
/// This method spawns a background task that fetches the pull requests from the GitHub API.
@@ -172,7 +153,7 @@ impl PullRequestsWidget {
// messages to refresh on demand, or with an interval timer to refresh every N seconds
self.set_loading_state(LoadingState::Loading);
match octocrab::instance()
.pulls("ratatui-org", "ratatui")
.pulls("ratatui", "ratatui")
.list()
.sort(Sort::Updated)
.direction(Direction::Descending)
@@ -185,9 +166,12 @@ impl PullRequestsWidget {
}
fn on_load(&self, page: &Page<OctoPullRequest>) {
let prs = page.items.iter().map(Into::into);
let mut inner = self.inner.write().unwrap();
inner.loading_state = LoadingState::Loaded;
inner.pulls.extend(prs);
let mut state = self.state.write().unwrap();
state.loading_state = LoadingState::Loaded;
state.pull_requests.extend(prs);
if !state.pull_requests.is_empty() {
state.table_state.select(Some(0));
}
}
fn on_err(&self, err: &octocrab::Error) {
@@ -195,15 +179,15 @@ impl PullRequestsWidget {
}
fn set_loading_state(&self, state: LoadingState) {
self.inner.write().unwrap().loading_state = state;
self.state.write().unwrap().loading_state = state;
}
fn scroll_down(&mut self) {
self.selected_index = self.selected_index.saturating_add(1);
fn scroll_down(&self) {
self.state.write().unwrap().table_state.scroll_down_by(1);
}
fn scroll_up(&mut self) {
self.selected_index = self.selected_index.saturating_sub(1);
fn scroll_up(&self) {
self.state.write().unwrap().table_state.scroll_up_by(1);
}
}
@@ -223,19 +207,19 @@ impl From<&OctoPullRequest> for PullRequest {
}
}
impl Widget for &PullRequestsWidget {
impl Widget for &PullRequestListWidget {
fn render(self, area: Rect, buf: &mut Buffer) {
let inner = self.inner.read().unwrap();
let mut state = self.state.write().unwrap();
// a block with a right aligned title with the loading state
let loading_state = Line::from(format!("{:?}", inner.loading_state)).right_aligned();
// a block with a right aligned title with the loading state on the right
let loading_state = Line::from(format!("{:?}", state.loading_state)).right_aligned();
let block = Block::bordered()
.border_type(BorderType::Rounded)
.title("Pull Requests")
.title(loading_state);
.title(loading_state)
.title_bottom("j/k to scroll, q to quit");
// a table with the list of pull requests
let rows = inner.pulls.iter();
let rows = state.pull_requests.iter();
let widths = [
Constraint::Length(5),
Constraint::Fill(1),
@@ -245,10 +229,9 @@ impl Widget for &PullRequestsWidget {
.block(block)
.highlight_spacing(HighlightSpacing::Always)
.highlight_symbol(">>")
.highlight_style(Modifier::REVERSED);
let mut table_state = TableState::new().with_selected(self.selected_index);
.row_highlight_style(Style::new().on_blue());
StatefulWidget::render(table, area, buf, &mut table_state);
StatefulWidget::render(table, area, buf, &mut state.table_state);
}
}
@@ -258,42 +241,3 @@ impl From<&PullRequest> for Row<'_> {
Row::new(vec![pr.id, pr.title, pr.url])
}
}
mod terminal {
use std::io;
use crossterm::{
execute,
terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
};
use ratatui::prelude::{CrosstermBackend, Terminal as RatatuiTerminal};
/// A type alias for the terminal type used in this example.
pub type Terminal = RatatuiTerminal<CrosstermBackend<io::Stdout>>;
pub fn init() -> io::Result<Terminal> {
set_panic_hook();
enable_raw_mode()?;
execute!(io::stdout(), EnterAlternateScreen)?;
let backend = CrosstermBackend::new(io::stdout());
Terminal::new(backend)
}
fn set_panic_hook() {
let hook = std::panic::take_hook();
std::panic::set_hook(Box::new(move |info| {
restore();
hook(info);
}));
}
/// Restores the terminal to its original state.
pub fn restore() {
if let Err(err) = disable_raw_mode() {
eprintln!("error disabling raw mode: {err}");
}
if let Err(err) = execute!(io::stdout(), LeaveAlternateScreen) {
eprintln!("error leaving alternate screen: {err}");
}
}
}

View File

@@ -0,0 +1,15 @@
[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 = { version = "0.3.37", features = ["formatting", "parsing"] }
[lints]
workspace = true

View File

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

@@ -0,0 +1,248 @@
//! 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, Event, KeyCode, KeyEventKind};
use ratatui::{
layout::{Constraint, Layout, Margin, Rect},
style::{Color, Modifier, Style, Stylize},
text::{Line, Text},
widgets::calendar::{CalendarEventStore, Monthly},
DefaultTerminal, Frame,
};
use time::{ext::NumericalDuration, Date, Month, OffsetDateTime};
fn main() -> Result<()> {
color_eyre::install()?;
let terminal = ratatui::init();
let result = run(terminal);
ratatui::restore();
result
}
/// Run the application.
fn run(mut terminal: 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 Event::Key(key) = event::read()? {
if key.kind == KeyEventKind::Press {
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 = previous_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 previous_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()
}
}
/// Draw 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 vertical = Layout::vertical([
Constraint::Length(header.height() as u16),
Constraint::Fill(1),
]);
let [text_area, area] = vertical.areas(frame.area());
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 area = area.inner(Margin {
vertical: 1,
horizontal: 1,
});
let rows = Layout::vertical([Constraint::Ratio(1, 3); 3]).split(area);
let areas = rows.iter().flat_map(|row| {
Layout::horizontal([Constraint::Ratio(1, 4); 4])
.split(*row)
.to_vec()
});
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

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

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

@@ -0,0 +1,265 @@
/// A Ratatui example that demonstrates how to draw on a canvas.
///
/// This example demonstrates how to draw various shapes such as rectangles, circles, and lines
/// on a canvas. It also demonstrates how to draw a map.
///
/// 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,
time::{Duration, Instant},
};
use color_eyre::Result;
use crossterm::{
event::{DisableMouseCapture, EnableMouseCapture, KeyEventKind},
ExecutableCommand,
};
use itertools::Itertools;
use ratatui::{
crossterm::event::{self, Event, KeyCode, MouseEventKind},
layout::{Constraint, Layout, Position, Rect},
style::{Color, Stylize},
symbols::Marker,
text::Text,
widgets::{
canvas::{Canvas, Circle, Map, MapResolution, Points, Rectangle},
Block, Widget,
},
DefaultTerminal, Frame,
};
fn main() -> Result<()> {
color_eyre::install()?;
stdout().execute(EnableMouseCapture)?;
let terminal = ratatui::init();
let app_result = App::new().run(terminal);
ratatui::restore();
stdout().execute(DisableMouseCapture)?;
app_result
}
struct App {
exit: bool,
x: f64,
y: f64,
ball: Circle,
playground: Rect,
vx: f64,
vy: f64,
marker: Marker,
points: Vec<Position>,
is_drawing: bool,
}
impl App {
const fn new() -> Self {
Self {
exit: false,
x: 0.0,
y: 0.0,
ball: Circle {
x: 20.0,
y: 40.0,
radius: 10.0,
color: Color::Yellow,
},
playground: Rect::new(10, 10, 200, 100),
vx: 1.0,
vy: 1.0,
marker: Marker::Dot,
points: vec![],
is_drawing: false,
}
}
pub fn run(mut self, mut terminal: DefaultTerminal) -> Result<()> {
let tick_rate = Duration::from_millis(16);
let mut last_tick = Instant::now();
while !self.exit {
terminal.draw(|frame| self.draw(frame))?;
let timeout = tick_rate.saturating_sub(last_tick.elapsed());
if event::poll(timeout)? {
match event::read()? {
Event::Key(key) => self.handle_key_press(key),
Event::Mouse(event) => self.handle_mouse_event(event),
_ => (),
}
}
if last_tick.elapsed() >= tick_rate {
self.on_tick();
last_tick = Instant::now();
}
}
Ok(())
}
fn handle_key_press(&mut self, key: event::KeyEvent) {
if key.kind != KeyEventKind::Press {
return;
}
match key.code {
KeyCode::Char('q') => self.exit = true,
KeyCode::Down | KeyCode::Char('j') => self.y += 1.0,
KeyCode::Up | KeyCode::Char('k') => self.y -= 1.0,
KeyCode::Right | KeyCode::Char('l') => self.x += 1.0,
KeyCode::Left | KeyCode::Char('h') => self.x -= 1.0,
KeyCode::Enter => {
self.marker = match self.marker {
Marker::Dot => Marker::Braille,
Marker::Braille => Marker::Block,
Marker::Block => Marker::HalfBlock,
Marker::HalfBlock => Marker::Bar,
Marker::Bar => Marker::Dot,
};
}
_ => {}
}
}
fn handle_mouse_event(&mut self, event: event::MouseEvent) {
match event.kind {
MouseEventKind::Down(_) => self.is_drawing = true,
MouseEventKind::Up(_) => self.is_drawing = false,
MouseEventKind::Drag(_) => {
self.points.push(Position::new(event.column, event.row));
}
_ => {}
}
}
fn on_tick(&mut self) {
// bounce the ball by flipping the velocity vector
let ball = &self.ball;
let playground = self.playground;
if ball.x - ball.radius < f64::from(playground.left())
|| ball.x + ball.radius > f64::from(playground.right())
{
self.vx = -self.vx;
}
if ball.y - ball.radius < f64::from(playground.top())
|| ball.y + ball.radius > f64::from(playground.bottom())
{
self.vy = -self.vy;
}
self.ball.x += self.vx;
self.ball.y += self.vy;
}
fn draw(&self, frame: &mut Frame) {
let header = Text::from_iter([
"Canvas Example".bold(),
"<q> Quit | <enter> Change Marker | <hjkl> Move".into(),
]);
let vertical = Layout::vertical([
Constraint::Length(header.height() as u16),
Constraint::Percentage(50),
Constraint::Percentage(50),
]);
let [text_area, up, down] = vertical.areas(frame.area());
frame.render_widget(header.centered(), text_area);
let horizontal =
Layout::horizontal([Constraint::Percentage(50), Constraint::Percentage(50)]);
let [draw, pong] = horizontal.areas(up);
let [map, boxes] = horizontal.areas(down);
frame.render_widget(self.map_canvas(), map);
frame.render_widget(self.draw_canvas(draw), draw);
frame.render_widget(self.pong_canvas(), pong);
frame.render_widget(self.boxes_canvas(boxes), boxes);
}
fn map_canvas(&self) -> impl Widget + '_ {
Canvas::default()
.block(Block::bordered().title("World"))
.marker(self.marker)
.paint(|ctx| {
ctx.draw(&Map {
color: Color::Green,
resolution: MapResolution::High,
});
ctx.print(self.x, -self.y, "You are here".yellow());
})
.x_bounds([-180.0, 180.0])
.y_bounds([-90.0, 90.0])
}
fn draw_canvas(&self, area: Rect) -> impl Widget + '_ {
Canvas::default()
.block(Block::bordered().title("Draw here"))
.marker(self.marker)
.x_bounds([0.0, f64::from(area.width)])
.y_bounds([0.0, f64::from(area.height)])
.paint(move |ctx| {
let points = self
.points
.iter()
.map(|p| {
(
f64::from(p.x) - f64::from(area.left()),
f64::from(area.bottom()) - f64::from(p.y),
)
})
.collect_vec();
ctx.draw(&Points {
coords: &points,
color: Color::White,
});
})
}
fn pong_canvas(&self) -> impl Widget + '_ {
Canvas::default()
.block(Block::bordered().title("Pong"))
.marker(self.marker)
.paint(|ctx| {
ctx.draw(&self.ball);
})
.x_bounds([10.0, 210.0])
.y_bounds([10.0, 110.0])
}
fn boxes_canvas(&self, area: Rect) -> impl Widget {
let left = 0.0;
let right = f64::from(area.width);
let bottom = 0.0;
let top = f64::from(area.height).mul_add(2.0, -4.0);
Canvas::default()
.block(Block::bordered().title("Rects"))
.marker(self.marker)
.x_bounds([left, right])
.y_bounds([bottom, top])
.paint(|ctx| {
for i in 0..=11 {
ctx.draw(&Rectangle {
x: f64::from(i * i + 3 * i) / 2.0 + 2.0,
y: 2.0,
width: f64::from(i),
height: f64::from(i),
color: Color::Red,
});
ctx.draw(&Rectangle {
x: f64::from(i * i + 3 * i) / 2.0 + 2.0,
y: 21.0,
width: f64::from(i),
height: f64::from(i),
color: Color::Blue,
});
}
for i in 0..100 {
if i % 10 != 0 {
ctx.print(f64::from(i) + 1.0, 0.0, format!("{i}", i = i % 10));
}
if i % 2 == 0 && i % 10 != 0 {
ctx.print(0.0, f64::from(i), format!("{i}", i = i % 10));
}
}
})
}
}

View File

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

@@ -0,0 +1,9 @@
# 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,39 +1,42 @@
//! # [Ratatui] Chart example
//!
//! The latest version of this example is available in the [examples] folder in the repository.
//!
//! Please note that the examples are designed to be run against the `main` branch of the Github
//! repository. This means that you may not be able to compile with the latest release version on
//! crates.io, or the one that you have installed locally.
//!
//! See the [examples readme] for more information on finding examples that match the version of the
//! library you are using.
//!
//! [Ratatui]: https://github.com/ratatui-org/ratatui
//! [examples]: https://github.com/ratatui-org/ratatui/blob/main/examples
//! [examples readme]: https://github.com/ratatui-org/ratatui/blob/main/examples/README.md
use std::{
error::Error,
io,
time::{Duration, Instant},
};
/// A Ratatui example that demonstrates how to handle charts.
///
/// This example demonstrates how to draw various types of charts such as line, bar, and
/// scatter charts.
///
/// 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::time::{Duration, Instant};
use color_eyre::Result;
use ratatui::{
backend::{Backend, CrosstermBackend},
crossterm::{
event::{self, DisableMouseCapture, EnableMouseCapture, Event, KeyCode},
execute,
terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
},
layout::{Alignment, Constraint, Layout, Rect},
crossterm::event::{self, Event, KeyCode},
layout::{Constraint, Layout, Rect},
style::{Color, Modifier, Style, Stylize},
symbols::{self, Marker},
text::Span,
widgets::{block::Title, Axis, Block, Chart, Dataset, GraphType, LegendPosition},
Frame, Terminal,
text::{Line, Span},
widgets::{Axis, Block, Chart, Dataset, GraphType, LegendPosition},
DefaultTerminal, Frame,
};
fn main() -> Result<()> {
color_eyre::install()?;
let terminal = ratatui::init();
let app_result = App::new().run(terminal);
ratatui::restore();
app_result
}
struct App {
signal1: SinSignal,
data1: Vec<(f64, f64)>,
signal2: SinSignal,
data2: Vec<(f64, f64)>,
window: [f64; 2],
}
#[derive(Clone)]
struct SinSignal {
x: f64,
@@ -62,14 +65,6 @@ impl Iterator for SinSignal {
}
}
struct App {
signal1: SinSignal,
data1: Vec<(f64, f64)>,
signal2: SinSignal,
data2: Vec<(f64, f64)>,
window: [f64; 2],
}
impl App {
fn new() -> Self {
let mut signal1 = SinSignal::new(0.2, 3.0, 18.0);
@@ -85,6 +80,27 @@ impl App {
}
}
fn run(mut self, mut terminal: DefaultTerminal) -> Result<()> {
let tick_rate = Duration::from_millis(250);
let mut last_tick = Instant::now();
loop {
terminal.draw(|frame| self.draw(frame))?;
let timeout = tick_rate.saturating_sub(last_tick.elapsed());
if event::poll(timeout)? {
if let Event::Key(key) = event::read()? {
if key.code == KeyCode::Char('q') {
return Ok(());
}
}
}
if last_tick.elapsed() >= tick_rate {
self.on_tick();
last_tick = Instant::now();
}
}
}
fn on_tick(&mut self) {
self.data1.drain(0..5);
self.data1.extend(self.signal1.by_ref().take(5));
@@ -95,118 +111,65 @@ impl App {
self.window[0] += 1.0;
self.window[1] += 1.0;
}
}
fn main() -> Result<(), Box<dyn Error>> {
// setup terminal
enable_raw_mode()?;
let mut stdout = io::stdout();
execute!(stdout, EnterAlternateScreen, EnableMouseCapture)?;
let backend = CrosstermBackend::new(stdout);
let mut terminal = Terminal::new(backend)?;
fn draw(&self, frame: &mut Frame) {
let [top, bottom] = Layout::vertical([Constraint::Fill(1); 2]).areas(frame.area());
let [animated_chart, bar_chart] =
Layout::horizontal([Constraint::Fill(1), Constraint::Length(29)]).areas(top);
let [line_chart, scatter] = Layout::horizontal([Constraint::Fill(1); 2]).areas(bottom);
// create app and run it
let tick_rate = Duration::from_millis(250);
let app = App::new();
let res = run_app(&mut terminal, app, tick_rate);
// restore terminal
disable_raw_mode()?;
execute!(
terminal.backend_mut(),
LeaveAlternateScreen,
DisableMouseCapture
)?;
terminal.show_cursor()?;
if let Err(err) = res {
println!("{err:?}");
self.render_animated_chart(frame, animated_chart);
render_barchart(frame, bar_chart);
render_line_chart(frame, line_chart);
render_scatter(frame, scatter);
}
Ok(())
}
fn render_animated_chart(&self, frame: &mut Frame, area: Rect) {
let x_labels = vec![
Span::styled(
format!("{}", self.window[0]),
Style::default().add_modifier(Modifier::BOLD),
),
Span::raw(format!("{}", (self.window[0] + self.window[1]) / 2.0)),
Span::styled(
format!("{}", self.window[1]),
Style::default().add_modifier(Modifier::BOLD),
),
];
let datasets = vec![
Dataset::default()
.name("data2")
.marker(symbols::Marker::Dot)
.style(Style::default().fg(Color::Cyan))
.data(&self.data1),
Dataset::default()
.name("data3")
.marker(symbols::Marker::Braille)
.style(Style::default().fg(Color::Yellow))
.data(&self.data2),
];
fn run_app<B: Backend>(
terminal: &mut Terminal<B>,
mut app: App,
tick_rate: Duration,
) -> io::Result<()> {
let mut last_tick = Instant::now();
loop {
terminal.draw(|f| ui(f, &app))?;
let chart = Chart::new(datasets)
.block(Block::bordered())
.x_axis(
Axis::default()
.title("X Axis")
.style(Style::default().fg(Color::Gray))
.labels(x_labels)
.bounds(self.window),
)
.y_axis(
Axis::default()
.title("Y Axis")
.style(Style::default().fg(Color::Gray))
.labels(["-20".bold(), "0".into(), "20".bold()])
.bounds([-20.0, 20.0]),
);
let timeout = tick_rate.saturating_sub(last_tick.elapsed());
if crossterm::event::poll(timeout)? {
if let Event::Key(key) = event::read()? {
if key.code == KeyCode::Char('q') {
return Ok(());
}
}
}
if last_tick.elapsed() >= tick_rate {
app.on_tick();
last_tick = Instant::now();
}
frame.render_widget(chart, area);
}
}
fn ui(frame: &mut Frame, app: &App) {
let [top, bottom] = Layout::vertical([Constraint::Fill(1); 2]).areas(frame.area());
let [animated_chart, bar_chart] =
Layout::horizontal([Constraint::Fill(1), Constraint::Length(29)]).areas(top);
let [line_chart, scatter] = Layout::horizontal([Constraint::Fill(1); 2]).areas(bottom);
render_animated_chart(frame, animated_chart, app);
render_barchart(frame, bar_chart);
render_line_chart(frame, line_chart);
render_scatter(frame, scatter);
}
fn render_animated_chart(f: &mut Frame, area: Rect, app: &App) {
let x_labels = vec![
Span::styled(
format!("{}", app.window[0]),
Style::default().add_modifier(Modifier::BOLD),
),
Span::raw(format!("{}", (app.window[0] + app.window[1]) / 2.0)),
Span::styled(
format!("{}", app.window[1]),
Style::default().add_modifier(Modifier::BOLD),
),
];
let datasets = vec![
Dataset::default()
.name("data2")
.marker(symbols::Marker::Dot)
.style(Style::default().fg(Color::Cyan))
.data(&app.data1),
Dataset::default()
.name("data3")
.marker(symbols::Marker::Braille)
.style(Style::default().fg(Color::Yellow))
.data(&app.data2),
];
let chart = Chart::new(datasets)
.block(Block::bordered())
.x_axis(
Axis::default()
.title("X Axis")
.style(Style::default().fg(Color::Gray))
.labels(x_labels)
.bounds(app.window),
)
.y_axis(
Axis::default()
.title("Y Axis")
.style(Style::default().fg(Color::Gray))
.labels(["-20".bold(), "0".into(), "20".bold()])
.bounds([-20.0, 20.0]),
);
f.render_widget(chart, area);
}
fn render_barchart(frame: &mut Frame, bar_chart: Rect) {
let dataset = Dataset::default()
.marker(symbols::Marker::HalfBlock)
@@ -228,13 +191,7 @@ fn render_barchart(frame: &mut Frame, bar_chart: Rect) {
]);
let chart = Chart::new(vec![dataset])
.block(
Block::bordered().title(
Title::default()
.content("Bar chart".cyan().bold())
.alignment(Alignment::Center),
),
)
.block(Block::bordered().title_top(Line::from("Bar chart").cyan().bold().centered()))
.x_axis(
Axis::default()
.style(Style::default().gray())
@@ -252,7 +209,7 @@ fn render_barchart(frame: &mut Frame, bar_chart: Rect) {
frame.render_widget(chart, bar_chart);
}
fn render_line_chart(f: &mut Frame, area: Rect) {
fn render_line_chart(frame: &mut Frame, area: Rect) {
let datasets = vec![Dataset::default()
.name("Line from only 2 points".italic())
.marker(symbols::Marker::Braille)
@@ -261,13 +218,7 @@ fn render_line_chart(f: &mut Frame, area: Rect) {
.data(&[(1., 1.), (4., 4.)])];
let chart = Chart::new(datasets)
.block(
Block::bordered().title(
Title::default()
.content("Line chart".cyan().bold())
.alignment(Alignment::Center),
),
)
.block(Block::bordered().title(Line::from("Line chart").cyan().bold().centered()))
.x_axis(
Axis::default()
.title("X Axis")
@@ -285,10 +236,10 @@ fn render_line_chart(f: &mut Frame, area: Rect) {
.legend_position(Some(LegendPosition::TopLeft))
.hidden_legend_constraints((Constraint::Ratio(1, 2), Constraint::Ratio(1, 2)));
f.render_widget(chart, area);
frame.render_widget(chart, area);
}
fn render_scatter(f: &mut Frame, area: Rect) {
fn render_scatter(frame: &mut Frame, area: Rect) {
let datasets = vec![
Dataset::default()
.name("Heavy")
@@ -311,13 +262,7 @@ fn render_scatter(f: &mut Frame, area: Rect) {
];
let chart = Chart::new(datasets)
.block(
Block::bordered().title(
Title::default()
.content("Scatter chart".cyan().bold())
.alignment(Alignment::Center),
),
)
.block(Block::bordered().title(Line::from("Scatter chart").cyan().bold().centered()))
.x_axis(
Axis::default()
.title("Year")
@@ -334,7 +279,7 @@ fn render_scatter(f: &mut Frame, area: Rect) {
)
.hidden_legend_constraints((Constraint::Ratio(1, 2), Constraint::Ratio(1, 2)));
f.render_widget(chart, area);
frame.render_widget(chart, area);
}
// Data from https://ourworldindata.org/space-exploration-satellites

View File

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

@@ -0,0 +1,9 @@
# 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,70 +1,44 @@
//! # [Ratatui] Colors example
//! A Ratatui example that demonstrates how to handle colors.
//!
//! The latest version of this example is available in the [examples] folder in the repository.
//! This example shows all the colors supported by Ratatui. It will render a grid of foreground
//! and background colors with their names and indexes.
//!
//! Please note that the examples are designed to be run against the `main` branch of the Github
//! repository. This means that you may not be able to compile with the latest release version on
//! crates.io, or the one that you have installed locally.
//! 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.
//!
//! See the [examples readme] for more information on finding examples that match the version of the
//! library you are using.
//!
//! [Ratatui]: https://github.com/ratatui-org/ratatui
//! [examples]: https://github.com/ratatui-org/ratatui/blob/main/examples
//! [examples readme]: https://github.com/ratatui-org/ratatui/blob/main/examples/README.md
// This example shows all the colors supported by ratatui. It will render a grid of foreground
// and background colors with their names and indexes.
use std::{
error::Error,
io::{self, Stdout},
result,
time::Duration,
};
//! [`latest`]: https://github.com/ratatui/ratatui/tree/latest
use color_eyre::Result;
use itertools::Itertools;
use ratatui::{
backend::{Backend, CrosstermBackend},
crossterm::{
event::{self, Event, KeyCode},
execute,
terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
},
crossterm::event::{self, Event, KeyCode, KeyEventKind},
layout::{Alignment, Constraint, Layout, Rect},
style::{Color, Style, Stylize},
text::Line,
widgets::{Block, Borders, Paragraph},
Frame, Terminal,
DefaultTerminal, Frame,
};
type Result<T> = result::Result<T, Box<dyn Error>>;
fn main() -> Result<()> {
let mut terminal = setup_terminal()?;
let res = run_app(&mut terminal);
restore_terminal(terminal)?;
if let Err(err) = res {
eprintln!("{err:?}");
}
Ok(())
color_eyre::install()?;
let terminal = ratatui::init();
let app_result = run(terminal);
ratatui::restore();
app_result
}
fn run_app<B: Backend>(terminal: &mut Terminal<B>) -> io::Result<()> {
fn run(mut terminal: DefaultTerminal) -> Result<()> {
loop {
terminal.draw(ui)?;
if event::poll(Duration::from_millis(250))? {
if let Event::Key(key) = event::read()? {
if key.code == KeyCode::Char('q') {
return Ok(());
}
terminal.draw(draw)?;
if let Event::Key(key) = event::read()? {
if key.kind == KeyEventKind::Press && key.code == KeyCode::Char('q') {
return Ok(());
}
}
}
}
fn ui(frame: &mut Frame) {
fn draw(frame: &mut Frame) {
let layout = Layout::vertical([
Constraint::Length(30),
Constraint::Length(17),
@@ -117,19 +91,16 @@ fn render_fg_named_colors(frame: &mut Frame, bg: Color, area: Rect) {
let inner = block.inner(area);
frame.render_widget(block, area);
let layout = Layout::vertical([Constraint::Length(1); 2])
.split(inner)
.iter()
.flat_map(|area| {
Layout::horizontal([Constraint::Ratio(1, 8); 8])
.split(*area)
.to_vec()
})
.collect_vec();
for (i, &fg) in NAMED_COLORS.iter().enumerate() {
let vertical = Layout::vertical([Constraint::Length(1); 2]).split(inner);
let areas = vertical.iter().flat_map(|area| {
Layout::horizontal([Constraint::Ratio(1, 8); 8])
.split(*area)
.to_vec()
});
for (fg, area) in NAMED_COLORS.into_iter().zip(areas) {
let color_name = fg.to_string();
let paragraph = Paragraph::new(color_name).fg(fg).bg(bg);
frame.render_widget(paragraph, layout[i]);
frame.render_widget(paragraph, area);
}
}
@@ -138,19 +109,16 @@ fn render_bg_named_colors(frame: &mut Frame, fg: Color, area: Rect) {
let inner = block.inner(area);
frame.render_widget(block, area);
let layout = Layout::vertical([Constraint::Length(1); 2])
.split(inner)
.iter()
.flat_map(|area| {
Layout::horizontal([Constraint::Ratio(1, 8); 8])
.split(*area)
.to_vec()
})
.collect_vec();
for (i, &bg) in NAMED_COLORS.iter().enumerate() {
let vertical = Layout::vertical([Constraint::Length(1); 2]).split(inner);
let areas = vertical.iter().flat_map(|area| {
Layout::horizontal([Constraint::Ratio(1, 8); 8])
.split(*area)
.to_vec()
});
for (bg, area) in NAMED_COLORS.into_iter().zip(areas) {
let color_name = bg.to_string();
let paragraph = Paragraph::new(color_name).fg(fg).bg(bg);
frame.render_widget(paragraph, layout[i]);
frame.render_widget(paragraph, area);
}
}
@@ -271,20 +239,3 @@ fn render_indexed_grayscale(frame: &mut Frame, area: Rect) {
frame.render_widget(paragraph, layout[i as usize - 232]);
}
}
fn setup_terminal() -> Result<Terminal<CrosstermBackend<Stdout>>> {
enable_raw_mode()?;
let mut stdout = io::stdout();
execute!(stdout, EnterAlternateScreen)?;
let backend = CrosstermBackend::new(stdout);
let mut terminal = Terminal::new(backend)?;
terminal.hide_cursor()?;
Ok(terminal)
}
fn restore_terminal(mut terminal: Terminal<CrosstermBackend<Stdout>>) -> Result<()> {
disable_raw_mode()?;
execute!(terminal.backend_mut(), LeaveAlternateScreen)?;
terminal.show_cursor()?;
Ok(())
}

View File

@@ -0,0 +1,17 @@
[package]
name = "demo"
publish = false
license.workspace = true
edition.workspace = true
rust-version.workspace = true
[features]
default = ["crossterm"]
crossterm = ["ratatui/crossterm"]
termion = ["ratatui/termion"]
termwiz = ["ratatui/termwiz"]
[dependencies]
clap = { version = "4.5.23", features = ["derive"] }
rand = "0.8.5"
ratatui.workspace = true

View File

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

@@ -122,7 +122,7 @@ pub struct TabsState<'a> {
}
impl<'a> TabsState<'a> {
pub fn new(titles: Vec<&'a str>) -> Self {
pub const fn new(titles: Vec<&'a str>) -> Self {
Self { titles, index: 0 }
}
pub fn next(&mut self) {

View File

@@ -26,7 +26,7 @@ pub fn run(tick_rate: Duration, enhanced_graphics: bool) -> Result<(), Box<dyn E
// create app and run it
let app = App::new("Crossterm Demo", enhanced_graphics);
let res = run_app(&mut terminal, app, tick_rate);
let app_result = run_app(&mut terminal, app, tick_rate);
// restore terminal
disable_raw_mode()?;
@@ -37,7 +37,7 @@ pub fn run(tick_rate: Duration, enhanced_graphics: bool) -> Result<(), Box<dyn E
)?;
terminal.show_cursor()?;
if let Err(err) = res {
if let Err(err) = app_result {
println!("{err:?}");
}
@@ -51,10 +51,10 @@ fn run_app<B: Backend>(
) -> io::Result<()> {
let mut last_tick = Instant::now();
loop {
terminal.draw(|f| ui::draw(f, &mut app))?;
terminal.draw(|frame| ui::draw(frame, &mut app))?;
let timeout = tick_rate.saturating_sub(last_tick.elapsed());
if crossterm::event::poll(timeout)? {
if event::poll(timeout)? {
if let Event::Key(key) = event::read()? {
if key.kind == KeyEventKind::Press {
match key.code {

View File

@@ -9,18 +9,18 @@
//! See the [examples readme] for more information on finding examples that match the version of the
//! library you are using.
//!
//! [Ratatui]: https://github.com/ratatui-org/ratatui
//! [examples]: https://github.com/ratatui-org/ratatui/blob/main/examples
//! [examples readme]: https://github.com/ratatui-org/ratatui/blob/main/examples/README.md
//! [Ratatui]: https://github.com/ratatui/ratatui
//! [examples]: https://github.com/ratatui/ratatui/blob/main/examples
//! [examples readme]: https://github.com/ratatui/ratatui/blob/main/examples/README.md
use std::{error::Error, time::Duration};
use argh::FromArgs;
use clap::Parser;
mod app;
#[cfg(feature = "crossterm")]
mod crossterm;
#[cfg(feature = "termion")]
#[cfg(all(not(windows), feature = "termion"))]
mod termion;
#[cfg(feature = "termwiz")]
mod termwiz;
@@ -28,24 +28,29 @@ mod termwiz;
mod ui;
/// Demo
#[derive(Debug, FromArgs)]
#[derive(Debug, Parser)]
struct Cli {
/// time in ms between two ticks.
#[argh(option, default = "250")]
#[arg(short, long, default_value_t = 250)]
tick_rate: u64,
/// whether unicode symbols are used to improve the overall look of the app
#[argh(option, default = "true")]
enhanced_graphics: bool,
#[arg(short, long, default_value_t = true)]
unicode: bool,
}
fn main() -> Result<(), Box<dyn Error>> {
let cli: Cli = argh::from_env();
let cli = Cli::parse();
let tick_rate = Duration::from_millis(cli.tick_rate);
#[cfg(feature = "crossterm")]
crate::crossterm::run(tick_rate, cli.enhanced_graphics)?;
#[cfg(feature = "termion")]
crate::termion::run(tick_rate, cli.enhanced_graphics)?;
#[cfg(feature = "termwiz")]
crate::termwiz::run(tick_rate, cli.enhanced_graphics)?;
crate::crossterm::run(tick_rate, cli.unicode)?;
#[cfg(all(not(windows), feature = "termion", not(feature = "crossterm")))]
crate::termion::run(tick_rate, cli.unicode)?;
#[cfg(all(
feature = "termwiz",
not(feature = "crossterm"),
not(feature = "termion")
))]
crate::termwiz::run(tick_rate, cli.unicode)?;
Ok(())
}

View File

@@ -1,3 +1,4 @@
#![allow(dead_code)]
use std::{error::Error, io, sync::mpsc, thread, time::Duration};
use ratatui::{
@@ -38,7 +39,7 @@ fn run_app<B: Backend>(
) -> Result<(), Box<dyn Error>> {
let events = events(tick_rate);
loop {
terminal.draw(|f| ui::draw(f, &mut app))?;
terminal.draw(|frame| ui::draw(frame, &mut app))?;
match events.recv()? {
Event::Input(key) => match key {

View File

@@ -1,3 +1,4 @@
#![allow(dead_code)]
use std::{
error::Error,
time::{Duration, Instant},
@@ -21,12 +22,12 @@ pub fn run(tick_rate: Duration, enhanced_graphics: bool) -> Result<(), Box<dyn E
// create app and run it
let app = App::new("Termwiz Demo", enhanced_graphics);
let res = run_app(&mut terminal, app, tick_rate);
let app_result = run_app(&mut terminal, app, tick_rate);
terminal.show_cursor()?;
terminal.flush()?;
if let Err(err) = res {
if let Err(err) = app_result {
println!("{err:?}");
}
@@ -40,7 +41,7 @@ fn run_app(
) -> Result<(), Box<dyn Error>> {
let mut last_tick = Instant::now();
loop {
terminal.draw(|f| ui::draw(f, &mut app))?;
terminal.draw(|frame| ui::draw(frame, &mut app))?;
let timeout = tick_rate.saturating_sub(last_tick.elapsed());
if let Some(input) = terminal

View File

@@ -13,8 +13,8 @@ use ratatui::{
use crate::app::App;
pub fn draw(f: &mut Frame, app: &mut App) {
let chunks = Layout::vertical([Constraint::Length(3), Constraint::Min(0)]).split(f.area());
pub fn draw(frame: &mut Frame, app: &mut App) {
let chunks = Layout::vertical([Constraint::Length(3), Constraint::Min(0)]).split(frame.area());
let tabs = app
.tabs
.titles
@@ -24,28 +24,28 @@ pub fn draw(f: &mut Frame, app: &mut App) {
.block(Block::bordered().title(app.title))
.highlight_style(Style::default().fg(Color::Yellow))
.select(app.tabs.index);
f.render_widget(tabs, chunks[0]);
frame.render_widget(tabs, chunks[0]);
match app.tabs.index {
0 => draw_first_tab(f, app, chunks[1]),
1 => draw_second_tab(f, app, chunks[1]),
2 => draw_third_tab(f, app, chunks[1]),
0 => draw_first_tab(frame, app, chunks[1]),
1 => draw_second_tab(frame, app, chunks[1]),
2 => draw_third_tab(frame, app, chunks[1]),
_ => {}
};
}
fn draw_first_tab(f: &mut Frame, app: &mut App, area: Rect) {
fn draw_first_tab(frame: &mut Frame, app: &mut App, area: Rect) {
let chunks = Layout::vertical([
Constraint::Length(9),
Constraint::Min(8),
Constraint::Length(7),
])
.split(area);
draw_gauges(f, app, chunks[0]);
draw_charts(f, app, chunks[1]);
draw_text(f, chunks[2]);
draw_gauges(frame, app, chunks[0]);
draw_charts(frame, app, chunks[1]);
draw_text(frame, chunks[2]);
}
fn draw_gauges(f: &mut Frame, app: &mut App, area: Rect) {
fn draw_gauges(frame: &mut Frame, app: &mut App, area: Rect) {
let chunks = Layout::vertical([
Constraint::Length(2),
Constraint::Length(3),
@@ -54,7 +54,7 @@ fn draw_gauges(f: &mut Frame, app: &mut App, area: Rect) {
.margin(1)
.split(area);
let block = Block::bordered().title("Graphs");
f.render_widget(block, area);
frame.render_widget(block, area);
let label = format!("{:.2}%", app.progress * 100.0);
let gauge = Gauge::default()
@@ -68,7 +68,7 @@ fn draw_gauges(f: &mut Frame, app: &mut App, area: Rect) {
.use_unicode(app.enhanced_graphics)
.label(label)
.ratio(app.progress);
f.render_widget(gauge, chunks[0]);
frame.render_widget(gauge, chunks[0]);
let sparkline = Sparkline::default()
.block(Block::new().title("Sparkline:"))
@@ -79,7 +79,7 @@ fn draw_gauges(f: &mut Frame, app: &mut App, area: Rect) {
} else {
symbols::bar::THREE_LEVELS
});
f.render_widget(sparkline, chunks[1]);
frame.render_widget(sparkline, chunks[1]);
let line_gauge = LineGauge::default()
.block(Block::new().title("LineGauge:"))
@@ -90,11 +90,11 @@ fn draw_gauges(f: &mut Frame, app: &mut App, area: Rect) {
symbols::line::NORMAL
})
.ratio(app.progress);
f.render_widget(line_gauge, chunks[2]);
frame.render_widget(line_gauge, chunks[2]);
}
#[allow(clippy::too_many_lines)]
fn draw_charts(f: &mut Frame, app: &mut App, area: Rect) {
fn draw_charts(frame: &mut Frame, app: &mut App, area: Rect) {
let constraints = if app.show_chart {
vec![Constraint::Percentage(50), Constraint::Percentage(50)]
} else {
@@ -120,7 +120,7 @@ fn draw_charts(f: &mut Frame, app: &mut App, area: Rect) {
.block(Block::bordered().title("List"))
.highlight_style(Style::default().add_modifier(Modifier::BOLD))
.highlight_symbol("> ");
f.render_stateful_widget(tasks, chunks[0], &mut app.tasks.state);
frame.render_stateful_widget(tasks, chunks[0], &mut app.tasks.state);
// Draw logs
let info_style = Style::default().fg(Color::Blue);
@@ -146,7 +146,7 @@ fn draw_charts(f: &mut Frame, app: &mut App, area: Rect) {
})
.collect();
let logs = List::new(logs).block(Block::bordered().title("List"));
f.render_stateful_widget(logs, chunks[1], &mut app.logs.state);
frame.render_stateful_widget(logs, chunks[1], &mut app.logs.state);
}
let barchart = BarChart::default()
@@ -167,7 +167,7 @@ fn draw_charts(f: &mut Frame, app: &mut App, area: Rect) {
)
.label_style(Style::default().fg(Color::Yellow))
.bar_style(Style::default().fg(Color::Green));
f.render_widget(barchart, chunks[1]);
frame.render_widget(barchart, chunks[1]);
}
if app.show_chart {
let x_labels = vec![
@@ -227,11 +227,11 @@ fn draw_charts(f: &mut Frame, app: &mut App, area: Rect) {
Span::styled("20", Style::default().add_modifier(Modifier::BOLD)),
]),
);
f.render_widget(chart, chunks[1]);
frame.render_widget(chart, chunks[1]);
}
}
fn draw_text(f: &mut Frame, area: Rect) {
fn draw_text(frame: &mut Frame, area: Rect) {
let text = vec![
text::Line::from("This is a paragraph with several lines. You can change style your text the way you want"),
text::Line::from(""),
@@ -266,10 +266,10 @@ fn draw_text(f: &mut Frame, area: Rect) {
.add_modifier(Modifier::BOLD),
));
let paragraph = Paragraph::new(text).block(block).wrap(Wrap { trim: true });
f.render_widget(paragraph, area);
frame.render_widget(paragraph, area);
}
fn draw_second_tab(f: &mut Frame, app: &mut App, area: Rect) {
fn draw_second_tab(frame: &mut Frame, app: &mut App, area: Rect) {
let chunks =
Layout::horizontal([Constraint::Percentage(30), Constraint::Percentage(70)]).split(area);
let up_style = Style::default().fg(Color::Green);
@@ -298,7 +298,7 @@ fn draw_second_tab(f: &mut Frame, app: &mut App, area: Rect) {
.bottom_margin(1),
)
.block(Block::bordered().title("Servers"));
f.render_widget(table, chunks[0]);
frame.render_widget(table, chunks[0]);
let map = Canvas::default()
.block(Block::bordered().title("World"))
@@ -352,10 +352,10 @@ fn draw_second_tab(f: &mut Frame, app: &mut App, area: Rect) {
})
.x_bounds([-180.0, 180.0])
.y_bounds([-90.0, 90.0]);
f.render_widget(map, chunks[1]);
frame.render_widget(map, chunks[1]);
}
fn draw_third_tab(f: &mut Frame, _app: &mut App, area: Rect) {
fn draw_third_tab(frame: &mut Frame, _app: &mut App, area: Rect) {
let chunks = Layout::horizontal([Constraint::Ratio(1, 2), Constraint::Ratio(1, 2)]).split(area);
let colors = [
Color::Reset,
@@ -396,5 +396,5 @@ fn draw_third_tab(f: &mut Frame, _app: &mut App, area: Rect) {
],
)
.block(Block::bordered().title("Colors"));
f.render_widget(table, chunks[0]);
frame.render_widget(table, chunks[0]);
}

View File

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

View File

@@ -0,0 +1,9 @@
## 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,23 +1,23 @@
use std::time::Duration;
use color_eyre::{eyre::Context, Result};
use crossterm::event;
use itertools::Itertools;
use ratatui::{
backend::Backend,
buffer::Buffer,
crossterm::event::{Event, KeyCode, KeyEvent, KeyEventKind},
layout::{Constraint, Layout, Rect},
style::Color,
text::{Line, Span},
widgets::{Block, Tabs, Widget},
Terminal,
DefaultTerminal, Frame,
};
use strum::{Display, EnumIter, FromRepr, IntoEnumIterator};
use crate::{
destroy,
tabs::{AboutTab, EmailTab, RecipeTab, TracerouteTab, WeatherTab},
term, THEME,
THEME,
};
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
@@ -49,15 +49,13 @@ enum Tab {
Weather,
}
pub fn run(terminal: &mut Terminal<impl Backend>) -> Result<()> {
App::default().run(terminal)
}
impl App {
/// Run the app until the user quits.
pub fn run(&mut self, terminal: &mut Terminal<impl Backend>) -> Result<()> {
pub fn run(mut self, mut terminal: DefaultTerminal) -> Result<()> {
while self.is_running() {
self.draw(terminal)?;
terminal
.draw(|frame| self.draw(frame))
.wrap_err("terminal.draw")?;
self.handle_events()?;
}
Ok(())
@@ -68,16 +66,11 @@ impl App {
}
/// Draw a single frame of the app.
fn draw(&self, terminal: &mut Terminal<impl Backend>) -> Result<()> {
terminal
.draw(|frame| {
frame.render_widget(self, frame.area());
if self.mode == Mode::Destroy {
destroy::destroy(frame);
}
})
.wrap_err("terminal.draw")?;
Ok(())
fn draw(&self, frame: &mut Frame) {
frame.render_widget(self, frame.area());
if self.mode == Mode::Destroy {
destroy::destroy(frame);
}
}
/// Handle events from the terminal.
@@ -86,8 +79,11 @@ impl App {
/// 1/50th of a second. This was chosen to try to match the default frame rate of a GIF in VHS.
fn handle_events(&mut self) -> Result<()> {
let timeout = Duration::from_secs_f64(1.0 / 50.0);
match term::next_event(timeout)? {
Some(Event::Key(key)) if key.kind == KeyEventKind::Press => self.handle_key_press(key),
if !event::poll(timeout)? {
return Ok(());
}
match event::read()? {
Event::Key(key) if key.kind == KeyEventKind::Press => self.handle_key_press(key),
_ => {}
}
Ok(())

View File

@@ -0,0 +1,54 @@
//! # [Ratatui] Demo2 example
//!
//! The latest version of this example is available in the [examples] folder in the repository.
//!
//! Please note that the examples are designed to be run against the `main` branch of the Github
//! repository. This means that you may not be able to compile with the latest release version on
//! crates.io, or the one that you have installed locally.
//!
//! See the [examples readme] for more information on finding examples that match the version of the
//! library you are using.
//!
//! [Ratatui]: https://github.com/ratatui/ratatui
//! [examples]: https://github.com/ratatui/ratatui/blob/main/examples
//! [examples readme]: https://github.com/ratatui/ratatui/blob/main/examples/README.md
#![allow(
clippy::missing_errors_doc,
clippy::module_name_repetitions,
clippy::must_use_candidate
)]
mod app;
mod colors;
mod destroy;
mod tabs;
mod theme;
use std::io::stdout;
use app::App;
use color_eyre::Result;
use crossterm::{
execute,
terminal::{EnterAlternateScreen, LeaveAlternateScreen},
};
use ratatui::{layout::Rect, TerminalOptions, Viewport};
pub use self::{
colors::{color_from_oklab, RgbSwatch},
theme::THEME,
};
fn main() -> Result<()> {
color_eyre::install()?;
// this size is to match the size of the terminal when running the demo
// using vhs in a 1280x640 sized window (github social preview size)
let viewport = Viewport::Fixed(Rect::new(0, 0, 81, 18));
let terminal = ratatui::init_with_options(TerminalOptions { viewport });
execute!(stdout(), EnterAlternateScreen).expect("failed to enter alternate screen");
let app_result = App::default().run(terminal);
execute!(stdout(), LeaveAlternateScreen).expect("failed to leave alternate screen");
ratatui::restore();
app_result
}

View File

@@ -0,0 +1,75 @@
use ratatui::{
buffer::Buffer,
layout::{Alignment, Constraint, Layout, Margin, Rect},
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 horizontal = Layout::horizontal([Constraint::Length(34), Constraint::Min(0)]);
let [logo_area, description] = horizontal.areas(area);
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

@@ -96,13 +96,10 @@ fn render_inbox(selected_index: usize, area: Rect, buf: &mut Buffer) {
.map(|e| e.from.width())
.max()
.unwrap_or_default();
let items = EMAILS
.iter()
.map(|e| {
let from = format!("{:width$}", e.from, width = from_width).into();
ListItem::new(Line::from(vec![from, " ".into(), e.subject.into()]))
})
.collect_vec();
let items = EMAILS.iter().map(|e| {
let from = format!("{:width$}", e.from, width = from_width).into();
ListItem::new(Line::from(vec![from, " ".into(), e.subject.into()]))
});
let mut state = ListState::default().with_selected(Some(selected_index));
StatefulWidget::render(
List::new(items)

View File

@@ -25,7 +25,7 @@ impl Ingredient {
}
}
impl<'a> From<Ingredient> for Row<'a> {
impl From<Ingredient> for Row<'_> {
fn from(i: Ingredient) -> Self {
Row::new(vec![i.quantity, i.name]).height(i.height())
}
@@ -164,7 +164,7 @@ fn render_ingredients(selected_row: usize, area: Rect, buf: &mut Buffer) {
Table::new(rows, [Constraint::Length(7), Constraint::Length(30)])
.block(Block::new().style(theme.ingredients))
.header(Row::new(vec!["Qty", "Ingredient"]).style(theme.ingredients_header))
.highlight_style(Style::new().light_yellow()),
.row_highlight_style(Style::new().light_yellow()),
area,
buf,
&mut state,

View File

@@ -52,10 +52,7 @@ impl Widget for TracerouteTab {
fn render_hops(selected_row: usize, area: Rect, buf: &mut Buffer) {
let mut state = TableState::default().with_selected(Some(selected_row));
let rows = HOPS
.iter()
.map(|hop| Row::new(vec![hop.host, hop.address]))
.collect_vec();
let rows = HOPS.iter().map(|hop| Row::new(vec![hop.host, hop.address]));
let block = Block::new()
.padding(Padding::new(1, 1, 1, 1))
.title_alignment(Alignment::Center)
@@ -63,7 +60,7 @@ fn render_hops(selected_row: usize, area: Rect, buf: &mut Buffer) {
StatefulWidget::render(
Table::new(rows, [Constraint::Max(100), Constraint::Length(15)])
.header(Row::new(vec!["Host", "Address"]).set_style(THEME.traceroute.header))
.highlight_style(THEME.traceroute.selected)
.row_highlight_style(THEME.traceroute.selected)
.block(block),
area,
buf,
@@ -102,7 +99,7 @@ pub fn render_ping(progress: usize, area: Rect, buf: &mut Buffer) {
.title_alignment(Alignment::Center)
.border_type(BorderType::Thick),
)
.data(&data)
.data(data)
.style(THEME.traceroute.ping)
.render(area, buf);
}

View File

@@ -88,7 +88,7 @@ fn render_simple_barchart(area: Rect, buf: &mut Buffer) {
Bar::default()
.value(value)
// This doesn't actually render correctly as the text is too wide for the bar
// See https://github.com/ratatui-org/ratatui/issues/513 for more info
// See https://github.com/ratatui/ratatui/issues/513 for more info
// (the demo GIFs hack around this by hacking the calculation in bars.rs)
.text_value(format!("{value}°"))
.style(if value > 70 {
@@ -101,7 +101,7 @@ fn render_simple_barchart(area: Rect, buf: &mut Buffer) {
} else {
Style::new().fg(Color::DarkGray).bg(Color::Yellow).bold()
})
.label(label.into())
.label(label)
})
.collect_vec();
let group = BarGroup::default().bars(&data);
@@ -115,15 +115,15 @@ fn render_simple_barchart(area: Rect, buf: &mut Buffer) {
fn render_horizontal_barchart(area: Rect, buf: &mut Buffer) {
let bg = Color::Rgb(32, 48, 96);
let data = [
Bar::default().text_value("Winter 37-51".into()).value(51),
Bar::default().text_value("Spring 40-65".into()).value(65),
Bar::default().text_value("Summer 54-77".into()).value(77),
Bar::default().text_value("Winter 37-51").value(51),
Bar::default().text_value("Spring 40-65").value(65),
Bar::default().text_value("Summer 54-77").value(77),
Bar::default()
.text_value("Fall 41-71".into())
.text_value("Fall 41-71")
.value(71)
.value_style(Style::new().bold()), // current season
];
let group = BarGroup::default().label("GPU".into()).bars(&data);
let group = BarGroup::default().label("GPU").bars(&data);
BarChart::default()
.block(Block::new().padding(Padding::new(0, 0, 2, 0)))
.direction(Direction::Horizontal)

View File

@@ -22,10 +22,8 @@ pub struct KeyBinding {
}
pub struct Logo {
pub rat: Color,
pub rat_eye: Color,
pub rat_eye_alt: Color,
pub term: Color,
}
pub struct Email {
@@ -77,10 +75,8 @@ pub const THEME: Theme = Theme {
description: Style::new().fg(LIGHT_GRAY).bg(DARK_BLUE),
description_title: Style::new().fg(LIGHT_GRAY).add_modifier(Modifier::BOLD),
logo: Logo {
rat: WHITE,
rat_eye: BLACK,
rat_eye_alt: RED,
term: BLACK,
},
key_binding: KeyBinding {
key: Style::new().fg(BLACK).bg(DARK_GRAY),

View File

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

@@ -0,0 +1,27 @@
# 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` discssion](https://github.com/bevyengine/bevy/discussions/15374) about focus
more generally.

View File

@@ -0,0 +1,270 @@
//! 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, Event, KeyCode, KeyEvent, KeyEventKind};
use ratatui::{
buffer::Buffer,
layout::{Constraint, Layout, Offset, Rect},
style::Stylize,
text::Line,
widgets::Widget,
DefaultTerminal, Frame,
};
use serde::Serialize;
fn main() -> Result<()> {
color_eyre::install()?;
let terminal = ratatui::init();
let result = App::default().run(terminal);
ratatui::restore();
// serialize the form to JSON if the user submitted it, otherwise print "Canceled"
match result {
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, mut terminal: 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<()> {
match event::read()? {
Event::Key(event) if event.kind == KeyEventKind::Press => match event.code {
KeyCode::Esc => self.state = AppState::Cancelled,
KeyCode::Enter => self.state = AppState::Submitted,
_ => self.form.on_key_press(event),
},
_ => {}
}
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 [first_name_area, last_name_area, age_area] =
Layout::vertical(Constraint::from_lengths([1, 1, 1])).areas(frame.area());
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 constraints = [
Constraint::Length(self.label.len() as u16 + 2),
Constraint::Fill(1),
];
let [label_area, value_area] = Layout::horizontal(constraints).areas(area);
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);
}
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 constraints = [
Constraint::Length(self.label.len() as u16 + 2),
Constraint::Fill(1),
];
let [label_area, value_area] = Layout::horizontal(constraints).areas(area);
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

@@ -0,0 +1,17 @@
[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.0.0"
rand = "0.8.5"
ratatui.workspace = true
[lints]
workspace = true

View File

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

@@ -0,0 +1,123 @@
/// 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,
},
execute,
};
use ratatui::{
layout::{Position, Rect, Size},
style::{Color, Stylize},
symbols,
text::Line,
DefaultTerminal, Frame,
};
fn main() -> Result<()> {
color_eyre::install()?;
let terminal = ratatui::init();
let result = MouseDrawingApp::default().run(terminal);
ratatui::restore();
result
}
#[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, mut terminal: 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, event: KeyEvent) {
match event.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

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

View File

@@ -0,0 +1,9 @@
# Weather demo
This example shows how to render weather data using barchart widget.
To run this demo:
```shell
cargo run -p weather
```

View File

@@ -0,0 +1,98 @@
//! A Ratatui example that demonstrates how to render weather data using [`BarChart`] widget.
//!
//! Generates random temperature data for each hour of the day and renders it as a vertical bar.
//!
//! 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 color_eyre::Result;
use rand::{thread_rng, Rng};
use ratatui::{
crossterm::event::{self, Event, KeyCode, KeyEventKind},
layout::{Constraint, Layout},
style::{Color, Style, Stylize},
text::Line,
widgets::{Bar, BarChart, BarGroup},
DefaultTerminal, Frame,
};
fn main() -> Result<()> {
color_eyre::install()?;
let terminal = ratatui::init();
let app_result = App::new().run(terminal);
ratatui::restore();
app_result
}
struct App {
should_exit: bool,
temperatures: Vec<u8>,
}
impl App {
fn new() -> Self {
let mut rng = thread_rng();
let temperatures = (0..24).map(|_| rng.gen_range(50..90)).collect();
Self {
should_exit: false,
temperatures,
}
}
fn run(mut self, mut terminal: DefaultTerminal) -> Result<()> {
while !self.should_exit {
terminal.draw(|frame| self.draw(frame))?;
self.handle_events()?;
}
Ok(())
}
fn handle_events(&mut self) -> Result<()> {
if let Event::Key(key) = event::read()? {
if key.kind == KeyEventKind::Press && key.code == KeyCode::Char('q') {
self.should_exit = true;
}
}
Ok(())
}
fn draw(&self, frame: &mut Frame) {
let [title, main] = Layout::vertical([Constraint::Length(1), Constraint::Fill(1)])
.spacing(1)
.areas(frame.area());
frame.render_widget("Weather demo".bold().into_centered_line(), title);
frame.render_widget(vertical_barchart(&self.temperatures), main);
}
}
/// Create a vertical bar chart from the temperatures data.
fn vertical_barchart(temperatures: &[u8]) -> BarChart {
let bars: Vec<Bar> = temperatures
.iter()
.enumerate()
.map(|(hour, value)| vertical_bar(hour, value))
.collect();
BarChart::default()
.data(BarGroup::default().bars(&bars))
.bar_width(5)
}
fn vertical_bar(hour: usize, temperature: &u8) -> Bar {
Bar::default()
.value(u64::from(*temperature))
.label(Line::from(format!("{hour:>02}:00")))
.text_value(format!("{temperature:>3}°"))
.style(temperature_style(*temperature))
.value_style(temperature_style(*temperature).reversed())
}
/// create a yellow to red value based on the value (50-90)
fn temperature_style(value: u8) -> Style {
let green = (255.0 * (1.0 - f64::from(value - 50) / 40.0)) as u8;
let color = Color::Rgb(255, green, 0);
Style::new().fg(color)
}

View File

@@ -1,278 +0,0 @@
//! # [Ratatui] `BarChart` example
//!
//! The latest version of this example is available in the [examples] folder in the repository.
//!
//! Please note that the examples are designed to be run against the `main` branch of the Github
//! repository. This means that you may not be able to compile with the latest release version on
//! crates.io, or the one that you have installed locally.
//!
//! See the [examples readme] for more information on finding examples that match the version of the
//! library you are using.
//!
//! [Ratatui]: https://github.com/ratatui-org/ratatui
//! [examples]: https://github.com/ratatui-org/ratatui/blob/main/examples
//! [examples readme]: https://github.com/ratatui-org/ratatui/blob/main/examples/README.md
use std::iter::zip;
use color_eyre::Result;
use ratatui::{
crossterm::event::{self, Event, KeyCode},
prelude::{Color, Constraint, Direction, Frame, Layout, Line, Style},
style::Stylize,
widgets::{Bar, BarChart, BarGroup, Block},
};
use self::terminal::Terminal;
const COMPANY_COUNT: usize = 3;
const PERIOD_COUNT: usize = 4;
struct App {
should_exit: bool,
companies: [Company; COMPANY_COUNT],
revenues: [Revenues; PERIOD_COUNT],
}
struct Revenues {
period: &'static str,
revenues: [u32; COMPANY_COUNT],
}
struct Company {
short_name: &'static str,
name: &'static str,
color: Color,
}
fn main() -> Result<()> {
color_eyre::install()?;
let mut terminal = terminal::init()?;
let app = App::new();
app.run(&mut terminal)?;
terminal::restore()?;
Ok(())
}
impl App {
const fn new() -> Self {
Self {
should_exit: false,
companies: fake_companies(),
revenues: fake_revenues(),
}
}
fn run(mut self, terminal: &mut Terminal) -> Result<()> {
while !self.should_exit {
self.draw(terminal)?;
self.handle_events()?;
}
Ok(())
}
fn draw(&self, terminal: &mut Terminal) -> Result<()> {
terminal.draw(|frame| self.render(frame))?;
Ok(())
}
fn handle_events(&mut self) -> Result<()> {
if let Event::Key(key) = event::read()? {
if key.code == KeyCode::Char('q') {
self.should_exit = true;
}
}
Ok(())
}
fn render(&self, frame: &mut Frame) {
use Constraint::{Fill, Length, Min};
let [title, top, bottom] = Layout::vertical([Length(1), Fill(1), Min(20)])
.spacing(1)
.areas(frame.area());
frame.render_widget("Grouped Barchart".bold().into_centered_line(), title);
frame.render_widget(self.vertical_revenue_barchart(), top);
frame.render_widget(self.horizontal_revenue_barchart(), bottom);
}
/// Create a vertical revenue bar chart with the data from the `revenues` field.
fn vertical_revenue_barchart(&self) -> BarChart<'_> {
let title = Line::from("Company revenues (Vertical)").centered();
let mut barchart = BarChart::default()
.block(Block::new().title(title))
.bar_gap(0)
.bar_width(6)
.group_gap(2);
for group in self
.revenues
.iter()
.map(|revenue| revenue.to_vertical_bar_group(&self.companies))
{
barchart = barchart.data(group);
}
barchart
}
/// Create a horizontal revenue bar chart with the data from the `revenues` field.
fn horizontal_revenue_barchart(&self) -> BarChart<'_> {
let title = Line::from("Company Revenues (Horizontal)").centered();
let mut barchart = BarChart::default()
.block(Block::new().title(title))
.bar_width(1)
.group_gap(2)
.bar_gap(0)
.direction(Direction::Horizontal);
for group in self
.revenues
.iter()
.map(|revenue| revenue.to_horizontal_bar_group(&self.companies))
{
barchart = barchart.data(group);
}
barchart
}
}
/// Generate fake company data
const fn fake_companies() -> [Company; COMPANY_COUNT] {
[
Company::new("BAKE", "Bake my day", Color::LightRed),
Company::new("BITE", "Bits and Bites", Color::Blue),
Company::new("TART", "Tart of the Table", Color::White),
]
}
/// Some fake revenue data
const fn fake_revenues() -> [Revenues; PERIOD_COUNT] {
[
Revenues::new("Jan", [8500, 6500, 7000]),
Revenues::new("Feb", [9000, 7500, 8500]),
Revenues::new("Mar", [9500, 4500, 8200]),
Revenues::new("Apr", [6300, 4000, 5000]),
]
}
impl Revenues {
/// Create a new instance of `Revenues`
const fn new(period: &'static str, revenues: [u32; COMPANY_COUNT]) -> Self {
Self { period, revenues }
}
/// Create a `BarGroup` with vertical bars for each company
fn to_vertical_bar_group<'a>(&self, companies: &'a [Company]) -> BarGroup<'a> {
let bars: Vec<Bar> = zip(companies, self.revenues)
.map(|(company, revenue)| company.vertical_revenue_bar(revenue))
.collect();
BarGroup::default()
.label(Line::from(self.period).centered())
.bars(&bars)
}
/// Create a `BarGroup` with horizontal bars for each company
fn to_horizontal_bar_group<'a>(&'a self, companies: &'a [Company]) -> BarGroup<'a> {
let bars: Vec<Bar> = zip(companies, self.revenues)
.map(|(company, revenue)| company.horizontal_revenue_bar(revenue))
.collect();
BarGroup::default()
.label(Line::from(self.period).centered())
.bars(&bars)
}
}
impl Company {
/// Create a new instance of `Company`
const fn new(short_name: &'static str, name: &'static str, color: Color) -> Self {
Self {
short_name,
name,
color,
}
}
/// Create a vertical revenue bar for the company
///
/// The label is the short name of the company, and will be displayed under the bar
fn vertical_revenue_bar(&self, revenue: u32) -> Bar {
let text_value = format!("{:.1}M", f64::from(revenue) / 1000.);
Bar::default()
.label(self.short_name.into())
.value(u64::from(revenue))
.text_value(text_value)
.style(self.color)
.value_style(Style::new().fg(Color::Black).bg(self.color))
}
/// Create a horizontal revenue bar for the company
///
/// The label is the long name of the company combined with the revenue and will be displayed
/// on the bar
fn horizontal_revenue_bar(&self, revenue: u32) -> Bar {
let text_value = format!("{} ({:.1} M)", self.name, f64::from(revenue) / 1000.);
Bar::default()
.value(u64::from(revenue))
.text_value(text_value)
.style(self.color)
.value_style(Style::new().fg(Color::Black).bg(self.color))
}
}
/// Contains functions common to all examples
mod terminal {
use std::{
io::{self, stdout, Stdout},
panic,
};
use ratatui::{
backend::CrosstermBackend,
crossterm::{
execute,
terminal::{
disable_raw_mode, enable_raw_mode, Clear, ClearType, EnterAlternateScreen,
LeaveAlternateScreen,
},
},
};
// A type alias to simplify the usage of the terminal and make it easier to change the backend
// or choice of writer.
pub type Terminal = ratatui::Terminal<CrosstermBackend<Stdout>>;
/// Initialize the terminal by enabling raw mode and entering the alternate screen.
///
/// This function should be called before the program starts to ensure that the terminal is in
/// the correct state for the application.
pub fn init() -> io::Result<Terminal> {
install_panic_hook();
enable_raw_mode()?;
execute!(stdout(), EnterAlternateScreen)?;
let backend = CrosstermBackend::new(stdout());
let terminal = Terminal::new(backend)?;
Ok(terminal)
}
/// Restore the terminal by leaving the alternate screen and disabling raw mode.
///
/// This function should be called before the program exits to ensure that the terminal is
/// restored to its original state.
pub fn restore() -> io::Result<()> {
disable_raw_mode()?;
execute!(
stdout(),
LeaveAlternateScreen,
Clear(ClearType::FromCursorDown),
)
}
/// Install a panic hook that restores the terminal before printing the panic.
///
/// This prevents error messages from being messed up by the terminal state.
fn install_panic_hook() {
let panic_hook = panic::take_hook();
panic::set_hook(Box::new(move |panic_info| {
let _ = restore();
panic_hook(panic_info);
}));
}
}

View File

@@ -1,199 +0,0 @@
//! # [Ratatui] `BarChart` example
//!
//! The latest version of this example is available in the [examples] folder in the repository.
//!
//! Please note that the examples are designed to be run against the `main` branch of the Github
//! repository. This means that you may not be able to compile with the latest release version on
//! crates.io, or the one that you have installed locally.
//!
//! See the [examples readme] for more information on finding examples that match the version of the
//! library you are using.
//!
//! [Ratatui]: https://github.com/ratatui-org/ratatui
//! [examples]: https://github.com/ratatui-org/ratatui/blob/main/examples
//! [examples readme]: https://github.com/ratatui-org/ratatui/blob/main/examples/README.md
use color_eyre::Result;
use rand::{thread_rng, Rng};
use ratatui::{
crossterm::event::{self, Event, KeyCode},
layout::{Constraint, Direction, Layout},
prelude::{Color, Line, Style, Stylize},
widgets::{Bar, BarChart, BarGroup, Block},
};
use self::terminal::Terminal;
struct App {
should_exit: bool,
temperatures: Vec<u8>,
}
fn main() -> Result<()> {
color_eyre::install()?;
let mut terminal = terminal::init()?;
let app = App::new();
app.run(&mut terminal)?;
terminal::restore()?;
Ok(())
}
impl App {
fn new() -> Self {
let mut rng = thread_rng();
let temperatures = (0..24).map(|_| rng.gen_range(50..90)).collect();
Self {
should_exit: false,
temperatures,
}
}
fn run(mut self, terminal: &mut Terminal) -> Result<()> {
while !self.should_exit {
self.draw(terminal)?;
self.handle_events()?;
}
Ok(())
}
fn draw(&self, terminal: &mut Terminal) -> Result<()> {
terminal.draw(|frame| self.render(frame))?;
Ok(())
}
fn handle_events(&mut self) -> Result<()> {
if let Event::Key(key) = event::read()? {
if key.code == KeyCode::Char('q') {
self.should_exit = true;
}
}
Ok(())
}
fn render(&self, frame: &mut ratatui::Frame) {
let [title, vertical, horizontal] = Layout::vertical([
Constraint::Length(1),
Constraint::Fill(1),
Constraint::Fill(1),
])
.spacing(1)
.areas(frame.area());
frame.render_widget("Barchart".bold().into_centered_line(), title);
frame.render_widget(vertical_barchart(&self.temperatures), vertical);
frame.render_widget(horizontal_barchart(&self.temperatures), horizontal);
}
}
/// Create a vertical bar chart from the temperatures data.
fn vertical_barchart(temperatures: &[u8]) -> BarChart {
let bars: Vec<Bar> = temperatures
.iter()
.map(|v| u64::from(*v))
.enumerate()
.map(|(i, value)| {
Bar::default()
.value(value)
.label(Line::from(format!("{i:>02}:00")))
.text_value(format!("{value:>3}°"))
.style(temperature_style(value))
.value_style(temperature_style(value).reversed())
})
.collect();
let title = Line::from("Weather (Vertical)").centered();
BarChart::default()
.data(BarGroup::default().bars(&bars))
.block(Block::new().title(title))
.bar_width(5)
}
/// Create a horizontal bar chart from the temperatures data.
fn horizontal_barchart(temperatures: &[u8]) -> BarChart {
let bars: Vec<Bar> = temperatures
.iter()
.map(|v| u64::from(*v))
.enumerate()
.map(|(i, value)| {
let style = temperature_style(value);
Bar::default()
.value(value)
.label(Line::from(format!("{i:>02}:00")))
.text_value(format!("{value:>3}°"))
.style(style)
.value_style(style.reversed())
})
.collect();
let title = Line::from("Weather (Horizontal)").centered();
BarChart::default()
.block(Block::new().title(title))
.data(BarGroup::default().bars(&bars))
.bar_width(1)
.bar_gap(0)
.direction(Direction::Horizontal)
}
/// create a yellow to red value based on the value (50-90)
fn temperature_style(value: u64) -> Style {
let green = (255.0 * (1.0 - (value - 50) as f64 / 40.0)) as u8;
let color = Color::Rgb(255, green, 0);
Style::new().fg(color)
}
/// Contains functions common to all examples
mod terminal {
use std::{
io::{self, stdout, Stdout},
panic,
};
use ratatui::{
backend::CrosstermBackend,
crossterm::{
execute,
terminal::{
disable_raw_mode, enable_raw_mode, Clear, ClearType, EnterAlternateScreen,
LeaveAlternateScreen,
},
},
};
// A type alias to simplify the usage of the terminal and make it easier to change the backend
// or choice of writer.
pub type Terminal = ratatui::Terminal<CrosstermBackend<Stdout>>;
/// Initialize the terminal by enabling raw mode and entering the alternate screen.
///
/// This function should be called before the program starts to ensure that the terminal is in
/// the correct state for the application.
pub fn init() -> io::Result<Terminal> {
install_panic_hook();
enable_raw_mode()?;
execute!(stdout(), EnterAlternateScreen)?;
let backend = CrosstermBackend::new(stdout());
let terminal = Terminal::new(backend)?;
Ok(terminal)
}
/// Restore the terminal by leaving the alternate screen and disabling raw mode.
///
/// This function should be called before the program exits to ensure that the terminal is
/// restored to its original state.
pub fn restore() -> io::Result<()> {
disable_raw_mode()?;
execute!(
stdout(),
LeaveAlternateScreen,
Clear(ClearType::FromCursorDown),
)
}
/// Install a panic hook that restores the terminal before printing the panic.
///
/// This prevents error messages from being messed up by the terminal state.
fn install_panic_hook() {
let panic_hook = panic::take_hook();
panic::set_hook(Box::new(move |panic_info| {
let _ = restore();
panic_hook(panic_info);
}));
}
}

View File

@@ -1,258 +0,0 @@
//! # [Ratatui] Block example
//!
//! The latest version of this example is available in the [examples] folder in the repository.
//!
//! Please note that the examples are designed to be run against the `main` branch of the Github
//! repository. This means that you may not be able to compile with the latest release version on
//! crates.io, or the one that you have installed locally.
//!
//! See the [examples readme] for more information on finding examples that match the version of the
//! library you are using.
//!
//! [Ratatui]: https://github.com/ratatui-org/ratatui
//! [examples]: https://github.com/ratatui-org/ratatui/blob/main/examples
//! [examples readme]: https://github.com/ratatui-org/ratatui/blob/main/examples/README.md
use std::{
error::Error,
io::{stdout, Stdout},
ops::ControlFlow,
time::Duration,
};
use itertools::Itertools;
use ratatui::{
backend::CrosstermBackend,
crossterm::{
event::{self, Event, KeyCode},
execute,
terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
},
layout::{Alignment, Constraint, Layout, Rect},
style::{Style, Stylize},
text::Line,
widgets::{
block::{Position, Title},
Block, BorderType, Borders, Padding, Paragraph, Wrap,
},
Frame,
};
// These type aliases are used to make the code more readable by reducing repetition of the generic
// types. They are not necessary for the functionality of the code.
type Terminal = ratatui::Terminal<CrosstermBackend<Stdout>>;
type Result<T> = std::result::Result<T, Box<dyn Error>>;
fn main() -> Result<()> {
let mut terminal = setup_terminal()?;
let result = run(&mut terminal);
restore_terminal(terminal)?;
if let Err(err) = result {
eprintln!("{err:?}");
}
Ok(())
}
fn setup_terminal() -> Result<Terminal> {
enable_raw_mode()?;
let mut stdout = stdout();
execute!(stdout, EnterAlternateScreen)?;
let backend = CrosstermBackend::new(stdout);
let terminal = Terminal::new(backend)?;
Ok(terminal)
}
fn restore_terminal(mut terminal: Terminal) -> Result<()> {
disable_raw_mode()?;
execute!(terminal.backend_mut(), LeaveAlternateScreen)?;
Ok(())
}
fn run(terminal: &mut Terminal) -> Result<()> {
loop {
terminal.draw(ui)?;
if handle_events()?.is_break() {
return Ok(());
}
}
}
fn handle_events() -> Result<ControlFlow<()>> {
if event::poll(Duration::from_millis(100))? {
if let Event::Key(key) = event::read()? {
if key.code == KeyCode::Char('q') {
return Ok(ControlFlow::Break(()));
}
}
}
Ok(ControlFlow::Continue(()))
}
fn ui(frame: &mut Frame) {
let (title_area, layout) = calculate_layout(frame.area());
render_title(frame, title_area);
let paragraph = placeholder_paragraph();
render_borders(&paragraph, Borders::ALL, frame, layout[0][0]);
render_borders(&paragraph, Borders::NONE, frame, layout[0][1]);
render_borders(&paragraph, Borders::LEFT, frame, layout[1][0]);
render_borders(&paragraph, Borders::RIGHT, frame, layout[1][1]);
render_borders(&paragraph, Borders::TOP, frame, layout[2][0]);
render_borders(&paragraph, Borders::BOTTOM, frame, layout[2][1]);
render_border_type(&paragraph, BorderType::Plain, frame, layout[3][0]);
render_border_type(&paragraph, BorderType::Rounded, frame, layout[3][1]);
render_border_type(&paragraph, BorderType::Double, frame, layout[4][0]);
render_border_type(&paragraph, BorderType::Thick, frame, layout[4][1]);
render_styled_block(&paragraph, frame, layout[5][0]);
render_styled_borders(&paragraph, frame, layout[5][1]);
render_styled_title(&paragraph, frame, layout[6][0]);
render_styled_title_content(&paragraph, frame, layout[6][1]);
render_multiple_titles(&paragraph, frame, layout[7][0]);
render_multiple_title_positions(&paragraph, frame, layout[7][1]);
render_padding(&paragraph, frame, layout[8][0]);
render_nested_blocks(&paragraph, frame, layout[8][1]);
}
/// Calculate the layout of the UI elements.
///
/// Returns a tuple of the title area and the main areas.
fn calculate_layout(area: Rect) -> (Rect, Vec<Vec<Rect>>) {
let main_layout = Layout::vertical([Constraint::Length(1), Constraint::Min(0)]);
let block_layout = Layout::vertical([Constraint::Max(4); 9]);
let [title_area, main_area] = main_layout.areas(area);
let main_areas = block_layout
.split(main_area)
.iter()
.map(|&area| {
Layout::horizontal([Constraint::Percentage(50), Constraint::Percentage(50)])
.split(area)
.to_vec()
})
.collect_vec();
(title_area, main_areas)
}
fn render_title(frame: &mut Frame, area: Rect) {
frame.render_widget(
Paragraph::new("Block example. Press q to quit")
.dark_gray()
.alignment(Alignment::Center),
area,
);
}
fn placeholder_paragraph() -> Paragraph<'static> {
let text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.";
Paragraph::new(text.dark_gray()).wrap(Wrap { trim: true })
}
fn render_borders(paragraph: &Paragraph, border: Borders, frame: &mut Frame, area: Rect) {
let block = Block::new()
.borders(border)
.title(format!("Borders::{border:#?}"));
frame.render_widget(paragraph.clone().block(block), area);
}
fn render_border_type(
paragraph: &Paragraph,
border_type: BorderType,
frame: &mut Frame,
area: Rect,
) {
let block = Block::bordered()
.border_type(border_type)
.title(format!("BorderType::{border_type:#?}"));
frame.render_widget(paragraph.clone().block(block), area);
}
fn render_styled_borders(paragraph: &Paragraph, frame: &mut Frame, area: Rect) {
let block = Block::bordered()
.border_style(Style::new().blue().on_white().bold().italic())
.title("Styled borders");
frame.render_widget(paragraph.clone().block(block), area);
}
fn render_styled_block(paragraph: &Paragraph, frame: &mut Frame, area: Rect) {
let block = Block::bordered()
.style(Style::new().blue().on_white().bold().italic())
.title("Styled block");
frame.render_widget(paragraph.clone().block(block), area);
}
// Note: this currently renders incorrectly, see https://github.com/ratatui-org/ratatui/issues/349
fn render_styled_title(paragraph: &Paragraph, frame: &mut Frame, area: Rect) {
let block = Block::bordered()
.title("Styled title")
.title_style(Style::new().blue().on_white().bold().italic());
frame.render_widget(paragraph.clone().block(block), area);
}
fn render_styled_title_content(paragraph: &Paragraph, frame: &mut Frame, area: Rect) {
let title = Line::from(vec![
"Styled ".blue().on_white().bold().italic(),
"title content".red().on_white().bold().italic(),
]);
let block = Block::bordered().title(title);
frame.render_widget(paragraph.clone().block(block), area);
}
fn render_multiple_titles(paragraph: &Paragraph, frame: &mut Frame, area: Rect) {
let block = Block::bordered()
.title("Multiple".blue().on_white().bold().italic())
.title("Titles".red().on_white().bold().italic());
frame.render_widget(paragraph.clone().block(block), area);
}
fn render_multiple_title_positions(paragraph: &Paragraph, frame: &mut Frame, area: Rect) {
let block = Block::bordered()
.title(
Title::from("top left")
.position(Position::Top)
.alignment(Alignment::Left),
)
.title(
Title::from("top center")
.position(Position::Top)
.alignment(Alignment::Center),
)
.title(
Title::from("top right")
.position(Position::Top)
.alignment(Alignment::Right),
)
.title(
Title::from("bottom left")
.position(Position::Bottom)
.alignment(Alignment::Left),
)
.title(
Title::from("bottom center")
.position(Position::Bottom)
.alignment(Alignment::Center),
)
.title(
Title::from("bottom right")
.position(Position::Bottom)
.alignment(Alignment::Right),
);
frame.render_widget(paragraph.clone().block(block), area);
}
fn render_padding(paragraph: &Paragraph, frame: &mut Frame, area: Rect) {
let block = Block::bordered()
.padding(Padding::new(5, 10, 1, 2))
.title("Padding");
frame.render_widget(paragraph.clone().block(block), area);
}
fn render_nested_blocks(paragraph: &Paragraph, frame: &mut Frame, area: Rect) {
let outer_block = Block::bordered().title("Outer block");
let inner_block = Block::bordered().title("Inner block");
let inner = outer_block.inner(area);
frame.render_widget(outer_block, area);
frame.render_widget(paragraph.clone().block(inner_block), inner);
}

View File

@@ -1,267 +0,0 @@
//! # [Ratatui] Calendar example
//!
//! The latest version of this example is available in the [examples] folder in the repository.
//!
//! Please note that the examples are designed to be run against the `main` branch of the Github
//! repository. This means that you may not be able to compile with the latest release version on
//! crates.io, or the one that you have installed locally.
//!
//! See the [examples readme] for more information on finding examples that match the version of the
//! library you are using.
//!
//! [Ratatui]: https://github.com/ratatui-org/ratatui
//! [examples]: https://github.com/ratatui-org/ratatui/blob/main/examples
//! [examples readme]: https://github.com/ratatui-org/ratatui/blob/main/examples/README.md
use std::{error::Error, io};
use ratatui::{
backend::CrosstermBackend,
crossterm::{
event::{self, Event, KeyCode},
execute,
terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
},
layout::{Constraint, Layout, Rect},
style::{Color, Modifier, Style},
widgets::calendar::{CalendarEventStore, DateStyler, Monthly},
Frame, Terminal,
};
use time::{Date, Month, OffsetDateTime};
fn main() -> Result<(), Box<dyn Error>> {
enable_raw_mode()?;
let mut stdout = io::stdout();
execute!(stdout, EnterAlternateScreen)?;
let backend = CrosstermBackend::new(stdout);
let mut terminal = Terminal::new(backend)?;
loop {
let _ = terminal.draw(draw);
if let Event::Key(key) = event::read()? {
#[allow(clippy::single_match)]
match key.code {
KeyCode::Char(_) => {
break;
}
_ => {}
};
}
}
disable_raw_mode()?;
execute!(terminal.backend_mut(), LeaveAlternateScreen)?;
terminal.show_cursor()?;
Ok(())
}
fn draw(frame: &mut Frame) {
let app_area = frame.area();
let calarea = Rect {
x: app_area.x + 1,
y: app_area.y + 1,
height: app_area.height - 1,
width: app_area.width - 1,
};
let mut start = OffsetDateTime::now_local()
.unwrap()
.date()
.replace_month(Month::January)
.unwrap()
.replace_day(1)
.unwrap();
let list = make_dates(start.year());
let rows = Layout::vertical([Constraint::Ratio(1, 3); 3]).split(calarea);
let cols = rows.iter().flat_map(|row| {
Layout::horizontal([Constraint::Ratio(1, 4); 4])
.split(*row)
.to_vec()
});
for col in cols {
let cal = cals::get_cal(start.month(), start.year(), &list);
frame.render_widget(cal, col);
start = start.replace_month(start.month().next()).unwrap();
}
}
fn make_dates(current_year: i32) -> CalendarEventStore {
let mut list = CalendarEventStore::today(
Style::default()
.add_modifier(Modifier::BOLD)
.bg(Color::Blue),
);
// Holidays
let holiday_style = Style::default()
.fg(Color::Red)
.add_modifier(Modifier::UNDERLINED);
// new year's
list.add(
Date::from_calendar_date(current_year, Month::January, 1).unwrap(),
holiday_style,
);
// next new_year's for December "show surrounding"
list.add(
Date::from_calendar_date(current_year + 1, Month::January, 1).unwrap(),
holiday_style,
);
// groundhog day
list.add(
Date::from_calendar_date(current_year, Month::February, 2).unwrap(),
holiday_style,
);
// april fool's
list.add(
Date::from_calendar_date(current_year, Month::April, 1).unwrap(),
holiday_style,
);
// earth day
list.add(
Date::from_calendar_date(current_year, Month::April, 22).unwrap(),
holiday_style,
);
// star wars day
list.add(
Date::from_calendar_date(current_year, Month::May, 4).unwrap(),
holiday_style,
);
// festivus
list.add(
Date::from_calendar_date(current_year, Month::December, 23).unwrap(),
holiday_style,
);
// new year's eve
list.add(
Date::from_calendar_date(current_year, Month::December, 31).unwrap(),
holiday_style,
);
// seasons
let season_style = Style::default()
.fg(Color::White)
.bg(Color::Yellow)
.add_modifier(Modifier::UNDERLINED);
// spring equinox
list.add(
Date::from_calendar_date(current_year, Month::March, 22).unwrap(),
season_style,
);
// summer solstice
list.add(
Date::from_calendar_date(current_year, Month::June, 21).unwrap(),
season_style,
);
// fall equinox
list.add(
Date::from_calendar_date(current_year, Month::September, 22).unwrap(),
season_style,
);
list.add(
Date::from_calendar_date(current_year, Month::December, 21).unwrap(),
season_style,
);
list
}
mod cals {
#[allow(clippy::wildcard_imports)]
use super::*;
pub fn get_cal<'a, DS: DateStyler>(m: Month, y: i32, es: DS) -> Monthly<'a, DS> {
match m {
Month::May => example1(m, y, es),
Month::June => example2(m, y, es),
Month::July | Month::December => example3(m, y, es),
Month::February => example4(m, y, es),
Month::November => example5(m, y, es),
_ => default(m, y, es),
}
}
fn default<'a, DS: DateStyler>(m: Month, y: i32, es: DS) -> Monthly<'a, DS> {
let default_style = Style::default()
.add_modifier(Modifier::BOLD)
.bg(Color::Rgb(50, 50, 50));
Monthly::new(Date::from_calendar_date(y, m, 1).unwrap(), es)
.show_month_header(Style::default())
.default_style(default_style)
}
fn example1<'a, DS: DateStyler>(m: Month, y: i32, es: DS) -> Monthly<'a, DS> {
let default_style = Style::default()
.add_modifier(Modifier::BOLD)
.bg(Color::Rgb(50, 50, 50));
Monthly::new(Date::from_calendar_date(y, m, 1).unwrap(), es)
.show_surrounding(default_style)
.default_style(default_style)
.show_month_header(Style::default())
}
fn example2<'a, DS: DateStyler>(m: Month, y: i32, es: DS) -> Monthly<'a, DS> {
let header_style = Style::default()
.add_modifier(Modifier::BOLD)
.add_modifier(Modifier::DIM)
.fg(Color::LightYellow);
let default_style = Style::default()
.add_modifier(Modifier::BOLD)
.bg(Color::Rgb(50, 50, 50));
Monthly::new(Date::from_calendar_date(y, m, 1).unwrap(), es)
.show_weekdays_header(header_style)
.default_style(default_style)
.show_month_header(Style::default())
}
fn example3<'a, DS: DateStyler>(m: Month, y: i32, es: DS) -> Monthly<'a, DS> {
let header_style = Style::default()
.add_modifier(Modifier::BOLD)
.fg(Color::Green);
let default_style = Style::default()
.add_modifier(Modifier::BOLD)
.bg(Color::Rgb(50, 50, 50));
Monthly::new(Date::from_calendar_date(y, m, 1).unwrap(), es)
.show_surrounding(Style::default().add_modifier(Modifier::DIM))
.show_weekdays_header(header_style)
.default_style(default_style)
.show_month_header(Style::default())
}
fn example4<'a, DS: DateStyler>(m: Month, y: i32, es: DS) -> Monthly<'a, DS> {
let header_style = Style::default()
.add_modifier(Modifier::BOLD)
.fg(Color::Green);
let default_style = Style::default()
.add_modifier(Modifier::BOLD)
.bg(Color::Rgb(50, 50, 50));
Monthly::new(Date::from_calendar_date(y, m, 1).unwrap(), es)
.show_weekdays_header(header_style)
.default_style(default_style)
}
fn example5<'a, DS: DateStyler>(m: Month, y: i32, es: DS) -> Monthly<'a, DS> {
let header_style = Style::default()
.add_modifier(Modifier::BOLD)
.fg(Color::Green);
let default_style = Style::default()
.add_modifier(Modifier::BOLD)
.bg(Color::Rgb(50, 50, 50));
Monthly::new(Date::from_calendar_date(y, m, 1).unwrap(), es)
.show_month_header(header_style)
.default_style(default_style)
}
}

View File

@@ -1,218 +0,0 @@
//! # [Ratatui] Canvas example
//!
//! The latest version of this example is available in the [examples] folder in the repository.
//!
//! Please note that the examples are designed to be run against the `main` branch of the Github
//! repository. This means that you may not be able to compile with the latest release version on
//! crates.io, or the one that you have installed locally.
//!
//! See the [examples readme] for more information on finding examples that match the version of the
//! library you are using.
//!
//! [Ratatui]: https://github.com/ratatui-org/ratatui
//! [examples]: https://github.com/ratatui-org/ratatui/blob/main/examples
//! [examples readme]: https://github.com/ratatui-org/ratatui/blob/main/examples/README.md
use std::{
io::{self, stdout, Stdout},
time::{Duration, Instant},
};
use ratatui::{
backend::CrosstermBackend,
crossterm::{
event::{self, Event, KeyCode},
terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
ExecutableCommand,
},
layout::{Constraint, Layout, Rect},
style::{Color, Stylize},
symbols::Marker,
widgets::{
canvas::{Canvas, Circle, Map, MapResolution, Rectangle},
Block, Widget,
},
Frame, Terminal,
};
fn main() -> io::Result<()> {
App::run()
}
struct App {
x: f64,
y: f64,
ball: Circle,
playground: Rect,
vx: f64,
vy: f64,
tick_count: u64,
marker: Marker,
}
impl App {
fn new() -> Self {
Self {
x: 0.0,
y: 0.0,
ball: Circle {
x: 20.0,
y: 40.0,
radius: 10.0,
color: Color::Yellow,
},
playground: Rect::new(10, 10, 200, 100),
vx: 1.0,
vy: 1.0,
tick_count: 0,
marker: Marker::Dot,
}
}
pub fn run() -> io::Result<()> {
let mut terminal = init_terminal()?;
let mut app = Self::new();
let mut last_tick = Instant::now();
let tick_rate = Duration::from_millis(16);
loop {
let _ = terminal.draw(|frame| app.ui(frame));
let timeout = tick_rate.saturating_sub(last_tick.elapsed());
if event::poll(timeout)? {
if let Event::Key(key) = event::read()? {
match key.code {
KeyCode::Char('q') => break,
KeyCode::Down | KeyCode::Char('j') => app.y += 1.0,
KeyCode::Up | KeyCode::Char('k') => app.y -= 1.0,
KeyCode::Right | KeyCode::Char('l') => app.x += 1.0,
KeyCode::Left | KeyCode::Char('h') => app.x -= 1.0,
_ => {}
}
}
}
if last_tick.elapsed() >= tick_rate {
app.on_tick();
last_tick = Instant::now();
}
}
restore_terminal()
}
fn on_tick(&mut self) {
self.tick_count += 1;
// only change marker every 180 ticks (3s) to avoid stroboscopic effect
if (self.tick_count % 180) == 0 {
self.marker = match self.marker {
Marker::Dot => Marker::Braille,
Marker::Braille => Marker::Block,
Marker::Block => Marker::HalfBlock,
Marker::HalfBlock => Marker::Bar,
Marker::Bar => Marker::Dot,
};
}
// bounce the ball by flipping the velocity vector
let ball = &self.ball;
let playground = self.playground;
if ball.x - ball.radius < f64::from(playground.left())
|| ball.x + ball.radius > f64::from(playground.right())
{
self.vx = -self.vx;
}
if ball.y - ball.radius < f64::from(playground.top())
|| ball.y + ball.radius > f64::from(playground.bottom())
{
self.vy = -self.vy;
}
self.ball.x += self.vx;
self.ball.y += self.vy;
}
fn ui(&self, frame: &mut Frame) {
let horizontal =
Layout::horizontal([Constraint::Percentage(50), Constraint::Percentage(50)]);
let vertical = Layout::vertical([Constraint::Percentage(50), Constraint::Percentage(50)]);
let [map, right] = horizontal.areas(frame.area());
let [pong, boxes] = vertical.areas(right);
frame.render_widget(self.map_canvas(), map);
frame.render_widget(self.pong_canvas(), pong);
frame.render_widget(self.boxes_canvas(boxes), boxes);
}
fn map_canvas(&self) -> impl Widget + '_ {
Canvas::default()
.block(Block::bordered().title("World"))
.marker(self.marker)
.paint(|ctx| {
ctx.draw(&Map {
color: Color::Green,
resolution: MapResolution::High,
});
ctx.print(self.x, -self.y, "You are here".yellow());
})
.x_bounds([-180.0, 180.0])
.y_bounds([-90.0, 90.0])
}
fn pong_canvas(&self) -> impl Widget + '_ {
Canvas::default()
.block(Block::bordered().title("Pong"))
.marker(self.marker)
.paint(|ctx| {
ctx.draw(&self.ball);
})
.x_bounds([10.0, 210.0])
.y_bounds([10.0, 110.0])
}
fn boxes_canvas(&self, area: Rect) -> impl Widget {
let left = 0.0;
let right = f64::from(area.width);
let bottom = 0.0;
let top = f64::from(area.height).mul_add(2.0, -4.0);
Canvas::default()
.block(Block::bordered().title("Rects"))
.marker(self.marker)
.x_bounds([left, right])
.y_bounds([bottom, top])
.paint(|ctx| {
for i in 0..=11 {
ctx.draw(&Rectangle {
x: f64::from(i * i + 3 * i) / 2.0 + 2.0,
y: 2.0,
width: f64::from(i),
height: f64::from(i),
color: Color::Red,
});
ctx.draw(&Rectangle {
x: f64::from(i * i + 3 * i) / 2.0 + 2.0,
y: 21.0,
width: f64::from(i),
height: f64::from(i),
color: Color::Blue,
});
}
for i in 0..100 {
if i % 10 != 0 {
ctx.print(f64::from(i) + 1.0, 0.0, format!("{i}", i = i % 10));
}
if i % 2 == 0 && i % 10 != 0 {
ctx.print(0.0, f64::from(i), format!("{i}", i = i % 10));
}
}
})
}
}
fn init_terminal() -> io::Result<Terminal<CrosstermBackend<Stdout>>> {
enable_raw_mode()?;
stdout().execute(EnterAlternateScreen)?;
Terminal::new(CrosstermBackend::new(stdout()))
}
fn restore_terminal() -> io::Result<()> {
disable_raw_mode()?;
stdout().execute(LeaveAlternateScreen)?;
Ok(())
}

View File

@@ -1,18 +0,0 @@
use color_eyre::{config::HookBuilder, Result};
use crate::term;
pub fn init_hooks() -> Result<()> {
let (panic, error) = HookBuilder::default().into_hooks();
let panic = panic.into_panic_hook();
let error = error.into_eyre_hook();
color_eyre::eyre::set_hook(Box::new(move |e| {
let _ = term::restore();
error(e)
}))?;
std::panic::set_hook(Box::new(move |info| {
let _ = term::restore();
panic(info);
}));
Ok(())
}

View File

@@ -1,43 +0,0 @@
//! # [Ratatui] Demo2 example
//!
//! The latest version of this example is available in the [examples] folder in the repository.
//!
//! Please note that the examples are designed to be run against the `main` branch of the Github
//! repository. This means that you may not be able to compile with the latest release version on
//! crates.io, or the one that you have installed locally.
//!
//! See the [examples readme] for more information on finding examples that match the version of the
//! library you are using.
//!
//! [Ratatui]: https://github.com/ratatui-org/ratatui
//! [examples]: https://github.com/ratatui-org/ratatui/blob/main/examples
//! [examples readme]: https://github.com/ratatui-org/ratatui/blob/main/examples/README.md
#![allow(
clippy::missing_errors_doc,
clippy::module_name_repetitions,
clippy::must_use_candidate
)]
mod app;
mod colors;
mod destroy;
mod errors;
mod tabs;
mod term;
mod theme;
use color_eyre::Result;
pub use self::{
colors::{color_from_oklab, RgbSwatch},
theme::THEME,
};
fn main() -> Result<()> {
errors::init_hooks()?;
let terminal = &mut term::init()?;
app::run(terminal)?;
term::restore()?;
Ok(())
}

View File

@@ -1,165 +0,0 @@
use itertools::Itertools;
use ratatui::{
buffer::Buffer,
layout::{Alignment, Constraint, Layout, Margin, Rect},
widgets::{Block, Borders, Clear, Padding, Paragraph, Widget, Wrap},
};
use crate::{RgbSwatch, THEME};
const RATATUI_LOGO: [&str; 32] = [
" ███ ",
" ██████ ",
" ███████ ",
" ████████ ",
" █████████ ",
" ██████████ ",
" ████████████ ",
" █████████████ ",
" █████████████ ██████",
" ███████████ ████████",
" █████ ███████████ ",
" ███ ██ee████████ ",
" █ █████████████ ",
" ████ █████████████ ",
" █████████████████ ",
" ████████████████ ",
" ████████████████ ",
" ███ ██████████ ",
" ██ █████████ ",
" █xx█ █████████ ",
" █xxxx█ ██████████ ",
" █xx█xxx█ █████████ ",
" █xx██xxxx█ ████████ ",
" █xxxxxxxxxx█ ██████████ ",
" █xxxxxxxxxxxx█ ██████████ ",
" █xxxxxxx██xxxxx█ █████████ ",
" █xxxxxxxxx██xxxxx█ ████ ███ ",
" █xxxxxxxxxxxxxxxxxx█ ██ ███ ",
"█xxxxxxxxxxxxxxxxxxxx█ █ ███ ",
"█xxxxxxxxxxxxxxxxxxxxx█ ███ ",
" █xxxxxxxxxxxxxxxxxxxxx█ ███ ",
" █xxxxxxxxxxxxxxxxxxxxx█ █ ",
];
#[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 horizontal = Layout::horizontal([Constraint::Length(34), Constraint::Min(0)]);
let [description, logo] = horizontal.areas(area);
render_crate_description(description, buf);
render_logo(self.row_index, logo, 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);
}
/// Use half block characters to render a logo based on the `RATATUI_LOGO` const.
///
/// The logo is rendered in three colors, one for the rat, one for the terminal, and one for the
/// rat's eye. The eye color alternates between two colors based on the selected row.
#[allow(clippy::cast_possible_truncation)]
pub fn render_logo(selected_row: usize, area: Rect, buf: &mut Buffer) {
let eye_color = if selected_row % 2 == 0 {
THEME.logo.rat_eye
} else {
THEME.logo.rat_eye_alt
};
let area = area.inner(Margin {
vertical: 0,
horizontal: 2,
});
for (y, (line1, line2)) in RATATUI_LOGO.iter().tuples().enumerate() {
for (x, (ch1, ch2)) in line1.chars().zip(line2.chars()).enumerate() {
let x = area.left() + x as u16;
let y = area.top() + y as u16;
let cell = &mut buf[(x, y)];
let rat_color = THEME.logo.rat;
let term_color = THEME.logo.term;
match (ch1, ch2) {
('█', '█') => {
cell.set_char('█');
cell.fg = rat_color;
cell.bg = rat_color;
}
('█', ' ') => {
cell.set_char('▀');
cell.fg = rat_color;
}
(' ', '█') => {
cell.set_char('▄');
cell.fg = rat_color;
}
('█', 'x') => {
cell.set_char('▀');
cell.fg = rat_color;
cell.bg = term_color;
}
('x', '█') => {
cell.set_char('▄');
cell.fg = rat_color;
cell.bg = term_color;
}
('x', 'x') => {
cell.set_char(' ');
cell.fg = term_color;
cell.bg = term_color;
}
('█', 'e') => {
cell.set_char('▀');
cell.fg = rat_color;
cell.bg = eye_color;
}
('e', '█') => {
cell.set_char('▄');
cell.fg = rat_color;
cell.bg = eye_color;
}
(_, _) => {}
};
}
}
}

View File

@@ -1,46 +0,0 @@
use std::{
io::{self, stdout},
time::Duration,
};
use color_eyre::{eyre::WrapErr, Result};
use ratatui::{
backend::{Backend, CrosstermBackend},
crossterm::{
event::{self, Event},
terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
ExecutableCommand,
},
layout::Rect,
Terminal, TerminalOptions, Viewport,
};
pub fn init() -> Result<Terminal<impl Backend>> {
// this size is to match the size of the terminal when running the demo
// using vhs in a 1280x640 sized window (github social preview size)
let options = TerminalOptions {
viewport: Viewport::Fixed(Rect::new(0, 0, 81, 18)),
};
let terminal = Terminal::with_options(CrosstermBackend::new(io::stdout()), options)?;
enable_raw_mode().context("enable raw mode")?;
stdout()
.execute(EnterAlternateScreen)
.wrap_err("enter alternate screen")?;
Ok(terminal)
}
pub fn restore() -> Result<()> {
disable_raw_mode().context("disable raw mode")?;
stdout()
.execute(LeaveAlternateScreen)
.wrap_err("leave alternate screen")?;
Ok(())
}
pub fn next_event(timeout: Duration) -> Result<Option<Event>> {
if !event::poll(timeout)? {
return Ok(None);
}
let event = event::read()?;
Ok(Some(event))
}

View File

@@ -1,112 +0,0 @@
//! # [Ratatui] Hello World example
//!
//! The latest version of this example is available in the [examples] folder in the repository.
//!
//! Please note that the examples are designed to be run against the `main` branch of the Github
//! repository. This means that you may not be able to compile with the latest release version on
//! crates.io, or the one that you have installed locally.
//!
//! See the [examples readme] for more information on finding examples that match the version of the
//! library you are using.
//!
//! [Ratatui]: https://github.com/ratatui-org/ratatui
//! [examples]: https://github.com/ratatui-org/ratatui/blob/main/examples
//! [examples readme]: https://github.com/ratatui-org/ratatui/blob/main/examples/README.md
use std::{
io::{self, Stdout},
time::Duration,
};
use color_eyre::{eyre::Context, Result};
use ratatui::{
backend::CrosstermBackend,
crossterm::{
event::{self, Event, KeyCode},
execute,
terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
},
widgets::Paragraph,
Frame, Terminal,
};
/// This is a bare minimum example. There are many approaches to running an application loop, so
/// this is not meant to be prescriptive. It is only meant to demonstrate the basic setup and
/// teardown of a terminal application.
///
/// This example does not handle events or update the application state. It just draws a greeting
/// and exits when the user presses 'q'.
fn main() -> Result<()> {
color_eyre::install()?; // augment errors / panics with easy to read messages
let mut terminal = init_terminal().context("setup failed")?;
let result = run(&mut terminal).context("app loop failed");
restore_terminal();
result
}
/// Setup the terminal. This is where you would enable raw mode, enter the alternate screen, and
/// hide the cursor. This example does not handle errors.
fn init_terminal() -> Result<Terminal<CrosstermBackend<Stdout>>> {
set_panic_hook();
let mut stdout = io::stdout();
enable_raw_mode().context("failed to enable raw mode")?;
execute!(stdout, EnterAlternateScreen).context("unable to enter alternate screen")?;
Terminal::new(CrosstermBackend::new(stdout)).context("creating terminal failed")
}
/// Restore the terminal. This is where you disable raw mode, leave the alternate screen, and show
/// the cursor.
fn restore_terminal() {
// There's not a lot we can do if these fail, so we just print an error message.
if let Err(err) = disable_raw_mode() {
eprintln!("Error disabling raw mode: {err}");
}
if let Err(err) = execute!(io::stdout(), LeaveAlternateScreen) {
eprintln!("Error leaving alternate screen: {err}");
}
}
/// Replace the default panic hook with one that restores the terminal before panicking.
fn set_panic_hook() {
let hook = std::panic::take_hook();
std::panic::set_hook(Box::new(move |panic_info| {
restore_terminal();
hook(panic_info);
}));
}
/// Run the application loop. This is where you would handle events and update the application
/// state. This example exits when the user presses 'q'. Other styles of application loops are
/// possible, for example, you could have multiple application states and switch between them based
/// on events, or you could have a single application state and update it based on events.
fn run(terminal: &mut Terminal<CrosstermBackend<Stdout>>) -> Result<()> {
loop {
terminal.draw(render_app)?;
if should_quit()? {
break;
}
}
Ok(())
}
/// Render the application. This is where you would draw the application UI. This example just
/// draws a greeting.
fn render_app(frame: &mut Frame) {
let greeting = Paragraph::new("Hello World! (press 'q' to quit)");
frame.render_widget(greeting, frame.area());
}
/// Check if the user has pressed 'q'. This is where you would handle events. This example just
/// checks if the user has pressed 'q' and returns true if they have. It does not handle any other
/// events. There is a 250ms timeout on the event poll so that the application can exit in a timely
/// manner, and to ensure that the terminal is rendered at least once every 250ms. This allows you
/// to do other work in the application loop, such as updating the application state, without
/// blocking the event loop for too long.
fn should_quit() -> Result<bool> {
if event::poll(Duration::from_millis(250)).context("event poll failed")? {
if let Event::Key(key) = event::read().context("event read failed")? {
return Ok(KeyCode::Char('q') == key.code);
}
}
Ok(false)
}

View File

@@ -1,48 +0,0 @@
//! # [Ratatui] Minimal example
//!
//! The latest version of this example is available in the [examples] folder in the repository.
//!
//! Please note that the examples are designed to be run against the `main` branch of the Github
//! repository. This means that you may not be able to compile with the latest release version on
//! crates.io, or the one that you have installed locally.
//!
//! See the [examples readme] for more information on finding examples that match the version of the
//! library you are using.
//!
//! [Ratatui]: https://github.com/ratatui-org/ratatui
//! [examples]: https://github.com/ratatui-org/ratatui/blob/main/examples
//! [examples readme]: https://github.com/ratatui-org/ratatui/blob/main/examples/README.md
use ratatui::{
backend::CrosstermBackend,
crossterm::{
event::{self, Event, KeyCode, KeyEventKind},
execute,
terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
},
text::Text,
Terminal,
};
/// 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.
///
/// [examples]: https://github.com/ratatui-org/ratatui/blob/main/examples
/// [hello-world]: https://github.com/ratatui-org/ratatui/blob/main/examples/hello_world.rs
fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut terminal = Terminal::new(CrosstermBackend::new(std::io::stdout()))?;
enable_raw_mode()?;
execute!(terminal.backend_mut(), EnterAlternateScreen)?;
loop {
terminal.draw(|frame| frame.render_widget(Text::raw("Hello World!"), frame.area()))?;
if let Event::Key(key) = event::read()? {
if key.kind == KeyEventKind::Press && key.code == KeyCode::Char('q') {
break;
}
}
}
disable_raw_mode()?;
execute!(terminal.backend_mut(), LeaveAlternateScreen)?;
Ok(())
}

View File

@@ -1,152 +0,0 @@
//! # [Ratatui] Panic Hook example
//!
//! The latest version of this example is available in the [examples] folder in the repository.
//!
//! Please note that the examples are designed to be run against the `main` branch of the Github
//! repository. This means that you may not be able to compile with the latest release version on
//! crates.io, or the one that you have installed locally.
//!
//! See the [examples readme] for more information on finding examples that match the version of the
//! library you are using.
//!
//! [Ratatui]: https://github.com/ratatui-org/ratatui
//! [examples]: https://github.com/ratatui-org/ratatui/blob/main/examples
//! [examples readme]: https://github.com/ratatui-org/ratatui/blob/main/examples/README.md
//! How to use a panic hook to reset the terminal before printing the panic to
//! the terminal.
//!
//! 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 chained panic hook, to see the difference.
use std::{error::Error, io};
use ratatui::{
backend::{Backend, CrosstermBackend},
crossterm::{
event::{self, Event, KeyCode},
terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
},
text::Line,
widgets::{Block, Paragraph},
Frame, Terminal,
};
type Result<T> = std::result::Result<T, Box<dyn Error>>;
#[derive(Default)]
struct App {
hook_enabled: bool,
}
impl App {
fn chain_hook(&mut self) {
let original_hook = std::panic::take_hook();
std::panic::set_hook(Box::new(move |panic| {
reset_terminal().unwrap();
original_hook(panic);
}));
self.hook_enabled = true;
}
}
fn main() -> Result<()> {
let mut terminal = init_terminal()?;
let mut app = App::default();
let res = run_tui(&mut terminal, &mut app);
reset_terminal()?;
if let Err(err) = res {
println!("{err:?}");
}
Ok(())
}
/// Initializes the terminal.
fn init_terminal() -> Result<Terminal<CrosstermBackend<io::Stdout>>> {
crossterm::execute!(io::stdout(), EnterAlternateScreen)?;
enable_raw_mode()?;
let backend = CrosstermBackend::new(io::stdout());
let mut terminal = Terminal::new(backend)?;
terminal.hide_cursor()?;
Ok(terminal)
}
/// Resets the terminal.
fn reset_terminal() -> Result<()> {
disable_raw_mode()?;
crossterm::execute!(io::stdout(), LeaveAlternateScreen)?;
Ok(())
}
/// Runs the TUI loop.
fn run_tui<B: Backend>(terminal: &mut Terminal<B>, app: &mut App) -> io::Result<()> {
loop {
terminal.draw(|f| ui(f, app))?;
if let Event::Key(key) = event::read()? {
match key.code {
KeyCode::Char('p') => {
panic!("intentional demo panic");
}
KeyCode::Char('e') => {
app.chain_hook();
}
_ => {
return Ok(());
}
}
}
}
}
/// Render the TUI.
fn ui(f: &mut Frame, app: &App) {
let text = vec![
if app.hook_enabled {
Line::from("HOOK IS CURRENTLY **ENABLED**")
} else {
Line::from("HOOK IS CURRENTLY **DISABLED**")
},
Line::from(""),
Line::from("press `p` to panic"),
Line::from("press `e` to enable the terminal-resetting panic hook"),
Line::from("press any other key to quit without panic"),
Line::from(""),
Line::from("when you panic without the chained hook,"),
Line::from("you will likely have to reset your terminal afterwards"),
Line::from("with the `reset` command"),
Line::from(""),
Line::from("with the chained panic hook enabled,"),
Line::from("you should see the panic report as you would without ratatui"),
Line::from(""),
Line::from("try first without the panic handler to see the difference"),
];
let paragraph = Paragraph::new(text)
.block(Block::bordered().title("Panic Handler Demo"))
.centered();
f.render_widget(paragraph, f.area());
}

View File

@@ -1,231 +0,0 @@
//! # [Ratatui] Paragraph example
//!
//! The latest version of this example is available in the [examples] folder in the repository.
//!
//! Please note that the examples are designed to be run against the `main` branch of the Github
//! repository. This means that you may not be able to compile with the latest release version on
//! crates.io, or the one that you have installed locally.
//!
//! See the [examples readme] for more information on finding examples that match the version of the
//! library you are using.
//!
//! [Ratatui]: https://github.com/ratatui-org/ratatui
//! [examples]: https://github.com/ratatui-org/ratatui/blob/main/examples
//! [examples readme]: https://github.com/ratatui-org/ratatui/blob/main/examples/README.md
use std::{
io::{self},
time::{Duration, Instant},
};
use crossterm::event::KeyEventKind;
use ratatui::{
buffer::Buffer,
crossterm::event::{self, Event, KeyCode},
layout::{Constraint, Layout, Rect},
style::{Color, Stylize},
text::{Line, Masked, Span},
widgets::{Block, Paragraph, Widget, Wrap},
};
use self::common::{init_terminal, install_hooks, restore_terminal, Tui};
fn main() -> color_eyre::Result<()> {
install_hooks()?;
let mut terminal = init_terminal()?;
let mut app = App::new();
app.run(&mut terminal)?;
restore_terminal()?;
Ok(())
}
#[derive(Debug)]
struct App {
should_exit: bool,
scroll: u16,
last_tick: Instant,
}
impl App {
/// The duration between each tick.
const TICK_RATE: Duration = Duration::from_millis(250);
/// Create a new instance of the app.
fn new() -> Self {
Self {
should_exit: false,
scroll: 0,
last_tick: Instant::now(),
}
}
/// Run the app until the user exits.
fn run(&mut self, terminal: &mut Tui) -> io::Result<()> {
while !self.should_exit {
self.draw(terminal)?;
self.handle_events()?;
if self.last_tick.elapsed() >= Self::TICK_RATE {
self.on_tick();
self.last_tick = Instant::now();
}
}
Ok(())
}
/// Draw the app to the terminal.
fn draw(&mut self, terminal: &mut Tui) -> io::Result<()> {
terminal.draw(|frame| frame.render_widget(self, frame.area()))?;
Ok(())
}
/// Handle events from the terminal.
fn handle_events(&mut self) -> io::Result<()> {
let timeout = Self::TICK_RATE.saturating_sub(self.last_tick.elapsed());
while event::poll(timeout)? {
if let Event::Key(key) = event::read()? {
if key.kind == KeyEventKind::Press && key.code == KeyCode::Char('q') {
self.should_exit = true;
}
}
}
Ok(())
}
/// Update the app state on each tick.
fn on_tick(&mut self) {
self.scroll = (self.scroll + 1) % 10;
}
}
impl Widget for &mut App {
fn render(self, area: Rect, buf: &mut Buffer) {
let areas = Layout::vertical([Constraint::Max(9); 4]).split(area);
Paragraph::new(create_lines(area))
.block(title_block("Default alignment (Left), no wrap"))
.gray()
.render(areas[0], buf);
Paragraph::new(create_lines(area))
.block(title_block("Default alignment (Left), with wrap"))
.gray()
.wrap(Wrap { trim: true })
.render(areas[1], buf);
Paragraph::new(create_lines(area))
.block(title_block("Right alignment, with wrap"))
.gray()
.right_aligned()
.wrap(Wrap { trim: true })
.render(areas[2], buf);
Paragraph::new(create_lines(area))
.block(title_block("Center alignment, with wrap, with scroll"))
.gray()
.centered()
.wrap(Wrap { trim: true })
.scroll((self.scroll, 0))
.render(areas[3], buf);
}
}
/// Create a bordered block with a title.
fn title_block(title: &str) -> Block {
Block::bordered()
.gray()
.title(title.bold().into_centered_line())
}
/// Create some lines to display in the paragraph.
fn create_lines(area: Rect) -> Vec<Line<'static>> {
let short_line = "A long line to demonstrate line wrapping. ";
let long_line = short_line.repeat(usize::from(area.width) / short_line.len() + 4);
let mut styled_spans = vec![];
for span in [
"Styled".blue(),
"Spans".red().on_white(),
"Bold".bold(),
"Italic".italic(),
"Underlined".underlined(),
"Strikethrough".crossed_out(),
] {
styled_spans.push(span);
styled_spans.push(" ".into());
}
vec![
Line::raw("Unstyled Line"),
Line::raw("Styled Line").black().on_red().bold().italic(),
Line::from(styled_spans),
Line::from(long_line.green().italic()),
Line::from_iter([
"Masked text: ".into(),
Span::styled(Masked::new("my secret password", '*'), Color::Red),
]),
]
}
/// A module for common functionality used in the examples.
mod common {
use std::{
io::{self, stdout, Stdout},
panic,
};
use color_eyre::{
config::{EyreHook, HookBuilder, PanicHook},
eyre,
};
use crossterm::ExecutableCommand;
use ratatui::{
backend::CrosstermBackend,
crossterm::terminal::{
disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen,
},
Terminal,
};
// A simple alias for the terminal type used in this example.
pub type Tui = Terminal<CrosstermBackend<Stdout>>;
/// Initialize the terminal and enter alternate screen mode.
pub fn init_terminal() -> io::Result<Tui> {
enable_raw_mode()?;
stdout().execute(EnterAlternateScreen)?;
let backend = CrosstermBackend::new(stdout());
Terminal::new(backend)
}
/// Restore the terminal to its original state.
pub fn restore_terminal() -> io::Result<()> {
disable_raw_mode()?;
stdout().execute(LeaveAlternateScreen)?;
Ok(())
}
/// Installs hooks for panic and error handling.
///
/// Makes the app resilient to panics and errors by restoring the terminal before printing the
/// panic or error message. This prevents error messages from being messed up by the terminal
/// state.
pub fn install_hooks() -> color_eyre::Result<()> {
let (panic_hook, eyre_hook) = HookBuilder::default().into_hooks();
install_panic_hook(panic_hook);
install_error_hook(eyre_hook)?;
Ok(())
}
/// Install a panic hook that restores the terminal before printing the panic.
fn install_panic_hook(panic_hook: PanicHook) {
let panic_hook = panic_hook.into_panic_hook();
panic::set_hook(Box::new(move |panic_info| {
let _ = restore_terminal();
panic_hook(panic_info);
}));
}
/// Install an error hook that restores the terminal before printing the error.
fn install_error_hook(eyre_hook: EyreHook) -> color_eyre::Result<()> {
let eyre_hook = eyre_hook.into_eyre_hook();
eyre::set_hook(Box::new(move |error| {
let _ = restore_terminal();
eyre_hook(error)
}))?;
Ok(())
}
}

View File

@@ -1,130 +0,0 @@
//! # [Ratatui] Popup example
//!
//! The latest version of this example is available in the [examples] folder in the repository.
//!
//! Please note that the examples are designed to be run against the `main` branch of the Github
//! repository. This means that you may not be able to compile with the latest release version on
//! crates.io, or the one that you have installed locally.
//!
//! See the [examples readme] for more information on finding examples that match the version of the
//! library you are using.
//!
//! [Ratatui]: https://github.com/ratatui-org/ratatui
//! [examples]: https://github.com/ratatui-org/ratatui/blob/main/examples
//! [examples readme]: https://github.com/ratatui-org/ratatui/blob/main/examples/README.md
// See also https://github.com/joshka/tui-popup and
// https://github.com/sephiroth74/tui-confirm-dialog
use std::{error::Error, io};
use ratatui::{
backend::{Backend, CrosstermBackend},
crossterm::{
event::{self, DisableMouseCapture, EnableMouseCapture, Event, KeyCode, KeyEventKind},
execute,
terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
},
layout::{Constraint, Layout, Rect},
style::Stylize,
widgets::{Block, Clear, Paragraph, Wrap},
Frame, Terminal,
};
struct App {
show_popup: bool,
}
impl App {
const fn new() -> Self {
Self { show_popup: false }
}
}
fn main() -> Result<(), Box<dyn Error>> {
// setup terminal
enable_raw_mode()?;
let mut stdout = io::stdout();
execute!(stdout, EnterAlternateScreen, EnableMouseCapture)?;
let backend = CrosstermBackend::new(stdout);
let mut terminal = Terminal::new(backend)?;
// create app and run it
let app = App::new();
let res = run_app(&mut terminal, app);
// restore terminal
disable_raw_mode()?;
execute!(
terminal.backend_mut(),
LeaveAlternateScreen,
DisableMouseCapture
)?;
terminal.show_cursor()?;
if let Err(err) = res {
println!("{err:?}");
}
Ok(())
}
fn run_app<B: Backend>(terminal: &mut Terminal<B>, mut app: App) -> io::Result<()> {
loop {
terminal.draw(|f| ui(f, &app))?;
if let Event::Key(key) = event::read()? {
if key.kind == KeyEventKind::Press {
match key.code {
KeyCode::Char('q') => return Ok(()),
KeyCode::Char('p') => app.show_popup = !app.show_popup,
_ => {}
}
}
}
}
}
fn ui(f: &mut Frame, app: &App) {
let area = f.area();
let vertical = Layout::vertical([Constraint::Percentage(20), Constraint::Percentage(80)]);
let [instructions, content] = vertical.areas(area);
let text = if app.show_popup {
"Press p to close the popup"
} else {
"Press p to show the popup"
};
let paragraph = Paragraph::new(text.slow_blink())
.centered()
.wrap(Wrap { trim: true });
f.render_widget(paragraph, instructions);
let block = Block::bordered().title("Content").on_blue();
f.render_widget(block, content);
if app.show_popup {
let block = Block::bordered().title("Popup");
let area = centered_rect(60, 20, area);
f.render_widget(Clear, area); //this clears out the background
f.render_widget(block, area);
}
}
/// helper function to create a centered rect using up certain percentage of the available rect `r`
fn centered_rect(percent_x: u16, percent_y: u16, r: Rect) -> Rect {
let popup_layout = Layout::vertical([
Constraint::Percentage((100 - percent_y) / 2),
Constraint::Percentage(percent_y),
Constraint::Percentage((100 - percent_y) / 2),
])
.split(r);
Layout::horizontal([
Constraint::Percentage((100 - percent_x) / 2),
Constraint::Percentage(percent_x),
Constraint::Percentage((100 - percent_x) / 2),
])
.split(popup_layout[1])[1]
}

View File

@@ -1,85 +0,0 @@
//! # [Ratatui] Logo example
//!
//! The latest version of this example is available in the [examples] folder in the repository.
//!
//! Please note that the examples are designed to be run against the `main` branch of the Github
//! repository. This means that you may not be able to compile with the latest release version on
//! crates.io, or the one that you have installed locally.
//!
//! See the [examples readme] for more information on finding examples that match the version of the
//! library you are using.
//!
//! [Ratatui]: https://github.com/ratatui-org/ratatui
//! [examples]: https://github.com/ratatui-org/ratatui/blob/main/examples
//! [examples readme]: https://github.com/ratatui-org/ratatui/blob/main/examples/README.md
use std::{
io::{self, stdout},
thread::sleep,
time::Duration,
};
use indoc::indoc;
use itertools::izip;
use ratatui::{
backend::{Backend, CrosstermBackend},
crossterm::terminal::{disable_raw_mode, enable_raw_mode},
widgets::Paragraph,
Terminal, TerminalOptions, Viewport,
};
/// A fun example of using half block characters to draw a logo
#[allow(clippy::many_single_char_names)]
fn logo() -> String {
let r = indoc! {"
▄▄▄
█▄▄▀
█ █
"};
let a = indoc! {"
▄▄
█▄▄█
█ █
"};
let t = indoc! {"
▄▄▄
"};
let u = indoc! {"
▄ ▄
█ █
▀▄▄▀
"};
let i = indoc! {"
"};
izip!(r.lines(), a.lines(), t.lines(), u.lines(), i.lines())
.map(|(r, a, t, u, i)| format!("{r:5}{a:5}{t:4}{a:5}{t:4}{u:5}{i:5}"))
.collect::<Vec<_>>()
.join("\n")
}
fn main() -> io::Result<()> {
let mut terminal = init()?;
terminal.draw(|frame| frame.render_widget(Paragraph::new(logo()), frame.area()))?;
sleep(Duration::from_secs(5));
restore()?;
println!();
Ok(())
}
fn init() -> io::Result<Terminal<impl Backend>> {
enable_raw_mode()?;
let options = TerminalOptions {
viewport: Viewport::Inline(3),
};
Terminal::with_options(CrosstermBackend::new(stdout()), options)
}
fn restore() -> io::Result<()> {
disable_raw_mode()?;
Ok(())
}

View File

@@ -1,238 +0,0 @@
//! # [Ratatui] Scrollbar example
//!
//! The latest version of this example is available in the [examples] folder in the repository.
//!
//! Please note that the examples are designed to be run against the `main` branch of the Github
//! repository. This means that you may not be able to compile with the latest release version on
//! crates.io, or the one that you have installed locally.
//!
//! See the [examples readme] for more information on finding examples that match the version of the
//! library you are using.
//!
//! [Ratatui]: https://github.com/ratatui-org/ratatui
//! [examples]: https://github.com/ratatui-org/ratatui/blob/main/examples
//! [examples readme]: https://github.com/ratatui-org/ratatui/blob/main/examples/README.md
#![warn(clippy::pedantic)]
use std::{
error::Error,
io,
time::{Duration, Instant},
};
use ratatui::{
backend::{Backend, CrosstermBackend},
crossterm::{
event::{self, DisableMouseCapture, EnableMouseCapture, Event, KeyCode},
execute,
terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
},
layout::{Alignment, Constraint, Layout, Margin},
style::{Color, Style, Stylize},
symbols::scrollbar,
text::{Line, Masked, Span},
widgets::{Block, Paragraph, Scrollbar, ScrollbarOrientation, ScrollbarState},
Frame, Terminal,
};
#[derive(Default)]
struct App {
pub vertical_scroll_state: ScrollbarState,
pub horizontal_scroll_state: ScrollbarState,
pub vertical_scroll: usize,
pub horizontal_scroll: usize,
}
fn main() -> Result<(), Box<dyn Error>> {
// setup terminal
enable_raw_mode()?;
let mut stdout = io::stdout();
execute!(stdout, EnterAlternateScreen, EnableMouseCapture)?;
let backend = CrosstermBackend::new(stdout);
let mut terminal = Terminal::new(backend)?;
// create app and run it
let tick_rate = Duration::from_millis(250);
let app = App::default();
let res = run_app(&mut terminal, app, tick_rate);
// restore terminal
disable_raw_mode()?;
execute!(
terminal.backend_mut(),
LeaveAlternateScreen,
DisableMouseCapture
)?;
terminal.show_cursor()?;
if let Err(err) = res {
println!("{err:?}");
}
Ok(())
}
fn run_app<B: Backend>(
terminal: &mut Terminal<B>,
mut app: App,
tick_rate: Duration,
) -> io::Result<()> {
let mut last_tick = Instant::now();
loop {
terminal.draw(|f| ui(f, &mut app))?;
let timeout = tick_rate.saturating_sub(last_tick.elapsed());
if crossterm::event::poll(timeout)? {
if let Event::Key(key) = event::read()? {
match key.code {
KeyCode::Char('q') => return Ok(()),
KeyCode::Char('j') | KeyCode::Down => {
app.vertical_scroll = app.vertical_scroll.saturating_add(1);
app.vertical_scroll_state =
app.vertical_scroll_state.position(app.vertical_scroll);
}
KeyCode::Char('k') | KeyCode::Up => {
app.vertical_scroll = app.vertical_scroll.saturating_sub(1);
app.vertical_scroll_state =
app.vertical_scroll_state.position(app.vertical_scroll);
}
KeyCode::Char('h') | KeyCode::Left => {
app.horizontal_scroll = app.horizontal_scroll.saturating_sub(1);
app.horizontal_scroll_state =
app.horizontal_scroll_state.position(app.horizontal_scroll);
}
KeyCode::Char('l') | KeyCode::Right => {
app.horizontal_scroll = app.horizontal_scroll.saturating_add(1);
app.horizontal_scroll_state =
app.horizontal_scroll_state.position(app.horizontal_scroll);
}
_ => {}
}
}
}
if last_tick.elapsed() >= tick_rate {
last_tick = Instant::now();
}
}
}
#[allow(clippy::too_many_lines, clippy::cast_possible_truncation)]
fn ui(f: &mut Frame, app: &mut App) {
let area = f.area();
// Words made "loooong" to demonstrate line breaking.
let s = "Veeeeeeeeeeeeeeeery loooooooooooooooooong striiiiiiiiiiiiiiiiiiiiiiiiiing. ";
let mut long_line = s.repeat(usize::from(area.width) / s.len() + 4);
long_line.push('\n');
let chunks = Layout::vertical([
Constraint::Min(1),
Constraint::Percentage(25),
Constraint::Percentage(25),
Constraint::Percentage(25),
Constraint::Percentage(25),
])
.split(area);
let text = vec![
Line::from("This is a line "),
Line::from("This is a line ".red()),
Line::from("This is a line".on_dark_gray()),
Line::from("This is a longer line".crossed_out()),
Line::from(long_line.clone()),
Line::from("This is a line".reset()),
Line::from(vec![
Span::raw("Masked text: "),
Span::styled(Masked::new("password", '*'), Style::new().fg(Color::Red)),
]),
Line::from("This is a line "),
Line::from("This is a line ".red()),
Line::from("This is a line".on_dark_gray()),
Line::from("This is a longer line".crossed_out()),
Line::from(long_line.clone()),
Line::from("This is a line".reset()),
Line::from(vec![
Span::raw("Masked text: "),
Span::styled(Masked::new("password", '*'), Style::new().fg(Color::Red)),
]),
];
app.vertical_scroll_state = app.vertical_scroll_state.content_length(text.len());
app.horizontal_scroll_state = app.horizontal_scroll_state.content_length(long_line.len());
let create_block = |title: &'static str| Block::bordered().gray().title(title.bold());
let title = Block::new()
.title_alignment(Alignment::Center)
.title("Use h j k l or ◄ ▲ ▼ ► to scroll ".bold());
f.render_widget(title, chunks[0]);
let paragraph = Paragraph::new(text.clone())
.gray()
.block(create_block("Vertical scrollbar with arrows"))
.scroll((app.vertical_scroll as u16, 0));
f.render_widget(paragraph, chunks[1]);
f.render_stateful_widget(
Scrollbar::new(ScrollbarOrientation::VerticalRight)
.begin_symbol(Some(""))
.end_symbol(Some("")),
chunks[1],
&mut app.vertical_scroll_state,
);
let paragraph = Paragraph::new(text.clone())
.gray()
.block(create_block(
"Vertical scrollbar without arrows, without track symbol and mirrored",
))
.scroll((app.vertical_scroll as u16, 0));
f.render_widget(paragraph, chunks[2]);
f.render_stateful_widget(
Scrollbar::new(ScrollbarOrientation::VerticalLeft)
.symbols(scrollbar::VERTICAL)
.begin_symbol(None)
.track_symbol(None)
.end_symbol(None),
chunks[2].inner(Margin {
vertical: 1,
horizontal: 0,
}),
&mut app.vertical_scroll_state,
);
let paragraph = Paragraph::new(text.clone())
.gray()
.block(create_block(
"Horizontal scrollbar with only begin arrow & custom thumb symbol",
))
.scroll((0, app.horizontal_scroll as u16));
f.render_widget(paragraph, chunks[3]);
f.render_stateful_widget(
Scrollbar::new(ScrollbarOrientation::HorizontalBottom)
.thumb_symbol("🬋")
.end_symbol(None),
chunks[3].inner(Margin {
vertical: 0,
horizontal: 1,
}),
&mut app.horizontal_scroll_state,
);
let paragraph = Paragraph::new(text.clone())
.gray()
.block(create_block(
"Horizontal scrollbar without arrows & custom thumb and track symbol",
))
.scroll((0, app.horizontal_scroll as u16));
f.render_widget(paragraph, chunks[4]);
f.render_stateful_widget(
Scrollbar::new(ScrollbarOrientation::HorizontalBottom)
.thumb_symbol("")
.track_symbol(Some("")),
chunks[4].inner(Margin {
vertical: 0,
horizontal: 1,
}),
&mut app.horizontal_scroll_state,
);
}

View File

@@ -1,183 +0,0 @@
//! # [Ratatui] Sparkline example
//!
//! The latest version of this example is available in the [examples] folder in the repository.
//!
//! Please note that the examples are designed to be run against the `main` branch of the Github
//! repository. This means that you may not be able to compile with the latest release version on
//! crates.io, or the one that you have installed locally.
//!
//! See the [examples readme] for more information on finding examples that match the version of the
//! library you are using.
//!
//! [Ratatui]: https://github.com/ratatui-org/ratatui
//! [examples]: https://github.com/ratatui-org/ratatui/blob/main/examples
//! [examples readme]: https://github.com/ratatui-org/ratatui/blob/main/examples/README.md
use std::{
error::Error,
io,
time::{Duration, Instant},
};
use rand::{
distributions::{Distribution, Uniform},
rngs::ThreadRng,
};
use ratatui::{
backend::{Backend, CrosstermBackend},
crossterm::{
event::{self, DisableMouseCapture, EnableMouseCapture, Event, KeyCode},
execute,
terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
},
layout::{Constraint, Layout},
style::{Color, Style},
widgets::{Block, Borders, Sparkline},
Frame, Terminal,
};
#[derive(Clone)]
struct RandomSignal {
distribution: Uniform<u64>,
rng: ThreadRng,
}
impl RandomSignal {
fn new(lower: u64, upper: u64) -> Self {
Self {
distribution: Uniform::new(lower, upper),
rng: rand::thread_rng(),
}
}
}
impl Iterator for RandomSignal {
type Item = u64;
fn next(&mut self) -> Option<u64> {
Some(self.distribution.sample(&mut self.rng))
}
}
struct App {
signal: RandomSignal,
data1: Vec<u64>,
data2: Vec<u64>,
data3: Vec<u64>,
}
impl App {
fn new() -> Self {
let mut signal = RandomSignal::new(0, 100);
let data1 = signal.by_ref().take(200).collect::<Vec<u64>>();
let data2 = signal.by_ref().take(200).collect::<Vec<u64>>();
let data3 = signal.by_ref().take(200).collect::<Vec<u64>>();
Self {
signal,
data1,
data2,
data3,
}
}
fn on_tick(&mut self) {
let value = self.signal.next().unwrap();
self.data1.pop();
self.data1.insert(0, value);
let value = self.signal.next().unwrap();
self.data2.pop();
self.data2.insert(0, value);
let value = self.signal.next().unwrap();
self.data3.pop();
self.data3.insert(0, value);
}
}
fn main() -> Result<(), Box<dyn Error>> {
// setup terminal
enable_raw_mode()?;
let mut stdout = io::stdout();
execute!(stdout, EnterAlternateScreen, EnableMouseCapture)?;
let backend = CrosstermBackend::new(stdout);
let mut terminal = Terminal::new(backend)?;
// create app and run it
let tick_rate = Duration::from_millis(250);
let app = App::new();
let res = run_app(&mut terminal, app, tick_rate);
// restore terminal
disable_raw_mode()?;
execute!(
terminal.backend_mut(),
LeaveAlternateScreen,
DisableMouseCapture
)?;
terminal.show_cursor()?;
if let Err(err) = res {
println!("{err:?}");
}
Ok(())
}
fn run_app<B: Backend>(
terminal: &mut Terminal<B>,
mut app: App,
tick_rate: Duration,
) -> io::Result<()> {
let mut last_tick = Instant::now();
loop {
terminal.draw(|f| ui(f, &app))?;
let timeout = tick_rate.saturating_sub(last_tick.elapsed());
if crossterm::event::poll(timeout)? {
if let Event::Key(key) = event::read()? {
if key.code == KeyCode::Char('q') {
return Ok(());
}
}
}
if last_tick.elapsed() >= tick_rate {
app.on_tick();
last_tick = Instant::now();
}
}
}
fn ui(f: &mut Frame, app: &App) {
let chunks = Layout::vertical([
Constraint::Length(3),
Constraint::Length(3),
Constraint::Min(0),
])
.split(f.area());
let sparkline = Sparkline::default()
.block(
Block::new()
.borders(Borders::LEFT | Borders::RIGHT)
.title("Data1"),
)
.data(&app.data1)
.style(Style::default().fg(Color::Yellow));
f.render_widget(sparkline, chunks[0]);
let sparkline = Sparkline::default()
.block(
Block::new()
.borders(Borders::LEFT | Borders::RIGHT)
.title("Data2"),
)
.data(&app.data2)
.style(Style::default().bg(Color::Green));
f.render_widget(sparkline, chunks[1]);
// Multiline
let sparkline = Sparkline::default()
.block(
Block::new()
.borders(Borders::LEFT | Borders::RIGHT)
.title("Data3"),
)
.data(&app.data3)
.style(Style::default().fg(Color::Red));
f.render_widget(sparkline, chunks[2]);
}

View File

@@ -1,257 +0,0 @@
//! # [Ratatui] Tabs example
//!
//! The latest version of this example is available in the [examples] folder in the repository.
//!
//! Please note that the examples are designed to be run against the `main` branch of the Github
//! repository. This means that you may not be able to compile with the latest release version on
//! crates.io, or the one that you have installed locally.
//!
//! See the [examples readme] for more information on finding examples that match the version of the
//! library you are using.
//!
//! [Ratatui]: https://github.com/ratatui-org/ratatui
//! [examples]: https://github.com/ratatui-org/ratatui/blob/main/examples
//! [examples readme]: https://github.com/ratatui-org/ratatui/blob/main/examples/README.md
use std::io::stdout;
use color_eyre::{config::HookBuilder, Result};
use ratatui::{
backend::{Backend, CrosstermBackend},
buffer::Buffer,
crossterm::{
event::{self, Event, KeyCode, KeyEventKind},
terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
ExecutableCommand,
},
layout::{Constraint, Layout, Rect},
style::{palette::tailwind, Color, Stylize},
symbols,
text::Line,
widgets::{Block, Padding, Paragraph, Tabs, Widget},
Terminal,
};
use strum::{Display, EnumIter, FromRepr, IntoEnumIterator};
#[derive(Default)]
struct App {
state: AppState,
selected_tab: SelectedTab,
}
#[derive(Default, Clone, Copy, PartialEq, Eq)]
enum AppState {
#[default]
Running,
Quitting,
}
#[derive(Default, Clone, Copy, Display, FromRepr, EnumIter)]
enum SelectedTab {
#[default]
#[strum(to_string = "Tab 1")]
Tab1,
#[strum(to_string = "Tab 2")]
Tab2,
#[strum(to_string = "Tab 3")]
Tab3,
#[strum(to_string = "Tab 4")]
Tab4,
}
fn main() -> Result<()> {
init_error_hooks()?;
let mut terminal = init_terminal()?;
App::default().run(&mut terminal)?;
restore_terminal()?;
Ok(())
}
impl App {
fn run(&mut self, terminal: &mut Terminal<impl Backend>) -> Result<()> {
while self.state == AppState::Running {
self.draw(terminal)?;
self.handle_events()?;
}
Ok(())
}
fn draw(&self, terminal: &mut Terminal<impl Backend>) -> Result<()> {
terminal.draw(|frame| frame.render_widget(self, frame.area()))?;
Ok(())
}
fn handle_events(&mut self) -> std::io::Result<()> {
if let Event::Key(key) = event::read()? {
if key.kind == KeyEventKind::Press {
match key.code {
KeyCode::Char('l') | KeyCode::Right => self.next_tab(),
KeyCode::Char('h') | KeyCode::Left => self.previous_tab(),
KeyCode::Char('q') | KeyCode::Esc => self.quit(),
_ => {}
}
}
}
Ok(())
}
pub fn next_tab(&mut self) {
self.selected_tab = self.selected_tab.next();
}
pub fn previous_tab(&mut self) {
self.selected_tab = self.selected_tab.previous();
}
pub fn quit(&mut self) {
self.state = AppState::Quitting;
}
}
impl SelectedTab {
/// Get the previous tab, if there is no previous tab return the current tab.
fn previous(self) -> Self {
let current_index: usize = self as usize;
let previous_index = current_index.saturating_sub(1);
Self::from_repr(previous_index).unwrap_or(self)
}
/// Get the next tab, if there is no next tab return the current tab.
fn next(self) -> Self {
let current_index = self as usize;
let next_index = current_index.saturating_add(1);
Self::from_repr(next_index).unwrap_or(self)
}
}
impl Widget for &App {
fn render(self, area: Rect, buf: &mut Buffer) {
use Constraint::{Length, Min};
let vertical = Layout::vertical([Length(1), Min(0), Length(1)]);
let [header_area, inner_area, footer_area] = vertical.areas(area);
let horizontal = Layout::horizontal([Min(0), Length(20)]);
let [tabs_area, title_area] = horizontal.areas(header_area);
render_title(title_area, buf);
self.render_tabs(tabs_area, buf);
self.selected_tab.render(inner_area, buf);
render_footer(footer_area, buf);
}
}
impl App {
fn render_tabs(&self, area: Rect, buf: &mut Buffer) {
let titles = SelectedTab::iter().map(SelectedTab::title);
let highlight_style = (Color::default(), self.selected_tab.palette().c700);
let selected_tab_index = self.selected_tab as usize;
Tabs::new(titles)
.highlight_style(highlight_style)
.select(selected_tab_index)
.padding("", "")
.divider(" ")
.render(area, buf);
}
}
fn render_title(area: Rect, buf: &mut Buffer) {
"Ratatui Tabs Example".bold().render(area, buf);
}
fn render_footer(area: Rect, buf: &mut Buffer) {
Line::raw("◄ ► to change tab | Press q to quit")
.centered()
.render(area, buf);
}
impl Widget for SelectedTab {
fn render(self, area: Rect, buf: &mut Buffer) {
// in a real app these might be separate widgets
match self {
Self::Tab1 => self.render_tab0(area, buf),
Self::Tab2 => self.render_tab1(area, buf),
Self::Tab3 => self.render_tab2(area, buf),
Self::Tab4 => self.render_tab3(area, buf),
}
}
}
impl SelectedTab {
/// Return tab's name as a styled `Line`
fn title(self) -> Line<'static> {
format!(" {self} ")
.fg(tailwind::SLATE.c200)
.bg(self.palette().c900)
.into()
}
fn render_tab0(self, area: Rect, buf: &mut Buffer) {
Paragraph::new("Hello, World!")
.block(self.block())
.render(area, buf);
}
fn render_tab1(self, area: Rect, buf: &mut Buffer) {
Paragraph::new("Welcome to the Ratatui tabs example!")
.block(self.block())
.render(area, buf);
}
fn render_tab2(self, area: Rect, buf: &mut Buffer) {
Paragraph::new("Look! I'm different than others!")
.block(self.block())
.render(area, buf);
}
fn render_tab3(self, area: Rect, buf: &mut Buffer) {
Paragraph::new("I know, these are some basic changes. But I think you got the main idea.")
.block(self.block())
.render(area, buf);
}
/// A block surrounding the tab's content
fn block(self) -> Block<'static> {
Block::bordered()
.border_set(symbols::border::PROPORTIONAL_TALL)
.padding(Padding::horizontal(1))
.border_style(self.palette().c700)
}
const fn palette(self) -> tailwind::Palette {
match self {
Self::Tab1 => tailwind::BLUE,
Self::Tab2 => tailwind::EMERALD,
Self::Tab3 => tailwind::INDIGO,
Self::Tab4 => tailwind::RED,
}
}
}
fn init_error_hooks() -> color_eyre::Result<()> {
let (panic, error) = HookBuilder::default().into_hooks();
let panic = panic.into_panic_hook();
let error = error.into_eyre_hook();
color_eyre::eyre::set_hook(Box::new(move |e| {
let _ = restore_terminal();
error(e)
}))?;
std::panic::set_hook(Box::new(move |info| {
let _ = restore_terminal();
panic(info);
}));
Ok(())
}
fn init_terminal() -> color_eyre::Result<Terminal<impl Backend>> {
enable_raw_mode()?;
stdout().execute(EnterAlternateScreen)?;
let backend = CrosstermBackend::new(stdout());
let terminal = Terminal::new(backend)?;
Ok(terminal)
}
fn restore_terminal() -> color_eyre::Result<()> {
disable_raw_mode()?;
stdout().execute(LeaveAlternateScreen)?;
Ok(())
}

View File

@@ -1,274 +0,0 @@
//! # [Ratatui] User Input example
//!
//! The latest version of this example is available in the [examples] folder in the repository.
//!
//! Please note that the examples are designed to be run against the `main` branch of the Github
//! repository. This means that you may not be able to compile with the latest release version on
//! crates.io, or the one that you have installed locally.
//!
//! See the [examples readme] for more information on finding examples that match the version of the
//! library you are using.
//!
//! [Ratatui]: https://github.com/ratatui-org/ratatui
//! [examples]: https://github.com/ratatui-org/ratatui/blob/main/examples
//! [examples readme]: https://github.com/ratatui-org/ratatui/blob/main/examples/README.md
// A simple example demonstrating how to handle user input. This is a bit out of the scope of
// the library as it does not provide any input handling out of the box. However, it may helps
// some to get started.
//
// This is a very simple example:
// * An input box always focused. Every character you type is registered here.
// * An entered character is inserted at the cursor position.
// * Pressing Backspace erases the left character before the cursor position
// * Pressing Enter pushes the current input in the history of previous messages. **Note: ** as
// this is a relatively simple example unicode characters are unsupported and their use will
// result in undefined behaviour.
//
// See also https://github.com/rhysd/tui-textarea and https://github.com/sayanarijit/tui-input/
use std::{error::Error, io};
use ratatui::{
backend::{Backend, CrosstermBackend},
crossterm::{
event::{self, DisableMouseCapture, EnableMouseCapture, Event, KeyCode, KeyEventKind},
execute,
terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
},
layout::{Constraint, Layout, Position},
style::{Color, Modifier, Style, Stylize},
text::{Line, Span, Text},
widgets::{Block, List, ListItem, Paragraph},
Frame, Terminal,
};
enum InputMode {
Normal,
Editing,
}
/// App holds the state of the application
struct App {
/// Current value of the input box
input: String,
/// Position of cursor in the editor area.
character_index: usize,
/// Current input mode
input_mode: InputMode,
/// History of recorded messages
messages: Vec<String>,
}
impl App {
const fn new() -> Self {
Self {
input: String::new(),
input_mode: InputMode::Normal,
messages: Vec::new(),
character_index: 0,
}
}
fn move_cursor_left(&mut self) {
let cursor_moved_left = self.character_index.saturating_sub(1);
self.character_index = self.clamp_cursor(cursor_moved_left);
}
fn move_cursor_right(&mut self) {
let cursor_moved_right = self.character_index.saturating_add(1);
self.character_index = self.clamp_cursor(cursor_moved_right);
}
fn enter_char(&mut self, new_char: char) {
let index = self.byte_index();
self.input.insert(index, new_char);
self.move_cursor_right();
}
/// Returns the byte index based on the character position.
///
/// Since each character in a string can be contain multiple bytes, it's necessary to calculate
/// the byte index based on the index of the character.
fn byte_index(&self) -> usize {
self.input
.char_indices()
.map(|(i, _)| i)
.nth(self.character_index)
.unwrap_or(self.input.len())
}
fn delete_char(&mut self) {
let is_not_cursor_leftmost = self.character_index != 0;
if is_not_cursor_leftmost {
// Method "remove" is not used on the saved text for deleting the selected char.
// Reason: Using remove on String works on bytes instead of the chars.
// Using remove would require special care because of char boundaries.
let current_index = self.character_index;
let from_left_to_current_index = current_index - 1;
// Getting all characters before the selected character.
let before_char_to_delete = self.input.chars().take(from_left_to_current_index);
// Getting all characters after selected character.
let after_char_to_delete = self.input.chars().skip(current_index);
// Put all characters together except the selected one.
// By leaving the selected one out, it is forgotten and therefore deleted.
self.input = before_char_to_delete.chain(after_char_to_delete).collect();
self.move_cursor_left();
}
}
fn clamp_cursor(&self, new_cursor_pos: usize) -> usize {
new_cursor_pos.clamp(0, self.input.chars().count())
}
fn reset_cursor(&mut self) {
self.character_index = 0;
}
fn submit_message(&mut self) {
self.messages.push(self.input.clone());
self.input.clear();
self.reset_cursor();
}
}
fn main() -> Result<(), Box<dyn Error>> {
// setup terminal
enable_raw_mode()?;
let mut stdout = io::stdout();
execute!(stdout, EnterAlternateScreen, EnableMouseCapture)?;
let backend = CrosstermBackend::new(stdout);
let mut terminal = Terminal::new(backend)?;
// create app and run it
let app = App::new();
let res = run_app(&mut terminal, app);
// restore terminal
disable_raw_mode()?;
execute!(
terminal.backend_mut(),
LeaveAlternateScreen,
DisableMouseCapture
)?;
terminal.show_cursor()?;
if let Err(err) = res {
println!("{err:?}");
}
Ok(())
}
fn run_app<B: Backend>(terminal: &mut Terminal<B>, mut app: App) -> io::Result<()> {
loop {
terminal.draw(|f| ui(f, &app))?;
if let Event::Key(key) = event::read()? {
match app.input_mode {
InputMode::Normal => match key.code {
KeyCode::Char('e') => {
app.input_mode = InputMode::Editing;
}
KeyCode::Char('q') => {
return Ok(());
}
_ => {}
},
InputMode::Editing if key.kind == KeyEventKind::Press => match key.code {
KeyCode::Enter => app.submit_message(),
KeyCode::Char(to_insert) => {
app.enter_char(to_insert);
}
KeyCode::Backspace => {
app.delete_char();
}
KeyCode::Left => {
app.move_cursor_left();
}
KeyCode::Right => {
app.move_cursor_right();
}
KeyCode::Esc => {
app.input_mode = InputMode::Normal;
}
_ => {}
},
InputMode::Editing => {}
}
}
}
}
fn ui(f: &mut Frame, app: &App) {
let vertical = Layout::vertical([
Constraint::Length(1),
Constraint::Length(3),
Constraint::Min(1),
]);
let [help_area, input_area, messages_area] = vertical.areas(f.area());
let (msg, style) = match app.input_mode {
InputMode::Normal => (
vec![
"Press ".into(),
"q".bold(),
" to exit, ".into(),
"e".bold(),
" to start editing.".bold(),
],
Style::default().add_modifier(Modifier::RAPID_BLINK),
),
InputMode::Editing => (
vec![
"Press ".into(),
"Esc".bold(),
" to stop editing, ".into(),
"Enter".bold(),
" to record the message".into(),
],
Style::default(),
),
};
let text = Text::from(Line::from(msg)).patch_style(style);
let help_message = Paragraph::new(text);
f.render_widget(help_message, help_area);
let input = Paragraph::new(app.input.as_str())
.style(match app.input_mode {
InputMode::Normal => Style::default(),
InputMode::Editing => Style::default().fg(Color::Yellow),
})
.block(Block::bordered().title("Input"));
f.render_widget(input, input_area);
match app.input_mode {
// Hide the cursor. `Frame` does this by default, so we don't need to do anything here
InputMode::Normal => {}
// Make the cursor visible and ask ratatui to put it at the specified coordinates after
// rendering
#[allow(clippy::cast_possible_truncation)]
InputMode::Editing => f.set_cursor_position(Position::new(
// Draw the cursor at the current position in the input field.
// This position is can be controlled via the left and right arrow key
input_area.x + app.character_index as u16 + 1,
// Move one line down, from the border to the input line
input_area.y + 1,
)),
}
let messages: Vec<ListItem> = app
.messages
.iter()
.enumerate()
.map(|(i, m)| {
let content = Line::from(Span::raw(format!("{i}: {m}")));
ListItem::new(content)
})
.collect();
let messages = List::new(messages).block(Block::bordered().title("Messages"));
f.render_widget(messages, messages_area);
}

69
ratatui-core/Cargo.toml Normal file
View File

@@ -0,0 +1,69 @@
[package]
name = "ratatui-core"
description = """
Core types and traits for the Ratatui Terminal UI library.
Widget libraries should use this crate. Applications should use the main Ratatui crate.
"""
version = "0.1.0-alpha.2"
readme = "README.md"
authors.workspace = true
documentation.workspace = true
repository.workspace = true
homepage.workspace = true
keywords.workspace = true
categories.workspace = true
license.workspace = true
exclude.workspace = true
edition.workspace = true
rust-version.workspace = true
[package.metadata.docs.rs]
all-features = true
rustdoc-args = ["--cfg", "docsrs"]
[features]
default = []
## enables conversions to / from colors, modifiers, and styles in the ['anstyle'] crate
anstyle = ["dep:anstyle"]
## enables conversions from colors in the [`palette`] crate to [`Color`](crate::style::Color).
palette = ["dep:palette"]
## enables the backend code that sets the underline color. Underline color is only supported by
## the Crossterm backend, and is not supported on Windows 7.
underline-color = []
## Use terminal scrolling regions to make some operations less prone to
## flickering. (i.e. Terminal::insert_before).
scrolling-regions = []
## enables serialization and deserialization of style and color types using the [`serde`] crate.
## This is useful if you want to save themes to a file.
serde = ["dep:serde", "bitflags/serde", "compact_str/serde"]
[dependencies]
anstyle = { version = "1", optional = true }
bitflags = "2.3"
cassowary = "0.3"
compact_str = "0.8.0"
document-features = { workspace = true, optional = true }
indoc.workspace = true
itertools.workspace = true
lru = "0.12.0"
palette = { version = "0.7.6", optional = true }
paste = "1.0.2"
serde = { workspace = true, optional = true }
strum.workspace = true
thiserror = "2"
unicode-segmentation.workspace = true
unicode-truncate = "2"
unicode-width.workspace = true
[dev-dependencies]
pretty_assertions.workspace = true
rstest.workspace = true
serde_json.workspace = true
[lints]
workspace = true

41
ratatui-core/README.md Normal file
View File

@@ -0,0 +1,41 @@
# Ratatui Core
[![Crates.io](https://img.shields.io/crates/v/ratatui-core)](https://crates.io/crates/ratatui-core)
[![Documentation](https://docs.rs/ratatui-core/badge.svg)](https://docs.rs/ratatui-core)
[![License](https://img.shields.io/crates/l/ratatui-core)](../LICENSE)
<!-- ⚠️ DO NOT EDIT THIS FILE DIRECTLY, EDIT lib.rs AND THEN RUN `cargo rdme` to update this file. -->
<!-- cargo-rdme start -->
**ratatui-core** is the core library of the [ratatui] project,
providing the essential building blocks for creating rich terminal user interfaces in Rust.
[ratatui]: https://github.com/ratatui/ratatui
### Why `ratatui-core`?
The `ratatui-core` crate is split from the main [`ratatui`](https://crates.io/crates/ratatui) crate
to offer better stability for widget library authors. Widget libraries should generally depend
on `ratatui-core`, benefiting from a stable API and reducing the need for frequent updates.
Applications, on the other hand, should depend on the main `ratatui` crate, which includes
built-in widgets and additional features.
## Installation
Add `ratatui-core` to your `Cargo.toml`:
```shell
cargo add ratatui-core
```
## Contributing
We welcome contributions from the community! Please see our [CONTRIBUTING](../CONTRIBUTING.md)
guide for more details on how to get involved.
### License
This project is licensed under the MIT License. See the [LICENSE](../LICENSE) file for details.
<!-- cargo-rdme end -->

View File

@@ -24,10 +24,10 @@
//!
//! # Example
//!
//! ```rust,no_run
//! ```rust,ignore
//! use std::io::stdout;
//!
//! use ratatui::prelude::*;
//! use ratatui::{backend::CrosstermBackend, Terminal};
//!
//! let backend = CrosstermBackend::new(stdout());
//! let mut terminal = Terminal::new(backend)?;
@@ -56,7 +56,7 @@
//! Each backend handles raw mode differently, so the behavior may vary depending on the backend
//! being used. Be sure to consult the backend's specific documentation for exact details on how it
//! implements raw mode.
//!
//! # Alternate Screen
//!
//! The alternate screen is a separate buffer that some terminals provide, distinct from the main
@@ -90,16 +90,16 @@
//! backend being used, and developers should consult the specific backend's documentation to
//! understand how it implements mouse capture.
//!
//! [`TermionBackend`]: termion/struct.TermionBackend.html
//! [`Terminal`]: crate::terminal::Terminal
//! [`TermionBackend`]: termion/struct.TermionBackend.html
//! [`CrosstermBackend`]: https://docs.rs/ratatui/latest/ratatui/backend/struct.CrosstermBackend.html
//! [`TermionBackend`]: https://docs.rs/ratatui/latest/ratatui/backend/struct.TermionBackend.html
//! [`TermwizBackend`]: https://docs.rs/ratatui/latest/ratatui/backend/struct.TermwizBackend.html
//! [`Terminal`]: https://docs.rs/ratatui/latest/ratatui/struct.Terminal.html
//! [Crossterm]: https://crates.io/crates/crossterm
//! [Termion]: https://crates.io/crates/termion
//! [Termwiz]: https://crates.io/crates/termwiz
//! [Examples]: https://github.com/ratatui-org/ratatui/tree/main/examples/README.md
//! [Backend Comparison]:
//! https://ratatui.rs/concepts/backends/comparison/
//! [Ratatui Website]: https://ratatui-org.github.io/ratatui-book
//! [Examples]: https://github.com/ratatui/ratatui/tree/main/ratatui/examples/README.md
//! [Backend Comparison]: https://ratatui.rs/concepts/backends/comparison/
//! [Ratatui Website]: https://ratatui.rs
use std::io;
use strum::{Display, EnumString};
@@ -109,21 +109,6 @@ use crate::{
layout::{Position, Size},
};
#[cfg(feature = "termion")]
mod termion;
#[cfg(feature = "termion")]
pub use self::termion::TermionBackend;
#[cfg(feature = "crossterm")]
mod crossterm;
#[cfg(feature = "crossterm")]
pub use self::crossterm::CrosstermBackend;
#[cfg(feature = "termwiz")]
mod termwiz;
#[cfg(feature = "termwiz")]
pub use self::termwiz::TermwizBackend;
mod test;
pub use self::test::TestBackend;
@@ -162,7 +147,7 @@ pub struct WindowSize {
/// Most applications should not need to interact with the `Backend` trait directly as the
/// [`Terminal`] struct provides a higher level interface for interacting with the terminal.
///
/// [`Terminal`]: crate::terminal::Terminal
/// [`Terminal`]: https://docs.rs/ratatui/latest/ratatui/struct.Terminal.html
pub trait Backend {
/// Draw the given content to the terminal screen.
///
@@ -186,9 +171,11 @@ pub trait Backend {
/// See also [`show_cursor`].
/// # Example
///
/// ```rust
/// # use ratatui::backend::{Backend, TestBackend};
/// ```rust,ignore
/// # use ratatui::backend::{TestBackend};
/// # let mut backend = TestBackend::new(80, 25);
/// use ratatui::backend::Backend;
///
/// backend.hide_cursor()?;
/// // do something with hidden cursor
/// backend.show_cursor()?;
@@ -221,10 +208,11 @@ pub trait Backend {
///
/// # Example
///
/// ```rust
/// # use ratatui::backend::{Backend, TestBackend};
/// # use ratatui::layout::Position;
/// ```rust,ignore
/// # use ratatui::backend::{TestBackend};
/// # let mut backend = TestBackend::new(80, 25);
/// use ratatui::{backend::Backend, layout::Position};
///
/// backend.set_cursor_position(Position { x: 10, y: 20 })?;
/// assert_eq!(backend.get_cursor_position()?, Position { x: 10, y: 20 });
/// # std::io::Result::Ok(())
@@ -253,9 +241,11 @@ pub trait Backend {
///
/// # Example
///
/// ```rust,no_run
/// # use ratatui::backend::{Backend, TestBackend};
/// ```rust,ignore
/// # use ratatui::backend::{TestBackend};
/// # let mut backend = TestBackend::new(80, 25);
/// use ratatui::backend::Backend;
///
/// backend.clear()?;
/// # std::io::Result::Ok(())
/// ```
@@ -269,9 +259,11 @@ pub trait Backend {
///
/// # Example
///
/// ```rust,no_run
/// # use ratatui::{prelude::*, backend::{TestBackend, ClearType}};
/// ```rust,ignore
/// # use ratatui::{backend::{TestBackend}};
/// # let mut backend = TestBackend::new(80, 25);
/// use ratatui::backend::{Backend, ClearType};
///
/// backend.clear_region(ClearType::All)?;
/// # std::io::Result::Ok(())
/// ```
@@ -301,9 +293,11 @@ pub trait Backend {
///
/// # Example
///
/// ```rust
/// # use ratatui::{prelude::*, backend::TestBackend};
/// let backend = TestBackend::new(80, 25);
/// ```rust,ignore
/// # use ratatui::{backend::{TestBackend}};
/// # let backend = TestBackend::new(80, 25);
/// use ratatui::{backend::Backend, layout::Size};
///
/// assert_eq!(backend.size()?, Size::new(80, 25));
/// # std::io::Result::Ok(())
/// ```
@@ -318,6 +312,64 @@ pub trait Backend {
/// Flush any buffered content to the terminal screen.
fn flush(&mut self) -> io::Result<()>;
/// Scroll a region of the screen upwards, where a region is specified by a (half-open) range
/// of rows.
///
/// Each row in the region is replaced by the row `line_count` rows below it, except the bottom
/// `line_count` rows, which are replaced by empty rows. If `line_count` is equal to or larger
/// than the number of rows in the region, then all rows are replaced with empty rows.
///
/// If the region includes row 0, then `line_count` rows are copied into the bottom of the
/// scrollback buffer. These rows are first taken from the old contents of the region, starting
/// from the top. If there aren't sufficient rows in the region, then the remainder are empty
/// rows.
///
/// The position of the cursor afterwards is undefined.
///
/// The behavior is designed to match what ANSI terminals do when scrolling regions are
/// established. With ANSI terminals, a scrolling region can be established with the "^[[X;Yr"
/// sequence, where X and Y define the lines of the region. The scrolling region can be reset
/// to be the whole screen with the "^[[r" sequence.
///
/// When a scrolling region is established in an ANSI terminal, various operations' behaviors
/// are changed in such a way that the scrolling region acts like a "virtual screen". In
/// particular, the scrolling sequence "^[[NS", which scrolls lines up by a count of N.
///
/// On an ANSI terminal, this method will probably translate to something like:
/// "^[[X;Yr^[[NS^[[r". That is, set the scrolling region, scroll up, then reset the scrolling
/// region.
///
/// For examples of how this function is expected to work, refer to the tests for
/// [`TestBackend::scroll_region_up`].
#[cfg(feature = "scrolling-regions")]
fn scroll_region_up(&mut self, region: std::ops::Range<u16>, line_count: u16)
-> io::Result<()>;
/// Scroll a region of the screen downwards, where a region is specified by a (half-open) range
/// of rows.
///
/// Each row in the region is replaced by the row `line_count` rows above it, except the top
/// `line_count` rows, which are replaced by empty rows. If `line_count` is equal to or larger
/// than the number of rows in the region, then all rows are replaced with empty rows.
///
/// The position of the cursor afterwards is undefined.
///
/// See the documentation for [`Self::scroll_region_down`] for more information about how this
/// is expected to be implemented for ANSI terminals. All of that applies, except the ANSI
/// sequence to scroll down is "^[[NT".
///
/// This function is asymmetrical with regards to the scrollback buffer. The reason is that
/// this how terminals seem to implement things.
///
/// For examples of how this function is expected to work, refer to the tests for
/// [`TestBackend::scroll_region_down`].
#[cfg(feature = "scrolling-regions")]
fn scroll_region_down(
&mut self,
region: std::ops::Range<u16>,
line_count: u16,
) -> io::Result<()>;
}
#[cfg(test)]

View File

@@ -3,7 +3,7 @@
use std::{
fmt::{self, Write},
io,
io, iter,
};
use unicode_width::UnicodeWidthStr;
@@ -23,8 +23,8 @@ use crate::{
///
/// # Example
///
/// ```rust
/// use ratatui::{backend::TestBackend, prelude::*};
/// ```rust,ignore
/// use ratatui::backend::{Backend, TestBackend};
///
/// let mut backend = TestBackend::new(10, 2);
/// backend.clear()?;
@@ -35,6 +35,7 @@ use crate::{
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct TestBackend {
buffer: Buffer,
scrollback: Buffer,
cursor: bool,
pos: (u16, u16),
}
@@ -73,6 +74,29 @@ impl TestBackend {
pub fn new(width: u16, height: u16) -> Self {
Self {
buffer: Buffer::empty(Rect::new(0, 0, width, height)),
scrollback: Buffer::empty(Rect::new(0, 0, width, 0)),
cursor: false,
pos: (0, 0),
}
}
/// Creates a new `TestBackend` with the specified lines as the initial screen state.
///
/// The backend's screen size is determined from the initial lines.
#[must_use]
pub fn with_lines<'line, Lines>(lines: Lines) -> Self
where
Lines: IntoIterator,
Lines::Item: Into<crate::text::Line<'line>>,
{
let buffer = Buffer::with_lines(lines);
let scrollback = Buffer::empty(Rect {
width: buffer.area.width,
..Rect::ZERO
});
Self {
buffer,
scrollback,
cursor: false,
pos: (0, 0),
}
@@ -83,9 +107,29 @@ impl TestBackend {
&self.buffer
}
/// Returns a reference to the internal scrollback buffer of the `TestBackend`.
///
/// The scrollback buffer represents the part of the screen that is currently hidden from view,
/// but that could be accessed by scrolling back in the terminal's history. This would normally
/// be done using the terminal's scrollbar or an equivalent keyboard shortcut.
///
/// The scrollback buffer starts out empty. Lines are appended when they scroll off the top of
/// the main buffer. This happens when lines are appended to the bottom of the main buffer
/// using [`Backend::append_lines`].
///
/// The scrollback buffer has a maximum height of [`u16::MAX`]. If lines are appended to the
/// bottom of the scrollback buffer when it is at its maximum height, a corresponding number of
/// lines will be removed from the top.
pub const fn scrollback(&self) -> &Buffer {
&self.scrollback
}
/// Resizes the `TestBackend` to the specified width and height.
pub fn resize(&mut self, width: u16, height: u16) {
self.buffer.resize(Rect::new(0, 0, width, height));
let scrollback_height = self.scrollback.area.height;
self.scrollback
.resize(Rect::new(0, 0, width, scrollback_height));
}
/// Asserts that the `TestBackend`'s buffer is equal to the expected buffer.
@@ -93,6 +137,7 @@ impl TestBackend {
/// This is a shortcut for `assert_eq!(self.buffer(), &expected)`.
///
/// # Panics
///
/// When they are not equal, a panic occurs with a detailed error message showing the
/// differences between the expected and actual buffers.
#[allow(deprecated)]
@@ -102,11 +147,42 @@ impl TestBackend {
crate::assert_buffer_eq!(&self.buffer, expected);
}
/// Asserts that the `TestBackend`'s scrollback buffer is equal to the expected buffer.
///
/// This is a shortcut for `assert_eq!(self.scrollback(), &expected)`.
///
/// # Panics
///
/// When they are not equal, a panic occurs with a detailed error message showing the
/// differences between the expected and actual buffers.
#[track_caller]
pub fn assert_scrollback(&self, expected: &Buffer) {
assert_eq!(&self.scrollback, expected);
}
/// Asserts that the `TestBackend`'s scrollback buffer is empty.
///
/// # Panics
///
/// When the scrollback buffer is not equal, a panic occurs with a detailed error message
/// showing the differences between the expected and actual buffers.
pub fn assert_scrollback_empty(&self) {
let expected = Buffer {
area: Rect {
width: self.scrollback.area.width,
..Rect::ZERO
},
content: vec![],
};
self.assert_scrollback(&expected);
}
/// Asserts that the `TestBackend`'s buffer is equal to the expected lines.
///
/// This is a shortcut for `assert_eq!(self.buffer(), &Buffer::with_lines(expected))`.
///
/// # Panics
///
/// When they are not equal, a panic occurs with a detailed error message showing the
/// differences between the expected and actual buffers.
#[track_caller]
@@ -118,11 +194,29 @@ impl TestBackend {
self.assert_buffer(&Buffer::with_lines(expected));
}
/// Asserts that the `TestBackend`'s scrollback buffer is equal to the expected lines.
///
/// This is a shortcut for `assert_eq!(self.scrollback(), &Buffer::with_lines(expected))`.
///
/// # Panics
///
/// When they are not equal, a panic occurs with a detailed error message showing the
/// differences between the expected and actual buffers.
#[track_caller]
pub fn assert_scrollback_lines<'line, Lines>(&self, expected: Lines)
where
Lines: IntoIterator,
Lines::Item: Into<crate::text::Line<'line>>,
{
self.assert_scrollback(&Buffer::with_lines(expected));
}
/// Asserts that the `TestBackend`'s cursor position is equal to the expected one.
///
/// This is a shortcut for `assert_eq!(self.get_cursor_position().unwrap(), expected)`.
///
/// # Panics
///
/// When they are not equal, a panic occurs with a detailed error message showing the
/// differences between the expected and actual position.
#[track_caller]
@@ -175,7 +269,7 @@ impl Backend for TestBackend {
Ok(())
}
fn clear_region(&mut self, clear_type: super::ClearType) -> io::Result<()> {
fn clear_region(&mut self, clear_type: ClearType) -> io::Result<()> {
let region = match clear_type {
ClearType::All => return self.clear(),
ClearType::AfterCursor => {
@@ -215,7 +309,7 @@ impl Backend for TestBackend {
/// the cursor y position then that number of empty lines (at most the buffer's height in this
/// case but this limit is instead replaced with scrolling in most backend implementations) will
/// be added after the current position and the cursor will be moved to the last row.
fn append_lines(&mut self, n: u16) -> io::Result<()> {
fn append_lines(&mut self, line_count: u16) -> io::Result<()> {
let Position { x: cur_x, y: cur_y } = self.get_cursor_position()?;
let Rect { width, height, .. } = self.buffer.area;
@@ -224,19 +318,29 @@ impl Backend for TestBackend {
let max_y = height.saturating_sub(1);
let lines_after_cursor = max_y.saturating_sub(cur_y);
if n > lines_after_cursor {
let rotate_by = n.saturating_sub(lines_after_cursor).min(max_y);
if rotate_by == height - 1 {
self.clear()?;
}
if line_count > lines_after_cursor {
// We need to insert blank lines at the bottom and scroll the lines from the top into
// scrollback.
let scroll_by: usize = (line_count - lines_after_cursor).into();
let width: usize = self.buffer.area.width.into();
let cells_to_scrollback = self.buffer.content.len().min(width * scroll_by);
self.set_cursor_position(Position { x: 0, y: rotate_by })?;
self.clear_region(ClearType::BeforeCursor)?;
self.buffer.content.rotate_left((width * rotate_by).into());
append_to_scrollback(
&mut self.scrollback,
self.buffer.content.splice(
0..cells_to_scrollback,
iter::repeat_with(Default::default).take(cells_to_scrollback),
),
);
self.buffer.content.rotate_left(cells_to_scrollback);
append_to_scrollback(
&mut self.scrollback,
iter::repeat_with(Default::default).take(width * scroll_by - cells_to_scrollback),
);
}
let new_cursor_y = cur_y.saturating_add(n).min(max_y);
let new_cursor_y = cur_y.saturating_add(line_count).min(max_y);
self.set_cursor_position(Position::new(new_cursor_x, new_cursor_y))?;
Ok(())
@@ -261,10 +365,98 @@ impl Backend for TestBackend {
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
#[cfg(feature = "scrolling-regions")]
fn scroll_region_up(&mut self, region: std::ops::Range<u16>, scroll_by: u16) -> io::Result<()> {
let width: usize = self.buffer.area.width.into();
let cell_region_start = width * region.start.min(self.buffer.area.height) as usize;
let cell_region_end = width * region.end.min(self.buffer.area.height) as usize;
let cell_region_len = cell_region_end - cell_region_start;
let cells_to_scroll_by = width * scroll_by as usize;
// Deal with the simple case where nothing needs to be copied into scrollback.
if cell_region_start > 0 {
if cells_to_scroll_by >= cell_region_len {
// The scroll amount is large enough to clear the whole region.
self.buffer.content[cell_region_start..cell_region_end].fill_with(Default::default);
} else {
// Scroll up by rotating, then filling in the bottom with empty cells.
self.buffer.content[cell_region_start..cell_region_end]
.rotate_left(cells_to_scroll_by);
self.buffer.content[cell_region_end - cells_to_scroll_by..cell_region_end]
.fill_with(Default::default);
}
return Ok(());
}
// The rows inserted into the scrollback will first come from the buffer, and if that is
// insufficient, will then be blank rows.
let cells_from_region = cell_region_len.min(cells_to_scroll_by);
append_to_scrollback(
&mut self.scrollback,
self.buffer.content.splice(
0..cells_from_region,
iter::repeat_with(Default::default).take(cells_from_region),
),
);
if cells_to_scroll_by < cell_region_len {
// Rotate the remaining cells to the front of the region.
self.buffer.content[cell_region_start..cell_region_end].rotate_left(cells_from_region);
} else {
// Splice cleared out the region. Insert empty rows in scrollback.
append_to_scrollback(
&mut self.scrollback,
iter::repeat_with(Default::default).take(cells_to_scroll_by - cell_region_len),
);
}
Ok(())
}
#[cfg(feature = "scrolling-regions")]
fn scroll_region_down(
&mut self,
region: std::ops::Range<u16>,
scroll_by: u16,
) -> io::Result<()> {
let width: usize = self.buffer.area.width.into();
let cell_region_start = width * region.start.min(self.buffer.area.height) as usize;
let cell_region_end = width * region.end.min(self.buffer.area.height) as usize;
let cell_region_len = cell_region_end - cell_region_start;
let cells_to_scroll_by = width * scroll_by as usize;
if cells_to_scroll_by >= cell_region_len {
// The scroll amount is large enough to clear the whole region.
self.buffer.content[cell_region_start..cell_region_end].fill_with(Default::default);
} else {
// Scroll up by rotating, then filling in the top with empty cells.
self.buffer.content[cell_region_start..cell_region_end]
.rotate_right(cells_to_scroll_by);
self.buffer.content[cell_region_start..cell_region_start + cells_to_scroll_by]
.fill_with(Default::default);
}
Ok(())
}
}
/// Append the provided cells to the bottom of a scrollback buffer. The number of cells must be a
/// multiple of the buffer's width. If the scrollback buffer ends up larger than 65535 lines tall,
/// then lines will be removed from the top to get it down to size.
fn append_to_scrollback(scrollback: &mut Buffer, cells: impl IntoIterator<Item = Cell>) {
scrollback.content.extend(cells);
let width = scrollback.area.width as usize;
let new_height = (scrollback.content.len() / width).min(u16::MAX as usize);
let keep_from = scrollback
.content
.len()
.saturating_sub(width * u16::MAX as usize);
scrollback.content.drain(0..keep_from);
scrollback.area.height = new_height as u16;
}
#[cfg(test)]
mod tests {
use itertools::Itertools as _;
use super::*;
#[test]
@@ -273,6 +465,7 @@ mod tests {
TestBackend::new(10, 2),
TestBackend {
buffer: Buffer::with_lines([" "; 2]),
scrollback: Buffer::empty(Rect::new(0, 0, 10, 0)),
cursor: false,
pos: (0, 0),
}
@@ -286,12 +479,12 @@ mod tests {
#[test]
fn buffer_view_with_overwrites() {
let multi_byte_char = "👨‍👩‍👧‍👦"; // renders 8 wide
let multi_byte_char = "👨‍👩‍👧‍👦"; // renders 2 wide
let buffer = Buffer::with_lines([multi_byte_char]);
assert_eq!(
buffer_view(&buffer),
format!(
r#""{multi_byte_char}" Hidden by multi-width symbols: [(1, " "), (2, " "), (3, " "), (4, " "), (5, " "), (6, " "), (7, " ")]
r#""{multi_byte_char}" Hidden by multi-width symbols: [(1, " ")]
"#,
)
);
@@ -323,6 +516,13 @@ mod tests {
backend.assert_buffer_lines(["aaaaaaaaaa"; 2]);
}
#[test]
#[should_panic = "assertion `left == right` failed"]
fn assert_scrollback_panics() {
let backend = TestBackend::new(10, 2);
backend.assert_scrollback_lines(["aaaaaaaaaa"; 2]);
}
#[test]
fn display() {
let backend = TestBackend::new(10, 2);
@@ -385,8 +585,7 @@ mod tests {
#[test]
fn clear_region_all() {
let mut backend = TestBackend::new(10, 5);
backend.buffer = Buffer::with_lines([
let mut backend = TestBackend::with_lines([
"aaaaaaaaaa",
"aaaaaaaaaa",
"aaaaaaaaaa",
@@ -406,8 +605,7 @@ mod tests {
#[test]
fn clear_region_after_cursor() {
let mut backend = TestBackend::new(10, 5);
backend.buffer = Buffer::with_lines([
let mut backend = TestBackend::with_lines([
"aaaaaaaaaa",
"aaaaaaaaaa",
"aaaaaaaaaa",
@@ -430,8 +628,7 @@ mod tests {
#[test]
fn clear_region_before_cursor() {
let mut backend = TestBackend::new(10, 5);
backend.buffer = Buffer::with_lines([
let mut backend = TestBackend::with_lines([
"aaaaaaaaaa",
"aaaaaaaaaa",
"aaaaaaaaaa",
@@ -454,8 +651,7 @@ mod tests {
#[test]
fn clear_region_current_line() {
let mut backend = TestBackend::new(10, 5);
backend.buffer = Buffer::with_lines([
let mut backend = TestBackend::with_lines([
"aaaaaaaaaa",
"aaaaaaaaaa",
"aaaaaaaaaa",
@@ -478,8 +674,7 @@ mod tests {
#[test]
fn clear_region_until_new_line() {
let mut backend = TestBackend::new(10, 5);
backend.buffer = Buffer::with_lines([
let mut backend = TestBackend::with_lines([
"aaaaaaaaaa",
"aaaaaaaaaa",
"aaaaaaaaaa",
@@ -502,8 +697,7 @@ mod tests {
#[test]
fn append_lines_not_at_last_line() {
let mut backend = TestBackend::new(10, 5);
backend.buffer = Buffer::with_lines([
let mut backend = TestBackend::with_lines([
"aaaaaaaaaa",
"bbbbbbbbbb",
"cccccccccc",
@@ -536,12 +730,12 @@ mod tests {
"dddddddddd",
"eeeeeeeeee",
]);
backend.assert_scrollback_empty();
}
#[test]
fn append_lines_at_last_line() {
let mut backend = TestBackend::new(10, 5);
backend.buffer = Buffer::with_lines([
let mut backend = TestBackend::with_lines([
"aaaaaaaaaa",
"bbbbbbbbbb",
"cccccccccc",
@@ -557,13 +751,14 @@ mod tests {
backend.append_lines(1).unwrap();
backend.buffer = Buffer::with_lines([
backend.assert_buffer_lines([
"bbbbbbbbbb",
"cccccccccc",
"dddddddddd",
"eeeeeeeeee",
" ",
]);
backend.assert_scrollback_lines(["aaaaaaaaaa"]);
// It also moves the cursor to the right, as is common of the behaviour of
// terminals in raw-mode
@@ -572,8 +767,7 @@ mod tests {
#[test]
fn append_multiple_lines_not_at_last_line() {
let mut backend = TestBackend::new(10, 5);
backend.buffer = Buffer::with_lines([
let mut backend = TestBackend::with_lines([
"aaaaaaaaaa",
"bbbbbbbbbb",
"cccccccccc",
@@ -597,12 +791,12 @@ mod tests {
"dddddddddd",
"eeeeeeeeee",
]);
backend.assert_scrollback_empty();
}
#[test]
fn append_multiple_lines_past_last_line() {
let mut backend = TestBackend::new(10, 5);
backend.buffer = Buffer::with_lines([
let mut backend = TestBackend::with_lines([
"aaaaaaaaaa",
"bbbbbbbbbb",
"cccccccccc",
@@ -624,12 +818,12 @@ mod tests {
" ",
" ",
]);
backend.assert_scrollback_lines(["aaaaaaaaaa", "bbbbbbbbbb"]);
}
#[test]
fn append_multiple_lines_where_cursor_at_end_appends_height_lines() {
let mut backend = TestBackend::new(10, 5);
backend.buffer = Buffer::with_lines([
let mut backend = TestBackend::with_lines([
"aaaaaaaaaa",
"bbbbbbbbbb",
"cccccccccc",
@@ -651,12 +845,18 @@ mod tests {
" ",
" ",
]);
backend.assert_scrollback_lines([
"aaaaaaaaaa",
"bbbbbbbbbb",
"cccccccccc",
"dddddddddd",
"eeeeeeeeee",
]);
}
#[test]
fn append_multiple_lines_where_cursor_appends_height_lines() {
let mut backend = TestBackend::new(10, 5);
backend.buffer = Buffer::with_lines([
let mut backend = TestBackend::with_lines([
"aaaaaaaaaa",
"bbbbbbbbbb",
"cccccccccc",
@@ -676,6 +876,114 @@ mod tests {
"eeeeeeeeee",
" ",
]);
backend.assert_scrollback_lines(["aaaaaaaaaa"]);
}
#[test]
fn append_multiple_lines_where_cursor_at_end_appends_more_than_height_lines() {
let mut backend = TestBackend::with_lines([
"aaaaaaaaaa",
"bbbbbbbbbb",
"cccccccccc",
"dddddddddd",
"eeeeeeeeee",
]);
backend
.set_cursor_position(Position { x: 0, y: 4 })
.unwrap();
backend.append_lines(8).unwrap();
backend.assert_cursor_position(Position { x: 1, y: 4 });
backend.assert_buffer_lines([
" ",
" ",
" ",
" ",
" ",
]);
backend.assert_scrollback_lines([
"aaaaaaaaaa",
"bbbbbbbbbb",
"cccccccccc",
"dddddddddd",
"eeeeeeeeee",
" ",
" ",
" ",
]);
}
#[test]
fn append_lines_truncates_beyond_u16_max() -> io::Result<()> {
let mut backend = TestBackend::new(10, 5);
// Fill the scrollback with 65535 + 10 lines.
let row_count = u16::MAX as usize + 10;
for row in 0..=row_count {
if row > 4 {
backend.set_cursor_position(Position { x: 0, y: 4 })?;
backend.append_lines(1)?;
}
let cells = format!("{row:>10}").chars().map(Cell::from).collect_vec();
let content = cells
.iter()
.enumerate()
.map(|(column, cell)| (column as u16, 4.min(row) as u16, cell));
backend.draw(content)?;
}
// check that the buffer contains the last 5 lines appended
backend.assert_buffer_lines([
" 65541",
" 65542",
" 65543",
" 65544",
" 65545",
]);
// TODO: ideally this should be something like:
// let lines = (6..=65545).map(|row| format!("{row:>10}"));
// backend.assert_scrollback_lines(lines);
// but there's some truncation happening in Buffer::with_lines that needs to be fixed
assert_eq!(
Buffer {
area: Rect::new(0, 0, 10, 5),
content: backend.scrollback.content[0..10 * 5].to_vec(),
},
Buffer::with_lines([
" 6",
" 7",
" 8",
" 9",
" 10",
]),
"first 5 lines of scrollback should have been truncated"
);
assert_eq!(
Buffer {
area: Rect::new(0, 0, 10, 5),
content: backend.scrollback.content[10 * 65530..10 * 65535].to_vec(),
},
Buffer::with_lines([
" 65536",
" 65537",
" 65538",
" 65539",
" 65540",
]),
"last 5 lines of scrollback should have been appended"
);
// These checks come after the content checks as otherwise we won't see the failing content
// when these checks fail.
// Make sure the scrollback is the right size.
assert_eq!(backend.scrollback.area.width, 10);
assert_eq!(backend.scrollback.area.height, 65535);
assert_eq!(backend.scrollback.content.len(), 10 * 65535);
Ok(())
}
#[test]
@@ -689,4 +997,81 @@ mod tests {
let mut backend = TestBackend::new(10, 2);
backend.flush().unwrap();
}
#[cfg(feature = "scrolling-regions")]
mod scrolling_regions {
use rstest::rstest;
use super::*;
const A: &str = "aaaa";
const B: &str = "bbbb";
const C: &str = "cccc";
const D: &str = "dddd";
const E: &str = "eeee";
const S: &str = " ";
#[rstest]
#[case([A, B, C, D, E], 0..5, 0, [], [A, B, C, D, E])]
#[case([A, B, C, D, E], 0..5, 2, [A, B], [C, D, E, S, S])]
#[case([A, B, C, D, E], 0..5, 5, [A, B, C, D, E], [S, S, S, S, S])]
#[case([A, B, C, D, E], 0..5, 7, [A, B, C, D, E, S, S], [S, S, S, S, S])]
#[case([A, B, C, D, E], 0..3, 0, [], [A, B, C, D, E])]
#[case([A, B, C, D, E], 0..3, 2, [A, B], [C, S, S, D, E])]
#[case([A, B, C, D, E], 0..3, 3, [A, B, C], [S, S, S, D, E])]
#[case([A, B, C, D, E], 0..3, 4, [A, B, C, S], [S, S, S, D, E])]
#[case([A, B, C, D, E], 1..4, 0, [], [A, B, C, D, E])]
#[case([A, B, C, D, E], 1..4, 2, [], [A, D, S, S, E])]
#[case([A, B, C, D, E], 1..4, 3, [], [A, S, S, S, E])]
#[case([A, B, C, D, E], 1..4, 4, [], [A, S, S, S, E])]
#[case([A, B, C, D, E], 0..0, 0, [], [A, B, C, D, E])]
#[case([A, B, C, D, E], 0..0, 2, [S, S], [A, B, C, D, E])]
#[case([A, B, C, D, E], 2..2, 0, [], [A, B, C, D, E])]
#[case([A, B, C, D, E], 2..2, 2, [], [A, B, C, D, E])]
fn scroll_region_up<const L: usize, const M: usize, const N: usize>(
#[case] initial_screen: [&'static str; L],
#[case] range: std::ops::Range<u16>,
#[case] scroll_by: u16,
#[case] expected_scrollback: [&'static str; M],
#[case] expected_buffer: [&'static str; N],
) {
let mut backend = TestBackend::with_lines(initial_screen);
backend.scroll_region_up(range, scroll_by).unwrap();
if expected_scrollback.is_empty() {
backend.assert_scrollback_empty();
} else {
backend.assert_scrollback_lines(expected_scrollback);
}
backend.assert_buffer_lines(expected_buffer);
}
#[rstest]
#[case([A, B, C, D, E], 0..5, 0, [A, B, C, D, E])]
#[case([A, B, C, D, E], 0..5, 2, [S, S, A, B, C])]
#[case([A, B, C, D, E], 0..5, 5, [S, S, S, S, S])]
#[case([A, B, C, D, E], 0..5, 7, [S, S, S, S, S])]
#[case([A, B, C, D, E], 0..3, 0, [A, B, C, D, E])]
#[case([A, B, C, D, E], 0..3, 2, [S, S, A, D, E])]
#[case([A, B, C, D, E], 0..3, 3, [S, S, S, D, E])]
#[case([A, B, C, D, E], 0..3, 4, [S, S, S, D, E])]
#[case([A, B, C, D, E], 1..4, 0, [A, B, C, D, E])]
#[case([A, B, C, D, E], 1..4, 2, [A, S, S, B, E])]
#[case([A, B, C, D, E], 1..4, 3, [A, S, S, S, E])]
#[case([A, B, C, D, E], 1..4, 4, [A, S, S, S, E])]
#[case([A, B, C, D, E], 0..0, 0, [A, B, C, D, E])]
#[case([A, B, C, D, E], 0..0, 2, [A, B, C, D, E])]
#[case([A, B, C, D, E], 2..2, 0, [A, B, C, D, E])]
#[case([A, B, C, D, E], 2..2, 2, [A, B, C, D, E])]
fn scroll_region_down<const M: usize, const N: usize>(
#[case] initial_screen: [&'static str; M],
#[case] range: std::ops::Range<u16>,
#[case] scroll_by: u16,
#[case] expected_buffer: [&'static str; N],
) {
let mut backend = TestBackend::with_lines(initial_screen);
backend.scroll_region_down(range, scroll_by).unwrap();
backend.assert_scrollback_empty();
backend.assert_buffer_lines(expected_buffer);
}
}
}

View File

@@ -41,7 +41,11 @@ macro_rules! assert_buffer_eq {
#[allow(deprecated)]
#[cfg(test)]
mod tests {
use crate::prelude::*;
use crate::{
buffer::Buffer,
layout::Rect,
style::{Color, Style},
};
#[test]
fn assert_buffer_eq_does_not_panic_on_equal_buffers() {

View File

@@ -6,7 +6,12 @@ use std::{
use unicode_segmentation::UnicodeSegmentation;
use unicode_width::UnicodeWidthStr;
use crate::{buffer::Cell, layout::Position, prelude::*};
use crate::{
buffer::Cell,
layout::{Position, Rect},
style::Style,
text::{Line, Span},
};
/// A buffer that maps to the desired content of the terminal after the draw call
///
@@ -18,7 +23,7 @@ use crate::{buffer::Cell, layout::Position, prelude::*};
/// # Examples:
///
/// ```
/// use ratatui::{
/// use ratatui_core::{
/// buffer::{Buffer, Cell},
/// layout::{Position, Rect},
/// style::{Color, Style},
@@ -163,7 +168,11 @@ impl Buffer {
/// # Examples
///
/// ```rust
/// # use ratatui::{prelude::*, buffer::Cell, layout::Position};
/// use ratatui_core::{
/// buffer::{Buffer, Cell},
/// layout::{Position, Rect},
/// };
///
/// let mut buffer = Buffer::empty(Rect::new(0, 0, 10, 10));
///
/// assert_eq!(buffer.cell(Position::new(0, 0)), Some(&Cell::default()));
@@ -190,7 +199,11 @@ impl Buffer {
/// # Examples
///
/// ```rust
/// # use ratatui::{prelude::*, buffer::Cell, layout::Position};
/// use ratatui_core::{
/// buffer::{Buffer, Cell},
/// layout::{Position, Rect},
/// style::{Color, Style},
/// };
/// let mut buffer = Buffer::empty(Rect::new(0, 0, 10, 10));
///
/// if let Some(cell) = buffer.cell_mut(Position::new(0, 0)) {
@@ -214,7 +227,8 @@ impl Buffer {
/// # Examples
///
/// ```
/// # use ratatui::prelude::*;
/// use ratatui_core::{buffer::Buffer, layout::Rect};
///
/// let buffer = Buffer::empty(Rect::new(200, 100, 10, 10));
/// // Global coordinates to the top corner of this buffer's area
/// assert_eq!(buffer.index_of(200, 100), 0);
@@ -225,7 +239,8 @@ impl Buffer {
/// Panics when given an coordinate that is outside of this Buffer's area.
///
/// ```should_panic
/// # use ratatui::prelude::*;
/// use ratatui_core::{buffer::Buffer, layout::Rect};
///
/// let buffer = Buffer::empty(Rect::new(200, 100, 10, 10));
/// // Top coordinate is outside of the buffer in global coordinate space, as the Buffer's area
/// // starts at (200, 100).
@@ -246,7 +261,7 @@ impl Buffer {
///
/// Returns `None` if the given coordinates are outside of the Buffer's area.
///
/// Note that this is private because of <https://github.com/ratatui-org/ratatui/issues/1122>
/// Note that this is private because of <https://github.com/ratatui/ratatui/issues/1122>
#[must_use]
const fn index_of_opt(&self, position: Position) -> Option<usize> {
let area = self.area;
@@ -254,9 +269,10 @@ impl Buffer {
return None;
}
// remove offset
let y = position.y - self.area.y;
let x = position.x - self.area.x;
Some((y * self.area.width + x) as usize)
let y = (position.y - self.area.y) as usize;
let x = (position.x - self.area.x) as usize;
let width = self.area.width as usize;
Some(y * width + x)
}
/// Returns the (global) coordinates of a cell given its index
@@ -266,7 +282,8 @@ impl Buffer {
/// # Examples
///
/// ```
/// # use ratatui::prelude::*;
/// use ratatui_core::{buffer::Buffer, layout::Rect};
///
/// let rect = Rect::new(200, 100, 10, 10);
/// let buffer = Buffer::empty(rect);
/// assert_eq!(buffer.pos_of(0), (200, 100));
@@ -278,22 +295,25 @@ impl Buffer {
/// Panics when given an index that is outside the Buffer's content.
///
/// ```should_panic
/// # use ratatui::prelude::*;
/// use ratatui_core::{buffer::Buffer, layout::Rect};
///
/// let rect = Rect::new(0, 0, 10, 10); // 100 cells in total
/// let buffer = Buffer::empty(rect);
/// // Index 100 is the 101th cell, which lies outside of the area of this Buffer.
/// buffer.pos_of(100); // Panics
/// ```
#[must_use]
pub fn pos_of(&self, i: usize) -> (u16, u16) {
pub fn pos_of(&self, index: usize) -> (u16, u16) {
debug_assert!(
i < self.content.len(),
"Trying to get the coords of a cell outside the buffer: i={i} len={}",
index < self.content.len(),
"Trying to get the coords of a cell outside the buffer: i={index} len={}",
self.content.len()
);
let x = index % self.area.width as usize + self.area.x as usize;
let y = index / self.area.width as usize + self.area.y as usize;
(
self.area.x + (i as u16) % self.area.width,
self.area.y + (i as u16) / self.area.width,
u16::try_from(x).expect("x overflow. This should never happen as area.width is u16"),
u16::try_from(y).expect("y overflow. This should never happen as area.height is u16"),
)
}
@@ -325,7 +345,7 @@ impl Buffer {
let max_width = max_width.try_into().unwrap_or(u16::MAX);
let mut remaining_width = self.area.right().saturating_sub(x).min(max_width);
let graphemes = UnicodeSegmentation::graphemes(string.as_ref(), true)
.filter(|symbol| !symbol.contains(|char: char| char.is_control()))
.filter(|symbol| !symbol.contains(char::is_control))
.map(|symbol| (symbol, symbol.width() as u16))
.filter(|(_symbol, width)| *width > 0)
.map_while(|(symbol, width)| {
@@ -377,6 +397,8 @@ impl Buffer {
///
/// `style` accepts any type that is convertible to [`Style`] (e.g. [`Style`], [`Color`], or
/// your own type that implements [`Into<Style>`]).
///
/// [`Color`]: crate::style::Color
pub fn set_style<S: Into<Style>>(&mut self, area: Rect, style: S) {
let style = style.into();
let area = self.area.intersection(area);
@@ -504,7 +526,11 @@ impl<P: Into<Position>> Index<P> for Buffer {
/// # Examples
///
/// ```
/// # use ratatui::{prelude::*, buffer::Cell, layout::Position};
/// use ratatui_core::{
/// buffer::{Buffer, Cell},
/// layout::{Position, Rect},
/// };
///
/// let buf = Buffer::empty(Rect::new(0, 0, 10, 10));
/// let cell = &buf[(0, 0)];
/// let cell = &buf[Position::new(0, 0)];
@@ -530,7 +556,11 @@ impl<P: Into<Position>> IndexMut<P> for Buffer {
/// # Examples
///
/// ```
/// # use ratatui::{prelude::*, buffer::Cell, layout::Position};
/// use ratatui_core::{
/// buffer::{Buffer, Cell},
/// layout::{Position, Rect},
/// };
///
/// let mut buf = Buffer::empty(Rect::new(0, 0, 10, 10));
/// buf[(0, 0)].set_symbol("A");
/// buf[Position::new(0, 0)].set_symbol("B");
@@ -622,6 +652,7 @@ mod tests {
use rstest::{fixture, rstest};
use super::*;
use crate::style::{Color, Modifier, Stylize};
#[test]
fn debug_empty_buffer() {
@@ -1214,11 +1245,12 @@ mod tests {
#[case::shrug("🤷", "🤷xxxxx")]
// Technically this is a (brown) bear, a zero-width joiner and a snowflake
// As it is joined its a single emoji and should therefore have a width of 2.
// It's correctly detected as a single grapheme but it's width is 4 for some reason
#[case::polarbear("🐻‍❄️", "🐻xxx")]
// Prior to unicode-width 0.2, this was incorrectly detected as width 4 for some reason
#[case::polarbear("🐻‍❄️", "🐻xxxxx")]
// Technically this is an eye, a zero-width joiner and a speech bubble
// Both eye and speech bubble include a 'display as emoji' variation selector
#[case::eye_speechbubble("👁️‍🗨️", "👁🗨xxx")]
// Prior to unicode-width 0.2, this was incorrectly detected as width 4 for some reason
#[case::eye_speechbubble("👁️‍🗨️", "👁🗨xxxxx")]
fn renders_emoji(#[case] input: &str, #[case] expected: &str) {
use unicode_width::UnicodeWidthChar;
@@ -1244,4 +1276,24 @@ mod tests {
let expected = Buffer::with_lines([expected]);
assert_eq!(buffer, expected);
}
/// Regression test for <https://github.com/ratatui/ratatui/issues/1441>
///
/// Previously the `pos_of` function would incorrectly cast the index to a u16 value instead of
/// using the index as is. This caused incorrect rendering of any buffer with an length > 65535.
#[test]
fn index_pos_of_u16_max() {
let buffer = Buffer::empty(Rect::new(0, 0, 256, 256 + 1));
assert_eq!(buffer.index_of(255, 255), 65535);
assert_eq!(buffer.pos_of(65535), (255, 255));
assert_eq!(buffer.index_of(0, 256), 65536);
assert_eq!(buffer.pos_of(65536), (0, 256)); // previously (0, 0)
assert_eq!(buffer.index_of(1, 256), 65537);
assert_eq!(buffer.pos_of(65537), (1, 256)); // previously (1, 0)
assert_eq!(buffer.index_of(255, 256), 65791);
assert_eq!(buffer.pos_of(65791), (255, 256)); // previously (255, 0)
}
}

View File

@@ -1,6 +1,6 @@
use compact_str::CompactString;
use crate::prelude::*;
use crate::style::{Color, Modifier, Style};
/// A buffer cell
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
@@ -13,7 +13,7 @@ pub struct Cell {
/// This is a [`CompactString`] which is a wrapper around [`String`] that uses a small inline
/// buffer for short strings.
///
/// See <https://github.com/ratatui-org/ratatui/pull/601> for more information.
/// See <https://github.com/ratatui/ratatui/pull/601> for more information.
symbol: CompactString,
/// The foreground color of the cell.
@@ -157,6 +157,14 @@ impl Default for Cell {
}
}
impl From<char> for Cell {
fn from(ch: char) -> Self {
let mut cell = Self::EMPTY;
cell.set_char(ch);
cell
}
}
#[cfg(test)]
mod tests {
use super::*;

View File

@@ -1,4 +1,5 @@
#![warn(clippy::missing_const_for_fn)]
//! Provides types and traits for working with layout and positioning in the terminal.
mod alignment;
mod constraint;
@@ -14,8 +15,8 @@ pub use alignment::Alignment;
pub use constraint::Constraint;
pub use direction::Direction;
pub use flex::Flex;
pub use layout::Layout;
pub use layout::{Layout, Spacing};
pub use margin::Margin;
pub use position::Position;
pub use rect::*;
pub use rect::{Columns, Offset, Positions, Rect, Rows};
pub use size::Size;

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