105 lines
3.1 KiB
Vue
105 lines
3.1 KiB
Vue
<script setup lang="ts">
|
|
import { h, Text, computed } from 'vue';
|
|
import MarkdownShikiHighlight from './ShikiHighlight.vue';
|
|
|
|
const props = defineProps<{
|
|
content: string;
|
|
finished: boolean;
|
|
id: string;
|
|
}>();
|
|
|
|
const { $remark } = useNuxtApp();
|
|
|
|
// this function effectively removes lazy concetenation from the markdown
|
|
// normally in markdown, if you have two lines that are separated by only
|
|
// one new line. E.g.:
|
|
//
|
|
// This is some markdown text but I split
|
|
// it into two lines so it's easier to read in the editor
|
|
//
|
|
// they are *usually* concatenated into one line, however,
|
|
// since we are a web editor, with line wrapping, that behavior
|
|
// in undesirable, so pre preserve newlines by just maiking
|
|
// every new line two newlines (as long as we arent in a codeblock)
|
|
// function preprocessMarkdown(doc: string) {
|
|
// const lines = doc.split('\n');
|
|
// let inCode = false;
|
|
// let result: string[] = [];
|
|
|
|
// for (let i = 0; i < lines.length; i++) {
|
|
// const fullLine = lines[i]!;
|
|
|
|
// const match = fullLine.match(/^( {0,3})(.*)/);
|
|
// const content = match?.[2] || '';
|
|
|
|
// if (content.startsWith('```')) {
|
|
// if (!inCode) {
|
|
// inCode = true;
|
|
// } else {
|
|
// inCode = false;
|
|
// }
|
|
// result.push(fullLine);
|
|
// continue;
|
|
// }
|
|
|
|
// if (inCode) {
|
|
// result.push(fullLine);
|
|
// } else {
|
|
// if (content.trim().length === 0) {
|
|
// result.push('');
|
|
// } else {
|
|
// if (result.length > 0 && result[result.length - 1] !== '') {
|
|
// result.push('');
|
|
// }
|
|
// result.push(fullLine);
|
|
// }
|
|
// }
|
|
// }
|
|
|
|
// return result.join('\n');
|
|
// }
|
|
|
|
|
|
const ast = computed(() => {
|
|
const mdast = $remark.parse(props.content);
|
|
return $remark.runSync(mdast);
|
|
});
|
|
|
|
const renderNode = (node: any, index: number): any => {
|
|
if (node.type === 'text' || node.type === 'raw') return h(Text, node.value);
|
|
|
|
if (node.type === 'element') {
|
|
if (node.tagName === 'code') {
|
|
const isBlock = node.position?.start.line !== node.position?.end.line;
|
|
if (isBlock && node.children?.[0]?.type === 'text') {
|
|
return h(MarkdownShikiHighlight, {
|
|
key: `code-${index}`,
|
|
code: node.children[0].value,
|
|
language: node.properties?.className?.[0]?.replace('language-', '') || 'text'
|
|
});
|
|
}
|
|
}
|
|
|
|
const children = node.children?.map((child: any, i: number) => renderNode(child, i)) || [];
|
|
|
|
return h(
|
|
node.tagName,
|
|
{ ...node.properties, key: `${node.tagName}-${index}` },
|
|
children
|
|
);
|
|
}
|
|
return null;
|
|
};
|
|
|
|
const render = () => {
|
|
const children = ast.value?.children?.flatMap(renderNode) || [];
|
|
|
|
return h('div', { class: 'prose-wrapper' }, [
|
|
h('article', { class: 'markdown-body' }, children)
|
|
]);
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<render />
|
|
</template> |