implement huh
This commit is contained in:
@@ -3,7 +3,6 @@ module dirmd
|
|||||||
go 1.26.4
|
go 1.26.4
|
||||||
|
|
||||||
require (
|
require (
|
||||||
charm.land/bubbles/v2 v2.2.1
|
|
||||||
charm.land/bubbletea/v2 v2.0.9
|
charm.land/bubbletea/v2 v2.0.9
|
||||||
charm.land/huh/v2 v2.0.3
|
charm.land/huh/v2 v2.0.3
|
||||||
charm.land/lipgloss/v2 v2.0.6
|
charm.land/lipgloss/v2 v2.0.6
|
||||||
@@ -11,6 +10,7 @@ require (
|
|||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
charm.land/bubbles/v2 v2.2.1 // indirect
|
||||||
github.com/atotto/clipboard v0.1.4 // indirect
|
github.com/atotto/clipboard v0.1.4 // indirect
|
||||||
github.com/catppuccin/go v0.2.0 // indirect
|
github.com/catppuccin/go v0.2.0 // indirect
|
||||||
github.com/charmbracelet/colorprofile v0.4.3 // indirect
|
github.com/charmbracelet/colorprofile v0.4.3 // indirect
|
||||||
|
|||||||
+100
-26
@@ -3,10 +3,12 @@ package tui
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"charm.land/bubbles/v2/key"
|
||||||
"charm.land/huh/v2"
|
"charm.land/huh/v2"
|
||||||
"charm.land/huh/v2/spinner"
|
"charm.land/huh/v2/spinner"
|
||||||
|
|
||||||
@@ -14,20 +16,17 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func Run() error {
|
func Run() error {
|
||||||
|
clearScreen()
|
||||||
|
|
||||||
var (
|
var (
|
||||||
inputDir string
|
inputDir string
|
||||||
outputDir string
|
outputDir string
|
||||||
force bool
|
options []string
|
||||||
verticalSlices bool
|
|
||||||
skipFrontend bool
|
|
||||||
frontendOnly bool
|
|
||||||
omitTest bool
|
|
||||||
mitMd bool
|
|
||||||
protonDrive bool
|
|
||||||
drivePath string
|
drivePath string
|
||||||
maxDepthStr string
|
maxDepthStr string
|
||||||
maxSizeStr string
|
maxSizeStr string
|
||||||
ignorePatterns string
|
ignorePatterns string
|
||||||
|
finalConfirm bool
|
||||||
)
|
)
|
||||||
|
|
||||||
inputDir = "."
|
inputDir = "."
|
||||||
@@ -35,33 +34,97 @@ func Run() error {
|
|||||||
maxSizeStr = "512"
|
maxSizeStr = "512"
|
||||||
defOut := defaultOutputDir()
|
defOut := defaultOutputDir()
|
||||||
outputDir = defOut
|
outputDir = defOut
|
||||||
|
drivePath = "/my-files/md/"
|
||||||
|
|
||||||
|
km := huh.NewDefaultKeyMap()
|
||||||
|
|
||||||
|
km.Input.Next = key.NewBinding(key.WithKeys("tab", "down"), key.WithHelp("tab/↓", "next"))
|
||||||
|
km.Input.Prev = key.NewBinding(key.WithKeys("shift+tab", "up"), key.WithHelp("shift+tab/↑", "prev"))
|
||||||
|
km.Input.Submit = key.NewBinding(key.WithKeys("enter"), key.WithHelp("enter", "submit"))
|
||||||
|
|
||||||
|
km.Text.Next = key.NewBinding(key.WithKeys("tab", "down"), key.WithHelp("tab/↓", "next"))
|
||||||
|
km.Text.Prev = key.NewBinding(key.WithKeys("shift+tab", "up"), key.WithHelp("shift+tab/↑", "prev"))
|
||||||
|
km.Text.NewLine = key.NewBinding(key.WithKeys("enter"), key.WithHelp("enter", "new line"))
|
||||||
|
km.Text.Submit = key.NewBinding(key.WithKeys("ctrl+s"), key.WithHelp("ctrl+s", "submit"))
|
||||||
|
|
||||||
|
km.Confirm.Next = key.NewBinding(key.WithKeys("tab", "down"), key.WithHelp("tab/↓", "next"))
|
||||||
|
km.Confirm.Prev = key.NewBinding(key.WithKeys("shift+tab", "up"), key.WithHelp("shift+tab/↑", "prev"))
|
||||||
|
km.Confirm.Toggle = key.NewBinding(key.WithKeys("left", "right", "h", "l"), key.WithHelp("←/→", "toggle"))
|
||||||
|
km.Confirm.Submit = key.NewBinding(key.WithKeys("enter"), key.WithHelp("enter", "confirm"))
|
||||||
|
km.Confirm.Accept = key.NewBinding(key.WithKeys("y"), key.WithHelp("y", "yes"))
|
||||||
|
km.Confirm.Reject = key.NewBinding(key.WithKeys("n"), key.WithHelp("n", "no"))
|
||||||
|
|
||||||
|
km.MultiSelect.Next = key.NewBinding(key.WithKeys("tab"), key.WithHelp("tab", "next field"))
|
||||||
|
km.MultiSelect.Prev = key.NewBinding(key.WithKeys("shift+tab"), key.WithHelp("shift+tab", "prev field"))
|
||||||
|
km.MultiSelect.Up = key.NewBinding(key.WithKeys("up", "k"), key.WithHelp("↑/k", "up"))
|
||||||
|
km.MultiSelect.Down = key.NewBinding(key.WithKeys("down", "j"), key.WithHelp("↓/j", "down"))
|
||||||
|
km.MultiSelect.Toggle = key.NewBinding(key.WithKeys("enter", " "), key.WithHelp("enter/space", "toggle"))
|
||||||
|
km.MultiSelect.Submit = key.NewBinding(key.WithKeys("tab"), key.WithHelp("tab", "next"))
|
||||||
|
km.MultiSelect.Filter = key.NewBinding(key.WithKeys("/"), key.WithHelp("/", "filter"))
|
||||||
|
km.MultiSelect.SelectAll = key.NewBinding(key.WithKeys("a"), key.WithHelp("a", "select all"))
|
||||||
|
km.MultiSelect.SelectNone = key.NewBinding(key.WithKeys("A"), key.WithHelp("A", "select none"))
|
||||||
|
|
||||||
form := huh.NewForm(
|
form := huh.NewForm(
|
||||||
huh.NewGroup(
|
huh.NewGroup(
|
||||||
huh.NewInput().Title("Input directory").Value(&inputDir).Placeholder("."),
|
huh.NewInput().Title("Input directory").Value(&inputDir).Placeholder("."),
|
||||||
huh.NewInput().Title("Output directory").Value(&outputDir).Placeholder(defOut),
|
huh.NewInput().Title("Output directory").Value(&outputDir).Placeholder(defOut),
|
||||||
huh.NewConfirm().Title("Force overwrite?").Value(&force),
|
huh.NewMultiSelect[string]().
|
||||||
huh.NewConfirm().Title("Vertical slices?").Value(&verticalSlices),
|
Title("Options").
|
||||||
huh.NewConfirm().Title("Skip frontend files?").Value(&skipFrontend),
|
Description("↑↓ navigate • enter/space toggle • tab next field").
|
||||||
huh.NewConfirm().Title("Frontend only?").Value(&frontendOnly),
|
Options(
|
||||||
huh.NewConfirm().Title("Omit test files?").Value(&omitTest),
|
huh.NewOption("Force overwrite", "force"),
|
||||||
huh.NewConfirm().Title("Omit non-README markdown?").Value(&mitMd),
|
huh.NewOption("Vertical slices", "vertical-slices"),
|
||||||
huh.NewConfirm().Title("Upload to Proton Drive?").Value(&protonDrive),
|
huh.NewOption("Skip frontend files", "skip-frontend"),
|
||||||
|
huh.NewOption("Frontend only", "frontend-only"),
|
||||||
|
huh.NewOption("Omit test files", "omit-test"),
|
||||||
|
huh.NewOption("Omit non-README markdown", "omit-md"),
|
||||||
|
huh.NewOption("Upload to Proton Drive", "proton-drive"),
|
||||||
|
).
|
||||||
|
Value(&options).
|
||||||
|
Height(10),
|
||||||
|
huh.NewInput().
|
||||||
|
Title("Proton Drive path").
|
||||||
|
Description("Used if 'Upload to Proton Drive' is toggled").
|
||||||
|
Value(&drivePath).
|
||||||
|
Placeholder("/my-files/md/"),
|
||||||
huh.NewInput().Title("Max depth").Value(&maxDepthStr).Placeholder("20"),
|
huh.NewInput().Title("Max depth").Value(&maxDepthStr).Placeholder("20"),
|
||||||
huh.NewInput().Title("Max size (KB)").Value(&maxSizeStr).Placeholder("512"),
|
huh.NewInput().Title("Max size (KB)").Value(&maxSizeStr).Placeholder("512"),
|
||||||
huh.NewText().Title("Ignore patterns (one per line)").Value(&ignorePatterns).Placeholder("One pattern per line..."),
|
huh.NewText().
|
||||||
|
Title("Ignore patterns (one per line)").
|
||||||
|
Description("enter = new line • tab = next field").
|
||||||
|
Value(&ignorePatterns).
|
||||||
|
Placeholder("One pattern per line...").
|
||||||
|
Lines(4),
|
||||||
|
huh.NewConfirm().
|
||||||
|
Title("Confirm and proceed?").
|
||||||
|
Description("←/→ or y/n to choose • enter to confirm").
|
||||||
|
Value(&finalConfirm).
|
||||||
|
Affirmative("Yes").
|
||||||
|
Negative("No"),
|
||||||
),
|
),
|
||||||
huh.NewGroup(
|
).WithWidth(80).WithKeyMap(km)
|
||||||
huh.NewInput().Title("Remote path on Proton Drive").Value(&drivePath).Placeholder("/my-files/md/"),
|
|
||||||
).WithHideFunc(func() bool {
|
|
||||||
return !protonDrive
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
|
|
||||||
if err := form.Run(); err != nil {
|
if err := form.Run(); err != nil {
|
||||||
return fmt.Errorf("form: %w", err)
|
return fmt.Errorf("form: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
force := containsOption(options, "force")
|
||||||
|
verticalSlices := containsOption(options, "vertical-slices")
|
||||||
|
skipFrontend := containsOption(options, "skip-frontend")
|
||||||
|
frontendOnly := containsOption(options, "frontend-only")
|
||||||
|
omitTest := containsOption(options, "omit-test")
|
||||||
|
omitMd := containsOption(options, "omit-md")
|
||||||
|
protonDrive := containsOption(options, "proton-drive")
|
||||||
|
|
||||||
|
if skipFrontend && frontendOnly {
|
||||||
|
return fmt.Errorf("--skip-frontend and --frontend-only are mutually exclusive")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !finalConfirm {
|
||||||
|
fmt.Fprintln(os.Stderr, "Cancelled.")
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
|
||||||
maxDepth, err := strconv.Atoi(strings.TrimSpace(maxDepthStr))
|
maxDepth, err := strconv.Atoi(strings.TrimSpace(maxDepthStr))
|
||||||
if err != nil || maxDepth <= 0 {
|
if err != nil || maxDepth <= 0 {
|
||||||
maxDepth = 20
|
maxDepth = 20
|
||||||
@@ -83,10 +146,6 @@ func Run() error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if skipFrontend && frontendOnly {
|
|
||||||
return fmt.Errorf("--skip-frontend and --frontend-only are mutually exclusive")
|
|
||||||
}
|
|
||||||
|
|
||||||
excludedExts := append([]string{}, config.DefaultExts...)
|
excludedExts := append([]string{}, config.DefaultExts...)
|
||||||
if skipFrontend {
|
if skipFrontend {
|
||||||
excludedExts = append(excludedExts, config.FrontendExts...)
|
excludedExts = append(excludedExts, config.FrontendExts...)
|
||||||
@@ -109,7 +168,7 @@ func Run() error {
|
|||||||
SkipFrontend: skipFrontend,
|
SkipFrontend: skipFrontend,
|
||||||
FrontendOnly: frontendOnly,
|
FrontendOnly: frontendOnly,
|
||||||
OmitTest: omitTest,
|
OmitTest: omitTest,
|
||||||
OmitMd: mitMd,
|
OmitMd: omitMd,
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd := buildCommand(cfg)
|
cmd := buildCommand(cfg)
|
||||||
@@ -139,6 +198,21 @@ func Run() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func clearScreen() {
|
||||||
|
cmd := exec.Command("clear")
|
||||||
|
cmd.Stdout = os.Stdout
|
||||||
|
cmd.Run()
|
||||||
|
}
|
||||||
|
|
||||||
|
func containsOption(opts []string, target string) bool {
|
||||||
|
for _, o := range opts {
|
||||||
|
if o == target {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func defaultOutputDir() string {
|
func defaultOutputDir() string {
|
||||||
cwd, err := os.Getwd()
|
cwd, err := os.Getwd()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
Reference in New Issue
Block a user