Skip to content

Commit

Permalink
feat: add symbols to alerts
Browse files Browse the repository at this point in the history
  • Loading branch information
mfontanini committed Jan 31, 2025
1 parent 22af066 commit 3a389c1
Show file tree
Hide file tree
Showing 3 changed files with 167 additions and 57 deletions.
22 changes: 13 additions & 9 deletions src/presentation/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -733,21 +733,25 @@ impl<'a> PresentationBuilder<'a> {
}

fn push_alert(&mut self, alert_type: AlertType, title: Option<String>, mut lines: Vec<Line>) -> BuildResult {
let (default_title, prefix_color) = match alert_type {
AlertType::Note => ("Note", self.theme.alert.colors.types.note),
AlertType::Tip => ("Tip", self.theme.alert.colors.types.tip),
AlertType::Important => ("Important", self.theme.alert.colors.types.important),
AlertType::Warning => ("Warning", self.theme.alert.colors.types.warning),
AlertType::Caution => ("Caution", self.theme.alert.colors.types.caution),
let (prefix_color, default_title, symbol) = match alert_type {
AlertType::Note => self.theme.alert.styles.note.as_parts(),
AlertType::Tip => self.theme.alert.styles.tip.as_parts(),
AlertType::Important => self.theme.alert.styles.important.as_parts(),
AlertType::Warning => self.theme.alert.styles.warning.as_parts(),
AlertType::Caution => self.theme.alert.styles.caution.as_parts(),
};
let prefix_color = prefix_color.or(self.theme.alert.colors.base.foreground);
let prefix_color = prefix_color.or(self.theme.alert.base_colors.foreground);
let title = title.unwrap_or_else(|| default_title.to_string());
let title_colors = Colors { foreground: prefix_color, background: self.theme.alert.colors.base.background };
let title = match symbol {
Some(symbol) => format!("{symbol} {title}"),
None => title,
};
let title_colors = Colors { foreground: prefix_color, background: self.theme.alert.base_colors.background };
lines.insert(0, Line::from(Text::from("")));
lines.insert(0, Line::from(Text::new(title, TextStyle::default().colors(title_colors))));

let prefix = self.theme.block_quote.prefix.clone().unwrap_or_default();
self.push_quoted_text(lines, prefix, self.theme.alert.colors.base, prefix_color)
self.push_quoted_text(lines, prefix, self.theme.alert.base_colors, prefix_color)
}

fn push_quoted_text(
Expand Down
183 changes: 142 additions & 41 deletions src/theme.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::markdown::text_style::{Color, Colors, FixedStr, UndefinedPaletteColorError};
use serde::{Deserialize, Serialize};
use std::{collections::BTreeMap, fs, io, path::Path};
use std::{collections::BTreeMap, fmt, fs, io, marker::PhantomData, path::Path};

include!(concat!(env!("OUT_DIR"), "/themes.rs"));

Expand Down Expand Up @@ -395,7 +395,7 @@ pub(crate) struct BlockQuoteStyle {

impl BlockQuoteStyle {
fn resolve_palette_colors(&mut self, palette: &ColorPalette) -> Result<(), UndefinedPaletteColorError> {
let Self { colors, alignment: _alignment, prefix: _prefix } = self;
let Self { colors, alignment: _, prefix: _ } = self;
colors.resolve_palette_colors(palette)?;
Ok(())
}
Expand Down Expand Up @@ -431,82 +431,183 @@ pub(crate) struct AlertStyle {
#[serde(flatten, default)]
pub(crate) alignment: Option<Alignment>,

/// The base colors.
#[serde(default)]
pub(crate) base_colors: Colors,

/// The prefix to be added to this block quote.
///
/// This allows adding something like a vertical bar before the text.
#[serde(default)]
pub(crate) prefix: Option<String>,

/// The colors to be used.
/// The style for each alert type.
#[serde(default)]
pub(crate) colors: AlertColors,
pub(crate) styles: AlertTypeStyles,
}

impl AlertStyle {
fn resolve_palette_colors(&mut self, palette: &ColorPalette) -> Result<(), UndefinedPaletteColorError> {
let Self { colors, alignment: _alignment, prefix: _prefix } = self;
colors.resolve_palette_colors(palette)?;
let Self { base_colors, styles, alignment: _, prefix: _ } = self;
*base_colors = base_colors.resolve(palette)?;
styles.resolve_palette_colors(palette)?;
Ok(())
}
}

/// The colors of an alert.
/// The style for each alert type.
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
pub(crate) struct AlertColors {
/// The foreground/background colors.
#[serde(flatten)]
pub(crate) base: Colors,
pub(crate) struct AlertTypeStyles {
/// The style for note alert types.
#[serde(default)]
pub(crate) note: AlertTypeStyle<NoteAlertType>,

/// The color of the vertical bar that prefixes each line in the quote.
/// The style for tip alert types.
#[serde(default)]
pub(crate) types: AlertTypeColors,
pub(crate) tip: AlertTypeStyle<TipAlertType>,

/// The style for important alert types.
#[serde(default)]
pub(crate) important: AlertTypeStyle<ImportantAlertType>,

/// The style for warning alert types.
#[serde(default)]
pub(crate) warning: AlertTypeStyle<WarningAlertType>,

/// The style for caution alert types.
#[serde(default)]
pub(crate) caution: AlertTypeStyle<CautionAlertType>,
}

impl AlertColors {
impl AlertTypeStyles {
fn resolve_palette_colors(&mut self, palette: &ColorPalette) -> Result<(), UndefinedPaletteColorError> {
let Self { base, types } = self;
*base = base.resolve(palette)?;
types.resolve_palette_colors(palette)?;
let Self { note, tip, important, warning, caution } = self;
note.resolve_palette_colors(palette)?;
tip.resolve_palette_colors(palette)?;
important.resolve_palette_colors(palette)?;
warning.resolve_palette_colors(palette)?;
caution.resolve_palette_colors(palette)?;
Ok(())
}
}

/// The colors of each alert type.
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
pub(crate) struct AlertTypeColors {
/// The color for note type alerts.
#[serde(default)]
pub(crate) note: Option<Color>,

/// The color for tip type alerts.
/// The style for an alert type.
#[derive(Deserialize, Serialize)]
pub(crate) struct AlertTypeStyle<S: AlertTypeProperties> {
/// The color to be used.
#[serde(default)]
pub(crate) tip: Option<Color>,
pub(crate) color: Option<Color>,

/// The color for important type alerts.
#[serde(default)]
pub(crate) important: Option<Color>,
/// The title to be used.
#[serde(default = "S::default_title")]
pub(crate) title: String,

/// The color for warning type alerts.
#[serde(default)]
pub(crate) warning: Option<Color>,
/// The symbol to be used.
#[serde(default = "S::default_symbol")]
pub(crate) symbol: Option<String>,

/// The color for caution type alerts.
#[serde(default)]
pub(crate) caution: Option<Color>,
#[serde(skip)]
_unused: PhantomData<S>,
}

impl AlertTypeColors {
impl<S: AlertTypeProperties> AlertTypeStyle<S> {
pub(crate) fn as_parts(&self) -> (&Option<Color>, &str, Option<&str>) {
(&self.color, &self.title, self.symbol.as_deref())
}

fn resolve_palette_colors(&mut self, palette: &ColorPalette) -> Result<(), UndefinedPaletteColorError> {
let Self { note, tip, important, warning, caution } = self;
for c in [note, tip, important, warning, caution] {
if let Some(c) = c.as_mut() {
*c = c.resolve(palette)?;
}
let Self { color, title: _, symbol: _, _unused: _ } = self;
if let Some(color) = color.as_mut() {
*color = color.resolve(palette)?;
}
Ok(())
}
}

impl<S: AlertTypeProperties> fmt::Debug for AlertTypeStyle<S> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("AlertTypeStyle")
.field("color", &self.color)
.field("title", &self.title)
.field("symbol", &self.symbol)
.field("_unused", &self._unused)
.finish()
}
}

impl<S: AlertTypeProperties> Clone for AlertTypeStyle<S> {
fn clone(&self) -> Self {
Self { color: self.color, title: self.title.clone(), symbol: self.symbol.clone(), _unused: PhantomData }
}
}

impl<S: AlertTypeProperties> Default for AlertTypeStyle<S> {
fn default() -> Self {
Self { color: None, title: S::default_title(), symbol: S::default_symbol(), _unused: PhantomData }
}
}

pub(crate) trait AlertTypeProperties {
fn default_title() -> String;
fn default_symbol() -> Option<String>;
}

pub(crate) struct NoteAlertType;
pub(crate) struct TipAlertType;
pub(crate) struct ImportantAlertType;
pub(crate) struct WarningAlertType;
pub(crate) struct CautionAlertType;

impl AlertTypeProperties for NoteAlertType {
fn default_title() -> String {
"Note".into()
}

fn default_symbol() -> Option<String> {
Some("󰋽".into())
}
}

impl AlertTypeProperties for TipAlertType {
fn default_title() -> String {
"Tip".into()
}

fn default_symbol() -> Option<String> {
Some("".into())
}
}

impl AlertTypeProperties for ImportantAlertType {
fn default_title() -> String {
"Important".into()
}

fn default_symbol() -> Option<String> {
Some("".into())
}
}

impl AlertTypeProperties for WarningAlertType {
fn default_title() -> String {
"Warning".into()
}

fn default_symbol() -> Option<String> {
Some("".into())
}
}

impl AlertTypeProperties for CautionAlertType {
fn default_title() -> String {
"Caution".into()
}

fn default_symbol() -> Option<String> {
Some("󰳦".into())
}
}

/// The style for the presentation introduction slide.
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
pub(crate) struct IntroSlideStyle {
Expand Down
19 changes: 12 additions & 7 deletions themes/dark.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -106,15 +106,20 @@ block_quote:

alert:
prefix: ""
colors:
base_colors:
foreground: palette:light_gray
background: palette:blue_gray
types:
note: palette:blue
tip: palette:light_green
important: palette:purple
warning: palette:orange
caution: palette:red
styles:
note:
color: palette:blue
tip:
color: palette:light_green
important:
color: palette:purple
warning:
color: palette:orange
caution:
color: palette:red

typst:
colors:
Expand Down

0 comments on commit 3a389c1

Please sign in to comment.