Skip to content

Commit 759a073

Browse files
Added possibility to view file attachments in chat (#594)
Co-authored-by: Thorsten Sommer <SommerEngineering@users.noreply.github.com>
1 parent 159b787 commit 759a073

File tree

9 files changed

+299
-33
lines changed

9 files changed

+299
-33
lines changed

app/MindWork AI Studio/Assistants/I18N/allTexts.lua

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1522,24 +1522,30 @@ UI_TEXT_CONTENT["AISTUDIO::CHAT::IIMAGESOURCEEXTENSIONS::T349928509"] = "The ima
15221522
-- Open Settings
15231523
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ASSISTANTBLOCK::T1172211894"] = "Open Settings"
15241524

1525+
-- Click the paperclip to attach files, or click the number to see your attached files.
1526+
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ATTACHDOCUMENTS::T1358313858"] = "Click the paperclip to attach files, or click the number to see your attached files."
1527+
1528+
-- Drop files here to attach them.
1529+
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ATTACHDOCUMENTS::T143112277"] = "Drop files here to attach them."
1530+
1531+
-- Click here to attach files.
1532+
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ATTACHDOCUMENTS::T1875575968"] = "Click here to attach files."
1533+
15251534
-- Drag and drop files into the marked area or click here to attach documents:
15261535
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ATTACHDOCUMENTS::T230755331"] = "Drag and drop files into the marked area or click here to attach documents:"
15271536

1537+
-- Select files to attach
1538+
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ATTACHDOCUMENTS::T2495931372"] = "Select files to attach"
1539+
15281540
-- Document Preview
15291541
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ATTACHDOCUMENTS::T285154968"] = "Document Preview"
15301542

1531-
-- Click to attach files
1532-
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ATTACHDOCUMENTS::T3521845090"] = "Click to attach files"
1533-
15341543
-- Clear file list
15351544
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ATTACHDOCUMENTS::T3759696136"] = "Clear file list"
15361545

15371546
-- Add file
15381547
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ATTACHDOCUMENTS::T4014053962"] = "Add file"
15391548

1540-
-- Select a file to attach
1541-
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ATTACHDOCUMENTS::T595772870"] = "Select a file to attach"
1542-
15431549
-- Changelog
15441550
UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::CHANGELOG::T3017574265"] = "Changelog"
15451551

@@ -3424,6 +3430,24 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::RETRIEVALPROCESSDIALOG::T900713019"] = "Canc
34243430
-- Embeddings
34253431
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::RETRIEVALPROCESSDIALOG::T951463987"] = "Embeddings"
34263432

3433+
-- Here you can see all attached files. Files that can no longer be found (deleted, renamed, or moved) are marked with a warning icon and a strikethrough name. You can remove any attachment using the trash can icon.
3434+
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::REVIEWATTACHMENTSDIALOG::T1746160064"] = "Here you can see all attached files. Files that can no longer be found (deleted, renamed, or moved) are marked with a warning icon and a strikethrough name. You can remove any attachment using the trash can icon."
3435+
3436+
-- There aren't any file attachments available right now.
3437+
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::REVIEWATTACHMENTSDIALOG::T2111340711"] = "There aren't any file attachments available right now."
3438+
3439+
-- The file was deleted, renamed, or moved.
3440+
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::REVIEWATTACHMENTSDIALOG::T3083729256"] = "The file was deleted, renamed, or moved."
3441+
3442+
-- Your attached file.
3443+
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::REVIEWATTACHMENTSDIALOG::T3154198222"] = "Your attached file."
3444+
3445+
-- Your attached files
3446+
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::REVIEWATTACHMENTSDIALOG::T3909191077"] = "Your attached files"
3447+
3448+
-- Remove this attachment.
3449+
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::REVIEWATTACHMENTSDIALOG::T3933470258"] = "Remove this attachment."
3450+
34273451
-- There is no social event
34283452
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::SETTINGS::SETTINGSDIALOGAGENDA::T1222800281"] = "There is no social event"
34293453

app/MindWork AI Studio/Chat/ContentBlockComponent.razor

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@
2020
{
2121
<MudTooltip Text="@T("Number of attachments")" Placement="Placement.Bottom">
2222
<MudBadge Content="@this.Content.FileAttachments.Count" Color="Color.Primary" Overlap="true" BadgeClass="sources-card-header">
23-
<MudIconButton Icon="@Icons.Material.Filled.AttachFile" />
23+
<MudIconButton Icon="@Icons.Material.Filled.AttachFile"
24+
OnClick="@this.OpenAttachmentsDialog"/>
2425
</MudBadge>
2526
</MudTooltip>
2627
}

app/MindWork AI Studio/Chat/ContentBlockComponent.razor.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using AIStudio.Components;
2+
using AIStudio.Dialogs;
23
using AIStudio.Tools.Services;
34
using Microsoft.AspNetCore.Components;
45

@@ -188,4 +189,9 @@ private async Task EditLastUserBlock()
188189
await this.EditLastUserBlockFunc(this.Content);
189190
}
190191

192+
private async Task OpenAttachmentsDialog()
193+
{
194+
var result = await ReviewAttachmentsDialog.OpenDialogAsync(this.DialogService, this.Content.FileAttachments.ToHashSet());
195+
this.Content.FileAttachments = result.ToList();
196+
}
191197
}

app/MindWork AI Studio/Components/AttachDocuments.razor

Lines changed: 32 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,28 +3,49 @@
33
@if (this.UseSmallForm)
44
{
55
<div @onmouseenter="@this.OnMouseEnter" @onmouseleave="@this.OnMouseLeave">
6-
@{
7-
var fileInfos = this.DocumentPaths.Select(file => new FileInfo(file)).ToList();
8-
}
9-
@if (fileInfos.Any())
6+
@if (this.isDraggingOver)
107
{
118
<MudBadge
129
Content="@this.DocumentPaths.Count"
1310
Color="Color.Primary"
14-
Overlap="true">
15-
<MudIconButton
16-
Icon="@Icons.Material.Filled.AttachFile"
17-
Color="Color.Default"
18-
OnClick="@AddFilesManually"/>
11+
Overlap="true"
12+
Class="cursor-pointer"
13+
OnClick="@this.OpenAttachmentsDialog">
14+
<MudLink OnClick="@this.AddFilesManually" Style="text-decoration: none;">
15+
<MudTextField T="string"
16+
Text="@DROP_FILES_HERE_TEXT"
17+
Adornment="Adornment.Start"
18+
AdornmentIcon="@Icons.Material.Filled.AttachFile"
19+
Typo="Typo.body2"
20+
Variant="Variant.Outlined"
21+
ReadOnly="true"
22+
/>
23+
</MudLink>
1924
</MudBadge>
2025
}
26+
else if (this.DocumentPaths.Any())
27+
{
28+
<MudTooltip Text="@T("Click the paperclip to attach files, or click the number to see your attached files.")" Placement="@TOOLBAR_TOOLTIP_PLACEMENT">
29+
<MudBadge
30+
Content="@this.DocumentPaths.Count"
31+
Color="Color.Primary"
32+
Overlap="true"
33+
Class="cursor-pointer"
34+
OnClick="@this.OpenAttachmentsDialog">
35+
<MudIconButton
36+
Icon="@Icons.Material.Filled.AttachFile"
37+
Color="Color.Default"
38+
OnClick="@this.AddFilesManually"/>
39+
</MudBadge>
40+
</MudTooltip>
41+
}
2142
else
2243
{
23-
<MudTooltip Text="@T("Click to attach files")" Placement="@TOOLBAR_TOOLTIP_PLACEMENT">
44+
<MudTooltip Text="@T("Click here to attach files.")" Placement="@TOOLBAR_TOOLTIP_PLACEMENT">
2445
<MudIconButton
2546
Icon="@Icons.Material.Filled.AttachFile"
2647
Color="Color.Default"
27-
OnClick="@AddFilesManually"/>
48+
OnClick="@this.AddFilesManually"/>
2849
</MudTooltip>
2950
}
3051
</div>

app/MindWork AI Studio/Components/AttachDocuments.razor.cs

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using AIStudio.Dialogs;
2+
using AIStudio.Tools.PluginSystem;
23
using AIStudio.Tools.Rust;
34
using AIStudio.Tools.Services;
45
using AIStudio.Tools.Validation;
@@ -11,6 +12,8 @@ namespace AIStudio.Components;
1112

1213
public partial class AttachDocuments : MSGComponentBase
1314
{
15+
private static string TB(string fallbackEN) => I18N.I.T(fallbackEN, typeof(AttachDocuments).Namespace, nameof(AttachDocuments));
16+
1417
[Parameter]
1518
public string Name { get; set; } = string.Empty;
1619

@@ -45,6 +48,10 @@ public partial class AttachDocuments : MSGComponentBase
4548
private PandocAvailabilityService PandocAvailabilityService { get; init; } = null!;
4649

4750
private const Placement TOOLBAR_TOOLTIP_PLACEMENT = Placement.Top;
51+
private static readonly string DROP_FILES_HERE_TEXT = TB("Drop files here to attach them.");
52+
53+
private bool isComponentHovered;
54+
private bool isDraggingOver;
4855

4956
#region Overrides of MSGComponentBase
5057

@@ -65,10 +72,23 @@ protected override async Task OnInitializedAsync()
6572
return;
6673
}
6774

75+
this.isDraggingOver = true;
6876
this.SetDragClass();
6977
this.StateHasChanged();
7078
break;
7179

80+
case Event.TAURI_EVENT_RECEIVED when data is TauriEvent { EventType: TauriEventType.FILE_DROP_CANCELED }:
81+
this.isDraggingOver = false;
82+
this.StateHasChanged();
83+
break;
84+
85+
case Event.TAURI_EVENT_RECEIVED when data is TauriEvent { EventType: TauriEventType.WINDOW_NOT_FOCUSED }:
86+
this.isDraggingOver = false;
87+
this.isComponentHovered = false;
88+
this.ClearDragClass();
89+
this.StateHasChanged();
90+
break;
91+
7292
case Event.TAURI_EVENT_RECEIVED when data is TauriEvent { EventType: TauriEventType.FILE_DROP_DROPPED, Payload: var paths }:
7393
if(!this.isComponentHovered && !this.CatchAllDocuments)
7494
{
@@ -85,6 +105,7 @@ protected override async Task OnInitializedAsync()
85105
if (!pandocState.IsAvailable)
86106
{
87107
this.Logger.LogWarning("The user cancelled the Pandoc installation or Pandoc is not available. Aborting file drop.");
108+
this.isDraggingOver = false;
88109
this.ClearDragClass();
89110
this.StateHasChanged();
90111
return;
@@ -100,6 +121,8 @@ protected override async Task OnInitializedAsync()
100121

101122
await this.DocumentPathsChanged.InvokeAsync(this.DocumentPaths);
102123
await this.OnChange(this.DocumentPaths);
124+
this.isDraggingOver = false;
125+
this.ClearDragClass();
103126
this.StateHasChanged();
104127
break;
105128
}
@@ -111,8 +134,6 @@ protected override async Task OnInitializedAsync()
111134

112135
private string dragClass = DEFAULT_DRAG_CLASS;
113136

114-
private bool isComponentHovered;
115-
116137
private async Task AddFilesManually()
117138
{
118139
// Ensure that Pandoc is installed and ready:
@@ -127,7 +148,7 @@ private async Task AddFilesManually()
127148
return;
128149
}
129150

130-
var selectFiles = await this.RustService.SelectFiles(T("Select a file to attach"));
151+
var selectFiles = await this.RustService.SelectFiles(T("Select files to attach"));
131152
if (selectFiles.UserCancelled)
132153
return;
133154

@@ -145,6 +166,11 @@ private async Task AddFilesManually()
145166
await this.DocumentPathsChanged.InvokeAsync(this.DocumentPaths);
146167
await this.OnChange(this.DocumentPaths);
147168
}
169+
170+
private async Task OpenAttachmentsDialog()
171+
{
172+
this.DocumentPaths = await ReviewAttachmentsDialog.OpenDialogAsync(this.DialogService, this.DocumentPaths);
173+
}
148174

149175
private async Task ClearAllFiles()
150176
{
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
@inherits MSGComponentBase
2+
3+
<MudDialog>
4+
<TitleContent>
5+
<MudText Typo="Typo.h4">
6+
@T("Your attached files")
7+
</MudText>
8+
</TitleContent>
9+
<DialogContent>
10+
<MudJustifiedText Typo="Typo.body1" Class="mb-3">
11+
@T("Here you can see all attached files. Files that can no longer be found (deleted, renamed, or moved) are marked with a warning icon and a strikethrough name. You can remove any attachment using the trash can icon.")
12+
</MudJustifiedText>
13+
14+
<MudDivider Class="mt-3 mb-3"/>
15+
16+
<div style="max-height: 50vh; overflow-y: auto; overflow-x: hidden; padding-right: 8px;">
17+
@if (!this.DocumentPaths.Any())
18+
{
19+
<MudJustifiedText Typo="Typo.body1" Class="mt-3">
20+
@T("There aren't any file attachments available right now.")
21+
</MudJustifiedText>
22+
}
23+
24+
@{
25+
var currentFolder = string.Empty;
26+
foreach (var filePath in this.DocumentPaths)
27+
{
28+
var folderPath = Path.GetDirectoryName(filePath);
29+
if (folderPath != currentFolder)
30+
{
31+
currentFolder = folderPath;
32+
<MudStack Row="true" AlignItems="AlignItems.Center" Class="mt-6 mb-3">
33+
<MudIcon Icon="@Icons.Material.Filled.Folder" Class="mr-2" />
34+
<MudText Typo="Typo.h6">
35+
@folderPath:
36+
</MudText>
37+
</MudStack>
38+
}
39+
40+
@if (File.Exists(filePath))
41+
{
42+
<MudStack Row Justify="Justify.SpaceBetween" AlignItems="AlignItems.Center" Class="ms-3 mb-2">
43+
<div style="min-width: 0; flex: 1; overflow: hidden;">
44+
<MudTooltip Text="@T("Your attached file.")" Placement="Placement.Bottom">
45+
<span class="d-inline-flex align-items-center" style="overflow: hidden; width: 100%;">
46+
<MudIcon Icon="@Icons.Material.Filled.AttachFile" Class="mr-2" Style="flex-shrink: 0;"/>
47+
<MudText Style="white-space: nowrap;">
48+
@Path.GetFileName(filePath)
49+
</MudText>
50+
</span>
51+
</MudTooltip>
52+
</div>
53+
54+
<MudTooltip Text="@T("Remove this attachment.")" Placement="Placement.Bottom">
55+
<MudIconButton Icon="@Icons.Material.Filled.Delete"
56+
Color="Color.Error"
57+
Class="ml-2"
58+
Style="flex-shrink: 0;"
59+
OnClick="@(() => this.DeleteAttachment(filePath))"/>
60+
</MudTooltip>
61+
</MudStack>
62+
63+
}
64+
else
65+
{
66+
<MudStack Row Justify="Justify.SpaceBetween" AlignItems="AlignItems.Center" Class="ms-3 mb-2">
67+
<div style="min-width: 0; flex: 1; overflow: hidden;">
68+
<MudTooltip Text="@T("The file was deleted, renamed, or moved.")" Placement="Placement.Bottom">
69+
<span class="d-inline-flex align-items-center" style="overflow: hidden; width: 100%;">
70+
<MudIcon Icon="@Icons.Material.Filled.Report" Color="Color.Error" Class="mr-2" Style="flex-shrink: 0;"/>
71+
<MudText Style="white-space: nowrap;">
72+
<s>@Path.GetFileName(filePath)</s>
73+
</MudText>
74+
</span>
75+
</MudTooltip>
76+
</div>
77+
78+
<MudTooltip Text="@T("Remove this attachment.")" Placement="Placement.Bottom">
79+
<MudIconButton Icon="@Icons.Material.Filled.Delete"
80+
Color="Color.Error"
81+
Class="ml-2"
82+
Style="flex-shrink: 0;"
83+
OnClick="@(() => this.DeleteAttachment(filePath))"/>
84+
</MudTooltip>
85+
</MudStack>
86+
}
87+
}
88+
}
89+
</div>
90+
</DialogContent>
91+
<DialogActions>
92+
<MudButton OnClick="@this.Close" Variant="Variant.Filled" Color="Color.Primary">
93+
Close
94+
</MudButton>
95+
</DialogActions>
96+
</MudDialog>
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
using AIStudio.Components;
2+
using Microsoft.AspNetCore.Components;
3+
4+
namespace AIStudio.Dialogs;
5+
6+
public partial class ReviewAttachmentsDialog : MSGComponentBase
7+
{
8+
[CascadingParameter]
9+
private IMudDialogInstance MudDialog { get; set; } = null!;
10+
11+
[Parameter]
12+
public HashSet<string> DocumentPaths { get; set; } = new();
13+
14+
[Inject]
15+
private IDialogService DialogService { get; set; } = null!;
16+
17+
private void Close() => this.MudDialog.Close(DialogResult.Ok(this.DocumentPaths));
18+
19+
public static async Task<HashSet<string>> OpenDialogAsync(IDialogService dialogService, params HashSet<string> documentPaths)
20+
{
21+
var dialogParameters = new DialogParameters<ReviewAttachmentsDialog>
22+
{
23+
{ x => x.DocumentPaths, documentPaths }
24+
};
25+
26+
var dialogReference = await dialogService.ShowAsync<ReviewAttachmentsDialog>("Your attached documents", dialogParameters, DialogOptions.FULLSCREEN);
27+
var dialogResult = await dialogReference.Result;
28+
if (dialogResult is null || dialogResult.Canceled)
29+
return documentPaths;
30+
31+
if (dialogResult.Data is null)
32+
return documentPaths;
33+
34+
return dialogResult.Data as HashSet<string> ?? documentPaths;
35+
}
36+
37+
private void DeleteAttachment(string filePath)
38+
{
39+
if (this.DocumentPaths.Remove(filePath))
40+
{
41+
this.StateHasChanged();
42+
}
43+
}
44+
}

0 commit comments

Comments
 (0)