Compare commits

..

No commits in common. "4c4a3f6bb065f2bd062b66963e5a45922558c17a" and "7bcf8e8301a131fe91f6f8a31dd82c2651b6d57d" have entirely different histories.

5 changed files with 74 additions and 196 deletions

View File

@ -1,11 +0,0 @@
package tui
import "termtap.dev/internal/model"
type EventMsg struct {
value model.Event
}
type ErrMsg struct {
err error
}

View File

@ -4,55 +4,58 @@ import (
"fmt" "fmt"
tea "github.com/charmbracelet/bubbletea" tea "github.com/charmbracelet/bubbletea"
"github.com/google/uuid"
"termtap.dev/internal/model" "termtap.dev/internal/model"
) )
// TODO: How big can we actually make this?
const ( const (
maxEvents = 256 maxEvents = 200
maxRequests = 256 maxRequests = 200
) )
type Model struct { type EventMsg struct {
channel <-chan model.Event value model.Event
}
events []model.Event type ErrMsg struct {
requests []model.Request err error
}
type Model struct {
msgCh <-chan model.Event
events []model.Event
requestOrder []uuid.UUID
requests map[uuid.UUID]model.Request
width int width int
height int height int
showEvents bool
showStd bool
showSearch bool
} }
func NewModel(ch <-chan model.Event) Model { func NewModel(msgCh <-chan model.Event) Model {
return Model{ return Model{
channel: ch, msgCh: msgCh,
events: make([]model.Event, 0, maxEvents), events: make([]model.Event, 0, maxEvents),
requests: make([]model.Request, 0, maxRequests), requestOrder: make([]uuid.UUID, 0, maxRequests),
width: 0, requests: map[uuid.UUID]model.Request{},
height: 0, width: 100,
showEvents: false, height: 28,
showStd: false,
showSearch: false,
} }
} }
func Run(ch <-chan model.Event) error { func Run(msgCh <-chan model.Event) error {
p := tea.NewProgram(NewModel(ch), tea.WithAltScreen()) p := tea.NewProgram(NewModel(msgCh), tea.WithAltScreen())
_, err := p.Run() _, err := p.Run()
return err return err
} }
func (m Model) Init() tea.Cmd { func (m Model) Init() tea.Cmd {
return waitForEvent(m.channel) return waitForAppMessage(m.msgCh)
} }
func waitForEvent(ch <-chan model.Event) tea.Cmd { func waitForAppMessage(msgCh <-chan model.Event) tea.Cmd {
return func() tea.Msg { return func() tea.Msg {
msg, ok := <-ch msg, ok := <-msgCh
if !ok { if !ok {
return ErrMsg{err: fmt.Errorf("event channel closed")} return ErrMsg{err: fmt.Errorf("event channel closed")}
} }

View File

@ -1,110 +0,0 @@
package tui
import "strings"
func (m Model) renderAppPane() string {
var (
searchW int = m.width
searchH int = 1
reqW int = int(float64(m.width) * 0.6)
reqH int = m.height
detW int = int(float64(m.width) * 0.4)
detH int = m.height
eventW int = m.width
eventH int = int(float64(m.height) * 0.15)
stdW int = m.width
stdH int = int(float64(m.height) * 0.2)
)
if m.showSearch {
reqH -= searchH
detH -= searchH
}
if m.showEvents {
reqH -= eventH
detH -= eventH
}
if m.showStd {
reqH -= stdH
detH -= stdH
}
reqPane := m.renderRequestPane(reqW, reqH)
detPane := m.renderDetailsPane(detW, detH)
if len(reqPane) != len(detPane) {
return "height of request and details did not match"
}
var screen []string
if m.showSearch {
searchPane := m.renderSearchPane(searchW, searchH)
screen = append(screen, searchPane...)
}
for i := range reqPane {
screen = append(screen, reqPane[i]+detPane[i])
}
if m.showEvents {
eventPane := m.renderEventsPane(eventW, eventH)
screen = append(screen, eventPane...)
}
if m.showStd {
stdPane := m.renderStdPane(stdW, stdH)
screen = append(screen, stdPane...)
}
if len(screen) != m.height {
return "height of screen does not match terminal height"
}
return strings.Join(screen, ("\n"))
}
func (m Model) renderSearchPane(w, h int) []string {
lines := make([]string, h)
for y := range lines {
lines[y] = strings.Repeat(" ", w)
}
return lines
}
func (m Model) renderRequestPane(w, h int) []string {
lines := make([]string, h)
for y := range lines {
lines[y] = strings.Repeat(".", w)
}
return lines
}
func (m Model) renderDetailsPane(w, h int) []string {
lines := make([]string, h)
for y := range lines {
lines[y] = strings.Repeat("^", w)
}
return lines
}
func (m Model) renderEventsPane(w, h int) []string {
lines := make([]string, h)
for y := range lines {
lines[y] = strings.Repeat("~", w)
}
return lines
}
func (m Model) renderStdPane(w, h int) []string {
lines := make([]string, h)
for y := range lines {
lines[y] = strings.Repeat(" ", w)
}
return lines
}

View File

@ -4,6 +4,7 @@ import (
"fmt" "fmt"
tea "github.com/charmbracelet/bubbletea" tea "github.com/charmbracelet/bubbletea"
"github.com/google/uuid"
"termtap.dev/internal/model" "termtap.dev/internal/model"
) )
@ -14,19 +15,10 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
m.height = msg.Height m.height = msg.Height
return m, nil return m, nil
// TODO: Abstract the keymaps
case tea.KeyMsg: case tea.KeyMsg:
switch msg.String() { switch msg.String() {
case "ctrl+c", "q": case "ctrl+c", "q", "esc":
return m, tea.Quit return m, tea.Quit
case "e":
m.showEvents = !m.showEvents
case "o":
m.showStd = !m.showStd
case "/":
m.showSearch = true
case "esc":
m.showSearch = false
} }
return m, nil return m, nil
@ -40,7 +32,7 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
case EventMsg: case EventMsg:
m.pushEvent(msg.value) m.pushEvent(msg.value)
m.applyMessage(msg.value) m.applyMessage(msg.value)
return m, waitForEvent(m.channel) return m, waitForAppMessage(m.msgCh)
} }
return m, nil return m, nil
@ -56,29 +48,30 @@ func (m *Model) pushEvent(msg model.Event) {
func (m *Model) applyMessage(msg model.Event) { func (m *Model) applyMessage(msg model.Event) {
switch msg.Type { switch msg.Type {
case model.EventTypeRequestStarted: case model.EventTypeRequestStarted:
m.createRequest(msg.Request) m.upsertRequest(msg.Request, true)
case model.EventTypeRequestFinished, model.EventTypeRequestFailed: case model.EventTypeRequestFinished, model.EventTypeRequestFailed:
m.updateRequest(msg.Request) m.upsertRequest(msg.Request, false)
} }
} }
func (m *Model) createRequest(req model.Request) { func (m *Model) upsertRequest(req model.Request, addIfMissing bool) {
m.requests = append(m.requests, req) if req.ID == uuid.Nil {
return
// If we passed the max, delete the first one
// Maybe we should notify the user?
if len(m.requests) > maxRequests {
m.requests = m.requests[1:]
} }
}
func (m *Model) updateRequest(req model.Request) { _, exists := m.requests[req.ID]
// Traverse backward, since the newest one is at the end, and its likely we will be if !exists && !addIfMissing {
// updated a new request. return
for i := len(m.requests) - 1; i >= 0; i-- { }
if m.requests[i].ID == req.ID {
m.requests[i] = req if !exists {
break m.requestOrder = append(m.requestOrder, req.ID)
if len(m.requestOrder) > maxRequests {
drop := m.requestOrder[0]
delete(m.requests, drop)
m.requestOrder = m.requestOrder[1:]
} }
} }
m.requests[req.ID] = req
} }

View File

@ -9,23 +9,20 @@ import (
// TODO: This is all temporary // TODO: This is all temporary
func (m Model) View() string { func (m Model) View() string {
return m.renderAppPane() eventLines := m.renderEvents(8)
requestLines := m.renderRequests(12)
// eventLines := m.renderEvents(8) return strings.Join([]string{
// requestLines := m.renderRequests(12) "termtap - live session",
// fmt.Sprintf("events=%d requests=%d", len(m.events), len(m.requestOrder)),
// return strings.Join([]string{ "keys: q/esc/ctrl+c quit",
// "termtap - live session", "",
// fmt.Sprintf("events=%d requests=%d", len(m.events), len(m.requests)), "Recent events:",
// fmt.Sprintf("%dx%d", m.height, m.width), eventLines,
// "keys: q/esc/ctrl+c quit", "",
// "", "Recent requests:",
// "Recent events:", requestLines,
// eventLines, }, "\n")
// "",
// "Recent requests:",
// requestLines,
// }, "\n")
} }
func (m Model) renderEvents(limit int) string { func (m Model) renderEvents(limit int) string {
@ -45,16 +42,22 @@ func (m Model) renderEvents(limit int) string {
} }
func (m Model) renderRequests(limit int) string { func (m Model) renderRequests(limit int) string {
if len(m.requests) == 0 { if len(m.requestOrder) == 0 {
return " (none yet)" return " (none yet)"
} }
start := max(0, len(m.requests)-limit) start := len(m.requestOrder) - limit
if start < 0 {
start = 0
}
// Traverse backwards since we don't have a stack rows := make([]string, 0, len(m.requestOrder)-start)
rows := make([]string, 0, len(m.requests)-start) for i := start; i < len(m.requestOrder); i++ {
for i := len(m.requests) - 1; i >= start; i-- { id := m.requestOrder[i]
req := m.requests[i] req, ok := m.requests[id]
if !ok {
continue
}
state := "done" state := "done"
if req.Pending { if req.Pending {