Skip to content

fix(lightspeed): fixed jump buttons location issue#2746

Open
ciiay wants to merge 1 commit intoredhat-developer:mainfrom
ciiay:rhdhbugs-2898
Open

fix(lightspeed): fixed jump buttons location issue#2746
ciiay wants to merge 1 commit intoredhat-developer:mainfrom
ciiay:rhdhbugs-2898

Conversation

@ciiay
Copy link
Copy Markdown
Member

@ciiay ciiay commented Apr 10, 2026

Hey, I just made a Pull Request!

For RHBUGS-2898

✔️ Checklist

  • A changeset describing the change and affected packages. (more info)
  • Added or Updated documentation
  • Tests for new functionality and regression tests for bug fixes
  • Screenshots attached (for UI changes)

screen recording

rhdhbugs_2898.mp4

Signed-off-by: Yi Cai <yicai@redhat.com>
@rhdh-gh-app
Copy link
Copy Markdown

rhdh-gh-app bot commented Apr 10, 2026

Missing Changesets

The following package(s) are changed by this PR but do not have a changeset:

  • @red-hat-developer-hub/backstage-plugin-lightspeed

See CONTRIBUTING.md for more information about how to add changesets.

Changed Packages

Package Name Package Path Changeset Bump Current Version
@red-hat-developer-hub/backstage-plugin-lightspeed workspaces/lightspeed/plugins/lightspeed none v1.4.0

@rhdh-qodo-merge
Copy link
Copy Markdown

Review Summary by Qodo

Fix jump buttons location and visibility in Lightspeed chat

🐞 Bug fix

Grey Divider

Walkthroughs

Description
• Fixed jump buttons positioning and visibility in chat interface
• Added overflow detection for chat content scroll container
• Conditionally show jump buttons only when content overflows
• Implemented ResizeObserver and MutationObserver for dynamic overflow tracking
Diagram
flowchart LR
  A["Chat Content"] --> B["Overflow Detection"]
  B --> C["hasChatContentOverflow State"]
  C --> D["Jump Buttons Visibility"]
  D --> E["Centered & Hidden by Default"]
  D --> F["Visible When Overflow Detected"]
Loading

Grey Divider

File Changes

1. workspaces/lightspeed/plugins/lightspeed/src/components/LightSpeedChat.tsx 🐞 Bug fix +104/-1

Implement overflow detection for jump button visibility

• Added chatbotContentHasOverflow CSS class to control jump button visibility
• Added hasChatContentOverflow state to track content overflow status
• Implemented comprehensive useEffect hook with ResizeObserver, MutationObserver, and scroll
 listeners to detect overflow
• Updated chatMainContent to conditionally apply overflow class based on state
• Styled jump buttons with centered positioning and hidden state by default

workspaces/lightspeed/plugins/lightspeed/src/components/LightSpeedChat.tsx


Grey Divider

Qodo Logo

@rhdh-qodo-merge
Copy link
Copy Markdown

rhdh-qodo-merge bot commented Apr 10, 2026

Code Review by Qodo

🐞 Bugs (2)   📘 Rule violations (0)   📎 Requirement gaps (0)   🎨 UX Issues (0)
🐞\ ≡ Correctness (1) ➹ Performance (1)

Grey Divider


Action required

1. Wrong overflow target 🐞
Description
The new overflow detection always prefers .pf-chatbot__messagebox when present, but in “new chat”
mode the MessageBox is configured to auto-size with overflow: visible, making the outer
ChatbotContentScroll div the real scroll container. This can leave hasChatContentOverflow false
and keep .pf-chatbot__jump hidden even while the user is scrolling overflowing chat content.
Code

workspaces/lightspeed/plugins/lightspeed/src/components/LightSpeedChat.tsx[R968-977]

