@autobe/ui 0.20.0 → 0.22.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 (251) hide show
  1. package/lib/{AutoBeAssistantMessageMovie.d.ts → components/AutoBeAssistantMessageMovie.d.ts} +1 -1
  2. package/lib/{AutoBeAssistantMessageMovie.js → components/AutoBeAssistantMessageMovie.js} +3 -1
  3. package/lib/components/AutoBeAssistantMessageMovie.js.map +1 -0
  4. package/lib/components/AutoBeChatMain.d.ts +12 -0
  5. package/lib/components/AutoBeChatMain.js +88 -0
  6. package/lib/components/AutoBeChatMain.js.map +1 -0
  7. package/lib/components/AutoBeStatusModal.d.ts +6 -0
  8. package/lib/components/AutoBeStatusModal.js +269 -0
  9. package/lib/components/AutoBeStatusModal.js.map +1 -0
  10. package/lib/{AutoBeUserMessageMovie.d.ts → components/AutoBeUserMessageMovie.d.ts} +1 -1
  11. package/lib/{AutoBeUserMessageMovie.js → components/AutoBeUserMessageMovie.js} +3 -1
  12. package/lib/components/AutoBeUserMessageMovie.js.map +1 -0
  13. package/lib/{common → components/common}/ChatBubble.js +1 -1
  14. package/lib/components/common/ChatBubble.js.map +1 -0
  15. package/lib/components/common/Collapsible.d.ts +15 -0
  16. package/lib/components/common/Collapsible.js +45 -0
  17. package/lib/components/common/Collapsible.js.map +1 -0
  18. package/lib/components/common/index.d.ts +2 -0
  19. package/lib/components/common/index.js +19 -0
  20. package/lib/components/common/index.js.map +1 -0
  21. package/lib/components/common/openai/OpenAIContent.js.map +1 -0
  22. package/lib/components/common/openai/OpenAIUserAudioContent.js.map +1 -0
  23. package/lib/components/common/openai/OpenAIUserFileContent.js.map +1 -0
  24. package/lib/components/common/openai/OpenAIUserImageContent.js.map +1 -0
  25. package/lib/{common → components/common}/openai/OpenAIUserTextContent.js +0 -1
  26. package/lib/components/common/openai/OpenAIUserTextContent.js.map +1 -0
  27. package/lib/components/common/openai/index.js.map +1 -0
  28. package/lib/components/events/AutoBeCompleteEventMovie.js.map +1 -0
  29. package/lib/components/events/AutoBeCorrectEventMovie.d.ts +9 -0
  30. package/lib/components/events/AutoBeCorrectEventMovie.js +146 -0
  31. package/lib/components/events/AutoBeCorrectEventMovie.js.map +1 -0
  32. package/lib/components/events/AutoBeEventMovie.d.ts +7 -0
  33. package/lib/components/events/AutoBeEventMovie.js +93 -0
  34. package/lib/components/events/AutoBeEventMovie.js.map +1 -0
  35. package/lib/components/events/AutoBeProgressEventMovie.js.map +1 -0
  36. package/lib/components/events/AutoBeScenarioEventMovie.js.map +1 -0
  37. package/lib/components/events/AutoBeStartEventMovie.js.map +1 -0
  38. package/lib/{events → components/events}/AutoBeValidateEventMovie.js +2 -2
  39. package/lib/components/events/AutoBeValidateEventMovie.js.map +1 -0
  40. package/lib/{events → components/events}/common/CollapsibleEventGroup.js +1 -5
  41. package/lib/components/events/common/CollapsibleEventGroup.js.map +1 -0
  42. package/lib/components/events/common/EventCard.js.map +1 -0
  43. package/lib/components/events/common/EventContent.js.map +1 -0
  44. package/lib/{events → components/events}/common/EventHeader.js +1 -1
  45. package/lib/components/events/common/EventHeader.js.map +1 -0
  46. package/lib/components/events/common/EventIcon.js.map +1 -0
  47. package/lib/{events → components/events}/common/ProgressBar.js +3 -2
  48. package/lib/components/events/common/ProgressBar.js.map +1 -0
  49. package/lib/components/events/common/index.js.map +1 -0
  50. package/lib/components/events/groups/CorrectEventGroup.d.ts +12 -0
  51. package/lib/components/events/groups/CorrectEventGroup.js +83 -0
  52. package/lib/components/events/groups/CorrectEventGroup.js.map +1 -0
  53. package/lib/{events → components/events}/groups/ValidateEventGroup.js +3 -3
  54. package/lib/components/events/groups/ValidateEventGroup.js.map +1 -0
  55. package/lib/{events → components/events}/groups/index.d.ts +1 -0
  56. package/lib/components/events/groups/index.js +8 -0
  57. package/lib/components/events/groups/index.js.map +1 -0
  58. package/lib/{events → components/events}/index.d.ts +3 -1
  59. package/lib/{events → components/events}/index.js +7 -3
  60. package/lib/components/events/index.js.map +1 -0
  61. package/lib/components/events/utils/eventGrouper.js.map +1 -0
  62. package/lib/components/events/utils/index.js.map +1 -0
  63. package/lib/components/index.d.ts +7 -0
  64. package/lib/components/index.js +24 -0
  65. package/lib/components/index.js.map +1 -0
  66. package/lib/components/upload/AutoBeChatUploadBox.d.ts +31 -0
  67. package/lib/components/upload/AutoBeChatUploadBox.js +222 -0
  68. package/lib/components/upload/AutoBeChatUploadBox.js.map +1 -0
  69. package/lib/components/upload/AutoBeChatUploadSendButton.js.map +1 -0
  70. package/lib/components/upload/AutoBeFileUploadBox.d.ts +8 -0
  71. package/lib/components/upload/AutoBeFileUploadBox.js.map +1 -0
  72. package/lib/components/upload/AutoBeUploadConfig.d.ts +9 -0
  73. package/lib/components/upload/AutoBeUploadConfig.js +3 -0
  74. package/lib/components/upload/AutoBeUploadConfig.js.map +1 -0
  75. package/lib/{AutoBeVoiceRecoderButton.js → components/upload/AutoBeVoiceRecoderButton.js} +2 -2
  76. package/lib/components/upload/AutoBeVoiceRecoderButton.js.map +1 -0
  77. package/lib/components/upload/index.d.ts +5 -0
  78. package/lib/components/upload/index.js +22 -0
  79. package/lib/components/upload/index.js.map +1 -0
  80. package/lib/constant/color.d.ts +22 -0
  81. package/lib/constant/color.js +29 -0
  82. package/lib/constant/color.js.map +1 -0
  83. package/lib/context/AutoBeAgentContext.d.ts +20 -0
  84. package/lib/context/AutoBeAgentContext.js +50 -0
  85. package/lib/context/AutoBeAgentContext.js.map +1 -0
  86. package/lib/hooks/index.d.ts +3 -0
  87. package/lib/hooks/index.js +20 -0
  88. package/lib/hooks/index.js.map +1 -0
  89. package/lib/hooks/useEscapeKey.d.ts +7 -0
  90. package/lib/hooks/useEscapeKey.js +27 -0
  91. package/lib/hooks/useEscapeKey.js.map +1 -0
  92. package/lib/hooks/useIsomorphicLayoutEffect.d.ts +6 -0
  93. package/lib/hooks/useIsomorphicLayoutEffect.js +10 -0
  94. package/lib/hooks/useIsomorphicLayoutEffect.js.map +1 -0
  95. package/lib/hooks/useMediaQuery.d.ts +16 -0
  96. package/lib/hooks/useMediaQuery.js +57 -0
  97. package/lib/hooks/useMediaQuery.js.map +1 -0
  98. package/lib/icons/Receipt.d.ts +12 -0
  99. package/lib/icons/Receipt.js +9 -0
  100. package/lib/icons/Receipt.js.map +1 -0
  101. package/lib/index.d.ts +4 -8
  102. package/lib/index.js +4 -17
  103. package/lib/index.js.map +1 -1
  104. package/lib/structure/AutoBeListener.d.ts +17 -0
  105. package/lib/structure/AutoBeListener.js +250 -0
  106. package/lib/structure/AutoBeListener.js.map +1 -0
  107. package/lib/structure/AutoBeListenerState.d.ts +14 -0
  108. package/lib/structure/AutoBeListenerState.js +39 -0
  109. package/lib/structure/AutoBeListenerState.js.map +1 -0
  110. package/lib/structure/IAutoBeEventGroup.d.ts +5 -0
  111. package/lib/structure/IAutoBeEventGroup.js +3 -0
  112. package/lib/structure/IAutoBeEventGroup.js.map +1 -0
  113. package/lib/structure/index.d.ts +3 -0
  114. package/lib/structure/index.js +20 -0
  115. package/lib/structure/index.js.map +1 -0
  116. package/lib/utils/index.d.ts +1 -0
  117. package/lib/utils/index.js +1 -0
  118. package/lib/utils/index.js.map +1 -1
  119. package/lib/utils/number.d.ts +1 -0
  120. package/lib/utils/number.js +20 -0
  121. package/lib/utils/number.js.map +1 -0
  122. package/package.json +7 -11
  123. package/src/{AutoBeAssistantMessageMovie.tsx → components/AutoBeAssistantMessageMovie.tsx} +1 -1
  124. package/src/components/AutoBeChatMain.tsx +178 -0
  125. package/src/components/AutoBeStatusModal.tsx +483 -0
  126. package/src/{AutoBeUserMessageMovie.tsx → components/AutoBeUserMessageMovie.tsx} +3 -1
  127. package/src/{common → components/common}/ChatBubble.tsx +1 -1
  128. package/src/components/common/Collapsible.tsx +95 -0
  129. package/src/components/common/index.ts +2 -0
  130. package/src/{common → components/common}/openai/OpenAIUserTextContent.tsx +0 -1
  131. package/src/components/events/AutoBeCorrectEventMovie.tsx +368 -0
  132. package/src/components/events/AutoBeEventMovie.tsx +130 -0
  133. package/src/{events → components/events}/AutoBeValidateEventMovie.tsx +2 -2
  134. package/src/components/events/README.md +300 -0
  135. package/src/{events → components/events}/common/CollapsibleEventGroup.tsx +1 -10
  136. package/src/{events → components/events}/common/EventHeader.tsx +1 -1
  137. package/src/{events → components/events}/common/ProgressBar.tsx +3 -2
  138. package/src/components/events/groups/CorrectEventGroup.tsx +183 -0
  139. package/src/{events → components/events}/groups/ValidateEventGroup.tsx +3 -3
  140. package/src/{events → components/events}/groups/index.ts +4 -0
  141. package/src/{events → components/events}/index.ts +3 -4
  142. package/src/components/index.ts +7 -0
  143. package/src/components/upload/AutoBeChatUploadBox.tsx +374 -0
  144. package/src/{AutoBeFileUploadBox.tsx → components/upload/AutoBeFileUploadBox.tsx} +7 -8
  145. package/src/components/upload/AutoBeUploadConfig.ts +5 -0
  146. package/src/{AutoBeVoiceRecoderButton.tsx → components/upload/AutoBeVoiceRecoderButton.tsx} +2 -2
  147. package/src/components/upload/index.ts +5 -0
  148. package/src/constant/color.ts +28 -0
  149. package/src/context/AutoBeAgentContext.tsx +79 -0
  150. package/src/hooks/index.ts +3 -0
  151. package/src/hooks/useEscapeKey.ts +24 -0
  152. package/src/hooks/useIsomorphicLayoutEffect.ts +8 -0
  153. package/src/hooks/useMediaQuery.ts +73 -0
  154. package/src/icons/Receipt.tsx +74 -0
  155. package/src/index.ts +4 -14
  156. package/src/structure/AutoBeListener.ts +263 -0
  157. package/src/structure/AutoBeListenerState.ts +53 -0
  158. package/src/structure/IAutoBeEventGroup.ts +6 -0
  159. package/src/structure/index.ts +3 -0
  160. package/src/utils/index.ts +1 -0
  161. package/src/utils/number.ts +17 -0
  162. package/tsconfig.json +2 -1
  163. package/lib/AutoBeAssistantMessageMovie.js.map +0 -1
  164. package/lib/AutoBeChatUploadSendButton.js.map +0 -1
  165. package/lib/AutoBeFileUploadBox.d.ts +0 -10
  166. package/lib/AutoBeFileUploadBox.js.map +0 -1
  167. package/lib/AutoBeUserMessageMovie.js.map +0 -1
  168. package/lib/AutoBeVoiceRecoderButton.js.map +0 -1
  169. package/lib/common/ChatBubble.js.map +0 -1
  170. package/lib/common/openai/OpenAIContent.js.map +0 -1
  171. package/lib/common/openai/OpenAIUserAudioContent.js.map +0 -1
  172. package/lib/common/openai/OpenAIUserFileContent.js.map +0 -1
  173. package/lib/common/openai/OpenAIUserImageContent.js.map +0 -1
  174. package/lib/common/openai/OpenAIUserTextContent.js.map +0 -1
  175. package/lib/common/openai/index.js.map +0 -1
  176. package/lib/events/AutoBeCompleteEventMovie.js.map +0 -1
  177. package/lib/events/AutoBeProgressEventMovie.js.map +0 -1
  178. package/lib/events/AutoBeScenarioEventMovie.js.map +0 -1
  179. package/lib/events/AutoBeStartEventMovie.js.map +0 -1
  180. package/lib/events/AutoBeValidateEventMovie.js.map +0 -1
  181. package/lib/events/common/CollapsibleEventGroup.js.map +0 -1
  182. package/lib/events/common/EventCard.js.map +0 -1
  183. package/lib/events/common/EventContent.js.map +0 -1
  184. package/lib/events/common/EventHeader.js.map +0 -1
  185. package/lib/events/common/EventIcon.js.map +0 -1
  186. package/lib/events/common/ProgressBar.js.map +0 -1
  187. package/lib/events/common/index.js.map +0 -1
  188. package/lib/events/groups/ValidateEventGroup.js.map +0 -1
  189. package/lib/events/groups/index.js +0 -6
  190. package/lib/events/groups/index.js.map +0 -1
  191. package/lib/events/index.js.map +0 -1
  192. package/lib/events/utils/eventGrouper.js.map +0 -1
  193. package/lib/events/utils/index.js.map +0 -1
  194. package/src/events/README.md +0 -169
  195. /package/lib/{common → components/common}/ChatBubble.d.ts +0 -0
  196. /package/lib/{common → components/common}/openai/OpenAIContent.d.ts +0 -0
  197. /package/lib/{common → components/common}/openai/OpenAIContent.js +0 -0
  198. /package/lib/{common → components/common}/openai/OpenAIUserAudioContent.d.ts +0 -0
  199. /package/lib/{common → components/common}/openai/OpenAIUserAudioContent.js +0 -0
  200. /package/lib/{common → components/common}/openai/OpenAIUserFileContent.d.ts +0 -0
  201. /package/lib/{common → components/common}/openai/OpenAIUserFileContent.js +0 -0
  202. /package/lib/{common → components/common}/openai/OpenAIUserImageContent.d.ts +0 -0
  203. /package/lib/{common → components/common}/openai/OpenAIUserImageContent.js +0 -0
  204. /package/lib/{common → components/common}/openai/OpenAIUserTextContent.d.ts +0 -0
  205. /package/lib/{common → components/common}/openai/index.d.ts +0 -0
  206. /package/lib/{common → components/common}/openai/index.js +0 -0
  207. /package/lib/{events → components/events}/AutoBeCompleteEventMovie.d.ts +0 -0
  208. /package/lib/{events → components/events}/AutoBeCompleteEventMovie.js +0 -0
  209. /package/lib/{events → components/events}/AutoBeProgressEventMovie.d.ts +0 -0
  210. /package/lib/{events → components/events}/AutoBeProgressEventMovie.js +0 -0
  211. /package/lib/{events → components/events}/AutoBeScenarioEventMovie.d.ts +0 -0
  212. /package/lib/{events → components/events}/AutoBeScenarioEventMovie.js +0 -0
  213. /package/lib/{events → components/events}/AutoBeStartEventMovie.d.ts +0 -0
  214. /package/lib/{events → components/events}/AutoBeStartEventMovie.js +0 -0
  215. /package/lib/{events → components/events}/AutoBeValidateEventMovie.d.ts +0 -0
  216. /package/lib/{events → components/events}/common/CollapsibleEventGroup.d.ts +0 -0
  217. /package/lib/{events → components/events}/common/EventCard.d.ts +0 -0
  218. /package/lib/{events → components/events}/common/EventCard.js +0 -0
  219. /package/lib/{events → components/events}/common/EventContent.d.ts +0 -0
  220. /package/lib/{events → components/events}/common/EventContent.js +0 -0
  221. /package/lib/{events → components/events}/common/EventHeader.d.ts +0 -0
  222. /package/lib/{events → components/events}/common/EventIcon.d.ts +0 -0
  223. /package/lib/{events → components/events}/common/EventIcon.js +0 -0
  224. /package/lib/{events → components/events}/common/ProgressBar.d.ts +0 -0
  225. /package/lib/{events → components/events}/common/index.d.ts +0 -0
  226. /package/lib/{events → components/events}/common/index.js +0 -0
  227. /package/lib/{events → components/events}/groups/ValidateEventGroup.d.ts +0 -0
  228. /package/lib/{events → components/events}/utils/eventGrouper.d.ts +0 -0
  229. /package/lib/{events → components/events}/utils/eventGrouper.js +0 -0
  230. /package/lib/{events → components/events}/utils/index.d.ts +0 -0
  231. /package/lib/{events → components/events}/utils/index.js +0 -0
  232. /package/lib/{AutoBeChatUploadSendButton.d.ts → components/upload/AutoBeChatUploadSendButton.d.ts} +0 -0
  233. /package/lib/{AutoBeChatUploadSendButton.js → components/upload/AutoBeChatUploadSendButton.js} +0 -0
  234. /package/lib/{AutoBeFileUploadBox.js → components/upload/AutoBeFileUploadBox.js} +0 -0
  235. /package/lib/{AutoBeVoiceRecoderButton.d.ts → components/upload/AutoBeVoiceRecoderButton.d.ts} +0 -0
  236. /package/src/{common → components/common}/openai/OpenAIContent.tsx +0 -0
  237. /package/src/{common → components/common}/openai/OpenAIUserAudioContent.tsx +0 -0
  238. /package/src/{common → components/common}/openai/OpenAIUserFileContent.tsx +0 -0
  239. /package/src/{common → components/common}/openai/OpenAIUserImageContent.tsx +0 -0
  240. /package/src/{common → components/common}/openai/index.ts +0 -0
  241. /package/src/{events → components/events}/AutoBeCompleteEventMovie.tsx +0 -0
  242. /package/src/{events → components/events}/AutoBeProgressEventMovie.tsx +0 -0
  243. /package/src/{events → components/events}/AutoBeScenarioEventMovie.tsx +0 -0
  244. /package/src/{events → components/events}/AutoBeStartEventMovie.tsx +0 -0
  245. /package/src/{events → components/events}/common/EventCard.tsx +0 -0
  246. /package/src/{events → components/events}/common/EventContent.tsx +0 -0
  247. /package/src/{events → components/events}/common/EventIcon.tsx +0 -0
  248. /package/src/{events → components/events}/common/index.ts +0 -0
  249. /package/src/{events → components/events}/utils/eventGrouper.tsx +0 -0
  250. /package/src/{events → components/events}/utils/index.ts +0 -0
  251. /package/src/{AutoBeChatUploadSendButton.tsx → components/upload/AutoBeChatUploadSendButton.tsx} +0 -0
