Spaces:
Running
on
Zero
Running
on
Zero
Vladyslav Humennyy
commited on
Commit
·
e676b08
1
Parent(s):
732a3b9
Fix auto scroll
Browse files- app.py +1 -0
- static/script.js +85 -0
app.py
CHANGED
|
@@ -164,6 +164,7 @@ with gr.Blocks(theme=THEME, css=CSS, fill_height=True) as demo:
|
|
| 164 |
show_label=False,
|
| 165 |
# likeable=True,
|
| 166 |
allow_tags=["think"],
|
|
|
|
| 167 |
examples=[
|
| 168 |
{"text": i}
|
| 169 |
for i in [
|
|
|
|
| 164 |
show_label=False,
|
| 165 |
# likeable=True,
|
| 166 |
allow_tags=["think"],
|
| 167 |
+
elem_id="chatbot",
|
| 168 |
examples=[
|
| 169 |
{"text": i}
|
| 170 |
for i in [
|
static/script.js
CHANGED
|
@@ -1,6 +1,83 @@
|
|
| 1 |
(function () {
|
| 2 |
console.log("Keyboard shortcuts script loaded");
|
| 3 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4 |
const send = () => {
|
| 5 |
// Try multiple selectors to find the send button
|
| 6 |
const selectors = [
|
|
@@ -39,6 +116,11 @@
|
|
| 39 |
|
| 40 |
if (btn) {
|
| 41 |
console.log("Clicking send button");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 42 |
btn.click();
|
| 43 |
return true;
|
| 44 |
} else {
|
|
@@ -52,6 +134,9 @@
|
|
| 52 |
|
| 53 |
const setupKeyboardShortcuts = () => {
|
| 54 |
console.log("Setting up keyboard shortcuts");
|
|
|
|
|
|
|
|
|
|
| 55 |
|
| 56 |
document.addEventListener('keydown', (e) => {
|
| 57 |
const isCmdEnter = (e.metaKey || e.ctrlKey) && e.key === 'Enter';
|
|
|
|
| 1 |
(function () {
|
| 2 |
console.log("Keyboard shortcuts script loaded");
|
| 3 |
|
| 4 |
+
// --- Autoscroll control ---
|
| 5 |
+
let lastSendTs = 0;
|
| 6 |
+
let userPinnedScroll = false;
|
| 7 |
+
let chatContainer = null;
|
| 8 |
+
let chatObserver = null;
|
| 9 |
+
|
| 10 |
+
const now = () => Date.now();
|
| 11 |
+
const SEND_AUTOSCROLL_WINDOW_MS = 2500;
|
| 12 |
+
const NEAR_BOTTOM_PX = 120;
|
| 13 |
+
|
| 14 |
+
const getChatContainer = () => {
|
| 15 |
+
if (chatContainer && document.body.contains(chatContainer)) return chatContainer;
|
| 16 |
+
// Prefer explicit elem_id container
|
| 17 |
+
const root = document.getElementById('chatbot') || document.querySelector('#chatbot');
|
| 18 |
+
// Fallbacks: any visible gr-chatbot within left pane
|
| 19 |
+
const candidates = [];
|
| 20 |
+
if (root) candidates.push(root);
|
| 21 |
+
candidates.push(
|
| 22 |
+
document.querySelector('#left-pane .gr-chatbot'),
|
| 23 |
+
document.querySelector('.gr-chatbot'),
|
| 24 |
+
document.querySelector('#left-pane [data-testid="chatbot"]')
|
| 25 |
+
);
|
| 26 |
+
for (const el of candidates) {
|
| 27 |
+
if (!el) continue;
|
| 28 |
+
// Try common inner scroll area
|
| 29 |
+
let container = el.querySelector('[data-testid="bot"]') || el.querySelector('[data-testid="chatbot"]') || el;
|
| 30 |
+
// Walk down to the element that actually scrolls
|
| 31 |
+
const stack = [container];
|
| 32 |
+
while (stack.length) {
|
| 33 |
+
const cur = stack.shift();
|
| 34 |
+
if (!cur) continue;
|
| 35 |
+
const style = cur instanceof Element ? getComputedStyle(cur) : null;
|
| 36 |
+
const canScroll = style && (style.overflowY === 'auto' || style.overflowY === 'scroll');
|
| 37 |
+
if (canScroll && cur.scrollHeight > cur.clientHeight + 10) {
|
| 38 |
+
chatContainer = cur;
|
| 39 |
+
break;
|
| 40 |
+
}
|
| 41 |
+
if (cur.children && cur.children.length) stack.push(...cur.children);
|
| 42 |
+
}
|
| 43 |
+
if (!chatContainer) chatContainer = container;
|
| 44 |
+
if (chatContainer) break;
|
| 45 |
+
}
|
| 46 |
+
return chatContainer;
|
| 47 |
+
};
|
| 48 |
+
|
| 49 |
+
const isNearBottom = (el) => {
|
| 50 |
+
if (!el) return true;
|
| 51 |
+
const distance = el.scrollHeight - el.scrollTop - el.clientHeight;
|
| 52 |
+
return distance <= NEAR_BOTTOM_PX;
|
| 53 |
+
};
|
| 54 |
+
|
| 55 |
+
const scrollToBottom = (el) => {
|
| 56 |
+
if (!el) return;
|
| 57 |
+
el.scrollTo({ top: el.scrollHeight, behavior: 'auto' });
|
| 58 |
+
};
|
| 59 |
+
|
| 60 |
+
const attachScrollListener = () => {
|
| 61 |
+
const el = getChatContainer();
|
| 62 |
+
if (!el) return;
|
| 63 |
+
el.addEventListener('scroll', () => {
|
| 64 |
+
// If the user scrolls up away from bottom, pin the position
|
| 65 |
+
userPinnedScroll = !isNearBottom(el);
|
| 66 |
+
}, { passive: true });
|
| 67 |
+
};
|
| 68 |
+
|
| 69 |
+
const observeChat = () => {
|
| 70 |
+
const el = getChatContainer();
|
| 71 |
+
if (!el) return;
|
| 72 |
+
if (chatObserver) chatObserver.disconnect();
|
| 73 |
+
chatObserver = new MutationObserver(() => {
|
| 74 |
+
const withinSendWindow = now() - lastSendTs < SEND_AUTOSCROLL_WINDOW_MS;
|
| 75 |
+
const shouldScroll = withinSendWindow || (!userPinnedScroll && isNearBottom(el));
|
| 76 |
+
if (shouldScroll) scrollToBottom(el);
|
| 77 |
+
});
|
| 78 |
+
chatObserver.observe(el, { childList: true, subtree: true, characterData: true });
|
| 79 |
+
};
|
| 80 |
+
|
| 81 |
const send = () => {
|
| 82 |
// Try multiple selectors to find the send button
|
| 83 |
const selectors = [
|
|
|
|
| 116 |
|
| 117 |
if (btn) {
|
| 118 |
console.log("Clicking send button");
|
| 119 |
+
lastSendTs = now();
|
| 120 |
+
// When sending, allow autoscroll for initial response
|
| 121 |
+
userPinnedScroll = false;
|
| 122 |
+
// Ensure observers are up
|
| 123 |
+
setTimeout(() => { attachScrollListener(); observeChat(); }, 0);
|
| 124 |
btn.click();
|
| 125 |
return true;
|
| 126 |
} else {
|
|
|
|
| 134 |
|
| 135 |
const setupKeyboardShortcuts = () => {
|
| 136 |
console.log("Setting up keyboard shortcuts");
|
| 137 |
+
// Initialize observers once UI is present
|
| 138 |
+
setTimeout(() => { attachScrollListener(); observeChat(); }, 50);
|
| 139 |
+
setTimeout(() => { attachScrollListener(); observeChat(); }, 400);
|
| 140 |
|
| 141 |
document.addEventListener('keydown', (e) => {
|
| 142 |
const isCmdEnter = (e.metaKey || e.ctrlKey) && e.key === 'Enter';
|