Files
2026-07-13 13:12:56 +08:00

269 lines
7.2 KiB
Go

package main
import (
"errors"
"fmt"
"regexp"
"strconv"
"strings"
"github.com/alecthomas/chroma/v2/styles"
"github.com/charmbracelet/huh"
"github.com/charmbracelet/lipgloss"
)
var green = lipgloss.Color("#03BF87")
func runForm(config *Config) (*Config, error) {
var (
padding = strings.Trim(fmt.Sprintf("%v", config.Padding), "[]")
margin = strings.Trim(fmt.Sprintf("%v", config.Margin), "[]")
fontSize = fmt.Sprintf("%d", int(config.Font.Size))
lineHeight = fmt.Sprintf("%.1f", config.LineHeight)
borderRadius = fmt.Sprintf("%.0f", config.Border.Radius)
borderWidth = fmt.Sprintf("%.0f", config.Border.Width)
shadowBlur = fmt.Sprintf("%.0f", config.Shadow.Blur)
shadowX = fmt.Sprintf("%.0f", config.Shadow.X)
shadowY = fmt.Sprintf("%.0f", config.Shadow.Y)
)
theme := huh.ThemeCharm()
theme.FieldSeparator = lipgloss.NewStyle()
theme.Blurred.TextInput.Text = theme.Blurred.TextInput.Text.Foreground(lipgloss.Color("243"))
theme.Blurred.BlurredButton = lipgloss.NewStyle().Foreground(lipgloss.Color("8")).PaddingRight(1)
theme.Blurred.FocusedButton = lipgloss.NewStyle().Foreground(lipgloss.Color("7")).PaddingRight(1)
theme.Focused.BlurredButton = lipgloss.NewStyle().Foreground(lipgloss.Color("8")).PaddingRight(1)
theme.Focused.FocusedButton = lipgloss.NewStyle().Foreground(lipgloss.Color("15")).PaddingRight(1)
theme.Focused.NoteTitle = theme.Focused.NoteTitle.Margin(1, 0)
theme.Blurred.NoteTitle = theme.Blurred.NoteTitle.Margin(1, 0)
theme.Blurred.Description = theme.Blurred.Description.Foreground(lipgloss.Color("0"))
theme.Focused.Description = theme.Focused.Description.Foreground(lipgloss.Color("7"))
theme.Blurred.Title = theme.Blurred.Title.Width(18).Foreground(lipgloss.Color("7"))
theme.Focused.Title = theme.Focused.Title.Width(18).Foreground(green).Bold(true)
theme.Blurred.SelectedOption = theme.Blurred.SelectedOption.Foreground(lipgloss.Color("243"))
theme.Focused.SelectedOption = lipgloss.NewStyle().Foreground(green)
theme.Focused.Base.BorderForeground(green)
f := huh.NewForm(
huh.NewGroup(
huh.NewNote().Title("\nCapture file"),
huh.NewFilePicker().
Title("").
Picking(true).
Height(10).
Value(&config.Input),
huh.NewNote().Description("Choose a code file to screenshot."),
).WithHide(config.Input != "" || config.Execute != ""),
huh.NewGroup(
huh.NewNote().Title("Settings"),
huh.NewInput().
Title("Output").
Placeholder(defaultOutputFilename).
// Description("Output location for image.").
Inline(true).
Prompt("").
Value(&config.Output),
huh.NewSelect[string]().Title("Theme ").
// Description("Theme for syntax highlighting.").
Inline(true).
Options(huh.NewOptions(styles.Names()...)...).
Value(&config.Theme),
// huh.NewInput().Title("Background ").
// // Description("Apply a background fill.").
// Placeholder("#FFF").
// Value(&config.Background).
// Inline(true).
// Prompt("").
// Validate(validateColor),
huh.NewNote().Title("Window"),
huh.NewInput().Title("Padding ").
// Description("Apply padding to the code.").
Placeholder("20 40").
Inline(true).
Value(&padding).
Prompt("").
Validate(validatePadding),
huh.NewInput().Title("Margin ").
// Description("Apply margin to the window.").
Placeholder("20").
Inline(true).
Value(&margin).
Prompt("").
Validate(validatePadding),
huh.NewConfirm().Title("Controls").
Inline(true).
Value(&config.Window),
huh.NewNote().Title("Font"),
huh.NewInput().Title("Font Family ").
// Description("Font family to use for code").
Placeholder("JetBrains Mono").
Inline(true).
Prompt("").
Value(&config.Font.Family),
huh.NewInput().Title("Font Size ").
// Description("Font size to use for code.").
Placeholder("14").
Inline(true).
Prompt("").
Value(&fontSize).
Validate(validateInteger),
huh.NewInput().Title("Line Height ").
// Description("Line height relative to size.").
Placeholder("1.2").
Inline(true).
Prompt("").
Value(&lineHeight).
Validate(validateFloat),
huh.NewNote().Title("Border"),
huh.NewInput().Title("Border Radius ").
// Description("Corner radius of the window.").
Placeholder("0").
Inline(true).
Prompt("").
Value(&borderRadius).
Validate(validateInteger),
huh.NewInput().Title("Border Width ").
// Description("Border width thickness.").
Placeholder("1").
Inline(true).
Prompt("").
Value(&borderWidth).
Validate(validateInteger),
huh.NewInput().Title("Border Color ").
// Description("Color of outline stroke.").
Validate(validateColor).
Inline(true).
Prompt("").
Value(&config.Border.Color).
Placeholder("#515151"),
huh.NewNote().Title("Shadow"),
huh.NewInput().Title("Blur ").
// Description("Shadow Gaussian Blur.").
Placeholder("0").
Inline(true).
Prompt("").
Value(&shadowBlur).
Validate(validateInteger),
huh.NewInput().Title("X Offset ").
// Description("Shadow offset x coordinate").
Placeholder("0").
Inline(true).
Prompt("").
Value(&shadowX).
Validate(validateInteger),
huh.NewInput().Title("Y Offset ").
// Description("Shadow offset y coordinate").
Placeholder("0").
Inline(true).
Prompt("").
Value(&shadowY).
Validate(validateInteger),
).WithHeight(33),
).WithTheme(theme).WithWidth(40)
err := f.Run()
if config.Output == "" {
config.Output = defaultOutputFilename
}
config.Padding = parsePadding(padding)
config.Margin = parseMargin(margin)
config.Font.Size, _ = strconv.ParseFloat(fontSize, 64)
config.LineHeight, _ = strconv.ParseFloat(lineHeight, 64)
config.Border.Radius, _ = strconv.ParseFloat(borderRadius, 64)
config.Border.Width, _ = strconv.ParseFloat(borderWidth, 64)
config.Shadow.Blur, _ = strconv.ParseFloat(shadowBlur, 64)
config.Shadow.X, _ = strconv.ParseFloat(shadowX, 64)
config.Shadow.Y, _ = strconv.ParseFloat(shadowY, 64)
return config, err //nolint: wrapcheck
}
func validateMargin(s string) error {
tokens := strings.Fields(s)
if len(tokens) > 4 {
return errors.New("maximum four values")
}
for _, t := range tokens {
_, err := strconv.Atoi(t)
if err != nil {
return errors.New("must be valid space-separated integers")
}
}
return nil
}
func validatePadding(s string) error {
return validateMargin(s)
}
func validateInteger(s string) error {
if len(s) <= 0 {
return nil
}
_, err := strconv.Atoi(s)
if err != nil {
return errors.New("must be valid integer")
}
return nil
}
func validateFloat(s string) error {
if len(s) <= 0 {
return nil
}
_, err := strconv.ParseFloat(s, 64)
if err != nil {
return errors.New("must be valid float")
}
return nil
}
var colorRegex = regexp.MustCompile("^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$")
func validateColor(s string) error {
if len(s) <= 0 {
return nil
}
if !colorRegex.MatchString(s) {
return errors.New("must be valid color")
}
return nil
}
func parsePadding(v string) []float64 {
//nolint: prealloc
var values []float64
for _, p := range strings.Fields(v) {
pi, _ := strconv.ParseFloat(p, 64) // already validated
values = append(values, pi)
}
return expandPadding(values, 1)
}
var parseMargin = parsePadding