detached 6701101

refactor(ui): add create method for tab

commit
67011016ea758e4c3ed6c92dfef96e0dc48b92c5
author
brice · · 2026-04-11 18:40 UTC
parent
1475275
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
2 files · +19 -6
M internal/ui/coordinator/tab.go +14 -6
@@ -78,6 +78,10 @@ func (c *TabCoordinator) notifyStateChanged() {
7878
7979 // Create creates a new tab with the given initial URL.
8080 func (c *TabCoordinator) Create(ctx context.Context, initialURL string) (*entity.Tab, error) {
81+ return c.create(ctx, initialURL, true)
82+}
83+
84+func (c *TabCoordinator) create(ctx context.Context, initialURL string, activate bool) (*entity.Tab, error) {
8185 log := logging.FromContext(ctx)
8286
8387 output, err := c.tabsUC.Create(ctx, usecase.CreateTabInput{
@@ -90,13 +94,17 @@ func (c *TabCoordinator) Create(ctx context.Context, initialURL string) (*entity
9094 return nil, err
9195 }
9296
93- // Set new tab as active (updates domain state and tracks previous)
94- c.tabs.SetActive(output.Tab.ID)
97+ if activate {
98+ // Set new tab as active (updates domain state and tracks previous)
99+ c.tabs.SetActive(output.Tab.ID)
100+ }
95101
96102 // Update tab bar
97103 if c.mainWindow != nil && c.mainWindow.TabBar() != nil {
98104 c.mainWindow.TabBar().AddTab(output.Tab)
99- c.mainWindow.TabBar().SetActive(output.Tab.ID)
105+ if activate {
106+ c.mainWindow.TabBar().SetActive(output.Tab.ID)
107+ }
100108 }
101109
102110 // Update tab bar visibility
@@ -107,8 +115,8 @@ func (c *TabCoordinator) Create(ctx context.Context, initialURL string) (*entity
107115 c.onTabCreated(ctx, output.Tab)
108116 }
109117
110- // Switch to the new tab's workspace view
111- if c.onTabSwitched != nil {
118+ // Switch to the new tab's workspace view when activation is requested.
119+ if activate && c.onTabSwitched != nil {
112120 c.onTabSwitched(ctx, output.Tab)
113121 }
114122
@@ -294,7 +302,7 @@ func (c *TabCoordinator) EnsureTabByIndex(ctx context.Context, index int, initia
294302 }
295303
296304 for c.tabs.Count() <= index {
297- if _, err := c.Create(ctx, initialURL); err != nil {
305+ if _, err := c.create(ctx, initialURL, false); err != nil {
298306 log.Error().Err(err).Int("index", index).Msg("failed to create tab while ensuring tab index")
299307 return err
300308 }
@@ -78,6 +78,10 @@ func (c *TabCoordinator) notifyStateChanged() {
78 78
79 // Create creates a new tab with the given initial URL.79 // Create creates a new tab with the given initial URL.
80 func (c *TabCoordinator) Create(ctx context.Context, initialURL string) (*entity.Tab, error) {80 func (c *TabCoordinator) Create(ctx context.Context, initialURL string) (*entity.Tab, error) {
81+ return c.create(ctx, initialURL, true)
82+}
83+
84+func (c *TabCoordinator) create(ctx context.Context, initialURL string, activate bool) (*entity.Tab, error) {
81 log := logging.FromContext(ctx)85 log := logging.FromContext(ctx)
82 86
83 output, err := c.tabsUC.Create(ctx, usecase.CreateTabInput{87 output, err := c.tabsUC.Create(ctx, usecase.CreateTabInput{
@@ -90,13 +94,17 @@ func (c *TabCoordinator) Create(ctx context.Context, initialURL string) (*entity
90 return nil, err94 return nil, err
91 }95 }
92 96
93- // Set new tab as active (updates domain state and tracks previous)97+ if activate {
94- c.tabs.SetActive(output.Tab.ID)98+ // Set new tab as active (updates domain state and tracks previous)
99+ c.tabs.SetActive(output.Tab.ID)
100+ }
95 101
96 // Update tab bar102 // Update tab bar
97 if c.mainWindow != nil && c.mainWindow.TabBar() != nil {103 if c.mainWindow != nil && c.mainWindow.TabBar() != nil {
98 c.mainWindow.TabBar().AddTab(output.Tab)104 c.mainWindow.TabBar().AddTab(output.Tab)
99- c.mainWindow.TabBar().SetActive(output.Tab.ID)105+ if activate {
106+ c.mainWindow.TabBar().SetActive(output.Tab.ID)
107+ }
100 }108 }
101 109
102 // Update tab bar visibility110 // Update tab bar visibility
@@ -107,8 +115,8 @@ func (c *TabCoordinator) Create(ctx context.Context, initialURL string) (*entity
107 c.onTabCreated(ctx, output.Tab)115 c.onTabCreated(ctx, output.Tab)
108 }116 }
109 117
110- // Switch to the new tab's workspace view118+ // Switch to the new tab's workspace view when activation is requested.
111- if c.onTabSwitched != nil {119+ if activate && c.onTabSwitched != nil {
112 c.onTabSwitched(ctx, output.Tab)120 c.onTabSwitched(ctx, output.Tab)
113 }121 }
114 122
@@ -294,7 +302,7 @@ func (c *TabCoordinator) EnsureTabByIndex(ctx context.Context, index int, initia
294 }302 }
295 303
296 for c.tabs.Count() <= index {304 for c.tabs.Count() <= index {
297- if _, err := c.Create(ctx, initialURL); err != nil {305+ if _, err := c.create(ctx, initialURL, false); err != nil {
298 log.Error().Err(err).Int("index", index).Msg("failed to create tab while ensuring tab index")306 log.Error().Err(err).Int("index", index).Msg("failed to create tab while ensuring tab index")
299 return err307 return err
300 }308 }
M internal/ui/dispatcher/keyboard_test.go +5 -0
@@ -43,9 +43,14 @@ func TestKeyboardDispatcher_AltNumberCreatesTabsUntilRequestedIndexExists(t *tes
4343 require.Len(t, tabs.Tabs, 3)
4444 require.NotNil(t, tabs.ActiveTab())
4545 assert.Equal(t, tabs.TabAt(2).ID, tabs.ActiveTabID)
46+ assert.Equal(t, tabs.TabAt(0).ID, tabs.PreviousActiveTabID)
4647 assert.Equal(t, 2, tabs.ActiveTab().Position)
4748 assert.Equal(t, domainurl.Normalize("https://new.example"), tabs.TabAt(1).Workspace.Root.Pane.URI)
4849 assert.Equal(t, domainurl.Normalize("https://new.example"), tabs.TabAt(2).Workspace.Root.Pane.URI)
50+
51+ err = tabCoord.SwitchToLastActive(ctx)
52+ require.NoError(t, err)
53+ assert.Equal(t, tabs.TabAt(0).ID, tabs.ActiveTabID)
4954 }
5055
5156 func TestKeyboardDispatcher_AltNumberSwitchesToExistingTabWithoutNewPaneURL(t *testing.T) {
@@ -43,9 +43,14 @@ func TestKeyboardDispatcher_AltNumberCreatesTabsUntilRequestedIndexExists(t *tes
43 require.Len(t, tabs.Tabs, 3)43 require.Len(t, tabs.Tabs, 3)
44 require.NotNil(t, tabs.ActiveTab())44 require.NotNil(t, tabs.ActiveTab())
45 assert.Equal(t, tabs.TabAt(2).ID, tabs.ActiveTabID)45 assert.Equal(t, tabs.TabAt(2).ID, tabs.ActiveTabID)
46+ assert.Equal(t, tabs.TabAt(0).ID, tabs.PreviousActiveTabID)
46 assert.Equal(t, 2, tabs.ActiveTab().Position)47 assert.Equal(t, 2, tabs.ActiveTab().Position)
47 assert.Equal(t, domainurl.Normalize("https://new.example"), tabs.TabAt(1).Workspace.Root.Pane.URI)48 assert.Equal(t, domainurl.Normalize("https://new.example"), tabs.TabAt(1).Workspace.Root.Pane.URI)
48 assert.Equal(t, domainurl.Normalize("https://new.example"), tabs.TabAt(2).Workspace.Root.Pane.URI)49 assert.Equal(t, domainurl.Normalize("https://new.example"), tabs.TabAt(2).Workspace.Root.Pane.URI)
50+
51+ err = tabCoord.SwitchToLastActive(ctx)
52+ require.NoError(t, err)
53+ assert.Equal(t, tabs.TabAt(0).ID, tabs.ActiveTabID)
49 }54 }
50 55
51 func TestKeyboardDispatcher_AltNumberSwitchesToExistingTabWithoutNewPaneURL(t *testing.T) {56 func TestKeyboardDispatcher_AltNumberSwitchesToExistingTabWithoutNewPaneURL(t *testing.T) {

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