Skip to content

feat(cli): introduce visualize tool for rich data display#18778

Closed
bdmorgan wants to merge 1 commit intomainfrom
feature/rich-data-visualization
Closed

feat(cli): introduce visualize tool for rich data display#18778
bdmorgan wants to merge 1 commit intomainfrom
feature/rich-data-visualization

Conversation

@bdmorgan
Copy link
Copy Markdown
Collaborator

Description

This PR introduces a new visualize tool that allows the model to present structured data such as tables, charts (bar, pie, line), and diffs in a more readable and visual format within the CLI.

Key Changes

  • Core: Added visualize tool and RichVisualization type.
  • CLI UI: Created RichDataDisplay component using Ink to render various visualization types.
  • Integration: Integrated RichDataDisplay into ToolResultDisplay for seamless rendering of tool outputs.
  • UX/DX: Updated system prompts to guide the model in using the new tool and added info semantic color to themes.
  • Maintenance: Fixed shell parser initialization issues in tests.

- Implement `visualize` tool in core to support tables, charts, and diffs.
- Add `RichDataDisplay` UI component using Ink for rendering visualizations.
- Integrate visualization support into `ToolResultDisplay`.
- Update system prompts to encourage the use of the `visualize` tool for structured data.
- Add `info` semantic color to themes.
- Fix shell parser initialization in tests.
@gemini-code-assist
Copy link
Copy Markdown
Contributor

Summary of Changes

Hello @bdmorgan, 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 CLI's ability to display complex data by introducing a dedicated visualization tool. This new feature allows for the structured presentation of information through tables, various chart types, and code diffs, moving beyond plain text outputs. The changes include core tool implementation, a new UI component for rendering these visualizations, and updates to the system's guidance to encourage its use, ultimately providing a richer and more intuitive user experience for data analysis and review within the command-line interface.

Highlights

  • New Visualize Tool: Introduced a new visualize tool that enables the model to present structured data (tables, charts, diffs) in a more readable and visual format within the CLI.
  • Rich Data Display Component: Developed a RichDataDisplay Ink component to render various visualization types, improving the CLI's user interface for data presentation.
  • Tool Output Integration: Integrated the RichDataDisplay component into ToolResultDisplay to seamlessly render the outputs of the new visualize tool.
  • System Prompt Updates: Updated system prompts to guide the model in effectively utilizing the new visualize tool and to prioritize structured outputs over raw text.
  • Semantic Color Addition: Added an info semantic color to the CLI themes, enhancing the visual feedback capabilities.
  • Shell Parser Test Fixes: Addressed and fixed issues related to shell parser initialization in tests, improving test reliability and isolation.
Changelog
  • packages/cli/src/ui/components/Header.test.tsx
    • Added 'info' property to the semantic colors in the test mock.
  • packages/cli/src/ui/components/messages/RichDataDisplay.test.tsx
    • Added a new test file for the RichDataDisplay component, covering various visualization types like table, bar chart, line chart, pie chart, and diff, as well as file saving.
  • packages/cli/src/ui/components/messages/RichDataDisplay.tsx
    • Added a new component RichDataDisplay responsible for rendering different types of rich visualizations (tables, bar charts, line charts, pie charts, and diffs) based on provided data and available terminal width.
  • packages/cli/src/ui/components/messages/ToolResultDisplay.test.tsx
    • Added new test cases to verify the rendering of rich visualization results (diff and table) within the ToolResultDisplay component.
  • packages/cli/src/ui/components/messages/ToolResultDisplay.tsx
    • Imported the RichVisualization type and RichDataDisplay component.
    • Modified the ToolResultDisplay component to conditionally render RichDataDisplay when the result is a rich visualization object.
  • packages/cli/src/ui/themes/no-color.ts
    • Added an 'info' property to the noColorSemanticColors object, setting its value to an empty string.
  • packages/cli/src/ui/themes/semantic-tokens.ts
    • Added an 'info' property to the SemanticColors interface.
    • Assigned lightTheme.AccentBlue, darkTheme.AccentBlue, and ansiTheme.AccentBlue to the 'info' property in lightSemanticColors, darkSemanticColors, and ansiSemanticColors respectively.
  • packages/cli/src/ui/themes/theme.ts
    • Added an 'info' property to the status object within the Theme class, mapping it to this.colors.AccentBlue.
    • Included an 'info' property in the status object when creating a custom theme, using customTheme.status?.info ?? colors.AccentBlue as its value.
  • packages/cli/src/ui/utils/InlineMarkdownRenderer.tsx
    • Updated the getPlainTextLength function to use a more robust regex and conditional logic for accurately stripping markdown formatting from text, handling bold, italic, strikethrough, inline code, links, and underlines.
  • packages/core/src/config/config.ts
    • Imported the VisualizeTool.
    • Added an optional info property to the CustomTheme interface.
    • Registered the VisualizeTool with the configuration, passing the message bus.
  • packages/core/src/core/snapshots/prompts.test.ts.snap
    • Updated multiple prompt snapshots to include a new guideline: 'Structured Outputs: Prioritize structured visualizations using the visualize tool for complex data (such as tables, comparisons, or trends) over manual markdown formatting or long, unformatted lists.'
    • Added a new 'Data Visualization' section to several prompt snapshots, detailing how to prefer and choose the right visualization type (table, bar_chart, line_chart, diff) and ensure contextual clarity.
  • packages/core/src/prompts/snippets.legacy.ts
    • Added a new function renderDataVisualization which returns a string containing guidelines for data visualization.
    • Integrated the renderDataVisualization output into the primary workflows, specifically within the new application steps and general guidelines.
  • packages/core/src/prompts/snippets.ts
    • Added a new function renderDataVisualization which returns a string containing guidelines for data visualization.
    • Integrated the renderDataVisualization output into the primary workflows, specifically within the new application steps and general guidelines.
  • packages/core/src/tools/tools.ts
    • Defined a new interface RichVisualization to represent structured data for visualization, including properties for type, title, data, columns, and saved file path.
    • Extended the ToolResultDisplay type to include RichVisualization, allowing tools to return rich visualization objects.
  • packages/core/src/tools/visualize.test.ts
    • Added a new test file for the VisualizeTool, including tests for table, bar chart, line chart, diff visualizations, saving data to a file, and error handling for invalid data types.
  • packages/core/src/tools/visualize.ts
    • Added a new file implementing the VisualizeTool and VisualizeInvocation classes.
    • The VisualizeTool provides functionality to render structured data as tables, charts, or diffs, and optionally save the data to a file (JSON or CSV).
    • Includes validation for data types based on the visualization type and error handling for file saving operations.
  • packages/core/src/utils/shell-utils.test.ts
    • Imported resetShellParsersForTesting.
    • Modified the 'should handle parser initialization failures gracefully' test to use resetShellParsersForTesting and vi.spyOn for better test isolation and cleanup.
  • packages/core/src/utils/shell-utils.ts
    • Added a new function resetShellParsersForTesting to reset the internal state of shell parser initialization, intended for testing purposes.
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 visualize tool for richer data display in the CLI, a great enhancement for usability. The implementation is comprehensive, covering tables, various charts, and diffs, along with the necessary UI components and tests.

