| | |
| | | import highlight from 'highlight.js'; |
| | | // import highlight from 'highlight.js'; |
| | | import hljs from 'highlight.js'; |
| | | import Markdown from 'markdown-it'; |
| | | // 导入 highlight.js 的样式,这里使用 github 主题 |
| | | import 'highlight.js/styles/github.css'; |
| | | |
| | | const mdOptions: Markdown.Options = { |
| | | const md = new Markdown({ |
| | | linkify: true, |
| | | typographer: true, |
| | | breaks: true, |
| | | langPrefix: 'language-', |
| | | // 代码高亮 |
| | | highlight(str, lang) { |
| | | if (lang && highlight.getLanguage(lang)) { |
| | | highlight: (str: string, lang: string) => { |
| | | if (lang && hljs.getLanguage(lang)) { |
| | | try { |
| | | return '<pre class="hljs"><code>' + highlight.highlight(lang, str, true).value + '</code></pre>'; |
| | | } catch (__) { |
| | | } |
| | | return hljs.highlight(str, { language: lang }).value; |
| | | } catch { |
| | | return str; |
| | | } |
| | | } |
| | | return ''; |
| | | return str; |
| | | }, |
| | | }); |
| | | |
| | | // 自定义代码块渲染 |
| | | md.renderer.rules.fence = (tokens, idx, options, env, slf) => { |
| | | const token = tokens[idx]; |
| | | const lang = token.info || ''; |
| | | const highlighted = options.highlight?.(token.content, lang, '') || token.content; |
| | | return ` |
| | | <div class="code-markdown-it-block"> |
| | | <div class="code-markdown-it-header"> |
| | | <div class="code-markdown-it-header-left"> |
| | | <span class="code-markdown-it-lang">${lang}</span> |
| | | </div> |
| | | <div class="code-markdown-it-header-right"> |
| | | <button title="复制" class="code-markdown-it-copy" onclick="this.classList.add('copied'); setTimeout(() => this.classList.remove('copied'), 1500); navigator.clipboard.writeText(this.parentElement.parentElement.nextElementSibling.textContent)"> |
| | | <svg class="copy-icon" viewBox="0 0 16 16" width="16" height="16"> |
| | | <path d="M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 0 1 0 1.5h-1.5a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-1.5a.75.75 0 0 1 1.5 0v1.5A1.75 1.75 0 0 1 9.25 16h-7.5A1.75 1.75 0 0 1 0 14.25Z"></path> |
| | | <path d="M5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0 1 14.25 11h-7.5A1.75 1.75 0 0 1 5 9.25Zm1.75-.25a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-7.5a.25.25 0 0 0-.25-.25Z"></path> |
| | | </svg> |
| | | <svg class="check-icon" viewBox="0 0 16 16" width="16" height="16"> |
| | | <path d="M13.78 4.22a.75.75 0 0 1 0 1.06l-7.25 7.25a.75.75 0 0 1-1.06 0L2.22 9.28a.75.75 0 0 1 1.06-1.06L6 10.94l6.72-6.72a.75.75 0 0 1 1.06 0Z"></path> |
| | | </svg> |
| | | </button> |
| | | <button title="展开" class="code-markdown-it-toggle" onclick="const block = this.parentElement.parentElement.parentElement; block.classList.toggle('collapsed'); this.title = block.classList.contains('collapsed') ? '展开' : '折叠'"> |
| | | <svg class="expand-icon" viewBox="0 0 24 24" width="16" height="16"> |
| | | <path d="M7.41 8.59L12 13.17l4.59-4.58L18 10l-6 6-6-6 1.41-1.41z"></path> |
| | | </svg> |
| | | </button> |
| | | </div> |
| | | </div> |
| | | <pre class="code-markdown-it-content"><code class="${options.langPrefix}${lang}">${highlighted}</code></pre> |
| | | </div> |
| | | `.trim(); |
| | | }; |
| | | |
| | | export const md = new Markdown(mdOptions); |
| | | // 添加自定义样式 |
| | | const style = document.createElement('style'); |
| | | // 自定义段落渲染规则 |
| | | md.renderer.rules.paragraph_open = (tokens, idx) => { |
| | | return '<p class="inline-block">'; // 添加 inline-block 类来控制显示 |
| | | }; |
| | | |
| | | // 自定义表格渲染规则 |
| | | md.renderer.rules.table_open = (tokens, idx) => { |
| | | return '<table class="markdown-table">'; |
| | | }; |
| | | |
| | | style.textContent = ` |
| | | .code-markdown-it-block { |
| | | margin: 1em 0; |
| | | border-radius: 6px; |
| | | overflow: hidden; |
| | | border: 1px solid #e1e4e8; |
| | | } |
| | | .code-markdown-it-header { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | padding: 8px 16px; |
| | | background: #f6f8fa; |
| | | border-bottom: 1px solid #e1e4e8; |
| | | } |
| | | .code-markdown-it-header-left { |
| | | display: flex; |
| | | align-items: center; |
| | | } |
| | | .code-markdown-it-header-right { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 8px; |
| | | } |
| | | .code-markdown-it-lang { |
| | | font-size: 14px; |
| | | font-weight: 600; |
| | | color: #24292e; |
| | | // text-transform: uppercase; |
| | | } |
| | | .code-markdown-it-copy { |
| | | display: inline-flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | background: none; |
| | | border: none; |
| | | padding: 4px; |
| | | cursor: pointer; |
| | | color: #586069; |
| | | border-radius: 4px; |
| | | } |
| | | .code-markdown-it-copy:hover { |
| | | background: #f3f4f6; |
| | | color: #24292e; |
| | | } |
| | | .code-markdown-it-copy .check-icon { |
| | | display: none; |
| | | } |
| | | .code-markdown-it-copy.copied .copy-icon { |
| | | display: none; |
| | | } |
| | | .code-markdown-it-copy.copied .check-icon { |
| | | display: block; |
| | | color: #22863a; |
| | | } |
| | | .code-markdown-it-toggle { |
| | | background: none; |
| | | border: none; |
| | | padding: 4px; |
| | | cursor: pointer; |
| | | color: #586069; |
| | | display: flex; |
| | | align-items: center; |
| | | } |
| | | .code-markdown-it-toggle:hover { |
| | | color: #24292e; |
| | | } |
| | | .code-markdown-it-content { |
| | | margin: 0; |
| | | padding: 16px; |
| | | overflow-x: auto; |
| | | } |
| | | .code-markdown-it-block.collapsed .code-markdown-it-content { |
| | | display: none; |
| | | } |
| | | .code-markdown-it-block.collapsed .expand-icon { |
| | | transform: rotate(-90deg); |
| | | } |
| | | .expand-icon { |
| | | transition: transform 0.2s; |
| | | fill: currentColor; |
| | | } |
| | | .copy-icon, .check-icon { |
| | | fill: currentColor; |
| | | } |
| | | .markdown-table { |
| | | border-collapse: collapse; |
| | | width: 100%; |
| | | } |
| | | .markdown-table thead { |
| | | background-color: #f2f2f2; |
| | | } |
| | | .markdown-table th, |
| | | .markdown-table td { |
| | | border: 1px solid #ebebeb; |
| | | padding: 8px; |
| | | text-align: center; |
| | | } |
| | | `; |
| | | document.head.appendChild(style); |
| | | export { md }; |