jim-bo Claude Opus 4.6 (1M context) commited on
Commit
0de7b7f
·
1 Parent(s): c7438e0

fix: prevent agent selection dialog from auto-closing + landing page tweaks

Browse files

- Add _agent_waiting_for_input flag to prevent focus-loss auto-cancel
from dismissing the selection dialog while the agent is blocked
waiting for user input (fixes hang in web/browser mode)
- Replace hardcoded "Env: dev / Status: ready" with description blurb
- Add ASCII robot to landing page graphic

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

src/cli_textual/app.py CHANGED
@@ -55,6 +55,7 @@ class ChatApp(App):
55
  self.message_history = [] # For LLM context memory
56
  self.interactive_input_queue = asyncio.Queue()
57
  self.verbose_mode = False
 
58
 
59
 
60
  # Initialize Core Managers
@@ -109,14 +110,15 @@ class ChatApp(App):
109
  def handle_agent_selection(self, event: OptionList.OptionSelected) -> None:
110
  """Handle user making a choice requested by an agent tool."""
111
  selection = str(event.option.prompt)
 
112
  # Clear the interaction area
113
  interaction = self.query_one("#interaction-container")
114
  interaction.remove_class("visible")
115
  interaction.query("*").remove()
116
-
117
  # Log choice to history
118
  self.add_to_history(f"Selected: **{selection}**", is_user=True)
119
-
120
  # Resume the agent by pushing the selection into the queue
121
  if hasattr(self, "interactive_input_queue"):
122
  # Drain any stale entries (safety measure)
@@ -206,17 +208,18 @@ class ChatApp(App):
206
 
207
  elif isinstance(event, AgentRequiresUserInput):
208
  # Pause and show the interaction UI
 
209
  interaction = self.query_one("#interaction-container")
210
  interaction.add_class("visible")
211
  interaction.query("*").remove()
212
-
213
  interaction.mount(Label(event.prompt))
214
-
215
  if event.tool_name == "/select":
216
  options = OptionList(*event.options, id="agent-select-tool")
217
  interaction.mount(options)
218
  self.call_after_refresh(options.focus)
219
-
220
  history.scroll_end(animate=False)
221
 
222
  elif isinstance(event, AgentExecuteCommand):
@@ -312,8 +315,11 @@ class ChatApp(App):
312
 
313
  def check_focus_loss(self):
314
  try:
 
 
315
  container = self.query_one("#interaction-container")
316
- if "visible" in container.classes and not any(w.has_focus for w in container.query("*")): self.cancel_interaction()
 
317
  except: pass
318
 
319
  def action_cancel_interaction(self):
 
55
  self.message_history = [] # For LLM context memory
56
  self.interactive_input_queue = asyncio.Queue()
57
  self.verbose_mode = False
58
+ self._agent_waiting_for_input = False
59
 
60
 
61
  # Initialize Core Managers
 
110
  def handle_agent_selection(self, event: OptionList.OptionSelected) -> None:
111
  """Handle user making a choice requested by an agent tool."""
112
  selection = str(event.option.prompt)
113
+ self._agent_waiting_for_input = False
114
  # Clear the interaction area
115
  interaction = self.query_one("#interaction-container")
116
  interaction.remove_class("visible")
117
  interaction.query("*").remove()
118
+
119
  # Log choice to history
120
  self.add_to_history(f"Selected: **{selection}**", is_user=True)
121
+
122
  # Resume the agent by pushing the selection into the queue
123
  if hasattr(self, "interactive_input_queue"):
124
  # Drain any stale entries (safety measure)
 
208
 
209
  elif isinstance(event, AgentRequiresUserInput):
210
  # Pause and show the interaction UI
211
+ self._agent_waiting_for_input = True
212
  interaction = self.query_one("#interaction-container")
213
  interaction.add_class("visible")
214
  interaction.query("*").remove()
215
+
216
  interaction.mount(Label(event.prompt))
217
+
218
  if event.tool_name == "/select":
219
  options = OptionList(*event.options, id="agent-select-tool")
220
  interaction.mount(options)
221
  self.call_after_refresh(options.focus)
222
+
223
  history.scroll_end(animate=False)
224
 
225
  elif isinstance(event, AgentExecuteCommand):
 
315
 
316
  def check_focus_loss(self):
317
  try:
318
+ if self._agent_waiting_for_input:
319
+ return
320
  container = self.query_one("#interaction-container")
321
+ if "visible" in container.classes and not any(w.has_focus for w in container.query("*")):
322
+ self.cancel_interaction()
323
  except: pass
324
 
325
  def action_cancel_interaction(self):
src/cli_textual/app.tcss CHANGED
@@ -58,9 +58,8 @@ LandingPage {
58
  }
59
 
60
  #landing-graphic {
61
- height: 2;
62
  width: 10;
63
- background: #2A2A2A;
64
  margin: 0;
65
  }
66
 
 
58
  }
59
 
60
  #landing-graphic {
61
+ height: 3;
62
  width: 10;
 
63
  margin: 0;
64
  }
65
 
src/cli_textual/ui/widgets/landing_page.py CHANGED
@@ -10,10 +10,14 @@ class LandingPage(Static):
10
  yield Label("— TUI Framework —", id="landing-title")
11
  with Horizontal(id="landing-content"):
12
  with Container(id="landing-left"):
13
- yield Static(id="landing-graphic")
 
 
 
 
 
14
  yield Label("Modular Agentic Interface", id="landing-subtitle")
15
- yield Static("Env: [cyan]dev[/]", classes="landing-info")
16
- yield Static("Status: [green]ready[/]", classes="landing-info")
17
 
18
  with Container(id="landing-right"):
19
  yield Label("Slash Commands", classes="landing-header")
 
10
  yield Label("— TUI Framework —", id="landing-title")
11
  with Horizontal(id="landing-content"):
12
  with Container(id="landing-left"):
13
+ yield Static(
14
+ " \\\\\\[o_o]/\n"
15
+ " /|_|\\\n"
16
+ " d b",
17
+ id="landing-graphic",
18
+ )
19
  yield Label("Modular Agentic Interface", id="landing-subtitle")
20
+ yield Static("A generic agentic CLI for learning\nhow these things work.", classes="landing-info")
 
21
 
22
  with Container(id="landing-right"):
23
  yield Label("Slash Commands", classes="landing-header")