Skip to content

Commit fbd79dc

Browse files
committed
Merge branch 'main' into feat-clickable-links
# Conflicts: # chat/src/components/message-list.tsx
2 parents 42b4bd1 + 6663ede commit fbd79dc

File tree

19 files changed

+1115
-154
lines changed

19 files changed

+1115
-154
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
# Changelog
22

3+
## v0.9.0
4+
5+
### Features
6+
- Add support for initial prompt via `-I` flag
7+
38
## v0.8.0
49

510
### Features

chat/bun.lock

Lines changed: 115 additions & 17 deletions
Large diffs are not rendered by default.

chat/package.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,19 @@
11
{
22
"dependencies": {
3+
"@radix-ui/react-checkbox": "^1.3.3",
4+
"@radix-ui/react-dialog": "^1.1.15",
35
"@radix-ui/react-dropdown-menu": "^2.1.14",
46
"@radix-ui/react-slot": "^1.2.2",
57
"@radix-ui/react-tabs": "^1.1.11",
68
"class-variance-authority": "^0.7.1",
79
"clsx": "^2.1.1",
10+
"jszip": "^3.10.1",
811
"lucide-react": "^0.511.0",
912
"next": "15.4.7",
1013
"next-themes": "^0.4.6",
1114
"react": "^19.0.0",
1215
"react-dom": "^19.0.0",
16+
"react-dropzone": "^14.3.8",
1317
"react-textarea-autosize": "^8.5.9",
1418
"sonner": "^2.0.3",
1519
"tailwind-merge": "^3.3.0"
@@ -44,5 +48,5 @@
4448
"start": "next start",
4549
"storybook": "storybook dev -p 6006"
4650
},
47-
"version": "0.8.0"
51+
"version": "0.9.0"
4852
}

chat/src/components/chat-provider.tsx

Lines changed: 71 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
useContext,
1111
} from "react";
1212
import { toast } from "sonner";
13+
import {getErrorMessage} from "@/lib/error-utils";
1314

1415
interface Message {
1516
id: number;
@@ -34,6 +35,22 @@ interface StatusChangeEvent {
3435
status: string;
3536
}
3637

38+
interface APIErrorDetail {
39+
location: string;
40+
message: string;
41+
value: null | string | number | boolean | object;
42+
}
43+
44+
interface APIErrorModel {
45+
$schema: string;
46+
detail: string;
47+
errors: APIErrorDetail[];
48+
instance: string;
49+
status: number;
50+
title: string;
51+
type: string;
52+
}
53+
3754
function isDraftMessage(message: Message | DraftMessage): boolean {
3855
return message.id === undefined;
3956
}
@@ -42,11 +59,17 @@ type MessageType = "user" | "raw";
4259

4360
export type ServerStatus = "stable" | "running" | "offline" | "unknown";
4461

62+
export interface FileUploadResponse {
63+
ok: boolean;
64+
filePath?: string;
65+
}
66+
4567
interface ChatContextValue {
4668
messages: (Message | DraftMessage)[];
4769
loading: boolean;
4870
serverStatus: ServerStatus;
4971
sendMessage: (message: string, type?: MessageType) => void;
72+
uploadFiles: (formData: FormData) => Promise<FileUploadResponse>;
5073
}
5174

5275
const ChatContext = createContext<ChatContextValue | undefined>(undefined);
@@ -229,34 +252,27 @@ export function ChatProvider({ children }: PropsWithChildren) {
229252
});
230253

