detached c1deb9b

refactor(vim): move navigation request into dto

commit
c1deb9bae4c1def1c5faca42d7088f1ab03eb2fe
author
brice · · 2026-08-14 14:52 UTC
parent
4bafd5c
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
7 files · +72 -62
A internal/application/dto/semantic_navigation.go +27 -0
@@ -0,0 +1,27 @@
1+package dto
2+
3+// SemanticNavigationTarget identifies a visible document target that Vim Mode
4+// can navigate without coupling the application layer to a browser engine.
5+type SemanticNavigationTarget int
6+
7+const (
8+ SemanticNavigationTargetHeading SemanticNavigationTarget = iota
9+)
10+
11+// SemanticNavigationDirection identifies the strict document-order direction
12+// for a semantic navigation request.
13+type SemanticNavigationDirection int
14+
15+const (
16+ SemanticNavigationBackward SemanticNavigationDirection = -1
17+ SemanticNavigationForward SemanticNavigationDirection = 1
18+)
19+
20+// SemanticNavigationRequest describes one Vim structural movement. Count uses
21+// Vim semantics: adapters treat values less than one as one.
22+type SemanticNavigationRequest struct {
23+ Target SemanticNavigationTarget
24+ Direction SemanticNavigationDirection
25+ Count int
26+ HighlightColor string
27+}
@@ -0,0 +1,27 @@
1+package dto
2+
3+// SemanticNavigationTarget identifies a visible document target that Vim Mode
4+// can navigate without coupling the application layer to a browser engine.
5+type SemanticNavigationTarget int
6+
7+const (
8+ SemanticNavigationTargetHeading SemanticNavigationTarget = iota
9+)
10+
11+// SemanticNavigationDirection identifies the strict document-order direction
12+// for a semantic navigation request.
13+type SemanticNavigationDirection int
14+
15+const (
16+ SemanticNavigationBackward SemanticNavigationDirection = -1
17+ SemanticNavigationForward SemanticNavigationDirection = 1
18+)
19+
20+// SemanticNavigationRequest describes one Vim structural movement. Count uses
21+// Vim semantics: adapters treat values less than one as one.
22+type SemanticNavigationRequest struct {
23+ Target SemanticNavigationTarget
24+ Direction SemanticNavigationDirection
25+ Count int
26+ HighlightColor string
27+}
M internal/application/port/webview.go +1 -27
@@ -445,36 +445,10 @@ type AccessibilityEnabler interface {
445445 EnableAccessibility()
446446 }
447447
448-// SemanticNavigationTarget identifies a visible document target that Vim Mode
449-// can navigate without coupling the application layer to a browser engine.
450-type SemanticNavigationTarget int
451-
452-const (
453- SemanticNavigationTargetHeading SemanticNavigationTarget = iota
454-)
455-
456-// SemanticNavigationDirection identifies the strict document-order direction
457-// for an optional semantic navigation request.
458-type SemanticNavigationDirection int
459-
460-const (
461- SemanticNavigationBackward SemanticNavigationDirection = -1
462- SemanticNavigationForward SemanticNavigationDirection = 1
463-)
464-
465-// SemanticNavigationRequest describes one Vim structural movement. Count uses
466-// Vim semantics: adapters treat values less than one as one.
467-type SemanticNavigationRequest struct {
468- Target SemanticNavigationTarget
469- Direction SemanticNavigationDirection
470- Count int
471- HighlightColor string
472-}
473-
474448 // SemanticNavigable is an optional WebView capability for visible structural
475449 // navigation. Unsupported engines safely leave it unimplemented.
476450 type SemanticNavigable interface {
477- NavigateSemantic(ctx context.Context, request SemanticNavigationRequest) error
451+ NavigateSemantic(ctx context.Context, request dto.SemanticNavigationRequest) error
478452 }
479453
480454 // PageInputFocuser is an optional WebView capability for moving Vim Mode
@@ -445,36 +445,10 @@ type AccessibilityEnabler interface {
445 EnableAccessibility()445 EnableAccessibility()
446 }446 }
447 447
448-// SemanticNavigationTarget identifies a visible document target that Vim Mode
449-// can navigate without coupling the application layer to a browser engine.
450-type SemanticNavigationTarget int
451-
452-const (
453- SemanticNavigationTargetHeading SemanticNavigationTarget = iota
454-)
455-
456-// SemanticNavigationDirection identifies the strict document-order direction
457-// for an optional semantic navigation request.
458-type SemanticNavigationDirection int
459-
460-const (
461- SemanticNavigationBackward SemanticNavigationDirection = -1
462- SemanticNavigationForward SemanticNavigationDirection = 1
463-)
464-
465-// SemanticNavigationRequest describes one Vim structural movement. Count uses
466-// Vim semantics: adapters treat values less than one as one.
467-type SemanticNavigationRequest struct {
468- Target SemanticNavigationTarget
469- Direction SemanticNavigationDirection
470- Count int
471- HighlightColor string
472-}
473-
474 // SemanticNavigable is an optional WebView capability for visible structural448 // SemanticNavigable is an optional WebView capability for visible structural
475 // navigation. Unsupported engines safely leave it unimplemented.449 // navigation. Unsupported engines safely leave it unimplemented.
476 type SemanticNavigable interface {450 type SemanticNavigable interface {
477- NavigateSemantic(ctx context.Context, request SemanticNavigationRequest) error451+ NavigateSemantic(ctx context.Context, request dto.SemanticNavigationRequest) error
478 }452 }
479 453
480 // PageInputFocuser is an optional WebView capability for moving Vim Mode454 // PageInputFocuser is an optional WebView capability for moving Vim Mode
M internal/application/usecase/vim_navigation.go +8 -7
@@ -4,6 +4,7 @@ import (
44 "context"
55 "errors"
66
7+ "github.com/bnema/dumber/internal/application/dto"
78 "github.com/bnema/dumber/internal/application/port"
89 )
910
@@ -34,19 +35,19 @@ func (*VimNavigationUseCase) Execute(ctx context.Context, wv port.WebView, actio
3435 return nil
3536 }
3637
37- var request port.SemanticNavigationRequest
38+ var request dto.SemanticNavigationRequest
3839 switch action {
3940 case "heading-next":
40- request = port.SemanticNavigationRequest{
41- Target: port.SemanticNavigationTargetHeading,
42- Direction: port.SemanticNavigationForward,
41+ request = dto.SemanticNavigationRequest{
42+ Target: dto.SemanticNavigationTargetHeading,
43+ Direction: dto.SemanticNavigationForward,
4344 Count: count,
4445 HighlightColor: highlightColor,
4546 }
4647 case "heading-prev":
47- request = port.SemanticNavigationRequest{
48- Target: port.SemanticNavigationTargetHeading,
49- Direction: port.SemanticNavigationBackward,
48+ request = dto.SemanticNavigationRequest{
49+ Target: dto.SemanticNavigationTargetHeading,
50+ Direction: dto.SemanticNavigationBackward,
5051 Count: count,
5152 HighlightColor: highlightColor,
5253 }
@@ -4,6 +4,7 @@ import (
4 "context"4 "context"
5 "errors"5 "errors"
6 6
7+ "github.com/bnema/dumber/internal/application/dto"
7 "github.com/bnema/dumber/internal/application/port"8 "github.com/bnema/dumber/internal/application/port"
8 )9 )
9 10
@@ -34,19 +35,19 @@ func (*VimNavigationUseCase) Execute(ctx context.Context, wv port.WebView, actio
34 return nil35 return nil
35 }36 }
36 37
37- var request port.SemanticNavigationRequest38+ var request dto.SemanticNavigationRequest
38 switch action {39 switch action {
39 case "heading-next":40 case "heading-next":
40- request = port.SemanticNavigationRequest{41+ request = dto.SemanticNavigationRequest{
41- Target: port.SemanticNavigationTargetHeading,42+ Target: dto.SemanticNavigationTargetHeading,
42- Direction: port.SemanticNavigationForward,43+ Direction: dto.SemanticNavigationForward,
43 Count: count,44 Count: count,
44 HighlightColor: highlightColor,45 HighlightColor: highlightColor,
45 }46 }
46 case "heading-prev":47 case "heading-prev":
47- request = port.SemanticNavigationRequest{48+ request = dto.SemanticNavigationRequest{
48- Target: port.SemanticNavigationTargetHeading,49+ Target: dto.SemanticNavigationTargetHeading,
49- Direction: port.SemanticNavigationBackward,50+ Direction: dto.SemanticNavigationBackward,
50 Count: count,51 Count: count,
51 HighlightColor: highlightColor,52 HighlightColor: highlightColor,
52 }53 }
M internal/application/usecase/vim_navigation_test.go +6 -6
@@ -4,7 +4,7 @@ import (
44 "context"
55 "testing"
66
7- "github.com/bnema/dumber/internal/application/port"
7+ "github.com/bnema/dumber/internal/application/dto"
88 portmocks "github.com/bnema/dumber/internal/application/port/mocks"
99 "github.com/stretchr/testify/assert"
1010 "github.com/stretchr/testify/require"
@@ -14,7 +14,7 @@ type vimNavigationWebView struct {
1414 *portmocks.MockWebView
1515 focusNextInputCalls int
1616 pageFocusBackward []bool
17- semanticRequests []port.SemanticNavigationRequest
17+ semanticRequests []dto.SemanticNavigationRequest
1818 }
1919
2020 func (wv *vimNavigationWebView) FocusNextInput() {
@@ -25,7 +25,7 @@ func (wv *vimNavigationWebView) NavigatePageFocus(backward bool) {
2525 wv.pageFocusBackward = append(wv.pageFocusBackward, backward)
2626 }
2727
28-func (wv *vimNavigationWebView) NavigateSemantic(_ context.Context, request port.SemanticNavigationRequest) error {
28+func (wv *vimNavigationWebView) NavigateSemantic(_ context.Context, request dto.SemanticNavigationRequest) error {
2929 wv.semanticRequests = append(wv.semanticRequests, request)
3030 return nil
3131 }
@@ -39,9 +39,9 @@ func TestVimNavigationUseCaseExecuteDispatchesConfiguredActions(t *testing.T) {
3939
4040 assert.Equal(t, 1, wv.focusNextInputCalls)
4141 require.Len(t, wv.semanticRequests, 1)
42- assert.Equal(t, port.SemanticNavigationRequest{
43- Target: port.SemanticNavigationTargetHeading,
44- Direction: port.SemanticNavigationBackward,
42+ assert.Equal(t, dto.SemanticNavigationRequest{
43+ Target: dto.SemanticNavigationTargetHeading,
44+ Direction: dto.SemanticNavigationBackward,
4545 Count: 3,
4646 HighlightColor: "#aabbcc",
4747 }, wv.semanticRequests[0])
@@ -4,7 +4,7 @@ import (
4 "context"4 "context"
5 "testing"5 "testing"
6 6
7- "github.com/bnema/dumber/internal/application/port"7+ "github.com/bnema/dumber/internal/application/dto"
8 portmocks "github.com/bnema/dumber/internal/application/port/mocks"8 portmocks "github.com/bnema/dumber/internal/application/port/mocks"
9 "github.com/stretchr/testify/assert"9 "github.com/stretchr/testify/assert"
10 "github.com/stretchr/testify/require"10 "github.com/stretchr/testify/require"
@@ -14,7 +14,7 @@ type vimNavigationWebView struct {
14 *portmocks.MockWebView14 *portmocks.MockWebView
15 focusNextInputCalls int15 focusNextInputCalls int
16 pageFocusBackward []bool16 pageFocusBackward []bool
17- semanticRequests []port.SemanticNavigationRequest17+ semanticRequests []dto.SemanticNavigationRequest
18 }18 }
19 19
20 func (wv *vimNavigationWebView) FocusNextInput() {20 func (wv *vimNavigationWebView) FocusNextInput() {
@@ -25,7 +25,7 @@ func (wv *vimNavigationWebView) NavigatePageFocus(backward bool) {
25 wv.pageFocusBackward = append(wv.pageFocusBackward, backward)25 wv.pageFocusBackward = append(wv.pageFocusBackward, backward)
26 }26 }
27 27
28-func (wv *vimNavigationWebView) NavigateSemantic(_ context.Context, request port.SemanticNavigationRequest) error {28+func (wv *vimNavigationWebView) NavigateSemantic(_ context.Context, request dto.SemanticNavigationRequest) error {
29 wv.semanticRequests = append(wv.semanticRequests, request)29 wv.semanticRequests = append(wv.semanticRequests, request)
30 return nil30 return nil
31 }31 }
@@ -39,9 +39,9 @@ func TestVimNavigationUseCaseExecuteDispatchesConfiguredActions(t *testing.T) {
39 39
40 assert.Equal(t, 1, wv.focusNextInputCalls)40 assert.Equal(t, 1, wv.focusNextInputCalls)
41 require.Len(t, wv.semanticRequests, 1)41 require.Len(t, wv.semanticRequests, 1)
42- assert.Equal(t, port.SemanticNavigationRequest{42+ assert.Equal(t, dto.SemanticNavigationRequest{
43- Target: port.SemanticNavigationTargetHeading,43+ Target: dto.SemanticNavigationTargetHeading,
44- Direction: port.SemanticNavigationBackward,44+ Direction: dto.SemanticNavigationBackward,
45 Count: 3,45 Count: 3,
46 HighlightColor: "#aabbcc",46 HighlightColor: "#aabbcc",
47 }, wv.semanticRequests[0])47 }, wv.semanticRequests[0])
M internal/infrastructure/cef/semantic_navigation.go +10 -5
@@ -5,20 +5,20 @@ import (
55 "fmt"
66 "strconv"
77
8- "github.com/bnema/dumber/internal/application/port"
8+ "github.com/bnema/dumber/internal/application/dto"
99 )
1010
1111 // NavigateSemantic performs the initial live Vim structural navigation path.
1212 // It deliberately uses only stable HTML semantics and a static script: CEF's
1313 // raw accessibility payload is not yet a documented application contract.
14-func (wv *WebView) NavigateSemantic(ctx context.Context, request port.SemanticNavigationRequest) error {
14+func (wv *WebView) NavigateSemantic(ctx context.Context, request dto.SemanticNavigationRequest) error {
1515 if wv == nil || wv.destroyed.Load() {
1616 return errDestroyed
1717 }
18- if request.Target != port.SemanticNavigationTargetHeading {
18+ if request.Target != dto.SemanticNavigationTargetHeading {
1919 return fmt.Errorf("unsupported semantic navigation target: %d", request.Target)
2020 }
21- if request.Direction != port.SemanticNavigationForward && request.Direction != port.SemanticNavigationBackward {
21+ if request.Direction != dto.SemanticNavigationForward && request.Direction != dto.SemanticNavigationBackward {
2222 return fmt.Errorf("unsupported semantic navigation direction: %d", request.Direction)
2323 }
2424
@@ -39,7 +39,12 @@ func headingNavigationScript(direction, count int, highlightColor string) string
3939 const className = "dumber-vim-heading-target";
4040 const highlightColor = ` + strconv.Quote(highlightColor) + `;
4141 const headings = Array.from(document.querySelectorAll("h1,h2,h3,h4,h5,h6"))
42- .filter((heading) => heading.getClientRects().length > 0);
42+ .filter((heading) => {
43+ if (heading.getClientRects().length === 0) return false;
44+ if (heading.closest("[hidden],[inert],[aria-hidden=\"true\"]")) return false;
45+ const style = window.getComputedStyle(heading);
46+ return style.visibility !== "hidden" && style.visibility !== "collapse";
47+ });
4348 if (headings.length === 0) return;
4449
4550 const prior = window[stateKey];
@@ -5,20 +5,20 @@ import (
5 "fmt"5 "fmt"
6 "strconv"6 "strconv"
7 7
8- "github.com/bnema/dumber/internal/application/port"8+ "github.com/bnema/dumber/internal/application/dto"
9 )9 )
10 10
11 // NavigateSemantic performs the initial live Vim structural navigation path.11 // NavigateSemantic performs the initial live Vim structural navigation path.
12 // It deliberately uses only stable HTML semantics and a static script: CEF's12 // It deliberately uses only stable HTML semantics and a static script: CEF's
13 // raw accessibility payload is not yet a documented application contract.13 // raw accessibility payload is not yet a documented application contract.
14-func (wv *WebView) NavigateSemantic(ctx context.Context, request port.SemanticNavigationRequest) error {14+func (wv *WebView) NavigateSemantic(ctx context.Context, request dto.SemanticNavigationRequest) error {
15 if wv == nil || wv.destroyed.Load() {15 if wv == nil || wv.destroyed.Load() {
16 return errDestroyed16 return errDestroyed
17 }17 }
18- if request.Target != port.SemanticNavigationTargetHeading {18+ if request.Target != dto.SemanticNavigationTargetHeading {
19 return fmt.Errorf("unsupported semantic navigation target: %d", request.Target)19 return fmt.Errorf("unsupported semantic navigation target: %d", request.Target)
20 }20 }
21- if request.Direction != port.SemanticNavigationForward && request.Direction != port.SemanticNavigationBackward {21+ if request.Direction != dto.SemanticNavigationForward && request.Direction != dto.SemanticNavigationBackward {
22 return fmt.Errorf("unsupported semantic navigation direction: %d", request.Direction)22 return fmt.Errorf("unsupported semantic navigation direction: %d", request.Direction)
23 }23 }
24 24
@@ -39,7 +39,12 @@ func headingNavigationScript(direction, count int, highlightColor string) string
39 const className = "dumber-vim-heading-target";39 const className = "dumber-vim-heading-target";
40 const highlightColor = ` + strconv.Quote(highlightColor) + `;40 const highlightColor = ` + strconv.Quote(highlightColor) + `;
41 const headings = Array.from(document.querySelectorAll("h1,h2,h3,h4,h5,h6"))41 const headings = Array.from(document.querySelectorAll("h1,h2,h3,h4,h5,h6"))
42- .filter((heading) => heading.getClientRects().length > 0);42+ .filter((heading) => {
43+ if (heading.getClientRects().length === 0) return false;
44+ if (heading.closest("[hidden],[inert],[aria-hidden=\"true\"]")) return false;
45+ const style = window.getComputedStyle(heading);
46+ return style.visibility !== "hidden" && style.visibility !== "collapse";
47+ });
43 if (headings.length === 0) return;48 if (headings.length === 0) return;
44 49
45 const prior = window[stateKey];50 const prior = window[stateKey];
M internal/infrastructure/cef/semantic_navigation_test.go +14 -12
@@ -9,7 +9,7 @@ import (
99 "github.com/stretchr/testify/mock"
1010 "github.com/stretchr/testify/require"
1111
12- "github.com/bnema/dumber/internal/application/port"
12+ "github.com/bnema/dumber/internal/application/dto"
1313 )
1414
1515 func TestWebViewNavigateSemantic_HeadingExecutesStrictNavigationScript(t *testing.T) {
@@ -18,6 +18,8 @@ func TestWebViewNavigateSemantic_HeadingExecutesStrictNavigationScript(t *testin
1818 browser.EXPECT().GetMainFrame().Return(frame).Once()
1919 frame.EXPECT().ExecuteJavaScript(mock.MatchedBy(mockStringContaining(t,
2020 "document.querySelectorAll(\"h1,h2,h3,h4,h5,h6\")",
21+ "heading.closest(\"[hidden],[inert],[aria-hidden=\\\"true\\\"]\")",
22+ "window.getComputedStyle(heading)",
2123 "firstAtOrAfterViewportTop",
2224 "if (next < 0 || next >= headings.length) return;",
2325 "previous && previous !== target",
@@ -30,9 +32,9 @@ func TestWebViewNavigateSemantic_HeadingExecutesStrictNavigationScript(t *testin
3032 )), "", int32(0)).Once()
3133
3234 wv := &WebView{browser: browser}
33- err := wv.NavigateSemantic(context.Background(), port.SemanticNavigationRequest{
34- Target: port.SemanticNavigationTargetHeading,
35- Direction: port.SemanticNavigationForward,
35+ err := wv.NavigateSemantic(context.Background(), dto.SemanticNavigationRequest{
36+ Target: dto.SemanticNavigationTargetHeading,
37+ Direction: dto.SemanticNavigationForward,
3638 Count: 3,
3739 HighlightColor: "#22c55e",
3840 })
@@ -50,21 +52,21 @@ func TestWebViewNavigateSemantic_NormalizesCountAndRejectsUnsupportedRequests(t
5052 assert.Contains(t, backward, "const highlightColor = \"#4ade80\"")
5153
5254 wv := &WebView{}
53- err := wv.NavigateSemantic(context.Background(), port.SemanticNavigationRequest{
54- Target: port.SemanticNavigationTargetHeading,
55- Direction: port.SemanticNavigationForward,
55+ err := wv.NavigateSemantic(context.Background(), dto.SemanticNavigationRequest{
56+ Target: dto.SemanticNavigationTargetHeading,
57+ Direction: dto.SemanticNavigationForward,
5658 Count: 0,
5759 })
5860 require.NoError(t, err)
5961
60- err = wv.NavigateSemantic(context.Background(), port.SemanticNavigationRequest{
61- Target: port.SemanticNavigationTargetHeading + 1,
62- Direction: port.SemanticNavigationForward,
62+ err = wv.NavigateSemantic(context.Background(), dto.SemanticNavigationRequest{
63+ Target: dto.SemanticNavigationTargetHeading + 1,
64+ Direction: dto.SemanticNavigationForward,
6365 })
6466 require.Error(t, err)
6567
66- err = wv.NavigateSemantic(context.Background(), port.SemanticNavigationRequest{
67- Target: port.SemanticNavigationTargetHeading,
68+ err = wv.NavigateSemantic(context.Background(), dto.SemanticNavigationRequest{
69+ Target: dto.SemanticNavigationTargetHeading,
6870 Direction: 0,
6971 })
7072 require.Error(t, err)
@@ -9,7 +9,7 @@ import (
9 "github.com/stretchr/testify/mock"9 "github.com/stretchr/testify/mock"
10 "github.com/stretchr/testify/require"10 "github.com/stretchr/testify/require"
11 11
12- "github.com/bnema/dumber/internal/application/port"12+ "github.com/bnema/dumber/internal/application/dto"
13 )13 )
14 14
15 func TestWebViewNavigateSemantic_HeadingExecutesStrictNavigationScript(t *testing.T) {15 func TestWebViewNavigateSemantic_HeadingExecutesStrictNavigationScript(t *testing.T) {
@@ -18,6 +18,8 @@ func TestWebViewNavigateSemantic_HeadingExecutesStrictNavigationScript(t *testin
18 browser.EXPECT().GetMainFrame().Return(frame).Once()18 browser.EXPECT().GetMainFrame().Return(frame).Once()
19 frame.EXPECT().ExecuteJavaScript(mock.MatchedBy(mockStringContaining(t,19 frame.EXPECT().ExecuteJavaScript(mock.MatchedBy(mockStringContaining(t,
20 "document.querySelectorAll(\"h1,h2,h3,h4,h5,h6\")",20 "document.querySelectorAll(\"h1,h2,h3,h4,h5,h6\")",
21+ "heading.closest(\"[hidden],[inert],[aria-hidden=\\\"true\\\"]\")",
22+ "window.getComputedStyle(heading)",
21 "firstAtOrAfterViewportTop",23 "firstAtOrAfterViewportTop",
22 "if (next < 0 || next >= headings.length) return;",24 "if (next < 0 || next >= headings.length) return;",
23 "previous && previous !== target",25 "previous && previous !== target",
@@ -30,9 +32,9 @@ func TestWebViewNavigateSemantic_HeadingExecutesStrictNavigationScript(t *testin
30 )), "", int32(0)).Once()32 )), "", int32(0)).Once()
31 33
32 wv := &WebView{browser: browser}34 wv := &WebView{browser: browser}
33- err := wv.NavigateSemantic(context.Background(), port.SemanticNavigationRequest{35+ err := wv.NavigateSemantic(context.Background(), dto.SemanticNavigationRequest{
34- Target: port.SemanticNavigationTargetHeading,36+ Target: dto.SemanticNavigationTargetHeading,
35- Direction: port.SemanticNavigationForward,37+ Direction: dto.SemanticNavigationForward,
36 Count: 3,38 Count: 3,
37 HighlightColor: "#22c55e",39 HighlightColor: "#22c55e",
38 })40 })
@@ -50,21 +52,21 @@ func TestWebViewNavigateSemantic_NormalizesCountAndRejectsUnsupportedRequests(t
50 assert.Contains(t, backward, "const highlightColor = \"#4ade80\"")52 assert.Contains(t, backward, "const highlightColor = \"#4ade80\"")
51 53
52 wv := &WebView{}54 wv := &WebView{}
53- err := wv.NavigateSemantic(context.Background(), port.SemanticNavigationRequest{55+ err := wv.NavigateSemantic(context.Background(), dto.SemanticNavigationRequest{
54- Target: port.SemanticNavigationTargetHeading,56+ Target: dto.SemanticNavigationTargetHeading,
55- Direction: port.SemanticNavigationForward,57+ Direction: dto.SemanticNavigationForward,
56 Count: 0,58 Count: 0,
57 })59 })
58 require.NoError(t, err)60 require.NoError(t, err)
59 61
60- err = wv.NavigateSemantic(context.Background(), port.SemanticNavigationRequest{62+ err = wv.NavigateSemantic(context.Background(), dto.SemanticNavigationRequest{
61- Target: port.SemanticNavigationTargetHeading + 1,63+ Target: dto.SemanticNavigationTargetHeading + 1,
62- Direction: port.SemanticNavigationForward,64+ Direction: dto.SemanticNavigationForward,
63 })65 })
64 require.Error(t, err)66 require.Error(t, err)
65 67
66- err = wv.NavigateSemantic(context.Background(), port.SemanticNavigationRequest{68+ err = wv.NavigateSemantic(context.Background(), dto.SemanticNavigationRequest{
67- Target: port.SemanticNavigationTargetHeading,69+ Target: dto.SemanticNavigationTargetHeading,
68 Direction: 0,70 Direction: 0,
69 })71 })
70 require.Error(t, err)72 require.Error(t, err)
M internal/ui/app_vim_mode_test.go +6 -5
@@ -10,6 +10,7 @@ import (
1010 "github.com/stretchr/testify/mock"
1111 "github.com/stretchr/testify/require"
1212
13+ "github.com/bnema/dumber/internal/application/dto"
1314 "github.com/bnema/dumber/internal/application/port"
1415 portmocks "github.com/bnema/dumber/internal/application/port/mocks"
1516 "github.com/bnema/dumber/internal/application/usecase"
@@ -175,7 +176,7 @@ func (wv *accessibilityEnablingWebView) EnableAccessibility() {
175176
176177 type semanticNavigatingWebView struct {
177178 *portmocks.MockWebView
178- requests []port.SemanticNavigationRequest
179+ requests []dto.SemanticNavigationRequest
179180 }
180181
181182 func newSemanticNavigatingWebView(t *testing.T, id port.WebViewID) *semanticNavigatingWebView {
@@ -185,7 +186,7 @@ func newSemanticNavigatingWebView(t *testing.T, id port.WebViewID) *semanticNavi
185186 return &semanticNavigatingWebView{MockWebView: wv}
186187 }
187188
188-func (wv *semanticNavigatingWebView) NavigateSemantic(_ context.Context, request port.SemanticNavigationRequest) error {
189+func (wv *semanticNavigatingWebView) NavigateSemantic(_ context.Context, request dto.SemanticNavigationRequest) error {
189190 wv.requests = append(wv.requests, request)
190191 return nil
191192 }
@@ -500,9 +501,9 @@ func TestVimMode_SequenceActionNavigatesActiveWebViewHeading(t *testing.T) {
500501 app.navigateVimSequenceAction(context.Background(), bw, "heading-prev", 0)
501502 app.navigateVimSequenceAction(context.Background(), bw, "code-next", 1)
502503
503- assert.Equal(t, []port.SemanticNavigationRequest{
504- {Target: port.SemanticNavigationTargetHeading, Direction: port.SemanticNavigationForward, Count: 3},
505- {Target: port.SemanticNavigationTargetHeading, Direction: port.SemanticNavigationBackward, Count: 0},
504+ assert.Equal(t, []dto.SemanticNavigationRequest{
505+ {Target: dto.SemanticNavigationTargetHeading, Direction: dto.SemanticNavigationForward, Count: 3},
506+ {Target: dto.SemanticNavigationTargetHeading, Direction: dto.SemanticNavigationBackward, Count: 0},
506507 }, wv.requests)
507508 }
508509
@@ -10,6 +10,7 @@ import (
10 "github.com/stretchr/testify/mock"10 "github.com/stretchr/testify/mock"
11 "github.com/stretchr/testify/require"11 "github.com/stretchr/testify/require"
12 12
13+ "github.com/bnema/dumber/internal/application/dto"
13 "github.com/bnema/dumber/internal/application/port"14 "github.com/bnema/dumber/internal/application/port"
14 portmocks "github.com/bnema/dumber/internal/application/port/mocks"15 portmocks "github.com/bnema/dumber/internal/application/port/mocks"
15 "github.com/bnema/dumber/internal/application/usecase"16 "github.com/bnema/dumber/internal/application/usecase"
@@ -175,7 +176,7 @@ func (wv *accessibilityEnablingWebView) EnableAccessibility() {
175 176
176 type semanticNavigatingWebView struct {177 type semanticNavigatingWebView struct {
177 *portmocks.MockWebView178 *portmocks.MockWebView
178- requests []port.SemanticNavigationRequest179+ requests []dto.SemanticNavigationRequest
179 }180 }
180 181
181 func newSemanticNavigatingWebView(t *testing.T, id port.WebViewID) *semanticNavigatingWebView {182 func newSemanticNavigatingWebView(t *testing.T, id port.WebViewID) *semanticNavigatingWebView {
@@ -185,7 +186,7 @@ func newSemanticNavigatingWebView(t *testing.T, id port.WebViewID) *semanticNavi
185 return &semanticNavigatingWebView{MockWebView: wv}186 return &semanticNavigatingWebView{MockWebView: wv}
186 }187 }
187 188
188-func (wv *semanticNavigatingWebView) NavigateSemantic(_ context.Context, request port.SemanticNavigationRequest) error {189+func (wv *semanticNavigatingWebView) NavigateSemantic(_ context.Context, request dto.SemanticNavigationRequest) error {
189 wv.requests = append(wv.requests, request)190 wv.requests = append(wv.requests, request)
190 return nil191 return nil
191 }192 }
@@ -500,9 +501,9 @@ func TestVimMode_SequenceActionNavigatesActiveWebViewHeading(t *testing.T) {
500 app.navigateVimSequenceAction(context.Background(), bw, "heading-prev", 0)501 app.navigateVimSequenceAction(context.Background(), bw, "heading-prev", 0)
501 app.navigateVimSequenceAction(context.Background(), bw, "code-next", 1)502 app.navigateVimSequenceAction(context.Background(), bw, "code-next", 1)
502 503
503- assert.Equal(t, []port.SemanticNavigationRequest{504+ assert.Equal(t, []dto.SemanticNavigationRequest{
504- {Target: port.SemanticNavigationTargetHeading, Direction: port.SemanticNavigationForward, Count: 3},505+ {Target: dto.SemanticNavigationTargetHeading, Direction: dto.SemanticNavigationForward, Count: 3},
505- {Target: port.SemanticNavigationTargetHeading, Direction: port.SemanticNavigationBackward, Count: 0},506+ {Target: dto.SemanticNavigationTargetHeading, Direction: dto.SemanticNavigationBackward, Count: 0},
506 }, wv.requests)507 }, wv.requests)
507 }508 }
508 509

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