@meetsmore-oss/use-ai-client 1.2.3 → 1.3.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";
@@ -50,8 +50,6 @@ var defaultStrings = {
50
50
  placeholder: "Type a message...",
51
51
  /** Input placeholder when connecting */
52
52
  connectingPlaceholder: "Connecting...",
53
- /** Send button text */
54
- send: "Send",
55
53
  /** Loading indicator text */
56
54
  thinking: "Thinking"
57
55
  },
@@ -380,8 +378,131 @@ function MarkdownContent({ content }) {
380
378
  );
381
379
  }
382
380
 
383
- // src/components/FileChip.tsx
381
+ // src/components/Spinner.tsx
384
382
  import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
383
+ function Spinner({
384
+ size = 16,
385
+ color = "currentColor",
386
+ trackColor,
387
+ strokeWidth = 2
388
+ }) {
389
+ const radius = 10;
390
+ const circumference = 2 * Math.PI * radius;
391
+ return /* @__PURE__ */ jsxs2(
392
+ "svg",
393
+ {
394
+ "data-testid": "spinner",
395
+ width: size,
396
+ height: size,
397
+ viewBox: "0 0 24 24",
398
+ style: {
399
+ animation: "use-ai-spin 1s linear infinite"
400
+ },
401
+ children: [
402
+ /* @__PURE__ */ jsx3("style", { children: `
403
+ @keyframes use-ai-spin {
404
+ from { transform: rotate(0deg); }
405
+ to { transform: rotate(360deg); }
406
+ }
407
+ ` }),
408
+ /* @__PURE__ */ jsx3(
409
+ "circle",
410
+ {
411
+ cx: "12",
412
+ cy: "12",
413
+ r: radius,
414
+ fill: "none",
415
+ stroke: trackColor || color,
416
+ strokeWidth,
417
+ opacity: trackColor ? 1 : 0.25
418
+ }
419
+ ),
420
+ /* @__PURE__ */ jsx3(
421
+ "circle",
422
+ {
423
+ cx: "12",
424
+ cy: "12",
425
+ r: radius,
426
+ fill: "none",
427
+ stroke: color,
428
+ strokeWidth,
429
+ strokeLinecap: "round",
430
+ strokeDasharray: circumference,
431
+ strokeDashoffset: circumference * 0.75,
432
+ style: {
433
+ transformOrigin: "center"
434
+ }
435
+ }
436
+ )
437
+ ]
438
+ }
439
+ );
440
+ }
441
+
442
+ // src/components/ProgressBar.tsx
443
+ import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
444
+ function ProgressBar({
445
+ progress,
446
+ size = 16,
447
+ color = "currentColor",
448
+ trackColor,
449
+ strokeWidth = 2
450
+ }) {
451
+ const clampedProgress = Math.min(100, Math.max(0, progress));
452
+ const radius = 10;
453
+ const circumference = 2 * Math.PI * radius;
454
+ const strokeDashoffset = circumference * (1 - clampedProgress / 100);
455
+ return /* @__PURE__ */ jsxs3(
456
+ "svg",
457
+ {
458
+ "data-testid": "progress-bar",
459
+ role: "progressbar",
460
+ "aria-valuenow": clampedProgress,
461
+ "aria-valuemin": 0,
462
+ "aria-valuemax": 100,
463
+ width: size,
464
+ height: size,
465
+ viewBox: "0 0 24 24",
466
+ style: {
467
+ transform: "rotate(-90deg)"
468
+ },
469
+ children: [
470
+ /* @__PURE__ */ jsx4(
471
+ "circle",
472
+ {
473
+ cx: "12",
474
+ cy: "12",
475
+ r: radius,
476
+ fill: "none",
477
+ stroke: trackColor || color,
478
+ strokeWidth,
479
+ opacity: trackColor ? 1 : 0.25
480
+ }
481
+ ),
482
+ /* @__PURE__ */ jsx4(
483
+ "circle",
484
+ {
485
+ cx: "12",
486
+ cy: "12",
487
+ r: radius,
488
+ fill: "none",
489
+ stroke: color,
490
+ strokeWidth,
491
+ strokeLinecap: "round",
492
+ strokeDasharray: circumference,
493
+ strokeDashoffset,
494
+ style: {
495
+ transition: "stroke-dashoffset 0.2s ease"
496
+ }
497
+ }
498
+ )
499
+ ]
500
+ }
501
+ );
502
+ }
503
+
504
+ // src/components/FileChip.tsx
505
+ import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
385
506
  function formatFileSize(bytes) {
386
507
  if (bytes < 1024) return `${bytes} B`;
387
508
  if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
@@ -396,11 +517,14 @@ function truncateFilename(name, maxLength = 20) {
396
517
  if (maxBaseLength < 5) return name.substring(0, maxLength - 3) + "...";
397
518
  return baseName.substring(0, maxBaseLength) + "..." + ext;
398
519
  }
399
- function FileChip({ attachment, onRemove, disabled }) {
520
+ function FileChip({ attachment, onRemove, disabled, processingState }) {
400
521
  const theme = useTheme();
401
522
  const { file, preview } = attachment;
402
523
  const isImage = file.type.startsWith("image/");
403
- return /* @__PURE__ */ jsxs2(
524
+ const isProcessing = processingState?.status === "processing";
525
+ const hasError = processingState?.status === "error";
526
+ const progress = processingState?.progress;
527
+ return /* @__PURE__ */ jsxs4(
404
528
  "div",
405
529
  {
406
530
  "data-testid": "file-chip",
@@ -413,10 +537,12 @@ function FileChip({ attachment, onRemove, disabled }) {
413
537
  borderRadius: "8px",
414
538
  fontSize: "13px",
415
539
  color: theme.textColor,
416
- maxWidth: "200px"
540
+ maxWidth: "200px",
541
+ position: "relative",
542
+ opacity: isProcessing ? 0.7 : 1
417
543
  },
418
544
  children: [
419
- isImage && preview ? /* @__PURE__ */ jsx3(
545
+ isImage && preview ? /* @__PURE__ */ jsx5(
420
546
  "img",
421
547
  {
422
548
  src: preview,
@@ -428,7 +554,7 @@ function FileChip({ attachment, onRemove, disabled }) {
428
554
  objectFit: "cover"
429
555
  }
430
556
  }
431
- ) : /* @__PURE__ */ jsx3(
557
+ ) : /* @__PURE__ */ jsx5(
432
558
  "div",
433
559
  {
434
560
  style: {
@@ -444,8 +570,8 @@ function FileChip({ attachment, onRemove, disabled }) {
444
570
  children: "\u{1F4CE}"
445
571
  }
446
572
  ),
447
- /* @__PURE__ */ jsxs2("div", { style: { flex: 1, minWidth: 0, overflow: "hidden" }, children: [
448
- /* @__PURE__ */ jsx3(
573
+ /* @__PURE__ */ jsxs4("div", { style: { flex: 1, minWidth: 0, overflow: "hidden" }, children: [
574
+ /* @__PURE__ */ jsx5(
449
575
  "div",
450
576
  {
451
577
  style: {
@@ -458,28 +584,28 @@ function FileChip({ attachment, onRemove, disabled }) {
458
584
  children: truncateFilename(file.name)
459
585
  }
460
586
  ),
461
- /* @__PURE__ */ jsx3("div", { style: { fontSize: "11px", color: theme.secondaryTextColor }, children: formatFileSize(file.size) })
587
+ /* @__PURE__ */ jsx5("div", { style: { fontSize: "11px", color: theme.secondaryTextColor }, children: formatFileSize(file.size) })
462
588
  ] }),
463
- /* @__PURE__ */ jsx3(
589
+ /* @__PURE__ */ jsx5(
464
590
  "button",
465
591
  {
466
592
  "data-testid": "file-chip-remove",
467
593
  onClick: onRemove,
468
- disabled,
594
+ disabled: disabled || isProcessing,
469
595
  style: {
470
596
  background: "transparent",
471
597
  border: "none",
472
598
  padding: "2px 4px",
473
- cursor: disabled ? "not-allowed" : "pointer",
599
+ cursor: disabled || isProcessing ? "not-allowed" : "pointer",
474
600
  color: theme.placeholderTextColor,
475
601
  fontSize: "16px",
476
602
  lineHeight: 1,
477
603
  borderRadius: "4px",
478
604
  transition: "all 0.15s",
479
- opacity: disabled ? 0.5 : 1
605
+ opacity: disabled || isProcessing ? 0.5 : 1
480
606
  },
481
607
  onMouseEnter: (e) => {
482
- if (!disabled) {
608
+ if (!disabled && !isProcessing) {
483
609
  e.currentTarget.style.background = theme.borderColor;
484
610
  e.currentTarget.style.color = theme.textColor;
485
611
  }
@@ -490,6 +616,44 @@ function FileChip({ attachment, onRemove, disabled }) {
490
616
  },
491
617
  children: "\xD7"
492
618
  }
619
+ ),
620
+ isProcessing && /* @__PURE__ */ jsx5(
621
+ "div",
622
+ {
623
+ "data-testid": "file-chip-processing",
624
+ style: {
625
+ position: "absolute",
626
+ inset: 0,
627
+ display: "flex",
628
+ alignItems: "center",
629
+ justifyContent: "center",
630
+ background: "rgba(255, 255, 255, 0.7)",
631
+ borderRadius: "inherit"
632
+ },
633
+ children: progress !== void 0 ? /* @__PURE__ */ jsx5(ProgressBar, { progress, size: 16, color: theme.secondaryTextColor }) : /* @__PURE__ */ jsx5(Spinner, { size: 16, color: theme.secondaryTextColor })
634
+ }
635
+ ),
636
+ hasError && /* @__PURE__ */ jsx5(
637
+ "div",
638
+ {
639
+ "data-testid": "file-chip-error",
640
+ style: {
641
+ position: "absolute",
642
+ bottom: "-2px",
643
+ right: "-2px",
644
+ width: "12px",
645
+ height: "12px",
646
+ borderRadius: "50%",
647
+ background: "#ef4444",
648
+ display: "flex",
649
+ alignItems: "center",
650
+ justifyContent: "center",
651
+ fontSize: "8px",
652
+ color: "white",
653
+ fontWeight: "bold"
654
+ },
655
+ children: "!"
656
+ }
493
657
  )
494
658
  ]
495
659
  }
@@ -497,7 +661,7 @@ function FileChip({ attachment, onRemove, disabled }) {
497
661
  }
498
662
  function FilePlaceholder({ name, size }) {
499
663
  const theme = useTheme();
500
- return /* @__PURE__ */ jsxs2(
664
+ return /* @__PURE__ */ jsxs4(
501
665
  "div",
502
666
  {
503
667
  "data-testid": "file-placeholder",
@@ -514,9 +678,9 @@ function FilePlaceholder({ name, size }) {
514
678
  maxWidth: "200px"
515
679
  },
516
680
  children: [
517
- /* @__PURE__ */ jsx3("span", { children: "\u{1F4CE}" }),
518
- /* @__PURE__ */ jsxs2("div", { style: { flex: 1, minWidth: 0, overflow: "hidden" }, children: [
519
- /* @__PURE__ */ jsx3(
681
+ /* @__PURE__ */ jsx5("span", { children: "\u{1F4CE}" }),
682
+ /* @__PURE__ */ jsxs4("div", { style: { flex: 1, minWidth: 0, overflow: "hidden" }, children: [
683
+ /* @__PURE__ */ jsx5(
520
684
  "div",
521
685
  {
522
686
  style: {
@@ -528,7 +692,7 @@ function FilePlaceholder({ name, size }) {
528
692
  children: truncateFilename(name)
529
693
  }
530
694
  ),
531
- /* @__PURE__ */ jsx3("div", { style: { fontSize: "11px" }, children: formatFileSize(size) })
695
+ /* @__PURE__ */ jsx5("div", { style: { fontSize: "11px" }, children: formatFileSize(size) })
532
696
  ] })
533
697
  ]
534
698
  }
@@ -557,7 +721,7 @@ function validateCommandName(name) {
557
721
 
558
722
  // src/components/CommandAutocomplete.tsx
559
723
  import { useEffect, useRef } from "react";
560
- import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
724
+ import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
561
725
  var MAX_VISIBLE_ITEMS = 8;
562
726
  function CommandAutocomplete({
563
727
  commands,
@@ -588,7 +752,7 @@ function CommandAutocomplete({
588
752
  }
589
753
  }, [highlightedIndex]);
590
754
  if (filteredCommands.length === 0) {
591
- return /* @__PURE__ */ jsx4(
755
+ return /* @__PURE__ */ jsx6(
592
756
  "div",
593
757
  {
594
758
  "data-testid": "command-autocomplete",
@@ -604,7 +768,7 @@ function CommandAutocomplete({
604
768
  overflow: "hidden",
605
769
  zIndex: 1005
606
770
  },
607
- children: /* @__PURE__ */ jsx4(
771
+ children: /* @__PURE__ */ jsx6(
608
772
  "div",
609
773
  {
610
774
  style: {
@@ -619,7 +783,7 @@ function CommandAutocomplete({
619
783
  }
620
784
  );
621
785
  }
622
- return /* @__PURE__ */ jsx4(
786
+ return /* @__PURE__ */ jsx6(
623
787
  "div",
624
788
  {
625
789
  "data-testid": "command-autocomplete",
@@ -638,7 +802,7 @@ function CommandAutocomplete({
638
802
  overflowY: "auto",
639
803
  zIndex: 1005
640
804
  },
641
- children: filteredCommands.map((cmd, index) => /* @__PURE__ */ jsxs3(
805
+ children: filteredCommands.map((cmd, index) => /* @__PURE__ */ jsxs5(
642
806
  "div",
643
807
  {
644
808
  ref: (el) => {
@@ -658,8 +822,8 @@ function CommandAutocomplete({
658
822
  gap: "8px"
659
823
  },
660
824
  children: [
661
- /* @__PURE__ */ jsxs3("div", { style: { flex: 1, minWidth: 0 }, children: [
662
- /* @__PURE__ */ jsxs3(
825
+ /* @__PURE__ */ jsxs5("div", { style: { flex: 1, minWidth: 0 }, children: [
826
+ /* @__PURE__ */ jsxs5(
663
827
  "div",
664
828
  {
665
829
  style: {
@@ -673,7 +837,7 @@ function CommandAutocomplete({
673
837
  ]
674
838
  }
675
839
  ),
676
- /* @__PURE__ */ jsx4(
840
+ /* @__PURE__ */ jsx6(
677
841
  "div",
678
842
  {
679
843
  style: {
@@ -688,7 +852,7 @@ function CommandAutocomplete({
688
852
  }
689
853
  )
690
854
  ] }),
691
- onDelete && /* @__PURE__ */ jsx4(
855
+ onDelete && /* @__PURE__ */ jsx6(
692
856
  "button",
693
857
  {
694
858
  "data-testid": "command-delete-button",
@@ -719,7 +883,7 @@ function CommandAutocomplete({
719
883
  e.currentTarget.style.background = "transparent";
720
884
  },
721
885
  title: strings.commands.deleteCommand,
722
- 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" }) })
886
+ 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" }) })
723
887
  }
724
888
  )
725
889
  ]
@@ -737,7 +901,7 @@ function getFilteredCommandsCount(commands, searchPrefix) {
737
901
  }
738
902
 
739
903
  // src/hooks/useSlashCommands.tsx
740
- import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
904
+ import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
741
905
  var MAX_VISIBLE_ITEMS2 = 8;
742
906
  function useSlashCommands({
743
907
  commands,
@@ -872,7 +1036,7 @@ function useSlashCommands({
872
1036
  }
873
1037
  }
874
1038
  }, [savingMessageId, savingMessageText, commandNameInput, commands, onRenameCommand, onSaveCommand, cancelInlineSave, strings]);
875
- const AutocompleteComponent = showAutocomplete && commands.length > 0 ? /* @__PURE__ */ jsx5(
1039
+ const AutocompleteComponent = showAutocomplete && commands.length > 0 ? /* @__PURE__ */ jsx7(
876
1040
  CommandAutocomplete,
877
1041
  {
878
1042
  commands,
@@ -888,7 +1052,7 @@ function useSlashCommands({
888
1052
  if (savingMessageId !== messageId) {
889
1053
  return null;
890
1054
  }
891
- return /* @__PURE__ */ jsxs4(
1055
+ return /* @__PURE__ */ jsxs6(
892
1056
  "div",
893
1057
  {
894
1058
  "data-testid": "inline-save-command",
@@ -902,9 +1066,9 @@ function useSlashCommands({
902
1066
  gap: "4px"
903
1067
  },
904
1068
  children: [
905
- /* @__PURE__ */ jsxs4("div", { style: { display: "flex", alignItems: "center", gap: "6px" }, children: [
906
- /* @__PURE__ */ jsx5("span", { style: { color: theme.primaryColor, fontSize: "13px", fontWeight: 500 }, children: "/" }),
907
- /* @__PURE__ */ jsx5(
1069
+ /* @__PURE__ */ jsxs6("div", { style: { display: "flex", alignItems: "center", gap: "6px" }, children: [
1070
+ /* @__PURE__ */ jsx7("span", { style: { color: theme.primaryColor, fontSize: "13px", fontWeight: 500 }, children: "/" }),
1071
+ /* @__PURE__ */ jsx7(
908
1072
  "input",
909
1073
  {
910
1074
  ref: commandNameInputRef,
@@ -936,7 +1100,7 @@ function useSlashCommands({
936
1100
  }
937
1101
  }
938
1102
  ),
939
- /* @__PURE__ */ jsx5(
1103
+ /* @__PURE__ */ jsx7(
940
1104
  "button",
941
1105
  {
942
1106
  "data-testid": "save-command-confirm",
@@ -954,15 +1118,15 @@ function useSlashCommands({
954
1118
  justifyContent: "center"
955
1119
  },
956
1120
  title: strings.commands.saveCommand,
957
- children: /* @__PURE__ */ jsxs4("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
958
- /* @__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" }),
959
- /* @__PURE__ */ jsx5("polyline", { points: "17 21 17 13 7 13 7 21" }),
960
- /* @__PURE__ */ jsx5("polyline", { points: "7 3 7 8 15 8" })
1121
+ children: /* @__PURE__ */ jsxs6("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
1122
+ /* @__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" }),
1123
+ /* @__PURE__ */ jsx7("polyline", { points: "17 21 17 13 7 13 7 21" }),
1124
+ /* @__PURE__ */ jsx7("polyline", { points: "7 3 7 8 15 8" })
961
1125
  ] })
962
1126
  }
963
1127
  )
964
1128
  ] }),
965
- commandSaveError && /* @__PURE__ */ jsx5(
1129
+ commandSaveError && /* @__PURE__ */ jsx7(
966
1130
  "div",
967
1131
  {
968
1132
  "data-testid": "command-save-error",
@@ -997,9 +1161,151 @@ import { useState as useState2, useRef as useRef3, useCallback as useCallback2,
997
1161
  // src/fileUpload/types.ts
998
1162
  var DEFAULT_MAX_FILE_SIZE = 10 * 1024 * 1024;
999
1163
 
1164
+ // src/fileUpload/mimeTypeMatcher.ts
1165
+ function matchesMimeType(mimeType, pattern) {
1166
+ if (!pattern.includes("*")) {
1167
+ return mimeType === pattern;
1168
+ }
1169
+ const regexPattern = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*");
1170
+ const regex = new RegExp(`^${regexPattern}$`);
1171
+ return regex.test(mimeType);
1172
+ }
1173
+ function findTransformer(mimeType, transformers) {
1174
+ if (!transformers) {
1175
+ return void 0;
1176
+ }
1177
+ let bestMatch;
1178
+ let bestIsExact = false;
1179
+ let bestLength = -1;
1180
+ for (const [pattern, transformer] of Object.entries(transformers)) {
1181
+ if (!matchesMimeType(mimeType, pattern)) {
1182
+ continue;
1183
+ }
1184
+ const isExact = !pattern.includes("*");
1185
+ if (isExact && !bestIsExact) {
1186
+ bestMatch = transformer;
1187
+ bestIsExact = true;
1188
+ bestLength = pattern.length;
1189
+ continue;
1190
+ }
1191
+ if (isExact === bestIsExact && pattern.length > bestLength) {
1192
+ bestMatch = transformer;
1193
+ bestLength = pattern.length;
1194
+ }
1195
+ }
1196
+ return bestMatch;
1197
+ }
1198
+
1199
+ // src/fileUpload/EmbedFileUploadBackend.ts
1200
+ var EmbedFileUploadBackend = class {
1201
+ /**
1202
+ * Converts a File to a base64 data URL.
1203
+ *
1204
+ * @param file - The File object to convert
1205
+ * @returns Promise resolving to a base64 data URL (e.g., "data:image/png;base64,...")
1206
+ * @throws Error if file reading fails
1207
+ */
1208
+ async prepareForSend(file) {
1209
+ return new Promise((resolve, reject) => {
1210
+ const reader = new FileReader();
1211
+ reader.onload = () => {
1212
+ if (typeof reader.result === "string") {
1213
+ resolve(reader.result);
1214
+ } else {
1215
+ reject(new Error("Failed to read file as data URL"));
1216
+ }
1217
+ };
1218
+ reader.onerror = () => {
1219
+ reject(new Error(`Failed to read file: ${file.name}`));
1220
+ };
1221
+ reader.readAsDataURL(file);
1222
+ });
1223
+ }
1224
+ };
1225
+
1226
+ // src/fileUpload/processAttachments.ts
1227
+ var transformationCache = /* @__PURE__ */ new Map();
1228
+ function getFileCacheKey(file) {
1229
+ return `${file.name}:${file.size}:${file.lastModified}`;
1230
+ }
1231
+ async function getTransformedContent(file, transformer, context, onProgress) {
1232
+ const cacheKey = getFileCacheKey(file);
1233
+ const cached = transformationCache.get(cacheKey);
1234
+ if (cached !== void 0) {
1235
+ return cached;
1236
+ }
1237
+ const result = await transformer.transform(file, context, onProgress);
1238
+ transformationCache.set(cacheKey, result);
1239
+ return result;
1240
+ }
1241
+ async function processAttachments(attachments, config) {
1242
+ const { getCurrentChat, backend = new EmbedFileUploadBackend(), transformers = {}, onFileProgress } = config;
1243
+ const contentParts = [];
1244
+ const chat = await getCurrentChat();
1245
+ const context = { chat };
1246
+ for (const attachment of attachments) {
1247
+ onFileProgress?.(attachment.id, { status: "processing" });
1248
+ try {
1249
+ if (attachment.transformedContent !== void 0) {
1250
+ contentParts.push({
1251
+ type: "transformed_file",
1252
+ text: attachment.transformedContent,
1253
+ originalFile: {
1254
+ name: attachment.file.name,
1255
+ mimeType: attachment.file.type,
1256
+ size: attachment.file.size
1257
+ }
1258
+ });
1259
+ onFileProgress?.(attachment.id, { status: "done" });
1260
+ continue;
1261
+ }
1262
+ const transformer = findTransformer(attachment.file.type, transformers);
1263
+ if (transformer) {
1264
+ const transformedText = await getTransformedContent(
1265
+ attachment.file,
1266
+ transformer,
1267
+ context,
1268
+ (progress) => {
1269
+ onFileProgress?.(attachment.id, { status: "processing", progress });
1270
+ }
1271
+ );
1272
+ contentParts.push({
1273
+ type: "transformed_file",
1274
+ text: transformedText,
1275
+ originalFile: {
1276
+ name: attachment.file.name,
1277
+ mimeType: attachment.file.type,
1278
+ size: attachment.file.size
1279
+ }
1280
+ });
1281
+ } else {
1282
+ const url = await backend.prepareForSend(attachment.file);
1283
+ if (attachment.file.type.startsWith("image/")) {
1284
+ contentParts.push({ type: "image", url });
1285
+ } else {
1286
+ contentParts.push({
1287
+ type: "file",
1288
+ url,
1289
+ mimeType: attachment.file.type,
1290
+ name: attachment.file.name
1291
+ });
1292
+ }
1293
+ }
1294
+ onFileProgress?.(attachment.id, { status: "done" });
1295
+ } catch (error) {
1296
+ onFileProgress?.(attachment.id, { status: "error" });
1297
+ throw error;
1298
+ }
1299
+ }
1300
+ return contentParts;
1301
+ }
1302
+ function clearTransformationCache() {
1303
+ transformationCache.clear();
1304
+ }
1305
+
1000
1306
  // src/hooks/useFileUpload.tsx
1001
1307
  import { v4 as uuidv4 } from "uuid";
1002
- import { jsx as jsx6 } from "react/jsx-runtime";
1308
+ import { jsx as jsx8 } from "react/jsx-runtime";
1003
1309
  async function generateImagePreview(file) {
1004
1310
  if (!file.type.startsWith("image/")) {
1005
1311
  return void 0;
@@ -1032,18 +1338,21 @@ function isTypeAccepted(mimeType, acceptedTypes) {
1032
1338
  function useFileUpload({
1033
1339
  config,
1034
1340
  disabled = false,
1035
- resetDependency
1341
+ resetDependency,
1342
+ getCurrentChat
1036
1343
  }) {
1037
1344
  const strings = useStrings();
1038
1345
  const theme = useTheme();
1039
1346
  const [attachments, setAttachments] = useState2([]);
1040
1347
  const [isDragging, setIsDragging] = useState2(false);
1041
1348
  const [fileError, setFileError] = useState2(null);
1349
+ const [processingState, setProcessingState] = useState2(/* @__PURE__ */ new Map());
1042
1350
  const fileInputRef = useRef3(null);
1043
1351
  const dragCounterRef = useRef3(0);
1044
1352
  const enabled = config !== void 0;
1045
1353
  const maxFileSize = config?.maxFileSize ?? DEFAULT_MAX_FILE_SIZE;
1046
1354
  const acceptedTypes = config?.acceptedTypes;
1355
+ const transformers = config?.transformers;
1047
1356
  useEffect3(() => {
1048
1357
  if (fileError) {
1049
1358
  const timer = setTimeout(() => setFileError(null), 3e3);
@@ -1053,7 +1362,28 @@ function useFileUpload({
1053
1362
  useEffect3(() => {
1054
1363
  setAttachments([]);
1055
1364
  setFileError(null);
1365
+ setProcessingState(/* @__PURE__ */ new Map());
1056
1366
  }, [resetDependency]);
1367
+ const runTransformer = useCallback2(async (attachmentId, file, transformer) => {
1368
+ setProcessingState((prev) => new Map(prev).set(attachmentId, { status: "processing" }));
1369
+ try {
1370
+ const chat = await getCurrentChat();
1371
+ const context = { chat };
1372
+ const transformedContent = await getTransformedContent(file, transformer, context, (progress) => {
1373
+ setProcessingState((prev) => new Map(prev).set(attachmentId, {
1374
+ status: "processing",
1375
+ progress
1376
+ }));
1377
+ });
1378
+ setAttachments((prev) => prev.map(
1379
+ (a) => a.id === attachmentId ? { ...a, transformedContent } : a
1380
+ ));
1381
+ setProcessingState((prev) => new Map(prev).set(attachmentId, { status: "done" }));
1382
+ } catch (error) {
1383
+ console.error(`[useFileUpload] Transformation failed for ${file.name}:`, error);
1384
+ setProcessingState((prev) => new Map(prev).set(attachmentId, { status: "error" }));
1385
+ }
1386
+ }, [getCurrentChat]);
1057
1387
  const handleFiles = useCallback2(async (files) => {
1058
1388
  const fileArray = Array.from(files);
1059
1389
  for (const file of fileArray) {
@@ -1068,21 +1398,30 @@ function useFileUpload({
1068
1398
  continue;
1069
1399
  }
1070
1400
  const preview = await generateImagePreview(file);
1071
- setAttachments((prev) => [
1072
- ...prev,
1073
- {
1074
- id: uuidv4(),
1075
- file,
1076
- preview
1077
- }
1078
- ]);
1401
+ const attachmentId = uuidv4();
1402
+ const attachment = {
1403
+ id: attachmentId,
1404
+ file,
1405
+ preview
1406
+ };
1407
+ setAttachments((prev) => [...prev, attachment]);
1408
+ const transformer = findTransformer(file.type, transformers);
1409
+ if (transformer) {
1410
+ runTransformer(attachmentId, file, transformer);
1411
+ }
1079
1412
  }
1080
- }, [maxFileSize, acceptedTypes, strings]);
1413
+ }, [maxFileSize, acceptedTypes, strings, transformers, runTransformer]);
1081
1414
  const removeAttachment = useCallback2((id) => {
1082
1415
  setAttachments((prev) => prev.filter((a) => a.id !== id));
1416
+ setProcessingState((prev) => {
1417
+ const next = new Map(prev);
1418
+ next.delete(id);
1419
+ return next;
1420
+ });
1083
1421
  }, []);
1084
1422
  const clearAttachments = useCallback2(() => {
1085
1423
  setAttachments([]);
1424
+ setProcessingState(/* @__PURE__ */ new Map());
1086
1425
  }, []);
1087
1426
  const openFilePicker = useCallback2(() => {
1088
1427
  fileInputRef.current?.click();
@@ -1135,7 +1474,7 @@ function useFileUpload({
1135
1474
  }), [handleDragEnter, handleDragOver, handleDragLeave, handleDrop]);
1136
1475
  const DropZoneOverlay = useMemo(() => {
1137
1476
  if (!isDragging || !enabled) return null;
1138
- return /* @__PURE__ */ jsx6(
1477
+ return /* @__PURE__ */ jsx8(
1139
1478
  "div",
1140
1479
  {
1141
1480
  style: {
@@ -1150,7 +1489,7 @@ function useFileUpload({
1150
1489
  zIndex: 1010,
1151
1490
  pointerEvents: "none"
1152
1491
  },
1153
- children: /* @__PURE__ */ jsx6(
1492
+ children: /* @__PURE__ */ jsx8(
1154
1493
  "div",
1155
1494
  {
1156
1495
  style: {
@@ -1159,7 +1498,7 @@ function useFileUpload({
1159
1498
  borderRadius: "12px",
1160
1499
  boxShadow: theme.buttonShadow
1161
1500
  },
1162
- children: /* @__PURE__ */ jsx6("span", { style: { color: theme.primaryColor, fontWeight: 600, fontSize: "16px" }, children: strings.fileUpload.dropFilesHere })
1501
+ children: /* @__PURE__ */ jsx8("span", { style: { color: theme.primaryColor, fontWeight: 600, fontSize: "16px" }, children: strings.fileUpload.dropFilesHere })
1163
1502
  }
1164
1503
  )
1165
1504
  }
@@ -1172,6 +1511,7 @@ function useFileUpload({
1172
1511
  enabled,
1173
1512
  maxFileSize,
1174
1513
  acceptedTypes,
1514
+ processingState,
1175
1515
  fileInputRef,
1176
1516
  handleFiles,
1177
1517
  removeAttachment,
@@ -1189,7 +1529,7 @@ function useFileUpload({
1189
1529
 
1190
1530
  // src/hooks/useDropdownState.tsx
1191
1531
  import { useState as useState3, useCallback as useCallback3, useMemo as useMemo2 } from "react";
1192
- import { jsx as jsx7 } from "react/jsx-runtime";
1532
+ import { jsx as jsx9 } from "react/jsx-runtime";
1193
1533
  function useDropdownState(options = {}) {
1194
1534
  const { backdropZIndex = 1002, initialOpen = false } = options;
1195
1535
  const [isOpen, setIsOpen] = useState3(initialOpen);
@@ -1204,7 +1544,7 @@ function useDropdownState(options = {}) {
1204
1544
  }, []);
1205
1545
  const Backdrop = useMemo2(() => {
1206
1546
  if (!isOpen) return null;
1207
- return /* @__PURE__ */ jsx7(
1547
+ return /* @__PURE__ */ jsx9(
1208
1548
  "div",
1209
1549
  {
1210
1550
  onClick: close,
@@ -1229,7 +1569,7 @@ function useDropdownState(options = {}) {
1229
1569
  }
1230
1570
 
1231
1571
  // src/components/UseAIChatPanel.tsx
1232
- import { Fragment, jsx as jsx8, jsxs as jsxs5 } from "react/jsx-runtime";
1572
+ import { Fragment, jsx as jsx10, jsxs as jsxs7 } from "react/jsx-runtime";
1233
1573
  function getTextContent(content) {
1234
1574
  if (typeof content === "string") {
1235
1575
  return content;
@@ -1250,6 +1590,7 @@ function UseAIChatPanel({
1250
1590
  onLoadChat,
1251
1591
  onDeleteChat,
1252
1592
  onListChats,
1593
+ onGetChat,
1253
1594
  suggestions,
1254
1595
  availableAgents,
1255
1596
  defaultAgent,
@@ -1270,12 +1611,14 @@ function UseAIChatPanel({
1270
1611
  const [chatHistory, setChatHistory] = useState4([]);
1271
1612
  const messagesEndRef = useRef4(null);
1272
1613
  const [displayedSuggestions, setDisplayedSuggestions] = useState4([]);
1614
+ const textareaRef = useRef4(null);
1273
1615
  const [hoveredMessageId, setHoveredMessageId] = useState4(null);
1274
1616
  const {
1275
1617
  attachments,
1276
1618
  fileError,
1277
1619
  enabled: fileUploadEnabled,
1278
1620
  acceptedTypes,
1621
+ processingState: fileProcessingState,
1279
1622
  fileInputRef,
1280
1623
  removeAttachment,
1281
1624
  clearAttachments,
@@ -1284,6 +1627,7 @@ function UseAIChatPanel({
1284
1627
  getDropZoneProps,
1285
1628
  DropZoneOverlay
1286
1629
  } = useFileUpload({
1630
+ getCurrentChat: onGetChat ?? (async () => null),
1287
1631
  config: fileUploadConfig,
1288
1632
  disabled: loading,
1289
1633
  resetDependency: currentChatId
@@ -1298,6 +1642,14 @@ function UseAIChatPanel({
1298
1642
  useEffect4(() => {
1299
1643
  messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
1300
1644
  }, [messages]);
1645
+ const maxTextareaHeight = 160;
1646
+ useEffect4(() => {
1647
+ const textarea = textareaRef.current;
1648
+ if (!textarea) return;
1649
+ textarea.style.height = "auto";
1650
+ const newHeight = Math.min(textarea.scrollHeight, maxTextareaHeight);
1651
+ textarea.style.height = `${newHeight}px`;
1652
+ }, [input]);
1301
1653
  useEffect4(() => {
1302
1654
  if (!suggestions || suggestions.length === 0) {
1303
1655
  setDisplayedSuggestions([]);
@@ -1347,7 +1699,7 @@ function UseAIChatPanel({
1347
1699
  chatHistoryDropdown.close();
1348
1700
  }
1349
1701
  };
1350
- return /* @__PURE__ */ jsxs5(
1702
+ return /* @__PURE__ */ jsxs7(
1351
1703
  "div",
1352
1704
  {
1353
1705
  onClick: () => {
@@ -1365,7 +1717,7 @@ function UseAIChatPanel({
1365
1717
  },
1366
1718
  children: [
1367
1719
  DropZoneOverlay,
1368
- /* @__PURE__ */ jsxs5(
1720
+ /* @__PURE__ */ jsxs7(
1369
1721
  "div",
1370
1722
  {
1371
1723
  style: {
@@ -1378,7 +1730,7 @@ function UseAIChatPanel({
1378
1730
  gap: "12px"
1379
1731
  },
1380
1732
  children: [
1381
- /* @__PURE__ */ jsx8("div", { style: { flex: 1, minWidth: 0, position: "relative" }, children: onListChats ? /* @__PURE__ */ jsxs5(
1733
+ /* @__PURE__ */ jsx10("div", { style: { flex: 1, minWidth: 0, position: "relative" }, children: onListChats ? /* @__PURE__ */ jsxs7(
1382
1734
  "button",
1383
1735
  {
1384
1736
  "data-testid": "chat-history-dropdown-button",
@@ -1411,7 +1763,7 @@ function UseAIChatPanel({
1411
1763
  e.currentTarget.style.background = "transparent";
1412
1764
  },
1413
1765
  children: [
1414
- /* @__PURE__ */ jsx8("span", { style: {
1766
+ /* @__PURE__ */ jsx10("span", { style: {
1415
1767
  overflow: "hidden",
1416
1768
  textOverflow: "ellipsis",
1417
1769
  whiteSpace: "nowrap",
@@ -1428,13 +1780,13 @@ function UseAIChatPanel({
1428
1780
  }
1429
1781
  return strings.header.newChat;
1430
1782
  })() }),
1431
- /* @__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" }) })
1783
+ /* @__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" }) })
1432
1784
  ]
1433
1785
  }
1434
- ) : /* @__PURE__ */ jsx8("div", { style: { fontSize: "14px", fontWeight: "600", color: theme.textColor, padding: "6px 8px" }, children: strings.header.aiAssistant }) }),
1435
- /* @__PURE__ */ jsxs5("div", { style: { display: "flex", alignItems: "center", gap: "4px" }, children: [
1436
- availableAgents && availableAgents.length > 1 && onAgentChange && /* @__PURE__ */ jsxs5("div", { style: { position: "relative" }, children: [
1437
- /* @__PURE__ */ jsxs5(
1786
+ ) : /* @__PURE__ */ jsx10("div", { style: { fontSize: "14px", fontWeight: "600", color: theme.textColor, padding: "6px 8px" }, children: strings.header.aiAssistant }) }),
1787
+ /* @__PURE__ */ jsxs7("div", { style: { display: "flex", alignItems: "center", gap: "4px" }, children: [
1788
+ availableAgents && availableAgents.length > 1 && onAgentChange && /* @__PURE__ */ jsxs7("div", { style: { position: "relative" }, children: [
1789
+ /* @__PURE__ */ jsxs7(
1438
1790
  "button",
1439
1791
  {
1440
1792
  "data-testid": "agent-selector",
@@ -1463,7 +1815,7 @@ function UseAIChatPanel({
1463
1815
  },
1464
1816
  title: "Select AI model",
1465
1817
  children: [
1466
- /* @__PURE__ */ jsx8("span", { style: {
1818
+ /* @__PURE__ */ jsx10("span", { style: {
1467
1819
  overflow: "hidden",
1468
1820
  textOverflow: "ellipsis",
1469
1821
  whiteSpace: "nowrap",
@@ -1472,11 +1824,11 @@ function UseAIChatPanel({
1472
1824
  const agent = availableAgents.find((a) => a.id === (selectedAgent ?? defaultAgent));
1473
1825
  return agent?.name || "AI";
1474
1826
  })() }),
1475
- /* @__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" }) })
1827
+ /* @__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" }) })
1476
1828
  ]
1477
1829
  }
1478
1830
  ),
1479
- agentDropdown.isOpen && /* @__PURE__ */ jsx8(
1831
+ agentDropdown.isOpen && /* @__PURE__ */ jsx10(
1480
1832
  "div",
1481
1833
  {
1482
1834
  style: {
@@ -1496,7 +1848,7 @@ function UseAIChatPanel({
1496
1848
  },
1497
1849
  children: availableAgents.map((agent) => {
1498
1850
  const isSelected = agent.id === (selectedAgent ?? defaultAgent);
1499
- return /* @__PURE__ */ jsxs5(
1851
+ return /* @__PURE__ */ jsxs7(
1500
1852
  "div",
1501
1853
  {
1502
1854
  "data-testid": "agent-option",
@@ -1526,13 +1878,13 @@ function UseAIChatPanel({
1526
1878
  }
1527
1879
  },
1528
1880
  children: [
1529
- /* @__PURE__ */ jsxs5("div", { style: { flex: 1, minWidth: 0 }, children: [
1530
- /* @__PURE__ */ jsx8("div", { style: {
1881
+ /* @__PURE__ */ jsxs7("div", { style: { flex: 1, minWidth: 0 }, children: [
1882
+ /* @__PURE__ */ jsx10("div", { style: {
1531
1883
  fontSize: "13px",
1532
1884
  fontWeight: isSelected ? "600" : "500",
1533
1885
  color: isSelected ? theme.primaryColor : theme.textColor
1534
1886
  }, children: agent.name }),
1535
- agent.annotation && /* @__PURE__ */ jsx8("div", { style: {
1887
+ agent.annotation && /* @__PURE__ */ jsx10("div", { style: {
1536
1888
  fontSize: "11px",
1537
1889
  color: theme.secondaryTextColor,
1538
1890
  marginTop: "2px",
@@ -1541,7 +1893,7 @@ function UseAIChatPanel({
1541
1893
  whiteSpace: "nowrap"
1542
1894
  }, children: agent.annotation })
1543
1895
  ] }),
1544
- 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" }) })
1896
+ 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" }) })
1545
1897
  ]
1546
1898
  },
1547
1899
  agent.id
@@ -1550,7 +1902,7 @@ function UseAIChatPanel({
1550
1902
  }
1551
1903
  )
1552
1904
  ] }),
1553
- onNewChat && /* @__PURE__ */ jsx8(
1905
+ onNewChat && /* @__PURE__ */ jsx10(
1554
1906
  "button",
1555
1907
  {
1556
1908
  "data-testid": "new-chat-button",
@@ -1577,10 +1929,10 @@ function UseAIChatPanel({
1577
1929
  e.currentTarget.style.color = theme.secondaryTextColor;
1578
1930
  },
1579
1931
  title: strings.header.newChat,
1580
- 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" }) })
1932
+ 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" }) })
1581
1933
  }
1582
1934
  ),
1583
- onDeleteChat && messages.length > 0 && /* @__PURE__ */ jsx8(
1935
+ onDeleteChat && messages.length > 0 && /* @__PURE__ */ jsx10(
1584
1936
  "button",
1585
1937
  {
1586
1938
  "data-testid": "delete-chat-button",
@@ -1604,7 +1956,7 @@ function UseAIChatPanel({
1604
1956
  e.currentTarget.style.color = theme.secondaryTextColor;
1605
1957
  },
1606
1958
  title: strings.header.deleteChat,
1607
- 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" }) })
1959
+ 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" }) })
1608
1960
  }
1609
1961
  ),
1610
1962
  closeButton
@@ -1612,7 +1964,7 @@ function UseAIChatPanel({
1612
1964
  ]
1613
1965
  }
1614
1966
  ),
1615
- chatHistoryDropdown.isOpen && onListChats && /* @__PURE__ */ jsx8(
1967
+ chatHistoryDropdown.isOpen && onListChats && /* @__PURE__ */ jsx10(
1616
1968
  "div",
1617
1969
  {
1618
1970
  style: {
@@ -1629,7 +1981,7 @@ function UseAIChatPanel({
1629
1981
  flexDirection: "column",
1630
1982
  overflow: "hidden"
1631
1983
  },
1632
- children: /* @__PURE__ */ jsx8(
1984
+ children: /* @__PURE__ */ jsx10(
1633
1985
  "div",
1634
1986
  {
1635
1987
  style: {
@@ -1637,7 +1989,7 @@ function UseAIChatPanel({
1637
1989
  overflowY: "auto",
1638
1990
  padding: "8px"
1639
1991
  },
1640
- children: chatHistory.length === 0 ? /* @__PURE__ */ jsx8(
1992
+ children: chatHistory.length === 0 ? /* @__PURE__ */ jsx10(
1641
1993
  "div",
1642
1994
  {
1643
1995
  style: {
@@ -1646,9 +1998,9 @@ function UseAIChatPanel({
1646
1998
  padding: "32px 16px",
1647
1999
  fontSize: "13px"
1648
2000
  },
1649
- children: /* @__PURE__ */ jsx8("p", { style: { margin: 0 }, children: strings.chatHistory.noChatHistory })
2001
+ children: /* @__PURE__ */ jsx10("p", { style: { margin: 0 }, children: strings.chatHistory.noChatHistory })
1650
2002
  }
1651
- ) : chatHistory.map((chat) => /* @__PURE__ */ jsxs5(
2003
+ ) : chatHistory.map((chat) => /* @__PURE__ */ jsxs7(
1652
2004
  "div",
1653
2005
  {
1654
2006
  "data-testid": "chat-history-item",
@@ -1672,10 +2024,10 @@ function UseAIChatPanel({
1672
2024
  }
1673
2025
  },
1674
2026
  children: [
1675
- /* @__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 }),
1676
- /* @__PURE__ */ jsxs5("div", { style: { fontSize: "11px", color: theme.secondaryTextColor }, children: [
2027
+ /* @__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 }),
2028
+ /* @__PURE__ */ jsxs7("div", { style: { fontSize: "11px", color: theme.secondaryTextColor }, children: [
1677
2029
  new Date(chat.updatedAt).toLocaleDateString([], { month: "short", day: "numeric" }),
1678
- currentChatId === chat.id && /* @__PURE__ */ jsxs5("span", { style: {
2030
+ currentChatId === chat.id && /* @__PURE__ */ jsxs7("span", { style: {
1679
2031
  marginLeft: "8px",
1680
2032
  color: theme.primaryColor,
1681
2033
  fontWeight: "600"
@@ -1694,7 +2046,7 @@ function UseAIChatPanel({
1694
2046
  ),
1695
2047
  chatHistoryDropdown.Backdrop,
1696
2048
  agentDropdown.Backdrop,
1697
- /* @__PURE__ */ jsxs5(
2049
+ /* @__PURE__ */ jsxs7(
1698
2050
  "div",
1699
2051
  {
1700
2052
  style: {
@@ -1706,7 +2058,7 @@ function UseAIChatPanel({
1706
2058
  gap: "12px"
1707
2059
  },
1708
2060
  children: [
1709
- messages.length === 0 && /* @__PURE__ */ jsxs5(
2061
+ messages.length === 0 && /* @__PURE__ */ jsxs7(
1710
2062
  "div",
1711
2063
  {
1712
2064
  style: {
@@ -1717,12 +2069,12 @@ function UseAIChatPanel({
1717
2069
  gap: "20px"
1718
2070
  },
1719
2071
  children: [
1720
- /* @__PURE__ */ jsxs5("div", { style: { textAlign: "center", color: theme.secondaryTextColor, fontSize: "14px" }, children: [
1721
- /* @__PURE__ */ jsx8("p", { style: { margin: 0, fontSize: "32px", marginBottom: "12px" }, children: "\u{1F4AC}" }),
1722
- /* @__PURE__ */ jsx8("p", { style: { margin: 0 }, children: strings.emptyChat.startConversation }),
1723
- /* @__PURE__ */ jsx8("p", { style: { margin: "8px 0 0", fontSize: "12px" }, children: strings.emptyChat.askMeToHelp })
2072
+ /* @__PURE__ */ jsxs7("div", { style: { textAlign: "center", color: theme.secondaryTextColor, fontSize: "14px" }, children: [
2073
+ /* @__PURE__ */ jsx10("p", { style: { margin: 0, fontSize: "32px", marginBottom: "12px" }, children: "\u{1F4AC}" }),
2074
+ /* @__PURE__ */ jsx10("p", { style: { margin: 0 }, children: strings.emptyChat.startConversation }),
2075
+ /* @__PURE__ */ jsx10("p", { style: { margin: "8px 0 0", fontSize: "12px" }, children: strings.emptyChat.askMeToHelp })
1724
2076
  ] }),
1725
- displayedSuggestions.length > 0 && /* @__PURE__ */ jsx8(
2077
+ displayedSuggestions.length > 0 && /* @__PURE__ */ jsx10(
1726
2078
  "div",
1727
2079
  {
1728
2080
  style: {
@@ -1732,7 +2084,7 @@ function UseAIChatPanel({
1732
2084
  width: "100%",
1733
2085
  maxWidth: "320px"
1734
2086
  },
1735
- children: displayedSuggestions.map((suggestion, index) => /* @__PURE__ */ jsx8(
2087
+ children: displayedSuggestions.map((suggestion, index) => /* @__PURE__ */ jsx10(
1736
2088
  "button",
1737
2089
  {
1738
2090
  "data-testid": "chat-suggestion-button",
@@ -1776,7 +2128,7 @@ function UseAIChatPanel({
1776
2128
  ]
1777
2129
  }
1778
2130
  ),
1779
- messages.map((message) => /* @__PURE__ */ jsxs5(
2131
+ messages.map((message) => /* @__PURE__ */ jsxs7(
1780
2132
  "div",
1781
2133
  {
1782
2134
  "data-testid": `chat-message-${message.role}`,
@@ -1789,7 +2141,7 @@ function UseAIChatPanel({
1789
2141
  onMouseEnter: () => message.role === "user" && setHoveredMessageId(message.id),
1790
2142
  onMouseLeave: () => setHoveredMessageId(null),
1791
2143
  children: [
1792
- /* @__PURE__ */ jsxs5(
2144
+ /* @__PURE__ */ jsxs7(
1793
2145
  "div",
1794
2146
  {
1795
2147
  style: {
@@ -1797,7 +2149,7 @@ function UseAIChatPanel({
1797
2149
  maxWidth: "80%"
1798
2150
  },
1799
2151
  children: [
1800
- message.role === "user" && hoveredMessageId === message.id && onSaveCommand && !slashCommands.isSavingCommand(message.id) && /* @__PURE__ */ jsx8(
2152
+ message.role === "user" && hoveredMessageId === message.id && onSaveCommand && !slashCommands.isSavingCommand(message.id) && /* @__PURE__ */ jsx10(
1801
2153
  "button",
1802
2154
  {
1803
2155
  "data-testid": "save-command-button",
@@ -1833,14 +2185,14 @@ function UseAIChatPanel({
1833
2185
  e.currentTarget.style.transform = "scale(1)";
1834
2186
  e.currentTarget.style.boxShadow = "0 2px 6px rgba(0, 0, 0, 0.15)";
1835
2187
  },
1836
- children: /* @__PURE__ */ jsxs5("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
1837
- /* @__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" }),
1838
- /* @__PURE__ */ jsx8("polyline", { points: "17 21 17 13 7 13 7 21" }),
1839
- /* @__PURE__ */ jsx8("polyline", { points: "7 3 7 8 15 8" })
2188
+ children: /* @__PURE__ */ jsxs7("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
2189
+ /* @__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" }),
2190
+ /* @__PURE__ */ jsx10("polyline", { points: "17 21 17 13 7 13 7 21" }),
2191
+ /* @__PURE__ */ jsx10("polyline", { points: "7 3 7 8 15 8" })
1840
2192
  ] })
1841
2193
  }
1842
2194
  ),
1843
- /* @__PURE__ */ jsxs5(
2195
+ /* @__PURE__ */ jsxs7(
1844
2196
  "div",
1845
2197
  {
1846
2198
  "data-testid": "chat-message-content",
@@ -1855,7 +2207,7 @@ function UseAIChatPanel({
1855
2207
  wordWrap: "break-word"
1856
2208
  },
1857
2209
  children: [
1858
- 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(
2210
+ 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(
1859
2211
  FilePlaceholder,
1860
2212
  {
1861
2213
  name: part.file.name,
@@ -1863,7 +2215,7 @@ function UseAIChatPanel({
1863
2215
  },
1864
2216
  idx
1865
2217
  )) }),
1866
- message.role === "assistant" ? /* @__PURE__ */ jsx8(MarkdownContent, { content: getTextContent(message.content) }) : getTextContent(message.content)
2218
+ message.role === "assistant" ? /* @__PURE__ */ jsx10(MarkdownContent, { content: getTextContent(message.content) }) : getTextContent(message.content)
1867
2219
  ]
1868
2220
  }
1869
2221
  ),
@@ -1874,7 +2226,7 @@ function UseAIChatPanel({
1874
2226
  ]
1875
2227
  }
1876
2228
  ),
1877
- /* @__PURE__ */ jsx8(
2229
+ /* @__PURE__ */ jsx10(
1878
2230
  "div",
1879
2231
  {
1880
2232
  style: {
@@ -1893,14 +2245,14 @@ function UseAIChatPanel({
1893
2245
  },
1894
2246
  message.id
1895
2247
  )),
1896
- loading && /* @__PURE__ */ jsx8(
2248
+ loading && /* @__PURE__ */ jsx10(
1897
2249
  "div",
1898
2250
  {
1899
2251
  style: {
1900
2252
  display: "flex",
1901
2253
  alignItems: "flex-start"
1902
2254
  },
1903
- children: /* @__PURE__ */ jsx8(
2255
+ children: /* @__PURE__ */ jsx10(
1904
2256
  "div",
1905
2257
  {
1906
2258
  className: "markdown-content",
@@ -1913,19 +2265,19 @@ function UseAIChatPanel({
1913
2265
  color: theme.textColor,
1914
2266
  maxWidth: "80%"
1915
2267
  },
1916
- children: streamingText ? /* @__PURE__ */ jsx8(MarkdownContent, { content: streamingText }) : /* @__PURE__ */ jsxs5(Fragment, { children: [
1917
- /* @__PURE__ */ jsx8("span", { style: { opacity: 0.6 }, children: strings.input.thinking }),
1918
- /* @__PURE__ */ jsx8("span", { className: "dots", style: { marginLeft: "4px" }, children: "..." })
2268
+ children: streamingText ? /* @__PURE__ */ jsx10(MarkdownContent, { content: streamingText }) : /* @__PURE__ */ jsxs7(Fragment, { children: [
2269
+ /* @__PURE__ */ jsx10("span", { style: { opacity: 0.6 }, children: strings.input.thinking }),
2270
+ /* @__PURE__ */ jsx10("span", { className: "dots", style: { marginLeft: "4px" }, children: "..." })
1919
2271
  ] })
1920
2272
  }
1921
2273
  )
1922
2274
  }
1923
2275
  ),
1924
- /* @__PURE__ */ jsx8("div", { ref: messagesEndRef })
2276
+ /* @__PURE__ */ jsx10("div", { ref: messagesEndRef })
1925
2277
  ]
1926
2278
  }
1927
2279
  ),
1928
- /* @__PURE__ */ jsxs5(
2280
+ /* @__PURE__ */ jsxs7(
1929
2281
  "div",
1930
2282
  {
1931
2283
  style: {
@@ -1933,7 +2285,7 @@ function UseAIChatPanel({
1933
2285
  borderTop: `1px solid ${theme.borderColor}`
1934
2286
  },
1935
2287
  children: [
1936
- fileError && /* @__PURE__ */ jsx8(
2288
+ fileError && /* @__PURE__ */ jsx10(
1937
2289
  "div",
1938
2290
  {
1939
2291
  "data-testid": "file-error",
@@ -1948,7 +2300,7 @@ function UseAIChatPanel({
1948
2300
  children: fileError
1949
2301
  }
1950
2302
  ),
1951
- attachments.length > 0 && /* @__PURE__ */ jsx8(
2303
+ attachments.length > 0 && /* @__PURE__ */ jsx10(
1952
2304
  "div",
1953
2305
  {
1954
2306
  "data-testid": "file-attachments",
@@ -1958,138 +2310,147 @@ function UseAIChatPanel({
1958
2310
  gap: "8px",
1959
2311
  marginBottom: "8px"
1960
2312
  },
1961
- children: attachments.map((attachment) => /* @__PURE__ */ jsx8(
2313
+ children: attachments.map((attachment) => /* @__PURE__ */ jsx10(
1962
2314
  FileChip,
1963
2315
  {
1964
2316
  attachment,
1965
2317
  onRemove: () => removeAttachment(attachment.id),
1966
- disabled: loading
2318
+ disabled: loading,
2319
+ processingState: fileProcessingState.get(attachment.id)
1967
2320
  },
1968
2321
  attachment.id
1969
2322
  ))
1970
2323
  }
1971
2324
  ),
1972
- /* @__PURE__ */ jsxs5(
2325
+ /* @__PURE__ */ jsxs7(
1973
2326
  "div",
1974
2327
  {
1975
2328
  style: {
1976
- display: "flex",
1977
- gap: "8px",
2329
+ border: `1px solid ${theme.borderColor}`,
2330
+ borderRadius: "12px",
2331
+ background: theme.backgroundColor,
2332
+ overflow: "hidden",
1978
2333
  position: "relative"
1979
2334
  },
1980
2335
  children: [
1981
2336
  slashCommands.AutocompleteComponent,
1982
- /* @__PURE__ */ jsx8(
2337
+ /* @__PURE__ */ jsx10(
1983
2338
  "input",
1984
2339
  {
1985
2340
  ref: fileInputRef,
1986
2341
  type: "file",
1987
2342
  multiple: true,
2343
+ "data-testid": "file-input",
1988
2344
  style: { display: "none" },
1989
2345
  onChange: handleFileInputChange,
1990
2346
  accept: acceptedTypes?.join(",")
1991
2347
  }
1992
2348
  ),
1993
- /* @__PURE__ */ jsxs5(
2349
+ /* @__PURE__ */ jsx10(
2350
+ "textarea",
2351
+ {
2352
+ ref: textareaRef,
2353
+ "data-testid": "chat-input",
2354
+ className: "chat-input",
2355
+ value: input,
2356
+ onChange: handleInputChange,
2357
+ onKeyDown: handleKeyDown,
2358
+ placeholder: connected ? strings.input.placeholder : strings.input.connectingPlaceholder,
2359
+ disabled: !connected || loading,
2360
+ rows: 1,
2361
+ style: {
2362
+ width: "100%",
2363
+ padding: "10px 14px 6px",
2364
+ border: "none",
2365
+ fontSize: "14px",
2366
+ lineHeight: "1.4",
2367
+ resize: "none",
2368
+ maxHeight: `${maxTextareaHeight}px`,
2369
+ fontFamily: "inherit",
2370
+ outline: "none",
2371
+ background: "transparent",
2372
+ overflowY: "auto",
2373
+ boxSizing: "border-box"
2374
+ }
2375
+ }
2376
+ ),
2377
+ /* @__PURE__ */ jsxs7(
1994
2378
  "div",
1995
2379
  {
1996
2380
  style: {
1997
- flex: 1,
1998
2381
  display: "flex",
1999
2382
  alignItems: "center",
2000
- border: `1px solid ${theme.borderColor}`,
2001
- borderRadius: "8px",
2002
- background: theme.backgroundColor,
2003
- overflow: "hidden"
2383
+ justifyContent: "space-between",
2384
+ padding: "4px 8px"
2004
2385
  },
2005
2386
  children: [
2006
- /* @__PURE__ */ jsx8(
2007
- "textarea",
2008
- {
2009
- "data-testid": "chat-input",
2010
- className: "chat-input",
2011
- value: input,
2012
- onChange: handleInputChange,
2013
- onKeyDown: handleKeyDown,
2014
- placeholder: connected ? strings.input.placeholder : strings.input.connectingPlaceholder,
2015
- disabled: !connected || loading,
2016
- style: {
2017
- flex: 1,
2018
- padding: "10px 12px",
2019
- border: "none",
2020
- fontSize: "14px",
2021
- resize: "none",
2022
- minHeight: "44px",
2023
- maxHeight: "120px",
2024
- fontFamily: "inherit",
2025
- outline: "none",
2026
- background: "transparent"
2027
- },
2028
- rows: 1
2029
- }
2030
- ),
2031
- fileUploadEnabled && /* @__PURE__ */ jsx8(
2387
+ /* @__PURE__ */ jsx10("div", { style: { display: "flex", alignItems: "center", gap: "8px" }, children: fileUploadEnabled && /* @__PURE__ */ jsx10(
2032
2388
  "button",
2033
2389
  {
2034
2390
  "data-testid": "file-picker-button",
2035
2391
  onClick: openFilePicker,
2036
2392
  disabled: !connected || loading,
2037
2393
  style: {
2038
- padding: "6px",
2039
- marginRight: "6px",
2394
+ padding: "4px",
2040
2395
  background: "transparent",
2041
- border: "none",
2042
- borderRadius: "6px",
2396
+ border: `1px solid ${theme.borderColor}`,
2397
+ borderRadius: "50%",
2043
2398
  cursor: connected && !loading ? "pointer" : "not-allowed",
2044
2399
  color: theme.secondaryTextColor,
2045
2400
  display: "flex",
2046
2401
  alignItems: "center",
2047
2402
  justifyContent: "center",
2403
+ width: "28px",
2404
+ height: "28px",
2048
2405
  transition: "all 0.15s",
2049
2406
  opacity: connected && !loading ? 1 : 0.5
2050
2407
  },
2051
2408
  onMouseEnter: (e) => {
2052
2409
  if (connected && !loading) {
2053
2410
  e.currentTarget.style.color = theme.primaryColor;
2054
- e.currentTarget.style.background = theme.activeBackground;
2411
+ e.currentTarget.style.borderColor = theme.primaryColor;
2055
2412
  }
2056
2413
  },
2057
2414
  onMouseLeave: (e) => {
2058
2415
  e.currentTarget.style.color = theme.secondaryTextColor;
2059
- e.currentTarget.style.background = "transparent";
2416
+ e.currentTarget.style.borderColor = theme.borderColor;
2060
2417
  },
2061
2418
  title: strings.fileUpload.attachFiles,
2062
- children: /* @__PURE__ */ jsxs5("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
2063
- /* @__PURE__ */ jsx8("circle", { cx: "12", cy: "12", r: "10" }),
2064
- /* @__PURE__ */ jsx8("line", { x1: "12", y1: "8", x2: "12", y2: "16" }),
2065
- /* @__PURE__ */ jsx8("line", { x1: "8", y1: "12", x2: "16", y2: "12" })
2419
+ 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: [
2420
+ /* @__PURE__ */ jsx10("line", { x1: "12", y1: "5", x2: "12", y2: "19" }),
2421
+ /* @__PURE__ */ jsx10("line", { x1: "5", y1: "12", x2: "19", y2: "12" })
2422
+ ] })
2423
+ }
2424
+ ) }),
2425
+ /* @__PURE__ */ jsx10(
2426
+ "button",
2427
+ {
2428
+ "data-testid": "chat-send-button",
2429
+ className: "chat-send-button",
2430
+ onClick: handleSend,
2431
+ disabled: !connected || loading || !input.trim() && attachments.length === 0,
2432
+ style: {
2433
+ padding: "6px",
2434
+ background: connected && !loading && (input.trim() || attachments.length > 0) ? theme.primaryGradient : theme.buttonDisabledBackground,
2435
+ color: connected && !loading && (input.trim() || attachments.length > 0) ? "white" : theme.secondaryTextColor,
2436
+ border: "none",
2437
+ borderRadius: "50%",
2438
+ cursor: connected && !loading && (input.trim() || attachments.length > 0) ? "pointer" : "not-allowed",
2439
+ display: "flex",
2440
+ alignItems: "center",
2441
+ justifyContent: "center",
2442
+ width: "32px",
2443
+ height: "32px",
2444
+ transition: "all 0.2s"
2445
+ },
2446
+ 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: [
2447
+ /* @__PURE__ */ jsx10("line", { x1: "12", y1: "19", x2: "12", y2: "5" }),
2448
+ /* @__PURE__ */ jsx10("polyline", { points: "5 12 12 5 19 12" })
2066
2449
  ] })
2067
2450
  }
2068
2451
  )
2069
2452
  ]
2070
2453
  }
2071
- ),
2072
- /* @__PURE__ */ jsx8(
2073
- "button",
2074
- {
2075
- "data-testid": "chat-send-button",
2076
- className: "chat-send-button",
2077
- onClick: handleSend,
2078
- disabled: !connected || loading || !input.trim() && attachments.length === 0,
2079
- style: {
2080
- padding: "10px 16px",
2081
- background: connected && !loading && (input.trim() || attachments.length > 0) ? theme.primaryGradient : theme.buttonDisabledBackground,
2082
- color: connected && !loading && (input.trim() || attachments.length > 0) ? "white" : theme.secondaryTextColor,
2083
- border: "none",
2084
- borderRadius: "8px",
2085
- cursor: connected && !loading && (input.trim() || attachments.length > 0) ? "pointer" : "not-allowed",
2086
- fontSize: "14px",
2087
- fontWeight: "600",
2088
- minWidth: "60px",
2089
- transition: "all 0.2s"
2090
- },
2091
- children: strings.input.send
2092
- }
2093
2454
  )
2094
2455
  ]
2095
2456
  }
@@ -2097,7 +2458,7 @@ function UseAIChatPanel({
2097
2458
  ]
2098
2459
  }
2099
2460
  ),
2100
- /* @__PURE__ */ jsx8("style", { children: `
2461
+ /* @__PURE__ */ jsx10("style", { children: `
2101
2462
  /* Markdown content styles */
2102
2463
  .markdown-content > :first-child {
2103
2464
  margin-top: 0 !important;
@@ -2122,7 +2483,7 @@ function UseAIChatPanel({
2122
2483
  }
2123
2484
 
2124
2485
  // src/components/UseAIFloatingChatWrapper.tsx
2125
- import { Fragment as Fragment2, jsx as jsx9, jsxs as jsxs6 } from "react/jsx-runtime";
2486
+ import { Fragment as Fragment2, jsx as jsx11, jsxs as jsxs8 } from "react/jsx-runtime";
2126
2487
  function UseAIFloatingChatWrapper({
2127
2488
  isOpen,
2128
2489
  onClose,
@@ -2130,8 +2491,8 @@ function UseAIFloatingChatWrapper({
2130
2491
  }) {
2131
2492
  const theme = useTheme();
2132
2493
  if (!isOpen) return null;
2133
- return /* @__PURE__ */ jsxs6(Fragment2, { children: [
2134
- /* @__PURE__ */ jsx9(
2494
+ return /* @__PURE__ */ jsxs8(Fragment2, { children: [
2495
+ /* @__PURE__ */ jsx11(
2135
2496
  "div",
2136
2497
  {
2137
2498
  style: {
@@ -2147,7 +2508,7 @@ function UseAIFloatingChatWrapper({
2147
2508
  onClick: onClose
2148
2509
  }
2149
2510
  ),
2150
- /* @__PURE__ */ jsx9(
2511
+ /* @__PURE__ */ jsx11(
2151
2512
  "div",
2152
2513
  {
2153
2514
  style: {
@@ -2166,7 +2527,7 @@ function UseAIFloatingChatWrapper({
2166
2527
  children
2167
2528
  }
2168
2529
  ),
2169
- /* @__PURE__ */ jsx9("style", { children: `
2530
+ /* @__PURE__ */ jsx11("style", { children: `
2170
2531
  @keyframes fadeIn {
2171
2532
  from { opacity: 0; }
2172
2533
  to { opacity: 1; }
@@ -2186,7 +2547,7 @@ function UseAIFloatingChatWrapper({
2186
2547
  }
2187
2548
  function CloseButton({ onClick }) {
2188
2549
  const theme = useTheme();
2189
- return /* @__PURE__ */ jsx9(
2550
+ return /* @__PURE__ */ jsx11(
2190
2551
  "button",
2191
2552
  {
2192
2553
  "data-testid": "chat-close-button",
@@ -2221,7 +2582,7 @@ function CloseButton({ onClick }) {
2221
2582
 
2222
2583
  // src/components/UseAIChat.tsx
2223
2584
  import { createContext as createContext3, useContext as useContext3 } from "react";
2224
- import { jsx as jsx10 } from "react/jsx-runtime";
2585
+ import { jsx as jsx12 } from "react/jsx-runtime";
2225
2586
  var __UseAIChatContext = createContext3(null);
2226
2587
  function useChatUIContext() {
2227
2588
  const context = useContext3(__UseAIChatContext);
@@ -2245,6 +2606,7 @@ function UseAIChat({ floating = false }) {
2245
2606
  onLoadChat: ctx.history.load,
2246
2607
  onDeleteChat: ctx.history.delete,
2247
2608
  onListChats: ctx.history.list,
2609
+ onGetChat: ctx.history.get,
2248
2610
  suggestions: ctx.suggestions,
2249
2611
  availableAgents: ctx.agents.available,
2250
2612
  defaultAgent: ctx.agents.default,
@@ -2257,22 +2619,22 @@ function UseAIChat({ floating = false }) {
2257
2619
  onDeleteCommand: ctx.commands.delete
2258
2620
  };
2259
2621
  if (floating) {
2260
- return /* @__PURE__ */ jsx10(
2622
+ return /* @__PURE__ */ jsx12(
2261
2623
  UseAIFloatingChatWrapper,
2262
2624
  {
2263
2625
  isOpen: ctx.ui.isOpen,
2264
2626
  onClose: () => ctx.ui.setOpen(false),
2265
- children: /* @__PURE__ */ jsx10(
2627
+ children: /* @__PURE__ */ jsx12(
2266
2628
  UseAIChatPanel,
2267
2629
  {
2268
2630
  ...chatPanelProps,
2269
- closeButton: /* @__PURE__ */ jsx10(CloseButton, { onClick: () => ctx.ui.setOpen(false) })
2631
+ closeButton: /* @__PURE__ */ jsx12(CloseButton, { onClick: () => ctx.ui.setOpen(false) })
2270
2632
  }
2271
2633
  )
2272
2634
  }
2273
2635
  );
2274
2636
  }
2275
- return /* @__PURE__ */ jsx10(UseAIChatPanel, { ...chatPanelProps });
2637
+ return /* @__PURE__ */ jsx12(UseAIChatPanel, { ...chatPanelProps });
2276
2638
  }
2277
2639
 
2278
2640
  // src/client.ts
@@ -2473,12 +2835,18 @@ var UseAIClient = class {
2473
2835
  return { type: "text", text: part.text };
2474
2836
  } else if (part.type === "image") {
2475
2837
  return { type: "image", url: part.url };
2476
- } else {
2838
+ } else if (part.type === "file") {
2477
2839
  return {
2478
2840
  type: "file",
2479
2841
  url: part.url,
2480
2842
  mimeType: part.mimeType
2481
2843
  };
2844
+ } else {
2845
+ return {
2846
+ type: "transformed_file",
2847
+ text: part.text,
2848
+ originalFile: part.originalFile
2849
+ };
2482
2850
  }
2483
2851
  });
2484
2852
  }
@@ -2853,7 +3221,8 @@ var LocalStorageChatRepository = class {
2853
3221
  title: options?.title,
2854
3222
  messages: [],
2855
3223
  createdAt: now,
2856
- updatedAt: now
3224
+ updatedAt: now,
3225
+ metadata: options?.metadata
2857
3226
  };
2858
3227
  await this.enforceMaxChatsLimit();
2859
3228
  await this.saveChat(chat);
@@ -2937,6 +3306,14 @@ var LocalStorageChatRepository = class {
2937
3306
  throw new Error(`Failed to clear all chats: ${error instanceof Error ? error.message : "Unknown error"}`);
2938
3307
  }
2939
3308
  }
3309
+ async updateMetadata(id, metadata, overwrite = false) {
3310
+ const chat = await this.loadChat(id);
3311
+ if (!chat) {
3312
+ throw new Error(`Chat not found: ${id}`);
3313
+ }
3314
+ chat.metadata = overwrite ? metadata : { ...chat.metadata, ...metadata };
3315
+ await this.saveChat(chat);
3316
+ }
2940
3317
  getChatKey(id) {
2941
3318
  return `${STORAGE_KEY_PREFIX}${id}`;
2942
3319
  }
@@ -2975,36 +3352,12 @@ var LocalStorageChatRepository = class {
2975
3352
  }
2976
3353
  };
2977
3354
 
2978
- // src/fileUpload/EmbedFileUploadBackend.ts
2979
- var EmbedFileUploadBackend = class {
2980
- /**
2981
- * Converts a File to a base64 data URL.
2982
- *
2983
- * @param file - The File object to convert
2984
- * @returns Promise resolving to a base64 data URL (e.g., "data:image/png;base64,...")
2985
- * @throws Error if file reading fails
2986
- */
2987
- async prepareForSend(file) {
2988
- return new Promise((resolve, reject) => {
2989
- const reader = new FileReader();
2990
- reader.onload = () => {
2991
- if (typeof reader.result === "string") {
2992
- resolve(reader.result);
2993
- } else {
2994
- reject(new Error("Failed to read file as data URL"));
2995
- }
2996
- };
2997
- reader.onerror = () => {
2998
- reject(new Error(`Failed to read file: ${file.name}`));
2999
- };
3000
- reader.readAsDataURL(file);
3001
- });
3002
- }
3003
- };
3004
-
3005
3355
  // src/hooks/useChatManagement.ts
3006
3356
  import { useState as useState5, useCallback as useCallback4, useRef as useRef5, useEffect as useEffect5 } from "react";
3007
3357
  var CHAT_TITLE_MAX_LENGTH = 50;
3358
+ function deepEquals(a, b) {
3359
+ return JSON.stringify(a) === JSON.stringify(b);
3360
+ }
3008
3361
  function generateChatTitle(message) {
3009
3362
  return message.length > CHAT_TITLE_MAX_LENGTH ? message.substring(0, CHAT_TITLE_MAX_LENGTH) + "..." : message;
3010
3363
  }
@@ -3035,7 +3388,11 @@ function transformMessagesToClientFormat(uiMessages) {
3035
3388
  }
3036
3389
  function useChatManagement({
3037
3390
  repository,
3038
- clientRef
3391
+ clientRef,
3392
+ onSendMessage,
3393
+ setOpen,
3394
+ connected,
3395
+ loading
3039
3396
  }) {
3040
3397
  const [currentChatId, setCurrentChatId] = useState5(null);
3041
3398
  const [pendingChatId, setPendingChatId] = useState5(null);
@@ -3068,18 +3425,18 @@ function useChatManagement({
3068
3425
  const loadedMessages = await loadChatMessages(chatId);
3069
3426
  setMessages(loadedMessages);
3070
3427
  }, [loadChatMessages]);
3071
- const createNewChat = useCallback4(async () => {
3428
+ const createNewChat = useCallback4(async (options) => {
3072
3429
  console.log("[ChatManagement] createNewChat called - currentChatId:", currentChatId, "pendingChatId:", pendingChatId, "messages.length:", messages.length);
3073
3430
  if (pendingChatId && messages.length === 0) {
3074
- console.log("[ChatManagement] Pending chat is already blank, not creating new chat");
3075
- return pendingChatId;
3076
- }
3077
- if (currentChatId && !pendingChatId && messages.length === 0) {
3078
- console.log("[ChatManagement] Current chat is already blank, not creating new chat");
3079
- return currentChatId;
3431
+ const existingChat = await repository.loadChat(pendingChatId);
3432
+ const optionsMatch = existingChat && existingChat.title === options?.title && deepEquals(existingChat.metadata, options?.metadata);
3433
+ if (optionsMatch) {
3434
+ console.log("[ChatManagement] Last created chat has matching options, reusing:", pendingChatId);
3435
+ return pendingChatId;
3436
+ }
3080
3437
  }
3081
3438
  console.log("[ChatManagement] Creating new chat...");
3082
- const chatId = await repository.createChat();
3439
+ const chatId = await repository.createChat(options);
3083
3440
  setPendingChatId(chatId);
3084
3441
  setMessages([]);
3085
3442
  if (clientRef.current) {
@@ -3124,6 +3481,22 @@ function useChatManagement({
3124
3481
  }
3125
3482
  }
3126
3483
  }, [currentChatId, repository]);
3484
+ const getCurrentChat = useCallback4(async () => {
3485
+ const chatId = pendingChatId || currentChatId;
3486
+ if (!chatId) return null;
3487
+ const chat = await repository.loadChat(chatId);
3488
+ if (chat?.metadata) {
3489
+ chat.metadata = Object.freeze({ ...chat.metadata });
3490
+ }
3491
+ return chat;
3492
+ }, [pendingChatId, currentChatId, repository]);
3493
+ const updateMetadata = useCallback4(async (metadata, overwrite = false) => {
3494
+ const chatId = pendingChatId || currentChatId;
3495
+ if (!chatId) {
3496
+ throw new Error("No active chat");
3497
+ }
3498
+ await repository.updateMetadata(chatId, metadata, overwrite);
3499
+ }, [pendingChatId, currentChatId, repository]);
3127
3500
  const activatePendingChat = useCallback4(() => {
3128
3501
  if (!pendingChatId) return null;
3129
3502
  console.log("[ChatManagement] Activating pending chat:", pendingChatId);
@@ -3142,7 +3515,7 @@ function useChatManagement({
3142
3515
  console.error("[ChatManagement] Chat not found:", chatId);
3143
3516
  return false;
3144
3517
  }
3145
- const { generateMessageId: generateMessageId2 } = await import("./types-TVUXB3NB.js");
3518
+ const { generateMessageId: generateMessageId2 } = await import("./types-64CH2HXY.js");
3146
3519
  chat.messages.push({
3147
3520
  id: generateMessageId2(),
3148
3521
  role: "user",
@@ -3178,7 +3551,7 @@ function useChatManagement({
3178
3551
  console.error("[ChatManagement] Chat not found:", currentChatIdValue);
3179
3552
  return;
3180
3553
  }
3181
- const { generateMessageId: generateMessageId2 } = await import("./types-TVUXB3NB.js");
3554
+ const { generateMessageId: generateMessageId2 } = await import("./types-64CH2HXY.js");
3182
3555
  chat.messages.push({
3183
3556
  id: generateMessageId2(),
3184
3557
  role: "assistant",
@@ -3235,6 +3608,70 @@ function useChatManagement({
3235
3608
  }
3236
3609
  }, [currentChatId, pendingChatId, createNewChat, repository, loadChatMessages, clientRef]);
3237
3610
  const displayedChatId = pendingChatId || currentChatId;
3611
+ const pendingMessagesRef = useRef5([]);
3612
+ const isProcessingQueueRef = useRef5(false);
3613
+ const loadingRef = useRef5(loading);
3614
+ useEffect5(() => {
3615
+ loadingRef.current = loading;
3616
+ }, [loading]);
3617
+ const processMessageQueue = useCallback4(async () => {
3618
+ if (isProcessingQueueRef.current || pendingMessagesRef.current.length === 0 || !onSendMessage) {
3619
+ return;
3620
+ }
3621
+ isProcessingQueueRef.current = true;
3622
+ while (pendingMessagesRef.current.length > 0) {
3623
+ const { message, options } = pendingMessagesRef.current.shift();
3624
+ const { newChat = false, attachments = [], openChat = true, metadata } = options ?? {};
3625
+ if (newChat) {
3626
+ await createNewChat({ metadata });
3627
+ }
3628
+ const fileAttachments = await Promise.all(
3629
+ attachments.map(async (file) => {
3630
+ let preview;
3631
+ if (file.type.startsWith("image/")) {
3632
+ preview = await new Promise((resolve) => {
3633
+ const reader = new FileReader();
3634
+ reader.onload = () => resolve(typeof reader.result === "string" ? reader.result : void 0);
3635
+ reader.onerror = () => resolve(void 0);
3636
+ reader.readAsDataURL(file);
3637
+ });
3638
+ }
3639
+ return {
3640
+ id: crypto.randomUUID(),
3641
+ file,
3642
+ preview
3643
+ };
3644
+ })
3645
+ );
3646
+ await onSendMessage(message, fileAttachments.length > 0 ? fileAttachments : void 0);
3647
+ if (openChat && setOpen) {
3648
+ setOpen(true);
3649
+ }
3650
+ await new Promise((resolve) => {
3651
+ const checkLoading = () => {
3652
+ setTimeout(() => {
3653
+ if (!loadingRef.current) {
3654
+ resolve();
3655
+ } else {
3656
+ checkLoading();
3657
+ }
3658
+ }, 100);
3659
+ };
3660
+ checkLoading();
3661
+ });
3662
+ }
3663
+ isProcessingQueueRef.current = false;
3664
+ }, [onSendMessage, createNewChat, setOpen]);
3665
+ const sendMessage = useCallback4(async (message, options) => {
3666
+ if (!onSendMessage) {
3667
+ throw new Error("sendMessage is not available (onSendMessage callback not provided)");
3668
+ }
3669
+ if (!connected) {
3670
+ throw new Error("Not connected to UseAI server");
3671
+ }
3672
+ pendingMessagesRef.current.push({ message, options });
3673
+ await processMessageQueue();
3674
+ }, [onSendMessage, connected, processMessageQueue]);
3238
3675
  return {
3239
3676
  currentChatId,
3240
3677
  pendingChatId,
@@ -3249,6 +3686,9 @@ function useChatManagement({
3249
3686
  saveUserMessage,
3250
3687
  saveAIResponse,
3251
3688
  reloadMessages,
3689
+ sendMessage,
3690
+ getCurrentChat,
3691
+ updateMetadata,
3252
3692
  currentChatIdSnapshot,
3253
3693
  pendingChatIdSnapshot
3254
3694
  };
@@ -3630,7 +4070,7 @@ function usePromptState({
3630
4070
  }
3631
4071
 
3632
4072
  // src/providers/useAIProvider.tsx
3633
- import { Fragment as Fragment3, jsx as jsx11, jsxs as jsxs7 } from "react/jsx-runtime";
4073
+ import { Fragment as Fragment3, jsx as jsx13, jsxs as jsxs9 } from "react/jsx-runtime";
3634
4074
  var __UseAIContext = createContext4(null);
3635
4075
  var hasWarnedAboutMissingProvider = false;
3636
4076
  var noOpContextValue = {
@@ -3660,6 +4100,11 @@ var noOpContextValue = {
3660
4100
  },
3661
4101
  list: async () => [],
3662
4102
  clear: async () => {
4103
+ },
4104
+ sendMessage: async () => {
4105
+ },
4106
+ get: async () => null,
4107
+ updateMetadata: async () => {
3663
4108
  }
3664
4109
  },
3665
4110
  agents: {
@@ -3699,7 +4144,8 @@ function UseAIProvider({
3699
4144
  renderChat = true,
3700
4145
  theme: customTheme,
3701
4146
  strings: customStrings,
3702
- visibleAgentIds
4147
+ visibleAgentIds,
4148
+ onOpenChange
3703
4149
  }) {
3704
4150
  const fileUploadConfig = fileUploadConfigProp === false ? void 0 : fileUploadConfigProp ?? DEFAULT_FILE_UPLOAD_CONFIG;
3705
4151
  const theme = { ...defaultTheme, ...customTheme };
@@ -3707,12 +4153,17 @@ function UseAIProvider({
3707
4153
  const [connected, setConnected] = useState10(false);
3708
4154
  const [isChatOpen, setIsChatOpen] = useState10(false);
3709
4155
  const [loading, setLoading] = useState10(false);
4156
+ const handleSetChatOpen = useCallback9((open) => {
4157
+ setIsChatOpen(open);
4158
+ onOpenChange?.(open);
4159
+ }, [onOpenChange]);
3710
4160
  const [streamingText, setStreamingText] = useState10("");
3711
4161
  const streamingChatIdRef = useRef9(null);
3712
4162
  const clientRef = useRef9(null);
3713
4163
  const repositoryRef = useRef9(
3714
4164
  chatRepository || new LocalStorageChatRepository()
3715
4165
  );
4166
+ const handleSendMessageRef = useRef9(null);
3716
4167
  const {
3717
4168
  registerTools,
3718
4169
  unregisterTools,
@@ -3734,9 +4185,18 @@ function UseAIProvider({
3734
4185
  clientRef,
3735
4186
  connected
3736
4187
  });
4188
+ const stableSendMessage = useCallback9(async (message, attachments) => {
4189
+ if (handleSendMessageRef.current) {
4190
+ await handleSendMessageRef.current(message, attachments);
4191
+ }
4192
+ }, []);
3737
4193
  const chatManagement = useChatManagement({
3738
4194
  repository: repositoryRef.current,
3739
- clientRef
4195
+ clientRef,
4196
+ onSendMessage: stableSendMessage,
4197
+ setOpen: handleSetChatOpen,
4198
+ connected,
4199
+ loading
3740
4200
  });
3741
4201
  const {
3742
4202
  currentChatId,
@@ -3750,7 +4210,10 @@ function UseAIProvider({
3750
4210
  clearCurrentChat,
3751
4211
  activatePendingChat,
3752
4212
  saveUserMessage,
3753
- saveAIResponse
4213
+ saveAIResponse,
4214
+ sendMessage,
4215
+ getCurrentChat,
4216
+ updateMetadata
3754
4217
  } = chatManagement;
3755
4218
  const {
3756
4219
  availableAgents,
@@ -3891,7 +4354,6 @@ function UseAIProvider({
3891
4354
  let persistedContent = message;
3892
4355
  let multimodalContent;
3893
4356
  if (attachments && attachments.length > 0) {
3894
- const backend = fileUploadConfig?.backend ?? new EmbedFileUploadBackend();
3895
4357
  const persistedParts = [];
3896
4358
  if (message.trim()) {
3897
4359
  persistedParts.push({ type: "text", text: message });
@@ -3907,35 +4369,24 @@ function UseAIProvider({
3907
4369
  });
3908
4370
  }
3909
4371
  persistedContent = persistedParts;
3910
- const contentParts = [];
4372
+ const fileContent = await processAttachments(attachments, {
4373
+ getCurrentChat,
4374
+ backend: fileUploadConfig?.backend,
4375
+ transformers: fileUploadConfig?.transformers
4376
+ });
4377
+ multimodalContent = [];
3911
4378
  if (message.trim()) {
3912
- contentParts.push({ type: "text", text: message });
4379
+ multimodalContent.push({ type: "text", text: message });
3913
4380
  }
3914
- for (const attachment of attachments) {
3915
- try {
3916
- const url = await backend.prepareForSend(attachment.file);
3917
- if (attachment.file.type.startsWith("image/")) {
3918
- contentParts.push({ type: "image", url });
3919
- } else {
3920
- contentParts.push({
3921
- type: "file",
3922
- url,
3923
- mimeType: attachment.file.type,
3924
- name: attachment.file.name
3925
- });
3926
- }
3927
- } catch (error) {
3928
- console.error("[Provider] Failed to prepare file for send:", error);
3929
- }
3930
- }
3931
- multimodalContent = contentParts;
4381
+ multimodalContent.push(...fileContent);
3932
4382
  }
3933
4383
  if (activeChatId) {
3934
4384
  await saveUserMessage(activeChatId, persistedContent);
3935
4385
  }
3936
4386
  setLoading(true);
3937
4387
  await clientRef.current.sendPrompt(message, multimodalContent);
3938
- }, [activatePendingChat, currentChatId, saveUserMessage, fileUploadConfig]);
4388
+ }, [activatePendingChat, currentChatId, saveUserMessage, fileUploadConfig, getCurrentChat]);
4389
+ handleSendMessageRef.current = handleSendMessage;
3939
4390
  const value = {
3940
4391
  serverUrl,
3941
4392
  connected,
@@ -3955,7 +4406,10 @@ function UseAIProvider({
3955
4406
  load: loadChat,
3956
4407
  delete: deleteChat,
3957
4408
  list: listChats,
3958
- clear: clearCurrentChat
4409
+ clear: clearCurrentChat,
4410
+ sendMessage,
4411
+ get: getCurrentChat,
4412
+ updateMetadata
3959
4413
  },
3960
4414
  agents: {
3961
4415
  available: availableAgents,
@@ -3985,7 +4439,8 @@ function UseAIProvider({
3985
4439
  create: createNewChat,
3986
4440
  load: loadChat,
3987
4441
  delete: deleteChat,
3988
- list: listChats
4442
+ list: listChats,
4443
+ get: getCurrentChat
3989
4444
  },
3990
4445
  agents: {
3991
4446
  available: availableAgents,
@@ -4001,7 +4456,7 @@ function UseAIProvider({
4001
4456
  },
4002
4457
  ui: {
4003
4458
  isOpen: isChatOpen,
4004
- setOpen: setIsChatOpen
4459
+ setOpen: handleSetChatOpen
4005
4460
  }
4006
4461
  };
4007
4462
  const isUIDisabled = CustomButton === null || CustomChat === null;
@@ -4031,21 +4486,21 @@ function UseAIProvider({
4031
4486
  };
4032
4487
  const renderDefaultChat = () => {
4033
4488
  if (isUIDisabled) return null;
4034
- return /* @__PURE__ */ jsx11(UseAIFloatingChatWrapper, { isOpen: isChatOpen, onClose: () => setIsChatOpen(false), children: /* @__PURE__ */ jsx11(
4489
+ return /* @__PURE__ */ jsx13(UseAIFloatingChatWrapper, { isOpen: isChatOpen, onClose: () => handleSetChatOpen(false), children: /* @__PURE__ */ jsx13(
4035
4490
  UseAIChatPanel,
4036
4491
  {
4037
4492
  ...chatPanelProps,
4038
- closeButton: /* @__PURE__ */ jsx11(CloseButton, { onClick: () => setIsChatOpen(false) })
4493
+ closeButton: /* @__PURE__ */ jsx13(CloseButton, { onClick: () => handleSetChatOpen(false) })
4039
4494
  }
4040
4495
  ) });
4041
4496
  };
4042
4497
  const renderCustomChat = () => {
4043
4498
  if (!CustomChat) return null;
4044
- return /* @__PURE__ */ jsx11(
4499
+ return /* @__PURE__ */ jsx13(
4045
4500
  CustomChat,
4046
4501
  {
4047
4502
  isOpen: isChatOpen,
4048
- onClose: () => setIsChatOpen(false),
4503
+ onClose: () => handleSetChatOpen(false),
4049
4504
  onSendMessage: handleSendMessage,
4050
4505
  messages,
4051
4506
  loading,
@@ -4060,18 +4515,18 @@ function UseAIProvider({
4060
4515
  };
4061
4516
  const renderBuiltInChat = () => {
4062
4517
  if (!renderChat) return null;
4063
- return /* @__PURE__ */ jsxs7(Fragment3, { children: [
4064
- ButtonComponent && /* @__PURE__ */ jsx11(
4518
+ return /* @__PURE__ */ jsxs9(Fragment3, { children: [
4519
+ ButtonComponent && /* @__PURE__ */ jsx13(
4065
4520
  ButtonComponent,
4066
4521
  {
4067
- onClick: () => setIsChatOpen(true),
4522
+ onClick: () => handleSetChatOpen(true),
4068
4523
  connected
4069
4524
  }
4070
4525
  ),
4071
4526
  hasCustomChat ? renderCustomChat() : renderDefaultChat()
4072
4527
  ] });
4073
4528
  };
4074
- 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: [
4529
+ 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: [
4075
4530
  children,
4076
4531
  renderBuiltInChat()
4077
4532
  ] }) }) }) });
@@ -4474,14 +4929,18 @@ export {
4474
4929
  UseAIFloatingButton,
4475
4930
  UseAIFloatingChatWrapper,
4476
4931
  UseAIProvider,
4932
+ clearTransformationCache,
4477
4933
  convertToolsToDefinitions,
4478
4934
  defaultStrings,
4479
4935
  defaultTheme,
4480
4936
  defineTool,
4481
4937
  executeDefinedTool,
4938
+ findTransformer,
4482
4939
  generateChatId,
4483
4940
  generateCommandId,
4484
4941
  generateMessageId,
4942
+ matchesMimeType,
4943
+ processAttachments,
4485
4944
  useAI,
4486
4945
  useAIContext,
4487
4946
  useAIWorkflow,