Skip to content

Commit 0205c72

Browse files
committed
[Feat]: Add chat component
1 parent bf9f269 commit 0205c72

File tree

8 files changed

+449
-313
lines changed

8 files changed

+449
-313
lines changed

client/packages/lowcoder/src/comps/comps/chatComp/chatView.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// client/packages/lowcoder/src/comps/comps/chatComp/chatView.tsx
22
import React from "react";
33
import { ChatCompProps } from "./chatCompTypes";
4-
import { ChatApp } from "./components/ChatWithThreads";
4+
import { ChatApp } from "./components/ChatApp";
55

66
import "@assistant-ui/styles/index.css";
77
import "@assistant-ui/styles/markdown.css";
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { ChatProvider } from "./context/ChatContext";
2+
import { ChatMain } from "./ChatMain";
3+
4+
export function ChatApp() {
5+
return (
6+
<ChatProvider>
7+
<ChatMain />
8+
</ChatProvider>
9+
);
10+
}
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,47 @@
1-
import React, { useState, useEffect } from "react";
1+
import React, { useState } from "react";
22
import {
33
useExternalStoreRuntime,
44
ThreadMessageLike,
55
AppendMessage,
66
AssistantRuntimeProvider,
77
ExternalStoreThreadListAdapter,
88
} from "@assistant-ui/react";
9-
import { useThreadContext, MyMessage, ThreadProvider } from "./context/ThreadContext";
109
import { Thread } from "./assistant-ui/thread";
1110
import { ThreadList } from "./assistant-ui/thread-list";
12-
import { chatStorage, ThreadData as StoredThreadData } from "../utils/chatStorage";
13-
import { useChatStorage } from "../hooks/useChatStorage";
11+
import {
12+
useChatContext,
13+
MyMessage,
14+
ThreadData,
15+
RegularThreadData,
16+
ArchivedThreadData
17+
} from "./context/ChatContext";
1418
import styled from "styled-components";
1519

