Skip to content

View renderer (views/*): git-worktree entity cards overflow horizontally / path field does not wrap (MainWindow entity-pane, legacy entity-card-tree inner ScrollViewer) #1064

Closed

Description

@JoshuaRowePhantom

Related to #1049 (agent chat editor) and #1029, #1045.

Consolidation update (revised): #1064 originally proposed a targeted inline setter, then a consolidated attached-property style that set ScrollViewer.HorizontalScrollBarVisibility="Disabled" on TreeView.entity-card-tree. Disabled makes wrapping work, but it removes horizontal scrolling entirely — so when the viewport is narrower than the card's hard minimum widths, the card is clipped with no way to scroll to reach it. The maintainer correctly objected: "With horizontal scrolling disabled completely, how do we scroll horizontally to view cards when their minimum size must be respected?" This body replaces Disabled with the same two-regime wrapper #1049 proved for the sibling entity-card-tree-view, so the consolidated entity-card-tree style respects hard minimum widths and exposes a horizontal scrollbar precisely when the viewport drops below them — while still wrapping to the viewport when the viewport is wider. Everything below is proven headlessly in Avalonia 12.1.

Summary

In a saved View such as "Git Workspaces" / views/git-workspaces, the entity cards for git-worktree entities overflow horizontally: the long path value runs off the right edge, a horizontal scrollbar appears alongside the vertical one, and the text does not wrap to the viewport. The screenshot control is the View renderer: the entity-pane TreeView in Phantom.Workspaces/MainWindow.axaml (not the Entity Browser, not the agent chat editor). The same latent defect exists on every legacy entity-card-tree consumer whose inner scroller is left at Avalonia's default.

The consolidated fix makes any TreeView.entity-card-tree behave correctly by merely carrying the class, with two regimes:

  • Viewport ≥ minimum widths: content caps to the viewport and wraps; no horizontal scrollbar.
  • Viewport < minimum widths (~160px): the card keeps its hard minimum width and a single horizontal scrollbar appears so the minimum-width content is reachable; content wraps at the minimum.

This is the identical pattern #1049 shipped for entity-card-tree-view. Verified empirically with headless Avalonia 12.1 layout tests (measure + arrange + assert Bounds/Extent/Viewport), not by inspection.

Answering the maintainer's question directly

Horizontal scrolling is NOT globally disabled. The consolidated style keeps ScrollViewer.HorizontalScrollBarVisibility="Auto" and places one items-region wrapper around the whole items area with MinWidth = 160 (the summed hard minimums from #1045) and MaxWidth bound to the tree's own inner-scroller viewport width. That yields exactly two regimes:

  1. Viewport ≥ 160 (normal/wide): MaxWidth = Viewport.Width caps the wrapper to the visible width. The measure is now finite, so Stretch fills and TextWrapping="Wrap" wraps. Extent == viewport ⇒ no horizontal scrollbar.
  2. Viewport < 160 (narrower than the hard minimum): MinWidth (160) > MaxWidth (= Viewport.Width). Avalonia's layout clamp (min wins when min > max) resolves the wrapper to 160, wider than the viewport ⇒ extent > viewport ⇒ a single horizontal scrollbar appears, and you can scroll to reveal the card's minimum-width content. Content wraps at 160, never at infinity.

So the hard minimums are respected and always reachable: below the minimum you scroll horizontally; above it, the card caps to the viewport and wraps. The earlier Disabled-only variant is rejected precisely because in regime 2 it clips the card with no scrollbar.

Root Cause

The inner scroller of a TreeView.entity-card-tree defaults to HorizontalScrollBarVisibility="Auto", and there is no base TreeView.entity-card-tree rule configuring it (only TreeView.entity-card-tree-view, SharedStyles.axaml:265-278, has the #1049 wrapper). Under Auto, the ScrollContentPresenter sets CanHorizontallyScroll = true and measures content at double.PositiveInfinity width, so TextWrapping="Wrap" on the path value (workspace-field-read-value) is inert; the card grows to intrinsic width and the inner scroller shows a horizontal scrollbar.

Confirmed empirically (baseline): with the inner scroller at default Auto, a bare tree arranges the card to 2370px inside an 800px viewport, Extent.Width = 2370 > Viewport.Width = 800, and the path value stays a single 16px line. Wrapping the tree in an outer Disabled ScrollViewer does not help — the inner Auto scroller re-introduces the infinite measure.

Design / Fix (consolidated style — two-regime wrapper)

Recommendation: an attached-property style on TreeView.entity-card-tree that (a) sets H=Auto, and (b) provides a single items-region wrapper via ItemsPanel with MinWidth=160 and MaxWidth bound to $parent[ScrollViewer].Viewport.Width. Not Disabled; not the nested-template /template/ ScrollViewer#PART_ScrollViewer selector (the inner scroller is unnamed); not a full control-template replacement.

Add this canonical rule to Phantom.Workspaces.Gui.Shared/Styles/SharedStyles.axaml (identical mechanism to entity-card-tree-view at :265-278):

<!-- Consolidated (#1064): any TreeView.entity-card-tree respects the card's hard minimum
     widths AND stays reachable. H=Auto keeps a horizontal scrollbar available; a single
     items-region wrapper caps content to the inner scroller's viewport (so it wraps and
     never overflows when wide) but floors at MinWidth=160 (so a horizontal scrollbar appears
     and the minimum-width card is scrollable when the viewport is narrower than the minimum).
     The tree's own inner scroller supplies vertical scrolling; no surrounding ScrollViewer. -->
<Style Selector="TreeView.entity-card-tree">
    <Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Auto" />
    <Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto" />
    <Setter Property="ScrollViewer.AllowAutoHide" Value="False" />
    <Setter Property="ItemsPanel">
        <ItemsPanelTemplate>
            <StackPanel Orientation="Vertical"
                        HorizontalAlignment="Stretch"
                        MinWidth="160"
                        MaxWidth="{Binding $parent[ScrollViewer].Viewport.Width}" />
        </ItemsPanelTemplate>
    </Setter>
</Style>

Recommended: single shared base style for both classes

entity-card-tree and entity-card-tree-view now use identical ScrollViewer config and the same wrapper. Consolidate them into one multi-selector rule (mirroring the shared TreeViewItem template at SharedStyles.axaml:236), keeping the entity-card-tree-view-only Background as a separate setter:

<Style Selector="TreeView.entity-card-tree, TreeView.entity-card-tree-view">
    <Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Auto" />
    <Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto" />
    <Setter Property="ScrollViewer.AllowAutoHide" Value="False" />
    <Setter Property="ItemsPanel">
        <ItemsPanelTemplate>
            <StackPanel Orientation="Vertical" HorizontalAlignment="Stretch"
                        MinWidth="160"
                        MaxWidth="{Binding $parent[ScrollViewer].Viewport.Width}" />
        </ItemsPanelTemplate>
    </Setter>
</Style>
<Style Selector="TreeView.entity-card-tree-view">
    <Setter Property="Background" Value="{DynamicResource Theme.Surface.EntityPane.Background}" />
</Style>

This makes #1064 and #1049 share one authoritative wrapper. If a single shared base is preferred it is recommended; otherwise duplicate the entity-card-tree rule verbatim.

Which viewport-width source resolves (proven experimentally)

Attached-property flow (unchanged from last round): the ScrollViewer.* attached properties set on the TreeView flow to the templated inner scroller — asserted HorizontalScrollBarVisibility = Auto, VerticalScrollBarVisibility = Auto, AllowAutoHide = false.

Pure-style solution — no fallback needed

The pure-style, name-free solution works: H=Auto via attached property + ItemsPanel wrapper with MinWidth + MaxWidth={Binding $parent[ScrollViewer].Viewport.Width}. No attached behavior, no named ScrollViewer, and no control-template replacement are required. (Had it failed, the minimal fallback would have been a one-line template tweak to name the inner ScrollViewer PART_ScrollViewer so MaxWidth="{Binding #PART_ScrollViewer.Viewport.Width}" could target it — but this was not necessary.)

Secondary — soften the fixed 200px label column (as in #1049)

At the narrow regime the wrapper floors at 160, but the field-row grids' fixed ColumnDefinitions="200,*" label column (WorkspaceDataTemplates.axaml) then starves the value column to 0 (empirically pathW=0, single line). With the label column softened to Auto,* (the #1049 secondary fix), the value column keeps room and the path wraps at the 160 min (empirically 84 × 428). The two-regime scrollbar behavior is correct either way; softening the label column is what lets the minimum-width content actually wrap rather than clip.

Why not the earlier variants

  • Disabled-only (rejected/insufficient): wraps, but removes the horizontal scrollbar entirely, so below the minimum widths the card is clipped with no way to scroll to reach it — the maintainer's exact objection. The two-regime wrapper supersedes it.
  • Nested-template /template/ ScrollViewer#PART_ScrollViewer: the inner ScrollViewer is unnamed in Avalonia 12.1, so #PART_ScrollViewer matches nothing. (Note: the ancestor lookup $parent[ScrollViewer] still resolves it by type — that is what the wrapper uses.)
  • Full ControlTemplate replacement: unnecessary and costly; the attached-property + ItemsPanel wrapper already achieves correct wrapping, minimum-respecting scroll, and vertical scroll.
  • Per-item MaxWidth → Viewport.Width: rejected (fights indentation); the single wrapper handles indentation normally inside it (see entity-card-tree-view: entity cards expand far beyond the viewport horizontally; must be capped to viewport width (follow-up to #1045) #1049).

One scroller, not two

The consolidated style means the tree's own inner scroller is the single scroller. The nested outer + inner arrangement in MainWindow.axaml and EntityBrowserWorkspaceTabView.axaml is the anti-pattern: the outer Disabled scroller cannot cap the inner scroller's measure, and it also cannot provide the below-minimum horizontal scrollbar the wrapper now supplies. Once the class is applied, the outer scrollers are removable.

Experimental Evidence

Headless Avalonia 12.1 (Avalonia.Headless + Avalonia.Headless.XUnit), project C:\dev\test\Avalonia\WrapExperiment (TwoRegimeExperiments.cs, plus the retained StyleOnlyExperiments.cs baseline). A bare TreeView with NO surrounding ScrollViewer, only the candidate class + consolidated two-regime style applied, rendering a git-worktree card whose path is the 154-char worktree path from the screenshot. Each row is measured/arranged at the given client size, then Bounds/Extent/Viewport and the inner scroller's props are asserted. Full suite: 43/43 pass.

Two regimes (standard card, fixed 200,* label column)

# Configuration (bare tree, no outer SV) Viewport (W×H) Wrapper W Item W Inner Extent.W path (W×H) H-bar V-bar Verdict
1 No style (inner default Auto) 800×600 — 2370 2370 (> viewport) 2156 × 16 (single line) yes no FAIL — reproduces overflow
2 Two-regime style — Regime 1 800×600 800 800 800 (== viewport) 586 × 77 (wraps) no no PASS — caps to viewport, wraps
3 Two-regime style — Regime 2 120×600 160 (MinWidth wins) 160 160 (> viewport 120) starved by 200px label (pathW=0) yes no PASS — H-scrollbar appears; card reachable

Two regimes (softened Auto,* label column — value column no longer starved)

# Configuration Viewport (W×H) Wrapper W Inner Extent.W path (W×H) H-bar Verdict
4 Two-regime style — Regime 1 800×600 800 800 (== viewport) 724 × 62 (wraps) no PASS — caps + wraps
5 Two-regime style — Regime 2 120×600 160 160 (> viewport 120) 84 × 428 (wraps at min) yes PASS — wraps at min AND scrollable

Vertical scroll, gutter, and scroll-reach

# Configuration Viewport (W×H) Inner Extent.H V-bar H-bar Notes
6 Two-regime style, 20 tall items 800×300 → viewport 784×300 3420 (> 300) yes no Vertical scroll via the tree's own inner scroller; viewport width shrinks 800→784 (16px gutter) — scrollbar does not overlap content (#1045).
7 Regime 2 scroll-reach 120×600 — — yes Extent.W − Viewport.W > 0; set Offset.X = maxOffset ⇒ Offset.X > 0: the minimum-width content is reachable by horizontal scroll.

Which viewport-width binding resolved

Binding source Resolved? Resolved value (vertical-scroll case) Verdict
$parent[ScrollViewer].Viewport.Width Yes wrapper MaxWidth == inner Viewport.Width = 784 (gutter excluded) WINNER — name-free type ancestor lookup reaches the unnamed inner scroller; gutter-correct; matches #1049
$parent[TreeView].Bounds.Width Yes wrapper MaxWidth = 800 (gutter included); no spurious H-bar only because Stretch re-caps in arrange Rejected — over-reports by the gutter, less robust

Key asserted numbers: regime 1 wrapper/extent collapse to exactly the viewport (800→800), path wraps; regime 2 wrapper clamps to MinWidth=160 > viewport, Extent.W=160, a horizontal scrollbar appears and content is reachable; vertical Extent.H=3420 > 300 with a 16px gutter; binding proof wrapper MaxWidth == inner Viewport.Width = 784.

Locked-in passing tests (TwoRegimeExperiments): TwoRegime_SV_AttachedProperty_FlowsToInnerScrollViewer, TwoRegime_SV_WideViewport_CapsToViewport_Wraps_NoHScroll, TwoRegime_SV_NarrowViewport_ShowsHScrollbar_WrapsAtMin, TwoRegime_SV_NarrowViewport_CanScrollToRevealMinContent, TwoRegime_SV_NestedItem_WrapsWithinWrapper, TwoRegime_SV_ManyItems_VerticalScrollIntact_NoHScroll, TwoRegime_SV_SoftLabel_NarrowViewport_WrapsAtMin_AndScrolls, TwoRegime_SV_SoftLabel_WideViewport_CapsAndWraps_NoHScroll, TwoRegime_SV_WrapperMaxWidth_ResolvesToInnerViewport, plus candidate-B comparators TwoRegime_TV_WideViewport_CapsToTreeBounds_Wraps_NoHScroll, TwoRegime_TV_NarrowViewport_ShowsHScrollbar, TwoRegime_TV_ManyItems_RecordResolvedWidths. The StyleOnlyExperiments.* and ViewRendererExperiments.* tests remain green.

Remediation Inventory

Surrounding ScrollViewers to remove

# File Lines Wraps Why removable
1 Phantom.Workspaces/MainWindow.axaml 172-187 View-renderer entity-pane TreeView (:177) The outer ScrollViewer H=Disabled V=Visible is superseded: the styled tree's inner scroller now supplies V=Auto vertical scrolling, wrap-to-viewport (regime 1), and the below-minimum horizontal scrollbar (regime 2) via the wrapper. No StickyScroll attached — clean removal.
2 Phantom.Workspaces/Templates/EntityBrowserWorkspaceTabView.axaml 7-11 BrowserTreeView (:12) The outer BrowserScrollViewer H=Disabled V=Auto is superseded by the styled inner scroller + wrapper. Caveat: it carries controls:StickyScroll.IsEnabled="True"; add the entity-card-tree-sticky class to the TreeView so the existing selector TreeView.entity-card-tree.entity-card-tree-sticky ScrollViewer (SharedStyles.axaml:307-308) enables StickyScroll on the inner scroller.

Count: 2 surrounding ScrollViewers should be removed. (Unchanged from the previous round; the change is that the class now carries the two-regime wrapper, not just H=Disabled.)

TreeViews to restyle

# File Line Current classes What to add/change
1 Phantom.Workspaces/MainWindow.axaml 177 entity-card-tree entity-card-tree-entity Correct automatically once the consolidated two-regime TreeView.entity-card-tree rule is added. Remove the surrounding ScrollViewer (inventory row 1). No per-site setter.
2 Phantom.Workspaces/Templates/EntityBrowserWorkspaceTabView.axaml 12-14 entity-card-tree entity-card-tree-entity Correct automatically via the base rule. Remove the surrounding ScrollViewer (inventory row 2) and add entity-card-tree-sticky to keep sticky scrolling.

Count: 2 TreeViews (both fixed by the single base rule + outer-scroller removal).

Keep as-is / already correct

Affected Files

File Change
Phantom.Workspaces.Gui.Shared/Styles/SharedStyles.axaml Primary. Add the consolidated two-regime TreeView.entity-card-tree rule (H=Auto, V=Auto, AllowAutoHide=False, ItemsPanel wrapper MinWidth=160 + MaxWidth={Binding $parent[ScrollViewer].Viewport.Width}). Recommended: fold entity-card-tree and entity-card-tree-view (:265-278) into one shared multi-selector base rule (keeping the entity-card-tree-view Background separate).
Phantom.Workspaces/Templates/WorkspaceDataTemplates.axaml Secondary. Soften the fixed ColumnDefinitions="200,*" label column in the field-row grids to Auto,* (or a min-bounded proportional column) so the value column is not starved at the 160 min and path/markdown wraps in the narrow regime.
Phantom.Workspaces/MainWindow.axaml Remove the surrounding ScrollViewer (:172-187); the styled TreeView (:177) now wraps + scrolls (both axes) on its own inner scroller.
Phantom.Workspaces/Templates/EntityBrowserWorkspaceTabView.axaml Remove the surrounding BrowserScrollViewer (:7-11); add entity-card-tree-sticky to BrowserTreeView (:12) to keep sticky scrolling.
Phantom.Workspaces.Agent.Gui/Controls/AgentChatToolsDetailControl.axaml Remove the inline ScrollViewer.HorizontalScrollBarVisibility="Disabled" (:16-21) so it inherits the two-regime behavior instead of staying clip-only when narrow.
Phantom.Workspaces/Controls/EntityCardControl.axaml No change — the header grid wraps correctly under a finite measure.
Phantom.Workspaces.Agent.Gui/Controls/AgentChatEditorControl.axaml No change — entity-card-tree-view (#1049) is the same pattern; backed by the shared base rule if consolidated.

Expected Tests

Naming Subject_Scenario_ExpectedOutcome, matching Phantom.Workspaces.Gui.Shared.Tests/SharedStylesTests.cs ([PhantomAvaloniaFact] layout tests + string-AXAML assertions) and Phantom.Workspaces.Agent.Gui.Tests/MainWindowAxamlTests.cs. The headless assertions below already pass in the experiment project (TwoRegimeExperiments.cs) and should be ported.

Test Name Class What It Verifies
EntityCardTree_Class_SetsAutoHScroll_AndWrapperMinMax SharedStylesTests The consolidated TreeView.entity-card-tree style declares ScrollViewer.HorizontalScrollBarVisibility="Auto" / VerticalScrollBarVisibility="Auto" / AllowAutoHide="False" and an ItemsPanel wrapper with MinWidth="160" and MaxWidth="{Binding $parent[ScrollViewer].Viewport.Width}".
EntityCardTree_AttachedScrollProperties_FlowToInnerScrollViewer SharedStylesTests (PhantomAvaloniaFact) After applying the class to a bare TreeView, the templated inner (unnamed) ScrollViewer reports H=Auto / V=Auto / AllowAutoHide=false.
EntityCardTree_ViewportWiderThanMin_CapsToViewport_Wraps_NoHScroll SharedStylesTests (PhantomAvaloniaFact) Regime 1: at viewport 800 the wrapper/extent == viewport (800), the path wraps (height grows), inner Extent.Width == Viewport.Width, no horizontal scrollbar.
EntityCardTree_ViewportNarrowerThanMin_ShowsHScrollbar_AndIsReachable SharedStylesTests (PhantomAvaloniaFact) Regime 2: at viewport 120 the wrapper clamps to MinWidth=160 > viewport, inner Extent.Width=160 > Viewport.Width, a horizontal scrollbar appears, and Offset.X can be scrolled > 0 to reveal the minimum-width content.
EntityCardTree_NestedItem_WrapsWithinWrapperWidth SharedStylesTests (PhantomAvaloniaFact) A nested (indented) TreeViewItem arranges within the wrapper width (indent + itemWidth <= wrapperWidth), confirming indentation is handled inside the single wrapper.
EntityCardTree_ManyItems_InnerScroller_ProvidesVerticalScroll SharedStylesTests (PhantomAvaloniaFact) With items exceeding height and no outer scroller, inner Extent.Height > Viewport.Height, a vertical scrollbar appears, the viewport width shrinks by a gutter (no overlap), and no horizontal scrollbar.
EntityCardTree_WrapperMaxWidth_ResolvesToInnerScrollerViewport SharedStylesTests (PhantomAvaloniaFact) The wrapper's resolved MaxWidth equals the inner scroller's Viewport.Width (gutter-excluded, e.g. 784), proving $parent[ScrollViewer] resolves name-free to the unnamed inner scroller.
EntityCardTree_DefaultInnerAutoScroll_OverflowsAndDoesNotWrap SharedStylesTests (PhantomAvaloniaFact) Regression guard: legacy entity-card-tree with no wrapper (default Auto) ⇒ item width ≫ viewport and the path stays a single line (the bug).
WorkspaceFieldRow_LabelColumn_IsNotFixedTwoHundred SharedStylesTests The field-row grids no longer hard-code ColumnDefinitions="200,*" (use Auto,* / min-bounded) so the value column is not starved at the 160 min.
MainWindowView_EntityPaneTree_HasNoSurroundingScrollViewer MainWindowAxamlTests (string-AXAML) The entity-pane TreeView (CurrentViewPopulation.RootEntities) is not wrapped in a surrounding ScrollViewer; it relies on the consolidated class.
EntityBrowserView_BrowserTree_HasNoSurroundingScrollViewer_AndIsSticky entity-browser tests (string-AXAML) BrowserTreeView has no surrounding ScrollViewer and carries entity-card-tree-sticky so sticky scrolling is preserved on the inner scroller.
AgentChatToolsDetail_Tree_InheritsTwoRegimeWrapper_NoInlineDisabled agent-gui tests (string-AXAML) AgentChatToolsDetailControl no longer sets inline HorizontalScrollBarVisibility="Disabled", so it inherits the two-regime behavior.
EntityCardTreeView_StillMatchesEntityCardTree_TwoRegime SharedStylesTests (PhantomAvaloniaFact) Guards that #1049's entity-card-tree-view and #1064's entity-card-tree produce the same two-regime behavior (shared base rule), wrapping with no h-scrollbar wide and a single h-scrollbar below the min.

Where a value is purely visual (exact gutter pixels), assert the style/attached-property value and the measured/arranged widths as above.

Considered / Background

Activity

  1. changed the title [-]entity-card-tree-view: card text still does not wrap to the viewport — empirically verified fix (legacy entity-card-tree inner ScrollViewer)[/-] [+]View renderer (views/*): git-worktree entity cards overflow horizontally / path field does not wrap (MainWindow entity-pane, legacy entity-card-tree inner ScrollViewer)[/+] on Jul 16, 2026
  2. JoshuaRowePhantom commented on Jul 16, 2026

    @JoshuaRowePhantom
    OwnerAuthor

    Verification result

    Status: ❌ 8 issue(s) found

    Commits checked: 2aca56e3 (Fix #1064) on batch/gui1 (branched off features 80bce408)

    What is correct

    The core fix is implemented and matches the diagnosed root cause: a consolidated TreeView.entity-card-tree style in SharedStyles.axaml sets ScrollViewer.HorizontalScrollBarVisibility=Auto / VerticalScrollBarVisibility=Auto / AllowAutoHide=False plus an ItemsPanel wrapper (MinWidth=160 + MaxWidth={Binding \[ScrollViewer].Viewport.Width}); the field-row grids drop the fixed 200,* for Auto,*; and the surrounding ScrollViewer on the MainWindow entity-pane tree and the inline H=Disabled on AgentChatToolsDetailControl are removed. These are covered by static AXAML-string tests that all pass.

    Outstanding gaps

    The problem is test depth: the issue's Expected Tests explicitly call for the two-regime runtime headless (PhantomAvaloniaFact) layout assertions ("already pass in the experiment project TwoRegimeExperiments.cs and should be ported"). Only static string-match tests were added — the actual behavioral proof of the two regimes is absent. Missing named tests:

    • EntityCardTree_AttachedScrollProperties_FlowToInnerScrollViewer (runtime: attached scroll props reach the templated inner ScrollViewer)
    • EntityCardTree_ViewportWiderThanMin_CapsToViewport_Wraps_NoHScroll (core regime 1 — wide viewport wraps, no H-scrollbar)
    • EntityCardTree_ViewportNarrowerThanMin_ShowsHScrollbar_AndIsReachable (core regime 2 — narrow viewport keeps MinWidth=160, H-scrollbar appears and is scrollable)
    • EntityCardTree_NestedItem_WrapsWithinWrapperWidth
    • EntityCardTree_ManyItems_InnerScroller_ProvidesVerticalScroll
    • EntityCardTree_WrapperMaxWidth_ResolvesToInnerScrollerViewport
    • EntityCardTree_DefaultInnerAutoScroll_OverflowsAndDoesNotWrap (regression guard)
    • EntityBrowserView_BrowserTree_HasNoSurroundingScrollViewer_AndIsSticky — diverged: the browser tree deliberately retains its surrounding ScrollViewer (implemented instead as the weaker EntityBrowserView_BrowserTree_HasEntityCardTreeWrapperClass). Per the issue the surrounding scroller should be removed and sticky preserved on the inner scroller; either implement as specified or justify the retained scroller and update the expected test accordingly.

    None of the added tests assert the wrap-vs-scrollbar layout at either viewport regime, so the fix's headline behavior is unverified at runtime. Port the TwoRegimeExperiments.cs headless assertions for the entity-card-tree class (the sibling entity-card-tree-view already has such a runtime test — EntityCardTreeViewStyle_HorizontalScrollBar_OnlyWhenMinWidthHit_AndNotOverlapping).

    Tests

    Fast suite (whole repo, from worktrees/9): Executed 4109 / Passed 4109 / Failed 0. All added tests pass, but they do not cover the described runtime two-regime cases. Not labeled verified-locally pending the missing headless tests. Diff touches only GUI styles/templates (no filesystem/Git layer) — slow tests not required.

  3. JoshuaRowePhantom commented on Jul 16, 2026

    @JoshuaRowePhantom
    OwnerAuthor

    Verification result

    Status: ✅ Verified

    Commits checked (this issue): 2aca56e3 (core two-regime wrapper) + 5fb08740 (re-fix: runtime proofs + browser-tree reconcile) on batch/gui1 (branched off features 80bce408).

    Prior gaps — all closed

    The earlier verification (8 issues) required the two-regime runtime headless (PhantomAvaloniaFact) layout proofs ported from TwoRegimeExperiments.cs and reconciliation of the browser-tree divergence. 5fb08740 closes every one:

    • EntityCardTree_AttachedScrollProperties_FlowToInnerScrollViewer — present; asserts inner ScrollViewer H=Auto/V=Auto/AllowAutoHide=false at runtime.
    • EntityCardTree_ViewportWiderThanMin_CapsToViewport_Wraps_NoHScroll — regime 1: wrapper/extent cap to viewport, path wraps, no H-scrollbar.
    • EntityCardTree_ViewportNarrowerThanMin_ShowsHScrollbar_AndIsReachable — regime 2: wrapper clamps to MinWidth=160 > viewport, extent > viewport, H-scrollbar appears, Offset.X scrolls > 0.
    • EntityCardTree_NestedItem_WrapsWithinWrapperWidth — nested item arranges within wrapper width.
    • EntityCardTree_ManyItems_InnerScroller_ProvidesVerticalScroll — inner extent height > viewport, V-scrollbar, gutter reserved, no H-scrollbar.
    • EntityCardTree_WrapperMaxWidth_ResolvesToInnerScrollerViewport — wrapper MaxWidth == inner Viewport.Width (gutter-excluded), proving name-free \[ScrollViewer] resolution.
    • EntityCardTree_DefaultInnerAutoScroll_OverflowsAndDoesNotWrap — regression guard: no wrapper => card overflows, path single line.
    • EntityCardTreeView_StillMatchesEntityCardTree_TwoRegime — parity guard for entity-card-tree-view: entity cards expand far beyond the viewport horizontally; must be capped to viewport width (follow-up to #1045) #1049's sibling class.
    • EntityBrowserView_BrowserTree_HasNoSurroundingScrollViewer_AndIsSticky — divergence reconciled: the surrounding BrowserScrollViewer is removed from EntityBrowserWorkspaceTabView.axaml and entity-card-tree-sticky added so StickyScroll is preserved on the tree's own inner scroller (TreeView.entity-card-tree.entity-card-tree-sticky ScrollViewer selector exists in SharedStyles.axaml). Test asserts the outer scroller/inline Disabled/StickyScroll are gone and the sticky class is present.

    The remaining Expected Tests (EntityCardTree_Class_SetsAutoHScroll..., WorkspaceFieldRow_LabelColumn_IsNotFixedTwoHundred*, MainWindowView_EntityPaneTree_HasNoSurroundingScrollViewer, AgentChatToolsDetail_Tree_Inherits...) are present as static AXAML assertions (accepted in the prior round). Root cause (default Auto inner scroller measuring at infinity) is fully addressed by the consolidated wrapper.

    Outstanding gaps

    None — all criteria satisfied.

    Tests

    Fast suite (whole repo, from worktrees/9): Executed 4124 / Passed 4124 / Failed 0. All ported runtime layout proofs pass. Diff touches only GUI styles/templates/tests + a test-only Avalonia.Markup.Xaml.Loader package (no filesystem/Git layer) — slow tests not required.

  4. added 3 commits that reference this issue on Jul 16, 2026
    2aca56e
    c58069a
    5fb0874
  5. JoshuaRowePhantom commented on Jul 16, 2026

    @JoshuaRowePhantom
    OwnerAuthor

    Checked in

    Commit: 4fecc1e
    Branch fast-forwarded: features -> 4fecc1e

    Batch batch/gui1 landed on features (fast suite 4124/4124 green; new GUI tests stable 5/5).

  6. added a commit that references this issue on Aug 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

bugSomething isn't workingdiagnosedRoot cause identifiedverified-locallyImplementation has been verified locally

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions