v0.33.2 57f21d6

history.go RawHistoryFolder

1package cmd
2
3import (
4	"encoding/json"
5	"fmt"
6	"os"
7
8	tea "github.com/charmbracelet/bubbletea"
9	"github.com/spf13/cobra"
10
11	"github.com/bnema/dumber/internal/cli/model"
12)
13
14var (
15	historyJSON bool
16	historyMax  int
17)
18
19const defaultHistoryMax = 50
20
21var historyCmd = &cobra.Command{
22	Use:   "history",
23	Short: "Browse and manage history",
24	Long:  `Interactive history browser with timeline tabs, fuzzy search, and cleanup.`,
25	RunE:  runHistory,
26}
27
28func init() {
29	rootCmd.AddCommand(historyCmd)
30
31	historyCmd.Flags().BoolVar(&historyJSON, "json", false, "output as JSON")
32	historyCmd.Flags().IntVar(&historyMax, "max", defaultHistoryMax, "maximum entries to show (for --json)")
33}
34
35func runHistory(_ *cobra.Command, _ []string) error {
36	app := GetApp()
37	if app == nil {
38		return fmt.Errorf("app not initialized")
39	}
40
41	// JSON output mode (non-interactive)
42	if historyJSON {
43		return runHistoryJSON()
44	}
45
46	// Interactive TUI mode
47	return runHistoryTUI()
48}
49
50// runHistoryTUI runs the interactive history browser.
51func runHistoryTUI() error {
52	app := GetApp()
53
54	m := model.NewHistoryModel(app.Ctx(), app.Theme, app.SearchHistoryUC)
55
56	p := tea.NewProgram(m, tea.WithAltScreen())
57	_, err := p.Run()
58	return err
59}
60
61// runHistoryJSON outputs history as JSON.
62func runHistoryJSON() error {
63	app := GetApp()
64
65	m := model.NewHistoryListModel(app.Ctx(), app.SearchHistoryUC, historyMax)
66
67	// Run briefly to load data
68	p := tea.NewProgram(m)
69	finalModel, err := p.Run()
70	if err != nil {
71		return fmt.Errorf("run model: %w", err)
72	}
73
74	// Extract results
75	listModel, ok := finalModel.(model.HistoryListModel)
76	if !ok {
77		return fmt.Errorf("unexpected model type")
78	}
79
80	if listModel.Error() != nil {
81		return listModel.Error()
82	}
83
84	// Output as JSON
85	entries := listModel.Entries()
86	enc := json.NewEncoder(os.Stdout)
87	enc.SetIndent("", "  ")
88	return enc.Encode(entries)
89}
90
91// statsCmd shows history statistics.
92var statsCmd = &cobra.Command{
93	Use:   "stats",
94	Short: "Show history statistics",
95	Long:  `Display analytics about your browsing history.`,
96	RunE:  runStats,
97}
98
99func init() {
100	historyCmd.AddCommand(statsCmd)
101}
102
103func runStats(_ *cobra.Command, _ []string) error {
104	app := GetApp()
105	if app == nil {
106		return fmt.Errorf("app not initialized")
107	}
108
109	// Run stats model
110	m := model.NewStatsModel(app.Theme, app.SearchHistoryUC)
111
112	p := tea.NewProgram(m)
113	_, err := p.Run()
114	return err
115}
116
117// clearCmd clears history.
118var clearCmd = &cobra.Command{
119	Use:   "clear",
120	Short: "Clear history",
121	Long:  `Interactively select time range to clear history.`,
122	RunE:  runClear,
123}
124
125func init() {
126	historyCmd.AddCommand(clearCmd)
127}
128
129func runClear(_ *cobra.Command, _ []string) error {
130	app := GetApp()
131	if app == nil {
132		return fmt.Errorf("app not initialized")
133	}
134
135	// Run cleanup modal directly
136	m := model.NewCleanupModel(app.Theme, app.SearchHistoryUC)
137
138	p := tea.NewProgram(m)
139	_, err := p.Run()
140	return err
141}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 8

Tags 42

↑↓ moveenter openesc close