Commit Graph
29 Commits
Author SHA1 Message Date
JonKazama-Hellion 0f9858a3d3 fix(screenshot-mode): the toggle never wrote its change to disk
Screenshot mode is a persisted setting, but neither of the two toggles
saved the config. Turning it on only stuck when some unrelated save
happened to run afterwards -- and once it was stored, turning it off never
reached the file at all, so it came back on with every plugin load.

Both toggles save now. An install currently stuck on it needs one click.
2026-08-20 07:54:38 +02:00
JonKazama-Hellion 16557213cd chore: comments, second pass -- the task codes the first pass missed
The first sweep matched a character class that swallowed the digit, so a bare
B1 slipped through while B1-2 was caught. Searching the whole A-Z space instead
of guessing prefixes turned up 130-odd more: B0 through B6, C2, C3, D1, H2, M6,
P7, P8, T2, W2, plus GP-04, KB-01, OD-1, PM-1, PM-3, SEC-01, TR-4, TR-7, UI-11,
UI-12, XC-8 and API-3.

Kept deliberately: 41 B4 01 is a byte signature, "N0" a format string,
#L119-L128 a source anchor, LS4/LS6 are linkshells, and A=FF B=0C G=41 R=C2
explains a colour-channel order. Those look like codes and are not.

Also translated the eight German comments left in the theme files and
ImGuiUtil. Seven of them described what a palette does to which channel, which
is worth reading -- just not in a second language in an otherwise English
codebase.
2026-08-19 22:03:12 +02:00
JonKazama-Hellion 5b738e6885 chore: comments say what the code does, not which task produced it
A comment that reads "MUST stay in lockstep with TryGetActiveCrossfade (K8)"
helps nobody outside the plan that used to have a K8 in it, and the plans are
not in this repo. Same for "Spec FR-4", "plan §B.2", "Sub-Task 4.4" and the
F/R/M/A/S round codes scattered through the style engine and the self-tests.

Personal names go too. "tester feedback from Jin (v1.4.7)" and "Flo decision
2026-06-15" carry the reason fine without naming anyone -- the version and the
reason are the parts a reader can act on, and a public repo should not need a
cast list to be read.

The rule applied throughout: keep the why, drop the reference. Version numbers
stay, since those resolve through the changelog. 77 files.

ChunkUtil also carried 281 lines of commented-out code -- an older ToChunks
variant and two helpers with no callers, inherited and never removed. Deleted;
git remembers them.
2026-08-19 21:50:31 +02:00
JonKazama-Hellion 8a9692a71d fix(chat): size the popups from their rows, not the rows from a guess
An ImGui popup does not grow for draw-list content, so both new popups carried a
fixed minimum width -- and German outran it within a day. The menu clipped
Schnellauswahl and Chat ausblenden mid-word; the channel picker would have done
the same to Freie Gesellschaft on a narrow theme font.

PopupRow gets a CalcWidth that measures label plus icon plus padding under the
faces that will draw them, and both popups take the widest visible entry as
their width. Locale-proof by construction: whatever language writes the longest
string sets the size.
2026-08-19 19:13:30 +02:00
JonKazama-Hellion d7a308b522 feat(style): option three across the board, every colour contrast-bound
Flo picked the third variant in all four lab sections, with one warning attached:
bind the glyph and text colours to the contrast helper or the theme, or they
drown. The warning was well aimed -- the ghost buttons from the previous commit
were feeding RGBA into EnsureContrast, the same channel-order mistake the header
made this morning, and the pill text was raw TextPrimary on an accent fill with
no check at all. Every colour in the row now goes through EnsureContrast against
the surface it actually lands on.

What changed shape:

The icon buttons glow. Flat at rest, and on hover a soft fill with an accent
glow border rising on the held hover value -- DrawGlowBorder's first caller ever.
The lab version of that glow had its alpha in the wrong byte (DrawGlowBorder
reads RGBA, ApplyAlpha writes ABGR), so what Flo approved was a full-alpha glow
with a dimmed red channel. Fixed in both places, with the alpha byte set by hand.

The channel pill is chamfered, the segmented control's corner language, with the
white depth gradient kept. The rounded Pill widget stays untouched for the
status bar.

