Skip to content

Commit c1030f6

Browse files
committed
complete chatcontroller architecture
1 parent 4da29c4 commit c1030f6

5 files changed

Lines changed: 161 additions & 864 deletions

File tree

Lines changed: 92 additions & 134 deletions
Original file line numberDiff line numberDiff line change
@@ -1,108 +1,108 @@
1-
import React, { useCallback, useContext, useEffect, useRef } from "react";
1+
import React, { useContext } from "react";
22
import { Section, sectionNames } from "lowcoder-design";
33
import { UICompBuilder, withDefault } from "../../generators";
4-
import { NameConfig, NameConfigHidden, withExposingConfigs } from "../../generators/withExposing";
5-
import { withMethodExposing } from "../../generators/withMethodExposing";
6-
import { stringExposingStateControl, arrayObjectExposingStateControl } from "comps/controls/codeStateControl";
4+
import {
5+
NameConfig,
6+
NameConfigHidden,
7+
withExposingConfigs,
8+
} from "../../generators/withExposing";
9+
import { stringExposingStateControl } from "comps/controls/codeStateControl";
710
import { BoolControl } from "comps/controls/boolControl";
8-
import { StringControl } from "comps/controls/codeControl";
11+
import { StringControl, jsonArrayControl } from "comps/controls/codeControl";
912
import { AutoHeightControl } from "comps/controls/autoHeightControl";
1013
import { eventHandlerControl } from "comps/controls/eventHandlerControl";
1114
import { styleControl } from "comps/controls/styleControl";
12-
import { AnimationStyle, TextStyle } from "comps/controls/styleControlConstants";
15+
import {
16+
AnimationStyle,
17+
TextStyle,
18+
} from "comps/controls/styleControlConstants";
1319
import { hiddenPropertyView } from "comps/utils/propertyUtils";
1420
import { EditorContext } from "comps/editorState";
15-
import { trans } from "i18n";
1621

17-
import { PluvRoomProvider, pluvConfig } from "./store";
18-
import { yjs } from "@pluv/crdt-yjs";
1922
import { ChatBoxView } from "./components/ChatBoxView";
2023

24+
// ─── Events ──────────────────────────────────────────────────────────────────
25+
2126
const ChatEvents = [
22-
{ label: trans("chatBox.messageSent"), value: "messageSent", description: trans("chatBox.messageSentDesc") },
23-
{ label: trans("chatBox.messageReceived"), value: "messageReceived", description: trans("chatBox.messageReceivedDesc") },
24-
{ label: trans("chatBox.roomJoined"), value: "roomJoined", description: trans("chatBox.roomJoinedDesc") },
25-
{ label: trans("chatBox.roomLeft"), value: "roomLeft", description: trans("chatBox.roomLeftDesc") },
2627
{
27-
label: "LLM Message Received",
28-
value: "llmMessageReceived",
29-
description: "Fired when an AI response arrives in an LLM room",
28+
label: "Message Sent",
29+
value: "messageSent",
30+
description:
31+
"Triggered when the user presses send. Read chatBox.lastSentMessageText to get the message content.",
32+
},
33+
{
34+
label: "Start Typing",
35+
value: "startTyping",
36+
description:
37+
"Triggered when the user starts typing. Wire this to chatController.startTyping().",
38+
},
39+
{
40+
label: "Stop Typing",
41+
value: "stopTyping",
42+
description:
43+
"Triggered when the user stops typing. Wire this to chatController.stopTyping().",
3044
},
3145
] as const;
3246

