Fixed the source counter of an answer doing nothing when clicked

This commit is contained in:
Thorsten Sommer committed 2026-09-15 17:38:36 +02:00
1 parent baa48c80e1
commit 408e06e9c0
4 files changed
+44 -3

No files matched your search

@@ -48,7 +48,7 @@
{ {
<MudTooltip Text="@T("Number of sources")" Placement="Placement.Bottom"> <MudTooltip Text="@T("Number of sources")" Placement="Placement.Bottom">
<MudBadge Content="@this.Content.Sources.Count" Color="Color.Primary" Overlap="true" BadgeClass="sources-card-header"> <MudBadge Content="@this.Content.Sources.Count" Color="Color.Primary" Overlap="true" BadgeClass="sources-card-header">
<MudIconButton Icon="@Icons.Material.Filled.Link"/> <MudIconButton Icon="@Icons.Material.Filled.Link" Disabled="@(!this.HasSourcesToShow)" OnClick="@this.ShowSources"/>
</MudBadge> </MudBadge>
</MudTooltip> </MudTooltip>
} }
@@ -223,7 +223,7 @@
} }
@if (textContent.Sources.Count > 0) @if (textContent.Sources.Count > 0)
{ {
<SourcesList Sources="@textContent.Sources"/> <SourcesList @ref="this.sourcesList" Sources="@textContent.Sources"/>
} }
</div> </div>
} }
@@ -123,6 +123,7 @@ public partial class ContentBlockComponent : MSGComponentBase
private IReadOnlyList<MessageTable> cachedMessageTables = []; private IReadOnlyList<MessageTable> cachedMessageTables = [];
private char csvSeparator = ','; private char csvSeparator = ',';
private ElementReference mathContentContainer; private ElementReference mathContentContainer;
private SourcesList? sourcesList;
private string lastMathRenderSignature = string.Empty; private string lastMathRenderSignature = string.Empty;
private bool hasActiveMathContainer; private bool hasActiveMathContainer;
private bool isDisposed; private bool isDisposed;
@@ -815,6 +816,25 @@ public partial class ContentBlockComponent : MSGComponentBase
this.Content.FileAttachments = [.. result]; this.Content.FileAttachments = [.. result];
} }
/// <summary>
/// Whether the sources of this block stand below the answer, where the counter can take the reader.
/// </summary>
/// <remarks>
/// The same condition the block itself renders the list under. While an answer is still coming
/// in, its sources may already be known, but there is nothing on the page yet to scroll to --
/// so the counter says it cannot do anything rather than doing nothing when clicked.
/// </remarks>
private bool HasSourcesToShow => this.Content is { InitialRemoteWait: false, IsStreaming: false, Sources.Count: > 0 };
/// <summary>
/// Takes the reader from the source counter down to the sources themselves.
/// </summary>
private async Task ShowSources()
{
if (this.sourcesList is not null)
await this.sourcesList.ScrollIntoViewAsync();
}
protected override async ValueTask DisposeResourcesAsync() protected override async ValueTask DisposeResourcesAsync()
{ {
if (this.isDisposed) if (this.isDisposed)
@@ -2,7 +2,7 @@
@* The class is what the Markdown renderer wraps its own output in, so the headings and the list @* The class is what the Markdown renderer wraps its own output in, so the headings and the list
keep the look they had while this list was Markdown. *@ keep the look they had while this list was Markdown. *@
<div class="mud-markdown-body"> <div @ref="this.listElement" class="mud-markdown-body">
@foreach (var group in this.groups) @foreach (var group in this.groups)
{ {
@* A level-two heading was shown as h5 while this list was Markdown, because that is what @* A level-two heading was shown as h5 while this list was Markdown, because that is what
@@ -17,6 +17,12 @@ namespace AIStudio.Components;
/// </remarks> /// </remarks>
public partial class SourcesList : MSGComponentBase public partial class SourcesList : MSGComponentBase
{ {
//
// The name is about the alignment the function uses, not about the page: it brings the element
// into view with its end at the bottom, which for a list at the end of an answer shows all of it.
//
private const string SCROLL_INTO_VIEW_FUNCTION = "scrollToBottom";
/// <summary> /// <summary>
/// The sources to show. /// The sources to show.
/// </summary> /// </summary>
@@ -26,11 +32,26 @@ public partial class SourcesList : MSGComponentBase
[Inject] [Inject]
private RustService RustService { get; init; } = null!; private RustService RustService { get; init; } = null!;
[Inject]
private IJSRuntime JsRuntime { get; init; } = null!;
[Inject] [Inject]
private ILogger<SourcesList> Logger { get; init; } = null!; private ILogger<SourcesList> Logger { get; init; } = null!;
private readonly List<SourceEntryGroup> groups = []; private readonly List<SourceEntryGroup> groups = [];
private ElementReference listElement;
/// <summary>
/// Brings this list into view.
/// </summary>
/// <remarks>
/// The counter above an answer says how many sources it rests on; this is how it takes the
/// reader to them. The element stays here, where it is rendered, rather than being handed to
/// whoever wants to scroll to it.
/// </remarks>
public async Task ScrollIntoViewAsync() => await this.JsRuntime.TryInvokeVoidAsync(this.CircuitState, SCROLL_INTO_VIEW_FUNCTION, this.listElement);
#region Overrides of ComponentBase #region Overrides of ComponentBase
protected override async Task OnParametersSetAsync() protected override async Task OnParametersSetAsync()