dragon-iso/src/TeamsISO.App/AboutWindow.xaml.cs

215 lines
7.4 KiB
C#
Raw Normal View History

feat: app icon, FPS, drops counter, --version, About dialog, Stop Teams toggle Six related polish items, all building on tonight's groundwork. 1. App icon: teamsiso.ico generated from dragon-mark.png at 7 sizes (16-256), wired as ApplicationIcon in the WPF csproj, MainWindow.Icon, AboutWindow.Icon, and ARPPRODUCTICON in the WiX MSI. Taskbar / window / Add-Remove-Programs all show the dragon mark now. 2. Running incoming FPS: ring buffer of last 30 frame timestamps in IsoPipeline; ComputeFps() returns moving-average rate. Surfaced on IsoHealthStats.IncomingFps and shown in the Source column of the participants DataGrid as 'WxH · 59.94 fps'. Resets cleanly on every supervisor restart. 3. Drops counter: FrameProcessor.Stats already aggregated FramesDropped (closest-frame strategy when the receiver outpaces the processor) and FramesDuplicated; just plumbed _liveProcessor through IsoPipeline so GetStats() can read them. Exposed in the Live column under the in/out counters as a coral-tinted 'drop N'. 4. Console --version flag: prints engine version (with embedded git SHA), .NET version, OS, NDI runtime banner, expected prefix, exit-code legend, plus a wilddragon.net link. Useful for support tickets. 5. About dialog: chromeless modal with the dragon mark + version / .NET / OS / NDI runtime fields and a link to wilddragon.net. Triggered by clicking the rail logo. 6. Teams launcher Stop toggle: TeamsLauncher gains IsRunning() and StopAll(). The rail's Teams button now toggles — if Teams is up, ask to close all Teams windows via WM_CLOSE; otherwise launch as before. Confirms before stopping so we don't kill the user's call mid-transition. Tests: 74/74 unit + 9/9 NDI integration green throughout. MSI builds clean and now embeds the dragon icon for ARP.
2026-05-08 13:50:19 -04:00
using System.Diagnostics;
using System.IO;
feat: app icon, FPS, drops counter, --version, About dialog, Stop Teams toggle Six related polish items, all building on tonight's groundwork. 1. App icon: teamsiso.ico generated from dragon-mark.png at 7 sizes (16-256), wired as ApplicationIcon in the WPF csproj, MainWindow.Icon, AboutWindow.Icon, and ARPPRODUCTICON in the WiX MSI. Taskbar / window / Add-Remove-Programs all show the dragon mark now. 2. Running incoming FPS: ring buffer of last 30 frame timestamps in IsoPipeline; ComputeFps() returns moving-average rate. Surfaced on IsoHealthStats.IncomingFps and shown in the Source column of the participants DataGrid as 'WxH · 59.94 fps'. Resets cleanly on every supervisor restart. 3. Drops counter: FrameProcessor.Stats already aggregated FramesDropped (closest-frame strategy when the receiver outpaces the processor) and FramesDuplicated; just plumbed _liveProcessor through IsoPipeline so GetStats() can read them. Exposed in the Live column under the in/out counters as a coral-tinted 'drop N'. 4. Console --version flag: prints engine version (with embedded git SHA), .NET version, OS, NDI runtime banner, expected prefix, exit-code legend, plus a wilddragon.net link. Useful for support tickets. 5. About dialog: chromeless modal with the dragon mark + version / .NET / OS / NDI runtime fields and a link to wilddragon.net. Triggered by clicking the rail logo. 6. Teams launcher Stop toggle: TeamsLauncher gains IsRunning() and StopAll(). The rail's Teams button now toggles — if Teams is up, ask to close all Teams windows via WM_CLOSE; otherwise launch as before. Confirms before stopping so we don't kill the user's call mid-transition. Tests: 74/74 unit + 9/9 NDI integration green throughout. MSI builds clean and now embeds the dragon icon for ARP.
2026-05-08 13:50:19 -04:00
using System.Reflection;
using System.Runtime.Versioning;
using System.Windows;
using System.Windows.Navigation;
using TeamsISO.App.Services;
feat: app icon, FPS, drops counter, --version, About dialog, Stop Teams toggle Six related polish items, all building on tonight's groundwork. 1. App icon: teamsiso.ico generated from dragon-mark.png at 7 sizes (16-256), wired as ApplicationIcon in the WPF csproj, MainWindow.Icon, AboutWindow.Icon, and ARPPRODUCTICON in the WiX MSI. Taskbar / window / Add-Remove-Programs all show the dragon mark now. 2. Running incoming FPS: ring buffer of last 30 frame timestamps in IsoPipeline; ComputeFps() returns moving-average rate. Surfaced on IsoHealthStats.IncomingFps and shown in the Source column of the participants DataGrid as 'WxH · 59.94 fps'. Resets cleanly on every supervisor restart. 3. Drops counter: FrameProcessor.Stats already aggregated FramesDropped (closest-frame strategy when the receiver outpaces the processor) and FramesDuplicated; just plumbed _liveProcessor through IsoPipeline so GetStats() can read them. Exposed in the Live column under the in/out counters as a coral-tinted 'drop N'. 4. Console --version flag: prints engine version (with embedded git SHA), .NET version, OS, NDI runtime banner, expected prefix, exit-code legend, plus a wilddragon.net link. Useful for support tickets. 5. About dialog: chromeless modal with the dragon mark + version / .NET / OS / NDI runtime fields and a link to wilddragon.net. Triggered by clicking the rail logo. 6. Teams launcher Stop toggle: TeamsLauncher gains IsRunning() and StopAll(). The rail's Teams button now toggles — if Teams is up, ask to close all Teams windows via WM_CLOSE; otherwise launch as before. Confirms before stopping so we don't kill the user's call mid-transition. Tests: 74/74 unit + 9/9 NDI integration green throughout. MSI builds clean and now embeds the dragon icon for ARP.
2026-05-08 13:50:19 -04:00
using TeamsISO.Engine.NdiInterop;
namespace TeamsISO.App;
/// <summary>
/// Modal "About" dialog. Surfaces enough info that a user filing a support ticket
/// can paste version + NDI runtime + OS in a single screenshot.
/// </summary>
public partial class AboutWindow : Window
{
public AboutWindow()
{
InitializeComponent();
PopulateText();
}
private void PopulateText()
{
var asm = typeof(App).Assembly;
var info = asm.GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.InformationalVersion
?? asm.GetName().Version?.ToString()
?? "unknown";
VersionText.Text = info;
RuntimeText.Text = $".NET {Environment.Version}";
OsText.Text = Environment.OSVersion.ToString();
NdiText.Text = TryGetNdiVersion();
}
[SupportedOSPlatform("windows")]
private static string TryGetNdiVersion()
{
try
{
using var interop = new NdiInteropPInvoke(
Microsoft.Extensions.Logging.Abstractions.NullLogger<NdiInteropPInvoke>.Instance);
return interop.GetRuntimeVersion();
}
catch (Exception ex)
{
return $"not initialized ({ex.Message})";
}
}
private void OnClose(object sender, RoutedEventArgs e) => Close();
/// <summary>
/// Re-open the first-launch welcome dialog from About so users can revisit
/// the setup checklist without having to delete the suppression flag file
/// by hand. The "Don't show again" checkbox in the welcome dialog defaults
/// to checked so a re-shown welcome won't unset the suppression on close.
/// </summary>
private void OnShowOnboarding(object sender, RoutedEventArgs e)
{
var onboarding = new OnboardingWindow { Owner = this };
onboarding.ShowDialog();
}
/// <summary>
/// Quick-jump: open a path in Explorer. Creates the directory if missing
/// (operator might click "Recordings" before any have been made). Best-
/// effort — Explorer launch failures don't surface a dialog.
/// </summary>
private static void OpenInExplorer(string path)
{
try
{
if (!Directory.Exists(path)) Directory.CreateDirectory(path);
Process.Start(new ProcessStartInfo
{
FileName = path,
UseShellExecute = true,
});
}
catch
{
// No-op: shell launch failed (path inaccessible / Explorer crashed)
}
}
private void OnOpenLogs(object sender, RoutedEventArgs e) =>
OpenInExplorer(Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
"TeamsISO", "Logs"));
// OnOpenRecordings removed — recording feature axed.
private void OnOpenNotes(object sender, RoutedEventArgs e) =>
OpenInExplorer(Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
"TeamsISO", "Notes"));
/// <summary>
/// Build the diagnostic bundle and tell the operator where it landed. The
/// bundle is just zipped logs / config / presets — no screenshots, no
/// memory dumps. Intended to be attached to a bug report.
/// </summary>
private void OnExportDiagnostics(object sender, RoutedEventArgs e)
{
try
{
var path = DiagnosticsBundle.Export();
var open = MessageBox.Show(
this,
$"Diagnostic bundle written to:\n\n{path}\n\nOpen the containing folder?",
"TeamsISO — Diagnostics exported",
MessageBoxButton.YesNo,
MessageBoxImage.Information);
if (open == MessageBoxResult.Yes)
{
try
{
Process.Start(new ProcessStartInfo
{
FileName = "explorer.exe",
Arguments = $"/select,\"{path}\"",
UseShellExecute = true,
});
}
catch { /* shell launch failure is best-effort */ }
}
}
catch (Exception ex)
{
MessageBox.Show(
this,
$"Diagnostic export failed.\n\n{ex.Message}",
"TeamsISO — Diagnostic export",
MessageBoxButton.OK,
MessageBoxImage.Warning);
}
}
/// <summary>
/// Click handler for "Check for updates". Disables the button while the
/// HTTP call is in flight (so a second click doesn't spawn parallel
/// requests), then surfaces the result via MessageBox. On
/// <see cref="UpdateChecker.UpdateStatus.UpdateAvailable"/> we offer
/// to open the releases page so the operator can grab the new MSI.
/// </summary>
private async void OnCheckUpdate(object sender, RoutedEventArgs e)
{
UpdateButton.IsEnabled = false;
try
{
var result = await UpdateChecker.CheckAsync();
switch (result.Status)
{
case UpdateChecker.UpdateStatus.UpdateAvailable:
var open = MessageBox.Show(
this,
$"{result.Message}\n\n" +
$"You're on {result.CurrentVersion}; latest is {result.LatestTag}.\n\n" +
"Open the releases page to download the new MSI?",
"TeamsISO — Update available",
MessageBoxButton.YesNo,
MessageBoxImage.Information);
if (open == MessageBoxResult.Yes)
UpdateChecker.OpenReleasesPage();
break;
case UpdateChecker.UpdateStatus.UpToDate:
MessageBox.Show(
this,
result.Message ?? "You're on the latest release.",
"TeamsISO — Up to date",
MessageBoxButton.OK,
MessageBoxImage.Information);
break;
case UpdateChecker.UpdateStatus.Error:
default:
MessageBox.Show(
this,
$"Couldn't check for updates.\n\n{result.Message}",
"TeamsISO — Update check failed",
MessageBoxButton.OK,
MessageBoxImage.Warning);
break;
}
}
finally
{
UpdateButton.IsEnabled = true;
}
}
feat: app icon, FPS, drops counter, --version, About dialog, Stop Teams toggle Six related polish items, all building on tonight's groundwork. 1. App icon: teamsiso.ico generated from dragon-mark.png at 7 sizes (16-256), wired as ApplicationIcon in the WPF csproj, MainWindow.Icon, AboutWindow.Icon, and ARPPRODUCTICON in the WiX MSI. Taskbar / window / Add-Remove-Programs all show the dragon mark now. 2. Running incoming FPS: ring buffer of last 30 frame timestamps in IsoPipeline; ComputeFps() returns moving-average rate. Surfaced on IsoHealthStats.IncomingFps and shown in the Source column of the participants DataGrid as 'WxH · 59.94 fps'. Resets cleanly on every supervisor restart. 3. Drops counter: FrameProcessor.Stats already aggregated FramesDropped (closest-frame strategy when the receiver outpaces the processor) and FramesDuplicated; just plumbed _liveProcessor through IsoPipeline so GetStats() can read them. Exposed in the Live column under the in/out counters as a coral-tinted 'drop N'. 4. Console --version flag: prints engine version (with embedded git SHA), .NET version, OS, NDI runtime banner, expected prefix, exit-code legend, plus a wilddragon.net link. Useful for support tickets. 5. About dialog: chromeless modal with the dragon mark + version / .NET / OS / NDI runtime fields and a link to wilddragon.net. Triggered by clicking the rail logo. 6. Teams launcher Stop toggle: TeamsLauncher gains IsRunning() and StopAll(). The rail's Teams button now toggles — if Teams is up, ask to close all Teams windows via WM_CLOSE; otherwise launch as before. Confirms before stopping so we don't kill the user's call mid-transition. Tests: 74/74 unit + 9/9 NDI integration green throughout. MSI builds clean and now embeds the dragon icon for ARP.
2026-05-08 13:50:19 -04:00
/// <summary>
/// Open the company site in the default browser. We intentionally use the
/// shell's URL handler rather than a tab inside the app — this is a
/// "tell me more" link, not a workflow.
/// </summary>
private void OnWebsiteClick(object sender, RoutedEventArgs e)
{
try
{
Process.Start(new ProcessStartInfo
{
FileName = "https://wilddragon.net",
UseShellExecute = true,
});
}
catch
{
// best-effort; if shell launch fails the click is a no-op
}
}
}