chore(release): prepare v1.0.2 — polish patch
Four small backlog items bundled: - New: hide chat (and every other plugin window) while the New Game+ menu is open. Settings -> Window -> Frame, default off. Skips the whole WindowSystem.Draw() pass while QuestRedo is visible, mirroring the existing HideInLoadingScreens pattern. - New: tint the channel selector button in the active channel colour. Settings -> Appearance -> Colours, default on. Reuses the existing inputColour computation (incl. ExtraChat override) and adds an ImGuiCol.Button push around the selector. New ColourUtil helper AdjustBrightness derives hover/active variants. - Fix: PayloadHandler.InlineIcon hardcoded all hover icons to 32x32. Replaced with float-based aspect-ratio-preserving shrink, single scale-factor, zero-size guard, named MaxInlineIconSize constant. Affects six call sites (status, item, achievement and other inline hover paths). - Diagnostic: HideState transitions log on Verbose level for both ChatLogWindow and Popout. Manifest bumped to 1.0.2 across csproj, yaml, repo.json. CHANGELOG entry added, README version line updated. yaml + repo.json changelog trimmed to the slim 4-version window (1.0.2, 1.0.1, 1.0.0, 0.6.1).
This commit is contained in:
@@ -145,6 +145,7 @@ public class Configuration : IPluginConfiguration
|
|||||||
public bool HideWhenUiHidden = true;
|
public bool HideWhenUiHidden = true;
|
||||||
public bool HideInLoadingScreens;
|
public bool HideInLoadingScreens;
|
||||||
public bool HideInBattle;
|
public bool HideInBattle;
|
||||||
|
public bool HideInNewGamePlusMenu;
|
||||||
public bool HideWhenInactive;
|
public bool HideWhenInactive;
|
||||||
public int InactivityHideTimeout = 10;
|
public int InactivityHideTimeout = 10;
|
||||||
public bool InactivityHideActiveDuringBattle = true;
|
public bool InactivityHideActiveDuringBattle = true;
|
||||||
@@ -221,6 +222,7 @@ public class Configuration : IPluginConfiguration
|
|||||||
public float TooltipOffset;
|
public float TooltipOffset;
|
||||||
public float WindowAlpha = 100f;
|
public float WindowAlpha = 100f;
|
||||||
public Dictionary<ChatType, uint> ChatColours = new();
|
public Dictionary<ChatType, uint> ChatColours = new();
|
||||||
|
public bool ColorSelectedInputChannelButton = true;
|
||||||
public List<Tab> Tabs = [];
|
public List<Tab> Tabs = [];
|
||||||
|
|
||||||
public bool OverrideStyle;
|
public bool OverrideStyle;
|
||||||
@@ -241,6 +243,7 @@ public class Configuration : IPluginConfiguration
|
|||||||
HideWhenUiHidden = other.HideWhenUiHidden;
|
HideWhenUiHidden = other.HideWhenUiHidden;
|
||||||
HideInLoadingScreens = other.HideInLoadingScreens;
|
HideInLoadingScreens = other.HideInLoadingScreens;
|
||||||
HideInBattle = other.HideInBattle;
|
HideInBattle = other.HideInBattle;
|
||||||
|
HideInNewGamePlusMenu = other.HideInNewGamePlusMenu;
|
||||||
HideWhenInactive = other.HideWhenInactive;
|
HideWhenInactive = other.HideWhenInactive;
|
||||||
InactivityHideTimeout = other.InactivityHideTimeout;
|
InactivityHideTimeout = other.InactivityHideTimeout;
|
||||||
InactivityHideActiveDuringBattle = other.InactivityHideActiveDuringBattle;
|
InactivityHideActiveDuringBattle = other.InactivityHideActiveDuringBattle;
|
||||||
@@ -288,6 +291,7 @@ public class Configuration : IPluginConfiguration
|
|||||||
TooltipOffset = other.TooltipOffset;
|
TooltipOffset = other.TooltipOffset;
|
||||||
WindowAlpha = other.WindowAlpha;
|
WindowAlpha = other.WindowAlpha;
|
||||||
ChatColours = other.ChatColours.ToDictionary(entry => entry.Key, entry => entry.Value);
|
ChatColours = other.ChatColours.ToDictionary(entry => entry.Key, entry => entry.Value);
|
||||||
|
ColorSelectedInputChannelButton = other.ColorSelectedInputChannelButton;
|
||||||
|
|
||||||
// Hellion Chat — Auto-Tell-Tabs are session-only and therefore
|
// Hellion Chat — Auto-Tell-Tabs are session-only and therefore
|
||||||
// never present in a disk-loaded copy. Keep the live temp tabs of
|
// never present in a disk-loaded copy. Keep the live temp tabs of
|
||||||
|
|||||||
@@ -20,6 +20,8 @@ namespace HellionChat.GameFunctions;
|
|||||||
|
|
||||||
internal unsafe class GameFunctions : IDisposable
|
internal unsafe class GameFunctions : IDisposable
|
||||||
{
|
{
|
||||||
|
internal const string NewGamePlusAddonName = "QuestRedo";
|
||||||
|
|
||||||
#region Hooks
|
#region Hooks
|
||||||
[Signature("E8 ?? ?? ?? ?? 48 85 C0 0F 84 ?? ?? ?? ?? 48 8B D0 49 8D 4F", DetourName = nameof(ResolveTextCommandPlaceholderDetour))]
|
[Signature("E8 ?? ?? ?? ?? 48 85 C0 0F 84 ?? ?? ?? ?? 48 8B D0 49 8D 4F", DetourName = nameof(ResolveTextCommandPlaceholderDetour))]
|
||||||
private Hook<ResolveTextCommandPlaceholderDelegate>? ResolveTextCommandPlaceholderHook = null!;
|
private Hook<ResolveTextCommandPlaceholderDelegate>? ResolveTextCommandPlaceholderHook = null!;
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
0.1.0 is our bootstrap release; the underlying Chat 2 base is
|
0.1.0 is our bootstrap release; the underlying Chat 2 base is
|
||||||
called out in the yaml changelog so users can see what it
|
called out in the yaml changelog so users can see what it
|
||||||
derives from. -->
|
derives from. -->
|
||||||
<Version>1.0.1</Version>
|
<Version>1.0.2</Version>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<!-- Honor packages.lock.json on restore so floating version ranges
|
<!-- Honor packages.lock.json on restore so floating version ranges
|
||||||
don't silently drift between machines or CI runs. -->
|
don't silently drift between machines or CI runs. -->
|
||||||
|
|||||||
@@ -49,6 +49,24 @@ tags:
|
|||||||
- Replacement
|
- Replacement
|
||||||
- Privacy
|
- Privacy
|
||||||
changelog: |-
|
changelog: |-
|
||||||
|
**Hellion Chat 1.0.2 — Polish patch**
|
||||||
|
|
||||||
|
- New: optionally hide chat (and every other plugin window) while the
|
||||||
|
New Game+ menu is open. Toggle in Settings → Window → Frame, default
|
||||||
|
off. Closing the menu restores all windows.
|
||||||
|
- New: optionally tint the channel selector button next to the input
|
||||||
|
field with the currently active channel's colour. Toggle in
|
||||||
|
Settings → Appearance → Colours, default on. Matches the existing
|
||||||
|
input-text tint and respects ExtraChat overrides.
|
||||||
|
- Fix: status, item and other inline hover icons keep their original
|
||||||
|
aspect ratio. Debuff icons with non-square dimensions are no longer
|
||||||
|
visually squished into a 32×32 box.
|
||||||
|
- Diagnostic: hide-state transitions (battle, cutscene, user-hide,
|
||||||
|
cutscene override) are now logged on Verbose level for easier bug
|
||||||
|
reports — off by default, enable with `/xllog set HellionChat verbose`.
|
||||||
|
|
||||||
|
Based on Chat 2 1.35.3 (upstream Infiziert90/ChatTwo, EUPL-1.2).
|
||||||
|
|
||||||
**Hellion Chat 1.0.1 — Window Position Recovery**
|
**Hellion Chat 1.0.1 — Window Position Recovery**
|
||||||
|
|
||||||
- Automatic bounds check on the first draw after plugin load.
|
- Automatic bounds check on the first draw after plugin load.
|
||||||
@@ -214,76 +232,6 @@ changelog: |-
|
|||||||
|
|
||||||
Based on Chat 2 1.35.3 (upstream Infiziert90/ChatTwo, EUPL-1.2).
|
Based on Chat 2 1.35.3 (upstream Infiziert90/ChatTwo, EUPL-1.2).
|
||||||
|
|
||||||
**Hellion Chat 0.6.0 — UX Polish: Pop-Out Input + Colour Presets**
|
|
||||||
|
|
||||||
Two opt-in UX features land in the same release. Existing users see
|
|
||||||
no change unless they enable the new toggles.
|
|
||||||
|
|
||||||
Pop-out input bar:
|
|
||||||
|
|
||||||
- New global master switch in Settings → Window → Frame: "Enable input
|
|
||||||
in pop-outs". Default OFF so existing behaviour is preserved
|
|
||||||
- When enabled, every pop-out window grows a compact input bar at the
|
|
||||||
bottom (channel-coloured icon button left, text input right). The
|
|
||||||
auto-translate picker is intentionally not part of the compact bar
|
|
||||||
in v0.6.0 — typical pop-out workflows (FC greeter, club hostess)
|
|
||||||
rarely need it there
|
|
||||||
- Each pop-out keeps an independent text buffer and history cursor;
|
|
||||||
channel changes still apply globally because that is how the FFXIV
|
|
||||||
channel API works
|
|
||||||
- Up/Down navigates a shared input history singleton across the main
|
|
||||||
window and every open pop-out
|
|
||||||
- First pop-out opening after the upgrade shows a one-time hint
|
|
||||||
banner pointing users to the new toggle
|
|
||||||
|
|
||||||
Chat colour presets:
|
|
||||||
|
|
||||||
- Seven built-in presets above the per-channel colour list in
|
|
||||||
Settings → Appearance → Colours: ChatTwo Default, High-Contrast,
|
|
||||||
Pastell, Dark-Mode-Tuned, Hellion (brand-coloured, blue/orange
|
|
||||||
Arctic Cyan + Ember Glow palette from the Hellion Online Media
|
|
||||||
branding spec), plus two bonus mood presets — Night Blue (royal
|
|
||||||
blue, classic-cool) and Indigo Violet (royal violet, glitter-mystic)
|
|
||||||
- Apply is immediate and overwrites the channels covered by the
|
|
||||||
preset; battle-channel colours are left alone so combat tuning
|
|
||||||
stays intact
|
|
||||||
|
|
||||||
Configuration migrates from v10 to v11 with a diagnostic log entry;
|
|
||||||
no data is reset. Bilingual (English/German) for both new sections.
|
|
||||||
|
|
||||||
Based on Chat 2 1.35.3 (upstream Infiziert90/ChatTwo, EUPL-1.2).
|
|
||||||
|
|
||||||
**Hellion Chat 0.5.4 — WrapText hardening**
|
|
||||||
|
|
||||||
Replaces the unsafe pointer-arithmetic in ImGuiUtil.WrapText with
|
|
||||||
Span- and index-based control flow. Closes the persistent CodeQL
|
|
||||||
Critical alert "unvalidated local pointer arithmetic" that kept
|
|
||||||
re-firing on every shape of the previous fix.
|
|
||||||
|
|
||||||
Hardening:
|
|
||||||
|
|
||||||
- WrapText now allocates a buffer sized by Encoding.UTF8.GetMaxByteCount
|
|
||||||
via ArrayPool, validates the actual encoded length against that
|
|
||||||
ceiling, and threads the rest of the algorithm through int offsets
|
|
||||||
instead of raw byte pointers
|
|
||||||
- Pointer arithmetic only happens inside two small private helpers
|
|
||||||
(CalcWordWrap and DrawText) that take the pinned base pointer plus
|
|
||||||
int offsets sourced from the plugin's own logic, not from any
|
|
||||||
virtual-method return
|
|
||||||
- Added a 16 KiB upper bound on the buffer rent to prevent a
|
|
||||||
pathological input from triggering an unbounded ArrayPool allocation
|
|
||||||
|
|
||||||
No user-visible behaviour change. Word-wrap output is byte-identical
|
|
||||||
to v0.5.3.
|
|
||||||
|
|
||||||
Based on Chat 2 1.35.3 (upstream Infiziert90/ChatTwo, EUPL-1.2).
|
|
||||||
|
|
||||||
**Hellion Chat 0.5.3 — Pointer arithmetic hardening**
|
|
||||||
|
|
||||||
Closed CodeQL Critical alert in ImGuiUtil.WrapText by validating the
|
|
||||||
encoded byte buffer length via GetByteCount before pointer
|
|
||||||
arithmetic. Single-fix patch on top of v0.5.2.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
Earlier history: https://github.com/JonKazama-Hellion/HellionChat/releases
|
Earlier history: https://github.com/JonKazama-Hellion/HellionChat/releases
|
||||||
|
|||||||
@@ -332,10 +332,19 @@ public sealed class PayloadHandler
|
|||||||
atkBase->SetPosition((short) x, (short) y);
|
atkBase->SetPosition((short) x, (short) y);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private const float MaxInlineIconSize = 32f;
|
||||||
|
|
||||||
private static void InlineIcon(IDalamudTextureWrap icon)
|
private static void InlineIcon(IDalamudTextureWrap icon)
|
||||||
{
|
{
|
||||||
|
if (icon.Size.X <= 0 || icon.Size.Y <= 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var width = (float) icon.Size.X;
|
||||||
|
var height = (float) icon.Size.Y;
|
||||||
|
var scale = Math.Min(1f, Math.Min(MaxInlineIconSize / width, MaxInlineIconSize / height));
|
||||||
|
var size = ImGuiHelpers.ScaledVector2(width * scale, height * scale);
|
||||||
|
|
||||||
var cursor = ImGui.GetCursorPos();
|
var cursor = ImGui.GetCursorPos();
|
||||||
var size = ImGuiHelpers.ScaledVector2(32, 32);
|
|
||||||
ImGui.Image(icon.Handle, size);
|
ImGui.Image(icon.Handle, size);
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
ImGui.SetCursorPos(cursor + new Vector2(size.X + 4, size.Y - ImGui.GetTextLineHeightWithSpacing()));
|
ImGui.SetCursorPos(cursor + new Vector2(size.X + 4, size.Y - ImGui.GetTextLineHeightWithSpacing()));
|
||||||
|
|||||||
@@ -571,6 +571,16 @@ public sealed class Plugin : IDalamudPlugin
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// v1.0.2 — global skip while the New Game+ menu (QuestRedo addon) is
|
||||||
|
// open. Hides every plugin window in one shot (chat log, pop-outs,
|
||||||
|
// settings, db viewer, etc.), matching the LoadingScreens pattern.
|
||||||
|
if (Config.HideInNewGamePlusMenu && GameFunctions.GameFunctions.IsAddonInteractable(GameFunctions.GameFunctions.NewGamePlusAddonName))
|
||||||
|
{
|
||||||
|
ChatLogWindow.FinalizeFrame();
|
||||||
|
TypingIpc.Update();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
ChatLogWindow.HideStateCheck();
|
ChatLogWindow.HideStateCheck();
|
||||||
|
|
||||||
Interface.UiBuilder.DisableUserUiHide = !Config.HideWhenUiHidden;
|
Interface.UiBuilder.DisableUserUiHide = !Config.HideWhenUiHidden;
|
||||||
|
|||||||
+36
@@ -2148,6 +2148,24 @@ namespace HellionChat.Resources {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to The channel selector button next to the input field is tinted with the currently active channel's colour. Matches the tinting of the input text itself..
|
||||||
|
/// </summary>
|
||||||
|
internal static string Options_ColorSelectedInputChannelButton_Description {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("Options_ColorSelectedInputChannelButton_Description", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Tint channel selector with channel colour.
|
||||||
|
/// </summary>
|
||||||
|
internal static string Options_ColorSelectedInputChannelButton_Name {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("Options_ColorSelectedInputChannelButton_Name", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to Chat colours.
|
/// Looks up a localized string similar to Chat colours.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -2661,6 +2679,24 @@ namespace HellionChat.Resources {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Hide the chat while the New Game+ menu is open. Closing the menu shows the chat again..
|
||||||
|
/// </summary>
|
||||||
|
internal static string Options_HideInNewGamePlusMenu_Description {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("Options_HideInNewGamePlusMenu_Description", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Hide while New Game+ menu is open.
|
||||||
|
/// </summary>
|
||||||
|
internal static string Options_HideInNewGamePlusMenu_Name {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("Options_HideInNewGamePlusMenu_Name", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to Hide {0} during loading screens..
|
/// Looks up a localized string similar to Hide {0} during loading screens..
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
Generated
+12
@@ -208,6 +208,12 @@
|
|||||||
<data name="Options_ChatColours_Import">
|
<data name="Options_ChatColours_Import">
|
||||||
<value>Vom Spiel importieren</value>
|
<value>Vom Spiel importieren</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Options_ColorSelectedInputChannelButton_Name" xml:space="preserve">
|
||||||
|
<value>Channel-Auswahl-Knopf in Channel-Farbe</value>
|
||||||
|
</data>
|
||||||
|
<data name="Options_ColorSelectedInputChannelButton_Description" xml:space="preserve">
|
||||||
|
<value>Der Channel-Auswahl-Knopf neben dem Eingabefeld bekommt die Farbe des aktuell aktiven Channels. Konsistent zur Färbung des Eingabetextes selbst.</value>
|
||||||
|
</data>
|
||||||
<data name="Options_Tabs_Tab">
|
<data name="Options_Tabs_Tab">
|
||||||
<value>Kanäle</value>
|
<value>Kanäle</value>
|
||||||
</data>
|
</data>
|
||||||
@@ -1190,6 +1196,12 @@ Sie wurden gewarnt.</value>
|
|||||||
<data name="Options_HideInBattle_Description" xml:space="preserve">
|
<data name="Options_HideInBattle_Description" xml:space="preserve">
|
||||||
<value>Blende den Chat während der Kämpfe aus.</value>
|
<value>Blende den Chat während der Kämpfe aus.</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Options_HideInNewGamePlusMenu_Name" xml:space="preserve">
|
||||||
|
<value>Während des New-Game+ Menüs ausblenden</value>
|
||||||
|
</data>
|
||||||
|
<data name="Options_HideInNewGamePlusMenu_Description" xml:space="preserve">
|
||||||
|
<value>Blendet den Chat aus, solange das New-Game+ Menü geöffnet ist. Schließen des Menüs blendet den Chat wieder ein.</value>
|
||||||
|
</data>
|
||||||
<data name="Options_Emote_EmoteStats" xml:space="preserve">
|
<data name="Options_Emote_EmoteStats" xml:space="preserve">
|
||||||
<value>Emote-Statistik</value>
|
<value>Emote-Statistik</value>
|
||||||
</data>
|
</data>
|
||||||
|
|||||||
@@ -208,6 +208,12 @@
|
|||||||
<data name="Options_ChatColours_Import">
|
<data name="Options_ChatColours_Import">
|
||||||
<value>Import from game</value>
|
<value>Import from game</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Options_ColorSelectedInputChannelButton_Name" xml:space="preserve">
|
||||||
|
<value>Tint channel selector with channel colour</value>
|
||||||
|
</data>
|
||||||
|
<data name="Options_ColorSelectedInputChannelButton_Description" xml:space="preserve">
|
||||||
|
<value>The channel selector button next to the input field is tinted with the currently active channel's colour. Matches the tinting of the input text itself.</value>
|
||||||
|
</data>
|
||||||
<data name="Options_Tabs_Tab">
|
<data name="Options_Tabs_Tab">
|
||||||
<value>Tabs</value>
|
<value>Tabs</value>
|
||||||
</data>
|
</data>
|
||||||
@@ -1189,6 +1195,12 @@
|
|||||||
<data name="Options_HideInBattle_Description" xml:space="preserve">
|
<data name="Options_HideInBattle_Description" xml:space="preserve">
|
||||||
<value>Hide the chat during battles.</value>
|
<value>Hide the chat during battles.</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Options_HideInNewGamePlusMenu_Name" xml:space="preserve">
|
||||||
|
<value>Hide while New Game+ menu is open</value>
|
||||||
|
</data>
|
||||||
|
<data name="Options_HideInNewGamePlusMenu_Description" xml:space="preserve">
|
||||||
|
<value>Hide the chat while the New Game+ menu is open. Closing the menu shows the chat again.</value>
|
||||||
|
</data>
|
||||||
<data name="Options_Emote_EmoteStats" xml:space="preserve">
|
<data name="Options_Emote_EmoteStats" xml:space="preserve">
|
||||||
<value>Emote Stats</value>
|
<value>Emote Stats</value>
|
||||||
</data>
|
</data>
|
||||||
|
|||||||
@@ -278,9 +278,11 @@ public sealed class ChatLogWindow : Window
|
|||||||
{
|
{
|
||||||
case "hide":
|
case "hide":
|
||||||
CurrentHideState = HideState.User;
|
CurrentHideState = HideState.User;
|
||||||
|
Plugin.Log.Verbose("HideState: → User (chat hide command)");
|
||||||
break;
|
break;
|
||||||
case "show":
|
case "show":
|
||||||
CurrentHideState = HideState.None;
|
CurrentHideState = HideState.None;
|
||||||
|
Plugin.Log.Verbose("HideState: → None (chat show command)");
|
||||||
break;
|
break;
|
||||||
case "toggle":
|
case "toggle":
|
||||||
CurrentHideState = CurrentHideState switch
|
CurrentHideState = CurrentHideState switch
|
||||||
@@ -290,6 +292,7 @@ public sealed class ChatLogWindow : Window
|
|||||||
HideState.None => HideState.User,
|
HideState.None => HideState.User,
|
||||||
_ => CurrentHideState,
|
_ => CurrentHideState,
|
||||||
};
|
};
|
||||||
|
Plugin.Log.Verbose($"HideState: → {CurrentHideState} (chat toggle command)");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -406,30 +409,48 @@ public sealed class ChatLogWindow : Window
|
|||||||
{
|
{
|
||||||
// if the chat has no hide state set, and the player has entered battle, we hide chat if they have configured it
|
// if the chat has no hide state set, and the player has entered battle, we hide chat if they have configured it
|
||||||
if (Plugin.Config.HideInBattle && CurrentHideState == HideState.None && Plugin.InBattle)
|
if (Plugin.Config.HideInBattle && CurrentHideState == HideState.None && Plugin.InBattle)
|
||||||
|
{
|
||||||
CurrentHideState = HideState.Battle;
|
CurrentHideState = HideState.Battle;
|
||||||
|
Plugin.Log.Verbose("HideState: None → Battle");
|
||||||
|
}
|
||||||
|
|
||||||
// If the chat is hidden because of battle, we reset it here
|
// If the chat is hidden because of battle, we reset it here
|
||||||
if (CurrentHideState is HideState.Battle && !Plugin.InBattle)
|
if (CurrentHideState is HideState.Battle && !Plugin.InBattle)
|
||||||
|
{
|
||||||
CurrentHideState = HideState.None;
|
CurrentHideState = HideState.None;
|
||||||
|
Plugin.Log.Verbose("HideState: Battle → None");
|
||||||
|
}
|
||||||
|
|
||||||
// if the chat has no hide state and in a cutscene, set the hide state to cutscene
|
// if the chat has no hide state and in a cutscene, set the hide state to cutscene
|
||||||
if (Plugin.Config.HideDuringCutscenes && CurrentHideState == HideState.None && (Plugin.CutsceneActive || Plugin.GposeActive))
|
if (Plugin.Config.HideDuringCutscenes && CurrentHideState == HideState.None && (Plugin.CutsceneActive || Plugin.GposeActive))
|
||||||
{
|
{
|
||||||
if (Plugin.Functions.Chat.CheckHideFlags())
|
if (Plugin.Functions.Chat.CheckHideFlags())
|
||||||
|
{
|
||||||
CurrentHideState = HideState.Cutscene;
|
CurrentHideState = HideState.Cutscene;
|
||||||
|
Plugin.Log.Verbose("HideState: None → Cutscene");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// if the chat is hidden because of a cutscene and no longer in a cutscene, set the hide state to none
|
// if the chat is hidden because of a cutscene and no longer in a cutscene, set the hide state to none
|
||||||
if (CurrentHideState is HideState.Cutscene or HideState.CutsceneOverride && !Plugin.CutsceneActive && !Plugin.GposeActive)
|
if (CurrentHideState is HideState.Cutscene or HideState.CutsceneOverride && !Plugin.CutsceneActive && !Plugin.GposeActive)
|
||||||
|
{
|
||||||
|
Plugin.Log.Verbose($"HideState: {CurrentHideState} → None (cutscene/gpose ended)");
|
||||||
CurrentHideState = HideState.None;
|
CurrentHideState = HideState.None;
|
||||||
|
}
|
||||||
|
|
||||||
// if the chat is hidden because of a cutscene and the chat has been activated, show chat
|
// if the chat is hidden because of a cutscene and the chat has been activated, show chat
|
||||||
if (CurrentHideState == HideState.Cutscene && Activate)
|
if (CurrentHideState == HideState.Cutscene && Activate)
|
||||||
|
{
|
||||||
CurrentHideState = HideState.CutsceneOverride;
|
CurrentHideState = HideState.CutsceneOverride;
|
||||||
|
Plugin.Log.Verbose("HideState: Cutscene → CutsceneOverride (user activate)");
|
||||||
|
}
|
||||||
|
|
||||||
// if the user hid the chat and is now activating chat, reset the hide state
|
// if the user hid the chat and is now activating chat, reset the hide state
|
||||||
if (CurrentHideState == HideState.User && Activate)
|
if (CurrentHideState == HideState.User && Activate)
|
||||||
|
{
|
||||||
CurrentHideState = HideState.None;
|
CurrentHideState = HideState.None;
|
||||||
|
Plugin.Log.Verbose("HideState: User → None (activate)");
|
||||||
|
}
|
||||||
|
|
||||||
if (CurrentHideState is HideState.Cutscene or HideState.User or HideState.Battle || (Plugin.Config.HideWhenNotLoggedIn && !Plugin.ClientState.IsLoggedIn))
|
if (CurrentHideState is HideState.Cutscene or HideState.User or HideState.Battle || (Plugin.Config.HideWhenNotLoggedIn && !Plugin.ClientState.IsLoggedIn))
|
||||||
{
|
{
|
||||||
@@ -600,9 +621,40 @@ public sealed class ChatLogWindow : Window
|
|||||||
DrawChannelName(activeTab);
|
DrawChannelName(activeTab);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// v1.0.2 — compute inputColour up front so the channel selector button
|
||||||
|
// can also tint with it (existing input-text push remains below).
|
||||||
|
var inputType = activeTab.CurrentChannel.UseTempChannel ? activeTab.CurrentChannel.TempChannel.ToChatType() : activeTab.CurrentChannel.Channel.ToChatType();
|
||||||
|
var isCommand = Chat.Trim().StartsWith('/');
|
||||||
|
if (isCommand)
|
||||||
|
{
|
||||||
|
var command = Chat.Split(' ')[0];
|
||||||
|
if (TextCommandChannels.TryGetValue(command, out var channel))
|
||||||
|
inputType = channel;
|
||||||
|
|
||||||
|
if (!IsValidCommand(command))
|
||||||
|
inputType = ChatType.Error;
|
||||||
|
}
|
||||||
|
|
||||||
|
var inputColour = Plugin.Config.ChatColours.TryGetValue(inputType, out var inputCol) ? inputCol : inputType.DefaultColor();
|
||||||
|
|
||||||
|
if (!isCommand && Plugin.ExtraChat.ChannelOverride is var (_, overrideColour))
|
||||||
|
inputColour = overrideColour;
|
||||||
|
|
||||||
|
if (isCommand && Plugin.ExtraChat.ChannelCommandColours.TryGetValue(Chat.Split(' ')[0], out var ecColour))
|
||||||
|
inputColour = ecColour;
|
||||||
|
|
||||||
var beforeIcon = ImGui.GetCursorPos();
|
var beforeIcon = ImGui.GetCursorPos();
|
||||||
|
|
||||||
|
var tintSelector = Plugin.Config.ColorSelectedInputChannelButton && inputColour.HasValue;
|
||||||
|
var selectorAbgr = tintSelector ? ColourUtil.RgbaToAbgr(inputColour!.Value) : 0u;
|
||||||
|
|
||||||
|
using (ImRaii.PushColor(ImGuiCol.Button, selectorAbgr, tintSelector))
|
||||||
|
using (ImRaii.PushColor(ImGuiCol.ButtonHovered, ColourUtil.AdjustBrightness(selectorAbgr, 1.15f), tintSelector))
|
||||||
|
using (ImRaii.PushColor(ImGuiCol.ButtonActive, ColourUtil.AdjustBrightness(selectorAbgr, 0.85f), tintSelector))
|
||||||
|
{
|
||||||
if (ImGuiUtil.IconButton(FontAwesomeIcon.Comment) && activeTab.Channel is null)
|
if (ImGuiUtil.IconButton(FontAwesomeIcon.Comment) && activeTab.Channel is null)
|
||||||
ImGui.OpenPopup(ChatChannelPicker);
|
ImGui.OpenPopup(ChatChannelPicker);
|
||||||
|
}
|
||||||
|
|
||||||
if (activeTab.Channel is not null && ImGui.IsItemHovered())
|
if (activeTab.Channel is not null && ImGui.IsItemHovered())
|
||||||
ImGuiUtil.Tooltip(Language.ChatLog_SwitcherDisabled);
|
ImGuiUtil.Tooltip(Language.ChatLog_SwitcherDisabled);
|
||||||
@@ -626,27 +678,7 @@ public sealed class ChatLogWindow : Window
|
|||||||
var buttonsRight = (showNovice ? 1 : 0) + (Plugin.Config.ShowHideButton ? 1 : 0);
|
var buttonsRight = (showNovice ? 1 : 0) + (Plugin.Config.ShowHideButton ? 1 : 0);
|
||||||
var inputWidth = ImGui.GetContentRegionAvail().X - buttonWidth * (1 + buttonsRight);
|
var inputWidth = ImGui.GetContentRegionAvail().X - buttonWidth * (1 + buttonsRight);
|
||||||
|
|
||||||
var inputType = activeTab.CurrentChannel.UseTempChannel ? activeTab.CurrentChannel.TempChannel.ToChatType() : activeTab.CurrentChannel.Channel.ToChatType();
|
|
||||||
var isCommand = Chat.Trim().StartsWith('/');
|
|
||||||
if (isCommand)
|
|
||||||
{
|
|
||||||
var command = Chat.Split(' ')[0];
|
|
||||||
if (TextCommandChannels.TryGetValue(command, out var channel))
|
|
||||||
inputType = channel;
|
|
||||||
|
|
||||||
if (!IsValidCommand(command))
|
|
||||||
inputType = ChatType.Error;
|
|
||||||
}
|
|
||||||
|
|
||||||
var normalColor = ImGui.GetColorU32(ImGuiCol.Text);
|
var normalColor = ImGui.GetColorU32(ImGuiCol.Text);
|
||||||
var inputColour = Plugin.Config.ChatColours.TryGetValue(inputType, out var inputCol) ? inputCol : inputType.DefaultColor();
|
|
||||||
|
|
||||||
if (!isCommand && Plugin.ExtraChat.ChannelOverride is var (_, overrideColour))
|
|
||||||
inputColour = overrideColour;
|
|
||||||
|
|
||||||
if (isCommand && Plugin.ExtraChat.ChannelCommandColours.TryGetValue(Chat.Split(' ')[0], out var ecColour))
|
|
||||||
inputColour = ecColour;
|
|
||||||
|
|
||||||
var push = inputColour != null;
|
var push = inputColour != null;
|
||||||
using (ImRaii.PushColor(ImGuiCol.Text, push ? ColourUtil.RgbaToAbgr(inputColour!.Value) : 0, push))
|
using (ImRaii.PushColor(ImGuiCol.Text, push ? ColourUtil.RgbaToAbgr(inputColour!.Value) : 0, push))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -229,30 +229,48 @@ internal class Popout : Window
|
|||||||
{
|
{
|
||||||
// if the chat has no hide state set, and the player has entered battle, we hide chat if they have configured it
|
// if the chat has no hide state set, and the player has entered battle, we hide chat if they have configured it
|
||||||
if (Tab.HideInBattle && CurrentHideState == HideState.None && Plugin.InBattle)
|
if (Tab.HideInBattle && CurrentHideState == HideState.None && Plugin.InBattle)
|
||||||
|
{
|
||||||
CurrentHideState = HideState.Battle;
|
CurrentHideState = HideState.Battle;
|
||||||
|
Plugin.Log.Verbose($"Popout HideState [{Tab.Name}]: None → Battle");
|
||||||
|
}
|
||||||
|
|
||||||
// If the chat is hidden because of battle, we reset it here
|
// If the chat is hidden because of battle, we reset it here
|
||||||
if (CurrentHideState is HideState.Battle && !Plugin.InBattle)
|
if (CurrentHideState is HideState.Battle && !Plugin.InBattle)
|
||||||
|
{
|
||||||
CurrentHideState = HideState.None;
|
CurrentHideState = HideState.None;
|
||||||
|
Plugin.Log.Verbose($"Popout HideState [{Tab.Name}]: Battle → None");
|
||||||
|
}
|
||||||
|
|
||||||
// if the chat has no hide state and in a cutscene, set the hide state to cutscene
|
// if the chat has no hide state and in a cutscene, set the hide state to cutscene
|
||||||
if (Tab.HideDuringCutscenes && CurrentHideState == HideState.None && (Plugin.CutsceneActive || Plugin.GposeActive))
|
if (Tab.HideDuringCutscenes && CurrentHideState == HideState.None && (Plugin.CutsceneActive || Plugin.GposeActive))
|
||||||
{
|
{
|
||||||
if (ChatLogWindow.Plugin.Functions.Chat.CheckHideFlags())
|
if (ChatLogWindow.Plugin.Functions.Chat.CheckHideFlags())
|
||||||
|
{
|
||||||
CurrentHideState = HideState.Cutscene;
|
CurrentHideState = HideState.Cutscene;
|
||||||
|
Plugin.Log.Verbose($"Popout HideState [{Tab.Name}]: None → Cutscene");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// if the chat is hidden because of a cutscene and no longer in a cutscene, set the hide state to none
|
// if the chat is hidden because of a cutscene and no longer in a cutscene, set the hide state to none
|
||||||
if (CurrentHideState is HideState.Cutscene or HideState.CutsceneOverride && !Plugin.CutsceneActive && !Plugin.GposeActive)
|
if (CurrentHideState is HideState.Cutscene or HideState.CutsceneOverride && !Plugin.CutsceneActive && !Plugin.GposeActive)
|
||||||
|
{
|
||||||
|
Plugin.Log.Verbose($"Popout HideState [{Tab.Name}]: {CurrentHideState} → None (cutscene/gpose ended)");
|
||||||
CurrentHideState = HideState.None;
|
CurrentHideState = HideState.None;
|
||||||
|
}
|
||||||
|
|
||||||
// if the chat is hidden because of a cutscene and the chat has been activated, show chat
|
// if the chat is hidden because of a cutscene and the chat has been activated, show chat
|
||||||
if (CurrentHideState == HideState.Cutscene && ChatLogWindow.Activate)
|
if (CurrentHideState == HideState.Cutscene && ChatLogWindow.Activate)
|
||||||
|
{
|
||||||
CurrentHideState = HideState.CutsceneOverride;
|
CurrentHideState = HideState.CutsceneOverride;
|
||||||
|
Plugin.Log.Verbose($"Popout HideState [{Tab.Name}]: Cutscene → CutsceneOverride (user activate)");
|
||||||
|
}
|
||||||
|
|
||||||
// if the user hid the chat and is now activating chat, reset the hide state
|
// if the user hid the chat and is now activating chat, reset the hide state
|
||||||
if (CurrentHideState == HideState.User && ChatLogWindow.Activate)
|
if (CurrentHideState == HideState.User && ChatLogWindow.Activate)
|
||||||
|
{
|
||||||
CurrentHideState = HideState.None;
|
CurrentHideState = HideState.None;
|
||||||
|
Plugin.Log.Verbose($"Popout HideState [{Tab.Name}]: User → None (activate)");
|
||||||
|
}
|
||||||
|
|
||||||
return CurrentHideState is HideState.Cutscene or HideState.User or HideState.Battle || (Tab.HideWhenNotLoggedIn && !Plugin.ClientState.IsLoggedIn);
|
return CurrentHideState is HideState.Cutscene or HideState.User or HideState.Battle || (Tab.HideWhenNotLoggedIn && !Plugin.ClientState.IsLoggedIn);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -236,6 +236,10 @@ internal sealed class Appearance : ISettingsTab
|
|||||||
ImGui.Separator();
|
ImGui.Separator();
|
||||||
ImGui.Spacing();
|
ImGui.Spacing();
|
||||||
|
|
||||||
|
ImGui.Checkbox(Language.Options_ColorSelectedInputChannelButton_Name, ref Mutable.ColorSelectedInputChannelButton);
|
||||||
|
ImGuiUtil.HelpMarker(Language.Options_ColorSelectedInputChannelButton_Description);
|
||||||
|
ImGui.Spacing();
|
||||||
|
|
||||||
foreach (var (_, types) in ChatTypeExt.SortOrder)
|
foreach (var (_, types) in ChatTypeExt.SortOrder)
|
||||||
{
|
{
|
||||||
foreach (var type in types)
|
foreach (var type in types)
|
||||||
|
|||||||
@@ -56,6 +56,9 @@ internal sealed class Window : ISettingsTab
|
|||||||
|
|
||||||
ImGui.Checkbox(Language.Options_HideInBattle_Name, ref Mutable.HideInBattle);
|
ImGui.Checkbox(Language.Options_HideInBattle_Name, ref Mutable.HideInBattle);
|
||||||
ImGuiUtil.HelpMarker(Language.Options_HideInBattle_Description);
|
ImGuiUtil.HelpMarker(Language.Options_HideInBattle_Description);
|
||||||
|
|
||||||
|
ImGui.Checkbox(Language.Options_HideInNewGamePlusMenu_Name, ref Mutable.HideInNewGamePlusMenu);
|
||||||
|
ImGuiUtil.HelpMarker(Language.Options_HideInNewGamePlusMenu_Description);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -48,4 +48,18 @@ internal static class ColourUtil {
|
|||||||
|
|
||||||
internal static uint ComponentsToRgba(byte red, byte green, byte blue, byte alpha = 0xFF)
|
internal static uint ComponentsToRgba(byte red, byte green, byte blue, byte alpha = 0xFF)
|
||||||
=> alpha | (uint) (red << 24) | (uint) (green << 16) | (uint) (blue << 8);
|
=> alpha | (uint) (red << 24) | (uint) (green << 16) | (uint) (blue << 8);
|
||||||
|
|
||||||
|
internal static uint AdjustBrightness(uint abgr, float factor)
|
||||||
|
{
|
||||||
|
var a = (byte) ((abgr & 0xFF000000) >> 24);
|
||||||
|
var b = (byte) ((abgr & 0x00FF0000) >> 16);
|
||||||
|
var g = (byte) ((abgr & 0x0000FF00) >> 8);
|
||||||
|
var r = (byte) (abgr & 0x000000FF);
|
||||||
|
|
||||||
|
var nr = (byte) Math.Clamp(r * factor, 0f, 255f);
|
||||||
|
var ng = (byte) Math.Clamp(g * factor, 0f, 255f);
|
||||||
|
var nb = (byte) Math.Clamp(b * factor, 0f, 255f);
|
||||||
|
|
||||||
|
return ((uint) a << 24) | ((uint) nb << 16) | ((uint) ng << 8) | nr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
[](https://dotnet.microsoft.com/)
|
[](https://dotnet.microsoft.com/)
|
||||||
[](https://www.finalfantasyxiv.com/)
|
[](https://www.finalfantasyxiv.com/)
|
||||||
|
|
||||||
**Version 1.0.0** — DSGVO-bewusster Chat-Ersatz für FINAL FANTASY XIV / Dalamud, basierend auf [Chat 2](https://github.com/Infiziert90/ChatTwo) (EUPL-1.2).
|
**Version 1.0.2** — DSGVO-bewusster Chat-Ersatz für FINAL FANTASY XIV / Dalamud, basierend auf [Chat 2](https://github.com/Infiziert90/ChatTwo) (EUPL-1.2).
|
||||||
|
|
||||||
Hellion Chat ergänzt das ursprüngliche Chat-2-Fundament um Datenschutz- und Daten-Handling-Kontrollen, die mit den Datenschutz-Regeln in der EU, den USA und Japan im Einklang sind. Alle aus Chat 2 übernommenen Funktionen, Befehle und Tastenkürzel funktionieren unverändert. Eigenständiger Plugin-Slot, eigene Konfiguration, eigene Datenbank.
|
Hellion Chat ergänzt das ursprüngliche Chat-2-Fundament um Datenschutz- und Daten-Handling-Kontrollen, die mit den Datenschutz-Regeln in der EU, den USA und Japan im Einklang sind. Alle aus Chat 2 übernommenen Funktionen, Befehle und Tastenkürzel funktionieren unverändert. Eigenständiger Plugin-Slot, eigene Konfiguration, eigene Datenbank.
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,33 @@ und verlinkt für Details auf die Release-Pages.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## [1.0.2] — 2026-05-04 — Polish patch
|
||||||
|
|
||||||
|
Vier kleine Polish-Items aus dem Backlog gebündelt:
|
||||||
|
|
||||||
|
- **Hide bei New Game+ Menü**: Optionaler globaler Toggle der Hellion
|
||||||
|
Chat (und alle weiteren Plugin-Fenster wie Settings, DB-Viewer,
|
||||||
|
Pop-Outs) ausblendet, solange das NG+-Menü offen ist. Settings →
|
||||||
|
Fenster → Rahmen, Default aus. Skipt analog zum bestehenden
|
||||||
|
LoadingScreens-Pattern den gesamten `WindowSystem.Draw()`-Pfad.
|
||||||
|
- **Channel-Selector-Färbung**: Optionales Tinting des
|
||||||
|
Channel-Auswahl-Knopfs (Comment-Icon) neben dem Eingabefeld in der
|
||||||
|
aktuellen Channel-Farbe. Settings → Aussehen → Chat-Farben, Default
|
||||||
|
an. Konsistent zur bestehenden Eingabetext-Färbung, ExtraChat-Override
|
||||||
|
wird übernommen.
|
||||||
|
- **(De)Buff-Icon Aspect-Ratio-Fix**: `PayloadHandler.InlineIcon` quetschte
|
||||||
|
alle Hover-Icons auf 32×32. Status-Icons mit nicht-quadratischen
|
||||||
|
Dimensionen (Debuffs mit Pfeil-Indikator) sind jetzt aspekt-erhaltend
|
||||||
|
geshrinkt. Eigenständige Float-Math-Implementierung mit Zero-Size-Guard
|
||||||
|
statt Cherry-Pick aus dem offenen ChatTwo PR #157 (der hatte eine
|
||||||
|
int-Division-Falle).
|
||||||
|
- **HideState-Logging-Sweep**: Alle HideState-Transitions
|
||||||
|
(Battle/Cutscene/User/Override plus die Pop-Out-Spiegelung) loggen sich
|
||||||
|
auf Verbose-Level. Aus by default, Aktivierung via
|
||||||
|
`/xllog set HellionChat verbose` für Bug-Report-Diagnose.
|
||||||
|
|
||||||
|
[Release-Notes 1.0.2](https://github.com/JonKazama-Hellion/HellionChat/releases/tag/v1.0.2)
|
||||||
|
|
||||||
## [1.0.1] — 2026-05-04 — Window Position Recovery
|
## [1.0.1] — 2026-05-04 — Window Position Recovery
|
||||||
|
|
||||||
Fixes an off-screen-window scenario the user could end up in after a
|
Fixes an off-screen-window scenario the user could end up in after a
|
||||||
|
|||||||
Reference in New Issue
Block a user