feat: Nachrichten als gelöscht markieren
- DB: deleted_on TEXT Spalte in messages (Migration)
- Backend: ToggleMessageDelete(), includeDeleted-Parameter in allen History-Queries
- Neuer Endpoint: PUT /api/messages/{id}/delete
- Frontend: Trash-Button zwischen Reply- und Mark-Button
- Gelöschte Nachrichten: durchgestrichen + roter Rand (oder versteckt je nach Einstellung)
- Einstellungen: Toggle-Switch 'Gelöschte Nachrichten anzeigen'
- KI-Payload: gelöschte Nachrichten werden nicht mitgeschickt
This commit is contained in:
1 parent
493dfcda1c
commit
a14a991a0f
11 files changed
+248
-39
No files matched your search
+56
-6
@@ -115,6 +115,46 @@ function truncate(s, max) {
|
||||
return s && s.length > max ? s.substring(0, max) + '...' : (s || '');
|
||||
}
|
||||
|
||||
// ── Delete Button ──
|
||||
|
||||
function _addDeleteButton(div, messageId, deletedOn) {
|
||||
if (!messageId) return;
|
||||
const isDeleted = !!deletedOn;
|
||||
const btn = document.createElement('button');
|
||||
btn.className = 'msg-delete-btn icon-btn' + (isDeleted ? ' deleted' : '');
|
||||
btn.innerHTML = '<i class="fa fa-trash-o"></i>';
|
||||
btn.title = isDeleted ? 'Löschung aufheben' : 'Nachricht löschen';
|
||||
btn.addEventListener('click', async (e) => {
|
||||
e.stopPropagation();
|
||||
try {
|
||||
const result = await apiToggleMessageDelete(messageId);
|
||||
const nowDeleted = !!result.deletedOn;
|
||||
btn.classList.toggle('deleted', nowDeleted);
|
||||
btn.title = nowDeleted ? 'Löschung aufheben' : 'Nachricht löschen';
|
||||
if (nowDeleted && !isDeletedVisible()) {
|
||||
// Nachricht ausblenden
|
||||
div.classList.add('msg-deleted-hidden');
|
||||
} else {
|
||||
div.classList.remove('msg-deleted-hidden');
|
||||
div.classList.toggle('msg-deleted', nowDeleted);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Delete toggle failed:', err);
|
||||
}
|
||||
});
|
||||
// Nach dem Reply-Button einfügen
|
||||
const replyBtn = div.querySelector('.msg-reply-btn');
|
||||
if (replyBtn) {
|
||||
replyBtn.after(btn);
|
||||
} else {
|
||||
div.prepend(btn);
|
||||
}
|
||||
}
|
||||
|
||||
function isDeletedVisible() {
|
||||
return loadSettings().showDeletedMessages === true;
|
||||
}
|
||||
|
||||
// ── Mark Button ──
|
||||
|
||||
function _addMarkButton(div, messageId, isMarked) {
|
||||
@@ -134,11 +174,17 @@ function _addMarkButton(div, messageId, isMarked) {
|
||||
console.error('Mark toggle failed:', err);
|
||||
}
|
||||
});
|
||||
const replyBtn = div.querySelector('.msg-reply-btn');
|
||||
if (replyBtn) {
|
||||
replyBtn.after(btn);
|
||||
// Nach dem Delete-Button einfügen (oder nach Reply-Button, oder am Anfang)
|
||||
const deleteBtn = div.querySelector('.msg-delete-btn');
|
||||
if (deleteBtn) {
|
||||
deleteBtn.after(btn);
|
||||
} else {
|
||||
div.prepend(btn);
|
||||
const replyBtn = div.querySelector('.msg-reply-btn');
|
||||
if (replyBtn) {
|
||||
replyBtn.after(btn);
|
||||
} else {
|
||||
div.prepend(btn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -238,23 +284,26 @@ function _renderUserTextContent(container, raw) {
|
||||
parseFluentEmojis(wrap);
|
||||
}
|
||||
|
||||
function addUserBubble(text, streamed = false, timestamp = '', messageId = null, isMarked = false) {
|
||||
function addUserBubble(text, streamed = false, timestamp = '', messageId = null, isMarked = false, deletedOn = null) {
|
||||
const div = document.createElement('div');
|
||||
div.className = 'msg user markdown' + (isMarked ? ' marked' : '');
|
||||
if (deletedOn) div.classList.add(isDeletedVisible() ? 'msg-deleted' : 'msg-deleted-hidden');
|
||||
div.dataset.role = 'user';
|
||||
div.dataset.timestamp = timestamp || new Date().toISOString();
|
||||
if (messageId) div.dataset.messageId = messageId;
|
||||
if (streamed) div.classList.add('streamed');
|
||||
renderUserContent(div, text);
|
||||
_addReplyButton(div);
|
||||
_addDeleteButton(div, messageId, deletedOn);
|
||||
_addMarkButton(div, messageId, isMarked);
|
||||
chatEl().appendChild(div);
|
||||
scrollDown();
|
||||
}
|
||||
|
||||
function addAssistantBubble(text, timestamp = '', messageId = null, isMarked = false) {
|
||||
function addAssistantBubble(text, timestamp = '', messageId = null, isMarked = false, deletedOn = null) {
|
||||
const div = document.createElement('div');
|
||||
div.className = 'msg assistant markdown' + (isMarked ? ' marked' : '');
|
||||
if (deletedOn) div.classList.add(isDeletedVisible() ? 'msg-deleted' : 'msg-deleted-hidden');
|
||||
div.dataset.role = 'assistant';
|
||||
div.dataset.timestamp = timestamp || new Date().toISOString();
|
||||
if (messageId) div.dataset.messageId = messageId;
|
||||
@@ -262,6 +311,7 @@ function addAssistantBubble(text, timestamp = '', messageId = null, isMarked = f
|
||||
parseFluentEmojis(div);
|
||||
wrapKreaGalleries(div);
|
||||
_addReplyButton(div);
|
||||
_addDeleteButton(div, messageId, deletedOn);
|
||||
_addMarkButton(div, messageId, isMarked);
|
||||
chatEl().appendChild(div);
|
||||
scrollDown();
|
||||
|
||||
Reference in new issue
Block a user