diff --git a/src/components/codeHighlight.ts b/src/components/codeHighlight.ts new file mode 100644 index 0000000..35dd723 --- /dev/null +++ b/src/components/codeHighlight.ts @@ -0,0 +1,237 @@ +/** + * 代码高亮组件 + * + * 功能说明: + * - 使用 highlight.js 提供专业的代码语法高亮 + * - 支持多种编程语言(Verilog, JavaScript, Python 等) + * - 提供行内代码和代码块的不同样式 + * - 自动检测语言类型 + */ + +/** + * 获取 highlight.js 的 CDN 链接 + */ +export function getHighlightJsLinks(): string { + return ` + + + + + + + `; +} + +/** + * 获取代码高亮的样式 + */ +export function getCodeHighlightStyles(): string { + return ` + /* 代码块基础样式 */ + .segment-text pre { + background: var(--vscode-textCodeBlock-background); + border: 1px solid var(--vscode-panel-border); + border-radius: 6px; + padding: 12px; + overflow-x: auto; + margin: 12px 0; + position: relative; + white-space: pre; + } + + .segment-text pre code { + background: transparent !important; + padding: 0; + border: none; + display: block; + line-height: 1.5; + white-space: pre; + font-family: 'Courier New', Consolas, 'Monaco', monospace; + font-size: 0.9em; + } + + /* 行内代码样式 */ + .segment-text code:not(pre code) { + background: var(--vscode-textCodeBlock-background); + padding: 2px 6px; + border-radius: 3px; + color: var(--vscode-textPreformat-foreground); + border: 1px solid var(--vscode-panel-border); + font-family: 'Courier New', Consolas, 'Monaco', monospace; + font-size: 0.9em; + } + + /* 覆盖 highlight.js 的背景色,使用 VSCode 主题色 */ + .segment-text pre code.hljs { + background: transparent !important; + padding: 0 !important; + } + + /* 代码块语言标签 */ + .code-block-wrapper { + position: relative; + margin: 12px 0; + } + + .code-language-label { + position: absolute; + top: 8px; + right: 8px; + background: var(--vscode-badge-background); + color: var(--vscode-badge-foreground); + padding: 2px 8px; + border-radius: 3px; + font-size: 11px; + font-weight: 500; + text-transform: uppercase; + opacity: 0.8; + z-index: 1; + } + + /* 代码块复制按钮 */ + .code-copy-btn { + position: absolute; + top: 8px; + right: 8px; + background: var(--vscode-button-secondaryBackground); + color: var(--vscode-button-secondaryForeground); + border: 1px solid var(--vscode-button-border); + border-radius: 4px; + padding: 4px 8px; + font-size: 11px; + cursor: pointer; + opacity: 0; + transition: opacity 0.2s ease; + z-index: 2; + } + + .code-block-wrapper:hover .code-copy-btn { + opacity: 1; + } + + .code-copy-btn:hover { + background: var(--vscode-button-secondaryHoverBackground); + } + + .code-copy-btn.copied { + background: var(--vscode-button-background); + color: var(--vscode-button-foreground); + } + + /* 代码块滚动条样式 */ + .segment-text pre::-webkit-scrollbar { + height: 8px; + } + + .segment-text pre::-webkit-scrollbar-track { + background: var(--vscode-scrollbarSlider-background); + border-radius: 4px; + } + + .segment-text pre::-webkit-scrollbar-thumb { + background: var(--vscode-scrollbarSlider-hoverBackground); + border-radius: 4px; + } + + .segment-text pre::-webkit-scrollbar-thumb:hover { + background: var(--vscode-scrollbarSlider-activeBackground); + } + `; +} + +/** + * 获取代码高亮的脚本 + */ +export function getCodeHighlightScript(): string { + return ` + /** + * 使用 highlight.js 进行代码高亮 + */ + function highlightCodeBlocks() { + // 等待 highlight.js 加载完成 + if (typeof hljs === 'undefined') { + setTimeout(highlightCodeBlocks, 100); + return; + } + + const codeBlocks = document.querySelectorAll('.segment-text pre code:not(.hljs)'); + codeBlocks.forEach((block) => { + hljs.highlightElement(block); + }); + } + + /** + * 为代码块添加复制按钮 + */ + function enhanceCodeBlocks() { + const codeBlocks = document.querySelectorAll('.segment-text pre code'); + + codeBlocks.forEach((codeElement) => { + const preElement = codeElement.parentElement; + if (!preElement || preElement.classList.contains('enhanced')) { + return; + } + + // 标记为已增强,避免重复处理 + preElement.classList.add('enhanced'); + + // 应用语法高亮 + if (typeof hljs !== 'undefined' && !codeElement.classList.contains('hljs')) { + hljs.highlightElement(codeElement); + } + + // 创建包装器 + const wrapper = document.createElement('div'); + wrapper.className = 'code-block-wrapper'; + preElement.parentNode.insertBefore(wrapper, preElement); + wrapper.appendChild(preElement); + + // 添加复制按钮 + const copyBtn = document.createElement('button'); + copyBtn.className = 'code-copy-btn'; + copyBtn.textContent = '复制'; + copyBtn.onclick = function() { + const code = codeElement.textContent; + navigator.clipboard.writeText(code).then(() => { + copyBtn.textContent = '已复制'; + copyBtn.classList.add('copied'); + setTimeout(() => { + copyBtn.textContent = '复制'; + copyBtn.classList.remove('copied'); + }, 2000); + }); + }; + wrapper.appendChild(copyBtn); + }); + } + + /** + * 监听 DOM 变化,自动增强新添加的代码块 + */ + function observeCodeBlocks() { + const observer = new MutationObserver((mutations) => { + mutations.forEach((mutation) => { + if (mutation.addedNodes.length > 0) { + enhanceCodeBlocks(); + } + }); + }); + + observer.observe(document.getElementById('messages'), { + childList: true, + subtree: true + }); + } + + // 初始化代码块增强 + if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', () => { + enhanceCodeBlocks(); + observeCodeBlocks(); + }); + } else { + enhanceCodeBlocks(); + observeCodeBlocks(); + } + `; +} diff --git a/src/views/agentCard.ts b/src/views/agentCard.ts index fd19d7c..2dbd82e 100644 --- a/src/views/agentCard.ts +++ b/src/views/agentCard.ts @@ -172,15 +172,8 @@ export function getAgentCardScript(): string { const icon = step.status === 'completed' ? '✅' : step.status === 'error' ? '❌' : '🔄'; const displayName = getAgentToolDisplayName(step.toolName); const result = step.toolResult ? \`: \${step.toolResult.substring(0, 50)}\${step.toolResult.length > 50 ? '...' : ''}\` : ''; - // 为技术性工具调用添加低调样式(用户看不懂的) - const lowProfileTools = [ - 'knowledge_save', 'knowledge_load', - 'queryKnowledgeSummary', 'queryRules', 'querySignals', - 'setModule', 'addSignal', 'addSignalExample', - 'validateKnowledgeGraph', 'addPlan', 'addEdge', - 'showPlan', 'spawnExplorer' - ]; - const stepClass = lowProfileTools.includes(step.toolName) ? 'agent-step low-profile' : 'agent-step'; + // 所有工具调用都使用低调样式 + const stepClass = 'agent-step low-profile'; return \`
' + escapedCode + '');
+ return placeholder;
+ });
+
+ // 提取行内代码(避免被转义)
+ const inlineCodes = [];
+ html = html.replace(/\`([^\`]+)\`/g, function(match, code) {
+ const escapedCode = code
+ .replace(/&/g, '&')
+ .replace(//g, '>');
+ const placeholder = \`___INLINE_CODE_\${inlineCodes.length}___\`;
+ inlineCodes.push('' + escapedCode + '');
+ return placeholder;
+ });
+
+ // 转义其他 HTML 特殊字符
+ html = html
.replace(/&/g, '&')
.replace(//g, '>');
- // 处理代码块(三个反引号包裹的代码)
- html = html.replace(/\`\`\`(\\w+)?\\n([\\s\\S]*?)\`\`\`/g, function(match, lang, code) {
- const language = lang || 'plaintext';
- return '' + code.trim() + '';
- });
-
- // 处理行内代码(单个反引号包裹)
- html = html.replace(/\`([^\`]+)\`/g, '$1');
-
// 处理标题 ### Title
html = html.replace(/^### (.+)$/gm, '