| 
									
										
										
										
											2025-06-24 22:45:50 +00:00
										 |  |  | import { BaseBlockHandler, type IBlockEventHandlers } from '../block.base.js'; | 
					
						
							|  |  |  | import type { IBlock } from '../../wysiwyg.types.js'; | 
					
						
							|  |  |  | import { cssManager } from '@design.estate/dees-element'; | 
					
						
							|  |  |  | import { WysiwygSelection } from '../../wysiwyg.selection.js'; | 
					
						
							| 
									
										
										
										
											2025-06-26 11:41:58 +00:00
										 |  |  | import hlight from 'highlight.js'; | 
					
						
							| 
									
										
										
										
											2025-06-27 17:32:01 +00:00
										 |  |  | import { cssGeistFontFamily, cssMonoFontFamily } from '../../../00fonts.js'; | 
					
						
							| 
									
										
										
										
											2025-06-27 18:38:39 +00:00
										 |  |  | import { PROGRAMMING_LANGUAGES } from '../../wysiwyg.constants.js'; | 
					
						
							| 
									
										
										
										
											2025-06-24 22:45:50 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-06-26 11:41:58 +00:00
										 |  |  | /** | 
					
						
							| 
									
										
										
										
											2025-06-26 13:38:09 +00:00
										 |  |  |  * CodeBlockHandler with improved architecture | 
					
						
							| 
									
										
										
										
											2025-06-26 11:41:58 +00:00
										 |  |  |  *  | 
					
						
							| 
									
										
										
										
											2025-06-26 13:38:09 +00:00
										 |  |  |  * Key features: | 
					
						
							|  |  |  |  * 1. Simple DOM structure | 
					
						
							|  |  |  |  * 2. Line number handling | 
					
						
							|  |  |  |  * 3. Syntax highlighting only when not focused (grey text while editing) | 
					
						
							|  |  |  |  * 4. Clean event handling | 
					
						
							|  |  |  |  * 5. Copy button functionality | 
					
						
							| 
									
										
										
										
											2025-06-26 11:41:58 +00:00
										 |  |  |  */ | 
					
						
							| 
									
										
										
										
											2025-06-24 22:45:50 +00:00
										 |  |  | export class CodeBlockHandler extends BaseBlockHandler { | 
					
						
							|  |  |  |   type = 'code'; | 
					
						
							|  |  |  |    | 
					
						
							| 
									
										
										
										
											2025-06-26 11:41:58 +00:00
										 |  |  |   private highlightTimer: any = null; | 
					
						
							| 
									
										
										
										
											2025-06-24 22:45:50 +00:00
										 |  |  |    | 
					
						
							|  |  |  |   render(block: IBlock, isSelected: boolean): string { | 
					
						
							| 
									
										
										
										
											2025-06-27 18:38:39 +00:00
										 |  |  |     const language = block.metadata?.language || 'typescript'; | 
					
						
							| 
									
										
										
										
											2025-06-26 11:41:58 +00:00
										 |  |  |     const content = block.content || ''; | 
					
						
							|  |  |  |     const lineCount = content.split('\n').length; | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     // Generate line numbers
 | 
					
						
							|  |  |  |     let lineNumbersHtml = ''; | 
					
						
							|  |  |  |     for (let i = 1; i <= lineCount; i++) { | 
					
						
							|  |  |  |       lineNumbersHtml += `<div class="line-number">${i}</div>`; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2025-06-24 22:45:50 +00:00
										 |  |  |      | 
					
						
							| 
									
										
										
										
											2025-06-27 18:38:39 +00:00
										 |  |  |     // Generate language options
 | 
					
						
							|  |  |  |     const languageOptions = PROGRAMMING_LANGUAGES.map(lang => { | 
					
						
							|  |  |  |       const value = lang.toLowerCase(); | 
					
						
							|  |  |  |       return `<option value="${value}" ${value === language ? 'selected' : ''}>${lang}</option>`; | 
					
						
							|  |  |  |     }).join(''); | 
					
						
							|  |  |  |      | 
					
						
							| 
									
										
										
										
											2025-06-24 22:45:50 +00:00
										 |  |  |     return `
 | 
					
						
							| 
									
										
										
										
											2025-06-26 11:41:58 +00:00
										 |  |  |       <div class="code-block-container${isSelected ? ' selected' : ''}" data-language="${language}"> | 
					
						
							|  |  |  |         <div class="code-header"> | 
					
						
							| 
									
										
										
										
											2025-06-27 18:38:39 +00:00
										 |  |  |           <select class="language-selector" data-block-id="${block.id}"> | 
					
						
							|  |  |  |             ${languageOptions} | 
					
						
							|  |  |  |           </select> | 
					
						
							| 
									
										
										
										
											2025-06-26 11:57:04 +00:00
										 |  |  |           <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> | 
					
						
							| 
									
										
										
										
											2025-06-26 11:41:58 +00:00
										 |  |  |         </div> | 
					
						
							|  |  |  |         <div class="code-body"> | 
					
						
							|  |  |  |           <div class="line-numbers">${lineNumbersHtml}</div> | 
					
						
							|  |  |  |           <div class="code-content"> | 
					
						
							|  |  |  |             <pre class="code-pre"><code class="code-editor"  | 
					
						
							|  |  |  |                  contenteditable="true" | 
					
						
							|  |  |  |                  data-block-id="${block.id}" | 
					
						
							|  |  |  |                  data-block-type="${block.type}" | 
					
						
							|  |  |  |                  spellcheck="false">${this.escapeHtml(content)}</code></pre> | 
					
						
							|  |  |  |           </div> | 
					
						
							|  |  |  |         </div> | 
					
						
							| 
									
										
										
										
											2025-06-24 22:45:50 +00:00
										 |  |  |       </div> | 
					
						
							|  |  |  |     `;
 | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |    | 
					
						
							|  |  |  |   setup(element: HTMLElement, block: IBlock, handlers: IBlockEventHandlers): void { | 
					
						
							| 
									
										
										
										
											2025-06-26 11:41:58 +00:00
										 |  |  |     const editor = element.querySelector('.code-editor') as HTMLElement; | 
					
						
							|  |  |  |     const container = element.querySelector('.code-block-container') as HTMLElement; | 
					
						
							| 
									
										
										
										
											2025-06-26 11:57:04 +00:00
										 |  |  |     const copyButton = element.querySelector('.copy-button') as HTMLButtonElement; | 
					
						
							| 
									
										
										
										
											2025-06-27 18:38:39 +00:00
										 |  |  |     const languageSelector = element.querySelector('.language-selector') as HTMLSelectElement; | 
					
						
							| 
									
										
										
										
											2025-06-24 22:45:50 +00:00
										 |  |  |      | 
					
						
							| 
									
										
										
										
											2025-06-26 11:41:58 +00:00
										 |  |  |     if (!editor || !container) return; | 
					
						
							|  |  |  |      | 
					
						
							| 
									
										
										
										
											2025-06-27 18:38:39 +00:00
										 |  |  |     // Setup language selector
 | 
					
						
							|  |  |  |     if (languageSelector) { | 
					
						
							|  |  |  |       languageSelector.addEventListener('change', (e) => { | 
					
						
							|  |  |  |         const newLanguage = (e.target as HTMLSelectElement).value; | 
					
						
							|  |  |  |         block.metadata = { ...block.metadata, language: newLanguage }; | 
					
						
							|  |  |  |         container.setAttribute('data-language', newLanguage); | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         // Update the syntax highlighting if content exists and not focused
 | 
					
						
							|  |  |  |         if (block.content && document.activeElement !== editor) { | 
					
						
							|  |  |  |           this.applyHighlighting(element, block); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         // Notify about the change
 | 
					
						
							|  |  |  |         if (handlers.onInput) { | 
					
						
							|  |  |  |           handlers.onInput(new InputEvent('input')); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |      | 
					
						
							| 
									
										
										
										
											2025-06-26 11:57:04 +00:00
										 |  |  |     // 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); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |      | 
					
						
							| 
									
										
										
										
											2025-06-26 11:41:58 +00:00
										 |  |  |     // Track if we're currently editing
 | 
					
						
							|  |  |  |     let isEditing = false; | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     // Focus handler
 | 
					
						
							|  |  |  |     editor.addEventListener('focus', () => { | 
					
						
							|  |  |  |       isEditing = true; | 
					
						
							|  |  |  |       container.classList.add('editing'); | 
					
						
							| 
									
										
										
										
											2025-06-26 13:38:09 +00:00
										 |  |  |        | 
					
						
							|  |  |  |       // Remove all syntax highlighting when focused
 | 
					
						
							|  |  |  |       const content = editor.textContent || ''; | 
					
						
							|  |  |  |       editor.textContent = content; // This removes all HTML formatting
 | 
					
						
							|  |  |  |        | 
					
						
							|  |  |  |       // Restore cursor position after removing highlighting
 | 
					
						
							|  |  |  |       requestAnimationFrame(() => { | 
					
						
							|  |  |  |         const range = document.createRange(); | 
					
						
							|  |  |  |         const selection = window.getSelection(); | 
					
						
							|  |  |  |         if (editor.firstChild) { | 
					
						
							|  |  |  |           range.setStart(editor.firstChild, 0); | 
					
						
							|  |  |  |           range.collapse(true); | 
					
						
							|  |  |  |           selection?.removeAllRanges(); | 
					
						
							|  |  |  |           selection?.addRange(range); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |        | 
					
						
							| 
									
										
										
										
											2025-06-26 11:41:58 +00:00
										 |  |  |       handlers.onFocus(); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     // Blur handler
 | 
					
						
							|  |  |  |     editor.addEventListener('blur', () => { | 
					
						
							|  |  |  |       isEditing = false; | 
					
						
							|  |  |  |       container.classList.remove('editing'); | 
					
						
							|  |  |  |       // Apply final highlighting on blur
 | 
					
						
							|  |  |  |       this.applyHighlighting(element, block); | 
					
						
							|  |  |  |       handlers.onBlur(); | 
					
						
							|  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2025-06-24 22:45:50 +00:00
										 |  |  |      | 
					
						
							|  |  |  |     // Input handler
 | 
					
						
							| 
									
										
										
										
											2025-06-26 11:41:58 +00:00
										 |  |  |     editor.addEventListener('input', (e) => { | 
					
						
							| 
									
										
										
										
											2025-06-24 22:45:50 +00:00
										 |  |  |       handlers.onInput(e as InputEvent); | 
					
						
							|  |  |  |        | 
					
						
							| 
									
										
										
										
											2025-06-26 11:41:58 +00:00
										 |  |  |       // Update line numbers
 | 
					
						
							|  |  |  |       this.updateLineNumbers(element); | 
					
						
							|  |  |  |        | 
					
						
							| 
									
										
										
										
											2025-06-26 13:38:09 +00:00
										 |  |  |       // Clear any pending highlight timer (no highlighting while editing)
 | 
					
						
							| 
									
										
										
										
											2025-06-26 11:41:58 +00:00
										 |  |  |       clearTimeout(this.highlightTimer); | 
					
						
							| 
									
										
										
										
											2025-06-24 22:45:50 +00:00
										 |  |  |     }); | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     // Keydown handler
 | 
					
						
							| 
									
										
										
										
											2025-06-26 11:41:58 +00:00
										 |  |  |     editor.addEventListener('keydown', (e) => { | 
					
						
							| 
									
										
										
										
											2025-06-26 13:45:00 +00:00
										 |  |  |       // Handle Tab key for code blocks
 | 
					
						
							| 
									
										
										
										
											2025-06-24 22:45:50 +00:00
										 |  |  |       if (e.key === 'Tab') { | 
					
						
							|  |  |  |         e.preventDefault(); | 
					
						
							|  |  |  |         const selection = window.getSelection(); | 
					
						
							|  |  |  |         if (selection && selection.rangeCount > 0) { | 
					
						
							|  |  |  |           const range = selection.getRangeAt(0); | 
					
						
							|  |  |  |           const textNode = document.createTextNode('  '); | 
					
						
							|  |  |  |           range.insertNode(textNode); | 
					
						
							|  |  |  |           range.setStartAfter(textNode); | 
					
						
							|  |  |  |           range.setEndAfter(textNode); | 
					
						
							|  |  |  |           selection.removeAllRanges(); | 
					
						
							|  |  |  |           selection.addRange(range); | 
					
						
							|  |  |  |           handlers.onInput(new InputEvent('input')); | 
					
						
							| 
									
										
										
										
											2025-06-26 11:41:58 +00:00
										 |  |  |           this.updateLineNumbers(element); | 
					
						
							| 
									
										
										
										
											2025-06-24 22:45:50 +00:00
										 |  |  |         } | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |        | 
					
						
							| 
									
										
										
										
											2025-06-26 13:45:00 +00:00
										 |  |  |       // Check cursor position for navigation keys
 | 
					
						
							|  |  |  |       if (['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown'].includes(e.key)) { | 
					
						
							|  |  |  |         const cursorPos = this.getCursorPosition(element); | 
					
						
							|  |  |  |         const textLength = editor.textContent?.length || 0; | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         // For ArrowLeft at position 0 or ArrowRight at end, let parent handle navigation
 | 
					
						
							|  |  |  |         if ((e.key === 'ArrowLeft' && cursorPos === 0) ||  | 
					
						
							|  |  |  |             (e.key === 'ArrowRight' && cursorPos === textLength)) { | 
					
						
							|  |  |  |           // Pass to parent handler for inter-block navigation
 | 
					
						
							|  |  |  |           handlers.onKeyDown(e); | 
					
						
							|  |  |  |           return; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         // For ArrowUp/Down, check if we're at first/last line
 | 
					
						
							|  |  |  |         if (e.key === 'ArrowUp' || e.key === 'ArrowDown') { | 
					
						
							|  |  |  |           const lines = (editor.textContent || '').split('\n'); | 
					
						
							|  |  |  |           const currentLine = this.getCurrentLineIndex(editor); | 
					
						
							|  |  |  |            | 
					
						
							|  |  |  |           if ((e.key === 'ArrowUp' && currentLine === 0) || | 
					
						
							|  |  |  |               (e.key === 'ArrowDown' && currentLine === lines.length - 1)) { | 
					
						
							|  |  |  |             // Let parent handle navigation to prev/next block
 | 
					
						
							|  |  |  |             handlers.onKeyDown(e); | 
					
						
							|  |  |  |             return; | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |        | 
					
						
							|  |  |  |       // Pass other keys to parent handler
 | 
					
						
							| 
									
										
										
										
											2025-06-24 22:45:50 +00:00
										 |  |  |       handlers.onKeyDown(e); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |      | 
					
						
							| 
									
										
										
										
											2025-06-26 11:41:58 +00:00
										 |  |  |     // Paste handler - plain text only
 | 
					
						
							|  |  |  |     editor.addEventListener('paste', (e) => { | 
					
						
							| 
									
										
										
										
											2025-06-24 22:45:50 +00:00
										 |  |  |       e.preventDefault(); | 
					
						
							|  |  |  |       const text = e.clipboardData?.getData('text/plain'); | 
					
						
							|  |  |  |       if (text) { | 
					
						
							|  |  |  |         const selection = window.getSelection(); | 
					
						
							|  |  |  |         if (selection && selection.rangeCount > 0) { | 
					
						
							|  |  |  |           const range = selection.getRangeAt(0); | 
					
						
							|  |  |  |           range.deleteContents(); | 
					
						
							|  |  |  |           const textNode = document.createTextNode(text); | 
					
						
							|  |  |  |           range.insertNode(textNode); | 
					
						
							|  |  |  |           range.setStartAfter(textNode); | 
					
						
							|  |  |  |           range.setEndAfter(textNode); | 
					
						
							|  |  |  |           selection.removeAllRanges(); | 
					
						
							|  |  |  |           selection.addRange(range); | 
					
						
							|  |  |  |           handlers.onInput(new InputEvent('input')); | 
					
						
							| 
									
										
										
										
											2025-06-26 11:41:58 +00:00
										 |  |  |           this.updateLineNumbers(element); | 
					
						
							| 
									
										
										
										
											2025-06-24 22:45:50 +00:00
										 |  |  |         } | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2025-06-26 11:41:58 +00:00
										 |  |  |      | 
					
						
							|  |  |  |     // Composition handlers
 | 
					
						
							|  |  |  |     editor.addEventListener('compositionstart', () => handlers.onCompositionStart()); | 
					
						
							|  |  |  |     editor.addEventListener('compositionend', () => handlers.onCompositionEnd()); | 
					
						
							|  |  |  |      | 
					
						
							| 
									
										
										
										
											2025-06-26 13:38:09 +00:00
										 |  |  |     // Initial syntax highlighting if content exists and not focused
 | 
					
						
							|  |  |  |     if (block.content && document.activeElement !== editor) { | 
					
						
							| 
									
										
										
										
											2025-06-26 11:41:58 +00:00
										 |  |  |       requestAnimationFrame(() => { | 
					
						
							|  |  |  |         this.applyHighlighting(element, block); | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2025-06-24 22:45:50 +00:00
										 |  |  |   } | 
					
						
							|  |  |  |    | 
					
						
							| 
									
										
										
										
											2025-06-26 11:41:58 +00:00
										 |  |  |   private updateLineNumbers(element: HTMLElement): void { | 
					
						
							|  |  |  |     const editor = element.querySelector('.code-editor') as HTMLElement; | 
					
						
							|  |  |  |     const lineNumbersContainer = element.querySelector('.line-numbers') as HTMLElement; | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     if (!editor || !lineNumbersContainer) return; | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     const content = editor.textContent || ''; | 
					
						
							|  |  |  |     const lines = content.split('\n'); | 
					
						
							|  |  |  |     const lineCount = lines.length || 1; | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     let lineNumbersHtml = ''; | 
					
						
							|  |  |  |     for (let i = 1; i <= lineCount; i++) { | 
					
						
							|  |  |  |       lineNumbersHtml += `<div class="line-number">${i}</div>`; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     lineNumbersContainer.innerHTML = lineNumbersHtml; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |    | 
					
						
							| 
									
										
										
										
											2025-06-26 13:45:00 +00:00
										 |  |  |   private getCurrentLineIndex(editor: HTMLElement): number { | 
					
						
							|  |  |  |     const selection = window.getSelection(); | 
					
						
							|  |  |  |     if (!selection || selection.rangeCount === 0) return 0; | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     const range = selection.getRangeAt(0); | 
					
						
							|  |  |  |     const preCaretRange = range.cloneRange(); | 
					
						
							|  |  |  |     preCaretRange.selectNodeContents(editor); | 
					
						
							|  |  |  |     preCaretRange.setEnd(range.startContainer, range.startOffset); | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     const textBeforeCursor = preCaretRange.toString(); | 
					
						
							|  |  |  |     const linesBeforeCursor = textBeforeCursor.split('\n'); | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     return linesBeforeCursor.length - 1; // 0-indexed
 | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |    | 
					
						
							| 
									
										
										
										
											2025-06-26 11:41:58 +00:00
										 |  |  |   private applyHighlighting(element: HTMLElement, block: IBlock): void { | 
					
						
							|  |  |  |     const editor = element.querySelector('.code-editor') as HTMLElement; | 
					
						
							|  |  |  |     if (!editor) return; | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     // Store cursor position
 | 
					
						
							|  |  |  |     const cursorPos = this.getCursorPosition(element); | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     // Get plain text content
 | 
					
						
							|  |  |  |     const content = editor.textContent || ''; | 
					
						
							| 
									
										
										
										
											2025-06-27 18:38:39 +00:00
										 |  |  |     const language = block.metadata?.language || 'typescript'; | 
					
						
							| 
									
										
										
										
											2025-06-26 11:41:58 +00:00
										 |  |  |      | 
					
						
							|  |  |  |     // Apply highlighting
 | 
					
						
							|  |  |  |     try { | 
					
						
							|  |  |  |       const result = hlight.highlight(content, {  | 
					
						
							|  |  |  |         language: language, | 
					
						
							|  |  |  |         ignoreIllegals: true  | 
					
						
							|  |  |  |       }); | 
					
						
							| 
									
										
										
										
											2025-06-24 22:45:50 +00:00
										 |  |  |        | 
					
						
							| 
									
										
										
										
											2025-06-26 11:41:58 +00:00
										 |  |  |       // Only update if we have valid highlighted content
 | 
					
						
							|  |  |  |       if (result.value) { | 
					
						
							|  |  |  |         editor.innerHTML = result.value; | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         // Restore cursor position if editor is focused
 | 
					
						
							|  |  |  |         if (document.activeElement === editor && cursorPos !== null) { | 
					
						
							|  |  |  |           requestAnimationFrame(() => { | 
					
						
							|  |  |  |             WysiwygSelection.setCursorPosition(editor, cursorPos); | 
					
						
							|  |  |  |           }); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2025-06-24 22:45:50 +00:00
										 |  |  |       } | 
					
						
							| 
									
										
										
										
											2025-06-26 11:41:58 +00:00
										 |  |  |     } catch (error) { | 
					
						
							|  |  |  |       // If highlighting fails, keep plain text
 | 
					
						
							|  |  |  |       console.warn('Syntax highlighting failed:', error); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2025-06-24 22:45:50 +00:00
										 |  |  |   } | 
					
						
							|  |  |  |    | 
					
						
							| 
									
										
										
										
											2025-06-26 11:41:58 +00:00
										 |  |  |   private escapeHtml(text: string): string { | 
					
						
							|  |  |  |     const div = document.createElement('div'); | 
					
						
							|  |  |  |     div.textContent = text; | 
					
						
							|  |  |  |     return div.innerHTML; | 
					
						
							| 
									
										
										
										
											2025-06-24 22:45:50 +00:00
										 |  |  |   } | 
					
						
							|  |  |  |    | 
					
						
							| 
									
										
										
										
											2025-06-26 11:41:58 +00:00
										 |  |  |   getContent(element: HTMLElement): string { | 
					
						
							|  |  |  |     const editor = element.querySelector('.code-editor') as HTMLElement; | 
					
						
							|  |  |  |     return editor?.textContent || ''; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2025-06-24 22:45:50 +00:00
										 |  |  |    | 
					
						
							| 
									
										
										
										
											2025-06-26 11:41:58 +00:00
										 |  |  |   setContent(element: HTMLElement, content: string): void { | 
					
						
							|  |  |  |     const editor = element.querySelector('.code-editor') as HTMLElement; | 
					
						
							|  |  |  |     if (!editor) return; | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     editor.textContent = content; | 
					
						
							|  |  |  |     this.updateLineNumbers(element); | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     // Apply highlighting if not focused
 | 
					
						
							|  |  |  |     if (document.activeElement !== editor) { | 
					
						
							|  |  |  |       const block: IBlock = { | 
					
						
							|  |  |  |         id: editor.dataset.blockId || '', | 
					
						
							|  |  |  |         type: 'code', | 
					
						
							|  |  |  |         content: content, | 
					
						
							|  |  |  |         metadata: {  | 
					
						
							| 
									
										
										
										
											2025-06-27 18:38:39 +00:00
										 |  |  |           language: element.querySelector('.code-block-container')?.getAttribute('data-language') || 'typescript' | 
					
						
							| 
									
										
										
										
											2025-06-26 11:41:58 +00:00
										 |  |  |         } | 
					
						
							|  |  |  |       }; | 
					
						
							|  |  |  |       this.applyHighlighting(element, block); | 
					
						
							| 
									
										
										
										
											2025-06-24 22:45:50 +00:00
										 |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |    | 
					
						
							| 
									
										
										
										
											2025-06-26 11:41:58 +00:00
										 |  |  |   getCursorPosition(element: HTMLElement): number | null { | 
					
						
							|  |  |  |     const editor = element.querySelector('.code-editor') as HTMLElement; | 
					
						
							|  |  |  |     if (!editor) return null; | 
					
						
							| 
									
										
										
										
											2025-06-24 22:45:50 +00:00
										 |  |  |      | 
					
						
							| 
									
										
										
										
											2025-06-26 11:41:58 +00:00
										 |  |  |     const selection = window.getSelection(); | 
					
						
							|  |  |  |     if (!selection || selection.rangeCount === 0) return null; | 
					
						
							| 
									
										
										
										
											2025-06-24 22:45:50 +00:00
										 |  |  |      | 
					
						
							| 
									
										
										
										
											2025-06-26 11:41:58 +00:00
										 |  |  |     const range = selection.getRangeAt(0); | 
					
						
							|  |  |  |     if (!editor.contains(range.startContainer)) return null; | 
					
						
							| 
									
										
										
										
											2025-06-24 22:45:50 +00:00
										 |  |  |      | 
					
						
							| 
									
										
										
										
											2025-06-26 11:41:58 +00:00
										 |  |  |     const preCaretRange = document.createRange(); | 
					
						
							|  |  |  |     preCaretRange.selectNodeContents(editor); | 
					
						
							|  |  |  |     preCaretRange.setEnd(range.startContainer, range.startOffset); | 
					
						
							| 
									
										
										
										
											2025-06-24 22:45:50 +00:00
										 |  |  |      | 
					
						
							| 
									
										
										
										
											2025-06-26 11:41:58 +00:00
										 |  |  |     return preCaretRange.toString().length; | 
					
						
							| 
									
										
										
										
											2025-06-24 22:45:50 +00:00
										 |  |  |   } | 
					
						
							|  |  |  |    | 
					
						
							| 
									
										
										
										
											2025-06-26 11:41:58 +00:00
										 |  |  |   setCursorToStart(element: HTMLElement): void { | 
					
						
							|  |  |  |     const editor = element.querySelector('.code-editor') as HTMLElement; | 
					
						
							|  |  |  |     if (editor) { | 
					
						
							|  |  |  |       WysiwygSelection.setCursorPosition(editor, 0); | 
					
						
							| 
									
										
										
										
											2025-06-24 22:45:50 +00:00
										 |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |    | 
					
						
							| 
									
										
										
										
											2025-06-26 11:41:58 +00:00
										 |  |  |   setCursorToEnd(element: HTMLElement): void { | 
					
						
							|  |  |  |     const editor = element.querySelector('.code-editor') as HTMLElement; | 
					
						
							|  |  |  |     if (editor) { | 
					
						
							|  |  |  |       const length = editor.textContent?.length || 0; | 
					
						
							|  |  |  |       WysiwygSelection.setCursorPosition(editor, length); | 
					
						
							| 
									
										
										
										
											2025-06-24 22:45:50 +00:00
										 |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |    | 
					
						
							| 
									
										
										
										
											2025-06-26 11:41:58 +00:00
										 |  |  |   focus(element: HTMLElement): void { | 
					
						
							|  |  |  |     const editor = element.querySelector('.code-editor') as HTMLElement; | 
					
						
							|  |  |  |     editor?.focus(); | 
					
						
							| 
									
										
										
										
											2025-06-24 22:45:50 +00:00
										 |  |  |   } | 
					
						
							|  |  |  |    | 
					
						
							| 
									
										
										
										
											2025-06-26 11:41:58 +00:00
										 |  |  |   focusWithCursor(element: HTMLElement, position: 'start' | 'end' | number = 'end'): void { | 
					
						
							|  |  |  |     const editor = element.querySelector('.code-editor') as HTMLElement; | 
					
						
							|  |  |  |     if (!editor) return; | 
					
						
							| 
									
										
										
										
											2025-06-24 22:45:50 +00:00
										 |  |  |      | 
					
						
							| 
									
										
										
										
											2025-06-26 11:41:58 +00:00
										 |  |  |     editor.focus(); | 
					
						
							| 
									
										
										
										
											2025-06-24 22:45:50 +00:00
										 |  |  |      | 
					
						
							| 
									
										
										
										
											2025-06-26 11:41:58 +00:00
										 |  |  |     requestAnimationFrame(() => { | 
					
						
							| 
									
										
										
										
											2025-06-24 22:45:50 +00:00
										 |  |  |       if (position === 'start') { | 
					
						
							| 
									
										
										
										
											2025-06-26 11:41:58 +00:00
										 |  |  |         this.setCursorToStart(element); | 
					
						
							| 
									
										
										
										
											2025-06-24 22:45:50 +00:00
										 |  |  |       } else if (position === 'end') { | 
					
						
							| 
									
										
										
										
											2025-06-26 11:41:58 +00:00
										 |  |  |         this.setCursorToEnd(element); | 
					
						
							| 
									
										
										
										
											2025-06-24 22:45:50 +00:00
										 |  |  |       } else if (typeof position === 'number') { | 
					
						
							| 
									
										
										
										
											2025-06-26 11:41:58 +00:00
										 |  |  |         WysiwygSelection.setCursorPosition(editor, position); | 
					
						
							| 
									
										
										
										
											2025-06-24 22:45:50 +00:00
										 |  |  |       } | 
					
						
							| 
									
										
										
										
											2025-06-26 11:41:58 +00:00
										 |  |  |     }); | 
					
						
							| 
									
										
										
										
											2025-06-24 22:45:50 +00:00
										 |  |  |   } | 
					
						
							|  |  |  |    | 
					
						
							| 
									
										
										
										
											2025-06-26 11:41:58 +00:00
										 |  |  |   getSplitContent(element: HTMLElement): { before: string; after: string } | null { | 
					
						
							|  |  |  |     const position = this.getCursorPosition(element); | 
					
						
							|  |  |  |     if (position === null) return null; | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     const content = this.getContent(element); | 
					
						
							|  |  |  |     return { | 
					
						
							|  |  |  |       before: content.substring(0, position), | 
					
						
							|  |  |  |       after: content.substring(position) | 
					
						
							| 
									
										
										
										
											2025-06-24 22:45:50 +00:00
										 |  |  |     }; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2025-06-26 11:41:58 +00:00
										 |  |  |    | 
					
						
							|  |  |  |   getStyles(): string { | 
					
						
							|  |  |  |     return `
 | 
					
						
							| 
									
										
										
										
											2025-06-26 12:00:35 +00:00
										 |  |  |       /* Code Block Container - Minimalist shadcn style */ | 
					
						
							| 
									
										
										
										
											2025-06-26 11:41:58 +00:00
										 |  |  |       .code-block-container { | 
					
						
							|  |  |  |         position: relative; | 
					
						
							| 
									
										
										
										
											2025-06-26 12:00:35 +00:00
										 |  |  |         margin: 12px 0; | 
					
						
							|  |  |  |         background: transparent; | 
					
						
							|  |  |  |         border: 1px solid ${cssManager.bdTheme('#e5e7eb', '#374151')}; | 
					
						
							|  |  |  |         border-radius: 6px; | 
					
						
							| 
									
										
										
										
											2025-06-26 11:41:58 +00:00
										 |  |  |         overflow: hidden; | 
					
						
							| 
									
										
										
										
											2025-06-26 12:00:35 +00:00
										 |  |  |         transition: all 0.15s ease; | 
					
						
							| 
									
										
										
										
											2025-06-26 11:41:58 +00:00
										 |  |  |       } | 
					
						
							|  |  |  |        | 
					
						
							|  |  |  |       .code-block-container.selected { | 
					
						
							| 
									
										
										
										
											2025-06-26 12:00:35 +00:00
										 |  |  |         border-color: ${cssManager.bdTheme('#9ca3af', '#6b7280')}; | 
					
						
							| 
									
										
										
										
											2025-06-26 11:41:58 +00:00
										 |  |  |       } | 
					
						
							|  |  |  |        | 
					
						
							|  |  |  |       .code-block-container.editing { | 
					
						
							| 
									
										
										
										
											2025-06-26 12:00:35 +00:00
										 |  |  |         border-color: ${cssManager.bdTheme('#6b7280', '#9ca3af')}; | 
					
						
							|  |  |  |         background: ${cssManager.bdTheme('#fafafa', '#0a0a0a')}; | 
					
						
							| 
									
										
										
										
											2025-06-26 11:41:58 +00:00
										 |  |  |       } | 
					
						
							|  |  |  |        | 
					
						
							| 
									
										
										
										
											2025-06-26 12:00:35 +00:00
										 |  |  |       /* Header - Simplified */ | 
					
						
							| 
									
										
										
										
											2025-06-26 11:41:58 +00:00
										 |  |  |       .code-header { | 
					
						
							| 
									
										
										
										
											2025-06-26 12:00:35 +00:00
										 |  |  |         background: transparent; | 
					
						
							|  |  |  |         border-bottom: 1px solid ${cssManager.bdTheme('#e5e7eb', '#374151')}; | 
					
						
							|  |  |  |         padding: 8px 12px; | 
					
						
							| 
									
										
										
										
											2025-06-26 11:41:58 +00:00
										 |  |  |         display: flex; | 
					
						
							| 
									
										
										
										
											2025-06-26 11:57:04 +00:00
										 |  |  |         justify-content: space-between; | 
					
						
							| 
									
										
										
										
											2025-06-26 11:41:58 +00:00
										 |  |  |         align-items: center; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |        | 
					
						
							| 
									
										
										
										
											2025-06-27 18:38:39 +00:00
										 |  |  |       .language-selector { | 
					
						
							| 
									
										
										
										
											2025-06-26 11:41:58 +00:00
										 |  |  |         font-size: 12px; | 
					
						
							| 
									
										
										
										
											2025-06-26 12:00:35 +00:00
										 |  |  |         color: ${cssManager.bdTheme('#6b7280', '#9ca3af')}; | 
					
						
							|  |  |  |         font-weight: 500; | 
					
						
							|  |  |  |         text-transform: uppercase; | 
					
						
							|  |  |  |         letter-spacing: 0.05em; | 
					
						
							| 
									
										
										
										
											2025-06-27 17:32:01 +00:00
										 |  |  |         font-family: ${cssGeistFontFamily}; | 
					
						
							| 
									
										
										
										
											2025-06-27 18:38:39 +00:00
										 |  |  |         background: transparent; | 
					
						
							|  |  |  |         border: 1px solid transparent; | 
					
						
							|  |  |  |         border-radius: 4px; | 
					
						
							|  |  |  |         padding: 4px 8px; | 
					
						
							|  |  |  |         cursor: pointer; | 
					
						
							|  |  |  |         transition: all 0.15s ease; | 
					
						
							|  |  |  |         outline: none; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |        | 
					
						
							|  |  |  |       .language-selector:hover { | 
					
						
							|  |  |  |         background: ${cssManager.bdTheme('#f9fafb', '#1f2937')}; | 
					
						
							|  |  |  |         border-color: ${cssManager.bdTheme('#e5e7eb', '#374151')}; | 
					
						
							|  |  |  |         color: ${cssManager.bdTheme('#374151', '#e5e7eb')}; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |        | 
					
						
							|  |  |  |       .language-selector:focus { | 
					
						
							|  |  |  |         border-color: ${cssManager.bdTheme('#9ca3af', '#6b7280')}; | 
					
						
							| 
									
										
										
										
											2025-06-26 11:41:58 +00:00
										 |  |  |       } | 
					
						
							|  |  |  |        | 
					
						
							| 
									
										
										
										
											2025-06-26 12:00:35 +00:00
										 |  |  |       /* Copy Button - Minimal */ | 
					
						
							| 
									
										
										
										
											2025-06-26 11:57:04 +00:00
										 |  |  |       .copy-button { | 
					
						
							|  |  |  |         display: flex; | 
					
						
							|  |  |  |         align-items: center; | 
					
						
							| 
									
										
										
										
											2025-06-26 12:00:35 +00:00
										 |  |  |         gap: 4px; | 
					
						
							|  |  |  |         padding: 4px 8px; | 
					
						
							| 
									
										
										
										
											2025-06-26 11:57:04 +00:00
										 |  |  |         background: transparent; | 
					
						
							| 
									
										
										
										
											2025-06-26 12:00:35 +00:00
										 |  |  |         border: 1px solid transparent; | 
					
						
							|  |  |  |         border-radius: 4px; | 
					
						
							|  |  |  |         color: ${cssManager.bdTheme('#6b7280', '#9ca3af')}; | 
					
						
							| 
									
										
										
										
											2025-06-26 11:57:04 +00:00
										 |  |  |         font-size: 12px; | 
					
						
							| 
									
										
										
										
											2025-06-27 17:32:01 +00:00
										 |  |  |         font-family: ${cssGeistFontFamily}; | 
					
						
							| 
									
										
										
										
											2025-06-26 11:57:04 +00:00
										 |  |  |         cursor: pointer; | 
					
						
							| 
									
										
										
										
											2025-06-26 12:00:35 +00:00
										 |  |  |         transition: all 0.15s ease; | 
					
						
							| 
									
										
										
										
											2025-06-26 11:57:04 +00:00
										 |  |  |         outline: none; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |        | 
					
						
							|  |  |  |       .copy-button:hover { | 
					
						
							| 
									
										
										
										
											2025-06-26 12:00:35 +00:00
										 |  |  |         background: ${cssManager.bdTheme('#f9fafb', '#1f2937')}; | 
					
						
							|  |  |  |         border-color: ${cssManager.bdTheme('#e5e7eb', '#374151')}; | 
					
						
							|  |  |  |         color: ${cssManager.bdTheme('#374151', '#e5e7eb')}; | 
					
						
							| 
									
										
										
										
											2025-06-26 11:57:04 +00:00
										 |  |  |       } | 
					
						
							|  |  |  |        | 
					
						
							|  |  |  |       .copy-button:active { | 
					
						
							| 
									
										
										
										
											2025-06-26 12:00:35 +00:00
										 |  |  |         transform: scale(0.98); | 
					
						
							| 
									
										
										
										
											2025-06-26 11:57:04 +00:00
										 |  |  |       } | 
					
						
							|  |  |  |        | 
					
						
							|  |  |  |       .copy-button.copied { | 
					
						
							| 
									
										
										
										
											2025-06-26 12:00:35 +00:00
										 |  |  |         color: ${cssManager.bdTheme('#059669', '#10b981')}; | 
					
						
							| 
									
										
										
										
											2025-06-26 11:57:04 +00:00
										 |  |  |       } | 
					
						
							|  |  |  |        | 
					
						
							|  |  |  |       .copy-icon { | 
					
						
							|  |  |  |         flex-shrink: 0; | 
					
						
							| 
									
										
										
										
											2025-06-26 12:00:35 +00:00
										 |  |  |         opacity: 0.7; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |        | 
					
						
							|  |  |  |       .copy-button:hover .copy-icon { | 
					
						
							|  |  |  |         opacity: 1; | 
					
						
							| 
									
										
										
										
											2025-06-26 11:57:04 +00:00
										 |  |  |       } | 
					
						
							|  |  |  |        | 
					
						
							|  |  |  |       .copy-text { | 
					
						
							| 
									
										
										
										
											2025-06-26 12:00:35 +00:00
										 |  |  |         min-width: 40px; | 
					
						
							| 
									
										
										
										
											2025-06-26 11:57:04 +00:00
										 |  |  |         text-align: center; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |        | 
					
						
							| 
									
										
										
										
											2025-06-26 11:41:58 +00:00
										 |  |  |       /* Code Body */ | 
					
						
							|  |  |  |       .code-body { | 
					
						
							|  |  |  |         display: flex; | 
					
						
							|  |  |  |         position: relative; | 
					
						
							| 
									
										
										
										
											2025-06-26 12:00:35 +00:00
										 |  |  |         background: ${cssManager.bdTheme('#fafafa', '#0a0a0a')}; | 
					
						
							| 
									
										
										
										
											2025-06-26 11:41:58 +00:00
										 |  |  |       } | 
					
						
							|  |  |  |        | 
					
						
							| 
									
										
										
										
											2025-06-26 12:00:35 +00:00
										 |  |  |       /* Line Numbers - Subtle */ | 
					
						
							| 
									
										
										
										
											2025-06-26 11:41:58 +00:00
										 |  |  |       .line-numbers { | 
					
						
							|  |  |  |         flex-shrink: 0; | 
					
						
							| 
									
										
										
										
											2025-06-26 12:00:35 +00:00
										 |  |  |         padding: 12px 0; | 
					
						
							|  |  |  |         background: transparent; | 
					
						
							| 
									
										
										
										
											2025-06-26 11:41:58 +00:00
										 |  |  |         text-align: right; | 
					
						
							|  |  |  |         user-select: none; | 
					
						
							| 
									
										
										
										
											2025-06-26 12:00:35 +00:00
										 |  |  |         min-width: 40px; | 
					
						
							|  |  |  |         border-right: 1px solid ${cssManager.bdTheme('#e5e7eb', '#374151')}; | 
					
						
							| 
									
										
										
										
											2025-06-26 11:41:58 +00:00
										 |  |  |       } | 
					
						
							|  |  |  |        | 
					
						
							|  |  |  |       .line-number { | 
					
						
							| 
									
										
										
										
											2025-06-26 12:00:35 +00:00
										 |  |  |         padding: 0 12px 0 8px; | 
					
						
							|  |  |  |         color: ${cssManager.bdTheme('#9ca3af', '#4b5563')}; | 
					
						
							| 
									
										
										
										
											2025-06-27 17:32:01 +00:00
										 |  |  |         font-family: ${cssMonoFontFamily}; | 
					
						
							| 
									
										
										
										
											2025-06-26 12:00:35 +00:00
										 |  |  |         font-size: 13px; | 
					
						
							|  |  |  |         line-height: 20px; | 
					
						
							|  |  |  |         height: 20px; | 
					
						
							| 
									
										
										
										
											2025-06-26 11:41:58 +00:00
										 |  |  |       } | 
					
						
							|  |  |  |        | 
					
						
							|  |  |  |       /* Code Content */ | 
					
						
							|  |  |  |       .code-content { | 
					
						
							|  |  |  |         flex: 1; | 
					
						
							|  |  |  |         overflow-x: auto; | 
					
						
							|  |  |  |         position: relative; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |        | 
					
						
							|  |  |  |       .code-pre { | 
					
						
							|  |  |  |         margin: 0; | 
					
						
							|  |  |  |         padding: 0; | 
					
						
							|  |  |  |         background: transparent; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |        | 
					
						
							|  |  |  |       .code-editor { | 
					
						
							|  |  |  |         display: block; | 
					
						
							| 
									
										
										
										
											2025-06-26 12:00:35 +00:00
										 |  |  |         padding: 12px 16px; | 
					
						
							| 
									
										
										
										
											2025-06-26 11:41:58 +00:00
										 |  |  |         margin: 0; | 
					
						
							| 
									
										
										
										
											2025-06-27 17:32:01 +00:00
										 |  |  |         font-family: ${cssMonoFontFamily}; | 
					
						
							| 
									
										
										
										
											2025-06-26 12:00:35 +00:00
										 |  |  |         font-size: 13px; | 
					
						
							|  |  |  |         line-height: 20px; | 
					
						
							|  |  |  |         color: ${cssManager.bdTheme('#111827', '#f9fafb')}; | 
					
						
							| 
									
										
										
										
											2025-06-26 11:41:58 +00:00
										 |  |  |         background: transparent; | 
					
						
							|  |  |  |         border: none; | 
					
						
							|  |  |  |         outline: none; | 
					
						
							|  |  |  |         white-space: pre-wrap; | 
					
						
							|  |  |  |         word-wrap: break-word; | 
					
						
							| 
									
										
										
										
											2025-06-26 12:00:35 +00:00
										 |  |  |         min-height: 60px; | 
					
						
							| 
									
										
										
										
											2025-06-26 11:41:58 +00:00
										 |  |  |         overflow: visible; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |        | 
					
						
							|  |  |  |       /* Placeholder */ | 
					
						
							|  |  |  |       .code-editor:empty::before { | 
					
						
							|  |  |  |         content: "// Type or paste code here..."; | 
					
						
							| 
									
										
										
										
											2025-06-26 12:00:35 +00:00
										 |  |  |         color: ${cssManager.bdTheme('#9ca3af', '#4b5563')}; | 
					
						
							| 
									
										
										
										
											2025-06-26 11:41:58 +00:00
										 |  |  |         pointer-events: none; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |        | 
					
						
							| 
									
										
										
										
											2025-06-26 13:38:09 +00:00
										 |  |  |       /* When editing (focused), show grey text without highlighting */ | 
					
						
							|  |  |  |       .code-block-container.editing .code-editor { | 
					
						
							|  |  |  |         color: ${cssManager.bdTheme('#6b7280', '#9ca3af')} !important; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |        | 
					
						
							|  |  |  |       .code-block-container.editing .code-editor * { | 
					
						
							|  |  |  |         color: inherit !important; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |        | 
					
						
							| 
									
										
										
										
											2025-06-26 12:00:35 +00:00
										 |  |  |       /* Syntax Highlighting - Muted colors */ | 
					
						
							| 
									
										
										
										
											2025-06-26 11:41:58 +00:00
										 |  |  |       .code-editor .hljs-keyword { | 
					
						
							| 
									
										
										
										
											2025-06-26 12:00:35 +00:00
										 |  |  |         color: ${cssManager.bdTheme('#dc2626', '#f87171')}; | 
					
						
							|  |  |  |         font-weight: 500; | 
					
						
							| 
									
										
										
										
											2025-06-26 11:41:58 +00:00
										 |  |  |       } | 
					
						
							|  |  |  |        | 
					
						
							|  |  |  |       .code-editor .hljs-string { | 
					
						
							| 
									
										
										
										
											2025-06-26 12:00:35 +00:00
										 |  |  |         color: ${cssManager.bdTheme('#059669', '#10b981')}; | 
					
						
							| 
									
										
										
										
											2025-06-26 11:41:58 +00:00
										 |  |  |       } | 
					
						
							|  |  |  |        | 
					
						
							|  |  |  |       .code-editor .hljs-number { | 
					
						
							| 
									
										
										
										
											2025-06-26 12:00:35 +00:00
										 |  |  |         color: ${cssManager.bdTheme('#7c3aed', '#a78bfa')}; | 
					
						
							| 
									
										
										
										
											2025-06-26 11:41:58 +00:00
										 |  |  |       } | 
					
						
							|  |  |  |        | 
					
						
							|  |  |  |       .code-editor .hljs-function { | 
					
						
							| 
									
										
										
										
											2025-06-26 12:00:35 +00:00
										 |  |  |         color: ${cssManager.bdTheme('#2563eb', '#60a5fa')}; | 
					
						
							| 
									
										
										
										
											2025-06-26 11:41:58 +00:00
										 |  |  |       } | 
					
						
							|  |  |  |        | 
					
						
							|  |  |  |       .code-editor .hljs-comment { | 
					
						
							| 
									
										
										
										
											2025-06-26 12:00:35 +00:00
										 |  |  |         color: ${cssManager.bdTheme('#6b7280', '#6b7280')}; | 
					
						
							| 
									
										
										
										
											2025-06-26 11:41:58 +00:00
										 |  |  |         font-style: italic; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |        | 
					
						
							|  |  |  |       .code-editor .hljs-variable, | 
					
						
							|  |  |  |       .code-editor .hljs-attr { | 
					
						
							| 
									
										
										
										
											2025-06-26 12:00:35 +00:00
										 |  |  |         color: ${cssManager.bdTheme('#ea580c', '#fb923c')}; | 
					
						
							| 
									
										
										
										
											2025-06-26 11:41:58 +00:00
										 |  |  |       } | 
					
						
							|  |  |  |        | 
					
						
							|  |  |  |       .code-editor .hljs-class, | 
					
						
							|  |  |  |       .code-editor .hljs-title { | 
					
						
							| 
									
										
										
										
											2025-06-26 12:00:35 +00:00
										 |  |  |         color: ${cssManager.bdTheme('#2563eb', '#60a5fa')}; | 
					
						
							|  |  |  |         font-weight: 500; | 
					
						
							| 
									
										
										
										
											2025-06-26 11:41:58 +00:00
										 |  |  |       } | 
					
						
							|  |  |  |        | 
					
						
							|  |  |  |       .code-editor .hljs-params { | 
					
						
							| 
									
										
										
										
											2025-06-26 12:00:35 +00:00
										 |  |  |         color: ${cssManager.bdTheme('#374151', '#e5e7eb')}; | 
					
						
							| 
									
										
										
										
											2025-06-26 11:41:58 +00:00
										 |  |  |       } | 
					
						
							|  |  |  |        | 
					
						
							|  |  |  |       .code-editor .hljs-built_in { | 
					
						
							| 
									
										
										
										
											2025-06-26 12:00:35 +00:00
										 |  |  |         color: ${cssManager.bdTheme('#7c3aed', '#a78bfa')}; | 
					
						
							| 
									
										
										
										
											2025-06-26 11:41:58 +00:00
										 |  |  |       } | 
					
						
							|  |  |  |        | 
					
						
							|  |  |  |       .code-editor .hljs-literal { | 
					
						
							| 
									
										
										
										
											2025-06-26 12:00:35 +00:00
										 |  |  |         color: ${cssManager.bdTheme('#7c3aed', '#a78bfa')}; | 
					
						
							| 
									
										
										
										
											2025-06-26 11:41:58 +00:00
										 |  |  |       } | 
					
						
							|  |  |  |        | 
					
						
							|  |  |  |       .code-editor .hljs-meta { | 
					
						
							| 
									
										
										
										
											2025-06-26 12:00:35 +00:00
										 |  |  |         color: ${cssManager.bdTheme('#6b7280', '#9ca3af')}; | 
					
						
							| 
									
										
										
										
											2025-06-26 11:41:58 +00:00
										 |  |  |       } | 
					
						
							|  |  |  |        | 
					
						
							|  |  |  |       .code-editor .hljs-punctuation { | 
					
						
							| 
									
										
										
										
											2025-06-26 12:00:35 +00:00
										 |  |  |         color: ${cssManager.bdTheme('#374151', '#d1d5db')}; | 
					
						
							| 
									
										
										
										
											2025-06-26 11:41:58 +00:00
										 |  |  |       } | 
					
						
							|  |  |  |        | 
					
						
							|  |  |  |       .code-editor .hljs-tag { | 
					
						
							| 
									
										
										
										
											2025-06-26 12:00:35 +00:00
										 |  |  |         color: ${cssManager.bdTheme('#dc2626', '#f87171')}; | 
					
						
							| 
									
										
										
										
											2025-06-26 11:41:58 +00:00
										 |  |  |       } | 
					
						
							|  |  |  |        | 
					
						
							|  |  |  |       .code-editor .hljs-attribute { | 
					
						
							| 
									
										
										
										
											2025-06-26 12:00:35 +00:00
										 |  |  |         color: ${cssManager.bdTheme('#2563eb', '#60a5fa')}; | 
					
						
							| 
									
										
										
										
											2025-06-26 11:41:58 +00:00
										 |  |  |       } | 
					
						
							|  |  |  |        | 
					
						
							|  |  |  |       .code-editor .hljs-selector-tag { | 
					
						
							| 
									
										
										
										
											2025-06-26 12:00:35 +00:00
										 |  |  |         color: ${cssManager.bdTheme('#dc2626', '#f87171')}; | 
					
						
							| 
									
										
										
										
											2025-06-26 11:41:58 +00:00
										 |  |  |       } | 
					
						
							|  |  |  |        | 
					
						
							|  |  |  |       .code-editor .hljs-selector-class { | 
					
						
							| 
									
										
										
										
											2025-06-26 12:00:35 +00:00
										 |  |  |         color: ${cssManager.bdTheme('#2563eb', '#60a5fa')}; | 
					
						
							| 
									
										
										
										
											2025-06-26 11:41:58 +00:00
										 |  |  |       } | 
					
						
							|  |  |  |        | 
					
						
							|  |  |  |       .code-editor .hljs-selector-id { | 
					
						
							| 
									
										
										
										
											2025-06-26 12:00:35 +00:00
										 |  |  |         color: ${cssManager.bdTheme('#7c3aed', '#a78bfa')}; | 
					
						
							| 
									
										
										
										
											2025-06-26 11:41:58 +00:00
										 |  |  |       } | 
					
						
							|  |  |  |        | 
					
						
							|  |  |  |       /* Selection */ | 
					
						
							|  |  |  |       .code-editor::selection, | 
					
						
							|  |  |  |       .code-editor *::selection { | 
					
						
							| 
									
										
										
										
											2025-06-26 12:00:35 +00:00
										 |  |  |         background: ${cssManager.bdTheme('rgba(99, 102, 241, 0.2)', 'rgba(99, 102, 241, 0.3)')}; | 
					
						
							| 
									
										
										
										
											2025-06-26 11:41:58 +00:00
										 |  |  |       } | 
					
						
							|  |  |  |        | 
					
						
							| 
									
										
										
										
											2025-06-26 12:00:35 +00:00
										 |  |  |       /* Scrollbar styling - Minimal */ | 
					
						
							| 
									
										
										
										
											2025-06-26 11:41:58 +00:00
										 |  |  |       .code-content::-webkit-scrollbar { | 
					
						
							| 
									
										
										
										
											2025-06-26 12:00:35 +00:00
										 |  |  |         height: 6px; | 
					
						
							| 
									
										
										
										
											2025-06-26 11:41:58 +00:00
										 |  |  |       } | 
					
						
							|  |  |  |        | 
					
						
							|  |  |  |       .code-content::-webkit-scrollbar-track { | 
					
						
							| 
									
										
										
										
											2025-06-26 12:00:35 +00:00
										 |  |  |         background: transparent; | 
					
						
							| 
									
										
										
										
											2025-06-26 11:41:58 +00:00
										 |  |  |       } | 
					
						
							|  |  |  |        | 
					
						
							|  |  |  |       .code-content::-webkit-scrollbar-thumb { | 
					
						
							| 
									
										
										
										
											2025-06-26 12:00:35 +00:00
										 |  |  |         background: ${cssManager.bdTheme('#d1d5db', '#4b5563')}; | 
					
						
							|  |  |  |         border-radius: 3px; | 
					
						
							| 
									
										
										
										
											2025-06-26 11:41:58 +00:00
										 |  |  |       } | 
					
						
							|  |  |  |        | 
					
						
							|  |  |  |       .code-content::-webkit-scrollbar-thumb:hover { | 
					
						
							| 
									
										
										
										
											2025-06-26 12:00:35 +00:00
										 |  |  |         background: ${cssManager.bdTheme('#9ca3af', '#6b7280')}; | 
					
						
							| 
									
										
										
										
											2025-06-26 11:41:58 +00:00
										 |  |  |       } | 
					
						
							|  |  |  |     `;
 | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2025-06-24 22:45:50 +00:00
										 |  |  | } |