"""Shared entry-point plumbing for the backend setup wizards. Every backend module exposes the same surface — ``_wizard`` (TUI screens), ``_execute_steps``/``_execute`` (the work, as task-view steps), ``setup_screen`` (hub-embedded flow), ``run_tui`` (standalone curses flow), ``main`` (CLI) — and this module holds the parts of that surface that are identical everywhere. The backend keeps the thin named wrappers so its public names (and test seams) stay on the backend module. """ import sys from ui import taskview, tui def interactive() -> bool: """True when the TUI wizard can run (curses importable + tty).""" try: import curses # noqa: F401 except ImportError: return False try: return sys.stdin.isatty() and sys.stdout.isatty() except (AttributeError, ValueError): return False def screen_flow(stdscr, *, wizard, steps_of, title, parser_factory) -> int: """Run the setup wizard on an existing curses screen (the hub's). The hub drives this as one screen of its own ``tui.Wizard`` stack, so Esc on the wizard's first screen simply returns here and the hub pops back to the menu that launched it. The setup tail runs inside the TUI task view on this same screen, so the hub's curses session stays intact and the user sees per-step status instead of being dropped to the console. Returns 0 on completion, 1 when the user aborted. WIZARD is ``(stdscr, args) -> settings | None``; STEPS_OF turns the settings into task-view steps; PARSER_FACTORY builds the argparse parser whose empty namespace seeds the wizard. """ args = parser_factory().parse_args([]) settings = wizard(stdscr, args) if settings is None: return 1 return taskview.run_steps(stdscr, title, steps_of(settings)) def tui_flow(wizard, execute, *, args, execute_takes_args=False, aborted_message="[INFO] Aborted") -> int: """Run the setup wizard end-to-end in its own curses session. WIZARD is ``(args) -> settings | None`` via ``curses.wrapper``; EXECUTE performs the settings (called with ARGS too when EXECUTE_TAKES_ARGS). Restores the text cursor afterwards and reports cancellation/abort consistently. Returns the process exit code. """ import curses try: settings = curses.wrapper(wizard, args) except tui.WizardCancelled: print("\n[INFO] Cancelled; nothing was written") return 1 try: curses.curs_set(1) except curses.error: pass if settings is None: print(aborted_message) return 1 return execute(settings) if not execute_takes_args \ else execute(settings, args)