components/markdown/CodeBlock.vue (135 lines of code) (raw):

<template> <div class="md-code-block"> <div class="nuxt-content-highlight"> <!-- NOTE!!!: don't indent code tag inside the pre tag https://github.com/PrismJS/prism/issues/554 --> <pre :class="`language-${highlightResult.lang} line-numbers`" ><code v-html="highlightResult.code"></code></pre> </div> <span v-if="fileName" class="filename">{{ fileName }}</span> <code-block-copy-clipboard :source="rawCode"></code-block-copy-clipboard> </div> </template> <script lang="ts"> import Prism from 'prismjs' // enable syntax highlighting on diff language import 'prismjs/components/prism-clike' import 'prismjs/components/prism-javascript' import 'prismjs/components/prism-markdown' import 'prismjs/components/prism-typescript' import 'prismjs/components/prism-markup' import 'prismjs/components/prism-css' import 'prismjs/components/prism-diff' import 'prismjs/plugins/diff-highlight/prism-diff-highlight' import 'prism-themes/themes/prism-material-oceanic.css' import { defineComponent, ref, onMounted, computed } from '@vue/composition-api' import * as base64 from 'js-base64' import CodeBlockCopyClipboard from './CodeBlockCopyClipboard.vue' const DIFF_HIGHLIGHT_SYNTAX = /^(diff)-([\w-]+)/i function escapeHtml(unsafe) { return unsafe .replace(/&/g, '&amp;') .replace(/</g, '&lt;') .replace(/>/g, '&gt;') .replace(/"/g, '&quot;') .replace(/'/g, '&#039;') } function highlight(lang: string, rawCode: string) { lang = lang || '' let grammer: string = '' const diffLanguage = lang.match(DIFF_HIGHLIGHT_SYNTAX) if (diffLanguage) { lang = diffLanguage[2] grammer = Prism.languages.diff } lang = lang === 'vue' ? 'html' : lang if (!grammer) { grammer = Prism.languages[lang] } const highlightLanguage = diffLanguage ? `diff-${lang}` : lang let code = grammer ? Prism.highlight(rawCode, grammer, highlightLanguage) : rawCode if (!lang || !grammer) { lang = 'text' code = escapeHtml(code) } return { lang, code } } export default defineComponent({ components: { CodeBlockCopyClipboard }, props: { lang: { type: String, default: 'js' }, code: { type: String }, lineHighlights: String, fileName: String }, setup(props, context) { const rawCode = ref(base64.decode(props.code!)) const highlightResult = computed(() => { return highlight(props.lang, rawCode.value) }) return { rawCode, highlightResult } } }) </script> <style lang="postcss"> .md-code-block { position: relative; } .nuxt-content-highlight { line-height: 1em; font-size: 13px; pre[class*='language-'], code[class*='language-'] { font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace; color: #c3cee3; background: #263238; white-space: pre; word-spacing: normal; word-break: normal; word-wrap: normal; text-align: left; border: none; &::selection, & ::selection { background: rgba(0, 116, 255, 0.8); color: #fff; } } .filename { position: absolute; right: 0; top: 0; z-index: 10; font-family: DM Mono, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace; font-size: 0.875rem; line-height: 1.25rem; letter-spacing: -0.025em; line-height: 1; @apply text-blue-gray-400 mr-4 mt-3; } .clipboard { display: none; } &:hover { .clipboard { display: block; } } } </style>