Without per-dockable view resolution the dock chrome caches one view per
slot and reuses it for every dockable that lands there, so a second pane
sharing an Alignment (Analyzer + Debug Steps both Bottom) renders the
first pane's content under the second pane's tab. Each dockable instead
owns its view (IDockableViewOwner) and DockableViewRecycling hands that
instance back keyed by dockable identity -- there is no app-lifetime
global view cache to leak every transient document tab's view subtree.
Resolution runs through the single application-wide ViewLocator (its
dispatch map is introduced here rather than in the later DataTemplate
cleanup) so the dock never re-resolves an owned view through the template
machinery on a cache hit.
Assisted-by: Claude:claude-opus-4-8:Claude Code
The three editor-command tests (Copy_Entry_Reflects, Copy_Execute, SelectAll_Execute)
selected `System.Linq.Enumerable` (the whole type, 300+ LINQ methods) and waited
on `view.Editor.Document.TextLength > 0` — a 15s `WaitForAsync` poll. In suite
context this was sloppy: it could latch on stale editor content from a previous
test, and in isolation it occasionally raced past 15s on a slow system,
producing the intermittent `failed: 1` we saw earlier.
Assisted-by: Claude:claude-opus-4-7:Claude Code
The user-reported "Debug Steps pane is empty after clicking Show Steps" bug
was a View-lifecycle issue: `DebugStepsPaneModel` declares
`IsVisibleByDefault = false`, so the `DebugSteps` UserControl was realised by
the dock factory only when its tab was first activated. By then
`BlockILLanguage.DecompileMethod` had already fired `OnStepperUpdated` into
the void — no subscriber existed yet.
LMB double-click is WPF's expand/collapse gesture, already wired through
`OnTreeGridDoubleTapped`. An earlier patch also added it as a third "open in
new tab" gesture in `OnTreeGridPointerPressed`, so users got BOTH effects on
every double-click (the row toggled *and* a new tab opened). Restore parity
by removing the `isDoubleClick` branch from `OnTreeGridPointerPressed`; MMB
single-click and Ctrl+LMB single-click remain the two new-tab gestures.
Regression test in `HeadlessMmbPointerTests`.
Imports the three SVG assets that were missing from `ILSpy.Avalonia/Assets/Icons/`
and registers them in `Images/Images.cs`, so the MEF-injected toolbar buttons
for "Reload all assemblies", "Sort assembly list by name", and "Collapse all
tree nodes" now render their icons instead of falling back to the tooltip text
(`MainToolBar.axaml.cs:226` `ResolveIcon` returned null for these). Adds a
regression test asserting every `[ExportToolbarCommand]`'s `ToolbarIcon`
resolves to a real `IImage` field on the registry.
Two missing toolbar buttons (Manage Assembly Lists, Show Search) and an
incorrect button order; the layout now mirrors WPF MainToolBar.xaml +
InitToolbar exactly. Also fixes an InitializeButtons no-op under
Avalonia.Headless (Loaded never fired; switched to AttachedToVisualTree).
Assisted-by: Claude:claude-opus-4-7:Claude Code
Persists the live dock layout to ILSpy.Layout.json next to ILSpy.xml on
MainWindow.OnClosing; loads it on DockWorkspace ctor; falls back to
factory.CreateLayout when the file is absent or fails to deserialize.
WPF stays XML in ILSpy.xml — this is Avalonia-side only.
Assisted-by: Claude:claude-opus-4-7:Claude Code
1) post-refresh-redecompile: AssemblyTreeModel.RefreshInternalAsync now calls
DockWorkspace.ForceRefreshActiveTab() after SelectNode. F5 on the same
assembly list doesn't rebuild the tree → FindNodeByPath returns the same
tree-node reference → SelectedItem setter early-outs → ShowSelectedNode's
lastShownNodes dedup short-circuits, leaving stale decompiled text. The
force path resets lastShownNodes and re-runs the decompile pipeline.
Mirrors WPF's RefreshDecompiledView() call.
Assisted-by: Claude:claude-opus-4-7:Claude Code
Cherry-picked from master commits 4c8e606a6 + 4c347ef6b. Refresh (F5) on a
selection inside a lazy-loaded resource tree (e.g. a .baml entry inside an
embedded .resources file) was collapsing the selection back to the resources
folder because FindNodeByPath ran before the assembly's metadata-file
finished loading and the resource children hadn't materialised yet.
Assisted-by: Claude:claude-opus-4-7:Claude Code
Splits Windows-only tests out of the cross-platform ILSpy.Avalonia.Tests
project so the Pdb2Xml / OpenFromGac code paths (which compile only on
Windows) can be exercised without conditional `#if WINDOWS` blocks inside
the main test suite.
Assisted-by: Claude:claude-opus-4-7:Claude Code
Lets non-decompile-protocol EntityReference hyperlinks route through plug-in
resolvers instead of always falling into FindTreeNode. The dispatcher in
DockWorkspace.OnNavigateRequested checks `entity.Protocol != "decompile"`,
walks every IProtocolHandler MEF export, and lands the user on the first
non-null tree node returned.
Assisted-by: Claude:claude-opus-4-7:Claude Code
Replaces the NotImplementedDialog stub in OpenFromGacCommand with a real
dialog. CanExecute gates on OperatingSystem.IsWindows() since the Windows
Global Assembly Cache doesn't exist on Linux/macOS (matches WPF's
AppEnvironment.IsWindows gate). The actual GAC enumeration uses the shared
UniversalAssemblyResolver.EnumerateGac() + GetAssemblyInGac() pair from
ICSharpCode.Decompiler — no platform-specific code in the Avalonia tree.
Assisted-by: Claude:claude-opus-4-7:Claude Code
WPF ships Pdb2XmlCommand behind `#if DEBUG && WINDOWS` because
Microsoft.DiaSymReader uses native COM interop. Avalonia parity uses the
same gate, with the package references conditional on the build host being
Windows + Debug, and the consuming command file `#if DEBUG && WINDOWS`.
Assisted-by: Claude:claude-opus-4-7:Claude Code
Replaces the NotImplementedDialog stub in FileCommands.cs's
ManageAssemblyListsCommand with a real File → Manage Assembly Lists dialog.
Assisted-by: Claude:claude-opus-4-7:Claude Code
Right-click an assembly → Select PDB. File-pick a portable / Windows PDB,
call LoadedAssembly.LoadDebugInfo, and refresh the active tree selection
so the new debug symbols feed back into the decompiler output.
Assisted-by: Claude:claude-opus-4-7:Claude Code
Adds BindTree_With_Small_AssemblyList_Settles_In_Reasonable_Time alongside
the existing 200-assembly [Explicit] benchmark. Not [Explicit] so it runs
in the regular suite; 8 copies of CoreLib with a 15s threshold catches
order-of-magnitude regressions in the open+settle pipeline without the
30s+ cost of the full benchmark.
Assisted-by: Claude:claude-opus-4-7:Claude Code
Single assertion guarding the existing IDeferredContentPresentation.DeferContentPresentation
override that keeps tool panes (search, analyzers, debug-steps, …) hot at
startup. Without the opt-out, headless tests can't reach pane descendants
until focus-activation, and the search pane's startup tasks (assembly index
scan etc.) wouldn't kick off until first user interaction.
Assisted-by: Claude:claude-opus-4-7:Claude Code
Same shape as DecompileAllCommand but routes through ILLanguage. Writes to
c:\temp\disassembled\<safe-name>.il in parallel, reports per-assembly time
and exception type, with CanExecute gating on the output dir existing.
Assisted-by: Claude:claude-opus-4-7:Claude Code
Replaces the NotImplementedDialog stubs in FileCommands.cs with real
implementations. Both are DEBUG-only File-menu entries for decompiler dev:
Assisted-by: Claude:claude-opus-4-7:Claude Code
Right-click one or more assemblies with CodeView debug-directory entries →
Generate Portable PDB. Folder picker for output, then PortablePdbWriter
runs per assembly under DockWorkspace.RunWithCancellation; the report tab
lists per-file success / fail / unsupported plus total elapsed time + an
Open Explorer button.
Assisted-by: Claude:claude-opus-4-7:Claude Code
Ctrl+M toggles the innermost fold containing the caret. Ctrl+Shift+M collapses
all when any fold is open and expands all otherwise (parity-based toggle).
Assisted-by: Claude:claude-opus-4-7:Claude Code
Standard popup convention; the chips/pills inside don't override KeyDown so
the Tunnel-routed handler on the popup root catches the key before any
inner control. Arrow-key navigation between chips is a bigger lift (chip
groups don't share a focus container) — leaving as a follow-up.
Assisted-by: Claude:claude-opus-4-7:Claude Code
The funnel glyph is a hand-drawn 10×9 trapezoid with no associated text, so
its purpose isn't obvious to a user who hasn't seen the active state. Adding
a tooltip on the funnel's hit-area Border surfaces the column name when the
user hovers (Avalonia 11+ ToolTip.SetTip).
Assisted-by: Claude:claude-opus-4-7:Claude Code
When a tree node becomes visible for the first time, its already-realised
children might carry stale IsHidden state — OnChildrenChanged only fires
for newly-added children, and only when the parent was visible at the time.
The visible result: a property's accessor children read as visible the first
time the user expands the property even under ShowApiLevel=PublicOnly, until
something else (refresh / language flip) forces a cascade.
Assisted-by: Claude:claude-opus-4-7:Claude Code
Pair to ea56a80cd. When DecompilerSettings.ExtensionMembers is on and the
active language is C#, methods that the decompiler identifies as implementation
methods for an extension block are filtered out of the outer static class —
they surface as ExtensionTreeNode children of the containing static class
instead. Without this gate the user would see each extension member twice:
once as a regular method on the static container, once under the extension
block. Mirrors WPF MethodTreeNode.Filter lines 95-104 verbatim.
Assisted-by: Claude:claude-opus-4-7:Claude Code
Each `extension<T>(ReceiverType source) { ... }` block inside a static class
now appears as its own tree node sibling to the regular methods/properties.
Expanding the node lists the block's declared methods + properties as
PropertyTreeNode / MethodTreeNode children sorted by name.
Assisted-by: Claude:claude-opus-4-7:Claude Code
Mirrors WPF's `ShowCFGContextMenuEntry` and `GraphVizGraph` utility behind
`#if DEBUG`. Right-click a `BlockContainer` reference in the IL output
("DEBUG -- Show CFG") → builds the ControlFlowGraph for the method, writes
a `.gv` file in the temp dir, shells out to `dot` (Graphviz) to render PNG,
opens the result with the OS default image viewer.
Assisted-by: Claude:claude-opus-4-7:Claude Code
Right-click → "Extract Package Entry" on items inside a NuGet zip or .NET
single-file bundle. Single-item selection (an AssemblyTreeNode or a
ResourceTreeNode) pops a SaveFileDialog; multi-item or PackageFolderTreeNode
selection pops a folder-picker. Runs the extraction on a background thread
through the new DockWorkspace.RunWithCancellation infrastructure, then
ShowText reports per-entry write paths + total elapsed time + an "Open
Explorer" button that selects the result in the OS file manager.
Assisted-by: Claude:claude-opus-4-7:Claude Code
Right-click an assembly tree node → Create Diagram. Prompts for an output
folder via the new FilePickers.PickFolderAsync helper, then runs the shared
ICSharpCode.ILSpyX.MermaidDiagrammer.GenerateHtmlDiagrammer engine on a
background thread under DockWorkspace.RunWithCancellation (the new wait UI
with a custom "Creating diagram…" title). On completion, pushes a report
into the active tab via ShowText — elapsed time, learn-more link, and an
"Open Explorer" button that selects the generated index.html in the OS file
manager (xdg-open / open -R / explorer.exe per platform).
Assisted-by: Claude:claude-opus-4-7:Claude Code
Adds the WPF-equivalent "run a long-running task with the wait UI showing a
custom title, then display the resulting text in the editor" primitives. Both
methods live on DecompilerTabPageModel (the natural owner — it's where the
wait adorner and editor state already live); DockWorkspace exposes thin
delegates that forward to the active tab.
Assisted-by: Claude:claude-opus-4-7:Claude Code
Mirrors WPF's ZoomScrollViewer behaviour without templating AvaloniaEdit's
TextEditor. The Avalonia path scales the editor's FontSize (which feeds
DisplaySettings.SelectedFontSize) on Ctrl+Wheel; AvaloniaEdit lays out at the
new size on the next render. WPF's full-content-tree LayoutTransform approach
doesn't fit AvaloniaEdit's architecture, but the font-scale result is what
users actually expect from "editor zoom".
Assisted-by: Claude:claude-opus-4-7:Claude Code
Foldings persistence rounds out the view-state work that landed in 185e0551d
(caret + scroll). Mirrors WPF's DecompilerTextViewState.SaveFoldingsState /
RestoreFoldings semantics verbatim — including the layout-checksum gate that
refuses restoration when the new document's foldings don't match the captured
layout, so a stale snapshot can't accidentally expand random regions of a
shifted document.
Assisted-by: Claude:claude-opus-4-7:Claude Code
Adds ICSharpCode.BamlDecompiler as a ProjectReference, ports IResourceFileHandler +
ResourceFileHandlerContext from WPF, and plumbs both through:
Assisted-by: Claude:claude-opus-4-7:Claude Code
Closes two tracker items in one commit:
- `BrowseForwardCommand` was already wired end-to-end (command, toolbar
button, Alt+Right key binding). New test pins the wiring so a future
regression to any of the three surfaces is caught.
- `ViewState (caret + scroll persistence across navigation)` was missing.
Navigating back to a previously-visited node landed the caret at offset
0 / scroll at top instead of where the user was looking before they
moved away.
Promotes the modern .NET XML-doc lookup from the Avalonia port into the
shared ICSharpCode.Decompiler library so every host (WPF, Avalonia, any
third-party consumer of XmlDocLoader) gets hover/tooltip documentation
for system entities without per-host fallback wiring.
The decompiler-view hover tooltip's XML-documentation lookup goes through
the shared ICSharpCode.Decompiler.Documentation.XmlDocLoader. That helper
only knows two layouts: .xml beside the .dll, and the .NET Framework
reference-assemblies paths (Program Files (x86)\Reference Assemblies\
Microsoft\Framework\v1.0 … v4.8.1). Modern .NET runtime DLLs
(System.Private.CoreLib.dll, System.Linq.dll, …) ship without any .xml
beside them — the XML files live in the parallel reference pack at
Closes the third logged analyzer follow-up. When the user double-clicks an
analyzer result (e.g. one of the methods listed under "Used By" on Foo.Bar),
the decompiled-output view now paints local-reference marks on every
mention of the originally-analysed entity inside the navigated-to body —
so the user can spot the exact call site the result row corresponds to.
Two of the three analyzer follow-ups I'd logged from earlier live-testing.
Both are tiny, both close gaps that made the analyzer feature feel
half-finished: