setup.go RawHistoryFolder

1package cmd
2
3import (
4	"fmt"
5	"strings"
6
7	"github.com/spf13/cobra"
8
9	"github.com/bnema/dumber/assets"
10	"github.com/bnema/dumber/internal/application/usecase"
11	"github.com/bnema/dumber/internal/infrastructure/desktop"
12	"github.com/bnema/dumber/internal/infrastructure/env"
13)
14
15var setupCmd = &cobra.Command{
16	Use:   "setup",
17	Short: "Setup desktop integration",
18	Long: `Setup dumber's integration with the desktop environment.
19
20Subcommands:
21  install  - Install desktop file to ~/.local/share/applications/
22  default  - Set dumber as the default web browser`,
23}
24
25var setupInstallCmd = &cobra.Command{
26	Use:   "install",
27	Short: "Install desktop file",
28	Long: `Install the dumber.desktop file to the user's applications directory.
29
30This registers dumber with the desktop environment, making it appear in
31application menus and available as a browser choice.
32
33Location: $XDG_DATA_HOME/applications/dumber.desktop
34         (typically ~/.local/share/applications/dumber.desktop)
35
36This command is idempotent - safe to run multiple times.`,
37	RunE: runSetupInstall,
38}
39
40var setupDefaultCmd = &cobra.Command{
41	Use:   "default",
42	Short: "Set dumber as default browser",
43	Long: `Set dumber as the default web browser using xdg-settings.
44
45This makes dumber handle http:// and https:// links from other applications.
46
47Requires the desktop file to be installed first (run 'dumber setup install').
48
49This command is idempotent - safe to run multiple times.`,
50	RunE: runSetupDefault,
51}
52
53func init() {
54	rootCmd.AddCommand(setupCmd)
55	setupCmd.AddCommand(setupInstallCmd)
56	setupCmd.AddCommand(setupDefaultCmd)
57}
58
59func runSetupInstall(_ *cobra.Command, _ []string) error {
60	cliApp := GetApp()
61	if cliApp == nil {
62		return fmt.Errorf("app not initialized")
63	}
64
65	theme := cliApp.Theme
66
67	// Check if installed via package manager
68	switch {
69	case env.IsFlatpak():
70		fmt.Printf("%s Desktop integration already handled by Flatpak\n",
71			theme.SuccessStyle.Render("\u2713"))
72		fmt.Println()
73		fmt.Println(theme.Subtle.Render("Run 'dumber setup default' to set as default browser"))
74		return nil
75	case env.IsPacman():
76		fmt.Printf("%s Desktop integration already handled by AUR package\n",
77			theme.SuccessStyle.Render("\u2713"))
78		fmt.Println()
79		fmt.Println(theme.Subtle.Render("Run 'dumber setup default' to set as default browser"))
80		return nil
81	}
82
83	adapter := desktop.New()
84	uc := usecase.NewInstallDesktopUseCase(adapter)
85
86	input := usecase.InstallDesktopInput{
87		IconData: assets.LogoSVG,
88	}
89
90	result, err := uc.Execute(cliApp.Ctx(), input)
91	if err != nil {
92		fmt.Printf("%s %s\n", theme.ErrorStyle.Render("\u2717"), err.Error())
93		return err
94	}
95
96	// Desktop file status
97	if result.WasDesktopExisting {
98		fmt.Printf("%s Desktop file updated at %s\n",
99			theme.SuccessStyle.Render("\u2713"),
100			theme.Highlight.Render(result.DesktopPath))
101	} else {
102		fmt.Printf("%s Desktop file installed to %s\n",
103			theme.SuccessStyle.Render("\u2713"),
104			theme.Highlight.Render(result.DesktopPath))
105	}
106
107	// Icon status
108	if result.IconPath != "" {
109		if result.WasIconExisting {
110			fmt.Printf("%s Icon updated at %s\n",
111				theme.SuccessStyle.Render("\u2713"),
112				theme.Highlight.Render(result.IconPath))
113		} else {
114			fmt.Printf("%s Icon installed to %s\n",
115				theme.SuccessStyle.Render("\u2713"),
116				theme.Highlight.Render(result.IconPath))
117		}
118	}
119
120	fmt.Println()
121	fmt.Println(theme.Subtle.Render("Run 'dumber setup default' to set as default browser"))
122
123	return nil
124}
125
126func runSetupDefault(_ *cobra.Command, _ []string) error {
127	cliApp := GetApp()
128	if cliApp == nil {
129		return fmt.Errorf("app not initialized")
130	}
131
132	theme := cliApp.Theme
133	adapter := desktop.New()
134	uc := usecase.NewSetDefaultBrowserUseCase(adapter)
135
136	result, err := uc.Execute(cliApp.Ctx())
137	if err != nil {
138		errMsg := err.Error()
139		fmt.Printf("%s %s\n", theme.ErrorStyle.Render("\u2717"), errMsg)
140		if strings.Contains(errMsg, "desktop file not installed") {
141			fmt.Println()
142			fmt.Println(theme.Subtle.Render("Hint: Run 'dumber setup install' first"))
143		}
144		return err
145	}
146
147	if result.WasAlreadyDefault {
148		fmt.Printf("%s Dumber is already the default browser\n",
149			theme.SuccessStyle.Render("\u2713"))
150	} else {
151		fmt.Printf("%s Dumber is now the default browser\n",
152			theme.SuccessStyle.Render("\u2713"))
153	}
154
155	return nil
156}

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