diff --git a/assets/workers/example.worker.js b/assets/workers/example.worker.js new file mode 100644 index 0000000..ce814f4 --- /dev/null +++ b/assets/workers/example.worker.js @@ -0,0 +1,9 @@ +addEventListener('message', (e) => { + const result = heavyTask(e.data) + postMessage(result) +}) +postMessage('Hello World!ABC') +function heavyTask(data) { + // 执行耗时计算 + return data * 2 +} diff --git a/components/Home/Blog/Marked.vue b/components/Home/Blog/Marked.vue index 12fb902..ddd95fb 100644 --- a/components/Home/Blog/Marked.vue +++ b/components/Home/Blog/Marked.vue @@ -3,13 +3,14 @@ import {Marked} from 'marked'; // 默认导入所有语言 import hljs from 'highlight.js'; import 'highlight.js/styles/atom-one-light.css'; -// 注册语言 -// import hljs from 'highlight.js/lib/core'; -// import javascript from 'highlight.js/lib/languages/javascript'; -// hljs.registerLanguage('javascript', javascript); +import DOMPurify from 'dompurify'; +import markedFootnote from 'marked-footnote' +import markedCodeFormat from 'marked-code-format' +import { markedEmoji } from 'marked-emoji'; const marked = new Marked(); + // 自定义行号注入函数 const injectLineNumbers = (highlightedCode: string) => { const lines = highlightedCode.split('\n') @@ -25,24 +26,93 @@ marked.use({ async: true, pedantic: false, gfm: true, - silent: true, + silent: false, + breaks: false, renderer: { code: ({text, lang}) => { - console.log(text); + // console.log(text); const validLang = hljs.getLanguage(lang) ? lang : 'plaintext' const highlighted = hljs.highlight(text, {language: lang}).value - console.log(highlighted) + // console.log(highlighted) const withLineNumbers = injectLineNumbers(highlighted) return `
${withLineNumbers}
`;
}
}
})
+// 支持脚注[^1] 会影响原始文本
+marked.use(markedFootnote())
+marked.use(markedCodeFormat({
+ /* Prettier options */
+ }))
+
+const markedEmojiOptions = {
+ emojis: {
+ "heart": "❤️",
+ "tada": "🎉"
+ },
+ renderer: (token) => {
+ console.log(token)
+ return token.emoji
+ }
+};
+marked.use(markedEmoji(markedEmojiOptions))
const content = ref(`
# Hello Vue 3!
+AAA
+AAA
+
+BBB
+B
+
+Hello :smile:
+
+- [ ] xsxs
+
+I :heart: marked! :tada:
+
+## 脚注
+
+这是标记[^1]
+
+[^1]: This is a footnote content.
+
+Here is a simple footnote[^1]. With some additional[^3] text after it[^@#$%] and without disrupting the blocks[^3].
+
+[^2]: The first paragraph of the definition2.
+
+[^3]: The first paragraph of the definition3.
+
+[^4]: ABC
+
**Markdown 内容示例:**
+1. 潇洒些
+2. 下洒下阿萨[^4]
+
+3. 想啊伤心啊伤心啊
+ 1. 下洒下阿萨
+ 2. 下洒下阿萨
+ a. sxaas
+ b. xsaxa
+ c. xsaxa
+ - xasx
+ - cascade
+
+1. 潇洒些
+2. 下洒下阿萨
+3. 想啊伤心啊伤心啊
+ 1. 下洒下阿萨
+ 2. 下洒下阿萨
+ a. sxaas
+ b. xsaxa
+ c. xsaxa
+ - xasx
+ - cascade
+
+> 引用
+
- 列表项
- 另一项
@@ -63,19 +133,95 @@ function greet() {
| sanitizer\t | function | \t自定义 HTML 过滤函数 | \t- |
| silent\t | boolean | \t静默模式:忽略解析错误(如未闭合的代码块) | \tfalse |
`);
+// 使用编译
+async function make(contents: string){
+ // 过滤不能识别,难识别的字符,空字符
+ let content = contents.replace(/^[\u200B\u200C\u200D\u200E\u200F\uFEFF]/,"");
+ // 编译
+ content = await marked.parse(content)
+ // 加强安全,防止xss攻击
+ content = DOMPurify.sanitize(content);
+ return content
+}
-const compiledMarkdown = ref(await marked.parse(content.value));
+const compiledMarkdown = ref(await make(content.value));
+
+const editableDiv = ref(null); // 绑定可编辑的 div
+const rawText = ref(""); // 存储纯文本内容
+
+// 实时监听输入事件
+const handleInput = async (event) => {
+ if (editableDiv.value) {
+ // 直接获取去标签后的纯文本
+ rawText.value = editableDiv.value.innerText.trim().replace(/ /g, ' ');
+ console.log(rawText.value)
+ compiledMarkdown.value = await make(rawText.value);
+ }
+};
+
+// 手动获取内容(通过按钮触发)
+const getContent = () => {
+ if (editableDiv.value) {
+ const text = editableDiv.value.innerText.trim();
+ console.log("手动获取:", text);
+ }
+};
+
+// 处理粘贴内容(自动去除粘贴的 HTML 标签)
+const handlePaste = (event) => {
+ event.preventDefault(); // 阻止默认粘贴行为
+ const text = event.clipboardData.getData("text/plain"); // 获取纯文本
+ document.execCommand("insertText", false, text); // 插入纯文本
+};
+onMounted(async () => {
+
+ const worker = new Worker(new URL("~/assets/workers/example.worker.js", import.meta.url));
+ worker.postMessage("Hello Worker");
+
+ worker.onmessage = (e) => {
+ console.log(e)
+ worker.terminate();
+ };
+})
-
+ ${withLineNumbers}
`;
+ }
+}
+
+
+// 编辑器Marked编译器
+const editorMarked = new Marked({
+ ...defaoltMarkOption,
+ renderer: editorRenderOption
+});
+// 显示器Marked编译器
+const viewerMarked = new Marked({
+ ...defaoltMarkOption,
+ renderer: viewerRenderOption
+});
+
+
+// 编辑器插件
+editorMarked.use(markedCodeFormat()).use()
+
+// 显示器插件
+// 自定义表情快速插入
+const markedEmojiOptions = {
+ emojis: {
+ "heart": "❤️",
+ "tada": "🎉"
+ },
+ renderer: (token) => {
+ console.log(token)
+ return token.emoji
+ }
+};
+// 支持脚注[^1] 会影响原始文本
+viewerMarked.use(markedFootnote()).use(markedCodeFormat()).use(markedEmoji(markedEmojiOptions))
+
+addEventListener('message', async (e) => {
+ console.log(e.data)
+ switch (e.data.type) {
+ case 'render': {
+ // const editContent = await makeEditContent(e.data.content)
+ const viewContent = await makeViewContent(e.data.content)
+ postMessage({
+ type: 'render',
+ content: {
+ viewContent,
+ // editContent,
+ },
+ })
+ break;
+ }
+ }
+})
+
+
+
+// 函数
+
+// 使用编译
+async function makeEditContent(contents){
+ // 去除html innerText两端的空格,将 转化为空格
+ let content = contents.trim().replace(/ /g, ' ');
+ // 过滤不能识别,难识别的字符,空字符
+ content = content.replace(/^[\u200B\u200C\u200D\u200E\u200F\uFEFF]/,"");
+ // 编译
+ content = await editorMarked.parse(content)
+ return content
+}
+async function makeViewContent(contents){
+ // 过滤不能识别,难识别的字符,空字符
+ let content = contents.replace(/^[\u200B\u200C\u200D\u200E\u200F\uFEFF]/,"");
+ // 编译
+ content = await viewerMarked.parse(content)
+ return content
+}
diff --git a/nuxt.config.ts b/nuxt.config.ts
index 4609fe7..3e39a5f 100644
--- a/nuxt.config.ts
+++ b/nuxt.config.ts
@@ -13,6 +13,10 @@ export default defineNuxtConfig({
esbuildOptions: {
target: 'esnext'
}
+ },
+
+ worker: {
+ format: 'es'
}
},
compatibilityDate: '2024-11-01',
diff --git a/package-lock.json b/package-lock.json
index ad4f78e..554b80c 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -16,6 +16,9 @@
"highlight.js": "^11.11.1",
"jsonwebtoken": "^9.0.2",
"marked": "^15.0.11",
+ "marked-code-format": "^1.1.6",
+ "marked-emoji": "^2.0.0",
+ "marked-footnote": "^1.2.4",
"mysql2": "^3.14.0",
"nuxt": "^3.16.2",
"redis": "^4.7.0",
@@ -6157,6 +6160,15 @@
"node": ">= 4.0.0"
}
},
+ "node_modules/attributes-parser": {
+ "version": "2.2.3",
+ "resolved": "https://registry.npmmirror.com/attributes-parser/-/attributes-parser-2.2.3.tgz",
+ "integrity": "sha512-zjOUWt95la8AdUO+kP1GBOonWrV5jy9NjJP+z9tva/DSA6FIzGKcN/gk3tdqQf/pOeB8dkyd3FCPrjhELMmrkg==",
+ "license": "MIT",
+ "dependencies": {
+ "json-loose": "^1.2.4"
+ }
+ },
"node_modules/autoprefixer": {
"version": "10.4.21",
"resolved": "https://registry.npmmirror.com/autoprefixer/-/autoprefixer-10.4.21.tgz",
@@ -7710,7 +7722,7 @@
},
"node_modules/dompurify": {
"version": "3.2.5",
- "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.2.5.tgz",
+ "resolved": "https://registry.npmmirror.com/dompurify/-/dompurify-3.2.5.tgz",
"integrity": "sha512-mLPd29uoRe9HpvwP2TxClGQBzGXeEC/we/q+bFlmPPmj2p2Ugl3r6ATu/UU1v77DXNcehiBg9zsr1dREyA/dJQ==",
"license": "(MPL-2.0 OR Apache-2.0)",
"optionalDependencies": {
@@ -10405,6 +10417,15 @@
"integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
"license": "MIT"
},
+ "node_modules/json-loose": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmmirror.com/json-loose/-/json-loose-1.2.4.tgz",
+ "integrity": "sha512-lwMWNC5pvVI33rhYWmAsmtICWE2IH7euDY/iIPeMFE5AuzAifYgqQrjqSMzwbrFV6MWPs41XD+CajElHI4cZMQ==",
+ "license": "MIT",
+ "dependencies": {
+ "moo": "^0.5.2"
+ }
+ },
"node_modules/json-schema-to-typescript-lite": {
"version": "14.1.0",
"resolved": "https://registry.npmmirror.com/json-schema-to-typescript-lite/-/json-schema-to-typescript-lite-14.1.0.tgz",
@@ -11403,6 +11424,37 @@
"node": ">= 18"
}
},
+ "node_modules/marked-code-format": {
+ "version": "1.1.6",
+ "resolved": "https://registry.npmmirror.com/marked-code-format/-/marked-code-format-1.1.6.tgz",
+ "integrity": "sha512-DSzefm4DvOOGNdtCx+WekoLyXd8rwcEROSzyydnbJ3rQ2vuE2dKW8cpJui/ulUUHMvyrF9TCq351Fqjd81ocEQ==",
+ "license": "MIT",
+ "dependencies": {
+ "attributes-parser": "^2.2.3"
+ },
+ "peerDependencies": {
+ "marked": ">=7.0.0",
+ "prettier": ">=3.0.0"
+ }
+ },
+ "node_modules/marked-emoji": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmmirror.com/marked-emoji/-/marked-emoji-2.0.0.tgz",
+ "integrity": "sha512-oTZ8fqbdVDHFQnqCE1tg4ND7zEd7cUVNHliR9Ldu4eys0J86uz/5Uksjd2mt5xcX16OOScDEr3MmPjajI/ZDHA==",
+ "license": "MIT",
+ "peerDependencies": {
+ "marked": ">=4 <16"
+ }
+ },
+ "node_modules/marked-footnote": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmmirror.com/marked-footnote/-/marked-footnote-1.2.4.tgz",
+ "integrity": "sha512-DB2Kl+wFh6YwZd70qABMY6WUkG1UuyqoNTFoDfGyG79Pz24neYtLBkB+45a7o72V7gkfvbC3CGzIYFobxfMT1Q==",
+ "license": "MIT",
+ "peerDependencies": {
+ "marked": ">=7.0.0"
+ }
+ },
"node_modules/math-intrinsics": {
"version": "1.1.0",
"resolved": "https://registry.npmmirror.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
@@ -11670,6 +11722,12 @@
"node": ">=14"
}
},
+ "node_modules/moo": {
+ "version": "0.5.2",
+ "resolved": "https://registry.npmmirror.com/moo/-/moo-0.5.2.tgz",
+ "integrity": "sha512-iSAJLHYKnX41mKcJKjqvnAN9sf0LMDTXDEvFv+ffuRR9a1MIuXLjMNL6EsnDHSkKLTWNqQQ5uo61P4EbU4NU+Q==",
+ "license": "BSD-3-Clause"
+ },
"node_modules/mrmime": {
"version": "2.0.1",
"resolved": "https://registry.npmmirror.com/mrmime/-/mrmime-2.0.1.tgz",
@@ -13539,6 +13597,22 @@
"node": ">= 0.8.0"
}
},
+ "node_modules/prettier": {
+ "version": "3.5.3",
+ "resolved": "https://registry.npmmirror.com/prettier/-/prettier-3.5.3.tgz",
+ "integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==",
+ "license": "MIT",
+ "peer": true,
+ "bin": {
+ "prettier": "bin/prettier.cjs"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/prettier/prettier?sponsor=1"
+ }
+ },
"node_modules/pretty-bytes": {
"version": "6.1.1",
"resolved": "https://registry.npmmirror.com/pretty-bytes/-/pretty-bytes-6.1.1.tgz",
diff --git a/package.json b/package.json
index 5c6d5e8..bb1a21d 100644
--- a/package.json
+++ b/package.json
@@ -23,6 +23,9 @@
"highlight.js": "^11.11.1",
"jsonwebtoken": "^9.0.2",
"marked": "^15.0.11",
+ "marked-code-format": "^1.1.6",
+ "marked-emoji": "^2.0.0",
+ "marked-footnote": "^1.2.4",
"mysql2": "^3.14.0",
"nuxt": "^3.16.2",
"redis": "^4.7.0",
diff --git a/pages/home/blog.vue b/pages/home/blog.vue
index 4487f6e..63ed563 100644
--- a/pages/home/blog.vue
+++ b/pages/home/blog.vue
@@ -66,7 +66,6 @@ const formatBlogMenuListToPidMap = (blogMenuList: Array