Commit 08c1003d by michaelpastushkov

fix

parent 7915bcb3
......@@ -7,7 +7,15 @@
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" />
<style>
/* Layout: header + main (scrolls) + composer; fixed footer */
:root {
/* tune these if footer/composer sizes change */
--footer-h: 2.75rem;
/* fixed footer height (approx) */
--composer-h: 6.5rem;
/* composer block height (approx) */
--bottom-pad: calc(var(--footer-h) + var(--composer-h) + 1rem);
}
body {
min-height: 100vh;
display: flex;
......@@ -26,31 +34,34 @@
flex: 1 1 auto;
overflow-y: auto;
position: relative;
/* leave room so fixed footer doesn’t overlap last messages */
padding-bottom: 3.5rem;
/* ≈ footer height */
padding-bottom: var(--bottom-pad);
/* leave room for composer + fixed footer */
}
.composer {
position: sticky;
bottom: 0;
bottom: var(--footer-h);
/* sit just above the fixed footer */
z-index: 1020;
background: #fff;
border-top: 1px solid #dee2e6;
}
/* --- Footer from your snippet (fixed, non-scrollable) --- */
footer {
position: fixed;
bottom: 0;
left: 0;
width: 100%;
z-index: 1010;
z-index: 1020;
font-size: .75rem;
color: #6c757d;
text-align: center;
padding: .5rem;
border-top: 1px solid #dee2e6;
background: #fdfdfd;
height: var(--footer-h);
box-sizing: border-box;
}
.cat-spinner {
......@@ -145,9 +156,10 @@
opacity: .98;
}
/* Sticky in-content scroll indicator (same pattern as previous file) */
/* Sticky in-content scroll indicator (same behavior as the earlier file) */
#scrollDownBtn {
position: sticky;
/* inside scrollable main */
margin-left: auto;
bottom: 1rem;
align-self: flex-end;
......@@ -212,7 +224,7 @@
</button>
</main>
<!-- Composer -->
<!-- Composer (sticks above fixed footer) -->
<div class="composer py-3">
<div class="container">
<form id="promptForm" class="d-flex flex-column gap-2">
......@@ -225,13 +237,13 @@
</div>
</div>
<!-- Disclaimer Footer (fixed, non-scrolling) -->
<!-- Fixed, non-scrollable footer (from your snippet) -->
<footer id="disclaimer"></footer>
<script src="https://cdn.jsdelivr.net/npm/marked@12.0.2/marked.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/dompurify@3.1.6/dist/purify.min.js"></script>
<script>
// --- i18n setup (kept) ---
// --- i18n setup (unchanged) ---
const DEFAULT_LOCALE = 'en';
const SUPPORTED = ['en', 'ru'];
let I18N = {};
......@@ -305,7 +317,7 @@
setWelcomeImage();
}
// --- UI elements ---
// --- UI refs ---
const ENDPOINT = '/api/stream';
const STOPPOINT = '/api/stop';
const CAT_GIF = '/static/images/cat-progress.gif';
......@@ -317,12 +329,11 @@
const welcomeLogo = document.getElementById('welcomeLogo');
const mainScroll = document.getElementById('mainScroll');
const scrollDownBtn = document.getElementById('scrollDownBtn');
const BOTTOM_EPS = 50;
let firstRequestDone = false;
let controller = null;
// --- Content rendering helpers ---
// --- helpers ---
function renderMarkdown(targetEl, markdownText) {
const html = marked.parse(markdownText, { breaks: true, gfm: true, headerIds: false });
targetEl.innerHTML = DOMPurify.sanitize(html, { USE_PROFILES: { html: true } });
......@@ -340,22 +351,13 @@
if (img) targetEl.innerHTML = '';
}
// --- Scroll indicator logic (mirrors the first script) ---
function needsScroll() {
return mainScroll.scrollHeight - mainScroll.clientHeight > 8;
}
function atBottom() {
return mainScroll.scrollTop + mainScroll.clientHeight >= mainScroll.scrollHeight - BOTTOM_EPS;
}
function isNearBottom() { return atBottom(); } // alias for clarity
// --- Scroll indicator (same behavior as before) ---
function needsScroll() { return mainScroll.scrollHeight - mainScroll.clientHeight > 8; }
function atBottom() { return mainScroll.scrollTop + mainScroll.clientHeight >= mainScroll.scrollHeight - 50; }
function updateScrollIndicator() {
if (needsScroll() && !atBottom()) {
scrollDownBtn.classList.add('show');
} else {
scrollDownBtn.classList.remove('show');
}
if (needsScroll() && !atBottom()) scrollDownBtn.classList.add('show');
else scrollDownBtn.classList.remove('show');
}
// scroll one viewport down on click
scrollDownBtn.addEventListener('click', () => {
mainScroll.scrollBy({ top: mainScroll.clientHeight, behavior: 'smooth' });
setTimeout(updateScrollIndicator, 320);
......@@ -363,26 +365,24 @@
mainScroll.addEventListener('scroll', updateScrollIndicator);
window.addEventListener('resize', updateScrollIndicator);
// --- Composer lock / buttons ---
// --- Composer lock ---
function setComposerLocked(locked) {
isRunning = locked;
inputText.placeholder = locked
? t('status.thinking', 'Thinking…')
: t('composer.placeholder', 'What can I help you with?');
inputText.placeholder = locked ? t('status.thinking', 'Thinking…') : t('composer.placeholder', 'What can I help you with?');
inputText.disabled = locked;
goBtn.style.display = locked ? 'none' : 'inline-block';
stopBtn.style.display = locked ? 'inline-block' : 'none';
if (locked) inputText.blur(); else inputText.focus();
}
// --- App flow ---
// --- Flow ---
async function start() {
const prompt = inputText.value.trim();
if (!prompt) return;
if (!firstRequestDone) { welcomeLogo?.remove(); firstRequestDone = true; }
const shouldAutoScroll = isNearBottom();
const shouldAutoScroll = atBottom();
const userMsg = document.createElement('div');
userMsg.className = 'message user-message';
......@@ -422,8 +422,7 @@
rafPending = false;
if (!receivedAny) { clearThinkingCatIfPresent(assistantMsg); receivedAny = true; }
renderMarkdown(assistantMsg, rawMarkdown);
if (shouldAutoScroll && isNearBottom()) {
if (shouldAutoScroll && atBottom()) {
mainScroll.scrollTop = mainScroll.scrollHeight;
}
updateScrollIndicator();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment