Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/TEST_COVERAGE.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ authoritative runtime totals.
### OpenClaw.Tray.Tests

- **Tray UI and state** - app state, menu display/position/sizing, tray tooltip formatting, activity streams, async list loading, diagnostics contracts, markup regressions, and chat timeline/markdown handling.
- **Chat scroll lifetime** - `ChatTimelinePresentationTests` guards the workaround for [microsoft/microsoft-ui-xaml#11865](https://github.com/microsoft/microsoft-ui-xaml/issues/11865): automatic tail navigation must not use `ItemsView.StartBringItemIntoView`, which can retain a recycled row as an invalid anchor and throw `E_INVALIDARG` during subsequent layout. Tail requests use non-animated `ScrollView` extent navigation with stable bottom anchoring. Runtime proof should repeatedly switch between large, mixed-height histories, verify the final message is visible, and check that a scrolled-up reader is not pulled to the bottom by new messages.
- **Connection and pairing** - connection manager node connector tests, connection page approval/channel metrics/row state, operator and Windows tray node pairing approval, and gateway action transport.
- **Settings and startup** - settings round-trip/isolation, consent and settings save, auto-start defaults, startup setup state, existing config guard policy, and local setup progress stage mapping.
- **Onboarding and local gateway setup** - onboarding completion/chat bootstrapper/existing config guard, wizard flow/selection/error/step parsing, setup code decoding, local gateway setup diagnostics, uninstall, WSL keep-alive, and auto-pair flags.
Expand Down
14 changes: 8 additions & 6 deletions src/OpenClaw.Tray.WinUI/Chat/ReactorItemsViewScrollController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using Microsoft.UI.Reactor.Core.V1Protocol;
using Microsoft.UI.Reactor.Input;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using System.Runtime.CompilerServices;
using WinUIAnnotatedScrollBar = Microsoft.UI.Xaml.Controls.AnnotatedScrollBar;
using WinUIItemsView = Microsoft.UI.Xaml.Controls.ItemsView;
Expand Down Expand Up @@ -253,7 +254,7 @@ private void QueueTailRequest(int version, TailNavigationRequest request)

private bool StartTailRequest(TailNavigationRequest request)
{
if (itemsView.ScrollView is not { IsLoaded: true })
if (itemsView.ScrollView is not { IsLoaded: true } scrollView)
return false;

if (!TailNavigationPolicy.CanExecute(
Expand All @@ -266,11 +267,12 @@ private bool StartTailRequest(TailNavigationRequest request)
}

_following = true;
itemsView.StartBringItemIntoView(request.Index, new BringIntoViewOptions
{
AnimationDesired = false,
VerticalAlignmentRatio = 1.0,
});
// Work around microsoft/microsoft-ui-xaml#11865 without retaining a row
// as a bring-into-view anchor across history replacement.
scrollView.ScrollTo(
scrollView.HorizontalOffset,
scrollView.ScrollableHeight,
new ScrollingScrollOptions(ScrollingAnimationMode.Disabled, ScrollingSnapPointsMode.Ignore));
return true;
}

Expand Down
9 changes: 6 additions & 3 deletions tests/OpenClaw.Tray.Tests/ChatTimelinePresentationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,11 @@ public void ReactorTimeline_UsesStableBottomAnchoringAndDiscreteTailRequests()
Assert.Contains("itemsView.Loaded += OnLoaded", binding);
Assert.Contains("itemsView.LayoutUpdated += OnLayoutUpdated", binding);
Assert.Contains("itemsView.DispatcherQueue.TryEnqueue", binding);
Assert.Contains("itemsView.StartBringItemIntoView(", binding);
Assert.Contains("VerticalAlignmentRatio = 1.0", binding);
Assert.Contains("scrollView.ScrollTo(", binding);
Assert.Contains("scrollView.HorizontalOffset,", binding);
Assert.Contains("scrollView.ScrollableHeight,", binding);
Assert.Contains("ScrollingAnimationMode.Disabled, ScrollingSnapPointsMode.Ignore", binding);
Assert.DoesNotContain("StartBringItemIntoView", binding);
Assert.Contains("!string.Equals(_displayedTailKey, displayedTailKey, StringComparison.Ordinal)", binding);
Assert.Contains("_following = IsNearBottom(sender)", binding);
Assert.Contains("_scrollView.VerticalAnchorRatio = 1.0", binding);
Expand All @@ -83,7 +86,6 @@ public void ReactorTimeline_UsesStableBottomAnchoringAndDiscreteTailRequests()
Assert.DoesNotContain("ChangeView", binding);
Assert.DoesNotContain("UpdateLayout", binding);
Assert.DoesNotContain("TailSettle", binding);
Assert.DoesNotContain("ScrollTo(", binding);
Assert.DoesNotContain("ScrollCompleted", binding);
Assert.DoesNotContain("DispatcherTimer", binding);
Assert.DoesNotContain("TextLength != current.TextLength", binding);
Expand All @@ -96,6 +98,7 @@ public void ReactorTimeline_UsesStableBottomAnchoringAndDiscreteTailRequests()
var viewChanged = binding[viewChangedStart..tailRequestStart];
Assert.DoesNotContain("VerticalAnchorRatio", viewChanged);
Assert.DoesNotContain("StartBringItemIntoView", viewChanged);
Assert.DoesNotContain("ScrollTo(", viewChanged);
}

[Fact]
Expand Down
Loading