diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 43426a8a..ebac8ea9 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -2,7 +2,7 @@ name: Bug report about: Create a report to help us improve title: '' -labels: '' +labels: 'bug' assignees: '' --- diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index bbcbbe7d..36014cde 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -2,7 +2,7 @@ name: Feature request about: Suggest an idea for this project title: '' -labels: '' +labels: 'enhancement' assignees: '' --- diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml index 52cdd8fa..7366e78c 100644 --- a/.github/release-drafter.yml +++ b/.github/release-drafter.yml @@ -1,5 +1,6 @@ name-template: '$RESOLVED_VERSION' tag-template: '$RESOLVED_VERSION' +tag-prefix: "" categories: - title: 'πŸš€ Features' labels: @@ -10,8 +11,10 @@ categories: - 'fix' - 'bugfix' - 'bug' - - title: '🧰 Maintenance' - label: 'chore' + - title: 'πŸ“š Documentation' + label: 'documentation' + - title: 'πŸ”’ Security' + label: 'security' change-template: '- $TITLE @$AUTHOR (#$NUMBER)' template: | ## Changes @@ -20,4 +23,38 @@ template: | ## Contributors - $CONTRIBUTORS \ No newline at end of file + $CONTRIBUTORS + +change-title-escapes: '\<*_&"''' +autolabeler: + - label: 'documentation' + files: + - '*.md' + branch: + - '/docs{0,1}\/.+/' + - label: 'bug' + branch: + - '/fix\/.+/' + title: + - '/fix/i' + - label: 'enhancement' + branch: + - '/feature\/.+/' + body: + - '/[A-Z]+-[0-9]+/' + - label: 'documentation' + files: + - '**/*.md' + - 'docs/**/*' + - label: 'security' + branch: + - '/security\/.+/' +replacers: + - search: /"/g + replace: '' + - search: /'/g + replace: '' + - search: /`/g + replace: '' +exclude-labels: + - 'skip-changelog' \ No newline at end of file diff --git a/.github/workflows/cargo-lock.yml b/.github/workflows/cargo-lock.yml new file mode 100644 index 00000000..40c37209 --- /dev/null +++ b/.github/workflows/cargo-lock.yml @@ -0,0 +1,25 @@ +name: Remove Cargo.lock changes + +on: + pull_request: + types: [opened, synchronize] + paths: + - 'Cargo.lock' + +jobs: + remove-cargo-lock: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: Remove Cargo.lock changes + run: | + git config user.name github-actions + git config user.email github-actions@github.com + git checkout ${{ github.head_ref }} + git reset origin/${{ github.base_ref }} -- Cargo.lock + git commit -m "Remove changes to Cargo.lock" || echo "No changes to commit" + git push origin ${{ github.head_ref }} --force diff --git a/.github/workflows/pre-release.yaml b/.github/workflows/pre-release.yaml index 5fdba7c7..5e4b139e 100644 --- a/.github/workflows/pre-release.yaml +++ b/.github/workflows/pre-release.yaml @@ -33,15 +33,6 @@ jobs: with: config-name: release-drafter.yml version: ${{ env.version }} - version-template: '$YEAR.$MONTH.$DAY' - - - name: Prepare Release Body - id: prepare_body - run: | - new_changes="${{ steps.generate_notes.outputs.body }}" - echo "body<> $GITHUB_OUTPUT - echo "$new_changes" >> $GITHUB_OUTPUT - echo "EOF" >> $GITHUB_OUTPUT - name: Create and Upload Release id: create_release @@ -50,7 +41,7 @@ jobs: tag_name: ${{ env.version }} name: Pre-Release ${{ env.version }} body: | - ${{ steps.prepare_body.outputs.body }} + ${{ steps.generate_notes.outputs.body }} ![GitHub Downloads (specific asset, specific tag)](https://img.shields.io/github/downloads/ChrisTitusTech/linutil/${{ env.version }}/linutil) append_body: false diff --git a/Cargo.lock b/Cargo.lock index a87ca14f..c3798ede 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -21,10 +21,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" [[package]] -name = "anstream" -version = "0.6.14" +name = "android-tzdata" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anstream" +version = "0.6.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" dependencies = [ "anstyle", "anstyle-parse", @@ -43,43 +58,43 @@ checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" [[package]] name = "anstyle-parse" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4" +checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad186efb764318d35165f1758e7dcef3b10628e26d41a44bc5550652e6804391" +checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" dependencies = [ - "windows-sys 0.52.0", + "windows-sys", ] [[package]] name = "anstyle-wincon" -version = "3.0.3" +version = "3.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19" +checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" dependencies = [ "anstyle", - "windows-sys 0.52.0", + "windows-sys", ] [[package]] name = "anyhow" -version = "1.0.86" +version = "1.0.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" +checksum = "4e1496f8fb1fbf272686b8d37f523dab3e4a7443300055e74cdaa449f3114356" [[package]] name = "arrayvec" -version = "0.7.4" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" [[package]] name = "autocfg" @@ -95,9 +110,15 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + +[[package]] +name = "bumpalo" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" [[package]] name = "cassowary" @@ -107,13 +128,22 @@ checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53" [[package]] name = "castaway" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a17ed5635fc8536268e5d4de1e22e81ac34419e5f052d4d51f4e01dcc263fcc" +checksum = "0abae9be0aaf9ea96a3b1b8b1b55c602ca751eba1b1500220cea4ecbafe7c0d5" dependencies = [ "rustversion", ] +[[package]] +name = "cc" +version = "1.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b62ac837cdb5cb22e10a256099b4fc502b1dfe560cb282963a974d7abd80e476" +dependencies = [ + "shlex", +] + [[package]] name = "cfg-if" version = "1.0.0" @@ -121,10 +151,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] -name = "clap" -version = "4.5.16" +name = "chrono" +version = "0.4.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed6719fffa43d0d87e5fd8caeab59be1554fb028cd30edc88fc4369b17971019" +checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "wasm-bindgen", + "windows-targets", +] + +[[package]] +name = "clap" +version = "4.5.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e5a21b8495e732f1b3c364c9949b201ca7bae518c502c80256c96ad79eaf6ac" dependencies = [ "clap_builder", "clap_derive", @@ -132,9 +176,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.15" +version = "4.5.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "216aec2b177652e3846684cbfe25c9964d18ec45234f0f5da5157b207ed1aab6" +checksum = "8cf2dd12af7a047ad9d6da2b6b249759a22a7abc0f474c1dae1777afa4b21a73" dependencies = [ "anstream", "anstyle", @@ -156,40 +200,47 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b82cf0babdbd58558212896d1a4272303a57bdb245c2bf1147185fb45640e70" +checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" [[package]] name = "colorchoice" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422" +checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" [[package]] name = "compact_str" -version = "0.7.1" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f86b9c4c00838774a6d902ef931eff7470720c51d90c2e32cfe15dc304737b3f" +checksum = "6050c3a16ddab2e412160b31f2c871015704239bca62f72f6e5f0be631d3f644" dependencies = [ "castaway", "cfg-if", "itoa", + "rustversion", "ryu", "static_assertions", ] [[package]] -name = "crossterm" -version = "0.27.0" +name = "core-foundation-sys" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "crossterm" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "crossterm_winapi", - "libc", "mio", "parking_lot", + "rustix", "signal-hook", "signal-hook-mio", "winapi", @@ -212,15 +263,15 @@ checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" [[package]] name = "ego-tree" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a68a4904193147e0a8dec3314640e6db742afd5f6e634f428a6af230d9b3591" +checksum = "12a0bb14ac04a9fcf170d0bbbef949b44cc492f4452bd20c095636956f653642" [[package]] name = "either" -version = "1.12.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" [[package]] name = "equivalent" @@ -235,7 +286,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys", ] [[package]] @@ -271,13 +322,42 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +[[package]] +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + [[package]] name = "home" version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" dependencies = [ - "windows-sys 0.52.0", + "windows-sys", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", ] [[package]] @@ -301,14 +381,24 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93ead53efc7ea8ed3cfb0c79fc8023fbb782a5432b52830b6518941cebe6505c" +checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5" dependencies = [ "equivalent", "hashbrown", ] +[[package]] +name = "instability" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b23a0c8dfe501baac4adf6ebbfa6eddf8f0c07f56b058cc1288017e32397846c" +dependencies = [ + "quote", + "syn", +] + [[package]] name = "ioctl-rs" version = "0.1.6" @@ -320,9 +410,9 @@ dependencies = [ [[package]] name = "is_terminal_polyfill" -version = "1.70.0" +version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" [[package]] name = "itertools" @@ -340,16 +430,53 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] -name = "lazy_static" -version = "1.4.0" +name = "js-sys" +version = "0.3.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.155" +version = "0.2.158" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" +checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" + +[[package]] +name = "linutil_core" +version = "0.1.0" +dependencies = [ + "ego-tree", + "include_dir", + "serde", + "tempdir", + "toml", + "which", +] + +[[package]] +name = "linutil_tui" +version = "0.1.0" +dependencies = [ + "chrono", + "clap", + "crossterm", + "ego-tree", + "linutil_core", + "oneshot", + "portable-pty", + "ratatui", + "tui-term", + "unicode-width", +] [[package]] name = "linux-raw-sys" @@ -369,15 +496,15 @@ dependencies = [ [[package]] name = "log" -version = "0.4.21" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] name = "lru" -version = "0.12.3" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3262e75e648fce39813cb56ac41f3c3e3f65217ebf3844d818d1f9398cfb0dc" +checksum = "37ee39891760e7d94734f6f63fedc29a2e4a152f836120753a72503f09fcf904" dependencies = [ "hashbrown", ] @@ -399,14 +526,15 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.11" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" +checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" dependencies = [ + "hermit-abi", "libc", "log", "wasi", - "windows-sys 0.48.0", + "windows-sys", ] [[package]] @@ -423,6 +551,15 @@ dependencies = [ "pin-utils", ] +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + [[package]] name = "once_cell" version = "1.19.0" @@ -437,9 +574,9 @@ checksum = "e296cf87e61c9cfc1a61c3c63a0f7f286ed4554e0e22be84e8a38e1d264a2a29" [[package]] name = "parking_lot" -version = "0.12.2" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e4af0ca4f6caed20e900d564c242b8e5d4903fdacf31d3daf527b66fe6f42fb" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" dependencies = [ "lock_api", "parking_lot_core", @@ -455,7 +592,7 @@ dependencies = [ "libc", "redox_syscall", "smallvec", - "windows-targets 0.52.5", + "windows-targets", ] [[package]] @@ -493,18 +630,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.83" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b33eb56c327dec362a9e55b3ad14f9d2f0904fb5a5b03b513ab5465399e9f43" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.36" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" dependencies = [ "proc-macro2", ] @@ -539,18 +676,18 @@ checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" [[package]] name = "ratatui" -version = "0.27.0" +version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d16546c5b5962abf8ce6e2881e722b4e0ae3b6f1a08a26ae3573c55853ca68d3" +checksum = "fdef7f9be5c0122f890d58bdf4d964349ba6a6161f705907526d891efabba57d" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "cassowary", "compact_str", "crossterm", + "instability", "itertools", "lru", "paste", - "stability", "strum", "strum_macros", "unicode-segmentation", @@ -569,11 +706,11 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.1" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e" +checksum = "0884ad60e090bf1345b93da0a5de8923c93884cd03f40dfcfddd3b4bee661853" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", ] [[package]] @@ -587,15 +724,15 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.34" +version = "0.38.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" +checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "errno", "libc", "linux-raw-sys", - "windows-sys 0.52.0", + "windows-sys", ] [[package]] @@ -618,18 +755,18 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "serde" -version = "1.0.205" +version = "1.0.210" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e33aedb1a7135da52b7c21791455563facbbcc43d0f0f66165b42c21b3dfb150" +checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.205" +version = "1.0.210" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "692d6f5ac90220161d6774db30c662202721e64aed9058d2c394f451261420c1" +checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" dependencies = [ "proc-macro2", "quote", @@ -703,6 +840,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde" +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + [[package]] name = "signal-hook" version = "0.3.17" @@ -715,9 +858,9 @@ dependencies = [ [[package]] name = "signal-hook-mio" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29ad2e15f37ec9a6cc544097b78a1ec90001e9f71b81338ca39f430adaca99af" +checksum = "34db1a06d485c9142248b7a054f034b349b212551f3dfd19c94d45a754a217cd" dependencies = [ "libc", "mio", @@ -739,16 +882,6 @@ version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" -[[package]] -name = "stability" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ff9eaf853dec4c8802325d8b6d3dffa86cc707fd7a1a4cdbf416e13b061787a" -dependencies = [ - "quote", - "syn", -] - [[package]] name = "static_assertions" version = "1.1.0" @@ -763,9 +896,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "strum" -version = "0.26.2" +version = "0.26.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d8cec3501a5194c432b2b7976db6b7d10ec95c253208b45f83f7136aa985e29" +checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" dependencies = [ "strum_macros", ] @@ -785,9 +918,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.65" +version = "2.0.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2863d96a84c6439701d7a38f9de935ec562c8832cc55d1dde0f513b52fad106" +checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" dependencies = [ "proc-macro2", "quote", @@ -815,18 +948,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.61" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" +checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.61" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" +checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" dependencies = [ "proc-macro2", "quote", @@ -867,30 +1000,11 @@ dependencies = [ "winnow", ] -[[package]] -name = "tui" -version = "0.1.0" -dependencies = [ - "clap", - "crossterm", - "ego-tree", - "include_dir", - "oneshot", - "portable-pty", - "ratatui", - "serde", - "tempdir", - "toml", - "tui-term", - "unicode-width", - "which", -] - [[package]] name = "tui-term" -version = "0.1.12" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fac7538a3b9d5e92739253323e169ed25f005669c0e7f9d98f50f697fcada4e8" +checksum = "d07f0233f0d4795d2dc6663cfc3ce56b87bebcee66d6bcc088aa6aff5c072361" dependencies = [ "ratatui", "vt100", @@ -898,9 +1012,9 @@ dependencies = [ [[package]] name = "unicode-ident" -version = "1.0.12" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" [[package]] name = "unicode-segmentation" @@ -927,15 +1041,15 @@ checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" [[package]] name = "utf8parse" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "version_check" -version = "0.9.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "vt100" @@ -962,9 +1076,9 @@ dependencies = [ [[package]] name = "vte_generate_state_changes" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d257817081c7dffcdbab24b9e62d2def62e2ff7d00b1c20062551e6cccc145ff" +checksum = "2e369bee1b05d510a7b4ed645f5faa90619e05437111783ea5848f28d97d3c2e" dependencies = [ "proc-macro2", "quote", @@ -976,6 +1090,61 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasm-bindgen" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5" +dependencies = [ + "cfg-if", + "once_cell", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" + [[package]] name = "which" version = "6.0.3" @@ -1011,12 +1180,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] -name = "windows-sys" -version = "0.48.0" +name = "windows-core" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-targets 0.48.5", + "windows-targets", ] [[package]] @@ -1025,129 +1194,72 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.5", + "windows-targets", ] [[package]] name = "windows-targets" -version = "0.48.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm 0.48.5", - "windows_aarch64_msvc 0.48.5", - "windows_i686_gnu 0.48.5", - "windows_i686_msvc 0.48.5", - "windows_x86_64_gnu 0.48.5", - "windows_x86_64_gnullvm 0.48.5", - "windows_x86_64_msvc 0.48.5", -] - -[[package]] -name = "windows-targets" -version = "0.52.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" -dependencies = [ - "windows_aarch64_gnullvm 0.52.5", - "windows_aarch64_msvc 0.52.5", - "windows_i686_gnu 0.52.5", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", "windows_i686_gnullvm", - "windows_i686_msvc 0.52.5", - "windows_x86_64_gnu 0.52.5", - "windows_x86_64_gnullvm 0.52.5", - "windows_x86_64_msvc 0.52.5", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] [[package]] name = "windows_aarch64_gnullvm" -version = "0.48.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" -version = "0.48.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" -version = "0.48.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" - -[[package]] -name = "windows_i686_gnu" -version = "0.52.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] name = "windows_i686_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" -version = "0.48.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" - -[[package]] -name = "windows_i686_msvc" -version = "0.52.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" -version = "0.48.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.52.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" -version = "0.48.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" -version = "0.48.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" @@ -1175,18 +1287,18 @@ checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904" [[package]] name = "zerocopy" -version = "0.7.34" +version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae87e3fcd617500e5d106f0380cf7b77f3c6092aae37191433159dda23cfb087" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.7.34" +version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index f146e377..551f0bc4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,37 +1,19 @@ -[package] -name = "tui" +[workspace.package] +license = "MIT" version = "0.1.0" -edition = "2021" -[dependencies] -clap = { version = "4.5.16", features = ["derive"] } -crossterm = "0.27.0" +[workspace.dependencies] ego-tree = "0.6.2" -oneshot = "0.1.8" -portable-pty = "0.8.1" -ratatui = "0.27.0" -tui-term = "0.1.12" -include_dir = "0.7.4" -tempdir = "0.3.7" -serde = { version = "1.0.205", features = ["derive"] } -toml = "0.8.19" -which = "6.0.3" -unicode-width = "0.1.13" - -[build-dependencies] -chrono = "0.4.33" - -[[bin]] -name = "linutil" -path = "src/main.rs" - +[workspace] +members = ["tui", "core"] +resolver = "2" [profile.release] -opt-level = 3 +opt-level = "z" debug = false lto = true codegen-units = 1 panic = "abort" strip = true -incremental = false \ No newline at end of file +incremental = false diff --git a/build.rs b/build.rs deleted file mode 100644 index ee7af066..00000000 --- a/build.rs +++ /dev/null @@ -1,12 +0,0 @@ -fn main() { - // Rebuild program if any file in commands directory changes. - println!("cargo:rerun-if-changed=src/commands"); - // Rerun build script if any code is modified - println!("cargo:rerun-if-changed=src"); - - // Add current date as a variable to be displayed in the 'Linux Toolbox' text. - println!( - "cargo:rustc-env=BUILD_DATE={}", - chrono::Local::now().format("%Y-%m-%d") - ); -} diff --git a/build/linutil b/build/linutil index 089268ea..080d83a9 100755 Binary files a/build/linutil and b/build/linutil differ diff --git a/cargo-lock-merge.txt b/cargo-lock-merge.txt new file mode 100644 index 00000000..649a99cf --- /dev/null +++ b/cargo-lock-merge.txt @@ -0,0 +1,5 @@ +What to do when you have a Cargo.lock merge conflict? +1. `git checkout origin/main -- Cargo.lock` to get the original Cargo.lock +2. `cargo build` to update Cargo.lock +3. `git add Cargo.lock` +4. continue the merge as normal \ No newline at end of file diff --git a/core/Cargo.toml b/core/Cargo.toml new file mode 100644 index 00000000..ebe1091d --- /dev/null +++ b/core/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "linutil_core" +edition = "2021" +version.workspace = true +license.workspace = true + +[dependencies] +include_dir = "0.7.4" +tempdir = "0.3.7" +serde = { version = "1.0.205", features = ["derive"] } +toml = "0.8.19" +which = "6.0.3" +ego-tree = { workspace = true } diff --git a/src/tabs.rs b/core/src/inner.rs similarity index 87% rename from src/tabs.rs rename to core/src/inner.rs index 97969326..f23d2104 100644 --- a/src/tabs.rs +++ b/core/src/inner.rs @@ -1,7 +1,42 @@ -use crate::running_command::Command; +use crate::{Command, ListNode, Tab}; use ego_tree::{NodeMut, Tree}; +use include_dir::{include_dir, Dir}; use serde::Deserialize; use std::path::{Path, PathBuf}; +use tempdir::TempDir; + +const TAB_DATA: Dir = include_dir!("$CARGO_MANIFEST_DIR/../tabs"); + +pub fn get_tabs(validate: bool) -> Vec { + let tab_files = TabList::get_tabs(); + let tabs = tab_files.into_iter().map(|path| { + let directory = path.parent().unwrap().to_owned(); + let data = std::fs::read_to_string(path).expect("Failed to read tab data"); + let mut tab_data: TabEntry = toml::from_str(&data).expect("Failed to parse tab data"); + + if validate { + filter_entries(&mut tab_data.data); + } + (tab_data, directory) + }); + + let tabs: Vec = tabs + .map(|(TabEntry { name, data }, directory)| { + let mut tree = Tree::new(ListNode { + name: "root".to_string(), + command: Command::None, + }); + let mut root = tree.root_mut(); + create_directory(data, &mut root, &directory); + Tab { name, tree } + }) + .collect(); + + if tabs.is_empty() { + panic!("No tabs found"); + } + tabs +} #[derive(Deserialize)] struct TabList { @@ -79,49 +114,6 @@ enum SystemDataType { CommandExists, } -#[derive(Hash, Eq, PartialEq)] -pub struct Tab { - pub name: String, - pub tree: Tree, -} - -#[derive(Clone, Hash, Eq, PartialEq)] -pub struct ListNode { - pub name: String, - pub command: Command, -} - -pub fn get_tabs(command_dir: &Path, validate: bool) -> Vec { - let tab_files = TabList::get_tabs(command_dir); - let tabs = tab_files.into_iter().map(|path| { - let directory = path.parent().unwrap().to_owned(); - let data = std::fs::read_to_string(path).expect("Failed to read tab data"); - let mut tab_data: TabEntry = toml::from_str(&data).expect("Failed to parse tab data"); - - if validate { - filter_entries(&mut tab_data.data); - } - (tab_data, directory) - }); - - let tabs: Vec = tabs - .map(|(TabEntry { name, data }, directory)| { - let mut tree = Tree::new(ListNode { - name: "root".to_string(), - command: Command::None, - }); - let mut root = tree.root_mut(); - create_directory(data, &mut root, &directory); - Tab { name, tree } - }) - .collect(); - - if tabs.is_empty() { - panic!("No tabs found"); - } - tabs -} - fn filter_entries(entries: &mut Vec) { entries.retain_mut(|entry| { if !entry.is_supported() { @@ -176,15 +168,21 @@ fn create_directory(data: Vec, node: &mut NodeMut, command_dir: } } } + impl TabList { - fn get_tabs(command_dir: &Path) -> Vec { - let tab_files = std::fs::read_to_string(command_dir.join("tabs.toml")) - .expect("Failed to read tabs.toml"); + fn get_tabs() -> Vec { + let temp_dir = TempDir::new("linutil_scripts").unwrap().into_path(); + TAB_DATA + .extract(&temp_dir) + .expect("Failed to extract the saved directory"); + + let tab_files = + std::fs::read_to_string(temp_dir.join("tabs.toml")).expect("Failed to read tabs.toml"); let data: Self = toml::from_str(&tab_files).expect("Failed to parse tabs.toml"); data.directories - .into_iter() - .map(|path| command_dir.join(path).join("tab_data.toml")) + .iter() + .map(|path| temp_dir.join(path).join("tab_data.toml")) .collect() } } diff --git a/core/src/lib.rs b/core/src/lib.rs new file mode 100644 index 00000000..93ce0619 --- /dev/null +++ b/core/src/lib.rs @@ -0,0 +1,25 @@ +mod inner; + +use ego_tree::Tree; +use std::path::PathBuf; + +pub use inner::get_tabs; + +#[derive(Clone, Hash, Eq, PartialEq)] +pub enum Command { + Raw(String), + LocalFile(PathBuf), + None, // Directory +} + +#[derive(Clone, Hash, Eq, PartialEq)] +pub struct Tab { + pub name: String, + pub tree: Tree, +} + +#[derive(Clone, Hash, Eq, PartialEq)] +pub struct ListNode { + pub name: String, + pub command: Command, +} diff --git a/docs/KnownIssues.md b/docs/KnownIssues.md index 7632f0f8..0d12097e 100644 --- a/docs/KnownIssues.md +++ b/docs/KnownIssues.md @@ -1 +1,4 @@ -# Known Issues \ No newline at end of file +# Known Issues +--- + +- [Known Issues](https://github.com/ChrisTitusTech/linutil/issues) diff --git a/docs/assets/mainpage.png b/docs/assets/mainpage.png deleted file mode 100644 index c9c7ec51..00000000 Binary files a/docs/assets/mainpage.png and /dev/null differ diff --git a/docs/contribute.md b/docs/contribute.md index 2189c3c0..e47f8172 100644 --- a/docs/contribute.md +++ b/docs/contribute.md @@ -1,2 +1,62 @@ # How to Contribute? +Thank you for considering contributing to Linutil! We appreciate your effort in helping improve this project. To ensure that your contributions align with the goals and quality standards of Linutil, please follow these guidelines: + +## 1. **Install Rust**: + +Make sure you have Rust installed on your machine. You can install it by following the instructions at [rust-lang.org](https://www.rust-lang.org/tools/install). + +## 2. **Fork and Clone the repo** + +1. Make a fork of the repo in GitHub +2. Clone the fork +```bash +git clone https://github.com/YOUR_USERNAME_HERE/linutil.git +cd linutil + ``` + +## 3. Make your changes +- **Edit the files you want to change**: Make your changes to the relevant files. +- **Test your changes**: Run `cargo run` to test your modifications in a local environment and ensure everything works as expected. + +## 4. Understand the existing code + +- **Have a clear reason**: Don’t change the way things are done without a valid reason. If you propose an alteration, be prepared to explain why it’s necessary and how it improves the project. +- **Respect existing conventions**: Changes should align with the existing code style, design patterns, and overall project philosophy. If you want to introduce a new way of doing things, justify it clearly. + +## 5. Learn from Past Pull Requests (PRs) + +- **Check merged PRs**: Reviewing merged pull requests can give you an idea of what kind of contributions are accepted and how they are implemented. +- **Study rejected PRs**: This is especially important as it helps you avoid making similar mistakes or proposing changes that have already been considered and declined. + +## 6. Write Clean, Descriptive Commit Messages + +- **Be descriptive**: Your commit messages should clearly describe what the change does and why it was made. +- **Use the imperative mood**: For example, "Add feature X" or "Fix bug in Y", rather than "Added feature X" or "Fixed bug in Y". +- **Keep commits clean**: Avoid committing a change and then immediately following it with a fix for that change. Instead, amend your commit or squash it if needed. + +## 7. Keep Your Pull Requests (PRs) Small and Focused + +- **Make small, targeted PRs**: Focus on one feature or fix per pull request. This makes it easier to review and increases the likelihood of acceptance. +- **Avoid combining unrelated changes**: PRs that tackle multiple unrelated issues are harder to review and might be rejected because of a single problem. + +## 8. Code Review and Feedback + +- **Expect feedback**: PRs will undergo code review. Be open to feedback and willing to make adjustments as needed. +- **Participate in reviews**: If you feel comfortable, review other contributors' PRs as well. Peer review is a great way to learn and ensure high-quality contributions. + +## 9. Contributing Is More Than Just Code + +- **Test the tool**: Running tests and providing feedback on how the tool works in different environments is a valuable contribution. +- **Write well-formed issues**: Clearly describe bugs or problems you encounter, providing as much detail as possible, including steps to reproduce the issue. +- **Propose reasonable feature requests**: When suggesting new features, ensure they fit within the scope, style, and design of the project. Provide clear reasoning and use cases. + +## 10. Documentation + +- **Update the documentation**: If your change affects the functionality, please update the relevant documentation files to reflect this. + +## 11. License + +- **Agree to the license**: By contributing to Linutil, you agree that your contributions will be licensed under the project's MIT license. + +We appreciate your contributions and look forward to collaborating with you to make Linutil better! diff --git a/docs/index.md b/docs/index.md index 91cd803e..2493dc82 100644 --- a/docs/index.md +++ b/docs/index.md @@ -3,14 +3,21 @@ [![Version](https://img.shields.io/github/v/release/ChrisTitusTech/linutil?color=%230567ff&label=Latest%20Release&style=for-the-badge)](https://github.com/ChrisTitusTech/linutil/releases/latest) ![GitHub Downloads (specific asset, all releases)](https://img.shields.io/github/downloads/ChrisTitusTech/linutil/start.sh?label=Total%20Downloads&style=for-the-badge) -## Running latest release of LinUtil +## Running the latest release of LinUtil -* You will first need to start a Terminal on your linux machine. -* Now you will need to run following command: - ``` - curl -fsSL https://christitus.com/linux | sh - ``` +To get started, run the following command in your terminal: + +### Stable branch + +``` +curl -fsSL https://christitus.com/linux | sh +``` +--- + +After you've ran the command, you should see a GUI on your screen; It will look something like this: + +![preview](assets/preview.png) !!! info - LinUtil is updated weekly as of the time of writing. Consequently, features and functionalities may evolve, and the documentation may not always reflect the most current images or information. \ No newline at end of file + LinUtil is updated weekly as of the time of writing. Consequently, features and functionalities may evolve, and the documentation may not always reflect the most current images or information. diff --git a/docs/userguide.md b/docs/userguide.md index 15ee5527..8fc385f5 100644 --- a/docs/userguide.md +++ b/docs/userguide.md @@ -1,9 +1,32 @@ # Walkthrough +--- -## How to run LinUtil? -Run the following command in a terminal on your Linux System: -``` -curl -fsSL https://christitus.com/linux | sh -``` -Once you have run that command you will see the following GUI show on your screen: -![Main Page](assets/mainpage.png) \ No newline at end of file +## System Setup + +- **System Update**: Starts a system-wide update. +- **Compile Setup**: Installs tools and dependencies for compiling software. +- **Gaming Setup**: Configures Steam, Lutris, etc. +- **Global Theming**: Sets up and manages global themes. +- **Remove Snaps**: Removes snap packages. + +## Arch Setup + +- **Paru Setup**: Installs the Paru AUR helper. +- **Yay Setup**: Installs the Yay AUR helper. +- **Server Setup**: Installs Arch Linux. + +## Fedora Setup + +- **RPM Fusion Setup**: Adds RPM Fusion repositories. + +## Applications Setup + +- **Alacritty Setup**: Installs and configures Alacritty for you. +- **DwmTitus Setup**: Sets up the Dwm window manager. +- **Kitty Setup**: Installs and configures Kitty for you. +- **Rofi Setup**: Installs the Rofi application/script launcher. +- **Zsh Setup**: Installs and configures Zsh for you. + +## Security Features + +- **Firewall Baselines**: Sets up firewall rules. diff --git a/src/commands/applications-setup/alacritty-setup.sh b/tabs/applications-setup/alacritty-setup.sh similarity index 100% rename from src/commands/applications-setup/alacritty-setup.sh rename to tabs/applications-setup/alacritty-setup.sh diff --git a/src/commands/applications-setup/dwmtitus-setup.sh b/tabs/applications-setup/dwmtitus-setup.sh similarity index 98% rename from src/commands/applications-setup/dwmtitus-setup.sh rename to tabs/applications-setup/dwmtitus-setup.sh index 6a1fb162..8c0fc97a 100644 --- a/src/commands/applications-setup/dwmtitus-setup.sh +++ b/tabs/applications-setup/dwmtitus-setup.sh @@ -15,11 +15,11 @@ setupDWM() { $ESCALATION_TOOL "$PACKAGER" -S --needed --noconfirm base-devel libx11 libxinerama libxft imlib2 libxcb ;; apt) - $ESCALATION_TOOL "$PACKAGER" install -y build-essential libx11-dev libxinerama-dev libxft-dev libimlib2-dev libxcb1-dev + $ESCALATION_TOOL "$PACKAGER" install -y build-essential libx11-dev libxinerama-dev libxft-dev libimlib2-dev libxcb1-dev libx11-xcb1 ;; dnf) $ESCALATION_TOOL "$PACKAGER" groupinstall -y "Development Tools" - $ESCALATION_TOOL "$PACKAGER" install -y libx11-devel libxinerama-devel libxft-devel imlib2-devel libxcb-devel + $ESCALATION_TOOL "$PACKAGER" install -y libX11-devel libXinerama-devel libXft-devel imlib2-devel libxcb-devel ;; *) echo "Unsupported package manager: $PACKAGER" @@ -302,4 +302,4 @@ setupDWM makeDWM install_nerd_font clone_config_folders -configure_backgrounds \ No newline at end of file +configure_backgrounds diff --git a/src/commands/applications-setup/kitty-setup.sh b/tabs/applications-setup/kitty-setup.sh similarity index 100% rename from src/commands/applications-setup/kitty-setup.sh rename to tabs/applications-setup/kitty-setup.sh diff --git a/src/commands/applications-setup/rofi-setup.sh b/tabs/applications-setup/rofi-setup.sh similarity index 100% rename from src/commands/applications-setup/rofi-setup.sh rename to tabs/applications-setup/rofi-setup.sh diff --git a/src/commands/applications-setup/tab_data.toml b/tabs/applications-setup/tab_data.toml similarity index 100% rename from src/commands/applications-setup/tab_data.toml rename to tabs/applications-setup/tab_data.toml diff --git a/src/commands/applications-setup/zsh-setup.sh b/tabs/applications-setup/zsh-setup.sh similarity index 100% rename from src/commands/applications-setup/zsh-setup.sh rename to tabs/applications-setup/zsh-setup.sh diff --git a/src/commands/common-script.sh b/tabs/common-script.sh similarity index 69% rename from src/commands/common-script.sh rename to tabs/common-script.sh index bfea62f4..aa338ba6 100644 --- a/src/commands/common-script.sh +++ b/tabs/common-script.sh @@ -11,6 +11,36 @@ command_exists() { which "$1" >/dev/null 2>&1 } +checkAURHelper() { + ## Check & Install AUR helper + if [ "$PACKAGER" = "pacman" ]; then + if [ -z "$AUR_HELPER_CHECKED" ]; then + AUR_HELPERS="yay paru" + for helper in ${AUR_HELPERS}; do + if command_exists "${helper}"; then + AUR_HELPER=${helper} + echo "Using ${helper} as AUR helper" + AUR_HELPER_CHECKED=true + return 0 + fi + done + + echo "Installing yay as AUR helper..." + $ESCALATION_TOOL "$PACKAGER" -S --needed --noconfirm base-devel + cd /opt && $ESCALATION_TOOL git clone https://aur.archlinux.org/yay-git.git && $ESCALATION_TOOL chown -R "$USER":"$USER" ./yay-git + cd yay-git && makepkg --noconfirm -si + + if command_exists yay; then + AUR_HELPER="yay" + AUR_HELPER_CHECKED=true + else + echo -e "${RED}Failed to install AUR helper.${RC}" + exit 1 + fi + fi + fi +} + checkEscalationTool() { ## Check for escalation tools. if [ -z "$ESCALATION_TOOL_CHECKED" ]; then @@ -100,4 +130,5 @@ checkEnv() { checkSuperUser checkDistro checkEscalationTool + checkAURHelper } diff --git a/src/commands/gaming/diablo-ii/d2r-loot-filters.sh b/tabs/gaming/diablo-ii/d2r-loot-filters.sh similarity index 100% rename from src/commands/gaming/diablo-ii/d2r-loot-filters.sh rename to tabs/gaming/diablo-ii/d2r-loot-filters.sh diff --git a/src/commands/gaming/tab_data.toml b/tabs/gaming/tab_data.toml similarity index 100% rename from src/commands/gaming/tab_data.toml rename to tabs/gaming/tab_data.toml diff --git a/src/commands/security/firewall-baselines.sh b/tabs/security/firewall-baselines.sh similarity index 100% rename from src/commands/security/firewall-baselines.sh rename to tabs/security/firewall-baselines.sh diff --git a/src/commands/security/tab_data.toml b/tabs/security/tab_data.toml similarity index 100% rename from src/commands/security/tab_data.toml rename to tabs/security/tab_data.toml diff --git a/src/commands/system-setup/1-compile-setup.sh b/tabs/system-setup/1-compile-setup.sh similarity index 76% rename from src/commands/system-setup/1-compile-setup.sh rename to tabs/system-setup/1-compile-setup.sh index 2e1f644f..1a45d153 100755 --- a/src/commands/system-setup/1-compile-setup.sh +++ b/tabs/system-setup/1-compile-setup.sh @@ -15,22 +15,6 @@ installDepend() { else echo "Multilib is already enabled." fi - if ! command_exists yay && ! command_exists paru; then - echo "Installing yay as AUR helper..." - $ESCALATION_TOOL "$PACKAGER" -S --needed --noconfirm base-devel - cd /opt && $ESCALATION_TOOL git clone https://aur.archlinux.org/yay-git.git && $ESCALATION_TOOL chown -R "$USER":"$USER" ./yay-git - cd yay-git && makepkg --noconfirm -si - else - echo "Aur helper already installed" - fi - if command_exists yay; then - AUR_HELPER="yay" - elif command_exists paru; then - AUR_HELPER="paru" - else - echo "No AUR helper found. Please install yay or paru." - exit 1 - fi $AUR_HELPER -S --needed --noconfirm "$DEPENDENCIES" ;; apt-get|nala) @@ -80,6 +64,7 @@ install_additional_dependencies() { } checkEnv +checkAURHelper checkEscalationTool installDepend -install_additional_dependencies +install_additional_dependencies \ No newline at end of file diff --git a/src/commands/system-setup/2-gaming-setup.sh b/tabs/system-setup/2-gaming-setup.sh similarity index 84% rename from src/commands/system-setup/2-gaming-setup.sh rename to tabs/system-setup/2-gaming-setup.sh index 207d7817..ce52aea2 100755 --- a/src/commands/system-setup/2-gaming-setup.sh +++ b/tabs/system-setup/2-gaming-setup.sh @@ -13,22 +13,6 @@ installDepend() { else echo "Multilib is already enabled." fi - if ! command_exists yay && ! command_exists paru; then - echo "Installing yay as AUR helper..." - $ESCALATION_TOOL ${PACKAGER} -S --needed --noconfirm base-devel - cd /opt && $ESCALATION_TOOL git clone https://aur.archlinux.org/yay-git.git && $ESCALATION_TOOL chown -R ${USER}:${USER} ./yay-git - cd yay-git && makepkg --noconfirm -si - else - echo "Aur helper already installed" - fi - if command_exists yay; then - AUR_HELPER="yay" - elif command_exists paru; then - AUR_HELPER="paru" - else - echo "No AUR helper found. Please install yay or paru." - exit 1 - fi $AUR_HELPER -S --needed --noconfirm wine giflib lib32-giflib libpng lib32-libpng libldap lib32-libldap gnutls lib32-gnutls \ mpg123 lib32-mpg123 openal lib32-openal v4l-utils lib32-v4l-utils libpulse lib32-libpulse libgpg-error \ lib32-libgpg-error alsa-plugins lib32-alsa-plugins alsa-lib lib32-alsa-lib libjpeg-turbo lib32-libjpeg-turbo \ @@ -109,6 +93,7 @@ install_additional_dependencies() { } checkEnv +checkAURHelper checkEscalationTool installDepend -install_additional_dependencies +install_additional_dependencies \ No newline at end of file diff --git a/src/commands/system-setup/3-global-theme.sh b/tabs/system-setup/3-global-theme.sh similarity index 100% rename from src/commands/system-setup/3-global-theme.sh rename to tabs/system-setup/3-global-theme.sh diff --git a/src/commands/system-setup/4-remove-snaps.sh b/tabs/system-setup/4-remove-snaps.sh similarity index 100% rename from src/commands/system-setup/4-remove-snaps.sh rename to tabs/system-setup/4-remove-snaps.sh diff --git a/src/commands/system-setup/arch/paru-setup.sh b/tabs/system-setup/arch/paru-setup.sh similarity index 100% rename from src/commands/system-setup/arch/paru-setup.sh rename to tabs/system-setup/arch/paru-setup.sh diff --git a/src/commands/system-setup/arch/server-setup.sh b/tabs/system-setup/arch/server-setup.sh similarity index 100% rename from src/commands/system-setup/arch/server-setup.sh rename to tabs/system-setup/arch/server-setup.sh diff --git a/src/commands/system-setup/arch/yay-setup.sh b/tabs/system-setup/arch/yay-setup.sh similarity index 100% rename from src/commands/system-setup/arch/yay-setup.sh rename to tabs/system-setup/arch/yay-setup.sh diff --git a/src/commands/system-setup/fedora/rpm-fusion-setup.sh b/tabs/system-setup/fedora/rpm-fusion-setup.sh similarity index 100% rename from src/commands/system-setup/fedora/rpm-fusion-setup.sh rename to tabs/system-setup/fedora/rpm-fusion-setup.sh diff --git a/src/commands/system-setup/system-update.sh b/tabs/system-setup/system-update.sh similarity index 75% rename from src/commands/system-setup/system-update.sh rename to tabs/system-setup/system-update.sh index 195ddf69..24c5eed2 100755 --- a/src/commands/system-setup/system-update.sh +++ b/tabs/system-setup/system-update.sh @@ -5,25 +5,8 @@ fastUpdate() { case ${PACKAGER} in pacman) - if ! command_exists yay && ! command_exists paru; then - echo "Installing yay as AUR helper..." - $ESCALATION_TOOL ${PACKAGER} -S --needed --noconfirm base-devel || { echo -e "${RED}Failed to install base-devel${RC}"; exit 1; } - cd /opt && $ESCALATION_TOOL git clone https://aur.archlinux.org/yay-git.git && $ESCALATION_TOOL chown -R ${USER}:${USER} ./yay-git - cd yay-git && makepkg --noconfirm -si || { echo -e "${RED}Failed to install yay${RC}"; exit 1; } - else - echo "AUR helper already installed" - fi - if command_exists yay; then - AUR_HELPER="yay" - elif command_exists paru; then - AUR_HELPER="paru" - else - echo "No AUR helper found. Please install yay or paru." - exit 1 - fi - - ${AUR_HELPER} -S --needed --noconfirm rate-mirrors-bin + $AUR_HELPER -S --needed --noconfirm rate-mirrors-bin if [ -s /etc/pacman.d/mirrorlist ]; then $ESCALATION_TOOL cp /etc/pacman.d/mirrorlist /etc/pacman.d/mirrorlist.bak @@ -113,7 +96,8 @@ updateFlatpaks() { } checkEnv +checkAURHelper checkEscalationTool fastUpdate updateSystem -updateFlatpaks +updateFlatpaks \ No newline at end of file diff --git a/src/commands/system-setup/tab_data.toml b/tabs/system-setup/tab_data.toml similarity index 100% rename from src/commands/system-setup/tab_data.toml rename to tabs/system-setup/tab_data.toml diff --git a/src/commands/tabs.toml b/tabs/tabs.toml similarity index 100% rename from src/commands/tabs.toml rename to tabs/tabs.toml diff --git a/tabs/test/lib.sh b/tabs/test/lib.sh new file mode 100755 index 00000000..45e08b7a --- /dev/null +++ b/tabs/test/lib.sh @@ -0,0 +1,5 @@ +#!/bin/sh + +say_hello () { + echo Hi +} diff --git a/tabs/test/main.sh b/tabs/test/main.sh new file mode 100755 index 00000000..7af16b96 --- /dev/null +++ b/tabs/test/main.sh @@ -0,0 +1,6 @@ +#!/bin/sh + +# The current working directory will always be inside "commands" +. test/lib.sh + +say_hello diff --git a/src/commands/utils/bluetooth-control.sh b/tabs/utils/bluetooth-control.sh similarity index 100% rename from src/commands/utils/bluetooth-control.sh rename to tabs/utils/bluetooth-control.sh diff --git a/src/commands/utils/monitor-control/auto_detect_displays.sh b/tabs/utils/monitor-control/auto_detect_displays.sh similarity index 100% rename from src/commands/utils/monitor-control/auto_detect_displays.sh rename to tabs/utils/monitor-control/auto_detect_displays.sh diff --git a/src/commands/utils/monitor-control/change_orientation.sh b/tabs/utils/monitor-control/change_orientation.sh similarity index 100% rename from src/commands/utils/monitor-control/change_orientation.sh rename to tabs/utils/monitor-control/change_orientation.sh diff --git a/src/commands/utils/monitor-control/disable_monitor.sh b/tabs/utils/monitor-control/disable_monitor.sh similarity index 100% rename from src/commands/utils/monitor-control/disable_monitor.sh rename to tabs/utils/monitor-control/disable_monitor.sh diff --git a/src/commands/utils/monitor-control/duplicate_displays.sh b/tabs/utils/monitor-control/duplicate_displays.sh similarity index 100% rename from src/commands/utils/monitor-control/duplicate_displays.sh rename to tabs/utils/monitor-control/duplicate_displays.sh diff --git a/src/commands/utils/monitor-control/enable_monitor.sh b/tabs/utils/monitor-control/enable_monitor.sh similarity index 100% rename from src/commands/utils/monitor-control/enable_monitor.sh rename to tabs/utils/monitor-control/enable_monitor.sh diff --git a/src/commands/utils/monitor-control/extend_displays.sh b/tabs/utils/monitor-control/extend_displays.sh similarity index 100% rename from src/commands/utils/monitor-control/extend_displays.sh rename to tabs/utils/monitor-control/extend_displays.sh diff --git a/src/commands/utils/monitor-control/manage_arrangement.sh b/tabs/utils/monitor-control/manage_arrangement.sh similarity index 100% rename from src/commands/utils/monitor-control/manage_arrangement.sh rename to tabs/utils/monitor-control/manage_arrangement.sh diff --git a/src/commands/utils/monitor-control/reset_scaling.sh b/tabs/utils/monitor-control/reset_scaling.sh similarity index 100% rename from src/commands/utils/monitor-control/reset_scaling.sh rename to tabs/utils/monitor-control/reset_scaling.sh diff --git a/src/commands/utils/monitor-control/scale_monitor.sh b/tabs/utils/monitor-control/scale_monitor.sh similarity index 100% rename from src/commands/utils/monitor-control/scale_monitor.sh rename to tabs/utils/monitor-control/scale_monitor.sh diff --git a/src/commands/utils/monitor-control/set_primary_monitor.sh b/tabs/utils/monitor-control/set_primary_monitor.sh similarity index 100% rename from src/commands/utils/monitor-control/set_primary_monitor.sh rename to tabs/utils/monitor-control/set_primary_monitor.sh diff --git a/src/commands/utils/monitor-control/set_resolutions.sh b/tabs/utils/monitor-control/set_resolutions.sh similarity index 100% rename from src/commands/utils/monitor-control/set_resolutions.sh rename to tabs/utils/monitor-control/set_resolutions.sh diff --git a/src/commands/utils/monitor-control/utility_functions.sh b/tabs/utils/monitor-control/utility_functions.sh similarity index 100% rename from src/commands/utils/monitor-control/utility_functions.sh rename to tabs/utils/monitor-control/utility_functions.sh diff --git a/src/commands/utils/numlock.sh b/tabs/utils/numlock.sh similarity index 100% rename from src/commands/utils/numlock.sh rename to tabs/utils/numlock.sh diff --git a/src/commands/utils/tab_data.toml b/tabs/utils/tab_data.toml similarity index 100% rename from src/commands/utils/tab_data.toml rename to tabs/utils/tab_data.toml diff --git a/src/commands/utils/wifi-control.sh b/tabs/utils/wifi-control.sh similarity index 100% rename from src/commands/utils/wifi-control.sh rename to tabs/utils/wifi-control.sh diff --git a/tui/Cargo.toml b/tui/Cargo.toml new file mode 100644 index 00000000..383dd154 --- /dev/null +++ b/tui/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "linutil_tui" +edition = "2021" +version.workspace = true +license.workspace = true + +[dependencies] +clap = { version = "4.5.16", features = ["derive"] } +crossterm = "0.28.1" +ego-tree = { workspace = true } +oneshot = "0.1.8" +portable-pty = "0.8.1" +ratatui = "0.28.1" +tui-term = "0.1.12" +unicode-width = "0.1.13" +linutil_core = { path = "../core" } + +[build-dependencies] +chrono = "0.4.33" + +[[bin]] +name = "linutil" +path = "src/main.rs" diff --git a/tui/build.rs b/tui/build.rs new file mode 100644 index 00000000..121931c1 --- /dev/null +++ b/tui/build.rs @@ -0,0 +1,7 @@ +fn main() { + // Add current date as a variable to be displayed in the 'Linux Toolbox' text. + println!( + "cargo:rustc-env=BUILD_DATE={}", + chrono::Local::now().format("%Y-%m-%d") + ); +} diff --git a/src/filter.rs b/tui/src/filter.rs similarity index 97% rename from src/filter.rs rename to tui/src/filter.rs index 3e90af8f..e6ff2cc2 100644 --- a/src/filter.rs +++ b/tui/src/filter.rs @@ -1,8 +1,9 @@ -use crate::{state::ListEntry, tabs::Tab, theme::Theme}; +use crate::{state::ListEntry, theme::Theme}; use crossterm::event::{KeyCode, KeyEvent}; use ego_tree::NodeId; +use linutil_core::Tab; use ratatui::{ - layout::Rect, + layout::{Position, Rect}, style::Style, text::Span, widgets::{Block, Borders, Paragraph}, @@ -108,7 +109,7 @@ impl Filter { .sum(); let x = area.x + cursor_position as u16 + 1; let y = area.y + 1; - frame.set_cursor(x, y); + frame.set_cursor_position(Position::new(x, y)); } } // Handles key events. Returns true if search must be exited diff --git a/src/float.rs b/tui/src/float.rs similarity index 92% rename from src/float.rs rename to tui/src/float.rs index 3b063cae..e60fe464 100644 --- a/src/float.rs +++ b/tui/src/float.rs @@ -4,10 +4,13 @@ use ratatui::{ Frame, }; +use crate::hint::ShortcutList; + pub trait FloatContent { fn draw(&mut self, frame: &mut Frame, area: Rect); fn handle_key_event(&mut self, key: &KeyEvent) -> bool; fn is_finished(&self) -> bool; + fn get_shortcut_list(&self) -> ShortcutList; } pub struct Float { @@ -69,4 +72,8 @@ impl Float { _ => self.content.handle_key_event(key), } } + + pub fn get_shortcut_list(&self) -> ShortcutList { + self.content.get_shortcut_list() + } } diff --git a/src/floating_text.rs b/tui/src/floating_text.rs similarity index 86% rename from src/floating_text.rs rename to tui/src/floating_text.rs index 482a8822..ccf796ee 100644 --- a/src/floating_text.rs +++ b/tui/src/floating_text.rs @@ -1,5 +1,9 @@ -use crate::{float::FloatContent, running_command::Command}; +use crate::{ + float::FloatContent, + hint::{Shortcut, ShortcutList}, +}; use crossterm::event::{KeyCode, KeyEvent}; +use linutil_core::Command; use ratatui::{ layout::Rect, style::{Style, Stylize}, @@ -103,4 +107,15 @@ impl FloatContent for FloatingText { fn is_finished(&self) -> bool { true } + + fn get_shortcut_list(&self) -> ShortcutList { + ShortcutList { + scope_name: "Floating text", + hints: vec![ + Shortcut::new(vec!["j", "Down"], "Scroll down"), + Shortcut::new(vec!["k", "Up"], "Scroll up"), + Shortcut::new(vec!["Enter", "q"], "Close window"), + ], + } + } } diff --git a/tui/src/hint.rs b/tui/src/hint.rs new file mode 100644 index 00000000..27474ea9 --- /dev/null +++ b/tui/src/hint.rs @@ -0,0 +1,161 @@ +use ratatui::{ + layout::{Margin, Rect}, + style::{Style, Stylize}, + text::{Line, Span}, + widgets::{Block, Borders, Paragraph}, + Frame, +}; + +use crate::state::{AppState, Focus}; + +pub const SHORTCUT_LINES: usize = 2; + +pub struct ShortcutList { + pub scope_name: &'static str, + pub hints: Vec, +} + +pub struct Shortcut { + pub key_sequenses: Vec>, + pub desc: &'static str, +} + +pub fn span_vec_len(span_vec: &[Span]) -> usize { + span_vec.iter().rfold(0, |init, s| init + s.width()) +} +impl ShortcutList { + pub fn draw(&self, frame: &mut Frame, area: Rect) { + let block = Block::default() + .title(self.scope_name) + .borders(Borders::all()); + let inner_area = area.inner(Margin::new(1, 1)); + let mut shortcut_list: Vec> = self.hints.iter().map(|h| h.to_spans()).collect(); + + let mut lines = vec![Line::default(); SHORTCUT_LINES]; + let mut idx = 0; + + while idx < SHORTCUT_LINES - 1 { + let split_idx = shortcut_list + .iter() + .scan(0usize, |total_len, s| { + *total_len += span_vec_len(s); + if *total_len > inner_area.width as usize { + None + } else { + *total_len += 4; + Some(1) + } + }) + .count(); + let new_shortcut_list = shortcut_list.split_off(split_idx); + let line: Vec<_> = shortcut_list + .into_iter() + .flat_map(|mut s| { + s.push(Span::default().content(" ")); + s + }) + .collect(); + shortcut_list = new_shortcut_list; + lines[idx] = line.into(); + idx += 1; + } + lines[idx] = shortcut_list + .into_iter() + .flat_map(|mut s| { + s.push(Span::default().content(" ")); + s + }) + .collect(); + + let p = Paragraph::new(lines).block(block); + frame.render_widget(p, area); + } +} + +impl Shortcut { + pub fn new(key_sequences: Vec<&'static str>, desc: &'static str) -> Self { + Self { + key_sequenses: key_sequences + .iter() + .map(|s| Span::styled(*s, Style::default().bold())) + .collect(), + desc, + } + } + + fn to_spans(&self) -> Vec { + let mut ret: Vec<_> = self + .key_sequenses + .iter() + .flat_map(|seq| { + [ + Span::default().content("["), + seq.clone(), + Span::default().content("] "), + ] + }) + .collect(); + ret.push(Span::styled(self.desc, Style::default().italic())); + ret + } +} + +fn get_list_item_shortcut(state: &AppState) -> Shortcut { + if state.selected_item_is_dir() { + Shortcut::new(vec!["l", "Right", "Enter"], "Go to selected dir") + } else { + Shortcut::new(vec!["l", "Right", "Enter"], "Run selected command") + } +} + +pub fn draw_shortcuts(state: &AppState, frame: &mut Frame, area: Rect) { + match state.focus { + Focus::Search => ShortcutList { + scope_name: "Search bar", + hints: vec![Shortcut::new(vec!["Enter"], "Finish search")], + }, + Focus::List => { + let mut hints = Vec::new(); + hints.push(Shortcut::new(vec!["q", "CTRL-c"], "Exit linutil")); + if state.at_root() { + hints.push(Shortcut::new(vec!["h", "Left", "Tab"], "Focus tab list")); + hints.push(get_list_item_shortcut(state)); + } else { + if state.selected_item_is_up_dir() { + hints.push(Shortcut::new( + vec!["l", "Right", "Enter", "h", "Left"], + "Go to parrent directory", + )); + } else { + hints.push(Shortcut::new(vec!["h", "Left"], "Go to parrent directory")); + hints.push(get_list_item_shortcut(state)); + if state.selected_item_is_cmd() { + hints.push(Shortcut::new(vec!["p"], "Enable preview")); + } + } + hints.push(Shortcut::new(vec!["Tab"], "Focus tab list")); + }; + hints.push(Shortcut::new(vec!["k", "Up"], "Select item above")); + hints.push(Shortcut::new(vec!["j", "Down"], "Select item below")); + hints.push(Shortcut::new(vec!["t"], "Next theme")); + hints.push(Shortcut::new(vec!["T"], "Previous theme")); + ShortcutList { + scope_name: "Item list", + hints, + } + } + Focus::TabList => ShortcutList { + scope_name: "Tab list", + hints: vec![ + Shortcut::new(vec!["q", "CTRL-c"], "Exit linutil"), + Shortcut::new(vec!["l", "Right", "Tab", "Enter"], "Focus action list"), + Shortcut::new(vec!["k", "Up"], "Select item above"), + Shortcut::new(vec!["j", "Down"], "Select item below"), + Shortcut::new(vec!["t"], "Next theme"), + Shortcut::new(vec!["T"], "Previous theme"), + ], + }, + Focus::FloatingWindow(ref float) => float.get_shortcut_list(), + } + .draw(frame, area); +} diff --git a/src/main.rs b/tui/src/main.rs similarity index 85% rename from src/main.rs rename to tui/src/main.rs index 85c0278f..4355a910 100644 --- a/src/main.rs +++ b/tui/src/main.rs @@ -1,9 +1,9 @@ mod filter; mod float; mod floating_text; +mod hint; mod running_command; pub mod state; -mod tabs; mod theme; use std::{ @@ -20,13 +20,11 @@ use crossterm::{ terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen}, ExecutableCommand, }; -use include_dir::include_dir; use ratatui::{ backend::{Backend, CrosstermBackend}, Terminal, }; use state::AppState; -use tempdir::TempDir; // Linux utility toolbox #[derive(Debug, Parser)] @@ -43,13 +41,7 @@ struct Args { fn main() -> std::io::Result<()> { let args = Args::parse(); - let commands_dir = include_dir!("src/commands"); - let temp_dir: TempDir = TempDir::new("linutil_scripts").unwrap(); - commands_dir - .extract(temp_dir.path()) - .expect("Failed to extract the saved directory"); - - let mut state = AppState::new(args.theme, temp_dir.path(), args.override_validation); + let mut state = AppState::new(args.theme, args.override_validation); stdout().execute(EnterAlternateScreen)?; enable_raw_mode()?; @@ -71,7 +63,6 @@ fn main() -> std::io::Result<()> { fn run(terminal: &mut Terminal, state: &mut AppState) -> io::Result<()> { loop { terminal.draw(|frame| state.draw(frame)).unwrap(); - // Wait for an event if !event::poll(Duration::from_millis(10))? { continue; diff --git a/src/running_command.rs b/tui/src/running_command.rs similarity index 95% rename from src/running_command.rs rename to tui/src/running_command.rs index f5041ee4..d8df3402 100644 --- a/src/running_command.rs +++ b/tui/src/running_command.rs @@ -1,5 +1,9 @@ -use crate::float::FloatContent; +use crate::{ + float::FloatContent, + hint::{Shortcut, ShortcutList}, +}; use crossterm::event::{KeyCode, KeyEvent, KeyModifiers}; +use linutil_core::Command; use oneshot::{channel, Receiver}; use portable_pty::{ ChildKiller, CommandBuilder, ExitStatus, MasterPty, NativePtySystem, PtySize, PtySystem, @@ -13,7 +17,6 @@ use ratatui::{ }; use std::{ io::Write, - path::PathBuf, sync::{Arc, Mutex}, thread::JoinHandle, }; @@ -22,13 +25,6 @@ use tui_term::{ widget::PseudoTerminal, }; -#[derive(Clone, Hash, Eq, PartialEq)] -pub enum Command { - Raw(String), - LocalFile(PathBuf), - None, // Directory -} - pub struct RunningCommand { /// A buffer to save all the command output (accumulates, until the command exits) buffer: Arc>>, @@ -123,6 +119,20 @@ impl FloatContent for RunningCommand { true } } + + fn get_shortcut_list(&self) -> ShortcutList { + if self.is_finished() { + ShortcutList { + scope_name: "Finished command", + hints: vec![Shortcut::new(vec!["Enter", "q"], "Close window")], + } + } else { + ShortcutList { + scope_name: "Running command", + hints: vec![Shortcut::new(vec!["CTRL-c"], "Kill the command")], + } + } + } } impl RunningCommand { diff --git a/src/state.rs b/tui/src/state.rs similarity index 71% rename from src/state.rs rename to tui/src/state.rs index 432a5af2..267e6769 100644 --- a/src/state.rs +++ b/tui/src/state.rs @@ -2,26 +2,26 @@ use crate::{ filter::{Filter, SearchAction}, float::{Float, FloatContent}, floating_text::FloatingText, - running_command::{Command, RunningCommand}, - tabs::{ListNode, Tab}, + hint::{draw_shortcuts, SHORTCUT_LINES}, + running_command::RunningCommand, theme::Theme, }; use crossterm::event::{KeyCode, KeyEvent, KeyEventKind}; use ego_tree::NodeId; +use linutil_core::{Command, ListNode, Tab}; use ratatui::{ - layout::{Constraint, Direction, Layout}, + layout::{Alignment, Constraint, Direction, Layout}, style::{Style, Stylize}, - text::Line, - widgets::{Block, Borders, List, ListState}, + text::{Line, Span}, + widgets::{Block, Borders, List, ListState, Paragraph}, Frame, }; -use std::path::Path; pub struct AppState { /// Selected theme theme: Theme, /// Currently focused area - focus: Focus, + pub focus: Focus, /// List of tabs tabs: Vec, /// Current tab @@ -49,8 +49,8 @@ pub struct ListEntry { } impl AppState { - pub fn new(theme: Theme, temp_path: &Path, override_validation: bool) -> Self { - let tabs = crate::tabs::get_tabs(temp_path, !override_validation); + pub fn new(theme: Theme, override_validation: bool) -> Self { + let tabs = linutil_core::get_tabs(!override_validation); let root_id = tabs[0].tree.root().id(); let mut state = Self { theme, @@ -65,12 +65,44 @@ impl AppState { state } pub fn draw(&mut self, frame: &mut Frame) { + let label_block = + Block::default() + .borders(Borders::all()) + .border_set(ratatui::symbols::border::Set { + top_left: " ", + top_right: " ", + bottom_left: " ", + bottom_right: " ", + vertical_left: " ", + vertical_right: " ", + horizontal_top: "*", + horizontal_bottom: "*", + }); + let str1 = "Linutil "; + let str2 = "by Chris Titus"; + let label = Paragraph::new(Line::from(vec![ + Span::styled(str1, Style::default().bold()), + Span::styled(str2, Style::default().italic()), + ])) + .block(label_block) + .alignment(Alignment::Center); + let longest_tab_display_len = self .tabs .iter() .map(|tab| tab.name.len() + self.theme.tab_icon().len()) .max() - .unwrap_or(0); + .unwrap_or(0) + .max(str1.len() + str2.len()); + + let vertical = Layout::default() + .direction(Direction::Vertical) + .constraints([ + Constraint::Percentage(100), + Constraint::Min(2 + SHORTCUT_LINES as u16), + ]) + .margin(0) + .split(frame.area()); let horizontal = Layout::default() .direction(Direction::Horizontal) @@ -78,11 +110,13 @@ impl AppState { Constraint::Min(longest_tab_display_len as u16 + 5), Constraint::Percentage(100), ]) - .split(frame.size()); + .split(vertical[0]); + let left_chunks = Layout::default() .direction(Direction::Vertical) .constraints([Constraint::Length(3), Constraint::Min(1)]) .split(horizontal[0]); + frame.render_widget(label, left_chunks[0]); let tabs = self .tabs @@ -148,6 +182,8 @@ impl AppState { if let Focus::FloatingWindow(float) = &mut self.focus { float.draw(frame, chunks[1]); } + + draw_shortcuts(self, frame, vertical[1]); } pub fn handle_key(&mut self, key: &KeyEvent) -> bool { match &mut self.focus { @@ -213,7 +249,7 @@ impl AppState { /// Checks ehther the current tree node is the root node (can we go up the tree or no) /// Returns `true` if we can't go up the tree (we are at the tree root) /// else returns `false` - fn at_root(&self) -> bool { + pub fn at_root(&self) -> bool { self.visit_stack.len() == 1 } fn enter_parent_directory(&mut self) { @@ -221,13 +257,10 @@ impl AppState { self.selection.select(Some(0)); self.update_items(); } - fn get_selected_command(&mut self, change_directory: bool) -> Option { + pub fn get_selected_command(&self) -> Option { let mut selected_index = self.selection.selected().unwrap_or(0); if !self.at_root() && selected_index == 0 { - if change_directory { - self.enter_parent_directory(); - } return None; } if !self.at_root() { @@ -237,25 +270,83 @@ impl AppState { if let Some(item) = self.filter.item_list().get(selected_index) { if !item.has_children { return Some(item.node.command.clone()); - } else if change_directory { + } + } + None + } + pub fn go_to_selected_dir(&mut self) { + let mut selected_index = self.selection.selected().unwrap_or(0); + + if !self.at_root() && selected_index == 0 { + self.enter_parent_directory(); + return; + } + + if !self.at_root() { + selected_index = selected_index.saturating_sub(1); + } + + if let Some(item) = self.filter.item_list().get(selected_index) { + if item.has_children { self.visit_stack.push(item.id); self.selection.select(Some(0)); self.update_items(); } } - None + } + pub fn selected_item_is_dir(&self) -> bool { + let mut selected_index = self.selection.selected().unwrap_or(0); + + if !self.at_root() && selected_index == 0 { + return false; + } + + if !self.at_root() { + selected_index = selected_index.saturating_sub(1); + } + + if let Some(item) = self.filter.item_list().get(selected_index) { + item.has_children + } else { + false + } + } + + pub fn selected_item_is_cmd(&self) -> bool { + let mut selected_index = self.selection.selected().unwrap_or(0); + + if !self.at_root() && selected_index == 0 { + return false; + } + + if !self.at_root() { + selected_index = selected_index.saturating_sub(1); + } + + if let Some(item) = self.filter.item_list().get(selected_index) { + !item.has_children + } else { + false + } + } + pub fn selected_item_is_up_dir(&self) -> bool { + let selected_index = self.selection.selected().unwrap_or(0); + + !self.at_root() && selected_index == 0 } fn enable_preview(&mut self) { - if let Some(command) = self.get_selected_command(false) { + if let Some(command) = self.get_selected_command() { if let Some(preview) = FloatingText::from_command(&command) { self.spawn_float(preview, 80, 80); } } } fn handle_enter(&mut self) { - if let Some(cmd) = self.get_selected_command(true) { + if let Some(cmd) = self.get_selected_command() { let command = RunningCommand::new(cmd); self.spawn_float(command, 80, 80); + } else { + self.go_to_selected_dir(); } } fn spawn_float(&mut self, float: T, width: u16, height: u16) { diff --git a/src/theme.rs b/tui/src/theme.rs similarity index 100% rename from src/theme.rs rename to tui/src/theme.rs