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
1 change: 1 addition & 0 deletions src/OpenClaw.Tray.WinUI/Helpers/FluentIconCatalog.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ public static class FluentIconCatalog
public const string Settings = "\uE713"; // Settings
public const string Setup = "\uE825"; // Bank — Reconfigure / Setup wizard launcher
public const string About = "\uE946"; // Info
public const string Notifications = "\uE7E7"; // Ringer — title-bar notifications bell
public const string Exit = "\uE711"; // Cancel (X) — used for "Close" menu item
public const string Add = "\uE710"; // Add — "+ Add gateway" header button
public const string Back = "\uE72B"; // Back — leading chevron on Back hyperlink
Expand Down
83 changes: 1 addition & 82 deletions src/OpenClaw.Tray.WinUI/Pages/NotificationsPage.xaml.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using OpenClawTray.Helpers;
using OpenClawTray.Services;
using System;
using OpenClawTray.ViewModels;
using System.Collections.ObjectModel;
using System.Globalization;
using System.Linq;

namespace OpenClawTray.Pages;

Expand Down Expand Up @@ -98,82 +95,4 @@ private void OnNotificationActionClick(object sender, RoutedEventArgs e)
((IAppCommands)CurrentApp).Navigate(item.ActionRoute);
_notificationService?.Dismiss(item.Id);
}

private sealed record NotificationItemViewModel(
string Id,
string SeverityGlyph,
string Title,
string Message,
string Metadata,
string? ActionLabel,
string? ActionRoute,
Visibility ActionVisibility,
string OccurrenceText,
Visibility OccurrenceVisibility,
string DismissAutomationName)
{
public static NotificationItemViewModel From(AppNotification notification)
{
var metadata = new[]
{
LocalizeMetadataValue(notification.Source),
LocalizeMetadataValue(notification.Category),
notification.CreatedAt.ToLocalTime().ToString("g", CultureInfo.CurrentCulture)
}
.Where(value => !string.IsNullOrWhiteSpace(value));

var occurrenceText = LocalizationHelper.Format(
"AppNotification_RepeatedBadgeFormat",
notification.OccurrenceCount);

return new(
notification.Id,
ToSeverityGlyph(notification.Severity),
notification.Title,
notification.Message,
string.Join(" - ", metadata),
notification.ActionLabel,
notification.ActionRoute,
!string.IsNullOrWhiteSpace(notification.ActionLabel) &&
!string.IsNullOrWhiteSpace(notification.ActionRoute)
? Visibility.Visible
: Visibility.Collapsed,
occurrenceText,
notification.OccurrenceCount > 1 ? Visibility.Visible : Visibility.Collapsed,
LocalizationHelper.Format("NotificationsPage_DismissAutomationNameFormat", notification.Title));
}

private static string ToSeverityGlyph(AppNotificationSeverity severity) => severity switch
{
AppNotificationSeverity.Success => "\uE930",
AppNotificationSeverity.Warning => "\uE7BA",
AppNotificationSeverity.Error => "\uE783",
_ => "\uE946"
};

private static string? LocalizeMetadataValue(string? value) => value switch
{
null or "" => null,
"authentication" => LocalizationHelper.GetString("NotificationsPage_MetadataAuthentication"),
"bindings" => LocalizationHelper.GetString("NotificationsPage_MetadataBindings"),
"channels" => LocalizationHelper.GetString("NotificationsPage_MetadataChannels"),
"config" => LocalizationHelper.GetString("NotificationsPage_MetadataConfig"),
"connection" => LocalizationHelper.GetString("NotificationsPage_MetadataConnection"),
"cron" => LocalizationHelper.GetString("NotificationsPage_MetadataCron"),
"exec-approval" => LocalizationHelper.GetString("NotificationsPage_MetadataSourceExecApproval"),
"gateway" => LocalizationHelper.GetString("NotificationsPage_MetadataGateway"),
"jobs" => LocalizationHelper.GetString("NotificationsPage_MetadataJobs"),
"load" => LocalizationHelper.GetString("NotificationsPage_MetadataLoad"),
"lifecycle" => LocalizationHelper.GetString("NotificationsPage_MetadataLifecycle"),
"local-gateway" => LocalizationHelper.GetString("NotificationsPage_MetadataLocalGateway"),
"node.invoke" => LocalizationHelper.GetString("NotificationsPage_MetadataCategoryNodeInvoke"),
"node" => LocalizationHelper.GetString("NotificationsPage_MetadataNode"),
"pairing" => LocalizationHelper.GetString("NotificationsPage_MetadataPairing"),
"sandbox" => LocalizationHelper.GetString("NotificationsPage_MetadataSandbox"),
"settings" => LocalizationHelper.GetString("NotificationsPage_MetadataSettings"),
"status" => LocalizationHelper.GetString("NotificationsPage_MetadataStatus"),
"system.run" => LocalizationHelper.GetString("NotificationsPage_MetadataSystemRun"),
_ => value
};
}
}
74 changes: 74 additions & 0 deletions src/OpenClaw.Tray.WinUI/Services/ConnectionStatusPresenter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
using OpenClaw.Connection;

