3 ChatAttachmentsListItem,
4 DialogChatAttachmentsPreview,
5 DialogMcpResourcePreview,
6 HorizontalScrollCarousel
7 } from '$lib/components/app';
8 import type { DatabaseMessageExtraMcpResource } from '$lib/types';
9 import { getAttachmentDisplayItems, isMcpPrompt, isMcpResource } from '$lib/utils';
14 // For ChatMessage - stored attachments
15 attachments?: DatabaseMessageExtra[];
17 // For ChatForm - pending uploads
18 onFileRemove?: (fileId: string) => void;
19 uploadedFiles?: ChatUploadedFile[];
20 // Image size customization
24 // Limit display to single row with "+ X more" button
25 limitToSingleRow?: boolean;
26 // For vision modality check
27 activeModelId?: string;
31 class: className = '',
36 uploadedFiles = $bindable([]),
37 // Default to small size for form previews
40 imageWidth = 'w-auto',
41 limitToSingleRow = false,
45 let carouselRef: HorizontalScrollCarousel | undefined = $state();
46 let mcpResourcePreviewOpen = $state(false);
47 let mcpResourcePreviewExtra = $state<DatabaseMessageExtraMcpResource | null>(null);
48 let previewFocusIndex = $state(0);
49 let viewAllDialogOpen = $state(false);
51 let displayItems = $derived(getAttachmentDisplayItems({ uploadedFiles, attachments }));
53 function openPreview(item: ChatAttachmentDisplayItem, event?: MouseEvent) {
54 event?.stopPropagation();
55 event?.preventDefault();
57 // Find the index of the clicked item among non-MCP attachments
58 const nonMcpItems = displayItems.filter((i) => !isMcpPrompt(i) && !isMcpResource(i));
59 const index = nonMcpItems.findIndex((i) => i.id === item.id);
61 previewFocusIndex = index >= 0 ? index : 0;
62 viewAllDialogOpen = true;
65 function openMcpResourcePreview(extra: DatabaseMessageExtraMcpResource) {
66 mcpResourcePreviewExtra = extra;
67 mcpResourcePreviewOpen = true;
71 if (carouselRef && displayItems.length) {
72 carouselRef.resetScroll();
77 {#snippet attachmentitem(item: ChatAttachmentDisplayItem)}
78 <ChatAttachmentsListItem
85 onMcpResourcePreview={openMcpResourcePreview}
86 onPreview={(i: ChatAttachmentDisplayItem, event?: MouseEvent) => openPreview(i, event)}
91 {#if displayItems.length > 0}
92 <div class={className} {style}>
93 {#if limitToSingleRow}
94 <HorizontalScrollCarousel bind:this={carouselRef}>
95 {#each displayItems as item (item.id)}
96 {@render attachmentitem(item)}
98 </HorizontalScrollCarousel>
100 <div class="flex flex-wrap items-start justify-end gap-3">
101 {#each displayItems as item (item.id)}
102 {@render attachmentitem(item)}
109 <DialogChatAttachmentsPreview
112 bind:open={viewAllDialogOpen}
117 {#if mcpResourcePreviewExtra}
118 <DialogMcpResourcePreview extra={mcpResourcePreviewExtra} bind:open={mcpResourcePreviewOpen} />