Vladyslav Humennyy commited on
Commit
e676b08
·
1 Parent(s): 732a3b9

Fix auto scroll

Browse files
Files changed (2) hide show
  1. app.py +1 -0
  2. 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';