|
3 | 3 | import { X } from '@lucide/svelte'; |
4 | 4 |
|
5 | 5 | interface Props { |
| 6 | + class?: string; |
6 | 7 | id: string; |
| 8 | + onClick?: () => void; |
| 9 | + onRemove?: (id: string) => void; |
7 | 10 | name: string; |
8 | | - type: string; |
9 | | - size?: number; |
10 | 11 | readonly?: boolean; |
11 | | - onRemove?: (id: string) => void; |
12 | | - class?: string; |
| 12 | + size?: number; |
13 | 13 | textContent?: string; |
14 | | - onClick?: () => void; |
| 14 | + type: string; |
15 | 15 | } |
16 | 16 |
|
17 | 17 | let { |
| 18 | + class: className = '', |
18 | 19 | id, |
| 20 | + onClick, |
| 21 | + onRemove, |
19 | 22 | name, |
20 | | - type, |
21 | | - size, |
22 | 23 | readonly = false, |
23 | | - onRemove, |
24 | | - class: className = '', |
| 24 | + size, |
25 | 25 | textContent, |
26 | | - onClick |
| 26 | + type, |
27 | 27 | }: Props = $props(); |
28 | 28 |
|
29 | 29 | function formatFileSize(bytes: number): string { |
|
39 | 39 | } |
40 | 40 |
|
41 | 41 | function getPreviewText(content: string): string { |
42 | | - // Get first 150 characters for preview |
43 | 42 | return content.length > 150 ? content.substring(0, 150) : content; |
44 | 43 | } |
45 | 44 | </script> |
46 | 45 |
|
47 | 46 | {#if type === 'text/plain' || type === 'text'} |
48 | 47 | {#if readonly} |
| 48 | + <!-- Readonly mode (ChatMessage) --> |
49 | 49 | <button |
50 | 50 | class="bg-muted border-border cursor-pointer rounded-lg border p-3 transition-shadow hover:shadow-md {className} w-full max-w-2xl" |
51 | 51 | onclick={onClick} |
|
55 | 55 | <div class="flex items-start gap-3"> |
56 | 56 | <div class="flex min-w-0 flex-1 flex-col items-start text-left"> |
57 | 57 | <span class="text-foreground w-full truncate text-sm font-medium">{name}</span> |
| 58 | + |
58 | 59 | {#if size} |
59 | 60 | <span class="text-muted-foreground text-xs">{formatFileSize(size)}</span> |
60 | 61 | {/if} |
| 62 | + |
61 | 63 | {#if textContent && type === 'text'} |
62 | 64 | <div class="relative mt-2 w-full"> |
63 | 65 | <div |
64 | 66 | class="text-muted-foreground overflow-hidden whitespace-pre-wrap break-words font-mono text-xs leading-relaxed" |
65 | 67 | > |
66 | 68 | {getPreviewText(textContent)} |
67 | 69 | </div> |
| 70 | + |
68 | 71 | {#if textContent.length > 150} |
69 | 72 | <div |
70 | 73 | class="from-muted pointer-events-none absolute bottom-0 left-0 right-0 h-6 bg-gradient-to-t to-transparent" |
|
78 | 81 | {:else} |
79 | 82 | <!-- Non-readonly mode (ChatForm) --> |
80 | 83 | <div class="bg-muted border-border relative rounded-lg border p-3 {className} w-64"> |
81 | | - <!-- Remove button in top-right corner --> |
82 | 84 | <Button |
83 | 85 | type="button" |
84 | 86 | variant="ghost" |
|
90 | 92 | <X class="h-3 w-3" /> |
91 | 93 | </Button> |
92 | 94 |
|
93 | | - <!-- Content --> |
94 | 95 | <div class="pr-8"> |
95 | | - <!-- Add right padding to avoid overlap with X button --> |
96 | 96 | <span class="text-foreground mb-3 block truncate text-sm font-medium">{name}</span> |
97 | 97 |
|
98 | 98 | {#if textContent} |
|
103 | 103 | > |
104 | 104 | {getPreviewText(textContent)} |
105 | 105 | </div> |
| 106 | + |
106 | 107 | {#if textContent.length > 150} |
107 | 108 | <div |
108 | 109 | class="from-muted pointer-events-none absolute bottom-0 left-0 right-0 h-4 bg-gradient-to-t to-transparent" |
|
120 | 121 | > |
121 | 122 | {getFileTypeLabel(type)} |
122 | 123 | </div> |
| 124 | + |
123 | 125 | <div class="flex flex-col"> |
124 | 126 | <span class="text-foreground max-w-36 truncate text-sm font-medium md:max-w-72" |
125 | 127 | >{name}</span |
|
128 | 130 | <span class="text-muted-foreground text-xs">{formatFileSize(size)}</span> |
129 | 131 | {/if} |
130 | 132 | </div> |
| 133 | + |
131 | 134 | {#if !readonly} |
132 | 135 | <Button |
133 | 136 | type="button" |
|
0 commit comments