Plan: Hyper-Modern Cross-Platform TUI MU\* Client (BeipMU-class)

August 11, 2026 · View on GitHub

Context

BeipMU is the best-in-class Windows-only MU* (MUSH/MUCK/MUD) client, but it's Win32/WPF and doesn't run natively on Linux or in modern GPU-accelerated terminals. The goal is a new client, written in C#/.NET, that reaches BeipMU feature parity while running as a TUI inside GPU-accelerated terminals (Kitty, WezTerm, Ghostty) on Windows and Linux.

Key reframing from research: "GPU-enabled" is a property of the terminal emulator, not our app. Any TUI running inside Kitty/WezTerm/Ghostty gets GPU-accelerated glyph rendering for free. Our job is to (a) emit rich truecolor/styled text and (b) use the Kitty graphics protocol (escape sequences) for inline images/maps, with graceful fallbacks. Both are fully achievable from managed C#.

Locked decisions (from planning Q&A)

  • Rendering base: SharpConsoleUI (nickprotop/ConsoleEx, stable, net8/9/10) — a compositor-based framework with split layouts, tabs, resizable/mouse windows, Spectre-style markup, and a native Kitty graphics protocol (+ Sixel/half-block). Superseded the original Terminal.Gui v2 choice (which was prerelease with an [Obsolete] mid-migration API); the switch was contained to SharpMUTerm.Tui since SharpMUTerm.Core is UI-agnostic. References below that describe Terminal.Gui reflect the earlier plan.
  • Scripting: Lua via MoonSharp (pure-managed, no native deps).
  • Inline graphics: must-have from day one.
  • Scope: broad BeipMU parity (phased into milestones below).
  • Target framework: .NET 10.
  • Protocol coverage: aim for compatibility with all common MU* protocols; MXP is first-class, and Pueblo (and its enhancements) are explicitly in scope alongside GMCP/MSDP/MSSP/MCCP.
  • Config: fresh JSON schema of our own — worlds (servers) hold characters; automation lives in shared, named trigger sets that characters opt into by name — versioned with automatic migration between schema revisions.

Research findings (cited)

  • TelnetNegotiationCore 1.0.0 (the requested package) implements: TELOPT (RFC855), GOAHEAD/GA (RFC858), TTYPE/terminal-type (RFC1091), MTTS, EOR (RFC885), NAWS (RFC1073), CHARSET (RFC2066), MSSP, and GMCP. It is a negotiation library only — it explicitly does not do ANSI, MXP, or Pueblo (those are our app layer). Gaps vs. a full MU* client: MCCP2/3 compression, MSDP, and MXP/Pueblo are not provided. Repo is described as young/rough. Sources: NuGet.
  • Terminal.Gui v2 (gui-cs) is the mature .NET TUI toolkit (Miguel de Icaza). Currently beta (v2.0.0-beta.218 era, ~2026), with truecolor and Kitty keyboard protocol support. It models the screen as a cell grid and has no native concept of the Kitty graphics layer — hence the custom GraphicsView. Sources: Terminal.Gui, V2 milestone.
  • Kitty graphics protocol: escape-sequence based (APC _G ... ST). The Unicode placeholder mechanism (rune U+10EEEE, image id encoded in cell fg color, position via combining diacritics) lets images live in real text cells so they scroll/clip/layout like text — the approach ratatui-image and Textual use. Supported by Kitty, WezTerm, Ghostty. Fallbacks: Sixel, then Unicode half-block/quadrant. Sources: Kitty graphics protocol.
  • BeipMU feature set to match: regex triggers, aliases, macros/keybinds, spawns (custom routed windows), flexible mapping, stat panes, image viewer, multiple input windows, puppets, ANSI 256 + 24-bit color, Unicode/emoji, HTML logging, SSL/TLS, IPv6, tab autocompletion, scripting. Sources: BeipMU, GitHub.
  • MU* protocol landscape we must cover at the app layer: ANSI/xterm-256/truecolor, GMCP, MSDP, MSSP, MCCP, MXP, NAWS, MTTS, charset. Sources: Mudlet supported protocols.

Architecture

Layered, with a strict separation between protocol/session (headless, unit-testable) and UI.

+-----------------------------------------------------------+
|  UI layer (Terminal.Gui v2)                               |
|   WorldTabView | OutputPane | InputPane | GraphicsView    |
|   MapView | StatPane | SpawnWindow | Dialogs/Settings     |
+-----------------------------------------------------------+
|  ViewModel / App services                                 |
|   TriggerEngine | AliasEngine | MacroEngine | Logger      |
|   ScriptHost(MoonSharp) | MapModel | SessionManager       |
+-----------------------------------------------------------+
|  Session layer (headless, testable)                       |
|   TelnetSession (TelnetNegotiationCore wrapper)           |
|   AnsiParser | MxpParser | GmcpRouter | McccpStream        |
|   LineBuffer / ScrollbackModel                            |
+-----------------------------------------------------------+
|  Transport: TcpClient + SslStream (TLS), IPv6             |
+-----------------------------------------------------------+

Solution structure (proposed)

  • SharpMUTerm.Core — transport, telnet, ANSI/MXP parsers, GMCP/MSDP routing, scrollback model, trigger/alias/macro engines, logging. No UI deps.
  • SharpMUTerm.Scripting — MoonSharp host + the scripting API surface (world, output, triggers, timers, gmcp).
  • SharpMUTerm.Graphics — Kitty graphics protocol encoder, capability probe, Sixel + half-block fallbacks, GraphicsView.
  • SharpMUTerm.Tui — SharpConsoleUI app: windows, panes, key routing, settings UI, wiring.
  • SharpMUTerm.Core.Tests / SharpMUTerm.Graphics.Tests — xUnit.
  • Target .NET 10 (confirm TelnetNegotiationCore + Terminal.Gui v2 support net10.0; if a dep lags, reference it via net8.0 compat and keep our own projects on net10.0).

Protocol layer

  • Wrap TelnetNegotiationCore behind a TelnetSession interface (so we can swap/extend it). Use it for: option negotiation, NAWS (report terminal size on resize), MTTS/TTYPE (advertise as e.g. MUCLIENT/XTERM-256COLOR + MTTS bitvector incl. 256/truecolor/UTF-8/MOUSE), CHARSET (UTF-8), EOR/GA (prompt detection), MSSP, and GMCP (routed to a GmcpRouter that dispatches JSON packages to subscribers + scripts).
  • Fill the gaps TelnetNegotiationCore lacks:
    • MCCP2/3: intercept the IAC SB COMPRESS2 negotiation and wrap the inbound stream in a System.IO.Compression.DeflateStream (zlib). Design TelnetSession so decompression sits below the telnet parser.
    • MSDP: implement as a subnegotiation handler if the target servers need it (many use GMCP instead — make it optional).
    • MXP / Pueblo: first-class app-layer parsers in Core. MXP (line-tagged HTML-ish markup → styled spans, clickable links/commands/<SEND>, inline images via the graphics layer, secure/open/locked line modes) and Pueblo (its HTML-subset predecessor + enhancements) both targeted. Clickable/link infrastructure shared with the graphics layer.
  • ANSI parser (AnsiParser): SGR incl. 256-color and 24-bit truecolor, cursor/erase handling relevant to MU* output, producing styled spans for the scrollback model. This is ours, not the toolkit's.
  • Prompt handling: use EOR/GA to keep prompts on the input line rather than scrollback (BeipMU-style).

Rendering & graphics

  • Text UI: Terminal.Gui v2 provides the window manager, tabbed worlds, dockable panes, scrollback view, multi-input, focus, and truecolor cell rendering. Advertise UTF-8 + truecolor to servers.
  • GraphicsView (SharpMUTerm.Graphics): renders images (maps, avatars, inline media) using Kitty Unicode placeholders so images occupy real cells and scroll/clip via Terminal.Gui's layout. Pipeline: probe capability → upload image once (base64-chunked APC _G transmit) → paint placeholder runes/colors into the view's cells → manage image lifecycle (a=d delete on close/replace).
  • Capability probe + fallbacks: query terminal for Kitty graphics; else Sixel; else Unicode half-block/quadrant approximation; else a text placeholder. Selection is per-session and user-overridable in settings.
  • Map rendering: MapModel (rooms/exits/z-levels) rendered either as box-drawing/Unicode vector art in a normal view or as a rasterized image through GraphicsView — start with box-drawing (works everywhere), add rasterized mode where graphics are available.

Scripting (MoonSharp / Lua)

  • ScriptHost embeds MoonSharp with a sandboxed environment (no raw IO/OS by default).
  • Expose an API: world.Send(), output.Print()/PrintStyled(), trigger.Add(), alias.Add(), timer.Every(), gmcp.On(), map.*, spawn.To(). Triggers/aliases can be pure-regex or call Lua callbacks.
  • Per-world script files + a shared global profile; hot-reload on save.
  • Trigger/alias/macro engines live in Core and are usable without scripting (regex + substitution), with Lua as the power layer.

Milestones (broad parity, phased so each is usable)

M1 — Usable text client Transport (TCP + SslStream TLS + IPv6); TelnetSession over TelnetNegotiationCore (NAWS/MTTS/charset/EOR/GA/GMCP); AnsiParser (256+truecolor); scrollback model + OutputPane; InputPane with history + tab completion; tabbed multi-world connections; connect/session manager; plaintext + HTML logging. MCCP2 decompression.

M2 — Automation TriggerEngine (regex, gag/highlight/rewrite/spawn actions), AliasEngine, MacroEngine/keybinds, timers. Settings UI for all of them. Per-world profiles (JSON).

M3 — Graphics day-one payoff SharpMUTerm.Graphics: Kitty placeholder GraphicsView + Sixel/half-block fallbacks + capability probe. Inline image viewer; map view (box-drawing first, rasterized where supported); stat panes driven by GMCP.

M4 — Scripting MoonSharp ScriptHost, scripting API, Lua-backed triggers/aliases, GMCP subscriptions from Lua, hot-reload.

M5 — Full parity & polish Spawns (route matched output to named windows), puppets, multiple input windows, MXP + Pueblo parsers (clickable links/commands/<SEND>, inline images via graphics layer), MSDP, Unicode emoji + :)→🙂, smooth-scroll/appearance options, theming, packaging (dotnet single-file for Windows + Linux; optional distro packages).

M5 progress (delivered)

  • MXP and Pueblo parsers in Core (ILineParser), selectable per world via WorldDefinition.ContentFormat; links/commands surface as SpanInteraction and are clickable in the TUI. Emoji substitution (EmojiSubstitutor), opt-in per world. GMCP-driven stat line, spawn capture, ReDoS-guarded regex engines, and self-contained single-file packaging (docs/PACKAGING.md) + a tagged release workflow.
  • In-TUI web view (SharpMUTerm.Web + WebView): fetch a URL or follow an MXP/Pueblo/HTML link and read the page as styled, word-wrapped text with clickable in-pane navigation (AngleSharp → StyledLines, reusing SpanInteraction). <img> renders as a labelled link today.

M5 UI design (delivered)

