Skip to content

Commit 6c28494

Browse files
committed
add event listeners for the chat component
1 parent 1de8ae6 commit 6c28494

File tree

6 files changed

+140
-40
lines changed

6 files changed

+140
-40
lines changed

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

Lines changed: 75 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { withDefault } from "comps/generators";
88
import { BoolControl } from "comps/controls/boolControl";
99
import { dropdownControl } from "comps/controls/dropdownControl";
1010
import QuerySelectControl from "comps/controls/querySelectControl";
11+
import { eventHandlerControl, EventConfigType } from "comps/controls/eventHandlerControl";
1112
import { ChatCore } from "./components/ChatCore";
1213
import { ChatPropertyView } from "./chatPropertyView";
1314
import { createChatStorage } from "./utils/storageFactory";
@@ -19,7 +20,58 @@ import "@assistant-ui/styles/index.css";
1920
import "@assistant-ui/styles/markdown.css";
2021

2122
// ============================================================================
22-
// SIMPLIFIED CHILDREN MAP - ONLY ESSENTIAL PROPS
23+
// CHAT-SPECIFIC EVENTS
24+
// ============================================================================
25+
26+
export const componentLoadEvent: EventConfigType = {
27+
label: "Component Load",
28+
value: "componentLoad",
29+
description: "Triggered when the chat component finishes loading - Load existing data from backend",
30+
};
31+
32+
export const messageSentEvent: EventConfigType = {
33+
label: "Message Sent",
34+
value: "messageSent",
35+
description: "Triggered when a user sends a message - Auto-save user messages",
36+
};
37+
38+
export const messageReceivedEvent: EventConfigType = {
39+
label: "Message Received",
40+
value: "messageReceived",
41+
description: "Triggered when a response is received from the AI - Auto-save AI responses",
42+
};
43+
44+
export const threadCreatedEvent: EventConfigType = {
45+
label: "Thread Created",
46+
value: "threadCreated",
47+
description: "Triggered when a new thread is created - Auto-save new threads",
48+
};
49+
50+
export const threadUpdatedEvent: EventConfigType = {
51+
label: "Thread Updated",
52+
value: "threadUpdated",
53+
description: "Triggered when a thread is updated - Auto-save thread changes",
54+
};
55+
56+
export const threadDeletedEvent: EventConfigType = {
57+
label: "Thread Deleted",
58+
value: "threadDeleted",
59+
description: "Triggered when a thread is deleted - Delete thread from backend",
60+
};
61+
62+
const ChatEventOptions = [
63+
componentLoadEvent,
64+
messageSentEvent,
65+
messageReceivedEvent,
66+
threadCreatedEvent,
67+
threadUpdatedEvent,
68+
threadDeletedEvent,
69+
] as const;
70+
71+
export const ChatEventHandlerControl = eventHandlerControl(ChatEventOptions);
72+
73+
// ============================================================================
74+
// SIMPLIFIED CHILDREN MAP - WITH EVENT HANDLERS
2375
// ============================================================================
2476

2577
function generateUniqueTableName(): string {
@@ -48,6 +100,9 @@ export const chatChildrenMap = {
48100
// Database Information (read-only)
49101
databaseName: withDefault(StringControl, ""),
50102

103+
// Event Handlers
104+
onEvent: ChatEventHandlerControl,
105+
51106
// Exposed Variables (not shown in Property View)
52107
currentMessage: stringExposingStateControl("currentMessage", ""),
53108
conversationHistory: stringExposingStateControl("conversationHistory", "[]"),
@@ -119,6 +174,8 @@ const ChatTmpComp = new UICompBuilder(
119174
// Handle message updates for exposed variable
120175
const handleMessageUpdate = (message: string) => {
121176
dispatch(changeChildAction("currentMessage", message, false));
177+
// Trigger messageSent event
178+
props.onEvent("messageSent");
122179
};
123180

124181
// Handle conversation history updates for exposed variable
@@ -130,26 +187,32 @@ const ChatTmpComp = new UICompBuilder(
130187
timestamp: msg.timestamp
131188
}));
132189
dispatch(changeChildAction("conversationHistory", JSON.stringify(formattedHistory), false));
190+
191+
// Trigger messageReceived event when bot responds
192+
const lastMessage = conversationHistory[conversationHistory.length - 1];
193+
if (lastMessage && lastMessage.role === 'assistant') {
194+
props.onEvent("messageReceived");
195+
}
133196
};
134197

135-
// Cleanup on unmount
136-
useEffect(() => {
137-
console.log("cleanup on unmount");
138-
return () => {
139-
console.log("cleanup on unmount");
140-
const tableName = uniqueTableName.current;
141-
if (tableName) {
142-
storage.cleanup();
143-
}
144-
};
145-
}, []);
198+
// Cleanup on unmount
199+
useEffect(() => {
200+
return () => {
201+
const tableName = uniqueTableName.current;
202+
if (tableName) {
203+
storage.cleanup();
204+
}
205+
};
206+
}, []);
146207

