Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
304 changes: 304 additions & 0 deletions src/cortex-core/src/markdown/theme.rs
Original file line number Diff line number Diff line change
Expand Up @@ -380,6 +380,209 @@ impl MarkdownTheme {
}
}

impl MarkdownTheme {
/// Create a markdown theme for the "dark" theme (same as default)
pub fn dark() -> Self {
Self::default()
}

/// Create a markdown theme for the "light" theme
pub fn light() -> Self {
Self {
h1: Style::default()
.fg(Color::Rgb(0, 100, 70))
.add_modifier(Modifier::BOLD | Modifier::UNDERLINED),
h2: Style::default()
.fg(Color::Rgb(0, 100, 70))
.add_modifier(Modifier::BOLD),
h3: Style::default()
.fg(Color::Rgb(0, 80, 60))
.add_modifier(Modifier::BOLD | Modifier::ITALIC),
h4: Style::default()
.fg(Color::Rgb(0, 80, 60))
.add_modifier(Modifier::ITALIC),
h5: Style::default()
.fg(Color::Rgb(80, 80, 80))
.add_modifier(Modifier::ITALIC),
h6: Style::default()
.fg(Color::Rgb(120, 120, 120))
.add_modifier(Modifier::ITALIC),
bold: Style::default()
.fg(Color::Rgb(0, 100, 70))
.add_modifier(Modifier::BOLD),
italic: Style::default()
.fg(Color::Rgb(30, 30, 30))
.add_modifier(Modifier::ITALIC),
strikethrough: Style::default()
.fg(Color::Rgb(100, 100, 100))
.add_modifier(Modifier::CROSSED_OUT),
code_inline: Style::default()
.fg(Color::Rgb(0, 80, 60))
.bg(Color::Rgb(235, 235, 235)),
code_block_bg: Color::Rgb(245, 245, 245),
code_block_border: Color::Rgb(200, 200, 200),
code_block_text: Style::default().fg(Color::Rgb(30, 30, 30)),
code_lang_tag: Style::default()
.fg(Color::Rgb(50, 100, 200))
.add_modifier(Modifier::ITALIC),
blockquote_border: Color::Rgb(0, 100, 70),
blockquote_text: Style::default()
.fg(Color::Rgb(80, 80, 80))
.add_modifier(Modifier::ITALIC),
list_bullet: Style::default().fg(Color::Rgb(0, 100, 70)),
list_number: Style::default().fg(Color::Rgb(0, 100, 70)),
task_checked: Style::default().fg(Color::Rgb(0, 150, 0)),
task_unchecked: Style::default().fg(Color::Rgb(120, 120, 120)),
table_border: Color::Rgb(0, 100, 70),
table_header_bg: Color::Rgb(235, 235, 235),
table_header_text: Style::default()
.fg(Color::Rgb(30, 30, 30))
.add_modifier(Modifier::BOLD),
table_cell_text: Style::default().fg(Color::Rgb(30, 30, 30)),
link_text: Style::default()
.fg(Color::Rgb(50, 100, 200))
.add_modifier(Modifier::UNDERLINED),
link_url: Style::default().fg(Color::Rgb(120, 120, 120)),
hr: Style::default().fg(Color::Rgb(200, 200, 200)),
text: Style::default().fg(Color::Rgb(30, 30, 30)),
}
}

/// Create a markdown theme for the "ocean_dark" theme
pub fn ocean_dark() -> Self {
Self {
h1: Style::default()
.fg(Color::Rgb(0, 200, 200))
.add_modifier(Modifier::BOLD | Modifier::UNDERLINED),
h2: Style::default()
.fg(Color::Rgb(0, 200, 200))
.add_modifier(Modifier::BOLD),
h3: Style::default()
.fg(Color::Rgb(100, 200, 220))
.add_modifier(Modifier::BOLD | Modifier::ITALIC),
h4: Style::default()
.fg(Color::Rgb(100, 200, 220))
.add_modifier(Modifier::ITALIC),
h5: Style::default()
.fg(Color::Rgb(140, 170, 200))
.add_modifier(Modifier::ITALIC),
h6: Style::default()
.fg(Color::Rgb(80, 110, 140))
.add_modifier(Modifier::ITALIC),
bold: Style::default()
.fg(Color::Rgb(0, 200, 200))
.add_modifier(Modifier::BOLD),
italic: Style::default()
.fg(Color::Rgb(230, 240, 250))
.add_modifier(Modifier::ITALIC),
strikethrough: Style::default()
.fg(Color::Rgb(140, 170, 200))
.add_modifier(Modifier::CROSSED_OUT),
code_inline: Style::default()
.fg(Color::Rgb(100, 180, 255))
.bg(Color::Rgb(25, 50, 80)),
code_block_bg: Color::Rgb(15, 35, 60),
code_block_border: Color::Rgb(40, 80, 120),
code_block_text: Style::default().fg(Color::Rgb(230, 240, 250)),
code_lang_tag: Style::default()
.fg(Color::Rgb(100, 180, 255))
.add_modifier(Modifier::ITALIC),
blockquote_border: Color::Rgb(0, 180, 180),
blockquote_text: Style::default()
.fg(Color::Rgb(140, 170, 200))
.add_modifier(Modifier::ITALIC),
list_bullet: Style::default().fg(Color::Rgb(0, 200, 200)),
list_number: Style::default().fg(Color::Rgb(0, 200, 200)),
task_checked: Style::default().fg(Color::Rgb(0, 220, 180)),
task_unchecked: Style::default().fg(Color::Rgb(80, 110, 140)),
table_border: Color::Rgb(0, 200, 200),
table_header_bg: Color::Rgb(25, 50, 80),
table_header_text: Style::default()
.fg(Color::Rgb(230, 240, 250))
.add_modifier(Modifier::BOLD),
table_cell_text: Style::default().fg(Color::Rgb(230, 240, 250)),
link_text: Style::default()
.fg(Color::Rgb(100, 180, 255))
.add_modifier(Modifier::UNDERLINED),
link_url: Style::default().fg(Color::Rgb(80, 110, 140)),
hr: Style::default().fg(Color::Rgb(40, 80, 120)),
text: Style::default().fg(Color::Rgb(230, 240, 250)),
}
}

/// Create a markdown theme for the "monokai" theme
pub fn monokai() -> Self {
Self {
h1: Style::default()
.fg(Color::Rgb(166, 226, 46)) // Monokai green
.add_modifier(Modifier::BOLD | Modifier::UNDERLINED),
h2: Style::default()
.fg(Color::Rgb(166, 226, 46))
.add_modifier(Modifier::BOLD),
h3: Style::default()
.fg(Color::Rgb(102, 217, 239)) // Monokai cyan
.add_modifier(Modifier::BOLD | Modifier::ITALIC),
h4: Style::default()
.fg(Color::Rgb(102, 217, 239))
.add_modifier(Modifier::ITALIC),
h5: Style::default()
.fg(Color::Rgb(180, 180, 170))
.add_modifier(Modifier::ITALIC),
h6: Style::default()
.fg(Color::Rgb(117, 113, 94)) // Monokai comment
.add_modifier(Modifier::ITALIC),
bold: Style::default()
.fg(Color::Rgb(249, 38, 114)) // Monokai pink
.add_modifier(Modifier::BOLD),
italic: Style::default()
.fg(Color::Rgb(248, 248, 242)) // Monokai white
.add_modifier(Modifier::ITALIC),
strikethrough: Style::default()
.fg(Color::Rgb(117, 113, 94))
.add_modifier(Modifier::CROSSED_OUT),
code_inline: Style::default()
.fg(Color::Rgb(230, 219, 116)) // Monokai yellow
.bg(Color::Rgb(55, 56, 50)),
code_block_bg: Color::Rgb(45, 46, 40),
code_block_border: Color::Rgb(70, 71, 65),
code_block_text: Style::default().fg(Color::Rgb(248, 248, 242)),
code_lang_tag: Style::default()
.fg(Color::Rgb(102, 217, 239))
.add_modifier(Modifier::ITALIC),
blockquote_border: Color::Rgb(166, 226, 46),
blockquote_text: Style::default()
.fg(Color::Rgb(117, 113, 94))
.add_modifier(Modifier::ITALIC),
list_bullet: Style::default().fg(Color::Rgb(249, 38, 114)),
list_number: Style::default().fg(Color::Rgb(249, 38, 114)),
task_checked: Style::default().fg(Color::Rgb(166, 226, 46)),
task_unchecked: Style::default().fg(Color::Rgb(117, 113, 94)),
table_border: Color::Rgb(166, 226, 46),
table_header_bg: Color::Rgb(55, 56, 50),
table_header_text: Style::default()
.fg(Color::Rgb(248, 248, 242))
.add_modifier(Modifier::BOLD),
table_cell_text: Style::default().fg(Color::Rgb(248, 248, 242)),
link_text: Style::default()
.fg(Color::Rgb(102, 217, 239))
.add_modifier(Modifier::UNDERLINED),
link_url: Style::default().fg(Color::Rgb(117, 113, 94)),
hr: Style::default().fg(Color::Rgb(70, 71, 65)),
text: Style::default().fg(Color::Rgb(248, 248, 242)),
}
}

/// Create a markdown theme from a theme name
pub fn from_name(name: &str) -> Self {
match name.to_lowercase().as_str() {
"light" => Self::light(),
"ocean_dark" | "ocean" => Self::ocean_dark(),
"monokai" => Self::monokai(),
"dark" | _ => Self::dark(),
}
}
}

