feat: Add colored node labels by type and clean up display format
This commit is contained in:
BIN
inspector.exe
BIN
inspector.exe
Binary file not shown.
@@ -56,7 +56,7 @@ func (nm *NetworkMap) FindByIP(ip string) *NetworkNode {
|
|||||||
// LabelForIP returns a human-readable label for an IP, or the IP itself if unknown
|
// LabelForIP returns a human-readable label for an IP, or the IP itself if unknown
|
||||||
func (nm *NetworkMap) LabelForIP(ip string) string {
|
func (nm *NetworkMap) LabelForIP(ip string) string {
|
||||||
if node := nm.FindByIP(ip); node != nil {
|
if node := nm.FindByIP(ip); node != nil {
|
||||||
return node.Name + " (" + string(node.Type) + ")"
|
return node.Name
|
||||||
}
|
}
|
||||||
return ip
|
return ip
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -99,6 +99,30 @@ type Styles struct {
|
|||||||
Box lipgloss.Style
|
Box lipgloss.Style
|
||||||
PacketRow lipgloss.Style
|
PacketRow lipgloss.Style
|
||||||
CallFlow lipgloss.Style
|
CallFlow lipgloss.Style
|
||||||
|
|
||||||
|
// SIP Styles
|
||||||
|
MethodInvite lipgloss.Style
|
||||||
|
MethodBye lipgloss.Style
|
||||||
|
MethodRegister lipgloss.Style
|
||||||
|
MethodOther lipgloss.Style
|
||||||
|
|
||||||
|
Status1xx lipgloss.Style
|
||||||
|
Status2xx lipgloss.Style
|
||||||
|
Status3xx lipgloss.Style
|
||||||
|
Status4xx lipgloss.Style
|
||||||
|
Status5xx lipgloss.Style
|
||||||
|
Status6xx lipgloss.Style
|
||||||
|
|
||||||
|
NodeLabel lipgloss.Style
|
||||||
|
NodePBX lipgloss.Style
|
||||||
|
NodeProxy lipgloss.Style
|
||||||
|
NodeGateway lipgloss.Style
|
||||||
|
NodeCarrier lipgloss.Style
|
||||||
|
NodeEndpoint lipgloss.Style
|
||||||
|
NodeDefault lipgloss.Style
|
||||||
|
|
||||||
|
ArrowOut lipgloss.Style
|
||||||
|
ArrowIn lipgloss.Style
|
||||||
}
|
}
|
||||||
|
|
||||||
func defaultStyles() Styles {
|
func defaultStyles() Styles {
|
||||||
@@ -135,6 +159,60 @@ func defaultStyles() Styles {
|
|||||||
Border(lipgloss.NormalBorder()).
|
Border(lipgloss.NormalBorder()).
|
||||||
BorderForeground(lipgloss.Color("#44475A")).
|
BorderForeground(lipgloss.Color("#44475A")).
|
||||||
Padding(0, 1),
|
Padding(0, 1),
|
||||||
|
|
||||||
|
// SIP Styles Initialization
|
||||||
|
MethodInvite: lipgloss.NewStyle().Foreground(lipgloss.Color("#8BE9FD")).Bold(true), // Cyan
|
||||||
|
MethodBye: lipgloss.NewStyle().Foreground(lipgloss.Color("#FF5555")).Bold(true), // Red
|
||||||
|
MethodRegister: lipgloss.NewStyle().Foreground(lipgloss.Color("#50FA7B")).Bold(true), // Green
|
||||||
|
MethodOther: lipgloss.NewStyle().Foreground(lipgloss.Color("#BD93F9")).Bold(true), // Purple
|
||||||
|
|
||||||
|
Status1xx: lipgloss.NewStyle().Foreground(lipgloss.Color("#F1FA8C")), // Yellow
|
||||||
|
Status2xx: lipgloss.NewStyle().Foreground(lipgloss.Color("#50FA7B")), // Green
|
||||||
|
Status3xx: lipgloss.NewStyle().Foreground(lipgloss.Color("#8BE9FD")), // Cyan
|
||||||
|
Status4xx: lipgloss.NewStyle().Foreground(lipgloss.Color("#FF5555")), // Red
|
||||||
|
Status5xx: lipgloss.NewStyle().Foreground(lipgloss.Color("#FF5555")).Bold(true), // Red Bold
|
||||||
|
Status6xx: lipgloss.NewStyle().Foreground(lipgloss.Color("#FF5555")).Bold(true).Underline(true),
|
||||||
|
|
||||||
|
NodeLabel: lipgloss.NewStyle().
|
||||||
|
Foreground(lipgloss.Color("#000000")).
|
||||||
|
Background(lipgloss.Color("#BD93F9")). // Purple background
|
||||||
|
Padding(0, 1).
|
||||||
|
Bold(true),
|
||||||
|
|
||||||
|
// Node Styles with different background colors
|
||||||
|
NodePBX: lipgloss.NewStyle().
|
||||||
|
Foreground(lipgloss.Color("#FFFFFF")).
|
||||||
|
Background(lipgloss.Color("#FF79C6")). // Pink
|
||||||
|
Padding(0, 1).
|
||||||
|
Bold(true),
|
||||||
|
NodeProxy: lipgloss.NewStyle().
|
||||||
|
Foreground(lipgloss.Color("#000000")).
|
||||||
|
Background(lipgloss.Color("#8BE9FD")). // Cyan
|
||||||
|
Padding(0, 1).
|
||||||
|
Bold(true),
|
||||||
|
NodeGateway: lipgloss.NewStyle().
|
||||||
|
Foreground(lipgloss.Color("#000000")).
|
||||||
|
Background(lipgloss.Color("#F1FA8C")). // Yellow
|
||||||
|
Padding(0, 1).
|
||||||
|
Bold(true),
|
||||||
|
NodeCarrier: lipgloss.NewStyle().
|
||||||
|
Foreground(lipgloss.Color("#FFFFFF")).
|
||||||
|
Background(lipgloss.Color("#BD93F9")). // Purple
|
||||||
|
Padding(0, 1).
|
||||||
|
Bold(true),
|
||||||
|
NodeEndpoint: lipgloss.NewStyle().
|
||||||
|
Foreground(lipgloss.Color("#FFFFFF")).
|
||||||
|
Background(lipgloss.Color("#6272A4")). // Grey/Blue
|
||||||
|
Padding(0, 1).
|
||||||
|
Bold(true),
|
||||||
|
NodeDefault: lipgloss.NewStyle().
|
||||||
|
Foreground(lipgloss.Color("#FFFFFF")).
|
||||||
|
Background(lipgloss.Color("#44475A")). // Grey
|
||||||
|
Padding(0, 1).
|
||||||
|
Bold(true),
|
||||||
|
|
||||||
|
ArrowOut: lipgloss.NewStyle().Foreground(lipgloss.Color("#FF79C6")), // Pink
|
||||||
|
ArrowIn: lipgloss.NewStyle().Foreground(lipgloss.Color("#F1FA8C")), // Yellow
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -608,6 +686,8 @@ func (m Model) renderAddNodeForm() string {
|
|||||||
b.WriteString("\n")
|
b.WriteString("\n")
|
||||||
}
|
}
|
||||||
b.WriteString("\n")
|
b.WriteString("\n")
|
||||||
|
b.WriteString(m.styles.Help.Render("Node Types: PBX, Proxy, SBC, Carrier, Handset, Firewall"))
|
||||||
|
b.WriteString("\n")
|
||||||
b.WriteString(m.styles.Help.Render("Tab navigate • Enter submit • Esc cancel"))
|
b.WriteString(m.styles.Help.Render("Tab navigate • Enter submit • Esc cancel"))
|
||||||
return b.String()
|
return b.String()
|
||||||
}
|
}
|
||||||
@@ -634,8 +714,19 @@ func (m Model) renderCallDetail() string {
|
|||||||
// Find first packet to get initial IPs
|
// Find first packet to get initial IPs
|
||||||
if len(flow.Packets) > 0 {
|
if len(flow.Packets) > 0 {
|
||||||
first := flow.Packets[0]
|
first := flow.Packets[0]
|
||||||
|
|
||||||
srcLabel := m.networkMap.LabelForIP(first.SourceIP)
|
srcLabel := m.networkMap.LabelForIP(first.SourceIP)
|
||||||
|
if srcLabel != first.SourceIP {
|
||||||
|
// Find node type to apply style
|
||||||
|
node := m.networkMap.FindByIP(first.SourceIP)
|
||||||
|
srcLabel = m.styleForNode(node).Render(srcLabel)
|
||||||
|
}
|
||||||
|
|
||||||
dstLabel := m.networkMap.LabelForIP(first.DestIP)
|
dstLabel := m.networkMap.LabelForIP(first.DestIP)
|
||||||
|
if dstLabel != first.DestIP {
|
||||||
|
node := m.networkMap.FindByIP(first.DestIP)
|
||||||
|
dstLabel = m.styleForNode(node).Render(dstLabel)
|
||||||
|
}
|
||||||
|
|
||||||
b.WriteString(fmt.Sprintf(" Source: %s (%s:%d)\n", srcLabel, first.SourceIP, first.SourcePort))
|
b.WriteString(fmt.Sprintf(" Source: %s (%s:%d)\n", srcLabel, first.SourceIP, first.SourcePort))
|
||||||
b.WriteString(fmt.Sprintf(" Destination: %s (%s:%d)\n", dstLabel, first.DestIP, first.DestPort))
|
b.WriteString(fmt.Sprintf(" Destination: %s (%s:%d)\n", dstLabel, first.DestIP, first.DestPort))
|
||||||
@@ -645,22 +736,63 @@ func (m Model) renderCallDetail() string {
|
|||||||
b.WriteString("Transaction Flow:\n")
|
b.WriteString("Transaction Flow:\n")
|
||||||
for i, pkt := range flow.Packets {
|
for i, pkt := range flow.Packets {
|
||||||
arrow := "→"
|
arrow := "→"
|
||||||
|
arrowStyle := m.styles.ArrowOut
|
||||||
|
|
||||||
// Simple direction indicator based on whether it matches initial source
|
// Simple direction indicator based on whether it matches initial source
|
||||||
if len(flow.Packets) > 0 && pkt.SourceIP != flow.Packets[0].SourceIP {
|
if len(flow.Packets) > 0 && pkt.SourceIP != flow.Packets[0].SourceIP {
|
||||||
arrow = "←"
|
arrow = "←"
|
||||||
|
arrowStyle = m.styles.ArrowIn
|
||||||
|
}
|
||||||
|
|
||||||
|
// Style the packet summary (Method or Status)
|
||||||
|
var summaryStyle lipgloss.Style
|
||||||
|
if pkt.IsRequest {
|
||||||
|
switch pkt.Method {
|
||||||
|
case sip.MethodINVITE:
|
||||||
|
summaryStyle = m.styles.MethodInvite
|
||||||
|
case sip.MethodBYE, sip.MethodCANCEL:
|
||||||
|
summaryStyle = m.styles.MethodBye
|
||||||
|
case sip.MethodREGISTER:
|
||||||
|
summaryStyle = m.styles.MethodRegister
|
||||||
|
default:
|
||||||
|
summaryStyle = m.styles.MethodOther
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
switch {
|
||||||
|
case pkt.StatusCode >= 100 && pkt.StatusCode < 200:
|
||||||
|
summaryStyle = m.styles.Status1xx
|
||||||
|
case pkt.StatusCode >= 200 && pkt.StatusCode < 300:
|
||||||
|
summaryStyle = m.styles.Status2xx
|
||||||
|
case pkt.StatusCode >= 300 && pkt.StatusCode < 400:
|
||||||
|
summaryStyle = m.styles.Status3xx
|
||||||
|
case pkt.StatusCode >= 400 && pkt.StatusCode < 500:
|
||||||
|
summaryStyle = m.styles.Status4xx
|
||||||
|
case pkt.StatusCode >= 500 && pkt.StatusCode < 600:
|
||||||
|
summaryStyle = m.styles.Status5xx
|
||||||
|
default:
|
||||||
|
summaryStyle = m.styles.Status6xx
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Format timestamp
|
// Format timestamp
|
||||||
ts := pkt.Timestamp.Format("15:04:05.000")
|
ts := pkt.Timestamp.Format("15:04:05.000")
|
||||||
|
|
||||||
// Clean packet info line (Timestamp + Arrow + Method/Status)
|
// Clean packet info line (Timestamp + Arrow + Method/Status)
|
||||||
b.WriteString(fmt.Sprintf(" %d. [%s] %s %s\n", i+1, ts, arrow, pkt.Summary()))
|
b.WriteString(fmt.Sprintf(" %d. [%s] %s %s\n",
|
||||||
|
i+1,
|
||||||
|
ts,
|
||||||
|
arrowStyle.Render(arrow),
|
||||||
|
summaryStyle.Render(pkt.Summary())))
|
||||||
|
|
||||||
// Show SDP info if present
|
// Show SDP info if present
|
||||||
if pkt.SDP != nil {
|
if pkt.SDP != nil {
|
||||||
mediaIP := pkt.SDP.GetSDPMediaIP()
|
mediaIP := pkt.SDP.GetSDPMediaIP()
|
||||||
if mediaIP != "" {
|
if mediaIP != "" {
|
||||||
label := m.networkMap.LabelForIP(mediaIP)
|
label := m.networkMap.LabelForIP(mediaIP)
|
||||||
|
if label != mediaIP {
|
||||||
|
node := m.networkMap.FindByIP(mediaIP)
|
||||||
|
label = m.styleForNode(node).Render(label)
|
||||||
|
}
|
||||||
b.WriteString(fmt.Sprintf(" SDP Media: %s (%s)\n", mediaIP, label))
|
b.WriteString(fmt.Sprintf(" SDP Media: %s (%s)\n", mediaIP, label))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -949,3 +1081,32 @@ func extractUser(sipAddr string) string {
|
|||||||
}
|
}
|
||||||
return sipAddr
|
return sipAddr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// styleForNode returns the style for a given node type
|
||||||
|
func (m Model) styleForNode(node *config.NetworkNode) lipgloss.Style {
|
||||||
|
if node == nil {
|
||||||
|
return m.styles.NodeDefault
|
||||||
|
}
|
||||||
|
switch node.Type {
|
||||||
|
case config.NodeTypePBX:
|
||||||
|
return m.styles.NodePBX
|
||||||
|
case config.NodeTypeProxy:
|
||||||
|
return m.styles.NodeProxy
|
||||||
|
case config.NodeTypeGateway:
|
||||||
|
return m.styles.NodeGateway
|
||||||
|
case config.NodeTypeMediaServer:
|
||||||
|
// Reusing Proxy style for MediaServer if no specific one defined
|
||||||
|
return m.styles.NodeProxy
|
||||||
|
case config.NodeTypeEndpoint:
|
||||||
|
return m.styles.NodeEndpoint
|
||||||
|
case config.NodeTypeUnknown:
|
||||||
|
return m.styles.NodeDefault
|
||||||
|
default:
|
||||||
|
// Attempt to match by name if type is custom or carrier
|
||||||
|
lowerName := strings.ToLower(node.Name)
|
||||||
|
if strings.Contains(lowerName, "carrier") || strings.Contains(lowerName, "provider") {
|
||||||
|
return m.styles.NodeCarrier
|
||||||
|
}
|
||||||
|
return m.styles.NodeDefault
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user