diff --git a/package.json b/package.json
index 27554d8..2335dec 100644
--- a/package.json
+++ b/package.json
@@ -11,8 +11,8 @@
"gen": "openapi-generator-cli generate -i ./api/swagger.yaml -g typescript-axios -o ./src/api"
},
"dependencies": {
+ "@iktakahiro/markdown-it-katex": "^4.0.1",
"@notable/html2markdown": "^2.0.3",
- "@traptitech/markdown-it-katex": "^3.6.0",
"animate.css": "^4.1.1",
"axios": "^1.7.7",
"highlight.js": "^11.10.0",
@@ -48,6 +48,7 @@
"naive-ui": "^2.39.0",
"postcss": "^8.4.45",
"prettier": "^3.3.3",
+ "sass-embedded": "^1.79.5",
"tailwindcss": "^3.4.10",
"typescript": "^5.5.2",
"unplugin-auto-import": "^0.18.2",
diff --git a/src/components.d.ts b/src/components.d.ts
index 57e2021..29d7c11 100644
--- a/src/components.d.ts
+++ b/src/components.d.ts
@@ -17,6 +17,7 @@ declare module 'vue' {
LeftSettings: typeof import('./components/settings/LeftSettings.vue')['default']
LibrarySettings: typeof import('./components/settings/LibrarySettings.vue')['default']
Lottie: typeof import('./components/Lottie.vue')['default']
+ Markdown: typeof import('./components/markdown/index.vue')['default']
Mask: typeof import('./components/Mask.vue')['default']
MemorySettings: typeof import('./components/settings/MemorySettings.vue')['default']
Menu: typeof import('./components/Menu.vue')['default']
diff --git a/src/components/chat/MessageList.vue b/src/components/chat/MessageList.vue
index d053715..d547950 100644
--- a/src/components/chat/MessageList.vue
+++ b/src/components/chat/MessageList.vue
@@ -35,7 +35,14 @@
-
+
@@ -51,11 +58,14 @@
{{ userStore.user.name }}
-
+ > -->
+
+
+
@@ -103,11 +113,10 @@
{{ message.assistant?.name }}
-
+
+
+
+
@@ -126,50 +135,9 @@ import { Ref } from "vue";
import { EntityChatMessage } from "@/api";
import { useUserStore } from "@/stores/user";
import leaflowPng from "@/assets/images/leaflow.png";
-import markdownKatex from "@traptitech/markdown-it-katex";
-import markdownIt from "markdown-it";
-// highlightjs
-import hljs from "highlight.js";
+import Markdown from "@/components/markdown/index.vue";
import config from "@/config/config";
-const mdIt = markdownIt();
-const mdInited = ref(true);
-
-const unsupportedLanguages = ["assembly", "blade", "vue"];
-mdIt.options.highlight = function (str: string, lang: string) {
- // TODO: 前面的区域以后再来探索吧
- lang = "text"
- if (!lang || unsupportedLanguages.includes(lang)) {
- // return str;
- lang = "text"
- }
-
- return hljs.highlight(str, { language: lang }).value;
-};
-mdIt.use(markdownKatex, {
- throwOnError: false,
- errorColor: "#cc0000",
- output: "html",
-});
-
-// async function initMD() {
-// mdIt.use(
-// await Shiki({
-// themes: {
-// light: "vitesse-light",
-// dark: "vitesse-dark",
-// },
-// })
-// );
-
-// mdIt.use(markdownKatex, {
-// throwOnError: false,
-// errorColor: "#cc0000",
-// output: "html",
-// });
-
-// mdInited.value = true;
-// }
const userStore = useUserStore();
const props = defineProps({
@@ -182,7 +150,4 @@ const props = defineProps({
const chat_messages = toRef(props, "chat_messages") as Ref;
const fileBaseUrl = config.backend + "/api/v1/files";
-onMounted(() => {
- // initMD();
-});
diff --git a/src/components/markdown/aPlugin/index.ts b/src/components/markdown/aPlugin/index.ts
new file mode 100644
index 0000000..63a0815
--- /dev/null
+++ b/src/components/markdown/aPlugin/index.ts
@@ -0,0 +1,17 @@
+import type { PluginWithOptions } from 'markdown-it'
+
+export interface APluginOptions {}
+
+export const aPlugin: PluginWithOptions = (
+ md,
+ options = {}
+): void => {
+ md.renderer.rules.link_open = (tokens, idx, options, env, slf) => {
+ const aIndex = tokens[idx].attrIndex('href')
+
+ if (aIndex !== -1) {
+ tokens[idx].attrs.push(['target', '_blank'])
+ }
+ return slf.renderToken(tokens, idx, options)
+ }
+}
diff --git a/src/components/markdown/codePlugin/index.ts b/src/components/markdown/codePlugin/index.ts
new file mode 100644
index 0000000..4529723
--- /dev/null
+++ b/src/components/markdown/codePlugin/index.ts
@@ -0,0 +1,52 @@
+import type { PluginWithOptions } from 'markdown-it'
+
+import { resolveLanguage } from './resolveLanguage.js'
+import { resolveLineNumbers } from './resolveLineNumbers.js'
+
+export interface CodePluginOptions {
+ lineNumbers?: boolean | number
+}
+
+export const codePlugin: PluginWithOptions = (
+ md,
+ { lineNumbers = true } = {}
+): void => {
+ md.renderer.rules.fence = (tokens, idx, options, env, slf) => {
+ const token = tokens[idx]
+ const info = token.info ? md.utils.unescapeAll(token.info).trim() : ''
+ const language = resolveLanguage(info)
+ const languageClass = `${options.langPrefix}${language.name}`
+
+ const code =
+ options.highlight?.(token.content, language.name, '') ||
+ md.utils.escapeHtml(token.content)
+
+ token.attrJoin('class', languageClass)
+ let result = code.startsWith('${code}
`
+ result = `${result}`
+ const lines = code.split('\n').slice(0, -1)
+
+ const useLineNumbers =
+ resolveLineNumbers(info) ??
+ (typeof lineNumbers === 'number'
+ ? lines.length >= lineNumbers
+ : lineNumbers)
+ if (useLineNumbers) {
+ const lineNumbersCode = lines
+ .map(() => ``)
+ .join('')
+
+ result = `${lineNumbersCode}
${result}`
+ }
+
+ result = ``
+
+ return result
+ }
+}
diff --git a/src/components/markdown/codePlugin/languages.ts b/src/components/markdown/codePlugin/languages.ts
new file mode 100644
index 0000000..ba8bc0b
--- /dev/null
+++ b/src/components/markdown/codePlugin/languages.ts
@@ -0,0 +1,105 @@
+/**
+ * Language type for syntax highlight
+ */
+export interface HighlightLanguage {
+ /**
+ * Name of the language
+ *
+ * The name to be used for the class name,
+ * e.g. `class="language-typescript"`
+ */
+ name: string
+
+ /**
+ * Extension of the language
+ *
+ * The file extension, which will be used for the
+ * class name, e.g. `class="ext-ts"`
+ */
+ ext: string
+
+ /**
+ * Aliases that point to this language
+ *
+ * Do not conflict with other languages
+ */
+ aliases: string[]
+}
+
+export const languageBash: HighlightLanguage = {
+ name: 'bash',
+ ext: 'sh',
+ aliases: ['bash', 'sh', 'shell', 'zsh']
+}
+
+export const languageCsharp: HighlightLanguage = {
+ name: 'csharp',
+ ext: 'cs',
+ aliases: ['cs', 'csharp']
+}
+
+export const languageDocker: HighlightLanguage = {
+ name: 'docker',
+ ext: 'docker',
+ aliases: ['docker', 'dockerfile']
+}
+
+export const languageFsharp: HighlightLanguage = {
+ name: 'fsharp',
+ ext: 'fs',
+ aliases: ['fs', 'fsharp']
+}
+
+export const languageJavascript: HighlightLanguage = {
+ name: 'javascript',
+ ext: 'js',
+ aliases: ['javascript', 'js']
+}
+
+export const languageKotlin: HighlightLanguage = {
+ name: 'kotlin',
+ ext: 'kt',
+ aliases: ['kotlin', 'kt']
+}
+
+export const languageMarkdown: HighlightLanguage = {
+ name: 'markdown',
+ ext: 'md',
+ aliases: ['markdown', 'md']
+}
+
+export const languagePython: HighlightLanguage = {
+ name: 'python',
+ ext: 'py',
+ aliases: ['py', 'python']
+}
+
+export const languageRuby: HighlightLanguage = {
+ name: 'ruby',
+ ext: 'rb',
+ aliases: ['rb', 'ruby']
+}
+
+export const languageRust: HighlightLanguage = {
+ name: 'rust',
+ ext: 'rs',
+ aliases: ['rs', 'rust']
+}
+
+export const languageStylus: HighlightLanguage = {
+ name: 'stylus',
+ ext: 'styl',
+ aliases: ['styl', 'stylus']
+}
+
+export const languageTypescript: HighlightLanguage = {
+ name: 'typescript',
+ ext: 'ts',
+ aliases: ['ts', 'typescript']
+}
+
+export const languageYaml: HighlightLanguage = {
+ name: 'yaml',
+ ext: 'yml',
+ aliases: ['yaml', 'yml']
+}
diff --git a/src/components/markdown/codePlugin/resolveLanguage.ts b/src/components/markdown/codePlugin/resolveLanguage.ts
new file mode 100644
index 0000000..8d8d4ae
--- /dev/null
+++ b/src/components/markdown/codePlugin/resolveLanguage.ts
@@ -0,0 +1,46 @@
+import * as languages from './languages.js'
+import type { HighlightLanguage } from './languages.js'
+
+type LanguagesMap = Record
+
+/**
+ * A key-value map to get language info from alias
+ *
+ * - key: alias
+ * - value: language
+ */
+let languagesMap: LanguagesMap
+
+/**
+ * Lazy generate languages map
+ */
+const getLanguagesMap = (): LanguagesMap => {
+ if (!languagesMap) {
+ languagesMap = Object.values(languages).reduce((result, item) => {
+ item.aliases.forEach((alias) => {
+ result[alias] = item
+ })
+ return result
+ }, {} as LanguagesMap)
+ }
+
+ return languagesMap
+}
+
+/**
+ * Resolve language for highlight from token info
+ */
+export const resolveLanguage = (info: string): HighlightLanguage => {
+ // get user-defined language alias
+ const alias = info.match(/^([^ :[{]+)/)?.[1] || ''
+
+ // if the alias does not have a match in the map
+ // fallback to the alias itself
+ return (
+ getLanguagesMap()[alias] ?? {
+ name: alias,
+ ext: alias,
+ aliases: [alias]
+ }
+ )
+}
diff --git a/src/components/markdown/codePlugin/resolveLineNumbers.ts b/src/components/markdown/codePlugin/resolveLineNumbers.ts
new file mode 100644
index 0000000..73cd43a
--- /dev/null
+++ b/src/components/markdown/codePlugin/resolveLineNumbers.ts
@@ -0,0 +1,14 @@
+/**
+ * Resolve the `:line-numbers` / `:no-line-numbers` mark from token info
+ */
+export const resolveLineNumbers = (info: string): boolean | null => {
+ if (/:line-numbers\b/.test(info)) {
+ return true
+ }
+
+ if (/:no-line-numbers\b/.test(info)) {
+ return false
+ }
+
+ return null
+}
diff --git a/src/components/markdown/customLink/index.ts b/src/components/markdown/customLink/index.ts
new file mode 100644
index 0000000..eaa39d6
--- /dev/null
+++ b/src/components/markdown/customLink/index.ts
@@ -0,0 +1,76 @@
+import type { PluginWithOptions } from 'markdown-it'
+
+const defaultMarker = '#'
+interface CustomLinkPluginOptions {
+ marker: string
+}
+export const customLinkPlugin: PluginWithOptions<
+ Partial
+> = (md, options = {}): void => {
+ const marker = options.marker || defaultMarker
+
+ md.block.ruler.before(
+ 'paragraph',
+ 'custom_link',
+ function (state, startLine, endLine, silent) {
+ const pos = state.bMarks[startLine] + state.tShift[startLine]
+ const max = state.eMarks[startLine]
+
+ if (pos >= max) {
+ return false
+ }
+ if (silent) {
+ return true
+ }
+ const text = state.src.substring(pos, max)
+ const start = text.indexOf(marker)
+ const end = text.lastIndexOf(marker)
+ if (start < 0 || end < 0 || start == end) {
+ return false
+ }
+ //#...#规则前面的内容
+ const startContent = text.substring(0, start)
+ //#...#规则后面的内容
+ const endContent = text.substring(end + 1)
+ //#...#规则中间的内容
+ const content = text.substring(start + 1, end)
+
+ //插入
+ const token_div_o = state.push('div_open', 'div', 1)
+ token_div_o.map = [startLine, state.line]
+
+ //插入#...#规则前面的内容
+ const token_s = state.push('inline', '', 0)
+ token_s.content = startContent
+ token_s.children = []
+
+ //插入
+ const token_a_o = state.push('link_open', 'a', 1)
+ token_a_o.attrs = [['class', 'markdown-custom-link']]
+ token_a_o.map = [startLine, state.line]
+
+ //插入#...#规则中间的内容
+ const token = state.push('inline', '', 0)
+ token.content = content
+ token.children = []
+
+ //闭合a标签
+ state.push('link_close', 'a', -1)
+ const token_e = state.push('inline', '', 0)
+
+ //插入#...#规则后面的内容
+ token_e.content = endContent
+ token_e.children = []
+
+ //闭合div标签
+ state.push('div_close', 'div', -1)
+
+ state.line = startLine + 1
+
+ return true
+ },
+ {
+ alt: ['paragraph', 'reference', 'blockquote']
+ }
+ )
+}
diff --git a/src/components/markdown/github-markdown.css b/src/components/markdown/github-markdown.css
new file mode 100644
index 0000000..d6f5b60
--- /dev/null
+++ b/src/components/markdown/github-markdown.css
@@ -0,0 +1,1102 @@
+
+:root.dark .markdown-body {
+ color-scheme: dark;
+ --color-prettylights-syntax-comment: #8b949e;
+ --color-prettylights-syntax-constant: #79c0ff;
+ --color-prettylights-syntax-entity: #d2a8ff;
+ --color-prettylights-syntax-storage-modifier-import: #c9d1d9;
+ --color-prettylights-syntax-entity-tag: #7ee787;
+ --color-prettylights-syntax-keyword: #ff7b72;
+ --color-prettylights-syntax-string: #a5d6ff;
+ --color-prettylights-syntax-variable: #ffa657;
+ --color-prettylights-syntax-brackethighlighter-unmatched: #f85149;
+ --color-prettylights-syntax-invalid-illegal-text: #f0f6fc;
+ --color-prettylights-syntax-invalid-illegal-bg: #8e1519;
+ --color-prettylights-syntax-carriage-return-text: #f0f6fc;
+ --color-prettylights-syntax-carriage-return-bg: #b62324;
+ --color-prettylights-syntax-string-regexp: #7ee787;
+ --color-prettylights-syntax-markup-list: #f2cc60;
+ --color-prettylights-syntax-markup-heading: #1f6feb;
+ --color-prettylights-syntax-markup-italic: #c9d1d9;
+ --color-prettylights-syntax-markup-bold: #c9d1d9;
+ --color-prettylights-syntax-markup-deleted-text: #ffdcd7;
+ --color-prettylights-syntax-markup-deleted-bg: #67060c;
+ --color-prettylights-syntax-markup-inserted-text: #aff5b4;
+ --color-prettylights-syntax-markup-inserted-bg: #033a16;
+ --color-prettylights-syntax-markup-changed-text: #ffdfb6;
+ --color-prettylights-syntax-markup-changed-bg: #5a1e02;
+ --color-prettylights-syntax-markup-ignored-text: #c9d1d9;
+ --color-prettylights-syntax-markup-ignored-bg: #1158c7;
+ --color-prettylights-syntax-meta-diff-range: #d2a8ff;
+ --color-prettylights-syntax-brackethighlighter-angle: #8b949e;
+ --color-prettylights-syntax-sublimelinter-gutter-mark: #484f58;
+ --color-prettylights-syntax-constant-other-reference-link: #a5d6ff;
+ --color-fg-default: #c9d1d9;
+ --color-fg-muted: #8b949e;
+ --color-fg-subtle: #6e7681;
+ --color-canvas-default: #0d1117;
+ --color-canvas-subtle: #161b22;
+ --color-border-default: #30363d;
+ --color-border-muted: #21262d;
+ --color-neutral-muted: rgba(110,118,129,0.4);
+ --color-accent-fg: #58a6ff;
+ --color-accent-emphasis: #1f6feb;
+ --color-attention-subtle: rgba(187,128,9,0.15);
+ --color-danger-fg: #f85149;
+}
+
+
+
+:root .markdown-body {
+ color-scheme: light;
+ --color-prettylights-syntax-comment: #6e7781;
+ --color-prettylights-syntax-constant: #0550ae;
+ --color-prettylights-syntax-entity: #8250df;
+ --color-prettylights-syntax-storage-modifier-import: #24292f;
+ --color-prettylights-syntax-entity-tag: #116329;
+ --color-prettylights-syntax-keyword: #cf222e;
+ --color-prettylights-syntax-string: #0a3069;
+ --color-prettylights-syntax-variable: #953800;
+ --color-prettylights-syntax-brackethighlighter-unmatched: #82071e;
+ --color-prettylights-syntax-invalid-illegal-text: #f6f8fa;
+ --color-prettylights-syntax-invalid-illegal-bg: #82071e;
+ --color-prettylights-syntax-carriage-return-text: #f6f8fa;
+ --color-prettylights-syntax-carriage-return-bg: #cf222e;
+ --color-prettylights-syntax-string-regexp: #116329;
+ --color-prettylights-syntax-markup-list: #3b2300;
+ --color-prettylights-syntax-markup-heading: #0550ae;
+ --color-prettylights-syntax-markup-italic: #24292f;
+ --color-prettylights-syntax-markup-bold: #24292f;
+ --color-prettylights-syntax-markup-deleted-text: #82071e;
+ --color-prettylights-syntax-markup-deleted-bg: #ffebe9;
+ --color-prettylights-syntax-markup-inserted-text: #116329;
+ --color-prettylights-syntax-markup-inserted-bg: #dafbe1;
+ --color-prettylights-syntax-markup-changed-text: #953800;
+ --color-prettylights-syntax-markup-changed-bg: #ffd8b5;
+ --color-prettylights-syntax-markup-ignored-text: #eaeef2;
+ --color-prettylights-syntax-markup-ignored-bg: #0550ae;
+ --color-prettylights-syntax-meta-diff-range: #8250df;
+ --color-prettylights-syntax-brackethighlighter-angle: #57606a;
+ --color-prettylights-syntax-sublimelinter-gutter-mark: #8c959f;
+ --color-prettylights-syntax-constant-other-reference-link: #0a3069;
+ --color-fg-default: #24292f;
+ --color-fg-muted: #57606a;
+ --color-fg-subtle: #6e7781;
+ --color-canvas-default: #ffffff;
+ --color-canvas-subtle: #f6f8fa;
+ --color-border-default: #d0d7de;
+ --color-border-muted: hsla(210,18%,87%,1);
+ --color-neutral-muted: rgba(175,184,193,0.2);
+ --color-accent-fg: #0969da;
+ --color-accent-emphasis: #0969da;
+ --color-attention-subtle: #fff8c5;
+ --color-danger-fg: #cf222e;
+ }
+
+
+.markdown-body {
+ -ms-text-size-adjust: 100%;
+ -webkit-text-size-adjust: 100%;
+ margin: 0;
+ color: var(--color-fg-default);
+ background-color: var(--color-canvas-default);
+ font-family: -apple-system,BlinkMacSystemFont,"Segoe UI","Noto Sans",Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji";
+ font-size: 16px;
+ line-height: 1.5;
+ word-wrap: break-word;
+}
+
+.markdown-body .octicon {
+ display: inline-block;
+ fill: currentColor;
+ vertical-align: text-bottom;
+}
+
+.markdown-body h1:hover .anchor .octicon-link:before,
+.markdown-body h2:hover .anchor .octicon-link:before,
+.markdown-body h3:hover .anchor .octicon-link:before,
+.markdown-body h4:hover .anchor .octicon-link:before,
+.markdown-body h5:hover .anchor .octicon-link:before,
+.markdown-body h6:hover .anchor .octicon-link:before {
+ width: 16px;
+ height: 16px;
+ content: ' ';
+ display: inline-block;
+ background-color: currentColor;
+ -webkit-mask-image: url("data:image/svg+xml,");
+ mask-image: url("data:image/svg+xml,");
+}
+
+.markdown-body details,
+.markdown-body figcaption,
+.markdown-body figure {
+ display: block;
+}
+
+.markdown-body summary {
+ display: list-item;
+}
+
+.markdown-body [hidden] {
+ display: none !important;
+}
+
+.markdown-body a {
+ background-color: transparent;
+ color: var(--color-accent-fg);
+ text-decoration: none;
+}
+
+.markdown-body abbr[title] {
+ border-bottom: none;
+ text-decoration: underline dotted;
+}
+
+.markdown-body b,
+.markdown-body strong {
+ font-weight: var(--base-text-weight-semibold, 600);
+}
+
+.markdown-body dfn {
+ font-style: italic;
+}
+
+.markdown-body h1 {
+ margin: .67em 0;
+ font-weight: var(--base-text-weight-semibold, 600);
+ padding-bottom: .3em;
+ font-size: 2em;
+ border-bottom: 1px solid var(--color-border-muted);
+}
+
+.markdown-body mark {
+ background-color: var(--color-attention-subtle);
+ color: var(--color-fg-default);
+}
+
+.markdown-body small {
+ font-size: 90%;
+}
+
+.markdown-body sub,
+.markdown-body sup {
+ font-size: 75%;
+ line-height: 0;
+ position: relative;
+ vertical-align: baseline;
+}
+
+.markdown-body sub {
+ bottom: -0.25em;
+}
+
+.markdown-body sup {
+ top: -0.5em;
+}
+
+.markdown-body img {
+ border-style: none;
+ max-width: 100%;
+ box-sizing: content-box;
+ background-color: var(--color-canvas-default);
+}
+
+.markdown-body code,
+.markdown-body kbd,
+.markdown-body pre,
+.markdown-body samp {
+ font-family: monospace;
+ font-size: 1em;
+}
+
+.markdown-body figure {
+ margin: 1em 40px;
+}
+
+.markdown-body hr {
+ box-sizing: content-box;
+ overflow: hidden;
+ background: transparent;
+ border-bottom: 1px solid var(--color-border-muted);
+ height: .25em;
+ padding: 0;
+ margin: 24px 0;
+ background-color: var(--color-border-default);
+ border: 0;
+}
+
+.markdown-body input {
+ font: inherit;
+ margin: 0;
+ overflow: visible;
+ font-family: inherit;
+ font-size: inherit;
+ line-height: inherit;
+}
+
+.markdown-body [type=button],
+.markdown-body [type=reset],
+.markdown-body [type=submit] {
+ -webkit-appearance: button;
+}
+
+.markdown-body [type=checkbox],
+.markdown-body [type=radio] {
+ box-sizing: border-box;
+ padding: 0;
+}
+
+.markdown-body [type=number]::-webkit-inner-spin-button,
+.markdown-body [type=number]::-webkit-outer-spin-button {
+ height: auto;
+}
+
+.markdown-body [type=search]::-webkit-search-cancel-button,
+.markdown-body [type=search]::-webkit-search-decoration {
+ -webkit-appearance: none;
+}
+
+.markdown-body ::-webkit-input-placeholder {
+ color: inherit;
+ opacity: .54;
+}
+
+.markdown-body ::-webkit-file-upload-button {
+ -webkit-appearance: button;
+ font: inherit;
+}
+
+.markdown-body a:hover {
+ text-decoration: underline;
+}
+
+.markdown-body ::placeholder {
+ color: var(--color-fg-subtle);
+ opacity: 1;
+}
+
+.markdown-body hr::before {
+ display: table;
+ content: "";
+}
+
+.markdown-body hr::after {
+ display: table;
+ clear: both;
+ content: "";
+}
+
+.markdown-body table {
+ border-spacing: 0;
+ border-collapse: collapse;
+ display: block;
+ width: max-content;
+ max-width: 100%;
+ overflow: auto;
+}
+
+.markdown-body td,
+.markdown-body th {
+ padding: 0;
+}
+
+.markdown-body details summary {
+ cursor: pointer;
+}
+
+.markdown-body details:not([open])>*:not(summary) {
+ display: none !important;
+}
+
+.markdown-body a:focus,
+.markdown-body [role=button]:focus,
+.markdown-body input[type=radio]:focus,
+.markdown-body input[type=checkbox]:focus {
+ outline: 2px solid var(--color-accent-fg);
+ outline-offset: -2px;
+ box-shadow: none;
+}
+
+.markdown-body a:focus:not(:focus-visible),
+.markdown-body [role=button]:focus:not(:focus-visible),
+.markdown-body input[type=radio]:focus:not(:focus-visible),
+.markdown-body input[type=checkbox]:focus:not(:focus-visible) {
+ outline: solid 1px transparent;
+}
+
+.markdown-body a:focus-visible,
+.markdown-body [role=button]:focus-visible,
+.markdown-body input[type=radio]:focus-visible,
+.markdown-body input[type=checkbox]:focus-visible {
+ outline: 2px solid var(--color-accent-fg);
+ outline-offset: -2px;
+ box-shadow: none;
+}
+
+.markdown-body a:not([class]):focus,
+.markdown-body a:not([class]):focus-visible,
+.markdown-body input[type=radio]:focus,
+.markdown-body input[type=radio]:focus-visible,
+.markdown-body input[type=checkbox]:focus,
+.markdown-body input[type=checkbox]:focus-visible {
+ outline-offset: 0;
+}
+
+.markdown-body kbd {
+ display: inline-block;
+ padding: 3px 5px;
+ font: 11px ui-monospace,SFMono-Regular,SF Mono,Menlo,Consolas,Liberation Mono,monospace;
+ line-height: 10px;
+ color: var(--color-fg-default);
+ vertical-align: middle;
+ background-color: var(--color-canvas-subtle);
+ border: solid 1px var(--color-neutral-muted);
+ border-bottom-color: var(--color-neutral-muted);
+ border-radius: 6px;
+ box-shadow: inset 0 -1px 0 var(--color-neutral-muted);
+}
+
+.markdown-body h1,
+.markdown-body h2,
+.markdown-body h3,
+.markdown-body h4,
+.markdown-body h5,
+.markdown-body h6 {
+ margin-top: 24px;
+ margin-bottom: 16px;
+ font-weight: var(--base-text-weight-semibold, 600);
+ line-height: 1.25;
+}
+
+.markdown-body h2 {
+ font-weight: var(--base-text-weight-semibold, 600);
+ padding-bottom: .3em;
+ font-size: 1.5em;
+ border-bottom: 1px solid var(--color-border-muted);
+}
+
+.markdown-body h3 {
+ font-weight: var(--base-text-weight-semibold, 600);
+ font-size: 1.25em;
+}
+
+.markdown-body h4 {
+ font-weight: var(--base-text-weight-semibold, 600);
+ font-size: 1em;
+}
+
+.markdown-body h5 {
+ font-weight: var(--base-text-weight-semibold, 600);
+ font-size: .875em;
+}
+
+.markdown-body h6 {
+ font-weight: var(--base-text-weight-semibold, 600);
+ font-size: .85em;
+ color: var(--color-fg-muted);
+}
+
+.markdown-body p {
+ margin-top: 0;
+ margin-bottom: 10px;
+}
+
+.markdown-body blockquote {
+ margin: 0;
+ padding: 0 1em;
+ color: var(--color-fg-muted);
+ border-left: .25em solid var(--color-border-default);
+}
+
+.markdown-body ul,
+.markdown-body ol {
+ margin-top: 0;
+ margin-bottom: 0;
+ padding-left: 2em;
+}
+
+.markdown-body ol ol,
+.markdown-body ul ol {
+ list-style-type: lower-roman;
+}
+
+.markdown-body ul ul ol,
+.markdown-body ul ol ol,
+.markdown-body ol ul ol,
+.markdown-body ol ol ol {
+ list-style-type: lower-alpha;
+}
+
+.markdown-body dd {
+ margin-left: 0;
+}
+
+.markdown-body tt,
+.markdown-body code,
+.markdown-body samp {
+ font-family: ui-monospace,SFMono-Regular,SF Mono,Menlo,Consolas,Liberation Mono,monospace;
+ font-size: 12px;
+}
+
+.markdown-body pre {
+ margin-top: 0;
+ margin-bottom: 0;
+ font-family: ui-monospace,SFMono-Regular,SF Mono,Menlo,Consolas,Liberation Mono,monospace;
+ font-size: 12px;
+ word-wrap: normal;
+}
+
+.markdown-body .octicon {
+ display: inline-block;
+ overflow: visible !important;
+ vertical-align: text-bottom;
+ fill: currentColor;
+}
+
+.markdown-body input::-webkit-outer-spin-button,
+.markdown-body input::-webkit-inner-spin-button {
+ margin: 0;
+ -webkit-appearance: none;
+ appearance: none;
+}
+
+.markdown-body::before {
+ display: table;
+ content: "";
+}
+
+.markdown-body::after {
+ display: table;
+ clear: both;
+ content: "";
+}
+
+.markdown-body>*:first-child {
+ margin-top: 0 !important;
+}
+
+.markdown-body>*:last-child {
+ margin-bottom: 0 !important;
+}
+
+.markdown-body a:not([href]) {
+ color: inherit;
+ text-decoration: none;
+}
+
+.markdown-body .absent {
+ color: var(--color-danger-fg);
+}
+
+.markdown-body .anchor {
+ float: left;
+ padding-right: 4px;
+ margin-left: -20px;
+ line-height: 1;
+}
+
+.markdown-body .anchor:focus {
+ outline: none;
+}
+
+.markdown-body p,
+.markdown-body blockquote,
+.markdown-body ul,
+.markdown-body ol,
+.markdown-body dl,
+.markdown-body table,
+.markdown-body pre,
+.markdown-body details {
+ margin-top: 0;
+ margin-bottom: 16px;
+}
+
+.markdown-body blockquote>:first-child {
+ margin-top: 0;
+}
+
+.markdown-body blockquote>:last-child {
+ margin-bottom: 0;
+}
+
+.markdown-body h1 .octicon-link,
+.markdown-body h2 .octicon-link,
+.markdown-body h3 .octicon-link,
+.markdown-body h4 .octicon-link,
+.markdown-body h5 .octicon-link,
+.markdown-body h6 .octicon-link {
+ color: var(--color-fg-default);
+ vertical-align: middle;
+ visibility: hidden;
+}
+
+.markdown-body h1:hover .anchor,
+.markdown-body h2:hover .anchor,
+.markdown-body h3:hover .anchor,
+.markdown-body h4:hover .anchor,
+.markdown-body h5:hover .anchor,
+.markdown-body h6:hover .anchor {
+ text-decoration: none;
+}
+
+.markdown-body h1:hover .anchor .octicon-link,
+.markdown-body h2:hover .anchor .octicon-link,
+.markdown-body h3:hover .anchor .octicon-link,
+.markdown-body h4:hover .anchor .octicon-link,
+.markdown-body h5:hover .anchor .octicon-link,
+.markdown-body h6:hover .anchor .octicon-link {
+ visibility: visible;
+}
+
+.markdown-body h1 tt,
+.markdown-body h1 code,
+.markdown-body h2 tt,
+.markdown-body h2 code,
+.markdown-body h3 tt,
+.markdown-body h3 code,
+.markdown-body h4 tt,
+.markdown-body h4 code,
+.markdown-body h5 tt,
+.markdown-body h5 code,
+.markdown-body h6 tt,
+.markdown-body h6 code {
+ padding: 0 .2em;
+ font-size: inherit;
+}
+
+.markdown-body summary h1,
+.markdown-body summary h2,
+.markdown-body summary h3,
+.markdown-body summary h4,
+.markdown-body summary h5,
+.markdown-body summary h6 {
+ display: inline-block;
+}
+
+.markdown-body summary h1 .anchor,
+.markdown-body summary h2 .anchor,
+.markdown-body summary h3 .anchor,
+.markdown-body summary h4 .anchor,
+.markdown-body summary h5 .anchor,
+.markdown-body summary h6 .anchor {
+ margin-left: -40px;
+}
+
+.markdown-body summary h1,
+.markdown-body summary h2 {
+ padding-bottom: 0;
+ border-bottom: 0;
+}
+
+.markdown-body ul.no-list,
+.markdown-body ol.no-list {
+ padding: 0;
+ list-style-type: none;
+}
+
+.markdown-body ol[type=a] {
+ list-style-type: lower-alpha;
+}
+
+.markdown-body ol[type=A] {
+ list-style-type: upper-alpha;
+}
+
+.markdown-body ol[type=i] {
+ list-style-type: lower-roman;
+}
+
+.markdown-body ol[type=I] {
+ list-style-type: upper-roman;
+}
+
+.markdown-body ol[type="1"] {
+ list-style-type: decimal;
+}
+
+.markdown-body div>ol:not([type]) {
+ list-style-type: decimal;
+}
+
+.markdown-body ul ul,
+.markdown-body ul ol,
+.markdown-body ol ol,
+.markdown-body ol ul {
+ margin-top: 0;
+ margin-bottom: 0;
+}
+
+.markdown-body li>p {
+ margin-top: 16px;
+}
+
+.markdown-body li+li {
+ margin-top: .25em;
+}
+
+.markdown-body dl {
+ padding: 0;
+}
+
+.markdown-body dl dt {
+ padding: 0;
+ margin-top: 16px;
+ font-size: 1em;
+ font-style: italic;
+ font-weight: var(--base-text-weight-semibold, 600);
+}
+
+.markdown-body dl dd {
+ padding: 0 16px;
+ margin-bottom: 16px;
+}
+
+.markdown-body table th {
+ font-weight: var(--base-text-weight-semibold, 600);
+}
+
+.markdown-body table th,
+.markdown-body table td {
+ padding: 6px 13px;
+ border: 1px solid var(--color-border-default);
+}
+
+.markdown-body table tr {
+ background-color: var(--color-canvas-default);
+ border-top: 1px solid var(--color-border-muted);
+}
+
+.markdown-body table tr:nth-child(2n) {
+ background-color: var(--color-canvas-subtle);
+}
+
+.markdown-body table img {
+ background-color: transparent;
+}
+
+.markdown-body img[align=right] {
+ padding-left: 20px;
+}
+
+.markdown-body img[align=left] {
+ padding-right: 20px;
+}
+
+.markdown-body .emoji {
+ max-width: none;
+ vertical-align: text-top;
+ background-color: transparent;
+}
+
+.markdown-body span.frame {
+ display: block;
+ overflow: hidden;
+}
+
+.markdown-body span.frame>span {
+ display: block;
+ float: left;
+ width: auto;
+ padding: 7px;
+ margin: 13px 0 0;
+ overflow: hidden;
+ border: 1px solid var(--color-border-default);
+}
+
+.markdown-body span.frame span img {
+ display: block;
+ float: left;
+}
+
+.markdown-body span.frame span span {
+ display: block;
+ padding: 5px 0 0;
+ clear: both;
+ color: var(--color-fg-default);
+}
+
+.markdown-body span.align-center {
+ display: block;
+ overflow: hidden;
+ clear: both;
+}
+
+.markdown-body span.align-center>span {
+ display: block;
+ margin: 13px auto 0;
+ overflow: hidden;
+ text-align: center;
+}
+
+.markdown-body span.align-center span img {
+ margin: 0 auto;
+ text-align: center;
+}
+
+.markdown-body span.align-right {
+ display: block;
+ overflow: hidden;
+ clear: both;
+}
+
+.markdown-body span.align-right>span {
+ display: block;
+ margin: 13px 0 0;
+ overflow: hidden;
+ text-align: right;
+}
+
+.markdown-body span.align-right span img {
+ margin: 0;
+ text-align: right;
+}
+
+.markdown-body span.float-left {
+ display: block;
+ float: left;
+ margin-right: 13px;
+ overflow: hidden;
+}
+
+.markdown-body span.float-left span {
+ margin: 13px 0 0;
+}
+
+.markdown-body span.float-right {
+ display: block;
+ float: right;
+ margin-left: 13px;
+ overflow: hidden;
+}
+
+.markdown-body span.float-right>span {
+ display: block;
+ margin: 13px auto 0;
+ overflow: hidden;
+ text-align: right;
+}
+
+.markdown-body code,
+.markdown-body tt {
+ padding: .2em .4em;
+ margin: 0;
+ font-size: 85%;
+ white-space: break-spaces;
+ background-color: var(--color-neutral-muted);
+ border-radius: 6px;
+}
+
+.markdown-body code br,
+.markdown-body tt br {
+ display: none;
+}
+
+.markdown-body del code {
+ text-decoration: inherit;
+}
+
+.markdown-body samp {
+ font-size: 85%;
+}
+
+.markdown-body pre code {
+ font-size: 100%;
+}
+
+.markdown-body pre>code {
+ padding: 0;
+ margin: 0;
+ word-break: normal;
+ white-space: pre;
+ background: transparent;
+ border: 0;
+}
+
+.markdown-body .highlight {
+ margin-bottom: 16px;
+}
+
+.markdown-body .highlight pre {
+ margin-bottom: 0;
+ word-break: normal;
+}
+
+.markdown-body .highlight pre,
+.markdown-body pre {
+ padding: 16px;
+ overflow: auto;
+ font-size: 85%;
+ line-height: 1.45;
+ background-color: var(--color-canvas-subtle);
+ border-radius: 6px;
+}
+
+.markdown-body pre code,
+.markdown-body pre tt {
+ display: inline;
+ max-width: auto;
+ padding: 0;
+ margin: 0;
+ overflow: visible;
+ line-height: inherit;
+ word-wrap: normal;
+ background-color: transparent;
+ border: 0;
+}
+
+.markdown-body .csv-data td,
+.markdown-body .csv-data th {
+ padding: 5px;
+ overflow: hidden;
+ font-size: 12px;
+ line-height: 1;
+ text-align: left;
+ white-space: nowrap;
+}
+
+.markdown-body .csv-data .blob-num {
+ padding: 10px 8px 9px;
+ text-align: right;
+ background: var(--color-canvas-default);
+ border: 0;
+}
+
+.markdown-body .csv-data tr {
+ border-top: 0;
+}
+
+.markdown-body .csv-data th {
+ font-weight: var(--base-text-weight-semibold, 600);
+ background: var(--color-canvas-subtle);
+ border-top: 0;
+}
+
+.markdown-body [data-footnote-ref]::before {
+ content: "[";
+}
+
+.markdown-body [data-footnote-ref]::after {
+ content: "]";
+}
+
+.markdown-body .footnotes {
+ font-size: 12px;
+ color: var(--color-fg-muted);
+ border-top: 1px solid var(--color-border-default);
+}
+
+.markdown-body .footnotes ol {
+ padding-left: 16px;
+}
+
+.markdown-body .footnotes ol ul {
+ display: inline-block;
+ padding-left: 16px;
+ margin-top: 16px;
+}
+
+.markdown-body .footnotes li {
+ position: relative;
+}
+
+.markdown-body .footnotes li:target::before {
+ position: absolute;
+ top: -8px;
+ right: -8px;
+ bottom: -8px;
+ left: -24px;
+ pointer-events: none;
+ content: "";
+ border: 2px solid var(--color-accent-emphasis);
+ border-radius: 6px;
+}
+
+.markdown-body .footnotes li:target {
+ color: var(--color-fg-default);
+}
+
+.markdown-body .footnotes .data-footnote-backref g-emoji {
+ font-family: monospace;
+}
+
+.markdown-body .pl-c {
+ color: var(--color-prettylights-syntax-comment);
+}
+
+.markdown-body .pl-c1,
+.markdown-body .pl-s .pl-v {
+ color: var(--color-prettylights-syntax-constant);
+}
+
+.markdown-body .pl-e,
+.markdown-body .pl-en {
+ color: var(--color-prettylights-syntax-entity);
+}
+
+.markdown-body .pl-smi,
+.markdown-body .pl-s .pl-s1 {
+ color: var(--color-prettylights-syntax-storage-modifier-import);
+}
+
+.markdown-body .pl-ent {
+ color: var(--color-prettylights-syntax-entity-tag);
+}
+
+.markdown-body .pl-k {
+ color: var(--color-prettylights-syntax-keyword);
+}
+
+.markdown-body .pl-s,
+.markdown-body .pl-pds,
+.markdown-body .pl-s .pl-pse .pl-s1,
+.markdown-body .pl-sr,
+.markdown-body .pl-sr .pl-cce,
+.markdown-body .pl-sr .pl-sre,
+.markdown-body .pl-sr .pl-sra {
+ color: var(--color-prettylights-syntax-string);
+}
+
+.markdown-body .pl-v,
+.markdown-body .pl-smw {
+ color: var(--color-prettylights-syntax-variable);
+}
+
+.markdown-body .pl-bu {
+ color: var(--color-prettylights-syntax-brackethighlighter-unmatched);
+}
+
+.markdown-body .pl-ii {
+ color: var(--color-prettylights-syntax-invalid-illegal-text);
+ background-color: var(--color-prettylights-syntax-invalid-illegal-bg);
+}
+
+.markdown-body .pl-c2 {
+ color: var(--color-prettylights-syntax-carriage-return-text);
+ background-color: var(--color-prettylights-syntax-carriage-return-bg);
+}
+
+.markdown-body .pl-sr .pl-cce {
+ font-weight: bold;
+ color: var(--color-prettylights-syntax-string-regexp);
+}
+
+.markdown-body .pl-ml {
+ color: var(--color-prettylights-syntax-markup-list);
+}
+
+.markdown-body .pl-mh,
+.markdown-body .pl-mh .pl-en,
+.markdown-body .pl-ms {
+ font-weight: bold;
+ color: var(--color-prettylights-syntax-markup-heading);
+}
+
+.markdown-body .pl-mi {
+ font-style: italic;
+ color: var(--color-prettylights-syntax-markup-italic);
+}
+
+.markdown-body .pl-mb {
+ font-weight: bold;
+ color: var(--color-prettylights-syntax-markup-bold);
+}
+
+.markdown-body .pl-md {
+ color: var(--color-prettylights-syntax-markup-deleted-text);
+ background-color: var(--color-prettylights-syntax-markup-deleted-bg);
+}
+
+.markdown-body .pl-mi1 {
+ color: var(--color-prettylights-syntax-markup-inserted-text);
+ background-color: var(--color-prettylights-syntax-markup-inserted-bg);
+}
+
+.markdown-body .pl-mc {
+ color: var(--color-prettylights-syntax-markup-changed-text);
+ background-color: var(--color-prettylights-syntax-markup-changed-bg);
+}
+
+.markdown-body .pl-mi2 {
+ color: var(--color-prettylights-syntax-markup-ignored-text);
+ background-color: var(--color-prettylights-syntax-markup-ignored-bg);
+}
+
+.markdown-body .pl-mdr {
+ font-weight: bold;
+ color: var(--color-prettylights-syntax-meta-diff-range);
+}
+
+.markdown-body .pl-ba {
+ color: var(--color-prettylights-syntax-brackethighlighter-angle);
+}
+
+.markdown-body .pl-sg {
+ color: var(--color-prettylights-syntax-sublimelinter-gutter-mark);
+}
+
+.markdown-body .pl-corl {
+ text-decoration: underline;
+ color: var(--color-prettylights-syntax-constant-other-reference-link);
+}
+
+.markdown-body g-emoji {
+ display: inline-block;
+ min-width: 1ch;
+ font-family: "Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol";
+ font-size: 1em;
+ font-style: normal !important;
+ font-weight: var(--base-text-weight-normal, 400);
+ line-height: 1;
+ vertical-align: -0.075em;
+}
+
+.markdown-body g-emoji img {
+ width: 1em;
+ height: 1em;
+}
+
+.markdown-body .task-list-item {
+ list-style-type: none;
+}
+
+.markdown-body .task-list-item label {
+ font-weight: var(--base-text-weight-normal, 400);
+}
+
+.markdown-body .task-list-item.enabled label {
+ cursor: pointer;
+}
+
+.markdown-body .task-list-item+.task-list-item {
+ margin-top: 4px;
+}
+
+.markdown-body .task-list-item .handle {
+ display: none;
+}
+
+.markdown-body .task-list-item-checkbox {
+ margin: 0 .2em .25em -1.4em;
+ vertical-align: middle;
+}
+
+.markdown-body .contains-task-list:dir(rtl) .task-list-item-checkbox {
+ margin: 0 -1.6em .25em .2em;
+}
+
+.markdown-body .contains-task-list {
+ position: relative;
+}
+
+.markdown-body .contains-task-list:hover .task-list-item-convert-container,
+.markdown-body .contains-task-list:focus-within .task-list-item-convert-container {
+ display: block;
+ width: auto;
+ height: 24px;
+ overflow: visible;
+ clip: auto;
+}
+
+.markdown-body ::-webkit-calendar-picker-indicator {
+ filter: invert(50%);
+}
diff --git a/src/components/markdown/index.vue b/src/components/markdown/index.vue
new file mode 100644
index 0000000..2b1bdab
--- /dev/null
+++ b/src/components/markdown/index.vue
@@ -0,0 +1,168 @@
+
+
+
+
+
+
+
diff --git a/src/components/markdown/markdown.scss b/src/components/markdown/markdown.scss
new file mode 100644
index 0000000..3da4336
--- /dev/null
+++ b/src/components/markdown/markdown.scss
@@ -0,0 +1,212 @@
+$line-height: 1.375;
+:root {
+ --code-bg-color: #35373f;
+ --code-hl-bg-color: rgba(0, 0, 0, 0.66);
+ --code-ln-color: #6e6e7f;
+ --code-ln-wrapper-width: 3.5rem;
+}
+@keyframes blink {
+ from,
+ to {
+ opacity: 0;
+ }
+ 50% {
+ opacity: 1;
+ }
+}
+.markdown-it-container {
+ .markdown-body {
+ background-color: transparent;
+ font-size: 15px;
+ .markdown-custom-link {
+ color: var(--el-color-primary);
+ cursor: pointer;
+ }
+ }
+ position: relative;
+ .markdown-typing {
+ position: absolute;
+ display: inline-block;
+ content: '';
+ width: 5px;
+ height: 14px;
+ transform: translate(4px, 2px) scaleY(1.3);
+ color: #1a202c;
+ background-color: currentColor;
+ animation: blink 0.6s infinite;
+ }
+ ul {
+ list-style: disc;
+ }
+ ol {
+ list-style: decimal;
+ }
+ code {
+ padding: 0.25rem;
+ margin: 0;
+ font-size: 0.85em;
+ border-radius: 3px;
+ }
+ code[class*='language-'],
+ pre[class*='language-'] {
+ color: #ccc;
+ background: none;
+ font-size: 1em;
+ text-align: left;
+ white-space: pre;
+ word-spacing: normal;
+ word-break: normal;
+ word-wrap: normal;
+ line-height: 1.5;
+
+ -moz-tab-size: 4;
+ -o-tab-size: 4;
+ tab-size: 4;
+
+ -webkit-hyphens: none;
+ -moz-hyphens: none;
+ -ms-hyphens: none;
+ hyphens: none;
+ }
+
+ /* Code blocks */
+ pre[class*='language-'] {
+ padding: 20px 0;
+ margin: 0;
+ overflow: auto;
+ }
+
+ :not(pre) > code[class*='language-'],
+ pre[class*='language-'] {
+ font-size: 0.85em;
+ background: #35373f;
+ }
+
+ /* Inline code */
+ :not(pre) > code[class*='language-'] {
+ border-radius: 0.3em;
+ white-space: normal;
+ }
+
+ pre,
+ pre[class*='language-'] {
+ line-height: $line-height;
+ border-radius: 6px;
+ overflow: visible;
+ display: inline-block;
+ padding: 20px;
+ code {
+ color: #fff;
+ padding: 0;
+ background-color: transparent !important;
+ border-radius: 0;
+ overflow-wrap: unset;
+ -webkit-font-smoothing: auto;
+ -moz-osx-font-smoothing: auto;
+ }
+ }
+
+ div[class*='language-'] {
+ position: relative;
+ background-color: var(--code-bg-color);
+ border-radius: 6px;
+ padding-top: 32px;
+ margin: 0.85rem 0;
+ overflow: hidden;
+ .code-copy-line {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ background-color: #595b63;
+ color: #e0e0e0;
+ height: 32px;
+ line-height: 32px;
+ font-size: 12px;
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+ padding: 0 12px;
+
+ &::before {
+ content: attr(data-ext);
+ position: absolute;
+ z-index: 3;
+ top: 0;
+ font-size: 0.75rem;
+ color: #e0e0e0;
+ }
+ .code-copy-btn {
+ position: absolute;
+ z-index: 3;
+ top: 0;
+ right: 1rem;
+ font-size: 0.75rem;
+ color: #e0e0e0;
+ cursor: pointer;
+ }
+ }
+
+ pre,
+ pre[class*='language-'] {
+ // force override the background color to be compatible with shiki
+ background: transparent !important;
+ position: relative;
+ z-index: 1;
+ }
+ .pre-code-scroll {
+ overflow: auto;
+ }
+
+ &:not(.line-numbers-mode) {
+ .line-numbers {
+ display: none;
+ }
+ }
+
+ &.line-numbers-mode {
+ padding-left: var(--code-ln-wrapper-width);
+ pre {
+ vertical-align: middle;
+ padding: 20px 20px 20px 0;
+ }
+
+ .line-numbers {
+ left: 0;
+ position: absolute;
+ top: 0;
+ width: var(--code-ln-wrapper-width);
+ text-align: center;
+ color: var(--code-ln-color);
+ padding-top: 52px;
+ line-height: $line-height;
+ counter-reset: line-number;
+
+ .line-number {
+ position: relative;
+ z-index: 3;
+ user-select: none;
+ height: #{$line-height - 0.2}em;
+ &::before {
+ display: block;
+ counter-increment: line-number;
+ content: counter(line-number);
+ font-size: 0.8em;
+ height: 100%;
+ }
+ }
+ }
+
+ // &::after {
+ // content: "";
+ // position: absolute;
+ // top: 0;
+ // left: 0;
+ // width: var(--code-ln-wrapper-width);
+ // height: 100%;
+ // border-radius: 6px 0 0 6px;
+ // border-right: 1px solid var(--code-hl-bg-color);
+ // }
+ }
+ }
+}