feat: the chat's arrow down midway between the answers' right edge and the chat's border, fading in and out

This commit is contained in:
redtea committed 2026-10-16 18:11:21 +03:00
1 parent 46f7f743b3
commit 4fb26a9652
2 files changed
+18 -5

No files matched your search

+15 -3
View File
@@ -25,7 +25,7 @@
'<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>' + '<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-chats" inert><div class="ai-chats-in"><button type="button" class="ai-chatnew">+ Новый чат</button><div class="ai-chatlist"></div></div></div>' + '<div class="ai-chats" inert><div class="ai-chats-in"><button type="button" class="ai-chatnew">+ Новый чат</button><div class="ai-chatlist"></div></div></div>' +
'<div class="ai-chat-body"><div class="ai-list"><div class="ai-hello"><span class="ai-hello-icon"><svg width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M14 9a2 2 0 0 1-2 2H6l-4 4V4a2 2 0 0 1 2-2h8a2 2 0 0 1 2 2z"/><path d="M18 9h2a2 2 0 0 1 2 2v11l-4-4h-6a2 2 0 0 1-2-2v-1"/></svg></span><span class="ai-hello-text">Спросите AI помощника</span></div></div>' + '<div class="ai-chat-body"><div class="ai-list"><div class="ai-hello"><span class="ai-hello-icon"><svg width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M14 9a2 2 0 0 1-2 2H6l-4 4V4a2 2 0 0 1 2-2h8a2 2 0 0 1 2 2z"/><path d="M18 9h2a2 2 0 0 1 2 2v11l-4-4h-6a2 2 0 0 1-2-2v-1"/></svg></span><span class="ai-hello-text">Спросите AI помощника</span></div></div>' +
'<button type="button" class="ai-jump" hidden title="К последним сообщениям" aria-label="К последним сообщениям"><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"><path d="M12 5v14M5 12l7 7 7-7"/></svg></button>' + '<button type="button" class="ai-jump" tabindex="-1" title="К последним сообщениям" aria-label="К последним сообщениям"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"><path d="M12 5v14M5 12l7 7 7-7"/></svg></button>' +
'<div class="ai-file-error" role="alert"></div></div>' + '<div class="ai-file-error" role="alert"></div></div>' +
'<div class="ai-pills">' + '<div class="ai-pills">' +
'<button type="button" class="ai-pill" data-mode="agent" aria-pressed="false" title="Сам выполняет шаги один за другим, спрашивая перед каждой командой и записью файла">' + '<button type="button" class="ai-pill" data-mode="agent" aria-pressed="false" title="Сам выполняет шаги один за другим, спрашивая перед каждой командой и записью файла">' +
@@ -47,7 +47,19 @@
let attachments = []; let attachments = [];
const importingPaneFiles = new Set(); const importingPaneFiles = new Set();
const nearBottom = () => list.scrollHeight - list.scrollTop - list.clientHeight < 80; const nearBottom = () => list.scrollHeight - list.scrollTop - list.clientHeight < 80;
function updateJump() { jump.hidden = nearBottom() || !isOpen(); } function updateJump() { showJump(!nearBottom() && isOpen()); }
// The arrow down fades in and out, midway between the right edge the
// answers reach (their max-width, 92% of the list) and the chat's border.
function showJump(on) {
if (on && !jump.classList.contains('show')) {
const body = list.parentNode.getBoundingClientRect(), l = list.getBoundingClientRect(), cs = getComputedStyle(list);
const pl = parseFloat(cs.paddingLeft), inner = list.clientWidth - pl - parseFloat(cs.paddingRight);
const gap = body.right - (l.left + list.clientLeft + pl + inner * 0.92);
jump.style.right = Math.max(2, (gap - jump.offsetWidth) / 2) + 'px';
}
jump.classList.toggle('show', on);
jump.tabIndex = on ? 0 : -1;
}
list.addEventListener('scroll', updateJump, {passive: true}); list.addEventListener('scroll', updateJump, {passive: true});
jump.onclick = () => { list.scrollTo({top: list.scrollHeight, behavior: 'smooth'}); }; jump.onclick = () => { list.scrollTo({top: list.scrollHeight, behavior: 'smooth'}); };
function scrollIfFollowing(follow) { if (follow) list.scrollTop = list.scrollHeight; updateJump(); } function scrollIfFollowing(follow) { if (follow) list.scrollTop = list.scrollHeight; updateJump(); }
@@ -217,7 +229,7 @@
drawChats(); drawChats();
list.querySelectorAll('.ai-msg').forEach(m => m.remove()); list.querySelectorAll('.ai-msg').forEach(m => m.remove());
list.scrollTop = 0; list.scrollTop = 0;
jump.hidden = true; showJump(false);
hideFileError(); hideFileError();
q('.ai-hello').hidden = !!id; q('.ai-hello').hidden = !!id;
if (!id) { updateJump(); return; } if (!id) { updateJump(); return; }
+3 -2
View File
@@ -868,9 +868,10 @@ body.touch.term-active .ai-fab { bottom:104px; }
.ai-hello-text { color:var(--muted); font-size:14px; font-weight:500; } .ai-hello-text { color:var(--muted); font-size:14px; font-weight:500; }
@keyframes ai-hello-in { from { opacity:0; transform:translateY(6px); } } @keyframes ai-hello-in { from { opacity:0; transform:translateY(6px); } }
.ai-list { height:100%; box-sizing:border-box; overflow:auto; padding:12px; display:flex; flex-direction:column; gap:10px; } .ai-list { height:100%; box-sizing:border-box; overflow:auto; padding:12px; display:flex; flex-direction:column; gap:10px; }
.ai-jump { position:absolute; right:14px; bottom:14px; z-index:2; width:38px; height:38px; padding:0; border-radius:50%; display:inline-flex; align-items:center; justify-content:center; color:var(--text); background:var(--panel); border:1px solid var(--border); box-shadow:0 4px 14px rgba(0,0,0,.35); } .ai-jump { position:absolute; right:4px; bottom:14px; z-index:2; width:32px; height:32px; padding:0; border-radius:50%; display:inline-flex; align-items:center; justify-content:center; color:var(--text); background:var(--panel); border:1px solid var(--border); box-shadow:0 4px 14px rgba(0,0,0,.35);
opacity:0; transform:translateY(8px) scale(.85); pointer-events:none; transition:opacity .2s ease, transform .2s ease, background-color .15s; }
.ai-jump.show { opacity:1; transform:none; pointer-events:auto; }
.ai-jump:hover { background:#202833; } .ai-jump:hover { background:#202833; }
.ai-jump[hidden] { display:none; }
.ai-msg { max-width:92%; padding:8px 11px; border-radius:10px; font-size:14px; line-height:1.45; overflow-wrap:anywhere; } .ai-msg { max-width:92%; padding:8px 11px; border-radius:10px; font-size:14px; line-height:1.45; overflow-wrap:anywhere; }
.ai-msg.user { align-self:flex-end; background:#16324f; white-space:normal; } .ai-msg.user { align-self:flex-end; background:#16324f; white-space:normal; }
.ai-user-copy { white-space:pre-wrap; } .ai-user-copy { white-space:pre-wrap; }