summaryrefslogtreecommitdiffstats
path: root/test_llamachat.py
diff options
context:
space:
mode:
authorDanilo M. <danix@danix.xyz>2026-07-31 11:25:52 +0200
committerDanilo M. <danix@danix.xyz>2026-07-31 11:25:52 +0200
commit08e11d04ef1150e5b4a862bf82179e2196daa850 (patch)
tree0d50cf3e59ad0a00634b5ad2c44c841ec62fd29b /test_llamachat.py
parente1105f76e127bf8c19511a7494a7c1331181b9dc (diff)
downloadllamachat-08e11d04ef1150e5b4a862bf82179e2196daa850.tar.gz
llamachat-08e11d04ef1150e5b4a862bf82179e2196daa850.zip
feat: add Ctrl+N and Ctrl+F shortcuts
Ctrl+N starts a fresh conversation and puts the cursor in the input box. Ctrl+F jumps to the search field. The search field sits in the top bar, but its results render in the history panel, so focusing it reveals a hidden panel rather than leaving the search with nowhere to show its hits. Escape now backs out of the search field before it hides the window: first press clears the text, second returns to the input, third hides. Previously a stray Escape while filtering dismissed the whole window. That check reads focusWidget() rather than hasFocus(), so it still holds when the window is not the active one. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Diffstat (limited to 'test_llamachat.py')
-rwxr-xr-xtest_llamachat.py108
1 files changed, 108 insertions, 0 deletions
diff --git a/test_llamachat.py b/test_llamachat.py
index ae2b478..0986631 100755
--- a/test_llamachat.py
+++ b/test_llamachat.py
@@ -654,6 +654,113 @@ def test_sidebar_toggle():
print("ok sidebar toggle")
+def test_shortcuts():
+ """Ctrl+N, Ctrl+F and Escape behave as advertised."""
+ os.environ.setdefault("QT_QPA_PLATFORM", "offscreen")
+ from PySide6.QtCore import QEvent, Qt
+ from PySide6.QtGui import QKeyEvent
+ from PySide6.QtWidgets import QApplication
+
+ from llamachat import backend as _backend
+ from llamachat import config as _config
+ from llamachat.ui import ChatWindow
+
+ app = QApplication.instance() or QApplication([])
+
+ with tempfile.TemporaryDirectory() as tmp:
+ cfg = _config.load(Path(tmp) / "config.toml")
+ cfg.prompts_dir = Path(tmp) / "prompts"
+ cfg.db_path = Path(tmp) / "t.db"
+ history = db.History(cfg.db_path)
+ window = ChatWindow(
+ cfg,
+ history,
+ _backend.Client(cfg.base_url),
+ _config.parse_presets(cfg.presets_path),
+ )
+ window.resize(1000, 700)
+ window.show()
+ window.raise_()
+ window.activateWindow()
+ # The offscreen platform only grants focus to the active window, and
+ # a window left over from an earlier check can still hold it.
+ window.setFocus()
+ app.processEvents()
+
+ registered = {
+ a.shortcut().toString()
+ for a in window.actions()
+ if not a.shortcut().isEmpty()
+ }
+ for wanted in ("Ctrl+N", "Ctrl+F", "Ctrl+\\"):
+ assert wanted in registered, (wanted, registered)
+
+ # Ctrl+F puts the cursor in the search box. The offscreen platform
+ # only grants real focus to one window per process, so check where
+ # focus was directed rather than whether the platform granted it.
+ window.input.setFocus()
+ app.processEvents()
+ window.focus_search()
+ app.processEvents()
+ assert window.focusWidget() is window.search_box, window.focusWidget()
+
+ # The search box lives in the top bar, so hiding the history panel
+ # must not take it away.
+ window.sidebar_button.setChecked(False)
+ app.processEvents()
+ assert not window.sidebar_visible()
+ assert window.search_box.isVisible()
+
+ # Its results render in the panel, so focusing reveals the panel.
+ window.focus_search()
+ app.processEvents()
+ assert window.sidebar_visible()
+
+ def press_escape() -> None:
+ window.keyPressEvent(
+ QKeyEvent(QEvent.KeyPress, Qt.Key_Escape, Qt.NoModifier)
+ )
+ app.processEvents()
+
+ # Escape backs out of the search box before hiding the window, so a
+ # stray press while filtering does not dismiss everything.
+ window.search_box.setFocus()
+ window.search_box.setText("otters")
+ app.processEvents()
+ assert window.focusWidget() is window.search_box
+ press_escape()
+ assert window.search_box.text() == ""
+ assert window.isVisible()
+ press_escape()
+ assert window.focusWidget() is window.input
+ assert window.isVisible()
+ press_escape()
+ assert not window.isVisible()
+
+ # Ctrl+N clears the conversation and puts the cursor in the input.
+ window.show()
+ window.raise_()
+ window.activateWindow()
+ # The offscreen platform only grants focus to the active window, and
+ # a window left over from an earlier check can still hold it.
+ window.setFocus()
+ app.processEvents()
+ session = history.create_session("chat", "m", "old")
+ history.add_message(session, "user", "something")
+ window.open_session(session)
+ assert window.session_id == session
+ window.search_box.setFocus()
+ window.new_session()
+ app.processEvents()
+ assert window.session_id is None
+ assert window.transcript.toPlainText().strip() == ""
+ assert window.focusWidget() is window.input
+
+ window.close()
+ history.close()
+ print("ok shortcuts")
+
+
def test_version_matches_changelog():
"""The package version must be the newest release in the changelog."""
import re
@@ -758,6 +865,7 @@ if __name__ == "__main__":
test_prompt_column_migration()
test_markdown_rendering()
test_sidebar_toggle()
+ test_shortcuts()
test_system_qt_theme_guard()
test_version_matches_changelog()
test_venv_discovery()