()
| 128 | } |
| 129 | |
| 130 | func (m scanModel) View() string { |
| 131 | header := sectionStyle.Render("Scan — discover SNIs and CDN IPs that work on this network") |
| 132 | controls := "\n" + |
| 133 | codeStyle.Render("s") + " scan SNIs " + |
| 134 | codeStyle.Render("i") + " scan IPs " + |
| 135 | codeStyle.Render("a") + " scan all " + |
| 136 | codeStyle.Render("t") + " edit target " + |
| 137 | codeStyle.Render("x") + " stop " + |
| 138 | codeStyle.Render("e") + " save top results → active profile" |
| 139 | |
| 140 | targetRow := "SNI target: " + m.target.View() |
| 141 | if !m.target.Focused() { |
| 142 | targetRow = "SNI target: " + codeStyle.Render(m.target.Value()) |
| 143 | } |
| 144 | |
| 145 | status := mutedStyle.Render("idle — pick an action above") |
| 146 | if m.running { |
| 147 | status = hintStyle.Render(fmt.Sprintf("running %s (%s)", |
| 148 | modeName(m.mode), time.Since(m.started).Round(time.Millisecond))) |
| 149 | } else if m.sniDone+m.ipDone > 0 { |
| 150 | status = okStyle.Render("done") |
| 151 | } |
| 152 | |
| 153 | sniPanel := m.renderSNIPanel() |
| 154 | ipPanel := m.renderIPPanel() |
| 155 | |
| 156 | w := m.app.width |
| 157 | if w < 40 { |
| 158 | w = 40 |
| 159 | } |
| 160 | colW := (w - 6) / 2 |
| 161 | if colW < 30 { |
| 162 | colW = 30 |
| 163 | } |
| 164 | cols := lipgloss.JoinHorizontal(lipgloss.Top, |
| 165 | panelStyle.Width(colW).Render(sniPanel), |
| 166 | panelStyle.Width(colW).Render(ipPanel), |
| 167 | ) |
| 168 | |
| 169 | return lipgloss.JoinVertical(lipgloss.Left, |
| 170 | header, |
| 171 | controls, |
| 172 | targetRow, |
| 173 | status, |
| 174 | "", |
| 175 | cols, |
| 176 | ) |
| 177 | } |
| 178 | |
| 179 | func (m scanModel) renderSNIPanel() string { |
| 180 | title := sectionStyle.Render("SNI probes") |
nothing calls this directly
no test coverage detected