mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2026-10-17 17:57:59 +00:00
Show data sources as collapsible panels and reach their settings
This commit is contained in:
8 files changed
+263
-121
No files matched your search
@@ -8959,6 +8959,9 @@ UI_TEXT_CONTENT["AISTUDIO::PAGES::EMBEDDINGS::T1946414905"] = "Tried again durin
|
|||||||
-- Skipped files: {0}
|
-- Skipped files: {0}
|
||||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::EMBEDDINGS::T196379388"] = "Skipped files: {0}"
|
UI_TEXT_CONTENT["AISTUDIO::PAGES::EMBEDDINGS::T196379388"] = "Skipped files: {0}"
|
||||||
|
|
||||||
|
-- Manage your data sources
|
||||||
|
UI_TEXT_CONTENT["AISTUDIO::PAGES::EMBEDDINGS::T2149927097"] = "Manage your data sources"
|
||||||
|
|
||||||
-- Noticed
|
-- Noticed
|
||||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::EMBEDDINGS::T2367007983"] = "Noticed"
|
UI_TEXT_CONTENT["AISTUDIO::PAGES::EMBEDDINGS::T2367007983"] = "Noticed"
|
||||||
|
|
||||||
@@ -8980,6 +8983,9 @@ UI_TEXT_CONTENT["AISTUDIO::PAGES::EMBEDDINGS::T2547971789"] = "Background embedd
|
|||||||
-- Refresh this data source
|
-- Refresh this data source
|
||||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::EMBEDDINGS::T2901874229"] = "Refresh this data source"
|
UI_TEXT_CONTENT["AISTUDIO::PAGES::EMBEDDINGS::T2901874229"] = "Refresh this data source"
|
||||||
|
|
||||||
|
-- Data source: {0}
|
||||||
|
UI_TEXT_CONTENT["AISTUDIO::PAGES::EMBEDDINGS::T2945218010"] = "Data source: {0}"
|
||||||
|
|
||||||
-- Embedding provider: {0}
|
-- Embedding provider: {0}
|
||||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::EMBEDDINGS::T300213237"] = "Embedding provider: {0}"
|
UI_TEXT_CONTENT["AISTUDIO::PAGES::EMBEDDINGS::T300213237"] = "Embedding provider: {0}"
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
<MudExpansionPanel Class="border-solid border rounded-lg" Expanded="@this.IsExpanded" MaxHeight="@this.MaxHeight" ExpandedChanged="async s => await this.ExpandedChanged(s)">
|
<MudExpansionPanel Class="border-solid border rounded-lg" HeaderClass="@this.HeaderClass" Expanded="@this.IsExpanded" MaxHeight="@this.MaxHeight" ExpandedChanged="async s => await this.ExpandedChanged(s)">
|
||||||
<TitleContent>
|
<TitleContent>
|
||||||
<div class="d-flex align-center">
|
<div class="d-flex align-center">
|
||||||
<MudIcon Icon="@this.HeaderIcon" Size="@this.IconSize" Color="@this.IconColor" class="mr-3"/>
|
<MudIcon Icon="@this.HeaderIcon" Size="@this.IconSize" Color="@this.IconColor" class="mr-3"/>
|
||||||
<MudText Typo="Typo.h6">
|
<MudText Typo="@this.HeaderTypo">
|
||||||
@this.HeaderText
|
@this.HeaderText
|
||||||
</MudText>
|
</MudText>
|
||||||
@if (this.ShowEndButton)
|
@if (this.ShowEndButton)
|
||||||
|
|||||||
@@ -15,7 +15,27 @@ public partial class ExpansionPanel : ComponentBase
|
|||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public string HeaderText { get; set; } = "n/a";
|
public string HeaderText { get; set; } = "n/a";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The typography of the header text.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Worth lowering for a panel which sits inside another one, together with the compact header
|
||||||
|
/// class below: two headers of the same size give no clue about which one contains the other.
|
||||||
|
/// </remarks>
|
||||||
|
[Parameter]
|
||||||
|
public Typo HeaderTypo { get; set; } = Typo.h6;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Additional class names for the header, separated by space.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// The one this exists for is expansion-panel-header-compact, which takes the height of the
|
||||||
|
/// header down for a nested panel.
|
||||||
|
/// </remarks>
|
||||||
|
[Parameter]
|
||||||
|
public string HeaderClass { get; set; } = string.Empty;
|
||||||
|
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public int? MaxHeight { get; set; }
|
public int? MaxHeight { get; set; }
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,13 @@
|
|||||||
|
|
||||||
<MudStack Spacing="3" Class="pr-2 pb-4" Style="height: 100%; min-height: 0; overflow-y: auto;">
|
<MudStack Spacing="3" Class="pr-2 pb-4" Style="height: 100%; min-height: 0; overflow-y: auto;">
|
||||||
<MudPaper Class="pa-4 border-dashed border rounded-lg" Elevation="0">
|
<MudPaper Class="pa-4 border-dashed border rounded-lg" Elevation="0">
|
||||||
<MudText Typo="Typo.h5">@T("Background embeddings")</MudText>
|
<MudStack Row="true" AlignItems="AlignItems.Center">
|
||||||
|
<MudText Typo="Typo.h5">@T("Background embeddings")</MudText>
|
||||||
|
<MudSpacer/>
|
||||||
|
<MudTooltip Text="@T("Manage your data sources")" Placement="Placement.Top">
|
||||||
|
<MudIconButton Icon="@Icons.Material.Filled.Settings" OnClick="@this.OpenDataSourceSettings"/>
|
||||||
|
</MudTooltip>
|
||||||
|
</MudStack>
|
||||||
<MudText Typo="Typo.body1" Class="mt-2">
|
<MudText Typo="Typo.body1" Class="mt-2">
|
||||||
@T("AI Studio indexes local RAG data sources in the background. Finished files stay recorded so unchanged files can be skipped after a restart, while added or deleted files are detected during the next run. The same applies to documents without readable text, such as scanned pages: AI Studio remembers them and reads them again only once they change.")
|
@T("AI Studio indexes local RAG data sources in the background. Finished files stay recorded so unchanged files can be skipped after a restart, while added or deleted files are detected during the next run. The same applies to documents without readable text, such as scanned pages: AI Studio remembers them and reads them again only once they change.")
|
||||||
</MudText>
|
</MudText>
|
||||||
@@ -23,133 +29,148 @@
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@foreach (var status in this.Statuses)
|
@*
|
||||||
{
|
One panel per data source, and only one of them open: a folder with thousands of files
|
||||||
<MudPaper Class="pa-4 border rounded-lg" Elevation="0">
|
fills the page by itself. What a closed panel still has to say stays in its header, so
|
||||||
<MudStack Spacing="2">
|
nobody has to open every one of them to see how their data sources are doing.
|
||||||
<MudStack Row="true" Justify="Justify.SpaceBetween" AlignItems="AlignItems.Center">
|
*@
|
||||||
<MudText Typo="Typo.h6">@status.DataSourceName</MudText>
|
<MudExpansionPanels>
|
||||||
<MudStack Row="true" Spacing="1" AlignItems="AlignItems.Center">
|
@foreach (var status in this.Statuses)
|
||||||
|
{
|
||||||
|
<MudExpansionPanel Class="border-solid border rounded-lg" Expanded="@(this.expandedDataSourceId == status.DataSourceId)" ExpandedChanged="@(isExpanded => this.DataSourcePanelExpandedChanged(status, isExpanded))">
|
||||||
|
<TitleContent>
|
||||||
|
<div class="d-flex align-center">
|
||||||
|
<MudText Typo="Typo.h6">@string.Format(T("Data source: {0}"), status.DataSourceName)</MudText>
|
||||||
|
<MudSpacer/>
|
||||||
<MudChip T="string" Color="@GetStatusColor(status)" Variant="Variant.Filled">@status.StateLabel</MudChip>
|
<MudChip T="string" Color="@GetStatusColor(status)" Variant="Variant.Filled">@status.StateLabel</MudChip>
|
||||||
@if (CanRefresh(status))
|
@if (CanRefresh(status))
|
||||||
{
|
{
|
||||||
|
@*
|
||||||
|
This opens or closes the panel along the way. The header is what
|
||||||
|
toggles it, and neither stopping the event nor swallowing the
|
||||||
|
click works in this project — see the end button of our own
|
||||||
|
ExpansionPanel component, which behaves the same way.
|
||||||
|
*@
|
||||||
<MudTooltip Text="@T("Refresh this data source")">
|
<MudTooltip Text="@T("Refresh this data source")">
|
||||||
<MudIconButton Icon="@Icons.Material.Filled.Sync" Color="Color.Info" Size="Size.Small" OnClick="@(async () => await this.RefreshDataSource(status))" />
|
<MudIconButton Icon="@Icons.Material.Filled.Sync" Color="Color.Info" Size="Size.Small" OnClick="@(async () => await this.RefreshDataSource(status))" />
|
||||||
</MudTooltip>
|
</MudTooltip>
|
||||||
}
|
}
|
||||||
</MudStack>
|
</div>
|
||||||
</MudStack>
|
</TitleContent>
|
||||||
|
<ChildContent>
|
||||||
|
<MudStack Spacing="2">
|
||||||
|
<MudProgressLinear Value="@status.ProgressPercent" Rounded="@true" Color="@GetStatusColor(status)" />
|
||||||
|
|
||||||
<MudProgressLinear Value="@status.ProgressPercent" Rounded="@true" Color="@GetStatusColor(status)" />
|
<MudText Typo="Typo.body2">
|
||||||
|
@string.Format(T("{0} of {1} files are indexed."), status.IndexedFiles, status.TotalFiles)
|
||||||
|
</MudText>
|
||||||
|
|
||||||
<MudText Typo="Typo.body2">
|
@if (status.PermanentlySkippedFiles > 0)
|
||||||
@string.Format(T("{0} of {1} files are indexed."), status.IndexedFiles, status.TotalFiles)
|
|
||||||
</MudText>
|
|
||||||
|
|
||||||
@if (status.PermanentlySkippedFiles > 0)
|
|
||||||
{
|
|
||||||
<MudText Typo="Typo.body2">
|
|
||||||
@string.Format(T("Skipped files: {0}. AI Studio reads them again once they change."), status.PermanentlySkippedFiles)
|
|
||||||
</MudText>
|
|
||||||
}
|
|
||||||
|
|
||||||
@if (status.FailedFiles > 0)
|
|
||||||
{
|
|
||||||
<MudText Typo="Typo.body2">
|
|
||||||
@string.Format(T("Failed files: {0}"), status.FailedFiles)
|
|
||||||
</MudText>
|
|
||||||
}
|
|
||||||
|
|
||||||
@if (!string.IsNullOrWhiteSpace(status.CurrentFile))
|
|
||||||
{
|
|
||||||
<MudText Typo="Typo.body2">
|
|
||||||
@string.Format(T("Current file: {0}"), status.CurrentFile)
|
|
||||||
</MudText>
|
|
||||||
}
|
|
||||||
|
|
||||||
@*
|
|
||||||
One panel per cause, the most pressing one first. A folder in which nine
|
|
||||||
hundred scanned documents were skipped otherwise buries the handful of
|
|
||||||
files somebody has to look at.
|
|
||||||
*@
|
|
||||||
@if (status.Failures.Count > 0)
|
|
||||||
{
|
|
||||||
<MudExpansionPanels MultiExpansion="@true" Class="mt-2">
|
|
||||||
@foreach (var group in this.GetFailureGroups(status))
|
|
||||||
{
|
{
|
||||||
<ExpansionPanel HeaderIcon="@group.Cause.Icon" IconColor="@group.Cause.Color" IconSize="Size.Small" HeaderText="@GetGroupHeader(group)">
|
<MudText Typo="Typo.body2">
|
||||||
<MudStack Spacing="3">
|
@string.Format(T("Skipped files: {0}. AI Studio reads them again once they change."), status.PermanentlySkippedFiles)
|
||||||
<MudStack Row="@true" Wrap="Wrap.Wrap" Spacing="2" AlignItems="AlignItems.Center">
|
</MudText>
|
||||||
<MudChip T="string" Size="Size.Small" Color="@group.Cause.Color" Variant="Variant.Outlined">
|
|
||||||
@(group.Cause.IsPermanent ? T("Skipped until the file changes") : T("Tried again during the next run"))
|
|
||||||
</MudChip>
|
|
||||||
@if (!string.IsNullOrWhiteSpace(group.EmbeddingProviderName))
|
|
||||||
{
|
|
||||||
<MudText Typo="Typo.caption">@string.Format(T("Embedding provider: {0}"), group.EmbeddingProviderName)</MudText>
|
|
||||||
}
|
|
||||||
@if (group.Cause.NeedsProviderSettings)
|
|
||||||
{
|
|
||||||
<MudButton Variant="Variant.Text" Size="Size.Small" Color="Color.Primary" StartIcon="@Icons.Material.Filled.Settings" OnClick="@this.OpenEmbeddingProviderSettings">
|
|
||||||
@T("Open the settings")
|
|
||||||
</MudButton>
|
|
||||||
}
|
|
||||||
</MudStack>
|
|
||||||
<MudTable Items="@group.Failures" Dense="@true" Hover="@true" Elevation="0" Breakpoint="Breakpoint.None" RowsPerPage="10" Class="border-dashed border rounded-lg">
|
|
||||||
<ColGroup>
|
|
||||||
<col/>
|
|
||||||
<col style="width: 11em;"/>
|
|
||||||
<col style="width: 7em;"/>
|
|
||||||
</ColGroup>
|
|
||||||
<HeaderContent>
|
|
||||||
<MudTh>@T("File")</MudTh>
|
|
||||||
<MudTh>@T("Noticed")</MudTh>
|
|
||||||
<MudTh>@T("Actions")</MudTh>
|
|
||||||
</HeaderContent>
|
|
||||||
<RowTemplate>
|
|
||||||
<MudTd Style="white-space: normal; overflow-wrap: anywhere;">
|
|
||||||
@* Captions render as spans, so each of them needs to be told to take its own line. *@
|
|
||||||
<MudText Typo="Typo.body2">@GetFileName(context.FilePath)</MudText>
|
|
||||||
<MudText Typo="Typo.caption" Class="mud-text-secondary d-block">@context.FilePath</MudText>
|
|
||||||
@if (group.Cause.ShowsMessagePerFile)
|
|
||||||
{
|
|
||||||
<MudText Typo="Typo.caption" Color="Color.Warning" Class="d-block">@context.Reason</MudText>
|
|
||||||
}
|
|
||||||
</MudTd>
|
|
||||||
<MudTd Style="white-space: nowrap;">
|
|
||||||
<MudText Typo="Typo.caption">@GetOccurrenceText(context)</MudText>
|
|
||||||
</MudTd>
|
|
||||||
<MudTd>
|
|
||||||
@if (CanShowInFileManager(context))
|
|
||||||
{
|
|
||||||
<MudTooltip Text="@T("Show this file in the file browser of your system")">
|
|
||||||
<MudIconButton Icon="@Icons.Material.Filled.FolderOpen" Color="Color.Info" Size="Size.Small" OnClick="@(async () => await this.ShowInFileManager(context))"/>
|
|
||||||
</MudTooltip>
|
|
||||||
}
|
|
||||||
</MudTd>
|
|
||||||
</RowTemplate>
|
|
||||||
<PagerContent>
|
|
||||||
<MudTablePager PageSizeOptions="@PAGE_SIZE_OPTIONS"/>
|
|
||||||
</PagerContent>
|
|
||||||
</MudTable>
|
|
||||||
</MudStack>
|
|
||||||
</ExpansionPanel>
|
|
||||||
}
|
}
|
||||||
</MudExpansionPanels>
|
|
||||||
}
|
|
||||||
|
|
||||||
@*
|
@if (status.FailedFiles > 0)
|
||||||
Shown next to the list, not instead of it: the list says which files failed,
|
{
|
||||||
while this is the one sentence about the data source as a whole. Hiding it
|
<MudText Typo="Typo.body2">
|
||||||
as soon as a single file failed is what made it invisible in practice.
|
@string.Format(T("Failed files: {0}"), status.FailedFiles)
|
||||||
*@
|
</MudText>
|
||||||
@if (!string.IsNullOrWhiteSpace(status.LastError))
|
}
|
||||||
{
|
|
||||||
<MudAlert Severity="Severity.Warning" Variant="Variant.Text">
|
@if (!string.IsNullOrWhiteSpace(status.CurrentFile))
|
||||||
@status.LastError
|
{
|
||||||
</MudAlert>
|
<MudText Typo="Typo.body2">
|
||||||
}
|
@string.Format(T("Current file: {0}"), status.CurrentFile)
|
||||||
</MudStack>
|
</MudText>
|
||||||
</MudPaper>
|
}
|
||||||
}
|
|
||||||
|
@*
|
||||||
|
One panel per cause, the most pressing one first. A folder in which nine
|
||||||
|
hundred scanned documents were skipped otherwise buries the handful of
|
||||||
|
files somebody has to look at.
|
||||||
|
*@
|
||||||
|
@if (status.Failures.Count > 0)
|
||||||
|
{
|
||||||
|
<MudExpansionPanels MultiExpansion="@true" Class="mt-2">
|
||||||
|
@foreach (var group in this.GetFailureGroups(status))
|
||||||
|
{
|
||||||
|
<ExpansionPanel HeaderIcon="@group.Cause.Icon" IconColor="@group.Cause.Color" IconSize="Size.Small" HeaderTypo="Typo.subtitle1" HeaderClass="expansion-panel-header-compact" HeaderText="@GetGroupHeader(group)">
|
||||||
|
<MudStack Spacing="3">
|
||||||
|
<MudStack Row="@true" Wrap="Wrap.Wrap" Spacing="2" AlignItems="AlignItems.Center">
|
||||||
|
<MudChip T="string" Size="Size.Small" Color="@group.Cause.Color" Variant="Variant.Outlined">
|
||||||
|
@(group.Cause.IsPermanent ? T("Skipped until the file changes") : T("Tried again during the next run"))
|
||||||
|
</MudChip>
|
||||||
|
@if (!string.IsNullOrWhiteSpace(group.EmbeddingProviderName))
|
||||||
|
{
|
||||||
|
<MudText Typo="Typo.caption">@string.Format(T("Embedding provider: {0}"), group.EmbeddingProviderName)</MudText>
|
||||||
|
}
|
||||||
|
@if (group.Cause.NeedsProviderSettings)
|
||||||
|
{
|
||||||
|
<MudButton Variant="Variant.Text" Size="Size.Small" Color="Color.Primary" StartIcon="@Icons.Material.Filled.Settings" OnClick="@this.OpenEmbeddingProviderSettings">
|
||||||
|
@T("Open the settings")
|
||||||
|
</MudButton>
|
||||||
|
}
|
||||||
|
</MudStack>
|
||||||
|
<MudTable Items="@group.Failures" Dense="@true" Hover="@true" Elevation="0" Breakpoint="Breakpoint.None" RowsPerPage="10" Class="border-dashed border rounded-lg">
|
||||||
|
<ColGroup>
|
||||||
|
<col/>
|
||||||
|
<col style="width: 11em;"/>
|
||||||
|
<col style="width: 7em;"/>
|
||||||
|
</ColGroup>
|
||||||
|
<HeaderContent>
|
||||||
|
<MudTh>@T("File")</MudTh>
|
||||||
|
<MudTh>@T("Noticed")</MudTh>
|
||||||
|
<MudTh>@T("Actions")</MudTh>
|
||||||
|
</HeaderContent>
|
||||||
|
<RowTemplate>
|
||||||
|
<MudTd Style="white-space: normal; overflow-wrap: anywhere;">
|
||||||
|
@* Captions render as spans, so each of them needs to be told to take its own line. *@
|
||||||
|
<MudText Typo="Typo.body2">@GetFileName(context.FilePath)</MudText>
|
||||||
|
<MudText Typo="Typo.caption" Class="mud-text-secondary d-block">@context.FilePath</MudText>
|
||||||
|
@if (group.Cause.ShowsMessagePerFile)
|
||||||
|
{
|
||||||
|
<MudText Typo="Typo.caption" Color="Color.Warning" Class="d-block">@context.Reason</MudText>
|
||||||
|
}
|
||||||
|
</MudTd>
|
||||||
|
<MudTd Style="white-space: nowrap;">
|
||||||
|
<MudText Typo="Typo.caption">@GetOccurrenceText(context)</MudText>
|
||||||
|
</MudTd>
|
||||||
|
<MudTd>
|
||||||
|
@if (CanShowInFileManager(context))
|
||||||
|
{
|
||||||
|
<MudTooltip Text="@T("Show this file in the file browser of your system")">
|
||||||
|
<MudIconButton Icon="@Icons.Material.Filled.FolderOpen" Color="Color.Info" Size="Size.Small" OnClick="@(async () => await this.ShowInFileManager(context))"/>
|
||||||
|
</MudTooltip>
|
||||||
|
}
|
||||||
|
</MudTd>
|
||||||
|
</RowTemplate>
|
||||||
|
<PagerContent>
|
||||||
|
<MudTablePager PageSizeOptions="@PAGE_SIZE_OPTIONS"/>
|
||||||
|
</PagerContent>
|
||||||
|
</MudTable>
|
||||||
|
</MudStack>
|
||||||
|
</ExpansionPanel>
|
||||||
|
}
|
||||||
|
</MudExpansionPanels>
|
||||||
|
}
|
||||||
|
|
||||||
|
@*
|
||||||
|
Shown next to the list, not instead of it: the list says which files failed,
|
||||||
|
while this is the one sentence about the data source as a whole. Hiding it
|
||||||
|
as soon as a single file failed is what made it invisible in practice.
|
||||||
|
*@
|
||||||
|
@if (!string.IsNullOrWhiteSpace(status.LastError))
|
||||||
|
{
|
||||||
|
<MudAlert Severity="Severity.Warning" Variant="Variant.Text">
|
||||||
|
@status.LastError
|
||||||
|
</MudAlert>
|
||||||
|
}
|
||||||
|
</MudStack>
|
||||||
|
</ChildContent>
|
||||||
|
</MudExpansionPanel>
|
||||||
|
}
|
||||||
|
</MudExpansionPanels>
|
||||||
}
|
}
|
||||||
</MudStack>
|
</MudStack>
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
using AIStudio.Components;
|
using AIStudio.Components;
|
||||||
|
using AIStudio.Dialogs.Settings;
|
||||||
using AIStudio.Provider;
|
using AIStudio.Provider;
|
||||||
using AIStudio.Settings.DataModel;
|
using AIStudio.Settings.DataModel;
|
||||||
using AIStudio.Tools.Rust;
|
using AIStudio.Tools.Rust;
|
||||||
@@ -6,6 +7,8 @@ using AIStudio.Tools.Services;
|
|||||||
|
|
||||||
using Microsoft.AspNetCore.Components;
|
using Microsoft.AspNetCore.Components;
|
||||||
|
|
||||||
|
using DialogOptions = AIStudio.Dialogs.DialogOptions;
|
||||||
|
|
||||||
namespace AIStudio.Pages;
|
namespace AIStudio.Pages;
|
||||||
|
|
||||||
public partial class Embeddings : MSGComponentBase
|
public partial class Embeddings : MSGComponentBase
|
||||||
@@ -21,11 +24,17 @@ public partial class Embeddings : MSGComponentBase
|
|||||||
[Inject]
|
[Inject]
|
||||||
private RustService RustService { get; init; } = null!;
|
private RustService RustService { get; init; } = null!;
|
||||||
|
|
||||||
|
[Inject]
|
||||||
|
private IDialogService DialogService { get; init; } = null!;
|
||||||
|
|
||||||
[Inject]
|
[Inject]
|
||||||
private ILogger<Embeddings> Logger { get; init; } = null!;
|
private ILogger<Embeddings> Logger { get; init; } = null!;
|
||||||
|
|
||||||
private IReadOnlyList<DataSourceEmbeddingStatus> Statuses { get; set; } = [];
|
private IReadOnlyList<DataSourceEmbeddingStatus> Statuses { get; set; } = [];
|
||||||
|
|
||||||
|
private string? expandedDataSourceId;
|
||||||
|
private bool userChoseExpansion;
|
||||||
|
|
||||||
private int TotalIndexedFiles => this.Statuses.Sum(status => status.IndexedFiles);
|
private int TotalIndexedFiles => this.Statuses.Sum(status => status.IndexedFiles);
|
||||||
|
|
||||||
private int TotalPendingFiles => this.Statuses.Sum(status => Math.Max(0, status.TotalFiles - status.IndexedFiles - status.FailedFiles - status.PermanentlySkippedFiles));
|
private int TotalPendingFiles => this.Statuses.Sum(status => Math.Max(0, status.TotalFiles - status.IndexedFiles - status.FailedFiles - status.PermanentlySkippedFiles));
|
||||||
@@ -70,6 +79,67 @@ public partial class Embeddings : MSGComponentBase
|
|||||||
.OrderBy(status => status.SortOrder)
|
.OrderBy(status => status.SortOrder)
|
||||||
.ThenBy(status => status.DataSourceName, StringComparer.OrdinalIgnoreCase)
|
.ThenBy(status => status.DataSourceName, StringComparer.OrdinalIgnoreCase)
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
|
this.UpdateAutoExpansion();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Opens the data source which is worth reading, as long as the user has not chosen one.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// It never closes what is open. A data source which finishes its run while somebody is reading
|
||||||
|
/// it would otherwise fold up at the very moment its result becomes interesting. From the first
|
||||||
|
/// click on, the page stops rearranging itself at all.
|
||||||
|
/// </remarks>
|
||||||
|
private void UpdateAutoExpansion()
|
||||||
|
{
|
||||||
|
if (this.userChoseExpansion)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// The list is sorted by state, so the first match is the most pressing one: a running data
|
||||||
|
// source before a queued one, and a failed one before a completed one:
|
||||||
|
var worthOpening = this.Statuses.FirstOrDefault(IsWorthOpening);
|
||||||
|
if (worthOpening is null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this.expandedDataSourceId = worthOpening.DataSourceId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <remarks>
|
||||||
|
/// Whoever opens this page does so because a run is under way or because something went wrong.
|
||||||
|
/// Meeting nothing but closed panels would be a step back from the version which showed every
|
||||||
|
/// data source at once.
|
||||||
|
/// </remarks>
|
||||||
|
private static bool IsWorthOpening(DataSourceEmbeddingStatus status) =>
|
||||||
|
status.State is DataSourceEmbeddingState.RUNNING or DataSourceEmbeddingState.QUEUED or DataSourceEmbeddingState.FAILED || status.FailedFiles > 0;
|
||||||
|
|
||||||
|
/// <remarks>
|
||||||
|
/// MudBlazor keeps track of which panel is open on its own, so this only records the decision.
|
||||||
|
/// Both events of a switch arrive, in either order — the one closing the old panel and the one
|
||||||
|
/// opening the new one — which is why the closing event only clears what it actually named.
|
||||||
|
/// </remarks>
|
||||||
|
private void DataSourcePanelExpandedChanged(DataSourceEmbeddingStatus status, bool isExpanded)
|
||||||
|
{
|
||||||
|
this.userChoseExpansion = true;
|
||||||
|
|
||||||
|
if (isExpanded)
|
||||||
|
this.expandedDataSourceId = status.DataSourceId;
|
||||||
|
else if (this.expandedDataSourceId == status.DataSourceId)
|
||||||
|
this.expandedDataSourceId = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Opens the data source settings, the same dialog the chat offers next to its data source selection.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Nothing is left to do once it closes: the dialog writes the settings itself and publishes
|
||||||
|
/// CONFIGURATION_CHANGED, which this page already listens to.
|
||||||
|
/// </remarks>
|
||||||
|
private async Task OpenDataSourceSettings()
|
||||||
|
{
|
||||||
|
var dialogParameters = new DialogParameters();
|
||||||
|
var dialogReference = await this.DialogService.ShowAsync<SettingsDialogDataSources>(null, dialogParameters, DialogOptions.FULLSCREEN);
|
||||||
|
await dialogReference.Result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Color GetStatusColor(DataSourceEmbeddingStatus status) => status.State switch
|
private static Color GetStatusColor(DataSourceEmbeddingStatus status) => status.State switch
|
||||||
|
|||||||
+7
-1
@@ -3871,7 +3871,7 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::DATASOURCESELECTION::T2113594442"] = "Sie
|
|||||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::DATASOURCESELECTION::T21181525"] = "Wählen Sie hier die Daten aus, die Sie verwenden möchten."
|
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::DATASOURCESELECTION::T21181525"] = "Wählen Sie hier die Daten aus, die Sie verwenden möchten."
|
||||||
|
|
||||||
-- Manage your data sources
|
-- Manage your data sources
|
||||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::DATASOURCESELECTION::T2149927097"] = "Verwalten Sie ihre Datenquellen"
|
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::DATASOURCESELECTION::T2149927097"] = "Ihre Datenquellen verwalten"
|
||||||
|
|
||||||
-- Select data
|
-- Select data
|
||||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::DATASOURCESELECTION::T274155039"] = "Daten auswählen"
|
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::DATASOURCESELECTION::T274155039"] = "Daten auswählen"
|
||||||
@@ -8961,6 +8961,9 @@ UI_TEXT_CONTENT["AISTUDIO::PAGES::EMBEDDINGS::T1946414905"] = "Beim nächsten Du
|
|||||||
-- Skipped files: {0}
|
-- Skipped files: {0}
|
||||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::EMBEDDINGS::T196379388"] = "Übersprungene Dateien: {0}"
|
UI_TEXT_CONTENT["AISTUDIO::PAGES::EMBEDDINGS::T196379388"] = "Übersprungene Dateien: {0}"
|
||||||
|
|
||||||
|
-- Manage your data sources
|
||||||
|
UI_TEXT_CONTENT["AISTUDIO::PAGES::EMBEDDINGS::T2149927097"] = "Ihre Datenquellen verwalten"
|
||||||
|
|
||||||
-- Noticed
|
-- Noticed
|
||||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::EMBEDDINGS::T2367007983"] = "Zur Kenntnis genommen"
|
UI_TEXT_CONTENT["AISTUDIO::PAGES::EMBEDDINGS::T2367007983"] = "Zur Kenntnis genommen"
|
||||||
|
|
||||||
@@ -8982,6 +8985,9 @@ UI_TEXT_CONTENT["AISTUDIO::PAGES::EMBEDDINGS::T2547971789"] = "Einbettungen im H
|
|||||||
-- Refresh this data source
|
-- Refresh this data source
|
||||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::EMBEDDINGS::T2901874229"] = "Diese Datenquelle aktualisieren"
|
UI_TEXT_CONTENT["AISTUDIO::PAGES::EMBEDDINGS::T2901874229"] = "Diese Datenquelle aktualisieren"
|
||||||
|
|
||||||
|
-- Data source: {0}
|
||||||
|
UI_TEXT_CONTENT["AISTUDIO::PAGES::EMBEDDINGS::T2945218010"] = "Datenquelle: {0}"
|
||||||
|
|
||||||
-- Embedding provider: {0}
|
-- Embedding provider: {0}
|
||||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::EMBEDDINGS::T300213237"] = "Einbettungsanbieter: {0}"
|
UI_TEXT_CONTENT["AISTUDIO::PAGES::EMBEDDINGS::T300213237"] = "Einbettungsanbieter: {0}"
|
||||||
|
|
||||||
|
|||||||
+6
@@ -8961,6 +8961,9 @@ UI_TEXT_CONTENT["AISTUDIO::PAGES::EMBEDDINGS::T1946414905"] = "Tried again durin
|
|||||||
-- Skipped files: {0}
|
-- Skipped files: {0}
|
||||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::EMBEDDINGS::T196379388"] = "Skipped files: {0}"
|
UI_TEXT_CONTENT["AISTUDIO::PAGES::EMBEDDINGS::T196379388"] = "Skipped files: {0}"
|
||||||
|
|
||||||
|
-- Manage your data sources
|
||||||
|
UI_TEXT_CONTENT["AISTUDIO::PAGES::EMBEDDINGS::T2149927097"] = "Manage your data sources"
|
||||||
|
|
||||||
-- Noticed
|
-- Noticed
|
||||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::EMBEDDINGS::T2367007983"] = "Noticed"
|
UI_TEXT_CONTENT["AISTUDIO::PAGES::EMBEDDINGS::T2367007983"] = "Noticed"
|
||||||
|
|
||||||
@@ -8982,6 +8985,9 @@ UI_TEXT_CONTENT["AISTUDIO::PAGES::EMBEDDINGS::T2547971789"] = "Background embedd
|
|||||||
-- Refresh this data source
|
-- Refresh this data source
|
||||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::EMBEDDINGS::T2901874229"] = "Refresh this data source"
|
UI_TEXT_CONTENT["AISTUDIO::PAGES::EMBEDDINGS::T2901874229"] = "Refresh this data source"
|
||||||
|
|
||||||
|
-- Data source: {0}
|
||||||
|
UI_TEXT_CONTENT["AISTUDIO::PAGES::EMBEDDINGS::T2945218010"] = "Data source: {0}"
|
||||||
|
|
||||||
-- Embedding provider: {0}
|
-- Embedding provider: {0}
|
||||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::EMBEDDINGS::T300213237"] = "Embedding provider: {0}"
|
UI_TEXT_CONTENT["AISTUDIO::PAGES::EMBEDDINGS::T300213237"] = "Embedding provider: {0}"
|
||||||
|
|
||||||
|
|||||||
@@ -444,3 +444,16 @@ tr:has(> .provider-group-header) .mud-icon-button {
|
|||||||
.provider-group-header {
|
.provider-group-header {
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Headers of an expansion panel which sits inside another one, such as the failure groups of a
|
||||||
|
* data source on the embeddings page. Two headers of the same size give no clue about which one
|
||||||
|
* contains the other. MudBlazor's own Dense is no help here: it takes the padding off the content,
|
||||||
|
* not off the header, whose height is fixed in the framework. Hence this rule — and it has to name
|
||||||
|
* the MudBlazor classes to outweigh their specificity.
|
||||||
|
*/
|
||||||
|
.mud-expand-panel .mud-expand-panel-header.expansion-panel-header-compact {
|
||||||
|
min-height: 2rem;
|
||||||
|
padding-top: 0.25rem;
|
||||||
|
padding-bottom: 0.25rem;
|
||||||
|
}
|
||||||
Reference in new issue
Block a user