detached 2d9aebe

cli: address review feedback for non-TUI style alignment

commit
2d9aebe2c40b476cfa9836b7592f499253e07528
author
bnema · · 2026-02-11 19:38 UTC
parent
88024fa
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
4 files · +33 -23
M internal/cli/cmd/config.go +3 -5
@@ -114,27 +114,26 @@ func runConfigOpen(_ *cobra.Command, _ []string) error {
114114 return fmt.Errorf("app not initialized")
115115 }
116116
117+ renderer := styles.NewConfigRenderer(app.Theme)
118+
117119 configFile, err := config.GetConfigFile()
118120 if err != nil {
119- renderer := styles.NewConfigRenderer(app.Theme)
120121 return fmt.Errorf("%s", strings.TrimRight(renderer.RenderError(fmt.Errorf("get config file: %w", err)), "\n"))
121122 }
122123
123124 // Check if config file exists
124125 if _, statErr := os.Stat(configFile); os.IsNotExist(statErr) {
125- renderer := styles.NewConfigRenderer(app.Theme)
126126 return fmt.Errorf("%s", strings.TrimRight(renderer.RenderError(fmt.Errorf("config file does not exist: %s", configFile)), "\n"))
127127 }
128128
129129 editor := getEditor()
130130 if editor == "" {
131- renderer := styles.NewConfigRenderer(app.Theme)
132131 const msg = "no editor found: set $EDITOR or $VISUAL environment variable"
133132 return fmt.Errorf("%s", strings.TrimRight(renderer.RenderError(fmt.Errorf("%s", msg)), "\n"))
134133 }
135134
136135 // Keep this a simple, non-interactive message; the actual editor takes over the terminal.
137- fmt.Println(styles.NewConfigRenderer(app.Theme).RenderOpening(configFile, editor))
136+ fmt.Println(renderer.RenderOpening(configFile, editor))
138137
139138 cmd := exec.Command(editor, configFile)
140139 cmd.Stdin = os.Stdin
@@ -142,7 +141,6 @@ func runConfigOpen(_ *cobra.Command, _ []string) error {
142141 cmd.Stderr = os.Stderr
143142
144143 if err := cmd.Run(); err != nil {
145- renderer := styles.NewConfigRenderer(app.Theme)
146144 return fmt.Errorf("%s", strings.TrimRight(renderer.RenderError(fmt.Errorf("open config: %w", err)), "\n"))
147145 }
148146 return nil
@@ -114,27 +114,26 @@ func runConfigOpen(_ *cobra.Command, _ []string) error {
114 return fmt.Errorf("app not initialized")114 return fmt.Errorf("app not initialized")
115 }115 }
116 116
117+ renderer := styles.NewConfigRenderer(app.Theme)
118+
117 configFile, err := config.GetConfigFile()119 configFile, err := config.GetConfigFile()
118 if err != nil {120 if err != nil {
119- renderer := styles.NewConfigRenderer(app.Theme)
120 return fmt.Errorf("%s", strings.TrimRight(renderer.RenderError(fmt.Errorf("get config file: %w", err)), "\n"))121 return fmt.Errorf("%s", strings.TrimRight(renderer.RenderError(fmt.Errorf("get config file: %w", err)), "\n"))
121 }122 }
122 123
123 // Check if config file exists124 // Check if config file exists
124 if _, statErr := os.Stat(configFile); os.IsNotExist(statErr) {125 if _, statErr := os.Stat(configFile); os.IsNotExist(statErr) {
125- renderer := styles.NewConfigRenderer(app.Theme)
126 return fmt.Errorf("%s", strings.TrimRight(renderer.RenderError(fmt.Errorf("config file does not exist: %s", configFile)), "\n"))126 return fmt.Errorf("%s", strings.TrimRight(renderer.RenderError(fmt.Errorf("config file does not exist: %s", configFile)), "\n"))
127 }127 }
128 128
129 editor := getEditor()129 editor := getEditor()
130 if editor == "" {130 if editor == "" {
131- renderer := styles.NewConfigRenderer(app.Theme)
132 const msg = "no editor found: set $EDITOR or $VISUAL environment variable"131 const msg = "no editor found: set $EDITOR or $VISUAL environment variable"
133 return fmt.Errorf("%s", strings.TrimRight(renderer.RenderError(fmt.Errorf("%s", msg)), "\n"))132 return fmt.Errorf("%s", strings.TrimRight(renderer.RenderError(fmt.Errorf("%s", msg)), "\n"))
134 }133 }
135 134
136 // Keep this a simple, non-interactive message; the actual editor takes over the terminal.135 // Keep this a simple, non-interactive message; the actual editor takes over the terminal.
137- fmt.Println(styles.NewConfigRenderer(app.Theme).RenderOpening(configFile, editor))136+ fmt.Println(renderer.RenderOpening(configFile, editor))
138 137
139 cmd := exec.Command(editor, configFile)138 cmd := exec.Command(editor, configFile)
140 cmd.Stdin = os.Stdin139 cmd.Stdin = os.Stdin
@@ -142,7 +141,6 @@ func runConfigOpen(_ *cobra.Command, _ []string) error {
142 cmd.Stderr = os.Stderr141 cmd.Stderr = os.Stderr
143 142
144 if err := cmd.Run(); err != nil {143 if err := cmd.Run(); err != nil {
145- renderer := styles.NewConfigRenderer(app.Theme)
146 return fmt.Errorf("%s", strings.TrimRight(renderer.RenderError(fmt.Errorf("open config: %w", err)), "\n"))144 return fmt.Errorf("%s", strings.TrimRight(renderer.RenderError(fmt.Errorf("open config: %w", err)), "\n"))
147 }145 }
148 return nil146 return nil
M internal/cli/cmd/crashes.go +4 -4
@@ -101,13 +101,13 @@ func runCrashesShow(_ *cobra.Command, args []string) error {
101101 report, err := resolveCrashReport(getCrashReportsDir(app.Config), args[0])
102102 if err != nil {
103103 _, _ = fmt.Fprintf(os.Stderr, "%s\n%s\n", renderer.RenderError(err), renderer.RenderHintList())
104- return nil
104+ return fmt.Errorf("resolve crash report: %w", err)
105105 }
106106
107107 body, err := os.ReadFile(report.MarkdownPath)
108108 if err != nil {
109109 _, _ = fmt.Fprintf(os.Stderr, "%s\n", renderer.RenderError(fmt.Errorf("read crash report markdown: %w", err)))
110- return nil
110+ return fmt.Errorf("read crash report markdown: %w", err)
111111 }
112112 fmt.Print(string(body))
113113 return nil
@@ -123,13 +123,13 @@ func runCrashesIssue(_ *cobra.Command, args []string) error {
123123 report, err := resolveCrashReport(getCrashReportsDir(app.Config), args[0])
124124 if err != nil {
125125 _, _ = fmt.Fprintf(os.Stderr, "%s\n%s\n", renderer.RenderError(err), renderer.RenderHintList())
126- return nil
126+ return fmt.Errorf("resolve crash report: %w", err)
127127 }
128128
129129 body, err := os.ReadFile(report.MarkdownPath)
130130 if err != nil {
131131 _, _ = fmt.Fprintf(os.Stderr, "%s\n", renderer.RenderError(fmt.Errorf("read crash report markdown: %w", err)))
132- return nil
132+ return fmt.Errorf("read crash report markdown: %w", err)
133133 }
134134 all := string(body)
135135 start := strings.Index(all, "## GitHub Issue Template")
@@ -101,13 +101,13 @@ func runCrashesShow(_ *cobra.Command, args []string) error {
101 report, err := resolveCrashReport(getCrashReportsDir(app.Config), args[0])101 report, err := resolveCrashReport(getCrashReportsDir(app.Config), args[0])
102 if err != nil {102 if err != nil {
103 _, _ = fmt.Fprintf(os.Stderr, "%s\n%s\n", renderer.RenderError(err), renderer.RenderHintList())103 _, _ = fmt.Fprintf(os.Stderr, "%s\n%s\n", renderer.RenderError(err), renderer.RenderHintList())
104- return nil104+ return fmt.Errorf("resolve crash report: %w", err)
105 }105 }
106 106
107 body, err := os.ReadFile(report.MarkdownPath)107 body, err := os.ReadFile(report.MarkdownPath)
108 if err != nil {108 if err != nil {
109 _, _ = fmt.Fprintf(os.Stderr, "%s\n", renderer.RenderError(fmt.Errorf("read crash report markdown: %w", err)))109 _, _ = fmt.Fprintf(os.Stderr, "%s\n", renderer.RenderError(fmt.Errorf("read crash report markdown: %w", err)))
110- return nil110+ return fmt.Errorf("read crash report markdown: %w", err)
111 }111 }
112 fmt.Print(string(body))112 fmt.Print(string(body))
113 return nil113 return nil
@@ -123,13 +123,13 @@ func runCrashesIssue(_ *cobra.Command, args []string) error {
123 report, err := resolveCrashReport(getCrashReportsDir(app.Config), args[0])123 report, err := resolveCrashReport(getCrashReportsDir(app.Config), args[0])
124 if err != nil {124 if err != nil {
125 _, _ = fmt.Fprintf(os.Stderr, "%s\n%s\n", renderer.RenderError(err), renderer.RenderHintList())125 _, _ = fmt.Fprintf(os.Stderr, "%s\n%s\n", renderer.RenderError(err), renderer.RenderHintList())
126- return nil126+ return fmt.Errorf("resolve crash report: %w", err)
127 }127 }
128 128
129 body, err := os.ReadFile(report.MarkdownPath)129 body, err := os.ReadFile(report.MarkdownPath)
130 if err != nil {130 if err != nil {
131 _, _ = fmt.Fprintf(os.Stderr, "%s\n", renderer.RenderError(fmt.Errorf("read crash report markdown: %w", err)))131 _, _ = fmt.Fprintf(os.Stderr, "%s\n", renderer.RenderError(fmt.Errorf("read crash report markdown: %w", err)))
132- return nil132+ return fmt.Errorf("read crash report markdown: %w", err)
133 }133 }
134 all := string(body)134 all := string(body)
135 start := strings.Index(all, "## GitHub Issue Template")135 start := strings.Index(all, "## GitHub Issue Template")
M internal/cli/cmd/sessions.go +25 -9
@@ -96,7 +96,9 @@ func runSessionsList(_ *cobra.Command, _ []string) error {
9696 renderer := styles.NewSessionsCLIRenderer(app.Theme)
9797
9898 if app.ListSessionsUC == nil {
99- return fmt.Errorf("%s", renderer.RenderError(fmt.Errorf("session management not available")))
99+ err := fmt.Errorf("session management not available")
100+ fmt.Fprintln(os.Stderr, renderer.RenderError(err))
101+ return err
100102 }
101103
102104 // Get current session ID (empty if not running as browser)
@@ -107,7 +109,9 @@ func runSessionsList(_ *cobra.Command, _ []string) error {
107109
108110 output, err := app.ListSessionsUC.Execute(app.Ctx(), currentSessionID, sessionsLimit)
109111 if err != nil {
110- return fmt.Errorf("%s", renderer.RenderError(fmt.Errorf("list sessions: %w", err)))
112+ wrappedErr := fmt.Errorf("list sessions: %w", err)
113+ fmt.Fprintln(os.Stderr, renderer.RenderError(wrappedErr))
114+ return wrappedErr
111115 }
112116
113117 if sessionsJSON {
@@ -154,13 +158,16 @@ func runSessionsRestore(_ *cobra.Command, args []string) error {
154158 renderer := styles.NewSessionsCLIRenderer(app.Theme)
155159
156160 if app.RestoreUC == nil || app.ListSessionsUC == nil {
157- return fmt.Errorf("%s", renderer.RenderError(fmt.Errorf("session restoration not available")))
161+ err := fmt.Errorf("session restoration not available")
162+ fmt.Fprintln(os.Stderr, renderer.RenderError(err))
163+ return err
158164 }
159165
160166 // Find session by ID or suffix
161167 sessionInfo, err := findSessionByIDOrSuffix(args[0])
162168 if err != nil {
163- return fmt.Errorf("%s", renderer.RenderError(err))
169+ fmt.Fprintln(os.Stderr, renderer.RenderError(err))
170+ return err
164171 }
165172
166173 sessionID := sessionInfo.Session.ID
@@ -168,13 +175,17 @@ func runSessionsRestore(_ *cobra.Command, args []string) error {
168175 // Validate the session has restorable state
169176 _, err = app.RestoreUC.Execute(app.Ctx(), usecase.RestoreInput{SessionID: sessionID})
170177 if err != nil {
171- return fmt.Errorf("%s", renderer.RenderError(fmt.Errorf("restore session: %w", err)))
178+ wrappedErr := fmt.Errorf("restore session: %w", err)
179+ fmt.Fprintln(os.Stderr, renderer.RenderError(wrappedErr))
180+ return wrappedErr
172181 }
173182
174183 // Spawn a new dumber instance with the session
175184 spawner := desktop.NewSessionSpawner(app.Ctx())
176185 if err := spawner.SpawnWithSession(sessionID); err != nil {
177- return fmt.Errorf("%s", renderer.RenderError(fmt.Errorf("spawn browser: %w", err)))
186+ wrappedErr := fmt.Errorf("spawn browser: %w", err)
187+ fmt.Fprintln(os.Stderr, renderer.RenderError(wrappedErr))
188+ return wrappedErr
178189 }
179190
180191 fmt.Println(renderer.RenderRestoreStarted(sessionID))
@@ -211,13 +222,16 @@ func runSessionsDelete(_ *cobra.Command, args []string) error {
211222 renderer := styles.NewSessionsCLIRenderer(app.Theme)
212223
213224 if app.DeleteSessionUC == nil || app.ListSessionsUC == nil {
214- return fmt.Errorf("%s", renderer.RenderError(fmt.Errorf("session management not available")))
225+ err := fmt.Errorf("session management not available")
226+ fmt.Fprintln(os.Stderr, renderer.RenderError(err))
227+ return err
215228 }
216229
217230 // Find session by ID or suffix
218231 sessionInfo, err := findSessionByIDOrSuffix(args[0])
219232 if err != nil {
220- return fmt.Errorf("%s", renderer.RenderError(err))
233+ fmt.Fprintln(os.Stderr, renderer.RenderError(err))
234+ return err
221235 }
222236
223237 // Get current session ID
@@ -231,7 +245,9 @@ func runSessionsDelete(_ *cobra.Command, args []string) error {
231245 SessionID: sessionInfo.Session.ID,
232246 CurrentSessionID: currentSessionID,
233247 }); err != nil {
234- return fmt.Errorf("%s", renderer.RenderError(fmt.Errorf("delete session: %w", err)))
248+ wrappedErr := fmt.Errorf("delete session: %w", err)
249+ fmt.Fprintln(os.Stderr, renderer.RenderError(wrappedErr))
250+ return wrappedErr
235251 }
236252
237253 fmt.Println(renderer.RenderDeleted(sessionInfo.Session.ID))
@@ -96,7 +96,9 @@ func runSessionsList(_ *cobra.Command, _ []string) error {
96 renderer := styles.NewSessionsCLIRenderer(app.Theme)96 renderer := styles.NewSessionsCLIRenderer(app.Theme)
97 97
98 if app.ListSessionsUC == nil {98 if app.ListSessionsUC == nil {
99- return fmt.Errorf("%s", renderer.RenderError(fmt.Errorf("session management not available")))99+ err := fmt.Errorf("session management not available")
100+ fmt.Fprintln(os.Stderr, renderer.RenderError(err))
101+ return err
100 }102 }
101 103
102 // Get current session ID (empty if not running as browser)104 // Get current session ID (empty if not running as browser)
@@ -107,7 +109,9 @@ func runSessionsList(_ *cobra.Command, _ []string) error {
107 109
108 output, err := app.ListSessionsUC.Execute(app.Ctx(), currentSessionID, sessionsLimit)110 output, err := app.ListSessionsUC.Execute(app.Ctx(), currentSessionID, sessionsLimit)
109 if err != nil {111 if err != nil {
110- return fmt.Errorf("%s", renderer.RenderError(fmt.Errorf("list sessions: %w", err)))112+ wrappedErr := fmt.Errorf("list sessions: %w", err)
113+ fmt.Fprintln(os.Stderr, renderer.RenderError(wrappedErr))
114+ return wrappedErr
111 }115 }
112 116
113 if sessionsJSON {117 if sessionsJSON {
@@ -154,13 +158,16 @@ func runSessionsRestore(_ *cobra.Command, args []string) error {
154 renderer := styles.NewSessionsCLIRenderer(app.Theme)158 renderer := styles.NewSessionsCLIRenderer(app.Theme)
155 159
156 if app.RestoreUC == nil || app.ListSessionsUC == nil {160 if app.RestoreUC == nil || app.ListSessionsUC == nil {
157- return fmt.Errorf("%s", renderer.RenderError(fmt.Errorf("session restoration not available")))161+ err := fmt.Errorf("session restoration not available")
162+ fmt.Fprintln(os.Stderr, renderer.RenderError(err))
163+ return err
158 }164 }
159 165
160 // Find session by ID or suffix166 // Find session by ID or suffix
161 sessionInfo, err := findSessionByIDOrSuffix(args[0])167 sessionInfo, err := findSessionByIDOrSuffix(args[0])
162 if err != nil {168 if err != nil {
163- return fmt.Errorf("%s", renderer.RenderError(err))169+ fmt.Fprintln(os.Stderr, renderer.RenderError(err))
170+ return err
164 }171 }
165 172
166 sessionID := sessionInfo.Session.ID173 sessionID := sessionInfo.Session.ID
@@ -168,13 +175,17 @@ func runSessionsRestore(_ *cobra.Command, args []string) error {
168 // Validate the session has restorable state175 // Validate the session has restorable state
169 _, err = app.RestoreUC.Execute(app.Ctx(), usecase.RestoreInput{SessionID: sessionID})176 _, err = app.RestoreUC.Execute(app.Ctx(), usecase.RestoreInput{SessionID: sessionID})
170 if err != nil {177 if err != nil {
171- return fmt.Errorf("%s", renderer.RenderError(fmt.Errorf("restore session: %w", err)))178+ wrappedErr := fmt.Errorf("restore session: %w", err)
179+ fmt.Fprintln(os.Stderr, renderer.RenderError(wrappedErr))
180+ return wrappedErr
172 }181 }
173 182
174 // Spawn a new dumber instance with the session183 // Spawn a new dumber instance with the session
175 spawner := desktop.NewSessionSpawner(app.Ctx())184 spawner := desktop.NewSessionSpawner(app.Ctx())
176 if err := spawner.SpawnWithSession(sessionID); err != nil {185 if err := spawner.SpawnWithSession(sessionID); err != nil {
177- return fmt.Errorf("%s", renderer.RenderError(fmt.Errorf("spawn browser: %w", err)))186+ wrappedErr := fmt.Errorf("spawn browser: %w", err)
187+ fmt.Fprintln(os.Stderr, renderer.RenderError(wrappedErr))
188+ return wrappedErr
178 }189 }
179 190
180 fmt.Println(renderer.RenderRestoreStarted(sessionID))191 fmt.Println(renderer.RenderRestoreStarted(sessionID))
@@ -211,13 +222,16 @@ func runSessionsDelete(_ *cobra.Command, args []string) error {
211 renderer := styles.NewSessionsCLIRenderer(app.Theme)222 renderer := styles.NewSessionsCLIRenderer(app.Theme)
212 223
213 if app.DeleteSessionUC == nil || app.ListSessionsUC == nil {224 if app.DeleteSessionUC == nil || app.ListSessionsUC == nil {
214- return fmt.Errorf("%s", renderer.RenderError(fmt.Errorf("session management not available")))225+ err := fmt.Errorf("session management not available")
226+ fmt.Fprintln(os.Stderr, renderer.RenderError(err))
227+ return err
215 }228 }
216 229
217 // Find session by ID or suffix230 // Find session by ID or suffix
218 sessionInfo, err := findSessionByIDOrSuffix(args[0])231 sessionInfo, err := findSessionByIDOrSuffix(args[0])
219 if err != nil {232 if err != nil {
220- return fmt.Errorf("%s", renderer.RenderError(err))233+ fmt.Fprintln(os.Stderr, renderer.RenderError(err))
234+ return err
221 }235 }
222 236
223 // Get current session ID237 // Get current session ID
@@ -231,7 +245,9 @@ func runSessionsDelete(_ *cobra.Command, args []string) error {
231 SessionID: sessionInfo.Session.ID,245 SessionID: sessionInfo.Session.ID,
232 CurrentSessionID: currentSessionID,246 CurrentSessionID: currentSessionID,
233 }); err != nil {247 }); err != nil {
234- return fmt.Errorf("%s", renderer.RenderError(fmt.Errorf("delete session: %w", err)))248+ wrappedErr := fmt.Errorf("delete session: %w", err)
249+ fmt.Fprintln(os.Stderr, renderer.RenderError(wrappedErr))
250+ return wrappedErr
235 }251 }
236 252
237 fmt.Println(renderer.RenderDeleted(sessionInfo.Session.ID))253 fmt.Println(renderer.RenderDeleted(sessionInfo.Session.ID))
M internal/cli/model/sessions.go +1 -5
@@ -463,7 +463,7 @@ func (m SessionsModel) renderSessionsList(maxHeight int) string {
463463 }
464464
465465 // Render expanded details, but never exceed available height.
466- if isExpanded && info.State != nil && remaining > 0 {
466+ if isExpanded && remaining > 0 {
467467 details := m.renderSessionDetails(info)
468468 details = truncateLines(details, remaining)
469469 if details != "" {
@@ -492,10 +492,6 @@ func (m SessionsModel) rowHeight(idx int) int {
492492 }
493493
494494 info := m.sessions[idx]
495- if info.State == nil {
496- return height
497- }
498-
499495 details := m.renderSessionDetails(info)
500496 if details == "" {
501497 return height
@@ -463,7 +463,7 @@ func (m SessionsModel) renderSessionsList(maxHeight int) string {
463 }463 }
464 464
465 // Render expanded details, but never exceed available height.465 // Render expanded details, but never exceed available height.
466- if isExpanded && info.State != nil && remaining > 0 {466+ if isExpanded && remaining > 0 {
467 details := m.renderSessionDetails(info)467 details := m.renderSessionDetails(info)
468 details = truncateLines(details, remaining)468 details = truncateLines(details, remaining)
469 if details != "" {469 if details != "" {
@@ -492,10 +492,6 @@ func (m SessionsModel) rowHeight(idx int) int {
492 }492 }
493 493
494 info := m.sessions[idx]494 info := m.sessions[idx]
495- if info.State == nil {
496- return height
497- }
498-
499 details := m.renderSessionDetails(info)495 details := m.renderSessionDetails(info)
500 if details == "" {496 if details == "" {
501 return height497 return height

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