patch notes setup, added some of the older patchnotes into a changelog.yaml

This commit is contained in:
choco
2025-10-15 13:29:32 +02:00
parent 77ff8ae372
commit a66a43dda8
9 changed files with 542 additions and 1 deletions

View File

@@ -0,0 +1,180 @@
tagline: "Lightless Sync v1.12.3"
subline: "FILLER"
changelog:
- name: "v1.12.3"
tagline: "FILLER"
date: "October 15th 2025"
is_current: true
versions:
- number: "New Features"
icon: ""
items:
- "New in-game Patch Notes window."
- "Credits section to thank contributors and supporters."
- "Patch notes only show after updates, not during first-time setup."
- number: "Notifications"
icon: ""
items:
- "More customizable notification options."
- "Perfomance limiter shows as notifications."
- "All notifications can be configured or disabled in Settings → Notifications."
- name: "v1.12.2"
tagline: "LightFinder fixes, Notifications overhaul"
date: "October 12th 2025"
is_current: false
versions:
- number: "LightFinder"
icon: ""
items:
- "Server-side improvements for LightFinder functionality."
- "Command changed from '/light lightfinder' to '/light finder'."
- "Option to enable LightFinder on connection (opt-in, refreshes every 3 hours)."
- "LightFinder indicator can now be shown on the server info bar."
- number: "Notifications"
icon: ""
items:
- "Completely reworked notification system with new UI."
- "Pair requests now show as notifications."
- "Download progress shows as notifications."
- "Customizable notification sounds, size, position, and duration."
- "All notifications can be configured or disabled in Settings → Notifications."
- number: "Bug Fixes"
icon: ""
items:
- "Fixed nameplate alignment issues with LightFinder and icons."
- "Icons now properly apply instead of swapping on choice."
- "Updated Discord URL."
- "File cache logic improvements."
- name: "v1.12.1"
tagline: "LightFinder customization and download limiter"
date: "October 8th 2025"
is_current: false
versions:
- number: "New Features"
icon: ""
items:
- "LightFinder text can be modified to an icon with customizable positioning."
- "Option to hide your own indicator or paired player indicators."
- "Pair Download Limiter: Limit simultaneous downloads to 1-6 users to reduce network strain."
- "Added '/light lightfinder' command to open LightFinder UI."
- number: "Improvements"
icon: ""
items:
- "Right-click menu option for Send Pair Request can be disabled."
- "Syncshell finder improvements."
- "Download limiter settings available in Settings → Transfers."
- name: "v1.12.0"
tagline: "LightFinder - Major feature release"
date: "October 5th 2025"
is_current: false
versions:
- number: "Major Features"
icon: ""
items:
- "Introduced LightFinder: Optional feature inspired by FFXIV's Party Finder."
- "Find fellow Lightless users and advertise your Syncshell to others."
- "When enabled, you're visible to other LightFinder users for 3 hours."
- "LightFinder tag displays above your nameplate when active."
- "Receive pair requests directly in UI without exchanging UIDs."
- "Syncshell Finder allows joining indexed Syncshells."
- "[L] Send Pair Request added to player context menus."
- number: "Vanity Features"
icon: ""
items:
- "Supporters can now customize their name color in the Lightless UI."
- "Color changes visible to all users."
- number: "General Improvements"
icon: ""
items:
- "Pairing nameplate color override can now override FC tags."
- "Added .kdb as whitelisted filetype for uploads."
- "Various UI fixes, updates, and improvements."
- name: "v1.11.12"
tagline: "Syncshell grouping and performance options"
date: "September 16th 2025"
is_current: false
versions:
- number: "New Features"
icon: ""
items:
- "Ability to show grouped syncshells in main UI/all syncshells (default ON)."
- "Transfer ownership button available in Admin Panel user list."
- "Self-threshold warning now opens character analysis screen when clicked."
- number: "Performance"
icon: ""
items:
- "Auto-pause combat and auto-pause performance are now optional settings."
- "Both options are auto-enabled by default - disable at your own risk."
- number: "Bug Fixes"
icon: ""
items:
- "Reworked file caching to reduce errors for some users."
- "Fixed bug where exiting PvP could desync some users."
- name: "v1.11.9"
tagline: "File cache improvements"
date: "September 13th 2025"
is_current: false
versions:
- number: "Bug Fixes"
icon: ""
items:
- "Identified and fixed potential file cache problems."
- "Improved cache error handling and stability."
- name: "v1.11.8"
tagline: "Hotfix - UI and exception handling"
date: "September 12th 2025"
is_current: false
versions:
- number: "Bug Fixes"
icon: ""
items:
- "Attempted fix for NullReferenceException spam."
- "Fixed additional UI edge cases preventing loading for some users."
- "Fixed color bar UI issues."
- name: "v1.11.7"
tagline: "Hotfix - UI loading and warnings"
date: "September 12th 2025"
is_current: false
versions:
- number: "Bug Fixes"
icon: ""
items:
- "Fixed UI not loading for some users."
- "Self warnings now behind 'Warn on loading in players exceeding performance thresholds' setting."
- name: "v1.11.6"
tagline: "Admin panel rework and new features"
date: "September 11th 2025"
is_current: false
versions:
- number: "New Features"
icon: ""
items:
- "Reworked Syncshell Admin Page with improved styling."
- "Right-click on Server Top Bar button to disconnect from Lightless."
- "Shift+Left click on Server Top Bar button to open settings."
- "Added colors section in settings to change accent colors."
- "Added pin option from Dalamud in the UI."
- "Ability to pause syncing while in Instance/Duty."
- "Functionality to create syncshell folders."
- "Added self-threshold warning."
- number: "Bug Fixes"
icon: ""
items:
- "Fixed owners being visible in moderator list view."
- "Removed Pin/Remove/Ban buttons on Owners when viewing as moderator."
- "Fixed nameplate bug in PvP."
- "Added 1 or 3 day options for inactive check."
- name: "Template"
tagline: ""
date: "October 15th 2025"
is_current: false
message: "Thank you for using Lightless Sync!\n\nThis update brings quality of life improvements and polish to the user experience.\nWe're committed to helping you share your character with others seamlessly.\n\nIf you have any suggestions or encounter any issues, please let us know on Discord or GitHub!\n\n- The Lightless Team"

