Skip to content

Consider replacing the custom customizer TUI with symfony/tui #1

Description

@AlexSkrypnyk

We build the customizer's interactive UI with a custom panel-based TUI (.vortex/customizer - Tui/ + Widget/ + Input/). This issue tracks whether we should consider replacing it with symfony/tui.

Current assessment: not yet. Its widget set overlaps ours well (SelectList, Input, Editor, SettingsList, focus/keybinding traits), but it is experimental (minimum-stability: dev, no BC promise), needs Symfony 8 (unreleased) + revolt/event-loop, raises the PHP floor to 8.4.1, and its event-loop model conflicts with our synchronous, headless-testable TUI (the basis of our 100% test coverage). It would also only replace the rendering layer, not the engine. Revisit when it leaves experimental status and Symfony 8 ships.

Full assessment

What symfony/tui is

A terminal-UI framework by Fabien Potencier / the Symfony community. Packages: Ansi, Style, Terminal, Input, Focus, Event, Loop, Render, Widget. It is explicitly experimental (README: "This Component is experimental", not covered by Symfony's BC promise), minimum-stability: dev, and "looking for a backer".

Requirements (composer.json):

  • php: >=8.4.1
  • revolt/event-loop: ^1.0 (an async event loop)
  • symfony/event-dispatcher: ^8.0 and symfony/string: ^8.0 (Symfony 8, not yet released)

Widget set: AbstractWidget, ContainerWidget, InputWidget, SelectListWidget, SettingsListWidget (+ SettingItem), EditorWidget, TextWidget, ProgressBarWidget, LoaderWidget, MarkdownWidget, FigletWidget, with Focusable/Quitable/Keybindings/ScheduledTick traits and a WidgetTree/WidgetContext composition model.

What our TUI does (the features to match)

From .vortex/customizer/src/Tui, Widget, Input:

  1. Panel navigation - a hub of panels and sub-panels with a breadcrumb (Navigator).
  2. Five field editors - text, single-select, multi-select (type-to-filter, select-all/none), autocomplete-with-fallback, yes/no.
  3. A scrolled viewport with pinned header/footer and cursor-follow vs mouse-wheel scroll (Scroller, Viewport).
  4. Provenance badges (default / discovered / edited), right-aligned by visible width.
  5. A theme system: self-contained theme classes owning colours and glyphs and layout, with dark/light built in and config-selectable custom classes.
  6. A start banner with version.
  7. Headless testability - the whole interaction is a synchronous, one-key-at-a-time state machine (PanelController::handle(Key)); tests feed an ArrayKeyStream and assert rendered frames. This is what gets the package to 100 percent coverage without a real terminal.

Feature-by-feature

Our feature symfony/tui equivalent Fit
Text / select / multiselect / suggest / confirm editors InputWidget, SelectListWidget, EditorWidget Good - close overlap, though multiselect-with-filter and suggest-with-fallback would need checking/porting
Panels of settings SettingsListWidget + SettingItem Good - conceptually the closest match to our panels
Navigation / focus Focus package, Focusable trait, WidgetTree Good
Scrolled viewport, pinned chrome Render + ContainerWidget Likely, but our exact scroll/badge behaviour would be re-implemented
Theme (colour + glyph + layout as a class) Style package Partial - Style covers colour; our glyph/layout-owning theme class would be reworked
Provenance badges none specific Build on top
Key parsing Input package Good
Headless, deterministic tests event-loop driven (Loop, revolt/event-loop, Event) Poor - architectural mismatch (see below)

Blockers

  1. Stability. Experimental, dev stability, no BC promise, and it needs Symfony 8 (unreleased). A distributable library pinning a dev-stability, BC-unstable dependency is a support liability. The customizer currently requires only symfony/console and alexskrypnyk/str2name.
  2. PHP floor. >=8.4.1 would raise the customizer's floor from >=8.2, dropping 8.2/8.3 consumers.
  3. Architecture mismatch with our test model. symfony/tui is event-loop driven (revolt/event-loop, an Event dispatcher, a Loop). Our TUI is a synchronous state machine specifically so tests can push one key and assert one frame, with no terminal, no timers and no async - the basis of the 100 percent-covered, deterministic suite. Moving to an event loop would make the interaction harder to test deterministically and would mean rewriting that suite.
  4. Scope. symfony/tui would only replace Tui/ + Widget/ + Input/. The customizer's value - the config-driven engine, derive/condition/discovery, handlers, schema - is untouched by it. So this is a rendering-layer swap, not a reduction of what we maintain overall.

Recommendation

Keep the custom TUI for now. It is small, dependency-light, headless-testable and fully covered, and it is coupled to the engine exactly as we need. Reassess symfony/tui when:

  • it leaves experimental status and gains a BC promise, and
  • Symfony 8 is released (removing the dev pin), and
  • we are comfortable with a PHP 8.4+ floor.

At that point the most valuable pieces to adopt would be its widget layer (SelectListWidget, InputWidget, SettingsListWidget) and Style/Ansi - provided we can preserve deterministic, terminal-free testing. Until then, the cost (unstable deps, higher PHP floor, a rewritten test suite) clearly outweighs the benefit (fewer rendering classes to maintain).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    Status
    BACKLOG

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions