much better

This commit is contained in:
2026-08-27 18:24:23 +01:00
parent 73c2009139
commit adc276c28a
5 changed files with 118 additions and 33 deletions
+2 -4
View File
@@ -21,11 +21,9 @@ func (c Checkbox) Render(focused bool) string {
marker = uncheckedStyle.Render(marker) marker = uncheckedStyle.Render(marker)
} }
label := labelStyle.Render(c.Label)
if focused { if focused {
label = focusedStyle.Render("▶ " + marker + " " + c.Label) return focusedStyle.Render("▶ " + marker + " " + c.Label)
return label
} }
return " " + marker + " " + label return " " + marker + " " + labelStyle.Render(c.Label)
} }
+20 -20
View File
@@ -14,7 +14,7 @@ import (
const ( const (
fieldInputDir = iota fieldInputDir = iota
fieldOutputFile fieldOutputDir
fieldAppend fieldAppend
fieldForce fieldForce
fieldVerticalSlices fieldVerticalSlices
@@ -35,7 +35,7 @@ const (
type FormModel struct { type FormModel struct {
focusIndex int focusIndex int
inputDir textinput.Model inputDir textinput.Model
outputFile textinput.Model outputDir textinput.Model
drivePath textinput.Model drivePath textinput.Model
maxDepth textinput.Model maxDepth textinput.Model
maxSize textinput.Model maxSize textinput.Model
@@ -60,10 +60,10 @@ func NewForm() FormModel {
inputDir.SetValue(".") inputDir.SetValue(".")
inputDir.SetWidth(60) inputDir.SetWidth(60)
outputFile := textinput.New() outputDir := textinput.New()
outputFile.Placeholder = "./output_dirmd/index.md" outputDir.Placeholder = "./output_dirmd"
outputFile.SetValue("./output_dirmd/index.md") outputDir.SetValue("./output_dirmd")
outputFile.SetWidth(60) outputDir.SetWidth(60)
drivePath := textinput.New() drivePath := textinput.New()
drivePath.Placeholder = "/my-files/md/" drivePath.Placeholder = "/my-files/md/"
@@ -85,7 +85,7 @@ func NewForm() FormModel {
m := FormModel{ m := FormModel{
inputDir: inputDir, inputDir: inputDir,
outputFile: outputFile, outputDir: outputDir,
drivePath: drivePath, drivePath: drivePath,
maxDepth: maxDepth, maxDepth: maxDepth,
maxSize: maxSize, maxSize: maxSize,
@@ -118,7 +118,7 @@ func (m *FormModel) SetDark(isDark bool) {
func (m *FormModel) applyStyles() { func (m *FormModel) applyStyles() {
tiStyles := getDefaultInputStyles(m.isDark) tiStyles := getDefaultInputStyles(m.isDark)
m.inputDir.SetStyles(tiStyles) m.inputDir.SetStyles(tiStyles)
m.outputFile.SetStyles(tiStyles) m.outputDir.SetStyles(tiStyles)
m.drivePath.SetStyles(tiStyles) m.drivePath.SetStyles(tiStyles)
m.maxDepth.SetStyles(tiStyles) m.maxDepth.SetStyles(tiStyles)
m.maxSize.SetStyles(tiStyles) m.maxSize.SetStyles(tiStyles)
@@ -133,7 +133,7 @@ func (m *FormModel) shouldSkipField(idx int) bool {
func isTextInputField(idx int) bool { func isTextInputField(idx int) bool {
switch idx { switch idx {
case fieldInputDir, fieldOutputFile, fieldDrivePath, fieldMaxDepth, fieldMaxSize, fieldIgnorePatterns: case fieldInputDir, fieldOutputDir, fieldDrivePath, fieldMaxDepth, fieldMaxSize, fieldIgnorePatterns:
return true return true
} }
return false return false
@@ -141,7 +141,7 @@ func isTextInputField(idx int) bool {
func (m *FormModel) focusActiveField() tea.Cmd { func (m *FormModel) focusActiveField() tea.Cmd {
m.inputDir.Blur() m.inputDir.Blur()
m.outputFile.Blur() m.outputDir.Blur()
m.drivePath.Blur() m.drivePath.Blur()
m.maxDepth.Blur() m.maxDepth.Blur()
m.maxSize.Blur() m.maxSize.Blur()
@@ -152,8 +152,8 @@ func (m *FormModel) focusActiveField() tea.Cmd {
switch m.focusIndex { switch m.focusIndex {
case fieldInputDir: case fieldInputDir:
cmd = m.inputDir.Focus() cmd = m.inputDir.Focus()
case fieldOutputFile: case fieldOutputDir:
cmd = m.outputFile.Focus() cmd = m.outputDir.Focus()
case fieldDrivePath: case fieldDrivePath:
cmd = m.drivePath.Focus() cmd = m.drivePath.Focus()
case fieldMaxDepth: case fieldMaxDepth:
@@ -269,9 +269,9 @@ func (m FormModel) Update(msg tea.Msg) (FormModel, tea.Cmd) {
var cmd tea.Cmd var cmd tea.Cmd
m.inputDir, cmd = m.inputDir.Update(msg) m.inputDir, cmd = m.inputDir.Update(msg)
cmds = append(cmds, cmd) cmds = append(cmds, cmd)
case fieldOutputFile: case fieldOutputDir:
var cmd tea.Cmd var cmd tea.Cmd
m.outputFile, cmd = m.outputFile.Update(msg) m.outputDir, cmd = m.outputDir.Update(msg)
cmds = append(cmds, cmd) cmds = append(cmds, cmd)
case fieldDrivePath: case fieldDrivePath:
var cmd tea.Cmd var cmd tea.Cmd
@@ -303,7 +303,7 @@ func (m FormModel) View() string {
usableWidth = 80 usableWidth = 80
} }
m.inputDir.SetWidth(usableWidth) m.inputDir.SetWidth(usableWidth)
m.outputFile.SetWidth(usableWidth) m.outputDir.SetWidth(usableWidth)
m.drivePath.SetWidth(usableWidth) m.drivePath.SetWidth(usableWidth)
m.ignoreArea.SetWidth(usableWidth) m.ignoreArea.SetWidth(usableWidth)
@@ -314,7 +314,7 @@ func (m FormModel) View() string {
b.WriteString(m.renderTextField("Input directory", m.inputDir, m.focusIndex == fieldInputDir)) b.WriteString(m.renderTextField("Input directory", m.inputDir, m.focusIndex == fieldInputDir))
b.WriteString("\n") b.WriteString("\n")
b.WriteString(m.renderTextField("Output file", m.outputFile, m.focusIndex == fieldOutputFile)) b.WriteString(m.renderTextField("Output directory", m.outputDir, m.focusIndex == fieldOutputDir))
b.WriteString("\n\n") b.WriteString("\n\n")
b.WriteString(m.appendMode.Render(m.focusIndex == fieldAppend)) b.WriteString(m.appendMode.Render(m.focusIndex == fieldAppend))
@@ -398,9 +398,9 @@ func (m FormModel) ToConfig() (*config.Config, error) {
inputDir = "." inputDir = "."
} }
outputFile := m.outputFile.Value() outputDir := m.outputDir.Value()
if outputFile == "" { if outputDir == "" {
return nil, fmt.Errorf("output file required") return nil, fmt.Errorf("output directory required")
} }
maxDepth, err := parseIntDefault(m.maxDepth.Value(), 20) maxDepth, err := parseIntDefault(m.maxDepth.Value(), 20)
@@ -442,7 +442,7 @@ func (m FormModel) ToConfig() (*config.Config, error) {
cfg := &config.Config{ cfg := &config.Config{
InputPath: inputDir, InputPath: inputDir,
OutputPath: outputFile, OutputPath: outputDir,
AppendMode: m.appendMode.Checked, AppendMode: m.appendMode.Checked,
Force: m.force.Checked, Force: m.force.Checked,
MaxSize: int64(maxSizeKB) * 1024, MaxSize: int64(maxSizeKB) * 1024,
+14 -8
View File
@@ -27,7 +27,7 @@ func Generate(cfg *config.Config) GenerateResult {
} }
cfg.AbsRoot = abs cfg.AbsRoot = abs
var outputBase string var outputDir string
if cfg.AppendMode || cfg.SingleFile { if cfg.AppendMode || cfg.SingleFile {
absOutput, err := filepath.Abs(cfg.OutputPath) absOutput, err := filepath.Abs(cfg.OutputPath)
@@ -49,18 +49,17 @@ func Generate(cfg *config.Config) GenerateResult {
return result return result
} }
outputBase = absOutput outputDir = absOutput
} else { } else {
absOutput, err := filepath.Abs(cfg.OutputPath) absOutput, err := filepath.Abs(cfg.OutputPath)
if err != nil { if err != nil {
result.Error = fmt.Errorf("resolve output path: %w", err) result.Error = fmt.Errorf("resolve output path: %w", err)
return result return result
} }
absOutput = filepath.Join(filepath.Dir(absOutput), filepath.Base(absOutput)+"_dirmd")
if !cfg.AppendMode { if _, err := os.Stat(absOutput); err == nil && !cfg.Force {
if _, err := os.Stat(absOutput); err == nil && !cfg.Force { if info, err2 := os.Stat(absOutput); err2 == nil && !info.IsDir() {
result.Error = fmt.Errorf("output %s exists (enable force)", absOutput) result.Error = fmt.Errorf("output %s exists and is not a directory (enable force)", absOutput)
return result return result
} }
} }
@@ -71,7 +70,7 @@ func Generate(cfg *config.Config) GenerateResult {
} }
cfg.OutputPath = absOutput cfg.OutputPath = absOutput
outputBase = absOutput outputDir = absOutput
} }
walkResult, err := walker.Run(cfg) walkResult, err := walker.Run(cfg)
@@ -96,6 +95,10 @@ func Generate(cfg *config.Config) GenerateResult {
result.Files = generated result.Files = generated
} else { } else {
indexPath := cfg.OutputPath indexPath := cfg.OutputPath
if !cfg.AppendMode && !cfg.SingleFile {
indexPath = filepath.Join(outputDir, "index_"+repoName+".md")
}
if err := renderer.Write(walkResult, indexPath, cfg.AppendMode, cfg.SingleFile); err != nil { if err := renderer.Write(walkResult, indexPath, cfg.AppendMode, cfg.SingleFile); err != nil {
result.Error = err result.Error = err
return result return result
@@ -106,7 +109,7 @@ func Generate(cfg *config.Config) GenerateResult {
if cfg.ProtonDrive { if cfg.ProtonDrive {
remoteDir := cfg.DrivePath remoteDir := cfg.DrivePath
if remoteDir == "" { if remoteDir == "" {
remoteDir = "/my-files/md/" + filepath.Base(outputBase) remoteDir = "/my-files/md/" + filepath.Base(outputDir)
} }
if err := drive.CheckAuth(); err != nil { if err := drive.CheckAuth(); err != nil {
@@ -121,6 +124,9 @@ func Generate(cfg *config.Config) GenerateResult {
} }
} else { } else {
indexPath := cfg.OutputPath indexPath := cfg.OutputPath
if !cfg.AppendMode && !cfg.SingleFile {
indexPath = filepath.Join(outputDir, "index_"+repoName+".md")
}
if err := drive.Upload(indexPath, remoteDir); err != nil { if err := drive.Upload(indexPath, remoteDir); err != nil {
result.Error = fmt.Errorf("drive upload: %w", err) result.Error = fmt.Errorf("drive upload: %w", err)
return result return result
+68
View File
@@ -7,6 +7,8 @@ import (
"charm.land/bubbles/v2/textinput" "charm.land/bubbles/v2/textinput"
tea "charm.land/bubbletea/v2" tea "charm.land/bubbletea/v2"
"charm.land/lipgloss/v2" "charm.land/lipgloss/v2"
"dirmd/internal/config"
) )
type State int type State int
@@ -39,6 +41,7 @@ type Model struct {
isDark bool isDark bool
width int width int
height int height int
command string
} }
func New() Model { func New() Model {
@@ -79,6 +82,7 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
m.form.SetError(err.Error()) m.form.SetError(err.Error())
return m, nil return m, nil
} }
m.command = buildCommand(cfg)
m.state = StateGenerating m.state = StateGenerating
return m, func() tea.Msg { return m, func() tea.Msg {
return genResultMsg{result: Generate(cfg)} return genResultMsg{result: Generate(cfg)}
@@ -114,6 +118,70 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
return m, nil return m, nil
} }
func buildCommand(cfg *config.Config) string {
var parts []string
parts = append(parts, "dirmd")
parts = append(parts, "-o", cfg.OutputPath)
if cfg.AppendMode {
parts = append(parts, "-a")
}
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", fmt.Sprintf("%d", cfg.MaxDepth))
}
if cfg.MaxSize != 512*1024 {
parts = append(parts, "--max-size", fmt.Sprintf("%d", cfg.MaxSize))
}
extraIgnores := extraIgnores(cfg)
for _, ig := range extraIgnores {
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
}
func (m Model) View() tea.View { func (m Model) View() tea.View {
var content string var content string
+14 -1
View File
@@ -2,15 +2,28 @@ package tui
import ( import (
"fmt" "fmt"
"os"
tea "charm.land/bubbletea/v2" tea "charm.land/bubbletea/v2"
) )
func Run() error { func Run() error {
p := tea.NewProgram(New()) p := tea.NewProgram(New())
_, err := p.Run() m, err := p.Run()
if err != nil { if err != nil {
return fmt.Errorf("tui error: %w", err) return fmt.Errorf("tui error: %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)
}
}
}
return nil return nil
} }