Skip to content
Open
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 @@
## 2024-05-30 - Terminal UX Enhancements
**Learning:** In terminal UI (TUI) environments, web-specific UX concepts translate to terminal equivalents such as using explicit prompt choices, providing clear shortcut hints ('Esc/Ctrl-C to cancel'), and avoiding keyboard traps by mapping standard interrupt bytes (\x03) to exit actions in raw mode. When using rich's Prompt.ask, manually formatting choices (e.g., \[option1|option2]) avoids markup parsing issues while bypassing strict validation that breaks custom case-insensitivity logic.
**Action:** When building TUI prompts, map \x03 to escape actions to prevent keyboard traps and manually inject explicit choices with escaped brackets into prompt strings.
12 changes: 7 additions & 5 deletions libs/terminal_ui.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ def _read_key(self):
return mapping.get(extended, extended)
if key == '\r':
return 'enter'
if key == '\x1b':
if key in ('\x1b', '\x03'):
return 'escape'
return key

Expand All @@ -43,6 +43,8 @@ def _read_key(self):
return mapping.get(next_chars, 'escape')
if key in ('\r', '\n'):
return 'enter'
if key == '\x03':
return 'escape'
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'))
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 All @@ -121,7 +123,7 @@ def select_model(self, default_model=None):
default_model = default_model or self.utility_manager.get_default_model_name()
if default_model not in models:
default_model = models[0]
return self._select_option('Model', models, default_model, 'Use Up/Down arrows, Enter, or type the first letter to jump.')
return self._select_option('Model', models, default_model, 'Use Up/Down arrows, Enter, or type the first letter to jump. Esc/Ctrl-C to cancel.')

def select_language(self, default_lang='python'):
return self._select_option('Language', ['python', 'javascript'], default_lang)
Expand Down
Loading