fix(ai): adaptive max_tokens per backend (reasoning profiles get 1024+, native llama.cpp stays compact; per-profile max_tokens override); include entry texts in ai_status SSE notify so journal updates live without reload; audit log action filter; links pagination; settings validation & UI improvements
This commit is contained in:
+61
-13
@@ -84,7 +84,8 @@ async function detachFile(id) {
|
||||
const res = await fetch(`${API}/api/files/${id}/detach`, { method: 'POST', headers: hdr() });
|
||||
if (res.ok) {
|
||||
showToast('Файл откреплён');
|
||||
loadEntries();
|
||||
if (f.entry_id) refreshEntryCard(f.entry_id);
|
||||
else loadEntries();
|
||||
} else {
|
||||
const err = await res.json().catch(() => ({}));
|
||||
alert(err.error || 'Ошибка');
|
||||
@@ -161,6 +162,29 @@ function renderGrid() {
|
||||
renderIcons();
|
||||
}
|
||||
|
||||
function refreshEntryCardDom(e) {
|
||||
const btn = document.querySelector(`[data-edit="${e.id}"]`);
|
||||
const card = btn ? btn.closest('.card, .journal-card') : null;
|
||||
if (!card) return;
|
||||
const tmp = document.createElement('div');
|
||||
tmp.innerHTML = (view === 'cards' ? cardEntryHTML(e) : gridEntryHTML(e)).trim();
|
||||
const fresh = tmp.firstElementChild;
|
||||
fresh.classList.remove('entry-enter', 'entry-appear');
|
||||
fresh.style.animationDelay = '';
|
||||
card.replaceWith(fresh);
|
||||
renderIcons();
|
||||
}
|
||||
|
||||
async function refreshEntryCard(id) {
|
||||
const res = await fetch(`${API}/api/entries/${id}`, { headers: hdr() });
|
||||
if (!res.ok) return false;
|
||||
const e = await res.json();
|
||||
const idx = currentEntries.findIndex(x => x.id === e.id);
|
||||
if (idx !== -1) currentEntries[idx] = e;
|
||||
refreshEntryCardDom(e);
|
||||
return true;
|
||||
}
|
||||
|
||||
function setView(v) {
|
||||
view = v === 'cards' ? 'cards' : 'list';
|
||||
localStorage.setItem('journalView', view);
|
||||
@@ -236,26 +260,36 @@ function goPage(p) {
|
||||
|
||||
let liveSource = null;
|
||||
let liveReconnectTimer = null;
|
||||
let aiRefreshTimer = null;
|
||||
let entriesRefreshTimer = null;
|
||||
function applyAiUpdate(update) {
|
||||
const e = currentEntries.find(x => x.id === update.id);
|
||||
if (!e) return;
|
||||
e.ai_status = update.ai_status;
|
||||
e.ai_error = update.ai_error || null;
|
||||
if (update.ai_status === 'done') {
|
||||
if (aiRefreshTimer) return;
|
||||
aiRefreshTimer = setTimeout(() => { aiRefreshTimer = null; loadEntries(); }, 300);
|
||||
return;
|
||||
}
|
||||
if (update.description !== null && update.description !== undefined) e.description = update.description;
|
||||
if (update.description_ai !== null && update.description_ai !== undefined) e.description_ai = update.description_ai;
|
||||
if (update.description_original !== null && update.description_original !== undefined) e.description_original = update.description_original;
|
||||
if (update.ai_status === 'done' || update.ai_status === 'reverted') e.ai_checked_at = new Date().toISOString();
|
||||
document.querySelectorAll(`[data-ai-badge="${update.id}"]`).forEach(badgeEl => {
|
||||
badgeEl.outerHTML = aiBadge(e);
|
||||
const desc = badgeEl.closest('.desc');
|
||||
if (desc && update.description !== null && update.description !== undefined) {
|
||||
desc.textContent = e.description;
|
||||
desc.insertAdjacentHTML('beforeend', aiBadge(e));
|
||||
} else {
|
||||
badgeEl.outerHTML = aiBadge(e);
|
||||
}
|
||||
});
|
||||
renderIcons();
|
||||
const editBadge = document.getElementById('editAiStatus');
|
||||
if (editId === update.id && editBadge && editBadge.style.display !== 'none') {
|
||||
renderEditAiStatus(e);
|
||||
}
|
||||
if (editId === update.id) {
|
||||
const ta = document.getElementById('editDesc');
|
||||
if (ta && document.activeElement !== ta && update.description !== null && update.description !== undefined) {
|
||||
ta.value = e.description;
|
||||
}
|
||||
}
|
||||
}
|
||||
function connectLiveUpdates() {
|
||||
if (liveSource) { liveSource.close(); liveSource = null; }
|
||||
@@ -335,7 +369,7 @@ function renderEditPhotos(photos, mainPath) {
|
||||
if (res.ok) {
|
||||
div.remove();
|
||||
showToast('Фото удалено');
|
||||
loadEntries();
|
||||
refreshEntryCard(editId);
|
||||
} else {
|
||||
alert('Ошибка удаления');
|
||||
}
|
||||
@@ -368,7 +402,7 @@ async function applyMainPhoto(btn) {
|
||||
btn.style.background = '#16a34a';
|
||||
btn.style.color = '#fff';
|
||||
showToast('Главное фото обновлено');
|
||||
loadEntries();
|
||||
refreshEntryCard(editId);
|
||||
}
|
||||
|
||||
function closeEdit() {
|
||||
@@ -450,17 +484,29 @@ async function revertAi(id) {
|
||||
if (!confirm('Вернуть исходный текст записи (до ИИ-проверки)?')) return;
|
||||
const res = await fetch(`${API}/api/entries/${id}/ai/revert`, { method: 'POST', headers: hdr() });
|
||||
if (!res.ok) { const err = await res.json().catch(() => ({})); alert(err.error || 'Ошибка'); return; }
|
||||
const e = currentEntries.find(x => x.id === id);
|
||||
if (e) {
|
||||
if (e.description_original) e.description = e.description_original;
|
||||
e.description_ai = null;
|
||||
e.ai_status = 'reverted';
|
||||
e.ai_error = null;
|
||||
}
|
||||
showToast('Оригинал восстановлен');
|
||||
closeEdit();
|
||||
loadEntries();
|
||||
refreshEntryCard(id);
|
||||
}
|
||||
|
||||
async function recheckAi(id) {
|
||||
const res = await fetch(`${API}/api/entries/${id}/ai/recheck`, { method: 'POST', headers: hdr() });
|
||||
if (!res.ok) { const err = await res.json().catch(() => ({})); alert(err.error || 'Ошибка'); return; }
|
||||
const e = currentEntries.find(x => x.id === id);
|
||||
if (e) {
|
||||
e.ai_status = 'pending';
|
||||
e.ai_error = null;
|
||||
}
|
||||
showToast('Отправлено на ИИ-проверку');
|
||||
closeEdit();
|
||||
loadEntries();
|
||||
refreshEntryCard(id);
|
||||
}
|
||||
|
||||
async function saveEdit() {
|
||||
@@ -501,8 +547,10 @@ async function saveEdit() {
|
||||
}
|
||||
}
|
||||
|
||||
const id = editId;
|
||||
closeEdit();
|
||||
loadEntries();
|
||||
const refreshed = await refreshEntryCard(id);
|
||||
if (!refreshed) loadEntries();
|
||||
showToast('Запись обновлена');
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user