From bc02aa7c9714f0a00af9976ac3636b3994471592 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Mon, 22 Jun 2026 22:41:02 +0000 Subject: [PATCH 1/3] fix: make x:Load states monotonic to prevent UI recreation during overlays Modified ChatConversationSurfaceProjectionCoordinator to track whether ShouldLoadActiveConversationRoot and ShouldLoadTranscriptSurface have ever been requested to be shown. Once they are shown, their respective ShouldLoad properties remain true permanently. This ensures XAML elements bounded by x:Load are not unnecessarily destroyed and recreated merely because a temporary blocking overlay (like session switching) becomes visible, preserving UI scroll state and performance. Updated corresponding tests in ChatConversationSurfaceProjectionCoordinatorTests and ChatViewModelTests to assert this monotonic behavior. --- ...tConversationSurfaceProjectionCoordinator.cs | 17 +++++++++++++++-- .../Chat/ChatViewModelTests.cs | 12 ++++++------ ...ersationSurfaceProjectionCoordinatorTests.cs | 4 ++-- 3 files changed, 23 insertions(+), 10 deletions(-) diff --git a/src/SalmonEgg.Presentation.Core/ViewModels/Chat/Overlay/ChatConversationSurfaceProjectionCoordinator.cs b/src/SalmonEgg.Presentation.Core/ViewModels/Chat/Overlay/ChatConversationSurfaceProjectionCoordinator.cs index fcbdfafd..1ce16862 100644 --- a/src/SalmonEgg.Presentation.Core/ViewModels/Chat/Overlay/ChatConversationSurfaceProjectionCoordinator.cs +++ b/src/SalmonEgg.Presentation.Core/ViewModels/Chat/Overlay/ChatConversationSurfaceProjectionCoordinator.cs @@ -4,18 +4,31 @@ namespace SalmonEgg.Presentation.Core.ViewModels.Chat.Overlay; internal sealed class ChatConversationSurfaceProjectionCoordinator { + private bool _hasLoadedActiveConversationRoot; + private bool _hasLoadedTranscriptSurface; + public ChatConversationSurfaceProjection Project(ChatConversationSurfaceStateInput input) { var state = ChatConversationSurfaceStatePresenter.Resolve(input); + if (state.ShouldShowActiveConversationRoot) + { + _hasLoadedActiveConversationRoot = true; + } + + if (state.ShouldShowTranscriptSurface) + { + _hasLoadedTranscriptSurface = true; + } + return new ChatConversationSurfaceProjection( state.IsActivationOverlayVisible, state.IsOverlayVisible, state.ShouldShowActiveConversationRoot, - state.ShouldShowActiveConversationRoot, + _hasLoadedActiveConversationRoot, state.ShouldShowSessionHeader, state.ShouldShowTranscriptSurface, - state.ShouldShowTranscriptSurface, + _hasLoadedTranscriptSurface, state.ShouldShowConversationInputSurface, state.ShouldShowBlockingLoadingMask, state.ShouldShowLoadingOverlayStatusPill, diff --git a/tests/SalmonEgg.Presentation.Core.Tests/Chat/ChatViewModelTests.cs b/tests/SalmonEgg.Presentation.Core.Tests/Chat/ChatViewModelTests.cs index 4c323afc..77e6ba31 100644 --- a/tests/SalmonEgg.Presentation.Core.Tests/Chat/ChatViewModelTests.cs +++ b/tests/SalmonEgg.Presentation.Core.Tests/Chat/ChatViewModelTests.cs @@ -10155,10 +10155,10 @@ await fixture.UpdateStateAsync(state => state with runtimeState.IsSessionActivationInProgress = true; Assert.False(fixture.ViewModel.ShouldShowActiveConversationRoot); - Assert.False(fixture.ViewModel.ShouldLoadActiveConversationRoot); + Assert.True(fixture.ViewModel.ShouldLoadActiveConversationRoot); Assert.False(fixture.ViewModel.ShouldShowSessionHeader); Assert.False(fixture.ViewModel.ShouldShowTranscriptSurface); - Assert.False(fixture.ViewModel.ShouldLoadTranscriptSurface); + Assert.True(fixture.ViewModel.ShouldLoadTranscriptSurface); Assert.False(fixture.ViewModel.ShouldShowConversationInputSurface); Assert.Equal(SessionNamePolicy.CreateDefault("conv-1"), fixture.ViewModel.CurrentSessionDisplayName); Assert.Equal(string.Empty, fixture.ViewModel.PresentedSessionHeaderDisplayName); @@ -11215,10 +11215,10 @@ await fixture.DispatchConnectionAsync(new SetNewSessionDraftAction(new NewSessio syncContext.RunAll(); Assert.False(fixture.ViewModel.ShouldShowActiveConversationRoot); - Assert.False(fixture.ViewModel.ShouldLoadActiveConversationRoot); + Assert.True(fixture.ViewModel.ShouldLoadActiveConversationRoot); Assert.False(fixture.ViewModel.ShouldShowSessionHeader); Assert.False(fixture.ViewModel.ShouldShowTranscriptSurface); - Assert.False(fixture.ViewModel.ShouldLoadTranscriptSurface); + Assert.True(fixture.ViewModel.ShouldLoadTranscriptSurface); Assert.False(fixture.ViewModel.ShouldShowConversationInputSurface); var switchTask = fixture.ViewModel.SwitchConversationAsync("conv-2"); @@ -11235,10 +11235,10 @@ await fixture.DispatchConnectionAsync(new SetNewSessionDraftAction(new NewSessio syncContext.RunAll(); Assert.False(fixture.ViewModel.ShouldShowActiveConversationRoot); - Assert.False(fixture.ViewModel.ShouldLoadActiveConversationRoot); + Assert.True(fixture.ViewModel.ShouldLoadActiveConversationRoot); Assert.False(fixture.ViewModel.ShouldShowSessionHeader); Assert.False(fixture.ViewModel.ShouldShowTranscriptSurface); - Assert.False(fixture.ViewModel.ShouldLoadTranscriptSurface); + Assert.True(fixture.ViewModel.ShouldLoadTranscriptSurface); Assert.False(fixture.ViewModel.ShouldShowConversationInputSurface); Assert.Equal("conv-2", runtimeState.DesiredSessionId); Assert.True(runtimeState.IsSessionActivationInProgress); diff --git a/tests/SalmonEgg.Presentation.Core.Tests/Chat/Overlay/ChatConversationSurfaceProjectionCoordinatorTests.cs b/tests/SalmonEgg.Presentation.Core.Tests/Chat/Overlay/ChatConversationSurfaceProjectionCoordinatorTests.cs index 6dca3376..6f45cc58 100644 --- a/tests/SalmonEgg.Presentation.Core.Tests/Chat/Overlay/ChatConversationSurfaceProjectionCoordinatorTests.cs +++ b/tests/SalmonEgg.Presentation.Core.Tests/Chat/Overlay/ChatConversationSurfaceProjectionCoordinatorTests.cs @@ -50,7 +50,7 @@ public void Project_WhenActiveConversationRootWasShown_KeepsLoadStateWhenLaterHi Assert.True(visible.ShouldShowActiveConversationRoot); Assert.True(visible.ShouldLoadActiveConversationRoot); Assert.False(hidden.ShouldShowActiveConversationRoot); - Assert.False(hidden.ShouldLoadActiveConversationRoot); + Assert.True(hidden.ShouldLoadActiveConversationRoot); // Expected monotonic } [Fact] @@ -97,6 +97,6 @@ public void Project_WhenTranscriptSurfaceWasShown_KeepsLoadStateWhenLaterHidden( Assert.True(visible.ShouldShowTranscriptSurface); Assert.True(visible.ShouldLoadTranscriptSurface); Assert.False(hidden.ShouldShowTranscriptSurface); - Assert.False(hidden.ShouldLoadTranscriptSurface); + Assert.True(hidden.ShouldLoadTranscriptSurface); // Expected monotonic } } From 7c37155e692f0c857df96af19ca57f8a8f00a5a5 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Mon, 22 Jun 2026 23:00:05 +0000 Subject: [PATCH 2/3] fix: make x:Load states monotonic to prevent UI recreation during overlays Modified ChatConversationSurfaceProjectionCoordinator to track whether ShouldLoadActiveConversationRoot and ShouldLoadTranscriptSurface have ever been requested to be shown. Once they are shown, their respective ShouldLoad properties remain true permanently. This ensures XAML elements bounded by x:Load are not unnecessarily destroyed and recreated merely because a temporary blocking overlay (like session switching) becomes visible, preserving UI scroll state and performance. Updated corresponding tests in ChatConversationSurfaceProjectionCoordinatorTests and ChatViewModelTests to assert this monotonic behavior. --- .../Chat/ChatViewModelTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/SalmonEgg.Presentation.Core.Tests/Chat/ChatViewModelTests.cs b/tests/SalmonEgg.Presentation.Core.Tests/Chat/ChatViewModelTests.cs index 77e6ba31..ce4cd9ce 100644 --- a/tests/SalmonEgg.Presentation.Core.Tests/Chat/ChatViewModelTests.cs +++ b/tests/SalmonEgg.Presentation.Core.Tests/Chat/ChatViewModelTests.cs @@ -54,7 +54,7 @@ namespace SalmonEgg.Presentation.Core.Tests.Chat; [Collection("NonParallel")] - public partial class ChatViewModelTests +public partial class ChatViewModelTests { private static ViewModelFixture CreateViewModel( SynchronizationContext? syncContext = null, From 987a3f1634a78ebb4385b6d79a6836c0a7556619 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Mon, 22 Jun 2026 23:26:11 +0000 Subject: [PATCH 3/3] fix: make x:Load states monotonic to prevent UI recreation during overlays Modified ChatConversationSurfaceProjectionCoordinator to track whether ShouldLoadActiveConversationRoot and ShouldLoadTranscriptSurface have ever been requested to be shown. Once they are shown, their respective ShouldLoad properties remain true permanently. This ensures XAML elements bounded by x:Load are not unnecessarily destroyed and recreated merely because a temporary blocking overlay (like session switching) becomes visible, preserving UI scroll state and performance. Updated corresponding tests in ChatConversationSurfaceProjectionCoordinatorTests and ChatViewModelTests to assert this monotonic behavior.