main 5041353

external_url_relay_reuse.go RawHistoryFolder

1package ui
2
3import (
4	"context"
5
6	"github.com/bnema/dumber/internal/domain/entity"
7	domainurl "github.com/bnema/dumber/internal/domain/url"
8	"github.com/bnema/dumber/internal/logging"
9)
10
11// openRelayURLOnMainThread opens a relayed external URL, reusing the pane from
12// a recent identical request when it still exists. Must run on the main thread.
13func (a *App) openRelayURLOnMainThread(ctx context.Context, url string, cfg entity.RuntimeConfigSnapshot) error {
14	if a.reuseRelayPane(ctx, url) {
15		return nil
16	}
17	if err := a.openExternalURLOnMainThread(ctx, url, cfg); err != nil {
18		return err
19	}
20	a.recordRelayPane(url)
21	return nil
22}
23
24// reuseRelayPane navigates and focuses the pane previously opened for url.
25// It reports false when there is nothing reusable and a new pane should be created.
26func (a *App) reuseRelayPane(ctx context.Context, url string) bool {
27	paneID, ok := a.externalRelayPanes.Lookup(url, func(id entity.PaneID) bool {
28		return a.browserWindowForPane(id) != nil
29	})
30	if !ok {
31		return false
32	}
33	log := logging.FromContext(ctx)
34
35	bw := a.browserWindowForPane(paneID)
36	var tab *entity.Tab
37	if bw != nil && bw.tabs != nil {
38		for _, candidate := range bw.tabs.Tabs {
39			if candidate != nil && candidate.Workspace != nil && candidate.Workspace.FindPane(paneID) != nil {
40				tab = candidate
41				break
42			}
43		}
44	}
45	if tab == nil || a.navCoord == nil || a.contentCoord == nil {
46		a.externalRelayPanes.Forget(url)
47		return false
48	}
49
50	// Reload the URL first: when the pane cannot be navigated, fall back to
51	// creating a fresh pane instead of focusing a dead one.
52	if err := a.navCoord.NavigateWebView(ctx, url, paneID, a.contentCoord.GetWebView(paneID)); err != nil {
53		log.Warn().Err(err).
54			Str("pane_id", string(paneID)).
55			Str("url_host", logging.SafeURLHost(url)).
56			Msg("ui: external URL relay pane could not be navigated; opening a new pane")
57		a.externalRelayPanes.Forget(url)
58		return false
59	}
60
61	a.focusRelayPane(ctx, bw, tab, paneID)
62	log.Debug().
63		Str("pane_id", string(paneID)).
64		Str("url_host", logging.SafeURLHost(url)).
65		Msg("ui: external URL reused relay pane")
66	return true
67}
68
69func (a *App) focusRelayPane(ctx context.Context, bw *browserWindow, tab *entity.Tab, paneID entity.PaneID) {
70	if bw.tabs.ActiveTabID != tab.ID {
71		if a.tabCoord != nil {
72			if err := a.tabCoord.Switch(ctx, a.tabTargetForBrowserWindow(bw), tab.ID); err != nil {
73				logging.FromContext(ctx).Warn().Err(err).
74					Str("tab_id", string(tab.ID)).
75					Msg("ui: failed to switch to relay pane tab")
76			}
77		} else {
78			bw.tabs.SetActive(tab.ID)
79			if bw.mainWindow != nil && bw.mainWindow.TabBar() != nil {
80				bw.mainWindow.TabBar().SetActive(tab.ID)
81			}
82			a.switchWorkspaceView(ctx, tab.ID)
83		}
84	}
85
86	wsView := a.workspaceViews[tab.ID]
87	if a.wsCoord != nil {
88		a.wsCoord.FocusPaneByID(ctx, tab.Workspace, wsView, paneID)
89	} else {
90		tab.Workspace.ActivePaneID = paneID
91	}
92
93	// A floating pane over the tab would hide the relay pane and keep the
94	// active-pane override pointing at itself.
95	a.hideVisibleFloatingSessions(ctx, tab.ID, nil)
96
97	if bw.mainWindow != nil {
98		bw.mainWindow.Show()
99	}
100	a.activateBrowserWindow(bw)
101	a.syncFloatingFocus()
102	if wsView != nil {
103		wsView.FocusPane(paneID)
104	}
105}
106
107// recordRelayPane remembers the pane that was just created for url. After a
108// successful placement the new pane is the active pane of the last-focused
109// window's active tab (tabbed, split, stacked, and fresh window alike).
110func (a *App) recordRelayPane(url string) {
111	ws := a.activeWorkspaceForBrowserWindow(a.lastFocusedBrowserWindow())
112	if ws == nil {
113		return
114	}
115	active := ws.ActivePane()
116	if active == nil || active.Pane == nil || domainurl.RelayURLKey(active.Pane.URI) != domainurl.RelayURLKey(url) {
117		return
118	}
119	a.externalRelayPanes.Record(url, active.Pane.ID)
120}

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