ChatAttachmentThumbnailFile.svelte 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  1. <script lang="ts">
  2. import { RemoveButton } from '$lib/components/app';
  3. import { formatFileSize, getFileTypeLabel, getPreviewText } from '$lib/utils/file-preview';
  4. import { FileTypeCategory, MimeTypeText } from '$lib/enums/files';
  5. interface Props {
  6. class?: string;
  7. id: string;
  8. onClick?: (event?: MouseEvent) => void;
  9. onRemove?: (id: string) => void;
  10. name: string;
  11. readonly?: boolean;
  12. size?: number;
  13. textContent?: string;
  14. type: string;
  15. }
  16. let {
  17. class: className = '',
  18. id,
  19. onClick,
  20. onRemove,
  21. name,
  22. readonly = false,
  23. size,
  24. textContent,
  25. type
  26. }: Props = $props();
  27. </script>
  28. {#if type === MimeTypeText.PLAIN || type === FileTypeCategory.TEXT}
  29. {#if readonly}
  30. <!-- Readonly mode (ChatMessage) -->
  31. <button
  32. class="cursor-pointer rounded-lg border border-border bg-muted p-3 transition-shadow hover:shadow-md {className} w-full max-w-2xl"
  33. onclick={onClick}
  34. aria-label={`Preview ${name}`}
  35. type="button"
  36. >
  37. <div class="flex items-start gap-3">
  38. <div class="flex min-w-0 flex-1 flex-col items-start text-left">
  39. <span class="w-full truncate text-sm font-medium text-foreground">{name}</span>
  40. {#if size}
  41. <span class="text-xs text-muted-foreground">{formatFileSize(size)}</span>
  42. {/if}
  43. {#if textContent && type === 'text'}
  44. <div class="relative mt-2 w-full">
  45. <div
  46. class="overflow-hidden font-mono text-xs leading-relaxed break-words whitespace-pre-wrap text-muted-foreground"
  47. >
  48. {getPreviewText(textContent)}
  49. </div>
  50. {#if textContent.length > 150}
  51. <div
  52. class="pointer-events-none absolute right-0 bottom-0 left-0 h-6 bg-gradient-to-t from-muted to-transparent"
  53. ></div>
  54. {/if}
  55. </div>
  56. {/if}
  57. </div>
  58. </div>
  59. </button>
  60. {:else}
  61. <!-- Non-readonly mode (ChatForm) -->
  62. <button
  63. class="group relative rounded-lg border border-border bg-muted p-3 {className} {textContent
  64. ? 'max-h-24 max-w-72'
  65. : 'max-w-36'} cursor-pointer text-left"
  66. onclick={onClick}
  67. >
  68. <div class="absolute top-2 right-2 opacity-0 transition-opacity group-hover:opacity-100">
  69. <RemoveButton {id} {onRemove} />
  70. </div>
  71. <div class="pr-8">
  72. <span class="mb-3 block truncate text-sm font-medium text-foreground">{name}</span>
  73. {#if textContent}
  74. <div class="relative">
  75. <div
  76. class="overflow-hidden font-mono text-xs leading-relaxed break-words whitespace-pre-wrap text-muted-foreground"
  77. style="max-height: 3rem; line-height: 1.2em;"
  78. >
  79. {getPreviewText(textContent)}
  80. </div>
  81. {#if textContent.length > 150}
  82. <div
  83. class="pointer-events-none absolute right-0 bottom-0 left-0 h-4 bg-gradient-to-t from-muted to-transparent"
  84. ></div>
  85. {/if}
  86. </div>
  87. {/if}
  88. </div>
  89. </button>
  90. {/if}
  91. {:else}
  92. <button
  93. class="group flex items-center gap-3 rounded-lg border border-border bg-muted p-3 {className} relative"
  94. onclick={onClick}
  95. >
  96. <div
  97. class="flex h-8 w-8 items-center justify-center rounded bg-primary/10 text-xs font-medium text-primary"
  98. >
  99. {getFileTypeLabel(type)}
  100. </div>
  101. <div class="flex flex-col gap-1">
  102. <span
  103. class="max-w-24 truncate text-sm font-medium text-foreground group-hover:pr-6 md:max-w-32"
  104. >
  105. {name}
  106. </span>
  107. {#if size}
  108. <span class="text-left text-xs text-muted-foreground">{formatFileSize(size)}</span>
  109. {/if}
  110. </div>
  111. {#if !readonly}
  112. <div class="absolute top-2 right-2 opacity-0 transition-opacity group-hover:opacity-100">
  113. <RemoveButton {id} {onRemove} />
  114. </div>
  115. {/if}
  116. </button>
  117. {/if}