@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/bundled.js CHANGED
@@ -4,7 +4,7 @@ import {
4
4
  __toESM,
5
5
  generateChatId,
6
6
  generateMessageId
7
- } from "./chunk-EGD4LT6R.js";
7
+ } from "./chunk-AKQM6IWU.js";
8
8
 
9
9
  // ../../node_modules/.bun/is-buffer@2.0.5/node_modules/is-buffer/index.js
10
10
  var require_is_buffer = __commonJS({
@@ -5752,8 +5752,6 @@ var defaultStrings = {
5752
5752
  placeholder: "Type a message...",
5753
5753
  /** Input placeholder when connecting */
5754
5754
  connectingPlaceholder: "Connecting...",
5755
- /** Send button text */
5756
- send: "Send",
5757
5755
  /** Loading indicator text */
5758
5756
  thinking: "Thinking"
5759
5757
  },
@@ -14686,8 +14684,131 @@ function MarkdownContent({ content: content3 }) {
14686
14684
  );
14687
14685
  }
14688
14686
 
14689
- // src/components/FileChip.tsx
14687
+ // src/components/Spinner.tsx
14690
14688
  import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
14689
+ function Spinner({
14690
+ size = 16,
14691
+ color: color2 = "currentColor",
14692
+ trackColor,
14693
+ strokeWidth = 2
14694
+ }) {
14695
+ const radius = 10;
14696
+ const circumference = 2 * Math.PI * radius;
14697
+ return /* @__PURE__ */ jsxs2(
14698
+ "svg",
14699
+ {
14700
+ "data-testid": "spinner",
14701
+ width: size,
14702
+ height: size,
14703
+ viewBox: "0 0 24 24",
14704
+ style: {
14705
+ animation: "use-ai-spin 1s linear infinite"
14706
+ },
14707
+ children: [
14708
+ /* @__PURE__ */ jsx3("style", { children: `
14709
+ @keyframes use-ai-spin {
14710
+ from { transform: rotate(0deg); }
14711
+ to { transform: rotate(360deg); }
14712
+ }
14713
+ ` }),
14714
+ /* @__PURE__ */ jsx3(
14715
+ "circle",
14716
+ {
14717
+ cx: "12",
14718
+ cy: "12",
14719
+ r: radius,
14720
+ fill: "none",
14721
+ stroke: trackColor || color2,
14722
+ strokeWidth,
14723
+ opacity: trackColor ? 1 : 0.25
14724
+ }
14725
+ ),
14726
+ /* @__PURE__ */ jsx3(
14727
+ "circle",
14728
+ {
14729
+ cx: "12",
14730
+ cy: "12",
14731
+ r: radius,
14732
+ fill: "none",
14733
+ stroke: color2,
14734
+ strokeWidth,
14735
+ strokeLinecap: "round",
14736
+ strokeDasharray: circumference,
14737
+ strokeDashoffset: circumference * 0.75,
14738
+ style: {
14739
+ transformOrigin: "center"
14740
+ }
14741
+ }
14742
+ )
14743
+ ]
14744
+ }
14745
+ );
14746
+ }
14747
+
14748
+ // src/components/ProgressBar.tsx
14749
+ import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
14750
+ function ProgressBar({
14751
+ progress,
14752
+ size = 16,
14753
+ color: color2 = "currentColor",
14754
+ trackColor,
14755
+ strokeWidth = 2
14756
+ }) {
14757
+ const clampedProgress = Math.min(100, Math.max(0, progress));
14758
+ const radius = 10;
14759
+ const circumference = 2 * Math.PI * radius;
14760
+ const strokeDashoffset = circumference * (1 - clampedProgress / 100);
14761
+ return /* @__PURE__ */ jsxs3(
14762
+ "svg",
14763
+ {
14764
+ "data-testid": "progress-bar",
14765
+ role: "progressbar",
14766
+ "aria-valuenow": clampedProgress,
14767
+ "aria-valuemin": 0,
14768
+ "aria-valuemax": 100,
14769
+ width: size,
14770
+ height: size,
14771
+ viewBox: "0 0 24 24",
14772
+ style: {
14773
+ transform: "rotate(-90deg)"
14774
+ },
14775
+ children: [
14776
+ /* @__PURE__ */ jsx4(
14777
+ "circle",
14778
+ {
14779
+ cx: "12",
14780
+ cy: "12",
14781
+ r: radius,
14782
+ fill: "none",
14783
+ stroke: trackColor || color2,
14784
+ strokeWidth,
14785
+ opacity: trackColor ? 1 : 0.25
14786
+ }
14787
+ ),
14788
+ /* @__PURE__ */ jsx4(
14789
+ "circle",
14790
+ {
14791
+ cx: "12",
14792
+ cy: "12",
14793
+ r: radius,
14794
+ fill: "none",
14795
+ stroke: color2,
14796
+ strokeWidth,
14797
+ strokeLinecap: "round",
14798
+ strokeDasharray: circumference,
14799
+ strokeDashoffset,
14800
+ style: {
14801
+ transition: "stroke-dashoffset 0.2s ease"
14802
+ }
14803
+ }
14804
+ )
14805
+ ]
14806
+ }
14807
+ );
14808
+ }
14809
+
14810
+ // src/components/FileChip.tsx
14811
+ import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
14691
14812
  function formatFileSize(bytes) {
14692
14813
  if (bytes < 1024) return `${bytes} B`;
14693
14814
  if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
@@ -14702,11 +14823,14 @@ function truncateFilename(name, maxLength = 20) {
14702
14823
  if (maxBaseLength < 5) return name.substring(0, maxLength - 3) + "...";
14703
14824
  return baseName.substring(0, maxBaseLength) + "..." + ext;
14704
14825
  }
14705
- function FileChip({ attachment, onRemove, disabled }) {
14826
+ function FileChip({ attachment, onRemove, disabled, processingState }) {
14706
14827
  const theme = useTheme();
14707
14828
  const { file: file2, preview } = attachment;
14708
14829
  const isImage = file2.type.startsWith("image/");
14709
- return /* @__PURE__ */ jsxs2(
14830
+ const isProcessing = processingState?.status === "processing";
14831
+ const hasError = processingState?.status === "error";
14832
+ const progress = processingState?.progress;
14833
+ return /* @__PURE__ */ jsxs4(
14710
14834
  "div",
14711
14835
  {
14712
14836
  "data-testid": "file-chip",
@@ -14719,10 +14843,12 @@ function FileChip({ attachment, onRemove, disabled }) {
14719
14843
  borderRadius: "8px",
14720
14844
  fontSize: "13px",
14721
14845
  color: theme.textColor,
14722
- maxWidth: "200px"
14846
+ maxWidth: "200px",
14847
+ position: "relative",
14848
+ opacity: isProcessing ? 0.7 : 1
14723
14849
  },
14724
14850
  children: [
14725
- isImage && preview ? /* @__PURE__ */ jsx3(
14851
+ isImage && preview ? /* @__PURE__ */ jsx5(
14726
14852
  "img",
14727
14853
  {
14728
14854
  src: preview,
@@ -14734,7 +14860,7 @@ function FileChip({ attachment, onRemove, disabled }) {
14734
14860
  objectFit: "cover"
14735
14861
  }
14736
14862
  }
14737
- ) : /* @__PURE__ */ jsx3(
14863
+ ) : /* @__PURE__ */ jsx5(
14738
14864
  "div",
14739
14865
  {
14740
14866
  style: {
@@ -14750,8 +14876,8 @@ function FileChip({ attachment, onRemove, disabled }) {
14750
14876
  children: "\u{1F4CE}"
14751
14877
  }
14752
14878
  ),
14753
- /* @__PURE__ */ jsxs2("div", { style: { flex: 1, minWidth: 0, overflow: "hidden" }, children: [
14754
- /* @__PURE__ */ jsx3(
14879
+ /* @__PURE__ */ jsxs4("div", { style: { flex: 1, minWidth: 0, overflow: "hidden" }, children: [
14880
+ /* @__PURE__ */ jsx5(
14755
14881
  "div",
14756
14882
  {
14757
14883
  style: {
@@ -14764,28 +14890,28 @@ function FileChip({ attachment, onRemove, disabled }) {
14764
14890
  children: truncateFilename(file2.name)
14765
14891
  }
14766
14892
  ),
14767
- /* @__PURE__ */ jsx3("div", { style: { fontSize: "11px", color: theme.secondaryTextColor }, children: formatFileSize(file2.size) })
14893
+ /* @__PURE__ */ jsx5("div", { style: { fontSize: "11px", color: theme.secondaryTextColor }, children: formatFileSize(file2.size) })
14768
14894
  ] }),
14769
- /* @__PURE__ */ jsx3(
14895
+ /* @__PURE__ */ jsx5(
14770
14896
  "button",
14771
14897
  {
14772
14898
  "data-testid": "file-chip-remove",
14773
14899
  onClick: onRemove,
14774
- disabled,
14900
+ disabled: disabled || isProcessing,
14775
14901
  style: {
14776
14902
  background: "transparent",
14777
14903
  border: "none",
14778
14904
  padding: "2px 4px",
14779
- cursor: disabled ? "not-allowed" : "pointer",
14905
+ cursor: disabled || isProcessing ? "not-allowed" : "pointer",
14780
14906
  color: theme.placeholderTextColor,
14781
14907
  fontSize: "16px",
14782
14908
  lineHeight: 1,
14783
14909
  borderRadius: "4px",
14784
14910
  transition: "all 0.15s",
14785
- opacity: disabled ? 0.5 : 1
14911
+ opacity: disabled || isProcessing ? 0.5 : 1
14786
14912
  },
14787
14913
  onMouseEnter: (e) => {
14788
- if (!disabled) {
14914
+ if (!disabled && !isProcessing) {
14789
14915
  e.currentTarget.style.background = theme.borderColor;
14790
14916
  e.currentTarget.style.color = theme.textColor;
14791
14917
  }
@@ -14796,6 +14922,44 @@ function FileChip({ attachment, onRemove, disabled }) {
14796
14922
  },
14797
14923
  children: "\xD7"
14798
14924
  }
14925
+ ),
14926
+ isProcessing && /* @__PURE__ */ jsx5(
14927
+ "div",
14928
+ {
14929
+ "data-testid": "file-chip-processing",
14930
+ style: {
14931
+ position: "absolute",
14932
+ inset: 0,
14933
+ display: "flex",
14934
+ alignItems: "center",
14935
+ justifyContent: "center",
14936
+ background: "rgba(255, 255, 255, 0.7)",
14937
+ borderRadius: "inherit"
14938
+ },
14939
+ children: progress !== void 0 ? /* @__PURE__ */ jsx5(ProgressBar, { progress, size: 16, color: theme.secondaryTextColor }) : /* @__PURE__ */ jsx5(Spinner, { size: 16, color: theme.secondaryTextColor })
14940
+ }
14941
+ ),
14942
+ hasError && /* @__PURE__ */ jsx5(
14943
+ "div",
14944
+ {
14945
+ "data-testid": "file-chip-error",
14946
+ style: {
14947
+ position: "absolute",
14948
+ bottom: "-2px",
14949
+ right: "-2px",
14950
+ width: "12px",
14951
+ height: "12px",
14952
+ borderRadius: "50%",
14953
+ background: "#ef4444",
14954
+ display: "flex",
14955
+ alignItems: "center",
14956
+ justifyContent: "center",
14957
+ fontSize: "8px",
14958
+ color: "white",
14959
+ fontWeight: "bold"
14960
+ },
14961
+ children: "!"
14962
+ }
14799
14963
  )
14800
14964
  ]
14801
14965
  }
@@ -14803,7 +14967,7 @@ function FileChip({ attachment, onRemove, disabled }) {
14803
14967
  }
14804
14968
  function FilePlaceholder({ name, size }) {
14805
14969
  const theme = useTheme();
14806
- return /* @__PURE__ */ jsxs2(
14970
+ return /* @__PURE__ */ jsxs4(
14807
14971
  "div",
14808
14972
  {
14809
14973
  "data-testid": "file-placeholder",
@@ -14820,9 +14984,9 @@ function FilePlaceholder({ name, size }) {
14820
14984
  maxWidth: "200px"
14821
14985
  },
14822
14986
  children: [
14823
- /* @__PURE__ */ jsx3("span", { children: "\u{1F4CE}" }),
14824
- /* @__PURE__ */ jsxs2("div", { style: { flex: 1, minWidth: 0, overflow: "hidden" }, children: [
14825
- /* @__PURE__ */ jsx3(
14987
+ /* @__PURE__ */ jsx5("span", { children: "\u{1F4CE}" }),
14988
+ /* @__PURE__ */ jsxs4("div", { style: { flex: 1, minWidth: 0, overflow: "hidden" }, children: [
14989
+ /* @__PURE__ */ jsx5(
14826
14990
  "div",
14827
14991
  {
14828
14992
  style: {
@@ -14834,7 +14998,7 @@ function FilePlaceholder({ name, size }) {
14834
14998
  children: truncateFilename(name)
14835
14999
  }
14836
15000
  ),
14837
- /* @__PURE__ */ jsx3("div", { style: { fontSize: "11px" }, children: formatFileSize(size) })
15001
+ /* @__PURE__ */ jsx5("div", { style: { fontSize: "11px" }, children: formatFileSize(size) })
14838
15002
  ] })
14839
15003
  ]
14840
15004
  }
@@ -14863,7 +15027,7 @@ function validateCommandName(name) {
14863
15027
 
14864
15028
  // src/components/CommandAutocomplete.tsx
14865
15029
  import { useEffect, useRef } from "react";
14866
- import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
15030
+ import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
14867
15031
  var MAX_VISIBLE_ITEMS = 8;
14868
15032
  function CommandAutocomplete({
14869
15033
  commands,
@@ -14894,7 +15058,7 @@ function CommandAutocomplete({
14894
15058
  }
14895
15059
  }, [highlightedIndex]);
14896
15060
  if (filteredCommands.length === 0) {
14897
- return /* @__PURE__ */ jsx4(
15061
+ return /* @__PURE__ */ jsx6(
14898
15062
  "div",
14899
15063
  {
14900
15064
  "data-testid": "command-autocomplete",
@@ -14910,7 +15074,7 @@ function CommandAutocomplete({
14910
15074
  overflow: "hidden",
14911
15075
  zIndex: 1005
14912
15076
  },
14913
- children: /* @__PURE__ */ jsx4(
15077
+ children: /* @__PURE__ */ jsx6(
14914
15078
  "div",
14915
15079
  {
14916
15080
  style: {
@@ -14925,7 +15089,7 @@ function CommandAutocomplete({
14925
15089
  }
14926
15090
  );
14927
15091
  }
14928
- return /* @__PURE__ */ jsx4(
15092
+ return /* @__PURE__ */ jsx6(
14929
15093
  "div",
14930
15094
  {
14931
15095
  "data-testid": "command-autocomplete",
@@ -14944,7 +15108,7 @@ function CommandAutocomplete({
14944
15108
  overflowY: "auto",
14945
15109
  zIndex: 1005
14946
15110
  },
14947
- children: filteredCommands.map((cmd, index2) => /* @__PURE__ */ jsxs3(
15111
+ children: filteredCommands.map((cmd, index2) => /* @__PURE__ */ jsxs5(
14948
15112
  "div",
14949
15113
  {
14950
15114
  ref: (el) => {
@@ -14964,8 +15128,8 @@ function CommandAutocomplete({
14964
15128
  gap: "8px"
14965
15129
  },
14966
15130
  children: [
14967
- /* @__PURE__ */ jsxs3("div", { style: { flex: 1, minWidth: 0 }, children: [
14968
- /* @__PURE__ */ jsxs3(
15131
+ /* @__PURE__ */ jsxs5("div", { style: { flex: 1, minWidth: 0 }, children: [
15132
+ /* @__PURE__ */ jsxs5(
14969
15133
  "div",
14970
15134
  {
14971
15135
  style: {
@@ -14979,7 +15143,7 @@ function CommandAutocomplete({
14979
15143
  ]
14980
15144
  }
14981
15145
  ),
14982
- /* @__PURE__ */ jsx4(
15146
+ /* @__PURE__ */ jsx6(
14983
15147
  "div",
14984
15148
  {
14985
15149
  style: {
@@ -14994,7 +15158,7 @@ function CommandAutocomplete({
14994
15158
  }
14995
15159
  )
14996
15160
  ] }),
14997
- onDelete && /* @__PURE__ */ jsx4(
15161
+ onDelete && /* @__PURE__ */ jsx6(
14998
15162
  "button",
14999
15163
  {
15000
15164
  "data-testid": "command-delete-button",
@@ -15025,7 +15189,7 @@ function CommandAutocomplete({
15025
15189
  e.currentTarget.style.background = "transparent";
15026
15190
  },
15027
15191
  title: strings.commands.deleteCommand,
15028
- 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" }) })
15192
+ 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" }) })
15029
15193
  }
15030
15194
  )
15031
15195
  ]
@@ -15043,7 +15207,7 @@ function getFilteredCommandsCount(commands, searchPrefix) {
15043
15207
  }
15044
15208
 
15045
15209
  // src/hooks/useSlashCommands.tsx
15046
- import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
15210
+ import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
15047
15211
  var MAX_VISIBLE_ITEMS2 = 8;
15048
15212
  function useSlashCommands({
15049
15213
  commands,
@@ -15178,7 +15342,7 @@ function useSlashCommands({
15178
15342
  }
15179
15343
  }
15180
15344
  }, [savingMessageId, savingMessageText, commandNameInput, commands, onRenameCommand, onSaveCommand, cancelInlineSave, strings]);
15181
- const AutocompleteComponent = showAutocomplete && commands.length > 0 ? /* @__PURE__ */ jsx5(
15345
+ const AutocompleteComponent = showAutocomplete && commands.length > 0 ? /* @__PURE__ */ jsx7(
15182
15346
  CommandAutocomplete,
15183
15347
  {
15184
15348
  commands,
@@ -15194,7 +15358,7 @@ function useSlashCommands({
15194
15358
  if (savingMessageId !== messageId) {
15195
15359
  return null;
15196
15360
  }
15197
- return /* @__PURE__ */ jsxs4(
15361
+ return /* @__PURE__ */ jsxs6(
15198
15362
  "div",
15199
15363
  {
15200
15364
  "data-testid": "inline-save-command",
@@ -15208,9 +15372,9 @@ function useSlashCommands({
15208
15372
  gap: "4px"
15209
15373
  },
15210
15374
  children: [
15211
- /* @__PURE__ */ jsxs4("div", { style: { display: "flex", alignItems: "center", gap: "6px" }, children: [
15212
- /* @__PURE__ */ jsx5("span", { style: { color: theme.primaryColor, fontSize: "13px", fontWeight: 500 }, children: "/" }),
15213
- /* @__PURE__ */ jsx5(
15375
+ /* @__PURE__ */ jsxs6("div", { style: { display: "flex", alignItems: "center", gap: "6px" }, children: [
15376
+ /* @__PURE__ */ jsx7("span", { style: { color: theme.primaryColor, fontSize: "13px", fontWeight: 500 }, children: "/" }),
15377
+ /* @__PURE__ */ jsx7(
15214
15378
  "input",
15215
15379
  {
15216
15380
  ref: commandNameInputRef,
@@ -15242,7 +15406,7 @@ function useSlashCommands({
15242
15406
  }
15243
15407
  }
15244
15408
  ),
15245
- /* @__PURE__ */ jsx5(
15409
+ /* @__PURE__ */ jsx7(
15246
15410
  "button",
15247
15411
  {
15248
15412
  "data-testid": "save-command-confirm",
@@ -15260,15 +15424,15 @@ function useSlashCommands({
15260
15424
  justifyContent: "center"
15261
15425
  },
15262
15426
  title: strings.commands.saveCommand,
15263
- children: /* @__PURE__ */ jsxs4("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
15264
- /* @__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" }),
15265
- /* @__PURE__ */ jsx5("polyline", { points: "17 21 17 13 7 13 7 21" }),
15266
- /* @__PURE__ */ jsx5("polyline", { points: "7 3 7 8 15 8" })
15427
+ children: /* @__PURE__ */ jsxs6("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
15428
+ /* @__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" }),
15429
+ /* @__PURE__ */ jsx7("polyline", { points: "17 21 17 13 7 13 7 21" }),
15430
+ /* @__PURE__ */ jsx7("polyline", { points: "7 3 7 8 15 8" })
15267
15431
  ] })
15268
15432
  }
15269
15433
  )
15270
15434
  ] }),
15271
- commandSaveError && /* @__PURE__ */ jsx5(
15435
+ commandSaveError && /* @__PURE__ */ jsx7(
15272
15436
  "div",
15273
15437
  {
15274
15438
  "data-testid": "command-save-error",
@@ -15303,6 +15467,148 @@ import { useState as useState2, useRef as useRef3, useCallback as useCallback2,
15303
15467
  // src/fileUpload/types.ts
15304
15468
  var DEFAULT_MAX_FILE_SIZE = 10 * 1024 * 1024;
15305
15469
 
15470
+ // src/fileUpload/mimeTypeMatcher.ts
15471
+ function matchesMimeType(mimeType, pattern) {
15472
+ if (!pattern.includes("*")) {
15473
+ return mimeType === pattern;
15474
+ }
15475
+ const regexPattern = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*");
15476
+ const regex = new RegExp(`^${regexPattern}$`);
15477
+ return regex.test(mimeType);
15478
+ }
15479
+ function findTransformer(mimeType, transformers) {
15480
+ if (!transformers) {
15481
+ return void 0;
15482
+ }
15483
+ let bestMatch;
15484
+ let bestIsExact = false;
15485
+ let bestLength = -1;
15486
+ for (const [pattern, transformer] of Object.entries(transformers)) {
15487
+ if (!matchesMimeType(mimeType, pattern)) {
15488
+ continue;
15489
+ }
15490
+ const isExact = !pattern.includes("*");
15491
+ if (isExact && !bestIsExact) {
15492
+ bestMatch = transformer;
15493
+ bestIsExact = true;
15494
+ bestLength = pattern.length;
15495
+ continue;
15496
+ }
15497
+ if (isExact === bestIsExact && pattern.length > bestLength) {
15498
+ bestMatch = transformer;
15499
+ bestLength = pattern.length;
15500
+ }
15501
+ }
15502
+ return bestMatch;
15503
+ }
15504
+
15505
+ // src/fileUpload/EmbedFileUploadBackend.ts
15506
+ var EmbedFileUploadBackend = class {
15507
+ /**
15508
+ * Converts a File to a base64 data URL.
15509
+ *
15510
+ * @param file - The File object to convert
15511
+ * @returns Promise resolving to a base64 data URL (e.g., "data:image/png;base64,...")
15512
+ * @throws Error if file reading fails
15513
+ */
15514
+ async prepareForSend(file2) {
15515
+ return new Promise((resolve, reject) => {
15516
+ const reader = new FileReader();
15517
+ reader.onload = () => {
15518
+ if (typeof reader.result === "string") {
15519
+ resolve(reader.result);
15520
+ } else {
15521
+ reject(new Error("Failed to read file as data URL"));
15522
+ }
15523
+ };
15524
+ reader.onerror = () => {
15525
+ reject(new Error(`Failed to read file: ${file2.name}`));
15526
+ };
15527
+ reader.readAsDataURL(file2);
15528
+ });
15529
+ }
15530
+ };
15531
+
15532
+ // src/fileUpload/processAttachments.ts
15533
+ var transformationCache = /* @__PURE__ */ new Map();
15534
+ function getFileCacheKey(file2) {
15535
+ return `${file2.name}:${file2.size}:${file2.lastModified}`;
15536
+ }
15537
+ async function getTransformedContent(file2, transformer, context, onProgress) {
15538
+ const cacheKey = getFileCacheKey(file2);
15539
+ const cached2 = transformationCache.get(cacheKey);
15540
+ if (cached2 !== void 0) {
15541
+ return cached2;
15542
+ }
15543
+ const result = await transformer.transform(file2, context, onProgress);
15544
+ transformationCache.set(cacheKey, result);
15545
+ return result;
15546
+ }
15547
+ async function processAttachments(attachments, config2) {
15548
+ const { getCurrentChat, backend = new EmbedFileUploadBackend(), transformers = {}, onFileProgress } = config2;
15549
+ const contentParts = [];
15550
+ const chat = await getCurrentChat();
15551
+ const context = { chat };
15552
+ for (const attachment of attachments) {
15553
+ onFileProgress?.(attachment.id, { status: "processing" });
15554
+ try {
15555
+ if (attachment.transformedContent !== void 0) {
15556
+ contentParts.push({
15557
+ type: "transformed_file",
15558
+ text: attachment.transformedContent,
15559
+ originalFile: {
15560
+ name: attachment.file.name,
15561
+ mimeType: attachment.file.type,
15562
+ size: attachment.file.size
15563
+ }
15564
+ });
15565
+ onFileProgress?.(attachment.id, { status: "done" });
15566
+ continue;
15567
+ }
15568
+ const transformer = findTransformer(attachment.file.type, transformers);
15569
+ if (transformer) {
15570
+ const transformedText = await getTransformedContent(
15571
+ attachment.file,
15572
+ transformer,
15573
+ context,
15574
+ (progress) => {
15575
+ onFileProgress?.(attachment.id, { status: "processing", progress });
15576
+ }
15577
+ );
15578
+ contentParts.push({
15579
+ type: "transformed_file",
15580
+ text: transformedText,
15581
+ originalFile: {
15582
+ name: attachment.file.name,
15583
+ mimeType: attachment.file.type,
15584
+ size: attachment.file.size
15585
+ }
15586
+ });
15587
+ } else {
15588
+ const url3 = await backend.prepareForSend(attachment.file);
15589
+ if (attachment.file.type.startsWith("image/")) {
15590
+ contentParts.push({ type: "image", url: url3 });
15591
+ } else {
15592
+ contentParts.push({
15593
+ type: "file",
15594
+ url: url3,
15595
+ mimeType: attachment.file.type,
15596
+ name: attachment.file.name
15597
+ });
15598
+ }
15599
+ }
15600
+ onFileProgress?.(attachment.id, { status: "done" });
15601
+ } catch (error46) {
15602
+ onFileProgress?.(attachment.id, { status: "error" });
15603
+ throw error46;
15604
+ }
15605
+ }
15606
+ return contentParts;
15607
+ }
15608
+ function clearTransformationCache() {
15609
+ transformationCache.clear();
15610
+ }
15611
+
15306
15612
  // ../../node_modules/.bun/uuid@11.1.0/node_modules/uuid/dist/esm-browser/stringify.js
15307
15613
  var byteToHex = [];
15308
15614
  for (let i = 0; i < 256; ++i) {
@@ -15356,7 +15662,7 @@ function v4(options, buf, offset) {
15356
15662
  var v4_default = v4;
15357
15663
 
15358
15664
  // src/hooks/useFileUpload.tsx
15359
- import { jsx as jsx6 } from "react/jsx-runtime";
15665
+ import { jsx as jsx8 } from "react/jsx-runtime";
15360
15666
  async function generateImagePreview(file2) {
15361
15667
  if (!file2.type.startsWith("image/")) {
15362
15668
  return void 0;
@@ -15389,18 +15695,21 @@ function isTypeAccepted(mimeType, acceptedTypes) {
15389
15695
  function useFileUpload({
15390
15696
  config: config2,
15391
15697
  disabled = false,
15392
- resetDependency
15698
+ resetDependency,
15699
+ getCurrentChat
15393
15700
  }) {
15394
15701
  const strings = useStrings();
15395
15702
  const theme = useTheme();
15396
15703
  const [attachments, setAttachments] = useState2([]);
15397
15704
  const [isDragging, setIsDragging] = useState2(false);
15398
15705
  const [fileError, setFileError] = useState2(null);
15706
+ const [processingState, setProcessingState] = useState2(/* @__PURE__ */ new Map());
15399
15707
  const fileInputRef = useRef3(null);
15400
15708
  const dragCounterRef = useRef3(0);
15401
15709
  const enabled = config2 !== void 0;
15402
15710
  const maxFileSize = config2?.maxFileSize ?? DEFAULT_MAX_FILE_SIZE;
15403
15711
  const acceptedTypes = config2?.acceptedTypes;
15712
+ const transformers = config2?.transformers;
15404
15713
  useEffect3(() => {
15405
15714
  if (fileError) {
15406
15715
  const timer = setTimeout(() => setFileError(null), 3e3);
@@ -15410,7 +15719,28 @@ function useFileUpload({
15410
15719
  useEffect3(() => {
15411
15720
  setAttachments([]);
15412
15721
  setFileError(null);
15722
+ setProcessingState(/* @__PURE__ */ new Map());
15413
15723
  }, [resetDependency]);
15724
+ const runTransformer = useCallback2(async (attachmentId, file2, transformer) => {
15725
+ setProcessingState((prev) => new Map(prev).set(attachmentId, { status: "processing" }));
15726
+ try {
15727
+ const chat = await getCurrentChat();
15728
+ const context = { chat };
15729
+ const transformedContent = await getTransformedContent(file2, transformer, context, (progress) => {
15730
+ setProcessingState((prev) => new Map(prev).set(attachmentId, {
15731
+ status: "processing",
15732
+ progress
15733
+ }));
15734
+ });
15735
+ setAttachments((prev) => prev.map(
15736
+ (a) => a.id === attachmentId ? { ...a, transformedContent } : a
15737
+ ));
15738
+ setProcessingState((prev) => new Map(prev).set(attachmentId, { status: "done" }));
15739
+ } catch (error46) {
15740
+ console.error(`[useFileUpload] Transformation failed for ${file2.name}:`, error46);
15741
+ setProcessingState((prev) => new Map(prev).set(attachmentId, { status: "error" }));
15742
+ }
15743
+ }, [getCurrentChat]);
15414
15744
  const handleFiles = useCallback2(async (files) => {
15415
15745
  const fileArray = Array.from(files);
15416
15746
  for (const file2 of fileArray) {
@@ -15425,21 +15755,30 @@ function useFileUpload({
15425
15755
  continue;
15426
15756
  }
15427
15757
  const preview = await generateImagePreview(file2);
15428
- setAttachments((prev) => [
15429
- ...prev,
15430
- {
15431
- id: v4_default(),
15432
- file: file2,
15433
- preview
15434
- }
15435
- ]);
15758
+ const attachmentId = v4_default();
15759
+ const attachment = {
15760
+ id: attachmentId,
15761
+ file: file2,
15762
+ preview
15763
+ };
15764
+ setAttachments((prev) => [...prev, attachment]);
15765
+ const transformer = findTransformer(file2.type, transformers);
15766
+ if (transformer) {
15767
+ runTransformer(attachmentId, file2, transformer);
15768
+ }
15436
15769
  }
15437
- }, [maxFileSize, acceptedTypes, strings]);
15770
+ }, [maxFileSize, acceptedTypes, strings, transformers, runTransformer]);
15438
15771
  const removeAttachment = useCallback2((id) => {
15439
15772
  setAttachments((prev) => prev.filter((a) => a.id !== id));
15773
+ setProcessingState((prev) => {
15774
+ const next = new Map(prev);
15775
+ next.delete(id);
15776
+ return next;
15777
+ });
15440
15778
  }, []);
15441
15779
  const clearAttachments = useCallback2(() => {
15442
15780
  setAttachments([]);
15781
+ setProcessingState(/* @__PURE__ */ new Map());
15443
15782
  }, []);
15444
15783
  const openFilePicker = useCallback2(() => {
15445
15784
  fileInputRef.current?.click();
@@ -15492,7 +15831,7 @@ function useFileUpload({
15492
15831
  }), [handleDragEnter, handleDragOver, handleDragLeave, handleDrop]);
15493
15832
  const DropZoneOverlay = useMemo(() => {
15494
15833
  if (!isDragging || !enabled) return null;
15495
- return /* @__PURE__ */ jsx6(
15834
+ return /* @__PURE__ */ jsx8(
15496
15835
  "div",
15497
15836
  {
15498
15837
  style: {
@@ -15507,7 +15846,7 @@ function useFileUpload({
15507
15846
  zIndex: 1010,
15508
15847
  pointerEvents: "none"
15509
15848
  },
15510
- children: /* @__PURE__ */ jsx6(
15849
+ children: /* @__PURE__ */ jsx8(
15511
15850
  "div",
15512
15851
  {
15513
15852
  style: {
@@ -15516,7 +15855,7 @@ function useFileUpload({
15516
15855
  borderRadius: "12px",
15517
15856
  boxShadow: theme.buttonShadow
15518
15857
  },
15519
- children: /* @__PURE__ */ jsx6("span", { style: { color: theme.primaryColor, fontWeight: 600, fontSize: "16px" }, children: strings.fileUpload.dropFilesHere })
15858
+ children: /* @__PURE__ */ jsx8("span", { style: { color: theme.primaryColor, fontWeight: 600, fontSize: "16px" }, children: strings.fileUpload.dropFilesHere })
15520
15859
  }
15521
15860
  )
15522
15861
  }
@@ -15529,6 +15868,7 @@ function useFileUpload({
15529
15868
  enabled,
15530
15869
  maxFileSize,
15531
15870
  acceptedTypes,
15871
+ processingState,
15532
15872
  fileInputRef,
15533
15873
  handleFiles,
15534
15874
  removeAttachment,
@@ -15546,7 +15886,7 @@ function useFileUpload({
15546
15886
 
15547
15887
  // src/hooks/useDropdownState.tsx
15548
15888
  import { useState as useState3, useCallback as useCallback3, useMemo as useMemo2 } from "react";
15549
- import { jsx as jsx7 } from "react/jsx-runtime";
15889
+ import { jsx as jsx9 } from "react/jsx-runtime";
15550
15890
  function useDropdownState(options = {}) {
15551
15891
  const { backdropZIndex = 1002, initialOpen = false } = options;
15552
15892
  const [isOpen, setIsOpen] = useState3(initialOpen);
@@ -15561,7 +15901,7 @@ function useDropdownState(options = {}) {
15561
15901
  }, []);
15562
15902
  const Backdrop = useMemo2(() => {
15563
15903
  if (!isOpen) return null;
15564
- return /* @__PURE__ */ jsx7(
15904
+ return /* @__PURE__ */ jsx9(
15565
15905
  "div",
15566
15906
  {
15567
15907
  onClick: close,
@@ -15586,7 +15926,7 @@ function useDropdownState(options = {}) {
15586
15926
  }
15587
15927
 
15588
15928
  // src/components/UseAIChatPanel.tsx
15589
- import { Fragment, jsx as jsx8, jsxs as jsxs5 } from "react/jsx-runtime";
15929
+ import { Fragment, jsx as jsx10, jsxs as jsxs7 } from "react/jsx-runtime";
15590
15930
  function getTextContent(content3) {
15591
15931
  if (typeof content3 === "string") {
15592
15932
  return content3;
@@ -15607,6 +15947,7 @@ function UseAIChatPanel({
15607
15947
  onLoadChat,
15608
15948
  onDeleteChat,
15609
15949
  onListChats,
15950
+ onGetChat,
15610
15951
  suggestions,
15611
15952
  availableAgents,
15612
15953
  defaultAgent,
@@ -15627,12 +15968,14 @@ function UseAIChatPanel({
15627
15968
  const [chatHistory, setChatHistory] = useState4([]);
15628
15969
  const messagesEndRef = useRef4(null);
15629
15970
  const [displayedSuggestions, setDisplayedSuggestions] = useState4([]);
15971
+ const textareaRef = useRef4(null);
15630
15972
  const [hoveredMessageId, setHoveredMessageId] = useState4(null);
15631
15973
  const {
15632
15974
  attachments,
15633
15975
  fileError,
15634
15976
  enabled: fileUploadEnabled,
15635
15977
  acceptedTypes,
15978
+ processingState: fileProcessingState,
15636
15979
  fileInputRef,
15637
15980
  removeAttachment,
15638
15981
  clearAttachments,
@@ -15641,6 +15984,7 @@ function UseAIChatPanel({
15641
15984
  getDropZoneProps,
15642
15985
  DropZoneOverlay
15643
15986
  } = useFileUpload({
15987
+ getCurrentChat: onGetChat ?? (async () => null),
15644
15988
  config: fileUploadConfig,
15645
15989
  disabled: loading,
15646
15990
  resetDependency: currentChatId
@@ -15655,6 +15999,14 @@ function UseAIChatPanel({
15655
15999
  useEffect4(() => {
15656
16000
  messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
15657
16001
  }, [messages]);
16002
+ const maxTextareaHeight = 160;
16003
+ useEffect4(() => {
16004
+ const textarea = textareaRef.current;
16005
+ if (!textarea) return;
16006
+ textarea.style.height = "auto";
16007
+ const newHeight = Math.min(textarea.scrollHeight, maxTextareaHeight);
16008
+ textarea.style.height = `${newHeight}px`;
16009
+ }, [input]);
15658
16010
  useEffect4(() => {
15659
16011
  if (!suggestions || suggestions.length === 0) {
15660
16012
  setDisplayedSuggestions([]);
@@ -15704,7 +16056,7 @@ function UseAIChatPanel({
15704
16056
  chatHistoryDropdown.close();
15705
16057
  }
15706
16058
  };
15707
- return /* @__PURE__ */ jsxs5(
16059
+ return /* @__PURE__ */ jsxs7(
15708
16060
  "div",
15709
16061
  {
15710
16062
  onClick: () => {
@@ -15722,7 +16074,7 @@ function UseAIChatPanel({
15722
16074
  },
15723
16075
  children: [
15724
16076
  DropZoneOverlay,
15725
- /* @__PURE__ */ jsxs5(
16077
+ /* @__PURE__ */ jsxs7(
15726
16078
  "div",
15727
16079
  {
15728
16080
  style: {
@@ -15735,7 +16087,7 @@ function UseAIChatPanel({
15735
16087
  gap: "12px"
15736
16088
  },
15737
16089
  children: [
15738
- /* @__PURE__ */ jsx8("div", { style: { flex: 1, minWidth: 0, position: "relative" }, children: onListChats ? /* @__PURE__ */ jsxs5(
16090
+ /* @__PURE__ */ jsx10("div", { style: { flex: 1, minWidth: 0, position: "relative" }, children: onListChats ? /* @__PURE__ */ jsxs7(
15739
16091
  "button",
15740
16092
  {
15741
16093
  "data-testid": "chat-history-dropdown-button",
@@ -15768,7 +16120,7 @@ function UseAIChatPanel({
15768
16120
  e.currentTarget.style.background = "transparent";
15769
16121
  },
15770
16122
  children: [
15771
- /* @__PURE__ */ jsx8("span", { style: {
16123
+ /* @__PURE__ */ jsx10("span", { style: {
15772
16124
  overflow: "hidden",
15773
16125
  textOverflow: "ellipsis",
15774
16126
  whiteSpace: "nowrap",
@@ -15785,13 +16137,13 @@ function UseAIChatPanel({
15785
16137
  }
15786
16138
  return strings.header.newChat;
15787
16139
  })() }),
15788
- /* @__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" }) })
16140
+ /* @__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" }) })
15789
16141
  ]
15790
16142
  }
15791
- ) : /* @__PURE__ */ jsx8("div", { style: { fontSize: "14px", fontWeight: "600", color: theme.textColor, padding: "6px 8px" }, children: strings.header.aiAssistant }) }),
15792
- /* @__PURE__ */ jsxs5("div", { style: { display: "flex", alignItems: "center", gap: "4px" }, children: [
15793
- availableAgents && availableAgents.length > 1 && onAgentChange && /* @__PURE__ */ jsxs5("div", { style: { position: "relative" }, children: [
15794
- /* @__PURE__ */ jsxs5(
16143
+ ) : /* @__PURE__ */ jsx10("div", { style: { fontSize: "14px", fontWeight: "600", color: theme.textColor, padding: "6px 8px" }, children: strings.header.aiAssistant }) }),
16144
+ /* @__PURE__ */ jsxs7("div", { style: { display: "flex", alignItems: "center", gap: "4px" }, children: [
16145
+ availableAgents && availableAgents.length > 1 && onAgentChange && /* @__PURE__ */ jsxs7("div", { style: { position: "relative" }, children: [
16146
+ /* @__PURE__ */ jsxs7(
15795
16147
  "button",
15796
16148
  {
15797
16149
  "data-testid": "agent-selector",
@@ -15820,7 +16172,7 @@ function UseAIChatPanel({
15820
16172
  },
15821
16173
  title: "Select AI model",
15822
16174
  children: [
15823
- /* @__PURE__ */ jsx8("span", { style: {
16175
+ /* @__PURE__ */ jsx10("span", { style: {
15824
16176
  overflow: "hidden",
15825
16177
  textOverflow: "ellipsis",
15826
16178
  whiteSpace: "nowrap",
@@ -15829,11 +16181,11 @@ function UseAIChatPanel({
15829
16181
  const agent = availableAgents.find((a) => a.id === (selectedAgent ?? defaultAgent));
15830
16182
  return agent?.name || "AI";
15831
16183
  })() }),
15832
- /* @__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" }) })
16184
+ /* @__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" }) })
15833
16185
  ]
15834
16186
  }
15835
16187
  ),
15836
- agentDropdown.isOpen && /* @__PURE__ */ jsx8(
16188
+ agentDropdown.isOpen && /* @__PURE__ */ jsx10(
15837
16189
  "div",
15838
16190
  {
15839
16191
  style: {
@@ -15853,7 +16205,7 @@ function UseAIChatPanel({
15853
16205
  },
15854
16206
  children: availableAgents.map((agent) => {
15855
16207
  const isSelected = agent.id === (selectedAgent ?? defaultAgent);
15856
- return /* @__PURE__ */ jsxs5(
16208
+ return /* @__PURE__ */ jsxs7(
15857
16209
  "div",
15858
16210
  {
15859
16211
  "data-testid": "agent-option",
@@ -15883,13 +16235,13 @@ function UseAIChatPanel({
15883
16235
  }
15884
16236
  },
15885
16237
  children: [
15886
- /* @__PURE__ */ jsxs5("div", { style: { flex: 1, minWidth: 0 }, children: [
15887
- /* @__PURE__ */ jsx8("div", { style: {
16238
+ /* @__PURE__ */ jsxs7("div", { style: { flex: 1, minWidth: 0 }, children: [
16239
+ /* @__PURE__ */ jsx10("div", { style: {
15888
16240
  fontSize: "13px",
15889
16241
  fontWeight: isSelected ? "600" : "500",
15890
16242
  color: isSelected ? theme.primaryColor : theme.textColor
15891
16243
  }, children: agent.name }),
15892
- agent.annotation && /* @__PURE__ */ jsx8("div", { style: {
16244
+ agent.annotation && /* @__PURE__ */ jsx10("div", { style: {
15893
16245
  fontSize: "11px",
15894
16246
  color: theme.secondaryTextColor,
15895
16247
  marginTop: "2px",
@@ -15898,7 +16250,7 @@ function UseAIChatPanel({
15898
16250
  whiteSpace: "nowrap"
15899
16251
  }, children: agent.annotation })
15900
16252
  ] }),
15901
- 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" }) })
16253
+ 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" }) })
15902
16254
  ]
15903
16255
  },
15904
16256
  agent.id
@@ -15907,7 +16259,7 @@ function UseAIChatPanel({
15907
16259
  }
15908
16260
  )
15909
16261
  ] }),
15910
- onNewChat && /* @__PURE__ */ jsx8(
16262
+ onNewChat && /* @__PURE__ */ jsx10(
15911
16263
  "button",
15912
16264
  {
15913
16265
  "data-testid": "new-chat-button",
@@ -15934,10 +16286,10 @@ function UseAIChatPanel({
15934
16286
  e.currentTarget.style.color = theme.secondaryTextColor;
15935
16287
  },
15936
16288
  title: strings.header.newChat,
15937
- 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" }) })
16289
+ 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" }) })
15938
16290
  }
15939
16291
  ),
15940
- onDeleteChat && messages.length > 0 && /* @__PURE__ */ jsx8(
16292
+ onDeleteChat && messages.length > 0 && /* @__PURE__ */ jsx10(
15941
16293
  "button",
15942
16294
  {
15943
16295
  "data-testid": "delete-chat-button",
@@ -15961,7 +16313,7 @@ function UseAIChatPanel({
15961
16313
  e.currentTarget.style.color = theme.secondaryTextColor;
15962
16314
  },
15963
16315
  title: strings.header.deleteChat,
15964
- 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" }) })
16316
+ 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" }) })
15965
16317
  }
15966
16318
  ),
15967
16319
  closeButton
@@ -15969,7 +16321,7 @@ function UseAIChatPanel({
15969
16321
  ]
15970
16322
  }
15971
16323
  ),
15972
- chatHistoryDropdown.isOpen && onListChats && /* @__PURE__ */ jsx8(
16324
+ chatHistoryDropdown.isOpen && onListChats && /* @__PURE__ */ jsx10(
15973
16325
  "div",
15974
16326
  {
15975
16327
  style: {
@@ -15986,7 +16338,7 @@ function UseAIChatPanel({
15986
16338
  flexDirection: "column",
15987
16339
  overflow: "hidden"
15988
16340
  },
15989
- children: /* @__PURE__ */ jsx8(
16341
+ children: /* @__PURE__ */ jsx10(
15990
16342
  "div",
15991
16343
  {
15992
16344
  style: {
@@ -15994,7 +16346,7 @@ function UseAIChatPanel({
15994
16346
  overflowY: "auto",
15995
16347
  padding: "8px"
15996
16348
  },
15997
- children: chatHistory.length === 0 ? /* @__PURE__ */ jsx8(
16349
+ children: chatHistory.length === 0 ? /* @__PURE__ */ jsx10(
15998
16350
  "div",
15999
16351
  {
16000
16352
  style: {
@@ -16003,9 +16355,9 @@ function UseAIChatPanel({
16003
16355
  padding: "32px 16px",
16004
16356
  fontSize: "13px"
16005
16357
  },
16006
- children: /* @__PURE__ */ jsx8("p", { style: { margin: 0 }, children: strings.chatHistory.noChatHistory })
16358
+ children: /* @__PURE__ */ jsx10("p", { style: { margin: 0 }, children: strings.chatHistory.noChatHistory })
16007
16359
  }
16008
- ) : chatHistory.map((chat) => /* @__PURE__ */ jsxs5(
16360
+ ) : chatHistory.map((chat) => /* @__PURE__ */ jsxs7(
16009
16361
  "div",
16010
16362
  {
16011
16363
  "data-testid": "chat-history-item",
@@ -16029,10 +16381,10 @@ function UseAIChatPanel({
16029
16381
  }
16030
16382
  },
16031
16383
  children: [
16032
- /* @__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 }),
16033
- /* @__PURE__ */ jsxs5("div", { style: { fontSize: "11px", color: theme.secondaryTextColor }, children: [
16384
+ /* @__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 }),
16385
+ /* @__PURE__ */ jsxs7("div", { style: { fontSize: "11px", color: theme.secondaryTextColor }, children: [
16034
16386
  new Date(chat.updatedAt).toLocaleDateString([], { month: "short", day: "numeric" }),
16035
- currentChatId === chat.id && /* @__PURE__ */ jsxs5("span", { style: {
16387
+ currentChatId === chat.id && /* @__PURE__ */ jsxs7("span", { style: {
16036
16388
  marginLeft: "8px",
16037
16389
  color: theme.primaryColor,
16038
16390
  fontWeight: "600"
@@ -16051,7 +16403,7 @@ function UseAIChatPanel({
16051
16403
  ),
16052
16404
  chatHistoryDropdown.Backdrop,
16053
16405
  agentDropdown.Backdrop,
16054
- /* @__PURE__ */ jsxs5(
16406
+ /* @__PURE__ */ jsxs7(
16055
16407
  "div",
16056
16408
  {
16057
16409
  style: {
@@ -16063,7 +16415,7 @@ function UseAIChatPanel({
16063
16415
  gap: "12px"
16064
16416
  },
16065
16417
  children: [
16066
- messages.length === 0 && /* @__PURE__ */ jsxs5(
16418
+ messages.length === 0 && /* @__PURE__ */ jsxs7(
16067
16419
  "div",
16068
16420
  {
16069
16421
  style: {
@@ -16074,12 +16426,12 @@ function UseAIChatPanel({
16074
16426
  gap: "20px"
16075
16427
  },
16076
16428
  children: [
16077
- /* @__PURE__ */ jsxs5("div", { style: { textAlign: "center", color: theme.secondaryTextColor, fontSize: "14px" }, children: [
16078
- /* @__PURE__ */ jsx8("p", { style: { margin: 0, fontSize: "32px", marginBottom: "12px" }, children: "\u{1F4AC}" }),
16079
- /* @__PURE__ */ jsx8("p", { style: { margin: 0 }, children: strings.emptyChat.startConversation }),
16080
- /* @__PURE__ */ jsx8("p", { style: { margin: "8px 0 0", fontSize: "12px" }, children: strings.emptyChat.askMeToHelp })
16429
+ /* @__PURE__ */ jsxs7("div", { style: { textAlign: "center", color: theme.secondaryTextColor, fontSize: "14px" }, children: [
16430
+ /* @__PURE__ */ jsx10("p", { style: { margin: 0, fontSize: "32px", marginBottom: "12px" }, children: "\u{1F4AC}" }),
16431
+ /* @__PURE__ */ jsx10("p", { style: { margin: 0 }, children: strings.emptyChat.startConversation }),
16432
+ /* @__PURE__ */ jsx10("p", { style: { margin: "8px 0 0", fontSize: "12px" }, children: strings.emptyChat.askMeToHelp })
16081
16433
  ] }),
16082
- displayedSuggestions.length > 0 && /* @__PURE__ */ jsx8(
16434
+ displayedSuggestions.length > 0 && /* @__PURE__ */ jsx10(
16083
16435
  "div",
16084
16436
  {
16085
16437
  style: {
@@ -16089,7 +16441,7 @@ function UseAIChatPanel({
16089
16441
  width: "100%",
16090
16442
  maxWidth: "320px"
16091
16443
  },
16092
- children: displayedSuggestions.map((suggestion, index2) => /* @__PURE__ */ jsx8(
16444
+ children: displayedSuggestions.map((suggestion, index2) => /* @__PURE__ */ jsx10(
16093
16445
  "button",
16094
16446
  {
16095
16447
  "data-testid": "chat-suggestion-button",
@@ -16133,7 +16485,7 @@ function UseAIChatPanel({
16133
16485
  ]
16134
16486
  }
16135
16487
  ),
16136
- messages.map((message) => /* @__PURE__ */ jsxs5(
16488
+ messages.map((message) => /* @__PURE__ */ jsxs7(
16137
16489
  "div",
16138
16490
  {
16139
16491
  "data-testid": `chat-message-${message.role}`,
@@ -16146,7 +16498,7 @@ function UseAIChatPanel({
16146
16498
  onMouseEnter: () => message.role === "user" && setHoveredMessageId(message.id),
16147
16499
  onMouseLeave: () => setHoveredMessageId(null),
16148
16500
  children: [
16149
- /* @__PURE__ */ jsxs5(
16501
+ /* @__PURE__ */ jsxs7(
16150
16502
  "div",
16151
16503
  {
16152
16504
  style: {
@@ -16154,7 +16506,7 @@ function UseAIChatPanel({
16154
16506
  maxWidth: "80%"
16155
16507
  },
16156
16508
  children: [
16157
- message.role === "user" && hoveredMessageId === message.id && onSaveCommand && !slashCommands.isSavingCommand(message.id) && /* @__PURE__ */ jsx8(
16509
+ message.role === "user" && hoveredMessageId === message.id && onSaveCommand && !slashCommands.isSavingCommand(message.id) && /* @__PURE__ */ jsx10(
16158
16510
  "button",
16159
16511
  {
16160
16512
  "data-testid": "save-command-button",
@@ -16190,14 +16542,14 @@ function UseAIChatPanel({
16190
16542
  e.currentTarget.style.transform = "scale(1)";
16191
16543
  e.currentTarget.style.boxShadow = "0 2px 6px rgba(0, 0, 0, 0.15)";
16192
16544
  },
16193
- children: /* @__PURE__ */ jsxs5("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
16194
- /* @__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" }),
16195
- /* @__PURE__ */ jsx8("polyline", { points: "17 21 17 13 7 13 7 21" }),
16196
- /* @__PURE__ */ jsx8("polyline", { points: "7 3 7 8 15 8" })
16545
+ children: /* @__PURE__ */ jsxs7("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
16546
+ /* @__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" }),
16547
+ /* @__PURE__ */ jsx10("polyline", { points: "17 21 17 13 7 13 7 21" }),
16548
+ /* @__PURE__ */ jsx10("polyline", { points: "7 3 7 8 15 8" })
16197
16549
  ] })
16198
16550
  }
16199
16551
  ),
16200
- /* @__PURE__ */ jsxs5(
16552
+ /* @__PURE__ */ jsxs7(
16201
16553
  "div",
16202
16554
  {
16203
16555
  "data-testid": "chat-message-content",
@@ -16212,7 +16564,7 @@ function UseAIChatPanel({
16212
16564
  wordWrap: "break-word"
16213
16565
  },
16214
16566
  children: [
16215
- 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(
16567
+ 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(
16216
16568
  FilePlaceholder,
16217
16569
  {
16218
16570
  name: part.file.name,
@@ -16220,7 +16572,7 @@ function UseAIChatPanel({
16220
16572
  },
16221
16573
  idx
16222
16574
  )) }),
16223
- message.role === "assistant" ? /* @__PURE__ */ jsx8(MarkdownContent, { content: getTextContent(message.content) }) : getTextContent(message.content)
16575
+ message.role === "assistant" ? /* @__PURE__ */ jsx10(MarkdownContent, { content: getTextContent(message.content) }) : getTextContent(message.content)
16224
16576
  ]
16225
16577
  }
16226
16578
  ),
@@ -16231,7 +16583,7 @@ function UseAIChatPanel({
16231
16583
  ]
16232
16584
  }
16233
16585
  ),
16234
- /* @__PURE__ */ jsx8(
16586
+ /* @__PURE__ */ jsx10(
16235
16587
  "div",
16236
16588
  {
16237
16589
  style: {
@@ -16250,14 +16602,14 @@ function UseAIChatPanel({
16250
16602
  },
16251
16603
  message.id
16252
16604
  )),
16253
- loading && /* @__PURE__ */ jsx8(
16605
+ loading && /* @__PURE__ */ jsx10(
16254
16606
  "div",
16255
16607
  {
16256
16608
  style: {
16257
16609
  display: "flex",
16258
16610
  alignItems: "flex-start"
16259
16611
  },
16260
- children: /* @__PURE__ */ jsx8(
16612
+ children: /* @__PURE__ */ jsx10(
16261
16613
  "div",
16262
16614
  {
16263
16615
  className: "markdown-content",
@@ -16270,19 +16622,19 @@ function UseAIChatPanel({
16270
16622
  color: theme.textColor,
16271
16623
  maxWidth: "80%"
16272
16624
  },
16273
- children: streamingText ? /* @__PURE__ */ jsx8(MarkdownContent, { content: streamingText }) : /* @__PURE__ */ jsxs5(Fragment, { children: [
16274
- /* @__PURE__ */ jsx8("span", { style: { opacity: 0.6 }, children: strings.input.thinking }),
16275
- /* @__PURE__ */ jsx8("span", { className: "dots", style: { marginLeft: "4px" }, children: "..." })
16625
+ children: streamingText ? /* @__PURE__ */ jsx10(MarkdownContent, { content: streamingText }) : /* @__PURE__ */ jsxs7(Fragment, { children: [
16626
+ /* @__PURE__ */ jsx10("span", { style: { opacity: 0.6 }, children: strings.input.thinking }),
16627
+ /* @__PURE__ */ jsx10("span", { className: "dots", style: { marginLeft: "4px" }, children: "..." })
16276
16628
  ] })
16277
16629
  }
16278
16630
  )
16279
16631
  }
16280
16632
  ),
16281
- /* @__PURE__ */ jsx8("div", { ref: messagesEndRef })
16633
+ /* @__PURE__ */ jsx10("div", { ref: messagesEndRef })
16282
16634
  ]
16283
16635
  }
16284
16636
  ),
16285
- /* @__PURE__ */ jsxs5(
16637
+ /* @__PURE__ */ jsxs7(
16286
16638
  "div",
16287
16639
  {
16288
16640
  style: {
@@ -16290,7 +16642,7 @@ function UseAIChatPanel({
16290
16642
  borderTop: `1px solid ${theme.borderColor}`
16291
16643
  },
16292
16644
  children: [
16293
- fileError && /* @__PURE__ */ jsx8(
16645
+ fileError && /* @__PURE__ */ jsx10(
16294
16646
  "div",
16295
16647
  {
16296
16648
  "data-testid": "file-error",
@@ -16305,7 +16657,7 @@ function UseAIChatPanel({
16305
16657
  children: fileError
16306
16658
  }
16307
16659
  ),
16308
- attachments.length > 0 && /* @__PURE__ */ jsx8(
16660
+ attachments.length > 0 && /* @__PURE__ */ jsx10(
16309
16661
  "div",
16310
16662
  {
16311
16663
  "data-testid": "file-attachments",
@@ -16315,138 +16667,147 @@ function UseAIChatPanel({
16315
16667
  gap: "8px",
16316
16668
  marginBottom: "8px"
16317
16669
  },
16318
- children: attachments.map((attachment) => /* @__PURE__ */ jsx8(
16670
+ children: attachments.map((attachment) => /* @__PURE__ */ jsx10(
16319
16671
  FileChip,
16320
16672
  {
16321
16673
  attachment,
16322
16674
  onRemove: () => removeAttachment(attachment.id),
16323
- disabled: loading
16675
+ disabled: loading,
16676
+ processingState: fileProcessingState.get(attachment.id)
16324
16677
  },
16325
16678
  attachment.id
16326
16679
  ))
16327
16680
  }
16328
16681
  ),
16329
- /* @__PURE__ */ jsxs5(
16682
+ /* @__PURE__ */ jsxs7(
16330
16683
  "div",
16331
16684
  {
16332
16685
  style: {
16333
- display: "flex",
16334
- gap: "8px",
16686
+ border: `1px solid ${theme.borderColor}`,
16687
+ borderRadius: "12px",
16688
+ background: theme.backgroundColor,
16689
+ overflow: "hidden",
16335
16690
  position: "relative"
16336
16691
  },
16337
16692
  children: [
16338
16693
  slashCommands.AutocompleteComponent,
16339
- /* @__PURE__ */ jsx8(
16694
+ /* @__PURE__ */ jsx10(
16340
16695
  "input",
16341
16696
  {
16342
16697
  ref: fileInputRef,
16343
16698
  type: "file",
16344
16699
  multiple: true,
16700
+ "data-testid": "file-input",
16345
16701
  style: { display: "none" },
16346
16702
  onChange: handleFileInputChange,
16347
16703
  accept: acceptedTypes?.join(",")
16348
16704
  }
16349
16705
  ),
16350
- /* @__PURE__ */ jsxs5(
16706
+ /* @__PURE__ */ jsx10(
16707
+ "textarea",
16708
+ {
16709
+ ref: textareaRef,
16710
+ "data-testid": "chat-input",
16711
+ className: "chat-input",
16712
+ value: input,
16713
+ onChange: handleInputChange,
16714
+ onKeyDown: handleKeyDown,
16715
+ placeholder: connected ? strings.input.placeholder : strings.input.connectingPlaceholder,
16716
+ disabled: !connected || loading,
16717
+ rows: 1,
16718
+ style: {
16719
+ width: "100%",
16720
+ padding: "10px 14px 6px",
16721
+ border: "none",
16722
+ fontSize: "14px",
16723
+ lineHeight: "1.4",
16724
+ resize: "none",
16725
+ maxHeight: `${maxTextareaHeight}px`,
16726
+ fontFamily: "inherit",
16727
+ outline: "none",
16728
+ background: "transparent",
16729
+ overflowY: "auto",
16730
+ boxSizing: "border-box"
16731
+ }
16732
+ }
16733
+ ),
16734
+ /* @__PURE__ */ jsxs7(
16351
16735
  "div",
16352
16736
  {
16353
16737
  style: {
16354
- flex: 1,
16355
16738
  display: "flex",
16356
16739
  alignItems: "center",
16357
- border: `1px solid ${theme.borderColor}`,
16358
- borderRadius: "8px",
16359
- background: theme.backgroundColor,
16360
- overflow: "hidden"
16740
+ justifyContent: "space-between",
16741
+ padding: "4px 8px"
16361
16742
  },
16362
16743
  children: [
16363
- /* @__PURE__ */ jsx8(
16364
- "textarea",
16365
- {
16366
- "data-testid": "chat-input",
16367
- className: "chat-input",
16368
- value: input,
16369
- onChange: handleInputChange,
16370
- onKeyDown: handleKeyDown,
16371
- placeholder: connected ? strings.input.placeholder : strings.input.connectingPlaceholder,
16372
- disabled: !connected || loading,
16373
- style: {
16374
- flex: 1,
16375
- padding: "10px 12px",
16376
- border: "none",
16377
- fontSize: "14px",
16378
- resize: "none",
16379
- minHeight: "44px",
16380
- maxHeight: "120px",
16381
- fontFamily: "inherit",
16382
- outline: "none",
16383
- background: "transparent"
16384
- },
16385
- rows: 1
16386
- }
16387
- ),
16388
- fileUploadEnabled && /* @__PURE__ */ jsx8(
16744
+ /* @__PURE__ */ jsx10("div", { style: { display: "flex", alignItems: "center", gap: "8px" }, children: fileUploadEnabled && /* @__PURE__ */ jsx10(
16389
16745
  "button",
16390
16746
  {
16391
16747
  "data-testid": "file-picker-button",
16392
16748
  onClick: openFilePicker,
16393
16749
  disabled: !connected || loading,
16394
16750
  style: {
16395
- padding: "6px",
16396
- marginRight: "6px",
16751
+ padding: "4px",
16397
16752
  background: "transparent",
16398
- border: "none",
16399
- borderRadius: "6px",
16753
+ border: `1px solid ${theme.borderColor}`,
16754
+ borderRadius: "50%",
16400
16755
  cursor: connected && !loading ? "pointer" : "not-allowed",
16401
16756
  color: theme.secondaryTextColor,
16402
16757
  display: "flex",
16403
16758
  alignItems: "center",
16404
16759
  justifyContent: "center",
16760
+ width: "28px",
16761
+ height: "28px",
16405
16762
  transition: "all 0.15s",
16406
16763
  opacity: connected && !loading ? 1 : 0.5
16407
16764
  },
16408
16765
  onMouseEnter: (e) => {
16409
16766
  if (connected && !loading) {
16410
16767
  e.currentTarget.style.color = theme.primaryColor;
16411
- e.currentTarget.style.background = theme.activeBackground;
16768
+ e.currentTarget.style.borderColor = theme.primaryColor;
16412
16769
  }
16413
16770
  },
16414
16771
  onMouseLeave: (e) => {
16415
16772
  e.currentTarget.style.color = theme.secondaryTextColor;
16416
- e.currentTarget.style.background = "transparent";
16773
+ e.currentTarget.style.borderColor = theme.borderColor;
16417
16774
  },
16418
16775
  title: strings.fileUpload.attachFiles,
16419
- children: /* @__PURE__ */ jsxs5("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
16420
- /* @__PURE__ */ jsx8("circle", { cx: "12", cy: "12", r: "10" }),
16421
- /* @__PURE__ */ jsx8("line", { x1: "12", y1: "8", x2: "12", y2: "16" }),
16422
- /* @__PURE__ */ jsx8("line", { x1: "8", y1: "12", x2: "16", y2: "12" })
16776
+ 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: [
16777
+ /* @__PURE__ */ jsx10("line", { x1: "12", y1: "5", x2: "12", y2: "19" }),
16778
+ /* @__PURE__ */ jsx10("line", { x1: "5", y1: "12", x2: "19", y2: "12" })
16779
+ ] })
16780
+ }
16781
+ ) }),
16782
+ /* @__PURE__ */ jsx10(
16783
+ "button",
16784
+ {
16785
+ "data-testid": "chat-send-button",
16786
+ className: "chat-send-button",
16787
+ onClick: handleSend,
16788
+ disabled: !connected || loading || !input.trim() && attachments.length === 0,
16789
+ style: {
16790
+ padding: "6px",
16791
+ background: connected && !loading && (input.trim() || attachments.length > 0) ? theme.primaryGradient : theme.buttonDisabledBackground,
16792
+ color: connected && !loading && (input.trim() || attachments.length > 0) ? "white" : theme.secondaryTextColor,
16793
+ border: "none",
16794
+ borderRadius: "50%",
16795
+ cursor: connected && !loading && (input.trim() || attachments.length > 0) ? "pointer" : "not-allowed",
16796
+ display: "flex",
16797
+ alignItems: "center",
16798
+ justifyContent: "center",
16799
+ width: "32px",
16800
+ height: "32px",
16801
+ transition: "all 0.2s"
16802
+ },
16803
+ 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: [
16804
+ /* @__PURE__ */ jsx10("line", { x1: "12", y1: "19", x2: "12", y2: "5" }),
16805
+ /* @__PURE__ */ jsx10("polyline", { points: "5 12 12 5 19 12" })
16423
16806
  ] })
16424
16807
  }
16425
16808
  )
16426
16809
  ]
16427
16810
  }
16428
- ),
16429
- /* @__PURE__ */ jsx8(
16430
- "button",
16431
- {
16432
- "data-testid": "chat-send-button",
16433
- className: "chat-send-button",
16434
- onClick: handleSend,
16435
- disabled: !connected || loading || !input.trim() && attachments.length === 0,
16436
- style: {
16437
- padding: "10px 16px",
16438
- background: connected && !loading && (input.trim() || attachments.length > 0) ? theme.primaryGradient : theme.buttonDisabledBackground,
16439
- color: connected && !loading && (input.trim() || attachments.length > 0) ? "white" : theme.secondaryTextColor,
16440
- border: "none",
16441
- borderRadius: "8px",
16442
- cursor: connected && !loading && (input.trim() || attachments.length > 0) ? "pointer" : "not-allowed",
16443
- fontSize: "14px",
16444
- fontWeight: "600",
16445
- minWidth: "60px",
16446
- transition: "all 0.2s"
16447
- },
16448
- children: strings.input.send
16449
- }
16450
16811
  )
16451
16812
  ]
16452
16813
  }
@@ -16454,7 +16815,7 @@ function UseAIChatPanel({
16454
16815
  ]
16455
16816
  }
16456
16817
  ),
16457
- /* @__PURE__ */ jsx8("style", { children: `
16818
+ /* @__PURE__ */ jsx10("style", { children: `
16458
16819
  /* Markdown content styles */
16459
16820
  .markdown-content > :first-child {
16460
16821
  margin-top: 0 !important;
@@ -16479,7 +16840,7 @@ function UseAIChatPanel({
16479
16840
  }
16480
16841
 
16481
16842
  // src/components/UseAIFloatingChatWrapper.tsx
16482
- import { Fragment as Fragment2, jsx as jsx9, jsxs as jsxs6 } from "react/jsx-runtime";
16843
+ import { Fragment as Fragment2, jsx as jsx11, jsxs as jsxs8 } from "react/jsx-runtime";
16483
16844
  function UseAIFloatingChatWrapper({
16484
16845
  isOpen,
16485
16846
  onClose,
@@ -16487,8 +16848,8 @@ function UseAIFloatingChatWrapper({
16487
16848
  }) {
16488
16849
  const theme = useTheme();
16489
16850
  if (!isOpen) return null;
16490
- return /* @__PURE__ */ jsxs6(Fragment2, { children: [
16491
- /* @__PURE__ */ jsx9(
16851
+ return /* @__PURE__ */ jsxs8(Fragment2, { children: [
16852
+ /* @__PURE__ */ jsx11(
16492
16853
  "div",
16493
16854
  {
16494
16855
  style: {
@@ -16504,7 +16865,7 @@ function UseAIFloatingChatWrapper({
16504
16865
  onClick: onClose
16505
16866
  }
16506
16867
  ),
16507
- /* @__PURE__ */ jsx9(
16868
+ /* @__PURE__ */ jsx11(
16508
16869
  "div",
16509
16870
  {
16510
16871
  style: {
@@ -16523,7 +16884,7 @@ function UseAIFloatingChatWrapper({
16523
16884
  children
16524
16885
  }
16525
16886
  ),
16526
- /* @__PURE__ */ jsx9("style", { children: `
16887
+ /* @__PURE__ */ jsx11("style", { children: `
16527
16888
  @keyframes fadeIn {
16528
16889
  from { opacity: 0; }
16529
16890
  to { opacity: 1; }
@@ -16543,7 +16904,7 @@ function UseAIFloatingChatWrapper({
16543
16904
  }
16544
16905
  function CloseButton({ onClick }) {
16545
16906
  const theme = useTheme();
16546
- return /* @__PURE__ */ jsx9(
16907
+ return /* @__PURE__ */ jsx11(
16547
16908
  "button",
16548
16909
  {
16549
16910
  "data-testid": "chat-close-button",
@@ -16578,7 +16939,7 @@ function CloseButton({ onClick }) {
16578
16939
 
16579
16940
  // src/components/UseAIChat.tsx
16580
16941
  import { createContext as createContext3, useContext as useContext3 } from "react";
16581
- import { jsx as jsx10 } from "react/jsx-runtime";
16942
+ import { jsx as jsx12 } from "react/jsx-runtime";
16582
16943
  var __UseAIChatContext = createContext3(null);
16583
16944
  function useChatUIContext() {
16584
16945
  const context = useContext3(__UseAIChatContext);
@@ -16602,6 +16963,7 @@ function UseAIChat({ floating = false }) {
16602
16963
  onLoadChat: ctx.history.load,
16603
16964
  onDeleteChat: ctx.history.delete,
16604
16965
  onListChats: ctx.history.list,
16966
+ onGetChat: ctx.history.get,
16605
16967
  suggestions: ctx.suggestions,
16606
16968
  availableAgents: ctx.agents.available,
16607
16969
  defaultAgent: ctx.agents.default,
@@ -16614,22 +16976,22 @@ function UseAIChat({ floating = false }) {
16614
16976
  onDeleteCommand: ctx.commands.delete
16615
16977
  };
16616
16978
  if (floating) {
16617
- return /* @__PURE__ */ jsx10(
16979
+ return /* @__PURE__ */ jsx12(
16618
16980
  UseAIFloatingChatWrapper,
16619
16981
  {
16620
16982
  isOpen: ctx.ui.isOpen,
16621
16983
  onClose: () => ctx.ui.setOpen(false),
16622
- children: /* @__PURE__ */ jsx10(
16984
+ children: /* @__PURE__ */ jsx12(
16623
16985
  UseAIChatPanel,
16624
16986
  {
16625
16987
  ...chatPanelProps,
16626
- closeButton: /* @__PURE__ */ jsx10(CloseButton, { onClick: () => ctx.ui.setOpen(false) })
16988
+ closeButton: /* @__PURE__ */ jsx12(CloseButton, { onClick: () => ctx.ui.setOpen(false) })
16627
16989
  }
16628
16990
  )
16629
16991
  }
16630
16992
  );
16631
16993
  }
16632
- return /* @__PURE__ */ jsx10(UseAIChatPanel, { ...chatPanelProps });
16994
+ return /* @__PURE__ */ jsx12(UseAIChatPanel, { ...chatPanelProps });
16633
16995
  }
16634
16996
 
16635
16997
  // ../../node_modules/.bun/engine.io-parser@5.2.3/node_modules/engine.io-parser/build/esm/commons.js
@@ -20188,12 +20550,18 @@ var UseAIClient = class {
20188
20550
  return { type: "text", text: part.text };
20189
20551
  } else if (part.type === "image") {
20190
20552
  return { type: "image", url: part.url };
20191
- } else {
20553
+ } else if (part.type === "file") {
20192
20554
  return {
20193
20555
  type: "file",
20194
20556
  url: part.url,
20195
20557
  mimeType: part.mimeType
20196
20558
  };
20559
+ } else {
20560
+ return {
20561
+ type: "transformed_file",
20562
+ text: part.text,
20563
+ originalFile: part.originalFile
20564
+ };
20197
20565
  }
20198
20566
  });
20199
20567
  }
@@ -33231,7 +33599,8 @@ var LocalStorageChatRepository = class {
33231
33599
  title: options?.title,
33232
33600
  messages: [],
33233
33601
  createdAt: now,
33234
- updatedAt: now
33602
+ updatedAt: now,
33603
+ metadata: options?.metadata
33235
33604
  };
33236
33605
  await this.enforceMaxChatsLimit();
33237
33606
  await this.saveChat(chat);
@@ -33315,6 +33684,14 @@ var LocalStorageChatRepository = class {
33315
33684
  throw new Error(`Failed to clear all chats: ${error46 instanceof Error ? error46.message : "Unknown error"}`);
33316
33685
  }
33317
33686
  }
33687
+ async updateMetadata(id, metadata, overwrite = false) {
33688
+ const chat = await this.loadChat(id);
33689
+ if (!chat) {
33690
+ throw new Error(`Chat not found: ${id}`);
33691
+ }
33692
+ chat.metadata = overwrite ? metadata : { ...chat.metadata, ...metadata };
33693
+ await this.saveChat(chat);
33694
+ }
33318
33695
  getChatKey(id) {
33319
33696
  return `${STORAGE_KEY_PREFIX}${id}`;
33320
33697
  }
@@ -33353,36 +33730,12 @@ var LocalStorageChatRepository = class {
33353
33730
  }
33354
33731
  };
33355
33732
 
33356
- // src/fileUpload/EmbedFileUploadBackend.ts
33357
- var EmbedFileUploadBackend = class {
33358
- /**
33359
- * Converts a File to a base64 data URL.
33360
- *
33361
- * @param file - The File object to convert
33362
- * @returns Promise resolving to a base64 data URL (e.g., "data:image/png;base64,...")
33363
- * @throws Error if file reading fails
33364
- */
33365
- async prepareForSend(file2) {
33366
- return new Promise((resolve, reject) => {
33367
- const reader = new FileReader();
33368
- reader.onload = () => {
33369
- if (typeof reader.result === "string") {
33370
- resolve(reader.result);
33371
- } else {
33372
- reject(new Error("Failed to read file as data URL"));
33373
- }
33374
- };
33375
- reader.onerror = () => {
33376
- reject(new Error(`Failed to read file: ${file2.name}`));
33377
- };
33378
- reader.readAsDataURL(file2);
33379
- });
33380
- }
33381
- };
33382
-
33383
33733
  // src/hooks/useChatManagement.ts
33384
33734
  import { useState as useState5, useCallback as useCallback4, useRef as useRef5, useEffect as useEffect5 } from "react";
33385
33735
  var CHAT_TITLE_MAX_LENGTH = 50;
33736
+ function deepEquals(a, b) {
33737
+ return JSON.stringify(a) === JSON.stringify(b);
33738
+ }
33386
33739
  function generateChatTitle(message) {
33387
33740
  return message.length > CHAT_TITLE_MAX_LENGTH ? message.substring(0, CHAT_TITLE_MAX_LENGTH) + "..." : message;
33388
33741
  }
@@ -33413,7 +33766,11 @@ function transformMessagesToClientFormat(uiMessages) {
33413
33766
  }
33414
33767
  function useChatManagement({
33415
33768
  repository,
33416
- clientRef
33769
+ clientRef,
33770
+ onSendMessage,
33771
+ setOpen,
33772
+ connected,
33773
+ loading
33417
33774
  }) {
33418
33775
  const [currentChatId, setCurrentChatId] = useState5(null);
33419
33776
  const [pendingChatId, setPendingChatId] = useState5(null);
@@ -33446,18 +33803,18 @@ function useChatManagement({
33446
33803
  const loadedMessages = await loadChatMessages(chatId);
33447
33804
  setMessages(loadedMessages);
33448
33805
  }, [loadChatMessages]);
33449
- const createNewChat = useCallback4(async () => {
33806
+ const createNewChat = useCallback4(async (options) => {
33450
33807
  console.log("[ChatManagement] createNewChat called - currentChatId:", currentChatId, "pendingChatId:", pendingChatId, "messages.length:", messages.length);
33451
33808
  if (pendingChatId && messages.length === 0) {
33452
- console.log("[ChatManagement] Pending chat is already blank, not creating new chat");
33453
- return pendingChatId;
33454
- }
33455
- if (currentChatId && !pendingChatId && messages.length === 0) {
33456
- console.log("[ChatManagement] Current chat is already blank, not creating new chat");
33457
- return currentChatId;
33809
+ const existingChat = await repository.loadChat(pendingChatId);
33810
+ const optionsMatch = existingChat && existingChat.title === options?.title && deepEquals(existingChat.metadata, options?.metadata);
33811
+ if (optionsMatch) {
33812
+ console.log("[ChatManagement] Last created chat has matching options, reusing:", pendingChatId);
33813
+ return pendingChatId;
33814
+ }
33458
33815
  }
33459
33816
  console.log("[ChatManagement] Creating new chat...");
33460
- const chatId = await repository.createChat();
33817
+ const chatId = await repository.createChat(options);
33461
33818
  setPendingChatId(chatId);
33462
33819
  setMessages([]);
33463
33820
  if (clientRef.current) {
@@ -33502,6 +33859,22 @@ function useChatManagement({
33502
33859
  }
33503
33860
  }
33504
33861
  }, [currentChatId, repository]);
33862
+ const getCurrentChat = useCallback4(async () => {
33863
+ const chatId = pendingChatId || currentChatId;
33864
+ if (!chatId) return null;
33865
+ const chat = await repository.loadChat(chatId);
33866
+ if (chat?.metadata) {
33867
+ chat.metadata = Object.freeze({ ...chat.metadata });
33868
+ }
33869
+ return chat;
33870
+ }, [pendingChatId, currentChatId, repository]);
33871
+ const updateMetadata = useCallback4(async (metadata, overwrite = false) => {
33872
+ const chatId = pendingChatId || currentChatId;
33873
+ if (!chatId) {
33874
+ throw new Error("No active chat");
33875
+ }
33876
+ await repository.updateMetadata(chatId, metadata, overwrite);
33877
+ }, [pendingChatId, currentChatId, repository]);
33505
33878
  const activatePendingChat = useCallback4(() => {
33506
33879
  if (!pendingChatId) return null;
33507
33880
  console.log("[ChatManagement] Activating pending chat:", pendingChatId);
@@ -33520,7 +33893,7 @@ function useChatManagement({
33520
33893
  console.error("[ChatManagement] Chat not found:", chatId);
33521
33894
  return false;
33522
33895
  }
33523
- const { generateMessageId: generateMessageId2 } = await import("./types-INERONQV.js");
33896
+ const { generateMessageId: generateMessageId2 } = await import("./types-STDS67SG.js");
33524
33897
  chat.messages.push({
33525
33898
  id: generateMessageId2(),
33526
33899
  role: "user",
@@ -33556,7 +33929,7 @@ function useChatManagement({
33556
33929
  console.error("[ChatManagement] Chat not found:", currentChatIdValue);
33557
33930
  return;
33558
33931
  }
33559
- const { generateMessageId: generateMessageId2 } = await import("./types-INERONQV.js");
33932
+ const { generateMessageId: generateMessageId2 } = await import("./types-STDS67SG.js");
33560
33933
  chat.messages.push({
33561
33934
  id: generateMessageId2(),
33562
33935
  role: "assistant",
@@ -33613,6 +33986,70 @@ function useChatManagement({
33613
33986
  }
33614
33987
  }, [currentChatId, pendingChatId, createNewChat, repository, loadChatMessages, clientRef]);
33615
33988
  const displayedChatId = pendingChatId || currentChatId;
33989
+ const pendingMessagesRef = useRef5([]);
33990
+ const isProcessingQueueRef = useRef5(false);
33991
+ const loadingRef = useRef5(loading);
33992
+ useEffect5(() => {
33993
+ loadingRef.current = loading;
33994
+ }, [loading]);
33995
+ const processMessageQueue = useCallback4(async () => {
33996
+ if (isProcessingQueueRef.current || pendingMessagesRef.current.length === 0 || !onSendMessage) {
33997
+ return;
33998
+ }
33999
+ isProcessingQueueRef.current = true;
34000
+ while (pendingMessagesRef.current.length > 0) {
34001
+ const { message, options } = pendingMessagesRef.current.shift();
34002
+ const { newChat = false, attachments = [], openChat = true, metadata } = options ?? {};
34003
+ if (newChat) {
34004
+ await createNewChat({ metadata });
34005
+ }
34006
+ const fileAttachments = await Promise.all(
34007
+ attachments.map(async (file2) => {
34008
+ let preview;
34009
+ if (file2.type.startsWith("image/")) {
34010
+ preview = await new Promise((resolve) => {
34011
+ const reader = new FileReader();
34012
+ reader.onload = () => resolve(typeof reader.result === "string" ? reader.result : void 0);
34013
+ reader.onerror = () => resolve(void 0);
34014
+ reader.readAsDataURL(file2);
34015
+ });
34016
+ }
34017
+ return {
34018
+ id: crypto.randomUUID(),
34019
+ file: file2,
34020
+ preview
34021
+ };
34022
+ })
34023
+ );
34024
+ await onSendMessage(message, fileAttachments.length > 0 ? fileAttachments : void 0);
34025
+ if (openChat && setOpen) {
34026
+ setOpen(true);
34027
+ }
34028
+ await new Promise((resolve) => {
34029
+ const checkLoading = () => {
34030
+ setTimeout(() => {
34031
+ if (!loadingRef.current) {
34032
+ resolve();
34033
+ } else {
34034
+ checkLoading();
34035
+ }
34036
+ }, 100);
34037
+ };
34038
+ checkLoading();
34039
+ });
34040
+ }
34041
+ isProcessingQueueRef.current = false;
34042
+ }, [onSendMessage, createNewChat, setOpen]);
34043
+ const sendMessage = useCallback4(async (message, options) => {
34044
+ if (!onSendMessage) {
34045
+ throw new Error("sendMessage is not available (onSendMessage callback not provided)");
34046
+ }
34047
+ if (!connected) {
34048
+ throw new Error("Not connected to UseAI server");
34049
+ }
34050
+ pendingMessagesRef.current.push({ message, options });
34051
+ await processMessageQueue();
34052
+ }, [onSendMessage, connected, processMessageQueue]);
33616
34053
  return {
33617
34054
  currentChatId,
33618
34055
  pendingChatId,
@@ -33627,6 +34064,9 @@ function useChatManagement({
33627
34064
  saveUserMessage,
33628
34065
  saveAIResponse,
33629
34066
  reloadMessages,
34067
+ sendMessage,
34068
+ getCurrentChat,
34069
+ updateMetadata,
33630
34070
  currentChatIdSnapshot,
33631
34071
  pendingChatIdSnapshot
33632
34072
  };
@@ -34008,7 +34448,7 @@ function usePromptState({
34008
34448
  }
34009
34449
 
34010
34450
  // src/providers/useAIProvider.tsx
34011
- import { Fragment as Fragment3, jsx as jsx11, jsxs as jsxs7 } from "react/jsx-runtime";
34451
+ import { Fragment as Fragment3, jsx as jsx13, jsxs as jsxs9 } from "react/jsx-runtime";
34012
34452
  var __UseAIContext = createContext4(null);
34013
34453
  var hasWarnedAboutMissingProvider = false;
34014
34454
  var noOpContextValue = {
@@ -34038,6 +34478,11 @@ var noOpContextValue = {
34038
34478
  },
34039
34479
  list: async () => [],
34040
34480
  clear: async () => {
34481
+ },
34482
+ sendMessage: async () => {
34483
+ },
34484
+ get: async () => null,
34485
+ updateMetadata: async () => {
34041
34486
  }
34042
34487
  },
34043
34488
  agents: {
@@ -34077,7 +34522,8 @@ function UseAIProvider({
34077
34522
  renderChat = true,
34078
34523
  theme: customTheme,
34079
34524
  strings: customStrings,
34080
- visibleAgentIds
34525
+ visibleAgentIds,
34526
+ onOpenChange
34081
34527
  }) {
34082
34528
  const fileUploadConfig = fileUploadConfigProp === false ? void 0 : fileUploadConfigProp ?? DEFAULT_FILE_UPLOAD_CONFIG;
34083
34529
  const theme = { ...defaultTheme, ...customTheme };
@@ -34085,12 +34531,17 @@ function UseAIProvider({
34085
34531
  const [connected, setConnected] = useState10(false);
34086
34532
  const [isChatOpen, setIsChatOpen] = useState10(false);
34087
34533
  const [loading, setLoading] = useState10(false);
34534
+ const handleSetChatOpen = useCallback9((open) => {
34535
+ setIsChatOpen(open);
34536
+ onOpenChange?.(open);
34537
+ }, [onOpenChange]);
34088
34538
  const [streamingText, setStreamingText] = useState10("");
34089
34539
  const streamingChatIdRef = useRef9(null);
34090
34540
  const clientRef = useRef9(null);
34091
34541
  const repositoryRef = useRef9(
34092
34542
  chatRepository || new LocalStorageChatRepository()
34093
34543
  );
34544
+ const handleSendMessageRef = useRef9(null);
34094
34545
  const {
34095
34546
  registerTools,
34096
34547
  unregisterTools,
@@ -34112,9 +34563,18 @@ function UseAIProvider({
34112
34563
  clientRef,
34113
34564
  connected
34114
34565
  });
34566
+ const stableSendMessage = useCallback9(async (message, attachments) => {
34567
+ if (handleSendMessageRef.current) {
34568
+ await handleSendMessageRef.current(message, attachments);
34569
+ }
34570
+ }, []);
34115
34571
  const chatManagement = useChatManagement({
34116
34572
  repository: repositoryRef.current,
34117
- clientRef
34573
+ clientRef,
34574
+ onSendMessage: stableSendMessage,
34575
+ setOpen: handleSetChatOpen,
34576
+ connected,
34577
+ loading
34118
34578
  });
34119
34579
  const {
34120
34580
  currentChatId,
@@ -34128,7 +34588,10 @@ function UseAIProvider({
34128
34588
  clearCurrentChat,
34129
34589
  activatePendingChat,
34130
34590
  saveUserMessage,
34131
- saveAIResponse
34591
+ saveAIResponse,
34592
+ sendMessage,
34593
+ getCurrentChat,
34594
+ updateMetadata
34132
34595
  } = chatManagement;
34133
34596
  const {
34134
34597
  availableAgents,
@@ -34269,7 +34732,6 @@ function UseAIProvider({
34269
34732
  let persistedContent = message;
34270
34733
  let multimodalContent;
34271
34734
  if (attachments && attachments.length > 0) {
34272
- const backend = fileUploadConfig?.backend ?? new EmbedFileUploadBackend();
34273
34735
  const persistedParts = [];
34274
34736
  if (message.trim()) {
34275
34737
  persistedParts.push({ type: "text", text: message });
@@ -34285,35 +34747,24 @@ function UseAIProvider({
34285
34747
  });
34286
34748
  }
34287
34749
  persistedContent = persistedParts;
34288
- const contentParts = [];
34750
+ const fileContent = await processAttachments(attachments, {
34751
+ getCurrentChat,
34752
+ backend: fileUploadConfig?.backend,
34753
+ transformers: fileUploadConfig?.transformers
34754
+ });
34755
+ multimodalContent = [];
34289
34756
  if (message.trim()) {
34290
- contentParts.push({ type: "text", text: message });
34757
+ multimodalContent.push({ type: "text", text: message });
34291
34758
  }
34292
- for (const attachment of attachments) {
34293
- try {
34294
- const url3 = await backend.prepareForSend(attachment.file);
34295
- if (attachment.file.type.startsWith("image/")) {
34296
- contentParts.push({ type: "image", url: url3 });
34297
- } else {
34298
- contentParts.push({
34299
- type: "file",
34300
- url: url3,
34301
- mimeType: attachment.file.type,
34302
- name: attachment.file.name
34303
- });
34304
- }
34305
- } catch (error46) {
34306
- console.error("[Provider] Failed to prepare file for send:", error46);
34307
- }
34308
- }
34309
- multimodalContent = contentParts;
34759
+ multimodalContent.push(...fileContent);
34310
34760
  }
34311
34761
  if (activeChatId) {
34312
34762
  await saveUserMessage(activeChatId, persistedContent);
34313
34763
  }
34314
34764
  setLoading(true);
34315
34765
  await clientRef.current.sendPrompt(message, multimodalContent);
34316
- }, [activatePendingChat, currentChatId, saveUserMessage, fileUploadConfig]);
34766
+ }, [activatePendingChat, currentChatId, saveUserMessage, fileUploadConfig, getCurrentChat]);
34767
+ handleSendMessageRef.current = handleSendMessage;
34317
34768
  const value2 = {
34318
34769
  serverUrl,
34319
34770
  connected,
@@ -34333,7 +34784,10 @@ function UseAIProvider({
34333
34784
  load: loadChat,
34334
34785
  delete: deleteChat,
34335
34786
  list: listChats,
34336
- clear: clearCurrentChat
34787
+ clear: clearCurrentChat,
34788
+ sendMessage,
34789
+ get: getCurrentChat,
34790
+ updateMetadata
34337
34791
  },
34338
34792
  agents: {
34339
34793
  available: availableAgents,
@@ -34363,7 +34817,8 @@ function UseAIProvider({
34363
34817
  create: createNewChat,
34364
34818
  load: loadChat,
34365
34819
  delete: deleteChat,
34366
- list: listChats
34820
+ list: listChats,
34821
+ get: getCurrentChat
34367
34822
  },
34368
34823
  agents: {
34369
34824
  available: availableAgents,
@@ -34379,7 +34834,7 @@ function UseAIProvider({
34379
34834
  },
34380
34835
  ui: {
34381
34836
  isOpen: isChatOpen,
34382
- setOpen: setIsChatOpen
34837
+ setOpen: handleSetChatOpen
34383
34838
  }
34384
34839
  };
34385
34840
  const isUIDisabled = CustomButton === null || CustomChat === null;
@@ -34409,21 +34864,21 @@ function UseAIProvider({
34409
34864
  };
34410
34865
  const renderDefaultChat = () => {
34411
34866
  if (isUIDisabled) return null;
34412
- return /* @__PURE__ */ jsx11(UseAIFloatingChatWrapper, { isOpen: isChatOpen, onClose: () => setIsChatOpen(false), children: /* @__PURE__ */ jsx11(
34867
+ return /* @__PURE__ */ jsx13(UseAIFloatingChatWrapper, { isOpen: isChatOpen, onClose: () => handleSetChatOpen(false), children: /* @__PURE__ */ jsx13(
34413
34868
  UseAIChatPanel,
34414
34869
  {
34415
34870
  ...chatPanelProps,
34416
- closeButton: /* @__PURE__ */ jsx11(CloseButton, { onClick: () => setIsChatOpen(false) })
34871
+ closeButton: /* @__PURE__ */ jsx13(CloseButton, { onClick: () => handleSetChatOpen(false) })
34417
34872
  }
34418
34873
  ) });
34419
34874
  };
34420
34875
  const renderCustomChat = () => {
34421
34876
  if (!CustomChat) return null;
34422
- return /* @__PURE__ */ jsx11(
34877
+ return /* @__PURE__ */ jsx13(
34423
34878
  CustomChat,
34424
34879
  {
34425
34880
  isOpen: isChatOpen,
34426
- onClose: () => setIsChatOpen(false),
34881
+ onClose: () => handleSetChatOpen(false),
34427
34882
  onSendMessage: handleSendMessage,
34428
34883
  messages,
34429
34884
  loading,
@@ -34438,18 +34893,18 @@ function UseAIProvider({
34438
34893
  };
34439
34894
  const renderBuiltInChat = () => {
34440
34895
  if (!renderChat) return null;
34441
- return /* @__PURE__ */ jsxs7(Fragment3, { children: [
34442
- ButtonComponent && /* @__PURE__ */ jsx11(
34896
+ return /* @__PURE__ */ jsxs9(Fragment3, { children: [
34897
+ ButtonComponent && /* @__PURE__ */ jsx13(
34443
34898
  ButtonComponent,
34444
34899
  {
34445
- onClick: () => setIsChatOpen(true),
34900
+ onClick: () => handleSetChatOpen(true),
34446
34901
  connected
34447
34902
  }
34448
34903
  ),
34449
34904
  hasCustomChat ? renderCustomChat() : renderDefaultChat()
34450
34905
  ] });
34451
34906
  };
34452
- return /* @__PURE__ */ jsx11(ThemeContext.Provider, { value: theme, children: /* @__PURE__ */ jsx11(StringsContext.Provider, { value: strings, children: /* @__PURE__ */ jsx11(__UseAIContext.Provider, { value: value2, children: /* @__PURE__ */ jsxs7(__UseAIChatContext.Provider, { value: chatUIContextValue, children: [
34907
+ return /* @__PURE__ */ jsx13(ThemeContext.Provider, { value: theme, children: /* @__PURE__ */ jsx13(StringsContext.Provider, { value: strings, children: /* @__PURE__ */ jsx13(__UseAIContext.Provider, { value: value2, children: /* @__PURE__ */ jsxs9(__UseAIChatContext.Provider, { value: chatUIContextValue, children: [
34453
34908
  children,
34454
34909
  renderBuiltInChat()
34455
34910
  ] }) }) }) });
@@ -34847,14 +35302,18 @@ export {
34847
35302
  UseAIFloatingButton,
34848
35303
  UseAIFloatingChatWrapper,
34849
35304
  UseAIProvider,
35305
+ clearTransformationCache,
34850
35306
  convertToolsToDefinitions,
34851
35307
  defaultStrings,
34852
35308
  defaultTheme,
34853
35309
  defineTool,
34854
35310
  executeDefinedTool,
35311
+ findTransformer,
34855
35312
  generateChatId,
34856
35313
  generateCommandId,
34857
35314
  generateMessageId,
35315
+ matchesMimeType,
35316
+ processAttachments,
34858
35317
  useAI,
34859
35318
  useAIContext,
34860
35319
  useAIWorkflow,