The "Compare..." right-click flow opened a tab that came up blank because
two stacked bugs masked each other.
Assisted-by: Claude:claude-opus-4-7:Claude Code
Build the layout and call InitLayout up front in DockWorkspace instead of
letting DockControl run it post-template-apply, and populate the locators
explicitly: tool panes map their id to the [Shared] singleton pane, the
Documents dock and main tab get entries for navigate-by-id, and
HostWindowLocator is registered so panes can float into their own windows.
Doing it before the chrome attaches keeps the layout's owner/factory
wiring ahead of the first content realisation.
Also drops the now-redundant per-VM DataTemplate blocks from App.axaml --
the single ViewLocator already resolves them -- and the unused
MainWindowViewModel.DockFactory property and DockControl init flags.
Assisted-by: Claude:claude-opus-4-8:Claude Code
Language.BracketSearcher defaulted to DefaultBracketSearcher
(no-op); only CSharpLanguage overrode it. Switching the active
language to ILAst or IL left caret-on-bracket inert.
Assisted-by: Claude:claude-opus-4-7:Claude Code
The default ApiVisibility=PublicOnly setting filters search results
through CheckVisibility, which rejects private entities. Compiler-
generated names — async/iterator state machines (<Method>d__N),
display classes (<>c__DisplayClass), anonymous-method closures (<>c)
— are all private, so they were unfindable even when the user
typed the exact full name.
Assisted-by: Claude:claude-opus-4-7:Claude Code
UpdateLastDocumentCanClose was counting only factory.Documents.VisibleDockables
to decide CanClose. After the user drags a tab out into a side-by-side
split, Dock creates a second IDocumentDock that holds the dragged tab.
factory.Documents still points at the ORIGINAL dock (now with one tab),
so the handler updates CanClose=false on its remaining tab — but the
new sibling dock's tab keeps the CanClose=true it had before the drag.
Result: one tab closable, the other not, even though the user has two
documents open.
Assisted-by: Claude:claude-opus-4-7:Claude Code
Stepper.Step's break-at-step-limit relies on Debugger.Break(), which
is a silent no-op when no debugger is attached. The wiring (command ->
RestartDecompileWithStepLimit -> DecompilationOptions.IsDebug ->
context.Stepper.IsDebug -> Stepper.Step) was all correct, but the
common-case "user runs from a terminal" left the gesture invisible.
Assisted-by: Claude:claude-opus-4-7:Claude Code
DockWorkspace previously subscribed to IFactory.ActiveDockableChanged
hoping it would fire on tab-strip clicks. It doesn't — Dock's
FactoryBase only raises that event from InitActiveDockable (layout
structural init at startup/load). User-driven tab clicks set
dock.ActiveDockable = X directly on the dock-model, which raises the
model's own INotifyPropertyChanged but bypasses the factory event.
Assisted-by: Claude:claude-opus-4-7:Claude Code
Ports the WPF assemblyList_CollectionChanged history-prune from
ILSpy/AssemblyTree/AssemblyTreeModel.cs to the Avalonia
DockWorkspace.OnAssemblyListChanged handler. NavigationHistory<T>
already exposed the RemoveAll(Predicate<T>) primitive; the caller
wiring was the missing piece. Without it, a tree-row click after
removing an assembly walked through NavigationEntry.DisplayText
on a stale TreeNodeEntry, which hit MemberReferenceTreeNode.Signature
-> Language.EntityToString -> ILAmbience.ConvertSymbol and NRE'd on
a now-null ParentModule.
Assisted-by: Claude:claude-opus-4-7:Claude Code
Sweep-up commit. The dotnet-format pre-commit hook keeps re-ordering
these usings on every other commit; landing them once stops the hook
from grumbling at unrelated diffs going forward.
Assisted-by: Claude:claude-opus-4-7:Claude Code
Generalises the existing internal StartupLog into a public AppLog with
named categories. The Startup category (default-on) keeps the existing
[startup +Nms] format and Mark/Phase API, so all 44 call sites get a
mechanical StartupLog. -> AppLog. rename without behavioural change.
Assisted-by: Claude:claude-opus-4-7:Claude Code
Replaces the reflection-driven Dock.Serializer.SystemTextJson._options
mutation with a from-scratch JsonSerializerOptions in ILSpyDockJson.cs.
Mirrors Newtonsoft's ListContractResolver semantics — polymorphism on
the six IDockable interfaces, IList<T> -> ObservableCollection<T>
substitution at deserialise time, writable-only property persistence,
the standard ignore set (ICommand, [IgnoreDataMember]) — and keeps the
existing back-reference / Task-shape strip plus the singleton-dockable
CreateObject hook.
Assisted-by: Claude:claude-opus-4-7:Claude Code
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