diff --git a/.jules/palette.md b/.jules/palette.md index 559dc665..2d8b72ff 100644 --- a/.jules/palette.md +++ b/.jules/palette.md @@ -1,3 +1,7 @@ ## 2026-08-29 - Truncation Awareness **Learning:** When displaying dynamic lists in terminal UIs (such as those using `ink`), if the list is truncated to fit constraints (e.g., a `maxVisible` limit), it is important to include an explicit visual indicator to preserve user situational awareness. **Action:** Always add an explicit UI element indicating truncation and count, rather than silently hiding items. + +## 2026-08-31 - Visual Pagination and Tab-Completion Hints in Terminal UIs +**Learning:** Terminal UI lists with `maxVisible` limits that truncate suggestions (like `CommandSuggestionList`) can cause users to lose situational awareness of how many total items exist, and hidden tab-completion shortcuts can be unintuitive for users expecting standard terminal behavior. Adding pagination indicators (e.g., `1-5 of 12`) and explicit shortcut hints (`⇥ complete`) dramatically improves discoverability and clarity without cluttering the interface. +**Action:** When designing or updating truncated lists in terminal UIs (like ink), always include a visual indicator of the total item count and explicit hints for available keyboard shortcuts (like tab completion) in the status bar to preserve context and discoverability. diff --git a/src/cli/ui/components/CommandInput.tsx b/src/cli/ui/components/CommandInput.tsx index 3f05f99b..47afbf1b 100644 --- a/src/cli/ui/components/CommandInput.tsx +++ b/src/cli/ui/components/CommandInput.tsx @@ -315,6 +315,8 @@ export const CommandInput: React.FC = ({ suggestions={visibleSuggestions} selectedIndex={selectedIndex - startIndex} parentCommand={activeCommand} + totalSuggestions={suggestions.length} + startIndex={startIndex} /> )} diff --git a/src/cli/ui/components/CommandSuggestionList.tsx b/src/cli/ui/components/CommandSuggestionList.tsx index 7e66838a..8157a723 100644 --- a/src/cli/ui/components/CommandSuggestionList.tsx +++ b/src/cli/ui/components/CommandSuggestionList.tsx @@ -9,12 +9,16 @@ interface CommandSuggestionListProps { selectedIndex: number; parentCommand?: Command; filterText?: string; + totalSuggestions: number; + startIndex: number; } export const CommandSuggestionList: React.FC = ({ suggestions, selectedIndex, parentCommand, + totalSuggestions, + startIndex, }) => { if (suggestions.length === 0) return null; @@ -116,7 +120,12 @@ export const CommandSuggestionList: React.FC = ({ - ↑↓ nav · ⏎ select · esc close + {totalSuggestions > suggestions.length && ( + + {startIndex + 1}-{startIndex + suggestions.length} of {totalSuggestions} ·{' '} + + )} + ↑↓ nav · ⇥ complete · ⏎ select · esc close diff --git a/tests/unit/cli/ui/components/command-suggestion-list.test.tsx b/tests/unit/cli/ui/components/command-suggestion-list.test.tsx new file mode 100644 index 00000000..1988f65e --- /dev/null +++ b/tests/unit/cli/ui/components/command-suggestion-list.test.tsx @@ -0,0 +1,54 @@ +import { render } from '@testing-library/react'; +import React from 'react'; + +mock.module('ink', () => ({ + Box: (props: any) => React.createElement('div', null, props.children), + Text: (props: any) => React.createElement('span', null, props.children), +})); + +describe('CommandSuggestionList pagination', () => { + async function loadCommandSuggestionList() { + return (await import('../../../../../src/cli/ui/components/CommandSuggestionList.js')) + .CommandSuggestionList; + } + + it('hides pagination indicator when totalSuggestions <= suggestions.length', async () => { + const CommandSuggestionList = await loadCommandSuggestionList(); + + const { container } = render( + , + ); + + // It should contain the shortcuts but NOT the pagination numbers + expect(container.textContent).toContain('↑↓ nav · ⇥ complete · ⏎ select · esc close'); + expect(container.textContent).not.toContain('1-2 of 2'); + }); + + it('shows pagination indicator when totalSuggestions > suggestions.length', async () => { + const CommandSuggestionList = await loadCommandSuggestionList(); + + const { container } = render( + , + ); + + // It should contain both the pagination numbers (3-4 of 12) and the shortcuts + expect(container.textContent).toContain('3-4 of 12'); + expect(container.textContent).toContain('↑↓ nav · ⇥ complete · ⏎ select · esc close'); + }); +});