Add more validation
This commit is contained in:
parent
9d35e65c07
commit
71aa5ed835
105
main.go
105
main.go
@ -6,6 +6,7 @@ import (
|
|||||||
"os/exec"
|
"os/exec"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/charmbracelet/bubbles/progress"
|
"github.com/charmbracelet/bubbles/progress"
|
||||||
@ -44,6 +45,7 @@ type model struct {
|
|||||||
fullscreen bool
|
fullscreen bool
|
||||||
width int
|
width int
|
||||||
height int
|
height int
|
||||||
|
err error
|
||||||
}
|
}
|
||||||
|
|
||||||
func initialModel(fullscreen bool, colorLeft string, colorRight string) model {
|
func initialModel(fullscreen bool, colorLeft string, colorRight string) model {
|
||||||
@ -53,14 +55,28 @@ func initialModel(fullscreen bool, colorLeft string, colorRight string) model {
|
|||||||
for i := range inputs {
|
for i := range inputs {
|
||||||
inputs[i] = textinput.New()
|
inputs[i] = textinput.New()
|
||||||
inputs[i].CharLimit = 10 // Increase char limit to allow duration strings
|
inputs[i].CharLimit = 10 // Increase char limit to allow duration strings
|
||||||
|
|
||||||
if i == 0 {
|
if i == 0 {
|
||||||
inputs[i].Focus() // Start focus on first input
|
inputs[i].Focus() // Start focus on first input
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
validateDuration := func(text string) error {
|
||||||
|
_, err := parseDuration(text)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
validateInt := func(text string) error {
|
||||||
|
_, err := strconv.Atoi(text)
|
||||||
|
return fmt.Errorf("invalid int input: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
inputs[0].Placeholder = "Interval length (minutes or duration)"
|
inputs[0].Placeholder = "Interval length (minutes or duration)"
|
||||||
|
inputs[0].Validate = validateDuration
|
||||||
inputs[1].Placeholder = "Break length (minutes or duration)"
|
inputs[1].Placeholder = "Break length (minutes or duration)"
|
||||||
|
inputs[1].Validate = validateDuration
|
||||||
inputs[2].Placeholder = "Number of intervals"
|
inputs[2].Placeholder = "Number of intervals"
|
||||||
|
inputs[2].Validate = validateInt
|
||||||
|
|
||||||
return model{
|
return model{
|
||||||
inputs: inputs,
|
inputs: inputs,
|
||||||
@ -79,6 +95,7 @@ func (m model) Init() tea.Cmd {
|
|||||||
// Handle Ctrl+C for graceful exit
|
// Handle Ctrl+C for graceful exit
|
||||||
c := make(chan os.Signal, 1)
|
c := make(chan os.Signal, 1)
|
||||||
signal.Notify(c, os.Interrupt)
|
signal.Notify(c, os.Interrupt)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
<-c
|
<-c
|
||||||
fmt.Println("\nExiting...")
|
fmt.Println("\nExiting...")
|
||||||
@ -114,15 +131,22 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||||||
if m.focusIndex == len(m.inputs)-1 {
|
if m.focusIndex == len(m.inputs)-1 {
|
||||||
focusTime, err := parseDuration(m.inputs[0].Value())
|
focusTime, err := parseDuration(m.inputs[0].Value())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
focusTime, _ = time.ParseDuration(m.inputs[0].Value() + "m")
|
m.err = fmt.Errorf("error parsing focus time duration: %w", err)
|
||||||
|
return m, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
breakTime, err := parseDuration(m.inputs[1].Value())
|
breakTime, err := parseDuration(m.inputs[1].Value())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
breakTime, _ = time.ParseDuration(m.inputs[1].Value() + "m")
|
m.err = fmt.Errorf("error parsing break time duration: %w", err)
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
m.intervals, err = strconv.Atoi(m.inputs[2].Value())
|
||||||
|
if err != nil {
|
||||||
|
m.err = fmt.Errorf("error parsing interval: %w", err)
|
||||||
|
return m, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
m.intervals = atoi(m.inputs[2].Value())
|
|
||||||
m.focusTime = focusTime
|
m.focusTime = focusTime
|
||||||
m.breakTime = breakTime
|
m.breakTime = breakTime
|
||||||
m.remaining = focusTime
|
m.remaining = focusTime
|
||||||
@ -203,24 +227,41 @@ func tick() tea.Cmd {
|
|||||||
|
|
||||||
// View rendering for input and timer screens
|
// View rendering for input and timer screens
|
||||||
func (m model) View() string {
|
func (m model) View() string {
|
||||||
|
var s strings.Builder
|
||||||
|
|
||||||
switch m.currentScreen {
|
switch m.currentScreen {
|
||||||
case inputScreen:
|
case inputScreen:
|
||||||
return m.inputScreenView()
|
s.WriteString(m.inputScreenView())
|
||||||
case timerScreen:
|
case timerScreen:
|
||||||
return m.timerScreenView()
|
s.WriteString(m.timerScreenView())
|
||||||
}
|
}
|
||||||
return ""
|
|
||||||
|
if m.err != nil {
|
||||||
|
s.WriteString(fmt.Sprintf("\n\nError: %v", m.err))
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
// View for input screen
|
// View for input screen
|
||||||
func (m model) inputScreenView() string {
|
func (m model) inputScreenView() string {
|
||||||
var builder string
|
var builder strings.Builder
|
||||||
builder = "Enter your Pomodoro settings:\n\n"
|
|
||||||
|
builder.WriteString("Enter your Pomodoro settings:\n\n")
|
||||||
|
|
||||||
for i := range m.inputs {
|
for i := range m.inputs {
|
||||||
builder += m.inputs[i].View() + "\n"
|
builder.WriteString(m.inputs[i].View())
|
||||||
|
|
||||||
|
if m.inputs[i].Value() != "" && m.inputs[i].Err != nil {
|
||||||
|
builder.WriteString(fmt.Sprintf("Error: %v", m.inputs[i].Err))
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.WriteString("\n")
|
||||||
}
|
}
|
||||||
builder += "\nUse TAB to navigate, ENTER to start."
|
|
||||||
return builder
|
builder.WriteString("\nUse TAB to navigate, ENTER to start.")
|
||||||
|
|
||||||
|
return builder.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
// View for timer screen with optional fullscreen centering
|
// View for timer screen with optional fullscreen centering
|
||||||
@ -236,6 +277,7 @@ func (m model) timerScreenView() string {
|
|||||||
if m.fullscreen {
|
if m.fullscreen {
|
||||||
return lipgloss.NewStyle().Width(m.width).Height(m.height).Align(lipgloss.Center, lipgloss.Center).Render(timerView)
|
return lipgloss.NewStyle().Width(m.width).Height(m.height).Align(lipgloss.Center, lipgloss.Center).Render(timerView)
|
||||||
}
|
}
|
||||||
|
|
||||||
return timerView
|
return timerView
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -256,6 +298,7 @@ func (m *model) runCommands(commands []string) {
|
|||||||
cmd := exec.Command("sh", "-c", cmdStr)
|
cmd := exec.Command("sh", "-c", cmdStr)
|
||||||
cmd.Stdout = os.Stdout
|
cmd.Stdout = os.Stdout
|
||||||
cmd.Stderr = os.Stderr
|
cmd.Stderr = os.Stderr
|
||||||
|
|
||||||
if err := cmd.Run(); err != nil {
|
if err := cmd.Run(); err != nil {
|
||||||
fmt.Printf("Error running command: %v\n", err)
|
fmt.Printf("Error running command: %v\n", err)
|
||||||
}
|
}
|
||||||
@ -267,13 +310,13 @@ func parseDuration(input string) (time.Duration, error) {
|
|||||||
if minutes, err := strconv.Atoi(input); err == nil {
|
if minutes, err := strconv.Atoi(input); err == nil {
|
||||||
return time.Duration(minutes) * time.Minute, nil
|
return time.Duration(minutes) * time.Minute, nil
|
||||||
}
|
}
|
||||||
return time.ParseDuration(input)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helper to convert string to int
|
d, err := time.ParseDuration(input)
|
||||||
func atoi(s string) int {
|
if err != nil {
|
||||||
n, _ := strconv.Atoi(s)
|
return d, fmt.Errorf("error parsing duration: %w", err)
|
||||||
return n
|
}
|
||||||
|
|
||||||
|
return d, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
@ -299,26 +342,29 @@ func main() {
|
|||||||
Usage: "Enable fullscreen mode",
|
Usage: "Enable fullscreen mode",
|
||||||
},
|
},
|
||||||
&cli.DurationFlag{
|
&cli.DurationFlag{
|
||||||
Name: "focus",
|
Name: "focus",
|
||||||
Usage: "Focus time duration (default prompt for input)",
|
Usage: "Focus time duration",
|
||||||
|
DefaultText: "prompt for input",
|
||||||
},
|
},
|
||||||
&cli.DurationFlag{
|
&cli.DurationFlag{
|
||||||
Name: "break",
|
Name: "break",
|
||||||
Usage: "Break time duration (default prompt for input)",
|
Usage: "Break time duration",
|
||||||
|
DefaultText: "prompt for input",
|
||||||
},
|
},
|
||||||
&cli.IntFlag{
|
&cli.IntFlag{
|
||||||
Name: "intervals",
|
Name: "intervals",
|
||||||
Usage: "Number of intervals (default prompt for input)",
|
Usage: "Number of intervals",
|
||||||
|
DefaultText: "prompt for input",
|
||||||
},
|
},
|
||||||
&cli.StringFlag{
|
&cli.StringFlag{
|
||||||
Name: "color-left",
|
Name: "color-left",
|
||||||
Usage: "Left color for progress bar",
|
Usage: "Left color for progress bar",
|
||||||
DefaultText: "#ffdd57",
|
Value: "#ffdd57",
|
||||||
},
|
},
|
||||||
&cli.StringFlag{
|
&cli.StringFlag{
|
||||||
Name: "color-right",
|
Name: "color-right",
|
||||||
Usage: "Right color for progress bar",
|
Usage: "Right color for progress bar",
|
||||||
DefaultText: "#57ddff",
|
Value: "#57ddff",
|
||||||
},
|
},
|
||||||
&cli.BoolFlag{
|
&cli.BoolFlag{
|
||||||
Name: "version",
|
Name: "version",
|
||||||
@ -352,6 +398,7 @@ func main() {
|
|||||||
// Start tea program
|
// Start tea program
|
||||||
p := tea.NewProgram(m, tea.WithAltScreen())
|
p := tea.NewProgram(m, tea.WithAltScreen())
|
||||||
_, err := p.Run()
|
_, err := p.Run()
|
||||||
|
|
||||||
return err
|
return err
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user