diff --git a/package-lock.json b/package-lock.json index 0d0d333..d5ed463 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,6 +22,7 @@ "csstype": "^3.1.3", "es-toolkit": "^1.27.0", "form-data": "4.0.1", + "front-matter": "^4.0.2", "highlight.js": "^11.10.0", "juice": "^11.0.0", "lucide-vue-next": "^0.462.0", @@ -9217,6 +9218,19 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/esquery": { "version": "1.6.0", "resolved": "https://registry.npmmirror.com/esquery/-/esquery-1.6.0.tgz", @@ -9704,6 +9718,37 @@ "url": "https://github.com/sponsors/rawify" } }, + "node_modules/front-matter": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/front-matter/-/front-matter-4.0.2.tgz", + "integrity": "sha512-I8ZuJ/qG92NWX8i5x1Y8qyj3vizhXS31OxjKDu3LKP+7/qBgfIKValiZIEwoVoJKUHlhWtYrktkxV1XsX+pPlg==", + "license": "MIT", + "dependencies": { + "js-yaml": "^3.13.1" + } + }, + "node_modules/front-matter/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/front-matter/node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, "node_modules/fs-extra": { "version": "11.2.0", "resolved": "https://registry.npmmirror.com/fs-extra/-/fs-extra-11.2.0.tgz", @@ -16856,6 +16901,12 @@ "node": ">=6" } }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "license": "BSD-3-Clause" + }, "node_modules/stable-hash": { "version": "0.0.4", "resolved": "https://registry.npmmirror.com/stable-hash/-/stable-hash-0.0.4.tgz", diff --git a/package.json b/package.json index 2091a78..9d6ec78 100644 --- a/package.json +++ b/package.json @@ -34,6 +34,7 @@ "csstype": "^3.1.3", "es-toolkit": "^1.27.0", "form-data": "4.0.1", + "front-matter": "^4.0.2", "highlight.js": "^11.10.0", "juice": "^11.0.0", "lucide-vue-next": "^0.462.0", diff --git a/src/config/theme.ts b/src/config/theme.ts index a19583c..fe77818 100644 --- a/src/config/theme.ts +++ b/src/config/theme.ts @@ -206,12 +206,14 @@ const defaultTheme: Theme = { hr: { 'border-style': `solid`, - 'border-width': `1px 0 0`, + 'border-width': `2px 0 0`, 'border-color': `rgba(0,0,0,0.1)`, '-webkit-transform-origin': `0 0`, '-webkit-transform': `scale(1, 0.5)`, 'transform-origin': `0 0`, 'transform': `scale(1, 0.5)`, + 'height': `0.4em`, + 'margin': `1.5em 0`, }, }, inline: { diff --git a/src/stores/index.ts b/src/stores/index.ts index 75b97d2..14351c5 100644 --- a/src/stores/index.ts +++ b/src/stores/index.ts @@ -179,7 +179,8 @@ export const useStore = defineStore(`store`, () => { codeThemeChange() renderer.reset({ citeStatus: isCiteStatus.value, legend: legend.value, isUseIndent: isUseIndent.value }) - let outputTemp = marked.parse(editor.value!.getValue()) as string + const { markdownContent } = renderer.parseFrontMatterAndContent(editor.value!.getValue()) + let outputTemp = marked.parse(markdownContent) as string // 去除第一行的 margin-top outputTemp = outputTemp.replace(/(style=".*?)"/, `$1;margin-top: 0"`) diff --git a/src/utils/renderer.ts b/src/utils/renderer.ts index d461b36..75fa8d7 100644 --- a/src/utils/renderer.ts +++ b/src/utils/renderer.ts @@ -2,14 +2,18 @@ import type { ExtendedProperties, IOpts, ThemeStyles } from '@/types' import type { PropertiesHyphen } from 'csstype' import type { Renderer, RendererObject, Tokens } from 'marked' import { cloneDeep, toMerged } from 'es-toolkit' -import hljs from 'highlight.js' +import frontMatter from 'front-matter' +import hljs from 'highlight.js' import { marked } from 'marked' import mermaid from 'mermaid' import { getStyleString } from '.' import markedAlert from './MDAlert' import { MDKatex } from './MDKatex' +marked.setOptions({ + breaks: true, +}) marked.use(MDKatex({ nonStandard: true })) function buildTheme({ theme: _theme, fonts, size, isUseIndent }: IOpts): ThemeStyles { @@ -107,6 +111,19 @@ export function initRenderer(opts: IOpts) { return getStyles(styleMapping, tag, addition) } + function parseFrontMatterAndContent(markdownText: string) { + try { + const parsed = frontMatter(markdownText) + const yamlData = parsed.attributes + const markdownContent = parsed.body + return { yamlData, markdownContent } + } + catch (error) { + console.error(`Error parsing front-matter:`, error) + return { yamlData: {}, markdownContent: markdownText } + } + } + function styledContent(styleLabel: string, content: string, tagName?: string): string { const tag = tagName ?? styleLabel return `<${tag} ${styles(styleLabel)}>${content}` @@ -175,7 +192,7 @@ export function initRenderer(opts: IOpts) { const language = hljs.getLanguage(langText) ? langText : `plaintext` let highlighted = hljs.highlight(text, { language }).value // tab to 4 spaces - highlighted = highlighted.replace(/\t/g, ' ') + highlighted = highlighted.replace(/\t/g, ` `) highlighted = highlighted .replace(/\r\n/g, `
`) .replace(/\n/g, `
`) @@ -186,7 +203,8 @@ export function initRenderer(opts: IOpts) { }, codespan({ text }: Tokens.Codespan): string { - return styledContent(`codespan`, text, `code`) + const escapedText = text.replace(//g, `>`) + return styledContent(`codespan`, escapedText, `code`) }, listitem(item: Tokens.ListItem): string { @@ -276,6 +294,7 @@ export function initRenderer(opts: IOpts) { buildFootnotes, setOptions, reset, + parseFrontMatterAndContent, createContainer(content: string) { return styledContent(`container`, content, `section`) },