From 10286934e6a549dcad557adecfc087552e13c983 Mon Sep 17 00:00:00 2001 From: christine betts Date: Thu, 21 Aug 2025 22:29:15 +0000 Subject: Introduce initial screen reader mode handling and flag (#6653) --- packages/cli/src/config/config.ts | 14 +++++++++++++- packages/cli/src/config/settings.ts | 1 + packages/cli/src/config/settingsSchema.ts | 10 ++++++++++ packages/cli/src/gemini.tsx | 2 +- packages/cli/src/ui/App.test.tsx | 2 ++ packages/cli/src/ui/App.tsx | 12 ++++++++---- packages/cli/src/ui/components/InputPrompt.tsx | 8 +++++++- .../cli/src/ui/components/messages/CompressionMessage.tsx | 2 ++ packages/cli/src/ui/components/messages/GeminiMessage.tsx | 8 +++++++- packages/cli/src/ui/components/messages/UserMessage.tsx | 5 ++++- packages/cli/src/ui/constants.ts | 4 ++++ 11 files changed, 59 insertions(+), 9 deletions(-) (limited to 'packages/cli/src') diff --git a/packages/cli/src/config/config.ts b/packages/cli/src/config/config.ts index 0b21ff2e..aaaf293d 100644 --- a/packages/cli/src/config/config.ts +++ b/packages/cli/src/config/config.ts @@ -73,6 +73,7 @@ export interface CliArgs { listExtensions: boolean | undefined; proxy: string | undefined; includeDirectories: string[] | undefined; + screenReader: boolean | undefined; } export async function parseArguments(): Promise { @@ -229,6 +230,11 @@ export async function parseArguments(): Promise { // Handle comma-separated values dirs.flatMap((dir) => dir.split(',').map((d) => d.trim())), }) + .option('screen-reader', { + type: 'boolean', + description: 'Enable screen reader mode for accessibility.', + default: false, + }) .check((argv) => { if (argv.prompt && argv['promptInteractive']) { @@ -465,6 +471,9 @@ export async function loadCliConfig( const sandboxConfig = await loadSandboxConfig(settings, argv); + // The screen reader argument takes precedence over the accessibility setting. + const screenReader = + argv.screenReader ?? settings.accessibility?.screenReader ?? false; return new Config({ sessionId, embeddingModel: DEFAULT_GEMINI_EMBEDDING_MODEL, @@ -490,7 +499,10 @@ export async function loadCliConfig( argv.show_memory_usage || settings.showMemoryUsage || false, - accessibility: settings.accessibility, + accessibility: { + ...settings.accessibility, + screenReader, + }, telemetry: { enabled: argv.telemetry ?? settings.telemetry?.enabled, target: (argv.telemetryTarget ?? diff --git a/packages/cli/src/config/settings.ts b/packages/cli/src/config/settings.ts index 3f94fe65..5f8c706d 100644 --- a/packages/cli/src/config/settings.ts +++ b/packages/cli/src/config/settings.ts @@ -58,6 +58,7 @@ export interface SummarizeToolOutputSettings { export interface AccessibilitySettings { disableLoadingPhrases?: boolean; + screenReader?: boolean; } export interface SettingsError { diff --git a/packages/cli/src/config/settingsSchema.ts b/packages/cli/src/config/settingsSchema.ts index 7f28b698..5f939b56 100644 --- a/packages/cli/src/config/settingsSchema.ts +++ b/packages/cli/src/config/settingsSchema.ts @@ -206,6 +206,16 @@ export const SETTINGS_SCHEMA = { description: 'Disable loading phrases for accessibility', showInDialog: true, }, + screenReader: { + type: 'boolean', + label: 'Screen Reader Mode', + category: 'Accessibility', + requiresRestart: true, + default: false, + description: + 'Render output in plain-text to be more screen reader accessible', + showInDialog: true, + }, }, }, checkpointing: { diff --git a/packages/cli/src/gemini.tsx b/packages/cli/src/gemini.tsx index 6661d3ef..b285a5af 100644 --- a/packages/cli/src/gemini.tsx +++ b/packages/cli/src/gemini.tsx @@ -316,7 +316,7 @@ export async function main() { /> , - { exitOnCtrlC: false }, + { exitOnCtrlC: false, isScreenReaderEnabled: config.getScreenReader() }, ); checkForUpdates() diff --git a/packages/cli/src/ui/App.test.tsx b/packages/cli/src/ui/App.test.tsx index 9f8a681f..d5f2ba82 100644 --- a/packages/cli/src/ui/App.test.tsx +++ b/packages/cli/src/ui/App.test.tsx @@ -87,6 +87,7 @@ interface MockServerConfig { getGeminiClient: Mock<() => GeminiClient | undefined>; getUserTier: Mock<() => Promise>; getIdeClient: Mock<() => { getCurrentIde: Mock<() => string | undefined> }>; + getScreenReader: Mock<() => boolean>; } // Mock @google/gemini-cli-core and its Config class @@ -168,6 +169,7 @@ vi.mock('@google/gemini-cli-core', async (importOriginal) => { getConnectionStatus: vi.fn(() => 'connected'), })), isTrustedFolder: vi.fn(() => true), + getScreenReader: vi.fn(() => false), }; }); diff --git a/packages/cli/src/ui/App.tsx b/packages/cli/src/ui/App.tsx index 6bf4f770..b4ec6a57 100644 --- a/packages/cli/src/ui/App.tsx +++ b/packages/cli/src/ui/App.tsx @@ -923,10 +923,12 @@ const App = ({ config, settings, startupWarnings = [], version }: AppProps) => { key={staticKey} items={[ - {!settings.merged.hideBanner && ( + {!(settings.merged.hideBanner || config.getScreenReader()) && (
)} - {!settings.merged.hideTips && } + {!(settings.merged.hideTips || config.getScreenReader()) && ( + + )} , ...history.map((h) => ( { = ({ > {shellModeActive ? ( reverseSearchActive ? ( - (r:) + + (r:){' '} + ) : ( '! ' ) diff --git a/packages/cli/src/ui/components/messages/CompressionMessage.tsx b/packages/cli/src/ui/components/messages/CompressionMessage.tsx index c7ef122b..6aededbb 100644 --- a/packages/cli/src/ui/components/messages/CompressionMessage.tsx +++ b/packages/cli/src/ui/components/messages/CompressionMessage.tsx @@ -9,6 +9,7 @@ import { Box, Text } from 'ink'; import { CompressionProps } from '../../types.js'; import Spinner from 'ink-spinner'; import { Colors } from '../../colors.js'; +import { SCREEN_READER_MODEL_PREFIX } from '../../constants.js'; export interface CompressionDisplayProps { compression: CompressionProps; @@ -40,6 +41,7 @@ export const CompressionMessage: React.FC = ({ color={ compression.isPending ? Colors.AccentPurple : Colors.AccentGreen } + aria-label={SCREEN_READER_MODEL_PREFIX} > {text} diff --git a/packages/cli/src/ui/components/messages/GeminiMessage.tsx b/packages/cli/src/ui/components/messages/GeminiMessage.tsx index 9863acd6..cfc3a297 100644 --- a/packages/cli/src/ui/components/messages/GeminiMessage.tsx +++ b/packages/cli/src/ui/components/messages/GeminiMessage.tsx @@ -8,6 +8,7 @@ import React from 'react'; import { Text, Box } from 'ink'; import { MarkdownDisplay } from '../../utils/MarkdownDisplay.js'; import { Colors } from '../../colors.js'; +import { SCREEN_READER_MODEL_PREFIX } from '../../constants.js'; interface GeminiMessageProps { text: string; @@ -28,7 +29,12 @@ export const GeminiMessage: React.FC = ({ return ( - {prefix} + + {prefix} + = ({ text }) => { alignSelf="flex-start" > - {prefix} + + {prefix} + diff --git a/packages/cli/src/ui/constants.ts b/packages/cli/src/ui/constants.ts index 6a77631c..6a1047dc 100644 --- a/packages/cli/src/ui/constants.ts +++ b/packages/cli/src/ui/constants.ts @@ -15,3 +15,7 @@ export const UI_WIDTH = export const STREAM_DEBOUNCE_MS = 100; export const SHELL_COMMAND_NAME = 'Shell Command'; + +export const SCREEN_READER_USER_PREFIX = 'User: '; + +export const SCREEN_READER_MODEL_PREFIX = 'Model: '; -- cgit v1.2.3