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.
Replace the single text-bar above the grid with one filter input per
column, baked into each DataGridColumn's header. The page model carries
a ColumnFilters collection alongside Columns; the filter predicate ANDs
every non-empty column's case-insensitive substring match. Column
instances are built fresh per page so sibling tabs don't share filter
state, and the column name moves to Tag for cell-level lookups now that
Header is a Panel.
Assisted-by: Claude:claude-opus-4-7:Claude Code
Tree-node selection now updates the inner Content of one persistent
ContentTabPage instead of swapping the dockable in the dock. The
wrapper view (ContentTabPageView) keeps both inner views — the
decompiler text editor and the metadata grid — pre-realised in the
visual tree from construction time and toggles which is visible based
on Content's runtime type.
Assisted-by: Claude:claude-opus-4-7:Claude Code
Tree-node selections always operate on whatever document tab is
currently active. If the active tab is the wrong concrete type for the
new selection (e.g. a decompiler tab is up and the user picks a
metadata node, or vice-versa), the active dockable is replaced in
place — close the old, add the new, no sibling tab left behind.
Previously the host added a sibling tab and switched focus, which
piled up dockables and made "back to entity from metadata" feel
broken because the user could see two tabs competing for the slot.
Assisted-by: Claude:claude-opus-4-7:Claude Code
Picking DOS Header → COFF Header → DOS Header was opening three
dockables because ShowCustomTab unconditionally added a fresh tab. The
docking host now searches for an open tab of the same concrete type,
copies the new node's tab state onto it, and re-activates it — same
shape as how the single decompiler tab gets reused across tree-node
selections.
Assisted-by: Claude:claude-opus-4-7:Claude Code
DOS / COFF / Optional / DataDirectories / DebugDirectory now expose
their entries through CreateTab() instead of writing a fixed-width text
table from Decompile. The grid view shows each entry's columns
reflected directly off the row shape — Phase 1's text table was a
stand-in until this view shipped. The existing PE-header tests are
rewritten to assert against MetadataTablePageModel.Items / Columns;
adds a Waiters.WaitForMetadataTabAsync helper used by every grid-tab
test.
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