diff --git a/app/MindWork AI Studio/Components/Blocks/ITreeItem.cs b/app/MindWork AI Studio/Components/Blocks/ITreeItem.cs new file mode 100644 index 00000000..b33c178d --- /dev/null +++ b/app/MindWork AI Studio/Components/Blocks/ITreeItem.cs @@ -0,0 +1,3 @@ +namespace AIStudio.Components.Blocks; + +public interface ITreeItem; \ No newline at end of file diff --git a/app/MindWork AI Studio/Components/Blocks/TreeButton.cs b/app/MindWork AI Studio/Components/Blocks/TreeButton.cs new file mode 100644 index 00000000..ff21b8d8 --- /dev/null +++ b/app/MindWork AI Studio/Components/Blocks/TreeButton.cs @@ -0,0 +1,3 @@ +namespace AIStudio.Components.Blocks; + +public readonly record struct TreeButton(WorkspaceBranch Branch, int Depth, string Text, string Icon) : ITreeItem; \ No newline at end of file diff --git a/app/MindWork AI Studio/Components/Blocks/TreeDivider.cs b/app/MindWork AI Studio/Components/Blocks/TreeDivider.cs new file mode 100644 index 00000000..c4a6d9f6 --- /dev/null +++ b/app/MindWork AI Studio/Components/Blocks/TreeDivider.cs @@ -0,0 +1,3 @@ +namespace AIStudio.Components.Blocks; + +public readonly record struct TreeDivider : ITreeItem; \ No newline at end of file diff --git a/app/MindWork AI Studio/Components/Blocks/TreeItemData.cs b/app/MindWork AI Studio/Components/Blocks/TreeItemData.cs new file mode 100644 index 00000000..7f825afa --- /dev/null +++ b/app/MindWork AI Studio/Components/Blocks/TreeItemData.cs @@ -0,0 +1,18 @@ +namespace AIStudio.Components.Blocks; + +public class TreeItemData : ITreeItem +{ + public WorkspaceBranch Branch { get; init; } = WorkspaceBranch.NONE; + + public int Depth { get; init; } + + public string Text { get; init; } = string.Empty; + + public string Icon { get; init; } = string.Empty; + + public T? Value { get; init; } + + public bool Expandable { get; init; } = true; + + public HashSet> Children { get; } = []; +} \ No newline at end of file diff --git a/app/MindWork AI Studio/Components/Blocks/WorkspaceBranch.cs b/app/MindWork AI Studio/Components/Blocks/WorkspaceBranch.cs new file mode 100644 index 00000000..1b19bd34 --- /dev/null +++ b/app/MindWork AI Studio/Components/Blocks/WorkspaceBranch.cs @@ -0,0 +1,9 @@ +namespace AIStudio.Components.Blocks; + +public enum WorkspaceBranch +{ + NONE, + + WORKSPACES, + TEMPORARY_CHATS, +} \ No newline at end of file diff --git a/app/MindWork AI Studio/Components/Blocks/Workspaces.razor b/app/MindWork AI Studio/Components/Blocks/Workspaces.razor index f85045ff..46a4169d 100644 --- a/app/MindWork AI Studio/Components/Blocks/Workspaces.razor +++ b/app/MindWork AI Studio/Components/Blocks/Workspaces.razor @@ -1 +1,41 @@ -TODO \ No newline at end of file + + + @switch (item) + { + case TreeDivider: +
  • + +
  • + break; + + case TreeItemData treeItem: + + +
    + @treeItem.Text + + @if (treeItem.Value is not "root" and not "temp") + { +
    + + +
    + } +
    +
    +
    + break; + + case TreeButton treeButton: +
  • +
    +
    + + @treeButton.Text + +
    +
  • + break; + } +
    +
    \ No newline at end of file diff --git a/app/MindWork AI Studio/Components/Blocks/Workspaces.razor.cs b/app/MindWork AI Studio/Components/Blocks/Workspaces.razor.cs index 53b6b382..e7c64fa1 100644 --- a/app/MindWork AI Studio/Components/Blocks/Workspaces.razor.cs +++ b/app/MindWork AI Studio/Components/Blocks/Workspaces.razor.cs @@ -1,7 +1,161 @@ -using Microsoft.AspNetCore.Components; +using AIStudio.Chat; +using AIStudio.Settings; + +using Microsoft.AspNetCore.Components; namespace AIStudio.Components.Blocks; public partial class Workspaces : ComponentBase { + [Inject] + private SettingsManager SettingsManager { get; set; } = null!; + + [Parameter] + public ChatThread? CurrentChatThread { get; set; } + + private readonly HashSet> initialTreeItems = new(); + + #region Overrides of ComponentBase + + protected override async Task OnInitializedAsync() + { + // + // Notice: In order to get the server-based loading to work, we need to respect the following rules: + // - We must have initial tree items + // - Those initial tree items cannot have children + // - When assigning the tree items to the MudTreeViewItem component, we must set the Value property to the value of the item + // + + this.initialTreeItems.Add(new TreeItemData + { + Depth = 0, + Branch = WorkspaceBranch.WORKSPACES, + Text = "Workspaces", + Icon = Icons.Material.Filled.Folder, + Expandable = true, + Value = "root", + }); + + this.initialTreeItems.Add(new TreeDivider()); + this.initialTreeItems.Add(new TreeItemData + { + Depth = 0, + Branch = WorkspaceBranch.TEMPORARY_CHATS, + Text = "Temporary chats", + Icon = Icons.Material.Filled.Timer, + Expandable = true, + Value = "temp", + }); + + await base.OnInitializedAsync(); + } + + #endregion + + private Task>> LoadServerData(ITreeItem? parent) + { + switch (parent) + { + case TreeItemData item: + switch (item.Branch) + { + case WorkspaceBranch.WORKSPACES: + var workspaceChildren = new HashSet>(); + + if (item.Depth == 0) + { + // + // Search for workspace folders in the data directory: + // + + // Get the workspace root directory: + var workspaceDirectories = Path.Join(SettingsManager.DataDirectory, "workspaces"); + + // Ensure the directory exists: + Directory.CreateDirectory(workspaceDirectories); + + // Enumerate the workspace directories: + foreach (var workspaceDirPath in Directory.EnumerateDirectories(workspaceDirectories)) + { + workspaceChildren.Add(new TreeItemData + { + Depth = item.Depth + 1, + Branch = WorkspaceBranch.WORKSPACES, + Text = Path.GetFileName(workspaceDirPath), + Icon = Icons.Material.Filled.Description, + Expandable = true, + Value = workspaceDirPath, + }); + } + + workspaceChildren.Add(new TreeButton(WorkspaceBranch.WORKSPACES, item.Depth + 1, "Add workspace",Icons.Material.Filled.Add)); + } + + else if (item.Depth == 1) + { + // + // Search for workspace chats in the workspace directory: + // + + // Get the workspace directory: + var workspaceDirPath = item.Value; + + if(workspaceDirPath is null) + return Task.FromResult(new HashSet>()); + + // Enumerate the workspace directory: + foreach (var chatPath in Directory.EnumerateDirectories(workspaceDirPath)) + { + workspaceChildren.Add(new TreeItemData + { + Depth = item.Depth + 1, + Branch = WorkspaceBranch.WORKSPACES, + Text = Path.GetFileNameWithoutExtension(chatPath), + Icon = Icons.Material.Filled.Chat, + Expandable = false, + Value = chatPath, + }); + } + + workspaceChildren.Add(new TreeButton(WorkspaceBranch.WORKSPACES, item.Depth + 1, "Add chat",Icons.Material.Filled.Add)); + } + + return Task.FromResult(workspaceChildren); + + case WorkspaceBranch.TEMPORARY_CHATS: + var tempChildren = new HashSet>(); + + // + // Search for workspace folders in the data directory: + // + + // Get the workspace root directory: + var temporaryDirectories = Path.Join(SettingsManager.DataDirectory, "tempChats"); + + // Ensure the directory exists: + Directory.CreateDirectory(temporaryDirectories); + + // Enumerate the workspace directories: + foreach (var tempChatDirPath in Directory.EnumerateDirectories(temporaryDirectories)) + { + tempChildren.Add(new TreeItemData + { + Depth = item.Depth + 1, + Branch = WorkspaceBranch.TEMPORARY_CHATS, + Text = Path.GetFileName(tempChatDirPath), + Icon = Icons.Material.Filled.Timer, + Expandable = false, + Value = tempChatDirPath, + }); + } + + return Task.FromResult(tempChildren); + } + + return Task.FromResult(new HashSet>()); + + default: + return Task.FromResult(new HashSet>()); + } + } } \ No newline at end of file