Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .jules/palette.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
## 2025-05-20 - TUI Accessibility and Keyboard Traps
**Learning:** Terminal UIs can trap keyboard users when standard interrupt bytes (\x03) are caught by raw mode without an explicit exit path, and headless prompts without inline choices lack intuitiveness.
**Action:** Always map \x03 to explicitly raise KeyboardInterrupt in TUI raw mode, include inline choices for text prompts, and provide explicit shortcut hints (Esc/Ctrl-C) in footers.
8 changes: 5 additions & 3 deletions libs/terminal_ui.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ def _read_key(self):
return mapping.get(next_chars, 'escape')
if key in ('\r', '\n'):
return 'enter'
if key == '\x03':
raise KeyboardInterrupt('Selection cancelled by user.')
return key
finally:
termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
Expand All @@ -67,7 +69,7 @@ def _render_selector(self, title, options, selected_index, help_text, default):
style = 'bold green' if index == selected_index else ''
table.add_row(marker, label, style=style)

footer = help_text or 'Use Up/Down arrows and Enter to select.'
footer = help_text or 'Use Up/Down arrows and Enter to select. (Esc/Ctrl-C to cancel)'
self.console.print(Panel.fit(footer, title='Interpreter TUI', border_style='green'))
Comment on lines +72 to 73

Copy link
Copy Markdown
Contributor

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

Ensure cancel hint is always present even when custom help text is passed.

footer = help_text or ... means custom help strings can silently drop the new (Esc/Ctrl-C to cancel) guidance (e.g., model selector help text), which weakens the accessibility objective.

Suggested patch
-        footer = help_text or 'Use Up/Down arrows and Enter to select. (Esc/Ctrl-C to cancel)'
+        footer = help_text or 'Use Up/Down arrows and Enter to select.'
+        if '(Esc/Ctrl-C to cancel)' not in footer:
+            footer = f'{footer} (Esc/Ctrl-C to cancel)'
πŸ“ 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
footer = help_text or 'Use Up/Down arrows and Enter to select. (Esc/Ctrl-C to cancel)'
self.console.print(Panel.fit(footer, title='Interpreter TUI', border_style='green'))
footer = help_text or 'Use Up/Down arrows and Enter to select.'
if '(Esc/Ctrl-C to cancel)' not in footer:
footer = f'{footer} (Esc/Ctrl-C to cancel)'
self.console.print(Panel.fit(footer, title='Interpreter TUI', border_style='green'))
πŸ€– 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 `@libs/terminal_ui.py` around lines 72 - 73, The current footer logic in the
assignment on line 72 uses the `or` operator which means if custom help_text is
provided, the "(Esc/Ctrl-C to cancel)" guidance is completely dropped. To ensure
the cancel hint is always present for accessibility, modify the footer
assignment to always include the cancel hint guidance by appending it to the
help text (whether custom or default) rather than choosing one or the other with
the `or` operator. This ensures the Panel.fit call on line 73 always displays
the cancel guidance regardless of whether custom help text is provided.

self.console.print(f"[bold cyan]{title}[/bold cyan]")
self.console.print(table)
Expand All @@ -76,7 +78,7 @@ def _render_selector(self, title, options, selected_index, help_text, default):
def _select_option(self, title, options, default, help_text=None):
if not sys.stdin.isatty():
default_choice = default if default in options else options[0]
answer = Prompt.ask(f"{title}", default=default_choice).strip()
answer = Prompt.ask(f"{title} \\[{'|'.join(options)}]", default=default_choice).strip()
if answer in options:
return answer
for option in options:
Expand Down Expand Up @@ -110,7 +112,7 @@ def _select_option(self, title, options, default, help_text=None):

def _select_boolean(self, title, default=False):
default_choice = 'yes' if default else 'no'
choice = self._select_option(title, ['yes', 'no'], default_choice, 'Use Up/Down arrows and Enter to choose.')
choice = self._select_option(title, ['yes', 'no'], default_choice, 'Use Up/Down arrows and Enter to choose. (Esc/Ctrl-C to cancel)')
return choice == 'yes'

def select_mode(self, default_mode='code'):
Expand Down
Loading