Skip to main content

tui_markdown/
style_sheet.rs

1//! Style sheet abstraction for tui-markdown.
2//!
3//! [`StyleSheet`] supplies the styles and alert text used while Markdown events are rendered.
4//! Every choice has a default. Implementations only need to override the styles or text they want
5//! to customize.
6//!
7//! [`DefaultStyleSheet`] is used by [`crate::Options::default`]. Applications can pass another
8//! implementation to [`crate::Options::new`].
9
10use ratatui_core::style::Style;
11
12/// The kind of a GitHub Flavored Markdown alert.
13#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
14pub enum AlertKind {
15    /// Supplementary information.
16    Note,
17    /// Helpful advice.
18    Tip,
19    /// Information essential to success.
20    Important,
21    /// Urgent information that needs attention.
22    Warning,
23    /// A risk or negative outcome.
24    Caution,
25}
26
27impl AlertKind {
28    /// The canonical English label for this alert kind.
29    pub const fn label(self) -> &'static str {
30        match self {
31            Self::Note => "Note",
32            Self::Tip => "Tip",
33            Self::Important => "Important",
34            Self::Warning => "Warning",
35            Self::Caution => "Caution",
36        }
37    }
38}
39
40/// Visual styles and symbols consumed by the renderer.
41///
42/// Every method has a default, which [`DefaultStyleSheet`] uses unchanged. Implementations only
43/// need to override the choices they want to customize.
44pub trait StyleSheet: Clone + Send + Sync + 'static {
45    /// Style for a Markdown heading.
46    ///
47    /// `level` is one-based (`1` for `# H1`, …).
48    fn heading(&self, level: u8) -> Style {
49        match level {
50            1 => Style::new().on_cyan().bold().underlined(),
51            2 => Style::new().cyan().bold(),
52            3 => Style::new().cyan().bold().italic(),
53            4 => Style::new().light_cyan().italic(),
54            5 => Style::new().light_cyan().italic(),
55            _ => Style::new().light_cyan().italic(),
56        }
57    }
58
59    /// Style for inline `code` spans and fenced code blocks when syntax highlighting is disabled.
60    fn code(&self) -> Style {
61        Style::new().white().on_black()
62    }
63
64    /// Style for an inline or reference link's visible label and appended destination.
65    fn link(&self) -> Style {
66        Style::new().blue().underlined()
67    }
68
69    /// Base style applied to blockquotes (`>` prefix and body text).
70    fn blockquote(&self) -> Style {
71        Style::new().green()
72    }
73
74    /// Style for heading attribute metadata appended to the heading text.
75    fn heading_meta(&self) -> Style {
76        Style::new().dim()
77    }
78
79    /// Style for metadata blocks (front matter).
80    fn metadata_block(&self) -> Style {
81        Style::new().light_yellow()
82    }
83
84    /// Marker displayed before a Markdown heading.
85    ///
86    /// `level` is one-based (`1` for an H1, …). The renderer adds one separating space after a
87    /// non-empty marker. Return an empty string to omit the marker and its separator.
88    fn heading_marker(&self, level: u8) -> &str {
89        match level {
90            1 => "#",
91            2 => "##",
92            3 => "###",
93            4 => "####",
94            5 => "#####",
95            _ => "######",
96        }
97    }
98
99    /// Delimiter displayed above and below block code.
100    ///
101    /// The renderer appends fenced-code info to the opening delimiter. Return an empty string to
102    /// omit both delimiter lines.
103    fn code_block_fence(&self) -> &str {
104        "```"
105    }
106
107    /// Style for raw HTML blocks and inline HTML tags.
108    fn html(&self) -> Style {
109        Style::new().dim()
110    }
111
112    /// Style for inline math (`$...$`).
113    fn math_inline(&self) -> Style {
114        Style::new().italic().magenta()
115    }
116
117    /// Style for display math (`$$...$$`).
118    fn math_display(&self) -> Style {
119        Style::new().magenta()
120    }
121
122    /// Style for footnote references, rendered as `[label]`.
123    fn footnote_ref(&self) -> Style {
124        Style::new().dim().italic()
125    }
126
127    /// Style for footnote definitions, including the `[label]: ` prefix.
128    fn footnote_def(&self) -> Style {
129        Style::new().dim()
130    }
131
132    /// Style for definition list terms.
133    fn definition_term(&self) -> Style {
134        Style::new().bold()
135    }
136
137    /// Style for definition list descriptions, including the `: ` prefix.
138    fn definition_description(&self) -> Style {
139        Style::default()
140    }
141    /// Style for a GFM alert heading and body.
142    ///
143    /// The generated icon and label are bold in addition to this base style.
144    fn alert(&self, kind: AlertKind) -> Style {
145        use ratatui_core::style::Color;
146
147        match kind {
148            AlertKind::Note => Style::new().fg(Color::Blue),
149            AlertKind::Tip => Style::new().fg(Color::Green),
150            AlertKind::Important => Style::new().fg(Color::Magenta),
151            AlertKind::Warning => Style::new().fg(Color::Yellow),
152            AlertKind::Caution => Style::new().fg(Color::Red),
153        }
154    }
155
156    /// Icon displayed before a GFM alert label.
157    ///
158    /// Return an empty string to render the label without an icon.
159    fn alert_icon(&self, kind: AlertKind) -> &str {
160        match kind {
161            AlertKind::Note => "\u{2139}\u{FE0F}",
162            AlertKind::Tip => "\u{1F4A1}",
163            AlertKind::Important => "\u{2757}",
164            AlertKind::Warning => "\u{26A0}\u{FE0F}",
165            AlertKind::Caution => "\u{1F534}",
166        }
167    }
168
169    /// Label displayed after a GFM alert icon.
170    ///
171    /// Return an empty string to render the icon without a label.
172    fn alert_label(&self, kind: AlertKind) -> &str {
173        kind.label()
174    }
175
176    /// Style patched onto cells in the table header row.
177    ///
178    /// Properties set by this style override the same properties in an inline style. The style
179    /// covers cell padding, while borders use [`Self::table_border`].
180    fn table_header(&self) -> Style {
181        Style::new().bold().cyan()
182    }
183
184    /// Style patched onto ordinary table cells.
185    ///
186    /// Properties set by this style override the same properties in an inline style. The style
187    /// covers cell padding, while borders use [`Self::table_border`].
188    fn table_cell(&self) -> Style {
189        Style::default()
190    }
191
192    /// Style for the Unicode box-drawing characters around table cells.
193    ///
194    /// This changes the presentation of the borders, not the box-drawing characters themselves.
195    fn table_border(&self) -> Style {
196        Style::new().dark_gray()
197    }
198
199    /// Style for the `[img]` marker and text used to represent Markdown images.
200    ///
201    /// The default is dim and italic. The renderer patches this over any enclosing inline style.
202    fn image_alt(&self) -> Style {
203        Style::new().dim().italic()
204    }
205}
206
207/// The default style set
208///
209/// This style sheet will be used by default if the user does not provide their own implementation.
210///
211/// Styles are:
212/// - H1: white on cyan, bold, underlined
213/// - H2: cyan, bold
214/// - H3: cyan, bold, italic
215/// - H4-H6: light cyan, italic
216/// - code: white on black
217/// - link: blue, underlined
218/// - blockquote: green
219/// - metadata block: light yellow
220/// - heading markers: one to six `#` characters
221/// - code block fences: three backticks
222/// - raw HTML: dim
223/// - inline math: magenta, italic
224/// - display math: magenta
225/// - footnote references: dim, italic
226/// - footnote definitions: dim
227/// - definition list terms: bold
228/// - definition list descriptions: the surrounding style
229/// - note alerts: blue
230/// - tip alerts: green
231/// - important alerts: magenta
232/// - warning alerts: yellow
233/// - caution alerts: red
234/// - table headers: bold cyan
235/// - table cells: the surrounding style
236/// - table borders: dark gray
237/// - image fallback text: dim and italic
238#[derive(Clone, Copy, Debug, Default)]
239pub struct DefaultStyleSheet;
240
241impl StyleSheet for DefaultStyleSheet {}