47+
// ─── Children map ────────────────────────────────────────────────────────────
48+
3349
const childrenMap = {
34-
chatName: stringExposingStateControl("chatName", "Chat Room"),
35-
userId: stringExposingStateControl("userId", "user_1"),
36-
userName: stringExposingStateControl("userName", "User"),
37-
applicationId: stringExposingStateControl("applicationId", "lowcoder_app"),
38-
39-
// Pluv.io connection settings
40-
pluvPublicKey: withDefault(StringControl, ""),
41-
pluvAuthUrl: withDefault(StringControl, "/api/auth/pluv"),
42-
43-
// Room panel
44-
allowRoomCreation: withDefault(BoolControl, true),
45-
allowRoomSearch: withDefault(BoolControl, true),
46-
showRoomPanel: withDefault(BoolControl, true),
47-
roomPanelWidth: withDefault(StringControl, "220px"),
48-
49-
// LLM settings
50-
systemPrompt: withDefault(
51-
StringControl,
52-
"You are a helpful AI assistant. Answer concisely and clearly.",
53-
),
54-
llmBotName: withDefault(StringControl, "AI Assistant"),
50+
chatTitle: stringExposingStateControl("chatTitle", "Chat"),
51+
showHeader: withDefault(BoolControl, true),
52+
53+
messages: jsonArrayControl([]),
54+
currentUserId: withDefault(StringControl, "user_1"),
55+
currentUserName: withDefault(StringControl, "User"),
56+
typingUsers: jsonArrayControl([]),
5557

56-
// Exposed state
57-
llmConversationHistory: arrayObjectExposingStateControl("llmConversationHistory", []),
58+
lastSentMessageText: stringExposingStateControl("lastSentMessageText", ""),
59+
messageText: stringExposingStateControl("messageText", ""),
5860

59-
// Layout / style
6061
autoHeight: AutoHeightControl,
6162
onEvent: eventHandlerControl(ChatEvents),
6263
style: styleControl(TextStyle, "style"),
6364
animationStyle: styleControl(AnimationStyle, "animationStyle"),
6465
};
6566