impl Default for MarkdownTheme {
fn default() -> Self {
Self {
Expand Down Expand Up @@ -902,4 +1105,105 @@ mod tests {
assert!(theme.hr.fg.is_some());
assert!(theme.text.fg.is_some());
}

#[test]
fn test_dark_theme() {
let theme = MarkdownTheme::dark();
let default_theme = MarkdownTheme::default();

// dark() should return the same as default()
assert_eq!(theme.h1.fg, default_theme.h1.fg);
assert_eq!(theme.bold.fg, default_theme.bold.fg);
assert_eq!(theme.code_block_bg, default_theme.code_block_bg);
}

#[test]
fn test_light_theme() {
let theme = MarkdownTheme::light();

// Light theme has different colors than dark
assert_eq!(theme.h1.fg, Some(Color::Rgb(0, 100, 70)));
assert_eq!(theme.text.fg, Some(Color::Rgb(30, 30, 30)));
assert_eq!(theme.code_block_bg, Color::Rgb(245, 245, 245));
}

#[test]
fn test_ocean_dark_theme() {
let theme = MarkdownTheme::ocean_dark();

// Ocean dark theme has cyan accent
assert_eq!(theme.h1.fg, Some(Color::Rgb(0, 200, 200)));
assert_eq!(theme.text.fg, Some(Color::Rgb(230, 240, 250)));
assert_eq!(theme.code_block_bg, Color::Rgb(15, 35, 60));
}

#[test]
fn test_monokai_theme() {
let theme = MarkdownTheme::monokai();

// Monokai theme has distinctive green headers
assert_eq!(theme.h1.fg, Some(Color::Rgb(166, 226, 46)));
assert_eq!(theme.bold.fg, Some(Color::Rgb(249, 38, 114))); // Monokai pink
assert_eq!(theme.code_block_bg, Color::Rgb(45, 46, 40));
}

#[test]
fn test_from_name() {
// Test all valid theme names
let dark = MarkdownTheme::from_name("dark");
assert_eq!(dark.h1.fg, MarkdownTheme::dark().h1.fg);

let light = MarkdownTheme::from_name("light");
assert_eq!(light.h1.fg, MarkdownTheme::light().h1.fg);

let ocean = MarkdownTheme::from_name("ocean_dark");
assert_eq!(ocean.h1.fg, MarkdownTheme::ocean_dark().h1.fg);

let ocean_short = MarkdownTheme::from_name("ocean");
assert_eq!(ocean_short.h1.fg, MarkdownTheme::ocean_dark().h1.fg);

let monokai = MarkdownTheme::from_name("monokai");
assert_eq!(monokai.h1.fg, MarkdownTheme::monokai().h1.fg);
}

#[test]
fn test_from_name_case_insensitive() {
// Should be case insensitive
let dark_upper = MarkdownTheme::from_name("DARK");
let dark_lower = MarkdownTheme::from_name("dark");
let dark_mixed = MarkdownTheme::from_name("DaRk");

assert_eq!(dark_upper.h1.fg, dark_lower.h1.fg);
assert_eq!(dark_mixed.h1.fg, dark_lower.h1.fg);
}

#[test]
fn test_from_name_fallback() {
// Unknown names should fall back to dark
let unknown = MarkdownTheme::from_name("unknown");
let dark = MarkdownTheme::dark();

assert_eq!(unknown.h1.fg, dark.h1.fg);
}

#[test]
fn test_all_theme_colors_are_set() {
// Verify all theme variants have colors set
for theme in [
MarkdownTheme::dark(),
MarkdownTheme::light(),
MarkdownTheme::ocean_dark(),
MarkdownTheme::monokai(),
] {
assert!(theme.h1.fg.is_some());
assert!(theme.h2.fg.is_some());
assert!(theme.h3.fg.is_some());
assert!(theme.h4.fg.is_some());
assert!(theme.h5.fg.is_some());
assert!(theme.h6.fg.is_some());
assert!(theme.bold.fg.is_some());
assert!(theme.italic.fg.is_some());
assert!(theme.text.fg.is_some());
}
}
}
Loading
Loading