", lang_str));
+ html_output.push_str(&highlight_code(lang, &code_block_content));
+ } else {
+ // Unsupported language: render as plain escaped text
+ if !lang_str.is_empty() {
+ html_output.push_str(&format!(" class=\"language-{}\">", lang_str));
+ } else {
+ html_output.push('>');
+ }
+ html_output.push_str(&html_escape(&code_block_content));
+ }
+
+ html_output.push_str("\n");
+ code_block_lang = None;
+ code_block_content.clear();
+ }
+ Event::Text(text) => {
+ // Regular text outside code blocks
+ html_output.push_str(&html_escape(&text));
+ }
+ Event::Code(text) => {
+ // Inline code
+ html_output.push_str("");
+ html_output.push_str(&html_escape(&text));
+ html_output.push_str("");
+ }
+ Event::Start(tag) => {
+ html_output.push_str(&start_tag_to_html(&tag));
+ }
+ Event::End(tag) => {
+ html_output.push_str(&end_tag_to_html(&tag));
+ }
+ Event::SoftBreak => {
+ html_output.push('\n');
+ }
+ Event::HardBreak => {
+ html_output.push_str("".to_string(),
+ Tag::Heading { level, .. } => format!("\n".to_string(),
+ Tag::CodeBlock(_) => String::new(), // Handled separately
+ Tag::List(Some(start)) => format!("
\n", start),
+ Tag::List(None) => "
\n".to_string(),
+ Tag::Item => "
Hello
"));
assert!(html.contains("test"));
}
+
+ #[test]
+ fn test_code_block_highlighting() {
+ let md = "```rust\nfn main() {}\n```";
+ let html = markdown_to_html(md);
+
+ // Should contain highlighted code
+ assert!(html.contains("cargo run"));
+ }
}
diff --git a/static/style.css b/static/style.css
index f20208e..9d621a5 100644
--- a/static/style.css
+++ b/static/style.css
@@ -40,7 +40,9 @@
}
/* Reset */
-*, *::before, *::after {
+*,
+*::before,
+*::after {
box-sizing: border-box;
margin: 0;
padding: 0;
@@ -62,7 +64,9 @@ body {
}
/* Layout */
-nav, main, footer {
+nav,
+main,
+footer {
width: 100%;
max-width: var(--max-width);
margin: 0 auto;
@@ -89,16 +93,28 @@ footer {
}
/* Typography */
-h1, h2, h3 {
+h1,
+h2,
+h3 {
line-height: 1.3;
margin-block: var(--space-lg) var(--space-md);
}
-h1 { font-size: 2rem; }
-h2 { font-size: 1.5rem; }
-h3 { font-size: 1.25rem; }
+h1 {
+ font-size: 2rem;
+}
-h1:first-child, h2:first-child, h3:first-child {
+h2 {
+ font-size: 1.5rem;
+}
+
+h3 {
+ font-size: 1.25rem;
+}
+
+h1:first-child,
+h2:first-child,
+h3:first-child {
margin-top: 0;
}
@@ -117,7 +133,8 @@ a:hover {
}
/* Code */
-code, pre {
+code,
+pre {
font-family: var(--font-mono);
font-size: 0.9em;
}
@@ -142,7 +159,8 @@ pre code {
}
/* Lists */
-ul, ol {
+ul,
+ol {
margin-block: var(--space-md);
padding-left: var(--space-lg);
}
@@ -249,3 +267,117 @@ article.post header {
font-size: 1.25rem;
color: var(--text-muted);
}
+
+/* Syntax Highlighting */
+:root {
+ --hl-keyword: #d73a49;
+ --hl-string: #22863a;
+ --hl-comment: #6a737d;
+ --hl-function: #6f42c1;
+ --hl-type: #005cc5;
+ --hl-number: #005cc5;
+ --hl-operator: #d73a49;
+ --hl-variable: #24292e;
+ --hl-constant: #005cc5;
+ --hl-property: #005cc5;
+ --hl-punctuation: #24292e;
+ --hl-attribute: #22863a;
+}
+
+@media (prefers-color-scheme: dark) {
+ :root {
+ --hl-keyword: #f97583;
+ --hl-string: #9ecbff;
+ --hl-comment: #6a737d;
+ --hl-function: #b392f0;
+ --hl-type: #79b8ff;
+ --hl-number: #79b8ff;
+ --hl-operator: #f97583;
+ --hl-variable: #e1e4e8;
+ --hl-constant: #79b8ff;
+ --hl-property: #79b8ff;
+ --hl-punctuation: #e1e4e8;
+ --hl-attribute: #85e89d;
+ }
+}
+
+.hl-keyword {
+ color: var(--hl-keyword);
+}
+
+.hl-string {
+ color: var(--hl-string);
+}
+
+.hl-comment {
+ color: var(--hl-comment);
+ font-style: italic;
+}
+
+.hl-function {
+ color: var(--hl-function);
+}
+
+.hl-function-builtin {
+ color: var(--hl-function);
+}
+
+.hl-type {
+ color: var(--hl-type);
+}
+
+.hl-type-builtin {
+ color: var(--hl-type);
+}
+
+.hl-number {
+ color: var(--hl-number);
+}
+
+.hl-operator {
+ color: var(--hl-operator);
+}
+
+.hl-variable {
+ color: var(--hl-variable);
+}
+
+.hl-variable-builtin {
+ color: var(--hl-variable);
+}
+
+.hl-variable-parameter {
+ color: var(--hl-variable);
+}
+
+.hl-constant {
+ color: var(--hl-constant);
+}
+
+.hl-constant-builtin {
+ color: var(--hl-constant);
+}
+
+.hl-property {
+ color: var(--hl-property);
+}
+
+.hl-punctuation {
+ color: var(--hl-punctuation);
+}
+
+.hl-punctuation-bracket {
+ color: var(--hl-punctuation);
+}
+
+.hl-punctuation-delimiter {
+ color: var(--hl-punctuation);
+}
+
+.hl-attribute {
+ color: var(--hl-attribute);
+}
+
+.hl-constructor {
+ color: var(--hl-type);
+}
\ No newline at end of file