// Package request contains renderers shared by the detail pane's
// Headers and Body tabs. They are kept separate from detail.Model to
// make them easy to snapshot-test.
//
// mercemay.top/src/httptap/
package request
import (
"sort"
"strings"
"mercemay.top/httptap/internal/tui/theme"
)
// RenderHeaders formats the header block as a two-column aligned list.
// Canonical casing is assumed to have already been applied; values are
// wrapped if they exceed a sensible width.
func RenderHeaders(pal theme.Palette, headers [][2]string) string {
if len(headers) == 0 {
return pal.Detail.Render("(no headers)")
}
// Compute key column width for alignment.
maxKey := 0
for _, kv := range headers {
if n := len(kv[0]); n > maxKey {
maxKey = n
}
}
if maxKey > 32 {
maxKey = 32
}
var b strings.Builder
for _, kv := range headers {
k := theme.Truncate(kv[0], maxKey)
pad := strings.Repeat(" ", maxKey-len(k))
b.WriteString(pal.HeaderKey().Render(k + pad))
b.WriteString(" ")
b.WriteString(pal.HeaderVal().Render(kv[1]))
b.WriteByte('\n')
}
return strings.TrimRight(b.String(), "\n")
}
// SortedCopy returns a copy of headers sorted by name. It is used by the
// "sort headers" keybinding so that diffs between requests are easier to
// eyeball.
func SortedCopy(headers [][2]string) [][2]string {
out := make([][2]string, len(headers))
copy(out, headers)
sort.SliceStable(out, func(i, j int) bool {
if out[i][0] == out[j][0] {
return out[i][1] < out[j][1]
}
return out[i][0] < out[j][0]
})
return out
}