@meetsmore-oss/use-ai-client 1.2.4 → 1.4.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.
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  generateChatId,
3
3
  generateMessageId
4
- } from "./chunk-EGKUR4C7.js";
4
+ } from "./chunk-QTQR7MAU.js";
5
5
 
6
6
  // src/useAI.ts
7
7
  import { useState as useState11, useEffect as useEffect10, useRef as useRef11, useCallback as useCallback10, useMemo as useMemo6 } from "react";
@@ -51,7 +51,9 @@ var defaultStrings = {
51
51
  /** Input placeholder when connecting */
52
52
  connectingPlaceholder: "Connecting...",
53
53
  /** Loading indicator text */
54
- thinking: "Thinking"
54
+ thinking: "Thinking",
55
+ /** File processing indicator text (shown during file transformation like OCR) */
56
+ processingFile: "Processing file..."
55
57
  },
56
58
  // File upload
57
59
  fileUpload: {
@@ -378,8 +380,131 @@ function MarkdownContent({ content }) {
378
380
  );
379
381
  }
380
382
 
381
- // src/components/FileChip.tsx
383
+ // src/components/Spinner.tsx
382
384
  import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
385
+ function Spinner({
386
+ size = 16,
387
+ color = "currentColor",
388
+ trackColor,
389
+ strokeWidth = 2
390
+ }) {
391
+ const radius = 10;
392
+ const circumference = 2 * Math.PI * radius;
393
+ return /* @__PURE__ */ jsxs2(
394
+ "svg",
395
+ {
396
+ "data-testid": "spinner",
397
+ width: size,
398
+ height: size,
399
+ viewBox: "0 0 24 24",
400
+ style: {
401
+ animation: "use-ai-spin 1s linear infinite"
402
+ },
403
+ children: [
404
+ /* @__PURE__ */ jsx3("style", { children: `
405
+ @keyframes use-ai-spin {
406
+ from { transform: rotate(0deg); }
407
+ to { transform: rotate(360deg); }
408
+ }
409
+ ` }),
410
+ /* @__PURE__ */ jsx3(
411
+ "circle",
412
+ {
413
+ cx: "12",
414
+ cy: "12",
415
+ r: radius,
416
+ fill: "none",
417
+ stroke: trackColor || color,
418
+ strokeWidth,
419
+ opacity: trackColor ? 1 : 0.25
420
+ }
421
+ ),
422
+ /* @__PURE__ */ jsx3(
423
+ "circle",
424
+ {
425
+ cx: "12",
426
+ cy: "12",
427
+ r: radius,
428
+ fill: "none",
429
+ stroke: color,
430
+ strokeWidth,
431
+ strokeLinecap: "round",
432
+ strokeDasharray: circumference,
433
+ strokeDashoffset: circumference * 0.75,
434
+ style: {
435
+ transformOrigin: "center"
436
+ }
437
+ }
438
+ )
439
+ ]
440
+ }
441
+ );
442
+ }
443
+
444
+ // src/components/ProgressBar.tsx
445
+ import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
446
+ function ProgressBar({
447
+ progress,
448
+ size = 16,
449
+ color = "currentColor",
450
+ trackColor,
451
+ strokeWidth = 2
452
+ }) {
453
+ const clampedProgress = Math.min(100, Math.max(0, progress));
454
+ const radius = 10;
455
+ const circumference = 2 * Math.PI * radius;
456
+ const strokeDashoffset = circumference * (1 - clampedProgress / 100);
457
+ return /* @__PURE__ */ jsxs3(
458
+ "svg",
459
+ {
460
+ "data-testid": "progress-bar",
461
+ role: "progressbar",
462
+ "aria-valuenow": clampedProgress,
463
+ "aria-valuemin": 0,
464
+ "aria-valuemax": 100,
465
+ width: size,
466
+ height: size,
467
+ viewBox: "0 0 24 24",
468
+ style: {
469
+ transform: "rotate(-90deg)"
470
+ },
471
+ children: [
472
+ /* @__PURE__ */ jsx4(
473
+ "circle",
474
+ {
475
+ cx: "12",
476
+ cy: "12",
477
+ r: radius,
478
+ fill: "none",
479
+ stroke: trackColor || color,
480
+ strokeWidth,
481
+ opacity: trackColor ? 1 : 0.25
482
+ }
483
+ ),
484
+ /* @__PURE__ */ jsx4(
485
+ "circle",
486
+ {
487
+ cx: "12",
488
+ cy: "12",
489
+ r: radius,
490
+ fill: "none",
491
+ stroke: color,
492
+ strokeWidth,
493
+ strokeLinecap: "round",
494
+ strokeDasharray: circumference,
495
+ strokeDashoffset,
496
+ style: {
497
+ transition: "stroke-dashoffset 0.2s ease"
498
+ }
499
+ }
500
+ )
501
+ ]
502
+ }
503
+ );
504
+ }
505
+
506
+ // src/components/FileChip.tsx
507
+ import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
383
508
  function formatFileSize(bytes) {
384
509
  if (bytes < 1024) return `${bytes} B`;
385
510
  if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
@@ -394,11 +519,14 @@ function truncateFilename(name, maxLength = 20) {
394
519
  if (maxBaseLength < 5) return name.substring(0, maxLength - 3) + "...";
395
520
  return baseName.substring(0, maxBaseLength) + "..." + ext;
396
521
  }
397
- function FileChip({ attachment, onRemove, disabled }) {
522
+ function FileChip({ attachment, onRemove, disabled, processingState }) {
398
523
  const theme = useTheme();
399
524
  const { file, preview } = attachment;
400
525
  const isImage = file.type.startsWith("image/");
401
- return /* @__PURE__ */ jsxs2(
526
+ const isProcessing = processingState?.status === "processing";
527
+ const hasError = processingState?.status === "error";
528
+ const progress = processingState?.progress;
529
+ return /* @__PURE__ */ jsxs4(
402
530
  "div",
403
531
  {
404
532
  "data-testid": "file-chip",
@@ -411,10 +539,12 @@ function FileChip({ attachment, onRemove, disabled }) {
411
539
  borderRadius: "8px",
412
540
  fontSize: "13px",
413
541
  color: theme.textColor,
414
- maxWidth: "200px"
542
+ maxWidth: "200px",
543
+ position: "relative",
544
+ opacity: isProcessing ? 0.7 : 1
415
545
  },
416
546
  children: [
417
- isImage && preview ? /* @__PURE__ */ jsx3(
547
+ isImage && preview ? /* @__PURE__ */ jsx5(
418
548
  "img",
419
549
  {
420
550
  src: preview,
@@ -426,7 +556,7 @@ function FileChip({ attachment, onRemove, disabled }) {
426
556
  objectFit: "cover"
427
557
  }
428
558
  }
429
- ) : /* @__PURE__ */ jsx3(
559
+ ) : /* @__PURE__ */ jsx5(
430
560
  "div",
431
561
  {
432
562
  style: {
@@ -442,8 +572,8 @@ function FileChip({ attachment, onRemove, disabled }) {
442
572
  children: "\u{1F4CE}"
443
573
  }
444
574
  ),
445
- /* @__PURE__ */ jsxs2("div", { style: { flex: 1, minWidth: 0, overflow: "hidden" }, children: [
446
- /* @__PURE__ */ jsx3(
575
+ /* @__PURE__ */ jsxs4("div", { style: { flex: 1, minWidth: 0, overflow: "hidden" }, children: [
576
+ /* @__PURE__ */ jsx5(
447
577
  "div",
448
578
  {
449
579
  style: {
@@ -456,28 +586,28 @@ function FileChip({ attachment, onRemove, disabled }) {
456
586
  children: truncateFilename(file.name)
457
587
  }
458
588
  ),
459
- /* @__PURE__ */ jsx3("div", { style: { fontSize: "11px", color: theme.secondaryTextColor }, children: formatFileSize(file.size) })
589
+ /* @__PURE__ */ jsx5("div", { style: { fontSize: "11px", color: theme.secondaryTextColor }, children: formatFileSize(file.size) })
460
590
  ] }),
461
- /* @__PURE__ */ jsx3(
591
+ /* @__PURE__ */ jsx5(
462
592
  "button",
463
593
  {
464
594
  "data-testid": "file-chip-remove",
465
595
  onClick: onRemove,
466
- disabled,
596
+ disabled: disabled || isProcessing,
467
597
  style: {
468
598
  background: "transparent",
469
599
  border: "none",
470
600
  padding: "2px 4px",
471
- cursor: disabled ? "not-allowed" : "pointer",
601
+ cursor: disabled || isProcessing ? "not-allowed" : "pointer",
472
602
  color: theme.placeholderTextColor,
473
603
  fontSize: "16px",
474
604
  lineHeight: 1,
475
605
  borderRadius: "4px",
476
606
  transition: "all 0.15s",
477
- opacity: disabled ? 0.5 : 1
607
+ opacity: disabled || isProcessing ? 0.5 : 1
478
608
  },
479
609
  onMouseEnter: (e) => {
480
- if (!disabled) {
610
+ if (!disabled && !isProcessing) {
481
611
  e.currentTarget.style.background = theme.borderColor;
482
612
  e.currentTarget.style.color = theme.textColor;
483
613
  }
@@ -488,6 +618,44 @@ function FileChip({ attachment, onRemove, disabled }) {
488
618
  },
489
619
  children: "\xD7"
490
620
  }
621
+ ),
622
+ isProcessing && /* @__PURE__ */ jsx5(
623
+ "div",
624
+ {
625
+ "data-testid": "file-chip-processing",
626
+ style: {
627
+ position: "absolute",
628
+ inset: 0,
629
+ display: "flex",
630
+ alignItems: "center",
631
+ justifyContent: "center",
632
+ background: "rgba(255, 255, 255, 0.7)",
633
+ borderRadius: "inherit"
634
+ },
635
+ children: progress !== void 0 ? /* @__PURE__ */ jsx5(ProgressBar, { progress, size: 16, color: theme.secondaryTextColor }) : /* @__PURE__ */ jsx5(Spinner, { size: 16, color: theme.secondaryTextColor })
636
+ }
637
+ ),
638
+ hasError && /* @__PURE__ */ jsx5(
639
+ "div",
640
+ {
641
+ "data-testid": "file-chip-error",
642
+ style: {
643
+ position: "absolute",
644
+ bottom: "-2px",
645
+ right: "-2px",
646
+ width: "12px",
647
+ height: "12px",
648
+ borderRadius: "50%",
649
+ background: "#ef4444",
650
+ display: "flex",
651
+ alignItems: "center",
652
+ justifyContent: "center",
653
+ fontSize: "8px",
654
+ color: "white",
655
+ fontWeight: "bold"
656
+ },
657
+ children: "!"
658
+ }
491
659
  )
492
660
  ]
493
661
  }
@@ -495,7 +663,7 @@ function FileChip({ attachment, onRemove, disabled }) {
495
663
  }
496
664
  function FilePlaceholder({ name, size }) {
497
665
  const theme = useTheme();
498
- return /* @__PURE__ */ jsxs2(
666
+ return /* @__PURE__ */ jsxs4(
499
667
  "div",
500
668
  {
501
669
  "data-testid": "file-placeholder",
@@ -512,9 +680,9 @@ function FilePlaceholder({ name, size }) {
512
680
  maxWidth: "200px"
513
681
  },
514
682
  children: [
515
- /* @__PURE__ */ jsx3("span", { children: "\u{1F4CE}" }),
516
- /* @__PURE__ */ jsxs2("div", { style: { flex: 1, minWidth: 0, overflow: "hidden" }, children: [
517
- /* @__PURE__ */ jsx3(
683
+ /* @__PURE__ */ jsx5("span", { children: "\u{1F4CE}" }),
684
+ /* @__PURE__ */ jsxs4("div", { style: { flex: 1, minWidth: 0, overflow: "hidden" }, children: [
685
+ /* @__PURE__ */ jsx5(
518
686
  "div",
519
687
  {
520
688
  style: {
@@ -526,7 +694,7 @@ function FilePlaceholder({ name, size }) {
526
694
  children: truncateFilename(name)
527
695
  }
528
696
  ),
529
- /* @__PURE__ */ jsx3("div", { style: { fontSize: "11px" }, children: formatFileSize(size) })
697
+ /* @__PURE__ */ jsx5("div", { style: { fontSize: "11px" }, children: formatFileSize(size) })
530
698
  ] })
531
699
  ]
532
700
  }
@@ -555,7 +723,7 @@ function validateCommandName(name) {
555
723
 
556
724
  // src/components/CommandAutocomplete.tsx
557
725
  import { useEffect, useRef } from "react";
558
- import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
726
+ import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
559
727
  var MAX_VISIBLE_ITEMS = 8;
560
728
  function CommandAutocomplete({
561
729
  commands,
@@ -586,7 +754,7 @@ function CommandAutocomplete({
586
754
  }
587
755
  }, [highlightedIndex]);
588
756
  if (filteredCommands.length === 0) {
589
- return /* @__PURE__ */ jsx4(
757
+ return /* @__PURE__ */ jsx6(
590
758
  "div",
591
759
  {
592
760
  "data-testid": "command-autocomplete",
@@ -602,7 +770,7 @@ function CommandAutocomplete({
602
770
  overflow: "hidden",
603
771
  zIndex: 1005
604
772
  },
605
- children: /* @__PURE__ */ jsx4(
773
+ children: /* @__PURE__ */ jsx6(
606
774
  "div",
607
775
  {
608
776
  style: {
@@ -617,7 +785,7 @@ function CommandAutocomplete({
617
785
  }
618
786
  );
619
787
  }
620
- return /* @__PURE__ */ jsx4(
788
+ return /* @__PURE__ */ jsx6(
621
789
  "div",
622
790
  {
623
791
  "data-testid": "command-autocomplete",
@@ -636,7 +804,7 @@ function CommandAutocomplete({
636
804
  overflowY: "auto",
637
805
  zIndex: 1005
638
806
  },
639
- children: filteredCommands.map((cmd, index) => /* @__PURE__ */ jsxs3(
807
+ children: filteredCommands.map((cmd, index) => /* @__PURE__ */ jsxs5(
640
808
  "div",
641
809
  {
642
810
  ref: (el) => {
@@ -656,8 +824,8 @@ function CommandAutocomplete({
656
824
  gap: "8px"
657
825
  },
658
826
  children: [
659
- /* @__PURE__ */ jsxs3("div", { style: { flex: 1, minWidth: 0 }, children: [
660
- /* @__PURE__ */ jsxs3(
827
+ /* @__PURE__ */ jsxs5("div", { style: { flex: 1, minWidth: 0 }, children: [
828
+ /* @__PURE__ */ jsxs5(
661
829
  "div",
662
830
  {
663
831
  style: {
@@ -671,7 +839,7 @@ function CommandAutocomplete({
671
839
  ]
672
840
  }
673
841
  ),
674
- /* @__PURE__ */ jsx4(
842
+ /* @__PURE__ */ jsx6(
675
843
  "div",
676
844
  {
677
845
  style: {
@@ -686,7 +854,7 @@ function CommandAutocomplete({
686
854
  }
687
855
  )
688
856
  ] }),
689
- onDelete && /* @__PURE__ */ jsx4(
857
+ onDelete && /* @__PURE__ */ jsx6(
690
858
  "button",
691
859
  {
692
860
  "data-testid": "command-delete-button",
@@ -717,7 +885,7 @@ function CommandAutocomplete({
717
885
  e.currentTarget.style.background = "transparent";
718
886
  },
719
887
  title: strings.commands.deleteCommand,
720
- children: /* @__PURE__ */ jsx4("svg", { width: "14", height: "14", viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx4("path", { d: "M2 4H14M6.5 7V11M9.5 7V11M3 4L4 13C4 13.5304 4.21071 14.0391 4.58579 14.4142C4.96086 14.7893 5.46957 15 6 15H10C10.5304 15 11.0391 14.7893 11.4142 14.4142C11.7893 14.0391 12 13.5304 12 13L13 4M5.5 4V2.5C5.5 2.23478 5.60536 1.98043 5.79289 1.79289C5.98043 1.60536 6.23478 1.5 6.5 1.5H9.5C9.76522 1.5 10.0196 1.60536 10.2071 1.79289C10.3946 1.98043 10.5 2.23478 10.5 2.5V4" }) })
888
+ children: /* @__PURE__ */ jsx6("svg", { width: "14", height: "14", viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx6("path", { d: "M2 4H14M6.5 7V11M9.5 7V11M3 4L4 13C4 13.5304 4.21071 14.0391 4.58579 14.4142C4.96086 14.7893 5.46957 15 6 15H10C10.5304 15 11.0391 14.7893 11.4142 14.4142C11.7893 14.0391 12 13.5304 12 13L13 4M5.5 4V2.5C5.5 2.23478 5.60536 1.98043 5.79289 1.79289C5.98043 1.60536 6.23478 1.5 6.5 1.5H9.5C9.76522 1.5 10.0196 1.60536 10.2071 1.79289C10.3946 1.98043 10.5 2.23478 10.5 2.5V4" }) })
721
889
  }
722
890
  )
723
891
  ]
@@ -735,7 +903,7 @@ function getFilteredCommandsCount(commands, searchPrefix) {
735
903
  }
736
904
 
737
905
  // src/hooks/useSlashCommands.tsx
738
- import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
906
+ import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
739
907
  var MAX_VISIBLE_ITEMS2 = 8;
740
908
  function useSlashCommands({
741
909
  commands,
@@ -870,7 +1038,7 @@ function useSlashCommands({
870
1038
  }
871
1039
  }
872
1040
  }, [savingMessageId, savingMessageText, commandNameInput, commands, onRenameCommand, onSaveCommand, cancelInlineSave, strings]);
873
- const AutocompleteComponent = showAutocomplete && commands.length > 0 ? /* @__PURE__ */ jsx5(
1041
+ const AutocompleteComponent = showAutocomplete && commands.length > 0 ? /* @__PURE__ */ jsx7(
874
1042
  CommandAutocomplete,
875
1043
  {
876
1044
  commands,
@@ -886,7 +1054,7 @@ function useSlashCommands({
886
1054
  if (savingMessageId !== messageId) {
887
1055
  return null;
888
1056
  }
889
- return /* @__PURE__ */ jsxs4(
1057
+ return /* @__PURE__ */ jsxs6(
890
1058
  "div",
891
1059
  {
892
1060
  "data-testid": "inline-save-command",
@@ -900,9 +1068,9 @@ function useSlashCommands({
900
1068
  gap: "4px"
901
1069
  },
902
1070
  children: [
903
- /* @__PURE__ */ jsxs4("div", { style: { display: "flex", alignItems: "center", gap: "6px" }, children: [
904
- /* @__PURE__ */ jsx5("span", { style: { color: theme.primaryColor, fontSize: "13px", fontWeight: 500 }, children: "/" }),
905
- /* @__PURE__ */ jsx5(
1071
+ /* @__PURE__ */ jsxs6("div", { style: { display: "flex", alignItems: "center", gap: "6px" }, children: [
1072
+ /* @__PURE__ */ jsx7("span", { style: { color: theme.primaryColor, fontSize: "13px", fontWeight: 500 }, children: "/" }),
1073
+ /* @__PURE__ */ jsx7(
906
1074
  "input",
907
1075
  {
908
1076
  ref: commandNameInputRef,
@@ -934,7 +1102,7 @@ function useSlashCommands({
934
1102
  }
935
1103
  }
936
1104
  ),
937
- /* @__PURE__ */ jsx5(
1105
+ /* @__PURE__ */ jsx7(
938
1106
  "button",
939
1107
  {
940
1108
  "data-testid": "save-command-confirm",
@@ -952,15 +1120,15 @@ function useSlashCommands({
952
1120
  justifyContent: "center"
953
1121
  },
954
1122
  title: strings.commands.saveCommand,
955
- children: /* @__PURE__ */ jsxs4("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
956
- /* @__PURE__ */ jsx5("path", { d: "M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z" }),
957
- /* @__PURE__ */ jsx5("polyline", { points: "17 21 17 13 7 13 7 21" }),
958
- /* @__PURE__ */ jsx5("polyline", { points: "7 3 7 8 15 8" })
1123
+ children: /* @__PURE__ */ jsxs6("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
1124
+ /* @__PURE__ */ jsx7("path", { d: "M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z" }),
1125
+ /* @__PURE__ */ jsx7("polyline", { points: "17 21 17 13 7 13 7 21" }),
1126
+ /* @__PURE__ */ jsx7("polyline", { points: "7 3 7 8 15 8" })
959
1127
  ] })
960
1128
  }
961
1129
  )
962
1130
  ] }),
963
- commandSaveError && /* @__PURE__ */ jsx5(
1131
+ commandSaveError && /* @__PURE__ */ jsx7(
964
1132
  "div",
965
1133
  {
966
1134
  "data-testid": "command-save-error",
@@ -995,9 +1163,150 @@ import { useState as useState2, useRef as useRef3, useCallback as useCallback2,
995
1163
  // src/fileUpload/types.ts
996
1164
  var DEFAULT_MAX_FILE_SIZE = 10 * 1024 * 1024;
997
1165
 
1166
+ // src/fileUpload/mimeTypeMatcher.ts
1167
+ function matchesMimeType(mimeType, pattern) {
1168
+ if (!pattern.includes("*")) {
1169
+ return mimeType === pattern;
1170
+ }
1171
+ const regexPattern = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*");
1172
+ const regex = new RegExp(`^${regexPattern}$`);
1173
+ return regex.test(mimeType);
1174
+ }
1175
+ function findTransformer(mimeType, transformers) {
1176
+ if (!transformers) {
1177
+ return void 0;
1178
+ }
1179
+ let bestMatch;
1180
+ let bestIsExact = false;
1181
+ let bestLength = -1;
1182
+ for (const [pattern, transformer] of Object.entries(transformers)) {
1183
+ if (!matchesMimeType(mimeType, pattern)) {
1184
+ continue;
1185
+ }
1186
+ const isExact = !pattern.includes("*");
1187
+ if (isExact && !bestIsExact) {
1188
+ bestMatch = transformer;
1189
+ bestIsExact = true;
1190
+ bestLength = pattern.length;
1191
+ continue;
1192
+ }
1193
+ if (isExact === bestIsExact && pattern.length > bestLength) {
1194
+ bestMatch = transformer;
1195
+ bestLength = pattern.length;
1196
+ }
1197
+ }
1198
+ return bestMatch;
1199
+ }
1200
+
1201
+ // src/fileUpload/EmbedFileUploadBackend.ts
1202
+ var EmbedFileUploadBackend = class {
1203
+ /**
1204
+ * Converts a File to a base64 data URL.
1205
+ *
1206
+ * @param file - The File object to convert
1207
+ * @returns Promise resolving to a base64 data URL (e.g., "data:image/png;base64,...")
1208
+ * @throws Error if file reading fails
1209
+ */
1210
+ async prepareForSend(file) {
1211
+ return new Promise((resolve, reject) => {
1212
+ const reader = new FileReader();
1213
+ reader.onload = () => {
1214
+ if (typeof reader.result === "string") {
1215
+ resolve(reader.result);
1216
+ } else {
1217
+ reject(new Error("Failed to read file as data URL"));
1218
+ }
1219
+ };
1220
+ reader.onerror = () => {
1221
+ reject(new Error(`Failed to read file: ${file.name}`));
1222
+ };
1223
+ reader.readAsDataURL(file);
1224
+ });
1225
+ }
1226
+ };
1227
+
1228
+ // src/fileUpload/processAttachments.ts
1229
+ var transformationCache = /* @__PURE__ */ new Map();
1230
+ function getFileCacheKey(file) {
1231
+ return `${file.name}:${file.size}:${file.lastModified}`;
1232
+ }
1233
+ async function getTransformedContent(file, transformer, context, onProgress) {
1234
+ const cacheKey = getFileCacheKey(file);
1235
+ const cached = transformationCache.get(cacheKey);
1236
+ if (cached !== void 0) {
1237
+ return cached;
1238
+ }
1239
+ const result = await transformer.transform(file, context, onProgress);
1240
+ transformationCache.set(cacheKey, result);
1241
+ return result;
1242
+ }
1243
+ async function processAttachments(attachments, config) {
1244
+ const { getCurrentChat, backend = new EmbedFileUploadBackend(), transformers = {}, onFileProgress } = config;
1245
+ const contentParts = [];
1246
+ const chat = await getCurrentChat();
1247
+ const context = { chat };
1248
+ for (const attachment of attachments) {
1249
+ try {
1250
+ if (attachment.transformedContent !== void 0) {
1251
+ contentParts.push({
1252
+ type: "transformed_file",
1253
+ text: attachment.transformedContent,
1254
+ originalFile: {
1255
+ name: attachment.file.name,
1256
+ mimeType: attachment.file.type,
1257
+ size: attachment.file.size
1258
+ }
1259
+ });
1260
+ continue;
1261
+ }
1262
+ const transformer = findTransformer(attachment.file.type, transformers);
1263
+ if (transformer) {
1264
+ onFileProgress?.(attachment.id, { status: "processing" });
1265
+ const transformedText = await getTransformedContent(
1266
+ attachment.file,
1267
+ transformer,
1268
+ context,
1269
+ (progress) => {
1270
+ onFileProgress?.(attachment.id, { status: "processing", progress });
1271
+ }
1272
+ );
1273
+ contentParts.push({
1274
+ type: "transformed_file",
1275
+ text: transformedText,
1276
+ originalFile: {
1277
+ name: attachment.file.name,
1278
+ mimeType: attachment.file.type,
1279
+ size: attachment.file.size
1280
+ }
1281
+ });
1282
+ onFileProgress?.(attachment.id, { status: "done" });
1283
+ } else {
1284
+ const url = await backend.prepareForSend(attachment.file);
1285
+ if (attachment.file.type.startsWith("image/")) {
1286
+ contentParts.push({ type: "image", url });
1287
+ } else {
1288
+ contentParts.push({
1289
+ type: "file",
1290
+ url,
1291
+ mimeType: attachment.file.type,
1292
+ name: attachment.file.name
1293
+ });
1294
+ }
1295
+ }
1296
+ } catch (error) {
1297
+ onFileProgress?.(attachment.id, { status: "error" });
1298
+ throw error;
1299
+ }
1300
+ }
1301
+ return contentParts;
1302
+ }
1303
+ function clearTransformationCache() {
1304
+ transformationCache.clear();
1305
+ }
1306
+
998
1307
  // src/hooks/useFileUpload.tsx
999
1308
  import { v4 as uuidv4 } from "uuid";
1000
- import { jsx as jsx6 } from "react/jsx-runtime";
1309
+ import { jsx as jsx8 } from "react/jsx-runtime";
1001
1310
  async function generateImagePreview(file) {
1002
1311
  if (!file.type.startsWith("image/")) {
1003
1312
  return void 0;
@@ -1030,18 +1339,21 @@ function isTypeAccepted(mimeType, acceptedTypes) {
1030
1339
  function useFileUpload({
1031
1340
  config,
1032
1341
  disabled = false,
1033
- resetDependency
1342
+ resetDependency,
1343
+ getCurrentChat
1034
1344
  }) {
1035
1345
  const strings = useStrings();
1036
1346
  const theme = useTheme();
1037
1347
  const [attachments, setAttachments] = useState2([]);
1038
1348
  const [isDragging, setIsDragging] = useState2(false);
1039
1349
  const [fileError, setFileError] = useState2(null);
1350
+ const [processingState, setProcessingState] = useState2(/* @__PURE__ */ new Map());
1040
1351
  const fileInputRef = useRef3(null);
1041
1352
  const dragCounterRef = useRef3(0);
1042
1353
  const enabled = config !== void 0;
1043
1354
  const maxFileSize = config?.maxFileSize ?? DEFAULT_MAX_FILE_SIZE;
1044
1355
  const acceptedTypes = config?.acceptedTypes;
1356
+ const transformers = config?.transformers;
1045
1357
  useEffect3(() => {
1046
1358
  if (fileError) {
1047
1359
  const timer = setTimeout(() => setFileError(null), 3e3);
@@ -1051,7 +1363,28 @@ function useFileUpload({
1051
1363
  useEffect3(() => {
1052
1364
  setAttachments([]);
1053
1365
  setFileError(null);
1366
+ setProcessingState(/* @__PURE__ */ new Map());
1054
1367
  }, [resetDependency]);
1368
+ const runTransformer = useCallback2(async (attachmentId, file, transformer) => {
1369
+ setProcessingState((prev) => new Map(prev).set(attachmentId, { status: "processing" }));
1370
+ try {
1371
+ const chat = await getCurrentChat();
1372
+ const context = { chat };
1373
+ const transformedContent = await getTransformedContent(file, transformer, context, (progress) => {
1374
+ setProcessingState((prev) => new Map(prev).set(attachmentId, {
1375
+ status: "processing",
1376
+ progress
1377
+ }));
1378
+ });
1379
+ setAttachments((prev) => prev.map(
1380
+ (a) => a.id === attachmentId ? { ...a, transformedContent } : a
1381
+ ));
1382
+ setProcessingState((prev) => new Map(prev).set(attachmentId, { status: "done" }));
1383
+ } catch (error) {
1384
+ console.error(`[useFileUpload] Transformation failed for ${file.name}:`, error);
1385
+ setProcessingState((prev) => new Map(prev).set(attachmentId, { status: "error" }));
1386
+ }
1387
+ }, [getCurrentChat]);
1055
1388
  const handleFiles = useCallback2(async (files) => {
1056
1389
  const fileArray = Array.from(files);
1057
1390
  for (const file of fileArray) {
@@ -1066,21 +1399,30 @@ function useFileUpload({
1066
1399
  continue;
1067
1400
  }
1068
1401
  const preview = await generateImagePreview(file);
1069
- setAttachments((prev) => [
1070
- ...prev,
1071
- {
1072
- id: uuidv4(),
1073
- file,
1074
- preview
1075
- }
1076
- ]);
1402
+ const attachmentId = uuidv4();
1403
+ const attachment = {
1404
+ id: attachmentId,
1405
+ file,
1406
+ preview
1407
+ };
1408
+ setAttachments((prev) => [...prev, attachment]);
1409
+ const transformer = findTransformer(file.type, transformers);
1410
+ if (transformer) {
1411
+ runTransformer(attachmentId, file, transformer);
1412
+ }
1077
1413
  }
1078
- }, [maxFileSize, acceptedTypes, strings]);
1414
+ }, [maxFileSize, acceptedTypes, strings, transformers, runTransformer]);
1079
1415
  const removeAttachment = useCallback2((id) => {
1080
1416
  setAttachments((prev) => prev.filter((a) => a.id !== id));
1417
+ setProcessingState((prev) => {
1418
+ const next = new Map(prev);
1419
+ next.delete(id);
1420
+ return next;
1421
+ });
1081
1422
  }, []);
1082
1423
  const clearAttachments = useCallback2(() => {
1083
1424
  setAttachments([]);
1425
+ setProcessingState(/* @__PURE__ */ new Map());
1084
1426
  }, []);
1085
1427
  const openFilePicker = useCallback2(() => {
1086
1428
  fileInputRef.current?.click();
@@ -1133,7 +1475,7 @@ function useFileUpload({
1133
1475
  }), [handleDragEnter, handleDragOver, handleDragLeave, handleDrop]);
1134
1476
  const DropZoneOverlay = useMemo(() => {
1135
1477
  if (!isDragging || !enabled) return null;
1136
- return /* @__PURE__ */ jsx6(
1478
+ return /* @__PURE__ */ jsx8(
1137
1479
  "div",
1138
1480
  {
1139
1481
  style: {
@@ -1148,7 +1490,7 @@ function useFileUpload({
1148
1490
  zIndex: 1010,
1149
1491
  pointerEvents: "none"
1150
1492
  },
1151
- children: /* @__PURE__ */ jsx6(
1493
+ children: /* @__PURE__ */ jsx8(
1152
1494
  "div",
1153
1495
  {
1154
1496
  style: {
@@ -1157,7 +1499,7 @@ function useFileUpload({
1157
1499
  borderRadius: "12px",
1158
1500
  boxShadow: theme.buttonShadow
1159
1501
  },
1160
- children: /* @__PURE__ */ jsx6("span", { style: { color: theme.primaryColor, fontWeight: 600, fontSize: "16px" }, children: strings.fileUpload.dropFilesHere })
1502
+ children: /* @__PURE__ */ jsx8("span", { style: { color: theme.primaryColor, fontWeight: 600, fontSize: "16px" }, children: strings.fileUpload.dropFilesHere })
1161
1503
  }
1162
1504
  )
1163
1505
  }
@@ -1170,6 +1512,7 @@ function useFileUpload({
1170
1512
  enabled,
1171
1513
  maxFileSize,
1172
1514
  acceptedTypes,
1515
+ processingState,
1173
1516
  fileInputRef,
1174
1517
  handleFiles,
1175
1518
  removeAttachment,
@@ -1187,7 +1530,7 @@ function useFileUpload({
1187
1530
 
1188
1531
  // src/hooks/useDropdownState.tsx
1189
1532
  import { useState as useState3, useCallback as useCallback3, useMemo as useMemo2 } from "react";
1190
- import { jsx as jsx7 } from "react/jsx-runtime";
1533
+ import { jsx as jsx9 } from "react/jsx-runtime";
1191
1534
  function useDropdownState(options = {}) {
1192
1535
  const { backdropZIndex = 1002, initialOpen = false } = options;
1193
1536
  const [isOpen, setIsOpen] = useState3(initialOpen);
@@ -1202,7 +1545,7 @@ function useDropdownState(options = {}) {
1202
1545
  }, []);
1203
1546
  const Backdrop = useMemo2(() => {
1204
1547
  if (!isOpen) return null;
1205
- return /* @__PURE__ */ jsx7(
1548
+ return /* @__PURE__ */ jsx9(
1206
1549
  "div",
1207
1550
  {
1208
1551
  onClick: close,
@@ -1227,7 +1570,7 @@ function useDropdownState(options = {}) {
1227
1570
  }
1228
1571
 
1229
1572
  // src/components/UseAIChatPanel.tsx
1230
- import { Fragment, jsx as jsx8, jsxs as jsxs5 } from "react/jsx-runtime";
1573
+ import { Fragment, jsx as jsx10, jsxs as jsxs7 } from "react/jsx-runtime";
1231
1574
  function getTextContent(content) {
1232
1575
  if (typeof content === "string") {
1233
1576
  return content;
@@ -1248,12 +1591,14 @@ function UseAIChatPanel({
1248
1591
  onLoadChat,
1249
1592
  onDeleteChat,
1250
1593
  onListChats,
1594
+ onGetChat,
1251
1595
  suggestions,
1252
1596
  availableAgents,
1253
1597
  defaultAgent,
1254
1598
  selectedAgent,
1255
1599
  onAgentChange,
1256
1600
  fileUploadConfig,
1601
+ fileProcessing,
1257
1602
  commands = [],
1258
1603
  onSaveCommand,
1259
1604
  onRenameCommand,
@@ -1275,6 +1620,7 @@ function UseAIChatPanel({
1275
1620
  fileError,
1276
1621
  enabled: fileUploadEnabled,
1277
1622
  acceptedTypes,
1623
+ processingState: fileProcessingState,
1278
1624
  fileInputRef,
1279
1625
  removeAttachment,
1280
1626
  clearAttachments,
@@ -1283,6 +1629,7 @@ function UseAIChatPanel({
1283
1629
  getDropZoneProps,
1284
1630
  DropZoneOverlay
1285
1631
  } = useFileUpload({
1632
+ getCurrentChat: onGetChat ?? (async () => null),
1286
1633
  config: fileUploadConfig,
1287
1634
  disabled: loading,
1288
1635
  resetDependency: currentChatId
@@ -1354,7 +1701,7 @@ function UseAIChatPanel({
1354
1701
  chatHistoryDropdown.close();
1355
1702
  }
1356
1703
  };
1357
- return /* @__PURE__ */ jsxs5(
1704
+ return /* @__PURE__ */ jsxs7(
1358
1705
  "div",
1359
1706
  {
1360
1707
  onClick: () => {
@@ -1372,7 +1719,7 @@ function UseAIChatPanel({
1372
1719
  },
1373
1720
  children: [
1374
1721
  DropZoneOverlay,
1375
- /* @__PURE__ */ jsxs5(
1722
+ /* @__PURE__ */ jsxs7(
1376
1723
  "div",
1377
1724
  {
1378
1725
  style: {
@@ -1385,7 +1732,7 @@ function UseAIChatPanel({
1385
1732
  gap: "12px"
1386
1733
  },
1387
1734
  children: [
1388
- /* @__PURE__ */ jsx8("div", { style: { flex: 1, minWidth: 0, position: "relative" }, children: onListChats ? /* @__PURE__ */ jsxs5(
1735
+ /* @__PURE__ */ jsx10("div", { style: { flex: 1, minWidth: 0, position: "relative" }, children: onListChats ? /* @__PURE__ */ jsxs7(
1389
1736
  "button",
1390
1737
  {
1391
1738
  "data-testid": "chat-history-dropdown-button",
@@ -1418,7 +1765,7 @@ function UseAIChatPanel({
1418
1765
  e.currentTarget.style.background = "transparent";
1419
1766
  },
1420
1767
  children: [
1421
- /* @__PURE__ */ jsx8("span", { style: {
1768
+ /* @__PURE__ */ jsx10("span", { style: {
1422
1769
  overflow: "hidden",
1423
1770
  textOverflow: "ellipsis",
1424
1771
  whiteSpace: "nowrap",
@@ -1435,13 +1782,13 @@ function UseAIChatPanel({
1435
1782
  }
1436
1783
  return strings.header.newChat;
1437
1784
  })() }),
1438
- /* @__PURE__ */ jsx8("svg", { width: "12", height: "12", viewBox: "0 0 12 12", fill: "none", style: { flexShrink: 0 }, children: /* @__PURE__ */ jsx8("path", { d: "M3 4.5L6 7.5L9 4.5", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) })
1785
+ /* @__PURE__ */ jsx10("svg", { width: "12", height: "12", viewBox: "0 0 12 12", fill: "none", style: { flexShrink: 0 }, children: /* @__PURE__ */ jsx10("path", { d: "M3 4.5L6 7.5L9 4.5", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) })
1439
1786
  ]
1440
1787
  }
1441
- ) : /* @__PURE__ */ jsx8("div", { style: { fontSize: "14px", fontWeight: "600", color: theme.textColor, padding: "6px 8px" }, children: strings.header.aiAssistant }) }),
1442
- /* @__PURE__ */ jsxs5("div", { style: { display: "flex", alignItems: "center", gap: "4px" }, children: [
1443
- availableAgents && availableAgents.length > 1 && onAgentChange && /* @__PURE__ */ jsxs5("div", { style: { position: "relative" }, children: [
1444
- /* @__PURE__ */ jsxs5(
1788
+ ) : /* @__PURE__ */ jsx10("div", { style: { fontSize: "14px", fontWeight: "600", color: theme.textColor, padding: "6px 8px" }, children: strings.header.aiAssistant }) }),
1789
+ /* @__PURE__ */ jsxs7("div", { style: { display: "flex", alignItems: "center", gap: "4px" }, children: [
1790
+ availableAgents && availableAgents.length > 1 && onAgentChange && /* @__PURE__ */ jsxs7("div", { style: { position: "relative" }, children: [
1791
+ /* @__PURE__ */ jsxs7(
1445
1792
  "button",
1446
1793
  {
1447
1794
  "data-testid": "agent-selector",
@@ -1470,7 +1817,7 @@ function UseAIChatPanel({
1470
1817
  },
1471
1818
  title: "Select AI model",
1472
1819
  children: [
1473
- /* @__PURE__ */ jsx8("span", { style: {
1820
+ /* @__PURE__ */ jsx10("span", { style: {
1474
1821
  overflow: "hidden",
1475
1822
  textOverflow: "ellipsis",
1476
1823
  whiteSpace: "nowrap",
@@ -1479,11 +1826,11 @@ function UseAIChatPanel({
1479
1826
  const agent = availableAgents.find((a) => a.id === (selectedAgent ?? defaultAgent));
1480
1827
  return agent?.name || "AI";
1481
1828
  })() }),
1482
- /* @__PURE__ */ jsx8("svg", { width: "10", height: "10", viewBox: "0 0 12 12", fill: "none", style: { flexShrink: 0 }, children: /* @__PURE__ */ jsx8("path", { d: "M3 4.5L6 7.5L9 4.5", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) })
1829
+ /* @__PURE__ */ jsx10("svg", { width: "10", height: "10", viewBox: "0 0 12 12", fill: "none", style: { flexShrink: 0 }, children: /* @__PURE__ */ jsx10("path", { d: "M3 4.5L6 7.5L9 4.5", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) })
1483
1830
  ]
1484
1831
  }
1485
1832
  ),
1486
- agentDropdown.isOpen && /* @__PURE__ */ jsx8(
1833
+ agentDropdown.isOpen && /* @__PURE__ */ jsx10(
1487
1834
  "div",
1488
1835
  {
1489
1836
  style: {
@@ -1503,7 +1850,7 @@ function UseAIChatPanel({
1503
1850
  },
1504
1851
  children: availableAgents.map((agent) => {
1505
1852
  const isSelected = agent.id === (selectedAgent ?? defaultAgent);
1506
- return /* @__PURE__ */ jsxs5(
1853
+ return /* @__PURE__ */ jsxs7(
1507
1854
  "div",
1508
1855
  {
1509
1856
  "data-testid": "agent-option",
@@ -1533,13 +1880,13 @@ function UseAIChatPanel({
1533
1880
  }
1534
1881
  },
1535
1882
  children: [
1536
- /* @__PURE__ */ jsxs5("div", { style: { flex: 1, minWidth: 0 }, children: [
1537
- /* @__PURE__ */ jsx8("div", { style: {
1883
+ /* @__PURE__ */ jsxs7("div", { style: { flex: 1, minWidth: 0 }, children: [
1884
+ /* @__PURE__ */ jsx10("div", { style: {
1538
1885
  fontSize: "13px",
1539
1886
  fontWeight: isSelected ? "600" : "500",
1540
1887
  color: isSelected ? theme.primaryColor : theme.textColor
1541
1888
  }, children: agent.name }),
1542
- agent.annotation && /* @__PURE__ */ jsx8("div", { style: {
1889
+ agent.annotation && /* @__PURE__ */ jsx10("div", { style: {
1543
1890
  fontSize: "11px",
1544
1891
  color: theme.secondaryTextColor,
1545
1892
  marginTop: "2px",
@@ -1548,7 +1895,7 @@ function UseAIChatPanel({
1548
1895
  whiteSpace: "nowrap"
1549
1896
  }, children: agent.annotation })
1550
1897
  ] }),
1551
- isSelected && /* @__PURE__ */ jsx8("svg", { width: "14", height: "14", viewBox: "0 0 14 14", fill: "none", style: { flexShrink: 0 }, children: /* @__PURE__ */ jsx8("path", { d: "M2 7L5.5 10.5L12 4", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) })
1898
+ isSelected && /* @__PURE__ */ jsx10("svg", { width: "14", height: "14", viewBox: "0 0 14 14", fill: "none", style: { flexShrink: 0 }, children: /* @__PURE__ */ jsx10("path", { d: "M2 7L5.5 10.5L12 4", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) })
1552
1899
  ]
1553
1900
  },
1554
1901
  agent.id
@@ -1557,7 +1904,7 @@ function UseAIChatPanel({
1557
1904
  }
1558
1905
  )
1559
1906
  ] }),
1560
- onNewChat && /* @__PURE__ */ jsx8(
1907
+ onNewChat && /* @__PURE__ */ jsx10(
1561
1908
  "button",
1562
1909
  {
1563
1910
  "data-testid": "new-chat-button",
@@ -1584,10 +1931,10 @@ function UseAIChatPanel({
1584
1931
  e.currentTarget.style.color = theme.secondaryTextColor;
1585
1932
  },
1586
1933
  title: strings.header.newChat,
1587
- children: /* @__PURE__ */ jsx8("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", children: /* @__PURE__ */ jsx8("path", { d: "M8 3.5V12.5M3.5 8H12.5", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" }) })
1934
+ children: /* @__PURE__ */ jsx10("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", children: /* @__PURE__ */ jsx10("path", { d: "M8 3.5V12.5M3.5 8H12.5", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" }) })
1588
1935
  }
1589
1936
  ),
1590
- onDeleteChat && messages.length > 0 && /* @__PURE__ */ jsx8(
1937
+ onDeleteChat && messages.length > 0 && /* @__PURE__ */ jsx10(
1591
1938
  "button",
1592
1939
  {
1593
1940
  "data-testid": "delete-chat-button",
@@ -1611,7 +1958,7 @@ function UseAIChatPanel({
1611
1958
  e.currentTarget.style.color = theme.secondaryTextColor;
1612
1959
  },
1613
1960
  title: strings.header.deleteChat,
1614
- children: /* @__PURE__ */ jsx8("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", children: /* @__PURE__ */ jsx8("path", { d: "M2 4H14M6.5 7V11M9.5 7V11M3 4L4 13C4 13.5304 4.21071 14.0391 4.58579 14.4142C4.96086 14.7893 5.46957 15 6 15H10C10.5304 15 11.0391 14.7893 11.4142 14.4142C11.7893 14.0391 12 13.5304 12 13L13 4M5.5 4V2.5C5.5 2.23478 5.60536 1.98043 5.79289 1.79289C5.98043 1.60536 6.23478 1.5 6.5 1.5H9.5C9.76522 1.5 10.0196 1.60536 10.2071 1.79289C10.3946 1.98043 10.5 2.23478 10.5 2.5V4", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) })
1961
+ children: /* @__PURE__ */ jsx10("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", children: /* @__PURE__ */ jsx10("path", { d: "M2 4H14M6.5 7V11M9.5 7V11M3 4L4 13C4 13.5304 4.21071 14.0391 4.58579 14.4142C4.96086 14.7893 5.46957 15 6 15H10C10.5304 15 11.0391 14.7893 11.4142 14.4142C11.7893 14.0391 12 13.5304 12 13L13 4M5.5 4V2.5C5.5 2.23478 5.60536 1.98043 5.79289 1.79289C5.98043 1.60536 6.23478 1.5 6.5 1.5H9.5C9.76522 1.5 10.0196 1.60536 10.2071 1.79289C10.3946 1.98043 10.5 2.23478 10.5 2.5V4", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) })
1615
1962
  }
1616
1963
  ),
1617
1964
  closeButton
@@ -1619,7 +1966,7 @@ function UseAIChatPanel({
1619
1966
  ]
1620
1967
  }
1621
1968
  ),
1622
- chatHistoryDropdown.isOpen && onListChats && /* @__PURE__ */ jsx8(
1969
+ chatHistoryDropdown.isOpen && onListChats && /* @__PURE__ */ jsx10(
1623
1970
  "div",
1624
1971
  {
1625
1972
  style: {
@@ -1636,7 +1983,7 @@ function UseAIChatPanel({
1636
1983
  flexDirection: "column",
1637
1984
  overflow: "hidden"
1638
1985
  },
1639
- children: /* @__PURE__ */ jsx8(
1986
+ children: /* @__PURE__ */ jsx10(
1640
1987
  "div",
1641
1988
  {
1642
1989
  style: {
@@ -1644,7 +1991,7 @@ function UseAIChatPanel({
1644
1991
  overflowY: "auto",
1645
1992
  padding: "8px"
1646
1993
  },
1647
- children: chatHistory.length === 0 ? /* @__PURE__ */ jsx8(
1994
+ children: chatHistory.length === 0 ? /* @__PURE__ */ jsx10(
1648
1995
  "div",
1649
1996
  {
1650
1997
  style: {
@@ -1653,9 +2000,9 @@ function UseAIChatPanel({
1653
2000
  padding: "32px 16px",
1654
2001
  fontSize: "13px"
1655
2002
  },
1656
- children: /* @__PURE__ */ jsx8("p", { style: { margin: 0 }, children: strings.chatHistory.noChatHistory })
2003
+ children: /* @__PURE__ */ jsx10("p", { style: { margin: 0 }, children: strings.chatHistory.noChatHistory })
1657
2004
  }
1658
- ) : chatHistory.map((chat) => /* @__PURE__ */ jsxs5(
2005
+ ) : chatHistory.map((chat) => /* @__PURE__ */ jsxs7(
1659
2006
  "div",
1660
2007
  {
1661
2008
  "data-testid": "chat-history-item",
@@ -1679,10 +2026,10 @@ function UseAIChatPanel({
1679
2026
  }
1680
2027
  },
1681
2028
  children: [
1682
- /* @__PURE__ */ jsx8("div", { style: { fontSize: "13px", fontWeight: "500", color: theme.textColor, marginBottom: "4px", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }, children: chat.title || strings.header.newChat }),
1683
- /* @__PURE__ */ jsxs5("div", { style: { fontSize: "11px", color: theme.secondaryTextColor }, children: [
2029
+ /* @__PURE__ */ jsx10("div", { style: { fontSize: "13px", fontWeight: "500", color: theme.textColor, marginBottom: "4px", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }, children: chat.title || strings.header.newChat }),
2030
+ /* @__PURE__ */ jsxs7("div", { style: { fontSize: "11px", color: theme.secondaryTextColor }, children: [
1684
2031
  new Date(chat.updatedAt).toLocaleDateString([], { month: "short", day: "numeric" }),
1685
- currentChatId === chat.id && /* @__PURE__ */ jsxs5("span", { style: {
2032
+ currentChatId === chat.id && /* @__PURE__ */ jsxs7("span", { style: {
1686
2033
  marginLeft: "8px",
1687
2034
  color: theme.primaryColor,
1688
2035
  fontWeight: "600"
@@ -1701,7 +2048,7 @@ function UseAIChatPanel({
1701
2048
  ),
1702
2049
  chatHistoryDropdown.Backdrop,
1703
2050
  agentDropdown.Backdrop,
1704
- /* @__PURE__ */ jsxs5(
2051
+ /* @__PURE__ */ jsxs7(
1705
2052
  "div",
1706
2053
  {
1707
2054
  style: {
@@ -1713,7 +2060,7 @@ function UseAIChatPanel({
1713
2060
  gap: "12px"
1714
2061
  },
1715
2062
  children: [
1716
- messages.length === 0 && /* @__PURE__ */ jsxs5(
2063
+ messages.length === 0 && /* @__PURE__ */ jsxs7(
1717
2064
  "div",
1718
2065
  {
1719
2066
  style: {
@@ -1724,12 +2071,12 @@ function UseAIChatPanel({
1724
2071
  gap: "20px"
1725
2072
  },
1726
2073
  children: [
1727
- /* @__PURE__ */ jsxs5("div", { style: { textAlign: "center", color: theme.secondaryTextColor, fontSize: "14px" }, children: [
1728
- /* @__PURE__ */ jsx8("p", { style: { margin: 0, fontSize: "32px", marginBottom: "12px" }, children: "\u{1F4AC}" }),
1729
- /* @__PURE__ */ jsx8("p", { style: { margin: 0 }, children: strings.emptyChat.startConversation }),
1730
- /* @__PURE__ */ jsx8("p", { style: { margin: "8px 0 0", fontSize: "12px" }, children: strings.emptyChat.askMeToHelp })
2074
+ /* @__PURE__ */ jsxs7("div", { style: { textAlign: "center", color: theme.secondaryTextColor, fontSize: "14px" }, children: [
2075
+ /* @__PURE__ */ jsx10("p", { style: { margin: 0, fontSize: "32px", marginBottom: "12px" }, children: "\u{1F4AC}" }),
2076
+ /* @__PURE__ */ jsx10("p", { style: { margin: 0 }, children: strings.emptyChat.startConversation }),
2077
+ /* @__PURE__ */ jsx10("p", { style: { margin: "8px 0 0", fontSize: "12px" }, children: strings.emptyChat.askMeToHelp })
1731
2078
  ] }),
1732
- displayedSuggestions.length > 0 && /* @__PURE__ */ jsx8(
2079
+ displayedSuggestions.length > 0 && /* @__PURE__ */ jsx10(
1733
2080
  "div",
1734
2081
  {
1735
2082
  style: {
@@ -1739,7 +2086,7 @@ function UseAIChatPanel({
1739
2086
  width: "100%",
1740
2087
  maxWidth: "320px"
1741
2088
  },
1742
- children: displayedSuggestions.map((suggestion, index) => /* @__PURE__ */ jsx8(
2089
+ children: displayedSuggestions.map((suggestion, index) => /* @__PURE__ */ jsx10(
1743
2090
  "button",
1744
2091
  {
1745
2092
  "data-testid": "chat-suggestion-button",
@@ -1783,7 +2130,7 @@ function UseAIChatPanel({
1783
2130
  ]
1784
2131
  }
1785
2132
  ),
1786
- messages.map((message) => /* @__PURE__ */ jsxs5(
2133
+ messages.map((message) => /* @__PURE__ */ jsxs7(
1787
2134
  "div",
1788
2135
  {
1789
2136
  "data-testid": `chat-message-${message.role}`,
@@ -1796,7 +2143,7 @@ function UseAIChatPanel({
1796
2143
  onMouseEnter: () => message.role === "user" && setHoveredMessageId(message.id),
1797
2144
  onMouseLeave: () => setHoveredMessageId(null),
1798
2145
  children: [
1799
- /* @__PURE__ */ jsxs5(
2146
+ /* @__PURE__ */ jsxs7(
1800
2147
  "div",
1801
2148
  {
1802
2149
  style: {
@@ -1804,7 +2151,7 @@ function UseAIChatPanel({
1804
2151
  maxWidth: "80%"
1805
2152
  },
1806
2153
  children: [
1807
- message.role === "user" && hoveredMessageId === message.id && onSaveCommand && !slashCommands.isSavingCommand(message.id) && /* @__PURE__ */ jsx8(
2154
+ message.role === "user" && hoveredMessageId === message.id && onSaveCommand && !slashCommands.isSavingCommand(message.id) && /* @__PURE__ */ jsx10(
1808
2155
  "button",
1809
2156
  {
1810
2157
  "data-testid": "save-command-button",
@@ -1840,14 +2187,14 @@ function UseAIChatPanel({
1840
2187
  e.currentTarget.style.transform = "scale(1)";
1841
2188
  e.currentTarget.style.boxShadow = "0 2px 6px rgba(0, 0, 0, 0.15)";
1842
2189
  },
1843
- children: /* @__PURE__ */ jsxs5("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
1844
- /* @__PURE__ */ jsx8("path", { d: "M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z" }),
1845
- /* @__PURE__ */ jsx8("polyline", { points: "17 21 17 13 7 13 7 21" }),
1846
- /* @__PURE__ */ jsx8("polyline", { points: "7 3 7 8 15 8" })
2190
+ children: /* @__PURE__ */ jsxs7("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
2191
+ /* @__PURE__ */ jsx10("path", { d: "M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z" }),
2192
+ /* @__PURE__ */ jsx10("polyline", { points: "17 21 17 13 7 13 7 21" }),
2193
+ /* @__PURE__ */ jsx10("polyline", { points: "7 3 7 8 15 8" })
1847
2194
  ] })
1848
2195
  }
1849
2196
  ),
1850
- /* @__PURE__ */ jsxs5(
2197
+ /* @__PURE__ */ jsxs7(
1851
2198
  "div",
1852
2199
  {
1853
2200
  "data-testid": "chat-message-content",
@@ -1862,7 +2209,7 @@ function UseAIChatPanel({
1862
2209
  wordWrap: "break-word"
1863
2210
  },
1864
2211
  children: [
1865
- message.role === "user" && hasFileContent(message.content) && /* @__PURE__ */ jsx8("div", { style: { display: "flex", flexWrap: "wrap", gap: "6px", marginBottom: "8px" }, children: message.content.filter((part) => part.type === "file").map((part, idx) => /* @__PURE__ */ jsx8(
2212
+ message.role === "user" && hasFileContent(message.content) && /* @__PURE__ */ jsx10("div", { style: { display: "flex", flexWrap: "wrap", gap: "6px", marginBottom: "8px" }, children: message.content.filter((part) => part.type === "file").map((part, idx) => /* @__PURE__ */ jsx10(
1866
2213
  FilePlaceholder,
1867
2214
  {
1868
2215
  name: part.file.name,
@@ -1870,7 +2217,7 @@ function UseAIChatPanel({
1870
2217
  },
1871
2218
  idx
1872
2219
  )) }),
1873
- message.role === "assistant" ? /* @__PURE__ */ jsx8(MarkdownContent, { content: getTextContent(message.content) }) : getTextContent(message.content)
2220
+ message.role === "assistant" ? /* @__PURE__ */ jsx10(MarkdownContent, { content: getTextContent(message.content) }) : getTextContent(message.content)
1874
2221
  ]
1875
2222
  }
1876
2223
  ),
@@ -1881,7 +2228,7 @@ function UseAIChatPanel({
1881
2228
  ]
1882
2229
  }
1883
2230
  ),
1884
- /* @__PURE__ */ jsx8(
2231
+ /* @__PURE__ */ jsx10(
1885
2232
  "div",
1886
2233
  {
1887
2234
  style: {
@@ -1900,14 +2247,14 @@ function UseAIChatPanel({
1900
2247
  },
1901
2248
  message.id
1902
2249
  )),
1903
- loading && /* @__PURE__ */ jsx8(
2250
+ loading && /* @__PURE__ */ jsx10(
1904
2251
  "div",
1905
2252
  {
1906
2253
  style: {
1907
2254
  display: "flex",
1908
2255
  alignItems: "flex-start"
1909
2256
  },
1910
- children: /* @__PURE__ */ jsx8(
2257
+ children: /* @__PURE__ */ jsx10(
1911
2258
  "div",
1912
2259
  {
1913
2260
  className: "markdown-content",
@@ -1920,19 +2267,41 @@ function UseAIChatPanel({
1920
2267
  color: theme.textColor,
1921
2268
  maxWidth: "80%"
1922
2269
  },
1923
- children: streamingText ? /* @__PURE__ */ jsx8(MarkdownContent, { content: streamingText }) : /* @__PURE__ */ jsxs5(Fragment, { children: [
1924
- /* @__PURE__ */ jsx8("span", { style: { opacity: 0.6 }, children: strings.input.thinking }),
1925
- /* @__PURE__ */ jsx8("span", { className: "dots", style: { marginLeft: "4px" }, children: "..." })
2270
+ children: streamingText ? /* @__PURE__ */ jsx10(MarkdownContent, { content: streamingText }) : fileProcessing && fileProcessing.status === "processing" ? /* @__PURE__ */ jsxs7("div", { children: [
2271
+ /* @__PURE__ */ jsx10("span", { style: { opacity: 0.6 }, children: strings.input.processingFile }),
2272
+ fileProcessing.progress != null && /* @__PURE__ */ jsxs7(Fragment, { children: [
2273
+ /* @__PURE__ */ jsxs7("span", { style: { opacity: 0.6, marginLeft: "4px" }, children: [
2274
+ Math.round(fileProcessing.progress),
2275
+ "%"
2276
+ ] }),
2277
+ /* @__PURE__ */ jsx10("div", { style: {
2278
+ marginTop: "6px",
2279
+ height: "4px",
2280
+ borderRadius: "2px",
2281
+ background: theme.borderColor,
2282
+ overflow: "hidden"
2283
+ }, children: /* @__PURE__ */ jsx10("div", { style: {
2284
+ height: "100%",
2285
+ width: `${fileProcessing.progress}%`,
2286
+ borderRadius: "2px",
2287
+ background: theme.primaryColor,
2288
+ transition: "width 0.3s ease"
2289
+ } }) })
2290
+ ] }),
2291
+ fileProcessing.progress == null && /* @__PURE__ */ jsx10("span", { className: "dots", style: { marginLeft: "4px" }, children: "..." })
2292
+ ] }) : /* @__PURE__ */ jsxs7(Fragment, { children: [
2293
+ /* @__PURE__ */ jsx10("span", { style: { opacity: 0.6 }, children: strings.input.thinking }),
2294
+ /* @__PURE__ */ jsx10("span", { className: "dots", style: { marginLeft: "4px" }, children: "..." })
1926
2295
  ] })
1927
2296
  }
1928
2297
  )
1929
2298
  }
1930
2299
  ),
1931
- /* @__PURE__ */ jsx8("div", { ref: messagesEndRef })
2300
+ /* @__PURE__ */ jsx10("div", { ref: messagesEndRef })
1932
2301
  ]
1933
2302
  }
1934
2303
  ),
1935
- /* @__PURE__ */ jsxs5(
2304
+ /* @__PURE__ */ jsxs7(
1936
2305
  "div",
1937
2306
  {
1938
2307
  style: {
@@ -1940,7 +2309,7 @@ function UseAIChatPanel({
1940
2309
  borderTop: `1px solid ${theme.borderColor}`
1941
2310
  },
1942
2311
  children: [
1943
- fileError && /* @__PURE__ */ jsx8(
2312
+ fileError && /* @__PURE__ */ jsx10(
1944
2313
  "div",
1945
2314
  {
1946
2315
  "data-testid": "file-error",
@@ -1955,7 +2324,7 @@ function UseAIChatPanel({
1955
2324
  children: fileError
1956
2325
  }
1957
2326
  ),
1958
- attachments.length > 0 && /* @__PURE__ */ jsx8(
2327
+ attachments.length > 0 && /* @__PURE__ */ jsx10(
1959
2328
  "div",
1960
2329
  {
1961
2330
  "data-testid": "file-attachments",
@@ -1965,18 +2334,19 @@ function UseAIChatPanel({
1965
2334
  gap: "8px",
1966
2335
  marginBottom: "8px"
1967
2336
  },
1968
- children: attachments.map((attachment) => /* @__PURE__ */ jsx8(
2337
+ children: attachments.map((attachment) => /* @__PURE__ */ jsx10(
1969
2338
  FileChip,
1970
2339
  {
1971
2340
  attachment,
1972
2341
  onRemove: () => removeAttachment(attachment.id),
1973
- disabled: loading
2342
+ disabled: loading,
2343
+ processingState: fileProcessingState.get(attachment.id)
1974
2344
  },
1975
2345
  attachment.id
1976
2346
  ))
1977
2347
  }
1978
2348
  ),
1979
- /* @__PURE__ */ jsxs5(
2349
+ /* @__PURE__ */ jsxs7(
1980
2350
  "div",
1981
2351
  {
1982
2352
  style: {
@@ -1988,18 +2358,19 @@ function UseAIChatPanel({
1988
2358
  },
1989
2359
  children: [
1990
2360
  slashCommands.AutocompleteComponent,
1991
- /* @__PURE__ */ jsx8(
2361
+ /* @__PURE__ */ jsx10(
1992
2362
  "input",
1993
2363
  {
1994
2364
  ref: fileInputRef,
1995
2365
  type: "file",
1996
2366
  multiple: true,
2367
+ "data-testid": "file-input",
1997
2368
  style: { display: "none" },
1998
2369
  onChange: handleFileInputChange,
1999
2370
  accept: acceptedTypes?.join(",")
2000
2371
  }
2001
2372
  ),
2002
- /* @__PURE__ */ jsx8(
2373
+ /* @__PURE__ */ jsx10(
2003
2374
  "textarea",
2004
2375
  {
2005
2376
  ref: textareaRef,
@@ -2027,7 +2398,7 @@ function UseAIChatPanel({
2027
2398
  }
2028
2399
  }
2029
2400
  ),
2030
- /* @__PURE__ */ jsxs5(
2401
+ /* @__PURE__ */ jsxs7(
2031
2402
  "div",
2032
2403
  {
2033
2404
  style: {
@@ -2037,7 +2408,7 @@ function UseAIChatPanel({
2037
2408
  padding: "4px 8px"
2038
2409
  },
2039
2410
  children: [
2040
- /* @__PURE__ */ jsx8("div", { style: { display: "flex", alignItems: "center", gap: "8px" }, children: fileUploadEnabled && /* @__PURE__ */ jsx8(
2411
+ /* @__PURE__ */ jsx10("div", { style: { display: "flex", alignItems: "center", gap: "8px" }, children: fileUploadEnabled && /* @__PURE__ */ jsx10(
2041
2412
  "button",
2042
2413
  {
2043
2414
  "data-testid": "file-picker-button",
@@ -2069,13 +2440,13 @@ function UseAIChatPanel({
2069
2440
  e.currentTarget.style.borderColor = theme.borderColor;
2070
2441
  },
2071
2442
  title: strings.fileUpload.attachFiles,
2072
- children: /* @__PURE__ */ jsxs5("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: [
2073
- /* @__PURE__ */ jsx8("line", { x1: "12", y1: "5", x2: "12", y2: "19" }),
2074
- /* @__PURE__ */ jsx8("line", { x1: "5", y1: "12", x2: "19", y2: "12" })
2443
+ children: /* @__PURE__ */ jsxs7("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: [
2444
+ /* @__PURE__ */ jsx10("line", { x1: "12", y1: "5", x2: "12", y2: "19" }),
2445
+ /* @__PURE__ */ jsx10("line", { x1: "5", y1: "12", x2: "19", y2: "12" })
2075
2446
  ] })
2076
2447
  }
2077
2448
  ) }),
2078
- /* @__PURE__ */ jsx8(
2449
+ /* @__PURE__ */ jsx10(
2079
2450
  "button",
2080
2451
  {
2081
2452
  "data-testid": "chat-send-button",
@@ -2096,9 +2467,9 @@ function UseAIChatPanel({
2096
2467
  height: "32px",
2097
2468
  transition: "all 0.2s"
2098
2469
  },
2099
- children: /* @__PURE__ */ jsxs5("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: [
2100
- /* @__PURE__ */ jsx8("line", { x1: "12", y1: "19", x2: "12", y2: "5" }),
2101
- /* @__PURE__ */ jsx8("polyline", { points: "5 12 12 5 19 12" })
2470
+ children: /* @__PURE__ */ jsxs7("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: [
2471
+ /* @__PURE__ */ jsx10("line", { x1: "12", y1: "19", x2: "12", y2: "5" }),
2472
+ /* @__PURE__ */ jsx10("polyline", { points: "5 12 12 5 19 12" })
2102
2473
  ] })
2103
2474
  }
2104
2475
  )
@@ -2111,7 +2482,7 @@ function UseAIChatPanel({
2111
2482
  ]
2112
2483
  }
2113
2484
  ),
2114
- /* @__PURE__ */ jsx8("style", { children: `
2485
+ /* @__PURE__ */ jsx10("style", { children: `
2115
2486
  /* Markdown content styles */
2116
2487
  .markdown-content > :first-child {
2117
2488
  margin-top: 0 !important;
@@ -2136,7 +2507,7 @@ function UseAIChatPanel({
2136
2507
  }
2137
2508
 
2138
2509
  // src/components/UseAIFloatingChatWrapper.tsx
2139
- import { Fragment as Fragment2, jsx as jsx9, jsxs as jsxs6 } from "react/jsx-runtime";
2510
+ import { Fragment as Fragment2, jsx as jsx11, jsxs as jsxs8 } from "react/jsx-runtime";
2140
2511
  function UseAIFloatingChatWrapper({
2141
2512
  isOpen,
2142
2513
  onClose,
@@ -2144,8 +2515,8 @@ function UseAIFloatingChatWrapper({
2144
2515
  }) {
2145
2516
  const theme = useTheme();
2146
2517
  if (!isOpen) return null;
2147
- return /* @__PURE__ */ jsxs6(Fragment2, { children: [
2148
- /* @__PURE__ */ jsx9(
2518
+ return /* @__PURE__ */ jsxs8(Fragment2, { children: [
2519
+ /* @__PURE__ */ jsx11(
2149
2520
  "div",
2150
2521
  {
2151
2522
  style: {
@@ -2161,7 +2532,7 @@ function UseAIFloatingChatWrapper({
2161
2532
  onClick: onClose
2162
2533
  }
2163
2534
  ),
2164
- /* @__PURE__ */ jsx9(
2535
+ /* @__PURE__ */ jsx11(
2165
2536
  "div",
2166
2537
  {
2167
2538
  style: {
@@ -2180,7 +2551,7 @@ function UseAIFloatingChatWrapper({
2180
2551
  children
2181
2552
  }
2182
2553
  ),
2183
- /* @__PURE__ */ jsx9("style", { children: `
2554
+ /* @__PURE__ */ jsx11("style", { children: `
2184
2555
  @keyframes fadeIn {
2185
2556
  from { opacity: 0; }
2186
2557
  to { opacity: 1; }
@@ -2200,7 +2571,7 @@ function UseAIFloatingChatWrapper({
2200
2571
  }
2201
2572
  function CloseButton({ onClick }) {
2202
2573
  const theme = useTheme();
2203
- return /* @__PURE__ */ jsx9(
2574
+ return /* @__PURE__ */ jsx11(
2204
2575
  "button",
2205
2576
  {
2206
2577
  "data-testid": "chat-close-button",
@@ -2235,7 +2606,7 @@ function CloseButton({ onClick }) {
2235
2606
 
2236
2607
  // src/components/UseAIChat.tsx
2237
2608
  import { createContext as createContext3, useContext as useContext3 } from "react";
2238
- import { jsx as jsx10 } from "react/jsx-runtime";
2609
+ import { jsx as jsx12 } from "react/jsx-runtime";
2239
2610
  var __UseAIChatContext = createContext3(null);
2240
2611
  function useChatUIContext() {
2241
2612
  const context = useContext3(__UseAIChatContext);
@@ -2259,34 +2630,36 @@ function UseAIChat({ floating = false }) {
2259
2630
  onLoadChat: ctx.history.load,
2260
2631
  onDeleteChat: ctx.history.delete,
2261
2632
  onListChats: ctx.history.list,
2633
+ onGetChat: ctx.history.get,
2262
2634
  suggestions: ctx.suggestions,
2263
2635
  availableAgents: ctx.agents.available,
2264
2636
  defaultAgent: ctx.agents.default,
2265
2637
  selectedAgent: ctx.agents.selected,
2266
2638
  onAgentChange: ctx.agents.set,
2267
2639
  fileUploadConfig: ctx.fileUploadConfig,
2640
+ fileProcessing: ctx.fileProcessing,
2268
2641
  commands: ctx.commands.list,
2269
2642
  onSaveCommand: ctx.commands.save,
2270
2643
  onRenameCommand: ctx.commands.rename,
2271
2644
  onDeleteCommand: ctx.commands.delete
2272
2645
  };
2273
2646
  if (floating) {
2274
- return /* @__PURE__ */ jsx10(
2647
+ return /* @__PURE__ */ jsx12(
2275
2648
  UseAIFloatingChatWrapper,
2276
2649
  {
2277
2650
  isOpen: ctx.ui.isOpen,
2278
2651
  onClose: () => ctx.ui.setOpen(false),
2279
- children: /* @__PURE__ */ jsx10(
2652
+ children: /* @__PURE__ */ jsx12(
2280
2653
  UseAIChatPanel,
2281
2654
  {
2282
2655
  ...chatPanelProps,
2283
- closeButton: /* @__PURE__ */ jsx10(CloseButton, { onClick: () => ctx.ui.setOpen(false) })
2656
+ closeButton: /* @__PURE__ */ jsx12(CloseButton, { onClick: () => ctx.ui.setOpen(false) })
2284
2657
  }
2285
2658
  )
2286
2659
  }
2287
2660
  );
2288
2661
  }
2289
- return /* @__PURE__ */ jsx10(UseAIChatPanel, { ...chatPanelProps });
2662
+ return /* @__PURE__ */ jsx12(UseAIChatPanel, { ...chatPanelProps });
2290
2663
  }
2291
2664
 
2292
2665
  // src/client.ts
@@ -2487,12 +2860,18 @@ var UseAIClient = class {
2487
2860
  return { type: "text", text: part.text };
2488
2861
  } else if (part.type === "image") {
2489
2862
  return { type: "image", url: part.url };
2490
- } else {
2863
+ } else if (part.type === "file") {
2491
2864
  return {
2492
2865
  type: "file",
2493
2866
  url: part.url,
2494
2867
  mimeType: part.mimeType
2495
2868
  };
2869
+ } else {
2870
+ return {
2871
+ type: "transformed_file",
2872
+ text: part.text,
2873
+ originalFile: part.originalFile
2874
+ };
2496
2875
  }
2497
2876
  });
2498
2877
  }
@@ -2867,7 +3246,8 @@ var LocalStorageChatRepository = class {
2867
3246
  title: options?.title,
2868
3247
  messages: [],
2869
3248
  createdAt: now,
2870
- updatedAt: now
3249
+ updatedAt: now,
3250
+ metadata: options?.metadata
2871
3251
  };
2872
3252
  await this.enforceMaxChatsLimit();
2873
3253
  await this.saveChat(chat);
@@ -2951,6 +3331,14 @@ var LocalStorageChatRepository = class {
2951
3331
  throw new Error(`Failed to clear all chats: ${error instanceof Error ? error.message : "Unknown error"}`);
2952
3332
  }
2953
3333
  }
3334
+ async updateMetadata(id, metadata, overwrite = false) {
3335
+ const chat = await this.loadChat(id);
3336
+ if (!chat) {
3337
+ throw new Error(`Chat not found: ${id}`);
3338
+ }
3339
+ chat.metadata = overwrite ? metadata : { ...chat.metadata, ...metadata };
3340
+ await this.saveChat(chat);
3341
+ }
2954
3342
  getChatKey(id) {
2955
3343
  return `${STORAGE_KEY_PREFIX}${id}`;
2956
3344
  }
@@ -2989,36 +3377,12 @@ var LocalStorageChatRepository = class {
2989
3377
  }
2990
3378
  };
2991
3379
 
2992
- // src/fileUpload/EmbedFileUploadBackend.ts
2993
- var EmbedFileUploadBackend = class {
2994
- /**
2995
- * Converts a File to a base64 data URL.
2996
- *
2997
- * @param file - The File object to convert
2998
- * @returns Promise resolving to a base64 data URL (e.g., "data:image/png;base64,...")
2999
- * @throws Error if file reading fails
3000
- */
3001
- async prepareForSend(file) {
3002
- return new Promise((resolve, reject) => {
3003
- const reader = new FileReader();
3004
- reader.onload = () => {
3005
- if (typeof reader.result === "string") {
3006
- resolve(reader.result);
3007
- } else {
3008
- reject(new Error("Failed to read file as data URL"));
3009
- }
3010
- };
3011
- reader.onerror = () => {
3012
- reject(new Error(`Failed to read file: ${file.name}`));
3013
- };
3014
- reader.readAsDataURL(file);
3015
- });
3016
- }
3017
- };
3018
-
3019
3380
  // src/hooks/useChatManagement.ts
3020
3381
  import { useState as useState5, useCallback as useCallback4, useRef as useRef5, useEffect as useEffect5 } from "react";
3021
3382
  var CHAT_TITLE_MAX_LENGTH = 50;
3383
+ function deepEquals(a, b) {
3384
+ return JSON.stringify(a) === JSON.stringify(b);
3385
+ }
3022
3386
  function generateChatTitle(message) {
3023
3387
  return message.length > CHAT_TITLE_MAX_LENGTH ? message.substring(0, CHAT_TITLE_MAX_LENGTH) + "..." : message;
3024
3388
  }
@@ -3049,7 +3413,11 @@ function transformMessagesToClientFormat(uiMessages) {
3049
3413
  }
3050
3414
  function useChatManagement({
3051
3415
  repository,
3052
- clientRef
3416
+ clientRef,
3417
+ onSendMessage,
3418
+ setOpen,
3419
+ connected,
3420
+ loading
3053
3421
  }) {
3054
3422
  const [currentChatId, setCurrentChatId] = useState5(null);
3055
3423
  const [pendingChatId, setPendingChatId] = useState5(null);
@@ -3082,18 +3450,18 @@ function useChatManagement({
3082
3450
  const loadedMessages = await loadChatMessages(chatId);
3083
3451
  setMessages(loadedMessages);
3084
3452
  }, [loadChatMessages]);
3085
- const createNewChat = useCallback4(async () => {
3453
+ const createNewChat = useCallback4(async (options) => {
3086
3454
  console.log("[ChatManagement] createNewChat called - currentChatId:", currentChatId, "pendingChatId:", pendingChatId, "messages.length:", messages.length);
3087
3455
  if (pendingChatId && messages.length === 0) {
3088
- console.log("[ChatManagement] Pending chat is already blank, not creating new chat");
3089
- return pendingChatId;
3090
- }
3091
- if (currentChatId && !pendingChatId && messages.length === 0) {
3092
- console.log("[ChatManagement] Current chat is already blank, not creating new chat");
3093
- return currentChatId;
3456
+ const existingChat = await repository.loadChat(pendingChatId);
3457
+ const optionsMatch = existingChat && existingChat.title === options?.title && deepEquals(existingChat.metadata, options?.metadata);
3458
+ if (optionsMatch) {
3459
+ console.log("[ChatManagement] Last created chat has matching options, reusing:", pendingChatId);
3460
+ return pendingChatId;
3461
+ }
3094
3462
  }
3095
3463
  console.log("[ChatManagement] Creating new chat...");
3096
- const chatId = await repository.createChat();
3464
+ const chatId = await repository.createChat(options);
3097
3465
  setPendingChatId(chatId);
3098
3466
  setMessages([]);
3099
3467
  if (clientRef.current) {
@@ -3138,6 +3506,22 @@ function useChatManagement({
3138
3506
  }
3139
3507
  }
3140
3508
  }, [currentChatId, repository]);
3509
+ const getCurrentChat = useCallback4(async () => {
3510
+ const chatId = pendingChatId || currentChatId;
3511
+ if (!chatId) return null;
3512
+ const chat = await repository.loadChat(chatId);
3513
+ if (chat?.metadata) {
3514
+ chat.metadata = Object.freeze({ ...chat.metadata });
3515
+ }
3516
+ return chat;
3517
+ }, [pendingChatId, currentChatId, repository]);
3518
+ const updateMetadata = useCallback4(async (metadata, overwrite = false) => {
3519
+ const chatId = pendingChatId || currentChatId;
3520
+ if (!chatId) {
3521
+ throw new Error("No active chat");
3522
+ }
3523
+ await repository.updateMetadata(chatId, metadata, overwrite);
3524
+ }, [pendingChatId, currentChatId, repository]);
3141
3525
  const activatePendingChat = useCallback4(() => {
3142
3526
  if (!pendingChatId) return null;
3143
3527
  console.log("[ChatManagement] Activating pending chat:", pendingChatId);
@@ -3156,7 +3540,7 @@ function useChatManagement({
3156
3540
  console.error("[ChatManagement] Chat not found:", chatId);
3157
3541
  return false;
3158
3542
  }
3159
- const { generateMessageId: generateMessageId2 } = await import("./types-TVUXB3NB.js");
3543
+ const { generateMessageId: generateMessageId2 } = await import("./types-64CH2HXY.js");
3160
3544
  chat.messages.push({
3161
3545
  id: generateMessageId2(),
3162
3546
  role: "user",
@@ -3192,7 +3576,7 @@ function useChatManagement({
3192
3576
  console.error("[ChatManagement] Chat not found:", currentChatIdValue);
3193
3577
  return;
3194
3578
  }
3195
- const { generateMessageId: generateMessageId2 } = await import("./types-TVUXB3NB.js");
3579
+ const { generateMessageId: generateMessageId2 } = await import("./types-64CH2HXY.js");
3196
3580
  chat.messages.push({
3197
3581
  id: generateMessageId2(),
3198
3582
  role: "assistant",
@@ -3249,6 +3633,70 @@ function useChatManagement({
3249
3633
  }
3250
3634
  }, [currentChatId, pendingChatId, createNewChat, repository, loadChatMessages, clientRef]);
3251
3635
  const displayedChatId = pendingChatId || currentChatId;
3636
+ const pendingMessagesRef = useRef5([]);
3637
+ const isProcessingQueueRef = useRef5(false);
3638
+ const loadingRef = useRef5(loading);
3639
+ useEffect5(() => {
3640
+ loadingRef.current = loading;
3641
+ }, [loading]);
3642
+ const processMessageQueue = useCallback4(async () => {
3643
+ if (isProcessingQueueRef.current || pendingMessagesRef.current.length === 0 || !onSendMessage) {
3644
+ return;
3645
+ }
3646
+ isProcessingQueueRef.current = true;
3647
+ while (pendingMessagesRef.current.length > 0) {
3648
+ const { message, options } = pendingMessagesRef.current.shift();
3649
+ const { newChat = false, attachments = [], openChat = true, metadata } = options ?? {};
3650
+ if (newChat) {
3651
+ await createNewChat({ metadata });
3652
+ }
3653
+ const fileAttachments = await Promise.all(
3654
+ attachments.map(async (file) => {
3655
+ let preview;
3656
+ if (file.type.startsWith("image/")) {
3657
+ preview = await new Promise((resolve) => {
3658
+ const reader = new FileReader();
3659
+ reader.onload = () => resolve(typeof reader.result === "string" ? reader.result : void 0);
3660
+ reader.onerror = () => resolve(void 0);
3661
+ reader.readAsDataURL(file);
3662
+ });
3663
+ }
3664
+ return {
3665
+ id: crypto.randomUUID(),
3666
+ file,
3667
+ preview
3668
+ };
3669
+ })
3670
+ );
3671
+ await onSendMessage(message, fileAttachments.length > 0 ? fileAttachments : void 0);
3672
+ if (openChat && setOpen) {
3673
+ setOpen(true);
3674
+ }
3675
+ await new Promise((resolve) => {
3676
+ const checkLoading = () => {
3677
+ setTimeout(() => {
3678
+ if (!loadingRef.current) {
3679
+ resolve();
3680
+ } else {
3681
+ checkLoading();
3682
+ }
3683
+ }, 100);
3684
+ };
3685
+ checkLoading();
3686
+ });
3687
+ }
3688
+ isProcessingQueueRef.current = false;
3689
+ }, [onSendMessage, createNewChat, setOpen]);
3690
+ const sendMessage = useCallback4(async (message, options) => {
3691
+ if (!onSendMessage) {
3692
+ throw new Error("sendMessage is not available (onSendMessage callback not provided)");
3693
+ }
3694
+ if (!connected) {
3695
+ throw new Error("Not connected to UseAI server");
3696
+ }
3697
+ pendingMessagesRef.current.push({ message, options });
3698
+ await processMessageQueue();
3699
+ }, [onSendMessage, connected, processMessageQueue]);
3252
3700
  return {
3253
3701
  currentChatId,
3254
3702
  pendingChatId,
@@ -3263,6 +3711,9 @@ function useChatManagement({
3263
3711
  saveUserMessage,
3264
3712
  saveAIResponse,
3265
3713
  reloadMessages,
3714
+ sendMessage,
3715
+ getCurrentChat,
3716
+ updateMetadata,
3266
3717
  currentChatIdSnapshot,
3267
3718
  pendingChatIdSnapshot
3268
3719
  };
@@ -3644,7 +4095,7 @@ function usePromptState({
3644
4095
  }
3645
4096
 
3646
4097
  // src/providers/useAIProvider.tsx
3647
- import { Fragment as Fragment3, jsx as jsx11, jsxs as jsxs7 } from "react/jsx-runtime";
4098
+ import { Fragment as Fragment3, jsx as jsx13, jsxs as jsxs9 } from "react/jsx-runtime";
3648
4099
  var __UseAIContext = createContext4(null);
3649
4100
  var hasWarnedAboutMissingProvider = false;
3650
4101
  var noOpContextValue = {
@@ -3674,6 +4125,11 @@ var noOpContextValue = {
3674
4125
  },
3675
4126
  list: async () => [],
3676
4127
  clear: async () => {
4128
+ },
4129
+ sendMessage: async () => {
4130
+ },
4131
+ get: async () => null,
4132
+ updateMetadata: async () => {
3677
4133
  }
3678
4134
  },
3679
4135
  agents: {
@@ -3713,7 +4169,8 @@ function UseAIProvider({
3713
4169
  renderChat = true,
3714
4170
  theme: customTheme,
3715
4171
  strings: customStrings,
3716
- visibleAgentIds
4172
+ visibleAgentIds,
4173
+ onOpenChange
3717
4174
  }) {
3718
4175
  const fileUploadConfig = fileUploadConfigProp === false ? void 0 : fileUploadConfigProp ?? DEFAULT_FILE_UPLOAD_CONFIG;
3719
4176
  const theme = { ...defaultTheme, ...customTheme };
@@ -3721,12 +4178,18 @@ function UseAIProvider({
3721
4178
  const [connected, setConnected] = useState10(false);
3722
4179
  const [isChatOpen, setIsChatOpen] = useState10(false);
3723
4180
  const [loading, setLoading] = useState10(false);
4181
+ const [fileProcessingState, setFileProcessingState] = useState10(null);
4182
+ const handleSetChatOpen = useCallback9((open) => {
4183
+ setIsChatOpen(open);
4184
+ onOpenChange?.(open);
4185
+ }, [onOpenChange]);
3724
4186
  const [streamingText, setStreamingText] = useState10("");
3725
4187
  const streamingChatIdRef = useRef9(null);
3726
4188
  const clientRef = useRef9(null);
3727
4189
  const repositoryRef = useRef9(
3728
4190
  chatRepository || new LocalStorageChatRepository()
3729
4191
  );
4192
+ const handleSendMessageRef = useRef9(null);
3730
4193
  const {
3731
4194
  registerTools,
3732
4195
  unregisterTools,
@@ -3748,9 +4211,18 @@ function UseAIProvider({
3748
4211
  clientRef,
3749
4212
  connected
3750
4213
  });
4214
+ const stableSendMessage = useCallback9(async (message, attachments) => {
4215
+ if (handleSendMessageRef.current) {
4216
+ await handleSendMessageRef.current(message, attachments);
4217
+ }
4218
+ }, []);
3751
4219
  const chatManagement = useChatManagement({
3752
4220
  repository: repositoryRef.current,
3753
- clientRef
4221
+ clientRef,
4222
+ onSendMessage: stableSendMessage,
4223
+ setOpen: handleSetChatOpen,
4224
+ connected,
4225
+ loading
3754
4226
  });
3755
4227
  const {
3756
4228
  currentChatId,
@@ -3764,7 +4236,10 @@ function UseAIProvider({
3764
4236
  clearCurrentChat,
3765
4237
  activatePendingChat,
3766
4238
  saveUserMessage,
3767
- saveAIResponse
4239
+ saveAIResponse,
4240
+ sendMessage,
4241
+ getCurrentChat,
4242
+ updateMetadata
3768
4243
  } = chatManagement;
3769
4244
  const {
3770
4245
  availableAgents,
@@ -3905,7 +4380,6 @@ function UseAIProvider({
3905
4380
  let persistedContent = message;
3906
4381
  let multimodalContent;
3907
4382
  if (attachments && attachments.length > 0) {
3908
- const backend = fileUploadConfig?.backend ?? new EmbedFileUploadBackend();
3909
4383
  const persistedParts = [];
3910
4384
  if (message.trim()) {
3911
4385
  persistedParts.push({ type: "text", text: message });
@@ -3921,35 +4395,39 @@ function UseAIProvider({
3921
4395
  });
3922
4396
  }
3923
4397
  persistedContent = persistedParts;
3924
- const contentParts = [];
3925
- if (message.trim()) {
3926
- contentParts.push({ type: "text", text: message });
4398
+ if (activeChatId) {
4399
+ await saveUserMessage(activeChatId, persistedContent);
3927
4400
  }
3928
- for (const attachment of attachments) {
3929
- try {
3930
- const url = await backend.prepareForSend(attachment.file);
3931
- if (attachment.file.type.startsWith("image/")) {
3932
- contentParts.push({ type: "image", url });
3933
- } else {
3934
- contentParts.push({
3935
- type: "file",
3936
- url,
3937
- mimeType: attachment.file.type,
3938
- name: attachment.file.name
3939
- });
4401
+ setLoading(true);
4402
+ try {
4403
+ const fileContent = await processAttachments(attachments, {
4404
+ getCurrentChat,
4405
+ backend: fileUploadConfig?.backend,
4406
+ transformers: fileUploadConfig?.transformers,
4407
+ onFileProgress: (_fileId, state) => {
4408
+ setFileProcessingState(state);
3940
4409
  }
3941
- } catch (error) {
3942
- console.error("[Provider] Failed to prepare file for send:", error);
4410
+ });
4411
+ multimodalContent = [];
4412
+ if (message.trim()) {
4413
+ multimodalContent.push({ type: "text", text: message });
3943
4414
  }
4415
+ multimodalContent.push(...fileContent);
4416
+ } catch (error) {
4417
+ setLoading(false);
4418
+ throw error;
4419
+ } finally {
4420
+ setFileProcessingState(null);
3944
4421
  }
3945
- multimodalContent = contentParts;
3946
- }
3947
- if (activeChatId) {
3948
- await saveUserMessage(activeChatId, persistedContent);
4422
+ } else {
4423
+ if (activeChatId) {
4424
+ await saveUserMessage(activeChatId, persistedContent);
4425
+ }
4426
+ setLoading(true);
3949
4427
  }
3950
- setLoading(true);
3951
4428
  await clientRef.current.sendPrompt(message, multimodalContent);
3952
- }, [activatePendingChat, currentChatId, saveUserMessage, fileUploadConfig]);
4429
+ }, [activatePendingChat, currentChatId, saveUserMessage, fileUploadConfig, getCurrentChat]);
4430
+ handleSendMessageRef.current = handleSendMessage;
3953
4431
  const value = {
3954
4432
  serverUrl,
3955
4433
  connected,
@@ -3969,7 +4447,10 @@ function UseAIProvider({
3969
4447
  load: loadChat,
3970
4448
  delete: deleteChat,
3971
4449
  list: listChats,
3972
- clear: clearCurrentChat
4450
+ clear: clearCurrentChat,
4451
+ sendMessage,
4452
+ get: getCurrentChat,
4453
+ updateMetadata
3973
4454
  },
3974
4455
  agents: {
3975
4456
  available: availableAgents,
@@ -3994,12 +4475,14 @@ function UseAIProvider({
3994
4475
  streamingText: effectiveStreamingText,
3995
4476
  suggestions: aggregatedSuggestions,
3996
4477
  fileUploadConfig,
4478
+ fileProcessing: fileProcessingState,
3997
4479
  history: {
3998
4480
  currentId: displayedChatId,
3999
4481
  create: createNewChat,
4000
4482
  load: loadChat,
4001
4483
  delete: deleteChat,
4002
- list: listChats
4484
+ list: listChats,
4485
+ get: getCurrentChat
4003
4486
  },
4004
4487
  agents: {
4005
4488
  available: availableAgents,
@@ -4015,7 +4498,7 @@ function UseAIProvider({
4015
4498
  },
4016
4499
  ui: {
4017
4500
  isOpen: isChatOpen,
4018
- setOpen: setIsChatOpen
4501
+ setOpen: handleSetChatOpen
4019
4502
  }
4020
4503
  };
4021
4504
  const isUIDisabled = CustomButton === null || CustomChat === null;
@@ -4038,6 +4521,7 @@ function UseAIProvider({
4038
4521
  selectedAgent,
4039
4522
  onAgentChange: setAgent,
4040
4523
  fileUploadConfig,
4524
+ fileProcessing: fileProcessingState,
4041
4525
  commands,
4042
4526
  onSaveCommand: saveCommand,
4043
4527
  onRenameCommand: renameCommand,
@@ -4045,21 +4529,21 @@ function UseAIProvider({
4045
4529
  };
4046
4530
  const renderDefaultChat = () => {
4047
4531
  if (isUIDisabled) return null;
4048
- return /* @__PURE__ */ jsx11(UseAIFloatingChatWrapper, { isOpen: isChatOpen, onClose: () => setIsChatOpen(false), children: /* @__PURE__ */ jsx11(
4532
+ return /* @__PURE__ */ jsx13(UseAIFloatingChatWrapper, { isOpen: isChatOpen, onClose: () => handleSetChatOpen(false), children: /* @__PURE__ */ jsx13(
4049
4533
  UseAIChatPanel,
4050
4534
  {
4051
4535
  ...chatPanelProps,
4052
- closeButton: /* @__PURE__ */ jsx11(CloseButton, { onClick: () => setIsChatOpen(false) })
4536
+ closeButton: /* @__PURE__ */ jsx13(CloseButton, { onClick: () => handleSetChatOpen(false) })
4053
4537
  }
4054
4538
  ) });
4055
4539
  };
4056
4540
  const renderCustomChat = () => {
4057
4541
  if (!CustomChat) return null;
4058
- return /* @__PURE__ */ jsx11(
4542
+ return /* @__PURE__ */ jsx13(
4059
4543
  CustomChat,
4060
4544
  {
4061
4545
  isOpen: isChatOpen,
4062
- onClose: () => setIsChatOpen(false),
4546
+ onClose: () => handleSetChatOpen(false),
4063
4547
  onSendMessage: handleSendMessage,
4064
4548
  messages,
4065
4549
  loading,
@@ -4074,18 +4558,18 @@ function UseAIProvider({
4074
4558
  };
4075
4559
  const renderBuiltInChat = () => {
4076
4560
  if (!renderChat) return null;
4077
- return /* @__PURE__ */ jsxs7(Fragment3, { children: [
4078
- ButtonComponent && /* @__PURE__ */ jsx11(
4561
+ return /* @__PURE__ */ jsxs9(Fragment3, { children: [
4562
+ ButtonComponent && /* @__PURE__ */ jsx13(
4079
4563
  ButtonComponent,
4080
4564
  {
4081
- onClick: () => setIsChatOpen(true),
4565
+ onClick: () => handleSetChatOpen(true),
4082
4566
  connected
4083
4567
  }
4084
4568
  ),
4085
4569
  hasCustomChat ? renderCustomChat() : renderDefaultChat()
4086
4570
  ] });
4087
4571
  };
4088
- return /* @__PURE__ */ jsx11(ThemeContext.Provider, { value: theme, children: /* @__PURE__ */ jsx11(StringsContext.Provider, { value: strings, children: /* @__PURE__ */ jsx11(__UseAIContext.Provider, { value, children: /* @__PURE__ */ jsxs7(__UseAIChatContext.Provider, { value: chatUIContextValue, children: [
4572
+ return /* @__PURE__ */ jsx13(ThemeContext.Provider, { value: theme, children: /* @__PURE__ */ jsx13(StringsContext.Provider, { value: strings, children: /* @__PURE__ */ jsx13(__UseAIContext.Provider, { value, children: /* @__PURE__ */ jsxs9(__UseAIChatContext.Provider, { value: chatUIContextValue, children: [
4089
4573
  children,
4090
4574
  renderBuiltInChat()
4091
4575
  ] }) }) }) });
@@ -4488,14 +4972,18 @@ export {
4488
4972
  UseAIFloatingButton,
4489
4973
  UseAIFloatingChatWrapper,
4490
4974
  UseAIProvider,
4975
+ clearTransformationCache,
4491
4976
  convertToolsToDefinitions,
4492
4977
  defaultStrings,
4493
4978
  defaultTheme,
4494
4979
  defineTool,
4495
4980
  executeDefinedTool,
4981
+ findTransformer,
4496
4982
  generateChatId,
4497
4983
  generateCommandId,
4498
4984
  generateMessageId,
4985
+ matchesMimeType,
4986
+ processAttachments,
4499
4987
  useAI,
4500
4988
  useAIContext,
4501
4989
  useAIWorkflow,