diff --git a/src/assets/markdown-override.css b/src/assets/markdown-override.css index 544ae43..19328a3 100644 --- a/src/assets/markdown-override.css +++ b/src/assets/markdown-override.css @@ -66,8 +66,20 @@ } .markdown-body .ql-code-block { - white-space: pre-wrap; - word-break: break-word; + white-space: pre; + word-break: normal; + overflow-x: auto; +} + +/* Markdown code blocks: horizontal scroll, no wrapping */ +.markdown-body pre { + overflow-x: auto; +} + +.markdown-body pre code { + white-space: pre; + word-break: normal; + overflow-x: auto; } .markdown-body.markdown-body-dark .ql-code-block-container { diff --git a/src/utils/markdown.ts b/src/utils/markdown.ts index e98943f..295234c 100644 --- a/src/utils/markdown.ts +++ b/src/utils/markdown.ts @@ -16,9 +16,42 @@ marked.use( }) ) +function highlightHtmlCode(html: string): string { + const doc = new DOMParser().parseFromString(html, 'text/html') + const blocks = Array.from(doc.querySelectorAll('pre')) + for (const pre of blocks) { + const container = pre.closest('.ql-code-block-container') + const codeEl = pre.querySelector('code') + let lang = container?.getAttribute('data-language') ?? '' + if (!lang && codeEl) { + const m = (codeEl.className || '').match(/language-([\w-]+)/) + if (m) lang = m[1] + } + const text = codeEl ? codeEl.textContent ?? '' : pre.textContent ?? '' + let highlighted: string + if (lang && hljs.getLanguage(lang)) { + highlighted = hljs.highlight(text, { language: lang }).value + } else { + highlighted = hljs.highlightAuto(text).value + } + if (codeEl) { + codeEl.innerHTML = highlighted + codeEl.classList.add('hljs') + } else { + const newCode = doc.createElement('code') + newCode.className = 'hljs' + newCode.innerHTML = highlighted + pre.innerHTML = '' + pre.appendChild(newCode) + } + } + return doc.body.innerHTML +} + export function renderArticle(content: string): string { if (content.trimStart().startsWith('<')) { - return DOMPurify.sanitize(content) + const safe = DOMPurify.sanitize(content) + return highlightHtmlCode(safe) } const rawHtml = marked.parse(content, { async: false }) as string return DOMPurify.sanitize(rawHtml)