Files
watchr/internal/ui/model.go
Chen Asraf 351aa0331b fix: unify all text input behavior, fix cursor navigations
fix: text cursor visual
fix: text delete word
fix: home/end navigation
2026-03-25 01:57:44 +02:00

97 lines
3.4 KiB
Go

package ui
import (
"context"
"time"
tea "github.com/charmbracelet/bubbletea"
"github.com/chenasraf/watchr/internal/runner"
)
// PreviewPosition defines where the preview panel is displayed
type PreviewPosition string
const (
PreviewBottom PreviewPosition = "bottom"
PreviewTop PreviewPosition = "top"
PreviewLeft PreviewPosition = "left"
PreviewRight PreviewPosition = "right"
)
// Config holds the UI configuration
type Config struct {
Command string
Shell string
PreviewSize int
PreviewSizeIsPercent bool
PreviewPosition PreviewPosition
ShowLineNums bool
LineNumWidth int
Prompt string
RefreshInterval time.Duration
RefreshFromStart bool // If true, refresh timer starts when command starts; if false, when command ends (default)
Interactive bool
}
// model represents the application state
type model struct {
config Config
lines []runner.Line
filtered []int // indices into lines that match filter
cursor int // cursor position in filtered list
offset int // scroll offset for visible window
filterInput textInput // filter text and cursor
filterMode bool
filterRegex bool // true when filter is in regex mode
filterRegexErr error // non-nil when regex pattern is invalid
showPreview bool
previewOffset int // scroll offset for preview pane
showHelp bool // help overlay visible
width int
height int
runner *runner.Runner
ctx context.Context
cancel context.CancelFunc
loading bool
streaming bool // true while command is running (streaming output)
streamResult *runner.StreamingResult // current streaming result
lastLineCount int // track line count for updates
userScrolled bool // true if user manually scrolled during streaming
refreshGeneration int // incremented on manual refresh to reset timer
refreshStartTime time.Time // when the refresh timer was started
spinnerFrame int // current spinner animation frame
errorMsg string
statusMsg string // temporary status message (e.g., "Yanked!")
exitCode int // last command exit code
cmdPaletteMode bool // whether command palette is open
cmdPaletteInput textInput // palette filter text and cursor
cmdPaletteSelected int // selected item index in filtered list
confirmMode bool // whether a confirmation dialog is visible
confirmMessage string // message to display in confirmation dialog
confirmAction func(m *model) (tea.Model, tea.Cmd)
}
// messages
type resultMsg struct {
lines []runner.Line
exitCode int
}
type errMsg struct{ err error }
type tickMsg struct {
generation int
}
type clearStatusMsg struct{}
type spinnerTickMsg time.Time
type streamTickMsg time.Time // periodic check for streaming updates
type startStreamMsg struct{} // trigger to start streaming
type countdownTickMsg struct { // periodic update for refresh countdown display
generation int
}
// Spinner frames for the loading animation
var spinnerFrames = []string{"⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"}
func (e errMsg) Error() string { return e.err.Error() }