View File

@@ -0,0 +1 @@
[Add contributor names - GitHub handles, etc.]

View File

@@ -0,0 +1,2 @@
UI design inspired by Brio's update window (Etheirys/Brio). Thanks to their team for the great UX ideas.
Special thanks to the Dalamud team and the XIV modding ecosystem for tooling & APIs.

View File

@@ -0,0 +1 @@
[Your Names Here]

View File

@@ -0,0 +1,332 @@
using Dalamud.Bindings.ImGui;
using Dalamud.Interface.Utility;
using Dalamud.Interface.Utility.Raii;
using Dalamud.Utility;
using LightlessSync.LightlessConfiguration;
using LightlessSync.Services;
using LightlessSync.Services.Mediator;
using Microsoft.Extensions.Logging;
using System.IO;
using System.Numerics;
using System.Reflection;
using System.Text;
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.NamingConventions;
namespace LightlessSync.UI;
public class UpdateNotesUi : WindowMediatorSubscriberBase
{
private readonly LightlessConfigService _configService;
private readonly UiSharedService _uiShared;
private readonly List<string> _contributors = [];
private readonly List<string> _credits = [];
private readonly List<string> _supporters = [];
private ChangelogFile _changelog = new();
private bool _scrollToTop;
private int _selectedTab;
public UpdateNotesUi(ILogger<UpdateNotesUi> logger,
LightlessMediator mediator,
UiSharedService uiShared,
LightlessConfigService configService,
PerformanceCollectorService performanceCollectorService)
: base(logger, mediator, "Lightless Sync — Update Notes", performanceCollectorService)
{
_configService = configService;
_uiShared = uiShared;
AllowClickthrough = false;
AllowPinning = false;
RespectCloseHotkey = true;
ShowCloseButton = true;
Flags = ImGuiWindowFlags.NoSavedSettings | ImGuiWindowFlags.NoResize | ImGuiWindowFlags.NoCollapse;
SizeConstraints = new WindowSizeConstraints()
{
MinimumSize = new Vector2(600, 500),
MaximumSize = new Vector2(900, 2000),
};
LoadEmbeddedResources();
}
public override void OnOpen()
{
_scrollToTop = true;
}
protected override void DrawInternal()
{
if (_uiShared.IsInGpose)
return;
DrawHeader();
DrawLinkButtons();
ImGuiHelpers.ScaledDummy(6);
DrawTabs();
DrawCloseButton();
}
private void DrawHeader()
{
using (_uiShared.UidFont.Push())
{
ImGui.TextUnformatted("Lightless Sync");
}
_uiShared.ColoredSeparator(UIColors.Get("LightlessBlue"), thickness: 2f);
if (!string.IsNullOrEmpty(_changelog.Tagline))
{
_uiShared.MediumText(_changelog.Tagline, UIColors.Get("LightlessBlue"));
if (!string.IsNullOrEmpty(_changelog.Subline))
{
ImGui.SameLine();
ImGui.TextColored(new Vector4(.75f, .75f, .85f, 1f), $" {_changelog.Subline}");
}
}
ImGuiHelpers.ScaledDummy(5);
}
private void DrawLinkButtons()
{
var segmentSize = ImGui.GetWindowSize().X / 4.2f;
var buttonSize = new Vector2(segmentSize, ImGui.GetTextLineHeight() * 1.6f);
if (ImGui.Button("Discord", buttonSize))
Util.OpenLink("https://discord.gg/dsbjcXMnhA");
ImGui.SameLine();
if (ImGui.Button("GitHub", buttonSize))
Util.OpenLink("https://github.com/Light-Public-Syncshells/LightlessSync");
ImGui.SameLine();
if (ImGui.Button("Ko-fi", buttonSize))
Util.OpenLink("https://ko-fi.com/lightlesssync");
ImGui.SameLine();
if (ImGui.Button("More Links", buttonSize))
Util.OpenLink("https://lightless.link");
}
private void DrawCloseButton()
{
var closeWidth = 300f * ImGuiHelpers.GlobalScale;
ImGui.SetCursorPosX((ImGui.GetWindowSize().X - closeWidth) / 2);
if (ImGui.Button("Close", new Vector2(closeWidth, 0)))
{
IsOpen = false;
}
}
private void DrawTabs()
{
using var tabBar = ImRaii.TabBar("lightless_update_tabs");
if (!tabBar)
return;
using (var changelogTab = ImRaii.TabItem(" Changelog "))
{
if (changelogTab)
{
_selectedTab = 0;
DrawChangelog();
}
}
using (var creditsTab = ImRaii.TabItem(" Supporters & Credits "))
{
if (creditsTab)
{
_selectedTab = 1;
DrawCredits();
}
}
}
private void DrawChangelog()
{
using var child = ImRaii.Child("###ll_changelog", new Vector2(0, ImGui.GetContentRegionAvail().Y - 44), false);
if (!child)
return;
if (_scrollToTop)
{
_scrollToTop = false;
ImGui.SetScrollHereY(0);
}
foreach (var entry in _changelog.Changelog)
DrawChangelogEntry(entry);
ImGui.Spacing();
}
private void DrawChangelogEntry(ChangelogEntry entry)
{
var currentColor = entry.IsCurrent == true
? new Vector4(0.5f, 0.9f, 0.5f, 1.0f)
: new Vector4(0.75f, 0.75f, 0.85f, 1.0f);
bool isOpen;
using (ImRaii.PushColor(ImGuiCol.Text, currentColor))
{
isOpen = ImGui.CollapsingHeader($" {entry.Name} — {entry.Date} ");
}
ImGui.SameLine();
ImGui.TextColored(new Vector4(0.75f, 0.75f, 0.85f, 1.0f), $" — {entry.Tagline}");
if (!isOpen)
return;
ImGuiHelpers.ScaledDummy(5);
if (!string.IsNullOrEmpty(entry.Message))
{
ImGui.TextWrapped(entry.Message);
ImGuiHelpers.ScaledDummy(5);
return;
}
if (entry.Versions != null)
{
foreach (var version in entry.Versions)
{
DrawFeatureHeader(version.Number, new Vector4(0.5f, 0.9f, 0.5f, 1.0f));
foreach (var item in version.Items)
ImGui.BulletText(item);
}
}
ImGuiHelpers.ScaledDummy(5);
}
private static void DrawFeatureHeader(string title, Vector4 accentColor)
{
var drawList = ImGui.GetWindowDrawList();
var startPos = ImGui.GetCursorScreenPos();
var backgroundMin = startPos + new Vector2(-10, -5);
var backgroundMax = startPos + new Vector2(ImGui.GetContentRegionAvail().X + 10, 25);
drawList.AddRectFilled(backgroundMin, backgroundMax, ImGui.GetColorU32(new Vector4(0.12f, 0.12f, 0.15f, 0.6f)), 4f);
drawList.AddRectFilled(backgroundMin, backgroundMin + new Vector2(3, backgroundMax.Y - backgroundMin.Y), ImGui.GetColorU32(accentColor), 2f);
ImGui.Spacing();
ImGui.TextColored(accentColor, title);
ImGui.Spacing();
}
private void DrawCredits()
{
ImGui.TextUnformatted("Maintained & Developed by the Lightless Sync team.");
ImGui.TextUnformatted("Thank you to all supporters and contributors!");
ImGuiHelpers.ScaledDummy(5);
var availableRegion = ImGui.GetContentRegionAvail();
var halfWidth = new Vector2(
availableRegion.X / 2f - ImGui.GetStyle().ItemSpacing.X / 2f,
availableRegion.Y - 120 * ImGuiHelpers.GlobalScale);
using (var leftChild = ImRaii.Child("left_supporters", halfWidth))
{
if (leftChild)
{
ImGui.TextUnformatted("Supporters (Ko-fi / Patreon)");
_uiShared.RoundedSeparator(UIColors.Get("LightlessBlue"), thickness: 2f);
foreach (var supporter in _supporters)
ImGui.BulletText(supporter);
}
}
ImGui.SameLine();
using (var rightChild = ImRaii.Child("right_contributors", halfWidth))
{
if (rightChild)
{
ImGui.TextUnformatted("Contributors");
_uiShared.RoundedSeparator(UIColors.Get("LightlessBlue"), thickness: 2f);
foreach (var contributor in _contributors)
ImGui.BulletText(contributor);
}
}
ImGuiHelpers.ScaledDummy(8);
ImGui.TextUnformatted("Credits");
_uiShared.RoundedSeparator(UIColors.Get("LightlessYellow"), thickness: 2f);
foreach (var credit in _credits)
ImGui.BulletText(credit);
}
private void LoadEmbeddedResources()
{
try
{
var assembly = Assembly.GetExecutingAssembly();
ReadLines(assembly, "LightlessSync.UI.Changelog.supporters.txt", _supporters);
ReadLines(assembly, "LightlessSync.UI.Changelog.contributors.txt", _contributors);
ReadLines(assembly, "LightlessSync.UI.Changelog.credits.txt", _credits);
using var changelogStream = assembly.GetManifestResourceStream("LightlessSync.UI.Changelog.changelog.yaml");
if (changelogStream != null)
{
using var reader = new StreamReader(changelogStream, Encoding.UTF8, true, 128);
var yaml = reader.ReadToEnd();
var deserializer = new DeserializerBuilder()
.WithNamingConvention(CamelCaseNamingConvention.Instance)
.IgnoreUnmatchedProperties()
.Build();
_changelog = deserializer.Deserialize<ChangelogFile>(yaml) ?? new();
}
}
catch
{
// Ignore - window will gracefully render with defaults
}
}
private static void ReadLines(Assembly assembly, string resourceName, List<string> target)
{
using var stream = assembly.GetManifestResourceStream(resourceName);
if (stream == null)
return;
using var reader = new StreamReader(stream, Encoding.UTF8, true, 128);
string? line;
while ((line = reader.ReadLine()) != null)
{
if (!string.IsNullOrWhiteSpace(line))
target.Add(line.Trim());
}
}
private sealed record ChangelogFile
{
public string Tagline { get; init; } = string.Empty;
public string Subline { get; init; } = string.Empty;
public List<ChangelogEntry> Changelog { get; init; } = new();
}
private sealed record ChangelogEntry
{
public string Name { get; init; } = string.Empty;
public string Date { get; init; } = string.Empty;
public string Tagline { get; init; } = string.Empty;
public bool? IsCurrent { get; init; }
public string? Message { get; init; }
public List<ChangelogVersion>? Versions { get; init; }
}
private sealed record ChangelogVersion
{
public string Number { get; init; } = string.Empty;
public List<string> Items { get; init; } = new();
}
}