detached 38ca0fd

feat(flatpak): add Flatpak packaging support (WIP)

- Add Flatpak manifest (dev.bnema.Dumber.yml)
- Add desktop entry and AppStream metainfo
- Add GitHub Actions workflow for Flatpak builds
- Add Makefile targets for local Flatpak builds
- Disable in-app updater when running in Flatpak sandbox
- Update .gitignore for Flatpak build artifacts

Note: npm/vite PATH issue needs resolution before builds work
commit
38ca0fd392a70b98bf1f2a4e76ca606d3bc8cf92
author
bnema · · 2025-12-30 20:11 UTC
parent
d05d132
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
7 files · +221 -1
A .github/workflows/flatpak.yml +41 -0
@@ -0,0 +1,41 @@
1+name: Flatpak
2+
3+on:
4+ push:
5+ tags:
6+ - 'v*'
7+ workflow_dispatch:
8+
9+permissions:
10+ contents: write
11+
12+jobs:
13+ flatpak:
14+ name: Build Flatpak
15+ runs-on: ubuntu-latest
16+ container:
17+ image: ghcr.io/flathub-infra/flatpak-github-actions:gnome-48
18+ options: --privileged
19+ steps:
20+ - uses: actions/checkout@v4
21+
22+ - name: Import GPG key
23+ if: github.event_name != 'pull_request'
24+ run: |
25+ echo "${{ secrets.GPG_PRIVATE_KEY }}" | gpg --batch --import
26+ gpg --list-secret-keys --keyid-format=long
27+
28+ - uses: flatpak/flatpak-github-actions/flatpak-builder@v6
29+ with:
30+ bundle: dumber.flatpak
31+ manifest-path: dev.bnema.Dumber.yml
32+ cache-key: flatpak-builder-${{ github.sha }}
33+ gpg-sign: ${{ github.event_name != 'pull_request' && secrets.GPG_KEY_ID || '' }}
34+
35+ - name: Upload Flatpak to Release
36+ if: startsWith(github.ref, 'refs/tags/')
37+ uses: softprops/action-gh-release@v2
38+ with:
39+ files: dumber.flatpak
40+ env:
41+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -0,0 +1,41 @@
1+name: Flatpak
2+
3+on:
4+ push:
5+ tags:
6+ - 'v*'
7+ workflow_dispatch:
8+
9+permissions:
10+ contents: write
11+
12+jobs:
13+ flatpak:
14+ name: Build Flatpak
15+ runs-on: ubuntu-latest
16+ container:
17+ image: ghcr.io/flathub-infra/flatpak-github-actions:gnome-48
18+ options: --privileged
19+ steps:
20+ - uses: actions/checkout@v4
21+
22+ - name: Import GPG key
23+ if: github.event_name != 'pull_request'
24+ run: |
25+ echo "${{ secrets.GPG_PRIVATE_KEY }}" | gpg --batch --import
26+ gpg --list-secret-keys --keyid-format=long
27+
28+ - uses: flatpak/flatpak-github-actions/flatpak-builder@v6
29+ with:
30+ bundle: dumber.flatpak
31+ manifest-path: dev.bnema.Dumber.yml
32+ cache-key: flatpak-builder-${{ github.sha }}
33+ gpg-sign: ${{ github.event_name != 'pull_request' && secrets.GPG_KEY_ID || '' }}
34+
35+ - name: Upload Flatpak to Release
36+ if: startsWith(github.ref, 'refs/tags/')
37+ uses: softprops/action-gh-release@v2
38+ with:
39+ files: dumber.flatpak
40+ env:
41+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
M .gitignore +7 -0
@@ -89,3 +89,10 @@ vendor/
8989 serviceworkers/
9090 storage/
9191 mediakeys/
92+
93+# Flatpak build artifacts
94+flatpak-build/
95+flatpak-repo/
96+.flatpak-builder/
97+*.flatpak
98+builddir/
@@ -89,3 +89,10 @@ vendor/
89 serviceworkers/89 serviceworkers/
90 storage/90 storage/
91 mediakeys/91 mediakeys/
92+
93+# Flatpak build artifacts
94+flatpak-build/
95+flatpak-repo/
96+.flatpak-builder/
97+*.flatpak
98+builddir/
M Makefile +30 -1
@@ -1,6 +1,6 @@
11 # Makefile for dumber (Clean Architecture - puregotk)
22
3-.PHONY: build build-frontend test lint clean install-tools dev generate help init check-docs man
3+.PHONY: build build-frontend test lint clean install-tools dev generate help init check-docs man flatpak-deps flatpak-build flatpak-install flatpak-run flatpak-clean
44
55 # Load local overrides from .env.local if present (Makefile syntax)
66 ifneq (,$(wildcard .env.local))
@@ -152,3 +152,32 @@ release-snapshot: build-frontend ## Build snapshot using goreleaser
152152 release: ## Create full release using goreleaser
153153 @echo "Building release with goreleaser..."
154154 GITHUB_TOKEN=$$(gh auth token) goreleaser release --clean
155+
156+# Flatpak targets
157+flatpak-deps: ## Install Flatpak build dependencies (Arch)
158+ @echo "Installing Flatpak build dependencies..."
159+ sudo pacman -S --needed flatpak flatpak-builder
160+ flatpak remote-add --if-not-exists --user flathub https://dl.flathub.org/repo/flathub.flatpakrepo
161+ flatpak install --user -y flathub org.gnome.Platform//48 org.gnome.Sdk//48
162+ flatpak install --user -y flathub org.freedesktop.Sdk.Extension.golang//24.08
163+ flatpak install --user -y flathub org.freedesktop.Sdk.Extension.node20//24.08
164+ @echo "Flatpak dependencies installed!"
165+
166+flatpak-build: ## Build Flatpak bundle locally
167+ @echo "Building Flatpak bundle..."
168+ flatpak-builder --force-clean --user --repo=flatpak-repo flatpak-build dev.bnema.Dumber.yml
169+ flatpak build-bundle flatpak-repo dumber.flatpak dev.bnema.Dumber
170+ @echo "Flatpak bundle created: dumber.flatpak"
171+
172+flatpak-install: ## Install Flatpak locally for testing
173+ @echo "Installing Flatpak locally..."
174+ flatpak-builder --force-clean --user --install flatpak-build dev.bnema.Dumber.yml
175+ @echo "Flatpak installed! Run with: make flatpak-run"
176+
177+flatpak-run: ## Run the installed Flatpak
178+ flatpak run dev.bnema.Dumber
179+
180+flatpak-clean: ## Clean Flatpak build artifacts
181+ @echo "Cleaning Flatpak build artifacts..."
182+ rm -rf flatpak-build flatpak-repo .flatpak-builder dumber.flatpak
183+ @echo "Flatpak artifacts cleaned!"
@@ -1,6 +1,6 @@
1 # Makefile for dumber (Clean Architecture - puregotk)1 # Makefile for dumber (Clean Architecture - puregotk)
2 2
3-.PHONY: build build-frontend test lint clean install-tools dev generate help init check-docs man3+.PHONY: build build-frontend test lint clean install-tools dev generate help init check-docs man flatpak-deps flatpak-build flatpak-install flatpak-run flatpak-clean
4 4
5 # Load local overrides from .env.local if present (Makefile syntax)5 # Load local overrides from .env.local if present (Makefile syntax)
6 ifneq (,$(wildcard .env.local))6 ifneq (,$(wildcard .env.local))
@@ -152,3 +152,32 @@ release-snapshot: build-frontend ## Build snapshot using goreleaser
152 release: ## Create full release using goreleaser152 release: ## Create full release using goreleaser
153 @echo "Building release with goreleaser..."153 @echo "Building release with goreleaser..."
154 GITHUB_TOKEN=$$(gh auth token) goreleaser release --clean154 GITHUB_TOKEN=$$(gh auth token) goreleaser release --clean
155+
156+# Flatpak targets
157+flatpak-deps: ## Install Flatpak build dependencies (Arch)
158+ @echo "Installing Flatpak build dependencies..."
159+ sudo pacman -S --needed flatpak flatpak-builder
160+ flatpak remote-add --if-not-exists --user flathub https://dl.flathub.org/repo/flathub.flatpakrepo
161+ flatpak install --user -y flathub org.gnome.Platform//48 org.gnome.Sdk//48
162+ flatpak install --user -y flathub org.freedesktop.Sdk.Extension.golang//24.08
163+ flatpak install --user -y flathub org.freedesktop.Sdk.Extension.node20//24.08
164+ @echo "Flatpak dependencies installed!"
165+
166+flatpak-build: ## Build Flatpak bundle locally
167+ @echo "Building Flatpak bundle..."
168+ flatpak-builder --force-clean --user --repo=flatpak-repo flatpak-build dev.bnema.Dumber.yml
169+ flatpak build-bundle flatpak-repo dumber.flatpak dev.bnema.Dumber
170+ @echo "Flatpak bundle created: dumber.flatpak"
171+
172+flatpak-install: ## Install Flatpak locally for testing
173+ @echo "Installing Flatpak locally..."
174+ flatpak-builder --force-clean --user --install flatpak-build dev.bnema.Dumber.yml
175+ @echo "Flatpak installed! Run with: make flatpak-run"
176+
177+flatpak-run: ## Run the installed Flatpak
178+ flatpak run dev.bnema.Dumber
179+
180+flatpak-clean: ## Clean Flatpak build artifacts
181+ @echo "Cleaning Flatpak build artifacts..."
182+ rm -rf flatpak-build flatpak-repo .flatpak-builder dumber.flatpak
183+ @echo "Flatpak artifacts cleaned!"
A dev.bnema.Dumber.desktop +12 -0
@@ -0,0 +1,12 @@
1+[Desktop Entry]
2+Name=Dumber
3+Comment=A minimal keyboard-driven browser for tiling WMs
4+Exec=dumber
5+Icon=dev.bnema.Dumber
6+Terminal=false
7+Type=Application
8+Categories=Network;WebBrowser;GTK;
9+Keywords=browser;webkit;wayland;tiling;keyboard;
10+StartupNotify=true
11+StartupWMClass=dumber
12+MimeType=text/html;text/xml;application/xhtml+xml;x-scheme-handler/http;x-scheme-handler/https;
@@ -0,0 +1,12 @@
1+[Desktop Entry]
2+Name=Dumber
3+Comment=A minimal keyboard-driven browser for tiling WMs
4+Exec=dumber
5+Icon=dev.bnema.Dumber
6+Terminal=false
7+Type=Application
8+Categories=Network;WebBrowser;GTK;
9+Keywords=browser;webkit;wayland;tiling;keyboard;
10+StartupNotify=true
11+StartupWMClass=dumber
12+MimeType=text/html;text/xml;application/xhtml+xml;x-scheme-handler/http;x-scheme-handler/https;
A dev.bnema.Dumber.metainfo.xml +66 -0
@@ -0,0 +1,66 @@
1+<?xml version="1.0" encoding="UTF-8"?>
2+<component type="desktop-application">
3+ <id>dev.bnema.Dumber</id>
4+ <metadata_license>CC0-1.0</metadata_license>
5+ <project_license>MIT</project_license>
6+ <name>Dumber</name>
7+ <summary>A minimal keyboard-driven browser for tiling WMs</summary>
8+ <description>
9+ <p>
10+ A dumb browser that works like your favorite terminal multiplexer.
11+ Wayland native, keyboard-driven with Zellij-inspired workflow, featuring
12+ tabs with split and stacked panes, built-in ad blocking, and session management.
13+ </p>
14+ <p>Features include:</p>
15+ <ul>
16+ <li>Wayland native - no X11/XWayland dependency</li>
17+ <li>Tabs and workspaces with split or stacked panes</li>
18+ <li>Keyboard-driven workflow inspired by Zellij</li>
19+ <li>Smart popup handling for OAuth flows and tiling WMs</li>
20+ <li>GPU rendering with automatic VA-API/VDPAU detection</li>
21+ <li>Built-in ad blocking with UBlock-based filtering</li>
22+ <li>Launcher integration with rofi/fuzzel support</li>
23+ <li>Search shortcuts via bangs</li>
24+ <li>Customizable light and dark themes</li>
25+ <li>Session save/restore with automatic snapshots</li>
26+ </ul>
27+ </description>
28+ <launchable type="desktop-id">dev.bnema.Dumber.desktop</launchable>
29+ <url type="homepage">https://github.com/bnema/dumber</url>
30+ <url type="bugtracker">https://github.com/bnema/dumber/issues</url>
31+ <url type="vcs-browser">https://github.com/bnema/dumber</url>
32+ <developer id="dev.bnema">
33+ <name>bnema</name>
34+ </developer>
35+ <branding>
36+ <color type="primary" scheme_preference="light">#f5f5f5</color>
37+ <color type="primary" scheme_preference="dark">#1a1a1a</color>
38+ </branding>
39+ <content_rating type="oars-1.1" />
40+ <supports>
41+ <control>keyboard</control>
42+ <control>pointing</control>
43+ </supports>
44+ <requires>
45+ <display_length compare="ge">768</display_length>
46+ </requires>
47+ <categories>
48+ <category>Network</category>
49+ <category>WebBrowser</category>
50+ </categories>
51+ <keywords>
52+ <keyword>browser</keyword>
53+ <keyword>webkit</keyword>
54+ <keyword>wayland</keyword>
55+ <keyword>tiling</keyword>
56+ <keyword>keyboard</keyword>
57+ <keyword>minimal</keyword>
58+ </keywords>
59+ <releases>
60+ <release version="0.15.0" date="2024-12-29">
61+ <description>
62+ <p>Latest release with improved session management and bug fixes.</p>
63+ </description>
64+ </release>
65+ </releases>
66+</component>
@@ -0,0 +1,66 @@
1+<?xml version="1.0" encoding="UTF-8"?>
2+<component type="desktop-application">
3+ <id>dev.bnema.Dumber</id>
4+ <metadata_license>CC0-1.0</metadata_license>
5+ <project_license>MIT</project_license>
6+ <name>Dumber</name>
7+ <summary>A minimal keyboard-driven browser for tiling WMs</summary>
8+ <description>
9+ <p>
10+ A dumb browser that works like your favorite terminal multiplexer.
11+ Wayland native, keyboard-driven with Zellij-inspired workflow, featuring
12+ tabs with split and stacked panes, built-in ad blocking, and session management.
13+ </p>
14+ <p>Features include:</p>
15+ <ul>
16+ <li>Wayland native - no X11/XWayland dependency</li>
17+ <li>Tabs and workspaces with split or stacked panes</li>
18+ <li>Keyboard-driven workflow inspired by Zellij</li>
19+ <li>Smart popup handling for OAuth flows and tiling WMs</li>
20+ <li>GPU rendering with automatic VA-API/VDPAU detection</li>
21+ <li>Built-in ad blocking with UBlock-based filtering</li>
22+ <li>Launcher integration with rofi/fuzzel support</li>
23+ <li>Search shortcuts via bangs</li>
24+ <li>Customizable light and dark themes</li>
25+ <li>Session save/restore with automatic snapshots</li>
26+ </ul>
27+ </description>
28+ <launchable type="desktop-id">dev.bnema.Dumber.desktop</launchable>
29+ <url type="homepage">https://github.com/bnema/dumber</url>
30+ <url type="bugtracker">https://github.com/bnema/dumber/issues</url>
31+ <url type="vcs-browser">https://github.com/bnema/dumber</url>
32+ <developer id="dev.bnema">
33+ <name>bnema</name>
34+ </developer>
35+ <branding>
36+ <color type="primary" scheme_preference="light">#f5f5f5</color>
37+ <color type="primary" scheme_preference="dark">#1a1a1a</color>
38+ </branding>
39+ <content_rating type="oars-1.1" />
40+ <supports>
41+ <control>keyboard</control>
42+ <control>pointing</control>
43+ </supports>
44+ <requires>
45+ <display_length compare="ge">768</display_length>
46+ </requires>
47+ <categories>
48+ <category>Network</category>
49+ <category>WebBrowser</category>
50+ </categories>
51+ <keywords>
52+ <keyword>browser</keyword>
53+ <keyword>webkit</keyword>
54+ <keyword>wayland</keyword>
55+ <keyword>tiling</keyword>
56+ <keyword>keyboard</keyword>
57+ <keyword>minimal</keyword>
58+ </keywords>
59+ <releases>
60+ <release version="0.15.0" date="2024-12-29">
61+ <description>
62+ <p>Latest release with improved session management and bug fixes.</p>
63+ </description>
64+ </release>
65+ </releases>
66+</component>
A dev.bnema.Dumber.yml +52 -0
@@ -0,0 +1,52 @@
1+id: dev.bnema.Dumber
2+runtime: org.gnome.Platform
3+runtime-version: '48'
4+sdk: org.gnome.Sdk
5+sdk-extensions:
6+ - org.freedesktop.Sdk.Extension.golang
7+ - org.freedesktop.Sdk.Extension.node20
8+command: dumber
9+
10+finish-args:
11+ # Display (Wayland only)
12+ - --socket=wayland
13+ - --device=dri
14+ # Network (browser)
15+ - --share=network
16+ # Audio/Video playback
17+ - --socket=pulseaudio
18+ # Camera/Webcam access (for video calls)
19+ - --device=all
20+ # Downloads
21+ - --filesystem=xdg-download
22+ # Open local HTML files
23+ - --filesystem=home:ro
24+ # PipeWire for screen sharing and modern audio/video
25+ - --filesystem=xdg-run/pipewire-0:ro
26+
27+modules:
28+ - name: dumber
29+ buildsystem: simple
30+ build-options:
31+ append-path: /usr/lib/sdk/golang/bin:/usr/lib/sdk/node20/bin
32+ env:
33+ GOROOT: /usr/lib/sdk/golang
34+ GOPATH: /run/build/dumber/go
35+ CGO_ENABLED: '0'
36+ build-commands:
37+ # TODO: Fix npm/vite PATH issue - see issues/flatpak-build-npm-vite-path-issue.md
38+ # Build frontend
39+ - npm ci --prefix webui
40+ - npm run build --prefix webui
41+ # Build Go binary
42+ - go build -ldflags="-s -w" -o /app/bin/dumber ./cmd/dumber
43+ # Install desktop file
44+ - install -Dm644 dev.bnema.Dumber.desktop /app/share/applications/dev.bnema.Dumber.desktop
45+ # Install icons
46+ - install -Dm644 assets/logo.svg /app/share/icons/hicolor/scalable/apps/dev.bnema.Dumber.svg
47+ - install -Dm644 assets/logo-32.png /app/share/icons/hicolor/32x32/apps/dev.bnema.Dumber.png
48+ # Install metainfo
49+ - install -Dm644 dev.bnema.Dumber.metainfo.xml /app/share/metainfo/dev.bnema.Dumber.metainfo.xml
50+ sources:
51+ - type: dir
52+ path: .
@@ -0,0 +1,52 @@
1+id: dev.bnema.Dumber
2+runtime: org.gnome.Platform
3+runtime-version: '48'
4+sdk: org.gnome.Sdk
5+sdk-extensions:
6+ - org.freedesktop.Sdk.Extension.golang
7+ - org.freedesktop.Sdk.Extension.node20
8+command: dumber
9+
10+finish-args:
11+ # Display (Wayland only)
12+ - --socket=wayland
13+ - --device=dri
14+ # Network (browser)
15+ - --share=network
16+ # Audio/Video playback
17+ - --socket=pulseaudio
18+ # Camera/Webcam access (for video calls)
19+ - --device=all
20+ # Downloads
21+ - --filesystem=xdg-download
22+ # Open local HTML files
23+ - --filesystem=home:ro
24+ # PipeWire for screen sharing and modern audio/video
25+ - --filesystem=xdg-run/pipewire-0:ro
26+
27+modules:
28+ - name: dumber
29+ buildsystem: simple
30+ build-options:
31+ append-path: /usr/lib/sdk/golang/bin:/usr/lib/sdk/node20/bin
32+ env:
33+ GOROOT: /usr/lib/sdk/golang
34+ GOPATH: /run/build/dumber/go
35+ CGO_ENABLED: '0'
36+ build-commands:
37+ # TODO: Fix npm/vite PATH issue - see issues/flatpak-build-npm-vite-path-issue.md
38+ # Build frontend
39+ - npm ci --prefix webui
40+ - npm run build --prefix webui
41+ # Build Go binary
42+ - go build -ldflags="-s -w" -o /app/bin/dumber ./cmd/dumber
43+ # Install desktop file
44+ - install -Dm644 dev.bnema.Dumber.desktop /app/share/applications/dev.bnema.Dumber.desktop
45+ # Install icons
46+ - install -Dm644 assets/logo.svg /app/share/icons/hicolor/scalable/apps/dev.bnema.Dumber.svg
47+ - install -Dm644 assets/logo-32.png /app/share/icons/hicolor/32x32/apps/dev.bnema.Dumber.png
48+ # Install metainfo
49+ - install -Dm644 dev.bnema.Dumber.metainfo.xml /app/share/metainfo/dev.bnema.Dumber.metainfo.xml
50+ sources:
51+ - type: dir
52+ path: .
M internal/infrastructure/updater/applier.go +13 -0
@@ -58,9 +58,16 @@ func (a *Applier) stagedBinaryPath() string {
5858 }
5959
6060 // CanSelfUpdate checks if the current binary is writable by the current user.
61+// Returns false if running inside a Flatpak sandbox (updates handled by Flatpak).
6162 func (a *Applier) CanSelfUpdate(ctx context.Context) bool {
6263 log := logging.FromContext(ctx)
6364
65+ // Flatpak sandboxed apps should not self-update; Flatpak handles updates.
66+ if isFlatpak() {
67+ log.Debug().Msg("running in Flatpak sandbox, self-update disabled")
68+ return false
69+ }
70+
6471 binaryPath, err := a.GetBinaryPath()
6572 if err != nil {
6673 log.Debug().Err(err).Msg("failed to get binary path")
@@ -81,6 +88,12 @@ func (a *Applier) CanSelfUpdate(ctx context.Context) bool {
8188 return true
8289 }
8390
91+// isFlatpak returns true if the application is running inside a Flatpak sandbox.
92+func isFlatpak() bool {
93+ _, err := os.Stat("/.flatpak-info")
94+ return err == nil
95+}
96+
8497 // GetBinaryPath returns the path to the currently running binary.
8598 func (*Applier) GetBinaryPath() (string, error) {
8699 path, err := os.Executable()
@@ -58,9 +58,16 @@ func (a *Applier) stagedBinaryPath() string {
58 }58 }
59 59
60 // CanSelfUpdate checks if the current binary is writable by the current user.60 // CanSelfUpdate checks if the current binary is writable by the current user.
61+// Returns false if running inside a Flatpak sandbox (updates handled by Flatpak).
61 func (a *Applier) CanSelfUpdate(ctx context.Context) bool {62 func (a *Applier) CanSelfUpdate(ctx context.Context) bool {
62 log := logging.FromContext(ctx)63 log := logging.FromContext(ctx)
63 64
65+ // Flatpak sandboxed apps should not self-update; Flatpak handles updates.
66+ if isFlatpak() {
67+ log.Debug().Msg("running in Flatpak sandbox, self-update disabled")
68+ return false
69+ }
70+
64 binaryPath, err := a.GetBinaryPath()71 binaryPath, err := a.GetBinaryPath()
65 if err != nil {72 if err != nil {
66 log.Debug().Err(err).Msg("failed to get binary path")73 log.Debug().Err(err).Msg("failed to get binary path")
@@ -81,6 +88,12 @@ func (a *Applier) CanSelfUpdate(ctx context.Context) bool {
81 return true88 return true
82 }89 }
83 90
91+// isFlatpak returns true if the application is running inside a Flatpak sandbox.
92+func isFlatpak() bool {
93+ _, err := os.Stat("/.flatpak-info")
94+ return err == nil
95+}
96+
84 // GetBinaryPath returns the path to the currently running binary.97 // GetBinaryPath returns the path to the currently running binary.
85 func (*Applier) GetBinaryPath() (string, error) {98 func (*Applier) GetBinaryPath() (string, error) {
86 path, err := os.Executable()99 path, err := os.Executable()

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