feat: style changes and event reworks.

Going to work on process restart so I can test those messages.
This commit is contained in:
Hayden Hargreaves 2026-04-17 22:08:35 -07:00
parent b43cab8b0a
commit adcee5f69e
3 changed files with 124 additions and 35 deletions

View File

@ -65,7 +65,7 @@ func proxyHandler(ch chan<- model.Event) http.Handler {
ch <- model.Event{ ch <- model.Event{
Time: time.Now().Local(), Time: time.Now().Local(),
Type: model.EventTypeWarn, Type: model.EventTypeWarn,
Body: fmt.Sprintf("(%s) failed to read request body", request.ID), Body: fmt.Sprintf("(%s) failed to read request body: %v", getEndOfUUID(request.ID), err),
Request: request, Request: request,
} }
} else { } else {
@ -86,7 +86,7 @@ func proxyHandler(ch chan<- model.Event) http.Handler {
ch <- model.Event{ ch <- model.Event{
Time: time.Now().Local(), Time: time.Now().Local(),
Type: model.EventTypeRequestStarted, Type: model.EventTypeRequestStarted,
Body: fmt.Sprintf("-> %+v", request), Body: fmt.Sprintf("(%s) %s %s", getEndOfUUID(request.ID), request.Method, request.RawURL),
Request: request, Request: request,
} }
@ -103,7 +103,7 @@ func proxyHandler(ch chan<- model.Event) http.Handler {
ch <- model.Event{ ch <- model.Event{
Time: time.Now().Local(), Time: time.Now().Local(),
Type: model.EventTypeRequestFailed, Type: model.EventTypeRequestFailed,
Body: fmt.Sprintf("upstream error for %s %s: %v", outReq.Method, outReq.URL.String(), err), Body: fmt.Sprintf("(%s) upstream error: %v", getEndOfUUID(request.ID), err),
Request: request, Request: request,
} }
return return
@ -115,7 +115,7 @@ func proxyHandler(ch chan<- model.Event) http.Handler {
ch <- model.Event{ ch <- model.Event{
Time: time.Now().Local(), Time: time.Now().Local(),
Type: model.EventTypeWarn, Type: model.EventTypeWarn,
Body: fmt.Sprintf("(%s) failed to read response body", request.ID), Body: fmt.Sprintf("(%s) failed to read response body: %v", getEndOfUUID(request.ID), err),
Request: request, Request: request,
} }
} else { } else {
@ -133,7 +133,7 @@ func proxyHandler(ch chan<- model.Event) http.Handler {
ch <- model.Event{ ch <- model.Event{
Time: time.Now().Local(), Time: time.Now().Local(),
Type: model.EventTypeRequestFailed, Type: model.EventTypeRequestFailed,
Body: fmt.Sprintf("write response body %s %s: %v", outReq.Method, outReq.URL.String(), err), Body: fmt.Sprintf("(%s) failed to write response body: %v", getEndOfUUID(request.ID), err),
} }
return return
} }
@ -146,7 +146,7 @@ func proxyHandler(ch chan<- model.Event) http.Handler {
ch <- model.Event{ ch <- model.Event{
Time: time.Now().Local(), Time: time.Now().Local(),
Type: model.EventTypeRequestFinished, Type: model.EventTypeRequestFinished,
Body: fmt.Sprintf("<- %+v %s", request, formatHeaders(resp.Request.Header)), Body: fmt.Sprintf("(%s) %s %s %d %dms", getEndOfUUID(request.ID), request.Method, request.RawURL, request.Status, request.Duration.Milliseconds()),
Request: request, Request: request,
} }
}) })
@ -199,6 +199,10 @@ func formatHeaders(headers http.Header) string {
return strings.Join(parts, ", ") return strings.Join(parts, ", ")
} }
func getEndOfUUID(id uuid.UUID) string {
return id.String()[24:]
}
// BUG: Not sure if this actually works, seems to favor the 502 // BUG: Not sure if this actually works, seems to favor the 502
func statusFromUpstreamError(req *http.Request, resp *http.Response, err error) int { func statusFromUpstreamError(req *http.Request, resp *http.Response, err error) int {
if resp != nil { if resp != nil {

View File

@ -23,7 +23,7 @@ func (m Model) renderStatusBar(w int) string {
avg := int(msSum) / max(1, len(m.requests)) avg := int(msSum) / max(1, len(m.requests))
left := fmt.Sprintf(" tap %3d reqs | %d err | avg %dms", len(m.requests), errCount, avg) left := fmt.Sprintf(" tap %3d reqs | %d err | avg %dms", len(m.requests), errCount, avg)
right := "j/k nav / search tab panel e events o output r replay q quit " right := "j/k nav / search tab panel e events o output r replay ctrl+r restart q quit "
spaceSize := max(w-(len(left)+len(right)), 0) spaceSize := max(w-(len(left)+len(right)), 0)
space := strings.Repeat(" ", spaceSize) space := strings.Repeat(" ", spaceSize)
@ -31,6 +31,7 @@ func (m Model) renderStatusBar(w int) string {
return m.theme.Header.Render(left + space + right) return m.theme.Header.Render(left + space + right)
} }
// TODO: Implement
func (m Model) renderSearchPane(w, h int) []string { func (m Model) renderSearchPane(w, h int) []string {
lines := make([]string, h) lines := make([]string, h)
for y := range lines { for y := range lines {
@ -91,6 +92,7 @@ func (m Model) renderRequestPane(w, h int) []string {
return lines return lines
} }
// TODO: Implement
func (m Model) renderDetailsPane(w, h int) []string { func (m Model) renderDetailsPane(w, h int) []string {
lines := make([]string, h) lines := make([]string, h)
for y := range lines { for y := range lines {
@ -123,22 +125,33 @@ func (m Model) renderEventsPane(w, h int) []string {
lines := []string{status} lines := []string{status}
for _, event := range events { for _, event := range events {
line := fmt.Sprintf( var (
"%s %-15s %s", eTime string = m.theme.TextMuted.Render(event.Time.Format("15:04:05") + " ")
event.Time.Format("15:04:05"), eType string = getEventColor(m.theme, event.Type).Render(fmt.Sprintf("%-15s ", event.Type))
event.Type,
event.Body, avail int = max(0, w-lipgloss.Width(eTime+eType))
) body string = clampRendered(m.theme.Text.Render(event.Body), avail)
if event.PID > 0 {
line = fmt.Sprintf(
"%s %-15s %d %s",
event.Time.Format("15:04:05"),
event.Type,
event.PID,
event.Body,
) )
if event.Type == model.EventTypeRequestFailed || event.Type == model.EventTypeFatal {
body = clampRendered(m.theme.TextError.Render(event.Body), avail)
eTime = m.theme.TextMutedError.Render(event.Time.Format("15:04:05") + " ")
} }
lines = append(lines, truncate(line, w))
line := eTime + eType + body
if event.PID > 0 {
pid := m.theme.TextMuted.Render(fmt.Sprintf("%d ", event.PID))
avail = max(0, w-lipgloss.Width(eTime+eType+pid))
body = clampRendered(m.theme.Text.Render(event.Body), avail)
line = eTime + eType + pid + body
}
if event.Type == model.EventTypeRequestFailed || event.Type == model.EventTypeFatal {
line += m.theme.TextError.Render(strings.Repeat(" ", w-lipgloss.Width(line)))
}
lines = append(lines, line)
} }
// Cleanup // Cleanup

View File

@ -3,6 +3,7 @@ package tui
import ( import (
"github.com/charmbracelet/lipgloss" "github.com/charmbracelet/lipgloss"
"github.com/charmbracelet/x/ansi" "github.com/charmbracelet/x/ansi"
"termtap.dev/internal/model"
) )
type Theme struct { type Theme struct {
@ -17,10 +18,15 @@ type Theme struct {
TextError lipgloss.Style TextError lipgloss.Style
TextMutedError lipgloss.Style TextMutedError lipgloss.Style
EventGreen lipgloss.Style EventDefault lipgloss.Style
EventRed lipgloss.Style EventSession lipgloss.Style
EventBlue lipgloss.Style EventProcess lipgloss.Style
EventOrange lipgloss.Style EventProxy lipgloss.Style
EventRequestInFlight lipgloss.Style
EventSuccess lipgloss.Style
EventWarn lipgloss.Style
EventError lipgloss.Style
EventFatal lipgloss.Style
} }
const background = lipgloss.Color("#010e1f") const background = lipgloss.Color("#010e1f")
@ -29,8 +35,11 @@ const text = lipgloss.Color("#dfe5ed")
const textMuted = lipgloss.Color("#7c7e80") const textMuted = lipgloss.Color("#7c7e80")
const blue = lipgloss.Color("#2280f2") const blue = lipgloss.Color("#2280f2")
const cyan = lipgloss.Color("#22b8f2")
const violetBlue = lipgloss.Color("#6f7dff")
const orange = lipgloss.Color("#f2a813") const orange = lipgloss.Color("#f2a813")
const red = lipgloss.Color("#e6130b") const red = lipgloss.Color("#e6130b")
const fatalRed = lipgloss.Color("#ff4d4d")
const green = lipgloss.Color("#10e31e") const green = lipgloss.Color("#10e31e")
func newTheme() Theme { func newTheme() Theme {
@ -64,18 +73,42 @@ func newTheme() Theme {
Foreground(textMuted). Foreground(textMuted).
Background(backgroundError), Background(backgroundError),
EventGreen: lipgloss.NewStyle(). EventDefault: lipgloss.NewStyle().
Foreground(green). Foreground(text).
Background(background), Background(background).
EventBlue: lipgloss.NewStyle(). Bold(true),
EventSession: lipgloss.NewStyle().
Foreground(textMuted).
Background(background).
Bold(true),
EventProcess: lipgloss.NewStyle().
Foreground(blue). Foreground(blue).
Background(background), Background(background).
EventRed: lipgloss.NewStyle(). Bold(true),
Foreground(red). EventProxy: lipgloss.NewStyle().
Background(backgroundError), Foreground(violetBlue).
EventOrange: lipgloss.NewStyle(). Background(background).
Bold(true),
EventRequestInFlight: lipgloss.NewStyle().
Foreground(cyan).
Background(background).
Bold(true),
EventSuccess: lipgloss.NewStyle().
Foreground(green).
Background(background).
Bold(true),
EventWarn: lipgloss.NewStyle().
Foreground(orange). Foreground(orange).
Background(background), Background(background).
Bold(true),
EventError: lipgloss.NewStyle().
Foreground(red).
Background(backgroundError).
Bold(true),
EventFatal: lipgloss.NewStyle().
Foreground(fatalRed).
Background(backgroundError).
Bold(true),
} }
} }
@ -88,3 +121,42 @@ func clampRendered(s string, maxCols int) string {
} }
return ansi.Truncate(s, maxCols, "...") return ansi.Truncate(s, maxCols, "...")
} }
func getEventColor(theme Theme, event model.EventType) lipgloss.Style {
switch event {
case model.EventTypeSessionStarted,
model.EventTypeSessionStopped:
return theme.EventSession
case model.EventTypeProxyStarted,
model.EventTypeProxyStarting,
model.EventTypeProxyStopped:
return theme.EventProxy
case model.EventTypeRequestStarted:
return theme.EventRequestInFlight
case model.EventTypeRequestFinished:
return theme.EventSuccess
case model.EventTypeFatal:
return theme.EventFatal
case model.EventTypeRequestFailed:
return theme.EventError
case model.EventTypeProcessStarting,
model.EventTypeProcessStarted,
model.EventTypeProcessExited,
model.EventTypeProcessSignaled,
model.EventTypeProcessStdout,
model.EventTypeProcessStderr:
return theme.EventProcess
case model.EventTypeWarn:
return theme.EventWarn
default:
return theme.EventDefault
}
}