detached 2bdff8a

fix(vim): format rebased native mode changes

commit
2bdff8a0340a943ac4072207b3feea04fabc67ba
author
brice · · 2026-08-13 08:53 UTC
committer
bnema · · 2026-08-14 02:42 UTC
parent
dd428b6
signature
unsigned
9 files · +86 -67
M internal/infrastructure/config/defaults.go +25 -25
@@ -38,23 +38,23 @@ const (
3838 defaultOmniboxAutoOpenOnNewPane = false
3939
4040 // Workspace defaults
41- defaultPaneActivationShortcut = "ctrl+p"
42- defaultPaneTimeoutMilliseconds = 3000
43- defaultTabActivationShortcut = "ctrl+t"
44- defaultTabTimeoutMilliseconds = 3000
45- defaultResizeActivationShortcut = "ctrl+n"
46- defaultResizeTimeoutMilliseconds = 3000
41+ defaultPaneActivationShortcut = "ctrl+p"
42+ defaultPaneTimeoutMilliseconds = 3000
43+ defaultTabActivationShortcut = "ctrl+t"
44+ defaultTabTimeoutMilliseconds = 3000
45+ defaultResizeActivationShortcut = "ctrl+n"
46+ defaultResizeTimeoutMilliseconds = 3000
4747 defaultPageActivationShortcut = "ctrl+y"
4848 defaultPageTimeoutMilliseconds = 0
4949 defaultPageSequenceTimeoutMilliseconds = 500
50- defaultResizeStepPercent = 5.0
51- defaultResizeMinPanePercent = 10.0
52- defaultTabBarPosition = "bottom"
53- defaultPopupPlacement = "right"
54- defaultExternalLinkBehavior = ExternalLinkBehaviorWindowed
55- defaultExternalLinkPlacement = ExternalLinkPlacementRight
56- defaultFloatingPaneWidthPct = 0.82
57- defaultFloatingPaneHeightPct = 0.72
50+ defaultResizeStepPercent = 5.0
51+ defaultResizeMinPanePercent = 10.0
52+ defaultTabBarPosition = "bottom"
53+ defaultPopupPlacement = "right"
54+ defaultExternalLinkBehavior = ExternalLinkBehaviorWindowed
55+ defaultExternalLinkPlacement = ExternalLinkPlacementRight
56+ defaultFloatingPaneWidthPct = 0.82
57+ defaultFloatingPaneHeightPct = 0.72
5858
5959 // Session defaults
6060 defaultSessionActivationShortcut = "ctrl+o"
@@ -318,17 +318,17 @@ func DefaultConfig() *Config {
318318 "vim-scroll-up-fast": {Keys: []string{"shift+k"}, Desc: "Scroll page up fast"},
319319 "confirm": {Keys: []string{"enter"}, Desc: "Confirm action"},
320320 "cancel": {Keys: []string{"escape"}, Desc: "Cancel/exit mode"},
321- "heading-next": {Keys: []string{"]]"}, Desc: "Jump to next heading"},
322- "heading-prev": {Keys: []string{"[["}, Desc: "Jump to previous heading"},
323- "code-next": {Keys: []string{"]c"}, Desc: "Jump to next code block"},
324- "code-prev": {Keys: []string{"[c"}, Desc: "Jump to previous code block"},
325- "table-next": {Keys: []string{"]t"}, Desc: "Jump to next table"},
326- "image-next": {Keys: []string{"]i"}, Desc: "Jump to next image"},
327- "list-next": {Keys: []string{"]l"}, Desc: "Jump to next list"},
328- "outline": {Keys: []string{"gO"}, Desc: "Show page outline"},
329- "yank-section": {Keys: []string{"yah"}, Desc: "Yank current section"},
330- "half-page-down": {Keys: []string{"<C-d>"}, Desc: "Scroll half page down"},
331- "half-page-up": {Keys: []string{"<C-u>"}, Desc: "Scroll half page up"},
321+ "heading-next": {Keys: []string{"]]"}, Desc: "Jump to next heading"},
322+ "heading-prev": {Keys: []string{"[["}, Desc: "Jump to previous heading"},
323+ "code-next": {Keys: []string{"]c"}, Desc: "Jump to next code block"},
324+ "code-prev": {Keys: []string{"[c"}, Desc: "Jump to previous code block"},
325+ "table-next": {Keys: []string{"]t"}, Desc: "Jump to next table"},
326+ "image-next": {Keys: []string{"]i"}, Desc: "Jump to next image"},
327+ "list-next": {Keys: []string{"]l"}, Desc: "Jump to next list"},
328+ "outline": {Keys: []string{"gO"}, Desc: "Show page outline"},
329+ "yank-section": {Keys: []string{"yah"}, Desc: "Yank current section"},
330+ "half-page-down": {Keys: []string{"<C-d>"}, Desc: "Scroll half page down"},
331+ "half-page-up": {Keys: []string{"<C-u>"}, Desc: "Scroll half page up"},
332332 },
333333 },
334334 ResizeMode: ResizeModeConfig{
@@ -38,23 +38,23 @@ const (
38 defaultOmniboxAutoOpenOnNewPane = false38 defaultOmniboxAutoOpenOnNewPane = false
39 39
40 // Workspace defaults40 // Workspace defaults
41- defaultPaneActivationShortcut = "ctrl+p"41+ defaultPaneActivationShortcut = "ctrl+p"
42- defaultPaneTimeoutMilliseconds = 300042+ defaultPaneTimeoutMilliseconds = 3000
43- defaultTabActivationShortcut = "ctrl+t"43+ defaultTabActivationShortcut = "ctrl+t"
44- defaultTabTimeoutMilliseconds = 300044+ defaultTabTimeoutMilliseconds = 3000
45- defaultResizeActivationShortcut = "ctrl+n"45+ defaultResizeActivationShortcut = "ctrl+n"
46- defaultResizeTimeoutMilliseconds = 300046+ defaultResizeTimeoutMilliseconds = 3000
47 defaultPageActivationShortcut = "ctrl+y"47 defaultPageActivationShortcut = "ctrl+y"
48 defaultPageTimeoutMilliseconds = 048 defaultPageTimeoutMilliseconds = 0
49 defaultPageSequenceTimeoutMilliseconds = 50049 defaultPageSequenceTimeoutMilliseconds = 500
50- defaultResizeStepPercent = 5.050+ defaultResizeStepPercent = 5.0
51- defaultResizeMinPanePercent = 10.051+ defaultResizeMinPanePercent = 10.0
52- defaultTabBarPosition = "bottom"52+ defaultTabBarPosition = "bottom"
53- defaultPopupPlacement = "right"53+ defaultPopupPlacement = "right"
54- defaultExternalLinkBehavior = ExternalLinkBehaviorWindowed54+ defaultExternalLinkBehavior = ExternalLinkBehaviorWindowed
55- defaultExternalLinkPlacement = ExternalLinkPlacementRight55+ defaultExternalLinkPlacement = ExternalLinkPlacementRight
56- defaultFloatingPaneWidthPct = 0.8256+ defaultFloatingPaneWidthPct = 0.82
57- defaultFloatingPaneHeightPct = 0.7257+ defaultFloatingPaneHeightPct = 0.72
58 58
59 // Session defaults59 // Session defaults
60 defaultSessionActivationShortcut = "ctrl+o"60 defaultSessionActivationShortcut = "ctrl+o"
@@ -318,17 +318,17 @@ func DefaultConfig() *Config {
318 "vim-scroll-up-fast": {Keys: []string{"shift+k"}, Desc: "Scroll page up fast"},318 "vim-scroll-up-fast": {Keys: []string{"shift+k"}, Desc: "Scroll page up fast"},
319 "confirm": {Keys: []string{"enter"}, Desc: "Confirm action"},319 "confirm": {Keys: []string{"enter"}, Desc: "Confirm action"},
320 "cancel": {Keys: []string{"escape"}, Desc: "Cancel/exit mode"},320 "cancel": {Keys: []string{"escape"}, Desc: "Cancel/exit mode"},
321- "heading-next": {Keys: []string{"]]"}, Desc: "Jump to next heading"},321+ "heading-next": {Keys: []string{"]]"}, Desc: "Jump to next heading"},
322- "heading-prev": {Keys: []string{"[["}, Desc: "Jump to previous heading"},322+ "heading-prev": {Keys: []string{"[["}, Desc: "Jump to previous heading"},
323- "code-next": {Keys: []string{"]c"}, Desc: "Jump to next code block"},323+ "code-next": {Keys: []string{"]c"}, Desc: "Jump to next code block"},
324- "code-prev": {Keys: []string{"[c"}, Desc: "Jump to previous code block"},324+ "code-prev": {Keys: []string{"[c"}, Desc: "Jump to previous code block"},
325- "table-next": {Keys: []string{"]t"}, Desc: "Jump to next table"},325+ "table-next": {Keys: []string{"]t"}, Desc: "Jump to next table"},
326- "image-next": {Keys: []string{"]i"}, Desc: "Jump to next image"},326+ "image-next": {Keys: []string{"]i"}, Desc: "Jump to next image"},
327- "list-next": {Keys: []string{"]l"}, Desc: "Jump to next list"},327+ "list-next": {Keys: []string{"]l"}, Desc: "Jump to next list"},
328- "outline": {Keys: []string{"gO"}, Desc: "Show page outline"},328+ "outline": {Keys: []string{"gO"}, Desc: "Show page outline"},
329- "yank-section": {Keys: []string{"yah"}, Desc: "Yank current section"},329+ "yank-section": {Keys: []string{"yah"}, Desc: "Yank current section"},
330- "half-page-down": {Keys: []string{"<C-d>"}, Desc: "Scroll half page down"},330+ "half-page-down": {Keys: []string{"<C-d>"}, Desc: "Scroll half page down"},
331- "half-page-up": {Keys: []string{"<C-u>"}, Desc: "Scroll half page up"},331+ "half-page-up": {Keys: []string{"<C-u>"}, Desc: "Scroll half page up"},
332 },332 },
333 },333 },
334 ResizeMode: ResizeModeConfig{334 ResizeMode: ResizeModeConfig{
M internal/infrastructure/config/loader.go +2 -0
@@ -708,6 +708,8 @@ func (m *Manager) setWorkspaceDefaults(defaults *Config) {
708708 m.viper.SetDefault("workspace.tab_mode.actions", defaults.Workspace.TabMode.Actions)
709709 m.viper.SetDefault("workspace.vim_mode.activation_shortcut", defaults.Workspace.VimMode.ActivationShortcut)
710710 m.viper.SetDefault("workspace.vim_mode.timeout_ms", defaults.Workspace.VimMode.TimeoutMilliseconds)
711+ m.viper.SetDefault("workspace.vim_mode.sequence_timeout_ms", defaults.Workspace.VimMode.SequenceTimeoutMilliseconds)
712+ m.viper.SetDefault("workspace.vim_mode.preload_accessibility", defaults.Workspace.VimMode.PreloadAccessibility)
711713 m.viper.SetDefault("workspace.vim_mode.actions", defaults.Workspace.VimMode.Actions)
712714 m.viper.SetDefault("workspace.resize_mode.activation_shortcut", defaults.Workspace.ResizeMode.ActivationShortcut)
713715 m.viper.SetDefault("workspace.resize_mode.timeout_ms", defaults.Workspace.ResizeMode.TimeoutMilliseconds)
@@ -708,6 +708,8 @@ func (m *Manager) setWorkspaceDefaults(defaults *Config) {
708 m.viper.SetDefault("workspace.tab_mode.actions", defaults.Workspace.TabMode.Actions)708 m.viper.SetDefault("workspace.tab_mode.actions", defaults.Workspace.TabMode.Actions)
709 m.viper.SetDefault("workspace.vim_mode.activation_shortcut", defaults.Workspace.VimMode.ActivationShortcut)709 m.viper.SetDefault("workspace.vim_mode.activation_shortcut", defaults.Workspace.VimMode.ActivationShortcut)
710 m.viper.SetDefault("workspace.vim_mode.timeout_ms", defaults.Workspace.VimMode.TimeoutMilliseconds)710 m.viper.SetDefault("workspace.vim_mode.timeout_ms", defaults.Workspace.VimMode.TimeoutMilliseconds)
711+ m.viper.SetDefault("workspace.vim_mode.sequence_timeout_ms", defaults.Workspace.VimMode.SequenceTimeoutMilliseconds)
712+ m.viper.SetDefault("workspace.vim_mode.preload_accessibility", defaults.Workspace.VimMode.PreloadAccessibility)
711 m.viper.SetDefault("workspace.vim_mode.actions", defaults.Workspace.VimMode.Actions)713 m.viper.SetDefault("workspace.vim_mode.actions", defaults.Workspace.VimMode.Actions)
712 m.viper.SetDefault("workspace.resize_mode.activation_shortcut", defaults.Workspace.ResizeMode.ActivationShortcut)714 m.viper.SetDefault("workspace.resize_mode.activation_shortcut", defaults.Workspace.ResizeMode.ActivationShortcut)
713 m.viper.SetDefault("workspace.resize_mode.timeout_ms", defaults.Workspace.ResizeMode.TimeoutMilliseconds)715 m.viper.SetDefault("workspace.resize_mode.timeout_ms", defaults.Workspace.ResizeMode.TimeoutMilliseconds)
M internal/infrastructure/config/schema_provider.go +19 -2
@@ -477,7 +477,7 @@ func (*SchemaProvider) getWorkspaceKeys(defaults *Config) []entity.ConfigKeyInfo
477477 Key: "workspace.vim_mode.activation_shortcut",
478478 Type: "string",
479479 Default: defaults.Workspace.VimMode.ActivationShortcut,
480- Description: "Shortcut to enter page scroll mode",
480+ Description: "Shortcut to enter vim scroll mode",
481481 Section: SectionWorkspace,
482482 },
483483 {
@@ -500,7 +500,7 @@ func (*SchemaProvider) getWorkspaceKeys(defaults *Config) []entity.ConfigKeyInfo
500500 Key: "workspace.vim_mode.preload_accessibility",
501501 Type: "bool",
502502 Default: fmt.Sprintf("%t", defaults.Workspace.VimMode.PreloadAccessibility),
503- Description: "Pre-enable browser accessibility when a WebView is shown (default false; enable on Page Mode enter otherwise)",
503+ Description: "Pre-enable browser accessibility when a WebView is shown (default false; enable on Vim Mode enter otherwise)",
504504 Section: SectionWorkspace,
505505 },
506506 {
@@ -579,6 +579,23 @@ func (*SchemaProvider) getWorkspaceKeys(defaults *Config) []entity.ConfigKeyInfo
579579 Description: "Named floating pane URL profiles with keys, url, and optional desc",
580580 Section: SectionWorkspace,
581581 },
582+ // External links
583+ {
584+ Key: "workspace.external_links.behavior",
585+ Type: "string",
586+ Default: string(defaults.Workspace.ExternalLinks.Behavior),
587+ Description: "How URLs opened from external applications are placed",
588+ Values: []string{"windowed", "tabbed", "split", "stacked"},
589+ Section: SectionWorkspace,
590+ },
591+ {
592+ Key: "workspace.external_links.placement",
593+ Type: "string",
594+ Default: string(defaults.Workspace.ExternalLinks.Placement),
595+ Description: "Placement direction for split external links",
596+ Values: []string{"right", "left", "top", "bottom"},
597+ Section: SectionWorkspace,
598+ },
582599 // Browsing contexts
583600 {
584601 Key: "workspace.browsing_contexts.behavior",
@@ -477,7 +477,7 @@ func (*SchemaProvider) getWorkspaceKeys(defaults *Config) []entity.ConfigKeyInfo
477 Key: "workspace.vim_mode.activation_shortcut",477 Key: "workspace.vim_mode.activation_shortcut",
478 Type: "string",478 Type: "string",
479 Default: defaults.Workspace.VimMode.ActivationShortcut,479 Default: defaults.Workspace.VimMode.ActivationShortcut,
480- Description: "Shortcut to enter page scroll mode",480+ Description: "Shortcut to enter vim scroll mode",
481 Section: SectionWorkspace,481 Section: SectionWorkspace,
482 },482 },
483 {483 {
@@ -500,7 +500,7 @@ func (*SchemaProvider) getWorkspaceKeys(defaults *Config) []entity.ConfigKeyInfo
500 Key: "workspace.vim_mode.preload_accessibility",500 Key: "workspace.vim_mode.preload_accessibility",
501 Type: "bool",501 Type: "bool",
502 Default: fmt.Sprintf("%t", defaults.Workspace.VimMode.PreloadAccessibility),502 Default: fmt.Sprintf("%t", defaults.Workspace.VimMode.PreloadAccessibility),
503- Description: "Pre-enable browser accessibility when a WebView is shown (default false; enable on Page Mode enter otherwise)",503+ Description: "Pre-enable browser accessibility when a WebView is shown (default false; enable on Vim Mode enter otherwise)",
504 Section: SectionWorkspace,504 Section: SectionWorkspace,
505 },505 },
506 {506 {
@@ -579,6 +579,23 @@ func (*SchemaProvider) getWorkspaceKeys(defaults *Config) []entity.ConfigKeyInfo
579 Description: "Named floating pane URL profiles with keys, url, and optional desc",579 Description: "Named floating pane URL profiles with keys, url, and optional desc",
580 Section: SectionWorkspace,580 Section: SectionWorkspace,
581 },581 },
582+ // External links
583+ {
584+ Key: "workspace.external_links.behavior",
585+ Type: "string",
586+ Default: string(defaults.Workspace.ExternalLinks.Behavior),
587+ Description: "How URLs opened from external applications are placed",
588+ Values: []string{"windowed", "tabbed", "split", "stacked"},
589+ Section: SectionWorkspace,
590+ },
591+ {
592+ Key: "workspace.external_links.placement",
593+ Type: "string",
594+ Default: string(defaults.Workspace.ExternalLinks.Placement),
595+ Description: "Placement direction for split external links",
596+ Values: []string{"right", "left", "top", "bottom"},
597+ Section: SectionWorkspace,
598+ },
582 // Browsing contexts599 // Browsing contexts
583 {600 {
584 Key: "workspace.browsing_contexts.behavior",601 Key: "workspace.browsing_contexts.behavior",
M internal/infrastructure/config/schema_provider_test.go +2 -2
@@ -34,8 +34,8 @@ func TestConfigurationReferenceCoversSchemaKeys(t *testing.T) {
3434 // schemaKeysPendingProductDocs lists SchemaProvider keys intentionally omitted from
3535 // product docs until a dedicated docs task lands them.
3636 var schemaKeysPendingProductDocs = map[string]struct{}{
37- "workspace.page_mode.sequence_timeout_ms": {},
38- "workspace.page_mode.preload_accessibility": {},
37+ "workspace.vim_mode.sequence_timeout_ms": {},
38+ "workspace.vim_mode.preload_accessibility": {},
3939 }
4040
4141 func configurationReferenceKeys(t *testing.T) map[string]struct{} {
@@ -34,8 +34,8 @@ func TestConfigurationReferenceCoversSchemaKeys(t *testing.T) {
34 // schemaKeysPendingProductDocs lists SchemaProvider keys intentionally omitted from34 // schemaKeysPendingProductDocs lists SchemaProvider keys intentionally omitted from
35 // product docs until a dedicated docs task lands them.35 // product docs until a dedicated docs task lands them.
36 var schemaKeysPendingProductDocs = map[string]struct{}{36 var schemaKeysPendingProductDocs = map[string]struct{}{
37- "workspace.page_mode.sequence_timeout_ms": {},37+ "workspace.vim_mode.sequence_timeout_ms": {},
38- "workspace.page_mode.preload_accessibility": {},38+ "workspace.vim_mode.preload_accessibility": {},
39 }39 }
40 40
41 func configurationReferenceKeys(t *testing.T) map[string]struct{} {41 func configurationReferenceKeys(t *testing.T) map[string]struct{} {
M internal/infrastructure/config/vim_mode_config_test.go +2 -2
@@ -404,8 +404,8 @@ func TestValidateModalModeActions_RegressionTable(t *testing.T) {
404404 "vim-scroll-right": {Keys: []string{"l"}},
405405 "vim-scroll-down-fast": {Keys: []string{"shift+j"}},
406406 "vim-scroll-up-fast": {Keys: []string{"shift+k"}},
407- "confirm": {Keys: []string{"enter"}},
408- "cancel": {Keys: []string{"escape"}},
407+ "confirm": {Keys: []string{"enter"}},
408+ "cancel": {Keys: []string{"escape"}},
409409 },
410410 want: "",
411411 },
@@ -404,8 +404,8 @@ func TestValidateModalModeActions_RegressionTable(t *testing.T) {
404 "vim-scroll-right": {Keys: []string{"l"}},404 "vim-scroll-right": {Keys: []string{"l"}},
405 "vim-scroll-down-fast": {Keys: []string{"shift+j"}},405 "vim-scroll-down-fast": {Keys: []string{"shift+j"}},
406 "vim-scroll-up-fast": {Keys: []string{"shift+k"}},406 "vim-scroll-up-fast": {Keys: []string{"shift+k"}},
407- "confirm": {Keys: []string{"enter"}},407+ "confirm": {Keys: []string{"enter"}},
408- "cancel": {Keys: []string{"escape"}},408+ "cancel": {Keys: []string{"escape"}},
409 },409 },
410 want: "",410 want: "",
411 },411 },
M internal/ui/app.go +2 -2
@@ -4069,8 +4069,8 @@ func (a *App) bindVimModeSequenceToaster(ctx context.Context, bw *browserWindow)
40694069 Msg("vim mode pending sequence toaster bound")
40704070 }
40714071
4072-// showPendingSequence shows "PAGE MODE · pending" on the per-window modeToaster
4073-// while a sequence is incomplete, and restores stable "PAGE MODE" when pending
4072+// showPendingSequence shows "VIM MODE · pending" on the per-window modeToaster
4073+// while a sequence is incomplete, and restores stable "VIM MODE" when pending
40744074 // clears. Mode exit/hide is handled by handleModeChange/updateModeIndicatorToaster;
40754075 // silent reset under ModalState avoids stale pending callbacks.
40764076 func (a *App) showPendingSequence(ctx context.Context, bw *browserWindow, pending string) {
@@ -4069,8 +4069,8 @@ func (a *App) bindVimModeSequenceToaster(ctx context.Context, bw *browserWindow)
4069 Msg("vim mode pending sequence toaster bound")4069 Msg("vim mode pending sequence toaster bound")
4070 }4070 }
4071 4071
4072-// showPendingSequence shows "PAGE MODE · pending" on the per-window modeToaster4072+// showPendingSequence shows "VIM MODE · pending" on the per-window modeToaster
4073-// while a sequence is incomplete, and restores stable "PAGE MODE" when pending4073+// while a sequence is incomplete, and restores stable "VIM MODE" when pending
4074 // clears. Mode exit/hide is handled by handleModeChange/updateModeIndicatorToaster;4074 // clears. Mode exit/hide is handled by handleModeChange/updateModeIndicatorToaster;
4075 // silent reset under ModalState avoids stale pending callbacks.4075 // silent reset under ModalState avoids stale pending callbacks.
4076 func (a *App) showPendingSequence(ctx context.Context, bw *browserWindow, pending string) {4076 func (a *App) showPendingSequence(ctx context.Context, bw *browserWindow, pending string) {
M internal/ui/input/keyboard.go +3 -3
@@ -141,7 +141,7 @@ func NewKeyboardHandler(ctx context.Context, workspace *entity.WorkspaceConfig,
141141 },
142142 vimScrollRepeatRemove: glib.SourceRemove,
143143 vimScrollNow: time.Now,
144- seq: newVimModeSequenceState(shortcuts.VimModeSequences(), seqTimeout),
144+ seq: newVimModeSequenceState(shortcuts.VimModeSequences(), seqTimeout),
145145 }
146146 h.SetOnModeChange(nil)
147147
@@ -327,7 +327,7 @@ func (h *KeyboardHandler) installGTKMainThreadSchedulers() {
327327 }
328328 h.modal.SetMainThreadScheduler(schedule)
329329 h.SetSequenceMainThreadScheduler(schedule)
330- logging.FromContext(h.ctx).Debug().Msg("gtk main-thread schedulers installed for modal and page-mode sequences")
330+ logging.FromContext(h.ctx).Debug().Msg("gtk main-thread schedulers installed for modal and vim-mode sequences")
331331 }
332332
333333 // Detach removes the keyboard handler from a live GTK window and releases its
@@ -437,7 +437,7 @@ func (h *KeyboardHandler) handleKeyPress(keyval, keycode uint, state gdk.Modifie
437437 // Fall through to shortcut processing below
438438 }
439439
440- // Page-mode multi-key sequences run before legacy single-chord lookup.
440+ // Vim-mode multi-key sequences run before legacy single-chord lookup.
441441 if h.feedVimModeSequence(keyval, state) {
442442 return true
443443 }
@@ -141,7 +141,7 @@ func NewKeyboardHandler(ctx context.Context, workspace *entity.WorkspaceConfig,
141 },141 },
142 vimScrollRepeatRemove: glib.SourceRemove,142 vimScrollRepeatRemove: glib.SourceRemove,
143 vimScrollNow: time.Now,143 vimScrollNow: time.Now,
144- seq: newVimModeSequenceState(shortcuts.VimModeSequences(), seqTimeout),144+ seq: newVimModeSequenceState(shortcuts.VimModeSequences(), seqTimeout),
145 }145 }
146 h.SetOnModeChange(nil)146 h.SetOnModeChange(nil)
147 147
@@ -327,7 +327,7 @@ func (h *KeyboardHandler) installGTKMainThreadSchedulers() {
327 }327 }
328 h.modal.SetMainThreadScheduler(schedule)328 h.modal.SetMainThreadScheduler(schedule)
329 h.SetSequenceMainThreadScheduler(schedule)329 h.SetSequenceMainThreadScheduler(schedule)
330- logging.FromContext(h.ctx).Debug().Msg("gtk main-thread schedulers installed for modal and page-mode sequences")330+ logging.FromContext(h.ctx).Debug().Msg("gtk main-thread schedulers installed for modal and vim-mode sequences")
331 }331 }
332 332
333 // Detach removes the keyboard handler from a live GTK window and releases its333 // Detach removes the keyboard handler from a live GTK window and releases its
@@ -437,7 +437,7 @@ func (h *KeyboardHandler) handleKeyPress(keyval, keycode uint, state gdk.Modifie
437 // Fall through to shortcut processing below437 // Fall through to shortcut processing below
438 }438 }
439 439
440- // Page-mode multi-key sequences run before legacy single-chord lookup.440+ // Vim-mode multi-key sequences run before legacy single-chord lookup.
441 if h.feedVimModeSequence(keyval, state) {441 if h.feedVimModeSequence(keyval, state) {
442 return true442 return true
443 }443 }
M internal/ui/input/vimkeys_dispatch.go +2 -2
@@ -12,7 +12,7 @@ import (
1212 )
1313
1414 // trieOwnsBinding reports whether a raw config binding belongs to the Vim
15-// sequence trie rather than the legacy single-chord Page Mode table.
15+// sequence trie rather than the legacy single-chord Vim Mode table.
1616 // Multi-key sequences and Vim-only chords that ParseKeyString cannot read are owned.
1717 func trieOwnsBinding(raw string) (vimkeys.Sequence, bool) {
1818 seq, err := vimkeys.ParseBinding(raw)
@@ -194,7 +194,7 @@ func (h *KeyboardHandler) teardownSequenceState() {
194194 h.seq.onPending = nil
195195 h.seq.onAction = nil
196196 h.seq.scheduleOnMainThread = nil
197- log.Debug().Msg("page mode sequence state torn down without notify")
197+ log.Debug().Msg("vim mode sequence state torn down without notify")
198198 }
199199
200200 // InvalidateSequenceMatcher stops timing, bumps generation, and replaces the
@@ -12,7 +12,7 @@ import (
12 )12 )
13 13
14 // trieOwnsBinding reports whether a raw config binding belongs to the Vim14 // trieOwnsBinding reports whether a raw config binding belongs to the Vim
15-// sequence trie rather than the legacy single-chord Page Mode table.15+// sequence trie rather than the legacy single-chord Vim Mode table.
16 // Multi-key sequences and Vim-only chords that ParseKeyString cannot read are owned.16 // Multi-key sequences and Vim-only chords that ParseKeyString cannot read are owned.
17 func trieOwnsBinding(raw string) (vimkeys.Sequence, bool) {17 func trieOwnsBinding(raw string) (vimkeys.Sequence, bool) {
18 seq, err := vimkeys.ParseBinding(raw)18 seq, err := vimkeys.ParseBinding(raw)
@@ -194,7 +194,7 @@ func (h *KeyboardHandler) teardownSequenceState() {
194 h.seq.onPending = nil194 h.seq.onPending = nil
195 h.seq.onAction = nil195 h.seq.onAction = nil
196 h.seq.scheduleOnMainThread = nil196 h.seq.scheduleOnMainThread = nil
197- log.Debug().Msg("page mode sequence state torn down without notify")197+ log.Debug().Msg("vim mode sequence state torn down without notify")
198 }198 }
199 199
200 // InvalidateSequenceMatcher stops timing, bumps generation, and replaces the200 // InvalidateSequenceMatcher stops timing, bumps generation, and replaces the
M internal/ui/input/vimkeys_dispatch_test.go +29 -29
@@ -137,13 +137,13 @@ func TestTrieOwnsBinding_CrossContractAtoms(t *testing.T) {
137137 func TestBuildVimModeTrie(t *testing.T) {
138138 cfg := &entity.VimModeConfig{
139139 Actions: map[string]entity.ActionBinding{
140- "page-scroll-down": {Keys: []string{"j"}},
141- "heading-next": {Keys: []string{"]]"}},
142- "yank-section": {Keys: []string{"yah"}},
143- "outline": {Keys: []string{"gO"}},
144- "half-page-down": {Keys: []string{"<C-d>"}},
145- "confirm": {Keys: []string{"enter"}},
146- "cancel": {Keys: []string{"escape"}},
140+ "vim-scroll-down": {Keys: []string{"j"}},
141+ "heading-next": {Keys: []string{"]]"}},
142+ "yank-section": {Keys: []string{"yah"}},
143+ "outline": {Keys: []string{"gO"}},
144+ "half-page-down": {Keys: []string{"<C-d>"}},
145+ "confirm": {Keys: []string{"enter"}},
146+ "cancel": {Keys: []string{"escape"}},
147147 },
148148 }
149149
@@ -184,9 +184,9 @@ func TestBuildVimModeTrie_ConflictKeepsFirstSortedWinner(t *testing.T) {
184184 // Losing raw key must not be marked owned (insert failed).
185185 cfg := &entity.VimModeConfig{
186186 Actions: map[string]entity.ActionBinding{
187- "zzz-later": {Keys: []string{"<c-d>"}},
188- "aaa-first": {Keys: []string{"<C-d>"}},
189- "page-scroll-down": {Keys: []string{"j"}},
187+ "zzz-later": {Keys: []string{"<c-d>"}},
188+ "aaa-first": {Keys: []string{"<C-d>"}},
189+ "vim-scroll-down": {Keys: []string{"j"}},
190190 },
191191 }
192192
@@ -215,14 +215,14 @@ func vimModeSequencesOwnershipFixture(t *testing.T) *ShortcutSet {
215215 VimMode: entity.VimModeConfig{
216216 ActivationShortcut: "ctrl+y",
217217 Actions: map[string]entity.ActionBinding{
218- "page-scroll-down": {Keys: []string{"j"}},
219- "page-scroll-down-fast": {Keys: []string{"shift+j"}},
220- "confirm": {Keys: []string{"enter"}},
221- "cancel": {Keys: []string{"escape"}},
222- "heading-next": {Keys: []string{"]]"}},
223- "yank-section": {Keys: []string{"yah"}},
224- "outline": {Keys: []string{"gO"}},
225- "half-page-down": {Keys: []string{"<C-d>"}},
218+ "vim-scroll-down": {Keys: []string{"j"}},
219+ "vim-scroll-down-fast": {Keys: []string{"shift+j"}},
220+ "confirm": {Keys: []string{"enter"}},
221+ "cancel": {Keys: []string{"escape"}},
222+ "heading-next": {Keys: []string{"]]"}},
223+ "yank-section": {Keys: []string{"yah"}},
224+ "outline": {Keys: []string{"gO"}},
225+ "half-page-down": {Keys: []string{"<C-d>"}},
226226 },
227227 },
228228 }
@@ -298,9 +298,9 @@ func TestShortcutSet_VimModeSequencesOwnership(t *testing.T) {
298298 func TestFeedVimModeSequence_CompleteDoubleBracket(t *testing.T) {
299299 ctx := context.Background()
300300 ws := vimModeSequenceWorkspace(map[string]entity.ActionBinding{
301- "heading-next": {Keys: []string{"]]"}},
302- "page-scroll-down": {Keys: []string{"j"}},
303- "cancel": {Keys: []string{"escape"}},
301+ "heading-next": {Keys: []string{"]]"}},
302+ "vim-scroll-down": {Keys: []string{"j"}},
303+ "cancel": {Keys: []string{"escape"}},
304304 })
305305 h := NewKeyboardHandler(ctx, ws, newTestSession())
306306 pending := capturePending(h)
@@ -334,8 +334,8 @@ func TestFeedVimModeSequence_CompleteDoubleBracket(t *testing.T) {
334334 func TestFeedVimModeSequence_PendingYankNotifications(t *testing.T) {
335335 ctx := context.Background()
336336 ws := vimModeSequenceWorkspace(map[string]entity.ActionBinding{
337- "yank-section": {Keys: []string{"yah"}},
338- "page-scroll-down": {Keys: []string{"j"}},
337+ "yank-section": {Keys: []string{"yah"}},
338+ "vim-scroll-down": {Keys: []string{"j"}},
339339 })
340340 h := NewKeyboardHandler(ctx, ws, newTestSession())
341341 pending := capturePending(h)
@@ -368,8 +368,8 @@ func TestFeedVimModeSequence_PendingYankNotifications(t *testing.T) {
368368 func TestFeedVimModeSequence_UnknownFallsThrough(t *testing.T) {
369369 ctx := context.Background()
370370 ws := vimModeSequenceWorkspace(map[string]entity.ActionBinding{
371- "heading-next": {Keys: []string{"]]"}},
372- "page-scroll-down": {Keys: []string{"j"}},
371+ "heading-next": {Keys: []string{"]]"}},
372+ "vim-scroll-down": {Keys: []string{"j"}},
373373 })
374374 h := NewKeyboardHandler(ctx, ws, newTestSession())
375375 var scrollCalls int
@@ -649,8 +649,8 @@ func TestFeedVimModeSequence_StaleQueuedTimeoutNoOp(t *testing.T) {
649649 func TestFeedVimModeSequence_ReloadReplacesMatcher(t *testing.T) {
650650 ctx := context.Background()
651651 wsA := vimModeSequenceWorkspace(map[string]entity.ActionBinding{
652- "action-a": {Keys: []string{"yah"}},
653- "page-scroll-down": {Keys: []string{"j"}},
652+ "action-a": {Keys: []string{"yah"}},
653+ "vim-scroll-down": {Keys: []string{"j"}},
654654 })
655655 h := NewKeyboardHandler(ctx, wsA, newTestSession())
656656 actions := captureSequenceActions(h)
@@ -664,8 +664,8 @@ func TestFeedVimModeSequence_ReloadReplacesMatcher(t *testing.T) {
664664 }
665665
666666 wsB := vimModeSequenceWorkspace(map[string]entity.ActionBinding{
667- "action-b": {Keys: []string{"gO"}},
668- "page-scroll-down": {Keys: []string{"j"}},
667+ "action-b": {Keys: []string{"gO"}},
668+ "vim-scroll-down": {Keys: []string{"j"}},
669669 })
670670 h.ReloadShortcuts(ctx, wsB, newTestSession())
671671 if h.PendingSequence() != "" {
@@ -137,13 +137,13 @@ func TestTrieOwnsBinding_CrossContractAtoms(t *testing.T) {
137 func TestBuildVimModeTrie(t *testing.T) {137 func TestBuildVimModeTrie(t *testing.T) {
138 cfg := &entity.VimModeConfig{138 cfg := &entity.VimModeConfig{
139 Actions: map[string]entity.ActionBinding{139 Actions: map[string]entity.ActionBinding{
140- "page-scroll-down": {Keys: []string{"j"}},140+ "vim-scroll-down": {Keys: []string{"j"}},
141- "heading-next": {Keys: []string{"]]"}},141+ "heading-next": {Keys: []string{"]]"}},
142- "yank-section": {Keys: []string{"yah"}},142+ "yank-section": {Keys: []string{"yah"}},
143- "outline": {Keys: []string{"gO"}},143+ "outline": {Keys: []string{"gO"}},
144- "half-page-down": {Keys: []string{"<C-d>"}},144+ "half-page-down": {Keys: []string{"<C-d>"}},
145- "confirm": {Keys: []string{"enter"}},145+ "confirm": {Keys: []string{"enter"}},
146- "cancel": {Keys: []string{"escape"}},146+ "cancel": {Keys: []string{"escape"}},
147 },147 },
148 }148 }
149 149
@@ -184,9 +184,9 @@ func TestBuildVimModeTrie_ConflictKeepsFirstSortedWinner(t *testing.T) {
184 // Losing raw key must not be marked owned (insert failed).184 // Losing raw key must not be marked owned (insert failed).
185 cfg := &entity.VimModeConfig{185 cfg := &entity.VimModeConfig{
186 Actions: map[string]entity.ActionBinding{186 Actions: map[string]entity.ActionBinding{
187- "zzz-later": {Keys: []string{"<c-d>"}},187+ "zzz-later": {Keys: []string{"<c-d>"}},
188- "aaa-first": {Keys: []string{"<C-d>"}},188+ "aaa-first": {Keys: []string{"<C-d>"}},
189- "page-scroll-down": {Keys: []string{"j"}},189+ "vim-scroll-down": {Keys: []string{"j"}},
190 },190 },
191 }191 }
192 192
@@ -215,14 +215,14 @@ func vimModeSequencesOwnershipFixture(t *testing.T) *ShortcutSet {
215 VimMode: entity.VimModeConfig{215 VimMode: entity.VimModeConfig{
216 ActivationShortcut: "ctrl+y",216 ActivationShortcut: "ctrl+y",
217 Actions: map[string]entity.ActionBinding{217 Actions: map[string]entity.ActionBinding{
218- "page-scroll-down": {Keys: []string{"j"}},218+ "vim-scroll-down": {Keys: []string{"j"}},
219- "page-scroll-down-fast": {Keys: []string{"shift+j"}},219+ "vim-scroll-down-fast": {Keys: []string{"shift+j"}},
220- "confirm": {Keys: []string{"enter"}},220+ "confirm": {Keys: []string{"enter"}},
221- "cancel": {Keys: []string{"escape"}},221+ "cancel": {Keys: []string{"escape"}},
222- "heading-next": {Keys: []string{"]]"}},222+ "heading-next": {Keys: []string{"]]"}},
223- "yank-section": {Keys: []string{"yah"}},223+ "yank-section": {Keys: []string{"yah"}},
224- "outline": {Keys: []string{"gO"}},224+ "outline": {Keys: []string{"gO"}},
225- "half-page-down": {Keys: []string{"<C-d>"}},225+ "half-page-down": {Keys: []string{"<C-d>"}},
226 },226 },
227 },227 },
228 }228 }
@@ -298,9 +298,9 @@ func TestShortcutSet_VimModeSequencesOwnership(t *testing.T) {
298 func TestFeedVimModeSequence_CompleteDoubleBracket(t *testing.T) {298 func TestFeedVimModeSequence_CompleteDoubleBracket(t *testing.T) {
299 ctx := context.Background()299 ctx := context.Background()
300 ws := vimModeSequenceWorkspace(map[string]entity.ActionBinding{300 ws := vimModeSequenceWorkspace(map[string]entity.ActionBinding{
301- "heading-next": {Keys: []string{"]]"}},301+ "heading-next": {Keys: []string{"]]"}},
302- "page-scroll-down": {Keys: []string{"j"}},302+ "vim-scroll-down": {Keys: []string{"j"}},
303- "cancel": {Keys: []string{"escape"}},303+ "cancel": {Keys: []string{"escape"}},
304 })304 })
305 h := NewKeyboardHandler(ctx, ws, newTestSession())305 h := NewKeyboardHandler(ctx, ws, newTestSession())
306 pending := capturePending(h)306 pending := capturePending(h)
@@ -334,8 +334,8 @@ func TestFeedVimModeSequence_CompleteDoubleBracket(t *testing.T) {
334 func TestFeedVimModeSequence_PendingYankNotifications(t *testing.T) {334 func TestFeedVimModeSequence_PendingYankNotifications(t *testing.T) {
335 ctx := context.Background()335 ctx := context.Background()
336 ws := vimModeSequenceWorkspace(map[string]entity.ActionBinding{336 ws := vimModeSequenceWorkspace(map[string]entity.ActionBinding{
337- "yank-section": {Keys: []string{"yah"}},337+ "yank-section": {Keys: []string{"yah"}},
338- "page-scroll-down": {Keys: []string{"j"}},338+ "vim-scroll-down": {Keys: []string{"j"}},
339 })339 })
340 h := NewKeyboardHandler(ctx, ws, newTestSession())340 h := NewKeyboardHandler(ctx, ws, newTestSession())
341 pending := capturePending(h)341 pending := capturePending(h)
@@ -368,8 +368,8 @@ func TestFeedVimModeSequence_PendingYankNotifications(t *testing.T) {
368 func TestFeedVimModeSequence_UnknownFallsThrough(t *testing.T) {368 func TestFeedVimModeSequence_UnknownFallsThrough(t *testing.T) {
369 ctx := context.Background()369 ctx := context.Background()
370 ws := vimModeSequenceWorkspace(map[string]entity.ActionBinding{370 ws := vimModeSequenceWorkspace(map[string]entity.ActionBinding{
371- "heading-next": {Keys: []string{"]]"}},371+ "heading-next": {Keys: []string{"]]"}},
372- "page-scroll-down": {Keys: []string{"j"}},372+ "vim-scroll-down": {Keys: []string{"j"}},
373 })373 })
374 h := NewKeyboardHandler(ctx, ws, newTestSession())374 h := NewKeyboardHandler(ctx, ws, newTestSession())
375 var scrollCalls int375 var scrollCalls int
@@ -649,8 +649,8 @@ func TestFeedVimModeSequence_StaleQueuedTimeoutNoOp(t *testing.T) {
649 func TestFeedVimModeSequence_ReloadReplacesMatcher(t *testing.T) {649 func TestFeedVimModeSequence_ReloadReplacesMatcher(t *testing.T) {
650 ctx := context.Background()650 ctx := context.Background()
651 wsA := vimModeSequenceWorkspace(map[string]entity.ActionBinding{651 wsA := vimModeSequenceWorkspace(map[string]entity.ActionBinding{
652- "action-a": {Keys: []string{"yah"}},652+ "action-a": {Keys: []string{"yah"}},
653- "page-scroll-down": {Keys: []string{"j"}},653+ "vim-scroll-down": {Keys: []string{"j"}},
654 })654 })
655 h := NewKeyboardHandler(ctx, wsA, newTestSession())655 h := NewKeyboardHandler(ctx, wsA, newTestSession())
656 actions := captureSequenceActions(h)656 actions := captureSequenceActions(h)
@@ -664,8 +664,8 @@ func TestFeedVimModeSequence_ReloadReplacesMatcher(t *testing.T) {
664 }664 }
665 665
666 wsB := vimModeSequenceWorkspace(map[string]entity.ActionBinding{666 wsB := vimModeSequenceWorkspace(map[string]entity.ActionBinding{
667- "action-b": {Keys: []string{"gO"}},667+ "action-b": {Keys: []string{"gO"}},
668- "page-scroll-down": {Keys: []string{"j"}},668+ "vim-scroll-down": {Keys: []string{"j"}},
669 })669 })
670 h.ReloadShortcuts(ctx, wsB, newTestSession())670 h.ReloadShortcuts(ctx, wsB, newTestSession())
671 if h.PendingSequence() != "" {671 if h.PendingSequence() != "" {

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