Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions EmoTracker.Data/Settings/ApplicationColors.cs
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,36 @@ public string Status_Generic_Active

#endregion

#region -- Notification Colors --

string mNotification_Message = "#41a054";
string mNotification_Celebration = "CornflowerBlue";
string mNotification_Warning = "#c09d35";
string mNotification_Error = "#c03535";

public string Notification_Message
{
get { return mNotification_Message; }
set { SetProperty(ref mNotification_Message, value); }
}
public string Notification_Celebration
{
get { return mNotification_Celebration; }
set { SetProperty(ref mNotification_Celebration, value); }
}
public string Notification_Warning
{
get { return mNotification_Warning; }
set { SetProperty(ref mNotification_Warning, value); }
}
public string Notification_Error
{
get { return mNotification_Error; }
set { SetProperty(ref mNotification_Error, value); }
}

#endregion

#region -- Miscellaneous --

string mMap_LocationNoteBadgeBackground = "#35e0b5";
Expand Down Expand Up @@ -132,6 +162,11 @@ public void ResetColors()
Status_Generic_Active = "#35e0b5";

Map_LocationNoteBadgeBackground = "#35e0b5";

Notification_Message = "#41a054";
Notification_Celebration = "CornflowerBlue";
Notification_Warning = "#c09d35";
Notification_Error = "#c03535";
}

public void LoadColors()
Expand Down Expand Up @@ -161,6 +196,11 @@ public void LoadColors()
Status_Generic_Error = root.GetValue<string>("status_generic_error", Status_Generic_Error);

Map_LocationNoteBadgeBackground = root.GetValue<string>("map_location_has_note_badge_background", Map_LocationNoteBadgeBackground);

Notification_Message = root.GetValue<string>("notification_message", Notification_Message);
Notification_Celebration = root.GetValue<string>("notification_celebration", Notification_Celebration);
Notification_Warning = root.GetValue<string>("notification_warning", Notification_Warning);
Notification_Error = root.GetValue<string>("notification_error", Notification_Error);
}
}
catch
Expand Down
27 changes: 27 additions & 0 deletions EmoTracker.UI/Converters/BrushConverters.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#nullable enable annotations
using EmoTracker.Core;
using EmoTracker.Data.Locations;
using EmoTracker.Data.Scripting;
using EmoTracker.Data.Settings;
using System;
using System.Collections.Generic;
Expand Down Expand Up @@ -98,6 +99,32 @@ public object ConvertBack(object value, Type targetType, object parameter, Cultu
=> throw new NotSupportedException();
}

/// <summary>
/// Converts a <see cref="NotificationType"/> to its matching accent <see cref="IBrush"/>
/// for the notification left-border stripe. Colors are resolved live from
/// <see cref="ApplicationColors.Instance"/> so user configuration is respected.
/// </summary>
public class NotificationTypeToBrushConverter : Singleton<NotificationTypeToBrushConverter>, IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var c = ApplicationColors.Instance;
string colorStr = value is NotificationType t ? t switch
{
NotificationType.Celebration => c.Notification_Celebration,
NotificationType.Warning => c.Notification_Warning,
NotificationType.Error => c.Notification_Error,
_ => c.Notification_Message,
} : c.Notification_Message;

try { return Brush.Parse(colorStr); }
catch { return Brush.Parse(c.Notification_Message); }
}

public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
=> throw new NotSupportedException();
}

/// <summary>
/// Multi-value converter for the Package Manager button foreground color.
/// Replicates WPF DataTrigger priority: !AnyPackagesInstalled → Active,
Expand Down
10 changes: 10 additions & 0 deletions EmoTracker/ApplicationModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1281,6 +1281,15 @@ private void NotificationExpirationTimer_Tick(object sender, EventArgs e)
}
}

private void OnNotificationForceExpired(object sender, EventArgs e)
{
if (sender is Notification n)
{
n.ForceExpired -= OnNotificationForceExpired;
mNotifications.Remove(n);
}
}

private void Notifications_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
NotifyPropertyChanged("HasPendingNotifications");
Expand All @@ -1305,6 +1314,7 @@ public void PushMarkdownNotification(NotificationType type, string markdown, int
mPreviousNotifications.RemoveAt(9);
}

