chore: housekeeping — linter & formatter setup
Add .prettierrc.json, .markdownlint.json, .yamllint.yaml, .gitattributes Run CSharpier, Prettier and markdownlint across the entire codebase. No logic changes — formatting, using order and line endings only.
This commit is contained in:
@@ -1,73 +1,73 @@
|
||||
name: Bug report
|
||||
description: Something in HellionChat is broken or behaves wrong
|
||||
labels:
|
||||
- bug
|
||||
- bug
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thanks for reporting. Please fill in the fields below so I can
|
||||
reproduce the issue. If this is a security issue, stop here and
|
||||
report it privately to [kontakt@hellion-media.de](mailto:kontakt@hellion-media.de?subject=%5BHellionChat%20Security%5D)
|
||||
instead.
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thanks for reporting. Please fill in the fields below so I can
|
||||
reproduce the issue. If this is a security issue, stop here and
|
||||
report it privately to [kontakt@hellion-media.de](mailto:kontakt@hellion-media.de?subject=%5BHellionChat%20Security%5D)
|
||||
instead.
|
||||
|
||||
- type: input
|
||||
id: version
|
||||
attributes:
|
||||
label: HellionChat version
|
||||
description: From Settings → Information → Version
|
||||
placeholder: "0.5.4"
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: dropdown
|
||||
id: platform
|
||||
attributes:
|
||||
label: Platform
|
||||
options:
|
||||
- Windows (XIVLauncher)
|
||||
- Linux (XIVLauncher Core)
|
||||
- macOS (XIVLauncher Core / wine)
|
||||
- Other
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: what-happened
|
||||
attributes:
|
||||
label: What happened
|
||||
description: Plain description, no log dumps yet
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: expected
|
||||
attributes:
|
||||
label: What you expected
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: steps
|
||||
attributes:
|
||||
label: How to reproduce
|
||||
description: Step-by-step from "open settings" or "log in" through to the broken behaviour
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: log
|
||||
attributes:
|
||||
label: Relevant /xllog excerpt
|
||||
description: Filter for "HellionChat" if the log is huge
|
||||
render: text
|
||||
|
||||
- type: checkboxes
|
||||
id: confirm
|
||||
attributes:
|
||||
label: Pre-flight
|
||||
options:
|
||||
- label: I am running the latest version of HellionChat
|
||||
- type: input
|
||||
id: version
|
||||
attributes:
|
||||
label: HellionChat version
|
||||
description: From Settings → Information → Version
|
||||
placeholder: "0.5.4"
|
||||
validations:
|
||||
required: true
|
||||
- label: I have searched existing issues for duplicates
|
||||
|
||||
- type: dropdown
|
||||
id: platform
|
||||
attributes:
|
||||
label: Platform
|
||||
options:
|
||||
- Windows (XIVLauncher)
|
||||
- Linux (XIVLauncher Core)
|
||||
- macOS (XIVLauncher Core / wine)
|
||||
- Other
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: what-happened
|
||||
attributes:
|
||||
label: What happened
|
||||
description: Plain description, no log dumps yet
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: expected
|
||||
attributes:
|
||||
label: What you expected
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: steps
|
||||
attributes:
|
||||
label: How to reproduce
|
||||
description: Step-by-step from "open settings" or "log in" through to the broken behaviour
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: log
|
||||
attributes:
|
||||
label: Relevant /xllog excerpt
|
||||
description: Filter for "HellionChat" if the log is huge
|
||||
render: text
|
||||
|
||||
- type: checkboxes
|
||||
id: confirm
|
||||
attributes:
|
||||
label: Pre-flight
|
||||
options:
|
||||
- label: I am running the latest version of HellionChat
|
||||
required: true
|
||||
- label: I have searched existing issues for duplicates
|
||||
required: true
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
blank_issues_enabled: false
|
||||
|
||||
contact_links:
|
||||
- name: Security vulnerability
|
||||
url: mailto:kontakt@hellion-media.de?subject=%5BHellionChat%20Security%5D
|
||||
about: Do not open a public issue for security problems. Report by e-mail instead.
|
||||
- name: Security vulnerability
|
||||
url: mailto:kontakt@hellion-media.de?subject=%5BHellionChat%20Security%5D
|
||||
about: Do not open a public issue for security problems. Report by e-mail instead.
|
||||
|
||||
- name: Upstream Chat 2 issue
|
||||
url: https://github.com/Infiziert90/ChatTwo/issues
|
||||
about: If the issue exists in upstream Chat 2 too, please report it there so the original maintainers see it as well.
|
||||
- name: Upstream Chat 2 issue
|
||||
url: https://github.com/Infiziert90/ChatTwo/issues
|
||||
about:
|
||||
If the issue exists in upstream Chat 2 too, please report it there so the original maintainers see it as well.
|
||||
|
||||
- name: Discord
|
||||
url: https://discord.com/users/j.j_kazama
|
||||
about: Quick questions, casual feedback. Bug reports still go through the issue tracker for tracking.
|
||||
- name: Discord
|
||||
url: https://discord.com/users/j.j_kazama
|
||||
about: Quick questions, casual feedback. Bug reports still go through the issue tracker for tracking.
|
||||
|
||||
@@ -1,55 +1,57 @@
|
||||
name: Feature request
|
||||
description: Suggest a feature or enhancement for HellionChat
|
||||
labels:
|
||||
- enhancement
|
||||
- enhancement
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thanks for the suggestion. HellionChat focuses on privacy by
|
||||
default and a small, well-scoped feature set. Suggestions that
|
||||
align with that scope are easier to accept than ones that pull
|
||||
the plugin toward "do everything".
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thanks for the suggestion. HellionChat focuses on privacy by
|
||||
default and a small, well-scoped feature set. Suggestions that
|
||||
align with that scope are easier to accept than ones that pull
|
||||
the plugin toward "do everything".
|
||||
|
||||
- type: textarea
|
||||
id: problem
|
||||
attributes:
|
||||
label: What problem are you trying to solve
|
||||
description: The user-side problem, not the proposed solution yet
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: solution
|
||||
attributes:
|
||||
label: What you would like HellionChat to do
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: alternatives
|
||||
attributes:
|
||||
label: Alternatives you have considered
|
||||
description: Other plugins, manual workarounds, settings combinations
|
||||
|
||||
- type: dropdown
|
||||
id: scope
|
||||
attributes:
|
||||
label: Scope estimate from your side
|
||||
options:
|
||||
- "Small (one tab, one toggle, one filter)"
|
||||
- "Medium (a settings section, persistent state, one new file)"
|
||||
- "Large (architectural, touches the message pipeline or the database)"
|
||||
- "I don't know"
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: checkboxes
|
||||
id: confirm
|
||||
attributes:
|
||||
label: Pre-flight
|
||||
options:
|
||||
- label: I have searched existing issues for similar requests
|
||||
- type: textarea
|
||||
id: problem
|
||||
attributes:
|
||||
label: What problem are you trying to solve
|
||||
description: The user-side problem, not the proposed solution yet
|
||||
validations:
|
||||
required: true
|
||||
- label: I understand HellionChat is a privacy-focused fork and not a feature parity tool with upstream Chat 2
|
||||
|
||||
- type: textarea
|
||||
id: solution
|
||||
attributes:
|
||||
label: What you would like HellionChat to do
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: alternatives
|
||||
attributes:
|
||||
label: Alternatives you have considered
|
||||
description: Other plugins, manual workarounds, settings combinations
|
||||
|
||||
- type: dropdown
|
||||
id: scope
|
||||
attributes:
|
||||
label: Scope estimate from your side
|
||||
options:
|
||||
- "Small (one tab, one toggle, one filter)"
|
||||
- "Medium (a settings section, persistent state, one new file)"
|
||||
- "Large (architectural, touches the message pipeline or the database)"
|
||||
- "I don't know"
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: checkboxes
|
||||
id: confirm
|
||||
attributes:
|
||||
label: Pre-flight
|
||||
options:
|
||||
- label: I have searched existing issues for similar requests
|
||||
required: true
|
||||
- label:
|
||||
I understand HellionChat is a privacy-focused fork and not a feature parity tool with upstream Chat
|
||||
2
|
||||
required: true
|
||||
|
||||
@@ -18,8 +18,7 @@ mailto:kontakt@hellion-media.de?subject=%5BHellionChat%20Security%5D
|
||||
|
||||
- [ ] Bug fix (non-breaking change that fixes an issue)
|
||||
- [ ] New feature (non-breaking change that adds behaviour)
|
||||
- [ ] Breaking change (config migration, removed feature, or behaviour
|
||||
change that user-visible defaults rely on)
|
||||
- [ ] Breaking change (config migration, removed feature, or behaviour change that user-visible defaults rely on)
|
||||
- [ ] Documentation only
|
||||
- [ ] Translation update
|
||||
- [ ] Build, CI or tooling change
|
||||
@@ -56,15 +55,11 @@ new commands, new translations, removed behaviour. If none, write
|
||||
|
||||
## Checklist
|
||||
|
||||
- [ ] I have read [CONTRIBUTING.md](../CONTRIBUTING.md) and
|
||||
[CODE_OF_CONDUCT.md](../CODE_OF_CONDUCT.md).
|
||||
- [ ] I have read [CONTRIBUTING.md](../CONTRIBUTING.md) and [CODE_OF_CONDUCT.md](../CODE_OF_CONDUCT.md).
|
||||
- [ ] My change matches the existing code style (`.editorconfig`).
|
||||
- [ ] I added or updated tests where the existing test infrastructure
|
||||
made that practical, or I have explained why tests are not
|
||||
applicable.
|
||||
- [ ] I updated the README, in-plugin strings or documentation if my
|
||||
change is user-visible.
|
||||
- [ ] I did not include any AI-generated code without disclosing it
|
||||
in the PR description (see [AI_DISCLOSURE.md](../docs/AI_DISCLOSURE.md)).
|
||||
- [ ] I confirm my contribution is released under the
|
||||
[EUPL-1.2](../LICENSE).
|
||||
- [ ] I added or updated tests where the existing test infrastructure made that practical, or I have explained why tests
|
||||
are not applicable.
|
||||
- [ ] I updated the README, in-plugin strings or documentation if my change is user-visible.
|
||||
- [ ] I did not include any AI-generated code without disclosing it in the PR description (see
|
||||
[AI_DISCLOSURE.md](../docs/AI_DISCLOSURE.md)).
|
||||
- [ ] I confirm my contribution is released under the [EUPL-1.2](../LICENSE).
|
||||
|
||||
+38
-38
@@ -1,42 +1,42 @@
|
||||
version: 2
|
||||
|
||||
updates:
|
||||
# NuGet package updates for the plugin project. Weekly cadence keeps the
|
||||
# noise down while still catching transitive security advisories within
|
||||
# a few days of disclosure.
|
||||
- package-ecosystem: nuget
|
||||
directory: /HellionChat
|
||||
schedule:
|
||||
interval: weekly
|
||||
day: monday
|
||||
time: "07:00"
|
||||
timezone: Europe/Berlin
|
||||
open-pull-requests-limit: 5
|
||||
labels:
|
||||
- dependencies
|
||||
- nuget
|
||||
commit-message:
|
||||
prefix: "chore(deps)"
|
||||
groups:
|
||||
patches:
|
||||
update-types:
|
||||
- patch
|
||||
minor:
|
||||
update-types:
|
||||
- minor
|
||||
# NuGet package updates for the plugin project. Weekly cadence keeps the
|
||||
# noise down while still catching transitive security advisories within
|
||||
# a few days of disclosure.
|
||||
- package-ecosystem: nuget
|
||||
directory: /HellionChat
|
||||
schedule:
|
||||
interval: weekly
|
||||
day: monday
|
||||
time: "07:00"
|
||||
timezone: Europe/Berlin
|
||||
open-pull-requests-limit: 5
|
||||
labels:
|
||||
- dependencies
|
||||
- nuget
|
||||
commit-message:
|
||||
prefix: "chore(deps)"
|
||||
groups:
|
||||
patches:
|
||||
update-types:
|
||||
- patch
|
||||
minor:
|
||||
update-types:
|
||||
- minor
|
||||
|
||||
# GitHub Actions versions in .github/workflows. Lower cadence because
|
||||
# Action releases ship less frequently and are usually safe to defer
|
||||
# for a month.
|
||||
- package-ecosystem: github-actions
|
||||
directory: /
|
||||
schedule:
|
||||
interval: monthly
|
||||
time: "07:00"
|
||||
timezone: Europe/Berlin
|
||||
open-pull-requests-limit: 3
|
||||
labels:
|
||||
- dependencies
|
||||
- github-actions
|
||||
commit-message:
|
||||
prefix: "chore(actions)"
|
||||
# GitHub Actions versions in .github/workflows. Lower cadence because
|
||||
# Action releases ship less frequently and are usually safe to defer
|
||||
# for a month.
|
||||
- package-ecosystem: github-actions
|
||||
directory: /
|
||||
schedule:
|
||||
interval: monthly
|
||||
time: "07:00"
|
||||
timezone: Europe/Berlin
|
||||
open-pull-requests-limit: 3
|
||||
labels:
|
||||
- dependencies
|
||||
- github-actions
|
||||
commit-message:
|
||||
prefix: "chore(actions)"
|
||||
|
||||
@@ -2,11 +2,16 @@
|
||||
subtitle: "Theme Foundation"
|
||||
versionsnatur: "Major-UI-Cycle"
|
||||
---
|
||||
- Theme-Engine mit fünf Built-In-Themes: Hellion Arctic (Default), Chat 2 Klassik, Event Horizon, Moonlit Bloom, Mint Grove
|
||||
- Settings öffnet jetzt eine Card-Grid-Übersicht — Klick auf eine Card führt in den Detail-View, Breadcrumb und ESC zurück zur Übersicht
|
||||
|
||||
- Theme-Engine mit fünf Built-In-Themes: Hellion Arctic (Default), Chat 2 Klassik, Event Horizon, Moonlit Bloom, Mint
|
||||
Grove
|
||||
- Settings öffnet jetzt eine Card-Grid-Übersicht — Klick auf eine Card führt in den Detail-View, Breadcrumb und ESC
|
||||
zurück zur Übersicht
|
||||
- Themes-Tab mit Mini-Mockup pro Theme, Live-Switch beim Klick
|
||||
- Eigene Themes als JSON in `pluginConfigs/HellionChat/themes/` — Beispiel-Vorlage wird beim ersten Start automatisch abgelegt
|
||||
- Eigene Themes als JSON in `pluginConfigs/HellionChat/themes/` — Beispiel-Vorlage wird beim ersten Start automatisch
|
||||
abgelegt
|
||||
- Optional pro Theme eigene Chat-Channel-Farben mit Übernehmen/Behalten-Banner — niemals automatisch überschrieben
|
||||
- Plugin-Icon zum Hellion-Forge-Hammer gewechselt
|
||||
- Migration v13 → v14: alle User landen auf Hellion Arctic. Wer den Upstream-Look will, wählt Chat 2 Klassik in Settings → Themes
|
||||
- Migration v13 → v14: alle User landen auf Hellion Arctic. Wer den Upstream-Look will, wählt Chat 2 Klassik in Settings
|
||||
→ Themes
|
||||
- Anleitung zum Schreiben eigener Themes: `docs/THEME-AUTHORING.md`
|
||||
|
||||
@@ -2,14 +2,23 @@
|
||||
subtitle: "Layout Refresh"
|
||||
versionsnatur: "Major-UI-Cycle"
|
||||
---
|
||||
- Sidebar im neuen Look: fix 44 px breit, nur Icons, Tab-Name als Tooltip beim Hover, vertikale Akzent-Pill markiert den aktiven Tab
|
||||
|
||||
- Sidebar im neuen Look: fix 44 px breit, nur Icons, Tab-Name als Tooltip beim Hover, vertikale Akzent-Pill markiert den
|
||||
aktiven Tab
|
||||
- Top-Tabs bekommen eine Akzent-Underline statt Background-Fill am aktiven Tab
|
||||
- Pro Tab eigenes Icon wählbar in Einstellungen → Tabs (FontAwesome-Pool)
|
||||
- Auto-Tell-Tabs sind jetzt visuell unterscheidbar: jeder Tell-Partner bekommt ein eigenes Icon (envelope/star/heart/bell/bookmark/flag/fire) plus eigene Farbe aus 12-Farb-Palette — 84 Kombinationen, gleicher Partner ergibt konsistent dieselbe
|
||||
- Pulsierender roter Dot oben rechts am Sidebar-Icon zeigt ungelesene Nachrichten an. Sanft, 2-Sekunden-Cycle, deaktivierbar über `Configuration.ReduceMotion` (UI-Toggle in v1.3.0)
|
||||
- Bottom-Status-Bar (22 px) mit fünf Live-Slots: aktiver Channel + Color-Dot, Privacy-Badge, Tab/Message-Counter, Auto-Tell-Counter, Plugin-Version. Update 1×/Sek
|
||||
- Card-Rows als Default-Message-Render: Sender-Header in Channel-Farbe, Body neue Zeile, dezenter Trenner. `Compact Density`-Toggle in Aussehen schaltet zurück auf den Einzeiler
|
||||
- Bug-Fix: Settings speichern löscht den Chat-Verlauf nicht mehr. Refilter läuft jetzt nur wenn Filter-relevante Settings geändert wurden — Cosmetic-Änderungen lassen den Chat unverändert. Persistente und Auto-Tell-Tabs überleben beide
|
||||
- Auto-Tell-Tabs sind jetzt visuell unterscheidbar: jeder Tell-Partner bekommt ein eigenes Icon
|
||||
(envelope/star/heart/bell/bookmark/flag/fire) plus eigene Farbe aus 12-Farb-Palette — 84 Kombinationen, gleicher
|
||||
Partner ergibt konsistent dieselbe
|
||||
- Pulsierender roter Dot oben rechts am Sidebar-Icon zeigt ungelesene Nachrichten an. Sanft, 2-Sekunden-Cycle,
|
||||
deaktivierbar über `Configuration.ReduceMotion` (UI-Toggle in v1.3.0)
|
||||
- Bottom-Status-Bar (22 px) mit fünf Live-Slots: aktiver Channel + Color-Dot, Privacy-Badge, Tab/Message-Counter,
|
||||
Auto-Tell-Counter, Plugin-Version. Update 1×/Sek
|
||||
- Card-Rows als Default-Message-Render: Sender-Header in Channel-Farbe, Body neue Zeile, dezenter Trenner.
|
||||
`Compact Density`-Toggle in Aussehen schaltet zurück auf den Einzeiler
|
||||
- Bug-Fix: Settings speichern löscht den Chat-Verlauf nicht mehr. Refilter läuft jetzt nur wenn Filter-relevante
|
||||
Settings geändert wurden — Cosmetic-Änderungen lassen den Chat unverändert. Persistente und Auto-Tell-Tabs überleben
|
||||
beide
|
||||
- Bug-Fix: Hellion-Schrift (Exo 2) blockt die Schriftgröße nicht mehr — 4K-User können hochskalieren
|
||||
- Migration v14 → v15: alte Theme-Felder entfernt, alle anderen Settings bleiben
|
||||
|
||||
|
||||
@@ -2,15 +2,28 @@
|
||||
subtitle: "Settings Cleanup"
|
||||
versionsnatur: "UX-Polish-Cycle"
|
||||
---
|
||||
- Settings-Übersicht thematisch re-sortiert: zusammenhängende Optionen wohnen jetzt zusammen, jede Card hat einen kurzen Untertitel — kein Raten mehr wo eine Setting steckt
|
||||
- Drei neue Cards: **Theme & Layout** (Theme-Picker, Fenster-Style, Zeitstempel-Style), **Schriften & Farben** (Schriftart, Schriftgröße, Chat-Farben pro Channel), **Daten-Verwaltung** (Aufbewahrung, Cleanup, Export, DB-Viewer, Advanced-Tools — vorher zwischen Datenschutz und Datenbank verteilt)
|
||||
|
||||
- Settings-Übersicht thematisch re-sortiert: zusammenhängende Optionen wohnen jetzt zusammen, jede Card hat einen kurzen
|
||||
Untertitel — kein Raten mehr wo eine Setting steckt
|
||||
- Drei neue Cards: **Theme & Layout** (Theme-Picker, Fenster-Style, Zeitstempel-Style), **Schriften & Farben**
|
||||
(Schriftart, Schriftgröße, Chat-Farben pro Channel), **Daten-Verwaltung** (Aufbewahrung, Cleanup, Export, DB-Viewer,
|
||||
Advanced-Tools — vorher zwischen Datenschutz und Datenbank verteilt)
|
||||
- Datenschutz fokussiert sich jetzt auf eine Aufgabe: den Privacy-Filter
|
||||
- Der Auto-Tell-Tabs-History-Preload-Slider ist von Datenschutz nach Chat → Auto-Tell-Tabs umgezogen
|
||||
- KeybindMode wohnt jetzt unter Allgemein → Eingabe statt unter Sprache
|
||||
- Vier tote Schema-Felder entfernt (alle obsolet seit der Theme-Engine in v1.1.0): `Stilüberschreiben`-Toggle, `Stilname`-Auswahl, alter `WindowAlpha`-Slider, ungenutztes `ShowThemeQuickPicker`
|
||||
- Migration v15 → v16: alter `WindowAlpha`-Wert wird automatisch nach `Theme & Layout → Fenster-Style → Fenster-Transparenz` gemappt (nur wenn der Slider noch auf Default 0.85 stand, sonst gewinnt der User-Wert). Backup der Pre-v16-Config liegt unter `pluginConfigs/HellionChat.json.pre-v16-backup`. User die `Stilüberschreiben` aktiv hatten sehen einen einmaligen Hinweis-Toast
|
||||
- UX-Default-Bumps für Bestand-User mit Default-Werten: Card-Rows-Layout zurück auf Single-Line, NG+ standardmäßig hidden, gleiche Zeitstempel werden zusammengefasst, MaxLinesToRender auf konservativere 2500
|
||||
- Frische Installs starten mit dem Hellion-Brand-Chat-Color-Preset out-of-the-box (der First-Run-Wizard hat keine Preset-Wahl)
|
||||
- Hinweis zum Window-Transparenz-Slider in der Beschreibung: Dalamud's per-Window-Hamburger-Menü (oben rechts in der Titelleiste) bietet eigene Overrides für Deckkraft, Hintergrund-Blur, Anpinnen und Durchklick — die haben Vorrang über unseren Slider für das jeweilige Fenster
|
||||
- Vier tote Schema-Felder entfernt (alle obsolet seit der Theme-Engine in v1.1.0): `Stilüberschreiben`-Toggle,
|
||||
`Stilname`-Auswahl, alter `WindowAlpha`-Slider, ungenutztes `ShowThemeQuickPicker`
|
||||
- Migration v15 → v16: alter `WindowAlpha`-Wert wird automatisch nach
|
||||
`Theme & Layout → Fenster-Style → Fenster-Transparenz` gemappt (nur wenn der Slider noch auf Default 0.85 stand, sonst
|
||||
gewinnt der User-Wert). Backup der Pre-v16-Config liegt unter `pluginConfigs/HellionChat.json.pre-v16-backup`. User
|
||||
die `Stilüberschreiben` aktiv hatten sehen einen einmaligen Hinweis-Toast
|
||||
- UX-Default-Bumps für Bestand-User mit Default-Werten: Card-Rows-Layout zurück auf Single-Line, NG+ standardmäßig
|
||||
hidden, gleiche Zeitstempel werden zusammengefasst, MaxLinesToRender auf konservativere 2500
|
||||
- Frische Installs starten mit dem Hellion-Brand-Chat-Color-Preset out-of-the-box (der First-Run-Wizard hat keine
|
||||
Preset-Wahl)
|
||||
- Hinweis zum Window-Transparenz-Slider in der Beschreibung: Dalamud's per-Window-Hamburger-Menü (oben rechts in der
|
||||
Titelleiste) bietet eigene Overrides für Deckkraft, Hintergrund-Blur, Anpinnen und Durchklick — die haben Vorrang über
|
||||
unseren Slider für das jeweilige Fenster
|
||||
|
||||
Pure UX-Polish, keine neuen Features. Nächster Cycle (v1.3.0): Animation-Polish (Lerps, Theme-Crossfade, Quick-Picker) wie ursprünglich geplant.
|
||||
Pure UX-Polish, keine neuen Features. Nächster Cycle (v1.3.0): Animation-Polish (Lerps, Theme-Crossfade, Quick-Picker)
|
||||
wie ursprünglich geplant.
|
||||
|
||||
@@ -2,12 +2,22 @@
|
||||
subtitle: "Theme Expansion"
|
||||
versionsnatur: "Theme-Pack-Patch"
|
||||
---
|
||||
- Vier neue Built-in-Themes verlängern die Auswahl im Picker — keine Engine-Änderung, keine Settings angefasst, einfach mehr Farboptionen
|
||||
- **Night Blue** — Royal Blue auf tiefem Marineblau. Kühles Tech-Dashboard-Mood, bewusst neutral gehalten damit es sich nicht mit den Brand-Themes beißt
|
||||
- **Indigo Violet** — Royal Violet auf Deep Indigo mit Türkis-Mint-Counter für Aurora-Glitter-Stimmung. Schwester von Event Horizon, aber dunkler und dichter; der Türkis-Akzent hält die beiden klar auseinander
|
||||
- **Forge Merchantman** — Patina-Bronze auf Workshop-Slate mit warmem Bernstein-Counter. Hellion Forge bekommt ein eigenes Theme im Plugin selbst — Schwester von Hellion Arctic, aber grüner und wärmer statt kaltem Cyan
|
||||
- **Hellion Spectrum** — Farbenblind-sichere Channel-Farben (Deuteranopie/Protanopie) auf Basis der Wong/Okabe-Ito-Palette. Channel-Identität bleibt erhalten (Tell pink, Yell gelb, Shout orange, Party blau, FC grün); die Töne sind so gewählt dass jeder Channel auch unter Rot-Grün-Schwäche klar trennbar bleibt. Deckt rund 99 % aller CVD-Fälle ab
|
||||
- Kein Schema-Bump, keine Migration. Das Default-Theme bleibt **Hellion Arctic**, eigene Custom-Themes laufen unverändert weiter
|
||||
|
||||
- Vier neue Built-in-Themes verlängern die Auswahl im Picker — keine Engine-Änderung, keine Settings angefasst, einfach
|
||||
mehr Farboptionen
|
||||
- **Night Blue** — Royal Blue auf tiefem Marineblau. Kühles Tech-Dashboard-Mood, bewusst neutral gehalten damit es sich
|
||||
nicht mit den Brand-Themes beißt
|
||||
- **Indigo Violet** — Royal Violet auf Deep Indigo mit Türkis-Mint-Counter für Aurora-Glitter-Stimmung. Schwester von
|
||||
Event Horizon, aber dunkler und dichter; der Türkis-Akzent hält die beiden klar auseinander
|
||||
- **Forge Merchantman** — Patina-Bronze auf Workshop-Slate mit warmem Bernstein-Counter. Hellion Forge bekommt ein
|
||||
eigenes Theme im Plugin selbst — Schwester von Hellion Arctic, aber grüner und wärmer statt kaltem Cyan
|
||||
- **Hellion Spectrum** — Farbenblind-sichere Channel-Farben (Deuteranopie/Protanopie) auf Basis der
|
||||
Wong/Okabe-Ito-Palette. Channel-Identität bleibt erhalten (Tell pink, Yell gelb, Shout orange, Party blau, FC grün);
|
||||
die Töne sind so gewählt dass jeder Channel auch unter Rot-Grün-Schwäche klar trennbar bleibt. Deckt rund 99 % aller
|
||||
CVD-Fälle ab
|
||||
- Kein Schema-Bump, keine Migration. Das Default-Theme bleibt **Hellion Arctic**, eigene Custom-Themes laufen
|
||||
unverändert weiter
|
||||
- Theme-Katalog wächst damit von fünf auf neun Built-ins
|
||||
|
||||
Reines Theme-Pack zwischen v1.2.1 und dem nächsten Polish-Cycle. Eine Tritan-Variante (Spectrum für Blau-Gelb-Schwäche) kann später nachgeliefert werden, falls Bedarf kommt.
|
||||
Reines Theme-Pack zwischen v1.2.1 und dem nächsten Polish-Cycle. Eine Tritan-Variante (Spectrum für Blau-Gelb-Schwäche)
|
||||
kann später nachgeliefert werden, falls Bedarf kommt.
|
||||
|
||||
@@ -2,9 +2,17 @@
|
||||
subtitle: "Plugin Integrations: Honorific"
|
||||
versionsnatur: "Plugin-Integration-Cycle 1"
|
||||
---
|
||||
|
||||
- Erste Plugin-Integration eingebaut, Cycle 1 von 6 auf der Roadmap
|
||||
- **Honorific-Custom-Titles im Chat-Header** — der Titel den du in Honorific gesetzt hast erscheint jetzt links über dem Message-Log mit der von dir gewählten Farbe, Auto-Hide wenn Honorific nicht installiert ist oder kein Custom-Titel aktiv ist
|
||||
- **Honorific-Custom-Titles im Chat-Header** — der Titel den du in Honorific gesetzt hast erscheint jetzt links über dem
|
||||
Message-Log mit der von dir gewählten Farbe, Auto-Hide wenn Honorific nicht installiert ist oder kein Custom-Titel
|
||||
aktiv ist
|
||||
- **Krone-Icon plus Tooltip** vor dem Titel-Text, damit klar ist woher der Slot kommt ohne dass der User raten muss
|
||||
- **Neuer Integrations-Settings-Tab** mit Status-Indikator (erkannt, nicht installiert, inkompatibel) und Toggle. Plus Vorschau-Block der die fünf weiteren geplanten Cycles ankündigt: Kontextmenü-Aktionen, Smart Notifications (NotificationMaster), RP-Status-Block (Moodles und LightlessClient), ExtraChat-Channels, Quick-DM-Button (XIVInstantMessenger)
|
||||
- **Maintainer-Attribution** im Tab als Höflichkeits-Geste, zwei Buttons zum Honorific-Repo und zum Caraxi-Profil. Plus Hellion-Forge-Discord-Button für Community-Vorschläge zu künftigen Integrationen
|
||||
- Keine Migration, keine Schema-Änderung. Wer Honorific eh schon nutzt sieht den Custom-Titel automatisch sobald HellionChat aktualisiert
|
||||
- **Neuer Integrations-Settings-Tab** mit Status-Indikator (erkannt, nicht installiert, inkompatibel) und Toggle. Plus
|
||||
Vorschau-Block der die fünf weiteren geplanten Cycles ankündigt: Kontextmenü-Aktionen, Smart Notifications
|
||||
(NotificationMaster), RP-Status-Block (Moodles und LightlessClient), ExtraChat-Channels, Quick-DM-Button
|
||||
(XIVInstantMessenger)
|
||||
- **Maintainer-Attribution** im Tab als Höflichkeits-Geste, zwei Buttons zum Honorific-Repo und zum Caraxi-Profil. Plus
|
||||
Hellion-Forge-Discord-Button für Community-Vorschläge zu künftigen Integrationen
|
||||
- Keine Migration, keine Schema-Änderung. Wer Honorific eh schon nutzt sieht den Custom-Titel automatisch sobald
|
||||
HellionChat aktualisiert
|
||||
|
||||
@@ -5,28 +5,19 @@ versionsnatur: Stability-Hotfix
|
||||
|
||||
**Hellion Chat 1.4.0 — Critical Lifecycle Fixes**
|
||||
|
||||
Erster Sub-Patch der v1.4.x Polish-Sweep-Serie. Sieben
|
||||
bekannte Lifecycle- und Race-Bugs aus den Audit-Pässen
|
||||
abgearbeitet, bevor Performance- und Architektur-Refactors
|
||||
draufkommen.
|
||||
Erster Sub-Patch der v1.4.x Polish-Sweep-Serie. Sieben bekannte Lifecycle- und Race-Bugs aus den Audit-Pässen
|
||||
abgearbeitet, bevor Performance- und Architektur-Refactors draufkommen.
|
||||
|
||||
- **SQLite-Dispose** lehnt sich nicht mehr an GC-Druck zur
|
||||
Datei-Freigabe an, Pooling=false auf der Connection macht
|
||||
den manuellen GC.Collect überflüssig
|
||||
- **Worker-Threads** (PendingMessage, RetentionSweep) sind
|
||||
jetzt explizit IsBackground=true, das Plugin-Domain kann
|
||||
- **SQLite-Dispose** lehnt sich nicht mehr an GC-Druck zur Datei-Freigabe an, Pooling=false auf der Connection macht den
|
||||
manuellen GC.Collect überflüssig
|
||||
- **Worker-Threads** (PendingMessage, RetentionSweep) sind jetzt explizit IsBackground=true, das Plugin-Domain kann
|
||||
sauber unloaden bei XIVLauncher-Reload ohne darauf zu warten
|
||||
- **EmoteCache-Loader** von async-void auf async-Task mit
|
||||
shared Task-Tracker, drain-on-Dispose. Kein Schreib-Risiko
|
||||
- **EmoteCache-Loader** von async-void auf async-Task mit shared Task-Tracker, drain-on-Dispose. Kein Schreib-Risiko
|
||||
mehr auf disposed EmoteImages-Einträge nach Plugin-Reload
|
||||
- **DisposeAsync-Timeout** (10s) warnt jetzt laut statt silent
|
||||
zu failen
|
||||
- **Plugin-Dispose** flushed pending DeferredSave bevor Services
|
||||
abgebaut werden, Settings-Änderungen aus den letzten Frames
|
||||
vor Disable überleben jetzt zuverlässig
|
||||
- **v13→v14 Config-Migration** liest pre-v13-Backup und überträgt
|
||||
HellionThemeWindowOpacity in das neue WindowOpacity-Feld statt
|
||||
auf 0.85 zurückzufallen
|
||||
- **DisposeAsync-Timeout** (10s) warnt jetzt laut statt silent zu failen
|
||||
- **Plugin-Dispose** flushed pending DeferredSave bevor Services abgebaut werden, Settings-Änderungen aus den letzten
|
||||
Frames vor Disable überleben jetzt zuverlässig
|
||||
- **v13→v14 Config-Migration** liest pre-v13-Backup und überträgt HellionThemeWindowOpacity in das neue
|
||||
WindowOpacity-Feld statt auf 0.85 zurückzufallen
|
||||
|
||||
Keine Schema-Bumps, keine User-sichtbaren Funktions-Änderungen
|
||||
außer dass Reload und Shutdown spürbar sauberer laufen.
|
||||
Keine Schema-Bumps, keine User-sichtbaren Funktions-Änderungen außer dass Reload und Shutdown spürbar sauberer laufen.
|
||||
|
||||
@@ -5,35 +5,23 @@ versionsnatur: Performance-Patch
|
||||
|
||||
**Hellion Chat 1.4.1 — Theme Engine Performance**
|
||||
|
||||
Zweiter Sub-Patch der v1.4.x Polish-Sweep-Serie. Heap-Pressure
|
||||
aus dem Theme-Engine-Render-Pfad eliminiert, Custom-Theme-
|
||||
Hot-Reload überlebt transiente File-Locks beim Editor-Save.
|
||||
Plus zehnter Built-In und überarbeitete Author-Credits.
|
||||
Zweiter Sub-Patch der v1.4.x Polish-Sweep-Serie. Heap-Pressure aus dem Theme-Engine-Render-Pfad eliminiert,
|
||||
Custom-Theme- Hot-Reload überlebt transiente File-Locks beim Editor-Save. Plus zehnter Built-In und überarbeitete
|
||||
Author-Credits.
|
||||
|
||||
- **ABGR-Cache auf den Theme-Records.** Beim Theme-Register
|
||||
(Built-In oder Custom) werden alle Color-Slots einmalig in
|
||||
ABGR-Pack-Form vor-konvertiert. HellionStyle.PushGlobal
|
||||
liest aus dem Cache statt pro Slot pro Frame durch
|
||||
ColourUtil.RgbaToAbgr zu jagen. Real gemessene
|
||||
Frame-Time-Recovery: **~13 %** in typischer Render-Szene
|
||||
- **ABGR-Cache auf den Theme-Records.** Beim Theme-Register (Built-In oder Custom) werden alle Color-Slots einmalig in
|
||||
ABGR-Pack-Form vor-konvertiert. HellionStyle.PushGlobal liest aus dem Cache statt pro Slot pro Frame durch
|
||||
ColourUtil.RgbaToAbgr zu jagen. Real gemessene Frame-Time-Recovery: **~13 %** in typischer Render-Szene
|
||||
(Plan-Erwartung war 2-6 % konservativ, real ~10-15 %)
|
||||
- **Custom-Theme File-Lock-Härtung.** Wenn der User ein
|
||||
Theme-JSON gerade speichert während HellionChat reloaden
|
||||
will, fängt der Loader jetzt explizit Sharing-Violation
|
||||
und Lock-Violation ab. Last-Known-Good-Snapshot bleibt im
|
||||
Picker, beim nächsten Tick wird automatisch retry'd —
|
||||
vorher fiel das Theme aus der Liste bis zum Plugin-Reload
|
||||
- **Defensive Cache-Refresh beim Theme-Switch.** Falls ein
|
||||
Theme auf einem alten Pfad ohne Cache-Fill in den Speicher
|
||||
- **Custom-Theme File-Lock-Härtung.** Wenn der User ein Theme-JSON gerade speichert während HellionChat reloaden will,
|
||||
fängt der Loader jetzt explizit Sharing-Violation und Lock-Violation ab. Last-Known-Good-Snapshot bleibt im Picker,
|
||||
beim nächsten Tick wird automatisch retry'd — vorher fiel das Theme aus der Liste bis zum Plugin-Reload
|
||||
- **Defensive Cache-Refresh beim Theme-Switch.** Falls ein Theme auf einem alten Pfad ohne Cache-Fill in den Speicher
|
||||
gekommen ist, holt Switch() das beim Anwenden nach
|
||||
- **Synthwave Sunset als zehnter Built-In.** Hot Magenta +
|
||||
Cyan auf Mitternachts-Violett, 80s-Neon-Grid-Vibes für
|
||||
- **Synthwave Sunset als zehnter Built-In.** Hot Magenta + Cyan auf Mitternachts-Violett, 80s-Neon-Grid-Vibes für
|
||||
Late-Night-Raids
|
||||
- **Author-Credits konsolidiert.** Brand-Themes laufen jetzt
|
||||
unter „Hellion Forge". Mint Grove und Forge Merchantman
|
||||
- **Author-Credits konsolidiert.** Brand-Themes laufen jetzt unter „Hellion Forge". Mint Grove und Forge Merchantman
|
||||
werden Carla Beleandis als Community-Geste zugeschrieben.
|
||||
|
||||
Keine Schema-Bumps, keine User-sichtbaren Funktions-
|
||||
Änderungen außer dass die Frames in Theme-getrieben
|
||||
rendernden Szenen merklich glatter laufen und ein neues
|
||||
Theme im Picker steht.
|
||||
Keine Schema-Bumps, keine User-sichtbaren Funktions- Änderungen außer dass die Frames in Theme-getrieben rendernden
|
||||
Szenen merklich glatter laufen und ein neues Theme im Picker steht.
|
||||
|
||||
@@ -5,39 +5,25 @@ versionsnatur: Performance-Patch
|
||||
|
||||
**Hellion Chat 1.4.2 — ChatLog Frame-Hot-Path**
|
||||
|
||||
Dritter Sub-Patch der v1.4.x Polish-Sweep-Serie. Drei
|
||||
Per-Frame-Allokations-Quellen aus dem ChatLogWindow-Render-
|
||||
Pfad und der Settings-StatusBar eliminiert.
|
||||
Dritter Sub-Patch der v1.4.x Polish-Sweep-Serie. Drei Per-Frame-Allokations-Quellen aus dem ChatLogWindow-Render- Pfad
|
||||
und der Settings-StatusBar eliminiert.
|
||||
|
||||
- **Card-Mode-Border-Loop entlastet.** DrawMessages hebt
|
||||
Theme, DrawList, Window-Left, Window-Right und die ABGR-
|
||||
Border-Color einmalig vor den Per-Message-Loop. Bei 100
|
||||
sichtbaren Messages sind das gut 500 redundante P/Invokes
|
||||
und Property-Reads, die der Hoist eliminiert. Pop-Out-
|
||||
Heavy-Setups (mehrere parallele Chat-Windows) profitieren
|
||||
proportional, weil der Hoist pro DrawMessages-Call greift,
|
||||
also pro Window
|
||||
- **Auto-Tell Tab-Tint und Icon gecached.** Die Hash-Color-
|
||||
Berechnung für Auto-Tell-Tabs lief pro Tab pro Frame, mit
|
||||
zwei String-Allokationen pro Tab (eine für Tint-Hash, eine
|
||||
für Icon-Hash). Der neue TabTintCache liest pre-computed
|
||||
Werte aus dem Tab und rechnet nur neu wenn das Tell-Target
|
||||
drifted. Beide Caches haben separate Validation-Keys, also
|
||||
keine Cross-Invalidation zwischen Tint- und Icon-Pfad.
|
||||
AutoTellTabTint selbst bleibt pure Hash-Helper, weiterhin
|
||||
ohne Tab-Awareness
|
||||
- **StatusBar-Aggregation hinter Cache-Gate.** Die Status-
|
||||
Leiste am unteren Window-Rand summiert die Tab-Message-
|
||||
Counts und zählt die Auto-Tell-Tabs pro Frame. Der Cache-
|
||||
Gate (1 Sekunde) lag bisher hinter den LINQ-Pfaden, also
|
||||
liefen Sum und Count trotzdem pro Frame. Jetzt vor dem
|
||||
Gate, plus die LINQ-Pfade durch eine Single-Pass-Foreach
|
||||
- **Card-Mode-Border-Loop entlastet.** DrawMessages hebt Theme, DrawList, Window-Left, Window-Right und die ABGR-
|
||||
Border-Color einmalig vor den Per-Message-Loop. Bei 100 sichtbaren Messages sind das gut 500 redundante P/Invokes und
|
||||
Property-Reads, die der Hoist eliminiert. Pop-Out- Heavy-Setups (mehrere parallele Chat-Windows) profitieren
|
||||
proportional, weil der Hoist pro DrawMessages-Call greift, also pro Window
|
||||
- **Auto-Tell Tab-Tint und Icon gecached.** Die Hash-Color- Berechnung für Auto-Tell-Tabs lief pro Tab pro Frame, mit
|
||||
zwei String-Allokationen pro Tab (eine für Tint-Hash, eine für Icon-Hash). Der neue TabTintCache liest pre-computed
|
||||
Werte aus dem Tab und rechnet nur neu wenn das Tell-Target drifted. Beide Caches haben separate Validation-Keys, also
|
||||
keine Cross-Invalidation zwischen Tint- und Icon-Pfad. AutoTellTabTint selbst bleibt pure Hash-Helper, weiterhin ohne
|
||||
Tab-Awareness
|
||||
- **StatusBar-Aggregation hinter Cache-Gate.** Die Status- Leiste am unteren Window-Rand summiert die Tab-Message-
|
||||
Counts und zählt die Auto-Tell-Tabs pro Frame. Der Cache- Gate (1 Sekunde) lag bisher hinter den LINQ-Pfaden, also
|
||||
liefen Sum und Count trotzdem pro Frame. Jetzt vor dem Gate, plus die LINQ-Pfade durch eine Single-Pass-Foreach
|
||||
ersetzt. Die Aggregation läuft auf etwa 1 % der Frames
|
||||
|
||||
Realistische Frame-Time-Recovery: 2-5 % in typischen Szenen,
|
||||
Pop-Out-Heavy-Setups potenziell mehr durch die Card-Border-
|
||||
Realistische Frame-Time-Recovery: 2-5 % in typischen Szenen, Pop-Out-Heavy-Setups potenziell mehr durch die Card-Border-
|
||||
Multiplikation pro Window.
|
||||
|
||||
Keine Schema-Bumps, keine User-sichtbaren Funktions-
|
||||
Änderungen außer dass die Frames im Chat-Log und in der
|
||||
Keine Schema-Bumps, keine User-sichtbaren Funktions- Änderungen außer dass die Frames im Chat-Log und in der
|
||||
Settings-Statusleiste merklich glatter laufen.
|
||||
|
||||
@@ -5,41 +5,25 @@ versionsnatur: Architecture-Refactor
|
||||
|
||||
**Hellion Chat 1.4.3 — Plugin-Load Async-Init + Repo-Cutover**
|
||||
|
||||
Vierter Sub-Patch der v1.4.x Polish-Sweep-Serie. Plugin-
|
||||
Lifecycle auf Dalamud's `IAsyncDalamudPlugin`-API migriert
|
||||
und das Custom-Repo zieht von GitHub auf Gitea um.
|
||||
Vierter Sub-Patch der v1.4.x Polish-Sweep-Serie. Plugin- Lifecycle auf Dalamud's `IAsyncDalamudPlugin`-API migriert und
|
||||
das Custom-Repo zieht von GitHub auf Gitea um.
|
||||
|
||||
- **Async-Plugin-Architektur.** Konstruktor übernimmt nur
|
||||
noch die Bootstrap-Essentials (Config-Load, Language-Init,
|
||||
Conflict-Detection). Migrationen, Service-Allokationen,
|
||||
Window-Konstruktion und Hook-Subscription wandern in
|
||||
LoadAsync, sodass Dalamud die UI während der schweren
|
||||
Arbeit responsive halten kann. Per-Line-CaptureFailure in
|
||||
DisposeAsync mirrort LightlessSync's Pattern, plus
|
||||
Idempotency-Guard gegen Reload-Races
|
||||
- **Custom-Repo-URL umgezogen auf Gitea.** Bestehende Tester
|
||||
müssen einmalig in XIVLauncher die Custom-Repo-URL auf
|
||||
`https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/raw/branch/main/repo.json`
|
||||
umstellen, dann XIVLauncher neu starten. Das alte
|
||||
GitHub-Repo bleibt als eingefrorener v1.4.2-Snapshot
|
||||
stehen und wird nicht mehr aktualisiert
|
||||
- **Schema-Gate statt Migrations-Kette.** Die v9 → v16
|
||||
Migrationen sind raus, ersetzt durch einen harten
|
||||
Schema-Check in Phase 1. Configs auf Schema v16+ laden
|
||||
direkt; ältere Configs (vor v1.2.1) bekommen jetzt eine
|
||||
klare „install v1.4.2 first"-Fehlermeldung statt eines
|
||||
impliziten Migrations-Pfads
|
||||
- **AutoTranslate-Cache läuft im Hintergrund.** Der Cache
|
||||
füllt sich jetzt fire-and-forget statt blockierend im
|
||||
Plugin-Load. Trade-off: die erste Auto-Translate-Nutzung
|
||||
einer Session kann einen kurzen Hitch haben, dafür kein
|
||||
- **Async-Plugin-Architektur.** Konstruktor übernimmt nur noch die Bootstrap-Essentials (Config-Load, Language-Init,
|
||||
Conflict-Detection). Migrationen, Service-Allokationen, Window-Konstruktion und Hook-Subscription wandern in
|
||||
LoadAsync, sodass Dalamud die UI während der schweren Arbeit responsive halten kann. Per-Line-CaptureFailure in
|
||||
DisposeAsync mirrort LightlessSync's Pattern, plus Idempotency-Guard gegen Reload-Races
|
||||
- **Custom-Repo-URL umgezogen auf Gitea.** Bestehende Tester müssen einmalig in XIVLauncher die Custom-Repo-URL auf
|
||||
`https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/raw/branch/main/repo.json` umstellen, dann
|
||||
XIVLauncher neu starten. Das alte GitHub-Repo bleibt als eingefrorener v1.4.2-Snapshot stehen und wird nicht mehr
|
||||
aktualisiert
|
||||
- **Schema-Gate statt Migrations-Kette.** Die v9 → v16 Migrationen sind raus, ersetzt durch einen harten Schema-Check in
|
||||
Phase 1. Configs auf Schema v16+ laden direkt; ältere Configs (vor v1.2.1) bekommen jetzt eine klare „install v1.4.2
|
||||
first"-Fehlermeldung statt eines impliziten Migrations-Pfads
|
||||
- **AutoTranslate-Cache läuft im Hintergrund.** Der Cache füllt sich jetzt fire-and-forget statt blockierend im
|
||||
Plugin-Load. Trade-off: die erste Auto-Translate-Nutzung einer Session kann einen kurzen Hitch haben, dafür kein
|
||||
300-ms-Block beim Plugin-Start
|
||||
- **Plugin-Load-Zeit ehrlich.** Median 3,7 s über fünf
|
||||
Reloads, vergleichbar mit v1.4.2. Der Async-Refactor ist
|
||||
Foundation für künftige Lazy-Init-Optimierungen (v1.4.4)
|
||||
und Code-Architektur-Hygiene, kein direkter
|
||||
User-spürbarer Speed-Win in dieser Release
|
||||
- **Plugin-Load-Zeit ehrlich.** Median 3,7 s über fünf Reloads, vergleichbar mit v1.4.2. Der Async-Refactor ist
|
||||
Foundation für künftige Lazy-Init-Optimierungen (v1.4.4) und Code-Architektur-Hygiene, kein direkter User-spürbarer
|
||||
Speed-Win in dieser Release
|
||||
|
||||
Keine User-sichtbaren Funktions-Änderungen außer dem
|
||||
Repo-URL-Update. Settings, Themes und Tabs bleiben
|
||||
unangetastet.
|
||||
Keine User-sichtbaren Funktions-Änderungen außer dem Repo-URL-Update. Settings, Themes und Tabs bleiben unangetastet.
|
||||
|
||||
+14
-13
@@ -1,26 +1,27 @@
|
||||
|
||||
---
|
||||
|
||||
## How to install
|
||||
|
||||
This release is distributed via the HellionChat custom repository, not the
|
||||
Dalamud main plugin repo. To install:
|
||||
This release is distributed via the HellionChat custom repository, not the Dalamud main plugin repo. To install:
|
||||
|
||||
1. In XIVLauncher: **Settings → Experimental → Custom Plugin Repositories**
|
||||
2. Add the URL:
|
||||
`https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/raw/branch/main/repo.json`
|
||||
2. Add the URL: `https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/raw/branch/main/repo.json`
|
||||
3. Enable, save, then `/xlplugins` → search **Hellion Chat** → install
|
||||
|
||||
## Project documents
|
||||
|
||||
- [README](https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/src/branch/main/README.md) — features, architecture, build
|
||||
- [Privacy notice](https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/src/branch/main/PRIVACY.md) — what the plugin stores and sends
|
||||
- [Third-party notices](https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/src/branch/main/docs/THIRD_PARTY_NOTICES.md) — dependencies and licences
|
||||
- [Security policy](https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/src/branch/main/SECURITY.md) — vulnerability reporting
|
||||
- [Support](https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/src/branch/main/SUPPORT.md) — bug reports, questions, contact paths
|
||||
- [README](https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/src/branch/main/README.md) — features,
|
||||
architecture, build
|
||||
- [Privacy notice](https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/src/branch/main/PRIVACY.md) — what
|
||||
the plugin stores and sends
|
||||
- [Third-party notices](https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/src/branch/main/docs/THIRD_PARTY_NOTICES.md)
|
||||
— dependencies and licences
|
||||
- [Security policy](https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/src/branch/main/SECURITY.md) —
|
||||
vulnerability reporting
|
||||
- [Support](https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/src/branch/main/SUPPORT.md) — bug reports,
|
||||
questions, contact paths
|
||||
|
||||
## Licence
|
||||
|
||||
[EUPL-1.2](https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/src/branch/main/LICENSE).
|
||||
Based on [Chat 2](https://github.com/Infiziert90/ChatTwo) by Infi and Anna,
|
||||
also EUPL-1.2.
|
||||
[EUPL-1.2](https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/src/branch/main/LICENSE). Based on
|
||||
[Chat 2](https://github.com/Infiziert90/ChatTwo) by Infi and Anna, also EUPL-1.2.
|
||||
|
||||
+27
-27
@@ -11,43 +11,43 @@ name: Build
|
||||
# Dalamud SDK 15 uses on Linux).
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
pull_request:
|
||||
branches: [main]
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches: [main]
|
||||
pull_request:
|
||||
branches: [main]
|
||||
workflow_dispatch:
|
||||
|
||||
# Minimum permissions for a build-only workflow: read the repo, nothing
|
||||
# else. Closes the CodeQL "Workflow does not contain permissions" alert
|
||||
# and matches the principle-of-least-privilege the security guide
|
||||
# recommends for workflows that don't push or create releases.
|
||||
permissions:
|
||||
contents: read
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build (Release)
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 15
|
||||
build:
|
||||
name: Build (Release)
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 15
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Setup .NET 10
|
||||
uses: actions/setup-dotnet@v5
|
||||
with:
|
||||
dotnet-version: 10.0.x
|
||||
- name: Setup .NET 10
|
||||
uses: actions/setup-dotnet@v5
|
||||
with:
|
||||
dotnet-version: 10.0.x
|
||||
|
||||
- name: Download Dalamud staging
|
||||
run: |
|
||||
hooks="$HOME/.xlcore/dalamud/Hooks/dev"
|
||||
mkdir -p "$hooks"
|
||||
curl -fsSL https://goatcorp.github.io/dalamud-distrib/stg/latest.zip -o dalamud.zip
|
||||
unzip -oq dalamud.zip -d "$hooks"
|
||||
- name: Download Dalamud staging
|
||||
run: |
|
||||
hooks="$HOME/.xlcore/dalamud/Hooks/dev"
|
||||
mkdir -p "$hooks"
|
||||
curl -fsSL https://goatcorp.github.io/dalamud-distrib/stg/latest.zip -o dalamud.zip
|
||||
unzip -oq dalamud.zip -d "$hooks"
|
||||
|
||||
- name: Restore
|
||||
run: dotnet restore HellionChat/HellionChat.csproj
|
||||
- name: Restore
|
||||
run: dotnet restore HellionChat/HellionChat.csproj
|
||||
|
||||
- name: Build (Release)
|
||||
run: dotnet build HellionChat/HellionChat.csproj --configuration Release --no-restore
|
||||
- name: Build (Release)
|
||||
run: dotnet build HellionChat/HellionChat.csproj --configuration Release --no-restore
|
||||
|
||||
@@ -17,209 +17,209 @@ name: Forge Announce
|
||||
# (issue titles, PR bodies, commit messages, etc.) flows into run-steps.
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v*'
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
tag:
|
||||
description: 'Existing tag to (re)post, e.g. v1.1.0'
|
||||
required: true
|
||||
type: string
|
||||
push:
|
||||
tags:
|
||||
- "v*"
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
tag:
|
||||
description: "Existing tag to (re)post, e.g. v1.1.0"
|
||||
required: true
|
||||
type: string
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
announce:
|
||||
name: Post changelog to Hellion Forge
|
||||
runs-on: ubuntu-latest
|
||||
# The DISCORD_FORGE_WEBHOOK secret is set as a repo-level Actions Secret
|
||||
# on Gitea (Settings → Actions → Secrets). Repo-level secrets are in
|
||||
# scope for every job by default, no environment: declaration needed.
|
||||
timeout-minutes: 5
|
||||
announce:
|
||||
name: Post changelog to Hellion Forge
|
||||
runs-on: ubuntu-latest
|
||||
# The DISCORD_FORGE_WEBHOOK secret is set as a repo-level Actions Secret
|
||||
# on Gitea (Settings → Actions → Secrets). Repo-level secrets are in
|
||||
# scope for every job by default, no environment: declaration needed.
|
||||
timeout-minutes: 5
|
||||
|
||||
steps:
|
||||
# On push:tags github.ref points at the tag commit; on workflow_dispatch
|
||||
# the user supplies the tag explicitly. Always check out that tag so
|
||||
# the yaml + forge-posts file are read from the tagged tree, not main.
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
ref: ${{ github.event.inputs.tag || github.ref }}
|
||||
steps:
|
||||
# On push:tags github.ref points at the tag commit; on workflow_dispatch
|
||||
# the user supplies the tag explicitly. Always check out that tag so
|
||||
# the yaml + forge-posts file are read from the tagged tree, not main.
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
ref: ${{ github.event.inputs.tag || github.ref }}
|
||||
|
||||
# Build embed-payload as a JSON file on disk. PowerShell-Core (pwsh)
|
||||
# ships pre-installed on ubuntu-latest so we get the same scripting
|
||||
# patterns release.yml uses on windows-latest. Tag is read via env: to
|
||||
# treat it as a string variable rather than inline shell text, and
|
||||
# validated against the semver regex before any interpolation.
|
||||
- name: Build embed payload
|
||||
id: build
|
||||
shell: pwsh
|
||||
env:
|
||||
TAG_NAME: ${{ github.event.inputs.tag || github.ref_name }}
|
||||
run: |
|
||||
$tag = $env:TAG_NAME
|
||||
if ($tag -notmatch '^v\d+\.\d+\.\d+$') {
|
||||
throw "V1: Refusing to announce non-semver tag: $tag"
|
||||
}
|
||||
$version = $tag.Substring(1)
|
||||
|
||||
# ---------- Forge-Post-Datei lesen ----------
|
||||
$forgePath = ".github/forge-posts/$tag.md"
|
||||
if (-not (Test-Path $forgePath)) {
|
||||
throw "V2: Forge-Post-Datei für $tag fehlt unter .github/forge-posts/. Datei vor dem Tag anlegen, dann Tag re-pushen oder workflow_dispatch."
|
||||
}
|
||||
$forgeRaw = Get-Content -Path $forgePath -Raw
|
||||
|
||||
# Frontmatter (--- … ---) am Datei-Anfang
|
||||
if ($forgeRaw -notmatch '(?s)\A---\s*\r?\n(.*?)\r?\n---\s*\r?\n(.*)\z') {
|
||||
throw "V3: Frontmatter (---) fehlt oder ist defekt in $forgePath"
|
||||
}
|
||||
$fmText = $matches[1]
|
||||
$deBody = $matches[2].Trim()
|
||||
|
||||
$subtitle = $null
|
||||
$versionsnatur = $null
|
||||
foreach ($line in ($fmText -split "`r?`n")) {
|
||||
if ($line -match '^subtitle:\s*"?([^"]*)"?\s*$') { $subtitle = $matches[1] }
|
||||
if ($line -match '^versionsnatur:\s*"?([^"]*)"?\s*$') { $versionsnatur = $matches[1] }
|
||||
}
|
||||
if ([string]::IsNullOrWhiteSpace($subtitle)) { throw "V3: Frontmatter-Feld 'subtitle' fehlt in $forgePath" }
|
||||
if ([string]::IsNullOrWhiteSpace($versionsnatur)) { throw "V3: Frontmatter-Feld 'versionsnatur' fehlt in $forgePath" }
|
||||
if ($subtitle.Length -gt 60) { throw "V4: Frontmatter-Feld 'subtitle' überschreitet Limit ($($subtitle.Length) Char, max 60)" }
|
||||
if ($versionsnatur.Length -gt 40) { throw "V4: Frontmatter-Feld 'versionsnatur' überschreitet Limit ($($versionsnatur.Length) Char, max 40)" }
|
||||
if ([string]::IsNullOrWhiteSpace($deBody)) { throw "V3: DE-Body fehlt in $forgePath" }
|
||||
|
||||
# ---------- EN-Block aus HellionChat.yaml ziehen ----------
|
||||
# 1:1 Pattern aus release.yml — gleicher Header-Marker, gleiches
|
||||
# Trailer-Verhalten. Bei Drift die zwei Workflows synchron halten.
|
||||
$yamlPath = "HellionChat/HellionChat.yaml"
|
||||
$raw = Get-Content -Path $yamlPath -Raw
|
||||
$marker = "changelog: |-"
|
||||
$idx = $raw.IndexOf($marker)
|
||||
if ($idx -lt 0) { throw "V5: changelog-Block nicht gefunden in $yamlPath" }
|
||||
$afterMarker = $raw.Substring($idx + $marker.Length)
|
||||
$changelogBody = (($afterMarker -split "`r?`n") | ForEach-Object {
|
||||
if ($_ -match '^ ') { $_.Substring(2) } else { $_ }
|
||||
}) -join "`n"
|
||||
|
||||
$header = "**Hellion Chat $version"
|
||||
$start = $changelogBody.IndexOf($header)
|
||||
if ($start -lt 0) {
|
||||
throw "V5: No changelog entry for version $version found in $yamlPath. Update the changelog block before tagging."
|
||||
}
|
||||
$rest = $changelogBody.Substring($start)
|
||||
$nextHdr = $rest.IndexOf("`n`n**Hellion Chat ", 1)
|
||||
$trailer = $rest.IndexOf("`n`n---")
|
||||
if ($nextHdr -ge 0 -and ($trailer -lt 0 -or $nextHdr -lt $trailer)) {
|
||||
$enBlock = $rest.Substring(0, $nextHdr).TrimEnd()
|
||||
} elseif ($trailer -ge 0) {
|
||||
$enBlock = $rest.Substring(0, $trailer).TrimEnd()
|
||||
} else {
|
||||
$enBlock = $rest.TrimEnd()
|
||||
}
|
||||
|
||||
# ---------- Char-Cap-Check (5500 Total auf title + description + footer) ----------
|
||||
$title = "Hellion Chat $version — $subtitle"
|
||||
$description = "**Deutsch**`n`n$deBody`n`n**English**`n`n$enBlock"
|
||||
$footerText = "Hellion Forge · $versionsnatur"
|
||||
$totalChars = $title.Length + $description.Length + $footerText.Length
|
||||
if ($totalChars -gt 5500) {
|
||||
throw "V6: Total char count $totalChars exceeds 5500 limit. Major-Release detected — please post manually via Bot/Multi-Embed (see forge style §8). Forge-Auto-Announce stays off for this tag."
|
||||
}
|
||||
Write-Host "Char-Cap OK: $totalChars / 5500"
|
||||
|
||||
# ---------- Embed-Payload bauen ----------
|
||||
$payload = [ordered]@{
|
||||
username = "Forge Herald"
|
||||
avatar_url = "https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/raw/branch/main/HellionChat/images/icon.png"
|
||||
content = "<@&1500489631555260446>"
|
||||
allowed_mentions = [ordered]@{
|
||||
parse = @()
|
||||
roles = @("1500489631555260446")
|
||||
}
|
||||
embeds = @(
|
||||
[ordered]@{
|
||||
title = $title
|
||||
url = "https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/releases/tag/$tag"
|
||||
color = 12730636
|
||||
description = $description
|
||||
footer = [ordered]@{ text = $footerText }
|
||||
timestamp = (Get-Date).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ss.fffZ")
|
||||
# Build embed-payload as a JSON file on disk. PowerShell-Core (pwsh)
|
||||
# ships pre-installed on ubuntu-latest so we get the same scripting
|
||||
# patterns release.yml uses on windows-latest. Tag is read via env: to
|
||||
# treat it as a string variable rather than inline shell text, and
|
||||
# validated against the semver regex before any interpolation.
|
||||
- name: Build embed payload
|
||||
id: build
|
||||
shell: pwsh
|
||||
env:
|
||||
TAG_NAME: ${{ github.event.inputs.tag || github.ref_name }}
|
||||
run: |
|
||||
$tag = $env:TAG_NAME
|
||||
if ($tag -notmatch '^v\d+\.\d+\.\d+$') {
|
||||
throw "V1: Refusing to announce non-semver tag: $tag"
|
||||
}
|
||||
)
|
||||
}
|
||||
$version = $tag.Substring(1)
|
||||
|
||||
$payloadJson = $payload | ConvertTo-Json -Depth 8 -Compress
|
||||
# Ausgabe-Datei ohne trailing newline für sauberes curl --data-binary @-
|
||||
[System.IO.File]::WriteAllText("$PWD/embed-payload.json", $payloadJson, [System.Text.UTF8Encoding]::new($false))
|
||||
# ---------- Forge-Post-Datei lesen ----------
|
||||
$forgePath = ".github/forge-posts/$tag.md"
|
||||
if (-not (Test-Path $forgePath)) {
|
||||
throw "V2: Forge-Post-Datei für $tag fehlt unter .github/forge-posts/. Datei vor dem Tag anlegen, dann Tag re-pushen oder workflow_dispatch."
|
||||
}
|
||||
$forgeRaw = Get-Content -Path $forgePath -Raw
|
||||
|
||||
Write-Host "Payload size: $($payloadJson.Length) chars"
|
||||
Write-Host "Embed title: $title"
|
||||
Write-Host "Embed footer: $footerText"
|
||||
# Frontmatter (--- … ---) am Datei-Anfang
|
||||
if ($forgeRaw -notmatch '(?s)\A---\s*\r?\n(.*?)\r?\n---\s*\r?\n(.*)\z') {
|
||||
throw "V3: Frontmatter (---) fehlt oder ist defekt in $forgePath"
|
||||
}
|
||||
$fmText = $matches[1]
|
||||
$deBody = $matches[2].Trim()
|
||||
|
||||
# POST to the Hellion Forge changelog webhook. curl from PowerShell-Core
|
||||
# so we can pipe the payload via stdin (--data-binary @-) and keep
|
||||
# secrets out of process arg lists. One retry on 5xx, hard fail on 4xx.
|
||||
- name: POST to Hellion Forge webhook
|
||||
shell: pwsh
|
||||
env:
|
||||
DISCORD_FORGE_WEBHOOK: ${{ secrets.DISCORD_FORGE_WEBHOOK }}
|
||||
run: |
|
||||
if ([string]::IsNullOrEmpty($env:DISCORD_FORGE_WEBHOOK)) {
|
||||
throw "V7: DISCORD_FORGE_WEBHOOK secret is empty. Check Settings → Environments → Webhook."
|
||||
}
|
||||
$subtitle = $null
|
||||
$versionsnatur = $null
|
||||
foreach ($line in ($fmText -split "`r?`n")) {
|
||||
if ($line -match '^subtitle:\s*"?([^"]*)"?\s*$') { $subtitle = $matches[1] }
|
||||
if ($line -match '^versionsnatur:\s*"?([^"]*)"?\s*$') { $versionsnatur = $matches[1] }
|
||||
}
|
||||
if ([string]::IsNullOrWhiteSpace($subtitle)) { throw "V3: Frontmatter-Feld 'subtitle' fehlt in $forgePath" }
|
||||
if ([string]::IsNullOrWhiteSpace($versionsnatur)) { throw "V3: Frontmatter-Feld 'versionsnatur' fehlt in $forgePath" }
|
||||
if ($subtitle.Length -gt 60) { throw "V4: Frontmatter-Feld 'subtitle' überschreitet Limit ($($subtitle.Length) Char, max 60)" }
|
||||
if ($versionsnatur.Length -gt 40) { throw "V4: Frontmatter-Feld 'versionsnatur' überschreitet Limit ($($versionsnatur.Length) Char, max 40)" }
|
||||
if ([string]::IsNullOrWhiteSpace($deBody)) { throw "V3: DE-Body fehlt in $forgePath" }
|
||||
|
||||
$payloadFile = "$PWD/embed-payload.json"
|
||||
if (-not (Test-Path $payloadFile)) {
|
||||
throw "Embed payload file missing — previous step did not produce embed-payload.json"
|
||||
}
|
||||
# ---------- EN-Block aus HellionChat.yaml ziehen ----------
|
||||
# 1:1 Pattern aus release.yml — gleicher Header-Marker, gleiches
|
||||
# Trailer-Verhalten. Bei Drift die zwei Workflows synchron halten.
|
||||
$yamlPath = "HellionChat/HellionChat.yaml"
|
||||
$raw = Get-Content -Path $yamlPath -Raw
|
||||
$marker = "changelog: |-"
|
||||
$idx = $raw.IndexOf($marker)
|
||||
if ($idx -lt 0) { throw "V5: changelog-Block nicht gefunden in $yamlPath" }
|
||||
$afterMarker = $raw.Substring($idx + $marker.Length)
|
||||
$changelogBody = (($afterMarker -split "`r?`n") | ForEach-Object {
|
||||
if ($_ -match '^ ') { $_.Substring(2) } else { $_ }
|
||||
}) -join "`n"
|
||||
|
||||
$maxAttempts = 2
|
||||
$attempt = 0
|
||||
while ($attempt -lt $maxAttempts) {
|
||||
$attempt++
|
||||
Write-Host "POST attempt $attempt of $maxAttempts"
|
||||
$tmpResp = "$PWD/.webhook-response"
|
||||
$tmpHeaders = "$PWD/.webhook-headers"
|
||||
# --silent suppresses progress; --show-error prints errors so
|
||||
# the workflow log shows what happened. -w prints HTTP status
|
||||
# to stdout for inspection. -o captures body for diagnosis,
|
||||
# -D captures headers.
|
||||
$rawStatus = Get-Content $payloadFile -Raw |
|
||||
curl --silent --show-error `
|
||||
--header 'Content-Type: application/json' `
|
||||
--data-binary '@-' `
|
||||
-D $tmpHeaders `
|
||||
-o $tmpResp `
|
||||
-w '%{http_code}' `
|
||||
"$env:DISCORD_FORGE_WEBHOOK"
|
||||
$status = [int]$rawStatus
|
||||
Write-Host "HTTP status: $status"
|
||||
$header = "**Hellion Chat $version"
|
||||
$start = $changelogBody.IndexOf($header)
|
||||
if ($start -lt 0) {
|
||||
throw "V5: No changelog entry for version $version found in $yamlPath. Update the changelog block before tagging."
|
||||
}
|
||||
$rest = $changelogBody.Substring($start)
|
||||
$nextHdr = $rest.IndexOf("`n`n**Hellion Chat ", 1)
|
||||
$trailer = $rest.IndexOf("`n`n---")
|
||||
if ($nextHdr -ge 0 -and ($trailer -lt 0 -or $nextHdr -lt $trailer)) {
|
||||
$enBlock = $rest.Substring(0, $nextHdr).TrimEnd()
|
||||
} elseif ($trailer -ge 0) {
|
||||
$enBlock = $rest.Substring(0, $trailer).TrimEnd()
|
||||
} else {
|
||||
$enBlock = $rest.TrimEnd()
|
||||
}
|
||||
|
||||
if ($status -ge 200 -and $status -lt 300) {
|
||||
Write-Host "Forge announce POST succeeded."
|
||||
exit 0
|
||||
}
|
||||
# ---------- Char-Cap-Check (5500 Total auf title + description + footer) ----------
|
||||
$title = "Hellion Chat $version — $subtitle"
|
||||
$description = "**Deutsch**`n`n$deBody`n`n**English**`n`n$enBlock"
|
||||
$footerText = "Hellion Forge · $versionsnatur"
|
||||
$totalChars = $title.Length + $description.Length + $footerText.Length
|
||||
if ($totalChars -gt 5500) {
|
||||
throw "V6: Total char count $totalChars exceeds 5500 limit. Major-Release detected — please post manually via Bot/Multi-Embed (see forge style §8). Forge-Auto-Announce stays off for this tag."
|
||||
}
|
||||
Write-Host "Char-Cap OK: $totalChars / 5500"
|
||||
|
||||
$bodySnippet = ""
|
||||
if (Test-Path $tmpResp) {
|
||||
$bodySnippet = (Get-Content $tmpResp -Raw -ErrorAction SilentlyContinue)
|
||||
if ($bodySnippet.Length -gt 500) { $bodySnippet = $bodySnippet.Substring(0, 500) + " …" }
|
||||
}
|
||||
# ---------- Embed-Payload bauen ----------
|
||||
$payload = [ordered]@{
|
||||
username = "Forge Herald"
|
||||
avatar_url = "https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/raw/branch/main/HellionChat/images/icon.png"
|
||||
content = "<@&1500489631555260446>"
|
||||
allowed_mentions = [ordered]@{
|
||||
parse = @()
|
||||
roles = @("1500489631555260446")
|
||||
}
|
||||
embeds = @(
|
||||
[ordered]@{
|
||||
title = $title
|
||||
url = "https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/releases/tag/$tag"
|
||||
color = 12730636
|
||||
description = $description
|
||||
footer = [ordered]@{ text = $footerText }
|
||||
timestamp = (Get-Date).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ss.fffZ")
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
if ($status -ge 400 -and $status -lt 500) {
|
||||
# E2: 4xx is permanent — webhook revoked, channel deleted,
|
||||
# payload malformed. No retry.
|
||||
throw "E2: Discord-Webhook returned permanent $status. Body: $bodySnippet"
|
||||
}
|
||||
$payloadJson = $payload | ConvertTo-Json -Depth 8 -Compress
|
||||
# Ausgabe-Datei ohne trailing newline für sauberes curl --data-binary @-
|
||||
[System.IO.File]::WriteAllText("$PWD/embed-payload.json", $payloadJson, [System.Text.UTF8Encoding]::new($false))
|
||||
|
||||
# E1: 5xx (or transport-level fail with status 0) — wait + retry once
|
||||
if ($attempt -lt $maxAttempts) {
|
||||
Write-Host "Transient $status — sleeping 30s before retry."
|
||||
Start-Sleep -Seconds 30
|
||||
} else {
|
||||
throw "E1: Discord-Webhook returned transient $status after $maxAttempts attempts. Body: $bodySnippet"
|
||||
}
|
||||
}
|
||||
Write-Host "Payload size: $($payloadJson.Length) chars"
|
||||
Write-Host "Embed title: $title"
|
||||
Write-Host "Embed footer: $footerText"
|
||||
|
||||
# POST to the Hellion Forge changelog webhook. curl from PowerShell-Core
|
||||
# so we can pipe the payload via stdin (--data-binary @-) and keep
|
||||
# secrets out of process arg lists. One retry on 5xx, hard fail on 4xx.
|
||||
- name: POST to Hellion Forge webhook
|
||||
shell: pwsh
|
||||
env:
|
||||
DISCORD_FORGE_WEBHOOK: ${{ secrets.DISCORD_FORGE_WEBHOOK }}
|
||||
run: |
|
||||
if ([string]::IsNullOrEmpty($env:DISCORD_FORGE_WEBHOOK)) {
|
||||
throw "V7: DISCORD_FORGE_WEBHOOK secret is empty. Check Settings → Environments → Webhook."
|
||||
}
|
||||
|
||||
$payloadFile = "$PWD/embed-payload.json"
|
||||
if (-not (Test-Path $payloadFile)) {
|
||||
throw "Embed payload file missing — previous step did not produce embed-payload.json"
|
||||
}
|
||||
|
||||
$maxAttempts = 2
|
||||
$attempt = 0
|
||||
while ($attempt -lt $maxAttempts) {
|
||||
$attempt++
|
||||
Write-Host "POST attempt $attempt of $maxAttempts"
|
||||
$tmpResp = "$PWD/.webhook-response"
|
||||
$tmpHeaders = "$PWD/.webhook-headers"
|
||||
# --silent suppresses progress; --show-error prints errors so
|
||||
# the workflow log shows what happened. -w prints HTTP status
|
||||
# to stdout for inspection. -o captures body for diagnosis,
|
||||
# -D captures headers.
|
||||
$rawStatus = Get-Content $payloadFile -Raw |
|
||||
curl --silent --show-error `
|
||||
--header 'Content-Type: application/json' `
|
||||
--data-binary '@-' `
|
||||
-D $tmpHeaders `
|
||||
-o $tmpResp `
|
||||
-w '%{http_code}' `
|
||||
"$env:DISCORD_FORGE_WEBHOOK"
|
||||
$status = [int]$rawStatus
|
||||
Write-Host "HTTP status: $status"
|
||||
|
||||
if ($status -ge 200 -and $status -lt 300) {
|
||||
Write-Host "Forge announce POST succeeded."
|
||||
exit 0
|
||||
}
|
||||
|
||||
$bodySnippet = ""
|
||||
if (Test-Path $tmpResp) {
|
||||
$bodySnippet = (Get-Content $tmpResp -Raw -ErrorAction SilentlyContinue)
|
||||
if ($bodySnippet.Length -gt 500) { $bodySnippet = $bodySnippet.Substring(0, 500) + " …" }
|
||||
}
|
||||
|
||||
if ($status -ge 400 -and $status -lt 500) {
|
||||
# E2: 4xx is permanent — webhook revoked, channel deleted,
|
||||
# payload malformed. No retry.
|
||||
throw "E2: Discord-Webhook returned permanent $status. Body: $bodySnippet"
|
||||
}
|
||||
|
||||
# E1: 5xx (or transport-level fail with status 0) — wait + retry once
|
||||
if ($attempt -lt $maxAttempts) {
|
||||
Write-Host "Transient $status — sleeping 30s before retry."
|
||||
Start-Sleep -Seconds 30
|
||||
} else {
|
||||
throw "E1: Discord-Webhook returned transient $status after $maxAttempts attempts. Body: $bodySnippet"
|
||||
}
|
||||
}
|
||||
|
||||
+131
-131
@@ -17,154 +17,154 @@ name: Release
|
||||
# Linux when the Dalamud staging assemblies sit under $(HOME)/.xlcore/...
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v*'
|
||||
# Manual recovery trigger. Use when a tag was pushed but the auto-run
|
||||
# was missed or failed: `gh workflow run release.yml -f tag=v0.6.1`.
|
||||
# The tag input is validated against the same semver regex as the
|
||||
# auto-trigger before any string interpolation happens.
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
tag:
|
||||
description: 'Existing tag to (re)release, e.g. v0.6.1'
|
||||
required: true
|
||||
type: string
|
||||
push:
|
||||
tags:
|
||||
- "v*"
|
||||
# Manual recovery trigger. Use when a tag was pushed but the auto-run
|
||||
# was missed or failed: `gh workflow run release.yml -f tag=v0.6.1`.
|
||||
# The tag input is validated against the same semver regex as the
|
||||
# auto-trigger before any string interpolation happens.
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
tag:
|
||||
description: "Existing tag to (re)release, e.g. v0.6.1"
|
||||
required: true
|
||||
type: string
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
release:
|
||||
name: Build and attach release ZIP
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 20
|
||||
release:
|
||||
name: Build and attach release ZIP
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 20
|
||||
|
||||
steps:
|
||||
# On push:tags, github.ref_name is the tag — checkout default works.
|
||||
# On workflow_dispatch, ref defaults to the branch the action was
|
||||
# invoked from; we need to explicitly check out the tag the user
|
||||
# supplied so the build comes from the tagged commit, not main.
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
ref: ${{ github.event.inputs.tag || github.ref }}
|
||||
steps:
|
||||
# On push:tags, github.ref_name is the tag — checkout default works.
|
||||
# On workflow_dispatch, ref defaults to the branch the action was
|
||||
# invoked from; we need to explicitly check out the tag the user
|
||||
# supplied so the build comes from the tagged commit, not main.
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
ref: ${{ github.event.inputs.tag || github.ref }}
|
||||
|
||||
- name: Setup .NET 10
|
||||
uses: actions/setup-dotnet@v5
|
||||
with:
|
||||
dotnet-version: 10.0.x
|
||||
- name: Setup .NET 10
|
||||
uses: actions/setup-dotnet@v5
|
||||
with:
|
||||
dotnet-version: 10.0.x
|
||||
|
||||
- name: Download Dalamud staging
|
||||
run: |
|
||||
hooks="$HOME/.xlcore/dalamud/Hooks/dev"
|
||||
mkdir -p "$hooks"
|
||||
curl -fsSL https://goatcorp.github.io/dalamud-distrib/stg/latest.zip -o dalamud.zip
|
||||
unzip -oq dalamud.zip -d "$hooks"
|
||||
- name: Download Dalamud staging
|
||||
run: |
|
||||
hooks="$HOME/.xlcore/dalamud/Hooks/dev"
|
||||
mkdir -p "$hooks"
|
||||
curl -fsSL https://goatcorp.github.io/dalamud-distrib/stg/latest.zip -o dalamud.zip
|
||||
unzip -oq dalamud.zip -d "$hooks"
|
||||
|
||||
- name: Build (Release)
|
||||
run: dotnet build HellionChat/HellionChat.csproj --configuration Release
|
||||
- name: Build (Release)
|
||||
run: dotnet build HellionChat/HellionChat.csproj --configuration Release
|
||||
|
||||
- name: Locate latest.zip
|
||||
id: locate
|
||||
run: |
|
||||
zip="$(find HellionChat/bin/Release -name latest.zip -print -quit)"
|
||||
if [ -z "$zip" ]; then
|
||||
echo "latest.zip not found under HellionChat/bin/Release" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "Found: $zip"
|
||||
echo "path=$zip" >> "$GITHUB_OUTPUT"
|
||||
- name: Locate latest.zip
|
||||
id: locate
|
||||
run: |
|
||||
zip="$(find HellionChat/bin/Release -name latest.zip -print -quit)"
|
||||
if [ -z "$zip" ]; then
|
||||
echo "latest.zip not found under HellionChat/bin/Release" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "Found: $zip"
|
||||
echo "path=$zip" >> "$GITHUB_OUTPUT"
|
||||
|
||||
# Build a release body from the matching changelog block in
|
||||
# HellionChat.yaml plus a static install / docs footer. Fails the
|
||||
# workflow if no block exists for the tagged version, which is the
|
||||
# automated counterpart to the "yaml + repo.json + release body
|
||||
# kept in sync" rule.
|
||||
#
|
||||
# GITHUB_REF_NAME is read via env: (not ${{ }} interpolation) so the
|
||||
# tag value is treated as a PowerShell variable, not as inline shell
|
||||
# text. The strict regex below rejects anything that is not a clean
|
||||
# semver tag before it is used to build a string.
|
||||
- name: Generate release body
|
||||
shell: pwsh
|
||||
env:
|
||||
# workflow_dispatch carries the user-supplied tag in inputs.tag;
|
||||
# push:tags carries it in github.ref_name. Either way the value
|
||||
# is treated as a PowerShell variable (env-var pass), not as
|
||||
# inline shell text, and validated against the semver regex
|
||||
# below before any string interpolation.
|
||||
TAG_NAME: ${{ github.event.inputs.tag || github.ref_name }}
|
||||
run: |
|
||||
$tag = $env:TAG_NAME
|
||||
if ($tag -notmatch '^v\d+\.\d+\.\d+$') {
|
||||
throw "Refusing to generate release body for non-semver tag: $tag"
|
||||
}
|
||||
$version = $tag.Substring(1)
|
||||
# Build a release body from the matching changelog block in
|
||||
# HellionChat.yaml plus a static install / docs footer. Fails the
|
||||
# workflow if no block exists for the tagged version, which is the
|
||||
# automated counterpart to the "yaml + repo.json + release body
|
||||
# kept in sync" rule.
|
||||
#
|
||||
# GITHUB_REF_NAME is read via env: (not ${{ }} interpolation) so the
|
||||
# tag value is treated as a PowerShell variable, not as inline shell
|
||||
# text. The strict regex below rejects anything that is not a clean
|
||||
# semver tag before it is used to build a string.
|
||||
- name: Generate release body
|
||||
shell: pwsh
|
||||
env:
|
||||
# workflow_dispatch carries the user-supplied tag in inputs.tag;
|
||||
# push:tags carries it in github.ref_name. Either way the value
|
||||
# is treated as a PowerShell variable (env-var pass), not as
|
||||
# inline shell text, and validated against the semver regex
|
||||
# below before any string interpolation.
|
||||
TAG_NAME: ${{ github.event.inputs.tag || github.ref_name }}
|
||||
run: |
|
||||
$tag = $env:TAG_NAME
|
||||
if ($tag -notmatch '^v\d+\.\d+\.\d+$') {
|
||||
throw "Refusing to generate release body for non-semver tag: $tag"
|
||||
}
|
||||
$version = $tag.Substring(1)
|
||||
|
||||
$yamlPath = "HellionChat/HellionChat.yaml"
|
||||
$raw = Get-Content -Path $yamlPath -Raw
|
||||
$yamlPath = "HellionChat/HellionChat.yaml"
|
||||
$raw = Get-Content -Path $yamlPath -Raw
|
||||
|
||||
$marker = "changelog: |-"
|
||||
$idx = $raw.IndexOf($marker)
|
||||
if ($idx -lt 0) { throw "changelog block not found in $yamlPath" }
|
||||
$marker = "changelog: |-"
|
||||
$idx = $raw.IndexOf($marker)
|
||||
if ($idx -lt 0) { throw "changelog block not found in $yamlPath" }
|
||||
|
||||
# changelog: is the last top-level key in the manifest, so
|
||||
# everything after the marker is the literal block. Strip the
|
||||
# 2-space yaml indent from each line.
|
||||
$afterMarker = $raw.Substring($idx + $marker.Length)
|
||||
$changelogBody = (($afterMarker -split "`r?`n") | ForEach-Object {
|
||||
if ($_ -match '^ ') { $_.Substring(2) } else { $_ }
|
||||
}) -join "`n"
|
||||
# changelog: is the last top-level key in the manifest, so
|
||||
# everything after the marker is the literal block. Strip the
|
||||
# 2-space yaml indent from each line.
|
||||
$afterMarker = $raw.Substring($idx + $marker.Length)
|
||||
$changelogBody = (($afterMarker -split "`r?`n") | ForEach-Object {
|
||||
if ($_ -match '^ ') { $_.Substring(2) } else { $_ }
|
||||
}) -join "`n"
|
||||
|
||||
$header = "**Hellion Chat $version"
|
||||
$start = $changelogBody.IndexOf($header)
|
||||
if ($start -lt 0) {
|
||||
throw "No changelog entry for version $version found in $yamlPath. Update the changelog block before tagging a release."
|
||||
}
|
||||
$header = "**Hellion Chat $version"
|
||||
$start = $changelogBody.IndexOf($header)
|
||||
if ($start -lt 0) {
|
||||
throw "No changelog entry for version $version found in $yamlPath. Update the changelog block before tagging a release."
|
||||
}
|
||||
|
||||
$rest = $changelogBody.Substring($start)
|
||||
$nextHdr = $rest.IndexOf("`n`n**Hellion Chat ", 1)
|
||||
$trailer = $rest.IndexOf("`n`n---")
|
||||
$rest = $changelogBody.Substring($start)
|
||||
$nextHdr = $rest.IndexOf("`n`n**Hellion Chat ", 1)
|
||||
$trailer = $rest.IndexOf("`n`n---")
|
||||
|
||||
if ($nextHdr -ge 0 -and ($trailer -lt 0 -or $nextHdr -lt $trailer)) {
|
||||
$currentBlock = $rest.Substring(0, $nextHdr).TrimEnd()
|
||||
} elseif ($trailer -ge 0) {
|
||||
$currentBlock = $rest.Substring(0, $trailer).TrimEnd()
|
||||
} else {
|
||||
$currentBlock = $rest.TrimEnd()
|
||||
}
|
||||
if ($nextHdr -ge 0 -and ($trailer -lt 0 -or $nextHdr -lt $trailer)) {
|
||||
$currentBlock = $rest.Substring(0, $nextHdr).TrimEnd()
|
||||
} elseif ($trailer -ge 0) {
|
||||
$currentBlock = $rest.Substring(0, $trailer).TrimEnd()
|
||||
} else {
|
||||
$currentBlock = $rest.TrimEnd()
|
||||
}
|
||||
|
||||
# Static install / docs / licence footer is maintained as a
|
||||
# separate file so the workflow YAML stays clean (no embedded
|
||||
# heredoc that would have to be indented under the run-block).
|
||||
$footerPath = ".github/release-footer.md"
|
||||
if (-not (Test-Path $footerPath)) {
|
||||
throw "Release footer template not found: $footerPath"
|
||||
}
|
||||
$footer = Get-Content -Path $footerPath -Raw
|
||||
# Static install / docs / licence footer is maintained as a
|
||||
# separate file so the workflow YAML stays clean (no embedded
|
||||
# heredoc that would have to be indented under the run-block).
|
||||
$footerPath = ".github/release-footer.md"
|
||||
if (-not (Test-Path $footerPath)) {
|
||||
throw "Release footer template not found: $footerPath"
|
||||
}
|
||||
$footer = Get-Content -Path $footerPath -Raw
|
||||
|
||||
$body = $currentBlock + "`n" + $footer
|
||||
$body | Out-File -FilePath release-body.md -Encoding utf8 -NoNewline
|
||||
$body = $currentBlock + "`n" + $footer
|
||||
$body | Out-File -FilePath release-body.md -Encoding utf8 -NoNewline
|
||||
|
||||
Write-Host "Generated release body for $tag :"
|
||||
Write-Host "----------------------------------------"
|
||||
Write-Host $body
|
||||
Write-Host "----------------------------------------"
|
||||
Write-Host "Generated release body for $tag :"
|
||||
Write-Host "----------------------------------------"
|
||||
Write-Host $body
|
||||
Write-Host "----------------------------------------"
|
||||
|
||||
# Gitea-native release action. Creates the release if the tag has no
|
||||
# release yet, or updates the existing one. body_path provides the
|
||||
# generated release body, files attaches latest.zip. The auto-injected
|
||||
# GITHUB_TOKEN on Gitea Actions has Gitea-API scope and is sufficient
|
||||
# for release write.
|
||||
- name: Attach to Gitea release
|
||||
uses: https://gitea.com/actions/release-action@main
|
||||
with:
|
||||
# Explicit tag_name so the action targets the correct release in
|
||||
# both push:tags (auto) and workflow_dispatch (manual recovery)
|
||||
# modes. Without this, dispatch runs would default to the branch
|
||||
# ref (main) and fail to find the release.
|
||||
tag_name: ${{ github.event.inputs.tag || github.ref_name }}
|
||||
files: ${{ steps.locate.outputs.path }}
|
||||
body_path: release-body.md
|
||||
api_key: ${{ secrets.GITHUB_TOKEN }}
|
||||
# Gitea-native release action. Creates the release if the tag has no
|
||||
# release yet, or updates the existing one. body_path provides the
|
||||
# generated release body, files attaches latest.zip. The auto-injected
|
||||
# GITHUB_TOKEN on Gitea Actions has Gitea-API scope and is sufficient
|
||||
# for release write.
|
||||
- name: Attach to Gitea release
|
||||
uses: https://gitea.com/actions/release-action@main
|
||||
with:
|
||||
# Explicit tag_name so the action targets the correct release in
|
||||
# both push:tags (auto) and workflow_dispatch (manual recovery)
|
||||
# modes. Without this, dispatch runs would default to the branch
|
||||
# ref (main) and fail to find the release.
|
||||
tag_name: ${{ github.event.inputs.tag || github.ref_name }}
|
||||
files: ${{ steps.locate.outputs.path }}
|
||||
body_path: release-body.md
|
||||
api_key: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
Reference in New Issue
Block a user