askeroo
Version:
A modern CLI prompt library with flow control, history navigation, and conditional prompts
163 lines • 7.83 kB
JavaScript
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
import { useState, useEffect } from "react";
import { Text, Box, useInput, Newline } from "ink";
import { createPrompt } from "../../core/registry.js";
import { useFieldReset } from "../../hooks/use-auto-submit.js";
import { TextInput } from "../../components/TextInput.js";
// Core text input plugin
export const text = createPrompt({
type: "text",
// autoSubmit: false (default) - Requires user interaction
component: ({ node, options, events }) => {
const initialValue = options.initialValue || "";
const [value, setValue] = useState(initialValue);
const [submitted, setSubmitted] = useState(false);
const [validationError, setValidationError] = useState(null);
// Reset state when field becomes active
const disabled = node.state === "disabled";
useFieldReset(disabled, submitted, setSubmitted);
useEffect(() => {
if (!disabled) {
const initial = options.initialValue || "";
setValue(initial);
}
}, [options.initialValue, disabled]);
// Validation helper
const runValidation = async (val) => {
if (!events.onValidate || node.state !== "active") {
setValidationError(null);
return true;
}
try {
const result = await events.onValidate(val);
setValidationError(result);
return result === null;
}
catch {
setValidationError("Validation error occurred");
return false;
}
};
// Hint management
useEffect(() => {
if (!events.onHintChange)
return;
// Only show hint if there's back navigation available
const hasHint = node.state === "active" &&
!node.isFirstRootPrompt &&
node.allowBack;
events.onHintChange(hasHint ? (_jsxs(_Fragment, { children: [_jsx(Newline, {}), _jsx(Text, { color: "yellow", children: "escape" }), " go back"] })) : null);
}, [
node.state,
node.isFirstRootPrompt,
node.allowBack,
events.onHintChange,
]);
useInput(async (input, key) => {
if (submitted || node.state !== "active")
return;
// Static group navigation
if (node.flow === "static" && node.enableArrowNavigation) {
if (key.downArrow && !node.isLastInGroup) {
if (!(await runValidation(value)))
return;
setSubmitted(true);
// Call user's onSubmit callback if provided and use return value if any
let finalValue = value;
if (options.onSubmit) {
const result = options.onSubmit(value);
if (result !== undefined) {
finalValue = result;
}
}
events.onSubmit?.(finalValue);
return;
}
if (key.upArrow && !node.isFirstInGroup) {
if (!(await runValidation(value)))
return;
setSubmitted(true);
// Call user's onSubmit callback if provided and use return value if any
let finalValue = value;
if (options.onSubmit) {
const result = options.onSubmit(value);
if (result !== undefined) {
finalValue = result;
}
}
events.onSubmit?.({
__preserveAndBack: true,
value: finalValue,
});
return;
}
if (key.downArrow || key.upArrow)
return;
}
// Escape handling
if (key.escape) {
if (node.flow === "static") {
if (node.enableArrowNavigation &&
!node.isFirstInGroup) {
if (!(await runValidation(value)))
return;
setSubmitted(true);
// Call user's onSubmit callback if provided and use return value if any
let finalValue = value;
if (options.onSubmit) {
const result = options.onSubmit(value);
if (result !== undefined) {
finalValue = result;
}
}
events.onSubmit?.({
__preserveAndBack: true,
value: finalValue,
});
}
else if (node.enableArrowNavigation &&
node.isFirstInGroup) {
setSubmitted(true);
// Note: For __clearGroupAndBack, we don't call user's onSubmit
// as this is a special navigation case
events.onSubmit?.({ __clearGroupAndBack: true });
}
else if (node.allowBack && events.onBack) {
events.onBack();
}
}
else if (node.allowBack && events.onBack) {
events.onBack();
}
return;
}
}, { isActive: node.state === "active" && !submitted });
const handleSubmit = async (val) => {
if (!(await runValidation(val)))
return;
setSubmitted(true);
// Call user's onSubmit callback if provided and use return value if any
let finalValue = val;
if (options.onSubmit) {
const result = options.onSubmit(val);
if (result !== undefined) {
finalValue = result;
}
}
events.onSubmit?.(finalValue);
};
const handleEscape = () => {
if (node.allowBack && events.onBack && node.flow !== "static") {
events.onBack();
}
};
if (node.state === "completed") {
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { children: options.label }), _jsx(Text, { color: "blue", children: node.completedValue || value })] }));
}
if (node.state === "disabled") {
return (_jsxs(Box, { gap: 1, children: [_jsx(Box, { width: 14, children: _jsx(Text, { dimColor: true, children: options.shortLabel || options.label }) }), _jsx(Text, { dimColor: true, color: "gray", children: "..." })] }));
}
return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { flexDirection: node.flow === "static" ? "row" : "column", gap: node.flow === "static" ? 1 : 0, marginBottom: node.flow === "static" && !node.isLastInGroup ? 1 : 0, children: [_jsx(Box, { width: node.flow === "static" ? 14 : undefined, children: _jsx(Text, { children: options.label }) }), _jsx(TextInput, { value: value, onChange: setValue, onSubmit: handleSubmit, isActive: node.state === "active" && !submitted, color: "cyan", onEscape: handleEscape, disableArrowKeys: node.flow === "static" && node.enableArrowNavigation })] }), validationError && (_jsx(Box, { children: _jsx(Text, { color: "red", children: validationError }) }))] }));
},
});
//# sourceMappingURL=index.js.map