@apia/ai 3.0.12 → 3.0.13

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.
package/dist/index.js CHANGED
@@ -1,8 +1,12 @@
1
1
  import { jsx, jsxs, Fragment } from '@apia/theme/jsx-runtime';
2
2
  import { Box, getVariant, Textarea, Paragraph } from '@apia/theme';
3
- import { getLabel, EventEmitter } from '@apia/util';
4
- import { useRef, useCallback, useState, useEffect } from 'react';
3
+ import { useRef, useCallback, useState } from 'react';
5
4
  import { IconButton, SimpleButton, ApiaUtil } from '@apia/components';
5
+ import { getLabel, noNaN, AudioRecorder, EventEmitter } from '@apia/util';
6
+ import { makeObservable, observable } from 'mobx';
7
+ import { FileIcon } from '@apia/icons';
8
+ import { uniqueId } from '@apia/notifications';
9
+ import { observer } from 'mobx-react-lite';
6
10
 
7
11
  const AutoscrollContainer = ({ children }) => {
8
12
  const observer = useRef(null);
@@ -143,7 +147,7 @@ function useHandleFileUpload({
143
147
  const handleFileUpload = useCallback(
144
148
  async (ev) => {
145
149
  setProgress(0);
146
- const imageProcessor = getImageOcrMaker();
150
+ const imageProcessor = getImageOcrMaker("spa");
147
151
  const pdfProcessor = getPdfReader();
148
152
  await (async () => {
149
153
  const obtainedText = [];
@@ -186,43 +190,50 @@ function useHandleFileUpload({
186
190
  return { progress, handleFileUpload };
187
191
  }
188
192
 
189
- var __defProp$2 = Object.defineProperty;
190
- var __defNormalProp$2 = (obj, key, value) => key in obj ? __defProp$2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
191
- var __publicField$2 = (obj, key, value) => {
192
- __defNormalProp$2(obj, typeof key !== "symbol" ? key + "" : key, value);
193
- return value;
194
- };
195
- var __accessCheck$1 = (obj, member, msg) => {
196
- if (!member.has(obj))
197
- throw TypeError("Cannot " + msg);
198
- };
199
- var __privateGet$1 = (obj, member, getter) => {
200
- __accessCheck$1(obj, member, "read from private field");
201
- return getter ? getter.call(obj) : member.get(obj);
202
- };
203
- var __privateAdd$1 = (obj, member, value) => {
204
- if (member.has(obj))
205
- throw TypeError("Cannot add the same private member more than once");
206
- member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
207
- };
208
- var __privateSet$1 = (obj, member, value, setter) => {
209
- __accessCheck$1(obj, member, "write to private field");
210
- setter ? setter.call(obj, value) : member.set(obj, value);
193
+ var __defProp$5 = Object.defineProperty;
194
+ var __defNormalProp$5 = (obj, key, value) => key in obj ? __defProp$5(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
195
+ var __publicField$5 = (obj, key, value) => {
196
+ __defNormalProp$5(obj, typeof key !== "symbol" ? key + "" : key, value);
211
197
  return value;
212
198
  };
213
- var _message;
214
199
  let maxId = 0;
215
200
  class ChatMessage {
216
- constructor(message, messageType) {
201
+ constructor(message = "", messageType = "user", attachments = []) {
202
+ this.message = message;
217
203
  this.messageType = messageType;
218
- __publicField$2(this, "id");
219
- __privateAdd$1(this, _message, void 0);
204
+ this.attachments = attachments;
205
+ __publicField$5(this, "id");
206
+ __publicField$5(this, "copy", () => new ChatMessage(this.message, this.messageType, this.attachments));
207
+ __publicField$5(this, "Component", () => {
208
+ return /* @__PURE__ */ jsxs(
209
+ Box,
210
+ {
211
+ as: "pre",
212
+ className: `history__message ${this.messageType}`,
213
+ children: [
214
+ /* @__PURE__ */ jsx(
215
+ Box,
216
+ {
217
+ className: "text_wrapper",
218
+ ...typeof this.message === "string" ? { dangerouslySetInnerHTML: { __html: this.message } } : { children: this.message }
219
+ }
220
+ ),
221
+ /* @__PURE__ */ jsx(Box, { className: "additionalContent_wrapper", children: this.attachments.map((x) => /* @__PURE__ */ jsx(x.Component, {}, x.id)) })
222
+ ]
223
+ },
224
+ this.id
225
+ );
226
+ });
220
227
  this.id = maxId++;
221
- __privateSet$1(this, _message, message);
222
228
  if (messageType === "response")
223
229
  this.messageType = "system";
224
230
  if (typeof message === "string")
225
- __privateSet$1(this, _message, this.parseMessage(message));
231
+ this.message = this.parseMessage(message);
232
+ makeObservable(this, {
233
+ attachments: observable,
234
+ message: observable,
235
+ messageType: observable
236
+ });
226
237
  }
227
238
  parseMessage(message) {
228
239
  let result = message;
@@ -230,229 +241,446 @@ class ChatMessage {
230
241
  result = result.replaceAll("[/strong]", "</strong>");
231
242
  return result;
232
243
  }
233
- get message() {
234
- return __privateGet$1(this, _message);
244
+ }
245
+
246
+ var __defProp$4 = Object.defineProperty;
247
+ var __defNormalProp$4 = (obj, key, value) => key in obj ? __defProp$4(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
248
+ var __publicField$4 = (obj, key, value) => {
249
+ __defNormalProp$4(obj, typeof key !== "symbol" ? key + "" : key, value);
250
+ return value;
251
+ };
252
+ class AIContent {
253
+ constructor(content) {
254
+ __publicField$4(this, "content");
255
+ __publicField$4(this, "Component", () => {
256
+ return /* @__PURE__ */ jsx(Box, { variant: "layout.common.components.aiMessage.textBox", children: /* @__PURE__ */ jsx(Box, { variant: "layout.common.components.aiMessage.textSnippet", children: this.content }) });
257
+ });
258
+ this.content = content;
235
259
  }
236
260
  }
237
- _message = new WeakMap();
238
261
 
239
- const makeTextarea = (gptController) => {
240
- const NewTextarea = ({
241
- hideDeleteButton,
242
- isLoading: outIsLoading,
243
- onSubmit,
244
- preventAppendUserMessages
245
- }) => {
246
- const [isLoading, setIsLoading] = useState(false);
247
- const submit = useCallback(
248
- (text) => {
262
+ const AIFileContentAllowedExtensions = {
263
+ svg: "svg",
264
+ png: "png",
265
+ jpg: "jpg",
266
+ jpeg: "jpeg",
267
+ txt: "txt",
268
+ doc: "doc",
269
+ pdf: "pdf",
270
+ docx: "docx",
271
+ csv: "csv",
272
+ xlsx: "xlsx",
273
+ xls: "xls",
274
+ mp3: "mp3",
275
+ wav: "wav"
276
+ };
277
+ var FileType = /* @__PURE__ */ ((FileType2) => {
278
+ FileType2["DOCUMENT"] = "DOCUMENT";
279
+ FileType2["IMAGE"] = "IMAGE";
280
+ FileType2["AUDIO"] = "AUDIO";
281
+ FileType2["SHEET"] = "SHEET";
282
+ return FileType2;
283
+ })(FileType || {});
284
+ var AIMessageRole = /* @__PURE__ */ ((AIMessageRole2) => {
285
+ AIMessageRole2["USER"] = "USER";
286
+ AIMessageRole2["SYSTEM"] = "SYSTEM";
287
+ return AIMessageRole2;
288
+ })(AIMessageRole || {});
289
+
290
+ var __defProp$3 = Object.defineProperty;
291
+ var __defNormalProp$3 = (obj, key, value) => key in obj ? __defProp$3(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
292
+ var __publicField$3 = (obj, key, value) => {
293
+ __defNormalProp$3(obj, typeof key !== "symbol" ? key + "" : key, value);
294
+ return value;
295
+ };
296
+ const _AIFileContent = class _AIFileContent extends AIContent {
297
+ constructor(content, fileUrl, fileName, f) {
298
+ const extension = _AIFileContent.getExtensionFromFileName(fileName);
299
+ if (!_AIFileContent.isValidExtension(extension))
300
+ throw new Error(_AIFileContent.getExtensionErrorMessage(extension));
301
+ super(content);
302
+ __publicField$3(this, "type");
303
+ __publicField$3(this, "fileUrl");
304
+ __publicField$3(this, "fileName");
305
+ __publicField$3(this, "extension");
306
+ __publicField$3(this, "__file");
307
+ __publicField$3(this, "Component", () => /* @__PURE__ */ jsxs(Box, { variant: "layout.common.components.aiMessage.fileBox", children: [
308
+ FileType.IMAGE === this.type ? /* @__PURE__ */ jsx("img", { src: URL.createObjectURL(this.file), alt: this.fileName }) : /* @__PURE__ */ jsx(FileIcon, { iconSize: "lg", docName: this.fileName }),
309
+ /* @__PURE__ */ jsx(
310
+ Box,
311
+ {
312
+ as: "p",
313
+ variant: "layout.common.components.aiMessage.fileName",
314
+ title: this.fileName,
315
+ children: this.fileName
316
+ }
317
+ )
318
+ ] }));
319
+ this.fileUrl = fileUrl;
320
+ this.extension = extension;
321
+ this.type = _AIFileContent.inferExtension(extension);
322
+ this.fileName = fileName;
323
+ this.__file = f;
324
+ }
325
+ get file() {
326
+ if (!this.__file)
327
+ throw new Error("File is undefined");
328
+ return this.__file;
329
+ }
330
+ static getExtensionFromFileName(fileName) {
331
+ return fileName.split(".", 2).pop();
332
+ }
333
+ static getExtensionErrorMessage(extension) {
334
+ return `extension "${extension}" is not included into valid types:
335
+ ${Object.keys(AIFileContentAllowedExtensions)}`;
336
+ }
337
+ };
338
+ __publicField$3(_AIFileContent, "blobToBase64", async (blob) => {
339
+ return await ((blob2) => new Promise((resolve, reject) => {
340
+ const reader = new FileReader();
341
+ reader.onloadend = () => {
342
+ const base64String = reader.result;
343
+ resolve(base64String);
344
+ };
345
+ reader.onerror = reject;
346
+ reader.readAsDataURL(blob2);
347
+ }))(blob);
348
+ });
349
+ __publicField$3(_AIFileContent, "fromFile", async (f, content) => {
350
+ const fileUrl = await ((f2) => new Promise((resolve, reject) => {
351
+ const reader = new FileReader();
352
+ reader.readAsDataURL(f2);
353
+ reader.onload = () => resolve(reader.result);
354
+ reader.onerror = () => reject();
355
+ }))(f);
356
+ return new _AIFileContent(content, fileUrl, f.name, f);
357
+ });
358
+ __publicField$3(_AIFileContent, "inferExtension", (extension) => {
359
+ switch (extension) {
360
+ case AIFileContentAllowedExtensions.jpg:
361
+ case AIFileContentAllowedExtensions.jpeg:
362
+ case AIFileContentAllowedExtensions.png:
363
+ case AIFileContentAllowedExtensions.svg:
364
+ return FileType.IMAGE;
365
+ case AIFileContentAllowedExtensions.txt:
366
+ case AIFileContentAllowedExtensions.doc:
367
+ case AIFileContentAllowedExtensions.pdf:
368
+ case AIFileContentAllowedExtensions.docx:
369
+ return FileType.DOCUMENT;
370
+ case AIFileContentAllowedExtensions.csv:
371
+ case AIFileContentAllowedExtensions.xlsx:
372
+ case AIFileContentAllowedExtensions.xls:
373
+ return FileType.SHEET;
374
+ case AIFileContentAllowedExtensions.mp3:
375
+ case AIFileContentAllowedExtensions.wav:
376
+ return FileType.AUDIO;
377
+ default:
378
+ throw new Error(_AIFileContent.getExtensionErrorMessage(extension));
379
+ }
380
+ });
381
+ __publicField$3(_AIFileContent, "isValidExtension", (extension) => {
382
+ return Object.values(
383
+ AIFileContentAllowedExtensions
384
+ ).includes(extension);
385
+ });
386
+ let AIFileContent = _AIFileContent;
387
+
388
+ var __defProp$2 = Object.defineProperty;
389
+ var __defNormalProp$2 = (obj, key, value) => key in obj ? __defProp$2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
390
+ var __publicField$2 = (obj, key, value) => {
391
+ __defNormalProp$2(obj, typeof key !== "symbol" ? key + "" : key, value);
392
+ return value;
393
+ };
394
+ class AIMessageAttachments {
395
+ constructor(role, content, close) {
396
+ this.close = close;
397
+ __publicField$2(this, "role");
398
+ __publicField$2(this, "content");
399
+ __publicField$2(this, "id", uniqueId());
400
+ __publicField$2(this, "Component", (props) => /* @__PURE__ */ jsxs(Box, { ...getVariant("layout.common.components.aiMessage"), children: [
401
+ props.closeButton ? /* @__PURE__ */ jsx(
402
+ IconButton,
403
+ {
404
+ className: "closeButton",
405
+ size: "Md",
406
+ icon: "Close",
407
+ onClick: this.close,
408
+ title: getLabel("lblDeleteItem").title
409
+ }
410
+ ) : /* @__PURE__ */ jsx(Fragment, {}),
411
+ /* @__PURE__ */ jsx(this.content.Component, {})
412
+ ] }));
413
+ this.role = role;
414
+ this.content = content;
415
+ }
416
+ }
417
+
418
+ const handleFileUploadAsMessage = async (fileInputElement, chatController) => {
419
+ if (fileInputElement.files) {
420
+ [...fileInputElement.files].forEach(async (f) => {
421
+ const m = new AIMessageAttachments(
422
+ AIMessageRole.USER,
423
+ await AIFileContent.fromFile(
424
+ f,
425
+ "you must collect information contained in this image as help to build your tool choice"
426
+ ),
427
+ () => chatController.attachments.drop(m)
428
+ );
429
+ chatController.attachments.add(m);
430
+ });
431
+ }
432
+ };
433
+
434
+ const makeTextarea = (chatController) => {
435
+ const NewTextarea = observer(
436
+ ({
437
+ hideDeleteButton,
438
+ isLoading: outIsLoading,
439
+ onSubmit,
440
+ preventAppendUserMessages
441
+ }) => {
442
+ const [isLoading, setIsLoading] = useState(false);
443
+ const submit = useCallback(() => {
249
444
  if (preventAppendUserMessages !== true)
250
- gptController.messages.add(new ChatMessage(text, "user"));
251
- gptController.history.add(text);
252
- onSubmit(text).finally(() => {
445
+ chatController.messages.add(chatController.state.current);
446
+ chatController.history.add(chatController.state.current);
447
+ onSubmit(chatController.state.current).finally(() => {
253
448
  setIsLoading(false);
449
+ chatController.state.current = new ChatMessage();
254
450
  });
255
- },
256
- [onSubmit, preventAppendUserMessages]
257
- );
258
- const handleSubmit = useCallback(
259
- (ev) => {
451
+ }, [onSubmit, preventAppendUserMessages]);
452
+ const handleSubmit = useCallback(
453
+ (ev) => {
454
+ ev.preventDefault();
455
+ if (chatController.state.current.message || chatController.attachments.size() > 0) {
456
+ setIsLoading(true);
457
+ submit();
458
+ }
459
+ const textarea = document.getElementById(
460
+ `GPTMessage${chatController.id}`
461
+ );
462
+ textarea.focus();
463
+ },
464
+ [submit]
465
+ );
466
+ const onKeyDown = useCallback((ev) => {
467
+ if (ev.target.readOnly)
468
+ return;
469
+ if (ev.code === "Enter" && !ev.shiftKey) {
470
+ ev.preventDefault();
471
+ ev.target.closest("form")?.querySelector('button[type="submit"]')?.click();
472
+ }
473
+ }, []);
474
+ const { progress, handleFileUpload } = useHandleFileUpload({
475
+ onContent(content) {
476
+ document.getElementById(
477
+ `GPTMessage${chatController.id}`
478
+ ).value = content.join("\n\n");
479
+ }
480
+ });
481
+ const actualIsLoading = isLoading || progress !== 100 || outIsLoading;
482
+ const [dragging, setDragging] = useState(false);
483
+ const handleDragLeave = (ev) => {
260
484
  ev.preventDefault();
261
- const textarea = document.getElementById(
262
- `GPTMessage${gptController.id}`
485
+ setDragging(false);
486
+ };
487
+ const handleDrop = (ev) => {
488
+ ev.preventDefault();
489
+ setDragging(false);
490
+ const fileElement = document.getElementById(
491
+ `gptUploadFile__${chatController.id}`
263
492
  );
264
- if (textarea.value) {
265
- setIsLoading(true);
266
- submit(textarea.value);
267
- }
268
- textarea.focus();
269
- textarea.value = "";
270
- },
271
- [submit]
272
- );
273
- const onKeyDown = useCallback((ev) => {
274
- if (ev.target.readOnly)
275
- return;
276
- if (ev.code === "Enter" && !ev.shiftKey) {
493
+ fileElement.files = ev.dataTransfer.files;
494
+ const event = new Event("change", { bubbles: true });
495
+ fileElement.dispatchEvent(event);
496
+ };
497
+ const handleDragOver = (ev) => {
277
498
  ev.preventDefault();
278
- ev.target.closest("form")?.querySelector('button[type="submit"]')?.click();
279
- }
280
- }, []);
281
- const { progress, handleFileUpload } = useHandleFileUpload({
282
- onContent(content) {
283
- document.getElementById(
284
- `GPTMessage${gptController.id}`
285
- ).value = content.join("\n\n");
286
- }
287
- });
288
- const actualIsLoading = isLoading || progress !== 100 || outIsLoading;
289
- const [dragging, setDragging] = useState(false);
290
- const handleDragLeave = (ev) => {
291
- ev.preventDefault();
292
- setDragging(false);
293
- };
294
- const handleDrop = (ev) => {
295
- ev.preventDefault();
296
- setDragging(false);
297
- const fileElement = document.getElementById(
298
- `gptUploadFile__${gptController.id}`
299
- );
300
- fileElement.files = ev.dataTransfer.files;
301
- const event = new Event("change", { bubbles: true });
302
- fileElement.dispatchEvent(event);
303
- };
304
- const handleDragOver = (ev) => {
305
- ev.preventDefault();
306
- };
307
- const handleDragEnter = (ev) => {
308
- ev.preventDefault();
309
- setDragging(true);
310
- };
311
- return /* @__PURE__ */ jsxs(
312
- "form",
313
- {
314
- onDrop: handleDrop,
315
- onDragOver: handleDragOver,
316
- onDragEnter: handleDragEnter,
317
- onDragLeave: handleDragLeave,
318
- onSubmit: handleSubmit,
319
- className: "gpt__form",
320
- action: "",
321
- children: [
322
- dragging ? /* @__PURE__ */ jsx(
323
- Box,
324
- {
325
- sx: {
326
- height: "150px",
327
- border: "3px dashed #aaa",
328
- width: "100%",
329
- display: "flex",
330
- alignItems: "center",
331
- justifyContent: "center"
332
- },
333
- children: getLabel("msgAiDragAndDrop").text
334
- }
335
- ) : /* @__PURE__ */ jsx(
336
- Textarea,
337
- {
338
- onKeyDown,
339
- id: `GPTMessage${gptController.id}`,
340
- readOnly: actualIsLoading
341
- }
342
- ),
343
- /* @__PURE__ */ jsxs(Box, { className: "gpt__buttonsContainer", children: [
344
- /* @__PURE__ */ jsx(Box, { sx: { display: "none" }, children: /* @__PURE__ */ jsx(
345
- "input",
346
- {
347
- type: "file",
348
- className: "gptUploadFile",
349
- id: `gptUploadFile__${gptController.id}`,
350
- onChange: (ev) => {
351
- handleFileUpload(ev).catch((e) => {
352
- if (e instanceof WrongFormatError) {
353
- setIsLoading(false);
354
- gptController.messages.add(
355
- new ChatMessage(
356
- getLabel("msgAiFileExtensionError").text,
357
- "error"
358
- )
359
- );
360
- } else {
361
- throw e;
362
- }
363
- });
364
- }
365
- }
366
- ) }),
367
- /* @__PURE__ */ jsx(
368
- IconButton,
369
- {
370
- title: getLabel("btnUploadFile").text,
371
- icon: "File",
372
- variant: "icon-outline",
373
- disabled: actualIsLoading,
374
- onClick: () => {
375
- document.getElementById(
376
- `gptUploadFile__${gptController.id}`
377
- ).click();
378
- },
379
- size: "Lg"
380
- }
381
- ),
382
- /* @__PURE__ */ jsx(
383
- SimpleButton,
384
- {
385
- title: getLabel("btnAiPrevious").title,
386
- variant: "outline",
387
- type: "button",
388
- onClick: () => {
389
- const historyValue = gptController.history.previous();
390
- if (historyValue)
391
- document.getElementById(
392
- `GPTMessage${gptController.id}`
393
- ).value = historyValue;
394
- },
395
- size: "sm",
396
- children: getLabel("btnAiPrevious").text
397
- }
398
- ),
399
- /* @__PURE__ */ jsx(
400
- SimpleButton,
401
- {
402
- title: getLabel("btnAiNext").title,
403
- variant: "outline",
404
- type: "button",
405
- onClick: () => {
406
- const historyValue = gptController.history.next();
407
- document.getElementById(
408
- `GPTMessage${gptController.id}`
409
- ).value = historyValue ?? "";
410
- },
411
- size: "sm",
412
- children: getLabel("btnAiNext").text
413
- }
414
- ),
415
- !hideDeleteButton && /* @__PURE__ */ jsxs(Fragment, { children: [
416
- /* @__PURE__ */ jsx(
417
- SimpleButton,
499
+ };
500
+ const handleDragEnter = (ev) => {
501
+ ev.preventDefault();
502
+ setDragging(true);
503
+ };
504
+ return /* @__PURE__ */ jsxs(Box, { sx: { display: "flex", flexDirection: "column", width: "100%" }, children: [
505
+ /* @__PURE__ */ jsx(
506
+ Box,
507
+ {
508
+ sx: {
509
+ flexWrap: "wrap",
510
+ display: "flex",
511
+ maxHeight: "280px",
512
+ overflowY: "auto",
513
+ paddingTop: "6px",
514
+ paddingBottom: "6px"
515
+ },
516
+ children: chatController.attachments.getComponents()
517
+ }
518
+ ),
519
+ /* @__PURE__ */ jsxs(
520
+ "form",
521
+ {
522
+ onDrop: handleDrop,
523
+ onDragOver: handleDragOver,
524
+ onDragEnter: handleDragEnter,
525
+ onDragLeave: handleDragLeave,
526
+ onSubmit: handleSubmit,
527
+ className: "gpt__form",
528
+ action: "",
529
+ children: [
530
+ dragging ? /* @__PURE__ */ jsx(
531
+ Box,
418
532
  {
419
- title: getLabel("btnAiDelete").title,
420
- variant: "outline",
421
- type: "button",
422
- onClick: () => document.getElementById(
423
- `GPTMessage${gptController.id}`
424
- ).value = "",
425
- size: "sm",
426
- children: getLabel("btnAiDelete").text
533
+ sx: {
534
+ height: "150px",
535
+ border: "3px dashed #aaa",
536
+ width: "100%",
537
+ display: "flex",
538
+ alignItems: "center",
539
+ justifyContent: "center"
540
+ },
541
+ children: getLabel("msgAiDragAndDrop").text
427
542
  }
428
- ),
429
- /* @__PURE__ */ jsx(
430
- SimpleButton,
543
+ ) : /* @__PURE__ */ jsx(
544
+ Textarea,
431
545
  {
432
- title: getLabel("btnAiDeleteMessages").title,
433
- variant: "outline",
434
- type: "button",
435
- onClick: () => gptController.messages.clear(),
436
- size: "sm",
437
- children: getLabel("btnAiDeleteMessages").text
546
+ onKeyDown,
547
+ id: `GPTMessage${chatController.id}`,
548
+ readOnly: actualIsLoading,
549
+ value: chatController.state.current.message,
550
+ onChange: (ev) => {
551
+ chatController.state.current.message = ev.target.value;
552
+ if (!chatController.state.current.message && chatController.attachments.size() < 1)
553
+ chatController.currentHistoryIndex = -1;
554
+ }
438
555
  }
439
- )
440
- ] }),
441
- /* @__PURE__ */ jsx(
442
- SimpleButton,
443
- {
444
- title: getLabel("btnAiSend").title,
445
- isLoading: actualIsLoading,
446
- type: "submit",
447
- size: "sm",
448
- children: getLabel("btnAiSend").text
449
- }
450
- )
451
- ] })
452
- ]
453
- }
454
- );
455
- };
556
+ ),
557
+ /* @__PURE__ */ jsxs(Box, { className: "gpt__buttonsContainer", children: [
558
+ /* @__PURE__ */ jsx(Box, { sx: { display: "none" }, children: /* @__PURE__ */ jsx(
559
+ "input",
560
+ {
561
+ type: "file",
562
+ className: "gptUploadFile",
563
+ multiple: true,
564
+ id: `gptUploadFile__${chatController.id}`,
565
+ onChange: (ev) => {
566
+ if (1 == noNaN("a"))
567
+ handleFileUpload(ev).catch((e) => {
568
+ if (e instanceof WrongFormatError) {
569
+ setIsLoading(false);
570
+ chatController.messages.add(
571
+ new ChatMessage(
572
+ getLabel("msgAiFileExtensionError").text,
573
+ "error"
574
+ )
575
+ );
576
+ } else {
577
+ throw e;
578
+ }
579
+ });
580
+ else {
581
+ handleFileUploadAsMessage(ev.target, chatController);
582
+ }
583
+ ev.target.value = "";
584
+ }
585
+ }
586
+ ) }),
587
+ /* @__PURE__ */ jsx(
588
+ IconButton,
589
+ {
590
+ variant: "icon-outline",
591
+ onClick: () => !chatController.audioRecorder.state.recording ? chatController.audioRecorder.start() : chatController.audioRecorder.stop(),
592
+ disabled: actualIsLoading && !chatController.audioRecorder.state.recording,
593
+ sx: {
594
+ color: chatController.audioRecorder.state.recording ? "red" : void 0
595
+ },
596
+ icon: chatController.audioRecorder.state.recording ? "Stop" : "Microphone",
597
+ id: "transcribeAudio",
598
+ size: "Lg",
599
+ title: "TODO: MAKE LABEL TRANSCRIBE AUDIO"
600
+ }
601
+ ),
602
+ /* @__PURE__ */ jsx(
603
+ IconButton,
604
+ {
605
+ title: getLabel("btnUploadFile").text,
606
+ icon: "File",
607
+ variant: "icon-outline",
608
+ disabled: actualIsLoading,
609
+ onClick: () => {
610
+ document.getElementById(
611
+ `gptUploadFile__${chatController.id}`
612
+ ).click();
613
+ },
614
+ size: "Lg"
615
+ }
616
+ ),
617
+ /* @__PURE__ */ jsx(
618
+ SimpleButton,
619
+ {
620
+ title: getLabel("btnAiPrevious").title,
621
+ variant: "outline",
622
+ type: "button",
623
+ onClick: chatController.history.previous,
624
+ size: "sm",
625
+ children: getLabel("btnAiPrevious").text
626
+ }
627
+ ),
628
+ /* @__PURE__ */ jsx(
629
+ SimpleButton,
630
+ {
631
+ title: getLabel("btnAiNext").title,
632
+ variant: "outline",
633
+ type: "button",
634
+ onClick: chatController.history.next,
635
+ size: "sm",
636
+ children: getLabel("btnAiNext").text
637
+ }
638
+ ),
639
+ !hideDeleteButton && /* @__PURE__ */ jsxs(Fragment, { children: [
640
+ /* @__PURE__ */ jsx(
641
+ SimpleButton,
642
+ {
643
+ title: getLabel("btnAiDelete").title,
644
+ variant: "outline",
645
+ type: "button",
646
+ onClick: () => {
647
+ chatController.state.current.message = "";
648
+ chatController.attachments.dropAll();
649
+ chatController.currentHistoryIndex = -1;
650
+ },
651
+ size: "sm",
652
+ children: getLabel("btnAiDelete").text
653
+ }
654
+ ),
655
+ /* @__PURE__ */ jsx(
656
+ SimpleButton,
657
+ {
658
+ title: getLabel("btnAiDeleteMessages").title,
659
+ variant: "outline",
660
+ type: "button",
661
+ onClick: () => chatController.messages.clear(),
662
+ size: "sm",
663
+ children: getLabel("btnAiDeleteMessages").text
664
+ }
665
+ )
666
+ ] }),
667
+ /* @__PURE__ */ jsx(
668
+ SimpleButton,
669
+ {
670
+ title: getLabel("btnAiSend").title,
671
+ isLoading: actualIsLoading,
672
+ type: "submit",
673
+ size: "sm",
674
+ children: getLabel("btnAiSend").text
675
+ }
676
+ )
677
+ ] })
678
+ ]
679
+ }
680
+ )
681
+ ] });
682
+ }
683
+ );
456
684
  NewTextarea.displayName = "";
457
685
  return NewTextarea;
458
686
  };
@@ -481,127 +709,148 @@ var __privateSet = (obj, member, value, setter) => {
481
709
  setter ? setter.call(obj, value) : member.set(obj, value);
482
710
  return value;
483
711
  };
484
- var __privateWrapper = (obj, member, setter, getter) => ({
485
- set _(value) {
486
- __privateSet(obj, member, value, setter);
487
- },
488
- get _() {
489
- return __privateGet(obj, member, getter);
490
- }
491
- });
492
- var _messages, _promptsHistory, _currentHistoryIndex;
493
- class ChatController extends EventEmitter {
712
+ var _promptsHistory, _internalAudioRecorder;
713
+ class ChatController {
494
714
  constructor(id, welcomeMessage) {
495
- super();
496
715
  this.id = id;
497
- __privateAdd(this, _messages, []);
716
+ __publicField$1(this, "state", {
717
+ current: new ChatMessage(),
718
+ messages: []
719
+ });
498
720
  __publicField$1(this, "components", {
499
- MessageHistory: () => {
500
- const messages = this.history.useList();
721
+ MessageHistory: observer(() => {
501
722
  return /* @__PURE__ */ jsx(
502
723
  Box,
503
724
  {
504
725
  ...getVariant("layout.common.components.chat"),
505
726
  className: "history",
506
- children: /* @__PURE__ */ jsx(AutoscrollContainer, { children: messages.map((current) => /* @__PURE__ */ jsx(
507
- Box,
508
- {
509
- as: "pre",
510
- className: `history__message ${current.messageType}`,
511
- ...typeof current.message === "string" ? { dangerouslySetInnerHTML: { __html: current.message } } : { children: current.message }
512
- },
513
- current.id
514
- )) })
727
+ children: /* @__PURE__ */ jsx(AutoscrollContainer, { children: this.state.messages.map((current) => /* @__PURE__ */ jsx(current.Component, {}, current.id)) })
515
728
  }
516
729
  );
517
- },
730
+ }),
518
731
  Textarea: makeTextarea(this)
519
732
  });
520
733
  __privateAdd(this, _promptsHistory, []);
521
- __privateAdd(this, _currentHistoryIndex, -1);
734
+ __publicField$1(this, "currentHistoryIndex", -1);
735
+ __privateAdd(this, _internalAudioRecorder, new AudioRecorder());
736
+ __publicField$1(this, "audioRecorder", {
737
+ start: () => __privateGet(this, _internalAudioRecorder).start().then(async (blob) => {
738
+ const m = new AIMessageAttachments(
739
+ AIMessageRole.USER,
740
+ new AIFileContent(
741
+ "this is a record from user's voice",
742
+ await AIFileContent.blobToBase64(blob),
743
+ `userRecord_${this.audioRecorder.record++}.wav`
744
+ ),
745
+ () => this.attachments.drop(m)
746
+ );
747
+ this.attachments.add(m);
748
+ }),
749
+ stop: async () => {
750
+ __privateGet(this, _internalAudioRecorder).stop();
751
+ },
752
+ record: 0,
753
+ state: __privateGet(this, _internalAudioRecorder).state
754
+ });
522
755
  __publicField$1(this, "history", {
523
- add: (prompt) => {
524
- __privateSet(this, _currentHistoryIndex, -1);
525
- if (prompt === __privateGet(this, _promptsHistory)[__privateGet(this, _promptsHistory).length]) {
756
+ add: (message) => {
757
+ this.currentHistoryIndex = -1;
758
+ if (message === __privateGet(this, _promptsHistory)[__privateGet(this, _promptsHistory).length]) {
526
759
  return;
527
760
  }
528
761
  __privateSet(this, _promptsHistory, [
529
- prompt,
762
+ message,
530
763
  ...__privateGet(this, _promptsHistory).slice(
531
764
  Math.max(0, __privateGet(this, _promptsHistory).length - 10)
532
765
  )
533
766
  ]);
534
767
  },
535
768
  next: () => {
536
- if (__privateGet(this, _promptsHistory)[__privateGet(this, _currentHistoryIndex) - 1])
537
- return __privateGet(this, _promptsHistory)[--__privateWrapper(this, _currentHistoryIndex)._];
769
+ let message = new ChatMessage();
770
+ if (__privateGet(this, _promptsHistory)[this.currentHistoryIndex - 1])
771
+ message = __privateGet(this, _promptsHistory)[--this.currentHistoryIndex].copy();
538
772
  else {
539
- __privateSet(this, _currentHistoryIndex, -1);
540
- return "";
773
+ this.currentHistoryIndex = -1;
541
774
  }
775
+ this.history.updateState(message);
542
776
  },
543
777
  previous: () => {
544
- if (__privateGet(this, _promptsHistory)[__privateGet(this, _currentHistoryIndex) + 1]) {
545
- return __privateGet(this, _promptsHistory)[++__privateWrapper(this, _currentHistoryIndex)._];
778
+ if (__privateGet(this, _promptsHistory)[this.currentHistoryIndex + 1]) {
779
+ this.history.updateState(
780
+ __privateGet(this, _promptsHistory)[++this.currentHistoryIndex].copy()
781
+ );
546
782
  }
547
783
  },
548
- useList: () => {
549
- const [messages, setMessages] = useState(__privateGet(this, _messages));
550
- useEffect(() => {
551
- return this.on("listChange", (messages2) => setMessages(messages2));
552
- }, []);
553
- return messages;
554
- }
784
+ updateState: (message) => {
785
+ if (message)
786
+ this.state.current = message;
787
+ else
788
+ this.state.current = new ChatMessage();
789
+ },
790
+ size: () => this.state.messages.length
555
791
  });
556
792
  __publicField$1(this, "messages", {
557
793
  add: (message, idx) => {
558
- if (idx !== void 0 && idx !== null)
559
- __privateGet(this, _messages).splice(idx, 0, message);
560
- else
561
- __privateGet(this, _messages).push(message);
562
- this.emit("listChange", [...__privateGet(this, _messages)]);
794
+ if (idx !== void 0 && idx !== null) {
795
+ this.state.messages.splice(idx, 0, message);
796
+ } else {
797
+ this.state.messages.push(message);
798
+ }
563
799
  },
564
800
  clear: () => {
565
- __privateSet(this, _messages, []);
566
- this.emit("listChange", []);
801
+ this.state.messages = [];
567
802
  }
568
803
  });
569
- if (welcomeMessage)
570
- __privateGet(this, _messages).splice(0, 0, new ChatMessage(welcomeMessage, "system"));
804
+ __publicField$1(this, "attachments", {
805
+ add: (...aiMessage) => {
806
+ this.state.current.attachments.push(...aiMessage);
807
+ },
808
+ dropAll: () => void (this.state.current.attachments = []),
809
+ drop: (el) => {
810
+ this.state.current.attachments = this.state.current.attachments.filter(
811
+ (c) => c !== el
812
+ );
813
+ if (!this.state.current.message && this.attachments.size() < 1)
814
+ this.currentHistoryIndex = -1;
815
+ },
816
+ getComponents: () => this.state.current.attachments.map((x) => /* @__PURE__ */ jsx(x.Component, { closeButton: true }, x.id)),
817
+ get: () => this.state.current.attachments,
818
+ size: () => this.state.current.attachments.length
819
+ });
820
+ if (welcomeMessage) {
821
+ this.state.messages.splice(
822
+ 0,
823
+ 0,
824
+ new ChatMessage(welcomeMessage, "system")
825
+ );
826
+ }
827
+ makeObservable(this, { state: observable });
571
828
  }
572
829
  }
573
- _messages = new WeakMap();
574
830
  _promptsHistory = new WeakMap();
575
- _currentHistoryIndex = new WeakMap();
831
+ _internalAudioRecorder = new WeakMap();
576
832
 
577
833
  class MultipleChoiceMessage extends ChatMessage {
578
834
  constructor(question, options, Renderer) {
579
- super("", "multipleChoice");
580
- this.question = question;
581
- this.options = options;
582
- this.Renderer = Renderer;
583
- }
584
- get message() {
585
- const Renderer = this.Renderer;
586
- if (Renderer) {
587
- return /* @__PURE__ */ jsxs(Box, { className: "multipleChoiceMessage customRenderer", children: [
588
- /* @__PURE__ */ jsx(Paragraph, { children: this.question }),
589
- this.options.map((item) => /* @__PURE__ */ jsx(Renderer, { item }, item.url))
590
- ] });
591
- }
592
- return /* @__PURE__ */ jsxs(Box, { className: "multipleChoiceMessage", children: [
593
- /* @__PURE__ */ jsx(Paragraph, { children: this.question }),
594
- !Renderer && /* @__PURE__ */ jsx(Box, { as: "ul", children: this.options.map((current) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx(
595
- SimpleButton,
596
- {
597
- className: "multipleChoice__option",
598
- onClick: () => {
599
- ApiaUtil.instance.tabs.openTab(current.label, current.url);
600
- },
601
- children: current.label
602
- }
603
- ) }, current.label)) })
604
- ] });
835
+ super(
836
+ Renderer ? /* @__PURE__ */ jsxs(Box, { className: "multipleChoiceMessage customRenderer", children: [
837
+ /* @__PURE__ */ jsx(Paragraph, { children: question }),
838
+ options.map((item) => /* @__PURE__ */ jsx(Renderer, { item }, item.url))
839
+ ] }) : /* @__PURE__ */ jsxs(Box, { className: "multipleChoiceMessage", children: [
840
+ /* @__PURE__ */ jsx(Paragraph, { children: question }),
841
+ /* @__PURE__ */ jsx(Box, { as: "ul", children: options.map((current) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx(
842
+ SimpleButton,
843
+ {
844
+ className: "multipleChoice__option",
845
+ onClick: () => {
846
+ ApiaUtil.instance.tabs.openTab(current.label, current.url);
847
+ },
848
+ children: current.label
849
+ }
850
+ ) }, current.label)) })
851
+ ] }),
852
+ "multipleChoice"
853
+ );
605
854
  }
606
855
  }
607
856
 
@@ -662,5 +911,5 @@ class ResponseStream extends EventEmitter {
662
911
  }
663
912
  }
664
913
 
665
- export { ChatController, ChatMessage, MultipleChoiceMessage, ResponseStream };
914
+ export { AIMessageAttachments as AIMessage, AutoscrollContainer, ChatController, ChatMessage, MultipleChoiceMessage, ResponseStream };
666
915
  //# sourceMappingURL=index.js.map