Introduce initial screen reader mode handling and flag (#6653)

This commit is contained in:
christine betts
2025-08-21 22:29:15 +00:00
committed by GitHub
parent 679acc45b2
commit 10286934e6
14 changed files with 125 additions and 52 deletions

View File

@@ -73,6 +73,7 @@ export interface CliArgs {
listExtensions: boolean | undefined;
proxy: string | undefined;
includeDirectories: string[] | undefined;
screenReader: boolean | undefined;
}
export async function parseArguments(): Promise<CliArgs> {
@@ -229,6 +230,11 @@ export async function parseArguments(): Promise<CliArgs> {
// 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 ??

View File

@@ -58,6 +58,7 @@ export interface SummarizeToolOutputSettings {
export interface AccessibilitySettings {
disableLoadingPhrases?: boolean;
screenReader?: boolean;
}
export interface SettingsError {

View File

@@ -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: {

View File

@@ -316,7 +316,7 @@ export async function main() {
/>
</SettingsContext.Provider>
</React.StrictMode>,
{ exitOnCtrlC: false },
{ exitOnCtrlC: false, isScreenReaderEnabled: config.getScreenReader() },
);
checkForUpdates()

View File

@@ -87,6 +87,7 @@ interface MockServerConfig {
getGeminiClient: Mock<() => GeminiClient | undefined>;
getUserTier: Mock<() => Promise<string | undefined>>;
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),
};
});

View File

@@ -923,10 +923,12 @@ const App = ({ config, settings, startupWarnings = [], version }: AppProps) => {
key={staticKey}
items={[
<Box flexDirection="column" key="header">
{!settings.merged.hideBanner && (
{!(settings.merged.hideBanner || config.getScreenReader()) && (
<Header version={version} nightly={nightly} />
)}
{!settings.merged.hideTips && <Tips config={config} />}
{!(settings.merged.hideTips || config.getScreenReader()) && (
<Tips config={config} />
)}
</Box>,
...history.map((h) => (
<HistoryItemDisplay
@@ -1093,12 +1095,14 @@ const App = ({ config, settings, startupWarnings = [], version }: AppProps) => {
<LoadingIndicator
thought={
streamingState === StreamingState.WaitingForConfirmation ||
config.getAccessibility()?.disableLoadingPhrases
config.getAccessibility()?.disableLoadingPhrases ||
config.getScreenReader()
? undefined
: thought
}
currentLoadingPhrase={
config.getAccessibility()?.disableLoadingPhrases
config.getAccessibility()?.disableLoadingPhrases ||
config.getScreenReader()
? undefined
: currentLoadingPhrase
}

View File

@@ -26,6 +26,7 @@ import {
cleanupOldClipboardImages,
} from '../utils/clipboardUtils.js';
import * as path from 'path';
import { SCREEN_READER_USER_PREFIX } from '../constants.js';
export interface InputPromptProps {
buffer: TextBuffer;
@@ -688,7 +689,12 @@ export const InputPrompt: React.FC<InputPromptProps> = ({
>
{shellModeActive ? (
reverseSearchActive ? (
<Text color={theme.text.link}>(r:) </Text>
<Text
color={theme.text.link}
aria-label={SCREEN_READER_USER_PREFIX}
>
(r:){' '}
</Text>
) : (
'! '
)

View File

@@ -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<CompressionDisplayProps> = ({
color={
compression.isPending ? Colors.AccentPurple : Colors.AccentGreen
}
aria-label={SCREEN_READER_MODEL_PREFIX}
>
{text}
</Text>

View File

@@ -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<GeminiMessageProps> = ({
return (
<Box flexDirection="row">
<Box width={prefixWidth}>
<Text color={Colors.AccentPurple}>{prefix}</Text>
<Text
color={Colors.AccentPurple}
aria-label={SCREEN_READER_MODEL_PREFIX}
>
{prefix}
</Text>
</Box>
<Box flexGrow={1} flexDirection="column">
<MarkdownDisplay

View File

@@ -7,6 +7,7 @@
import React from 'react';
import { Text, Box } from 'ink';
import { Colors } from '../../colors.js';
import { SCREEN_READER_USER_PREFIX } from '../../constants.js';
interface UserMessageProps {
text: string;
@@ -31,7 +32,9 @@ export const UserMessage: React.FC<UserMessageProps> = ({ text }) => {
alignSelf="flex-start"
>
<Box width={prefixWidth}>
<Text color={textColor}>{prefix}</Text>
<Text color={textColor} aria-label={SCREEN_READER_USER_PREFIX}>
{prefix}
</Text>
</Box>
<Box flexGrow={1}>
<Text wrap="wrap" color={textColor}>

View File

@@ -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: ';

View File

@@ -62,6 +62,7 @@ export enum ApprovalMode {
export interface AccessibilitySettings {
disableLoadingPhrases?: boolean;
screenReader?: boolean;
}
export interface BugCommandSettings {
@@ -734,6 +735,10 @@ export class Config {
return this.skipNextSpeakerCheck;
}
getScreenReader(): boolean {
return this.accessibility.screenReader ?? false;
}
getEnablePromptCompletion(): boolean {
return this.enablePromptCompletion;
}