Add flags to history

Signed-off-by: Hadi <112569860+anotherhadi@users.noreply.github.com>
This commit is contained in:
Hadi
2026-05-19 14:48:15 +02:00
parent 6f56e0b26a
commit 28b070dafc
9 changed files with 46 additions and 8 deletions
+1
View File
@@ -82,6 +82,7 @@ keybindings:
delete_all: "X" delete_all: "X"
sql_query: ":" sql_query: ":"
filter: "/" filter: "/"
flag: "m"
home: home:
open: "enter,l" open: "enter,l"
+1
View File
@@ -39,6 +39,7 @@ type HistoryKeys struct {
DeleteAll string `mapstructure:"delete_all"` DeleteAll string `mapstructure:"delete_all"`
Filter string `mapstructure:"filter"` Filter string `mapstructure:"filter"`
SqlQuery string `mapstructure:"sql_query"` SqlQuery string `mapstructure:"sql_query"`
Flag string `mapstructure:"flag"`
} }
type HomeKeys struct { type HomeKeys struct {
+2 -1
View File
@@ -40,7 +40,8 @@ func (d *DB) migrate() error {
status_code INTEGER NOT NULL, status_code INTEGER NOT NULL,
request_raw TEXT NOT NULL, request_raw TEXT NOT NULL,
response_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 ( CREATE TABLE IF NOT EXISTS replay_entries (
id INTEGER PRIMARY KEY AUTOINCREMENT, id INTEGER PRIMARY KEY AUTOINCREMENT,
+12 -4
View File
@@ -17,6 +17,7 @@ type Entry struct {
StatusCode int StatusCode int
RequestRaw string RequestRaw string
ResponseRaw string ResponseRaw string
Flagged bool
} }
func bodyHash(body string) string { func bodyHash(body string) string {
@@ -71,10 +72,12 @@ func scanEntries(rows *sql.Rows) ([]Entry, error) {
for rows.Next() { for rows.Next() {
var e Entry var e Entry
var ts string 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 return nil, err
} }
e.Timestamp, _ = time.Parse(time.RFC3339, ts) e.Timestamp, _ = time.Parse(time.RFC3339, ts)
e.Flagged = flagged != 0
entries = append(entries, e) entries = append(entries, e)
} }
return entries, rows.Err() return entries, rows.Err()
@@ -82,7 +85,7 @@ func scanEntries(rows *sql.Rows) ([]Entry, error) {
func (d *DB) ListEntries() ([]Entry, error) { func (d *DB) ListEntries() ([]Entry, error) {
rows, err := d.conn.Query( 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`, FROM entries ORDER BY id DESC`,
) )
if err != nil { if err != nil {
@@ -95,7 +98,7 @@ func (d *DB) ListEntries() ([]Entry, error) {
func (d *DB) SearchEntries(term string) ([]Entry, error) { func (d *DB) SearchEntries(term string) ([]Entry, error) {
like := "%" + term + "%" like := "%" + term + "%"
rows, err := d.conn.Query( 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 FROM entries
WHERE method LIKE ? OR host LIKE ? OR path LIKE ? OR request_raw LIKE ? OR response_raw LIKE ? WHERE method LIKE ? OR host LIKE ? OR path LIKE ? OR request_raw LIKE ? OR response_raw LIKE ?
ORDER BY id DESC`, 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 { if _, err := roConn.Exec("PRAGMA query_only=ON"); err != nil {
return nil, err 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) rows, err := roConn.Query(q)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -130,6 +133,11 @@ func (d *DB) QueryEntries(where string) ([]Entry, error) {
return scanEntries(rows) 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 { func (d *DB) DeleteEntry(id int64) error {
_, err := d.conn.Exec(`DELETE FROM entries WHERE id = ?`, id) _, err := d.conn.Exec(`DELETE FROM entries WHERE id = ?`, id)
return err return err
+2
View File
@@ -20,6 +20,7 @@ type Icons struct {
New string New string
Temp string Temp string
Project string Project string
Flag string
} }
var I *Icons var I *Icons
@@ -44,6 +45,7 @@ func Init(cfg *config.Config) {
New: "󰐕 ", New: "󰐕 ",
Temp: "󰙨 ", Temp: "󰙨 ",
Project: "󰉋 ", Project: "󰉋 ",
Flag: "󰈻 ",
} }
} else { } else {
I = &Icons{} I = &Icons{}
+3 -1
View File
@@ -10,6 +10,7 @@ type HistoryKeyMap struct {
DeleteAll key.Binding DeleteAll key.Binding
Filter key.Binding Filter key.Binding
SqlQuery key.Binding SqlQuery key.Binding
Flag key.Binding
} }
func newHistoryKeyMap(cfg config.HistoryKeys) HistoryKeyMap { func newHistoryKeyMap(cfg config.HistoryKeys) HistoryKeyMap {
@@ -18,9 +19,10 @@ func newHistoryKeyMap(cfg config.HistoryKeys) HistoryKeyMap {
DeleteAll: binding(cfg.DeleteAll, "delete all"), DeleteAll: binding(cfg.DeleteAll, "delete all"),
Filter: binding(cfg.Filter, "filter"), Filter: binding(cfg.Filter, "filter"),
SqlQuery: binding(cfg.SqlQuery, "sql query"), SqlQuery: binding(cfg.SqlQuery, "sql query"),
Flag: binding(cfg.Flag, "flag"),
} }
} }
func (h HistoryKeyMap) Bindings() []key.Binding { func (h HistoryKeyMap) Bindings() []key.Binding {
return []key.Binding{h.DeleteEntry, h.DeleteAll} return []key.Binding{h.DeleteEntry, h.DeleteAll, h.Flag}
} }
+1 -1
View File
@@ -159,7 +159,7 @@ func (m historyKeyMap) FullHelp() [][]key.Binding {
h := keys.Keys.History h := keys.Keys.History
g := keys.Keys.Global 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} 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, pageGlobals...)
all = append(all, g.CommonBindings()...) all = append(all, g.CommonBindings()...)
return keys.ChunkByWidth(all, m.width) return keys.ChunkByWidth(all, m.width)
+6
View File
@@ -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): case key.Matches(msg, h.DeleteEntry):
if len(m.entries) > 0 { if len(m.entries) > 0 {
id := m.entries[m.cursor].ID id := m.entries[m.cursor].ID
+18 -1
View File
@@ -113,7 +113,7 @@ func (m *Model) renderList() string {
w := m.listViewport.Width() w := m.listViewport.Width()
statusStr := fmt.Sprintf("%3d", e.StatusCode) 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 hostPathW := w - fixedW
if hostPathW < 0 { if hostPathW < 0 {
hostPathW = 0 hostPathW = 0
@@ -121,12 +121,21 @@ func (m *Model) renderList() string {
ts := e.Timestamp.Format("15:04:05") ts := e.Timestamp.Format("15:04:05")
statusSt := style.StatusStyle(e.StatusCode, 3) statusSt := style.StatusStyle(e.StatusCode, 3)
flagSt := lipgloss.NewStyle().Foreground(s.Primary)
var line string var line string
if selected { if selected {
bg := lipgloss.NewStyle().Background(selBg) bg := lipgloss.NewStyle().Background(selBg)
flagStr := " "
if e.Flagged {
flagStr = icons.I.Flag + " "
if icons.I.Flag == "" {
flagStr = "★ "
}
}
line = lipgloss.JoinHorizontal(lipgloss.Top, line = lipgloss.JoinHorizontal(lipgloss.Top,
bg.Bold(true).Foreground(s.Primary).Width(2).Render(">"), 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), s.Method(e.Method).Background(selBg).Render(e.Method),
bg.Width(1).Render(""), bg.Width(1).Render(""),
statusSt.Background(selBg).Render(statusStr), statusSt.Background(selBg).Render(statusStr),
@@ -136,8 +145,16 @@ func (m *Model) renderList() string {
bg.Bold(true).Width(hostPathW).Render(e.Host+e.Path), bg.Bold(true).Width(hostPathW).Render(e.Host+e.Path),
) )
} else { } else {
flagStr := " "
if e.Flagged {
flagStr = icons.I.Flag + " "
if icons.I.Flag == "" {
flagStr = "★ "
}
}
line = lipgloss.JoinHorizontal(lipgloss.Top, line = lipgloss.JoinHorizontal(lipgloss.Top,
" ", " ",
flagSt.Width(2).Render(flagStr),
s.Method(e.Method).Render(e.Method), s.Method(e.Method).Render(e.Method),
" ", " ",
statusSt.Render(statusStr), statusSt.Render(statusStr),