Skip to content

Commit 624aea7

Browse files
use previewcard to render documents
1 parent 954113e commit 624aea7

File tree

4 files changed

+71
-75
lines changed

4 files changed

+71
-75
lines changed

templates/types/streaming/nextjs/app/components/ui/chat/chat-message/chat-sources.tsx

Lines changed: 26 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
1-
import { Check, Copy, FileText } from "lucide-react";
2-
import Image from "next/image";
1+
import { Check, Copy } from "lucide-react";
32
import { useMemo } from "react";
43
import { Button } from "../../button";
5-
import { FileIcon } from "../../document-preview";
4+
import { PreviewCard } from "../../document-preview";
65
import {
76
HoverCard,
87
HoverCardContent,
@@ -49,13 +48,7 @@ export function ChatSources({ data }: { data: SourceData }) {
4948
);
5049
}
5150

52-
export function SourceInfo({
53-
node,
54-
index,
55-
}: {
56-
node?: SourceNode;
57-
index: number;
58-
}) {
51+
function SourceInfo({ node, index }: { node?: SourceNode; index: number }) {
5952
if (!node) return <SourceNumberButton index={index} />;
6053
return (
6154
<HoverCard>
@@ -97,55 +90,33 @@ export function SourceNumberButton({
9790
);
9891
}
9992

100-
export function DocumentInfo({ document }: { document: Document }) {
93+
export function DocumentInfo({
94+
document,
95+
className,
96+
}: {
97+
document: Document;
98+
className?: string;
99+
}) {
101100
const { url, sources } = document;
102-
let fileName: string | undefined;
103-
if (sources.length > 0) {
104-
fileName = sources[0].metadata.file_name as string | undefined;
105-
} else {
106-
// Extract filename from URL if sources is empty
107-
const urlParts = url.split("/");
108-
fileName = urlParts[urlParts.length - 1];
109-
}
110-
const fileExt = fileName?.split(".").pop();
111-
const fileImage = fileExt ? FileIcon[fileExt as DocumentFileType] : null;
101+
// Extract filename from URL
102+
const urlParts = url.split("/");
103+
const fileName = urlParts.length > 0 ? urlParts[urlParts.length - 1] : url;
104+
const fileExt = fileName?.split(".").pop() as DocumentFileType | undefined;
105+
106+
const previewFile = {
107+
filename: fileName,
108+
filetype: fileExt,
109+
};
112110

113111
const DocumentDetail = (
114-
<div
115-
key={url}
116-
className="h-28 w-48 flex flex-col justify-between p-4 border rounded-md shadow-md cursor-pointer"
117-
>
118-
<p
119-
title={fileName}
120-
className={cn(
121-
fileName ? "truncate" : "text-blue-900 break-words",
122-
"text-left",
123-
)}
124-
>
125-
{fileName ?? url}
126-
</p>
127-
<div className="flex justify-between items-center">
128-
<div className="space-x-2 flex">
129-
{sources.map((node: SourceNode, index: number) => {
130-
return (
131-
<div key={node.id}>
132-
<SourceInfo node={node} index={index} />
133-
</div>
134-
);
135-
})}
136-
</div>
137-
{fileImage ? (
138-
<div className="relative h-8 w-8 shrink-0 overflow-hidden rounded-md">
139-
<Image
140-
className="h-full w-auto"
141-
priority
142-
src={fileImage}
143-
alt="Icon"
144-
/>
112+
<div className={`relative ${className}`}>
113+
<PreviewCard className={"cursor-pointer"} file={previewFile} />
114+
<div className="absolute bottom-2 left-2 space-x-2 flex">
115+
{sources.map((node: SourceNode, index: number) => (
116+
<div key={node.id}>
117+
<SourceInfo node={node} index={index} />
145118
</div>
146-
) : (
147-
<FileText className="text-gray-500" />
148-
)}
119+
))}
149120
</div>
150121
</div>
151122
);

templates/types/streaming/nextjs/app/components/ui/chat/chat-message/markdown.tsx

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import rehypeKatex from "rehype-katex";
55
import remarkGfm from "remark-gfm";
66
import remarkMath from "remark-math";
77

8-
import { SourceData } from "..";
8+
import { DOCUMENT_FILE_TYPES, DocumentFileType, SourceData } from "..";
99
import { useClientConfig } from "../hooks/use-config";
1010
import { DocumentInfo, SourceNumberButton } from "./chat-sources";
1111
import { CodeBlock } from "./codeblock";
@@ -124,14 +124,23 @@ export default function Markdown({
124124
a({ href, children }) {
125125
// If href starts with `{backend}/api/files`, then it's a local document and we use DocumenInfo for rendering
126126
if (href?.startsWith(backend + "/api/files")) {
127-
return (
128-
<DocumentInfo
129-
document={{
130-
url: href,
131-
sources: [],
132-
}}
133-
/>
134-
);
127+
// Check if the file is document file type
128+
const fileExtension = href.split(".").pop()?.toLowerCase();
129+
130+
if (
131+
fileExtension &&
132+
DOCUMENT_FILE_TYPES.includes(fileExtension as DocumentFileType)
133+
) {
134+
return (
135+
<DocumentInfo
136+
document={{
137+
url: href,
138+
sources: [],
139+
}}
140+
className="mb-2 mt-2"
141+
/>
142+
);
143+
}
135144
}
136145
// If a text link starts with 'citation:', then render it as a citation reference
137146
if (

templates/types/streaming/nextjs/app/components/ui/chat/index.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,12 @@ export type ImageData = {
2020
};
2121

2222
export type DocumentFileType = "csv" | "pdf" | "txt" | "docx";
23+
export const DOCUMENT_FILE_TYPES: DocumentFileType[] = [
24+
"csv",
25+
"pdf",
26+
"txt",
27+
"docx",
28+
];
2329

2430
export type UploadedFileMeta = {
2531
id: string;

templates/types/streaming/nextjs/app/components/ui/document-preview.tsx

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ export function DocumentPreview(props: DocumentPreviewProps) {
3737
<Drawer direction="left">
3838
<DrawerTrigger asChild>
3939
<div>
40-
<PreviewCard {...props} />
40+
<PreviewCard className="cursor-pointer" {...props} />
4141
</div>
4242
</DrawerTrigger>
4343
<DrawerContent className="w-3/5 mt-24 h-full max-h-[96%] ">
@@ -71,31 +71,41 @@ export const FileIcon: Record<DocumentFileType, string> = {
7171
txt: TxtIcon,
7272
};
7373

74-
function PreviewCard(props: DocumentPreviewProps) {
75-
const { onRemove, file } = props;
74+
export function PreviewCard(props: {
75+
file: {
76+
filename: string;
77+
filesize?: number;
78+
filetype?: DocumentFileType;
79+
};
80+
onRemove?: () => void;
81+
className?: string;
82+
}) {
83+
const { onRemove, file, className } = props;
7684
return (
7785
<div
7886
className={cn(
7987
"p-2 w-60 max-w-60 bg-secondary rounded-lg text-sm relative",
80-
file.metadata?.refs?.length ? "" : "cursor-pointer",
88+
className,
8189
)}
8290
>
8391
<div className="flex flex-row items-center gap-2">
84-
<div className="relative h-8 w-8 shrink-0 overflow-hidden rounded-md">
92+
<div className="relative h-8 w-8 shrink-0 overflow-hidden rounded-md flex items-center justify-center">
8593
<Image
86-
className="h-full w-auto"
94+
className="h-full w-auto object-contain"
8795
priority
88-
src={FileIcon[file.filetype]}
96+
src={FileIcon[file.filetype || "txt"]}
8997
alt="Icon"
9098
/>
9199
</div>
92100
<div className="overflow-hidden">
93101
<div className="truncate font-semibold">
94-
{file.filename} ({inKB(file.filesize)} KB)
95-
</div>
96-
<div className="truncate text-token-text-tertiary flex items-center gap-2">
97-
<span>{file.filetype.toUpperCase()} File</span>
102+
{file.filename} {file.filesize ? `(${inKB(file.filesize)} KB)` : ""}
98103
</div>
104+
{file.filetype && (
105+
<div className="truncate text-token-text-tertiary flex items-center gap-2">
106+
<span>{file.filetype.toUpperCase()} File</span>
107+
</div>
108+
)}
99109
</div>
100110
</div>
101111
{onRemove && (

0 commit comments

Comments
 (0)