use gpui::{FontWeight, Hsla, SharedString};
pub const TYPESCALE_RATIO: f32 = 1.2;
pub const BASE_SIZE: f32 = 1.0;
#[derive(Clone, Debug)]
pub struct TextStyle {
pub size: f32,
pub line_height: f32,
pub weight: FontWeight,
pub color: Option<Hsla>,
pub margin_top: f32,
}
impl Default for TextStyle {
fn default() -> Self {
Self {
size: BASE_SIZE,
line_height: 1.5,
weight: FontWeight::NORMAL,
color: None,
margin_top: 0.0,
}
}
}
impl TextStyle {
pub fn heading(level: u8) -> Self {
let scale_power = match level {
1 => 4,
2 => 3,
3 => 2,
4 => 1,
_ => 0, };
let size = BASE_SIZE * TYPESCALE_RATIO.powi(scale_power);
Self {
size,
line_height: 1.2,
weight: FontWeight::BOLD,
color: None,
margin_top: 0.0,
}
}
pub fn body() -> Self {
Self {
size: BASE_SIZE,
line_height: 1.5,
weight: FontWeight::NORMAL,
color: None,
margin_top: 0.0,
}
}
pub fn code() -> Self {
Self {
size: BASE_SIZE * 0.875,
line_height: 1.5,
weight: FontWeight::NORMAL,
color: None,
margin_top: 0.0,
}
}
}
#[derive(Clone, Debug)]
pub struct MarkdownStyle {
pub body: TextStyle,
pub h1: TextStyle,
pub h2: TextStyle,
pub h3: TextStyle,
pub h4: TextStyle,
pub h5: TextStyle,
pub h6: TextStyle,
pub code: TextStyle,
pub code_font_family: SharedString,
pub block_spacing: f32,
pub code_block_bg: Option<Hsla>,
pub code_block_border: Option<Hsla>,
pub inline_code_bg: Option<Hsla>,
pub block_quote_border: Option<Hsla>,
pub block_quote_text: Option<Hsla>,
pub rule_color: Option<Hsla>,
pub link_color: Option<Hsla>,
}
impl Default for MarkdownStyle {
fn default() -> Self {
Self {
body: TextStyle::body(),
h1: TextStyle::heading(1),
h2: TextStyle::heading(2),
h3: TextStyle::heading(3),
h4: TextStyle::heading(4),
h5: TextStyle::heading(5),
h6: TextStyle::heading(6),
code: TextStyle::code(),
code_font_family: SharedString::from("monospace"),
block_spacing: 0.5,
code_block_bg: None,
code_block_border: None,
inline_code_bg: None,
block_quote_border: None,
block_quote_text: None,
rule_color: None,
link_color: None,
}
}
}
impl MarkdownStyle {
pub fn new() -> Self {
Self::default()
}
pub fn code_font(mut self, family: impl Into<SharedString>) -> Self {
self.code_font_family = family.into();
self
}
pub fn block_spacing(mut self, spacing: f32) -> Self {
self.block_spacing = spacing;
self
}
pub fn code_colors(mut self, bg: Hsla, border: Hsla) -> Self {
self.code_block_bg = Some(bg);
self.code_block_border = Some(border);
self.inline_code_bg = Some(bg);
self
}
pub fn block_quote_colors(mut self, border: Hsla, text: Hsla) -> Self {
self.block_quote_border = Some(border);
self.block_quote_text = Some(text);
self
}
pub fn rule_color(mut self, color: Hsla) -> Self {
self.rule_color = Some(color);
self
}
pub fn link_color(mut self, color: Hsla) -> Self {
self.link_color = Some(color);
self
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_typescale() {
let h1 = TextStyle::heading(1);
let h2 = TextStyle::heading(2);
let body = TextStyle::body();
assert!(h1.size > h2.size);
assert!(h2.size > body.size);
let ratio = h1.size / h2.size;
assert!((ratio - TYPESCALE_RATIO).abs() < 0.01);
}
#[test]
fn test_heading_sizes() {
let h4 = TextStyle::heading(4);
let h5 = TextStyle::heading(5);
let h6 = TextStyle::heading(6);
let body = TextStyle::body();
assert!(
(h4.size - TYPESCALE_RATIO).abs() < 0.01,
"H4 should be one typescale step above base"
);
assert!((h5.size - BASE_SIZE).abs() < 0.01, "H5 should be base size");
assert!((h6.size - BASE_SIZE).abs() < 0.01, "H6 should be base size");
assert!(
(body.size - BASE_SIZE).abs() < 0.01,
"Body should be base size"
);
}
#[test]
fn test_line_heights() {
let body = TextStyle::body();
let h1 = TextStyle::heading(1);
assert!(body.line_height > h1.line_height);
}
}