Skip to content

feat: show the server's prompt on a row above the command line - #39

Merged
HarryCordewener merged 2 commits into
mainfrom
feat/prompt-row
Aug 20, 2026
Merged

feat: show the server's prompt on a row above the command line#39
HarryCordewener merged 2 commits into
mainfrom
feat/prompt-row

Conversation

@HarryCordewener

Copy link
Copy Markdown
Member

What

Follow-on from #38. That took the TelnetNegotiationCore fix so an IAC GA prompt boundary finally arrives, and TelnetSession flushes the buffered prompt for it — but the prompt was still displayed nowhere.

WorldSession.OnOutputReceived routes an IsPrompt event to CurrentPrompt and returns before ProcessOutputLine, so it never reaches a pane. And nothing read the property:

  • grep -rn "CurrentPrompt" src/ returned two hits — the declaration and the assignment.
  • PromptChanged had one subscriber, SharpMUTermApp.cs: session.PromptChanged += (_, _) => OnUi(UpdateStatus); — both arguments discarded.

The routing is right and stays. A game ending every turn with HP:100 MP:50 > would double its own output into the scrollback and the transcript, which is what Prompt_UpdatesCurrentPrompt_WithoutScrollback pins. What was missing was a reader, so this adds one.

The row

PromptRowRenderer plus a sticky row directly above the command line, hidden until a prompt arrives.

  • Always exactly one row, elided rather than wrapped. Every sticky row is reserved before the workspace is measured, and per-pane NAWS is derived from the pane rectangles — so a band whose height followed whatever the server last sent would re-announce a new terminal size to every connected game on every prompt. Same rule as the rail's reserved badge fields and the status row's capped scrollback count: chrome measured from wire data must not change size. The row appearing at all still costs one row once per session.
  • Painted on the untinted idle input band, not the armed one: the armed band means "⏎ sends from here" and the prompt row is not somewhere you can type. Untinted because the bar immediately below already wears the focused character's hue.
  • MarkupFormatter gained an optional plane for it. The contrast floor is only meaningful against the fill the ink lands on, and this is the one place the client paints the game's colours outside a pane.
  • Resolved through WindowSession, never _active, so a background pane's prompt is never shown above a command line aimed elsewhere.
  • Cleared on disconnect. A prompt is a live connection's question; left standing it is a dead server's question above a command line that cannot answer it. Cleared before the state change so nothing repainting on StateChanged sees the two disagree, and raised only when there was something to clear.

StyledText.Truncate is new and does the eliding on the styled line rather than on the markup — cutting markup by character count lands inside a [#rrggbb] or orphans a [/].

Verification

All five suites green: Core 950, Tui 1828, Graphics 83, Scripting 42, Web 37, zero failures.

Backed by a rendered frame rather than by reading markup: a new --view prompt snapshot, added to FrameContrastTests' list so the band is measured on all three themes — the game's own colours land on a fill nothing else in the client uses.

dotnet run -c Release --project src/SharpMUTerm.Tui --no-build -- \
--snapshot --demo-config --view prompt --size 120x32 --out frame.ansi

Both behavioural fixes were confirmed to fail without their change: deleting the ClearPrompt() call fails Prompt_IsClearedOnDisconnect, and the first render put the text on the bare backdrop, which is what caught the band being claimed in a doc comment but not drawn.

🤖 Generated with Claude Code

TelnetNegotiationCore 2.11.0 made the GA boundary arrive and TelnetSession
flushed it, but the prompt was still displayed nowhere: WorldSession routes an
IsPrompt event to CurrentPrompt rather than to ProcessOutputLine, and grep found
that property written in one place and read in none. PromptChanged had a single
subscriber, `(_, _) => OnUi(UpdateStatus)`, which discards both arguments. The
prompt was being received perfectly and painted by nobody.
The routing is right and stays: a game ending every turn with HP:100 MP:50 >
would double its own output into the scrollback and the transcript, which is
what Prompt_UpdatesCurrentPrompt_WithoutScrollback pins. What was missing was a
reader, so this adds one.
Always exactly one row, elided rather than wrapped. Sticky rows are reserved
before the workspace is measured and per-pane NAWS is derived from the pane
rectangles, so a band whose height followed whatever the server last sent would
re-announce a new terminal size to every connected game on every prompt — the
same rule the rail's reserved badge fields already follow.
Painted on the untinted idle input band, not the armed one: the armed band means
"enter sends from here" and the prompt row is not somewhere you can type. The
band is why MarkupFormatter takes an optional plane — the contrast floor is only
meaningful against the fill the ink lands on, and the game's own colours land
here rather than on a pane.
Resolved through WindowSession, never _active, so a background pane's prompt is
never shown above a command line aimed elsewhere.
StyledText.Truncate is new and does the eliding on the styled line rather than
on the markup: cutting markup by character count lands inside a [#rrggbb] or
orphans a [/].
Verified on a rendered frame (--view prompt, added to FrameContrastTests so the
band is measured on all three themes), not by reading the markup.
948 / 1848 / 83 / 42 / 37, all green.
A prompt is a live connection's question. CurrentPrompt was set and never
cleared, so after a disconnect the row kept a dead server's question above a
command line that could no longer answer it — and its own doc already said "or
null if none is active", which was never true once one had arrived.
Cleared before the state change, so anything repainting on StateChanged already
sees no prompt and the two never disagree for a frame. Raised only when there
was something to clear: a session that disconnects without ever having been
prompted must not report a change it did not make.
OnDisconnected is the only place it is needed — TelnetSession raises
Disconnected from the finally of its read loop, so a clean close and a faulted
one both arrive there.
@coderabbitai

Copy link
Copy Markdown

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your current included review allowance is based on your included PR review attempts over the past 7 days.

Next review available in:52 minutes

Limit details: You’ve used the included review currently available. Your 69 included PR review attempts over the past 7 days set your current allowance at 1 review per hour.

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

Wait for the limit to reset, then comment @coderabbitai review or push new commits to the PR.

An organization admin can change what happens after included review limits in Billing.

How do review limits work?

CodeRabbit enforces per-developer PR review limits within each organization.

For paid Pro and Pro+ reviews, CodeRabbit uses a developer's included PR review attempts over the past 7 days to set the current hourly allowance. At typical activity levels, the full plan allowance applies. Higher sustained activity can lower the allowance until earlier attempts leave the 7-day window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 097d2eff-d9a1-423d-b227-3201cb21f5a0

📥 Commits

Reviewing files that changed from the base of the PR and between 4c2a331 and e9901da.

📒 Files selected for processing (9)
  • src/SharpMUTerm.Core/Session/WorldSession.cs
  • src/SharpMUTerm.Core/Text/StyledText.cs
  • src/SharpMUTerm.Tui/MarkupFormatter.cs
  • src/SharpMUTerm.Tui/PromptRowRenderer.cs
  • src/SharpMUTerm.Tui/SharpMUTermApp.cs
  • tests/SharpMUTerm.Core.Tests/Session/WorldSessionTests.cs
  • tests/SharpMUTerm.Core.Tests/Text/StyledTextTests.cs
  • tests/SharpMUTerm.Tui.Tests/FrameContrastTests.cs
  • tests/SharpMUTerm.Tui.Tests/PromptRowRendererTests.cs

Comment @coderabbitai help to get the list of available commands.

@HarryCordewener
HarryCordewener merged commit ee85411 into mainAug 20, 2026
3 checks passed
@HarryCordewener
HarryCordewener deleted the feat/prompt-row branch August 20, 2026 20:22
Sign up for freeto 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.

1 participant

@HarryCordewener