| 1 | import type { BundledLanguage, BundledTheme, HighlighterGeneric } from "shiki" |
| 2 | import { createHighlighter } from "shiki" |
| 3 | import { createOnigurumaEngine } from "shiki/engine/oniguruma" |
| 4 | |
| 5 | const THEME_LIGHT = "slack-ochin" |
| 6 | const THEME_DARK = "aurora-x" |
| 7 | const LEADING_WHITESPACE_REGEX = /^\s+/ |
| 8 | const TAB_REGEX = /\t/g |
| 9 | |
| 10 | let highlighterInstance: HighlighterGeneric<BundledLanguage, BundledTheme> | null = null |
| 11 | let highlighterPromise: Promise<HighlighterGeneric<BundledLanguage, BundledTheme>> | null = null |
| 12 | |
| 13 | export async function getHighlighter() { |
| 14 | if (highlighterInstance) { |
| 15 | return highlighterInstance |
| 16 | } |
| 17 | if (highlighterPromise) { |
| 18 | return highlighterPromise |
| 19 | } |
| 20 | |
| 21 | highlighterPromise = createHighlighter({ |
| 22 | themes: [import("shiki/themes/slack-ochin.mjs"), import("shiki/themes/aurora-x.mjs")], |
| 23 | langs: [ |
| 24 | import("shiki/langs/javascript.mjs"), |
| 25 | import("shiki/langs/typescript.mjs"), |
| 26 | import("shiki/langs/powershell.mjs"), |
| 27 | import("shiki/langs/shellscript.mjs"), |
| 28 | import("shiki/langs/json.mjs"), |
| 29 | import("shiki/langs/xml.mjs"), |
| 30 | import("shiki/langs/yaml.mjs"), |
| 31 | import("shiki/langs/html.mjs"), |
| 32 | import("shiki/langs/scss.mjs"), |
| 33 | import("shiki/langs/css.mjs"), |
| 34 | import("shiki/langs/csharp.mjs"), |
| 35 | import("shiki/langs/http.mjs"), |
| 36 | import("shiki/langs/sql.mjs"), |
| 37 | import("shiki/langs/lua.mjs"), |
| 38 | import("shiki/langs/vb.mjs"), |
| 39 | import("shiki/langs/php.mjs") |
| 40 | ], |
| 41 | engine: createOnigurumaEngine(() => import("shiki/wasm")) |
| 42 | }).then(instance => { |
| 43 | highlighterInstance = instance |
| 44 | return instance |
| 45 | }) |
| 46 | |
| 47 | return highlighterPromise |
| 48 | } |
| 49 | |
| 50 | export const codeThemes = { |
| 51 | light: THEME_LIGHT, |
| 52 | dark: THEME_DARK |
| 53 | } |
| 54 | |
| 55 | export function resetIndent(el: HTMLElement) { |
| 56 | if (el) { |
| 57 | let lines: string[] = el.innerHTML?.split("\n") |
| 58 | |
| 59 | if (lines?.length) { |
| 60 | if (lines[0] === "") { |
| 61 | lines.shift() |
| 62 | } |
| 63 | |
| 64 | const firstLine = lines[0] |
| 65 | |
| 66 | if (!firstLine) return |
| 67 | |
| 68 | const matches = LEADING_WHITESPACE_REGEX.exec(firstLine) |
| 69 | const indentation = matches !== null ? matches[0] : null |
| 70 | if (indentation) { |
| 71 | lines = lines.map(line => { |
| 72 | line = line.replace(indentation, "") |
| 73 | return line.replace(TAB_REGEX, " ") |
| 74 | }) |
| 75 | |
| 76 | el.innerHTML = lines.join("\n").trim() |
| 77 | } |
| 78 | } |
| 79 | } |
| 80 | } |