Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions src/components/SettingsDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { themeToCss, detectThemeTone } from '../lib/custom-theme';
import {
store,
setTerminalFont,
setTerminalScreenReaderMode,
setAutoTrustFolders,
setShowPlans,
setShowPromptInput,
Expand Down Expand Up @@ -463,6 +464,13 @@ export function SettingsDialog(props: SettingsDialogProps) {
description="Enable antialiasing and geometric text rendering"
align="flex-start"
/>
<SettingsCheckboxRow
label="Terminal screen reader mode"
checked={store.terminalScreenReaderMode}
onChange={setTerminalScreenReaderMode}
description="Expose terminal output to assistive technologies. May reduce rendering performance."
align="flex-start"
/>
</SettingsSection>

<SettingsSection title="New Task Defaults">
Expand Down
7 changes: 7 additions & 0 deletions src/components/TerminalView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -416,6 +416,7 @@ export function TerminalView(props: TerminalViewProps) {
cursorBlink: true,
fontSize: initialFontSize,
fontFamily: getTerminalFontFamily(store.terminalFont),
screenReaderMode: store.terminalScreenReaderMode,
theme: activeTerminalTheme(),
allowProposedApi: true,
scrollback: TERMINAL_SCROLLBACK_LINES,
Expand Down Expand Up @@ -1107,6 +1108,12 @@ export function TerminalView(props: TerminalViewProps) {
markDirty(props.agentId);
});

createEffect(() => {
const screenReaderMode = store.terminalScreenReaderMode;
if (!term) return;
term.options.screenReaderMode = screenReaderMode;
});

createEffect(() => {
if (!term) return;
term.options.theme = activeTerminalTheme();
Expand Down
9 changes: 9 additions & 0 deletions src/store/autosave.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,15 @@ describe('autosave snapshot includes new-task-default fields', () => {
setStore('defaultPropagateSkipPermissions', false);
});

it('terminalScreenReaderMode changes the snapshot', () => {
setStore('terminalScreenReaderMode', false);
const before = persistedSnapshot();
setStore('terminalScreenReaderMode', true);
const after = persistedSnapshot();
expect(before).not.toBe(after);
setStore('terminalScreenReaderMode', false);
});

it('showSteps is not tracked separately (migrated to defaultStepsEnabled)', () => {
expect('showSteps' in store).toBe(false);
});
Expand Down
1 change: 1 addition & 0 deletions src/store/autosave.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export function persistedSnapshot(): string {
mergedLinesAdded: store.mergedLinesAdded,
mergedLinesRemoved: store.mergedLinesRemoved,
terminalFont: store.terminalFont,
terminalScreenReaderMode: store.terminalScreenReaderMode,
themePreset: store.themePreset,
windowState: store.windowState,
autoTrustFolders: store.autoTrustFolders,
Expand Down
1 change: 1 addition & 0 deletions src/store/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ export const [store, setStore] = createStore<AppStore>({
mergedLinesAdded: 0,
mergedLinesRemoved: 0,
terminalFont: DEFAULT_TERMINAL_FONT,
terminalScreenReaderMode: false,
themePreset: 'islands-dark',
appearanceMode: 'dark',
lightThemePreset: 'islands-light',
Expand Down
48 changes: 48 additions & 0 deletions src/store/persistence.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ beforeEach(() => {
setStore('customAgents', []);
setStore('coordinatorControlHintDismissed', false);
setStore('autoStartRemoteAccess', false);
setStore('terminalScreenReaderMode', false);
});

describe('resolveIncomingPanelUserSize', () => {
Expand Down Expand Up @@ -490,6 +491,53 @@ describe('loadState theme persistence', () => {
});
});

describe('terminal screen reader mode persistence', () => {
it('defaults to false when absent from saved state', async () => {
mockInvoke.mockResolvedValueOnce(basePayload());

await loadState();

expect(store.terminalScreenReaderMode).toBe(false);
});

it('restores an enabled screen reader mode', async () => {
mockInvoke.mockResolvedValueOnce(basePayload({ terminalScreenReaderMode: true }));

await loadState();

expect(store.terminalScreenReaderMode).toBe(true);
});

it.each(['true', 1, null])('rejects a non-boolean persisted value (%s)', async (value) => {
setStore('terminalScreenReaderMode', true);
mockInvoke.mockResolvedValueOnce(basePayload({ terminalScreenReaderMode: value }));

await loadState();

expect(store.terminalScreenReaderMode).toBe(false);
});

it('omits the disabled default when saving', async () => {
setStore('terminalScreenReaderMode', false);
mockInvoke.mockResolvedValueOnce(undefined);

await saveState();

const saved = JSON.parse(mockInvoke.mock.calls[0][1].json);
expect(saved.terminalScreenReaderMode).toBeUndefined();
});

it('persists the enabled setting', async () => {
setStore('terminalScreenReaderMode', true);
mockInvoke.mockResolvedValueOnce(undefined);

await saveState();

const saved = JSON.parse(mockInvoke.mock.calls[0][1].json);
expect(saved.terminalScreenReaderMode).toBe(true);
});
});

describe('coordinator control hint persistence', () => {
it('does not persist dismissed=false', async () => {
setStore('coordinatorControlHintDismissed', false);
Expand Down
4 changes: 4 additions & 0 deletions src/store/persistence.ts
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,7 @@ export async function saveState(): Promise<void> {
mergedLinesAdded: store.mergedLinesAdded,
mergedLinesRemoved: store.mergedLinesRemoved,
terminalFont: store.terminalFont,
terminalScreenReaderMode: store.terminalScreenReaderMode || undefined,
themePreset: store.themePreset,
showPromptInput: store.showPromptInput,
fontSmoothing: store.fontSmoothing,
Expand Down Expand Up @@ -351,6 +352,7 @@ interface LegacyPersistedState {
mergedLinesAdded?: unknown;
mergedLinesRemoved?: unknown;
terminalFont?: unknown;
terminalScreenReaderMode?: unknown;
themePreset?: unknown;
showPromptInput?: unknown;
fontSmoothing?: unknown;
Expand Down Expand Up @@ -497,6 +499,8 @@ export async function loadState(): Promise<void> {
typeof raw.terminalFont === 'string' && raw.terminalFont.trim()
? raw.terminalFont
: DEFAULT_TERMINAL_FONT;
s.terminalScreenReaderMode =
typeof raw.terminalScreenReaderMode === 'boolean' ? raw.terminalScreenReaderMode : false;
s.themePreset = isLookPreset(raw.themePreset) ? raw.themePreset : 'minimal';
s.showPromptInput = typeof raw.showPromptInput === 'boolean' ? raw.showPromptInput : true;
s.fontSmoothing = typeof raw.fontSmoothing === 'boolean' ? raw.fontSmoothing : true;
Expand Down
1 change: 1 addition & 0 deletions src/store/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ export {
toggleTaskFocusMode,
setTaskSplitMode,
setTerminalFont,
setTerminalScreenReaderMode,
applyAppearanceMode,
markCustomThemesReady,
setAppearanceMode,
Expand Down
2 changes: 2 additions & 0 deletions src/store/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,7 @@ export interface PersistedState {
mergedLinesAdded?: number;
mergedLinesRemoved?: number;
terminalFont?: string;
terminalScreenReaderMode?: boolean;
themePreset?: LookPreset;
showPromptInput?: boolean;
fontSmoothing?: boolean;
Expand Down Expand Up @@ -346,6 +347,7 @@ export interface AppStore {
mergedLinesAdded: number;
mergedLinesRemoved: number;
terminalFont: string;
terminalScreenReaderMode: boolean;
themePreset: LookPreset;
showPromptInput: boolean;
fontSmoothing: boolean;
Expand Down
11 changes: 11 additions & 0 deletions src/store/ui.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ type MockStore = {
panelUserSize: Record<string, number>;
projectsCollapsed: boolean;
sidebarFocusedProjectId: string | null;
terminalScreenReaderMode: boolean;
};

let mockStore: MockStore;
Expand Down Expand Up @@ -63,6 +64,7 @@ import {
setPanelUserSize,
deletePanelUserSize,
setProjectsCollapsed,
setTerminalScreenReaderMode,
} from './ui';

beforeEach(() => {
Expand All @@ -78,6 +80,7 @@ beforeEach(() => {
panelUserSize: {},
projectsCollapsed: false,
sidebarFocusedProjectId: null,
terminalScreenReaderMode: false,
});

vi.stubGlobal('requestAnimationFrame', (cb: FrameRequestCallback) => {
Expand Down Expand Up @@ -195,3 +198,11 @@ describe('setProjectsCollapsed', () => {
expect(mockStore.sidebarFocusedProjectId).toBeNull();
});
});

describe('setTerminalScreenReaderMode', () => {
it('updates the global terminal accessibility option', () => {
setTerminalScreenReaderMode(true);

expect(mockStore.terminalScreenReaderMode).toBe(true);
});
});
4 changes: 4 additions & 0 deletions src/store/ui.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,10 @@ export function setTerminalFont(terminalFont: string): void {
setStore('terminalFont', terminalFont);
}

export function setTerminalScreenReaderMode(enabled: boolean): void {
setStore('terminalScreenReaderMode', enabled);
}

export function applyAppearanceMode(): void {
const isDark = osIsDark();
const mode = store.appearanceMode;
Expand Down
Loading