Merge restoration block 1 (dead settings) into v1.8.x track
This commit is contained in:
@@ -35,7 +35,7 @@ public class ConfigKeyBind
|
||||
[Serializable]
|
||||
public class Configuration : IPluginConfiguration
|
||||
{
|
||||
internal const int LatestVersion = 22;
|
||||
internal const int LatestVersion = 23;
|
||||
|
||||
public int Version { get; set; } = LatestVersion;
|
||||
|
||||
@@ -177,6 +177,11 @@ public class Configuration : IPluginConfiguration
|
||||
public bool MoreCompactPretty;
|
||||
public bool HideSameTimestamps = true;
|
||||
public bool ShowNoviceNetwork;
|
||||
|
||||
// Migration-only since v23: the 1.5.6 sidebar↔top-tabs switch, superseded by
|
||||
// MainWindowLayoutMode in the v1.6.0 rewrite. No UI control anymore; read by
|
||||
// the v23 migration in Plugin.cs and kept deserializable so a 1.5.6 user's
|
||||
// false value survives one load. Remove in a later schema bump.
|
||||
public bool SidebarTabView = true;
|
||||
public bool PrintChangelog = true;
|
||||
public bool OnlyPreviewIf;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<Project Sdk="Dalamud.NET.Sdk/15.0.0">
|
||||
<PropertyGroup>
|
||||
<!-- Independent versioning; see yaml changelog for upstream Chat 2 base -->
|
||||
<Version>1.8.1</Version>
|
||||
<Version>1.8.2</Version>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<!-- Use lock file to pin exact versions -->
|
||||
|
||||
+14
-2
@@ -223,7 +223,17 @@ public sealed class Plugin : IAsyncDalamudPlugin
|
||||
+ "Please install v1.4.2 first to migrate the configuration, then upgrade to v1.4.10."
|
||||
);
|
||||
}
|
||||
Config.Version = 22;
|
||||
// v23 migration: SidebarTabView was the 1.5.6 sidebar↔top-tabs switch,
|
||||
// superseded by MainWindowLayoutMode in the v1.6.0 rewrite. A user who
|
||||
// set it false (only effective in 1.5.6) wanted top tabs — carry that
|
||||
// intent forward. Runs only for pre-v23 configs; fresh configs load at
|
||||
// LatestVersion and skip it. Additive v20/v22 fields keep their
|
||||
// initializer defaults as before.
|
||||
if (Config.Version < 23 && !Config.SidebarTabView)
|
||||
{
|
||||
Config.MainWindowLayoutMode = MainWindowLayoutMode.TopTabs;
|
||||
}
|
||||
Config.Version = 23;
|
||||
|
||||
// Unpinned TempTabs are session-only and dropped on every load. Pinned
|
||||
// TempTabs survive reload — Jin's tester feedback (v1.4.7).
|
||||
@@ -369,10 +379,12 @@ public sealed class Plugin : IAsyncDalamudPlugin
|
||||
new SelfTests.SettingsWindowOpenStep(this),
|
||||
new SelfTests.OnOpenMainUiRoutesMainWindowStep(this),
|
||||
new SelfTests.TypingIpcStateStep(this),
|
||||
new SelfTests.ConfigMigrationV22Step(this),
|
||||
new SelfTests.ConfigMigrationV23Step(this),
|
||||
new SelfTests.HoverSheenAllocStep(this),
|
||||
new SelfTests.HonorificHeaderRenderStep(this),
|
||||
new SelfTests.PerformanceBaselineStep(this),
|
||||
new SelfTests.MainWindowFocusOpacityStep(this),
|
||||
new SelfTests.MainWindowFlagsStep(this),
|
||||
]);
|
||||
|
||||
// Re-surface the wizard for existing users when a major UX
|
||||
|
||||
+10
-8
@@ -3,23 +3,25 @@ using Dalamud.Plugin.SelfTest;
|
||||
|
||||
namespace HellionChat.SelfTests;
|
||||
|
||||
// Pins the post-migration shape of the v22 config. By /xlperf time the schema
|
||||
// gate has already stamped Config.Version = 22, so the v21 fields plus the new
|
||||
// MainWindowLayoutMode must carry valid values here; this probe never rewrites config.
|
||||
internal sealed class ConfigMigrationV22Step : ISelfTestStep
|
||||
// Pins the post-migration shape of the v23 config. By /xlperf time the schema
|
||||
// gate has already stamped Config.Version = 23 and run the SidebarTabView→
|
||||
// TopTabs migration, so MainWindowLayoutMode must carry a valid value here.
|
||||
// This probe never rewrites config; the actual migration (false → TopTabs) is
|
||||
// load-time and verified by the prepared-config smoke in the plan.
|
||||
internal sealed class ConfigMigrationV23Step : ISelfTestStep
|
||||
{
|
||||
public ConfigMigrationV22Step(Plugin plugin)
|
||||
public ConfigMigrationV23Step(Plugin plugin)
|
||||
{
|
||||
_ = plugin;
|
||||
}
|
||||
|
||||
public string Name => "Hellion Chat - Config v22 migration";
|
||||
public string Name => "Hellion Chat - Config v23 migration";
|
||||
|
||||
public SelfTestStepResult RunStep()
|
||||
{
|
||||
if (Plugin.Config.Version != 22)
|
||||
if (Plugin.Config.Version != 23)
|
||||
{
|
||||
ImGui.Text($"Config.Version is {Plugin.Config.Version}, expected 22");
|
||||
ImGui.Text($"Config.Version is {Plugin.Config.Version}, expected 23");
|
||||
return SelfTestStepResult.Fail;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
using Dalamud.Bindings.ImGui;
|
||||
using Dalamud.Plugin.SelfTest;
|
||||
using HellionChat.Ui.Windows;
|
||||
|
||||
namespace HellionChat.SelfTests;
|
||||
|
||||
// B1-2 window flags. Drives the REAL MainWindow.PreDraw and asserts it wired
|
||||
// Window.Flags to ResolveFlags(CanMove, CanResize), then pins the pure
|
||||
// fresh-base contract: false/false adds NoMove|NoResize, true/true clears them
|
||||
// (the masterplan's "flags must rebuild from a fresh base, else NoMove sticks
|
||||
// after toggling back" risk). NoScrollbar|NoScrollWithMouse always present.
|
||||
// Non-test caller of ResolveFlags: MainWindow.PreDraw.
|
||||
internal sealed class MainWindowFlagsStep : ISelfTestStep
|
||||
{
|
||||
private readonly Plugin plugin;
|
||||
|
||||
public MainWindowFlagsStep(Plugin plugin)
|
||||
{
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
public string Name => "Hellion Chat - MainWindow flags";
|
||||
|
||||
public SelfTestStepResult RunStep()
|
||||
{
|
||||
var window = this.plugin.MainWindow;
|
||||
if (window is null)
|
||||
{
|
||||
ImGui.Text("Plugin.MainWindow is null");
|
||||
return SelfTestStepResult.Fail;
|
||||
}
|
||||
|
||||
// Wiring proof: drive the real PreDraw and confirm Flags == the helper's
|
||||
// value for the live config. No state mutation needed.
|
||||
var savedFlags = window.Flags;
|
||||
window.PreDraw();
|
||||
var expected = MainWindow.ResolveFlags(Plugin.Config.CanMove, Plugin.Config.CanResize);
|
||||
if (window.Flags != expected)
|
||||
{
|
||||
ImGui.Text($"PreDraw set Flags {window.Flags}, expected ResolveFlags = {expected}");
|
||||
window.Flags = savedFlags;
|
||||
return SelfTestStepResult.Fail;
|
||||
}
|
||||
|
||||
// Fresh-base contract: locked window carries NoMove|NoResize ...
|
||||
var locked = MainWindow.ResolveFlags(false, false);
|
||||
if (
|
||||
!locked.HasFlag(ImGuiWindowFlags.NoMove)
|
||||
|| !locked.HasFlag(ImGuiWindowFlags.NoResize)
|
||||
|| !locked.HasFlag(ImGuiWindowFlags.NoScrollbar)
|
||||
)
|
||||
{
|
||||
ImGui.Text(
|
||||
$"ResolveFlags(false,false) = {locked}, missing NoMove/NoResize/NoScrollbar"
|
||||
);
|
||||
window.Flags = savedFlags;
|
||||
return SelfTestStepResult.Fail;
|
||||
}
|
||||
|
||||
// ... and re-enabling both CLEARS NoMove|NoResize (no accumulation).
|
||||
var free = MainWindow.ResolveFlags(true, true);
|
||||
if (free.HasFlag(ImGuiWindowFlags.NoMove) || free.HasFlag(ImGuiWindowFlags.NoResize))
|
||||
{
|
||||
ImGui.Text($"ResolveFlags(true,true) = {free}, NoMove/NoResize stuck after re-enable");
|
||||
window.Flags = savedFlags;
|
||||
return SelfTestStepResult.Fail;
|
||||
}
|
||||
|
||||
window.Flags = savedFlags;
|
||||
return SelfTestStepResult.Pass;
|
||||
}
|
||||
|
||||
public void CleanUp() { }
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
using Dalamud.Bindings.ImGui;
|
||||
using Dalamud.Plugin.SelfTest;
|
||||
|
||||
namespace HellionChat.SelfTests;
|
||||
|
||||
// UI-12 focus opacity. Pins the pure ResolveBgAlpha contract (focused →
|
||||
// WindowOpacity, unfocused → WindowOpacityInactive). The PreDraw wiring
|
||||
// (BgAlpha = ResolveBgAlpha(IsFocused) behind the main-viewport/!docked guard)
|
||||
// is NOT headless-deterministic — the guard may leave BgAlpha null when
|
||||
// LastViewport is stale on a /xlperf frame — so the wiring is verified by the
|
||||
// reviewer grep (ResolveBgAlpha has a non-test caller: MainWindow.PreDraw) and
|
||||
// the visible transparency by in-game smoke, not by driving PreDraw here.
|
||||
internal sealed class MainWindowFocusOpacityStep : ISelfTestStep
|
||||
{
|
||||
private readonly Plugin plugin;
|
||||
|
||||
public MainWindowFocusOpacityStep(Plugin plugin)
|
||||
{
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
public string Name => "Hellion Chat - MainWindow focus opacity";
|
||||
|
||||
public SelfTestStepResult RunStep()
|
||||
{
|
||||
var window = this.plugin.MainWindow;
|
||||
if (window is null)
|
||||
{
|
||||
ImGui.Text("Plugin.MainWindow is null");
|
||||
return SelfTestStepResult.Fail;
|
||||
}
|
||||
|
||||
// Contract: focused returns the focused opacity, unfocused the inactive one.
|
||||
if (window.ResolveBgAlpha(true) != Plugin.Config.WindowOpacity)
|
||||
{
|
||||
ImGui.Text(
|
||||
$"ResolveBgAlpha(true) = {window.ResolveBgAlpha(true)}, expected {Plugin.Config.WindowOpacity}"
|
||||
);
|
||||
return SelfTestStepResult.Fail;
|
||||
}
|
||||
|
||||
if (window.ResolveBgAlpha(false) != Plugin.Config.WindowOpacityInactive)
|
||||
{
|
||||
ImGui.Text(
|
||||
$"ResolveBgAlpha(false) = {window.ResolveBgAlpha(false)}, expected {Plugin.Config.WindowOpacityInactive}"
|
||||
);
|
||||
return SelfTestStepResult.Fail;
|
||||
}
|
||||
|
||||
return SelfTestStepResult.Pass;
|
||||
}
|
||||
|
||||
public void CleanUp() { }
|
||||
}
|
||||
@@ -58,6 +58,48 @@ internal sealed class SidebarModeAutoSwitchStep : ISelfTestStep
|
||||
return SelfTestStepResult.Fail;
|
||||
}
|
||||
|
||||
// B1-3a: the expanded width must come from Config.SidebarWidth, not the
|
||||
// old fixed 150 constant. Drive the REAL GetWidth (the single source
|
||||
// Sidebar.Draw consumes) with concrete values and assert the OBSERVED
|
||||
// effect — in-range passthrough plus clamping — instead of mirroring the
|
||||
// Math.Clamp logic (SelfTests/README.md forbids re-implementing helper
|
||||
// logic in the test). Restore the config in finally so the live render
|
||||
// path is untouched.
|
||||
var savedSidebarWidth = Plugin.Config.SidebarWidth;
|
||||
try
|
||||
{
|
||||
Plugin.Config.SidebarWidth = 220;
|
||||
if (sidebar.GetWidth(threshold + 100f) != 220f)
|
||||
{
|
||||
ImGui.Text(
|
||||
$"GetWidth expanded = {sidebar.GetWidth(threshold + 100f)}, expected in-range Config.SidebarWidth 220"
|
||||
);
|
||||
return SelfTestStepResult.Fail;
|
||||
}
|
||||
|
||||
Plugin.Config.SidebarWidth = 9999;
|
||||
if (sidebar.GetWidth(threshold + 100f) != Sidebar.MaxSidebarWidth)
|
||||
{
|
||||
ImGui.Text(
|
||||
$"GetWidth expanded = {sidebar.GetWidth(threshold + 100f)}, expected clamp to MaxSidebarWidth {Sidebar.MaxSidebarWidth}"
|
||||
);
|
||||
return SelfTestStepResult.Fail;
|
||||
}
|
||||
|
||||
Plugin.Config.SidebarWidth = 1;
|
||||
if (sidebar.GetWidth(threshold + 100f) != Sidebar.MinSidebarWidth)
|
||||
{
|
||||
ImGui.Text(
|
||||
$"GetWidth expanded = {sidebar.GetWidth(threshold + 100f)}, expected clamp to MinSidebarWidth {Sidebar.MinSidebarWidth}"
|
||||
);
|
||||
return SelfTestStepResult.Fail;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
Plugin.Config.SidebarWidth = savedSidebarWidth;
|
||||
}
|
||||
|
||||
return SelfTestStepResult.Pass;
|
||||
}
|
||||
|
||||
|
||||
@@ -16,12 +16,17 @@ internal sealed class ChannelsTab
|
||||
{
|
||||
if (ImGui.CollapsingHeader("Tab management", ImGuiTreeNodeFlags.DefaultOpen))
|
||||
{
|
||||
DrawToggle(
|
||||
"Enable auto-tell tabs",
|
||||
() => Plugin.Config.EnableAutoTellTabs,
|
||||
v => Plugin.Config.EnableAutoTellTabs = v
|
||||
);
|
||||
DrawSliderInt(
|
||||
"Auto-tell tabs limit",
|
||||
() => Plugin.Config.AutoTellTabsLimit,
|
||||
v => Plugin.Config.AutoTellTabsLimit = v,
|
||||
1,
|
||||
32
|
||||
50
|
||||
);
|
||||
DrawToggle(
|
||||
"Compact display",
|
||||
@@ -55,21 +60,14 @@ internal sealed class ChannelsTab
|
||||
|
||||
if (ImGui.CollapsingHeader("Sidebar"))
|
||||
{
|
||||
DrawToggle(
|
||||
"Show sidebar tabs",
|
||||
() => Plugin.Config.SidebarTabView,
|
||||
v => Plugin.Config.SidebarTabView = v
|
||||
);
|
||||
// Range covers the on-disk default (44) plus Master-Spec §4.1 reference
|
||||
// (38px icon-only, 150px expanded). An earlier 120-400 range would clamp
|
||||
// the default 44 up to 120 silently. 30 leaves headroom for a future
|
||||
// ultra-tight icon-only mode; 300 stays above the 150 expanded reference
|
||||
// without giving the slider an absurd ceiling.
|
||||
// Range matches Sidebar.MinSidebarWidth/MaxSidebarWidth (40-300). The
|
||||
// lower bound sits just above the 38px icon-only threshold; the
|
||||
// on-disk default (44) and the 150px expanded reference both fit.
|
||||
DrawSliderInt(
|
||||
"Sidebar width",
|
||||
() => Plugin.Config.SidebarWidth,
|
||||
v => Plugin.Config.SidebarWidth = v,
|
||||
30,
|
||||
40,
|
||||
300
|
||||
);
|
||||
}
|
||||
|
||||
@@ -15,16 +15,6 @@ internal sealed class GeneralTab
|
||||
{
|
||||
if (ImGui.CollapsingHeader("Behavior", ImGuiTreeNodeFlags.DefaultOpen))
|
||||
{
|
||||
DrawToggle(
|
||||
"Allow window movement",
|
||||
() => Plugin.Config.CanMove,
|
||||
v => Plugin.Config.CanMove = v
|
||||
);
|
||||
DrawToggle(
|
||||
"Allow window resize",
|
||||
() => Plugin.Config.CanResize,
|
||||
v => Plugin.Config.CanResize = v
|
||||
);
|
||||
DrawToggle(
|
||||
"Reduce motion (no theme crossfade)",
|
||||
() => Plugin.Config.ReduceMotion,
|
||||
@@ -44,11 +34,6 @@ internal sealed class GeneralTab
|
||||
() => Plugin.Config.ShowNoviceNetwork,
|
||||
v => Plugin.Config.ShowNoviceNetwork = v
|
||||
);
|
||||
DrawToggle(
|
||||
"Enable auto-tell tabs",
|
||||
() => Plugin.Config.EnableAutoTellTabs,
|
||||
v => Plugin.Config.EnableAutoTellTabs = v
|
||||
);
|
||||
}
|
||||
|
||||
if (ImGui.CollapsingHeader("Volumes", ImGuiTreeNodeFlags.DefaultOpen))
|
||||
|
||||
@@ -48,6 +48,11 @@ internal sealed class WindowTab
|
||||
|
||||
if (ImGui.CollapsingHeader("Resize behavior", ImGuiTreeNodeFlags.DefaultOpen))
|
||||
{
|
||||
DrawToggle(
|
||||
"Allow movement",
|
||||
() => Plugin.Config.CanMove,
|
||||
v => Plugin.Config.CanMove = v
|
||||
);
|
||||
DrawToggle(
|
||||
"Allow resize",
|
||||
() => Plugin.Config.CanResize,
|
||||
|
||||
@@ -11,7 +11,7 @@ using Microsoft.Extensions.Logging;
|
||||
namespace HellionChat.Ui.Components;
|
||||
|
||||
// Channel-list panel pinned to the left of the chat window. Auto-switches
|
||||
// between an icon-only column (38px) and an expanded column (150px) once
|
||||
// between an icon-only column (38px) and an expanded column (Config.SidebarWidth) once
|
||||
// the outer window crosses Config.SidebarAutoSwitchThresholdPx. The
|
||||
// pop-out affordance (hover button + right-click menu) routes through the
|
||||
// injected ChannelPopoutPool via TryOpen, which reserves a slot and binds
|
||||
@@ -19,7 +19,12 @@ namespace HellionChat.Ui.Components;
|
||||
internal sealed class Sidebar
|
||||
{
|
||||
public const float IconOnlyWidth = 38f;
|
||||
public const float ExpandedWidth = 150f;
|
||||
|
||||
// B1-3a: expanded sidebar width is user-configurable (Config.SidebarWidth),
|
||||
// clamped to these bounds (matches the ChannelsTab slider range). Replaces
|
||||
// the old fixed 150px ExpandedWidth constant.
|
||||
public const float MinSidebarWidth = 40f;
|
||||
public const float MaxSidebarWidth = 300f;
|
||||
|
||||
private const float RowHeight = 32f;
|
||||
private const float PopOutHitWidth = 22f;
|
||||
@@ -72,7 +77,9 @@ internal sealed class Sidebar
|
||||
windowWidth >= Plugin.Config.SidebarAutoSwitchThresholdPx;
|
||||
|
||||
public float GetWidth(float windowWidth) =>
|
||||
IsExpanded(windowWidth) ? ExpandedWidth : IconOnlyWidth;
|
||||
IsExpanded(windowWidth)
|
||||
? Math.Clamp((float)Plugin.Config.SidebarWidth, MinSidebarWidth, MaxSidebarWidth)
|
||||
: IconOnlyWidth;
|
||||
|
||||
public void Draw(float windowWidth, IList<Tab> tabs, ref Tab? activeTab)
|
||||
{
|
||||
@@ -83,7 +90,7 @@ internal sealed class Sidebar
|
||||
}
|
||||
|
||||
var expanded = IsExpanded(windowWidth);
|
||||
var width = expanded ? ExpandedWidth : IconOnlyWidth;
|
||||
var width = GetWidth(windowWidth);
|
||||
using var child = ImRaii.Child("##hellion-sidebar", new Vector2(width, 0));
|
||||
if (!child.Success)
|
||||
return;
|
||||
|
||||
@@ -37,6 +37,9 @@ internal sealed class MainWindow : Window
|
||||
public Vector2 LastWindowSize { get; private set; } = Vector2.Zero;
|
||||
internal unsafe ImGuiViewport* LastViewport;
|
||||
|
||||
// 1.5.6 viewport-guard input: tracked in Draw, read by PreDraw next frame.
|
||||
private bool _wasDocked;
|
||||
|
||||
public MainWindow(
|
||||
Components.HonorificHeader honorific,
|
||||
Components.Sidebar sidebar,
|
||||
@@ -63,13 +66,57 @@ internal sealed class MainWindow : Window
|
||||
MinimumSize = new Vector2(MinWidth, MinHeight),
|
||||
MaximumSize = new Vector2(float.MaxValue, float.MaxValue),
|
||||
};
|
||||
// The message list owns its own scroll inside the body child;
|
||||
// the outer window must not show a second scrollbar.
|
||||
Flags = ImGuiWindowFlags.NoScrollbar | ImGuiWindowFlags.NoScrollWithMouse;
|
||||
IsOpen = Plugin.Config.MainWindowOpen;
|
||||
RespectCloseHotkey = false;
|
||||
}
|
||||
|
||||
// UI-12: per-window focus-dependent opacity. ResolveBgAlpha stays guard-free
|
||||
// and pure so the self-test can drive it directly; PreDraw owns the guard +
|
||||
// wiring. 1.5.6 parity (focused → WindowOpacity, unfocused →
|
||||
// WindowOpacityInactive, ChatLogWindow.PreOpenCheck 1d3b429:724).
|
||||
internal float ResolveBgAlpha(bool isFocused) =>
|
||||
isFocused ? Plugin.Config.WindowOpacity : Plugin.Config.WindowOpacityInactive;
|
||||
|
||||
// B1-2: rebuild flags from a fresh base every frame so toggling CanMove/
|
||||
// CanResize back on actually CLEARS NoMove/NoResize (not accumulating).
|
||||
// Move/resize toggle logic as 1.5.6 (ChatLogWindow.PreOpenCheck
|
||||
// 1d3b429:703-707); base flags = today's MainWindow set (NoScrollbar|
|
||||
// NoScrollWithMouse — the message list owns its own scroll; 1.5.6's
|
||||
// NoFocusOnAppearing/NoTitleBar are deliberately not restored).
|
||||
internal static ImGuiWindowFlags ResolveFlags(bool canMove, bool canResize)
|
||||
{
|
||||
var flags = ImGuiWindowFlags.NoScrollbar | ImGuiWindowFlags.NoScrollWithMouse;
|
||||
if (!canMove)
|
||||
flags |= ImGuiWindowFlags.NoMove;
|
||||
if (!canResize)
|
||||
flags |= ImGuiWindowFlags.NoResize;
|
||||
return flags;
|
||||
}
|
||||
|
||||
public override void PreDraw()
|
||||
{
|
||||
// Dalamud's WindowHost turns Window.BgAlpha into SetNextWindowBgAlpha
|
||||
// (WindowHost.cs:650-652), which REPLACES this one window's WindowBg
|
||||
// alpha (imgui.cpp:7229). The global GlobalStyleScope clamp is left
|
||||
// untouched, so Settings/DbViewer/popouts/wizard keep today's opacity.
|
||||
// Viewport guard (1.5.6 parity, ChatLogWindow.PreOpenCheck 1d3b429:718):
|
||||
// only drive BgAlpha while the window is on the main viewport and not
|
||||
// docked. On a floated own-viewport (Dalamud multi-viewport mode) the
|
||||
// WindowBg alpha would compose against the OS-layer alpha (double
|
||||
// transparency), so leave BgAlpha null there and let the global scope
|
||||
// govern. LastViewport/_wasDocked are last frame's values from Draw
|
||||
// (one-frame latency, accepted, matches 1.5.6).
|
||||
unsafe
|
||||
{
|
||||
if (LastViewport == ImGuiHelpers.MainViewport.Handle && !_wasDocked)
|
||||
BgAlpha = ResolveBgAlpha(IsFocused);
|
||||
else
|
||||
BgAlpha = null;
|
||||
}
|
||||
|
||||
Flags = ResolveFlags(Plugin.Config.CanMove, Plugin.Config.CanResize);
|
||||
}
|
||||
|
||||
public Tab? ActiveTab => _activeTab;
|
||||
|
||||
// Internal accessors for self-tests so the probes can reach the live
|
||||
@@ -99,6 +146,7 @@ internal sealed class MainWindow : Window
|
||||
{
|
||||
LastViewport = ImGui.GetWindowViewport().Handle;
|
||||
}
|
||||
_wasDocked = ImGui.IsWindowDocked();
|
||||
|
||||
// Primary pool-reset path; InputPreview has a defensive fallback for the MainWindow-closed edge case.
|
||||
_handlerLender.ResetCounter();
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"Author": "Jon Kazama (Hellion Forge)",
|
||||
"Name": "Hellion Chat",
|
||||
"InternalName": "HellionChat",
|
||||
"AssemblyVersion": "1.8.1.0",
|
||||
"AssemblyVersion": "1.8.2.0",
|
||||
"Description": "A Hellion Forge plugin — privacy-focused chat replacement for FINAL FANTASY XIV, built for EU, US and JP data rules.\n\nBy default only your own conversations are stored. Public chat, NPC dialogue, system messages and battle logs are discarded at the storage layer unless you opt in. Retention windows are configurable per channel, history can be wiped retroactively, and everything can be exported on demand.\n\nFeatures:\n- Channel whitelist with a Privacy-First default\n- Per-channel retention with a daily background sweep\n- Retroactive cleanup with preview and Ctrl+Shift confirm\n- Export to Markdown, JSON or CSV\n- First-run wizard with four profiles: Privacy-First, Casual, Roleplay, Full History\n- Multi-language UI (24 locales) with live language switching\n- Own config and database — no shared state with other plugins\n\nBased on Chat 2 by Infi and Anna (EUPL-1.2).\nSupport: https://discord.gg/X9V7Kcv5gR",
|
||||
"ApplicableVersion": "any",
|
||||
"RepoUrl": "https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat",
|
||||
@@ -25,7 +25,7 @@
|
||||
"DownloadLinkInstall": "https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/releases/download/v1.5.6/latest.zip",
|
||||
"DownloadLinkUpdate": "https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/releases/download/v1.5.6/latest.zip",
|
||||
"DownloadLinkTesting": "https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/releases/download/v1.5.6/latest.zip",
|
||||
"TestingAssemblyVersion": "1.8.1.0",
|
||||
"TestingAssemblyVersion": "1.8.2.0",
|
||||
"IconUrl": "https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/raw/branch/main/HellionChat/images/icon.png",
|
||||
"ImageUrls": [
|
||||
"https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/raw/branch/main/HellionChat/images/chatWindow.png",
|
||||
|
||||
Reference in New Issue
Block a user