detached 40983c7

feat(dmenu): integrate DmenuConfig into CLI command with formatting support

commit
40983c7d77da3f0163cbdb7df57d7ee982ffbe35
author
brice · · 2025-12-18 17:21 UTC
parent
6bf5e05
signature
unsigned
2 files · +57 -75
M docs/CONFIG.md +25 -70
@@ -68,17 +68,31 @@ description = "YouTube"
6868
6969 ## Dmenu / Launcher
7070
71+These settings control the `dumber dmenu` CLI command for rofi/fuzzel integration.
72+
7173 | Key | Type | Default | Description |
7274 |-----|------|---------|-------------|
7375 | `dmenu.max_history_items` | int | `20` | Max items shown in launcher |
74-| `dmenu.show_visit_count` | bool | `true` | Show visit counts |
75-| `dmenu.show_last_visited` | bool | `true` | Show last visited dates |
76+| `dmenu.show_visit_count` | bool | `true` | Show visit counts in output |
77+| `dmenu.show_last_visited` | bool | `true` | Show last visited dates in output |
7678 | `dmenu.history_prefix` | string | `"🕒"` | Prefix for history items |
7779 | `dmenu.shortcut_prefix` | string | `"🔍"` | Prefix for shortcuts |
7880 | `dmenu.url_prefix` | string | `"🌐"` | Prefix for URLs |
7981 | `dmenu.date_format` | string | `"2006-01-02 15:04"` | Go time format string |
8082 | `dmenu.sort_by_visit_count` | bool | `true` | Sort by popularity |
8183
84+**CLI Usage:**
85+```bash
86+# Pipe mode (default) - outputs history for rofi/fuzzel
87+dumber dmenu | rofi -dmenu -p "Browse: " | dumber dmenu --select
88+
89+# Interactive TUI mode
90+dumber dmenu --interactive
91+
92+# Override max items via CLI flag
93+dumber dmenu --max 50
94+```
95+
8296 ## Omnibox
8397
8498 | Key | Type | Default | Valid Values | Description |
@@ -101,19 +115,10 @@ initial_behavior = "recent" # Show recent history when omnibox opens
101115 |-----|------|---------|--------------|-------------|
102116 | `logging.level` | string | `"info"` | `debug`, `info`, `warn`, `error` | Log level |
103117 | `logging.format` | string | `"text"` | `text`, `json` | Log format |
104-| `logging.filename` | string | `""` | any | Empty = stdout |
105-| `logging.max_size` | int | `100` | > 0 | Max log size in MB |
106-| `logging.max_backups` | int | `3` | >= 0 | Number of log backups |
107118 | `logging.max_age` | int | `7` | >= 0 | Days to keep logs |
108-| `logging.compress` | bool | `true` | - | Compress old logs |
109119 | `logging.log_dir` | string | `~/.local/state/dumber/logs/` | any | Log directory |
110120 | `logging.enable_file_log` | bool | `true` | - | Enable file logging |
111-| `logging.capture_stdout` | bool | `false` | - | Capture stdout to log |
112-| `logging.capture_stderr` | bool | `false` | - | Capture stderr to log |
113-| `logging.capture_c_output` | bool | `false` | - | Capture C/CGO output |
114-| `logging.capture_console` | bool | `false` | - | Capture browser console |
115-| `logging.debug_file` | string | `"debug.log"` | any | Debug log filename |
116-| `logging.verbose_webkit` | bool | `false` | - | Verbose WebKit logs |
121+| `logging.capture_console` | bool | `false` | - | Capture browser console to logs |
117122
118123 ## Appearance
119124
@@ -151,48 +156,11 @@ accent = "#a8a8a8"
151156 border = "#363636"
152157 ```
153158
154-## Video Acceleration
155-
156-| Key | Type | Default | Description |
157-|-----|------|---------|-------------|
158-| `video_acceleration.enable_vaapi` | bool | `true` | Enable VA-API hardware accel |
159-| `video_acceleration.auto_detect_gpu` | bool | `true` | Auto-detect GPU driver |
160-| `video_acceleration.vaapi_driver_name` | string | `""` | Driver name (auto-detected) |
161-| `video_acceleration.enable_all_drivers` | bool | `true` | Enable all VA-API drivers |
162-| `video_acceleration.legacy_vaapi` | bool | `false` | Legacy VA-API mode |
163-
164-## Codec Preferences
165-
166-| Key | Type | Default | Valid Values | Description |
167-|-----|------|---------|--------------|-------------|
168-| `codec_preferences.preferred_codecs` | string | `"av1,h264"` | Comma-separated | Codec priority order |
169-| `codec_preferences.force_av1` | bool | `false` | - | Force AV1 codec |
170-| `codec_preferences.block_vp9` | bool | `false` | - | Block VP9 codec |
171-| `codec_preferences.block_vp8` | bool | `false` | - | Block VP8 codec |
172-| `codec_preferences.av1_hardware_only` | bool | `false` | - | Disable AV1 software fallback |
173-| `codec_preferences.disable_vp9_hardware` | bool | `false` | - | Disable VP9 hardware accel |
174-| `codec_preferences.video_buffer_size_mb` | int | `64` | > 0 | Video buffer size in MB |
175-| `codec_preferences.queue_buffer_time_sec` | int | `20` | > 0 | Queue buffer time in seconds |
176-| `codec_preferences.custom_user_agent` | string | Chrome UA | any | Custom user agent string |
177-| `codec_preferences.av1_max_resolution` | string | `"1080p"` | `720p`, `1080p`, `1440p`, `4k`, `unlimited` | Max AV1 resolution |
178-| `codec_preferences.disable_twitch_codec_control` | bool | `true` | - | Disable codec control on Twitch |
179-
180159 ## Debug Options
181160
182161 | Key | Type | Default | Description |
183162 |-----|------|---------|-------------|
184-| `debug.enable_webkit_debug` | bool | `false` | Enable WebKit debug logs |
185-| `debug.webkit_debug_categories` | string | `"Network:preconnectTo,ContentFilters"` | Debug categories |
186-| `debug.enable_filtering_debug` | bool | `false` | Content filter debug |
187-| `debug.enable_webview_debug` | bool | `false` | WebView state debug |
188-| `debug.log_webkit_crashes` | bool | `true` | Log WebKit crashes |
189-| `debug.enable_script_debug` | bool | `false` | Script injection debug |
190-| `debug.enable_general_debug` | bool | `false` | General debug mode |
191-| `debug.enable_workspace_debug` | bool | `false` | Workspace navigation debug |
192-| `debug.enable_focus_debug` | bool | `false` | Focus state debug |
193-| `debug.enable_css_debug` | bool | `false` | CSS reconciler debug |
194-| `debug.enable_focus_metrics` | bool | `false` | Focus metrics tracking |
195-| `debug.enable_pane_close_debug` | bool | `false` | Pane close debug |
163+| `debug.enable_devtools` | bool | `true` | Enable browser developer tools (F12, Inspect Element) |
196164
197165 ## Rendering & Zoom
198166
@@ -287,19 +255,9 @@ cancel = ["escape"]
287255 | Key | Type | Default | Description |
288256 |-----|------|---------|-------------|
289257 | `content_filtering.enabled` | bool | `true` | Enable ad blocking |
290-| `content_filtering.whitelist` | array | See below | Domains to skip ad blocking |
291258 | `content_filtering.filter_lists` | array | See below | Filter list URLs to use |
292259
293-**Default whitelist:**
294-```toml
295-[content_filtering]
296-enabled = true
297-whitelist = [
298- "twitch.tv",
299- "passport.twitch.tv",
300- "gql.twitch.tv"
301-]
302-```
260+> **Note:** Domain whitelist is managed via the database (`content_whitelist` table), not the config file.
303261
304262 **Default filter lists:**
305263 ```toml
@@ -329,20 +287,17 @@ filter_lists = [
329287
330288 ## Environment Variables
331289
332-All config values can be overridden via environment variables with the prefix `DUMB_BROWSER_`:
290+All config values can be overridden via environment variables with the prefix `DUMBER_`:
333291
334292 ```bash
335293 # Database
336-DUMB_BROWSER_DATABASE_PATH=/custom/path/db.sqlite
294+DUMBER_DATABASE_PATH=/custom/path/db.sqlite
337295
338296 # Rendering
339297 DUMBER_RENDERING_MODE=cpu
340-DUMBER_DEFAULT_ZOOM=1.5
298+DUMBER_DEFAULT_WEBPAGE_ZOOM=1.5
341299
342-# Video/Codec
343-DUMBER_VIDEO_ACCELERATION_ENABLE=true
344-LIBVA_DRIVER_NAME=iHD
345-DUMBER_PREFERRED_CODECS=av1,vp9,h264
300+# Logging
301+DUMBER_LOG_LEVEL=debug
302+DUMBER_LOG_FORMAT=json
346303 ```
347-
348-**Note:** Some video/codec env vars use `DUMBER_*` prefix and match GStreamer/VA-API conventions.
@@ -68,17 +68,31 @@ description = "YouTube"
68 68
69 ## Dmenu / Launcher69 ## Dmenu / Launcher
70 70
71+These settings control the `dumber dmenu` CLI command for rofi/fuzzel integration.
72+
71 | Key | Type | Default | Description |73 | Key | Type | Default | Description |
72 |-----|------|---------|-------------|74 |-----|------|---------|-------------|
73 | `dmenu.max_history_items` | int | `20` | Max items shown in launcher |75 | `dmenu.max_history_items` | int | `20` | Max items shown in launcher |
74-| `dmenu.show_visit_count` | bool | `true` | Show visit counts |76+| `dmenu.show_visit_count` | bool | `true` | Show visit counts in output |
75-| `dmenu.show_last_visited` | bool | `true` | Show last visited dates |77+| `dmenu.show_last_visited` | bool | `true` | Show last visited dates in output |
76 | `dmenu.history_prefix` | string | `"🕒"` | Prefix for history items |78 | `dmenu.history_prefix` | string | `"🕒"` | Prefix for history items |
77 | `dmenu.shortcut_prefix` | string | `"🔍"` | Prefix for shortcuts |79 | `dmenu.shortcut_prefix` | string | `"🔍"` | Prefix for shortcuts |
78 | `dmenu.url_prefix` | string | `"🌐"` | Prefix for URLs |80 | `dmenu.url_prefix` | string | `"🌐"` | Prefix for URLs |
79 | `dmenu.date_format` | string | `"2006-01-02 15:04"` | Go time format string |81 | `dmenu.date_format` | string | `"2006-01-02 15:04"` | Go time format string |
80 | `dmenu.sort_by_visit_count` | bool | `true` | Sort by popularity |82 | `dmenu.sort_by_visit_count` | bool | `true` | Sort by popularity |
81 83
84+**CLI Usage:**
85+```bash
86+# Pipe mode (default) - outputs history for rofi/fuzzel
87+dumber dmenu | rofi -dmenu -p "Browse: " | dumber dmenu --select
88+
89+# Interactive TUI mode
90+dumber dmenu --interactive
91+
92+# Override max items via CLI flag
93+dumber dmenu --max 50
94+```
95+
82 ## Omnibox96 ## Omnibox
83 97
84 | Key | Type | Default | Valid Values | Description |98 | Key | Type | Default | Valid Values | Description |
@@ -101,19 +115,10 @@ initial_behavior = "recent" # Show recent history when omnibox opens
101 |-----|------|---------|--------------|-------------|115 |-----|------|---------|--------------|-------------|
102 | `logging.level` | string | `"info"` | `debug`, `info`, `warn`, `error` | Log level |116 | `logging.level` | string | `"info"` | `debug`, `info`, `warn`, `error` | Log level |
103 | `logging.format` | string | `"text"` | `text`, `json` | Log format |117 | `logging.format` | string | `"text"` | `text`, `json` | Log format |
104-| `logging.filename` | string | `""` | any | Empty = stdout |
105-| `logging.max_size` | int | `100` | > 0 | Max log size in MB |
106-| `logging.max_backups` | int | `3` | >= 0 | Number of log backups |
107 | `logging.max_age` | int | `7` | >= 0 | Days to keep logs |118 | `logging.max_age` | int | `7` | >= 0 | Days to keep logs |
108-| `logging.compress` | bool | `true` | - | Compress old logs |
109 | `logging.log_dir` | string | `~/.local/state/dumber/logs/` | any | Log directory |119 | `logging.log_dir` | string | `~/.local/state/dumber/logs/` | any | Log directory |
110 | `logging.enable_file_log` | bool | `true` | - | Enable file logging |120 | `logging.enable_file_log` | bool | `true` | - | Enable file logging |
111-| `logging.capture_stdout` | bool | `false` | - | Capture stdout to log |121+| `logging.capture_console` | bool | `false` | - | Capture browser console to logs |
112-| `logging.capture_stderr` | bool | `false` | - | Capture stderr to log |
113-| `logging.capture_c_output` | bool | `false` | - | Capture C/CGO output |
114-| `logging.capture_console` | bool | `false` | - | Capture browser console |
115-| `logging.debug_file` | string | `"debug.log"` | any | Debug log filename |
116-| `logging.verbose_webkit` | bool | `false` | - | Verbose WebKit logs |
117 122
118 ## Appearance123 ## Appearance
119 124
@@ -151,48 +156,11 @@ accent = "#a8a8a8"
151 border = "#363636"156 border = "#363636"
152 ```157 ```
153 158
154-## Video Acceleration
155-
156-| Key | Type | Default | Description |
157-|-----|------|---------|-------------|
158-| `video_acceleration.enable_vaapi` | bool | `true` | Enable VA-API hardware accel |
159-| `video_acceleration.auto_detect_gpu` | bool | `true` | Auto-detect GPU driver |
160-| `video_acceleration.vaapi_driver_name` | string | `""` | Driver name (auto-detected) |
161-| `video_acceleration.enable_all_drivers` | bool | `true` | Enable all VA-API drivers |
162-| `video_acceleration.legacy_vaapi` | bool | `false` | Legacy VA-API mode |
163-
164-## Codec Preferences
165-
166-| Key | Type | Default | Valid Values | Description |
167-|-----|------|---------|--------------|-------------|
168-| `codec_preferences.preferred_codecs` | string | `"av1,h264"` | Comma-separated | Codec priority order |
169-| `codec_preferences.force_av1` | bool | `false` | - | Force AV1 codec |
170-| `codec_preferences.block_vp9` | bool | `false` | - | Block VP9 codec |
171-| `codec_preferences.block_vp8` | bool | `false` | - | Block VP8 codec |
172-| `codec_preferences.av1_hardware_only` | bool | `false` | - | Disable AV1 software fallback |
173-| `codec_preferences.disable_vp9_hardware` | bool | `false` | - | Disable VP9 hardware accel |
174-| `codec_preferences.video_buffer_size_mb` | int | `64` | > 0 | Video buffer size in MB |
175-| `codec_preferences.queue_buffer_time_sec` | int | `20` | > 0 | Queue buffer time in seconds |
176-| `codec_preferences.custom_user_agent` | string | Chrome UA | any | Custom user agent string |
177-| `codec_preferences.av1_max_resolution` | string | `"1080p"` | `720p`, `1080p`, `1440p`, `4k`, `unlimited` | Max AV1 resolution |
178-| `codec_preferences.disable_twitch_codec_control` | bool | `true` | - | Disable codec control on Twitch |
179-
180 ## Debug Options159 ## Debug Options
181 160
182 | Key | Type | Default | Description |161 | Key | Type | Default | Description |
183 |-----|------|---------|-------------|162 |-----|------|---------|-------------|
184-| `debug.enable_webkit_debug` | bool | `false` | Enable WebKit debug logs |163+| `debug.enable_devtools` | bool | `true` | Enable browser developer tools (F12, Inspect Element) |
185-| `debug.webkit_debug_categories` | string | `"Network:preconnectTo,ContentFilters"` | Debug categories |
186-| `debug.enable_filtering_debug` | bool | `false` | Content filter debug |
187-| `debug.enable_webview_debug` | bool | `false` | WebView state debug |
188-| `debug.log_webkit_crashes` | bool | `true` | Log WebKit crashes |
189-| `debug.enable_script_debug` | bool | `false` | Script injection debug |
190-| `debug.enable_general_debug` | bool | `false` | General debug mode |
191-| `debug.enable_workspace_debug` | bool | `false` | Workspace navigation debug |
192-| `debug.enable_focus_debug` | bool | `false` | Focus state debug |
193-| `debug.enable_css_debug` | bool | `false` | CSS reconciler debug |
194-| `debug.enable_focus_metrics` | bool | `false` | Focus metrics tracking |
195-| `debug.enable_pane_close_debug` | bool | `false` | Pane close debug |
196 164
197 ## Rendering & Zoom165 ## Rendering & Zoom
198 166
@@ -287,19 +255,9 @@ cancel = ["escape"]
287 | Key | Type | Default | Description |255 | Key | Type | Default | Description |
288 |-----|------|---------|-------------|256 |-----|------|---------|-------------|
289 | `content_filtering.enabled` | bool | `true` | Enable ad blocking |257 | `content_filtering.enabled` | bool | `true` | Enable ad blocking |
290-| `content_filtering.whitelist` | array | See below | Domains to skip ad blocking |
291 | `content_filtering.filter_lists` | array | See below | Filter list URLs to use |258 | `content_filtering.filter_lists` | array | See below | Filter list URLs to use |
292 259
293-**Default whitelist:**260+> **Note:** Domain whitelist is managed via the database (`content_whitelist` table), not the config file.
294-```toml
295-[content_filtering]
296-enabled = true
297-whitelist = [
298- "twitch.tv",
299- "passport.twitch.tv",
300- "gql.twitch.tv"
301-]
302-```
303 261
304 **Default filter lists:**262 **Default filter lists:**
305 ```toml263 ```toml
@@ -329,20 +287,17 @@ filter_lists = [
329 287
330 ## Environment Variables288 ## Environment Variables
331 289
332-All config values can be overridden via environment variables with the prefix `DUMB_BROWSER_`:290+All config values can be overridden via environment variables with the prefix `DUMBER_`:
333 291
334 ```bash292 ```bash
335 # Database293 # Database
336-DUMB_BROWSER_DATABASE_PATH=/custom/path/db.sqlite294+DUMBER_DATABASE_PATH=/custom/path/db.sqlite
337 295
338 # Rendering296 # Rendering
339 DUMBER_RENDERING_MODE=cpu297 DUMBER_RENDERING_MODE=cpu
340-DUMBER_DEFAULT_ZOOM=1.5298+DUMBER_DEFAULT_WEBPAGE_ZOOM=1.5
341 299
342-# Video/Codec300+# Logging
343-DUMBER_VIDEO_ACCELERATION_ENABLE=true301+DUMBER_LOG_LEVEL=debug
344-LIBVA_DRIVER_NAME=iHD302+DUMBER_LOG_FORMAT=json
345-DUMBER_PREFERRED_CODECS=av1,vp9,h264
346 ```303 ```
347-
348-**Note:** Some video/codec env vars use `DUMBER_*` prefix and match GStreamer/VA-API conventions.
M internal/cli/cmd/dmenu.go +32 -5
@@ -12,6 +12,7 @@ import (
1212 "github.com/spf13/cobra"
1313
1414 "github.com/bnema/dumber/internal/cli/model"
15+ "github.com/bnema/dumber/internal/infrastructure/config"
1516 )
1617
1718 var (
@@ -38,7 +39,7 @@ func init() {
3839
3940 dmenuCmd.Flags().BoolVarP(&dmenuInteractive, "interactive", "i", false, "use interactive TUI mode")
4041 dmenuCmd.Flags().BoolVar(&dmenuSelect, "select", false, "process selection from stdin")
41- dmenuCmd.Flags().IntVar(&dmenuMax, "max", 100, "maximum entries to output")
42+ dmenuCmd.Flags().IntVar(&dmenuMax, "max", 0, "maximum entries to output (default from config)")
4243 }
4344
4445 func runDmenu(cmd *cobra.Command, args []string) error {
@@ -61,18 +62,31 @@ func runDmenu(cmd *cobra.Command, args []string) error {
6162 return runDmenuPipe()
6263 }
6364
65+// getDmenuConfig returns the dmenu configuration, with CLI flag overrides.
66+func getDmenuConfig() config.DmenuConfig {
67+ app := GetApp()
68+ cfg := app.Config.Dmenu
69+
70+ // Override max if explicitly set via CLI flag
71+ if dmenuMax > 0 {
72+ cfg.MaxHistoryItems = dmenuMax
73+ }
74+
75+ return cfg
76+}
77+
6478 // runDmenuPipe outputs history entries for launcher consumption.
6579 func runDmenuPipe() error {
6680 app := GetApp()
6781 ctx := context.Background()
82+ cfg := getDmenuConfig()
6883
69- entries, err := app.SearchHistoryUC.GetRecent(ctx, dmenuMax, 0)
84+ entries, err := app.SearchHistoryUC.GetRecent(ctx, cfg.MaxHistoryItems, 0)
7085 if err != nil {
7186 return fmt.Errorf("get history: %w", err)
7287 }
7388
74- // Output in rofi-compatible format with icon hints
75- // Format: text\0icon\x1ficonpath
89+ // Output in rofi-compatible format
7690 for _, entry := range entries {
7791 title := entry.Title
7892 if title == "" {
@@ -84,9 +98,22 @@ func runDmenuPipe() error {
8498 title = title[:77] + "..."
8599 }
86100
101+ // Build output line based on config
102+ var parts []string
103+ parts = append(parts, cfg.HistoryPrefix+" "+title)
104+
105+ if cfg.ShowVisitCount && entry.VisitCount > 0 {
106+ parts[0] = fmt.Sprintf("%s (%d)", parts[0], entry.VisitCount)
107+ }
108+
109+ if cfg.ShowLastVisited && !entry.LastVisited.IsZero() {
110+ dateStr := entry.LastVisited.Format(cfg.DateFormat)
111+ parts[0] = fmt.Sprintf("%s [%s]", parts[0], dateStr)
112+ }
113+
87114 // Output with URL as the actual value (for selection)
88115 // Using tab separator so rofi can use -d '\t'
89- fmt.Printf("%s\t%s\n", title, entry.URL)
116+ fmt.Printf("%s\t%s\n", parts[0], entry.URL)
90117 }
91118
92119 return nil
@@ -12,6 +12,7 @@ import (
12 "github.com/spf13/cobra"12 "github.com/spf13/cobra"
13 13
14 "github.com/bnema/dumber/internal/cli/model"14 "github.com/bnema/dumber/internal/cli/model"
15+ "github.com/bnema/dumber/internal/infrastructure/config"
15 )16 )
16 17
17 var (18 var (
@@ -38,7 +39,7 @@ func init() {
38 39
39 dmenuCmd.Flags().BoolVarP(&dmenuInteractive, "interactive", "i", false, "use interactive TUI mode")40 dmenuCmd.Flags().BoolVarP(&dmenuInteractive, "interactive", "i", false, "use interactive TUI mode")
40 dmenuCmd.Flags().BoolVar(&dmenuSelect, "select", false, "process selection from stdin")41 dmenuCmd.Flags().BoolVar(&dmenuSelect, "select", false, "process selection from stdin")
41- dmenuCmd.Flags().IntVar(&dmenuMax, "max", 100, "maximum entries to output")42+ dmenuCmd.Flags().IntVar(&dmenuMax, "max", 0, "maximum entries to output (default from config)")
42 }43 }
43 44
44 func runDmenu(cmd *cobra.Command, args []string) error {45 func runDmenu(cmd *cobra.Command, args []string) error {
@@ -61,18 +62,31 @@ func runDmenu(cmd *cobra.Command, args []string) error {
61 return runDmenuPipe()62 return runDmenuPipe()
62 }63 }
63 64
65+// getDmenuConfig returns the dmenu configuration, with CLI flag overrides.
66+func getDmenuConfig() config.DmenuConfig {
67+ app := GetApp()
68+ cfg := app.Config.Dmenu
69+
70+ // Override max if explicitly set via CLI flag
71+ if dmenuMax > 0 {
72+ cfg.MaxHistoryItems = dmenuMax
73+ }
74+
75+ return cfg
76+}
77+
64 // runDmenuPipe outputs history entries for launcher consumption.78 // runDmenuPipe outputs history entries for launcher consumption.
65 func runDmenuPipe() error {79 func runDmenuPipe() error {
66 app := GetApp()80 app := GetApp()
67 ctx := context.Background()81 ctx := context.Background()
82+ cfg := getDmenuConfig()
68 83
69- entries, err := app.SearchHistoryUC.GetRecent(ctx, dmenuMax, 0)84+ entries, err := app.SearchHistoryUC.GetRecent(ctx, cfg.MaxHistoryItems, 0)
70 if err != nil {85 if err != nil {
71 return fmt.Errorf("get history: %w", err)86 return fmt.Errorf("get history: %w", err)
72 }87 }
73 88
74- // Output in rofi-compatible format with icon hints89+ // Output in rofi-compatible format
75- // Format: text\0icon\x1ficonpath
76 for _, entry := range entries {90 for _, entry := range entries {
77 title := entry.Title91 title := entry.Title
78 if title == "" {92 if title == "" {
@@ -84,9 +98,22 @@ func runDmenuPipe() error {
84 title = title[:77] + "..."98 title = title[:77] + "..."
85 }99 }
86 100
101+ // Build output line based on config
102+ var parts []string
103+ parts = append(parts, cfg.HistoryPrefix+" "+title)
104+
105+ if cfg.ShowVisitCount && entry.VisitCount > 0 {
106+ parts[0] = fmt.Sprintf("%s (%d)", parts[0], entry.VisitCount)
107+ }
108+
109+ if cfg.ShowLastVisited && !entry.LastVisited.IsZero() {
110+ dateStr := entry.LastVisited.Format(cfg.DateFormat)
111+ parts[0] = fmt.Sprintf("%s [%s]", parts[0], dateStr)
112+ }
113+
87 // Output with URL as the actual value (for selection)114 // Output with URL as the actual value (for selection)
88 // Using tab separator so rofi can use -d '\t'115 // Using tab separator so rofi can use -d '\t'
89- fmt.Printf("%s\t%s\n", title, entry.URL)116+ fmt.Printf("%s\t%s\n", parts[0], entry.URL)
90 }117 }
91 118
92 return nil119 return nil

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