UNPKG

@rtdui/editor

Version:

React rich text editor based on tiptap

75 lines (72 loc) 2.57 kB
import { findChildren } from "@tiptap/core"; import { bundledLanguages, bundledThemes, createHighlighter } from "shiki"; //#region packages/editor/src/RichTextEditor/tiptap_extensions/extension-code-block-shiki/shiki-highlighter.ts let highlighter; let highlighterPromise; const loadingLanguages = /* @__PURE__ */ new Set(); const loadingThemes = /* @__PURE__ */ new Set(); function getShiki() { return highlighter; } /** * Load the highlighter. Makes sure the highlighter is only loaded once. */ function loadHighlighter(opts) { if (!highlighter && !highlighterPromise) { highlighterPromise = createHighlighter({ themes: opts.themes.filter((theme) => !!theme && theme in bundledThemes), langs: opts.languages.filter((lang) => !!lang && lang in bundledLanguages) }).then((h) => { highlighter = h; }); return highlighterPromise; } if (highlighterPromise) return highlighterPromise; } /** * Loads a theme if it's valid and not yet loaded. * @returns true or false depending on if it got loaded. */ async function loadTheme(theme) { if (highlighter && !highlighter.getLoadedThemes().includes(theme) && !loadingThemes.has(theme) && theme in bundledThemes) { loadingThemes.add(theme); await highlighter.loadTheme(theme); loadingThemes.delete(theme); return true; } return false; } /** * Loads a language if it's valid and not yet loaded * @returns true or false depending on if it got loaded. */ async function loadLanguage(language) { if (highlighter && !highlighter.getLoadedLanguages().includes(language) && !loadingLanguages.has(language) && language in bundledLanguages) { loadingLanguages.add(language); await highlighter.loadLanguage(language); loadingLanguages.delete(language); return true; } return false; } /** * Initializes the highlighter based on the prosemirror document, * with the themes and languages in the document. */ async function initHighlighter({ doc, name, defaultTheme, defaultLanguage }) { const codeBlocks = findChildren(doc, (node) => node.type.name === name); const themes = [ ...codeBlocks.map((block) => block.node.attrs.theme), defaultTheme, "one-light", "one-dark-pro" ]; const languages = [...codeBlocks.map((block) => block.node.attrs.language), defaultLanguage]; if (!highlighter) await loadHighlighter({ languages, themes }); else await Promise.all([...themes.flatMap((theme) => loadTheme(theme)), ...languages.flatMap((language) => !!language && loadLanguage(language))]); } //#endregion export { getShiki, initHighlighter, loadHighlighter, loadLanguage, loadTheme };