diff --git a/CHANGELOG.md b/CHANGELOG.md index 4d19e6d..32777d5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,42 @@ All notable changes to this project will be documented in this file. +## [0.11.6](https://github.com/alexpasmantier/television/releases/tag/0.11.6) - 2025-04-09 + +### ⛰️ Features + +- [5bf3d20](https://github.com/alexpasmantier/television/commit/5bf3d20c83d5ea0d5e4c8e146d40f0cc37423611) *(cli)* Add a `--no-help` flag to allow disabling showing the help panel by @alexpasmantier in [#456](https://github.com/alexpasmantier/television/pull/456) + +- [b818737](https://github.com/alexpasmantier/television/commit/b81873738a89d1aaa7ac04d67fedf495bc25f062) *(cli)* Add a `--no-remote` flag to lock the application on the cli-invoked channel by @alexpasmantier in [#455](https://github.com/alexpasmantier/television/pull/455) + +- [4892dc3](https://github.com/alexpasmantier/television/commit/4892dc3c3c5a5b970b21fb431e6411f6f63e26ab) *(cli)* Add `--select-1` cli flag to automatically select unique result by @alexpasmantier in [#448](https://github.com/alexpasmantier/television/pull/448) + +### 🐛 Bug Fixes + +- [4a584b4](https://github.com/alexpasmantier/television/commit/4a584b437c413f26c376154fe0751791b9cbd971) *(pwsh)* Use adequate quoting when formatting preview commands for pwsh by @alexpasmantier in [#454](https://github.com/alexpasmantier/television/pull/454) + +- [d4913d7](https://github.com/alexpasmantier/television/commit/d4913d73f61d13bdfba67c246845c0849a3caf0c) *(uncategorized)* Silence the `string match` in tv_smart_autocomplete by @dkmar in [#449](https://github.com/alexpasmantier/television/pull/449) + +### 🚜 Refactor + +- [69c4dcc](https://github.com/alexpasmantier/television/commit/69c4dcc5c57a43bab29f93ce4e14c1cae42d3528) *(debug)* Improve configuration debug logging by @alexpasmantier in [#447](https://github.com/alexpasmantier/television/pull/447) + +- [82e3f89](https://github.com/alexpasmantier/television/commit/82e3f890c83eb435c6d0d118a7e72ac30dbb3059) *(passthrough)* Drop support for unused passthrough keybindings by @alexpasmantier in [#446](https://github.com/alexpasmantier/television/pull/446) [**breaking**] + +### ⚙️ Miscellaneous Tasks + +- [8b267bb](https://github.com/alexpasmantier/television/commit/8b267bb1ff3a005ef6bc20c6ebca952ea99ae0ca) *(changelog)* Update changelog (auto) by @github-actions[bot] in [#443](https://github.com/alexpasmantier/television/pull/443) + +- [a008d3f](https://github.com/alexpasmantier/television/commit/a008d3f4b45f85718087a0b203f132416f3a7dc7) *(uncategorized)* Bump to 0.11.6 by @alexpasmantier in [#457](https://github.com/alexpasmantier/television/pull/457) + + + +### New Contributors +* @dkmar made their first contribution in [#449](https://github.com/alexpasmantier/television/pull/449) + +**Full Changelog**: https://github.com/alexpasmantier/television/compare/0.11.5...0.11.6 + + ## [0.11.5](https://github.com/alexpasmantier/television/releases/tag/0.11.5) - 2025-03-31 ### 🐛 Bug Fixes diff --git a/Cargo.lock b/Cargo.lock index a5347b7..79b7acc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -282,9 +282,9 @@ checksum = "6099cdc01846bc367c4e7dd630dc5966dccf36b652fae7a74e17b640411a91b2" [[package]] name = "bstr" -version = "1.11.3" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "531a9155a481e2ee699d4f98f43c0ca4ff8ee1bfd55c31e9e98fb29d2b176fe0" +checksum = "234113d19d0d7d613b40e86fb654acf958910802bcceab913a4f9e7cda03b1a4" dependencies = [ "memchr", "serde", @@ -322,9 +322,9 @@ checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" [[package]] name = "bytesize" -version = "1.3.2" +version = "1.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d2c12f985c78475a6b8d629afd0c360260ef34cfef52efccdcfd31972f81c2e" +checksum = "2e93abca9e28e0a1b9877922aacb20576e05d4679ffa78c3d6dc22a26a216659" [[package]] name = "cassowary" @@ -349,9 +349,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.17" +version = "1.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fcb57c740ae1daf453ae85f16e37396f672b039e00d9d866e07ddb24e328e3a" +checksum = "525046617d8376e3db1deffb079e91cef90a89fc3ca5c185bbf8c9ecdd15cd5c" dependencies = [ "jobserver", "libc", @@ -403,9 +403,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.32" +version = "4.5.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6088f3ae8c3608d19260cd7445411865a485688711b78b5be70d78cd96136f83" +checksum = "d8aa86934b44c19c50f87cc2790e19f54f7a67aedb64101c2e1a2e5ecfb73944" dependencies = [ "clap_builder", "clap_derive", @@ -413,9 +413,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.32" +version = "4.5.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22a7ef7f676155edfb82daa97f99441f3ebf4a58d5e32f295a56259f1b6facc8" +checksum = "2414dbb2dd0695280da6ea9261e327479e9d37b0630f6b53ba2a11c60c679fd9" dependencies = [ "anstream", "anstyle", @@ -626,9 +626,9 @@ checksum = "43da5946c66ffcc7745f48db692ffbb10a83bfe0afd96235c5c2a4fb23994929" [[package]] name = "darling" -version = "0.20.10" +version = "0.20.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" +checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" dependencies = [ "darling_core", "darling_macro", @@ -636,9 +636,9 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.20.10" +version = "0.20.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" +checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" dependencies = [ "fnv", "ident_case", @@ -650,9 +650,9 @@ dependencies = [ [[package]] name = "darling_macro" -version = "0.20.10" +version = "0.20.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" +checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" dependencies = [ "darling_core", "quote", @@ -727,9 +727,9 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "errno" -version = "0.3.10" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" +checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e" dependencies = [ "libc", "windows-sys 0.59.0", @@ -794,9 +794,9 @@ dependencies = [ [[package]] name = "flate2" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11faaf5a5236997af9848be0bef4db95824b1d534ebc64d0f0c6cf3e67bd38dc" +checksum = "7ced92e76e966ca2fd84c8f7aa01a4aea65b0eb6648d72f7c8f3e2764a67fece" dependencies = [ "crc32fast", "miniz_oxide", @@ -939,9 +939,9 @@ dependencies = [ [[package]] name = "half" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7db2ff139bba50379da6aa0766b52fdcb62cb5b263009b09ed58ba604e14bbd1" +checksum = "459196ed295495a68f7d7fe1d84f6c4b7ff0e21fe3017b2f283c6fac3ad803c9" dependencies = [ "cfg-if", "crunchy", @@ -1019,9 +1019,9 @@ dependencies = [ [[package]] name = "image" -version = "0.25.5" +version = "0.25.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd6f44aed642f18953a158afeb30206f4d50da59fbc66ecb53c66488de73563b" +checksum = "db35664ce6b9810857a38a906215e75a9c879f0696556a39f59c62829710251a" dependencies = [ "bytemuck", "byteorder-lite", @@ -1058,9 +1058,9 @@ checksum = "d0263a3d970d5c054ed9312c0057b4f3bde9c0b33836d3637361d4a9e6e7a408" [[package]] name = "indexmap" -version = "2.8.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3954d50fe15b02142bf25d3b8bdadb634ec3948f103d04ffe3031bc8fe9d7058" +checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" dependencies = [ "equivalent", "hashbrown", @@ -1149,10 +1149,11 @@ checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "jobserver" -version = "0.1.32" +version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" +checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a" dependencies = [ + "getrandom 0.3.2", "libc", ] @@ -1218,9 +1219,9 @@ checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" [[package]] name = "linux-raw-sys" -version = "0.9.3" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe7db12097d22ec582439daf8618b8fdd1a7bef6270e9af3b1ebcd30893cf413" +checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" [[package]] name = "lock_api" @@ -1234,9 +1235,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.26" +version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30bde2b3dc3671ae49d8e2e9f044c7c005836e7a023ee57cffa25ab82764bb9e" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" [[package]] name = "loop9" @@ -1289,9 +1290,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.8.5" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e3e04debbb59698c15bacbb6d93584a8c0ca9cc3213cb423d31f760d8843ce5" +checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a" dependencies = [ "adler2", "simd-adler32", @@ -1438,9 +1439,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.21.1" +version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d75b0bedcc4fe52caa0e03d9f1151a323e4aa5e2d78ba3580400cd3c9e2bc4bc" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" [[package]] name = "onig" @@ -1551,9 +1552,9 @@ checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" [[package]] name = "plist" -version = "1.7.0" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42cf17e9a1800f5f396bc67d193dc9411b59012a5876445ef450d449881e1016" +checksum = "eac26e981c03a6e53e0aee43c113e3202f5581d5360dae7bd2c70e800dd0451d" dependencies = [ "base64", "indexmap", @@ -1809,9 +1810,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.10" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b8c0c260b63a8219631167be35e6a988e9554dbd323f8bd08439c8ed1302bd1" +checksum = "d2f103c6d277498fbceb16e84d317e2a400f160f46904d5f5410848c829511a3" dependencies = [ "bitflags 2.9.0", ] @@ -1913,14 +1914,14 @@ dependencies = [ [[package]] name = "rustix" -version = "1.0.3" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e56a18552996ac8d29ecc3b190b4fdbb2d91ca4ec396de7bbffaf43f3d637e96" +checksum = "d97817398dd4bb2e6da002002db259209759911da105da92bec29ccb12cf58bf" dependencies = [ "bitflags 2.9.0", "errno", "libc", - "linux-raw-sys 0.9.3", + "linux-raw-sys 0.9.4", "windows-sys 0.59.0", ] @@ -2102,15 +2103,15 @@ checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" [[package]] name = "smallvec" -version = "1.14.0" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd" +checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9" [[package]] name = "socket2" -version = "0.5.8" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8" +checksum = "4f5fd57c80058a56cf5c777ab8a126398ece8e442983605d280a44ce79d0edef" dependencies = [ "libc", "windows-sys 0.52.0", @@ -2209,7 +2210,7 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" [[package]] name = "television" -version = "0.11.5" +version = "0.11.6" dependencies = [ "anyhow", "base64", @@ -2267,7 +2268,7 @@ dependencies = [ "fastrand", "getrandom 0.3.2", "once_cell", - "rustix 1.0.3", + "rustix 1.0.5", "windows-sys 0.59.0", ] @@ -2360,9 +2361,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.40" +version = "0.3.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d9c75b47bdff86fa3334a3db91356b8d7d86a9b839dab7d0bdc5c3d3a077618" +checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" dependencies = [ "deranged", "itoa", @@ -2381,9 +2382,9 @@ checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" [[package]] name = "time-macros" -version = "0.2.21" +version = "0.2.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29aa485584182073ed57fd5004aa09c371f021325014694e432313345865fd04" +checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49" dependencies = [ "num-conv", "time-core", @@ -2870,9 +2871,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.7.4" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e97b544156e9bebe1a0ffbc03484fc1ffe3100cbce3ffb17eac35f7cdd7ab36" +checksum = "63d3fcd9bba44b03821e7d699eeee959f3126dcc4aa8e4ae18ec617c2a5cea10" dependencies = [ "memchr", ] diff --git a/Cargo.toml b/Cargo.toml index 642c239..a30e70f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "television" -version = "0.11.5" +version = "0.11.6" edition = "2021" description = "A cross-platform, fast and extensible general purpose fuzzy finder TUI." license = "MIT" diff --git a/benches/main/draw.rs b/benches/main/draw.rs index e3191c0..3b88bf3 100644 --- a/benches/main/draw.rs +++ b/benches/main/draw.rs @@ -30,7 +30,8 @@ fn draw(c: &mut Criterion) { ])); channel.find("television"); // Wait for the channel to finish loading - let mut tv = Television::new(tx, channel, config, None); + let mut tv = + Television::new(tx, channel, config, None, false, false); for _ in 0..5 { // tick the matcher let _ = tv.channel.results(10, 0); diff --git a/man/tv.1 b/man/tv.1 index 92a8356..12aa386 100644 --- a/man/tv.1 +++ b/man/tv.1 @@ -1,10 +1,10 @@ .ie \n(.g .ds Aq \(aq .el .ds Aq ' -.TH television 1 "television 0.11.5" +.TH television 1 "television 0.11.6" .SH NAME television \- A cross\-platform, fast and extensible general purpose fuzzy finder TUI. .SH SYNOPSIS -\fBtelevision\fR [\fB\-p\fR|\fB\-\-preview\fR] [\fB\-\-no\-preview\fR] [\fB\-\-delimiter\fR] [\fB\-t\fR|\fB\-\-tick\-rate\fR] [\fB\-f\fR|\fB\-\-frame\-rate\fR] [\fB\-k\fR|\fB\-\-keybindings\fR] [\fB\-i\fR|\fB\-\-input\fR] [\fB\-\-autocomplete\-prompt\fR] [\fB\-\-select\-1\fR] [\fB\-h\fR|\fB\-\-help\fR] [\fB\-V\fR|\fB\-\-version\fR] [\fICHANNEL\fR] [\fIPATH\fR] [\fIsubcommands\fR] +\fBtelevision\fR [\fB\-p\fR|\fB\-\-preview\fR] [\fB\-\-no\-preview\fR] [\fB\-\-delimiter\fR] [\fB\-t\fR|\fB\-\-tick\-rate\fR] [\fB\-f\fR|\fB\-\-frame\-rate\fR] [\fB\-k\fR|\fB\-\-keybindings\fR] [\fB\-i\fR|\fB\-\-input\fR] [\fB\-\-autocomplete\-prompt\fR] [\fB\-\-select\-1\fR] [\fB\-\-no\-remote\fR] [\fB\-\-no\-help\fR] [\fB\-h\fR|\fB\-\-help\fR] [\fB\-V\fR|\fB\-\-version\fR] [\fICHANNEL\fR] [\fIPATH\fR] [\fIsubcommands\fR] .SH DESCRIPTION A cross\-platform, fast and extensible general purpose fuzzy finder TUI. .SH OPTIONS @@ -77,6 +77,24 @@ to finish loading first. For most channels and workloads this shouldn\*(Aqt be a problem since the loading times are usually very short and will go unnoticed by the user. .TP +\fB\-\-no\-remote\fR +Disable the remote control. + +This will disable the remote control panel and associated actions +entirely. This is useful when the remote control is not needed or +when the user wants `tv` to run in single\-channel mode (e.g. when +using it as a file picker for a script or embedding it in a larger +application). +.TP +\fB\-\-no\-help\fR +Disable the help panel. + +This will disable the help panel and associated toggling actions +entirely. This is useful when the help panel is not needed or +when the user wants `tv` to run with a minimal interface (e.g. when +using it as a file picker for a script or embedding it in a larger +application). +.TP \fB\-h\fR, \fB\-\-help\fR Print help (see a summary with \*(Aq\-h\*(Aq) .TP @@ -108,6 +126,6 @@ Initializes shell completion ("tv init zsh") television\-help(1) Print this message or the help of the given subcommand(s) .SH VERSION -v0.11.5 +v0.11.6 .SH AUTHORS Alexandre Pasmantier diff --git a/television/app.rs b/television/app.rs index f1f8909..524bbb1 100644 --- a/television/app.rs +++ b/television/app.rs @@ -6,7 +6,7 @@ use tracing::{debug, trace}; use crate::channels::entry::{Entry, PreviewType}; use crate::channels::{OnAir, TelevisionChannel}; -use crate::config::Config; +use crate::config::{default_tick_rate, Config}; use crate::keymap::Keymap; use crate::render::UiState; use crate::television::{Mode, Television}; @@ -16,12 +16,47 @@ use crate::{ render::{render, RenderingTask}, }; +pub struct AppOptions { + /// Whether the application should automatically select the first entry if there is only one + /// entry available. + pub select_1: bool, + /// Whether the application should disable the remote control feature. + pub no_remote: bool, + /// Whether the application should disable the help panel feature. + pub no_help: bool, + pub tick_rate: f64, +} + +impl Default for AppOptions { + fn default() -> Self { + Self { + select_1: false, + no_remote: false, + no_help: false, + tick_rate: default_tick_rate(), + } + } +} + +impl AppOptions { + pub fn new( + select_1: bool, + no_remote: bool, + no_help: bool, + tick_rate: f64, + ) -> Self { + Self { + select_1, + no_remote, + no_help, + tick_rate, + } + } +} + /// The main application struct that holds the state of the application. pub struct App { keymap: Keymap, - // maybe move these two into config instead of passing them - // via the cli? - tick_rate: f64, /// The television instance that handles channels and entries. television: Television, /// A flag that indicates whether the application should quit during the next frame. @@ -53,9 +88,7 @@ pub struct App { ui_state_tx: mpsc::UnboundedSender, /// Render task handle render_task: Option>>, - /// Whether the application should automatically select the first entry if there is only one - /// entry available. - select_1: bool, + options: AppOptions, } /// The outcome of an action. @@ -99,23 +132,27 @@ impl App { channel: TelevisionChannel, config: Config, input: Option, - select_1: bool, + options: AppOptions, ) -> Self { let (action_tx, action_rx) = mpsc::unbounded_channel(); let (render_tx, render_rx) = mpsc::unbounded_channel(); let (_, event_rx) = mpsc::unbounded_channel(); let (event_abort_tx, _) = mpsc::unbounded_channel(); - let tick_rate = config.application.tick_rate; let keymap = Keymap::from(&config.keybindings); debug!("{:?}", keymap); let (ui_state_tx, ui_state_rx) = mpsc::unbounded_channel(); - let television = - Television::new(action_tx.clone(), channel, config, input); + let television = Television::new( + action_tx.clone(), + channel, + config, + input, + options.no_remote, + options.no_help, + ); Self { keymap, - tick_rate, television, should_quit: false, should_suspend: false, @@ -128,7 +165,7 @@ impl App { ui_state_rx, ui_state_tx, render_task: None, - select_1, + options, } } @@ -154,7 +191,7 @@ impl App { // Event loop if !headless { debug!("Starting backend event loop"); - let event_loop = EventLoop::new(self.tick_rate, true); + let event_loop = EventLoop::new(self.options.tick_rate, true); self.event_rx = event_loop.rx; self.event_abort_tx = event_loop.abort_tx; } @@ -204,7 +241,7 @@ impl App { // If `self.select_1` is true, the channel is not running, and there is // only one entry available, automatically select the first entry. - if self.select_1 + if self.options.select_1 && !self.television.channel.running() && self.television.channel.total_count() == 1 { @@ -400,7 +437,7 @@ mod test { TelevisionChannel::Stdin(StdinChannel::new(PreviewType::None)), Config::default(), None, - true, + AppOptions::default(), ); app.television .results_picker diff --git a/television/cli/args.rs b/television/cli/args.rs index 5206fa5..25318e2 100644 --- a/television/cli/args.rs +++ b/television/cli/args.rs @@ -1,6 +1,7 @@ use clap::{Parser, Subcommand, ValueEnum}; -#[derive(Parser, Debug)] +#[allow(clippy::struct_excessive_bools)] +#[derive(Parser, Debug, Default)] #[command(author, version, about, long_about = None)] pub struct Cli { /// Which channel shall we watch? @@ -102,6 +103,26 @@ pub struct Cli { #[arg(long, default_value = "false", verbatim_doc_comment)] pub select_1: bool, + /// Disable the remote control. + /// + /// This will disable the remote control panel and associated actions + /// entirely. This is useful when the remote control is not needed or + /// when the user wants `tv` to run in single-channel mode (e.g. when + /// using it as a file picker for a script or embedding it in a larger + /// application). + #[arg(long, default_value = "false", verbatim_doc_comment)] + pub no_remote: bool, + + /// Disable the help panel. + /// + /// This will disable the help panel and associated toggling actions + /// entirely. This is useful when the help panel is not needed or + /// when the user wants `tv` to run with a minimal interface (e.g. when + /// using it as a file picker for a script or embedding it in a larger + /// application). + #[arg(long, default_value = "false", verbatim_doc_comment)] + pub no_help: bool, + #[command(subcommand)] pub command: Option, } diff --git a/television/cli/mod.rs b/television/cli/mod.rs index 0eb1edd..3b97580 100644 --- a/television/cli/mod.rs +++ b/television/cli/mod.rs @@ -17,6 +17,7 @@ use crate::{ pub mod args; +#[allow(clippy::struct_excessive_bools)] #[derive(Debug, Clone)] pub struct PostProcessedCli { pub channel: ParsedCliChannel, @@ -30,6 +31,8 @@ pub struct PostProcessedCli { pub autocomplete_prompt: Option, pub keybindings: Option, pub select_1: bool, + pub no_remote: bool, + pub no_help: bool, } impl Default for PostProcessedCli { @@ -46,6 +49,8 @@ impl Default for PostProcessedCli { autocomplete_prompt: None, keybindings: None, select_1: false, + no_remote: false, + no_help: false, } } } @@ -111,6 +116,8 @@ impl From for PostProcessedCli { autocomplete_prompt: cli.autocomplete_prompt, keybindings, select_1: cli.select_1, + no_remote: cli.no_remote, + no_help: cli.no_help, } } } @@ -316,16 +323,9 @@ mod tests { let cli = Cli { channel: "files".to_string(), preview: Some("bat -n --color=always {}".to_string()), - no_preview: false, delimiter: ":".to_string(), - tick_rate: Some(50.0), - frame_rate: Some(60.0), - keybindings: None, - input: None, - command: None, working_directory: Some("/home/user".to_string()), - autocomplete_prompt: None, - select_1: false, + ..Default::default() }; let post_processed_cli: PostProcessedCli = cli.into(); @@ -341,8 +341,8 @@ mod tests { delimiter: ":".to_string() }) ); - assert_eq!(post_processed_cli.tick_rate, Some(50.0)); - assert_eq!(post_processed_cli.frame_rate, Some(60.0)); + assert_eq!(post_processed_cli.tick_rate, None); + assert_eq!(post_processed_cli.frame_rate, None); assert_eq!( post_processed_cli.working_directory, Some("/home/user".to_string()) @@ -354,17 +354,8 @@ mod tests { fn test_from_cli_no_args() { let cli = Cli { channel: ".".to_string(), - preview: None, - no_preview: false, delimiter: ":".to_string(), - tick_rate: Some(50.0), - frame_rate: Some(60.0), - keybindings: None, - input: None, - command: None, - working_directory: None, - autocomplete_prompt: None, - select_1: false, + ..Default::default() }; let post_processed_cli: PostProcessedCli = cli.into(); @@ -385,16 +376,8 @@ mod tests { let cli = Cli { channel: "files".to_string(), preview: Some(":files:".to_string()), - no_preview: false, delimiter: ":".to_string(), - tick_rate: Some(50.0), - frame_rate: Some(60.0), - keybindings: None, - input: None, - command: None, - working_directory: None, - autocomplete_prompt: None, - select_1: false, + ..Default::default() }; let post_processed_cli: PostProcessedCli = cli.into(); @@ -410,16 +393,8 @@ mod tests { let cli = Cli { channel: "files".to_string(), preview: Some(":env_var:".to_string()), - no_preview: false, delimiter: ":".to_string(), - tick_rate: Some(50.0), - frame_rate: Some(60.0), - keybindings: None, - input: None, - command: None, - working_directory: None, - autocomplete_prompt: None, - select_1: false, + ..Default::default() }; let post_processed_cli: PostProcessedCli = cli.into(); @@ -435,19 +410,12 @@ mod tests { let cli = Cli { channel: "files".to_string(), preview: Some(":env_var:".to_string()), - no_preview: false, delimiter: ":".to_string(), - tick_rate: Some(50.0), - frame_rate: Some(60.0), keybindings: Some( "quit=\"esc\";select_next_entry=[\"down\",\"ctrl-j\"]" .to_string(), ), - input: None, - command: None, - working_directory: None, - autocomplete_prompt: None, - select_1: false, + ..Default::default() }; let post_processed_cli: PostProcessedCli = cli.into(); diff --git a/television/config/mod.rs b/television/config/mod.rs index fead734..429ab90 100644 --- a/television/config/mod.rs +++ b/television/config/mod.rs @@ -260,7 +260,7 @@ fn default_frame_rate() -> f64 { 60.0 } -fn default_tick_rate() -> f64 { +pub fn default_tick_rate() -> f64 { 50.0 } diff --git a/television/main.rs b/television/main.rs index 1c02fdd..09802ee 100644 --- a/television/main.rs +++ b/television/main.rs @@ -10,7 +10,7 @@ use television::cli::parse_channel; use television::utils::clipboard::CLIPBOARD; use tracing::{debug, error, info}; -use television::app::App; +use television::app::{App, AppOptions}; use television::channels::{ entry::PreviewType, stdin::Channel as StdinChannel, TelevisionChannel, }; @@ -65,7 +65,13 @@ async fn main() -> Result<()> { CLIPBOARD.with(<_>::default); debug!("Creating application..."); - let mut app = App::new(channel, config, args.input, args.select_1); + let options = AppOptions::new( + args.select_1, + args.no_remote, + args.no_help, + config.application.tick_rate, + ); + let mut app = App::new(channel, config, args.input, options); stdout().flush()?; debug!("Running application..."); let output = app.run(stdout().is_terminal(), false).await?; diff --git a/television/television.rs b/television/television.rs index d528ecc..637d483 100644 --- a/television/television.rs +++ b/television/television.rs @@ -35,7 +35,7 @@ pub struct Television { action_tx: UnboundedSender, pub config: Config, pub channel: TelevisionChannel, - pub remote_control: TelevisionChannel, + pub remote_control: Option, pub mode: Mode, pub current_pattern: String, pub results_picker: Picker, @@ -48,6 +48,7 @@ pub struct Television { pub colorscheme: Colorscheme, pub ticks: u64, pub ui_state: UiState, + pub no_help: bool, } impl Television { @@ -55,8 +56,10 @@ impl Television { pub fn new( action_tx: UnboundedSender, mut channel: TelevisionChannel, - config: Config, + mut config: Config, input: Option, + no_remote: bool, + no_help: bool, ) -> Self { let mut results_picker = Picker::new(input.clone()); if config.ui.input_bar_position == InputPosition::Bottom { @@ -87,13 +90,24 @@ impl Television { None, ); + let remote_control = if no_remote { + None + } else { + Some(TelevisionChannel::RemoteControl(RemoteControl::new( + builtin_channels, + Some(cable_channels), + ))) + }; + + if no_help { + config.ui.show_help_bar = false; + } + Self { action_tx, config, channel, - remote_control: TelevisionChannel::RemoteControl( - RemoteControl::new(builtin_channels, Some(cable_channels)), - ), + remote_control, mode: Mode::Channel, current_pattern: EMPTY_STRING.to_string(), results_picker, @@ -106,6 +120,7 @@ impl Television { colorscheme, ticks: 0, ui_state: UiState::default(), + no_help, } } @@ -118,9 +133,9 @@ impl Television { let builtin_channels = load_builtin_channels(Some( &cable_channels.keys().collect::>(), )); - self.remote_control = TelevisionChannel::RemoteControl( + self.remote_control = Some(TelevisionChannel::RemoteControl( RemoteControl::new(builtin_channels, Some(cable_channels)), - ); + )); } pub fn dump_context(&self) -> Ctx { @@ -170,7 +185,7 @@ impl Television { self.channel.find(pattern); } Mode::RemoteControl | Mode::SendToChannel => { - self.remote_control.find(pattern); + self.remote_control.as_mut().unwrap().find(pattern); } } } @@ -188,6 +203,8 @@ impl Television { if let Some(i) = self.rc_picker.selected() { return self .remote_control + .as_ref() + .unwrap() .get_result(i.try_into().unwrap()); } None @@ -217,9 +234,10 @@ impl Television { Mode::Channel => { (self.channel.result_count(), &mut self.results_picker) } - Mode::RemoteControl | Mode::SendToChannel => { - (self.remote_control.total_count(), &mut self.rc_picker) - } + Mode::RemoteControl | Mode::SendToChannel => ( + self.remote_control.as_ref().unwrap().total_count(), + &mut self.rc_picker, + ), }; if result_count == 0 { return; @@ -236,9 +254,10 @@ impl Television { Mode::Channel => { (self.channel.result_count(), &mut self.results_picker) } - Mode::RemoteControl | Mode::SendToChannel => { - (self.remote_control.total_count(), &mut self.rc_picker) - } + Mode::RemoteControl | Mode::SendToChannel => ( + self.remote_control.as_ref().unwrap().total_count(), + &mut self.rc_picker, + ), }; if result_count == 0 { return; @@ -368,18 +387,20 @@ impl Television { pub fn update_rc_picker_state(&mut self) { if self.rc_picker.selected().is_none() - && self.remote_control.result_count() > 0 + && self.remote_control.as_ref().unwrap().result_count() > 0 { self.rc_picker.select(Some(0)); self.rc_picker.relative_select(Some(0)); } - self.rc_picker.entries = self.remote_control.results( - // this'll be more than the actual rc height but it's fine - self.ui_state.layout.results.height.into(), - u32::try_from(self.rc_picker.offset()).unwrap(), - ); - self.rc_picker.total_items = self.remote_control.total_count(); + self.rc_picker.entries = + self.remote_control.as_mut().unwrap().results( + // this'll be more than the actual rc height but it's fine + self.ui_state.layout.results.height.into(), + u32::try_from(self.rc_picker.offset()).unwrap(), + ); + self.rc_picker.total_items = + self.remote_control.as_ref().unwrap().total_count(); } pub fn handle_input_action(&mut self, action: &Action) { @@ -408,6 +429,9 @@ impl Television { } pub fn handle_toggle_rc(&mut self) { + if self.remote_control.is_none() { + return; + } match self.mode { Mode::Channel => { self.mode = Mode::RemoteControl; @@ -417,7 +441,7 @@ impl Television { // this resets the RC picker self.reset_picker_input(); self.init_remote_control(); - self.remote_control.find(EMPTY_STRING); + self.remote_control.as_mut().unwrap().find(EMPTY_STRING); self.reset_picker_selection(); self.mode = Mode::Channel; } @@ -426,16 +450,19 @@ impl Television { } pub fn handle_toggle_send_to_channel(&mut self) { + if self.remote_control.is_none() { + return; + } match self.mode { Mode::Channel | Mode::RemoteControl => { self.mode = Mode::SendToChannel; - self.remote_control = TelevisionChannel::RemoteControl( + self.remote_control = Some(TelevisionChannel::RemoteControl( RemoteControl::with_transitions_from(&self.channel), - ); + )); } Mode::SendToChannel => { self.reset_picker_input(); - self.remote_control.find(EMPTY_STRING); + self.remote_control.as_mut().unwrap().find(EMPTY_STRING); self.reset_picker_selection(); self.mode = Mode::Channel; } @@ -462,12 +489,15 @@ impl Television { } Mode::RemoteControl => { if let Some(entry) = self.get_selected_entry(None) { - let new_channel = - self.remote_control.zap(entry.name.as_str())?; + let new_channel = self + .remote_control + .as_ref() + .unwrap() + .zap(entry.name.as_str())?; // this resets the RC picker self.reset_picker_selection(); self.reset_picker_input(); - self.remote_control.find(EMPTY_STRING); + self.remote_control.as_mut().unwrap().find(EMPTY_STRING); self.mode = Mode::Channel; self.change_channel(new_channel); } @@ -479,7 +509,7 @@ impl Television { .transition_to(entry.name.as_str().try_into()?); self.reset_picker_selection(); self.reset_picker_input(); - self.remote_control.find(EMPTY_STRING); + self.remote_control.as_mut().unwrap().find(EMPTY_STRING); self.mode = Mode::Channel; self.change_channel(new_channel); } @@ -569,6 +599,9 @@ impl Television { self.handle_toggle_send_to_channel(); } Action::ToggleHelp => { + if self.no_help { + return Ok(()); + } self.config.ui.show_help_bar = !self.config.ui.show_help_bar; } Action::TogglePreview => { @@ -589,7 +622,9 @@ impl Television { self.update_results_picker_state(); - self.update_rc_picker_state(); + if self.remote_control.is_some() { + self.update_rc_picker_state(); + } let selected_entry = self .get_selected_entry(Some(Mode::Channel)) diff --git a/tests/app.rs b/tests/app.rs index f087215..64e9e85 100644 --- a/tests/app.rs +++ b/tests/app.rs @@ -1,7 +1,9 @@ use std::{collections::HashSet, path::PathBuf, time::Duration}; use television::{ - action::Action, app::App, channels::TelevisionChannel, + action::Action, + app::{App, AppOptions}, + channels::TelevisionChannel, config::default_config_from_file, }; use tokio::{task::JoinHandle, time::timeout}; @@ -39,7 +41,9 @@ fn setup_app( config.application.tick_rate = 100.0; let input = None; - let mut app = App::new(chan, config, input, select_1); + let options = + AppOptions::new(select_1, false, false, config.application.tick_rate); + let mut app = App::new(chan, config, input, options); // retrieve the app's action channel handle in order to send a quit action let tx = app.action_tx.clone();