@autobe/ui 0.30.2 → 0.30.4-dev.20260324

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/LICENSE +661 -661
  2. package/README.md +261 -0
  3. package/lib/components/AutoBeChatMain.d.ts +6 -0
  4. package/lib/components/AutoBeChatMain.js +46 -37
  5. package/lib/components/AutoBeChatMain.js.map +1 -1
  6. package/lib/components/AutoBeConfigModal.js +9 -9
  7. package/lib/components/events/AutoBeEventMovie.js +0 -4
  8. package/lib/components/events/AutoBeEventMovie.js.map +1 -1
  9. package/lib/components/events/AutoBeProgressEventMovie.js +0 -10
  10. package/lib/components/events/AutoBeProgressEventMovie.js.map +1 -1
  11. package/lib/components/upload/AutoBeChatUploadBox.d.ts +1 -0
  12. package/lib/components/upload/AutoBeChatUploadBox.js +11 -16
  13. package/lib/components/upload/AutoBeChatUploadBox.js.map +1 -1
  14. package/lib/context/AutoBeAgentContext.js +78 -30
  15. package/lib/context/AutoBeAgentContext.js.map +1 -1
  16. package/lib/context/SearchParamsContext.js +1 -1
  17. package/lib/context/SearchParamsContext.js.map +1 -1
  18. package/lib/structure/AutoBeListener.d.ts +1 -0
  19. package/lib/structure/AutoBeListener.js +9 -11
  20. package/lib/structure/AutoBeListener.js.map +1 -1
  21. package/package.json +2 -2
  22. package/src/components/AutoBeAssistantMessageMovie.tsx +22 -22
  23. package/src/components/AutoBeChatMain.tsx +394 -376
  24. package/src/components/AutoBeChatSidebar.tsx +414 -414
  25. package/src/components/AutoBeConfigButton.tsx +83 -83
  26. package/src/components/AutoBeConfigModal.tsx +443 -443
  27. package/src/components/AutoBeStatusButton.tsx +75 -75
  28. package/src/components/AutoBeStatusModal.tsx +486 -486
  29. package/src/components/AutoBeUserMessageMovie.tsx +27 -27
  30. package/src/components/common/ActionButton.tsx +205 -205
  31. package/src/components/common/ActionButtonGroup.tsx +80 -80
  32. package/src/components/common/AutoBeConfigInput.tsx +185 -185
  33. package/src/components/common/ChatBubble.tsx +119 -119
  34. package/src/components/common/Collapsible.tsx +95 -95
  35. package/src/components/common/CompactSessionIndicator.tsx +73 -73
  36. package/src/components/common/CompactSessionList.tsx +82 -82
  37. package/src/components/common/openai/OpenAIContent.tsx +53 -53
  38. package/src/components/common/openai/OpenAIUserAudioContent.tsx +70 -70
  39. package/src/components/common/openai/OpenAIUserFileContent.tsx +76 -76
  40. package/src/components/common/openai/OpenAIUserImageContent.tsx +34 -34
  41. package/src/components/common/openai/OpenAIUserTextContent.tsx +15 -15
  42. package/src/components/events/AutoBeCompleteEventMovie.tsx +402 -402
  43. package/src/components/events/AutoBeCorrectEventMovie.tsx +354 -354
  44. package/src/components/events/AutoBeEventGroupMovie.tsx +18 -18
  45. package/src/components/events/AutoBeEventMovie.tsx +154 -158
  46. package/src/components/events/AutoBeProgressEventMovie.tsx +207 -217
  47. package/src/components/events/AutoBeScenarioEventMovie.tsx +135 -135
  48. package/src/components/events/AutoBeStartEventMovie.tsx +82 -82
  49. package/src/components/events/AutoBeValidateEventMovie.tsx +249 -249
  50. package/src/components/events/README.md +300 -300
  51. package/src/components/events/common/CollapsibleEventGroup.tsx +211 -211
  52. package/src/components/events/common/EventCard.tsx +61 -61
  53. package/src/components/events/common/EventContent.tsx +31 -31
  54. package/src/components/events/common/EventHeader.tsx +85 -85
  55. package/src/components/events/common/EventIcon.tsx +82 -82
  56. package/src/components/events/common/ProgressBar.tsx +64 -64
  57. package/src/components/events/groups/CorrectEventGroup.tsx +183 -183
  58. package/src/components/events/groups/ValidateEventGroup.tsx +143 -143
  59. package/src/components/events/utils/eventGrouper.tsx +116 -116
  60. package/src/components/upload/AutoBeChatUploadBox.tsx +433 -425
  61. package/src/components/upload/AutoBeChatUploadSendButton.tsx +66 -66
  62. package/src/components/upload/AutoBeFileUploadBox.tsx +123 -123
  63. package/src/components/upload/AutoBeVoiceRecoderButton.tsx +100 -100
  64. package/src/context/AutoBeAgentContext.tsx +301 -245
  65. package/src/context/AutoBeAgentSessionList.tsx +58 -58
  66. package/src/context/SearchParamsContext.tsx +49 -49
  67. package/src/icons/Receipt.tsx +74 -74
  68. package/src/structure/AutoBeListener.ts +368 -373
  69. package/tsconfig.json +9 -9
