Skip to content
Back to skills

523 2026 02 02 12 56 40 Esc Key Flow A822b2d2

ASecurity

**Date:** 2026-02-02 **Owner:** agent **Phase:** Research

  • 9 stars
  • 0 votes
  • 0 copies
  • 2 views
  • Added June 1, 2026
documentationpythongoshelldocumentation

Security analysis

A100/100

Pro scans all 2 files and shows the line behind each finding

Scanned June 1, 2026

npx -y skills add tools-only/X-Skills --skill 523-2026-02-02_12-56-40_esc-key-flow_a822b2d2 --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of 523 2026 02 02 12 56 40 Esc Key Flow A822b2d2?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for 523 2026 02 02 12 56 40 Esc Key Flow A822b2d2
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/tools-only-523-2026-02-02-12-56-40-esc-key-flow-a822b2d2/badge)](https://www.skillsdirectory.com/skills/tools-only-523-2026-02-02-12-56-40-esc-key-flow-a822b2d2)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
# Research - ESC Key Logic Flow in TUI Conversation

**Date:** 2026-02-02
**Owner:** agent
**Phase:** Research

## Goal

Map the complete ESC key logic flow when a user is in conversation with the agent, including how chat disappears and panel dismissal behavior.

## Findings

### ESC Key Binding Location

- `src/tunacode/ui/app.py:65` - Global ESC binding with priority flag
  ```python
  Binding("escape", "cancel_request", "Cancel", show=False, priority=True)
  ```

### Main ESC Handler - Priority Cascade

**File:** `src/tunacode/ui/app.py:343-356`

```
User presses ESC
       |
       v
action_cancel_request() executes
       |
       v
Cascading checks (first match wins):
       |
       +---> 1. Active request task? --> Cancel asyncio task, RETURN
       |
       +---> 2. Shell command running? --> Cancel shell subprocess, RETURN
       |
       +---> 3. Editor has content/paste buffer? --> Clear editor input
```

**Code Flow:**

1. **Cancel Running Agent Request** (lines 345-347):
   - Checks `self._current_request_task is not None`
   - Calls `self._current_request_task.cancel()` - cancels asyncio task
   - Returns immediately (no fallthrough)

2. **Cancel Shell Command** (lines 349-352):
   - Checks `shell_runner.is_running()`
   - Calls `shell_runner.cancel()` to terminate subprocess
   - Shell runner at `src/tunacode/ui/shell_runner.py:119-137`
   - Returns immediately

3. **Clear Editor Input** (lines 354-355):
   - Checks `self.editor.value or self.editor.has_paste_buffer`
   - Calls `editor.clear_input()` at `src/tunacode/ui/widgets/editor.py:110-113`

### Modal Screen ESC Handling

Modal screens intercept ESC before the app-level handler:

| Screen | File | Binding Line | Action |
|--------|------|--------------|--------|
| ProviderPickerScreen | `screens/model_picker.py` | 94 | Two-phase: clear filter first, then dismiss |
| ModelPickerScreen | `screens/model_picker.py` | 212 | Two-phase: clear filter first, then dismiss |
| SessionPickerScreen | `screens/session_picker.py` | 55 | Dismiss with None |
| ThemePickerScreen | `screens/theme_picker.py` | 48 | Revert theme, dismiss with None |
| UpdateConfirmScreen | `screens/update_confirm.py` | 46 | Dismiss with False |
| SetupScreen | `screens/setup.py` | 29 | Skip setup, dismiss with False |

### Two-Phase ESC in Picker Modals

**File:** `src/tunacode/ui/screens/model_picker.py:160-164`

```
ESC pressed in Model/Provider Picker
       |
       v
Filter has text? ----YES----> Clear filter input, rebuild options
       |                      event.stop() prevents propagation
       NO                     (User must press ESC again to close)
       |
       v
action_cancel() called
       |
       v
self.dismiss(None) --> Modal closes, callback receives None
```

### Chat Container Behavior

**File:** `src/tunacode/ui/widgets/chat.py`

- ChatContainer is **always visible** - no show/hide toggle mechanism
- Has `clear()` method (lines 83-87) but no visibility state
- Cannot be "dismissed" - it's a permanent part of the main layout
- ESC does NOT hide the chat container

### Editor Clear Logic

**File:** `src/tunacode/ui/widgets/editor.py:110-113`

```python
def clear_input(self) -> None:
    self.value = ""
    self._clear_paste_buffer()
    self.scroll_to(x=0, y=0, animate=False, immediate=True)
```

## Key Patterns / Solutions Found

- **Priority Binding**: `priority=True` ensures ESC takes precedence over widget-level bindings
- **Cascade Pattern**: Handler uses if-return cascade for clear priority ordering
- **Event Propagation Control**:
  - `event.prevent_default()` stops widget's default behavior
  - `event.stop()` prevents propagation to parent/bindings
- **Screen Dismissal**: All modals use Textual's `self.dismiss(value)` pattern
- **No Hidden State**: No panel visibility toggles or state flags - modals are pushed/popped from screen stack

## Visual Flow Diagram

```
+-------------------+
|  User presses ESC |
+--------+----------+
         |
         v
+--------+----------+     YES    +----------------------+
| Modal screen      +----------->| Modal's on_key()     |
| on screen stack?  |            | or action_cancel()   |
+--------+----------+            +----------+-----------+
         | NO                               |
         v                                  v
+--------+----------+            +----------+-----------+
| App binding:      |            | screen.dismiss()     |
| action_cancel_    |            | pops screen stack    |
| request()         |            +----------------------+
+--------+----------+
         |
         v
+--------+----------+     YES    +----------------------+
| Request task      +----------->| task.cancel()        |
| running?          |            | RETURN               |
+--------+----------+            +----------------------+
         | NO
         v
+--------+----------+     YES    +----------------------+
| Shell command     +----------->| shell_runner.cancel()|
| running?          |            | RETURN               |
+--------+----------+            +----------------------+
         | NO
         v
+--------+----------+     YES    +----------------------+
| Editor has        +----------->| editor.clear_input() |
| content?          |            +----------------------+
+--------+----------+
         | NO
         v
+--------+----------+
| No action taken   |
+-------------------+
```

## Knowledge Gaps

- No documentation on why chat container uses always-visible pattern vs modal approach
- No explicit "panel save" functionality found - sessions are managed via StateManager, not visible panels

## References

- `src/tunacode/ui/app.py` - Main application, ESC binding and handler (lines 65, 343-356)
- `src/tunacode/ui/screens/model_picker.py` - Two-phase ESC pattern (lines 160-164, 289-293)
- `src/tunacode/ui/widgets/editor.py` - Editor clear logic (lines 110-113)
- `src/tunacode/ui/shell_runner.py` - Shell cancellation (lines 119-137)
- `src/tunacode/ui/widgets/chat.py` - Chat container (always visible)

Files in this skill

  • README.md938 B
  • skill.md5.9 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.