feat: the panel's own confirm and prompt dialogs everywhere; the assistant's field is one line with a square send button
This commit is contained in:
1 parent
3f113ce8d6
commit
1f930e5aea
13 files changed
+104
-41
No files matched your search
@@ -6,6 +6,8 @@
|
||||
// Off until the user turns it on in the profile.
|
||||
(function () {
|
||||
'use strict';
|
||||
const PLANE = '<svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor"><path d="M2.4 10.6 20.6 3.3c.8-.3 1.5.4 1.2 1.2l-7.3 18.2c-.3.8-1.5.8-1.8 0l-2.6-6.9-6.9-2.6c-.8-.3-.8-1.5 0-1.8zm7.3 4.4 2 5.2L18 6 9.7 15z"/></svg>';
|
||||
const STOP = '<svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor"><rect x="5" y="5" width="14" height="14" rx="2"/></svg>';
|
||||
const esc = v => String(v).replace(/[&<>"]/g, c => ({'&': '&', '<': '<', '>': '>', '"': '"'}[c]));
|
||||
const fab = document.createElement('button');
|
||||
fab.type = 'button';
|
||||
@@ -22,8 +24,8 @@
|
||||
'<button type="button" class="btn ghost small ai-clear" title="Новый разговор">Очистить</button>' +
|
||||
'<button type="button" class="btn ghost small icon close x ai-close" aria-label="Закрыть"><svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.6" stroke-linecap="round"><path d="M18 6 6 18M6 6l12 12"/></svg></button></div>' +
|
||||
'<div class="ai-list"><p class="muted ai-hello">Спросите о том, что открыто в панели: ассистент видит вкладки и активный терминал или папку. Команды он предложит кнопкой «Выполнить».</p></div>' +
|
||||
'<form class="ai-form-in"><textarea rows="2" placeholder="Сообщение… (Enter - отправить, Shift+Enter - строка)"></textarea>' +
|
||||
'<button type="submit" class="ai-send">Отправить</button></form>';
|
||||
'<form class="ai-form-in"><textarea rows="1" placeholder="Сообщение…"></textarea>' +
|
||||
'<button type="submit" class="ai-send" title="Отправить" aria-label="Отправить">' + PLANE + '</button></form>';
|
||||
document.body.append(fab, box);
|
||||
const q = s => box.querySelector(s);
|
||||
const list = q('.ai-list'), input = q('textarea'), send = q('.ai-send');
|
||||
@@ -122,7 +124,7 @@
|
||||
const el = add('assistant', '');
|
||||
el.classList.add('wait');
|
||||
ac = new AbortController();
|
||||
send.textContent = 'Стоп';
|
||||
send.innerHTML = STOP; send.title = 'Остановить';
|
||||
let answer = '';
|
||||
try {
|
||||
const r = await fetch('/api/panel/ai/chat', {
|
||||
@@ -144,7 +146,7 @@
|
||||
if (e.name !== 'AbortError') answer += (answer ? '\n' : '') + '[ошибка: ' + (e.message || 'нет связи') + ']';
|
||||
} finally {
|
||||
ac = null;
|
||||
send.textContent = 'Отправить';
|
||||
send.innerHTML = PLANE; send.title = 'Отправить';
|
||||
el.classList.remove('wait');
|
||||
render(el, answer || '[нет ответа]', true);
|
||||
talk.push({role: 'assistant', content: answer});
|
||||
@@ -156,9 +158,13 @@
|
||||
const text = input.value.trim();
|
||||
if (!text) return;
|
||||
input.value = '';
|
||||
grow();
|
||||
where();
|
||||
ask(text);
|
||||
};
|
||||
// The field grows with the text, up to a few lines.
|
||||
function grow() { input.style.height = '40px'; input.style.height = Math.min(140, input.scrollHeight + 2) + 'px'; }
|
||||
input.addEventListener('input', grow);
|
||||
input.addEventListener('keydown', ev => {
|
||||
if (ev.key === 'Enter' && !ev.shiftKey && !ev.isComposing) { ev.preventDefault(); q('.ai-form-in').requestSubmit(); }
|
||||
});
|
||||
|
||||
Reference in new issue
Block a user