From 8d10fce499e7fb2d91806d5abde38b6ebca0121f Mon Sep 17 00:00:00 2001 From: btea <2356281422@qq.com> Date: Fri, 16 Oct 2026 22:11:26 +0800 Subject: [PATCH 1/2] fix(core): prevent selecting disabled selectKey options --- .changeset/proud-donuts-listen.md | 5 +++++ packages/core/src/prompts/select-key.ts | 8 +++++--- packages/prompts/test/select-key.test.ts | 17 +++++++++++++++++ 3 files changed, 27 insertions(+), 3 deletions(-) create mode 100644 .changeset/proud-donuts-listen.md diff --git a/.changeset/proud-donuts-listen.md b/.changeset/proud-donuts-listen.md new file mode 100644 index 00000000..462f6a4e --- /dev/null +++ b/.changeset/proud-donuts-listen.md @@ -0,0 +1,5 @@ +--- +"@clack/prompts": patch +--- + +Prevent `selectKey` from selecting disabled options. diff --git a/packages/core/src/prompts/select-key.ts b/packages/core/src/prompts/select-key.ts index 04a49a52..c03911e6 100644 --- a/packages/core/src/prompts/select-key.ts +++ b/packages/core/src/prompts/select-key.ts @@ -1,11 +1,13 @@ import Prompt, { type PromptOptions } from './prompt.js'; -export interface SelectKeyOptions +export interface SelectKeyOptions extends PromptOptions> { options: T[]; caseSensitive?: boolean; } -export default class SelectKeyPrompt extends Prompt { +export default class SelectKeyPrompt< + T extends { value: string; disabled?: boolean }, +> extends Prompt { options: T[]; cursor = 0; @@ -31,7 +33,7 @@ export default class SelectKeyPrompt extends Prompt const value = this.options.find(({ value: [initial] }) => { return caseSensitive ? initial === casedKey : initial?.toLowerCase() === casedKey; }); - if (value) { + if (value && !value.disabled) { this.value = value.value; this.state = 'submit'; this.emit('submit'); diff --git a/packages/prompts/test/select-key.test.ts b/packages/prompts/test/select-key.test.ts index 856afde8..1455d009 100644 --- a/packages/prompts/test/select-key.test.ts +++ b/packages/prompts/test/select-key.test.ts @@ -65,6 +65,23 @@ describe.each(['true', 'false'])('text (isCI = %s)', (isCI) => { expect(value).toBe('b'); }); + test('does not select disabled options', async () => { + const result = prompts.selectKey({ + message: 'foo', + options: [ + { label: 'Disabled', value: 'd', disabled: true }, + { label: 'Enabled', value: 'e' }, + ], + input, + output, + }); + + input.emit('keypress', 'd', { name: 'd' }); + input.emit('keypress', 'e', { name: 'e' }); + + expect(await result).toBe('e'); + }); + test('can cancel by pressing escape', async () => { const result = prompts.selectKey({ message: 'foo', From f46cbc73b640aa77c029c6891c20c07f6cd47fa2 Mon Sep 17 00:00:00 2001 From: btea <2356281422@qq.com> Date: Mon, 19 Oct 2026 09:19:08 +0800 Subject: [PATCH 2/2] fix: update --- .changeset/proud-donuts-listen.md | 1 + packages/core/src/prompts/select-key.ts | 7 +++++-- packages/prompts/test/select-key.test.ts | 16 ++++++++++++++++ 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/.changeset/proud-donuts-listen.md b/.changeset/proud-donuts-listen.md index 462f6a4e..02205e97 100644 --- a/.changeset/proud-donuts-listen.md +++ b/.changeset/proud-donuts-listen.md @@ -1,4 +1,5 @@ --- +"@clack/core": patch "@clack/prompts": patch --- diff --git a/packages/core/src/prompts/select-key.ts b/packages/core/src/prompts/select-key.ts index c03911e6..2bf2d319 100644 --- a/packages/core/src/prompts/select-key.ts +++ b/packages/core/src/prompts/select-key.ts @@ -30,10 +30,13 @@ export default class SelectKeyPrompt< return; } - const value = this.options.find(({ value: [initial] }) => { + const value = this.options.find(({ value: [initial], disabled }) => { + if (disabled) { + return false; + } return caseSensitive ? initial === casedKey : initial?.toLowerCase() === casedKey; }); - if (value && !value.disabled) { + if (value) { this.value = value.value; this.state = 'submit'; this.emit('submit'); diff --git a/packages/prompts/test/select-key.test.ts b/packages/prompts/test/select-key.test.ts index 1455d009..6838bb5f 100644 --- a/packages/prompts/test/select-key.test.ts +++ b/packages/prompts/test/select-key.test.ts @@ -82,6 +82,22 @@ describe.each(['true', 'false'])('text (isCI = %s)', (isCI) => { expect(await result).toBe('e'); }); + test('skips disabled options sharing the same initial', async () => { + const result = prompts.selectKey({ + message: 'foo', + options: [ + { label: 'delete', value: 'delete', disabled: true }, + { label: 'deploy', value: 'deploy' }, + ], + input, + output, + }); + + input.emit('keypress', 'd', { name: 'd' }); + + expect(await result).toBe('deploy'); + }); + test('can cancel by pressing escape', async () => { const result = prompts.selectKey({ message: 'foo',