main
vue 110 lines 2.58 KB
Raw
1 <template>
2 <div>
3 <vue-markdown-it
4 v-if="highlighter"
5 :source
6 :options="mdOptions"
7 :plugins="[
8 [
9 fromHighlighter(highlighter, {
10 themes: codeThemes
11 })
12 ],
13 markdownItLinkTargetBlank
14 ]"
15 class="markdown-style scrollbar-styled"
16 :class="{ 'code-bg-transparent': codeBgTransparent }"
17 @click="emit('click', $event)"
18 />
19 <div v-else>
20 {{ source }}
21 </div>
22 </div>
23 </template>
24
25 <script setup lang="ts">
26 import type MarkdownIt from "markdown-it/lib/index.mjs"
27 import type Token from "markdown-it/lib/token.mjs"
28 import type { HighlighterGeneric } from "shiki/core"
29 import { VueMarkdownIt } from "@f3ve/vue-markdown-it"
30 import { fromHighlighter } from "@shikijs/markdown-it/core"
31 import { computed, onMounted, ref, toRefs } from "vue"
32 import { codeThemes, getHighlighter } from "@/utils/highlighter"
33 import "@/assets/scss/overrides/vue-md-it-override.scss"
34
35 const props = defineProps<{
36 source: string
37 codeBgTransparent?: boolean
38 breaks?: boolean
39 }>()
40
41 const emit = defineEmits<{
42 (e: "click", value: PointerEvent): void
43 (e: "mounted"): void
44 }>()
45
46 const highlighter = ref<HighlighterGeneric<string, string> | null>(null)
47
48 function markdownItLinkTargetBlank(md: MarkdownIt): void {
49 const defaultRender =
50 md.renderer.rules.link_open ||
51 function (tokens: Token[], idx: number, options, _env, self) {
52 return self.renderToken(tokens, idx, options)
53 }
54
55 md.renderer.rules.link_open = function (tokens: Token[], idx: number, options, env, self) {
56 const token = tokens[idx]
57
58 if (token) {
59 // Aggiungi target="_blank"
60 const targetIndex = token.attrIndex("target")
61 if (targetIndex < 0) {
62 token.attrPush(["target", "_blank"])
63 } else if (token.attrs?.[targetIndex]?.[1]) {
64 token.attrs[targetIndex][1] = "_blank"
65 }
66
67 // Aggiungi rel="noopener noreferrer"
68 const relIndex = token.attrIndex("rel")
69 if (relIndex < 0) {
70 token.attrPush(["rel", "noopener noreferrer"])
71 } else if (token.attrs?.[relIndex]?.[1]) {
72 token.attrs[relIndex][1] = "noopener noreferrer"
73 }
74 }
75
76 return defaultRender(tokens, idx, options, env, self)
77 }
78 }
79
80 const { source, codeBgTransparent, breaks } = toRefs(props)
81
82 const mdOptions = computed(() => ({
83 ...(breaks.value ? { breaks: true } : {})
84 }))
85
86 onMounted(async () => {
87 highlighter.value = (await getHighlighter()) as unknown as HighlighterGeneric<string, string>
88 emit("mounted")
89 })
90 </script>
91
92 <style lang="scss" scoped>
93 .markdown-style {
94 :deep() {
95 & > * {
96 margin-bottom: 15px;
97 }
98 }
99
100 &.code-bg-transparent {
101 :deep() {
102 & > pre {
103 & > code {
104 background-color: transparent;
105 }
106 }
107 }
108 }
109 }
110 </style>