I've identified two high-severity issues related to the rendering logic that could lead to UI bugs. One concerns the pie chart's proportional bar calculation, which can cause it to exceed its container width, and has been addressed with a suggestion for a more robust distribution method. The other is a hardcoded height for diff rendering, which can result in improper truncation. My detailed comments provide suggestions for fixing these.

Comment on lines +205 to +216
{normalized.map((item, i) => {
const percent = total > 0 ? item.value / total : 0;
const barWidth = Math.max(
1,
Math.floor(percent * availableWidth),
);
return (
<Text key={i} color={colors[i % colors.length]}>
{'█'.repeat(barWidth)}
</Text>
);
})}
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.

high

The current logic for calculating barWidth for the pie chart's proportional bar can lead to the total width exceeding availableWidth. When percent * availableWidth is less than 1, Math.floor makes it 0, and Math.max(1, 0) sets barWidth to 1. If there are many small-percentage items, the sum of these minimum-width bars can overflow the container, causing wrapping and breaking the visualization.

A more robust approach is to calculate all widths proportionally and distribute any remainder from flooring to ensure the total width exactly matches availableWidth.

            {(() => {
              if (total <= 0) {
                return null;
              }

              const preciseWidths = normalized.map(
                (item) => (item.value / total) * availableWidth,
              );

              const flooredWidths = preciseWidths.map((w) => Math.floor(w));
              let remainder =
                availableWidth - flooredWidths.reduce((sum, w) => sum + w, 0);

              const fractionalParts = preciseWidths.map((w, i) => ({
                index: i,
                fraction: w - flooredWidths[i],
              }));
              fractionalParts.sort((a, b) => b.fraction - a.fraction);

              for (let i = 0; i < remainder; i++) {
                const indexToIncrement =
                  fractionalParts[i % fractionalParts.length].index;
                flooredWidths[indexToIncrement]++;
              }

              return normalized.map((_item, i) => {
                const barWidth = flooredWidths[i];
                if (barWidth <= 0) {
                  return null;
                }
                return (
                  <Text key={i} color={colors[i % colors.length]}>
                    {'█'.repeat(barWidth)}
                  </Text>
                );
              });
            })()}
References
  1. When distributing surplus space proportionally (e.g., in a table layout), avoid simple rounding with Math.floor() for each column's share as it can lead to unused space. Instead, use a more precise method like the Largest Remainder Method to ensure all available space is utilized.

Comment on lines +206 to +211
content = (
<RichDataDisplay
data={truncatedResultDisplay as RichVisualization}
availableWidth={childWidth}
/>
);
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.

high

The RichDataDisplay component isn't receiving the availableTerminalHeight, causing it to use a hardcoded height for diff rendering. This can lead to improper truncation.

To fix this, you should pass availableHeight to the RichDataDisplay component.

You will also need to update RichDataDisplay.tsx to accept and use this prop:

  1. Add availableTerminalHeight?: number; to RichDataDisplayProps.
  2. Destructure availableTerminalHeight from the component's props.
  3. Pass availableTerminalHeight to the DiffRenderer, for example: availableTerminalHeight={availableTerminalHeight ?? 20}.
Suggested change
content = (
<RichDataDisplay
data={truncatedResultDisplay as RichVisualization}
availableWidth={childWidth}
/>
);
content = (
<RichDataDisplay
data={truncatedResultDisplay as RichVisualization}
availableWidth={childWidth}
availableTerminalHeight={availableHeight}
/>
);

@gemini-cli
Copy link
Copy Markdown
Contributor

gemini-cli Bot commented Mar 13, 2026

Hi there! Thank you for your contribution to Gemini CLI. We really appreciate the time and effort you've put into this pull request.

To keep our backlog manageable and ensure we're focusing on current priorities, we are closing pull requests that haven't seen maintainer activity for 30 days. Currently, the team is prioritizing work associated with 🔒 maintainer only or help wanted issues.

If you believe this change is still critical, please feel free to comment with updated details. Otherwise, we encourage contributors to focus on open issues labeled as help wanted. Thank you for your understanding!

@gemini-cli gemini-cli Bot closed this Mar 13, 2026
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