From e76593f3bbec596a972a68c536d9058e1751a67a Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Wed, 30 Jul 2025 10:18:01 +0800 Subject: [PATCH 1/6] Fix continious scroll to prevent jumping to the top --- src/Aspire.Dashboard/wwwroot/js/app.js | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/Aspire.Dashboard/wwwroot/js/app.js b/src/Aspire.Dashboard/wwwroot/js/app.js index 90aaf815b2a..280a752b9c1 100644 --- a/src/Aspire.Dashboard/wwwroot/js/app.js +++ b/src/Aspire.Dashboard/wwwroot/js/app.js @@ -62,6 +62,7 @@ window.getIsScrolledToContent = function () { window.setIsScrolledToContent = function (value) { if (isScrolledToContent != value) { isScrolledToContent = value; + console.log(`isScrolledToContent=${isScrolledToContent}`); } } @@ -82,8 +83,11 @@ window.initializeContinuousScroll = function () { // The scroll event is used to detect when the user scrolls to view content. container.addEventListener('scroll', () => { - var v = !isScrolledToBottom(container); - setIsScrolledToContent(v); + var atBottom = isScrolledToBottom(container); + if (atBottom === null) { + return; + } + setIsScrolledToContent(!atBottom); }, { passive: true }); // The ResizeObserver reports changes in the grid size. @@ -91,7 +95,10 @@ window.initializeContinuousScroll = function () { // unless the user has scrolled to view content. const observer = new ResizeObserver(function () { lastScrollHeight = container.scrollHeight; - if (!getIsScrolledToContent()) { + var isScrolledToContent = getIsScrolledToContent(); + + if (!isScrolledToContent) { + console.log(`lastScrollHeight=${lastScrollHeight}`); container.scrollTop = lastScrollHeight; } }); @@ -108,6 +115,7 @@ function isScrolledToBottom(container) { if (!getIsScrolledToContent()) { if (lastScrollHeight != container.scrollHeight) { console.log(`lastScrollHeight ${lastScrollHeight} doesn't equal container scrollHeight ${container.scrollHeight}.`); + return null; } } @@ -115,7 +123,8 @@ function isScrolledToBottom(container) { const containerScrollBottom = lastScrollHeight - container.clientHeight; const difference = containerScrollBottom - container.scrollTop; - return difference < marginOfError; + var atBottom = difference < marginOfError; + return atBottom; } window.buttonOpenLink = function (element) { From 0ade1acd9fa39eb6c84c79f6f05f4cb740bae2a8 Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Wed, 30 Jul 2025 12:15:19 +0800 Subject: [PATCH 2/6] Overscan counts --- src/Aspire.Dashboard/Components/Controls/LogViewer.razor | 2 +- .../Components/Dialogs/TextVisualizerDialog.razor | 2 +- src/Aspire.Dashboard/Components/Pages/Resources.razor | 1 + src/Aspire.Dashboard/Components/Pages/StructuredLogs.razor | 1 + src/Aspire.Dashboard/Components/Pages/StructuredLogs.razor.cs | 2 ++ src/Aspire.Dashboard/Components/Pages/TraceDetail.razor | 1 + src/Aspire.Dashboard/Components/Pages/Traces.razor | 1 + 7 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/Aspire.Dashboard/Components/Controls/LogViewer.razor b/src/Aspire.Dashboard/Components/Controls/LogViewer.razor index 2bc3915e7ba..75031c1ac1f 100644 --- a/src/Aspire.Dashboard/Components/Controls/LogViewer.razor +++ b/src/Aspire.Dashboard/Components/Controls/LogViewer.razor @@ -13,7 +13,7 @@
@if (LogEntries is { } logEntries) { - + @if (context.Pause is { } pause) { // If this is a previous pause but no logs were obtained during the pause, we don't need to show anything. diff --git a/src/Aspire.Dashboard/Components/Dialogs/TextVisualizerDialog.razor b/src/Aspire.Dashboard/Components/Dialogs/TextVisualizerDialog.razor index ae3a236326c..be8e050cb83 100644 --- a/src/Aspire.Dashboard/Components/Dialogs/TextVisualizerDialog.razor +++ b/src/Aspire.Dashboard/Components/Dialogs/TextVisualizerDialog.razor @@ -35,7 +35,7 @@ {
- +
diff --git a/src/Aspire.Dashboard/Components/Pages/Resources.razor b/src/Aspire.Dashboard/Components/Pages/Resources.razor index f2395e9c2ad..660b9abd5c7 100644 --- a/src/Aspire.Dashboard/Components/Pages/Resources.razor +++ b/src/Aspire.Dashboard/Components/Pages/Resources.razor @@ -127,6 +127,7 @@ Virtualize="true" GenerateHeader="GenerateHeaderOption.Sticky" ItemSize="46" + OverscanCount="100" ItemsProvider="@GetData" ResizableColumns="true" GridTemplateColumns="@_manager.GetGridTemplateColumns()" diff --git a/src/Aspire.Dashboard/Components/Pages/StructuredLogs.razor b/src/Aspire.Dashboard/Components/Pages/StructuredLogs.razor index 0fe48bfdff6..b3ce1a5dba2 100644 --- a/src/Aspire.Dashboard/Components/Pages/StructuredLogs.razor +++ b/src/Aspire.Dashboard/Components/Pages/StructuredLogs.razor @@ -127,6 +127,7 @@ RowClass="@GetRowClass" GenerateHeader="GenerateHeaderOption.Sticky" ItemSize="46" + OverscanCount="100" ResizableColumns="true" ItemsProvider="@GetData" TGridItem="OtlpLogEntry" diff --git a/src/Aspire.Dashboard/Components/Pages/StructuredLogs.razor.cs b/src/Aspire.Dashboard/Components/Pages/StructuredLogs.razor.cs index 970e5eda546..74c23d56420 100644 --- a/src/Aspire.Dashboard/Components/Pages/StructuredLogs.razor.cs +++ b/src/Aspire.Dashboard/Components/Pages/StructuredLogs.razor.cs @@ -143,6 +143,8 @@ private async ValueTask> GetData(GridItems TelemetryRepository.MarkViewedErrorLogs(ViewModel.ApplicationKey); + Logger.LogInformation($"Start index: {request.StartIndex}, count: {request.Count}, Result item count: {logs.TotalItemCount}"); + return GridItemsProviderResult.From(logs.Items, logs.TotalItemCount); } diff --git a/src/Aspire.Dashboard/Components/Pages/TraceDetail.razor b/src/Aspire.Dashboard/Components/Pages/TraceDetail.razor index 0b2180fc2ec..3bc2f389844 100644 --- a/src/Aspire.Dashboard/Components/Pages/TraceDetail.razor +++ b/src/Aspire.Dashboard/Components/Pages/TraceDetail.razor @@ -97,6 +97,7 @@ RowClass="@GetRowClass" GridTemplateColumns="@_manager.GetGridTemplateColumns()" RowSize="DataGridRowSize.Small" + OverscanCount="200" ShowHover="true" ItemKey="@(r => r.Span.SpanId)" OnRowClick="@(r => r.ExecuteOnDefault(d => OnShowPropertiesAsync(d, buttonId: null)))"> diff --git a/src/Aspire.Dashboard/Components/Pages/Traces.razor b/src/Aspire.Dashboard/Components/Pages/Traces.razor index 685a240590b..c542269473f 100644 --- a/src/Aspire.Dashboard/Components/Pages/Traces.razor +++ b/src/Aspire.Dashboard/Components/Pages/Traces.razor @@ -83,6 +83,7 @@ Virtualize="true" GenerateHeader="GenerateHeaderOption.Sticky" ItemSize="46" + OverscanCount="100" ResizableColumns="true" ItemsProvider="@GetData" TGridItem="OtlpTrace" From e53bd75e57178afb80806f01edd051cb6bef68b1 Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Wed, 30 Jul 2025 12:25:51 +0800 Subject: [PATCH 3/6] Update --- src/Aspire.Dashboard/Components/Controls/LogViewer.razor | 2 +- .../Components/Dialogs/TextVisualizerDialog.razor | 2 +- src/Aspire.Dashboard/Components/Pages/StructuredLogs.razor.cs | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Aspire.Dashboard/Components/Controls/LogViewer.razor b/src/Aspire.Dashboard/Components/Controls/LogViewer.razor index 75031c1ac1f..2bc3915e7ba 100644 --- a/src/Aspire.Dashboard/Components/Controls/LogViewer.razor +++ b/src/Aspire.Dashboard/Components/Controls/LogViewer.razor @@ -13,7 +13,7 @@
@if (LogEntries is { } logEntries) { - + @if (context.Pause is { } pause) { // If this is a previous pause but no logs were obtained during the pause, we don't need to show anything. diff --git a/src/Aspire.Dashboard/Components/Dialogs/TextVisualizerDialog.razor b/src/Aspire.Dashboard/Components/Dialogs/TextVisualizerDialog.razor index be8e050cb83..ae3a236326c 100644 --- a/src/Aspire.Dashboard/Components/Dialogs/TextVisualizerDialog.razor +++ b/src/Aspire.Dashboard/Components/Dialogs/TextVisualizerDialog.razor @@ -35,7 +35,7 @@ {
- +
diff --git a/src/Aspire.Dashboard/Components/Pages/StructuredLogs.razor.cs b/src/Aspire.Dashboard/Components/Pages/StructuredLogs.razor.cs index 74c23d56420..944507d642a 100644 --- a/src/Aspire.Dashboard/Components/Pages/StructuredLogs.razor.cs +++ b/src/Aspire.Dashboard/Components/Pages/StructuredLogs.razor.cs @@ -136,6 +136,8 @@ private async ValueTask> GetData(GridItems message.Close(); } + Logger.LogTrace("Log data updated. Start index: {RequestStartIndex}, count: {RequestCount}, result item count: {ResultTotalItemCount}", request.StartIndex, request.Count, logs.TotalItemCount); + // Updating the total item count as a field doesn't work because it isn't updated with the grid. // The workaround is to explicitly update and refresh the control. _totalItemsCount = logs.TotalItemCount; @@ -143,8 +145,6 @@ private async ValueTask> GetData(GridItems TelemetryRepository.MarkViewedErrorLogs(ViewModel.ApplicationKey); - Logger.LogInformation($"Start index: {request.StartIndex}, count: {request.Count}, Result item count: {logs.TotalItemCount}"); - return GridItemsProviderResult.From(logs.Items, logs.TotalItemCount); } From b68a4e971e76cbaf9b2d2462357dfba114e491a6 Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Wed, 30 Jul 2025 12:28:16 +0800 Subject: [PATCH 4/6] Update --- src/Aspire.Dashboard/Components/Pages/TraceDetail.razor | 2 +- src/Aspire.Dashboard/wwwroot/js/app.js | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Aspire.Dashboard/Components/Pages/TraceDetail.razor b/src/Aspire.Dashboard/Components/Pages/TraceDetail.razor index 3bc2f389844..0df4ca5625c 100644 --- a/src/Aspire.Dashboard/Components/Pages/TraceDetail.razor +++ b/src/Aspire.Dashboard/Components/Pages/TraceDetail.razor @@ -97,7 +97,7 @@ RowClass="@GetRowClass" GridTemplateColumns="@_manager.GetGridTemplateColumns()" RowSize="DataGridRowSize.Small" - OverscanCount="200" + OverscanCount="100" ShowHover="true" ItemKey="@(r => r.Span.SpanId)" OnRowClick="@(r => r.ExecuteOnDefault(d => OnShowPropertiesAsync(d, buttonId: null)))"> diff --git a/src/Aspire.Dashboard/wwwroot/js/app.js b/src/Aspire.Dashboard/wwwroot/js/app.js index 280a752b9c1..7a0554c4673 100644 --- a/src/Aspire.Dashboard/wwwroot/js/app.js +++ b/src/Aspire.Dashboard/wwwroot/js/app.js @@ -115,6 +115,8 @@ function isScrolledToBottom(container) { if (!getIsScrolledToContent()) { if (lastScrollHeight != container.scrollHeight) { console.log(`lastScrollHeight ${lastScrollHeight} doesn't equal container scrollHeight ${container.scrollHeight}.`); + + // Unknown because the container size changed. return null; } } From a81d143c5dc634d4add954c6fc6b5d79469f14d7 Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Wed, 30 Jul 2025 12:40:44 +0800 Subject: [PATCH 5/6] Continious scroll is re-enabled when there is no scrollbar --- src/Aspire.Dashboard/wwwroot/js/app.js | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/Aspire.Dashboard/wwwroot/js/app.js b/src/Aspire.Dashboard/wwwroot/js/app.js index 7a0554c4673..e3efba15c06 100644 --- a/src/Aspire.Dashboard/wwwroot/js/app.js +++ b/src/Aspire.Dashboard/wwwroot/js/app.js @@ -95,11 +95,18 @@ window.initializeContinuousScroll = function () { // unless the user has scrolled to view content. const observer = new ResizeObserver(function () { lastScrollHeight = container.scrollHeight; - var isScrolledToContent = getIsScrolledToContent(); + if (lastScrollHeight == container.clientHeight) { + // There is no scrollbar. This could be because there's no content, or the content might have been cleared. + // Reset to default behavior: scroll to bottom + setIsScrolledToContent(false); + return; + } + + var isScrolledToContent = getIsScrolledToContent(); if (!isScrolledToContent) { - console.log(`lastScrollHeight=${lastScrollHeight}`); container.scrollTop = lastScrollHeight; + return; } }); for (const child of container.children) { From 2cfc74e2b3a1c87331a3fa9b88b876f6a15859ac Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Thu, 7 Aug 2025 07:27:49 +0800 Subject: [PATCH 6/6] Remove page logging --- src/Aspire.Dashboard/Components/Pages/StructuredLogs.razor.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Aspire.Dashboard/Components/Pages/StructuredLogs.razor.cs b/src/Aspire.Dashboard/Components/Pages/StructuredLogs.razor.cs index 944507d642a..970e5eda546 100644 --- a/src/Aspire.Dashboard/Components/Pages/StructuredLogs.razor.cs +++ b/src/Aspire.Dashboard/Components/Pages/StructuredLogs.razor.cs @@ -136,8 +136,6 @@ private async ValueTask> GetData(GridItems message.Close(); } - Logger.LogTrace("Log data updated. Start index: {RequestStartIndex}, count: {RequestCount}, result item count: {ResultTotalItemCount}", request.StartIndex, request.Count, logs.TotalItemCount); - // Updating the total item count as a field doesn't work because it isn't updated with the grid. // The workaround is to explicitly update and refresh the control. _totalItemsCount = logs.TotalItemCount;