Commands and context-menu entries kick off async work as `_ = SomeAsync()`,
discarding the task. When one faulted, the failure only surfaced via
TaskScheduler.UnobservedTaskException after the GC finalized the dropped
task -- late, on the finalizer thread, and disconnected from the gesture
that caused it (a click would appear to do nothing, then a mysterious
crash report arrived seconds later).
Add a HandleExceptions() extension that observes such a task on the UI
context and routes any fault to the existing GlobalExceptionHandler at
the point of failure (treating cancellation as a normal outcome), and
route the user-invoked command/entry sites through it. The global
unhandled-exception net already existed; this just makes these failures
prompt and attributable instead of deferred to GC.
Assisted-by: Claude:claude-opus-4-8:Claude Code
The single-assembly project save was already wired, but selecting
multiple assemblies and choosing Save Code only ever saved one of
them: SolutionWriter had never been ported, so there was no path that
emitted a .sln with one decompiled project per assembly.
Port SolutionWriter UI-agnostically and route both Save Code surfaces
(the context-menu entry and the Ctrl+S command) through a shared
SolutionExport helper that recognises a multi-assembly selection,
prompts for the .sln path, runs the export behind the tab's cancellable
progress UI, and reports the result into the active decompiler tab
(matching how the other long-running commands surface their output).
Unlike the prior app, the project-export path writes the .csproj to
disk itself and only returns a breadcrumb through the text output, so
each project's on-disk file is located after decompiling rather than
captured from the output stream.
Assisted-by: Claude:claude-opus-4-8:Claude Code
Fourteen entries surfaced when right-clicking nodes in the assembly
tree, but six were in the implicit null category, four shared a
single overloaded "Save" category with three different operations
(save code, create diagram, extract package), and seven had Order =
double.MaxValue ties so their relative position was MEF discovery
order rather than declared. Several entries lacked icons even though
matching IImage fields already existed in Images.cs.
Apply the same non-overlapping range-per-category shape used for the
File and View menus, with 100-wide bands so each entry has a stable
slot and room for future siblings:
Navigate 100-199 Decompile to new panel (ViewCode), Compare...
Analyze 200-299 Analyze, ScopeSearchToAssembly (FindAssembly),
ScopeSearchToNamespace (Namespace), SearchMSDN (Search)
Save 300-399 Save Code, Create Diagram, Extract package entry
(FolderOpen), Extract all entries (FolderOpen)
Debug 400-499 Select PDB (ProgramDebugDatabase),
Generate Portable PDB (ProgramDebugDatabase)
Shell 500-599 Open containing folder (FolderOpen)
Edit 600-699 Copy fully qualified name
(null) 900+ Reload (Refresh, 900), Remove (Delete, 910)
Extract Package entries previously borrowed the Save icon, which
read semantically wrong -- a folder-open glyph is closer to what
"extract to a directory" actually does. Decompile-to-new-panel moves
from Analyze to Navigate because surfacing the same content in a new
tab is a navigation primitive, not an analysis one. The Reload /
Remove pair stays in the null group at the bottom of the menu --
they're list-mutation operations that conceptually anchor the menu
end, matching where they currently render.
Assisted-by: Claude:claude-opus-4-7:Claude Code
Both menu builders previously ignored the MenuIcon/Icon metadata on
ExportMainMenuCommand and ExportContextMenuEntry: the strings were
preserved through the WPF -> Avalonia port (~12 main-menu commands
declare MenuIcon, plus a handful of context-menu entries), but the
NativeMenuItem and MenuItem instances they fed into had no icon
assignment. Avalonia's toolbar already had a private path-to-image
reflection resolver; this lifts it into Images.cs as a shared helper
so the two menu sites can consume it too.
NativeMenuItem.Icon is Bitmap-typed (macOS NSImage is the platform
target and has no vector form), so the main-menu path rasterises
SVG icons through RenderTargetBitmap at attach-time -- one-time cost
per menu item at boot. MenuItem.Icon accepts any Control, so the
context-menu path keeps the IImage live inside an <Image> control
and preserves vector rendering.
SaveCodeContextMenuEntry and AssemblyTreeNode.ReloadAssembly were
missing Icon metadata even though their main-menu equivalents
declared MenuIcon. Adding Icon = "Images/Save" and "Images/Refresh"
respectively, matching the toolbar/menu sibling. Other context-menu
entries without Icon metadata are intentionally textless: most are
node-type-specific actions (Remove, Copy results, Search MSDN) where
no main-menu sibling exists to inherit an icon mapping from.
The App.axaml separator style selector "MenuItem Separator" only
matched separators nested inside a templated MenuItem (the main-menu
submenu case). ContextMenuProvider adds Separator directly to
ContextMenu.Items between Category groups, where there's no MenuItem
ancestor for the selector to match -- so context-menu separators
rendered with the Simple theme's default near-invisible style.
Extending to "MenuItem Separator, ContextMenu Separator" covers both
surfaces; toolbar and docking-chrome separators stay scoped out.
MenuIconWiringProbe walks the materialised NativeMenu tree post-build
and asserts File > Open has its Icon populated -- regression net for
the metadata-flow path.
Assisted-by: Claude:claude-opus-4-7:Claude Code
Brings the assembly-to-project export across. Right-click an assembly in
the tree (or use the existing File → Save Code / Ctrl+S that calls into
`node.Save()`) and choose between two filters in the save picker:
Lay the foundation for the analyzer pane. None of these classes are
wired into the live view yet — that lands when the pane view-model and
the ProDataGrid view are filled in two commits down. Background-fetch
behaviour on AnalyzerSearchTreeNode is also deferred.
Assisted-by: Claude:claude-opus-4-7:Claude Code
File / View / Window / Help built from MEF main-menu commands plus a static
View submenu (API visibility, Theme = Light/Dark via Application.RequestedThemeVariant,
UI Language) bound to LanguageSettings on SessionSettings. Window menu also lists
the existing tool-pane toggles. Commands without ported deps (Options, About,
Updates, GAC, manage lists, save/PDB) surface a "not yet ported" stub dialog.
Open uses Avalonia StorageProvider; CloseAllDocs/ResetLayout call into DockWorkspace.
InputGestureText on the export attribute is parsed into MenuItem.HotKey, giving
Open Ctrl+O / Save Ctrl+S / Reload F5 as window-scoped accelerators.
Assisted-by: Claude:claude-opus-4-7:Claude Code
Adds the Language abstraction so tree-node labels render through the
active output language instead of raw metadata names.
Assisted-by: Claude:claude-opus-4-7:Claude Code
Replaces the temporary 3-column Grid placeholder in MainWindow with a
Dock.Avalonia DockControl driven by an ILSpyDockFactory and orchestrated
by a [Shared] DockWorkspace exported via MEF.
Assisted-by: Claude:claude-opus-4-7:Claude Code
Adds the assembly tree pane (DataGrid-based, ProDataGrid hierarchical
rows), the SharpTreeNode-derived node type skeletons it consumes, the
empty MainToolBar placeholder, the ILSpySettings file-path provider
wiring in App.axaml.cs, and the AssemblyTreeModel.Initialize() hook in
the MainWindow code-behind.
Assisted-by: Claude:claude-opus-4-7:Claude Code