The Avalonia port had placed the UI app in an ILSpy.* namespace tree,
while the csproj RootNamespace and every prior release (through 10.1)
use ICSharpCode.ILSpy.*. Restoring the historical namespace reduces the
public API diff against release/10.1 for plugin authors and removes the
shadowing that forced global:: qualifiers in the test project. The
Images class and AccessOverlayIcon enum move back into the root
namespace (as in 10.1), since an ICSharpCode.ILSpy.Images namespace
would shadow the Images class for all code inside ICSharpCode.ILSpy.
Assisted-by: Claude:claude-fable-5:Claude Code
The command always built a decompiler tab and force-decompiled the
node, so a metadata-table (or resource) node opened an empty code tab
instead of its grid. Route a single node through OpenNodeInNewTab, which
honours the node's CreateTab() custom content and falls back to a
decompiler tab for ordinary nodes; multi-node selections keep the
decompile-the-union behaviour.
Assisted-by: Claude:claude-opus-4-8:Claude Code
The quick Save Code path exports a project (one assembly) or a solution
(several) straight through a file picker, with no way to tune the
output. Add a dedicated, discoverable "Export Project/Solution..." entry
(File menu + assembly context menu, alongside Save Code) that opens a
configuration dialog: pick the output folder, preview the projects that
will be written (with invalid / duplicate-name / PDB-eligible badges),
toggle project-format and decompiler options, optionally sign with a
strong-name key, and optionally emit a portable PDB per assembly --
defaulting source-embedding off since the project's .cs are on disk.
The export runs on a clone of the live settings (never the persisted
instance) behind the tab's cancellable progress UI and reports into the
active decompiler tab. StrongNameKeyFile now flows through
DecompilationOptions into the project writer, which had no reachable
setter before. The engine (ProjectExporter) and the preview computation
are split out from the window so they are headless-testable.
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
Finishes the decompiler text-view context menu: the remaining tree-only entries now also act on a right-clicked symbol (IEntity). Decompile to new tab opens the entity's definition in a fresh tab -- which required honouring the previously-dead InNewTabPage flag on NavigateToReferenceEventArgs (OnNavigateToReference now opens the resolved node via OpenNodeInNewTab). Scope search to assembly/namespace read the entity's ParentModule.AssemblyName / Namespace, matching the existing inassembly:/innamespace: filters.
Assisted-by: Claude:claude-opus-4-8:Claude Code
The decompiler text-view context menu was missing the navigation and folding entries the previous version had. Add Decompile (go to definition) on a right-clicked symbol -- it navigates the tree to the entity's definition via the existing NavigateToReference bus message -- and Toggle folding / Toggle all folding, which reuse the editor's existing Ctrl+M logic (extracted into public DecompilerTextView methods). The folding entries only show when the document has foldings.
Assisted-by: Claude:claude-opus-4-8:Claude Code
Two implemented tree gestures had no coverage (flagged by a gesture audit): middle mouse button on a row opens it in a new document tab, and Ctrl+R analyzes the selected member into the analyzer pane.
Assisted-by: Claude:claude-opus-4-8:Claude Code
The Simple theme drew keyboard focus as a dotted, fill-less adorner that vanished outright once a context menu took focus, and right-click versus keyboard invocation marked the target inconsistently. Render focus as a selection-coloured fill with a dotted darker-blue border via a FocusAdornerTemplate, and reuse the same visual as a ContextTargetVisual template part for the right-click target. A keyboard-invoked menu (Shift+F10 / Apps) now marks the focused row and restores its focus and adorner on close, since Avalonia's ContextMenu never restores focus itself.
Assisted-by: Claude:claude-opus-4-8:Claude Code
The assembly-tree SharpTreeView migration (0995d32df, "WIP: tests pending")
left two bits of debt that surfaced as seven failing tests:
- It dropped the old AssemblyListPane.CenterRowInView, so a model-driven
selection only scrolled the row to the nearest viewport edge. Reinstate
centring in SharpTreeView.ScrollIntoNodeView via CenterNodeInView: bring the
row on screen, then offset so it sits at the vertical centre. It skips the
move only when the row is already roughly centred (not merely visible at an
edge), because the ListBox's AutoScrollToSelectedItem drags the selected row
to an edge first and a reveal should still pull it to the centre -- while a
click on an already-centred row doesn't twitch.
- DecompileInNewViewTests still queried the removed ProDataGrid surface
(DataGrid / DataGridRow / HierarchicalNode). Retarget to SharpTreeView /
SharpTreeViewItem and the node-valued SelectedItem, and clamp the right-click
X to the visible grid width (tree rows stretch to content width, so a row
centre can sit off-screen).
Full suite: the seven deterministic failures are gone; the only remaining two
are the pre-existing group-ordering-flaky pair (both pass in isolation).
Switching to a document tab is supposed to pull the tree selection over to
what that tab shows, but it didn't reliably -- the gap was masked until the
right-click change stopped moving the selection on its own. Two problems:
The SelectedItem setter replaced the collection with Clear()+Add(), and the
transient empty step made the grid sync defer its completion flag, which then
suppressed the sync for the real new value -- so the tree visual stopped
following tab activation. Route every selection replacement through a single
batched SelectNodes() that fires the selection-changed fan-out once, with the
final set, so consumers never observe the transient empty (or a transient
multi, which would break metadata-tab reuse).
And a tab decompiled from several nodes carries no single SourceNode, so
activating it restored nothing. Restore the tab's full node set from
CurrentNodes (one or many), and mirror a multi-selection into the grid so every
restored node is highlighted, not just the primary.
Assisted-by: Claude:claude-opus-4-8:Claude Code
Right-clicking a tree row to reach its context menu used to move the real
selection there first, because ProDataGrid selects the row on press. With the
preview document bound to the selection, that meant 'Decompile to new tab' on
node B (while viewing A) jumped the preview to B before the command ran, so you
ended up with B twice instead of the intended A + B. Middle-click avoided it,
but not every mouse has a usable one.
Capture the right-clicked row in ContextRequested (which fires even when a
previous menu's light-dismiss popup swallows the press) and swallow the
right-press so the grid never reselects: the menu now acts on the clicked row
as a Thunderbird-style context target while the selection -- and the document
-- stay put. The targeted row gets a faint focus-box highlight, cleared when
the menu closes (guarded so a stale menu's close can't wipe a newer target).
Also adds TestHarness.ClickItem to collapse the repeated
Items.OfType<MenuItem>().Single(...).RaiseEvent(...) menu-click dance.
Assisted-by: Claude:claude-opus-4-8:Claude Code
To audit what each UI test actually exercises, every step now snapshots the
live window to <TestFixtureName>/<TestName>_<NN>_<ShortDescription>.png: a
booted frame (emitted automatically by TestHarness.BootAsync), one after each
state-changing action, and one before each assertion. Flip ILSPY_TESTS_VISIBLE=1
to render the filmstrip; it lands under %TEMP%/ilspy-test-captures (overridable
via ILSPY_TEST_CAPTURES).
The step number and fixture/test name are derived automatically so inserting a
breakpoint never renumbers the rest. The identity is recorded up front from the
real ITest in an ITestAction hook rather than read live: NUnit's
TestContext.CurrentContext does not flow onto async continuations, so a capture
after an await would otherwise collide under the ad-hoc context. And when
rendering is off the whole call is a true no-op -- not even a dispatcher pump --
so instrumenting a test can never perturb the navigation/tab timing it asserts
on. Full headless suite stays green.
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
The Reload/Remove/SearchMSDN/OpenContainingFolder/DecompileInNewView
tests used to call `entry.Execute(synthetic TextViewContext)` against
a hand-built context. They now select via the assembly-tree model,
build the context menu through `BuildContextMenuForCurrentState`
(the same call the live `Opening` event makes), and fire the menu
item's `Click` routed event — the same handler the user's click
invokes. This widens the coverage to the menu-build → click-handler
attachment pipeline.
Assisted-by: Claude:claude-opus-4-7:Claude Code
Extends the double-click-to-decompile path with a new-tab variant. RaiseRowActivated
now carries an OpenInNewTab flag; DockWorkspace branches on it to either reuse the
active tab (existing tree-selection path) or spawn a fresh DecompilerTabPageModel.
The view's OnGridDoubleTapped reads KeyModifiers.Shift; a new
DecompileMetadataRowInNewTabCommand exports the same gesture as a context-menu entry.
Assisted-by: Claude:claude-opus-4-7:Claude Code
Replaces the brittle root.GetVisualDescendants().OfType<T>().Single() / .First()
pattern with a new WaitForComponent<T>() extension that polls until the requested
control is in the visual tree, then returns it. Avalonia.Headless tests routinely
queried the visual tree before lazily-templated panes (DataGrid, dock content)
had materialised, surfacing as intermittent 'Sequence contains no elements'
failures across the suite.
Assisted-by: Claude:claude-opus-4-7:Claude Code
Right-click on a Type / Method / Field / Property / Event / Namespace row
opens the MS-Docs landing page for the selected entity in the default
browser. URL shape:
https://learn.microsoft.com/dotnet/api/<reflection-name>
with backticks (generic arity) → hyphens, '+' (nested types) → '.', and
'.ctor' → '-ctor' so constructors resolve correctly.
Assisted-by: Claude:claude-opus-4-7:Claude Code
Reveals the assembly's file in the OS file manager. Walks the selection's
parent chain to the enclosing AssemblyTreeNode so the entry works on
members / namespaces too, not just the assembly row.
Assisted-by: Claude:claude-opus-4-7:Claude Code
Adds the IMemberTreeNode interface (the same contract WPF uses) and makes
the five entity-bearing tree-node types — Type / Method / Field / Property /
Event — implement it so generic tooling can reach the underlying IEntity
without knowing which concrete tree node it has on hand.
Assisted-by: Claude:claude-opus-4-7:Claude Code
[ExportContextMenuEntry(Header = "DecompileToNewPanel")] sitting in the
Analyze category — opens a fresh DecompilerTabPageModel, asks the dock
workspace to put it next to the existing tab, and assigns the selected
tree-node(s) as its CurrentNodes (which fires the decompile pipeline the
same way a regular selection change does).
Assisted-by: Claude:claude-opus-4-7:Claude Code
Adds the MEF extension surface that lets commands contribute right-click
entries the same way they contribute main-menu and toolbar items today:
Assisted-by: Claude:claude-opus-4-7:Claude Code