The channel header trades its fading rule for a tenth-opacity accent wash from
the top edge. Colour as atmosphere rather than as a box -- at this strength it
survives the violet themes that killed the filled bar in v1.11.0.

The lab stays in permanently, by Flo's call: a dev playground for seeing ideas
in-game against the live theme. Its radios now default to what shipped, so the
window doubles as a record of which variant won.
2026-08-19 19:04:05 +02:00
JonKazama-Hellion 6bdfecb1df feat(chat): the input row learns the window's own language
Five filled plates become two ghost buttons and a menu. The plates were ImGui
defaults sitting between a drawn pill and a drawn status bar -- three shape
languages in the one row a user works in, which is what the tester's screenshot
made obvious. The ghosts follow the sidebar's icon buttons: nothing at rest, a
held hover fill, the glyph lifting toward the accent.

Symbols stay outside the menu because they are used mid-sentence. Theme,
settings, screenshot and hide move in; the screenshot toggle was allowed in only
because its state moved to the status bar first, as an accent pill that shows
while the mode is on. A privacy state behind a closed menu answers nothing.

The input field paints its own rounded surface and hands ImGui a transparent
frame, so the widget draws only text and caret -- the Boutique.Inputs technique
from Character Select+, no rebuild of the widget itself. Focus is a two-pixel
accent rail on the left edge: the same mark the rows, the popups and the message
list already use for "this is where you are".

The channel pill gets the CS+ treatment, a faint white gradient and a one-pixel
light along the top edge. White over the fill rather than a second hue, so it
reads as depth in every palette.

And the popups that open from the row are made of PopupRow now -- the sidebar
row's shape, which is also exactly what CS+ draws for its own popup entries. The
channel picker was the reported case: a drawn pill opening a list of naked
selectables, the style breaking mid-click.

A hundred-odd pixels of button reserve go back to the input field.
2026-08-19 18:30:15 +02:00
JonKazama-Hellion 439c919d77 feat(chat): a button for screenshot mode, where it can actually be found
It has only ever lived in the right-click menu on a player name. For a privacy
feature that is the same as not existing -- reported as missing by a tester who
has been running the plugin for months and never found it.

Now a camera in the input row, next to hide-window, and lit in the accent colour
while active. A mode whose state you cannot see is worse than no mode: the whole
point is knowing whether the names on your screen are real before you press the
screenshot key.

The button reserve goes from 130 to 156 to fit it. No new string -- the context
menu's label is already translated into all 25 languages and says exactly what
the button does.
2026-08-19 17:13:06 +02:00
JonKazama-Hellion e8d06e05fa fix(input): put the right-click menu back on the input field
Reported by a tester: the input field in v1.5.6 had right-click actions,
the current one has no right-click at all.

Correct, and the commit before this one put those actions in the wrong
place. The v1.5.6 menu was ImRaii.ContextPopupItem("ChatInputContext")
bound to the input field itself; I hung the two token entries off the
chat-message menu, where the rest of that window's items had landed.
They are on the input field now, which is also where they are useful:
you insert a token while composing, not while reading.

Two details from the original that the misplaced version had lost:

- The agent pointers are null-checked. Both can be null during a zone
  transition, which is exactly when somebody is most likely to be typing
  a flag into party chat.
- Inserting refocuses the field and puts the caret behind the token.
  Picking from a menu and then having to click back into the field is
  the kind of friction that makes a feature go unused.

Hiding the chat is not repeated in the menu. It was in the v1.5.6 one,
but it has its own button two widgets to the right now, and one way in
is enough.
2026-08-18 22:45:36 +02:00
JonKazama-Hellion 06ef0bfb1c fix(input): let the arrow keys walk the sent-message history again
Reported against v1.11.0: type a command, send it, need it again, and
up-arrow does nothing. v1.5.6 recalled it.

Everything needed was already in the tree. InputHistoryService holds the
last thirty entries with move-to-newest dedup, CompactInputHistoryNavigator
owns the cursor maths, and both have their own test mirrors. Neither had
a single caller, and the input field never set CallbackHistory, so ImGui
had no reason to raise the event in the first place.

So: the flag goes on, the callback grows a history branch, and TrySend
pushes the trimmed line before it clears the buffer. Editing ends the
walk, otherwise down-arrow after changing a recalled line would jump to
the next entry and discard the edit.