@@ -0,0 +1,374 @@
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 { useMediaQuery } from "../../hooks/useMediaQuery";
15
+ import { AutoBeFileUploader } from "../../utils";
16
+
17
+ export interface IAutoBeBucket {
18
+ file: File;
19
+ content:
20
+ | AutoBeUserMessageAudioContent
21
+ | AutoBeUserMessageFileContent
22
+ | AutoBeUserMessageImageContent;
23
+ }
24
+
25
+ export interface IAutoBeChatUploadConfig {
26
+ supportAudio?: boolean;
27
+ file?: (file: File) => Promise<{ id: string }>;
28
+ image?: (file: File) => Promise<{ url: string }>;
29
+ }
30
+
31
+ export const AutoBeChatUploadBox = (props: AutoBeChatUploadBox.IProps) => {
32
+ const inputRef = useRef<HTMLTextAreaElement>(null);
33
+ const fileInputRef = useRef<HTMLInputElement>(null);
34
+
35
+ const [dragging, setDragging] = useState(false);
36
+ const [enabled, setEnabled] = useState(true);
37
+ const [text, setText] = useState("");
38
+ const [buckets, setBuckets] = useState<IAutoBeBucket[]>([]);
39
+ const [extensionError, setExtensionError] = useState<ReactNode | null>(null);
40
+
41
+ const [emptyText, setEmptyText] = useState(false);
42
+
43
+ const removeFile = (index: number) => {
44
+ setBuckets(buckets.filter((_, i) => i !== index));
45
+ };
46
+
47
+ const conversate = async () => {
48
+ if (enabled === false) return;
49
+
50
+ if (text.trim().length === 0 && buckets.length === 0) {
51
+ setEmptyText(true);
52
+ return;
53
+ }
54
+
55
+ const messages = [
56
+ {
57
+ type: "text",
58
+ text: text.trim(),
59
+ },
60
+ ...buckets.map(({ content }) => content),
61
+ ] as AutoBeUserMessageContent[];
62
+
63
+ setEnabled(false);
64
+ setEmptyText(false);
65
+ setText("");
66
+ setBuckets([]);
67
+
68
+ try {
69
+ await props.conversate(messages);
70
+ } catch (error) {
71
+ props.setError(
72
+ error instanceof Error ? error : new Error("Unknown error"),
73
+ );
74
+ }
75
+ setEnabled(true);
76
+ };
77
+
78
+ const handleFileSelect = async (fileList: FileList | null) => {
79
+ if (!fileList) return;
80
+
81
+ setEnabled(false);
82
+ setExtensionError(null);
83
+
84
+ const newFiles: IAutoBeBucket[] = [];
85
+ const errorFileNames: string[] = [];
86
+
87
+ for (const file of fileList) {
88
+ try {
89
+ newFiles.push(
90
+ await AutoBeFileUploader.compose(props.uploadConfig ?? {}, file),
91
+ );
92
+ } catch (error) {
93
+ errorFileNames.push(file.name);
94
+ }
95
+ }
96
+ if (errorFileNames.length > 0) {
97
+ const extensions: string[] = Array.from(
98
+ new Set(errorFileNames.map((n) => n.split(".").pop() ?? "unknown")),
99
+ ).sort();
100
+ setExtensionError(
101
+ <>
102
+ <h2>
103
+ Unsupported extension{extensions.length > 1 ? "s" : ""}: (
104
+ {extensions.join(", ")})
105
+ </h2>
106
+ <ul>
107
+ {errorFileNames.map((name) => (
108
+ <li key={name}>{name}</li>
109
+ ))}
110
+ </ul>
111
+ </>,
112
+ );
113
+ setTimeout(() => setExtensionError(null), 5_000);
114
+ }
115
+ setBuckets((o) => [...o, ...newFiles]);
116
+ setEnabled(true);
117
+ };
118
+
119
+ const handleDragEnter = (e: React.DragEvent) => {
120
+ e.preventDefault();
121
+ e.stopPropagation();
122
+ setDragging(true);
123
+ };
124
+
125
+ const handleDragLeave = (e: React.DragEvent) => {
126
+ e.preventDefault();
127
+ e.stopPropagation();
128
+ // Only set dragging to false if we're leaving the entire container
129
+ const rect = e.currentTarget.getBoundingClientRect();
130
+ const x = e.clientX;
131
+ const y = e.clientY;
132
+ if (x < rect.left || x >= rect.right || y < rect.top || y >= rect.bottom) {
133
+ setDragging(false);
134
+ }
135
+ };
136
+
137
+ const handleDragOver = (e: React.DragEvent) => {
138
+ e.preventDefault();
139
+ e.stopPropagation();
140
+ };
141
+
142
+ const handleDrop = async (e: React.DragEvent) => {
143
+ e.preventDefault();
144
+ e.stopPropagation();
145
+ setDragging(false);
146
+
147
+ const files = e.dataTransfer.files;
148
+ await handleFileSelect(files);
149
+ };
150
+
151
+ useEffect(() => {
152
+ if (!props.listener?.current) return;
153
+ props.listener.current.handleDragEnter = handleDragEnter;
154
+ props.listener.current.handleDragLeave = handleDragLeave;
155
+ props.listener.current.handleDrop = handleDrop;
156
+ props.listener.current.handleDragOver = handleDragOver;
157
+ }, [props.listener]);
158
+
159
+ return (
160
+ <div
161
+ style={{
162
+ maxWidth: useMediaQuery.WIDTH_MD,
163
+ margin: "0 auto",
164
+ padding: "12px",
165
+ borderRadius: "16px",
166
+ border: dragging ? "3px solid #1976d2" : "2px solid #e0e0e0",
167
+ backgroundColor: dragging
168
+ ? "rgba(25, 118, 210, 0.04)"
169
+ : "rgba(255, 255, 255, 0.95)",
170
+ backdropFilter: "blur(10px)",
171
+ transition: "all 0.2s",
172
+ position: "relative",
173
+ boxShadow: "0px 8px 24px rgba(0, 0, 0, 0.12)",
174
+ }}
175
+ >
176
+ {dragging ? (
177
+ <div
178
+ style={{
179
+ display: "flex",
180
+ alignItems: "center",
181
+ justifyContent: "center",
182
+ minHeight: "120px",
183
+ paddingTop: "32px",
184
+ paddingBottom: "32px",
185
+ }}
186
+ >
187
+ <h6
188
+ style={{
189
+ color: "#1976d2",
190
+ fontWeight: "bold",
191
+ textAlign: "center",
192
+ fontSize: "1.25rem",
193
+ margin: 0,
194
+ }}
195
+ >
196
+ Drop files here to upload
197
+ </h6>
198
+ </div>
199
+ ) : null}
200
+
201
+ <div
202
+ style={{
203
+ display: dragging ? "none" : "flex",
204
+ flexDirection: "column",
205
+ gap: "8px",
206
+ }}
207
+ >
208
+ {buckets.length > 0 && (
209
+ <div
210
+ style={{
211
+ display: "flex",
212
+ flexWrap: "wrap",
213
+ gap: "4px",
214
+ }}
215
+ >
216
+ {buckets.map(({ file }, index) => (
217
+ <div
218
+ key={index}
219
+ style={{
220
+ display: "inline-flex",
221
+ alignItems: "center",
222
+ backgroundColor: "#f5f5f5",
223
+ borderRadius: "16px",
224
+ padding: "4px 8px",
225
+ fontSize: "0.875rem",
226
+ maxWidth: "200px",
227
+ border: "1px solid #e0e0e0",
228
+ }}
229
+ >
230
+ <span
231
+ style={{
232
+ overflow: "hidden",
233
+ textOverflow: "ellipsis",
234
+ whiteSpace: "nowrap",
235
+ marginRight: "4px",
236
+ }}
237
+ >
238
+ {file.name}
239
+ </span>
240
+ <button
241
+ style={{
242
+ background: "none",
243
+ border: "none",
244
+ cursor: "pointer",
245
+ padding: "2px",
246
+ display: "flex",
247
+ alignItems: "center",
248
+ justifyContent: "center",
249
+ borderRadius: "50%",
250
+ width: "16px",
251
+ height: "16px",
252
+ }}
253
+ onClick={() => removeFile(index)}
254
+ onMouseEnter={(e) => {
255
+ e.currentTarget.style.backgroundColor = "#f0f0f0";
256
+ }}
257
+ onMouseLeave={(e) => {
258
+ e.currentTarget.style.backgroundColor = "transparent";
259
+ }}
260
+ >
261
+ <svg
262
+ width="12"
263
+ height="12"
264
+ viewBox="0 0 24 24"
265
+ fill="currentColor"
266
+ >
267
+ <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" />
268
+ </svg>
269
+ </button>
270
+ </div>
271
+ ))}
272
+ </div>
273
+ )}
274
+
275
+ <textarea
276
+ ref={inputRef}
277
+ style={{
278
+ width: "100%",
279
+ minHeight: "40px",
280
+ maxHeight: "192px",
281
+ padding: "8px 12px",
282
+ border: `2px solid ${emptyText ? "#f44336" : dragging ? "#1976d2" : "#e0e0e0"}`,
283
+ borderRadius: "8px",
284
+ fontSize: "0.95rem",
285
+ fontFamily: "inherit",
286
+ resize: "none",
287
+ outline: "none",
288
+ color: dragging ? "#1976d2" : "inherit",
289
+ backgroundColor: "transparent",
290
+ transition: "border-color 0.2s",
291
+ }}
292
+ placeholder={
293
+ emptyText
294
+ ? "Cannot send empty message"
295
+ : dragging
296
+ ? "Drop files here..."
297
+ : "Conversate with AI Chatbot"
298
+ }
299
+ value={text}
300
+ onKeyDown={(e) => {
301
+ if (e.key === "Enter" && !e.shiftKey) {
302
+ e.preventDefault();
303
+ if (enabled) void conversate();
304
+ }
305
+ }}
306
+ onChange={(e) => setText(e.target.value)}
307
+ onFocus={(e) => {
308
+ e.currentTarget.style.borderColor = "#1976d2";
309
+ }}
310
+ onBlur={(e) => {
311
+ e.currentTarget.style.borderColor = emptyText
312
+ ? "#f44336"
313
+ : "#e0e0e0";
314
+ }}
315
+ />
316
+
317
+ <input
318
+ ref={fileInputRef}
319
+ type="file"
320
+ multiple
321
+ accept={AutoBeFileUploader.getAcceptAttribute(
322
+ props.uploadConfig?.supportAudio ?? false,
323
+ !!props.uploadConfig?.file,
324
+ )}
325
+ style={{ display: "none" }}
326
+ onChange={(e) => {
327
+ void handleFileSelect(e.target.files);
328
+ // Reset input to allow selecting the same file again
329
+ if (fileInputRef.current) fileInputRef.current.value = "";
330
+ }}
331
+ />
332
+
333
+ <div
334
+ style={{
335
+ display: "flex",
336
+ justifyContent: "space-between",
337
+ alignItems: "center",
338
+ }}
339
+ >
340
+ <AutoBeFileUploadBox
341
+ extensionError={extensionError}
342
+ onClick={() => fileInputRef.current?.click()}
343
+ enabled={enabled}
344
+ />
345
+ {props.uploadConfig?.supportAudio === true ? (
346
+ <AutoBeVoiceRecoderButton
347
+ enabled={enabled}
348
+ onComplete={(content) => setBuckets((o) => [...o, content])}
349
+ />
350
+ ) : null}
351
+ <AutoBeChatUploadSendButton
352
+ onClick={() => conversate()}
353
+ enabled={enabled}
354
+ />
355
+ </div>
356
+ </div>
357
+ </div>
358
+ );
359
+ };
360
+
361
+ export namespace AutoBeChatUploadBox {
362
+ export interface IProps {
363
+ listener: RefObject<IListener>;
364
+ uploadConfig?: IAutoBeChatUploadConfig;
365
+ conversate: (messages: AutoBeUserMessageContent[]) => Promise<void>;
366
+ setError: (error: Error) => void;
367
+ }
368
+ export interface IListener {
369
+ handleDragEnter: (event: React.DragEvent) => void;
370
+ handleDragLeave: (event: React.DragEvent) => void;
371
+ handleDragOver: (event: React.DragEvent) => void;
372
+ handleDrop: (event: React.DragEvent) => void;
373
+ }
374
+ }
@@ -1,6 +1,12 @@
1
1
  import { ReactNode, useState } from "react";
