@widgetic/chat 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +440 -0
- package/dist/adapters/index.d.ts +2 -0
- package/dist/adapters/index.js +2 -0
- package/dist/adapters/widgeticAdapter.d.ts +185 -0
- package/dist/adapters/widgeticAdapter.js +766 -0
- package/dist/components/ActionBar.svelte +342 -0
- package/dist/components/ActionBar.svelte.d.ts +37 -0
- package/dist/components/AttachmentDisplay.svelte +547 -0
- package/dist/components/AttachmentDisplay.svelte.d.ts +12 -0
- package/dist/components/Chat.svelte +1253 -0
- package/dist/components/Chat.svelte.d.ts +112 -0
- package/dist/components/ChatHeader.svelte +182 -0
- package/dist/components/ChatHeader.svelte.d.ts +12 -0
- package/dist/components/ChatInput.svelte +290 -0
- package/dist/components/ChatInput.svelte.d.ts +15 -0
- package/dist/components/ChatMessages.svelte +996 -0
- package/dist/components/ChatMessages.svelte.d.ts +28 -0
- package/dist/components/CodeBlock.svelte +286 -0
- package/dist/components/CodeBlock.svelte.d.ts +10 -0
- package/dist/components/ContextPreview.svelte +66 -0
- package/dist/components/ContextPreview.svelte.d.ts +8 -0
- package/dist/components/LoadingIndicator.svelte +151 -0
- package/dist/components/LoadingIndicator.svelte.d.ts +9 -0
- package/dist/components/StatusIndicator.svelte +116 -0
- package/dist/components/StatusIndicator.svelte.d.ts +9 -0
- package/dist/components/SuggestionButtons.svelte +244 -0
- package/dist/components/SuggestionButtons.svelte.d.ts +26 -0
- package/dist/components/index.d.ts +12 -0
- package/dist/components/index.js +12 -0
- package/dist/config.d.ts +43 -0
- package/dist/config.js +67 -0
- package/dist/constants/colors.d.ts +20 -0
- package/dist/constants/colors.js +16 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +20 -0
- package/dist/services/chatService.d.ts +72 -0
- package/dist/services/chatService.js +355 -0
- package/dist/services/index.d.ts +2 -0
- package/dist/services/index.js +2 -0
- package/dist/stores/chatStore.d.ts +46 -0
- package/dist/stores/chatStore.js +219 -0
- package/dist/stores/index.d.ts +2 -0
- package/dist/stores/index.js +2 -0
- package/dist/types/adapter.d.ts +86 -0
- package/dist/types/adapter.js +1 -0
- package/dist/types/api-temp.d.ts +61 -0
- package/dist/types/api-temp.js +42 -0
- package/dist/types/attachment.d.ts +84 -0
- package/dist/types/attachment.js +33 -0
- package/dist/types/chat.d.ts +114 -0
- package/dist/types/chat.js +1 -0
- package/dist/types/config.d.ts +89 -0
- package/dist/types/config.js +63 -0
- package/dist/types/context.d.ts +108 -0
- package/dist/types/context.js +11 -0
- package/dist/types/conversation.d.ts +45 -0
- package/dist/types/conversation.js +1 -0
- package/dist/types/events.d.ts +141 -0
- package/dist/types/events.js +1 -0
- package/dist/types/index.d.ts +16 -0
- package/dist/types/index.js +10 -0
- package/dist/types/message.d.ts +82 -0
- package/dist/types/message.js +1 -0
- package/dist/types/state.d.ts +117 -0
- package/dist/types/state.js +1 -0
- package/dist/utils/fileUtils.d.ts +93 -0
- package/dist/utils/fileUtils.js +299 -0
- package/dist/utils/index.d.ts +3 -0
- package/dist/utils/index.js +4 -0
- package/dist/utils/logger.d.ts +4 -0
- package/dist/utils/logger.js +28 -0
- package/package.json +104 -0
|
@@ -0,0 +1,547 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { ContentItemOutput } from '../types/message.js';
|
|
3
|
+
|
|
4
|
+
// Props
|
|
5
|
+
interface Props {
|
|
6
|
+
attachments: ContentItemOutput[];
|
|
7
|
+
variant?: 'message' | 'preview' | 'compact';
|
|
8
|
+
showPreview?: boolean;
|
|
9
|
+
maxDisplayCount?: number;
|
|
10
|
+
onRemove?: (attachment: ContentItemOutput) => void;
|
|
11
|
+
onView?: (attachment: ContentItemOutput) => void;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
let {
|
|
15
|
+
attachments,
|
|
16
|
+
variant = 'message',
|
|
17
|
+
showPreview = true,
|
|
18
|
+
maxDisplayCount = 10,
|
|
19
|
+
onRemove,
|
|
20
|
+
onView
|
|
21
|
+
}: Props = $props();
|
|
22
|
+
|
|
23
|
+
// Helper functions
|
|
24
|
+
function getFileIcon(fileType: string): string {
|
|
25
|
+
if (fileType.startsWith('image/')) return '🖼️';
|
|
26
|
+
if (fileType.startsWith('video/')) return '🎥';
|
|
27
|
+
if (fileType.startsWith('audio/')) return '🎵';
|
|
28
|
+
if (fileType.includes('pdf')) return '📄';
|
|
29
|
+
if (fileType.includes('text') || fileType.includes('markdown')) return '📝';
|
|
30
|
+
if (fileType.includes('mermaid')) return '📊';
|
|
31
|
+
return '📎';
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function formatFileSize(bytes: number | null | undefined): string {
|
|
35
|
+
if (!bytes || bytes === 0) return '';
|
|
36
|
+
const k = 1024;
|
|
37
|
+
const sizes = ['B', 'KB', 'MB', 'GB'];
|
|
38
|
+
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
39
|
+
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function isImageType(fileType: string): boolean {
|
|
43
|
+
return fileType.startsWith('image/');
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function isVideoType(fileType: string): boolean {
|
|
47
|
+
return fileType.startsWith('video/');
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
let lightboxAttachment: ContentItemOutput | null = $state(null);
|
|
51
|
+
|
|
52
|
+
function isViewableInLightbox(fileType: string): boolean {
|
|
53
|
+
return fileType.startsWith('image/') || fileType.includes('pdf');
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function handleView(attachment: ContentItemOutput) {
|
|
57
|
+
if (onView) {
|
|
58
|
+
onView(attachment);
|
|
59
|
+
} else if (isViewableInLightbox(attachment.file_type)) {
|
|
60
|
+
lightboxAttachment = attachment;
|
|
61
|
+
} else if (attachment.url) {
|
|
62
|
+
window.open(attachment.url, '_blank', 'noopener,noreferrer');
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function closeLightbox() {
|
|
67
|
+
lightboxAttachment = null;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function handleRemove(attachment: ContentItemOutput) {
|
|
71
|
+
if (onRemove) {
|
|
72
|
+
onRemove(attachment);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Limit displayed attachments if needed
|
|
77
|
+
let displayedAttachments = $derived(
|
|
78
|
+
maxDisplayCount && attachments.length > maxDisplayCount
|
|
79
|
+
? attachments.slice(0, maxDisplayCount)
|
|
80
|
+
: attachments
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
let hiddenCount = $derived(
|
|
84
|
+
maxDisplayCount && attachments.length > maxDisplayCount
|
|
85
|
+
? attachments.length - maxDisplayCount
|
|
86
|
+
: 0
|
|
87
|
+
);
|
|
88
|
+
</script>
|
|
89
|
+
|
|
90
|
+
{#if attachments.length > 0}
|
|
91
|
+
<div class="attachment-display {variant}">
|
|
92
|
+
<!-- Message variant - inline in chat bubbles -->
|
|
93
|
+
{#if variant === 'message'}
|
|
94
|
+
<div class="message-attachments">
|
|
95
|
+
{#each displayedAttachments as attachment (attachment.id)}
|
|
96
|
+
<div class="message-attachment-item">
|
|
97
|
+
{#if isImageType(attachment.file_type) && showPreview}
|
|
98
|
+
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
|
99
|
+
<!-- svelte-ignore a11y_no_noninteractive_element_interactions -->
|
|
100
|
+
<img
|
|
101
|
+
src={attachment.url}
|
|
102
|
+
alt={attachment.file_name || 'Attachment'}
|
|
103
|
+
class="attachment-image"
|
|
104
|
+
loading="lazy"
|
|
105
|
+
onclick={() => handleView(attachment)}
|
|
106
|
+
/>
|
|
107
|
+
{:else if isVideoType(attachment.file_type) && showPreview}
|
|
108
|
+
<video
|
|
109
|
+
src={attachment.url}
|
|
110
|
+
class="attachment-video"
|
|
111
|
+
controls
|
|
112
|
+
preload="metadata"
|
|
113
|
+
>
|
|
114
|
+
<track kind="captions" />
|
|
115
|
+
Your browser does not support video playback.
|
|
116
|
+
</video>
|
|
117
|
+
{:else}
|
|
118
|
+
<button
|
|
119
|
+
class="attachment-file"
|
|
120
|
+
onclick={() => handleView(attachment)}
|
|
121
|
+
title="Click to view {attachment.file_name || 'file'}"
|
|
122
|
+
>
|
|
123
|
+
<span class="file-icon">{getFileIcon(attachment.file_type)}</span>
|
|
124
|
+
<div class="file-info">
|
|
125
|
+
<span class="file-name">{attachment.file_name || 'Untitled'}</span>
|
|
126
|
+
{#if attachment.file_size}
|
|
127
|
+
<span class="file-size">{formatFileSize(attachment.file_size)}</span>
|
|
128
|
+
{/if}
|
|
129
|
+
</div>
|
|
130
|
+
</button>
|
|
131
|
+
{/if}
|
|
132
|
+
</div>
|
|
133
|
+
{/each}
|
|
134
|
+
|
|
135
|
+
{#if hiddenCount > 0}
|
|
136
|
+
<div class="more-attachments">
|
|
137
|
+
+{hiddenCount} more file{hiddenCount !== 1 ? 's' : ''}
|
|
138
|
+
</div>
|
|
139
|
+
{/if}
|
|
140
|
+
</div>
|
|
141
|
+
|
|
142
|
+
<!-- Preview variant - larger display with remove buttons -->
|
|
143
|
+
{:else if variant === 'preview'}
|
|
144
|
+
<div class="preview-attachments">
|
|
145
|
+
{#each displayedAttachments as attachment (attachment.id)}
|
|
146
|
+
<div class="preview-attachment-item">
|
|
147
|
+
{#if isImageType(attachment.file_type) && showPreview}
|
|
148
|
+
<div class="preview-image-container">
|
|
149
|
+
<img
|
|
150
|
+
src={attachment.url}
|
|
151
|
+
alt={attachment.file_name || 'Attachment'}
|
|
152
|
+
class="preview-image"
|
|
153
|
+
loading="lazy"
|
|
154
|
+
/>
|
|
155
|
+
{#if onRemove}
|
|
156
|
+
<button
|
|
157
|
+
class="remove-button"
|
|
158
|
+
onclick={() => handleRemove(attachment)}
|
|
159
|
+
title="Remove attachment"
|
|
160
|
+
>
|
|
161
|
+
✕
|
|
162
|
+
</button>
|
|
163
|
+
{/if}
|
|
164
|
+
</div>
|
|
165
|
+
{:else if isVideoType(attachment.file_type) && showPreview}
|
|
166
|
+
<div class="preview-video-container">
|
|
167
|
+
<video
|
|
168
|
+
src={attachment.url}
|
|
169
|
+
class="preview-video"
|
|
170
|
+
controls
|
|
171
|
+
preload="metadata"
|
|
172
|
+
>
|
|
173
|
+
<track kind="captions" />
|
|
174
|
+
Your browser does not support video playback.
|
|
175
|
+
</video>
|
|
176
|
+
{#if onRemove}
|
|
177
|
+
<button
|
|
178
|
+
class="remove-button"
|
|
179
|
+
onclick={() => handleRemove(attachment)}
|
|
180
|
+
title="Remove attachment"
|
|
181
|
+
>
|
|
182
|
+
✕
|
|
183
|
+
</button>
|
|
184
|
+
{/if}
|
|
185
|
+
</div>
|
|
186
|
+
{:else}
|
|
187
|
+
<div class="preview-file-container">
|
|
188
|
+
<div class="preview-file">
|
|
189
|
+
<span class="file-icon">{getFileIcon(attachment.file_type)}</span>
|
|
190
|
+
<div class="file-info">
|
|
191
|
+
<span class="file-name">{attachment.file_name || 'Untitled'}</span>
|
|
192
|
+
{#if attachment.file_size}
|
|
193
|
+
<span class="file-size">{formatFileSize(attachment.file_size)}</span>
|
|
194
|
+
{/if}
|
|
195
|
+
</div>
|
|
196
|
+
</div>
|
|
197
|
+
{#if onRemove}
|
|
198
|
+
<button
|
|
199
|
+
class="remove-button"
|
|
200
|
+
onclick={() => handleRemove(attachment)}
|
|
201
|
+
title="Remove attachment"
|
|
202
|
+
>
|
|
203
|
+
✕
|
|
204
|
+
</button>
|
|
205
|
+
{/if}
|
|
206
|
+
</div>
|
|
207
|
+
{/if}
|
|
208
|
+
</div>
|
|
209
|
+
{/each}
|
|
210
|
+
</div>
|
|
211
|
+
|
|
212
|
+
<!-- Compact variant - minimal space usage -->
|
|
213
|
+
{:else if variant === 'compact'}
|
|
214
|
+
<div class="compact-attachments">
|
|
215
|
+
{#each displayedAttachments as attachment (attachment.id)}
|
|
216
|
+
<button
|
|
217
|
+
class="compact-attachment-item"
|
|
218
|
+
onclick={() => handleView(attachment)}
|
|
219
|
+
title="{attachment.file_name || 'Untitled'} ({formatFileSize(attachment.file_size)})"
|
|
220
|
+
>
|
|
221
|
+
<span class="file-icon">{getFileIcon(attachment.file_type)}</span>
|
|
222
|
+
<span class="file-name">{attachment.file_name || 'File'}</span>
|
|
223
|
+
</button>
|
|
224
|
+
{/each}
|
|
225
|
+
|
|
226
|
+
{#if hiddenCount > 0}
|
|
227
|
+
<span class="more-count">+{hiddenCount}</span>
|
|
228
|
+
{/if}
|
|
229
|
+
</div>
|
|
230
|
+
{/if}
|
|
231
|
+
</div>
|
|
232
|
+
{/if}
|
|
233
|
+
|
|
234
|
+
<!-- Lightbox overlay for images and PDFs -->
|
|
235
|
+
{#if lightboxAttachment}
|
|
236
|
+
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
|
237
|
+
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
238
|
+
<div class="lightbox-overlay" onclick={closeLightbox}>
|
|
239
|
+
<button class="lightbox-close" onclick={closeLightbox} title="Close">✕</button>
|
|
240
|
+
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
|
241
|
+
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
242
|
+
<div class="lightbox-content" onclick={(e) => e.stopPropagation()}>
|
|
243
|
+
{#if isImageType(lightboxAttachment.file_type)}
|
|
244
|
+
<img
|
|
245
|
+
src={lightboxAttachment.url}
|
|
246
|
+
alt={lightboxAttachment.file_name || 'Attachment'}
|
|
247
|
+
class="lightbox-image"
|
|
248
|
+
/>
|
|
249
|
+
{:else if lightboxAttachment.file_type.includes('pdf')}
|
|
250
|
+
<iframe
|
|
251
|
+
src={lightboxAttachment.url}
|
|
252
|
+
class="lightbox-pdf"
|
|
253
|
+
title={lightboxAttachment.file_name || 'PDF Document'}
|
|
254
|
+
></iframe>
|
|
255
|
+
{/if}
|
|
256
|
+
{#if lightboxAttachment.file_name}
|
|
257
|
+
<div class="lightbox-caption">{lightboxAttachment.file_name}</div>
|
|
258
|
+
{/if}
|
|
259
|
+
</div>
|
|
260
|
+
</div>
|
|
261
|
+
{/if}
|
|
262
|
+
|
|
263
|
+
<style>
|
|
264
|
+
.attachment-display {
|
|
265
|
+
width: 100%;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/* Message variant styles */
|
|
269
|
+
.message-attachments {
|
|
270
|
+
display: flex;
|
|
271
|
+
flex-wrap: wrap;
|
|
272
|
+
gap: 8px;
|
|
273
|
+
margin: 0;
|
|
274
|
+
padding: 8px 12px 12px;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
.message-attachment-item {
|
|
278
|
+
margin: 0;
|
|
279
|
+
padding: 0;
|
|
280
|
+
flex: 0 0 auto;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
.message-attachment-item .attachment-image {
|
|
284
|
+
width: auto;
|
|
285
|
+
height: 120px;
|
|
286
|
+
max-width: 200px;
|
|
287
|
+
max-height: 120px;
|
|
288
|
+
border-radius: 0.5rem;
|
|
289
|
+
cursor: pointer;
|
|
290
|
+
object-fit: cover;
|
|
291
|
+
transition: opacity 0.2s;
|
|
292
|
+
display: block;
|
|
293
|
+
margin: 0;
|
|
294
|
+
padding: 0;
|
|
295
|
+
border: 1px solid rgba(255, 255, 255, 0.1);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
.message-attachment-item .attachment-image:hover {
|
|
299
|
+
opacity: 0.9;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
.message-attachment-item .attachment-video {
|
|
303
|
+
width: 175px;
|
|
304
|
+
height: auto;
|
|
305
|
+
border-radius: 0.5rem;
|
|
306
|
+
border: 1px solid rgba(255, 255, 255, 0.1);
|
|
307
|
+
margin: 0;
|
|
308
|
+
padding: 0;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
.message-attachment-item .attachment-file {
|
|
312
|
+
display: flex;
|
|
313
|
+
align-items: center;
|
|
314
|
+
gap: 0.5rem;
|
|
315
|
+
padding: 0.5rem;
|
|
316
|
+
background: rgba(0, 0, 0, 0.1);
|
|
317
|
+
border: none;
|
|
318
|
+
border-radius: 0.375rem;
|
|
319
|
+
cursor: pointer;
|
|
320
|
+
transition: background-color 0.2s;
|
|
321
|
+
text-align: left;
|
|
322
|
+
width: 100%;
|
|
323
|
+
color: inherit;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
.message-attachment-item .attachment-file:hover {
|
|
327
|
+
background: rgba(0, 0, 0, 0.15);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
.more-attachments {
|
|
331
|
+
font-size: 0.75rem;
|
|
332
|
+
color: rgba(255, 255, 255, 0.7);
|
|
333
|
+
padding: 0.25rem 0.5rem;
|
|
334
|
+
background: rgba(0, 0, 0, 0.1);
|
|
335
|
+
border-radius: 0.25rem;
|
|
336
|
+
text-align: center;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/* Preview variant styles */
|
|
340
|
+
.preview-attachments {
|
|
341
|
+
display: flex;
|
|
342
|
+
flex-wrap: wrap;
|
|
343
|
+
gap: 0.75rem;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
.preview-attachment-item {
|
|
347
|
+
position: relative;
|
|
348
|
+
flex: 0 0 auto;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
.preview-image-container,
|
|
352
|
+
.preview-video-container,
|
|
353
|
+
.preview-file-container {
|
|
354
|
+
position: relative;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
.preview-image {
|
|
358
|
+
width: 4rem;
|
|
359
|
+
height: 4rem;
|
|
360
|
+
object-fit: cover;
|
|
361
|
+
border-radius: 0.5rem;
|
|
362
|
+
border: 1px solid #e5e7eb;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
.preview-video {
|
|
366
|
+
width: 6rem;
|
|
367
|
+
height: 4rem;
|
|
368
|
+
border-radius: 0.5rem;
|
|
369
|
+
border: 1px solid #e5e7eb;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
.preview-file {
|
|
373
|
+
display: flex;
|
|
374
|
+
align-items: center;
|
|
375
|
+
gap: 0.5rem;
|
|
376
|
+
padding: 0.75rem;
|
|
377
|
+
background: white;
|
|
378
|
+
border: 1px solid #e5e7eb;
|
|
379
|
+
border-radius: 0.5rem;
|
|
380
|
+
min-width: 8rem;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
.remove-button {
|
|
384
|
+
position: absolute;
|
|
385
|
+
top: -0.25rem;
|
|
386
|
+
right: -0.25rem;
|
|
387
|
+
width: 1.25rem;
|
|
388
|
+
height: 1.25rem;
|
|
389
|
+
background: #ef4444;
|
|
390
|
+
color: white;
|
|
391
|
+
border: none;
|
|
392
|
+
border-radius: 50%;
|
|
393
|
+
cursor: pointer;
|
|
394
|
+
font-size: 0.75rem;
|
|
395
|
+
line-height: 1;
|
|
396
|
+
display: flex;
|
|
397
|
+
align-items: center;
|
|
398
|
+
justify-content: center;
|
|
399
|
+
transition: background-color 0.2s;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
.remove-button:hover {
|
|
403
|
+
background: #dc2626;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
/* Compact variant styles */
|
|
407
|
+
.compact-attachments {
|
|
408
|
+
display: flex;
|
|
409
|
+
flex-wrap: wrap;
|
|
410
|
+
gap: 0.25rem;
|
|
411
|
+
align-items: center;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
.compact-attachment-item {
|
|
415
|
+
display: flex;
|
|
416
|
+
align-items: center;
|
|
417
|
+
gap: 0.25rem;
|
|
418
|
+
padding: 0.25rem 0.5rem;
|
|
419
|
+
background: #f3f4f6;
|
|
420
|
+
border: 1px solid #e5e7eb;
|
|
421
|
+
border-radius: 0.375rem;
|
|
422
|
+
font-size: 0.75rem;
|
|
423
|
+
cursor: pointer;
|
|
424
|
+
transition: background-color 0.2s;
|
|
425
|
+
color: #374151;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
.compact-attachment-item:hover {
|
|
429
|
+
background: #e5e7eb;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
.more-count {
|
|
433
|
+
font-size: 0.75rem;
|
|
434
|
+
color: #6b7280;
|
|
435
|
+
padding: 0.25rem;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
/* Common styles */
|
|
439
|
+
.file-icon {
|
|
440
|
+
font-size: 1rem;
|
|
441
|
+
flex-shrink: 0;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
.file-info {
|
|
445
|
+
display: flex;
|
|
446
|
+
flex-direction: column;
|
|
447
|
+
gap: 0.125rem;
|
|
448
|
+
min-width: 0;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
.file-name {
|
|
452
|
+
font-weight: 500;
|
|
453
|
+
font-size: 0.875rem;
|
|
454
|
+
overflow: hidden;
|
|
455
|
+
text-overflow: ellipsis;
|
|
456
|
+
white-space: nowrap;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
.file-size {
|
|
460
|
+
font-size: 0.75rem;
|
|
461
|
+
opacity: 0.7;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
/* Responsive adjustments */
|
|
465
|
+
@media (max-width: 640px) {
|
|
466
|
+
.preview-attachments {
|
|
467
|
+
gap: 0.5rem;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
.preview-image,
|
|
471
|
+
.preview-video {
|
|
472
|
+
width: 3rem;
|
|
473
|
+
height: 3rem;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
.preview-file {
|
|
477
|
+
min-width: 6rem;
|
|
478
|
+
padding: 0.5rem;
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
/* Lightbox overlay */
|
|
483
|
+
.lightbox-overlay {
|
|
484
|
+
position: fixed;
|
|
485
|
+
inset: 0;
|
|
486
|
+
z-index: 9999;
|
|
487
|
+
display: flex;
|
|
488
|
+
align-items: center;
|
|
489
|
+
justify-content: center;
|
|
490
|
+
background: rgba(0, 0, 0, 0.85);
|
|
491
|
+
backdrop-filter: blur(4px);
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
.lightbox-close {
|
|
495
|
+
position: absolute;
|
|
496
|
+
top: 16px;
|
|
497
|
+
right: 16px;
|
|
498
|
+
width: 36px;
|
|
499
|
+
height: 36px;
|
|
500
|
+
border-radius: 50%;
|
|
501
|
+
border: none;
|
|
502
|
+
background: rgba(255, 255, 255, 0.15);
|
|
503
|
+
color: white;
|
|
504
|
+
font-size: 18px;
|
|
505
|
+
cursor: pointer;
|
|
506
|
+
display: flex;
|
|
507
|
+
align-items: center;
|
|
508
|
+
justify-content: center;
|
|
509
|
+
transition: background 0.2s;
|
|
510
|
+
z-index: 10;
|
|
511
|
+
}
|
|
512
|
+
.lightbox-close:hover {
|
|
513
|
+
background: rgba(255, 255, 255, 0.3);
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
.lightbox-content {
|
|
517
|
+
display: flex;
|
|
518
|
+
flex-direction: column;
|
|
519
|
+
align-items: center;
|
|
520
|
+
gap: 12px;
|
|
521
|
+
max-width: 90vw;
|
|
522
|
+
max-height: 90vh;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
.lightbox-image {
|
|
526
|
+
max-width: 90vw;
|
|
527
|
+
max-height: 85vh;
|
|
528
|
+
object-fit: contain;
|
|
529
|
+
border-radius: 8px;
|
|
530
|
+
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.5);
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
.lightbox-pdf {
|
|
534
|
+
width: 80vw;
|
|
535
|
+
height: 85vh;
|
|
536
|
+
border: none;
|
|
537
|
+
border-radius: 8px;
|
|
538
|
+
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.5);
|
|
539
|
+
background: white;
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
.lightbox-caption {
|
|
543
|
+
color: rgba(255, 255, 255, 0.7);
|
|
544
|
+
font-size: 0.875rem;
|
|
545
|
+
text-align: center;
|
|
546
|
+
}
|
|
547
|
+
</style>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { ContentItemOutput } from '../types/message.js';
|
|
2
|
+
interface Props {
|
|
3
|
+
attachments: ContentItemOutput[];
|
|
4
|
+
variant?: 'message' | 'preview' | 'compact';
|
|
5
|
+
showPreview?: boolean;
|
|
6
|
+
maxDisplayCount?: number;
|
|
7
|
+
onRemove?: (attachment: ContentItemOutput) => void;
|
|
8
|
+
onView?: (attachment: ContentItemOutput) => void;
|
|
9
|
+
}
|
|
10
|
+
declare const AttachmentDisplay: import("svelte").Component<Props, {}, "">;
|
|
11
|
+
type AttachmentDisplay = ReturnType<typeof AttachmentDisplay>;
|
|
12
|
+
export default AttachmentDisplay;
|