1use std::collections::HashMap;
2
3#[derive(Debug, Clone)]
5pub struct ThemeOptions {
6 pub name: String,
8 pub variables: HashMap<String, String>,
10}
11
12impl Default for ThemeOptions {
13 fn default() -> Self {
14 Self {
15 name: "modern".to_string(),
16 variables: HashMap::new(),
17 }
18 }
19}
20
21pub struct Theme {
23 name: String,
24 variables: HashMap<String, String>,
25}
26
27impl Theme {
28 pub fn new(name: &str) -> Self {
30 let mut theme = Self {
31 name: name.to_string(),
32 variables: HashMap::new(),
33 };
34
35 theme.set_default_variables();
37
38 theme
39 }
40
41 fn set_default_variables(&mut self) {
43 let defaults = match self.name.as_str() {
44 "modern" => get_modern_theme_variables(),
45 "minimal" => get_minimal_theme_variables(),
46 "dark" => get_dark_theme_variables(),
47 "light" => get_light_theme_variables(),
48 _ => get_modern_theme_variables(), };
50
51 for (key, value) in defaults {
52 self.variables.insert(key.to_string(), value.to_string());
53 }
54 }
55
56 pub fn get_css(&self) -> String {
58 let mut css = String::from(":root {\n");
59
60 for (key, value) in &self.variables {
62 css.push_str(&format!(" --{}: {};\n", key, value));
63 }
64
65 css.push_str("}\n\n");
66
67 match self.name.as_str() {
69 "modern" => css.push_str(MODERN_THEME_CSS),
70 "minimal" => css.push_str(MINIMAL_THEME_CSS),
71 "dark" => css.push_str(DARK_THEME_CSS),
72 "light" => css.push_str(LIGHT_THEME_CSS),
73 _ => css.push_str(MODERN_THEME_CSS), }
75
76 css
77 }
78
79 pub fn set_variable(&mut self, key: &str, value: &str) {
81 self.variables.insert(key.to_string(), value.to_string());
82 }
83
84 pub fn get_variable(&self, key: &str) -> Option<&String> {
86 self.variables.get(key)
87 }
88}
89
90fn get_modern_theme_variables() -> Vec<(&'static str, &'static str)> {
92 vec![
93 ("primary-color", "#3b82f6"),
94 ("secondary-color", "#6b7280"),
95 ("background-color", "#ffffff"),
96 ("text-color", "#1f2937"),
97 ("muted-color", "#6b7280"),
98 ("border-color", "#e5e7eb"),
99 ("font-family", "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif"),
100 ("monospace-font", "'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace"),
101 ("content-width", "768px"),
102 ("info-color", "#3b82f6"),
103 ("info-light-color", "#eff6ff"),
104 ("warning-color", "#f59e0b"),
105 ("warning-light-color", "#fffbeb"),
106 ("error-color", "#ef4444"),
107 ("error-light-color", "#fee2e2"),
108 ("success-color", "#10b981"),
109 ("success-light-color", "#ecfdf5"),
110 ]
111}
112
113fn get_minimal_theme_variables() -> Vec<(&'static str, &'static str)> {
115 vec![
116 ("primary-color", "#000000"),
117 ("secondary-color", "#4b5563"),
118 ("background-color", "#ffffff"),
119 ("text-color", "#000000"),
120 ("muted-color", "#4b5563"),
121 ("border-color", "#e5e7eb"),
122 ("font-family", "Georgia, serif"),
123 (
124 "monospace-font",
125 "'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace",
126 ),
127 ("content-width", "700px"),
128 ("info-color", "#000000"),
129 ("info-light-color", "#f3f4f6"),
130 ("warning-color", "#000000"),
131 ("warning-light-color", "#f3f4f6"),
132 ("error-color", "#000000"),
133 ("error-light-color", "#f3f4f6"),
134 ("success-color", "#000000"),
135 ("success-light-color", "#f3f4f6"),
136 ]
137}
138
139fn get_dark_theme_variables() -> Vec<(&'static str, &'static str)> {
141 vec![
142 ("primary-color", "#3b82f6"),
143 ("secondary-color", "#9ca3af"),
144 ("background-color", "#111827"),
145 ("text-color", "#f9fafb"),
146 ("muted-color", "#9ca3af"),
147 ("border-color", "#374151"),
148 ("font-family", "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif"),
149 ("monospace-font", "'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace"),
150 ("content-width", "768px"),
151 ("info-color", "#3b82f6"),
152 ("info-light-color", "#1e3a8a"),
153 ("warning-color", "#f59e0b"),
154 ("warning-light-color", "#78350f"),
155 ("error-color", "#ef4444"),
156 ("error-light-color", "#7f1d1d"),
157 ("success-color", "#10b981"),
158 ("success-light-color", "#064e3b"),
159 ]
160}
161
162fn get_light_theme_variables() -> Vec<(&'static str, &'static str)> {
164 vec![
165 ("primary-color", "#0284c7"),
166 ("secondary-color", "#64748b"),
167 ("background-color", "#f8fafc"),
168 ("text-color", "#334155"),
169 ("muted-color", "#64748b"),
170 ("border-color", "#e2e8f0"),
171 ("font-family", "system-ui, -apple-system, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif"),
172 ("monospace-font", "ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace"),
173 ("content-width", "768px"),
174 ("info-color", "#0284c7"),
175 ("info-light-color", "#e0f2fe"),
176 ("warning-color", "#ea580c"),
177 ("warning-light-color", "#fff7ed"),
178 ("error-color", "#dc2626"),
179 ("error-light-color", "#fee2e2"),
180 ("success-color", "#16a34a"),
181 ("success-light-color", "#f0fdf4"),
182 ]
183}
184
185const MODERN_THEME_CSS: &str = r#"
187/* Modern theme */
188body {
189 font-family: var(--font-family);
190 color: var(--text-color);
191 background-color: var(--background-color);
192 line-height: 1.6;
193 margin: 0;
194 padding: 0;
195}
196
197.markrust-container {
198 max-width: var(--content-width);
199 margin: 0 auto;
200 padding: 2rem 1rem;
201}
202
203.markrust-content {
204 margin-top: 2rem;
205}
206
207h1, h2, h3, h4, h5, h6 {
208 margin-top: 2rem;
209 margin-bottom: 1rem;
210 font-weight: 600;
211 line-height: 1.25;
212}
213
214h1 {
215 font-size: 2.25rem;
216 border-bottom: 1px solid var(--border-color);
217 padding-bottom: 0.5rem;
218}
219
220h2 {
221 font-size: 1.8rem;
222}
223
224h3 {
225 font-size: 1.5rem;
226}
227
228h4 {
229 font-size: 1.25rem;
230}
231
232h5 {
233 font-size: 1rem;
234}
235
236h6 {
237 font-size: 0.875rem;
238}
239
240a {
241 color: var(--primary-color);
242 text-decoration: none;
243}
244
245a:hover {
246 text-decoration: underline;
247}
248
249p {
250 margin-top: 0;
251 margin-bottom: 1rem;
252}
253
254ul, ol {
255 margin-top: 0;
256 margin-bottom: 1rem;
257 padding-left: 2rem;
258}
259
260li {
261 margin-bottom: 0.25rem;
262}
263
264blockquote {
265 margin: 1rem 0;
266 padding: 0.5rem 1rem;
267 border-left: 4px solid var(--primary-color);
268 background-color: rgba(0, 0, 0, 0.05);
269}
270
271hr {
272 border: 0;
273 height: 1px;
274 background-color: var(--border-color);
275 margin: 2rem 0;
276}
277
278code {
279 font-family: var(--monospace-font);
280 font-size: 0.9em;
281 background-color: rgba(0, 0, 0, 0.05);
282 padding: 0.2em 0.4em;
283 border-radius: 3px;
284}
285
286pre {
287 margin: 1rem 0;
288 padding: 1rem;
289 overflow-x: auto;
290 background-color: rgba(0, 0, 0, 0.05);
291 border-radius: 0.375rem;
292}
293
294pre code {
295 background: none;
296 padding: 0;
297 font-size: 0.9em;
298 color: inherit;
299}
300
301/* Code block with copy button */
302.markrust-code-block {
303 position: relative;
304}
305
306.markrust-copy-button {
307 position: absolute;
308 top: 0.5rem;
309 right: 0.5rem;
310 padding: 0.25rem;
311 background-color: rgba(255, 255, 255, 0.2);
312 border: none;
313 border-radius: 0.25rem;
314 color: var(--text-color);
315 cursor: pointer;
316 opacity: 0.6;
317 transition: opacity 0.2s;
318}
319
320.markrust-copy-button:hover {
321 opacity: 1;
322}
323
324/* Table styles */
325.markrust-table {
326 border-collapse: collapse;
327 width: 100%;
328 margin: 1rem 0;
329}
330
331.markrust-table th,
332.markrust-table td {
333 border: 1px solid var(--border-color);
334 padding: 0.5rem 0.75rem;
335 text-align: left;
336}
337
338.markrust-table th {
339 background-color: rgba(0, 0, 0, 0.05);
340 font-weight: 600;
341}
342
343.markrust-table .align-center {
344 text-align: center;
345}
346
347.markrust-table .align-right {
348 text-align: right;
349}
350
351/* Table of contents */
352.markrust-toc {
353 background-color: rgba(0, 0, 0, 0.03);
354 border-radius: 0.375rem;
355 padding: 1rem;
356 margin-bottom: 2rem;
357}
358
359.markrust-toc-header {
360 font-weight: 600;
361 margin-bottom: 0.5rem;
362}
363
364.markrust-toc ul {
365 list-style-type: none;
366 padding-left: 0;
367 margin-bottom: 0;
368}
369
370.markrust-toc ul ul {
371 padding-left: 1.5rem;
372}
373
374.markrust-toc li {
375 margin-bottom: 0.25rem;
376}
377
378.markrust-toc a {
379 text-decoration: none;
380 color: var(--text-color);
381}
382
383.markrust-toc a:hover {
384 color: var(--primary-color);
385 text-decoration: underline;
386}
387"#;
388
389const MINIMAL_THEME_CSS: &str = r#"
391/* Minimal theme */
392body {
393 font-family: var(--font-family);
394 color: var(--text-color);
395 background-color: var(--background-color);
396 line-height: 1.7;
397 margin: 0;
398 padding: 0;
399}
400
401.markrust-container {
402 max-width: var(--content-width);
403 margin: 0 auto;
404 padding: 2rem 1rem;
405}
406
407.markrust-content {
408 margin-top: 2rem;
409}
410
411h1, h2, h3, h4, h5, h6 {
412 margin-top: 2.5rem;
413 margin-bottom: 1rem;
414 font-weight: 400;
415 line-height: 1.25;
416 letter-spacing: -0.02em;
417}
418
419h1 {
420 font-size: 2.5rem;
421 border-bottom: 1px solid var(--border-color);
422 padding-bottom: 0.5rem;
423}
424
425h2 {
426 font-size: 2rem;
427}
428
429h3 {
430 font-size: 1.75rem;
431}
432
433h4 {
434 font-size: 1.5rem;
435}
436
437h5 {
438 font-size: 1.25rem;
439}
440
441h6 {
442 font-size: 1rem;
443}
444
445a {
446 color: var(--text-color);
447 text-decoration: underline;
448}
449
450a:hover {
451 color: var(--primary-color);
452}
453
454p {
455 margin-top: 0;
456 margin-bottom: 1.5rem;
457}
458
459ul, ol {
460 margin-top: 0;
461 margin-bottom: 1.5rem;
462 padding-left: 2rem;
463}
464
465li {
466 margin-bottom: 0.5rem;
467}
468
469blockquote {
470 margin: 1.5rem 0;
471 padding: 0 0 0 1.5rem;
472 border-left: 1px solid var(--text-color);
473 font-style: italic;
474}
475
476hr {
477 border: 0;
478 height: 1px;
479 background-color: var(--border-color);
480 margin: 3rem 0;
481}
482
483code {
484 font-family: var(--monospace-font);
485 font-size: 0.85em;
486 background-color: transparent;
487 padding: 0;
488 border-radius: 0;
489}
490
491pre {
492 margin: 1.5rem 0;
493 padding: 1rem;
494 overflow-x: auto;
495 background-color: rgb(245, 245, 245);
496 border-radius: 0;
497}
498
499pre code {
500 background: none;
501 padding: 0;
502 font-size: 0.85em;
503 color: inherit;
504}
505
506/* Table styles */
507.markrust-table {
508 border-collapse: collapse;
509 width: 100%;
510 margin: 1.5rem 0;
511}
512
513.markrust-table th,
514.markrust-table td {
515 border: 1px solid var(--border-color);
516 padding: 0.75rem 1rem;
517 text-align: left;
518}
519
520.markrust-table th {
521 font-weight: 400;
522 border-bottom: 2px solid var(--text-color);
523}
524
525/* Table of contents */
526.markrust-toc {
527 border: 1px solid var(--border-color);
528 padding: 1.5rem;
529 margin-bottom: 2.5rem;
530}
531
532.markrust-toc-header {
533 font-weight: 400;
534 margin-bottom: 1rem;
535 font-size: 1.25rem;
536}
537
538.markrust-toc ul {
539 list-style-type: none;
540 padding-left: 0;
541 margin-bottom: 0;
542}
543
544.markrust-toc ul ul {
545 padding-left: 1.5rem;
546}
547
548.markrust-toc li {
549 margin-bottom: 0.5rem;
550}
551
552.markrust-toc a {
553 text-decoration: none;
554 color: var(--text-color);
555}
556
557.markrust-toc a:hover {
558 text-decoration: underline;
559}
560"#;
561
562const DARK_THEME_CSS: &str = r#"
564/* Dark theme */
565body {
566 font-family: var(--font-family);
567 color: var(--text-color);
568 background-color: var(--background-color);
569 line-height: 1.6;
570 margin: 0;
571 padding: 0;
572}
573
574.markrust-container {
575 max-width: var(--content-width);
576 margin: 0 auto;
577 padding: 2rem 1rem;
578}
579
580.markrust-content {
581 margin-top: 2rem;
582}
583
584h1, h2, h3, h4, h5, h6 {
585 margin-top: 2rem;
586 margin-bottom: 1rem;
587 font-weight: 600;
588 line-height: 1.25;
589 color: var(--primary-color);
590}
591
592h1 {
593 font-size: 2.25rem;
594 border-bottom: 1px solid var(--border-color);
595 padding-bottom: 0.5rem;
596}
597
598h2 {
599 font-size: 1.8rem;
600}
601
602h3 {
603 font-size: 1.5rem;
604}
605
606h4 {
607 font-size: 1.25rem;
608}
609
610h5 {
611 font-size: 1rem;
612}
613
614h6 {
615 font-size: 0.875rem;
616}
617
618a {
619 color: var(--primary-color);
620 text-decoration: none;
621}
622
623a:hover {
624 text-decoration: underline;
625}
626
627p {
628 margin-top: 0;
629 margin-bottom: 1rem;
630}
631
632ul, ol {
633 margin-top: 0;
634 margin-bottom: 1rem;
635 padding-left: 2rem;
636}
637
638li {
639 margin-bottom: 0.25rem;
640}
641
642blockquote {
643 margin: 1rem 0;
644 padding: 0.5rem 1rem;
645 border-left: 4px solid var(--primary-color);
646 background-color: rgba(255, 255, 255, 0.05);
647}
648
649hr {
650 border: 0;
651 height: 1px;
652 background-color: var(--border-color);
653 margin: 2rem 0;
654}
655
656code {
657 font-family: var(--monospace-font);
658 font-size: 0.9em;
659 background-color: rgba(255, 255, 255, 0.1);
660 padding: 0.2em 0.4em;
661 border-radius: 3px;
662}
663
664pre {
665 margin: 1rem 0;
666 padding: 1rem;
667 overflow-x: auto;
668 background-color: rgba(255, 255, 255, 0.1);
669 border-radius: 0.375rem;
670}
671
672pre code {
673 background: none;
674 padding: 0;
675 font-size: 0.9em;
676 color: inherit;
677}
678
679/* Code block with copy button */
680.markrust-code-block {
681 position: relative;
682}
683
684.markrust-copy-button {
685 position: absolute;
686 top: 0.5rem;
687 right: 0.5rem;
688 padding: 0.25rem;
689 background-color: rgba(255, 255, 255, 0.1);
690 border: none;
691 border-radius: 0.25rem;
692 color: var(--text-color);
693 cursor: pointer;
694 opacity: 0.6;
695 transition: opacity 0.2s;
696}
697
698.markrust-copy-button:hover {
699 opacity: 1;
700}
701
702/* Table styles */
703.markrust-table {
704 border-collapse: collapse;
705 width: 100%;
706 margin: 1rem 0;
707}
708
709.markrust-table th,
710.markrust-table td {
711 border: 1px solid var(--border-color);
712 padding: 0.5rem 0.75rem;
713 text-align: left;
714}
715
716.markrust-table th {
717 background-color: rgba(255, 255, 255, 0.05);
718 font-weight: 600;
719}
720
721.markrust-table .align-center {
722 text-align: center;
723}
724
725.markrust-table .align-right {
726 text-align: right;
727}
728
729/* Table of contents */
730.markrust-toc {
731 background-color: rgba(255, 255, 255, 0.05);
732 border-radius: 0.375rem;
733 padding: 1rem;
734 margin-bottom: 2rem;
735}
736
737.markrust-toc-header {
738 font-weight: 600;
739 margin-bottom: 0.5rem;
740 color: var(--primary-color);
741}
742
743.markrust-toc ul {
744 list-style-type: none;
745 padding-left: 0;
746 margin-bottom: 0;
747}
748
749.markrust-toc ul ul {
750 padding-left: 1.5rem;
751}
752
753.markrust-toc li {
754 margin-bottom: 0.25rem;
755}
756
757.markrust-toc a {
758 text-decoration: none;
759 color: var(--text-color);
760}
761
762.markrust-toc a:hover {
763 color: var(--primary-color);
764 text-decoration: underline;
765}
766"#;
767
768const LIGHT_THEME_CSS: &str = r#"
770/* Light theme */
771body {
772 font-family: var(--font-family);
773 color: var(--text-color);
774 background-color: var(--background-color);
775 line-height: 1.6;
776 margin: 0;
777 padding: 0;
778}
779
780.markrust-container {
781 max-width: var(--content-width);
782 margin: 0 auto;
783 padding: 2rem 1rem;
784}
785
786.markrust-content {
787 margin-top: 2rem;
788}
789
790h1, h2, h3, h4, h5, h6 {
791 margin-top: 2rem;
792 margin-bottom: 1rem;
793 font-weight: 600;
794 line-height: 1.25;
795 color: var(--primary-color);
796}
797
798h1 {
799 font-size: 2.25rem;
800 border-bottom: 1px solid var(--border-color);
801 padding-bottom: 0.5rem;
802}
803
804h2 {
805 font-size: 1.8rem;
806}
807
808h3 {
809 font-size: 1.5rem;
810}
811
812h4 {
813 font-size: 1.25rem;
814}
815
816h5 {
817 font-size: 1rem;
818}
819
820h6 {
821 font-size: 0.875rem;
822}
823
824a {
825 color: var(--primary-color);
826 text-decoration: none;
827}
828
829a:hover {
830 text-decoration: underline;
831}
832
833p {
834 margin-top: 0;
835 margin-bottom: 1rem;
836}
837
838ul, ol {
839 margin-top: 0;
840 margin-bottom: 1rem;
841 padding-left: 2rem;
842}
843
844li {
845 margin-bottom: 0.25rem;
846}
847
848blockquote {
849 margin: 1rem 0;
850 padding: 0.5rem 1rem;
851 border-left: 4px solid var(--primary-color);
852 background-color: var(--info-light-color);
853}
854
855hr {
856 border: 0;
857 height: 1px;
858 background-color: var(--border-color);
859 margin: 2rem 0;
860}
861
862code {
863 font-family: var(--monospace-font);
864 font-size: 0.9em;
865 background-color: rgba(0, 0, 0, 0.05);
866 padding: 0.2em 0.4em;
867 border-radius: 3px;
868}
869
870pre {
871 margin: 1rem 0;
872 padding: 1rem;
873 overflow-x: auto;
874 background-color: rgba(0, 0, 0, 0.05);
875 border-radius: 0.375rem;
876}
877
878pre code {
879 background: none;
880 padding: 0;
881 font-size: 0.9em;
882 color: inherit;
883}
884
885/* Code block with copy button */
886.markrust-code-block {
887 position: relative;
888}
889
890.markrust-copy-button {
891 position: absolute;
892 top: 0.5rem;
893 right: 0.5rem;
894 padding: 0.25rem;
895 background-color: rgba(255, 255, 255, 0.7);
896 border: none;
897 border-radius: 0.25rem;
898 color: var(--text-color);
899 cursor: pointer;
900 opacity: 0.6;
901 transition: opacity 0.2s;
902}
903
904.markrust-copy-button:hover {
905 opacity: 1;
906}
907
908/* Table styles */
909.markrust-table {
910 border-collapse: collapse;
911 width: 100%;
912 margin: 1rem 0;
913}
914
915.markrust-table th,
916.markrust-table td {
917 border: 1px solid var(--border-color);
918 padding: 0.5rem 0.75rem;
919 text-align: left;
920}
921
922.markrust-table th {
923 background-color: rgba(0, 0, 0, 0.03);
924 font-weight: 600;
925}
926
927.markrust-table .align-center {
928 text-align: center;
929}
930
931.markrust-table .align-right {
932 text-align: right;
933}
934
935/* Table of contents */
936.markrust-toc {
937 background-color: rgba(0, 0, 0, 0.02);
938 border-radius: 0.375rem;
939 padding: 1rem;
940 margin-bottom: 2rem;
941}
942
943.markrust-toc-header {
944 font-weight: 600;
945 margin-bottom: 0.5rem;
946 color: var(--primary-color);
947}
948
949.markrust-toc ul {
950 list-style-type: none;
951 padding-left: 0;
952 margin-bottom: 0;
953}
954
955.markrust-toc ul ul {
956 padding-left: 1.5rem;
957}
958
959.markrust-toc li {
960 margin-bottom: 0.25rem;
961}
962
963.markrust-toc a {
964 text-decoration: none;
965 color: var(--text-color);
966}
967
968.markrust-toc a:hover {
969 color: var(--primary-color);
970 text-decoration: underline;
971}
972"#;