The two were identical bar the skip predicate (the home tab vs the kept
tab); both snapshot the visible documents and route each through CloseTab.
Extract the shared loop as CloseTabsWhere(predicate).
Assisted-by: Claude:claude-opus-4-8:Claude Code
The WPF host attached a registry-driven context menu to the search list
(ContextMenuProvider.Add) and showed a tooltip on every result column;
the Avalonia port carried neither. Right-clicking a result now opens the
same menu the trees use -- the selected result's entity is handed to the
entries via TextViewContext.Reference, so Analyze and the scope-search
entries light up -- and the Location/Assembly cells regain their
full-text tooltips (the Name cell already showed the file path per
Fix#1263).
Assisted-by: Claude:claude-opus-4-8:Claude Code
The TryGetExport consolidation missed five more copies of the
GetExport-or-null idiom in the assembly-list pane and analyzer tree view
(TryGetLanguageSettings / TryGetContextMenuRegistry /
TryGetAnalyzerTreeViewModel plus an inline OpenNodeInNewTab guard).
Route them through AppComposition.TryGetExport like the rest.
Assisted-by: Claude:claude-opus-4-8:Claude Code
The classic-desktop lifetime cast for the dialog owner, the clipboard and
application shutdown was repeated across the file commands, file pickers,
project export, the PDB selector, the taskbar service and the analyzer
copy entries. Replace those copies with a single UiContext exposing
MainWindow, Clipboard and Shutdown.
Assisted-by: Claude:claude-opus-4-8:Claude Code
The explorer.exe / open / xdg-open switch for opening a folder, revealing
a file, or opening a file with its default app was copied across eight
commands (export, save, PDB, diagram, package extraction, CFG diagram).
Replace those copies with one ShellHelper exposing OpenFolder, RevealFile
and OpenWithDefaultApplication, and route every site through it.
Assisted-by: Claude:claude-opus-4-8:Claude Code
The decompiler text view's lastTooltipSegment was assigned on hover and
cleared on close but never read anywhere, so it tracked nothing. Remove
the field and its three assignments.
Assisted-by: Claude:claude-opus-4-8:Claude Code
The WPF host carried two ILSpy.csproj build steps the Avalonia port left
behind. ApplyStackExtension runs editbin /stack:16777216 on the apphost so
deeply nested types/expressions don't overflow the 1 MB main-thread stack
during decompilation (the managed runtime can't grow the main thread's
stack at run time on Windows). SortResX keeps the .resx entries sorted so
localisation diffs stay clean; its script was still in BuildTools but no
target invoked it.
Both are gated to do nothing off Windows / without the MSVC tools / on CI,
so they only act in the same local-Windows scenario they did before. The
VC-tools props import is restored alongside, since it is what populates
$(VCToolsVersion) for the editbin gate.
Assisted-by: Claude:claude-opus-4-8:Claude Code
Eleven call sites each wrapped AppComposition.Current.GetExport<T>() in a
try/catch that returned null, to survive design-time previews and minimal
test hosts where the composition host isn't up. Replace those copies with
one AppComposition.TryGetExport<T>()/TryGetExports<T>() and inline it at
every site, deleting the per-class wrappers.
The shared helper does not blanket-swallow: host absence is checked
explicitly (the static field, not the throwing Current), and the built-in
TryGetExport(out) reports a missing export as false. So an export that is
present but genuinely broken now surfaces its activation error instead of
being silently turned into null.
Assisted-by: Claude:claude-opus-4-8:Claude Code
The File-menu entry was a stub that popped a "not implemented" dialog,
while the working generation logic lived only in the assembly context-
menu entry. Lift that logic into a shared PdbGenerator and route both
entry points through it, so the menu command now generates PDBs for the
selected assemblies (enabled only when the selection holds a valid one).
This was the last NotImplementedDialog caller, so the dialog is removed.
Assisted-by: Claude:claude-opus-4-8:Claude Code
The reflection-driven column builder turned every public property into a
column, so each {Column}Tooltip companion (NameTooltip, FlagsTooltip,
...) rendered as its own raw column next to the column it describes. That
text is already surfaced as the sibling cell's hover tooltip by
MetadataCellTooltip, which reflects the property off the row directly, so
dropping these properties from the column set leaves the tooltips intact
and removes the duplicate columns.
Assisted-by: Claude:claude-opus-4-8:Claude Code
DockWorkspace keeps the last remaining document un-closeable (CanClose
false) so the document area can't be emptied, and Dock's own close
button already honours that. The context-menu Close entry did not, so a
right-click still offered to close the final tab. Bind its IsEnabled to
the tab's CanClose, which is observable, so it tracks tabs opening and
closing.
Assisted-by: Claude:claude-opus-4-8:Claude Code
The document tab strip's multi-row layout and the mouse-wheel gesture
that toggles it were only reachable by the wheel itself, with no
discoverable UI and no way to opt out of the gesture. Surface both as
checkboxes under a new "Tab options" group on the Display page, and add
a MouseWheelTogglesTabStripRows setting (default on) so the wheel toggle
can be turned off while keeping the persisted multi-row choice.
Assisted-by: Claude:claude-opus-4-8:Claude Code
Clone / Rename / Delete / Select act on the selected list but were always
enabled, so clicking them with nothing selected silently no-op'd (the
handlers already guarded on SelectedListName). Bind their IsEnabled to the
list's selection via ObjectConverters.IsNotNull so they disable until a list
is picked; New / Reset / Add-preconfigured / Close stay enabled.
Assisted-by: Claude:claude-opus-4-8:Claude Code
SharpTreeView is a ListBox with AutoScrollToSelectedItem left at its default
(true), so it chased the selected row whenever its index shifted: expanding an
unrelated node pushed the selection off-screen and the ListBox yanked the
viewport back to it, fighting the expand's own reveal -- the "weird scrolling".
The rule is that a user mutating a control directly should not have the app
mutate the view too; only navigation from a *different* control (search
results, code/metadata links, analyzer nodes, Back/forward) may sync the tree.
The app already does that sync explicitly through the model
(TreeSelectionBinder -> CenterNodeInView), so the ListBox's own auto-scroll is
redundant and wrong for in-tree actions. Disable it.
Assisted-by: Claude:claude-opus-4-8:Claude Code
The search panel rendered hits in a bare ListBox with no headers, so the only
ordering was the fixed fitness/name streaming order chosen at search time --
the user could not re-sort by Name, Location or Assembly. Replace the ListBox
with the app's standard sortable Avalonia DataGrid (mirroring OpenFromGacDialog
/ MetadataTablePage): three template columns that keep the per-field icons,
each with a SortMemberPath so clicking a header sorts by Name / Location /
Assembly. No initial column sort is applied, so the default fitness ranking is
preserved until the user clicks a header.
The code-behind is unchanged: ListBox and DataGrid share the
SelectingItemsControl surface, so the existing selection / activation /
keyboard / middle-click handlers port as-is (verified by the existing search
niceties tests now running against the DataGrid).
Assisted-by: Claude:claude-opus-4-8:Claude Code
The Export Project/Solution, Save Code and Compare tests decompiled whatever
the default assembly list seeds -- System.Linq, CoreLib, the Uri assembly --
so each spent ~10s purely on type count (Solution/CreateSolution/SaveCode/
DecompileAssembly ~10s, CompareView ~4.5s).
Add FixtureAssembly, which emits a minimal two-type assembly via
PersistedAssemblyBuilder, and point these tests at it. They still exercise the
full project/solution writer, save-to-file and compare-tree pipelines, but
each now runs well under a second (e.g. Solution_Mode 10.8s -> 0.3s,
CreateSolution 10.8s -> 0.2s, ShowIdentical 4.5s -> 0.3s).
Assisted-by: Claude:claude-opus-4-8:Claude Code
The Pdb2Xml command (ILSpy) and the PDB round-trip tests (Decompiler.Tests)
reference Microsoft.DiaSymReader*, previously gated on the build host being
Windows. That made dotnet restore resolve a different graph on Windows than
on Linux -- the packages (and their transitive tail: DiaSymReader.PortablePdb,
the legacy NETCore.Platforms/Win32 packages) appear only on Windows, and
DiaSymReader.Native flips between Direct and CentralTransitive. So a checkout
could not be developed across OSes without the committed packages.lock.json
churning on every Windows restore.
Drop the OS gate (keep Debug-only) so the restored graph, and the committed
lock, are identical on every OS. The consuming code is still gated by DEBUG
and WINDOWS, so on non-Windows the packages are restored but never compiled
in; the native asset only resolves for win-* RIDs.
The "Verify package contents" step (which checks the committed *.filelist
snapshots still match the built VSIX/MSI contents) had been excluding
packages.lock.json from its git diff to tolerate that per-OS churn. With the
locks now identical across OSes the carve-out is unnecessary, so it goes back
to a plain git diff --exit-code.
Assisted-by: Claude:claude-opus-4-8:Claude Code
ICSharpCode.Decompiler.Tests builds for the host RID (so the native
Microsoft.DiaSymReader.Native assets the Windows PDB tests need are copied),
which made its packages.lock.json host-specific (linux-x64 vs win-x64) and
drift between Linux and Windows restores. Add an explicit <RuntimeIdentifiers>
list next to the single <RuntimeIdentifier>: the lock then records every RID
host-independently while the build still targets the host RID. Apply the same
list to the TestRunner it pulls in.
TestRunner's lock is now fully portable. The test project's lock still carries
the OS-conditional DiaSymReader.Native difference in its base graph, which the
*.filelist-scoped "Verify package contents" step already tolerates.
Assisted-by: Claude:claude-opus-4-8:Claude Code
create-filelists.ps1 filtered to bin\Debug with `-contains` (collection
membership, always false for a substring test), so it skipped every artifact;
drop the broken filter. Point the installer ProductIcon at the relocated
ILSpy.ico (Images -> Assets) and regenerate the committed .filelist snapshots.
Assisted-by: Claude:claude-opus-4-8:Claude Code
publish.ps1 now takes a -Configuration parameter (the workflow passes the
GNU-style --configuration <Debug|Release>, which pwsh binds to it) and uses
it for both the -c flag and the output paths. The win-x64 framework-dependent
bundle is produced for every configuration; the win-arm64 framework-dependent
and win-x64 self-contained bundles stay Release-only, matching what the zip
steps consume.
The build workflow publishes per-configuration before zipping, and the
framework-dependent binaries zip now reads straight from the publish output
(...\win-x64\publish\fwdependent) for the active configuration instead of
globbing the build output.
Assisted-by: Claude:claude-opus-4-8:Claude Code
Replace the Windows-only .bat helpers (clean / debugbuild / releasebuild /
restore / updatedeps and BuildTools/format) with cross-platform pwsh
scripts at the repo root: restore.ps1, build.ps1 (-Configuration), clean.ps1,
updatedeps.ps1 and BuildTools/format.ps1, alongside the existing publish.ps1.
Enable a packages.lock.json for every project by hoisting
RestorePackagesWithLockFile into the root Directory.Build.props (the four
core libraries set it individually before) and commit the generated locks,
so restores are repeatable and CI can cache packages off them.
Cache the NuGet packages folder in the three setup-dotnet workflows
(build-ilspy, build-frontends, codeql-analysis), keyed on the lock files
per the setup-dotnet caching guidance.
Scope the Debug "Verify package contents" check to the *.filelist outputs
it actually generates. A project's packages.lock.json is keyed only by
(framework, RID), with no host-OS axis, so a lock produced on Linux
legitimately differs from one produced on Windows whenever an OS-conditional
PackageReference applies (Debug+Windows pulls Microsoft.DiaSymReader*). The
Windows restore then rewrites those locks; that churn must not fail a step
whose job is to police the VSIX/MSI file lists.
Also drop the dead ILSpy.BamlDecompiler publish line from
publishlocaldev.ps1, mirroring the earlier publish.ps1 fix.
Assisted-by: Claude:claude-opus-4-8:Claude Code
publish.ps1 is a WPF-era script that was never updated for the current
tree, and the "Publish x64/arm64" CI step aborted on two stale things:
- It published ./ILSpy.BamlDecompiler/ILSpy.BamlDecompiler.csproj, which
does not exist here (the BAML library is ICSharpCode.BamlDecompiler and
already ships transitively via the ILSpy dependency graph) -> MSB1009.
- It publishes the ReadyToRun plugin RID-specifically without restoring
again, but the plugin declared no RuntimeIdentifiers, so the RID-less
solution restore produced no win-x64/win-arm64 assets -> NETSDK1047.
Drop the dead BamlDecompiler lines and give the plugin RuntimeIdentifiers
mirroring ILSpy.csproj, so it restores and publishes for every platform
the host supports (the plugin is pure managed and RID-agnostic).
Assisted-by: Claude:claude-opus-4-8:Claude Code
Keep the headless UI tests from timing out on the slow Windows runner:
decompile a small CoreLib type (System.Object) in the folding and
token-history tests instead of a large one, and raise the shared
decompile-wait timeout so a cold first decompile (JIT plus building a
CoreLib-scale type system) finishes in time.
Assisted-by: Claude:claude-opus-4-8:Claude Code
XmlDocLoader's modern-.NET fallback built the ref-pack path from the EXACT runtime
patch version (shared/Microsoft.NETCore.App/10.0.8 -> packs/Microsoft.NETCore.App.Ref/
10.0.8/ref), but the targeting (ref) pack version usually differs from the installed
runtime patch -- e.g. pack 10.0.0 -- and a relocated CI install (DOTNET_INSTALL_DIR)
may carry only a different feature band. The exact-version lookup then missed and the
provider came back null, leaving CoreLib hovers undocumented. Broaden the search:
prefer an exact match, then the newest pack sharing the runtime's major.minor, then
the newest pack present.
Assisted-by: Claude:claude-opus-4-8:Claude Code
NavigationHistory.Record's 0.5s debounce collapsed ANY two selections inside the
window -- including two DIFFERENT nodes -- so a navigation whose decompile finished
quickly (cheap targets, or a fast machine) never pushed the previous node onto the
back stack, silently losing history. Its real purpose is only to swallow the
double-fire a single click produces (SelectedItems.CollectionChanged + SelectedItem
PropertyChanged, same node) and tree-refresh re-selects, so gate the collapse on the
rapid entry being equal to the current one; a distinct node now records normally.
The view-state/navigation tests navigate between two cheap namespace nodes (a C#
namespace decompiles to just its "// Some.Name.Space" line) instead of decompiling
CoreLib types in full -- fast enough for the headless 15s budget on slow CI runners,
and a direct regression test for the collapse bug above.
Assisted-by: Claude:claude-opus-4-8:Claude Code
Three comments described the old WPF host: the NoWarn rationale and the
DiaSymReader-gating note in ILSpy.csproj, and the no-[Shared] note in
DecompilerSettingsViewModel. Reword them to describe the current
System.Composition / build behavior directly. No code or suppression changes;
the TomsToolbox.Composition.Analyzer (MEF002/MEF004 source) stays.
Assisted-by: Claude:claude-opus-4-8:Claude Code
The Avalonia app targets net10.0, so dotnet build emits ILSpy/bin/<config>/net10.0/,
but the framework-dependent zip still globbed a net10.0-windows folder that does not
exist, so it captured nothing. Point that zip at net10.0. The self-contained and arm64
zips keep net10.0-windows because that is the explicit -o path publish.ps1 (and the
installer/VSIX) write the published output to.
Assisted-by: Claude:claude-opus-4-8:Claude Code
.gitattributes marked *.cs as `text` with no `eol=`, so the checked-out line
ending was left to each machine's git config. On the CI runner the .cs come out
LF, but .editorconfig sets end_of_line=crlf, so dotnet-format's --verify-no-changes
flagged every line of every file and the Format check step failed repo-wide
(independent of code formatting). Pinning eol=crlf makes the checkout match the
editorconfig on every platform. Blobs stay LF; only the working-tree EOL is fixed.
Assisted-by: Claude:claude-opus-4-8:Claude Code
Make explicit what was only implied: the pre-commit hook IS the formatter, so
always let it run and never commit .cs with --no-verify (bypassing it is what
lets unformatted code accumulate). Scope the "new files must be CRLF" rule to
Windows -- on Linux/macOS that fights the repo's text=auto LF storage and makes
the whole tree look phantom-modified. Update the test example to the
Microsoft.Testing.Platform syntax the repo now pins. The parity-tracker line is
dropped (that tracker is on its way out).
Assisted-by: Claude:claude-opus-4-8:Claude Code
The Windows build workflow had several run steps on one very long line --
the framework-dependent 7z zip with six glob paths, the restore/build/test
invocations, and the nuget push calls -- which were hard to scan and review.
Reformat them: single-command steps use a YAML folded scalar (run: >) with
one argument group per line, and the long lines inside the nuget-push |
blocks use PowerShell backtick continuation. The folded scalars rejoin to
byte-identical command strings, so runtime behavior is unchanged. No step
ordering, conditions, or the net10.0-windows publish paths are touched.
Assisted-by: Claude:claude-opus-4-8:Claude Code
Restores the result-navigation gestures the pane shipped with before the
port: Ctrl+T/M/S jump the picker to Type/Member/Constant, Down/Up arrow
hand focus between the search box and the result list, and Ctrl+Enter or a
middle-click open a result in a new document tab instead of reusing the
active one. New-tab activation routes through the existing
OpenNodeInNewTab so it matches the tree's open-in-new-tab behaviour.
Assisted-by: Claude:claude-opus-4-8:Claude Code
The metadata-grid hover infrastructure shipped but no table entry fed it,
so every cell tooltip the previous version had was silently gone: the
heap-offset/value hints on string and blob columns, the entity description
on token columns, and the per-bit breakdown on flag columns.
Reinstate all of them. FlagsTooltip renders the rich per-bit view (a
checkbox per flag plus the selected member of each mutually exclusive
sub-range) instead of a flat string; GenerateTooltip on the table base
describes what a token column points at; the remaining columns expose
their heap-offset and value hints. MetadataCellTooltip now hands a
FlagsTooltip back as a built control and stringifies everything else.
Assisted-by: Claude:claude-opus-4-8:Claude Code
The list-root node had no Decompile override, so selecting it produced
empty output. Restore the behaviour of emitting a "// List:" header and
each contained assembly under a comment rule.
Assisted-by: Claude:claude-opus-4-8:Claude Code
The previous version's About page carried a Check-for-updates / Download
button and an "automatically check for updates every week" toggle. Embed
both back into the About output: the button reuses the toolbar banner's
shared UpdatePanelViewModel (same check, same state), and the toggle is
two-way bound to UpdateSettings.AutomaticUpdateCheckEnabled.
Assisted-by: Claude:claude-opus-4-8:Claude Code
The previous version let you pick the theme from a dropdown on the Display
options page; the Avalonia port only exposed theme switching via the menu.
Bind a ComboBox to SessionSettings.Theme -- the same property the menu's
SetThemeCommand sets and ThemeManager applies live -- so the page switches
the theme too, and resetting Display defaults restores the default theme.
Assisted-by: Claude:claude-opus-4-8:Claude Code
AvaloniaEdit's copy puts only plain text on the clipboard, so pasting into
an HTML-aware target (a document, mail, chat) lost the colours the previous
version preserved. Build a coloured HTML fragment from the selection that
merges BOTH the xshd syntax highlighter and ILSpy's semantic RichTextModel
(the decompiler's reference / theme colours the xshd alone doesn't carry),
and place it alongside the text under the native HTML format -- text/html on
Linux/macOS, CF_HTML on Windows. Wired to Ctrl+C (tunnel, so it pre-empts
the plain-text copy) and the right-click Copy entry.
Assisted-by: Claude:claude-opus-4-8:Claude Code
Port the mixed IL/C# disassembler language that the previous version
shipped: it disassembles IL with the decompiled C# source interleaved
above each instruction (mapped via sequence points) as gray comments. The
language was missing entirely from the Avalonia tree, though it is a
Release-visible entry in the language dropdown.
Assisted-by: Claude:claude-opus-4-8:Claude Code
A member that decompiled to a very large amount of text could hang or
exhaust memory on the UI thread with no escape. Cap display output at five
million characters and, on overflow, show a message offering to display
anyway (at an extended limit) or save to disk.
Also bridge three Display options that were persisted but never reached the
decompiler -- brace folding, debug-symbol info, and the indentation string
-- so toggling them once again affects the generated source.
Assisted-by: Claude:claude-opus-4-8:Claude Code
The search already stopped accepting results at 1000 and cancelled the
producer, but the user was never told the list had been truncated. Append
the "Search aborted, more than 1000 results found" row (a no-op to click).
Assisted-by: Claude:claude-opus-4-8:Claude Code
An on-demand resolved dependency had no way to be promoted into the
assembly list, so it was discarded on the next list reload. Restore the
context entry that clears IsAutoLoaded and re-saves the list.
Assisted-by: Claude:claude-opus-4-8:Claude Code
FindTreeNode only handled entity references, so activating an assembly,
resource, or namespace search result -- and clicking a resource or
namespace link -- silently did nothing. Restore the non-entity cases
(LoadedAssembly / MetadataFile / Resource / namespace) the previous
version handled.
Assisted-by: Claude:claude-opus-4-8:Claude Code
TestPlugin was still WPF (net10.0-windows, UseWpf, System.Windows,
TomsToolbox, the old ICSharpCode.ILSpy namespace), so it could not load
into the cross-platform app. Re-target it to net10.0 and map each
extension point onto the port's contracts: the custom Language, About-page
addition, context-menu entry, main-menu/toolbar command, and an Avalonia
options page whose view the ViewLocator resolves by naming convention. The
command needs [ImportingConstructor] for System.Composition to satisfy its
service dependency. Reference it build-only from the tests (no DLL copied
into the test output) so a composition test can load it without the
headless app auto-loading the plugin into every other test.
Assisted-by: Claude:claude-opus-4-8:Claude Code
A plugin whose assembly won't load, or an exported command whose
constructor can't be satisfied, previously either blanked the app into the
startup error window or threw mid-build and took the whole menu/toolbar
down with it. Collect such failures in a CompositionErrors sink instead,
skip the offending part, keep running, and show the errors in a document
tab via an ITextOutput once the window is up. Add a Composition log
category for opt-in file logging.
Assisted-by: Claude:claude-opus-4-8:Claude Code
Rename Avalonia.Xaml.Behaviors to its successor Xaml.Behaviors.Avalonia
(12.0.0.1), and bump Dock (12.0.0.2), Svg.Controls.Skia.Avalonia,
AvaloniaUI.DiagnosticsSupport, CliWrap, coverlet.MTP, and NUnit to their
latest releases.
Assisted-by: Claude:claude-opus-4-8:Claude Code
The document tab strip can now flow tabs onto multiple rows or stay a
single scrolling row; the mode is toggled by the mouse wheel over the
strip and persisted in SessionSettings. In single-line mode an overflow
dropdown at the strip's trailing edge lists every open document for quick
switching. Popups render in the window overlay layer (OverlayPopups) so
the dropdown's menu doesn't open as a native X11 child window that loses
focus and self-dismisses before it becomes visible.
Assisted-by: Claude:claude-opus-4-8:Claude Code
#3753: when many documents are open, the tab strip scrolls a single row,
hiding most tabs behind the overflow scroller.
Dock's DocumentTabStrip hardcodes a horizontal StackPanel inside a
PART_ScrollViewer in its theme template and ignores an ItemsPanel set via
a Style, so a MultiRowTabStripBehavior sets the WrapPanel ItemsPanel
directly and disables the scroll-viewer's horizontal scrolling, giving
the WrapPanel a bounded width so tabs wrap onto new rows. Enabled on the
document strip (not tool tabs) from App.axaml.
Drag-reorder across rows is not yet validated (Dock's drop-position logic
assumes a single horizontal strip), so this stays an opt-in prototype for
now rather than a closing fix for the issue.
Assisted-by: Claude:claude-opus-4-8:Claude Code
Project/solution export, PDB generation, Create Diagram and Extract
Package all ran via RunWithCancellation on the active preview tab --
whose cancellation token tree-node navigation cancels. So selecting any
node while one of these ran killed it: you couldn't browse while an
export was in progress.
Add DockWorkspace.RunInNewTabAsync, which opens a new frozen document
tab (its own decompile/cancellation scope, never the navigation target),
runs the work there, and shows the report in it. Route the long-running
command sites through it. A frozen tab is immune to the selection-driven
re-decompile, so navigation can no longer cancel the work -- and the
report opens in its own tab instead of replacing the current view.
The single-node Save Code path intentionally stays on the active tab
(matches the prior version and is quick).
Assisted-by: Claude:claude-opus-4-8:Claude Code
Right-click Save Code on a node without a Save() override (type,
namespace, member) did nothing: the context-menu entry only called
node.Save() and stopped when it returned false, unlike Ctrl+S which
falls through to a generic decompile-to-file save. The prior version
always had this fallback in both paths; the port dropped it from the
context entry.
Extract the shared "Save() else decompile the node to a single file"
flow into SaveCodeHelper and route both Ctrl+S and the context-menu
entry through it, targeting the clicked node. As before, the fallback
runs in the active decompiler tab with its progress/cancel overlay and
then shows a "decompilation complete" breadcrumb with an Open-folder
button; the default file name derives from the node's text.
Assisted-by: Claude:claude-opus-4-8:Claude Code
Window > Debug Steps did nothing. Two bugs in ToolPaneMenuItem:
the getter treated a pane with a null Owner (one hidden by default and
never placed in the layout) as visible, so the menu showed it checked
and toggling tried to hide it; and the show branch used
factory.RestoreDockable, which only un-hides a previously-shown pane and
is a no-op for one that was never in the layout.
Report visibility from real layout membership, and show via
ShowToolPane, which materialises the pane and (re)creates its home dock.
Assisted-by: Claude:claude-opus-4-8:Claude Code