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.