docs: restructure into docs/ folder, add roadmap and learning notes
- Move AI_DISCLOSURE, THIRD_PARTY_NOTICES, UPSTREAM_SYNC, ipc.md into docs/ (ipc.md renamed to IPC.md for consistency) - Add docs/ROADMAP.md, docs/CHANGELOG.md, docs/CONTRIBUTORS.md, docs/LEARNING-JOURNEY.md - Update README to reflect the v1.0.0 standalone state, drop the development section, refresh the architecture tree, add a release-cadence block linking to LEARNING-JOURNEY - Fix stale ChatTwo/* source paths to HellionChat/* across docs - Update cross-links in PRIVACY, CONTRIBUTING and .github/* so they point at the new docs/ paths Pure documentation pass, no code changes.
This commit is contained in:
@@ -0,0 +1,77 @@
|
||||
# AI assistance disclosure
|
||||
|
||||
This fork uses AI assistance per the [Dalamud Plugin AI Usage Policy](https://github.com/goatcorp/DalamudPluginsD17/)
|
||||
at the **Pair** level.
|
||||
|
||||
A note up front: Hellion Chat is currently in a rebuild and adjustment
|
||||
phase, and there are no plans to submit it to the Dalamud team for review
|
||||
while it stays standalone. If the plugin stays out of the official repo I
|
||||
technically wouldn't need to disclose any of this, but I'd rather be
|
||||
upfront about how it's built.
|
||||
|
||||
Hellion Chat is my entry point into game modding and plugin development. I
|
||||
have never written a plugin for a game before. I work alone, so I get help
|
||||
where I need it. That's not something I want to hide.
|
||||
|
||||
## How I actually work
|
||||
|
||||
I plan the architecture, decide what gets built, and own every design
|
||||
decision. For each change I:
|
||||
|
||||
- Read the code Claude drafts before I integrate it
|
||||
- Test with my own tooling and in the running game
|
||||
- Read the Dalamud log output to verify behaviour
|
||||
- Run security and privacy audits on anything that touches user data
|
||||
|
||||
One of the main reasons I use AI is consistency. I want the Hellion code to
|
||||
match the style of the upstream Chat 2 codebase and stay readable for
|
||||
anyone who opens the repo, not just for me. Claude helps me catch when I'm
|
||||
drifting from upstream conventions or writing something that only makes
|
||||
sense in my own head.
|
||||
|
||||
The balance is shifting toward more hand-written work as I get more
|
||||
comfortable with Dalamud and plugin development in general.
|
||||
|
||||
## What AI is used for
|
||||
|
||||
- API explanations (Dalamud, ImGui, .NET specifics I haven't worked with before)
|
||||
- Code drafts that I read, edit, and integrate
|
||||
- Pattern suggestions and code review
|
||||
- Keeping the style aligned with the upstream Chat 2 codebase
|
||||
|
||||
## What AI isn't used for
|
||||
|
||||
- **Visual assets.** Logos, icons, banners, and screenshots are human-drawn
|
||||
or taken from the running game.
|
||||
- **German translations.** Written by me as a native speaker.
|
||||
|
||||
## What's where
|
||||
|
||||
Upstream Chat 2 (by Infi & Anna, EUPL-1.2) is the foundation and was not
|
||||
produced with AI assistance. Hellion-specific code lives in
|
||||
`HellionChat/Privacy/`, `HellionChat/Export/`, `HellionChat/Resources/HellionStrings*`,
|
||||
`Ui/SettingsTabs/Privacy.cs`, `Ui/FirstRunWizard.cs`, `Ui/HellionStyle.cs`,
|
||||
plus the Migrate3 recovery and plugin layout migration in `MessageStore.cs`
|
||||
and `Plugin.cs`. These were developed with Pair-level assistance as
|
||||
described above.
|
||||
|
||||
## If AI-assisted development is a dealbreaker for you
|
||||
|
||||
Fair enough. There are solid alternatives that don't rely on AI in their
|
||||
development:
|
||||
|
||||
- [Chat 2](https://github.com/Infiziert90/ChatTwo), the original upstream
|
||||
this fork is based on
|
||||
- [XIV Instant Messenger](https://github.com/NightmareXIV/XIVInstantMessenger),
|
||||
a different approach to chat in FFXIV
|
||||
|
||||
Both are good projects. Use what fits you best.
|
||||
|
||||
## Tooling
|
||||
|
||||
- Claude (Anthropic) via Claude Code CLI
|
||||
- Context7 / Microsoft Learn for current Dalamud and .NET documentation
|
||||
|
||||
## Contact
|
||||
|
||||
Questions about this disclosure: <https://github.com/JonKazama-Hellion/HellionChat/issues>.
|
||||
@@ -0,0 +1,83 @@
|
||||
# Changelog — Hellion Chat
|
||||
|
||||
Alle nutzersichtbaren Änderungen an Hellion Chat. Das Format orientiert
|
||||
sich an [Keep a Changelog](https://keepachangelog.com/de/1.0.0/), die
|
||||
Version-Nummern folgen [Semantischer Versionierung](https://semver.org/lang/de/).
|
||||
|
||||
Detaillierte Release-Notes pro Version stehen direkt am
|
||||
[GitHub-Release](https://github.com/JonKazama-Hellion/HellionChat/releases)
|
||||
und im Plugin-Changelog-Block (`HellionChat/HellionChat.yaml` →
|
||||
`changelog:`). Diese Datei fasst die Releases als Überblick zusammen
|
||||
und verlinkt für Details auf die Release-Pages.
|
||||
|
||||
---
|
||||
|
||||
## [1.0.0] — 2026-05-03 — Standalone Major Release
|
||||
|
||||
Erste vollständig eigenständige Version. Code-Namespace, IPC-Kanäle und
|
||||
Source-Tree-Struktur wurden auf `HellionChat.*` konsolidiert. Plugin
|
||||
verweigert den Start bei aktivem Upstream Chat 2 (bilinguale
|
||||
Konflikt-Meldung). SQLite-Native auf 3.50.3 gepinnt (CVE-2025-6965,
|
||||
CVE-2025-7709). Tab-Layout-Default für neue Installationen und für
|
||||
User auf Config-Version 12 oder älter neu strukturiert (5 thematische
|
||||
Tabs statt 6+ kitchen-sink). Sweep aus Critical- und Major-Findings
|
||||
aus dem Codebase-Audit eingearbeitet.
|
||||
|
||||
[Release-Notes 1.0.0](https://github.com/JonKazama-Hellion/HellionChat/releases/tag/v1.0.0)
|
||||
|
||||
## [0.6.1] — 2026-05-03 — Pop-Out Discoverability & /tell Auto-Pop-Out
|
||||
|
||||
Pop-Out-Button im Chat-Header sichtbar, einmaliger Hint-Banner für die
|
||||
Pop-Out-Funktionalität. Neue Einstellung "Neue /tell-Tabs direkt als
|
||||
Pop-Out öffnen". Pop-Out-Input ist jetzt standardmäßig aktiv.
|
||||
Bugfixes: Ghost-Windows bei LRU-Drop / Logout, Dead-Zone unter dem
|
||||
Input-Bar bei aktivem Hint-Banner.
|
||||
|
||||
[Release-Notes 0.6.1](https://github.com/JonKazama-Hellion/HellionChat/releases/tag/v0.6.1)
|
||||
|
||||
## [0.6.0] — 2026-05-03 — UX Polish: Pop-Out Input + Colour Presets
|
||||
|
||||
Zwei opt-in UX-Features. Pop-Out-Fenster bekommen optional eine
|
||||
kompakte Eingabe-Bar mit channel-farbigem Icon-Button und unabhängigem
|
||||
Text-Buffer pro Pop-Out. Sieben Built-in-Color-Presets (Klassik,
|
||||
High-Contrast, Pastell, Dark-Mode-Tuned, Hellion, Night Blue, Indigo
|
||||
Violet) zum One-Click-Apply. Konfigurations-Migration v10 → v11.
|
||||
|
||||
[Release-Notes 0.6.0](https://github.com/JonKazama-Hellion/HellionChat/releases/tag/v0.6.0)
|
||||
|
||||
## [0.5.4] — 2026-05-02 — WrapText Hardening
|
||||
|
||||
`ImGuiUtil.WrapText` von Pointer-Arithmetik auf Span- und
|
||||
Index-basierten Control-Flow umgestellt. Schließt das wiederkehrende
|
||||
CodeQL-Critical-Alert "unvalidated local pointer arithmetic"
|
||||
dauerhaft. Keine nutzersichtbare Verhaltensänderung — Word-Wrap-Output
|
||||
ist byte-identisch zu 0.5.3.
|
||||
|
||||
[Release-Notes 0.5.4](https://github.com/JonKazama-Hellion/HellionChat/releases/tag/v0.5.4)
|
||||
|
||||
## [0.5.3] — 2026-05-02 — Pointer Arithmetic Hardening
|
||||
|
||||
Erster Anlauf zur Schließung des CodeQL-Critical-Alerts in
|
||||
`ImGuiUtil.WrapText`. Encoded-Byte-Buffer-Length wird vor der
|
||||
Pointer-Arithmetik via `GetByteCount` validiert.
|
||||
|
||||
[Release-Notes 0.5.3](https://github.com/JonKazama-Hellion/HellionChat/releases/tag/v0.5.3)
|
||||
|
||||
---
|
||||
|
||||
## Frühere Versionen
|
||||
|
||||
Releases vor 0.5.3 (Bootstrap-Phase 0.1.0 bis 0.5.2) sind direkt am
|
||||
GitHub-Release-Stream einsehbar:
|
||||
|
||||
[Alle Releases](https://github.com/JonKazama-Hellion/HellionChat/releases)
|
||||
|
||||
---
|
||||
|
||||
## Pflege-Hinweis
|
||||
|
||||
Die Source-of-Truth für den nutzersichtbaren Changelog ist der
|
||||
`changelog:`-Block in `HellionChat/HellionChat.yaml`. `repo.json` und
|
||||
der GitHub-Release-Body werden daraus gespeist. Diese Datei
|
||||
(`docs/CHANGELOG.md`) ist eine kuratierte Zusammenfassung mit Verweis
|
||||
auf die Release-Pages und wird beim Versions-Bump manuell ergänzt.
|
||||
@@ -0,0 +1,61 @@
|
||||
# Contributors — Hellion Chat
|
||||
|
||||
Hellion Chat ist von der Code-Seite ein Ein-Personen-Projekt. Aber ohne die Leute auf dieser Seite gäbe es weder die Bug-Fixes noch die UX-Verbesserungen, die seit den frühen Versionen reingelaufen sind. Jeder Eintrag hier hat das Plugin konkret besser gemacht.
|
||||
|
||||
Die Anerkennung an die Upstream-Autoren von Chat 2 (Infi und Anna) liegt bewusst in [`../NOTICE.md`](../NOTICE.md), nicht hier. Diese Datei deckt explizit Beiträge zur Hellion-Chat-Seite ab.
|
||||
|
||||
---
|
||||
|
||||
## Entwicklung
|
||||
|
||||
### JonKazama (Florian Wathling) — Maintainer
|
||||
|
||||
Hellion Chat ist mein erstes FFXIV-Plugin und mein erstes größeres C#-/Dalamud-Projekt. Mein beruflicher Hintergrund ist Webentwicklung (Next.js, React, TypeScript, Prisma). Plugin-Entwicklung in einer fremden Codebase, ImGui, FFXIV-Game-Hooks und der gesamte Dalamud-Stack waren Neuland.
|
||||
|
||||
Privacy-First-Defaults, Per-Channel-Retention, Auto-Tell-Tabs, Pop-Out-Input, ChatColours-Presets, Hellion-Theme plus Exo-2-Font und der v1.0.0-Standalone-Cut sind die Hellion-spezifischen Surface-Areas, die ich auf das Chat-2-Fundament aufgebaut habe. Die Lern-Geschichte dahinter steht in [`LEARNING-JOURNEY.md`](LEARNING-JOURNEY.md).
|
||||
|
||||
Hellion Chat ist Teil von [Hellion Online Media](https://hellion-media.de).
|
||||
|
||||
---
|
||||
|
||||
## Tester
|
||||
|
||||
Eine kurze Notiz vorneweg: Ich teste das Plugin nicht allein. Die Leute hier haben mir Bugs gemeldet, bevor sie bei mehr Nutzern aufgeschlagen wären. Sie haben UX-Probleme angesprochen, die ich blind nicht mehr gesehen habe. Und sie haben Feature-Wünsche eingebracht, die das Plugin in Richtungen geschoben haben, in die ich von alleine nicht gegangen wäre. Das ist nicht selbstverständlich. Externe Tester sind ihre Zeit wert.
|
||||
|
||||
### Carl Beleandis (Carla) — Beta-Tester
|
||||
|
||||
Carl testet seit der Bootstrap-Phase und hat sowohl die Pop-Out-Mechanik als auch die Theme-Richtung geprägt. Sein Feedback kommt direkt und ohne Umschweife und das ist genau, was ich beim Testen brauche.
|
||||
|
||||
Konkrete Beiträge:
|
||||
|
||||
- **Pop-Out-Discoverability** — der Hinweis, dass Pop-Outs nur per Rechtsklick erreichbar waren, hat den Header-Button und den einmaligen Hint-Banner in v0.6.1 ausgelöst. Ich kannte den Rechtsklick-Pfad blind, deshalb hatte ich nicht mehr gesehen, dass neue Nutzer die Funktion gar nicht finden.
|
||||
- **/tell-Pop-Out-Mode** — der Wunsch, /tell-Tabs direkt als Pop-Out zu öffnen statt über den Tab-Umweg, ist in v0.6.1 als opt-in Settings-Toggle gelandet. Bonus: Bei der Implementation ist ein alter Ghost-Window-Bug aufgefallen (LRU-Drop ließ Pop-Out-Fenster als Geister stehen), der gleich mit gefixt wurde.
|
||||
- **Theme-Varianten mit Helligkeits-Abstufungen** — der Wunsch nach einer Grün-Familie hat mein Verständnis von "ein Theme = eine Farbe" auf "Theme-Familien mit Stimmungs-Varianten" verschoben. Steht in der [Roadmap](ROADMAP.md) für einen späteren Cycle.
|
||||
|
||||
### Jin (Jingliu) — Alpha-Tester
|
||||
|
||||
Jin ist der aktive Tester der ersten Stunde und hat den Pop-Out-Workflow architektonisch in eine andere Richtung geschoben.
|
||||
|
||||
Konkrete Beiträge:
|
||||
|
||||
- **Pop-Out-Tab mit Input-Feld** — der Vorschlag, in einem Pop-Out auch tippen zu können (statt nur lesen), hat die v0.6.0 Pop-Out-Input-Bar ausgelöst. Das war ein größerer Refactor: Der Input-Layer aus `ChatLogWindow` musste so geöffnet werden, dass er auch in `Popout.cs` lebt, mit unabhängigem Text-Buffer und History-Cursor pro Pop-Out. Hat den Cycle dominiert, weil das Design erst sauber sein musste, bevor Code passieren konnte.
|
||||
- **TempTell Persistence** — der Wunsch, /tell-Tabs per Pin-Toggle einen Relog überleben zu lassen, steht in der [Roadmap](ROADMAP.md) für einen späteren Cycle. Berührt das Tab-System architektonisch und braucht eigenes Design.
|
||||
|
||||
---
|
||||
|
||||
## Übersetzungen
|
||||
|
||||
Hellion-eigene UI-Strings werden in `HellionChat/Resources/HellionStrings.<lang>.resx` gepflegt.
|
||||
|
||||
- **Deutsch (DE):** JonKazama (Native Speaker, Hauptsprache des Projekts)
|
||||
|
||||
Die Upstream-Sprach-Dateien (`Language.<lang>.resx`) sind nicht Teil dieser Datei. Sie werden über das [Chat-2-Crowdin-Projekt](https://github.com/Infiziert90/ChatTwo) gepflegt; Crowdin-Übersetzer findest du in den Plugin-Settings unter **Info → "Chat 2 community translators"**.
|
||||
|
||||
---
|
||||
|
||||
## Wie du beitragen kannst
|
||||
|
||||
Bug-Reports, Feature-Wünsche und Pull-Requests laufen über [GitHub Issues](https://github.com/JonKazama-Hellion/HellionChat/issues). Workflow und Erwartungen stehen in [`../CONTRIBUTING.md`](../CONTRIBUTING.md), Code of Conduct in [`../CODE_OF_CONDUCT.md`](../CODE_OF_CONDUCT.md).
|
||||
|
||||
Tester-Pool für neue Versionen läuft über den Hellion-Forge-Discord: [discord.gg/X9V7Kcv5gR](https://discord.gg/X9V7Kcv5gR). Wer in den Tester-Channel rein will, einfach im Forge melden.
|
||||
|
||||
Executable
+250
@@ -0,0 +1,250 @@
|
||||
# Hellion Chat IPC Integration Guide
|
||||
|
||||
This document describes the inter-plugin-communication (IPC) channels that
|
||||
Hellion Chat exposes to other Dalamud plugins. Two integration surfaces are
|
||||
covered: the **Context Menu IPC** for adding custom items to Hellion Chat's
|
||||
right-click menus, and the **Typing State IPC** for reacting to the user's
|
||||
input-box activity.
|
||||
|
||||
---
|
||||
|
||||
## Compatibility with Chat 2
|
||||
|
||||
Hellion Chat is a standalone fork of [Chat 2](https://github.com/Infiziert90/ChatTwo)
|
||||
(EUPL-1.2). The IPC surface is one of the parts the fork inherits directly:
|
||||
the same call shapes, the same tuple payloads, the same call semantics, the
|
||||
same lifecycle. We did not redesign the API, we re-published it under our own
|
||||
plugin name.
|
||||
|
||||
Concretely, this means:
|
||||
|
||||
- **Tuple shapes are identical.** A subscriber that worked against Chat 2's
|
||||
`ChatTwo.Invoke` works against Hellion Chat's `HellionChat.Invoke` without
|
||||
any code change beyond the channel string.
|
||||
- **Lifecycle is identical.** The `Available` ping fires when the plugin
|
||||
becomes ready, your subscriber re-registers, and the registration ID is
|
||||
returned by the same `Register` call as before.
|
||||
- **Channel-name prefix changed in v1.0.0.** Every `ChatTwo.*` channel name
|
||||
is now `HellionChat.*`. Existing third-party integrations need a one-line
|
||||
rename per channel string and nothing else.
|
||||
|
||||
If your plugin already supports Chat 2 and you want to add Hellion Chat
|
||||
support, the cleanest path is to bind both prefixes and treat whichever one
|
||||
becomes available first as the active host.
|
||||
|
||||
---
|
||||
|
||||
## Channel Reference
|
||||
|
||||
| Surface | Channel | Direction | Payload |
|
||||
| ------------- | ------------------------------------ | --------------- | ---------------------------------------------------------------------------------------- |
|
||||
| Context Menu | `HellionChat.Available` | plugin → caller | event, no payload |
|
||||
| Context Menu | `HellionChat.Register` | caller → plugin | returns registration `string` ID |
|
||||
| Context Menu | `HellionChat.Unregister` | caller → plugin | takes registration ID `string` |
|
||||
| Context Menu | `HellionChat.Invoke` | plugin → caller | event, see Context Menu section |
|
||||
| Typing State | `HellionChat.GetChatInputState` | caller → plugin | returns the typing-state tuple |
|
||||
| Typing State | `HellionChat.ChatInputStateChanged` | plugin → caller | event, fires once on subscribe and on every state change, payload is the same tuple |
|
||||
|
||||
---
|
||||
|
||||
## Context Menu IPC
|
||||
|
||||
Use this surface to draw your own selectables inside Hellion Chat's
|
||||
right-click context menus. All registrations are called inside an ImGui
|
||||
`BeginMenu`, so anything you draw appears as a regular menu entry.
|
||||
|
||||
### Lifecycle
|
||||
|
||||
1. Subscribe to `HellionChat.Available`. The host fires this once when it
|
||||
loads or reloads, so your plugin can re-register without polling.
|
||||
2. Call `HellionChat.Register` to obtain a registration ID. Save it. You
|
||||
need it to filter `Invoke` callbacks that target your registration and
|
||||
to call `Unregister` later.
|
||||
3. Subscribe to `HellionChat.Invoke` and draw your menu items inside the
|
||||
handler when the `id` matches your saved registration ID.
|
||||
4. On plugin disable or unload, call `HellionChat.Unregister` with your
|
||||
saved ID and unsubscribe from `Invoke`.
|
||||
|
||||
### Example
|
||||
|
||||
```cs
|
||||
public class HellionChatContextMenu {
|
||||
// Used to register your plugin with the IPC; returns an ID you must save.
|
||||
private ICallGateSubscriber<string> Register { get; }
|
||||
// Used to unregister your plugin from the IPC; call this on unload.
|
||||
private ICallGateSubscriber<string, object?> Unregister { get; }
|
||||
// Subscribe to receive a notification when Hellion Chat becomes ready
|
||||
// or reloads, so you can re-register.
|
||||
private ICallGateSubscriber<object?> Available { get; }
|
||||
// Subscribe to draw your custom context-menu items.
|
||||
private ICallGateSubscriber<string, PlayerPayload?, ulong, Payload?, SeString?, SeString?, object?> Invoke { get; }
|
||||
|
||||
private string? _id;
|
||||
|
||||
public HellionChatContextMenu(DalamudPluginInterface @interface) {
|
||||
this.Register = @interface.GetIpcSubscriber<string>("HellionChat.Register");
|
||||
this.Unregister = @interface.GetIpcSubscriber<string, object?>("HellionChat.Unregister");
|
||||
this.Invoke = @interface.GetIpcSubscriber<string, PlayerPayload?, ulong, Payload?, SeString?, SeString?, object?>("HellionChat.Invoke");
|
||||
this.Available = @interface.GetIpcSubscriber<object?>("HellionChat.Available");
|
||||
}
|
||||
|
||||
public void Enable() {
|
||||
// Re-register automatically when Hellion Chat becomes ready or reloads.
|
||||
this.Available.Subscribe(() => this.DoRegister());
|
||||
// Register if Hellion Chat is already loaded.
|
||||
this.DoRegister();
|
||||
|
||||
// Listen for context-menu events.
|
||||
this.Invoke.Subscribe(this.OnInvoke);
|
||||
}
|
||||
|
||||
private void DoRegister() {
|
||||
this._id = this.Register.InvokeFunc();
|
||||
}
|
||||
|
||||
public void Disable() {
|
||||
if (this._id != null) {
|
||||
this.Unregister.InvokeAction(this._id);
|
||||
this._id = null;
|
||||
}
|
||||
this.Invoke.Unsubscribe(this.OnInvoke);
|
||||
}
|
||||
|
||||
private void OnInvoke(string id, PlayerPayload? sender, ulong contentId, Payload? payload, SeString? senderString, SeString? content) {
|
||||
// Filter: only react to invocations that target our registration.
|
||||
if (id != this._id) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Draw your custom menu items here. Available context:
|
||||
// sender — first PlayerPayload in the sender SeString, or null
|
||||
// contentId — content ID of the message sender, or 0 when not known
|
||||
// payload — the payload that was right-clicked, or null when text
|
||||
// senderString — the full sender SeString
|
||||
// content — the full message content SeString
|
||||
if (ImGui.Selectable("Test plugin")) {
|
||||
PluginLog.Log($"hi!\nsender: {sender}\ncontent id: {contentId:X}\npayload: {payload}\nsender string: {senderString}\ncontent string: {content}");
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Migration from Chat 2
|
||||
|
||||
If your plugin already integrates with `ChatTwo.*`, the rename is the only
|
||||
required change:
|
||||
|
||||
```diff
|
||||
-this.Register = @interface.GetIpcSubscriber<string>("ChatTwo.Register");
|
||||
-this.Unregister = @interface.GetIpcSubscriber<string, object?>("ChatTwo.Unregister");
|
||||
-this.Invoke = @interface.GetIpcSubscriber<...>("ChatTwo.Invoke");
|
||||
-this.Available = @interface.GetIpcSubscriber<object?>("ChatTwo.Available");
|
||||
+this.Register = @interface.GetIpcSubscriber<string>("HellionChat.Register");
|
||||
+this.Unregister = @interface.GetIpcSubscriber<string, object?>("HellionChat.Unregister");
|
||||
+this.Invoke = @interface.GetIpcSubscriber<...>("HellionChat.Invoke");
|
||||
+this.Available = @interface.GetIpcSubscriber<object?>("HellionChat.Available");
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Typing State IPC
|
||||
|
||||
Use this surface when you need to know whether the player is currently
|
||||
interacting with Hellion Chat's input box. Useful for typing indicators,
|
||||
keyboard-shortcut suppression, or HUD elements that hide while the user is
|
||||
typing.
|
||||
|
||||
### Tuple Payload
|
||||
|
||||
Both `HellionChat.GetChatInputState` (poll) and
|
||||
`HellionChat.ChatInputStateChanged` (event) return the same tuple:
|
||||
|
||||
```cs
|
||||
(bool InputVisible, bool InputFocused, bool HasText, bool IsTyping, int TextLength, ChatType ChannelType)
|
||||
```
|
||||
|
||||
| Field | Type | Meaning |
|
||||
| -------------- | ---------- | -------------------------------------------------------------------------------- |
|
||||
| `InputVisible` | `bool` | True when Hellion Chat is not hidden by user, cutscene or battle settings. |
|
||||
| `InputFocused` | `bool` | True while the input box currently has keyboard focus. |
|
||||
| `HasText` | `bool` | True when the input buffer contains more than whitespace. |
|
||||
| `IsTyping` | `bool` | Convenience flag, equivalent to `InputFocused && HasText`. |
|
||||
| `TextLength` | `int` | Length of the raw input buffer in characters. |
|
||||
| `ChannelType` | `ChatType` | The channel/mode that will be used if the buffer is submitted right now. |
|
||||
|
||||
### Where `ChannelType` comes from
|
||||
|
||||
`ChannelType` is the `HellionChat.Code.ChatType` enum value representing the
|
||||
target channel for the current submission. It is sourced from the active
|
||||
tab's `UsedChannel` (`HellionChat/Configuration.cs`), which the plugin keeps
|
||||
in sync by hooking the in-game shell (`HellionChat/GameFunctions/Chat.cs`)
|
||||
and by resolving temporary overrides inside the chat UI
|
||||
(`HellionChat/Ui/ChatLogWindow.cs:597`). `InputChannel` values are converted
|
||||
into the exported `ChatType` via
|
||||
`HellionChat/Code/InputChannelExt.ToChatType`.
|
||||
|
||||
### Behavior
|
||||
|
||||
- `ChatInputStateChanged` fires once immediately after subscribe so you do
|
||||
not need a separate `GetChatInputState` poll for the initial snapshot.
|
||||
- After that it fires only when one or more fields actually change, so it
|
||||
is safe to subscribe without rate-limiting.
|
||||
- `GetChatInputState` is available for one-shot polls, e.g. on plugin
|
||||
enable.
|
||||
|
||||
### Example
|
||||
|
||||
```cs
|
||||
public sealed class HellionChatTypingIntegration {
|
||||
private ICallGateSubscriber<(bool InputVisible, bool InputFocused, bool HasText, bool IsTyping, int TextLength, ChatType ChannelType)> GetChatInputState { get; }
|
||||
private ICallGateSubscriber<(bool InputVisible, bool InputFocused, bool HasText, bool IsTyping, int TextLength, ChatType ChannelType)> ChatInputStateChanged { get; }
|
||||
|
||||
public HellionChatTypingIntegration(DalamudPluginInterface @interface) {
|
||||
this.GetChatInputState = @interface.GetIpcSubscriber<(bool, bool, bool, bool, int, ChatType)>("HellionChat.GetChatInputState");
|
||||
this.ChatInputStateChanged = @interface.GetIpcSubscriber<(bool, bool, bool, bool, int, ChatType)>("HellionChat.ChatInputStateChanged");
|
||||
}
|
||||
|
||||
public void Enable() {
|
||||
this.ChatInputStateChanged.Subscribe(OnChatInputStateChanged);
|
||||
|
||||
// Optional: poll once for an initial snapshot. The Subscribe call
|
||||
// above already fires once, so this is only useful when your code
|
||||
// path needs the value before the framework hands you the event.
|
||||
var state = this.GetChatInputState.InvokeFunc();
|
||||
PluginLog.Information($"Initial typing state: {state}");
|
||||
}
|
||||
|
||||
public void Disable() {
|
||||
this.ChatInputStateChanged.Unsubscribe(OnChatInputStateChanged);
|
||||
}
|
||||
|
||||
private void OnChatInputStateChanged((bool InputVisible, bool InputFocused, bool HasText, bool IsTyping, int TextLength, ChatType ChannelType) state) {
|
||||
if (state.IsTyping) {
|
||||
// Show typing indicator.
|
||||
} else {
|
||||
// Hide typing indicator.
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Migration from Chat 2
|
||||
|
||||
Same shape as the Context Menu surface — only the channel-name prefix needs
|
||||
the rename:
|
||||
|
||||
```diff
|
||||
-this.GetChatInputState = @interface.GetIpcSubscriber<...>("ChatTwo.GetChatInputState");
|
||||
-this.ChatInputStateChanged = @interface.GetIpcSubscriber<...>("ChatTwo.ChatInputStateChanged");
|
||||
+this.GetChatInputState = @interface.GetIpcSubscriber<...>("HellionChat.GetChatInputState");
|
||||
+this.ChatInputStateChanged = @interface.GetIpcSubscriber<...>("HellionChat.ChatInputStateChanged");
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## License & Attribution
|
||||
|
||||
This guide and the IPC surface it documents derive directly from the Chat 2
|
||||
codebase. Hellion Chat is licensed under [EUPL-1.2](LICENSE), and credit for
|
||||
the original IPC design and implementation goes to **Infiziert90 (Infi)**
|
||||
and **Anna Clemens** — see [`NOTICE.md`](NOTICE.md) for full attribution.
|
||||
@@ -0,0 +1,219 @@
|
||||
# Entwicklungsgeschichte und Lernprozess
|
||||
|
||||
## Hintergrund
|
||||
|
||||
Ich bin Autodidakt. Hellion Chat ist mein erstes FFXIV-Plugin und mein erstes größeres C#-Projekt. Mein beruflicher Hintergrund ist Webentwicklung (Next.js, React, TypeScript, Prisma, MySQL), also Browser-Welt mit JavaScript-Toolchain. C# kannte ich vor diesem Projekt nur oberflächlich, ImGui gar nicht, Dalamud nur als Endnutzer über andere Plugins.
|
||||
|
||||
Wenn ich an einer Stelle nicht weiterkomme, nutze ich AI-Tools wie Claude Code als Pair-Hilfsmittel. Wie das genau aussieht und welche Klassifikation ich verwende, steht transparent in [`AI_DISCLOSURE.md`](AI_DISCLOSURE.md).
|
||||
|
||||
---
|
||||
|
||||
## Warum überhaupt ein Chat-Plugin?
|
||||
|
||||
Hellion Chat soll Chat 2 nicht ersetzen. Chat 2 liefert ein vollständiges Chat-Erlebnis mit kompletter Historie, Filtern, Suche und Replay. Für die meisten Nutzer ist genau das richtig.
|
||||
|
||||
### Zwei Millionen Nachrichten in zwei Jahren
|
||||
|
||||
Mein Wunsch nach einem engeren Default war ehrlich gesagt erstmal persönlich. Nach zwei Jahren mit Chat 2 lag meine Datenbank bei über zwei Millionen Nachrichten, der Großteil davon /say, /shout und /yell von wildfremden Leuten in Limsa. Genau diese Daten machen Chat 2's Voll-Historie nützlich, und die meisten Nutzer behalten sie auch gerne. Mein eigener Geschmack wollte einen kleineren Default. Also habe ich diesen Fork gebaut.
|
||||
|
||||
### Greeter in mehreren Clubs
|
||||
|
||||
Dazu kam ein zweiter Use-Case: Ich bin in mehreren FFXIV-Clubs als Greeter aktiv. Für die Greeter-Arbeit reicht die Vanilla-Chat-Oberfläche nicht. Parallel laufende /tell-Gespräche schreiben in einem einzigen Tab durcheinander, und ich verliere ständig den Faden, wer mir gerade was geschrieben hat. Auto-Tell-Tabs (eines der frühen Hellion-Chat-Features) ist genau für diesen Workflow entstanden: ein Tab pro Gesprächspartner, automatisch gespawnt, mit manuellem Greeted-Status. Dass das auch der Privacy-Hygiene gut tut, war ein netter Bonus, nicht der Auslöser.
|
||||
|
||||
### Hellion Online Media
|
||||
|
||||
Die Privacy-Defaults sind außerdem eine Position aus meinem Hauptberuf. Hellion Online Media ist mein Einzelunternehmen, und Datenschutz gegenüber Kunden ist da kein Marketing-Slogan, sondern operativ relevant. Dieser Fork ist die Plugin-Form derselben Haltung.
|
||||
|
||||
---
|
||||
|
||||
## Warum nicht beim Original mitarbeiten?
|
||||
|
||||
Drei Gründe, in absteigender Wichtigkeit.
|
||||
|
||||
### Defaults sind nicht verhandelbar, auch nicht meine
|
||||
|
||||
Privacy-First als Standard ist eine Minderheits-Position. Chat 2 bedient zu Recht die breite Masse mit Voll-Historie als Default. Diese Defaults im Upstream zu ändern wäre falsch gewesen. Ich hätte den Standard für eine große Nutzerbasis umgekippt, die ihn so wollte, wie er ist. Saubere Trennung über einen eigenen Plugin-Slot war der respektvollere Weg.
|
||||
|
||||
### Das Webinterface musste weg
|
||||
|
||||
Das ist ein zentrales Chat-2-Feature für Remote-Zugriff vom Zweitgerät. Ein PR der das entfernt, hat in einem gepflegten Upstream-Projekt keine Chance, und das ist auch richtig so. Aber genau das Webinterface kollidiert mit der Privacy-First-These dieses Forks: Ein Chat-Plugin das einen lokalen HTTP-Server startet, ist für mein Threat-Model eine zu große Angriffsfläche. Also raus damit.
|
||||
|
||||
### Tempo
|
||||
|
||||
Ein Solo-Maintainer-Projekt mit kleinem Tester-Pool kann schneller iterieren als ein etabliertes Plugin mit großer Nutzerbasis. Das ist kein Vorwurf an Upstream, sondern eine andere Optimierung. Ich brauche keine Roadmap-Abstimmung, keine Reviewer-Verfügbarkeit, und kann Audit-Konsequenzen wie das Webinterface-Removal in einer einzigen Version durchziehen statt über mehrere Releases.
|
||||
|
||||
EUPL-1.2 erlaubt das alles ausdrücklich, mit klarer Attribution. Der Code liegt offen unter derselben Lizenz wie Chat 2. Infi, Anna oder sonst jemand dürfen reinschauen, Ideen mitnehmen, Fragen stellen oder den Fork einfach ignorieren. Alles drei ist für mich okay.
|
||||
|
||||
---
|
||||
|
||||
## Wie ich so schnell release
|
||||
|
||||
Wer auf den Repo schaut, sieht in kurzer Zeit viele Releases und sehr viele Commits. Beides wird von außen gerne als Red-Flag gelesen: KI-Slop, Salami-Taktik, Code-Spam. Bei Hellion Chat ist beides eine bewusste Entscheidung, und ich erkläre lieber einmal warum, als mich später dafür zu rechtfertigen.
|
||||
|
||||
### Vorarbeit, lange bevor der Fork existierte
|
||||
|
||||
Bevor ich die erste Zeile in `HellionChat/` getippt habe, war ich wochenlang nur Leser. Chat 2 ingame nutzen und damit rumspielen. Issues im Upstream-Tracker durchgehen, vor allem die geschlossenen, weil dort steht, wie Infi und Anna Bugs einkreisen. Commits lesen, gerne auch ältere, um zu verstehen, warum eine Architektur-Entscheidung getroffen wurde, nicht nur, dass sie getroffen wurde. Wenn ich heute weiß, wo im Code was liegt, dann nicht, weil ich besonders schnell durch eine Codebase navigiere, sondern weil ich den Code vorher gelesen habe.
|
||||
|
||||
Klingt nach Selbstverständlichkeit, ist es aber nicht. Die übliche Reihenfolge bei Solo-Forks heißt erst forken, dann verstehen. Ich habe es andersrum gemacht.
|
||||
|
||||
### Die Codebase von Infi und Anna
|
||||
|
||||
Hellion Chat baut auf einem Boden auf, der schon flach ist. Chat 2 ist sauber strukturiert, die Naming-Konventionen sind konsistent, die Trennung zwischen Layern (Storage, UI, Game-Hooks, IPC) ist klar gezogen. Das ist in Open-Source-Plugin-Welten nicht selbstverständlich, und es ist der Hauptgrund, warum sich Hellion-spezifische Features oft "fast nativ" einbauen lassen. Ich muss nicht erst Spaghetti entwirren bevor ich was Eigenes danebenstellen kann.
|
||||
|
||||
Side-Fact: Selbst beim ersten Codebase-Walkthrough mit Claude kam mehrfach der Hinweis, dass die Architektur ungewöhnlich gut aufgeräumt ist und mehrere Erweiterungspunkte vorbereitet. Das hat Gewicht, weil es von außen kommt, aber den eigentlichen Kredit kriegen Infi und Anna, nicht Claude.
|
||||
|
||||
### Atomar arbeiten, kleine Commits
|
||||
|
||||
Ein Commit, eine logische Änderung. Wenn ich einen Bug fixe, parallel eine Variable umbenenne und nebenbei einen Kommentar einbaue, sind das drei Commits, nicht einer. Klingt nach Mikro-Management, ist es aber nicht. Wenn in sechs Monaten ein Bug auftaucht und ich `git bisect` brauche, finde ich die kaputte Änderung in zwei Minuten statt in zwei Stunden. Bei einem 4000-Zeilen-Mega-Commit darf ich raten, welche der hundert Änderungen die kaputte ist.
|
||||
|
||||
Den Stil habe ich bewusst auch deshalb beibehalten, weil Infi im Upstream häufig genauso arbeitet. Manchmal ein Sechs-Zeilen-Commit, manchmal nur ein Typo-Fix. Das ist keine Schwäche, das ist eine Entscheidung für lesbare Git-History. Den Stil im Fork beizubehalten ist ein Respekt-Move: Wer die beiden Repos vergleicht, soll den gleichen Lese-Rhythmus haben.
|
||||
|
||||
Bonus für mich persönlich: Kleine Commits zwingen mich, jeden Schritt einzeln zu durchdenken und zu benennen. Wenn ich nicht in zwei Sätzen erklären kann, was ein Commit macht, ist die Änderung wahrscheinlich noch nicht klar genug. Auf Beginner-Niveau ist das ein eingebauter Sanity-Check, den ich bei einem Big-Bang-Commit nicht hätte.
|
||||
|
||||
### AI als Beschleuniger, ehrlich
|
||||
|
||||
Ja, AI hilft beim Tempo, und nicht zu knapp. Ohne CodeRabbit hätte ich Critical-Bugs der Klasse `Equals/GetHashCode`-Anti-Pattern, Hook-Subscription-Leaks und TOCTOU-Races nicht gefunden. Ich bin schlicht zu unerfahren für diese Klasse von Findings, das schreibe ich genau so hin.
|
||||
|
||||
Was ich aber nicht mache: blind Code übernehmen, weil ein Tool ihn als Fix markiert hat. Bei mehreren CodeRabbit-Findings stand in den Original-Commits von Infi oder Anna sogar ein Stackoverflow-Link mit Begründung dabei, warum eine bestimmte Stelle so aussieht wie sie aussieht. Die habe ich gelesen, bevor ich was geändert habe. Erst verstehen, dann anfassen, dann committen. Das ist der Unterschied zwischen "AI gibt mir Code, ich pushe" und "AI zeigt mir wo's klemmt, ich entscheide".
|
||||
|
||||
Klassifikation und konkrete Beispiele zur AI-Nutzung stehen in [`AI_DISCLOSURE.md`](AI_DISCLOSURE.md). Hier in dieser Sektion ging es nur um den Tempo-Aspekt: Recherche plus saubere Codebase plus atomare Commits plus AI-gestütztes Review-Sparring sind die vier Faktoren zusammen. Kein einzelner davon erklärt das Tempo allein.
|
||||
|
||||
---
|
||||
|
||||
## Vom Web-Stack zu C# / Dalamud
|
||||
|
||||
### Type-System? Weniger Schock als erwartet
|
||||
|
||||
C# nach TypeScript war angenehmer als gedacht. Properties statt getter/setter sind sauber, nullable reference types fühlen sich an wie `strict: true` in TypeScript. Ungewohnt war Wert-Typen vs. Referenz-Typen explizit denken zu müssen (`struct` vs. `class` mit echten Verhaltens-Konsequenzen), und Generics mit Constraints sind syntaktisch anders genug, dass ich beim Lesen kurz stocke. `async`/`await` ist semantisch ähnlich, aber Threading-Modelle sind in C# expliziter: `Task.Run`, `ConfigureAwait`, Synchronization-Contexts. Das hat mich mehrere Bugs gekostet, bevor ich verstanden hatte, wann der Main-Thread (in Plugin-Welt: der Framework-Tick) wirklich kritisch ist.
|
||||
|
||||
### Build-Toolchain: ähnlich, aber anders
|
||||
|
||||
`dotnet` CLI, csproj-XML, NuGet sind funktional nicht weit weg von npm und tsconfig. Aber das XML-Format der csproj ist eine andere Sprache als JSON-Configs. Die Lock-Datei (`packages.lock.json`) musste ich erst aktiv aktivieren (`RestorePackagesWithLockFile=true`); das ist nicht Default. Im Web-Stack ist Lock-File-First Standard, im .NET-Stack offenbar nicht. Das war eine echte Überraschung.
|
||||
|
||||
### ImGui ist eine andere Welt
|
||||
|
||||
Immediate-Mode-Rendering hat mit React-Component-Trees nichts gemein. Es gibt keine virtuelle DOM, keine Reconciliation, keinen "State der Komponente". Pro Frame zeichnet der Code die UI komplett neu, und der State lebt entweder in lokalen Variablen, die ich selbst verwalten muss, oder in der ImGui-eigenen ID-Stack-Logik.
|
||||
|
||||
Was in React zwei Zeilen `useState` sind, ist in ImGui ein Member-Field plus manuelle ID-Stempel auf den Widgets, sonst kollidieren zwei Selectables in derselben Loop, weil sie auf die gleiche ID zurückfallen. Die ID-Stack-Kollision in `SearchSelector` (gefixt in v1.0.0) war genau dieses Symptom: Alle Selectables fielen auf dieselbe ambiguous ID zurück, bis ich den Row-Index in den Push-ID gemixt habe. Klassischer "warum klickt der falsche Eintrag"-Bug, den man nur findet, wenn man verstanden hat, wie ImGui IDs intern handhabt.
|
||||
|
||||
### Dalamud-Spezifika
|
||||
|
||||
Plugin-Lifecycle, IPC-Subscriber-Pattern, Hook-System für Game-Functions, Game-Object-Threading. Viel davon war nur durch Lesen der Upstream-Codebase und durch [dalamud.dev](https://dalamud.dev) zu verstehen. Meine Trainings- und Such-Ergebnisse für "Dalamud" liefern oft veraltete API-Beispiele aus alten Versionen. dalamud.dev ist die zuverlässige Quelle. Wenn jemand neu anfängt: dort hin, nicht zu Stack Overflow.
|
||||
|
||||
### Der Tag, an dem mich der DalamudPackager einen Tag gekostet hat
|
||||
|
||||
Dalamud SDK 15 liefert seinen eigenen Default-Packager mit, der Icons und Image-URLs ins Manifest einträgt. Ich hatte aus dem Upstream-Repo eine eigene `DalamudPackager.targets`-Datei mit `HandleImages`-Override übernommen, und die hat den SDK-Default überschrieben. Resultat: Das Manifest hatte keinen `IconUrl` mehr, und das Plugin tauchte in der Plugin-Liste ohne Icon auf.
|
||||
|
||||
Symptom war einfach zu sehen, Ursache hat einen Tag gekostet. Ich hatte die Override-Datei für eine Pflicht-Datei gehalten, war sie aber nicht. Removal in v0.5.2, seitdem läuft der SDK-Default. Lektion: Erstmal mit Defaults arbeiten, Overrides erst wenn der Default nachweislich nicht passt.
|
||||
|
||||
---
|
||||
|
||||
## Was ich aus dem Fork gelernt habe
|
||||
|
||||
### Refactor in einer fremden Codebase
|
||||
|
||||
Der Standalone-Cut in v1.0.0 hat die `ChatTwo.*`-Identität komplett auf `HellionChat.*` migriert. Klingt nach Find-and-Replace. War es nicht.
|
||||
|
||||
Konkret bedeutete das: Code-Namespace über alle 80 Source-Files plus 100 using-Direktiven plus zwei FQN-Aliases plus die Resource-Designer-Strings. Sechs IPC-Channels umbenannt (Breaking Change für Drittplugins, keine bekannten Anbindungen). Repo-Ordner-Struktur (`ChatTwo/` → `HellionChat/`) inklusive csproj, sln, allen GitHub-Workflows und der dependabot.yml. Public-Facing-Branding in README, repo.json, yaml auf Standalone-Framing umformuliert.
|
||||
|
||||
Das war kein Solo-Find-and-Replace, weil Unicode-String-Pfade in Workflow-YAMLs anders quotiert werden müssen als C#-Strings. Weil Resource-Designer-Files generierte Inhalte haben, die nicht jede Toolchain im Blick hat. Und weil die `ChatTwo.*`-IPC-Channel-Namen Strings in `GetIpcSubscriber`-Calls sind: kein Symbol, kein Compile-Error, wenn man einen vergisst. Da merkst du, was alles still bleibt.
|
||||
|
||||
### Sicherheit ist kein abstraktes Thema mehr
|
||||
|
||||
Vor diesem Projekt war Supply-Chain-Sicherheit für mich akademisch. Drei konkrete Lektionen haben das geändert.
|
||||
|
||||
**SQLite-Native-Binary.** Ich musste auf 3.50.3 pinnen (`SQLitePCLRaw.lib.e_sqlite3` Override), weil `Microsoft.Data.Sqlite` die transitiv nachgezogene Lib in einer Version mitschleppte, die CVE-2025-6965 (Memory-Corruption durch Aggregate-Term-Overflow) und CVE-2025-7709 enthielt. Der Managed-Wrapper war neu, die Native-Lib war es nicht. Lektion: Transitive Dependencies prüfen sich nicht von selbst, du musst hinschauen.
|
||||
|
||||
**Lock-File-Drift.** `packages.lock.json` honored bei `dotnet restore` (per `RestorePackagesWithLockFile=true` in der csproj) verhindert, dass transitive Versionen zwischen meiner Maschine und CI silent driften. Erst nach einem Build-Output-Mismatch zwischen lokal und GitHub-Actions hatte ich überhaupt verstanden, warum das nicht der Default ist.
|
||||
|
||||
**WrapText und der CodeQL-Alarm der drei Releases gekostet hat.** CodeQL hat in `ImGuiUtil.WrapText` einen Critical-Alert wegen "unvalidated local pointer arithmetic" geworfen. v0.5.2 hat einen Edge-Case validiert. Alert kam wieder. v0.5.3 hat den Buffer-Length via `GetByteCount` vor der Pointer-Math gecheckt. Alert kam wieder. v0.5.4 hat den ganzen Algorithmus auf `Span` und int-Offsets umgebaut, mit einem 16-KiB-Cap auf den ArrayPool-Rent. Erst da war Ruhe.
|
||||
|
||||
Lektion: Wenn ein statischer Analyzer drei Mal hintereinander meckert, ist nicht der Analyzer überempfindlich. Die Datenflusslogik ist es.
|
||||
|
||||
### CodeRabbit als externer Code-Reviewer
|
||||
|
||||
Der v1.0.0-Sweep hat 3 Critical und 21 Major Findings hochgespült. Drei Klassen davon waren besonders lehrreich:
|
||||
|
||||
- **`Equals`-Methoden die `GetHashCode()` vergleichen.** Klassisches Hash-Kollisions-Anti-Pattern. Klingt nach "ist doch egal, wenn Hashes gleich sind, sind die Objekte auch gleich", ist aber genau falsch. Hashes können kollidieren, Objekte sind dann nicht gleich.
|
||||
- **`Dispose`-Methoden die nur einen Teil der Subscriptions wieder abmelden.** Leak bei jedem Plugin-Reload. Im Nutzer-Alltag merkst du das nicht sofort, im Long-Running-Test schon.
|
||||
- **TOCTOU-Races.** Zwischen Bounds-Check und Read kann ein anderer Thread das Array unter dir austauschen (`GlobalParametersCache`, `AutoTranslate`).
|
||||
|
||||
Davon hatte ich vorher bestenfalls die Theorie gelesen, nicht selbst diagnostiziert. CodeRabbit war für mich der Moment, wo "akademisches Wissen" zu "okay, das ist mein Code, das ist mein Bug" wurde.
|
||||
|
||||
### Externe Tester sind ihr Gewicht in Gold wert
|
||||
|
||||
Carlas Feedback zur Pop-Out-Discoverability hat den Header-Button in v0.6.1 ausgelöst. Dass Pop-Outs nur per Rechtsklick erreichbar waren, hatte ich als Maintainer nicht mehr gesehen, ich kannte den Pfad blind. Carls Wunsch nach Theme-Varianten mit Helligkeits-Abstufungen hat mein Verständnis von "ein Theme = eine Farbe" auf "Theme-Familien mit Stimmungs-Varianten" verschoben. Jingliu hat TempTell-Persistence gefordert, was das Tab-System architektonisch in Frage stellt.
|
||||
|
||||
Solo hätte ich diese drei Dinge nicht erkannt. Punkt.
|
||||
|
||||
### release.yml und die Markdown-Hölle
|
||||
|
||||
Der `release.yml`-Workflow ist beim ersten v0.6.0-Tag-Push einfach nicht losgegangen. Ich habe Stunden in Permissions, Secret-Scopes und Tag-Trigger-Konfiguration gegraben, bevor ich verstand, was eigentlich los war: Der PowerShell-Heredoc-Footer im "Generate release body"-Step enthielt eine `---`-Markdown-Horizontal-Rule an Spalte 1, und genau das hat das YAML-Block-Scalar von `run: |` beendet. GitHub konnte die Workflow-Datei nicht parsen, also hat der Push-Tag-Trigger nie registriert.
|
||||
|
||||
Fix: Footer in eine externe `.github/release-footer.md` extrahiert, Workflow liest sie via `Get-Content` ein. Lektion: Wenn ein Workflow nicht triggert, verifiziere als Erstes, dass GitHub die Datei überhaupt parsen kann. Das war einer der Bugs, bei denen ich nach dem Fix kurz gelacht habe und mich dann gefragt, wie viele andere YAML-Dateien ich noch habe, die so eine Falle drin haben könnten.
|
||||
|
||||
---
|
||||
|
||||
## Was ich noch lerne
|
||||
|
||||
### Performance-Profiling im Game-Context
|
||||
|
||||
Der FPS-Drop-Bug aus Upstream Chat 2 ([#145](https://github.com/Infiziert90/ChatTwo/issues/145)) ist auch in Hellion Chat noch nicht reproduziert oder verifiziert. v1.0.0 hat mehrere Fixes auf den verdächtigen Pfaden (DbViewer O(N²) → O(N), AutoTranslate Lock-Serialisierung, EmoteCache HttpClient-Reuse), aber das systematische Vermessen unter Last fehlt mir. Ich muss noch lernen, wie man im Plugin-Kontext sauber misst, was wirklich das Frame-Budget frisst.
|
||||
|
||||
### Native-Interop und Pointer-Math
|
||||
|
||||
Auch nach dem WrapText-Span-Refactor in v0.5.4 ist mir Pointer-Math unsicher. ImGui zwingt einen an mehreren Stellen in `unsafe`-Code, und der Sicherheitsabstand zur "unbounded ArrayPool allocation"-Klasse von Bugs ist schmaler als mir lieb ist. Da will ich besser werden, bevor ich tieferes ImGui-Custom-Drawing anfasse.
|
||||
|
||||
### Test-Disziplin für Plugin-Code
|
||||
|
||||
Aktuell hat das Repo kein Test-Projekt. Das ist eine bewusste Entscheidung, keine vergessene. Plugin-Code mit FFXIV-Hooks und Dalamud-Lifecycle sauber zu testen ist nicht trivial, und ich hatte keinen Ansatz gefunden, der ohne riesiges Mocking-Gerüst sinnvoll wirkte. Privacy-Filter und Configuration-Migration wären gute Testkandidaten, weil sie isoliert sind. Steht auf der Liste, ist aber kein Quick-Win.
|
||||
|
||||
### Linux-Eigenheiten unter Wine
|
||||
|
||||
XDG-Compliance, libnotify-Integration, WireGuard-Network-Detection, alles in der [Roadmap](ROADMAP.md), und alles technisch noch nicht ganz klar. Wine und sandboxed Plugin-Code teilen nicht alle System-APIs, und ich weiß nicht, wo die Stolperfallen liegen, bevor ich sie gefunden habe.
|
||||
|
||||
---
|
||||
|
||||
## Einsatz von AI-Tools
|
||||
|
||||
Ich verwende Claude Code als Hilfsmittel, nicht als Ersatz für eigene Arbeit.
|
||||
|
||||
**Wofür ich AI einsetze:**
|
||||
|
||||
- Debugging von Problemen, bei denen ich nach längerer Eigenrecherche nicht weiterkomme
|
||||
- Mustererkennen über große Codebasen hinweg (z. B. der ChatTwo→HellionChat-Sweep über 80 Dateien)
|
||||
- Verständnisfragen zu C#- und Dalamud-Konzepten, die mir noch nicht geläufig sind
|
||||
- Code-Review-Sparring, bevor ich CodeRabbit drauflasse
|
||||
|
||||
**Was ich selbst mache:**
|
||||
|
||||
- Architektur und Designentscheidungen
|
||||
- Privacy-First-Defaults und das Threat-Model dahinter
|
||||
- Tester-Kommunikation und Roadmap-Priorisierung
|
||||
- Reviewen, Verifizieren, Pushen
|
||||
|
||||
Die Klassifikation und konkrete Beispiele stehen in [`AI_DISCLOSURE.md`](AI_DISCLOSURE.md). Mir ist wichtig, dass Nutzer und potenzielle Beiträger verstehen, wie der Code zustande gekommen ist, gerade bei einem Plugin, das mit Nutzerdaten arbeitet.
|
||||
|
||||
Ja, AI. Ja, alleine. Beides öfter erwähnt als nötig. Willkommen im Open-Source-Plugin-Klima.
|
||||
|
||||
---
|
||||
|
||||
## Warum diese Transparenz
|
||||
|
||||
Wer sich den Quellcode ansieht, soll wissen:
|
||||
|
||||
- Ich bin kein professioneller C#- oder Plugin-Entwickler und lerne weiterhin dazu
|
||||
- AI-Unterstützung ist ein Werkzeug, kein Ghostwriter
|
||||
- Die Privacy-Position, die Designentscheidungen und die Roadmap sind meine
|
||||
- Ich versuche, meinen Code so sauber und sicher zu halten, wie meine aktuellen Fähigkeiten es zulassen
|
||||
|
||||
Hellion Chat ist auch ein Lernprojekt, und das soll man dem Repository ansehen dürfen.
|
||||
|
||||
---
|
||||
|
||||
## Verlinkungen
|
||||
|
||||
- [`AI_DISCLOSURE.md`](AI_DISCLOSURE.md) — KI-Pair-Disclosure mit Klassifikations-Schema
|
||||
- [`CONTRIBUTORS.md`](CONTRIBUTORS.md) — wer hat dieses Plugin neben mir besser gemacht
|
||||
- [`../NOTICE.md`](../NOTICE.md) — Anerkennung an Infi und Anna für das Chat-2-Fundament
|
||||
- [`ROADMAP.md`](ROADMAP.md) — geplante Cycles und Themen
|
||||
+131
@@ -0,0 +1,131 @@
|
||||
# Hellion Chat — Roadmap
|
||||
|
||||
Geplante Arbeit nach dem v1.0.0 Standalone-Cut. Diese Liste ist absichtlich
|
||||
grob: konkrete Specs, Größenschätzungen und Repro-Steps liegen im
|
||||
internen Backlog. Tracking nach außen läuft über
|
||||
[GitHub Issues](https://github.com/JonKazama-Hellion/HellionChat/issues)
|
||||
mit dem `roadmap`-Label, sobald ein Item für einen Cycle eingeplant ist.
|
||||
|
||||
Reihenfolge ist Priorität, nicht Garantie. Items können sich verschieben
|
||||
oder ganz wegfallen wenn sie sich beim Brainstorm als nicht passend zur
|
||||
Privacy-First-Schnittmenge des Plugins erweisen.
|
||||
|
||||
---
|
||||
|
||||
## Nächster Cycle (v1.1.0)
|
||||
|
||||
- **Ad-Block / Spam-Filter** — Hybrid-Konzept aus eigenem Light-Filter und
|
||||
optionaler `NoSoliciting`-IPC-Integration. Adressiert Werbe-Spam in
|
||||
öffentlichen Channels und Tells. Größter Block des Cycles.
|
||||
- **Receive-Suppressed-Tells-Toggle** — Auto-Tell-Tabs greift auch wenn ein
|
||||
Drittplugin (z.B. XIVMessenger) die /tell-Anzeige global suppressed.
|
||||
Gleicher Hook-Layer wie Ad-Block, deshalb gebündelt.
|
||||
|
||||
## Mittelfristig (v1.1.x – v1.2.0)
|
||||
|
||||
- **Plugin-weite Theme-Varianten** — über die ChatColours-Presets aus v0.6.0
|
||||
hinaus. Mehrere komplette Window-Themes (Frame, Surface, Border, Text)
|
||||
inkl. Farbfamilien mit Helligkeits-Abstufungen. Anknüpfung an
|
||||
Hellion-Online-Media-Brand-Themes (Event Horizon, Night Blue, Indigo Violet
|
||||
und weitere).
|
||||
- **Database-Viewer Inline-Search** — Volltext-Suche im DB-Viewer via
|
||||
SQLite FTS5. Aktuell gibt es nur Datums- und Channel-Filter.
|
||||
- **TempTell Persistence** — Pin-Toggle auf TempTell-Tabs damit ausgewählte
|
||||
Tells einen Relog überleben. Tester-Wunsch von Jingliu.
|
||||
- **FontManager Async-Refactor** — `LoadGameSymFontAsync` aus dem
|
||||
blockierenden Plugin-Constructor herausziehen. Cold-Start-Hitching beim
|
||||
ersten Plugin-Start beheben (Severity niedrig, Plugin ist funktional).
|
||||
- **Separate Opacity Active vs. Inactive** — zweiter Slider für inaktive
|
||||
Fenster-Deckkraft. Upstream lehnt das ab; wir können hier anders
|
||||
entscheiden.
|
||||
- **Failed-Tell-Notification** — sichtbare Nachricht bei /tell-Fail
|
||||
(offline, restricted instance, blacklisted, world-mismatch) statt
|
||||
stillem Failure.
|
||||
- **Per-Tab Sound-Notification** — Sound-Toggle und optional eigene .wav
|
||||
pro Tab, mit Mute-In-Combat-Option.
|
||||
|
||||
## Langfrist (v1.x+)
|
||||
|
||||
### Storage-Backends (drei Stufen Bestätigung)
|
||||
|
||||
- MySQL/MariaDB-Backend für Multi-Device-Setups
|
||||
- PostgreSQL-Backend
|
||||
- AES-256-Verschlüsselung für sensible Channels mit lokalem Key
|
||||
|
||||
### Linux-spezifisch
|
||||
|
||||
- WireGuard-Network-Detection als optionaler Filter-Trigger
|
||||
- libnotify-Integration für native Linux-Toasts
|
||||
- XDG-Compliance (komplex unter Wine)
|
||||
|
||||
### UX und Tab-Management
|
||||
|
||||
- **Regex Tab Routing** — Plugin-Output-Spam in eigene Tabs, Tells
|
||||
bestimmter Personen automatisch sortieren. Klar abgegrenzt zum Ad-Block:
|
||||
Routing sortiert in Views, Block versteckt global.
|
||||
- **Auto-Detect Duties** — Tab-Switch beim Duty-Start via Condition-Flag.
|
||||
- **UX Bundle** — Vertical-Tab-Bar als Layout-Option, Shift+Mousewheel zum
|
||||
Tab-Header-Scrollen ohne Aktivierung, globaler Hotkey zum Schließen des
|
||||
aktiven Tabs.
|
||||
- **Configure Tab Title** — konfigurierbares Tab-Title-Format
|
||||
(Name / Name + abgekürzter World / voller Name / Custom), pro Tab
|
||||
überschreibbar.
|
||||
- **Name Display Options** — analog zu FFXIV-Vanilla (voller Name, Vorname
|
||||
abgekürzt, Initialen), per-Channel-Override möglich.
|
||||
- **Item & Flag Linking** — Outgoing: Shift-Klick auf Item/Flag sendet ins
|
||||
fokussierte Plugin-Input. Incoming: Item-Links und Map-Coords klickbar.
|
||||
- **Color Currently Selected Input Channel** — Channel-Selector-Button im
|
||||
Input-Bar mit Channel-Farbe einfärben.
|
||||
- **Plugin-Disclosure Pre-Send Filter** — konfigurierbare Wort-/Regex-Liste
|
||||
blockiert das Senden mit Pre-Send-Confirm. Schutz vor versehentlicher
|
||||
Plugin-Nennung in öffentlichen Channels.
|
||||
- **Chat Clear on Name Change** — bei Charakter-Namensänderung lokalen
|
||||
Verlauf migrieren oder löschen, Default Wipe für maximale Privacy.
|
||||
- **Hide Plugin Window on NG+ Screen** — Hide-Logik um zusätzliche
|
||||
Addon-Namen erweitern.
|
||||
- **Kick from Novice Network** — Mentor-Nische, Context-Menü-Eintrag mit
|
||||
Confirmation.
|
||||
- **Text-to-Speech für /tell** — eingehende Tells via TTS, optional pro
|
||||
Sender, mit Channel-Filter und Mute-In-Combat. Geringe Priorität.
|
||||
|
||||
### Distribution und Branding
|
||||
|
||||
- Hand-gezeichnetes Hellion-Logo (aktuell Platzhalter aus dem
|
||||
Hellion-Online-Media-Brand-Repo)
|
||||
- GitHub Action für automatischen `repo.json`-Sync nach Tag-Push
|
||||
- Submission ans Dalamud-Main-Plugin-Repo (zusätzlich zum Custom-Repo)
|
||||
|
||||
---
|
||||
|
||||
## Bug-Verifizierungen
|
||||
|
||||
Aus dem Upstream-Issue-Tracker übernommen, in Hellion Chat 1.0.0 noch
|
||||
nicht reproduziert oder verifiziert. Werden bei Gelegenheit gegen den
|
||||
aktuellen Stand getestet.
|
||||
|
||||
- **Right-Click Whisper Error** in Field Ops / Special Instances (Eureka,
|
||||
Bozja, Occult Crescent, DRS) — Upstream
|
||||
[#168](https://github.com/Infiziert90/ChatTwo/issues/168). Reply-Helper
|
||||
scheint `@World`-Suffix zu schlucken.
|
||||
- **FPS Drops with Plugin active** — Upstream
|
||||
[#145](https://github.com/Infiziert90/ChatTwo/issues/145). 10–20 % Drop
|
||||
seit upstream v1.29.19.0. v1.0.0 hat mehrere Fixes auf den verdächtigen
|
||||
Pfaden, Repro-Test gegen aktuellen Stand offen.
|
||||
- **Add Blacklist from Plugin Window** — Upstream
|
||||
[#140](https://github.com/Infiziert90/ChatTwo/issues/140). Right-Click
|
||||
Add-to-Blacklist wirft "Cannot locate character with that name", via
|
||||
Vanilla-Chat funktioniert es.
|
||||
- **DB-Viewer Column Sort** — sortiert State-Column lexikografisch statt
|
||||
numerisch (10 vor 2). XIVIM
|
||||
[#82](https://github.com/NightmareXIV/XIVInstantMessenger/issues/82),
|
||||
Repro in Hellion Chat offen.
|
||||
|
||||
---
|
||||
|
||||
## Lizenz-Boundary
|
||||
|
||||
Hellion Chat ist EUPL-1.2-lizenziert. Konzept-Imports aus AGPL-3.0-Plugins
|
||||
(z.B. XIV Instant Messenger) sind ausschließlich architektonische
|
||||
Inspiration, kein Code-Port. Imports aus dem GPL-3.0-kompatiblen
|
||||
Upstream-Bestand laufen weiter über
|
||||
[`UPSTREAM_SYNC.md`](UPSTREAM_SYNC.md).
|
||||
@@ -0,0 +1,92 @@
|
||||
# Third-party notices
|
||||
|
||||
HellionChat ships and depends on a number of third-party components.
|
||||
This document lists them, their licences and which of them touch the
|
||||
network. It is the inventory referenced by `PRIVACY.md`.
|
||||
|
||||
Last reviewed: 2026-05-03 (HellionChat v0.5.4).
|
||||
|
||||
---
|
||||
|
||||
## Direct NuGet dependencies
|
||||
|
||||
Pinned in `HellionChat/HellionChat.csproj`. Versions reflect the v1.0.0 build.
|
||||
|
||||
| Package | Version | Licence | Network | Purpose |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| [MessagePack](https://github.com/MessagePack-CSharp/MessagePack-CSharp) | 3.1.4 | MIT | no | Binary serialisation for the SQLite message store. |
|
||||
| [Microsoft.Data.Sqlite](https://learn.microsoft.com/dotnet/standard/data/sqlite/) | 10.0.7 | MIT | no | Local SQLite access for the message database. |
|
||||
| [morelinq](https://github.com/morelinq/MoreLINQ) | 4.4.0 | Apache-2.0 | no | LINQ helper extensions. |
|
||||
| [Pidgin](https://github.com/benjamin-hodgson/Pidgin) | 3.3.0 | MIT | no | Parser combinator library used for chat-input parsing. |
|
||||
| [SixLabors.ImageSharp](https://github.com/SixLabors/ImageSharp) | 3.1.12 | [Six Labors Split License 1.0](https://github.com/SixLabors/ImageSharp/blob/main/LICENSE) (OSI-approved; free for open-source / non-commercial use, commercial licence required for closed-source commercial use) | no | Image decoding for cached emotes. |
|
||||
|
||||
Six Labors note: HellionChat is an EUPL-1.2-licensed open-source
|
||||
project distributed at no cost. Use of ImageSharp 3.x under the
|
||||
Six Labors Split License 1.0 is permitted on that basis. Anyone
|
||||
forking HellionChat for closed-source or commercial redistribution
|
||||
should review the
|
||||
[Six Labors licence terms](https://github.com/SixLabors/ImageSharp/blob/main/LICENSE)
|
||||
and obtain a commercial licence if required.
|
||||
|
||||
## SDK and tooling
|
||||
|
||||
| Component | Licence | Notes |
|
||||
| --- | --- | --- |
|
||||
| [Dalamud.NET.Sdk](https://github.com/goatcorp/Dalamud) 15.0.0 | AGPL-3.0 (Dalamud) / SDK terms per goatcorp | Plugin SDK; pulls in DalamudPackager 15.0.0. |
|
||||
| [.NET 10 SDK](https://dotnet.microsoft.com/) | MIT | Build toolchain. |
|
||||
|
||||
## Bundled assets
|
||||
|
||||
| Asset | Licence | Source |
|
||||
| --- | --- | --- |
|
||||
| Exo 2 (`HellionFont.ttf`) | SIL Open Font License 1.1 | [Google Fonts / Natanael Gama](https://fonts.google.com/specimen/Exo+2). The OFL licence text travels embedded next to the font (`HellionFont-OFL.txt`) to satisfy the "licence must be distributed with the font" clause. |
|
||||
| Hellion plugin icon (`images/icon.png`) | © Hellion Media, included under the project licence (EUPL-1.2). | Original artwork. |
|
||||
|
||||
---
|
||||
|
||||
## Upstream code
|
||||
|
||||
HellionChat is a fork of [Chat 2](https://github.com/Infiziert90/ChatTwo)
|
||||
by Infiziert90 (Infi) and Anna Clemens, also licensed under EUPL-1.2.
|
||||
The bulk of the code, including the message store architecture, the
|
||||
channel logic, the hook system and the ImGui chat window, originates
|
||||
from upstream. See `../NOTICE.md` and `UPSTREAM_SYNC.md` for the
|
||||
attribution and the cherry-pick policy.
|
||||
|
||||
---
|
||||
|
||||
## Components that touch the network
|
||||
|
||||
Of everything listed above, **none** of the bundled or NuGet
|
||||
components opens network connections on their own. All outbound
|
||||
traffic is initiated explicitly by HellionChat's own source files
|
||||
and is documented in `PRIVACY.md` under "Outbound network calls":
|
||||
|
||||
- `HellionChat/EmoteCache.cs` → BetterTTV API + CDN (opt-out via setting)
|
||||
- `HellionChat/FontManager.cs` → Square Enix Lodestone font CDN (one-time
|
||||
download)
|
||||
|
||||
---
|
||||
|
||||
## Verifying this list
|
||||
|
||||
To regenerate the dependency inventory after a version bump:
|
||||
|
||||
```bash
|
||||
dotnet list HellionChat.sln package --include-transitive
|
||||
```
|
||||
|
||||
The "direct NuGet dependencies" table above only lists direct
|
||||
references. Transitive dependencies pulled in by Dalamud SDK or by
|
||||
the listed packages are covered by the SDK / package licences and
|
||||
documented by their respective maintainers.
|
||||
|
||||
To re-audit the network-call inventory:
|
||||
|
||||
```bash
|
||||
grep -rn -E "HttpClient|HttpRequest|new Uri\(|https?://" \
|
||||
--include="*.cs" HellionChat/
|
||||
```
|
||||
|
||||
Any new hit that is not a click-through (`Util.OpenLink`) or a
|
||||
payload-parsing call must be added to `PRIVACY.md` before release.
|
||||
@@ -0,0 +1,110 @@
|
||||
# Upstream sync workflow
|
||||
|
||||
HellionChat is a standalone EUPL-1.2 fork of [Chat 2](https://github.com/Infiziert90/ChatTwo).
|
||||
We pull selected patches from upstream manually instead of running an
|
||||
automated mirror. This file documents how that works so anyone (including
|
||||
future-me) can do it cleanly.
|
||||
|
||||
## One-time setup
|
||||
|
||||
Add the upstream repo as a remote on a fresh clone:
|
||||
|
||||
```bash
|
||||
git remote add upstream https://github.com/Infiziert90/ChatTwo.git
|
||||
git fetch upstream
|
||||
```
|
||||
|
||||
Verify both remotes are wired up:
|
||||
|
||||
```bash
|
||||
git remote -v
|
||||
# origin https://github.com/JonKazama-Hellion/HellionChat.git (fetch)
|
||||
# origin https://github.com/JonKazama-Hellion/HellionChat.git (push)
|
||||
# upstream https://github.com/Infiziert90/ChatTwo.git (fetch)
|
||||
# upstream https://github.com/Infiziert90/ChatTwo.git (push)
|
||||
```
|
||||
|
||||
You never push to `upstream`. It is read-only for us.
|
||||
|
||||
## Reviewing what is new upstream
|
||||
|
||||
Before any feature cycle starts I run a quick check:
|
||||
|
||||
```bash
|
||||
git fetch upstream
|
||||
git log --oneline main..upstream/main | head -30
|
||||
```
|
||||
|
||||
That shows every commit Infi or contributors landed since the last sync.
|
||||
Read the messages, decide which ones apply.
|
||||
|
||||
## What we cherry-pick
|
||||
|
||||
**Always:** security fixes, API-version compatibility patches (Dalamud
|
||||
API 15 → 16 → ...), BetterTTV / emote-cache fixes, regression fixes for
|
||||
the upstream behaviour we still rely on.
|
||||
|
||||
**Sometimes:** small bug fixes in `MessageManager.cs`, `MessageStore.cs`,
|
||||
`ChatLogWindow.cs`, the Tabs system. Pull them when they touch code we
|
||||
have not heavily modified.
|
||||
|
||||
**Never:** webinterface changes (the entire webinterface tree is gone in
|
||||
HellionChat), changes that conflict with the privacy filter, changes that
|
||||
re-add upstream defaults we deliberately reversed (full-history logging,
|
||||
Tell Exclusive defaults, etc.).
|
||||
|
||||
## How we cherry-pick
|
||||
|
||||
Always with `-x` so authorship and the original commit hash stay
|
||||
visible:
|
||||
|
||||
```bash
|
||||
git checkout -b sync/upstream-<topic> main
|
||||
git cherry-pick -x <upstream-commit-sha>
|
||||
```
|
||||
|
||||
`-x` adds a `(cherry picked from commit <sha>)` line to the commit
|
||||
message. That preserves the upstream-author credit and lets anyone
|
||||
reading `git log` trace the change back to ChatTwo. Co-Author trail
|
||||
intact, no AI lines, no "Hellion" prefix on commits that were not
|
||||
authored by us.
|
||||
|
||||
## Conflict handling
|
||||
|
||||
When a cherry-pick conflicts:
|
||||
|
||||
1. Resolve the conflict by hand. Do not "fix" upstream code to match
|
||||
Hellion conventions; that is what the merge marker showed us.
|
||||
2. If the conflict is fundamental (touches code that no longer exists
|
||||
in our fork), abort the cherry-pick and document why in
|
||||
`Hellion Chat Backlog.md` instead. Some upstream patches are not
|
||||
portable; that is fine.
|
||||
3. After a successful resolve, the commit message stays identical to
|
||||
the upstream message, with the `-x` cherry-pick footer Git appends
|
||||
automatically. Do not rewrite the message to match our format.
|
||||
|
||||
## Pushing the sync
|
||||
|
||||
Cherry-picked commits go through the same review as our own work: the
|
||||
sync branch lands in `main` via a no-fast-forward merge, then a release
|
||||
tag if the user-visible behaviour changes (otherwise just merged).
|
||||
|
||||
```bash
|
||||
git checkout main
|
||||
git merge --no-ff sync/upstream-<topic> -m "merge: upstream sync — <topic>"
|
||||
```
|
||||
|
||||
## When upstream goes silent
|
||||
|
||||
If Chat 2 stops receiving updates entirely we keep this workflow alive
|
||||
anyway. The remote stays configured, the documentation stays here. The
|
||||
moment maintenance picks back up we are ready to pull again.
|
||||
|
||||
## When upstream takes a direction we cannot follow
|
||||
|
||||
If a future ChatTwo release breaks compatibility with our privacy
|
||||
philosophy in a way we cannot resolve (e.g. mandatory cloud sync,
|
||||
removal of the local message store, a license change that makes EUPL
|
||||
incompatible), HellionChat continues on its own from the last
|
||||
compatible cherry-pick. The history we already inherited stays under
|
||||
EUPL-1.2 and stays attributed.
|
||||
Reference in New Issue
Block a user