The cursor is per input bar while the history is global. Which line each
window is looking at is not something the others should inherit.
2026-08-18 22:13:41 +02:00
JonKazama-Hellion d1e12ce866 fix(popouts): restore a way to close a pop-out
Last commit hid the header row when the title bar was on, to stop the tab name
appearing twice. The close button was in that row, and the title bar carries
none of its own -- ShowCloseButton is off because closing has to go through the
pool to release the slot. So a pop-out with its title bar on could not be closed
at all.

Pop-in moves into the input row, alongside the settings and hide buttons that
are already there. It is red, contrast-checked against the button plate it sits
on, and carries a tooltip, since a bare X next to an emoji picker does not say
where the tab is going.

Wired by setter after construction rather than through the constructor: the
window is what the button has to call, and it does not exist yet while its own
input row is being built. Routing it through the graph would close a
factory-callsite cycle MS.DI cannot detect.

The header row is now just the tab name, drawn only when there is no title bar
to carry it.

InputBar_PopIn_Tooltip ships in all 25 languages, including the Designer entry.
New strings get translated with the change from here on, not batched.
2026-08-18 19:21:24 +02:00
JonKazama-Hellion 2686a8f74b feat(i18n): localise the quick-button tooltips
The three quick-button tooltips were hard-coded English while the rest of the
UI goes through HellionStrings. All 25 resource files now carry them, machine
translated on Flo's go-ahead.

Verified: every file still parses as XML, and the base file stays English so a
missing language falls back to what was there before.
2026-08-18 10:09:23 +02:00
JonKazama-Hellion 30c495849d fix(ui): stop the context menu crashing and give the quick buttons their tooltips back
Two things Flo hit on the first run.

The context menu threw on EndPopup. The spacing guard added in 929188e was a
`using var`, which disposes at the end of the method -- after EndPopup. ImGui
asserts when a popup closes with a style var still on its stack. The body moved
into a scoped block so the pop happens inside the popup.

The quick-button tooltips were empty boxes. SetTooltip ran inside the
FontAwesome push, and that atlas has no ASCII glyphs, so the text had nothing to
render with. ImRaii.DefaultFont did not save it. The hovered label is collected
now and drawn after the font is popped. Same trap as the unread badge in C5,
different place.

Those three tooltip strings are still hard-coded English while the rest of the
UI is localised. Pre-existing, and adding resources means touching 24 language
files, so it is noted rather than fixed here.
2026-08-18 09:23:12 +02:00
JonKazama-Hellion bfc61909cf refactor(ui): scale the remaining layout constants
What blocks A to E did not already touch: the honorific header height and its
two offsets, the message list dummy widths, and the quick-button reserve in the
input bar.

The reserve is the one with visible consequences. At 150% the buttons grow with
the font while a fixed 130px column does not, so they stopped fitting.

The honorific offsets are centred rather than scaled. The 8f there was
(30 - 14) / 2 for the old font, structurally the same case as the sidebar: a
scaled constant keeps its mis-centering, a computed one does not.
2026-08-18 00:05:23 +02:00
JonKazama-Hellion f2dd49e454 refactor(input): move the channel pill onto the pill widget
The pill was already hand-drawn here -- filled rect, rounding 6, a frozen 3px
text offset and an InvisibleButton over the top. That is the Pill widget, built
before the widget existed, so it becomes the widget's first consumer. Its text
now centres against the measured line height instead of the frozen offset.

InputBar.Height moves to Metrics as well. MainWindow and ChannelPopoutWindow
both reserve their body height against it, so both follow without changes. It
had to happen in the same commit: the pill inside the bar now scales, and a bar
that did not would have clipped it at 150%.
2026-08-18 00:00:15 +02:00
JonKazama-Hellion a73f4d0d0c feat(window): restore hide-chat-window + Enter-to-restore (1.5.6)
The eye/hide button now hides the HellionChat window (runtime-only, via a new
DrawConditions gate) instead of toggling native-chat suppression, matching 1.5.6.
The chat-activation keybind (Enter / "/"), whose dispatch was a dead stub in the
KeybindManager since the v1.6.0 rewrite, is re-wired to MainWindow.ActivateChat:
it un-hides, opens if closed, brings the window to front and focuses the input --
so the chat reacts to Enter again from any state. /hellion is a reliable one-press
recovery (Toggle now clears the hide), and the window always shows on login
(start state no longer read from the persisted flag). Adds HideRestoreSelfTestStep.
2026-06-15 20:49:14 +02:00
JonKazama-Hellion b397591ba4 feat(window): restore the title-bar, hide-button, and 24-hour-clock toggles
Re-wires four 1.5.6 settings that survived the v1.6.0 rewrite as dormant config
fields but lost their UI + consumers:
- ShowTitleBar / ShowPopOutTitleBar: gate ImGuiWindowFlags.NoTitleBar on the main
  window (ResolveFlags) and pop-out windows (new PreDraw). Inverted logic matches
  1.5.6 (flag set only when the toggle is off).
