This commit is contained in:
+77
-77
@@ -14,7 +14,7 @@ var VM_UPLOAD_URL = 'https://contracts-upload.sinefo.ru/contracts-upload/';
|
||||
// Прогрев приложения при загрузке страницы.
|
||||
// ⚠️ Без прогрева первый POST может упасть с ERR_CONNECTION_RESET (MTU/Geneve)
|
||||
(async function init() {
|
||||
try { await fetch('/health'); } catch(e) { /* ignore */ }
|
||||
try { await fetch('/health'); } catch (e) { /* ignore */ }
|
||||
})();
|
||||
|
||||
/**
|
||||
@@ -31,15 +31,15 @@ filePicker = window.FilePicker.initFilePicker({
|
||||
allowedExt: ['.pdf', '.doc', '.docx'],
|
||||
limits: FILE_PICKER_LIMITS,
|
||||
onChange: onPickerFilesChange,
|
||||
onError: function(error, file) {
|
||||
onError: function (error, file) {
|
||||
filePicker.setMessage('Ошибка чтения ' + file.name + ': ' + error.message);
|
||||
},
|
||||
onDetails: function(id) {
|
||||
var index = state.files.findIndex(function(file) { return file.pickerId === id; });
|
||||
onDetails: function (id) {
|
||||
var index = state.files.findIndex(function (file) { return file.pickerId === id; });
|
||||
if (index >= 0) toggleClassifyDetail(index);
|
||||
},
|
||||
onText: function(id) {
|
||||
var index = state.files.findIndex(function(file) { return file.pickerId === id; });
|
||||
onText: function (id) {
|
||||
var index = state.files.findIndex(function (file) { return file.pickerId === id; });
|
||||
if (index >= 0) showText(index);
|
||||
}
|
||||
});
|
||||
@@ -59,10 +59,10 @@ function renderStepper(state) {
|
||||
var steps = state.ui.steps;
|
||||
var map = { upload: 'stepUpload', classify: 'stepClassify', groups: 'stepGroups', compare: 'stepCompare' };
|
||||
var colors = { '○': 'var(--topbar-muted)', '⏳': 'var(--topbar-active)', '✓': 'var(--topbar-done)' };
|
||||
Object.keys(map).forEach(function(key) {
|
||||
Object.keys(map).forEach(function (key) {
|
||||
var el = document.getElementById(map[key]);
|
||||
if (el) {
|
||||
el.textContent = steps[key] + ' ' + {upload:'Загрузка',classify:'Классификация',groups:'Группировка',compare:'Сравнение'}[key];
|
||||
el.textContent = steps[key] + ' ' + { upload: 'Загрузка', classify: 'Классификация', groups: 'Группировка', compare: 'Сравнение' }[key];
|
||||
el.style.color = colors[steps[key]] || 'var(--muted)';
|
||||
}
|
||||
});
|
||||
@@ -92,10 +92,10 @@ function stepActive(id) {
|
||||
* Мутирует state.ui.steps, вызывает renderStepper.
|
||||
*/
|
||||
function resetStepper(fromId) {
|
||||
var order = ['stepUpload','stepClassify','stepGroups','stepCompare'];
|
||||
var keys = ['upload','classify','groups','compare'];
|
||||
var order = ['stepUpload', 'stepClassify', 'stepGroups', 'stepCompare'];
|
||||
var keys = ['upload', 'classify', 'groups', 'compare'];
|
||||
var reset = false;
|
||||
order.forEach(function(id, i) {
|
||||
order.forEach(function (id, i) {
|
||||
if (id === fromId) reset = true;
|
||||
if (reset) { state.ui.steps[keys[i]] = '○'; }
|
||||
});
|
||||
@@ -133,7 +133,7 @@ async function runClassify() {
|
||||
stepActive('stepClassify');
|
||||
|
||||
var start = Date.now();
|
||||
var timer = setInterval(function() {
|
||||
var timer = setInterval(function () {
|
||||
btn.innerHTML = '<span class="spinner"></span> Классификация... ' + Math.round((Date.now() - start) / 1000) + 'с';
|
||||
}, 1000);
|
||||
|
||||
@@ -141,7 +141,7 @@ async function runClassify() {
|
||||
var classifyDone = false;
|
||||
|
||||
// Poll progress каждые 2с — работает и для sync, и для async classify
|
||||
var pollTimer = setInterval(async function() {
|
||||
var pollTimer = setInterval(async function () {
|
||||
try {
|
||||
var progressUrl = state.v3Mode
|
||||
? '/api/classify-progress?batch=' + encodeURIComponent(state.batchId)
|
||||
@@ -171,8 +171,8 @@ async function runClassify() {
|
||||
}
|
||||
if (d.ok && d.counts) {
|
||||
var done = (d.counts.classified || 0)
|
||||
+ (d.counts.failed || 0)
|
||||
+ (d.counts.garbage || 0);
|
||||
+ (d.counts.failed || 0)
|
||||
+ (d.counts.garbage || 0);
|
||||
var total = d.total || 0;
|
||||
if (total > 0) totalFiles = total;
|
||||
btn.innerHTML = '<span class="spinner"></span> Классификация... ' + done + '/' + total + ' (' + Math.round((Date.now() - start) / 1000) + 'с)';
|
||||
@@ -186,14 +186,14 @@ async function runClassify() {
|
||||
loadGroupsAction();
|
||||
}
|
||||
}
|
||||
} catch(e) {}
|
||||
} catch (e) { }
|
||||
}, 2000);
|
||||
|
||||
try {
|
||||
var resp = await fetch(VM_API + '/api/classify-batch', {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({batch_id: state.batchId})
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ batch_id: state.batchId })
|
||||
});
|
||||
var data = await resp.json();
|
||||
state.v3Mode = Boolean(data.v3);
|
||||
@@ -211,7 +211,7 @@ async function runClassify() {
|
||||
if (data.v3) summary += ' | арифметика: ' + data.math_checks_passed;
|
||||
if (data.v3 && data.failed) summary += ' | ошибок: ' + data.failed;
|
||||
if (data.garbage) summary += ' | 🗑️ ' + data.garbage + ' мусор';
|
||||
if (data.types) summary += ' | ' + Object.entries(data.types).map(function(e){return e[1]+'×'+e[0];}).join(', ');
|
||||
if (data.types) summary += ' | ' + Object.entries(data.types).map(function (e) { return e[1] + '×' + e[0]; }).join(', ');
|
||||
btn.innerHTML = '✓ ' + summary + ' (' + Math.round((Date.now() - start) / 1000) + 'с)';
|
||||
stepDone('stepClassify');
|
||||
stepActive('stepGroups');
|
||||
@@ -231,7 +231,7 @@ async function runClassify() {
|
||||
btn.innerHTML = '✗ ' + (data.error || 'ошибка');
|
||||
btn.disabled = false;
|
||||
}
|
||||
} catch(e) {
|
||||
} catch (e) {
|
||||
if (!classifyDone) {
|
||||
clearInterval(timer);
|
||||
clearInterval(pollTimer);
|
||||
@@ -246,7 +246,7 @@ async function runClassify() {
|
||||
// Текущий активный EventSource (только один одновременно)
|
||||
// state._activeCompare — теперь в state.js (Фаза 0: state + render)
|
||||
|
||||
window.runCompareForGroup = async function(gi, btn) {
|
||||
window.runCompareForGroup = async function (gi, btn) {
|
||||
var group = state.groups[gi];
|
||||
if (!group || group.contract_number === '__unresolved__') return;
|
||||
|
||||
@@ -265,17 +265,17 @@ window.runCompareForGroup = async function(gi, btn) {
|
||||
btn.disabled = true;
|
||||
btn.innerHTML = '<span class="spinner"></span> Применяю...';
|
||||
// Заблокировать ВСЕ кнопки сравнения пока идёт процесс
|
||||
document.querySelectorAll('.cmp-btn').forEach(function(b) { b.disabled = true; });
|
||||
document.querySelectorAll('.cmp-btn').forEach(function (b) { b.disabled = true; });
|
||||
|
||||
var ar = await fetch(VM_API + '/api/apply-groups', {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({batch_id: state.batchId, groups: [group]})
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ batch_id: state.batchId, groups: [group] })
|
||||
});
|
||||
var ad = await ar.json();
|
||||
if (!ad.ok || !ad.contract_ids || !ad.contract_ids.length) {
|
||||
btn.innerHTML = '✗ ошибка'; btn.disabled = false;
|
||||
document.querySelectorAll('.cmp-btn').forEach(function(b) { b.disabled = false; });
|
||||
document.querySelectorAll('.cmp-btn').forEach(function (b) { b.disabled = false; });
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -306,7 +306,7 @@ window.runCompareForGroup = async function(gi, btn) {
|
||||
cmpBody,
|
||||
statusEl,
|
||||
{
|
||||
onDone: function(d, sections) {
|
||||
onDone: function (d, sections) {
|
||||
state._activeCompare.timer = null;
|
||||
state._activeCompare.es = null;
|
||||
group.compare = {
|
||||
@@ -316,23 +316,23 @@ window.runCompareForGroup = async function(gi, btn) {
|
||||
sections: sections
|
||||
};
|
||||
renderGroups(state);
|
||||
document.querySelectorAll('.cmp-btn').forEach(function(b) { b.disabled = false; });
|
||||
document.querySelectorAll('.cmp-btn').forEach(function (b) { b.disabled = false; });
|
||||
stepDone('stepCompare');
|
||||
},
|
||||
onError: function(d) {
|
||||
onError: function (d) {
|
||||
state._activeCompare.timer = null;
|
||||
state._activeCompare.es = null;
|
||||
group.compare = null;
|
||||
cmpBody.innerHTML += '<div style="color:var(--destructive);">✗ ' + escHtml(d.message) + '</div>';
|
||||
btn.innerHTML = '✗'; btn.disabled = false;
|
||||
document.querySelectorAll('.cmp-btn').forEach(function(b) { b.disabled = false; });
|
||||
document.querySelectorAll('.cmp-btn').forEach(function (b) { b.disabled = false; });
|
||||
},
|
||||
onConnectionError: function() {
|
||||
onConnectionError: function () {
|
||||
state._activeCompare.timer = null;
|
||||
state._activeCompare.es = null;
|
||||
group.compare = null;
|
||||
btn.innerHTML = '✗ соединение'; btn.disabled = false;
|
||||
document.querySelectorAll('.cmp-btn').forEach(function(b) { b.disabled = false; });
|
||||
document.querySelectorAll('.cmp-btn').forEach(function (b) { b.disabled = false; });
|
||||
}
|
||||
}
|
||||
);
|
||||
@@ -343,7 +343,7 @@ window.runCompareForGroup = async function(gi, btn) {
|
||||
// uploadFile → files.js (Фаза 1)
|
||||
|
||||
// ── LLM: Общее сравнение (Event Sourcing) ────────────────────────
|
||||
document.getElementById('llmBtn').addEventListener('click', function() {
|
||||
document.getElementById('llmBtn').addEventListener('click', function () {
|
||||
if (!state.contractId) return;
|
||||
var btn = this;
|
||||
btn.disabled = true;
|
||||
@@ -356,13 +356,13 @@ document.getElementById('llmBtn').addEventListener('click', function() {
|
||||
compareBody.innerHTML = '';
|
||||
compareStatus.textContent = '⏳ 0.0с';
|
||||
|
||||
var order = state.files.map(function(f) { return f.supp_id; }).filter(Boolean).join(',');
|
||||
var order = state.files.map(function (f) { return f.supp_id; }).filter(Boolean).join(',');
|
||||
var url = '/process-v2?contract_id=' + state.contractId +
|
||||
(order ? '&order=' + encodeURIComponent(order) : '');
|
||||
|
||||
// Фаза 3: унифицированный SSE через startCompareSSE (compare.js)
|
||||
startCompareSSE(url, compareBody, compareStatus, {
|
||||
onDone: function(d) {
|
||||
onDone: function (d) {
|
||||
btn.innerHTML = '<i data-lucide="check" style="width:16px;height:16px;"></i> ✓ Готово';
|
||||
stepDone('stepCompare');
|
||||
if (d.total_unresolved > 0) {
|
||||
@@ -371,12 +371,12 @@ document.getElementById('llmBtn').addEventListener('click', function() {
|
||||
document.getElementById('chatCard').style.display = 'block';
|
||||
lucide.createIcons();
|
||||
},
|
||||
onError: function(d) {
|
||||
onError: function (d) {
|
||||
compareBody.innerHTML += '<div style="color:var(--destructive);margin-top:8px;">✗ ' + escHtml(d.message) + '</div>';
|
||||
btn.innerHTML = '<i data-lucide="scale" style="width:16px;height:16px;"></i> Общее сравнение';
|
||||
btn.disabled = false;
|
||||
},
|
||||
onConnectionError: function() {
|
||||
onConnectionError: function () {
|
||||
if (compareBody.innerHTML === '') {
|
||||
compareBody.innerHTML = '<div style="color:var(--destructive);">✗ Ошибка соединения</div>';
|
||||
}
|
||||
@@ -390,7 +390,7 @@ document.getElementById('llmBtn').addEventListener('click', function() {
|
||||
// refreshSupps → files.js (Фаза 1)
|
||||
|
||||
// ── Чат ──────────────────────────────────────────────────────
|
||||
document.getElementById('chatSend').addEventListener('click', function() {
|
||||
document.getElementById('chatSend').addEventListener('click', function () {
|
||||
var input = document.getElementById('chatInput');
|
||||
var q = input.value.trim();
|
||||
if (!q || !state.contractId) return;
|
||||
@@ -404,21 +404,21 @@ document.getElementById('chatSend').addEventListener('click', function() {
|
||||
var fd = new FormData();
|
||||
fd.append('question', q);
|
||||
fetch('/chat.cfm?contract_id=' + state.contractId, { method: 'POST', body: fd })
|
||||
.then(function(r) { return r.json(); })
|
||||
.then(function(d) {
|
||||
.then(function (r) { return r.json(); })
|
||||
.then(function (d) {
|
||||
msgs.lastChild.remove();
|
||||
msgs.innerHTML += '<div style="margin-top:2px;"><strong>Ответ:</strong> ' + (d.OK ? d.ANSWER : (d.ERROR || '—')) + '</div>';
|
||||
input.disabled = false;
|
||||
document.getElementById('chatSend').disabled = false;
|
||||
input.focus();
|
||||
}).catch(function() {
|
||||
}).catch(function () {
|
||||
msgs.lastChild.remove();
|
||||
msgs.innerHTML += '<div style="color:var(--destructive);margin-top:2px;">Ошибка сети</div>';
|
||||
input.disabled = false;
|
||||
document.getElementById('chatSend').disabled = false;
|
||||
});
|
||||
});
|
||||
document.getElementById('chatInput').addEventListener('keydown', function(e) {
|
||||
document.getElementById('chatInput').addEventListener('keydown', function (e) {
|
||||
if (e.key === 'Enter') document.getElementById('chatSend').click();
|
||||
});
|
||||
|
||||
@@ -454,24 +454,24 @@ async function showText(i) {
|
||||
|
||||
// Общий textify для обеих панелей
|
||||
var textLines = [];
|
||||
elements.forEach(function(el) {
|
||||
elements.forEach(function (el) {
|
||||
var etype = el.type || el.TYPE || '?';
|
||||
if (etype === 'paragraph') {
|
||||
textLines.push(el.text || el.TEXT || '');
|
||||
} else if (etype === 'table') {
|
||||
var rows = el.rows || el.ROWS || [];
|
||||
if (rows.length > 0) {
|
||||
var ncols = (rows[0]||[]).length;
|
||||
var ncols = (rows[0] || []).length;
|
||||
textLines.push('--- Таблица ' + rows.length + '×' + ncols + ' ---');
|
||||
rows.forEach(function(row) {
|
||||
var cells = (row||[]).map(function(c){ return String(c||'').replace(/\n/g,' ').replace(/\|/g,'\\|'); });
|
||||
rows.forEach(function (row) {
|
||||
var cells = (row || []).map(function (c) { return String(c || '').replace(/\n/g, ' ').replace(/\|/g, '\\|'); });
|
||||
textLines.push('| ' + cells.join(' | ') + ' |');
|
||||
});
|
||||
textLines.push('');
|
||||
}
|
||||
}
|
||||
});
|
||||
var textify = textLines.join('\n').replace(/</g,'<').replace(/>/g,'>');
|
||||
var textify = textLines.join('\n').replace(/</g, '<').replace(/>/g, '>');
|
||||
|
||||
// Левая: сырой текст (textify)
|
||||
leftHtml = '<pre>' + textify + '</pre>';
|
||||
@@ -481,7 +481,7 @@ async function showText(i) {
|
||||
rightHtml += '<div class="kv"><span class="k">Изменён</span><span class="v">' + formatDate(f.lastModified) + '</span></div>';
|
||||
if (f.parseInfo) {
|
||||
var d = f.parseInfo;
|
||||
rightHtml += '<div class="kv"><span class="k">Элементов</span><span class="v">' + (d.element_count||0) + '</span></div>';
|
||||
rightHtml += '<div class="kv"><span class="k">Элементов</span><span class="v">' + (d.element_count || 0) + '</span></div>';
|
||||
if (d.error) rightHtml += '<div style="margin-top:8px;color:var(--destructive);">' + d.error + '</div>';
|
||||
}
|
||||
rightHtml += '<div style="margin-top:10px;font-weight:600;font-size:12px;">Все элементы:</div>';
|
||||
@@ -499,13 +499,13 @@ async function showText(i) {
|
||||
if (qData.doc_date) rightHtml += '<div class="kv"><span class="k">Дата</span><span class="v">' + escHtml(qData.doc_date) + '</span></div>';
|
||||
if (qData.counterparty) rightHtml += '<div class="kv"><span class="k">Контрагент</span><span class="v">' + escHtml(qData.counterparty) + '</span></div>';
|
||||
if (qData.classify_raw) {
|
||||
var rawId = 'raw_' + docId.replace(/-/g,'');
|
||||
var rawId = 'raw_' + docId.replace(/-/g, '');
|
||||
rightHtml += '<details style="margin-top:8px;"><summary style="cursor:pointer;font-size:11px;color:var(--brand-primary);">Сырой ответ LLM</summary>';
|
||||
rightHtml += '<pre style="font-size:10px;max-height:200px;overflow:auto;background:var(--brand-grey-light);padding:6px;border-radius:4px;margin-top:4px;">' + escHtml(qData.classify_raw) + '</pre>';
|
||||
rightHtml += '</details>';
|
||||
}
|
||||
}
|
||||
} catch(e) {
|
||||
} catch (e) {
|
||||
leftHtml = '<span style="color:var(--destructive);">Ошибка</span>';
|
||||
}
|
||||
} else {
|
||||
@@ -535,11 +535,11 @@ var promptNotes = document.getElementById('promptNotes');
|
||||
// extract — извлечение строк из первого документа (базовый договор)
|
||||
// diff — сравнение допсоглашений (ADD/UPDATE/DELETE)
|
||||
// classify — классификация документа (тип, номер, контрагент, дата)
|
||||
var activePromptId = {extract: '', diff: '', classify: ''};
|
||||
var activePromptId = { extract: '', diff: '', classify: '' };
|
||||
|
||||
function openPromptModal(role) {
|
||||
promptRole = role;
|
||||
var labels = {extract: 'Извлечение', diff: 'Сравнение', classify: 'Классификация'};
|
||||
var labels = { extract: 'Извлечение', diff: 'Сравнение', classify: 'Классификация' };
|
||||
promptModalTitle.textContent = 'Промпт: ' + (labels[role] || role);
|
||||
document.getElementById('promptTabExtract').style.background = role === 'extract' ? 'var(--brand-primary)' : '';
|
||||
document.getElementById('promptTabExtract').style.color = role === 'extract' ? '#fff' : '';
|
||||
@@ -561,26 +561,26 @@ function closePromptModal(e) {
|
||||
|
||||
function loadPrompt(role) {
|
||||
fetch(VM_API + '/api/prompts?role=' + role)
|
||||
.then(function(r) { return r.json(); })
|
||||
.then(function(d) {
|
||||
.then(function (r) { return r.json(); })
|
||||
.then(function (d) {
|
||||
if (d.ok) {
|
||||
promptEditor.value = d.body || '';
|
||||
activePromptId[role] = d.id || '';
|
||||
loadHistory(role);
|
||||
}
|
||||
})
|
||||
.catch(function(e) { console.error('loadPrompt:', e); });
|
||||
.catch(function (e) { console.error('loadPrompt:', e); });
|
||||
}
|
||||
|
||||
function loadHistory(role) {
|
||||
fetch(VM_API + '/api/prompts/list?role=' + role)
|
||||
.then(function(r) { return r.json(); })
|
||||
.then(function(d) {
|
||||
.then(function (r) { return r.json(); })
|
||||
.then(function (d) {
|
||||
if (!d.ok || !d.versions || !d.versions.length) {
|
||||
promptHistory.innerHTML = '<div style="color:var(--muted);">нет версий</div>';
|
||||
return;
|
||||
}
|
||||
promptHistory.innerHTML = d.versions.map(function(v) {
|
||||
promptHistory.innerHTML = d.versions.map(function (v) {
|
||||
var isActive = v.is_active ? '<span style="display:inline-block;width:8px;height:8px;border-radius:50%;background:var(--green);margin-right:4px;" title="активная"></span> ' : '';
|
||||
var style = v.is_active ? 'font-weight:600;' : '';
|
||||
return '<div style="display:flex;align-items:center;padding:4px 0;border-bottom:1px solid var(--brand-gray);font-size:11px;' + style + '">'
|
||||
@@ -592,17 +592,17 @@ function loadHistory(role) {
|
||||
+ '</div>';
|
||||
}).join('');
|
||||
})
|
||||
.catch(function(e) { console.error('loadHistory:', e); });
|
||||
.catch(function (e) { console.error('loadHistory:', e); });
|
||||
}
|
||||
|
||||
function activatePrompt(id) {
|
||||
fetch(VM_API + '/api/prompts/activate', {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({id: id})
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ id: id })
|
||||
})
|
||||
.then(function(r) { return r.json(); })
|
||||
.then(function(d) {
|
||||
.then(function (r) { return r.json(); })
|
||||
.then(function (d) {
|
||||
if (d.ok) { loadPrompt(promptRole); promptStatus.textContent = '✓ активирован'; }
|
||||
else { promptStatus.textContent = '✕ ' + (d.error || 'ошибка'); }
|
||||
});
|
||||
@@ -612,30 +612,30 @@ function deletePrompt(id) {
|
||||
if (!confirm('Удалить эту версию промпта?')) return;
|
||||
fetch(VM_API + '/api/prompts/delete', {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({id: id})
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ id: id })
|
||||
})
|
||||
.then(function(r) { return r.json(); })
|
||||
.then(function(d) {
|
||||
.then(function (r) { return r.json(); })
|
||||
.then(function (d) {
|
||||
if (d.ok) { loadHistory(promptRole); promptStatus.textContent = '✓ удалено'; }
|
||||
else { promptStatus.textContent = '✕ ' + (d.error || 'ошибка'); }
|
||||
});
|
||||
}
|
||||
|
||||
document.getElementById('promptSave').addEventListener('click', function() {
|
||||
document.getElementById('promptSave').addEventListener('click', function () {
|
||||
var body = promptEditor.value.trim();
|
||||
if (!body) { promptStatus.textContent = '✕ тело промпта пустое'; return; }
|
||||
var name = promptName.value.trim() || ('v' + new Date().toISOString().replace(/[:.]/g,'-').substring(0,16));
|
||||
var name = promptName.value.trim() || ('v' + new Date().toISOString().replace(/[:.]/g, '-').substring(0, 16));
|
||||
fetch(VM_API + '/api/prompts/save', {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
role: promptRole, name: name, body: body,
|
||||
notes: promptNotes.value.trim(), is_active: true
|
||||
})
|
||||
})
|
||||
.then(function(r) { return r.json(); })
|
||||
.then(function(d) {
|
||||
.then(function (r) { return r.json(); })
|
||||
.then(function (d) {
|
||||
if (d.ok) {
|
||||
promptStatus.textContent = '✓ сохранено (новая версия)';
|
||||
promptName.value = ''; promptNotes.value = '';
|
||||
@@ -646,10 +646,10 @@ document.getElementById('promptSave').addEventListener('click', function() {
|
||||
});
|
||||
});
|
||||
|
||||
document.getElementById('promptTabExtract').addEventListener('click', function() { openPromptModal('extract'); });
|
||||
document.getElementById('promptTabDiff').addEventListener('click', function() { openPromptModal('diff'); });
|
||||
document.getElementById('promptTabClassify').addEventListener('click', function() { openPromptModal('classify'); });
|
||||
document.getElementById('promptBtnExtract').addEventListener('click', function() { openPromptModal('extract'); });
|
||||
document.getElementById('promptBtnDiff').addEventListener('click', function() { openPromptModal('diff'); });
|
||||
document.getElementById('promptBtnClassify').addEventListener('click', function() { openPromptModal('classify'); });
|
||||
document.getElementById('promptTabExtract').addEventListener('click', function () { openPromptModal('extract'); });
|
||||
document.getElementById('promptTabDiff').addEventListener('click', function () { openPromptModal('diff'); });
|
||||
document.getElementById('promptTabClassify').addEventListener('click', function () { openPromptModal('classify'); });
|
||||
document.getElementById('promptBtnExtract').addEventListener('click', function () { openPromptModal('extract'); });
|
||||
document.getElementById('promptBtnDiff').addEventListener('click', function () { openPromptModal('diff'); });
|
||||
document.getElementById('promptBtnClassify').addEventListener('click', function () { openPromptModal('classify'); });
|
||||
lucide.createIcons();
|
||||
|
||||
Reference in New Issue
Block a user