chore(release): 2.0.2 -- BetterTTV out, placeholders fixed
Forge Announce / Post changelog to Hellion Forge (push) Successful in 9s
Security Scan (reusable) / Security Scan (push) Successful in 27s
Security / scan (push) Successful in 27s
Build / Build (Release) (push) Successful in 34s
Release / Build and attach release ZIP (push) Successful in 31s
Forge Announce / Post changelog to Hellion Forge (push) Successful in 9s
Security Scan (reusable) / Security Scan (push) Successful in 27s
Security / scan (push) Successful in 27s
Build / Build (Release) (push) Successful in 34s
Release / Build and attach release ZIP (push) Successful in 31s
BetterTTV emote support is removed rather than switched off. Its shared-emote
endpoint went behind authentication and that is where nearly all of them came
from; what was left is the 65-entry global set, eleven of those on the plugin's
own known-broken list, so 54 largely static images from Twitch's early days.
Exactly one is animated, and that one is 492 frames at 140x140 -- 37 MB of
texture memory for a single emote, uploaded one frame at a time. Not worth a
network call and an on-disk cache on every start.
Gone with it: the download path, the cache directory, the GIF renderer, the
settings section, the block list, and 13 translation keys across 50 resource
files. 1567 lines. The plugin now makes no outbound network calls at all, which
is the claim PRIVACY.md has always wanted to make without an asterisk.
EmotePayload and its MessagePack type byte stay, deliberately. Around a thousand
rows in a two-month-old database carry them, and dropping the type would make
those fail to deserialise -- the history is the one thing 2.0.0 promised not to
touch. Nothing writes one any more and a stored emote renders as the code that
was typed, which is what the sender saw when they typed it.
Five descriptions in the Window tab printed {0} where the plugin name belonged,
handed to the widget directly instead of through string.Format the way the rows
around them do. Every language was affected including English; German surfaced
it because the placeholder lands at the start of the sentence there. A test now
walks every placeholder-carrying resource string, finds its uses, and fails on
an unformatted one -- verified by putting the defect back and watching it go red.
New preview images. The old set was from 2026-05-08, older than every cycle in
2.0.0, and showed stacked ImGui defaults to anyone browsing the installer. Taken
with screenshot mode on so no character names reach a public repo. The wizard
takes the theme picker's slot.
This commit is contained in:
@@ -88,17 +88,7 @@ internal sealed class ChunkRenderer
|
||||
DrawChunk(chunks[i], wrap, handler, lineWidth);
|
||||
|
||||
if (i < chunks.Count - 1)
|
||||
{
|
||||
ImGui.SameLine();
|
||||
}
|
||||
else if (chunks[i].Link is EmotePayload && Plugin.Config.ShowEmotes)
|
||||
{
|
||||
// Emote payloads seem to not automatically put newlines, which
|
||||
// is an issue when modern mode is disabled.
|
||||
ImGui.SameLine();
|
||||
// Use default ImGui behavior for newlines.
|
||||
ImGui.TextUnformatted("");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,31 +108,6 @@ internal sealed class ChunkRenderer
|
||||
if (chunk is not TextChunk text)
|
||||
return;
|
||||
|
||||
if (chunk.Link is EmotePayload emotePayload && Plugin.Config.ShowEmotes)
|
||||
{
|
||||
var emoteSize = ImGui.CalcTextSize("W");
|
||||
emoteSize = emoteSize with { Y = emoteSize.X } * 1.5f;
|
||||
|
||||
// TextWrap doesn't work for emotes, so we have to wrap them manually
|
||||
if (ImGui.GetContentRegionAvail().X < emoteSize.X)
|
||||
ImGui.NewLine();
|
||||
|
||||
// We only draw a dummy if it is still loading, in the case it failed we draw the actual name
|
||||
var image = EmoteCache.GetEmote(emotePayload.Code);
|
||||
if (image is { Failed: false })
|
||||
{
|
||||
if (image.IsLoaded)
|
||||
image.Draw(emoteSize);
|
||||
else
|
||||
ImGui.Dummy(emoteSize);
|
||||
|
||||
if (ImGui.IsItemHovered())
|
||||
ImGuiUtil.Tooltip(emotePayload.Code);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
var colour = text.Foreground;
|
||||
if (colour == null && text.FallbackColour != null)
|
||||
{
|
||||
|
||||
@@ -13,8 +13,6 @@ internal sealed class ChatTab
|
||||
private readonly Plugin _plugin;
|
||||
private readonly SettingsWidgets _w;
|
||||
|
||||
private string _blockedEmoteInput = string.Empty;
|
||||
|
||||
public ChatTab(Plugin plugin, TokenResolver resolver)
|
||||
{
|
||||
_plugin = plugin;
|
||||
@@ -129,17 +127,6 @@ internal sealed class ChatTab
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
_w.Section(
|
||||
ImGui.GetID("chat.emotes"u8),
|
||||
HellionStrings.Settings_Section_Emotes,
|
||||
open: false
|
||||
)
|
||||
)
|
||||
{
|
||||
DrawEmotes();
|
||||
}
|
||||
|
||||
if (
|
||||
_w.Section(
|
||||
ImGui.GetID("chat.autotranslate"u8),
|
||||
@@ -157,66 +144,4 @@ internal sealed class ChatTab
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// PRIVACY.md names the switch below as the way to stop the one outbound
|
||||
// call the plugin makes, and it had no control at all since May. A
|
||||
// documented opt-out that lives only in the JSON is not an opt-out.
|
||||
private void DrawEmotes()
|
||||
{
|
||||
_w.ToggleRow(
|
||||
ImGui.GetID("chat.emotes.show"u8),
|
||||
Language.Options_ShowEmotes_Name,
|
||||
Language.Options_ShowEmotes_Desc,
|
||||
() => Plugin.Config.ShowEmotes,
|
||||
v => Plugin.Config.ShowEmotes = v
|
||||
);
|
||||
|
||||
if (!Plugin.Config.ShowEmotes)
|
||||
return;
|
||||
|
||||
ImGui.Spacing();
|
||||
ImGui.TextUnformatted(
|
||||
EmoteCache.State == EmoteCache.LoadingState.Done
|
||||
? $"{Language.Options_Emote_Loaded} {EmoteCache.SortedCodeArray.Length:N0} ({Language.Options_Emote_Ready})"
|
||||
: $"{Language.Options_Emote_Loaded} {Language.Options_Emote_NotReady}"
|
||||
);
|
||||
|
||||
ImGui.Spacing();
|
||||
ImGui.TextUnformatted(Language.Options_Emote_BlockedEmotes);
|
||||
|
||||
// A blocked code stops that one emote from ever being drawn or
|
||||
// downloaded, which is a finer instrument than switching emotes off
|
||||
// wholesale. The list has been in the config since v1.0 with nowhere to
|
||||
// edit it.
|
||||
ImGui.SetNextItemWidth(220f * ImGuiHelpers.GlobalScale);
|
||||
ImGui.InputText("##hc-blocked-emote", ref _blockedEmoteInput, 64);
|
||||
ImGui.SameLine();
|
||||
|
||||
var candidate = _blockedEmoteInput.Trim();
|
||||
using (ImRaii.Disabled(candidate.Length == 0))
|
||||
{
|
||||
if (ImGui.Button(HellionStrings.Settings_Emotes_Block))
|
||||
{
|
||||
lock (_plugin.ConfigMapsLock)
|
||||
Plugin.Config.BlockedEmotes.Add(candidate);
|
||||
_blockedEmoteInput = string.Empty;
|
||||
_plugin.SaveConfig();
|
||||
}
|
||||
}
|
||||
|
||||
// Snapshot: the remove button mutates the set inside the loop.
|
||||
foreach (var blocked in Plugin.Config.BlockedEmotes.OrderBy(e => e).ToList())
|
||||
{
|
||||
using var id = ImRaii.PushId(blocked);
|
||||
if (ImGuiUtil.IconButton(FontAwesomeIcon.Trash, "##remove"))
|
||||
{
|
||||
lock (_plugin.ConfigMapsLock)
|
||||
Plugin.Config.BlockedEmotes.Remove(blocked);
|
||||
_plugin.SaveConfig();
|
||||
}
|
||||
|
||||
ImGui.SameLine();
|
||||
ImGui.TextUnformatted(blocked);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -187,14 +187,14 @@ internal sealed class WindowTab
|
||||
_w.ToggleRow(
|
||||
ImGui.GetID("window.hide.uihidden"u8),
|
||||
Language.Options_HideWhenUiHidden_Name,
|
||||
Language.Options_HideWhenUiHidden_Description,
|
||||
string.Format(Language.Options_HideWhenUiHidden_Description, Plugin.PluginName),
|
||||
() => Plugin.Config.HideWhenUiHidden,
|
||||
v => Plugin.Config.HideWhenUiHidden = v
|
||||
);
|
||||
_w.ToggleRow(
|
||||
ImGui.GetID("window.hide.loading"u8),
|
||||
Language.Options_HideInLoadingScreens_Name,
|
||||
Language.Options_HideInLoadingScreens_Description,
|
||||
string.Format(Language.Options_HideInLoadingScreens_Description, Plugin.PluginName),
|
||||
() => Plugin.Config.HideInLoadingScreens,
|
||||
v => Plugin.Config.HideInLoadingScreens = v
|
||||
);
|
||||
@@ -212,7 +212,7 @@ internal sealed class WindowTab
|
||||
_w.ToggleRow(
|
||||
ImGui.GetID("window.hide.cutscenes"u8),
|
||||
Language.Options_HideDuringCutscenes_Name,
|
||||
Language.Options_HideDuringCutscenes_Description,
|
||||
string.Format(Language.Options_HideDuringCutscenes_Description, Plugin.PluginName),
|
||||
() => Plugin.Config.HideDuringCutscenes,
|
||||
v => Plugin.Config.HideDuringCutscenes = v
|
||||
);
|
||||
@@ -226,7 +226,7 @@ internal sealed class WindowTab
|
||||
_w.ToggleRow(
|
||||
ImGui.GetID("window.hide.notloggedin"u8),
|
||||
Language.Options_HideWhenNotLoggedIn_Name,
|
||||
Language.Options_HideWhenNotLoggedIn_Description,
|
||||
string.Format(Language.Options_HideWhenNotLoggedIn_Description, Plugin.PluginName),
|
||||
() => Plugin.Config.HideWhenNotLoggedIn,
|
||||
v => Plugin.Config.HideWhenNotLoggedIn = v
|
||||
);
|
||||
@@ -243,7 +243,7 @@ internal sealed class WindowTab
|
||||
_w.ToggleRow(
|
||||
ImGui.GetID("window.tooltips.native"u8),
|
||||
Language.Options_NativeItemTooltips_Name,
|
||||
Language.Options_NativeItemTooltips_Description,
|
||||
string.Format(Language.Options_NativeItemTooltips_Description, Plugin.PluginName),
|
||||
() => Plugin.Config.NativeItemTooltips,
|
||||
v => Plugin.Config.NativeItemTooltips = v
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user