Skip to content

feat(web): inline tool activity cards with auto-collapsing#376

Merged
henrypark133 merged 2 commits intomainfrom
feat/web-ui-tool-activity-cards
Feb 26, 2026
Merged

feat(web): inline tool activity cards with auto-collapsing#376
henrypark133 merged 2 commits intomainfrom
feat/web-ui-tool-activity-cards

Conversation

@henrypark133
Copy link
Copy Markdown
Collaborator

Summary

  • Add inline tool activity cards to the web UI, inspired by Claude/Codex — tool execution is now shown directly in the chat conversation instead of only in the ephemeral status bar
  • While processing: animated thinking dots + individual tool cards with live spinner and elapsed timer
  • After response: activity group auto-collapses to a single ▸ Used N tools (Xs) summary line to keep the conversation clean
  • Click summary to expand tool cards; click a card header to see its monospace output preview
  • Add "Calling LLM..." thinking status from the dispatcher (benefits all channels)

Changes

File What
src/agent/dispatcher.rs Emit Thinking("Calling LLM...") before each LLM call
src/channels/web/static/app.js Activity group system (7 functions), SSE handler integration, dead code cleanup
src/channels/web/static/style.css Styles for activity group, thinking dots, tool cards, collapsed summary

Details

  • Thinking indicator is hidden (not destroyed) when tools start, preventing flicker during rapid multi-step execution
  • Tool cards use array-per-name tracking to handle the same tool being called twice
  • 5-minute max timer guard prevents interval leaks if SSE drops mid-execution
  • finalizeActivityGroup() is idempotent and called from all terminal events (response, stream_chunk, status Done, error, thread switch, SSE reconnect)
  • Removed now-dead status bar code (showToolResult, spinner timer, related CSS)

Test plan

  • Send message → thinking dots animate with "Calling LLM..."
  • Tool runs → card appears with spinner + live elapsed timer
  • Tool completes → spinner becomes ✓, timer freezes
  • Response arrives → group auto-collapses to "▸ Used N tools (Xs)"
  • Click summary → expands to show individual tool cards
  • Click card header → tool output preview expands in monospace
  • Multi-tool turn → multiple cards, correct summary count
  • cargo clippy --all --all-features — zero warnings
  • cargo test — all 1,597 tests pass

🤖 Generated with Claude Code

Add Claude/Codex-style inline tool activity cards to the web UI that
show tool execution progress directly in the chat conversation.

While processing:
- Animated thinking dots with message text (e.g. "Calling LLM...")
- Individual tool cards with live spinner and elapsed timer
- Cards show tool name, duration, and expandable output preview

After response arrives:
- Activity group auto-collapses to "Used N tools (Xs)"
- Click summary to expand and see individual tool cards
- Click card header to see tool output in monospace

Also includes:
- "Calling LLM..." thinking status from dispatcher (all channels)
- 5-minute max timer guard to prevent leaks on dropped SSE
- Handles parallel tools, same tool twice, failures, thread switching

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@github-actions github-actions Bot added scope: agent Agent core (agent loop, router, scheduler) scope: channel/web Web gateway channel size: L 200-499 changed lines risk: medium Business logic, config, or moderate-risk modules contributor: experienced 6-19 merged PRs labels Feb 25, 2026
@gemini-code-assist
Copy link
Copy Markdown
Contributor

Summary of Changes

Hello @henrypark133, 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 significantly enhances the user experience of the web UI by integrating dynamic, inline feedback for tool execution and LLM interactions. It moves away from a simple status bar to a more interactive and informative display, allowing users to track agent activity in real-time and review tool outputs directly within the chat, while maintaining a clean interface through auto-collapsing summaries.

Highlights

  • Inline Tool Activity Cards: Introduced inline tool activity cards directly into the chat conversation, replacing the ephemeral status bar for tool execution feedback. This includes animated thinking dots, live spinners, and elapsed timers during tool processing.
  • Auto-Collapsing Activity Groups: Implemented an auto-collapsing mechanism for activity groups. After a response, tool cards collapse into a single 'Used N tools (Xs)' summary line to maintain a clean conversation view. Users can click the summary to expand the cards or click a card header to view its monospace output preview.
  • Enhanced LLM Thinking Status: Added a 'Calling LLM...' thinking status emitted from the dispatcher, providing a clearer indication when the Language Model is being invoked across all channels.
  • Robust Activity Group Management: Ensured proper lifecycle management for activity groups by making finalizeActivityGroup() idempotent and calling it from all terminal events (response, stream_chunk, status Done, error, thread switch, SSE reconnect) to prevent leaks and ensure consistent UI state.
Changelog
  • src/agent/dispatcher.rs
    • Added a Thinking("Calling LLM...") status update before each LLM call to provide better user feedback.
  • src/channels/web/static/app.js
    • Introduced global state variables (_activeGroup, _activeToolCards, _activityThinking) for managing tool activity groups and cards.
    • Integrated finalizeActivityGroup calls into various SSE event handlers (response, stream_chunk, status updates, errors) and thread switching functions to ensure proper activity group lifecycle management.
    • Updated SSE event listeners for thinking, tool_started, and tool_completed to use new inline activity card functions instead of the old status bar.
    • Added a new SSE event listener for tool_result to display tool output previews.
    • Implemented core JavaScript logic for creating, updating, and collapsing inline tool activity cards, including animated thinking dots, live timers, and output previews.
    • Removed the spinning parameter from the setStatus function as its functionality is now handled by the new activity cards.
  • src/channels/web/static/style.css
    • Added comprehensive CSS styles for the new inline tool activity cards, including activity-group, activity-thinking (with animated dots), activity-tool-card (with status indicators, icons, and duration), and activity-summary (for collapsed view).
    • Included responsive adjustments for activity groups on smaller screens.
