Files
remanso/src/hooks/useMarkdown.hook.ts
2026-02-11 03:12:52 +01:00

136 lines
3.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import markdownItLatex from "@vscode/markdown-it-katex"
import MarkdownIt, { Options, Renderer, Token } from "markdown-it"
import blockEmbedPlugin from "markdown-it-block-embed"
import markdownItCheckbox from "markdown-it-checkbox"
import MarkdownItGitHubAlerts from "markdown-it-github-alerts"
import markdownItIframe from "markdown-it-iframe"
import Shikiji from "markdown-it-shikiji"
import { Ref, toValue } from "vue"
import { decodeBase64ToUTF8 } from "@/utils/decodeBase64ToUTF8"
import { html5Media } from "@/utils/markdown/markdown-html5-media"
import { twitterPlugin } from "@/utils/markdown/markdown-it-twitter"
import mermaid from "mermaid"
const markdownItMermaidExtractor = (md: MarkdownIt) => {
const defaultFence =
md.renderer.rules.fence ||
function (
tokens: Array<Token>,
index: number,
options: Options,
_: unknown,
self: Renderer,
) {
return self.renderToken(tokens, index, options)
}
md.renderer.rules.fence = function (
tokens: Array<Token>,
index: number,
options: Options,
env: unknown,
self: Renderer,
) {
const token = tokens[index]
if (token.info.trim() === "mermaid") {
const content = token.content.trim()
return `<pre class="mermaid">\n${md.utils.escapeHtml(content)}\n</pre>\n`
}
return defaultFence(tokens, index, options, env, self)
}
}
const md = new MarkdownIt({
typographer: true,
quotes: ["«\xA0", "\xA0»", "\xA0", "\xA0"],
})
.use(markdownItMermaidExtractor)
.use(html5Media)
.use(blockEmbedPlugin, {
youtube: {
width: "100%",
height: 300,
},
})
.use(twitterPlugin)
.use(markdownItCheckbox)
.use(markdownItLatex)
.use(markdownItIframe, {
width: "100%",
})
.use(MarkdownItGitHubAlerts)
let shikijiInitialized = false
export const useShikiji = async () => {
if (shikijiInitialized) {
return
}
shikijiInitialized = true
md.use(
await Shikiji({
themes: {
light: "vitesse-light",
dark: "vitesse-black",
},
langs: [
"bash",
"javascript",
"typescript",
"markdown",
"mermaid",
"html",
"css",
"json",
],
}),
)
}
let mermaidInitialized = false
export const runMermaid = (querySelector: string) => {
if (!mermaidInitialized) {
mermaidInitialized = true
mermaid.initialize({
theme: "dark",
startOnLoad: false,
flowchart: { curve: "natural" },
})
}
mermaid.run({
querySelector,
})
}
const rules: Renderer.RenderRuleRecord = {
table_open: () =>
'<div class="overflow-x-auto"><table class="table table-zebra">',
table_close: () => "</table></div>",
}
md.renderer.rules = { ...md.renderer.rules, ...rules }
export const markdownBuilder = (defaultPrefix?: Ref<string> | string) => {
const getRawContent = (content: string) => decodeBase64ToUTF8(content)
const renderFromUTF8 = (content: string, prefix?: string) =>
content
? md.render(content, {
docId: defaultPrefix ? toValue(defaultPrefix) : (prefix ?? ""),
})
: ""
return {
toHTML: (content: string) => (content ? md.render(content) : ""),
render: (content: string, prefix?: string) =>
renderFromUTF8(decodeBase64ToUTF8(content), prefix),
renderFromUTF8,
getRawContent,
}
}