${lineNumbersHtml}
@@ -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;
diff --git a/ts_web/elements/wysiwyg/wysiwyg.modalmanager.ts b/ts_web/elements/wysiwyg/wysiwyg.modalmanager.ts
index 3814869..86a707a 100644
--- a/ts_web/elements/wysiwyg/wysiwyg.modalmanager.ts
+++ b/ts_web/elements/wysiwyg/wysiwyg.modalmanager.ts
@@ -1,4 +1,4 @@
-import { html, type TemplateResult } from '@design.estate/dees-element';
+import { html, type TemplateResult, cssManager } from '@design.estate/dees-element';
import { DeesModal } from '../dees-modal.js';
import { type IBlock } from './wysiwyg.types.js';
import { WysiwygShortcuts } from './wysiwyg.shortcuts.js';
@@ -16,37 +16,56 @@ export class WysiwygModalManager {
heading: 'Select Programming Language',
content: html`
-
- ${this.getLanguages().map(lang => html`
-
{
- selectedLanguage = lang.toLowerCase();
- const modal = (e.target as HTMLElement).closest('dees-modal');
- if (modal) {
- const okButton = modal.shadowRoot?.querySelector('.bottomButton.ok') as HTMLElement;
- if (okButton) okButton.click();
- }
- }}">${lang}
- `)}
+
+
+ ${this.getLanguages().map(lang => html`
+
{
+ selectedLanguage = lang.toLowerCase();
+ // Close modal by finding it in DOM
+ const modal = document.querySelector('dees-modal');
+ if (modal && typeof (modal as any).destroy === 'function') {
+ (modal as any).destroy();
+ }
+ resolve(selectedLanguage);
+ }}">
+ ${lang}
+
+ `)}
+
`,
menuOptions: [
@@ -56,13 +75,6 @@ export class WysiwygModalManager {
modal.destroy();
resolve(null);
}
- },
- {
- name: 'OK',
- action: async (modal) => {
- modal.destroy();
- resolve(selectedLanguage);
- }
}
]
});
@@ -76,48 +88,61 @@ export class WysiwygModalManager {
block: IBlock,
onUpdate: (block: IBlock) => void
): Promise
{
+
const content = html`
@@ -131,7 +156,7 @@ export class WysiwygModalManager {
content,
menuOptions: [
{
- name: 'Close',
+ name: 'Done',
action: async (modal) => {
modal.destroy();
}
@@ -147,57 +172,55 @@ export class WysiwygModalManager {
block: IBlock,
onUpdate: (block: IBlock) => void
): TemplateResult {
- const currentLanguage = block.metadata?.language || 'plain text';
+ const currentLanguage = block.metadata?.language || 'javascript';
return html`
Programming Language
${this.getLanguages().map(lang => html`
-
`)}
@@ -228,6 +251,8 @@ export class WysiwygModalManager {
{
+ const button = e.currentTarget as HTMLElement;
+
const oldType = block.type;
block.type = item.type as IBlock['type'];
@@ -252,11 +277,10 @@ export class WysiwygModalManager {
onUpdate(block);
- // Close modal after selection
- const modal = (e.target as HTMLElement).closest('dees-modal');
- if (modal) {
- const closeButton = modal.shadowRoot?.querySelector('.bottomButton') as HTMLElement;
- if (closeButton) closeButton.click();
+ // Close modal immediately
+ const modal = document.querySelector('dees-modal');
+ if (modal && typeof (modal as any).destroy === 'function') {
+ (modal as any).destroy();
}
}}"
>