use huh
This commit is contained in:
+187
-12
@@ -3,27 +3,202 @@ package tui
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
tea "charm.land/bubbletea/v2"
|
||||
"charm.land/huh/v2"
|
||||
"charm.land/huh/v2/spinner"
|
||||
|
||||
"dirmd/internal/config"
|
||||
)
|
||||
|
||||
func Run() error {
|
||||
p := tea.NewProgram(New())
|
||||
m, err := p.Run()
|
||||
if err != nil {
|
||||
return fmt.Errorf("tui error: %w", err)
|
||||
var (
|
||||
inputDir string
|
||||
outputDir string
|
||||
force bool
|
||||
verticalSlices bool
|
||||
skipFrontend bool
|
||||
frontendOnly bool
|
||||
omitTest bool
|
||||
mitMd bool
|
||||
protonDrive bool
|
||||
drivePath string
|
||||
maxDepthStr string
|
||||
maxSizeStr string
|
||||
ignorePatterns string
|
||||
)
|
||||
|
||||
inputDir = "."
|
||||
maxDepthStr = "20"
|
||||
maxSizeStr = "512"
|
||||
defOut := defaultOutputDir()
|
||||
outputDir = defOut
|
||||
|
||||
form := huh.NewForm(
|
||||
huh.NewGroup(
|
||||
huh.NewInput().Title("Input directory").Value(&inputDir).Placeholder("."),
|
||||
huh.NewInput().Title("Output directory").Value(&outputDir).Placeholder(defOut),
|
||||
huh.NewConfirm().Title("Force overwrite?").Value(&force),
|
||||
huh.NewConfirm().Title("Vertical slices?").Value(&verticalSlices),
|
||||
huh.NewConfirm().Title("Skip frontend files?").Value(&skipFrontend),
|
||||
huh.NewConfirm().Title("Frontend only?").Value(&frontendOnly),
|
||||
huh.NewConfirm().Title("Omit test files?").Value(&omitTest),
|
||||
huh.NewConfirm().Title("Omit non-README markdown?").Value(&mitMd),
|
||||
huh.NewConfirm().Title("Upload to Proton Drive?").Value(&protonDrive),
|
||||
huh.NewInput().Title("Max depth").Value(&maxDepthStr).Placeholder("20"),
|
||||
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.NewGroup(
|
||||
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 {
|
||||
return fmt.Errorf("form: %w", err)
|
||||
}
|
||||
|
||||
if model, ok := m.(Model); ok {
|
||||
if model.command != "" {
|
||||
if model.result.Error != nil {
|
||||
fmt.Fprintf(os.Stderr, "failed: %s\n", model.command)
|
||||
fmt.Fprintf(os.Stderr, "error: %v\n", model.result.Error)
|
||||
} else {
|
||||
fmt.Fprintf(os.Stdout, "%s\n", model.command)
|
||||
maxDepth, err := strconv.Atoi(strings.TrimSpace(maxDepthStr))
|
||||
if err != nil || maxDepth <= 0 {
|
||||
maxDepth = 20
|
||||
}
|
||||
|
||||
maxSizeKB, err := strconv.Atoi(strings.TrimSpace(maxSizeStr))
|
||||
if err != nil || maxSizeKB <= 0 {
|
||||
maxSizeKB = 512
|
||||
}
|
||||
|
||||
var ignores []string
|
||||
raw := strings.TrimSpace(ignorePatterns)
|
||||
if raw != "" {
|
||||
for _, line := range strings.Split(raw, "\n") {
|
||||
line = strings.TrimSpace(line)
|
||||
if line != "" {
|
||||
ignores = append(ignores, line)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if skipFrontend && frontendOnly {
|
||||
return fmt.Errorf("--skip-frontend and --frontend-only are mutually exclusive")
|
||||
}
|
||||
|
||||
excludedExts := append([]string{}, config.DefaultExts...)
|
||||
if skipFrontend {
|
||||
excludedExts = append(excludedExts, config.FrontendExts...)
|
||||
}
|
||||
|
||||
allIgnores := append([]string{}, config.DefaultIgnores...)
|
||||
allIgnores = append(allIgnores, ignores...)
|
||||
|
||||
cfg := &config.Config{
|
||||
InputPath: inputDir,
|
||||
OutputPath: outputDir,
|
||||
Force: force,
|
||||
MaxSize: int64(maxSizeKB) * 1024,
|
||||
MaxDepth: maxDepth,
|
||||
Ignores: allIgnores,
|
||||
Extensions: excludedExts,
|
||||
ProtonDrive: protonDrive,
|
||||
DrivePath: drivePath,
|
||||
VerticalSlices: verticalSlices,
|
||||
SkipFrontend: skipFrontend,
|
||||
FrontendOnly: frontendOnly,
|
||||
OmitTest: omitTest,
|
||||
OmitMd: mitMd,
|
||||
}
|
||||
|
||||
cmd := buildCommand(cfg)
|
||||
fmt.Fprintf(os.Stderr, "%s\n", cmd)
|
||||
|
||||
var result GenerateResult
|
||||
if err := spinner.New().
|
||||
Title("Generating...").
|
||||
Action(func() {
|
||||
result = Generate(cfg)
|
||||
}).
|
||||
Run(); err != nil {
|
||||
return fmt.Errorf("interrupted: %w", err)
|
||||
}
|
||||
|
||||
if result.Error != nil {
|
||||
return result.Error
|
||||
}
|
||||
|
||||
for _, f := range result.Files {
|
||||
fmt.Fprintf(os.Stdout, " %s\n", f)
|
||||
}
|
||||
if result.Uploaded {
|
||||
fmt.Fprintln(os.Stdout, "Uploaded to Proton Drive")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func defaultOutputDir() string {
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return "/tmp/output_dirmd"
|
||||
}
|
||||
base := filepath.Base(cwd)
|
||||
return filepath.Join("/tmp", base+"_dirmd")
|
||||
}
|
||||
|
||||
func buildCommand(cfg *config.Config) string {
|
||||
var parts []string
|
||||
parts = append(parts, "dirmd")
|
||||
parts = append(parts, "-o", cfg.OutputPath)
|
||||
if cfg.Force {
|
||||
parts = append(parts, "-f")
|
||||
}
|
||||
if cfg.VerticalSlices {
|
||||
parts = append(parts, "--vertical-slices")
|
||||
}
|
||||
if cfg.SkipFrontend {
|
||||
parts = append(parts, "--skip-frontend")
|
||||
}
|
||||
if cfg.FrontendOnly {
|
||||
parts = append(parts, "--frontend-only")
|
||||
}
|
||||
if cfg.OmitTest {
|
||||
parts = append(parts, "--omit-test")
|
||||
}
|
||||
if cfg.OmitMd {
|
||||
parts = append(parts, "--omit-md")
|
||||
}
|
||||
if cfg.ProtonDrive {
|
||||
parts = append(parts, "--proton-drive")
|
||||
if cfg.DrivePath != "" {
|
||||
parts = append(parts, "--drive-path", cfg.DrivePath)
|
||||
}
|
||||
}
|
||||
if cfg.MaxDepth != 20 {
|
||||
parts = append(parts, "--max-depth", strconv.Itoa(cfg.MaxDepth))
|
||||
}
|
||||
if cfg.MaxSize != 512*1024 {
|
||||
parts = append(parts, "--max-size", strconv.FormatInt(cfg.MaxSize, 10))
|
||||
}
|
||||
for _, ig := range extraIgnores(cfg) {
|
||||
parts = append(parts, "--ignore", ig)
|
||||
}
|
||||
parts = append(parts, cfg.InputPath)
|
||||
return strings.Join(parts, " ")
|
||||
}
|
||||
|
||||
func extraIgnores(cfg *config.Config) []string {
|
||||
defaultSet := make(map[string]bool)
|
||||
for _, d := range config.DefaultIgnores {
|
||||
defaultSet[d] = true
|
||||
}
|
||||
var extra []string
|
||||
for _, ig := range cfg.Ignores {
|
||||
if !defaultSet[ig] {
|
||||
extra = append(extra, ig)
|
||||
}
|
||||
}
|
||||
return extra
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user