diff options
| author | historia <historiavg@proton.me> | 2026-08-26 19:12:11 -0400 |
|---|---|---|
| committer | historia <historiavg@proton.me> | 2026-08-26 19:12:11 -0400 |
| commit | 5af37bbd575eec89c6ac2fecf2d8c2eda4c1728d (patch) | |
| tree | 4f8fe9b79d339695ca83968e03301b853eb2e2ef /app/ui/tui.py | |
| parent | df95e7034df683c38fde67890430ab4c2abfa4ba (diff) | |
| download | tts-audiobook-generator-5af37bbd575eec89c6ac2fecf2d8c2eda4c1728d.tar.gz | |
fix: crash when menu of clone voices is empty
Diffstat (limited to 'app/ui/tui.py')
| -rw-r--r-- | app/ui/tui.py | 43 |
1 files changed, 30 insertions, 13 deletions
diff --git a/app/ui/tui.py b/app/ui/tui.py index e2118eb..56aa79e 100644 --- a/app/ui/tui.py +++ b/app/ui/tui.py @@ -909,6 +909,10 @@ def form(scr, title: str, fields: Sequence[dict], keep their value across hide/show. A field may set ``on_change`` to a callable of the field list, invoked whenever its value changes so dependent fields (choices, visibility, defaults) can be recomputed. + A choice field whose resolved list is empty cannot be opened: Enter + is a no-op, or flashes the field's optional ``on_empty_choices`` + message (string or callable of the field list) — an explanation the + submit-time ``validate`` can echo when an empty pick must be refused. An optional ``note`` string on a field renders as a dim, non-selectable line in a blank-line frame above that field's row — a @@ -1064,20 +1068,33 @@ def form(scr, title: str, fields: Sequence[dict], if callable(choices): choices = choices(fields) choices = list(choices) - if choices and isinstance(choices[0], (tuple, list)) \ - and len(choices[0]) == 2: - pairs = [(label, value) for label, value in choices] + if not choices: + # A dynamic choice list can legitimately come + # back empty (e.g. an audio.cpp model whose + # server hosts no clone voices). menu() would + # raise; explain instead when the field says + # how to fill the list. + message = field.get("on_empty_choices") + if callable(message): + message = message(fields) + if message: + frame.flash(str(message), "err") else: - pairs = [(c, c) for c in choices] - values = [value for _, value in pairs] - default = values.index(field["value"]) \ - if field["value"] in values else 0 - chosen = menu(scr, field["label"], pairs, - default_index=default, - back_value=edit_cancel) - if chosen is not edit_cancel: - field["value"] = chosen - run_on_change(field) + if isinstance(choices[0], (tuple, list)) \ + and len(choices[0]) == 2: + pairs = [(label, value) + for label, value in choices] + else: + pairs = [(c, c) for c in choices] + values = [value for _, value in pairs] + default = values.index(field["value"]) \ + if field["value"] in values else 0 + chosen = menu(scr, field["label"], pairs, + default_index=default, + back_value=edit_cancel) + if chosen is not edit_cancel: + field["value"] = chosen + run_on_change(field) elif field.get("kind") == "dir": start = field["value"] start = Path(start) if start else Path.cwd() |