notification.ForceExpired += OnNotificationForceExpired;
mPreviousNotifications.Insert(0, notification);
mNotifications.Insert(0, notification);
});
Expand Down
1 change: 1 addition & 0 deletions EmoTracker/Extensions/McpServer/McpServerExtension.cs
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ private async Task StartServerAsync()
.WithTools<Tools.ExtensionTools>()
.WithTools<Tools.PackageTools>()
.WithTools<Tools.ImageCacheTools>()
.WithTools<Tools.NotificationTools>()
.WithHttpTransport();

mApp = builder.Build();
Expand Down
38 changes: 38 additions & 0 deletions EmoTracker/Extensions/McpServer/Tools/NotificationTools.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
using Avalonia.Threading;
using EmoTracker.Data.Scripting;
using ModelContextProtocol.Server;
using System;
using System.ComponentModel;
using System.Text.Json;
using System.Threading.Tasks;

namespace EmoTracker.Extensions.McpServer.Tools
{
[McpServerToolType]
public class NotificationTools
{
[McpServerTool(Name = "push_notification")]
[Description("Push a markdown notification to the tracker UI. Type must be one of: Message, Celebration, Warning, Error. Timeout is in milliseconds (-1 = 10s default, 0 = never expires).")]
public static async Task<string> PushNotification(
[Description("Notification type: Message, Celebration, Warning, or Error")] string type,
[Description("Markdown content for the notification")] string markdown,
[Description("Timeout in milliseconds (-1 for default 10 seconds, 0 for no expiry)")] int timeout = -1)
{
return await Dispatcher.UIThread.InvokeAsync(() =>
{
try
{
if (!Enum.TryParse<NotificationType>(type, ignoreCase: true, out var notifType))
return JsonSerializer.Serialize(new { success = false, error = $"Invalid type '{type}'. Must be: Message, Celebration, Warning, or Error." });

ApplicationModel.Instance.PushMarkdownNotification(notifType, markdown, timeout);
return JsonSerializer.Serialize(new { success = true, type = notifType.ToString(), markdown });
}
catch (Exception ex)
{
return JsonSerializer.Serialize(new { success = false, error = ex.Message });
}
});
}
}
}
27 changes: 19 additions & 8 deletions EmoTracker/MainWindow.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -51,15 +51,26 @@
Margin="20" VerticalAlignment="Top">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border Background="#d8212121" BorderThickness="8,0,0,0"
BorderBrush="#41a054" Margin="0,0,0,10">
<Grid Background="Transparent">
<Button Command="{Binding ForceExpireCommand}"
Padding="0" Background="Transparent" BorderThickness="0"
HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch">
<Button.Styles>
<Style Selector="Button:pointerover /template/ ContentPresenter">
<Setter Property="Background" Value="Transparent"/>
</Style>
<Style Selector="Button:pressed /template/ ContentPresenter">
<Setter Property="Background" Value="Transparent"/>
</Style>
</Button.Styles>
<Border Background="#d8212121" BorderThickness="8,0,0,0"
BorderBrush="{Binding Type, Converter={x:Static converters:NotificationTypeToBrushConverter.Instance}}"
Margin="0,0,0,10">
<Border.Effect>
<DropShadowDirectionEffect BlurRadius="15" ShadowDepth="0" Opacity="0.8" Color="Black"/>
</Border.Effect>
<controls:MarkdownViewer Markdown="{Binding Markdown}" Margin="10,7"/>
<Button Background="Transparent" BorderThickness="0"
Command="{Binding ForceExpireCommand}"
Opacity="0" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
</Grid>
</Border>
</Border>
</Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Expand Down
4 changes: 4 additions & 0 deletions EmoTracker/Notifications/Notification.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,13 @@ protected Notification(int timeout)

mForceExpireCommand = new DelegateCommand(ForceExpireNotification);
}
public event EventHandler ForceExpired;

private void ForceExpireNotification(object obj)
{
ExpirationTime = DateTime.Now;
Expired = true;
ForceExpired?.Invoke(this, EventArgs.Empty);
}
}
}
Loading