@@ -380,6 +380,209 @@ impl MarkdownTheme {
380380 }
381381}
382382
383+ impl MarkdownTheme {
384+ /// Create a markdown theme for the "dark" theme (same as default)
385+ pub fn dark ( ) -> Self {
386+ Self :: default ( )
387+ }
388+
389+ /// Create a markdown theme for the "light" theme
390+ pub fn light ( ) -> Self {
391+ Self {
392+ h1 : Style :: default ( )
393+ . fg ( Color :: Rgb ( 0 , 100 , 70 ) )
394+ . add_modifier ( Modifier :: BOLD | Modifier :: UNDERLINED ) ,
395+ h2 : Style :: default ( )
396+ . fg ( Color :: Rgb ( 0 , 100 , 70 ) )
397+ . add_modifier ( Modifier :: BOLD ) ,
398+ h3 : Style :: default ( )
399+ . fg ( Color :: Rgb ( 0 , 80 , 60 ) )
400+ . add_modifier ( Modifier :: BOLD | Modifier :: ITALIC ) ,
401+ h4 : Style :: default ( )
402+ . fg ( Color :: Rgb ( 0 , 80 , 60 ) )
403+ . add_modifier ( Modifier :: ITALIC ) ,
404+ h5 : Style :: default ( )
405+ . fg ( Color :: Rgb ( 80 , 80 , 80 ) )
406+ . add_modifier ( Modifier :: ITALIC ) ,
407+ h6 : Style :: default ( )
408+ . fg ( Color :: Rgb ( 120 , 120 , 120 ) )
409+ . add_modifier ( Modifier :: ITALIC ) ,
410+ bold : Style :: default ( )
411+ . fg ( Color :: Rgb ( 0 , 100 , 70 ) )
412+ . add_modifier ( Modifier :: BOLD ) ,
413+ italic : Style :: default ( )
414+ . fg ( Color :: Rgb ( 30 , 30 , 30 ) )
415+ . add_modifier ( Modifier :: ITALIC ) ,
416+ strikethrough : Style :: default ( )
417+ . fg ( Color :: Rgb ( 100 , 100 , 100 ) )
418+ . add_modifier ( Modifier :: CROSSED_OUT ) ,
419+ code_inline : Style :: default ( )
420+ . fg ( Color :: Rgb ( 0 , 80 , 60 ) )
421+ . bg ( Color :: Rgb ( 235 , 235 , 235 ) ) ,
422+ code_block_bg : Color :: Rgb ( 245 , 245 , 245 ) ,
423+ code_block_border : Color :: Rgb ( 200 , 200 , 200 ) ,
424+ code_block_text : Style :: default ( ) . fg ( Color :: Rgb ( 30 , 30 , 30 ) ) ,
425+ code_lang_tag : Style :: default ( )
426+ . fg ( Color :: Rgb ( 50 , 100 , 200 ) )
427+ . add_modifier ( Modifier :: ITALIC ) ,
428+ blockquote_border : Color :: Rgb ( 0 , 100 , 70 ) ,
429+ blockquote_text : Style :: default ( )
430+ . fg ( Color :: Rgb ( 80 , 80 , 80 ) )
431+ . add_modifier ( Modifier :: ITALIC ) ,
432+ list_bullet : Style :: default ( ) . fg ( Color :: Rgb ( 0 , 100 , 70 ) ) ,
433+ list_number : Style :: default ( ) . fg ( Color :: Rgb ( 0 , 100 , 70 ) ) ,
434+ task_checked : Style :: default ( ) . fg ( Color :: Rgb ( 0 , 150 , 0 ) ) ,
435+ task_unchecked : Style :: default ( ) . fg ( Color :: Rgb ( 120 , 120 , 120 ) ) ,
436+ table_border : Color :: Rgb ( 0 , 100 , 70 ) ,
437+ table_header_bg : Color :: Rgb ( 235 , 235 , 235 ) ,
438+ table_header_text : Style :: default ( )
439+ . fg ( Color :: Rgb ( 30 , 30 , 30 ) )
440+ . add_modifier ( Modifier :: BOLD ) ,
441+ table_cell_text : Style :: default ( ) . fg ( Color :: Rgb ( 30 , 30 , 30 ) ) ,
442+ link_text : Style :: default ( )
443+ . fg ( Color :: Rgb ( 50 , 100 , 200 ) )
444+ . add_modifier ( Modifier :: UNDERLINED ) ,
445+ link_url : Style :: default ( ) . fg ( Color :: Rgb ( 120 , 120 , 120 ) ) ,
446+ hr : Style :: default ( ) . fg ( Color :: Rgb ( 200 , 200 , 200 ) ) ,
447+ text : Style :: default ( ) . fg ( Color :: Rgb ( 30 , 30 , 30 ) ) ,
448+ }
449+ }
450+
451+ /// Create a markdown theme for the "ocean_dark" theme
452+ pub fn ocean_dark ( ) -> Self {
453+ Self {
454+ h1 : Style :: default ( )
455+ . fg ( Color :: Rgb ( 0 , 200 , 200 ) )
456+ . add_modifier ( Modifier :: BOLD | Modifier :: UNDERLINED ) ,
457+ h2 : Style :: default ( )
458+ . fg ( Color :: Rgb ( 0 , 200 , 200 ) )
459+ . add_modifier ( Modifier :: BOLD ) ,
460+ h3 : Style :: default ( )
461+ . fg ( Color :: Rgb ( 100 , 200 , 220 ) )
462+ . add_modifier ( Modifier :: BOLD | Modifier :: ITALIC ) ,
463+ h4 : Style :: default ( )
464+ . fg ( Color :: Rgb ( 100 , 200 , 220 ) )
465+ . add_modifier ( Modifier :: ITALIC ) ,
466+ h5 : Style :: default ( )
467+ . fg ( Color :: Rgb ( 140 , 170 , 200 ) )
468+ . add_modifier ( Modifier :: ITALIC ) ,
469+ h6 : Style :: default ( )
470+ . fg ( Color :: Rgb ( 80 , 110 , 140 ) )
471+ . add_modifier ( Modifier :: ITALIC ) ,
472+ bold : Style :: default ( )
473+ . fg ( Color :: Rgb ( 0 , 200 , 200 ) )
474+ . add_modifier ( Modifier :: BOLD ) ,
475+ italic : Style :: default ( )
476+ . fg ( Color :: Rgb ( 230 , 240 , 250 ) )
477+ . add_modifier ( Modifier :: ITALIC ) ,
478+ strikethrough : Style :: default ( )
479+ . fg ( Color :: Rgb ( 140 , 170 , 200 ) )
480+ . add_modifier ( Modifier :: CROSSED_OUT ) ,
481+ code_inline : Style :: default ( )
482+ . fg ( Color :: Rgb ( 100 , 180 , 255 ) )
483+ . bg ( Color :: Rgb ( 25 , 50 , 80 ) ) ,
484+ code_block_bg : Color :: Rgb ( 15 , 35 , 60 ) ,
485+ code_block_border : Color :: Rgb ( 40 , 80 , 120 ) ,
486+ code_block_text : Style :: default ( ) . fg ( Color :: Rgb ( 230 , 240 , 250 ) ) ,
487+ code_lang_tag : Style :: default ( )
488+ . fg ( Color :: Rgb ( 100 , 180 , 255 ) )
489+ . add_modifier ( Modifier :: ITALIC ) ,
490+ blockquote_border : Color :: Rgb ( 0 , 180 , 180 ) ,
491+ blockquote_text : Style :: default ( )
492+ . fg ( Color :: Rgb ( 140 , 170 , 200 ) )
493+ . add_modifier ( Modifier :: ITALIC ) ,
494+ list_bullet : Style :: default ( ) . fg ( Color :: Rgb ( 0 , 200 , 200 ) ) ,
495+ list_number : Style :: default ( ) . fg ( Color :: Rgb ( 0 , 200 , 200 ) ) ,
496+ task_checked : Style :: default ( ) . fg ( Color :: Rgb ( 0 , 220 , 180 ) ) ,
497+ task_unchecked : Style :: default ( ) . fg ( Color :: Rgb ( 80 , 110 , 140 ) ) ,
498+ table_border : Color :: Rgb ( 0 , 200 , 200 ) ,
499+ table_header_bg : Color :: Rgb ( 25 , 50 , 80 ) ,
500+ table_header_text : Style :: default ( )
501+ . fg ( Color :: Rgb ( 230 , 240 , 250 ) )
502+ . add_modifier ( Modifier :: BOLD ) ,
503+ table_cell_text : Style :: default ( ) . fg ( Color :: Rgb ( 230 , 240 , 250 ) ) ,
504+ link_text : Style :: default ( )
505+ . fg ( Color :: Rgb ( 100 , 180 , 255 ) )
506+ . add_modifier ( Modifier :: UNDERLINED ) ,
507+ link_url : Style :: default ( ) . fg ( Color :: Rgb ( 80 , 110 , 140 ) ) ,
508+ hr : Style :: default ( ) . fg ( Color :: Rgb ( 40 , 80 , 120 ) ) ,
509+ text : Style :: default ( ) . fg ( Color :: Rgb ( 230 , 240 , 250 ) ) ,
510+ }
511+ }
512+
513+ /// Create a markdown theme for the "monokai" theme
514+ pub fn monokai ( ) -> Self {
515+ Self {
516+ h1 : Style :: default ( )
517+ . fg ( Color :: Rgb ( 166 , 226 , 46 ) ) // Monokai green
518+ . add_modifier ( Modifier :: BOLD | Modifier :: UNDERLINED ) ,
519+ h2 : Style :: default ( )
520+ . fg ( Color :: Rgb ( 166 , 226 , 46 ) )
521+ . add_modifier ( Modifier :: BOLD ) ,
522+ h3 : Style :: default ( )
523+ . fg ( Color :: Rgb ( 102 , 217 , 239 ) ) // Monokai cyan
524+ . add_modifier ( Modifier :: BOLD | Modifier :: ITALIC ) ,
525+ h4 : Style :: default ( )
526+ . fg ( Color :: Rgb ( 102 , 217 , 239 ) )
527+ . add_modifier ( Modifier :: ITALIC ) ,
528+ h5 : Style :: default ( )
529+ . fg ( Color :: Rgb ( 180 , 180 , 170 ) )
530+ . add_modifier ( Modifier :: ITALIC ) ,
531+ h6 : Style :: default ( )
532+ . fg ( Color :: Rgb ( 117 , 113 , 94 ) ) // Monokai comment
533+ . add_modifier ( Modifier :: ITALIC ) ,
534+ bold : Style :: default ( )
535+ . fg ( Color :: Rgb ( 249 , 38 , 114 ) ) // Monokai pink
536+ . add_modifier ( Modifier :: BOLD ) ,
537+ italic : Style :: default ( )
538+ . fg ( Color :: Rgb ( 248 , 248 , 242 ) ) // Monokai white
539+ . add_modifier ( Modifier :: ITALIC ) ,
540+ strikethrough : Style :: default ( )
541+ . fg ( Color :: Rgb ( 117 , 113 , 94 ) )
542+ . add_modifier ( Modifier :: CROSSED_OUT ) ,
543+ code_inline : Style :: default ( )
544+ . fg ( Color :: Rgb ( 230 , 219 , 116 ) ) // Monokai yellow
545+ . bg ( Color :: Rgb ( 55 , 56 , 50 ) ) ,
546+ code_block_bg : Color :: Rgb ( 45 , 46 , 40 ) ,
547+ code_block_border : Color :: Rgb ( 70 , 71 , 65 ) ,
548+ code_block_text : Style :: default ( ) . fg ( Color :: Rgb ( 248 , 248 , 242 ) ) ,
549+ code_lang_tag : Style :: default ( )
550+ . fg ( Color :: Rgb ( 102 , 217 , 239 ) )
551+ . add_modifier ( Modifier :: ITALIC ) ,
552+ blockquote_border : Color :: Rgb ( 166 , 226 , 46 ) ,
553+ blockquote_text : Style :: default ( )
554+ . fg ( Color :: Rgb ( 117 , 113 , 94 ) )
555+ . add_modifier ( Modifier :: ITALIC ) ,
556+ list_bullet : Style :: default ( ) . fg ( Color :: Rgb ( 249 , 38 , 114 ) ) ,
557+ list_number : Style :: default ( ) . fg ( Color :: Rgb ( 249 , 38 , 114 ) ) ,
558+ task_checked : Style :: default ( ) . fg ( Color :: Rgb ( 166 , 226 , 46 ) ) ,
559+ task_unchecked : Style :: default ( ) . fg ( Color :: Rgb ( 117 , 113 , 94 ) ) ,
560+ table_border : Color :: Rgb ( 166 , 226 , 46 ) ,
561+ table_header_bg : Color :: Rgb ( 55 , 56 , 50 ) ,
562+ table_header_text : Style :: default ( )
563+ . fg ( Color :: Rgb ( 248 , 248 , 242 ) )
564+ . add_modifier ( Modifier :: BOLD ) ,
565+ table_cell_text : Style :: default ( ) . fg ( Color :: Rgb ( 248 , 248 , 242 ) ) ,
566+ link_text : Style :: default ( )
567+ . fg ( Color :: Rgb ( 102 , 217 , 239 ) )
568+ . add_modifier ( Modifier :: UNDERLINED ) ,
569+ link_url : Style :: default ( ) . fg ( Color :: Rgb ( 117 , 113 , 94 ) ) ,
570+ hr : Style :: default ( ) . fg ( Color :: Rgb ( 70 , 71 , 65 ) ) ,
571+ text : Style :: default ( ) . fg ( Color :: Rgb ( 248 , 248 , 242 ) ) ,
572+ }
573+ }
574+
575+ /// Create a markdown theme from a theme name
576+ pub fn from_name ( name : & str ) -> Self {
577+ match name. to_lowercase ( ) . as_str ( ) {
578+ "light" => Self :: light ( ) ,
579+ "ocean_dark" | "ocean" => Self :: ocean_dark ( ) ,
580+ "monokai" => Self :: monokai ( ) ,
581+ "dark" | _ => Self :: dark ( ) ,
582+ }
583+ }
584+ }
585+
383586impl Default for MarkdownTheme {
384587 fn default ( ) -> Self {
385588 Self {
@@ -902,4 +1105,105 @@ mod tests {
9021105 assert ! ( theme. hr. fg. is_some( ) ) ;
9031106 assert ! ( theme. text. fg. is_some( ) ) ;
9041107 }
1108+
1109+ #[ test]
1110+ fn test_dark_theme ( ) {
1111+ let theme = MarkdownTheme :: dark ( ) ;
1112+ let default_theme = MarkdownTheme :: default ( ) ;
1113+
1114+ // dark() should return the same as default()
1115+ assert_eq ! ( theme. h1. fg, default_theme. h1. fg) ;
1116+ assert_eq ! ( theme. bold. fg, default_theme. bold. fg) ;
1117+ assert_eq ! ( theme. code_block_bg, default_theme. code_block_bg) ;
1118+ }
1119+
1120+ #[ test]
1121+ fn test_light_theme ( ) {
1122+ let theme = MarkdownTheme :: light ( ) ;
1123+
1124+ // Light theme has different colors than dark
1125+ assert_eq ! ( theme. h1. fg, Some ( Color :: Rgb ( 0 , 100 , 70 ) ) ) ;
1126+ assert_eq ! ( theme. text. fg, Some ( Color :: Rgb ( 30 , 30 , 30 ) ) ) ;
1127+ assert_eq ! ( theme. code_block_bg, Color :: Rgb ( 245 , 245 , 245 ) ) ;
1128+ }
1129+
1130+ #[ test]
1131+ fn test_ocean_dark_theme ( ) {
1132+ let theme = MarkdownTheme :: ocean_dark ( ) ;
1133+
1134+ // Ocean dark theme has cyan accent
1135+ assert_eq ! ( theme. h1. fg, Some ( Color :: Rgb ( 0 , 200 , 200 ) ) ) ;
1136+ assert_eq ! ( theme. text. fg, Some ( Color :: Rgb ( 230 , 240 , 250 ) ) ) ;
1137+ assert_eq ! ( theme. code_block_bg, Color :: Rgb ( 15 , 35 , 60 ) ) ;
1138+ }
1139+
1140+ #[ test]
1141+ fn test_monokai_theme ( ) {
1142+ let theme = MarkdownTheme :: monokai ( ) ;
1143+
1144+ // Monokai theme has distinctive green headers
1145+ assert_eq ! ( theme. h1. fg, Some ( Color :: Rgb ( 166 , 226 , 46 ) ) ) ;
1146+ assert_eq ! ( theme. bold. fg, Some ( Color :: Rgb ( 249 , 38 , 114 ) ) ) ; // Monokai pink
1147+ assert_eq ! ( theme. code_block_bg, Color :: Rgb ( 45 , 46 , 40 ) ) ;
1148+ }
1149+
1150+ #[ test]
1151+ fn test_from_name ( ) {
1152+ // Test all valid theme names
1153+ let dark = MarkdownTheme :: from_name ( "dark" ) ;
1154+ assert_eq ! ( dark. h1. fg, MarkdownTheme :: dark( ) . h1. fg) ;
1155+
1156+ let light = MarkdownTheme :: from_name ( "light" ) ;
1157+ assert_eq ! ( light. h1. fg, MarkdownTheme :: light( ) . h1. fg) ;
1158+
1159+ let ocean = MarkdownTheme :: from_name ( "ocean_dark" ) ;
1160+ assert_eq ! ( ocean. h1. fg, MarkdownTheme :: ocean_dark( ) . h1. fg) ;
1161+
1162+ let ocean_short = MarkdownTheme :: from_name ( "ocean" ) ;
1163+ assert_eq ! ( ocean_short. h1. fg, MarkdownTheme :: ocean_dark( ) . h1. fg) ;
1164+
1165+ let monokai = MarkdownTheme :: from_name ( "monokai" ) ;
1166+ assert_eq ! ( monokai. h1. fg, MarkdownTheme :: monokai( ) . h1. fg) ;
1167+ }
1168+
1169+ #[ test]
1170+ fn test_from_name_case_insensitive ( ) {
1171+ // Should be case insensitive
1172+ let dark_upper = MarkdownTheme :: from_name ( "DARK" ) ;
1173+ let dark_lower = MarkdownTheme :: from_name ( "dark" ) ;
1174+ let dark_mixed = MarkdownTheme :: from_name ( "DaRk" ) ;
1175+
1176+ assert_eq ! ( dark_upper. h1. fg, dark_lower. h1. fg) ;
1177+ assert_eq ! ( dark_mixed. h1. fg, dark_lower. h1. fg) ;
1178+ }
1179+
1180+ #[ test]
1181+ fn test_from_name_fallback ( ) {
1182+ // Unknown names should fall back to dark
1183+ let unknown = MarkdownTheme :: from_name ( "unknown" ) ;
1184+ let dark = MarkdownTheme :: dark ( ) ;
1185+
1186+ assert_eq ! ( unknown. h1. fg, dark. h1. fg) ;
1187+ }
1188+
1189+ #[ test]
1190+ fn test_all_theme_colors_are_set ( ) {
1191+ // Verify all theme variants have colors set
1192+ for theme in [
1193+ MarkdownTheme :: dark ( ) ,
1194+ MarkdownTheme :: light ( ) ,
1195+ MarkdownTheme :: ocean_dark ( ) ,
1196+ MarkdownTheme :: monokai ( ) ,
1197+ ] {
1198+ assert ! ( theme. h1. fg. is_some( ) ) ;
1199+ assert ! ( theme. h2. fg. is_some( ) ) ;
1200+ assert ! ( theme. h3. fg. is_some( ) ) ;
1201+ assert ! ( theme. h4. fg. is_some( ) ) ;
1202+ assert ! ( theme. h5. fg. is_some( ) ) ;
1203+ assert ! ( theme. h6. fg. is_some( ) ) ;
1204+ assert ! ( theme. bold. fg. is_some( ) ) ;
1205+ assert ! ( theme. italic. fg. is_some( ) ) ;
1206+ assert ! ( theme. text. fg. is_some( ) ) ;
1207+ }
1208+ }
9051209}
0 commit comments