Activity
  • This pull request introduces a new feature to the web UI, enhancing the display of tool activity and LLM thinking states.
  • The author has provided a comprehensive test plan, including checks for various scenarios like message sending, tool execution, response arrival, and UI interactions, ensuring the new functionality works as expected.
  • The changes include updates to Rust backend for status emission and significant frontend work in JavaScript and CSS for the new UI components.
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.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

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.

Copy link
Copy Markdown
Contributor

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

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 introduces a significant UI enhancement by adding inline tool activity cards, providing users with better visibility into tool execution. While this is an excellent feature addition that greatly improves the user experience, a minor correctness issue was identified in the JavaScript implementation where the total duration of tool activity could be miscalculated, leading to an inflated value in the summary.

Comment thread src/channels/web/static/app.js Outdated
}, 100);

if (!_activeToolCards[name]) _activeToolCards[name] = [];
_activeToolCards[name].push({ card, startTime, timer: timerInterval, duration, icon });
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

To accurately calculate the total duration of tool executions, it's helpful to store the final duration of each tool when it completes. Please initialize a finalDuration property here. This will be used by completeToolCard and finalizeActivityGroup.

Suggested change
_activeToolCards[name].push({ card, startTime, timer: timerInterval, duration, icon });
_activeToolCards[name].push({ card, startTime, timer: timerInterval, duration, icon, finalDuration: null });

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Good catch — this is a real bug. When finalizeActivityGroup() fires, it was recalculating duration from Date.now() for tools that had already completed, inflating the summary. Fixed in e87c4b6: added finalDuration: null to the entry object, store the frozen elapsed time in completeToolCard(), and use it in finalizeActivityGroup().

Comment on lines +526 to +527
const elapsed = (Date.now() - entry.startTime) / 1000;
entry.duration.textContent = elapsed < 10 ? elapsed.toFixed(1) + 's' : Math.floor(elapsed) + 's';
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

When a tool card is completed, please store its final duration on the entry object. This will allow finalizeActivityGroup to correctly sum up the durations of all completed tools, rather than using the time at which the group is finalized.

  const elapsed = (Date.now() - entry.startTime) / 1000;
  entry.finalDuration = elapsed;
  entry.duration.textContent = elapsed < 10 ? elapsed.toFixed(1) + 's' : Math.floor(elapsed) + 's';

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Done — entry.finalDuration = elapsed is now stored in completeToolCard(). See e87c4b6.

Comment on lines +568 to +576
let toolCount = 0;
let totalDuration = 0;
for (const tname in _activeToolCards) {
const tentries = _activeToolCards[tname];
for (let j = 0; j < tentries.length; j++) {
toolCount++;
totalDuration += (Date.now() - tentries[j].startTime) / 1000;
}
}
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 calculation for totalDuration is incorrect for tools that have already completed, as it uses Date.now() at the time of finalization. To fix this, please use the finalDuration property (set in completeToolCard) for completed tools. For tools that are still running, you can continue to calculate the duration up to the current time.

  let toolCount = 0;
  let totalDuration = 0;
  for (const tname in _activeToolCards) {
    const tentries = _activeToolCards[tname];
    for (let j = 0; j < tentries.length; j++) {
      const entry = tentries[j];
      toolCount++;
      if (entry.finalDuration !== null) {
        totalDuration += entry.finalDuration;
      } else {
        // Tool was still running when finalized.
        totalDuration += (Date.now() - entry.startTime) / 1000;
      }
    }
  }

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Fixed — finalizeActivityGroup() now uses entry.finalDuration for completed tools and only falls back to Date.now() - startTime for tools still running at finalization time. See e87c4b6.

The collapsed activity summary was showing inflated total duration
because finalizeActivityGroup() recalculated elapsed time from
Date.now() for already-completed tools. Now each tool card stores
its final duration at completion time and the summary uses that
frozen value instead.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@henrypark133 henrypark133 merged commit 443b120 into main Feb 26, 2026
4 checks passed
@henrypark133 henrypark133 deleted the feat/web-ui-tool-activity-cards branch February 26, 2026 00:01
bkutasi pushed a commit to bkutasi/ironclaw that referenced this pull request Mar 28, 2026
* feat(web): inline tool activity cards with auto-collapsing

Add Claude/Codex-style inline tool activity cards to the web UI that
show tool execution progress directly in the chat conversation.

While processing:
- Animated thinking dots with message text (e.g. "Calling LLM...")
- Individual tool cards with live spinner and elapsed timer
- Cards show tool name, duration, and expandable output preview

After response arrives:
- Activity group auto-collapses to "Used N tools (Xs)"
- Click summary to expand and see individual tool cards
- Click card header to see tool output in monospace

Also includes:
- "Calling LLM..." thinking status from dispatcher (all channels)
- 5-minute max timer guard to prevent leaks on dropped SSE
- Handles parallel tools, same tool twice, failures, thread switching

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(web): use frozen duration for completed tools in activity summary

The collapsed activity summary was showing inflated total duration
because finalizeActivityGroup() recalculated elapsed time from
Date.now() for already-completed tools. Now each tool card stores
its final duration at completion time and the summary uses that
frozen value instead.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

contributor: experienced 6-19 merged PRs risk: medium Business logic, config, or moderate-risk modules scope: agent Agent core (agent loop, router, scheduler) scope: channel/web Web gateway channel size: L 200-499 changed lines

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants