add multiline-hidden prompt for both ui and cli
This commit is contained in:
@@ -364,11 +364,13 @@ in
|
||||
- hidden: A hidden text (e.g. password)
|
||||
- line: A single line of text
|
||||
- multiline: A multiline text
|
||||
- multiline-hidden: A multiline text
|
||||
'';
|
||||
type = enum [
|
||||
"hidden"
|
||||
"line"
|
||||
"multiline"
|
||||
"multiline-hidden"
|
||||
];
|
||||
default = "line";
|
||||
};
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import enum
|
||||
import logging
|
||||
import sys
|
||||
import termios
|
||||
import tty
|
||||
from dataclasses import dataclass
|
||||
from getpass import getpass
|
||||
from typing import Any
|
||||
@@ -15,6 +17,7 @@ class PromptType(enum.Enum):
|
||||
LINE = "line"
|
||||
HIDDEN = "hidden"
|
||||
MULTILINE = "multiline"
|
||||
MULTILINE_HIDDEN = "multiline-hidden"
|
||||
|
||||
|
||||
@dataclass
|
||||
@@ -36,6 +39,63 @@ class Prompt:
|
||||
)
|
||||
|
||||
|
||||
def get_multiline_hidden_input() -> str:
|
||||
"""
|
||||
Get multiline input from the user without echoing the input.
|
||||
This function allows the user to enter multiple lines of text,
|
||||
and it will return the concatenated string of all lines entered.
|
||||
The user can finish the input by pressing Ctrl-D (EOF).
|
||||
"""
|
||||
|
||||
# Save terminal settings
|
||||
fd = sys.stdin.fileno()
|
||||
old_settings = termios.tcgetattr(fd)
|
||||
|
||||
lines = []
|
||||
current_line: list[str] = []
|
||||
|
||||
try:
|
||||
# Change terminal settings - disable echo
|
||||
tty.setraw(fd)
|
||||
|
||||
while True:
|
||||
char = sys.stdin.read(1)
|
||||
|
||||
# Check for Ctrl-D (ASCII value 4 or EOF)
|
||||
if not char or ord(char) == 4:
|
||||
# Add last line if not empty
|
||||
if current_line:
|
||||
lines.append("".join(current_line))
|
||||
break
|
||||
|
||||
# Check for Ctrl-C (KeyboardInterrupt)
|
||||
if ord(char) == 3:
|
||||
raise KeyboardInterrupt
|
||||
|
||||
# Handle Enter key
|
||||
if char == "\r" or char == "\n":
|
||||
lines.append("".join(current_line))
|
||||
current_line = []
|
||||
# Print newline for visual feedback
|
||||
sys.stdout.write("\r\n")
|
||||
sys.stdout.flush()
|
||||
# Handle backspace
|
||||
elif ord(char) == 127 or ord(char) == 8:
|
||||
if current_line:
|
||||
current_line.pop()
|
||||
# Regular character
|
||||
else:
|
||||
current_line.append(char)
|
||||
|
||||
finally:
|
||||
# Restore terminal settings
|
||||
termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
|
||||
# Print a final newline for clean display
|
||||
print()
|
||||
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
def ask(
|
||||
ident: str,
|
||||
input_type: PromptType,
|
||||
@@ -53,6 +113,9 @@ def ask(
|
||||
case PromptType.MULTILINE:
|
||||
print(f"{text} (Finish with Ctrl-D): ")
|
||||
result = sys.stdin.read()
|
||||
case PromptType.MULTILINE_HIDDEN:
|
||||
print("Enter multiple lines (press Ctrl-D to finish or Ctrl-C to cancel):")
|
||||
result = get_multiline_hidden_input()
|
||||
case PromptType.HIDDEN:
|
||||
result = getpass(f"{text} (hidden): ")
|
||||
|
||||
|
||||
@@ -33,19 +33,19 @@ def set_var(machine: str | Machine, var: str | Var, value: bytes, flake: Flake)
|
||||
)
|
||||
|
||||
|
||||
def set_via_stdin(machine: str, var_id: str, flake: Flake) -> None:
|
||||
var = get_var(str(flake.path), machine, var_id)
|
||||
def set_via_stdin(machine_name: str, var_id: str, flake: Flake) -> None:
|
||||
machine = Machine(name=machine_name, flake=flake)
|
||||
var = get_var(str(flake.path), machine_name, var_id)
|
||||
if sys.stdin.isatty():
|
||||
new_value = ask(
|
||||
var.id,
|
||||
PromptType.HIDDEN,
|
||||
PromptType.MULTILINE_HIDDEN,
|
||||
None,
|
||||
).encode("utf-8")
|
||||
else:
|
||||
new_value = sys.stdin.buffer.read()
|
||||
|
||||
_machine = Machine(name=machine, flake=flake)
|
||||
set_var(_machine, var, new_value, flake)
|
||||
set_var(machine, var, new_value, flake)
|
||||
|
||||
|
||||
def _set_command(args: argparse.Namespace) -> None:
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { callApi } from "@/src/api";
|
||||
import {
|
||||
createForm,
|
||||
FieldValues,
|
||||
SubmitHandler,
|
||||
validate,
|
||||
FieldValues,
|
||||
} from "@modular-forms/solid";
|
||||
import { createQuery, useQueryClient } from "@tanstack/solid-query";
|
||||
import { Typography } from "@/src/components/Typography";
|
||||
@@ -113,21 +113,43 @@ export const VarsStep = (props: VarsStepProps) => {
|
||||
</Typography>
|
||||
{/* Avoid nesting issue in case of a "." */}
|
||||
<Field
|
||||
name={`${generator.name.replaceAll(".", "__dot__")}.${prompt.name.replaceAll(".", "__dot__")}`}
|
||||
name={`${generator.name.replaceAll(
|
||||
".",
|
||||
"__dot__",
|
||||
)}.${prompt.name.replaceAll(".", "__dot__")}`}
|
||||
>
|
||||
{(field, props) => (
|
||||
<TextInput
|
||||
inputProps={{
|
||||
...props,
|
||||
type:
|
||||
prompt.prompt_type === "hidden"
|
||||
? "password"
|
||||
: "text",
|
||||
}}
|
||||
label={prompt.description}
|
||||
value={prompt.previous_value ?? ""}
|
||||
error={field.error}
|
||||
/>
|
||||
<Switch
|
||||
fallback={
|
||||
<TextInput
|
||||
inputProps={{
|
||||
...props,
|
||||
type:
|
||||
prompt.prompt_type === "hidden"
|
||||
? "password"
|
||||
: "text",
|
||||
}}
|
||||
label={prompt.description}
|
||||
value={prompt.previous_value ?? ""}
|
||||
error={field.error}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<Match
|
||||
when={
|
||||
prompt.prompt_type === "multiline" ||
|
||||
prompt.prompt_type === "multiline-hidden"
|
||||
}
|
||||
>
|
||||
<textarea
|
||||
{...props}
|
||||
class="w-full h-32 border border-gray-300 rounded-md p-2"
|
||||
placeholder={prompt.description}
|
||||
value={prompt.previous_value ?? ""}
|
||||
name={prompt.description}
|
||||
/>
|
||||
</Match>
|
||||
</Switch>
|
||||
)}
|
||||
</Field>
|
||||
</Group>
|
||||
|
||||
Reference in New Issue
Block a user