Address review comments

Support ctrl+enter
Support mermaid-nocode
Use `contenteditable="plaintext-only"`

Co-authored-by: Alois Klink <alois@aloisklink.com>
This commit is contained in:
Sidharth Vinod
2024-03-11 17:03:26 +05:30
parent 6422175ef2
commit 08a7f662ea
3 changed files with 32 additions and 37 deletions

View File

@@ -252,11 +252,12 @@ export function transformMarkdownAst({
node.lang = MERMAID_KEYWORD; node.lang = MERMAID_KEYWORD;
return [node]; return [node];
} else if (MERMAID_EXAMPLE_KEYWORDS.includes(node.lang)) { } else if (MERMAID_EXAMPLE_KEYWORDS.includes(node.lang)) {
// Return 2 nodes: // If Vitepress, return only the original node with the language now set to 'mermaid-example' (will be rendered using custom renderer)
// Else Return 2 nodes:
// 1. the original node with the language now set to 'mermaid-example' (will be rendered as code), and // 1. the original node with the language now set to 'mermaid-example' (will be rendered as code), and
// 2. a copy of the original node with the language set to 'mermaid' (will be rendered as a diagram) // 2. a copy of the original node with the language set to 'mermaid' (will be rendered as a diagram)
node.lang = MERMAID_CODE_ONLY_KEYWORD; node.lang = MERMAID_CODE_ONLY_KEYWORD;
return [node, Object.assign({}, node, { lang: MERMAID_KEYWORD })]; return vitepress ? [node] : [node, Object.assign({}, node, { lang: MERMAID_KEYWORD })];
} }
// Transform these blocks into block quotes. // Transform these blocks into block quotes.

View File

@@ -9,22 +9,15 @@ const MermaidExample = async (md: MarkdownRenderer) => {
md.renderer.rules.fence = (tokens, index, options, env, slf) => { md.renderer.rules.fence = (tokens, index, options, env, slf) => {
const token = tokens[index]; const token = tokens[index];
const language = token.info.trim();
if (token.info.trim() === 'mermaid-example') { if (language.startsWith('mermaid')) {
if (!md.options.highlight) {
// this function is always created by vitepress, but we need to check it
// anyway to make TypeScript happy
throw new Error(
'Missing MarkdownIt highlight function (should be automatically created by vitepress'
);
}
return '';
} else if (token.info.trim() === 'mermaid') {
const key = index; const key = index;
return ` return `
<Suspense> <Suspense>
<template #default> <template #default>
<Mermaid id="mermaid-${key}" graph="${encodeURIComponent(token.content)}"></Mermaid> <Mermaid id="mermaid-${key}" :showCode="${
language === 'mermaid-example'
}" graph="${encodeURIComponent(token.content)}"></Mermaid>
</template> </template>
<!-- loading state via #fallback slot --> <!-- loading state via #fallback slot -->
<template #fallback> <template #fallback>
@@ -32,25 +25,18 @@ const MermaidExample = async (md: MarkdownRenderer) => {
</template> </template>
</Suspense> </Suspense>
`; `;
} } else if (language === 'warning') {
if (token.info.trim() === 'warning') {
return `<div class="warning custom-block"><p class="custom-block-title">WARNING</p><p>${token.content}}</p></div>`; return `<div class="warning custom-block"><p class="custom-block-title">WARNING</p><p>${token.content}}</p></div>`;
} } else if (language === 'note') {
if (token.info.trim() === 'note') {
return `<div class="tip custom-block"><p class="custom-block-title">NOTE</p><p>${token.content}}</p></div>`; return `<div class="tip custom-block"><p class="custom-block-title">NOTE</p><p>${token.content}}</p></div>`;
} } else if (language === 'regexp') {
if (token.info.trim() === 'regexp') {
// shiki doesn't yet support regexp code blocks, but the javascript // shiki doesn't yet support regexp code blocks, but the javascript
// one still makes RegExes look good // one still makes RegExes look good
token.info = 'javascript'; token.info = 'javascript';
// use trimEnd to move trailing `\n` outside if the JavaScript regex `/` block // use trimEnd to move trailing `\n` outside if the JavaScript regex `/` block
token.content = `/${token.content.trimEnd()}/\n`; token.content = `/${token.content.trimEnd()}/\n`;
return defaultRenderer(tokens, index, options, env, slf); return defaultRenderer(tokens, index, options, env, slf);
} } else if (language === 'jison') {
if (token.info.trim() === 'jison') {
return `<div class="language-"> return `<div class="language-">
<button class="copy"></button> <button class="copy"></button>
<span class="lang">jison</span> <span class="lang">jison</span>

View File

@@ -1,14 +1,16 @@
<template> <template>
<div v-if="props.showCode">
<h5>Code:</h5> <h5>Code:</h5>
<div class="language-mermaid"> <div class="language-mermaid">
<button class="copy"></button> <button class="copy"></button>
<span class="lang">mermaid</span> <span class="lang">mermaid</span>
<pre><code contenteditable="true" @input="updateCode" @keydown.meta.enter="renderChart" ref="editableContent" class="editable-code"></code></pre> <pre><code contenteditable="plaintext-only" @input="updateCode" @keydown.meta.enter="renderChart" @keydown.ctrl.enter="renderChart" ref="editableContent" class="editable-code"></code></pre>
<div class="buttons-container"> <div class="buttons-container">
<span>{{ ctrlSymbol }} + Enter</span><span>|</span> <span>{{ ctrlSymbol }} + Enter</span><span>|</span>
<button @click="renderChart">Run </button> <button @click="renderChart">Run </button>
</div> </div>
</div> </div>
</div>
<div v-html="svg"></div> <div v-html="svg"></div>
</template> </template>
@@ -25,6 +27,10 @@ const props = defineProps({
type: String, type: String,
required: true, required: true,
}, },
showCode: {
type: Boolean,
default: true,
},
}); });
const svg = ref(''); const svg = ref('');
@@ -42,10 +48,12 @@ onMounted(async () => {
mut = new MutationObserver(() => renderChart()); mut = new MutationObserver(() => renderChart());
mut.observe(document.documentElement, { attributes: true }); mut.observe(document.documentElement, { attributes: true });
if (editableContent.value) {
// Set the initial value of the contenteditable element // Set the initial value of the contenteditable element
// We cannot bind using `{{ code }}` because it will rerender the whole component // We cannot bind using `{{ code }}` because it will rerender the whole component
// when the value changes, shifting the cursor when enter is used // when the value changes, shifting the cursor when enter is used
editableContent.value.textContent = code.value; editableContent.value.textContent = code.value;
}
await renderChart(); await renderChart();