Rebuild the interactive tui on Textual¶
The first forest tui drove its interactive loop with hand-rolled
termios/tty code: cbreak mode, manual arrow-key escape parsing, an
alternate-screen enter/restore dance, and a select-poll workaround for a
real footgun — polars installs a SIGINT handler with SA_RESTART at
import, so a blocking os.read never woke for Ctrl-C. That loop worked,
but every new capability (mouse selection, a scrollable drill-down,
resize handling) meant more bespoke terminal plumbing, and none of it was
testable without a pty.
The interactive experience now runs on Textual
(textual>=8, which keeps the project's Python 3.9 floor). The split that
made the first version safe is preserved: scenery.py keeps the
renderer-agnostic layers — gathering (local metadata only, never the
network) and the canvas renderer — and the Textual app in tui_app.py is
a thin shell around them. The scene widget draws the exact frames the
canvas produces (so forest tui --once and piped output stay
byte-identical and pipe-safe, with no Textual on that path), and the
renderer additionally reports clickable hit regions so mouse clicks map
to trees and canopy bands without duplicating drawing logic. Per-unit
drill-down detail is gathered lazily — only when a stage is opened —
keeping the scene overview cheap.
Consequences¶
textualandrichbecome runtime dependencies (rich is textual's own renderer, imported directly for ANSI-to-Text conversion); the dev extra gainspytest-asyncioto driveApp.run_test()/Pilot tests headlessly.- The termios loop and the polars-SIGINT poll workaround are deleted:
Textual's driver owns raw mode (Ctrl-C arrives as a key event, so the
SA_RESTARThandler is never in the read path) and restores the terminal even on crash. - Mouse selection, the stage drill-down screen, the bindings footer, and
the command palette come from Textual; keyboard behavior is unchanged
(arrows or
h/l/j/k,r,q/Esc). - The interactive path is exercised by real key/click tests in CI instead of being untestable pty-only code.