Skip to content

Commit 592bf9e

Browse files
committed
Better UI/UX feedback for uploaded pdf
1 parent 9385e4f commit 592bf9e

File tree

7 files changed

+114
-38
lines changed

7 files changed

+114
-38
lines changed

src/lib/components/UploadBtn.svelte

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,26 @@
11
<script lang="ts">
2-
import { PdfUploadStatus } from "$lib/types/PdfChat";
2+
import { PdfUploadStatus, type PdfUpload } from "$lib/types/PdfChat";
33
import { createEventDispatcher, onDestroy } from "svelte";
44
import CarbonUpload from "~icons/carbon/upload";
5+
import CarbonCheckmark from "~icons/carbon/checkmark";
56
67
export let classNames = "";
78
export let multimodal = false;
89
export let files: File[];
9-
export let uploadPdfStatus: PdfUploadStatus | undefined = undefined;
10+
export let pdfUpload: PdfUpload | undefined = undefined;
1011
const accept = multimodal ? "image/*,.pdf" : ".pdf";
1112
const label = multimodal ? "Upload image or PDF" : "Upload PDF";
1213
let fileInput: HTMLInputElement;
1314
let interval: ReturnType<typeof setInterval>;
1415
15-
$: uploading = uploadPdfStatus === PdfUploadStatus.Uploading;
16+
$: pdfUploading = pdfUpload?.status === PdfUploadStatus.Uploading;
1617
$: {
17-
if (uploadPdfStatus === PdfUploadStatus.Uploaded) {
18+
if (pdfUpload?.status === PdfUploadStatus.Uploaded) {
1819
interval = setInterval(() => {
19-
uploadPdfStatus = PdfUploadStatus.Ready;
20+
if (!pdfUpload) {
21+
return;
22+
}
23+
pdfUpload.status = PdfUploadStatus.Ready;
2024
}, 1500);
2125
}
2226
}
@@ -34,7 +38,7 @@
3438
if (file?.type === "application/pdf") {
3539
// pdf upload
3640
dispatch("uploadpdf", file);
37-
} else if(multimodal && file?.type.startsWith("image")){
41+
} else if (multimodal && file?.type.startsWith("image")) {
3842
// image files for multimodal models
3943
files = Array.from(fileInput.files);
4044
}
@@ -49,23 +53,25 @@
4953

5054
<button
5155
class="btn relative h-8 rounded-lg border bg-white px-3 py-1 text-sm text-gray-500 shadow-sm transition-all hover:bg-gray-100 dark:border-gray-600 dark:bg-gray-700 dark:text-gray-300 dark:hover:bg-gray-600 {classNames}"
52-
class:animate-pulse={uploading}
53-
class:pointer-events-none={uploading}
56+
class:animate-pulse={pdfUploading}
57+
class:pointer-events-none={pdfUploading || pdfUpload?.status === PdfUploadStatus.Uploaded}
5458
>
5559
<input
5660
bind:this={fileInput}
5761
on:change={onChange}
5862
class="absolute w-full cursor-pointer opacity-0"
5963
type="file"
6064
{accept}
61-
disabled={uploading}
65+
disabled={pdfUploading}
6266
/>
63-
<CarbonUpload class="mr-2 text-xs " />
64-
{#if uploadPdfStatus === PdfUploadStatus.Uploaded}
65-
PDF Uploaded ✅
66-
{:else if uploading}
67-
Processing PDF file
67+
{#if pdfUpload?.status !== PdfUploadStatus.Uploaded}
68+
<CarbonUpload class="text-xs" />
6869
{:else}
69-
{label}
70+
<CarbonCheckmark class="text-xs text-green-500" />
71+
{/if}
72+
{#if multimodal || !pdfUpload?.name}
73+
<div class="ml-2">
74+
{label}
75+
</div>
7076
{/if}
7177
</button>
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<script lang="ts">
2+
import { PdfUploadStatus, type PdfUpload } from "$lib/types/PdfChat";
3+
import CarbonDocumentBlank from "~icons/carbon/document-blank";
4+
import CarbonClose from "~icons/carbon/close";
5+
import { createEventDispatcher } from "svelte";
6+
7+
export let pdfUpload: PdfUpload;
8+
9+
const dispatch = createEventDispatcher<{
10+
deletepdf: void;
11+
}>();
12+
13+
$: uploading = pdfUpload.status === PdfUploadStatus.Uploading;
14+
</script>
15+
16+
<div
17+
class="max-w-48 group flex items-center gap-x-1"
18+
class:animate-pulse={uploading}
19+
class:pointer-events-none={uploading}
20+
title={pdfUpload.name}
21+
>
22+
<button
23+
class="-mr-1 block md:hidden shrink-0 opacity-70 group-hover:block"
24+
on:click={() => dispatch("deletepdf")}><CarbonClose /></button
25+
>
26+
<CarbonDocumentBlank class="shrink-0" />
27+
<p class="truncate hidden md:block">{pdfUpload.name}</p>
28+
</div>

src/lib/components/chat/ChatWindow.svelte

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@
2424
import UploadBtn from "../UploadBtn.svelte";
2525
import file2base64 from "$lib/utils/file2base64";
2626
import { useSettingsStore } from "$lib/stores/settings";
27-
import type { PdfUploadStatus } from "$lib/types/PdfChat";
27+
import type { PdfUpload } from "$lib/types/PdfChat";
28+
import UploadedPdfStatus from "../UploadedPdfStatus.svelte";
2829
2930
export let messages: Message[] = [];
3031
export let loading = false;
@@ -35,7 +36,7 @@
3536
export let RAGMessages: RAGUpdate[] = [];
3637
export let preprompt: string | undefined = undefined;
3738
export let files: File[] = [];
38-
export let uploadPdfStatus: PdfUploadStatus | undefined = undefined;
39+
export let pdfUpload: PdfUpload | undefined = undefined;
3940
4041
$: isReadOnly = !models.some((model) => model.id === currentModel.id);
4142
@@ -176,13 +177,12 @@
176177
})}
177178
/>
178179
{/if}
179-
<UploadBtn
180-
bind:files
181-
on:uploadpdf
182-
classNames="ml-auto"
183-
multimodal={currentModel.multimodal}
184-
{uploadPdfStatus}
185-
/>
180+
<div class="ml-auto flex items-center gap-x-3">
181+
{#if pdfUpload?.name}
182+
<UploadedPdfStatus on:deletepdf {pdfUpload} />
183+
{/if}
184+
<UploadBtn bind:files on:uploadpdf multimodal={currentModel.multimodal} {pdfUpload} />
185+
</div>
186186
</div>
187187
<form
188188
on:dragover={onDragOver}

src/lib/server/files/uploadFile.ts

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,22 +21,27 @@ export async function uploadImgFile(file: Blob, conv: Conversation): Promise<str
2121
});
2222
}
2323

24-
export async function uploadPdfEmbeddings(
25-
embeddings: Tensor,
26-
textChunks: string[],
27-
conv: Conversation
28-
): Promise<void> {
29-
const filename = `${conv._id}-pdf`;
30-
24+
export async function deleteFile(filename: string){
3125
// Step 1: Check if the file exists
3226
const existingFile = await collections.files.findOne({ filename });
3327

3428
// Step 2: Delete the existing file if it exists
3529
if (existingFile) {
3630
await collections.bucket.delete(existingFile._id);
3731
}
32+
}
33+
34+
export async function uploadPdfEmbeddings(
35+
embeddings: Tensor,
36+
textChunks: string[],
37+
conv: Conversation
38+
): Promise<void> {
39+
const filename = `${conv._id}-pdf`;
40+
41+
// Step 1: Delete the existing file if it exists
42+
await deleteFile(filename);
3843

39-
// Step 3: Upload the new file
44+
// Step 2: Upload the new file
4045
const upload = collections.bucket.openUploadStream(filename, {
4146
metadata: { conversation: conv._id.toString(), textChunks, dims: embeddings.dims },
4247
});

src/lib/types/PdfChat.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,8 @@ export enum PdfUploadStatus {
1212
Uploading = "Uploading",
1313
Uploaded = "Uploaded",
1414
}
15+
16+
export interface PdfUpload {
17+
status: PdfUploadStatus;
18+
name: string;
19+
}

src/routes/conversation/[id]/+page.svelte

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
import type { MessageUpdate, RAGUpdate } from "$lib/types/MessageUpdate";
1717
import titleUpdate from "$lib/stores/titleUpdate";
1818
import file2base64 from "$lib/utils/file2base64";
19-
import { PdfUploadStatus } from "$lib/types/PdfChat.js";
19+
import { PdfUploadStatus, type PdfUpload } from "$lib/types/PdfChat.js";
2020
export let data;
2121
2222
let messages = data.messages;
@@ -32,7 +32,7 @@
3232
3333
let loading = false;
3434
let pending = false;
35-
let uploadPdfStatus: PdfUploadStatus;
35+
let pdfUpload: PdfUpload | undefined = undefined;
3636
3737
let files: File[] = [];
3838
@@ -275,7 +275,7 @@
275275
}
276276
277277
async function uploadPdf(file: File) {
278-
uploadPdfStatus = PdfUploadStatus.Uploading;
278+
pdfUpload = { status: PdfUploadStatus.Uploading, name: file.name };
279279
280280
const formData = new FormData();
281281
formData.append("pdf", file);
@@ -290,7 +290,20 @@
290290
console.error("Error while uploading PDF: " + (await res.text()));
291291
}
292292
293-
uploadPdfStatus = PdfUploadStatus.Uploaded;
293+
pdfUpload.status = PdfUploadStatus.Uploaded;
294+
}
295+
296+
async function deletePdf() {
297+
const res = await fetch(`${base}/conversation/${$page.params.id}/upload-pdf`, {
298+
method: "DELETE",
299+
});
300+
301+
if (!res.ok) {
302+
error.set("Error while deleting PDF, try again.");
303+
console.error("Error while deleting PDF: " + (await res.text()));
304+
}
305+
306+
pdfUpload = undefined;
294307
}
295308
296309
onMount(async () => {
@@ -359,7 +372,8 @@
359372
on:retry={onRetry}
360373
on:vote={(event) => voteMessage(event.detail.score, event.detail.id)}
361374
on:uploadpdf={(event) => uploadPdf(event.detail)}
362-
{uploadPdfStatus}
375+
on:deletepdf={deletePdf}
376+
{pdfUpload}
363377
on:share={() => shareConversation($page.params.id, data.title)}
364378
on:stop={() => (($isAborted = true), (loading = false))}
365379
models={data.models}

src/routes/conversation/[id]/upload-pdf/+server.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { authCondition } from "$lib/server/auth";
22
import { collections } from "$lib/server/database";
33
import { MAX_SEQ_LEN as CHUNK_CAR_LEN, createEmbeddings } from "$lib/server/embeddings";
4-
import { uploadPdfEmbeddings } from "$lib/server/files/uploadFile";
4+
import { deleteFile, uploadPdfEmbeddings } from "$lib/server/files/uploadFile";
55
import { chunk } from "$lib/utils/chunk";
66
import { error } from "@sveltejs/kit";
77
import { ObjectId } from "mongodb";
@@ -41,3 +41,21 @@ export async function POST({ request, params, locals }) {
4141

4242
return new Response();
4343
}
44+
45+
export async function DELETE({ params, locals }) {
46+
const conversationId = new ObjectId(params.id);
47+
const conversation = await collections.conversations.findOne({
48+
_id: conversationId,
49+
...authCondition(locals),
50+
});
51+
52+
if (!conversation) {
53+
throw error(404, "Conversation not found");
54+
}
55+
56+
const filename = `${conversation._id}-pdf`;
57+
58+
await deleteFile(filename);
59+
60+
return new Response();
61+
}

0 commit comments

Comments
 (0)