Aligns the first-run tab layout with the sharpened defaults that
external testers asked for. Three changes in one commit:
1. VanillaGeneral now contains only Say/Yell/Shout. The previous
30-channel kitchen-sink (party, FC, every linkshell, all gameplay
events) buried the actual immediate-surroundings conversation
under loot rolls, crafting and PF pings.
2. HellionSystem absorbs the gameplay-event streams that used to live
in General — NpcDialogue, LootNotice, LootRoll, Crafting, Gathering,
PeriodicRecruitmentNotification — plus the announcement and battle-
system noise (BattleSystem, FreeCompanyAnnouncement,
PvpTeamAnnouncement) that previously had no fixed home.
3. The first-run / wipe default no longer adds HellionBeginner
conditionally and no longer adds a static VanillaTellExclusive tab.
Auto-Tell-Tabs spawns per-conversation tabs on demand, the static
tell-bucket is redundant. NoviceNetwork users can still add the
Beginner preset from Settings -> Tabs.
A new v12 -> v13 migration triggers a hard tab-wipe on existing
installs because per-channel mapping from the old General preset to
the new General/System split is ambiguous. The wipe scope is
narrow: only Config.Tabs is cleared, every other knob (Privacy,
Retention, Theme, etc.) keeps its current value. A pre-v13 backup
of the live config is written alongside it for manual restore.
Users see the existing SettingsRefactor migration notification.
Replaces the inherited Chat-2 IPC guide with a guide that follows the
Hellion README structure: top-level intro, a dedicated "Compatibility
with Chat 2" section that calls out exactly what the v1.0.0 rename did
(and did not) change, a channel-reference table, per-surface sections
with separate "Migration from Chat 2" diff blocks, and a closing
license/attribution paragraph that points back to NOTICE.md.
Content additions on top of the previous version:
- Explicit statement that the IPC surface is the Chat-2 surface re-
published under the Hellion name. Tuple shapes, lifecycle and call
semantics are unchanged; only the channel-string prefix differs
- Channel-reference table at the top so integrators can see the full
surface at a glance instead of reading two long code samples
- Tuple-payload field table for the Typing State IPC with a short
meaning per field
- Behavior notes for ChatInputStateChanged (fires once on subscribe,
then only on real changes) so integrators don't need to read the
source to learn the contract
- Explicit migration-diff blocks for both surfaces
Existing example code is preserved with the same identifiers; only
the host-class names are aligned to "HellionChat..." instead of
the old "ChatTwoIpc"/"TypingIntegration" placeholders.
ipc.md guides third-party plugin authors who want to bind to our
context-menu IPC and our typing-state IPC. After the v1.0.0 channel
rename (ChatTwo.* → HellionChat.*) the example code in this file no
longer matched the channels the plugin actually exposes — third-party
authors who copy-pasted from the doc would see silent no-op subscriptions.
Updated:
- All 6 channel string literals in code samples (Register, Unregister,
Invoke, Available, GetChatInputState, ChatInputStateChanged)
- Code-path references in the Typing State IPC explanation block
(HellionChat.Code.ChatType, HellionChat/Configuration.cs etc.)
- Class/variable name in the example (ChatTwoIpc → HellionChatIpc)
- Prose references to the plugin name
Added a short migration note at the top so existing integrators see
the rename in one paragraph instead of having to diff the file.
Brings the feature/hellionchat-rebrand-v1.0.0 branch into main as the
first fully standalone Hellion Chat release.
Includes:
- Rebrand from ChatTwo.* fork identity to standalone HellionChat
identity (namespace, repo folder, IPC channels, ImGui IDs, build
pipeline)
- Runtime detector that refuses to load the plugin when upstream
Chat 2 is also active, preventing the parallel-load FFXIV crash
- Three critical and twenty-one major pre-existing defects fixed
in the same release (CodeRabbit-flagged): AABB overlap correctness,
Equals/GetHashCode anti-pattern, IPC dispose mismatch, IDisposable
contract on DebuggerWindow, ExtraChat / GameFunctions null-deref
guards, AutoTranslate concurrent-access lock, Privacy retention
bounded waits, EmoteCache and FontManager HttpClient leaks,
SearchSelector ImGui ID collision, DbViewer count caching, plus
Sheets/Tabs/Popout bounds checks and the IconUtil binary-search
off-by-one
- SQLite native binary pinned to 3.50.3 (CVE-2025-6965, CVE-2025-7709)
- packages.lock.json now enforced via RestorePackagesWithLockFile
- Public-facing branding text aligned to standalone framing while
EUPL-1.2 license attribution is preserved unchanged
Verified locally on KAZAMA via dotnet build and manual FFXIV
smoketests A/B/C plus the post-fix sweep test.
The original v1.0.0 changelog only documented the rebrand. After the
CodeRabbit pass added 9 follow-up fix commits (3 critical bugs plus
21 major findings, grouped into safety / crash-class / correctness /
threading / resource / performance categories), the changelog needs
to reflect what users are actually receiving.
yaml + repo.json synchronized.
The DB export loop called filteredHistory.Count twice per 5000-message
batch — once for the progress fraction, once for the status text.
filteredHistory is built lazily by Filter() and re-enumerates on every
.Count access, so on a 2M-message history each batch was paying for
two full passes through the IEnumerable. Materializing the count once
at the top reduces the export to a single O(N) traversal as intended.
The RunOnTick + delayTicks pacing is intentional (keeps SeString
encoding on the framework thread and rate-limits the export to avoid
laggy frames during long exports), so the rest of the loop stays put.
Two pre-existing build/security defects flagged by CodeRabbit:
- HellionChat.csproj sets RestorePackagesWithLockFile=true so dotnet
restore honors the committed packages.lock.json. Floating version
ranges in the lockfile previously could drift between machines or
CI runs, producing builds with subtly different transitive
dependencies
- HellionChat.csproj pins SQLitePCLRaw.lib.e_sqlite3 to 3.50.3 to
override the older 2.1.11 native build that
Microsoft.Data.Sqlite 10.0.7 transitively pulls in. Ships SQLite
3.50.3 which contains the fixes for CVE-2025-6965 (memory
corruption from aggregate-term overflow) and CVE-2025-7709. The
managed Microsoft.Data.Sqlite wrapper stays on 10.0.7 — only the
native binary is bumped, no API breakage. Verified via the NuGet
spec: "the first three numbers in the version number of this
package indicate the version of SQLite that was used to build it"
- EmoteCache.cs replaces the per-call "new HttpClient()" with the
existing static Client field. The static instance already exists
for two other endpoints in the same file and reuses connection
pooling; the third call site was a stray that leaked a socket
on every emote download
- FontManager.cs wraps both the HttpClient and the HttpResponseMessage
in using-blocks, replaces the .Result/AggregateException sandwich
with GetAwaiter().GetResult() for clean exception propagation, and
adds EnsureSuccessStatusCode so failed downloads don't silently
produce a zero-byte font file. Full async refactor of the FontManager
constructor is tracked separately
- Util/AutoTranslate.cs introduces a single EntriesLock object and
serializes every read and write of the static Entries dictionary
and ValidEntries hash set behind it. PreloadCache spawns a worker
thread that fills both while the main thread reads them via the
Matching / ReplaceWithPayload / StartsWithCommand entry points;
without the lock the underlying collection access was undefined.
AllEntries() splits into a thin lock wrapper plus a private
BuildEntriesLocked() helper that runs under the lock
- Ui/SettingsTabs/Privacy.cs bounds the .Wait() on the framework
refresh after a manual retention sweep and after the privacy
cleanup. A hung framework tick previously could deadlock the
background worker thread. Five-second timeout, log on miss
- Util/SearchSelector.cs ImRaii.PushId(id) collapsed every row in the
filtered list to the same ImGui ID, leaving the ID stack ambiguous
for click resolution. Mix the row index into the pushed id so every
Selectable has a distinct ImGui identifier
- Ui/SettingsTabs/Chat.cs blocked-emote add-button never opened the
selector popup because SearchSelector.SelectorPopup is wrapped in
ImRaii.ContextPopupItem (right-click semantics). Detect the
IsItemClicked() event after the button and call ImGui.OpenPopup
explicitly so left-click opens the picker too
- Ui/Debugger.cs DebuggerWindow now declares IDisposable so the
existing Dispose() method participates in disposal patterns
(using-blocks, container cleanup). Previously the method existed
but the type didn't advertise it, so callers had no way to invoke
it correctly and the command-handler subscription leaked
- Resources/Language.zh-Hans.resx Webinterface_Start_Success
contained "网页界面已停止。" (web interface stopped) for what is
semantically the start-success message; corrected to
"网页界面已启动。" (web interface started). String is unused in
the Hellion fork (webinterface removed) but remains in the
resource bundle for upstream compatibility
Two pre-existing upstream defects fixed in v1.0.0:
- Ui/Popout.cs PopOutDocked[Idx] now bounds-checks Idx against
ChatLogWindow.PopOutDocked.Count before reading or writing. A
popout instance can outlive a list resize when AddPopOutsToDraw()
rebuilds the docked-state list while a draw frame is in flight,
which previously produced an out-of-range crash on tab drop
- Ui/SettingsTabs/Tabs.cs guards against an empty worlds list before
indexing worlds[selectedWorld]. Empty lists can occur briefly when
switching characters or before the datacenter sheet finishes
loading — the previous code would crash with an
ArgumentOutOfRangeException
Four pre-existing upstream defects fixed in v1.0.0:
- Util/GlobalParametersCache.cs GetValue captures Cache into a local
before the bounds check, so the check and the indexed read operate
on the same array reference even when Refresh reassigns Cache from
the main thread between the two operations
- Util/IconUtil.cs binary search bounds: hi initialized to
entries.Length-1 (was Length), and reset on redirect-restart;
added entries.Length==0 short-circuit to prevent indexing into
empty arrays
- Sheets.cs WorldsOnDatacenter compared Region.RowId, which groups
by region instead of datacenter — now compares DataCenter.RowId
directly so the result actually reflects same-DC worlds
- Message.cs back-reference loop iterates the processed Sender/Content
properties rather than the raw constructor parameters, so chunks
added or replaced by CheckMessageContent also get Message set
Three pre-existing upstream defects flagged by CodeRabbit, fixed in the
v1.0.0 standalone cut where we own the codebase:
- Ipc/ExtraChat.cs Dispose now unsubscribes all three IPC subscriptions
(OverrideChannelGate, ChannelCommandColoursGate, ChannelNamesGate)
instead of only the first; previously the latter two leaked their
subscriptions on every plugin reload
- GameFunctions/Types/TellTarget.cs FromTarget guards against a zero
IPlayerCharacter.Address before dereferencing the unsafe Character*
cast; previously a missing/destroyed target object would crash the
game on /tell construction
- GameFunctions/GameFunctions.cs ResolveTextCommandPlaceholderDetour
null-checks the Hook reference before calling .Original instead of
using the null-forgiving operator; defensive guard for teardown races
UnregisterGate is registered via RegisterAction(Unregister) on
construction (Unregister returns void), but Dispose was calling
UnregisterFunc() instead of UnregisterAction(). The mismatched
unregister call leaks the action subscription on plugin reload —
subsequent Dispose/Init cycles would accumulate orphan handlers
in the Dalamud IPC layer.
Pre-existing upstream issue (CodeRabbit critical finding); fixed in
v1.0.0 standalone cut where we own the codebase.
Equals(object?) was delegating to GetHashCode() comparison, which is
the textbook hash-collision anti-pattern: two distinct ChatCode values
could in principle share a hash and be wrongly reported as equal. The
current GetHashCode implementation packs Type/Source/Target into 24
bits and happens to be collision-free, but the contract is fragile —
any future change to GetHashCode silently breaks Equals.
Replaced with direct field-by-field comparison of Type, Source, Target.
GetHashCode is left unchanged so dictionary/HashSet behavior stays
identical.
Pre-existing upstream issue (CodeRabbit critical finding); fixed in
v1.0.0 standalone cut where we own the codebase.
The previous implementation nested a ValueInRange helper that used
strict inequalities at both ends. That dropped identical rectangles
and shared-edge cases as false negatives — two rectangles with
a.X == b.X would miss the overlap on the X axis even when they
clearly overlapped.
Replaced with the standard AABB test: rectangles overlap iff they
overlap on both axes (a.Min < b.Max && a.Max > b.Min per axis).
Pre-existing upstream issue (CodeRabbit critical finding); fixed in
v1.0.0 standalone cut where we own the codebase.
AssemblyVersion and TestingAssemblyVersion bumped to 1.0.0.0,
DownloadLinks (Install/Update/Testing) all point to
releases/download/v1.0.0/latest.zip, Changelog block mirrors
HellionChat.yaml.
Updates the project root namespace and replaces the historical comment
about cherry-pick compatibility — that compatibility was the rationale
for keeping ChatTwo.* in source while AssemblyName was already
HellionChat. With v1.0.0 the standalone cut is complete and both
identifiers match.
Note: this commit also fixes the runtime MissingManifestResourceException
that the previous Task 6 commit caused — the embedded resource prefix
is derived from RootNamespace at build time, so Designer.cs string
arguments and the actual resource names only line up once both are set
to HellionChat.
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.
README, repo.json and HellionChat.yaml describe the plugin as a
chat-replacement based on Chat 2 rather than as a Chat-2 fork. License
attribution (NOTICE.md, COPYRIGHT, THIRD_PARTY_NOTICES.md, README
credits section) is left untouched per EUPL-1.2 obligations.
Architecture section in README updated to reflect that the
HellionChat namespace is now consolidated (post-v1.0.0).
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.
All 6 inter-plugin communication channels are renamed for the v1.0.0
standalone cut. Prevents Dalamud IPC registration conflicts when a user
has both Hellion Chat and upstream Chat 2 installed.
- IpcManager: Register, Available, Unregister, Invoke
- TypingIpc: GetChatInputState, ChatInputStateChanged
Breaking change for third-party plugins that bound to ChatTwo.* — none
known at the time of this commit.
- Visible pop-out icon button in chat header toolbar (right-aligned)
- One-time hint banner introduces toolbar + right-click and the v0.6.1 default flip
- Settings → Chat → Auto-Tell-Tabs → "Open new /tell tabs directly as pop-out"
- PopOutInputEnabled hard-flipped to true via v11 → v12 migration
- Bugfix: pop-out windows of LRU-dropped or logout-stripped temp tabs are now properly torn down (no more ghost windows)
- Bugfix: dead zone below chat input bar when v0.6.0 hint banner was visible (also fixes Jin's report on the v0.6.0 in-pop-out banner)
- CI: fix release.yml YAML parse failure (heredoc footer extracted to .github/release-footer.md), add workflow_dispatch recovery trigger
- README + SUPPORT.md + repo.json + yaml: Hellion Forge Discord link
Two opt-in UX features. Existing users see no change unless they
enable the new toggles.
- Pop-out input: global master switch in Settings → Window → Frame.
When enabled, every pop-out window grows a compact input bar
(channel-coloured icon + text input). Independent text buffer and
history cursor per pop-out; channel changes apply globally.
- Chat colour presets: seven built-ins above the per-channel colour
list — ChatTwo Default, High-Contrast, Pastell, Dark-Mode-Tuned,
Hellion (brand), Night Blue (bonus), Indigo Violet (bonus).
Configuration migrates from v10 to v11 with a diagnostic log.
Closes the remaining gaps in GitHub's community-standards check, adds
explicit privacy and dependency documentation matching the plugin's
"DSGVO-by-design" claim, and removes the stale upstream Crowdin
artefact so the repo no longer suggests it ships its own translation
pipeline.
New community-health files:
- CODE_OF_CONDUCT.md: project-specific, short and direct, single
reporting path to kontakt@hellion-media.de
- CONTRIBUTING.md: scope, accepted vs declined contributions, build
and test instructions, EUPL-1.2 contribution terms, translation
policy split between Hellion-specific (here) and upstream strings
(Chat 2 repo)
- SUPPORT.md: routing for bugs, security, privacy and casual feedback
- .github/PULL_REQUEST_TEMPLATE.md: summary, change-type checklist,
testing notes, compatibility notes for migrations and manifest
fields, contribution checklist
- .github/FUNDING.yml: comments-only file, no platforms enabled,
points donors at the upstream Chat 2 maintainers' Ko-fi pages
New privacy and compliance documentation:
- PRIVACY.md: what the plugin stores locally (config, SQLite,
EmoteCacheV1), retention defaults, the two outbound network calls
(BetterTTV API+CDN with ShowEmotes opt-out, Square Enix Lodestone
font once-off), explicit no-telemetry statement, GDPR
Art. 15/17/18/20/21 rights mapped to plugin features, third-party
privacy-policy links
- THIRD_PARTY_NOTICES.md: direct NuGet dependencies with versions
pinned to v0.5.4 (MessagePack, Microsoft.Data.Sqlite, morelinq,
Pidgin, SixLabors.ImageSharp under Six Labors Split License 1.0),
Dalamud SDK and .NET tooling, bundled Exo 2 font (OFL-1.1) and
plugin icon, network-touch status per component, re-audit commands
Crowdin cleanup:
- crowdin.yml deleted (was upstream Chat 2's project_id 663694,
pointed at /ChatTwo/Resources/Language.resx, never wired to
HellionChat strings)
- README, CONTRIBUTING and CODE_OF_CONDUCT no longer suggest
HellionChat operates a Crowdin project; remaining mentions are
explicitly framed as upstream Chat 2's workflow
Contact and version consistency:
- Maintainer email switched from maintainer@hellion-media.de to
kontakt@hellion-media.de in SECURITY.md and NOTICE.md
- README version references updated to 0.5.4 (header, project status
block) and the update-tag pattern generalised from v0.1.x to v0.X.Y
- bug_report.yml version placeholder bumped to 0.5.4
- Project-documents table added to README footer linking all health
and reference files in one place
Release-body automation:
- .github/workflows/release.yml now extracts the matching version
block from ChatTwo/HellionChat.yaml's changelog and combines it
with a static install / docs footer (custom-repo URL, project
document links, licence) before passing the result to
softprops/action-gh-release@v3 via body_path
- Workflow fails fast if no changelog block exists for the tagged
version, automating the existing "yaml + repo.json + release body
kept in sync" rule
- Tag value passed via env: TAG_NAME with strict ^v\d+\.\d+\.\d+$
validation before any string concatenation, so the tag input cannot
break out into shell evaluation
The pointer-arithmetic CodeQL alert kept re-firing on each shape of
the previous shallow fix because Encoding.GetBytes is virtual and
every length value derived from its return inherited the taint.
Refactor the routine to thread int offsets through index-based
control flow and only compute pointers inside two small helpers
(CalcWordWrap and DrawText) that take an already-pinned base pointer
plus offsets sourced from local logic, not from any virtual return.
Buffer is now allocated against Encoding.UTF8.GetMaxByteCount via
ArrayPool with a real 16 KiB upper bound, and the encoded length
returned by GetBytes is validated against that ceiling before
anything touches the pointer. Behaviour is byte-identical to v0.5.3,
verified locally with the same input shapes the previous code path
handled.
Slim changelog: trimmed the per-version blocks down to v0.5.1-v0.5.4
plus a link to GitHub releases for older history. The previous block
ran ~9000 characters and was dragging the manifest payload down for
no benefit; users see the latest release block first anyway.
Single-fix patch to close the CodeQL pointer-arithmetic alert that
v0.5.2 left open. v0.5.2 already shipped, so we tag forward instead
of moving the published tag.
CodeQL re-opened the unvalidated-pointer-arithmetic alert at the new
textEnd line because Encoding.GetBytes is a virtual method on
Encoding and the returned array's Length is therefore tracked as
untrusted input for pointer arithmetic.
Compute the expected byte count from the same encoder via
GetByteCount and bail out if the actual buffer length does not match.
That is a real consistency check that would catch a maliciously
swapped Encoding.UTF8 instance, not a dead defensive guard. The
empty-split early-out from the previous fix is folded into the same
condition.
Three packaging defects rolled into one fix:
- The custom DalamudPackager.targets override forced HandleImages and
ImagesPath through the legacy code path. SDK 15 handles images by
default and the override produced an output manifest with neither
IconUrl nor ImageUrls populated. Removed.
- The csproj only included images/icon.png explicitly via
<None Include>, so chatWindow.png and withSimpleTweaks.png never
reached the build output and never made it into the release ZIP
either. Switched to a glob include.
- HellionChat.yaml carried no icon_url / image_urls, so even after
the SDK started writing the manifest correctly, both fields stayed
unset. Added them pointing at the public raw.githubusercontent
URLs that already work for the repo.json IconUrl.
Net effect on a fresh release: Dalamud picks up the icon next to the
DLL on dev installs, the plugin-installer card shows the proper
HellionChat logo for users coming through the custom repo, and the
two screenshot images are listed alongside the description so the
plugin installer carousel works the way other Dalamud plugins look.
Two CodeQL alerts opened against the codeql-manual-build workflow's
first scan. Both real, both small fixes.
#1 Medium / Workflow does not contain permissions
build.yml runs read-only against the repo (no push, no release
creation, no API mutations) but never declared a permissions
block, so the default GITHUB_TOKEN scope applied. Pin to
contents: read at workflow level. Release and CodeQL workflows
already have their explicit minimal scopes.
#2 Critical / Unvalidated local pointer arithmetic
ImGuiUtil.WrappedTextWithPos splits its input on newlines and
passes each part through Encoding.UTF8.GetBytes inside a fixed
block. Empty splits (consecutive newlines, blank lines) produced
a zero-length byte array, fixed gave us a valid pointer, and
textEnd = text + bytes.Length collapsed onto text. The downstream
ImGuiNative.CalcWordWrapPositionA calls received identical start
and end pointers, which is undefined behaviour at the native
boundary even if it happens to no-op on the current ImGui build.
Bail before entering the fixed block when bytes.Length == 0 and
render an empty line for the gap, which is what the original
text == null guard was trying to do but could never reach inside
a fixed block over a non-null array.
Three real-world adjustments to the default config that ships with a
fresh install:
- HellionThemeWindowOpacity 0.92 -> 0.5 so a fresh install lands at
the more glass-like default the maintainer uses daily
- Use24HourClock false -> true to match a German / European locale.
Works correctly thanks to the v0.5.1 strict-format fix that uses
CultureInfo.InvariantCulture instead of the host culture
- HellionParty preset Channel: InputChannel.Party -> null. Auto-
routing /party into a tab that also collects /alliance and /pvpteam
surprises the user when they wanted to type into the other ones;
the tab stays as a read surface
LoadPreviousSession and FilterIncludePreviousSessions stay false to
keep the privacy-strict 'every session starts fresh' line. The
maintainer's personal settings flip them on, but that's an
opt-in choice, not a default we should ship to every fresh install.
RetentionEnabled also stays false for the same opt-in reason.
Four defaults now match what a daily-driver Hellion install ends up at
anyway, so a fresh install does not feel like the wrong product:
- HellionThemeWindowOpacity 0.92 -> 0.5 (more glass-like)
- LoadPreviousSession + FilterIncludePreviousSessions false -> true
(tabs pick up where they left off after a crash or restart). The
privacy filter still gates what goes into the store; loading what
is already in there is not an additional privacy cost.
- Use24HourClock false -> true (matches a German / European locale,
works with the strict CultureInfo.InvariantCulture format from the
v0.5.1 fix).
RetentionEnabled stays at false because that one is a documented
opt-in privacy line, not a UX default. The persistent retention sweep
should require an explicit user gesture even though my own install
has it on.
The default GitHub-managed CodeQL setup builds C# without the Dalamud
assemblies (they live in user AppData, not in the repo or in NuGet),
so call-target resolution sits at 64% and the analysis tile reports
'Low C# analysis quality'. This workflow runs the same Dalamud staging
download we use for the regular build before the CodeQL build step,
which gives the analyser a fully-resolved compilation and pushes both
quality metrics above the 85% thresholds.
Two jobs:
- analyze-csharp on windows-latest with build-mode: manual and the
security-extended query suite, so we get the full SQL-injection,
path-traversal and crypto-misuse rule set on a clean compilation
- analyze-actions on ubuntu-latest with build-mode: none, scans the
workflow files in .github for action-injection patterns
Schedule runs Mondays at 06:17 UTC (low-traffic window).
The repo's CodeQL default setup needs to be switched to advanced in
Settings -> Code security before this workflow takes over, otherwise
both run in parallel and we waste runner minutes.
The live tell that triggers an Auto-Tell-Tab spawn is already in the
message store by the time MessageProcessed fires, because
MessageManager calls Store.UpsertMessage on line 266 before invoking
the event on line 277. PreloadHistory therefore picked up the live
tell as the youngest historic message and the separator landed below
it instead of above.
Pass the live message id through SpawnTempTab into PreloadHistory and
filter it out of the result. Pull one extra row so a successful
exclude does not cost the user a preload-budget slot.
Seven badges covering build status, CodeQL scanning, license, latest
release, Dalamud API level, .NET version and FFXIV expansion. Quick
visual indicator that the plugin is healthy and which tooling
generation it targets, plus shortcut links to the workflow runs and
security findings.
LICENSE now starts with the EUPL-1.2 standard header so github-linguist
detects the licence correctly in the repo header. The dual-copyright
block (upstream ChatTwo authors plus Hellion Online Media) moves into a
new COPYRIGHT file referenced from the README. NOTICE.md and
UPSTREAM_SYNC.md stay as-is.
New files under .github:
- workflows/build.yml: validates every push to main and every PR
against the current Dalamud staging branch on a Windows runner
- workflows/release.yml: builds Release on every v* tag, locates the
DalamudPackager latest.zip and attaches it to the matching GitHub
Release via softprops/action-gh-release
- dependabot.yml: weekly NuGet sweeps and monthly GitHub Actions
sweeps with conventional-commit prefixes, grouped patch and minor
PRs to cut review noise
- ISSUE_TEMPLATE/bug_report.yml + feature_request.yml + config.yml:
structured intake that pushes security reports through the private
advisory flow and routes upstream-only issues to ChatTwo
- SECURITY.md: documents the vulnerability reporting channels, scope,
and target disclosure window
The release workflow replaces the previous manual upload step. Tag a
release and the ZIP shows up on the release page automatically.
Drop the test project from version control. Upstream Chat 2 also
keeps ChatTwo.Tests outside the public repo, and the test sources
need a Dalamud assembly bundle that only resolves on a configured
Windows dev box anyway. The files stay on disk for local runs but
no longer ship with the source.
The LICENSE file added in ad2feb5 carries the dual-copyright block
(upstream ChatTwo authors plus Hellion Online Media) and is the one
the README now points at. The original Upstream-only LICENCE was a
verbatim copy of the EUPL text without our Hellion attribution and
became redundant the moment LICENSE landed. GitHub also prefers the
LICENSE filename for its license-detection in the repo header.
Three new top-level files plus README update in preparation for
leaving the GitHub fork network:
- LICENSE: full EUPL-1.2 text plus dual copyright notice (upstream
ChatTwo authors and Hellion Online Media). README previously
pointed at a non-existent LICENCE file, fixing that compliance
gap was overdue regardless of the fork-network decision.
- NOTICE.md: acknowledgements addressed directly to Infi and Anna,
honest framing of why the fork exists alongside upstream rather
than trying to displace it, plus maintainer contact channels for
attribution or takedown questions.
- UPSTREAM_SYNC.md: documents the manual cherry-pick workflow with
-x authorship preservation, the conflict-handling policy, and
what we will and will not pull from upstream. Replaces the
GitHub-Fork sync UI we will lose after detaching.
- README.md: version bump to 0.5.1, fork-network detach note, link
to NOTICE.md and LICENSE, fixed the LICENCE / LICENSE typo.
Both PRAGMA call sites take values that SQLite does not accept as
bound parameters. ColumnExists takes a hardcoded table name, the
migration call takes a compile-time int from the version sequence.
Comments now state both facts so future readers don't try to wedge a
defensive whitelist into a path that cannot be reached from anywhere
user-controlled.
Per-channel WHERE tuples and the catch-all default-clause now bind
ChatType and cutoff via named parameters instead of being inlined as
literals. Combines BindIntList for the explicit-types exclusion with
explicit AddWithValue for each (type, cutoff) tuple. Behavioural diff
against v0.5.0: none — same retention windows, same cutoff math, just
parameterised.
Migrate CleanupRetainOnly, StreamForExport, CountDateRange, GetDateRange
and GetPagedDateRange from interpolated IN lists onto BindIntList.
Eliminates the string-interpolation pattern for SQL value lists in the
IN-clause sites. Behavioural diff against v0.5.0: none — same enum/byte
values, just bound under named parameters instead of inlined.
Centralised builder for dynamic IN-clauses that binds each value as a
named parameter and returns the comma-joined placeholder string. Used
by the upcoming MessageStore migrations away from string-interpolated
SQL.
The text-disabled colour alone made greeted tabs visually weak in the
sidebar. Push dimmed Header and HeaderHovered values alongside the
existing Text push so the selected and hovered states match the
greeted state too. Idle state stays untouched because ImGui Selectable
has no idle background slot.
The preview block caches the deletion estimate from the last refresh.
When the user toggles whitelist channels afterwards the cached number
no longer reflects the current selection. Snapshot the whitelist on
refresh and detect drift on every frame; on drift, grey out the counts
and surface a stale hint plus an emphasised refresh button. Sits
alongside the existing Cleanup_Help_SavedNote, which warns about a
different mismatch (mutable vs saved) and stays as-is.