This commit is contained in:
Juergen Kunz
2025-06-26 11:57:04 +00:00
parent 09e35d0245
commit 3ea7186d6c
3 changed files with 198 additions and 683 deletions

View File

@ -33,6 +33,13 @@ export class CodeBlockHandler extends BaseBlockHandler {
<div class="code-block-container${isSelected ? ' selected' : ''}" data-language="${language}">
<div class="code-header">
<span class="language-label">${language}</span>
<button class="copy-button" title="Copy code">
<svg class="copy-icon" width="16" height="16" viewBox="0 0 16 16" fill="currentColor">
<path d="M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 010 1.5h-1.5a.25.25 0 00-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 00.25-.25v-1.5a.75.75 0 011.5 0v1.5A1.75 1.75 0 019.25 16h-7.5A1.75 1.75 0 010 14.25v-7.5z"></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 0114.25 11h-7.5A1.75 1.75 0 015 9.25v-7.5zm1.75-.25a.25.25 0 00-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 00.25-.25v-7.5a.25.25 0 00-.25-.25h-7.5z"></path>
</svg>
<span class="copy-text">Copy</span>
</button>
</div>
<div class="code-body">
<div class="line-numbers">${lineNumbersHtml}</div>
@ -51,9 +58,59 @@ export class CodeBlockHandler extends BaseBlockHandler {
setup(element: HTMLElement, block: IBlock, handlers: IBlockEventHandlers): void {
const editor = element.querySelector('.code-editor') as HTMLElement;
const container = element.querySelector('.code-block-container') as HTMLElement;
const copyButton = element.querySelector('.copy-button') as HTMLButtonElement;
if (!editor || !container) return;
// Setup copy button
if (copyButton) {
copyButton.addEventListener('click', async () => {
const content = editor.textContent || '';
try {
await navigator.clipboard.writeText(content);
// Show feedback
const copyText = copyButton.querySelector('.copy-text') as HTMLElement;
const originalText = copyText.textContent;
copyText.textContent = 'Copied!';
copyButton.classList.add('copied');
// Reset after 2 seconds
setTimeout(() => {
copyText.textContent = originalText;
copyButton.classList.remove('copied');
}, 2000);
} catch (err) {
console.error('Failed to copy:', err);
// Fallback for older browsers
const textArea = document.createElement('textarea');
textArea.value = content;
textArea.style.position = 'fixed';
textArea.style.opacity = '0';
document.body.appendChild(textArea);
textArea.select();
try {
// @ts-ignore - execCommand is deprecated but needed for fallback
document.execCommand('copy');
// Show feedback
const copyText = copyButton.querySelector('.copy-text') as HTMLElement;
const originalText = copyText.textContent;
copyText.textContent = 'Copied!';
copyButton.classList.add('copied');
setTimeout(() => {
copyText.textContent = originalText;
copyButton.classList.remove('copied');
}, 2000);
} catch (err) {
console.error('Fallback copy failed:', err);
}
document.body.removeChild(textArea);
}
});
}
// Track if we're currently editing
let isEditing = false;
@ -325,7 +382,7 @@ export class CodeBlockHandler extends BaseBlockHandler {
border-bottom: 1px solid ${cssManager.bdTheme('#d1d5da', '#30363d')};
padding: 8px 16px;
display: flex;
justify-content: flex-end;
justify-content: space-between;
align-items: center;
}
@ -338,6 +395,48 @@ export class CodeBlockHandler extends BaseBlockHandler {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
}
/* Copy Button */
.copy-button {
display: flex;
align-items: center;
gap: 6px;
padding: 4px 12px;
background: transparent;
border: 1px solid ${cssManager.bdTheme('#d1d5da', '#30363d')};
border-radius: 6px;
color: ${cssManager.bdTheme('#57606a', '#8b949e')};
font-size: 12px;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
cursor: pointer;
transition: all 0.2s ease;
outline: none;
}
.copy-button:hover {
background: ${cssManager.bdTheme('#f3f4f6', '#1c2128')};
color: ${cssManager.bdTheme('#24292f', '#c9d1d9')};
border-color: ${cssManager.bdTheme('#8b949e', '#484f58')};
}
.copy-button:active {
transform: scale(0.95);
}
.copy-button.copied {
background: ${cssManager.bdTheme('#2ea043', '#238636')};
border-color: ${cssManager.bdTheme('#2ea043', '#238636')};
color: white;
}
.copy-icon {
flex-shrink: 0;
}
.copy-text {
min-width: 45px;
text-align: center;
}
/* Code Body */
.code-body {
display: flex;