@@ -44,7 +65,13 @@
@if (IsDisabled && !IsLivePresent)
{
- This log is not on this computer. Use Open from folder to analyze an exported copy.
+ One or more of these logs are not on this computer. Use Open from folder to analyze an exported copy.
+
+ }
+ else if (IsDisabled)
+ {
+
+ Live access to one or more of these logs is blocked. Use Open from folder to analyze an exported copy.
}
@@ -53,7 +80,7 @@
CssClass="scenario-detail__star"
IconClass="@(IsFavored ? "bi bi-star-fill" : "bi bi-star")"
OnClick="OnToggleFavorite" />
- ChannelReadiness { get; set; } = [];
+
[Parameter] public bool IsBusy { get; set; }
[Parameter] public bool IsDisabled { get; set; }
@@ -29,8 +31,25 @@ public sealed partial class ScenarioDetail
[Parameter] public EventCallback OnToggleFavorite { get; set; }
+ [Parameter] public IReadOnlyList OptionalChannelReadiness { get; set; } = [];
+
[Parameter][EditorRequired] public ScenarioDefinition Scenario { get; set; } = null!;
+ private IReadOnlyList DisplayOptionalReadiness =>
+ OptionalChannelReadiness.Count > 0 ? OptionalChannelReadiness :
+ Scenario.OptionalChannels.IsDefaultOrEmpty ? [] :
+ [
+ .. Scenario.OptionalChannels.Select(channel =>
+ new ChannelReadiness(channel, ChannelPresence.Unknown, ChannelEnablement.Unknown))
+ ];
+
+ private IReadOnlyList DisplayReadiness =>
+ ChannelReadiness.Count > 0 ? ChannelReadiness :
+ [
+ .. Scenario.Channels.Select(channel =>
+ new ChannelReadiness(channel, ChannelPresence.Unknown, ChannelEnablement.Unknown))
+ ];
+
private IReadOnlyList FilterLines
{
get
@@ -50,6 +69,20 @@ private IReadOnlyList FilterLines
}
}
+ private static string EnablementLabel(ChannelEnablement enablement) => enablement switch
+ {
+ ChannelEnablement.Enabled => "Enabled",
+ ChannelEnablement.Disabled => "Disabled",
+ _ => "Enablement unknown"
+ };
+
+ private static string PresenceLabel(ChannelPresence presence) => presence switch
+ {
+ ChannelPresence.Present => "Present",
+ ChannelPresence.Absent => "Not present",
+ _ => "Presence unknown"
+ };
+
private async Task LaunchAsync()
{
if (IsDisabled) { return; }
@@ -57,8 +90,6 @@ private async Task LaunchAsync()
await OnLaunch.InvokeAsync();
}
- // Opening exported files from a folder needs no elevation, so this stays available even when the live Launch is
- // admin-gated (IsDisabled); only an in-flight operation (IsBusy) blocks it.
private async Task LaunchFromFolderAsync()
{
if (IsBusy) { return; }
diff --git a/src/EventLogExpert.UI/Dashboard/ScenarioDetail.razor.css b/src/EventLogExpert.UI/Dashboard/ScenarioDetail.razor.css
index 70025a46..715a800c 100644
--- a/src/EventLogExpert.UI/Dashboard/ScenarioDetail.razor.css
+++ b/src/EventLogExpert.UI/Dashboard/ScenarioDetail.razor.css
@@ -77,6 +77,27 @@
color: color-mix(in srgb, var(--text-secondary) 78%, transparent);
}
+.scenario-detail__channel-readiness {
+ display: flex;
+ flex-wrap: wrap;
+ align-items: center;
+ gap: 0.4rem;
+}
+
+.scenario-detail__channel-readiness--optional {
+ opacity: 0.85;
+}
+
+.scenario-detail__status {
+ display: inline-flex;
+ align-items: center;
+ padding: 0.1rem 0.45rem;
+ border: 1px solid var(--border-divider);
+ border-radius: 999px;
+ font-size: 0.78rem;
+ color: var(--text-secondary);
+}
+
.scenario-detail__admin-badge {
display: inline-flex;
align-items: center;
diff --git a/src/EventLogExpert.UI/Menu/MenuBar.razor.cs b/src/EventLogExpert.UI/Menu/MenuBar.razor.cs
index e4a813e4..81957bb8 100644
--- a/src/EventLogExpert.UI/Menu/MenuBar.razor.cs
+++ b/src/EventLogExpert.UI/Menu/MenuBar.razor.cs
@@ -2,15 +2,16 @@
// // Licensed under the MIT License.
using EventLogExpert.Eventing.Common.Channels;
+using EventLogExpert.Eventing.Readers;
using EventLogExpert.Runtime.Alerts;
using EventLogExpert.Runtime.Common.Clipboard;
-using EventLogExpert.Runtime.Common.Versioning;
using EventLogExpert.Runtime.EventLog;
using EventLogExpert.Runtime.Export;
using EventLogExpert.Runtime.FilterPane;
using EventLogExpert.Runtime.Histogram;
using EventLogExpert.Runtime.LogTable;
using EventLogExpert.Runtime.Menu;
+using EventLogExpert.Runtime.Scenarios;
using EventLogExpert.Runtime.Settings;
using EventLogExpert.UI.Common;
using EventLogExpert.UI.Common.Interop;
@@ -34,6 +35,8 @@ public sealed partial class MenuBar
private IJSObjectReference? _menuAnchorModule;
private ElementReference _menuBarRootRef;
private long _openRequestId;
+ private IReadOnlyDictionary _readinessByChannel =
+ new Dictionary(StringComparer.OrdinalIgnoreCase);
private IJSObjectReference? _scrollSuppressorModule;
[Inject] private IMenuActionService Actions { get; init; } = null!;
@@ -42,11 +45,11 @@ public sealed partial class MenuBar
[Inject] private IAlertDialogService AlertDialogService { get; init; } = null!;
+ [Inject] private IChannelReadinessService ChannelReadinessService { get; init; } = null!;
+
[Inject]
private IStateSelection ContinuouslyUpdate { get; init; } = null!;
- [Inject] private ICurrentVersionProvider CurrentVersionProvider { get; init; } = null!;
-
[Inject]
private IStateSelection FilterPaneIsEnabled { get; init; } = null!;
@@ -134,6 +137,11 @@ protected override void OnInitialized()
base.OnInitialized();
}
+ private static string? ReadinessStatusText(ChannelReadiness readiness) =>
+ readiness.Access == ChannelAccess.RequiresElevation
+ ? "(elevate)"
+ : readiness.Enablement == ChannelEnablement.Disabled ? "(disabled)" : null;
+
private IReadOnlyList