16-
17-
18-
1920
const ChatContainer = styled.div`
2021
display: flex;
2122
height: 500px;
2223
2324
.aui-thread-list-root {
2425
width: 250px;
25-
background-color: #333;
26+
background-color: #fff;
27+
padding: 10px;
2628
}
2729
2830
.aui-thread-root {
2931
flex: 1;
30-
background-color: #f0f0f0;
32+
background-color: #f9fafb;
3133
}
3234
33-
`;
34-
35-
// Define thread data interfaces to match ExternalStoreThreadData requirements
36-
interface RegularThreadData {
37-
threadId: string;
38-
status: "regular";
39-
title: string;
40-
}
35+
.aui-thread-list-item {
36+
cursor: pointer;
37+
transition: background-color 0.2s ease;
4138
42-
interface ArchivedThreadData {
43-
threadId: string;
44-
status: "archived";
45-
title: string;
46-
}
47-
48-
type ThreadData = RegularThreadData | ArchivedThreadData;
39+
&[data-active="true"] {
40+
background-color: #dbeafe;
41+
border: 1px solid #bfdbfe;
42+
}
43+
}
44+
`;
4945

5046
const generateId = () => Math.random().toString(36).substr(2, 9);
5147

@@ -59,22 +55,14 @@ const callYourAPI = async (text: string) => {
5955
};
6056
};
6157

62-
function ChatWithThreads() {
63-
const { currentThreadId, setCurrentThreadId, threads, setThreads } =
64-
useThreadContext();
58+
export function ChatMain() {
59+
const { state, actions } = useChatContext();
6560
const [isRunning, setIsRunning] = useState(false);
66-
const [threadList, setThreadList] = useState<ThreadData[]>([
67-
{ threadId: "default", status: "regular", title: "New Chat" } as RegularThreadData,
68-
]);
69-
const { isInitialized } = useChatStorage({
70-
threadList,
71-
threads,
72-
setThreadList,
73-
setThreads,
74-
setCurrentThreadId,
75-
});
61+
62+
console.log("STATE", state);
63+
7664
// Get messages for current thread
77-
const currentMessages = threads.get(currentThreadId) || [];
65+
const currentMessages = actions.getCurrentMessages();
7866

7967
// Convert custom format to ThreadMessageLike
8068
const convertMessage = (message: MyMessage): ThreadMessageLike => ({
@@ -99,8 +87,7 @@ function ChatWithThreads() {
9987
};
10088

10189
// Update current thread with new user message
102-
const updatedMessages = [...currentMessages, userMessage];
103-
setThreads(prev => new Map(prev).set(currentThreadId, updatedMessages));
90+
await actions.addMessage(state.currentThreadId, userMessage);
10491
setIsRunning(true);
10592

10693
try {
@@ -115,8 +102,7 @@ function ChatWithThreads() {
115102
};
116103

117104
// Update current thread with assistant response
118-
const finalMessages = [...updatedMessages, assistantMessage];
119-
setThreads(prev => new Map(prev).set(currentThreadId, finalMessages));
105+
await actions.addMessage(state.currentThreadId, assistantMessage);
120106
} catch (error) {
121107
// Handle errors gracefully
122108
const errorMessage: MyMessage = {
@@ -126,8 +112,7 @@ function ChatWithThreads() {
126112
timestamp: Date.now(),
127113
};
128114

129-
const finalMessages = [...updatedMessages, errorMessage];
130-
setThreads(prev => new Map(prev).set(currentThreadId, finalMessages));
115+
await actions.addMessage(state.currentThreadId, errorMessage);
131116
} finally {
132117
setIsRunning(false);
133118
}
@@ -155,7 +140,8 @@ function ChatWithThreads() {
155140
};
156141
newMessages.push(editedMessage);
157142

158-
setThreads(prev => new Map(prev).set(currentThreadId, newMessages));
143+
// Update messages using the new context action
144+
await actions.updateMessages(state.currentThreadId, newMessages);
159145
setIsRunning(true);
160146

161147
try {
@@ -170,7 +156,7 @@ function ChatWithThreads() {
170156
};
171157

172158
newMessages.push(assistantMessage);
173-
setThreads(prev => new Map(prev).set(currentThreadId, newMessages));
159+
await actions.updateMessages(state.currentThreadId, newMessages);
174160
} catch (error) {
175161
// Handle errors gracefully
176162
const errorMessage: MyMessage = {
@@ -181,89 +167,44 @@ function ChatWithThreads() {
181167
};
182168

183169
newMessages.push(errorMessage);
184-
setThreads(prev => new Map(prev).set(currentThreadId, newMessages));
170+
await actions.updateMessages(state.currentThreadId, newMessages);
185171
} finally {
186172
setIsRunning(false);
187173
}
188174
};
189175

190176
// Thread list adapter for managing multiple threads
191177
const threadListAdapter: ExternalStoreThreadListAdapter = {
192-
threadId: currentThreadId,
193-
threads: threadList.filter((t): t is RegularThreadData => t.status === "regular"),
194-
archivedThreads: threadList.filter((t): t is ArchivedThreadData => t.status === "archived"),
178+
threadId: state.currentThreadId,
179+
threads: state.threadList.filter((t): t is RegularThreadData => t.status === "regular"),
180+
archivedThreads: state.threadList.filter((t): t is ArchivedThreadData => t.status === "archived"),
195181

196182
onSwitchToNewThread: async () => {
197-
const newId = `thread-${Date.now()}`;
198-
const newThread: RegularThreadData = {
199-
threadId: newId,
200-
status: "regular",
201-
title: "New Chat",
202-
};
203-
204-
setThreadList((prev) => [...prev, newThread]);
205-
setThreads((prev) => new Map(prev).set(newId, []));
206-
setCurrentThreadId(newId);
207-
208-
// Save new thread to storage
209-
try {
210-
const storedThread: StoredThreadData = {
211-
threadId: newId,
212-
status: "regular",
213-
title: "New Chat",
214-
createdAt: Date.now(),
215-
updatedAt: Date.now(),
216-
};
217-
await chatStorage.saveThread(storedThread);
218-
} catch (error) {
219-
console.error("Failed to save new thread:", error);
220-
}
183+
const threadId = await actions.createThread("New Chat");
184+
actions.setCurrentThread(threadId);
221185
},
222186

223187
onSwitchToThread: (threadId) => {
224-
setCurrentThreadId(threadId);
188+
actions.setCurrentThread(threadId);
225189
},
226190

227-
onRename: (threadId, newTitle) => {
228-
setThreadList((prev) =>
229-
prev.map((t) =>
230-
t.threadId === threadId ? { ...t, title: newTitle } : t,
231-
),
232-
);
191+
onRename: async (threadId, newTitle) => {
192+
await actions.updateThread(threadId, { title: newTitle });
233193
},
234194

235-
onArchive: (threadId) => {
236-
setThreadList((prev) =>
237-
prev.map((t) =>
238-
t.threadId === threadId ? { ...t, status: "archived" } : t,
239-
),
240-
);
195+
onArchive: async (threadId) => {
196+
await actions.updateThread(threadId, { status: "archived" });
241197
},
242198

243199
onDelete: async (threadId) => {
244-
setThreadList((prev) => prev.filter((t) => t.threadId !== threadId));
245-
setThreads((prev) => {
246-
const next = new Map(prev);
247-
next.delete(threadId);
248-
return next;
249-
});
250-
if (currentThreadId === threadId) {
251-
setCurrentThreadId("default");
252-
}
253-
254-
// Delete thread from storage
255-
try {
256-
await chatStorage.deleteThread(threadId);
257-
} catch (error) {
258-
console.error("Failed to delete thread from storage:", error);
259-
}
200+
await actions.deleteThread(threadId);
260201
},
261202
};
262203

263204
const runtime = useExternalStoreRuntime({
264205
messages: currentMessages,
265206
setMessages: (messages) => {
266-
setThreads((prev) => new Map(prev).set(currentThreadId, messages));
207+
actions.updateMessages(state.currentThreadId, messages);
267208
},
268209
convertMessage,
269210
isRunning,
@@ -274,7 +215,7 @@ function ChatWithThreads() {
274215
},
275216
});
276217

277-
if (!isInitialized) {
218+
if (!state.isInitialized) {
278219
return <div>Loading...</div>;
279220
}
280221

@@ -288,13 +229,3 @@ function ChatWithThreads() {
288229
);
289230
}
290231

291-
// Main App component with proper context wrapping
292-
export function ChatApp() {
293-
return (
294-
<ThreadProvider>
295-
<ChatWithThreads />
296-
</ThreadProvider>
297-
);
298-
}
299-
300-
export { ChatWithThreads };

client/packages/lowcoder/src/comps/comps/chatComp/components/assistant-ui/thread-list.tsx

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,18 @@ import {
55
} from "@assistant-ui/react";
66
import { PencilIcon, PlusIcon, Trash2Icon } from "lucide-react";
77

8-
import { Button } from "../ui/button";
98
import { TooltipIconButton } from "./tooltip-icon-button";
109
import { useThreadListItemRuntime } from "@assistant-ui/react";
10+
import { Button } from "antd";
11+
12+
import styled from "styled-components";
13+
import { useChatContext } from "../context/ChatContext";
14+
15+
const StyledPrimaryButton = styled(Button)`
16+
padding: 20px;
17+
margin-bottom: 20px;
18+
`;
19+
1120

1221
export const ThreadList: FC = () => {
1322
return (
@@ -21,10 +30,9 @@ export const ThreadList: FC = () => {
2130
const ThreadListNew: FC = () => {
2231
return (
2332
<ThreadListPrimitive.New asChild>
24-
<Button className="aui-thread-list-new" variant="ghost">
25-
<PlusIcon />
33+
<StyledPrimaryButton size="large" type="primary" icon={<PlusIcon />}>
2634
New Thread
27-
</Button>
35+
</StyledPrimaryButton>
2836
</ThreadListPrimitive.New>
2937
);
3038
};
@@ -46,6 +54,7 @@ const ThreadListItem: FC = () => {
4654
};
4755

4856
const ThreadListItemTitle: FC = () => {
57+
4958
return (
5059
<p className="aui-thread-list-item-title">
5160
<ThreadListItemPrimitive.Title fallback="New Chat" />

0 commit comments

Comments
 (0)