实现代码高亮
This commit is contained in:
parent
c026b17fdc
commit
15c8b6d7a0
@ -6,7 +6,7 @@
|
|||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||||
<link rel="icon" href="<%= BASE_URL %>favicon.svg">
|
<link rel="icon" href="<%= BASE_URL %>favicon.svg">
|
||||||
<link rel="stylesheet" href="default-light.css">
|
<link rel="stylesheet" href="default-dark.css">
|
||||||
<link rel="stylesheet" href="vscode.css">
|
<link rel="stylesheet" href="vscode.css">
|
||||||
<link rel="stylesheet" href="mcp.css">
|
<link rel="stylesheet" href="mcp.css">
|
||||||
<link rel="stylesheet" href="iconfont.css">
|
<link rel="stylesheet" href="iconfont.css">
|
||||||
|
@ -10,8 +10,10 @@
|
|||||||
--sidebar-item-margin: 5px;
|
--sidebar-item-margin: 5px;
|
||||||
--toolbar-height: 50px;
|
--toolbar-height: 50px;
|
||||||
--toolbar-item-height: 32px;
|
--toolbar-item-height: 32px;
|
||||||
|
--font-size-xsp: 11px;
|
||||||
|
--md-zoom: 1.12;
|
||||||
|
|
||||||
--code-font-family: "Cascadia code";
|
--code-font-family: Consolas, Monaco, "Cascadia code", 'Andale Mono', 'Ubuntu Mono', monospace;
|
||||||
|
|
||||||
/* css 动画属性 */
|
/* css 动画属性 */
|
||||||
--animation-7s: .7s cubic-bezier(0.23, 1, 0.32, 1);
|
--animation-7s: .7s cubic-bezier(0.23, 1, 0.32, 1);
|
||||||
@ -37,46 +39,6 @@ body::-webkit-scrollbar {
|
|||||||
width: 0;
|
width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
* hr {
|
|
||||||
border: none;
|
|
||||||
background-color: var(--main-color);
|
|
||||||
height: 1.5px;
|
|
||||||
width: 95%;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
::-webkit-scrollbar {
|
|
||||||
width: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
::-webkit-scrollbar-track {
|
|
||||||
background: var(--sidebar);
|
|
||||||
}
|
|
||||||
|
|
||||||
::-webkit-scrollbar-thumb {
|
|
||||||
background: var(--scrollbar-background);
|
|
||||||
border-radius: .3em;
|
|
||||||
}
|
|
||||||
|
|
||||||
::-webkit-scrollbar-thumb:hover {
|
|
||||||
background: var(--scrollbar-hover);
|
|
||||||
border-radius: .3em;
|
|
||||||
}
|
|
||||||
|
|
||||||
::-webkit-scrollbar-thumb:active {
|
|
||||||
background: var(--scrollbar-active);
|
|
||||||
border-radius: .3em;
|
|
||||||
}
|
|
||||||
|
|
||||||
::-webkit-scrollbar-button {
|
|
||||||
height: 0;
|
|
||||||
width: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
::-webkit-scrollbar-corner {
|
|
||||||
background: none;
|
|
||||||
display: none;
|
|
||||||
} */
|
|
||||||
|
|
||||||
.el-select__wrapper {
|
.el-select__wrapper {
|
||||||
min-width: 100px;
|
min-width: 100px;
|
||||||
@ -134,12 +96,80 @@ a {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.el-textarea__inner::-webkit-scrollbar-thumb {
|
.el-textarea__inner::-webkit-scrollbar-thumb {
|
||||||
background: #cb81da;
|
background: var(--main-color);
|
||||||
/* 使用您的主题色 */
|
/* 使用您的主题色 */
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-textarea__inner::-webkit-scrollbar-thumb:hover {
|
.el-textarea__inner::-webkit-scrollbar-thumb:hover {
|
||||||
|
background: var(--main-color);
|
||||||
|
/* 深紫色悬停状态 */
|
||||||
|
}
|
||||||
|
|
||||||
|
* hr {
|
||||||
|
border: none;
|
||||||
|
width: 95%;
|
||||||
|
background-color: var(--background);
|
||||||
|
height: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.openmcp-code-block {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
margin-bottom: 1em;
|
||||||
|
border: 1px solid var(--main-color);
|
||||||
|
border-radius: 4px;
|
||||||
|
overflow: hidden;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.openmcp-code-block pre code::-webkit-scrollbar {
|
||||||
|
width: 8px;
|
||||||
|
height: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.openmcp-code-block pre code::-webkit-scrollbar-track {
|
||||||
|
background: transparent;
|
||||||
|
/* 浅紫色背景 */
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.openmcp-code-block pre code::-webkit-scrollbar-thumb {
|
||||||
|
background: #cb81da;
|
||||||
|
/* 使用您的主题色 */
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.openmcp-code-block pre code::-webkit-scrollbar-thumb:hover {
|
||||||
background: #a259b5;
|
background: #a259b5;
|
||||||
/* 深紫色悬停状态 */
|
/* 深紫色悬停状态 */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.code-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
color: var(--sidebar);
|
||||||
|
background-color: var(--main-color);
|
||||||
|
padding: 5px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lang-id {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.copy-button {
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 0.2em 0.5em;
|
||||||
|
border: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
background-color: transparent;
|
||||||
|
color: var(--sidebar);
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.copy-button:hover {
|
||||||
|
background-color: var(--main-color);
|
||||||
|
color: #fff;
|
||||||
|
}
|
@ -48,8 +48,6 @@ onMounted(() => {
|
|||||||
connectionResult.logString = msg;
|
connectionResult.logString = msg;
|
||||||
}, { once: true });
|
}, { once: true });
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
// 初始化 设置
|
// 初始化 设置
|
||||||
loadSetting();
|
loadSetting();
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
<div class="message-content" v-else-if="message.role === 'assistant'">
|
<div class="message-content" v-else-if="message.role === 'assistant'">
|
||||||
<div class="message-role">Agent</div>
|
<div class="message-role">Agent</div>
|
||||||
<div class="message-text">
|
<div class="message-text">
|
||||||
<div v-html="markdownToHtml(message.content)" ref="markdownContent"></div>
|
<div v-html="markdownToHtml(message.content)"></div>
|
||||||
<span class="iconfont icon-copy" @click="handleCopy(message.content)"></span>
|
<span class="iconfont icon-copy" @click="handleCopy(message.content)"></span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -36,13 +36,17 @@
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- 正在加载的部分实时解析 markdown -->
|
||||||
<div v-if="isLoading" class="message-item assistant">
|
<div v-if="isLoading" class="message-item assistant">
|
||||||
<div class="message-avatar">
|
<div class="message-avatar">
|
||||||
<span class="iconfont icon-chat"></span>
|
<span class="iconfont icon-chat"></span>
|
||||||
</div>
|
</div>
|
||||||
<div class="message-content">
|
<div class="message-content">
|
||||||
<div class="message-role">Agent</div>
|
<div class="message-role">Agent</div>
|
||||||
<div class="message-text">{{ streamingContent }}<span class="typing-cursor">|</span></div>
|
<div class="message-text">
|
||||||
|
<span v-html="markdownToHtml(streamingContent)"></span>
|
||||||
|
<span class="typing-cursor">|</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -193,7 +197,6 @@ const handleSend = () => {
|
|||||||
}
|
}
|
||||||
if (streamingContent.value) {
|
if (streamingContent.value) {
|
||||||
// 加入消息列表
|
// 加入消息列表
|
||||||
console.log(tabStorage);
|
|
||||||
|
|
||||||
tabStorage.messages.push({
|
tabStorage.messages.push({
|
||||||
role: 'assistant',
|
role: 'assistant',
|
||||||
@ -381,9 +384,5 @@ onUnmounted(() => {
|
|||||||
margin-top: 0.2em;
|
margin-top: 0.2em;
|
||||||
margin-bottom: 0.2em;
|
margin-bottom: 0.2em;
|
||||||
}
|
}
|
||||||
</style>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
/* 引入 KaTeX 样式 */
|
|
||||||
@import 'katex/dist/katex.min.css';
|
|
||||||
</style>
|
</style>
|
@ -0,0 +1,57 @@
|
|||||||
|
import Prism from "./prism";
|
||||||
|
|
||||||
|
// 定义 escapeHtml 函数
|
||||||
|
function escapeHtml(unsafe: string) {
|
||||||
|
return unsafe
|
||||||
|
.replace(/&/g, "&")
|
||||||
|
.replace(/</g, "<")
|
||||||
|
.replace(/>/g, ">")
|
||||||
|
.replace(/"/g, """)
|
||||||
|
.replace(/'/g, "'");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 导出默认的 highlight 函数
|
||||||
|
export default function highlight(str: string, lang: string) {
|
||||||
|
// 创建代码块容器
|
||||||
|
let container = `<div class="openmcp-code-block">`;
|
||||||
|
|
||||||
|
// 添加复制按钮(右上角)
|
||||||
|
container += `
|
||||||
|
<div class="code-header">
|
||||||
|
<div class="code-language">${lang || ''}</div>
|
||||||
|
<button class="copy-button" onclick="copyCode(this)">复制</button>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
if (lang && Prism.languages[lang]) {
|
||||||
|
// 使用 Prism 高亮代码
|
||||||
|
const highlightedCode = Prism.highlight(str, Prism.languages[lang], lang);
|
||||||
|
// 添加代码区域
|
||||||
|
container += `<pre class="language-${lang}"><code class="language-${lang}">${highlightedCode}</code></pre>`;
|
||||||
|
} else {
|
||||||
|
// 普通代码块
|
||||||
|
container += `<pre class="language-none"><code>${escapeHtml(str)}</code></pre>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
container += `</div>`;
|
||||||
|
return container;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 全局复制函数
|
||||||
|
(window as any).copyCode = function (button: HTMLElement) {
|
||||||
|
const codeBlock = button.closest('.openmcp-code-block');
|
||||||
|
if (!codeBlock) return;
|
||||||
|
const codeElement = codeBlock.querySelector('code');
|
||||||
|
const code = codeElement?.textContent || '';
|
||||||
|
|
||||||
|
navigator.clipboard.writeText(code).then(() => {
|
||||||
|
const originalText = button.textContent;
|
||||||
|
button.textContent = '已复制';
|
||||||
|
setTimeout(() => {
|
||||||
|
button.textContent = originalText;
|
||||||
|
}, 500);
|
||||||
|
}).catch((error) => {
|
||||||
|
console.error('复制失败:', error);
|
||||||
|
button.textContent = '复制失败';
|
||||||
|
});
|
||||||
|
};
|
@ -1 +0,0 @@
|
|||||||
declare module 'markdown-it-katex';
|
|
76
renderer/src/components/main-panel/chat/markdown-katex.ts
Normal file
76
renderer/src/components/main-panel/chat/markdown-katex.ts
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
import katex from 'katex';
|
||||||
|
import type MarkdownIt from "markdown-it";
|
||||||
|
|
||||||
|
|
||||||
|
// 定义 delimiter 对象的类型
|
||||||
|
type Delimiter = {
|
||||||
|
left: string;
|
||||||
|
right: string;
|
||||||
|
display: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 定义 defaultOptions 对象的类型
|
||||||
|
type DefaultOptions = {
|
||||||
|
delimiters: Delimiter[];
|
||||||
|
};
|
||||||
|
|
||||||
|
// 定义 defaultOptions 常量
|
||||||
|
const defaultOptions: DefaultOptions = {
|
||||||
|
delimiters: [
|
||||||
|
{ left: '\\[', right: '\\]', display: true },
|
||||||
|
{ left: '\\(', right: '\\)', display: false },
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
// 定义 escapedBracketRule 函数
|
||||||
|
function escapedBracketRule(options: DefaultOptions): any {
|
||||||
|
return (state: any, silent: boolean): boolean => {
|
||||||
|
const max: number = state.posMax;
|
||||||
|
const start: number = state.pos;
|
||||||
|
|
||||||
|
for (const { left, right, display } of options.delimiters) {
|
||||||
|
// 检查是否以左标记开始
|
||||||
|
if (!state.src.slice(start).startsWith(left)) continue;
|
||||||
|
|
||||||
|
// 跳过左标记的长度
|
||||||
|
let pos: number = start + left.length;
|
||||||
|
|
||||||
|
// 寻找匹配的右标记
|
||||||
|
while (pos < max) {
|
||||||
|
if (state.src.slice(pos).startsWith(right)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
pos++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 没找到匹配的右标记,跳过,进入下个匹配
|
||||||
|
if (pos >= max) continue;
|
||||||
|
|
||||||
|
// 如果不是静默模式,将 LaTeX 公式转换为 MathML
|
||||||
|
if (!silent) {
|
||||||
|
const content: string = state.src.slice(start + left.length, pos);
|
||||||
|
try {
|
||||||
|
const renderedContent: string = katex.renderToString(content, {
|
||||||
|
throwOnError: false,
|
||||||
|
output: 'mathml',
|
||||||
|
displayMode: display,
|
||||||
|
});
|
||||||
|
const token = state.push('html_inline', '', 0);
|
||||||
|
token.content = renderedContent;
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新位置,跳过右标记的长度
|
||||||
|
state.pos = pos + right.length;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// 定义主函数
|
||||||
|
export default function (md: MarkdownIt, options: DefaultOptions = defaultOptions): void {
|
||||||
|
md.inline.ruler.after('text', 'escaped_bracket', escapedBracketRule(options));
|
||||||
|
}
|
@ -1,13 +1,17 @@
|
|||||||
import MarkdownIt from 'markdown-it';
|
import MarkdownIt from 'markdown-it';
|
||||||
import katex from 'katex';
|
import MarkdownKatex from './markdown-katex';
|
||||||
import markdownItKatex from 'markdown-it-katex';
|
import MarkdownHighlight from './markdown-highlight';
|
||||||
import { pinkLog } from '@/views/setting/util';
|
|
||||||
|
|
||||||
const md = new MarkdownIt();
|
const md = new MarkdownIt({
|
||||||
|
highlight: MarkdownHighlight,
|
||||||
|
});
|
||||||
|
|
||||||
md.use(markdownItKatex, {
|
md.use(MarkdownKatex, {
|
||||||
throwOnError: false,
|
delimiters: [
|
||||||
errorColor: '#cc0000'
|
{ left: '\\[', right: '\\]', display: true },
|
||||||
|
{ left: '\\(', right: '\\)', display: false },
|
||||||
|
{ left: '$$', right: '$$', display: false },
|
||||||
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
export const markdownToHtml = (markdown: string) => {
|
export const markdownToHtml = (markdown: string) => {
|
||||||
|
6
renderer/src/components/main-panel/chat/prism/index.d.ts
vendored
Normal file
6
renderer/src/components/main-panel/chat/prism/index.d.ts
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
const Prism: {
|
||||||
|
highlight: (code: string, language: any, lang: string) => string;
|
||||||
|
languages: Record<string, any>;
|
||||||
|
// 添加其他需要的类型定义
|
||||||
|
};
|
||||||
|
export default Prism;
|
41
renderer/src/components/main-panel/chat/prism/index.js
Normal file
41
renderer/src/components/main-panel/chat/prism/index.js
Normal file
File diff suppressed because one or more lines are too long
432
renderer/src/components/main-panel/chat/prism/prism.css
Normal file
432
renderer/src/components/main-panel/chat/prism/prism.css
Normal file
@ -0,0 +1,432 @@
|
|||||||
|
.vscode-dark code[class*=language-],
|
||||||
|
.vscode-dark pre[class*=language-] {
|
||||||
|
color: #abb2bf;
|
||||||
|
font-family: var(--code-font-family);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vscode-dark pre[class*=language-] {
|
||||||
|
position: relative;
|
||||||
|
margin: .5em 0;
|
||||||
|
overflow: visible;
|
||||||
|
padding: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vscode-dark pre[class*=language-]>code {
|
||||||
|
position: relative;
|
||||||
|
background-size: 3em 3em;
|
||||||
|
background-origin: content-box;
|
||||||
|
background-attachment: local;
|
||||||
|
padding: 0 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vscode-dark code[class*=language-] {
|
||||||
|
max-height: inherit;
|
||||||
|
height: inherit;
|
||||||
|
display: block;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vscode-dark :not(pre)>code[class*=language-],
|
||||||
|
.vscode-dark pre[class*=language-] {
|
||||||
|
-webkit-box-sizing: border-box;
|
||||||
|
-moz-box-sizing: border-box;
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin-bottom: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vscode-dark :not(pre)>code[class*=language-] {
|
||||||
|
position: relative;
|
||||||
|
padding: .2em;
|
||||||
|
border-radius: .3em;
|
||||||
|
color: #c92c2c;
|
||||||
|
border: 1px solid rgba(0, 0, 0, .1);
|
||||||
|
display: inline;
|
||||||
|
white-space: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vscode-dark pre[class*=language-]:after,
|
||||||
|
.vscode-dark pre[class*=language-]:before {
|
||||||
|
content: '';
|
||||||
|
display: block;
|
||||||
|
position: absolute;
|
||||||
|
bottom: .75em;
|
||||||
|
left: .18em;
|
||||||
|
-webkit-transform: rotate(-2deg);
|
||||||
|
-moz-transform: rotate(-2deg);
|
||||||
|
-ms-transform: rotate(-2deg);
|
||||||
|
-o-transform: rotate(-2deg);
|
||||||
|
transform: rotate(-2deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.vscode-dark pre[class*=language-]:after {
|
||||||
|
right: .75em;
|
||||||
|
left: auto;
|
||||||
|
-webkit-transform: rotate(2deg);
|
||||||
|
-moz-transform: rotate(2deg);
|
||||||
|
-ms-transform: rotate(2deg);
|
||||||
|
-o-transform: rotate(2deg);
|
||||||
|
transform: rotate(2deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.vscode-dark .token.block-comment,
|
||||||
|
.vscode-dark .token.cdata,
|
||||||
|
.vscode-dark .token.comment,
|
||||||
|
.vscode-dark .token.doctype,
|
||||||
|
.vscode-dark .token.prolog {
|
||||||
|
color: #7F848E;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vscode-dark .token.punctuation {
|
||||||
|
color: #D19A66;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vscode-dark .token.boolean,
|
||||||
|
.vscode-dark .token.constant,
|
||||||
|
.vscode-dark .token.deleted,
|
||||||
|
.vscode-dark .token.function-name,
|
||||||
|
.vscode-dark .token.property,
|
||||||
|
.vscode-dark .token.symbol,
|
||||||
|
.vscode-dark .token.tag {
|
||||||
|
color: #d55f69;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vscode-dark .token.number {
|
||||||
|
color: #d19a66;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vscode-dark .token.attr-name,
|
||||||
|
.vscode-dark .token.builtin,
|
||||||
|
.vscode-dark .token.char,
|
||||||
|
.vscode-dark .token.inserted,
|
||||||
|
.vscode-dark .token.string {
|
||||||
|
color: #4da736;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vscode-dark .token.selector {
|
||||||
|
color: #af7137;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vscode-dark .token.function {
|
||||||
|
color: #61afef;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vscode-dark .token.entity,
|
||||||
|
.vscode-dark .token.operator,
|
||||||
|
.vscode-dark .token.url {
|
||||||
|
color: #56B6C2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vscode-dark .token.variable {
|
||||||
|
color: #E06C75;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vscode-dark .token.atrule,
|
||||||
|
.vscode-dark .token.attr-value {
|
||||||
|
color: #61AFEF;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vscode-dark .token.class-name {
|
||||||
|
color: #9e32d4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vscode-dark .token.keyword {
|
||||||
|
color: #c586c0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vscode-dark .token.important,
|
||||||
|
.vscode-dark .token.regex {
|
||||||
|
color: #e90;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vscode-dark .language-css .token.string,
|
||||||
|
.vscode-dark .style .token.string {
|
||||||
|
color: #a67f59;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vscode-dark .token.important {
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vscode-dark .token.bold {
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vscode-dark .token.italic {
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vscode-dark .token.entity {
|
||||||
|
cursor: help;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vscode-dark .token.namespace {
|
||||||
|
color: #9e32d4;
|
||||||
|
opacity: .7;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width:767px) {
|
||||||
|
.vscode-dark pre[class*=language-]:after,
|
||||||
|
.vscode-dark pre[class*=language-]:before {
|
||||||
|
bottom: 14px;
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.vscode-dark pre[class*=language-].line-numbers.line-numbers {
|
||||||
|
padding-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vscode-dark pre[class*=language-].line-numbers.line-numbers code {
|
||||||
|
padding-left: 3.8em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vscode-dark pre[class*=language-].line-numbers.line-numbers .line-numbers-rows {
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vscode-dark pre[class*=language-][data-line] {
|
||||||
|
padding-top: 0;
|
||||||
|
padding-bottom: 0;
|
||||||
|
padding-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vscode-dark pre[data-line] code {
|
||||||
|
position: relative;
|
||||||
|
padding-left: 4em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vscode-dark pre .line-highlight {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.vscode-light code[class*=language-],
|
||||||
|
.vscode-light pre[class*=language-] {
|
||||||
|
color: #383A42;
|
||||||
|
font-family: var(--code-font-family);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vscode-light pre[class*=language-] {
|
||||||
|
position: relative;
|
||||||
|
margin: .5em 0;
|
||||||
|
overflow: visible;
|
||||||
|
padding: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vscode-light pre[class*=language-]>code {
|
||||||
|
position: relative;
|
||||||
|
background-origin: content-box;
|
||||||
|
background-attachment: local;
|
||||||
|
padding: 0 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vscode-light code[class*=language-] {
|
||||||
|
max-height: inherit;
|
||||||
|
height: inherit;
|
||||||
|
display: block;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vscode-light :not(pre)>code[class*=language-],
|
||||||
|
.vscode-light pre[class*=language-] {
|
||||||
|
-webkit-box-sizing: border-box;
|
||||||
|
-moz-box-sizing: border-box;
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin-bottom: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vscode-light :not(pre)>code[class*=language-] {
|
||||||
|
position: relative;
|
||||||
|
padding: .2em;
|
||||||
|
border-radius: .3em;
|
||||||
|
color: #c92c2c;
|
||||||
|
border: 1px solid rgba(0, 0, 0, .1);
|
||||||
|
display: inline;
|
||||||
|
white-space: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vscode-light pre[class*=language-]:after,
|
||||||
|
.vscode-light pre[class*=language-]:before {
|
||||||
|
content: '';
|
||||||
|
display: block;
|
||||||
|
position: absolute;
|
||||||
|
bottom: .75em;
|
||||||
|
left: .18em;
|
||||||
|
width: 40%;
|
||||||
|
height: 20%;
|
||||||
|
max-height: 13em;
|
||||||
|
-webkit-transform: rotate(-2deg);
|
||||||
|
-moz-transform: rotate(-2deg);
|
||||||
|
-ms-transform: rotate(-2deg);
|
||||||
|
-o-transform: rotate(-2deg);
|
||||||
|
transform: rotate(-2deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.vscode-light pre[class*=language-]:after {
|
||||||
|
right: .75em;
|
||||||
|
left: auto;
|
||||||
|
-webkit-transform: rotate(2deg);
|
||||||
|
-moz-transform: rotate(2deg);
|
||||||
|
-ms-transform: rotate(2deg);
|
||||||
|
-o-transform: rotate(2deg);
|
||||||
|
transform: rotate(2deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.vscode-light .token.block-comment,
|
||||||
|
.vscode-light .token.cdata,
|
||||||
|
.vscode-light .token.comment,
|
||||||
|
.vscode-light .token.doctype,
|
||||||
|
.vscode-light .token.prolog {
|
||||||
|
color: #7F848E;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vscode-light .token.punctuation {
|
||||||
|
color: #D19A66;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vscode-light .token.boolean,
|
||||||
|
.vscode-light .token.constant,
|
||||||
|
.vscode-light .token.deleted,
|
||||||
|
.vscode-light .token.function-name,
|
||||||
|
.vscode-light .token.property,
|
||||||
|
.vscode-light .token.symbol,
|
||||||
|
.vscode-light .token.tag {
|
||||||
|
color: #d55f69;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vscode-light .token.number {
|
||||||
|
color: #be8550;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vscode-light .token.attr-name,
|
||||||
|
.vscode-light .token.builtin,
|
||||||
|
.vscode-light .token.char,
|
||||||
|
.vscode-light .token.inserted,
|
||||||
|
.vscode-light .token.string {
|
||||||
|
color: #4da736;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vscode-light .token.selector {
|
||||||
|
color: #af7137;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vscode-light .token.function {
|
||||||
|
color: #4284f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vscode-light .token.entity,
|
||||||
|
.vscode-light .token.operator,
|
||||||
|
.vscode-light .token.url {
|
||||||
|
color: #56B6C2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vscode-light .token.variable {
|
||||||
|
color: #E06C75;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vscode-light .token.atrule,
|
||||||
|
.vscode-light .token.attr-value {
|
||||||
|
color: #61AFEF;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vscode-light .token.class-name {
|
||||||
|
color: #9e32d4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vscode-light .token.keyword {
|
||||||
|
color: #c37429;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vscode-light .token.important,
|
||||||
|
.vscode-light .token.regex {
|
||||||
|
color: #e90;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vscode-light .language-css .token.string,
|
||||||
|
.vscode-light .style .token.string {
|
||||||
|
color: #a67f59;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vscode-light .token.important {
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vscode-light .token.bold {
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vscode-light .token.italic {
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vscode-light .token.entity {
|
||||||
|
cursor: help;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vscode-light .token.namespace {
|
||||||
|
color: #9e32d4;
|
||||||
|
opacity: .7;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width:767px) {
|
||||||
|
.vscode-light pre[class*=language-]:after,
|
||||||
|
.vscode-light pre[class*=language-]:before {
|
||||||
|
bottom: 14px;
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.vscode-light pre[class*=language-].line-numbers.line-numbers {
|
||||||
|
padding-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vscode-light pre[class*=language-].line-numbers.line-numbers code {
|
||||||
|
padding-left: 3.8em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vscode-light pre[class*=language-].line-numbers.line-numbers .line-numbers-rows {
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vscode-light pre[class*=language-][data-line] {
|
||||||
|
padding-top: 0;
|
||||||
|
padding-bottom: 0;
|
||||||
|
padding-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vscode-light pre[data-line] code {
|
||||||
|
position: relative;
|
||||||
|
padding-left: 4em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vscode-light pre .line-highlight {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
@ -50,6 +50,34 @@ export function setDefaultCss() {
|
|||||||
} else {
|
} else {
|
||||||
setExtraDarkColorCss();
|
setExtraDarkColorCss();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const acquireVsCodeApi = (window as any)['acquireVsCodeApi']
|
||||||
|
const mode = acquireVsCodeApi === undefined ? 'debug' : 'release';
|
||||||
|
if (mode === 'debug') {
|
||||||
|
// 判断颜色深浅,模拟 .vscode-dark 的加入
|
||||||
|
const theme = getThemeColor();
|
||||||
|
const app = document.getElementById('app');
|
||||||
|
|
||||||
|
app?.classList.add('vscode-' + theme);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let themeColor: 'light' | 'dark' | undefined = undefined;
|
||||||
|
|
||||||
|
export function getThemeColor(): 'light' | 'dark' {
|
||||||
|
if (themeColor) {
|
||||||
|
return themeColor;
|
||||||
|
}
|
||||||
|
const rootStyles = getComputedStyle(document.documentElement);
|
||||||
|
const backgroundColorString = rootStyles.getPropertyValue('--background');
|
||||||
|
const backgroundColor = parseColor(backgroundColorString);
|
||||||
|
if (backgroundColor) {
|
||||||
|
const isLight = isLightColorTheme(backgroundColor.r, backgroundColor.g, backgroundColor.b);
|
||||||
|
themeColor = isLight ? 'light' : 'dark';
|
||||||
|
return themeColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'dark';
|
||||||
}
|
}
|
||||||
|
|
||||||
function setExtraLightColorCss() {
|
function setExtraLightColorCss() {
|
||||||
|
@ -2,8 +2,10 @@ import { createApp } from "vue";
|
|||||||
import App from "./App.vue";
|
import App from "./App.vue";
|
||||||
import router from "./router";
|
import router from "./router";
|
||||||
import i18n from './i18n';
|
import i18n from './i18n';
|
||||||
import ElementPlus from 'element-plus'
|
import ElementPlus from 'element-plus';
|
||||||
import 'element-plus/dist/index.css'
|
import 'element-plus/dist/index.css';
|
||||||
|
import "@/components/main-panel/chat/prism/prism.css";
|
||||||
|
import 'katex/dist/katex.min.css';
|
||||||
|
|
||||||
createApp(App)
|
createApp(App)
|
||||||
.use(i18n)
|
.use(i18n)
|
||||||
|
@ -61,40 +61,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "交互测试",
|
|
||||||
"icon": "icon-robot",
|
|
||||||
"type": "blank",
|
|
||||||
"componentIndex": 3,
|
|
||||||
"storage": {
|
|
||||||
"messages": [
|
|
||||||
{
|
|
||||||
"role": "user",
|
|
||||||
"content": "如何在 typescript 项目中使用 markdown-it"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"role": "assistant",
|
|
||||||
"content": "# 在 TypeScript 项目中使用 markdown-it\n\nmarkdown-it 是一个流行的 Markdown 解析器,可以很方便地在 TypeScript 项目中使用。以下是完整的配置和使用指南:\n\n## 1. 安装依赖\n\n首先安装 markdown-it 及其 TypeScript 类型定义:\n\n```bash\nnpm install markdown-it\nnpm install --save-dev @types/markdown-it\n```\n\n## 2. 基本使用\n\n创建一个 TypeScript 文件(如 `markdown.ts`)并添加以下代码:\n\n```typescript\nimport MarkdownIt from 'markdown-it';\n\n// 创建 markdown-it 实例\nconst md = new MarkdownIt();\n\n// 渲染 Markdown 字符串\nconst result = md.render('# Hello markdown-it in TypeScript!');\nconsole.log(result);\n```\n\n## 3. 配置选项\n\n你可以自定义 markdown-it 的配置:\n\n```typescript\nconst md = new MarkdownIt({\n html: true, // 启用 HTML 标签\n linkify: true, // 自动将 URL 转换为链接\n typographer: true, // 启用排版美化\n breaks: true // 将换行符转换为 <br>\n});\n```\n\n## 4. 添加插件\n\nmarkdown-it 有许多插件可以扩展功能。例如添加 markdown-it-anchor:\n\n```bash\nnpm install markdown-it-anchor\nnpm install --save-dev @types/markdown-it-anchor\n```\n\n然后在代码中使用:\n\n```typescript\nimport MarkdownIt from 'markdown-it';\nimport anchor from 'markdown-it-anchor';\n\nconst md = new MarkdownIt().use(anchor, {\n permalink: true,\n permalinkBefore: true\n});\n\nconst result = md.render('# Section Title\\n\\nSome content');\nconsole.log(result);\n```\n\n## 5. 自定义渲染规则\n\n你可以覆盖默认的渲染规则:\n\n```typescript\nconst md = new MarkdownIt();\n\n// 覆盖默认的链接渲染\nmd.renderer.rules.link_open = (tokens, idx, options, env, self) => {\n const token = tokens[idx];\n token.attrSet('target', '_blank');\n token.attrSet('rel', 'noopener noreferrer');\n return self.renderToken(tokens, idx, options);\n};\n\nconst result = md.render('[Open in new tab](https://example.com)');\n```\n\n## 6. 在 React/Vue 等框架中使用\n\n### 在 React 中使用:\n\n```typescript\nimport React from 'react';\nimport MarkdownIt from 'markdown-it';\n\nconst md = new MarkdownIt();\n\nconst MarkdownRenderer: React.FC<{ content: string }> = ({ content }) => {\n return <div dangerouslySetInnerHTML={{ __html: md.render(content) }} />;\n};\n\n// 使用\n<MarkdownRenderer content=\"# Hello React with markdown-it\" />\n```\n\n### 在 Vue 中使用:\n\n```typescript\nimport { defineComponent } from 'vue';\nimport MarkdownIt from 'markdown-it';\n\nconst md = new MarkdownIt();\n\nexport default defineComponent({\n props: ['content'],\n setup(props) {\n return () => (\n <div domPropsInnerHTML={md.render(props.content)} />\n );\n }\n});\n```\n\n## 7. 处理安全性\n\n如果需要处理用户输入的 Markdown,建议使用 markdown-it-sanitizer 或其他安全插件:\n\n```bash\nnpm install markdown-it-sanitizer\n```\n\n```typescript\nimport MarkdownIt from 'markdown-it';\nimport sanitizer from 'markdown-it-sanitizer';\n\nconst md = new MarkdownIt().use(sanitizer);\n```\n\n## 8. 完整示例\n\n下面是一个完整的 TypeScript 类示例:\n\n```typescript\nimport MarkdownIt from 'markdown-it';\nimport anchor from 'markdown-it-anchor';\nimport sanitizer from 'markdown-it-sanitizer';\n\nclass MarkdownRenderer {\n private md: MarkdownIt;\n\n constructor() {\n this.md = new MarkdownIt({\n html: true,\n linkify: true,\n typographer: true\n })\n .use(anchor)\n .use(sanitizer);\n }\n\n public render(markdown: string): string {\n return this.md.render(markdown);\n }\n\n public renderInline(markdown: string): string {\n return this.md.renderInline(markdown);\n }\n}\n\n// 使用示例\nconst renderer = new MarkdownRenderer();\nconst html = renderer.render('# Title\\n\\nSome *markdown* content');\nconsole.log(html);\n```\n\n通过以上步骤,你就可以在 TypeScript 项目中轻松使用 markdown-it 来解析和渲染 Markdown 内容了。"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"role": "assistant",
|
|
||||||
"content": "康托洛维奇不等式(Kantorovich Inequality)的数学公式为:\n\n对于正定矩阵 \\( A \\) 和任意非零向量 \\( x \\),有\n\n\\[\n\\frac{(x^\\top A x)(x^\\top A^{-1} x)}{(x^\\top x)^2} \\leq \\frac{(\\lambda_{\\max} + \\lambda_{\\min})^2}{4 \\lambda_{\\max} \\lambda_{\\min}},\n\\]\n\n其中 \\( \\lambda_{\\max} \\) 和 \\( \\lambda_{\\min} \\) 分别是矩阵 \\( A \\) 的最大和最小特征值。"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"role": "assistant",
|
|
||||||
"content": "调和平均数(Harmonic Mean)的数学公式为:\n\n对于正数 \\( x_1, x_2, \\dots, x_n \\),调和平均数 \\( H \\) 定义为:\n\n\\[\nH = \\frac{n}{\\frac{1}{x_1} + \\frac{1}{x_2} + \\cdots + \\frac{1}{x_n}}.\n\\]"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"settings": {
|
|
||||||
"modelIndex": 0,
|
|
||||||
"enableTools": true,
|
|
||||||
"enableWebSearch": false,
|
|
||||||
"temperature": 0.7,
|
|
||||||
"contextLength": 10,
|
|
||||||
"systemPrompt": ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "交互测试",
|
"name": "交互测试",
|
||||||
"icon": "icon-robot",
|
"icon": "icon-robot",
|
||||||
@ -128,6 +94,40 @@
|
|||||||
"systemPrompt": ""
|
"systemPrompt": ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "交互测试",
|
||||||
|
"icon": "icon-robot",
|
||||||
|
"type": "blank",
|
||||||
|
"componentIndex": 3,
|
||||||
|
"storage": {
|
||||||
|
"messages": [
|
||||||
|
{
|
||||||
|
"role": "user",
|
||||||
|
"content": "如何让 mardown-it 支持渲染出来的编程语言自带语法高亮"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"role": "assistant",
|
||||||
|
"content": "要让 `markdown-it` 渲染的代码块自带语法高亮,你可以通过以下步骤实现:\n\n---\n\n### 1. **安装必要的依赖**\n首先确保已安装 `markdown-it` 和语法高亮库(如 `highlight.js` 或 `prismjs`):\n```bash\nnpm install markdown-it highlight.js\n# 或\nnpm install markdown-it prismjs\n```\n\n---\n\n### 2. **配置 markdown-it 启用代码高亮**\n#### 方案一:使用 `highlight.js`(推荐)\n```javascript\nconst md = require('markdown-it')({\n highlight: function (str, lang) {\n if (lang && require('highlight.js').getLanguage(lang)) {\n try {\n return require('highlight.js').highlight(str, { language: lang }).value;\n } catch (__) {}\n }\n return ''; // 使用默认转义\n }\n});\n```\n\n#### 方案二:使用 `prismjs`\n```javascript\nconst md = require('markdown-it')({\n highlight: function (str, lang) {\n if (lang && require('prismjs').languages[lang]) {\n try {\n return require('prismjs').highlight(str, require('prismjs').languages[lang], lang);\n } catch (__) {}\n }\n return ''; // 使用默认转义\n }\n});\n```\n\n---\n\n### 3. **引入高亮样式**\n语法高亮需要对应的 CSS 样式文件,可以通过以下方式引入:\n#### 对于 `highlight.js`:\n- 从 [highlight.js 主题库](https://highlightjs.org/static/demo/) 选择样式,然后在 HTML 中引入:\n ```html\n <link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/styles/atom-one-dark.min.css\">\n ```\n\n#### 对于 `prismjs`:\n- 从 [Prism 主题库](https://prismjs.com/) 下载样式,或在 HTML 中引入:\n ```html\n <link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/themes/prism-tomorrow.min.css\">\n ```\n\n---\n\n### 4. **渲染 Markdown 内容**\n```javascript\nconst result = md.render('```js\\nconsole.log(\"Hello\");\\n```');\nconsole.log(result);\n```\n输出会包含语法高亮的 HTML(如 `<span class=\"hljs-keyword\">console</span>`)。\n\n---\n\n### 5. **可选:自定义高亮逻辑**\n如果需要更复杂的处理(如行号、错误提示),可以修改 `highlight` 函数:\n```javascript\nhighlight: function (str, lang) {\n if (lang === 'mermaid') {\n return `<div class=\"mermaid\">${str}</div>`; // 特殊处理 Mermaid 图表\n }\n // ...默认高亮逻辑\n}\n```\n\n---\n\n### 6. **在浏览器中使用**\n如果在前端使用,确保通过 CDN 或打包工具引入库:\n```html\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/markdown-it/13.0.2/markdown-it.min.js\"></script>\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/highlight.min.js\"></script>\n<script>\n const md = window.markdownit({\n highlight: (str, lang) => {\n if (lang && hljs.getLanguage(lang)) {\n return hljs.highlight(str, { language: lang }).value;\n }\n return '';\n }\n });\n</script>\n```\n\n---\n\n### 常见问题\n- **语言未高亮?** 检查是否安装了对应的语言包(如 `highlight.js` 可能需要额外加载语言模块)。\n- **样式不生效?** 确认 CSS 文件路径正确且未被其他样式覆盖。\n\n通过以上步骤,`markdown-it` 渲染的代码块将自动支持语法高亮。"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"role": "user",
|
||||||
|
"content": "markdown-it 如何让生成的"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"role": "assistant",
|
||||||
|
"content": "要让 `markdown-it` 生成的代码块支持语法高亮,你需要结合语法高亮库(如 `highlight.js` 或 `Prism.js`)进行配置。以下是详细步骤:\n\n---\n\n## **1. 安装依赖**\n首先,确保安装了 `markdown-it` 和语法高亮库(如 `highlight.js` 或 `Prism.js`):\n\n### **使用 `highlight.js`**\n```bash\nnpm install markdown-it highlight.js\n```\n### **使用 `Prism.js`**\n```bash\nnpm install markdown-it prismjs\n```\n\n---\n\n## **2. 配置 `markdown-it` 启用语法高亮**\n### **方案 1:使用 `highlight.js`(推荐)**\n```javascript\nconst md = require(\"markdown-it\")({\n highlight: function (str, lang) {\n if (lang && require(\"highlight.js\").getLanguage(lang)) {\n try {\n return require(\"highlight.js\").highlight(str, { language: lang }).value;\n } catch (err) {\n console.error(\"语法高亮失败:\", err);\n }\n }\n return \"\"; // 使用默认的转义\n },\n});\n```\n### **方案 2:使用 `Prism.js`**\n```javascript\nconst md = require(\"markdown-it\")({\n highlight: function (str, lang) {\n if (lang && require(\"prismjs\").languages[lang]) {\n try {\n return require(\"prismjs\").highlight(str, require(\"prismjs\").languages[lang], lang);\n } catch (err) {\n console.error(\"语法高亮失败:\", err);\n }\n }\n return \"\"; // 使用默认的转义\n },\n});\n```\n\n---\n\n## **3. 引入高亮样式**\n语法高亮需要对应的 CSS 样式文件,你可以:\n### **`highlight.js` 样式**\n```html\n<!-- 选择一个主题,如 atom-one-dark -->\n<link\n rel=\"stylesheet\"\n href=\"https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/styles/atom-one-dark.min.css\"\n/>\n```\n### **`Prism.js` 样式**\n```html\n<!-- 选择一个主题,如 prism-tomorrow -->\n<link\n rel=\"stylesheet\"\n href=\"https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/themes/prism-tomorrow.min.css\"\n/>\n```\n\n---\n\n## **4. 渲染 Markdown**\n```javascript\nconst markdownText = `\n\\`\\`\\`javascript\nconsole.log(\"Hello, World!\");\n\\`\\`\\`\n`;\n\nconst htmlOutput = md.render(markdownText);\nconsole.log(htmlOutput);\n```\n输出:\n```html\n<pre><code class=\"language-javascript\"><span class=\"hljs-built_in\">console</span>.<span class=\"hljs-title function_\">log</span>(<span class=\"hljs-string\">\"Hello, World!\"</span>);</code></pre>\n```\n\n---\n\n## **5. 在浏览器中使用**\n如果你在前端使用 `markdown-it`,可以这样加载:\n```html\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/markdown-it/13.0.2/markdown-it.min.js\"></script>\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/highlight.min.js\"></script>\n<script>\n const md = window.markdownit({\n highlight: function (str, lang) {\n if (lang && hljs.getLanguage(lang)) {\n try {\n return hljs.highlight(str, { language: lang }).value;\n } catch (err) {\n console.error(\"语法高亮失败:\", err);\n }\n }\n return \"\";\n },\n });\n const html = md.render(\"```js\\nconsole.log('Hello');\\n```\");\n document.body.innerHTML = html;\n hljs.highlightAll(); // 确保动态加载的代码块也能高亮\n</script>\n```\n\n---\n\n## **6. 高级用法**\n### **(1)自定义高亮**\n```javascript\nhighlight: function (str, lang) {\n if (lang === \"mermaid\") {\n return `<div class=\"mermaid\">${str}</div>`; // 特殊处理 Mermaid 图表\n }\n // 其他语言的高亮逻辑\n // ...\n}\n```\n### **(2)行号支持**\n使用 `highlight.js` + `highlightjs-line-numbers.js`:\n```html\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/highlightjs-line-numbers.js/2.8.0/highlightjs-line-numbers.min.js\"></script>\n<script>\n hljs.highlightAll();\n hljs.initLineNumbersOnLoad();\n</script>\n```\n\n---\n\n## **7. 常见问题**\n### **(1)某些语言不高亮?**\n- `highlight.js` 默认只支持部分语言,其他语言需要额外引入:\n ```html\n <script src=\"https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/languages/go.min.js\"></script>\n ```\n- `Prism.js` 需要手动加载语言:\n ```javascript\n require(\"prismjs/components/prism-go\");\n ```\n\n### **(2)样式不生效?**\n- 确保 CSS 文件正确加载。\n- 检查是否有其他样式覆盖。\n\n---\n\n## **总结**\n| 步骤 | 操作 |\n|------|------|\n| 1. 安装依赖 | `npm install markdown-it highlight.js` |\n| 2. 配置高亮 | 在 `markdown-it` 的 `highlight` 回调中使用 `highlight.js` 或 `Prism.js` |\n| 3. 引入样式 | 加载对应的 CSS 文件 |\n| 4. 渲染 Markdown | `md.render(\"```js\\nconsole.log('Hi');\\n```\")` |\n| 5. 浏览器支持 | 确保 `hljs.highlightAll()` 执行 |\n\n这样,`markdown-it` 生成的代码块就会自动带有语法高亮! 🎨"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"settings": {
|
||||||
|
"modelIndex": 0,
|
||||||
|
"enableTools": true,
|
||||||
|
"enableWebSearch": false,
|
||||||
|
"temperature": 0.7,
|
||||||
|
"contextLength": 10,
|
||||||
|
"systemPrompt": ""
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user