The multi-pane workspace design (tmux-style pane tree hosting BeipMU-style windows) is rendered by the SharpConsoleUI shell over the Core models: the connection rail (worlds → characters → windows), split panes with tabbed windows (each leaf pane a tab strip; row/column splits become proportional grids with draggable splitters; zoom collapses to one pane), the command surface (Ctrl+P) ranking GO TO / WORLD / TERMINAL / LAYOUT actions, per-world accent colours threaded through the header/rail/status, optional per-character pane tints (a named colour on the plane a character's panes are painted on, at the theme surface's own luminance so the focus cue — which is luminance — stays the only thing saying where you are), a status bar with GMCP HP/EN meters, and a character-bound input prompt with a destination/draft gutter. Built on these Core pieces (pure + tested):

  • Config schema (Core.Configuration): worlds (servers) hold characters; automation lives in shared, named trigger sets that characters opt into. Sessions key on world.character and compose engines from the union of a character's sets. Versioned with ConfigurationMigrator.
  • Workspace model (Core.Workspaces): a pure WorkspaceLayout split tree — PaneNode (tab strip of window ids) / SplitNode (row/col) with focus, zoom, freeze, and the tmux-style split / close / cycle / move / reorder operations, maintaining the no-empty-pane / no-lone-split invariants.
  • Windows & spawn routing (Core.Workspaces): a Workspace aggregate ties the layout to a registry of WorkspaceWindows (title, kind, owning world.character, unread count, unsent-input marker). RouteSpawn finds-or-creates a background spawn window per TriggerEngine SpawnTarget and accrues unread while it is not the visible tab; activating a window clears it. The SharpConsoleUI view hosting (splits, tabs, rail) renders from this model, rebuilding the pane area on every layout change (split / close / zoom / spawn) and swapping it into the live window.

Still open (M5+)

  • Freeze view (split-scrollback) rendering, settings dialogs (F-keys), and mouse/drag pane resizing — the models and command-surface entries exist; the interactive view work remains.
  • Dedicated multiple input windows (capture + routing hooks exist), puppets, MSDP-driven stat panes, and the map view.
  • Web view enhancements: render <img> inline through the existing InlineImageRenderer (Kitty → Sixel → half-block) in graphics-capable terminals, and an optional high-fidelity mode that snapshots the page with headless Chromium (Playwright) and displays the image via the graphics layer.

Key risks & mitigations

  • Terminal.Gui v2 is beta → pin a known-good beta; wrap it behind our own view interfaces so churn is contained; keep Core UI-agnostic so a renderer swap is possible.
  • Graphics/cell-grid friction → committed to the Unicode-placeholder approach specifically because images become real cells the toolkit already manages; degrade gracefully when unsupported.
  • TelnetNegotiationCore immaturity / missing MCCP-MSDP-MXP → wrapped behind TelnetSession; gaps implemented in our layer; the wrapper lets us replace the library entirely if needed.
  • Terminal capability variance → probe + user override; never hard-require Kitty graphics for core text use.

Verification

  • Unit tests (Core.Tests): ANSI/SGR parser (256 + truecolor + edge sequences), telnet negotiation round-trips, MCCP decompression against captured zlib streams, trigger/alias regex + action application, GMCP JSON routing.
  • Graphics tests: Kitty placeholder-sequence encoder golden-output tests; capability-probe fallback selection.
  • Manual/integration: connect to a public test MU* (and a local throwaway server) from Kitty, WezTerm, Ghostty, and a non-graphics terminal; verify truecolor, prompts on input line, logging, triggers firing, an inline image rendering under Kitty and degrading to half-block elsewhere. Run on both Windows and Linux.
  • Run the tests in CI with dotnet run --project <testproj> per test project (GitHub Actions matrix: windows-latest + ubuntu-latest). TUnit runs on Microsoft.Testing.Platform, where the classic dotnet test/VSTest path is unsupported on .NET 10 and later.

Open items to confirm before/at M1

  • Confirm TelnetNegotiationCore + Terminal.Gui v2 both build against net10.0 (fallback: consume via net8.0 compat).
  • Which servers you actually play on (helps prioritize protocol edge cases; all are targeted regardless).
  • Final project/repo name (currently scaffolded as SharpMUTerm — trivially renamable).