Redesign desktop workspace with compact task views and themes

This commit is contained in:
2026-09-09 14:12:23 +03:00
parent 1fa5343baa
commit e5e9ae66e8
27 changed files with 1466 additions and 109 deletions
+7 -3
View File
@@ -148,8 +148,8 @@ class Parser(unittest.TestCase):
def parse(self, *argv):
return cli.build_parser().parse_args(list(argv))
def test_no_verb_at_all_is_the_settings_window(self):
# argparse leaves the dest as None; run() is what turns it into "".
def test_no_verb_uses_the_plain_gui_command(self):
# argparse leaves the dest as None; run() selects home.
opts = self.parse()
self.assertIsNone(opts.verb)
self.assertEqual(opts.func, cli.cmd_plain)
@@ -700,10 +700,14 @@ class WithoutAnInstance(DikteTest):
launch.assert_called_once_with("toggle")
def test_every_verb_that_opens_a_window_can_start_it(self):
for verb in ("settings", "toggle", "ask", "meeting"):
for verb in ("home", "settings", "toggle", "ask", "meeting"):
with self.subTest(verb=verb):
self.assertTrue(self.run_verb([verb])[3].called)
def test_bare_command_opens_the_daily_workspace(self):
_, _, _, launch = self.run_verb([])
launch.assert_called_once_with("home")
def test_a_verb_asked_to_wait_starts_nothing(self):
"""There would be no run to wait for; the process would just be replaced."""
_, _, _, launch = self.run_verb(["toggle", "--wait"])
+369
View File
@@ -0,0 +1,369 @@
"""Native workspace navigation, capture boundaries and persisted results."""
from types import SimpleNamespace
from unittest import mock
from PyQt6.QtCore import Qt
from PyQt6.QtWidgets import QApplication, QMessageBox
from dikte import config as cfg, home_ui, i18n
from dikte.app import Dikte
from tests import test_ui
from tests.support import DikteTest
class Home(DikteTest):
def setUp(self):
super().setUp()
self.fixture = test_ui.Settings("runTest")
self.fixture.setUp()
self.addCleanup(self.fixture.doCleanups)
self.conf = self.fixture.config(transcribe_provider="openai", openai_api_key="test",
cleanup_enabled=False)
self.settings = self.fixture.window(self.conf)
self.controller = SimpleNamespace(
conf=self.conf, state="idle", ask_state="idle", meeting_state="idle",
meeting_message="", home_messages={}, recording=False, paused=False,
paste_override={}, open_settings=mock.Mock(), reset_conversation=mock.Mock(),
_recorded_seconds=lambda: 65, meeting_elapsed=SimpleNamespace(elapsed=lambda: 90000),
)
for name in ("start", "stop", "start_ask", "stop_ask", "_toggle_pause", "_cancel",
"cancel_ask", "_toggle_meeting", "cancel_meeting"):
setattr(self.controller, name, mock.Mock())
self.window = home_ui.HomeWindow(self.controller, self.settings)
self.addCleanup(self.window.deleteLater)
self.addCleanup(self.window.close)
self.window.show()
QApplication.processEvents()
def test_daily_tasks_are_reachable_outside_configuration(self):
for mode in ("dictation", "file", "meeting", "ask", "history"):
self.window.show_mode(mode)
QApplication.processEvents()
self.assertEqual(self.window.pages.currentWidget(), self.window.mode_pages[mode])
self.window.show_mode("meeting")
self.assertTrue(self.settings.minutes_view.isVisible())
self.window.show_mode("history")
self.assertTrue(self.settings.history.isVisible())
self.assertEqual(self.settings.tabs.count(), 7)
self.assertFalse(self.settings.tabs.tabBar().isVisible())
def test_native_chrome_and_capture_geometry(self):
self.assertFalse(self.window.windowFlags() & Qt.WindowType.FramelessWindowHint)
self.assertEqual(self.window.capture_button.width(), self.window.capture_button.height())
self.assertGreaterEqual(self.window.capture_button.height(), 100)
def test_theme_preview_discard_and_save_keep_runtime_separate(self):
self.assertEqual(self.conf["theme"], "nord")
self.settings.theme_choice.setCurrentIndex(self.settings.theme_choice.findData("light"))
self.assertEqual(self.conf["theme"], "nord")
self.assertIn("#FFFFFF", self.settings.styleSheet())
self.assertNotEqual(self.window.styleSheet(), self.settings.styleSheet())
self.settings._discard_changes()
self.assertEqual(self.settings.theme_choice.currentData(), "nord")
self.settings.theme_choice.setCurrentIndex(self.settings.theme_choice.findData("dracula"))
self.settings._save()
self.assertEqual(cfg.Config()["theme"], "dracula")
self.assertEqual(self.window._theme_name, "dracula")
self.assertIn("#282A36", self.window.styleSheet())
def test_theme_colors_are_per_overlay_and_unknown_name_falls_back(self):
from dikte import overlay, theme
dark = overlay.Overlay(theme_name="dark")
light = overlay.Overlay(theme_name="light")
self.addCleanup(dark.deleteLater)
self.addCleanup(light.deleteLater)
self.assertEqual(light.colors["base"].name(), "#ffffff")
self.assertEqual(dark.colors["base"].name(), "#101010")
dark.set_theme("dracula")
self.assertEqual(light.colors["base"].name(), "#ffffff")
self.assertEqual(theme.stylesheet("unknown"), theme.stylesheet("nord"))
def test_wide_meeting_page_keeps_actions_compact(self):
self.window.resize(900, 700)
self.window.show_mode("meeting")
QApplication.processEvents()
self.assertLess(self.window.meeting_button.width(), 300)
self.assertLessEqual(self.settings.minutes_view.width(), 680)
def test_empty_state_does_not_invent_a_transcript(self):
self.assertEqual(self.window.latest_text.toPlainText(), "")
self.assertFalse(self.window.copy_button.isEnabled())
self.assertFalse(self.window.open_button.isEnabled())
def test_clearing_history_removes_the_latest_preview(self):
cfg.append_history({"text": "Remove this preview"})
self.window.refresh_results()
self.window.show_mode("history")
with mock.patch.object(self.settings, "_confirm", return_value=True):
self.settings._clear_history()
self.window.show_mode("dictation")
self.assertEqual(self.window.latest_text.toPlainText(), "")
self.assertFalse(self.window.copy_button.isEnabled())
def test_missing_local_model_opens_setup_without_recording(self):
self.conf["transcribe_provider"] = "local"
self.window.refresh()
self.assertEqual(self.window.capture_status.text(), "Set up transcription")
self.window._capture()
self.controller.start.assert_not_called()
self.controller.open_settings.assert_called_once()
self.assertEqual(self.settings.tabs.currentIndex(), self.settings.api_tab_index)
self.assertEqual(self.controller.paste_override, {})
def test_button_capture_never_automatically_pastes(self):
def start():
self.assertIs(self.controller.paste_override["dictation"], False)
self.controller.state = "recording"
self.controller.recording = True
self.controller.start.side_effect = start
self.window._capture()
self.controller.start.assert_called_once()
self.assertFalse(self.controller.paste_override["dictation"])
self.assertIn("01:05", self.window.capture_status.text())
self.assertTrue(self.window.pause_button.isVisible())
self.window._capture()
self.controller.stop.assert_called_once()
def test_failed_capture_does_not_leak_a_paste_override(self):
self.window._capture()
self.assertEqual(self.controller.paste_override, {})
def test_busy_capture_can_queue_but_does_not_steal_assistant_microphone(self):
self.controller.state = "busy"
self.window.refresh()
self.assertTrue(self.window.capture_button.isEnabled())
self.controller.ask_state = "recording"
self.controller.recording = True
self.window.refresh()
self.assertFalse(self.window.capture_button.isEnabled())
self.window._capture()
self.controller.start.assert_not_called()
def test_pause_cancel_and_failures_are_visible(self):
self.controller.state = "recording"
self.controller.recording = True
self.controller.paused = True
self.window.refresh()
self.assertIn("Paused", self.window.capture_status.text())
self.window._pause()
self.controller._toggle_pause.assert_called_once()
self.window._cancel_capture()
self.controller._cancel.assert_called_once()
self.controller.home_messages["dictation"] = "Microphone permission denied"
self.window.refresh()
self.assertIn("permission denied", self.window.capture_error.text())
def test_real_latest_dictation_is_separate_from_assistant_answer(self):
cfg.append_history({"ts": "2026-09-09 12:00:00", "text": "Actual transcript"})
cfg.append_history({"mode": "ask", "text": "Actual answer"})
self.window.refresh_results()
self.assertEqual(self.window.latest_text.toPlainText(), "Actual transcript")
self.assertEqual(self.window.ask_output.toPlainText(), "Actual answer")
cursor = self.window.latest_text.textCursor()
cursor.setPosition(3)
self.window.latest_text.setTextCursor(cursor)
self.window.refresh()
self.window.refresh_results()
self.assertEqual(self.window.latest_text.textCursor().position(), 3)
cfg.clear_history()
self.window.refresh_results()
self.assertEqual(self.window.latest_text.toPlainText(), "")
def test_processing_summary_uses_full_models_and_actual_acceleration(self):
self.conf["transcribe_provider"] = "local"
self.conf["local_model"] = "ggml-large-v3-turbo-q5_0.bin"
self.conf["cleanup_enabled"] = True
self.conf["cleanup_provider"] = "local"
self.conf["local_llm_model"] = "gemma-3-4b-it-Q4_K_M.gguf"
self.conf["local_gpu"] = True
with mock.patch.object(home_ui.ggml, "state", return_value={
"whisper": {"running": True, "backend": "CPU"},
"llama": {"running": True, "backend": "Vulkan"},
}):
text = home_ui.processing_locations(self.conf)
self.assertIn("ggml-large-v3-turbo-q5_0.bin (Local CPU)", text)
self.assertIn("gemma-3-4b-it-Q4_K_M.gguf (Local GPU)", text)
self.assertNotIn("API", text)
with mock.patch.object(home_ui.ggml, "state", return_value={}):
text = home_ui.processing_locations(self.conf)
self.assertNotIn("GPU", text)
self.assertIn("(Local)", text)
self.assertIn(self.conf["meeting_model"], home_ui.processing_locations(self.conf, "meeting"))
self.conf["assistant_cleanup"] = True
self.conf["cleanup_provider"] = "gemini"
self.conf["assistant_provider"] = "codex"
text = home_ui.processing_locations(self.conf, "ask")
self.assertIn(self.conf["cleanup_gemini_model"] + " (API)", text)
self.assertIn("Codex default model (CLI)", text)
def test_timestamped_file_summary_uses_the_timestamp_model(self):
self.conf["transcribe_provider"] = "openrouter"
self.conf["openrouter_transcribe_model"] = "google/gemini-audio"
self.conf["openrouter_file_model"] = "openai/whisper-1"
text = home_ui.processing_locations(self.conf, "file", file_timestamps=True)
self.assertIn("openai/whisper-1 (API)", text)
self.assertNotIn("google/gemini-audio", text)
def test_meeting_summary_uses_segment_model_even_without_file_timestamps(self):
self.conf["transcribe_provider"] = "openai"
self.conf["transcribe_model"] = "gpt-4o-transcribe"
text = home_ui.processing_locations(self.conf, "meeting", file_timestamps=False)
self.assertIn("whisper-1 (API)", text)
self.assertNotIn("gpt-4o-transcribe", text)
self.conf["transcribe_provider"] = "openrouter"
self.conf["openrouter_file_model"] = "mistralai/voxtral-small-24b-2507"
text = home_ui.processing_locations(self.conf, "meeting", file_timestamps=False)
self.assertIn("mistralai/voxtral-small-24b-2507 (API)", text)
def test_unsupported_meeting_is_disabled(self):
self.enterContext(mock.patch.object(home_ui.audio, "sound", return_value=SimpleNamespace(meetings=False)))
self.window.show_mode("meeting")
self.assertFalse(self.window.meeting_button.isEnabled())
self.assertIn("not supported", self.window.meeting_hint.text())
self.window._meeting()
self.controller._toggle_meeting.assert_not_called()
def test_assistant_scope_uses_actual_shortcut_and_permissions(self):
self.conf["assistant_provider"] = "codex"
self.conf["assistant_shortcut"] = "Ctrl+Alt+A"
self.conf["assistant_dir"] = self.root
self.conf["assistant_codex_sandbox"] = "danger-full-access"
self.window.show_mode("ask")
self.assertIn("Ctrl+Alt+A", self.window.ask_scope.text())
self.assertIn(self.root, self.window.ask_scope.text())
self.assertIn("No sandbox at all", self.window.ask_scope.text())
self.assertEqual(self.window.ask_button.text(), "Set up assistant")
self.window._ask()
self.controller.start_ask.assert_not_called()
self.controller.open_settings.assert_called_once()
def test_failed_settings_save_keeps_runtime_config_and_form_edits(self):
before = dict(self.conf.data)
self.settings.auto_paste.setChecked(not self.conf["auto_paste"])
self.assertEqual(self.settings.dirty_label.text(), "Unsaved changes")
with mock.patch.object(self.conf, "save", side_effect=OSError("disk full")), mock.patch.object(QMessageBox, "warning"):
self.settings._save()
self.assertEqual(self.conf.data, before)
self.assertNotEqual(self.settings.auto_paste.isChecked(), self.conf["auto_paste"])
self.settings.file_path = "/tmp/chosen.wav"
self.settings._discard_changes()
self.assertEqual(self.settings.dirty_label.text(), "")
self.assertEqual(self.settings.file_path, "/tmp/chosen.wav")
def test_small_window_keeps_navigation_and_footer_accessible(self):
self.window.resize(460, 460)
QApplication.processEvents()
for mode in ("dictation", "file", "meeting", "ask", "history"):
self.window.show_mode(mode)
QApplication.processEvents()
if self.window.footer.isVisible():
self.assertTrue(self.window.rect().contains(self.window.footer.geometry()))
self.assertTrue(self.window.rect().contains(self.window.mode_buttons["dictation"].geometry().topLeft()))
def test_apply_merges_unrelated_cli_changes_and_keeps_user_edits(self):
self.settings.auto_paste.setChecked(False)
self.conf["shortcut"] = "Ctrl+Shift+F9"
self.conf["groq_transcribe_model"] = "external-model"
self.settings.refresh_configuration()
self.assertFalse(self.settings.auto_paste.isChecked())
self.settings._save()
self.assertFalse(self.conf["auto_paste"])
self.assertEqual(self.conf["shortcut"], "Ctrl+Shift+F9")
self.assertEqual(self.conf["groq_transcribe_model"], "external-model")
self.assertEqual(self.settings._shortcut_rows["toggle"][0].currentText(), "Ctrl+Shift+F9")
self.assertEqual(self.settings.dirty_label.text(), "")
def test_clean_form_refreshes_from_cli_without_changing_file_result(self):
self.settings.file_output.setPlainText("Existing file result")
self.conf["shortcut"] = "Ctrl+Alt+F9"
self.settings.refresh_configuration()
self.assertEqual(self.settings._shortcut_rows["toggle"][0].currentText(), "Ctrl+Alt+F9")
self.assertEqual(self.settings.file_output.toPlainText(), "Existing file result")
self.assertEqual(self.settings.dirty_label.text(), "")
def test_cached_provider_model_edits_remain_dirty_after_switching_back(self):
self.settings._select_data(self.settings.transcribe_provider, "groq")
self.settings.transcribe_model.setCurrentText("my-groq-model")
self.settings._select_data(self.settings.transcribe_provider, "openai")
self.assertEqual(self.settings.dirty_label.text(), "Unsaved changes")
self.settings._save()
self.assertEqual(self.conf["groq_transcribe_model"], "my-groq-model")
def test_cli_selected_new_source_is_resolved_when_settings_reopens(self):
self.conf["mic_target"] = "new-usb"
self.settings.refresh_configuration()
with mock.patch.object(home_ui.audio, "list_sources", return_value=[("new-usb", "New USB microphone")]), mock.patch.object(home_ui.audio, "list_monitors", return_value=[]):
self.settings.refresh_sources()
self.assertEqual(self.settings.mic.currentData(), "new-usb")
self.assertEqual(self.settings.mic.currentText(), "New USB microphone")
self.settings._save()
self.assertEqual(self.conf["mic_target"], "new-usb")
def test_language_rebuild_preserves_file_result_and_active_mode(self):
from dikte.meeting import MeetingPipeline
controller = Dikte.__new__(Dikte)
controller.__dict__.update(vars(self.controller))
controller.meetings = MeetingPipeline(self.conf)
controller._make_settings()
old_settings = controller.settings_window
old_home = home_ui.HomeWindow(controller, old_settings)
controller.home_window = old_home
old_home.show_mode("file")
old_home.show()
old_settings.file_path = "/tmp/chosen.wav"
old_settings.file_label.setText("chosen.wav")
old_settings.file_output.setPlainText("Retained transcript")
old_settings.file_segments = [{"text": "Retained transcript", "start": 0, "end": 2}]
i18n.set_language("tr")
controller._reopen_settings()
self.addCleanup(controller.settings_window.deleteLater)
self.addCleanup(controller.settings_window.close)
self.addCleanup(controller.home_window.deleteLater)
self.addCleanup(controller.home_window.close)
self.assertEqual(controller.home_window.mode, "file")
self.assertEqual(controller.settings_window.file_path, "/tmp/chosen.wav")
self.assertEqual(controller.settings_window.file_output.toPlainText(), "Retained transcript")
self.assertTrue(controller.settings_window.file_save_srt.isEnabled())
self.assertEqual(controller.home_window.mode_buttons["file"].text(), "Dosya")
def test_disconnected_configured_source_survives_an_unrelated_apply(self):
self.conf["mic_target"] = "disconnected-usb"
self.settings.refresh_configuration()
self.assertEqual(self.settings.mic.currentData(), "disconnected-usb")
self.settings.auto_paste.setChecked(False)
self.settings._save()
self.assertEqual(self.conf["mic_target"], "disconnected-usb")
def test_source_refresh_keeps_selection_and_discovers_hotplugged_devices(self):
self.settings.mic.addItem("Old microphone", "old")
self.settings.mic.setCurrentIndex(self.settings.mic.findData("old"))
with mock.patch.object(home_ui.audio, "list_sources", return_value=[("usb", "USB microphone")]), mock.patch.object(home_ui.audio, "list_monitors", return_value=[("loop", "Loopback")]):
self.settings.refresh_sources()
self.assertEqual(self.settings.mic.currentData(), "old")
self.assertGreaterEqual(self.settings.mic.findData("usb"), 0)
self.assertGreaterEqual(self.settings.meeting_mic.findData("usb"), 0)
self.assertGreaterEqual(self.settings.meeting_system.findData("loop"), 0)
def test_missing_assistant_directory_displays_the_actual_fallback(self):
self.conf["assistant_provider"] = "codex"
self.conf["assistant_dir"] = "/does/not/exist/dikte-test"
self.window.show_mode("ask")
self.assertNotIn(self.conf["assistant_dir"], self.window.ask_scope.text())
self.assertIn(home_ui.assistant.working_dir(self.conf), self.window.ask_scope.text())
def test_turkish_task_labels_and_runtime_status(self):
i18n.set_language("tr")
self.window.refresh()
self.assertEqual(self.window.capture_status.text(), "Konuşmaya hazır")
self.assertIn("Ses:", self.window.capture_models.text())
def test_completed_run_refreshes_workspace_without_changing_controller_state(self):
controller = Dikte.__new__(Dikte)
controller.home_messages = {}
controller.home_window = self.window
controller._waiters = {}
cfg.append_history({"text": "Finished"})
controller._settle("dictation", {"ok": True, "text": "Finished"})
self.assertEqual(self.window.latest_text.toPlainText(), "Finished")
self.assertEqual(controller.home_messages["dictation"], "Transcript ready")
+32 -11
View File
@@ -179,10 +179,11 @@ class Settings(DikteTest):
Qt.KeyboardModifier.NoModifier,
Qt.ScrollPhase.NoScrollPhase, False)
def test_the_window_opens_with_every_tab_on_it(self):
def test_settings_keeps_configuration_and_exposes_separate_task_pages(self):
window = self.window(cfg.Config())
tabs = window.findChildren(settings_ui.QTabWidget)[0]
self.assertEqual(tabs.count(), 10)
self.assertEqual(tabs.count(), 7)
self.assertEqual(set(window.task_pages), {"file", "minutes", "history"})
self.assertEqual(window.windowTitle(), "Dikte Settings")
def test_no_tab_can_stretch_the_window_past_a_small_screen(self):
@@ -1198,7 +1199,7 @@ class Overlay(DikteTest):
mock.patch.object(QApplication, "screenAt") as screen_at:
widget._reposition()
screen_at.assert_not_called()
self.assertEqual(widget.pos(), QPoint(1948, 995))
self.assertEqual(widget.pos(), QPoint(1948, 1003))
def _screen(self, name, area):
screen = mock.Mock()
@@ -1224,7 +1225,7 @@ class Overlay(DikteTest):
mock.patch.object(QApplication, "screenAt") as screen_at:
widget._reposition()
screen_at.assert_not_called()
self.assertEqual(widget.pos(), QPoint(1948, 995))
self.assertEqual(widget.pos(), QPoint(1948, 1003))
def test_the_pointer_decides_when_the_compositor_will_not_say(self):
"""Every desktop but Plasma, and Plasma while KWin is being replaced."""
@@ -1236,7 +1237,7 @@ class Overlay(DikteTest):
return_value=screens[0]) as screen_at:
widget._reposition()
screen_at.assert_called()
self.assertEqual(widget.pos(), QPoint(28, 995))
self.assertEqual(widget.pos(), QPoint(28, 1003))
def _two_screens(self):
return [self._screen("DP-1", settings_ui.QRect(0, 0, 1920, 1080)),
@@ -1258,10 +1259,10 @@ class Overlay(DikteTest):
with mock.patch.object(overlay_module, "_kwin", kwin), \
mock.patch.object(QApplication, "screens", return_value=screens):
widget.show_recording()
self.assertEqual(widget.pos(), QPoint(1948, 995))
self.assertEqual(widget.pos(), QPoint(1948, 1003))
kwin.call.return_value.arguments.return_value = ["DP-1"]
self._ticks_on(widget, screens, kwin)
self.assertEqual(widget.pos(), QPoint(28, 995))
self.assertEqual(widget.pos(), QPoint(28, 1003))
def test_it_stays_where_it_appeared_unless_it_was_told_otherwise(self):
"""Left off, because an indicator that jumps desks mid-sentence is one
@@ -1274,7 +1275,7 @@ class Overlay(DikteTest):
widget.show_recording()
kwin.call.return_value.arguments.return_value = ["DP-1"]
self._ticks_on(widget, screens, kwin)
self.assertEqual(widget.pos(), QPoint(1948, 995))
self.assertEqual(widget.pos(), QPoint(1948, 1003))
def test_a_named_screen_is_never_left_for_the_pointer(self):
"""Naming one is the whole answer; following it would undo the naming."""
@@ -1285,7 +1286,7 @@ class Overlay(DikteTest):
widget.show_recording()
self._ticks_on(widget, screens, kwin)
kwin.call.assert_not_called()
self.assertEqual(widget.pos(), QPoint(28, 995))
self.assertEqual(widget.pos(), QPoint(28, 1003))
def test_the_one_on_top_goes_where_the_one_underneath_is(self):
"""Asking for itself would put the pair on two monitors, with this one
@@ -1299,8 +1300,8 @@ class Overlay(DikteTest):
kwin.call.return_value.arguments.return_value = ["DP-1"]
second = self.overlay(below=first)
second.show_busy("Asking Claude…")
self.assertEqual(first.pos(), QPoint(1948, 995))
self.assertEqual(second.pos(), QPoint(1948, 929))
self.assertEqual(first.pos(), QPoint(1948, 1003))
self.assertEqual(second.pos(), QPoint(1948, 945))
def test_the_compositor_is_asked_only_now_and_then(self):
"""Every tick would be thirty conversations a second about a hand
@@ -1404,6 +1405,7 @@ class LocalModels(DikteTest):
def setUp(self):
super().setUp()
self.enterContext(mock.patch.object(settings_ui.LocalModelBox, "_fetch_models"))
# A machine Dikte is actually installed on would otherwise answer the
# "nothing can transcribe" question from its real binary and model.
self.patch_attr(ggml, "BIN_DIR", self.path("bin"))
@@ -1687,6 +1689,25 @@ class LocalModels(DikteTest):
time.sleep(0.05)
_app.processEvents()
self.assertEqual(fetch.call_count, 1)
def test_reloading_settings_keeps_the_fetched_model_choices(self):
repo = "ggml-org/SmolLM3-3B-GGUF"
box = self.window(self.config(local_llm_repo=repo)).local_llm
box._on_listed([("models", [self._item("first.gguf"), self._item("second.gguf")], repo)], "")
box.load("first.gguf", repo)
self.assertGreaterEqual(box.model.findData("second.gguf"), 0)
self.assertEqual(box.selected(), "first.gguf")
def test_reload_before_repository_debounce_does_not_reuse_previous_catalog(self):
first_repo = "ggml-org/SmolLM3-3B-GGUF"
second_repo = "ggml-org/gemma-3-4b-it-GGUF"
box = self.window(self.config(local_llm_repo=first_repo)).local_llm
box._on_listed([("models", [self._item("first.gguf"), self._item("second.gguf")], first_repo)], "")
box.repo.setCurrentText(second_repo)
box.load("first.gguf", second_repo)
self.assertLess(box.model.findData("second.gguf"), 0)
self.assertTrue(box._pending)
self.assertFalse(box._answered)
def test_the_models_are_grouped_by_the_model_rather_than_by_size(self):
# Sorted by size alone, the turbo files land between the two medium
# ones, half a screen from the model they are a copy of.