@autobe/ui 0.19.1 → 0.21.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 (177) hide show
  1. package/lib/AutoBeChatMain.d.ts +16 -0
  2. package/lib/AutoBeChatMain.js +51 -0
  3. package/lib/AutoBeChatMain.js.map +1 -0
  4. package/lib/banner/AutoBeAgentInformation.d.ts +15 -0
  5. package/lib/banner/AutoBeAgentInformation.js +41 -0
  6. package/lib/banner/AutoBeAgentInformation.js.map +1 -0
  7. package/lib/banner/AutoBeChatBanner.d.ts +15 -0
  8. package/lib/banner/AutoBeChatBanner.js +29 -0
  9. package/lib/banner/AutoBeChatBanner.js.map +1 -0
  10. package/lib/banner/AutoBeChatState.d.ts +6 -0
  11. package/lib/banner/AutoBeChatState.js +80 -0
  12. package/lib/banner/AutoBeChatState.js.map +1 -0
  13. package/lib/banner/AutoBeTokenUsage.d.ts +14 -0
  14. package/lib/banner/AutoBeTokenUsage.js +60 -0
  15. package/lib/banner/AutoBeTokenUsage.js.map +1 -0
  16. package/lib/banner/index.d.ts +4 -0
  17. package/lib/banner/index.js +21 -0
  18. package/lib/banner/index.js.map +1 -0
  19. package/lib/common/Collapsible.d.ts +15 -0
  20. package/lib/common/Collapsible.js +45 -0
  21. package/lib/common/Collapsible.js.map +1 -0
  22. package/lib/common/index.d.ts +2 -0
  23. package/lib/common/index.js +19 -0
  24. package/lib/common/index.js.map +1 -0
  25. package/lib/constant/color.d.ts +18 -0
  26. package/lib/constant/color.js +25 -0
  27. package/lib/constant/color.js.map +1 -0
  28. package/lib/events/AutoBeCompleteEventMovie.d.ts +7 -0
  29. package/lib/events/AutoBeCompleteEventMovie.js +210 -0
  30. package/lib/events/AutoBeCompleteEventMovie.js.map +1 -0
  31. package/lib/events/AutoBeEventMovie.d.ts +8 -0
  32. package/lib/events/AutoBeEventMovie.js +84 -0
  33. package/lib/events/AutoBeEventMovie.js.map +1 -0
  34. package/lib/events/AutoBeProgressEventMovie.js +2 -61
  35. package/lib/events/AutoBeProgressEventMovie.js.map +1 -1
  36. package/lib/events/AutoBeScenarioEventMovie.js +2 -44
  37. package/lib/events/AutoBeScenarioEventMovie.js.map +1 -1
  38. package/lib/events/AutoBeValidateEventMovie.d.ts +6 -0
  39. package/lib/events/AutoBeValidateEventMovie.js +115 -0
  40. package/lib/events/AutoBeValidateEventMovie.js.map +1 -0
  41. package/lib/events/common/CollapsibleEventGroup.d.ts +28 -0
  42. package/lib/events/common/CollapsibleEventGroup.js +89 -0
  43. package/lib/events/common/CollapsibleEventGroup.js.map +1 -0
  44. package/lib/events/common/EventCard.d.ts +13 -0
  45. package/lib/events/common/EventCard.js +43 -0
  46. package/lib/events/common/EventCard.js.map +1 -0
  47. package/lib/events/common/EventContent.d.ts +11 -0
  48. package/lib/events/common/EventContent.js +14 -0
  49. package/lib/events/common/EventContent.js.map +1 -0
  50. package/lib/events/common/EventHeader.d.ts +15 -0
  51. package/lib/events/common/EventHeader.js +41 -0
  52. package/lib/events/common/EventHeader.js.map +1 -0
  53. package/lib/events/common/EventIcon.d.ts +11 -0
  54. package/lib/events/common/EventIcon.js +50 -0
  55. package/lib/events/common/EventIcon.js.map +1 -0
  56. package/lib/events/common/ProgressBar.d.ts +14 -0
  57. package/lib/events/common/ProgressBar.js +33 -0
  58. package/lib/events/common/ProgressBar.js.map +1 -0
  59. package/lib/events/common/index.d.ts +6 -0
  60. package/lib/events/common/index.js +16 -0
  61. package/lib/events/common/index.js.map +1 -0
  62. package/lib/events/groups/ValidateEventGroup.d.ts +12 -0
  63. package/lib/events/groups/ValidateEventGroup.js +78 -0
  64. package/lib/events/groups/ValidateEventGroup.js.map +1 -0
  65. package/lib/events/groups/index.d.ts +1 -0
  66. package/lib/events/groups/index.js +6 -0
  67. package/lib/events/groups/index.js.map +1 -0
  68. package/lib/events/index.d.ts +6 -0
  69. package/lib/events/index.js +27 -1
  70. package/lib/events/index.js.map +1 -1
  71. package/lib/events/utils/eventGrouper.d.ts +20 -0
  72. package/lib/events/utils/eventGrouper.js +74 -0
  73. package/lib/events/utils/eventGrouper.js.map +1 -0
  74. package/lib/events/utils/index.d.ts +1 -0
  75. package/lib/events/utils/index.js +6 -0
  76. package/lib/events/utils/index.js.map +1 -0
  77. package/lib/hooks/index.d.ts +2 -0
  78. package/lib/hooks/index.js +19 -0
  79. package/lib/hooks/index.js.map +1 -0
  80. package/lib/hooks/useIsomorphicLayoutEffect.d.ts +6 -0
  81. package/lib/hooks/useIsomorphicLayoutEffect.js +10 -0
  82. package/lib/hooks/useIsomorphicLayoutEffect.js.map +1 -0
  83. package/lib/hooks/useMediaQuery.d.ts +11 -0
  84. package/lib/hooks/useMediaQuery.js +52 -0
  85. package/lib/hooks/useMediaQuery.js.map +1 -0
  86. package/lib/index.d.ts +5 -0
  87. package/lib/index.js +8 -1
  88. package/lib/index.js.map +1 -1
  89. package/lib/structure/AutoBeListener.d.ts +17 -0
  90. package/lib/structure/AutoBeListener.js +250 -0
  91. package/lib/structure/AutoBeListener.js.map +1 -0
  92. package/lib/structure/AutoBeListenerState.d.ts +14 -0
  93. package/lib/structure/AutoBeListenerState.js +39 -0
  94. package/lib/structure/AutoBeListenerState.js.map +1 -0
  95. package/lib/structure/IAutoBeEventGroup.d.ts +5 -0
  96. package/lib/structure/IAutoBeEventGroup.js +3 -0
  97. package/lib/structure/IAutoBeEventGroup.js.map +1 -0
  98. package/lib/structure/index.d.ts +3 -0
  99. package/lib/structure/index.js +20 -0
  100. package/lib/structure/index.js.map +1 -0
  101. package/lib/upload/AutoBeChatUploadBox.d.ts +31 -0
  102. package/lib/upload/AutoBeChatUploadBox.js +221 -0
  103. package/lib/upload/AutoBeChatUploadBox.js.map +1 -0
  104. package/lib/upload/AutoBeChatUploadSendButton.d.ts +15 -0
  105. package/lib/upload/AutoBeChatUploadSendButton.js +38 -0
  106. package/lib/upload/AutoBeChatUploadSendButton.js.map +1 -0
  107. package/lib/upload/AutoBeFileUploadBox.d.ts +8 -0
  108. package/lib/upload/AutoBeFileUploadBox.js +68 -0
  109. package/lib/upload/AutoBeFileUploadBox.js.map +1 -0
  110. package/lib/upload/AutoBeUploadConfig.d.ts +9 -0
  111. package/lib/upload/AutoBeUploadConfig.js +3 -0
  112. package/lib/upload/AutoBeUploadConfig.js.map +1 -0
  113. package/lib/upload/AutoBeVoiceRecoderButton.d.ts +11 -0
  114. package/lib/upload/AutoBeVoiceRecoderButton.js +58 -0
  115. package/lib/upload/AutoBeVoiceRecoderButton.js.map +1 -0
  116. package/lib/upload/index.d.ts +5 -0
  117. package/lib/upload/index.js +22 -0
  118. package/lib/upload/index.js.map +1 -0
  119. package/lib/utils/AutoBeFileUploader.d.ts +28 -0
  120. package/lib/utils/AutoBeFileUploader.js +237 -0
  121. package/lib/utils/AutoBeFileUploader.js.map +1 -0
  122. package/lib/utils/AutoBeVoiceRecorder.d.ts +7 -0
  123. package/lib/utils/AutoBeVoiceRecorder.js +94 -0
  124. package/lib/utils/AutoBeVoiceRecorder.js.map +1 -0
  125. package/lib/utils/index.d.ts +4 -0
  126. package/lib/utils/index.js +21 -0
  127. package/lib/utils/index.js.map +1 -0
  128. package/lib/utils/number.d.ts +1 -0
  129. package/lib/utils/number.js +20 -0
  130. package/lib/utils/number.js.map +1 -0
  131. package/package.json +13 -2
  132. package/src/AutoBeChatMain.tsx +123 -0
  133. package/src/banner/AutoBeAgentInformation.tsx +102 -0
  134. package/src/banner/AutoBeChatBanner.tsx +72 -0
  135. package/src/banner/AutoBeChatState.tsx +152 -0
  136. package/src/banner/AutoBeTokenUsage.tsx +152 -0
  137. package/src/banner/index.ts +4 -0
  138. package/src/common/Collapsible.tsx +95 -0
  139. package/src/common/index.ts +2 -0
  140. package/src/constant/color.ts +24 -0
  141. package/src/events/AutoBeCompleteEventMovie.tsx +402 -0
  142. package/src/events/AutoBeEventMovie.tsx +114 -0
  143. package/src/events/AutoBeProgressEventMovie.tsx +12 -125
  144. package/src/events/AutoBeScenarioEventMovie.tsx +5 -93
  145. package/src/events/AutoBeValidateEventMovie.tsx +326 -0
  146. package/src/events/README.md +300 -0
  147. package/src/events/common/CollapsibleEventGroup.tsx +220 -0
  148. package/src/events/common/EventCard.tsx +61 -0
  149. package/src/events/common/EventContent.tsx +31 -0
  150. package/src/events/common/EventHeader.tsx +85 -0
  151. package/src/events/common/EventIcon.tsx +82 -0
  152. package/src/events/common/ProgressBar.tsx +63 -0
  153. package/src/events/common/index.ts +13 -0
  154. package/src/events/groups/ValidateEventGroup.tsx +150 -0
  155. package/src/events/groups/index.ts +4 -0
  156. package/src/events/index.ts +12 -0
  157. package/src/events/utils/eventGrouper.tsx +118 -0
  158. package/src/events/utils/index.ts +1 -0
  159. package/src/hooks/index.ts +2 -0
  160. package/src/hooks/useIsomorphicLayoutEffect.ts +8 -0
  161. package/src/hooks/useMediaQuery.ts +68 -0
  162. package/src/index.ts +5 -0
  163. package/src/structure/AutoBeListener.ts +263 -0
  164. package/src/structure/AutoBeListenerState.ts +53 -0
  165. package/src/structure/IAutoBeEventGroup.ts +6 -0
  166. package/src/structure/index.ts +3 -0
  167. package/src/upload/AutoBeChatUploadBox.tsx +372 -0
  168. package/src/upload/AutoBeChatUploadSendButton.tsx +66 -0
  169. package/src/upload/AutoBeFileUploadBox.tsx +123 -0
  170. package/src/upload/AutoBeUploadConfig.ts +5 -0
  171. package/src/upload/AutoBeVoiceRecoderButton.tsx +100 -0
  172. package/src/upload/index.ts +5 -0
  173. package/src/utils/AutoBeFileUploader.ts +279 -0
  174. package/src/utils/AutoBeVoiceRecorder.ts +95 -0
  175. package/src/utils/index.ts +4 -0
  176. package/src/utils/number.ts +17 -0
  177. package/tsconfig.json +2 -1
