1<script lang="ts">
2 import {
3 ChatAttachmentThumbnailImage,
4 ChatAttachmentThumbnailFile,
5 DialogChatAttachmentPreview
6 } from '$lib/components/app';
7 import { getAttachmentDisplayItems } from '$lib/utils';
8
9 interface Props {
10 uploadedFiles?: ChatUploadedFile[];
11 attachments?: DatabaseMessageExtra[];
12 readonly?: boolean;
13 onFileRemove?: (fileId: string) => void;
14 imageHeight?: string;
15 imageWidth?: string;
16 imageClass?: string;
17 activeModelId?: string;
18 }
19
20 let {
21 uploadedFiles = [],
22 attachments = [],
23 readonly = false,
24 onFileRemove,
25 imageHeight = 'h-24',
26 imageWidth = 'w-auto',
27 imageClass = '',
28 activeModelId
29 }: Props = $props();
30
31 let previewDialogOpen = $state(false);
32 let previewItem = $state<ChatAttachmentPreviewItem | null>(null);
33
34 let displayItems = $derived(getAttachmentDisplayItems({ uploadedFiles, attachments }));
35 let imageItems = $derived(displayItems.filter((item) => item.isImage));
36 let fileItems = $derived(displayItems.filter((item) => !item.isImage));
37
38 function openPreview(item: (typeof displayItems)[0], event?: Event) {
39 if (event) {
40 event.preventDefault();
41 event.stopPropagation();
42 }
43
44 previewItem = {
45 uploadedFile: item.uploadedFile,
46 attachment: item.attachment,
47 preview: item.preview,
48 name: item.name,
49 size: item.size,
50 textContent: item.textContent
51 };
52 previewDialogOpen = true;
53 }
54</script>
55
56<div class="space-y-4">
57 <div class="min-h-0 flex-1 space-y-6 overflow-y-auto px-1">
58 {#if fileItems.length > 0}
59 <div>
60 <h3 class="mb-3 text-sm font-medium text-foreground">Files ({fileItems.length})</h3>
61 <div class="flex flex-wrap items-start gap-3">
62 {#each fileItems as item (item.id)}
63 <ChatAttachmentThumbnailFile
64 class="cursor-pointer"
65 id={item.id}
66 name={item.name}
67 size={item.size}
68 {readonly}
69 onRemove={onFileRemove}
70 textContent={item.textContent}
71 attachment={item.attachment}
72 uploadedFile={item.uploadedFile}
73 onClick={(event?: MouseEvent) => openPreview(item, event)}
74 />
75 {/each}
76 </div>
77 </div>
78 {/if}
79
80 {#if imageItems.length > 0}
81 <div>
82 <h3 class="mb-3 text-sm font-medium text-foreground">Images ({imageItems.length})</h3>
83 <div class="flex flex-wrap items-start gap-3">
84 {#each imageItems as item (item.id)}
85 {#if item.preview}
86 <ChatAttachmentThumbnailImage
87 class="cursor-pointer"
88 id={item.id}
89 name={item.name}
90 preview={item.preview}
91 {readonly}
92 onRemove={onFileRemove}
93 height={imageHeight}
94 width={imageWidth}
95 {imageClass}
96 onClick={(event) => openPreview(item, event)}
97 />
98 {/if}
99 {/each}
100 </div>
101 </div>
102 {/if}
103 </div>
104</div>
105
106{#if previewItem}
107 <DialogChatAttachmentPreview
108 bind:open={previewDialogOpen}
109 uploadedFile={previewItem.uploadedFile}
110 attachment={previewItem.attachment}
111 preview={previewItem.preview}
112 name={previewItem.name}
113 size={previewItem.size}
114 textContent={previewItem.textContent}
115 {activeModelId}
116 />
117{/if}