@autobe/ui 0.30.0-dev.20260315 → 0.30.0

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 (86) hide show
  1. package/LICENSE +661 -661
  2. package/lib/components/AutoBeChatMain.js +5 -5
  3. package/lib/components/AutoBeConfigModal.js +9 -9
  4. package/package.json +2 -2
  5. package/src/components/AutoBeAssistantMessageMovie.tsx +22 -22
  6. package/src/components/AutoBeChatMain.tsx +376 -376
  7. package/src/components/AutoBeChatSidebar.tsx +414 -414
  8. package/src/components/AutoBeConfigButton.tsx +83 -83
  9. package/src/components/AutoBeConfigModal.tsx +443 -443
  10. package/src/components/AutoBeStatusButton.tsx +75 -75
  11. package/src/components/AutoBeStatusModal.tsx +486 -486
  12. package/src/components/AutoBeUserMessageMovie.tsx +27 -27
  13. package/src/components/common/ActionButton.tsx +205 -205
  14. package/src/components/common/ActionButtonGroup.tsx +80 -80
  15. package/src/components/common/AutoBeConfigInput.tsx +185 -185
  16. package/src/components/common/ChatBubble.tsx +119 -119
  17. package/src/components/common/Collapsible.tsx +95 -95
  18. package/src/components/common/CompactSessionIndicator.tsx +73 -73
  19. package/src/components/common/CompactSessionList.tsx +82 -82
  20. package/src/components/common/index.ts +8 -8
  21. package/src/components/common/openai/OpenAIContent.tsx +53 -53
  22. package/src/components/common/openai/OpenAIUserAudioContent.tsx +70 -70
  23. package/src/components/common/openai/OpenAIUserFileContent.tsx +76 -76
  24. package/src/components/common/openai/OpenAIUserImageContent.tsx +34 -34
  25. package/src/components/common/openai/OpenAIUserTextContent.tsx +15 -15
  26. package/src/components/common/openai/index.ts +5 -5
  27. package/src/components/events/AutoBeCompleteEventMovie.tsx +402 -402
  28. package/src/components/events/AutoBeCorrectEventMovie.tsx +354 -354
  29. package/src/components/events/AutoBeEventGroupMovie.tsx +18 -18
  30. package/src/components/events/AutoBeEventMovie.tsx +158 -158
  31. package/src/components/events/AutoBeProgressEventMovie.tsx +217 -217
  32. package/src/components/events/AutoBeScenarioEventMovie.tsx +135 -135
  33. package/src/components/events/AutoBeStartEventMovie.tsx +82 -82
  34. package/src/components/events/AutoBeValidateEventMovie.tsx +249 -249
  35. package/src/components/events/README.md +300 -300
  36. package/src/components/events/common/CollapsibleEventGroup.tsx +211 -211
  37. package/src/components/events/common/EventCard.tsx +61 -61
  38. package/src/components/events/common/EventContent.tsx +31 -31
  39. package/src/components/events/common/EventHeader.tsx +85 -85
  40. package/src/components/events/common/EventIcon.tsx +82 -82
  41. package/src/components/events/common/ProgressBar.tsx +64 -64
  42. package/src/components/events/common/index.ts +13 -13
  43. package/src/components/events/groups/CorrectEventGroup.tsx +183 -183
  44. package/src/components/events/groups/ValidateEventGroup.tsx +143 -143
  45. package/src/components/events/groups/index.ts +8 -8
  46. package/src/components/events/index.ts +16 -16
  47. package/src/components/events/utils/eventGrouper.tsx +116 -116
  48. package/src/components/events/utils/index.ts +1 -1
  49. package/src/components/index.ts +13 -13
  50. package/src/components/upload/AutoBeChatUploadBox.tsx +425 -425
  51. package/src/components/upload/AutoBeChatUploadSendButton.tsx +66 -66
  52. package/src/components/upload/AutoBeFileUploadBox.tsx +123 -123
  53. package/src/components/upload/AutoBeUploadConfig.ts +5 -5
  54. package/src/components/upload/AutoBeVoiceRecoderButton.tsx +100 -100
  55. package/src/components/upload/index.ts +5 -5
  56. package/src/constant/color.ts +28 -28
  57. package/src/context/AutoBeAgentContext.tsx +245 -245
  58. package/src/context/AutoBeAgentSessionList.tsx +58 -58
  59. package/src/context/SearchParamsContext.tsx +49 -49
  60. package/src/hooks/index.ts +3 -3
  61. package/src/hooks/useEscapeKey.ts +24 -24
  62. package/src/hooks/useIsomorphicLayoutEffect.ts +8 -8
  63. package/src/hooks/useMediaQuery.ts +73 -73
  64. package/src/hooks/useSessionStorage.ts +10 -10
  65. package/src/icons/Receipt.tsx +74 -74
  66. package/src/index.ts +9 -9
  67. package/src/strategy/AutoBeAgentSessionStorageStrategy.ts +127 -127
  68. package/src/structure/AutoBeListener.ts +373 -373
  69. package/src/structure/AutoBeListenerState.ts +53 -53
  70. package/src/structure/IAutoBeAgentSessionStorageStrategy.ts +87 -87
  71. package/src/structure/IAutoBeEventGroup.ts +6 -6
  72. package/src/structure/index.ts +4 -4
  73. package/src/types/config.ts +44 -44
  74. package/src/types/index.ts +1 -1
  75. package/src/utils/AutoBeFileUploader.ts +279 -279
  76. package/src/utils/AutoBeVoiceRecorder.ts +95 -95
  77. package/src/utils/__tests__/crypto.test.ts +286 -286
  78. package/src/utils/__tests__/storage.test.ts +229 -229
  79. package/src/utils/crypto.ts +95 -95
  80. package/src/utils/index.ts +6 -6
  81. package/src/utils/number.ts +17 -17
  82. package/src/utils/storage.ts +96 -96
  83. package/src/utils/time.ts +14 -14
  84. package/tsconfig.json +9 -9
  85. package/vitest.config.ts +15 -15
  86. package/README.md +0 -261
@@ -1,376 +1,376 @@
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 { 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;