diff --git a/cmd/lk/app.go b/cmd/lk/app.go index 96ba0f71..4897073c 100644 --- a/cmd/lk/app.go +++ b/cmd/lk/app.go @@ -88,10 +88,17 @@ var ( { Name: "install", Usage: "Execute installation defined in " + bootstrap.TaskFile, - ArgsUsage: "`DIR` location or the project directory (default: current directory)", + ArgsUsage: "[DIR] location of the project directory (default: current directory)", Before: requireProject, Action: installTemplate, }, + { + Hidden: true, + Name: "run", + Usage: "Execute a task defined in " + bootstrap.TaskFile, + ArgsUsage: "[TASK] to run in the project's taskfile.yaml", + Action: runTask, + }, { Name: "env", Usage: "Manage environment variables", @@ -100,12 +107,6 @@ var ( return instantiateEnv(ctx, cmd, ".") }, }, - { - Name: "run", - Usage: "Execute a task defined in " + bootstrap.TaskFile, - ArgsUsage: "`DIR` location or the project directory (default: current directory)", - Action: runTask, - }, }, }, } @@ -253,7 +254,7 @@ func setupSandboxTemplate(ctx context.Context, cmd *cli.Command) error { return errors.New("sandbox ID is required") } - _, token, err := requireToken(ctx, cmd) + token, err := requireToken(ctx, cmd) if err != nil { return err } @@ -364,7 +365,10 @@ func doInstall(ctx context.Context, task bootstrap.KnownTask, rootPath string, v fullPath, err := filepath.Abs(rootPath) if fullPath != "" { - fmt.Println("Installed template to " + fullPath) + fmt.Println("Installed template to " + fullPath + ". To start your sandbox:\n") + fmt.Println(" cd " + fullPath) + fmt.Println(" lk app run dev_sandbox") + fmt.Println("") } return err @@ -372,17 +376,29 @@ func doInstall(ctx context.Context, task bootstrap.KnownTask, rootPath string, v func runTask(ctx context.Context, cmd *cli.Command) error { verbose := cmd.Bool("verbose") - taskName := cmd.Args().First() - if taskName == "" { - return errors.New("task name is required") - } - rootDir := "." tf, err := bootstrap.ParseTaskfile(rootDir) if err != nil { return err } + taskName := cmd.Args().First() + if taskName == "" { + var options []huh.Option[string] + for _, name := range tf.Tasks.Keys() { + options = append(options, huh.NewOption(name, name)) + } + + if err := huh.NewSelect[string](). + Title("Select Task"). + Options(options...). + Value(&taskName). + WithTheme(theme). + Run(); err != nil { + return err + } + } + task, err := bootstrap.NewTask(ctx, tf, rootDir, taskName, cmd.Bool("verbose")) if err != nil { return err diff --git a/cmd/lk/cloud.go b/cmd/lk/cloud.go index 80fe59d8..02fa72e3 100644 --- a/cmd/lk/cloud.go +++ b/cmd/lk/cloud.go @@ -230,35 +230,38 @@ func handleAuth(ctx context.Context, cmd *cli.Command) error { if err := loadProjectConfig(ctx, cmd); err != nil { return err } - cfg, token, err := requireToken(ctx, cmd) + token, err := requireToken(ctx, cmd) if err != nil { return err } - return authClient.Deauthenticate(ctx, cfg.Name, token) + return authClient.Deauthenticate(ctx, project.Name, token) } return tryAuthIfNeeded(ctx, cmd) } -func requireToken(_ context.Context, cmd *cli.Command) (*config.ProjectConfig, string, error) { - cfg, err := loadProjectDetails(cmd) - if err != nil { - return nil, "", err +func requireToken(_ context.Context, cmd *cli.Command) (string, error) { + if project == nil { + var err error + project, err = loadProjectDetails(cmd) + if err != nil { + return "", err + } } // construct a token from the chosen project, using the hashed secret as the identity // as a means of preventing any old token generated with this key/secret pair from // deleting it - hash, err := hashString(cfg.APISecret) + hash, err := hashString(project.APISecret) if err != nil { - return nil, "", err + return "", err } - at := auth.NewAccessToken(cfg.APIKey, cfg.APISecret).SetIdentity(hash) + at := auth.NewAccessToken(project.APIKey, project.APISecret).SetIdentity(hash) token, err := at.ToJWT() if err != nil { - return nil, "", err + return "", err } - return cfg, token, nil + return token, nil } func tryAuthIfNeeded(ctx context.Context, cmd *cli.Command) error { diff --git a/cmd/lk/main.go b/cmd/lk/main.go index 16a482b6..6b7b8ae7 100644 --- a/cmd/lk/main.go +++ b/cmd/lk/main.go @@ -91,7 +91,6 @@ func main() { go func() { <-ctx.Done() stop() - fmt.Println() }() checkForLegacyName() diff --git a/cmd/lk/project.go b/cmd/lk/project.go index 85f2cc7c..d55dd41d 100644 --- a/cmd/lk/project.go +++ b/cmd/lk/project.go @@ -19,7 +19,6 @@ import ( "errors" "fmt" "net/url" - "os" "regexp" "github.com/charmbracelet/huh" @@ -243,10 +242,9 @@ func listProjects(ctx context.Context, cmd *cli.Command) error { return nil } - re := lipgloss.NewRenderer(os.Stdout) - baseStyle := re.NewStyle().Padding(0, 1) + baseStyle := theme.Form.Foreground(fg).Padding(0, 1) headerStyle := baseStyle.Bold(true) - selectedStyle := baseStyle.Foreground(cyan) + selectedStyle := theme.Focused.Title.Padding(0, 1) table := CreateTable(). StyleFunc(func(row, col int) lipgloss.Style { diff --git a/cmd/lk/style.go b/cmd/lk/style.go index 5511ca2d..d16b3af9 100644 --- a/cmd/lk/style.go +++ b/cmd/lk/style.go @@ -20,47 +20,9 @@ import ( ) var ( - normalFg = lipgloss.AdaptiveColor{Light: "235", Dark: "252"} - normalBg = lipgloss.AdaptiveColor{Light: "20", Dark: "0"} - dimFg = lipgloss.AdaptiveColor{Light: "", Dark: "243"} - placeholderFg = lipgloss.AdaptiveColor{Light: "248", Dark: "238"} - cyan = lipgloss.AdaptiveColor{Light: "#06B7DB", Dark: "#1FD5F9"} - red = lipgloss.AdaptiveColor{Light: "#CE4A3B", Dark: "#FF6352"} - yellow = lipgloss.AdaptiveColor{Light: "#DB9406", Dark: "#F9B11F"} - green = lipgloss.AdaptiveColor{Light: "#036D26", Dark: "#06DB4D"} - + fg = lipgloss.AdaptiveColor{Light: "235", Dark: "252"} theme = func() *huh.Theme { - t := huh.ThemeBase() - - t.Focused.Base = t.Focused.Base.BorderForeground(lipgloss.Color("238")) - t.Focused.Title = t.Focused.Title.Foreground(cyan).Bold(true) - t.Focused.NoteTitle = t.Focused.NoteTitle.Foreground(cyan).Bold(true).MarginBottom(1) - t.Focused.Directory = t.Focused.Directory.Foreground(cyan) - t.Focused.Description = t.Focused.Description.Foreground(dimFg) - t.Focused.ErrorIndicator = t.Focused.ErrorIndicator.Foreground(red) - t.Focused.ErrorMessage = t.Focused.ErrorMessage.Foreground(red) - t.Focused.SelectSelector = t.Focused.SelectSelector.Foreground(yellow) - t.Focused.NextIndicator = t.Focused.NextIndicator.Foreground(yellow) - t.Focused.PrevIndicator = t.Focused.PrevIndicator.Foreground(yellow) - t.Focused.Option = t.Focused.Option.Foreground(normalFg) - t.Focused.MultiSelectSelector = t.Focused.MultiSelectSelector.Foreground(yellow) - t.Focused.SelectedOption = t.Focused.SelectedOption.Foreground(green) - t.Focused.SelectedPrefix = lipgloss.NewStyle().Foreground(green).SetString("✓ ") - t.Focused.UnselectedPrefix = lipgloss.NewStyle().Foreground(dimFg).SetString("• ") - t.Focused.UnselectedOption = t.Focused.UnselectedOption.Foreground(normalFg) - t.Focused.FocusedButton = t.Focused.FocusedButton.Foreground(normalBg).Background(cyan) - t.Focused.Next = t.Focused.FocusedButton - t.Focused.BlurredButton = t.Focused.BlurredButton.Foreground(normalFg).Background(lipgloss.AdaptiveColor{Light: "252", Dark: "237"}) - - // t.Focused.TextInput.Cursor = t.Focused.TextInput.Cursor.Foreground(yellow) - t.Focused.TextInput.Placeholder = t.Focused.TextInput.Placeholder.Foreground(placeholderFg) - t.Focused.TextInput.Prompt = t.Focused.TextInput.Prompt.Foreground(yellow) - - t.Blurred = t.Focused - t.Blurred.Base = t.Focused.Base.BorderStyle(lipgloss.HiddenBorder()) - t.Blurred.NextIndicator = lipgloss.NewStyle() - t.Blurred.PrevIndicator = lipgloss.NewStyle() - + t := huh.ThemeBase16() return t }() ) diff --git a/cmd/lk/utils.go b/cmd/lk/utils.go index 38af364f..11108e1a 100644 --- a/cmd/lk/utils.go +++ b/cmd/lk/utils.go @@ -166,8 +166,7 @@ func PrintJSON(obj any) { } func CreateTable() *table.Table { - re := lipgloss.NewRenderer(os.Stdout) - baseStyle := re.NewStyle().Padding(0, 1) + baseStyle := theme.Form.Foreground(fg).Padding(0, 1) headerStyle := baseStyle.Bold(true) styleFunc := func(row, col int) lipgloss.Style { @@ -179,7 +178,7 @@ func CreateTable() *table.Table { t := table.New(). Border(lipgloss.NormalBorder()). - BorderStyle(re.NewStyle().Foreground(normalFg)). + BorderStyle(theme.Form.Foreground(fg)). StyleFunc(styleFunc) return t @@ -266,7 +265,7 @@ func loadProjectDetails(c *cli.Command, opts ...loadOption) (*config.ProjectConf // load default project dp, err := config.LoadDefaultProject() if err == nil { - fmt.Println("Using default project", dp.Name) + fmt.Println("Using default project [" + theme.Focused.Title.Render(dp.Name) + "]") logDetails(c, dp) return dp, nil }