@@ -0,0 +1,263 @@
1
+ import { AutoBeEvent, IAutoBeRpcListener } from "@autobe/interface";
2
+ import { List } from "tstl";
3
+
4
+ import { AutoBeListenerState } from "./AutoBeListenerState";
5
+ import { IAutoBeEventGroup } from "./IAutoBeEventGroup";
6
+
7
+ export class AutoBeListener {
8
+ private callback_:
9
+ | ((eventGroups: IAutoBeEventGroup[]) => Promise<void>)
10
+ | null;
11
+ private listener_: Required<IAutoBeRpcListener>;
12
+ private events_: List<IAutoBeEventGroup> = new List();
13
+ private dict_: Map<AutoBeEvent.Type, List.Iterator<IAutoBeEventGroup>> =
14
+ new Map();
15
+ private readonly state_: AutoBeListenerState;
16
+
17
+ public constructor() {
18
+ this.callback_ = null;
19
+
20
+ this.state_ = new AutoBeListenerState();
21
+ this.listener_ = {
22
+ assistantMessage: async (event) => {
23
+ this.insert(event);
24
+ },
25
+ userMessage: async (event) => {
26
+ this.insert(event);
27
+ },
28
+
29
+ // ANALYZE
30
+ analyzeStart: async (event) => {
31
+ this.dict_.delete("analyzeWrite");
32
+ this.dict_.delete("analyzeReview");
33
+ this.insert(event);
34
+ },
35
+ analyzeScenario: async (event) => {
36
+ this.accumulate(event);
37
+ },
38
+ analyzeWrite: async (event) => {
39
+ this.accumulate(event);
40
+ },
41
+ analyzeReview: async (event) => {
42
+ this.accumulate(event);
43
+ },
44
+ analyzeComplete: async (event) => {
45
+ this.dict_.delete("analyzeWrite");
46
+ this.dict_.delete("analyzeReview");
47
+ this.state_.setAnalyze(event);
48
+ this.insert(event);
49
+ },
50
+
51
+ // PRISMA
52
+ prismaStart: async (event) => {
53
+ this.dict_.delete("prismaSchemas");
54
+ this.dict_.delete("prismaReview");
55
+ this.insert(event);
56
+ },
57
+ prismaComponents: async (event) => {
58
+ this.insert(event);
59
+ },
60
+ prismaSchemas: async (event) => {
61
+ this.accumulate(event);
62
+ },
63
+ prismaInsufficient: async (event) => {
64
+ this.insert(event);
65
+ },
66
+ prismaReview: async (event) => {
67
+ this.accumulate(event);
68
+ },
69
+ prismaValidate: async (event) => {
70
+ this.insert(event);
71
+ },
72
+ prismaCorrect: async (event) => {
73
+ this.insert(event);
74
+ },
75
+ prismaComplete: async (event) => {
76
+ this.dict_.delete("prismaSchemas");
77
+ this.dict_.delete("prismaReview");
78
+ this.state_.setPrisma(event);
79
+ this.insert(event);
80
+ },
81
+
82
+ // INTERFACE
83
+ interfaceStart: async (event) => {
84
+ this.dict_.delete("interfaceAuthorization");
85
+ this.dict_.delete("interfaceEndpoints");
86
+ this.dict_.delete("interfaceOperations");
87
+ this.dict_.delete("interfaceOperationsReview");
88
+ this.dict_.delete("interfaceSchemas");
89
+ this.dict_.delete("interfaceSchemasReview");
90
+ this.insert(event);
91
+ },
92
+ interfaceGroups: async (event) => {
93
+ this.insert(event);
94
+ },
95
+ interfaceEndpoints: async (event) => {
96
+ this.accumulate(event);
97
+ },
98
+ interfaceEndpointsReview: async (event) => {
99
+ this.accumulate(event);
100
+ },
101
+ interfaceOperations: async (event) => {
102
+ this.accumulate(event);
103
+ },
104
+ interfaceOperationsReview: async (event) => {
105
+ this.accumulate(event);
106
+ },
107
+ interfaceAuthorization: async (event) => {
108
+ this.accumulate(event);
109
+ },
110
+ interfaceSchemas: async (event) => {
111
+ this.accumulate(event);
112
+ },
113
+ interfaceSchemasReview: async (event) => {
114
+ this.accumulate(event);
115
+ },
116
+ interfaceComplement: async (event) => {
117
+ this.insert(event);
118
+ },
119
+ interfaceComplete: async (event) => {
120
+ this.dict_.delete("interfaceEndpoints");
121
+ this.dict_.delete("interfaceOperations");
122
+ this.dict_.delete("interfaceOperationsReview");
123
+ this.dict_.delete("interfaceAuthorization");
124
+ this.dict_.delete("interfaceSchemas");
125
+ this.dict_.delete("interfaceSchemasReview");
126
+ this.state_.setInterface(event);
127
+ this.insert(event);
128
+ },
129
+
130
+ // TEST
131
+ testStart: async (event) => {
132
+ this.dict_.delete("testScenarios");
133
+ this.dict_.delete("testWrite");
134
+ this.dict_.delete("testValidate");
135
+ this.dict_.delete("testCorrect");
136
+ this.insert(event);
137
+ },
138
+ testScenarios: async (event) => {
139
+ this.accumulate(event);
140
+ },
141
+ testWrite: async (event) => {
142
+ this.accumulate(event);
143
+ },
144
+ testValidate: async (event) => {
145
+ this.accumulate(event);
146
+ },
147
+ testCorrect: async (event) => {
148
+ this.accumulate(event);
149
+ },
150
+ testComplete: async (event) => {
151
+ this.dict_.delete("testScenarios");
152
+ this.dict_.delete("testWrite");
153
+ this.dict_.delete("testValidate");
154
+ this.dict_.delete("testCorrect");
155
+ this.state_.setTest(event);
156
+ this.insert(event);
157
+ },
158
+
159
+ //----
160
+ // REALIZE
161
+ //----
162
+ // REALIZE-MAIN
163
+ realizeStart: async (event) => {
164
+ this.dict_.delete("realizeWrite");
165
+ this.dict_.delete("realizeValidate");
166
+ this.insert(event);
167
+ },
168
+ realizeWrite: async (event) => {
169
+ this.accumulate(event);
170
+ },
171
+ realizeCorrect: async (event) => {
172
+ this.accumulate(event);
173
+ },
174
+
175
+ realizeValidate: async (event) => {
176
+ this.accumulate(event);
177
+ },
178
+ realizeComplete: async (event) => {
179
+ this.dict_.delete("realizeWrite");
180
+ this.dict_.delete("realizeValidate");
181
+ this.state_.setRealize(event);
182
+ this.insert(event);
183
+ },
184
+ // REALIZE-AUTHORIZATION
185
+ realizeAuthorizationStart: async (event) => {
186
+ this.dict_.delete("realizeAuthorizationWrite");
187
+ this.dict_.delete("realizeAuthorizationCorrect");
188
+ this.dict_.delete("realizeAuthorizationValidate");
189
+ this.insert(event);
190
+ },
191
+ realizeAuthorizationWrite: async (event) => {
192
+ this.accumulate(event);
193
+ },
194
+ realizeAuthorizationValidate: async (event) => {
195
+ this.accumulate(event);
196
+ },
197
+ realizeAuthorizationCorrect: async (event) => {
198
+ this.insert(event);
199
+ },
200
+ realizeAuthorizationComplete: async (event) => {
201
+ this.dict_.delete("realizeAuthorizationWrite");
202
+ this.dict_.delete("realizeAuthorizationCorrect");
203
+ this.dict_.delete("realizeAuthorizationValidate");
204
+ this.insert(event);
205
+ },
206
+ // REALILZE-TEST
207
+ realizeTestStart: async (event) => {
208
+ this.dict_.delete("realizeTestOperation");
209
+ this.insert(event);
210
+ },
211
+ realizeTestReset: async (event) => {
212
+ this.insert(event);
213
+ },
214
+ realizeTestOperation: async (event) => {
215
+ this.accumulate(event);
216
+ },
217
+ realizeTestComplete: async (event) => {
218
+ this.dict_.delete("realizeTestOperation");
219
+ this.insert(event);
220
+ },
221
+ };
222
+ }
223
+
224
+ public on(callback: (eventGroups: IAutoBeEventGroup[]) => Promise<void>) {
225
+ this.callback_ = callback;
226
+ }
227
+
228
+ public getListener(): Required<IAutoBeRpcListener> {
229
+ return this.listener_;
230
+ }
231
+
232
+ public getState(): AutoBeListenerState {
233
+ return this.state_;
234
+ }
235
+
236
+ private accumulate(event: AutoBeEvent) {
237
+ const it: List.Iterator<IAutoBeEventGroup> | undefined = this.dict_.get(
238
+ event.type,
239
+ );
240
+ if (it === undefined)
241
+ this.dict_.set(
242
+ event.type,
243
+ this.events_.insert(this.events_.end(), {
244
+ type: event.type,
245
+ events: [event],
246
+ }),
247
+ );
248
+ else it.value.events.push(event);
249
+ this.dispatch();
250
+ }
251
+
252
+ private insert(event: AutoBeEvent) {
253
+ this.events_.push_back({
254
+ type: event.type,
255
+ events: [event],
256
+ });
257
+ this.dispatch();
258
+ }
259
+
260
+ private dispatch() {
261
+ this.callback_?.(this.events_.toJSON()).catch(() => {});
262
+ }
263
+ }
@@ -0,0 +1,53 @@
1
+ import {
2
+ AutoBeAnalyzeCompleteEvent,
3
+ AutoBeInterfaceCompleteEvent,
4
+ AutoBePrismaCompleteEvent,
5
+ AutoBeRealizeCompleteEvent,
6
+ AutoBeTestCompleteEvent,
7
+ } from "@autobe/interface";
8
+
9
+ export class AutoBeListenerState {
10
+ public analyze: AutoBeAnalyzeCompleteEvent | null;
11
+ public prisma: AutoBePrismaCompleteEvent | null;
12
+ public interface: AutoBeInterfaceCompleteEvent | null;
13
+ public test: AutoBeTestCompleteEvent | null;
14
+ public realize: AutoBeRealizeCompleteEvent | null;
15
+
16
+ public constructor() {
17
+ this.analyze = null;
18
+ this.prisma = null;
19
+ this.interface = null;
20
+ this.test = null;
21
+ this.realize = null;
22
+ }
23
+
24
+ public setAnalyze(event: AutoBeAnalyzeCompleteEvent): void {
25
+ this.analyze = event;
26
+ this.prisma = null;
27
+ this.interface = null;
28
+ this.test = null;
29
+ this.realize = null;
30
+ }
31
+
32
+ public setPrisma(event: AutoBePrismaCompleteEvent): void {
33
+ this.prisma = event;
34
+ this.interface = null;
35
+ this.test = null;
36
+ this.realize = null;
37
+ }
38
+
39
+ public setInterface(event: AutoBeInterfaceCompleteEvent): void {
40
+ this.interface = event;
41
+ this.test = null;
42
+ this.realize = null;
43
+ }
44
+
45
+ public setTest(event: AutoBeTestCompleteEvent): void {
46
+ this.test = event;
47
+ this.realize = null;
48
+ }
49
+
50
+ public setRealize(event: AutoBeRealizeCompleteEvent): void {
51
+ this.realize = event;
52
+ }
53
+ }
@@ -0,0 +1,6 @@
1
+ import { AutoBeEvent } from "@autobe/interface";
2
+
3
+ export interface IAutoBeEventGroup<Event extends AutoBeEvent = AutoBeEvent> {
4
+ type: Event["type"];
5
+ events: Array<Event>;
6
+ }
@@ -0,0 +1,3 @@
1
+ export * from "./AutoBeListener";
2
+ export * from "./AutoBeListenerState";
3
+ export * from "./IAutoBeEventGroup";
@@ -0,0 +1,372 @@
1
+ import { AutoBeUserMessageContent } from "@autobe/interface";
2
+ import {
3
+ AutoBeUserMessageAudioContent,
4
+ AutoBeUserMessageFileContent,
5
+ AutoBeUserMessageImageContent,
6
+ } from "@autobe/interface";
7
+ import { ReactNode, RefObject, useEffect, useRef, useState } from "react";
8
+
9
+ import {
10
+ AutoBeChatUploadSendButton,
11
+ AutoBeFileUploadBox,
12
+ AutoBeVoiceRecoderButton,
13
+ } from ".";
14
+ import { AutoBeFileUploader } from "../utils";
15
+
16
+ export interface IAutoBeBucket {
17
+ file: File;
18
+ content:
19
+ | AutoBeUserMessageAudioContent
20
+ | AutoBeUserMessageFileContent
21
+ | AutoBeUserMessageImageContent;
22
+ }
23
+
24
+ export interface IAutoBeChatUploadConfig {
25
+ supportAudio?: boolean;
26
+ file?: (file: File) => Promise<{ id: string }>;
27
+ image?: (file: File) => Promise<{ url: string }>;
28
+ }
29
+
30
+ export const AutoBeChatUploadBox = (props: AutoBeChatUploadBox.IProps) => {
31
+ const inputRef = useRef<HTMLTextAreaElement>(null);
32
+ const fileInputRef = useRef<HTMLInputElement>(null);
33
+
34
+ const [dragging, setDragging] = useState(false);
35
+ const [enabled, setEnabled] = useState(true);
36
+ const [text, setText] = useState("");
37
+ const [buckets, setBuckets] = useState<IAutoBeBucket[]>([]);
38
+ const [extensionError, setExtensionError] = useState<ReactNode | null>(null);
39
+
40
+ const [emptyText, setEmptyText] = useState(false);
41
+
42
+ const removeFile = (index: number) => {
43
+ setBuckets(buckets.filter((_, i) => i !== index));
44
+ };
45
+
46
+ const conversate = async () => {
47
+ if (enabled === false) return;
48
+
49
+ if (text.trim().length === 0 && buckets.length === 0) {
50
+ setEmptyText(true);
51
+ return;
52
+ }
53
+
54
+ const messages = [
55
+ {
56
+ type: "text",
57
+ text: text.trim(),
58
+ },
59
+ ...buckets.map(({ content }) => content),
60
+ ] as AutoBeUserMessageContent[];
61
+
62
+ setEnabled(false);
63
+ setEmptyText(false);
64
+ setText("");
65
+ setBuckets([]);
66
+
67
+ try {
68
+ await props.conversate(messages);
69
+ } catch (error) {
70
+ props.setError(
71
+ error instanceof Error ? error : new Error("Unknown error"),
72
+ );
73
+ }
74
+ setEnabled(true);
75
+ };
76
+
77
+ const handleFileSelect = async (fileList: FileList | null) => {
78
+ if (!fileList) return;
79
+
80
+ setEnabled(false);
81
+ setExtensionError(null);
82
+
83
+ const newFiles: IAutoBeBucket[] = [];
84
+ const errorFileNames: string[] = [];
85
+
86
+ for (const file of fileList) {
87
+ try {
88
+ newFiles.push(
89
+ await AutoBeFileUploader.compose(props.uploadConfig ?? {}, file),
90
+ );
91
+ } catch (error) {
92
+ errorFileNames.push(file.name);
93
+ }
94
+ }
95
+ if (errorFileNames.length > 0) {
96
+ const extensions: string[] = Array.from(
97
+ new Set(errorFileNames.map((n) => n.split(".").pop() ?? "unknown")),
98
+ ).sort();
99
+ setExtensionError(
100
+ <>
101
+ <h2>
102
+ Unsupported extension{extensions.length > 1 ? "s" : ""}: (
103
+ {extensions.join(", ")})
104
+ </h2>
105
+ <ul>
106
+ {errorFileNames.map((name) => (
107
+ <li key={name}>{name}</li>
108
+ ))}
109
+ </ul>
110
+ </>,
111
+ );
112
+ setTimeout(() => setExtensionError(null), 5_000);
113
+ }
114
+ setBuckets((o) => [...o, ...newFiles]);
115
+ setEnabled(true);
116
+ };
117
+
118
+ const handleDragEnter = (e: React.DragEvent) => {
119
+ e.preventDefault();
120
+ e.stopPropagation();
121
+ setDragging(true);
122
+ };
123
+
124
+ const handleDragLeave = (e: React.DragEvent) => {
125
+ e.preventDefault();
126
+ e.stopPropagation();
127
+ // Only set dragging to false if we're leaving the entire container
128
+ const rect = e.currentTarget.getBoundingClientRect();
129
+ const x = e.clientX;
130
+ const y = e.clientY;
131
+ if (x < rect.left || x >= rect.right || y < rect.top || y >= rect.bottom) {
132
+ setDragging(false);
133
+ }
134
+ };
135
+
136
+ const handleDragOver = (e: React.DragEvent) => {
137
+ e.preventDefault();
138
+ e.stopPropagation();
139
+ };
140
+
141
+ const handleDrop = async (e: React.DragEvent) => {
142
+ e.preventDefault();
143
+ e.stopPropagation();
144
+ setDragging(false);
145
+
146
+ const files = e.dataTransfer.files;
147
+ await handleFileSelect(files);
148
+ };
149
+
150
+ useEffect(() => {
151
+ if (!props.listener?.current) return;
152
+ props.listener.current.handleDragEnter = handleDragEnter;
153
+ props.listener.current.handleDragLeave = handleDragLeave;
154
+ props.listener.current.handleDrop = handleDrop;
155
+ props.listener.current.handleDragOver = handleDragOver;
156
+ }, [props.listener]);
157
+
158
+ return (
159
+ <div
160
+ style={{
161
+ maxWidth: "768px",
162
+ margin: "0 auto",
163
+ padding: "12px",
164
+ borderRadius: "16px",
165
+ border: dragging ? "3px solid #1976d2" : "2px solid #e0e0e0",
166
+ backgroundColor: dragging
167
+ ? "rgba(25, 118, 210, 0.04)"
168
+ : "rgba(255, 255, 255, 0.95)",
169
+ backdropFilter: "blur(10px)",
170
+ transition: "all 0.2s",
171
+ position: "relative",
172
+ boxShadow: "0px 8px 24px rgba(0, 0, 0, 0.12)",
173
+ }}
174
+ >
175
+ {dragging ? (
176
+ <div
177
+ style={{
178
+ display: "flex",
179
+ alignItems: "center",
180
+ justifyContent: "center",
181
+ minHeight: "120px",
182
+ paddingTop: "32px",
183
+ paddingBottom: "32px",
184
+ }}
185
+ >
186
+ <h6
187
+ style={{
188
+ color: "#1976d2",
189
+ fontWeight: "bold",
190
+ textAlign: "center",
191
+ fontSize: "1.25rem",
192
+ margin: 0,
193
+ }}
194
+ >
195
+ Drop files here to upload
196
+ </h6>
197
+ </div>
198
+ ) : null}
199
+
200
+ <div
201
+ style={{
202
+ display: dragging ? "none" : "flex",
203
+ flexDirection: "column",
204
+ gap: "8px",
205
+ }}
206
+ >
207
+ {buckets.length > 0 && (
208
+ <div
209
+ style={{
210
+ display: "flex",
211
+ flexWrap: "wrap",
212
+ gap: "4px",
213
+ }}
214
+ >
215
+ {buckets.map(({ file }, index) => (
216
+ <div
217
+ key={index}
218
+ style={{
219
+ display: "inline-flex",
220
+ alignItems: "center",
221
+ backgroundColor: "#f5f5f5",
222
+ borderRadius: "16px",
223
+ padding: "4px 8px",
224
+ fontSize: "0.875rem",
225
+ maxWidth: "200px",
226
+ border: "1px solid #e0e0e0",
227
+ }}
228
+ >
229
+ <span
230
+ style={{
231
+ overflow: "hidden",
232
+ textOverflow: "ellipsis",
233
+ whiteSpace: "nowrap",
234
+ marginRight: "4px",
235
+ }}
236
+ >
237
+ {file.name}
238
+ </span>
239
+ <button
240
+ style={{
241
+ background: "none",
242
+ border: "none",
243
+ cursor: "pointer",
244
+ padding: "2px",
245
+ display: "flex",
246
+ alignItems: "center",
247
+ justifyContent: "center",
248
+ borderRadius: "50%",
249
+ width: "16px",
250
+ height: "16px",
251
+ }}
252
+ onClick={() => removeFile(index)}
253
+ onMouseEnter={(e) => {
254
+ e.currentTarget.style.backgroundColor = "#f0f0f0";
255
+ }}
256
+ onMouseLeave={(e) => {
257
+ e.currentTarget.style.backgroundColor = "transparent";
258
+ }}
259
+ >
260
+ <svg
261
+ width="12"
262
+ height="12"
263
+ viewBox="0 0 24 24"
264
+ fill="currentColor"
265
+ >
266
+ <path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z" />
267
+ </svg>
268
+ </button>
269
+ </div>
270
+ ))}
271
+ </div>
272
+ )}
273
+ <textarea
274
+ ref={inputRef}
275
+ style={{
276
+ width: "100%",
277
+ minHeight: "40px",
278
+ maxHeight: "192px",
279
+ padding: "8px 12px",
280
+ border: `2px solid ${emptyText ? "#f44336" : dragging ? "#1976d2" : "#e0e0e0"}`,
281
+ borderRadius: "8px",
282
+ fontSize: "0.95rem",
283
+ fontFamily: "inherit",
284
+ resize: "none",
285
+ outline: "none",
286
+ color: dragging ? "#1976d2" : "inherit",
287
+ backgroundColor: "transparent",
288
+ transition: "border-color 0.2s",
289
+ }}
290
+ placeholder={
291
+ emptyText
292
+ ? "Cannot send empty message"
293
+ : dragging
294
+ ? "Drop files here..."
295
+ : "Conversate with AI Chatbot"
296
+ }
297
+ value={text}
298
+ onKeyDown={(e) => {
299
+ if (e.key === "Enter" && !e.shiftKey) {
300
+ e.preventDefault();
301
+ if (enabled) void conversate();
302
+ }
303
+ }}
304
+ onChange={(e) => setText(e.target.value)}
305
+ onFocus={(e) => {
306
+ e.currentTarget.style.borderColor = "#1976d2";
307
+ }}
308
+ onBlur={(e) => {
309
+ e.currentTarget.style.borderColor = emptyText
310
+ ? "#f44336"
311
+ : "#e0e0e0";
312
+ }}
313
+ />
314
+
315
+ <input
316
+ ref={fileInputRef}
317
+ type="file"
318
+ multiple
319
+ accept={AutoBeFileUploader.getAcceptAttribute(
320
+ props.uploadConfig?.supportAudio ?? false,
321
+ !!props.uploadConfig?.file,
322
+ )}
323
+ style={{ display: "none" }}
324
+ onChange={(e) => {
325
+ void handleFileSelect(e.target.files);
326
+ // Reset input to allow selecting the same file again
327
+ if (fileInputRef.current) fileInputRef.current.value = "";
328
+ }}
329
+ />
330
+
331
+ <div
332
+ style={{
333
+ display: "flex",
334
+ justifyContent: "space-between",
335
+ alignItems: "center",
336
+ }}
337
+ >
338
+ <AutoBeFileUploadBox
339
+ extensionError={extensionError}
340
+ onClick={() => fileInputRef.current?.click()}
341
+ enabled={enabled}
342
+ />
343
+ {props.uploadConfig?.supportAudio === true ? (
344
+ <AutoBeVoiceRecoderButton
345
+ enabled={enabled}
346
+ onComplete={(content) => setBuckets((o) => [...o, content])}
347
+ />
348
+ ) : null}
349
+ <AutoBeChatUploadSendButton
350
+ onClick={() => conversate()}
351
+ enabled={enabled}
352
+ />
353
+ </div>
354
+ </div>
355
+ </div>
356
+ );
357
+ };
358
+
359
+ export namespace AutoBeChatUploadBox {
360
+ export interface IProps {
361
+ listener: RefObject<IListener>;
362
+ uploadConfig?: IAutoBeChatUploadConfig;
363
+ conversate: (messages: AutoBeUserMessageContent[]) => Promise<void>;
364
+ setError: (error: Error) => void;
365
+ }
366
+ export interface IListener {
367
+ handleDragEnter: (event: React.DragEvent) => void;
368
+ handleDragLeave: (event: React.DragEvent) => void;
369
+ handleDragOver: (event: React.DragEvent) => void;
370
+ handleDrop: (event: React.DragEvent) => void;
371
+ }
372
+ }