mirror of
https://github.com/usestrix/strix.git
synced 2026-10-12 03:47:56 +00:00
feat(tui): move the run's targets into their own sidebar box under the viewer
This commit is contained in:
parent
0265487b48
commit
5b8ad7c2ff
3 changed files with 57 additions and 16 deletions
|
|
@ -66,7 +66,7 @@ func panelFocus(panel sidebarPanel) (focusMode, bool) {
|
|||
|
||||
func (m Model) sidebarPanels() []panelRect {
|
||||
statsHeight, vulnHeight, mcpHeight, agentHeight := m.sidebarHeights()
|
||||
top := m.viewerHeight()
|
||||
top := m.sidebarTopHeight()
|
||||
rects := []panelRect{{panelAgents, top, agentHeight}}
|
||||
top += agentHeight
|
||||
if vulnHeight > 0 {
|
||||
|
|
@ -83,7 +83,7 @@ func (m Model) sidebarPanels() []panelRect {
|
|||
|
||||
func (m Model) sidebarGap() int {
|
||||
statsHeight, vulnHeight, mcpHeight, agentHeight := m.sidebarHeights()
|
||||
return max(0, m.height-m.viewerHeight()-agentHeight-vulnHeight-mcpHeight-statsHeight)
|
||||
return max(0, m.height-m.sidebarTopHeight()-agentHeight-vulnHeight-mcpHeight-statsHeight)
|
||||
}
|
||||
|
||||
func (m Model) panelAt(y int) (panelRect, bool) {
|
||||
|
|
|
|||
|
|
@ -356,16 +356,35 @@ func TestLonePanelHasNoControls(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestStatsPanelListsTargets(t *testing.T) {
|
||||
func TestTargetsBoxSitsUnderViewer(t *testing.T) {
|
||||
m := panelsModel(t)
|
||||
m.snapshot.Targets = []string{"https://example.com", "./app"}
|
||||
m.snapshot.TargetCount = 2
|
||||
m.resizeViewport()
|
||||
_, sidebarWidth, _, _ := m.layout()
|
||||
view := ansi.Strip(m.sidebarView(sidebarWidth, m.height))
|
||||
for _, want := range []string{"Targets 2", "▸ https://example.com", "▸ ./app"} {
|
||||
if !strings.Contains(view, want) {
|
||||
t.Fatalf("sidebar missing %q:\n%s", want, view)
|
||||
rows := strings.Split(ansi.Strip(m.sidebarView(sidebarWidth, m.height)), "\n")
|
||||
if len(rows) != m.height {
|
||||
t.Fatalf("sidebar renders %d rows, want %d", len(rows), m.height)
|
||||
}
|
||||
top := m.viewerHeight()
|
||||
for i, want := range []string{"Targets 2", "▸ https://example.com", "▸ ./app"} {
|
||||
if !strings.Contains(rows[top+1+i], want) {
|
||||
t.Fatalf("row %d missing %q:\n%s", top+1+i, want, strings.Join(rows, "\n"))
|
||||
}
|
||||
}
|
||||
if !strings.Contains(rows[panelRectOf(t, m, panelAgents).top+1], "Agents") {
|
||||
t.Fatalf("agents panel not where its rect says:\n%s", strings.Join(rows, "\n"))
|
||||
}
|
||||
}
|
||||
|
||||
func TestTargetsBoxDropsOnShortTerminal(t *testing.T) {
|
||||
m := panelsModel(t)
|
||||
m.height = 19
|
||||
m.snapshot.Targets = []string{"a", "b", "c", "d", "e"}
|
||||
m.snapshot.TargetCount = 5
|
||||
m.resizeViewport()
|
||||
_, sidebarWidth, _, _ := m.layout()
|
||||
if got := lipgloss.Height(m.sidebarView(sidebarWidth, m.height)); got > m.height {
|
||||
t.Fatalf("sidebar renders %d rows on a %d-row terminal", got, m.height)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -542,6 +542,15 @@ func (m Model) sidebarView(width, height int) string {
|
|||
BorderForeground(dark).
|
||||
Padding(0, 1).
|
||||
Render(m.viewerBox(width - 4))}
|
||||
if targetsHeight := m.targetsBoxHeight(); targetsHeight > 0 {
|
||||
parts = append(parts, lipgloss.NewStyle().
|
||||
Width(width-2).
|
||||
Height(targetsHeight-2).
|
||||
Border(lipgloss.RoundedBorder()).
|
||||
BorderForeground(dark).
|
||||
Padding(0, 1).
|
||||
Render(fixedPanelBody(m.targetsView(width-4), width-4, targetsHeight-2)))
|
||||
}
|
||||
agents := ""
|
||||
if agentHeight > 1 {
|
||||
agentRows := max(1, agentHeight-4)
|
||||
|
|
@ -611,7 +620,7 @@ func (m Model) sidebarHeights() (statsHeight, vulnHeight, mcpHeight, agentHeight
|
|||
h *int
|
||||
floor int
|
||||
}{{&statsHeight, 4}, {&mcpHeight, 5}, {&vulnHeight, 5}} {
|
||||
over := m.viewerHeight() + statsHeight + vulnHeight + mcpHeight + agentHeight - m.height
|
||||
over := m.sidebarTopHeight() + statsHeight + vulnHeight + mcpHeight + agentHeight - m.height
|
||||
if over <= 0 {
|
||||
break
|
||||
}
|
||||
|
|
@ -620,14 +629,14 @@ func (m Model) sidebarHeights() (statsHeight, vulnHeight, mcpHeight, agentHeight
|
|||
}
|
||||
}
|
||||
for _, h := range []*int{&mcpHeight, &vulnHeight, &statsHeight} {
|
||||
if m.viewerHeight()+statsHeight+vulnHeight+mcpHeight+agentHeight <= m.height {
|
||||
if m.sidebarTopHeight()+statsHeight+vulnHeight+mcpHeight+agentHeight <= m.height {
|
||||
break
|
||||
}
|
||||
if *h > 1 {
|
||||
*h = 1
|
||||
}
|
||||
}
|
||||
spare := max(0, m.height-m.viewerHeight()-statsHeight-vulnHeight-mcpHeight-agentHeight)
|
||||
spare := max(0, m.height-m.sidebarTopHeight()-statsHeight-vulnHeight-mcpHeight-agentHeight)
|
||||
switch {
|
||||
case m.zoomedPanel == panelFindings && vulnHeight > 1:
|
||||
vulnHeight += spare
|
||||
|
|
@ -643,6 +652,25 @@ func (m Model) viewerHeight() int {
|
|||
return strings.Count(m.viewerView(m.viewerContentWidth()), "\n") + 3
|
||||
}
|
||||
|
||||
// sidebarTopHeight is the rows above the panels: the viewer box and, when the
|
||||
// run has targets, the targets box under it.
|
||||
func (m Model) sidebarTopHeight() int {
|
||||
return m.viewerHeight() + m.targetsBoxHeight()
|
||||
}
|
||||
|
||||
// targetsBoxHeight drops the box on terminals too short to also fit the agents
|
||||
// box, the stats box at its floor and squeezed findings and MCP headers.
|
||||
func (m Model) targetsBoxHeight() int {
|
||||
if len(m.snapshot.Targets) == 0 {
|
||||
return 0
|
||||
}
|
||||
room := m.height - m.viewerHeight() - 5 - 4 - 2
|
||||
if room < 3 {
|
||||
return 0
|
||||
}
|
||||
return min(lipgloss.Height(m.targetsView(m.sidebarInnerWidth()))+2, room)
|
||||
}
|
||||
|
||||
func (m Model) sidebarInnerWidth() int {
|
||||
_, sidebarWidth, _, _ := m.layout()
|
||||
if sidebarWidth == 0 {
|
||||
|
|
@ -694,12 +722,6 @@ func (m Model) statsView() string {
|
|||
b.WriteString(w.Render(fmt.Sprintf(" · $%.2f", cost)))
|
||||
}
|
||||
}
|
||||
if targets := m.targetsView(m.sidebarInnerWidth()); targets != "" {
|
||||
if b.Len() > 0 {
|
||||
b.WriteString("\n")
|
||||
}
|
||||
b.WriteString(targets)
|
||||
}
|
||||
if caido := m.snapshot.CaidoURL; caido != "" {
|
||||
if b.Len() > 0 {
|
||||
b.WriteString("\n")
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue