feat: replace textarea with CodeMirror 6 editor

This commit is contained in:
2026-03-04 15:38:33 +05:30
parent 47f3b7557e
commit 233225f12c
4 changed files with 234 additions and 297 deletions
+4 -2
View File
@@ -1297,12 +1297,14 @@
<div class="tab-content active" id="tab-content-1" data-tab-id="1"> <div class="tab-content active" id="tab-content-1" data-tab-id="1">
<div id="editor-pane-1" class="pane"> <div id="editor-pane-1" class="pane">
<div class="editor-wrapper"> <div class="editor-wrapper">
<div id="line-numbers-1" class="line-numbers hidden"></div> <div id="editor-cm-1" class="codemirror-container"></div>
<textarea id="editor-1" class="editor-textarea"></textarea>
</div> </div>
</div> </div>
<div class="pane-resizer" id="pane-resizer-1" title="Drag to resize"></div> <div class="pane-resizer" id="pane-resizer-1" title="Drag to resize"></div>
<div id="preview-pane-1" class="pane"> <div id="preview-pane-1" class="pane">
<div class="preview-header">
<button class="preview-popout-btn" id="preview-popout-1" title="Pop out preview in new window">&#x2B1C;</button>
</div>
<div id="preview-1" class="preview-content"></div> <div id="preview-1" class="preview-content"></div>
</div> </div>
</div> </div>
+188 -281
View File
@@ -8,6 +8,7 @@ const marked = require('marked');
const { markedHighlight } = require('marked-highlight'); const { markedHighlight } = require('marked-highlight');
const DOMPurify = require('dompurify'); const DOMPurify = require('dompurify');
const hljs = require('highlight.js'); const hljs = require('highlight.js');
const { createEditor } = require('./editor/codemirror-setup');
// Configure marked with highlight extension // Configure marked with highlight extension
marked.use(markedHighlight({ marked.use(markedHighlight({
@@ -47,8 +48,7 @@ class TabManager {
content: '', content: '',
filePath: null, filePath: null,
isDirty: false, isDirty: false,
undoStack: [], editorView: null,
redoStack: [],
findMatches: [], findMatches: [],
currentMatchIndex: -1 currentMatchIndex: -1
}); });
@@ -114,8 +114,7 @@ class TabManager {
content: '', content: '',
filePath: null, filePath: null,
isDirty: false, isDirty: false,
undoStack: [], editorView: null,
redoStack: [],
findMatches: [], findMatches: [],
currentMatchIndex: -1 currentMatchIndex: -1
}; };
@@ -137,8 +136,7 @@ class TabManager {
tabContent.innerHTML = ` tabContent.innerHTML = `
<div id="editor-pane-${tab.id}" class="pane"> <div id="editor-pane-${tab.id}" class="pane">
<div class="editor-wrapper"> <div class="editor-wrapper">
<div id="line-numbers-${tab.id}" class="line-numbers hidden"></div> <div id="editor-cm-${tab.id}" class="codemirror-container"></div>
<textarea id="editor-${tab.id}" class="editor-textarea"></textarea>
</div> </div>
</div> </div>
<div class="pane-resizer" id="pane-resizer-${tab.id}" title="Drag to resize"></div> <div class="pane-resizer" id="pane-resizer-${tab.id}" title="Drag to resize"></div>
@@ -152,21 +150,21 @@ class TabManager {
document.querySelector('.editor-container').appendChild(tabContent); document.querySelector('.editor-container').appendChild(tabContent);
// Directly attach input listener to the new editor // Initialize CodeMirror editor
const editor = document.getElementById(`editor-${tab.id}`); const editorContainer = document.getElementById(`editor-cm-${tab.id}`);
if (editor) { if (editorContainer) {
editor.addEventListener('input', () => { const isDark = document.body.className.includes('dark');
this.handleEditorInput(tab.id); tab.editorView = createEditor(editorContainer, {
}); content: tab.content,
onChange: (newContent) => {
// Add scroll listener for line number sync tab.content = newContent;
editor.addEventListener('scroll', () => { tab.isDirty = true;
if (this.showLineNumbers && this.activeTabId === tab.id) { this.updatePreview(tab.id);
const lineNumbers = document.getElementById(`line-numbers-${tab.id}`); this.updateWordCount();
if (lineNumbers) { this.updateTabBar();
lineNumbers.scrollTop = editor.scrollTop; },
} isDark,
} showLineNumbers: this.showLineNumbers,
}); });
} }
} }
@@ -208,6 +206,11 @@ class TabManager {
if (!result) return; if (!result) return;
} }
// Destroy CodeMirror view
if (tab?.editorView) {
tab.editorView.destroy();
}
// Remove tab elements // Remove tab elements
const tabElement = document.querySelector(`[data-tab-id="${tabId}"]`); const tabElement = document.querySelector(`[data-tab-id="${tabId}"]`);
const tabContent = document.getElementById(`tab-content-${tabId}`); const tabContent = document.getElementById(`tab-content-${tabId}`);
@@ -282,9 +285,8 @@ class TabManager {
const tab = this.tabs.get(this.activeTabId); const tab = this.tabs.get(this.activeTabId);
if (!tab) return; if (!tab) return;
const editor = document.getElementById(`editor-${this.activeTabId}`); if (tab.editorView) {
if (editor) { tab.content = tab.editorView.state.doc.toString();
tab.content = editor.value;
tab.isDirty = tab.content !== (tab.originalContent || ''); tab.isDirty = tab.content !== (tab.originalContent || '');
} }
} }
@@ -293,19 +295,17 @@ class TabManager {
const tab = this.tabs.get(tabId); const tab = this.tabs.get(tabId);
if (!tab) return; if (!tab) return;
const editor = document.getElementById(`editor-${tabId}`); if (tab.editorView) {
this.setEditorContent(tabId, tab.content);
if (editor) {
editor.value = tab.content;
this.updatePreview(tabId); this.updatePreview(tabId);
this.updateWordCount(); this.updateWordCount();
} }
} }
focusActiveEditor() { focusActiveEditor() {
const editor = document.getElementById(`editor-${this.activeTabId}`); const tab = this.tabs.get(this.activeTabId);
if (editor) { if (tab?.editorView) {
editor.focus(); tab.editorView.focus();
} }
} }
@@ -403,23 +403,9 @@ class TabManager {
} }
updateLineNumbers() { updateLineNumbers() {
const editor = document.getElementById(`editor-${this.activeTabId}`); // CodeMirror handles line numbers natively — this is now a no-op.
const lineNumbers = document.getElementById(`line-numbers-${this.activeTabId}`); // Line number visibility is controlled via the showLineNumbers option
// passed to createEditor().
if (!editor || !lineNumbers) return;
if (this.showLineNumbers) {
const lines = editor.value.split('\n');
lineNumbers.innerHTML = lines.map((_, i) =>
`<div class="line-number">${i + 1}</div>`
).join('');
lineNumbers.classList.remove('hidden');
// Sync scroll position
lineNumbers.scrollTop = editor.scrollTop;
} else {
lineNumbers.classList.add('hidden');
}
} }
updateWordCount() { updateWordCount() {
@@ -455,82 +441,45 @@ class TabManager {
} }
setupEditorEvents() { setupEditorEvents() {
// Set up editor events using event delegation on the container // CodeMirror handles editor events via the onChange callback
const editorContainer = document.querySelector('.editor-container'); // passed to createEditor(). No additional event delegation needed.
if (editorContainer) {
editorContainer.addEventListener('input', (e) => {
if (e.target.classList.contains('editor-textarea')) {
const tabId = parseInt(e.target.id.split('-')[1]);
this.handleEditorInput(tabId);
}
});
editorContainer.addEventListener('scroll', (e) => {
if (e.target.classList.contains('editor-textarea')) {
this.updateLineNumbers();
}
}, true);
}
} }
handleEditorInput(tabId) { handleEditorInput(tabId) {
// CodeMirror's onChange callback handles content syncing.
// This method is kept for any remaining callers but is largely a no-op now.
const tab = this.tabs.get(tabId); const tab = this.tabs.get(tabId);
if (!tab) return; if (!tab) return;
const editor = document.getElementById(`editor-${tabId}`); if (tab.editorView) {
if (!editor) return; tab.content = tab.editorView.state.doc.toString();
}
tab.content = editor.value;
tab.isDirty = true; tab.isDirty = true;
this.updatePreview(tabId); this.updatePreview(tabId);
this.updateWordCount(); this.updateWordCount();
this.updateLineNumbers();
this.updateTabBar(); this.updateTabBar();
// Add to undo stack
this.pushUndoState(tabId);
} }
pushUndoState(tabId) { pushUndoState(tabId) {
const tab = this.tabs.get(tabId); // CodeMirror has built-in history — this is now a no-op.
if (!tab) return; // Kept as a stub for any remaining callers.
tab.undoStack.push(tab.content);
if (tab.undoStack.length > 50) {
tab.undoStack.shift();
}
tab.redoStack = [];
} }
undo() { undo() {
// CodeMirror handles undo via its built-in history extension.
// This stub is kept for the IPC handler; Task 9 will wire it properly.
const tab = this.tabs.get(this.activeTabId); const tab = this.tabs.get(this.activeTabId);
if (!tab || tab.undoStack.length === 0) return; if (!tab?.editorView) return;
// CodeMirror's Ctrl+Z keybinding handles undo automatically.
tab.redoStack.push(tab.content);
tab.content = tab.undoStack.pop();
const editor = document.getElementById(`editor-${this.activeTabId}`);
if (editor) {
editor.value = tab.content;
this.updatePreview();
this.updateWordCount();
}
} }
redo() { redo() {
// CodeMirror handles redo via its built-in history extension.
// This stub is kept for the IPC handler; Task 9 will wire it properly.
const tab = this.tabs.get(this.activeTabId); const tab = this.tabs.get(this.activeTabId);
if (!tab || tab.redoStack.length === 0) return; if (!tab?.editorView) return;
// CodeMirror's Ctrl+Shift+Z / Ctrl+Y keybinding handles redo automatically.
tab.undoStack.push(tab.content);
tab.content = tab.redoStack.pop();
const editor = document.getElementById(`editor-${this.activeTabId}`);
if (editor) {
editor.value = tab.content;
this.updatePreview();
this.updateWordCount();
}
} }
// Auto-save functionality // Auto-save functionality
@@ -681,103 +630,64 @@ class TabManager {
// Helper function to wrap selected text // Helper function to wrap selected text
wrapSelection(before, after) { wrapSelection(before, after) {
const editor = document.getElementById(`editor-${this.activeTabId}`); const tab = this.tabs.get(this.activeTabId);
if (!editor) return; if (!tab?.editorView) return;
const start = editor.selectionStart; const view = tab.editorView;
const end = editor.selectionEnd; const { from, to } = view.state.selection.main;
const selectedText = editor.value.substring(start, end); const selectedText = view.state.sliceDoc(from, to);
const replacement = before + (selectedText || 'text') + after; const replacement = before + (selectedText || 'text') + after;
editor.value = editor.value.substring(0, start) + replacement + editor.value.substring(end); view.dispatch({
changes: { from, to, insert: replacement }
// Update cursor position });
const newCursorPos = selectedText ? start + replacement.length : start + before.length; view.focus();
editor.selectionStart = editor.selectionEnd = newCursorPos;
editor.focus();
// Trigger update
this.handleEditorInput(this.activeTabId);
} }
// Helper function to insert text at the start of current line // Helper function to insert text at the start of current line
insertAtLineStart(prefix) { insertAtLineStart(prefix) {
const editor = document.getElementById(`editor-${this.activeTabId}`); const tab = this.tabs.get(this.activeTabId);
if (!editor) return; if (!tab?.editorView) return;
const start = editor.selectionStart; const view = tab.editorView;
const text = editor.value; const pos = view.state.selection.main.head;
const line = view.state.doc.lineAt(pos);
// Find the start of the current line view.dispatch({
let lineStart = text.lastIndexOf('\n', start - 1) + 1; changes: { from: line.from, insert: prefix }
});
// Insert the prefix view.focus();
editor.value = text.substring(0, lineStart) + prefix + text.substring(lineStart);
// Update cursor position
editor.selectionStart = editor.selectionEnd = start + prefix.length;
editor.focus();
// Trigger update
this.handleEditorInput(this.activeTabId);
} }
// Insert a markdown table // Insert a markdown table
insertTable() { insertTable() {
const editor = document.getElementById(`editor-${this.activeTabId}`);
if (!editor) return;
const table = '\n| Column 1 | Column 2 | Column 3 |\n' + const table = '\n| Column 1 | Column 2 | Column 3 |\n' +
'|----------|----------|----------|\n' + '|----------|----------|----------|\n' +
'| Cell 1 | Cell 2 | Cell 3 |\n' + '| Cell 1 | Cell 2 | Cell 3 |\n' +
'| Cell 4 | Cell 5 | Cell 6 |\n'; '| Cell 4 | Cell 5 | Cell 6 |\n';
const start = editor.selectionStart; this.insertAtCursor(table);
editor.value = editor.value.substring(0, start) + table + editor.value.substring(start);
// Update cursor position
editor.selectionStart = editor.selectionEnd = start + table.length;
editor.focus();
// Trigger update
this.handleEditorInput(this.activeTabId);
} }
// Insert a code block // Insert a code block
insertCodeBlock() { insertCodeBlock() {
const editor = document.getElementById(`editor-${this.activeTabId}`); const tab = this.tabs.get(this.activeTabId);
if (!editor) return; if (!tab?.editorView) return;
const selectedText = editor.value.substring(editor.selectionStart, editor.selectionEnd); const view = tab.editorView;
const { from, to } = view.state.selection.main;
const selectedText = view.state.sliceDoc(from, to);
const codeBlock = '\n```\n' + (selectedText || 'code here') + '\n```\n'; const codeBlock = '\n```\n' + (selectedText || 'code here') + '\n```\n';
const start = editor.selectionStart; view.dispatch({
editor.value = editor.value.substring(0, start) + codeBlock + editor.value.substring(editor.selectionEnd); changes: { from, to, insert: codeBlock }
});
// Update cursor position view.focus();
editor.selectionStart = editor.selectionEnd = start + codeBlock.length;
editor.focus();
// Trigger update
this.handleEditorInput(this.activeTabId);
} }
// Insert a horizontal rule // Insert a horizontal rule
insertHorizontalRule() { insertHorizontalRule() {
const editor = document.getElementById(`editor-${this.activeTabId}`); this.insertAtCursor('\n\n---\n\n');
if (!editor) return;
const hr = '\n\n---\n\n';
const start = editor.selectionStart;
editor.value = editor.value.substring(0, start) + hr + editor.value.substring(start);
// Update cursor position
editor.selectionStart = editor.selectionEnd = start + hr.length;
editor.focus();
// Trigger update
this.handleEditorInput(this.activeTabId);
} }
setupFindEvents() { setupFindEvents() {
@@ -848,9 +758,8 @@ class TabManager {
performFind() { performFind() {
const findText = document.getElementById('find-input').value; const findText = document.getElementById('find-input').value;
const tab = this.tabs.get(this.activeTabId); const tab = this.tabs.get(this.activeTabId);
const editor = document.getElementById(`editor-${this.activeTabId}`);
if (!findText || !tab || !editor) { if (!findText || !tab) {
this.clearFindHighlights(); this.clearFindHighlights();
const findCount = document.getElementById('find-count'); const findCount = document.getElementById('find-count');
if (findCount) { if (findCount) {
@@ -859,7 +768,7 @@ class TabManager {
return; return;
} }
const content = editor.value; const content = this.getEditorContent();
const matches = []; const matches = [];
let index = 0; let index = 0;
@@ -905,40 +814,28 @@ class TabManager {
highlightMatch(matchIndex) { highlightMatch(matchIndex) {
const tab = this.tabs.get(this.activeTabId); const tab = this.tabs.get(this.activeTabId);
const editor = document.getElementById(`editor-${this.activeTabId}`);
const findText = document.getElementById('find-input').value; const findText = document.getElementById('find-input').value;
if (!tab || !editor || matchIndex < 0 || matchIndex >= tab.findMatches.length) return; if (!tab || !tab.editorView || matchIndex < 0 || matchIndex >= tab.findMatches.length) return;
const position = tab.findMatches[matchIndex]; const position = tab.findMatches[matchIndex];
const view = tab.editorView;
// Select the match WITHOUT focusing (to keep focus on find input) // Select the match in CodeMirror
editor.setSelectionRange(position, position + findText.length); view.dispatch({
selection: { anchor: position, head: position + findText.length },
scrollIntoView: true,
});
// Make the selection visible by briefly focusing and then restoring focus // Restore focus to find input
const findInput = document.getElementById('find-input'); const findInput = document.getElementById('find-input');
const hadFocus = document.activeElement === findInput; if (findInput) {
// Temporarily focus editor to make selection visible
editor.focus();
// Restore focus to find input if it had focus
if (hadFocus) {
setTimeout(() => { setTimeout(() => {
findInput.focus(); findInput.focus();
// Restore cursor position in find input
findInput.setSelectionRange(findInput.value.length, findInput.value.length); findInput.setSelectionRange(findInput.value.length, findInput.value.length);
}, 10); }, 10);
} }
// Scroll into view
const lineHeight = 20; // Approximate line height
const charPosition = position;
const numLines = editor.value.substring(0, charPosition).split('\n').length;
const scrollPosition = (numLines - 5) * lineHeight; // Show match 5 lines from top
editor.scrollTop = Math.max(0, scrollPosition);
// Update match counter // Update match counter
const findCount = document.getElementById('find-count'); const findCount = document.getElementById('find-count');
if (findCount) { if (findCount) {
@@ -948,18 +845,19 @@ class TabManager {
replaceOne() { replaceOne() {
const tab = this.tabs.get(this.activeTabId); const tab = this.tabs.get(this.activeTabId);
const editor = document.getElementById(`editor-${this.activeTabId}`);
const findText = document.getElementById('find-input').value; const findText = document.getElementById('find-input').value;
const replaceText = document.getElementById('replace-input').value; const replaceText = document.getElementById('replace-input').value;
if (!tab || !editor || tab.findMatches.length === 0 || tab.currentMatchIndex < 0) return; if (!tab || !tab.editorView || tab.findMatches.length === 0 || tab.currentMatchIndex < 0) return;
const position = tab.findMatches[tab.currentMatchIndex]; const position = tab.findMatches[tab.currentMatchIndex];
const before = editor.value.substring(0, position); const view = tab.editorView;
const after = editor.value.substring(position + findText.length);
editor.value = before + replaceText + after; view.dispatch({
tab.content = editor.value; changes: { from: position, to: position + findText.length, insert: replaceText }
});
tab.content = view.state.doc.toString();
tab.isDirty = true; tab.isDirty = true;
this.updatePreview(this.activeTabId); this.updatePreview(this.activeTabId);
@@ -972,18 +870,16 @@ class TabManager {
replaceAll() { replaceAll() {
const tab = this.tabs.get(this.activeTabId); const tab = this.tabs.get(this.activeTabId);
const editor = document.getElementById(`editor-${this.activeTabId}`);
const findText = document.getElementById('find-input').value; const findText = document.getElementById('find-input').value;
const replaceText = document.getElementById('replace-input').value; const replaceText = document.getElementById('replace-input').value;
if (!tab || !editor || !findText) return; if (!tab || !tab.editorView || !findText) return;
// Simple replace all const content = this.getEditorContent();
const newContent = editor.value.split(findText).join(replaceText); const newContent = content.split(findText).join(replaceText);
const replacedCount = tab.findMatches.length; const replacedCount = tab.findMatches.length;
editor.value = newContent; this.setEditorContent(this.activeTabId, newContent);
tab.content = newContent;
tab.isDirty = true; tab.isDirty = true;
this.updatePreview(this.activeTabId); this.updatePreview(this.activeTabId);
@@ -1023,13 +919,9 @@ class TabManager {
tab.isDirty = false; tab.isDirty = false;
// Update the editor and preview // Update the editor and preview
const editor = document.getElementById(`editor-${this.activeTabId}`); this.setEditorContent(this.activeTabId, content);
if (editor) {
editor.value = content;
// Update preview after editor is updated
this.updatePreview(this.activeTabId); this.updatePreview(this.activeTabId);
this.updateWordCount(); this.updateWordCount();
}
} else { } else {
// Create new tab for the file // Create new tab for the file
console.log('Creating new tab for file'); console.log('Creating new tab for file');
@@ -1041,16 +933,11 @@ class TabManager {
tab.originalContent = content; tab.originalContent = content;
tab.isDirty = false; tab.isDirty = false;
// Wait a moment for the DOM to update, then set content // Set content in the CodeMirror editor
setTimeout(() => { this.setEditorContent(this.activeTabId, content);
const editor = document.getElementById(`editor-${this.activeTabId}`);
if (editor) {
editor.value = content;
this.updatePreview(this.activeTabId); this.updatePreview(this.activeTabId);
this.updateWordCount(); this.updateWordCount();
} }
}, 50);
}
this.startAutoSave(); this.startAutoSave();
this.addToRecentFiles(filePath); this.addToRecentFiles(filePath);
this.updateTabBar(); this.updateTabBar();
@@ -1061,6 +948,58 @@ class TabManager {
console.log('File opened successfully'); console.log('File opened successfully');
} }
// Get content from active editor
getEditorContent(tabId = this.activeTabId) {
const tab = this.tabs.get(tabId);
if (tab?.editorView) {
return tab.editorView.state.doc.toString();
}
return tab?.content || '';
}
// Set content in editor
setEditorContent(tabId, content) {
const tab = this.tabs.get(tabId);
if (tab?.editorView) {
tab.editorView.dispatch({
changes: { from: 0, to: tab.editorView.state.doc.length, insert: content }
});
}
tab.content = content;
}
// Insert text at cursor position
insertAtCursor(text) {
const tab = this.tabs.get(this.activeTabId);
if (!tab?.editorView) return;
const view = tab.editorView;
const pos = view.state.selection.main.head;
view.dispatch({
changes: { from: pos, insert: text }
});
view.focus();
}
// Get selected text
getSelection() {
const tab = this.tabs.get(this.activeTabId);
if (!tab?.editorView) return '';
const state = tab.editorView.state;
return state.sliceDoc(state.selection.main.from, state.selection.main.to);
}
// Replace selection
replaceSelection(text) {
const tab = this.tabs.get(this.activeTabId);
if (!tab?.editorView) return;
const view = tab.editorView;
const { from, to } = view.state.selection.main;
view.dispatch({
changes: { from, to, insert: text }
});
view.focus();
}
getCurrentContent() { getCurrentContent() {
const tab = this.tabs.get(this.activeTabId); const tab = this.tabs.get(this.activeTabId);
return tab ? tab.content : ''; return tab ? tab.content : '';
@@ -1078,21 +1017,22 @@ let tabManager;
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', () => {
tabManager = new TabManager(); tabManager = new TabManager();
// Attach input listener to the initial editor (tab 1) // Initialize CodeMirror for the initial tab (tab 1)
const initialEditor = document.getElementById('editor-1'); const editorContainer = document.getElementById('editor-cm-1');
if (initialEditor) { if (editorContainer) {
initialEditor.addEventListener('input', () => { const tab = tabManager.tabs.get(1);
tabManager.handleEditorInput(1); const isDark = document.body.className.includes('dark');
}); tab.editorView = createEditor(editorContainer, {
content: tab.content,
// Add scroll listener for line number sync onChange: (newContent) => {
initialEditor.addEventListener('scroll', () => { tab.content = newContent;
if (tabManager.showLineNumbers && tabManager.activeTabId === 1) { tab.isDirty = true;
const lineNumbers = document.getElementById('line-numbers-1'); tabManager.updatePreview(tab.id);
if (lineNumbers) { tabManager.updateWordCount();
lineNumbers.scrollTop = initialEditor.scrollTop; tabManager.updateTabBar();
} },
} isDark,
showLineNumbers: tabManager.showLineNumbers,
}); });
} }
@@ -1193,7 +1133,7 @@ ipcRenderer.on('redo', () => {
let currentFontSize = parseInt(localStorage.getItem('fontSize')) || 15; let currentFontSize = parseInt(localStorage.getItem('fontSize')) || 15;
function updateFontSizes(size) { function updateFontSizes(size) {
const editors = document.querySelectorAll('#editor, .editor-textarea'); const editors = document.querySelectorAll('.cm-editor');
const previews = document.querySelectorAll('#preview, .preview-content'); const previews = document.querySelectorAll('#preview, .preview-content');
editors.forEach(editor => { editors.forEach(editor => {
@@ -3341,31 +3281,19 @@ function insertGeneratedTable() {
return; return;
} }
// Get active editor // Insert table using CodeMirror
const activeTabId = tabManager ? tabManager.activeTabId : 1; if (!tabManager) {
const editor = document.getElementById(`editor-${activeTabId}`);
if (!editor) {
alert('No active editor found'); alert('No active editor found');
return; return;
} }
// Insert table at cursor position
const start = editor.selectionStart;
const before = editor.value.substring(0, start);
const after = editor.value.substring(editor.selectionEnd);
// Add newlines for spacing // Add newlines for spacing
const tableWithSpacing = '\n' + table + '\n'; const tableWithSpacing = '\n' + table + '\n';
editor.value = before + tableWithSpacing + after; tabManager.insertAtCursor(tableWithSpacing);
// Update cursor position
editor.selectionStart = editor.selectionEnd = start + tableWithSpacing.length;
editor.focus();
// Trigger update // Trigger update
if (tabManager) { if (tabManager) {
tabManager.handleEditorInput(activeTabId); tabManager.handleEditorInput(tabManager.activeTabId);
} }
// Close dialog // Close dialog
@@ -3836,30 +3764,19 @@ function insertASCIIArt() {
return; return;
} }
// Get active editor // Insert using CodeMirror
const activeTabId = tabManager ? tabManager.activeTabId : 1; if (!tabManager) {
const editor = document.getElementById(`editor-${activeTabId}`);
if (!editor) {
alert('No active editor found'); alert('No active editor found');
return; return;
} }
// Insert in code block for proper rendering // Insert in code block for proper rendering
const start = editor.selectionStart;
const before = editor.value.substring(0, start);
const after = editor.value.substring(editor.selectionEnd);
const codeBlock = '\n```\n' + asciiArt + '\n```\n'; const codeBlock = '\n```\n' + asciiArt + '\n```\n';
editor.value = before + codeBlock + after; tabManager.insertAtCursor(codeBlock);
// Update cursor position
editor.selectionStart = editor.selectionEnd = start + codeBlock.length;
editor.focus();
// Trigger update // Trigger update
if (tabManager) { if (tabManager) {
tabManager.handleEditorInput(activeTabId); tabManager.handleEditorInput(tabManager.activeTabId);
} }
// Close dialog // Close dialog
@@ -4147,24 +4064,14 @@ document.addEventListener('click', (e) => {
// ============================================ // ============================================
ipcRenderer.on('insert-content', (event, content) => { ipcRenderer.on('insert-content', (event, content) => {
if (tabManager) { if (tabManager) {
tabManager.insertAtCursor(content);
const activeTabId = tabManager.activeTabId; const activeTabId = tabManager.activeTabId;
const editor = document.getElementById(`editor-${activeTabId}`); const tab = tabManager.tabs.get(activeTabId);
if (editor) { if (tab) {
const cursorPos = editor.selectionStart; tab.isDirty = true;
const textBefore = editor.value.substring(0, cursorPos);
const textAfter = editor.value.substring(cursorPos);
editor.value = textBefore + content + textAfter;
editor.focus();
const newPos = cursorPos + content.length;
editor.setSelectionRange(newPos, newPos);
// Update state and preview
tabManager.tabs.get(activeTabId).content = editor.value;
tabManager.tabs.get(activeTabId).modified = true;
tabManager.updatePreview(activeTabId); tabManager.updatePreview(activeTabId);
tabManager.updateTabTitle(activeTabId); tabManager.updateTabBar();
} }
} }
}); });
+14
View File
@@ -290,6 +290,20 @@ body {
/* Color controlled by theme */ /* Color controlled by theme */
} }
/* CodeMirror container */
.codemirror-container {
height: 100%;
overflow: auto;
}
.codemirror-container .cm-editor {
height: 100%;
font-family: 'JetBrains Mono', 'Fira Code', 'Consolas', monospace;
font-size: 14px;
}
.codemirror-container .cm-scroller {
overflow: auto;
}
.pane:last-child { .pane:last-child {
padding: 0; padding: 0;
/* Background controlled by theme */ /* Background controlled by theme */
+14
View File
@@ -222,6 +222,20 @@ body {
resize: none; resize: none;
} }
/* CodeMirror container */
.codemirror-container {
height: 100%;
overflow: auto;
}
.codemirror-container .cm-editor {
height: 100%;
font-family: 'JetBrains Mono', 'Fira Code', 'Consolas', monospace;
font-size: 14px;
}
.codemirror-container .cm-scroller {
overflow: auto;
}
.pane:last-child { .pane:last-child {
padding: 0; padding: 0;
background: #fff; background: #fff;