From 99c35302f11ceaf6fd89f6d577447e07b3dd9dbd Mon Sep 17 00:00:00 2001 From: James Garbutt <43081j@users.noreply.github.com> Date: Sat, 12 Apr 2025 16:05:50 +0100 Subject: [PATCH] feat: add `format` option to `note` prompt (#284) --- .changeset/empty-buses-wonder.md | 5 + .../src/__snapshots__/index.test.ts.snap | 106 ++++++++++++++++++ packages/prompts/src/index.test.ts | 41 +++++++ packages/prompts/src/index.ts | 16 ++- 4 files changed, 162 insertions(+), 6 deletions(-) create mode 100644 .changeset/empty-buses-wonder.md diff --git a/.changeset/empty-buses-wonder.md b/.changeset/empty-buses-wonder.md new file mode 100644 index 0000000..4d05353 --- /dev/null +++ b/.changeset/empty-buses-wonder.md @@ -0,0 +1,5 @@ +--- +"@clack/prompts": minor +--- + +Adds `format` option to the note prompt to allow formatting of individual lines diff --git a/packages/prompts/src/__snapshots__/index.test.ts.snap b/packages/prompts/src/__snapshots__/index.test.ts.snap index 1850dfb..3399371 100644 --- a/packages/prompts/src/__snapshots__/index.test.ts.snap +++ b/packages/prompts/src/__snapshots__/index.test.ts.snap @@ -1265,6 +1265,59 @@ exports[`prompts (isCI = false) > multiselect > sliding window loops upwards 1`] ] `; +exports[`prompts (isCI = false) > note > formatter which adds colors works 1`] = ` +[ + "│ +◇ title ──╮ +│ │ +│ line 0 │ +│ line 1 │ +│ line 2 │ +│ │ +├──────────╯ +", +] +`; + +exports[`prompts (isCI = false) > note > formatter which adds length works 1`] = ` +[ + "│ +◇ title ──────╮ +│ │ +│ * line 0 * │ +│ * line 1 * │ +│ * line 2 * │ +│ │ +├──────────────╯ +", +] +`; + +exports[`prompts (isCI = false) > note > renders as wide as longest line 1`] = ` +[ + "│ +◇ title ───────────────────────────╮ +│ │ +│ short │ +│ somewhat questionably long line │ +│ │ +├───────────────────────────────────╯ +", +] +`; + +exports[`prompts (isCI = false) > note > renders message with title 1`] = ` +[ + "│ +◇ title ───╮ +│ │ +│ message │ +│ │ +├───────────╯ +", +] +`; + exports[`prompts (isCI = false) > password > renders and clears validation errors 1`] = ` [ "[?25l", @@ -3127,6 +3180,59 @@ exports[`prompts (isCI = true) > multiselect > sliding window loops upwards 1`] ] `; +exports[`prompts (isCI = true) > note > formatter which adds colors works 1`] = ` +[ + "│ +◇ title ──╮ +│ │ +│ line 0 │ +│ line 1 │ +│ line 2 │ +│ │ +├──────────╯ +", +] +`; + +exports[`prompts (isCI = true) > note > formatter which adds length works 1`] = ` +[ + "│ +◇ title ──────╮ +│ │ +│ * line 0 * │ +│ * line 1 * │ +│ * line 2 * │ +│ │ +├──────────────╯ +", +] +`; + +exports[`prompts (isCI = true) > note > renders as wide as longest line 1`] = ` +[ + "│ +◇ title ───────────────────────────╮ +│ │ +│ short │ +│ somewhat questionably long line │ +│ │ +├───────────────────────────────────╯ +", +] +`; + +exports[`prompts (isCI = true) > note > renders message with title 1`] = ` +[ + "│ +◇ title ───╮ +│ │ +│ message │ +│ │ +├───────────╯ +", +] +`; + exports[`prompts (isCI = true) > password > renders and clears validation errors 1`] = ` [ "[?25l", diff --git a/packages/prompts/src/index.test.ts b/packages/prompts/src/index.test.ts index 7d55856..f6b85e4 100644 --- a/packages/prompts/src/index.test.ts +++ b/packages/prompts/src/index.test.ts @@ -1,4 +1,5 @@ import { Readable, Writable } from 'node:stream'; +import colors from 'picocolors'; import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, test, vi } from 'vitest'; import * as prompts from './index.js'; @@ -1249,4 +1250,44 @@ describe.each(['true', 'false'])('prompts (isCI = %s)', (isCI) => { }); }); }); + + describe('note', () => { + test('renders message with title', () => { + prompts.note('message', 'title', { + input, + output, + }); + + expect(output.buffer).toMatchSnapshot(); + }); + + test('renders as wide as longest line', () => { + prompts.note('short\nsomewhat questionably long line', 'title', { + input, + output, + }); + + expect(output.buffer).toMatchSnapshot(); + }); + + test('formatter which adds length works', () => { + prompts.note('line 0\nline 1\nline 2', 'title', { + format: (line) => `* ${line} *`, + input, + output, + }); + + expect(output.buffer).toMatchSnapshot(); + }); + + test('formatter which adds colors works', () => { + prompts.note('line 0\nline 1\nline 2', 'title', { + format: (line) => colors.red(line), + input, + output, + }); + + expect(output.buffer).toMatchSnapshot(); + }); + }); }); diff --git a/packages/prompts/src/index.ts b/packages/prompts/src/index.ts index 1fa1c67..af0471a 100644 --- a/packages/prompts/src/index.ts +++ b/packages/prompts/src/index.ts @@ -642,8 +642,15 @@ export const groupMultiselect = (opts: GroupMultiSelectOptions) => }).prompt() as Promise; }; -export const note = (message = '', title = '', opts?: CommonOptions) => { - const lines = `\n${message}\n`.split('\n'); +export interface NoteOptions extends CommonOptions { + format?: (line: string) => string; +} + +const defaultNoteFormatter = (line: string): string => color.dim(line); + +export const note = (message = '', title = '', opts?: NoteOptions) => { + const format = opts?.format ?? defaultNoteFormatter; + const lines = ['', ...message.split('\n').map(format), '']; const titleLen = strip(title).length; const output: Writable = opts?.output ?? process.stdout; const len = @@ -656,10 +663,7 @@ export const note = (message = '', title = '', opts?: CommonOptions) => { ) + 2; const msg = lines .map( - (ln) => - `${color.gray(S_BAR)} ${color.dim(ln)}${' '.repeat(len - strip(ln).length)}${color.gray( - S_BAR - )}` + (ln) => `${color.gray(S_BAR)} ${ln}${' '.repeat(len - strip(ln).length)}${color.gray(S_BAR)}` ) .join('\n'); output.write( -- 2.51.2