162abdc97c
- Add Prettier 3 with prettier-plugin-organize-imports (sorts/removes unused imports) - Add ESLint 10 flat config with typescript-eslint + eslint-plugin-vue + eslint-config-prettier - Add root scripts: format, format:check, lint, lint:fix - Reformat 257 files across admin-web, ops-web, desktop-client, packages - Remove unused locals/exports flagged by --noUnusedLocals/--noUnusedParameters - Fix duplicate localTabLabel key, surrogate-pair regex u-flag, NBSP literal in regex - Skip server/ (Go) and apps/browser-extension/ (deprecated per ADR) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
153 lines
2.7 KiB
Vue
153 lines
2.7 KiB
Vue
<script setup lang="ts">
|
|
import DOMPurify from 'dompurify'
|
|
import MarkdownIt from 'markdown-it'
|
|
import { computed } from 'vue'
|
|
|
|
const props = defineProps<{
|
|
content: string
|
|
}>()
|
|
|
|
const renderer = new MarkdownIt({
|
|
html: true,
|
|
linkify: true,
|
|
breaks: true,
|
|
})
|
|
|
|
const html = computed(() => {
|
|
const rawHtml = renderer.render(props.content || '')
|
|
return DOMPurify.sanitize(rawHtml)
|
|
})
|
|
</script>
|
|
|
|
<template>
|
|
<div class="markdown-preview" v-html="html" />
|
|
</template>
|
|
|
|
<style scoped>
|
|
.markdown-preview {
|
|
color: #111827;
|
|
font-size: 14px;
|
|
line-height: 1.75;
|
|
}
|
|
|
|
.markdown-preview :deep(h1),
|
|
.markdown-preview :deep(h2),
|
|
.markdown-preview :deep(h3),
|
|
.markdown-preview :deep(h4) {
|
|
margin: 1.2em 0 0.6em;
|
|
color: #111827;
|
|
font-weight: 700;
|
|
}
|
|
|
|
.markdown-preview :deep(h1) {
|
|
font-size: 28px;
|
|
}
|
|
|
|
.markdown-preview :deep(h2) {
|
|
font-size: 22px;
|
|
}
|
|
|
|
.markdown-preview :deep(h3) {
|
|
font-size: 18px;
|
|
}
|
|
|
|
.markdown-preview :deep(p),
|
|
.markdown-preview :deep(ul),
|
|
.markdown-preview :deep(ol),
|
|
.markdown-preview :deep(blockquote) {
|
|
margin: 0 0 1em;
|
|
}
|
|
|
|
.markdown-preview :deep(ul),
|
|
.markdown-preview :deep(ol) {
|
|
padding-left: 1.5em;
|
|
}
|
|
|
|
.markdown-preview :deep(a) {
|
|
color: #1677ff;
|
|
}
|
|
|
|
.markdown-preview :deep(code) {
|
|
padding: 2px 6px;
|
|
border-radius: 6px;
|
|
background: #f3f4f6;
|
|
font-size: 13px;
|
|
}
|
|
|
|
.markdown-preview :deep(pre) {
|
|
overflow-x: auto;
|
|
padding: 16px;
|
|
border-radius: 12px;
|
|
background: #111827;
|
|
color: #f9fafb;
|
|
}
|
|
|
|
.markdown-preview :deep(pre code) {
|
|
padding: 0;
|
|
background: transparent;
|
|
color: inherit;
|
|
}
|
|
|
|
.markdown-preview :deep(p.article-editor-image) {
|
|
margin: 1.25em 0;
|
|
}
|
|
|
|
.markdown-preview :deep(p.article-editor-image[align='left']) {
|
|
text-align: left;
|
|
}
|
|
|
|
.markdown-preview :deep(p.article-editor-image[align='center']) {
|
|
text-align: center;
|
|
}
|
|
|
|
.markdown-preview :deep(p.article-editor-image[align='right']) {
|
|
text-align: right;
|
|
}
|
|
|
|
.markdown-preview :deep(p.article-editor-image img) {
|
|
max-width: min(100%, 960px);
|
|
border-radius: 16px;
|
|
box-shadow: 0 10px 28px rgba(15, 23, 42, 0.08);
|
|
}
|
|
|
|
.markdown-preview :deep(table) {
|
|
width: 100%;
|
|
margin: 1em 0;
|
|
border-collapse: separate;
|
|
border-spacing: 0;
|
|
border: 1px solid #e5e7eb;
|
|
border-radius: 8px;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.markdown-preview :deep(th),
|
|
.markdown-preview :deep(td) {
|
|
padding: 12px 16px;
|
|
border-bottom: 1px solid #e5e7eb;
|
|
border-right: 1px solid #e5e7eb;
|
|
text-align: left;
|
|
vertical-align: top;
|
|
}
|
|
|
|
.markdown-preview :deep(th:last-child),
|
|
.markdown-preview :deep(td:last-child) {
|
|
border-right: none;
|
|
}
|
|
|
|
.markdown-preview :deep(tr:last-child td) {
|
|
border-bottom: none;
|
|
}
|
|
|
|
.markdown-preview :deep(th) {
|
|
background: #f9fafb;
|
|
font-weight: 600;
|
|
color: #374151;
|
|
}
|
|
|
|
.markdown-preview :deep(hr) {
|
|
margin: 24px 0;
|
|
border: 0;
|
|
border-top: 1px solid #e5e7eb;
|
|
}
|
|
</style>
|