2.0.0 (#92)
All checks were successful
Tag and Release Lightless / tag-and-release (push) Successful in 2m27s

2.0.0 Changes:

- Reworked shell finder UI with compact or list view with profile tags showing with the listing, allowing moderators to broadcast the syncshell as well to have it be used more.
- Reworked user list in syncshell admin screen to have filter visible and moved away from table to its own thing, allowing to copy uid/note/alias when clicking on the name.
- Reworked download bars and download box to make it look more modern, removed the jitter around, so it shouldn't vibrate around much.
- Chat has been added to the top menu, working in Zone or in Syncshells to be used there.
- Paired system has been revamped to make pausing and unpausing faster, and loading people should be faster as well.
- Moved to the internal object table to have faster load times for users; people should load in faster
- Compactor is running on a multi-threaded level instead of single-threaded; this should increase the speed of compacting files
- Nameplate Service has been reworked so it wouldn't use the nameplate handler anymore.
- Files can be resized when downloading to reduce load on users if they aren't compressed. (can be toggled to resize all).
- Penumbra Collections are now only made when people are visible, reducing the load on boot-up when having many syncshells in your list.
- Lightfinder plates have been moved away from using Nameplates, but will use an overlay.
- Main UI has been changed a bit with a gradient, and on hover will glow up now.
- Reworked Profile UI for Syncshell and Users to be more user-facing with more customizable items.
- Reworked Settings UI to look more modern.
- Performance should be better due to new systems that would dispose of the collections and better caching of items.

Co-authored-by: defnotken <itsdefnotken@gmail.com>
Co-authored-by: azyges <aaaaaa@aaa.aaa>
Co-authored-by: choco <choco@patat.nl>
Co-authored-by: cake <admin@cakeandbanana.nl>
Co-authored-by: Minmoose <KennethBohr@outlook.com>
Reviewed-on: #92
This commit was merged in pull request #92.
This commit is contained in:
2025-12-21 17:19:34 +00:00
parent 906f401940
commit 835a0a637d
191 changed files with 32636 additions and 8841 deletions

View File

@@ -1,13 +1,12 @@
using Dalamud.Interface;
using Dalamud.Interface.Colors;
using Dalamud.Interface.Utility;
using Dalamud.Interface.Utility.Raii;
using Dalamud.Interface.Windowing;
using LightlessSync.LightlessConfiguration;
using LightlessSync.LightlessConfiguration.Models;
using LightlessSync.Services;
using LightlessSync.Services.Mediator;
using LightlessSync.UI.Models;
using LightlessSync.UI.Style;
using Microsoft.Extensions.Logging;
using System.Numerics;
@@ -27,11 +26,12 @@ public class LightlessNotificationUi : WindowMediatorSubscriberBase
private const float _titleMessageSpacing = 4f;
private const float _actionButtonSpacing = 8f;
private readonly List<LightlessNotification> _notifications = new();
private readonly List<LightlessNotification> _notifications = [];
private readonly object _notificationLock = new();
private readonly LightlessConfigService _configService;
private readonly Dictionary<string, float> _notificationYOffsets = new();
private readonly Dictionary<string, float> _notificationTargetYOffsets = new();
private readonly Dictionary<string, float> _notificationYOffsets = [];
private readonly Dictionary<string, float> _notificationTargetYOffsets = [];
private readonly Dictionary<string, Vector4> _notificationBackgrounds = [];
public LightlessNotificationUi(ILogger<LightlessNotificationUi> logger, LightlessMediator mediator, PerformanceCollectorService performanceCollector, LightlessConfigService configService)
: base(logger, mediator, "Lightless Notifications##LightlessNotifications", performanceCollector)
@@ -45,7 +45,6 @@ public class LightlessNotificationUi : WindowMediatorSubscriberBase
ImGuiWindowFlags.NoNav |
ImGuiWindowFlags.NoBackground |
ImGuiWindowFlags.NoCollapse |
ImGuiWindowFlags.NoInputs |
ImGuiWindowFlags.NoTitleBar |
ImGuiWindowFlags.NoScrollbar |
ImGuiWindowFlags.AlwaysAutoResize;
@@ -68,7 +67,7 @@ public class LightlessNotificationUi : WindowMediatorSubscriberBase
{
lock (_notificationLock)
{
var existingNotification = _notifications.FirstOrDefault(n => n.Id == notification.Id);
var existingNotification = _notifications.FirstOrDefault(n => string.Equals(n.Id, notification.Id, StringComparison.Ordinal));
if (existingNotification != null)
{
UpdateExistingNotification(existingNotification, notification);
@@ -76,7 +75,7 @@ public class LightlessNotificationUi : WindowMediatorSubscriberBase
else
{
_notifications.Add(notification);
_logger.LogDebug("Added new notification: {Title}", notification.Title);
_logger.LogTrace("Added new notification: {Title}", notification.Title);
}
if (!IsOpen) IsOpen = true;
@@ -96,14 +95,14 @@ public class LightlessNotificationUi : WindowMediatorSubscriberBase
existing.CreatedAt = DateTime.UtcNow;
}
_logger.LogDebug("Updated existing notification: {Title}", updated.Title);
_logger.LogTrace("Updated existing notification: {Title}", updated.Title);
}
public void RemoveNotification(string id)
{
lock (_notificationLock)
{
var notification = _notifications.FirstOrDefault(n => n.Id == id);
var notification = _notifications.FirstOrDefault(n => string.Equals(n.Id, id, StringComparison.Ordinal));
if (notification != null)
{
StartOutAnimation(notification);
@@ -122,13 +121,13 @@ public class LightlessNotificationUi : WindowMediatorSubscriberBase
}
}
private void StartOutAnimation(LightlessNotification notification)
private static void StartOutAnimation(LightlessNotification notification)
{
notification.IsAnimatingOut = true;
notification.IsAnimatingIn = false;
}
private bool ShouldRemoveNotification(LightlessNotification notification) =>
private static bool ShouldRemoveNotification(LightlessNotification notification) =>
notification.IsAnimatingOut && notification.AnimationProgress <= 0.01f;
protected override void DrawInternal()
@@ -162,30 +161,30 @@ public class LightlessNotificationUi : WindowMediatorSubscriberBase
{
var corner = _configService.Current.NotificationCorner;
var offsetX = _configService.Current.NotificationOffsetX;
var offsetY = _configService.Current.NotificationOffsetY;
var width = _configService.Current.NotificationWidth;
float posX = corner == NotificationCorner.Left
? viewport.WorkPos.X + offsetX - _windowPaddingOffset
: viewport.WorkPos.X + viewport.WorkSize.X - width - offsetX - _windowPaddingOffset;
return new Vector2(posX, viewport.WorkPos.Y);
float posY = viewport.WorkPos.Y + offsetY;
return new Vector2(posX, posY);
}
private void DrawAllNotifications()
{
var offsetY = _configService.Current.NotificationOffsetY;
var startY = ImGui.GetCursorPosY() + offsetY;
var startY = ImGui.GetCursorPosY();
for (int i = 0; i < _notifications.Count; i++)
{
var notification = _notifications[i];
if (_notificationYOffsets.TryGetValue(notification.Id, out var yOffset))
{
ImGui.SetCursorPosY(startY + yOffset);
}
DrawNotification(notification, i);
DrawNotification(notification);
}
}
@@ -228,6 +227,7 @@ public class LightlessNotificationUi : WindowMediatorSubscriberBase
_notifications.RemoveAt(i);
_notificationYOffsets.Remove(notification.Id);
_notificationTargetYOffsets.Remove(notification.Id);
_notificationBackgrounds.Remove(notification.Id);
}
}
}
@@ -304,7 +304,7 @@ public class LightlessNotificationUi : WindowMediatorSubscriberBase
return corner == NotificationCorner.Left ? new Vector2(-distance, 0) : new Vector2(distance, 0);
}
private void DrawNotification(LightlessNotification notification, int index)
private void DrawNotification(LightlessNotification notification)
{
var alpha = notification.AnimationProgress;
if (alpha <= 0f) return;
@@ -336,14 +336,15 @@ public class LightlessNotificationUi : WindowMediatorSubscriberBase
var windowPos = ImGui.GetWindowPos();
var windowSize = ImGui.GetWindowSize();
var bgColor = CalculateBackgroundColor(alpha, ImGui.IsWindowHovered());
var accentColor = GetNotificationAccentColor(notification.Type);
accentColor.W *= alpha;
var bgColor = CalculateBackgroundColor(notification, alpha, ImGui.IsWindowHovered(), accentColor);
var accentColorWithAlpha = accentColor;
accentColorWithAlpha.W *= alpha;
DrawShadow(drawList, windowPos, windowSize, alpha);
HandleClickToDismiss(notification);
DrawBackground(drawList, windowPos, windowSize, bgColor);
DrawAccentBar(drawList, windowPos, windowSize, accentColor);
DrawAccentBar(drawList, windowPos, windowSize, accentColorWithAlpha);
DrawDurationProgressBar(notification, alpha, windowPos, windowSize, drawList);
// Draw download progress bar above duration bar for download notifications
@@ -355,22 +356,44 @@ public class LightlessNotificationUi : WindowMediatorSubscriberBase
DrawNotificationText(notification, alpha);
}
private Vector4 CalculateBackgroundColor(float alpha, bool isHovered)
private Vector4 CalculateBackgroundColor(LightlessNotification notification, float alpha, bool isHovered, Vector4 accentColor)
{
var baseOpacity = _configService.Current.NotificationOpacity;
var finalOpacity = baseOpacity * alpha;
var bgColor = new Vector4(30f/255f, 30f/255f, 30f/255f, finalOpacity);
float boost = Luminance.ComputeHighlight(null, accentColor);
var baseBg = new Vector4(
30f/255f + boost,
30f/255f + boost,
30f/255f + boost,
finalOpacity
);
if (!_notificationBackgrounds.ContainsKey(notification.Id))
{
_notificationBackgrounds[notification.Id] = baseBg;
}
var currentBg = _notificationBackgrounds[notification.Id];
var bgColor = Luminance.BackgroundContrast(null, accentColor, baseBg, ref currentBg);
_notificationBackgrounds[notification.Id] = currentBg;
bgColor.W = finalOpacity;
if (isHovered)
{
bgColor *= 1.1f;
bgColor.W = Math.Min(bgColor.W, 0.98f);
bgColor = new Vector4(
bgColor.X * 1.1f,
bgColor.Y * 1.1f,
bgColor.Z * 1.1f,
Math.Min(bgColor.W, 0.98f)
);
}
return bgColor;
}
private void DrawShadow(ImDrawListPtr drawList, Vector2 windowPos, Vector2 windowSize, float alpha)
private static void DrawShadow(ImDrawListPtr drawList, Vector2 windowPos, Vector2 windowSize, float alpha)
{
var shadowOffset = new Vector2(1f, 1f);
var shadowColor = new Vector4(0f, 0f, 0f, 0.4f * alpha);
@@ -384,9 +407,13 @@ public class LightlessNotificationUi : WindowMediatorSubscriberBase
private void HandleClickToDismiss(LightlessNotification notification)
{
if (ImGui.IsWindowHovered() &&
var pos = ImGui.GetWindowPos();
var size = ImGui.GetWindowSize();
bool hovered = ImGui.IsMouseHoveringRect(pos, new Vector2(pos.X + size.X, pos.Y + size.Y));
if ((hovered || ImGui.IsWindowHovered()) &&
_configService.Current.DismissNotificationOnClick &&
!notification.Actions.Any() &&
notification.Actions.Count == 0 &&
ImGui.IsMouseClicked(ImGuiMouseButton.Left))
{
notification.IsDismissed = true;
@@ -394,7 +421,7 @@ public class LightlessNotificationUi : WindowMediatorSubscriberBase
}
}
private void DrawBackground(ImDrawListPtr drawList, Vector2 windowPos, Vector2 windowSize, Vector4 bgColor)
private static void DrawBackground(ImDrawListPtr drawList, Vector2 windowPos, Vector2 windowSize, Vector4 bgColor)
{
drawList.AddRectFilled(
windowPos,
@@ -431,14 +458,14 @@ public class LightlessNotificationUi : WindowMediatorSubscriberBase
);
}
private void DrawDurationProgressBar(LightlessNotification notification, float alpha, Vector2 windowPos, Vector2 windowSize, ImDrawListPtr drawList)
private static void DrawDurationProgressBar(LightlessNotification notification, float alpha, Vector2 windowPos, Vector2 windowSize, ImDrawListPtr drawList)
{
var progress = CalculateDurationProgress(notification);
var progressBarColor = UIColors.Get("LightlessBlue");
var progressHeight = 2f;
var progressY = windowPos.Y + windowSize.Y - progressHeight;
var progressWidth = windowSize.X * progress;
DrawProgressBackground(drawList, windowPos, windowSize, progressY, progressHeight, progressBarColor, alpha);
if (progress > 0)
@@ -447,7 +474,7 @@ public class LightlessNotificationUi : WindowMediatorSubscriberBase
}
}
private void DrawDownloadProgressBar(LightlessNotification notification, float alpha, Vector2 windowPos, Vector2 windowSize, ImDrawListPtr drawList)
private static void DrawDownloadProgressBar(LightlessNotification notification, float alpha, Vector2 windowPos, Vector2 windowSize, ImDrawListPtr drawList)
{
var progress = Math.Clamp(notification.Progress, 0f, 1f);
var progressBarColor = UIColors.Get("LightlessGreen");
@@ -455,7 +482,7 @@ public class LightlessNotificationUi : WindowMediatorSubscriberBase
// Position above the duration bar (2px duration bar + 1px spacing)
var progressY = windowPos.Y + windowSize.Y - progressHeight - 3f;
var progressWidth = windowSize.X * progress;
DrawProgressBackground(drawList, windowPos, windowSize, progressY, progressHeight, progressBarColor, alpha);
if (progress > 0)
@@ -464,14 +491,14 @@ public class LightlessNotificationUi : WindowMediatorSubscriberBase
}
}
private float CalculateDurationProgress(LightlessNotification notification)
private static float CalculateDurationProgress(LightlessNotification notification)
{
// Calculate duration timer progress
var elapsed = DateTime.UtcNow - notification.CreatedAt;
return Math.Min(1.0f, (float)(elapsed.TotalSeconds / notification.Duration.TotalSeconds));
}
private void DrawProgressBackground(ImDrawListPtr drawList, Vector2 windowPos, Vector2 windowSize, float progressY, float progressHeight, Vector4 progressBarColor, float alpha)
private static void DrawProgressBackground(ImDrawListPtr drawList, Vector2 windowPos, Vector2 windowSize, float progressY, float progressHeight, Vector4 progressBarColor, float alpha)
{
var bgProgressColor = new Vector4(progressBarColor.X * 0.3f, progressBarColor.Y * 0.3f, progressBarColor.Z * 0.3f, 0.5f * alpha);
drawList.AddRectFilled(
@@ -482,7 +509,7 @@ public class LightlessNotificationUi : WindowMediatorSubscriberBase
);
}
private void DrawProgressForeground(ImDrawListPtr drawList, Vector2 windowPos, float progressY, float progressHeight, float progressWidth, Vector4 progressBarColor, float alpha)
private static void DrawProgressForeground(ImDrawListPtr drawList, Vector2 windowPos, float progressY, float progressHeight, float progressWidth, Vector4 progressBarColor, float alpha)
{
var progressColor = progressBarColor;
progressColor.W *= alpha;
@@ -512,13 +539,13 @@ public class LightlessNotificationUi : WindowMediatorSubscriberBase
}
}
private float CalculateContentWidth(float windowWidth) =>
private static float CalculateContentWidth(float windowWidth) =>
windowWidth - (_contentPaddingX * 2);
private bool HasActions(LightlessNotification notification) =>
private static bool HasActions(LightlessNotification notification) =>
notification.Actions.Count > 0;
private void PositionActionsAtBottom(float windowHeight)
private static void PositionActionsAtBottom(float windowHeight)
{
var actionHeight = ImGui.GetFrameHeight();
var bottomY = windowHeight - _contentPaddingY - actionHeight;
@@ -546,7 +573,7 @@ public class LightlessNotificationUi : WindowMediatorSubscriberBase
return $"[{timestamp}] {notification.Title}";
}
private float DrawWrappedText(string text, float wrapWidth)
private static float DrawWrappedText(string text, float wrapWidth)
{
ImGui.PushTextWrapPos(ImGui.GetCursorPosX() + wrapWidth);
var startY = ImGui.GetCursorPosY();
@@ -556,7 +583,7 @@ public class LightlessNotificationUi : WindowMediatorSubscriberBase
return height;
}
private void DrawMessage(LightlessNotification notification, Vector2 contentPos, float contentWidth, float titleHeight, float alpha)
private static void DrawMessage(LightlessNotification notification, Vector2 contentPos, float contentWidth, float titleHeight, float alpha)
{
if (string.IsNullOrEmpty(notification.Message)) return;
@@ -575,7 +602,7 @@ public class LightlessNotificationUi : WindowMediatorSubscriberBase
{
var buttonWidth = CalculateActionButtonWidth(notification.Actions.Count, availableWidth);
_logger.LogDebug("Drawing {ActionCount} notification actions, buttonWidth: {ButtonWidth}, availableWidth: {AvailableWidth}",
_logger.LogTrace("Drawing {ActionCount} notification actions, buttonWidth: {ButtonWidth}, availableWidth: {AvailableWidth}",
notification.Actions.Count, buttonWidth, availableWidth);
var startX = ImGui.GetCursorPosX();
@@ -591,13 +618,13 @@ public class LightlessNotificationUi : WindowMediatorSubscriberBase
}
}
private float CalculateActionButtonWidth(int actionCount, float availableWidth)
private static float CalculateActionButtonWidth(int actionCount, float availableWidth)
{
var totalSpacing = (actionCount - 1) * _actionButtonSpacing;
return (availableWidth - totalSpacing) / actionCount;
}
private void PositionActionButton(int index, float startX, float buttonWidth)
private static void PositionActionButton(int index, float startX, float buttonWidth)
{
var xPosition = startX + index * (buttonWidth + _actionButtonSpacing);
ImGui.SetCursorPosX(xPosition);
@@ -605,7 +632,7 @@ public class LightlessNotificationUi : WindowMediatorSubscriberBase
private void DrawActionButton(LightlessNotificationAction action, LightlessNotification notification, float alpha, float buttonWidth)
{
_logger.LogDebug("Drawing action button: {ActionId} - {ActionLabel}, width: {ButtonWidth}", action.Id, action.Label, buttonWidth);
_logger.LogTrace("Drawing action button: {ActionId} - {ActionLabel}, width: {ButtonWidth}", action.Id, action.Label, buttonWidth);
var buttonColor = action.Color;
buttonColor.W *= alpha;
@@ -625,22 +652,22 @@ public class LightlessNotificationUi : WindowMediatorSubscriberBase
if (action.Icon != FontAwesomeIcon.None)
{
buttonPressed = DrawIconTextButton(action.Icon, action.Label, buttonWidth, alpha);
buttonPressed = DrawIconTextButton(action.Icon, action.Label, buttonWidth);
}
else
{
buttonPressed = ImGui.Button(action.Label, new Vector2(buttonWidth, 0));
}
_logger.LogDebug("Button {ActionId} pressed: {ButtonPressed}", action.Id, buttonPressed);
_logger.LogTrace("Button {ActionId} pressed: {ButtonPressed}", action.Id, buttonPressed);
if (buttonPressed)
{
try
{
_logger.LogDebug("Executing action: {ActionId}", action.Id);
_logger.LogTrace("Executing action: {ActionId}", action.Id);
action.OnClick(notification);
_logger.LogDebug("Action executed successfully: {ActionId}", action.Id);
_logger.LogTrace("Action executed successfully: {ActionId}", action.Id);
}
catch (Exception ex)
{
@@ -650,10 +677,10 @@ public class LightlessNotificationUi : WindowMediatorSubscriberBase
}
}
private bool DrawIconTextButton(FontAwesomeIcon icon, string text, float width, float alpha)
private static bool DrawIconTextButton(FontAwesomeIcon icon, string text, float width)
{
var drawList = ImGui.GetWindowDrawList();
var cursorPos = ImGui.GetCursorScreenPos();
ImGui.GetCursorScreenPos();
var frameHeight = ImGui.GetFrameHeight();
Vector2 iconSize;
@@ -729,7 +756,7 @@ public class LightlessNotificationUi : WindowMediatorSubscriberBase
return ImGui.CalcTextSize(titleText, true, contentWidth).Y;
}
private float CalculateMessageHeight(LightlessNotification notification, float contentWidth)
private static float CalculateMessageHeight(LightlessNotification notification, float contentWidth)
{
if (string.IsNullOrEmpty(notification.Message)) return 0f;
@@ -737,7 +764,7 @@ public class LightlessNotificationUi : WindowMediatorSubscriberBase
return 4f + messageHeight;
}
private Vector4 GetNotificationAccentColor(NotificationType type)
private static Vector4 GetNotificationAccentColor(NotificationType type)
{
return type switch
{