+ file.thumbUrl || file.url || (file.originFileObj ? URL.createObjectURL(file.originFileObj) : undefined)
+
+const DOC_ICONS: [string[], string][] = [
+ [['pdf'], "rb:bg-[url('@/assets/images/file/pdf.svg')]"],
+ [['excel', 'spreadsheetml.sheet', 'xls', 'xlsx'], "rb:bg-[url('@/assets/images/file/excel.svg')]"],
+ [['csv'], "rb:bg-[url('@/assets/images/file/csv.svg')]"],
+ [['html'], "rb:bg-[url('@/assets/images/file/html.svg')]"],
+ [['json'], "rb:bg-[url('@/assets/images/file/json.svg')]"],
+ [['ppt'], "rb:bg-[url('@/assets/images/file/ppt.svg')]"],
+ [['markdown'], "rb:bg-[url('@/assets/images/file/md.svg')]"],
+ [['text'], "rb:bg-[url('@/assets/images/file/txt.svg')]"],
+ [['doc', 'docx', 'word', 'wordprocessingml.document'], "rb:bg-[url('@/assets/images/file/word.svg')]"],
+]
+
+const getDocIcon = (parts: string[]) => {
+ const match = DOC_ICONS.find(([keys]) => keys.some(k => parts.includes(k)))
+ return match ? match[1] : "rb:bg-[url('@/assets/images/file/txt.svg')]"
+}
+
+interface MessageFilesProps {
+ files: any[]
+ contentClassNames?: string | Record
+ onDownload: (file: any) => void
+}
+
+const MessageFiles = ({ files, contentClassNames, onDownload }: MessageFilesProps) => {
+ if (!files?.length) return null
+ return (
+
+ {files.map((file) => {
+ const key = file.url || file.uid
+ if (file.type.includes('image')) {
+ return (
+
+
+
+ )
+ }
+ if (file.type.includes('video')) {
+ return (
+
+
+
+ )
+ }
+ if (file.type.includes('audio')) {
+ return (
+
+ )
+ }
+ const documentType = (file.file_type || file.type)?.split('/') ?? []
+ return (
+ onDownload(file)}
+ >
+
+
+
{file.name}
+
+ {documentType?.[documentType.length - 1]} · {file.size}
+
+
+
+ )
+ })}
+
+ )
+}
+
+export default MessageFiles
diff --git a/web/src/components/OverflowTags/index.tsx b/web/src/components/OverflowTags/index.tsx
index 9ad9cd92..82fdb2c9 100644
--- a/web/src/components/OverflowTags/index.tsx
+++ b/web/src/components/OverflowTags/index.tsx
@@ -3,14 +3,14 @@ import { Popover, type PopoverProps } from 'antd'
import Tag, { type TagProps } from '@/components/Tag'
interface OverflowTagsProps {
- items: ReactNode[];
+ items?: ReactNode[];
gap?: number;
numTagColor?: TagProps['color'];
numTag?: (num?: number) => ReactNode;
popoverProps?: PopoverProps | false;
}
-const OverflowTags = ({ items, gap = 8, numTagColor = 'default', numTag, popoverProps }: OverflowTagsProps) => {
+const OverflowTags = ({ items = [], gap = 8, numTagColor = 'default', numTag, popoverProps }: OverflowTagsProps) => {
const containerRef = useRef(null)
const measureRef = useRef(null)
const [visibleCount, setVisibleCount] = useState(items.length)
@@ -20,7 +20,7 @@ const OverflowTags = ({ items, gap = 8, numTagColor = 'default', numTag, popover
if (!measure || containerWidth === 0) return
const children = Array.from(measure.children) as HTMLElement[]
- if (!children.length) return
+ if (!children.length) { setVisibleCount(0); return }
// last child is the sample +N tag
const extraTagWidth = (children[children.length - 1] as HTMLElement).offsetWidth
diff --git a/web/src/components/SiderMenu/index.tsx b/web/src/components/SiderMenu/index.tsx
index e1d7e596..c0698389 100644
--- a/web/src/components/SiderMenu/index.tsx
+++ b/web/src/components/SiderMenu/index.tsx
@@ -399,7 +399,7 @@ const Menu: FC<{
className="rb:overflow-y-auto rb:flex-1!"
/>
{/* Return to space button for superusers */}
- {user?.is_superuser && source === 'space' &&
+ {source === 'space' &&
{collapsed ? null : t('common.switchSpace')}
-