231254
if (!response.ok) {
232-
const errorData = await response.json();
255+
const errorData = await response.json() as APIErrorModel;
233256
console.error("Failed to send message:", errorData);
234257
const detail = errorData.detail;
235258
const messages =
236259
"errors" in errorData
237-
? // eslint-disable-next-line @typescript-eslint/no-explicit-any
238-
errorData.errors.map((e: any) => e.message).join(", ")
260+
?
261+
errorData.errors.map((e: APIErrorDetail) => e.message).join(", ")
239262
: "";
240263

241264
const fullDetail = `${detail}: ${messages}`;
242265
toast.error(`Failed to send message`, {
243266
description: fullDetail,
244267
});
245268
}
246-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
247-
} catch (error: any) {
248-
console.error("Error sending message:", error);
249-
const detail = error.detail;
250-
const messages =
251-
"errors" in error
252-
? // eslint-disable-next-line @typescript-eslint/no-explicit-any
253-
error.errors.map((e: any) => e.message).join("\n")
254-
: "";
255269

256-
const fullDetail = `${detail}: ${messages}`;
270+
} catch (error) {
271+
console.error("Error sending message:", error);
272+
const message = getErrorMessage(error)
257273

258274
toast.error(`Error sending message`, {
259-
description: fullDetail,
275+
description: message,
260276
});
261277
} finally {
262278
if (type === "user") {
@@ -268,13 +284,54 @@ export function ChatProvider({ children }: PropsWithChildren) {
268284
}
269285
};
270286

287+
// Upload files to workspace
288+
const uploadFiles = async (formData: FormData): Promise<FileUploadResponse> => {
289+
let result: FileUploadResponse = {ok: true};
290+
try{
291+
const response = await fetch(`${agentAPIUrl}/upload`, {
292+
method: 'POST',
293+
body: formData,
294+
});
295+
296+
if (!response.ok) {
297+
result.ok = false;
298+
const errorData = await response.json() as APIErrorModel;
299+
console.error("Failed to send message:", errorData);
300+
const detail = errorData.detail;
301+
const messages =
302+
"errors" in errorData
303+
?
304+
errorData.errors.map((e: APIErrorDetail) => e.message).join(", ")
305+
: "";
306+
307+
const fullDetail = `${detail}: ${messages}`;
308+
toast.error(`Failed to upload files`, {
309+
description: fullDetail,
310+
});
311+
} else {
312+
result = (await response.json()) as FileUploadResponse;
313+
}
314+
315+
} catch (error) {
316+
result.ok = false;
317+
console.error("Error uploading files:", error);
318+
const message = getErrorMessage(error)
319+
320+
toast.error(`Error uploading files`, {
321+
description: message,
322+
});
323+
}
324+
return result;
325+
}
326+
271327
return (
272328
<ChatContext.Provider
273329
value={{
274330
messages,
275331
loading,
276332
sendMessage,
277333
serverStatus,
334+
uploadFiles,
278335
}}
279336
>
280337
{children}

chat/src/components/chat.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
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";
66

77
export function Chat() {
8-
const { messages, loading, sendMessage, serverStatus } = useChat();
8+
const {messages, loading, sendMessage, serverStatus} = useChat();
99

1010
return (
1111
<>
12-
<MessageList messages={messages} />
12+
<MessageList messages={messages}/>
1313
<MessageInput
1414
onSendMessage={sendMessage}
1515
disabled={loading}

chat/src/components/drag-drop.tsx

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import React from "react";
2+
import { useDropzone } from "react-dropzone";
3+
4+
export interface DragDropProps {
5+
onFilesAdded: (files: File[]) => void;
6+
disabled?: boolean;
7+
children: React.ReactNode;
8+
className?: string;
9+
}
10+
11+
export function DragDrop({ onFilesAdded, disabled = false, children, className = "" }: DragDropProps) {
12+
const { getRootProps, getInputProps, isDragActive } = useDropzone({
13+
noClick: true,
14+
disabled,
15+
onDropAccepted: (files: File[]) => {
16+
onFilesAdded(files);
17+
},
18+
multiple: true,
19+
});
20+
21+
return (
22+
<div
23+
{...getRootProps()}
24+
className={`relative ${className} ${
25+
isDragActive && !disabled ? 'border-primary border-2 border-dashed rounded-lg text-center transition-colors' : ''
26+
}`}
27+
>
28+
<input {...getInputProps()} />
29+
{isDragActive && !disabled && (
30+
<div className="absolute inset-0 flex items-center justify-center bg-primary/20z-10">
31+
<p className="text-sm text-primary font-medium">Drop the files here</p>
32+
</div>
33+
)}
34+
{children}
35+
</div>
36+
);
37+
}

0 commit comments

Comments
 (0)