summaryrefslogtreecommitdiff
path: root/tui.go
diff options
context:
space:
mode:
Diffstat (limited to 'tui.go')
-rw-r--r--tui.go161
1 files changed, 129 insertions, 32 deletions
diff --git a/tui.go b/tui.go
index c131110..48bc2d9 100644
--- a/tui.go
+++ b/tui.go
@@ -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
}