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 }),