diff --git a/ts_web/elements/wysiwyg/blocks/text/code.block.old.ts b/ts_web/elements/wysiwyg/blocks/text/code.block.old.ts
new file mode 100644
index 0000000..5679638
--- /dev/null
+++ b/ts_web/elements/wysiwyg/blocks/text/code.block.old.ts
@@ -0,0 +1,608 @@
+import { BaseBlockHandler, type IBlockEventHandlers } from '../block.base.js';
+import type { IBlock } from '../../wysiwyg.types.js';
+import { cssManager } from '@design.estate/dees-element';
+import { WysiwygBlocks } from '../../wysiwyg.blocks.js';
+import { WysiwygSelection } from '../../wysiwyg.selection.js';
+import hlight from 'highlight.js';
+
+export class CodeBlockHandler extends BaseBlockHandler {
+ type = 'code';
+
+ // Track cursor position
+ private lastKnownCursorPosition: number = 0;
+
+ // Debounce timer for highlighting
+ private highlightingTimer: any = null;
+
+ render(block: IBlock, isSelected: boolean): string {
+ const language = block.metadata?.language || 'javascript';
+ const selectedClass = isSelected ? ' selected' : '';
+
+ return `
+
-
${language}
-
+
+
+
+
${lineNumbersHtml}
+
+
${this.escapeHtml(content)}
+
+
`;
}
setup(element: HTMLElement, block: IBlock, handlers: IBlockEventHandlers): void {
- const codeBlock = element.querySelector('.block.code') as HTMLDivElement;
- if (!codeBlock) {
- console.error('CodeBlockHandler.setup: No code block element found');
- return;
- }
+ const editor = element.querySelector('.code-editor') as HTMLElement;
+ const container = element.querySelector('.code-block-container') as HTMLElement;
- // Set initial content if needed - use textContent for code blocks
- if (block.content && !codeBlock.textContent) {
- codeBlock.textContent = block.content;
- }
+ if (!editor || !container) return;
+
+ // Track if we're currently editing
+ let isEditing = false;
+
+ // Focus handler
+ editor.addEventListener('focus', () => {
+ isEditing = true;
+ container.classList.add('editing');
+ handlers.onFocus();
+ });
+
+ // Blur handler
+ editor.addEventListener('blur', () => {
+ isEditing = false;
+ container.classList.remove('editing');
+ // Apply final highlighting on blur
+ this.applyHighlighting(element, block);
+ handlers.onBlur();
+ });
// Input handler
- codeBlock.addEventListener('input', (e) => {
+ editor.addEventListener('input', (e) => {
handlers.onInput(e as InputEvent);
- // Track cursor position after input
- const pos = this.getCursorPosition(element);
- if (pos !== null) {
- this.lastKnownCursorPosition = pos;
+ // Update line numbers
+ this.updateLineNumbers(element);
+
+ // Clear any pending highlight
+ clearTimeout(this.highlightTimer);
+
+ // Schedule highlighting (only if not actively editing)
+ if (!isEditing) {
+ this.highlightTimer = setTimeout(() => {
+ this.applyHighlighting(element, block);
+ }, 500);
}
});
// Keydown handler
- codeBlock.addEventListener('keydown', (e) => {
- // Track cursor position before keydown
- const pos = this.getCursorPosition(element);
- if (pos !== null) {
- this.lastKnownCursorPosition = pos;
- }
-
- // Special handling for Tab key in code blocks
+ editor.addEventListener('keydown', (e) => {
+ // Handle Tab
if (e.key === 'Tab') {
e.preventDefault();
- // Insert two spaces for tab
const selection = window.getSelection();
if (selection && selection.rangeCount > 0) {
const range = selection.getRangeAt(0);
- range.deleteContents();
const textNode = document.createTextNode(' ');
range.insertNode(textNode);
range.setStartAfter(textNode);
range.setEndAfter(textNode);
selection.removeAllRanges();
selection.addRange(range);
-
- // Trigger input event
handlers.onInput(new InputEvent('input'));
+ this.updateLineNumbers(element);
}
return;
}
@@ -83,54 +114,8 @@ export class CodeBlockHandler extends BaseBlockHandler {
handlers.onKeyDown(e);
});
- // Focus handler
- codeBlock.addEventListener('focus', () => {
- handlers.onFocus();
- });
-
- // Blur handler
- codeBlock.addEventListener('blur', () => {
- handlers.onBlur();
- });
-
- // Composition handlers for IME support
- codeBlock.addEventListener('compositionstart', () => {
- handlers.onCompositionStart();
- });
-
- codeBlock.addEventListener('compositionend', () => {
- handlers.onCompositionEnd();
- });
-
- // Mouse up handler
- codeBlock.addEventListener('mouseup', (e) => {
- const pos = this.getCursorPosition(element);
- if (pos !== null) {
- this.lastKnownCursorPosition = pos;
- }
- handlers.onMouseUp?.(e);
- });
-
- // Click handler with delayed cursor tracking
- codeBlock.addEventListener('click', (e: MouseEvent) => {
- setTimeout(() => {
- const pos = this.getCursorPosition(element);
- if (pos !== null) {
- this.lastKnownCursorPosition = pos;
- }
- }, 0);
- });
-
- // Keyup handler for cursor tracking
- codeBlock.addEventListener('keyup', (e) => {
- const pos = this.getCursorPosition(element);
- if (pos !== null) {
- this.lastKnownCursorPosition = pos;
- }
- });
-
- // Paste handler - handle as plain text
- codeBlock.addEventListener('paste', (e) => {
+ // Paste handler - plain text only
+ editor.addEventListener('paste', (e) => {
e.preventDefault();
const text = e.clipboardData?.getData('text/plain');
if (text) {
@@ -144,257 +129,373 @@ export class CodeBlockHandler extends BaseBlockHandler {
range.setEndAfter(textNode);
selection.removeAllRanges();
selection.addRange(range);
-
- // Trigger input event
handlers.onInput(new InputEvent('input'));
+ this.updateLineNumbers(element);
}
}
});
+
+ // Composition handlers
+ editor.addEventListener('compositionstart', () => handlers.onCompositionStart());
+ editor.addEventListener('compositionend', () => handlers.onCompositionEnd());
+
+ // Initial syntax highlighting if content exists
+ if (block.content && !isEditing) {
+ requestAnimationFrame(() => {
+ this.applyHighlighting(element, block);
+ });
+ }
+ }
+
+ 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 += `
${i}
`;
+ }
+
+ lineNumbersContainer.innerHTML = lineNumbersHtml;
+ }
+
+ 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 || '';
+ const language = block.metadata?.language || 'javascript';
+
+ // Apply highlighting
+ try {
+ const result = hlight.highlight(content, {
+ language: language,
+ ignoreIllegals: true
+ });
+
+ // 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);
+ });
+ }
+ }
+ } catch (error) {
+ // If highlighting fails, keep plain text
+ console.warn('Syntax highlighting failed:', error);
+ }
+ }
+
+ private escapeHtml(text: string): string {
+ const div = document.createElement('div');
+ div.textContent = text;
+ return div.innerHTML;
+ }
+
+ getContent(element: HTMLElement): string {
+ const editor = element.querySelector('.code-editor') as HTMLElement;
+ return editor?.textContent || '';
+ }
+
+ 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: {
+ language: element.querySelector('.code-block-container')?.getAttribute('data-language') || 'javascript'
+ }
+ };
+ this.applyHighlighting(element, block);
+ }
+ }
+
+ getCursorPosition(element: HTMLElement): number | null {
+ const editor = element.querySelector('.code-editor') as HTMLElement;
+ if (!editor) return null;
+
+ const selection = window.getSelection();
+ if (!selection || selection.rangeCount === 0) return null;
+
+ const range = selection.getRangeAt(0);
+ if (!editor.contains(range.startContainer)) return null;
+
+ const preCaretRange = document.createRange();
+ preCaretRange.selectNodeContents(editor);
+ preCaretRange.setEnd(range.startContainer, range.startOffset);
+
+ return preCaretRange.toString().length;
+ }
+
+ setCursorToStart(element: HTMLElement): void {
+ const editor = element.querySelector('.code-editor') as HTMLElement;
+ if (editor) {
+ WysiwygSelection.setCursorPosition(editor, 0);
+ }
+ }
+
+ setCursorToEnd(element: HTMLElement): void {
+ const editor = element.querySelector('.code-editor') as HTMLElement;
+ if (editor) {
+ const length = editor.textContent?.length || 0;
+ WysiwygSelection.setCursorPosition(editor, length);
+ }
+ }
+
+ focus(element: HTMLElement): void {
+ const editor = element.querySelector('.code-editor') as HTMLElement;
+ editor?.focus();
+ }
+
+ focusWithCursor(element: HTMLElement, position: 'start' | 'end' | number = 'end'): void {
+ const editor = element.querySelector('.code-editor') as HTMLElement;
+ if (!editor) return;
+
+ editor.focus();
+
+ requestAnimationFrame(() => {
+ if (position === 'start') {
+ this.setCursorToStart(element);
+ } else if (position === 'end') {
+ this.setCursorToEnd(element);
+ } else if (typeof position === 'number') {
+ WysiwygSelection.setCursorPosition(editor, position);
+ }
+ });
+ }
+
+ 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)
+ };
}
getStyles(): string {
return `
- /* Code block specific styles */
+ /* Code Block Container */
.code-block-container {
position: relative;
- margin: 20px 0;
+ margin: 16px 0;
+ background: ${cssManager.bdTheme('#f6f8fa', '#0d1117')};
+ border: 1px solid ${cssManager.bdTheme('#d1d5da', '#30363d')};
+ border-radius: 8px;
+ overflow: hidden;
+ transition: border-color 0.2s ease, box-shadow 0.2s ease;
}
- .block.code {
- font-family: 'SF Mono', 'Monaco', 'Inconsolata', 'Fira Code', monospace;
- font-size: 14px;
- background: ${cssManager.bdTheme('#f8f8f8', '#0d0d0d')};
- border: 1px solid ${cssManager.bdTheme('#e0e0e0', '#2a2a2a')};
- padding: 16px 20px;
- padding-top: 32px;
- border-radius: 6px;
- white-space: pre-wrap;
- color: ${cssManager.bdTheme('#24292e', '#e1e4e8')};
- line-height: 1.5;
- overflow-x: auto;
- margin: 0;
+ .code-block-container.selected {
+ border-color: ${cssManager.bdTheme('#0969da', '#58a6ff')};
+ box-shadow: 0 0 0 3px ${cssManager.bdTheme('rgba(9, 105, 218, 0.15)', 'rgba(88, 166, 255, 0.15)')};
}
- .code-language {
- position: absolute;
- top: 0;
- right: 0;
- background: ${cssManager.bdTheme('#e1e4e8', '#333333')};
- color: ${cssManager.bdTheme('#586069', '#8b949e')};
- padding: 4px 12px;
+ .code-block-container.editing {
+ border-color: ${cssManager.bdTheme('#0969da', '#58a6ff')};
+ }
+
+ /* Header */
+ .code-header {
+ background: ${cssManager.bdTheme('#f6f8fa', '#161b22')};
+ border-bottom: 1px solid ${cssManager.bdTheme('#d1d5da', '#30363d')};
+ padding: 8px 16px;
+ display: flex;
+ justify-content: flex-end;
+ align-items: center;
+ }
+
+ .language-label {
font-size: 12px;
- border-radius: 0 6px 0 6px;
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
- text-transform: lowercase;
- z-index: 1;
+ color: ${cssManager.bdTheme('#57606a', '#8b949e')};
+ background: ${cssManager.bdTheme('#ffffff', '#0d1117')};
+ padding: 2px 8px;
+ border-radius: 4px;
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
+ }
+
+ /* Code Body */
+ .code-body {
+ display: flex;
+ position: relative;
+ }
+
+ /* Line Numbers */
+ .line-numbers {
+ flex-shrink: 0;
+ padding: 16px 0;
+ background: ${cssManager.bdTheme('#f6f8fa', '#010409')};
+ border-right: 1px solid ${cssManager.bdTheme('#d1d5da', '#30363d')};
+ text-align: right;
+ user-select: none;
+ min-width: 50px;
+ }
+
+ .line-number {
+ padding: 0 12px;
+ color: ${cssManager.bdTheme('#57606a', '#484f58')};
+ font-family: 'SF Mono', Monaco, Consolas, monospace;
+ font-size: 14px;
+ line-height: 21px;
+ height: 21px;
+ }
+
+ /* Code Content */
+ .code-content {
+ flex: 1;
+ overflow-x: auto;
+ position: relative;
+ }
+
+ .code-pre {
+ margin: 0;
+ padding: 0;
+ background: transparent;
+ }
+
+ .code-editor {
+ display: block;
+ padding: 16px;
+ margin: 0;
+ font-family: 'SF Mono', Monaco, Consolas, monospace;
+ font-size: 14px;
+ line-height: 21px;
+ color: ${cssManager.bdTheme('#24292f', '#c9d1d9')};
+ background: transparent;
+ border: none;
+ outline: none;
+ white-space: pre-wrap;
+ word-wrap: break-word;
+ min-height: 84px;
+ overflow: visible;
+ }
+
+ /* Placeholder */
+ .code-editor:empty::before {
+ content: "// Type or paste code here...";
+ color: ${cssManager.bdTheme('#6a737d', '#484f58')};
+ pointer-events: none;
+ }
+
+ /* Syntax Highlighting Colors */
+ .code-editor .hljs-keyword {
+ color: ${cssManager.bdTheme('#d73a49', '#ff7b72')};
+ }
+
+ .code-editor .hljs-string {
+ color: ${cssManager.bdTheme('#032f62', '#a5d6ff')};
+ }
+
+ .code-editor .hljs-number {
+ color: ${cssManager.bdTheme('#005cc5', '#79c0ff')};
+ }
+
+ .code-editor .hljs-function {
+ color: ${cssManager.bdTheme('#6f42c1', '#d2a8ff')};
+ }
+
+ .code-editor .hljs-comment {
+ color: ${cssManager.bdTheme('#6a737d', '#8b949e')};
+ font-style: italic;
+ }
+
+ .code-editor .hljs-variable,
+ .code-editor .hljs-attr {
+ color: ${cssManager.bdTheme('#e36209', '#ffa657')};
+ }
+
+ .code-editor .hljs-class,
+ .code-editor .hljs-title {
+ color: ${cssManager.bdTheme('#6f42c1', '#d2a8ff')};
+ }
+
+ .code-editor .hljs-params {
+ color: ${cssManager.bdTheme('#24292f', '#c9d1d9')};
+ }
+
+ .code-editor .hljs-built_in {
+ color: ${cssManager.bdTheme('#005cc5', '#79c0ff')};
+ }
+
+ .code-editor .hljs-literal {
+ color: ${cssManager.bdTheme('#005cc5', '#79c0ff')};
+ }
+
+ .code-editor .hljs-meta {
+ color: ${cssManager.bdTheme('#735c0f', '#f2cc60')};
+ }
+
+ .code-editor .hljs-punctuation {
+ color: ${cssManager.bdTheme('#24292f', '#c9d1d9')};
+ }
+
+ .code-editor .hljs-tag {
+ color: ${cssManager.bdTheme('#22863a', '#7ee83f')};
+ }
+
+ .code-editor .hljs-attribute {
+ color: ${cssManager.bdTheme('#6f42c1', '#d2a8ff')};
+ }
+
+ .code-editor .hljs-selector-tag {
+ color: ${cssManager.bdTheme('#22863a', '#7ee83f')};
+ }
+
+ .code-editor .hljs-selector-class {
+ color: ${cssManager.bdTheme('#6f42c1', '#d2a8ff')};
+ }
+
+ .code-editor .hljs-selector-id {
+ color: ${cssManager.bdTheme('#005cc5', '#79c0ff')};
+ }
+
+ /* Selection */
+ .code-editor::selection,
+ .code-editor *::selection {
+ background: ${cssManager.bdTheme('rgba(9, 105, 218, 0.3)', 'rgba(88, 166, 255, 0.3)')};
+ }
+
+ /* Scrollbar styling */
+ .code-content::-webkit-scrollbar {
+ height: 8px;
+ }
+
+ .code-content::-webkit-scrollbar-track {
+ background: ${cssManager.bdTheme('#f6f8fa', '#010409')};
+ }
+
+ .code-content::-webkit-scrollbar-thumb {
+ background: ${cssManager.bdTheme('#d1d5da', '#30363d')};
+ border-radius: 4px;
+ }
+
+ .code-content::-webkit-scrollbar-thumb:hover {
+ background: ${cssManager.bdTheme('#c8c8c8', '#484f58')};
}
`;
}
-
- getPlaceholder(): string {
- return '';
- }
-
- // Helper methods for code functionality
-
- getCursorPosition(element: HTMLElement, context?: any): number | null {
- // Get the actual code element
- const codeBlock = element.querySelector('.block.code') as HTMLDivElement;
- if (!codeBlock) {
- return null;
- }
-
- // Get shadow roots from context
- const wysiwygBlock = context?.component;
- const parentComponent = wysiwygBlock?.closest('dees-input-wysiwyg');
- const parentShadowRoot = parentComponent?.shadowRoot;
- const blockShadowRoot = context?.shadowRoot;
-
- // Get selection info with both shadow roots for proper traversal
- const shadowRoots: ShadowRoot[] = [];
- if (parentShadowRoot) shadowRoots.push(parentShadowRoot);
- if (blockShadowRoot) shadowRoots.push(blockShadowRoot);
-
- const selectionInfo = WysiwygSelection.getSelectionInfo(...shadowRoots);
-
- if (!selectionInfo) {
- return null;
- }
-
- if (!WysiwygSelection.containsAcrossShadowDOM(codeBlock, selectionInfo.startContainer)) {
- return null;
- }
-
- // Create a range from start of element to cursor position
- const preCaretRange = document.createRange();
- preCaretRange.selectNodeContents(codeBlock);
- preCaretRange.setEnd(selectionInfo.startContainer, selectionInfo.startOffset);
-
- // Get the text content length up to cursor
- const position = preCaretRange.toString().length;
-
- return position;
- }
-
- getContent(element: HTMLElement, context?: any): string {
- const codeBlock = element.querySelector('.block.code') as HTMLDivElement;
- if (!codeBlock) return '';
-
- // For code blocks, get textContent to avoid HTML formatting
- const content = codeBlock.textContent || '';
- return content;
- }
-
- setContent(element: HTMLElement, content: string, context?: any): void {
- const codeBlock = element.querySelector('.block.code') as HTMLDivElement;
- if (!codeBlock) return;
-
- // Store if we have focus
- const hadFocus = document.activeElement === codeBlock ||
- element.shadowRoot?.activeElement === codeBlock;
-
- // Use textContent for code blocks
- codeBlock.textContent = content;
-
- // Restore focus if we had it
- if (hadFocus) {
- codeBlock.focus();
- }
- }
-
- setCursorToStart(element: HTMLElement, context?: any): void {
- const codeBlock = element.querySelector('.block.code') as HTMLDivElement;
- if (codeBlock) {
- WysiwygBlocks.setCursorToStart(codeBlock);
- }
- }
-
- setCursorToEnd(element: HTMLElement, context?: any): void {
- const codeBlock = element.querySelector('.block.code') as HTMLDivElement;
- if (codeBlock) {
- WysiwygBlocks.setCursorToEnd(codeBlock);
- }
- }
-
- focus(element: HTMLElement, context?: any): void {
- const codeBlock = element.querySelector('.block.code') as HTMLDivElement;
- if (!codeBlock) return;
-
- // Ensure the element is focusable
- if (!codeBlock.hasAttribute('contenteditable')) {
- codeBlock.setAttribute('contenteditable', 'true');
- }
-
- codeBlock.focus();
-
- // If focus failed, try again after a microtask
- if (document.activeElement !== codeBlock && element.shadowRoot?.activeElement !== codeBlock) {
- Promise.resolve().then(() => {
- codeBlock.focus();
- });
- }
- }
-
- focusWithCursor(element: HTMLElement, position: 'start' | 'end' | number = 'end', context?: any): void {
- const codeBlock = element.querySelector('.block.code') as HTMLDivElement;
- if (!codeBlock) return;
-
- // Ensure element is focusable first
- if (!codeBlock.hasAttribute('contenteditable')) {
- codeBlock.setAttribute('contenteditable', 'true');
- }
-
- // Focus the element
- codeBlock.focus();
-
- // Set cursor position after focus is established
- const setCursor = () => {
- if (position === 'start') {
- this.setCursorToStart(element, context);
- } else if (position === 'end') {
- this.setCursorToEnd(element, context);
- } else if (typeof position === 'number') {
- // Use the selection utility to set cursor position
- WysiwygSelection.setCursorPosition(codeBlock, position);
- }
- };
-
- // Ensure cursor is set after focus
- if (document.activeElement === codeBlock || element.shadowRoot?.activeElement === codeBlock) {
- setCursor();
- } else {
- // Wait for focus to be established
- Promise.resolve().then(() => {
- if (document.activeElement === codeBlock || element.shadowRoot?.activeElement === codeBlock) {
- setCursor();
- }
- });
- }
- }
-
- getSplitContent(element: HTMLElement, context?: any): { before: string; after: string } | null {
- const codeBlock = element.querySelector('.block.code') as HTMLDivElement;
- if (!codeBlock) {
- return null;
- }
-
- // Get shadow roots from context
- const wysiwygBlock = context?.component;
- const parentComponent = wysiwygBlock?.closest('dees-input-wysiwyg');
- const parentShadowRoot = parentComponent?.shadowRoot;
- const blockShadowRoot = context?.shadowRoot;
-
- // Get selection info with both shadow roots for proper traversal
- const shadowRoots: ShadowRoot[] = [];
- if (parentShadowRoot) shadowRoots.push(parentShadowRoot);
- if (blockShadowRoot) shadowRoots.push(blockShadowRoot);
-
- const selectionInfo = WysiwygSelection.getSelectionInfo(...shadowRoots);
-
- if (!selectionInfo) {
- // Try using last known cursor position
- if (this.lastKnownCursorPosition !== null) {
- const fullText = codeBlock.textContent || '';
- const pos = Math.min(this.lastKnownCursorPosition, fullText.length);
- return {
- before: fullText.substring(0, pos),
- after: fullText.substring(pos)
- };
- }
- return null;
- }
-
- // Make sure the selection is within this block
- if (!WysiwygSelection.containsAcrossShadowDOM(codeBlock, selectionInfo.startContainer)) {
- // Try using last known cursor position
- if (this.lastKnownCursorPosition !== null) {
- const fullText = codeBlock.textContent || '';
- const pos = Math.min(this.lastKnownCursorPosition, fullText.length);
- return {
- before: fullText.substring(0, pos),
- after: fullText.substring(pos)
- };
- }
- return null;
- }
-
- // Get cursor position
- const cursorPos = this.getCursorPosition(element, context);
-
- if (cursorPos === null || cursorPos === 0) {
- // If cursor is at start or can't determine position, move all content
- return {
- before: '',
- after: codeBlock.textContent || ''
- };
- }
-
- // For code blocks, split based on text content only
- const fullText = codeBlock.textContent || '';
-
- return {
- before: fullText.substring(0, cursorPos),
- after: fullText.substring(cursorPos)
- };
- }
}
\ No newline at end of file
diff --git a/ts_web/elements/wysiwyg/dees-wysiwyg-block.ts b/ts_web/elements/wysiwyg/dees-wysiwyg-block.ts
index 9d20b15..adcda5d 100644
--- a/ts_web/elements/wysiwyg/dees-wysiwyg-block.ts
+++ b/ts_web/elements/wysiwyg/dees-wysiwyg-block.ts
@@ -48,12 +48,12 @@ export class DeesWysiwygBlock extends DeesElement {
private lastKnownCursorPosition: number = 0;
private lastSelectedText: string = '';
- private static handlerStylesInjected = false;
+ private handlerStylesInjected = false;
private injectHandlerStyles(): void {
- // Only inject once per component class
- if (DeesWysiwygBlock.handlerStylesInjected) return;
- DeesWysiwygBlock.handlerStylesInjected = true;
+ // Only inject once per instance
+ if (this.handlerStylesInjected) return;
+ this.handlerStylesInjected = true;
// Get styles from all registered block handlers
let styles = '';
@@ -131,20 +131,7 @@ export class DeesWysiwygBlock extends DeesElement {
margin: 16px 0;
}
- .block.code {
- font-family: 'SF Mono', 'Monaco', 'Inconsolata', 'Fira Code', monospace;
- font-size: 14px;
- background: ${cssManager.bdTheme('#f8f8f8', '#0d0d0d')};
- border: 1px solid ${cssManager.bdTheme('#e0e0e0', '#2a2a2a')};
- padding: 16px 20px;
- padding-top: 32px;
- border-radius: 6px;
- white-space: pre-wrap;
- color: ${cssManager.bdTheme('#24292e', '#e1e4e8')};
- line-height: 1.5;
- overflow-x: auto;
- margin: 20px 0;
- }
+ /* Code block styles moved to handler */
.block.list {
padding: 0;
@@ -200,24 +187,7 @@ export class DeesWysiwygBlock extends DeesElement {
border-bottom-color: ${cssManager.bdTheme('#0066cc', '#4d94ff')};
}
- .code-language {
- position: absolute;
- top: 0;
- right: 0;
- background: ${cssManager.bdTheme('#e1e4e8', '#333333')};
- color: ${cssManager.bdTheme('#586069', '#8b949e')};
- padding: 4px 12px;
- font-size: 12px;
- border-radius: 0 6px 0 6px;
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
- text-transform: lowercase;
- z-index: 1;
- }
-
- .code-block-container {
- position: relative;
- margin: 20px 0;
- }
+ /* Code block container and language styles moved to handler */
/* Selection styles */
.block ::selection {