Compare commits
8
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b15cb73f59 | ||
|
|
c1d6ca3bbd | ||
|
|
4ab9202533 | ||
|
|
5e15d34eeb | ||
|
|
b9feb8650f | ||
|
|
0f9858a3d3 | ||
|
|
a3379818eb | ||
|
|
d5c7db9f43 |
@@ -0,0 +1,7 @@
|
||||
---
|
||||
subtitle: "Kontextmenü in Pop-outs, Screenshot-Modus"
|
||||
versionsnatur: "Fehlerbehebung"
|
||||
---
|
||||
- **Das Kontextmenü ging in Pop-out-Fenstern nicht auf.** Ein Rechtsklick auf einen Namen oder ein Item hat dort schlicht nichts gemacht. Alle Chat-Flächen teilen sich einen Popup-Zustand, und das zuerst gezeichnete Fenster hat das Menü verworfen, bevor das Pop-out überhaupt an der Reihe war. Das Menü bleibt jetzt bei dem Fenster, aus dem der Klick kam.
|
||||
- **Der Screenshot-Modus ließ sich nicht ausschalten.** Er wird gespeichert, aber keiner der beiden Schalter hat die Änderung in die Konfiguration geschrieben. Einmal gespeichert kam er bei jedem Laden des Plugins wieder. Beide Schalter speichern jetzt. Wer ihn gerade festhängen hat, klickt ihn einmal aus.
|
||||
- **Vom Spiel eingefärbter Text konnte unsichtbar werden.** Manche dieser Farben kommen ohne Alpha-Byte an und wurden beim Umsortieren vollständig transparent.
|
||||
@@ -61,7 +61,7 @@ public sealed class FontManager : IDisposable
|
||||
// widget gallery exposes it.
|
||||
internal static float SenderWeight = 1.3f;
|
||||
|
||||
// Wired post-build (B4b-3); a Func keeps FontManager off the theme layer.
|
||||
// Wired post-build; a Func keeps FontManager off the theme layer.
|
||||
private Func<ThemeTypography?>? _typographySource;
|
||||
|
||||
// Lets RebuildDelegateFontsIfChanged skip rebuilds when the size is unchanged.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<Project Sdk="Dalamud.NET.Sdk/15.0.0">
|
||||
<PropertyGroup>
|
||||
<!-- Independent versioning; see yaml changelog for upstream Chat 2 base -->
|
||||
<Version>2.0.2</Version>
|
||||
<Version>2.0.3</Version>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<!-- Use lock file to pin exact versions -->
|
||||
|
||||
@@ -35,6 +35,14 @@ tags:
|
||||
- Replacement
|
||||
- Privacy
|
||||
changelog: |-
|
||||
**v2.0.3 — Pop-out context menu, screenshot mode (2026-08-20)**
|
||||
|
||||
- **The context menu never opened in a pop-out window.** Right-clicking a name or an item there did nothing at all. Every chat surface shares one popup state, and whichever window drew first cleared the menu it had not opened before the pop-out got its turn. The menu now stays with the window the click came from.
|
||||
- **Screenshot mode could not be turned off.** It is a saved setting, but neither of its two toggles wrote the change to the config file, so switching it off never survived a reload and the mode came back on at every plugin load. Both toggles save now. If it is stuck on for you, one click off is enough.
|
||||
- **Text the game colours itself could render invisible.** Some of those colours arrive without an alpha byte, and the byte order swap then left them fully transparent.
|
||||
|
||||
---
|
||||
|
||||
**v2.0.2 — Emotes out, placeholders fixed (2026-08-19)**
|
||||
|
||||
- **BetterTTV emote support is gone.** Its shared-emote endpoint went behind authentication, and that was where nearly all of them came from — what remained was a 65-entry global set, eleven of which are on the known-broken list, so 54 mostly static images from Twitch's early days. Exactly one was animated, and that one wanted 492 frames and 37 MB of video memory. The plugin now makes no outbound network calls at all. Messages stored with emotes still read fine; they show the code that was typed.
|
||||
@@ -83,18 +91,4 @@ changelog: |-
|
||||
|
||||
Based on Chat 2 1.35.3 (upstream Infiziert90/ChatTwo, EUPL-1.2). The two codebases have diverged far enough that they no longer line up.
|
||||
|
||||
---
|
||||
|
||||
**v1.5.6 — Settings Overhaul + Filter & Notification Polish (2026-05-23)**
|
||||
|
||||
- Settings window reorganised: ten tabs down to seven (General, Appearance, Chat, Window, Channels, Data & Privacy, About). Each tab now uses collapsible sections grouped by control type. Sections start collapsed every time you open a tab — less noise, easier to find what you need.
|
||||
- New sender-name display options under Chat → Messages: separate world-suffix and name-format modes (Full name / First name only / Initials × Never / Other worlds only / Always).
|
||||
- Plugin-only symbols now show a pre-send warning so other players do not get empty boxes (Chat → Messages → "Warn before sending plugin-only symbols").
|
||||
- Separate window opacity for focused vs. inactive chat window (Appearance → Window style → "Inactive window opacity"). The slider above sets the focused value.
|
||||
- Custom notification sound volume slider (General → Sound, and mirrored in Channels → per-tab → Notification). Affects only the three bundled custom sounds; the 16 game sounds are unaffected.
|
||||
- The per-tab regex filter that briefly shipped earlier in this cycle has been removed — FFXIV's built-in blackword filter covers the same need.
|
||||
- All 24 locale files updated for the new section labels and the v1.5.6 control labels (machine translation; native review continues via the Hellion Forge Discord).
|
||||
|
||||
Based on Chat 2 1.35.3 (upstream Infiziert90/ChatTwo, EUPL-1.2).
|
||||
|
||||
Earlier history: https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/releases
|
||||
|
||||
@@ -29,7 +29,7 @@ internal sealed class ThemeRegistryInitHostedService(
|
||||
foreach (var _ in registry.AllCustom()) { }
|
||||
registry.SwitchSilent(Plugin.Config.Theme);
|
||||
|
||||
// B4b-3: point font sizes at the active theme's typography, wire future
|
||||
// Point font sizes at the active theme's typography, wire future
|
||||
// theme switches to the atlas rebuild, and apply the boot theme's override.
|
||||
fontManager.SetTypographySource(() => registry.Active.Typography);
|
||||
registry.SetActiveChangedCallback(() => fontManager.RebuildDelegateFontsIfChanged());
|
||||
|
||||
@@ -49,6 +49,7 @@ internal sealed class PayloadHandler
|
||||
public uint LastHoverCounter;
|
||||
|
||||
private (Chunk, Payload?)? _popup;
|
||||
private object? _popupOwner;
|
||||
|
||||
public PayloadHandler(
|
||||
ThemeRegistry themes,
|
||||
@@ -69,6 +70,10 @@ internal sealed class PayloadHandler
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
// Every chat surface shares this handler, so each one claims it before it
|
||||
// renders. Without that the popup cannot tell whose click it belongs to.
|
||||
internal object? ActiveSurface { get; set; }
|
||||
|
||||
internal void Draw()
|
||||
{
|
||||
DrawPopups();
|
||||
@@ -84,7 +89,9 @@ internal sealed class PayloadHandler
|
||||
|
||||
private void DrawPopups()
|
||||
{
|
||||
if (_popup == null)
|
||||
// A foreign surface finds no open popup here and would otherwise read
|
||||
// that as "closed" and drop the state before the owner gets to draw.
|
||||
if (_popup == null || !PopupOwnership.Owns(_popupOwner, ActiveSurface))
|
||||
return;
|
||||
|
||||
var (chunk, payload) = _popup.Value;
|
||||
@@ -93,6 +100,7 @@ internal sealed class PayloadHandler
|
||||
if (!popup.Success)
|
||||
{
|
||||
_popup = null;
|
||||
_popupOwner = null;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -186,7 +194,10 @@ internal sealed class PayloadHandler
|
||||
return;
|
||||
}
|
||||
|
||||
ImGui.Checkbox(Language.Context_ScreenshotMode, ref Plugin.Config.ScreenshotMode);
|
||||
// The flag is persisted, so every toggle has to write it. Without the
|
||||
// write it only sticks when some unrelated save happens to run after.
|
||||
if (ImGui.Checkbox(Language.Context_ScreenshotMode, ref Plugin.Config.ScreenshotMode))
|
||||
Plugin.Instance.SaveConfig();
|
||||
|
||||
if (ImGui.Selectable(Language.Context_HideChat))
|
||||
Plugin.Config.HideChat = true;
|
||||
@@ -934,6 +945,7 @@ internal sealed class PayloadHandler
|
||||
private void RightClickPayload(Chunk chunk, Payload? payload)
|
||||
{
|
||||
_popup = (chunk, payload);
|
||||
_popupOwner = ActiveSurface;
|
||||
ImGui.OpenPopup(PopupId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ using Dalamud.Plugin.SelfTest;
|
||||
namespace HellionChat.SelfTests;
|
||||
|
||||
// Optional metric capture. Accumulates 1000 steady-state frames of ImGui IO
|
||||
// counters plus the plugin's full-Draw wall-time (Plugin.LastDrawMs, B5-1),
|
||||
// counters plus the plugin's full-Draw wall-time (Plugin.LastDrawMs),
|
||||
// then writes a single perf-baseline.json into the plugin ConfigDirectory so
|
||||
// the cycle-notes author can copy the baseline figures without a separate
|
||||
// profiling harness. The step only records — it never fails on a threshold
|
||||
|
||||
@@ -13,7 +13,7 @@ internal static class ThemeJsonLoader
|
||||
// policy from the v2.x style refactor: v1 user themes are not migrated,
|
||||
// they're silently ignored so the loader stays free of legacy mapping
|
||||
// code. Any other malformed input still throws FormatException.
|
||||
// B4b-2: callers must pass the logger or the default-fill warnings go silent.
|
||||
// Callers must pass the logger or the default-fill warnings go silent.
|
||||
public static Theme? LoadFromString(string json, ILogger? logger = null)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(json))
|
||||
|
||||
@@ -901,7 +901,10 @@ internal sealed class InputBar
|
||||
)
|
||||
)
|
||||
{
|
||||
// Persisted flag -- write it here too, or turning it back off
|
||||
// never reaches the config file.
|
||||
Plugin.Config.ScreenshotMode = !Plugin.Config.ScreenshotMode;
|
||||
Plugin.Instance.SaveConfig();
|
||||
}
|
||||
|
||||
if (
|
||||
|
||||
@@ -158,6 +158,11 @@ internal sealed class MessageList
|
||||
return;
|
||||
}
|
||||
|
||||
// Claim the shared handler, so a popup opened here stays with this
|
||||
// window instead of being cleared by whichever surface draws first.
|
||||
if (_handler is not null)
|
||||
_handler.ActiveSurface = this;
|
||||
|
||||
// No own ImRaii.Child here — MainWindow already wraps the message
|
||||
// area in one. Nesting would give the window two stacked scrolls
|
||||
// and a runaway content-height computation.
|
||||
|
||||
@@ -52,7 +52,7 @@ internal sealed class TopTabBar
|
||||
for (var i = 0; i < tabs.Count; i++)
|
||||
{
|
||||
var tab = tabs[i];
|
||||
// POP-1b: a popped-out tab is owned by its pop-out window, not the main
|
||||
// A popped-out tab is owned by its pop-out window, not the main
|
||||
// strip (1.5.6 exclusivity). Gate on the pool, not Tab.PopOut (stale flag).
|
||||
if (_pool.IsOpen(tab.Identifier))
|
||||
continue;
|
||||
|
||||
@@ -175,6 +175,7 @@ internal sealed class InputPreview : Window
|
||||
_handlerLender.ResetCounter();
|
||||
|
||||
var handler = _handlerLender.Borrow();
|
||||
handler.ActiveSurface = this;
|
||||
_chunkRenderer.DrawChunks(
|
||||
_previewMessage!.Content,
|
||||
wrap: true,
|
||||
|
||||
@@ -155,7 +155,7 @@ internal sealed class ChannelPopoutWindow : Window, IFocusableChatWindow
|
||||
if (Bound is null)
|
||||
return;
|
||||
|
||||
// POP-1f: the bound tab is live-visible in this pop-out, so it carries no
|
||||
// The bound tab is live-visible in this pop-out, so it carries no
|
||||
// unread badge — mirror MainWindow's per-frame zero for the active tab.
|
||||
// View-state reset only (tab.Messages store is untouched).
|
||||
Bound.Unread = 0;
|
||||
|
||||
@@ -310,9 +310,9 @@ internal sealed class MainWindow : Window, IFocusableChatWindow
|
||||
TabLifecycleHelpers.OnTabActivated(reseed, active);
|
||||
}
|
||||
|
||||
// POP-1c: a popped-out tab must not stay the main window's active surface
|
||||
// A popped-out tab must not stay the main window's active surface
|
||||
// (1.5.6 exclusivity). Re-anchor to the first non-popped tab the moment the
|
||||
// active one is popped; null when every tab is popped (POP-1d guards Draw).
|
||||
// active one is popped; null when every tab is popped, which Draw guards for.
|
||||
// Runs post-seed, before the sidebar/top-tab draw, so the popped tab never
|
||||
// renders. Idempotent: PickMainActiveTab returns the same reference once
|
||||
// settled, so OnTabActivated fires only on the pop frame.
|
||||
|
||||
@@ -66,8 +66,23 @@ internal static class ColourUtil
|
||||
);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// Cherry-picked from ChatTwo upstream a51789b + 5b58513 (Infiziert90,
|
||||
// 2026-06-12). Both guards belong together -- a51789b alone turns a zero
|
||||
// colour into opaque black, which then slips past the invisible-text
|
||||
// guard in ChunkUtil and paints over the inherited colour.
|
||||
// TEST-MIRROR: ../../../Hellion Build test/Util/ColourUtilTests.cs
|
||||
// ---------------------------------------------------------------
|
||||
public static unsafe uint ArgbToRgba(uint x)
|
||||
{
|
||||
if (x == 0)
|
||||
return 0;
|
||||
|
||||
// The game omits the alpha byte on some GlobalValue colours, and the
|
||||
// swap below would leave alpha at 0 -- invisible text.
|
||||
if (x <= 0x00FFFFFFu)
|
||||
x |= 0xFF000000u;
|
||||
|
||||
var buf = (byte*)&x;
|
||||
(buf[1], buf[2], buf[3], buf[0]) = (buf[0], buf[1], buf[2], buf[3]);
|
||||
return x;
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
namespace HellionChat.Util;
|
||||
|
||||
// Chat surfaces share one payload handler and all render it in the same frame.
|
||||
// A popup belongs to the surface that opened it; the others must leave its
|
||||
// state alone instead of reading "no popup here" as "popup was closed".
|
||||
internal static class PopupOwnership
|
||||
{
|
||||
// Reference identity on purpose -- two surfaces can compare equal without
|
||||
// being the same window.
|
||||
internal static bool Owns(object? owner, object? surface) =>
|
||||
owner is not null && ReferenceEquals(owner, surface);
|
||||
}
|
||||
@@ -165,7 +165,7 @@ internal static class TabLifecycleHelpers
|
||||
|
||||
// Sectioned sidebar render order (1.5.6 parity): persistent → pinned TempTabs →
|
||||
// unpinned TempTabs. Returns indices into the live tab list so the list order is
|
||||
// never mutated and DrawRow keeps each tab's ORIGINAL index for PushID. POP-1a:
|
||||
// never mutated and DrawRow keeps each tab's ORIGINAL index for PushID.
|
||||
// isPoppedOut excludes tabs bound to a pop-out window — an excluded tab draws no
|
||||
// row and its pool's section header gates on the first tab actually reached. Pure
|
||||
// + Dalamud-free so the Build-Suite can pin it.
|
||||
@@ -215,7 +215,7 @@ internal static class TabLifecycleHelpers
|
||||
return persistent;
|
||||
}
|
||||
|
||||
// POP-1c: returns the tab the main window should display — the current tab if it
|
||||
// Returns the tab the main window should display — the current tab if it
|
||||
// is not popped out, else the FIRST non-popped tab in list order, else null when
|
||||
// every tab is popped. NOTE: deliberately NOT ResetActiveTabIfRemoved's
|
||||
// unconditional Tabs[0] — Tabs[0] may itself be popped and would re-trigger the
|
||||
@@ -243,7 +243,7 @@ internal static class TabLifecycleHelpers
|
||||
Popout,
|
||||
}
|
||||
|
||||
// POP-1f: the alreadyPopped case is the whole reason this is a function.
|
||||
// The alreadyPopped case is the whole reason this is a function.
|
||||
//
|
||||
// Revealing a popped-out tab in the main window looks like it does nothing,
|
||||
// and then does something worse: PickMainActiveTab re-anchors on the next
|
||||
@@ -268,12 +268,12 @@ internal static class TabLifecycleHelpers
|
||||
_ => switchAlways && !alreadyPopped ? TellReveal.MainWindow : TellReveal.None,
|
||||
};
|
||||
|
||||
// POP-1e: popout-aware sibling of WrapTabIndex for the ChatTabForward/Backward
|
||||
// Popout-aware sibling of WrapTabIndex for the ChatTabForward/Backward
|
||||
// keybind. Steps from current by delta's sign (±1), wrapping, and returns the
|
||||
// first index whose tab is NOT popped out within tabs.Count steps; returns current
|
||||
// when every other tab is popped (no-op), the list is empty, or a single tab.
|
||||
// Skipping popped tabs here is what stops the POP-1c re-anchor from making the
|
||||
// cycle stick (CYCLE-1). Pure + Dalamud-free.
|
||||
// Skipping popped tabs here is what stops the PickMainActiveTab re-anchor
|
||||
// from making the cycle stick. Pure + Dalamud-free.
|
||||
// TEST-MIRROR: ../../../Hellion Build test/_Helpers/NextMainTabIndexTests.cs
|
||||
internal static int NextMainTabIndex(
|
||||
int current,
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
[](https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/actions/workflows/build.yml)
|
||||
[](LICENSE)
|
||||
[](https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/releases/latest)
|
||||
[](https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/releases/latest)
|
||||
[](https://github.com/goatcorp/Dalamud)
|
||||
[](https://dotnet.microsoft.com/)
|
||||
[](https://www.finalfantasyxiv.com/)
|
||||
@@ -11,7 +11,7 @@
|
||||
<img src="docs/images/hellion-forge.png" alt="Hellion Forge" width="180" />
|
||||
</p>
|
||||
|
||||
**Version 2.0.2** — Privacy-first chat plugin for FINAL FANTASY XIV / Dalamud, originally
|
||||
**Version 2.0.3** — Privacy-first chat plugin for FINAL FANTASY XIV / Dalamud, originally
|
||||
forked from [Chat 2](https://github.com/Infiziert90/ChatTwo) (EUPL-1.2).
|
||||
|
||||
Hellion Chat is a privacy-first chat plugin that began as a fork of Chat 2. The core it grew from
|
||||
|
||||
@@ -11,6 +11,26 @@ releases as an overview and links to the release pages for details.
|
||||
|
||||
---
|
||||
|
||||
## [2.0.3] — 2026-08-20
|
||||
|
||||
### Fixed
|
||||
|
||||
- The context menu never opened in a pop-out window. Right-clicking a name or an
|
||||
item there did nothing at all. One payload handler is shared by the main
|
||||
window, every pop-out and the input preview, and it carries a single popup
|
||||
state. The main window draws first, found no open popup in its own scope, read
|
||||
that as closed and cleared the state before the pop-out had drawn. A popup now
|
||||
belongs to the surface that opened it and the others leave it alone.
|
||||
- Screenshot mode could not be turned off. The setting is persisted, but neither
|
||||
of its two toggles saved the config, so turning it on only stuck when an
|
||||
unrelated save happened to follow — and once stored, turning it off never
|
||||
reached the file, so it returned at every plugin load. Both toggles save now;
|
||||
an install currently stuck on it needs one click.
|
||||
- Text the game colours itself could render invisible. Some of those colours
|
||||
arrive without an alpha byte, and the byte order swap left them fully
|
||||
transparent. A colour that carries no value at all still resolves to the
|
||||
colour already in effect rather than to opaque black.
|
||||
|
||||
## [2.0.2] — 2026-08-19
|
||||
|
||||
### Removed
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"Author": "Jon Kazama (Hellion Forge)",
|
||||
"Name": "Hellion Chat",
|
||||
"InternalName": "HellionChat",
|
||||
"AssemblyVersion": "2.0.2.0",
|
||||
"AssemblyVersion": "2.0.3.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",
|
||||
@@ -20,12 +20,12 @@
|
||||
"CanUnloadAsync": false,
|
||||
"LoadPriority": 0,
|
||||
"Punchline": "A Hellion Forge plugin. Privacy-first chat for FFXIV, built to stay out of your way.",
|
||||
"Changelog": "**v2.0.2 — Emotes out, placeholders fixed (2026-08-19)**\n\n- **BetterTTV emote support is gone.** Its shared-emote endpoint went behind authentication, and that was where nearly all of them came from — what remained was a 65-entry global set, eleven of which are on the known-broken list, so 54 mostly static images from Twitch's early days. Exactly one was animated, and that one wanted 492 frames and 37 MB of video memory. The plugin now makes no outbound network calls at all. Messages stored with emotes still read fine; they show the code that was typed.\n- **Five settings descriptions printed `{0}` instead of the plugin name.** All 25 languages were affected, English included — it just hid better there, since \"Hide {0} during cutscenes\" still scans as a sentence while German puts the placeholder first. A test now walks every resource string with a placeholder and fails if one reaches a widget unformatted.\n- **New preview images.** The ones in the plugin installer were from 8 May and showed the interface as it looked before any of the window rebuilds.",
|
||||
"Changelog": "**v2.0.3 \u2014 Pop-out context menu, screenshot mode (2026-08-20)**\n\n- **The context menu never opened in a pop-out window.** Right-clicking a name or an item there did nothing at all. Every chat surface shares one popup state, and whichever window drew first cleared the menu it had not opened before the pop-out got its turn. The menu now stays with the window the click came from.\n- **Screenshot mode could not be turned off.** It is a saved setting, but neither of its two toggles wrote the change to the config file, so switching it off never survived a reload and the mode came back on at every plugin load. Both toggles save now. If it is stuck on for you, one click off is enough.\n- **Text the game colours itself could render invisible.** Some of those colours arrive without an alpha byte, and the byte order swap then left them fully transparent.\n\n---\n\n**v2.0.2 — Emotes out, placeholders fixed (2026-08-19)**\n\n- **BetterTTV emote support is gone.** Its shared-emote endpoint went behind authentication, and that was where nearly all of them came from — what remained was a 65-entry global set, eleven of which are on the known-broken list, so 54 mostly static images from Twitch's early days. Exactly one was animated, and that one wanted 492 frames and 37 MB of video memory. The plugin now makes no outbound network calls at all. Messages stored with emotes still read fine; they show the code that was typed.\n- **Five settings descriptions printed `{0}` instead of the plugin name.** All 25 languages were affected, English included — it just hid better there, since \"Hide {0} during cutscenes\" still scans as a sentence while German puts the placeholder first. A test now walks every resource string with a placeholder and fails if one reaches a widget unformatted.\n- **New preview images.** The ones in the plugin installer were from 8 May and showed the interface as it looked before any of the window rebuilds.",
|
||||
"AcceptsFeedback": true,
|
||||
"DownloadLinkInstall": "https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/releases/download/v2.0.2/latest.zip",
|
||||
"DownloadLinkUpdate": "https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/releases/download/v2.0.2/latest.zip",
|
||||
"DownloadLinkTesting": "https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/releases/download/v2.0.2/latest.zip",
|
||||
"TestingAssemblyVersion": "2.0.2.0",
|
||||
"DownloadLinkInstall": "https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/releases/download/v2.0.3/latest.zip",
|
||||
"DownloadLinkUpdate": "https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/releases/download/v2.0.3/latest.zip",
|
||||
"DownloadLinkTesting": "https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/releases/download/v2.0.3/latest.zip",
|
||||
"TestingAssemblyVersion": "2.0.3.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