- ShowHideButton: gate the input-bar hide button on the toggle.
- Use24HourClock: add the toggle (MessageList already consumes the field).
New 'Window style' section in WindowTab; Use24HourClock in ChatTab display modes.
MainWindowFlagsStep extended with the NoTitleBar fresh-base contract.
2026-06-15 20:19:59 +02:00
JonKazama-Hellion 6813b80d58 feat(themes): restore the header theme/tab quick-picker
Brings back the 1.5.4 quick-picker lost in the v1.6.0 rewrite: a palette button
in the input-bar button row (left of the cog) opens a popup that switches the
theme (built-in + custom, active row checked) and jumps between chat tabs without
opening settings. Theme switch mirrors the settings ThemePicker; the tab jump
routes through a new MainWindow.ActivateTab that replays the click path
(previous -> set -> OnTabActivated) so tell/unread handling is unchanged. Main
window only -- pop-out InputBars get a null picker. Adds QuickPickerSelfTestStep.
2026-06-15 19:53:31 +02:00
JonKazama-Hellion d40b120b91 fix(tell): restore outgoing tell routing from the input bar
Input-bar tells went out as a bare "/t" without the target, so the game
rejected them with "you must add the World name". Rebuild the full
"/tell name@world" from the 1.5.6 target chain in a pure BuildOutgoing:

- leg2/leg3 gated on current == Tell so a stale tell target on a Say tab
  can't send a say line silently as /tell (CORR-1)
- world-resolve gate: an unresolvable world falls back to the channel
  prefix, never "/tell name@ text" (COMP-1)
- ResetTempChannel after the send, tell-only

Also clear the runtime tell state on PromoteToPermanent so a promoted tab
can't route a typed line to the old partner, and surface the tell partner
("-> name@world") in the channel pill so a misfire stays visible. Adds
tell-routing and pill-transparency self tests.
2026-06-04 17:11:32 +02:00
JonKazama-Hellion b0bee25770 feat(input): warn and hold before sending plugin-only symbols 2026-05-31 00:45:31 +02:00
JonKazama-Hellion b221a6e418 feat(input-bar): wire auto-translate tab picker + payload-replace on send
v1.7.0 component-layer refactor removed ChatLogWindow.cs (which housed
the auto-translate popup) and dropped Ui/AutoCompleteInfo.cs without
migrating the logic into the new InputBar component — v2.x spec §3
said "LÖSCHEN + Logik migrieren", but only the deletion happened.
Result: Tab key did nothing in v1.7.1, and even manually typed
<at:group,key> tokens were never resolved into real auto-translate
payloads on send.

Root cause confirmed empirically via VN-1 diagnostic build
(_logger.LogDebug in SlashCommandCallback proved CallbackCompletion
fires on Tab once the flag is set). Following the diagnose-zuerst
pattern established by Issue #3 to avoid the source-code-only
hypothesis trap from Issue #2.

Migration follows v1.5.6 ChatLogWindow.DrawAutoComplete + ChatTwo
upstream AutoCompleteHandler patterns, but ported to v1.7.0 stil:

- ImGuiInputTextFlags extended with CallbackCompletion (Tab trigger)
  and CallbackAlways (cursor restore via _activatePos analog v1.5.6
  ActivatePos)
- SlashCommandCallback now dispatches three branches: CallbackAlways
  (cursor restore), CallbackCompletion (Tab → word-boundary search
  via Encoding.UTF8.GetString on the byte span, char-offset DTO
  construction to avoid the byte-vs-char drift in v1.5.6's raw
  pointer arithmetic), CallbackEdit (existing slash-command help
  detection, now properly scoped)
