MarkupString.Ansi
2.31.1
See the version list below for details.
dotnet add package MarkupString.Ansi --version 2.31.1
NuGet\Install-Package MarkupString.Ansi -Version 2.31.1
<PackageReference Include="MarkupString.Ansi" Version="2.31.1" />
<PackageVersion Include="MarkupString.Ansi" Version="2.31.1" />
<PackageReference Include="MarkupString.Ansi" />
paket add MarkupString.Ansi --version 2.31.1
#r "nuget: MarkupString.Ansi, 2.31.1"
#:package MarkupString.Ansi@2.31.1
#addin nuget:?package=MarkupString.Ansi&version=2.31.1
#tool nuget:?package=MarkupString.Ansi&version=2.31.1
MarkupString.Ansi
Terminal styling for MarkupString: colours (the 16
standard ones, xterm-256 and truecolor), bold/faint/italic/underline/overline/strike/blink/invert,
and links — either a URL or a command for the client to send.
One AnsiMarkup layer per span; a run's stack of layers folds into a single SGR sequence for the
terminal, one <span> for HTML, or the nearest equivalent in the other formats. Transitions are
written as diffs, so nested spans do not restate what is already in effect.
dotnet add package MarkupString.Ansi
Usage
using MarkupString;
using MarkupString.Ansi;
using MarkupString.Html;
MarkupRegistry.Default = MarkupRegistry.Empty.WithAnsi().WithHtml();
var text = MarkupText.Concat(
MarkupText.Plain("Hello, "),
MarkupText.Wrap(AnsiCodeParser.Parse("hr"), "world"));
text.Render(MarkupFormat.Ansi); // Hello, \e[1;31mworld\e[0m
text.Render(MarkupFormat.Html); // Hello, <span style="color: #ff5555">world</span>
var json = MarkupTextSerializer.Serialize(text);
var back = MarkupTextSerializer.Deserialize(json);
AnsiCodeParser.Parse takes MUSH ansi() syntax — hr, /R, +xterm200, #ff5555,
<255 0 0> — and AnsiMarkup.Create(...) builds the same thing from named arguments.
AnsiCodeWriter.Write(style) goes the other way, from a style to the codes that produce it.
Formats
WithAnsi() registers emitters for Ansi, Html, Pueblo, Mxp, BBCode and Markdown. Plain
needs none: the body passes through. For Markdown it also draws a layout from its tree — a table as a
pipe table, lists as lists — so a page made with BlockLayout can be stored in a wiki. See
Markdown.
A command link (linkKind: LinkKind.Command) is written in each client's own dialect: <A XCH_CMD>
for Pueblo, <SEND HREF> for MXP, an ms-cmd-link anchor carrying xch_cmd for HTML, and the bare
text for a terminal. Pueblo and MXP are different dialects, and each client prints the other's
tags as text.
An MXP client reads tags only on a line opened in secure mode. The core package's
MarkupRegistry.WithMxpSecureLines() opens every line of Mxp output with ESC[1z. Use it on the
registry that renders for an MXP connection, and leave it off the one used for tests, logs and
previews.
A client that cannot show every colour gets its own registry:
MarkupRegistry.Default.WithAnsiOutput(AnsiColorDepth.Xterm256) writes each colour as the nearest one the
256-colour palette has, Standard as the nearest of the sixteen, Attributes with no colour at all, and
None with no SGR. It covers Ansi, Pueblo and Mxp. Pass hyperlinks: false for a terminal that
prints OSC 8 instead of reading it.
Terminal features: links and pictures
WithAnsiOutput(new AnsiOutputOptions(depth, features) { Pictures = source }) describes one client
fully. TerminalFeatures says what its terminal reads beyond colour:
| Feature | Written as |
|---|---|
Hyperlinks |
a URL link as OSC 8 (ESC ] 8 ; ; url ST) |
CommandLinks |
a command link as MSLP (ESC ] 68 ; 1 ; SEND ; command BEL, then the underlined text) |
KittyGraphics |
a picture through the Kitty graphics protocol, with Unicode placeholders |
InlineImages |
a picture as an iTerm2 inline image (ESC ] 1337 ; File=…) |
Sixel |
a picture as sixel graphics |
BlockArt |
a picture as coloured ▀/▄ half blocks, plain text any UTF-8 colour terminal shows |
MovingPictures |
a moving picture played, through Kitty frames or an iTerm2 GIF, rather than its first frame |
Pictures are drawn into the cells a Figure reserves when it is laid out for such a reader
(LayoutContext.Pictures, which answers the cells a picture takes). Each row is marked with the
figure's ImageMarkup, its Row saying which row of the picture it is, over the figure's text art, so a
box, a flex row or a table around it lines up whichever way it ends up drawn. The pixels come from the host's ITerminalPictureSource: this package
neither fetches nor decodes a file. Without the pixels, or without the feature, the row is its art.
- Kitty sends the picture once per connection (
a=T,U=1, a PNG in 4096-byte chunks,q=2so nothing comes back as input) and writes each cell asU+10EEEEwith row and column diacritics, the image id as a truecolor foreground. Placeholders are text: they wrap, scroll and are erased like it.MarkTransmittedis how the source says whether the terminal already has it. A moving picture (aTerminalPicturemade fromTerminalPictureFrames) sends its later frames after the first (a=f,X=1, each with its duration asz), sets the first frame's duration, and starts it looping (a=a,s=3,v=1); the terminal plays it with nothing more sent. Only a client withMovingPicturesis sent the frames. - iTerm2 and sixel are pixels over the screen. A moving picture is sent to iTerm2, given
MovingPictures, as a looping GIF. On the picture's first row the cursor makes room below (ESC Dper row), comes back up, and draws the picture betweenESC 7andESC 8; every row then steps over its cells withCSI n C, so no text is written over it. Sixel is the cells' size in pixels (CellWidth×CellHeight, 10 × 20 unless the host knows better; 0, which a terminal reports when it does not know, counts as unknown). - Half blocks letterbox the picture into two pixels a cell, at the client's colour depth. A client
with no colour (
AttributesorNone) is sent the text art instead.
Each encoding (the PNG, the Kitty transmission, the sixels, the half-block rows) is made once per
picture and size and kept with the TerminalPicture, for as long as the host keeps the picture: every
other connection shown it at that size is sent a copy. So a host should hand out one
TerminalPicture per picture rather than a new one per render.
None of these is sent on a guess: a MUD client that is not a terminal emulator may print them.
Terminals by name
TerminalProfile names the terminals this package knows and what each can be sent. A host identifies
the client's terminal from what it reports (TerminalProfile.Identify("kitty(0.35.2)"), a telnet
terminal type or an XTVERSION reply) or from the player (TerminalProfile.Find("wezterm")), and sends it
only what the player turned on:
var options = AnsiOutputOptions.For(TerminalProfile.WezTerm, chosenByPlayer) with { Pictures = source };
| Terminal | Links | Kitty (placeholders) | iTerm2 images | Sixel | Moving pictures |
|---|---|---|---|---|---|
| kitty | yes | yes | Kitty frames | ||
| Ghostty | yes | yes | |||
| WezTerm | yes | yes | yes | GIF | |
| iTerm2 | yes | yes, in parts past 1 MiB | yes | GIF | |
| Konsole | yes | yes | yes | ||
| foot | yes | yes | |||
| xterm | yes (-ti vt340) |
||||
| Windows Terminal | yes | yes | |||
| mintty | yes | yes | yes | ||
| VS Code | yes | yes (with images on) | yes | ||
| Contour | yes | yes | |||
| Rio | yes | yes | yes | yes | |
| mlterm | yes | yes | |||
| Alacritty, VTE, tmux | yes |
Every one draws half blocks. A cell is empty where the terminal lacks the feature or it could not be confirmed: several terminals read Kitty graphics without the Unicode placeholders this package writes (WezTerm, Konsole, Contour, VS Code), Ghostty does not yet animate them in a release, and iTerm2's own Kitty support is unannounced, so it is drawn for with its own protocol. Windows Terminal and Alacritty do not answer XTVERSION, so only a player can name them.
Reading terminal output
DecoderRegistry.Empty.WithVt().WithPictures() reads SGR, the C0 controls, OSC links, titles and
palette, terminal queries, and Kitty, iTerm2 and sixel pictures into MarkupText lines.
DisplayRegistry.Core.WithAnsi() resolves them for a web page or a cell grid. See
Decoding and Display.
Styling the HTML output
The HTML-family emitters write ms-* classes for the attributes with a fixed rendering (bold,
italic, underline, strike, overline, faint, blink, invert, and command links) and inline style
for colours, which are open-ended. AnsiCss.Fixed is the stylesheet for those classes — include
it once per page, or copy its rules into your own sheet.
Extension points
IAnsiStyleSource— implement it on your ownIMarkupand this package's fold picks the style up, so a layer of yours can contribute bold or a colour without its own emitter per format.IMarkupEmitter/IMarkupSetEmitter/IMarkupCodec— replace any of this package's registrations by adding yours to the registry afterwards.
AOT and trimming
IsAotCompatible; no reflection, no dynamic code. Registration is an explicit WithAnsi() call,
so nothing depends on a type surviving the trimmer by name.
Licence
Apache-2.0. Source, guides and issues: SharpMUSH/MarkupString.
MudForge 2.0.5657 is recognized by MUDFORGE and supports still iTerm2 inline images and Sixel.
This profile records verified image support; it does not enable graphics without the host/player's
opt-in or imply support for animation or Kitty. Its web client currently does not forward terminal
query replies, so automatic cell-size discovery still requires a client fix.
Sixel uses an adaptive palette of up to 255 colours, selected from the image's colour histogram, with a reserved transparent index. The bounded median-cut palette implementation is shared with GIF encoding; GIF's existing palette and animation behaviour are unchanged.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. net11.0 is compatible. |
-
net10.0
- MarkupString (>= 2.31.1)
-
net11.0
- MarkupString (>= 2.31.1)
NuGet packages (4)
Showing the top 4 NuGet packages that depend on MarkupString.Ansi:
| Package | Downloads |
|---|---|
|
MarkupString.Html
Raw HTML tag markup for MarkupString — an anchor, a pre, a span — rendered as itself in the HTML, Pueblo and MXP formats and folded into terminal styling elsewhere, with checked construction and tag policies for untrusted input. |
|
|
MarkupString.Mxp
MXP for MarkupString: writes the shared vocabulary — sounds, music, pictures, panes, gauges, status, variables, link expiry, relocation, login prompts — as MXP tags, holding each to what the client said it supports. |
|
|
SharpMUSH.Library
SharpMUSH plugin contract surface (interim). External SharpMUSH plugins reference this package to write [SharpPlugin] : PluginBase entry types with [SharpCommand]/[SharpFunction] members. Aligned to PluginContractVersion 4.0.0. |
|
|
MarkupString.Extensions.DependencyInjection
Microsoft.Extensions.DependencyInjection setup for MarkupString: AddMarkupString() registers the markup, decoder and display registries, a shared picture cache, a decoder factory and the link policy, configured in code or bound from configuration. |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 2.31.3 | 0 | 11/15/2026 |
| 2.31.2 | 198 | 11/15/2026 |
| 2.31.1 | 187 | 11/15/2026 |
| 2.31.0 | 1,535 | 11/13/2026 |
| 2.30.0 | 338 | 11/12/2026 |
| 2.29.1 | 121 | 11/11/2026 |
| 2.29.0 | 110 | 11/11/2026 |
| 2.28.1 | 136 | 11/11/2026 |
| 2.28.0 | 1,589 | 11/10/2026 |
| 2.27.3 | 625 | 11/9/2026 |
| 2.27.2 | 365 | 11/7/2026 |
| 2.27.1 | 187 | 11/7/2026 |
| 2.27.0 | 149 | 11/7/2026 |
| 2.26.0 | 133 | 11/7/2026 |
| 2.25.0 | 1,757 | 11/3/2026 |
| 2.24.2 | 1,645 | 10/29/2026 |
| 2.24.1 | 122 | 10/29/2026 |
| 2.24.0 | 379 | 10/28/2026 |
| 2.23.1 | 826 | 10/27/2026 |
| 2.23.0 | 1,090 | 10/24/2026 |