Skip to content

Conversation

@jigar-arc10
Copy link
Contributor

@jigar-arc10 jigar-arc10 commented Oct 28, 2025

Summary by CodeRabbit

  • Bug Fixes
    • Improved activity log scroll-to-bottom detection and per-task tracking.
    • Enhanced auto-follow functionality for more responsive real-time log viewing.

@jigar-arc10 jigar-arc10 requested a review from a team as a code owner October 28, 2025 22:50
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 28, 2025

Walkthrough

The ActivityLogDetails component now manages scroll-follow state per task. This allows the component to track whether users are at the bottom of each task's log and automatically maintain or release the auto-follow behavior accordingly.

Changes

Cohort / File(s) Summary
Scroll-follow state management
apps/provider-console/src/components/shared/ActivityLogDetails.tsx
Added per-task followScroll state to track auto-scroll behavior. Extended onScroll handler to detect when scrolling reaches the bottom and update followScroll accordingly. Removed key prop from LazyLog component. State is passed to ScrollFollow component to control initial follow behavior.

Sequence Diagram

sequenceDiagram
    actor User
    participant ActivityLogDetails
    participant LazyLog
    participant ScrollFollow

    User->>LazyLog: Scroll in log
    LazyLog->>ActivityLogDetails: onScroll event
    ActivityLogDetails->>ActivityLogDetails: Detect bottom reach
    alt At bottom
        ActivityLogDetails->>ActivityLogDetails: Set followScroll = true
    else Not at bottom
        ActivityLogDetails->>ActivityLogDetails: Set followScroll = false
    end
    ActivityLogDetails->>ScrollFollow: Pass followScroll as startFollowing
    ScrollFollow->>User: Auto-follow or release scroll behavior
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

  • Review the scroll detection logic in the extended onScroll handler for edge cases
  • Verify state update timing doesn't cause performance issues with frequent scroll events
  • Confirm removal of the key prop doesn't introduce unintended re-render behavior

Poem

🐰 Hopping through logs with a scrolling delight,
Each task gets its own scroll-follow height,
Down to the bottom, the rabbit will stay,
Then let it go free in its scrolling ballet!
No janky rerenders to ruin the show—
Just smooth state that flows where users may go!

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The PR title "fix(provider): added auto-scroll fix" directly and clearly describes the main objective of the changeset. The modifications to ActivityLogDetails.tsx specifically implement scroll-follow state management to fix auto-scroll behavior in the provider console, which aligns precisely with the title's claim. The title uses specific terminology ("auto-scroll fix") rather than vague language, follows conventional commit format, and would allow a teammate scanning the repository history to immediately understand that this fixes auto-scroll functionality in the provider console.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/provider-console/auto-scroll-fix

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov
Copy link

codecov bot commented Oct 28, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 46.06%. Comparing base (cc8d5a6) to head (930858f).
⚠️ Report is 1 commits behind head on main.
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2127      +/-   ##
==========================================
- Coverage   46.42%   46.06%   -0.36%     
==========================================
  Files        1016     1006      -10     
  Lines       28692    28343     -349     
  Branches     7462     7421      -41     
==========================================
- Hits        13319    13056     -263     
+ Misses      15060    14983      -77     
+ Partials      313      304       -9     
Flag Coverage Δ *Carryforward flag
api 81.92% <ø> (ø) Carriedforward from cc8d5a6
deploy-web 24.63% <ø> (ø) Carriedforward from cc8d5a6
log-collector ?
notifications 88.11% <ø> (ø) Carriedforward from cc8d5a6
provider-console 81.48% <ø> (ø) Carriedforward from cc8d5a6
provider-proxy 84.61% <ø> (ø) Carriedforward from cc8d5a6

*This pull request uses carry forward flags. Click here to find out more.
see 10 files with indirect coverage changes

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
apps/provider-console/src/components/shared/ActivityLogDetails.tsx (2)

188-188: Consider clearer default handling.

The logic is correct but the double negative pattern (!== false) is less readable. Consider using nullish coalescing for clarity.

Apply this diff for improved readability:

-      const shouldFollow = followScroll[taskId] !== false; // Default to true, but allow user to disable
+      const shouldFollow = followScroll[taskId] ?? true; // Default to true unless explicitly disabled

20-20: Consider cleanup of followScroll state entries.

The followScroll state accumulates entries for all tasks but never removes them when tasks complete or accordions close. While not immediately critical, this could lead to memory growth in long-running sessions with many tasks.

Consider clearing entries when accordions close:

const handleAccordionToggle = (index: number, task: Task) => {
  setOpenAccordions(prev => {
    const newState = [...prev];
    newState[index] = !newState[index];

    if (newState[index]) {
      if (task.status === "in_progress") {
        setupLogStream(task.id);
      } else if (!taskLogs[task.id]) {
        fetchTaskLogs(task.id);
      }
    } else {
      logStreams.current[task.id]?.close();
      logStreams.current[task.id] = null;
      // Clean up followScroll state when closing accordion
      setFollowScroll(prev => {
        const updated = { ...prev };
        delete updated[task.id];
        return updated;
      });
    }

    return newState;
  });
};
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 10b4d28 and 930858f.

⛔ Files ignored due to path filters (1)
  • packages/net/src/generated/netConfigData.ts is excluded by !**/generated/**
📒 Files selected for processing (1)
  • apps/provider-console/src/components/shared/ActivityLogDetails.tsx (2 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/general.mdc)

Never use type any or cast to type any. Always define the proper TypeScript types.

Files:

  • apps/provider-console/src/components/shared/ActivityLogDetails.tsx
**/*.{js,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/general.mdc)

**/*.{js,ts,tsx}: Never use deprecated methods from libraries.
Don't add unnecessary comments to the code

Files:

  • apps/provider-console/src/components/shared/ActivityLogDetails.tsx
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (11)
  • GitHub Check: validate (packages) / validate-unsafe
  • GitHub Check: validate / validate-app
  • GitHub Check: test-build
  • GitHub Check: validate / validate-app
  • GitHub Check: test-build
  • GitHub Check: test-build
  • GitHub Check: validate / validate-app
  • GitHub Check: test-build
  • GitHub Check: test-build
  • GitHub Check: test-build
  • GitHub Check: Validate local packages
🔇 Additional comments (1)
apps/provider-console/src/components/shared/ActivityLogDetails.tsx (1)

20-20: Good addition of per-task scroll state.

The followScroll state properly tracks scroll-follow behavior for each task individually, which aligns with the PR objective.

Comment on lines +198 to +210
onScroll={({ scrollTop, scrollHeight, clientHeight }) => {
// Check if user has scrolled to the bottom
const isAtBottom = scrollTop + clientHeight >= scrollHeight - 10; // 10px threshold

// Update follow state based on scroll position
setFollowScroll(prev => ({
...prev,
[taskId]: isAtBottom
}));

// Call the original onScroll handler
onScroll({ scrollTop, scrollHeight, clientHeight });
}}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Optimize scroll handler performance and verify ScrollFollow behavior.

The onScroll handler updates state on every scroll event, which fires continuously during scrolling and may cause performance issues.

Additionally, verify that the ScrollFollow component respects the startFollowing prop on re-renders. If it only uses startFollowing during initial mount and maintains internal state thereafter, updating followScroll state may not affect the auto-follow behavior as intended.

Performance optimization: Consider throttling or debouncing the state updates:

onScroll={({ scrollTop, scrollHeight, clientHeight }) => {
  const isAtBottom = scrollTop + clientHeight >= scrollHeight - 10;
  
  // Only update state if the value actually changes
  setFollowScroll(prev => {
    if (prev[taskId] !== isAtBottom) {
      return { ...prev, [taskId]: isAtBottom };
    }
    return prev;
  });

  onScroll({ scrollTop, scrollHeight, clientHeight });
}}

Verification: Please test that toggling auto-follow works correctly by:

  1. Scrolling up in an active log stream (should disable auto-follow)
  2. Scrolling back to bottom (should re-enable auto-follow)
  3. Verifying new logs appear automatically when at bottom


---

🏁 Script executed:

```shell
#!/bin/bash
# Find and examine ActivityLogDetails.tsx
fd --type f "ActivityLogDetails.tsx" --exec cat {} \;

Length of output: 11611


🏁 Script executed:

#!/bin/bash
# Find ScrollFollow component
fd --type f -e "ts" -e "tsx" -e "js" -e "jsx" | xargs rg -l "ScrollFollow" -g '!node_modules' | head -20

Length of output: 66


🏁 Script executed:

#!/bin/bash
# Search for ScrollFollow component definition
rg -nP --type=ts --type=tsx "(?:export\s+)?(const|function|class)\s+ScrollFollow\b" -A 15

Length of output: 27


🏁 Script executed:

#!/bin/bash
# Check for throttle/debounce imports or usage in the ActivityLogDetails file
rg -nP "(throttle|debounce)" apps/provider-console/src/components/shared/ActivityLogDetails.tsx

Length of output: 0



---

🌐 Web query:

```web
@melloware/react-logviewer ScrollFollow startFollowing prop behavior

💡 Result:

Short answer: startFollowing is the initial boolean that determines whether ScrollFollow begins in "follow" mode (i.e., child LazyLog receives follow=true). It only sets the initial state — user scrolling will turn follow off, and to re-enable following you must call the startFollowing helper returned by the render/child function (the prop itself is not a runtime restart callback). [1][2]

Sources:

  • @melloware/react-logviewer ScrollFollow docs (usage, startFollowing prop example). [1]
  • react-lazylog ScrollFollow docs (explains startFollowing is initial value and exposes startFollowing/stopFollowing functions from the child render). [2]

Address both performance and architectural issues with ScrollFollow integration.

The performance concern is valid: the onScroll handler updates state on every scroll event without checking if the value changes, causing unnecessary re-renders.

More critically, the current approach is architecturally problematic. According to the library documentation, startFollowing is an initial prop only—it doesn't control follow behavior on subsequent renders. The code recreates the entire ScrollFollow component on each render attempt to work around this, which is inefficient. The proper approach is to use the startFollowing() and stopFollowing() functions from the render prop to control follow state dynamically.

Required fixes:

  1. Performance: Only update state when the value actually changes:
onScroll={({ scrollTop, scrollHeight, clientHeight }) => {
  const isAtBottom = scrollTop + clientHeight >= scrollHeight - 10;
  
  setFollowScroll(prev => {
    if (prev[taskId] !== isAtBottom) {
      return { ...prev, [taskId]: isAtBottom };
    }
    return prev;
  });

  onScroll({ scrollTop, scrollHeight, clientHeight });
}}
  1. Architecture: Refactor to use the startFollowing and stopFollowing callback functions instead of relying on prop changes:
render={({ follow, onScroll, startFollowing, stopFollowing }) => (
  <LazyLog
    text={sanitizedLogs}
    follow={follow}
    onScroll={({ scrollTop, scrollHeight, clientHeight }) => {
      const isAtBottom = scrollTop + clientHeight >= scrollHeight - 10;
      if (isAtBottom) {
        startFollowing();
      } else {
        stopFollowing();
      }
      onScroll({ scrollTop, scrollHeight, clientHeight });
    }}
    // ... rest of props
  />
)}
🤖 Prompt for AI Agents
In apps/provider-console/src/components/shared/ActivityLogDetails.tsx around
lines 198-210, the current onScroll handler updates follow state on every scroll
and relies on re-rendering via the startFollowing prop; change it to (1) only
call setFollowScroll when the computed isAtBottom value actually differs from
the existing state for taskId to avoid needless re-renders, and (2) refactor the
ScrollFollow render prop to use the provided startFollowing and stopFollowing
functions to control follow behavior dynamically (call startFollowing when at
bottom, stopFollowing when not) while still invoking the original onScroll({
scrollTop, scrollHeight, clientHeight }) after handling follow state.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants