srdusr
aboutsummaryrefslogtreecommitdiffstats
path: root/cmd
diff options
context:
space:
mode:
Diffstat (limited to 'cmd')
-rw-r--r--cmd/mitmux/main.go87
1 files changed, 78 insertions, 9 deletions
diff --git a/cmd/mitmux/main.go b/cmd/mitmux/main.go
index 6d7fe8c..f47461c 100644
--- a/cmd/mitmux/main.go
+++ b/cmd/mitmux/main.go
@@ -13,6 +13,7 @@ import (
"github.com/charmbracelet/bubbles/table"
"github.com/charmbracelet/bubbles/textarea"
+ "github.com/charmbracelet/bubbles/textinput"
"github.com/charmbracelet/bubbles/viewport"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
@@ -94,6 +95,11 @@ type model struct {
entries []store.Summary
table table.Model
+ searchInput textinput.Model
+ searching bool // actively typing a query
+ query string // applied filter, "" means unfiltered
+ pendingNew int // live entries captured while a filter hides them
+
viewport viewport.Model
detail *ipc.EntryDetail
activeTab detailTab
@@ -135,12 +141,17 @@ func newModel(client *ipc.Client, subCh <-chan store.Summary) *model {
ta.Placeholder = "raw request bytes"
ta.ShowLineNumbers = false
+ si := textinput.New()
+ si.Prompt = "/"
+ si.Placeholder = "search - plain text, or host:example.com / AND / OR / NOT"
+
return &model{
- client: client,
- subCh: subCh,
- mode: viewList,
- table: t,
- reqArea: ta,
+ client: client,
+ subCh: subCh,
+ mode: viewList,
+ table: t,
+ reqArea: ta,
+ searchInput: si,
}
}
@@ -166,7 +177,13 @@ type repeatSentMsg struct {
}
func (m *model) loadList() tea.Msg {
- entries, err := m.client.List(500, 0)
+ var entries []store.Summary
+ var err error
+ if m.query != "" {
+ entries, err = m.client.Search(m.query, 500, 0)
+ } else {
+ entries, err = m.client.List(500, 0)
+ }
return listLoadedMsg{entries: entries, err: err}
}
@@ -220,7 +237,8 @@ func (m *model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
m.width, m.height = msg.Width, msg.Height
m.ready = true
m.table.SetWidth(msg.Width)
- m.table.SetHeight(msg.Height - 4)
+ m.table.SetHeight(msg.Height - 5)
+ m.searchInput.Width = msg.Width - 2
m.viewport = viewport.New(msg.Width, msg.Height-5)
reqHeight := (msg.Height - 6) / 2
@@ -243,6 +261,14 @@ func (m *model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
m.statusMsg = "live updates disconnected"
return m, nil
}
+ if m.query != "" {
+ // Whether it matches the active filter isn't knowable without
+ // re-querying; rather than guess, just surface a count and let
+ // the user refresh the search deliberately.
+ m.pendingNew++
+ m.statusMsg = fmt.Sprintf("+%d new captured (re-enter search to refresh)", m.pendingNew)
+ return m, m.waitForEntry
+ }
m.entries = append([]store.Summary{msg.entry}, m.entries...)
m.table.SetRows(rowsFor(m.entries))
return m, m.waitForEntry
@@ -277,6 +303,25 @@ func (m *model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
case tea.KeyMsg:
switch m.mode {
case viewList:
+ if m.searching {
+ switch msg.String() {
+ case "enter":
+ m.searching = false
+ m.query = m.searchInput.Value()
+ m.searchInput.Blur()
+ m.pendingNew = 0
+ m.statusMsg = ""
+ return m, m.loadList
+ case "esc":
+ m.searching = false
+ m.searchInput.Blur()
+ return m, nil
+ }
+ var cmd tea.Cmd
+ m.searchInput, cmd = m.searchInput.Update(msg)
+ return m, cmd
+ }
+
switch msg.String() {
case "q", "ctrl+c":
return m, tea.Quit
@@ -291,6 +336,18 @@ func (m *model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
m.statusMsg = ""
return m, m.loadDetail(m.entries[row].ID, true)
}
+ case "/":
+ m.searching = true
+ m.searchInput.SetValue(m.query)
+ m.searchInput.CursorEnd()
+ return m, m.searchInput.Focus()
+ case "esc":
+ if m.query != "" {
+ m.query = ""
+ m.pendingNew = 0
+ m.statusMsg = ""
+ return m, m.loadList
+ }
}
var cmd tea.Cmd
m.table, cmd = m.table.Update(msg)
@@ -385,15 +442,27 @@ var (
func (m *model) listView() string {
var b strings.Builder
- b.WriteString(titleStyle.Render(fmt.Sprintf(" mitmux - history (%d) ", len(m.entries))))
+ title := fmt.Sprintf(" mitmux - history (%d) ", len(m.entries))
+ if m.query != "" && !m.searching {
+ title = fmt.Sprintf(" mitmux - history (%d matches for %q) ", len(m.entries), m.query)
+ }
+ b.WriteString(titleStyle.Render(title))
b.WriteString("\n")
+ if m.searching {
+ b.WriteString(m.searchInput.View())
+ b.WriteString("\n")
+ }
b.WriteString(m.table.View())
b.WriteString("\n")
if m.statusMsg != "" {
b.WriteString(statusStyle.Render(m.statusMsg))
b.WriteString("\n")
}
- b.WriteString(helpStyle.Render("↑/↓ navigate · enter view · r repeater · q quit"))
+ help := "↑/↓ navigate · enter view · r repeater · / search · q quit"
+ if m.query != "" {
+ help = "↑/↓ navigate · enter view · r repeater · / search · esc clear filter · q quit"
+ }
+ b.WriteString(helpStyle.Render(help))
return b.String()
}