- 7 new private state fields (_autoCompleteInfo, _autoCompleteOpen,
  _autoCompleteList, _fixCursor, _autoCompleteSelection,
  _autoCompleteShouldScroll, _activatePos)
- DrawAutoCompletePopup renders the picker at the end of Draw():
  IsWindowAppearing seeds _fixCursor + focus, ListClipper-wrapper
  from Util/SearchSelector.cs (IDisposable, automatic Destroy) for
  the result list, Ctrl+0-9 quick-pick, Enter/Escape handling,
  char-splice commit (_pendingMessage = before + replacement + after)
- AutoCompleteCallback handles popup-input-field fix-cursor seeding,
  Up/Down navigation with wrap-around, Tab cycle in the default case
- TrySend now runs AutoTranslate.ReplaceWithPayload(ref bytes) and
  sends via ChatBox.SendMessageUnsafe(byte[]) with a manual 500-byte
  guard, because SendMessage(string) would route through SanitiseText
  which destroys the binary SeString macro bytes that
  ReplaceWithPayload emits
- AutoCompleteInfo DTO added as sealed internal companion type at the
  end of InputBar.cs (15 LOC, exclusively consumed by InputBar);
  ToComplete is a mutable field rather than auto-property so it can
  be passed as ref to ImGui.InputTextWithHint without CS0206

Verified in-game (Flo): Tab on empty input opens picker with full
list, "fire" + Tab filters correctly, Up/Down/Tab navigate, Enter
commits <at:group,key>, send resolves to real auto-translate payload
in chat, Ctrl+0-9 quick-pick works, Escape closes without commit.
dotnet build clean, dotnet csharpier check clean.

Single minor plan-drift: scroll-to-selected uses
ImGui.SetScrollY(selection * lineHeight) instead of
SetScrollFromPosY(clipper.StartPosY) because the local
ListClipper-wrapper does not expose StartPosY — same UX effect.
2026-05-28 17:23:11 +02:00
JonKazama-Hellion 29fb4b92eb fix(input-preview): wire Inside-mode + Tooltip-mode render paths
InputPreview was only rendered for PreviewPosition.Top/Bottom (the
DrawConditions IsWindowMode gate). Inside-mode (the default) and
Tooltip-mode had no caller at all because v1.5.6's inline-render path
lived on the deleted ChatLogWindow and was not migrated to the v1.7.0
Components-Layer.

Wire Inside-mode by calling CalculatePreviewHeight + DrawPreview
inline from MainWindow.DrawMainArea between the message-list child
and the input bar, with the message-list height reserved for the
preview block. Wire Tooltip-mode by sampling IsItemHovered() on the
input text widget inside InputBar.DrawInputField (analog to the
existing _isFocused = ImGui.IsItemFocused() idiom on the same line)
and exposing it as WasInputTextHovered; MainWindow opens the tooltip
after _input.Draw when both the hover-flag and PreviewPosition.Tooltip
are active.

Plan-drift acknowledged: the plan stated Plugin.InputPreview is
statically reachable, but the property was declared as an instance
member on Plugin.cs:101. Hoisted to internal static to match the
plan's intention (analog to Plugin.Config); updated the single
external instance-access site in PluginLifecycle.RegisterWindows
to the type-qualified form.

Verified in-game: Inside-mode preview block appears between message
list and input bar on first keystroke; tooltip-mode shows preview on
text-field hover only; Top/Bottom-mode unchanged; empty buffer hides
the preview in all modes. dotnet build clean, dotnet csharpier check
clean.
2026-05-28 13:21:59 +02:00
JonKazama-Hellion f6749d206b chore(polish): cycle-end sweep — drop dead fields, dep-cycle, comments
Accumulated polish across the v1.7.1 R-Block reviewer findings. Single
sweep before Phase-3 Smoke-Gate.

