@autobe/ui 0.29.2 → 0.30.0-dev.20260315

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 (151) hide show
  1. package/LICENSE +661 -661
  2. package/README.md +261 -0
  3. package/lib/components/AutoBeChatMain.js +5 -5
  4. package/lib/components/AutoBeChatMain.js.map +1 -1
  5. package/lib/components/AutoBeConfigModal.js +9 -9
  6. package/lib/components/AutoBeStatusModal.js +4 -4
  7. package/lib/components/AutoBeStatusModal.js.map +1 -1
  8. package/lib/components/AutoBeUserMessageMovie.d.ts +2 -2
  9. package/lib/components/common/ChatBubble.d.ts +2 -2
  10. package/lib/components/common/openai/OpenAIContent.d.ts +2 -2
  11. package/lib/components/common/openai/OpenAIContent.js.map +1 -1
  12. package/lib/components/common/openai/OpenAIUserAudioContent.js +1 -1
  13. package/lib/components/common/openai/OpenAIUserAudioContent.js.map +1 -1
  14. package/lib/components/common/openai/OpenAIUserFileContent.js +1 -1
  15. package/lib/components/common/openai/OpenAIUserFileContent.js.map +1 -1
  16. package/lib/components/common/openai/OpenAIUserImageContent.d.ts +2 -2
  17. package/lib/components/events/AutoBeCompleteEventMovie.d.ts +2 -2
  18. package/lib/components/events/AutoBeCompleteEventMovie.js +5 -5
  19. package/lib/components/events/AutoBeCompleteEventMovie.js.map +1 -1
  20. package/lib/components/events/AutoBeCorrectEventMovie.d.ts +2 -2
  21. package/lib/components/events/AutoBeCorrectEventMovie.js +4 -4
  22. package/lib/components/events/AutoBeCorrectEventMovie.js.map +1 -1
  23. package/lib/components/events/AutoBeEventMovie.js +38 -17
  24. package/lib/components/events/AutoBeEventMovie.js.map +1 -1
  25. package/lib/components/events/AutoBeProgressEventMovie.js +73 -13
  26. package/lib/components/events/AutoBeProgressEventMovie.js.map +1 -1
  27. package/lib/components/events/AutoBeScenarioEventMovie.d.ts +2 -2
  28. package/lib/components/events/AutoBeScenarioEventMovie.js +18 -5
  29. package/lib/components/events/AutoBeScenarioEventMovie.js.map +1 -1
  30. package/lib/components/events/AutoBeStartEventMovie.d.ts +2 -2
  31. package/lib/components/events/AutoBeStartEventMovie.js +2 -2
  32. package/lib/components/events/AutoBeStartEventMovie.js.map +1 -1
  33. package/lib/components/events/AutoBeValidateEventMovie.d.ts +2 -2
  34. package/lib/components/events/AutoBeValidateEventMovie.js +3 -11
  35. package/lib/components/events/AutoBeValidateEventMovie.js.map +1 -1
  36. package/lib/components/events/groups/CorrectEventGroup.d.ts +2 -2
  37. package/lib/components/events/groups/CorrectEventGroup.js +1 -1
  38. package/lib/components/events/groups/CorrectEventGroup.js.map +1 -1
  39. package/lib/components/events/groups/ValidateEventGroup.d.ts +2 -2
  40. package/lib/components/events/groups/ValidateEventGroup.js +1 -2
  41. package/lib/components/events/groups/ValidateEventGroup.js.map +1 -1
  42. package/lib/components/events/utils/eventGrouper.js +1 -2
  43. package/lib/components/events/utils/eventGrouper.js.map +1 -1
  44. package/lib/components/upload/AutoBeChatUploadBox.d.ts +3 -4
  45. package/lib/components/upload/AutoBeChatUploadBox.js +2 -1
  46. package/lib/components/upload/AutoBeChatUploadBox.js.map +1 -1
  47. package/lib/components/upload/AutoBeChatUploadSendButton.js +1 -1
  48. package/lib/components/upload/AutoBeChatUploadSendButton.js.map +1 -1
  49. package/lib/context/AutoBeAgentContext.d.ts +1 -3
  50. package/lib/context/AutoBeAgentContext.js +0 -4
  51. package/lib/context/AutoBeAgentContext.js.map +1 -1
  52. package/lib/hooks/useSessionStorage.d.ts +4 -0
  53. package/lib/hooks/useSessionStorage.js +16 -0
  54. package/lib/hooks/useSessionStorage.js.map +1 -0
  55. package/lib/index.d.ts +1 -0
  56. package/lib/index.js +1 -0
  57. package/lib/index.js.map +1 -1
  58. package/lib/strategy/AutoBeAgentSessionStorageStrategy.d.ts +10 -0
  59. package/lib/strategy/AutoBeAgentSessionStorageStrategy.js +117 -0
  60. package/lib/strategy/AutoBeAgentSessionStorageStrategy.js.map +1 -0
  61. package/lib/structure/AutoBeListener.js +91 -23
  62. package/lib/structure/AutoBeListener.js.map +1 -1
  63. package/lib/structure/AutoBeListenerState.d.ts +3 -3
  64. package/lib/structure/AutoBeListenerState.js +4 -4
  65. package/lib/structure/AutoBeListenerState.js.map +1 -1
  66. package/lib/structure/IAutoBeAgentSessionStorageStrategy.js +1 -1
  67. package/lib/structure/IAutoBeAgentSessionStorageStrategy.js.map +1 -1
  68. package/lib/utils/AutoBeFileUploader.d.ts +2 -2
  69. package/lib/utils/AutoBeFileUploader.js.map +1 -1
  70. package/package.json +3 -4
  71. package/src/components/AutoBeAssistantMessageMovie.tsx +22 -22
  72. package/src/components/AutoBeChatMain.tsx +376 -376
  73. package/src/components/AutoBeChatSidebar.tsx +414 -414
  74. package/src/components/AutoBeConfigButton.tsx +83 -83
  75. package/src/components/AutoBeConfigModal.tsx +443 -443
  76. package/src/components/AutoBeStatusButton.tsx +75 -75
  77. package/src/components/AutoBeStatusModal.tsx +486 -484
  78. package/src/components/AutoBeUserMessageMovie.tsx +27 -27
  79. package/src/components/common/ActionButton.tsx +205 -205
  80. package/src/components/common/ActionButtonGroup.tsx +80 -80
  81. package/src/components/common/AutoBeConfigInput.tsx +185 -185
  82. package/src/components/common/ChatBubble.tsx +119 -119
  83. package/src/components/common/Collapsible.tsx +95 -95
  84. package/src/components/common/CompactSessionIndicator.tsx +73 -73
  85. package/src/components/common/CompactSessionList.tsx +82 -82
  86. package/src/components/common/index.ts +8 -8
  87. package/src/components/common/openai/OpenAIContent.tsx +53 -53
  88. package/src/components/common/openai/OpenAIUserAudioContent.tsx +70 -70
  89. package/src/components/common/openai/OpenAIUserFileContent.tsx +76 -76
  90. package/src/components/common/openai/OpenAIUserImageContent.tsx +34 -34
  91. package/src/components/common/openai/OpenAIUserTextContent.tsx +15 -15
  92. package/src/components/common/openai/index.ts +5 -5
  93. package/src/components/events/AutoBeCompleteEventMovie.tsx +402 -402
  94. package/src/components/events/AutoBeCorrectEventMovie.tsx +354 -368
  95. package/src/components/events/AutoBeEventGroupMovie.tsx +18 -18
  96. package/src/components/events/AutoBeEventMovie.tsx +158 -139
  97. package/src/components/events/AutoBeProgressEventMovie.tsx +217 -157
  98. package/src/components/events/AutoBeScenarioEventMovie.tsx +135 -95
  99. package/src/components/events/AutoBeStartEventMovie.tsx +82 -82
  100. package/src/components/events/AutoBeValidateEventMovie.tsx +249 -286
  101. package/src/components/events/README.md +300 -300
  102. package/src/components/events/common/CollapsibleEventGroup.tsx +211 -211
  103. package/src/components/events/common/EventCard.tsx +61 -61
  104. package/src/components/events/common/EventContent.tsx +31 -31
  105. package/src/components/events/common/EventHeader.tsx +85 -85
  106. package/src/components/events/common/EventIcon.tsx +82 -82
  107. package/src/components/events/common/ProgressBar.tsx +64 -64
  108. package/src/components/events/common/index.ts +13 -13
  109. package/src/components/events/groups/CorrectEventGroup.tsx +183 -183
  110. package/src/components/events/groups/ValidateEventGroup.tsx +143 -146
  111. package/src/components/events/groups/index.ts +8 -8
  112. package/src/components/events/index.ts +16 -16
  113. package/src/components/events/utils/eventGrouper.tsx +116 -117
  114. package/src/components/events/utils/index.ts +1 -1
  115. package/src/components/index.ts +13 -13
  116. package/src/components/upload/AutoBeChatUploadBox.tsx +425 -424
  117. package/src/components/upload/AutoBeChatUploadSendButton.tsx +66 -66
  118. package/src/components/upload/AutoBeFileUploadBox.tsx +123 -123
  119. package/src/components/upload/AutoBeUploadConfig.ts +5 -5
  120. package/src/components/upload/AutoBeVoiceRecoderButton.tsx +100 -100
  121. package/src/components/upload/index.ts +5 -5
  122. package/src/constant/color.ts +28 -28
  123. package/src/context/AutoBeAgentContext.tsx +245 -258
  124. package/src/context/AutoBeAgentSessionList.tsx +58 -58
  125. package/src/context/SearchParamsContext.tsx +49 -49
  126. package/src/hooks/index.ts +3 -3
  127. package/src/hooks/useEscapeKey.ts +24 -24
  128. package/src/hooks/useIsomorphicLayoutEffect.ts +8 -8
  129. package/src/hooks/useMediaQuery.ts +73 -73
  130. package/src/hooks/useSessionStorage.ts +10 -0
  131. package/src/icons/Receipt.tsx +74 -74
  132. package/src/index.ts +9 -8
  133. package/src/strategy/AutoBeAgentSessionStorageStrategy.ts +127 -0
  134. package/src/structure/AutoBeListener.ts +373 -304
  135. package/src/structure/AutoBeListenerState.ts +53 -53
  136. package/src/structure/IAutoBeAgentSessionStorageStrategy.ts +87 -87
  137. package/src/structure/IAutoBeEventGroup.ts +6 -6
  138. package/src/structure/index.ts +4 -4
  139. package/src/types/config.ts +44 -44
  140. package/src/types/index.ts +1 -1
  141. package/src/utils/AutoBeFileUploader.ts +279 -279
  142. package/src/utils/AutoBeVoiceRecorder.ts +95 -95
  143. package/src/utils/__tests__/crypto.test.ts +286 -286
  144. package/src/utils/__tests__/storage.test.ts +229 -229
  145. package/src/utils/crypto.ts +95 -95
  146. package/src/utils/index.ts +6 -6
  147. package/src/utils/number.ts +17 -17
  148. package/src/utils/storage.ts +96 -96
  149. package/src/utils/time.ts +14 -14
  150. package/tsconfig.json +9 -9
  151. package/vitest.config.ts +15 -15
@@ -1,279 +1,279 @@
1
- import {
2
- AutoBeUserMessageAudioContent,
3
- AutoBeUserMessageFileContent,
4
- AutoBeUserMessageImageContent,
5
- } from "@autobe/interface";
6
-
7
- export namespace AutoBeFileUploader {
8
- interface IConfig {
9
- supportAudio?: boolean;
10
- file?: (file: File) => Promise<{ id: string }>;
11
- image?: (file: File) => Promise<{ url: string }>;
12
- }
13
- export const isValidFileExtension = (
14
- filename: string,
15
- supportAudio: boolean,
16
- hasFileUploadAPI: boolean,
17
- ): boolean => {
18
- const extension = filename
19
- .toLowerCase()
20
- .substring(filename.lastIndexOf("."));
21
- const format: IFileFormat | undefined = FORMATS[extension];
22
- if (format === undefined) return false;
23
- else if (!supportAudio && format.category === "audio") return false;
24
-
25
- // Without file upload API, only support images, audio (if enabled), and PDF
26
- if (!hasFileUploadAPI) {
27
- if (format.category === "document") return extension === ".pdf";
28
- else if (format.category === "video") return false;
29
-
30
- const allowedCategories: string[] = ["image"];
31
- if (supportAudio) allowedCategories.push("audio");
32
- return allowedCategories.includes(format.category);
33
- }
34
- return true;
35
- };
36
-
37
- export const getAcceptAttribute = (
38
- supportAudio: boolean = false,
39
- hasFileUploadAPI: boolean = false,
40
- ): string => {
41
- const acceptParts = Object.values(FORMATS)
42
- .filter((format) => {
43
- // Audio filter
44
- if (!supportAudio && format.category === "audio") return false;
45
-
46
- // Without file upload API, only allow images, audio (if enabled), and PDF
47
- if (!hasFileUploadAPI) {
48
- if (format.category === "image") return true;
49
- if (format.category === "audio" && supportAudio) return true;
50
- if (format.category === "document" && format.extension === ".pdf")
51
- return true;
52
- return false;
53
- }
54
- return true;
55
- })
56
- .map((format) => format.extension);
57
- return acceptParts.join(",");
58
- };
59
-
60
- export const getMimeType = (filename: string): string => {
61
- const extension = filename
62
- .toLowerCase()
63
- .substring(filename.lastIndexOf("."));
64
- const format = FORMATS[extension];
65
- return format?.mimeType || "application/octet-stream";
66
- };
67
- export const compose = async (config: IConfig, file: File) => {
68
- // Validate file extension first
69
- if (
70
- !isValidFileExtension(
71
- file.name,
72
- config.supportAudio ?? false,
73
- !!config.file,
74
- )
75
- )
76
- throw new Error(
77
- `Unsupported file format: ${file.name}. ${!config.file ? "Only images, PDF, and audio files (if enabled) are supported without file upload API." : ""}`,
78
- );
79
-
80
- // Check for image files
81
- const extension = file.name
82
- .toLowerCase()
83
- .substring(file.name.lastIndexOf("."));
84
- const format = FORMATS[extension];
85
-
86
- if (format?.category === "image")
87
- return {
88
- file,
89
- content: await composeImageContent(config, file),
90
- };
91
- else if (
92
- config.supportAudio &&
93
- format?.category === "audio" &&
94
- AUDIO_MIME_VARIANTS.includes(file.type)
95
- )
96
- return {
97
- file,
98
- content: await composeAudioContent(file),
99
- };
100
- return {
101
- file,
102
- content: await composeFileContent(config, file),
103
- };
104
- };
105
-
106
- export const convertToBase64 = (file: File): Promise<string> =>
107
- new Promise((resolve, reject) => {
108
- const reader = new FileReader();
109
- reader.onload = () => {
110
- let data: string = reader.result as string;
111
-
112
- // If browser couldn't determine MIME type properly, replace with correct one
113
- if (
114
- data.startsWith("data:application/octet-stream") ||
115
- data.startsWith("data:;")
116
- ) {
117
- const mimeType = getMimeType(file.name);
118
- data = data.replace(/^data:[^;]*/, `data:${mimeType}`);
119
- }
120
-
121
- resolve(data);
122
- };
123
- reader.onerror = reject;
124
- reader.readAsDataURL(file);
125
- });
126
-
127
- export const readAsText = (file: File): Promise<string> =>
128
- new Promise((resolve, reject) => {
129
- const reader = new FileReader();
130
- reader.onload = () => {
131
- const text = reader.result as string;
132
- // Convert text to base64
133
- const base64 = btoa(unescape(encodeURIComponent(text)));
134
- resolve(base64);
135
- };
136
- reader.onerror = reject;
137
- reader.readAsText(file);
138
- });
139
-
140
- const composeImageContent = async (
141
- config: IConfig,
142
- file: File,
143
- ): Promise<AutoBeUserMessageImageContent> => ({
144
- type: "image",
145
- image: config.image
146
- ? {
147
- type: "url",
148
- url: await config.image(file).then((res) => res.url),
149
- }
150
- : {
151
- type: "base64",
152
- data: await convertToBase64(file),
153
- },
154
- });
155
-
156
- const composeAudioContent = async (
157
- file: File,
158
- ): Promise<AutoBeUserMessageAudioContent> => ({
159
- type: "audio",
160
- data: (await convertToBase64(file)).split(",")[1]!,
161
- format: file.type.includes("wav") ? "wav" : "mp3",
162
- });
163
-
164
- const composeFileContent = async (
165
- config: IConfig,
166
- file: File,
167
- ): Promise<AutoBeUserMessageFileContent> => {
168
- // Get MIME type for the file
169
- const mimeType = getMimeType(file.name);
170
-
171
- // If file upload API is available, use it
172
- if (config.file) {
173
- return {
174
- type: "file",
175
- file: {
176
- type: "id",
177
- id: await config.file(file).then((res) => res.id),
178
- } satisfies AutoBeUserMessageFileContent.IId,
179
- };
180
- }
181
-
182
- // If MIME type starts with text/, read as text and encode to base64 without data URL
183
- if (mimeType.startsWith("text/")) {
184
- return {
185
- type: "file",
186
- file: {
187
- type: "base64",
188
- name: file.name,
189
- data: await readAsText(file),
190
- } satisfies AutoBeUserMessageFileContent.IBase64,
191
- };
192
- }
193
-
194
- // For other files, use data URL format
195
- return {
196
- type: "file",
197
- file: {
198
- type: "base64",
199
- name: file.name,
200
- data: await convertToBase64(file),
201
- } satisfies AutoBeUserMessageFileContent.IBase64,
202
- };
203
- };
204
- }
205
-
206
- interface IFileFormat {
207
- extension: string;
208
- mimeType: string;
209
- category: "image" | "audio" | "video" | "document";
210
- }
211
-
212
- const FORMATS: Record<string, IFileFormat> = {
213
- // Images
214
- ".png": { extension: ".png", mimeType: "image/png", category: "image" },
215
- ".jpg": { extension: ".jpg", mimeType: "image/jpeg", category: "image" },
216
- ".jpeg": { extension: ".jpeg", mimeType: "image/jpeg", category: "image" },
217
- ".gif": { extension: ".gif", mimeType: "image/gif", category: "image" },
218
- ".webp": { extension: ".webp", mimeType: "image/webp", category: "image" },
219
-
220
- // Audio
221
- ".mp3": { extension: ".mp3", mimeType: "audio/mpeg", category: "audio" },
222
- ".wav": { extension: ".wav", mimeType: "audio/wav", category: "audio" },
223
-
224
- // Video
225
- ".mp4": { extension: ".mp4", mimeType: "video/mp4", category: "video" },
226
- ".mpeg": { extension: ".mpeg", mimeType: "video/mpeg", category: "video" },
227
- ".mov": { extension: ".mov", mimeType: "video/quicktime", category: "video" },
228
- ".avi": { extension: ".avi", mimeType: "video/x-msvideo", category: "video" },
229
- ".webm": { extension: ".webm", mimeType: "video/webm", category: "video" },
230
- ".flv": { extension: ".flv", mimeType: "video/x-flv", category: "video" },
231
- ".mkv": {
232
- extension: ".mkv",
233
- mimeType: "video/x-matroska",
234
- category: "video",
235
- },
236
- ".wmv": { extension: ".wmv", mimeType: "video/x-ms-wmv", category: "video" },
237
-
238
- // Documents
239
- ".pdf": {
240
- extension: ".pdf",
241
- mimeType: "application/pdf",
242
- category: "document",
243
- },
244
- ".txt": { extension: ".txt", mimeType: "text/plain", category: "document" },
245
- ".md": { extension: ".md", mimeType: "text/plain", category: "document" },
246
- ".docx": {
247
- extension: ".docx",
248
- mimeType:
249
- "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
250
- category: "document",
251
- },
252
- ".html": { extension: ".html", mimeType: "text/html", category: "document" },
253
- ".json": {
254
- extension: ".json",
255
- mimeType: "application/json",
256
- category: "document",
257
- },
258
- ".csv": { extension: ".csv", mimeType: "text/csv", category: "document" },
259
- ".xml": {
260
- extension: ".xml",
261
- mimeType: "application/xml",
262
- category: "document",
263
- },
264
- ".rtf": {
265
- extension: ".rtf",
266
- mimeType: "application/rtf",
267
- category: "document",
268
- },
269
- };
270
-
271
- // Alternative MIME types for audio files that browsers might use
272
- const AUDIO_MIME_VARIANTS = [
273
- "audio/mpeg",
274
- "audio/mp3",
275
- "audio/wav",
276
- "audio/x-wav",
277
- "audio/wave",
278
- "audio/x-wave",
279
- ];
1
+ import {
2
+ AutoBeUserImageConversateContent,
3
+ AutoBeUserMessageAudioContent,
4
+ AutoBeUserMessageFileContent,
5
+ } from "@autobe/interface";
6
+
7
+ export namespace AutoBeFileUploader {
8
+ interface IConfig {
9
+ supportAudio?: boolean;
10
+ file?: (file: File) => Promise<{ id: string }>;
11
+ image?: (file: File) => Promise<{ url: string }>;
12
+ }
13
+ export const isValidFileExtension = (
14
+ filename: string,
15
+ supportAudio: boolean,
16
+ hasFileUploadAPI: boolean,
17
+ ): boolean => {
18
+ const extension = filename
19
+ .toLowerCase()
20
+ .substring(filename.lastIndexOf("."));
21
+ const format: IFileFormat | undefined = FORMATS[extension];
22
+ if (format === undefined) return false;
23
+ else if (!supportAudio && format.category === "audio") return false;
24
+
25
+ // Without file upload API, only support images, audio (if enabled), and PDF
26
+ if (!hasFileUploadAPI) {
27
+ if (format.category === "document") return extension === ".pdf";
28
+ else if (format.category === "video") return false;
29
+
30
+ const allowedCategories: string[] = ["image"];
31
+ if (supportAudio) allowedCategories.push("audio");
32
+ return allowedCategories.includes(format.category);
33
+ }
34
+ return true;
35
+ };
36
+
37
+ export const getAcceptAttribute = (
38
+ supportAudio: boolean = false,
39
+ hasFileUploadAPI: boolean = false,
40
+ ): string => {
41
+ const acceptParts = Object.values(FORMATS)
42
+ .filter((format) => {
43
+ // Audio filter
44
+ if (!supportAudio && format.category === "audio") return false;
45
+
46
+ // Without file upload API, only allow images, audio (if enabled), and PDF
47
+ if (!hasFileUploadAPI) {
48
+ if (format.category === "image") return true;
49
+ if (format.category === "audio" && supportAudio) return true;
50
+ if (format.category === "document" && format.extension === ".pdf")
51
+ return true;
52
+ return false;
53
+ }
54
+ return true;
55
+ })
56
+ .map((format) => format.extension);
57
+ return acceptParts.join(",");
58
+ };
59
+
60
+ export const getMimeType = (filename: string): string => {
61
+ const extension = filename
62
+ .toLowerCase()
63
+ .substring(filename.lastIndexOf("."));
64
+ const format = FORMATS[extension];
65
+ return format?.mimeType || "application/octet-stream";
66
+ };
67
+ export const compose = async (config: IConfig, file: File) => {
68
+ // Validate file extension first
69
+ if (
70
+ !isValidFileExtension(
71
+ file.name,
72
+ config.supportAudio ?? false,
73
+ !!config.file,
74
+ )
75
+ )
76
+ throw new Error(
77
+ `Unsupported file format: ${file.name}. ${!config.file ? "Only images, PDF, and audio files (if enabled) are supported without file upload API." : ""}`,
78
+ );
79
+
80
+ // Check for image files
81
+ const extension = file.name
82
+ .toLowerCase()
83
+ .substring(file.name.lastIndexOf("."));
84
+ const format = FORMATS[extension];
85
+
86
+ if (format?.category === "image")
87
+ return {
88
+ file,
89
+ content: await composeImageContent(config, file),
90
+ };
91
+ else if (
92
+ config.supportAudio &&
93
+ format?.category === "audio" &&
94
+ AUDIO_MIME_VARIANTS.includes(file.type)
95
+ )
96
+ return {
97
+ file,
98
+ content: await composeAudioContent(file),
99
+ };
100
+ return {
101
+ file,
102
+ content: await composeFileContent(config, file),
103
+ };
104
+ };
105
+
106
+ export const convertToBase64 = (file: File): Promise<string> =>
107
+ new Promise((resolve, reject) => {
108
+ const reader = new FileReader();
109
+ reader.onload = () => {
110
+ let data: string = reader.result as string;
111
+
112
+ // If browser couldn't determine MIME type properly, replace with correct one
113
+ if (
114
+ data.startsWith("data:application/octet-stream") ||
115
+ data.startsWith("data:;")
116
+ ) {
117
+ const mimeType = getMimeType(file.name);
118
+ data = data.replace(/^data:[^;]*/, `data:${mimeType}`);
119
+ }
120
+
121
+ resolve(data);
122
+ };
123
+ reader.onerror = reject;
124
+ reader.readAsDataURL(file);
125
+ });
126
+
127
+ export const readAsText = (file: File): Promise<string> =>
128
+ new Promise((resolve, reject) => {
129
+ const reader = new FileReader();
130
+ reader.onload = () => {
131
+ const text = reader.result as string;
132
+ // Convert text to base64
133
+ const base64 = btoa(unescape(encodeURIComponent(text)));
134
+ resolve(base64);
135
+ };
136
+ reader.onerror = reject;
137
+ reader.readAsText(file);
138
+ });
139
+
140
+ const composeImageContent = async (
141
+ config: IConfig,
142
+ file: File,
143
+ ): Promise<AutoBeUserImageConversateContent> => ({
144
+ type: "image",
145
+ image: config.image
146
+ ? {
147
+ type: "url",
148
+ url: await config.image(file).then((res) => res.url),
149
+ }
150
+ : {
151
+ type: "base64",
152
+ data: await convertToBase64(file),
153
+ },
154
+ });
155
+
156
+ const composeAudioContent = async (
157
+ file: File,
158
+ ): Promise<AutoBeUserMessageAudioContent> => ({
159
+ type: "audio",
160
+ data: (await convertToBase64(file)).split(",")[1]!,
161
+ format: file.type.includes("wav") ? "wav" : "mp3",
162
+ });
163
+
164
+ const composeFileContent = async (
165
+ config: IConfig,
166
+ file: File,
167
+ ): Promise<AutoBeUserMessageFileContent> => {
168
+ // Get MIME type for the file
169
+ const mimeType = getMimeType(file.name);
170
+
171
+ // If file upload API is available, use it
172
+ if (config.file) {
173
+ return {
174
+ type: "file",
175
+ file: {
176
+ type: "id",
177
+ id: await config.file(file).then((res) => res.id),
178
+ } satisfies AutoBeUserMessageFileContent.IId,
179
+ };
180
+ }
181
+
182
+ // If MIME type starts with text/, read as text and encode to base64 without data URL
183
+ if (mimeType.startsWith("text/")) {
184
+ return {
185
+ type: "file",
186
+ file: {
187
+ type: "base64",
188
+ name: file.name,
189
+ data: await readAsText(file),
190
+ } satisfies AutoBeUserMessageFileContent.IBase64,
191
+ };
192
+ }
193
+
194
+ // For other files, use data URL format
195
+ return {
196
+ type: "file",
197
+ file: {
198
+ type: "base64",
199
+ name: file.name,
200
+ data: await convertToBase64(file),
201
+ } satisfies AutoBeUserMessageFileContent.IBase64,
202
+ };
203
+ };
204
+ }
205
+
206
+ interface IFileFormat {
207
+ extension: string;
208
+ mimeType: string;
209
+ category: "image" | "audio" | "video" | "document";
210
+ }
211
+
212
+ const FORMATS: Record<string, IFileFormat> = {
213
+ // Images
214
+ ".png": { extension: ".png", mimeType: "image/png", category: "image" },
215
+ ".jpg": { extension: ".jpg", mimeType: "image/jpeg", category: "image" },
216
+ ".jpeg": { extension: ".jpeg", mimeType: "image/jpeg", category: "image" },
217
+ ".gif": { extension: ".gif", mimeType: "image/gif", category: "image" },
218
+ ".webp": { extension: ".webp", mimeType: "image/webp", category: "image" },
219
+
220
+ // Audio
221
+ ".mp3": { extension: ".mp3", mimeType: "audio/mpeg", category: "audio" },
222
+ ".wav": { extension: ".wav", mimeType: "audio/wav", category: "audio" },
223
+
224
+ // Video
225
+ ".mp4": { extension: ".mp4", mimeType: "video/mp4", category: "video" },
226
+ ".mpeg": { extension: ".mpeg", mimeType: "video/mpeg", category: "video" },
227
+ ".mov": { extension: ".mov", mimeType: "video/quicktime", category: "video" },
228
+ ".avi": { extension: ".avi", mimeType: "video/x-msvideo", category: "video" },
229
+ ".webm": { extension: ".webm", mimeType: "video/webm", category: "video" },
230
+ ".flv": { extension: ".flv", mimeType: "video/x-flv", category: "video" },
231
+ ".mkv": {
232
+ extension: ".mkv",
233
+ mimeType: "video/x-matroska",
234
+ category: "video",
235
+ },
236
+ ".wmv": { extension: ".wmv", mimeType: "video/x-ms-wmv", category: "video" },
237
+
238
+ // Documents
239
+ ".pdf": {
240
+ extension: ".pdf",
241
+ mimeType: "application/pdf",
242
+ category: "document",
243
+ },
244
+ ".txt": { extension: ".txt", mimeType: "text/plain", category: "document" },
245
+ ".md": { extension: ".md", mimeType: "text/plain", category: "document" },
246
+ ".docx": {
247
+ extension: ".docx",
248
+ mimeType:
249
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
250
+ category: "document",
251
+ },
252
+ ".html": { extension: ".html", mimeType: "text/html", category: "document" },
253
+ ".json": {
254
+ extension: ".json",
255
+ mimeType: "application/json",
256
+ category: "document",
257
+ },
258
+ ".csv": { extension: ".csv", mimeType: "text/csv", category: "document" },
259
+ ".xml": {
260
+ extension: ".xml",
261
+ mimeType: "application/xml",
262
+ category: "document",
263
+ },
264
+ ".rtf": {
265
+ extension: ".rtf",
266
+ mimeType: "application/rtf",
267
+ category: "document",
268
+ },
269
+ };
270
+
271
+ // Alternative MIME types for audio files that browsers might use
272
+ const AUDIO_MIME_VARIANTS = [
273
+ "audio/mpeg",
274
+ "audio/mp3",
275
+ "audio/wav",
276
+ "audio/x-wav",
277
+ "audio/wave",
278
+ "audio/x-wave",
279
+ ];