From eb465fb2a972b6b07c4b427f940b186745ce6d90 Mon Sep 17 00:00:00 2001 From: historia Date: Thu, 27 Aug 2026 22:40:58 -0400 Subject: fix: can't check checkboxes in tts model page --- app/tests/test_hub.py | 22 +++++++++++++--------- app/tests/test_tui.py | 28 ++++++++++++++++++++++++++++ app/ui/hub.py | 18 ++++++++++-------- app/ui/tui.py | 10 +++++----- 4 files changed, 56 insertions(+), 22 deletions(-) (limited to 'app') diff --git a/app/tests/test_hub.py b/app/tests/test_hub.py index daa4abd..fbede75 100644 --- a/app/tests/test_hub.py +++ b/app/tests/test_hub.py @@ -364,20 +364,24 @@ class HubMenuTests(unittest.TestCase): self.assertIn("qwen3_tts_1_7b_customvoice_q8_0", text) self.assertIn("5. Go to Generate Audiobooks.", text) # The "no need to manually start/stop" sentence sits on its own - # indented line below the step-5 paragraph. - self.assertIn("stop it.\nThere is no need to manually " + # indented line below the step-5 paragraph (one leading space + # beyond the 2-space indent, lining up with the step text). + self.assertIn("stop it.\n There is no need to manually " "start/stop servers.", text) self.assertIn("6. Generated audiobooks (m4b, mp3, etc.) will " "output here:", text) # The three folder paths are their own indented, white-bold - # ("input") rows; the numbered steps start at the margin. They - # resolve live from the converter module, so a Settings change - # this session is reflected. - self.assertIn(([(str(hub.converter_mod.BOOKS_FOLDER), "input")], 1), + # ("input") rows; the numbered steps start at the margin and + # the indented rows carry a leading space of their own, so + # they line up with the step text after the "N. " prefixes. + # They resolve live from the converter module, so a Settings + # change this session is reflected. + self.assertIn(([(" " + str(hub.converter_mod.BOOKS_FOLDER), + "input")], 1), items) + self.assertIn(([(" " + str(hub.common.VOICES_DIR), "input")], 1), items) - self.assertIn(([(str(hub.common.VOICES_DIR), "input")], 1), items) - self.assertIn(([(str(hub.converter_mod.AUDIOBOOKS_FOLDER), "input")], - 1), items) + self.assertIn(([(" " + str(hub.converter_mod.AUDIOBOOKS_FOLDER), + "input")], 1), items) self.assertEqual(items[0][1], 0) diff --git a/app/tests/test_tui.py b/app/tests/test_tui.py index e12da76..e765e60 100644 --- a/app/tests/test_tui.py +++ b/app/tests/test_tui.py @@ -1147,6 +1147,34 @@ class CheckboxTreeTests(TuiTestCase): picked = tui.checkbox_tree(screen, "Pick models", self.FAMILIES) self.assertEqual(picked, [(0, "pkg-a")]) + def test_space_toggles_option_row(self): + # Enter on the family checks pkg-a and expands; Down twice lands + # on the pkg-b option row; Space checks it; confirm returns both. + screen = FakeScreen(keys=[10, FakeCurses.KEY_DOWN, + FakeCurses.KEY_DOWN, ord(" "), 9, 10]) + picked = tui.checkbox_tree(screen, "Pick models", self.FAMILIES) + self.assertEqual(picked, [(0, "pkg-a"), (0, "pkg-b")]) + + def test_enter_toggles_option_row(self): + # Enter on the family checks pkg-a and expands; Down lands on the + # pkg-a option row; Enter there unchecks it; Down to pkg-b, Space + # checks it. Confirm returns only pkg-b. + screen = FakeScreen(keys=[10, FakeCurses.KEY_DOWN, 10, + FakeCurses.KEY_DOWN, ord(" "), 9, 10]) + picked = tui.checkbox_tree(screen, "Pick models", self.FAMILIES) + self.assertEqual(picked, [(0, "pkg-b")]) + + def test_unhandled_key_on_row_is_ignored(self): + # A key that is neither motion nor toggle (here "x") must not + # toggle anything or crash on either row kind; Esc then aborts. + marker = object() + screen = FakeScreen(keys=[ord("x"), FakeCurses.KEY_DOWN, ord("x"), + 27]) + self.assertIs( + tui.checkbox_tree(screen, "Pick models", self.FAMILIES, + back_value=marker), + marker) + def test_prechecked_selection_confirmed(self): # checked= seeds the tree (modify flow): Tab then Confirm accepts # the pre-checked options without any extra key presses. diff --git a/app/ui/hub.py b/app/ui/hub.py index 68417f7..4fae67b 100644 --- a/app/ui/hub.py +++ b/app/ui/hub.py @@ -834,7 +834,9 @@ def _help_lines() -> list: Items are text_viewer rows: "" (a blank line) or a (segments, indent) pair — SEGMENTS are (text, kind) with KIND a theme key (None = body). Numbered steps start at the margin; every other - line is indented two spaces so it reads as part of its step. The + line is indented three spaces — Frame's two-space indent unit + plus a leading space in the row's first segment — so it lines up + with the step text after the "N. " prefixes. The input/output folders are read from the converter module at call time, so a Settings change this session is reflected without a restart. @@ -842,11 +844,11 @@ def _help_lines() -> list: return [ ([("1. ", "title"), ("Put your ebooks (epub, txt, or pdf) here:", None)], 0), - ([(str(converter_mod.BOOKS_FOLDER), "input")], 1), + ([(" " + str(converter_mod.BOOKS_FOLDER), "input")], 1), "", ([("2. ", "title"), ("Put any .wavs of voices to clone here:", None)], 0), - ([(str(common.VOICES_DIR), "input")], 1), + ([(" " + str(common.VOICES_DIR), "input")], 1), "", ([("3. ", "title"), ("If no backend is installed, go to ", None), @@ -858,24 +860,24 @@ def _help_lines() -> list: ("Select TTS models to install. If you're unsure, try " "these qwen3-tts models:", None)], 0), "", - ([("Voice cloning:", None), (" ", None), + ([(" Voice cloning:", None), (" ", None), ("qwen3_tts_1_7b_base_q8_0", "ok")], 1), - ([("Built-in-voice:", None), (" ", None), + ([(" Built-in-voice:", None), (" ", None), ("qwen3_tts_1_7b_customvoice_q8_0", "ok")], 1), "", - ([("It will take a while to build audio.cpp and download " + ([(" It will take a while to build audio.cpp and download " "the model files.", None)], 1), "", ([("5. ", "title"), ("Go to ", None), ("Generate Audiobooks", "accent"), (". It will automatically start the necessary server, " "generate the books, and stop it.", None)], 0), - ([("There is no need to manually start/stop servers.", None)], 1), + ([(" There is no need to manually start/stop servers.", None)], 1), "", ([("6. ", "title"), ("Generated audiobooks (m4b, mp3, etc.) will output here:", None)], 0), - ([(str(converter_mod.AUDIOBOOKS_FOLDER), "input")], 1), + ([(" " + str(converter_mod.AUDIOBOOKS_FOLDER), "input")], 1), ] diff --git a/app/ui/tui.py b/app/ui/tui.py index 1997415..7da1dd1 100644 --- a/app/ui/tui.py +++ b/app/ui/tui.py @@ -1565,12 +1565,12 @@ def checkbox_tree(scr, title: str, families: List[dict], if options: checked.add((index, options[0]["key"])) expanded.add(index) - else: - _, index, option_key = node - if (index, option_key) in checked: - checked.discard((index, option_key)) else: - checked.add((index, option_key)) + _, index, option_key = node + if (index, option_key) in checked: + checked.discard((index, option_key)) + else: + checked.add((index, option_key)) # --------------------------------------------------------------------------- -- cgit v1.2.3