2
2
 
3
- export const AutoBeFileUploadBox = (props: AutoBeFileUploadBox.IProps) => {
3
+ export interface IAutoBeFileUploadBoxProps {
4
+ extensionError: ReactNode | null;
5
+ onClick: () => void;
6
+ enabled: boolean;
7
+ }
8
+
9
+ export const AutoBeFileUploadBox = (props: IAutoBeFileUploadBoxProps) => {
4
10
  const [isHovered, setIsHovered] = useState(false);
5
11
 
6
12
  const hasError = !!props.extensionError;
@@ -115,10 +121,3 @@ export const AutoBeFileUploadBox = (props: AutoBeFileUploadBox.IProps) => {
115
121
  };
116
122
 
117
123
  export default AutoBeFileUploadBox;
118
- export namespace AutoBeFileUploadBox {
119
- export interface IProps {
120
- extensionError: ReactNode | null;
121
- onClick: () => void;
122
- enabled: boolean;
123
- }
124
- }
@@ -0,0 +1,5 @@
1
+ export interface IAutoBeUploadConfig {
2
+ supportAudio?: boolean;
3
+ file?: (file: File) => Promise<{ id: string }>;
4
+ image?: (file: File) => Promise<{ url: string }>;
5
+ }
@@ -1,7 +1,7 @@
1
1
  import { AutoBeUserMessageAudioContent } from "@autobe/interface";
2
2
  import { useState } from "react";
3
3
 
4
- import { AutoBeVoiceRecorder } from "./utils/AutoBeVoiceRecorder";
4
+ import { AutoBeVoiceRecorder } from "../../utils/AutoBeVoiceRecorder";
5
5
 
6
6
  export const AutoBeVoiceRecoderButton = (
7
7
  props: AutoBeVoiceRecoderButton.IProps,
@@ -82,7 +82,7 @@ export const AutoBeVoiceRecoderButton = (
82
82
  fill="currentColor"
83
83
  style={{ display: "block" }}
84
84
  >
85
- <path d="M12 2c1.1 0 2 .9 2 2v6c0 1.1-.9 2-2 2s-2-.9-2-2V4c0-1.1.9-2 2-2zm5.3 6.7c.4-.4 1-.4 1.4 0 .4.4.4 1 0 1.4l-1.9 1.9c.03.33.05.66.05 1v.5c0 2.8-2.2 5-5 5s-5-2.2-5-5v-.5c0-.34.02-.67.05-1L4.9 10.1c-.4-.4-.4-1 0-1.4.4-.4 1-.4 1.4 0l1.9 1.9c1.8-1.6 4.3-1.6 6.1 0l1.9-1.9zM19 10.5v.5c0 3.9-3.1 7-7 7s-7-3.1-7-7v-.5c0-.8.7-1.5 1.5-1.5s1.5.7 1.5 1.5v.5c0 2.2 1.8 4 4 4s4-1.8 4-4v-.5c0-.8.7-1.5 1.5-1.5s1.5.7 1.5 1.5z" />
85
+ <path d="M12 2C13.1 2 14 2.9 14 4V10C14 11.1 13.1 12 12 12C10.9 12 10 11.1 10 10V4C10 2.9 10.9 2 12 2ZM19 10V12C19 15.866 15.866 19 12 19C8.134 19 5 15.866 5 12V10H7V12C7 14.761 9.239 17 12 17C14.761 17 17 14.761 17 12V10H19ZM11 20V22H13V20H16V22H8V20H11Z" />
86
86
  </svg>
87
87
  )}
88
88
  </button>
@@ -0,0 +1,5 @@
1
+ export * from "./AutoBeFileUploadBox";
2
+ export * from "./AutoBeVoiceRecoderButton";
3
+ export * from "./AutoBeChatUploadBox";
4
+ export * from "./AutoBeChatUploadSendButton";
5
+ export * from "./AutoBeUploadConfig";
@@ -0,0 +1,28 @@
1
+ /** Color tokens for consistent styling */
2
+ export const COLORS = {
3
+ // Gray scale
4
+ GRAY_BORDER: "#d1d5db",
5
+ GRAY_BACKGROUND: "#f9fafb",
6
+ GRAY_BORDER_LIGHT: "#e5e7eb",
7
+ GRAY_TEXT_DARK: "#1f2937",
8
+ GRAY_TEXT_MEDIUM: "#4b5563",
9
+
10
+ // Token value colors
11
+ TOKEN_INPUT: "#2563eb", // Blue
12
+ TOKEN_OUTPUT: "#16a34a", // Green
13
+ TOKEN_TOTAL: "#9333ea", // Purple
14
+
15
+ // Table colors
16
+ TABLE_BORDER: "#eee",
17
+ TABLE_BORDER_THICK: "#ddd",
18
+ TABLE_HEADER_BG: "#f5f5f5",
19
+ } as const;
20
+
21
+ /** Shadow styles */
22
+ export const SHADOWS = {
23
+ CARD: "0 1px 2px 0 rgba(0, 0, 0, 0.05)",
24
+ BANNER: "0 1px 2px 0 rgba(0, 0, 0, 0.1)",
25
+ STRONG: "0 4px 12px 0 rgba(0, 0, 0, 0.15)",
26
+ BUTTON: "0 2px 8px 0 rgba(0, 0, 0, 0.12)",
27
+ BUTTON_HOVER: "0 4px 16px 0 rgba(0, 0, 0, 0.2)",
28
+ } as const;
@@ -0,0 +1,79 @@
1
+ import {
2
+ IAutoBePlaygroundHeader,
3
+ IAutoBeRpcService,
4
+ IAutoBeTokenUsageJson,
5
+ } from "@autobe/interface";
6
+ import { ILlmSchema } from "@samchon/openapi";
7
+ import { ReactNode, createContext, useContext, useState } from "react";
8
+ import { useEffect } from "react";
9
+
10
+ import {
11
+ AutoBeListener,
12
+ AutoBeListenerState,
13
+ IAutoBeEventGroup,
14
+ } from "../structure";
15
+
16
+ interface AutoBeAgentContextType {
17
+ eventGroups: IAutoBeEventGroup[];
18
+ tokenUsage: IAutoBeTokenUsageJson | null;
19
+ state: AutoBeListenerState;
20
+ header: IAutoBePlaygroundHeader<ILlmSchema.Model>;
21
+ service: IAutoBeRpcService;
22
+ listener: AutoBeListener;
23
+ }
24
+
25
+ const AutoBeAgentContext = createContext<AutoBeAgentContextType | null>(null);
26
+
27
+ export function AutoBeAgentProvider({
28
+ children,
29
+ listener,
30
+ service,
31
+ header,
32
+ }: {
33
+ listener: AutoBeListener;
34
+ service: IAutoBeRpcService;
35
+ header: IAutoBePlaygroundHeader<ILlmSchema.Model>;
36
+ children: ReactNode;
37
+ }) {
38
+ const [tokenUsage, setTokenUsage] = useState<IAutoBeTokenUsageJson | null>(
39
+ null,
40
+ );
41
+ const [eventGroups, setEventGroups] = useState<IAutoBeEventGroup[]>([]);
42
+
43
+ useEffect(() => {
44
+ listener.on(async (e) => {
45
+ service
46
+ .getTokenUsage()
47
+ .then(setTokenUsage)
48
+ .catch(() => {});
49
+ setEventGroups(e);
50
+ });
51
+ service
52
+ .getTokenUsage()
53
+ .then(setTokenUsage)
54
+ .catch(() => {});
55
+ }, []);
56
+
57
+ return (
58
+ <AutoBeAgentContext.Provider
59
+ value={{
60
+ eventGroups,
61
+ tokenUsage,
62
+ state: listener.getState(),
63
+ header,
64
+ service,
65
+ listener,
66
+ }}
67
+ >
68
+ {children}
69
+ </AutoBeAgentContext.Provider>
70
+ );
71
+ }
72
+
73
+ export function useAutoBeAgent() {
74
+ const context = useContext(AutoBeAgentContext);
75
+ if (!context) {
76
+ throw new Error("useAutoBeAgent must be used within a AutoBeAgentProvider");
77
+ }
78
+ return context;
79
+ }
@@ -0,0 +1,3 @@
1
+ export * from "./useEscapeKey";
2
+ export * from "./useIsomorphicLayoutEffect";
3
+ export * from "./useMediaQuery";
@@ -0,0 +1,24 @@
1
+ import { useEffect } from "react";
2
+
3
+ /**
4
+ * Hook to handle ESC key press to close modal or overlay
5
+ *
6
+ * @param isOpen - Whether the modal/overlay is open
7
+ * @param onClose - Callback function to close the modal/overlay
8
+ */
9
+ export const useEscapeKey = (isOpen: boolean, onClose: () => void): void => {
10
+ useEffect(() => {
11
+ const handleKeyDown = (event: KeyboardEvent) => {
12
+ if (event.key === "Escape" && isOpen) {
13
+ onClose();
14
+ }
15
+ };
16
+
17
+ if (isOpen) {
18
+ document.addEventListener("keydown", handleKeyDown);
19
+ return () => {
20
+ document.removeEventListener("keydown", handleKeyDown);
21
+ };
22
+ }
23
+ }, [isOpen, onClose]);
24
+ };
@@ -0,0 +1,8 @@
1
+ /**
2
+ * From usehooks-ts
3
+ * https://usehooks-ts.com/react-hook/use-isomorphic-layout-effect
4
+ */
5
+ import { useEffect, useLayoutEffect } from "react";
6
+
7
+ export const useIsomorphicLayoutEffect =
8
+ typeof window !== "undefined" ? useLayoutEffect : useEffect;
@@ -0,0 +1,73 @@
1
+ /** From usehooks-ts https://usehooks-ts.com/react-hook/use-media-query */
2
+ import { useState } from "react";
3
+
4
+ import { useIsomorphicLayoutEffect } from "./useIsomorphicLayoutEffect";
5
+
6
+ type UseMediaQueryOptions = {
7
+ defaultValue?: boolean;
8
+ initializeWithValue?: boolean;
9
+ };
10
+
11
+ const IS_SERVER = typeof window === "undefined";
12
+
13
+ export function useMediaQuery(
14
+ query: string,
15
+ {
16
+ defaultValue = false,
17
+ initializeWithValue = true,
18
+ }: UseMediaQueryOptions = {},
19
+ ): boolean {
20
+ const getMatches = (query: string): boolean => {
21
+ if (IS_SERVER) {
22
+ return defaultValue;
23
+ }
24
+ return window.matchMedia(query).matches;
25
+ };
26
+
27
+ const [matches, setMatches] = useState<boolean>(() => {
28
+ if (initializeWithValue) {
29
+ return getMatches(query);
30
+ }
31
+ return defaultValue;
32
+ });
33
+
34
+ // Handles the change event of the media query.
35
+ function handleChange() {
36
+ setMatches(getMatches(query));
37
+ }
38
+
39
+ useIsomorphicLayoutEffect(() => {
40
+ const matchMedia = window.matchMedia(query);
41
+
42
+ // Triggered at the first client-side load and if query changes
43
+ handleChange();
44
+
45
+ // Use deprecated `addListener` and `removeListener` to support Safari < 14 (#135)
46
+ if (matchMedia.addListener) {
47
+ matchMedia.addListener(handleChange);
48
+ } else {
49
+ matchMedia.addEventListener("change", handleChange);
50
+ }
51
+
52
+ return () => {
53
+ if (matchMedia.removeListener) {
54
+ matchMedia.removeListener(handleChange);
55
+ } else {
56
+ matchMedia.removeEventListener("change", handleChange);
57
+ }
58
+ };
59
+ }, [query]);
60
+
61
+ return matches;
62
+ }
63
+
64
+ export namespace useMediaQuery {
65
+ export const WIDTH_XL = "80rem";
66
+ export const WIDTH_LG = "64rem";
67
+ export const WIDTH_MD = "48rem";
68
+ export const WIDTH_SM = "40rem";
69
+ export const MIN_WIDTH_XL = `(min-width: ${WIDTH_XL})`;
70
+ export const MIN_WIDTH_LG = `(min-width: ${WIDTH_LG})`;
71
+ export const MIN_WIDTH_MD = `(min-width: ${WIDTH_MD})`;
72
+ export const MIN_WIDTH_SM = `(min-width: ${WIDTH_SM})`;
73
+ }