+    const getScrollTarget = () =>
+      (scrollContainer.querySelector(
+        '.pf-chatbot__messagebox',
+      ) as HTMLElement | null) ?? scrollContainer;
+
+    const updateOverflow = () => {
+      const scrollTarget = getScrollTarget();
+      setHasChatContentOverflow(
+        scrollTarget.scrollHeight > scrollTarget.clientHeight + 1,
+      );
Evidence
LightspeedChatBox explicitly makes the MessageBox auto-height with visible overflow for new chats,
so the outer ChatbotContent scroll wrapper becomes the scrolling element; however LightSpeedChat’s
getScrollTarget() always selects .pf-chatbot__messagebox if it exists and uses that element to
compute overflow. Since jump buttons are hidden by default via CSS and only shown when
hasChatContentOverflow is true, measuring the wrong element can keep them hidden even when the
actual scroll container overflows.

workspaces/lightspeed/plugins/lightspeed/src/components/LightspeedChatBox.tsx[88-99]
workspaces/lightspeed/plugins/lightspeed/src/components/LightSpeedChat.tsx[304-323]
workspaces/lightspeed/plugins/lightspeed/src/components/LightSpeedChat.tsx[961-978]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`hasChatContentOverflow` is computed from `.pf-chatbot__messagebox` whenever it exists, but in “new chat” mode the MessageBox is not the scrolling element (it is auto-height with `overflow: visible`). This causes jump buttons to remain hidden even when the *actual* scroll container (`contentScrollRef`) overflows.

## Issue Context
- New chat mode uses `messageBoxAutoHeight` specifically so **ChatbotContent is the scroll container**.
- Jump buttons are hidden by default and only shown when `hasChatContentOverflow` is true.

## Fix approach
Update overflow detection to use the element that actually scrolls:
- Prefer `.pf-chatbot__messagebox` **only when it is the scrollable container** (e.g., `overflowY` is `auto|scroll` and/or it has internal overflow), otherwise fall back to `scrollContainer`.
- Alternatively, compute overflow for both elements and set `hasChatContentOverflow` true if *either* one overflows.

## Fix Focus Areas
- workspaces/lightspeed/plugins/lightspeed/src/components/LightSpeedChat.tsx[968-978]
- workspaces/lightspeed/plugins/lightspeed/src/components/LightSpeedChat.tsx[304-323]
- workspaces/lightspeed/plugins/lightspeed/src/components/LightspeedChatBox.tsx[88-99]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

2. Unthrottled overflow recomputation 🐞
Description
The new effect recomputes overflow from DOM measurements on captured scroll events and on
MutationObserver callbacks (subtree + characterData), which can run extremely often during token
streaming where message text updates incrementally. Even if React avoids re-rendering when the
boolean doesn’t change, repeated querySelector + scrollHeight/clientHeight reads can add
avoidable main-thread work.
Code

workspaces/lightspeed/plugins/lightspeed/src/components/LightSpeedChat.tsx[R982-1021]

+    // Use capture so scroll events from inner messagebox also trigger updates.
+    scrollContainer.addEventListener('scroll', updateOverflow, {
+      passive: true,
+      capture: true,
+    });
+
+    const resizeObserver =
+      typeof ResizeObserver !== 'undefined'
+        ? new ResizeObserver(() => updateOverflow())
+        : undefined;
+    resizeObserver?.observe(scrollContainer);
+    let observedScrollTarget: HTMLElement | null = getScrollTarget();
+    if (observedScrollTarget !== scrollContainer) {
+      resizeObserver?.observe(observedScrollTarget);
+    }
+
+    const mutationObserver =
+      typeof MutationObserver !== 'undefined'
+        ? new MutationObserver(() => {
+            const nextScrollTarget = getScrollTarget();
+            if (nextScrollTarget !== observedScrollTarget) {
+              if (
+                observedScrollTarget &&
+                observedScrollTarget !== scrollContainer
+              ) {
+                resizeObserver?.unobserve(observedScrollTarget);
+              }
+              if (nextScrollTarget !== scrollContainer) {
+                resizeObserver?.observe(nextScrollTarget);
+              }
+              observedScrollTarget = nextScrollTarget;
+            }
+            updateOverflow();
+          })
+        : undefined;
+    mutationObserver?.observe(scrollContainer, {
+      childList: true,
+      subtree: true,
+      characterData: true,
+    });
Evidence
During streaming, the assistant message content is appended token-by-token and setConversations is
called on each token, causing frequent DOM text updates. The added MutationObserver watches
characterData across the entire subtree and calls updateOverflow() on each mutation;
updateOverflow() performs DOM queries and layout reads (scrollHeight/clientHeight), increasing
work during streaming-heavy periods.

workspaces/lightspeed/plugins/lightspeed/src/hooks/useConversationMessages.ts[566-612]
workspaces/lightspeed/plugins/lightspeed/src/components/LightSpeedChat.tsx[973-987]
workspaces/lightspeed/plugins/lightspeed/src/components/LightSpeedChat.tsx[998-1021]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`updateOverflow()` can be invoked at high frequency (scroll capture + MutationObserver with `characterData`/`subtree`) and performs `querySelector` + `scrollHeight/clientHeight` reads each time. During token streaming, this can create unnecessary main-thread overhead.

## Issue Context
Token streaming appends content incrementally, which updates DOM text nodes frequently.

## Fix approach
Coalesce/limit overflow checks:
- Wrap `updateOverflow` behind a `requestAnimationFrame` scheduler (only one overflow check per frame), or debounce to ~50–100ms during streaming.
- Narrow MutationObserver scope/options (e.g., consider dropping `characterData: true` and relying on `childList` changes if sufficient, or observe only the message list element rather than the entire subtree).
- Avoid repeated `querySelector` by caching the messagebox element ref and only re-resolving when it changes.

## Fix Focus Areas
- workspaces/lightspeed/plugins/lightspeed/src/components/LightSpeedChat.tsx[973-987]
- workspaces/lightspeed/plugins/lightspeed/src/components/LightSpeedChat.tsx[988-1021]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

ⓘ The new review experience is currently in Beta. Learn more

Grey Divider

Qodo Logo

@sonarqubecloud
Copy link
Copy Markdown

Comment on lines +968 to +977
const getScrollTarget = () =>
(scrollContainer.querySelector(
'.pf-chatbot__messagebox',
) as HTMLElement | null) ?? scrollContainer;

const updateOverflow = () => {
const scrollTarget = getScrollTarget();
setHasChatContentOverflow(
scrollTarget.scrollHeight > scrollTarget.clientHeight + 1,
);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Action required

1. Wrong overflow target 🐞 Bug ≡ Correctness

The new overflow detection always prefers .pf-chatbot__messagebox when present, but in “new chat”
mode the MessageBox is configured to auto-size with overflow: visible, making the outer
ChatbotContentScroll div the real scroll container. This can leave hasChatContentOverflow false
and keep .pf-chatbot__jump hidden even while the user is scrolling overflowing chat content.
Agent Prompt
## Issue description
`hasChatContentOverflow` is computed from `.pf-chatbot__messagebox` whenever it exists, but in “new chat” mode the MessageBox is not the scrolling element (it is auto-height with `overflow: visible`). This causes jump buttons to remain hidden even when the *actual* scroll container (`contentScrollRef`) overflows.

## Issue Context
- New chat mode uses `messageBoxAutoHeight` specifically so **ChatbotContent is the scroll container**.
- Jump buttons are hidden by default and only shown when `hasChatContentOverflow` is true.

## Fix approach
Update overflow detection to use the element that actually scrolls:
- Prefer `.pf-chatbot__messagebox` **only when it is the scrollable container** (e.g., `overflowY` is `auto|scroll` and/or it has internal overflow), otherwise fall back to `scrollContainer`.
- Alternatively, compute overflow for both elements and set `hasChatContentOverflow` true if *either* one overflows.

## Fix Focus Areas
- workspaces/lightspeed/plugins/lightspeed/src/components/LightSpeedChat.tsx[968-978]
- workspaces/lightspeed/plugins/lightspeed/src/components/LightSpeedChat.tsx[304-323]
- workspaces/lightspeed/plugins/lightspeed/src/components/LightspeedChatBox.tsx[88-99]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

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.

1 participant