mirror of
https://github.com/andrepimenta/claude-code-chat.git
synced 2025-12-08 22:59:44 +00:00
Copy code block
This commit is contained in:
@@ -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);
|
||||
|
||||
52
src/ui.ts
52
src/ui.ts
@@ -1815,6 +1815,32 @@ const html = `<!DOCTYPE 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 = '<svg width="12" height="12" viewBox="0 0 24 24" fill="currentColor"><path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"/></svg>';
|
||||
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 = `<!DOCTYPE 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 = `<!DOCTYPE html>
|
||||
codeHtml += '<div class="code-line">' + escapedLine + '</div>';
|
||||
}
|
||||
|
||||
return '\\n<pre class="code-block"><code class="language-' + language + '">' + codeHtml + '</code></pre>\\n';
|
||||
// Create unique ID for this code block
|
||||
const codeId = 'code_' + Math.random().toString(36).substr(2, 9);
|
||||
const escapedCode = escapeHtml(code);
|
||||
|
||||
const codeBlockHtml = '<div class="code-block-container"><div class="code-block-header"><span class="code-block-language">' + language + '</span><button class="code-copy-btn" onclick="copyCodeBlock(\\\'' + codeId + '\\\')" title="Copy code"><svg width="12" height="12" viewBox="0 0 24 24" fill="currentColor"><path d="M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 16H8V7h11v14z"/></svg></button></div><pre class="code-block"><code class="language-' + language + '" id="' + codeId + '" data-raw-code="' + escapedCode.replace(/"/g, '"') + '">' + codeHtml + '</code></pre></div>';
|
||||
|
||||
// Store the code block and return a placeholder
|
||||
const placeholder = '__CODEBLOCK_' + codeBlockPlaceholders.length + '__';
|
||||
codeBlockPlaceholders.push(codeBlockHtml);
|
||||
return placeholder;
|
||||
});
|
||||
|
||||
// Handle inline code with single backticks
|
||||
@@ -2292,9 +2330,9 @@ const html = `<!DOCTYPE html>
|
||||
for (let line of lines) {
|
||||
line = line.trim();
|
||||
|
||||
// Check if this is a code block
|
||||
if (line.includes('<pre class="code-block"><code')) {
|
||||
// This is a complete code block on potentially multiple lines
|
||||
// Check if this is a code block placeholder
|
||||
if (line.startsWith('__CODEBLOCK_') && line.endsWith('__')) {
|
||||
// This is a code block placeholder, don't process it
|
||||
html += line;
|
||||
continue;
|
||||
}
|
||||
@@ -2363,6 +2401,12 @@ const html = `<!DOCTYPE html>
|
||||
if (inUnorderedList) html += '</ul>';
|
||||
if (inOrderedList) html += '</ol>';
|
||||
|
||||
// Restore code block placeholders
|
||||
for (let i = 0; i < codeBlockPlaceholders.length; i++) {
|
||||
const placeholder = '__CODEBLOCK_' + i + '__';
|
||||
html = html.replace(placeholder, codeBlockPlaceholders[i]);
|
||||
}
|
||||
|
||||
return html;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user