From d0c29687c13821a6a9e8be819a3e70a3fc2e1c74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Orhun=20Parmaks=C4=B1z?= Date: Mon, 16 Dec 2024 23:06:24 +0300 Subject: [PATCH] style(tui): add splash logo --- Cargo.lock | 104 ++++++++++++++++++++++++++ git-cliff-tui/Cargo.toml | 6 +- git-cliff-tui/src/logo.rs | 146 +++++++++++++++++++++++++++++++++++++ git-cliff-tui/src/main.rs | 9 +++ git-cliff-tui/src/state.rs | 5 ++ git-cliff-tui/src/ui.rs | 19 +++++ 6 files changed, 288 insertions(+), 1 deletion(-) create mode 100644 git-cliff-tui/src/logo.rs diff --git a/Cargo.lock b/Cargo.lock index 95657e7e02..41cff6cc38 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -274,6 +274,31 @@ dependencies = [ "generic-array", ] +[[package]] +name = "bon" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f265cdb2e8501f1c952749e78babe8f1937be92c98120e5f78fc72d634682bad" +dependencies = [ + "bon-macros", + "rustversion", +] + +[[package]] +name = "bon-macros" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38aa5c627cd7706490e5b003d685f8b9d69bc343b1a00b9fdd01e75fdf6827cf" +dependencies = [ + "darling", + "ident_case", + "prettyplease", + "proc-macro2", + "quote", + "rustversion", + "syn", +] + [[package]] name = "bstr" version = "1.10.0" @@ -503,6 +528,12 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" +[[package]] +name = "colorsys" +version = "0.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54261aba646433cb567ec89844be4c4825ca92a4f8afba52fc4dd88436e31bbd" + [[package]] name = "compact_str" version = "0.8.0" @@ -694,6 +725,41 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96a6ac251f4a2aca6b3f91340350eab87ae57c3f127ffeb585e92bd336717991" +[[package]] +name = "darling" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn", +] + +[[package]] +name = "darling_macro" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" +dependencies = [ + "darling_core", + "quote", + "syn", +] + [[package]] name = "dary_heap" version = "0.3.6" @@ -1157,11 +1223,15 @@ dependencies = [ name = "git-cliff-tui" version = "2.5.0" dependencies = [ + "ansi-to-tui", "copypasta", "git-cliff", + "lazy_static", "ratatui", + "tachyonfx", "throbber-widgets-tui", "tui-markdown", + "unicode-width 0.2.0", ] [[package]] @@ -1661,6 +1731,12 @@ dependencies = [ "syn", ] +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + [[package]] name = "idna" version = "1.0.3" @@ -2484,6 +2560,16 @@ dependencies = [ "yansi", ] +[[package]] +name = "prettyplease" +version = "0.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64d1ec885c64d0457d564db4ec299b2dae3f9c02808b8ad9c3a089c591b18033" +dependencies = [ + "proc-macro2", + "syn", +] + [[package]] name = "proc-macro-crate" version = "3.2.0" @@ -3230,6 +3316,12 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" +[[package]] +name = "simple-easing" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "832ddd7df0d98d6fd93b973c330b7c8e0742d5cb8f1afc7dea89dba4d2531aa1" + [[package]] name = "siphasher" version = "0.3.11" @@ -3485,6 +3577,18 @@ dependencies = [ "libc", ] +[[package]] +name = "tachyonfx" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e81d1b0a9fc05226969b98527bcc4d9d73bccabf77782ceff55d35d398817ba9" +dependencies = [ + "bon", + "colorsys", + "ratatui", + "simple-easing", +] + [[package]] name = "temp-dir" version = "0.1.14" diff --git a/git-cliff-tui/Cargo.toml b/git-cliff-tui/Cargo.toml index 19ad380e36..0ab1e1511b 100644 --- a/git-cliff-tui/Cargo.toml +++ b/git-cliff-tui/Cargo.toml @@ -6,10 +6,14 @@ license = "MIT" edition = "2021" [dependencies] -ratatui = "0.29.0" +ratatui = { version = "0.29.0", features = ["unstable-widget-ref"] } copypasta = "0.10.1" throbber-widgets-tui = "0.8.0" tui-markdown = "0.3.0" +ansi-to-tui = "7.0.0" +unicode-width = "0.2.0" +tachyonfx = "0.10.1" +lazy_static.workspace = true [dependencies.git-cliff] version = "2.5.0" # managed by release.sh diff --git a/git-cliff-tui/src/logo.rs b/git-cliff-tui/src/logo.rs new file mode 100644 index 0000000000..28da9b1e75 --- /dev/null +++ b/git-cliff-tui/src/logo.rs @@ -0,0 +1,146 @@ +use std::time::Instant; + +use ansi_to_tui::IntoText; +use lazy_static::lazy_static; +use ratatui::{ + buffer::Buffer, + layout::Rect, + style::{ + Color, + Styled, + Stylize, + }, + text::{ + Line, + Text, + }, + widgets::Widget, +}; +use tachyonfx::{ + fx, + Duration, + Effect, + EffectRenderer, + Interpolation, + Motion, + Shader, +}; +use unicode_width::UnicodeWidthStr; + +/// ANSI representation of the project logo. +#[rustfmt::skip] +const LOGO_ANSI: &str = " +\x1b[m +\x1b[49m \x1b[38;2;169;182;182;49m▄\x1b[38;2;178;186;189;49m▄\x1b[38;2;177;185;188;49m▄▄\x1b[38;2;178;185;191;49m▄▄\x1b[38;2;176;184;187;49m▄\x1b[49m \x1b[m +\x1b[49m \x1b[38;2;181;186;190;49m▄\x1b[38;2;182;187;191;49m▄\x1b[38;2;180;188;191;48;2;178;186;189m▄\x1b[38;2;180;188;191;48;2;179;186;190m▄\x1b[38;2;181;186;190;48;2;178;185;189m▄\x1b[38;2;179;187;190;48;2;180;186;189m▄\x1b[38;2;179;187;190;48;2;178;186;189m▄\x1b[38;2;179;187;190;48;2;180;186;190m▄\x1b[38;2;179;187;190;48;2;176;185;190m▄\x1b[38;2;179;186;192;48;2;178;186;191m▄\x1b[38;2;179;186;192;48;2;177;185;191m▄\x1b[48;2;178;186;189m \x1b[38;2;179;187;190;48;2;178;186;189m▄\x1b[38;2;178;186;189;48;2;177;185;188m▄\x1b[38;2;178;186;189;49m▄▄\x1b[49m \x1b[m +\x1b[49m \x1b[38;2;185;190;194;49m▄\x1b[38;2;183;191;194;48;2;183;188;194m▄\x1b[38;2;184;191;194;48;2;183;188;194m▄\x1b[38;2;183;191;194;48;2;180;188;191m▄\x1b[38;2;183;191;194;48;2;181;189;192m▄\x1b[38;2;181;189;192;48;2;180;188;191m▄\x1b[38;2;183;191;194;48;2;182;187;191m▄\x1b[38;2;183;191;194;48;2;181;189;192m▄\x1b[38;2;184;190;194;48;2;180;188;191m▄\x1b[38;2;182;190;193;48;2;181;189;192m▄\x1b[38;2;183;191;194;48;2;180;188;191m▄\x1b[38;2;182;190;193;48;2;180;188;191m▄\x1b[38;2;182;190;193;48;2;181;189;192m▄\x1b[38;2;184;189;193;48;2;180;188;191m▄\x1b[38;2;183;188;192;48;2;180;188;191m▄\x1b[38;2;184;189;193;48;2;180;188;191m▄\x1b[38;2;184;189;193;48;2;180;186;192m▄\x1b[38;2;182;189;193;48;2;180;188;191m▄\x1b[38;2;184;189;193;48;2;180;188;191m▄\x1b[38;2;182;190;193;49m▄\x1b[49m \x1b[m +\x1b[49m \x1b[38;2;185;193;196;49m▄\x1b[48;2;184;192;195m \x1b[38;2;185;192;198;48;2;184;192;195m▄\x1b[38;2;186;193;199;48;2;184;192;195m▄\x1b[38;2;185;193;196;48;2;186;191;195m▄\x1b[38;2;185;193;196;48;2;184;192;195m▄\x1b[38;2;185;192;198;48;2;183;191;194m▄\x1b[38;2;184;191;197;48;2;186;191;195m▄\x1b[38;2;185;193;196;48;2;186;191;195m▄\x1b[38;2;188;193;197;48;2;184;192;195m▄\x1b[48;2;184;192;195m \x1b[38;2;185;192;196;48;2;184;192;195m▄\x1b[38;2;186;194;197;48;2;184;192;195m▄\x1b[38;2;185;193;196;48;2;184;192;195m▄\x1b[38;2;186;191;195;48;2;183;191;194m▄\x1b[38;2;185;193;196;48;2;183;191;194m▄\x1b[38;2;186;194;197;48;2;184;193;196m▄\x1b[38;2;187;192;196;48;2;183;191;194m▄\x1b[38;2;187;192;196;48;2;185;192;195m▄\x1b[38;2;186;191;197;48;2;182;190;193m▄\x1b[38;2;187;192;196;48;2;186;191;195m▄\x1b[38;2;186;191;196;48;2;184;189;193m▄\x1b[38;2;184;192;195;48;2;185;190;194m▄\x1b[38;2;186;191;195;49m▄\x1b[49m \x1b[m +\x1b[49m \x1b[38;2;189;197;200;49m▄\x1b[38;2;190;198;201;48;2;187;195;198m▄\x1b[38;2;190;198;201;48;2;187;195;197m▄\x1b[38;2;190;198;201;48;2;189;194;200m▄\x1b[38;2;192;197;201;48;2;187;194;200m▄\x1b[38;2;191;196;200;48;2;187;194;200m▄\x1b[38;2;189;197;200;48;2;187;195;198m▄\x1b[38;2;190;198;200;48;2;187;194;200m▄\x1b[38;2;190;198;201;48;2;187;194;200m▄\x1b[38;2;190;198;201;48;2;187;195;198m▄\x1b[38;2;192;197;201;48;2;189;194;198m▄\x1b[38;2;189;197;200;48;2;186;194;197m▄\x1b[38;2;191;196;200;48;2;187;195;198m▄\x1b[38;2;190;198;201;48;2;187;194;200m▄\x1b[38;2;190;198;201;48;2;187;195;198m▄▄\x1b[38;2;190;197;203;48;2;187;195;198m▄\x1b[38;2;190;198;201;48;2;187;194;200m▄\x1b[38;2;190;198;201;48;2;189;194;198m▄\x1b[38;2;191;197;200;48;2;189;194;198m▄\x1b[38;2;189;197;200;48;2;189;194;200m▄\x1b[38;2;188;195;201;48;2;188;193;199m▄\x1b[38;2;188;195;201;48;2;189;193;199m▄\x1b[38;2;189;194;198;48;2;185;193;196m▄▄\x1b[38;2;187;193;199;49m▄\x1b[49m \x1b[m +\x1b[49m \x1b[38;2;193;201;204;48;2;190;198;201m▄\x1b[38;2;193;200;206;48;2;191;199;202m▄\x1b[38;2;194;200;204;48;2;192;200;203m▄\x1b[38;2;193;200;206;48;2;192;200;203m▄\x1b[38;2;193;200;206;48;2;192;199;205m▄\x1b[38;2;193;200;206;48;2;192;200;203m▄\x1b[38;2;193;200;206;48;2;190;198;201m▄\x1b[38;2;193;201;204;48;2;192;200;203m▄\x1b[38;2;195;200;204;48;2;191;199;202m▄\x1b[38;2;193;200;204;48;2;191;198;204m▄\x1b[38;2;193;201;204;48;2;190;199;204m▄\x1b[38;2;193;201;204;48;2;191;199;202m▄\x1b[38;2;193;200;206;48;2;190;198;204m▄\x1b[38;2;195;200;204;48;2;190;197;203m▄\x1b[38;2;196;201;205;48;2;191;201;203m▄\x1b[38;2;195;200;204;48;2;191;198;204m▄\x1b[38;2;193;200;206;48;2;192;199;205m▄\x1b[38;2;195;200;204;48;2;190;197;203m▄\x1b[38;2;196;201;207;48;2;191;198;204m▄\x1b[38;2;194;200;204;48;2;192;199;204m▄\x1b[38;2;193;201;204;48;2;190;198;201m▄\x1b[38;2;194;202;205;48;2;190;197;203m▄\x1b[38;2;193;200;205;48;2;188;199;202m▄\x1b[38;2;190;200;202;48;2;189;196;202m▄\x1b[38;2;190;198;201;48;2;190;195;199m▄\x1b[38;2;190;198;201;48;2;189;194;198m▄\x1b[49m \x1b[m +\x1b[49m \x1b[38;2;195;203;206;48;2;194;203;207m▄\x1b[38;2;196;204;207;48;2;193;201;204m▄\x1b[38;2;196;204;207;48;2;194;202;207m▄\x1b[38;2;196;204;207;48;2;196;202;205m▄\x1b[38;2;195;202;208;48;2;194;201;207m▄\x1b[38;2;196;204;207;48;2;194;202;205m▄\x1b[38;2;194;202;205;48;2;194;201;206m▄\x1b[38;2;195;203;206;48;2;193;201;206m▄\x1b[38;2;195;203;206;48;2;194;202;205m▄\x1b[38;2;195;203;208;48;2;194;202;206m▄\x1b[38;2;251;137;72;48;2;195;200;204m▄\x1b[38;2;253;140;74;48;2;193;200;204m▄\x1b[38;2;222;193;185;48;2;194;202;205m▄\x1b[38;2;197;203;206;48;2;194;201;206m▄\x1b[38;2;196;204;207;48;2;194;202;205m▄\x1b[38;2;196;204;207;48;2;194;201;207m▄\x1b[38;2;196;204;207;48;2;196;202;206m▄\x1b[38;2;196;204;207;48;2;195;203;206m▄▄\x1b[38;2;196;204;207;48;2;196;202;206m▄\x1b[38;2;196;204;207;48;2;194;202;205m▄\x1b[38;2;198;203;207;48;2;196;201;207m▄\x1b[38;2;195;203;206;48;2;195;200;204m▄\x1b[38;2;195;203;206;48;2;196;201;205m▄\x1b[38;2;194;202;205;48;2;193;201;204m▄\x1b[38;2;194;199;203;48;2;192;200;203m▄\x1b[38;2;195;200;204;48;2;190;199;201m▄\x1b[38;2;191;201;202;48;2;194;206;206m▄\x1b[49m \x1b[m +\x1b[49m \x1b[38;2;197;202;207;48;2;195;203;206m▄\x1b[48;2;197;205;208m \x1b[38;2;197;205;208;48;2;196;204;207m▄\x1b[48;2;196;204;207m \x1b[38;2;197;205;208;48;2;198;203;207m▄\x1b[48;2;196;204;207m \x1b[38;2;196;202;208;48;2;196;204;207m▄\x1b[38;2;28;42;45;48;2;195;202;208m▄\x1b[38;2;246;139;75;48;2;175;110;76m▄\x1b[38;2;253;138;72;48;2;253;139;72m▄\x1b[38;2;253;138;71;48;2;254;139;72m▄\x1b[38;2;254;139;72;48;2;255;140;73m▄\x1b[38;2;255;139;72;48;2;230;152;93m▄\x1b[38;2;244;140;71;48;2;196;204;207m▄\x1b[38;2;196;202;209;48;2;196;203;209m▄\x1b[48;2;196;203;209m \x1b[48;2;196;204;207m \x1b[48;2;197;205;208m \x1b[38;2;197;205;208;48;2;196;204;207m▄\x1b[48;2;196;204;207m \x1b[38;2;197;205;208;48;2;196;204;207m▄▄\x1b[38;2;198;203;207;48;2;195;203;206m▄\x1b[38;2;196;201;205;48;2;195;200;204m▄\x1b[38;2;198;201;206;48;2;195;200;204m▄\x1b[48;2;192;200;203m \x1b[49m \x1b[m +\x1b[49m \x1b[49;38;2;196;205;207m▀\x1b[38;2;196;204;207;48;2;197;205;208m▄▄▄\x1b[38;2;195;203;209;48;2;196;204;207m▄\x1b[38;2;21;36;39;48;2;196;204;207m▄\x1b[38;2;27;45;47;48;2;207;214;222m▄\x1b[38;2;27;45;47;48;2;26;41;44m▄\x1b[38;2;25;43;45;48;2;26;41;44m▄\x1b[38;2;25;40;43;48;2;27;42;45m▄\x1b[38;2;21;43;41;48;2;25;40;43m▄\x1b[38;2;252;143;71;48;2;50;42;28m▄\x1b[38;2;189;122;71;48;2;249;141;72m▄\x1b[38;2;27;48;55;48;2;253;139;71m▄\x1b[38;2;25;45;46;48;2;248;144;75m▄\x1b[38;2;26;44;46;48;2;254;141;64m▄\x1b[38;2;27;42;45;48;2;166;182;184m▄\x1b[38;2;28;43;46;48;2;194;202;208m▄\x1b[38;2;27;43;42;48;2;192;206;209m▄\x1b[38;2;192;204;214;48;2;197;205;208m▄\x1b[38;2;199;204;208;48;2;197;204;208m▄\x1b[38;2;196;204;207;48;2;197;205;208m▄▄\x1b[38;2;197;205;208;48;2;196;204;207m▄▄\x1b[38;2;199;204;208;48;2;196;204;207m▄\x1b[38;2;199;204;208;48;2;197;200;205m▄\x1b[49;38;2;196;205;207m▀\x1b[49m \x1b[m +\x1b[49m \x1b[38;2;195;203;205;48;2;196;204;207m▄\x1b[38;2;18;39;41;48;2;196;204;207m▄\x1b[38;2;27;50;53;48;2;196;204;207m▄\x1b[38;2;24;48;50;48;2;27;46;48m▄\x1b[38;2;27;48;51;48;2;27;47;48m▄\x1b[38;2;28;47;50;48;2;26;46;47m▄\x1b[38;2;22;48;50;48;2;25;45;46m▄\x1b[38;2;26;52;52;48;2;26;44;46m▄\x1b[38;2;27;51;53;48;2;25;47;48m▄\x1b[38;2;27;46;48;48;2;27;49;51m▄\x1b[38;2;24;44;45;48;2;28;49;52m▄\x1b[38;2;26;45;46;48;2;28;48;49m▄\x1b[38;2;26;43;45;48;2;26;46;47m▄\x1b[38;2;27;42;45;48;2;26;44;46m▄\x1b[38;2;28;43;46;48;2;27;42;45m▄\x1b[48;2;27;42;45m \x1b[38;2;27;41;42;48;2;28;43;46m▄\x1b[38;2;26;40;41;48;2;29;43;46m▄\x1b[38;2;27;41;43;48;2;26;41;43m▄\x1b[38;2;223;114;45;48;2;236;118;39m▄\x1b[38;2;227;114;48;48;2;198;203;209m▄\x1b[38;2;217;160;128;48;2;196;203;209m▄\x1b[38;2;194;205;215;48;2;198;205;210m▄\x1b[38;2;197;205;208;48;2;196;204;207m▄\x1b[38;2;197;204;209;48;2;197;205;208m▄\x1b[38;2;196;203;209;48;2;199;204;208m▄\x1b[49m \x1b[m +\x1b[49m \x1b[49;38;2;26;46;53m▀\x1b[48;2;26;54;55m \x1b[38;2;21;49;50;48;2;27;52;54m▄\x1b[38;2;22;55;54;48;2;28;52;54m▄\x1b[38;2;27;49;52;48;2;25;49;51m▄\x1b[38;2;27;47;50;48;2;27;53;54m▄\x1b[38;2;27;47;48;48;2;31;50;54m▄\x1b[38;2;24;44;45;48;2;27;46;50m▄\x1b[38;2;25;45;46;48;2;26;46;45m▄\x1b[38;2;29;47;49;48;2;27;45;47m▄\x1b[38;2;29;44;47;48;2;25;44;46m▄\x1b[38;2;30;45;48;48;2;28;43;46m▄\x1b[48;2;28;43;46m \x1b[38;2;28;42;45;48;2;26;41;44m▄\x1b[38;2;28;42;45;48;2;27;42;45m▄\x1b[38;2;28;42;43;48;2;28;42;45m▄\x1b[38;2;27;41;42;48;2;28;42;43m▄\x1b[38;2;26;40;41;48;2;28;42;43m▄\x1b[38;2;27;41;42;48;2;26;40;41m▄\x1b[38;2;204;102;44;48;2;209;103;40m▄\x1b[38;2;206;101;40;48;2;217;108;43m▄\x1b[38;2;213;106;42;48;2;222;113;46m▄\x1b[38;2;219;110;45;48;2;228;117;47m▄\x1b[38;2;225;113;47;48;2;202;200;201m▄\x1b[38;2;241;116;43;48;2;198;203;209m▄\x1b[49m \x1b[m +\x1b[49m \x1b[38;2;25;45;48;48;2;28;50;53m▄\x1b[38;2;27;47;48;48;2;30;48;52m▄\x1b[38;2;27;47;48;48;2;25;45;49m▄\x1b[38;2;27;42;45;48;2;28;47;51m▄\x1b[38;2;28;43;46;48;2;26;46;47m▄▄\x1b[48;2;27;42;45m \x1b[38;2;28;43;46;48;2;26;41;44m▄\x1b[48;2;27;42;45m \x1b[38;2;27;42;45;48;2;28;43;46m▄\x1b[38;2;28;42;44;48;2;27;42;45m▄\x1b[48;2;28;42;45m \x1b[38;2;27;41;42;48;2;26;40;41m▄\x1b[38;2;26;40;41;48;2;27;41;44m▄\x1b[48;2;27;41;42m \x1b[38;2;27;41;36;48;2;27;41;42m▄\x1b[38;2;190;98;38;48;2;25;39;39m▄\x1b[38;2;199;97;38;48;2;199;97;35m▄\x1b[38;2;199;97;39;48;2;203;99;38m▄\x1b[38;2;199;99;39;48;2;205;99;39m▄\x1b[38;2;203;98;40;48;2;210;102;40m▄\x1b[38;2;206;101;41;48;2;214;105;40m▄\x1b[49m \x1b[m +\x1b[49m \x1b[49;38;2;26;46;47m▀\x1b[38;2;26;42;45;48;2;28;43;46m▄\x1b[48;2;27;42;45m \x1b[38;2;27;42;45;48;2;28;43;46m▄\x1b[48;2;27;42;45m \x1b[38;2;28;42;45;48;2;27;42;45m▄\x1b[38;2;28;42;45;48;2;28;44;44m▄\x1b[38;2;27;41;42;48;2;27;41;44m▄\x1b[38;2;26;40;41;48;2;29;43;44m▄\x1b[38;2;26;40;41;48;2;27;41;42m▄\x1b[38;2;27;41;44;48;2;26;40;41m▄\x1b[38;2;78;46;24;48;2;27;41;42m▄\x1b[38;2;191;89;31;48;2;24;41;38m▄\x1b[38;2;191;92;35;48;2;192;93;35m▄\x1b[48;2;194;93;37m \x1b[38;2;193;92;36;48;2;196;96;38m▄\x1b[38;2;194;93;37;48;2;197;94;36m▄\x1b[38;2;197;95;37;48;2;199;96;38m▄\x1b[49;38;2;200;95;39m▀\x1b[49m \x1b[m +\x1b[49m \x1b[49;38;2;27;42;45m▀\x1b[49;38;2;28;42;45m▀\x1b[38;2;29;40;42;48;2;28;42;43m▄\x1b[38;2;28;39;41;48;2;28;42;43m▄\x1b[38;2;31;36;43;48;2;27;42;43m▄\x1b[38;2;75;47;26;48;2;28;40;42m▄\x1b[38;2;187;89;41;48;2;26;40;46m▄\x1b[38;2;190;91;36;48;2;58;33;24m▄\x1b[38;2;191;92;37;48;2;188;87;36m▄\x1b[38;2;190;91;36;48;2;190;90;38m▄\x1b[38;2;192;91;35;48;2;190;89;33m▄\x1b[38;2;191;90;34;48;2;191;91;35m▄\x1b[49;38;2;192;91;35m▀▀\x1b[49m \x1b[m +"; + +lazy_static! { + /// ANSI logo converted to a [`Text`] + static ref LOGO_TEXT: Text<'static> = { + let text = LOGO_ANSI.into_text().expect("failed to parse ANSI"); + text.lines + .into_iter() + .map(|line| { + line.spans + .into_iter() + .map(|span| { + // Remove the background color + if let (Some(fg), Some(Color::Reset)) = + (span.style.fg, span.style.bg) + { + span.set_style(fg) + } else { + span + } + }) + .collect::>() + }) + .collect() + }; +} + +/// A logo widget +#[derive(Debug)] +pub struct Logo { + pub is_rendered: bool, + /// The height of the logo + pub height: u16, + /// The width of the logo + pub width: u16, + + /// The time the widget was initialized + init_time: Instant, + /// The effect to render the logo + effect: Effect, +} + +impl Default for Logo { + fn default() -> Self { + let contents = LOGO_TEXT.clone().reset_style().to_string(); + Self { + is_rendered: false, + height: contents.lines().count() as u16, + width: contents + .lines() + .map(|line| line.trim().width()) + .max() + .unwrap_or_default() as u16 + + 2, + init_time: Instant::now(), + effect: { + let c = Color::from_u32(0x1b1b1b); + let timer = (2000, Interpolation::Linear); + fx::slide_in(Motion::UpToDown, 10, 0, c, timer) + }, + } + } +} + +impl Widget for &mut Logo { + fn render(self, area: Rect, buf: &mut Buffer) { + LOGO_TEXT.clone().render(area, buf); + + // Define the styled line with colored text + let line = Line::from(vec![ + "git-".fg(Color::Rgb(255, 255, 255)), + "cliff".fg(Color::Rgb(205, 100, 42)), + ]) + .italic() + .bold(); + + // Calculate the length and position + let line_length = line.to_string().len() as u16; + let center_x = area.x + self.width / 2 - line_length / 2; + let center_y = area.y + self.height + 1; + let centered_rect = Rect { + x: center_x.max(area.x), + y: center_y.max(area.y), + width: line_length, + height: 1, + }; + + // Render the line + line.render(centered_rect, buf); + + // Render the effect if running + if self.effect.running() { + buf.render_effect(&mut self.effect, area, Duration::from_millis(100)); + } + + // Mark as rendered after a delay + self.is_rendered = self.init_time.elapsed().as_millis() > 2000; + } +} diff --git a/git-cliff-tui/src/main.rs b/git-cliff-tui/src/main.rs index 1b917c7b60..af1158d0fa 100644 --- a/git-cliff-tui/src/main.rs +++ b/git-cliff-tui/src/main.rs @@ -1,3 +1,5 @@ +use std::time::Duration; + use crate::{ event::{ Event, @@ -10,6 +12,7 @@ use crate::{ }; pub mod event; +pub mod logo; pub mod state; pub mod ui; @@ -32,6 +35,12 @@ fn main() -> Result<()> { // Start the main loop. loop { terminal.draw(|frame| ui::render(&mut state, frame))?; + + if !state.logo.is_rendered { + std::thread::sleep(Duration::from_millis(16)); + continue; + } + let event = events.receiver.recv()?; match event { Event::Tick => state.tick(), diff --git a/git-cliff-tui/src/state.rs b/git-cliff-tui/src/state.rs index 8638ccb7c9..f8eafc6aa1 100644 --- a/git-cliff-tui/src/state.rs +++ b/git-cliff-tui/src/state.rs @@ -6,6 +6,8 @@ use ratatui::widgets::ListState; use std::error; use throbber_widgets_tui::ThrobberState; +use crate::logo::Logo; + /// Application result type. pub type Result = std::result::Result>; @@ -29,6 +31,8 @@ pub struct State<'a> { pub throbber_state: ThrobberState, /// Is generating? pub is_generating: bool, + /// Logo widget. + pub logo: Logo, } impl State<'_> { @@ -54,6 +58,7 @@ impl State<'_> { } }, is_generating: false, + logo: Logo::default(), args, }; state.generate_changelog(false)?; diff --git a/git-cliff-tui/src/ui.rs b/git-cliff-tui/src/ui.rs index 03e4d055ff..31fedaf82d 100644 --- a/git-cliff-tui/src/ui.rs +++ b/git-cliff-tui/src/ui.rs @@ -28,6 +28,11 @@ use ratatui::{ /// Renders the user interface widgets. pub fn render(state: &mut State, frame: &mut Frame) { + frame.render_widget( + Block::new().style(Style::default().bg(Color::Rgb(27, 27, 27))), + frame.area(), + ); + frame.render_widget( Block::new() .title_top(format!("{} ⛰️", env!("CARGO_PKG_NAME")).bold()) @@ -50,6 +55,20 @@ pub fn render(state: &mut State, frame: &mut Frame) { .split(frame.area()); render_list(state, frame, rects[0]); render_changelog(state, frame, rects[1]); + + if !state.logo.is_rendered { + render_logo(state, frame); + } +} + +fn render_logo(state: &mut State, frame: &mut Frame) { + let logo_area = Rect::new( + frame.area().width / 2 - state.logo.width / 2, + frame.area().height / 2 - state.logo.height / 2, + state.logo.width, + state.logo.height, + ); + frame.render_widget(&mut state.logo, logo_area); } fn render_list(state: &mut State, frame: &mut Frame, area: Rect) {