Skip to content

fix: stabilize compact context picker selection#220

Merged
nathanhuh merged 2 commits into
mainfrom
bugfix/218-context-picker-selection
May 22, 2026
Merged

fix: stabilize compact context picker selection#220
nathanhuh merged 2 commits into
mainfrom
bugfix/218-context-picker-selection

Conversation

@nathanhuh
Copy link
Copy Markdown
Contributor

@nathanhuh nathanhuh commented May 21, 2026

Summary

Fix compact context picker rendering and navigation so selected rows remain visible in small terminal windows. Selection styling now uses amber foreground/background treatment for stronger contrast against blue terminal themes.

Related Issues

Closes #218
Closes #219

Validation

  • go test ./internal/app -run 'TestContextTableSelectedStyleUsesHighContrast|TestContextPickerKeepsSelectedTableRowVisibleInSmallWindow|TestContextPickerMoveUpFromBottomVisibleRowDoesNotScroll|TestContextPickerKeepsFilteredSelectedTableRowVisibleInSmallWindow' -count=1
  • make test
  • make build

Checklist

  • Scope is focused
  • Branch name follows docs/branch-naming-harness.md
  • Documentation harness reviewed (docs/documentation-harness.md)
  • README updated if user-facing behavior changed
  • Relevant docs/ pages updated if architecture, auth, config, or workflow changed
  • Tests/validation included
  • Breaking changes documented

Summary by CodeRabbit

  • New Features

    • Context picker adapts to a compact mode for very small terminals and shows context-specific help
  • Bug Fixes

    • Selected context stays visible while navigating large or filtered context lists
    • Selection remains visible across list views when terminal space is limited
    • Improved scrolling and wraparound navigation for context selection
  • Style

    • Increased contrast and bold styling for selected rows
  • Tests

    • Added compact-terminal rendering and context-picker visibility tests

Review Change Stack

- keep context table selections visible in compact terminals
- avoid premature upward scrolling from the bottom visible row
- use amber selected styling for stronger contrast against blue themes
- add regression tests for compact context picker navigation
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 21, 2026

Walkthrough

This PR keeps selected rows visible in compact terminals by adding clamped cursor-setting and navigation helpers, compact-mode sizing/rendering for the context picker, anchor-aware fit-to-height trimming that centers on the selected row, and tests validating these behaviors.

Changes

Compact Terminal Selection Visibility

Layer / File(s) Summary
Context table cursor management and navigation
internal/app/context_table.go
Selected-row style rebuilt with explicit lipgloss style; syncContextTable delegates cursor positioning to clamping helper; new clamped-set and move up/down helpers added; minimum context table height fallback lowered to 2.
Context picker sizing and compact-mode rendering
internal/app/context_table.go, internal/app/screen_context.go
Adds contextPickerTableHeight, contextPickerCompact, and contextPickerFilterVisible; viewContextPicker branches rendering and help-bar on compact mode; key handlers now call moveContextTableUp/Down.
Overflow / fit-to-height anchored to selection
internal/app/styles.go
Adds ANSI escape regex and selectedRenderedLine detection; fitToHeight gains anchor-aware path using trimLinesAroundAnchor and supporting helpers to preserve a window around the selected item with dim ellipsis and small-height handling.
Context picker small-window visibility tests
internal/app/screen_context_test.go
Adds manyTestContexts helper and three tests validating selected-row visibility when navigating and filtering in small-height windows.
Compact terminal UI rendering tests & style expectation update
internal/app/styles_test.go
Adds three compact-terminal rendering tests for service/resource/feature lists and updates the context-table selected-style expectation to a concrete high-contrast lipgloss style.

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant UI as handleContextMsg
  participant Nav as moveContextTableUp/Down
  participant Clamp as setContextTableCursorClamped
  participant Table as contextTable
  User->>UI: press up/down key
  UI->>Nav: route to moveContextTableUp/Down
  Nav->>Clamp: compute/clamp index, scroll top, step down
  Clamp->>Table: SetCursor(clampedIndex)
  Table->>UI: table cursor read -> m.ctxIdx synced
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested reviewers

  • YoungJinJung
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title uses the conventional 'fix:' prefix and clearly describes the main change: stabilizing compact context picker selection.
Description check ✅ Passed The PR description follows the template with all required sections: Summary, Related Issues, Validation, and Checklist all completed appropriately.
Linked Issues check ✅ Passed The code changes address both linked issues: #218 (selected row visibility in compact terminals via anchor-based trimming) and #219 (proper up/down navigation with cursor clamping).
Out of Scope Changes check ✅ Passed All changes are directly related to fixing compact context picker selection and styling; no unrelated modifications were introduced.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch bugfix/218-context-picker-selection

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@amazon-q-developer amazon-q-developer Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The implementation successfully addresses the compact context picker stability issues described in #218 and #219. The changes include improved contrast for selected rows using amber styling, smart viewport management to keep selections visible in small terminal windows, and refactored navigation methods that properly sync with the table model. The comprehensive test coverage validates all new behaviors including compact mode detection, selection visibility, and navigation wrapping. The code is well-structured and ready to merge.


You can now have the agent implement changes and create commits directly on your pull request's source branch. Simply comment with /q followed by your request in natural language to ask the agent to make changes.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@internal/app/context_table.go`:
- Around line 135-152: The table height calculations currently clamp to a
minimum of 2 visible lines; update them to use the required minimum of 5 visible
lines by replacing all occurrences of max(..., 2) with max(..., 5) in the
contextPickerTableHeight function and any helper used (e.g.,
contextTableHeight), and ensure the intermediate computed height adjustments
(the -6 and -2 offsets and the compact branch using max(m.height-6, ...)) still
call max(..., 5) so visibleLines := max(m.height - N, 5) is enforced; update
references to contextPickerCompact() and contextPickerFilterVisible() logic
accordingly so final returned value never drops below 5.

In `@internal/app/styles_test.go`:
- Around line 231-233: The test currently only checks presence of ">" and
selected separately which can be satisfied if the marker and label are on
different lines; update the assertion in the test that uses variables view and
selected to require the marker and label to appear together (for example assert
that the string "> "+selected is contained in view or parse view into lines and
verify a line equals or contains the marker plus selected), and apply the same
tightening to the similar assertion later in the file (the other check around
lines 255-257) so the test verifies the selected row itself remains visible
rather than just the marker and label existing anywhere in the output.

In `@internal/app/styles.go`:
- Around line 232-296: trimLinesAroundAnchor can produce fewer than height lines
when clamping start/end near the top or bottom removes an ellipsis without
reallocating that slot; update trimLinesAroundAnchor to guarantee len(result) ==
height by recalculating and redistributing the freed line budget when start<1 or
end>len(lines)-1 (adjust remaining, contextBefore, contextAfter or expand
start/end accordingly) before appending lines and ellipses. Specifically, after
computing start and end and performing the clamping steps that modify start/end,
detect how many lines are still needed to reach height (taking into account the
first and last lines and any ellipsis slots created via
beforeEllipsis/afterEllipsis), then expand the available window (increase end or
decrease start as possible) or remove an ellipsis and replace it with an actual
line from the opposite side so that the final result slice built into result
always has exactly height entries; keep references to trimLinesAroundAnchor,
height, anchor, start, end, remaining, contextBefore, contextAfter, result and
dimStyle.Render to locate and modify the logic.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: b82d2a37-bf61-4ee0-b328-73c610d11c33

📥 Commits

Reviewing files that changed from the base of the PR and between 62da8af and 94cc214.

📒 Files selected for processing (5)
  • internal/app/context_table.go
  • internal/app/screen_context.go
  • internal/app/screen_context_test.go
  • internal/app/styles.go
  • internal/app/styles_test.go
📜 Review details
🧰 Additional context used
📓 Path-based instructions (3)
**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.go: Use lipgloss for styled TUI output — column-aligned tables with dimmed labels in Go implementation files
Implement scroll windowing with formula: visibleLines := max(m.height-N, 5) in Go TUI implementation

Files:

  • internal/app/styles.go
  • internal/app/screen_context_test.go
  • internal/app/styles_test.go
  • internal/app/screen_context.go
  • internal/app/context_table.go

⚙️ CodeRabbit configuration file

**/*.go: For Go reviews, look beyond compilation and prioritize nil pointer risks,
context propagation, AWS SDK pagination, error wrapping, deterministic
sorting, and stable table/detail rendering. For new AWS service work,
verify that repository interfaces, model mapping, app integration, and
tests are updated together.

Files:

  • internal/app/styles.go
  • internal/app/screen_context_test.go
  • internal/app/styles_test.go
  • internal/app/screen_context.go
  • internal/app/context_table.go
internal/app/**

⚙️ CodeRabbit configuration file

internal/app/**: For Bubble Tea screen changes, verify message routing, key handling,
filter target resets, height-based windowing, help text, and back/home
navigation against the existing screen patterns.

Files:

  • internal/app/styles.go
  • internal/app/screen_context_test.go
  • internal/app/styles_test.go
  • internal/app/screen_context.go
  • internal/app/context_table.go
**/*_test.go

📄 CodeRabbit inference engine (CLAUDE.md)

Tests use mock client interfaces (see rds_test.go pattern) in Go test files

Files:

  • internal/app/screen_context_test.go
  • internal/app/styles_test.go

⚙️ CodeRabbit configuration file

**/*_test.go: Check that tests cover API errors, mapping edge cases, and navigation
state transitions, not only happy paths. Prefer mock-based tests that do
not depend on external AWS calls.

Files:

  • internal/app/screen_context_test.go
  • internal/app/styles_test.go
🔇 Additional comments (5)
internal/app/context_table.go (1)

53-101: LGTM!

Also applies to: 154-163

internal/app/screen_context.go (1)

148-150: LGTM!

Also applies to: 266-308

internal/app/screen_context_test.go (1)

27-37: LGTM!

Also applies to: 130-211

internal/app/styles.go (1)

5-17: LGTM!

Also applies to: 203-230

internal/app/styles_test.go (1)

289-296: LGTM!

Comment on lines +135 to +152
return max(terminalHeight-10, 2)
}

func (m Model) contextPickerTableHeight() int {
if m.contextPickerCompact() {
height := max(m.height-6, 2)
if m.contextPickerFilterVisible() {
height -= 2
}
return max(height, 2)
}

height := contextTableHeight(m.height)
if m.contextPickerFilterVisible() {
height -= 2
}
return max(height, 2)
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Use the required minimum visible window size in table height calculations.

Line 135 and Lines 138-152 use a minimum of 2, but this TUI windowing should clamp to 5 visible lines using max(m.height-N, 5).

💡 Suggested adjustment
 func contextTableHeight(terminalHeight int) int {
 	if terminalHeight <= 0 {
 		return defaultContextTableHeight
 	}
-	return max(terminalHeight-10, 2)
+	return max(terminalHeight-10, 5)
 }
 
 func (m Model) contextPickerTableHeight() int {
-	if m.contextPickerCompact() {
-		height := max(m.height-6, 2)
-		if m.contextPickerFilterVisible() {
-			height -= 2
-		}
-		return max(height, 2)
-	}
-
-	height := contextTableHeight(m.height)
-	if m.contextPickerFilterVisible() {
-		height -= 2
-	}
-	return max(height, 2)
+	offset := 10
+	if m.contextPickerCompact() {
+		offset = 6
+	}
+	if m.contextPickerFilterVisible() {
+		offset += 2
+	}
+	return max(m.height-offset, 5)
 }

As per coding guidelines, Implement scroll windowing with formula: visibleLines := max(m.height-N, 5) in Go TUI implementation.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
return max(terminalHeight-10, 2)
}
func (m Model) contextPickerTableHeight() int {
if m.contextPickerCompact() {
height := max(m.height-6, 2)
if m.contextPickerFilterVisible() {
height -= 2
}
return max(height, 2)
}
height := contextTableHeight(m.height)
if m.contextPickerFilterVisible() {
height -= 2
}
return max(height, 2)
}
return max(terminalHeight-10, 5)
}
func (m Model) contextPickerTableHeight() int {
offset := 10
if m.contextPickerCompact() {
offset = 6
}
if m.contextPickerFilterVisible() {
offset += 2
}
return max(m.height-offset, 5)
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/app/context_table.go` around lines 135 - 152, The table height
calculations currently clamp to a minimum of 2 visible lines; update them to use
the required minimum of 5 visible lines by replacing all occurrences of max(...,
2) with max(..., 5) in the contextPickerTableHeight function and any helper used
(e.g., contextTableHeight), and ensure the intermediate computed height
adjustments (the -6 and -2 offsets and the compact branch using max(m.height-6,
...)) still call max(..., 5) so visibleLines := max(m.height - N, 5) is
enforced; update references to contextPickerCompact() and
contextPickerFilterVisible() logic accordingly so final returned value never
drops below 5.

Comment thread internal/app/styles_test.go Outdated
Comment thread internal/app/styles.go
- require selected markers and labels on the same rendered row
- keep anchored height trimming exact near top and bottom edges
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@internal/app/styles.go`:
- Around line 251-260: When height is very small (e.g. height == 4) the current
windowSize calculation can drop to 0 and make contextAfter negative so the
anchor row disappears; detect this case and force the anchor to consume one
visible line by ensuring windowSize is at least 1 (or by limiting ellipsis to a
single slot). Concretely, in the block that computes windowSize, contextBefore
and contextAfter (variables: windowSize, contextBefore, contextAfter, anchor,
lines), add a special-case for small heights/interior anchors that sets
windowSize = 1 (or reduces the number of ellipsis slots to one) and then
recompute contextBefore/contextAfter so they are non-negative and guarantee the
computed slice indices (start:end) include the anchor line.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 2f475c64-205d-4bbc-a8d0-88fd54b742a0

📥 Commits

Reviewing files that changed from the base of the PR and between 94cc214 and fa16aee.

📒 Files selected for processing (2)
  • internal/app/styles.go
  • internal/app/styles_test.go
📜 Review details
🧰 Additional context used
📓 Path-based instructions (3)
**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.go: Use lipgloss for styled TUI output — column-aligned tables with dimmed labels in Go implementation files
Implement scroll windowing with formula: visibleLines := max(m.height-N, 5) in Go TUI implementation

Files:

  • internal/app/styles.go
  • internal/app/styles_test.go

⚙️ CodeRabbit configuration file

**/*.go: For Go reviews, look beyond compilation and prioritize nil pointer risks,
context propagation, AWS SDK pagination, error wrapping, deterministic
sorting, and stable table/detail rendering. For new AWS service work,
verify that repository interfaces, model mapping, app integration, and
tests are updated together.

Files:

  • internal/app/styles.go
  • internal/app/styles_test.go
internal/app/**

⚙️ CodeRabbit configuration file

internal/app/**: For Bubble Tea screen changes, verify message routing, key handling,
filter target resets, height-based windowing, help text, and back/home
navigation against the existing screen patterns.

Files:

  • internal/app/styles.go
  • internal/app/styles_test.go
**/*_test.go

📄 CodeRabbit inference engine (CLAUDE.md)

Tests use mock client interfaces (see rds_test.go pattern) in Go test files

Files:

  • internal/app/styles_test.go

⚙️ CodeRabbit configuration file

**/*_test.go: Check that tests cover API errors, mapping edge cases, and navigation
state transitions, not only happy paths. Prefer mock-based tests that do
not depend on external AWS calls.

Files:

  • internal/app/styles_test.go

Comment thread internal/app/styles.go
Comment on lines +251 to +260
windowSize := height - 2 // first line + footer
if anchor > 1 {
windowSize--
}
if anchor < len(lines)-2 {
windowSize--
}

contextBefore := windowSize / 2
contextAfter := windowSize - contextBefore - 1
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Preserve the anchor row when only four lines fit.

With height == 4 and an interior anchor, windowSize drops to 0, contextAfter becomes -1, and lines[start:end] is empty. The rendered result is just header/ellipsis/ellipsis/footer, so the selected row disappears again in the smallest compact views. Special-case 4-line rendering (or cap ellipsis usage to one slot there) so the anchor always consumes one visible line.

Possible fix
 	if height == 3 {
 		return []string{lines[0], lines[anchor], lines[len(lines)-1]}
 	}
+	if height == 4 {
+		if anchor <= len(lines)/2 {
+			return []string{lines[0], lines[anchor], dimStyle.Render("  ..."), lines[len(lines)-1]}
+		}
+		return []string{lines[0], dimStyle.Render("  ..."), lines[anchor], lines[len(lines)-1]}
+	}
 
 	if anchor <= 0 || anchor >= len(lines)-1 {
 		return trimLinesWithFixedEdges(lines, height)
 	}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/app/styles.go` around lines 251 - 260, When height is very small
(e.g. height == 4) the current windowSize calculation can drop to 0 and make
contextAfter negative so the anchor row disappears; detect this case and force
the anchor to consume one visible line by ensuring windowSize is at least 1 (or
by limiting ellipsis to a single slot). Concretely, in the block that computes
windowSize, contextBefore and contextAfter (variables: windowSize,
contextBefore, contextAfter, anchor, lines), add a special-case for small
heights/interior anchors that sets windowSize = 1 (or reduces the number of
ellipsis slots to one) and then recompute contextBefore/contextAfter so they are
non-negative and guarantee the computed slice indices (start:end) include the
anchor line.

@nathanhuh nathanhuh merged commit 321ab19 into main May 22, 2026
2 checks passed
@nathanhuh nathanhuh deleted the bugfix/218-context-picker-selection branch May 22, 2026 05:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug: context picker scrolls too early when moving up bug: context picker selection disappears in compact terminal

1 participant