Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/tall-keys-allow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@clack/prompts": patch
"@clack/core": patch
---

Add a new `withBorder` option to all prompts to disable the default clack border
2 changes: 1 addition & 1 deletion packages/core/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{
"extends": "../../tsconfig.json",
"include": ["src"]
"include": ["src", "test"]
}
3 changes: 1 addition & 2 deletions packages/prompts/src/box.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ export interface BoxOptions extends CommonOptions {
titlePadding?: number;
contentPadding?: number;
rounded?: boolean;
includePrefix?: boolean;
formatBorder?: (text: string) => string;
}

Expand Down Expand Up @@ -68,7 +67,7 @@ export const box = (message = '', title = '', opts?: BoxOptions) => {
const titlePadding = opts?.titlePadding ?? 1;
const contentPadding = opts?.contentPadding ?? 2;
const width = opts?.width === undefined || opts.width === 'auto' ? 1 : Math.min(1, opts.width);
const linePrefix = opts?.includePrefix ? `${S_BAR} ` : '';
const linePrefix = opts?.withBorder === false ? '' : `${S_BAR} `;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this means boxes would render without a left border? i.e. something like

╭───────────────────────╮
  example box           │
  some more text...     │
╰───────────────────────╯

I’m not sure, but this one feels like even when rendering without a left border, you’d still want boxes to render with the four sides intact. At least if this is a generic option set at a global level. (And if a local thing like box('example', null, { withBorder: false }), then I’d expect no border at all, not only the left border to be missing.

Maybe there’s an argument that what we’re describing as a “border” is not really a border in the generic rendering sense. It has a UI function in connecting prompts, guiding the eye from step to step, etc. So something like withGuide could be a more appropriate naming? And then that would make it more obvious that a box would not be impacted (but might have other styling hooks given it genuinely is a bordered element).

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a fair point

This is configuring the "guide" , the box would still have a left border (as part of the box).

withGuide might work 🤔 but yeah that's basically what I'm trying to make configurable here - the left "guide line" 😅

const formatBorder = opts?.formatBorder ?? defaultFormatBorder;
const symbols = (opts?.rounded ? roundedSymbols : squareSymbols).map(formatBorder);
const hSymbol = formatBorder(S_BAR_H);
Expand Down
1 change: 1 addition & 0 deletions packages/prompts/src/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,4 +57,5 @@ export interface CommonOptions {
input?: Readable;
output?: Writable;
signal?: AbortSignal;
withBorder?: boolean;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See above question re: whether this is the correct naming or if it should be withGuide or similar.

}
17 changes: 12 additions & 5 deletions packages/prompts/src/log.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,25 +23,32 @@ export const log = {
secondarySymbol = color.gray(S_BAR),
output = process.stdout,
spacing = 1,
withBorder,
}: LogMessageOptions = {}
) => {
const parts: string[] = [];
const isStandalone = withBorder === false;
const spacingString = isStandalone ? '' : secondarySymbol;
const prefix = isStandalone ? '' : `${symbol} `;
const secondaryPrefix = isStandalone ? '' : `${secondarySymbol} `;

for (let i = 0; i < spacing; i++) {
parts.push(`${secondarySymbol}`);
parts.push(spacingString);
}

const messageParts = Array.isArray(message) ? message : message.split('\n');
if (messageParts.length > 0) {
const [firstLine, ...lines] = messageParts;
if (firstLine.length > 0) {
parts.push(`${symbol} ${firstLine}`);
parts.push(`${prefix}${firstLine}`);
} else {
parts.push(symbol);
parts.push(isStandalone ? '' : symbol);
}
for (const ln of lines) {
if (ln.length > 0) {
parts.push(`${secondarySymbol} ${ln}`);
parts.push(`${secondaryPrefix}${ln}`);
} else {
parts.push(secondarySymbol);
parts.push(isStandalone ? '' : secondarySymbol);
}
}
}
Expand Down
24 changes: 16 additions & 8 deletions packages/prompts/src/text.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ 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 withBorder = opts.withBorder !== false;
const titlePrefix = withBorder ? `${color.gray(S_BAR)}\n${symbol(this.state)} ` : '';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIUC this means no symbols prefixing prompts when disabling the border? It’s personal preference I guess, but I’d still expect some kind of leading marker even when disabling borders/guides. These markers can be helpful indicators of state.

For example, to mirror something a bit like what prompts does, state is indicated via something like

? What is your name?
  Placeholder

And then post-confirm

✔︎ What is your name?
  Chris

These could definitely just be the standard clack symbols by default but the left border/guide seems a distinct UI element from these status indicators to me.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks a little weird without some rework since the symbol shows up where the guide line would usually be, detached from the box

I'll have a think

const title = `${titlePrefix}${opts.message}\n`;
const placeholder = opts.placeholder
? color.inverse(opts.placeholder[0]) + color.dim(opts.placeholder.slice(1))
: color.inverse(color.hidden('_'));
Expand All @@ -30,20 +32,26 @@ export const text = (opts: TextOptions) => {
switch (this.state) {
case 'error': {
const errorText = this.error ? ` ${color.yellow(this.error)}` : '';
return `${title.trim()}\n${color.yellow(S_BAR)} ${userInput}\n${color.yellow(
S_BAR_END
)}${errorText}\n`;
const errorPrefix = withBorder ? `${color.yellow(S_BAR)} ` : '';
const errorPrefixEnd = withBorder ? color.yellow(S_BAR_END) : '';
return `${title.trim()}\n${errorPrefix}${userInput}\n${errorPrefixEnd}${errorText}\n`;
}
case 'submit': {
const valueText = value ? ` ${color.dim(value)}` : '';
return `${title}${color.gray(S_BAR)}${valueText}`;
const submitPrefix = withBorder ? color.gray(S_BAR) : '';
return `${title}${submitPrefix}${valueText}`;
}
case 'cancel': {
const valueText = value ? ` ${color.strikethrough(color.dim(value))}` : '';
return `${title}${color.gray(S_BAR)}${valueText}${value.trim() ? `\n${color.gray(S_BAR)}` : ''}`;
const cancelPrefix = withBorder ? color.gray(S_BAR) : '';
return `${title}${cancelPrefix}${valueText}${value.trim() ? `\n${cancelPrefix}` : ''}`;
}
default: {
const defaultPrefix = withBorder ? `${color.cyan(S_BAR)} ` : '';
const defaultPrefixEnd = withBorder ? color.cyan(S_BAR_END) : '';
color.cyan(S_BAR_END);
return `${title}${defaultPrefix}${userInput}\n${defaultPrefixEnd}\n`;
}
default:
return `${title}${color.cyan(S_BAR)} ${userInput}\n${color.cyan(S_BAR_END)}\n`;
}
},
}).prompt() as Promise<string | symbol>;
Expand Down
Loading
Loading