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{
Time: time.Now().Local(),
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,
}
} else {
@ -86,7 +86,7 @@ func proxyHandler(ch chan<- model.Event) http.Handler {
ch <- model.Event{
Time: time.Now().Local(),
Type: model.EventTypeRequestStarted,
Body: fmt.Sprintf("-> %+v", request),
Body: fmt.Sprintf("(%s) %s %s", getEndOfUUID(request.ID), request.Method, request.RawURL),
Request: request,
}
@ -103,7 +103,7 @@ func proxyHandler(ch chan<- model.Event) http.Handler {
ch <- model.Event{
Time: time.Now().Local(),
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,
}
return
@ -115,7 +115,7 @@ func proxyHandler(ch chan<- model.Event) http.Handler {
ch <- model.Event{
Time: time.Now().Local(),
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,
}
} else {
@ -133,7 +133,7 @@ func proxyHandler(ch chan<- model.Event) http.Handler {
ch <- model.Event{
Time: time.Now().Local(),
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
}
@ -146,7 +146,7 @@ func proxyHandler(ch chan<- model.Event) http.Handler {
ch <- model.Event{
Time: time.Now().Local(),
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,
}
})
@ -199,6 +199,10 @@ func formatHeaders(headers http.Header) string {
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
func statusFromUpstreamError(req *http.Request, resp *http.Response, err error) int {
if resp != nil {

View File

@ -23,7 +23,7 @@ func (m Model) renderStatusBar(w int) string {
avg := int(msSum) / max(1, len(m.requests))
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)
space := strings.Repeat(" ", spaceSize)
@ -31,6 +31,7 @@ func (m Model) renderStatusBar(w int) string {
return m.theme.Header.Render(left + space + right)
}
// TODO: Implement
func (m Model) renderSearchPane(w, h int) []string {
lines := make([]string, h)
for y := range lines {
@ -91,6 +92,7 @@ func (m Model) renderRequestPane(w, h int) []string {
return lines
}
// TODO: Implement
func (m Model) renderDetailsPane(w, h int) []string {
lines := make([]string, h)
for y := range lines {
@ -123,22 +125,33 @@ func (m Model) renderEventsPane(w, h int) []string {
lines := []string{status}
for _, event := range events {
line := fmt.Sprintf(
"%s %-15s %s",
event.Time.Format("15:04:05"),
event.Type,
event.Body,
)
if event.PID > 0 {
line = fmt.Sprintf(
"%s %-15s %d %s",
event.Time.Format("15:04:05"),
event.Type,
event.PID,
event.Body,
var (
eTime string = m.theme.TextMuted.Render(event.Time.Format("15:04:05") + " ")
eType string = getEventColor(m.theme, event.Type).Render(fmt.Sprintf("%-15s ", event.Type))
avail int = max(0, w-lipgloss.Width(eTime+eType))
body string = clampRendered(m.theme.Text.Render(event.Body), avail)
)
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

View File

@ -3,6 +3,7 @@ package tui
import (
"github.com/charmbracelet/lipgloss"
"github.com/charmbracelet/x/ansi"
"termtap.dev/internal/model"
)
type Theme struct {
@ -17,10 +18,15 @@ type Theme struct {
TextError lipgloss.Style
TextMutedError lipgloss.Style
EventGreen lipgloss.Style
EventRed lipgloss.Style
EventBlue lipgloss.Style
EventOrange lipgloss.Style
EventDefault lipgloss.Style
EventSession lipgloss.Style
EventProcess 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")
@ -29,8 +35,11 @@ const text = lipgloss.Color("#dfe5ed")
const textMuted = lipgloss.Color("#7c7e80")
const blue = lipgloss.Color("#2280f2")
const cyan = lipgloss.Color("#22b8f2")
const violetBlue = lipgloss.Color("#6f7dff")
const orange = lipgloss.Color("#f2a813")
const red = lipgloss.Color("#e6130b")
const fatalRed = lipgloss.Color("#ff4d4d")
const green = lipgloss.Color("#10e31e")
func newTheme() Theme {
@ -64,18 +73,42 @@ func newTheme() Theme {
Foreground(textMuted).
Background(backgroundError),
EventGreen: lipgloss.NewStyle().
Foreground(green).
Background(background),
EventBlue: lipgloss.NewStyle().
EventDefault: lipgloss.NewStyle().
Foreground(text).
Background(background).
Bold(true),
EventSession: lipgloss.NewStyle().
Foreground(textMuted).
Background(background).
Bold(true),
EventProcess: lipgloss.NewStyle().
Foreground(blue).
Background(background),
EventRed: lipgloss.NewStyle().
Foreground(red).
Background(backgroundError),
EventOrange: lipgloss.NewStyle().
Background(background).
Bold(true),
EventProxy: lipgloss.NewStyle().
Foreground(violetBlue).
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).
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, "...")
}
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
}
}