Lays the rails for Phase 2 without flipping any existing nodes onto the
new path: MetadataTablePageModel + MetadataTablePage view +
MetadataColumnBuilder (cached per row type), an ILSpyTreeNode.CreateTab()
hook the docking host now consults before falling through to the
decompiler tab, and an App.axaml DataTemplate routing the model to the
view. PE-header / heap / table nodes still decompile as text — they get
flipped to CreateTab() one batch at a time in subsequent commits.
Assisted-by: Claude:claude-opus-4-7:Claude Code
Closes out Phase 1e: every CLI metadata table now has its own typed
leaf. Bytes-level table walking goes through MetadataReader.AsBlobReader
(public API) instead of the WPF Helpers' AsReadOnlySpan path; coded-token
columns are decoded by the new MetadataReaderHelpers shim, which
reflects against the same private TypeDefOrRefTag / HasFieldMarshalTag /
MemberForwardedTag.ConvertToHandle methods + ComputeCodedTokenSize that
the WPF host uses (a known forward-compatibility risk per the plan's
risk register).
Assisted-by: Claude:claude-opus-4-7:Claude Code
Replaces the universal placeholder for the 8 PDB-only metadata tables:
Document, MethodDebugInformation, LocalScope, LocalVariable,
LocalConstant, ImportScope, StateMachineMethod, CustomDebugInformation.
StateMachineMethod reads its rows directly via MetadataReader.AsBlobReader
(public API, no internals reflection); the rest follow the typed-handle
pattern used by the CorTables.
Assisted-by: Claude:claude-opus-4-7:Claude Code
Replaces the universal placeholder for StandAloneSig, DeclSecurity, and
File. These three are the last CorTables that System.Reflection.Metadata
exposes as direct enumerations / typed handles. The remaining ~12 tables
(InterfaceImpl, NestedClass, ClassLayout, FieldLayout, FieldRva,
FieldMarshal, ImplMap, MethodImpl, MethodSemantics, EventMap,
PropertyMap, the *Ptr family) read raw little-endian metadata bytes via
Helpers.GetValueLittleEndian + coded-token reflection helpers in the
WPF tree, so they wait for the Helpers infrastructure port.
Assisted-by: Claude:claude-opus-4-7:Claude Code
Replaces the universal placeholder for ModuleRef, TypeSpec, Constant,
Property, Event, GenericParam, GenericParamConstraint, MethodSpec,
ManifestResource, and ExportedType with their own typed leaves. Same
pattern as the prior CorTables batch — RID / Token / Offset followed
by the table's own column shape.
Assisted-by: Claude:claude-opus-4-7:Claude Code
Replaces the universal placeholder for Field, MethodDef, Param,
MemberRef, CustomAttribute, and Assembly with their own typed leaves.
Each carries the columns the WPF view shows (RID / Token / Offset / row
data) — the analogous tooltips and click handlers stay deferred to the
Phase-3 navigation work since Phase 1 only renders text. The remaining
25 CorTables continue to land as placeholders until the next pass.
Assisted-by: Claude:claude-opus-4-7:Claude Code
Adds the generic MetadataTableTreeNode<TEntry> base — caches LoadTable's
result and runs it through MetadataTextWriter for the Phase 1 text dump.
Ports Module / TypeRef / TypeDef / AssemblyRef as the first concrete
leaves; the remaining 31 CorTables and 8 DebugTables continue to land
through the universal placeholder until subsequent commits replace
them. Also fixes MetadataTextWriter to convert enum values to their
underlying integer before applying digit-bearing format strings, since
Enum.ToString rejects "X8".
Assisted-by: Claude:claude-opus-4-7:Claude Code
Adds the synthetic "Tables" folder that lazy-loads one leaf per
non-empty CLI metadata table, sitting between the PE headers and the
heaps to match WPF tree shape. The leaves are universal placeholders for
now — typed per-table viewers (35 CorTables + 8 DebugTables) replace
them in the next commit.
Assisted-by: Claude:claude-opus-4-7:Claude Code
Adds the four CLI heap views (#Strings, #US, #GUID, #Blob) under each
assembly's Metadata folder. Phase 1 caps each heap's text dump at 200
preview rows with a truncation footer; the full set arrives with the
DataGrid view in Phase 2. Heap rows are materialised lazily on first
demand so opening the Metadata folder doesn't pay for heaps the user
never visits.
Assisted-by: Claude:claude-opus-4-7:Claude Code
Adds the five PE-format leaves (DOS / COFF / Optional / DataDirectories
/ DebugDirectory) under each assembly's Metadata folder. Phase 1 renders
them as fixed-width text tables via Decompile; Phase 2 will swap to a
DataGrid tab. Introduces Entry / BitEntry / ColumnInfoAttribute and a
MetadataTextWriter helper that the heap and table nodes reuse.
Assisted-by: Claude:claude-opus-4-7:Claude Code
First slice of the Metadata Explorer port (Task 31). Adds a synthetic
MetadataTreeNode child to every loaded PE assembly, slotted before the
References folder to mirror WPF's AssemblyTreeNode ordering. The container
itself decompiles to the same metadata-summary text WPF's DumpMetadataInfo
emits — kind, version, debug header (if any), per-table row counts.
Assisted-by: Claude:claude-opus-4-7:Claude Code
Mirrors the WPF AssemblyTreeNode.SetPublicAPI recursive walk: a namespace
counts as public-API iff at least one type it contains is public-API. Inline
on NamespaceTreeNode itself as a lazy-evaluated cached aggregate (types don't
change accessibility at runtime), so the cell template's gray-foreground
binding picks up the right value on first render.
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
Mirrors the WPF host's nested ReloadAssembly entry. Same visibility shape
as Remove (every selected node must be an AssemblyTreeNode). Execute calls
AssemblyList.ReloadAssembly on each selection's path, snapshots the
pre-reload tree paths so that after the LoadedAssembly instances are swapped
out the model can re-select an equivalent node via FindNodeByPath. The
existing SelectedItem -> ShowSelectedNode plumbing then re-decompiles into
the active tab.
Assisted-by: Claude:claude-opus-4-7:Claude Code
- Suppress MEF002/CA1001/CA2213 in the csproj (intentional System.Composition
use and CTS-on-tab-page lifetime; CA1001/CA2213 already suppressed in
the WPF csproj).
- Drop the unused dockWorkspace parameter on the SaveCommand primary ctor.
- Add Language.MemberDescription helper that handles a null
DeclaringTypeDefinition — FakeMember-derived entities (the decompiler's
fallback for unresolved members) can carry one — so the four
DecompileMethod/Field/Property/Event fallbacks no longer pass null to
TypeToString.
- App.axaml.cs / ResourcesFileTreeNode.cs: small null-handling fixes.
- CSharpHighlightingTokenWriter.cs: nullable-annotate the optional
textOutput/locatable ctor params and the few helpers that legitimately
return null (GetCurrentDefinition, GetCurrentMemberReference,
ApplyTypeColor's color out-param).
Assisted-by: Claude:claude-opus-4-7:Claude Code
AvaloniaEdit's TextEditorOptions.RequireControlModifierForHyperlinkClick
only flows into its *built-in* LinkElementGenerator (via the
IBuiltinElementGenerator.FetchOptions hook the editor calls from
UpdateBuiltinElementGeneratorsFromOptions). Subclasses added to
ElementGenerators by hand inherit whatever the parameterless base
constructor sets — which is "true" — so the editor-level flag flipped in
DecompilerTextView didn't actually reach our generator.
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
Each tree-expansion, selection change, command execution, async wait and
key/mouse injection inside an [AvaloniaTest] now carries a short imperative
comment on the line above (e.g. "// expand typeNode", "// select methodA",
"// wait for assemblies to load", "// execute aboutCmd"). The comments are
the same scaffolding the manual debug-with-screenshot workflow uses to
follow what's happening at each breakpoint, surfaced into the committed
source so the tests are readable without the breakpoint markers attached.
Assisted-by: Claude:claude-opus-4-7:Claude Code
AssemblyTreeNode now overrides CanDelete / Delete / DeleteCore (matching
the WPF host: Delete unloads the assembly via AssemblyList.Unload, with
package-nested entries opting out via CanDelete=false) and exports a
nested RemoveAssembly context-menu entry tagged Header="_Remove".
Assisted-by: Claude:claude-opus-4-7:Claude Code
AssemblyListPane attaches a ContextMenu to its TreeGrid at construction
time. The menu's Opening handler builds entries from the
ContextMenuEntryRegistry against a fresh TextViewContext (the live
selection + the grid as OriginalSource), and cancels Opening when no
entry would be visible so an empty popup never flashes up.
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
CommandLineArguments was already parsed at App.OnFrameworkInitializationCompleted
but never read. AssemblyTreeModel grows HandleCommandLineArgumentsAsync that
applies the parsed values:
Assisted-by: Claude:claude-opus-4-7:Claude Code
Flips Editor.Options.RequireControlModifierForHyperlinkClick to false in
DecompilerTextView's constructor — that's the value AvaloniaEdit's built-in
LinkElementGenerator forwards onto every VisualLineLinkText it constructs,
so toggling it once at the editor level removes the Ctrl requirement for
every hyperlink in the decompiled output (in-app references, About-page
resource links, future external URLs alike).
Assisted-by: Claude:claude-opus-4-7:Claude Code
Generalises NavigationHistory<SharpTreeNode> into NavigationHistory<NavigationEntry>
with two subtypes — TreeNodeEntry (a tree-node selection in a specific tab)
and StaticPageEntry (a static page like About in a specific tab) — mirroring
the WPF host's NavigationState/ViewState pair.
Assisted-by: Claude:claude-opus-4-7:Claude Code
Replaces the previous WriteReference(string, Uri) approach with a
LinkElementGenerator subclass that scans the rendered document for the
target phrases ("MIT License" / "third-party notices"), matching the
WPF host's MyLinkElementGenerator pattern. The custom generator collection
travels through AvaloniaEditTextOutput → DecompilerTabPageModel.
CustomElementGenerators → DecompilerTextView (which installs and tears
them down per ApplyDocument).
Assisted-by: Claude:claude-opus-4-7:Claude Code
The "MIT License" and "third-party notices" phrases inside the About blurb
now write as ReferenceSegments carrying resource: URIs instead of plain
text. The new tab subscribes to NavigateRequested and opens the embedded
LICENSE / third-party-notices.txt content in a new tab on click.
Assisted-by: Claude:claude-opus-4-7:Claude Code
Replaces the NotImplementedDialog stub with a real AboutCommand that builds
an AvaloniaEditTextOutput (ILSpy version, .NET version, embedded
ILSpyAboutPage.txt) and routes it through DockWorkspace.OpenNewTab as a
DecompilerTabPageModel. Adds an IAboutPageAddition extension point so future
plugins can append to the page. ILSpyAboutPage.txt / third-party-notices.txt
/ LICENSE are embedded via the same shape WPF uses.
Assisted-by: Claude:claude-opus-4-7:Claude Code
Adds a leading paragraph describing what each test verifies and Arrange /
Act / Assert markers (split into numbered phases for multi-step tests) so
the intent of each fixture is readable from comments alone.
Assisted-by: Claude:claude-opus-4-7:Claude Code
ProDataGrid's HierarchicalModel kept its own per-wrapper expanded state and
ignored SharpTreeNode.IsExpanded — toggling the model property had no visible
effect, and clicking the chevron in the grid didn't update the model either.
Set HierarchicalOptions.IsExpandedPropertyPath = "IsExpanded" so ProDataGrid
reads/writes the property via reflection and observes INotifyPropertyChanged
on the source. Production code stays a one-liner; the imperative
NodeExpanded/PropertyChanged plumbing first attempt would have been ~80 lines.
Test exercises the model→grid direction.
Assisted-by: Claude:claude-opus-4-7:Claude Code
TestApp routes ILSpySettings to a fresh GUID dir under %TEMP%/ILSpy.Avalonia.Tests/
so production settings stay untouched, but the dirs were never removed —
hundreds piled up across runs. Register a ProcessExit handler that deletes
the active session's dir, plus sweep any leftovers from previous runs at
startup so an interrupted session doesn't strand a stale folder forever.
Both deletes are best-effort to avoid a Windows file-lock crash on shutdown.
Assisted-by: Claude:claude-opus-4-7:Claude Code
Both commands have correct implementations since the WPF main-menu port; this
locks the behaviour in. Clear: with a populated list, CanExecute is true and
Execute drains the list to zero. Remove-load-errors: drops a synthetic non-PE
file into the list, waits for HasLoadError (GetLoadResultAsync rethrows the
underlying BadImageFormatException so it isn't a usable wait probe), invokes
the command, asserts the broken entry is unloaded while every previously-valid
entry is preserved. Both confirmed red by temporarily no-op'ing Execute and
green again after restoring.
Assisted-by: Claude:claude-opus-4-7:Claude Code
Mirrors the WPF SharpTreeView's ApplicationCommands.Delete handling:
KeyDown on the tree DataGrid scoops up every selected AssemblyTreeNode and
calls AssemblyList.Unload on each. Snapshots the list before mutating because
Unload triggers CollectionChanged which clears the model selection mid-loop.
Non-assembly selections (types, methods, namespaces, …) are ignored — Del is
specifically for top-level entries today; richer per-node Delete semantics
can come with the broader IPlatformDataObject port. Test pins the dispatch.
Assisted-by: Claude:claude-opus-4-7:Claude Code
Mirrors WPF SaveCodeContextMenuEntry.Execute: when a single tree node is
selected, call its Save() override first (resource nodes already self-handle
the dialog + raw/ResX choice). Otherwise prompt for a path with the active
language's extension and re-decompile the node with FullDecompilation = true,
EscapeInvalidIdentifiers = true, writing the output as plain text via
ICSharpCode.Decompiler.PlainTextOutput on a background task.
Assisted-by: Claude:claude-opus-4-7:Claude Code
Single .NET classlib that embeds one resource of every type the Avalonia port
recognises — .xml / .xsd / .xslt / .xaml / .png / .bmp / .jpg / .gif / .ico /
.cur / .resources / .bin (unknown-extension fall-through). The image fixtures
render a 64×64 cream square with a blue circle and red diagonal stripe so the
inline preview is obviously decoded rather than blank; the .ico is multi-frame
(16/32/48) so future per-frame work has something to chew on.
Assisted-by: Claude:claude-opus-4-7:Claude Code
Override Save on ResourcesFileTreeNode to present a dual-format file picker
("Resources file" / "Resource XML") and dispatch by chosen extension; .resx
re-emits the entries via the bundled ICSharpCode.Decompiler.Util.ResXResourceWriter
while .resources copies the original byte stream. The base ResourceTreeNode's
inline Save button now dispatches through the virtual Save() so this override
is actually reached — previously it called the private SaveAsync directly,
making subclass overrides unreachable from the inline button. Tests pin the
WriteResX output and the FilePickers filter parsing.
Assisted-by: Claude:claude-opus-4-7:Claude Code
Replace the comment-line dump in ResourcesFileTreeNode.Decompile with two
AvaloniaUI DataGrid views — ResourceStringTable for string entries (Name/Value)
and ResourceObjectTable for everything else (Name/Value/Type, including
ResourceSerializedObject and primitive-typed values). otherEntries collection
plus SerializedObjectRepresentation row model are added on the node so
ResX export (next commit) can iterate the same shape. Test pins the behaviour:
a synthetic .resources stream produces three UIElements (Save button + two
grids) where the prior implementation produced one.
Assisted-by: Claude:claude-opus-4-7:Claude Code
XML / XSD / XSLT / XAML resources don't go through the C# decompiler so the
DecompilerTabPageModel has no pre-collected foldings — only HighlightingService
was kicking in. Run AvaloniaEdit's bundled XmlFoldingStrategy when the active
syntax extension is .xml so multi-line elements collapse from the gutter.
Assisted-by: Claude:claude-opus-4-7:Claude Code
Clicking an already-visible row was running ScrollIntoView+CenterRowInView via
the SelectionChanged → model PropertyChanged round-trip, yanking the viewport.
Add a clickInProgress flag separate from syncingSelection: clickInProgress is
set in OnTreeGridSelectionChanged and cleared at Background priority alongside
syncingSelection, so the model's two PropertyChanged notifications (Clear+Add
on SelectedItems) both bail out — but hyperlink / Back/Forward / search-hit
navigation, which never enters OnTreeGridSelectionChanged, still scrolls the
target into view.
Assisted-by: Claude:claude-opus-4-7:Claude Code