216 lines
6.3 KiB
JavaScript
Raw Normal View History

2025-11-11 01:05:13 +01:00
import app from '../app.js';
const api = app.getAPI();
const logger = app.getLogger();
2025-11-10 15:46:40 +01:00
class CodeEditorView extends HTMLElement {
constructor() {
super();
this.editor = null;
this.file = null;
this.previousView = null;
2025-11-11 01:05:13 +01:00
this.isRendered = false;
2025-11-10 15:46:40 +01:00
}
connectedCallback() {
2025-11-11 01:05:13 +01:00
logger.debug('CodeEditorView connected');
2025-11-10 15:46:40 +01:00
}
disconnectedCallback() {
2025-11-11 01:05:13 +01:00
logger.debug('CodeEditorView disconnected');
this.destroyEditor();
2025-11-10 15:46:40 +01:00
}
2025-11-11 01:05:13 +01:00
async setFile(file, previousView = 'files') {
if (this.isRendered) {
logger.warn('Editor already rendered, skipping');
return;
2025-11-10 15:46:40 +01:00
}
this.file = file;
this.previousView = previousView;
2025-11-11 01:05:13 +01:00
this.isRendered = true;
2025-11-10 15:46:40 +01:00
try {
2025-11-11 01:05:13 +01:00
logger.debug('Loading file', { fileName: file.name });
const blob = await api.downloadFile(file.id);
2025-11-10 15:46:40 +01:00
const content = await blob.text();
2025-11-11 01:05:13 +01:00
this.createUI(content);
this.createEditor(content);
2025-11-10 15:46:40 +01:00
} catch (error) {
2025-11-11 01:05:13 +01:00
logger.error('Failed to load file', error);
2025-11-10 15:46:40 +01:00
document.dispatchEvent(new CustomEvent('show-toast', {
detail: { message: 'Failed to load file: ' + error.message, type: 'error' }
}));
window.history.back();
}
}
2025-11-11 01:05:13 +01:00
createUI(content) {
2025-11-10 15:46:40 +01:00
this.innerHTML = `
2025-11-11 01:05:13 +01:00
<div class="code-editor-overlay">
<div class="code-editor-container">
<div class="code-editor-header">
<div class="header-left">
<button class="button" id="back-btn">Back</button>
<h2 class="editor-filename">${this.escapeHtml(this.file.name)}</h2>
</div>
<div class="header-right">
<button class="button button-primary" id="save-btn">Save & Close</button>
</div>
2025-11-10 15:46:40 +01:00
</div>
2025-11-11 01:05:13 +01:00
<div class="code-editor-body">
<textarea id="editor-textarea"></textarea>
2025-11-10 15:46:40 +01:00
</div>
</div>
</div>
`;
2025-11-11 01:05:13 +01:00
const backBtn = this.querySelector('#back-btn');
const saveBtn = this.querySelector('#save-btn');
backBtn.addEventListener('click', () => this.close());
saveBtn.addEventListener('click', () => this.save());
document.addEventListener('keydown', this.handleKeydown.bind(this));
2025-11-10 15:46:40 +01:00
}
2025-11-11 01:05:13 +01:00
createEditor(content) {
const textarea = this.querySelector('#editor-textarea');
if (!textarea) {
logger.error('Textarea not found');
return;
}
textarea.value = content;
const mode = this.getMode(this.file.name);
logger.debug('Creating CodeMirror editor', { mode, fileSize: content.length });
2025-11-10 15:46:40 +01:00
this.editor = CodeMirror.fromTextArea(textarea, {
2025-11-11 01:05:13 +01:00
mode: mode,
2025-11-10 15:46:40 +01:00
lineNumbers: true,
lineWrapping: true,
indentUnit: 4,
indentWithTabs: false,
2025-11-11 01:05:13 +01:00
theme: 'default',
readOnly: false,
autofocus: true,
2025-11-10 15:46:40 +01:00
extraKeys: {
2025-11-11 01:05:13 +01:00
'Ctrl-S': () => { this.save(); return false; },
'Cmd-S': () => { this.save(); return false; },
'Esc': () => { this.close(); return false; }
2025-11-10 15:46:40 +01:00
}
});
this.editor.setSize('100%', '100%');
2025-11-11 01:05:13 +01:00
setTimeout(() => {
if (this.editor) {
this.editor.refresh();
this.editor.focus();
logger.debug('Editor ready and focused');
}
}, 100);
}
getMode(filename) {
const ext = filename.split('.').pop().toLowerCase();
const modes = {
'js': 'javascript',
'json': { name: 'javascript', json: true },
'py': 'python',
'md': 'markdown',
'html': 'htmlmixed',
'xml': 'xml',
'css': 'css',
'txt': 'text/plain',
'log': 'text/plain',
'sh': 'shell',
'yaml': 'yaml',
'yml': 'yaml'
};
return modes[ext] || 'text/plain';
2025-11-10 15:46:40 +01:00
}
2025-11-11 01:05:13 +01:00
escapeHtml(text) {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
handleKeydown(e) {
if (e.key === 'Escape' && !this.editor.getOption('readOnly')) {
this.close();
2025-11-10 15:46:40 +01:00
}
}
async save() {
2025-11-11 01:05:13 +01:00
if (!this.editor) {
logger.warn('No editor instance');
return;
}
const saveBtn = this.querySelector('#save-btn');
if (!saveBtn) return;
2025-11-10 15:46:40 +01:00
try {
2025-11-11 01:05:13 +01:00
saveBtn.disabled = true;
saveBtn.textContent = 'Saving...';
2025-11-10 15:46:40 +01:00
const content = this.editor.getValue();
2025-11-11 01:05:13 +01:00
logger.debug('Saving file', { fileName: this.file.name, size: content.length });
2025-11-10 15:46:40 +01:00
await api.updateFile(this.file.id, content);
2025-11-11 01:05:13 +01:00
logger.info('File saved successfully', { fileName: this.file.name });
2025-11-10 15:46:40 +01:00
document.dispatchEvent(new CustomEvent('show-toast', {
detail: { message: 'File saved successfully!', type: 'success' }
}));
2025-11-11 01:05:13 +01:00
setTimeout(() => {
this.close();
}, 500);
2025-11-10 15:46:40 +01:00
} catch (error) {
2025-11-11 01:05:13 +01:00
logger.error('Failed to save file', error);
2025-11-10 15:46:40 +01:00
document.dispatchEvent(new CustomEvent('show-toast', {
2025-11-11 01:05:13 +01:00
detail: { message: 'Failed to save: ' + error.message, type: 'error' }
2025-11-10 15:46:40 +01:00
}));
2025-11-11 01:05:13 +01:00
if (saveBtn) {
saveBtn.disabled = false;
saveBtn.textContent = 'Save & Close';
}
2025-11-10 15:46:40 +01:00
}
}
2025-11-11 01:05:13 +01:00
close() {
logger.debug('Closing editor');
2025-11-10 15:46:40 +01:00
window.history.back();
}
hide() {
2025-11-11 01:05:13 +01:00
logger.debug('Hiding editor');
this.destroyEditor();
this.remove();
}
destroyEditor() {
2025-11-10 15:46:40 +01:00
if (this.editor) {
2025-11-11 01:05:13 +01:00
logger.debug('Destroying CodeMirror instance');
try {
this.editor.toTextArea();
} catch (e) {
logger.warn('Error destroying editor', e);
}
2025-11-10 15:46:40 +01:00
this.editor = null;
}
}
}
customElements.define('code-editor-view', CodeEditorView);
export { CodeEditorView };