Files
Mester Gábor 2dd6519168 Initial commit
2026-03-19 07:12:03 +01:00

182 lines
4.6 KiB
Go

package tui
import (
"fmt"
"strings"
"time"
"github.com/charmbracelet/lipgloss"
)
const maxHistory = 100
// HistoryEntry records one completed HTTP transaction.
type HistoryEntry struct {
At time.Time
Name string
Method string
URL string
StatusCode int
Status string
Duration time.Duration
Size int
ContentType string
IsError bool
ErrMsg string
}
// ---------------------------------------------------------------------------
// Formatting helpers
// ---------------------------------------------------------------------------
func fmtDuration(d time.Duration) string {
switch {
case d < time.Millisecond:
return "< 1ms"
case d < time.Second:
return fmt.Sprintf("%dms", d.Milliseconds())
case d < time.Minute:
return fmt.Sprintf("%.1fs", d.Seconds())
default:
m := int(d.Minutes())
s := int(d.Seconds()) % 60
return fmt.Sprintf("%dm%ds", m, s)
}
}
func fmtSize(n int) string {
switch {
case n < 1024:
return fmt.Sprintf("%d B", n)
case n < 1024*1024:
return fmt.Sprintf("%.1f KB", float64(n)/1024)
default:
return fmt.Sprintf("%.1f MB", float64(n)/1024/1024)
}
}
func durationStyle(d time.Duration) lipgloss.Style {
switch {
case d < 200*time.Millisecond:
return lipgloss.NewStyle().Foreground(colorGreen)
case d < time.Second:
return lipgloss.NewStyle().Foreground(colorYellow)
default:
return lipgloss.NewStyle().Foreground(colorRed)
}
}
// ---------------------------------------------------------------------------
// Stats bar (rendered inside the response panel, below the viewport)
// ---------------------------------------------------------------------------
// renderStatsBar returns a single line summarising the last response.
func renderStatsBar(e HistoryEntry, width int) string {
if e.IsError {
return statusErrStyle.Render(" ✗ " + e.ErrMsg)
}
status := statusCodeStyle(e.StatusCode).Render(e.Status)
dur := durationStyle(e.Duration).Render("⚡ " + fmtDuration(e.Duration))
size := lipgloss.NewStyle().Foreground(colorFg).Render("◎ " + fmtSize(e.Size))
sep := dimItemStyle.Render(" │ ")
bar := " " + status + sep + dur + sep + size
// Append content-type if it fits
if e.ContentType != "" {
candidate := bar + sep + dimItemStyle.Render(e.ContentType)
if lipgloss.Width(candidate) <= width-2 {
bar = candidate
}
}
return bar
}
// ---------------------------------------------------------------------------
// History overlay table
// ---------------------------------------------------------------------------
// renderHistoryTable builds the full text content for the history viewport.
func renderHistoryTable(history []HistoryEntry, vpWidth int) string {
if len(history) == 0 {
return hintStyle.Render("\n No requests yet in this session.")
}
// Column widths
const (
colTime = 8 // HH:MM:SS
colMethod = 7 // DELETE + space
colStatus = 10 // "200 OK" etc.
colDur = 9 // "1.2s" etc.
colSize = 8 // "4.2 KB"
colSep = 2 // " "
)
fixedW := colTime + colMethod + colStatus + colDur + colSize + colSep*5
nameW := vpWidth - fixedW - 2
if nameW < 10 {
nameW = 10
}
header := buildHistoryRow(
dimItemStyle,
"Time", "Method", "Status", "Duration", "Size", "Name",
colTime, colMethod, colStatus, colDur, colSize, nameW,
)
divider := dimItemStyle.Render(strings.Repeat("─", vpWidth-2))
var sb strings.Builder
sb.WriteString(header + "\n")
sb.WriteString(divider + "\n")
// Most recent first
for i := len(history) - 1; i >= 0; i-- {
e := history[i]
timeStr := e.At.Format("15:04:05")
method := e.Method
status := e.Status
dur := fmtDuration(e.Duration)
size := fmtSize(e.Size)
name := e.Name
if len(name) > nameW {
name = name[:nameW-1] + "…"
}
var rowStyle lipgloss.Style
if e.IsError {
rowStyle = lipgloss.NewStyle().Foreground(colorRed)
} else {
rowStyle = lipgloss.NewStyle().Foreground(colorFg)
}
methodStr := methodStyle(method).Render(fmt.Sprintf("%-*s", colMethod, method))
statusStr := statusCodeStyle(e.StatusCode).Render(fmt.Sprintf("%-*s", colStatus, status))
durStr := durationStyle(e.Duration).Render(fmt.Sprintf("%-*s", colDur, dur))
row := rowStyle.Render(timeStr+" ") +
methodStr + " " +
statusStr + " " +
durStr + " " +
rowStyle.Render(fmt.Sprintf("%-*s", colSize, size)+" "+name)
sb.WriteString(row + "\n")
}
return sb.String()
}
func buildHistoryRow(s lipgloss.Style, t, method, status, dur, size, name string,
wT, wM, wSt, wD, wSz, wN int) string {
return s.Render(
fmt.Sprintf("%-*s %-*s %-*s %-*s %-*s %-*s",
wT, t,
wM, method,
wSt, status,
wD, dur,
wSz, size,
wN, name,
),
)
}