index : sparkle-git.git

ascending towards madness

author holly sparkles <sparkles@holly.sh> 2024-01-02 19:56:09.0 +00:00:00
committer holly sparkles <sparkles@holly.sh> 2024-05-14 6:40:47.0 +00:00:00
commit
523ae871d8f66c88952eb457b7a275b26dca5950 [patch]
tree
8ca46d3cc15f08ce91829214d1cf1e9ac9af938e
parent
c50c46422046cff0daab9fe71cde0bec4834a676
download
523ae871d8f66c88952eb457b7a275b26dca5950.tar.gz

feat!: add new header styles

- Default: same as previous versions of rgit
- Text: text and subtitle only, no image or emoji
- Image: image, text, and subtitle

also utilises `root_description` in the configuration

BREAKING CHANGE: this breaks previous use of `logo` and `logo_alt`

Diff

 Cargo.lock               |  1 +
 Cargo.toml               |  1 +
 rgit.conf.example        |  8 +++++---
 src/configuration/mod.rs | 44 ++++++++++++++++++++++++++++++++++++++++++--
 src/main.rs              | 42 ++++++++++++++++++++++++++++++++++++++++++
 statics/sass/style.scss  | 29 +++++++++++++++++++++++++++++
 templates/base.html      | 26 +++++++++++++++++++-------
 7 files changed, 139 insertions(+), 12 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock
index 881531e..7001daf 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2171,6 +2171,7 @@ dependencies = [
 "humantime",
 "itertools",
 "md5",
 "mime_guess",
 "moka",
 "nom",
 "once_cell",
diff --git a/Cargo.toml b/Cargo.toml
index d77931f..fe8c4a9 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -55,6 +55,7 @@ yoke = { version = "0.7.1", features = ["derive"] }
rand = "0.8.5"
confy = "0.5.1"
rsass = "0.28.0"
mime_guess = "2.0.4"


[build-dependencies]
diff --git a/rgit.conf.example b/rgit.conf.example
index 3479f0b..3ea3274 100644
--- a/rgit.conf.example
+++ b/rgit.conf.example
@@ -12,9 +12,9 @@ snapshots = [
]
# Add a favicon
favicon = '/config/favicon.ico'
# Use a custom logo image
# Specify a path to a logo image, or an emoji (eg. '🏔️')
logo = '/config/logo.png'
# Alt text for the custom logo image
# Alt text for the logo image
logo_alt = '🏔️'
# Url loaded when clicking on the logo
logo_link = '/'
@@ -33,4 +33,6 @@ max_commit_message_length = 80
# Limit length of repository descriptions; use 0 for no limit
max_repo_desc_length = 80
# Use custom css on all pages
css = "/custom.scss"
\ No newline at end of file
css = "/custom.scss"
# Change the header style
header = "Image"
\ No newline at end of file
diff --git a/src/configuration/mod.rs b/src/configuration/mod.rs
index 68be7c1..7305fb2 100644
--- a/src/configuration/mod.rs
+++ b/src/configuration/mod.rs
@@ -97,6 +97,17 @@ pub struct AppConfig {
    ///
    /// Default value: `None`
    pub css: Option<String>,

    /// Specifies the style of header to use.
    ///
    /// `Default` is the default style of an emoji and title text
    ///
    /// `Text` is only the title text and a subtitle (if applicable)
    ///
    /// `Image` is an image, title text, and a subtitle (if applicable)
    ///
    /// Default value: `Default`
    pub header: Option<HeaderStyle>,
}

impl ::std::default::Default for AppConfig {
@@ -111,8 +122,8 @@ impl ::std::default::Default for AppConfig {
                String::from("zip"),
            ],
            favicon: String::new(),
            logo: String::new(),
            logo_alt: String::from("🏡"),
            logo: String::from("🏡"),
            logo_alt: String::new(),
            logo_link: String::from("/"),
            enable_http_clone: true,
            enable_index_links: false,
@@ -122,6 +133,7 @@ impl ::std::default::Default for AppConfig {
            max_commit_message_length: 0,
            max_repo_desc_length: 0,
            css: None,
            header: Some(HeaderStyle::default()),
        }
    }
}
@@ -183,3 +195,31 @@ impl ReadmeConfig {
        options
    }
}

/// Represents available header styles for rendering the frontend.
#[derive(Debug, Default, Clone, Serialize, PartialEq, Eq)]
pub enum HeaderStyle {
    /// Emoji logo, text title
    #[default]
    Default,
    /// Text title, text subtitle
    Text,
    /// Image logo, text title, text subtitle
    Image,
}

