openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
718 lines (717 loc) • 33.3 kB
JavaScript
import { o as truncateToVisibleWidth, r as stripAnsi, s as visibleWidth } from "./ansi-DrXAcdMD.js";
import "./src-vebZIeLe.js";
import { t as expectDefined } from "./expect-CyE8FADM.js";
import { o as normalizeLowercaseStringOrEmpty } from "./string-coerce-CIXf7egm.js";
import { n as isRich, r as theme } from "./theme-vjDs9tao.js";
import { n as stylePromptMessage, r as stylePromptTitle } from "./prompt-style-BQVvtDcR.js";
import { n as noteToStream } from "./note-DqHk3fA1.js";
import { t as createCliProgress } from "./progress-DSeqxbrQ.js";
import { n as WizardCancelledError, r as WizardNavigationError } from "./prompts-DLsO8MlU.js";
import { t as styleSelectParams } from "./prompt-select-styled-params-CvMQXWIw.js";
import { styleText } from "node:util";
import { S_BAR, S_BAR_END, S_CHECKBOX_ACTIVE, S_CHECKBOX_INACTIVE, S_CHECKBOX_SELECTED, S_PASSWORD_MASK, S_RADIO_ACTIVE, S_RADIO_INACTIVE, autocomplete, autocompleteMultiselect, cancel, confirm, intro, isCancel, limitOptions, multiselect, outro, password, select, settings, spinner, symbol, symbolBar, text } from "@clack/prompts";
import { AutocompletePrompt, ConfirmPrompt, MultiSelectPrompt, PasswordPrompt, SelectPrompt, TextPrompt, settings as settings$1, wrapTextWithPrefix } from "@clack/core";
//#region src/wizard/clack-navigation-prompts.ts
function getOptionLabel(option) {
return option.label ?? String(option.value ?? "");
}
function computeLabel(label, format) {
if (!label.includes("\n")) return format(label);
return label.split("\n").map((line) => format(line)).join("\n");
}
function getFilteredOption(searchText, option) {
if (!searchText) return true;
const term = searchText.toLowerCase();
return getOptionLabel(option).toLowerCase().includes(term) || (option.hint ?? "").toLowerCase().includes(term) || String(option.value).toLowerCase().includes(term);
}
function formatNavigationFooter(navigation) {
if (!navigation || !navigation.canGoBack && !navigation.canGoForward) return "";
return [navigation.canGoBack ? styleText("dim", "← back") : void 0, navigation.canGoForward ? styleText("dim", "→ next") : void 0].filter(Boolean).join(" ");
}
function navigationFooterLines(guideVisible, barStyle, navigation, extraHints = []) {
const footer = formatNavigationFooter(navigation);
if (!footer) return [];
const hintLine = [footer, ...extraHints].join(" ");
return [`${guideVisible ? `${styleText(barStyle, S_BAR)} ` : ""}${hintLine}`];
}
function hasGuide(opts) {
return opts.withGuide ?? settings$1.withGuide;
}
function selectOptionRenderer(option, state) {
const label = getOptionLabel(option);
switch (state) {
case "disabled": return `${styleText("gray", S_RADIO_INACTIVE)} ${computeLabel(label, (text) => styleText("gray", text))}${option.hint ? ` ${styleText("dim", `(${option.hint})`)}` : ""}`;
case "selected": return computeLabel(label, (text) => styleText("dim", text));
case "active": return `${styleText("green", S_RADIO_ACTIVE)} ${label}${option.hint ? ` ${styleText("dim", `(${option.hint})`)}` : ""}`;
case "cancelled": return computeLabel(label, (text) => styleText(["strikethrough", "dim"], text));
default: return `${styleText("dim", S_RADIO_INACTIVE)} ${computeLabel(label, (text) => styleText("dim", text))}`;
}
}
function selectWithNavigationFooter(opts) {
return new SelectPrompt({
options: opts.options,
signal: opts.signal,
input: opts.input,
output: opts.output,
initialValue: opts.initialValue,
render() {
const showGuide = hasGuide(opts);
const titlePrefix = `${symbol(this.state)} `;
const titlePrefixBar = `${symbolBar(this.state)} `;
const messageLines = wrapTextWithPrefix(opts.output, opts.message, titlePrefixBar, titlePrefix);
const title = `${showGuide ? `${styleText("gray", S_BAR)}\n` : ""}${messageLines}\n`;
switch (this.state) {
case "submit": {
const submitPrefix = showGuide ? `${styleText("gray", S_BAR)} ` : "";
return `${title}${wrapTextWithPrefix(opts.output, selectOptionRenderer(expectDefined(this.options[this.cursor], "options entry at this.cursor"), "selected"), submitPrefix)}`;
}
case "cancel": {
const cancelPrefix = showGuide ? `${styleText("gray", S_BAR)} ` : "";
return `${title}${wrapTextWithPrefix(opts.output, selectOptionRenderer(expectDefined(this.options[this.cursor], "options entry at this.cursor"), "cancelled"), cancelPrefix)}${showGuide ? `\n${styleText("gray", S_BAR)}` : ""}`;
}
default: {
const prefix = showGuide ? `${styleText("cyan", S_BAR)} ` : "";
const footerLines = [...navigationFooterLines(showGuide, "cyan", opts.navigation, [styleText("dim", "↑/↓ option")]), showGuide ? styleText("cyan", S_BAR_END) : ""];
const titleLineCount = title.split("\n").length;
const footerLineCount = footerLines.length + 1;
return `${title}${prefix}${limitOptions({
output: opts.output,
cursor: this.cursor,
options: this.options,
maxItems: opts.maxItems,
columnPadding: prefix.length,
rowPadding: titleLineCount + footerLineCount,
style: (item, active) => selectOptionRenderer(item, item.disabled ? "disabled" : active ? "active" : "inactive")
}).join(`\n${prefix}`)}\n${footerLines.join("\n")}\n`;
}
}
}
}).prompt();
}
function autocompleteWithNavigationFooter(opts) {
return new AutocompletePrompt({
options: opts.options,
initialValue: opts.initialValue === void 0 ? void 0 : [opts.initialValue],
initialUserInput: opts.initialUserInput,
placeholder: opts.placeholder,
filter: opts.filter ?? getFilteredOption,
signal: opts.signal,
input: opts.input,
output: opts.output,
validate: opts.validate,
render() {
const showGuide = hasGuide(opts);
const headings = showGuide ? [styleText("gray", S_BAR), `${symbol(this.state)} ${opts.message}`] : [`${symbol(this.state)} ${opts.message}`];
const userInput = this.userInput;
const options = this.options;
const showPlaceholder = userInput === "" && opts.placeholder !== void 0;
const opt = (option, state) => {
const label = getOptionLabel(option);
const hint = option.hint && option.value === this.focusedValue ? styleText("dim", ` (${option.hint})`) : "";
switch (state) {
case "active": return `${styleText("green", S_RADIO_ACTIVE)} ${label}${hint}`;
case "inactive": return `${styleText("dim", S_RADIO_INACTIVE)} ${styleText("dim", label)}`;
case "disabled": return `${styleText("gray", S_RADIO_INACTIVE)} ${styleText(["strikethrough", "gray"], label)}`;
}
return "";
};
switch (this.state) {
case "submit": {
const selected = options.filter((option) => this.selectedValues.includes(option.value));
const label = selected.length > 0 ? ` ${styleText("dim", selected.map(getOptionLabel).join(", "))}` : "";
const submitPrefix = showGuide ? styleText("gray", S_BAR) : "";
return `${headings.join("\n")}\n${submitPrefix}${label}`;
}
case "cancel": {
const userInputText = userInput ? ` ${styleText(["strikethrough", "dim"], userInput)}` : "";
const cancelPrefix = showGuide ? styleText("gray", S_BAR) : "";
return `${headings.join("\n")}\n${cancelPrefix}${userInputText}`;
}
default: {
const barStyle = this.state === "error" ? "yellow" : "cyan";
const guidePrefix = showGuide ? `${styleText(barStyle, S_BAR)} ` : "";
const guidePrefixEnd = showGuide ? styleText(barStyle, S_BAR_END) : "";
const searchText = this.isNavigating || showPlaceholder ? opts.placeholder || userInput ? ` ${styleText("dim", showPlaceholder ? opts.placeholder ?? "" : userInput)}` : "" : ` ${this.userInputWithCursor}`;
const matches = this.filteredOptions.length !== options.length ? styleText("dim", ` (${this.filteredOptions.length} match${this.filteredOptions.length === 1 ? "" : "es"})`) : "";
const noResults = this.filteredOptions.length === 0 && userInput ? [`${guidePrefix}${styleText("yellow", "No matches found")}`] : [];
const validationError = this.state === "error" ? [`${guidePrefix}${styleText("yellow", this.error)}`] : [];
if (showGuide) headings.push(guidePrefix.trimEnd());
headings.push(`${guidePrefix}${styleText("dim", "Search:")}${searchText}${matches}`, ...noResults, ...validationError);
const footers = [
`${guidePrefix}${[
`${styleText("dim", "↑/↓")} to select`,
`${styleText("dim", "Enter:")} confirm`,
`${styleText("dim", "Type:")} to search`
].join(" • ")}`,
...navigationFooterLines(showGuide, barStyle, opts.navigation),
guidePrefixEnd
];
const displayOptions = this.filteredOptions.length === 0 ? [] : limitOptions({
cursor: this.cursor,
options: this.filteredOptions,
columnPadding: showGuide ? 3 : 0,
rowPadding: headings.length + footers.length,
style: (option, active) => opt(option, option.disabled ? "disabled" : active ? "active" : "inactive"),
maxItems: opts.maxItems,
output: opts.output
});
return [
...headings,
...displayOptions.map((option) => `${guidePrefix}${option}`),
...footers
].join("\n");
}
}
}
}).prompt();
}
function textWithNavigationFooter(opts) {
return new TextPrompt({
validate: opts.validate,
placeholder: opts.placeholder,
defaultValue: opts.defaultValue,
initialValue: opts.initialValue,
output: opts.output,
signal: opts.signal,
input: opts.input,
render() {
const showGuide = hasGuide(opts);
const title = `${`${showGuide ? `${styleText("gray", S_BAR)}\n` : ""}${symbol(this.state)} `}${opts.message}\n`;
const placeholder = opts.placeholder ? styleText("inverse", opts.placeholder[0] ?? "") + styleText("dim", opts.placeholder.slice(1)) : styleText(["inverse", "hidden"], "_");
const userInput = !this.userInput ? placeholder : this.userInputWithCursor;
const value = this.value ?? "";
switch (this.state) {
case "error": {
const errorText = this.error ? ` ${styleText("yellow", this.error)}` : "";
const errorPrefix = showGuide ? `${styleText("yellow", S_BAR)} ` : "";
const errorPrefixEnd = showGuide ? styleText("yellow", S_BAR_END) : "";
const footerLines = navigationFooterLines(showGuide, "yellow", opts.navigation);
return `${title.trim()}\n${errorPrefix}${userInput}\n${footerLines.length ? `${footerLines.join("\n")}\n` : ""}${errorPrefixEnd}${errorText}\n`;
}
case "submit": {
const valueText = value ? ` ${styleText("dim", value)}` : "";
return `${title}${showGuide ? styleText("gray", S_BAR) : ""}${valueText}`;
}
case "cancel": {
const valueText = value ? ` ${styleText(["strikethrough", "dim"], value)}` : "";
const cancelPrefix = showGuide ? styleText("gray", S_BAR) : "";
return `${title}${cancelPrefix}${valueText}${value.trim() ? `\n${cancelPrefix}` : ""}`;
}
default: {
const defaultPrefix = showGuide ? `${styleText("cyan", S_BAR)} ` : "";
const defaultPrefixEnd = showGuide ? styleText("cyan", S_BAR_END) : "";
const footerLines = navigationFooterLines(showGuide, "cyan", opts.navigation);
return `${title}${defaultPrefix}${userInput}\n${footerLines.length ? `${footerLines.join("\n")}\n` : ""}${defaultPrefixEnd}\n`;
}
}
}
}).prompt();
}
function passwordWithNavigationFooter(opts) {
return new PasswordPrompt({
validate: opts.validate,
mask: opts.mask ?? S_PASSWORD_MASK,
signal: opts.signal,
input: opts.input,
output: opts.output,
render() {
const showGuide = hasGuide(opts);
const title = `${showGuide ? `${styleText("gray", S_BAR)}\n` : ""}${symbol(this.state)} ${opts.message}\n`;
const userInput = this.userInputWithCursor;
const masked = this.masked;
switch (this.state) {
case "error": {
const errorPrefix = showGuide ? `${styleText("yellow", S_BAR)} ` : "";
const errorPrefixEnd = showGuide ? `${styleText("yellow", S_BAR_END)} ` : "";
const maskedText = masked ?? "";
if (opts.clearOnError) this.clear();
const footerLines = navigationFooterLines(showGuide, "yellow", opts.navigation);
return `${title.trim()}\n${errorPrefix}${maskedText}\n${footerLines.length ? `${footerLines.join("\n")}\n` : ""}${errorPrefixEnd}${styleText("yellow", this.error)}\n`;
}
case "submit": return `${title}${showGuide ? `${styleText("gray", S_BAR)} ` : ""}${masked ? styleText("dim", masked) : ""}`;
case "cancel": return `${title}${showGuide ? `${styleText("gray", S_BAR)} ` : ""}${masked ? styleText(["strikethrough", "dim"], masked) : ""}${masked && showGuide ? `\n${styleText("gray", S_BAR)}` : ""}`;
default: {
const defaultPrefix = showGuide ? `${styleText("cyan", S_BAR)} ` : "";
const defaultPrefixEnd = showGuide ? styleText("cyan", S_BAR_END) : "";
const footerLines = navigationFooterLines(showGuide, "cyan", opts.navigation);
return `${title}${defaultPrefix}${userInput}\n${footerLines.length ? `${footerLines.join("\n")}\n` : ""}${defaultPrefixEnd}\n`;
}
}
}
}).prompt();
}
function multiselectOptionRenderer(option, state) {
const label = getOptionLabel(option);
if (state === "disabled") return `${styleText("gray", S_CHECKBOX_INACTIVE)} ${computeLabel(label, (str) => styleText(["strikethrough", "gray"], str))}${option.hint ? ` ${styleText("dim", `(${option.hint})`)}` : ""}`;
if (state === "active") return `${styleText("cyan", S_CHECKBOX_ACTIVE)} ${label}${option.hint ? ` ${styleText("dim", `(${option.hint})`)}` : ""}`;
if (state === "selected") return `${styleText("green", S_CHECKBOX_SELECTED)} ${computeLabel(label, (text) => styleText("dim", text))}${option.hint ? ` ${styleText("dim", `(${option.hint})`)}` : ""}`;
if (state === "cancelled") return computeLabel(label, (text) => styleText(["strikethrough", "dim"], text));
if (state === "active-selected") return `${styleText("green", S_CHECKBOX_SELECTED)} ${label}${option.hint ? ` ${styleText("dim", `(${option.hint})`)}` : ""}`;
if (state === "submitted") return computeLabel(label, (text) => styleText("dim", text));
return `${styleText("dim", S_CHECKBOX_INACTIVE)} ${computeLabel(label, (text) => styleText("dim", text))}`;
}
function multiselectWithNavigationFooter(opts) {
const required = opts.required ?? true;
return new MultiSelectPrompt({
options: opts.options,
signal: opts.signal,
input: opts.input,
output: opts.output,
initialValues: opts.initialValues,
cursorAt: opts.cursorAt,
validate(selected) {
if (required && (selected === void 0 || selected.length === 0)) return `Please select at least one option.\n${styleText("reset", styleText("dim", `Press ${styleText([
"gray",
"bgWhite",
"inverse"
], " space ")} to select, ${styleText("gray", styleText("bgWhite", styleText("inverse", " enter ")))} to submit`))}`;
},
render() {
const showGuide = hasGuide(opts);
const wrappedMessage = wrapTextWithPrefix(opts.output, opts.message, showGuide ? `${symbolBar(this.state)} ` : "", `${symbol(this.state)} `);
const title = `${showGuide ? `${styleText("gray", S_BAR)}\n` : ""}${wrappedMessage}\n`;
const value = this.value ?? [];
const styleOption = (option, active) => {
if (option.disabled) return multiselectOptionRenderer(option, "disabled");
const selected = value.includes(option.value);
if (active && selected) return multiselectOptionRenderer(option, "active-selected");
if (selected) return multiselectOptionRenderer(option, "selected");
return multiselectOptionRenderer(option, active ? "active" : "inactive");
};
switch (this.state) {
case "submit": {
const submitText = this.options.filter(({ value: optionValue }) => value.includes(optionValue)).map((option) => multiselectOptionRenderer(option, "submitted")).join(styleText("dim", ", ")) || styleText("dim", "none");
return `${title}${wrapTextWithPrefix(opts.output, submitText, showGuide ? `${styleText("gray", S_BAR)} ` : "")}`;
}
case "cancel": {
const label = this.options.filter(({ value: optionValue }) => value.includes(optionValue)).map((option) => multiselectOptionRenderer(option, "cancelled")).join(styleText("dim", ", "));
if (label.trim() === "") return `${title}${styleText("gray", S_BAR)}`;
return `${title}${wrapTextWithPrefix(opts.output, label, showGuide ? `${styleText("gray", S_BAR)} ` : "")}${showGuide ? `\n${styleText("gray", S_BAR)}` : ""}`;
}
case "error": {
const prefix = showGuide ? `${styleText("yellow", S_BAR)} ` : "";
const footer = this.error.split("\n").map((line, index) => index === 0 ? `${showGuide ? `${styleText("yellow", S_BAR_END)} ` : ""}${styleText("yellow", line)}` : ` ${line}`).join("\n");
const titleLineCount = title.split("\n").length;
const footerLineCount = footer.split("\n").length + 1;
return `${title}${prefix}${limitOptions({
output: opts.output,
options: this.options,
cursor: this.cursor,
maxItems: opts.maxItems,
columnPadding: prefix.length,
rowPadding: titleLineCount + footerLineCount,
style: styleOption
}).join(`\n${prefix}`)}\n${footer}\n`;
}
default: {
const prefix = showGuide ? `${styleText("cyan", S_BAR)} ` : "";
const footerLines = [...navigationFooterLines(showGuide, "cyan", opts.navigation, [styleText("dim", "↑/↓ option"), styleText("dim", "space select")]), showGuide ? styleText("cyan", S_BAR_END) : ""];
const titleLineCount = title.split("\n").length;
const footerLineCount = footerLines.length + 1;
return `${title}${prefix}${limitOptions({
output: opts.output,
options: this.options,
cursor: this.cursor,
maxItems: opts.maxItems,
columnPadding: prefix.length,
rowPadding: titleLineCount + footerLineCount,
style: styleOption
}).join(`\n${prefix}`)}\n${footerLines.join("\n")}\n`;
}
}
}
}).prompt();
}
function autocompleteMultiselectWithNavigationFooter(opts) {
const formatOption = (option, active, selectedValues, focusedValue) => {
const isSelected = selectedValues.includes(option.value);
const label = getOptionLabel(option);
const hint = option.hint && focusedValue !== void 0 && option.value === focusedValue ? styleText("dim", ` (${option.hint})`) : "";
const checkbox = isSelected ? styleText("green", S_CHECKBOX_SELECTED) : styleText("dim", S_CHECKBOX_INACTIVE);
if (option.disabled) return `${styleText("gray", S_CHECKBOX_INACTIVE)} ${styleText(["strikethrough", "gray"], label)}`;
if (active) return `${checkbox} ${label}${hint}`;
return `${checkbox} ${styleText("dim", label)}`;
};
const prompt = new AutocompletePrompt({
options: opts.options,
multiple: true,
placeholder: opts.placeholder,
filter: opts.filter ?? getFilteredOption,
validate: () => {
if (opts.required && prompt.selectedValues.length === 0) return "Please select at least one item";
},
initialValue: opts.initialValues,
signal: opts.signal,
input: opts.input,
output: opts.output,
render() {
const showGuide = hasGuide(opts);
const title = `${showGuide ? `${styleText("gray", S_BAR)}\n` : ""}${symbol(this.state)} ${opts.message}\n`;
const userInput = this.userInput;
const showPlaceholder = userInput === "" && opts.placeholder !== void 0;
const searchText = this.isNavigating || showPlaceholder ? styleText("dim", showPlaceholder ? opts.placeholder ?? "" : userInput) : this.userInputWithCursor;
const options = this.options;
const matches = this.filteredOptions.length !== options.length ? styleText("dim", ` (${this.filteredOptions.length} match${this.filteredOptions.length === 1 ? "" : "es"})`) : "";
switch (this.state) {
case "submit": return `${title}${showGuide ? `${styleText("gray", S_BAR)} ` : ""}${styleText("dim", `${this.selectedValues.length} items selected`)}`;
case "cancel": return `${title}${showGuide ? `${styleText("gray", S_BAR)} ` : ""}${styleText(["strikethrough", "dim"], userInput)}`;
default: {
const barStyle = this.state === "error" ? "yellow" : "cyan";
const guidePrefix = showGuide ? `${styleText(barStyle, S_BAR)} ` : "";
const guidePrefixEnd = showGuide ? styleText(barStyle, S_BAR_END) : "";
const instructions = [
`${styleText("dim", "↑/↓")} to navigate`,
`${styleText("dim", this.isNavigating ? "Space/Tab:" : "Tab:")} select`,
`${styleText("dim", "Enter:")} confirm`,
`${styleText("dim", "Type:")} to search`
];
const noResults = this.filteredOptions.length === 0 && userInput ? [`${guidePrefix}${styleText("yellow", "No matches found")}`] : [];
const errorMessage = this.state === "error" ? [`${guidePrefix}${styleText("yellow", this.error)}`] : [];
const headerLines = [
...`${title}${showGuide ? styleText(barStyle, S_BAR) : ""}`.split("\n"),
`${guidePrefix}${styleText("dim", "Search:")} ${searchText}${matches}`,
...noResults,
...errorMessage
];
const footerLines = [
`${guidePrefix}${instructions.join(" • ")}`,
...navigationFooterLines(showGuide, barStyle, opts.navigation),
guidePrefixEnd
];
const displayOptions = limitOptions({
cursor: this.cursor,
options: this.filteredOptions,
style: (option, active) => formatOption(option, active, this.selectedValues, this.focusedValue),
maxItems: opts.maxItems,
output: opts.output,
rowPadding: headerLines.length + footerLines.length
});
return [
...headerLines,
...displayOptions.map((option) => `${guidePrefix}${option}`),
...footerLines
].join("\n");
}
}
}
});
return prompt.prompt();
}
function confirmWithNavigationFooter(opts) {
const active = opts.active ?? "Yes";
const inactive = opts.inactive ?? "No";
return new ConfirmPrompt({
active,
inactive,
signal: opts.signal,
input: opts.input,
output: opts.output,
initialValue: opts.initialValue ?? true,
render() {
const showGuide = hasGuide(opts);
const titlePrefix = `${symbol(this.state)} `;
const titlePrefixBar = showGuide ? `${styleText("gray", S_BAR)} ` : "";
const messageLines = wrapTextWithPrefix(opts.output, opts.message, titlePrefixBar, titlePrefix);
const title = `${showGuide ? `${styleText("gray", S_BAR)}\n` : ""}${messageLines}\n`;
const value = this.value ? active : inactive;
switch (this.state) {
case "submit": return `${title}${showGuide ? `${styleText("gray", S_BAR)} ` : ""}${styleText("dim", value)}`;
case "cancel": return `${title}${showGuide ? `${styleText("gray", S_BAR)} ` : ""}${styleText(["strikethrough", "dim"], value)}${showGuide ? `\n${styleText("gray", S_BAR)}` : ""}`;
default: {
const defaultPrefix = showGuide ? `${styleText("cyan", S_BAR)} ` : "";
const defaultPrefixEnd = showGuide ? styleText("cyan", S_BAR_END) : "";
const separator = opts.vertical ? showGuide ? `\n${styleText("cyan", S_BAR)} ` : "\n" : ` ${styleText("dim", "/")} `;
const footerLines = navigationFooterLines(showGuide, "cyan", opts.navigation, [styleText("dim", "↑/↓ option")]);
return `${title}${defaultPrefix}${this.value ? `${styleText("green", S_RADIO_ACTIVE)} ${active}` : `${styleText("dim", S_RADIO_INACTIVE)} ${styleText("dim", active)}`}${separator}${!this.value ? `${styleText("green", S_RADIO_ACTIVE)} ${inactive}` : `${styleText("dim", S_RADIO_INACTIVE)} ${styleText("dim", inactive)}`}\n${footerLines.length > 0 ? `${footerLines.join("\n")}\n` : ""}${defaultPrefixEnd}\n`;
}
}
}
}).prompt();
}
//#endregion
//#region src/wizard/clack-prompter.ts
const CLAW_SPINNER_FRAMES = [
"(\\/)",
"(||)",
"(--)",
"(||)"
];
const SPINNER_DECORATION_COLUMNS = 10;
function readProgressColumns(output) {
const columns = output.columns;
if (typeof columns !== "number" || !Number.isFinite(columns) || columns <= 0) return;
return columns;
}
function clampProgressLabel(label, columns) {
if (columns === void 0) return label;
const maxLabelWidth = columns - SPINNER_DECORATION_COLUMNS;
if (maxLabelWidth <= 0) return "";
if (visibleWidth(label) <= maxLabelWidth) return label;
return `${truncateToVisibleWidth(label, maxLabelWidth - 1)}…`;
}
function guardCancel(value, output, signal) {
if (isCancel(value)) {
if (!signal?.aborted) cancel(stylePromptTitle("Setup cancelled.") ?? "Setup cancelled.", { output });
throw new WizardCancelledError();
}
return value;
}
function resolveNavigationDirection(navigation, key) {
if (key?.name === "left" && navigation?.canGoBack) return "back";
if (key?.name === "right" && navigation?.canGoForward) return "forward";
}
function hasPromptNavigation(navigation) {
return navigation?.canGoBack === true || navigation?.canGoForward === true;
}
async function withHorizontalCursorActionsDisabled(disabled, work) {
if (!disabled) return await work();
const hadLeft = settings.actions.has("left");
const hadRight = settings.actions.has("right");
settings.actions.delete("left");
settings.actions.delete("right");
try {
return await work();
} finally {
if (hadLeft) settings.actions.add("left");
if (hadRight) settings.actions.add("right");
}
}
async function runPromptWithNavigation(navigation, work, output, externalSignal) {
const controller = new AbortController();
const signal = externalSignal ? AbortSignal.any([controller.signal, externalSignal]) : controller.signal;
let navigationDirection;
let promptSettled = false;
let cancellationImmediate;
const queueCancellation = () => {
if (cancellationImmediate || signal.aborted || navigationDirection) return;
cancellationImmediate = setImmediate(() => {
cancellationImmediate = void 0;
if (!promptSettled && !signal.aborted && !navigationDirection) controller.abort();
});
};
const onStdinEnd = () => queueCancellation();
const onKeypress = (input, key) => {
if (input === "" || key?.ctrl === true && key.name === "d") {
queueCancellation();
return;
}
const nextDirection = resolveNavigationDirection(navigation, key);
if (!nextDirection) return;
navigationDirection ??= nextDirection;
controller.abort();
};
try {
process.stdin.once("end", onStdinEnd);
if (process.stdin.readableEnded) queueCancellation();
process.stdin.on("keypress", onKeypress);
const value = await work(signal).finally(() => {
promptSettled = true;
});
if (navigationDirection) throw new WizardNavigationError(navigationDirection);
return guardCancel(value, output, externalSignal);
} finally {
if (cancellationImmediate) {
clearImmediate(cancellationImmediate);
cancellationImmediate = void 0;
}
process.stdin.off("end", onStdinEnd);
process.stdin.off("keypress", onKeypress);
}
}
function normalizeSearchTokens(search) {
return normalizeLowercaseStringOrEmpty(search).split(/\s+/).map((token) => token.trim()).filter((token) => token.length > 0);
}
function buildOptionSearchText(option) {
const label = stripAnsi(option.label ?? "");
const hint = stripAnsi(option.hint ?? "");
const value = String(option.value ?? "");
return normalizeLowercaseStringOrEmpty(`${label} ${hint} ${value}`);
}
function tokenizedOptionFilter(search, option) {
const tokens = normalizeSearchTokens(search);
if (tokens.length === 0) return true;
const haystack = buildOptionSearchText(option);
return tokens.every((token) => haystack.includes(token));
}
function createClackPrompter(output = process.stdout, signal) {
return {
intro: async (title) => {
intro(stylePromptTitle(title) ?? title, { output });
},
outro: async (message) => {
outro(stylePromptTitle(message) ?? message, { output });
},
note: async (message, title) => {
noteToStream(message, title, output);
},
plain: async (message) => {
output.write(message.endsWith("\n") ? message : `${message}\n`);
},
select: async (params) => {
const { message, options: styledOptions } = styleSelectParams(params);
const options = styledOptions;
return await withHorizontalCursorActionsDisabled(hasPromptNavigation(params.navigation), async () => await runPromptWithNavigation(params.navigation, async (promptSignal) => {
if (params.searchable) return params.navigation ? await autocompleteWithNavigationFooter({
message,
options,
initialValue: params.initialValue,
filter: tokenizedOptionFilter,
signal: promptSignal,
navigation: params.navigation,
output
}) : await autocomplete({
message,
options,
initialValue: params.initialValue,
filter: tokenizedOptionFilter,
signal: promptSignal,
output
});
return params.navigation ? await selectWithNavigationFooter({
message,
options,
initialValue: params.initialValue,
signal: promptSignal,
navigation: params.navigation,
output
}) : await select({
message,
options,
initialValue: params.initialValue,
signal: promptSignal,
output
});
}, output, signal));
},
multiselect: async (params) => {
const { message, options: styledOptions } = styleSelectParams(params);
const options = styledOptions;
return await withHorizontalCursorActionsDisabled(hasPromptNavigation(params.navigation), async () => await runPromptWithNavigation(params.navigation, async (promptSignal) => {
if (params.searchable) return params.navigation ? await autocompleteMultiselectWithNavigationFooter({
message,
options,
initialValues: params.initialValues,
filter: tokenizedOptionFilter,
signal: promptSignal,
navigation: params.navigation,
output
}) : await autocompleteMultiselect({
message,
options,
initialValues: params.initialValues,
filter: tokenizedOptionFilter,
signal: promptSignal,
output
});
return params.navigation ? await multiselectWithNavigationFooter({
message,
options,
initialValues: params.initialValues,
signal: promptSignal,
navigation: params.navigation,
output
}) : await multiselect({
message,
options,
initialValues: params.initialValues,
signal: promptSignal,
output
});
}, output, signal));
},
text: async (params) => {
const validate = params.validate;
return await withHorizontalCursorActionsDisabled(hasPromptNavigation(params.navigation), async () => await runPromptWithNavigation(params.navigation, async (promptSignal) => {
const message = stylePromptMessage(params.message);
const validateInput = validate ? (value) => validate(value ?? "") : void 0;
if (params.sensitive) return params.navigation ? await passwordWithNavigationFooter({
message,
validate: validateInput,
navigation: params.navigation,
signal: promptSignal,
output
}) : await password({
message,
validate: validateInput,
signal: promptSignal,
output
});
return params.navigation ? await textWithNavigationFooter({
message,
initialValue: params.initialValue,
placeholder: params.placeholder,
validate: validateInput,
navigation: params.navigation,
signal: promptSignal,
output
}) : await text({
message,
initialValue: params.initialValue,
placeholder: params.placeholder,
validate: validateInput,
signal: promptSignal,
output
});
}, output, params.signal && signal ? AbortSignal.any([params.signal, signal]) : params.signal ?? signal));
},
confirm: async (params) => await withHorizontalCursorActionsDisabled(hasPromptNavigation(params.navigation), async () => await runPromptWithNavigation(params.navigation, async (promptSignal) => {
const message = stylePromptMessage(params.message);
if (params.navigation) return await confirmWithNavigationFooter({
message,
initialValue: params.initialValue,
vertical: params.layout === "vertical",
navigation: params.navigation,
signal: promptSignal,
output
});
return await confirm({
message,
initialValue: params.initialValue,
vertical: params.layout === "vertical",
signal: promptSignal,
output
});
}, output, signal)),
progress: (label) => {
const spin = output.isTTY && isRich() && !process.env.CI && !process.env.VITEST ? spinner({
frames: CLAW_SPINNER_FRAMES,
delay: 120,
styleFrame: theme.accent,
output
}) : spinner({ output });
let currentLabel = label;
let maxColumns = readProgressColumns(output);
const renderLabel = () => theme.accent(clampProgressLabel(currentLabel, maxColumns));
const handleResize = () => {
const columns = readProgressColumns(output);
if (maxColumns === void 0 || columns === void 0 || columns >= maxColumns) return;
maxColumns = columns;
spin.message(renderLabel());
};
if (maxColumns !== void 0) output.on("resize", handleResize);
spin.start(renderLabel());
const osc = createCliProgress({
label,
indeterminate: true,
enabled: true,
fallback: "none",
stream: output
});
return {
update: (message) => {
currentLabel = message;
spin.message(renderLabel());
osc.setLabel(message);
},
stop: (message) => {
output.off("resize", handleResize);
osc.done();
if (message === void 0) spin.clear();
else spin.stop(message);
}
};
}
};
}
//#endregion
export { tokenizedOptionFilter as n, createClackPrompter as t };