diff --git a/src/ui-styles.ts b/src/ui-styles.ts index 96d0134..bfb3336 100644 --- a/src/ui-styles.ts +++ b/src/ui-styles.ts @@ -856,6 +856,59 @@ const styles = ` word-break: break-word; } + /* Code block container and header */ + .code-block-container { + margin: 8px 0; + border: 1px solid var(--vscode-panel-border); + border-radius: 4px; + background-color: var(--vscode-textCodeBlock-background); + overflow: hidden; + } + + .code-block-header { + display: flex; + justify-content: space-between; + align-items: center; + padding: 4px 6px; + background-color: var(--vscode-editor-background); + border-bottom: 1px solid var(--vscode-panel-border); + font-size: 10px; + } + + .code-block-language { + color: var(--vscode-descriptionForeground); + font-family: var(--vscode-editor-font-family); + font-weight: 500; + text-transform: uppercase; + letter-spacing: 0.5px; + } + + .code-copy-btn { + background: none; + border: none; + color: var(--vscode-descriptionForeground); + cursor: pointer; + padding: 4px; + border-radius: 3px; + display: flex; + align-items: center; + justify-content: center; + transition: all 0.2s ease; + opacity: 0.7; + } + + .code-copy-btn:hover { + background-color: var(--vscode-list-hoverBackground); + opacity: 1; + } + + .code-block-container .code-block { + margin: 0; + border: none; + border-radius: 0; + background: none; + } + /* Inline code */ .message-content code { background-color: var(--vscode-textCodeBlock-background); diff --git a/src/ui.ts b/src/ui.ts index 0381dea..8d9d21c 100644 --- a/src/ui.ts +++ b/src/ui.ts @@ -1815,6 +1815,32 @@ const html = ` }); } } + + function copyCodeBlock(codeId) { + const codeElement = document.getElementById(codeId); + if (codeElement) { + const rawCode = codeElement.getAttribute('data-raw-code'); + if (rawCode) { + // Decode HTML entities + const decodedCode = rawCode.replace(/"/g, '"').replace(/</g, '<').replace(/>/g, '>').replace(/&/g, '&'); + navigator.clipboard.writeText(decodedCode).then(() => { + // Show temporary feedback + const copyBtn = codeElement.closest('.code-block-container').querySelector('.code-copy-btn'); + if (copyBtn) { + const originalInnerHTML = copyBtn.innerHTML; + copyBtn.innerHTML = ''; + copyBtn.style.color = '#4caf50'; + setTimeout(() => { + copyBtn.innerHTML = originalInnerHTML; + copyBtn.style.color = ''; + }, 1000); + } + }).catch(err => { + console.error('Failed to copy code:', err); + }); + } + } + } window.addEventListener('message', event => { const message = event.data; @@ -2263,6 +2289,9 @@ const html = ` // First, handle code blocks before line-by-line processing let processedMarkdown = markdown; + // Store code blocks temporarily to protect them from further processing + const codeBlockPlaceholders = []; + // Handle multi-line code blocks with triple backticks // Using RegExp constructor to avoid backtick conflicts in template literal const codeBlockRegex = new RegExp('\\\`\\\`\\\`(\\\\w*)\\n([\\\\s\\\\S]*?)\\\`\\\`\\\`', 'g'); @@ -2277,7 +2306,16 @@ const html = ` codeHtml += '
' + codeHtml + '\\n';
+ // Create unique ID for this code block
+ const codeId = 'code_' + Math.random().toString(36).substr(2, 9);
+ const escapedCode = escapeHtml(code);
+
+ const codeBlockHtml = '' + codeHtml + '
if (inUnorderedList) html += '';
if (inOrderedList) html += '';
+ // Restore code block placeholders
+ for (let i = 0; i < codeBlockPlaceholders.length; i++) {
+ const placeholder = '__CODEBLOCK_' + i + '__';
+ html = html.replace(placeholder, codeBlockPlaceholders[i]);
+ }
+
return html;
}