refactor(vim): move navigation request into dto
- commit
- c1deb9bae4c1def1c5faca42d7088f1ab03eb2fe
- author
- brice · · 2026-08-14 14:52 UTC
- parent
- 4bafd5c
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· 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 { | |||
| 445 | 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 | 448 | // SemanticNavigable is an optional WebView capability for visible structural | |
| 475 | 449 | // navigation. Unsupported engines safely leave it unimplemented. | |
| 476 | 450 | type SemanticNavigable interface { | |
| 477 | - | NavigateSemantic(ctx context.Context, request SemanticNavigationRequest) error | |
| 451 | + | NavigateSemantic(ctx context.Context, request dto.SemanticNavigationRequest) error | |
| 478 | 452 | } | |
| 479 | 453 | ||
| 480 | 454 | // 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 structural | 448 | // 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) error | 451 | + | NavigateSemantic(ctx context.Context, request dto.SemanticNavigationRequest) error |
| 478 | } | 452 | } | ||
| 479 | 453 | ||||
| 480 | // PageInputFocuser is an optional WebView capability for moving Vim Mode | 454 | // PageInputFocuser is an optional WebView capability for moving Vim Mode | ||
M internal/application/usecase/vim_navigation.go +8 -7
| @@ -4,6 +4,7 @@ import ( | |||
| 4 | 4 | "context" | |
| 5 | 5 | "errors" | |
| 6 | 6 | ||
| 7 | + | "github.com/bnema/dumber/internal/application/dto" | |
| 7 | 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 | 35 | return nil | |
| 35 | 36 | } | |
| 36 | 37 | ||
| 37 | - | var request port.SemanticNavigationRequest | |
| 38 | + | var request dto.SemanticNavigationRequest | |
| 38 | 39 | switch action { | |
| 39 | 40 | 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, | |
| 43 | 44 | Count: count, | |
| 44 | 45 | HighlightColor: highlightColor, | |
| 45 | 46 | } | |
| 46 | 47 | 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, | |
| 50 | 51 | Count: count, | |
| 51 | 52 | HighlightColor: highlightColor, | |
| 52 | 53 | } | |
| @@ -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 nil | 35 | return nil | ||
| 35 | } | 36 | } | ||
| 36 | 37 | ||||
| 37 | - | var request port.SemanticNavigationRequest | 38 | + | 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 ( | |||
| 4 | 4 | "context" | |
| 5 | 5 | "testing" | |
| 6 | 6 | ||
| 7 | - | "github.com/bnema/dumber/internal/application/port" | |
| 7 | + | "github.com/bnema/dumber/internal/application/dto" | |
| 8 | 8 | portmocks "github.com/bnema/dumber/internal/application/port/mocks" | |
| 9 | 9 | "github.com/stretchr/testify/assert" | |
| 10 | 10 | "github.com/stretchr/testify/require" | |
| @@ -14,7 +14,7 @@ type vimNavigationWebView struct { | |||
| 14 | 14 | *portmocks.MockWebView | |
| 15 | 15 | focusNextInputCalls int | |
| 16 | 16 | pageFocusBackward []bool | |
| 17 | - | semanticRequests []port.SemanticNavigationRequest | |
| 17 | + | semanticRequests []dto.SemanticNavigationRequest | |
| 18 | 18 | } | |
| 19 | 19 | ||
| 20 | 20 | func (wv *vimNavigationWebView) FocusNextInput() { | |
| @@ -25,7 +25,7 @@ func (wv *vimNavigationWebView) NavigatePageFocus(backward bool) { | |||
| 25 | 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 | 29 | wv.semanticRequests = append(wv.semanticRequests, request) | |
| 30 | 30 | return nil | |
| 31 | 31 | } | |
| @@ -39,9 +39,9 @@ func TestVimNavigationUseCaseExecuteDispatchesConfiguredActions(t *testing.T) { | |||
| 39 | 39 | ||
| 40 | 40 | assert.Equal(t, 1, wv.focusNextInputCalls) | |
| 41 | 41 | 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, | |
| 45 | 45 | Count: 3, | |
| 46 | 46 | HighlightColor: "#aabbcc", | |
| 47 | 47 | }, 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.MockWebView | 14 | *portmocks.MockWebView | ||
| 15 | focusNextInputCalls int | 15 | focusNextInputCalls int | ||
| 16 | pageFocusBackward []bool | 16 | pageFocusBackward []bool | ||
| 17 | - | semanticRequests []port.SemanticNavigationRequest | 17 | + | 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 nil | 30 | 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 ( | |||
| 5 | 5 | "fmt" | |
| 6 | 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 | 11 | // NavigateSemantic performs the initial live Vim structural navigation path. | |
| 12 | 12 | // It deliberately uses only stable HTML semantics and a static script: CEF's | |
| 13 | 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 | 15 | if wv == nil || wv.destroyed.Load() { | |
| 16 | 16 | return errDestroyed | |
| 17 | 17 | } | |
| 18 | - | if request.Target != port.SemanticNavigationTargetHeading { | |
| 18 | + | if request.Target != dto.SemanticNavigationTargetHeading { | |
| 19 | 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 | 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 | 39 | const className = "dumber-vim-heading-target"; | |
| 40 | 40 | const highlightColor = ` + strconv.Quote(highlightColor) + `; | |
| 41 | 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 | 48 | if (headings.length === 0) return; | |
| 44 | 49 | ||
| 45 | 50 | 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's | 12 | // 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 errDestroyed | 16 | 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 ( | |||
| 9 | 9 | "github.com/stretchr/testify/mock" | |
| 10 | 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 | 15 | func TestWebViewNavigateSemantic_HeadingExecutesStrictNavigationScript(t *testing.T) { | |
| @@ -18,6 +18,8 @@ func TestWebViewNavigateSemantic_HeadingExecutesStrictNavigationScript(t *testin | |||
| 18 | 18 | browser.EXPECT().GetMainFrame().Return(frame).Once() | |
| 19 | 19 | frame.EXPECT().ExecuteJavaScript(mock.MatchedBy(mockStringContaining(t, | |
| 20 | 20 | "document.querySelectorAll(\"h1,h2,h3,h4,h5,h6\")", | |
| 21 | + | "heading.closest(\"[hidden],[inert],[aria-hidden=\\\"true\\\"]\")", | |
| 22 | + | "window.getComputedStyle(heading)", | |
| 21 | 23 | "firstAtOrAfterViewportTop", | |
| 22 | 24 | "if (next < 0 || next >= headings.length) return;", | |
| 23 | 25 | "previous && previous !== target", | |
| @@ -30,9 +32,9 @@ func TestWebViewNavigateSemantic_HeadingExecutesStrictNavigationScript(t *testin | |||
| 30 | 32 | )), "", int32(0)).Once() | |
| 31 | 33 | ||
| 32 | 34 | 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, | |
| 36 | 38 | Count: 3, | |
| 37 | 39 | HighlightColor: "#22c55e", | |
| 38 | 40 | }) | |
| @@ -50,21 +52,21 @@ func TestWebViewNavigateSemantic_NormalizesCountAndRejectsUnsupportedRequests(t | |||
| 50 | 52 | assert.Contains(t, backward, "const highlightColor = \"#4ade80\"") | |
| 51 | 53 | ||
| 52 | 54 | 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, | |
| 56 | 58 | Count: 0, | |
| 57 | 59 | }) | |
| 58 | 60 | require.NoError(t, err) | |
| 59 | 61 | ||
| 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, | |
| 63 | 65 | }) | |
| 64 | 66 | require.Error(t, err) | |
| 65 | 67 | ||
| 66 | - | err = wv.NavigateSemantic(context.Background(), port.SemanticNavigationRequest{ | |
| 67 | - | Target: port.SemanticNavigationTargetHeading, | |
| 68 | + | err = wv.NavigateSemantic(context.Background(), dto.SemanticNavigationRequest{ | |
| 69 | + | Target: dto.SemanticNavigationTargetHeading, | |
| 68 | 70 | Direction: 0, | |
| 69 | 71 | }) | |
| 70 | 72 | 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 ( | |||
| 10 | 10 | "github.com/stretchr/testify/mock" | |
| 11 | 11 | "github.com/stretchr/testify/require" | |
| 12 | 12 | ||
| 13 | + | "github.com/bnema/dumber/internal/application/dto" | |
| 13 | 14 | "github.com/bnema/dumber/internal/application/port" | |
| 14 | 15 | portmocks "github.com/bnema/dumber/internal/application/port/mocks" | |
| 15 | 16 | "github.com/bnema/dumber/internal/application/usecase" | |
| @@ -175,7 +176,7 @@ func (wv *accessibilityEnablingWebView) EnableAccessibility() { | |||
| 175 | 176 | ||
| 176 | 177 | type semanticNavigatingWebView struct { | |
| 177 | 178 | *portmocks.MockWebView | |
| 178 | - | requests []port.SemanticNavigationRequest | |
| 179 | + | requests []dto.SemanticNavigationRequest | |
| 179 | 180 | } | |
| 180 | 181 | ||
| 181 | 182 | func newSemanticNavigatingWebView(t *testing.T, id port.WebViewID) *semanticNavigatingWebView { | |
| @@ -185,7 +186,7 @@ func newSemanticNavigatingWebView(t *testing.T, id port.WebViewID) *semanticNavi | |||
| 185 | 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 | 190 | wv.requests = append(wv.requests, request) | |
| 190 | 191 | return nil | |
| 191 | 192 | } | |
| @@ -500,9 +501,9 @@ func TestVimMode_SequenceActionNavigatesActiveWebViewHeading(t *testing.T) { | |||
| 500 | 501 | app.navigateVimSequenceAction(context.Background(), bw, "heading-prev", 0) | |
| 501 | 502 | app.navigateVimSequenceAction(context.Background(), bw, "code-next", 1) | |
| 502 | 503 | ||
| 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}, | |
| 506 | 507 | }, wv.requests) | |
| 507 | 508 | } | |
| 508 | 509 | ||
| @@ -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.MockWebView | 178 | *portmocks.MockWebView | ||
| 178 | - | requests []port.SemanticNavigationRequest | 179 | + | 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 nil | 191 | 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 | ||||