···11+---
22+"@clack/core": patch
33+"@clack/prompts": patch
44+---
55+66+Don't mutate `initialValue` in `multiselect`, fix parameter type for `validate()`.
77+88+Credits to @banjo for the bug report and initial PR!
+12-33
packages/core/src/prompts/multi-select.ts
···11-import color from 'picocolors';
21import Prompt, { PromptOptions } from './prompt';
3243interface MultiSelectOptions<T extends { value: any }> extends PromptOptions<MultiSelectPrompt<T>> {
···109export default class MultiSelectPrompt<T extends { value: any }> extends Prompt {
1110 options: T[];
1211 cursor: number = 0;
1313- selectedValues: T[];
14121513 private get _value() {
1614 return this.options[this.cursor];
1715 }
18161919- private changeValue() {
2020- const isValueAlreadySelected = this.selectedValues.some((value) => value === this._value.value);
2121- if (isValueAlreadySelected) {
2222- this.selectedValues = this.selectedValues.filter((value) => value !== this._value.value);
2323- } else {
2424- this.selectedValues.push(this._value.value);
2525- }
1717+ private toggleValue() {
1818+ const selected = this.value.some(({ value }: T) => value === this._value.value);
1919+ this.value = selected
2020+ ? this.value.filter(({ value }: T) => value !== this._value.value)
2121+ : [...this.value, this._value];
2622 }
27232824 constructor(opts: MultiSelectOptions<T>) {
2929- if (!opts.validate) {
3030- opts.validate = () => {
3131- if (opts.required && this.selectedValues.length === 0)
3232- return `Please select at least one option\n${color.reset(
3333- color.dim(
3434- `Press ${color.gray(color.bgWhite(color.inverse(' space ')))} to select, ${color.gray(
3535- color.bgWhite(color.inverse(' enter '))
3636- )} to submit`
3737- )
3838- )}`;
3939- };
4040- }
4125 super(opts, false);
4242- this.once('finalize', () => {
4343- this.value = this.selectedValues;
4444- });
2626+4527 this.options = opts.options;
4646- this.cursor = this.options.findIndex(({ value }) => value === opts.cursorAt);
4747- this.selectedValues = opts.initialValue || [];
4848- if (this.cursor === -1) this.cursor = 0;
2828+ this.value = this.options.filter(({ value }) => opts.initialValue?.includes(value));
2929+ this.cursor = Math.max(
3030+ this.options.findIndex(({ value }) => value === opts.cursorAt),
3131+ 0
3232+ );
49335034 this.on('cursor', (key) => {
5135 switch (key) {
···5842 this.cursor = this.cursor === this.options.length - 1 ? 0 : this.cursor + 1;
5943 break;
6044 case 'space':
6161- this.changeValue();
6262- break;
6363- case 'enter':
6464- case 'return':
6565- this.state = 'submit';
6666- this.close();
4545+ this.toggleValue();
6746 break;
6847 }
6948 });