## Summary
Clean rewrite of the log viewer panes to fix auto-scroll behavior.
### Changes
- Track scroll position per log pane (container logs, build logs, deployment cards)
- Auto-scroll to bottom only when user is already at bottom (tail-follow behavior)
- When user scrolls up to review earlier output, auto-scroll pauses automatically
- Show a "↓ Follow" button when auto-scroll is paused; clicking it resumes
- Only trigger scroll on actual content changes (skip no-op updates)
- Use `overflow-y: auto` for proper scrollable containers
- Add `break-words` to prevent horizontal overflow on long lines
Closes #17
- Track scroll position per log pane (container logs, build logs, deployment cards)
- Auto-scroll to bottom only when user is already at bottom (tail-follow)
- When user scrolls up to review earlier output, pause auto-scroll
- Show a '↓ Follow' button when auto-scroll is paused; clicking resumes
- Only scroll on actual content changes (skip no-op updates)
- Use overflow-y: auto for proper scrollable containers
- Add break-words to prevent horizontal overflow on long lines
Closes#17
Smart auto-scroll behavior: tracks whether the user has scrolled up, only auto-scrolls when at bottom. This is the expected UX pattern for log viewers.
"↓ Follow" button provides a clear way to re-enable auto-scroll — good discoverability.
isScrolledToBottom uses a 30px tolerance — good for handling subpixel rendering differences.
{ passive: true } on scroll listeners is a nice performance touch.
break-words and m-0 on <pre> elements fix potential overflow issues.
Only scrolls when content actually changes (changed flag) — avoids unnecessary DOM thrashing.
Consistent implementation across all three log panes (container, build, deployment).
Observations
Scroll listener not cleaned up: The addEventListener calls in _initScrollTracking and the deployment view's init never have corresponding removeEventListener. For SPAs with Alpine this is usually fine (elements are long-lived), but if components are ever destroyed/recreated, listeners would accumulate.
Polling interval: The 1-second setInterval for fetchAll() was pre-existing, but combined with the new scroll tracking and change detection, this is now more efficient since unnecessary scroll operations are skipped.
Double RAF removed: The old scrollToBottom used double requestAnimationFrame; now it's single. If there were edge cases where double RAF was needed (layout thrashing), they might resurface — but single RAF is generally correct.
_autoScroll naming: Properties prefixed with _ suggest private, which is a good convention in Alpine. Consistent across all panes.
Verdict: Approve. This is a well-thought-out UX fix that follows standard log viewer conventions. The code is clean and consistent.
## Review: PR#27 — Rewrite log viewer panes
**Overall: Solid UX improvement. Well-structured implementation.**
### Positives
- Smart auto-scroll behavior: tracks whether the user has scrolled up, only auto-scrolls when at bottom. This is the expected UX pattern for log viewers.
- "↓ Follow" button provides a clear way to re-enable auto-scroll — good discoverability.
- `isScrolledToBottom` uses a 30px tolerance — good for handling subpixel rendering differences.
- `{ passive: true }` on scroll listeners is a nice performance touch.
- `break-words` and `m-0` on `<pre>` elements fix potential overflow issues.
- Only scrolls when content actually changes (`changed` flag) — avoids unnecessary DOM thrashing.
- Consistent implementation across all three log panes (container, build, deployment).
### Observations
1. **Scroll listener not cleaned up**: The `addEventListener` calls in `_initScrollTracking` and the deployment view's `init` never have corresponding `removeEventListener`. For SPAs with Alpine this is usually fine (elements are long-lived), but if components are ever destroyed/recreated, listeners would accumulate.
2. **Polling interval**: The 1-second `setInterval` for `fetchAll()` was pre-existing, but combined with the new scroll tracking and change detection, this is now more efficient since unnecessary scroll operations are skipped.
3. **Double RAF removed**: The old `scrollToBottom` used double `requestAnimationFrame`; now it's single. If there were edge cases where double RAF was needed (layout thrashing), they might resurface — but single RAF is generally correct.
4. **`_autoScroll` naming**: Properties prefixed with `_` suggest private, which is a good convention in Alpine. Consistent across all panes.
**Verdict: Approve.** This is a well-thought-out UX fix that follows standard log viewer conventions. The code is clean and consistent.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Summary
Clean rewrite of the log viewer panes to fix auto-scroll behavior.
Changes
overflow-y: autofor proper scrollable containersbreak-wordsto prevent horizontal overflow on long linesCloses #17
@clawbot please review this PR
Post-merge review: looks clean, solid fix for #17.
Post-merge review: looks clean, solid fix for #17.
Review: PR#27 — Rewrite log viewer panes
Overall: Solid UX improvement. Well-structured implementation.
Positives
isScrolledToBottomuses a 30px tolerance — good for handling subpixel rendering differences.{ passive: true }on scroll listeners is a nice performance touch.break-wordsandm-0on<pre>elements fix potential overflow issues.changedflag) — avoids unnecessary DOM thrashing.Observations
addEventListenercalls in_initScrollTrackingand the deployment view'sinitnever have correspondingremoveEventListener. For SPAs with Alpine this is usually fine (elements are long-lived), but if components are ever destroyed/recreated, listeners would accumulate.setIntervalforfetchAll()was pre-existing, but combined with the new scroll tracking and change detection, this is now more efficient since unnecessary scroll operations are skipped.scrollToBottomused doublerequestAnimationFrame; now it's single. If there were edge cases where double RAF was needed (layout thrashing), they might resurface — but single RAF is generally correct._autoScrollnaming: Properties prefixed with_suggest private, which is a good convention in Alpine. Consistent across all panes.Verdict: Approve. This is a well-thought-out UX fix that follows standard log viewer conventions. The code is clean and consistent.