the rest of the refactor
This commit is contained in:
125
cht/ui/agent_input.py
Normal file
125
cht/ui/agent_input.py
Normal file
@@ -0,0 +1,125 @@
|
||||
"""Agent input panel — entry, action buttons, model/language dropdowns."""
|
||||
|
||||
import logging
|
||||
|
||||
import gi
|
||||
gi.require_version("Gtk", "4.0")
|
||||
from gi.repository import Gtk, GObject
|
||||
|
||||
from cht.agent.runner import ACTIONS
|
||||
from cht.transcriber.engine import LANGUAGES
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class AgentInputPanel(Gtk.Frame):
|
||||
"""Input bar with action buttons, model/lang selectors, and text entry."""
|
||||
|
||||
__gsignals__ = {
|
||||
"send-requested": (GObject.SignalFlags.RUN_FIRST, None, (str,)),
|
||||
"action-requested": (GObject.SignalFlags.RUN_FIRST, None, (str,)),
|
||||
"model-changed": (GObject.SignalFlags.RUN_FIRST, None, (str,)),
|
||||
"lang-changed": (GObject.SignalFlags.RUN_FIRST, None, (str,)),
|
||||
"history-toggled": (GObject.SignalFlags.RUN_FIRST, None, (bool,)),
|
||||
}
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
|
||||
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)
|
||||
|
||||
actions_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=4)
|
||||
for label, verb in ACTIONS.items():
|
||||
btn = Gtk.Button(label=label)
|
||||
btn.add_css_class("flat")
|
||||
btn.connect("clicked", lambda b, v=verb: self.emit("action-requested", v))
|
||||
actions_box.append(btn)
|
||||
|
||||
spacer = Gtk.Box()
|
||||
spacer.set_hexpand(True)
|
||||
actions_box.append(spacer)
|
||||
|
||||
model_label = Gtk.Label(label="Model:")
|
||||
model_label.add_css_class("dim-label")
|
||||
actions_box.append(model_label)
|
||||
|
||||
self._model_dropdown = Gtk.DropDown.new_from_strings([])
|
||||
self._model_dropdown.set_size_request(200, -1)
|
||||
self._model_dropdown.connect("notify::selected", self._on_model_changed)
|
||||
actions_box.append(self._model_dropdown)
|
||||
|
||||
lang_label = Gtk.Label(label="Lang:")
|
||||
lang_label.add_css_class("dim-label")
|
||||
actions_box.append(lang_label)
|
||||
|
||||
lang_names = list(LANGUAGES.keys())
|
||||
self._lang_names = lang_names
|
||||
self._lang_dropdown = Gtk.DropDown.new_from_strings(lang_names)
|
||||
self._lang_dropdown.set_selected(0)
|
||||
self._lang_dropdown.connect("notify::selected", self._on_lang_changed)
|
||||
actions_box.append(self._lang_dropdown)
|
||||
|
||||
history_toggle = Gtk.CheckButton(label="Chat")
|
||||
history_toggle.set_tooltip_text("Include conversation history in prompts")
|
||||
history_toggle.connect("toggled", lambda b: self.emit("history-toggled", b.get_active()))
|
||||
actions_box.append(history_toggle)
|
||||
|
||||
outer.append(actions_box)
|
||||
|
||||
input_row = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=4)
|
||||
self._entry = Gtk.Entry()
|
||||
self._entry.set_hexpand(True)
|
||||
self._entry.set_placeholder_text("Message agent... (@F1-3 frames, @T1-5 transcript)")
|
||||
self._entry.connect("activate", lambda e: self._do_send())
|
||||
input_row.append(self._entry)
|
||||
|
||||
send_btn = Gtk.Button(label="Send")
|
||||
send_btn.add_css_class("suggested-action")
|
||||
send_btn.connect("clicked", lambda b: self._do_send())
|
||||
input_row.append(send_btn)
|
||||
outer.append(input_row)
|
||||
|
||||
self.set_child(outer)
|
||||
|
||||
@property
|
||||
def entry(self) -> Gtk.Entry:
|
||||
"""The text entry widget (for focus checks)."""
|
||||
return self._entry
|
||||
|
||||
def get_text(self) -> str:
|
||||
return self._entry.get_text().strip()
|
||||
|
||||
def clear_text(self) -> None:
|
||||
self._entry.set_text("")
|
||||
|
||||
def populate_models(self, models: list[str], current: str | None = None) -> None:
|
||||
if not models:
|
||||
return
|
||||
string_list = Gtk.StringList.new(models)
|
||||
self._model_dropdown.set_model(string_list)
|
||||
if current:
|
||||
for i, m in enumerate(models):
|
||||
if m == current:
|
||||
self._model_dropdown.set_selected(i)
|
||||
break
|
||||
|
||||
def _do_send(self):
|
||||
text = self.get_text()
|
||||
self.clear_text()
|
||||
self.emit("send-requested", text)
|
||||
|
||||
def _on_model_changed(self, dropdown, _pspec):
|
||||
idx = dropdown.get_selected()
|
||||
model = dropdown.get_model()
|
||||
if model and idx < model.get_n_items():
|
||||
self.emit("model-changed", model.get_string(idx))
|
||||
|
||||
def _on_lang_changed(self, dropdown, _pspec):
|
||||
idx = dropdown.get_selected()
|
||||
if idx < len(self._lang_names):
|
||||
lang_code = LANGUAGES[self._lang_names[idx]]
|
||||
self.emit("lang-changed", lang_code or "")
|
||||
106
cht/ui/session_dialog.py
Normal file
106
cht/ui/session_dialog.py
Normal file
@@ -0,0 +1,106 @@
|
||||
"""Session browser dialog — lists sessions, supports load and delete."""
|
||||
|
||||
import json
|
||||
import logging
|
||||
|
||||
import gi
|
||||
gi.require_version("Gtk", "4.0")
|
||||
gi.require_version("Adw", "1")
|
||||
from gi.repository import Gtk, Adw, GObject
|
||||
|
||||
from cht.stream.manager import list_sessions, delete_sessions
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class SessionDialog(Adw.Window):
|
||||
"""Modal dialog listing sessions. Emits 'session-selected' with session id."""
|
||||
|
||||
__gsignals__ = {
|
||||
"session-selected": (GObject.SignalFlags.RUN_FIRST, None, (str,)),
|
||||
}
|
||||
|
||||
def __init__(self, parent, **kwargs):
|
||||
super().__init__(transient_for=parent, modal=True, **kwargs)
|
||||
self.set_title("Load Session")
|
||||
self.set_default_size(500, 400)
|
||||
|
||||
sessions = list_sessions()
|
||||
|
||||
toolbar = Adw.ToolbarView()
|
||||
header = Adw.HeaderBar()
|
||||
|
||||
select_all_btn = Gtk.CheckButton(label="All")
|
||||
header.pack_start(select_all_btn)
|
||||
|
||||
delete_btn = Gtk.Button(label="Delete")
|
||||
delete_btn.add_css_class("destructive-action")
|
||||
header.pack_end(delete_btn)
|
||||
|
||||
toolbar.add_top_bar(header)
|
||||
|
||||
scroll = Gtk.ScrolledWindow()
|
||||
scroll.set_vexpand(True)
|
||||
listbox = Gtk.ListBox()
|
||||
listbox.set_selection_mode(Gtk.SelectionMode.NONE)
|
||||
listbox.add_css_class("boxed-list")
|
||||
|
||||
checks: list[tuple[str, Gtk.CheckButton]] = []
|
||||
|
||||
for sid, sdir in sessions:
|
||||
idx = sdir / "frames" / "index.json"
|
||||
nframes = 0
|
||||
try:
|
||||
nframes = len(json.loads(idx.read_text()))
|
||||
except Exception:
|
||||
pass
|
||||
nrec = len(list((sdir / "stream").glob("recording_*.mp4")))
|
||||
|
||||
check = Gtk.CheckButton()
|
||||
checks.append((sid, check))
|
||||
|
||||
row = Adw.ActionRow()
|
||||
row.set_title(sid)
|
||||
row.set_subtitle(f"{nframes} frames, {nrec} segments")
|
||||
row.set_activatable(True)
|
||||
row.add_prefix(check)
|
||||
|
||||
def _on_row_activated(r, s=sid):
|
||||
self.close()
|
||||
self.emit("session-selected", s)
|
||||
row.connect("activated", _on_row_activated)
|
||||
listbox.append(row)
|
||||
|
||||
def _on_select_all(btn):
|
||||
active = btn.get_active()
|
||||
for _, cb in checks:
|
||||
cb.set_active(active)
|
||||
select_all_btn.connect("toggled", _on_select_all)
|
||||
|
||||
def _on_delete(btn):
|
||||
to_delete = [sid for sid, cb in checks if cb.get_active()]
|
||||
if not to_delete:
|
||||
return
|
||||
if self._current_session in to_delete:
|
||||
to_delete.remove(self._current_session)
|
||||
if to_delete:
|
||||
delete_sessions(to_delete)
|
||||
self.close()
|
||||
# Re-open with updated list
|
||||
new_dialog = SessionDialog(self.get_transient_for())
|
||||
new_dialog._current_session = self._current_session
|
||||
# Forward the signal
|
||||
new_dialog.connect("session-selected",
|
||||
lambda d, s: self.emit("session-selected", s))
|
||||
new_dialog.present()
|
||||
delete_btn.connect("clicked", _on_delete)
|
||||
|
||||
scroll.set_child(listbox)
|
||||
toolbar.set_content(scroll)
|
||||
self.set_content(toolbar)
|
||||
|
||||
self._current_session = None
|
||||
|
||||
def set_current_session(self, session_id: str | None) -> None:
|
||||
"""Set the active session id so it won't be deleted."""
|
||||
self._current_session = session_id
|
||||
Reference in New Issue
Block a user