@@ -1,376 +1,394 @@
1
- import { AutoBeUserConversateContent } from "@autobe/interface";
2
- import { OverlayProvider, overlay } from "overlay-kit";
3
- import { RefObject, useEffect, useRef } from "react";
4
-
5
- import { AutoBeChatUploadBox, useAutoBeAgentSessionList } from "..";
6
- import { useAutoBeAgent } from "../context/AutoBeAgentContext";
7
- import { useMediaQuery } from "../hooks";
8
- import {
9
- DEFAULT_CONFIG,
10
- IAutoBeConfig,
11
- IAutoBePartialConfig,
12
- } from "../types/config";
13
- import { getEncryptedSessionStorage } from "../utils/storage";
14
- import AutoBeConfigButton from "./AutoBeConfigButton";
15
- import AutoBeConfigModal, { IConfigField } from "./AutoBeConfigModal";
16
- import AutoBeStatusButton from "./AutoBeStatusButton";
17
- import AutoBeEventGroupMovie from "./events/AutoBeEventGroupMovie";
18
-
19
- export interface IAutoBeChatMainProps {
20
- isUnusedConfig?: boolean;
21
- isReplay?: boolean;
22
- isMobile: boolean;
23
- setError: (error: Error) => void;
24
- className?: string;
25
- style?: React.CSSProperties;
26
- configFields?: IConfigField[];
27
-
28
- /** Additional required config fields beyond openApiKey */
29
- requiredFields?: string[];
30
- }
31
-
32
- export const AutoBeChatMain = (props: IAutoBeChatMainProps) => {
33
- const bodyContainerRef = useRef<HTMLDivElement>(null);
34
- const scrollAnchorRef = useRef<HTMLDivElement>(null);
35
- const { eventGroups, getAutoBeService, connectionStatus } = useAutoBeAgent();
36
- const { refreshSessionList } = useAutoBeAgentSessionList();
37
- const listener: RefObject<AutoBeChatUploadBox.IListener> = useRef({
38
- handleDragEnter: () => {},
39
- handleDragLeave: () => {},
40
- handleDrop: () => {},
41
- handleDragOver: () => {},
42
- });
43
-
44
- // Simplified config reader
45
- const getCurrentConfig = (): IAutoBeConfig => {
46
- const config: IAutoBePartialConfig = {};
47
-
48
- props.configFields?.forEach((field) => {
49
- const value = field.encrypted
50
- ? getEncryptedSessionStorage(field.storageKey)
51
- : localStorage.getItem(field.storageKey) || "";
52
-
53
- if (field.type === "checkbox") {
54
- config[field.key] = String(value) === "true";
55
- } else if (field.type === "number") {
56
- config[field.key] = parseInt(String(value)) || 0;
57
- } else {
58
- config[field.key] = String(value);
59
- }
60
- });
61
-
62
- return { ...DEFAULT_CONFIG, ...config };
63
- };
64
-
65
- // Check if required config is available
66
- const hasRequiredConfig = (): boolean => {
67
- const config = getCurrentConfig();
68
-
69
- // Check additional required fields from props
70
- if (props.requiredFields) {
71
- for (const field of props.requiredFields) {
72
- if (!config[field]) {
73
- return false;
74
- }
75
- }
76
- }
77
-
78
- return true;
79
- };
80
-
81
- // Unified service connection handler
82
- const conversate = async (
83
- messages: AutoBeUserConversateContent[],
84
- ): Promise<void> => {
85
- // Check if we have required config
86
- if (props.isUnusedConfig === false && !hasRequiredConfig()) {
87
- overlay.open(({ isOpen, close }) => (
88
- <AutoBeConfigModal
89
- isOpen={isOpen}
90
- onClose={close}
91
- title="Server Connection Required"
92
- fields={props.configFields || []}
93
- onSave={() => {
94
- conversate(messages);
95
- }}
96
- />
97
- ));
98
- }
99
-
100
- // Connect to service
101
- try {
102
- const config = getCurrentConfig();
103
- const serviceData = await getAutoBeService(config);
104
- if (messages.length !== 0) {
105
- await new Promise((resolve) => {
106
- if (serviceData.listener.getEnable() === true) {
107
- resolve(void 0);
108
- }
109
- serviceData.listener.onEnable(async (value) => {
110
- if (value === true) {
111
- resolve(void 0);
112
- }
113
- });
114
- });
115
- await serviceData.service.conversate(messages);
116
- }
117
- if (eventGroups.length === 0) {
118
- refreshSessionList();
119
- }
120
- } catch (error) {
121
- console.error("Failed to connect:", error);
122
- props.setError(error as Error);
123
- }
124
- };
125
-
126
- // Auto-scroll when new events arrive
127
- useEffect(() => {
128
- if (eventGroups.length > 0) {
129
- scrollAnchorRef.current?.scrollIntoView({ behavior: "smooth" });
130
- }
131
- }, [eventGroups.length]);
132
-
133
- // Auto-connect if there are existing conversations and config is ready
134
- useEffect(() => {
135
- if (props.isReplay === true) {
136
- conversate([]);
137
- return;
138
- }
139
-
140
- if (
141
- eventGroups.length > 0 &&
142
- hasRequiredConfig() &&
143
- connectionStatus === "disconnected"
144
- ) {
145
- conversate([]);
146
- return;
147
- }
148
- }, [connectionStatus, eventGroups.length]);
149
-
150
- return (
151
- <OverlayProvider>
152
- <div
153
- onDragEnter={(e) => listener.current.handleDragEnter(e)}
154
- onDragLeave={(e) => listener.current.handleDragLeave(e)}
155
- onDragOver={(e) => listener.current.handleDragOver(e)}
156
- onDrop={(e) => listener.current.handleDrop(e)}
157
- style={{
158
- position: "relative",
159
- overflowY: "auto",
160
- margin: 0,
161
- flexGrow: 1,
162
- display: "flex",
163
- flexDirection: "column",
164
- ...props.style,
165
- }}
166
- className={props.className}
167
- ref={bodyContainerRef}
168
- >
169
- {/* Control Buttons & Status - Sticky position in top right */}
170
- <div
171
- style={{
172
- position: "sticky",
173
- top: "1rem",
174
- zIndex: 1001,
175
- display: "flex",
176
- justifyContent: "flex-end",
177
- alignItems: "center",
178
- gap: "0.5rem",
179
- marginBottom: "-3rem",
180
- paddingRight: "1.5rem",
181
- }}
182
- >
183
- {/* Connection Status Indicator */}
184
- {connectionStatus === "disconnected" && (
185
- <div
186
- style={{
187
- background: "#f8d7da",
188
- color: "#721c24",
189
- border: "1px solid #f5c6cb",
190
- borderRadius: "50px",
191
- padding: "0.4rem 0.8rem",
192
- fontSize: "0.8rem",
193
- fontWeight: "500",
194
- display: "flex",
195
- alignItems: "center",
196
- gap: "0.4rem",
197
- boxShadow: "0 2px 4px rgba(0, 0, 0, 0.1)",
198
- }}
199
- >
200
- <div
201
- style={{
202
- width: "6px",
203
- height: "6px",
204
- backgroundColor: "#dc3545",
205
- borderRadius: "50%",
206
- animation: "pulse 2s infinite",
207
- }}
208
- ></div>
209
- Disconnected
210
- </div>
211
- )}
212
-
213
- {connectionStatus === "connecting" && (
214
- <div
215
- style={{
216
- background: "#fff3cd",
217
- color: "#856404",
218
- border: "1px solid #ffeaa7",
219
- borderRadius: "50px",
220
- padding: "0.4rem 0.8rem",
221
- fontSize: "0.8rem",
222
- fontWeight: "500",
223
- display: "flex",
224
- alignItems: "center",
225
- gap: "0.4rem",
226
- boxShadow: "0 2px 4px rgba(0, 0, 0, 0.1)",
227
- }}
228
- >
229
- <div
230
- style={{
231
- width: "6px",
232
- height: "6px",
233
- backgroundColor: "#f39c12",
234
- borderRadius: "50%",
235
- animation: "pulse 1.5s infinite",
236
- }}
237
- ></div>
238
- Connecting...
239
- </div>
240
- )}
241
-
242
- {connectionStatus === "connected" && (
243
- <div
244
- style={{
245
- background: "#d4edda",
246
- color: "#155724",
247
- border: "1px solid #c3e6cb",
248
- borderRadius: "50px",
249
- padding: "0.4rem 0.8rem",
250
- fontSize: "0.8rem",
251
- fontWeight: "500",
252
- display: "flex",
253
- alignItems: "center",
254
- gap: "0.4rem",
255
- boxShadow: "0 2px 4px rgba(0, 0, 0, 0.1)",
256
- }}
257
- >
258
- <div
259
- style={{
260
- width: "6px",
261
- height: "6px",
262
- backgroundColor: "#28a745",
263
- borderRadius: "50%",
264
- animation: "pulse 1.5s infinite",
265
- }}
266
- ></div>
267
- Connected
268
- </div>
269
- )}
270
-
271
- <style>
272
- {`
273
- @keyframes pulse {
274
- 0%, 100% { opacity: 1; }
275
- 50% { opacity: 0.5; }
276
- }
277
- `}
278
- </style>
279
-
280
- {props.isUnusedConfig === false &&
281
- props.configFields?.length != null &&
282
- props.configFields.length > 0 && (
283
- <AutoBeConfigButton fields={props.configFields || []} />
284
- )}
285
- <AutoBeStatusButton />
286
- </div>
287
- <div
288
- style={{
289
- display: "flex",
290
- flexDirection: "column",
291
- maxWidth: useMediaQuery.WIDTH_MD,
292
- width: "100%",
293
- margin: "0 auto",
294
- }}
295
- >
296
- <div
297
- style={{
298
- padding: "2rem",
299
- gap: 16,
300
- display: "flex",
301
- flexDirection: "column",
302
- }}
303
- >
304
- {connectionStatus === "disconnected" && (
305
- <div
306
- style={{
307
- display: "flex",
308
- flexDirection: "column",
309
- justifyContent: "center",
310
- alignItems: "center",
311
- padding: "3rem",
312
- color: "#666",
313
- textAlign: "center",
314
- gap: "1rem",
315
- }}
316
- >
317
- <div style={{ fontSize: "3rem" }}>⚙️</div>
318
- <div style={{ fontSize: "1.25rem", fontWeight: "600" }}>
319
- Configuration Required
320
- </div>
321
- <div
322
- style={{
323
- fontSize: "1rem",
324
- maxWidth: "400px",
325
- lineHeight: "1.5",
326
- }}
327
- >
328
- Please click the settings button ⚙️ to configure your server
329
- connection and API credentials, or start typing to begin
330
- setup.
331
- </div>
332
- </div>
333
- )}
334
-
335
- {connectionStatus === "connected" && (
336
- <AutoBeEventGroupMovie eventGroups={eventGroups} />
337
- )}
338
- </div>
339
- </div>
340
-
341
- {/*
342
- * Prompt input area
343
- * this flexGrow: 1 means that the prompt input area will take up the remaining space
344
- * so that the upload box will be at the bottom of the screen
345
- */}
346
- <div
347
- style={{ flexGrow: 1, minHeight: "1rem" }}
348
- ref={scrollAnchorRef}
349
- ></div>
350
- <div
351
- style={{
352
- position: "sticky",
353
- bottom: 16,
354
- left: 0,
355
- right: 0,
356
- zIndex: 1000,
357
- }}
358
- >
359
- <AutoBeChatUploadBox
360
- listener={listener}
361
- uploadConfig={
362
- getCurrentConfig().supportAudioEnable
363
- ? {
364
- supportAudio: true,
365
- }
366
- : undefined
367
- }
368
- conversate={conversate}
369
- setError={props.setError}
370
- />
371
- </div>
372
- </div>
373
- </OverlayProvider>
374
- );
375
- };
376
- export default AutoBeChatMain;
1
+ import { AutoBeUserConversateContent } from "@autobe/interface";
2
+ import { OverlayProvider, overlay } from "overlay-kit";
3
+ import { RefObject, useEffect, useRef } from "react";
4
+
5
+ import { AutoBeChatUploadBox, useAutoBeAgentSessionList } from "..";
6
+ import { useAutoBeAgent } from "../context/AutoBeAgentContext";
7
+ import { useSearchParams } from "../context/SearchParamsContext";
8
+ import { useMediaQuery } from "../hooks";
9
+ import {
10
+ DEFAULT_CONFIG,
11
+ IAutoBeConfig,
12
+ IAutoBePartialConfig,
13
+ } from "../types/config";
14
+ import { getEncryptedSessionStorage } from "../utils/storage";
15
+ import AutoBeConfigButton from "./AutoBeConfigButton";
16
+ import AutoBeConfigModal, { IConfigField } from "./AutoBeConfigModal";
17
+ import AutoBeStatusButton from "./AutoBeStatusButton";
18
+ import AutoBeEventGroupMovie from "./events/AutoBeEventGroupMovie";
19
+
20
+ export interface IAutoBeChatMainProps {
21
+ isUnusedConfig?: boolean;
22
+ isReplay?: boolean;
23
+ isMobile: boolean;
24
+ setError: (error: Error) => void;
25
+ className?: string;
26
+ style?: React.CSSProperties;
27
+ configFields?: IConfigField[];
28
+
29
+ /** Additional required config fields beyond openApiKey */
30
+ requiredFields?: string[];
31
+
32
+ /** Custom content to render when disconnected instead of the default placeholder */
33
+ disconnectedContent?: React.ReactNode;
34
+
35
+ /** Hide the status info button (e.g. when a persistent status panel is shown) */
36
+ hideStatusButton?: boolean;
37
+
38
+ /** Disable the chat input (e.g. when required config like vendor/model is not selected) */
39
+ chatDisabled?: boolean;
40
+ }
41
+
42
+ export const AutoBeChatMain = (props: IAutoBeChatMainProps) => {
43
+ const bodyContainerRef = useRef<HTMLDivElement>(null);
44
+ const scrollAnchorRef = useRef<HTMLDivElement>(null);
45
+ const { eventGroups, getAutoBeService, connectionStatus } = useAutoBeAgent();
46
+ const { refreshSessionList } = useAutoBeAgentSessionList();
47
+ const { searchParams } = useSearchParams();
48
+ const activeSessionId = searchParams.get("session-id");
49
+ const listener: RefObject<AutoBeChatUploadBox.IListener> = useRef({
50
+ handleDragEnter: () => {},
51
+ handleDragLeave: () => {},
52
+ handleDrop: () => {},
53
+ handleDragOver: () => {},
54
+ });
55
+
56
+ // Simplified config reader
57
+ const getCurrentConfig = (): IAutoBeConfig => {
58
+ const config: IAutoBePartialConfig = {};
59
+
60
+ props.configFields?.forEach((field) => {
61
+ const value = field.encrypted
62
+ ? getEncryptedSessionStorage(field.storageKey)
63
+ : localStorage.getItem(field.storageKey) || "";
64
+
65
+ if (field.type === "checkbox") {
66
+ config[field.key] = String(value) === "true";
67
+ } else if (field.type === "number") {
68
+ config[field.key] = parseInt(String(value)) || 0;
69
+ } else {
70
+ config[field.key] = String(value);
71
+ }
72
+ });
73
+
74
+ return { ...DEFAULT_CONFIG, ...config };
75
+ };
76
+
77
+ // Check if required config is available
78
+ const hasRequiredConfig = (): boolean => {
79
+ const config = getCurrentConfig();
80
+
81
+ // Check additional required fields from props
82
+ if (props.requiredFields) {
83
+ for (const field of props.requiredFields) {
84
+ if (!config[field]) {
85
+ return false;
86
+ }
87
+ }
88
+ }
89
+
90
+ return true;
91
+ };
92
+
93
+ // Unified service connection handler
94
+ const conversate = async (
95
+ messages: AutoBeUserConversateContent[],
96
+ ): Promise<void> => {
97
+ // Check if we have required config
98
+ if (props.isUnusedConfig === false && !hasRequiredConfig()) {
99
+ overlay.open(({ isOpen, close }) => (
100
+ <AutoBeConfigModal
101
+ isOpen={isOpen}
102
+ onClose={close}
103
+ title="Server Connection Required"
104
+ fields={props.configFields || []}
105
+ onSave={() => {
106
+ conversate(messages);
107
+ }}
108
+ />
109
+ ));
110
+ }
111
+
112
+ // Connect to service
113
+ try {
114
+ const config = getCurrentConfig();
115
+ const serviceData = await getAutoBeService(config);
116
+ if (messages.length !== 0) {
117
+ if (serviceData.listener.getEnable() !== true) {
118
+ await new Promise<void>((resolve) => {
119
+ const onEnable = async (value: boolean) => {
120
+ if (value === true) {
121
+ serviceData.listener.offEnable(onEnable);
122
+ resolve();
123
+ }
124
+ };
125
+ serviceData.listener.onEnable(onEnable);
126
+ });
127
+ }
128
+ await serviceData.service.conversate(messages);
129
+ }
130
+ if (eventGroups.length === 0) {
131
+ refreshSessionList();
132
+ }
133
+ } catch (error) {
134
+ console.error("Failed to connect:", error);
135
+ props.setError(error as Error);
136
+ }
137
+ };
138
+
139
+ // Auto-scroll when new events arrive
140
+ useEffect(() => {
141
+ if (eventGroups.length > 0) {
142
+ scrollAnchorRef.current?.scrollIntoView({ behavior: "smooth" });
143
+ }
144
+ }, [eventGroups.length]);
145
+
146
+ // Auto-connect: immediately when URL has session-id or on replay
147
+ useEffect(() => {
148
+ if (connectionStatus !== "disconnected") return;
149
+
150
+ if (props.isReplay === true) {
151
+ conversate([]);
152
+ return;
153
+ }
154
+
155
+ if (activeSessionId) {
156
+ conversate([]);
157
+ return;
158
+ }
159
+
160
+ if (eventGroups.length > 0 && hasRequiredConfig()) {
161
+ conversate([]);
162
+ }
163
+ }, [connectionStatus, activeSessionId, eventGroups.length]);
164
+
165
+ return (
166
+ <OverlayProvider>
167
+ <div
168
+ onDragEnter={(e) => listener.current.handleDragEnter(e)}
169
+ onDragLeave={(e) => listener.current.handleDragLeave(e)}
170
+ onDragOver={(e) => listener.current.handleDragOver(e)}
171
+ onDrop={(e) => listener.current.handleDrop(e)}
172
+ style={{
173
+ position: "relative",
174
+ overflowY: "auto",
175
+ margin: 0,
176
+ flexGrow: 1,
177
+ display: "flex",
178
+ flexDirection: "column",
179
+ ...props.style,
180
+ }}
181
+ className={props.className}
182
+ ref={bodyContainerRef}
183
+ >
184
+ {/* Control Buttons & Status - Sticky position in top right */}
185
+ <div
186
+ style={{
187
+ position: "sticky",
188
+ top: "1rem",
189
+ zIndex: 1001,
190
+ display: "flex",
191
+ justifyContent: "flex-end",
192
+ alignItems: "center",
193
+ gap: "0.5rem",
194
+ marginBottom: "-3rem",
195
+ paddingRight: "1.5rem",
196
+ }}
197
+ >
198
+ {/* Connection Status Indicator */}
199
+ {connectionStatus === "disconnected" && (
200
+ <div
201
+ style={{
202
+ background: "#f8d7da",
203
+ color: "#721c24",
204
+ border: "1px solid #f5c6cb",
205
+ borderRadius: "50px",
206
+ padding: "0.4rem 0.8rem",
207
+ fontSize: "0.8rem",
208
+ fontWeight: "500",
209
+ display: "flex",
210
+ alignItems: "center",
211
+ gap: "0.4rem",
212
+ boxShadow: "0 2px 4px rgba(0, 0, 0, 0.1)",
213
+ }}
214
+ >
215
+ <div
216
+ style={{
217
+ width: "6px",
218
+ height: "6px",
219
+ backgroundColor: "#dc3545",
220
+ borderRadius: "50%",
221
+ animation: "pulse 2s infinite",
222
+ }}
223
+ ></div>
224
+ Disconnected
225
+ </div>
226
+ )}
227
+
228
+ {connectionStatus === "connecting" && (
229
+ <div
230
+ style={{
231
+ background: "#fff3cd",
232
+ color: "#856404",
233
+ border: "1px solid #ffeaa7",
234
+ borderRadius: "50px",
235
+ padding: "0.4rem 0.8rem",
236
+ fontSize: "0.8rem",
237
+ fontWeight: "500",
238
+ display: "flex",
239
+ alignItems: "center",
240
+ gap: "0.4rem",
241
+ boxShadow: "0 2px 4px rgba(0, 0, 0, 0.1)",
242
+ }}
243
+ >
244
+ <div
245
+ style={{
246
+ width: "6px",
247
+ height: "6px",
248
+ backgroundColor: "#f39c12",
249
+ borderRadius: "50%",
250
+ animation: "pulse 1.5s infinite",
251
+ }}
252
+ ></div>
253
+ Connecting...
254
+ </div>
255
+ )}
256
+
257
+ {connectionStatus === "connected" && (
258
+ <div
259
+ style={{
260
+ background: "#d4edda",
261
+ color: "#155724",
262
+ border: "1px solid #c3e6cb",
263
+ borderRadius: "50px",
264
+ padding: "0.4rem 0.8rem",
265
+ fontSize: "0.8rem",
266
+ fontWeight: "500",
267
+ display: "flex",
268
+ alignItems: "center",
269
+ gap: "0.4rem",
270
+ boxShadow: "0 2px 4px rgba(0, 0, 0, 0.1)",
271
+ }}
272
+ >
273
+ <div
274
+ style={{
275
+ width: "6px",
276
+ height: "6px",
277
+ backgroundColor: "#28a745",
278
+ borderRadius: "50%",
279
+ animation: "pulse 1.5s infinite",
280
+ }}
281
+ ></div>
282
+ Connected
283
+ </div>
284
+ )}
285
+
286
+ <style>
287
+ {`
288
+ @keyframes pulse {
289
+ 0%, 100% { opacity: 1; }
290
+ 50% { opacity: 0.5; }
291
+ }
292
+ `}
293
+ </style>
294
+
295
+ {props.isUnusedConfig === false &&
296
+ props.configFields?.length != null &&
297
+ props.configFields.length > 0 && (
298
+ <AutoBeConfigButton fields={props.configFields || []} />
299
+ )}
300
+ {!props.hideStatusButton && <AutoBeStatusButton />}
301
+ </div>
302
+ <div
303
+ style={{
304
+ display: "flex",
305
+ flexDirection: "column",
306
+ maxWidth: useMediaQuery.WIDTH_MD,
307
+ width: "100%",
308
+ margin: "0 auto",
309
+ }}
310
+ >
311
+ <div
312
+ style={{
313
+ padding: "2rem",
314
+ gap: 16,
315
+ display: "flex",
316
+ flexDirection: "column",
317
+ }}
318
+ >
319
+ {connectionStatus === "disconnected" &&
320
+ eventGroups.length === 0 &&
321
+ (props.disconnectedContent ?? (
322
+ <div
323
+ style={{
324
+ display: "flex",
325
+ flexDirection: "column",
326
+ justifyContent: "center",
327
+ alignItems: "center",
328
+ padding: "3rem",
329
+ color: "#666",
330
+ textAlign: "center",
331
+ gap: "1rem",
332
+ }}
333
+ >
334
+ <div style={{ fontSize: "3rem" }}>⚙️</div>
335
+ <div style={{ fontSize: "1.25rem", fontWeight: "600" }}>
336
+ Configuration Required
337
+ </div>
338
+ <div
339
+ style={{
340
+ fontSize: "1rem",
341
+ maxWidth: "400px",
342
+ lineHeight: "1.5",
343
+ }}
344
+ >
345
+ Please click the settings button ⚙️ to configure your
346
+ server connection and API credentials, or start typing to
347
+ begin setup.
348
+ </div>
349
+ </div>
350
+ ))}
351
+
352
+ {eventGroups.length > 0 && (
353
+ <AutoBeEventGroupMovie eventGroups={eventGroups} />
354
+ )}
355
+ </div>
356
+ </div>
357
+
358
+ {/*
359
+ * Prompt input area
360
+ * this flexGrow: 1 means that the prompt input area will take up the remaining space
361
+ * so that the upload box will be at the bottom of the screen
362
+ */}
363
+ <div
364
+ style={{ flexGrow: 1, minHeight: "1rem" }}
365
+ ref={scrollAnchorRef}
366
+ ></div>
367
+ <div
368
+ style={{
369
+ position: "sticky",
370
+ bottom: 16,
371
+ left: 0,
372
+ right: 0,
373
+ zIndex: 1000,
374
+ }}
375
+ >
376
+ <AutoBeChatUploadBox
377
+ listener={listener}
378
+ uploadConfig={
379
+ getCurrentConfig().supportAudioEnable
380
+ ? {
381
+ supportAudio: true,
382
+ }
383
+ : undefined
384
+ }
385
+ conversate={conversate}
386
+ setError={props.setError}
387
+ disabled={props.chatDisabled}
388
+ />
389
+ </div>
390
+ </div>
391
+ </OverlayProvider>
392
+ );
393
+ };
394
+ export default AutoBeChatMain;