Fix: Markdown rendering style for different themes
This commit is contained in:
1 parent
caa4f47480
commit
9fecf2d201
2 files changed
+501
-188
No files matched your search
@@ -1,192 +1,183 @@
|
||||
<template>
|
||||
<div class="markdown-content" v-html="renderedMarkdown"></div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, watch, onMounted } from 'vue';
|
||||
import { marked } from 'marked';
|
||||
import DOMPurify from 'dompurify';
|
||||
import hljs from 'highlight.js';
|
||||
import 'highlight.js/styles/github.css';
|
||||
|
||||
const props = defineProps({
|
||||
content: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
});
|
||||
|
||||
const renderedMarkdown = ref('');
|
||||
|
||||
// 配置marked使用highlight.js进行代码高亮
|
||||
marked.setOptions({
|
||||
highlight: function(code, lang) {
|
||||
const language = hljs.getLanguage(lang) ? lang : 'plaintext';
|
||||
return hljs.highlight(code, { language }).value;
|
||||
},
|
||||
langPrefix: 'hljs language-',
|
||||
breaks: true,
|
||||
gfm: true,
|
||||
headerIds: true,
|
||||
mangle: false,
|
||||
sanitize: false,
|
||||
smartLists: true,
|
||||
smartypants: false
|
||||
});
|
||||
|
||||
// 渲染Markdown内容
|
||||
const renderMarkdown = () => {
|
||||
if (!props.content) {
|
||||
renderedMarkdown.value = '';
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// 使用marked将Markdown转为HTML
|
||||
const rawHtml = marked.parse(props.content);
|
||||
// 使用DOMPurify清理HTML,防止XSS攻击
|
||||
renderedMarkdown.value = DOMPurify.sanitize(rawHtml);
|
||||
} catch (error) {
|
||||
console.error('Markdown parsing error:', error);
|
||||
renderedMarkdown.value = `<p class="text-red-500">Error rendering markdown: ${error.message}</p>`;
|
||||
}
|
||||
};
|
||||
|
||||
// 监听content变化时重新渲染
|
||||
watch(() => props.content, renderMarkdown, { immediate: true });
|
||||
|
||||
// 组件挂载时渲染
|
||||
onMounted(() => {
|
||||
renderMarkdown();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
/* Markdown样式 */
|
||||
.markdown-content {
|
||||
color: var(--theme-text-color, #333);
|
||||
line-height: 1.6;
|
||||
word-wrap: break-word;
|
||||
<div class="markdown-content theme-markdown-content theme-text" v-html="renderedMarkdown"></div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, watch, onMounted } from 'vue';
|
||||
import { marked } from 'marked';
|
||||
import DOMPurify from 'dompurify';
|
||||
import hljs from 'highlight.js';
|
||||
import 'highlight.js/styles/github.css';
|
||||
|
||||
const props = defineProps({
|
||||
content: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
});
|
||||
|
||||
const renderedMarkdown = ref('');
|
||||
|
||||
// 配置marked使用highlight.js进行代码高亮
|
||||
marked.setOptions({
|
||||
highlight: function(code, lang) {
|
||||
const language = hljs.getLanguage(lang) ? lang : 'plaintext';
|
||||
return hljs.highlight(code, { language }).value;
|
||||
},
|
||||
langPrefix: 'hljs language-',
|
||||
breaks: true,
|
||||
gfm: true,
|
||||
headerIds: true,
|
||||
mangle: false,
|
||||
sanitize: false,
|
||||
smartLists: true,
|
||||
smartypants: false
|
||||
});
|
||||
|
||||
// 渲染Markdown内容
|
||||
const renderMarkdown = () => {
|
||||
if (!props.content) {
|
||||
renderedMarkdown.value = '';
|
||||
return;
|
||||
}
|
||||
|
||||
.markdown-content h1 {
|
||||
font-size: 2em;
|
||||
margin-top: 1.5em;
|
||||
margin-bottom: 0.8em;
|
||||
font-weight: 600;
|
||||
try {
|
||||
// 使用marked将Markdown转为HTML
|
||||
const rawHtml = marked.parse(props.content);
|
||||
// 使用DOMPurify清理HTML,防止XSS攻击
|
||||
renderedMarkdown.value = DOMPurify.sanitize(rawHtml);
|
||||
} catch (error) {
|
||||
console.error('Markdown parsing error:', error);
|
||||
renderedMarkdown.value = `<p class="text-red-500">Error rendering markdown: ${error.message}</p>`;
|
||||
}
|
||||
|
||||
.markdown-content h2 {
|
||||
font-size: 1.5em;
|
||||
margin-top: 1.3em;
|
||||
margin-bottom: 0.7em;
|
||||
font-weight: 600;
|
||||
border-bottom: 1px solid #eee;
|
||||
padding-bottom: 0.3em;
|
||||
}
|
||||
|
||||
.markdown-content h3 {
|
||||
font-size: 1.25em;
|
||||
margin-top: 1.2em;
|
||||
margin-bottom: 0.6em;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.markdown-content h4 {
|
||||
font-size: 1em;
|
||||
margin-top: 1.1em;
|
||||
margin-bottom: 0.5em;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.markdown-content p {
|
||||
margin-bottom: 1em;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.markdown-content ul,
|
||||
.markdown-content ol {
|
||||
padding-left: 2em;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
.markdown-content li {
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
.markdown-content pre {
|
||||
background-color: #f6f8fa;
|
||||
border-radius: 6px;
|
||||
padding: 1em;
|
||||
overflow: auto;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
.markdown-content code {
|
||||
font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace;
|
||||
font-size: 0.9em;
|
||||
padding: 0.2em 0.4em;
|
||||
margin: 0;
|
||||
background-color: rgba(175, 184, 193, 0.2);
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.markdown-content pre code {
|
||||
padding: 0;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.markdown-content blockquote {
|
||||
padding: 0 1em;
|
||||
color: #6a737d;
|
||||
border-left: 0.25em solid #dfe2e5;
|
||||
margin-left: 0;
|
||||
margin-right: 0;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
.markdown-content table {
|
||||
border-collapse: collapse;
|
||||
width: 100%;
|
||||
margin-bottom: 1em;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.markdown-content table th,
|
||||
.markdown-content table td {
|
||||
padding: 0.5em 1em;
|
||||
border: 1px solid #dfe2e5;
|
||||
}
|
||||
|
||||
.markdown-content table th {
|
||||
font-weight: 600;
|
||||
background-color: #f6f8fa;
|
||||
}
|
||||
|
||||
.markdown-content table tr:nth-child(2n) {
|
||||
background-color: #f6f8fa;
|
||||
}
|
||||
|
||||
.markdown-content img {
|
||||
max-width: 100%;
|
||||
box-sizing: border-box;
|
||||
margin: 1em 0;
|
||||
}
|
||||
|
||||
.markdown-content hr {
|
||||
height: 0.25em;
|
||||
padding: 0;
|
||||
margin: 1.5em 0;
|
||||
background-color: #e1e4e8;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.markdown-content a {
|
||||
color: var(--theme-primary-color, #0366d6);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.markdown-content a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
</style>
|
||||
};
|
||||
|
||||
// 监听content变化时重新渲染
|
||||
watch(() => props.content, renderMarkdown, { immediate: true });
|
||||
|
||||
// 组件挂载时渲染
|
||||
onMounted(() => {
|
||||
renderMarkdown();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
/* 基本布局和非颜色样式 */
|
||||
.markdown-content {
|
||||
line-height: 1.6;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
/* 标题样式 */
|
||||
.markdown-content h1 {
|
||||
font-size: 2em;
|
||||
margin-top: 1.5em;
|
||||
margin-bottom: 0.8em;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.markdown-content h2 {
|
||||
font-size: 1.5em;
|
||||
margin-top: 1.3em;
|
||||
margin-bottom: 0.7em;
|
||||
font-weight: 600;
|
||||
padding-bottom: 0.3em;
|
||||
}
|
||||
|
||||
.markdown-content h3 {
|
||||
font-size: 1.25em;
|
||||
margin-top: 1.2em;
|
||||
margin-bottom: 0.6em;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.markdown-content h4 {
|
||||
font-size: 1em;
|
||||
margin-top: 1.1em;
|
||||
margin-bottom: 0.5em;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* 段落样式 */
|
||||
.markdown-content p {
|
||||
margin-bottom: 1em;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
/* 列表样式 */
|
||||
.markdown-content ul,
|
||||
.markdown-content ol {
|
||||
padding-left: 2em;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
.markdown-content li {
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
/* 代码块样式 */
|
||||
.markdown-content pre {
|
||||
border-radius: 6px;
|
||||
padding: 1em;
|
||||
overflow: auto;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
.markdown-content code {
|
||||
font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace;
|
||||
font-size: 0.9em;
|
||||
padding: 0.2em 0.4em;
|
||||
margin: 0;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.markdown-content pre code {
|
||||
padding: 0;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
/* 引用样式 */
|
||||
.markdown-content blockquote {
|
||||
padding: 0 1em;
|
||||
margin-left: 0;
|
||||
margin-right: 0;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
/* 表格样式 */
|
||||
.markdown-content table {
|
||||
border-collapse: collapse;
|
||||
width: 100%;
|
||||
margin-bottom: 1em;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.markdown-content table th,
|
||||
.markdown-content table td {
|
||||
padding: 0.5em 1em;
|
||||
}
|
||||
|
||||
/* 图片样式 */
|
||||
.markdown-content img {
|
||||
max-width: 100%;
|
||||
box-sizing: border-box;
|
||||
margin: 1em 0;
|
||||
}
|
||||
|
||||
/* 水平线样式 */
|
||||
.markdown-content hr {
|
||||
height: 0.25em;
|
||||
padding: 0;
|
||||
margin: 1.5em 0;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
/* 链接样式 */
|
||||
.markdown-content a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.markdown-content a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
</style>
|
||||
@@ -440,6 +440,89 @@ html * {
|
||||
.theme-manager-text-disabled {
|
||||
@apply text-stone-400;
|
||||
}
|
||||
/* Markdown主题样式 - Light Mode (默认主题) */
|
||||
.theme-markdown-content {
|
||||
@apply text-stone-600;
|
||||
}
|
||||
|
||||
.theme-markdown-content h1,
|
||||
.theme-markdown-content h2,
|
||||
.theme-markdown-content h3,
|
||||
.theme-markdown-content h4 {
|
||||
@apply text-stone-700;
|
||||
}
|
||||
|
||||
.theme-markdown-content h2 {
|
||||
@apply border-b border-stone-200;
|
||||
}
|
||||
|
||||
.theme-markdown-content a {
|
||||
@apply text-stone-800 underline hover:text-stone-600;
|
||||
}
|
||||
|
||||
.theme-markdown-content pre {
|
||||
@apply bg-stone-100 border border-stone-200;
|
||||
}
|
||||
|
||||
.theme-markdown-content code {
|
||||
@apply bg-stone-100 text-stone-700;
|
||||
}
|
||||
|
||||
.theme-markdown-content blockquote {
|
||||
@apply border-l-4 border-stone-300 bg-stone-50 text-stone-600 px-4 py-2;
|
||||
}
|
||||
|
||||
.theme-markdown-content table {
|
||||
@apply border-collapse border border-stone-200 my-4;
|
||||
}
|
||||
|
||||
.theme-markdown-content table th {
|
||||
@apply bg-stone-100 text-stone-700 border border-stone-300 p-2 font-semibold;
|
||||
}
|
||||
|
||||
.theme-markdown-content table td {
|
||||
@apply border border-stone-200 text-stone-600 p-2;
|
||||
}
|
||||
|
||||
.theme-markdown-content tr:nth-child(even) {
|
||||
@apply bg-stone-50;
|
||||
}
|
||||
|
||||
.theme-markdown-content hr {
|
||||
@apply border-stone-200 my-4;
|
||||
}
|
||||
|
||||
.theme-markdown-content ul {
|
||||
@apply list-disc pl-5 my-3;
|
||||
}
|
||||
|
||||
.theme-markdown-content ol {
|
||||
@apply list-decimal pl-5 my-3;
|
||||
}
|
||||
|
||||
.theme-markdown-content img {
|
||||
@apply max-w-full rounded border border-stone-200 my-3;
|
||||
}
|
||||
|
||||
.theme-markdown-content p {
|
||||
@apply my-3;
|
||||
}
|
||||
|
||||
.theme-markdown-content strong {
|
||||
@apply font-semibold text-stone-700;
|
||||
}
|
||||
|
||||
.theme-markdown-content em {
|
||||
@apply italic;
|
||||
}
|
||||
|
||||
.theme-markdown-content pre code {
|
||||
@apply block p-4 overflow-x-auto;
|
||||
}
|
||||
|
||||
.theme-markdown-content code {
|
||||
@apply px-1.5 py-0.5 rounded text-sm;
|
||||
}
|
||||
}
|
||||
|
||||
/* 主题变体 */
|
||||
@@ -758,6 +841,66 @@ html * {
|
||||
@apply
|
||||
text-blue-400;
|
||||
}
|
||||
|
||||
/* 蓝色主题的 Markdown 样式 */
|
||||
.theme-markdown-content {
|
||||
@apply text-sky-700;
|
||||
}
|
||||
|
||||
.theme-markdown-content h1,
|
||||
.theme-markdown-content h2,
|
||||
.theme-markdown-content h3,
|
||||
.theme-markdown-content h4 {
|
||||
@apply text-sky-800;
|
||||
}
|
||||
|
||||
.theme-markdown-content h2 {
|
||||
@apply border-b border-sky-200;
|
||||
}
|
||||
|
||||
.theme-markdown-content a {
|
||||
@apply text-blue-600 hover:text-blue-800;
|
||||
}
|
||||
|
||||
.theme-markdown-content pre {
|
||||
@apply bg-sky-50 border border-sky-200;
|
||||
}
|
||||
|
||||
.theme-markdown-content code {
|
||||
@apply bg-sky-50 text-sky-800;
|
||||
}
|
||||
|
||||
.theme-markdown-content blockquote {
|
||||
@apply border-l-4 border-sky-300 bg-sky-50/60 text-sky-700;
|
||||
}
|
||||
|
||||
.theme-markdown-content table {
|
||||
@apply border border-sky-200;
|
||||
}
|
||||
|
||||
.theme-markdown-content table th {
|
||||
@apply bg-sky-100 text-sky-800 border border-sky-300;
|
||||
}
|
||||
|
||||
.theme-markdown-content table td {
|
||||
@apply border border-sky-200 text-sky-700;
|
||||
}
|
||||
|
||||
.theme-markdown-content tr:nth-child(even) {
|
||||
@apply bg-sky-50;
|
||||
}
|
||||
|
||||
.theme-markdown-content hr {
|
||||
@apply border-sky-200;
|
||||
}
|
||||
|
||||
.theme-markdown-content img {
|
||||
@apply border border-sky-200;
|
||||
}
|
||||
|
||||
.theme-markdown-content strong {
|
||||
@apply text-sky-800;
|
||||
}
|
||||
}
|
||||
|
||||
:root[data-theme="green"] {
|
||||
@@ -973,6 +1116,65 @@ html * {
|
||||
text-red-900 hover:text-white
|
||||
transition-colors;
|
||||
}
|
||||
/* 绿色主题的 Markdown 样式 */
|
||||
.theme-markdown-content {
|
||||
@apply text-teal-200;
|
||||
}
|
||||
|
||||
.theme-markdown-content h1,
|
||||
.theme-markdown-content h2,
|
||||
.theme-markdown-content h3,
|
||||
.theme-markdown-content h4 {
|
||||
@apply text-teal-100;
|
||||
}
|
||||
|
||||
.theme-markdown-content h2 {
|
||||
@apply border-b border-teal-600;
|
||||
}
|
||||
|
||||
.theme-markdown-content a {
|
||||
@apply text-teal-300 hover:text-teal-200;
|
||||
}
|
||||
|
||||
.theme-markdown-content pre {
|
||||
@apply bg-teal-900/50 border border-teal-700;
|
||||
}
|
||||
|
||||
.theme-markdown-content code {
|
||||
@apply bg-teal-800 text-teal-200;
|
||||
}
|
||||
|
||||
.theme-markdown-content blockquote {
|
||||
@apply border-l-4 border-teal-600 bg-teal-800/50 text-teal-300;
|
||||
}
|
||||
|
||||
.theme-markdown-content table {
|
||||
@apply border border-teal-700;
|
||||
}
|
||||
|
||||
.theme-markdown-content table th {
|
||||
@apply bg-teal-800 text-teal-200 border border-teal-600;
|
||||
}
|
||||
|
||||
.theme-markdown-content table td {
|
||||
@apply border border-teal-700 text-teal-300;
|
||||
}
|
||||
|
||||
.theme-markdown-content tr:nth-child(even) {
|
||||
@apply bg-teal-800/50;
|
||||
}
|
||||
|
||||
.theme-markdown-content hr {
|
||||
@apply border-teal-600;
|
||||
}
|
||||
|
||||
.theme-markdown-content img {
|
||||
@apply border border-teal-700;
|
||||
}
|
||||
|
||||
.theme-markdown-content strong {
|
||||
@apply text-teal-100;
|
||||
}
|
||||
}
|
||||
|
||||
:root[data-theme="purple"] {
|
||||
@@ -1245,6 +1447,66 @@ html * {
|
||||
.theme-manager-text-disabled {
|
||||
@apply text-white/40;
|
||||
}
|
||||
|
||||
/* 紫色主题的 Markdown 样式 */
|
||||
.theme-markdown-content {
|
||||
@apply text-white/90;
|
||||
}
|
||||
|
||||
.theme-markdown-content h1,
|
||||
.theme-markdown-content h2,
|
||||
.theme-markdown-content h3,
|
||||
.theme-markdown-content h4 {
|
||||
@apply text-white;
|
||||
}
|
||||
|
||||
.theme-markdown-content h2 {
|
||||
@apply border-b border-purple-600/50;
|
||||
}
|
||||
|
||||
.theme-markdown-content a {
|
||||
@apply text-purple-300 hover:text-purple-200;
|
||||
}
|
||||
|
||||
.theme-markdown-content pre {
|
||||
@apply bg-black/20 border border-purple-600/50;
|
||||
}
|
||||
|
||||
.theme-markdown-content code {
|
||||
@apply bg-purple-900/50 text-purple-200;
|
||||
}
|
||||
|
||||
.theme-markdown-content blockquote {
|
||||
@apply border-l-4 border-purple-500 bg-purple-900/30 text-purple-200;
|
||||
}
|
||||
|
||||
.theme-markdown-content table {
|
||||
@apply border border-purple-700/50;
|
||||
}
|
||||
|
||||
.theme-markdown-content table th {
|
||||
@apply bg-purple-800/50 text-white border border-purple-600/50;
|
||||
}
|
||||
|
||||
.theme-markdown-content table td {
|
||||
@apply border border-purple-700/50 text-white/90;
|
||||
}
|
||||
|
||||
.theme-markdown-content tr:nth-child(even) {
|
||||
@apply bg-purple-800/30;
|
||||
}
|
||||
|
||||
.theme-markdown-content hr {
|
||||
@apply border-purple-600/50;
|
||||
}
|
||||
|
||||
.theme-markdown-content img {
|
||||
@apply border border-purple-600/50;
|
||||
}
|
||||
|
||||
.theme-markdown-content strong {
|
||||
@apply text-white;
|
||||
}
|
||||
}
|
||||
|
||||
:root[data-theme="dark"] {
|
||||
@@ -1462,4 +1724,64 @@ html * {
|
||||
.theme-history-card-header {
|
||||
@apply border-slate-700/50 /*卡片头部下边线*/;
|
||||
}
|
||||
|
||||
/* 暗色主题的 Markdown 样式 */
|
||||
.theme-markdown-content {
|
||||
@apply text-slate-100;
|
||||
}
|
||||
|
||||
.theme-markdown-content h1,
|
||||
.theme-markdown-content h2,
|
||||
.theme-markdown-content h3,
|
||||
.theme-markdown-content h4 {
|
||||
@apply text-white;
|
||||
}
|
||||
|
||||
.theme-markdown-content h2 {
|
||||
@apply border-b border-slate-700;
|
||||
}
|
||||
|
||||
.theme-markdown-content a {
|
||||
@apply text-purple-400 hover:text-purple-300;
|
||||
}
|
||||
|
||||
.theme-markdown-content pre {
|
||||
@apply bg-slate-800 border border-slate-700;
|
||||
}
|
||||
|
||||
.theme-markdown-content code {
|
||||
@apply bg-slate-800 text-slate-200;
|
||||
}
|
||||
|
||||
.theme-markdown-content blockquote {
|
||||
@apply border-l-4 border-slate-600 bg-slate-800/50 text-slate-300;
|
||||
}
|
||||
|
||||
.theme-markdown-content table {
|
||||
@apply border border-slate-700;
|
||||
}
|
||||
|
||||
.theme-markdown-content table th {
|
||||
@apply bg-slate-800 text-slate-200 border border-slate-700;
|
||||
}
|
||||
|
||||
.theme-markdown-content table td {
|
||||
@apply border border-slate-700 text-slate-300;
|
||||
}
|
||||
|
||||
.theme-markdown-content tr:nth-child(even) {
|
||||
@apply bg-slate-800/70;
|
||||
}
|
||||
|
||||
.theme-markdown-content hr {
|
||||
@apply border-slate-700;
|
||||
}
|
||||
|
||||
.theme-markdown-content img {
|
||||
@apply border border-slate-700;
|
||||
}
|
||||
|
||||
.theme-markdown-content strong {
|
||||
@apply text-white;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user