Skip to content

Move thinking indicator inline into message row#45

Merged
wende merged 5 commits into
mainfrom
feat/inline-thinking-indicator
Mar 21, 2026
Merged

Move thinking indicator inline into message row#45
wende merged 5 commits into
mainfrom
feat/inline-thinking-indicator

Conversation

@wende

@wende wende commented Mar 21, 2026

Copy link
Copy Markdown
Owner

Summary

  • Removes the floating ThinkingIndicator overlay that was absolutely positioned above the composer bar
  • Adds an InlineThinkingIndicator component rendered directly inside each streaming assistant message, showing animated "Thinking..." dots with an elapsed seconds counter
  • When streaming ends, the inline indicator disappears and the existing "Worked for Xs" duration text appears in its place

Test plan

  • Send a message and verify "Thinking..." with animated dots appears inline below the assistant's streaming content
  • Verify the elapsed seconds counter starts ticking after 1s
  • Verify "Thinking..." disappears and "Worked for Xs" appears once the run completes
  • Verify no floating thinking indicator appears above the composer bar

Replace the floating ThinkingIndicator overlay with an inline
indicator rendered directly inside each streaming assistant message.
Shows animated "Thinking..." dots with an elapsed seconds counter,
then transitions to "Worked for Xs" when the run completes.
@vercel

vercel Bot commented Mar 21, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
mobileclaw Ready Ready Preview, Comment Mar 21, 2026 1:31pm

@gemini-code-assist

Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request refactors the user interface to improve the display of the 'thinking' state during AI assistant responses. Instead of a static, globally positioned indicator, the system now provides a more contextual and integrated visual cue directly within the assistant's message stream, enhancing the user experience by clearly associating the thinking process with the relevant message.

Highlights

  • Removed Floating Indicator: The previously floating ThinkingIndicator overlay, which was positioned above the composer bar, has been completely removed.
  • Introduced Inline Indicator: A new InlineThinkingIndicator component has been added, which renders directly within each streaming assistant message. This indicator displays animated 'Thinking...' dots along with an elapsed seconds counter.
  • Dynamic Display: The inline indicator appears when an assistant message is streaming and disappears once the run completes, at which point the existing 'Worked for Xs' duration text is shown instead.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Hey - I've found 1 issue, and left some high level feedback:

  • InlineThinkingIndicator initializes elapsed with Date.now() during render, which can cause server/client hydration mismatches; consider initializing to 0 and computing the initial value in an effect instead.
  • The thinking timer logic is duplicated from the previous overlay-style ThinkingIndicator; it might be worth extracting shared timing logic into a small hook or utility to keep behavior consistent between indicators and make future changes easier.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- InlineThinkingIndicator initializes `elapsed` with `Date.now()` during render, which can cause server/client hydration mismatches; consider initializing to 0 and computing the initial value in an effect instead.
- The thinking timer logic is duplicated from the previous overlay-style ThinkingIndicator; it might be worth extracting shared timing logic into a small hook or utility to keep behavior consistent between indicators and make future changes easier.

## Individual Comments

### Comment 1
<location path="components/MessageRow.tsx" line_range="434-440" />
<code_context>
   return null;
 }

+function InlineThinkingIndicator({ startTime }: { startTime?: number }) {
+  const [elapsed, setElapsed] = useState(() =>
+    startTime ? Math.floor((Date.now() - startTime) / 1000) : 0,
+  );
+
+  useEffect(() => {
+    if (!startTime) return;
+    const id = setInterval(() => {
</code_context>
<issue_to_address>
**suggestion:** Sync elapsed immediately when `startTime` becomes available or changes, not only on the next interval tick.

Because `elapsed` is only initialized from `startTime` on mount, if `startTime` is initially undefined and later set (or changed), the displayed value stays stale until the next 1s tick. To keep the UI in sync with prop changes, update `elapsed` immediately inside the effect before starting the interval:
```ts
useEffect(() => {
  if (!startTime) return;
  setElapsed(Math.floor((Date.now() - startTime) / 1000));

  const id = setInterval(() => {
    setElapsed(Math.floor((Date.now() - startTime) / 1000));
  }, 1000);
  return () => clearInterval(id);
}, [startTime]);
```

```suggestion
  useEffect(() => {
    if (!startTime) return;

    // Sync immediately when startTime becomes available or changes
    setElapsed(Math.floor((Date.now() - startTime) / 1000));

    const id = setInterval(() => {
      setElapsed(Math.floor((Date.now() - startTime) / 1000));
    }, 1000);

    return () => clearInterval(id);
  }, [startTime]);
```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread components/MessageRow.tsx Outdated

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request successfully moves the thinking indicator to be inline with the message row, removing the floating overlay. The implementation of the new InlineThinkingIndicator is clean, but I've found a minor issue with how it handles state updates for the elapsed time, which could lead to a stale timer display in some edge cases. I've provided a suggestion to make it more robust. Otherwise, the changes look good and align with the goal of the PR.

Comment thread components/MessageRow.tsx Outdated
Comment on lines +430 to +440
const [elapsed, setElapsed] = useState(() =>
startTime ? Math.floor((Date.now() - startTime) / 1000) : 0,
);

useEffect(() => {
if (!startTime) return;
const id = setInterval(() => {
setElapsed(Math.floor((Date.now() - startTime) / 1000));
}, 1000);
return () => clearInterval(id);
}, [startTime]);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

The current implementation of the InlineThinkingIndicator has a potential bug where the elapsed time could become stale or not reset correctly.

  1. If startTime changes from a defined value to undefined, the useEffect will clear the interval but won't reset elapsed, causing a stale timer value to remain on screen.
  2. If startTime changes from one timestamp to another (less likely, but possible if the component is reused), the elapsed state will not be updated immediately, showing a stale value for up to one second until the new interval fires.

A more robust implementation would use useEffect to handle all updates to startTime, ensuring the elapsed state is always in sync.

  const [elapsed, setElapsed] = useState(0);

  useEffect(() => {
    if (!startTime) {
      setElapsed(0);
      return;
    }

    setElapsed(Math.floor((Date.now() - startTime) / 1000));

    const timerId = setInterval(() => {
      setElapsed(Math.floor((Date.now() - startTime) / 1000));
    }, 1000);

    return () => clearInterval(timerId);
  }, [startTime]);

…indicator

# Conflicts:
#	components/MessageRow.tsx
#	components/chat/ChatViewport.tsx
@wende wende merged commit e3f58e9 into main Mar 21, 2026
4 checks passed
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