mishig HF Staff commited on
Commit
8452884
·
verified ·
1 Parent(s): cf9bf77

Tail-follow improvements in source

Browse files
Files changed (1) hide show
  1. src/lib/TraceViewer.svelte +28 -10
src/lib/TraceViewer.svelte CHANGED
@@ -87,6 +87,7 @@
87
  async function playback() {
88
  playing = true;
89
  skipFlag = false;
 
90
  for (let mi = 0; mi < messages.length; mi++) {
91
  if (skipFlag) break;
92
  const msg = messages[mi];
@@ -106,6 +107,7 @@
106
  await typeBlock(block);
107
  } else {
108
  await wait(90);
 
109
  followTail();
110
  }
111
  }
@@ -130,22 +132,34 @@
130
  for (let c = step; c < len; c += step) {
131
  if (skipFlag) break;
132
  block._typedText = full.slice(0, c);
133
- await wait(tickMs);
134
  followTail();
 
135
  }
136
  block._typedText = full;
137
- block._typing = false;
138
  followTail();
 
139
  }
140
 
141
- // Terminal-style tail: while content is streaming, keep the bottom of the
142
- // scroll container pinned but only if the user hasn't scrolled away.
 
 
 
 
143
  function followTail() {
144
- if (!listEl) return;
145
- const distanceFromBottom =
146
- listEl.scrollHeight - listEl.scrollTop - listEl.clientHeight;
147
- if (distanceFromBottom < 240) {
148
- listEl.scrollTop = listEl.scrollHeight;
 
 
 
 
 
 
149
  }
150
  }
151
 
@@ -303,7 +317,11 @@
303
  </div>
304
 
305
  <!-- messages / log -->
306
- <div bind:this={listEl} class="flex-1 overflow-y-auto px-5 py-3">
 
 
 
 
307
  {#if loading}
308
  <div class="flex items-baseline gap-2">
309
  <span class="w-[1ch] text-center text-[#5f5f5c]"
 
87
  async function playback() {
88
  playing = true;
89
  skipFlag = false;
90
+ userScrolledUp = false;
91
  for (let mi = 0; mi < messages.length; mi++) {
92
  if (skipFlag) break;
93
  const msg = messages[mi];
 
107
  await typeBlock(block);
108
  } else {
109
  await wait(90);
110
+ await tick();
111
  followTail();
112
  }
113
  }
 
132
  for (let c = step; c < len; c += step) {
133
  if (skipFlag) break;
134
  block._typedText = full.slice(0, c);
135
+ await tick();
136
  followTail();
137
+ await wait(tickMs);
138
  }
139
  block._typedText = full;
140
+ await tick();
141
  followTail();
142
+ block._typing = false;
143
  }
144
 
145
+ // Terminal-style tail: during playback, keep the scroll container pinned
146
+ // to the bottom so new content scrolls line-by-line into view. If the user
147
+ // actively scrolls up during playback, stop tailing until playback ends.
148
+ let userScrolledUp = false;
149
+ let lastAutoScrollTop = 0;
150
+
151
  function followTail() {
152
+ if (!listEl || !playing || userScrolledUp) return;
153
+ listEl.scrollTop = listEl.scrollHeight;
154
+ lastAutoScrollTop = listEl.scrollTop;
155
+ }
156
+
157
+ function onListScroll() {
158
+ if (!listEl || !playing) return;
159
+ // If the current scroll position differs from what we last wrote by more
160
+ // than a small threshold, treat it as user-initiated.
161
+ if (Math.abs(listEl.scrollTop - lastAutoScrollTop) > 40) {
162
+ userScrolledUp = true;
163
  }
164
  }
165
 
 
317
  </div>
318
 
319
  <!-- messages / log -->
320
+ <div
321
+ bind:this={listEl}
322
+ onscroll={onListScroll}
323
+ class="flex-1 overflow-y-auto px-5 py-3"
324
+ >
325
  {#if loading}
326
  <div class="flex items-baseline gap-2">
327
  <span class="w-[1ch] text-center text-[#5f5f5c]"