feat(input): warn and hold before sending plugin-only symbols
This commit is contained in:
@@ -2,8 +2,10 @@ using System.Numerics;
|
||||
using System.Text;
|
||||
using Dalamud.Bindings.ImGui;
|
||||
using Dalamud.Interface;
|
||||
using Dalamud.Interface.Colors;
|
||||
using Dalamud.Interface.Utility;
|
||||
using Dalamud.Interface.Utility.Raii;
|
||||
using HellionChat._Helpers;
|
||||
using HellionChat.Code;
|
||||
using HellionChat.GameFunctions;
|
||||
using HellionChat.Resources;
|
||||
@@ -42,6 +44,12 @@ internal sealed class InputBar
|
||||
private bool _wasInputTextHovered;
|
||||
private bool? _isFocusedOverride; // Test-only; null = honour per-frame Draw() value.
|
||||
|
||||
// UI-11 plugin-disclosure arm-and-hold: holds the buffer that armed the
|
||||
// disclosure warning. null = not armed. Compared by value so an edit
|
||||
// re-arms and a resend on the identical buffer goes through. 1.5.6 parity
|
||||
// (ChatInputBar 1d3b429:27).
|
||||
private string? _disclosureArmedBuffer;
|
||||
|
||||
// Auto-translate popup state — lives here because the popup lifecycle is
|
||||
// tightly coupled to the input callback and the pending message buffer.
|
||||
private const string AutoCompleteId = "##hellion-at-complete";
|
||||
@@ -158,6 +166,21 @@ internal sealed class InputBar
|
||||
ImGui.SameLine();
|
||||
DrawQuickButtons();
|
||||
|
||||
// UI-11: yellow inline warning while a plugin-only-glyph message is
|
||||
// armed-and-held (buffer unchanged since it armed). Renders on its own
|
||||
// line below the input row. 1.5.6 parity (ChatInputBar 1d3b429:93-103).
|
||||
if (
|
||||
Plugin.Config.NotifyPluginDisclosure
|
||||
&& _disclosureArmedBuffer is not null
|
||||
&& _pendingMessage == _disclosureArmedBuffer
|
||||
)
|
||||
{
|
||||
ImGui.TextColored(
|
||||
ImGuiColors.DalamudYellow,
|
||||
HellionStrings.ChatInput_PluginDisclosure_Warning
|
||||
);
|
||||
}
|
||||
|
||||
// SymbolPicker popup is rendered last so it can splice its fragment
|
||||
// straight into the pending buffer.
|
||||
var inserted = _symbolPicker.DrawAndConsume();
|
||||
@@ -334,6 +357,31 @@ internal sealed class InputBar
|
||||
if (string.IsNullOrEmpty(text))
|
||||
return;
|
||||
|
||||
// UI-11: plugin-disclosure arm-and-hold. Arm + scan on the RAW
|
||||
// _pendingMessage (NOT the trimmed `text`) so the Draw warning gate
|
||||
// (_pendingMessage == _disclosureArmedBuffer) matches byte-for-byte even
|
||||
// when the buffer has leading/trailing whitespace. 1.5.6 armed/held/
|
||||
// warned on the raw buffer and only trimmed at SendChatBox; storing the
|
||||
// trimmed value here would silently kill the warning for a padded buffer
|
||||
// (the Draw gate compares the untrimmed _pendingMessage). Runs BEFORE the
|
||||
// channel prefix + AutoTranslate.ReplaceWithPayload (the resolved <at:>
|
||||
// macro carries its own non-ASCII bytes and would false-positive;
|
||||
// whitespace is never a PUA codepoint, so scanning the raw buffer is
|
||||
// equivalent for detection). First Enter on a buffer with a plugin-only
|
||||
// PUA glyph arms + HOLDS (returns without sending, buffer kept); a second
|
||||
// Enter on the same unchanged buffer sends; editing re-checks. 1.5.6
|
||||
// parity (ChatInputBar.SubmitCompact 1d3b429:108-118).
|
||||
if (
|
||||
Plugin.Config.NotifyPluginDisclosure
|
||||
&& _disclosureArmedBuffer != _pendingMessage
|
||||
&& PluginDisclosureScanner.ContainsPrivateUseGlyph(_pendingMessage)
|
||||
)
|
||||
{
|
||||
_disclosureArmedBuffer = _pendingMessage;
|
||||
return;
|
||||
}
|
||||
_disclosureArmedBuffer = null;
|
||||
|
||||
// Slash commands route through verbatim — the game's chat parser
|
||||
// handles /tell, /fc, /hellion etc. on its own. Other text gets
|
||||
// the active channel's prefix so the line lands on the channel
|
||||
@@ -417,6 +465,21 @@ internal sealed class InputBar
|
||||
// override and let Draw()'s ImGui.IsItemFocused() result take over again.
|
||||
internal void TestSetFocusedForSelfTest(bool? value) => _isFocusedOverride = value;
|
||||
|
||||
// Test-only hook; do not call from production code. Drives the REAL TrySend
|
||||
// arm path: with NotifyPluginDisclosure on and a PUA glyph in the buffer the
|
||||
// first call arms and HOLDS (no send). Returns whether the buffer is armed.
|
||||
// The caller asserts PendingMessage is unchanged (held) so a regressed wiring
|
||||
// that fell through to ChatBox.SendMessageUnsafe is caught.
|
||||
internal bool TestTryArmDisclosureForSelfTest(Tab? activeTab)
|
||||
{
|
||||
TrySend(activeTab);
|
||||
return _disclosureArmedBuffer is not null;
|
||||
}
|
||||
|
||||
// Test-only hook; do not call from production code. Clears the armed buffer
|
||||
// so a SelfTest leaves no residual arm state.
|
||||
internal void TestResetDisclosureForSelfTest() => _disclosureArmedBuffer = null;
|
||||
|
||||
private void DrawAutoCompletePopup()
|
||||
{
|
||||
if (_autoCompleteInfo == null)
|
||||
|
||||
@@ -56,6 +56,16 @@ internal sealed class ChatTab
|
||||
{
|
||||
DrawCommandHelpSideCombo();
|
||||
}
|
||||
|
||||
if (ImGui.CollapsingHeader("Plugin disclosure"))
|
||||
{
|
||||
DrawToggle(
|
||||
HellionStrings.Settings_Chat_NotifyPluginDisclosure_Name,
|
||||
() => Plugin.Config.NotifyPluginDisclosure,
|
||||
v => Plugin.Config.NotifyPluginDisclosure = v
|
||||
);
|
||||
ImGuiUtil.HelpMarker(HellionStrings.Settings_Chat_NotifyPluginDisclosure_Description);
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawPrivacyPersistChannels()
|
||||
|
||||
Reference in New Issue
Block a user