154 lines
4.6 KiB
JavaScript
Raw Normal View History

2025-11-10 15:46:40 +01:00
import { api } from '../api.js';
class CodeEditorView extends HTMLElement {
constructor() {
super();
this.editor = null;
this.file = null;
this.previousView = null;
this.boundHandleClick = this.handleClick.bind(this);
this.boundHandleEscape = this.handleEscape.bind(this);
}
connectedCallback() {
this.addEventListener('click', this.boundHandleClick);
document.addEventListener('keydown', this.boundHandleEscape);
}
disconnectedCallback() {
this.removeEventListener('click', this.boundHandleClick);
document.removeEventListener('keydown', this.boundHandleEscape);
if (this.editor) {
this.editor.toTextArea();
this.editor = null;
}
}
handleEscape(e) {
if (e.key === 'Escape') {
this.goBack();
}
}
async setFile(file, previousView = 'files') {
this.file = file;
this.previousView = previousView;
await this.loadAndRender();
}
async loadAndRender() {
try {
const blob = await api.downloadFile(this.file.id);
const content = await blob.text();
this.render(content);
this.initializeEditor(content);
} catch (error) {
console.error('Failed to load file:', error);
document.dispatchEvent(new CustomEvent('show-toast', {
detail: { message: 'Failed to load file: ' + error.message, type: 'error' }
}));
this.render('');
window.history.back();
}
}
getMimeType(filename) {
const extension = filename.split('.').pop().toLowerCase();
const mimeMap = {
'js': 'text/javascript',
'json': 'application/json',
'py': 'text/x-python',
'md': 'text/x-markdown',
'html': 'text/html',
'xml': 'application/xml',
'css': 'text/css',
'txt': 'text/plain',
'log': 'text/plain',
'sh': 'text/x-sh',
'yaml': 'text/x-yaml',
'yml': 'text/x-yaml'
};
return mimeMap[extension] || 'text/plain';
}
render(content) {
this.innerHTML = `
<div class="code-editor-view">
<div class="code-editor-header">
<div class="header-left">
<button class="button" id="back-btn">Back</button>
<h2 class="editor-filename">${this.file.name}</h2>
</div>
<div class="header-right">
<button class="button button-primary" id="save-btn">Save</button>
</div>
</div>
<div class="code-editor-body">
<textarea id="code-editor-textarea">${content}</textarea>
</div>
</div>
`;
}
initializeEditor(content) {
const textarea = this.querySelector('#code-editor-textarea');
if (!textarea) return;
this.editor = CodeMirror.fromTextArea(textarea, {
value: content,
mode: this.getMimeType(this.file.name),
lineNumbers: true,
theme: 'default',
lineWrapping: true,
indentUnit: 4,
indentWithTabs: false,
extraKeys: {
'Ctrl-S': () => this.save(),
'Cmd-S': () => this.save()
}
});
this.editor.setSize('100%', '100%');
}
handleClick(e) {
if (e.target.id === 'back-btn') {
this.goBack();
} else if (e.target.id === 'save-btn') {
this.save();
}
}
async save() {
if (!this.editor) return;
try {
const content = this.editor.getValue();
await api.updateFile(this.file.id, content);
document.dispatchEvent(new CustomEvent('show-toast', {
detail: { message: 'File saved successfully!', type: 'success' }
}));
} catch (error) {
document.dispatchEvent(new CustomEvent('show-toast', {
detail: { message: 'Failed to save file: ' + error.message, type: 'error' }
}));
}
}
goBack() {
window.history.back();
}
hide() {
document.removeEventListener('keydown', this.boundHandleEscape);
if (this.editor) {
this.editor.toTextArea();
this.editor = null;
}
this.remove();
}
}
customElements.define('code-editor-view', CodeEditorView);
export { CodeEditorView };