diff --git a/CHANGELOG.md b/CHANGELOG.md
index b1318a17..a5547c3d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning 2.0](https://semver.org/spec/v2
## v2.8.0-pre.3
### Added
- Added "Config" Shared Model Folder option for Fooocus
+- Added "Copy Details" button to Unexpected Error dialog
### Changed
- (Internal) Updated to Avalonia 11.0.7
- Changed the Close button on the package install dialog to "Hide"
diff --git a/StabilityMatrix.Avalonia/Languages/Resources.Designer.cs b/StabilityMatrix.Avalonia/Languages/Resources.Designer.cs
index 1d7ad737..132fdfbc 100644
--- a/StabilityMatrix.Avalonia/Languages/Resources.Designer.cs
+++ b/StabilityMatrix.Avalonia/Languages/Resources.Designer.cs
@@ -167,6 +167,15 @@ namespace StabilityMatrix.Avalonia.Languages {
}
}
+ ///
+ /// Looks up a localized string similar to Copy Details.
+ ///
+ public static string Action_CopyDetails {
+ get {
+ return ResourceManager.GetString("Action_CopyDetails", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Copy Trigger Words.
///
diff --git a/StabilityMatrix.Avalonia/Languages/Resources.resx b/StabilityMatrix.Avalonia/Languages/Resources.resx
index 8015e2d8..f731e8b0 100644
--- a/StabilityMatrix.Avalonia/Languages/Resources.resx
+++ b/StabilityMatrix.Avalonia/Languages/Resources.resx
@@ -939,4 +939,7 @@
Hide
+
+ Copy Details
+
diff --git a/StabilityMatrix.Avalonia/Program.cs b/StabilityMatrix.Avalonia/Program.cs
index 5e7d7336..e034c61c 100644
--- a/StabilityMatrix.Avalonia/Program.cs
+++ b/StabilityMatrix.Avalonia/Program.cs
@@ -347,11 +347,13 @@ public static class Program
if (e.ExceptionObject is not Exception ex)
return;
+ SentryId? sentryId = null;
+
// Exception automatically logged by Sentry if enabled
if (SentrySdk.IsEnabled)
{
ex.SetSentryMechanism("AppDomain.UnhandledException", handled: false);
- SentrySdk.CaptureException(ex);
+ sentryId = SentrySdk.CaptureException(ex);
SentrySdk.FlushAsync().SafeFireAndForget();
}
else
@@ -361,7 +363,10 @@ public static class Program
if (Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime lifetime)
{
- var dialog = new ExceptionDialog { DataContext = new ExceptionViewModel { Exception = ex } };
+ var dialog = new ExceptionDialog
+ {
+ DataContext = new ExceptionViewModel { Exception = ex, SentryId = sentryId }
+ };
var mainWindow = lifetime.MainWindow;
// We can only show dialog if main window exists, and is visible
diff --git a/StabilityMatrix.Avalonia/ViewModels/Dialogs/ExceptionViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/Dialogs/ExceptionViewModel.cs
index 42e8a6d6..9531c864 100644
--- a/StabilityMatrix.Avalonia/ViewModels/Dialogs/ExceptionViewModel.cs
+++ b/StabilityMatrix.Avalonia/ViewModels/Dialogs/ExceptionViewModel.cs
@@ -1,4 +1,5 @@
using System;
+using Sentry;
using StabilityMatrix.Avalonia.ViewModels.Base;
using StabilityMatrix.Avalonia.Views.Dialogs;
using StabilityMatrix.Core.Attributes;
@@ -12,7 +13,41 @@ public partial class ExceptionViewModel : ViewModelBase
{
public Exception? Exception { get; set; }
+ public SentryId? SentryId { get; set; }
+
public string? Message => Exception?.Message;
public string? ExceptionType => Exception?.GetType().Name ?? "";
+
+ public string? FormatAsMarkdown()
+ {
+ if (Exception is null)
+ {
+ return null;
+ }
+
+ var message = $"## Exception\n{ExceptionType}: {Message}\n";
+
+ if (SentryId is not null)
+ {
+ message += $"### Sentry ID\n```\n{SentryId}\n```\n";
+ }
+
+ if (Exception.StackTrace != null)
+ {
+ message += $"### Stack Trace\n```\n{Exception.StackTrace}\n```\n";
+ }
+
+ if (Exception.InnerException is { } innerException)
+ {
+ message += $"## Inner Exception\n{innerException.GetType().Name}: {innerException.Message}\n";
+
+ if (innerException.StackTrace != null)
+ {
+ message += $"### Stack Trace\n```\n{innerException.StackTrace}\n```\n";
+ }
+ }
+
+ return message;
+ }
}
diff --git a/StabilityMatrix.Avalonia/Views/Dialogs/ExceptionDialog.axaml b/StabilityMatrix.Avalonia/Views/Dialogs/ExceptionDialog.axaml
index 2612d4db..89bf3c97 100644
--- a/StabilityMatrix.Avalonia/Views/Dialogs/ExceptionDialog.axaml
+++ b/StabilityMatrix.Avalonia/Views/Dialogs/ExceptionDialog.axaml
@@ -6,6 +6,7 @@
xmlns:mocks="clr-namespace:StabilityMatrix.Avalonia.DesignData"
xmlns:controls="clr-namespace:StabilityMatrix.Avalonia.Controls"
xmlns:lang="clr-namespace:StabilityMatrix.Avalonia.Languages"
+ xmlns:fluentIcons="clr-namespace:FluentIcons.Avalonia.Fluent;assembly=FluentIcons.Avalonia.Fluent"
d:DataContext="{x:Static mocks:DesignData.ExceptionViewModel}"
x:DataType="dialogs:ExceptionViewModel"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="550"
@@ -73,16 +74,38 @@
-
-
+ HorizontalAlignment="Right"
+ Grid.Row="2">
+
+
+
+
+
+
+
diff --git a/StabilityMatrix.Avalonia/Views/Dialogs/ExceptionDialog.axaml.cs b/StabilityMatrix.Avalonia/Views/Dialogs/ExceptionDialog.axaml.cs
index b2bf44ad..67339d59 100644
--- a/StabilityMatrix.Avalonia/Views/Dialogs/ExceptionDialog.axaml.cs
+++ b/StabilityMatrix.Avalonia/Views/Dialogs/ExceptionDialog.axaml.cs
@@ -3,6 +3,7 @@ using Avalonia.Interactivity;
using Avalonia.Markup.Xaml;
using FluentAvalonia.UI.Windowing;
using StabilityMatrix.Avalonia.Controls;
+using StabilityMatrix.Avalonia.ViewModels.Dialogs;
using StabilityMatrix.Core.Attributes;
namespace StabilityMatrix.Avalonia.Views.Dialogs;
@@ -23,6 +24,17 @@ public partial class ExceptionDialog : AppWindowBase
AvaloniaXamlLoader.Load(this);
}
+ [SuppressMessage("ReSharper", "UnusedParameter.Local")]
+ private async void CopyButton_OnClick(object? sender, RoutedEventArgs e)
+ {
+ var content = (DataContext as ExceptionViewModel)?.FormatAsMarkdown();
+
+ if (content is not null && Clipboard is not null)
+ {
+ await Clipboard.SetTextAsync(content);
+ }
+ }
+
[SuppressMessage("ReSharper", "UnusedParameter.Local")]
private void ExitButton_OnClick(object? sender, RoutedEventArgs e)
{