diff options
| author | tjp <tjp@ctrl-c.club> | 2024-02-16 09:44:52 -0700 |
|---|---|---|
| committer | tjp <tjp@ctrl-c.club> | 2024-02-16 09:44:52 -0700 |
| commit | b787ef92df3f859b033aa8cded6b1f6dbc7c2be0 (patch) | |
| tree | e60f8e7d4010559f0579951a993f19240a8f84da /tui.go | |
| parent | 865c6dc23099e129cb3ba9709b2926734144e605 (diff) | |
lipgloss styles
Diffstat (limited to 'tui.go')
| -rw-r--r-- | tui.go | 161 |
1 files changed, 129 insertions, 32 deletions
@@ -1,80 +1,177 @@ package main import ( + "fmt" "os" + "strings" - tea "github.com/charmbracelet/bubbletea" + "github.com/charmbracelet/bubbles/textinput" "github.com/charmbracelet/bubbles/viewport" + tea "github.com/charmbracelet/bubbletea" + "github.com/charmbracelet/lipgloss" ) -type TUIModel struct { - State *BrowserState +func runTUI(state *BrowserState, args []string) { + model := NewMainModel(state) + state.Printer = (*TUIPrinter)(model) + + if len(args) > 0 { + if err := Go(state, args[0]); err != nil { + state.Printer.PrintError(err.Error()) + } + } + + p := tea.NewProgram(model, tea.WithAltScreen()) + if _, err := p.Run(); err != nil { + state.Printer.PrintError(err.Error()) + os.Exit(1) + } +} + +var ( + hdrStyle = lipgloss.NewStyle().Background(lipgloss.Color("56")).Bold(true) + ftrStyle = lipgloss.NewStyle().Background(lipgloss.Color("56")) + errStyle = lipgloss.NewStyle().Background(lipgloss.Color("196")).Bold(true) +) + +type MainModel struct { + State *BrowserState Viewport viewport.Model - inited bool + Prompt *textinput.Model + ErrorMsg string } -func NewTUIModel(state *BrowserState) *TUIModel { - return &TUIModel{State: state} +func NewMainModel(state *BrowserState) *MainModel { + return &MainModel{ + State: state, + Viewport: viewport.Model{ + HighPerformanceRendering: true, + YPosition: 2, + }, + } } -func (model *TUIModel) Init() tea.Cmd { +func (model *MainModel) Init() tea.Cmd { return nil } -func (model *TUIModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { +func (model *MainModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { + if model.Prompt != nil { + return model.updatePrompt(msg) + } + + m := model + var cmds []tea.Cmd + switch msg := msg.(type) { case tea.KeyMsg: + model.ErrorMsg = "" + switch msg.String() { case "ctrl+c", "q": - return model, tea.Quit + cmds = append(cmds, tea.Quit) + case "ctrl+l": + cmds = append(cmds, viewport.Sync(model.Viewport)) case "g": - model.Viewport.GotoTop() - return model, nil + lines := model.Viewport.GotoTop() + cmds = append(cmds, viewport.ViewUp(model.Viewport, lines)) case "G": - model.Viewport.GotoBottom() - return model, nil + lines := model.Viewport.GotoBottom() + cmds = append(cmds, viewport.ViewDown(model.Viewport, lines)) + case ":": + p := textinput.New() + model.Prompt = &p + cmds = append(cmds, p.Focus()) } case tea.WindowSizeMsg: - model.inited = true model.Viewport.Width = msg.Width - model.Viewport.Height = msg.Height - 1 + model.Viewport.Height = msg.Height - 2 + hdrStyle = hdrStyle.Width(msg.Width) + cmds = append(cmds, viewport.Sync(model.Viewport)) } var cmd tea.Cmd - model.Viewport, cmd = model.Viewport.Update(msg) + vp, cmd := model.Viewport.Update(msg) + model.Viewport = vp + cmds = append(cmds, cmd) + return m, tea.Batch(cmds...) +} + +func (model *MainModel) updatePrompt(msg tea.Msg) (tea.Model, tea.Cmd) { + if keymsg, ok := msg.(tea.KeyMsg); ok { + model.ErrorMsg = "" + + switch keymsg.String() { + case "enter": + cmd, err := ParseCommand(model.Prompt.Value()) + model.Prompt = nil + if err != nil { + model.State.Printer.PrintError(err.Error()) + return model, nil + } + if err := RunCommand(cmd, model.State); err != nil { + model.State.Printer.PrintError(err.Error()) + return model, nil + } + return model, viewport.Sync(model.Viewport) + } + } + + p, cmd := model.Prompt.Update(msg) + model.Prompt = &p return model, cmd } -func (model *TUIModel) View() string { - return model.Viewport.View() +func (model *MainModel) View() string { + return model.viewHeader() + "\n" + model.Viewport.View() + "\n" + model.viewFooter() } -func runTUI(state *BrowserState, args []string) { - model := NewTUIModel(state) - state.Printer = (*TUIPrinter)(model) +func (model *MainModel) viewHeader() string { + hdrLine := " Welcome to X-1" + if model.State.Url != nil { + hdrLine = " " + model.State.Url.String() + } + return hdrStyle.Render(hdrLine) +} - if len(args) > 0 { - if err := Go(state, args[0]); err != nil { - writeError(err.Error()) - } +func (model *MainModel) viewFooter() string { + if model.ErrorMsg != "" { + return errStyle.Render(model.ErrorMsg) } - p := tea.NewProgram(model, tea.WithAltScreen()) - if _, err := p.Run(); err != nil { - writeError(err.Error()) - os.Exit(1) + var footerLine string + if model.Prompt != nil { + footerLine = ftrStyle.Width(hdrStyle.GetWidth()).Render(model.Prompt.View()) + } else { + footerLine = ftrStyle.Render(" ") + ftrStyle.Copy().Italic(true).Render(model.State.DocType) + pct := ftrStyle.Render(fmt.Sprintf("%3.f%% ", model.Viewport.ScrollPercent()*100)) + footerLine += ftrStyle.Render(strings.Repeat(" ", max(0, model.Viewport.Width-lipgloss.Width(footerLine)-lipgloss.Width(pct)))) + footerLine += pct } + return footerLine } -type TUIPrinter TUIModel +func max(a, b int) int { + if a < b { + return b + } + return a +} + +type TUIPrinter MainModel func (p *TUIPrinter) PrintModal(state *BrowserState, contents []byte) error { - (*TUIModel)(p).Viewport.SetContent(string(contents)) + (*MainModel)(p).Viewport.SetContent(strings.TrimSuffix(string(contents), "\n")) return nil } func (p *TUIPrinter) PrintPage(state *BrowserState, body string) error { - (*TUIModel)(p).Viewport.SetContent(body) + (*MainModel)(p).Viewport.SetContent(strings.TrimSuffix(body, "\n")) + return nil +} + +func (p *TUIPrinter) PrintError(msg string) error { + (*MainModel)(p).ErrorMsg = msg return nil } |