147208
return (
148209
<ChatCore
149210
storage={storage}
150211
messageHandler={messageHandler}
212+
placeholder={props.placeholder}
151213
onMessageUpdate={handleMessageUpdate}
152214
onConversationUpdate={handleConversationUpdate}
215+
onEvent={props.onEvent}
153216
/>
154217
);
155218
}

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

Lines changed: 22 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
// client/packages/lowcoder/src/comps/comps/chatComp/chatPropertyView.tsx
22

3-
import React from "react";
3+
import React, { useMemo } from "react";
44
import { Section, sectionNames } from "lowcoder-design";
5+
import { placeholderPropertyView } from "../../utils/propertyUtils";
56

67
// ============================================================================
78
// CLEAN PROPERTY VIEW - FOCUSED ON ESSENTIAL CONFIGURATION
@@ -10,24 +11,8 @@ import { Section, sectionNames } from "lowcoder-design";
1011
export const ChatPropertyView = React.memo((props: any) => {
1112
const { children } = props;
1213

13-
return (
14+
return useMemo(() => (
1415
<>
15-
{/* Basic Configuration */}
16-
<Section name={sectionNames.basic}>
17-
{children.placeholder.propertyView({
18-
label: "Placeholder Text",
19-
placeholder: "Enter placeholder text..."
20-
})}
21-
22-
{children.databaseName.propertyView({
23-
label: "Database Name",
24-
placeholder: "Database will be auto-generated...",
25-
tooltip: "Read-only: Auto-generated database name for data persistence. You can reference this in queries if needed.",
26-
disabled: true
27-
})}
28-
29-
</Section>
30-
3116
{/* Message Handler Configuration */}
3217
<Section name="Message Handler">
3318
{children.handlerType.propertyView({
@@ -64,8 +49,26 @@ export const ChatPropertyView = React.memo((props: any) => {
6449
})}
6550
</Section>
6651

52+
{/* UI Configuration */}
53+
<Section name="UI Configuration">
54+
{placeholderPropertyView(children)}
55+
</Section>
56+
57+
{/* Database Information */}
58+
<Section name="Database">
59+
{children.databaseName.propertyView({
60+
label: "Database Name",
61+
tooltip: "Auto-generated database name for this chat component (read-only)"
62+
})}
63+
</Section>
64+
65+
{/* STANDARD EVENT HANDLERS SECTION */}
66+
<Section name={sectionNames.interaction}>
67+
{children.onEvent.getPropertyView()}
68+
</Section>
69+
6770
</>
68-
);
71+
), [children]);
6972
});
7073

7174
ChatPropertyView.displayName = 'ChatPropertyView';

client/packages/lowcoder/src/comps/comps/chatComp/components/ChatCore.tsx

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,22 @@ import { ChatCoreProps } from "../types/chatTypes";
99
// CHAT CORE - THE SHARED FOUNDATION
1010
// ============================================================================
1111

12-
export function ChatCore({ storage, messageHandler, onMessageUpdate, onConversationUpdate }: ChatCoreProps) {
12+
export function ChatCore({
13+
storage,
14+
messageHandler,
15+
placeholder,
16+
onMessageUpdate,
17+
onConversationUpdate,
18+
onEvent
19+
}: ChatCoreProps) {
1320
return (
1421
<ChatProvider storage={storage}>
1522
<ChatCoreMain
1623
messageHandler={messageHandler}
24+
placeholder={placeholder}
1725
onMessageUpdate={onMessageUpdate}
1826
onConversationUpdate={onConversationUpdate}
27+
onEvent={onEvent}
1928
/>
2029
</ChatProvider>
2130
);

client/packages/lowcoder/src/comps/comps/chatComp/components/ChatCoreMain.tsx

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// client/packages/lowcoder/src/comps/comps/chatComp/components/ChatCoreMain.tsx
22

3-
import React, { useState } from "react";
3+
import React, { useState, useEffect } from "react";
44
import {
55
useExternalStoreRuntime,
66
ThreadMessageLike,
@@ -59,13 +59,22 @@ const ChatContainer = styled.div`
5959

6060
interface ChatCoreMainProps {
6161
messageHandler: MessageHandler;
62+
placeholder?: string;
6263
onMessageUpdate?: (message: string) => void;
6364
onConversationUpdate?: (conversationHistory: ChatMessage[]) => void;
65+
// STANDARD LOWCODER EVENT PATTERN - SINGLE CALLBACK (OPTIONAL)
66+
onEvent?: (eventName: string) => void;
6467
}
6568

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

68-
export function ChatCoreMain({ messageHandler, onMessageUpdate, onConversationUpdate }: ChatCoreMainProps) {
71+
export function ChatCoreMain({
72+
messageHandler,
73+
placeholder,
74+
onMessageUpdate,
75+
onConversationUpdate,
76+
onEvent
77+
}: ChatCoreMainProps) {
6978
const { state, actions } = useChatContext();
7079
const [isRunning, setIsRunning] = useState(false);
7180

@@ -78,10 +87,15 @@ export function ChatCoreMain({ messageHandler, onMessageUpdate, onConversationUp
7887
console.log("CURRENT MESSAGES", currentMessages);
7988

8089
// Notify parent component of conversation changes
81-
React.useEffect(() => {
90+
useEffect(() => {
8291
onConversationUpdate?.(currentMessages);
8392
}, [currentMessages]);
8493

94+
// Trigger component load event on mount
95+
useEffect(() => {
96+
onEvent?.("componentLoad");
97+
}, [onEvent]);
98+
8599
// Convert custom format to ThreadMessageLike (same as your current implementation)
86100
const convertMessage = (message: ChatMessage): ThreadMessageLike => ({
87101
role: message.role,
@@ -209,6 +223,7 @@ export function ChatCoreMain({ messageHandler, onMessageUpdate, onConversationUp
209223
onSwitchToNewThread: async () => {
210224
const threadId = await actions.createThread("New Chat");
211225
actions.setCurrentThread(threadId);
226+
onEvent?.("threadCreated");
212227
},
213228

214229
onSwitchToThread: (threadId) => {
@@ -217,14 +232,17 @@ export function ChatCoreMain({ messageHandler, onMessageUpdate, onConversationUp
217232

218233
onRename: async (threadId, newTitle) => {
219234
await actions.updateThread(threadId, { title: newTitle });
235+
onEvent?.("threadUpdated");
220236
},
221237

222238
onArchive: async (threadId) => {
223239
await actions.updateThread(threadId, { status: "archived" });
240+
onEvent?.("threadUpdated");
224241
},
225242

226243
onDelete: async (threadId) => {
227244
await actions.deleteThread(threadId);
245+
onEvent?.("threadDeleted");
228246
},
229247
};
230248

@@ -250,7 +268,7 @@ export function ChatCoreMain({ messageHandler, onMessageUpdate, onConversationUp
250268
<AssistantRuntimeProvider runtime={runtime}>
251269
<ChatContainer>
252270
<ThreadList />
253-
<Thread />
271+
<Thread placeholder={placeholder} />
254272
</ChatContainer>
255273
</AssistantRuntimeProvider>
256274
);

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

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,11 @@ import {
2222
import { MarkdownText } from "./markdown-text";
2323
import { TooltipIconButton } from "./tooltip-icon-button";
2424

25-
export const Thread: FC = () => {
25+
interface ThreadProps {
26+
placeholder?: string;
27+
}
28+
29+
export const Thread: FC<ThreadProps> = ({ placeholder = "Write a message..." }) => {
2630
return (
2731
<ThreadPrimitive.Root
2832
className="aui-root aui-thread-root"
@@ -47,7 +51,7 @@ import {
4751

4852
<div className="aui-thread-viewport-footer">
4953
<ThreadScrollToBottom />
50-
<Composer />
54+
<Composer placeholder={placeholder} />
5155
</div>
5256
</ThreadPrimitive.Viewport>
5357
</ThreadPrimitive.Root>
@@ -110,13 +114,13 @@ import {
110114
);
111115
};
112116

113-
const Composer: FC = () => {
117+
const Composer: FC<{ placeholder?: string }> = ({ placeholder = "Write a message..." }) => {
114118
return (
115119
<ComposerPrimitive.Root className="aui-composer-root">
116120
<ComposerPrimitive.Input
117121
rows={1}
118122
autoFocus
119-
placeholder="Write a message..."
123+
placeholder={placeholder}
120124
className="aui-composer-input"
121125
/>
122126
<ComposerAction />

client/packages/lowcoder/src/comps/comps/chatComp/types/chatTypes.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,11 @@ export interface ChatMessage {
7575
export interface ChatCoreProps {
7676
storage: ChatStorage;
7777
messageHandler: MessageHandler;
78+
placeholder?: string;
7879
onMessageUpdate?: (message: string) => void;
7980
onConversationUpdate?: (conversationHistory: ChatMessage[]) => void;
81+
// STANDARD LOWCODER EVENT PATTERN - SINGLE CALLBACK
82+
onEvent?: (eventName: string) => void;
8083
}
8184

8285
export interface ChatPanelProps {

0 commit comments

Comments
 (0)