From 0dda9a5921eafd03853d233e077138ff72d64e4d Mon Sep 17 00:00:00 2001 From: historia Date: Fri, 28 Aug 2026 04:24:06 -0400 Subject: feat: .wav hints for all voice directory pickers in tui --- app/backends/audiocpp/wizard.py | 1 + app/backends/faster.py | 3 ++- app/tests/test_backends_audiocpp.py | 4 ++++ app/tests/test_backends_faster.py | 5 +++++ app/tests/test_hub.py | 3 +++ app/tests/test_tui.py | 21 +++++++++++++++++++++ app/ui/hub.py | 1 + app/ui/tui.py | 2 ++ 8 files changed, 39 insertions(+), 1 deletion(-) (limited to 'app') diff --git a/app/backends/audiocpp/wizard.py b/app/backends/audiocpp/wizard.py index 849ad5a..d85076a 100644 --- a/app/backends/audiocpp/wizard.py +++ b/app/backends/audiocpp/wizard.py @@ -464,6 +464,7 @@ def _wizard(stdscr, args: argparse.Namespace, parser: argparse.ArgumentParser wav_field = { "key": "wav_dir", "label": "Voice clone .wav directory", "kind": "dir", "value": Path(wav_start) if wav_start else None, + "info": common.wav_dir_info, "preview": common.wav_dir_preview, "visible": lambda fs: bool(s["include_clone"]), } fields.append(wav_field) diff --git a/app/backends/faster.py b/app/backends/faster.py index 3f279f8..285d865 100755 --- a/app/backends/faster.py +++ b/app/backends/faster.py @@ -217,7 +217,8 @@ def _wizard(stdscr, args: argparse.Namespace) -> Optional[dict]: fields: List[dict] = [ {"key": "wav_dir", "label": "Voices directory", "kind": "dir", "value": Path(args.input_dir) if args.input_dir is not None - else wav_start}, + else wav_start, + "info": common.wav_dir_info, "preview": common.wav_dir_preview}, {"key": "language", "label": "Language", "kind": "text", "value": default_language, "validate": lambda s: None if _try_language(s) diff --git a/app/tests/test_backends_audiocpp.py b/app/tests/test_backends_audiocpp.py index 4a8ee5f..95f8bec 100644 --- a/app/tests/test_backends_audiocpp.py +++ b/app/tests/test_backends_audiocpp.py @@ -2713,6 +2713,10 @@ class WizardNavigationTests(unittest.TestCase): by_key = {f["key"]: f for f in fields} self.assertEqual(by_key["wav_dir"]["value"], Path(recorded_voices)) + # The directory browser alerts on the .wavs it lists. + self.assertIs(by_key["wav_dir"]["info"], common.wav_dir_info) + self.assertIs(by_key["wav_dir"]["preview"], + common.wav_dir_preview) return {f["key"]: f["value"] for f in fields} with patch.object(make_server.build, "find_local_checkout", diff --git a/app/tests/test_backends_faster.py b/app/tests/test_backends_faster.py index 44f4907..2f21377 100644 --- a/app/tests/test_backends_faster.py +++ b/app/tests/test_backends_faster.py @@ -273,6 +273,11 @@ class WizardFormTests(unittest.TestCase): captured["title"] = title captured["keys"] = [f["key"] for f in fields] by_key = {f["key"]: f for f in fields} + # The directory browser alerts on the .wavs it lists. + self.assertIs(by_key["wav_dir"]["info"], + make_voices.common.wav_dir_info) + self.assertIs(by_key["wav_dir"]["preview"], + make_voices.common.wav_dir_preview) by_key["wav_dir"]["value"] = folder return {f["key"]: f["value"] for f in fields} diff --git a/app/tests/test_hub.py b/app/tests/test_hub.py index 0bdf2d5..ee1425d 100644 --- a/app/tests/test_hub.py +++ b/app/tests/test_hub.py @@ -1596,6 +1596,9 @@ class ConvertFlowTests(unittest.TestCase): mode_field["choices"]}, {13}) speaker_field = self._field("speaker") clone_dir_field = self._field("clone_dir") + # The .wav directory browser alerts on the .wavs it lists. + self.assertIs(clone_dir_field["info"], hub.common.wav_dir_info) + self.assertIs(clone_dir_field["preview"], hub.common.wav_dir_preview) clone_field = self._field("clone") design_field = self._field("qwen_instructions") # Speaker shows in custom mode; the .wav directory browser and diff --git a/app/tests/test_tui.py b/app/tests/test_tui.py index a0e0309..6fc3fd5 100644 --- a/app/tests/test_tui.py +++ b/app/tests/test_tui.py @@ -965,6 +965,27 @@ class FormTests(TuiTestCase): self.assertEqual(Path(mk_browser.call_args[1]["start"]), Path("/start")) + def test_dir_field_passes_info_and_preview_to_the_browser(self): + # A dir field may carry info/preview callbacks (e.g. the .wav + # count alerts); the form forwards them to the browser untouched. + def info(directory): + return ("info text", "ok") + + def preview(directory): + return ("preview text", "warn") + + picked = Path("/picked/voices") + fields = [{"key": "voices", "label": "Voices directory", + "kind": "dir", "value": Path("/start"), + "info": info, "preview": preview}] + with patch.object(tui, "browse_directory", + return_value=picked) as mk_browser: + screen = FakeScreen(keys=[10, 10]) + result = tui.form(screen, "Settings", fields) + self.assertEqual(result, {"voices": picked}) + self.assertIs(mk_browser.call_args[1]["info"], info) + self.assertIs(mk_browser.call_args[1]["preview"], preview) + def test_dir_pick_moves_focus_to_the_accept_button(self): # Accepting a directory is a completed choice: focus lands on # Save, so the very next Enter submits — no Tab hunting. diff --git a/app/ui/hub.py b/app/ui/hub.py index e17e862..8a6fda7 100644 --- a/app/ui/hub.py +++ b/app/ui/hub.py @@ -1428,6 +1428,7 @@ def _qwen_fields(remote_modes: Optional[list] = None, "visible": lambda fs: _field_value(fs, prefix + "mode") == "custom"}, {"key": prefix + "clone_dir", "label": "Clone .wav directory", "kind": "dir", "value": common.VOICES_DIR, + "info": common.wav_dir_info, "preview": common.wav_dir_preview, "on_change": reset_clone_wav, "visible": lambda fs: _field_value(fs, prefix + "mode") == "clone"}, {"key": prefix + "clone", "label": "Voice to clone", diff --git a/app/ui/tui.py b/app/ui/tui.py index e2216a3..b22a186 100644 --- a/app/ui/tui.py +++ b/app/ui/tui.py @@ -1207,6 +1207,8 @@ def form(scr, title: str, fields: Sequence[dict], picked = browse_directory( scr, field_label(field), start=start, validate=field.get("validate"), + info=field.get("info"), + preview=field.get("preview"), back_value=edit_cancel) if picked is not edit_cancel: field["value"] = picked -- cgit v1.2.3