# 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](../../mplex.md) and [../../uiflow.md](../../uiflow.md); the code being transcribed is the `Mplex*` and `Plexer*` classes of [../../tai.py](../../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.