]> git.djapps.eu Git - pkg/ggml/sources/llama.cpp/blob
3eeace42f16fe71f934dac79ee162f78c94aa672
[pkg/ggml/sources/llama.cpp] /
1 <script lang="ts">
2 import { X } from '@lucide/svelte';
3 import {
4 formatFileSize,
5 getFileTypeLabel,
6 getPreviewText,
7 isPdfFile,
8 isTextFile
9 } from '$lib/utils';
10 import { ActionIcon } from '$lib/components/app';
11 import { AttachmentType } from '$lib/enums';
12
13 interface Props {
14 attachment?: DatabaseMessageExtra;
15 class?: string;
16 id: string;
17 onclick?: (event: MouseEvent) => void;
18 onRemove?: (id: string) => void;
19 name: string;
20 readonly?: boolean;
21 size?: number;
22 textContent?: string;
23 // Either uploaded file or stored attachment
24 uploadedFile?: ChatUploadedFile;
25 }
26
27 let {
28 attachment,
29 class: className = '',
30 id,
31 onclick,
32 onRemove,
33 name,
34 readonly = false,
35 size,
36 textContent,
37 uploadedFile
38 }: Props = $props();
39
40 let isPdf = $derived(isPdfFile(attachment, uploadedFile));
41 let isPdfWithContent = $derived(isPdf && !!textContent);
42
43 let isText = $derived(isTextFile(attachment, uploadedFile));
44 let isTextWithContent = $derived(isText && !!textContent);
45
46 let fileTypeLabel = $derived.by(() => {
47 if (uploadedFile?.type) {
48 return getFileTypeLabel(uploadedFile.type);
49 }
50
51 if (attachment) {
52 if ('mimeType' in attachment && attachment.mimeType) {
53 return getFileTypeLabel(attachment.mimeType);
54 }
55
56 if (attachment.type) {
57 return getFileTypeLabel(attachment.type);
58 }
59 }
60
61 return getFileTypeLabel(name);
62 });
63
64 let pdfProcessingMode = $derived.by(() => {
65 if (attachment?.type === AttachmentType.PDF) {
66 const pdfAttachment = attachment as DatabaseMessageExtraPdfFile;
67
68 return pdfAttachment.processedAsImages ? 'Sent as Image' : 'Sent as Text';
69 }
70
71 return null;
72 });
73 </script>
74
75 {#snippet textPreview(content: string)}
76 <div class="relative">
77 <div
78 class="font-mono text-xs leading-relaxed break-words whitespace-pre-wrap text-muted-foreground {!readonly
79 ? 'max-h-3rem line-height-1.2'
80 : ''}"
81 >
82 {getPreviewText(content)}
83 </div>
84
85 {#if content.length > 150}
86 <div
87 class="pointer-events-none absolute right-0 bottom-0 left-0 h-4 bg-gradient-to-t from-muted to-transparent {readonly
88 ? 'h-6'
89 : ''}"
90 ></div>
91 {/if}
92 </div>
93 {/snippet}
94
95 {#snippet removeButton()}
96 <div class="absolute top-2 right-2 opacity-0 transition-opacity group-hover:opacity-100">
97 <ActionIcon icon={X} tooltip="Remove" stopPropagationOnClick onclick={() => onRemove?.(id)} />
98 </div>
99 {/snippet}
100
101 {#snippet fileIcon()}
102 <div
103 class="flex h-8 w-8 items-center justify-center rounded bg-primary/10 text-xs font-medium text-primary"
104 >
105 {fileTypeLabel}
106 </div>
107 {/snippet}
108
109 {#snippet info(text: string | undefined)}
110 {#if text}
111 <span class="text-xs text-muted-foreground">{text}</span>
112 {/if}
113 {/snippet}
114
115 {#if isTextWithContent || isPdfWithContent}
116 <button
117 aria-label={readonly ? `Preview ${name}` : undefined}
118 class="rounded-lg border border-border bg-muted p-3 {className} cursor-pointer {readonly
119 ? 'w-full max-w-2xl transition-shadow hover:shadow-md'
120 : `group relative text-left ${textContent ? 'max-h-24 max-w-72' : 'max-w-36'}`} overflow-hidden"
121 {onclick}
122 type="button"
123 >
124 {#if !readonly}
125 {@render removeButton()}
126 {/if}
127
128 <div class={[!readonly && 'pr-8', 'overflow-hidden']}>
129 {#if readonly}
130 <div class="flex items-start gap-3">
131 <div class="flex min-w-0 flex-1 flex-col items-start text-left">
132 <span class="w-full truncate text-sm font-medium text-foreground">{name}</span>
133
134 {@render info(pdfProcessingMode || (size ? formatFileSize(size) : undefined))}
135
136 {#if textContent}
137 {@render textPreview(textContent)}
138 {/if}
139 </div>
140 </div>
141 {:else}
142 <span class="mb-3 block truncate text-sm font-medium text-foreground">{name}</span>
143
144 {#if textContent}
145 {@render textPreview(textContent)}
146 {/if}
147 {/if}
148 </div>
149 </button>
150 {:else}
151 <button
152 class="group flex items-center gap-3 rounded-lg border border-border bg-muted p-3 {className} relative"
153 {onclick}
154 type="button"
155 >
156 {@render fileIcon()}
157
158 <div class="flex flex-col items-start gap-0.5">
159 <span
160 class="max-w-24 truncate text-sm font-medium text-foreground {readonly
161 ? ''
162 : 'group-hover:pr-6'} md:max-w-32"
163 >
164 {name}
165 </span>
166
167 {@render info(pdfProcessingMode || (size ? formatFileSize(size) : undefined))}
168 </div>
169
170 {#if !readonly}
171 {@render removeButton()}
172 {/if}
173 </button>
174 {/if}