From 28b070dafc547724f815330f169219f0dab10ed0 Mon Sep 17 00:00:00 2001 From: Hadi <112569860+anotherhadi@users.noreply.github.com> Date: Tue, 19 May 2026 14:48:15 +0200 Subject: [PATCH] Add flags to history Signed-off-by: Hadi <112569860+anotherhadi@users.noreply.github.com> --- internal/config/default_config.yaml | 1 + internal/config/keybindings.go | 1 + internal/db/db.go | 3 ++- internal/db/entries.go | 16 ++++++++++++---- internal/icons/icons.go | 2 ++ internal/keys/history.go | 4 +++- internal/ui/history/model.go | 2 +- internal/ui/history/update.go | 6 ++++++ internal/ui/history/view.go | 19 ++++++++++++++++++- 9 files changed, 46 insertions(+), 8 deletions(-) diff --git a/internal/config/default_config.yaml b/internal/config/default_config.yaml index 7ce75e7..95dcc76 100644 --- a/internal/config/default_config.yaml +++ b/internal/config/default_config.yaml @@ -82,6 +82,7 @@ keybindings: delete_all: "X" sql_query: ":" filter: "/" + flag: "m" home: open: "enter,l" diff --git a/internal/config/keybindings.go b/internal/config/keybindings.go index f22a9d5..5052009 100644 --- a/internal/config/keybindings.go +++ b/internal/config/keybindings.go @@ -39,6 +39,7 @@ type HistoryKeys struct { DeleteAll string `mapstructure:"delete_all"` Filter string `mapstructure:"filter"` SqlQuery string `mapstructure:"sql_query"` + Flag string `mapstructure:"flag"` } type HomeKeys struct { diff --git a/internal/db/db.go b/internal/db/db.go index d2a2c19..ff505d6 100644 --- a/internal/db/db.go +++ b/internal/db/db.go @@ -40,7 +40,8 @@ func (d *DB) migrate() error { status_code INTEGER NOT NULL, request_raw TEXT NOT NULL, response_raw TEXT NOT NULL, - body_hash TEXT NOT NULL DEFAULT '' + body_hash TEXT NOT NULL DEFAULT '', + flagged INTEGER NOT NULL DEFAULT 0 ); CREATE TABLE IF NOT EXISTS replay_entries ( id INTEGER PRIMARY KEY AUTOINCREMENT, diff --git a/internal/db/entries.go b/internal/db/entries.go index ffd034b..ccb7bc7 100644 --- a/internal/db/entries.go +++ b/internal/db/entries.go @@ -17,6 +17,7 @@ type Entry struct { StatusCode int RequestRaw string ResponseRaw string + Flagged bool } func bodyHash(body string) string { @@ -71,10 +72,12 @@ func scanEntries(rows *sql.Rows) ([]Entry, error) { for rows.Next() { var e Entry var ts string - if err := rows.Scan(&e.ID, &ts, &e.Method, &e.Host, &e.Path, &e.StatusCode, &e.RequestRaw, &e.ResponseRaw); err != nil { + var flagged int + if err := rows.Scan(&e.ID, &ts, &e.Method, &e.Host, &e.Path, &e.StatusCode, &e.RequestRaw, &e.ResponseRaw, &flagged); err != nil { return nil, err } e.Timestamp, _ = time.Parse(time.RFC3339, ts) + e.Flagged = flagged != 0 entries = append(entries, e) } return entries, rows.Err() @@ -82,7 +85,7 @@ func scanEntries(rows *sql.Rows) ([]Entry, error) { func (d *DB) ListEntries() ([]Entry, error) { rows, err := d.conn.Query( - `SELECT id, timestamp, method, host, path, status_code, request_raw, response_raw + `SELECT id, timestamp, method, host, path, status_code, request_raw, response_raw, flagged FROM entries ORDER BY id DESC`, ) if err != nil { @@ -95,7 +98,7 @@ func (d *DB) ListEntries() ([]Entry, error) { func (d *DB) SearchEntries(term string) ([]Entry, error) { like := "%" + term + "%" rows, err := d.conn.Query( - `SELECT id, timestamp, method, host, path, status_code, request_raw, response_raw + `SELECT id, timestamp, method, host, path, status_code, request_raw, response_raw, flagged FROM entries WHERE method LIKE ? OR host LIKE ? OR path LIKE ? OR request_raw LIKE ? OR response_raw LIKE ? ORDER BY id DESC`, @@ -121,7 +124,7 @@ func (d *DB) QueryEntries(where string) ([]Entry, error) { if _, err := roConn.Exec("PRAGMA query_only=ON"); err != nil { return nil, err } - q := "SELECT id, timestamp, method, host, path, status_code, request_raw, response_raw FROM entries WHERE " + strings.TrimSpace(where) + q := "SELECT id, timestamp, method, host, path, status_code, request_raw, response_raw, flagged FROM entries WHERE " + strings.TrimSpace(where) rows, err := roConn.Query(q) if err != nil { return nil, err @@ -130,6 +133,11 @@ func (d *DB) QueryEntries(where string) ([]Entry, error) { return scanEntries(rows) } +func (d *DB) ToggleFlag(id int64) error { + _, err := d.conn.Exec(`UPDATE entries SET flagged = NOT flagged WHERE id = ?`, id) + return err +} + func (d *DB) DeleteEntry(id int64) error { _, err := d.conn.Exec(`DELETE FROM entries WHERE id = ?`, id) return err diff --git a/internal/icons/icons.go b/internal/icons/icons.go index e81201f..c71ad8f 100644 --- a/internal/icons/icons.go +++ b/internal/icons/icons.go @@ -20,6 +20,7 @@ type Icons struct { New string Temp string Project string + Flag string } var I *Icons @@ -44,6 +45,7 @@ func Init(cfg *config.Config) { New: "󰐕 ", Temp: "󰙨 ", Project: "󰉋 ", + Flag: "󰈻 ", } } else { I = &Icons{} diff --git a/internal/keys/history.go b/internal/keys/history.go index 3426701..47c9a07 100644 --- a/internal/keys/history.go +++ b/internal/keys/history.go @@ -10,6 +10,7 @@ type HistoryKeyMap struct { DeleteAll key.Binding Filter key.Binding SqlQuery key.Binding + Flag key.Binding } func newHistoryKeyMap(cfg config.HistoryKeys) HistoryKeyMap { @@ -18,9 +19,10 @@ func newHistoryKeyMap(cfg config.HistoryKeys) HistoryKeyMap { DeleteAll: binding(cfg.DeleteAll, "delete all"), Filter: binding(cfg.Filter, "filter"), SqlQuery: binding(cfg.SqlQuery, "sql query"), + Flag: binding(cfg.Flag, "flag"), } } func (h HistoryKeyMap) Bindings() []key.Binding { - return []key.Binding{h.DeleteEntry, h.DeleteAll} + return []key.Binding{h.DeleteEntry, h.DeleteAll, h.Flag} } diff --git a/internal/ui/history/model.go b/internal/ui/history/model.go index 77c11a9..58e68f3 100644 --- a/internal/ui/history/model.go +++ b/internal/ui/history/model.go @@ -159,7 +159,7 @@ func (m historyKeyMap) FullHelp() [][]key.Binding { h := keys.Keys.History g := keys.Keys.Global pageGlobals := []key.Binding{g.Up, g.Down, g.CycleFocus, g.ScrollUp, g.ScrollDown, g.Left, g.Right, g.Escape, g.SendToReplay, g.SendToDiff, g.Copy, g.CopyAs} - all := []key.Binding{h.DeleteEntry, h.DeleteAll, h.Filter, h.SqlQuery} + all := []key.Binding{h.Flag, h.DeleteEntry, h.DeleteAll, h.Filter, h.SqlQuery} all = append(all, pageGlobals...) all = append(all, g.CommonBindings()...) return keys.ChunkByWidth(all, m.width) diff --git a/internal/ui/history/update.go b/internal/ui/history/update.go index dfeee03..54180db 100644 --- a/internal/ui/history/update.go +++ b/internal/ui/history/update.go @@ -230,6 +230,12 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { } } + case key.Matches(msg, h.Flag): + if len(m.entries) > 0 && m.database != nil { + m.database.ToggleFlag(m.entries[m.cursor].ID) + return m, m.RefreshCmd() + } + case key.Matches(msg, h.DeleteEntry): if len(m.entries) > 0 { id := m.entries[m.cursor].ID diff --git a/internal/ui/history/view.go b/internal/ui/history/view.go index a5d920b..4513cf7 100644 --- a/internal/ui/history/view.go +++ b/internal/ui/history/view.go @@ -113,7 +113,7 @@ func (m *Model) renderList() string { w := m.listViewport.Width() statusStr := fmt.Sprintf("%3d", e.StatusCode) - const fixedW = 2 + 7 + 1 + 3 + 1 + 10 + 1 + const fixedW = 2 + 2 + 7 + 1 + 3 + 1 + 10 + 1 hostPathW := w - fixedW if hostPathW < 0 { hostPathW = 0 @@ -121,12 +121,21 @@ func (m *Model) renderList() string { ts := e.Timestamp.Format("15:04:05") statusSt := style.StatusStyle(e.StatusCode, 3) + flagSt := lipgloss.NewStyle().Foreground(s.Primary) var line string if selected { bg := lipgloss.NewStyle().Background(selBg) + flagStr := " " + if e.Flagged { + flagStr = icons.I.Flag + " " + if icons.I.Flag == "" { + flagStr = "★ " + } + } line = lipgloss.JoinHorizontal(lipgloss.Top, bg.Bold(true).Foreground(s.Primary).Width(2).Render(">"), + bg.Foreground(s.Primary).Width(2).Render(flagStr), s.Method(e.Method).Background(selBg).Render(e.Method), bg.Width(1).Render(""), statusSt.Background(selBg).Render(statusStr), @@ -136,8 +145,16 @@ func (m *Model) renderList() string { bg.Bold(true).Width(hostPathW).Render(e.Host+e.Path), ) } else { + flagStr := " " + if e.Flagged { + flagStr = icons.I.Flag + " " + if icons.I.Flag == "" { + flagStr = "★ " + } + } line = lipgloss.JoinHorizontal(lipgloss.Top, " ", + flagSt.Width(2).Render(flagStr), s.Method(e.Method).Render(e.Method), " ", statusSt.Render(statusStr),