Skip to content

Upgrade TelnetNegotiationCore to 4.6.1; truecolour, inline images, Pueblo via TNC - #29

Merged
HarryCordewener merged 4 commits into
masterfrom
claude/project-thread-rsojjh
Nov 6, 2026
Merged

HarryCordewener merged 4 commits into
masterfrom
claude/project-thread-rsojjh

Conversation

@HarryCordewener

@HarryCordewener HarryCordewener commented Nov 6, 2026 •

Copy link
Copy Markdown
Member

Requested by Grave · project thread

Why

SharpClient is on TNC 2.5.3, and the current release is 4.6.1. This PR upgrades it and adds the MUSH features asked for in the same thread (24-bit colour and inline images). Pueblo images are only safe once Pueblo detection is fixed, so that fix is included too.

Changes

TNC upgrade

  • NAWS. SendNAWS was removed in 2.8.0. The window size now goes through WindowSizeProtocol, a NAWSProtocol subclass (SharpMUTerm does the same):

    • It keeps the last size and reports it when the server sends DO NAWS. TNC drops a report sent before that, so on 2.5.3 the size measured at connect, and the size carried over an auto-reconnect, never reached the server.
    • It clamps to 0..65535. TNC now throws outside that range instead of wrapping.

    Because AddDefaultMUDProtocols always registers the base NAWSProtocol, the protocol list is now written out in full.

  • Client identity. Since 2.8.0, TTYPE answers UNKNOWN unless the client names itself. It now sends SharpClient, then XTERM, then MTTS ANSI | UTF-8 | 256 colours | truecolour.

  • Package floor. Microsoft.Extensions.* packages that are referenced directly move from 10.0.9 to 10.0.14, the version TNC 4.6.1 depends on. Without that, restore fails with NU1605.

  • Android AOT and trimming stay off. The original reason (FSharp.Core) is gone, but .NET for Android allows AOT only together with trimming (XA1030). Trimming needs a device check first, because EF Core and the Blazor WebView use reflection. The csproj comment now explains this.

Rendering

  • 24-bit colour. SGR 38;2;r;g;b and 48;2;r;g;b used to be drawn in the default colour. They now produce AnsiColorKind.Rgb, which SegmentStyle writes out as #rrggbb. Components outside 0..255 are clamped. A truncated sequence is still consumed and leaves no colour.
  • Inline images.
    • MXP <IMAGE> is honoured only on a Secure line, the same as <SEND>. The URL is URL + T + FNAME (or the first positional argument), as the spec describes.
    • Pueblo <img src alt> is honoured in Pueblo mode. <img xch_mode=…> is ignored, since it is Pueblo's mode switch and not a picture.
    • Only http, https and data:image/ URLs are allowed. javascript:, file:, content: and local paths are refused.
    • OutputView renders an <img loading=lazy referrerpolicy=no-referrer>, capped at the column width and 40vh. Inside a <SEND>/<a> the image is a clickable button.
  • Pueblo detection uses TNC's PuebloProtocol. Before, Session switched Pueblo on for any line containing "This world is Pueblo", including another player's pose. It then sent PUEBLOCLIENT 2.01 as a game command, and after that every <a xch_cmd> in the output was clickable. Now:
    • Only the server's hello and its start sequence count. TNC consumes both.
    • The client answers PUEBLOCLIENT 2.50 only if the server has not agreed MXP. SharpMUSH offers both, and answering Pueblo makes it stop sending MXP; SharpMUTerm learned the same thing.
    • MXP and Pueblo state is cleared when the connection drops, so a reconnect negotiates them again.

Tests

  • Connection: the TTYPE cycle; NAWS held until DO NAWS; NAWS clamping and IAC doubling.
  • AnsiParser: truecolour foreground, background, mixed with other codes, truncated, clamped.
  • SegmentStyle: RGB → hex.
  • InlineImageTests: MXP URL composition, FName, Open-line refusal, bare file name refusal, Pueblo src/alt, the mode-switch img, refused schemes, data:image, an image inside SEND.
  • Session: Pueblo enabled only by the connection event, never by a quoted hello (nothing sent, links inert); state reset on reconnect.
  • bUnit: <img> attributes, and an image inside a command button.

