The global:: prefixes existed because the test project's namespace
ICSharpCode.ILSpy.Tests used to shadow the app's old top-level ILSpy
namespace. With the UI code back under ICSharpCode.ILSpy there is
nothing left to shadow, so plain fully qualified names resolve fine.
Assisted-by: Claude:claude-fable-5:Claude Code
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
Search, Analyzer and Debug Steps cluttered the default layout. They now opt out via ExportToolPane.IsVisibleByDefault = false (which BuildToolDock finally honours), so a fresh launch shows just the assembly tree. Each pane keeps its home alignment and is materialised there on demand by ShowToolPane, so opening Search / Analyze surfaces it in the same place as before.
The document tabs had two parallel hierarchies: the ContentTabPage dockable wrapper, and four unrelated content viewmodels in its object? Content slot -- three TabPageModel subclasses plus OptionsPageModel, which derived from a bare ObservableObject. The wrapper bridged the gap by reflection (reading Title off the runtime type) and the router duck-typed IsStaticContent across two unrelated classes.
Collapse the content side under one abstract ContentPageModel base (named to avoid a clash with Avalonia.Controls.ContentPage). OptionsPageModel joins it, dropping its duplicate Title/IsStaticContent. ContentTabPage.Content becomes ContentPageModel?, so title/language-switching read directly and IsWritablePreview collapses from two runtime type-tests to one IsStaticContent check. CreateTab and the dock-router helpers are typed through. Morph-in-place is unchanged -- the One still swaps its Content in place; this only strengthens the types behind it.
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.
Nearly every headless UI test opened with the same four-line prologue
(resolve the shared MainWindow, show it, cast its DataContext, wait for the
assembly list), then repeated the corelib lookup, the EnsureLazyChildren +
Children.OfType<T>().Single() drill, the registry single-by-header lookups,
and the open-an-assembly-and-wait dance. The duplication made the intent of
each test hard to see and every signature tweak a suite-wide edit.
Collapse those into TestHarness (BootAsync, OpenAssemblyAsync, GetCommand,
GetEntry) and TreeNavigation extensions (FindCoreLib, GetChild<T>, Expand<T>),
then apply them across the suite. Net ~865 lines of boilerplate removed with
no change in behaviour; the full headless suite stays green.
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
Sweep-up commit. The dotnet-format pre-commit hook keeps re-ordering
these usings on every other commit; landing them once stops the hook
from grumbling at unrelated diffs going forward.
Assisted-by: Claude:claude-opus-4-7:Claude Code
Persists the live dock layout to ILSpy.Layout.json next to ILSpy.xml on
MainWindow.OnClosing; loads it on DockWorkspace ctor; falls back to
factory.CreateLayout when the file is absent or fails to deserialize.
WPF stays XML in ILSpy.xml — this is Avalonia-side only.
Assisted-by: Claude:claude-opus-4-7:Claude Code