From 11852d1df121054886a0672618c3cac82230808e Mon Sep 17 00:00:00 2001 From: Jagger <634750802@qq.com> Date: Tue, 31 Dec 2024 14:27:01 +0800 Subject: [PATCH] feat(frontend): support download uploaded files (#553) close #466 --- .../chat/message-content-sources.tsx | 8 +++++--- frontend/app/src/components/chat/utils.ts | 20 ++++++++++++++++++- .../components/documents/documents-table.tsx | 19 ++++++++++++++++-- 3 files changed, 41 insertions(+), 6 deletions(-) diff --git a/frontend/app/src/components/chat/message-content-sources.tsx b/frontend/app/src/components/chat/message-content-sources.tsx index 8455a6e49..c8df02612 100644 --- a/frontend/app/src/components/chat/message-content-sources.tsx +++ b/frontend/app/src/components/chat/message-content-sources.tsx @@ -2,7 +2,7 @@ import type { ChatMessageSource } from '@/api/chats'; import { useChatMessageField, useChatMessageStreamContainsState, useChatMessageStreamState } from '@/components/chat/chat-hooks'; import { ChatMessageController } from '@/components/chat/chat-message-controller'; import { AppChatStreamState } from '@/components/chat/chat-stream-state'; -import { isNotFinished, parseSource } from '@/components/chat/utils'; +import { isNotFinished, parseHref, parseSource } from '@/components/chat/utils'; import { ScrollArea, ScrollBar } from '@/components/ui/scroll-area'; import { Skeleton } from '@/components/ui/skeleton'; import { cn } from '@/lib/utils'; @@ -72,7 +72,7 @@ function MessageContextSource ({ index, animation, context }: { index: number, a initial={animation && { x: '-30%', opacity: 0 }} animate={{ x: 0, opacity: 1 }} > - +
{context.name}
@@ -90,8 +90,10 @@ export function MessageContextSourceCard ({ title, href }: { title?: string, hre return parseSource(href); }, [href]); + const isHttp = /^https?:\/\//.test(href ?? ''); + return ( -
+
{title}
diff --git a/frontend/app/src/components/chat/utils.ts b/frontend/app/src/components/chat/utils.ts index ed81f64c3..e0a4bdd7a 100644 --- a/frontend/app/src/components/chat/utils.ts +++ b/frontend/app/src/components/chat/utils.ts @@ -1,7 +1,15 @@ +import type { ChatMessageSource } from '@/api/chats'; import type { OngoingState } from '@/components/chat/chat-message-controller'; export type { ChatEngineOptions } from '@/api/chat-engines'; +const truncateUrl = (url: string, maxLength: number = 20): string => { + if (!url || url.length <= maxLength) return url; + const start = url.substring(0, maxLength / 2); + const end = url.substring(url.length - maxLength / 2); + return `${start}...${end}`; +}; + export function parseSource (uri?: string) { if (!uri) { return 'Unknown'; @@ -9,7 +17,17 @@ export function parseSource (uri?: string) { if (/^https:\/\//.test(uri)) { return new URL(uri).hostname; } else { - return uri; + return truncateUrl(uri); + } +} + +export function parseHref (source: ChatMessageSource): { href: string, download?: string, target?: HTMLAnchorElement['target'] } { + if (/^https?:\/\//.test(source.source_uri)) { + return { href: source.source_uri, target: '_blank' }; + } else if (source.source_uri.startsWith('uploads/')) { + return { href: `/api/v1/documents/${source.id}/download`, download: source.source_uri.slice(source.source_uri.lastIndexOf('/') + 1) }; + } else { + return { href: 'javascript:void(0)' }; } } diff --git a/frontend/app/src/components/documents/documents-table.tsx b/frontend/app/src/components/documents/documents-table.tsx index ab34258e2..70cc09d54 100644 --- a/frontend/app/src/components/documents/documents-table.tsx +++ b/frontend/app/src/components/documents/documents-table.tsx @@ -7,6 +7,7 @@ import { datetime } from '@/components/cells/datetime'; import { link } from '@/components/cells/link'; import { mono } from '@/components/cells/mono'; import { DatasourceCell } from '@/components/cells/reference'; +import { parseHref } from '@/components/chat/utils'; import { DataTableRemote } from '@/components/data-table-remote'; import { DocumentPreviewDialog } from '@/components/document-viewer'; import { DocumentsTableFilters } from '@/components/documents/documents-table-filters'; @@ -14,7 +15,7 @@ import { NextLink } from '@/components/nextjs/NextLink'; import { getErrorMessage } from '@/lib/errors'; import type { CellContext, ColumnDef } from '@tanstack/react-table'; import { createColumnHelper } from '@tanstack/table-core'; -import { UploadIcon } from 'lucide-react'; +import { FileDownIcon, FileIcon, UploadIcon } from 'lucide-react'; import { useMemo, useState } from 'react'; import { toast } from 'sonner'; @@ -27,7 +28,21 @@ const truncateUrl = (url: string, maxLength: number = 30): string => { return `${start}...${end}`; }; -const href = (cell: CellContext) =>
{truncateUrl(cell.getValue())}; +const href = (cell: CellContext) => { + const url = cell.getValue(); + if (/^https?:\/\//.test(url)) { + return {url}; + } else if (url.startsWith('uploads/')) { + return ( + + + {truncateUrl(url)} + + ); + } else { + return {truncateUrl(url)}; + } +}; const getColumns = (kbId: number) => [ helper.accessor('id', { cell: mono }),