Add fullscreen file editor for email templates, project docs and agent files
This commit is contained in:
parent
7844e82c95
commit
5c75ad575d
3 changed files with 274 additions and 66 deletions
179
templates/file_editor.html
Normal file
179
templates/file_editor.html
Normal file
|
|
@ -0,0 +1,179 @@
|
|||
{% extends "base.html" %}
|
||||
{% block title %}Editor · {{ filename }}{% endblock %}
|
||||
|
||||
{% block head %}
|
||||
<style>
|
||||
/* Vollbild-Editor Layout */
|
||||
.editor-wrap {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: calc(100vh - 130px);
|
||||
gap: .75rem;
|
||||
}
|
||||
.editor-topbar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: .75rem;
|
||||
flex-shrink: 0;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.editor-meta {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
.editor-filename {
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
font-family: 'JetBrains Mono', monospace;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.editor-breadcrumb {
|
||||
font-size: .75rem;
|
||||
color: var(--text-muted);
|
||||
margin-top: .1rem;
|
||||
}
|
||||
#editor-textarea {
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
resize: none;
|
||||
background: var(--bg-base);
|
||||
color: var(--text-primary);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-sm);
|
||||
padding: 1.1rem 1.25rem;
|
||||
font-family: 'JetBrains Mono', monospace;
|
||||
font-size: .875rem;
|
||||
line-height: 1.65;
|
||||
outline: none;
|
||||
transition: border-color .15s;
|
||||
tab-size: 2;
|
||||
}
|
||||
#editor-textarea:focus {
|
||||
border-color: var(--accent);
|
||||
box-shadow: 0 0 0 2px rgba(99,102,241,.15);
|
||||
}
|
||||
.editor-statusbar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
font-size: .75rem;
|
||||
color: var(--text-muted);
|
||||
flex-shrink: 0;
|
||||
padding: 0 .1rem;
|
||||
}
|
||||
#save-status {
|
||||
font-weight: 500;
|
||||
min-width: 120px;
|
||||
text-align: right;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% if error %}
|
||||
<div class="alert alert-danger">{{ error }}</div>
|
||||
<a href="/files" class="btn btn-secondary">← Zurück zu Dateien</a>
|
||||
{% else %}
|
||||
|
||||
<div class="editor-wrap">
|
||||
<!-- Topbar -->
|
||||
<div class="editor-topbar">
|
||||
<a href="/files" class="btn btn-outline-secondary btn-sm">← Zurück</a>
|
||||
<div class="editor-meta">
|
||||
<div class="editor-filename">{{ filename }}</div>
|
||||
<div class="editor-breadcrumb">
|
||||
{% if file_type == 'email' %}✉ Email-Vorlage · emails/{{ filename }}
|
||||
{% elif file_type == 'project' %}📄 Projektdokument · {{ filename }}
|
||||
{% elif file_type == 'agent' %}🤖 {{ agent_key }} · agents/{{ agent_key }}/work/{{ filename }}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<button id="save-btn" class="btn btn-primary btn-sm" onclick="saveFile()">💾 Speichern</button>
|
||||
</div>
|
||||
|
||||
<!-- Editor -->
|
||||
<textarea id="editor-textarea" spellcheck="false">{{ content }}</textarea>
|
||||
|
||||
<!-- Statusbar -->
|
||||
<div class="editor-statusbar">
|
||||
<span id="cursor-pos">Zeile 1, Spalte 1</span>
|
||||
<span id="save-status"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const FILE_TYPE = {{ file_type|tojson }};
|
||||
const FILENAME = {{ filename|tojson }};
|
||||
const AGENT_KEY = {{ agent_key|tojson }};
|
||||
|
||||
const ta = document.getElementById('editor-textarea');
|
||||
const saveStatus = document.getElementById('save-status');
|
||||
const cursorPos = document.getElementById('cursor-pos');
|
||||
|
||||
// Cursor-Position anzeigen
|
||||
ta.addEventListener('keyup', updateCursor);
|
||||
ta.addEventListener('click', updateCursor);
|
||||
function updateCursor() {
|
||||
const text = ta.value.substring(0, ta.selectionStart);
|
||||
const line = text.split('\n').length;
|
||||
const col = text.split('\n').pop().length + 1;
|
||||
cursorPos.textContent = `Zeile ${line}, Spalte ${col}`;
|
||||
}
|
||||
|
||||
// Tab-Taste einfügen statt Fokus verlieren
|
||||
ta.addEventListener('keydown', function(e) {
|
||||
if (e.key === 'Tab') {
|
||||
e.preventDefault();
|
||||
const s = ta.selectionStart, end = ta.selectionEnd;
|
||||
ta.value = ta.value.substring(0, s) + ' ' + ta.value.substring(end);
|
||||
ta.selectionStart = ta.selectionEnd = s + 2;
|
||||
}
|
||||
// Ctrl/Cmd+S → Speichern
|
||||
if ((e.ctrlKey || e.metaKey) && e.key === 's') {
|
||||
e.preventDefault();
|
||||
saveFile();
|
||||
}
|
||||
});
|
||||
|
||||
// Speichern
|
||||
function saveFile() {
|
||||
const btn = document.getElementById('save-btn');
|
||||
btn.disabled = true;
|
||||
saveStatus.style.color = 'var(--text-muted)';
|
||||
saveStatus.textContent = 'Speichern…';
|
||||
|
||||
let url;
|
||||
if (FILE_TYPE === 'email') url = '/files/email/save/' + encodeURIComponent(FILENAME);
|
||||
if (FILE_TYPE === 'project') url = '/files/project/save/' + encodeURIComponent(FILENAME);
|
||||
if (FILE_TYPE === 'agent') url = '/files/agent/' + encodeURIComponent(AGENT_KEY) + '/save/' + encodeURIComponent(FILENAME);
|
||||
|
||||
fetch(url, {
|
||||
method: 'POST',
|
||||
credentials: 'same-origin',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({content: ta.value})
|
||||
})
|
||||
.then(r => r.json())
|
||||
.then(d => {
|
||||
if (d.ok) {
|
||||
saveStatus.style.color = 'var(--success, #22c55e)';
|
||||
saveStatus.textContent = '✓ Gespeichert';
|
||||
setTimeout(() => { saveStatus.textContent = ''; }, 3000);
|
||||
} else {
|
||||
saveStatus.style.color = 'var(--danger, #ef4444)';
|
||||
saveStatus.textContent = '✗ ' + (d.error || 'Fehler');
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
saveStatus.style.color = 'var(--danger, #ef4444)';
|
||||
saveStatus.textContent = '✗ ' + err.message;
|
||||
})
|
||||
.finally(() => { btn.disabled = false; });
|
||||
}
|
||||
|
||||
updateCursor();
|
||||
</script>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
Loading…
Add table
Add a link
Reference in a new issue