diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 344be6e..7c7087f 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -19,5 +19,8 @@ jobs: steps: - uses: actions/checkout@v4 - run: rustup update ${{ matrix.toolchain }} && rustup default ${{ matrix.toolchain }} - - run: cargo test --verbose + - run: rustup target add wasm32-unknown-unknown + - run: cargo build -p kanash + - run: cargo build --target wasm32-unknown-unknown -p kanash-ratzilla + - run: cargo test --verbose --workspace --exclude kanash-ratzilla - run: cargo fmt --check diff --git a/.helix/languages.toml b/.helix/languages.toml new file mode 100644 index 0000000..4e50d77 --- /dev/null +++ b/.helix/languages.toml @@ -0,0 +1,2 @@ +[language-server.rust-analyzer.config] +rust-analyzer.cargo.target = "wasm32-unknown-unknown" diff --git a/Cargo.lock b/Cargo.lock index adafa29..398c0d7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -74,36 +74,56 @@ dependencies = [ [[package]] name = "anstyle-query" -version = "1.1.4" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e231f6134f61b71076a3eab506c379d4f36122f2af15a9ff04415ea4c3339e2" +checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" dependencies = [ - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] name = "anstyle-wincon" -version = "3.0.10" +version = "3.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e0633414522a32ffaac8ac6cc8f748e090c5717661fddeea04219e2344f5f2a" +checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" dependencies = [ "anstyle", "once_cell_polyfill", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] -[[package]] -name = "anyhow" -version = "1.0.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" - [[package]] name = "autocfg" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" +[[package]] +name = "beamterm-data" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03a460829990fe27b44ebdd2864258078955b0181a3d0d0ca32014d098eb756b" +dependencies = [ + "compact_str 0.9.0", + "miniz_oxide", +] + +[[package]] +name = "beamterm-renderer" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11d0f031d2e6c679e46cfcd03c6443b760338467d2191423e8d7e663153ca670" +dependencies = [ + "beamterm-data", + "compact_str 0.9.0", + "console_error_panic_hook", + "js-sys", + "thiserror 2.0.17", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + [[package]] name = "bit_field" version = "0.10.3" @@ -122,6 +142,18 @@ version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + [[package]] name = "bon" version = "3.8.1" @@ -147,6 +179,12 @@ dependencies = [ "syn", ] +[[package]] +name = "bumpalo" +version = "3.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" + [[package]] name = "bytemuck" version = "1.24.0" @@ -159,12 +197,6 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" -[[package]] -name = "bytes" -version = "1.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" - [[package]] name = "cassowary" version = "0.3.0" @@ -266,6 +298,16 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "console_error_panic_hook" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" +dependencies = [ + "cfg-if", + "wasm-bindgen", +] + [[package]] name = "convert_case" version = "0.7.1" @@ -428,6 +470,15 @@ dependencies = [ "syn", ] +[[package]] +name = "deranged" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587" +dependencies = [ + "powerfmt", +] + [[package]] name = "derive_builder" version = "0.20.2" @@ -513,9 +564,9 @@ dependencies = [ [[package]] name = "exr" -version = "1.73.0" +version = "1.74.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f83197f59927b46c04a183a619b7c29df34e63e63c7869320862268c0ef687e0" +checksum = "4300e043a56aa2cb633c01af81ca8f699a321879a7854d3896a0ba89056363be" dependencies = [ "bit_field", "half", @@ -564,93 +615,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "875488b8711a968268c7cf5d139578713097ca4635a76044e8fe8eedf831d07e" [[package]] -name = "futures" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" -dependencies = [ - "futures-channel", - "futures-core", - "futures-executor", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-channel" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" -dependencies = [ - "futures-core", - "futures-sink", -] - -[[package]] -name = "futures-core" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" - -[[package]] -name = "futures-executor" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" -dependencies = [ - "futures-core", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-io" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" - -[[package]] -name = "futures-macro" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "futures-sink" -version = "0.3.31" +name = "funty" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" - -[[package]] -name = "futures-task" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" - -[[package]] -name = "futures-util" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" -dependencies = [ - "futures-channel", - "futures-core", - "futures-io", - "futures-macro", - "futures-sink", - "futures-task", - "memchr", - "pin-project-lite", - "pin-utils", - "slab", -] +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" [[package]] name = "getrandom" @@ -659,9 +627,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" dependencies = [ "cfg-if", + "js-sys", "libc", "r-efi", "wasip2", + "wasm-bindgen", ] [[package]] @@ -796,15 +766,23 @@ dependencies = [ "rayon", ] +[[package]] +name = "js-sys" +version = "0.3.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "464a3709c7f55f1f721e5389aa6ea4e3bc6aba669353300af094b29ffbdde1d8" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + [[package]] name = "kanash" version = "0.1.5" dependencies = [ "ansi-to-tui", - "anyhow", "clap", "crossterm 0.29.0", - "futures", "kanash-components", "rand 0.9.2", "rand_core 0.9.3", @@ -812,7 +790,6 @@ dependencies = [ "rascii_art", "ratatui", "tachyonfx", - "tokio", "tui-big-text", "tui-rain", "wana_kana", @@ -823,42 +800,29 @@ name = "kanash-components" version = "0.1.4" dependencies = [ "ansi-to-tui", - "anyhow", - "clap", - "crossterm 0.29.0", - "futures", + "getrandom", "rand 0.9.2", "rand_core 0.9.3", "rand_pcg 0.9.0", "rascii_art", "ratatui", - "tachyonfx", - "tokio", - "tui-big-text", + "ratzilla", "tui-rain", "wana_kana", + "web-time", ] [[package]] name = "kanash-ratzilla" version = "0.1.0" dependencies = [ - "ansi-to-tui", - "anyhow", - "clap", - "crossterm 0.29.0", - "futures", "kanash-components", - "rand 0.9.2", - "rand_core 0.9.3", - "rand_pcg 0.9.0", - "rascii_art", - "ratatui", + "ratzilla", "tachyonfx", - "tokio", "tui-big-text", - "tui-rain", - "wana_kana", + "wasm-bindgen", + "web-sys", + "web-time", ] [[package]] @@ -965,6 +929,12 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + [[package]] name = "num-traits" version = "0.2.19" @@ -974,6 +944,21 @@ dependencies = [ "autocfg", ] +[[package]] +name = "num_threads" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9" +dependencies = [ + "libc", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + [[package]] name = "once_cell_polyfill" version = "1.70.2" @@ -1009,18 +994,6 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" -[[package]] -name = "pin-project-lite" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" - -[[package]] -name = "pin-utils" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" - [[package]] name = "png" version = "0.17.16" @@ -1034,6 +1007,12 @@ dependencies = [ "miniz_oxide", ] +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + [[package]] name = "ppv-lite86" version = "0.2.21" @@ -1073,9 +1052,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.41" +version = "1.0.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1" +checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" dependencies = [ "proc-macro2", ] @@ -1086,6 +1065,12 @@ version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + [[package]] name = "rand" version = "0.8.5" @@ -1176,11 +1161,27 @@ dependencies = [ "lru", "paste", "strum", + "time", "unicode-segmentation", "unicode-truncate", "unicode-width 0.2.0", ] +[[package]] +name = "ratzilla" +version = "0.2.0" +source = "git+https://github.com/benoitlx/ratzilla.git#c46f010384a19a949078a8934d213875dca4e394" +dependencies = [ + "beamterm-renderer", + "bitvec", + "compact_str 0.9.0", + "console_error_panic_hook", + "ratatui", + "thiserror 2.0.17", + "unicode-width 0.2.0", + "web-sys", +] + [[package]] name = "rayon" version = "1.11.0" @@ -1254,6 +1255,35 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "signal-hook" version = "0.3.18" @@ -1277,9 +1307,9 @@ dependencies = [ [[package]] name = "signal-hook-registry" -version = "1.4.6" +version = "1.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b" +checksum = "7664a098b8e616bdfcc2dc0e9ac44eb231eedf41db4e9fe95d8d32ec728dedad" dependencies = [ "libc", ] @@ -1296,28 +1326,12 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" -[[package]] -name = "slab" -version = "0.4.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" - [[package]] name = "smallvec" version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" -[[package]] -name = "socket2" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17129e116933cf371d018bb80ae557e889637989d8638274fb25622827b03881" -dependencies = [ - "libc", - "windows-sys 0.60.2", -] - [[package]] name = "static_assertions" version = "1.1.0" @@ -1354,9 +1368,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.108" +version = "2.0.111" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da58917d35242480a05c2897064da0a80589a2a0476c9a3f2fdc83b53502e917" +checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87" dependencies = [ "proc-macro2", "quote", @@ -1375,8 +1389,15 @@ dependencies = [ "ratatui", "thiserror 2.0.17", "unicode-width 0.2.0", + "web-time", ] +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + [[package]] name = "thiserror" version = "1.0.69" @@ -1429,32 +1450,25 @@ dependencies = [ ] [[package]] -name = "tokio" -version = "1.48.0" +name = "time" +version = "0.3.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408" +checksum = "91e7d9e3bb61134e77bde20dd4825b97c010155709965fedf0f49bb138e52a9d" dependencies = [ - "bytes", + "deranged", "libc", - "mio", - "parking_lot", - "pin-project-lite", - "signal-hook-registry", - "socket2", - "tokio-macros", - "windows-sys 0.61.2", + "num-conv", + "num_threads", + "powerfmt", + "serde", + "time-core", ] [[package]] -name = "tokio-macros" -version = "2.6.0" +name = "time-core" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] +checksum = "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b" [[package]] name = "tui-big-text" @@ -1546,11 +1560,89 @@ dependencies = [ "wit-bindgen", ] +[[package]] +name = "wasm-bindgen" +version = "0.2.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d759f433fa64a2d763d1340820e46e111a7a5ab75f993d1852d70b03dbb80fd" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "836d9622d604feee9e5de25ac10e3ea5f2d65b41eac0d9ce72eb5deae707ce7c" +dependencies = [ + "cfg-if", + "js-sys", + "once_cell", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48cb0d2638f8baedbc542ed444afc0644a29166f1595371af4fecf8ce1e7eeb3" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cefb59d5cd5f92d9dcf80e4683949f15ca4b511f4ac0a6e14d4e1ac60c6ecd40" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbc538057e648b67f72a982e708d485b2efa771e1ac05fec311f9f63e5800db4" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "web-sys" +version = "0.3.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b32828d774c412041098d182a8b38b16ea816958e07cf40eec2bc080ae137ac" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "weezl" -version = "0.1.10" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a751b3277700db47d3e574514de2eced5e54dc8a5436a3bf7a0b248b2cee16f3" +checksum = "a28ac98ddc8b9274cb41bb4d9d4d5c425b6020c50c46f25559911905610b4a88" [[package]] name = "winapi" @@ -1586,16 +1678,7 @@ version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-sys" -version = "0.60.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" -dependencies = [ - "windows-targets 0.53.5", + "windows-targets", ] [[package]] @@ -1613,31 +1696,14 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm 0.52.6", - "windows_aarch64_msvc 0.52.6", - "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm 0.52.6", - "windows_i686_msvc 0.52.6", - "windows_x86_64_gnu 0.52.6", - "windows_x86_64_gnullvm 0.52.6", - "windows_x86_64_msvc 0.52.6", -] - -[[package]] -name = "windows-targets" -version = "0.53.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" -dependencies = [ - "windows-link", - "windows_aarch64_gnullvm 0.53.1", - "windows_aarch64_msvc 0.53.1", - "windows_i686_gnu 0.53.1", - "windows_i686_gnullvm 0.53.1", - "windows_i686_msvc 0.53.1", - "windows_x86_64_gnu 0.53.1", - "windows_x86_64_gnullvm 0.53.1", - "windows_x86_64_msvc 0.53.1", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] [[package]] @@ -1646,116 +1712,77 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" - [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" -[[package]] -name = "windows_aarch64_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" - [[package]] name = "windows_i686_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" -[[package]] -name = "windows_i686_gnu" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" - [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" -[[package]] -name = "windows_i686_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" - [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" -[[package]] -name = "windows_i686_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" - [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" -[[package]] -name = "windows_x86_64_gnu" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" - [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" - [[package]] name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" -[[package]] -name = "windows_x86_64_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" - [[package]] name = "wit-bindgen" version = "0.46.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + [[package]] name = "zerocopy" -version = "0.8.27" +version = "0.8.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c" +checksum = "4ea879c944afe8a2b25fef16bb4ba234f47c694565e97383b36f3a878219065c" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.27" +version = "0.8.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831" +checksum = "cf955aa904d6040f70dc8e9384444cb1030aed272ba3cb09bbc4ab9e7c1f34f5" dependencies = [ "proc-macro2", "quote", diff --git a/justfile b/justfile new file mode 100644 index 0000000..5ff8d65 --- /dev/null +++ b/justfile @@ -0,0 +1,27 @@ +native-crate := "kanash" +wasm-crate := "kanash-ratzilla" + +[private] +default: + @just --list --justfile {{justfile()}} + +# target only native package +[group("build")] +build-native: + cargo build -p {{native-crate}} + +# target only wasm package +[group("build")] +build-wasm: + cargo build --target wasm32-unknown-unknown -p {{wasm-crate}} + +# build all +[group("build")] +build: + @just build-native + @just build-wasm + +# Find TODOs and comments silencing lints +[group('tooling')] +todo: + grep --recursive --extended-regexp --ignore-case --line-number --color=always 'noqa|todo' --exclude-dir target diff --git a/kanash-components/Cargo.toml b/kanash-components/Cargo.toml index b6f1e44..e83b342 100644 --- a/kanash-components/Cargo.toml +++ b/kanash-components/Cargo.toml @@ -3,29 +3,27 @@ name = "kanash-components" version = "0.1.4" authors = ["Benoit Leroux "] edition = "2021" -description = "Learn Hiragana and Katakana in a terminal !" +description = "Core library for kanash and kanash-ratzilla" repository = "https://github.com/benoitlx/kanash" license = "MIT" keywords = ["ratatui", "learning", "japanese", "TUI"] publish = false [dependencies] -anyhow = "1.0.97" rand = "0.9.0" -rand_core = "0.9.3" -ratatui = "0.29.0" wana_kana = "4.0.0" -tachyonfx = "0.20.1" +rand_core = "0.9.3" tui-rain = "1.0.1" -tokio = { version = "1.44.2", features = ["full"] } -futures = "0.3.31" -tui-big-text = "0.7.1" -#ratatui-image = "5.0.0" -#image = "0.25.6" rand_pcg = "0.9.0" -ansi-to-tui = "7.0.0" rascii_art = "0.4.5" -clap = { version = "4.5.53", features = ["derive"] } -[dev-dependencies] -crossterm = "0.29.0" +[target.'cfg(target_arch = "wasm32")'.dependencies] +getrandom = { version = "0.3", features = ["wasm_js"] } +# ratzilla = "0.2.0" +# ratzilla = { path = "../../ratzilla/" } +ratzilla = { git = "https://github.com/benoitlx/ratzilla.git" } +web-time = "1.1.0" + +[target.'cfg(not(target_arch = "wasm32"))'.dependencies] +ratatui = "0.29.0" +ansi-to-tui = "7.0.0" diff --git a/kanash-components/src/app.rs b/kanash-components/src/app.rs index f13a5f1..affd8b8 100644 --- a/kanash-components/src/app.rs +++ b/kanash-components/src/app.rs @@ -1,8 +1,15 @@ use super::{home::HomeModel, kana::KanaModel, *}; use crate::helper::rain; + +#[cfg(not(target_arch = "wasm32"))] use ansi_to_tui::IntoText; +#[cfg(not(target_arch = "wasm32"))] use rascii_art::{render_to, RenderOptions}; + +#[cfg(not(target_arch = "wasm32"))] use ratatui::text::Text; +#[cfg(target_arch = "wasm32")] +use ratzilla::ratatui::text::Text; #[derive(Debug, PartialEq, Eq)] enum AppPage { @@ -38,10 +45,10 @@ impl Components for App { } } - fn handle_event(&self) -> Option { + fn handle_event(&self, event: &PlatformKeyEvent) -> Option { match &self.page { - AppPage::Home(h) => h.handle_event(), - AppPage::Kana(k) => k.handle_event(), + AppPage::Home(h) => h.handle_event(event), + AppPage::Kana(k) => k.handle_event(event), } } @@ -119,12 +126,15 @@ impl Components for App { } fn view(&mut self, frame: &mut Frame, elapsed: Duration) { + #[cfg(not(target_arch = "wasm32"))] if !self.disable_background { self.background(frame); } + if !self.disable_rain { rain::view(frame, elapsed); } + match &mut self.page { AppPage::Home(ref mut h) => h.view(frame, elapsed), AppPage::Kana(ref mut k) => k.view(frame, elapsed), @@ -132,6 +142,7 @@ impl Components for App { } } +#[cfg(not(target_arch = "wasm32"))] impl App { fn background(&mut self, frame: &mut Frame) { let actual_height = frame.area().height; diff --git a/kanash-components/src/helper/background.rs b/kanash-components/src/helper/background.rs deleted file mode 100644 index 0bae4d3..0000000 --- a/kanash-components/src/helper/background.rs +++ /dev/null @@ -1,667 +0,0 @@ -#![allow(dead_code)] - -/// From https://github.com/Levilutz/tui-rain/blob/main/src/lib.rs -/// Thanks to Levi Lutz -use std::{cmp::Ordering, time::Duration}; - -use rand::{RngCore, SeedableRng}; -use rand_pcg::Pcg64Mcg; -use ratatui::{ - buffer::Buffer, - layout::Rect, - style::{Color, Style, Stylize}, - widgets::Widget, -}; - -/// A configuration for the density of the rain effect. -#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)] -pub enum RainDensity { - /// An absolute target number of drops to have in the frame. - Absolute { num_drops: usize }, - - /// Compute the number of drops based on the frame size. Lower value is denser. - /// - /// Is converted to an absolute value, with 1 drop per `sparseness` pixels. - Relative { sparseness: usize }, - - /// A dense rain. Equivalent to `Relative { sparseness: 20 }`. - Dense, - - /// A normal rain. Equivalent to `Relative { sparseness: 50 }`. - Normal, - - /// A sparse rain. Equivalent to `Relative { sparseness: 100 }`. - Sparse, -} - -impl RainDensity { - /// Get the absolute number of drops given an area. - fn num_drops(&self, area: Rect) -> usize { - match self { - RainDensity::Absolute { num_drops } => *num_drops, - RainDensity::Relative { sparseness } if *sparseness == 0 => 0, - RainDensity::Relative { sparseness } => { - (area.width * area.height) as usize / *sparseness - } - RainDensity::Dense => RainDensity::Relative { sparseness: 20 }.num_drops(area), - RainDensity::Normal => RainDensity::Relative { sparseness: 50 }.num_drops(area), - RainDensity::Sparse => RainDensity::Relative { sparseness: 100 }.num_drops(area), - } - } -} - -/// The speed of the rain. -#[derive(Copy, Clone, PartialEq, PartialOrd, Debug)] -pub enum RainSpeed { - /// An absolute target speed in pixels / second. - Absolute { speed: f64 }, - - /// A fast rain. Equivalent to `Absolute { speed: 20.0 }`. - Fast, - - /// A normal rain. Equivalent to `Absolute { speed: 10.0 }`. - Normal, - - /// A slow rain. Equivalent to `Absolute { speed: 5.0 }`. - Slow, -} - -impl RainSpeed { - /// Get the absolute speed. - fn speed(&self) -> f64 { - match self { - RainSpeed::Absolute { speed } => *speed, - RainSpeed::Fast => 20.0, - RainSpeed::Normal => 10.0, - RainSpeed::Slow => 5.0, - } - } -} - -/// A character set for the rain. -#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)] -pub enum CharacterSet { - /// An explicit enumeration of character options. This is the least performant. - Explicit { options: Vec }, - - /// A range of unicode values. - UnicodeRange { start: u32, len: u32 }, - - /// Half-width Japanese Kana characters. This is the closest to the original. - /// - /// Equivalent to `CharacterSet::UnicodeRange { start: 0xFF66, len: 56 }`. - HalfKana, - - /// The lowercase English alphabet. - /// - /// Equivalent to `CharacterSet::UnicodeRange { start: 0x61, len: 26 }`. - Lowercase, -} - -impl CharacterSet { - fn get(&self, seed: u32) -> char { - match self { - CharacterSet::Explicit { options } => options[seed as usize % options.len()], - CharacterSet::UnicodeRange { start, len } => { - char::from_u32((seed % len) + start).unwrap() - } - CharacterSet::HalfKana => CharacterSet::UnicodeRange { - start: 0xFF66, - len: 56, - } - .get(seed), - CharacterSet::Lowercase => CharacterSet::UnicodeRange { - start: 0x61, - len: 26, - } - .get(seed), - } - } - - fn size(&self) -> usize { - match self { - CharacterSet::Explicit { options } => options.len(), - CharacterSet::UnicodeRange { start: _, len } => *len as usize, - CharacterSet::HalfKana => 56, - CharacterSet::Lowercase => 26, - } - } -} - -#[derive(Clone, PartialEq, Debug)] -pub struct Rain { - elapsed: Duration, - seed: u64, - rain_density: RainDensity, - rain_speed: RainSpeed, - rain_speed_variance: f64, - tail_lifespan: Duration, - color: Color, - head_color: Color, - bold_dim_effect: bool, - noise_interval: Duration, - character_set: CharacterSet, -} - -impl Rain { - /// Construct a new rain widget with defaults for matrix rain. - pub fn new_matrix(elapsed: Duration) -> Rain { - Rain { - elapsed, - seed: 1234, - rain_density: RainDensity::Normal, - rain_speed: RainSpeed::Slow, - rain_speed_variance: 0.5, - tail_lifespan: Duration::from_secs(2), - color: Color::LightGreen, - head_color: Color::White, - bold_dim_effect: true, - noise_interval: Duration::from_secs(5), - character_set: CharacterSet::HalfKana, - } - } - - /// Construct a new rain widget with defaults for standard rain. - pub fn new_rain(elapsed: Duration) -> Rain { - Rain { - elapsed, - seed: 1234, - rain_density: RainDensity::Dense, - rain_speed: RainSpeed::Fast, - rain_speed_variance: 0.5, - tail_lifespan: Duration::from_millis(250), - color: Color::LightBlue, - head_color: Color::White, - bold_dim_effect: true, - noise_interval: Duration::from_secs(1), - character_set: CharacterSet::UnicodeRange { - start: 0x7c, - len: 1, - }, - } - } - - /// Construct a new rain widget with defaults for snow. - pub fn new_snow(elapsed: Duration) -> Rain { - Rain { - elapsed, - seed: 1234, - rain_density: RainDensity::Dense, - rain_speed: RainSpeed::Absolute { speed: 2.0 }, - rain_speed_variance: 0.1, - tail_lifespan: Duration::from_millis(500), - color: Color::White, - head_color: Color::White, - bold_dim_effect: true, - noise_interval: Duration::from_secs(1), - character_set: CharacterSet::UnicodeRange { - start: 0x2a, - len: 1, - }, - } - } - - /// Construct a new rain widget with defaults for emoji soup. - /// - /// Terminals that render emojis as two characters wide will not enjoy this. - pub fn new_emoji_soup(elapsed: Duration) -> Rain { - Rain { - elapsed, - seed: 1234, - rain_density: RainDensity::Dense, - rain_speed: RainSpeed::Normal, - rain_speed_variance: 0.1, - tail_lifespan: Duration::from_millis(500), - color: Color::White, - head_color: Color::White, - bold_dim_effect: true, - noise_interval: Duration::from_secs(1), - character_set: CharacterSet::UnicodeRange { - start: 0x1f600, - len: 80, - }, - } - } - - /// Set the random seed for the generation. - /// - /// The random seed can be configured. Given a constant screen size, results should - /// be reproducible across executions, operating systems, and architectures. - /// - /// ``` - /// use std::time::Duration; - /// use tui_rain::Rain; - /// - /// let elapsed = Duration::from_secs(5); - /// - /// Rain::new_matrix(elapsed) - /// .with_seed(1234); - /// ``` - pub fn with_seed(mut self, seed: u64) -> Rain { - self.seed = seed; - self - } - - /// Set the target density for the rain. - /// - /// This can be configured as an absolute number of drops: - /// - /// ``` - /// use std::time::Duration; - /// use tui_rain::{Rain, RainDensity}; - /// - /// Rain::new_matrix(Duration::from_secs(0)) - /// .with_rain_density(RainDensity::Absolute { - /// num_drops: 100, - /// }); - /// ``` - /// Or a ratio of screen pixels to drops (lower is more dense): - /// - /// ``` - /// use std::time::Duration; - /// use tui_rain::{Rain, RainDensity}; - /// - /// Rain::new_matrix(Duration::from_secs(0)) - /// .with_rain_density(RainDensity::Relative { - /// sparseness: 50, - /// }); - /// ``` - /// - /// The actual number of drops on the screen at any time is randomly distributed - /// between 0 and twice the target. - /// - /// Preset relative options include: - /// - /// - `RainDensity::Sparse` - /// - `RainDensity::Normal` - /// - `RainDensity::Dense` - pub fn with_rain_density(mut self, rain_density: RainDensity) -> Rain { - self.rain_density = rain_density; - self - } - - /// Set the target speed for the rain. - /// - /// Speed can be configured as an absolute value of pixels per second, or as a - /// preset. - /// - /// For an absolute speed in pixels per second: - /// - /// ``` - /// use std::time::Duration; - /// use tui_rain::{Rain, RainSpeed}; - /// - /// let elapsed = Duration::from_secs(5); - /// - /// Rain::new_matrix(elapsed) - /// .with_rain_speed(RainSpeed::Absolute { - /// speed: 10.0, - /// }); - /// ``` - /// - /// Preset options include: - /// - /// - `RainSpeed::Slow` - /// - `RainSpeed::Normal` - /// - `RainSpeed::Fast` - pub fn with_rain_speed(mut self, rain_speed: RainSpeed) -> Rain { - self.rain_speed = rain_speed; - self - } - - /// Set the rain speed variance. - /// - /// To avoid perfectly consistent patterns, you can configure some variance in the - /// speed of each drop. This can also give an impression of parallax (depth). - /// - /// For example, a value of `0.1` will cause each drop's speed to be uniformly - /// distrbuted within ±10% of the target speed: - /// - /// ``` - /// use std::time::Duration; - /// use tui_rain::Rain; - /// - /// let elapsed = Duration::from_secs(5); - /// - /// Rain::new_matrix(elapsed) - /// .with_rain_speed_variance(0.1); - /// ``` - /// - /// The speed of an individual drop will never go below 0.001 pixels / second, but - /// can vary arbitrarily high. - pub fn with_rain_speed_variance(mut self, rain_speed_variance: f64) -> Rain { - self.rain_speed_variance = rain_speed_variance; - self - } - - /// Set the tail lifespan for the rain. - /// - /// You can make the rain drop tails appear shorter / longer by configuring how long - /// the tail effect lasts: - /// - /// ``` - /// use std::time::Duration; - /// use tui_rain::Rain; - /// - /// let elapsed = Duration::from_secs(5); - /// - /// Rain::new_matrix(elapsed) - /// .with_tail_lifespan(Duration::from_secs(5)); - /// ``` - /// - /// The drop length is capped at the screen height to avoid strange wraparound - /// effects. - pub fn with_tail_lifespan(mut self, tail_lifespan: Duration) -> Rain { - self.tail_lifespan = tail_lifespan; - self - } - - /// Set the color for the rain. - /// - /// You can change the tail color for each drop: - /// - /// ``` - /// use std::time::Duration; - /// use tui_rain::Rain; - /// - /// let elapsed = Duration::from_secs(5); - /// - /// Rain::new_matrix(elapsed) - /// .with_color(ratatui::style::Color::LightGreen); - /// ``` - /// - /// The color of the head is [independently configured](Rain::with_head_color). The - /// bold / dim effects that automatically get applied over a drop's length may tweak - /// the color inadvertently, but [this can be disabled](Rain::with_bold_dim_effect). - pub fn with_color(mut self, color: Color) -> Rain { - self.color = color; - self - } - - /// Set the head color for the rain. - /// - /// You can change the head color for each drop: - /// - /// ``` - /// use std::time::Duration; - /// use tui_rain::Rain; - /// - /// let elapsed = Duration::from_secs(5); - /// - /// Rain::new_matrix(elapsed) - /// .with_head_color(ratatui::style::Color::Green); - /// ``` - /// - /// The color of the tail is [independently configured](Rain::with_color). The - /// bold / dim effects that automatically get applied over a drop's length may tweak - /// the color inadvertently, but [this can be disabled](Rain::with_bold_dim_effect). - pub fn with_head_color(mut self, head_color: Color) -> Rain { - self.head_color = head_color; - self - } - - /// Set whether to apply the bold / dim effect. - /// - /// By default, the lower third of each drop has the bold effect applied, and the - /// upper third has the dim effect applied. This produces an impression of the drop - /// fading instead of abruptly ending. - /// - /// This may tweak the color of glyphs away from the base color on some terminals, - /// so it can be disabled if desired: - /// - /// ``` - /// use std::time::Duration; - /// use tui_rain::Rain; - /// - /// let elapsed = Duration::from_secs(5); - /// - /// Rain::new_matrix(elapsed) - /// .with_bold_dim_effect(false); - ///``` - pub fn with_bold_dim_effect(mut self, bold_dim_effect: bool) -> Rain { - self.bold_dim_effect = bold_dim_effect; - self - } - - /// Set the interval between random character changes. - /// - /// A more subtle effect is that glyphs already rendered in a drop occasionally - /// switch characters before dissapearing. The time interval between each character - /// switch is per-glyph, and can be adjusted: - /// - /// ``` - /// use std::time::Duration; - /// use tui_rain::Rain; - /// - /// let elapsed = Duration::from_secs(5); - /// - /// Rain::new_matrix(elapsed) - /// .with_noise_interval(Duration::from_secs(10)); - /// ``` - pub fn with_noise_interval(mut self, noise_interval: Duration) -> Rain { - self.noise_interval = noise_interval; - self - } - - /// Set the character set for the drops. - /// - /// The simplest option is to provide an explicit set of characters to choose from: - /// - /// ``` - /// use std::time::Duration; - /// use tui_rain::{CharacterSet, Rain}; - /// - /// let elapsed = Duration::from_secs(5); - /// - /// Rain::new_matrix(elapsed) - /// .with_character_set(CharacterSet::Explicit { - /// options: vec!['a', 'b', 'c'], - /// }); - /// ``` - /// - /// More performant is to provide a unicode range: - /// - /// ``` - /// use std::time::Duration; - /// use tui_rain::{CharacterSet, Rain}; - /// - /// let elapsed = Duration::from_secs(5); - /// - /// Rain::new_matrix(elapsed) - /// .with_character_set(CharacterSet::UnicodeRange { - /// start: 0x61, - /// len: 26, - /// }); - /// ``` - /// - /// Preset unicode ranges include: - /// - /// - `CharacterSet::HalfKana` is the half-width Japanese kana character set (used - /// in the classic matrix rain) - /// - `CharacterSet::Lowercase` is the lowercase English character set - pub fn with_character_set(mut self, character_set: CharacterSet) -> Rain { - self.character_set = character_set; - self - } - - /// Build the rng. Uses a fast but portable and reproducible rng. - fn build_rng(&self) -> impl RngCore { - Pcg64Mcg::seed_from_u64(self.seed) - } - - /// Build a drop from the given consistent initial entropy state. - /// - /// The entropy vector's length becomes the drop's track length, so ensure it's at - /// least the window height. - fn build_drop(&self, entropy: Vec, width: u16, height: u16) -> Vec { - let elapsed = self.elapsed.as_secs_f64(); - let rain_speed = self.rain_speed.speed(); - let tail_lifespan = self.tail_lifespan.as_secs_f64(); - let noise_interval = self.noise_interval.as_secs_f64(); - - // A single drop can expect to be called with the exact same entropy vec on each - // frame. This means we can sample the entropy vec to reproducibly generate - // features every frame (e.g. speed). - - // Later code assumes at least 1 entry in the entropy vec, so break early if not. - if entropy.is_empty() { - return vec![]; - } - - // The length of the entropy vec becomes the length of the drop's track. - // This track is usually longer than the screen height by a random amount. - let track_len = entropy.len() as u16; - - // Use some entropy to compute the drop's actual speed. - // n.b. since the entropy vec is stable, the drop's speed will not vary over time. - let rain_speed = uniform( - entropy[0], - rain_speed * (1.0 - self.rain_speed_variance), - rain_speed * (1.0 + self.rain_speed_variance), - ) - .max(1e-3); // Prevent speed from hitting 0 (if user specifies high variance) - - // Compute how long our drop will take to make 1 cycle given our track len and speed - let cycle_time_secs = entropy.len() as f64 / rain_speed; - - // Use some entropy to compute a stable random time offset for this drop. - // If this value were 0, every drop would start falling with an identical y value. - let initial_cycle_offset_secs = uniform(entropy[0], 0.0, cycle_time_secs); - - // Compute how far we are into the current cycle and current drop head height. - let current_cycle_offset_secs = (elapsed + initial_cycle_offset_secs) % cycle_time_secs; - let head_y = (current_cycle_offset_secs * rain_speed) as u16; - - // Compute drop length given speed and tail lifespan. - // Cap at screen height to avoid weird wraparound when tail length is long. - let drop_len = ((rain_speed * tail_lifespan) as u16).min(height); - - // Render each glyph in the drop. - (0..drop_len) - .filter_map(|y_offset| { - // Compute how long ago this glyph would have first appeared - let age = y_offset as f64 / rain_speed; - - // If it would have first appeared before the rendering began, don't render. - if age > elapsed { - return None; - } - - // Compute which cycle this particular glyph is a member of - let cycle_num = - ((elapsed + initial_cycle_offset_secs - age) / cycle_time_secs) as usize; - - // Don't render glyphs from cycle 0 - // (prevents drops from appearing to spawn in the middle of the screen) - if cycle_num == 0 { - return None; - } - - // Get stable entropy to decide what column cycle X is rendered in. - // This must be per-glyph to prevent drops from jumping side-to-side when they wrap around. - let x_entropy = entropy[cycle_num % entropy.len()]; - let x = (x_entropy % width as u64) as u16; - - // Compute the y value for this glyph, and don't render if off the screen. - let y = (head_y + track_len - y_offset) % track_len; - if y >= height { - return None; - } - - // The 'noise' of glyphs randomly changing is actually modeled as every glyph in the track - // just cycling through possible values veeeery slowly. We need a random offset for this - // cycling so every glyph doesn't change at the same time. - let time_offset = uniform( - entropy[y as usize], - 0.0, - noise_interval * self.character_set.size() as f64, - ); - - // Decide what character is rendered based on noise. - let content = self - .character_set - .get(((time_offset + elapsed) / noise_interval) as u32); - - // Compute the styling for the glyph - let mut style = Style::default(); - - // Color appropriately depending on whether this glyph is the head. - if age > 0.0 { - style = style.fg(self.color) - } else { - style = style.fg(self.head_color) - } - - // The lowest third of glyphs is bold, the highest third is dim - if self.bold_dim_effect { - if y_offset < drop_len / 3 { - style = style.bold().not_dim() - } else if y_offset > drop_len * 2 / 3 { - style = style.dim().not_bold() - } else { - style = style.not_bold().not_dim() - } - } - - Some(Glyph { - x, - y, - age, - content, - style, - }) - }) - .collect() - } -} - -impl Widget for Rain { - fn render(self, area: Rect, buf: &mut Buffer) { - let mut rng = self.build_rng(); - - // We don't actually have n drops with tracks equal to the screen height. - // We actually have 2n drops with tracks ranging from 1.5 to 2.5 the screen height. - // This introduces more randomness to the apparent n and reduces cyclic appearance. - let num_drops = self.rain_density.num_drops(area) * 2; - let drop_track_lens: Vec = (0..num_drops) - .map(|_| (area.height as u64 * 3 / 2 + rng.next_u64() % area.height as u64) as usize) - .collect(); - - // We construct entropy consistently every frame to mimic statefulness. - // This is not a performance bottleneck, so caching wouldn't deliver much benefit. - let entropy: Vec> = drop_track_lens - .iter() - .map(|track_len| (0..*track_len).map(|_| rng.next_u64()).collect()) - .collect(); - - // For every entropy vec, construct a single drop (vertical line of glyphs). - let mut glyphs: Vec = entropy - .into_iter() - .flat_map(|drop_entropy| self.build_drop(drop_entropy, area.width, area.height)) - .collect(); - - // Sort all the glyphs by age so drop heads always render on top. - // This is a moderate bottleneck when the screen is large / there's a lot of glyphs. - glyphs.sort_by(|a, b| a.age.partial_cmp(&b.age).unwrap_or(Ordering::Equal)); - - // Actually render to the buffer. - for glyph in glyphs { - buf[(glyph.x, glyph.y)].set_char(glyph.content); - buf[(glyph.x, glyph.y)].set_style(glyph.style); - } - } -} - -/// A Glyph to be rendered on the screen. -struct Glyph { - x: u16, - y: u16, - age: f64, - content: char, - style: Style, -} - -/// Map a uniform random u64 to a uniform random f64 in the range [lower, upper). -fn uniform(seed: u64, lower: f64, upper: f64) -> f64 { - (seed as f64 / u64::MAX as f64) * (upper - lower) + lower -} diff --git a/kanash-components/src/helper/ja.rs b/kanash-components/src/helper/ja.rs index cdd8f70..77455dd 100644 --- a/kanash-components/src/helper/ja.rs +++ b/kanash-components/src/helper/ja.rs @@ -2,7 +2,7 @@ use rand::{seq::IndexedRandom, Rng}; use rand_pcg::Mcg128Xsl64; const HIRAGANA_NUMBER: usize = 71; -const WANTED_KANA: [u16; HIRAGANA_NUMBER] = [ +const WANTED_HIRAGANA: [u16; HIRAGANA_NUMBER] = [ 12354, 12356, 12358, 12360, 12362, 12363, 12364, 12365, 12366, 12367, 12368, 12369, 12370, 12371, 12372, 12373, 12374, 12375, 12376, 12377, 12378, 12379, 12380, 12381, 12382, 12383, 12384, 12385, 12386, 12388, 12389, 12390, 12391, 12392, 12393, 12394, 12395, 12396, 12397, @@ -22,7 +22,7 @@ const WANTED_KATAKANA: [u16; KATAKANA_NUMBER] = [ ]; pub fn random_hiragana(rng: &mut Mcg128Xsl64) -> String { - String::from_utf16(&[*WANTED_KANA.choose(rng).unwrap()]).expect("error hiragana") + String::from_utf16(&[*WANTED_HIRAGANA.choose(rng).unwrap()]).expect("error hiragana") } pub fn random_katakana(rng: &mut Mcg128Xsl64) -> String { diff --git a/kanash-components/src/helper/mod.rs b/kanash-components/src/helper/mod.rs index a51f5f7..2035d94 100644 --- a/kanash-components/src/helper/mod.rs +++ b/kanash-components/src/helper/mod.rs @@ -1,4 +1,2 @@ -pub mod background; -// pub mod image; pub mod ja; pub mod rain; diff --git a/kanash-components/src/helper/rain.rs b/kanash-components/src/helper/rain.rs index aba9557..8d9bf7a 100644 --- a/kanash-components/src/helper/rain.rs +++ b/kanash-components/src/helper/rain.rs @@ -1,7 +1,16 @@ +#[cfg(not(target_arch = "wasm32"))] use ratatui::{style::Color, Frame}; + +#[cfg(not(target_arch = "wasm32"))] use std::time::Duration; -use super::background::{CharacterSet, Rain, RainDensity, RainSpeed}; +#[cfg(target_arch = "wasm32")] +use ratzilla::ratatui::{style::Color, Frame}; + +#[cfg(target_arch = "wasm32")] +use web_time::Duration; + +use tui_rain::{CharacterSet, Rain, RainDensity, RainSpeed}; const TAIL_COLOR: Color = Color::from_u32(0x0008dbbe); @@ -17,7 +26,7 @@ pub fn view(frame: &mut Frame, elapsed: Duration) { .clone() .with_character_set(CharacterSet::HalfKana) .with_tail_lifespan(Duration::from_millis(120)) - .with_color(ratatui::style::Color::LightGreen); + .with_color(Color::LightGreen); frame.render_widget(rain, frame.area()); frame.render_widget(kana_tail, frame.area()); diff --git a/kanash-components/src/home.rs b/kanash-components/src/home.rs index c9280a4..cb7c38c 100644 --- a/kanash-components/src/home.rs +++ b/kanash-components/src/home.rs @@ -9,7 +9,7 @@ const SELECTED_STYLE: Style = Style::new() .bg(ColorPalette::SELECTION) .add_modifier(Modifier::BOLD); -#[derive(Debug, PartialEq, Eq, Clone)] +#[derive(Debug, PartialEq, Eq, Clone, Copy)] pub enum Mode { Hira, Kata, @@ -30,7 +30,7 @@ pub struct HomeModel { pub key_helper_state: BackgroundMode, } -#[derive(Debug, PartialEq, Eq, Clone)] +#[derive(Debug, PartialEq, Eq, Clone, Copy)] pub enum HomeMessage { /// Launch a Page Enter(Mode), @@ -60,34 +60,26 @@ impl Components for HomeModel { } /// Handle Event (Mostly convert key event to message) - fn handle_event(&self) -> Option { - if event::poll(Duration::from_millis(10)).unwrap() { - if let Event::Key(key) = event::read().unwrap() { - match key.code { - KeyCode::Esc => Some(Message::Back), - KeyCode::Enter => { - if let Some(i) = self.state.selected() { - match i { - 0 => Some(Message::Home(HomeMessage::Enter(Mode::Hira))), - 1 => Some(Message::Home(HomeMessage::Enter(Mode::Kata))), - 2 => Some(Message::Home(HomeMessage::Enter(Mode::Both))), - _ => None, - } - } else { - None - } + fn handle_event(&self, event: &PlatformKeyEvent) -> Option { + match event.code { + KeyCode::Esc => Some(Message::Back), + KeyCode::Enter => { + if let Some(i) = self.state.selected() { + match i { + 0 => Some(Message::Home(HomeMessage::Enter(Mode::Hira))), + 1 => Some(Message::Home(HomeMessage::Enter(Mode::Kata))), + 2 => Some(Message::Home(HomeMessage::Enter(Mode::Both))), + _ => None, } - KeyCode::Char('j') | KeyCode::Down => Some(Message::Home(HomeMessage::Down)), - KeyCode::Char('k') | KeyCode::Up => Some(Message::Home(HomeMessage::Up)), - KeyCode::Char('x') => Some(Message::Home(HomeMessage::RainFx)), - KeyCode::Char('b') => Some(Message::Home(HomeMessage::Background)), - _ => None, + } else { + None } - } else { - None } - } else { - None + KeyCode::Char('j') | KeyCode::Down => Some(Message::Home(HomeMessage::Down)), + KeyCode::Char('k') | KeyCode::Up => Some(Message::Home(HomeMessage::Up)), + KeyCode::Char('x') => Some(Message::Home(HomeMessage::RainFx)), + KeyCode::Char('b') => Some(Message::Home(HomeMessage::Background)), + _ => None, } } diff --git a/kanash-components/src/kana.rs b/kanash-components/src/kana.rs index 3596e23..2b37a12 100644 --- a/kanash-components/src/kana.rs +++ b/kanash-components/src/kana.rs @@ -1,5 +1,3 @@ -use std::time::UNIX_EPOCH; - // use crate::components::helper::image; use crate::helper::ja::random_kana; use rand::SeedableRng; @@ -27,7 +25,7 @@ pub struct KanaModel { pub mode: Mode, } -#[derive(Debug, PartialEq, Eq, Clone)] +#[derive(Debug, PartialEq, Eq, Clone, Copy)] pub enum KanaMessage { /// reveal the answer Answer, @@ -46,8 +44,14 @@ impl Components for KanaModel { /// Create a new kana model fn new() -> Self { let mut r = Pcg64Mcg::seed_from_u64( + #[cfg(not(target_arch = "wasm32"))] std::time::SystemTime::now() - .duration_since(UNIX_EPOCH) + .duration_since(std::time::SystemTime::UNIX_EPOCH) + .unwrap() + .as_secs(), + #[cfg(target_arch = "wasm32")] + web_time::SystemTime::now() + .duration_since(web_time::SystemTime::UNIX_EPOCH) .unwrap() .as_secs(), ); @@ -63,21 +67,13 @@ impl Components for KanaModel { } /// Handle Event (Mostly convert key event to message) - fn handle_event(&self) -> Option { - if event::poll(Duration::from_millis(10)).unwrap() { - if let Event::Key(key) = event::read().unwrap() { - match key.code { - KeyCode::Esc => Some(Message::Back), - KeyCode::Backspace => Some(Message::Kana(KanaMessage::DeleteRoma)), - KeyCode::Char(' ') => Some(Message::Kana(KanaMessage::Answer)), - KeyCode::Char(c) => Some(Message::Kana(KanaMessage::TypingRoma(c))), - _ => None, - } - } else { - None - } - } else { - None + fn handle_event(&self, key_event: &PlatformKeyEvent) -> Option { + match key_event.code { + KeyCode::Esc => Some(Message::Back), + KeyCode::Backspace => Some(Message::Kana(KanaMessage::DeleteRoma)), + KeyCode::Char(' ') => Some(Message::Kana(KanaMessage::Answer)), + KeyCode::Char(c) => Some(Message::Kana(KanaMessage::TypingRoma(c))), + _ => None, } } diff --git a/kanash-components/src/lib.rs b/kanash-components/src/lib.rs index 5f1e8d3..1f18603 100644 --- a/kanash-components/src/lib.rs +++ b/kanash-components/src/lib.rs @@ -3,6 +3,7 @@ pub mod helper; pub mod home; pub mod kana; +#[cfg(not(target_arch = "wasm32"))] pub use ratatui::{ crossterm::event::{self, Event, KeyCode}, layout::{Constraint, Layout}, @@ -14,6 +15,26 @@ pub use ratatui::{ Frame, }; +#[cfg(not(target_arch = "wasm32"))] +pub type PlatformKeyEvent = ratatui::crossterm::event::KeyEvent; + +#[cfg(target_arch = "wasm32")] +pub use ratzilla::ratatui::{ + layout::{Constraint, Layout}, + style::Stylize, + style::{palette::tailwind::SLATE, Color, Modifier, Style}, + text::Line, + widgets::{Block, Paragraph}, + widgets::{BorderType, Borders, Clear, HighlightSpacing, List, ListItem, ListState, Padding}, + Frame, +}; + +#[cfg(target_arch = "wasm32")] +pub type PlatformKeyEvent = ratzilla::event::KeyEvent; + +#[cfg(target_arch = "wasm32")] +pub use ratzilla::{event::KeyCode, web_sys::console}; + use std::time::Duration; use home::{BackgroundMode, HomeMessage, Mode}; @@ -31,7 +52,7 @@ impl ColorPalette { // #99ff33 } -#[derive(Debug, PartialEq, Eq, Clone)] +#[derive(Debug, PartialEq, Eq, Copy, Clone)] pub enum Message { /// Go to the previous page or quit the app Back, @@ -43,7 +64,7 @@ pub enum Message { pub trait Components { fn new() -> Self; - fn handle_event(&self) -> Option; + fn handle_event(&self, event: &PlatformKeyEvent) -> Option; fn update(&mut self, msg: Message) -> Option; diff --git a/kanash-ratzilla/.cargo/config.toml b/kanash-ratzilla/.cargo/config.toml new file mode 100644 index 0000000..f4e8c00 --- /dev/null +++ b/kanash-ratzilla/.cargo/config.toml @@ -0,0 +1,2 @@ +[build] +target = "wasm32-unknown-unknown" diff --git a/kanash-ratzilla/.gitignore b/kanash-ratzilla/.gitignore new file mode 100644 index 0000000..849ddff --- /dev/null +++ b/kanash-ratzilla/.gitignore @@ -0,0 +1 @@ +dist/ diff --git a/kanash-ratzilla/Cargo.toml b/kanash-ratzilla/Cargo.toml index b315a45..3dc83a1 100644 --- a/kanash-ratzilla/Cargo.toml +++ b/kanash-ratzilla/Cargo.toml @@ -3,30 +3,21 @@ name = "kanash-ratzilla" version = "0.1.0" authors = ["Benoit Leroux "] edition = "2021" -description = "Learn Hiragana and Katakana in a terminal !" +description = "Learn Hiragana and Katakana in a browser with terminal aesthetic!" repository = "https://github.com/benoitlx/kanash" license = "MIT" keywords = ["ratatui", "learning", "japanese", "TUI"] publish = false [dependencies] -kanash-components = { path = "../kanash-components/", version = "0.1.4" } -anyhow = "1.0.97" -rand = "0.9.0" -rand_core = "0.9.3" -ratatui = "0.29.0" -wana_kana = "4.0.0" -tachyonfx = "0.20.1" -tui-rain = "1.0.1" -tokio = { version = "1.44.2", features = ["full"] } -futures = "0.3.31" -tui-big-text = "0.7.1" -#ratatui-image = "5.0.0" -#image = "0.25.6" -rand_pcg = "0.9.0" -ansi-to-tui = "7.0.0" -rascii_art = "0.4.5" -clap = { version = "4.5.53", features = ["derive"] } +kanash-components = { path = "../kanash-components/" } +# ratzilla = "0.2.0" +# ratzilla = { path = "../../ratzilla/" } +ratzilla = { git = "https://github.com/benoitlx/ratzilla.git" } +tachyonfx = { version = "0.20.1", default-features = false, features = ["wasm"] } +tui-big-text = "0.7.3" +wasm-bindgen = "0.2.105" +web-time = "1.1.0" [dev-dependencies] -crossterm = "0.29.0" +web-sys = "0.3.82" diff --git a/kanash-ratzilla/bitmap_font.atlas b/kanash-ratzilla/bitmap_font.atlas new file mode 100644 index 0000000..11dc551 Binary files /dev/null and b/kanash-ratzilla/bitmap_font.atlas differ diff --git a/kanash-ratzilla/build.rs b/kanash-ratzilla/build.rs new file mode 100644 index 0000000..bd3f3ea --- /dev/null +++ b/kanash-ratzilla/build.rs @@ -0,0 +1,6 @@ +fn main() { + let target = std::env::var("TARGET").unwrap_or_default(); + if !target.starts_with("wasm32") { + panic!("kanash-ratzilla must be built for a wasm32 target (e.g. wasm32-unknown-unknown)."); + } +} diff --git a/kanash-ratzilla/index.html b/kanash-ratzilla/index.html new file mode 100644 index 0000000..1265bf8 --- /dev/null +++ b/kanash-ratzilla/index.html @@ -0,0 +1,43 @@ + + + + + + + + Ratzilla + + + + + + + diff --git a/kanash-ratzilla/src/main.rs b/kanash-ratzilla/src/main.rs index e7a11a9..68c1588 100644 --- a/kanash-ratzilla/src/main.rs +++ b/kanash-ratzilla/src/main.rs @@ -1,3 +1,98 @@ -fn main() { - println!("Hello, world!"); +use kanash_components::{app::App, ColorPalette, Components}; + +use ratzilla::backend::webgl2::{FontAtlasData, WebGl2BackendOptions}; +use ratzilla::ratatui::{ + layout::{Constraint, Layout}, + style::Style, + text::Line, + Terminal, +}; +use ratzilla::{CanvasBackend, DomBackend, WebGl2Backend, WebRenderer}; + +use tachyonfx::{fx, EffectRenderer}; +use tui_big_text::{BigText, PixelSize}; + +use std::io; +use web_time::Instant; + +use std::{cell::RefCell, rc::Rc}; + +type Outbox = Rc>>; + +static ATLAS_BYTES: &[u8] = include_bytes!("../bitmap_font.atlas"); + +fn main() -> io::Result<()> { + // let webgl2_options = WebGl2BackendOptions::new() + // .fallback_glyph("@") + // .font_atlas(FontAtlasData::from_binary(ATLAS_BYTES).unwrap()); + // let backend = WebGl2Backend::new_with_options(webgl2_options)?; + let backend = DomBackend::new()?; + // let backend = CanvasBackend::new()?; + let terminal = Terminal::new(backend)?; + + let mut render_app = App::new(); + let mut event_app = App::new(); + let start_time = Instant::now(); + + let mut fade_effect = fx::dissolve(2000); //(20000, Interpolation::QuadOut)); + + let outbox: Outbox = Rc::new(RefCell::new(None)); + let outbox_event = Rc::clone(&outbox); + + terminal.on_key_event({ + move |key_event| { + let current_msg = event_app.handle_event(&key_event); + + if let Some(msg) = current_msg { + *outbox_event.borrow_mut() = Some(msg); + event_app.update(current_msg.unwrap()); + } + } + }); + + terminal.draw_web(move |f| { + let p = BigText::builder() + .pixel_size(PixelSize::HalfHeight) + .lines(vec![">> Kana SH <<".into()]) + .centered() + .style(Style::fg(Style::new(), ColorPalette::TITLE)) + .build(); + + let credit = Line::from("@benoitlx") + .centered() + .style(Style::fg(Style::new(), ColorPalette::SUBTITLE)); + + let [_, area, _, bottom] = Layout::vertical([ + Constraint::Min(0), + Constraint::Length(4), + Constraint::Min(0), + Constraint::Length(1), + ]) + .areas(f.area()); + + let [_, creds_area, _] = Layout::horizontal([ + Constraint::Min(0), + Constraint::Percentage(10), + Constraint::Min(0), + ]) + .areas(bottom); + + if start_time.elapsed() < web_time::Duration::from_millis(1000) { + f.render_widget(p, area); + f.render_widget(credit, creds_area); + } else if start_time.elapsed() < web_time::Duration::from_millis(1800) { + f.render_widget(p, area); + f.render_widget(credit, creds_area); + f.render_effect(&mut fade_effect, area, tachyonfx::Duration::from_millis(33)); + } else { + render_app.view(f, start_time.elapsed()); + + let mut current_msg = outbox.borrow_mut().take(); + while current_msg.is_some() { + current_msg = render_app.update(current_msg.unwrap()); + } + } + }); + + Ok(()) } diff --git a/kanash/Cargo.toml b/kanash/Cargo.toml index b3af2b1..c6a7b04 100644 --- a/kanash/Cargo.toml +++ b/kanash/Cargo.toml @@ -10,15 +10,12 @@ keywords = ["ratatui", "learning", "japanese", "TUI"] [dependencies] kanash-components = { path = "../kanash-components/", version = "0.1.4" } -anyhow = "1.0.97" rand = "0.9.0" rand_core = "0.9.3" ratatui = "0.29.0" wana_kana = "4.0.0" tachyonfx = "0.20.1" tui-rain = "1.0.1" -tokio = { version = "1.44.2", features = ["full"] } -futures = "0.3.31" tui-big-text = "0.7.1" #ratatui-image = "5.0.0" #image = "0.25.6" diff --git a/kanash/src/main.rs b/kanash/src/main.rs index a045e00..cc0e14a 100644 --- a/kanash/src/main.rs +++ b/kanash/src/main.rs @@ -1,6 +1,7 @@ use kanash_components::app::App; use kanash_components::{ColorPalette, Components}; +use ratatui::crossterm::event::{self, *}; use ratatui::layout::{Constraint, Layout}; use ratatui::text::Line; use ratatui::{restore, style::Style}; @@ -24,19 +25,10 @@ fn main() { let mut terminal = ratatui::init(); - let mut app = App::new(); - if let Some(path) = arg.path { - let assets = std::fs::read_dir(path) - .unwrap() - .map(|entry| entry.unwrap().path().to_str().unwrap().to_string()) - .collect::>(); - app.background_paths = assets; - } + let mut fade_effect = fx::dissolve((20000, Interpolation::QuadOut)); let start_time = Instant::now(); - let mut fade_effect = fx::dissolve((20000, Interpolation::QuadOut)); - // Splash Screen Rendering while start_time.elapsed() < Duration::from_millis(2000) { // let p = Paragraph::new(Line::from("~ Kana SH ~")).centered(); @@ -76,11 +68,25 @@ fn main() { }); } + let mut app = App::new(); + if let Some(path) = arg.path { + let assets = std::fs::read_dir(path) + .unwrap() + .map(|entry| entry.unwrap().path().to_str().unwrap().to_string()) + .collect::>(); + app.background_paths = assets; + } + // Main app rendering while !app.exit { let _ = terminal.draw(|frame| app.view(frame, start_time.elapsed())); - let mut current_msg = app.handle_event(); + let mut current_msg = None; + if event::poll(Duration::from_millis(10)).unwrap() { + if let Event::Key(key) = event::read().unwrap() { + current_msg = app.handle_event(&key); + } + } while current_msg.is_some() { current_msg = app.update(current_msg.unwrap());