Author:
Hash:
Timestamp:
+45 -3 +/-3 browse
Kevin Schoon [me@kevinschoon.com]
913a5696980d368005fe4eb5010176a99535c4ba
Mon, 10 Nov 2025 20:44:37 +0000 (3 weeks ago)
| 1 | diff --git a/ayllu/src/config.rs b/ayllu/src/config.rs |
| 2 | index 052cd17..58a0c87 100644 |
| 3 | --- a/ayllu/src/config.rs |
| 4 | +++ b/ayllu/src/config.rs |
| 5 | @@ -84,11 +84,12 @@ pub struct Theme { |
| 6 | |
| 7 | #[derive(Deserialize, Serialize, Clone, Debug)] |
| 8 | pub struct Web { |
| 9 | - pub themes_path: Option<String>, |
| 10 | + pub extra_css: Option<String>, |
| 11 | #[serde(default = "Web::default_default_theme")] |
| 12 | pub default_theme: String, |
| 13 | #[serde(default = "Web::default_themes")] |
| 14 | pub themes: Vec<Theme>, |
| 15 | + pub additional_themes: Option<Vec<Theme>>, |
| 16 | #[serde(default = "Web::default_unsafe_markdown")] |
| 17 | pub unsafe_markdown: bool, |
| 18 | } |
| 19 | @@ -96,9 +97,10 @@ pub struct Web { |
| 20 | impl Default for Web { |
| 21 | fn default() -> Self { |
| 22 | Self { |
| 23 | - themes_path: None, |
| 24 | + extra_css: None, |
| 25 | default_theme: Web::default_default_theme(), |
| 26 | themes: Web::default_themes(), |
| 27 | + additional_themes: None, |
| 28 | unsafe_markdown: Web::default_unsafe_markdown(), |
| 29 | } |
| 30 | } |
| 31 | @@ -297,6 +299,12 @@ impl Configurable for Config { |
| 32 | } |
| 33 | } |
| 34 | |
| 35 | + if let Some(additional_themes) = self.web.additional_themes.as_ref() { |
| 36 | + self.web |
| 37 | + .themes |
| 38 | + .extend_from_slice(additional_themes.as_slice()); |
| 39 | + }; |
| 40 | + |
| 41 | if !self |
| 42 | .web |
| 43 | .themes |
| 44 | diff --git a/ayllu/src/web2/routes/assets.rs b/ayllu/src/web2/routes/assets.rs |
| 45 | index 25f48f2..74ad2d0 100644 |
| 46 | --- a/ayllu/src/web2/routes/assets.rs |
| 47 | +++ b/ayllu/src/web2/routes/assets.rs |
| 48 | @@ -4,6 +4,7 @@ use crate::{config::Config, web2::template::DEFAULT_THEME_CSS}; |
| 49 | use axum::{extract, http::header::CONTENT_TYPE, response::Response}; |
| 50 | |
| 51 | struct CSSBuilder<'a> { |
| 52 | + extra_css: &'a str, |
| 53 | base_css: &'a str, |
| 54 | colorizer: &'a str, |
| 55 | theme: &'a str, |
| 56 | @@ -11,7 +12,10 @@ struct CSSBuilder<'a> { |
| 57 | |
| 58 | impl CSSBuilder<'_> { |
| 59 | pub fn generate(&self) -> String { |
| 60 | - format!("{}\n\n{}\n\n{}", self.base_css, self.theme, self.colorizer,) |
| 61 | + format!( |
| 62 | + "{}\n\n{}\n\n{}\n\n{}", |
| 63 | + self.base_css, self.theme, self.colorizer, self.extra_css |
| 64 | + ) |
| 65 | } |
| 66 | } |
| 67 | |
| 68 | @@ -19,6 +23,7 @@ pub async fn serve_stylesheet( |
| 69 | extract::State(state): extract::State<Config>, |
| 70 | ConfigReader(user_config): ConfigReader, |
| 71 | ) -> Response<String> { |
| 72 | + let extra_css = state.web.extra_css.as_deref().unwrap_or(""); |
| 73 | let theme_css = if let Some(current_theme) = user_config.theme { |
| 74 | state |
| 75 | .web |
| 76 | @@ -27,6 +32,7 @@ pub async fn serve_stylesheet( |
| 77 | .find(|theme| theme.name == current_theme) |
| 78 | .map(|theme| { |
| 79 | CSSBuilder { |
| 80 | + extra_css, |
| 81 | base_css: DEFAULT_THEME_CSS, |
| 82 | colorizer: DEFAULT_THEME_COLORIZER_CSS, |
| 83 | theme: &theme.css_content, |
| 84 | @@ -35,6 +41,7 @@ pub async fn serve_stylesheet( |
| 85 | }) |
| 86 | .unwrap_or_else(|| { |
| 87 | CSSBuilder { |
| 88 | + extra_css, |
| 89 | base_css: DEFAULT_THEME_CSS, |
| 90 | colorizer: DEFAULT_THEME_COLORIZER_CSS, |
| 91 | theme: &state.web.read_default_theme().css_content, |
| 92 | @@ -43,6 +50,7 @@ pub async fn serve_stylesheet( |
| 93 | }) |
| 94 | } else { |
| 95 | CSSBuilder { |
| 96 | + extra_css, |
| 97 | base_css: DEFAULT_THEME_CSS, |
| 98 | colorizer: DEFAULT_THEME_COLORIZER_CSS, |
| 99 | theme: &state.web.read_default_theme().css_content, |
| 100 | diff --git a/config.example.toml b/config.example.toml |
| 101 | index 75cfa82..c169746 100644 |
| 102 | --- a/config.example.toml |
| 103 | +++ b/config.example.toml |
| 104 | @@ -137,6 +137,32 @@ timeout = 1800 |
| 105 | address = "127.0.0.1:10000" |
| 106 | |
| 107 | [web] |
| 108 | + # # Themes are CSS files but you can also directly include some |
| 109 | + # # simple CSS right in your config which will be applied everywhere. |
| 110 | + # extra-css = """ |
| 111 | + # .fuu-bar { |
| 112 | + # color: pink; |
| 113 | + # } |
| 114 | + # """ |
| 115 | + # # Set the theme which is served by default when no setting |
| 116 | + # # is configured in client cookies. |
| 117 | + # default-theme = "stella" |
| 118 | + # # Additional themes can be added such as below, see ayllu/themes for |
| 119 | + # # examples of customization. |
| 120 | + # [[web.themes]] |
| 121 | + # # Name for your theme |
| 122 | + # name = "stella" |
| 123 | + # # All of the CSS content served as the theme. |
| 124 | + # css-content = """ |
| 125 | + # body { |
| 126 | + # background-color: pink; |
| 127 | + # } |
| 128 | + # """ |
| 129 | + # # Additional themes is the same as themes but will be appended to the |
| 130 | + # # themes which are already shipped with the Ayllu binary |
| 131 | + # [[web.additional-themes]] |
| 132 | + # name = "fuu" |
| 133 | + # css-content = "body {color: pink}" |
| 134 | # default theme that will be used when none is specified in user configuration. |
| 135 | # additionally the default theme will be used to load assets that are not |
| 136 | # overriden in the actively selected theme. |