Files
tai/taicpp/docs/mplex.md
T

7.9 KiB

mplex + uiflow (detz port)

Spec delta for the C++ port of tai's terminal multiplexer and the interactive UI flows. The behavior specification is unchanged and lives in ../../mplex.md and ../../uiflow.md; the code being transcribed is the Mplex* and Plexer* classes of ../../tai.py. This page records what the port adds: the file layout, the Python-to-C++ mapping rules, the parity table, the tests and the known gaps.

Goal

A literal transpilation with identical behavior: the same keys, commands, options, formats, wire frames, control protocol, config file and error texts. Where Python and C++ differ at the language level, the port reproduces Python's semantics (mplex_py.h) instead of adopting C++'s.

Layout

All Mplex code is in taicpp/src/mplex/, namespace detz::mplex; the uiflow code is in taicpp/src/plexer/plexer_flow.{h,cpp}, namespace detz::plexer. One class per header and source pair; every file name is prefixed mplex_ so the flat include path of the Makefile cannot clash with other modules. Class and method names are the Python names (snake_case methods, Mplex* classes).

Mapping rules

Python C++
str std::string (UTF-8) at API edges, UStr (std::wstring, one wchar_t per code point) wherever len, index or slice count code points
bytes Bytes (std::string holding raw bytes)
None std::optional, an empty shared_ptr, or Color 0
iterated dict OrderedMap (insertion order is iteration order)
(fg, bg, attr) / cell tuple Style / Cell (ch == 0 is a never written cell)
SGR colour fragment "38;5;12" Color (32 bit pack, lossless for every fragment tai.py produces)
MplexError, ValueError MplexError, ValueError (both std::runtime_error)
closures std::function
back references (pane.window, window.session, layout parent) weak_ptr with an accessor returning shared_ptr
modes and overlays on a client shared_ptr held by the owner; MplexServer::discard_pane clears pane.modes to break the pane/mode cycle
json, tomllib, re, fnmatch, textwrap, shlex, time.strftime, subprocess.run mplex_json.h, mplex_toml.h, mplex_py.h (py::Regex, py::fnmatchcase, py::textwrap_wrap, py::shlex_quote, py::strftime, py::run_shell)

Parity table

tai.py symbol C++ symbol files
MplexError MplexError mplex_common.h
MplexKeys MplexKeys mplex_keys.{h,cpp}
tui_parse_one and helpers tui_parse_one mplex_input.{h,cpp}
MplexArgs, MplexCommandParser same mplex_args.{h,cpp}
MplexStyle MplexStyle mplex_style.{h,cpp}
MplexOptions MplexOptions mplex_options.{h,cpp}
MplexBinding, MplexConfig, MPLEX_DEFAULT_KEYS same mplex_config.{h,cpp}, mplex_default_keys.cpp (generated)
MplexFormat MplexFormat mplex_format.{h,cpp}
MplexLayoutNode, MplexLayout same mplex_layout.{h,cpp}
TuiVtScreen (as used by panes) MplexVtScreen mplex_vtscreen.{h,cpp}
MplexPane, MplexWindow, MplexSession same mplex_pane.{h,cpp}
MplexCanvas, MplexStatusText, MplexFont same mplex_canvas.{h,cpp}
MplexMode, MplexClockMode same mplex_mode.{h,cpp}, mplex_clockmode.{h,cpp}
MplexCopyMode MplexCopyMode mplex_copymode.{h,cpp}
MplexPrompt MplexPrompt mplex_prompt.{h,cpp}
MplexPaneNumbers, MplexFlowScreen same mplex_panenumbers.{h,cpp}, mplex_flowscreen.{h,cpp}
MplexChoice, MplexChooser same mplex_chooser.{h,cpp}
MplexRenderer MplexRenderer mplex_renderer.{h,cpp}
MplexCtx, MplexTargets same mplex_ctx.{h,cpp}
MplexClientConn, MplexPending same mplex_clientconn.{h,cpp}
MplexServer MplexServer mplex_server.{h,cpp}
MplexCommands, MPLEX_COMMAND_SPECS, MPLEX_DEFAULT_FORMATS same mplex_commands.{h,cpp}, mplex_command_specs.cpp (generated)
MplexClient, MplexCli, NativePlexerBackend, plexer_should_wrap, bashrc blocks same mplex_client.{h,cpp}
PlexerController, NativePlexerController, TmuxPlexerController, PlexerControllerFactory same src/plexer/plexer_flow.{h,cpp}
validate_flow_*, PlexerFlow, PlexerBackend same src/plexer/plexer_flow.{h,cpp}

The two generated files are produced from the Python tables; they carry a header saying so and mplex_spec_test compares them with mplex.md.

Tests

  • tests/mplex_spec_test.cpp: the C++ twin of MplexSpecTests and UiflowDocTests: mplex.md and uiflow.md must list exactly the options, commands, default bindings, copy-mode commands, caps and flow words that the C++ tables hold.
  • tests/mplex_foundation_test.cpp: the Python-shaped helpers, JSON and TOML, against Python 3.14.
  • tests/mplex_vt_test.cpp: the VT screen, input parser and keys against TuiVtScreen and MplexKeys.
  • tests/mplex_core_test.cpp: args, parser, style, options, config, format and layout against tai.py.
  • tests/mplex_model_test.cpp: panes, canvas, prompt, chooser, flow screen, renderer, targets.
  • tests/mplex_copy_test.cpp: copy mode against tai.py's recorded tmux results and a seeded fuzz.
  • tests/mplex_server_test.cpp, tests/mplex_commands_test.cpp: the real C++ server over its socket, scripted command corpus compared with the Python server.
  • tests/mplex_flow_test.cpp: validate_flow_spec and PlexerFlow against tai.py.
  • tests/mplex_e2e.py with tests/mplex_cli_main.cpp: the real client under a pty, as tests/mplex.py does.

Expected values in these tests are produced by running tai.py (and tmux 3.6 where mplex.md names it the oracle), never typed in from memory.

Known gaps of the port

Listed next to what would close them; the final status of each is recorded in the milestone report.

  • Regular expressions (py::Regex, std::wregex ECMAScript): Python-only syntax (lookbehind, named groups, possessive quantifiers, \Z) is translated where an equivalent exists and rejected with a loud error otherwise; copy-mode search then falls back to a literal, like Python does for an invalid expression.
  • Unicode classification (str.isalpha, isprintable, case mapping) uses the glibc C.UTF-8 tables, which may lag or lead CPython's unicodedata by a Unicode version.
  • TOML error texts follow tomllib's wording; positions are reproduced, rare messages may differ.
  • Anything else found while testing is added here, never hidden.

Feed panes (taicpp extension, orchestration)

A feed pane is a pane without a process: text is written into it through the control socket and the server composes it like any other pane. They exist for sub agent panels; the full contract (protocol, layout policy, lifecycle, caps, examples) is orchestration.md section 3. The Python app has none of this, so it is outside the tables mplex_spec_test compares with mplex.md.

Piece Where
registry, layout, linger, orphan lease, the four commands src/mplex/mplex_vpane.{h,cpp} (MplexVpanes)
virtual_pane, header, header_style, open_log; screen has h - 1 rows; write is a no-op src/mplex/mplex_pane.{h,cpp}
header row drawn above the pane's grid MplexRenderer::frame
vpane_open, vpane_write, vpane_close, vpane_list dispatch MplexServer::dispatch_control
entry forgotten when a pane is discarded (also by kill-pane) MplexServer::discard_pane
lease and linger sweep, once a second MplexServer::tick
a window whose real panes are all gone is closed MplexServer::kill_pane

Behavior that differs from a process pane: no pty, no reaper, no input; list-panes, capture-pane -p, kill-pane, copy mode, zoom and the read control command work; select-layout can be used but the next panel open or close re-applies main-vertical with the main pane at 55 percent.