mirror of
https://github.com/psviderski/uncloud.git
synced 2026-08-26 19:13:34 +00:00
refactor: tui package for CLI styles and prompts, restyle confirmation
This commit is contained in:
+10
-2
@@ -71,7 +71,7 @@ func (cli *CLI) SetCurrentContext(name string) error {
|
||||
return cli.Config.Save()
|
||||
}
|
||||
|
||||
func (cli *CLI) GetContextOverrideOrCurrent() string {
|
||||
func (cli *CLI) ContextOverrideOrCurrent() string {
|
||||
if cli.contextOverride != "" {
|
||||
return cli.contextOverride
|
||||
}
|
||||
@@ -81,6 +81,14 @@ func (cli *CLI) GetContextOverrideOrCurrent() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
// DirectConnection returns the connection string if --connect or UNCLOUD_CONNECT was specified.
|
||||
func (cli *CLI) DirectConnection() string {
|
||||
if cli.conn != nil {
|
||||
return cli.conn.String()
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// ConnectCluster connects to a cluster using the context override or the current context if not specified.
|
||||
// If the CLI was initialised with a machine connection, the config is ignored and the connection is used instead.
|
||||
func (cli *CLI) ConnectCluster(ctx context.Context) (*client.Client, error) {
|
||||
@@ -316,7 +324,7 @@ type AddMachineOptions struct {
|
||||
// cluster. The machine client is connected to the new machine and can be used to interact with it.
|
||||
// Both client should be closed after use by the caller.
|
||||
func (cli *CLI) AddMachine(ctx context.Context, opts AddMachineOptions) (*client.Client, *client.Client, error) {
|
||||
contextName := cli.GetContextOverrideOrCurrent()
|
||||
contextName := cli.ContextOverrideOrCurrent()
|
||||
c, err := cli.ConnectCluster(ctx)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("connect to cluster (context '%s'): %w", contextName, err)
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
tea "charm.land/bubbletea/v2"
|
||||
"charm.land/lipgloss/v2"
|
||||
"github.com/psviderski/uncloud/internal/cli/config"
|
||||
"github.com/psviderski/uncloud/internal/cli/tui"
|
||||
"github.com/psviderski/uncloud/internal/fs"
|
||||
"github.com/psviderski/uncloud/pkg/client"
|
||||
"github.com/psviderski/uncloud/pkg/client/connector"
|
||||
@@ -33,7 +34,7 @@ func ConnectCluster(ctx context.Context, conn config.MachineConnection, opts Con
|
||||
// If the stdout is not a terminal, it falls back to simple progress logs to stderr.
|
||||
func connectClusterWithProgress(ctx context.Context, conn config.MachineConnection) (*client.Client, error) {
|
||||
// If stdout is not a terminal, fall back to simple progress logs.
|
||||
if !IsStdoutTerminal() {
|
||||
if !tui.IsStdoutTerminal() {
|
||||
fmt.Fprintln(os.Stderr, "Connecting to", conn.String())
|
||||
cli, err := connectCluster(ctx, conn)
|
||||
if err != nil {
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
|
||||
"charm.land/huh/v2"
|
||||
"github.com/cenkalti/backoff/v4"
|
||||
"github.com/psviderski/uncloud/internal/cli/tui"
|
||||
"github.com/psviderski/uncloud/internal/machine/api/pb"
|
||||
"github.com/psviderski/uncloud/internal/sshexec"
|
||||
"google.golang.org/protobuf/types/known/emptypb"
|
||||
@@ -88,7 +89,7 @@ func provisionMachine(ctx context.Context, exec sshexec.Executor, version string
|
||||
}
|
||||
|
||||
func promptResetMachine() error {
|
||||
if !IsStdinTerminal() {
|
||||
if !tui.IsStdinTerminal() {
|
||||
return errors.New("the remote machine is already initialised as a cluster member; " +
|
||||
"cannot ask to confirm reset in non-interactive mode, " +
|
||||
"use --yes flag or set UNCLOUD_AUTO_CONFIRM=true to auto-confirm")
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"charm.land/huh/v2"
|
||||
"golang.org/x/term"
|
||||
)
|
||||
|
||||
func Confirm() (bool, error) {
|
||||
var confirmed bool
|
||||
form := huh.NewForm(
|
||||
huh.NewGroup(
|
||||
huh.NewConfirm().
|
||||
Title(
|
||||
"Do you want to continue?",
|
||||
).
|
||||
Affirmative("Yes!").
|
||||
Negative("No").
|
||||
Value(&confirmed),
|
||||
),
|
||||
).WithAccessible(true)
|
||||
if err := form.Run(); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return confirmed, nil
|
||||
}
|
||||
|
||||
// IsStdinTerminal checks if the standard input is a terminal (TTY).
|
||||
func IsStdinTerminal() bool {
|
||||
return term.IsTerminal(int(os.Stdin.Fd()))
|
||||
}
|
||||
|
||||
// IsStdoutTerminal checks if the standard output is a terminal (TTY).
|
||||
func IsStdoutTerminal() bool {
|
||||
return term.IsTerminal(int(os.Stdout.Fd()))
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package tui
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
func PrintWarning(msg string) {
|
||||
styledMsg := BoldYellow.Render(fmt.Sprintf("WARNING: %s", msg))
|
||||
fmt.Fprintln(os.Stderr, styledMsg)
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package tui
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"charm.land/huh/v2"
|
||||
"charm.land/lipgloss/v2"
|
||||
"golang.org/x/term"
|
||||
)
|
||||
|
||||
// Confirm shows a confirmation prompt with a yellow-styled title.
|
||||
// If title is empty, it defaults to "Do you want to continue?".
|
||||
func Confirm(title string) (bool, error) {
|
||||
if title == "" {
|
||||
title = "Do you want to continue?"
|
||||
}
|
||||
|
||||
var confirmed bool
|
||||
form := huh.NewForm(
|
||||
huh.NewGroup(
|
||||
huh.NewConfirm().
|
||||
Title(title).
|
||||
Affirmative("Yes!").
|
||||
Negative("No").
|
||||
Value(&confirmed),
|
||||
),
|
||||
).WithTheme(ThemeConfirm()).
|
||||
WithAccessible(true)
|
||||
if err := form.Run(); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return confirmed, nil
|
||||
}
|
||||
|
||||
// ThemeConfirm returns a huh theme with a bold yellow title style for the confirmation prompt.
|
||||
func ThemeConfirm() huh.Theme {
|
||||
return huh.ThemeFunc(func(isDark bool) *huh.Styles {
|
||||
t := huh.ThemeBase(isDark)
|
||||
t.Focused.Title = t.Focused.Title.Foreground(lipgloss.Yellow).Bold(true)
|
||||
return t
|
||||
})
|
||||
}
|
||||
|
||||
// IsStdinTerminal checks if the standard input is a terminal (TTY).
|
||||
func IsStdinTerminal() bool {
|
||||
return term.IsTerminal(int(os.Stdin.Fd()))
|
||||
}
|
||||
|
||||
// IsStdoutTerminal checks if the standard output is a terminal (TTY).
|
||||
func IsStdoutTerminal() bool {
|
||||
return term.IsTerminal(int(os.Stdout.Fd()))
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package tui
|
||||
|
||||
import "charm.land/lipgloss/v2"
|
||||
|
||||
var (
|
||||
Faint = lipgloss.NewStyle().Faint(true)
|
||||
Red = lipgloss.NewStyle().Foreground(lipgloss.Red)
|
||||
Green = lipgloss.NewStyle().Foreground(lipgloss.Green)
|
||||
Yellow = lipgloss.NewStyle().Foreground(lipgloss.Yellow)
|
||||
|
||||
Bold = lipgloss.NewStyle().Bold(true)
|
||||
BoldRed = lipgloss.NewStyle().Bold(true).Foreground(lipgloss.Red)
|
||||
BoldGreen = lipgloss.NewStyle().Bold(true).Foreground(lipgloss.Green)
|
||||
BoldYellow = lipgloss.NewStyle().Bold(true).Foreground(lipgloss.Yellow)
|
||||
|
||||
NameStyle = lipgloss.NewStyle().Bold(true).Foreground(lipgloss.Color("152"))
|
||||
)
|
||||
Reference in New Issue
Block a user