namespace OpenClawTray.Services;

internal enum ConnectionStatusAccent
{
Neutral,
Success,
Caution,
Critical,
}

internal static class ConnectionStatusPresenter
{
public static (string LabelKey, ConnectionStatusAccent Accent) Pill(OverallConnectionState overall) => overall switch
{
OverallConnectionState.Connected or OverallConnectionState.Ready =>
("StatusDisplay_Connected", ConnectionStatusAccent.Success),
OverallConnectionState.Connecting =>
("StatusDisplay_Connecting", ConnectionStatusAccent.Caution),
OverallConnectionState.Degraded =>
("HubWindow_Pill_Degraded", ConnectionStatusAccent.Caution),
OverallConnectionState.PairingRequired =>
("HubWindow_Pill_PairingRequired", ConnectionStatusAccent.Caution),
OverallConnectionState.Error =>
("StatusDisplay_Error", ConnectionStatusAccent.Critical),
_ => ("StatusDisplay_Disconnected", ConnectionStatusAccent.Neutral),
};

public static ConnectionStatusAccent RoleAccent(RoleConnectionState state) => state switch
{
RoleConnectionState.Connected => ConnectionStatusAccent.Success,
RoleConnectionState.Connecting => ConnectionStatusAccent.Caution,
RoleConnectionState.PairingRequired => ConnectionStatusAccent.Caution,
RoleConnectionState.Error or
RoleConnectionState.PairingRejected or
RoleConnectionState.RateLimited => ConnectionStatusAccent.Critical,
_ => ConnectionStatusAccent.Neutral,
};

public static string RoleStateLabelKey(RoleConnectionState state) => state switch
{
RoleConnectionState.Connected => "StatusDisplay_Connected",
RoleConnectionState.Connecting => "StatusDisplay_Connecting",
RoleConnectionState.PairingRequired => "HubWindow_Role_PairingRequired",
RoleConnectionState.PairingRejected => "HubWindow_Role_PairingRejected",
RoleConnectionState.RateLimited => "HubWindow_Role_RateLimited",
RoleConnectionState.Error => "StatusDisplay_Error",
RoleConnectionState.Disabled => "HubWindow_Role_Disabled",
_ => "HubWindow_Role_Off",
};

public static (string LabelKey, ConnectionStatusAccent Accent) NodeRow(
GatewayConnectionSnapshot snapshot, bool nodeModeEnabled, int enabledCapabilityCount)
{
if (!nodeModeEnabled || snapshot.OperatorState != RoleConnectionState.Connected)
return ("HubWindow_Role_Disabled", ConnectionStatusAccent.Neutral);

return snapshot.NodeState switch
{
RoleConnectionState.PairingRequired => (RoleStateLabelKey(snapshot.NodeState), ConnectionStatusAccent.Caution),
RoleConnectionState.PairingRejected or
RoleConnectionState.RateLimited or
RoleConnectionState.Error => (RoleStateLabelKey(snapshot.NodeState), ConnectionStatusAccent.Critical),
_ when enabledCapabilityCount == 0 => ("HubWindow_Role_PermissionsIncomplete", ConnectionStatusAccent.Caution),
_ => (RoleStateLabelKey(snapshot.NodeState), RoleAccent(snapshot.NodeState)),
};
}

public static bool NodeNeedsApproval(GatewayConnectionSnapshot snapshot, bool nodeModeEnabled) =>
nodeModeEnabled
&& snapshot.OperatorState == RoleConnectionState.Connected
&& snapshot.NodeState == RoleConnectionState.PairingRequired;
}
78 changes: 66 additions & 12 deletions src/OpenClaw.Tray.WinUI/Strings/en-us/Resources.resw
Original file line number Diff line number Diff line change
Expand Up @@ -2421,9 +2421,6 @@ On your gateway host (Mac/Linux), run:
<data name="HubWindow_NavigationViewItem_136.Content" xml:space="preserve">
<value>Permissions</value>
</data>
<data name="HubWindow_NavigationViewItem_Notifications.Content" xml:space="preserve">
<value>Notifications</value>
</data>
<data name="HubWindow_NavigationViewItem_145.Content" xml:space="preserve">
<value>Diagnostics</value>
</data>
Expand Down Expand Up @@ -5029,12 +5026,6 @@ Commands are blocked while sandboxing is unavailable because strict fallback blo
<data name="HubWindow_Disconnected.Text" xml:space="preserve">
<value>Disconnected</value>
</data>
<data name="HubWindow_Op.Text" xml:space="preserve">
<value>Op</value>
</data>
<data name="HubWindow_Node.Text" xml:space="preserve">
<value>Node</value>
</data>
<data name="HubWindow_Advanced.Content" xml:space="preserve">
<value>Advanced</value>
</data>
Expand Down Expand Up @@ -5065,9 +5056,6 @@ Commands are blocked while sandboxing is unavailable because strict fallback blo
<data name="VoiceSettingsPage_PreviewVoice.Text" xml:space="preserve">
<value>Preview Voice</value>
</data>
<data name="HubWindow_TitleStatus.Text" xml:space="preserve">
<value>Disconnected</value>
</data>
<data name="SandboxPage_GatewayCommandsNote.Text" xml:space="preserve">
<value>Commands the agent runs directly on the gateway aren't. If the gateway is on this PC they may still be able to reach your Windows files, clipboard, and network — check your gateway's configuration.</value>
</data> <data name="AgentEventsPage_AllAgents" xml:space="preserve">
Expand Down Expand Up @@ -6706,4 +6694,70 @@ Make sure the gateway is running.</value>
<data name="ConnectionPage_RemoteSetupHelpAuthTitle.Text" xml:space="preserve">
<value>Auth</value>
</data>
<data name="HubWindow_StatusFlyout_Title.Text" xml:space="preserve">
<value>Connection</value>
</data>
<data name="HubWindow_StatusFlyout_OpenConnection.Content" xml:space="preserve">
<value>Open Connection</value>
</data>
<data name="HubWindow_StatusFlyout_GatewayLabel.Text" xml:space="preserve">
<value>Gateway</value>
</data>
<data name="HubWindow_StatusFlyout_Reconnect.Content" xml:space="preserve">
<value>Reconnect</value>
</data>
<data name="HubWindow_StatusFlyout_OperatorLabel.Text" xml:space="preserve">
<value>Operator</value>
</data>
<data name="HubWindow_StatusFlyout_NodeLabel.Text" xml:space="preserve">
<value>Node</value>
</data>
<data name="HubWindow_StatusFlyout_Approve.Content" xml:space="preserve">
<value>Approve…</value>
</data>
<data name="HubWindow_StatusPill_Tooltip" xml:space="preserve">
<value>Connection status</value>
</data>
<data name="HubWindow_Bell_Tooltip" xml:space="preserve">
<value>Notifications</value>
</data>
<data name="HubWindow_Pill_Ready" xml:space="preserve">
<value>Ready</value>
</data>
<data name="HubWindow_Pill_PairingRequired" xml:space="preserve">
<value>Pairing required</value>
</data>
<data name="HubWindow_Pill_Degraded" xml:space="preserve">
<value>Degraded</value>
</data>
<data name="HubWindow_Role_Off" xml:space="preserve">
<value>Off</value>
</data>
<data name="HubWindow_Role_PairingRequired" xml:space="preserve">
<value>Pairing required</value>
</data>
<data name="HubWindow_Role_PairingRejected" xml:space="preserve">
<value>Pairing rejected</value>
</data>
<data name="HubWindow_Role_RateLimited" xml:space="preserve">
<value>Rate limited</value>
</data>
<data name="HubWindow_Role_Disabled" xml:space="preserve">
<value>Disabled</value>
</data>
<data name="NotificationsFlyout_ClearAll.Content" xml:space="preserve">
<value>Clear all</value>
</data>
<data name="NotificationsFlyout_EmptyTitle.Text" xml:space="preserve">
<value>No notifications</value>
</data>
<data name="NotificationsFlyout_OpenPage.Content" xml:space="preserve">
<value>Open notifications</value>
</data>
<data name="NotificationsFlyout_ActiveCountFormat" xml:space="preserve">
<value>{0} active</value>
</data>
<data name="HubWindow_Role_PermissionsIncomplete" xml:space="preserve">
<value>Permissions incomplete</value>
</data>
</root>
78 changes: 66 additions & 12 deletions src/OpenClaw.Tray.WinUI/Strings/fr-fr/Resources.resw
Original file line number Diff line number Diff line change
Expand Up @@ -2376,9 +2376,6 @@ Sur votre hôte passerelle (Mac/Linux), exécutez :
<data name="HubWindow_NavigationViewItem_145.Content" xml:space="preserve">
<value>Débogage</value>
</data>
<data name="HubWindow_NavigationViewItem_Notifications.Content" xml:space="preserve">
<value>Alertes</value>
</data>
<data name="HubWindow_NavigationViewItem_148.Content" xml:space="preserve">
<value>Informations</value>
</data>
Expand Down Expand Up @@ -4981,12 +4978,6 @@ Les commandes sont bloquées tant que le sandboxing est indisponible, car le blo
<data name="HubWindow_Disconnected.Text" xml:space="preserve">
<value>Déconnecté</value>
</data>
<data name="HubWindow_Op.Text" xml:space="preserve">
<value>Op.</value>
</data>
<data name="HubWindow_Node.Text" xml:space="preserve">
<value>Nœud</value>
</data>
<data name="HubWindow_Advanced.Content" xml:space="preserve">
<value>Avancé</value>
</data>
Expand Down Expand Up @@ -5017,9 +5008,6 @@ Les commandes sont bloquées tant que le sandboxing est indisponible, car le blo
<data name="VoiceSettingsPage_PreviewVoice.Text" xml:space="preserve">
<value>Aperçu de la voix</value>
</data>
<data name="HubWindow_TitleStatus.Text" xml:space="preserve">
<value>Déconnecté</value>
</data>
<data name="SandboxPage_GatewayCommandsNote.Text" xml:space="preserve">
<value>Les commandes que l'agent exécute directement sur la passerelle ne le sont pas. Si la passerelle se trouve sur ce PC, elles peuvent toujours accéder à vos fichiers Windows, au presse-papiers et au réseau — vérifiez la configuration de votre passerelle.</value>
</data> <data name="AgentEventsPage_AllAgents" xml:space="preserve">
Expand Down Expand Up @@ -6658,4 +6646,70 @@ Assurez-vous que la passerelle est en cours d'exécution.</value>
<data name="ConnectionPage_RemoteSetupHelpAuthTitle.Text" xml:space="preserve">
<value>Authentification</value>
</data>
<data name="HubWindow_StatusFlyout_Title.Text" xml:space="preserve">
<value>Connection</value>
</data>
<data name="HubWindow_StatusFlyout_OpenConnection.Content" xml:space="preserve">
<value>Open Connection</value>
</data>
<data name="HubWindow_StatusFlyout_GatewayLabel.Text" xml:space="preserve">
<value>Gateway</value>
</data>
<data name="HubWindow_StatusFlyout_Reconnect.Content" xml:space="preserve">
<value>Reconnect</value>
</data>
<data name="HubWindow_StatusFlyout_OperatorLabel.Text" xml:space="preserve">
<value>Operator</value>
</data>
<data name="HubWindow_StatusFlyout_NodeLabel.Text" xml:space="preserve">
<value>Node</value>
</data>
<data name="HubWindow_StatusFlyout_Approve.Content" xml:space="preserve">
<value>Approve…</value>
</data>
<data name="HubWindow_StatusPill_Tooltip" xml:space="preserve">
<value>Connection status</value>
</data>
<data name="HubWindow_Bell_Tooltip" xml:space="preserve">
<value>Notifications</value>
</data>
<data name="HubWindow_Pill_Ready" xml:space="preserve">
<value>Ready</value>
</data>
<data name="HubWindow_Pill_PairingRequired" xml:space="preserve">
<value>Pairing required</value>
</data>
<data name="HubWindow_Pill_Degraded" xml:space="preserve">
<value>Degraded</value>
</data>
<data name="HubWindow_Role_Off" xml:space="preserve">
<value>Off</value>
</data>
<data name="HubWindow_Role_PairingRequired" xml:space="preserve">
<value>Pairing required</value>
</data>
<data name="HubWindow_Role_PairingRejected" xml:space="preserve">
<value>Pairing rejected</value>
</data>
<data name="HubWindow_Role_RateLimited" xml:space="preserve">
<value>Rate limited</value>
</data>
<data name="HubWindow_Role_Disabled" xml:space="preserve">
<value>Disabled</value>
</data>
<data name="NotificationsFlyout_ClearAll.Content" xml:space="preserve">
<value>Clear all</value>
</data>
<data name="NotificationsFlyout_EmptyTitle.Text" xml:space="preserve">
<value>No notifications</value>
</data>
<data name="NotificationsFlyout_OpenPage.Content" xml:space="preserve">
<value>Open notifications</value>
</data>
<data name="NotificationsFlyout_ActiveCountFormat" xml:space="preserve">
<value>{0} active</value>
</data>
<data name="HubWindow_Role_PermissionsIncomplete" xml:space="preserve">
<value>Permissions incomplete</value>
</data>
</root>
Loading
Loading