feat(ui): #17 ChatPanel input field + slash commands + reply-to-last-tell
ChatPanel gains an Enter-to-submit input field via the I.1 InputTextSubmit widget. Submitted text routes through ChatInputParser to a SendChatCmd published on ctx.Commands; LiveCommandBus (I.3) handles the wire send + ChatLog echo. Recognised prefixes (ported from holtburger commands.rs): /say msg or no prefix -> Say /t Name msg or /tell -> Tell (first whitespace token = target) /r msg -> Tell (target = LastIncomingTellSender) /g msg -> General /f msg -> Fellowship /a msg -> Allegiance /m msg -> Monarch /p msg -> Patron /v msg -> Vassals /cv msg -> CoVassals /lfg msg -> Lfg /trade msg -> Trade /role msg -> Roleplay /society msg -> Society /olthoi msg -> Olthoi Edge cases: empty / whitespace / cmd-without-message / /r without prior tell -> null (no-op). Unknown /xyz prefix -> Say with literal text (matches holtburger's Talk(command) default arm). ChatVM.LastIncomingTellSender populated only on incoming Tell entries; discriminated by SenderGuid != 0 (OnSelfSent echoes always carry 0). 32 new tests: - ChatInputParserTests: 22 covering every prefix + edge case - ChatVMLastTellSenderTests: 6 covering capture + skip rules - ChatPanelInputTests: 6 using FakePanelRenderer + recording ICommandBus to assert publish behaviour UI.Abstractions.Tests: 60 -> 92. Solution total: 934 green. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
8e6e5a0b61
commit
f14296c75f
6 changed files with 710 additions and 10 deletions
|
|
@ -28,6 +28,19 @@ public sealed class ChatVM
|
|||
private readonly ChatLog _log;
|
||||
private readonly int _displayLimit;
|
||||
|
||||
/// <summary>
|
||||
/// Sender name of the most recent INCOMING Tell. Drives the
|
||||
/// <c>/r</c> reply slash command in <see cref="ChatInputParser"/>.
|
||||
/// Null until the first Tell arrives. Outgoing self-sent Tell
|
||||
/// echoes (which run through <see cref="ChatLog.OnSelfSent"/>) do
|
||||
/// NOT update this — we discriminate by <c>SenderGuid != 0</c>;
|
||||
/// only real inbound tells from <see cref="ChatLog.OnTellReceived"/>
|
||||
/// carry a non-zero guid. Mirrors holtburger
|
||||
/// <c>chat.rs::ChatState::last_incoming_tell_sender</c> (line 74 +
|
||||
/// the assignment at line 152).
|
||||
/// </summary>
|
||||
public string? LastIncomingTellSender { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Build a ChatVM bound to a <see cref="ChatLog"/> instance.
|
||||
/// </summary>
|
||||
|
|
@ -43,6 +56,20 @@ public sealed class ChatVM
|
|||
if (displayLimit < 1)
|
||||
throw new ArgumentOutOfRangeException(nameof(displayLimit), displayLimit, "must be >= 1");
|
||||
_displayLimit = displayLimit;
|
||||
_log.EntryAppended += OnEntryAppended;
|
||||
}
|
||||
|
||||
private void OnEntryAppended(ChatEntry entry)
|
||||
{
|
||||
// Only INCOMING tells update the reply target. Self-sent
|
||||
// echoes from OnSelfSent always carry SenderGuid == 0 (we
|
||||
// never know our own guid here), so guid == 0 is a safe
|
||||
// discriminator for "skip this one". An incoming tell from
|
||||
// a player always carries the sender's real guid.
|
||||
if (entry.Kind != ChatKind.Tell) return;
|
||||
if (entry.SenderGuid == 0) return;
|
||||
if (string.IsNullOrEmpty(entry.Sender)) return;
|
||||
LastIncomingTellSender = entry.Sender;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue