81 namespace declarations and 100 using directives converted via sed,
plus two FQN-aliases (ChatTwoPartyFinderPayload in PayloadHandler.cs and
ModifierFlag in KeybindManager.cs) updated. Critical: Language.Designer.cs
and HellionStrings.Designer.cs ResourceManager string arguments updated
synchronously — these are runtime reflection lookups not caught by the
C# compiler.
Two intentional ChatTwo references remain: the legacy migration path
'ChatTwo.json' in Plugin.cs (still points to upstream Chat 2's config
file by design) and the InternalsVisibleTo declaration in
AssemblyInfo.cs (handled in the upcoming repo-folder rename task).
The local alias names 'ChatTwoPartyFinderPayload' and 'ChatTwoConflictDetector'
are preserved as local symbols; only their target namespaces and references
changed.
When the user has both Hellion Chat and upstream Chat 2 installed and
loaded, the parallel chat-window replacement and Hook collisions can
crash FFXIV at frame boundaries. The new detector inspects
IDalamudPluginInterface.InstalledPlugins and throws an
InvalidOperationException with a localized message if Chat 2 is loaded,
which Dalamud surfaces cleanly instead of letting the load proceed
into a runtime crash.
Bilingual messages (EN/DE) follow the existing HellionStrings pattern.
async void Task.Run() is a no-op for awaiting purposes since the void
returns immediately. Switch LoadData to async Task and have the two
callers fire-and-forget the task directly. Exceptions still go through
the existing try/catch inside LoadData.
Split the technical/notification streams (System, Error, Echo, Debug,
NPC announcements, login/logout, retainer sales, gathering system,
glamour notifications, sign messages, alarms, orchestrion, message
book, random number, progress) out of the General tab into their own
System tab. General now shows player conversation plus the active
gameplay events (loot rolls, crafting, gathering, NPC dialogue, party
finder pings) without burying chat under technical chatter.
Spawn six themed tabs out of the box instead of one General catch-all:
General (everything), Free Company (FC chat plus FC announcements and
login/logout), Party (Party, CrossParty, Alliance, PvP team plus loot
rolls), Beginner (Novice Network only when ShowNoviceNetwork is on),
Linkshell (all eight regular and cross-world linkshells together) and
Tell Exclusive (TellIncoming/TellOutgoing as a safety-net catch-all in
case Auto-Tell-Tabs misses one).
Tab names live in HellionStrings (EN/DE). The Tabs settings tab gains a
help-text hint above the list recommending one tab per linkshell when
the user is in multiple, since a single combined Linkshell tab gets
noisy fast for active users.
- Plugin.cs: mark RetentionSweepRunning volatile so the ImGui thread
reads the latest value without a stale register-cached copy
- EmoteCache.cs: reset State to Unloaded on exception so a later
trigger can retry instead of being blocked by the early-out
- Settings.cs: switch the SaveAndClose / Discard buttons to Allman
bracing for consistency with the rest of the file, and include the
ItemSpacing in the Ko-fi-button right-edge calculation
- Privacy.cs: add a saved-policy hint above the manual retention
Ctrl+Shift button so the existing Cleanup wording pattern is
matched here too
- HellionStrings: drop seven unreferenced keys (Theme_Heading,
Migration_Notification_*, Migration_Webinterface_Removed_*,
AutoTellTabs_Migration_*) and their EN/DE values, add the new
Retention_Help_SavedNote string
Audit findings M-3 and M-4. The 24h auto-sweep launched from
Plugin's constructor and the manual button in the Privacy tab were
both starting a background thread that called DeleteByRetentionPolicy
on the shared MessageStore connection without coordinating. With
unfortunate timing — manual click moments after a fresh plugin load
— two sweeps would race for the same connection and the second
would just re-do work the first one already did, while still
overwriting RetentionLastRunAt.
Move the running flag and a lock object to Plugin so both paths see
the same gate. Each entry point takes the lock long enough to check
and set the flag, then runs the actual delete on its background
thread without holding the lock (other DB operations already happen
without locking; spreading the lock further would suggest a
guarantee we do not actually provide). The Privacy tab keeps a
read-only property that surfaces the shared flag for its UI disable
state — ImGui is single-threaded and bool reads are atomic, so the
lock-free read is fine.
The webinterface fields are gone from the Configuration class so any
existing entries (WebinterfacePassword, AuthStore, WebinterfacePort
and friends) get dropped on the next save automatically — Newtonsoft
silently skips properties the target type does not declare. The
version bump itself is what stops the one-shot notification from
firing on every launch.
A new pair of HellionStrings entries (EN + DE) explains the change to
users coming from 0.1.x. Title: "Hellion Chat 0.2.0", body points at
the README for context. Notification fires once per upgrade.
Drops the entire ChatTwo/Http/ tree (ServerCore, HostContext,
RouteController, Processing, SSEConnection, the message protocol DTOs
and the bundled Svelte frontend) plus WebinterfaceUtil. Also removes
every ServerCore.Send* call site that fed the SSE stream:
- MessageManager.ProcessMessage no longer broadcasts new messages
- Chat.cs no longer notifies on login
- PayloadHandler no longer rebroadcasts on screenshot-mode toggle
- ChatLogWindow no longer announces tab and channel switches
The Plugin class drops the ServerCore field, the auto-start branch and
the Dispose hook. The DbViewer still imported a stale namespace from
the message protocol; the using is removed.
Language.resx and its generated Designer file keep the Webinterface
string keys for now so future upstream cherry-picks do not break on
missing resources. They are dead code from our perspective but harmless.
A tester migrating from upstream Chat 2 ended up with a zero-row
database in the new layout: Chat 2 was still loaded when Hellion
Chat first started, the SQLite handle kept chat-sqlite.db locked,
and File.Move silently fell into the catch-all without telling
the user anything. Anyone hitting this would think Hellion Chat
lost their history when it just hadn't been allowed to take it.
Wrap each move on its own so a single locked file no longer
abandons the rest of the migration: the JSON config, font cache
and EmoteCacheV1 directory still travel even if the database
itself is held open. When any IOException fires during the moves,
flag a sticky 30-second warning notification on plugin start that
tells the user exactly what's going on — disable Chat 2, fully
close the game, restart — and points at the README troubleshooting
section.
The README now spells out the migration order step by step in two
sections (fresh install vs. coming from Chat 2) and includes the
manual mv/Move-Item one-liner for both Linux and Windows so users
can recover without waiting for the next plugin update.
True per-window focus-aware transparency would require touching
ChatLogWindow and SettingsWindow individually (both upstream code,
both prone to cherry-pick churn). Instead expose a single opacity
slider that mixes a configured alpha into the WindowBg color in
PushGlobal — applies to every Hellion-rendered pane uniformly,
the game shines through, and form fields / dialogs / popups stay
opaque on top so input remains readable.
Default 92%, range clamped to 0.5–1.0 in the UI and 0x55–0xFF in
the alpha conversion so users can't accidentally make the panes
disappear entirely. Slider sits inside the Appearance section
right under the master Hellion-theme checkbox and is greyed out
when the theme is disabled.
Move from a local color stack inside Hellion-only surfaces to a
single push wrapping Plugin.Draw, so chat log, settings,
viewers, the file dialog and the wizard all render under the same
palette. The local Push() helper stays for explicit use, but the
two existing call sites (Privacy tab, FirstRunWizard) now drop
their local pushes — the global stack already covers them and
double-pushing would shift colors on every frame.
Palette grew from a single cyan accent into a three-tone HUD set:
Primary cyan-teal (#00B8D4) → buttons, checkboxes, slider grabs,
separator hover/active.
Secondary industrial amber → scrollbar grab and resize-grip
(#FFB300) hover/active highlights.
Tertiary slate violet → active title bars and active tabs
(#7B61FF) so identity beats out the cyan
accent without competing with it on
action controls.
Surfaces are deep slate (#0E1A20 windows, #102027 children, #162831
frames) with steel borders (#37474F). Style variables flatten the
default Dalamud rounding into something more geometric: 4 px window
rounding, 2 px frame/grab/tab/scrollbar, 1 px borders.
A new Configuration.HellionThemeEnabled (default true) and a
matching Appearance section at the top of the Privacy tab let users
turn the whole thing off and fall back to the Dalamud default look.
The flag is checked once per frame in Plugin.Draw — `using
IDisposable? _ = ... ? PushGlobal() : null` — so disabling has zero
overhead beyond a bool check.
The plugin list expects a main UI entry point so the "Open Plugin"
button has something to fire on. Hook UiBuilder.OpenMainUi to a
toggle of the settings window — same surface as OpenConfigUi, just
exposed under both buttons. Wiring lives in Plugin.cs (not the
inherited Settings.cs) so future upstream cherry-picks don't fight
with this addition.
Fresh installs now open a setup window on first plugin load that
asks the user to pick one of three starting profiles. Existing
ChatTwo users keep skipping the wizard because the v6→v7 migration
sets Configuration.FirstRunCompleted = true on the same pass that
seeds the Privacy-First defaults — they already saw the migration
notification and can reopen the wizard from the Privacy tab if
they want to choose differently.
The three profiles map to concrete configuration sets:
Privacy-First (recommended): own-conversation whitelist (30
channels), retention enabled with the spec defaults (Tells 365
days, own-conversation channels 90, fallback 30).
Casual: Privacy-First plus public chat (Say/Shout/Yell, both
emote types, Novice Network) with a 1-day retention window so
RP players can scroll back the last scene without keeping
third-party speech forever.
Full History: filter off, retention off, GDPR warning shown
inline. Behaves like upstream Chat 2.
The wizard window is non-modal but covers a wide layout (three
side-by-side cards) and closing it without picking anything is
treated as accepting whatever defaults are already in place. The
Privacy tab gains a "show wizard again" button at the top so the
choice is reversible.
Add HellionStrings.resx as the English source and HellionStrings.de.resx
for German, with a hand-maintained Designer.cs that mirrors the layout
of Language.Designer.cs. Resource files live next to the upstream
Language.resx but are kept entirely separate so upstream cherry-picks
never collide with our translations and any future Hellion-only
translation tooling (Crowdin, manual contribution) can target this
file without touching the Chat 2 dictionary.
Plugin.LanguageChanged now updates HellionStrings.Culture alongside
Language.Culture so every UI string flips to the active locale at the
same moment. The Privacy tab title, master switch, channel groups
(now resolved per frame so the language can change without restart),
preset buttons, failsafe toggle, retention section, cleanup section,
status messages and notification bodies all read from HellionStrings.
The migration toast also takes its title and body from there.
Translations follow the project's German style: Du-Form, full
diacritics (ä, ö, ü), no em-dashes inside flowing prose, "Whitelist"
and "Linkshell" kept as-is because they are the established terms.
GetRetentionDays previously dropped straight from a missing user
override to RetentionDefaultDays, so every channel showed 30 days
in the UI even though the spec lists 365 for Tells and 90 for own-
conversation channels. Insert a middle layer: user override → spec
default → global default. The retention sweep now seeds its policy
from PrivacyDefaults.DefaultRetentionDays first and lets explicit
user overrides win on top, and the per-channel UI tags each row as
[override], [spec], or [global] so the source of the value is
visible without guessing.
Privacy filter trimmed history "by what" — this adds the time axis.
Each ChatType gets its own retention window in days; channels
without an explicit override fall back to a configurable global
default. The master switch defaults to OFF: the plugin never
deletes history without explicit user consent.
MessageStore.DeleteByRetentionPolicy builds an OR'd WHERE clause
over (ChatType = X AND Date < cutoff_X) plus a NOT IN catch-all
for the global default, hard-deletes matches, and only runs VACUUM
when something was actually removed.
Plugin.RunRetentionSweepIfDue runs at most once per 24 hours on a
background thread (off the load path) and persists the timestamp
so subsequent restarts skip the sweep until enough time has
passed. The Privacy tab gains a retention section with the master
switch, default-days input, per-channel override tree, reset
buttons, and a Ctrl+Shift "apply now" action that mirrors the
auto-sweep but on demand.
Spec defaults: Tells 365 days, own-conversation channels (Party,
Cross-Party, Alliance, PvP Team, FC, Linkshells 1-8, Cross-World
Linkshells 1-8, ExtraChat 1-8) 90 days, fallback 30 days.
The original migration skipped the database move whenever
pluginConfigs/HellionChat already existed. In practice that
directory is materialised by Dalamud before our plugin constructor
runs, so the check was effectively always true and the legacy
chat-sqlite.db plus the EmoteCacheV1 directory stayed behind.
Walk the legacy directory entry by entry instead. Move every file
or subdirectory whose name is not already present on the target
side, then delete the legacy directory if it ends up empty. This
handles both fresh installs and the realistic case where Dalamud
has pre-created an empty config directory for the new plugin.
Switch the assembly name to HellionChat so Dalamud uses
pluginConfigs/HellionChat for our config file and database
directory, instead of sharing those locations with the upstream
Chat 2 plugin. Code namespace stays ChatTwo.* so upstream
cherry-picks apply cleanly.
Rename the DalamudPackager manifest to HellionChat.yaml with
fork-specific name, author, repo URL, description, tags and
changelog. Plugin.PluginName becomes "Hellion Chat".
Add a one-shot migration in the plugin constructor that runs
before GetPluginConfig: if pluginConfigs/ChatTwo.json or the
ChatTwo/ directory exist and our equivalents don't, move them
into the HellionChat layout. Idempotent: only fires on the first
load where legacy paths are present and ours are not.
Introduce an opt-out channel whitelist so the database only persists
messages from channels the user explicitly wants to keep. Default
profile follows GDPR data minimization: own conversations only
(Tells, Party, FC, Linkshells, Cross-World Linkshells, Alliance,
ExtraChat). Public chat (Say/Shout/Yell), Novice Network, NPC
dialogue and system logs are dropped by default.
The filter sits inside MessageStore.UpsertMessage so any current or
future write path is covered uniformly. Configuration provides an
IsAllowedForStorage(ChatType) helper plus a "persist unknown
channels" failsafe (default off) for ChatTypes added by future
patches.
A new Privacy settings tab exposes the whitelist as grouped
checkboxes with three preset buttons (Privacy-First, Clear all,
Select all). Configuration version bumps from 6 to 7; existing
users are migrated to the Privacy-First defaults on first load
and notified once via the Dalamud notification manager.
Also includes a small .env.example and gitignore hygiene for local
development setup.
- Plugin commands trigger the command helper window now
- Fix auto translation with empty text appearing
- Switch up all dalamud payload usage to ROSS if possible
- Prepare 7.5 changes
- Cleanup