refactor: Consolidate MCP resource attachment components

This commit is contained in:
Aleksander Grygier 2026-02-11 12:11:20 +01:00
parent 2bc1111f8d
commit 5824e8b98a
3 changed files with 32 additions and 92 deletions

View File

@ -1,69 +0,0 @@
<script lang="ts">
import { cn } from '$lib/components/ui/utils';
import { mcpStore } from '$lib/stores/mcp.svelte';
import { getResourceIcon } from '$lib/utils';
import type { DatabaseMessageExtraMcpResource } from '$lib/types';
import * as Tooltip from '$lib/components/ui/tooltip';
import { ActionIconRemove } from '$lib/components/app';
interface Props {
extra: DatabaseMessageExtraMcpResource;
readonly?: boolean;
onRemove?: () => void;
onClick?: (event?: MouseEvent) => void;
class?: string;
}
let { extra, readonly = true, onRemove, onClick, class: className }: Props = $props();
const ResourceIcon = $derived(getResourceIcon(extra.mimeType, extra.uri));
const serverName = $derived(mcpStore.getServerDisplayName(extra.serverName));
const favicon = $derived(mcpStore.getServerFavicon(extra.serverName));
</script>
<Tooltip.Root>
<Tooltip.Trigger>
<button
type="button"
class={cn(
'flex flex-shrink-0 items-center gap-2 rounded-md border border-border/50 bg-muted/30 p-0.5 px-2 text-sm',
onClick && 'cursor-pointer hover:bg-muted/50',
className
)}
onclick={(e) => {
e.stopPropagation();
onClick?.(e);
}}
disabled={!onClick}
>
<ResourceIcon class="h-3.5 w-3.5 text-muted-foreground" />
<span class="max-w-[150px] truncate">
{extra.name}
</span>
{#if !readonly && onRemove}
<ActionIconRemove class="bg-transparent" id={extra.uri} onRemove={() => onRemove?.()} />
{/if}
</button>
</Tooltip.Trigger>
<Tooltip.Content>
<div class="flex items-center gap-1 text-xs">
{#if favicon}
<img
src={favicon}
alt=""
class="h-3 w-3 shrink-0 rounded-sm"
onerror={(e) => {
(e.currentTarget as HTMLImageElement).style.display = 'none';
}}
/>
{/if}
<span class="truncate">
{serverName}
</span>
</div>
</Tooltip.Content>
</Tooltip.Root>

View File

@ -1,7 +1,7 @@
<script lang="ts">
import {
ChatAttachmentMcpPrompt,
ChatAttachmentMcpResourceStored,
ChatAttachmentMcpResource,
ChatAttachmentThumbnailImage,
ChatAttachmentThumbnailFile,
HorizontalScrollCarousel,
@ -11,7 +11,11 @@
} from '$lib/components/app';
import { Button } from '$lib/components/ui/button';
import { AttachmentType } from '$lib/enums';
import type { DatabaseMessageExtraMcpPrompt, DatabaseMessageExtraMcpResource } from '$lib/types';
import type {
DatabaseMessageExtraMcpPrompt,
DatabaseMessageExtraMcpResource,
MCPResourceAttachment
} from '$lib/types';
import { getAttachmentDisplayItems } from '$lib/utils';
interface Props {
@ -74,13 +78,26 @@
previewDialogOpen = true;
}
function openMcpResourcePreview(extra: DatabaseMessageExtraMcpResource, event?: MouseEvent) {
event?.stopPropagation();
event?.preventDefault();
function openMcpResourcePreview(extra: DatabaseMessageExtraMcpResource) {
mcpResourcePreviewExtra = extra;
mcpResourcePreviewOpen = true;
}
function toMcpResourceAttachment(
extra: DatabaseMessageExtraMcpResource,
id: string
): MCPResourceAttachment {
return {
id,
resource: {
uri: extra.uri,
name: extra.name,
title: extra.name,
serverName: extra.serverName
}
};
}
$effect(() => {
if (carouselRef && displayItems.length) {
carouselRef.resetScroll();
@ -123,13 +140,12 @@
/>
{/if}
{:else if item.isMcpResource && item.attachment?.type === AttachmentType.MCP_RESOURCE}
<ChatAttachmentMcpResourceStored
{@const mcpResource = item.attachment as DatabaseMessageExtraMcpResource}
<ChatAttachmentMcpResource
class="flex-shrink-0 {limitToSingleRow ? 'first:ml-4 last:mr-4' : ''}"
extra={item.attachment as DatabaseMessageExtraMcpResource}
{readonly}
onRemove={onFileRemove ? () => onFileRemove(item.id) : undefined}
onClick={(event) =>
openMcpResourcePreview(item.attachment as DatabaseMessageExtraMcpResource, event)}
attachment={toMcpResourceAttachment(mcpResource, item.id)}
onClick={() => openMcpResourcePreview(mcpResource)}
/>
{:else if item.isImage && item.preview}
<ChatAttachmentThumbnailImage
@ -203,12 +219,11 @@
/>
{/if}
{:else if item.isMcpResource && item.attachment?.type === AttachmentType.MCP_RESOURCE}
<ChatAttachmentMcpResourceStored
extra={item.attachment as DatabaseMessageExtraMcpResource}
{readonly}
onRemove={onFileRemove ? () => onFileRemove(item.id) : undefined}
onClick={(event) =>
openMcpResourcePreview(item.attachment as DatabaseMessageExtraMcpResource, event)}
{@const mcpResource = item.attachment as DatabaseMessageExtraMcpResource}
<ChatAttachmentMcpResource
attachment={toMcpResourceAttachment(mcpResource, item.id)}
onClick={() => openMcpResourcePreview(mcpResource)}
/>
{:else if item.isImage && item.preview}
<ChatAttachmentThumbnailImage

View File

@ -65,12 +65,6 @@ export { default as ChatAttachmentMcpPrompt } from './ChatAttachments/ChatAttach
*/
export { default as ChatAttachmentMcpResource } from './ChatAttachments/ChatAttachmentMcpResource.svelte';
/**
* Displays a stored MCP Resource attachment (from database extras) with icon,
* name, and server info tooltip. Compact chip style matching live resource display.
*/
export { default as ChatAttachmentMcpResourceStored } from './ChatAttachments/ChatAttachmentMcpResourceStored.svelte';
/**
* Full-size attachment preview component for dialog display. Handles different file types:
* images (full-size display), text files (syntax highlighted), PDFs (text extraction or image preview),