diff --git a/.changeset/wild-boats-hammer.md b/.changeset/wild-boats-hammer.md new file mode 100644 index 00000000..50829fc3 --- /dev/null +++ b/.changeset/wild-boats-hammer.md @@ -0,0 +1,6 @@ +--- +"@clack/prompts": minor +"@clack/core": minor +--- + +Replaces `picocolors` with Node.js built-in `styleText` diff --git a/examples/basic/autocomplete-multiselect.ts b/examples/basic/autocomplete-multiselect.ts index c34420c6..e3636f76 100644 --- a/examples/basic/autocomplete-multiselect.ts +++ b/examples/basic/autocomplete-multiselect.ts @@ -1,5 +1,5 @@ import * as p from '@clack/prompts'; -import color from 'picocolors'; +import { styleText } from 'node:util'; /** * Example demonstrating the integrated autocomplete multiselect component @@ -9,17 +9,17 @@ import color from 'picocolors'; async function main() { console.clear(); - p.intro(`${color.bgCyan(color.black(' Integrated Autocomplete Multiselect Example '))}`); + p.intro(`${styleText('bgCyan', styleText('black', ' Integrated Autocomplete Multiselect Example '))}`); p.note( ` -${color.cyan('Filter and select multiple items in a single interface:')} -- ${color.yellow('Type')} to filter the list in real-time -- Use ${color.yellow('up/down arrows')} to navigate with improved stability -- Press ${color.yellow('Space')} to select/deselect the highlighted item ${color.green('(multiple selections allowed)')} -- Use ${color.yellow('Backspace')} to modify your filter text when searching for different options -- Press ${color.yellow('Enter')} when done selecting all items -- Press ${color.yellow('Ctrl+C')} to cancel +${styleText('cyan', 'Filter and select multiple items in a single interface:')} +- ${styleText('yellow', 'Type')} to filter the list in real-time +- Use ${styleText('yellow', 'up/down arrows')} to navigate with improved stability +- Press ${styleText('yellow', 'Space')} to select/deselect the highlighted item ${styleText('green', '(multiple selections allowed)')} +- Use ${styleText('yellow', 'Backspace')} to modify your filter text when searching for different options +- Press ${styleText('yellow', 'Enter')} when done selecting all items +- Press ${styleText('yellow', 'Ctrl+C')} to cancel `, 'Instructions' ); @@ -79,7 +79,7 @@ ${color.cyan('Filter and select multiple items in a single interface:')} // Display selected frameworks with detailed information p.note( - `You selected ${color.green(selectedFrameworks.length)} frameworks:`, + `You selected ${styleText('green', `${selectedFrameworks.length}`)} frameworks:`, 'Selection Complete' ); @@ -88,13 +88,13 @@ ${color.cyan('Filter and select multiple items in a single interface:')} .map((value) => { const framework = frameworks.find((f) => f.value === value); return framework - ? `${color.cyan(framework.label)} ${color.dim(`- ${framework.hint}`)}` + ? `${styleText('cyan', framework.label)} ${styleText('dim', `- ${framework.hint}`)}` : value; }) .join('\n'); p.log.message(selectedDetails); - p.outro(`Successfully selected ${color.green(selectedFrameworks.length)} frameworks.`); + p.outro(`Successfully selected ${styleText('green', `${selectedFrameworks.length}`)} frameworks.`); } main().catch(console.error); diff --git a/examples/basic/autocomplete.ts b/examples/basic/autocomplete.ts index ea94ece1..7b924603 100644 --- a/examples/basic/autocomplete.ts +++ b/examples/basic/autocomplete.ts @@ -1,18 +1,18 @@ import * as p from '@clack/prompts'; -import color from 'picocolors'; +import { styleText } from 'node:util'; async function main() { console.clear(); - p.intro(`${color.bgCyan(color.black(' Autocomplete Example '))}`); + p.intro(`${styleText('bgCyan', styleText('black', ' Autocomplete Example '))}`); p.note( ` -${color.cyan('This example demonstrates the type-ahead autocomplete feature:')} -- ${color.yellow('Type')} to filter the list in real-time -- Use ${color.yellow('up/down arrows')} to navigate the filtered results -- Press ${color.yellow('Enter')} to select the highlighted option -- Press ${color.yellow('Ctrl+C')} to cancel +${styleText('cyan', 'This example demonstrates the type-ahead autocomplete feature:')} +- ${styleText('yellow', 'Type')} to filter the list in real-time +- Use ${styleText('yellow', 'up/down arrows')} to navigate the filtered results +- Press ${styleText('yellow', 'Enter')} to select the highlighted option +- Press ${styleText('yellow', 'Ctrl+C')} to cancel `, 'Instructions' ); @@ -53,7 +53,13 @@ ${color.cyan('This example demonstrates the type-ahead autocomplete feature:')} } const selected = countries.find((c) => c.value === result); - p.outro(`You selected: ${color.cyan(selected?.label)} (${color.yellow(selected?.hint)})`); + + if (!selected) { + p.outro('No country selected.'); + process.exit(1); + } + + p.outro(`You selected: ${styleText('cyan', selected?.label)} (${styleText('yellow', selected?.hint)})`); } main().catch(console.error); diff --git a/examples/basic/default-value.ts b/examples/basic/default-value.ts index 6e7f4f3f..df7d715f 100644 --- a/examples/basic/default-value.ts +++ b/examples/basic/default-value.ts @@ -1,5 +1,5 @@ import * as p from '@clack/prompts'; -import color from 'picocolors'; +import { styleText } from 'node:util'; async function main() { const defaultPath = 'my-project'; @@ -24,7 +24,7 @@ async function main() { process.exit(0); } - p.outro(`Let's bootstrap the project in ${color.cyan(result)}`); + p.outro(`Let's bootstrap the project in ${styleText('cyan', result)}`); } main().catch(console.error); diff --git a/examples/basic/index.ts b/examples/basic/index.ts index 0115d4ac..7d4e69fa 100644 --- a/examples/basic/index.ts +++ b/examples/basic/index.ts @@ -1,6 +1,6 @@ import { setTimeout } from 'node:timers/promises'; import * as p from '@clack/prompts'; -import color from 'picocolors'; +import { styleText } from 'node:util'; async function main() { console.clear(); @@ -16,7 +16,7 @@ async function main() { }, }); - p.intro(`${color.bgCyan(color.black(' create-app '))}`); + p.intro(`${styleText('bgCyan', styleText('black', ' create-app '))}`); const project = await p.group( { @@ -87,7 +87,7 @@ async function main() { p.note(nextSteps, 'Next steps.'); - p.outro(`Problems? ${color.underline(color.cyan('https://example.com/issues'))}`); + p.outro(`Problems? ${styleText('underline', styleText('cyan', 'https://example.com/issues'))}`); } main().catch(console.error); diff --git a/examples/basic/package.json b/examples/basic/package.json index f8e617e7..2ad7031f 100644 --- a/examples/basic/package.json +++ b/examples/basic/package.json @@ -5,10 +5,14 @@ "type": "module", "dependencies": { "@clack/prompts": "workspace:*", - "picocolors": "^1.0.0", "jiti": "^1.17.0" }, "scripts": { + "autocomplete": "jiti ./autocomplete.ts", + "autocomplete-multiselect": "jiti ./autocomplete-multiselect.ts", + "default-value": "jiti ./default-value.ts", + "spinner-cancel": "jiti ./spinner-cancel.ts", + "spinner-cancel-advanced": "jiti ./spinner-cancel-advanced.ts", "start": "jiti ./index.ts", "stream": "jiti ./stream.ts", "progress": "jiti ./progress.ts", @@ -16,7 +20,8 @@ "path": "jiti ./path.ts", "spinner-ci": "npx cross-env CI=\"true\" jiti ./spinner-ci.ts", "spinner-timer": "jiti ./spinner-timer.ts", - "task-log": "jiti ./task-log.ts" + "task-log": "jiti ./task-log.ts", + "text-validation": "jiti ./text-validation.ts" }, "devDependencies": { "cross-env": "^7.0.3" diff --git a/examples/basic/spinner-ci.ts b/examples/basic/spinner-ci.ts index 36b30bea..08ae45b3 100644 --- a/examples/basic/spinner-ci.ts +++ b/examples/basic/spinner-ci.ts @@ -14,7 +14,7 @@ import * as p from '@clack/prompts'; const s = p.spinner(); let progress = 0; let counter = 0; -let loop: NodeJS.Timer; +let loop: NodeJS.Timeout; p.intro('Running spinner in CI environment'); s.start('spinner.start'); diff --git a/examples/basic/stream.ts b/examples/basic/stream.ts index c87ad5bf..e3209fb4 100644 --- a/examples/basic/stream.ts +++ b/examples/basic/stream.ts @@ -1,13 +1,13 @@ import { setTimeout } from 'node:timers/promises'; import * as p from '@clack/prompts'; -import color from 'picocolors'; +import { styleText } from 'node:util'; async function main() { console.clear(); await setTimeout(1000); - p.intro(`${color.bgCyan(color.black(' create-app '))}`); + p.intro(`${styleText('bgCyan', styleText('black', ' create-app '))}`); await p.stream.step( (async function* () { @@ -25,7 +25,7 @@ async function main() { })() ); - p.outro(`Problems? ${color.underline(color.cyan('https://example.com/issues'))}`); + p.outro(`Problems? ${styleText('underline', styleText('cyan', 'https://example.com/issues'))}`); } const lorem = [ diff --git a/examples/basic/text-validation.ts b/examples/basic/text-validation.ts index 9a637c68..e12f9004 100644 --- a/examples/basic/text-validation.ts +++ b/examples/basic/text-validation.ts @@ -9,6 +9,7 @@ async function main() { message: 'Enter your name (letters and spaces only)', initialValue: 'John123', // Invalid initial value with numbers validate: (value) => { + if (!value) return 'Name is required'; if (!/^[a-zA-Z\s]+$/.test(value)) return 'Name can only contain letters and spaces'; return undefined; }, @@ -25,6 +26,7 @@ async function main() { message: 'Enter another name (letters and spaces only)', initialValue: 'John Doe', // Valid initial value validate: (value) => { + if (!value) return 'Name is required'; if (!/^[a-zA-Z\s]+$/.test(value)) return 'Name can only contain letters and spaces'; return undefined; }, diff --git a/examples/changesets/index.ts b/examples/changesets/index.ts index 044afddc..08ba46c6 100644 --- a/examples/changesets/index.ts +++ b/examples/changesets/index.ts @@ -1,6 +1,6 @@ import { setTimeout } from 'node:timers/promises'; import * as p from '@clack/prompts'; -import color from 'picocolors'; +import { styleText } from 'node:util'; function onCancel() { p.cancel('Operation cancelled.'); @@ -12,7 +12,7 @@ async function main() { await setTimeout(1000); - p.intro(`${color.bgCyan(color.black(' changesets '))}`); + p.intro(`${styleText('bgCyan', styleText('black', ' changesets '))}`); const changeset = await p.group( { @@ -35,7 +35,7 @@ async function main() { major: ({ results }) => { const packages = results.packages ?? []; return p.multiselect({ - message: `Which packages should have a ${color.red('major')} bump?`, + message: `Which packages should have a ${styleText('red', 'major')} bump?`, options: packages.map((value) => ({ value })), required: false, }); @@ -46,7 +46,7 @@ async function main() { const possiblePackages = packages.filter((pkg) => !major.includes(pkg)); if (possiblePackages.length === 0) return; return p.multiselect({ - message: `Which packages should have a ${color.yellow('minor')} bump?`, + message: `Which packages should have a ${styleText('yellow', 'minor')} bump?`, options: possiblePackages.map((value) => ({ value })), required: false, }); @@ -59,9 +59,9 @@ async function main() { (pkg) => !major.includes(pkg) && !minor.includes(pkg) ); if (possiblePackages.length === 0) return; - const note = possiblePackages.join(color.dim(', ')); + const note = possiblePackages.join(styleText('dim', ', ')); - p.log.step(`These packages will have a ${color.green('patch')} bump.\n${color.dim(note)}`); + p.log.step(`These packages will have a ${styleText('green', 'patch')} bump.\n${styleText('dim', note)}`); return possiblePackages; }, }, @@ -79,7 +79,7 @@ async function main() { return onCancel(); } - p.outro(`Changeset added! ${color.underline(color.cyan('.changeset/orange-crabs-sing.md'))}`); + p.outro(`Changeset added! ${styleText('underline', styleText('cyan', '.changeset/orange-crabs-sing.md'))}`); } main().catch(console.error); diff --git a/examples/changesets/package.json b/examples/changesets/package.json index df4a061d..54c1f990 100644 --- a/examples/changesets/package.json +++ b/examples/changesets/package.json @@ -5,8 +5,7 @@ "type": "module", "dependencies": { "jiti": "^1.17.0", - "@clack/prompts": "workspace:*", - "picocolors": "^1.0.0" + "@clack/prompts": "workspace:*" }, "scripts": { "start": "jiti ./index.ts" diff --git a/package.json b/package.json index 39950050..f0f20e16 100644 --- a/package.json +++ b/package.json @@ -17,13 +17,13 @@ "devDependencies": { "@biomejs/biome": "1.9.4", "@changesets/cli": "^2.26.2", - "@types/node": "^18.16.0", + "@types/node": "^20.12.0", + "jsr": "^0.13.4", "knip": "^5.50.4", "typescript": "^5.8.3", - "unbuild": "^2.0.0", - "jsr": "^0.13.4" + "unbuild": "^2.0.0" }, - "packageManager": "pnpm@9.14.2", + "packageManager": "pnpm@10.12.1", "volta": { "node": "20.18.1" } diff --git a/packages/core/package.json b/packages/core/package.json index f0c72b43..4a3e1ead 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -50,7 +50,6 @@ "test": "vitest run" }, "dependencies": { - "picocolors": "^1.0.0", "sisteransi": "^1.0.5" }, "devDependencies": { diff --git a/packages/core/src/prompts/autocomplete.ts b/packages/core/src/prompts/autocomplete.ts index 05bc8cf9..7473872f 100644 --- a/packages/core/src/prompts/autocomplete.ts +++ b/packages/core/src/prompts/autocomplete.ts @@ -1,5 +1,5 @@ import type { Key } from 'node:readline'; -import color from 'picocolors'; +import { styleText } from 'node:util'; import Prompt, { type PromptOptions } from './prompt.js'; interface OptionLike { @@ -71,14 +71,14 @@ export default class AutocompletePrompt extends Prompt< get userInputWithCursor() { if (!this.userInput) { - return color.inverse(color.hidden('_')); + return styleText(['inverse', 'hidden'], '_'); } if (this._cursor >= this.userInput.length) { return `${this.userInput}█`; } const s1 = this.userInput.slice(0, this._cursor); const [s2, ...s3] = this.userInput.slice(this._cursor); - return `${s1}${color.inverse(s2)}${s3.join('')}`; + return `${s1}${styleText(['inverse'], s2)}${s3.join('')}`; } get options(): T[] { diff --git a/packages/core/src/prompts/password.ts b/packages/core/src/prompts/password.ts index c27cde8d..a9469e09 100644 --- a/packages/core/src/prompts/password.ts +++ b/packages/core/src/prompts/password.ts @@ -1,4 +1,4 @@ -import color from 'picocolors'; +import { styleText } from 'node:util'; import Prompt, { type PromptOptions } from './prompt.js'; interface PasswordOptions extends PromptOptions { @@ -18,12 +18,12 @@ export default class PasswordPrompt extends Prompt { } const userInput = this.userInput; if (this.cursor >= userInput.length) { - return `${this.masked}${color.inverse(color.hidden('_'))}`; + return `${this.masked}${styleText(['inverse', 'hidden'], '_')}`; } const masked = this.masked; const s1 = masked.slice(0, this.cursor); const s2 = masked.slice(this.cursor); - return `${s1}${color.inverse(s2[0])}${s2.slice(1)}`; + return `${s1}${styleText(['inverse'], s2[0])}${s2.slice(1)}`; } constructor({ mask, ...opts }: PasswordOptions) { super(opts); diff --git a/packages/core/src/prompts/text.ts b/packages/core/src/prompts/text.ts index c76a5188..9f61b3be 100644 --- a/packages/core/src/prompts/text.ts +++ b/packages/core/src/prompts/text.ts @@ -1,4 +1,4 @@ -import color from 'picocolors'; +import { styleText } from 'node:util'; import Prompt, { type PromptOptions } from './prompt.js'; interface TextOptions extends PromptOptions { @@ -17,7 +17,7 @@ export default class TextPrompt extends Prompt { } const s1 = userInput.slice(0, this.cursor); const [s2, ...s3] = userInput.slice(this.cursor); - return `${s1}${color.inverse(s2)}${s3.join('')}`; + return `${s1}${styleText(['inverse'], s2)}${s3.join('')}`; } get cursor() { return this._cursor; diff --git a/packages/core/test/prompts/confirm.test.ts b/packages/core/test/prompts/confirm.test.ts index a89a4093..8ad14c56 100644 --- a/packages/core/test/prompts/confirm.test.ts +++ b/packages/core/test/prompts/confirm.test.ts @@ -1,4 +1,3 @@ -import color from 'picocolors'; import { cursor } from 'sisteransi'; import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest'; import { default as ConfirmPrompt } from '../../src/prompts/confirm.js'; diff --git a/packages/core/test/prompts/password.test.ts b/packages/core/test/prompts/password.test.ts index dc145fc7..3ae9f5ad 100644 --- a/packages/core/test/prompts/password.test.ts +++ b/packages/core/test/prompts/password.test.ts @@ -1,4 +1,4 @@ -import color from 'picocolors'; +import { styleText } from 'node:util'; import { cursor } from 'sisteransi'; import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest'; import { default as PasswordPrompt } from '../../src/prompts/password.js'; @@ -65,7 +65,7 @@ describe('PasswordPrompt', () => { }); instance.prompt(); input.emit('keypress', 'x', { name: 'x' }); - expect(instance.userInputWithCursor).to.equal(`•${color.inverse(color.hidden('_'))}`); + expect(instance.userInputWithCursor).to.equal(`•${styleText('inverse', styleText('hidden', '_'))}`); }); test('renders cursor inside value', () => { @@ -80,7 +80,7 @@ describe('PasswordPrompt', () => { input.emit('keypress', 'z', { name: 'z' }); input.emit('keypress', 'left', { name: 'left' }); input.emit('keypress', 'left', { name: 'left' }); - expect(instance.userInputWithCursor).to.equal(`•${color.inverse('•')}•`); + expect(instance.userInputWithCursor).to.equal(`•${styleText('inverse', '•')}•`); }); test('renders custom mask', () => { @@ -92,7 +92,7 @@ describe('PasswordPrompt', () => { }); instance.prompt(); input.emit('keypress', 'x', { name: 'x' }); - expect(instance.userInputWithCursor).to.equal(`X${color.inverse(color.hidden('_'))}`); + expect(instance.userInputWithCursor).to.equal(`X${styleText('inverse', styleText('hidden', '_'))}`); }); }); }); diff --git a/packages/core/test/prompts/select.test.ts b/packages/core/test/prompts/select.test.ts index c95dc45b..996b4f56 100644 --- a/packages/core/test/prompts/select.test.ts +++ b/packages/core/test/prompts/select.test.ts @@ -1,4 +1,3 @@ -import color from 'picocolors'; import { cursor } from 'sisteransi'; import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest'; import { default as SelectPrompt } from '../../src/prompts/select.js'; diff --git a/packages/core/test/prompts/text.test.ts b/packages/core/test/prompts/text.test.ts index 9ae20333..8dd5574a 100644 --- a/packages/core/test/prompts/text.test.ts +++ b/packages/core/test/prompts/text.test.ts @@ -1,4 +1,4 @@ -import color from 'picocolors'; +import { styleText } from "node:util" import { cursor } from 'sisteransi'; import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest'; import { default as TextPrompt } from '../../src/prompts/text.js'; @@ -93,7 +93,7 @@ describe('TextPrompt', () => { input.emit('keypress', keys[i], { name: keys[i] }); } input.emit('keypress', 'left', { name: 'left' }); - expect(instance.userInputWithCursor).to.equal(`fo${color.inverse('o')}`); + expect(instance.userInputWithCursor).to.equal(`fo${styleText('inverse', 'o')}`); }); test('shows cursor at end if beyond value', () => { diff --git a/packages/prompts/package.json b/packages/prompts/package.json index 3f634a02..12d5786e 100644 --- a/packages/prompts/package.json +++ b/packages/prompts/package.json @@ -51,7 +51,6 @@ }, "dependencies": { "@clack/core": "workspace:*", - "picocolors": "^1.0.0", "sisteransi": "^1.0.5" }, "devDependencies": { diff --git a/packages/prompts/src/autocomplete.ts b/packages/prompts/src/autocomplete.ts index 71173c0f..7baacc25 100644 --- a/packages/prompts/src/autocomplete.ts +++ b/packages/prompts/src/autocomplete.ts @@ -1,5 +1,5 @@ +import { styleText } from 'node:util'; import { AutocompletePrompt } from '@clack/core'; -import color from 'picocolors'; import { type CommonOptions, S_BAR, @@ -89,7 +89,7 @@ export const autocomplete = (opts: AutocompleteOptions) => { validate: opts.validate, render() { // Title and message display - const title = `${color.gray(S_BAR)}\n${symbol(this.state)} ${opts.message}\n`; + const title = `${styleText('gray', S_BAR)}\n${symbol(this.state)} ${opts.message}`; const userInput = this.userInput; const valueAsString = String(this.value ?? ''); const options = this.options; @@ -102,25 +102,31 @@ export const autocomplete = (opts: AutocompleteOptions) => { // Show selected value const selected = getSelectedOptions(this.selectedValues, options); const label = selected.length > 0 ? selected.map(getLabel).join(', ') : ''; - return `${title}${color.gray(S_BAR)} ${color.dim(label)}`; + return `${title}${styleText('gray', S_BAR)} ${styleText('dim', label)}`; } case 'cancel': { - return `${title}${color.gray(S_BAR)} ${color.strikethrough(color.dim(userInput))}`; + return `${title}${styleText('gray', S_BAR)} ${styleText( + 'strikethrough', + styleText('dim', userInput) + )}`; } default: { // Display cursor position - show plain text in navigation mode const searchText = this.isNavigating || showPlaceholder - ? color.dim(showPlaceholder ? placeholder : userInput) + ? styleText('dim', showPlaceholder ? placeholder : userInput) : this.userInputWithCursor; // Show match count if filtered const matches = this.filteredOptions.length !== options.length - ? color.dim( - ` (${this.filteredOptions.length} match${this.filteredOptions.length === 1 ? '' : 'es'})` + ? styleText( + 'dim', + ` (${this.filteredOptions.length} match${ + this.filteredOptions.length === 1 ? '' : 'es' + })` ) : ''; @@ -135,12 +141,12 @@ export const autocomplete = (opts: AutocompleteOptions) => { const label = getLabel(option); const hint = option.hint && option.value === this.focusedValue - ? color.dim(` (${option.hint})`) + ? styleText('dim', ` (${option.hint})`) : ''; return active - ? `${color.green(S_RADIO_ACTIVE)} ${label}${hint}` - : `${color.dim(S_RADIO_INACTIVE)} ${color.dim(label)}${hint}`; + ? `${styleText('green', S_RADIO_ACTIVE)} ${label}${hint}` + : `${styleText('dim', S_RADIO_INACTIVE)} ${styleText('dim', label)}${hint}`; }, maxItems: opts.maxItems, output: opts.output, @@ -148,29 +154,32 @@ export const autocomplete = (opts: AutocompleteOptions) => { // Show instructions const instructions = [ - `${color.dim('↑/↓')} to select`, - `${color.dim('Enter:')} confirm`, - `${color.dim('Type:')} to search`, + `${styleText('dim', '↑/↓')} to select`, + `${styleText('dim', 'Enter:')} confirm`, + `${styleText('dim', 'Type:')} to search`, ]; // No matches message const noResults = this.filteredOptions.length === 0 && userInput - ? [`${color.cyan(S_BAR)} ${color.yellow('No matches found')}`] + ? [`${styleText('cyan', S_BAR)} ${styleText('yellow', 'No matches found')}`] : []; const validationError = - this.state === 'error' ? [`${color.yellow(S_BAR)} ${color.yellow(this.error)}`] : []; + this.state === 'error' + ? [`${styleText('yellow', S_BAR)} ${styleText('yellow', this.error)}`] + : []; // Return the formatted prompt return [ title, - `${color.cyan(S_BAR)} ${color.dim('Search:')} ${searchText}${matches}`, + '', + `${styleText('cyan', S_BAR)} ${styleText('dim', 'Search:')} ${searchText}${matches}`, ...noResults, ...validationError, - ...displayOptions.map((option) => `${color.cyan(S_BAR)} ${option}`), - `${color.cyan(S_BAR)} ${color.dim(instructions.join(' • '))}`, - `${color.cyan(S_BAR_END)}`, + ...displayOptions.map((option) => `${styleText('cyan', S_BAR)} ${option}`), + `${styleText('cyan', S_BAR)} ${styleText('dim', instructions.join(' • '))}`, + `${styleText('cyan', S_BAR_END)}`, ].join('\n'); } } @@ -207,14 +216,16 @@ export const autocompleteMultiselect = (opts: AutocompleteMultiSelectOpti const label = option.label ?? String(option.value ?? ''); const hint = option.hint && focusedValue !== undefined && option.value === focusedValue - ? color.dim(` (${option.hint})`) + ? styleText('dim', ` (${option.hint})`) : ''; - const checkbox = isSelected ? color.green(S_CHECKBOX_SELECTED) : color.dim(S_CHECKBOX_INACTIVE); + const checkbox = isSelected + ? styleText('green', S_CHECKBOX_SELECTED) + : styleText('dim', S_CHECKBOX_INACTIVE); if (active) { return `${checkbox} ${label}${hint}`; } - return `${checkbox} ${color.dim(label)}`; + return `${checkbox} ${styleText('dim', label)}${hint}`; }; // Create text prompt which we'll use as foundation @@ -236,7 +247,7 @@ export const autocompleteMultiselect = (opts: AutocompleteMultiSelectOpti output: opts.output, render() { // Title and symbol - const title = `${color.gray(S_BAR)}\n${symbol(this.state)} ${opts.message}\n`; + const title = `${styleText('gray', S_BAR)}\n${symbol(this.state)} ${opts.message}\n`; // Selection counter const userInput = this.userInput; @@ -246,43 +257,54 @@ export const autocompleteMultiselect = (opts: AutocompleteMultiSelectOpti // Search input display const searchText = this.isNavigating || showPlaceholder - ? color.dim(showPlaceholder ? placeholder : userInput) // Just show plain text when in navigation mode + ? styleText('dim', showPlaceholder ? placeholder : userInput) // Just show plain text when in navigation mode : this.userInputWithCursor; const options = this.options; const matches = this.filteredOptions.length !== options.length - ? color.dim( - ` (${this.filteredOptions.length} match${this.filteredOptions.length === 1 ? '' : 'es'})` - ) + ? styleText( + 'dim', + ` (${this.filteredOptions.length} match${ + this.filteredOptions.length === 1 ? '' : 'es' + })` + ) : ''; // Render prompt state switch (this.state) { case 'submit': { - return `${title}${color.gray(S_BAR)} ${color.dim(`${this.selectedValues.length} items selected`)}`; + return `${title}${styleText('gray', S_BAR)} ${styleText( + 'dim', + `${this.selectedValues.length} items selected` + )}`; } case 'cancel': { - return `${title}${color.gray(S_BAR)} ${color.strikethrough(color.dim(userInput))}`; + return `${title}${styleText('gray', S_BAR)} ${styleText( + 'strikethrough', + styleText('dim', userInput) + )}`; } default: { // Instructions const instructions = [ - `${color.dim('↑/↓')} to navigate`, - `${color.dim('Space:')} select`, - `${color.dim('Enter:')} confirm`, - `${color.dim('Type:')} to search`, + `${styleText('dim', '↑/↓')} to navigate`, + `${styleText('dim', 'Space:')} select`, + `${styleText('dim', 'Enter:')} confirm`, + `${styleText('dim', 'Type:')} to search`, ]; // No results message const noResults = this.filteredOptions.length === 0 && userInput - ? [`${color.cyan(S_BAR)} ${color.yellow('No matches found')}`] + ? [`${styleText('cyan', S_BAR)} ${styleText('yellow', 'No matches found')}`] : []; const errorMessage = - this.state === 'error' ? [`${color.cyan(S_BAR)} ${color.yellow(this.error)}`] : []; + this.state === 'error' + ? [`${styleText('cyan', S_BAR)} ${styleText('yellow', this.error)}`] + : []; // Get limited options for display const displayOptions = limitOptions({ @@ -297,12 +319,13 @@ export const autocompleteMultiselect = (opts: AutocompleteMultiSelectOpti // Build the prompt display return [ title, - `${color.cyan(S_BAR)} ${color.dim('Search:')} ${searchText}${matches}`, + '', + `${styleText('cyan', S_BAR)} ${styleText('dim', 'Search:')} ${searchText}${matches}`, ...noResults, ...errorMessage, - ...displayOptions.map((option) => `${color.cyan(S_BAR)} ${option}`), - `${color.cyan(S_BAR)} ${color.dim(instructions.join(' • '))}`, - `${color.cyan(S_BAR_END)}`, + ...displayOptions.map((option) => `${styleText('cyan', S_BAR)} ${option}`), + `${styleText('cyan', S_BAR)} ${styleText('dim', instructions.join(' • '))}`, + `${styleText('cyan', S_BAR_END)}`, ].join('\n'); } } diff --git a/packages/prompts/src/common.ts b/packages/prompts/src/common.ts index ffae42d9..505d63fc 100644 --- a/packages/prompts/src/common.ts +++ b/packages/prompts/src/common.ts @@ -1,7 +1,7 @@ import type { Readable, Writable } from 'node:stream'; +import { styleText } from 'node:util'; import type { State } from '@clack/core'; import isUnicodeSupported from 'is-unicode-supported'; -import color from 'picocolors'; export const unicode = isUnicodeSupported(); export const isCI = (): boolean => process.env.CI === 'true'; @@ -36,13 +36,13 @@ export const symbol = (state: State) => { switch (state) { case 'initial': case 'active': - return color.cyan(S_STEP_ACTIVE); + return styleText('cyan', S_STEP_ACTIVE); case 'cancel': - return color.red(S_STEP_CANCEL); + return styleText('red', S_STEP_CANCEL); case 'error': - return color.yellow(S_STEP_ERROR); + return styleText('yellow', S_STEP_ERROR); case 'submit': - return color.green(S_STEP_SUBMIT); + return styleText('green', S_STEP_SUBMIT); } }; diff --git a/packages/prompts/src/confirm.ts b/packages/prompts/src/confirm.ts index 4ee30acc..7d6d5423 100644 --- a/packages/prompts/src/confirm.ts +++ b/packages/prompts/src/confirm.ts @@ -1,5 +1,5 @@ +import { styleText } from 'node:util'; import { ConfirmPrompt } from '@clack/core'; -import color from 'picocolors'; import { type CommonOptions, S_BAR, @@ -26,26 +26,24 @@ export const confirm = (opts: ConfirmOptions) => { output: opts.output, initialValue: opts.initialValue ?? true, render() { - const title = `${color.gray(S_BAR)}\n${symbol(this.state)} ${opts.message}\n`; + const title = `${styleText('gray', S_BAR)}\n${symbol(this.state)} ${opts.message}\n`; const value = this.value ? active : inactive; switch (this.state) { case 'submit': - return `${title}${color.gray(S_BAR)} ${color.dim(value)}`; + return `${title}${styleText('gray', S_BAR)} ${styleText('dim', value)}`; case 'cancel': - return `${title}${color.gray(S_BAR)} ${color.strikethrough( - color.dim(value) - )}\n${color.gray(S_BAR)}`; + return `${title}${styleText('gray', S_BAR)} ${styleText('strikethrough', styleText('dim', value))}\n${styleText('gray', S_BAR)}`; default: { - return `${title}${color.cyan(S_BAR)} ${ + return `${title}${styleText('cyan', S_BAR)} ${ this.value - ? `${color.green(S_RADIO_ACTIVE)} ${active}` - : `${color.dim(S_RADIO_INACTIVE)} ${color.dim(active)}` - } ${color.dim('/')} ${ + ? `${styleText('green', S_RADIO_ACTIVE)} ${active}` + : `${styleText('dim', S_RADIO_INACTIVE)} ${styleText('dim', active)}` + } ${styleText('dim', '/')} ${ !this.value - ? `${color.green(S_RADIO_ACTIVE)} ${inactive}` - : `${color.dim(S_RADIO_INACTIVE)} ${color.dim(inactive)}` - }\n${color.cyan(S_BAR_END)}\n`; + ? `${styleText('green', S_RADIO_ACTIVE)} ${inactive}` + : `${styleText('dim', S_RADIO_INACTIVE)} ${styleText('dim', inactive)}` + }\n${styleText('cyan', S_BAR_END)}\n`; } } }, diff --git a/packages/prompts/src/group-multi-select.ts b/packages/prompts/src/group-multi-select.ts index 8962c6df..d064b4e9 100644 --- a/packages/prompts/src/group-multi-select.ts +++ b/packages/prompts/src/group-multi-select.ts @@ -1,5 +1,5 @@ +import { styleText } from 'node:util'; import { GroupMultiSelectPrompt } from '@clack/core'; -import color from 'picocolors'; import { type CommonOptions, S_BAR, @@ -41,38 +41,41 @@ export const groupMultiselect = (opts: GroupMultiSelectOptions) => const isLast = isItem && (next as any).group === true; const prefix = isItem ? (selectableGroups ? `${isLast ? S_BAR_END : S_BAR} ` : ' ') : ''; const spacingPrefix = - groupSpacing > 0 && !isItem ? `\n${color.cyan(S_BAR)} `.repeat(groupSpacing) : ''; + // groupSpacing > 0 && !isItem ? `\n${color.cyan(S_BAR)} `.repeat(groupSpacing) : ''; + groupSpacing > 0 && !isItem ? `\n${styleText('cyan', S_BAR)} `.repeat(groupSpacing) : ''; if (state === 'active') { - return `${spacingPrefix}${color.dim(prefix)}${color.cyan(S_CHECKBOX_ACTIVE)} ${label} ${ - option.hint ? color.dim(`(${option.hint})`) : '' + return `${spacingPrefix}${styleText('dim', prefix)}${styleText('cyan', S_CHECKBOX_ACTIVE)} ${label} ${ + option.hint ? styleText('dim', `(${option.hint})`) : '' }`; } if (state === 'group-active') { - return `${spacingPrefix}${prefix}${color.cyan(S_CHECKBOX_ACTIVE)} ${color.dim(label)}`; + return `${spacingPrefix}${styleText('dim', prefix)}${styleText('cyan', S_CHECKBOX_ACTIVE)} ${styleText('dim', label)}`; } if (state === 'group-active-selected') { - return `${spacingPrefix}${prefix}${color.green(S_CHECKBOX_SELECTED)} ${color.dim(label)}`; + return `${spacingPrefix}${styleText('dim', prefix)}${styleText('green', S_CHECKBOX_SELECTED)} ${styleText('dim', label)}`; } if (state === 'selected') { - const selectedCheckbox = isItem || selectableGroups ? color.green(S_CHECKBOX_SELECTED) : ''; - return `${spacingPrefix}${color.dim(prefix)}${selectedCheckbox} ${color.dim(label)} ${ - option.hint ? color.dim(`(${option.hint})`) : '' + const selectedCheckbox = + isItem || selectableGroups ? styleText('green', S_CHECKBOX_SELECTED) : ''; + return `${spacingPrefix}${styleText('dim', prefix)}${selectedCheckbox} ${styleText('dim', label)} ${ + option.hint ? styleText('dim', `(${option.hint})`) : '' }`; } if (state === 'cancelled') { - return `${color.strikethrough(color.dim(label))}`; + return `${styleText('strikethrough', styleText('dim', label))}`; } if (state === 'active-selected') { - return `${spacingPrefix}${color.dim(prefix)}${color.green(S_CHECKBOX_SELECTED)} ${label} ${ - option.hint ? color.dim(`(${option.hint})`) : '' + return `${spacingPrefix}${styleText('dim', prefix)}${styleText('green', S_CHECKBOX_SELECTED)} ${label} ${ + option.hint ? styleText('dim', `(${option.hint})`) : '' }`; } if (state === 'submitted') { - return `${color.dim(label)}`; + return `${styleText('dim', label)}`; } - const unselectedCheckbox = isItem || selectableGroups ? color.dim(S_CHECKBOX_INACTIVE) : ''; - return `${spacingPrefix}${color.dim(prefix)}${unselectedCheckbox} ${color.dim(label)}`; + const unselectedCheckbox = + isItem || selectableGroups ? styleText('dim', S_CHECKBOX_INACTIVE) : ''; + return `${spacingPrefix}${styleText('dim', prefix)}${unselectedCheckbox} ${styleText('dim', label)}`; }; const required = opts.required ?? true; @@ -87,42 +90,45 @@ export const groupMultiselect = (opts: GroupMultiSelectOptions) => selectableGroups, validate(selected: Value[] | undefined) { if (required && (selected === undefined || selected.length === 0)) - return `Please select at least one option.\n${color.reset( - color.dim( - `Press ${color.gray(color.bgWhite(color.inverse(' space ')))} to select, ${color.gray( - color.bgWhite(color.inverse(' enter ')) + return `Please select at least one option.\n${styleText( + 'reset', + styleText( + 'dim', + `Press ${styleText('gray', styleText('bgWhite', styleText('inverse', ' space ')))} to select, ${styleText( + 'gray', + styleText('bgWhite', styleText('inverse', ' enter ')) )} to submit` ) )}`; }, render() { - const title = `${color.gray(S_BAR)}\n${symbol(this.state)} ${opts.message}\n`; + const title = `${styleText('gray', S_BAR)}\n${symbol(this.state)} ${opts.message}\n`; const value = this.value ?? []; switch (this.state) { case 'submit': { - return `${title}${color.gray(S_BAR)} ${this.options + return `${title}${styleText('gray', S_BAR)} ${this.options .filter(({ value: optionValue }) => value.includes(optionValue)) .map((option) => opt(option, 'submitted')) - .join(color.dim(', '))}`; + .join(styleText('dim', ', '))}`; } case 'cancel': { const label = this.options .filter(({ value: optionValue }) => value.includes(optionValue)) .map((option) => opt(option, 'cancelled')) - .join(color.dim(', ')); - return `${title}${color.gray(S_BAR)} ${ - label.trim() ? `${label}\n${color.gray(S_BAR)}` : '' + .join(styleText('dim', ', ')); + return `${title}${styleText('gray', S_BAR)} ${ + label.trim() ? `${label}\n${styleText('gray', S_BAR)}` : '' }`; } case 'error': { const footer = this.error .split('\n') .map((ln, i) => - i === 0 ? `${color.yellow(S_BAR_END)} ${color.yellow(ln)}` : ` ${ln}` + i === 0 ? `${styleText('yellow', S_BAR_END)} ${styleText('yellow', ln)}` : ` ${ln}` ) .join('\n'); - return `${title}${color.yellow(S_BAR)} ${this.options + return `${title}${styleText('yellow', S_BAR)} ${this.options .map((option, i, options) => { const selected = value.includes(option.value) || @@ -143,10 +149,10 @@ export const groupMultiselect = (opts: GroupMultiSelectOptions) => } return opt(option, active ? 'active' : 'inactive', options); }) - .join(`\n${color.yellow(S_BAR)} `)}\n${footer}\n`; + .join(`\n${styleText('yellow', S_BAR)} `)}\n${footer}\n`; } default: { - return `${title}${color.cyan(S_BAR)} ${this.options + return `${title}${styleText('cyan', S_BAR)} ${this.options .map((option, i, options) => { const selected = value.includes(option.value) || @@ -167,7 +173,7 @@ export const groupMultiselect = (opts: GroupMultiSelectOptions) => } return opt(option, active ? 'active' : 'inactive', options); }) - .join(`\n${color.cyan(S_BAR)} `)}\n${color.cyan(S_BAR_END)}\n`; + .join(`\n${styleText('cyan', S_BAR)} `)}\n${styleText('cyan', S_BAR_END)}\n`; } } }, diff --git a/packages/prompts/src/limit-options.ts b/packages/prompts/src/limit-options.ts index f1c317a1..79acaf2e 100644 --- a/packages/prompts/src/limit-options.ts +++ b/packages/prompts/src/limit-options.ts @@ -1,6 +1,6 @@ import type { Writable } from 'node:stream'; import { WriteStream } from 'node:tty'; -import color from 'picocolors'; +import { styleText } from 'node:util'; import type { CommonOptions } from './common.js'; export interface LimitOptionsParams extends CommonOptions { @@ -14,7 +14,7 @@ export const limitOptions = (params: LimitOptionsParams): stri const { cursor, options, style } = params; const output: Writable = params.output ?? process.stdout; const rows = output instanceof WriteStream && output.rows !== undefined ? output.rows : 10; - const overflowFormat = color.dim('...'); + const overflowFormat = styleText('dim', '...'); const paramMaxItems = params.maxItems ?? Number.POSITIVE_INFINITY; const outputMaxItems = Math.max(rows - 4, 0); diff --git a/packages/prompts/src/log.ts b/packages/prompts/src/log.ts index ff4e00cd..431fc0a8 100644 --- a/packages/prompts/src/log.ts +++ b/packages/prompts/src/log.ts @@ -1,4 +1,4 @@ -import color from 'picocolors'; +import { styleText } from 'node:util'; import { type CommonOptions, S_BAR, @@ -19,8 +19,8 @@ export const log = { message: ( message: string | string[] = [], { - symbol = color.gray(S_BAR), - secondarySymbol = color.gray(S_BAR), + symbol = styleText('gray', S_BAR), + secondarySymbol = styleText('gray', S_BAR), output = process.stdout, spacing = 1, }: LogMessageOptions = {} @@ -48,22 +48,22 @@ export const log = { output.write(`${parts.join('\n')}\n`); }, info: (message: string, opts?: LogMessageOptions) => { - log.message(message, { ...opts, symbol: color.blue(S_INFO) }); + log.message(message, { ...opts, symbol: styleText('blue', S_INFO) }); }, success: (message: string, opts?: LogMessageOptions) => { - log.message(message, { ...opts, symbol: color.green(S_SUCCESS) }); + log.message(message, { ...opts, symbol: styleText('green', S_SUCCESS) }); }, step: (message: string, opts?: LogMessageOptions) => { - log.message(message, { ...opts, symbol: color.green(S_STEP_SUBMIT) }); + log.message(message, { ...opts, symbol: styleText('green', S_STEP_SUBMIT) }); }, warn: (message: string, opts?: LogMessageOptions) => { - log.message(message, { ...opts, symbol: color.yellow(S_WARN) }); + log.message(message, { ...opts, symbol: styleText('yellow', S_WARN) }); }, /** alias for `log.warn()`. */ warning: (message: string, opts?: LogMessageOptions) => { log.warn(message, opts); }, error: (message: string, opts?: LogMessageOptions) => { - log.message(message, { ...opts, symbol: color.red(S_ERROR) }); + log.message(message, { ...opts, symbol: styleText('red', S_ERROR) }); }, }; diff --git a/packages/prompts/src/messages.ts b/packages/prompts/src/messages.ts index db96d46e..aff5f422 100644 --- a/packages/prompts/src/messages.ts +++ b/packages/prompts/src/messages.ts @@ -1,18 +1,18 @@ import type { Writable } from 'node:stream'; -import color from 'picocolors'; +import { styleText } from 'node:util'; import { type CommonOptions, S_BAR, S_BAR_END, S_BAR_START } from './common.js'; export const cancel = (message = '', opts?: CommonOptions) => { const output: Writable = opts?.output ?? process.stdout; - output.write(`${color.gray(S_BAR_END)} ${color.red(message)}\n\n`); + output.write(`${styleText('gray', S_BAR_END)} ${styleText('red', message)}\n\n`); }; export const intro = (title = '', opts?: CommonOptions) => { const output: Writable = opts?.output ?? process.stdout; - output.write(`${color.gray(S_BAR_START)} ${title}\n`); + output.write(`${styleText('gray', S_BAR_START)} ${title}\n`); }; export const outro = (message = '', opts?: CommonOptions) => { const output: Writable = opts?.output ?? process.stdout; - output.write(`${color.gray(S_BAR)}\n${color.gray(S_BAR_END)} ${message}\n\n`); + output.write(`${styleText('gray', S_BAR)}\n${styleText('gray', S_BAR_END)} ${message}\n\n`); }; diff --git a/packages/prompts/src/multi-select.ts b/packages/prompts/src/multi-select.ts index c717958d..3f57e7fc 100644 --- a/packages/prompts/src/multi-select.ts +++ b/packages/prompts/src/multi-select.ts @@ -1,5 +1,5 @@ +import { styleText } from 'node:util'; import { MultiSelectPrompt } from '@clack/core'; -import color from 'picocolors'; import { type CommonOptions, S_BAR, @@ -27,27 +27,27 @@ export const multiselect = (opts: MultiSelectOptions) => { ) => { const label = option.label ?? String(option.value); if (state === 'active') { - return `${color.cyan(S_CHECKBOX_ACTIVE)} ${label} ${ - option.hint ? color.dim(`(${option.hint})`) : '' + return `${styleText('cyan', S_CHECKBOX_ACTIVE)} ${label} ${ + option.hint ? styleText('dim', `(${option.hint})`) : '' }`; } if (state === 'selected') { - return `${color.green(S_CHECKBOX_SELECTED)} ${color.dim(label)} ${ - option.hint ? color.dim(`(${option.hint})`) : '' + return `${styleText('green', S_CHECKBOX_SELECTED)} ${styleText('dim', label)} ${ + option.hint ? styleText('dim', `(${option.hint})`) : '' }`; } if (state === 'cancelled') { - return `${color.strikethrough(color.dim(label))}`; + return `${styleText('strikethrough', styleText('dim', label))}`; } if (state === 'active-selected') { - return `${color.green(S_CHECKBOX_SELECTED)} ${label} ${ - option.hint ? color.dim(`(${option.hint})`) : '' + return `${styleText('green', S_CHECKBOX_SELECTED)} ${label} ${ + option.hint ? styleText('dim', `(${option.hint})`) : '' }`; } if (state === 'submitted') { - return `${color.dim(label)}`; + return `${styleText('dim', label)}`; } - return `${color.dim(S_CHECKBOX_INACTIVE)} ${color.dim(label)}`; + return `${styleText('dim', S_CHECKBOX_INACTIVE)} ${styleText('dim', label)}`; }; const required = opts.required ?? true; @@ -61,16 +61,19 @@ export const multiselect = (opts: MultiSelectOptions) => { cursorAt: opts.cursorAt, validate(selected: Value[] | undefined) { if (required && (selected === undefined || selected.length === 0)) - return `Please select at least one option.\n${color.reset( - color.dim( - `Press ${color.gray(color.bgWhite(color.inverse(' space ')))} to select, ${color.gray( - color.bgWhite(color.inverse(' enter ')) + return `Please select at least one option.\n${styleText( + 'reset', + styleText( + 'dim', + `Press ${styleText('gray', styleText('bgWhite', styleText('inverse', ' space ')))} to select, ${styleText( + 'gray', + styleText('bgWhite', styleText('inverse', ' enter ')) )} to submit` ) )}`; }, render() { - const title = `${color.gray(S_BAR)}\n${symbol(this.state)} ${opts.message}\n`; + const title = `${styleText('gray', S_BAR)}\n${symbol(this.state)} ${opts.message}\n`; const value = this.value ?? []; const styleOption = (option: Option, active: boolean) => { @@ -86,45 +89,45 @@ export const multiselect = (opts: MultiSelectOptions) => { switch (this.state) { case 'submit': { - return `${title}${color.gray(S_BAR)} ${ + return `${title}${styleText('gray', S_BAR)} ${ this.options .filter(({ value: optionValue }) => value.includes(optionValue)) .map((option) => opt(option, 'submitted')) - .join(color.dim(', ')) || color.dim('none') + .join(styleText('dim', ', ')) || styleText('dim', 'none') }`; } case 'cancel': { const label = this.options .filter(({ value: optionValue }) => value.includes(optionValue)) .map((option) => opt(option, 'cancelled')) - .join(color.dim(', ')); - return `${title}${color.gray(S_BAR)} ${ - label.trim() ? `${label}\n${color.gray(S_BAR)}` : '' + .join(styleText('dim', ', ')); + return `${title}${styleText('gray', S_BAR)} ${ + label.trim() ? `${label}\n${styleText('gray', S_BAR)}` : '' }`; } case 'error': { const footer = this.error .split('\n') .map((ln, i) => - i === 0 ? `${color.yellow(S_BAR_END)} ${color.yellow(ln)}` : ` ${ln}` + i === 0 ? `${styleText('yellow', S_BAR_END)} ${styleText('yellow', ln)}` : ` ${ln}` ) .join('\n'); - return `${title + color.yellow(S_BAR)} ${limitOptions({ + return `${title + styleText('yellow', S_BAR)} ${limitOptions({ output: opts.output, options: this.options, cursor: this.cursor, maxItems: opts.maxItems, style: styleOption, - }).join(`\n${color.yellow(S_BAR)} `)}\n${footer}\n`; + }).join(`\n${styleText('yellow', S_BAR)} `)}\n${footer}\n`; } default: { - return `${title}${color.cyan(S_BAR)} ${limitOptions({ + return `${title}${styleText('cyan', S_BAR)} ${limitOptions({ output: opts.output, options: this.options, cursor: this.cursor, maxItems: opts.maxItems, style: styleOption, - }).join(`\n${color.cyan(S_BAR)} `)}\n${color.cyan(S_BAR_END)}\n`; + }).join(`\n${styleText('cyan', S_BAR)} `)}\n${styleText('cyan', S_BAR_END)}\n`; } } }, diff --git a/packages/prompts/src/note.ts b/packages/prompts/src/note.ts index 0d6ff83a..b1f5c96f 100644 --- a/packages/prompts/src/note.ts +++ b/packages/prompts/src/note.ts @@ -1,6 +1,6 @@ import type { Writable } from 'node:stream'; import { stripVTControlCharacters as strip } from 'node:util'; -import color from 'picocolors'; +import { styleText } from 'node:util'; import { type CommonOptions, S_BAR, @@ -15,7 +15,7 @@ export interface NoteOptions extends CommonOptions { format?: (line: string) => string; } -const defaultNoteFormatter = (line: string): string => color.dim(line); +const defaultNoteFormatter = (line: string): string => styleText('dim', line); export const note = (message = '', title = '', opts?: NoteOptions) => { const format = opts?.format ?? defaultNoteFormatter; @@ -32,12 +32,14 @@ export const note = (message = '', title = '', opts?: NoteOptions) => { ) + 2; const msg = lines .map( - (ln) => `${color.gray(S_BAR)} ${ln}${' '.repeat(len - strip(ln).length)}${color.gray(S_BAR)}` + (ln) => + `${styleText('gray', S_BAR)} ${ln || ''}${' '.repeat(len - strip(ln || '').length)}${styleText('gray', S_BAR)}` ) .join('\n'); output.write( - `${color.gray(S_BAR)}\n${color.green(S_STEP_SUBMIT)} ${color.reset(title)} ${color.gray( + `${styleText('gray', S_BAR)}\n${styleText('green', S_STEP_SUBMIT)} ${styleText('reset', title || '')} ${styleText( + 'gray', S_BAR_H.repeat(Math.max(len - titleLen - 1, 1)) + S_CORNER_TOP_RIGHT - )}\n${msg}\n${color.gray(S_CONNECT_LEFT + S_BAR_H.repeat(len + 2) + S_CORNER_BOTTOM_RIGHT)}\n` + )}\n${msg}\n${styleText('gray', S_CONNECT_LEFT + S_BAR_H.repeat(len + 2) + S_CORNER_BOTTOM_RIGHT)}\n` ); }; diff --git a/packages/prompts/src/password.ts b/packages/prompts/src/password.ts index 58632aa7..09772586 100644 --- a/packages/prompts/src/password.ts +++ b/packages/prompts/src/password.ts @@ -1,5 +1,5 @@ +import { styleText } from 'node:util'; import { PasswordPrompt } from '@clack/core'; -import color from 'picocolors'; import { type CommonOptions, S_BAR, S_BAR_END, S_PASSWORD_MASK, symbol } from './common.js'; export interface PasswordOptions extends CommonOptions { @@ -15,23 +15,25 @@ export const password = (opts: PasswordOptions) => { input: opts.input, output: opts.output, render() { - const title = `${color.gray(S_BAR)}\n${symbol(this.state)} ${opts.message}\n`; + // const title = `${color.gray(S_BAR)}\n${symbol(this.state)} ${opts.message}\n`; + const title = `${styleText('gray', S_BAR)}\n${symbol(this.state)} ${opts.message}\n`; const userInput = this.userInputWithCursor; const masked = this.masked; switch (this.state) { case 'error': - return `${title.trim()}\n${color.yellow(S_BAR)} ${masked}\n${color.yellow( + return `${title.trim()}\n${styleText('yellow', S_BAR)} ${masked}\n${styleText( + 'yellow', S_BAR_END - )} ${color.yellow(this.error)}\n`; + )} ${styleText('yellow', this.error)}\n`; case 'submit': - return `${title}${color.gray(S_BAR)} ${color.dim(masked)}`; + return `${title}${styleText('gray', S_BAR)} ${styleText('dim', masked)}`; case 'cancel': - return `${title}${color.gray(S_BAR)} ${color.strikethrough(color.dim(masked))}${ - masked ? `\n${color.gray(S_BAR)}` : '' + return `${title}${styleText('gray', S_BAR)} ${styleText('strikethrough', styleText('dim', masked))}${ + masked ? `\n${styleText('gray', S_BAR)}` : '' }`; default: - return `${title}${color.cyan(S_BAR)} ${userInput}\n${color.cyan(S_BAR_END)}\n`; + return `${title}${styleText('cyan', S_BAR)} ${userInput}\n${styleText('cyan', S_BAR_END)}\n`; } }, }).prompt() as Promise; diff --git a/packages/prompts/src/progress-bar.ts b/packages/prompts/src/progress-bar.ts index 82aa911a..ee489702 100644 --- a/packages/prompts/src/progress-bar.ts +++ b/packages/prompts/src/progress-bar.ts @@ -1,5 +1,5 @@ +import { styleText } from 'node:util'; import type { State } from '@clack/core'; -import color from 'picocolors'; import { unicodeOr } from './common.js'; import { type SpinnerOptions, type SpinnerResult, spinner } from './spinner.js'; @@ -36,19 +36,19 @@ export function progress({ switch (state) { case 'initial': case 'active': - return color.magenta; + return (text: string) => styleText('magenta', text); case 'error': case 'cancel': - return color.red; + return (text: string) => styleText('red', text); case 'submit': - return color.green; + return (text: string) => styleText('green', text); default: - return color.magenta; + return (text: string) => styleText('magenta', text); } }; const drawProgress = (state: State, msg: string) => { const active = Math.floor((value / max) * size); - return `${activeStyle(state)(S_PROGRESS_CHAR[style].repeat(active))}${color.dim(S_PROGRESS_CHAR[style].repeat(size - active))} ${msg}`; + return `${activeStyle(state)(S_PROGRESS_CHAR[style].repeat(active))}${styleText('dim', S_PROGRESS_CHAR[style].repeat(size - active))} ${msg}`; }; const start = (msg = '') => { diff --git a/packages/prompts/src/select-key.ts b/packages/prompts/src/select-key.ts index f5bbbf69..6d53ddd2 100644 --- a/packages/prompts/src/select-key.ts +++ b/packages/prompts/src/select-key.ts @@ -1,5 +1,5 @@ +import { styleText } from 'node:util'; import { SelectKeyPrompt } from '@clack/core'; -import color from 'picocolors'; import { S_BAR, S_BAR_END, symbol } from './common.js'; import type { Option, SelectOptions } from './select.js'; @@ -10,18 +10,18 @@ export const selectKey = (opts: SelectOptions) => { ) => { const label = option.label ?? String(option.value); if (state === 'selected') { - return `${color.dim(label)}`; + return `${styleText('dim', label)}`; } if (state === 'cancelled') { - return `${color.strikethrough(color.dim(label))}`; + return `${styleText('strikethrough', styleText('dim', label))}`; } if (state === 'active') { - return `${color.bgCyan(color.gray(` ${option.value} `))} ${label} ${ - option.hint ? color.dim(`(${option.hint})`) : '' + return `${styleText('bgCyan', styleText('gray', ` ${option.value} `))} ${label} ${ + option.hint ? styleText('dim', `(${option.hint})`) : '' }`; } - return `${color.gray(color.bgWhite(color.inverse(` ${option.value} `)))} ${label} ${ - option.hint ? color.dim(`(${option.hint})`) : '' + return `${styleText('gray', styleText('bgWhite', styleText('inverse', ` ${option.value} `)))} ${label} ${ + option.hint ? styleText('dim', `(${option.hint})`) : '' }`; }; @@ -32,22 +32,23 @@ export const selectKey = (opts: SelectOptions) => { output: opts.output, initialValue: opts.initialValue, render() { - const title = `${color.gray(S_BAR)}\n${symbol(this.state)} ${opts.message}\n`; + const title = `${styleText('gray', S_BAR)}\n${symbol(this.state)} ${opts.message}\n`; switch (this.state) { case 'submit': - return `${title}${color.gray(S_BAR)} ${opt( + return `${title}${styleText('gray', S_BAR)} ${opt( this.options.find((opt) => opt.value === this.value) ?? opts.options[0], 'selected' )}`; case 'cancel': - return `${title}${color.gray(S_BAR)} ${opt(this.options[0], 'cancelled')}\n${color.gray( + return `${title}${styleText('gray', S_BAR)} ${opt(this.options[0], 'cancelled')}\n${styleText( + 'gray', S_BAR )}`; default: { - return `${title}${color.cyan(S_BAR)} ${this.options + return `${title}${styleText('cyan', S_BAR)} ${this.options .map((option, i) => opt(option, i === this.cursor ? 'active' : 'inactive')) - .join(`\n${color.cyan(S_BAR)} `)}\n${color.cyan(S_BAR_END)}\n`; + .join(`\n${styleText('cyan', S_BAR)} `)}\n${styleText('cyan', S_BAR_END)}\n`; } } }, diff --git a/packages/prompts/src/select.ts b/packages/prompts/src/select.ts index efb7c486..98259d66 100644 --- a/packages/prompts/src/select.ts +++ b/packages/prompts/src/select.ts @@ -1,5 +1,5 @@ +import { styleText } from 'node:util'; import { SelectPrompt } from '@clack/core'; -import color from 'picocolors'; import { type CommonOptions, S_BAR, @@ -62,15 +62,15 @@ export const select = (opts: SelectOptions) => { const label = option.label ?? String(option.value); switch (state) { case 'selected': - return `${color.dim(label)}`; + return `${styleText('dim', label)}`; case 'active': - return `${color.green(S_RADIO_ACTIVE)} ${label} ${ - option.hint ? color.dim(`(${option.hint})`) : '' + return `${styleText('green', S_RADIO_ACTIVE)} ${label} ${ + option.hint ? styleText('dim', `(${option.hint})`) : '' }`; case 'cancelled': - return `${color.strikethrough(color.dim(label))}`; + return `${styleText('strikethrough', styleText('dim', label))}`; default: - return `${color.dim(S_RADIO_INACTIVE)} ${color.dim(label)}`; + return `${styleText('dim', S_RADIO_INACTIVE)} ${styleText('dim', label)}`; } }; @@ -81,24 +81,27 @@ export const select = (opts: SelectOptions) => { output: opts.output, initialValue: opts.initialValue, render() { - const title = `${color.gray(S_BAR)}\n${symbol(this.state)} ${opts.message}\n`; + const title = `${styleText('gray', S_BAR)}\n${symbol(this.state)} ${opts.message}\n`; switch (this.state) { case 'submit': - return `${title}${color.gray(S_BAR)} ${opt(this.options[this.cursor], 'selected')}`; + return `${title}${styleText('gray', S_BAR)} ${opt( + this.options[this.cursor], + 'selected' + )}`; case 'cancel': - return `${title}${color.gray(S_BAR)} ${opt( + return `${title}${styleText('gray', S_BAR)} ${opt( this.options[this.cursor], 'cancelled' - )}\n${color.gray(S_BAR)}`; + )}\n${styleText('gray', S_BAR)}`; default: { - return `${title}${color.cyan(S_BAR)} ${limitOptions({ + return `${title}${styleText('cyan', S_BAR)} ${limitOptions({ output: opts.output, cursor: this.cursor, options: this.options, maxItems: opts.maxItems, style: (item, active) => opt(item, active ? 'active' : 'inactive'), - }).join(`\n${color.cyan(S_BAR)} `)}\n${color.cyan(S_BAR_END)}\n`; + }).join(`\n${styleText('cyan', S_BAR)} `)}\n${styleText('cyan', S_BAR_END)}\n`; } } }, diff --git a/packages/prompts/src/spinner.ts b/packages/prompts/src/spinner.ts index 803b26ce..2ddbe801 100644 --- a/packages/prompts/src/spinner.ts +++ b/packages/prompts/src/spinner.ts @@ -1,5 +1,5 @@ +import { styleText } from 'node:util'; import { block, settings } from '@clack/core'; -import color from 'picocolors'; import { cursor, erase } from 'sisteransi'; import { type CommonOptions, @@ -115,7 +115,8 @@ export const spinner = ({ unblock = block({ output }); _message = removeTrailingDots(msg); _origin = performance.now(); - output.write(`${color.gray(S_BAR)}\n`); + // output.write(`${color.gray(S_BAR)}\n`); + output.write(`${styleText('gray', S_BAR)}\n`); let frameIndex = 0; let indicatorTimer = 0; registerHooks(); @@ -125,7 +126,7 @@ export const spinner = ({ } clearPrevMessage(); _prevMessage = _message; - const frame = color.magenta(frames[frameIndex]); + const frame = styleText('magenta', frames[frameIndex]); if (isCI) { output.write(`${frame} ${_message}...`); @@ -148,10 +149,10 @@ export const spinner = ({ clearPrevMessage(); const step = code === 0 - ? color.green(S_STEP_SUBMIT) + ? styleText('green', S_STEP_SUBMIT) : code === 1 - ? color.red(S_STEP_CANCEL) - : color.red(S_STEP_ERROR); + ? styleText('red', S_STEP_CANCEL) + : styleText('red', S_STEP_ERROR); _message = msg ?? _message; if (indicator === 'timer') { output.write(`${step} ${_message} ${formatTimer(_origin)}\n`); diff --git a/packages/prompts/src/stream.ts b/packages/prompts/src/stream.ts index 19e79c11..b8fbcb79 100644 --- a/packages/prompts/src/stream.ts +++ b/packages/prompts/src/stream.ts @@ -1,9 +1,9 @@ import { stripVTControlCharacters as strip } from 'node:util'; -import color from 'picocolors'; +import { styleText } from 'node:util'; import { S_BAR, S_ERROR, S_INFO, S_STEP_SUBMIT, S_SUCCESS, S_WARN } from './common.js'; import type { LogMessageOptions } from './log.js'; -const prefix = `${color.gray(S_BAR)} `; +const prefix = `${styleText('gray', S_BAR)} `; // TODO (43081j): this currently doesn't support custom `output` writables // because we rely on `columns` existing (i.e. `process.stdout.columns). @@ -13,9 +13,9 @@ const prefix = `${color.gray(S_BAR)} `; export const stream = { message: async ( iterable: Iterable | AsyncIterable, - { symbol = color.gray(S_BAR) }: LogMessageOptions = {} + { symbol = styleText('gray', S_BAR) }: LogMessageOptions = {} ) => { - process.stdout.write(`${color.gray(S_BAR)}\n${symbol} `); + process.stdout.write(`${styleText('gray', S_BAR)}\n${symbol} `); let lineWidth = 3; for await (let chunk of iterable) { chunk = chunk.replace(/\n/g, `\n${prefix}`); @@ -34,22 +34,22 @@ export const stream = { process.stdout.write('\n'); }, info: (iterable: Iterable | AsyncIterable) => { - return stream.message(iterable, { symbol: color.blue(S_INFO) }); + return stream.message(iterable, { symbol: styleText('blue', S_INFO) }); }, success: (iterable: Iterable | AsyncIterable) => { - return stream.message(iterable, { symbol: color.green(S_SUCCESS) }); + return stream.message(iterable, { symbol: styleText('green', S_SUCCESS) }); }, step: (iterable: Iterable | AsyncIterable) => { - return stream.message(iterable, { symbol: color.green(S_STEP_SUBMIT) }); + return stream.message(iterable, { symbol: styleText('green', S_STEP_SUBMIT) }); }, warn: (iterable: Iterable | AsyncIterable) => { - return stream.message(iterable, { symbol: color.yellow(S_WARN) }); + return stream.message(iterable, { symbol: styleText('yellow', S_WARN) }); }, /** alias for `log.warn()`. */ warning: (iterable: Iterable | AsyncIterable) => { return stream.warn(iterable); }, error: (iterable: Iterable | AsyncIterable) => { - return stream.message(iterable, { symbol: color.red(S_ERROR) }); + return stream.message(iterable, { symbol: styleText('red', S_ERROR) }); }, }; diff --git a/packages/prompts/src/task-log.ts b/packages/prompts/src/task-log.ts index 6ea09aa0..e42e7225 100644 --- a/packages/prompts/src/task-log.ts +++ b/packages/prompts/src/task-log.ts @@ -1,6 +1,6 @@ import type { Writable } from 'node:stream'; +import { styleText } from 'node:util'; import { getColumns } from '@clack/core'; -import color from 'picocolors'; import { erase } from 'sisteransi'; import { type CommonOptions, S_BAR, S_STEP_SUBMIT, isCI as isCIFn } from './common.js'; import { log } from './log.js'; @@ -26,14 +26,14 @@ export interface TaskLogCompletionOptions { export const taskLog = (opts: TaskLogOptions) => { const output: Writable = opts.output ?? process.stdout; const columns = getColumns(output); - const secondarySymbol = color.gray(S_BAR); + const secondarySymbol = styleText('gray', S_BAR); const spacing = opts.spacing ?? 1; const barSize = 3; const retainLog = opts.retainLog === true; const isCI = isCIFn(); output.write(`${secondarySymbol}\n`); - output.write(`${color.green(S_STEP_SUBMIT)} ${opts.title}\n`); + output.write(`${styleText('green', S_STEP_SUBMIT)} ${opts.title}\n`); for (let i = 0; i < spacing; i++) { output.write(`${secondarySymbol}\n`); } @@ -56,12 +56,15 @@ export const taskLog = (opts: TaskLogOptions) => { output.write(erase.lines(lines)); }; const printBuffer = (buf: string, messageSpacing?: number): void => { - log.message(buf.split('\n').map(color.dim), { - output, - secondarySymbol, - symbol: secondarySymbol, - spacing: messageSpacing ?? spacing, - }); + log.message( + buf.split('\n').map((line) => styleText('dim', line)), + { + output, + secondarySymbol, + symbol: secondarySymbol, + spacing: messageSpacing ?? spacing, + } + ); }; const renderBuffer = (): void => { if (retainLog === true && fullBuffer.length > 0) { diff --git a/packages/prompts/src/text.ts b/packages/prompts/src/text.ts index 2bfcd8eb..68b2d046 100644 --- a/packages/prompts/src/text.ts +++ b/packages/prompts/src/text.ts @@ -1,5 +1,5 @@ +import { styleText } from 'node:util'; import { TextPrompt } from '@clack/core'; -import color from 'picocolors'; import { type CommonOptions, S_BAR, S_BAR_END, symbol } from './common.js'; export interface TextOptions extends CommonOptions { @@ -20,27 +20,29 @@ export const text = (opts: TextOptions) => { signal: opts.signal, input: opts.input, render() { - const title = `${color.gray(S_BAR)}\n${symbol(this.state)} ${opts.message}\n`; + const title = `${styleText('gray', S_BAR)}\n${symbol(this.state)} ${opts.message}\n`; const placeholder = opts.placeholder - ? color.inverse(opts.placeholder[0]) + color.dim(opts.placeholder.slice(1)) - : color.inverse(color.hidden('_')); + ? styleText('inverse', opts.placeholder[0]) + styleText('dim', opts.placeholder.slice(1)) + : styleText('inverse', styleText('hidden', '_')); const userInput = !this.userInput ? placeholder : this.userInputWithCursor; const value = this.value ?? ''; switch (this.state) { case 'error': - return `${title.trim()}\n${color.yellow(S_BAR)} ${userInput}\n${color.yellow( + return `${title.trim()}\n${styleText('yellow', S_BAR)} ${userInput}\n${styleText( + 'yellow', S_BAR_END - )} ${color.yellow(this.error)}\n`; + )} ${styleText('yellow', this.error)}\n`; case 'submit': { - return `${title}${color.gray(S_BAR)} ${color.dim(value)}`; + return `${title}${styleText('gray', S_BAR)} ${styleText('dim', value)}`; } case 'cancel': - return `${title}${color.gray(S_BAR)} ${color.strikethrough( - color.dim(value) - )}${value.trim() ? `\n${color.gray(S_BAR)}` : ''}`; + return `${title}${styleText('gray', S_BAR)} ${styleText( + 'strikethrough', + styleText('dim', value) + )}${value.trim() ? `\n${styleText('gray', S_BAR)}` : ''}`; default: - return `${title}${color.cyan(S_BAR)} ${userInput}\n${color.cyan(S_BAR_END)}\n`; + return `${title}${styleText('cyan', S_BAR)} ${userInput}\n${styleText('cyan', S_BAR_END)}\n`; } }, }).prompt() as Promise; diff --git a/packages/prompts/test/__snapshots__/autocomplete.test.ts.snap b/packages/prompts/test/__snapshots__/autocomplete.test.ts.snap index b414a4eb..d6ddf9cf 100644 --- a/packages/prompts/test/__snapshots__/autocomplete.test.ts.snap +++ b/packages/prompts/test/__snapshots__/autocomplete.test.ts.snap @@ -12,7 +12,7 @@ exports[`autocomplete > can be aborted by a signal 1`] = ` │ ○ Cherry │ ○ Grape │ ○ Orange -│ ↑/↓ to select • Enter: confirm • Type: to search +│ ↑/↓ to select • Enter: confirm • Type: to search └", " ", @@ -33,13 +33,12 @@ exports[`autocomplete > limits displayed options when maxItems is set 1`] = ` │ ○ Option 3 │ ○ Option 4 │ ... -│ ↑/↓ to select • Enter: confirm • Type: to search +│ ↑/↓ to select • Enter: confirm • Type: to search └", "", "", "", - "◇ Select an option -│ Option 0", + "◇ Select an option│ Option 0", " ", "", @@ -58,13 +57,12 @@ exports[`autocomplete > renders initial UI with message and instructions 1`] = ` │ ○ Cherry │ ○ Grape │ ○ Orange -│ ↑/↓ to select • Enter: confirm • Type: to search +│ ↑/↓ to select • Enter: confirm • Type: to search └", "", "", "", - "◇ Select a fruit -│ Apple", + "◇ Select a fruit│ Apple", " ", "", @@ -83,13 +81,12 @@ exports[`autocomplete > renders placeholder if set 1`] = ` │ ○ Cherry │ ○ Grape │ ○ Orange -│ ↑/↓ to select • Enter: confirm • Type: to search +│ ↑/↓ to select • Enter: confirm • Type: to search └", "", "", "", - "◇ Select a fruit -│ Apple", + "◇ Select a fruit│ Apple", " ", "", @@ -109,7 +106,7 @@ exports[`autocomplete > shows hint when option has hint and is focused 1`] = ` │ ○ Grape │ ○ Orange │ ○ Kiwi -│ ↑/↓ to select • Enter: confirm • Type: to search +│ ↑/↓ to select • Enter: confirm • Type: to search └", "", "", @@ -121,7 +118,7 @@ exports[`autocomplete > shows hint when option has hint and is focused 1`] = ` │ ○ Grape │ ○ Orange │ ○ Kiwi -│ ↑/↓ to select • Enter: confirm • Type: to search +│ ↑/↓ to select • Enter: confirm • Type: to search └", "", "", @@ -131,7 +128,7 @@ exports[`autocomplete > shows hint when option has hint and is focused 1`] = ` │ ○ Grape │ ○ Orange │ ○ Kiwi -│ ↑/↓ to select • Enter: confirm • Type: to search +│ ↑/↓ to select • Enter: confirm • Type: to search └", "", "", @@ -140,7 +137,7 @@ exports[`autocomplete > shows hint when option has hint and is focused 1`] = ` │ ● Grape │ ○ Orange │ ○ Kiwi -│ ↑/↓ to select • Enter: confirm • Type: to search +│ ↑/↓ to select • Enter: confirm • Type: to search └", "", "", @@ -148,20 +145,19 @@ exports[`autocomplete > shows hint when option has hint and is focused 1`] = ` "│ ○ Grape │ ● Orange │ ○ Kiwi -│ ↑/↓ to select • Enter: confirm • Type: to search +│ ↑/↓ to select • Enter: confirm • Type: to search └", "", "", "", "│ ○ Orange │ ● Kiwi (New Zealand) -│ ↑/↓ to select • Enter: confirm • Type: to search +│ ↑/↓ to select • Enter: confirm • Type: to search └", "", "", "", - "◇ Select a fruit -│ Kiwi", + "◇ Select a fruit│ Kiwi", " ", "", @@ -180,20 +176,19 @@ exports[`autocomplete > shows no matches message when search has no results 1`] │ ○ Cherry │ ○ Grape │ ○ Orange -│ ↑/↓ to select • Enter: confirm • Type: to search +│ ↑/↓ to select • Enter: confirm • Type: to search └", "", "", "", "│ Search: z█ (0 matches) │ No matches found -│ ↑/↓ to select • Enter: confirm • Type: to search +│ ↑/↓ to select • Enter: confirm • Type: to search └", "", "", "", - "◇ Select a fruit -│", + "◇ Select a fruit│", " ", "", @@ -212,7 +207,7 @@ exports[`autocomplete > shows selected value in submit state 1`] = ` │ ○ Cherry │ ○ Grape │ ○ Orange -│ ↑/↓ to select • Enter: confirm • Type: to search +│ ↑/↓ to select • Enter: confirm • Type: to search └", "", "", @@ -223,13 +218,12 @@ exports[`autocomplete > shows selected value in submit state 1`] = ` │ ○ Cherry │ ○ Grape │ ○ Orange -│ ↑/↓ to select • Enter: confirm • Type: to search +│ ↑/↓ to select • Enter: confirm • Type: to search └", "", "", "", - "◇ Select a fruit -│ Banana", + "◇ Select a fruit│ Banana", " ", "", @@ -248,13 +242,12 @@ exports[`autocomplete > shows strikethrough in cancel state 1`] = ` │ ○ Cherry │ ○ Grape │ ○ Orange -│ ↑/↓ to select • Enter: confirm • Type: to search +│ ↑/↓ to select • Enter: confirm • Type: to search └", "", "", "", - "■ Select a fruit -│", + "■ Select a fruit│", " ", "", @@ -273,13 +266,12 @@ exports[`autocomplete > supports initialValue 1`] = ` │ ● Cherry │ ○ Grape │ ○ Orange -│ ↑/↓ to select • Enter: confirm • Type: to search +│ ↑/↓ to select • Enter: confirm • Type: to search └", "", "", "", - "◇ Select a fruit -│ Cherry", + "◇ Select a fruit│ Cherry", " ", "", @@ -292,13 +284,14 @@ exports[`autocompleteMultiselect > can be aborted by a signal 1`] = ` "│ ◆ foo + │ Search: _ │ ◻ Apple │ ◻ Banana │ ◻ Cherry │ ◻ Grape │ ◻ Orange -│ ↑/↓ to navigate • Space: select • Enter: confirm • Type: to search +│ ↑/↓ to navigate • Space: select • Enter: confirm • Type: to search └", " ", @@ -312,19 +305,21 @@ exports[`autocompleteMultiselect > renders error when empty selection & required "│ ◆ Select a fruit + │ Search: _ │ ◻ Apple │ ◻ Banana │ ◻ Cherry │ ◻ Grape │ ◻ Orange -│ ↑/↓ to navigate • Space: select • Enter: confirm • Type: to search +│ ↑/↓ to navigate • Space: select • Enter: confirm • Type: to search └", - "", + "", "", "", "▲ Select a fruit + │ Search: _ │ Please select at least one item │ ◻ Apple @@ -332,22 +327,23 @@ exports[`autocompleteMultiselect > renders error when empty selection & required │ ◻ Cherry │ ◻ Grape │ ◻ Orange -│ ↑/↓ to navigate • Space: select • Enter: confirm • Type: to search +│ ↑/↓ to navigate • Space: select • Enter: confirm • Type: to search └", - "", + "", "", "", "◆ Select a fruit + │ Search: _ │ ◼ Apple │ ◻ Banana │ ◻ Cherry │ ◻ Grape │ ◻ Orange -│ ↑/↓ to navigate • Space: select • Enter: confirm • Type: to search +│ ↑/↓ to navigate • Space: select • Enter: confirm • Type: to search └", - "", + "", "", "", "◇ Select a fruit diff --git a/packages/prompts/test/__snapshots__/group-multi-select.test.ts.snap b/packages/prompts/test/__snapshots__/group-multi-select.test.ts.snap index 942a30ce..621c191c 100644 --- a/packages/prompts/test/__snapshots__/group-multi-select.test.ts.snap +++ b/packages/prompts/test/__snapshots__/group-multi-select.test.ts.snap @@ -6,7 +6,7 @@ exports[`groupMultiselect (isCI = false) > can be aborted by a signal 1`] = ` "│ ◆ Select a fruit │ ◻ group1 -│ └ ◻ group1value0 +│ └ ◻ group1value0 │ ◻ group2 │ └ ◻ group2value0 └ @@ -23,8 +23,8 @@ exports[`groupMultiselect (isCI = false) > can deselect an option 1`] = ` "│ ◆ foo │ ◻ group1 -│ │ ◻ group1value0 -│ └ ◻ group1value1 +│ │ ◻ group1value0 +│ └ ◻ group1value1 └ ", "", @@ -80,16 +80,16 @@ exports[`groupMultiselect (isCI = false) > can select a group 1`] = ` "│ ◆ foo │ ◻ group1 -│ │ ◻ group1value0 -│ └ ◻ group1value1 +│ │ ◻ group1value0 +│ └ ◻ group1value1 └ ", "", "", "", "│ ◼ group1 -│ │ ◼ group1value0 -│ └ ◼ group1value1 +│ │ ◼ group1value0 +│ └ ◼ group1value1 └ ", "", @@ -109,8 +109,8 @@ exports[`groupMultiselect (isCI = false) > can select a group by selecting all m "│ ◆ foo │ ◻ group1 -│ │ ◻ group1value0 -│ └ ◻ group1value1 +│ │ ◻ group1value0 +│ └ ◻ group1value1 └ ", "", @@ -158,9 +158,9 @@ exports[`groupMultiselect (isCI = false) > can select multiple options 1`] = ` "│ ◆ foo │ ◻ group1 -│ │ ◻ group1value0 -│ │ ◻ group1value1 -│ └ ◻ group1value2 +│ │ ◻ group1value0 +│ │ ◻ group1value1 +│ └ ◻ group1value2 └ ", "", @@ -207,8 +207,8 @@ exports[`groupMultiselect (isCI = false) > can submit empty selection when requi "│ ◆ foo │ ◻ group1 -│ │ ◻ group1value0 -│ └ ◻ group1value1 +│ │ ◻ group1value0 +│ └ ◻ group1value1 └ ", "", @@ -254,7 +254,7 @@ exports[`groupMultiselect (isCI = false) > groupSpacing > negative spacing is ig "│ ◆ foo │ ◻ group1 -│ └ ◻ group1value0 +│ └ ◻ group1value0 │ ◻ group2 │ └ ◻ group2value0 └ @@ -296,7 +296,7 @@ exports[`groupMultiselect (isCI = false) > groupSpacing > renders spaced groups │ │ │ ◻ group1 -│ └ ◻ group1value0 +│ └ ◻ group1value0 │ │ │ ◻ group2 @@ -342,8 +342,8 @@ exports[`groupMultiselect (isCI = false) > initial values can be set 1`] = ` "│ ◆ foo │ ◻ group1 -│ │ ◻ group1value0 -│ └ ◼ group1value1 +│ │ ◻ group1value0 +│ └ ◼ group1value1 └ ", "", @@ -363,8 +363,8 @@ exports[`groupMultiselect (isCI = false) > renders error when nothing selected 1 "│ ◆ foo │ ◻ group1 -│ │ ◻ group1value0 -│ └ ◻ group1value1 +│ │ ◻ group1value0 +│ └ ◻ group1value1 └ ", "", @@ -372,8 +372,8 @@ exports[`groupMultiselect (isCI = false) > renders error when nothing selected 1 "", "▲ foo │ ◻ group1 -│ │ ◻ group1value0 -│ └ ◻ group1value1 +│ │ ◻ group1value0 +│ └ ◻ group1value1 └ Please select at least one option. Press  space  to select,  enter  to submit ", @@ -408,8 +408,8 @@ exports[`groupMultiselect (isCI = false) > renders message with options 1`] = ` "│ ◆ foo │ ◻ group1 -│ │ ◻ group1value0 -│ └ ◻ group1value1 +│ │ ◻ group1value0 +│ └ ◻ group1value1 │ ◻ group2 │ └ ◻ group2value0 └ @@ -510,8 +510,8 @@ exports[`groupMultiselect (isCI = false) > values can be non-primitive 1`] = ` "│ ◆ foo │ ◻ group1 -│ │ ◻ value0 -│ └ ◻ value1 +│ │ ◻ value0 +│ └ ◻ value1 └ ", "", @@ -544,7 +544,7 @@ exports[`groupMultiselect (isCI = true) > can be aborted by a signal 1`] = ` "│ ◆ Select a fruit │ ◻ group1 -│ └ ◻ group1value0 +│ └ ◻ group1value0 │ ◻ group2 │ └ ◻ group2value0 └ @@ -561,8 +561,8 @@ exports[`groupMultiselect (isCI = true) > can deselect an option 1`] = ` "│ ◆ foo │ ◻ group1 -│ │ ◻ group1value0 -│ └ ◻ group1value1 +│ │ ◻ group1value0 +│ └ ◻ group1value1 └ ", "", @@ -618,16 +618,16 @@ exports[`groupMultiselect (isCI = true) > can select a group 1`] = ` "│ ◆ foo │ ◻ group1 -│ │ ◻ group1value0 -│ └ ◻ group1value1 +│ │ ◻ group1value0 +│ └ ◻ group1value1 └ ", "", "", "", "│ ◼ group1 -│ │ ◼ group1value0 -│ └ ◼ group1value1 +│ │ ◼ group1value0 +│ └ ◼ group1value1 └ ", "", @@ -647,8 +647,8 @@ exports[`groupMultiselect (isCI = true) > can select a group by selecting all me "│ ◆ foo │ ◻ group1 -│ │ ◻ group1value0 -│ └ ◻ group1value1 +│ │ ◻ group1value0 +│ └ ◻ group1value1 └ ", "", @@ -696,9 +696,9 @@ exports[`groupMultiselect (isCI = true) > can select multiple options 1`] = ` "│ ◆ foo │ ◻ group1 -│ │ ◻ group1value0 -│ │ ◻ group1value1 -│ └ ◻ group1value2 +│ │ ◻ group1value0 +│ │ ◻ group1value1 +│ └ ◻ group1value2 └ ", "", @@ -745,8 +745,8 @@ exports[`groupMultiselect (isCI = true) > can submit empty selection when requir "│ ◆ foo │ ◻ group1 -│ │ ◻ group1value0 -│ └ ◻ group1value1 +│ │ ◻ group1value0 +│ └ ◻ group1value1 └ ", "", @@ -792,7 +792,7 @@ exports[`groupMultiselect (isCI = true) > groupSpacing > negative spacing is ign "│ ◆ foo │ ◻ group1 -│ └ ◻ group1value0 +│ └ ◻ group1value0 │ ◻ group2 │ └ ◻ group2value0 └ @@ -834,7 +834,7 @@ exports[`groupMultiselect (isCI = true) > groupSpacing > renders spaced groups 1 │ │ │ ◻ group1 -│ └ ◻ group1value0 +│ └ ◻ group1value0 │ │ │ ◻ group2 @@ -880,8 +880,8 @@ exports[`groupMultiselect (isCI = true) > initial values can be set 1`] = ` "│ ◆ foo │ ◻ group1 -│ │ ◻ group1value0 -│ └ ◼ group1value1 +│ │ ◻ group1value0 +│ └ ◼ group1value1 └ ", "", @@ -901,8 +901,8 @@ exports[`groupMultiselect (isCI = true) > renders error when nothing selected 1` "│ ◆ foo │ ◻ group1 -│ │ ◻ group1value0 -│ └ ◻ group1value1 +│ │ ◻ group1value0 +│ └ ◻ group1value1 └ ", "", @@ -910,8 +910,8 @@ exports[`groupMultiselect (isCI = true) > renders error when nothing selected 1` "", "▲ foo │ ◻ group1 -│ │ ◻ group1value0 -│ └ ◻ group1value1 +│ │ ◻ group1value0 +│ └ ◻ group1value1 └ Please select at least one option. Press  space  to select,  enter  to submit ", @@ -946,8 +946,8 @@ exports[`groupMultiselect (isCI = true) > renders message with options 1`] = ` "│ ◆ foo │ ◻ group1 -│ │ ◻ group1value0 -│ └ ◻ group1value1 +│ │ ◻ group1value0 +│ └ ◻ group1value1 │ ◻ group2 │ └ ◻ group2value0 └ @@ -1048,8 +1048,8 @@ exports[`groupMultiselect (isCI = true) > values can be non-primitive 1`] = ` "│ ◆ foo │ ◻ group1 -│ │ ◻ value0 -│ └ ◻ value1 +│ │ ◻ value0 +│ └ ◻ value1 └ ", "", diff --git a/packages/prompts/test/__snapshots__/path.test.ts.snap b/packages/prompts/test/__snapshots__/path.test.ts.snap index 181952f8..9786b540 100644 --- a/packages/prompts/test/__snapshots__/path.test.ts.snap +++ b/packages/prompts/test/__snapshots__/path.test.ts.snap @@ -9,13 +9,12 @@ exports[`text (isCI = false) > can cancel 1`] = ` │ Search: /tmp/█ │ ● /tmp/bar │ ○ /tmp/root.zip -│ ↑/↓ to select • Enter: confirm • Type: to search +│ ↑/↓ to select • Enter: confirm • Type: to search └", "", "", "", - "■ foo -│ /tmp/", + "■ foo│ /tmp/", " ", "", @@ -31,14 +30,14 @@ exports[`text (isCI = false) > cannot submit unknown value 1`] = ` │ Search: /tmp/█ │ ● /tmp/bar │ ○ /tmp/root.zip -│ ↑/↓ to select • Enter: confirm • Type: to search +│ ↑/↓ to select • Enter: confirm • Type: to search └", "", "", "", "│ Search: /tmp/_█ │ No matches found -│ ↑/↓ to select • Enter: confirm • Type: to search +│ ↑/↓ to select • Enter: confirm • Type: to search └", "", "", @@ -48,7 +47,7 @@ exports[`text (isCI = false) > cannot submit unknown value 1`] = ` │ Search: /tmp/_█ │ No matches found │ Please select a path -│ ↑/↓ to select • Enter: confirm • Type: to search +│ ↑/↓ to select • Enter: confirm • Type: to search └", "", "", @@ -58,20 +57,19 @@ exports[`text (isCI = false) > cannot submit unknown value 1`] = ` │ Search: /tmp/█ │ ● /tmp/bar │ ○ /tmp/root.zip -│ ↑/↓ to select • Enter: confirm • Type: to search +│ ↑/↓ to select • Enter: confirm • Type: to search └", "", "", "", "│ Search: /tmp/b█ │ ● /tmp/bar -│ ↑/↓ to select • Enter: confirm • Type: to search +│ ↑/↓ to select • Enter: confirm • Type: to search └", "", "", "", - "◇ foo -│ /tmp/bar", + "◇ foo│ /tmp/bar", " ", "", @@ -86,13 +84,12 @@ exports[`text (isCI = false) > initialValue sets the value 1`] = ` │ Search: /tmp/bar█ │ ● /tmp/bar -│ ↑/↓ to select • Enter: confirm • Type: to search +│ ↑/↓ to select • Enter: confirm • Type: to search └", "", "", "", - "◇ foo -│ /tmp/bar", + "◇ foo│ /tmp/bar", " ", "", @@ -108,14 +105,14 @@ exports[`text (isCI = false) > renders cancelled value if one set 1`] = ` │ Search: /tmp/█ │ ● /tmp/bar │ ○ /tmp/root.zip -│ ↑/↓ to select • Enter: confirm • Type: to search +│ ↑/↓ to select • Enter: confirm • Type: to search └", "", "", "", "│ Search: /tmp/x█ │ No matches found -│ ↑/↓ to select • Enter: confirm • Type: to search +│ ↑/↓ to select • Enter: confirm • Type: to search └", "", "", @@ -125,8 +122,7 @@ exports[`text (isCI = false) > renders cancelled value if one set 1`] = ` "", "", "", - "■ foo -│ /tmp/xy", + "■ foo│ /tmp/xy", " ", "", @@ -142,13 +138,12 @@ exports[`text (isCI = false) > renders message 1`] = ` │ Search: /tmp/█ │ ● /tmp/bar │ ○ /tmp/root.zip -│ ↑/↓ to select • Enter: confirm • Type: to search +│ ↑/↓ to select • Enter: confirm • Type: to search └", "", "", "", - "◇ foo -│ /tmp/bar", + "◇ foo│ /tmp/bar", " ", "", @@ -164,14 +159,14 @@ exports[`text (isCI = false) > renders submitted value 1`] = ` │ Search: /tmp/█ │ ● /tmp/bar │ ○ /tmp/root.zip -│ ↑/↓ to select • Enter: confirm • Type: to search +│ ↑/↓ to select • Enter: confirm • Type: to search └", "", "", "", "│ Search: /tmp/b█ │ ● /tmp/bar -│ ↑/↓ to select • Enter: confirm • Type: to search +│ ↑/↓ to select • Enter: confirm • Type: to search └", "", "", @@ -181,8 +176,7 @@ exports[`text (isCI = false) > renders submitted value 1`] = ` "", "", "", - "◇ foo -│ /tmp/bar", + "◇ foo│ /tmp/bar", " ", "", @@ -198,14 +192,14 @@ exports[`text (isCI = false) > validation errors render and clear (using Error) │ Search: /tmp/█ │ ● /tmp/bar │ ○ /tmp/root.zip -│ ↑/↓ to select • Enter: confirm • Type: to search +│ ↑/↓ to select • Enter: confirm • Type: to search └", "", "", "", "│ Search: /tmp/r█ │ ● /tmp/root.zip -│ ↑/↓ to select • Enter: confirm • Type: to search +│ ↑/↓ to select • Enter: confirm • Type: to search └", "", "", @@ -215,7 +209,7 @@ exports[`text (isCI = false) > validation errors render and clear (using Error) │ Search: /tmp/r█ │ should be /tmp/bar │ ● /tmp/root.zip -│ ↑/↓ to select • Enter: confirm • Type: to search +│ ↑/↓ to select • Enter: confirm • Type: to search └", "", "", @@ -225,20 +219,19 @@ exports[`text (isCI = false) > validation errors render and clear (using Error) │ Search: /tmp/█ │ ○ /tmp/bar │ ● /tmp/root.zip -│ ↑/↓ to select • Enter: confirm • Type: to search +│ ↑/↓ to select • Enter: confirm • Type: to search └", "", "", "", "│ Search: /tmp/b█ │ ● /tmp/bar -│ ↑/↓ to select • Enter: confirm • Type: to search +│ ↑/↓ to select • Enter: confirm • Type: to search └", "", "", "", - "◇ foo -│ /tmp/bar", + "◇ foo│ /tmp/bar", " ", "", @@ -254,14 +247,14 @@ exports[`text (isCI = false) > validation errors render and clear 1`] = ` │ Search: /tmp/█ │ ● /tmp/bar │ ○ /tmp/root.zip -│ ↑/↓ to select • Enter: confirm • Type: to search +│ ↑/↓ to select • Enter: confirm • Type: to search └", "", "", "", "│ Search: /tmp/r█ │ ● /tmp/root.zip -│ ↑/↓ to select • Enter: confirm • Type: to search +│ ↑/↓ to select • Enter: confirm • Type: to search └", "", "", @@ -271,7 +264,7 @@ exports[`text (isCI = false) > validation errors render and clear 1`] = ` │ Search: /tmp/r█ │ should be /tmp/bar │ ● /tmp/root.zip -│ ↑/↓ to select • Enter: confirm • Type: to search +│ ↑/↓ to select • Enter: confirm • Type: to search └", "", "", @@ -281,20 +274,19 @@ exports[`text (isCI = false) > validation errors render and clear 1`] = ` │ Search: /tmp/█ │ ○ /tmp/bar │ ● /tmp/root.zip -│ ↑/↓ to select • Enter: confirm • Type: to search +│ ↑/↓ to select • Enter: confirm • Type: to search └", "", "", "", "│ Search: /tmp/b█ │ ● /tmp/bar -│ ↑/↓ to select • Enter: confirm • Type: to search +│ ↑/↓ to select • Enter: confirm • Type: to search └", "", "", "", - "◇ foo -│ /tmp/bar", + "◇ foo│ /tmp/bar", " ", "", @@ -310,13 +302,12 @@ exports[`text (isCI = true) > can cancel 1`] = ` │ Search: /tmp/█ │ ● /tmp/bar │ ○ /tmp/root.zip -│ ↑/↓ to select • Enter: confirm • Type: to search +│ ↑/↓ to select • Enter: confirm • Type: to search └", "", "", "", - "■ foo -│ /tmp/", + "■ foo│ /tmp/", " ", "", @@ -332,14 +323,14 @@ exports[`text (isCI = true) > cannot submit unknown value 1`] = ` │ Search: /tmp/█ │ ● /tmp/bar │ ○ /tmp/root.zip -│ ↑/↓ to select • Enter: confirm • Type: to search +│ ↑/↓ to select • Enter: confirm • Type: to search └", "", "", "", "│ Search: /tmp/_█ │ No matches found -│ ↑/↓ to select • Enter: confirm • Type: to search +│ ↑/↓ to select • Enter: confirm • Type: to search └", "", "", @@ -349,7 +340,7 @@ exports[`text (isCI = true) > cannot submit unknown value 1`] = ` │ Search: /tmp/_█ │ No matches found │ Please select a path -│ ↑/↓ to select • Enter: confirm • Type: to search +│ ↑/↓ to select • Enter: confirm • Type: to search └", "", "", @@ -359,20 +350,19 @@ exports[`text (isCI = true) > cannot submit unknown value 1`] = ` │ Search: /tmp/█ │ ● /tmp/bar │ ○ /tmp/root.zip -│ ↑/↓ to select • Enter: confirm • Type: to search +│ ↑/↓ to select • Enter: confirm • Type: to search └", "", "", "", "│ Search: /tmp/b█ │ ● /tmp/bar -│ ↑/↓ to select • Enter: confirm • Type: to search +│ ↑/↓ to select • Enter: confirm • Type: to search └", "", "", "", - "◇ foo -│ /tmp/bar", + "◇ foo│ /tmp/bar", " ", "", @@ -387,13 +377,12 @@ exports[`text (isCI = true) > initialValue sets the value 1`] = ` │ Search: /tmp/bar█ │ ● /tmp/bar -│ ↑/↓ to select • Enter: confirm • Type: to search +│ ↑/↓ to select • Enter: confirm • Type: to search └", "", "", "", - "◇ foo -│ /tmp/bar", + "◇ foo│ /tmp/bar", " ", "", @@ -409,14 +398,14 @@ exports[`text (isCI = true) > renders cancelled value if one set 1`] = ` │ Search: /tmp/█ │ ● /tmp/bar │ ○ /tmp/root.zip -│ ↑/↓ to select • Enter: confirm • Type: to search +│ ↑/↓ to select • Enter: confirm • Type: to search └", "", "", "", "│ Search: /tmp/x█ │ No matches found -│ ↑/↓ to select • Enter: confirm • Type: to search +│ ↑/↓ to select • Enter: confirm • Type: to search └", "", "", @@ -426,8 +415,7 @@ exports[`text (isCI = true) > renders cancelled value if one set 1`] = ` "", "", "", - "■ foo -│ /tmp/xy", + "■ foo│ /tmp/xy", " ", "", @@ -443,13 +431,12 @@ exports[`text (isCI = true) > renders message 1`] = ` │ Search: /tmp/█ │ ● /tmp/bar │ ○ /tmp/root.zip -│ ↑/↓ to select • Enter: confirm • Type: to search +│ ↑/↓ to select • Enter: confirm • Type: to search └", "", "", "", - "◇ foo -│ /tmp/bar", + "◇ foo│ /tmp/bar", " ", "", @@ -465,14 +452,14 @@ exports[`text (isCI = true) > renders submitted value 1`] = ` │ Search: /tmp/█ │ ● /tmp/bar │ ○ /tmp/root.zip -│ ↑/↓ to select • Enter: confirm • Type: to search +│ ↑/↓ to select • Enter: confirm • Type: to search └", "", "", "", "│ Search: /tmp/b█ │ ● /tmp/bar -│ ↑/↓ to select • Enter: confirm • Type: to search +│ ↑/↓ to select • Enter: confirm • Type: to search └", "", "", @@ -482,8 +469,7 @@ exports[`text (isCI = true) > renders submitted value 1`] = ` "", "", "", - "◇ foo -│ /tmp/bar", + "◇ foo│ /tmp/bar", " ", "", @@ -499,14 +485,14 @@ exports[`text (isCI = true) > validation errors render and clear (using Error) 1 │ Search: /tmp/█ │ ● /tmp/bar │ ○ /tmp/root.zip -│ ↑/↓ to select • Enter: confirm • Type: to search +│ ↑/↓ to select • Enter: confirm • Type: to search └", "", "", "", "│ Search: /tmp/r█ │ ● /tmp/root.zip -│ ↑/↓ to select • Enter: confirm • Type: to search +│ ↑/↓ to select • Enter: confirm • Type: to search └", "", "", @@ -516,7 +502,7 @@ exports[`text (isCI = true) > validation errors render and clear (using Error) 1 │ Search: /tmp/r█ │ should be /tmp/bar │ ● /tmp/root.zip -│ ↑/↓ to select • Enter: confirm • Type: to search +│ ↑/↓ to select • Enter: confirm • Type: to search └", "", "", @@ -526,20 +512,19 @@ exports[`text (isCI = true) > validation errors render and clear (using Error) 1 │ Search: /tmp/█ │ ○ /tmp/bar │ ● /tmp/root.zip -│ ↑/↓ to select • Enter: confirm • Type: to search +│ ↑/↓ to select • Enter: confirm • Type: to search └", "", "", "", "│ Search: /tmp/b█ │ ● /tmp/bar -│ ↑/↓ to select • Enter: confirm • Type: to search +│ ↑/↓ to select • Enter: confirm • Type: to search └", "", "", "", - "◇ foo -│ /tmp/bar", + "◇ foo│ /tmp/bar", " ", "", @@ -555,14 +540,14 @@ exports[`text (isCI = true) > validation errors render and clear 1`] = ` │ Search: /tmp/█ │ ● /tmp/bar │ ○ /tmp/root.zip -│ ↑/↓ to select • Enter: confirm • Type: to search +│ ↑/↓ to select • Enter: confirm • Type: to search └", "", "", "", "│ Search: /tmp/r█ │ ● /tmp/root.zip -│ ↑/↓ to select • Enter: confirm • Type: to search +│ ↑/↓ to select • Enter: confirm • Type: to search └", "", "", @@ -572,7 +557,7 @@ exports[`text (isCI = true) > validation errors render and clear 1`] = ` │ Search: /tmp/r█ │ should be /tmp/bar │ ● /tmp/root.zip -│ ↑/↓ to select • Enter: confirm • Type: to search +│ ↑/↓ to select • Enter: confirm • Type: to search └", "", "", @@ -582,20 +567,19 @@ exports[`text (isCI = true) > validation errors render and clear 1`] = ` │ Search: /tmp/█ │ ○ /tmp/bar │ ● /tmp/root.zip -│ ↑/↓ to select • Enter: confirm • Type: to search +│ ↑/↓ to select • Enter: confirm • Type: to search └", "", "", "", "│ Search: /tmp/b█ │ ● /tmp/bar -│ ↑/↓ to select • Enter: confirm • Type: to search +│ ↑/↓ to select • Enter: confirm • Type: to search └", "", "", "", - "◇ foo -│ /tmp/bar", + "◇ foo│ /tmp/bar", " ", "", diff --git a/packages/prompts/test/note.test.ts b/packages/prompts/test/note.test.ts index 91b91ca5..a8aa5593 100644 --- a/packages/prompts/test/note.test.ts +++ b/packages/prompts/test/note.test.ts @@ -1,7 +1,7 @@ -import colors from 'picocolors'; import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, test, vi } from 'vitest'; import * as prompts from '../src/index.js'; import { MockReadable, MockWritable } from './test-utils.js'; +import { styleText } from 'node:util'; describe.each(['true', 'false'])('note (isCI = %s)', (isCI) => { let originalCI: string | undefined; @@ -56,7 +56,7 @@ describe.each(['true', 'false'])('note (isCI = %s)', (isCI) => { test('formatter which adds colors works', () => { prompts.note('line 0\nline 1\nline 2', 'title', { - format: (line) => colors.red(line), + format: (line) => styleText('red', line), input, output, }); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d2ae3cb1..763685e1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -15,14 +15,14 @@ importers: specifier: ^2.26.2 version: 2.26.2 '@types/node': - specifier: ^18.16.0 - version: 18.16.0 + specifier: ^20.12.0 + version: 20.19.0 jsr: specifier: ^0.13.4 version: 0.13.4 knip: specifier: ^5.50.4 - version: 5.50.4(@types/node@18.16.0)(typescript@5.8.3) + version: 5.50.4(@types/node@20.19.0)(typescript@5.8.3) typescript: specifier: ^5.8.3 version: 5.8.3 @@ -38,9 +38,6 @@ importers: jiti: specifier: ^1.17.0 version: 1.17.0 - picocolors: - specifier: ^1.0.0 - version: 1.0.0 devDependencies: cross-env: specifier: ^7.0.3 @@ -54,22 +51,16 @@ importers: jiti: specifier: ^1.17.0 version: 1.17.0 - picocolors: - specifier: ^1.0.0 - version: 1.0.0 packages/core: dependencies: - picocolors: - specifier: ^1.0.0 - version: 1.0.0 sisteransi: specifier: ^1.0.5 version: 1.0.5 devDependencies: vitest: specifier: ^3.1.1 - version: 3.1.1(@types/node@18.16.0) + version: 3.1.1(@types/node@20.19.0) wrap-ansi: specifier: ^8.1.0 version: 8.1.0 @@ -79,9 +70,6 @@ importers: '@clack/core': specifier: workspace:* version: link:../core - picocolors: - specifier: ^1.0.0 - version: 1.0.0 sisteransi: specifier: ^1.0.5 version: 1.0.5 @@ -94,10 +82,10 @@ importers: version: 4.17.1 vitest: specifier: ^3.1.1 - version: 3.1.1(@types/node@18.16.0) + version: 3.1.1(@types/node@20.19.0) vitest-ansi-serializer: specifier: ^0.1.2 - version: 0.1.2(vitest@3.1.1(@types/node@18.16.0)) + version: 0.1.2(vitest@3.1.1(@types/node@20.19.0)) packages: @@ -915,8 +903,8 @@ packages: '@types/node@12.20.55': resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==} - '@types/node@18.16.0': - resolution: {integrity: sha512-BsAaKhB+7X+H4GnSjGhJG9Qi8Tw+inU9nJDwmD5CgOmBLEI6ArdhikpLX7DjbjDRDTbqZzU2LSQNZg8WGPiSZQ==} + '@types/node@20.19.0': + resolution: {integrity: sha512-hfrc+1tud1xcdVTABC2JiomZJEklMcXYNTVtZLAeqTVWD+qL5jkHKT+1lOtqDdGxt+mB53DTtiz673vfjU8D1Q==} '@types/normalize-package-data@2.4.4': resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==} @@ -2016,9 +2004,6 @@ packages: resolution: {integrity: sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==} engines: {node: '>= 14.16'} - picocolors@1.0.0: - resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} - picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} @@ -2608,6 +2593,9 @@ packages: typescript: optional: true + undici-types@6.21.0: + resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} + universalify@0.1.2: resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} engines: {node: '>= 4.0.0'} @@ -3471,7 +3459,9 @@ snapshots: '@types/node@12.20.55': {} - '@types/node@18.16.0': {} + '@types/node@20.19.0': + dependencies: + undici-types: 6.21.0 '@types/normalize-package-data@2.4.4': {} @@ -3486,13 +3476,13 @@ snapshots: chai: 5.2.0 tinyrainbow: 2.0.0 - '@vitest/mocker@3.1.1(vite@5.4.11(@types/node@18.16.0))': + '@vitest/mocker@3.1.1(vite@5.4.11(@types/node@20.19.0))': dependencies: '@vitest/spy': 3.1.1 estree-walker: 3.0.3 magic-string: 0.30.17 optionalDependencies: - vite: 5.4.11(@types/node@18.16.0) + vite: 5.4.11(@types/node@20.19.0) '@vitest/pretty-format@3.1.1': dependencies: @@ -4448,10 +4438,10 @@ snapshots: kleur@4.1.5: {} - knip@5.50.4(@types/node@18.16.0)(typescript@5.8.3): + knip@5.50.4(@types/node@20.19.0)(typescript@5.8.3): dependencies: '@nodelib/fs.walk': 1.2.8 - '@types/node': 18.16.0 + '@types/node': 20.19.0 easy-table: 1.2.0 enhanced-resolve: 5.18.1 fast-glob: 3.3.3 @@ -4679,8 +4669,6 @@ snapshots: pathval@2.0.0: {} - picocolors@1.0.0: {} - picocolors@1.1.1: {} picomatch@2.3.1: {} @@ -5306,6 +5294,8 @@ snapshots: - supports-color - vue-tsc + undici-types@6.21.0: {} + universalify@0.1.2: {} untyped@1.5.1: @@ -5333,13 +5323,13 @@ snapshots: spdx-correct: 3.2.0 spdx-expression-parse: 3.0.1 - vite-node@3.1.1(@types/node@18.16.0): + vite-node@3.1.1(@types/node@20.19.0): dependencies: cac: 6.7.14 debug: 4.4.0 es-module-lexer: 1.6.0 pathe: 2.0.3 - vite: 5.4.11(@types/node@18.16.0) + vite: 5.4.11(@types/node@20.19.0) transitivePeerDependencies: - '@types/node' - less @@ -5351,23 +5341,23 @@ snapshots: - supports-color - terser - vite@5.4.11(@types/node@18.16.0): + vite@5.4.11(@types/node@20.19.0): dependencies: esbuild: 0.21.5 postcss: 8.4.49 rollup: 4.27.3 optionalDependencies: - '@types/node': 18.16.0 + '@types/node': 20.19.0 fsevents: 2.3.3 - vitest-ansi-serializer@0.1.2(vitest@3.1.1(@types/node@18.16.0)): + vitest-ansi-serializer@0.1.2(vitest@3.1.1(@types/node@20.19.0)): dependencies: - vitest: 3.1.1(@types/node@18.16.0) + vitest: 3.1.1(@types/node@20.19.0) - vitest@3.1.1(@types/node@18.16.0): + vitest@3.1.1(@types/node@20.19.0): dependencies: '@vitest/expect': 3.1.1 - '@vitest/mocker': 3.1.1(vite@5.4.11(@types/node@18.16.0)) + '@vitest/mocker': 3.1.1(vite@5.4.11(@types/node@20.19.0)) '@vitest/pretty-format': 3.1.1 '@vitest/runner': 3.1.1 '@vitest/snapshot': 3.1.1 @@ -5383,11 +5373,11 @@ snapshots: tinyexec: 0.3.2 tinypool: 1.0.2 tinyrainbow: 2.0.0 - vite: 5.4.11(@types/node@18.16.0) - vite-node: 3.1.1(@types/node@18.16.0) + vite: 5.4.11(@types/node@20.19.0) + vite-node: 3.1.1(@types/node@20.19.0) why-is-node-running: 2.3.0 optionalDependencies: - '@types/node': 18.16.0 + '@types/node': 20.19.0 transitivePeerDependencies: - less - lightningcss