Dep-cycle cleanup (Block H + #30):
- CommandHelpWindow drops the dead _inputBar ctor-param + discard that
  was J's speculative prep; this eliminates the InputBar <-> CommandHelpWindow
  ctor cycle at its root
- InputBar replaces Lazy<CommandHelpWindow> wrapper with direct
  CommandHelpWindow ctor-param now that the cycle is broken
- PluginHostFactory InputBar + CommandHelpWindow DI-regs simplified

Dead-field removals:
- MessageList drops _themes + _resolver (no reads after H's render-path
  swap to _chunkRenderer.DrawChunks)
- InputBar drops FocusedPreview (no consumer wiring in the new architecture)
- InputPreview drops SelectedCursorPos (v1.5.6 letter-by-letter renderer
  artifact, no callers in R1)
- InputPreview drops WhitespaceRegex + partial keyword on class (dead
  GeneratedRegex with no callers)

Visibility fixes:
- InputPreview + CommandHelpWindow + DebuggerWindow ctors flip
  public -> internal for consistency with internal sealed class declarations

DI helper extraction:
- PluginHostFactory MakePayloadHandler private static helper DRYs the
  7-arg list shared between PayloadHandler-singleton and Lender<T> factory

ImGui-rendering fix:
- MessageList.DrawCompactRow uses SameLine(0f, 0f) — eliminates visible
  ItemSpacing.X gap between sender-prefix and chunk content

Bug fixes:
- PayloadHandler.LeftClickPayload drops spurious unsafe keyword (no
  pointer ops in the method body; v1.5.6 had no unsafe here)
- PayloadHandler.StringifyMessage Aggregate seeded with string.Empty to
  fix empty-sequence crash for pure-icon messages
- PayloadHandler.MoveTooltip args==null LogWarning template simplified
  (?.GetType().Name was always null after the null-check — misleading)
- InputBar.SlashCommandCallback drops redundant BufTextLen==0 guard
  (BufTextSpan handles empty correctly)

Comment improvements (WHY-not-WHAT):
- ImGuiUtil.cs payload-state cluster comment moved below Buttons array
- PayloadHandler: §6.9 trimmed to 1 line, FindCharacterForPayload
  documented, hq symbol marker restored, MoveTooltip guard documented
  as defensive v1.7.1 addition, NativeItemTooltips branch explained,
  §4.2 theme colour swap explained
- DebuggerWindow class comment mentions PayloadHandler counters section
- InitHostedServices StopAsync explains params-overload semantics
- InputBar AppendPending null policy vs SetPendingMessage documented,
  CommandManager leading-slash assumption noted
- PluginHostFactory block comment explains singleton+Lender split

Build: 0 warnings, 0 errors. csharpier: clean. Version unchanged.
2026-05-27 23:42:28 +02:00
JonKazama-Hellion cba6a16f8e feat(input-bar): wire slash-command callback + AllCommands (J2)
J2 closes the trigger-gap discovered in J review (2026-05-27): J
migrated CommandHelpWindow as a window but the v1.5.6 trigger-path
was never ported. J2 restores it:

- InputBar.cs adds ImGuiInputTextFlags.CallbackEdit + character-level
  callback that reads data.BufTextSpan, detects /-prefix, extracts
  command word, and calls _commandHelpWindow.Value.UpdateContent(desc)
- AllCommands.cs (new file, 1:1 port from v1.5.6) populates a static
  Dictionary<string, TextCommand> from Sheets.TextCommandSheet at
  startup; Plugin.CommandManager.Commands is the fallback for
  non-hardcoded commands
- CommandHelpWindow injected into InputBar via Lazy<T> ctor param to
  break the InputBar <-> CommandHelpWindow circular dep; PluginHostFactory
  DI-reg extended with the Lazy wrapper accordingly

Closes the smoke-step-9 gap. Phase-3 windows are now all reachable
end-to-end (R1 InputPreview, R2 CommandHelpWindow, R3 DebuggerWindow).
2026-05-27 23:19:21 +02:00
JonKazama-Hellion 01fc69efda feat(input-bar): add SetPendingMessage/AppendPending mutators + Activate/FocusedPreview flags
Replaces v1.5.6's direct LogWindow.Chat mutation pattern with typed mutators
that LogWarning + clip/drop on BufferCapacity overflow (silent-overwrite
semantics preserved, but overflow is now observable via /xllog).

Plumbing for v1.7.1 PayloadHandler resurrection — DrawPlayerPopup (tell-
prefix) and DrawStatusPopup (status-link append) will call these mutators
instead of mutating a public field.
2026-05-27 07:59:39 +02:00
JonKazama-Hellion e01de0403a feat(input): expose state API, wire settings cog, add test hooks 2026-05-26 13:31:00 +02:00
JonKazama-Hellion 2f099fd4e1 fix(ui): clickable channel pill, auto-seed channel, kill outer scrollbar
Three smoke bugs from the second in-game test:

1. The channel pill was draw-list only, so it didn't react to clicks
   and there was no way to switch channels inside a tab. The pill now
   has a hit area on top and opens a popup that lists every ChatType
   in tab.SelectedChannels with a ToInputChannel mapping; selecting
   one writes through CurrentChannel.SetChannel.

2. Switching to Allgemein / Gruppe / Linkshell still showed "—"
   because tab.CurrentChannel.Channel stayed at Invalid until somebody
   set it. The sidebar now seeds CurrentChannel on tab activation by
   walking SelectedChannels for the first key with a valid mapping,
   so every tab opens with its own real channel instead of inheriting
   the FC default.

3. MainWindow still surfaced an outer scrollbar next to the message
   list's own scroll. Adding NoScrollbar + NoScrollWithMouse to the
   window flags strips the second bar — the body child owns scroll on
   its own.

Plus the system-icon path: System / BattleSystem / GatheringSystem /
Error / Notice / LootNotice all map to fa-cog now, so the System tab
renders the gear instead of falling back to the generic comment.
2026-05-23 20:58:50 +02:00
JonKazama-Hellion 52b0fa7c67 fix(ui): wire chat send and fix sidebar icons, channel pill, scrollbar
Five smoke bugs from the first in-game test:

1. Sidebar showed fa-comment for every tab because the resolve path
   only honoured tab.Icon. Channel-type fallback restored — auto-tell
   tabs render the envelope, the rest map their first SelectedChannels
   key onto FontAwesome (Linkshells → link, FC → users, Party →
   user-friends, System/Echo → cog, emotes → comments).

2. InputBar's channel pill read from tab.Channel (the saved default),
   which is null on most non-FC tabs and rendered as "—". The pill now
   reads tab.CurrentChannel.Channel first so the runtime input state
   surfaces on every tab, with the saved default as a second fallback.

3. MessageList was making its own ImRaii.Child inside the main-area
   child MainWindow already owns. That nested scroll created the second
   scrollbar on the outer window. The component now lays out directly
   into the parent's scroll region.

4. The input field reserved 90px for the three FontAwesome buttons,
   which clipped them on standard frame padding. Reserve raised to
   130px so the trailing buttons fully render.

5. Pressing Enter dropped the buffer — there was no send wiring. The
   field now uses ImGuiInputTextFlags.EnterReturnsTrue and routes the
   pending message through GameFunctions.ChatBox.SendMessage. Lines
   that don't start with a slash get the active channel's prefix
   prepended so typing in /fc lands on the FC channel instead of the
   current game-side default.

InputBar gains an ILogger<InputBar> for the send-failure path; the
DI registration in PluginHostFactory is updated to match.
2026-05-23 20:50:54 +02:00
JonKazama-Hellion 576cd6dafd feat(ui): assemble MainWindow from the components layer
Top-level chat window composes HonorificHeader, Sidebar, MessageList,
InputBar and StatusBar in the layout from the master spec: header row,
horizontal body (sidebar + main area with messages + input), status
strip pinned to the bottom. Component types are fully qualified through
the Ui.Components prefix so the v1.5.6 Ui.StatusBar type cannot shadow
the new layer through parent-namespace resolution before it is removed.
Toggle is a new-shadow on Window.Toggle so the open path also writes
Config.MainWindowOpen; OnClose covers the close path through the base
behaviour. InputBar.Height is now public so the layout math can reach
it from outside the components folder.
2026-05-23 19:52:37 +02:00
JonKazama-Hellion 36afce21e5 feat(ui): add InputBar with channel pill and symbol picker overlay
Channel pill picks Token.AccentEmber when the tab is a tell (matched by
IsTempTab + a set TellTarget) and Token.AccentPrimary otherwise, so the
tinted background reads as the channel type at a glance. SymbolPicker
runs as an overlay popup — the inserted fragment splices straight into
the pending buffer up to a 500-char cap. Send wiring and the settings
button arrive when the main window assembles the components.
2026-05-23 19:17:25 +02:00