Skip to content

Commit d8235dd

Browse files
committed
feat: update UI to append file path to message box
1 parent bf2257f commit d8235dd

File tree

6 files changed

+84
-119
lines changed

6 files changed

+84
-119
lines changed

chat/bun.lock

Lines changed: 13 additions & 91 deletions
Large diffs are not rendered by default.

chat/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
{
22
"dependencies": {
3+
"@radix-ui/react-checkbox": "^1.3.3",
34
"@radix-ui/react-dialog": "^1.1.15",
45
"@radix-ui/react-dropdown-menu": "^2.1.14",
56
"@radix-ui/react-slot": "^1.2.2",
67
"@radix-ui/react-tabs": "^1.1.11",
7-
"@uppy/core": "^5.0.2",
8-
"@uppy/react": "^5.0.3",
98
"class-variance-authority": "^0.7.1",
109
"clsx": "^2.1.1",
1110
"jszip": "^3.10.1",
@@ -14,6 +13,7 @@
1413
"next-themes": "^0.4.6",
1514
"react": "^19.0.0",
1615
"react-dom": "^19.0.0",
16+
"react-dropzone": "^14.3.8",
1717
"react-textarea-autosize": "^8.5.9",
1818
"sonner": "^2.0.3",
1919
"tailwind-merge": "^3.3.0"

chat/src/components/chat-provider.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ interface ChatContextValue {
4747
loading: boolean;
4848
serverStatus: ServerStatus;
4949
sendMessage: (message: string, type?: MessageType) => void;
50-
uploadFiles: (formData: FormData) => void;
50+
uploadFiles: (formData: FormData) => Promise<boolean>;
5151
}
5252

5353
const ChatContext = createContext<ChatContextValue | undefined>(undefined);
@@ -270,14 +270,16 @@ export function ChatProvider({ children }: PropsWithChildren) {
270270
};
271271

272272
// Upload files to workspace
273-
const uploadFiles = async (formData: FormData) => {
273+
const uploadFiles = async (formData: FormData): Promise<boolean> => {
274+
let success = true;
274275
try{
275276
const response = await fetch(`${agentAPIUrl}/upload`, {
276277
method: 'POST',
277278
body: formData,
278279
});
279280

280281
if (!response.ok) {
282+
success = false;
281283
const errorData = await response.json();
282284
console.error("Failed to send message:", errorData);
283285
const detail = errorData.detail;
@@ -294,6 +296,7 @@ export function ChatProvider({ children }: PropsWithChildren) {
294296
}
295297
// eslint-disable-next-line @typescript-eslint/no-explicit-any
296298
} catch (error: any) {
299+
success = false;
297300
console.error("Error uploading files:", error);
298301
const detail = error.detail;
299302
const messages =
@@ -308,6 +311,7 @@ export function ChatProvider({ children }: PropsWithChildren) {
308311
description: fullDetail,
309312
});
310313
}
314+
return success;
311315
}
312316

313317

chat/src/components/chat.tsx

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,20 @@
11
"use client";
22

3-
import { useChat } from "./chat-provider";
3+
import {useChat} from "./chat-provider";
44
import MessageInput from "./message-input";
55
import MessageList from "./message-list";
6-
import {UppyContextProvider} from "@uppy/react";
7-
import {useState} from "react";
8-
import {Uppy} from "@uppy/core";
96

107
export function Chat() {
11-
const { messages, loading, sendMessage, serverStatus } = useChat();
12-
const [uppy] = useState(() => new Uppy());
8+
const {messages, loading, sendMessage, serverStatus} = useChat();
139

1410
return (
15-
<UppyContextProvider uppy={uppy}>
16-
<MessageList messages={messages} />
11+
<>
12+
<MessageList messages={messages}/>
1713
<MessageInput
1814
onSendMessage={sendMessage}
1915
disabled={loading}
2016
serverStatus={serverStatus}
2117
/>
22-
</UppyContextProvider>
18+
</>
2319
);
2420
}

chat/src/components/message-input.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,7 @@ export default function MessageInput({
284284
<UploadDialog
285285
open={uploadDialogOpen}
286286
onOpenChange={setUploadDialogOpen}
287+
setMessage={setMessage}
287288
/>
288289
</Tabs>
289290
);

chat/src/components/upload-dialog.tsx

Lines changed: 57 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,61 @@
11
"use client"
22

3-
import { useState, MouseEvent } from "react"
3+
import {useState, MouseEvent, Dispatch, SetStateAction, useRef} from "react"
44
import { useDropzone } from "react-dropzone"
55
import { Dialog, DialogContent, DialogHeader, DialogTitle } from "./ui/dialog"
66
import { Button } from "./ui/button"
77
import { Upload, X } from "lucide-react"
88
import JSZip from "jszip";
99
import { useChat } from "@/components/chat-provider";
1010
import {toast} from "sonner";
11+
import {Checkbox} from "@/components/ui/checkbox";
12+
import path from "node:path";
1113

1214
interface UploadDialogProps {
1315
open: boolean
1416
onOpenChange: (open: boolean) => void
17+
setMessage: Dispatch<SetStateAction<string>>
1518
}
1619

17-
export function UploadDialog({ open, onOpenChange }: UploadDialogProps) {
20+
export function UploadDialog({ open, onOpenChange, setMessage }: UploadDialogProps) {
1821
const [uploadPath, setUploadPath] = useState("")
19-
2022
const {uploadFiles} = useChat();
21-
2223
const [filesToUpload, setFilesToUpload] = useState<File[]>([]);
24+
const filePathsToAppend = useRef<Set<string>>(new Set([]));
25+
const [disableUploadPath, setDisableUploadPath] = useState(false);
2326

2427

2528
const { getRootProps, getInputProps, isDragActive } = useDropzone({
2629
onDropAccepted: (files: File[]) => {
27-
setFilesToUpload((oldFiles) => [...oldFiles, ...files]);
30+
setFilesToUpload((oldFiles) => {
31+
const updatedFiles = oldFiles;
32+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
33+
// @ts-expect-error
34+
const oldSet = new Set<string>(oldFiles.map((f) => f.relativePath));
35+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
36+
// @ts-expect-error
37+
files.forEach(file => oldSet.has(file.relativePath) ? {} : updatedFiles.push(file))
38+
return updatedFiles;
39+
});
2840
}
2941
})
3042

3143
const cleanup = (open? : boolean) => {
3244
if (open !== undefined && open) {
3345
return
3446
}
47+
48+
filePathsToAppend.current = new Set<string>([]);
49+
setUploadPath("")
3550
setFilesToUpload([]);
51+
setDisableUploadPath(false);
3652
onOpenChange(false)
3753
}
3854

3955
const handleFilesUpload = async (e: MouseEvent<HTMLButtonElement>) => {
4056
e.preventDefault()
57+
let success = true;
58+
setDisableUploadPath(true);
4159

4260
try {
4361
// Create a new JSZip instance
@@ -66,14 +84,23 @@ export function UploadDialog({ open, onOpenChange }: UploadDialogProps) {
6684
formData.append('uploadPath', uploadPath);
6785

6886
// Upload to agent API
69-
uploadFiles(formData)
87+
success = await uploadFiles(formData)
7088

7189
// eslint-disable-next-line @typescript-eslint/no-explicit-any
7290
} catch (error: any) {
91+
success = false;
7392
toast.error("Failed to zip and upload files:", {
7493
description: error.message,
7594
});
7695
}
96+
if (success) {
97+
for (const filePath of filePathsToAppend.current) {
98+
setMessage(oldMessage => oldMessage + ' @"' + path.join(uploadPath, filePath) + '"');
99+
}
100+
cleanup()
101+
} else {
102+
setDisableUploadPath(false);
103+
}
77104
}
78105

79106

@@ -106,6 +133,7 @@ export function UploadDialog({ open, onOpenChange }: UploadDialogProps) {
106133
onChange={(e) => setUploadPath(e.target.value)}
107134
className="w-full px-3 py-2 text-sm border rounded-md focus:outline-none focus:ring-1 focus:ring-ring"
108135
placeholder="Enter upload path..."
136+
disabled={disableUploadPath}
109137
/>
110138
</div>
111139

@@ -131,7 +159,7 @@ export function UploadDialog({ open, onOpenChange }: UploadDialogProps) {
131159

132160
{filesToUpload.length > 0 && (
133161
<div className="space-y-2">
134-
<h4 className="text-sm font-medium">Selected Files:</h4>
162+
<h4 className="text-sm font-medium">Selected Files (select the checkbox to append @filepath to message)</h4>
135163
<div className="space-y-1 max-h-32 overflow-y-auto">
136164
{filesToUpload.map((file, index) => (
137165
<div
@@ -141,14 +169,28 @@ export function UploadDialog({ open, onOpenChange }: UploadDialogProps) {
141169
{/* eslint-disable-next-line @typescript-eslint/ban-ts-comment */}
142170
{/*// @ts-expect-error*/}
143171
<span className="truncate">{file.relativePath}</span>
144-
<Button
145-
variant="ghost"
146-
size="icon"
147-
className="h-6 w-6"
148-
onClick={() => removeFile(file)}
149-
>
150-
<X className="h-3 w-3" />
151-
</Button>
172+
<div className="flex items-center justify-between gap-2">
173+
<Checkbox onCheckedChange={(checked: boolean) => {
174+
if (checked) {
175+
{/* eslint-disable-next-line @typescript-eslint/ban-ts-comment */}
176+
{/*// @ts-expect-error*/}
177+
filePathsToAppend.current.add(file.relativePath)
178+
} else {
179+
{/* eslint-disable-next-line @typescript-eslint/ban-ts-comment */}
180+
{/*// @ts-expect-error*/}
181+
filePathsToAppend.current.delete(file.relativePath)
182+
}
183+
}}/>
184+
<Button
185+
variant="ghost"
186+
size="icon"
187+
className="h-6 w-6"
188+
onClick={() => removeFile(file)}
189+
>
190+
<X className="h-3 w-3" />
191+
</Button>
192+
</div>
193+
152194
</div>
153195
))}
154196
</div>

0 commit comments

Comments
 (0)