impl<'de> Deserialize<'de> for HeaderStyle {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        let variant: &str = Deserialize::deserialize(deserializer)?;
        // Support case insensitivity
        match variant.to_lowercase().as_str() {
            "default" => Ok(HeaderStyle::Default),
            "text" => Ok(HeaderStyle::Text),
            "image" => Ok(HeaderStyle::Image),
            _ => Ok(HeaderStyle::Default),
        }
    }
}
diff --git a/src/main.rs b/src/main.rs
index 71eb480..59da25a 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -201,6 +201,35 @@ async fn main() -> Result<(), anyhow::Error> {
        }
    };

    let static_file = |file: String| {
        move || async move {
            let mime_type = mime_guess::from_path(&file)
                .first()
                .map_or("text/plain".to_string(), |mime| {
                    mime.essence_str().to_string()
                });

            let content = std::fs::read(&PathBuf::from(&file)).unwrap_or_else(|_| {
                if file.is_empty() {
                    warn!("Requested filename is blank.");
                }
                error!("Unable to load file: {}", file);
                Vec::new()
            });

            //TODO: create a 410 (gone) response here when content is empty or file is invalid.
            let mut resp = Response::new(Body::from(content));
            resp.headers_mut().insert(
                http::header::CONTENT_TYPE,
                HeaderValue::from_str(&mime_type).unwrap_or_else(|_| {
                    warn!("Invalid MIME type: {} for file: {}", mime_type, file);
                    HeaderValue::from_static("text/plain")
                }),
            );
            resp
        }
    };

    let config = configuration::AppConfig::load(args.config);

    let app = Router::new()
@@ -226,6 +255,10 @@ async fn main() -> Result<(), anyhow::Error> {
        )
        .route("/favicon.ico", get(static_favicon(config.favicon.clone())))
        .route("/about", get(methods::about::handle))
        .route(
            &format!("/{}", get_logo_filename(config.logo.clone())),
            get(static_file(config.logo.clone())),
        )
        .fallback(methods::repo::service)
        .layer(TimeoutLayer::new(args.request_timeout.into()))
        .layer(layer_fn(LoggingMiddleware))
@@ -392,3 +425,12 @@ impl<A: IntoResponse, B: IntoResponse> IntoResponse for ResponseEither<A, B> {
        }
    }
}

pub fn get_logo_filename(filename: String) -> String {
    let extension = PathBuf::from(filename)
        .extension()
        .unwrap_or_else(|| std::ffi::OsStr::new("png"))
        .to_string_lossy()
        .to_string();
    format!("logo.{}", extension)
}
diff --git a/statics/sass/style.scss b/statics/sass/style.scss
index 0be2ab2..9702da1 100644
--- a/statics/sass/style.scss
+++ b/statics/sass/style.scss
@@ -104,3 +104,32 @@ a {
  font-size: 80%;
  padding: 0em 0.5em;
}

header.full {
  border: none;
  display: flex;

  hgroup {
    width: 100%;
    vertical-align: middle;
    h1 {
      border-bottom: solid 1px #ccc;
      margin-bottom: 0rem;
      padding-bottom: 0.2rem;
    }
    p {
      margin-top: 0rem;
      padding-top: 0.2rem;
    }
  }

  a.logo {
    margin: auto 0.9rem auto 0;
    font-size: 4rem;
    
    img {
      max-height: 5rem;
      vertical-align: middle;
    }
  }
}
diff --git a/templates/base.html b/templates/base.html
index 6387c8d..2b911a8 100644
--- a/templates/base.html
+++ b/templates/base.html
@@ -13,19 +13,31 @@
</head>

<body>
{% let header = config.header.clone().unwrap_or_default() %}
{% if header == crate::configuration::HeaderStyle::Default %}
<header>
    <h1>
        <a href="{{ config.logo_link }}" class="no-hover">
			{% if config.logo.is_empty() %}
			{{ config.logo_alt }}
			{% else %}
			<img src="{{ config.logo }}" alt="{{ config.logo_alt }}">
			{% endif %}
		</a>
            {{ config.logo }}
        </a>
        {% block header -%}{{ config.root_title }}{%- endblock %}
    </h1>
</header>

{% else %}
<header class="full">
	{% if header == crate::configuration::HeaderStyle::Image %}
    <a class="logo no-hover" href="{{ config.logo_link }}">
        <img src="/{{ crate::get_logo_filename(config.logo.clone()) }}" alt="{{ config.logo_alt }}">
    </a>
	{% endif %}
    <hgroup>
        <h1>
            {% block header -%}{{ config.root_title }}{%- endblock %}
        </h1>
        <p>{{ config.root_description }}</p>
    </hgroup>
</header>
{% endif %}
{%- block nav -%}
<nav>
    <div>