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
|
||||
func (nm *NetworkMap) LabelForIP(ip string) string {
|
||||
if node := nm.FindByIP(ip); node != nil {
|
||||
return node.Name + " (" + string(node.Type) + ")"
|
||||
return node.Name
|
||||
}
|
||||
return ip
|
||||
}
|
||||
|
||||
@@ -99,6 +99,30 @@ type Styles struct {
|
||||
Box lipgloss.Style
|
||||
PacketRow 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 {
|
||||
@@ -135,6 +159,60 @@ func defaultStyles() Styles {
|
||||
Border(lipgloss.NormalBorder()).
|
||||
BorderForeground(lipgloss.Color("#44475A")).
|
||||
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(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"))
|
||||
return b.String()
|
||||
}
|
||||
@@ -634,8 +714,19 @@ func (m Model) renderCallDetail() string {
|
||||
// Find first packet to get initial IPs
|
||||
if len(flow.Packets) > 0 {
|
||||
first := flow.Packets[0]
|
||||
|
||||
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)
|
||||
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(" Destination: %s (%s:%d)\n", dstLabel, first.DestIP, first.DestPort))
|
||||
@@ -645,22 +736,63 @@ func (m Model) renderCallDetail() string {
|
||||
b.WriteString("Transaction Flow:\n")
|
||||
for i, pkt := range flow.Packets {
|
||||
arrow := "→"
|
||||
arrowStyle := m.styles.ArrowOut
|
||||
|
||||
// Simple direction indicator based on whether it matches initial source
|
||||
if len(flow.Packets) > 0 && pkt.SourceIP != flow.Packets[0].SourceIP {
|
||||
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
|
||||
ts := pkt.Timestamp.Format("15:04:05.000")
|
||||
|
||||
// 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
|
||||
if pkt.SDP != nil {
|
||||
mediaIP := pkt.SDP.GetSDPMediaIP()
|
||||
if 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))
|
||||
}
|
||||
}
|
||||
@@ -949,3 +1081,32 @@ func extractUser(sipAddr string) string {
|
||||
}
|
||||
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