aboutsummaryrefslogtreecommitdiff
path: root/app/ui
diff options
context:
space:
mode:
authorhistoria <historiavg@proton.me>2026-08-25 19:03:09 -0400
committerhistoria <historiavg@proton.me>2026-08-25 19:03:09 -0400
commit8c9a782dfe94525dc5f0893c98fd19543648264b (patch)
treefdff70d967ef06f0da560ca284842427cbe647a8 /app/ui
parent64e9940d43fad1bc5908b39673b03e4fdc1e8f2f (diff)
downloadtts-audiobook-generator-8c9a782dfe94525dc5f0893c98fd19543648264b.tar.gz
fix: tui errors wait for getch()
Diffstat (limited to 'app/ui')
-rw-r--r--app/ui/hub.py26
-rw-r--r--app/ui/runview.py3
-rw-r--r--app/ui/taskview.py6
-rw-r--r--app/ui/tui.py32
4 files changed, 48 insertions, 19 deletions
diff --git a/app/ui/hub.py b/app/ui/hub.py
index d20b28e..c1f5dce 100644
--- a/app/ui/hub.py
+++ b/app/ui/hub.py
@@ -237,20 +237,32 @@ class _Hub:
back to the menu that launched it. A crash flashes and does the same.
"""
def screen():
- try:
- info.setup_screen(self.stdscr)
- except tui.WizardCancelled:
- pass
- except Exception as exc: # noqa: BLE001 - keep the hub alive
- tui.flash(self.stdscr, str(exc), "err")
+ self._run_setup(info)
return tui.Wizard.BACK
return screen
+ def _run_setup(self, info) -> None:
+ """Run one backend's setup wizard on this session (no stack frame)."""
+ try:
+ info.setup_screen(self.stdscr)
+ except tui.WizardCancelled:
+ pass
+ except Exception as exc: # noqa: BLE001 - keep the hub alive
+ tui.flash(self.stdscr, str(exc), "err")
+
def screen_install(self):
+ """Pick a backend to install and run its setup inline.
+
+ The setup is not pushed as a stack frame: when it finishes this
+ screen returns BACK, popping straight past the picker to the
+ Configure menu, whose status table re-detects the new install.
+ Esc on the picker still pops back one screen normally.
+ """
info = self._pick_backend(installed_only=False)
if info is None:
return tui.Wizard.BACK
- return self.screen_setup(info)
+ self._run_setup(info)
+ return tui.Wizard.BACK
def screen_uninstall(self):
"""Pick a backend, confirm, then uninstall it inside the task view.
diff --git a/app/ui/runview.py b/app/ui/runview.py
index f016922..d49d949 100644
--- a/app/ui/runview.py
+++ b/app/ui/runview.py
@@ -301,6 +301,9 @@ class RunView:
finally:
self._monitor_stop.set()
self._cancel.set()
+ # Leave the screen blocking again: the timed redraw getch must
+ # not make later hub dialogs (e.g. tui.flash) dismiss themselves.
+ self._blocking()
def _get_key(self) -> Optional[int]:
"""One key from the screen (None on the redraw timeout)."""
diff --git a/app/ui/taskview.py b/app/ui/taskview.py
index c4fd35e..6708304 100644
--- a/app/ui/taskview.py
+++ b/app/ui/taskview.py
@@ -349,6 +349,9 @@ class TaskView:
return self._result_rc()
finally:
self._cancel.set()
+ # Leave the screen blocking again: the timed redraw getch must
+ # not make later hub dialogs (e.g. tui.flash) dismiss themselves.
+ self._blocking()
def _result_rc(self) -> int:
"""The exit code for the whole run (cancelled counts as failure)."""
@@ -860,6 +863,9 @@ class LanesView:
return self._result_rc()
finally:
self._cancel.set()
+ # Leave the screen blocking again: the timed redraw getch
+ # must not make later hub dialogs dismiss themselves.
+ self._blocking()
finally:
sys.stdout, sys.stderr = saved_out, saved_err
diff --git a/app/ui/tui.py b/app/ui/tui.py
index d675946..b1b18f1 100644
--- a/app/ui/tui.py
+++ b/app/ui/tui.py
@@ -123,17 +123,22 @@ def flash(scr, text: str, kind: str = "warn") -> None:
Used by the hub for "not set up yet"-style messages. KIND is a theme
key (warn/err/ok/info). The notice itself is the dialog's only
content (no "Notice" heading); Esc dismisses it (it does not abort).
+ A timed-out getch (-1; the screen may still be in a redraw cadence)
+ is ignored so the notice really waits for a key.
"""
frame = Frame(scr, "", "Press any key to continue Esc = back")
frame.mark(text, frame.theme.get(kind, frame.theme["body"]))
frame.cursor = None
frame.draw()
- try:
- key = scr.getch()
- except KeyboardInterrupt:
- raise WizardCancelled() from None
- if key == 3: # Ctrl-C still aborts
- raise WizardCancelled()
+ while True:
+ try:
+ key = scr.getch()
+ except KeyboardInterrupt:
+ raise WizardCancelled() from None
+ if key == 3: # Ctrl-C still aborts
+ raise WizardCancelled()
+ if key != -1:
+ return
# On screens without typed text, Esc and 'q' mean the same thing: go
@@ -581,12 +586,15 @@ class Frame:
"""Show TEXT on the status line until any key is pressed."""
self.status = (text, kind)
self.draw()
- try:
- key = self.scr.getch()
- if key == 3: # Ctrl-C still aborts
- raise WizardCancelled()
- except KeyboardInterrupt:
- raise WizardCancelled() from None
+ while True:
+ try:
+ key = self.scr.getch()
+ if key == 3: # Ctrl-C still aborts
+ raise WizardCancelled()
+ except KeyboardInterrupt:
+ raise WizardCancelled() from None
+ if key != -1:
+ break
self.status = None
def edit_status(self, prompt: str = "") -> Optional[str]: