mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2026-06-07 04:16:29 +00:00
Added the option to search for chats in all workspaces (#795)
Some checks failed
Build and Release / Determine run mode (push) Has been cancelled
Build and Release / Prepare & create release (push) Has been cancelled
Build and Release / Publish release (push) Has been cancelled
Build and Release / Read metadata (push) Has been cancelled
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-apple-darwin, osx-arm64, macos-latest, aarch64-apple-darwin, dmg,app,updater, dmg) (push) Has been cancelled
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-pc-windows-msvc.exe, win-arm64, windows-latest, aarch64-pc-windows-msvc, nsis,updater, nsis) (push) Has been cancelled
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-unknown-linux-gnu, linux-arm64, ubuntu-22.04-arm, aarch64-unknown-linux-gnu, appimage,updater, appimage) (push) Has been cancelled
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-apple-darwin, osx-x64, macos-latest, x86_64-apple-darwin, dmg,app,updater, dmg) (push) Has been cancelled
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-pc-windows-msvc.exe, win-x64, windows-latest, x86_64-pc-windows-msvc, nsis,updater, nsis) (push) Has been cancelled
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-unknown-linux-gnu, linux-x64, ubuntu-22.04, x86_64-unknown-linux-gnu, appimage,updater, appimage) (push) Has been cancelled
Some checks failed
Build and Release / Determine run mode (push) Has been cancelled
Build and Release / Prepare & create release (push) Has been cancelled
Build and Release / Publish release (push) Has been cancelled
Build and Release / Read metadata (push) Has been cancelled
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-apple-darwin, osx-arm64, macos-latest, aarch64-apple-darwin, dmg,app,updater, dmg) (push) Has been cancelled
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-pc-windows-msvc.exe, win-arm64, windows-latest, aarch64-pc-windows-msvc, nsis,updater, nsis) (push) Has been cancelled
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-unknown-linux-gnu, linux-arm64, ubuntu-22.04-arm, aarch64-unknown-linux-gnu, appimage,updater, appimage) (push) Has been cancelled
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-apple-darwin, osx-x64, macos-latest, x86_64-apple-darwin, dmg,app,updater, dmg) (push) Has been cancelled
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-pc-windows-msvc.exe, win-x64, windows-latest, x86_64-pc-windows-msvc, nsis,updater, nsis) (push) Has been cancelled
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-unknown-linux-gnu, linux-x64, ubuntu-22.04, x86_64-unknown-linux-gnu, appimage,updater, appimage) (push) Has been cancelled
This commit is contained in:
parent
9fc7eaff99
commit
0b41f5eb96
@ -3196,15 +3196,27 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T1469573738"] = "Delete"
|
||||
-- Rename Workspace
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T1474303418"] = "Rename Workspace"
|
||||
|
||||
-- Clear search
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T1511254342"] = "Clear search"
|
||||
|
||||
-- Rename Chat
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T156144855"] = "Rename Chat"
|
||||
|
||||
-- Add workspace
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T1586005241"] = "Add workspace"
|
||||
|
||||
-- Search chats
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T1615077202"] = "Search chats"
|
||||
|
||||
-- Start a new chat in workspace '{0}'
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T1840064668"] = "Start a new chat in workspace '{0}'"
|
||||
|
||||
-- Add chat
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T1874060138"] = "Add chat"
|
||||
|
||||
-- No chats found
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T1886517101"] = "No chats found"
|
||||
|
||||
-- Create Chat
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T1939006681"] = "Create Chat"
|
||||
|
||||
@ -3250,6 +3262,9 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T3355849203"] = "Rename"
|
||||
-- Please enter a new or edit the name for your chat '{0}':
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T3419791373"] = "Please enter a new or edit the name for your chat '{0}':"
|
||||
|
||||
-- Search chat contents
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T3436662033"] = "Search chat contents"
|
||||
|
||||
-- Load Chat
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T3555709365"] = "Load Chat"
|
||||
|
||||
@ -5956,6 +5971,9 @@ UI_TEXT_CONTENT["AISTUDIO::PAGES::ASSISTANTS::T878695986"] = "Learn about one co
|
||||
-- Localization
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::ASSISTANTS::T897888480"] = "Localization"
|
||||
|
||||
-- Hide search
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::CHAT::T1281128983"] = "Hide search"
|
||||
|
||||
-- Reload your workspaces
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::CHAT::T194629703"] = "Reload your workspaces"
|
||||
|
||||
@ -5968,6 +5986,9 @@ UI_TEXT_CONTENT["AISTUDIO::PAGES::CHAT::T2813205227"] = "Open Chat Options"
|
||||
-- Disappearing Chat
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::CHAT::T3046519404"] = "Disappearing Chat"
|
||||
|
||||
-- Search your workspaces
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::CHAT::T3059773282"] = "Search your workspaces"
|
||||
|
||||
-- Configure your workspaces
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::CHAT::T3586092784"] = "Configure your workspaces"
|
||||
|
||||
|
||||
@ -11,6 +11,36 @@
|
||||
}
|
||||
else
|
||||
{
|
||||
@if (this.SearchVisible)
|
||||
{
|
||||
<MudStack Class="mx-3 mt-2 mb-1" Spacing="1" Style="position: sticky; top: 0; z-index: 2; background-color: var(--mud-palette-background);">
|
||||
<MudStack Row="@true" AlignItems="AlignItems.Center" Wrap="Wrap.NoWrap" Spacing="1">
|
||||
<MudTextField T="string"
|
||||
Text="@this.searchText"
|
||||
TextChanged="@this.OnSearchTextChanged"
|
||||
Placeholder="@T("Search chats")"
|
||||
Variant="Variant.Outlined"
|
||||
Margin="Margin.Dense"
|
||||
Immediate="@true"
|
||||
Adornment="Adornment.Start"
|
||||
AdornmentIcon="@Icons.Material.Filled.Search"/>
|
||||
<MudTooltip Text="@T("Clear search")" Placement="@WORKSPACE_ITEM_TOOLTIP_PLACEMENT">
|
||||
<MudIconButton Icon="@Icons.Material.Filled.Clear" Size="Size.Medium" Color="Color.Inherit" Disabled="@(string.IsNullOrWhiteSpace(this.searchText))" OnClick="@this.ClearSearchAsync"/>
|
||||
</MudTooltip>
|
||||
</MudStack>
|
||||
|
||||
<MudStack Row="@true" AlignItems="AlignItems.Center" Wrap="Wrap.NoWrap" Spacing="1">
|
||||
<MudSwitch T="bool" Value="@this.includeThreadContents" ValueChanged="@this.IncludeThreadContentsChanged" Color="Color.Primary">
|
||||
@T("Search chat contents")
|
||||
</MudSwitch>
|
||||
@if (this.isSearchRunning)
|
||||
{
|
||||
<MudProgressCircular Size="Size.Small" Indeterminate="@true"/>
|
||||
}
|
||||
</MudStack>
|
||||
</MudStack>
|
||||
}
|
||||
|
||||
<MudTreeView T="ITreeItem" Items="@this.treeItems" SelectionMode="SelectionMode.SingleSelection" Hover="@true" ExpandOnClick="@true" Class="ma-3">
|
||||
<ItemTemplate Context="item">
|
||||
@switch (item.Value)
|
||||
@ -71,19 +101,22 @@ else
|
||||
<MudText Style="justify-self: start;">
|
||||
@treeItem.Text
|
||||
</MudText>
|
||||
<div style="justify-self: end;">
|
||||
<MudTooltip Text="@T("Add chat")" Placement="@WORKSPACE_ITEM_TOOLTIP_PLACEMENT">
|
||||
<MudIconButton Icon="@Icons.Material.Filled.AddComment" Size="Size.Medium" Color="Color.Inherit" OnClick="@(() => this.AddChatAsync(treeItem.Path))"/>
|
||||
</MudTooltip>
|
||||
@if (!this.HasSearchQuery)
|
||||
{
|
||||
<div style="justify-self: end;">
|
||||
<MudTooltip Text="@this.GetAddChatToWorkspaceTooltip(treeItem.Text)" Placement="@WORKSPACE_ITEM_TOOLTIP_PLACEMENT">
|
||||
<MudIconButton Icon="@Icons.Material.Filled.AddComment" Size="Size.Medium" Color="Color.Inherit" OnClick="@(() => this.AddChatAsync(treeItem.Path))"/>
|
||||
</MudTooltip>
|
||||
|
||||
<MudTooltip Text="@T("Rename")" Placement="@WORKSPACE_ITEM_TOOLTIP_PLACEMENT">
|
||||
<MudIconButton Icon="@Icons.Material.Filled.Edit" Size="Size.Medium" Color="Color.Inherit" OnClick="@(() => this.RenameWorkspaceAsync(treeItem.Path))"/>
|
||||
</MudTooltip>
|
||||
<MudTooltip Text="@T("Rename")" Placement="@WORKSPACE_ITEM_TOOLTIP_PLACEMENT">
|
||||
<MudIconButton Icon="@Icons.Material.Filled.Edit" Size="Size.Medium" Color="Color.Inherit" OnClick="@(() => this.RenameWorkspaceAsync(treeItem.Path))"/>
|
||||
</MudTooltip>
|
||||
|
||||
<MudTooltip Text="@T("Delete")" Placement="@WORKSPACE_ITEM_TOOLTIP_PLACEMENT">
|
||||
<MudIconButton Icon="@Icons.Material.Filled.Delete" Size="Size.Medium" Color="Color.Error" OnClick="@(() => this.DeleteWorkspaceAsync(treeItem.Path))"/>
|
||||
</MudTooltip>
|
||||
</div>
|
||||
<MudTooltip Text="@T("Delete")" Placement="@WORKSPACE_ITEM_TOOLTIP_PLACEMENT">
|
||||
<MudIconButton Icon="@Icons.Material.Filled.Delete" Size="Size.Medium" Color="Color.Error" OnClick="@(() => this.DeleteWorkspaceAsync(treeItem.Path))"/>
|
||||
</MudTooltip>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</BodyContent>
|
||||
</MudTreeViewItem>
|
||||
|
||||
@ -32,14 +32,25 @@ public partial class Workspaces : MSGComponentBase
|
||||
[Parameter]
|
||||
public bool ExpandRootNodes { get; set; } = true;
|
||||
|
||||
[Parameter]
|
||||
public bool SearchVisible { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public EventCallback<bool> SearchVisibleChanged { get; set; }
|
||||
|
||||
private const Placement WORKSPACE_ITEM_TOOLTIP_PLACEMENT = Placement.Bottom;
|
||||
private readonly SemaphoreSlim treeLoadingSemaphore = new(1, 1);
|
||||
private readonly List<TreeItemData<ITreeItem>> treeItems = [];
|
||||
private readonly HashSet<Guid> loadingWorkspaceChatLists = [];
|
||||
|
||||
private CancellationTokenSource? prefetchCancellationTokenSource;
|
||||
private CancellationTokenSource? searchCancellationTokenSource;
|
||||
private bool isInitialLoading = true;
|
||||
private bool isDisposed;
|
||||
private bool includeThreadContents;
|
||||
private bool isSearchRunning;
|
||||
private string searchText = string.Empty;
|
||||
private long searchRevision;
|
||||
|
||||
#region Overrides of ComponentBase
|
||||
|
||||
@ -54,6 +65,7 @@ public partial class Workspaces : MSGComponentBase
|
||||
|
||||
private async Task LoadTreeItemsAsync(bool startPrefetch = true, bool forceReload = false)
|
||||
{
|
||||
var shouldRunSearch = false;
|
||||
await this.treeLoadingSemaphore.WaitAsync();
|
||||
try
|
||||
{
|
||||
@ -64,7 +76,11 @@ public partial class Workspaces : MSGComponentBase
|
||||
await WorkspaceBehaviour.ForceReloadWorkspaceTreeAsync();
|
||||
|
||||
var snapshot = await WorkspaceBehaviour.GetOrLoadWorkspaceTreeShellAsync();
|
||||
this.BuildTreeItems(snapshot);
|
||||
if (this.HasSearchQuery)
|
||||
shouldRunSearch = true;
|
||||
else
|
||||
this.BuildTreeItems(snapshot);
|
||||
|
||||
this.isInitialLoading = false;
|
||||
}
|
||||
finally
|
||||
@ -72,12 +88,19 @@ public partial class Workspaces : MSGComponentBase
|
||||
this.treeLoadingSemaphore.Release();
|
||||
}
|
||||
|
||||
await this.SafeStateHasChanged();
|
||||
if (shouldRunSearch)
|
||||
await this.SearchWorkspaceItemsAsync();
|
||||
else
|
||||
await this.SafeStateHasChanged();
|
||||
|
||||
if (startPrefetch)
|
||||
await this.StartPrefetchAsync();
|
||||
}
|
||||
|
||||
private bool HasSearchQuery => this.SearchVisible && !string.IsNullOrWhiteSpace(this.searchText);
|
||||
|
||||
private string GetAddChatToWorkspaceTooltip(string workspaceName) => string.Format(T("Start a new chat in workspace '{0}'"), workspaceName);
|
||||
|
||||
private void BuildTreeItems(WorkspaceTreeCacheSnapshot snapshot)
|
||||
{
|
||||
this.treeItems.Clear();
|
||||
@ -219,6 +242,109 @@ public partial class Workspaces : MSGComponentBase
|
||||
};
|
||||
}
|
||||
|
||||
private void BuildSearchTreeItems(WorkspaceSearchSnapshot snapshot)
|
||||
{
|
||||
this.treeItems.Clear();
|
||||
|
||||
if (snapshot.Workspaces.Count == 0 && snapshot.TemporaryChats.Count == 0)
|
||||
{
|
||||
this.treeItems.Add(new TreeItemData<ITreeItem>
|
||||
{
|
||||
Expandable = false,
|
||||
Value = new TreeItemData
|
||||
{
|
||||
Depth = 0,
|
||||
Branch = WorkspaceBranch.NONE,
|
||||
Text = T("No chats found"),
|
||||
Icon = Icons.Material.Filled.Search,
|
||||
Expandable = false,
|
||||
Path = "search_empty",
|
||||
},
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (snapshot.Workspaces.Count > 0)
|
||||
{
|
||||
var workspaceChildren = new List<TreeItemData<ITreeItem>>();
|
||||
foreach (var workspace in snapshot.Workspaces)
|
||||
workspaceChildren.Add(this.CreateSearchWorkspaceTreeItem(workspace));
|
||||
|
||||
this.treeItems.Add(new TreeItemData<ITreeItem>
|
||||
{
|
||||
Expanded = true,
|
||||
Expandable = true,
|
||||
Value = new TreeItemData
|
||||
{
|
||||
Depth = 0,
|
||||
Branch = WorkspaceBranch.WORKSPACES,
|
||||
Text = T("Workspaces"),
|
||||
Icon = Icons.Material.Filled.Folder,
|
||||
Expandable = true,
|
||||
Path = "search_workspaces",
|
||||
Children = workspaceChildren,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (snapshot.Workspaces.Count > 0 && snapshot.TemporaryChats.Count > 0)
|
||||
{
|
||||
this.treeItems.Add(new TreeItemData<ITreeItem>
|
||||
{
|
||||
Expandable = false,
|
||||
Value = new TreeDivider(),
|
||||
});
|
||||
}
|
||||
|
||||
if (snapshot.TemporaryChats.Count > 0)
|
||||
{
|
||||
var temporaryChatsChildren = new List<TreeItemData<ITreeItem>>();
|
||||
foreach (var temporaryChat in snapshot.TemporaryChats)
|
||||
temporaryChatsChildren.Add(this.CreateChatTreeItem(temporaryChat.Chat, WorkspaceBranch.TEMPORARY_CHATS, depth: 1, icon: Icons.Material.Filled.Timer));
|
||||
|
||||
this.treeItems.Add(new TreeItemData<ITreeItem>
|
||||
{
|
||||
Expanded = true,
|
||||
Expandable = true,
|
||||
Value = new TreeItemData
|
||||
{
|
||||
Depth = 0,
|
||||
Branch = WorkspaceBranch.TEMPORARY_CHATS,
|
||||
Text = T("Disappearing Chats"),
|
||||
Icon = Icons.Material.Filled.Timer,
|
||||
Expandable = true,
|
||||
Path = "search_temp",
|
||||
Children = temporaryChatsChildren,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private TreeItemData<ITreeItem> CreateSearchWorkspaceTreeItem(WorkspaceSearchWorkspace workspace)
|
||||
{
|
||||
var children = new List<TreeItemData<ITreeItem>>();
|
||||
foreach (var chat in workspace.Chats)
|
||||
children.Add(this.CreateChatTreeItem(chat.Chat, WorkspaceBranch.WORKSPACES, depth: 2, icon: Icons.Material.Filled.Chat));
|
||||
|
||||
return new TreeItemData<ITreeItem>
|
||||
{
|
||||
Expanded = true,
|
||||
Expandable = true,
|
||||
Value = new TreeItemData
|
||||
{
|
||||
Type = TreeItemType.WORKSPACE,
|
||||
Depth = 1,
|
||||
Branch = WorkspaceBranch.WORKSPACES,
|
||||
Text = workspace.Name,
|
||||
Icon = Icons.Material.Filled.Description,
|
||||
Expandable = true,
|
||||
Path = workspace.WorkspacePath,
|
||||
Children = children,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
private string GetTreeItemIcon(TreeItemData treeItem)
|
||||
{
|
||||
if (treeItem.Type is not TreeItemType.CHAT)
|
||||
@ -289,6 +415,106 @@ public partial class Workspaces : MSGComponentBase
|
||||
}
|
||||
}
|
||||
|
||||
public async Task ToggleSearchAsync()
|
||||
{
|
||||
var searchVisible = !this.SearchVisible;
|
||||
this.SearchVisible = searchVisible;
|
||||
await this.SearchVisibleChanged.InvokeAsync(searchVisible);
|
||||
|
||||
if (this.SearchVisible)
|
||||
{
|
||||
await this.SafeStateHasChanged();
|
||||
return;
|
||||
}
|
||||
|
||||
await this.CancelSearchAsync();
|
||||
this.searchText = string.Empty;
|
||||
this.isSearchRunning = false;
|
||||
await this.LoadTreeItemsAsync(startPrefetch: false);
|
||||
}
|
||||
|
||||
private async Task CancelSearchAsync()
|
||||
{
|
||||
this.searchRevision++;
|
||||
if (this.searchCancellationTokenSource is not null)
|
||||
{
|
||||
await this.searchCancellationTokenSource.CancelAsync();
|
||||
this.searchCancellationTokenSource.Dispose();
|
||||
this.searchCancellationTokenSource = null;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task OnSearchTextChanged(string value)
|
||||
{
|
||||
this.searchText = value;
|
||||
if (string.IsNullOrWhiteSpace(this.searchText))
|
||||
{
|
||||
await this.CancelSearchAsync();
|
||||
this.isSearchRunning = false;
|
||||
await this.LoadTreeItemsAsync(startPrefetch: false);
|
||||
return;
|
||||
}
|
||||
|
||||
await this.SearchWorkspaceItemsAsync();
|
||||
}
|
||||
|
||||
private async Task IncludeThreadContentsChanged(bool value)
|
||||
{
|
||||
this.includeThreadContents = value;
|
||||
if (this.HasSearchQuery)
|
||||
await this.SearchWorkspaceItemsAsync();
|
||||
}
|
||||
|
||||
private async Task ClearSearchAsync()
|
||||
{
|
||||
this.searchText = string.Empty;
|
||||
await this.CancelSearchAsync();
|
||||
this.isSearchRunning = false;
|
||||
await this.LoadTreeItemsAsync(startPrefetch: false);
|
||||
}
|
||||
|
||||
private async Task SearchWorkspaceItemsAsync()
|
||||
{
|
||||
await this.CancelSearchAsync();
|
||||
|
||||
var text = this.searchText;
|
||||
if (string.IsNullOrWhiteSpace(text))
|
||||
return;
|
||||
|
||||
this.searchCancellationTokenSource = new CancellationTokenSource();
|
||||
var token = this.searchCancellationTokenSource.Token;
|
||||
var revision = ++this.searchRevision;
|
||||
|
||||
this.isSearchRunning = true;
|
||||
await this.SafeStateHasChanged();
|
||||
|
||||
try
|
||||
{
|
||||
var snapshot = await WorkspaceBehaviour.SearchWorkspaceChatsAsync(text, this.includeThreadContents, token);
|
||||
if (this.isDisposed || token.IsCancellationRequested || revision != this.searchRevision)
|
||||
return;
|
||||
|
||||
this.BuildSearchTreeItems(snapshot);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
// Expected when the user keeps typing or hides the search row.
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
this.Logger.LogWarning(ex, "Failed while searching workspace chats.");
|
||||
this.BuildSearchTreeItems(new([], []));
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (revision == this.searchRevision)
|
||||
{
|
||||
this.isSearchRunning = false;
|
||||
await this.SafeStateHasChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task OnWorkspaceClicked(TreeItemData treeItem)
|
||||
{
|
||||
if (treeItem.Type is not TreeItemType.WORKSPACE)
|
||||
@ -656,6 +882,9 @@ public partial class Workspaces : MSGComponentBase
|
||||
this.prefetchCancellationTokenSource?.Cancel();
|
||||
this.prefetchCancellationTokenSource?.Dispose();
|
||||
this.prefetchCancellationTokenSource = null;
|
||||
this.searchCancellationTokenSource?.Cancel();
|
||||
this.searchCancellationTokenSource?.Dispose();
|
||||
this.searchCancellationTokenSource = null;
|
||||
|
||||
base.DisposeResources();
|
||||
}
|
||||
|
||||
@ -51,13 +51,16 @@
|
||||
<MudTooltip Text="@T("Reload your workspaces")" Placement="@TOOLBAR_TOOLTIP_PLACEMENT">
|
||||
<MudIconButton Icon="@Icons.Material.Filled.Refresh" Size="Size.Medium" OnClick="@this.RefreshWorkspaces"/>
|
||||
</MudTooltip>
|
||||
<MudTooltip Text="@this.WorkspaceSearchTooltip" Placement="@TOOLBAR_TOOLTIP_PLACEMENT">
|
||||
<MudIconButton Icon="@this.WorkspaceSearchIcon" Size="Size.Medium" OnClick="@this.ToggleWorkspaceSearch"/>
|
||||
</MudTooltip>
|
||||
<MudTooltip Text="@T("Hide your workspaces")" Placement="@TOOLBAR_TOOLTIP_PLACEMENT">
|
||||
<MudIconButton Size="Size.Medium" Icon="@this.WorkspaceSidebarToggleIcon" Class="me-1" OnClick="@(() => this.ToggleWorkspaceSidebar())"/>
|
||||
</MudTooltip>
|
||||
</MudStack>
|
||||
</HeaderContent>
|
||||
<ChildContent>
|
||||
<Workspaces @ref="this.workspaces" @bind-CurrentChatThread="@this.chatThread"/>
|
||||
<Workspaces @ref="this.workspaces" @bind-CurrentChatThread="@this.chatThread" @bind-SearchVisible="@this.workspaceSearchVisible"/>
|
||||
</ChildContent>
|
||||
</InnerScrolling>
|
||||
}
|
||||
@ -77,10 +80,13 @@
|
||||
<MudTooltip Text="@T("Reload your workspaces")" Placement="@TOOLBAR_TOOLTIP_PLACEMENT">
|
||||
<MudIconButton Icon="@Icons.Material.Filled.Refresh" Size="Size.Medium" OnClick="@this.RefreshWorkspaces"/>
|
||||
</MudTooltip>
|
||||
<MudTooltip Text="@this.WorkspaceSearchTooltip" Placement="@TOOLBAR_TOOLTIP_PLACEMENT">
|
||||
<MudIconButton Icon="@this.WorkspaceSearchIcon" Size="Size.Medium" OnClick="@this.ToggleWorkspaceSearch"/>
|
||||
</MudTooltip>
|
||||
</MudStack>
|
||||
</HeaderContent>
|
||||
<ChildContent>
|
||||
<Workspaces @ref="this.workspaces" @bind-CurrentChatThread="@this.chatThread"/>
|
||||
<Workspaces @ref="this.workspaces" @bind-CurrentChatThread="@this.chatThread" @bind-SearchVisible="@this.workspaceSearchVisible"/>
|
||||
</ChildContent>
|
||||
</InnerScrolling>
|
||||
}
|
||||
@ -149,11 +155,14 @@
|
||||
<MudTooltip Text="@T("Reload your workspaces")" Placement="@TOOLBAR_TOOLTIP_PLACEMENT">
|
||||
<MudIconButton Icon="@Icons.Material.Filled.Refresh" Size="Size.Medium" OnClick="@this.RefreshWorkspaces"/>
|
||||
</MudTooltip>
|
||||
<MudTooltip Text="@this.WorkspaceSearchTooltip" Placement="@TOOLBAR_TOOLTIP_PLACEMENT">
|
||||
<MudIconButton Icon="@this.WorkspaceSearchIcon" Size="Size.Medium" OnClick="@this.ToggleWorkspaceSearch"/>
|
||||
</MudTooltip>
|
||||
<MudIconButton Icon="@Icons.Material.Filled.Close" Color="Color.Error" Size="Size.Medium" OnClick="@(() => this.ToggleWorkspacesOverlay())"/>
|
||||
</MudStack>
|
||||
</MudDrawerHeader>
|
||||
<MudDrawerContainer Class="ml-6">
|
||||
<Workspaces @ref="this.workspaces" @bind-CurrentChatThread="@this.chatThread"/>
|
||||
<Workspaces @ref="this.workspaces" @bind-CurrentChatThread="@this.chatThread" @bind-SearchVisible="@this.workspaceSearchVisible"/>
|
||||
</MudDrawerContainer>
|
||||
</MudDrawer>
|
||||
}
|
||||
|
||||
@ -23,6 +23,7 @@ public partial class Chat : MSGComponentBase
|
||||
private ChatThread? chatThread;
|
||||
private AIStudio.Settings.Provider providerSettings = AIStudio.Settings.Provider.NONE;
|
||||
private bool workspaceOverlayVisible;
|
||||
private bool workspaceSearchVisible;
|
||||
private string currentWorkspaceName = string.Empty;
|
||||
private Workspaces? workspaces;
|
||||
private double splitterPosition = 30;
|
||||
@ -51,6 +52,10 @@ public partial class Chat : MSGComponentBase
|
||||
|
||||
private string WorkspaceSidebarToggleIcon => this.SettingsManager.ConfigurationData.Workspace.IsSidebarVisible ? Icons.Material.Filled.ArrowCircleLeft : Icons.Material.Filled.ArrowCircleRight;
|
||||
|
||||
private string WorkspaceSearchIcon => this.workspaceSearchVisible ? Icons.Material.Filled.SearchOff : Icons.Material.Filled.Search;
|
||||
|
||||
private string WorkspaceSearchTooltip => this.workspaceSearchVisible ? T("Hide search") : T("Search your workspaces");
|
||||
|
||||
private bool AreWorkspacesVisible => this.SettingsManager.ConfigurationData.Workspace.StorageBehavior is not WorkspaceStorageBehavior.DISABLE_WORKSPACES
|
||||
&& ((this.SettingsManager.ConfigurationData.Workspace.DisplayBehavior is WorkspaceDisplayBehavior.TOGGLE_SIDEBAR && this.SettingsManager.ConfigurationData.Workspace.IsSidebarVisible)
|
||||
|| this.SettingsManager.ConfigurationData.Workspace.DisplayBehavior is WorkspaceDisplayBehavior.SIDEBAR_ALWAYS_VISIBLE);
|
||||
@ -107,6 +112,14 @@ public partial class Chat : MSGComponentBase
|
||||
await this.workspaces.ForceRefreshFromDiskAsync();
|
||||
}
|
||||
|
||||
private async Task ToggleWorkspaceSearch()
|
||||
{
|
||||
if (this.workspaces is null)
|
||||
return;
|
||||
|
||||
await this.workspaces.ToggleSearchAsync();
|
||||
}
|
||||
|
||||
#region Overrides of MSGComponentBase
|
||||
|
||||
protected override void DisposeResources()
|
||||
|
||||
@ -3198,15 +3198,27 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T1469573738"] = "Löschen"
|
||||
-- Rename Workspace
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T1474303418"] = "Arbeitsbereich umbenennen"
|
||||
|
||||
-- Clear search
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T1511254342"] = "Suche zurücksetzen"
|
||||
|
||||
-- Rename Chat
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T156144855"] = "Chat umbenennen"
|
||||
|
||||
-- Add workspace
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T1586005241"] = "Arbeitsbereich hinzufügen"
|
||||
|
||||
-- Search chats
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T1615077202"] = "Chats durchsuchen"
|
||||
|
||||
-- Start a new chat in workspace '{0}'
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T1840064668"] = "Neuen Chat im Arbeitsbereich „{0}“ starten"
|
||||
|
||||
-- Add chat
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T1874060138"] = "Chat hinzufügen"
|
||||
|
||||
-- No chats found
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T1886517101"] = "Keine Chats gefunden"
|
||||
|
||||
-- Create Chat
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T1939006681"] = "Chat erstellen"
|
||||
|
||||
@ -3252,6 +3264,9 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T3355849203"] = "Umbenennen"
|
||||
-- Please enter a new or edit the name for your chat '{0}':
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T3419791373"] = "Bitte geben Sie einen neuen Namen für ihren Chat „{0}“ ein oder bearbeiten Sie ihn:"
|
||||
|
||||
-- Search chat contents
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T3436662033"] = "Chat-Inhalte durchsuchen"
|
||||
|
||||
-- Load Chat
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T3555709365"] = "Chat laden"
|
||||
|
||||
@ -5958,6 +5973,9 @@ UI_TEXT_CONTENT["AISTUDIO::PAGES::ASSISTANTS::T878695986"] = "Lerne jeden Tag ei
|
||||
-- Localization
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::ASSISTANTS::T897888480"] = "Lokalisierung"
|
||||
|
||||
-- Hide search
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::CHAT::T1281128983"] = "Suche ausblenden"
|
||||
|
||||
-- Reload your workspaces
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::CHAT::T194629703"] = "Arbeitsbereiche neu laden"
|
||||
|
||||
@ -5970,6 +5988,9 @@ UI_TEXT_CONTENT["AISTUDIO::PAGES::CHAT::T2813205227"] = "Chat-Optionen öffnen"
|
||||
-- Disappearing Chat
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::CHAT::T3046519404"] = "Selbstlöschender Chat"
|
||||
|
||||
-- Search your workspaces
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::CHAT::T3059773282"] = "Arbeitsbereiche durchsuchen"
|
||||
|
||||
-- Configure your workspaces
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::CHAT::T3586092784"] = "Konfigurieren Sie ihre Arbeitsbereiche"
|
||||
|
||||
@ -6270,12 +6291,12 @@ UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T2989678330"] = "Kopiert den Fing
|
||||
-- Changelog
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T3017574265"] = "Änderungsprotokoll"
|
||||
|
||||
-- Vector store
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T3046399223"] = "Vektordatenbank"
|
||||
|
||||
-- External HTTPS custom root certificates are configured but not active.
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T3021325354"] = "Externe benutzerdefinierte Stammzertifikate sind konfiguriert, aber nicht aktiv."
|
||||
|
||||
-- Vector store
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T3046399223"] = "Vektordatenbank"
|
||||
|
||||
-- Enterprise configuration ID:
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T3092349641"] = "Unternehmenskonfigurations-ID:"
|
||||
|
||||
|
||||
@ -3198,15 +3198,27 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T1469573738"] = "Delete"
|
||||
-- Rename Workspace
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T1474303418"] = "Rename Workspace"
|
||||
|
||||
-- Clear search
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T1511254342"] = "Clear search"
|
||||
|
||||
-- Rename Chat
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T156144855"] = "Rename Chat"
|
||||
|
||||
-- Add workspace
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T1586005241"] = "Add workspace"
|
||||
|
||||
-- Search chats
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T1615077202"] = "Search chats"
|
||||
|
||||
-- Start a new chat in workspace '{0}'
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T1840064668"] = "Start a new chat in workspace '{0}'"
|
||||
|
||||
-- Add chat
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T1874060138"] = "Add chat"
|
||||
|
||||
-- No chats found
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T1886517101"] = "No chats found"
|
||||
|
||||
-- Create Chat
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T1939006681"] = "Create Chat"
|
||||
|
||||
@ -3252,6 +3264,9 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T3355849203"] = "Rename"
|
||||
-- Please enter a new or edit the name for your chat '{0}':
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T3419791373"] = "Please enter a new or edit the name for your chat '{0}':"
|
||||
|
||||
-- Search chat contents
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T3436662033"] = "Search chat contents"
|
||||
|
||||
-- Load Chat
|
||||
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T3555709365"] = "Load Chat"
|
||||
|
||||
@ -5958,6 +5973,9 @@ UI_TEXT_CONTENT["AISTUDIO::PAGES::ASSISTANTS::T878695986"] = "Learn about one co
|
||||
-- Localization
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::ASSISTANTS::T897888480"] = "Localization"
|
||||
|
||||
-- Hide search
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::CHAT::T1281128983"] = "Hide search"
|
||||
|
||||
-- Reload your workspaces
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::CHAT::T194629703"] = "Reload your workspaces"
|
||||
|
||||
@ -5970,6 +5988,9 @@ UI_TEXT_CONTENT["AISTUDIO::PAGES::CHAT::T2813205227"] = "Open Chat Options"
|
||||
-- Disappearing Chat
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::CHAT::T3046519404"] = "Disappearing Chat"
|
||||
|
||||
-- Search your workspaces
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::CHAT::T3059773282"] = "Search your workspaces"
|
||||
|
||||
-- Configure your workspaces
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::CHAT::T3586092784"] = "Configure your workspaces"
|
||||
|
||||
@ -6270,11 +6291,12 @@ UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T2989678330"] = "Copies the root
|
||||
-- Changelog
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T3017574265"] = "Changelog"
|
||||
|
||||
-- Vector store
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T3046399223"] = "Vector store"
|
||||
-- External HTTPS custom root certificates are configured but not active.
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T3021325354"] = "External HTTPS custom root certificates are configured but not active."
|
||||
|
||||
-- Vector store
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T3046399223"] = "Vector store"
|
||||
|
||||
-- Enterprise configuration ID:
|
||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T3092349641"] = "Enterprise configuration ID:"
|
||||
|
||||
|
||||
@ -230,6 +230,92 @@ public static class WorkspaceBehaviour
|
||||
chats.RemoveAt(existingIndex);
|
||||
}
|
||||
|
||||
private static IReadOnlyList<string> ParseSearchTerms(string searchText) => searchText
|
||||
.Split(' ', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)
|
||||
.Where(term => !string.IsNullOrWhiteSpace(term))
|
||||
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||
.ToList();
|
||||
|
||||
private static IReadOnlyList<string> GetMissingTerms(string text, IReadOnlyList<string> terms) => terms
|
||||
.Where(term => text.IndexOf(term, StringComparison.OrdinalIgnoreCase) < 0)
|
||||
.ToList();
|
||||
|
||||
private static bool ChatThreadContainsTerms(ChatThread thread, IReadOnlyList<string> terms)
|
||||
{
|
||||
var matchedTerms = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||
foreach (var block in thread.Blocks)
|
||||
{
|
||||
if (block.HideFromUser || block.Content is not ContentText textContent || string.IsNullOrWhiteSpace(textContent.Text))
|
||||
continue;
|
||||
|
||||
foreach (var term in terms)
|
||||
if (textContent.Text.Contains(term, StringComparison.OrdinalIgnoreCase))
|
||||
matchedTerms.Add(term);
|
||||
|
||||
if (matchedTerms.Count == terms.Count)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static async Task<bool> ThreadContainsTermsAsync(WorkspaceTreeChat chat, IReadOnlyList<string> terms, CancellationToken token)
|
||||
{
|
||||
var (acquired, semaphore) = await TryAcquireChatSemaphoreAsync(chat.WorkspaceId, chat.ChatId, nameof(ThreadContainsTermsAsync));
|
||||
if (!acquired)
|
||||
return false;
|
||||
|
||||
try
|
||||
{
|
||||
var threadPath = Path.Join(chat.ChatPath, "thread.json");
|
||||
if (!File.Exists(threadPath))
|
||||
return false;
|
||||
|
||||
var chatData = await File.ReadAllTextAsync(threadPath, Encoding.UTF8, token);
|
||||
token.ThrowIfCancellationRequested();
|
||||
var thread = JsonSerializer.Deserialize<ChatThread>(chatData, JSON_OPTIONS);
|
||||
return thread is not null && ChatThreadContainsTerms(thread, terms);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LOG.LogWarning(ex, "Failed to search chat thread for workspace '{WorkspaceId}', chat '{ChatId}'.", chat.WorkspaceId, chat.ChatId);
|
||||
return false;
|
||||
}
|
||||
finally
|
||||
{
|
||||
semaphore.Release();
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task<List<WorkspaceSearchResult>> SearchChatsAsync(IReadOnlyList<WorkspaceTreeChat> chats, IReadOnlyList<string> terms, bool includeThreadContents, CancellationToken token)
|
||||
{
|
||||
var results = new List<WorkspaceSearchResult>();
|
||||
foreach (var chat in chats)
|
||||
{
|
||||
token.ThrowIfCancellationRequested();
|
||||
|
||||
var missingTerms = GetMissingTerms(chat.Name, terms);
|
||||
if (missingTerms.Count == 0)
|
||||
{
|
||||
results.Add(new(chat, NameMatched: true, ThreadMatched: false));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!includeThreadContents)
|
||||
continue;
|
||||
|
||||
var threadMatched = await ThreadContainsTermsAsync(chat, missingTerms, token);
|
||||
if (threadMatched)
|
||||
results.Add(new(chat, NameMatched: false, ThreadMatched: true));
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
private static async Task UpdateCacheAfterChatStored(Guid workspaceId, Guid chatId, string chatDirectory, string chatName, DateTimeOffset lastEditTime)
|
||||
{
|
||||
await WORKSPACE_TREE_CACHE_SEMAPHORE.WaitAsync();
|
||||
@ -348,6 +434,55 @@ public static class WorkspaceBehaviour
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task<WorkspaceSearchSnapshot> SearchWorkspaceChatsAsync(string searchText, bool includeThreadContents, CancellationToken token = default)
|
||||
{
|
||||
var terms = ParseSearchTerms(searchText);
|
||||
if (terms.Count == 0)
|
||||
return new([], []);
|
||||
|
||||
List<WorkspaceTreeWorkspace> workspaces;
|
||||
List<WorkspaceTreeChat> temporaryChats;
|
||||
|
||||
await WORKSPACE_TREE_CACHE_SEMAPHORE.WaitAsync(token);
|
||||
try
|
||||
{
|
||||
await EnsureTreeShellLoadedCoreAsync();
|
||||
workspaces = [];
|
||||
foreach (var workspaceId in WORKSPACE_TREE_CACHE.WorkspaceOrder)
|
||||
{
|
||||
token.ThrowIfCancellationRequested();
|
||||
if (!WORKSPACE_TREE_CACHE.Workspaces.TryGetValue(workspaceId, out var workspace))
|
||||
continue;
|
||||
|
||||
if (!workspace.ChatsLoaded)
|
||||
{
|
||||
workspace.Chats = await ReadWorkspaceChatsCoreAsync(workspaceId, workspace.WorkspacePath);
|
||||
workspace.ChatsLoaded = true;
|
||||
}
|
||||
|
||||
workspaces.Add(ToPublicWorkspace(workspace));
|
||||
}
|
||||
|
||||
temporaryChats = WORKSPACE_TREE_CACHE.TemporaryChats.Select(ToPublicChat).ToList();
|
||||
}
|
||||
finally
|
||||
{
|
||||
WORKSPACE_TREE_CACHE_SEMAPHORE.Release();
|
||||
}
|
||||
|
||||
var matchingWorkspaces = new List<WorkspaceSearchWorkspace>();
|
||||
foreach (var workspace in workspaces)
|
||||
{
|
||||
token.ThrowIfCancellationRequested();
|
||||
var matchingChats = await SearchChatsAsync(workspace.Chats, terms, includeThreadContents, token);
|
||||
if (matchingChats.Count > 0)
|
||||
matchingWorkspaces.Add(new(workspace.WorkspaceId, workspace.WorkspacePath, workspace.Name, matchingChats));
|
||||
}
|
||||
|
||||
var matchingTemporaryChats = await SearchChatsAsync(temporaryChats, terms, includeThreadContents, token);
|
||||
return new(matchingWorkspaces, matchingTemporaryChats);
|
||||
}
|
||||
|
||||
public static async Task TryPrefetchRemainingChatsAsync(Func<Guid, Task>? onWorkspaceUpdated = null, CancellationToken token = default)
|
||||
{
|
||||
while (true)
|
||||
|
||||
3
app/MindWork AI Studio/Tools/WorkspaceSearchResult.cs
Normal file
3
app/MindWork AI Studio/Tools/WorkspaceSearchResult.cs
Normal file
@ -0,0 +1,3 @@
|
||||
namespace AIStudio.Tools;
|
||||
|
||||
public readonly record struct WorkspaceSearchResult(WorkspaceTreeChat Chat, bool NameMatched, bool ThreadMatched);
|
||||
3
app/MindWork AI Studio/Tools/WorkspaceSearchSnapshot.cs
Normal file
3
app/MindWork AI Studio/Tools/WorkspaceSearchSnapshot.cs
Normal file
@ -0,0 +1,3 @@
|
||||
namespace AIStudio.Tools;
|
||||
|
||||
public readonly record struct WorkspaceSearchSnapshot(IReadOnlyList<WorkspaceSearchWorkspace> Workspaces, IReadOnlyList<WorkspaceSearchResult> TemporaryChats);
|
||||
3
app/MindWork AI Studio/Tools/WorkspaceSearchWorkspace.cs
Normal file
3
app/MindWork AI Studio/Tools/WorkspaceSearchWorkspace.cs
Normal file
@ -0,0 +1,3 @@
|
||||
namespace AIStudio.Tools;
|
||||
|
||||
public readonly record struct WorkspaceSearchWorkspace(Guid WorkspaceId, string WorkspacePath, string Name, IReadOnlyList<WorkspaceSearchResult> Chats);
|
||||
@ -4,6 +4,7 @@
|
||||
- Added support for managed custom root certificate bundles and host allowlists for external HTTPS requests, helping Flatpak deployments connect to organization-internal services with private root CAs while keeping built-in cloud provider endpoints on system trust.
|
||||
- Added support for reading enterprise policy files from a Flatpak provisioning extension.
|
||||
- Added startup path and Linux package type details to the information page to make support easier.
|
||||
- Added the option to search for chats in all workspaces.
|
||||
- Improved workspaces by adding a shortcut to start a new chat directly from each workspace row.
|
||||
- Improved the enterprise configuration details on the information page by showing where each configuration comes from and which configuration slot was used.
|
||||
- Upgraded dependencies.
|
||||
Loading…
Reference in New Issue
Block a user