This commit is contained in:
2026-04-01 22:46:27 -03:00
parent dbc8fd814c
commit 73b824f6c3
6 changed files with 460 additions and 14 deletions

View File

@@ -15,6 +15,7 @@ from cht.ui.timeline import Timeline, TimelineControls
from cht.ui.monitor import MonitorWidget
from cht.stream.manager import StreamManager
from cht.stream.tracker import RecordingTracker
from cht.agent.runner import AgentRunner, ACTIONS, check_claude_cli
log = logging.getLogger(__name__)
@@ -32,6 +33,7 @@ class ChtWindow(Adw.ApplicationWindow):
# Timeline is the central state machine
self._timeline = Timeline()
self._agent = AgentRunner()
# Main layout
self._main_paned = Gtk.Paned(orientation=Gtk.Orientation.HORIZONTAL)
@@ -61,8 +63,8 @@ class ChtWindow(Adw.ApplicationWindow):
self.connect("close-request", self._on_close)
log.info("Window initialized")
# Auto-connect on startup
GLib.idle_add(self._start_stream)
GLib.idle_add(self._check_agent_auth)
def _on_connect_clicked(self, button):
if self._streaming:
@@ -340,34 +342,90 @@ class ChtWindow(Adw.ApplicationWindow):
return frame
def _build_agent_input(self):
box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=4)
box.set_margin_start(4)
box.set_margin_end(4)
box.set_margin_top(4)
box.set_margin_bottom(4)
outer = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=4)
outer.set_margin_start(4)
outer.set_margin_end(4)
outer.set_margin_top(4)
outer.set_margin_bottom(4)
# Quick action buttons
actions_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=4)
for label in ACTIONS:
btn = Gtk.Button(label=label)
btn.add_css_class("flat")
btn.connect("clicked", lambda b, l=label: self._send_message(ACTIONS[l]))
actions_box.append(btn)
outer.append(actions_box)
# Text entry + send
input_row = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=4)
self._input_entry = Gtk.Entry()
self._input_entry.set_hexpand(True)
self._input_entry.set_placeholder_text("Message agent... (use @ to reference frames/transcripts)")
self._input_entry.set_placeholder_text("Message agent... (use @F0001 to reference a frame)")
self._input_entry.connect("activate", lambda e: self._send_message())
box.append(self._input_entry)
input_row.append(self._input_entry)
send_btn = Gtk.Button(label="Send")
send_btn.add_css_class("suggested-action")
send_btn.connect("clicked", lambda b: self._send_message())
box.append(send_btn)
input_row.append(send_btn)
outer.append(input_row)
frame = Gtk.Frame()
frame.set_child(box)
frame.set_child(outer)
return frame
def _send_message(self):
text = self._input_entry.get_text().strip()
def _send_message(self, text: str | None = None):
if text is None:
text = self._input_entry.get_text().strip()
self._input_entry.set_text("")
if not text:
return
if not self._stream_mgr:
self._append_agent_output("No active session.\n")
return
self._append_agent_output(f"\n> {text}\n\n")
self._agent.send(
message=text,
stream_mgr=self._stream_mgr,
tracker=self._tracker,
on_chunk=lambda chunk: GLib.idle_add(self._replace_thinking, chunk),
on_done=lambda err: GLib.idle_add(
self._append_agent_output,
f"[Error: {err}]\n" if err else "\n"
),
)
self._thinking_replaced = False
def _replace_thinking(self, chunk: str):
"""Replace the '' placeholder with the first chunk, then append normally."""
if not self._thinking_replaced:
self._thinking_replaced = True
buf = self._agent_output_view.get_buffer()
# Remove the trailing '…\n' (3 chars)
end = buf.get_end_iter()
start = end.copy()
start.backward_chars(2)
buf.delete(start, end)
self._append_agent_output(chunk)
def _check_agent_auth(self):
import os
if os.environ.get("GROQ_API_KEY") or os.environ.get("OPENAI_API_KEY"):
return # using external provider, no CLI check needed
err = check_claude_cli()
if err:
self._append_agent_output(f"{err}\n")
else:
self._append_agent_output(f"Agent ready ({self._agent.provider_name})\n")
def _append_agent_output(self, text: str):
buf = self._agent_output_view.get_buffer()
buf.insert(buf.get_end_iter(), f"\n> {text}\n")
self._input_entry.set_text("")
buf.insert(buf.get_end_iter(), text)
# Auto-scroll to bottom
self._agent_output_view.scroll_to_iter(buf.get_end_iter(), 0, False, 0, 0)
# -- Frame thumbnails --