67+
// ─── Property panel ──────────────────────────────────────────────────────────
68+
6669
const ChatBoxPropertyView = React.memo((props: { children: any }) => {
6770
const { children } = props;
6871
const editorMode = useContext(EditorContext).editorModeStatus;
6972

7073
return (
7174
<>
7275
<Section name={sectionNames.basic}>
73-
{children.chatName.propertyView({ label: "Chat Name", tooltip: "Display name for the chat header" })}
74-
{children.userId.propertyView({ label: "User ID", tooltip: "Current user's unique identifier" })}
75-
{children.userName.propertyView({ label: "User Name", tooltip: "Current user's display name" })}
76-
{children.applicationId.propertyView({ label: "Application ID", tooltip: "Scopes rooms to this application" })}
77-
</Section>
78-
79-
<Section name="Pluv.io Connection">
80-
{children.pluvPublicKey.propertyView({
81-
label: "Public Key",
82-
tooltip: "Pluv.io publishable key (pk_...). Can also be set via VITE_PLUV_PUBLIC_KEY env var.",
76+
{children.chatTitle.propertyView({
77+
label: "Chat Title",
78+
tooltip: "Display title shown in the chat header",
79+
})}
80+
{children.messages.propertyView({
81+
label: "Messages",
82+
tooltip:
83+
'Bind to your data query, e.g. {{ loadMessages.data }}. Expected shape: [{ id, text, authorId, authorName, timestamp }]',
8384
})}
84-
{children.pluvAuthUrl.propertyView({
85-
label: "Auth URL",
86-
tooltip: "Pluv auth endpoint URL for token exchange (e.g. /api/auth/pluv or http://localhost:3006/api/auth/pluv)",
85+
{children.currentUserId.propertyView({
86+
label: "Current User ID",
87+
tooltip:
88+
"The current user's ID — used to distinguish own vs. other messages. Bind to {{ chatController1.userId }}",
89+
})}
90+
{children.currentUserName.propertyView({
91+
label: "Current User Name",
92+
tooltip: "The current user's display name",
8793
})}
8894
</Section>
8995

90-
<Section name="Room Settings">
91-
{children.allowRoomCreation.propertyView({ label: "Allow Room Creation" })}
92-
{children.allowRoomSearch.propertyView({ label: "Allow Room Search" })}
93-
{children.showRoomPanel.propertyView({ label: "Show Room Panel" })}
94-
{children.roomPanelWidth.propertyView({ label: "Panel Width", tooltip: "e.g. 220px or 25%" })}
96+
<Section name="Real-time">
97+
{children.typingUsers.propertyView({
98+
label: "Typing Users",
99+
tooltip:
100+
"Array of users currently typing. Bind to {{ chatController1.typingUsers }}",
101+
})}
95102
</Section>
96103

97-
<Section name="AI / LLM Settings">
98-
{children.systemPrompt.propertyView({
99-
label: "System Prompt",
100-
tooltip: "Prepended to the conversation history sent to your query. Tells the AI how to behave.",
101-
})}
102-
{children.llmBotName.propertyView({
103-
label: "AI Bot Name",
104-
tooltip: "Display name shown on AI messages in LLM rooms.",
105-
})}
104+
<Section name="Display">
105+
{children.showHeader.propertyView({ label: "Show Header" })}
106106
</Section>
107107

108108
{["logic", "both"].includes(editorMode) && (
@@ -131,55 +131,29 @@ const ChatBoxPropertyView = React.memo((props: { children: any }) => {
131131

132132
ChatBoxPropertyView.displayName = "ChatBoxV2PropertyView";
133133

134-
let ChatBoxV2Tmp = (function () {
135-
return new UICompBuilder(childrenMap, (props, dispatch) => {
136-
const onChangeRef = useRef(props.llmConversationHistory.onChange);
137-
useEffect(() => {
138-
onChangeRef.current = props.llmConversationHistory.onChange;
139-
});
140-
141-
const onConversationHistoryChange = useCallback((history: any[]) => {
142-
onChangeRef.current(history);
143-
}, []);
144-
145-
const appId = props.applicationId.value || "lowcoder_app";
146-
const userId = props.userId.value || "user_1";
147-
const userName = props.userName.value || "User";
148-
const roomName = `chatv2_${appId}`;
149-
150-
// Update the module-level config before pluv connects.
151-
// publicKey MUST be set here so resolvePluvPublicKey() returns the right
152-
// value when PluvRoomProvider opens its WebSocket connection.
153-
pluvConfig.userId = userId;
154-
pluvConfig.userName = userName;
155-
pluvConfig.authUrl = props.pluvAuthUrl || "/api/auth/pluv";
156-
pluvConfig.publicKey = props.pluvPublicKey || "";
134+
// ─── Component ───────────────────────────────────────────────────────────────
157135

136+
let ChatBoxV2Tmp = (function () {
137+
return new UICompBuilder(childrenMap, (props) => {
158138
return (
159-
<PluvRoomProvider
160-
room={roomName}
161-
initialPresence={{ typing: null } as any}
162-
initialStorage={(t: any) => ({
163-
rooms: t.map("rooms", []),
164-
members: t.map("members", []),
165-
invites: t.map("invites", []),
166-
messages: t.map("messages", []),
167-
})}
168-
onAuthorizationFail={(error: Error) => {
169-
console.error("[PluvChat] Auth failed:", error);
170-
}}
171-
>
172-
<ChatBoxView
173-
{...props}
174-
dispatch={dispatch}
175-
onConversationHistoryChange={onConversationHistoryChange}
176-
systemPrompt={props.systemPrompt}
177-
llmBotName={props.llmBotName}
178-
/>
179-
</PluvRoomProvider>
139+
<ChatBoxView
140+
chatTitle={props.chatTitle}
141+
showHeader={props.showHeader}
142+
messages={props.messages}
143+
currentUserId={props.currentUserId}
144+
currentUserName={props.currentUserName}
145+
typingUsers={props.typingUsers}
146+
lastSentMessageText={props.lastSentMessageText}
147+
messageText={props.messageText}
148+
style={props.style}
149+
animationStyle={props.animationStyle}
150+
onEvent={props.onEvent}
151+
/>
180152
);
181153
})
182-
.setPropertyViewFn((children) => <ChatBoxPropertyView children={children} />)
154+
.setPropertyViewFn((children) => (
155+
<ChatBoxPropertyView children={children} />
156+
))
183157
.build();
184158
})();
185159

@@ -189,28 +163,12 @@ ChatBoxV2Tmp = class extends ChatBoxV2Tmp {
189163
}
190164
};
191165

192-
ChatBoxV2Tmp = withMethodExposing(ChatBoxV2Tmp, [
193-
{
194-
method: {
195-
name: "setUser",
196-
description: "Update the current chat user",
197-
params: [
198-
{ name: "userId", type: "string" },
199-
{ name: "userName", type: "string" },
200-
],
201-
},
202-
execute: (comp: any, values: any[]) => {
203-
if (values[0]) comp.children.userId.getView().onChange(values[0]);
204-
if (values[1]) comp.children.userName.getView().onChange(values[1]);
205-
},
206-
},
207-
]);
208-
209166
export const ChatBoxV2Comp = withExposingConfigs(ChatBoxV2Tmp, [
210-
new NameConfig("chatName", "Chat display name"),
211-
new NameConfig("userId", "Current user ID"),
212-
new NameConfig("userName", "Current user name"),
213-
new NameConfig("applicationId", "Application scope"),
214-
new NameConfig("llmConversationHistory", "Conversation history for the active LLM room (role + content array)"),
167+
new NameConfig("chatTitle", "Chat display title"),
168+
new NameConfig(
169+
"lastSentMessageText",
170+
"Text of the last message sent by the user — use in your save query",
171+
),
172+
new NameConfig("messageText", "Current text in the message input"),
215173
NameConfigHidden,
216174
]);

0 commit comments

Comments
 (0)