Verification

  • CI unit tests at e95c04c: 265 core / 75 UI / 17 data, all passing. Re-running on the latest commit.
  • CI Android head build
  • Not done: a live session against Convergence or a Pueblo/MXP world with images, and a check on an Android device. The container I worked in has no .NET SDK, so CI is the only build. On Android, http: images are likely blocked as mixed content in the WebView (it serves from https://0.0.0.1), so expect only https: images to show there.

Out of scope / follow-ups

  • Answering MXP <SUPPORT>/<VERSION>, TLS, MCCP and NEW-ENVIRON, from the SharpMUTerm gap list.
  • A setting to turn off remote images, since loading one tells the image host your IP.
  • AOT and trimming together, after a device check.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Rqqt7TbSP3xxcS6oaZwo3C

- SendNAWS was removed in TNC 2.8.0; report the window size through
  NAWSProtocol.SendWindowSizeAsync, clamped to RFC 1073's 0..65535
  (TNC now throws outside it instead of wrapping).
- TNC 2.8.0 stopped naming the client: TTYPE would answer UNKNOWN.
  Set WithClientIdentity("SharpClient") with TERMINAL_TYPE XTERM and
  MTTS ANSI|UTF-8|256 colours (truecolour is not rendered, so not claimed).
- Bump directly referenced Microsoft.Extensions.* to 10.0.14, the floor
  TNC 4.6.1 depends on, so restore does not report a downgrade (NU1605).
- TNC 4.0 dropped FSharp.Core, so Android Release AOT is re-enabled.
  Trimming stays off until a trimmed APK is checked on a device.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rqqt7TbSP3xxcS6oaZwo3C
@HarryCordewener HarryCordewener self-assigned this Nov 6, 2026
@coderabbitai

coderabbitai Bot commented Nov 6, 2026 •

Copy link
Copy Markdown

Explain Change →

Caution

Review failed

The pull request is closed.

  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

…ects

TNC drops a NAWS report made before the server's DO NAWS, as RFC 1073
requires, and nothing reported it again: the size the UI measured at
connect and the size carried over an auto-reconnect never reached the
server until the next resize. WindowSizeProtocol keeps the last size and
reports it when NAWS is agreed, the way SharpMUTerm does. The default
protocol set is spelled out because AddDefaultMUDProtocols always
registers the base NAWSProtocol.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rqqt7TbSP3xxcS6oaZwo3C
…for AOT (XA1030)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rqqt7TbSP3xxcS6oaZwo3C
- SGR 38;2 / 48;2 now render as the given colour (AnsiColorKind.Rgb)
  instead of the default, and MTTS claims truecolour.
- MXP <IMAGE> on a Secure line and Pueblo <img src> render as <img> in
  the output, inside a <SEND>/<a> link when one is open. Only http(s)
  and data:image URLs are accepted; images load with no-referrer.
- Pueblo is detected by TNC's PuebloProtocol (the server's hello and its
  start sequence, both consumed) instead of a substring match on any
  line, which let a player's pose switch the client into HTML mode and
  sent PUEBLOCLIENT as a game command. A server that agreed MXP stays on
  MXP, as SharpMUTerm does. MXP/Pueblo state resets when the connection
  drops, so a reconnect negotiates it again.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rqqt7TbSP3xxcS6oaZwo3C
@HarryCordewener HarryCordewener changed the title chore(deps): upgrade TelnetNegotiationCore 2.5.3 → 4.6.1 Upgrade TelnetNegotiationCore to 4.6.1; truecolour, inline images, Pueblo via TNC Nov 6, 2026
@HarryCordewener
HarryCordewener marked this pull request as ready for review November 6, 2026 17:43
Copilot AI balanced review requested due to automatic review settings November 6, 2026 17:43
@HarryCordewener
HarryCordewener merged commit 8384309 into master Nov 6, 2026
3 checks passed

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

An unresolved critical flaw allows unsolicited Pueblo activation to enable command links without a client announcement, even when Pueblo was declined in favor of MXP.

5 open issues
What changed in this PR

Upgrades SharpClient to TelnetNegotiationCore 4.6.1 and extends its MUD/MUSH output rendering with truecolour and inline images.

Changes:

  • Updates dependencies, declares client identity, and adapts window-size reporting to the new negotiation API.
  • Adds RGB colour rendering and filtered MXP/Pueblo images, including command-linked images.
  • Moves Pueblo detection into the connection layer, resets markup state on disconnect, and adds regression tests.
File Description
tests/​SharpClient.UI.Tests/​OutputViewLinkTests.cs Tests image attributes and clickable images.
tests/​SharpClient.Tests/​SharpClient.Tests.csproj Updates test dependency versions.
tests/​SharpClient.Tests/​Sessions/​SessionMxpTests.cs Tests Pueblo events and markup-state reset.
tests/​SharpClient.Tests/​Sessions/​FakeTelnetConnection.cs Adds simulated Pueblo activation.
tests/​SharpClient.Tests/​Rendering/​SegmentStyleTests.cs Tests RGB-to-CSS conversion.
tests/​SharpClient.Tests/​Rendering/​InlineImageTests.cs Tests image parsing, URL filtering, and links.
tests/​SharpClient.Tests/​Rendering/​AnsiParserTests.cs Tests truecolour parsing and edge cases.
tests/​SharpClient.Tests/​Connection/​ClientIdentityAndNawsTests.cs Tests terminal identity and window-size negotiation.
src/​SharpClient.Web/​SharpClient.Web.csproj Upgrades the web host’s TNC dependency.
src/​SharpClient.UI/​wwwroot/​app.css Adds image sizing and image-button styles.
src/​SharpClient.UI/​SharpClient.UI.csproj Updates dependency injection abstractions.
src/​SharpClient.UI/​Components/​OutputView.razor Renders images and command-linked image buttons.
src/​SharpClient.Core/​SharpClient.Core.csproj Upgrades TNC and logging abstractions.
src/​SharpClient.Core/​Sessions/​Session.cs Uses Pueblo events and clears connection-specific markup state.
src/​SharpClient.Core/​Rendering/​StyledSegment.cs Adds inline-image URL metadata.
src/​SharpClient.Core/​Rendering/​SegmentStyle.cs Converts RGB colours to CSS hex values.
src/​SharpClient.Core/​Rendering/​MxpParserState.cs Clarifies the Pueblo-state documentation.
src/​SharpClient.Core/​Rendering/​ImageSource.cs Resolves and filters image URLs.
src/​SharpClient.Core/​Rendering/​AnsiParser.cs Parses truecolour sequences and image tags.
src/​SharpClient.Core/​Rendering/​AnsiColor.cs Adds clamped RGB colour representation.
src/​SharpClient.Core/​Connection/​WindowSizeProtocol.cs Caches window dimensions until negotiation permits reporting.
src/​SharpClient.Core/​Connection/​TelnetConnection.cs Configures identity, protocols, and Pueblo callbacks.
src/​SharpClient.Core/​Connection/​ITelnetConnection.cs Exposes Pueblo activation notifications.
src/​SharpClient.App/​SharpClient.App.csproj Upgrades TNC and revises AOT/trimming comments; settings remain unchanged.

Review effort: Balanced


private ValueTask OnPuebloEnabledAsync(PuebloClient client)
{
PuebloEnabled?.Invoke();
onMXPEnabled: OnMxpEnabledAsync,
charsetOrder: CharsetPreference)
.WithClientIdentity(Identity)
.AddPlugin(new WindowSizeProtocol(_windowSize))
Comment on lines +46 to +50
Flush();
segments.Add(new StyledSegment(label, style)
{
ImageUrl = url,
Command = pendingCommand,
Comment on lines +315 to +337
var isNamed = false;
while (i < body.Length && body[i] is not (' ' or '\t'))
{
if (body[i] == '=')
{
isNamed = true;
i++;
if (i < body.Length && body[i] is '"' or '\'')
{
var quote = body[i++];
while (i < body.Length && body[i] != quote)
{
i++;
}

i++;
}

continue;
}

i++;
}
@foreach (var segment in line.Segments)
{
@if (segment.Command is not null && OnSendCommand is not null)
@if (segment.ImageUrl is not null)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants