Skip to content

Fix(accessibility): add screen reader support to RewindViewer#20750

Merged
Adib234 merged 10 commits intogoogle-gemini:mainfrom
Famous077:fix/rewind-screen-reader-accessibility
Mar 6, 2026
Merged

Fix(accessibility): add screen reader support to RewindViewer#20750
Adib234 merged 10 commits intogoogle-gemini:mainfrom
Famous077:fix/rewind-screen-reader-accessibility

Conversation

@Famous077
Copy link
Copy Markdown
Contributor

Summary:
The /rewind command was completely inaccessible to screen reader users (NVDA, Braille displays). Once triggered, users were trapped in a silent visual-only state with no audible feedback and no standard way to exit — forcing them to use Ctrl+C twice to recover. This fix makes the /rewind interface accessible by rendering a screen reader-friendly text view when screen reader mode is active.

Details:
Added a conditional rendering path in RewindViewer.tsx using the useIsScreenReaderEnabled() hook from ink — the same pattern already used in Notifications.tsx. When a screen reader is detected, the visual BaseSelectionList is replaced with a simple numbered text list of conversation points, along with clear navigation instructions. This mirrors the accessible behavior of the /resume command which already works correctly with screen readers.

Related Issues
Fixed issue : #20285

How to Validate

Set screenReaderMode: true in ~/.gemini/settings.json
Run gemini in interactive mode
Type /rewind and press Enter
Verify a numbered text list of conversation points is displayed
Verify navigation instructions are visible
Press Esc to confirm exit works correctly
Without screen reader mode, confirm the original visual interface is unchanged

Pre-Merge Checklist

  • Updated relevant documentation and README (if needed)
  • Added/updated tests (if needed)
  • Noted breaking changes (if any) — No breaking changes
  • Validated on required platforms/methods:
  • MacOS
  • npm run
  • npx
  • Docker
  • Podman
  • Seatbelt
  • Windows
  • npm run
  • npx
  • Docker
  • Linux
  • npm run
  • npx
  • Docker

@Famous077 Famous077 requested a review from a team as a code owner March 1, 2026 11:05
@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 significantly enhances the accessibility of the /rewind command for users relying on screen readers. By introducing a screen reader-friendly text view, it resolves an issue where these users were unable to interact with or exit the rewind interface, ensuring a more inclusive and functional experience.

Highlights

  • Accessibility Improvement: Implemented screen reader support for the /rewind command, addressing a critical accessibility gap where screen reader users were previously trapped in a silent, visual-only state.
  • Conditional Rendering: Introduced a conditional rendering path in RewindViewer.tsx that detects when a screen reader is enabled and switches to a simplified, text-based interface for navigation.
  • User Experience: Replaced the visual BaseSelectionList with a numbered text list of conversation points and clear navigation instructions, mirroring the accessible behavior of the /resume command.
Changelog
  • packages/cli/src/ui/components/RewindViewer.tsx
    • Imported the useIsScreenReaderEnabled hook from ink.
    • Utilized the useIsScreenReaderEnabled hook to detect screen reader mode.
    • Added a conditional rendering block to display a text-based list and navigation instructions when a screen reader is active, replacing the visual component.
Activity
  • No specific activity (comments, reviews, etc.) has been recorded for this pull request yet, beyond its initial creation.
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

The pull request aims to add screen reader support for the /rewind command. The approach of adding a conditional rendering path for screen reader users is correct. However, the implementation for the screen reader view currently renders a static list of text, which is not interactive. My review includes a critical fix to make the list interactive by using the BaseSelectionList component, which provides the necessary keyboard navigation and selection functionality and aligns with existing UI patterns. This change ensures that the accessibility feature works as intended.

Comment on lines +190 to +197
{items.map((item, idx) => (
<Text key={item.key}>
{idx + 1}.{' '}
{item.value.id === 'current-position'
? 'Stay at current position'
: getCleanedRewindText(item.value)}
</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.

critical

The current implementation for the screen reader view renders a static list of items using items.map(). This makes the list non-interactive, as the key press handlers for navigation (arrow keys) and selection (Enter) from BaseSelectionList are not being used. As a result, screen reader users can see the list but cannot interact with it, which prevents them from using the rewind functionality and negates the goal of this accessibility fix.

To address this, you should use the BaseSelectionList component for the screen reader view as well, but with a simplified renderItem function that produces plain text output. This will provide the necessary keyboard interactivity.

As a follow-up, you could extract the onSelect handler into a named function to avoid duplicating it between the screen reader and visual rendering paths.

        <BaseSelectionList
          items={items}
          initialIndex={items.length - 1}
          isFocused={true}
          showNumbers={false}
          wrapAround={false}
          onSelect={(item: MessageRecord) => {
            if (item?.id) {
              if (item.id === 'current-position') {
                onExit();
              } else {
                selectMessage(item.id);
              }
            }
          }}
          renderItem={(itemWrapper) => {
            const item = itemWrapper.value;
            const text =
              item.id === 'current-position'
                ? 'Stay at current position'
                : getCleanedRewindText(item);
            return (
              <Text>
                {itemWrapper.index + 1}. {text}
              </Text>
            );
          }}
        />
References
  1. Maintain consistency with existing UI behavior across components. Defer non-standard UX pattern improvements to be addressed holistically rather than in a single component.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Updated successfully

@Adib234
Copy link
Copy Markdown
Contributor

Adib234 commented Mar 2, 2026

Great work on improving the accessibility of the /rewind command. Here are a few points to address before merging:

  1. Tests: Add coverage in RewindViewer.test.tsx by mocking useIsScreenReaderEnabled(true) to ensure the accessible view remains functional.
  2. Selection Announcement: BaseSelectionList ties aria-state to the item number. With showNumbers={false}, screen readers might miss selection changes. Consider enabling showNumbers={true} or moving the aria-state.
  3. Confirmation Dialog: RewindConfirmation.tsx currently lacks screen reader support. Users may get stuck after selecting a message. Please extend the accessible view to the confirmation step.
  4. Numbering: Instead of manually prepending numbers in renderItem, use the built-in showNumbers={true} prop in BaseSelectionList for better consistency.
  5. UI State: Ensure the accessible view respects maxItemsToShow or terminal boundaries to prevent layout issues in small windows.
  6. Theming: Use theme.text.secondary for the navigation instructions to match existing TUI patterns.

@Famous077
Copy link
Copy Markdown
Contributor Author

Famous077 commented Mar 2, 2026

Hi @Adib234 , addressed all 6 points from your review:

  • Tests — Added screen reader accessibility test in RewindViewer.test.tsx
  • Selection Announcement — Switched to showNumbers={true} to correctly tie aria-state to item index
  • Confirmation Dialog — Extended accessible view to RewindConfirmation.tsx
  • Numbering — Replaced manual prepending with built-in showNumbers={true}
  • UI State — Applied maxItemsToShow slice in accessible view to prevent overflow
  • Theming — Navigation instructions now use theme.text.secondary

All 14 tests passing. Let me know if anything needs further adjustment.

@Adib234
Copy link
Copy Markdown
Contributor

Adib234 commented Mar 3, 2026

PR 20750 - Review Findings

Thank you for the accessibility improvements! Here are some important issues that should be addressed:

  1. Critical Bug in RewindViewer.tsx: The manual slicing items.slice(0, maxItemsToShow) in the accessible view (lines 191+) will hide all recent messages and the "Stay at current position" option from screen reader users, only showing the maxItemsToShow oldest messages. Since BaseSelectionList already handles maxItemsToShow and scrolling internally, you should pass the full items list and let it manage the selection window. For Rewind, starting at the end of the list is essential for UX.

  2. Test Flaw in RewindViewer.test.tsx: The new test renders the rewind viewer with conversation items does not actually exercise the screen reader code path. useIsScreenReaderEnabled() from ink defaults to false in this test environment. To properly validate the accessible view, you should mock ink's hook like this:

    vi.mock('ink', async () => {
      const actual = await vi.importActual('ink');
      return { ...actual, useIsScreenReaderEnabled: vi.fn() };
    });
    // ... and then in the test:
    vi.mocked(useIsScreenReaderEnabled).mockReturnValue(true);
  3. Consistency in RewindConfirmation.tsx: This component currently accepts isScreenReaderEnabled as a prop while its parent uses the useIsScreenReaderEnabled() hook. For consistency with other components in the codebase (e.g., Composer, Notifications, RewindViewer), it's better to use the hook directly inside RewindConfirmation.

Fixing these will ensure the feature actually works as intended for screen reader users and is properly tested.

@gemini-cli gemini-cli bot added the help wanted We will accept PRs from all issues marked as "help wanted". Thanks for your support! label Mar 4, 2026
@Famous077
Copy link
Copy Markdown
Contributor Author

Hi @Adib234, I have addressed all 3 points:

  1. Critical Bug : Removed items.slice(), passing full items to BaseSelectionList with initialIndex={items.length - 1}.
  2. Test Flaw : Added ink mock and mockReturnValue(true) to properly exercise the screen reader path. All 15 tests passing.
  3. Consistency : Moved useIsScreenReaderEnabled() hook inside RewindConfirmation.tsx, removed the prop.

Let me know if anything needs further adjustment.

@Adib234
Copy link
Copy Markdown
Contributor

Adib234 commented Mar 5, 2026

Hi @Famous077, thank you for the PR! The accessibility improvements for /rewind are a great addition.

I have a few points for cleanup and correctness:

  1. Remove Development Comments: There are several internal comments like // FIX #3: ... and // FIX #6: ... in the code. Please remove these "fix markers" before merging to keep the codebase clean.
  2. Redundant Prop in RewindConfirmation: You've added isScreenReaderEnabled to the RewindConfirmationProps interface, but the component body uses the useIsScreenReaderEnabled() hook directly and doesn't destructure the prop. Since you've opted for the hook (which is more consistent), please remove the prop from the interface and the RewindViewer call site.
  3. Flawed Accessibility Tests: The snapshots for the "Screen Reader Accessibility" block currently show visual elements like borders (╭───, ). This means the tests aren't actually exercising the screen reader mode. To fix this, you need to mock the hook to return true within that block:
    beforeEach(() => {
      vi.mocked(useIsScreenReaderEnabled).mockReturnValue(true);
    });
    This will ensure the snapshots capture the intended plain-text accessible view.

Once these are addressed, the PR should be in great shape!

@Famous077
Copy link
Copy Markdown
Contributor Author

Hi @Adib234 , Can you check and review now. I have changed everything as per you suggestions. let me know if anything else need to be implemented.

Copy link
Copy Markdown
Contributor

@Adib234 Adib234 left a comment

Choose a reason for hiding this comment

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

LGTM, thanks for this!

@Adib234 Adib234 enabled auto-merge March 6, 2026 14:58
@Adib234 Adib234 added this pull request to the merge queue Mar 6, 2026
Merged via the queue into google-gemini:main with commit d97eaf3 Mar 6, 2026
27 checks passed
kunal-10-cloud pushed a commit to kunal-10-cloud/gemini-cli that referenced this pull request Mar 12, 2026
yashodipmore pushed a commit to yashodipmore/geemi-cli that referenced this pull request Mar 21, 2026
SUNDRAM07 pushed a commit to SUNDRAM07/gemini-cli that referenced this pull request Mar 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

help wanted We will accept PRs from all issues marked as "help wanted". Thanks for your support!

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants