Simple theme ships no Dark resource dictionary, so flipping
RequestedThemeVariant alone is a no-op against any surface whose
brushes are hardcoded or pulled from a Simple key. We can't swap
themes (Simple is the deliberate visual identity), but we can layer
our own ThemeDictionaries on top so the surfaces ILSpy owns directly
respond to View > Theme.
Introduce a single block of Light / Dark brush pairs under
Application.Resources.ThemeDictionaries (App.axaml), then route the
hardcoded brushes in the editor view, the toolbar, the zoom overlay,
and tooltips through DynamicResource lookups against those keys.
Surfaces wired:
- Decompiler text view background and wait-adorner overlay.
- Main toolbar background, bottom border, separator hairlines,
combobox border, and the disabled-button content fills.
- Zoom-buttons overlay background and border.
- Tooltips (background, foreground, border) via a Style selector
since Simple paints them via hardcoded setters that don't expose
a brush key.
Simple-themed chrome (menu, tree, scrollbars, status bar, dock
splitters) remains Light in both variants. That's partial dark mode
by design -- covering it requires either swapping to Fluent (which
costs the Simple visual identity, and a separate test showed the
chrome regressing under Fluent) or redeclaring every Simple resource
key per variant, which is a much larger effort. Follow-ups will
extend coverage one surface at a time.
Light values match what each file shipped (no visible diff in the
default theme). Dark values follow the VS-style palette:
editor #1E1E1E
toolbar #2D2D30 border #3F3F46
tooltip #252526 border #3F3F46 foreground #DCDCDC
zoom overlay #E01E1E1E with translucent gray border
Assisted-by: Claude:claude-opus-4-7:Claude Code
FieldTreeNode used the base Field icon for every kind of field. The
WPF version distinguished EnumValue and Literal so a reader could
tell at a glance whether a member was a backing field, a const, or
an enum case.
Mirror the master logic (master:ILSpy/TreeNodes/FieldTreeNode.cs:60),
but with two scope cuts:
- Use Images.Enum for enum values. We don't have a dedicated
EnumValue.svg, and the type-level Enum glyph reads correctly in
context (an enum case sits inside an Enum node).
- Skip the FieldReadOnly branch entirely -- no asset exists yet and
inventing one isn't in scope for this commit. Readonly fields keep
falling through to the base Field icon, the same as before.
Detection rule for an enum value: DeclaringType.Kind is Enum AND
ReturnType.Kind is Enum. The second clause is what excludes the
synthesised int32 value__ backing field that every enum carries --
that field's return type is Int32, not the enum itself.
Assisted-by: Claude:claude-opus-4-7:Claude Code
Matches the WPF behaviour at master:ILSpy/AssemblyTree/AssemblyTreeModel.cs:440
where DEBUG builds get "ILSpy {DecompilerVersionInfo.FullVersion}" so the
developer can tell from a glance which build the running window came from
(especially handy when the avalonia-preview tag floats forward and several
windows are open at once). Release builds keep the bare "ILSpy" title.
The title is bound from MainWindowViewModel.Title (axaml: Title="{Binding
Title}"), so threading the version through the VM is the natural seam --
no axaml change and the bound value can later be made notifying if we
want to mix in the active assembly-list name.
Assisted-by: Claude:claude-opus-4-7:Claude Code
The Simple theme's SimpleMenuScrollViewer template wraps menu items
in a ScrollViewer whose Top / Bottom docked RepeatButtons are the
classic Win9x scroll chevrons. Their IsVisible binding feeds through
MenuScrollingVisibilityConverter which does an AreClose check on
Extent vs Viewport. At any non-100% DPI scale (125%, 150%, ...) the
layout-rounding pass leaves a small gap so AreClose returns false
and the converter declares "content overflows," then both chevrons
render even when the menu only has two items and obviously fits.
Bug tracked upstream as AvaloniaUI/Avalonia#9501; the partial fix in
PR #11916 (ScrollContentPresenter rounding) is already in 12.0.3 but
doesn't widen the converter's tolerance, so the symptom persists.
Hide both chevrons unconditionally inside any menu's scroll viewer.
Safe because: (a) the popup positioner already clamps menu height to
the available screen area, and (b) ILSpy's menus are short enough
that real overflow doesn't occur on any sane display. Height = 0
collapses the DockPanel slot the chevrons would otherwise reserve
before the visibility binding propagates.
Two selectors (MenuFlyoutPresenter and ContextMenu) cover both
managed-menu paths; the inline NativeMenuBar on Linux / Windows
routes through the same SimpleMenuScrollViewer template, so the
top-level menus pick this up too.
Assisted-by: Claude:claude-opus-4-7:Claude Code
Two assembly-tree right-click entries existed in master but were
dropped in the avalonia port:
* Load Dependencies (master/AssemblyTreeNode.cs:602). Resolves every
AssemblyReference on the selected assemblies through their resolver
in parallel; turns unresolved-type placeholders in subsequent
decompiles into real linked nodes. Same IsLoadedAsValidAssembly gate
master used; placed inside AssemblyTreeNode.cs as a nested type to
match the Remove / Reload pattern. Category = Dependencies, Order =
700 -- sits between Edit (600-699) and the trailing null group
(Reload 900, Remove 910).
* Open Command Line Here (master/AssemblyTreeNode.cs:734). Walks the
selection up to the enclosing AssemblyTreeNode and opens a terminal
cd'd to its containing folder. Master's GlobalUtils.OpenTerminalAt
was Windows-only (cmd.exe); ported cross-platform: cmd.exe on
Windows, `open -a Terminal` on macOS, $TERMINAL / xdg standard list
fallback on Linux. Lives in Commands/ as its own file, mirroring
OpenContainingFolderContextMenuEntry. Category = Shell, Order = 510
(just after OpenContainingFolder).
Also: DecompileToNewPanel was missing master's "MMB" InputGestureText
annotation (the middle-mouse-button affordance is what users see in
the menu). Add it to the existing attribute.
Assisted-by: Claude:claude-opus-4-7:Claude Code
Three friction points:
* ShowInMetadataContextMenuEntry used the literal string "Metadata"
for Category, breaking the codebase's nameof(Resources.X) convention
and missing the chance to share a category with the navigation-style
entries we just established in the tree menu. Move to "Navigation",
Order = 200 (matching the tree-side ShowInMetadata sibling).
* DecompileMetadataRowInNewTabCommand had the same literal "Metadata"
category plus Order = 110, which collided with the editor Copy/Select
band (also 100/110). Move to "Navigation" Order = 100 so it parallels
the tree-side Decompile-to-new-panel entry.
* ShowCFGContextMenuEntry had no Category and no Order, so it floated
to the bottom of the implicit null group in the DEBUG-builds menu.
Give it Category = "Diagnostics" + Order = 9000 to match the pattern
we use elsewhere for DEBUG-only entries.
Adding a Copy.svg asset for the Copy / Select editor entries deferred
to a separate commit -- it's an asset import, not a metadata change.
Assisted-by: Claude:claude-opus-4-7: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
SortAssemblyListCommand and CollapseAllCommand both left ToolbarOrder
at its default of 0, so their relative position on the toolbar fell
out of MEF discovery order. Make it explicit: Sort = 0, Collapse = 1.
Assisted-by: Claude:claude-opus-4-7:Claude Code
macOS convention puts About / Check for Updates under the bold app-
named menu next to the Apple logo, not under a separate top-level Help
menu. Add a PromoteHelpToMacAppMenu pass that runs only on macOS:
1. Find the _Help submenu in topLevelByTag (populated by
AppendRegistryCommands when it sees the Help-targeted MEF commands).
2. Move its children into a new NativeMenu and SetMenu it on
Application.Current. Avalonia's Cocoa exporter watches
NativeMenu.MenuProperty on the Application as the channel for the
bold app menu, and our items get prepended above its auto-inserted
Services / Hide / Quit block.
3. Remove _Help from the window menu so the items don't appear twice.
On Windows / Linux the gate skips and the _Help top-level renders
inline as before.
The CheckForUpdatesCommand / AboutCommand attributes still declare
ParentMenuID = _Help -- they remain Help-categorised in metadata, just
re-routed at attachment time on macOS. No special Cocoa metadata
needed; the exporter picks up plain NativeMenuItems.
Assisted-by: Claude:claude-opus-4-7:Claude Code
CheckUpdates and About both lacked MenuCategory -- they fell into the
empty default category. Add MenuCategory = "Help" so the two entries
form a declared group (separators would appear if any other category
joined Help later), and renumber CheckUpdates from the arbitrary 5000
to 0 so its position relative to future siblings is meaningful.
About keeps the 99999 sentinel that matches File > Exit's convention
for "stays pinned to the bottom no matter what's inserted above."
No icons added: ILSpy/Images.cs has no Help / About / Info / Update /
Question field, so wiring would need new SVG assets -- out of scope.
Assisted-by: Claude:claude-opus-4-7:Claude Code
CloseAllDocuments, ResetLayout, and PinCurrentTab all had no
MenuCategory and no MenuOrder, so their relative order fell out of
whatever sequence the C# reflection enumerator returned for their
attributes -- not stable across refactors or partial-class layouts.
Group them into a single MenuCategory = "Window" with the three
explicit MenuOrder slots, sequenced non-destructive first then
destructive-reversible then destructive-nuke:
0 Pin current tab (toggles state; reversible by Unpin)
1 Close all documents (closes tabs; reopenable via tree click)
2 Reset layout (rebuilds the dock root; loses pin / tool-pane positions)
The tool-pane toggles (Search, Assemblies, Analyzer, Debug Steps)
and the open-document tab radios already sit below this block via
AppendWindowDynamicContent + AppendTabSection -- separators between
them stay where they were.
Assisted-by: Claude:claude-opus-4-7:Claude Code
DecompilerVersionInfo.template.cs is the source-of-truth for the
runtime version constants -- update-assemblyinfo.ps1 rewrites the
generated DecompilerVersionInfo.cs from it on every build, picking
up the live git revision and commit hash for the $INSERT...$ slots.
Assisted-by: Claude:claude-opus-4-7:Claude Code
The MEF block of the View menu used MenuOrder = 100/101 for Back/
Forward but no MenuOrder on Sort assembly list / Collapse all tree
nodes, so the latter two implicitly defaulted to 0 and ended up
appearing before Back/Forward despite their numerically larger
explicit order. The intent reads better the other way around:
Back/Forward are high-frequency keyboard-driven actions (Alt+Left/
Right) and belong adjacent to the show-radios block, while Sort and
Collapse are tree-operation bulk actions that belong further down.
Use the same non-overlapping MenuOrder ranges that File now follows:
Navigation: 0 .. 9 (Back, Forward)
Tree: 10 .. 19 (Sort, Collapse)
Options: 999 (Options)
Sort and Collapse moved from MenuCategory = "View" to "Tree" because
the category identifier should describe the operand -- both act on
the assembly tree, neither is a generic "view" action.
Assisted-by: Claude:claude-opus-4-7:Claude Code
FileCommands.cs already declared MenuIcon = "Images/AssemblyListGAC"
on the Open-from-GAC main-menu command, but the reflection lookup
in Images.cs found no field of that name so the menu item rendered
without an icon. Port the SVG asset from master/ILSpy/Images/ into
Assets/Icons/ and add the static IImage registration so the metadata
flow completes.
Assisted-by: Claude:claude-opus-4-7:Claude Code
The File menu's MenuCategory metadata was inherited from the WPF tree
and showed two friction points: the four DEBUG diagnostics
(Decompile All / Disassemble All / Decompile 100x / Dump PDB) lived
in the Open category, so they appeared right after Reload with no
visual separator -- the menu read as if Decompile All was a variant
of Open; and the assembly-list mutators (Remove with load errors,
Clear assembly list, Manage assembly lists) were split across two
categories (Remove and Open) even though they all operate on the
same object.
Reshape into five categories with non-overlapping MenuOrder ranges
so category order is deterministic instead of falling out of MEF
discovery order:
Open (Open, GAC, Reload) MenuOrder 0 .. 9
AssemblyList (Manage, Remove, Clear) MenuOrder 10 .. 19
Save (Save Code, Generate PDB) MenuOrder 20 .. 29
Debug (DEBUG -- ...) MenuOrder 30 .. 39
Exit (Exit) MenuOrder 99999
The "AssemblyList" category replaces the previous "Remove" identifier
(carrying Remove-with-load-errors + Clear) since both members and the
newly-relocated Manage entry are list-of-assemblies operations rather
than pure removal. The category value is a string literal because it
is only ever used as a grouping key by ContextMenuProvider /
MainMenu.AppendRegistryCommands -- never displayed.
Stride-10 within each category gives room to insert later items
without renumbering. The earlier 2.5 / 2.55 / 2.6 / 2.7 fractional
scheme had no category-boundary contract: the existing 2.6 tie
between Remove-with-load-errors and Clear meant their order between
each other depended on whatever order reflection enumerated the
fields in. Generate portable PDB previously had no MenuOrder at all
so its position relative to Save Code was MEF-implementation defined.
Assisted-by: Claude:claude-opus-4-7:Claude Code
Master's manual ILSpy/Properties/AssemblyInfo.cs was dropped during
the Avalonia port; the SDK's auto-generated AssemblyInfo took over
with default-zero versions. As a result the built ILSpy.dll's
assembly metadata read 1.0.0.0 even though DecompilerVersionInfo
(the static class generated from the .template by the build pipeline)
had the right values. Anything that queried Assembly.GetName().Version
or AssemblyInformationalVersionAttribute -- update checks, crash
report context, the title-bar version line we still need to wire --
got the SDK default, not the real revision.
Port the cross-platform-safe slice of master's AssemblyInfo.cs:
Title/Description/Company/Product/Copyright, ComVisible(false), the
two version attributes pointing at DecompilerVersionInfo,
NeutralResourcesLanguage, InternalsVisibleTo("ILSpy.Tests"), and the
CA2243 suppression. Deliberately skipped: [SupportedOSPlatform(
"Windows7.0")] and [TargetPlatform("Windows10.0")] (would re-make the
assembly Windows-only at metadata level and emit CA1416 noise for
cross-platform code paths), [InternalsVisibleTo("ILSpy.AddIn")] (the
AddIn project isn't in the avalonia build path), and the WPF-only
Properties/WPFAssemblyInfo.cs ThemeInfo (System.Windows-typed).
The single-line ILSpy/AssemblyInfo.cs stub that only carried the
ILSpy.Tests InternalsVisibleTo gets folded into the new file and
removed, matching master's layout.
GenerateAssemblyInfo=false in the csproj is the required companion --
without it the SDK emits a second AssemblyVersion attribute and
compilation fails with CS0579.
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
The trailing HeaderedContentControl in the Display panel (Other -> Sort
results by fitness) sat 15 px below the ScrollViewer.Extent's reported
height: the outer StackPanel's measure under-counted that last child's
inner content, so MaxYOffset never grew enough to scroll the checkbox
into view. The Reset-to-defaults border below the ScrollViewer covered
the gap visually, making the checkbox look obscured.
ScrollViewer.Padding alone can't compensate because Avalonia 12's
Simple-theme ScrollContentPresenter collapses Padding on the axis the
scrollbar lives on -- the horizontal 6 px stuck, the vertical 6 px was
dropped. Wrapping the content in a Border with explicit Padding makes
the padding part of the StackPanel's measured extent, and the last
child becomes reachable. Applied to both panels that use a top-level
StackPanel under a ScrollViewer; Misc is unaffected (no ScrollViewer,
two checkboxes never overflow).
The new OptionsPageScrollReachTests.Last_Item_In_Display_Panel_Is_-
Reachable_At_Max_Scroll opens the Options tab, scrolls the Display
panel to ScrollViewer.Extent.Height, and asserts the "Sort results by
fitness" CheckBox's rendered bottom sits at or above the Reset border's
top in window-coordinate space.
Assisted-by: Claude:claude-opus-4-7:Claude Code
Avalonia's Window.Icon doesn't reach the macOS Dock, so the app needs a
proper .app bundle: an ILSpy.icns produced from the existing artwork, an
Info.plist with CFBundleIconFile=ILSpy and CFBundleIdentifier
net.sharpdevelop.ilspy, and a BuildMacAppBundle target that runs
AfterTargets="Publish" for any osx-* RuntimeIdentifier. The target lays
out the standard Contents/MacOS + Contents/Resources structure under
bin/<config>/<tfm>/<rid>/ILSpy.app/ and chmods the apphost executable
so macOS LaunchServices will accept it.
The Avalonia resource glob ('Assets/**') is told to skip the bundle
inputs since they're consumed by the OS loader rather than at runtime
via avares://. The lock files pick up RID-specific runtime asset entries
that NuGet adds when an osx RID is in the project's RuntimeIdentifiers.
The slnf name was a leftover from when ILSpy.csproj was WPF; its
contents are now the Avalonia desktop projects. Drop the dead
ILSpy.BamlDecompiler.csproj ref (the directory no longer holds a
project after the engine moved to ICSharpCode.BamlDecompiler) and the
out-of-scope items (TestPlugin, ILSpy.BamlDecompiler.Tests) -- they're
in neither ILSpy.sln nor any active build flow, so the slnf filter
surfacing them in IDE listings was just noise.
Assisted-by: Claude:claude-opus-4-7:Claude Code
If anything thrown by App.OnFrameworkInitializationCompleted propagated
past it -- a CompositionFailedException from an unresolvable plugin
dependency, say -- the process died before the dispatcher pump started,
so the AppDomain.UnhandledException dialog wired by GlobalExceptionHandler
never had a chance to surface. The user saw a silent exit; the only
trace was a stderr stack on terminal launches and nothing at all when
clicking the icon. Wrap the critical resolution paths so anything that
goes wrong lands in StartupExceptions, and on any failure swap the
MainWindow for a StartupErrorWindow that displays the captured text.
Assisted-by: Claude:claude-opus-4-7:Claude Code
The MainMenu UserControl previously built a regular Avalonia Menu of
MenuItems, which on macOS would render inline in the window instead
of in the system menu bar -- not what Mac users expect. Avalonia's
NativeMenu + NativeMenuBar is the cross-platform abstraction: on
macOS the menu is projected into the system bar, on Windows / Linux
NativeMenuBar's presenter renders the same items inline. The MEF
registry, theme submenu, tool-pane toggles, and dynamic tab list all
flow through unchanged; only the leaf widget type swaps from MenuItem
to NativeMenuItem. KeyModifiers.Control is translated to Meta on
macOS so the system menu bar shows Cmd glyphs instead of Ctrl.
Assisted-by: Claude:claude-opus-4-7:Claude Code
Previous rule mandated "Avalonia: <lowercase phrase>" subjects, which
encouraged restating the area at the cost of subject-line budget. The
prefix has been stripped from all 336 branch commits; update the rule
so future commits match.
Assisted-by: Claude:claude-opus-4-7:Claude Code
R2R was the only legacy plugin still keeping the solution-level build red
(27 errors against the Avalonia tree). Port mirrors the four-corner
substitution used for the rest of the rewrite:
Assisted-by: Claude:claude-opus-4-7:Claude Code
When the user removes an assembly while a decompile is still in flight,
the title-refresh paths (spinner tick, post-Task.Run InvokeAsync,
StopSpinner) walk node.Text -> LoadedAssembly.Text ->
metadata.GetAssemblyDefinition().Version, reading directly from the
PE file's MemoryMappedFile. AssemblyList.Unload calls Dispose
synchronously and unmaps that file; any continuation that lands on the
UI thread after Dispose dereferences freed pages and the CLR reports
the AV through FailFastIfCorruptingStateException -- a catch block
cannot intervene in .NET 5+.
Assisted-by: Claude:claude-opus-4-7:Claude Code
The four `WaitForDecompiledTextAsync` calls in PreviewTabPromotionTests
asserted only on dock structure -- SourceNode, IsPreview, tab count,
Content reference -- never on decompiled Text. Those values are set
synchronously by ShowSelectedNode before DecompileAsync's first await
returns, so the ~25s wait was pure dead weight. Worse, the fire-and-forget
DecompileAsync kept chewing CPU after the test returned and contended on
the ThreadPool with the next test's decompile, which is what pushed the
same workload from 12-16s in isolation to 24-28s in full-suite -- right
up against the 30s budget. One slow GC pause and PinCurrentTab_Flips
timed out at 30.997s exactly.
Mirrors the master-side addition in commit 82edef3bc (`CSharpLanguage.cs`
in the WPF host) so the Avalonia version stays in feature parity with the
shared decompiler's CSharp15_0 enum. The RuntimeAsync setting itself rides
in via the shared `ICSharpCode.Decompiler` library; only the dropdown entry
needed mirroring on the UI side.
Every_ExportToolbarCommand_Resolves_An_Icon used GetField against
ILSpy.Avalonia.Images.Images to verify every [ExportToolbarCommand]'s
ToolbarIcon resolves to a live IImage. Images.Images currently
declares its icons as static readonly fields, but a future refactor
could lazify them into properties (we explored that path earlier in
this session before reverting). Accept either FieldInfo or
PropertyInfo so the test keeps holding under that refactor.
Assisted-by: Claude:claude-opus-4-7:Claude Code
AppLog.cs flips the Startup category from default-on to default-off:
all categories now require an explicit opt-in via the ILSPY_LOG
environment variable (e.g. ILSPY_LOG=Startup) or a runtime
AppLog.Enable("Startup") call. Production launches stay silent and
incur near-zero overhead — IsEnabled short-circuits before any
allocation, the %TEMP%\ilspy-avalonia.log file is only created on
first emitted line.
Assisted-by: Claude:claude-opus-4-7:Claude Code
The InitMainMenu build path already inserts Separators between
MenuCategory groups (Open / Save / Remove / Exit in File, View /
Navigation / Options in View, etc.). The Simple-theme default style
renders them as a 1px line at very low contrast — barely visible
against the menu background, which is why "no groups in the menus"
was a reasonable read.
Assisted-by: Claude:claude-opus-4-7:Claude Code
Previously pin used an explicit 22x22 size, custom padding, and a
Button.preview-pin class with hand-rolled #33000000/#55000000 hover
tints. The close button (its sibling in the tab strip) is a plain
Avalonia.Controls.Button with a ControlTheme applied by Dock's tab
template — no classes, no local sizing. Visual mismatch was noticeable.
Assisted-by: Claude:claude-opus-4-7:Claude Code
The ShowIdentical toggle was wired model-side
(CompareTabPageModel.OnShowIdenticalChanged → RootEntry.EnsureChildrenFiltered)
and view-side (Rebind on PropertyChanged), and the cascade was
correctly setting IsHidden via ComparisonEntryTreeNode.Filter. But the
HierarchicalModel's ChildrenSelector and SetRoots passed node.Children
through unfiltered — so IsHidden flags had no effect on what the
DataGrid actually rendered. Toggling the checkbox looked like a no-op.
Assisted-by: Claude:claude-opus-4-7:Claude Code
The Language and Language-Version pickers in the main toolbar have no
effect when the active tab renders PE-header / metadata-table fields
straight from the metadata, or shows a structural compare diff —
language choice doesn't change what's drawn. Mirrors WPF's per-tab
TabPageModel.SupportsLanguageSwitching flag.
Assisted-by: Claude:claude-opus-4-7:Claude Code
VS-style preview-tab semantics, refined per user spec: pinning a tab
should freeze its contents in place but NOT immediately spawn a fresh
preview tab beside it. The new preview tab opens lazily, on the next
tree-node selection that finds the active tab frozen — at which point
the new content lands in a brand-new tab and the pinned one is left
untouched.
Assisted-by: Claude:claude-opus-4-7:Claude Code
The sweep fires LoadedAssembly.GetLoadResultAsync on every sibling
assembly so users see icons "fill in" rather than each one showing
a loading state until first click. As written the sweep was firing
500 ms after TreeReady — which lands inside the first decompile's
Dispatcher.InvokeAsync window, triggering Server-GC pauses on the
UI thread during apply-text marshal-back. Measured: +460 ms FirstText
vs sweep-disabled on a single-type decompile workload.
Assisted-by: Claude:claude-opus-4-7:Claude Code
When --navigateto (-n) is supplied on the command line the user is
asking us to navigate to a specific entity. Today's code restores the
previously-saved tree path AND then runs the explicit target, which
produces two concurrent decompiles racing on SelectedItem (last write
wins). The saved decompile contaminates perf measurements taken via
`-n T:Some.Type` and adds GC pressure to startup for no user benefit.
Assisted-by: Claude:claude-opus-4-7:Claude Code
The polymorphism modifier on the dock-layout JsonSerializerOptions
ran a fresh AppDomain.GetAssemblies() + SelectMany(GetTypes()) +
IsAssignableFrom walk for every polymorphic base type it built
JsonTypeInfo for — IDockable, IDock, IRootDock, IDockWindow,
IDocumentTemplate, IToolTemplate. Six independent passes over
~100 loaded assemblies, ~500 ms total on warm starts.
Assisted-by: Claude:claude-opus-4-7:Claude Code
Workstation GC pauses every managed thread on Gen-1/2 collections,
so background allocations (decompiler work, layout deserialise,
search streaming) stall the UI thread for the duration of the pause.
Server GC spawns one heap per CPU core with dedicated GC threads;
collections of one heap don't pause the others. Concurrent stays on
so even server collections don't stop-the-world for long.
Assisted-by: Claude:claude-opus-4-7:Claude Code
Preview tab semantics. ContentTabPage gains IsPreview; the persistent MainTab
starts preview (tree-node clicks replace its Content in place). The user pins
via Window menu, right-click context menu, or inline pin icon — the
just-pinned tab keeps its content/identity and a fresh preview MainTab spawns.
Carve-out tabs (Open in new tab, Options) are born pinned and survive tree
selections.
Assisted-by: Claude:claude-opus-4-7:Claude Code
DisplaySettings.SortResults was persisted but unused — RunningSearch hardcoded
ComparerByFitness. Now SearchPaneModel.RestartSearch reads the setting at
start-of-search and passes either ComparerByFitness (default) or ComparerByName
into RunningSearch. Capturing at start matches WPF — mid-run toggles only
affect the next search.
Assisted-by: Claude:claude-opus-4-7:Claude Code
Adds TabPageMenuItem (mirrors ToolPaneMenuItem) and a live ObservableCollection
on DockWorkspace kept in sync with factory.Documents.VisibleDockables. MainMenu
appends a separator + radio-style MenuItem per tab, with Header bound to Title
and IsChecked bound to IsActive. WPF parity for the previously-skipped tabs
section of the Window menu.
Assisted-by: Claude:claude-opus-4-7:Claude Code
WPF's Open-from-GAC dialog uses a ListView with five SortableGridViewColumns
(Reference Name / Version / Culture / Public Key Token / Location), each
click-to-sort, with the initial sort by name ascending. The Avalonia port
had reduced this to a one-column ListBox showing the raw ToString() —
all field-level information mashed into a single TextBlock, no per-field
sort, no Location column, no localised strings.
Assisted-by: Claude:claude-opus-4-7:Claude Code
User-reported: editor hover tooltips were not wide enough to show full
method signatures. Root cause was a three-way layout interaction —
DocumentationRenderer.AddSignatureBlock created the signature
SelectableTextBlock with TextWrapping.NoWrap, the outer Border capped
MaxWidth at 600px, and the wrapping ScrollViewer disabled horizontal
scrolling. So anything past 600px was simply clipped, with no way to
reveal the rest.
Assisted-by: Claude:claude-opus-4-7:Claude Code
WPF parity: under DEBUG, the language dropdown gains one extra entry per
C# AST transform — "C# - no transforms", "C# - after FirstTransformName",
… "C# - after LastTransformName" — so a developer can pick a step and
see the decompiler's mid-pipeline AST output.
Assisted-by: Claude:claude-opus-4-7:Claude Code
User-reported: Ctrl+LMB on an assembly-tree row both toggled the row in
the multi-selection AND opened a new decompiler tab. ProDataGrid's
Extended-selection mode treats Ctrl+LMB as a toggle, and
OnTreeGridPointerPressed also treated it as "open in new tab", so every
Ctrl+click fired both effects regardless of the e.Handled=true on our
handler — selection lives in the DataGrid template's own pre-bubble
pipeline.
Assisted-by: Claude:claude-opus-4-7:Claude Code
User-reported: opening multiple document tabs and restarting brings them
all back broken except the first one.
Assisted-by: Claude:claude-opus-4-7:Claude Code
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