From 4aa3b288a3fb16c4ee89c877a5bc1de86e350c37 Mon Sep 17 00:00:00 2001 From: Paul Valladares <85648028+dreyfus92@users.noreply.github.com> Date: Thu, 13 Aug 2026 16:42:28 -0600 Subject: [PATCH 1/2] feat(core): add `accessible` prompt option --- .changeset/dirty-forks-study.md | 5 +++ packages/core/src/index.ts | 2 +- packages/core/src/prompts/prompt.ts | 14 ++++++- packages/core/src/types.ts | 11 +++++ packages/core/test/prompts/prompt.test.ts | 51 ++++++++++++++++++++++- 5 files changed, 79 insertions(+), 4 deletions(-) create mode 100644 .changeset/dirty-forks-study.md diff --git a/.changeset/dirty-forks-study.md b/.changeset/dirty-forks-study.md new file mode 100644 index 00000000..89bd7530 --- /dev/null +++ b/.changeset/dirty-forks-study.md @@ -0,0 +1,5 @@ +--- +'@clack/core': minor +--- + +Add an `accessible` option to the shared prompt options (`CommonPromptOptions`) and a resolved `accessible` getter on the base `Prompt`, so any prompt can opt into accessible mode individually. Resolution order: per-prompt option > `updateSettings({ accessible })` > the `ACCESSIBLE` environment variable. diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 1ce83777..66fb51e6 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -20,7 +20,7 @@ export type { SelectKeyOptions } from './prompts/select-key.js'; export { default as SelectKeyPrompt } from './prompts/select-key.js'; export type { TextOptions } from './prompts/text.js'; export { default as TextPrompt } from './prompts/text.js'; -export type { ClackState as State } from './types.js'; +export type { ClackState as State, CommonPromptOptions } from './types.js'; export { block, CANCEL_SYMBOL, diff --git a/packages/core/src/prompts/prompt.ts b/packages/core/src/prompts/prompt.ts index 2291544b..051a7f0f 100644 --- a/packages/core/src/prompts/prompt.ts +++ b/packages/core/src/prompts/prompt.ts @@ -3,12 +3,13 @@ import readline, { type Key, type ReadLine } from 'node:readline'; import type { Readable, Writable } from 'node:stream'; import { wrapAnsi } from 'fast-wrap-ansi'; import { cursor, erase } from 'sisteransi'; -import type { ClackEvents, ClackState } from '../types.js'; +import type { ClackEvents, ClackState, CommonPromptOptions } from '../types.js'; import type { Action } from '../utils/index.js'; import { CANCEL_SYMBOL, diffLines, getRows, + isAccessible, isActionKey, setRawMode, settings, @@ -16,7 +17,7 @@ import { import type { Validate } from '../utils/validation.js'; import { runValidation } from '../utils/validation.js'; -export interface PromptOptions> { +export interface PromptOptions> extends CommonPromptOptions { render(this: Omit): string | undefined; initialValue?: any; initialUserInput?: string; @@ -50,6 +51,15 @@ export default class Prompt { public value: TValue | undefined; public userInput = ''; + /** + * Whether accessible (static, screen-reader friendly) output is enabled for + * this prompt, resolved from the `accessible` option, the global setting, + * and the `ACCESSIBLE` env var. + */ + public get accessible(): boolean { + return isAccessible(this.opts.accessible); + } + constructor(options: PromptOptions>, trackValue = true) { const { input = stdin, output = stdout, render, signal, ...opts } = options; diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index a4a56405..b3dc1a87 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -6,6 +6,17 @@ import type { Action } from './utils/settings.js'; */ export type ClackState = 'initial' | 'active' | 'cancel' | 'submit' | 'error'; +/** + * Options shared by all Clack prompts + */ +export interface CommonPromptOptions { + /** + * Force accessible (static, screen-reader friendly) output for this prompt. + * Overrides the global `accessible` setting and the `ACCESSIBLE` env var. + */ + accessible?: boolean; +} + /** * Typed event emitter for clack */ diff --git a/packages/core/test/prompts/prompt.test.ts b/packages/core/test/prompts/prompt.test.ts index 89c1e37f..02dc4570 100644 --- a/packages/core/test/prompts/prompt.test.ts +++ b/packages/core/test/prompts/prompt.test.ts @@ -2,7 +2,7 @@ import { type } from 'arktype'; import { cursor } from 'sisteransi'; import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest'; import { default as Prompt } from '../../src/prompts/prompt.js'; -import { isCancel } from '../../src/utils/index.js'; +import { isCancel, settings } from '../../src/utils/index.js'; import { MockReadable } from '../mock-readable.js'; import { MockWritable } from '../mock-writable.js'; @@ -315,6 +315,55 @@ describe('Prompt', () => { }); }); + describe('accessible', () => { + const originalEnv = process.env.ACCESSIBLE; + + afterEach(() => { + if (originalEnv === undefined) { + delete process.env.ACCESSIBLE; + } else { + process.env.ACCESSIBLE = originalEnv; + } + settings.accessible = undefined; + }); + + test('accessible: true option enables it', () => { + const instance = new Prompt({ + input, + output, + render: () => 'foo', + accessible: true, + }); + + expect(instance.accessible).to.equal(true); + }); + + test('accessible: false option overrides the env var', () => { + process.env.ACCESSIBLE = '1'; + const instance = new Prompt({ + input, + output, + render: () => 'foo', + accessible: false, + }); + + expect(instance.accessible).to.equal(false); + }); + + test('falls through to the env var when option is unset', () => { + delete process.env.ACCESSIBLE; + const instance = new Prompt({ + input, + output, + render: () => 'foo', + }); + + expect(instance.accessible).to.equal(false); + process.env.ACCESSIBLE = '1'; + expect(instance.accessible).to.equal(true); + }); + }); + describe('standard schema', () => { test('accepts invalid initial value', () => { const instance = new Prompt({ From 9d660c65e5e14576bc506598054f14b2889ce653 Mon Sep 17 00:00:00 2001 From: Paul Valladares <85648028+dreyfus92@users.noreply.github.com> Date: Fri, 14 Aug 2026 12:57:02 -0600 Subject: [PATCH 2/2] Apply suggestions from @43081j's review --- .changeset/dirty-forks-study.md | 2 +- packages/core/src/index.ts | 2 +- packages/core/src/prompts/prompt.ts | 9 +++++++-- packages/core/src/types.ts | 11 ----------- 4 files changed, 9 insertions(+), 15 deletions(-) diff --git a/.changeset/dirty-forks-study.md b/.changeset/dirty-forks-study.md index 89bd7530..d1a4f42c 100644 --- a/.changeset/dirty-forks-study.md +++ b/.changeset/dirty-forks-study.md @@ -2,4 +2,4 @@ '@clack/core': minor --- -Add an `accessible` option to the shared prompt options (`CommonPromptOptions`) and a resolved `accessible` getter on the base `Prompt`, so any prompt can opt into accessible mode individually. Resolution order: per-prompt option > `updateSettings({ accessible })` > the `ACCESSIBLE` environment variable. +Add an `accessible` option to the shared `PromptOptions` and a resolved `accessible` getter on the base `Prompt`, so any prompt can opt into accessible mode individually. Resolution order: per-prompt option > `updateSettings({ accessible })` > the `ACCESSIBLE` environment variable. diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 66fb51e6..1ce83777 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -20,7 +20,7 @@ export type { SelectKeyOptions } from './prompts/select-key.js'; export { default as SelectKeyPrompt } from './prompts/select-key.js'; export type { TextOptions } from './prompts/text.js'; export { default as TextPrompt } from './prompts/text.js'; -export type { ClackState as State, CommonPromptOptions } from './types.js'; +export type { ClackState as State } from './types.js'; export { block, CANCEL_SYMBOL, diff --git a/packages/core/src/prompts/prompt.ts b/packages/core/src/prompts/prompt.ts index 051a7f0f..16830864 100644 --- a/packages/core/src/prompts/prompt.ts +++ b/packages/core/src/prompts/prompt.ts @@ -3,7 +3,7 @@ import readline, { type Key, type ReadLine } from 'node:readline'; import type { Readable, Writable } from 'node:stream'; import { wrapAnsi } from 'fast-wrap-ansi'; import { cursor, erase } from 'sisteransi'; -import type { ClackEvents, ClackState, CommonPromptOptions } from '../types.js'; +import type { ClackEvents, ClackState } from '../types.js'; import type { Action } from '../utils/index.js'; import { CANCEL_SYMBOL, @@ -17,8 +17,13 @@ import { import type { Validate } from '../utils/validation.js'; import { runValidation } from '../utils/validation.js'; -export interface PromptOptions> extends CommonPromptOptions { +export interface PromptOptions> { render(this: Omit): string | undefined; + /** + * Whether to render this prompt in accessible (static, screen-reader friendly) mode. + * Takes precedence over the global `accessible` setting and the `ACCESSIBLE` env var. + */ + accessible?: boolean; initialValue?: any; initialUserInput?: string; diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index b3dc1a87..a4a56405 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -6,17 +6,6 @@ import type { Action } from './utils/settings.js'; */ export type ClackState = 'initial' | 'active' | 'cancel' | 'submit' | 'error'; -/** - * Options shared by all Clack prompts - */ -export interface CommonPromptOptions { - /** - * Force accessible (static, screen-reader friendly) output for this prompt. - * Overrides the global `accessible` setting and the `ACCESSIBLE` env var. - */ - accessible?: boolean; -} - /** * Typed event emitter for clack */