DebugReporter.Avalonia
0.1.1
dotnet add package DebugReporter.Avalonia --version 0.1.1
NuGet\Install-Package DebugReporter.Avalonia -Version 0.1.1
<PackageReference Include="DebugReporter.Avalonia" Version="0.1.1" />
<PackageVersion Include="DebugReporter.Avalonia" Version="0.1.1" />
<PackageReference Include="DebugReporter.Avalonia" />
paket add DebugReporter.Avalonia --version 0.1.1
#r "nuget: DebugReporter.Avalonia, 0.1.1"
#:package DebugReporter.Avalonia@0.1.1
#addin nuget:?package=DebugReporter.Avalonia&version=0.1.1
#tool nuget:?package=DebugReporter.Avalonia&version=0.1.1
DebugReporter.Avalonia
Reusable Avalonia bug-report dialog for desktop apps: problem description, screenshots (file / clipboard / OS snip / annotation), optional local speech-to-text, automatic session-log attachment, and a host-provided send pipeline (Gmail API, SMTP, webhook, etc.).
NuGet: DebugReporter.Avalonia
Assembly / namespaces: AvaloniaDebugReporter (unchanged for API stability)
What you get vs what you implement
| Provided by the package | Implemented by your app (required for production) |
|---|---|
| Bug-report UI (dialog, annotation, recipient prompt) | IDebugReportSender — actually deliver the report |
| Read & summarize a log file you point to | LogFilePathProvider — path to your session log on disk |
| Built-in en/sv UI strings (optional) | IDebugReportLocalizer — other languages or your own wording |
| Default Whisper.net dictation (KB-Whisper model download) | ITextDictationService (optional) — use your own STT instead |
| MIME builder for email bodies + attachments | Gmail / SMTP / API credentials and transport |
NoOpDebugReportSender if you forget registration |
Replace with a real sender before shipping |
The package does not ship Gmail, SMTP, or Serilog. It does not send anything until you register IDebugReportSender.
Install
dotnet add package DebugReporter.Avalonia
Requirements: .NET 8+ host, Avalonia 11.x, Microsoft.Extensions.DependencyInjection.
Quick start (dialog only)
using AvaloniaDebugReporter.Services;
using Microsoft.Extensions.DependencyInjection;
services.AddAvaloniaDebugReporter(options =>
{
options.AppName = "My App";
options.DeveloperName = "the developer";
options.LogFilePathProvider = () => MyApp.CurrentSessionLogPath;
options.VersionProvider = () => "1.2.3";
});
// Register AFTER AddAvaloniaDebugReporter (replaces the built-in no-op sender):
services.AddSingleton<IDebugReportSender, MyDebugReportSender>();
Open the dialog from a window:
var dialog = serviceProvider.GetRequiredService<IDebugReportDialogService>();
bool? sent = await dialog.ShowAsync(ownerWindow);
// true = sender returned success, false = send failed, null = user cancelled
Until IDebugReportSender is registered, Send logs a warning and returns false.
Integration checklist
Use this order when wiring a production app:
AddAvaloniaDebugReporter— setAppName,LogFilePathProvider, and optional providers (language, version, developer display name).IDebugReportSender— sendDebugReport(email, ticket system, HTTP, …).IDebugReportLocalizer(recommended if not en/sv) — UI + email body strings.ITextDictationService(optional) — replace built-in Whisper dictation.ILoggerFactory(optional) — route package diagnostics to your logging (see below).- Menu / command — call
IDebugReportDialogService.ShowAsync(owner).
Extension points
IDebugReportSender (required for real delivery)
public interface IDebugReportSender
{
Task<bool> SendAsync(DebugReport report, CancellationToken cancellationToken = default);
}
DebugReport contains everything the user entered plus collected data:
Description,ReproductionStepsSessionLog(summarized text, not the raw file path)Attachments(image bytes + file names)RecipientEmail— who receives the report (see Recipient email)ReporterContact— optional; set in the sender if you know the user's emailLanguage— language code used for email body labels (e.g.en,sv,de)Version,AppName, OS / architecture,CreatedAtUtc
Return true only when delivery succeeded.
IDebugReportLocalizer (optional — UI and email text)
public interface IDebugReportLocalizer
{
string? GetString(string key, string? language);
}
Used for:
- All dialog, annotation, recipient, and dictation UI strings
- Email plain-text section headings via
DebugReportMimeMessageBuilder(Report.*keys)
Return null or the key itself to fall back to built-in English, then Swedish for known keys.
Built-in languages: en, sv (prefix match, e.g. sv-SE → Swedish).
Dynamic UI language: set options.Language or options.LanguageProvider (called when the dialog opens).
Email language: taken from DebugReport.Language (same provider / option at send time).
Keys are defined in DefaultDebugReportLocalizer (e.g. Dialog.BugReport.Title, Dialog.Recipient.Message, Report.SessionLog). Copy that class or grep the source when adding locales.
Dialog.BugReport.Title supports {DeveloperName} — set DeveloperName or DeveloperNameProvider.
ITextDictationService (optional — dictation button)
Default registration: WhisperTextDictationService (Whisper.net + PvRecorder).
Replace when your app already has speech-to-text:
services.AddSingleton<ITextDictationService, MySpeechToTextAdapter>();
Disable dictation entirely:
options.EnableDictation = false;
IDebugLogCollector (optional)
Default: reads the file from LogFilePathProvider and summarizes it. Replace only if you need a custom log source.
IDebugReportSettingsStore (optional)
Default: saves prompted recipient email under
%LocalAppData%/{DataDirectoryName}/debug-reporter-settings.json.
Session logs (Serilog-friendly, not Serilog-coupled)
The package does not reference Serilog. It reads a text file path you supply:
options.LogFilePathProvider = () => pathToSessionLogFile;
LogSummarizer expects lines similar to Serilog file output, for example:
[Instance] [12:53:19.177 INF App] Initialized localization with language: sv
It collapses consecutive duplicate lines and truncates to MaxSummarizedLogLength (default 60 000). The result is embedded in DebugReport.SessionLog when the user sends.
Typical Serilog setup (host app):
Log.Logger = new LoggerConfiguration()
.WriteTo.File(sessionLogPath, shared: true, ...)
.CreateLogger();
options.LogFilePathProvider = () => sessionLogPath;
Use shared: true if the dialog reads the log while the app is still writing.
Package diagnostics (Microsoft.Extensions.Logging)
Internal features (screen snip, clipboard, Whisper install, etc.) log through ILogger with category AvaloniaDebugReporter.
By default the package registers NullLoggerFactory — nothing is written.
To see package logs in your pipeline (e.g. Serilog), register a factory before or after AddAvaloniaDebugReporter so it replaces the null factory:
services.AddLogging(builder => builder.AddSerilog(Log.Logger));
// or: services.AddSingleton<ILoggerFactory>(myLoggerFactory);
services.AddAvaloniaDebugReporter(...);
Dictation (local AI — KB-Whisper)
When EnableDictation is true (default), the dictation button uses:
| Setting | Default | Meaning |
|---|---|---|
WhisperModelFileName |
kb-whisper-small.bin |
Local file name under app data |
WhisperModelUrl |
KBLab Hugging Face URL | Downloaded on first use |
WhisperLanguage |
sv |
Whisper language code |
The model is downloaded once; the UI shows install / record / transcribe states (Dictation.* strings).
Dependencies bundled in the package: Whisper.net, Whisper.net.Runtime, PvRecorder, NAudio.Core.
Override with ITextDictationService if you use another engine or want to share models with the rest of your app.
Email delivery (Gmail, SMTP, or anything else)
The package builds MIME messages; you send them.
Helper: DebugReportMimeMessageBuilder
// Plain MIME (SMTP, MailKit, etc.)
var mime = DebugReportMimeMessageBuilder.BuildMimeMessage(
report, from: sender@app.com, to: report.RecipientEmail!,
subject: $"[{report.AppName}] Debug report",
localizer: myLocalizer, language: report.Language);
// Gmail API `users.messages.send` raw field:
var raw = DebugReportMimeMessageBuilder.BuildGmailRaw(
report, from, to, subject, myLocalizer, report.Language);
Pass the same IDebugReportLocalizer you use for the UI so the email body matches the user's language.
Example sender skeleton (SMTP pseudo-code)
public sealed class SmtpDebugReportSender : IDebugReportSender
{
public async Task<bool> SendAsync(DebugReport report, CancellationToken ct)
{
if (string.IsNullOrWhiteSpace(report.RecipientEmail))
return false;
var mime = DebugReportMimeMessageBuilder.BuildMimeMessage(
report,
from: "noreply@myapp.com",
to: report.RecipientEmail,
subject: $"[{report.AppName}] v{report.Version} debug report");
// Send `mime` with MailKit / System.Net.Mail / your provider
return await SendMimeAsync(mime, ct);
}
}
Gmail API
Use BuildGmailRaw, then Users.Messages.Send with Message.Raw (base64url). Your app must own OAuth / service account setup — same as any Gmail integration.
Recipient email (who receives reports)
Flow when the user clicks Send:
- If
DeveloperEmail/DeveloperEmailProvideris set → used as recipient. - Else if
PromptForDeveloperEmailIfMissing(defaulttrue) → show Recipient address dialog (Dialog.Recipient.*strings). - If
RememberPromptedDeveloperEmail(defaulttrue) → save to
%LocalAppData%/{DataDirectoryName}/debug-reporter-settings.json.
Set a fixed support address and skip the prompt:
options.DeveloperEmail = "support@example.com";
options.PromptForDeveloperEmailIfMissing = false;
report.RecipientEmail in IDebugReportSender is the resolved address.
Configuration reference (DebugReportOptions)
| Property | Default | Description |
|---|---|---|
AppName |
"Avalonia app" |
Shown in UI and report title |
DeveloperName |
"the developer" |
Title token {DeveloperName} |
DeveloperEmail |
null |
Fixed recipient; skips prompt when set |
Language |
"en" |
UI language if no LanguageProvider |
LanguageProvider |
null |
Dynamic UI language (e.g. app settings) |
DeveloperNameProvider |
null |
Localized “developer” label in title |
DeveloperEmailProvider |
null |
Dynamic recipient |
LogFilePathProvider |
null |
Recommended — session log file path |
VersionProvider |
null |
App version in report |
ReporterContactProvider |
null |
Prefill reporter email in payload |
DataDirectoryName |
"AvaloniaDebugReporter" |
Subfolder under %LocalAppData% for settings |
ScreenClipSourceName |
"AvaloniaDebugReporter" |
OS snip source name (Windows) |
EnableDictation |
true |
Show dictation buttons |
PromptForDeveloperEmailIfMissing |
true |
Ask for recipient when not configured |
RememberPromptedDeveloperEmail |
true |
Persist recipient JSON settings |
MaxAttachments |
12 |
Image count limit |
MaxTotalAttachmentBytes |
18 MiB | Total attachment size |
MaxSummarizedLogLength |
60000 | Truncation for session log text |
AllowedImageExtensions |
png, jpg, … | File picker / validation |
WhisperLanguage |
"sv" |
Default Whisper language |
WhisperModelFileName |
kb-whisper-small.bin |
Local model file name |
WhisperModelUrl |
KBLab URL | Model download URL |
UI resources
Dialogs and styles are embedded in the assembly (avares://AvaloniaDebugReporter/...). You do not need to copy XAML into your app.
Return values
IDebugReportDialogService.ShowAsync:
| Result | Meaning |
|---|---|
true |
User sent; IDebugReportSender returned true |
false |
User sent; sender failed or no sender |
null |
User cancelled or closed the dialog |
Minimal vs full registration
Playground / UI test only
services.AddAvaloniaDebugReporter(o => o.AppName = "Demo");
// No IDebugReportSender → Send does nothing useful
Production
services.AddLogging(...); // optional
services.AddAvaloniaDebugReporter(options => { /* see checklist */ });
services.AddSingleton<IDebugReportLocalizer, MyLocalizer>(); // if needed
services.AddSingleton<ITextDictationService, MyStt>(); // if needed
services.AddSingleton<IDebugReportSender, MySender>(); // required
License
MIT — see repository for details.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net8.0
- Avalonia (>= 11.3.15)
- CommunityToolkit.Mvvm (>= 8.4.2)
- Material.Icons.Avalonia (>= 2.4.1)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 10.0.7)
- Microsoft.Extensions.Logging.Abstractions (>= 10.0.7)
- Microsoft.Extensions.Options (>= 10.0.7)
- NAudio.Core (>= 2.3.0)
- PvRecorder (>= 1.2.13)
- SkiaSharp (>= 3.116.1)
- Whisper.net (>= 1.9.0)
- Whisper.net.Runtime (>= 1.9.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.