Skip to content

Feature: quality-of-life additions for agent/LLM TUIs (streaming log, TabbedContent events, palette commands, selection modal) #6717

Closed

Description

@thunderballfists

Summary

A few small quality-of-life additions that came up building an agent-management TUI (master-detail, streaming output, tabbed detail views, a command palette, and modal pickers). Each has a working local workaround — these are ergonomics, not blockers. Grouped into one issue; happy to split into separate issues if you'd prefer.


1. Streaming-friendly transcript in RichLog (grow-then-commit a line)

Use case: streaming an agent/LLM response token-by-token into a log, interleaved with styled status lines (e.g. ● calling weather_lookup, ✓ done).

Gap: RichLog is line-oriented; appending deltas to the same logical line means buffering and re-rendering, or one-token-per-line.

Wish: an append_inline(text) / RichLog.stream() that grows the current line until commit_line(), with auto-scroll that pauses when the user scrolls up and resumes at the bottom (plus a reactive for "user has scrolled away" so apps can show a "jump to latest" affordance). This pattern is increasingly common with agent/LLM TUIs.

Workaround: keep the in-progress text in a reactive str rendered in a Static below a RichLog of committed events; commit on done. Two widgets doing one job.


2. TabbedContent: public activate-by-id + a "first shown" event

Use case: sub-tabs switchable by click, by single-key accelerators from another focused widget, and by the command palette — and lazy-loading a tab's data the first time it's shown (not on every revisit).

Gap: activating a specific tab from a global binding is manual, and there's no event distinguishing "shown for the first time" from "activated" for lazy-loading.

Wish: a documented public activate(id), an on_tab_first_shown / TabPane.first_shown message, and optionally an accelerator= per TabPane that auto-registers a binding.

Workaround: a local active/loaded set + helper bindings. Just boilerplate.


3. Lightweight dynamic palette commands without a full Provider

Use case: a handful of state-dependent palette entries (e.g. "switch account" only when >1 account).

Gap: authoring a full CommandProvider (async search yielding Hits) is heavier than needed for a few simple, conditional actions.

Wish: a decorator/registration for simple commands, e.g. @command("switch account", when=lambda app: len(app.accounts) > 1), that the default palette surfaces — the 80% case without a Provider.

Workaround: write the Provider; not hard, just verbose.


4. A selection-modal helper (SelectionScreen[T])

Use case: "pick one of N" modals — choose an account, a backend, a release environment — with arrow/number select, enter to confirm, esc to cancel (when allowed).

Gap: ModalScreen + OptionList works, but every app re-implements the same select-one-and-return-a-value modal.

Wish: a SelectionScreen[T] helper you push and await for the dismissed value, with number accelerators and a configurable cancel policy — the modal analog of Select.

Workaround: a ~30-line reusable ModalScreen[str] around OptionList.


Context: these surfaced building dtn --tui, a local terminal console for managing agents, on Textual 8.2.8. None block us; filing per your "let us know what would help" spirit.

Activity

  1. github-actions commented on Sep 18, 2026

    @github-actions

    We found the following entries in the FAQ which you may find helpful:

    Feel free to close this issue if you found an answer in the FAQ. Otherwise, please give us a little time to review.

    This project is developed and maintained by Will McGugan. Consider sponsoring Will's work on this project (and others).

    This is an automated reply, generated by FAQtory

  2. thunderballfists commented on Sep 18, 2026

    @thunderballfists
    Author

    Retracting — filed in error; will track this in our own project instead. Apologies for the noise.

  3. github-actions commented on Sep 18, 2026

    @github-actions

    I hope I solved your issue!

    Consider sponsoring my work to continue to maintain and develop Textual.

    • Will McGugan
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions