From b8964ab0bcb7bf9abbc3eee92fe018b6056d22fb Mon Sep 17 00:00:00 2001 From: Triston Date: Sun, 5 May 2024 09:19:16 -0500 Subject: [PATCH] init commit containing existing app --- .gitignore | 1 + Cargo.lock | 773 ++++++++++++++++++++++++++++++++++++ Cargo.toml | 11 + src/app.rs | 254 ++++++++++++ src/errors.rs | 25 ++ src/main.rs | 17 + src/tui.rs | 17 + src/ui.rs | 46 +++ src/widgets/add_new.rs | 30 ++ src/widgets/instructions.rs | 49 +++ src/widgets/left_pane.rs | 56 +++ src/widgets/mod.rs | 5 + src/widgets/right_pane.rs | 58 +++ src/widgets/title.rs | 20 + 14 files changed, 1362 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 src/app.rs create mode 100644 src/errors.rs create mode 100644 src/main.rs create mode 100644 src/tui.rs create mode 100644 src/ui.rs create mode 100644 src/widgets/add_new.rs create mode 100644 src/widgets/instructions.rs create mode 100644 src/widgets/left_pane.rs create mode 100644 src/widgets/mod.rs create mode 100644 src/widgets/right_pane.rs create mode 100644 src/widgets/title.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..48334c6 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,773 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "allocator-api2" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" + +[[package]] +name = "autocfg" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" + +[[package]] +name = "backtrace" +version = "0.3.71" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "bitflags" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" + +[[package]] +name = "cassowary" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53" + +[[package]] +name = "castaway" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a17ed5635fc8536268e5d4de1e22e81ac34419e5f052d4d51f4e01dcc263fcc" +dependencies = [ + "rustversion", +] + +[[package]] +name = "cc" +version = "1.0.96" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "065a29261d53ba54260972629f9ca6bffa69bac13cd1fed61420f7fa68b9f8bd" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "color-eyre" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55146f5e46f237f7423d74111267d4597b59b0dad0ffaf7303bce9945d843ad5" +dependencies = [ + "backtrace", + "color-spantrace", + "eyre", + "indenter", + "once_cell", + "owo-colors", + "tracing-error", +] + +[[package]] +name = "color-spantrace" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd6be1b2a7e382e2b98b43b2adcca6bb0e465af0bdd38123873ae61eb17a72c2" +dependencies = [ + "once_cell", + "owo-colors", + "tracing-core", + "tracing-error", +] + +[[package]] +name = "compact_str" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f86b9c4c00838774a6d902ef931eff7470720c51d90c2e32cfe15dc304737b3f" +dependencies = [ + "castaway", + "cfg-if", + "itoa", + "ryu", + "static_assertions", +] + +[[package]] +name = "crossterm" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df" +dependencies = [ + "bitflags", + "crossterm_winapi", + "libc", + "mio", + "parking_lot", + "signal-hook", + "signal-hook-mio", + "winapi", +] + +[[package]] +name = "crossterm_winapi" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b" +dependencies = [ + "winapi", +] + +[[package]] +name = "either" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2" + +[[package]] +name = "eyre" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd915d99f24784cdc19fd37ef22b97e3ff0ae756c7e492e9fbfe897d61e2aec" +dependencies = [ + "indenter", + "once_cell", +] + +[[package]] +name = "gimli" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +dependencies = [ + "ahash", + "allocator-api2", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "indenter" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" + +[[package]] +name = "indoc" +version = "2.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5" + +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.154" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346" + +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" + +[[package]] +name = "lru" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3262e75e648fce39813cb56ac41f3c3e3f65217ebf3844d818d1f9398cfb0dc" +dependencies = [ + "hashbrown", +] + +[[package]] +name = "memchr" +version = "2.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" + +[[package]] +name = "miniz_oxide" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" +dependencies = [ + "adler", +] + +[[package]] +name = "mio" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" +dependencies = [ + "libc", + "log", + "wasi", + "windows-sys", +] + +[[package]] +name = "object" +version = "0.32.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "owo-colors" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f" + +[[package]] +name = "parking_lot" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e4af0ca4f6caed20e900d564c242b8e5d4903fdacf31d3daf527b66fe6f42fb" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets 0.52.5", +] + +[[package]] +name = "paste" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" + +[[package]] +name = "pin-project-lite" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" + +[[package]] +name = "proc-macro2" +version = "1.0.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "ratatui" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a564a852040e82671dc50a37d88f3aa83bbc690dfc6844cfe7a2591620206a80" +dependencies = [ + "bitflags", + "cassowary", + "compact_str", + "crossterm", + "indoc", + "itertools", + "lru", + "paste", + "stability", + "strum", + "unicode-segmentation", + "unicode-width", +] + +[[package]] +name = "redox_syscall" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e" +dependencies = [ + "bitflags", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + +[[package]] +name = "rustversion" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80af6f9131f277a45a3fba6ce8e2258037bb0477a67e610d3c1fe046ab31de47" + +[[package]] +name = "rusty_standup" +version = "0.1.0" +dependencies = [ + "color-eyre", + "crossterm", + "ratatui", +] + +[[package]] +name = "ryu" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "signal-hook" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801" +dependencies = [ + "libc", + "signal-hook-registry", +] + +[[package]] +name = "signal-hook-mio" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29ad2e15f37ec9a6cc544097b78a1ec90001e9f71b81338ca39f430adaca99af" +dependencies = [ + "libc", + "mio", + "signal-hook", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +dependencies = [ + "libc", +] + +[[package]] +name = "smallvec" +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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "strum" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d8cec3501a5194c432b2b7976db6b7d10ec95c253208b45f83f7136aa985e29" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6cf59daf282c0a494ba14fd21610a0325f9f90ec9d1231dea26bcb1d696c946" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "rustversion", + "syn", +] + +[[package]] +name = "syn" +version = "2.0.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thread_local" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +dependencies = [ + "cfg-if", + "once_cell", +] + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-error" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d686ec1c0f384b1277f097b2f279a2ecc11afe8c133c1aabf036a27cb4cd206e" +dependencies = [ + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +dependencies = [ + "sharded-slab", + "thread_local", + "tracing-core", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unicode-segmentation" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" + +[[package]] +name = "unicode-width" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68f5e5f3158ecfd4b8ff6fe086db7c8467a2dfdac97fe420f2b7c4aa97af66d6" + +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +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_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", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +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" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +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" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +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" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +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" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +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" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +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" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +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" + +[[package]] +name = "zerocopy" +version = "0.7.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..4dad240 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "rusty_standup" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +color-eyre = "0.6.3" +crossterm = "0.27.0" +ratatui = "0.26.2" diff --git a/src/app.rs b/src/app.rs new file mode 100644 index 0000000..8f8ae11 --- /dev/null +++ b/src/app.rs @@ -0,0 +1,254 @@ +use crate::{tui, ui}; +use color_eyre::eyre::{Context, Ok}; +use color_eyre::Result; +use crossterm::event::{self, Event, KeyCode, KeyEvent, KeyEventKind}; +use ratatui::widgets::ListState; +use ratatui::Frame; + +pub struct StatefulList { + pub state: ListState, + pub items: Vec, +} + +#[derive(PartialEq)] +pub enum Pane { + Todo, + Completed, +} + +pub struct App { + pub exit: bool, + pub adding_new: bool, + pub new_value: String, + pub todos: StatefulList, + pub completed: StatefulList, + pub pane_in_focus: Pane, +} + +impl App { + pub fn new() -> App { + App { + exit: false, + adding_new: false, + new_value: String::new(), + pane_in_focus: Pane::Todo, + todos: StatefulList { + state: ListState::default(), + items: vec![], + }, + completed: StatefulList { + state: ListState::default(), + items: vec![], + }, + } + } + + pub fn run(&mut self, terminal: &mut tui::Tui) -> Result<()> { + while !self.exit { + terminal.draw(|frame| self.render_frame(frame))?; + self.handle_events().wrap_err("handle events failed")?; + } + Ok(()) + } + + fn render_frame(&mut self, frame: &mut Frame) { + ui::Ui::default().build(frame, self); + } + + fn handle_events(&mut self) -> Result<()> { + // check for timeout event + if !event::poll(std::time::Duration::from_millis(16))? { + return Ok(()); + } + + match event::read()? { + // check if event key is pressed + Event::Key(key_event) => { + // guard clause preventing + if key_event.kind != KeyEventKind::Press { + return Ok(()); + } + + return self + .handle_key_event(key_event) + .wrap_err_with(|| format!("handling key event failed:\n{key_event:#?}")); + } + _ => Ok(()), + } + } + + fn add_new_todo(&mut self) { + self.adding_new = true; + } + + fn handle_enter_press(&mut self) { + match self.pane_in_focus { + Pane::Todo => self.complete_todo(), + Pane::Completed => self.revert_todo(), + } + } + + fn prev_pane(&mut self) { + match self.pane_in_focus { + Pane::Todo => self.pane_in_focus = Pane::Completed, + Pane::Completed => self.pane_in_focus = Pane::Todo, + } + } + + fn next_pane(&mut self) { + match self.pane_in_focus { + Pane::Todo => self.pane_in_focus = Pane::Completed, + Pane::Completed => self.pane_in_focus = Pane::Todo, + } + } + + fn handle_edit_mode_key_events(&mut self, key_code: KeyCode) -> Result<()> { + match key_code { + KeyCode::Char(value) => { + self.new_value.push(value); + Ok(()) + } + KeyCode::Backspace => { + self.new_value.pop(); + Ok(()) + } + KeyCode::Esc => { + self.new_value = String::new(); + self.adding_new = false; + Ok(()) + } + KeyCode::Enter => { + self.todos.items.push(self.new_value.clone()); + self.todos.state.select(Some(self.todos.items.len() - 1)); + self.adding_new = false; + self.new_value = String::new(); + Ok(()) + } + _ => Ok(()), + } + } + + fn handle_normal_mode_key_events(&mut self, key_code: KeyCode) -> Result<()> { + match key_code { + KeyCode::Char('q') => self.exit(), + KeyCode::Char('a') => self.add_new_todo(), + KeyCode::Char('j') => self.go_next(), + KeyCode::Char('k') => self.go_prev(), + KeyCode::Char('h') => self.prev_pane(), + KeyCode::Char('l') => self.next_pane(), + KeyCode::Char('d') => self.delete(), + KeyCode::Enter => self.handle_enter_press(), + _ => {} + }; + Ok(()) + } + + fn handle_key_event(&mut self, key_event: KeyEvent) -> Result<()> { + match self.adding_new { + true => self.handle_edit_mode_key_events(key_event.code), + false => self.handle_normal_mode_key_events(key_event.code), + } + } + + fn exit(&mut self) { + self.exit = true; + } + + fn go_next(&mut self) { + match self.pane_in_focus { + Pane::Todo => { + match self.todos.items.len() { + i if i > 0 => self.todos.next(), + _ => {} + }; + } + Pane::Completed => { + match self.completed.items.len() { + i if i > 0 => self.completed.next(), + _ => {} + }; + } + }; + } + + fn go_prev(&mut self) { + match self.pane_in_focus { + Pane::Todo => { + match self.todos.items.len() { + i if i > 0 => self.todos.previous(), + _ => {} + }; + } + Pane::Completed => { + match self.completed.items.len() { + i if i > 0 => self.completed.previous(), + _ => {} + }; + } + } + } + + fn complete_todo(&mut self) { + if self.todos.items.len() < 1 { + return; + } + let curr = self.todos.state.selected().unwrap(); + let item_at_curr = self.todos.items.get(curr).unwrap(); + self.completed.items.push(item_at_curr.to_string()); + self.todos.items.remove(curr); + } + + fn revert_todo(&mut self) { + if self.completed.items.len() < 1 { + return; + } + let curr = self.completed.state.selected().unwrap(); + let item_at_curr = self.completed.items.get(curr).unwrap(); + self.todos.items.push(item_at_curr.to_string()); + self.completed.items.remove(curr); + } + + fn delete(&mut self) { + self.go_prev(); + match self.pane_in_focus { + Pane::Todo => { + let curr = self.todos.state.selected().unwrap(); + self.todos.items.remove(curr); + } + Pane::Completed => { + let curr = self.completed.state.selected().unwrap(); + self.completed.items.remove(curr); + } + } + } +} + +impl StatefulList { + fn next(&mut self) { + let i = match self.state.selected() { + Some(i) => { + if i >= self.items.len() - 1 { + 0 + } else { + i + 1 + } + } + None => 0, + }; + self.state.select(Some(i)); + } + + fn previous(&mut self) { + let i = match self.state.selected() { + Some(i) => { + if i == 0 { + self.items.len() - 1 + } else { + i - 1 + } + } + None => 0, + }; + self.state.select(Some(i)); + } +} diff --git a/src/errors.rs b/src/errors.rs new file mode 100644 index 0000000..1073781 --- /dev/null +++ b/src/errors.rs @@ -0,0 +1,25 @@ +use std::panic; + +use crate::tui; +use color_eyre::{config::HookBuilder, eyre}; +use color_eyre::{eyre::Ok, Result}; + +pub fn install_hooks() -> Result<()> { + let (panic_hook, eyre_hook) = HookBuilder::default().into_hooks(); + + let panic_hook = panic_hook.into_panic_hook(); + panic::set_hook(Box::new(move |panic_info| { + tui::restore().unwrap(); + panic_hook(panic_info); + })); + + let eyre_hook = eyre_hook.into_eyre_hook(); + eyre::set_hook(Box::new( + move |error: &(dyn std::error::Error + 'static)| { + tui::restore().unwrap(); + eyre_hook(error) + }, + ))?; + + Ok(()) +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..6597cb8 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,17 @@ +use app::App; +use color_eyre::Result; + +mod app; +mod errors; +mod tui; +mod ui; +mod widgets; + +fn main() -> Result<()> { + errors::install_hooks()?; + let mut terminal = tui::init()?; + let app = &mut App::new(); + app.run(&mut terminal)?; + tui::restore()?; + Ok(()) +} diff --git a/src/tui.rs b/src/tui.rs new file mode 100644 index 0000000..77bb24b --- /dev/null +++ b/src/tui.rs @@ -0,0 +1,17 @@ +use crossterm::{execute, terminal::*}; +use ratatui::prelude::*; +use std::io::{self, stdout, Stdout}; + +pub type Tui = Terminal>; + +pub fn init() -> io::Result { + execute!(stdout(), EnterAlternateScreen)?; + enable_raw_mode()?; + Terminal::new(CrosstermBackend::new(stdout())) +} + +pub fn restore() -> io::Result<()> { + execute!(stdout(), LeaveAlternateScreen)?; + disable_raw_mode()?; + Ok(()) +} diff --git a/src/ui.rs b/src/ui.rs new file mode 100644 index 0000000..b2e083e --- /dev/null +++ b/src/ui.rs @@ -0,0 +1,46 @@ +use ratatui::prelude::*; + +use crate::app::App; +use crate::widgets::add_new::AddNew; +use crate::widgets::instructions::Instructions; +use crate::widgets::left_pane::LeftPane; +use crate::widgets::right_pane::RightPane; +use crate::widgets::title::TitleWidget; + +#[derive(Default)] +pub struct Ui {} + +impl Ui { + pub fn build(&self, frame: &mut Frame, app: &mut App) { + let container = Layout::default() + .direction(Direction::Horizontal) + .constraints(vec![ + Constraint::Min(0), + Constraint::Max(100), + Constraint::Min(0), + ]) + .split(frame.size()); + // layout containing a top middle and bottom panel + let main_layout = Layout::default() + .direction(Direction::Vertical) + .constraints(vec![ + Constraint::Length(2), // title + Constraint::Length(3), // add new + Constraint::Min(2), // dual panes + add new + Constraint::Length(2), // instructions + ]) + .split(container[1]); + + let todo_panes = Layout::default() + .direction(Direction::Horizontal) + .constraints(vec![Constraint::Percentage(50), Constraint::Percentage(50)]) + .split(main_layout[2]); + + TitleWidget::default().create(frame, main_layout[0]); + AddNew::default().create(frame, main_layout[1], app); + Instructions::default().create(frame, main_layout[3], app); + + LeftPane::default().create(frame, todo_panes[0], app); + RightPane::default().create(frame, todo_panes[1], app); + } +} diff --git a/src/widgets/add_new.rs b/src/widgets/add_new.rs new file mode 100644 index 0000000..074080d --- /dev/null +++ b/src/widgets/add_new.rs @@ -0,0 +1,30 @@ +use ratatui::prelude::*; +use ratatui::widgets::block::{Position, Title}; +use ratatui::widgets::{Block, BorderType, Borders, Paragraph}; + +use crate::app::App; + +#[derive(Default)] +pub struct AddNew {} +impl AddNew { + pub fn create(self, frame: &mut Frame, area: Rect, app: &App) { + let active_style = match app.adding_new { + true => Style::default().fg(Color::LightGreen), + false => Style::default(), + }; + + let add_new_block = Block::new() + .title(if app.adding_new { + Title::from(" New Todo ".bold()) + } else { + Title::from(" New Todo ") + }) + .title_position(Position::Top) + .borders(Borders::ALL) + .border_type(BorderType::Rounded) + .border_style(active_style); + let add_new_final = Paragraph::new(app.new_value.clone()).block(add_new_block); + + frame.render_widget(add_new_final, area); + } +} diff --git a/src/widgets/instructions.rs b/src/widgets/instructions.rs new file mode 100644 index 0000000..37c5575 --- /dev/null +++ b/src/widgets/instructions.rs @@ -0,0 +1,49 @@ +use ratatui::prelude::*; +use ratatui::widgets::Paragraph; + +use crate::app::{App, Pane}; + +#[derive(Default)] +pub struct Instructions {} +impl Instructions { + pub fn create(self, frame: &mut Frame, area: Rect, app: &App) { + let instructions = match app.adding_new { + false => Self::create_add_new_instructions(app), + true => Self::create_normal_instructions(), + }; + let text = Line::from(instructions); + let instructions_final = Paragraph::new(text).style(Style::default()); + + frame.render_widget(instructions_final, area); + } + + fn create_add_new_instructions(app: &App) -> Vec> { + vec![ + " Quit ".into(), + " ".blue().bold(), + " Add Task ".into(), + " ".blue().bold(), + " Up/Down ".into(), + " ".blue().bold(), + " Left/Right".into(), + " ".blue().bold(), + if app.pane_in_focus == Pane::Todo { + " Complete Item ".into() + } else { + " Undo Item ".into() + }, + " ".blue().bold(), + " Delete Item ".into(), + " ".blue().bold(), + ] + } + + fn create_normal_instructions() -> Vec> { + vec![ + " Exit Add Mode ".into(), + " ".blue().bold(), + " Submit Task ".into(), + " ".blue().bold(), + ] + } +} diff --git a/src/widgets/left_pane.rs b/src/widgets/left_pane.rs new file mode 100644 index 0000000..69dfd09 --- /dev/null +++ b/src/widgets/left_pane.rs @@ -0,0 +1,56 @@ +use ratatui::prelude::*; +use ratatui::widgets::block::Title; +use ratatui::widgets::{Block, BorderType, Borders, List}; + +use crate::app::{App, Pane}; + +#[derive(Default)] +pub struct LeftPane {} +impl LeftPane { + pub fn create(self, frame: &mut Frame, area: Rect, app: &App) { + let focus_style = match app.pane_in_focus { + Pane::Todo if app.adding_new == false => Style::default().fg(Color::LightGreen), + _ => Style::default(), + }; + + let left_pane = Block::default() + .borders(Borders::ALL) + .border_type(BorderType::Rounded) + .title(if app.pane_in_focus == Pane::Todo { + Title::from(" Today ".bold()) + } else { + Title::from(" Today ") + }) + .title_alignment(Alignment::Center) + .border_style(focus_style); + + let items = app + .todos + .items + .clone() + .into_iter() + .enumerate() + .map(|(_i, item)| { + let style = match app.todos.state.selected() { + Some(i) => match app.pane_in_focus { + Pane::Todo => { + if i == _i { + Style::new().fg(Color::LightBlue).bg(Color::Blue) + } else { + Style::default() + } + } + _ => Style::default(), + }, + None => Style::default(), + }; + + Text::styled(format!(" ☐ {}", item), style) + }); + + let left_pane_content = List::new(items).block(left_pane); + + // Create a List from all list items and highlight the currently selected one + frame.render_widget(left_pane_content, area); + } +} diff --git a/src/widgets/mod.rs b/src/widgets/mod.rs new file mode 100644 index 0000000..31523ca --- /dev/null +++ b/src/widgets/mod.rs @@ -0,0 +1,5 @@ +pub(crate) mod add_new; +pub(crate) mod instructions; +pub(crate) mod left_pane; +pub(crate) mod right_pane; +pub(crate) mod title; diff --git a/src/widgets/right_pane.rs b/src/widgets/right_pane.rs new file mode 100644 index 0000000..bffc976 --- /dev/null +++ b/src/widgets/right_pane.rs @@ -0,0 +1,58 @@ +use ratatui::prelude::*; +use ratatui::widgets::block::Title; +use ratatui::widgets::{Block, BorderType, Borders, List}; + +use crate::app::{App, Pane}; + +#[derive(Default)] +pub struct RightPane {} +impl RightPane { + pub fn create(self, frame: &mut Frame, area: Rect, app: &mut App) { + let focus_style = match app.pane_in_focus { + Pane::Completed if app.adding_new == false => Style::default().fg(Color::LightGreen), + _ => Style::default(), + }; + + let yesterday_pane = Block::default() + .title(if app.pane_in_focus == Pane::Completed { + Title::from(" Yesterday ".bold()) + } else { + Title::from(" Yesterday ") + }) + .borders(Borders::ALL) + .border_type(BorderType::Rounded) + .title_alignment(Alignment::Center) + .border_style(focus_style); + + if app.completed.items.len() > 0 && app.completed.state.selected().is_none() { + app.completed.state.select(Some(0)); + } + + let items = app + .completed + .items + .clone() + .into_iter() + .enumerate() + .map(|(_i, item)| { + let style = match app.completed.state.selected() { + Some(i) => match app.pane_in_focus { + Pane::Completed => { + if i == _i { + Style::new().fg(Color::LightBlue).bg(Color::Blue) + } else { + Style::default() + } + } + _ => Style::default(), + }, + None => Style::default(), + }; + + Text::styled(format!(" ✓ {}", item), style) + }); + + let right_pane_content = List::new(items).block(yesterday_pane); + frame.render_widget(right_pane_content, area); + } +} diff --git a/src/widgets/title.rs b/src/widgets/title.rs new file mode 100644 index 0000000..19f1b53 --- /dev/null +++ b/src/widgets/title.rs @@ -0,0 +1,20 @@ +use ratatui::prelude::*; +use ratatui::widgets::block::{Position, Title}; +use ratatui::widgets::Block; + +#[derive(Default)] +pub struct TitleWidget {} +impl TitleWidget { + pub fn create(self, frame: &mut Frame, area: Rect) { + frame.render_widget(&self, area); + } +} +impl Widget for &TitleWidget { + fn render(self, area: Rect, buf: &mut Buffer) { + let _ = Block::new() + .title(Title::from(" Daily Standup ".bold())) + .title_position(Position::Top) + .title_alignment(Alignment::Center) + .render(area, buf); + } +}