Merge branch 'feature/v2.0.0' into main
Security Scan (reusable) / Security Scan (push) Failing after 24s
Security / scan (push) Failing after 24s
Forge Announce / Post changelog to Hellion Forge (push) Successful in 11s
Build / Build (Release) (push) Successful in 33s
Release / Build and attach release ZIP (push) Failing after 30s

This commit is contained in:
2026-08-19 22:36:26 +02:00
124 changed files with 1931 additions and 1148 deletions
+11
View File
@@ -0,0 +1,11 @@
---
subtitle: "Rebuilt, Repaired, Reset"
versionsnatur: "Major-Release mit Config-Reset"
---
- **Deine Einstellungen werden zurückgesetzt.** Neun Zyklen Umbau haben gespeicherte Werte hinterlassen, die auf Oberflächen zeigen, die es nicht mehr gibt. Neu anzufangen ist der einzige Weg, dass alle dieselben Vorgaben haben. **Dein Nachrichtenverlauf bleibt unberührt**, der liegt in einer eigenen Datenbank. Die alten Einstellungen liegen als `HellionChat.json.pre-2.0.0.bak` daneben.
- **Behoben, und mehrere davon haben Daten verloren oder versteckt:** Die rückwirkende Bereinigung ließ sich nie anwenden. Das Verdichten der Datenbank schlug fehl und meldete danach, es sei nichts gelöscht worden, während alles weg war. Gelöschte Nachrichten blieben im Suchindex. Angepinnte Flüster-Tabs kamen eine ganze Sitzung lang leer hoch. Der Export schrieb ungültiges JSON. Ein Auskoppelfenster mit Titelleiste ließ sich nicht schließen. Ein 403 eines Emote-Dienstes riss alle 65 funktionierenden Emotes mit.
- **Was sich am Speichern ändert:** Das Kanalraster entscheidet jetzt allein. Bisher griff die Unbekannt-Absicherung auch bei bekannten Kanälen, ein abgewählter Kanal wurde also trotzdem geschrieben. Wen das betraf, der speichert ab jetzt weniger. Nichts in der Datenbank wird angefasst.
- **Jedes Fenster zeichnet das Plugin selbst** und alle sprechen eine Sprache: Struktur trägt die Typografie, alles Drückbare bekommt eine Fläche, jede Farbe wird gegen ihren Untergrund gemessen. Dazu benannte Typo-Rollen, Zeitstempel in eigener Spalte, kursive Systemmeldungen.
- **Wieder erreichbar:** Export, Tab-Editor, Datenbankpflege und Anpinnen hatten beim Umbau ihre Zugänge verloren. Der Screenshot-Modus erreichte eine von vier Flächen mit Tab-Namen, jetzt alle vier. Der DSGVO-Hinweis beim Profil "Volle Historie" war in 25 Sprachen übersetzt und seit Mai unsichtbar.
- **Gestrichen:** sechs Einstellungen mit Regler, gespeichertem Wert und ohne jeden Leser. Ein Regex-Filter pro Tab, den der spieleigene Wortfilter abdeckt. Ein Assistenten-Häkchen, das abgefragt, als angewendet gemeldet und nie gelesen wurde.
- **Neu:** Emote-Tab im Standard-Layout (Testerwunsch), Orts- und Serverzeit in der Statusleiste, Screenshot-Modus aus der Eingabezeile erreichbar, `/hellion wizard`, ein Stil-Labor unter `/hellion lab`, und ein Hinweis im Assistenten, dass Plugins in FFXIV eine Grauzone sind und nicht in öffentliche Kanäle gehören.
+6 -6
View File
@@ -23,7 +23,7 @@ internal sealed class AutoTellTabsService : IDisposable
private readonly ILogger<AutoTellTabsService> _logger;
// Tabs-list structure lock now lives on Plugin (neutral owner) so the
// MessageManager refilter can share it. See Plugin.TabsListLock / B3.
// MessageManager refilter can share it. See Plugin.TabsListLock.
private object TabsListLock => _plugin.TabsListLock;
// Bumped whenever something wipes unpinned temp tabs wholesale (logout).
@@ -58,8 +58,8 @@ internal sealed class AutoTellTabsService : IDisposable
// Derived from the tab list on read. Pin/Unpin/Promote/Logout simply
// mutate IsPinned or remove tabs — the count adapts automatically.
// Replaces the F2.1 Interlocked counter because the new pin-state
// transitions are cold-path and don't need lock-free reads.
// Replaces an Interlocked counter: the pin-state transitions are cold-path
// and don't need lock-free reads.
internal int ActiveTempTabCount =>
Plugin.Config.Tabs.Count(TabLifecycleHelpers.IsInUnpinnedPool);
@@ -179,7 +179,7 @@ internal sealed class AutoTellTabsService : IDisposable
}
// Three steps, because building the tab pulls history out of the store and
// that must not happen under TabsListLock (B3 rule; the query sorts the whole
// that must not happen under TabsListLock (the query sorts the whole
// receiver history). Step 1 and 3 are locked, step 2 is not.
int generation;
lock (TabsListLock)
@@ -303,7 +303,7 @@ internal sealed class AutoTellTabsService : IDisposable
internal void DropOldestTempTab()
{
// B3: lock the list-structure ops so the (currently caller-less) Unpin path
// Lock the list-structure ops so the (currently caller-less) Unpin path
// can't race the worker; re-entrant when HandleTell already holds the lock.
lock (TabsListLock)
{
@@ -577,7 +577,7 @@ internal sealed class AutoTellTabsService : IDisposable
// Count and flag under one lock so the cap can't be raced. SaveConfig stays
// OUTSIDE -- holding TabsListLock across a save would put an fsync on the
// click path, which is what B6 just removed elsewhere.
// click path, which is what a later cycle just removed elsewhere.
lock (TabsListLock)
{
if (PinnedTempTabCount >= MaxPinnedTempTabs)
+3 -3
View File
@@ -1,7 +1,7 @@
namespace HellionChat;
// Reduced CJK fallback coverage for the v1.5.3 NotoSansCjk fallback merge (B1).
// Before B1 the fallback merged over the full `Ranges` array (Default + endonyms),
// Reduced CJK fallback coverage for the v1.5.3 NotoSansCjk fallback merge.
// Before that the fallback merged over the full `Ranges` array (Default + endonyms),
// duplicating the Latin/Default work already done by the global/Japanese fonts.
// This is the trimmed remainder the fallback is actually the sole source for:
// - Hangul Syllables (AC00-D7A3): no other merged font ships Korean glyphs.
@@ -9,7 +9,7 @@ namespace HellionChat;
// font is Inter-Light (no CJK), so the fallback is the SOLE Han source. The JpRange
// overlap is harmless (MergeMode: the Japanese font wins for shared kanji).
// Deliberately excluded: ONLY the ASCII/Latin Default block (0x20-0xFF), which the
// global font already owns -- that doubled Latin merge is the B1 waste being removed.
// global font already owns -- that doubled Latin merge is the waste being removed.
// Kept as plain start/end pairs so it is unit-testable without the unsafe ImGui
// glyph-range builder (mirrors FontSizeResolver's split-for-test rationale).
internal static class CjkFallbackRange
+64 -23
View File
@@ -35,7 +35,7 @@ public class ConfigKeyBind
[Serializable]
public class Configuration : IPluginConfiguration
{
internal const int LatestVersion = 26;
internal const int LatestVersion = 27;
public int Version { get; set; } = LatestVersion;
@@ -45,16 +45,16 @@ public class Configuration : IPluginConfiguration
// Global window opacity, applied across all themes.
public float WindowOpacity = 0.85f;
// UI-12: background opacity of the main chat window while unfocused.
// Background opacity of the main chat window while unfocused.
// WindowOpacity above stays the focused value.
public float WindowOpacityInactive = 0.65f;
public float WindowOpacityInactive = 0.75f;
// Reserved for future UI toggles; pre-declared to avoid a migration later.
public bool ReduceMotion;
// v1.2.1: default flipped false → true. Compact single-line layout is
// more readable than the card-rows layout introduced in v1.2.0.
public bool UseCompactDensity = true;
public bool UseCompactDensity;
// Privacy by Default master switch. Set false to restore upstream behaviour.
public bool PrivacyFilterEnabled = true;
@@ -77,7 +77,7 @@ public class Configuration : IPluginConfiguration
.PrivacyDefaults
.DefaultPersistUnknownChannels;
// F3.2: dedup unknown-ChatType warnings so a chatty filter doesn't spam
// Dedup unknown-ChatType warnings so a chatty filter doesn't spam
// the log every frame. NonSerialized so the warning fires once per
// runtime, not once-ever-per-install.
[NonSerialized]
@@ -107,7 +107,7 @@ public class Configuration : IPluginConfiguration
var known = Enum.IsDefined(typeof(ChatType), type);
// F3.2: log first occurrence of a ChatType the running build doesn't
// Log the first occurrence of a ChatType the running build doesn't
// recognise — i.e. one a future FFXIV patch may have added.
if (!known && !listed && _warnedUnknownChannels.Add(type))
{
@@ -145,23 +145,31 @@ public class Configuration : IPluginConfiguration
// who don't care, and dodges the per-frame DrawList overhead on low-end
// hardware. Gradient (Color3 / GradientColourSet) is parsed but rendered
// as the primary Color until a later cycle ports the animation.
public bool ShowHonorificGlow;
public bool ShowHonorificGlow = true;
public bool EnableAutoTellTabs = true;
public int AutoTellTabsLimit = 15;
public bool AutoTellTabsCompactDisplay;
public int AutoTellTabsHistoryPreload = 20;
public bool AutoTellTabsCompactDisplay = true;
public int AutoTellTabsHistoryPreload = 100;
// Sidebar width in pixels. Default 44 mirrors the icon-only layout from
// v1.2.0; users can widen up to 160 to fit a section-header line like
// "Active Tells (3)" without truncation.
public int SidebarWidth = 44;
// Expanded sidebar width in pixels. 44 was carried over from the v1.2.0
// icon-only layout and stayed the default long after the sidebar started
// drawing labels beside those icons, so every tab name came out clipped --
// it only went unnoticed because everyone had widened it by hand. 160 fits
// the German tab names, which are the longest of the 25 languages, and the
// floor below is set where they stop being readable rather than where the
// icons stop fitting.
public int SidebarWidth = 160;
public bool AutoTellTabsShowGreetedToggle;
public bool SeenPopOutInputHint;
public bool PopOutInputEnabled = true;
public bool SeenPopOutHeaderHint;
public bool AutoTellTabsOpenAsPopout;
// UI-7: how sender names are rendered in the chat log.
// On by default: the wizard's closing step tells the user to try /tell and
// watch a conversation open on its own, so the behaviour it describes has to
// be the behaviour they get.
public bool AutoTellTabsOpenAsPopout = true;
// How sender names are rendered in the chat log.
public WorldSuffixMode WorldSuffixMode = WorldSuffixMode.OtherWorldOnly;
public NameFormMode NameFormMode = NameFormMode.Full;
@@ -194,7 +202,7 @@ public class Configuration : IPluginConfiguration
// resource sets disagree on what PrettierTimestamps even means -- the wizard
// called it "relative time", the settings tab "modern layout".
public bool PrettierTimestamps = true;
public bool MoreCompactPretty;
public bool MoreCompactPretty = true;
public bool HideSameTimestamps = true;
// No reader; see the reconnect backlog.
@@ -211,12 +219,12 @@ public class Configuration : IPluginConfiguration
public bool OnlyPreviewIf;
public int PreviewMinimum = 1;
public PreviewPosition PreviewPosition = PreviewPosition.Inside;
public CommandHelpSide CommandHelpSide = CommandHelpSide.None;
public CommandHelpSide CommandHelpSide = CommandHelpSide.Right;
public KeybindMode KeybindMode = KeybindMode.Strict;
public LanguageOverride LanguageOverride = LanguageOverride.None;
public bool CanMove = true;
public bool CanResize = true;
public bool ShowTitleBar = true;
public bool ShowTitleBar;
public bool ShowPopOutTitleBar = true;
public bool DatabaseBattleMessages;
public bool FilterIncludePreviousSessions;
@@ -232,7 +240,7 @@ public class Configuration : IPluginConfiguration
// Toast when a tell the user sent could not be delivered.
public bool NotifyFailedTell = true;
// UI-11: warn before sending a message that carries plugin-only glyphs.
// Warn before sending a message that carries plugin-only glyphs.
public bool NotifyPluginDisclosure = true;
public bool KeepInputFocus = true;
public bool Use24HourClock = true;
@@ -402,9 +410,9 @@ public class Tab
public bool IsTempTab;
// Pinned TempTabs survive plugin reload and logout — tester feedback from
// Jin (v1.4.7). Pinned tabs live in their own pool (MaxPinnedTempTabs)
// separate from the AutoTellTabsLimit bucket.
// Pinned TempTabs survive plugin reload and logout -- tester feedback in
// v1.4.7. Pinned tabs live in their own pool (MaxPinnedTempTabs) separate
// from the AutoTellTabsLimit bucket.
public bool IsPinned;
public bool AllSenderMessages;
public TellTarget TellTarget = TellTarget.Empty();
@@ -462,7 +470,7 @@ public class Tab
[NonSerialized]
internal string? _cachedTellIcon;
// PM-3 hover-lerp state. Default 0f means "not hovered". Sidebar
// hover-lerp state. Default 0f means "not hovered". Sidebar
// path animates per tab; card-mode-border path is tab-aggregate
// (any card-row hover ramps the alpha for all cards in this tab).
// Lerp speed lives in the render loop, not here, so the same field
@@ -944,6 +952,39 @@ public static class LanguageOverrideExt
// Mutable.ExtraGlyphRanges so users do not need to know which range
// to tick manually. Returns 0 for locales fully covered by the default
// ImGui glyph range (Latin-1) or by the separate Japanese font handle.
// The same mapping keyed by culture code, for when the language override is
// None and the UI follows Dalamud. Without this the ranges only ever get
// filled by an explicit language pick -- installs that never touched the
// setting rendered their own locale in whatever the default range covers,
// which for Korean, Chinese, Cyrillic and Greek is boxes. It went unnoticed
// while configs accumulated ranges over time; a fresh config has none.
public static ExtraGlyphRanges RequiredGlyphRangesForCulture(string? cultureCode)
{
var code = (cultureCode ?? string.Empty).ToLowerInvariant();
// Longest first: zh-hant has to win over the zh prefix.
if (code.StartsWith("zh-hant") || code.StartsWith("zh-tw") || code.StartsWith("zh-hk"))
return ExtraGlyphRanges.ChineseFull;
if (code.StartsWith("zh"))
return ExtraGlyphRanges.ChineseSimplifiedCommon;
if (code.StartsWith("ko"))
return ExtraGlyphRanges.Korean;
if (code.StartsWith("uk") || code.StartsWith("ru") || code.StartsWith("be"))
return ExtraGlyphRanges.Cyrillic;
if (code.StartsWith("el"))
return ExtraGlyphRanges.Greek;
if (
code.StartsWith("cs")
|| code.StartsWith("pl")
|| code.StartsWith("ro")
|| code.StartsWith("hu")
|| code.StartsWith("tr")
)
return ExtraGlyphRanges.LatinExtended;
return 0;
}
public static ExtraGlyphRanges RequiredGlyphRanges(this LanguageOverride mode) =>
mode switch
{
+6 -6
View File
@@ -94,13 +94,13 @@ public sealed class FontManager : IDisposable
private ushort[] Ranges = [];
private ushort[] JpRange = [];
// B1: trimmed remainder the NotoSansCjk fallback is the sole source for
// Trimmed remainder the NotoSansCjk fallback is the sole source for
// (Hangul + full Han); excludes the Default/Latin block already merged
// by the global font, so the fallback no longer re-merges the full Ranges array.
private ushort[] CjkFallbackGlyphRange = [];
// Report accessor for the ctor self-test: built glyph-range array lengths so
// the step can show the B1 dedup effect (a small trimmed fallback vs the large
// the step can show the dedup effect (a small trimmed fallback vs the large
// primary range) in its on-disk report instead of a bare Pass.
internal (int Ranges, int JpRange, int CjkFallback) GlyphRangeLengths =>
(Ranges.Length, JpRange.Length, CjkFallbackGlyphRange.Length);
@@ -247,7 +247,7 @@ public sealed class FontManager : IDisposable
// Instance method so Ranges / JpRange are reachable without parameter
// plumbing; PascalCase field names follow the existing class style.
// B1: shared CJK + symbols tail for both the regular and italic delegate
// Shared CJK + symbols tail for both the regular and italic delegate
// fonts. Earlier-merged fonts win for shared codepoints (imgui MergeMode),
// so this runs AFTER the primary font is set as config.MergeFont. The CJK
// fallback is the sole Hangul/Simplified-Han source when UseHellionFont=true
@@ -265,7 +265,7 @@ public sealed class FontManager : IDisposable
config.GlyphRanges = JpRange;
AddFontWithFallback(tk, Plugin.Config.JapaneseFontV2.FontId, config, "japanese");
// NotoSansCjk fallback, trimmed to CjkFallbackGlyphRange (B1). Merged last so earlier fonts win.
// NotoSansCjk fallback, trimmed to CjkFallbackGlyphRange. Merged last so earlier fonts win.
config.SizePt = basePt;
config.GlyphRanges = CjkFallbackGlyphRange;
AddFontWithFallback(
@@ -427,7 +427,7 @@ public sealed class FontManager : IDisposable
// Common extras (Axis ingame glyphs, endonyms, enclosed alphanumerics)
// belong to the primary/Japanese ranges only. The trimmed CJK fallback
// (B1) skips them so it stays a pure Hangul/Simplified-Han remainder and
// skips them so it stays a pure Hangul/Simplified-Han remainder and
// does not re-merge the Default-block work the global font already did.
if (includeCommonExtras)
{
@@ -498,7 +498,7 @@ public sealed class FontManager : IDisposable
);
JpRange = BuildRange(GlyphRangesJapanese.GlyphRanges, includeCommonExtras: true);
// B1: the fallback gets only the trimmed Hangul/Simplified-Han remainder.
// The fallback gets only the trimmed Hangul/Simplified-Han remainder.
// No Default block, no endonyms — those are already merged by the global and
// Japanese fonts, so re-merging them on the fallback was wasted atlas work.
CjkFallbackGlyphRange = BuildRange(CjkFallbackRange.Pairs, includeCommonExtras: false);
+5 -5
View File
@@ -234,7 +234,7 @@ internal sealed unsafe class Chat : IDisposable
// Seed the just-typed character into our input field and focus it, the
// same InputBar.AppendPending + Activate prefill path inventory item-links
// use. Prefill-only — no tab switch (Flo decision 2026-06-15).
// use. Prefill only, deliberately: no tab switch.
if (input != null)
{
Plugin.InputBar.AppendPending(input);
@@ -355,9 +355,9 @@ internal sealed unsafe class Chat : IDisposable
if (playerName != null)
{
// Right-click -> Send Tell: prefill our input the same way our own
// "Send Tell" payload menu does (PayloadHandler), then focus. Prefill-
// only — no tab switch, no ChatActivatedArgs revival (Flo decision
// 2026-06-15). The game supplies worldName here, so no sheet lookup.
// "Send Tell" payload menu does (PayloadHandler), then focus. Prefill
// only, deliberately: no tab switch, no ChatActivatedArgs revival.
// The game supplies worldName here, so no sheet lookup.
PrefillTellInput(
playerName->ToString(),
worldName != null ? worldName->ToString() : null
@@ -393,7 +393,7 @@ internal sealed unsafe class Chat : IDisposable
{
// In-foray right-click -> Send Tell: same prefill path as the non-foray
// tell. The foray-specific TellSpecial channel routing stays deferred
// (v1.8.1, SetEurekaTellChannel) — prefill-only here (Flo decision 2026-06-15).
// (v1.8.1, SetEurekaTellChannel) -- prefill only here as well.
PrefillTellInput(
playerName->ToString(),
worldName != null ? worldName->ToString() : null
+8 -8
View File
@@ -507,14 +507,14 @@ internal unsafe class KeybindManager : IDisposable
// Resolve the surface this keybind acts on FIRST: a focused pop-out otherwise
// the main window. Channel-set/REPLY/prefill all write here so the action
// follows the input the user is typing in (C3 full tail rebuild, OD-1).
// follows the input the user is typing in.
var (targetWindow, targetTab) = ResolveKeybindTarget();
// Surface + focus the resolved target ONCE, before routing. Main: ActivateChat
// re-surfaces it from a hide/closed state (the chat-activation entry point
// retired in v1.6.0). Pop-out: arm only its focus — NOT ActivateChat, which
// would yank the main window to front and un-hide it on every pop-out-targeted
// keybind (OD-1: stay where the user types). Exactly one window arms focus per
// keybind: stay where the user types. Exactly one window arms focus per
// keybind, so the next frame has no SetKeyboardFocusHere race.
if (targetWindow is ChannelPopoutWindow)
targetWindow.RequestInputFocus();
@@ -530,7 +530,7 @@ internal unsafe class KeybindManager : IDisposable
{
// Direct channel-switch binds (CMD_SAY/PARTY/numbered linkshells/…): switch
// the game channel AND mirror it onto the resolved tab so the input pill
// shows the real send target (pill-sync, Flo decision 2026-06-15).
// shows the real send target (pill-sync).
Plugin.Instance.Functions.Chat.SetChannel(channel);
// Only mirror onto the tab when the game actually accepted the switch — an
// empty linkshell slot leaves the game channel untouched, so the pill must
@@ -547,7 +547,7 @@ internal unsafe class KeybindManager : IDisposable
// Rotation binds (REPLY / linkshell-cycle). Ported from v1.5.6's
// ChatLogWindow.Activated (1d3b429:240-334) without the ChatActivatedArgs
// indirection (gone in the rewrite). Writes onto the resolved surface's
// tab (C2/C3 shared target), not Plugin.CurrentTab.
// tab, not Plugin.CurrentTab.
if (targetTab is { } rotTab)
{
var targetChannel = (InputChannel?)rotateChannel;
@@ -629,7 +629,7 @@ internal unsafe class KeybindManager : IDisposable
if (info.Permanent)
{
// KB-01 (1.5.6 parity, ChatLogWindow.SetChannel 1d3b429:1476-1479):
// 1.5.6 parity (ChatLogWindow.SetChannel, 1d3b429:1476-1479):
// committing the game channel also pre-targets the game's native input.
// Forward the tab's reply target for Tell so the partner is armed
// game-side (ChangeChatChannel code 17); null for a linkshell —
@@ -654,7 +654,7 @@ internal unsafe class KeybindManager : IDisposable
// Prefill text binds (CMD_COMMAND seeds "/"): the token always goes to the
// main InputBar (the focus contract does not expose pop-out buffers); a
// focused pop-out already received focus above, so only token routing matters
// here (documented scope limit, OD-1).
// here -- a documented scope limit.
if (info.Text is { } text)
Plugin.Instance.InputBar.SetPendingMessage(text);
}
@@ -665,7 +665,7 @@ internal unsafe class KeybindManager : IDisposable
}
// Resolve which chat surface a keybind action targets: the open pop-out whose
// input currently has focus, otherwise the main window. C2/C3 share this so a
// input currently has focus, otherwise the main window. Both paths share it so a
// channel-switch/REPLY/prefill follows the surface the user is typing in. The
// returned tab is that surface's bound tab (pop-out: Bound; main: ActiveTab).
// Null tab => skip the tab-write (early-load window where no tab exists yet).
@@ -682,7 +682,7 @@ internal unsafe class KeybindManager : IDisposable
}
// Tab-delta keybinds (ChatTabForward/Backward) stay main-window-only by design:
// a channel-bound pop-out has no tab list to cycle (OD-1). The focus contract is
// a channel-bound pop-out has no tab list to cycle. The focus contract is
// consumed by the channel-set/REPLY/prefill tail, not here.
private void DispatchTabDelta(int delta)
{
+1 -1
View File
@@ -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.14.0</Version>
<Version>2.0.0</Version>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<!-- Use lock file to pin exact versions -->
+34 -62
View File
@@ -35,6 +35,39 @@ tags:
- Replacement
- Privacy
changelog: |-
**v2.0.0 — Rebuilt, Repaired, Reset (2026-08-19)**
Nine development cycles in one release. Everything built as v1.6.0 through v1.15.0 ships here; those versions were never published on their own.
**This update resets your settings.** Nine cycles of rebuilding left saved values pointing at surfaces that no longer exist, and starting over is the only way to be sure every install is on the same defaults. **Your message history is untouched** — it lives in a separate database. Your old settings are kept next to the config file as `HellionChat.json.pre-2.0.0.bak`.
Fixed, and several of these lost data or hid it:
- Retroactive cleanup could never be applied at all. The preview took the database lock itself and that counted as a change, so every preview went stale the instant it finished and the apply button never appeared.
- Compacting the database ran against an open reader and failed, after which the plugin reported that nothing had been deleted — while everything had.
- Deleting messages left them in the search index, so search kept returning rows that were already gone.
- Pinned tell tabs came up empty for a whole session: the history query ran at plugin start, before any character is logged in, and never tried again.
- Export wrote invalid JSON where a setting value was involved, and a byte-order mark that strict parsers reject.
- A pop-out with its title bar switched on could not be closed, and a tell from a popped-out partner hijacked the main window's active tab.
- One third-party emote service started returning 403, and that response took all 65 working global emotes down with it on every start.
- The GDPR notice for the full-history profile had been translated into 25 languages and shown nowhere since May.
Changed, and one of these changes what gets stored:
- **The channel grid is now authoritative.** Until now the unknown-channel failsafe was applied to known channels too, so a channel you had unticked was still being written while that failsafe was on. If that was you, this release stores less than before. Nothing already in the database is touched.
- Every window is drawn by the plugin rather than by ImGui defaults, and they share one visual language: structure carried by typography, a surface on anything you can press, and colours measured against what sits behind them.
- Typography has named roles — sender, body and timestamp mean the same thing everywhere, timestamps sit in their own column, system messages are italic.
- Export, the tab editor, database maintenance and pinning had lost their entry points during the rebuild and are reachable again.
- Screenshot mode reached one of four surfaces that draw a tab name. It reaches all four now.
Removed: six settings that had a control and a saved value but no reader anywhere in the plugin; a per-tab regex filter the game's own blackword filter covers; and a wizard checkbox that was collected, reported as applied and never read.
New: an Emote tab in the default layout, local and server clocks in the status bar, a screenshot mode reachable from the input row, `/hellion wizard` to reopen the setup wizard, a style lab under `/hellion lab`, and 25 UI languages with the settings window and wizard fully covered.
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.
@@ -116,65 +149,4 @@ changelog: |-
Based on Chat 2 1.35.3 (upstream Infiziert90/ChatTwo, EUPL-1.2).
---
**v1.5.3 — Localisation Wave + Bundled-Font Overhaul (2026-05-19)**
Multi-language pass plus a long-standing first-frame HITCH lands
as a side effect of a font-stack rewrite.
User-visible:
- 24 selectable UI languages (was 2). Catalan, Czech, Danish,
Dutch, English, Finnish, French, German, Greek, Hungarian,
Italian, Japanese, Korean, Norsk bokmål, Polish, Portuguese
(BR + PT), Romanian, Russian, Spanish, Swedish, Turkish,
Ukrainian, Simplified + Traditional Chinese. Sorted by endonym,
"None" pinned first. Non-native locales are AI-assisted and
flagged for native-speaker review via the Forge Discord.
- Bundled Inter Light replaces Exo 2 (SIL OFL 1.1, 343 KB). The
Inter font ships Latin Extended-A/B, Greek polytonic and
Cyrillic Supplement coverage; NotoSansCjkRegular joins as a
third merge layer for Hangul and Simplified-Han glyphs the
FFXIV Japanese game font does not ship.
- First-frame HITCH dropped from ~74 ms (v1.5.2 baseline that
held since v1.4.x) to a median of ~20 ms (5-reload sample
17.9-23.6 ms, Linux/Wine). The bundled-font path silently
fell back to the FFXIV Axis font for the entire v1.5.x series
because of an early-return in the draw loop. The fix that
routes RegularFont through draw also lands the defer-pattern
win the v1.5.1 cycle was reaching for.
- ExtraGlyphRanges auto-activates on language change. Korean,
ChineseFull and the two new flags (LatinExtended, Greek) toggle
on without a manual visit to Fonts and Colours.
- New WarningText under the language dropdown notes FFXIV's
chat input only fully supports EN/DE/FR/JA character sets.
Other languages render in HellionChat but may garble when
typed into in-game chat.
Under the hood:
- Three-layer font stack: Inter Light primary, FFXIV
JapaneseFont merge 1 for kana/kanji style, NotoSansCjkRegular
merge 2 for everything else CJK.
- LanguageOverride enum gains ten locales plus three previously
commented out (Italian, Korean, Norwegian as `nb`). New
values append to the enum so existing config integers stay
stable across update.
- Crowdin gap closed: four post-sync ChatTwo keys backfilled
into 13 legacy locales with per-key AI markers.
- Plugin.LoadAsync runs a one-shot migration that ORs in the
matching ExtraGlyphRanges flag for users already on a
non-default language. Settings.Apply auto-activates on
change going forward.
- Em-dash sweep across the EN source and 18 translations to the
house style. Russian and Ukrainian keep the typographic norm.
Migration v17 stays. UseHellionFont users transition from Exo 2
to Inter Light transparently on first reload.
Based on Chat 2 1.35.3 (upstream Infiziert90/ChatTwo, EUPL-1.2).
---
Full history: https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/releases
Earlier history: https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/releases
@@ -135,7 +135,7 @@ internal sealed class PayloadHandlerInitHostedService(
{
public async Task StartAsync(CancellationToken cancellationToken)
{
// §6.2 cycle-resolution: both singletons exist by the time HostedServices
// Cycle resolution: both singletons exist by the time HostedServices
// run, so this is the first safe point to wire the setter.
messageList.AttachPayloadHandler(payloadHandler);
@@ -172,7 +172,7 @@ internal sealed class PayloadHandlerInitHostedService(
// InputBar -> CommandHelpWindow -> MainWindow -> InputBar (MS.DI does not catch
// it through FactoryCallSite registrations and the resolve recurses silently).
// Both singletons exist by host.StartAsync time, so this is the first safe point
// to wire the setter — same §6.2 pattern as MessageList.AttachPayloadHandler.
// to wire the setter — same setter-injection pattern as MessageList.AttachPayloadHandler.
internal sealed class CommandHelpWindowInitHostedService(
CommandHelpWindow commandHelpWindow,
MainWindow mainWindow
@@ -190,7 +190,7 @@ internal sealed class CommandHelpWindowInitHostedService(
// Attaches the singleton PayloadHandler to every pre-allocated pop-out
// window's MessageList post-container-build. Pool/window cannot take the
// PayloadHandler via ctor (that would close the silent FactoryCallSite cycle —
// same §6.2 reason as MessageList.AttachPayloadHandler / CommandHelpWindow.
// same setter-injection reason as MessageList.AttachPayloadHandler / CommandHelpWindow.
// AttachMainWindow). Both singletons exist by host.StartAsync time.
internal sealed class ChannelPopoutInitHostedService(
ChannelPopoutPool pool,
+1 -1
View File
@@ -31,7 +31,7 @@ public sealed class ExtraChat : IDisposable
// volatile: IPC callbacks fire on a Dalamud thread while ImGui reads these.
// Reference assignment is atomic on x64, but the barrier ensures visibility
// across threads (especially Mono/Wine). See AUDIT-2026-05-05 [SEC-01].
// across threads (especially Mono/Wine). Raised in the 2026-05-05 audit.
private volatile Dictionary<string, uint> ChannelCommandColoursInternal = new();
internal IReadOnlyDictionary<string, uint> ChannelCommandColours =>
ChannelCommandColoursInternal;
+3 -3
View File
@@ -20,7 +20,7 @@ internal sealed class TypingIpc : IDisposable
private ICallGateProvider<ChatInputState> StateQueryGate { get; }
private ICallGateProvider<ChatInputState, object?> StateChangedGate { get; }
// v1.4.9 R4: ChatTwo IPC compatibility mirror. Some third-party plugins
// v1.4.9: ChatTwo IPC compatibility mirror. Some third-party plugins
// have a no-fork policy and subscribe only to ChatTwo.*-prefixed IPC
// gates. HellionChat replaces ChatTwo (conflict detection prevents
// parallel loading), so mirroring the ChatTwo provider slots lets those
@@ -50,7 +50,7 @@ internal sealed class TypingIpc : IDisposable
"HellionChat.ChatInputStateChanged"
);
// v1.4.9 R4: ChatTwo-prefixed compatibility slots (see class-level comment).
// v1.4.9: ChatTwo-prefixed compatibility slots (see class-level comment).
ChatTwoStateQueryGate = Plugin.Interface.GetIpcProvider<ChatInputState>(
"ChatTwo.GetChatInputState"
);
@@ -102,7 +102,7 @@ internal sealed class TypingIpc : IDisposable
HasState = true;
LastState = state;
StateChangedGate.SendMessage(state);
// v1.4.9 R4: mirror on ChatTwo-prefixed slot for no-fork-policy plugins.
// v1.4.9: mirror on ChatTwo-prefixed slot for no-fork-policy plugins.
ChatTwoStateChangedGate.SendMessage(state);
}
+3 -3
View File
@@ -22,7 +22,7 @@ internal sealed class IpcManager : IDisposable
object?
> InvokeGate { get; }
// v1.4.9 R4: ChatTwo IPC compatibility mirror. Third-party plugins with
// v1.4.9: ChatTwo IPC compatibility mirror. Third-party plugins with
// a no-fork policy (e.g. Artisan, AllaganTools) only subscribe to the
// ChatTwo.*-prefixed context-menu integration gates. Mirroring all four
// provider slots under the ChatTwo namespace lets those plugins keep
@@ -65,7 +65,7 @@ internal sealed class IpcManager : IDisposable
object?
>("HellionChat.Invoke");
// v1.4.9 R4: ChatTwo-prefixed mirrors of the four context-menu slots
// v1.4.9: ChatTwo-prefixed mirrors of the four context-menu slots
// above. Share the same Register/Unregister backing methods so a
// plugin that subscribes via either namespace lands in the same
// Registered list. SendMessage on Invoke fans out to both gates.
@@ -103,7 +103,7 @@ internal sealed class IpcManager : IDisposable
)
{
InvokeGate.SendMessage(id, sender, contentId, payload, senderString, content);
// v1.4.9 R4: fan out the same event to plugins listening on ChatTwo.Invoke.
// v1.4.9: fan out the same event to plugins listening on ChatTwo.Invoke.
ChatTwoInvokeGate.SendMessage(id, sender, contentId, payload, senderString, content);
}
+9 -10
View File
@@ -163,7 +163,7 @@ internal class MessageManager : IAsyncDisposable
internal void ClearAllTabs()
{
// B3: snapshot the tab LIST under the shared lock so the worker-thread
// Snapshot the tab LIST under the shared lock so the worker-thread
// add/remove can't tear the enumeration; tab.Clear() then runs lock-free
// (each tab's Messages has its own SemaphoreSlim — lock order: list outer).
List<Tab> tabsSnapshot;
@@ -184,8 +184,8 @@ internal class MessageManager : IAsyncDisposable
using var messages = Store.GetMostRecentMessages(CurrentContentId, since);
// TempTabs excluded (live state from AutoTellTabsService). Bucket via the
// pure MapMessagesToTabs so the assignment stays testable outside Dalamud (B3-1).
// B3: snapshot under the shared lock (list copy only — short critical
// pure MapMessagesToTabs so the assignment stays testable outside Dalamud.
// Snapshot under the shared lock (list copy only — short critical
// section). The Store query above and the AddSortPrune writes below stay
// OUTSIDE the lock (lock order: list outer, MessageList inner).
List<Tab> nonTempTabs;
@@ -248,9 +248,8 @@ internal class MessageManager : IAsyncDisposable
_logger.LogError(ex, "Error in FilterAllTabs");
}
// v1.4.9 R3 profiling: Information so the xllog tail surfaces this
// without a Debug filter. Belt-and-suspenders for future plugin-load
// regressions; remains in place after Sub-Task 3.4 Befund.
// Information, not Debug, so the xllog tail surfaces this without a
// filter. Kept as a guard against future plugin-load regressions.
_logger.LogInformation($"FilterAllTabs took {stopwatch.ElapsedMilliseconds}ms");
});
}
@@ -436,10 +435,10 @@ internal class MessageManager : IAsyncDisposable
// TEST-MIRROR: ../_Helpers/TabSoundDecision.cs
// Unseen ("count only what you haven't seen") suppresses unread on an inactive
// tab when the active tab ALSO shows this message — you already saw it in the
// tab you're looking at (1.5.6 / upstream ChatTwo behavior). Pre-F2 the "active
// tab" was wrongly pinned to Tabs[0], so this fired against the wrong tab; F2
// recoupled CurrentTab to the REAL active tab, so currentTabMatches is now
// measured against the tab you actually see. All -> always counts; None ->
// tab you're looking at (1.5.6 / upstream ChatTwo behavior). The "active tab"
// used to be pinned to Tabs[0], so this fired against the wrong one until
// CurrentTab was recoupled to the real active tab, and currentTabMatches is
// now measured against the tab you see. All -> always counts; None ->
// counts here and is gated out at the display layer. Pure + SelfTest-able.
internal static bool ShouldCountUnread(Tab tab, Tab currentTab, bool currentTabMatches) =>
!(
+3 -4
View File
@@ -245,7 +245,7 @@ internal class MessageStore : IDisposable
private SqliteConnection Connect()
{
// v1.4.9 R3 profiling: trace cost of SQLite open + pragma-apply. Paired
// v1.4.9 profiling: trace cost of SQLite open + pragma-apply. Paired
// with the Migrate-Stopwatch below — Connect alone is the cheap half
// (Open + a handful of PRAGMAs); the expensive half typically lives in
// Migrate, especially on a large DB after a schema bump.
@@ -260,7 +260,7 @@ internal class MessageStore : IDisposable
private void Migrate()
{
// v1.4.9 R3 profiling: trace cost of the schema-migration chain. On a
// v1.4.9 profiling: trace cost of the schema-migration chain. On a
// large DB after a fresh schema bump this is the dominant SQLite cost
// at plugin-load, not Connect.
var migrateSw = System.Diagnostics.Stopwatch.StartNew();
@@ -939,8 +939,7 @@ internal class MessageStore : IDisposable
// storage form on both sides so the IN(...) compare matches. SQLite has a
// hard parameter limit of 999 in default builds, so we chunk the input --
// a 1000-hit FTS query never explodes the SELECT. Result ordering is not
// guaranteed; callers re-sort (e.g. DbViewer sorts by Date descending in
// Sub-Task 4.4).
// guaranteed; callers re-sort (DbViewer sorts by Date descending).
public IReadOnlyList<Message> LoadByGuids(IReadOnlyList<string> guidStrings)
{
if (guidStrings.Count == 0)
+1 -1
View File
@@ -2,7 +2,7 @@ using HellionChat.Resources;
namespace HellionChat;
// UI-7: how a sender's name is rendered in the chat log. Kept in its own file
// How a sender's name is rendered in the chat log. Kept in its own file
// (no Dalamud usings) so the SenderNameFormatter pure-helper test stays
// AppDomain-isolated (feedback_dalamud_test_isolation).
+4 -4
View File
@@ -263,8 +263,8 @@ internal sealed class PayloadHandler
// Eureka, Bozja and Occult need special handling as tells work different
if (!Sheets.IsInForay())
{
// §6.9: single SetPendingMessage call; v1.5.6 used incremental Chat += writes.
// XC-8: shares the /tell builder with the native detours. IsPublic (not
// Single SetPendingMessage call; v1.5.6 used incremental Chat += writes.
// Shares the /tell builder with the native detours. IsPublic (not
// IsNullOrEmpty) is resolved HERE — a private/null world must NOT leak @World.
_inputBar.SetPendingMessage(
GameFunctions.Chat.BuildTellCommand(
@@ -393,7 +393,7 @@ internal sealed class PayloadHandler
var inputChannel = chunk.Message?.Code.Type.ToInputChannel();
if (inputChannel != null && ImGui.Selectable(Language.Context_ReplyInSelectedChatMode))
{
// §6.3: route channel-switch through MainWindow's active tab
// Route the channel switch through MainWindow's active tab
_mainWindow.ActiveTab?.CurrentChannel?.SetChannel(inputChannel.Value);
_inputBar.Activate = true;
}
@@ -731,7 +731,7 @@ internal sealed class PayloadHandler
using (ImRaii.Tooltip())
using (ImRaii.TextWrapPos(0.0f))
using (
// §4.2: use active theme text colour instead of the former LogWindow.DefaultText static.
// Use the active theme text colour instead of the former LogWindow.DefaultText static.
ImRaii.PushColor(
ImGuiCol.Text,
ColourUtil.RgbaToVector4(_themes.Active.Colors.TextPrimary)
+157 -75
View File
@@ -134,7 +134,7 @@ public sealed class Plugin : IAsyncDalamudPlugin
internal Integrations.HonorificService HonorificService { get; private set; } = null!;
internal Integrations.CustomAudioPlayer CustomAudioPlayer { get; private set; } = null!;
// Ctor-smoke anchors (B0-2). Exposed so the Payload/Chunk ctor-smoke steps
// Ctor-smoke anchors. Exposed so the Payload/Chunk ctor-smoke steps
// can drive the real per-frame Lender path (Borrow()) and the eager
// singletons through the container, never via new(). Mirror of the
// FontManager property pattern — every SelfTest reaches services this way.
@@ -178,7 +178,7 @@ public sealed class Plugin : IAsyncDalamudPlugin
internal bool ChatActivationRequested;
// Set in the first DisposeAsync statement so async callbacks scheduled
// via Framework.RunOnTick (v1.4.8 B3 retention sweep) can early-bail
// via Framework.RunOnTick (v1.4.8 retention sweep) can early-bail
// before they touch state that has already been torn down. Volatile
// because the tick reads it from a different thread than the writer.
private volatile bool _isDisposing;
@@ -188,9 +188,9 @@ public sealed class Plugin : IAsyncDalamudPlugin
// just unloaded belongs to nobody.
internal bool IsDisposing => _isDisposing;
// v1.9.0 B5: last full Draw() wall-time in ms, written once per frame at
// v1.9.0: last full Draw() wall-time in ms, written once per frame at
// the end of the UiBuilder.Draw handler. Covers the GlobalStyleScope push
// and the font push (§7.5 First-Frame-HITCH must include atlas/style
// and the font push (the first-frame hitch measurement must include atlas/style
// prologue cost), not just WindowSystem.Draw — measuring the inner call
// alone would drop the prologue and make the figure non-comparable to the
// v1.5.6 baseline. Only accumulated here; the disk write happens in
@@ -214,7 +214,7 @@ public sealed class Plugin : IAsyncDalamudPlugin
// must never block doing so.
internal readonly Util.DbOperationGate DbOperations = new();
// B3: neutral owner of the Config.Tabs LIST-structure lock so both the
// Neutral owner of the Config.Tabs LIST-structure lock so both the
// worker-thread mutator (AutoTellTabsService) and the framework-thread
// refilter (MessageManager) share ONE monitor. Lock order: this outer,
// MessageList's SemaphoreSlim inner — never the reverse.
@@ -287,83 +287,118 @@ public sealed class Plugin : IAsyncDalamudPlugin
+ "Please install v1.4.2 first to migrate the configuration, then upgrade to v1.4.10."
);
}
// 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)
// 2.0.0 does not migrate, it starts over. Five cycles rebuilt the whole
// window layer, and a config carried through them keeps values chosen
// against surfaces that no longer exist -- an opacity picked for a
// window that has been redrawn twice since, tabs laid out for a sidebar
// that works differently now. Every user of this build is a tester who
// was told this happens, and it is the only way to be sure everyone
// sees the same defaults.
//
// The file is copied aside first. Rolling back to 1.5.6 is a supported
// move here and it stays cheap: the old settings are a file copy away,
// rather than an evening of clicking them back in.
if (Config.Version < 27)
{
Config.MainWindowLayoutMode = MainWindowLayoutMode.TopTabs;
}
BackUpConfigBeforeReset();
Config = Configuration.CreateFresh();
Config.Version = 27;
// Saved immediately: a crash between here and the first user-driven
// save would otherwise run the whole reset again on the next start,
// and the second run would back up the already-reset file over the
// real backup.
SaveConfig();
// v24 migration: the privacy filter used to route a known but unticked
// channel through the unknown-type failsafe, so the channel grid was
// inert whenever that failsafe was on. Corrected in v1.12.0. A config
// that never picked a channel was storing everything through that hole,
// and the corrected rule would store nothing at all -- so the intent is
// carried forward as a filter that is honestly switched off.
if (
Config.Version < 24
&& Privacy.StorageRule.ShouldDisableFilterOnV24(
Config.PrivacyFilterEnabled,
Config.PrivacyPersistUnknownChannels,
Config.PrivacyPersistChannels.Count
)
)
{
Config.PrivacyFilterEnabled = false;
// Log, not LogProxy: this runs in Phase-0 and the proxy is only
// resolved from the container further down.
Log.Information(
"Privacy filter switched off during the v24 migration: it was on with no channels "
+ "picked, which stored everything through the unknown-channel failsafe. Pick "
+ "channels in Settings to switch it back on."
"Config reset to defaults for 2.0.0. Previous settings kept as "
+ "HellionChat.json.pre-2.0.0.bak next to the config file."
);
}
// v25 carried no migration step; the bump was documentation.
//
// v26 does. NameCameFromPartner is what screenshot mode reads to decide
// whether a tab name is a person, and a config written before it existed
// has it false on every tab -- including pinned tell tabs, which survive
// reloads and are named "Player@World". Anything still carrying a tell
// binding or the temp flag got its name from a partner, so the flag is
// set from those two.
//
// Tabs promoted before this version are past saving: promotion clears
// both markers and keeps the name, so nothing in the stored data says
// where that name came from. Renaming one clears the flag anyway, which
// is the same outcome the user gets by editing it.
if (Config.Version < 26)
else
{
var carried = 0;
foreach (var tab in Config.Tabs)
// 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)
{
if (tab.NameCameFromPartner || (!tab.IsTempTab && tab.TellTarget?.IsSet() != true))
continue;
tab.NameCameFromPartner = true;
carried++;
Config.MainWindowLayoutMode = MainWindowLayoutMode.TopTabs;
}
if (carried > 0)
// v24 migration: the privacy filter used to route a known but unticked
// channel through the unknown-type failsafe, so the channel grid was
// inert whenever that failsafe was on. Corrected in v1.12.0. A config
// that never picked a channel was storing everything through that hole,
// and the corrected rule would store nothing at all -- so the intent is
// carried forward as a filter that is honestly switched off.
if (
Config.Version < 24
&& Privacy.StorageRule.ShouldDisableFilterOnV24(
Config.PrivacyFilterEnabled,
Config.PrivacyPersistUnknownChannels,
Config.PrivacyPersistChannels.Count
)
)
{
Config.PrivacyFilterEnabled = false;
// Log, not LogProxy: this runs in Phase-0 and the proxy is only
// resolved from the container further down.
Log.Information(
$"Marked {carried} tab(s) as partner-named during the v26 migration, so "
+ "screenshot mode hides them in the channel header."
"Privacy filter switched off during the v24 migration: it was on with no channels "
+ "picked, which stored everything through the unknown-channel failsafe. Pick "
+ "channels in Settings to switch it back on."
);
}
// v25 carried no migration step; the bump was documentation.
//
// v26 does. NameCameFromPartner is what screenshot mode reads to decide
// whether a tab name is a person, and a config written before it existed
// has it false on every tab -- including pinned tell tabs, which survive
// reloads and are named "Player@World". Anything still carrying a tell
// binding or the temp flag got its name from a partner, so the flag is
// set from those two.
//
// Tabs promoted before this version are past saving: promotion clears
// both markers and keeps the name, so nothing in the stored data says
// where that name came from. Renaming one clears the flag anyway, which
// is the same outcome the user gets by editing it.
if (Config.Version < 26)
{
var carried = 0;
foreach (var tab in Config.Tabs)
{
if (
tab.NameCameFromPartner
|| (!tab.IsTempTab && tab.TellTarget?.IsSet() != true)
)
continue;
tab.NameCameFromPartner = true;
carried++;
}
if (carried > 0)
{
Log.Information(
$"Marked {carried} tab(s) as partner-named during the v26 migration, so "
+ "screenshot mode hides them in the channel header."
);
}
}
}
Config.Version = 26;
Config.Version = 27;
// Unpinned TempTabs are session-only and dropped on every load. Pinned
// TempTabs survive reload — Jin's tester feedback (v1.4.7).
// TempTabs survive reload -- tester feedback in v1.4.7.
Config.Tabs.RemoveAll(TabLifecycleHelpers.ShouldStripOnLoad);
// GP-04: clear stale Tab.PopOut flags now — the pool binds further down
// Clear stale Tab.PopOut flags now — the pool binds further down
// (ChannelPopoutPool resolve below), so at this point no tab can own a
// slot. A persisted PopOut=true (notably on surviving pinned TempTabs)
// would otherwise be a flag with no window. Runs after the strip, before
@@ -378,7 +413,10 @@ public sealed class Plugin : IAsyncDalamudPlugin
// that path. ORing in the required flag here lets the first atlas
// build pick it up, so an upgrade from v1.5.2 renders correctly
// without forcing the user to toggle the language twice.
var requiredRanges = Config.LanguageOverride.RequiredGlyphRanges();
var requiredRanges =
Config.LanguageOverride is LanguageOverride.None
? LanguageOverrideExt.RequiredGlyphRangesForCulture(Interface.UiLanguage)
: Config.LanguageOverride.RequiredGlyphRanges();
if (requiredRanges != 0 && !Config.ExtraGlyphRanges.HasFlag(requiredRanges))
Config.ExtraGlyphRanges |= requiredRanges;
@@ -462,7 +500,7 @@ public sealed class Plugin : IAsyncDalamudPlugin
FirstRunWizard = _host.Services.GetRequiredService<FirstRunWizard>();
ChannelPopoutPool = _host.Services.GetRequiredService<Ui.Windows.ChannelPopoutPool>();
// Ctor-smoke anchors (B0-2). Resolved last, against the fully built
// Ctor-smoke anchors. Resolved last, against the fully built
// container: every MakePayloadHandler dep (MainWindow, InputBar,
// ChunkRenderer, ...) is resolvable here, and the ChunkRenderer resolve
// below just reuses the same cached singleton. These are plain
@@ -485,6 +523,7 @@ public sealed class Plugin : IAsyncDalamudPlugin
{
Config.Tabs.Add(TabsUtil.VanillaGeneral);
Config.Tabs.Add(TabsUtil.HellionSystem);
Config.Tabs.Add(TabsUtil.HellionEmote);
Config.Tabs.Add(TabsUtil.HellionFreeCompany);
Config.Tabs.Add(TabsUtil.HellionParty);
Config.Tabs.Add(TabsUtil.HellionLinkshell);
@@ -521,7 +560,7 @@ public sealed class Plugin : IAsyncDalamudPlugin
new SelfTests.SettingsWindowOpenStep(this),
new SelfTests.OnOpenMainUiRoutesMainWindowStep(this),
new SelfTests.TypingIpcStateStep(this),
new SelfTests.ConfigMigrationV26Step(this),
new SelfTests.ConfigMigrationV27Step(this),
new SelfTests.DbGateWiringStep(this),
new SelfTests.ChannelPopoutBindStep(this),
new SelfTests.HoverStateFootprintStep(),
@@ -599,7 +638,7 @@ public sealed class Plugin : IAsyncDalamudPlugin
_ = Task.Run(
async () =>
{
// FQN: Plugin.Notification (Z.74) shadows the type name.
// FQN: the Plugin.Notification property shadows the type name.
Dalamud.Interface.ImGuiNotification.IActiveNotification? notif = null;
try
{
@@ -732,7 +771,7 @@ public sealed class Plugin : IAsyncDalamudPlugin
return;
// Set before any cleanup so deferred Framework.RunOnTick callbacks
// (B3 retention sweep) see the flag and bail out before they touch
// (the retention sweep) see the flag and bail out before they touch
// MessageManager / Log / static fields that the rest of this method
// is about to tear down.
_isDisposing = true;
@@ -840,6 +879,38 @@ public sealed class Plugin : IAsyncDalamudPlugin
return failure;
}
// Copies the config aside before the 2.0.0 reset overwrites it. Best effort
// by design: a backup that cannot be written must not stop the plugin from
// starting, and the reset itself is what the user was told would happen.
//
// Overwrite=false, so a second run cannot bury the real backup under a copy
// of the already-reset file. The reset saves immediately for the same
// reason, but a crash in between is exactly when this matters.
private static void BackUpConfigBeforeReset()
{
try
{
var dir = Interface.ConfigDirectory.Parent?.FullName;
if (dir is null)
return;
var configFile = Path.Combine(dir, "HellionChat.json");
if (!File.Exists(configFile))
return;
var backup = Path.Combine(dir, "HellionChat.json.pre-2.0.0.bak");
if (File.Exists(backup))
return;
File.Copy(configFile, backup);
Log.Information($"HellionChat: config backed up to {backup} before the 2.0.0 reset");
}
catch (Exception e)
{
Log.Warning(e, "HellionChat: could not back up the config before the 2.0.0 reset");
}
}
private static void MigrateFromChatTwoLayout()
{
var pluginConfigsDir = Interface.ConfigDirectory.Parent?.FullName;
@@ -956,7 +1027,7 @@ public sealed class Plugin : IAsyncDalamudPlugin
{
_hellionSettingsCmd = Commands.Register(
"/hellion",
"Toggle Hellion Chat. /hellion settings opens settings, /hellion reset restores the default theme."
"Toggle Hellion Chat. /hellion settings opens settings, /hellion wizard reopens the setup wizard, /hellion reset restores the default theme."
);
_hellionSettingsCmd.Execute += OnHellionSettingsCommand;
@@ -1051,11 +1122,22 @@ public sealed class Plugin : IAsyncDalamudPlugin
InputBarLab.Toggle();
return;
}
// The wizard had no way back into it: the reopen button the resource
// file still carries in 25 languages lost its call site in the
// four-step rewrite, and nothing replaced it. Without this the only
// route is closing the game and editing FirstRunCompleted by hand.
// Toggle, not IsOpen = true, so the same command closes it again.
if (arg.Equals("wizard", StringComparison.OrdinalIgnoreCase))
{
FirstRunWizard.Toggle();
return;
}
#if DEBUG
#endif
if (arg.Equals("reset", StringComparison.OrdinalIgnoreCase))
{
// Recovery path documented in the v2.x master spec — drops a
// Documented recovery path -- drops a
// broken custom theme out of the loader cache without touching
// the user's JSON on disk.
ThemeRegistry.SwitchSilent(Themes.ThemeRegistry.DefaultSlug);
@@ -1170,7 +1252,7 @@ public sealed class Plugin : IAsyncDalamudPlugin
// Schedule on the next framework tick to avoid the ~194ms
// hitch from blocking with .Wait() while the frame finishes.
// The Config.Tabs enumeration in ClearAllTabs/FilterAllTabs is
// now guarded by the shared Plugin.TabsListLock (B3), so this
// now guarded by the shared Plugin.TabsListLock, so this
// tick scheduling is purely hitch-avoidance, not safety.
// Pattern reference: SimpleTweaks
// Tweaks/Chat/CaseInsensitiveCommands.cs:45.
@@ -1257,7 +1339,7 @@ public sealed class Plugin : IAsyncDalamudPlugin
private void Draw()
{
// v1.9.0 B5: time the whole handler (style + font prologue included).
// v1.9.0: time the whole handler (style + font prologue included).
// Bail before measuring once teardown has begun — a late Draw tick
// must not touch ThemeRegistry / FontManager after DisposeAsync.
if (_isDisposing)
@@ -1266,7 +1348,7 @@ public sealed class Plugin : IAsyncDalamudPlugin
var drawWatch = Stopwatch.StartNew();
try
{
// v1.4.8 B2: pick up external edits of the active custom theme JSON
// v1.4.8: pick up external edits of the active custom theme JSON
// without forcing the user to re-click the picker. The disk-stat is
// 1Hz-throttled inside RefreshActiveIfStale, so this is essentially
// free on built-in themes and ~1 stat/second on custom themes.
@@ -1356,10 +1438,10 @@ public sealed class Plugin : IAsyncDalamudPlugin
// Config.Tabs across the save so JSON includes them. Cloning only the
// unpinned subset keeps the allocation proportional to
// AutoTellTabsLimit (<=15) instead of the full tab list.
// B3: the strip/restore mutates the tab LIST, so it shares TabsListLock
// The strip/restore mutates the tab LIST, so it shares TabsListLock
// with the worker add/remove and the refilter snapshot. Re-entrant: the
// one worker caller (HandleTell) already holds it; framework callers take
// it here. SavePluginConfig runs inside (short, in-memory) — the §8 fallback
// it here. SavePluginConfig runs inside (short, in-memory) — the documented fallback
// (serialize a copy outside the lock) is a tracked pre-beta to-do.
lock (TabsListLock)
{
+11 -8
View File
@@ -15,7 +15,7 @@ namespace HellionChat;
// Builds the generic-host DI container that drives v1.5.0+. The factory is
// invoked synchronously from Plugin.ctor (after the schema gate clears) so the
// container exists before PluginLifecycle.LoadAsync runs. See plan §1 for the
// container exists before PluginLifecycle.LoadAsync runs. For the
// deliberate divergence from Lightless' deferred Func-delegate pattern.
internal static class PluginHostFactory
{
@@ -48,7 +48,7 @@ internal static class PluginHostFactory
PluginHostDependencies dependencies
)
{
// Block A — Dalamud services (21 [PluginService] singletons).
// Dalamud services (21 [PluginService] singletons).
services.AddSingleton(dependencies);
services.AddSingleton(dependencies.PluginInterface);
services.AddSingleton(dependencies.PluginLog);
@@ -77,7 +77,7 @@ internal static class PluginHostFactory
services.AddSingleton(plugin.WindowSystem);
services.AddSingleton<PluginLifecycle>();
// Block B — HellionChat singletons. Factory lambdas because most
// HellionChat singletons. Factory lambdas because most
// classes are internal-sealed and the default activator only sees
// public ctors.
services.AddSingleton<IPlatformUtil>(_ => new DalamudPlatformUtil());
@@ -307,7 +307,7 @@ internal static class PluginHostFactory
// Pop-out windows: each gets its OWN MessageList + InputBar so the
// channel pill and message scroll are per-window. The PayloadHandler is
// attached post-build (ChannelPopoutInitHostedService), NEVER via ctor
// (plan §B.2 — would close a silent FactoryCallSite cycle).
// (would close a silent FactoryCallSite cycle).
services.AddSingleton<Func<int, Ui.Windows.ChannelPopoutWindow>>(sp =>
slot => new Ui.Windows.ChannelPopoutWindow(
slot,
@@ -336,7 +336,7 @@ internal static class PluginHostFactory
sp.GetRequiredService<ILogger<Ui.Windows.ChannelPopoutPool>>()
));
// Block C — Windows. WindowSystem.AddWindow is called from
// Windows. WindowSystem.AddWindow is called from
// PluginLifecycle.LoadAsync on the framework thread.
services.AddSingleton(sp => new Ui.Windows.SettingsWindow(
sp.GetRequiredService<Plugin>(),
@@ -380,8 +380,8 @@ internal static class PluginHostFactory
));
#endif
// The style lab: variants side by side, in-game, against the live theme.
// Permanent by Flo's call, and deliberately not behind DEBUG -- style
// decisions happen in the build he actually runs.
// Permanent, and deliberately not behind DEBUG: style decisions get
// made in the build that actually ships.
services.AddSingleton(sp => new Ui.Windows.InputBarLabWindow(
sp.GetRequiredService<Plugin>()
));
@@ -389,7 +389,10 @@ internal static class PluginHostFactory
sp.GetRequiredService<Plugin>(),
sp.GetRequiredService<PayloadHandler>()
));
services.AddSingleton(sp => new FirstRunWizard(sp.GetRequiredService<Plugin>()));
services.AddSingleton(sp => new FirstRunWizard(
sp.GetRequiredService<Plugin>(),
sp.GetRequiredService<Ui.StyleEngine.SurfaceBackdrop>()
));
// Hosted-service adapters: thin wrappers around the existing init
// methods so the service class bodies stay unchanged. FontManager
+1 -1
View File
@@ -4,7 +4,7 @@ namespace HellionChat.Privacy;
internal static class PrivacyDefaults
{
// F3.1: failsafe for ChatTypes added by future FFXIV patches. New installs
// Failsafe for ChatTypes added by future FFXIV patches. New installs
// persist unknown channels so a major patch's added ChatType isn't silently
// dropped before the user can opt in or out. Existing configs keep their
// explicit choice — see Configuration.cs PrivacyPersistUnknownChannels.
+11 -8
View File
@@ -116,10 +116,12 @@ internal class HellionStrings
internal static string Wizard_Step1_Title => Get(nameof(Wizard_Step1_Title));
internal static string Wizard_Step1_Subtitle => Get(nameof(Wizard_Step1_Subtitle));
internal static string Wizard_Step1_Footer_Hint => Get(nameof(Wizard_Step1_Footer_Hint));
internal static string Wizard_Step1_PluginNotice => Get(nameof(Wizard_Step1_PluginNotice));
internal static string Wizard_Step1_Heritage => Get(nameof(Wizard_Step1_Heritage));
internal static string Wizard_Step1_Skip_Label => Get(nameof(Wizard_Step1_Skip_Label));
internal static string Wizard_Step1_Skip_Tooltip => Get(nameof(Wizard_Step1_Skip_Tooltip));
internal static string Wizard_Step2_Title => Get(nameof(Wizard_Step2_Title));
internal static string Wizard_Step2_RecommendedFooter => Get(nameof(Wizard_Step2_RecommendedFooter));
internal static string Wizard_Profile_Recommended_Badge => Get(nameof(Wizard_Profile_Recommended_Badge));
internal static string Wizard_Profile_Roleplay_Heading => Get(nameof(Wizard_Profile_Roleplay_Heading));
internal static string Wizard_Profile_Roleplay_Description => Get(nameof(Wizard_Profile_Roleplay_Description));
internal static string Wizard_Profile_Roleplay_Apply => Get(nameof(Wizard_Profile_Roleplay_Apply));
@@ -282,6 +284,7 @@ internal class HellionStrings
// Hellion Chat — Default tab presets (channel-themed)
internal static string Tabs_Presets_System => Get(nameof(Tabs_Presets_System));
internal static string Tabs_Presets_Emote => Get(nameof(Tabs_Presets_Emote));
internal static string Tabs_Presets_FreeCompany => Get(nameof(Tabs_Presets_FreeCompany));
internal static string Tabs_Presets_Party => Get(nameof(Tabs_Presets_Party));
internal static string Tabs_Presets_Beginner => Get(nameof(Tabs_Presets_Beginner));
@@ -343,7 +346,7 @@ internal class HellionStrings
// Hellion Chat — v1.2.1 Data Management tab section headings
internal static string Settings_DataManagement_Advanced_Heading => Get(nameof(Settings_DataManagement_Advanced_Heading));
// v1.5.6: Data & Privacy tab section titles (R6)
// v1.5.6: Data & Privacy tab section titles
internal static string Settings_Section_PrivacyFilter => Get(nameof(Settings_Section_PrivacyFilter));
internal static string Settings_Section_Storage => Get(nameof(Settings_Section_Storage));
internal static string Settings_Section_Retention => Get(nameof(Settings_Section_Retention));
@@ -535,14 +538,14 @@ internal class HellionStrings
internal static string Settings_General_CustomSoundVolume_Name => Get(nameof(Settings_General_CustomSoundVolume_Name));
internal static string Settings_General_CustomSoundVolume_Description => Get(nameof(Settings_General_CustomSoundVolume_Description));
// v1.5.6: General tab collapsible section titles (R6)
// v1.5.6: General tab collapsible section titles
internal static string Settings_Section_Input => Get(nameof(Settings_Section_Input));
internal static string Settings_Section_Sound => Get(nameof(Settings_Section_Sound));
internal static string Settings_Section_Language => Get(nameof(Settings_Section_Language));
internal static string Settings_Section_Performance => Get(nameof(Settings_Section_Performance));
internal static string Settings_Section_Sound_TabsHint => Get(nameof(Settings_Section_Sound_TabsHint));
// v1.5.6: Chat tab collapsible section titles (R6)
// v1.5.6: Chat tab collapsible section titles
internal static string Settings_Section_Messages => Get(nameof(Settings_Section_Messages));
internal static string Settings_Section_InputPreview => Get(nameof(Settings_Section_InputPreview));
internal static string Settings_Section_AutoTellTabs => Get(nameof(Settings_Section_AutoTellTabs));
@@ -550,7 +553,7 @@ internal class HellionStrings
internal static string Settings_Section_LinksTooltips => Get(nameof(Settings_Section_LinksTooltips));
internal static string Settings_Section_NoviceNetwork => Get(nameof(Settings_Section_NoviceNetwork));
// v1.5.6: Appearance tab collapsible section titles (R6)
// v1.5.6: Appearance tab collapsible section titles
internal static string Settings_Section_Theme => Get(nameof(Settings_Section_Theme));
internal static string Settings_Section_Fonts => Get(nameof(Settings_Section_Fonts));
internal static string Settings_Section_Colours => Get(nameof(Settings_Section_Colours));
@@ -558,12 +561,12 @@ internal class HellionStrings
internal static string Settings_Section_Timestamps => Get(nameof(Settings_Section_Timestamps));
internal static string Settings_Section_Animations => Get(nameof(Settings_Section_Animations));
// v1.5.6: Window tab collapsible section titles (R6)
// v1.5.6: Window tab collapsible section titles
internal static string Settings_Section_Hide => Get(nameof(Settings_Section_Hide));
internal static string Settings_Section_InactivityHide => Get(nameof(Settings_Section_InactivityHide));
internal static string Settings_Section_Frame => Get(nameof(Settings_Section_Frame));
// v1.5.6: Tabs tab per-tab-item sub-section titles (R6)
// v1.5.6: Tabs tab per-tab-item sub-section titles
internal static string Settings_Section_Tab_Channels => Get(nameof(Settings_Section_Tab_Channels));
internal static string Settings_Section_Tab_Display => Get(nameof(Settings_Section_Tab_Display));
internal static string Settings_Section_Tab_Notification => Get(nameof(Settings_Section_Tab_Notification));
@@ -571,7 +574,7 @@ internal class HellionStrings
internal static string Settings_Section_Tab_PopOut => Get(nameof(Settings_Section_Tab_PopOut));
internal static string Settings_Section_Tab_Volume_AllTabsHint => Get(nameof(Settings_Section_Tab_Volume_AllTabsHint));
// v1.5.6: About tab collapsible section titles (R6)
// v1.5.6: About tab collapsible section titles
internal static string Settings_Section_Extensions => Get(nameof(Settings_Section_Extensions));
internal static string Settings_Section_PluginInfo => Get(nameof(Settings_Section_PluginInfo));
internal static string Settings_Section_Project => Get(nameof(Settings_Section_Project));
+14 -5
View File
@@ -172,7 +172,7 @@
<value>Tria un perfil inicial. Podràs ajustar-ho tot més tard a Configuració → Privadesa.</value>
</data>
<data name="Wizard_Profile_PrivacyFirst_Heading" xml:space="preserve">
<value>Minimització de dades (recomanat)</value>
<value>Minimització de dades</value>
</data>
<data name="Wizard_Profile_PrivacyFirst_Description" xml:space="preserve">
<value>Només es guarden les teves pròpies converses: tells, party, FC, linkshells, cross-world linkshells, alliance i ExtraChat. El xat públic, els diàlegs dels PNJ i el correu brossa del sistema es descarten al nivell d'emmagatzematge. La retenció segueix els valors per defecte de l'especificació (tells 365 dies, canals de conversa propis 90 dies).</value>
@@ -214,7 +214,13 @@
<value>Benvingut a Hellion Chat</value>
</data>
<data name="Wizard_Step1_Subtitle" xml:space="preserve">
<value>Un fork de Chat 2 de Hellion Forge amb valors per defecte respectuosos amb la privadesa, visuals coherents amb la marca i alguns retocs de qualitat de vida.</value>
<value>La teva finestra de xat, de Hellion Forge. Privadesa des del primer moment, en 25 idiomes, i l'organitzes com vulguis.</value>
</data>
<data name="Wizard_Step1_Heritage" xml:space="preserve">
<value>Hellion Chat va començar com un fork de Chat 2. Des d'aleshores tots dos s'han allunyat prou perquè les bases de codi ja no siguin compatibles.</value>
</data>
<data name="Wizard_Step1_PluginNotice" xml:space="preserve">
<value>Els plugins són una zona grisa a Final Fantasy XIV: les condicions d'ús de Square Enix no els cobreixen, i Naoki Yoshida ha demanat públicament que no se'n faci publicitat. Mantén, doncs, el tema fora de Say, Yell, Shout i de qualsevol altre canal públic.</value>
</data>
<data name="Wizard_Step1_Footer_Hint" xml:space="preserve">
<value>Tres passos ràpids. Podràs canviar-ho tot més tard a Configuració → Hellion Chat.</value>
@@ -228,8 +234,8 @@
<data name="Wizard_Step2_Title" xml:space="preserve">
<value>Què es guarda?</value>
</data>
<data name="Wizard_Step2_RecommendedFooter" xml:space="preserve">
<value>★ = recomanat per a la majoria de jugadors.</value>
<data name="Wizard_Profile_Recommended_Badge" xml:space="preserve">
<value>Recomanat</value>
</data>
<data name="Wizard_Profile_Roleplay_Heading" xml:space="preserve">
<value>Roleplay</value>
@@ -298,7 +304,7 @@
<value>(sense canvis)</value>
</data>
<data name="Wizard_Step4_TestHint" xml:space="preserve">
<value>💡 Prova-ho: escriu /tell &lt;Nom del jugador&gt; al xat. Hellion Chat obre una pestanya dedicada per a la conversa i precarrega els últims {0} missatges.</value>
<value>Prova-ho: escriu /tell &lt;Nom del jugador&gt; al xat. Hellion Chat obre una pestanya dedicada per a la conversa i precarrega els últims {0} missatges.</value>
</data>
<data name="Wizard_Step4_SettingsHint" xml:space="preserve">
<value>Configuració → Hellion Chat per ajustar-ho més tard</value>
@@ -605,6 +611,9 @@
<data name="Tabs_Presets_System" xml:space="preserve">
<value>Sistema</value>
</data>
<data name="Tabs_Presets_Emote" xml:space="preserve">
<value>Emotes</value>
</data>
<data name="Tabs_Presets_FreeCompany" xml:space="preserve">
<value>Free Company</value>
</data>
+14 -5
View File
@@ -172,7 +172,7 @@
<value>Vyber si výchozí profil. Vše můžeš kdykoli později upravit v Nastavení → Soukromí.</value>
</data>
<data name="Wizard_Profile_PrivacyFirst_Heading" xml:space="preserve">
<value>Minimalizace dat (doporučeno)</value>
<value>Minimalizace dat</value>
</data>
<data name="Wizard_Profile_PrivacyFirst_Description" xml:space="preserve">
<value>Ukládají se pouze tvoje vlastní konverzace: telly, party, FC, linkshelly, cross-world linkshelly, aliance a ExtraChat. Veřejný chat, dialogy NPC a systémový spam se na úrovni úložiště zahodí. Uchovávání dle výchozích hodnot specifikace (telly 365 dní, vlastní konverzační kanály 90 dní).</value>
@@ -214,7 +214,13 @@
<value>Vítej v Hellion Chat</value>
</data>
<data name="Wizard_Step1_Subtitle" xml:space="preserve">
<value>Fork Chat 2 od Hellion Forge s výchozím nastavením ohleduplným ke soukromí, vizuálem odpovídajícím značce a pár vylepšeními kvality života.</value>
<value>Tvoje okno chatu od Hellion Forge. Soukromí od prvního spuštění, 25 jazyků a rozvržení si nastavíš sám.</value>
</data>
<data name="Wizard_Step1_Heritage" xml:space="preserve">
<value>Hellion Chat vznikl jako fork Chat 2. Od té doby se oba projekty rozešly natolik, že jejich kódové základny už nejsou kompatibilní.</value>
</data>
<data name="Wizard_Step1_PluginNotice" xml:space="preserve">
<value>Pluginy jsou ve Final Fantasy XIV šedá zóna: podmínky užívání Square Enix je nepokrývají a Naoki Yoshida veřejně požádal, aby se nepropagovaly. Nezmiňuj proto toto téma na Say, Yell, Shout ani na žádném jiném veřejném kanálu.</value>
</data>
<data name="Wizard_Step1_Footer_Hint" xml:space="preserve">
<value>Tři krátké kroky. Vše můžeš kdykoli změnit v Nastavení → Hellion Chat.</value>
@@ -228,8 +234,8 @@
<data name="Wizard_Step2_Title" xml:space="preserve">
<value>Co se bude ukládat?</value>
</data>
<data name="Wizard_Step2_RecommendedFooter" xml:space="preserve">
<value>★ = doporučeno pro většinu hráčů.</value>
<data name="Wizard_Profile_Recommended_Badge" xml:space="preserve">
<value>Doporučeno</value>
</data>
<data name="Wizard_Profile_Roleplay_Heading" xml:space="preserve">
<value>Roleplay</value>
@@ -298,7 +304,7 @@
<value>(beze změny)</value>
</data>
<data name="Wizard_Step4_TestHint" xml:space="preserve">
<value>💡 Vyzkoušej: napiš /tell &lt;Jméno hráče&gt; do chatu. Hellion Chat automaticky otevře vlastní záložku pro konverzaci a přednačte posledních {0} zpráv.</value>
<value>Vyzkoušej: napiš /tell &lt;Jméno hráče&gt; do chatu. Hellion Chat automaticky otevře vlastní záložku pro konverzaci a přednačte posledních {0} zpráv.</value>
</data>
<data name="Wizard_Step4_SettingsHint" xml:space="preserve">
<value>Nastavení → Hellion Chat pro pozdější doladění</value>
@@ -605,6 +611,9 @@
<data name="Tabs_Presets_System" xml:space="preserve">
<value>Systém</value>
</data>
<data name="Tabs_Presets_Emote" xml:space="preserve">
<value>Emoty</value>
</data>
<data name="Tabs_Presets_FreeCompany" xml:space="preserve">
<value>Free Company</value>
</data>
+14 -5
View File
@@ -172,7 +172,7 @@
<value>Vælg en startprofil. Du kan justere alt efterfølgende under Indstillinger → Privatliv.</value>
</data>
<data name="Wizard_Profile_PrivacyFirst_Heading" xml:space="preserve">
<value>Dataminimering (anbefalet)</value>
<value>Dataminimering</value>
</data>
<data name="Wizard_Profile_PrivacyFirst_Description" xml:space="preserve">
<value>Kun dine egne samtaler gemmes: tells, gruppe, FC, linkshells, cross-world linkshells, alliance og ExtraChat. Offentlig chat, NPC-dialoger og systemstøj kasseres på lagerniveau. Opbevaring følger spec-standarder (tells 365 dage, egne samtalekanaler 90 dage).</value>
@@ -214,7 +214,13 @@
<value>Velkommen til Hellion Chat</value>
</data>
<data name="Wizard_Step1_Subtitle" xml:space="preserve">
<value>En Chat 2 fork fra Hellion Forge med privatlivsbevidste standarder, brandkonsistent udseende og et par praktiske forbedringer.</value>
<value>Dit chatvindue fra Hellion Forge. Privatliv fra første start, 25 sprog, og du sætter det op, som du vil.</value>
</data>
<data name="Wizard_Step1_Heritage" xml:space="preserve">
<value>Hellion Chat startede som en fork af Chat 2. De to har siden fjernet sig så meget fra hinanden, at kodebaserne ikke længere er kompatible.</value>
</data>
<data name="Wizard_Step1_PluginNotice" xml:space="preserve">
<value>Plugins er en gråzone i Final Fantasy XIV: Square Enix' brugsvilkår dækker dem ikke, og Naoki Yoshida har offentligt bedt om, at man ikke reklamerer for dem. Hold derfor emnet ude af Say, Yell, Shout og alle andre offentlige kanaler.</value>
</data>
<data name="Wizard_Step1_Footer_Hint" xml:space="preserve">
<value>Tre korte trin. Du kan ændre alt efterfølgende under Indstillinger → Hellion Chat.</value>
@@ -228,8 +234,8 @@
<data name="Wizard_Step2_Title" xml:space="preserve">
<value>Hvad gemmes?</value>
</data>
<data name="Wizard_Step2_RecommendedFooter" xml:space="preserve">
<value>★ = anbefalet til de fleste spillere.</value>
<data name="Wizard_Profile_Recommended_Badge" xml:space="preserve">
<value>Anbefalet</value>
</data>
<data name="Wizard_Profile_Roleplay_Heading" xml:space="preserve">
<value>Roleplay</value>
@@ -298,7 +304,7 @@
<value>(uændret)</value>
</data>
<data name="Wizard_Step4_TestHint" xml:space="preserve">
<value>💡 Prøv det: skriv /tell &lt;Spillernavn&gt; i chatten. Hellion Chat åbner en dedikeret tab til samtalen og forudindlæser de sidste {0} beskeder.</value>
<value>Prøv det: skriv /tell &lt;Spillernavn&gt; i chatten. Hellion Chat åbner en dedikeret tab til samtalen og forudindlæser de sidste {0} beskeder.</value>
</data>
<data name="Wizard_Step4_SettingsHint" xml:space="preserve">
<value>Indstillinger → Hellion Chat for at finjustere senere</value>
@@ -605,6 +611,9 @@
<data name="Tabs_Presets_System" xml:space="preserve">
<value>System</value>
</data>
<data name="Tabs_Presets_Emote" xml:space="preserve">
<value>Emotes</value>
</data>
<data name="Tabs_Presets_FreeCompany" xml:space="preserve">
<value>Free Company</value>
</data>
+14 -5
View File
@@ -172,7 +172,7 @@
<value>Wähle ein Start-Profil. Du kannst später alles unter Einstellungen → Datenschutz anpassen.</value>
</data>
<data name="Wizard_Profile_PrivacyFirst_Heading" xml:space="preserve">
<value>Datensparsamkeit (empfohlen)</value>
<value>Datensparsamkeit</value>
</data>
<data name="Wizard_Profile_PrivacyFirst_Description" xml:space="preserve">
<value>Es werden nur deine eigenen Konversationen gespeichert: Flüsternachrichten, Gruppe, FC, Linkshells, Cross-World-Linkshells, Allianz und ExtraChat. Öffentlicher Chat, NPC-Dialoge und System-Spam werden auf der Storage-Ebene verworfen. Aufbewahrung nach Spec-Defaults (Flüsternachrichten 365 Tage, eigene Konversations-Kanäle 90 Tage).</value>
@@ -214,7 +214,13 @@
<value>Willkommen bei Hellion Chat</value>
</data>
<data name="Wizard_Step1_Subtitle" xml:space="preserve">
<value>Ein Chat 2 Fork von Hellion Forge mit DSGVO-konformen Defaults, brand-konsistentem Look und Quality-of-Life-Verbesserungen.</value>
<value>Dein Chat-Fenster von Hellion Forge. Datensparsam ab Werk, in 25 Sprachen, und du richtest es dir ein wie du willst.</value>
</data>
<data name="Wizard_Step1_PluginNotice" xml:space="preserve">
<value>Plugins sind in Final Fantasy XIV eine Grauzone: Die Nutzungsbedingungen von Square Enix decken sie nicht ab, und Naoki Yoshida hat öffentlich darum gebeten, nicht damit zu werben. Halte das Thema deshalb aus Sagen, Rufen, Schreien und allen anderen öffentlichen Kanälen heraus.</value>
</data>
<data name="Wizard_Step1_Heritage" xml:space="preserve">
<value>Hellion Chat ging ursprünglich aus Chat 2 hervor. Beide haben sich seitdem so weit auseinanderentwickelt, dass die Codebasen nicht mehr kompatibel sind.</value>
</data>
<data name="Wizard_Step1_Footer_Hint" xml:space="preserve">
<value>3 kurze Schritte. Du kannst alles später unter Einstellungen → Hellion Chat ändern.</value>
@@ -228,8 +234,8 @@
<data name="Wizard_Step2_Title" xml:space="preserve">
<value>Was darf gespeichert werden?</value>
</data>
<data name="Wizard_Step2_RecommendedFooter" xml:space="preserve">
<value>★ = empfohlen für die meisten Spieler.</value>
<data name="Wizard_Profile_Recommended_Badge" xml:space="preserve">
<value>Empfohlen</value>
</data>
<data name="Wizard_Profile_Roleplay_Heading" xml:space="preserve">
<value>Roleplay</value>
@@ -298,7 +304,7 @@
<value>(unverändert)</value>
</data>
<data name="Wizard_Step4_TestHint" xml:space="preserve">
<value>💡 Probier's aus: Tipp /tell &lt;Spielername&gt; in den Chat. Hellion Chat öffnet automatisch einen eigenen Tab für die Unterhaltung und lädt die letzten {0} Messages mit.</value>
<value>Probier's aus: Tipp /tell &lt;Spielername&gt; in den Chat. Hellion Chat öffnet automatisch einen eigenen Tab für die Unterhaltung und lädt die letzten {0} Messages mit.</value>
</data>
<data name="Wizard_Step4_SettingsHint" xml:space="preserve">
<value>Einstellungen → Hellion Chat zum späteren Anpassen</value>
@@ -605,6 +611,9 @@
<data name="Tabs_Presets_System" xml:space="preserve">
<value>System</value>
</data>
<data name="Tabs_Presets_Emote" xml:space="preserve">
<value>Emotes</value>
</data>
<data name="Tabs_Presets_FreeCompany" xml:space="preserve">
<value>Free Company</value>
</data>
+14 -5
View File
@@ -172,7 +172,7 @@
<value>Επίλεξε ένα αρχικό προφίλ. Μπορείς να ρυθμίσεις τα πάντα αργότερα στις Ρυθμίσεις → Απόρρητο.</value>
</data>
<data name="Wizard_Profile_PrivacyFirst_Heading" xml:space="preserve">
<value>Ελαχιστοποίηση δεδομένων (συνιστάται)</value>
<value>Ελαχιστοποίηση δεδομένων</value>
</data>
<data name="Wizard_Profile_PrivacyFirst_Description" xml:space="preserve">
<value>Αποθηκεύονται μόνο οι δικές σου συνομιλίες: tells, party, FC, linkshells, cross-world linkshells, alliance και ExtraChat. Το δημόσιο chat, οι διάλογοι NPC και το system spam απορρίπτονται σε επίπεδο αποθήκευσης. Η διατήρηση ακολουθεί τις προεπιλογές spec (tells 365 ημέρες, κανάλια ιδιωτικών συνομιλιών 90 ημέρες).</value>
@@ -214,7 +214,13 @@
<value>Καλωσόρισες στο Hellion Chat</value>
</data>
<data name="Wizard_Step1_Subtitle" xml:space="preserve">
<value>Ένα fork του Chat 2 από το Hellion Forge με προεπιλογές φιλικές στο απόρρητο, συνεπή εμφάνιση brand και μερικές βελτιώσεις ευχρηστίας.</value>
<value>Το παράθυρο συνομιλίας σου από το Hellion Forge. Ιδιωτικότητα από την αρχή, σε 25 γλώσσες, και το στήνεις όπως θέλεις.</value>
</data>
<data name="Wizard_Step1_Heritage" xml:space="preserve">
<value>Το Hellion Chat ξεκίνησε ως fork του Chat 2. Έκτοτε τα δύο έχουν απομακρυνθεί τόσο ώστε οι κώδικές τους να μην είναι πλέον συμβατοί.</value>
</data>
<data name="Wizard_Step1_PluginNotice" xml:space="preserve">
<value>Τα plugin αποτελούν γκρίζα ζώνη στο Final Fantasy XIV: οι όροι χρήσης της Square Enix δεν τα καλύπτουν και ο Naoki Yoshida έχει ζητήσει δημόσια να μην διαφημίζονται. Κράτα λοιπόν το θέμα μακριά από τα Say, Yell, Shout και κάθε άλλο δημόσιο κανάλι.</value>
</data>
<data name="Wizard_Step1_Footer_Hint" xml:space="preserve">
<value>Τρία σύντομα βήματα. Μπορείς να αλλάξεις τα πάντα αργότερα στις Ρυθμίσεις → Hellion Chat.</value>
@@ -228,8 +234,8 @@
<data name="Wizard_Step2_Title" xml:space="preserve">
<value>Τι αποθηκεύεται;</value>
</data>
<data name="Wizard_Step2_RecommendedFooter" xml:space="preserve">
<value>★ = συνιστάται για τους περισσότερους παίκτες.</value>
<data name="Wizard_Profile_Recommended_Badge" xml:space="preserve">
<value>Συνιστάται</value>
</data>
<data name="Wizard_Profile_Roleplay_Heading" xml:space="preserve">
<value>Roleplay</value>
@@ -298,7 +304,7 @@
<value>(αμετάβλητο)</value>
</data>
<data name="Wizard_Step4_TestHint" xml:space="preserve">
<value>💡 Δοκίμασέ το: πληκτρολόγησε /tell &lt;Όνομα Παίκτη&gt; στο chat. Το Hellion Chat ανοίγει αυτόματα μια αποκλειστική καρτέλα για τη συνομιλία και προφορτώνει τα τελευταία {0} μηνύματα.</value>
<value>Δοκίμασέ το: πληκτρολόγησε /tell &lt;Όνομα Παίκτη&gt; στο chat. Το Hellion Chat ανοίγει αυτόματα μια αποκλειστική καρτέλα για τη συνομιλία και προφορτώνει τα τελευταία {0} μηνύματα.</value>
</data>
<data name="Wizard_Step4_SettingsHint" xml:space="preserve">
<value>Ρυθμίσεις → Hellion Chat για προσαρμογή αργότερα</value>
@@ -605,6 +611,9 @@
<data name="Tabs_Presets_System" xml:space="preserve">
<value>System</value>
</data>
<data name="Tabs_Presets_Emote" xml:space="preserve">
<value>Emotes</value>
</data>
<data name="Tabs_Presets_FreeCompany" xml:space="preserve">
<value>Free Company</value>
</data>
+14 -5
View File
@@ -172,7 +172,7 @@
<value>Elige un perfil inicial. Puedes ajustarlo todo más tarde en Ajustes → Privacidad.</value>
</data>
<data name="Wizard_Profile_PrivacyFirst_Heading" xml:space="preserve">
<value>Minimización de datos (recomendado)</value>
<value>Minimización de datos</value>
</data>
<data name="Wizard_Profile_PrivacyFirst_Description" xml:space="preserve">
<value>Solo se almacenan tus propias conversaciones: tells, escuadrón, FC, linkshells, linkshells cross-world, alianza y ExtraChat. El chat público, los diálogos de PNJ y el spam del sistema se descartan a nivel de almacenamiento. La retención sigue los valores predeterminados de spec (tells 365 días, canales de conversación propios 90 días).</value>
@@ -214,7 +214,13 @@
<value>Bienvenido a Hellion Chat</value>
</data>
<data name="Wizard_Step1_Subtitle" xml:space="preserve">
<value>Un fork de Chat 2 de Hellion Forge con valores predeterminados respetuosos con la privacidad, diseño coherente con la marca y algunas mejoras de calidad de vida.</value>
<value>Tu ventana de chat, de Hellion Forge. Privacidad desde el primer momento, traducida a 25 idiomas, y la organizas a tu gusto.</value>
</data>
<data name="Wizard_Step1_Heritage" xml:space="preserve">
<value>Hellion Chat nació como un fork de Chat 2. Desde entonces ambos se han separado lo suficiente como para que sus bases de código ya no sean compatibles.</value>
</data>
<data name="Wizard_Step1_PluginNotice" xml:space="preserve">
<value>Los plugins son una zona gris en Final Fantasy XIV: las condiciones de uso de Square Enix no los contemplan, y Naoki Yoshida ha pedido públicamente que no se promocionen. Mantén el tema fuera de Decir, Gritar, Vociferar y de cualquier otro canal público.</value>
</data>
<data name="Wizard_Step1_Footer_Hint" xml:space="preserve">
<value>Tres pasos breves. Puedes cambiar todo más tarde en Ajustes → Hellion Chat.</value>
@@ -228,8 +234,8 @@
<data name="Wizard_Step2_Title" xml:space="preserve">
<value>¿Qué se almacena?</value>
</data>
<data name="Wizard_Step2_RecommendedFooter" xml:space="preserve">
<value>★ = recomendado para la mayoría de jugadores.</value>
<data name="Wizard_Profile_Recommended_Badge" xml:space="preserve">
<value>Recomendado</value>
</data>
<data name="Wizard_Profile_Roleplay_Heading" xml:space="preserve">
<value>Roleplay</value>
@@ -298,7 +304,7 @@
<value>(sin cambios)</value>
</data>
<data name="Wizard_Step4_TestHint" xml:space="preserve">
<value>💡 Pruébalo: escribe /tell &lt;Nombre del jugador&gt; en el chat. Hellion Chat abre una pestaña dedicada para la conversación y precarga los últimos {0} mensajes.</value>
<value>Pruébalo: escribe /tell &lt;Nombre del jugador&gt; en el chat. Hellion Chat abre una pestaña dedicada para la conversación y precarga los últimos {0} mensajes.</value>
</data>
<data name="Wizard_Step4_SettingsHint" xml:space="preserve">
<value>Ajustes → Hellion Chat para personalizar más tarde</value>
@@ -605,6 +611,9 @@
<data name="Tabs_Presets_System" xml:space="preserve">
<value>Sistema</value>
</data>
<data name="Tabs_Presets_Emote" xml:space="preserve">
<value>Emotes</value>
</data>
<data name="Tabs_Presets_FreeCompany" xml:space="preserve">
<value>Free Company</value>
</data>
+14 -5
View File
@@ -172,7 +172,7 @@
<value>Valitse aloitusprofiili. Voit muuttaa kaikkea myöhemmin kohdassa Asetukset → Tietosuoja.</value>
</data>
<data name="Wizard_Profile_PrivacyFirst_Heading" xml:space="preserve">
<value>Tietojen minimointi (suositeltu)</value>
<value>Tietojen minimointi</value>
</data>
<data name="Wizard_Profile_PrivacyFirst_Description" xml:space="preserve">
<value>Vain omat keskustelusi tallennetaan: tellit, ryhmä, FC, linkshells, cross-world linkshells, allianssi ja ExtraChat. Julkinen chat, NPC-dialogit ja järjestelmäroskaviestit hylätään tallennusvaiheessa. Säilytys noudattaa spec-oletuksia (tellit 365 päivää, omat keskustelukanavat 90 päivää).</value>
@@ -214,7 +214,13 @@
<value>Tervetuloa Hellion Chatiin</value>
</data>
<data name="Wizard_Step1_Subtitle" xml:space="preserve">
<value>Chat 2 -haarautuma Hellion Forgelta, tietosuojatietoisilla oletuksilla, yhtenäisellä visuaalisella ilmeellä ja muutamilla käytännöllisillä parannuksilla.</value>
<value>Chat-ikkunasi Hellion Forgelta. Yksityisyys heti alusta, 25 kieltä, ja järjestät sen kuten haluat.</value>
</data>
<data name="Wizard_Step1_Heritage" xml:space="preserve">
<value>Hellion Chat sai alkunsa Chat 2:n forkkina. Sittemmin ne ovat eronneet toisistaan niin paljon, etteivät koodipohjat ole enää yhteensopivia.</value>
</data>
<data name="Wizard_Step1_PluginNotice" xml:space="preserve">
<value>Pluginit ovat Final Fantasy XIV:ssä harmaata aluetta: Square Enixin käyttöehdot eivät kata niitä, ja Naoki Yoshida on julkisesti pyytänyt, ettei niitä mainostettaisi. Pidä aihe siis poissa kanavilta Say, Yell, Shout ja kaikilta muilta julkisilta kanavilta.</value>
</data>
<data name="Wizard_Step1_Footer_Hint" xml:space="preserve">
<value>Kolme lyhyttä vaihetta. Voit muuttaa kaikkea myöhemmin kohdassa Asetukset → Hellion Chat.</value>
@@ -228,8 +234,8 @@
<data name="Wizard_Step2_Title" xml:space="preserve">
<value>Mitä tallennetaan?</value>
</data>
<data name="Wizard_Step2_RecommendedFooter" xml:space="preserve">
<value>★ = suositeltu useimmille pelaajille.</value>
<data name="Wizard_Profile_Recommended_Badge" xml:space="preserve">
<value>Suositeltu</value>
</data>
<data name="Wizard_Profile_Roleplay_Heading" xml:space="preserve">
<value>Roleplay</value>
@@ -298,7 +304,7 @@
<value>(ei muutosta)</value>
</data>
<data name="Wizard_Step4_TestHint" xml:space="preserve">
<value>💡 Kokeile: kirjoita /tell &lt;Pelaajan nimi&gt; chattiin. Hellion Chat avaa erillisen välilehden keskustelulle ja esivalmistelee viimeiset {0} viestiä.</value>
<value>Kokeile: kirjoita /tell &lt;Pelaajan nimi&gt; chattiin. Hellion Chat avaa erillisen välilehden keskustelulle ja esivalmistelee viimeiset {0} viestiä.</value>
</data>
<data name="Wizard_Step4_SettingsHint" xml:space="preserve">
<value>Asetukset → Hellion Chat hienosäätöä varten myöhemmin</value>
@@ -605,6 +611,9 @@
<data name="Tabs_Presets_System" xml:space="preserve">
<value>Järjestelmä</value>
</data>
<data name="Tabs_Presets_Emote" xml:space="preserve">
<value>Emootiot</value>
</data>
<data name="Tabs_Presets_FreeCompany" xml:space="preserve">
<value>Free Company</value>
</data>
+14 -5
View File
@@ -172,7 +172,7 @@
<value>Choisissez un profil de départ. Vous pouvez tout ajuster par la suite dans Paramètres → Confidentialité.</value>
</data>
<data name="Wizard_Profile_PrivacyFirst_Heading" xml:space="preserve">
<value>Minimisation des données (recommandé)</value>
<value>Minimisation des données</value>
</data>
<data name="Wizard_Profile_PrivacyFirst_Description" xml:space="preserve">
<value>Seules vos propres conversations sont enregistrées : messages privés, équipe, CL, linkshells, linkshells inter-mondes, alliance et ExtraChat. Le chat public, les dialogues PNJ et le spam système sont écartés au niveau du stockage. La conservation suit les valeurs par défaut de la spécification (messages privés 365 jours, vos canaux de conversation 90 jours).</value>
@@ -214,7 +214,13 @@
<value>Bienvenue dans Hellion Chat</value>
</data>
<data name="Wizard_Step1_Subtitle" xml:space="preserve">
<value>Un fork de Chat 2 par Hellion Forge avec des valeurs par défaut axées sur la confidentialité, une identité visuelle cohérente et quelques améliorations de confort.</value>
<value>Ta fenêtre de discussion, signée Hellion Forge. Confidentialité par défaut, traduite en 25 langues, et à agencer comme tu veux.</value>
</data>
<data name="Wizard_Step1_Heritage" xml:space="preserve">
<value>Hellion Chat est né d'un fork de Chat 2. Les deux se sont depuis suffisamment éloignés pour que les bases de code ne soient plus compatibles.</value>
</data>
<data name="Wizard_Step1_PluginNotice" xml:space="preserve">
<value>Les plugins sont une zone grise dans Final Fantasy XIV : les conditions d'utilisation de Square Enix ne les couvrent pas, et Naoki Yoshida a publiquement demandé de ne pas en faire la promotion. Évite donc le sujet dans Dire, Crier, Hurler et tout autre canal public.</value>
</data>
<data name="Wizard_Step1_Footer_Hint" xml:space="preserve">
<value>Trois étapes courtes. Vous pouvez tout modifier plus tard dans Paramètres → Hellion Chat.</value>
@@ -228,8 +234,8 @@
<data name="Wizard_Step2_Title" xml:space="preserve">
<value>Qu'est-ce qui est enregistré ?</value>
</data>
<data name="Wizard_Step2_RecommendedFooter" xml:space="preserve">
<value>★ = recommandé pour la plupart des joueurs.</value>
<data name="Wizard_Profile_Recommended_Badge" xml:space="preserve">
<value>Recommandé</value>
</data>
<data name="Wizard_Profile_Roleplay_Heading" xml:space="preserve">
<value>Roleplay</value>
@@ -298,7 +304,7 @@
<value>(inchangé)</value>
</data>
<data name="Wizard_Step4_TestHint" xml:space="preserve">
<value>💡 Essayez : tapez /tell &lt;Nom du joueur&gt; dans le chat. Hellion Chat ouvre un onglet dédié à la conversation et précharge les {0} derniers messages.</value>
<value>Essayez : tapez /tell &lt;Nom du joueur&gt; dans le chat. Hellion Chat ouvre un onglet dédié à la conversation et précharge les {0} derniers messages.</value>
</data>
<data name="Wizard_Step4_SettingsHint" xml:space="preserve">
<value>Paramètres → Hellion Chat pour affiner plus tard</value>
@@ -605,6 +611,9 @@
<data name="Tabs_Presets_System" xml:space="preserve">
<value>Système</value>
</data>
<data name="Tabs_Presets_Emote" xml:space="preserve">
<value>Emotes</value>
</data>
<data name="Tabs_Presets_FreeCompany" xml:space="preserve">
<value>Compagnie libre</value>
</data>
+14 -5
View File
@@ -172,7 +172,7 @@
<value>Válassz egy kezdő profilt. Mindent később is módosíthatsz a Beállítások → Adatvédelem menüpontban.</value>
</data>
<data name="Wizard_Profile_PrivacyFirst_Heading" xml:space="preserve">
<value>Adatminimalizálás (ajánlott)</value>
<value>Adatminimalizálás</value>
</data>
<data name="Wizard_Profile_PrivacyFirst_Description" xml:space="preserve">
<value>Csak a saját beszélgetéseid tárolódnak: tellek, party, FC, linkshellек, cross-world linkshellек, szövetség és ExtraChat. A nyilvános chat, az NPC-párbeszédek és a rendszerüzenetek a tárolás szintjén eldobódnak. A megőrzés a spec-alapértelmezettet követi (tellek 365 nap, saját csatornák 90 nap).</value>
@@ -214,7 +214,13 @@
<value>Üdvözöl a Hellion Chat</value>
</data>
<data name="Wizard_Step1_Subtitle" xml:space="preserve">
<value>Egy Chat 2 fork a Hellion Forge-tól, adatvédelmet szem előtt tartó alapértelmezésekkel, egységes arculattal és néhány kényelmi funkcióval.</value>
<value>A te chatablakod a Hellion Forge-tól. Adatvédelem az első indítástól, 25 nyelven, és úgy rendezed be, ahogy szeretnéd.</value>
</data>
<data name="Wizard_Step1_Heritage" xml:space="preserve">
<value>A Hellion Chat a Chat 2 forkjaként indult. A kettő azóta annyira eltávolodott egymástól, hogy a kódbázisok már nem kompatibilisek.</value>
</data>
<data name="Wizard_Step1_PluginNotice" xml:space="preserve">
<value>A pluginek a Final Fantasy XIV-ben szürke zónát jelentenek: a Square Enix felhasználási feltételei nem terjednek ki rájuk, és Naoki Yoshida nyilvánosan kérte, hogy ne reklámozzák őket. Ne hozd tehát szóba a témát a Say, Yell, Shout és bármely más nyilvános csatornán.</value>
</data>
<data name="Wizard_Step1_Footer_Hint" xml:space="preserve">
<value>Három rövid lépés. Mindent megváltoztathatsz később a Beállítások → Hellion Chat menüpontban.</value>
@@ -228,8 +234,8 @@
<data name="Wizard_Step2_Title" xml:space="preserve">
<value>Mi tárolódjon?</value>
</data>
<data name="Wizard_Step2_RecommendedFooter" xml:space="preserve">
<value>★ = a legtöbb játékosnak ajánlott.</value>
<data name="Wizard_Profile_Recommended_Badge" xml:space="preserve">
<value>Ajánlott</value>
</data>
<data name="Wizard_Profile_Roleplay_Heading" xml:space="preserve">
<value>Roleplay</value>
@@ -298,7 +304,7 @@
<value>(változatlan)</value>
</data>
<data name="Wizard_Step4_TestHint" xml:space="preserve">
<value>💡 Próbáld ki: írj /tell &lt;Játékosnév&gt; a chatbe. A Hellion Chat automatikusan megnyit egy külön fület a beszélgetéshez, és előtölti az utolsó {0} üzenetet.</value>
<value>Próbáld ki: írj /tell &lt;Játékosnév&gt; a chatbe. A Hellion Chat automatikusan megnyit egy külön fület a beszélgetéshez, és előtölti az utolsó {0} üzenetet.</value>
</data>
<data name="Wizard_Step4_SettingsHint" xml:space="preserve">
<value>Beállítások → Hellion Chat a finomhangoláshoz</value>
@@ -605,6 +611,9 @@
<data name="Tabs_Presets_System" xml:space="preserve">
<value>Rendszer</value>
</data>
<data name="Tabs_Presets_Emote" xml:space="preserve">
<value>Emote-ok</value>
</data>
<data name="Tabs_Presets_FreeCompany" xml:space="preserve">
<value>Free Company</value>
</data>
+14 -5
View File
@@ -172,7 +172,7 @@
<value>Scegli un profilo di partenza. Puoi regolare tutto in seguito in Impostazioni → Privacy.</value>
</data>
<data name="Wizard_Profile_PrivacyFirst_Heading" xml:space="preserve">
<value>Minimizzazione dei dati (consigliata)</value>
<value>Minimizzazione dei dati</value>
</data>
<data name="Wizard_Profile_PrivacyFirst_Description" xml:space="preserve">
<value>Vengono salvate solo le tue conversazioni: tell, party, FC, linkshell, cross-world linkshell, alliance ed ExtraChat. La chat pubblica, i dialoghi NPC e lo spam di sistema vengono scartati a livello di archiviazione. La conservazione segue i valori predefiniti dello spec (tell 365 giorni, canali di conversazione propri 90 giorni).</value>
@@ -214,7 +214,13 @@
<value>Benvenuto in Hellion Chat</value>
</data>
<data name="Wizard_Step1_Subtitle" xml:space="preserve">
<value>Un fork di Chat 2 da Hellion Forge con impostazioni predefinite attente alla privacy, un'estetica coerente con il brand e qualche miglioramento alla qualità della vita.</value>
<value>La tua finestra di chat, firmata Hellion Forge. Privacy fin da subito, tradotta in 25 lingue, e la sistemi come vuoi.</value>
</data>
<data name="Wizard_Step1_Heritage" xml:space="preserve">
<value>Hellion Chat è nato come fork di Chat 2. Da allora i due si sono allontanati al punto che le basi di codice non sono più compatibili.</value>
</data>
<data name="Wizard_Step1_PluginNotice" xml:space="preserve">
<value>I plugin sono una zona grigia in Final Fantasy XIV: le condizioni d'uso di Square Enix non li contemplano, e Naoki Yoshida ha chiesto pubblicamente di non pubblicizzarli. Tieni quindi l'argomento fuori da Say, Yell, Shout e da ogni altro canale pubblico.</value>
</data>
<data name="Wizard_Step1_Footer_Hint" xml:space="preserve">
<value>Tre brevi passaggi. Puoi cambiare tutto in seguito in Impostazioni → Hellion Chat.</value>
@@ -228,8 +234,8 @@
<data name="Wizard_Step2_Title" xml:space="preserve">
<value>Cosa viene salvato?</value>
</data>
<data name="Wizard_Step2_RecommendedFooter" xml:space="preserve">
<value>★ = consigliato per la maggior parte dei giocatori.</value>
<data name="Wizard_Profile_Recommended_Badge" xml:space="preserve">
<value>Consigliato</value>
</data>
<data name="Wizard_Profile_Roleplay_Heading" xml:space="preserve">
<value>Roleplay</value>
@@ -298,7 +304,7 @@
<value>(invariato)</value>
</data>
<data name="Wizard_Step4_TestHint" xml:space="preserve">
<value>💡 Provalo: digita /tell &lt;Nome Giocatore&gt; in chat. Hellion Chat apre un tab dedicato alla conversazione e precarica gli ultimi {0} messaggi.</value>
<value>Provalo: digita /tell &lt;Nome Giocatore&gt; in chat. Hellion Chat apre un tab dedicato alla conversazione e precarica gli ultimi {0} messaggi.</value>
</data>
<data name="Wizard_Step4_SettingsHint" xml:space="preserve">
<value>Impostazioni → Hellion Chat per regolazioni successive</value>
@@ -605,6 +611,9 @@
<data name="Tabs_Presets_System" xml:space="preserve">
<value>Sistema</value>
</data>
<data name="Tabs_Presets_Emote" xml:space="preserve">
<value>Emote</value>
</data>
<data name="Tabs_Presets_FreeCompany" xml:space="preserve">
<value>Free Company</value>
</data>
+14 -5
View File
@@ -172,7 +172,7 @@
<value>開始プロファイルを選択してください。後で設定 → プライバシーからいつでも変更できます。</value>
</data>
<data name="Wizard_Profile_PrivacyFirst_Heading" xml:space="preserve">
<value>データ最小化(推奨)</value>
<value>データ最小化</value>
</data>
<data name="Wizard_Profile_PrivacyFirst_Description" xml:space="preserve">
<value>自分の会話のみ保存されます: テル、パーティ、フリーカンパニー、リンクシェル、クロスワールドリンクシェル、アライアンス、ExtraChat。パブリックチャット、NPCの台詞、システムスパムはストレージレベルで破棄されます。保持期間は仕様デフォルト(テル 365日、自分の会話チャンネル 90日)に従います。</value>
@@ -214,7 +214,13 @@
<value>Hellion Chat へようこそ</value>
</data>
<data name="Wizard_Step1_Subtitle" xml:space="preserve">
<value>Hellion Forge による Chat 2 フォーク。プライバシーに配慮したデフォルト設定、ブランド一貫のビジュアル、そしていくつかの利便性向上機能を備えています。</value>
<value>Hellion Forge がお届けするチャットウィンドウ。初期設定からプライバシー重視、25言語対応、レイアウトは自由に組み替えられます。</value>
</data>
<data name="Wizard_Step1_Heritage" xml:space="preserve">
<value>Hellion Chat は Chat 2 のフォークとして始まりました。その後、両者は大きく分かれ、コードベースはすでに互換性がありません。</value>
</data>
<data name="Wizard_Step1_PluginNotice" xml:space="preserve">
<value>ファイナルファンタジーXIVにおいてプラグインはグレーゾーンです。スクウェア・エニックスの利用規約は対象としておらず、吉田直樹氏も公の場で宣伝しないよう求めています。Say、Yell、Shout をはじめとする公開チャンネルでは話題にしないでください。</value>
</data>
<data name="Wizard_Step1_Footer_Hint" xml:space="preserve">
<value>3つの短いステップです。後で設定 → Hellion Chat からすべて変更できます。</value>
@@ -228,8 +234,8 @@
<data name="Wizard_Step2_Title" xml:space="preserve">
<value>何が保存されますか?</value>
</data>
<data name="Wizard_Step2_RecommendedFooter" xml:space="preserve">
<value>★ = ほとんどのプレイヤーに推奨。</value>
<data name="Wizard_Profile_Recommended_Badge" xml:space="preserve">
<value>推奨</value>
</data>
<data name="Wizard_Profile_Roleplay_Heading" xml:space="preserve">
<value>ロールプレイ</value>
@@ -298,7 +304,7 @@
<value>(変更なし)</value>
</data>
<data name="Wizard_Step4_TestHint" xml:space="preserve">
<value>💡 試してみましょう: チャットに /tell &lt;プレイヤー名&gt; と入力してください。Hellion Chat が会話専用のタブを自動で開き、最新 {0} 件のメッセージをプリロードします。</value>
<value>試してみましょう: チャットに /tell &lt;プレイヤー名&gt; と入力してください。Hellion Chat が会話専用のタブを自動で開き、最新 {0} 件のメッセージをプリロードします。</value>
</data>
<data name="Wizard_Step4_SettingsHint" xml:space="preserve">
<value>設定 → Hellion Chat で後から細かく調整できます</value>
@@ -605,6 +611,9 @@
<data name="Tabs_Presets_System" xml:space="preserve">
<value>システム</value>
</data>
<data name="Tabs_Presets_Emote" xml:space="preserve">
<value>エモート</value>
</data>
<data name="Tabs_Presets_FreeCompany" xml:space="preserve">
<value>フリーカンパニー</value>
</data>
+14 -5
View File
@@ -172,7 +172,7 @@
<value>시작 프로필을 선택하세요. 나중에 설정 → 개인정보에서 모두 조정할 수 있습니다.</value>
</data>
<data name="Wizard_Profile_PrivacyFirst_Heading" xml:space="preserve">
<value>데이터 최소화 (권장)</value>
<value>데이터 최소화</value>
</data>
<data name="Wizard_Profile_PrivacyFirst_Description" xml:space="preserve">
<value>자신의 대화만 저장됩니다. 귓속말, 파티, 자유부대, 링크셸, 서버 초월 링크셸, 연합 파티, ExtraChat이 포함됩니다. 공개 채팅, NPC 대화, 시스템 스팸은 저장 단계에서 제외됩니다. 보존 기간은 기본 스펙을 따릅니다 (귓속말 365일, 개인 대화 채널 90일).</value>
@@ -214,7 +214,13 @@
<value>Hellion Chat에 오신 것을 환영합니다</value>
</data>
<data name="Wizard_Step1_Subtitle" xml:space="preserve">
<value>Hellion Forge에서 만든 Chat 2 포크입니다. 개인정보 보호 기본값, 브랜드 일관성 있는 디자인, 그리고 몇 가지 편의 기능을 제공합니다.</value>
<value>Hellion Forge가 만든 채팅 창입니다. 기본값부터 개인정보 우선, 25개 언어 지원, 배치는 원하는 대로.</value>
</data>
<data name="Wizard_Step1_Heritage" xml:space="preserve">
<value>Hellion Chat은 Chat 2의 포크로 시작했습니다. 그 뒤로 둘은 코드베이스가 더 이상 호환되지 않을 만큼 멀어졌습니다.</value>
</data>
<data name="Wizard_Step1_PluginNotice" xml:space="preserve">
<value>파이널 판타지 XIV에서 플러그인은 회색 지대입니다. 스퀘어 에닉스 이용약관은 이를 다루지 않으며, 요시다 나오키는 공개적으로 홍보하지 말아 달라고 요청했습니다. 말하기, 떠들기, 외치기를 비롯한 모든 공개 채널에서는 이 주제를 꺼내지 마세요.</value>
</data>
<data name="Wizard_Step1_Footer_Hint" xml:space="preserve">
<value>세 가지 간단한 단계입니다. 나중에 설정 → Hellion Chat에서 모두 변경할 수 있습니다.</value>
@@ -228,8 +234,8 @@
<data name="Wizard_Step2_Title" xml:space="preserve">
<value>무엇을 저장할까요?</value>
</data>
<data name="Wizard_Step2_RecommendedFooter" xml:space="preserve">
<value>★ = 대부분의 플레이어에게 권장됩니다.</value>
<data name="Wizard_Profile_Recommended_Badge" xml:space="preserve">
<value>권장</value>
</data>
<data name="Wizard_Profile_Roleplay_Heading" xml:space="preserve">
<value>롤플레이</value>
@@ -298,7 +304,7 @@
<value>(변경 없음)</value>
</data>
<data name="Wizard_Step4_TestHint" xml:space="preserve">
<value>💡 테스트해보세요. 채팅창에 /tell &lt;Player Name&gt;을 입력하면 Hellion Chat이 대화를 위한 전용 탭을 열고 마지막 {0}개의 메시지를 미리 불러옵니다.</value>
<value>테스트해보세요. 채팅창에 /tell &lt;Player Name&gt;을 입력하면 Hellion Chat이 대화를 위한 전용 탭을 열고 마지막 {0}개의 메시지를 미리 불러옵니다.</value>
</data>
<data name="Wizard_Step4_SettingsHint" xml:space="preserve">
<value>나중에 세부 조정은 설정 → Hellion Chat에서</value>
@@ -605,6 +611,9 @@
<data name="Tabs_Presets_System" xml:space="preserve">
<value>시스템</value>
</data>
<data name="Tabs_Presets_Emote" xml:space="preserve">
<value>감정 표현</value>
</data>
<data name="Tabs_Presets_FreeCompany" xml:space="preserve">
<value>자유부대</value>
</data>
+14 -5
View File
@@ -172,7 +172,7 @@
<value>Velg en startprofil. Du kan justere alt senere under Innstillinger → Personvern.</value>
</data>
<data name="Wizard_Profile_PrivacyFirst_Heading" xml:space="preserve">
<value>Dataminimering (anbefalt)</value>
<value>Dataminimering</value>
</data>
<data name="Wizard_Profile_PrivacyFirst_Description" xml:space="preserve">
<value>Bare dine egne samtaler lagres: tells, party, FC, linkshells, cross-world linkshells, alliance og ExtraChat. Offentlig chat, NPC-dialoger og systemspam forkastes på lagringsnivå. Oppbevaring følger spec-standarder (tells 365 dager, egne samtalekanaler 90 dager).</value>
@@ -214,7 +214,13 @@
<value>Velkommen til Hellion Chat</value>
</data>
<data name="Wizard_Step1_Subtitle" xml:space="preserve">
<value>En Chat 2-fork fra Hellion Forge med personvernvennlige standarder, merkevarekonsekvente visuals og noen livskvalitetsforbedringer.</value>
<value>Chatvinduet ditt fra Hellion Forge. Personvern fra første start, 25 språk, og du setter det opp slik du vil.</value>
</data>
<data name="Wizard_Step1_Heritage" xml:space="preserve">
<value>Hellion Chat startet som en fork av Chat 2. De to har siden fjernet seg så mye fra hverandre at kodebasene ikke lenger er kompatible.</value>
</data>
<data name="Wizard_Step1_PluginNotice" xml:space="preserve">
<value>Plugins er en gråsone i Final Fantasy XIV: Square Enix' bruksvilkår dekker dem ikke, og Naoki Yoshida har offentlig bedt om at man ikke reklamerer for dem. Hold derfor temaet unna Say, Yell, Shout og alle andre offentlige kanaler.</value>
</data>
<data name="Wizard_Step1_Footer_Hint" xml:space="preserve">
<value>Tre korte steg. Du kan endre alt senere under Innstillinger → Hellion Chat.</value>
@@ -228,8 +234,8 @@
<data name="Wizard_Step2_Title" xml:space="preserve">
<value>Hva blir lagret?</value>
</data>
<data name="Wizard_Step2_RecommendedFooter" xml:space="preserve">
<value>★ = anbefalt for de fleste spillere.</value>
<data name="Wizard_Profile_Recommended_Badge" xml:space="preserve">
<value>Anbefalt</value>
</data>
<data name="Wizard_Profile_Roleplay_Heading" xml:space="preserve">
<value>Roleplay</value>
@@ -298,7 +304,7 @@
<value>(uendret)</value>
</data>
<data name="Wizard_Step4_TestHint" xml:space="preserve">
<value>💡 Prøv det: skriv /tell &lt;Spillernavn&gt; i chatten. Hellion Chat åpner en dedikert fane for samtalen og forhåndslaster de siste {0} meldingene.</value>
<value>Prøv det: skriv /tell &lt;Spillernavn&gt; i chatten. Hellion Chat åpner en dedikert fane for samtalen og forhåndslaster de siste {0} meldingene.</value>
</data>
<data name="Wizard_Step4_SettingsHint" xml:space="preserve">
<value>Innstillinger → Hellion Chat for å finjustere senere</value>
@@ -605,6 +611,9 @@
<data name="Tabs_Presets_System" xml:space="preserve">
<value>System</value>
</data>
<data name="Tabs_Presets_Emote" xml:space="preserve">
<value>Emotes</value>
</data>
<data name="Tabs_Presets_FreeCompany" xml:space="preserve">
<value>Free Company</value>
</data>
+14 -5
View File
@@ -172,7 +172,7 @@
<value>Kies een startprofiel. Je kunt later alles aanpassen via Instellingen → Privacy.</value>
</data>
<data name="Wizard_Profile_PrivacyFirst_Heading" xml:space="preserve">
<value>Dataminimalisatie (aanbevolen)</value>
<value>Dataminimalisatie</value>
</data>
<data name="Wizard_Profile_PrivacyFirst_Description" xml:space="preserve">
<value>Alleen je eigen gesprekken worden opgeslagen: tells, groep, FC, linkshells, cross-world linkshells, alliantie en ExtraChat. Openbare chat, NPC-dialogen en systeemspam worden op opslagniveau verwijderd. Retentie volgt spec-standaarden (tells 365 dagen, eigen gesprekkanalen 90 dagen).</value>
@@ -214,7 +214,13 @@
<value>Welkom bij Hellion Chat</value>
</data>
<data name="Wizard_Step1_Subtitle" xml:space="preserve">
<value>Een Chat 2 fork van Hellion Forge met privacybewuste standaarden, merkconforme uitstraling en handige quality-of-life verbeteringen.</value>
<value>Jouw chatvenster van Hellion Forge. Privacy vanaf de eerste start, in 25 talen, en je richt het in zoals jij wilt.</value>
</data>
<data name="Wizard_Step1_Heritage" xml:space="preserve">
<value>Hellion Chat begon als een fork van Chat 2. De twee zijn sindsdien zo ver uit elkaar gegroeid dat de codebases niet meer compatibel zijn.</value>
</data>
<data name="Wizard_Step1_PluginNotice" xml:space="preserve">
<value>Plugins zijn in Final Fantasy XIV een grijs gebied: de gebruiksvoorwaarden van Square Enix dekken ze niet, en Naoki Yoshida heeft publiekelijk gevraagd er geen reclame voor te maken. Houd het onderwerp dus buiten Zeg, Roep, Schreeuw en elk ander openbaar kanaal.</value>
</data>
<data name="Wizard_Step1_Footer_Hint" xml:space="preserve">
<value>Drie korte stappen. Je kunt later alles aanpassen via Instellingen → Hellion Chat.</value>
@@ -228,8 +234,8 @@
<data name="Wizard_Step2_Title" xml:space="preserve">
<value>Wat wordt er opgeslagen?</value>
</data>
<data name="Wizard_Step2_RecommendedFooter" xml:space="preserve">
<value>★ = aanbevolen voor de meeste spelers.</value>
<data name="Wizard_Profile_Recommended_Badge" xml:space="preserve">
<value>Aanbevolen</value>
</data>
<data name="Wizard_Profile_Roleplay_Heading" xml:space="preserve">
<value>Roleplay</value>
@@ -298,7 +304,7 @@
<value>(ongewijzigd)</value>
</data>
<data name="Wizard_Step4_TestHint" xml:space="preserve">
<value>💡 Probeer het uit: typ /tell &lt;Spelernaam&gt; in de chat. Hellion Chat opent een eigen tabblad voor het gesprek en laadt de laatste {0} berichten vooraf.</value>
<value>Probeer het uit: typ /tell &lt;Spelernaam&gt; in de chat. Hellion Chat opent een eigen tabblad voor het gesprek en laadt de laatste {0} berichten vooraf.</value>
</data>
<data name="Wizard_Step4_SettingsHint" xml:space="preserve">
<value>Instellingen → Hellion Chat om later te verfijnen</value>
@@ -605,6 +611,9 @@
<data name="Tabs_Presets_System" xml:space="preserve">
<value>Systeem</value>
</data>
<data name="Tabs_Presets_Emote" xml:space="preserve">
<value>Emotes</value>
</data>
<data name="Tabs_Presets_FreeCompany" xml:space="preserve">
<value>Free Company</value>
</data>
+14 -5
View File
@@ -172,7 +172,7 @@
<value>Wybierz profil startowy. Wszystko możesz zmienić później w Ustawieniach → Prywatność.</value>
</data>
<data name="Wizard_Profile_PrivacyFirst_Heading" xml:space="preserve">
<value>Minimalizacja danych (zalecane)</value>
<value>Minimalizacja danych</value>
</data>
<data name="Wizard_Profile_PrivacyFirst_Description" xml:space="preserve">
<value>Przechowywane są tylko twoje własne rozmowy: tells, grupa, FC, linkshells, cross-world linkshells, sojusz i ExtraChat. Czat publiczny, dialogi NPC i spam systemowy są odrzucane na poziomie zapisu. Czas przechowywania według domyślnych wartości specyfikacji (tells 365 dni, własne kanały rozmów 90 dni).</value>
@@ -214,7 +214,13 @@
<value>Witaj w Hellion Chat</value>
</data>
<data name="Wizard_Step1_Subtitle" xml:space="preserve">
<value>Fork Chat 2 od Hellion Forge z domyślnymi ustawieniami chroniącymi prywatność, spójną identyfikacją wizualną i drobnymi usprawnieniami komfortu gry.</value>
<value>Twoje okno czatu od Hellion Forge. Prywatność od pierwszego uruchomienia, 25 języków, a układ ustawiasz sam.</value>
</data>
<data name="Wizard_Step1_Heritage" xml:space="preserve">
<value>Hellion Chat powstał jako fork Chat 2. Od tamtej pory oba projekty rozeszły się na tyle, że ich bazy kodu nie są już zgodne.</value>
</data>
<data name="Wizard_Step1_PluginNotice" xml:space="preserve">
<value>Pluginy to w Final Fantasy XIV szara strefa: warunki korzystania Square Enix ich nie obejmują, a Naoki Yoshida publicznie prosił, by ich nie reklamować. Nie poruszaj więc tego tematu na Say, Yell, Shout ani na żadnym innym kanale publicznym.</value>
</data>
<data name="Wizard_Step1_Footer_Hint" xml:space="preserve">
<value>Trzy krótkie kroki. Wszystko możesz zmienić później w Ustawieniach → Hellion Chat.</value>
@@ -228,8 +234,8 @@
<data name="Wizard_Step2_Title" xml:space="preserve">
<value>Co ma być zapisywane?</value>
</data>
<data name="Wizard_Step2_RecommendedFooter" xml:space="preserve">
<value>★ = zalecane dla większości graczy.</value>
<data name="Wizard_Profile_Recommended_Badge" xml:space="preserve">
<value>Zalecane</value>
</data>
<data name="Wizard_Profile_Roleplay_Heading" xml:space="preserve">
<value>Roleplay</value>
@@ -298,7 +304,7 @@
<value>(bez zmian)</value>
</data>
<data name="Wizard_Step4_TestHint" xml:space="preserve">
<value>💡 Wypróbuj: wpisz /tell &lt;Nazwa gracza&gt; w czacie. Hellion Chat otworzy dedykowaną zakładkę dla rozmowy i wstępnie wczyta ostatnie {0} wiadomości.</value>
<value>Wypróbuj: wpisz /tell &lt;Nazwa gracza&gt; w czacie. Hellion Chat otworzy dedykowaną zakładkę dla rozmowy i wstępnie wczyta ostatnie {0} wiadomości.</value>
</data>
<data name="Wizard_Step4_SettingsHint" xml:space="preserve">
<value>Ustawienia → Hellion Chat, aby dostosować później</value>
@@ -605,6 +611,9 @@
<data name="Tabs_Presets_System" xml:space="preserve">
<value>System</value>
</data>
<data name="Tabs_Presets_Emote" xml:space="preserve">
<value>Emoty</value>
</data>
<data name="Tabs_Presets_FreeCompany" xml:space="preserve">
<value>Free Company</value>
</data>
@@ -172,7 +172,7 @@
<value>Escolha um perfil inicial. Você pode ajustar tudo depois em Configurações → Privacidade.</value>
</data>
<data name="Wizard_Profile_PrivacyFirst_Heading" xml:space="preserve">
<value>Minimização de dados (recomendado)</value>
<value>Minimização de dados</value>
</data>
<data name="Wizard_Profile_PrivacyFirst_Description" xml:space="preserve">
<value>Apenas suas próprias conversas são armazenadas: tells, grupo, FC, linkshells, cross-world linkshells, aliança e ExtraChat. Bate-papo público, diálogos de NPC e spam de sistema são descartados no nível de armazenamento. A retenção segue os padrões da spec (tells 365 dias, canais de conversa próprios 90 dias).</value>
@@ -214,7 +214,13 @@
<value>Bem-vindo ao Hellion Chat</value>
</data>
<data name="Wizard_Step1_Subtitle" xml:space="preserve">
<value>Um fork do Chat 2 pela Hellion Forge com padrões voltados para privacidade, visual consistente com a marca e alguns toques de qualidade de vida.</value>
<value>Sua janela de chat, da Hellion Forge. Privacidade desde o primeiro uso, 25 idiomas, e você organiza do seu jeito.</value>
</data>
<data name="Wizard_Step1_Heritage" xml:space="preserve">
<value>O Hellion Chat começou como um fork do Chat 2. Desde então os dois se afastaram a ponto de as bases de código não serem mais compatíveis.</value>
</data>
<data name="Wizard_Step1_PluginNotice" xml:space="preserve">
<value>Plugins são uma zona cinzenta no Final Fantasy XIV: os termos de uso da Square Enix não os cobrem, e Naoki Yoshida pediu publicamente que não sejam divulgados. Mantenha o assunto fora de Falar, Grita, Berrar e de qualquer outro canal público.</value>
</data>
<data name="Wizard_Step1_Footer_Hint" xml:space="preserve">
<value>Três passos rápidos. Você pode mudar tudo depois em Configurações → Hellion Chat.</value>
@@ -228,8 +234,8 @@
<data name="Wizard_Step2_Title" xml:space="preserve">
<value>O que será armazenado?</value>
</data>
<data name="Wizard_Step2_RecommendedFooter" xml:space="preserve">
<value>★ = recomendado para a maioria dos jogadores.</value>
<data name="Wizard_Profile_Recommended_Badge" xml:space="preserve">
<value>Recomendado</value>
</data>
<data name="Wizard_Profile_Roleplay_Heading" xml:space="preserve">
<value>Roleplay</value>
@@ -298,7 +304,7 @@
<value>(sem alteração)</value>
</data>
<data name="Wizard_Step4_TestHint" xml:space="preserve">
<value>💡 Experimente: digite /tell &lt;Nome do Jogador&gt; no chat. O Hellion Chat abre uma aba dedicada para a conversa e pré-carrega as últimas {0} mensagens.</value>
<value>Experimente: digite /tell &lt;Nome do Jogador&gt; no chat. O Hellion Chat abre uma aba dedicada para a conversa e pré-carrega as últimas {0} mensagens.</value>
</data>
<data name="Wizard_Step4_SettingsHint" xml:space="preserve">
<value>Configurações → Hellion Chat para ajustar depois</value>
@@ -605,6 +611,9 @@
<data name="Tabs_Presets_System" xml:space="preserve">
<value>Sistema</value>
</data>
<data name="Tabs_Presets_Emote" xml:space="preserve">
<value>Emotes</value>
</data>
<data name="Tabs_Presets_FreeCompany" xml:space="preserve">
<value>Free Company</value>
</data>
@@ -172,7 +172,7 @@
<value>Escolhe um perfil inicial. Podes ajustar tudo depois em Definições → Privacidade.</value>
</data>
<data name="Wizard_Profile_PrivacyFirst_Heading" xml:space="preserve">
<value>Minimização de dados (recomendado)</value>
<value>Minimização de dados</value>
</data>
<data name="Wizard_Profile_PrivacyFirst_Description" xml:space="preserve">
<value>Só as tuas próprias conversas são armazenadas: tells, grupo, FC, linkshells, cross-world linkshells, aliança e ExtraChat. O chat público, os diálogos de NPC e o spam de sistema são descartados ao nível do armazenamento. A retenção segue os valores predefinidos da spec (tells 365 dias, canais de conversas próprias 90 dias).</value>
@@ -214,7 +214,13 @@
<value>Bem-vindo ao Hellion Chat</value>
</data>
<data name="Wizard_Step1_Subtitle" xml:space="preserve">
<value>Um fork do Chat 2 da Hellion Forge com predefinições com privacidade em mente, visuais consistentes com a marca e alguns retoques de qualidade de vida.</value>
<value>A tua janela de conversa, da Hellion Forge. Privacidade desde o primeiro arranque, 25 idiomas, e organizas tudo como quiseres.</value>
</data>
<data name="Wizard_Step1_Heritage" xml:space="preserve">
<value>O Hellion Chat começou como um fork do Chat 2. Desde então os dois afastaram-se ao ponto de as bases de código já não serem compatíveis.</value>
</data>
<data name="Wizard_Step1_PluginNotice" xml:space="preserve">
<value>Os plugins são uma zona cinzenta no Final Fantasy XIV: os termos de utilização da Square Enix não os abrangem e Naoki Yoshida pediu publicamente que não fossem promovidos. Mantém, portanto, o assunto fora de Say, Yell, Shout e de qualquer outro canal público.</value>
</data>
<data name="Wizard_Step1_Footer_Hint" xml:space="preserve">
<value>Três passos rápidos. Podes mudar tudo depois em Definições → Hellion Chat.</value>
@@ -228,8 +234,8 @@
<data name="Wizard_Step2_Title" xml:space="preserve">
<value>O que fica armazenado?</value>
</data>
<data name="Wizard_Step2_RecommendedFooter" xml:space="preserve">
<value>★ = recomendado para a maioria dos jogadores.</value>
<data name="Wizard_Profile_Recommended_Badge" xml:space="preserve">
<value>Recomendado</value>
</data>
<data name="Wizard_Profile_Roleplay_Heading" xml:space="preserve">
<value>Roleplay</value>
@@ -298,7 +304,7 @@
<value>(sem alterações)</value>
</data>
<data name="Wizard_Step4_TestHint" xml:space="preserve">
<value>💡 Experimenta: escreve /tell &lt;Nome do Jogador&gt; no chat. O Hellion Chat abre um separador dedicado para a conversa e pré-carrega as últimas {0} mensagens.</value>
<value>Experimenta: escreve /tell &lt;Nome do Jogador&gt; no chat. O Hellion Chat abre um separador dedicado para a conversa e pré-carrega as últimas {0} mensagens.</value>
</data>
<data name="Wizard_Step4_SettingsHint" xml:space="preserve">
<value>Definições → Hellion Chat para ajustar mais tarde</value>
@@ -605,6 +611,9 @@
<data name="Tabs_Presets_System" xml:space="preserve">
<value>Sistema</value>
</data>
<data name="Tabs_Presets_Emote" xml:space="preserve">
<value>Emotes</value>
</data>
<data name="Tabs_Presets_FreeCompany" xml:space="preserve">
<value>Free Company</value>
</data>
+14 -5
View File
@@ -172,7 +172,7 @@
<value>Choose a starting profile. You can adjust everything later under Settings → Privacy.</value>
</data>
<data name="Wizard_Profile_PrivacyFirst_Heading" xml:space="preserve">
<value>Data minimisation (recommended)</value>
<value>Data minimisation</value>
</data>
<data name="Wizard_Profile_PrivacyFirst_Description" xml:space="preserve">
<value>Only your own conversations are stored: tells, party, FC, linkshells, cross-world linkshells, alliance, and ExtraChat. Public chat, NPC dialogues, and system spam are discarded at the storage level. Retention follows spec defaults (tells 365 days, own conversation channels 90 days).</value>
@@ -214,7 +214,13 @@
<value>Welcome to Hellion Chat</value>
</data>
<data name="Wizard_Step1_Subtitle" xml:space="preserve">
<value>A Chat 2 fork from Hellion Forge with privacy-aware defaults, brand-consistent visuals, and a few quality-of-life touches.</value>
<value>Your chat window, from Hellion Forge. Privacy-first out of the box, translated into 25 languages, and yours to arrange.</value>
</data>
<data name="Wizard_Step1_PluginNotice" xml:space="preserve">
<value>Plugins are a grey area in Final Fantasy XIV: Square Enix's terms of service do not cover them, and Naoki Yoshida has publicly asked that people not advertise them. Keep the subject out of Say, Yell, Shout and every other public channel.</value>
</data>
<data name="Wizard_Step1_Heritage" xml:space="preserve">
<value>Hellion Chat started out as a fork of Chat 2. The two have drifted far enough apart since that the codebases are no longer compatible.</value>
</data>
<data name="Wizard_Step1_Footer_Hint" xml:space="preserve">
<value>Three short steps. You can change everything later under Settings → Hellion Chat.</value>
@@ -228,8 +234,8 @@
<data name="Wizard_Step2_Title" xml:space="preserve">
<value>What gets stored?</value>
</data>
<data name="Wizard_Step2_RecommendedFooter" xml:space="preserve">
<value>★ = recommended for most players.</value>
<data name="Wizard_Profile_Recommended_Badge" xml:space="preserve">
<value>Recommended</value>
</data>
<data name="Wizard_Profile_Roleplay_Heading" xml:space="preserve">
<value>Roleplay</value>
@@ -298,7 +304,7 @@
<value>(unchanged)</value>
</data>
<data name="Wizard_Step4_TestHint" xml:space="preserve">
<value>💡 Try it: type /tell &lt;Player Name&gt; into chat. Hellion Chat opens a dedicated tab for the conversation and preloads the last {0} messages.</value>
<value>Try it: type /tell &lt;Player Name&gt; into chat. Hellion Chat opens a dedicated tab for the conversation and preloads the last {0} messages.</value>
</data>
<data name="Wizard_Step4_SettingsHint" xml:space="preserve">
<value>Settings → Hellion Chat to fine-tune later</value>
@@ -598,6 +604,9 @@
<data name="Tabs_Presets_System" xml:space="preserve">
<value>System</value>
</data>
<data name="Tabs_Presets_Emote" xml:space="preserve">
<value>Emotes</value>
</data>
<data name="Tabs_Presets_FreeCompany" xml:space="preserve">
<value>Free Company</value>
</data>
+14 -5
View File
@@ -172,7 +172,7 @@
<value>Alege un profil de start. Poți ajusta orice mai târziu din Setări → Confidențialitate.</value>
</data>
<data name="Wizard_Profile_PrivacyFirst_Heading" xml:space="preserve">
<value>Minimizarea datelor (recomandat)</value>
<value>Minimizarea datelor</value>
</data>
<data name="Wizard_Profile_PrivacyFirst_Description" xml:space="preserve">
<value>Sunt stocate doar propriile tale conversații: tells, party, FC, linkshells, cross-world linkshells, alliance și ExtraChat. Chatul public, dialogurile NPC și spam-ul de sistem sunt respinse la nivelul stocării. Retenția urmează implicite spec (tells 365 zile, canale de conversație proprii 90 zile).</value>
@@ -214,7 +214,13 @@
<value>Bun venit în Hellion Chat</value>
</data>
<data name="Wizard_Step1_Subtitle" xml:space="preserve">
<value>Un fork Chat 2 de la Hellion Forge cu setări implicite orientate spre confidențialitate, aspect consecvent cu brandul și câteva îmbunătățiri de calitate a vieții.</value>
<value>Fereastra ta de chat, de la Hellion Forge. Confidențialitate din start, 25 de limbi, iar aranjarea îți aparține.</value>
</data>
<data name="Wizard_Step1_Heritage" xml:space="preserve">
<value>Hellion Chat a pornit ca un fork al Chat 2. De atunci cele două s-au îndepărtat suficient încât bazele de cod nu mai sunt compatibile.</value>
</data>
<data name="Wizard_Step1_PluginNotice" xml:space="preserve">
<value>Pluginurile sunt o zonă gri în Final Fantasy XIV: termenii de utilizare Square Enix nu le acoperă, iar Naoki Yoshida a cerut public să nu fie promovate. Ține deci subiectul departe de Say, Yell, Shout și de orice alt canal public.</value>
</data>
<data name="Wizard_Step1_Footer_Hint" xml:space="preserve">
<value>Trei pași scurți. Poți schimba orice mai târziu din Setări → Hellion Chat.</value>
@@ -228,8 +234,8 @@
<data name="Wizard_Step2_Title" xml:space="preserve">
<value>Ce se stochează?</value>
</data>
<data name="Wizard_Step2_RecommendedFooter" xml:space="preserve">
<value>★ = recomandat pentru cei mai mulți jucători.</value>
<data name="Wizard_Profile_Recommended_Badge" xml:space="preserve">
<value>Recomandat</value>
</data>
<data name="Wizard_Profile_Roleplay_Heading" xml:space="preserve">
<value>Roleplay</value>
@@ -298,7 +304,7 @@
<value>(nemodificat)</value>
</data>
<data name="Wizard_Step4_TestHint" xml:space="preserve">
<value>💡 Încearcă: tastează /tell &lt;Nume Jucător&gt; în chat. Hellion Chat deschide un tab dedicat pentru conversație și preîncarcă ultimele {0} mesaje.</value>
<value>Încearcă: tastează /tell &lt;Nume Jucător&gt; în chat. Hellion Chat deschide un tab dedicat pentru conversație și preîncarcă ultimele {0} mesaje.</value>
</data>
<data name="Wizard_Step4_SettingsHint" xml:space="preserve">
<value>Setări → Hellion Chat pentru ajustări ulterioare</value>
@@ -605,6 +611,9 @@
<data name="Tabs_Presets_System" xml:space="preserve">
<value>System</value>
</data>
<data name="Tabs_Presets_Emote" xml:space="preserve">
<value>Emotes</value>
</data>
<data name="Tabs_Presets_FreeCompany" xml:space="preserve">
<value>Free Company</value>
</data>
+14 -5
View File
@@ -172,7 +172,7 @@
<value>Выберите начальный профиль. Всё можно изменить позже в разделе Настройки → Конфиденциальность.</value>
</data>
<data name="Wizard_Profile_PrivacyFirst_Heading" xml:space="preserve">
<value>Минимизация данных (рекомендуется)</value>
<value>Минимизация данных</value>
</data>
<data name="Wizard_Profile_PrivacyFirst_Description" xml:space="preserve">
<value>Сохраняются только ваши собственные разговоры: ЛС, группа, свободная компания, Linkshells, межмировые Linkshells, альянс и ExtraChat. Публичный чат, диалоги NPC и системный спам отбрасываются на уровне хранения. Сроки хранения соответствуют значениям по умолчанию (ЛС — 365 дней, собственные каналы разговора — 90 дней).</value>
@@ -214,7 +214,13 @@
<value>Добро пожаловать в Hellion Chat</value>
</data>
<data name="Wizard_Step1_Subtitle" xml:space="preserve">
<value>Форк Chat 2 от Hellion Forge с настройками конфиденциальности по умолчанию, фирменным оформлением и рядом улучшений удобства использования.</value>
<value>Твоё окно чата от Hellion Forge. Приватность по умолчанию, 25 языков, и раскладка полностью в твоих руках.</value>
</data>
<data name="Wizard_Step1_Heritage" xml:space="preserve">
<value>Hellion Chat начинался как форк Chat 2. С тех пор проекты разошлись настолько, что кодовые базы больше не совместимы.</value>
</data>
<data name="Wizard_Step1_PluginNotice" xml:space="preserve">
<value>Плагины в Final Fantasy XIV находятся в серой зоне: условия использования Square Enix их не охватывают, а Наоки Ёсида публично просил не рекламировать их. Не поднимай эту тему в каналах Сказать, Вопль, Крик и любых других публичных.</value>
</data>
<data name="Wizard_Step1_Footer_Hint" xml:space="preserve">
<value>Три коротких шага. Всё можно изменить позже в разделе Настройки → Hellion Chat.</value>
@@ -228,8 +234,8 @@
<data name="Wizard_Step2_Title" xml:space="preserve">
<value>Что будет сохраняться?</value>
</data>
<data name="Wizard_Step2_RecommendedFooter" xml:space="preserve">
<value>★ = рекомендуется для большинства игроков.</value>
<data name="Wizard_Profile_Recommended_Badge" xml:space="preserve">
<value>Рекомендовано</value>
</data>
<data name="Wizard_Profile_Roleplay_Heading" xml:space="preserve">
<value>Roleplay</value>
@@ -298,7 +304,7 @@
<value>(без изменений)</value>
</data>
<data name="Wizard_Step4_TestHint" xml:space="preserve">
<value>💡 Попробуйте: введите /tell &lt;Имя игрока&gt; в чате. Hellion Chat откроет отдельную вкладку для разговора и предзагрузит последние {0} сообщений.</value>
<value>Попробуйте: введите /tell &lt;Имя игрока&gt; в чате. Hellion Chat откроет отдельную вкладку для разговора и предзагрузит последние {0} сообщений.</value>
</data>
<data name="Wizard_Step4_SettingsHint" xml:space="preserve">
<value>Настройки → Hellion Chat для тонкой настройки позже</value>
@@ -605,6 +611,9 @@
<data name="Tabs_Presets_System" xml:space="preserve">
<value>Система</value>
</data>
<data name="Tabs_Presets_Emote" xml:space="preserve">
<value>Эмоции</value>
</data>
<data name="Tabs_Presets_FreeCompany" xml:space="preserve">
<value>Свободная компания</value>
</data>
+14 -5
View File
@@ -172,7 +172,7 @@
<value>Välj en startprofil. Du kan justera allt senare under Inställningar → Sekretess.</value>
</data>
<data name="Wizard_Profile_PrivacyFirst_Heading" xml:space="preserve">
<value>Dataminimering (rekommenderas)</value>
<value>Dataminimering</value>
</data>
<data name="Wizard_Profile_PrivacyFirst_Description" xml:space="preserve">
<value>Bara dina egna konversationer sparas: tells, grupp, FC, linkshells, flervärlds-linkshells, allians och ExtraChat. Offentlig chatt, NPC-dialoger och systemskräp kasseras på lagringsnivå. Lagring följer spec-standarder (tells 365 dagar, egna konversationskanaler 90 dagar).</value>
@@ -214,7 +214,13 @@
<value>Välkommen till Hellion Chat</value>
</data>
<data name="Wizard_Step1_Subtitle" xml:space="preserve">
<value>En Chat 2-fork från Hellion Forge med sekretessmedvetna standardinställningar, konsekvent varumärkesutseende och några livskvalitetsförbättringar.</value>
<value>Ditt chattfönster från Hellion Forge. Integritet från första start, 25 språk, och du ställer in det som du vill.</value>
</data>
<data name="Wizard_Step1_Heritage" xml:space="preserve">
<value>Hellion Chat började som en fork av Chat 2. De två har sedan dess glidit isär så mycket att kodbaserna inte längre är kompatibla.</value>
</data>
<data name="Wizard_Step1_PluginNotice" xml:space="preserve">
<value>Plugins är en gråzon i Final Fantasy XIV: Square Enix användarvillkor täcker dem inte, och Naoki Yoshida har offentligt bett om att man inte gör reklam för dem. Håll därför ämnet borta från Säg, Skrik, Ropa och alla andra offentliga kanaler.</value>
</data>
<data name="Wizard_Step1_Footer_Hint" xml:space="preserve">
<value>Tre korta steg. Du kan ändra allt senare under Inställningar → Hellion Chat.</value>
@@ -228,8 +234,8 @@
<data name="Wizard_Step2_Title" xml:space="preserve">
<value>Vad sparas?</value>
</data>
<data name="Wizard_Step2_RecommendedFooter" xml:space="preserve">
<value>★ = rekommenderas för de flesta spelare.</value>
<data name="Wizard_Profile_Recommended_Badge" xml:space="preserve">
<value>Rekommenderad</value>
</data>
<data name="Wizard_Profile_Roleplay_Heading" xml:space="preserve">
<value>Roleplay</value>
@@ -298,7 +304,7 @@
<value>(oförändrat)</value>
</data>
<data name="Wizard_Step4_TestHint" xml:space="preserve">
<value>💡 Prova: skriv /tell &lt;Spelarnamn&gt; i chatten. Hellion Chat öppnar en dedikerad flik för konversationen och förladdar de senaste {0} meddelandena.</value>
<value>Prova: skriv /tell &lt;Spelarnamn&gt; i chatten. Hellion Chat öppnar en dedikerad flik för konversationen och förladdar de senaste {0} meddelandena.</value>
</data>
<data name="Wizard_Step4_SettingsHint" xml:space="preserve">
<value>Inställningar → Hellion Chat för att finjustera senare</value>
@@ -605,6 +611,9 @@
<data name="Tabs_Presets_System" xml:space="preserve">
<value>System</value>
</data>
<data name="Tabs_Presets_Emote" xml:space="preserve">
<value>Emotes</value>
</data>
<data name="Tabs_Presets_FreeCompany" xml:space="preserve">
<value>Free Company</value>
</data>
+14 -5
View File
@@ -172,7 +172,7 @@
<value>Bir başlangıç profili seç. Her şeyi daha sonra Ayarlar → Gizlilik altında değiştirebilirsin.</value>
</data>
<data name="Wizard_Profile_PrivacyFirst_Heading" xml:space="preserve">
<value>Veri minimizasyonu (önerilen)</value>
<value>Veri minimizasyonu</value>
</data>
<data name="Wizard_Profile_PrivacyFirst_Description" xml:space="preserve">
<value>Yalnızca kendi konuşmaların saklanır: tell'ler, parti, FC, linkshell'ler, cross-world linkshell'ler, alliance ve ExtraChat. Genel sohbet, NPC diyalogları ve sistem spam'i depolama düzeyinde atılır. Saklama süresi spec varsayılanlarına göre ayarlanır (tell'ler 365 gün, kendi konuşma kanalları 90 gün).</value>
@@ -214,7 +214,13 @@
<value>Hellion Chat'e hoş geldin</value>
</data>
<data name="Wizard_Step1_Subtitle" xml:space="preserve">
<value>Hellion Forge'dan gizlilik odaklı varsayılanlar, marka tutarlı görsel tasarım ve birkaç kullanım kolaylığı dokunuşuyla Chat 2'nin bir fork'u.</value>
<value>Hellion Forge'un sohbet penceresi. İlk açılıştan itibaren gizlilik öncelikli, 25 dilde, ve düzenini kendin kurarsın.</value>
</data>
<data name="Wizard_Step1_Heritage" xml:space="preserve">
<value>Hellion Chat, Chat 2'nin bir fork'u olarak başladı. İkisi o zamandan beri kod tabanları artık uyumlu olmayacak kadar birbirinden uzaklaştı.</value>
</data>
<data name="Wizard_Step1_PluginNotice" xml:space="preserve">
<value>Pluginler Final Fantasy XIV'te gri bir alandadır: Square Enix'in kullanım koşulları onları kapsamaz ve Naoki Yoshida bunların tanıtılmamasını açıkça rica etmiştir. Bu yüzden konuyu Say, Yell, Shout ve diğer tüm herkese açık kanalların dışında tut.</value>
</data>
<data name="Wizard_Step1_Footer_Hint" xml:space="preserve">
<value>Üç kısa adım. Her şeyi daha sonra Ayarlar → Hellion Chat altında değiştirebilirsin.</value>
@@ -228,8 +234,8 @@
<data name="Wizard_Step2_Title" xml:space="preserve">
<value>Ne saklanacak?</value>
</data>
<data name="Wizard_Step2_RecommendedFooter" xml:space="preserve">
<value>★ = çoğu oyuncu için önerilen.</value>
<data name="Wizard_Profile_Recommended_Badge" xml:space="preserve">
<value>Önerilen</value>
</data>
<data name="Wizard_Profile_Roleplay_Heading" xml:space="preserve">
<value>Roleplay</value>
@@ -298,7 +304,7 @@
<value>(değiştirilmedi)</value>
</data>
<data name="Wizard_Step4_TestHint" xml:space="preserve">
<value>💡 Dene: sohbete /tell &lt;Oyuncu Adı&gt; yaz. Hellion Chat konuşma için özel bir sekme açar ve son {0} mesajı önceden yükler.</value>
<value>Dene: sohbete /tell &lt;Oyuncu Adı&gt; yaz. Hellion Chat konuşma için özel bir sekme açar ve son {0} mesajı önceden yükler.</value>
</data>
<data name="Wizard_Step4_SettingsHint" xml:space="preserve">
<value>Daha sonra ince ayar için Ayarlar → Hellion Chat</value>
@@ -605,6 +611,9 @@
<data name="Tabs_Presets_System" xml:space="preserve">
<value>Sistem</value>
</data>
<data name="Tabs_Presets_Emote" xml:space="preserve">
<value>Emote'lar</value>
</data>
<data name="Tabs_Presets_FreeCompany" xml:space="preserve">
<value>Free Company</value>
</data>
+14 -5
View File
@@ -172,7 +172,7 @@
<value>Виберіть початковий профіль. Все можна налаштувати пізніше в розділі Налаштування → Конфіденційність.</value>
</data>
<data name="Wizard_Profile_PrivacyFirst_Heading" xml:space="preserve">
<value>Мінімізація даних (рекомендовано)</value>
<value>Мінімізація даних</value>
</data>
<data name="Wizard_Profile_PrivacyFirst_Description" xml:space="preserve">
<value>Зберігаються лише Ваші власні розмови: tells, група, FC, linkshells, cross-world linkshells, альянс і ExtraChat. Публічний чат, діалоги NPC та системний спам відкидаються на рівні зберігання. Термін зберігання за стандартними значеннями специфікації (tells — 365 днів, канали власних розмов — 90 днів).</value>
@@ -214,7 +214,13 @@
<value>Ласкаво просимо до Hellion Chat</value>
</data>
<data name="Wizard_Step1_Subtitle" xml:space="preserve">
<value>Форк Chat 2 від Hellion Forge з урахуванням конфіденційності, фірмовим оформленням та кількома зручними покращеннями.</value>
<value>Твоє вікно чату від Hellion Forge. Приватність за замовчуванням, 25 мов, і розкладка цілком у твоїх руках.</value>
</data>
<data name="Wizard_Step1_Heritage" xml:space="preserve">
<value>Hellion Chat починався як форк Chat 2. Відтоді проєкти розійшлися настільки, що кодові бази більше не сумісні.</value>
</data>
<data name="Wizard_Step1_PluginNotice" xml:space="preserve">
<value>Плагіни у Final Fantasy XIV перебувають у сірій зоні: умови використання Square Enix їх не охоплюють, а Наокі Йосіда публічно просив не рекламувати їх. Не піднімай цю тему в каналах Say, Yell, Shout та будь-яких інших публічних.</value>
</data>
<data name="Wizard_Step1_Footer_Hint" xml:space="preserve">
<value>Три коротких кроки. Все можна змінити пізніше в розділі Налаштування → Hellion Chat.</value>
@@ -228,8 +234,8 @@
<data name="Wizard_Step2_Title" xml:space="preserve">
<value>Що зберігається?</value>
</data>
<data name="Wizard_Step2_RecommendedFooter" xml:space="preserve">
<value>★ = рекомендовано для більшості гравців.</value>
<data name="Wizard_Profile_Recommended_Badge" xml:space="preserve">
<value>Рекомендовано</value>
</data>
<data name="Wizard_Profile_Roleplay_Heading" xml:space="preserve">
<value>Roleplay</value>
@@ -298,7 +304,7 @@
<value>(без змін)</value>
</data>
<data name="Wizard_Step4_TestHint" xml:space="preserve">
<value>💡 Спробуйте: введіть /tell &lt;Ім'я гравця&gt; у чат. Hellion Chat відкриє окрему вкладку для розмови й попередньо завантажить останні {0} повідомлень.</value>
<value>Спробуйте: введіть /tell &lt;Ім'я гравця&gt; у чат. Hellion Chat відкриє окрему вкладку для розмови й попередньо завантажить останні {0} повідомлень.</value>
</data>
<data name="Wizard_Step4_SettingsHint" xml:space="preserve">
<value>Налаштування → Hellion Chat для подальшого тонкого налаштування</value>
@@ -605,6 +611,9 @@
<data name="Tabs_Presets_System" xml:space="preserve">
<value>Система</value>
</data>
<data name="Tabs_Presets_Emote" xml:space="preserve">
<value>Емоти</value>
</data>
<data name="Tabs_Presets_FreeCompany" xml:space="preserve">
<value>Free Company</value>
</data>
@@ -172,7 +172,7 @@
<value>选择一个初始配置方案。之后可在设置 → 隐私中随时调整。</value>
</data>
<data name="Wizard_Profile_PrivacyFirst_Heading" xml:space="preserve">
<value>数据最小化(推荐)</value>
<value>数据最小化</value>
</data>
<data name="Wizard_Profile_PrivacyFirst_Description" xml:space="preserve">
<value>仅保存你自己的对话:密语、小队、部队、通讯贝、跨服通讯贝、团队以及 ExtraChat。公共聊天、NPC 对话和系统垃圾信息将在存储层直接丢弃。保留期限遵循规格默认值(密语 365 天,自有对话频道 90 天)。</value>
@@ -214,7 +214,13 @@
<value>欢迎使用 Hellion Chat</value>
</data>
<data name="Wizard_Step1_Subtitle" xml:space="preserve">
<value>来自 Hellion Forge 的 Chat 2 分支,具备隐私友好的默认设置、品牌一致的视觉风格,以及若干实用改进。</value>
<value>来自 Hellion Forge 的聊天窗口。默认即注重隐私,支持 25 种语言,界面由你自己安排。</value>
</data>
<data name="Wizard_Step1_Heritage" xml:space="preserve">
<value>Hellion Chat 最初是 Chat 2 的分支。此后两者已相去甚远,代码库不再兼容。</value>
</data>
<data name="Wizard_Step1_PluginNotice" xml:space="preserve">
<value>插件在《最终幻想14》中处于灰色地带:史克威尔艾尼克斯的使用条款并未涵盖插件,吉田直树也曾公开呼吁不要宣传。请不要在说话、呼喊、喊话以及其他任何公开频道谈论此事。</value>
</data>
<data name="Wizard_Step1_Footer_Hint" xml:space="preserve">
<value>共三个简短步骤。之后可在设置 → Hellion Chat 中随时修改。</value>
@@ -228,8 +234,8 @@
<data name="Wizard_Step2_Title" xml:space="preserve">
<value>保存哪些内容?</value>
</data>
<data name="Wizard_Step2_RecommendedFooter" xml:space="preserve">
<value>★ = 推荐大多数玩家使用。</value>
<data name="Wizard_Profile_Recommended_Badge" xml:space="preserve">
<value>推荐</value>
</data>
<data name="Wizard_Profile_Roleplay_Heading" xml:space="preserve">
<value>Roleplay</value>
@@ -298,7 +304,7 @@
<value>(未更改)</value>
</data>
<data name="Wizard_Step4_TestHint" xml:space="preserve">
<value>💡 试一试:在聊天框输入 /tell &lt;玩家名称&gt;。Hellion Chat 会自动为该对话开启专属标签页,并预加载最近 {0} 条消息。</value>
<value>试一试:在聊天框输入 /tell &lt;玩家名称&gt;。Hellion Chat 会自动为该对话开启专属标签页,并预加载最近 {0} 条消息。</value>
</data>
<data name="Wizard_Step4_SettingsHint" xml:space="preserve">
<value>进入设置 → Hellion Chat 可进一步微调</value>
@@ -605,6 +611,9 @@
<data name="Tabs_Presets_System" xml:space="preserve">
<value>系统</value>
</data>
<data name="Tabs_Presets_Emote" xml:space="preserve">
<value>情感动作</value>
</data>
<data name="Tabs_Presets_FreeCompany" xml:space="preserve">
<value>部队</value>
</data>
@@ -172,7 +172,7 @@
<value>選擇一個起始設定檔。之後可在設定 → 隱私中調整所有選項。</value>
</data>
<data name="Wizard_Profile_PrivacyFirst_Heading" xml:space="preserve">
<value>資料最小化(推薦)</value>
<value>資料最小化</value>
</data>
<data name="Wizard_Profile_PrivacyFirst_Description" xml:space="preserve">
<value>只儲存你自己的對話:悄悄話、小隊、部隊、通訊貝、跨服通訊貝、團隊和 ExtraChat。公開聊天、NPC 對話和系統垃圾訊息在儲存層即被丟棄。保留期限遵循規格預設值(悄悄話 365 天,自己的對話頻道 90 天)。</value>
@@ -214,7 +214,13 @@
<value>歡迎使用 Hellion Chat</value>
</data>
<data name="Wizard_Step1_Subtitle" xml:space="preserve">
<value>Hellion Forge 推出的 Chat 2 分支版本,具備重視隱私的預設值、品牌一致的視覺設計以及一些生活品質改善。</value>
<value>來自 Hellion Forge 的聊天視窗。預設即重視隱私,支援 25 種語言,版面由你自己安排。</value>
</data>
<data name="Wizard_Step1_Heritage" xml:space="preserve">
<value>Hellion Chat 最初是 Chat 2 的分支。此後兩者已相去甚遠,程式碼庫不再相容。</value>
</data>
<data name="Wizard_Step1_PluginNotice" xml:space="preserve">
<value>外掛在《Final Fantasy XIV》中屬於灰色地帶:史克威爾艾尼克斯的使用條款並未涵蓋,吉田直樹也曾公開呼籲不要宣傳。請勿在説話、呼喊、喊話以及其他任何公開頻道談論此事。</value>
</data>
<data name="Wizard_Step1_Footer_Hint" xml:space="preserve">
<value>共三個簡短步驟。之後可在設定 → Hellion Chat 中變更所有設定。</value>
@@ -228,8 +234,8 @@
<data name="Wizard_Step2_Title" xml:space="preserve">
<value>哪些內容會被儲存?</value>
</data>
<data name="Wizard_Step2_RecommendedFooter" xml:space="preserve">
<value>★ = 推薦給大多數玩家。</value>
<data name="Wizard_Profile_Recommended_Badge" xml:space="preserve">
<value>推薦</value>
</data>
<data name="Wizard_Profile_Roleplay_Heading" xml:space="preserve">
<value>角色扮演</value>
@@ -298,7 +304,7 @@
<value>(未變更)</value>
</data>
<data name="Wizard_Step4_TestHint" xml:space="preserve">
<value>💡 試試看:在聊天中輸入 /tell &lt;玩家名稱&gt;。Hellion Chat 會為此對話開啟專屬標籤頁,並預載最後 {0} 則訊息。</value>
<value>試試看:在聊天中輸入 /tell &lt;玩家名稱&gt;。Hellion Chat 會為此對話開啟專屬標籤頁,並預載最後 {0} 則訊息。</value>
</data>
<data name="Wizard_Step4_SettingsHint" xml:space="preserve">
<value>設定 → Hellion Chat 可在之後進行細部調整</value>
@@ -605,6 +611,9 @@
<data name="Tabs_Presets_System" xml:space="preserve">
<value>系統</value>
</data>
<data name="Tabs_Presets_Emote" xml:space="preserve">
<value>情感動作</value>
</data>
<data name="Tabs_Presets_FreeCompany" xml:space="preserve">
<value>部隊</value>
</data>
+1 -1
View File
@@ -731,7 +731,7 @@
<value>Decir</value>
</data>
<data name="ChatType_Shout">
<value>Shout</value>
<value>Vociferar</value>
</data>
<data name="ChatType_TellOutgoing">
<value>Tell (saliente)</value>
+2 -2
View File
@@ -5,7 +5,7 @@ using HellionChat.Util;
namespace HellionChat.SelfTests;
// B2: behavioural check that the card path feeds CardClipPlanner AND that a
// Behavioural check that the card path feeds CardClipPlanner AND that a
// layout change clears the height cache — not a non-null check. Pure plan math
// is pinned headless by CardClipPlanTests; this drives the live accessors.
internal sealed class CardClipPlanStep : ISelfTestStep
@@ -56,7 +56,7 @@ internal sealed class CardClipPlanStep : ISelfTestStep
int remaining;
try
{
// v1.10.0/A1: the fingerprint gate waits for the value to settle, so
// v1.10.0: the fingerprint gate waits for the value to settle, so
// the step walks a synthetic clock past the window instead of sleeping.
var clock = Environment.TickCount64;
messages.RunHeightCacheInvalidationForSelfTest(tab, 400f, clock);
@@ -4,24 +4,37 @@ using Dalamud.Plugin.SelfTest;
namespace HellionChat.SelfTests;
// Pins the post-migration shape of the config. By /xlperf time the schema gate
// has already stamped Config.Version and run both migrations, so the fields
// below must carry valid values here. This probe never rewrites config; the
// migrations themselves are load-time and verified by the prepared-config smoke
// in the plan.
internal sealed class ConfigMigrationV26Step : ISelfTestStep
// has stamped Config.Version and the 2.0.0 reset has run, so the fields below
// must carry valid values here. This probe never rewrites config; the reset
// itself is load-time.
internal sealed class ConfigMigrationV27Step : ISelfTestStep
{
public ConfigMigrationV26Step(Plugin plugin)
public ConfigMigrationV27Step(Plugin plugin)
{
_ = plugin;
}
public string Name => "Hellion Chat - Config v26 migration";
public string Name => "Hellion Chat - Config v27 migration";
public SelfTestStepResult RunStep()
{
if (Plugin.Config.Version != 26)
if (Plugin.Config.Version != 27)
{
ImGui.Text($"Config.Version is {Plugin.Config.Version}, expected 26");
ImGui.Text($"Config.Version is {Plugin.Config.Version}, expected 27");
return SelfTestStepResult.Fail;
}
// v27 replaces the config rather than migrating it, and CreateFresh
// hands back an empty tab list. The seeding in LoadAsync is what fills
// it, and it runs in a different method than the reset does -- if that
// ordering ever breaks, every user comes out of the update with no tabs
// at all and nothing else in the plugin would notice.
if (Plugin.Config.Tabs.Count == 0)
{
ImGui.Text(
"No tabs at all. The v27 reset empties the list and LoadAsync is what "
+ "seeds the presets back -- reaching this point empty means it did not."
);
return SelfTestStepResult.Fail;
}
@@ -3,7 +3,7 @@ using Dalamud.Plugin.SelfTest;
namespace HellionChat.SelfTests;
// F2: CurrentTab is coupled to MainWindow.ActiveTab (no longer the fixed index-0
// CurrentTab is coupled to MainWindow.ActiveTab (no longer the fixed index-0
// Tabs lookup). Asserts ReferenceEquals between the two, with false-green
// defenses: (1) empty-config exercises the getter's fallback; (2) null ActiveTab
// opens the window so the Draw-seed sets it and retries via Waiting (bounded so a
@@ -5,17 +5,17 @@ using HellionChat.GameFunctions.Types;
namespace HellionChat.SelfTests;
// F2 (guided): interactive, fires NO synthetic probes. Shows the full measured
// Guided: interactive, fires NO synthetic probes. Shows the full measured
// state every frame so a result is observable, not a guess, and walks the user
// through the real switch-away-and-back flow. It verifies the PRIVACY-relevant
// effect, keyed on the tab type:
// - a NORMAL tab carrying a game-side tell must lose its RUNTIME target
// (CurrentChannel.TellTarget) on switch-away-and-back (the F1 strip), so a
// (CurrentChannel.TellTarget) on switch-away-and-back, so a
// typed line can't /tell the old partner;
// - a BOUND auto-tell tab keeps its partner by design (leg1) — its binding is
// Tab.TellTarget and is deliberately untouched by the strip.
// The channel label is intentionally NOT asserted: a tell tab re-derives back to
// Tell after the strip (spec TR-7); only the target matters for privacy.
// Tell after the strip ; only the target matters for privacy.
internal sealed class CurrentTabGuidedStep : ISelfTestStep
{
private readonly Plugin _plugin;
+1 -1
View File
@@ -5,7 +5,7 @@ using HellionChat._Helpers;
namespace HellionChat.SelfTests;
// B2-3: proves the plugin-disclosure arm-and-hold wires the (otherwise verwaist)
// Proves the plugin-disclosure arm-and-hold wires the (otherwise verwaist)
// scanner into the REAL send entry InputBar.TrySend. Drives TrySend via the
// arm-test-hook with a PUA glyph in the buffer and NotifyPluginDisclosure on:
// the first send must ARM and HOLD (no send), so PendingMessage stays the probe
+1 -1
View File
@@ -7,7 +7,7 @@ using HellionChat.Util;
namespace HellionChat.SelfTests;
// v1.12.0/A2: the exporter now reads text from the chunk lists instead of the
// v1.12.0: the exporter now reads text from the chunk lists instead of the
// raw SeStrings. That change is invisible to the build suite -- ExportToFile
// takes IEnumerable<Message>, Message needs SeString, and xUnit cannot load
// Dalamud.dll, so even an empty list fails before the body runs.
@@ -112,7 +112,7 @@ internal sealed class FontManagerCtorSmokeStep : ISelfTestStep
return SelfTestStepResult.Fail;
}
// B1: assert the atlas actually finished building all required handles,
// Assert the atlas actually finished building all required handles,
// not just that the references are non-null. FontsReady is the observable
// state the trimmed-fallback rebuild must still reach; a half-built atlas
// would pass the null/exception checks above but fail here.
@@ -127,8 +127,8 @@ internal sealed class FontManagerCtorSmokeStep : ISelfTestStep
return SelfTestStepResult.Fail;
}
// Report what was actually verified (Flo's request: don't just show Pass).
// The glyph-range entry counts make the B1 dedup visible — the cjk-fallback
// Report what was actually verified rather than a bare Pass.
// The glyph-range entry counts make the dedup visible — the cjk-fallback
// range is now a small trimmed remainder next to the large primary range.
var counts = fm.GlyphRangeLengths;
var italicState =
@@ -43,7 +43,7 @@ internal sealed class GlobalStyleScopeAllocStep : ISelfTestStep
GlobalStyleScope.Push(theme, registry, opacity).Dispose();
var delta = GC.GetAllocatedBytesForCurrentThread() - before;
// Report the measured figure on BOTH outcomes (Flo's request: don't just
// Report the measured figure on BOTH outcomes (a bare Pass hides
// show Pass) — the byte delta is the whole point of the GC-reserve probe.
var ok = delta <= AllocBudgetBytes;
var status = ok ? "PASS" : "FAIL";
@@ -4,7 +4,7 @@ using HellionChat.Ui.Windows;
namespace HellionChat.SelfTests;
// P8 wiring: UserHide() suppresses DrawConditions; both ActivateChat() (Enter) and
// wiring: UserHide() suppresses DrawConditions; both ActivateChat() (Enter) and
// Toggle() (/hellion) restore it. Pure window-state — the focus side is left to smoke.
internal sealed class HideRestoreSelfTestStep : ISelfTestStep
{
@@ -68,7 +68,7 @@ internal sealed class HonorificHeaderRenderStep : ISelfTestStep
);
// Draw at a deliberately wide 420px so the title never hits the truncation
// clamp — LastTitleRendered then reflects the GATE outcome, not the width.
// clamp -- LastTitleRendered then reflects the gate outcome, not the width.
try
{
// (a) available + valid title + toggle on -> title renders
@@ -4,7 +4,7 @@ using HellionChat.Ui.StyleEngine;
namespace HellionChat.SelfTests;
// Master-spec §7.5 scope note: the hover registry must not grow frame by frame.
// the hover registry must not grow frame by frame.
// Successor to HoverSheenAllocStep, which pinned the same contract against the
// old sheen start-timestamp dictionary.
//
+4 -4
View File
@@ -4,11 +4,11 @@ using HellionChat.Ui.Windows;
namespace HellionChat.SelfTests;
// B1-2 window flags. Drives the REAL MainWindow.PreDraw and asserts it wired
// 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.
// -- flags must rebuild from a fresh base, or NoMove sticks after toggling
// back. NoScrollbar|NoScrollWithMouse always present.
// Non-test caller of ResolveFlags: MainWindow.PreDraw.
internal sealed class MainWindowFlagsStep : ISelfTestStep
{
@@ -72,7 +72,7 @@ internal sealed class MainWindowFlagsStep : ISelfTestStep
return SelfTestStepResult.Fail;
}
// P7 title-bar contract: ShowTitleBar=false adds NoTitleBar from the
// title-bar contract: ShowTitleBar=false adds NoTitleBar from the
// fresh base, true clears it (same no-accumulation guarantee).
var barHidden = MainWindow.ResolveFlags(true, true, false);
var barShown = MainWindow.ResolveFlags(true, true, true);
@@ -3,7 +3,7 @@ using Dalamud.Plugin.SelfTest;
namespace HellionChat.SelfTests;
// UI-12 focus opacity. Pins the pure ResolveBgAlpha contract (focused →
// 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
@@ -7,7 +7,7 @@ using HellionChat.Util;
namespace HellionChat.SelfTests;
// B3-3: notification-sound selection. Drives the pure SelectNotificationSound
// Notification-sound selection. Drives the pure SelectNotificationSound
// (the exact pick logic ProcessMessage runs per message) through its SelfTest
// wrapper with local synthetic tabs — Plugin.Config.Tabs is never touched, so
// no real tab gains messages or unread state. The audible preview button is
@@ -3,11 +3,11 @@ using System.IO;
namespace HellionChat.SelfTests;
// Disk sink for the B5 performance baseline. Kept separate from the SelfTest
// Disk sink for the performance baseline. Kept separate from the SelfTest
// step so the per-frame hot path never references file IO. Writes one
// perf-baseline.json into the plugin ConfigDirectory, atomically (tmp + move)
// like ThemeRegistry's theme writer, so a mid-write crash leaves either the
// old file or the new file, never a half JSON. Field names track §7.5:
// old file or the new file, never a half JSON. Field names track the performance-baseline layout:
// steady-state Draw cost (avg/max ms), the quad-proxy draw-call count
// (avg/max), and frame delta (avg/max). First-frame-HITCH is read off
// drawMs max/avg by the human author, platform-annotated in the notes.
@@ -6,19 +6,19 @@ 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),
// then writes a single perf-baseline.json into the plugin ConfigDirectory so
// the cycle-notes author can copy the §7.5 figures without a separate
// the cycle-notes author can copy the baseline figures without a separate
// profiling harness. The step only records — it never fails on a threshold
// (the budgets are evaluated by a human against the JSON, §7.5 "optional,
// (the budgets are evaluated by a human against the JSON ("optional,
// manual"). It returns Waiting until the sample window fills, mirroring the
// per-frame poll idiom of ThemeSwitchSelfTestStep.
internal sealed class PerformanceBaselineStep : ISelfTestStep
{
// §7.5 steady-state window. 1000 frames ≈ 16s at 60fps, long enough to
// Steady-state window. 1000 frames ≈ 16s at 60fps, long enough to
// average out GC blips without making the manual step tedious.
private const int TargetFrames = 1000;
// Rough draw-call proxy: ImGui emits 6 indices per quad, so vertices/6 is an
// intentional under-count of draw work, not the exact quad count (API-3).
// intentional under-count of draw work, not the exact quad count.
private const int VerticesPerQuadProxy = 6;
private readonly Plugin _plugin;
@@ -3,7 +3,7 @@ using Dalamud.Plugin.SelfTest;
namespace HellionChat.SelfTests;
// B3-5: only the snap decision is headless-testable. Scroll detection + bar +
// Only the snap decision is headless-testable. Scroll detection + bar +
// hit-test are smoke-only (the scroll child exists only in-game; GetScrollY is
// garbage headless). Drives ResolveSnapToBottom via the SelfTest accessor and
// asserts the OR + the request reset invariant.
@@ -5,7 +5,7 @@ using Dalamud.Plugin.SelfTest;
namespace HellionChat.SelfTests;
// B2-1/B2-2: proves the WorldSuffixMode/NameFormMode reformat reaches the REAL
// Proves the WorldSuffixMode/NameFormMode reformat reaches the REAL
// render entry. Drives ChunkRenderer.DrawChunks (a SelfTests/README-sanctioned
// real entry that wires SenderNameDisplay.ForDisplay at ChunkRenderer.cs:54)
// with a synthetic ChunkSource.Sender chunk carrying a PlayerPayload, at a
@@ -4,7 +4,7 @@ using HellionChat.Code;
namespace HellionChat.SelfTests;
// v1.10.0/C3: the active row gets a surface and an accent bar, so exactly the
// v1.10.0: the active row gets a surface and an accent bar, so exactly the
// row the user is on must be marked -- and only that one. Drives the real
// Sidebar.Draw and reads the render-observability counter, so a regression in
// the draw path fails rather than a parallel calculation passing.
@@ -5,7 +5,7 @@ using HellionChat.GameFunctions.Types;
namespace HellionChat.SelfTests;
// B3-2: greeted glyph renders only for temp tabs when the toggle is on. Drives
// Greeted glyph renders only for temp tabs when the toggle is on. Drives
// the REAL Sidebar.Draw (render precedent: HonorificHeaderRenderStep, the only
// real .Draw in this pool — NOT SidebarModeAutoSwitchStep which only calls
// IsExpanded/GetWidth) inside the /xlperf window frame and reads the render
@@ -58,7 +58,7 @@ internal sealed class SidebarModeAutoSwitchStep : ISelfTestStep
return SelfTestStepResult.Fail;
}
// B1-3a: the expanded width must come from Config.SidebarWidth, not the
// 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
@@ -5,7 +5,7 @@ using HellionChat.GameFunctions.Types;
namespace HellionChat.SelfTests;
// B3-4: section headers render once per non-empty temp-tab pool, and compact
// Section headers render once per non-empty temp-tab pool, and compact
// mode suppresses the header text (separators stay). Drives the REAL
// Sidebar.Draw inside the /xlperf window frame (same render precedent as
// SidebarGreetedGlyphStep) and reads the render observability counter.
@@ -4,7 +4,7 @@ using HellionChat.Code;
namespace HellionChat.SelfTests;
// F3: the unread dot the v1.8.x sidebar rebuild dropped. Drives the REAL
// The unread dot the v1.8.x sidebar rebuild dropped. Drives the REAL
// Sidebar.Draw (render precedent: SidebarGreetedGlyphStep) with a probe tab that
// is inactive and carries Unread>0, then reads the render-observability counter
// so a regressed/absent dot fails. Asserts: dot drawn for an inactive Unseen tab;
@@ -4,7 +4,7 @@ using HellionChat.Ui.Components;
namespace HellionChat.SelfTests;
// B3-1: rename must persist. Drives the real ApplyTabRename (the InputText
// Rename must persist. Drives the real ApplyTabRename (the InputText
// callback path), then SaveConfig + reload from disk and asserts the new name
// survived — a fresh-from-config tab, not the same reference (a reference check
// would pass on a dead roundtrip). Uses a persistent (non-temp) tab: unpinned
@@ -7,10 +7,10 @@ using HellionChat.Util;
namespace HellionChat.SelfTests;
// F1: the activation strip. Drives the REAL OnTabActivated — the entry the
// The activation strip. Drives the REAL OnTabActivated -- the entry the
// Sidebar/TopTabBar click handlers, the pop-out path and the Draw-seed all call
// — with local probe tabs (Plugin.Config.Tabs is never touched). Asserts the
// five contracts: strip-on-switch, no-strip-on-reclick (TR-4), leg1 preserve,
// five contracts: strip-on-switch, no-strip-on-reclick, leg1 preserve,
// derive, and non-tell untouched.
internal sealed class TellResetOnActivateStep : ISelfTestStep
{
@@ -41,7 +41,7 @@ internal sealed class TellResetOnActivateStep : ISelfTestStep
}
// (b) re-clicking the already-active tab (previous == tab) must NOT strip
// a live game-tell conversation (TR-4 regression guard).
// a live game-tell conversation (regression guard).
var reclick = MakeStaleTellTab(boundTellTarget: false, withLabel: false);
TabLifecycleHelpers.OnTabActivated(reclick, reclick);
if (reclick.CurrentChannel.TellTarget is null)
@@ -4,7 +4,7 @@ using HellionChat.Themes;
namespace HellionChat.SelfTests;
// Verifies the v1.5.4 PM-1 crossfade contract: switching the active
// Verifies the v1.5.4 crossfade contract: switching the active
// theme arms TryGetActiveCrossfade for ~300ms, then the registry
// returns to direct AbgrCache reads. A second switch within 100ms
// keeps the lerped path active (no identity-snap). CleanUp restores
@@ -67,7 +67,7 @@ internal sealed class ThemeCrossfadeSelfTestStep : ISelfTestStep
// it as "saw the start" if more than 300ms have elapsed.
// Skip the mid-crossfade-switch phase in that case -- the
// lerped path is no longer active, so a second switch would
// re-arm a fresh crossfade and not exercise PM-1b's
// re-arm a fresh crossfade and not exercise its
// mid-flight-origin override.
if (Environment.TickCount64 - this.armedAtTickMs > 300)
{
@@ -83,7 +83,7 @@ internal sealed class ThemeCrossfadeSelfTestStep : ISelfTestStep
if (!this.sawMidCrossfadeSwitch)
{
// PM-Test-3 mid-crossfade-switch phase: within ~100ms of the
// mid-crossfade-switch phase: within ~100ms of the
// first observed crossfade, fire a second Switch to a THIRD
// theme. ArmCrossfade must compose the current lerped state
// as the new origin -- TryGetActiveCrossfade still returns
+1 -1
View File
@@ -4,7 +4,7 @@ using HellionChat.Code;
namespace HellionChat.SelfTests;
// v1.10.0/D1: the top-tab strip marks the active tab with a fill plus an accent
// v1.10.0: the top-tab strip marks the active tab with a fill plus an accent
// underline. Drives the real TopTabBar.Draw and reads the render counter.
//
// "At most one", not "exactly one": the strip skips popped-out tabs, so zero
+1 -1
View File
@@ -7,7 +7,7 @@ using HellionChat.Ui.StyleEngine;
namespace HellionChat.SelfTests;
// v1.13.0/A7: the type scale has no call site in the message list until block C,
// v1.13.0: the type scale has no call site in the message list until block C,
// so without this step block A would end with nothing to look at and two helpers
// (TypeScale, BaselineMath) with no caller at all.
//
+7 -6
View File
@@ -3,12 +3,12 @@ using Dalamud.Plugin.SelfTest;
namespace HellionChat.SelfTests;
// F3: the unread decision (MessageManager.ShouldCountUnread). Unseen suppresses
// The unread decision (MessageManager.ShouldCountUnread). Unseen suppresses
// unread on an inactive tab only when the active tab ALSO shows the message (you
// saw it there) — 1.5.6/upstream semantics, now measured against the REAL active
// tab thanks to F2. Asserts the truth table: suppressed when active tab also
// matches; counts when it does not (the Carla/Jin case); All always counts; None
// counts at the increment layer (the display gate hides it).
// saw it there) -- 1.5.6/upstream semantics, measured against the real active
// tab. Asserts the truth table: suppressed when the active tab also matches;
// counts when it does not; All always counts; None counts at the increment
// layer (the display gate hides it).
internal sealed class UnreadDecisionStep : ISelfTestStep
{
public string Name => "Hellion Chat - Unread decision (per active tab)";
@@ -27,7 +27,8 @@ internal sealed class UnreadDecisionStep : ISelfTestStep
}
// (b) inactive Unseen tab + the active tab does NOT show the message
// (currentTabMatches=false) => counts (badge). The Carla/Jin case.
// (currentTabMatches=false) => counts (badge). Two people talking in
// a channel the active tab does not carry.
if (!MessageManager.ShouldCountUnread(inactive, active, currentTabMatches: false))
{
ImGui.Text("(b) inactive Unseen tab must count when the active tab does not show it");
@@ -64,7 +64,7 @@ internal sealed class WizardStateSmokeStep : ISelfTestStep
// jumps straight to Step 4 (no Step-3 entry → no seed for
// FilterIncludePreviousSessions), commits, and asserts the history
// toggle remained on its pre-test value. Pins the null-semantics
// from Spec Z.176 so a regression in CommitPending that started
// so a regression in CommitPending that started
// writing seeded recommendations unconditionally would surface
// here.
// CommitPending → ApplyRoleplay overwrites six privacy /
+1 -1
View File
@@ -5,7 +5,7 @@ using Microsoft.Extensions.Logging;
namespace HellionChat.Services;
// Routes an incoming tell to the configured TellAutoOpenMode (Off/Sidebar/
// TopTab/Popout). Decoupled from AutoTellTabsService (Flo decision 2026-06-15):
// TopTab/Popout). Deliberately decoupled from AutoTellTabsService:
// that service owns tab CREATION + lifecycle; this only REVEALS/pops the tab it
// finds. Popout guards on pool.IsOpen so it never double-pops a tab the
// AutoTellTabsOpenAsPopout path already opened. Subscribes to the resolved
+3 -3
View File
@@ -51,9 +51,9 @@ internal static class EventHorizon
ChatColors: new ThemeChatColors(
new Dictionary<HellionChat.Code.ChatType, uint>
{
// Event Horizon — Cosmic-Purple-Drift: helle Pastelle bekommen
// Lavender-Tinte, Akzent-Channels (Tell) ziehen Richtung Magenta-
// Lila. Channel-Identität bleibt klar erkennbar.
// Cosmic purple drift: the pale pastels take a lavender tint and
// the accent channels (tell) pull towards magenta-violet. Channel
// identity stays readable throughout.
[HellionChat.Code.ChatType.Say] = ColourUtil.HexToRgba("#E6E0F5"),
[HellionChat.Code.ChatType.Yell] = ColourUtil.HexToRgba("#F2C25C"),
[HellionChat.Code.ChatType.Shout] = ColourUtil.HexToRgba("#FF9050"),
@@ -51,8 +51,8 @@ internal static class ForgeMerchantman
ChatColors: new ThemeChatColors(
new Dictionary<HellionChat.Code.ChatType, uint>
{
// Forge Merchantman — Patina-Tinte in Party/FC, Bernstein-Tinte in
// Yell/Alliance/CustomEmote. Channel-identity bleibt voll erhalten.
// Patina tint on party and free company, amber on yell, alliance
// and custom emotes. Channel identity is left fully intact.
[HellionChat.Code.ChatType.Say] = ColourUtil.HexToRgba("#FFFFFF"),
[HellionChat.Code.ChatType.Yell] = ColourUtil.HexToRgba("#F0C060"),
[HellionChat.Code.ChatType.Shout] = ColourUtil.HexToRgba("#E8902C"),
+2 -2
View File
@@ -51,8 +51,8 @@ internal static class HellionArctic
ChatColors: new ThemeChatColors(
new Dictionary<HellionChat.Code.ChatType, uint>
{
// Hellion Arctic — FFXIV-Standard mit dezenter Cyan-Tinte in den
// blauen Channels (Party/FC). Channel-Identität bleibt klar.
// The FFXIV defaults with a restrained cyan tint on the blue
// channels (party, free company). Channel identity stays clear.
[HellionChat.Code.ChatType.Say] = ColourUtil.HexToRgba("#FFFFFF"),
[HellionChat.Code.ChatType.Yell] = ColourUtil.HexToRgba("#FFE066"),
[HellionChat.Code.ChatType.Shout] = ColourUtil.HexToRgba("#FFA040"),
+4 -3
View File
@@ -51,9 +51,10 @@ internal static class IndigoViolet
ChatColors: new ThemeChatColors(
new Dictionary<HellionChat.Code.ChatType, uint>
{
// Indigo Violet — Lavender-Pink-Drift in Tell und LS6/7. Türkis-
// Mint-Aurora-Counter in Party/FC und LS4. Glitter-Gold in Yell.
// Differenzierung zu Event Horizon: dunkler, dichter, Türkis statt Gold.
// Lavender-pink drift on tell and linkshells 6 and 7, countered by
// a turquoise-mint aurora on party, free company and linkshell 4.
// Glitter gold on yell. What sets it apart from Event Horizon:
// darker, denser, and turquoise where that one goes gold.
[HellionChat.Code.ChatType.Say] = ColourUtil.HexToRgba("#F0E6FF"),
[HellionChat.Code.ChatType.Yell] = ColourUtil.HexToRgba("#F0D880"),
[HellionChat.Code.ChatType.Shout] = ColourUtil.HexToRgba("#F09A60"),
+3 -3
View File
@@ -51,9 +51,9 @@ internal static class MintGrove
ChatColors: new ThemeChatColors(
new Dictionary<HellionChat.Code.ChatType, uint>
{
// Mint Grove — Naturthemen-Tönung: Honey-Amber in Yell-Familie,
// Mint-Drift in NoviceNetwork und Linkshell. Tell-Pink-Identität
// bleibt erhalten für Erkennbarkeit.
// Nature-themed tint: honey amber across the yell family, a mint
// drift in novice network and linkshell. Tell keeps its pink so
// the channel stays recognisable.
[HellionChat.Code.ChatType.Say] = ColourUtil.HexToRgba("#E8F5EA"),
[HellionChat.Code.ChatType.Yell] = ColourUtil.HexToRgba("#F9D580"),
[HellionChat.Code.ChatType.Shout] = ColourUtil.HexToRgba("#F0A050"),
+3 -2
View File
@@ -51,8 +51,9 @@ internal static class NightBlue
ChatColors: new ThemeChatColors(
new Dictionary<HellionChat.Code.ChatType, uint>
{
// Night Blue — Royal-Blue-Tinte in Party/FC, Bronze-Gold in Yell/
// Alliance. Channel-identity (Tell-Pink, NN-Lime) bleibt erhalten.
// Royal blue on party and free company, bronze gold on yell and
// alliance. Channel identity is preserved -- tell stays pink,
// novice network stays lime.
[HellionChat.Code.ChatType.Say] = ColourUtil.HexToRgba("#FFFFFF"),
[HellionChat.Code.ChatType.Yell] = ColourUtil.HexToRgba("#FFD060"),
[HellionChat.Code.ChatType.Shout] = ColourUtil.HexToRgba("#FFA040"),
@@ -51,8 +51,9 @@ internal static class SynthwaveSunset
ChatColors: new ThemeChatColors(
new Dictionary<HellionChat.Code.ChatType, uint>
{
// Synthwave Sunset — Magenta dominiert die warmen Channels (Yell/Shout/FC),
// Cyan dominiert die kühlen (Tell/Party). Neon-Akzente für Status-nahe Channels.
// Magenta carries the warm channels (yell, shout, free company),
// cyan the cool ones (tell, party). Neon accents on the status-
// adjacent channels.
[HellionChat.Code.ChatType.Say] = ColourUtil.HexToRgba("#F0DFFF"),
[HellionChat.Code.ChatType.Yell] = ColourUtil.HexToRgba("#FF2D95"),
[HellionChat.Code.ChatType.Shout] = ColourUtil.HexToRgba("#FF6BB6"),
+16 -16
View File
@@ -10,7 +10,7 @@ public sealed class ThemeRegistry
public const string DefaultSlug = HellionArctic.Slug;
// 1Hz throttle for the v1.4.8 B2 auto-refresh-on-active path. The
// 1Hz throttle for the v1.4.8 auto-refresh-on-active path. The
// Plugin.Draw hook calls RefreshActiveIfStale every frame, but the
// actual File.GetLastWriteTimeUtc disk-stat only runs once per second
// -- 60fps would otherwise mean 3600 stats/min on the same path (more
@@ -24,7 +24,7 @@ public sealed class ThemeRegistry
private readonly string? _customThemesDir;
private Theme _active;
// v1.4.8 B2: source path of the currently active custom theme. Captured
// v1.4.8: source path of the currently active custom theme. Captured
// at Switch() time so RefreshActiveIfStale does not have to reconstruct
// a filename from the slug -- custom theme filenames are not required
// to match the slug they declare in the JSON body. Null when the active
@@ -33,7 +33,7 @@ public sealed class ThemeRegistry
private long _lastActiveStampCheckMs = -ActiveStampPollIntervalMs;
private DateTime _lastActiveStamp = DateTime.MinValue;
// PM-1 crossfade state. Switch() captures the previous AbgrCache as a
// crossfade state. Switch() captures the previous AbgrCache as a
// VALUE-COPY (not a Theme reference) -- the built-in singletons share
// their RecomputeAbgrCache identity, so a reference would mutate
// alongside the new active. _crossfadeStartTickMs == long.MinValue
@@ -54,7 +54,7 @@ public sealed class ThemeRegistry
internal void SetActiveChangedCallback(Action callback) => _onActiveChanged = callback;
// Shared slug guard for any code path that turns a slug into a filename.
// Both SaveEditingBuffer (F1) and ImportFromPath (M6) call this so the
// Both SaveEditingBuffer and ImportFromPath call this so the
// path-traversal/invalid-char rules live in exactly one place.
//
// Whitespace rejection is intentional: Path.GetInvalidFileNameChars on
@@ -112,16 +112,16 @@ public sealed class ThemeRegistry
public Theme Active => _active;
// Read-only exposure of the configured custom themes directory.
// M6 ThemeImportExportRow opens this path via Process.Start.
// ThemeImportExportRow opens this path via Process.Start.
public string? CustomThemesDir => _customThemesDir;
// Read-only enumeration of all built-in theme slugs. T2 ThemePickerCategoryStep
// Read-only enumeration of all built-in theme slugs. ThemePickerCategoryStep
// diffs this set against ThemePicker.CategoryMapSlugs to enforce coverage.
public IEnumerable<string> BuiltinSlugs => _builtIns.Keys;
// True try-pattern lookup: returns false when neither built-in nor custom
// cache holds the slug, no fallback to default. M3 ThemePicker uses this
// for card-rendering, M6 ThemeImportExportRow for fork-slug collisions.
// cache holds the slug, no fallback to default. ThemePicker uses this
// for card-rendering, ThemeImportExportRow for fork-slug collisions.
// Cold-cache fallback: see `LoadCustomBySlug` lookup-by-slug reverse
// iteration — it only walks the pre-populated _customCache. If a freshly
// imported file has not been enumerated yet (or no warm-up ran), the first
@@ -292,8 +292,8 @@ public sealed class ThemeRegistry
// Switch() prefers built-ins over custom themes with the same slug
// (see `Switch` built-in-first lookup), so saving a custom file under
// a built-in slug persists the file but leaves the built-in active —
// looks green, behaves broken. M4 ColorPicker DrawIdleState forks
// built-in themes into a custom slug before BeginEditing, M6
// looks green, behaves broken. ColorPicker DrawIdleState forks
// built-in themes into a custom slug before BeginEditing,
// ImportFromPath renames built-in-colliding imports to <slug>_imported.
// New call-sites must either fork first or rename to a non-built-in slug.
public bool SaveEditingBuffer(out string targetPath)
@@ -308,7 +308,7 @@ public sealed class ThemeRegistry
// separators, parent-directory tokens, or platform-invalid filename chars.
// Without this guard an imported theme with Slug "../../../etc/passwd"
// would let Path.Combine escape _customThemesDir entirely. Shared helper
// so M6 ImportFromPath uses the exact same rule set.
// so ImportFromPath uses the exact same rule set.
var safeSlug = _editingThemeBuffer.Slug;
if (!IsSafeThemeSlug(safeSlug))
{
@@ -326,8 +326,8 @@ public sealed class ThemeRegistry
// persist a custom file under a built-in slug — the file lands on disk,
// Switch keeps the built-in active, and the post-save active-slug check
// below returns false. The caller then sees "save failed" while a garbage
// file accumulates in the themes dir on every retry. M4 ColorPicker forks
// built-in themes into a custom slug before BeginEditing, M6 ImportFromPath
// file accumulates in the themes dir on every retry. ColorPicker forks
// built-in themes into a custom slug before BeginEditing, ImportFromPath
// renames built-in-colliding imports to <slug>_imported, so production
// paths already steer clear; this guard catches everything else.
if (_builtIns.ContainsKey(safeSlug))
@@ -522,8 +522,8 @@ public sealed class ThemeRegistry
)
{
var t = (float)(now - _crossfadeStartTickMs) / CrossfadeDurationMs;
// A2: SmoothStep easing so the fade eases in/out instead of a
// linear ramp. MUST stay in lockstep with TryGetActiveCrossfade (K8).
// SmoothStep easing so the fade eases in and out instead of running
// linear. MUST stay in lockstep with TryGetActiveCrossfade.
var te = t * t * (3f - 2f * t);
snapshot = ThemeAbgrCacheLerp.Lerp(_previousAbgrSnapshot.Value, _active.AbgrCache, te);
}
@@ -551,7 +551,7 @@ public sealed class ThemeRegistry
return false;
var t = (float)elapsed / CrossfadeDurationMs;
// A2: SmoothStep easing -- keep identical to ArmCrossfade (K8).
// SmoothStep easing -- keep identical to ArmCrossfade.
var te = t * t * (3f - 2f * t);
lerped = ThemeAbgrCacheLerp.Lerp(_previousAbgrSnapshot.Value, _active.AbgrCache, te);
return true;
+1 -1
View File
@@ -1,6 +1,6 @@
namespace HellionChat.Themes;
// Pure stale-check for the v1.4.8 B2 theme-auto-refresh-on-active path.
// Pure stale-check for the v1.4.8 theme-auto-refresh-on-active path.
// Lives in a free helper class so the Build-Suite can exercise the diff
// rules without instantiating ThemeRegistry (which touches the Dalamud
// log proxy and the filesystem). The rules:
+2 -2
View File
@@ -18,7 +18,7 @@ internal sealed class CommandHelpWindow : Window
// Setter-injected post-ctor to break the InputBar -> CommandHelpWindow ->
// MainWindow -> InputBar singleton cycle (MS.DI does not detect cycles
// through FactoryCallSite registrations). Wired in
// CommandHelpWindowInitHostedService.StartAsync, same §6.2 pattern as
// CommandHelpWindowInitHostedService.StartAsync, same setter-injection pattern as
// MessageList.AttachPayloadHandler.
private Windows.MainWindow? _mainWindow;
@@ -41,7 +41,7 @@ internal sealed class CommandHelpWindow : Window
RespectCloseHotkey = false;
DisableWindowSounds = true;
// Logger injected for future diagnostic hooks (no call-sites yet in R2).
// Logger injected for future diagnostic hooks; no call sites yet.
_ = _logger;
}
+2 -2
View File
@@ -2,8 +2,8 @@ using System.Collections.Generic;
namespace HellionChat.Ui.Components;
// B2 (PERF-B2): variable-height clip plan. ImGuiListClipper needs a constant
// row height, and since v1.10.0/A2 neither density has one (compact rows wrap
// Variable-height clip plan. ImGuiListClipper needs a constant
// row height, and since v1.10.0 neither density has one (compact rows wrap
// too), so both compute a plan from the cached per-row heights: a lead dummy
// for the rows above
// the viewport, the [first..last] index range that overlaps the viewport, and
+3 -3
View File
@@ -34,11 +34,11 @@ internal sealed class ChunkRenderer
// names change every plugin reload to avoid stable cross-session linkage.
_salt = new Random().Next().ToString();
// Not yet consumed in C2/C3; E-task wiring will likely add log call-sites later.
// No call sites yet; logging here will likely come later.
_ = _logger;
}
// B2-1/B2-2 render-observability: the formatted sender text the real draw
// render-observability: the formatted sender text the real draw
// path actually produced (post-ForDisplay). A SelfTest reads this after
// driving DrawChunks to prove the WorldSuffixMode/NameFormMode reformat
// reached the real render entry — never the helper in isolation. null until
@@ -52,7 +52,7 @@ internal sealed class ChunkRenderer
float lineWidth = 0f
)
{
// UI-7: render a copy with the sender name reformatted per the user's
// Render a copy with the sender name reformatted per the user's
// display options. Skipped in screenshot mode so the name-anonymising
// path in DrawChunk stays reliable (privacy wins). ForDisplay returns
// the list unchanged when nothing applies, so non-sender lists and the
+5 -5
View File
@@ -64,7 +64,7 @@ internal sealed class InputBar
private bool _wasInputTextHovered;
private bool? _isFocusedOverride; // Test-only; null = honour per-frame Draw() value.
// UI-11 plugin-disclosure arm-and-hold: holds the buffer that armed the
// plugin-disclosure arm-and-hold: holds the buffer that armed the
// disclosure warning. null = not armed. Compared by value so an edit
// re-arms and a resend on the identical buffer goes through. 1.5.6 parity
// (ChatInputBar 1d3b429:27).
@@ -118,7 +118,7 @@ internal sealed class InputBar
// Note: when MainWindow is closed, DrawInputField never runs, so
// _isFocused keeps the last value written by the previous draw pass.
// The consumer that actually pushes this state across the IPC boundary
// (TypingIpc.BuildState, see F3 Step 2) gates on Plugin.MainWindow.IsOpen
// (TypingIpc.BuildState) gates on Plugin.MainWindow.IsOpen
// itself, so the stale backing-field never leaks to subscribers. Mirroring
// the gate here would require an extra Plugin-backref in InputBar that the
// rest of the component doesn't need.
@@ -196,7 +196,7 @@ internal sealed class InputBar
ImGui.SameLine();
DrawQuickButtons();
// UI-11: yellow inline warning while a plugin-only-glyph message is
// Yellow inline warning while a plugin-only-glyph message is
// armed-and-held (buffer unchanged since it armed). Renders on its own
// line below the input row. 1.5.6 parity (ChatInputBar 1d3b429:93-103).
if (
@@ -589,7 +589,7 @@ internal sealed class InputBar
if (string.IsNullOrEmpty(text))
return;
// UI-11: plugin-disclosure arm-and-hold. Arm + scan on the RAW
// Plugin-disclosure arm-and-hold. Arm + scan on the RAW
// _pendingMessage (NOT the trimmed `text`) so the Draw warning gate
// (_pendingMessage == _disclosureArmedBuffer) matches byte-for-byte even
// when the buffer has leading/trailing whitespace. 1.5.6 armed/held/
@@ -1183,7 +1183,7 @@ internal sealed class InputBar
}
// DTO for an in-flight auto-translate completion. Lives as a companion type
// in this file because it is only consumed by InputBar (see v1.7.1 Fix #4 plan §2.4).
// in this file because it is only consumed by InputBar.
internal sealed class AutoCompleteInfo
{
// ToComplete MUST be a mutable field (not an auto-property), because the
+14 -14
View File
@@ -18,13 +18,13 @@ internal sealed class MessageList
private PayloadHandler? _handler;
// B3-5: scroll-to-bottom state. Per-instance, so pop-out windows (own
// Scroll-to-bottom state. Per-instance, so pop-out windows (own
// MessageList instance, PluginHostFactory.cs:263-266) isolate automatically —
// the old 1.5.6 updateScrollState flag is NOT needed here.
private bool _scrolledUp;
private bool _scrollToBottomRequested;
// B2: the height cache is only valid while these inputs are unchanged.
// The height cache is only valid while these inputs are unchanged.
// FontManager's own fingerprint covers font sizes only, not density / the two
// name-display modes / width — a stale height would misplace the clipper dummies.
// Per tab, not per list: the old single field let a width change in tab A mark
@@ -38,7 +38,7 @@ internal sealed class MessageList
private readonly Action<Message, string?> _drawCardRow;
// Reused across frames: at MessageManager.MessageDisplayLimit a fresh array
// per frame is 40 KB of garbage, and A2 put the default density on this
// per frame is 40 KB of garbage, and A later cycle put the default density on this
// path. The old comment named MaxLinesToRender and its 2500 default, a
// config field that had stopped bounding anything.
private float[] _heightScratch = [];
@@ -49,7 +49,7 @@ internal sealed class MessageList
private bool _stampVisible;
private float _metaDrop;
// §6.2: setter-injection breaks the PayloadHandler → MainWindow → MessageList → PayloadHandler 3-cycle.
// Setter-injection breaks the PayloadHandler → MainWindow → MessageList → PayloadHandler 3-cycle.
// Wired by PayloadHandlerInitHostedService.StartAsync after both singletons exist.
internal void AttachPayloadHandler(PayloadHandler handler)
{
@@ -73,12 +73,12 @@ internal sealed class MessageList
return snap;
}
// SelfTest hook (B3-5 reset-invariant, REQUIRED — not optional). Lets
// SelfTest hook (reset-invariant, REQUIRED — not optional). Lets
// ScrollSnapDecisionStep flip the request flag without a real click, so the
// post-snap reset can be asserted; without it only the OR branch is testable.
internal void RequestScrollToBottomForSelfTest() => _scrollToBottomRequested = true;
// SelfTest hook (B2): runs the real planner against a caller fixture so the
// SelfTest hook: runs the real planner against a caller fixture so the
// step asserts the plan without a live scroll child (GetScrollY is garbage headless).
internal CardClipPlan PlanCardClipForSelfTest(
IReadOnlyList<float> heights,
@@ -86,9 +86,9 @@ internal sealed class MessageList
float viewportHeight
) => CardClipPlanner.Plan(heights, scrollY, viewportHeight);
// SelfTest hook (B2): drives the live invalidation, returns the tab's remaining
// SelfTest hook: drives the live invalidation, returns the tab's remaining
// cached-height count so the step can assert the drop. nowMs is a parameter so
// the step can step past the settle window without sleeping (v1.10.0/A1).
// the step can step past the settle window without sleeping (v1.10.0).
internal int RunHeightCacheInvalidationForSelfTest(Tab tab, float contentWidth, long nowMs)
{
InvalidateHeightCacheIfLayoutChanged(tab, contentWidth, nowMs);
@@ -165,7 +165,7 @@ internal sealed class MessageList
MeasureTimestampColumn(tab);
// B2: drop stale cached heights before the snapshot draw. Both densities
// Drop stale cached heights before the snapshot draw. Both densities
// need this now -- compact rows are not constant height either, they wrap.
// Width read here while it is valid.
InvalidateHeightCacheIfLayoutChanged(
@@ -187,7 +187,7 @@ internal sealed class MessageList
var frozen = _fingerprintGates[tab.Identifier].IsPending;
DrawRows(tab, messages, compact ? _drawCompactRow : _drawCardRow, frozen);
// B3-5: scroll values are frame-constant inside the child, so this
// Scroll values are frame-constant inside the child, so this
// reflects the current frame's state wherever it runs; kept after the
// render to mirror the 1.5.6 end-of-DrawMessageLog placement.
_scrolledUp = ImGui.GetScrollMaxY() - ImGui.GetScrollY() > 1f;
@@ -282,7 +282,7 @@ internal sealed class MessageList
ImGui.SetCursorPos(origin with { X = origin.X + _stampColumnWidth });
}
// B3-5: Discord-style full-width bar pinned to the bottom edge of the
// Discord-style full-width bar pinned to the bottom edge of the
// visible region while the user is scrolled up. Geometry comes from window
// pos + size (visible region), never from the content flow: when scrolled
// up the visible bottom sits above the content bottom, so the
@@ -335,7 +335,7 @@ internal sealed class MessageList
private void DrawCompactRow(Message message, string? previousStamp)
{
// B2-1/B2-2: render the sender through DrawChunks (the name-aware path
// Render the sender through DrawChunks (the name-aware path
// that applies WorldSuffixMode/NameFormMode via ForDisplay), not as a
// flat SenderSource.TextValue string. message.Sender already carries the
// channel brackets/colon as ChunkSource.None wrappers (MessageManager
@@ -566,10 +566,10 @@ internal sealed class MessageList
private void DrawCardRow(Message message, string? previousStamp)
{
// B2-1/B2-2: sender via DrawChunks (name-aware path), on its own line
// Sender via DrawChunks (name-aware path), on its own line
// with content below — 1.5.6 card parity (ChatLogWindow.cs:1913, no
// SameLine after the sender). The 1.5.6 channel-colour push on the
// sender is deferred styling polish (masterplan §6 -> v1.9.0); plain
// sender is deferred styling polish (deferred to v1.9.0); plain
// text here.
// A system message has no sender, so a header row would be a stamp on a
// line of its own -- an empty gesture. Those stay single-line in both
@@ -20,7 +20,7 @@ internal sealed class LivePreviewPanel : IDisposable
private static uint Abgr(StyleEngine.Token token, ThemeColors colors) =>
ColourUtil.RgbaToAbgr(Tokens.Resolve(token, colors));
// Static counter for S5 reload-stress verification: after 10 reloads the
// Static counter for reload-stress verification: after 10 reloads the
// counter must read 0 (plugin disabled) or 1 (plugin enabled). Anything
// higher signals a Dispose skip and a subscriber leak against ThemeRegistry.
internal static int InstanceCount;
@@ -133,7 +133,7 @@ internal sealed class AboutTab
LastHonorificStatusKey = kind.ToString();
var colors = _themes.Active.Colors;
// Null-safety via the `is { } v` pattern, never `.Value` raw (spec SEC-2):
// Null-safety via the `is { } v` pattern, never `.Value` raw :
// the version is bound only on the arms that have it; the impossible
// Detected/Incompatible-without-version state falls through to default.
switch (kind)
@@ -254,7 +254,7 @@ internal sealed class DataPrivacyTab
)
{
// Read-only statement, not a switch. Do not promote it to one
// without an explicit Sub-Spec change: a toggle implies there is
// without an explicit design change: a toggle implies there is
// something to turn off.
ImGuiUtil.HelpText(HellionStrings.Settings_Telemetry_None);
}
@@ -173,8 +173,7 @@ internal sealed class ThemeImportExportRow
// Slug sanitisation BEFORE BeginEditing — SaveEditingBuffer would
// reject too, but rejecting here means an unsafe slug never enters
// the editing buffer. Shared helper ThemeRegistry.IsSafeThemeSlug
// keeps the rule set in sync with F1's save-side guard (see
// ThemeRegistry.IsSafeThemeSlug shared helper).
// keeps the rule set in sync with the save-side guard.
var importSlug = theme.Slug;
if (!ThemeRegistry.IsSafeThemeSlug(importSlug))
{
@@ -186,9 +185,9 @@ internal sealed class ThemeImportExportRow
return;
}
// Pragmatic deviation from §1.6 wording ("File.Copy into themes/"):
// BeginEditing+SaveEditingBuffer produces the same end-state and
// reuses the validated F1 save pipeline. Trade-off: destination
// Not a plain File.Copy into themes/: BeginEditing+SaveEditingBuffer
// produces the same end state and reuses the validated save
// pipeline. Trade-off: destination
// filename becomes the theme's slug, not the original filename.
//
// Slug-collision handling:
@@ -29,7 +29,7 @@ internal sealed class ThemePicker
(HellionStrings.Settings_Theme_Category_Retro, new[] { "synthwave-sunset" }, false),
];
// T2 ThemePickerCategoryStep diffs this against ThemeRegistry.BuiltinSlugs
// ThemePickerCategoryStep diffs this against ThemeRegistry.BuiltinSlugs
// to enforce coverage. Kept on the static map so the test does not pierce instance state.
internal static IEnumerable<string> CategoryMapSlugs => CategoryMap.SelectMany(c => c.Slugs);
+13 -9
View File
@@ -22,10 +22,14 @@ internal sealed class Sidebar
{
public const float IconOnlyWidth = 38f;
// 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;
// Expanded sidebar width is user-configurable (Config.SidebarWidth),
// clamped to these bounds (matches the ChannelsTab slider range).
//
// The floor is where a tab name stops being readable, not where the icons
// stop fitting: 40 let the expanded sidebar be narrower than the icon-only
// one, which drew labels into a column too narrow to hold them. Anyone who
// wants it that slim wants the collapsed layout, and that is IconOnlyWidth.
public const float MinSidebarWidth = 130f;
public const float MaxSidebarWidth = 300f;
private static float RowHeight => Metrics.SidebarRowHeight;
@@ -38,7 +42,7 @@ internal sealed class Sidebar
// previously active one -- that row was still active when it was painted.
internal int LastRenderedActiveSurfaceCount { get; private set; }
// B3-2 render observability: counts greeted glyphs actually drawn this frame.
// render observability: counts greeted glyphs actually drawn this frame.
// Incremented ONLY in the real glyph branch in DrawRow; reset at Draw start.
// The SelfTest reads it after driving the real Draw — no dead service roundtrip.
internal int LastRenderedGreetedGlyphCount;
@@ -48,7 +52,7 @@ internal sealed class Sidebar
// beside it.
private const float PinGlyphScale = 0.6f;
// B3-4 render observability: section headers actually drawn this frame.
// render observability: section headers actually drawn this frame.
// Incremented only in the real header branch; reset at Draw start.
internal int LastDrawnSectionHeaderCount;
@@ -149,7 +153,7 @@ internal sealed class Sidebar
var dimAbgr = ColourUtil.RgbaToAbgr(theme.Colors.TextDim);
var dl = ImGui.GetWindowDrawList();
// B3-4 sectioned render order (1.5.6 parity): persistent → pinned
// sectioned render order (1.5.6 parity): persistent → pinned
// TempTabs → unpinned TempTabs. Only the display sequence regroups;
// the tab list itself stays untouched and every row keeps its
// ORIGINAL list index for PushID, so an open context-menu popup
@@ -250,7 +254,7 @@ internal sealed class Sidebar
// Only split off a separate pop-out hit area when there's room for
// both buttons. Below that, the whole row stays as a single
// selectable strip without the pop-out affordance.
// A3: gate the pop-out affordance on the expanded sidebar too. In
// Gate the pop-out affordance on the expanded sidebar too. In
// icon-only mode avail still clears the width threshold, which used to
// paint the pop-out glyph over the tab icon. The row stays a single
// selectable strip when collapsed; right-click pop-out is unaffected.
@@ -476,7 +480,7 @@ internal sealed class Sidebar
// The hit area sits at the LEFT edge of the row, but the item must
// be submitted AFTER TabContextMenu.Draw — any interactive item
// between the row button and the popup call would steal the
// right-click trigger (B3-1 ordering constraint).
// right-click trigger (ordering constraint).
ImGui.SetCursorScreenPos(origin);
// CheckCircle = greeted, plain Check = still pending (1.5.6 mapping).
+1 -1
View File
@@ -82,7 +82,7 @@ internal static class TabContextMenu
ClearPendingRename();
}
// Per-tab notification sound (B3-3). The checkbox gates the picker so
// Per-tab notification sound. The checkbox gates the picker so
// tabs that never want a sound keep the popup short.
if (
ImGui.Checkbox(
+2 -2
View File
@@ -36,7 +36,7 @@ public class DbViewer : Window
private int CurrentPage = 1;
private string SimpleSearchTerm = "";
// v1.4.8 H2: opt-in full-text search across the whole DB via FTS5.
// v1.4.8: opt-in full-text search across the whole DB via FTS5.
// Transient UI state (per-session), not persisted -- users opt in fresh
// every time so they always see the page-filter as the default mode.
private bool UseFullTextSearch;
@@ -232,7 +232,7 @@ public class DbViewer : Window
tooltipRight: Language.Page_ArrowRight_Tooltip
);
// Full-text search toggle (v1.4.8 H2). IsFtsIndexBuilt is a cached
// Full-text search toggle (v1.4.8). IsFtsIndexBuilt is a cached
// volatile bool in MessageStore -- single field read per frame, no
// SELECT count(*). ImRaii.Disabled blocks any click while the index
// is still being built, so no defensive force-off branch needed

Some files were not shown because too many files have changed in this diff Show More