@meetsmore-oss/use-ai-client 1.2.4 → 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({
@@ -14684,8 +14684,131 @@ function MarkdownContent({ content: content3 }) {
14684
14684
  );
14685
14685
  }
14686
14686
 
14687
- // src/components/FileChip.tsx
14687
+ // src/components/Spinner.tsx
14688
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";
14689
14812
  function formatFileSize(bytes) {
14690
14813
  if (bytes < 1024) return `${bytes} B`;
14691
14814
  if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
@@ -14700,11 +14823,14 @@ function truncateFilename(name, maxLength = 20) {
14700
14823
  if (maxBaseLength < 5) return name.substring(0, maxLength - 3) + "...";
14701
14824
  return baseName.substring(0, maxBaseLength) + "..." + ext;
14702
14825
  }
14703
- function FileChip({ attachment, onRemove, disabled }) {
14826
+ function FileChip({ attachment, onRemove, disabled, processingState }) {
14704
14827
  const theme = useTheme();
14705
14828
  const { file: file2, preview } = attachment;
14706
14829
  const isImage = file2.type.startsWith("image/");
14707
- return /* @__PURE__ */ jsxs2(
14830
+ const isProcessing = processingState?.status === "processing";
14831
+ const hasError = processingState?.status === "error";
14832
+ const progress = processingState?.progress;
14833
+ return /* @__PURE__ */ jsxs4(
14708
14834
  "div",
14709
14835
  {
14710
14836
  "data-testid": "file-chip",
@@ -14717,10 +14843,12 @@ function FileChip({ attachment, onRemove, disabled }) {
14717
14843
  borderRadius: "8px",
14718
14844
  fontSize: "13px",
14719
14845
  color: theme.textColor,
14720
- maxWidth: "200px"
14846
+ maxWidth: "200px",
14847
+ position: "relative",
14848
+ opacity: isProcessing ? 0.7 : 1
14721
14849
  },
14722
14850
  children: [
14723
- isImage && preview ? /* @__PURE__ */ jsx3(
14851
+ isImage && preview ? /* @__PURE__ */ jsx5(
14724
14852
  "img",
14725
14853
  {
14726
14854
  src: preview,
@@ -14732,7 +14860,7 @@ function FileChip({ attachment, onRemove, disabled }) {
14732
14860
  objectFit: "cover"
14733
14861
  }
14734
14862
  }
14735
- ) : /* @__PURE__ */ jsx3(
14863
+ ) : /* @__PURE__ */ jsx5(
14736
14864
  "div",
14737
14865
  {
14738
14866
  style: {
@@ -14748,8 +14876,8 @@ function FileChip({ attachment, onRemove, disabled }) {
14748
14876
  children: "\u{1F4CE}"
14749
14877
  }
14750
14878
  ),
14751
- /* @__PURE__ */ jsxs2("div", { style: { flex: 1, minWidth: 0, overflow: "hidden" }, children: [
14752
- /* @__PURE__ */ jsx3(
14879
+ /* @__PURE__ */ jsxs4("div", { style: { flex: 1, minWidth: 0, overflow: "hidden" }, children: [
14880
+ /* @__PURE__ */ jsx5(
14753
14881
  "div",
14754
14882
  {
14755
14883
  style: {
@@ -14762,28 +14890,28 @@ function FileChip({ attachment, onRemove, disabled }) {
14762
14890
  children: truncateFilename(file2.name)
14763
14891
  }
14764
14892
  ),
14765
- /* @__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) })
14766
14894
  ] }),
14767
- /* @__PURE__ */ jsx3(
14895
+ /* @__PURE__ */ jsx5(
14768
14896
  "button",
14769
14897
  {
14770
14898
  "data-testid": "file-chip-remove",
14771
14899
  onClick: onRemove,
14772
- disabled,
14900
+ disabled: disabled || isProcessing,
14773
14901
  style: {
14774
14902
  background: "transparent",
14775
14903
  border: "none",
14776
14904
  padding: "2px 4px",
14777
- cursor: disabled ? "not-allowed" : "pointer",
14905
+ cursor: disabled || isProcessing ? "not-allowed" : "pointer",
14778
14906
  color: theme.placeholderTextColor,
14779
14907
  fontSize: "16px",
14780
14908
  lineHeight: 1,
14781
14909
  borderRadius: "4px",
14782
14910
  transition: "all 0.15s",
14783
- opacity: disabled ? 0.5 : 1
14911
+ opacity: disabled || isProcessing ? 0.5 : 1
14784
14912
  },
14785
14913
  onMouseEnter: (e) => {
14786
- if (!disabled) {
14914
+ if (!disabled && !isProcessing) {
14787
14915
  e.currentTarget.style.background = theme.borderColor;
14788
14916
  e.currentTarget.style.color = theme.textColor;
14789
14917
  }
@@ -14794,6 +14922,44 @@ function FileChip({ attachment, onRemove, disabled }) {
14794
14922
  },
14795
14923
  children: "\xD7"
14796
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
+ }
14797
14963
  )
14798
14964
  ]
14799
14965
  }
@@ -14801,7 +14967,7 @@ function FileChip({ attachment, onRemove, disabled }) {
14801
14967
  }
14802
14968
  function FilePlaceholder({ name, size }) {
14803
14969
  const theme = useTheme();
14804
- return /* @__PURE__ */ jsxs2(
14970
+ return /* @__PURE__ */ jsxs4(
14805
14971
  "div",
14806
14972
  {
14807
14973
  "data-testid": "file-placeholder",
@@ -14818,9 +14984,9 @@ function FilePlaceholder({ name, size }) {
14818
14984
  maxWidth: "200px"
14819
14985
  },
14820
14986
  children: [
14821
- /* @__PURE__ */ jsx3("span", { children: "\u{1F4CE}" }),
14822
- /* @__PURE__ */ jsxs2("div", { style: { flex: 1, minWidth: 0, overflow: "hidden" }, children: [
14823
- /* @__PURE__ */ jsx3(
14987
+ /* @__PURE__ */ jsx5("span", { children: "\u{1F4CE}" }),
14988
+ /* @__PURE__ */ jsxs4("div", { style: { flex: 1, minWidth: 0, overflow: "hidden" }, children: [
14989
+ /* @__PURE__ */ jsx5(
14824
14990
  "div",
14825
14991
  {
14826
14992
  style: {
@@ -14832,7 +14998,7 @@ function FilePlaceholder({ name, size }) {
14832
14998
  children: truncateFilename(name)
14833
14999
  }
14834
15000
  ),
14835
- /* @__PURE__ */ jsx3("div", { style: { fontSize: "11px" }, children: formatFileSize(size) })
15001
+ /* @__PURE__ */ jsx5("div", { style: { fontSize: "11px" }, children: formatFileSize(size) })
14836
15002
  ] })
14837
15003
  ]
14838
15004
  }
@@ -14861,7 +15027,7 @@ function validateCommandName(name) {
14861
15027
 
14862
15028
  // src/components/CommandAutocomplete.tsx
14863
15029
  import { useEffect, useRef } from "react";
14864
- import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
15030
+ import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
14865
15031
  var MAX_VISIBLE_ITEMS = 8;
14866
15032
  function CommandAutocomplete({
14867
15033
  commands,
@@ -14892,7 +15058,7 @@ function CommandAutocomplete({
14892
15058
  }
14893
15059
  }, [highlightedIndex]);
14894
15060
  if (filteredCommands.length === 0) {
14895
- return /* @__PURE__ */ jsx4(
15061
+ return /* @__PURE__ */ jsx6(
14896
15062
  "div",
14897
15063
  {
14898
15064
  "data-testid": "command-autocomplete",
@@ -14908,7 +15074,7 @@ function CommandAutocomplete({
14908
15074
  overflow: "hidden",
14909
15075
  zIndex: 1005
14910
15076
  },
14911
- children: /* @__PURE__ */ jsx4(
15077
+ children: /* @__PURE__ */ jsx6(
14912
15078
  "div",
14913
15079
  {
14914
15080
  style: {
@@ -14923,7 +15089,7 @@ function CommandAutocomplete({
14923
15089
  }
14924
15090
  );
14925
15091
  }
14926
- return /* @__PURE__ */ jsx4(
15092
+ return /* @__PURE__ */ jsx6(
14927
15093
  "div",
14928
15094
  {
14929
15095
  "data-testid": "command-autocomplete",
@@ -14942,7 +15108,7 @@ function CommandAutocomplete({
14942
15108
  overflowY: "auto",
14943
15109
  zIndex: 1005
14944
15110
  },
14945
- children: filteredCommands.map((cmd, index2) => /* @__PURE__ */ jsxs3(
15111
+ children: filteredCommands.map((cmd, index2) => /* @__PURE__ */ jsxs5(
14946
15112
  "div",
14947
15113
  {
14948
15114
  ref: (el) => {
@@ -14962,8 +15128,8 @@ function CommandAutocomplete({
14962
15128
  gap: "8px"
14963
15129
  },
14964
15130
  children: [
14965
- /* @__PURE__ */ jsxs3("div", { style: { flex: 1, minWidth: 0 }, children: [
14966
- /* @__PURE__ */ jsxs3(
15131
+ /* @__PURE__ */ jsxs5("div", { style: { flex: 1, minWidth: 0 }, children: [
15132
+ /* @__PURE__ */ jsxs5(
14967
15133
  "div",
14968
15134
  {
14969
15135
  style: {
@@ -14977,7 +15143,7 @@ function CommandAutocomplete({
14977
15143
  ]
14978
15144
  }
14979
15145
  ),
14980
- /* @__PURE__ */ jsx4(
15146
+ /* @__PURE__ */ jsx6(
14981
15147
  "div",
14982
15148
  {
14983
15149
  style: {
@@ -14992,7 +15158,7 @@ function CommandAutocomplete({
14992
15158
  }
14993
15159
  )
14994
15160
  ] }),
14995
- onDelete && /* @__PURE__ */ jsx4(
15161
+ onDelete && /* @__PURE__ */ jsx6(
14996
15162
  "button",
14997
15163
  {
14998
15164
  "data-testid": "command-delete-button",
@@ -15023,7 +15189,7 @@ function CommandAutocomplete({
15023
15189
  e.currentTarget.style.background = "transparent";
15024
15190
  },
15025
15191
  title: strings.commands.deleteCommand,
15026
- 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" }) })
15027
15193
  }
15028
15194
  )
15029
15195
  ]
@@ -15041,7 +15207,7 @@ function getFilteredCommandsCount(commands, searchPrefix) {
15041
15207
  }
15042
15208
 
15043
15209
  // src/hooks/useSlashCommands.tsx
15044
- import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
15210
+ import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
15045
15211
  var MAX_VISIBLE_ITEMS2 = 8;
15046
15212
  function useSlashCommands({
15047
15213
  commands,
@@ -15176,7 +15342,7 @@ function useSlashCommands({
15176
15342
  }
15177
15343
  }
15178
15344
  }, [savingMessageId, savingMessageText, commandNameInput, commands, onRenameCommand, onSaveCommand, cancelInlineSave, strings]);
15179
- const AutocompleteComponent = showAutocomplete && commands.length > 0 ? /* @__PURE__ */ jsx5(
15345
+ const AutocompleteComponent = showAutocomplete && commands.length > 0 ? /* @__PURE__ */ jsx7(
15180
15346
  CommandAutocomplete,
15181
15347
  {
15182
15348
  commands,
@@ -15192,7 +15358,7 @@ function useSlashCommands({
15192
15358
  if (savingMessageId !== messageId) {
15193
15359
  return null;
15194
15360
  }
15195
- return /* @__PURE__ */ jsxs4(
15361
+ return /* @__PURE__ */ jsxs6(
15196
15362
  "div",
15197
15363
  {
15198
15364
  "data-testid": "inline-save-command",
@@ -15206,9 +15372,9 @@ function useSlashCommands({
15206
15372
  gap: "4px"
15207
15373
  },
15208
15374
  children: [
15209
- /* @__PURE__ */ jsxs4("div", { style: { display: "flex", alignItems: "center", gap: "6px" }, children: [
15210
- /* @__PURE__ */ jsx5("span", { style: { color: theme.primaryColor, fontSize: "13px", fontWeight: 500 }, children: "/" }),
15211
- /* @__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(
15212
15378
  "input",
15213
15379
  {
15214
15380
  ref: commandNameInputRef,
@@ -15240,7 +15406,7 @@ function useSlashCommands({
15240
15406
  }
15241
15407
  }
15242
15408
  ),
15243
- /* @__PURE__ */ jsx5(
15409
+ /* @__PURE__ */ jsx7(
15244
15410
  "button",
15245
15411
  {
15246
15412
  "data-testid": "save-command-confirm",
@@ -15258,15 +15424,15 @@ function useSlashCommands({
15258
15424
  justifyContent: "center"
15259
15425
  },
15260
15426
  title: strings.commands.saveCommand,
15261
- children: /* @__PURE__ */ jsxs4("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
15262
- /* @__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" }),
15263
- /* @__PURE__ */ jsx5("polyline", { points: "17 21 17 13 7 13 7 21" }),
15264
- /* @__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" })
15265
15431
  ] })
15266
15432
  }
15267
15433
  )
15268
15434
  ] }),
15269
- commandSaveError && /* @__PURE__ */ jsx5(
15435
+ commandSaveError && /* @__PURE__ */ jsx7(
15270
15436
  "div",
15271
15437
  {
15272
15438
  "data-testid": "command-save-error",
@@ -15301,6 +15467,148 @@ import { useState as useState2, useRef as useRef3, useCallback as useCallback2,
15301
15467
  // src/fileUpload/types.ts
15302
15468
  var DEFAULT_MAX_FILE_SIZE = 10 * 1024 * 1024;
15303
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
+
15304
15612
  // ../../node_modules/.bun/uuid@11.1.0/node_modules/uuid/dist/esm-browser/stringify.js
15305
15613
  var byteToHex = [];
15306
15614
  for (let i = 0; i < 256; ++i) {
@@ -15354,7 +15662,7 @@ function v4(options, buf, offset) {
15354
15662
  var v4_default = v4;
15355
15663
 
15356
15664
  // src/hooks/useFileUpload.tsx
15357
- import { jsx as jsx6 } from "react/jsx-runtime";
15665
+ import { jsx as jsx8 } from "react/jsx-runtime";
15358
15666
  async function generateImagePreview(file2) {
15359
15667
  if (!file2.type.startsWith("image/")) {
15360
15668
  return void 0;
@@ -15387,18 +15695,21 @@ function isTypeAccepted(mimeType, acceptedTypes) {
15387
15695
  function useFileUpload({
15388
15696
  config: config2,
15389
15697
  disabled = false,
15390
- resetDependency
15698
+ resetDependency,
15699
+ getCurrentChat
15391
15700
  }) {
15392
15701
  const strings = useStrings();
15393
15702
  const theme = useTheme();
15394
15703
  const [attachments, setAttachments] = useState2([]);
15395
15704
  const [isDragging, setIsDragging] = useState2(false);
15396
15705
  const [fileError, setFileError] = useState2(null);
15706
+ const [processingState, setProcessingState] = useState2(/* @__PURE__ */ new Map());
15397
15707
  const fileInputRef = useRef3(null);
15398
15708
  const dragCounterRef = useRef3(0);
15399
15709
  const enabled = config2 !== void 0;
15400
15710
  const maxFileSize = config2?.maxFileSize ?? DEFAULT_MAX_FILE_SIZE;
15401
15711
  const acceptedTypes = config2?.acceptedTypes;
15712
+ const transformers = config2?.transformers;
15402
15713
  useEffect3(() => {
15403
15714
  if (fileError) {
15404
15715
  const timer = setTimeout(() => setFileError(null), 3e3);
@@ -15408,7 +15719,28 @@ function useFileUpload({
15408
15719
  useEffect3(() => {
15409
15720
  setAttachments([]);
15410
15721
  setFileError(null);
15722
+ setProcessingState(/* @__PURE__ */ new Map());
15411
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]);
15412
15744
  const handleFiles = useCallback2(async (files) => {
15413
15745
  const fileArray = Array.from(files);
15414
15746
  for (const file2 of fileArray) {
@@ -15423,21 +15755,30 @@ function useFileUpload({
15423
15755
  continue;
15424
15756
  }
15425
15757
  const preview = await generateImagePreview(file2);
15426
- setAttachments((prev) => [
15427
- ...prev,
15428
- {
15429
- id: v4_default(),
15430
- file: file2,
15431
- preview
15432
- }
15433
- ]);
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
+ }
15434
15769
  }
15435
- }, [maxFileSize, acceptedTypes, strings]);
15770
+ }, [maxFileSize, acceptedTypes, strings, transformers, runTransformer]);
15436
15771
  const removeAttachment = useCallback2((id) => {
15437
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
+ });
15438
15778
  }, []);
15439
15779
  const clearAttachments = useCallback2(() => {
15440
15780
  setAttachments([]);
15781
+ setProcessingState(/* @__PURE__ */ new Map());
15441
15782
  }, []);
15442
15783
  const openFilePicker = useCallback2(() => {
15443
15784
  fileInputRef.current?.click();
@@ -15490,7 +15831,7 @@ function useFileUpload({
15490
15831
  }), [handleDragEnter, handleDragOver, handleDragLeave, handleDrop]);
15491
15832
  const DropZoneOverlay = useMemo(() => {
15492
15833
  if (!isDragging || !enabled) return null;
15493
- return /* @__PURE__ */ jsx6(
15834
+ return /* @__PURE__ */ jsx8(
15494
15835
  "div",
15495
15836
  {
15496
15837
  style: {
@@ -15505,7 +15846,7 @@ function useFileUpload({
15505
15846
  zIndex: 1010,
15506
15847
  pointerEvents: "none"
15507
15848
  },
15508
- children: /* @__PURE__ */ jsx6(
15849
+ children: /* @__PURE__ */ jsx8(
15509
15850
  "div",
15510
15851
  {
15511
15852
  style: {
@@ -15514,7 +15855,7 @@ function useFileUpload({
15514
15855
  borderRadius: "12px",
15515
15856
  boxShadow: theme.buttonShadow
15516
15857
  },
15517
- 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 })
15518
15859
  }
15519
15860
  )
15520
15861
  }
@@ -15527,6 +15868,7 @@ function useFileUpload({
15527
15868
  enabled,
15528
15869
  maxFileSize,
15529
15870
  acceptedTypes,
15871
+ processingState,
15530
15872
  fileInputRef,
15531
15873
  handleFiles,
15532
15874
  removeAttachment,
@@ -15544,7 +15886,7 @@ function useFileUpload({
15544
15886
 
15545
15887
  // src/hooks/useDropdownState.tsx
15546
15888
  import { useState as useState3, useCallback as useCallback3, useMemo as useMemo2 } from "react";
15547
- import { jsx as jsx7 } from "react/jsx-runtime";
15889
+ import { jsx as jsx9 } from "react/jsx-runtime";
15548
15890
  function useDropdownState(options = {}) {
15549
15891
  const { backdropZIndex = 1002, initialOpen = false } = options;
15550
15892
  const [isOpen, setIsOpen] = useState3(initialOpen);
@@ -15559,7 +15901,7 @@ function useDropdownState(options = {}) {
15559
15901
  }, []);
15560
15902
  const Backdrop = useMemo2(() => {
15561
15903
  if (!isOpen) return null;
15562
- return /* @__PURE__ */ jsx7(
15904
+ return /* @__PURE__ */ jsx9(
15563
15905
  "div",
15564
15906
  {
15565
15907
  onClick: close,
@@ -15584,7 +15926,7 @@ function useDropdownState(options = {}) {
15584
15926
  }
15585
15927
 
15586
15928
  // src/components/UseAIChatPanel.tsx
15587
- 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";
15588
15930
  function getTextContent(content3) {
15589
15931
  if (typeof content3 === "string") {
15590
15932
  return content3;
@@ -15605,6 +15947,7 @@ function UseAIChatPanel({
15605
15947
  onLoadChat,
15606
15948
  onDeleteChat,
15607
15949
  onListChats,
15950
+ onGetChat,
15608
15951
  suggestions,
15609
15952
  availableAgents,
15610
15953
  defaultAgent,
@@ -15632,6 +15975,7 @@ function UseAIChatPanel({
15632
15975
  fileError,
15633
15976
  enabled: fileUploadEnabled,
15634
15977
  acceptedTypes,
15978
+ processingState: fileProcessingState,
15635
15979
  fileInputRef,
15636
15980
  removeAttachment,
15637
15981
  clearAttachments,
@@ -15640,6 +15984,7 @@ function UseAIChatPanel({
15640
15984
  getDropZoneProps,
15641
15985
  DropZoneOverlay
15642
15986
  } = useFileUpload({
15987
+ getCurrentChat: onGetChat ?? (async () => null),
15643
15988
  config: fileUploadConfig,
15644
15989
  disabled: loading,
15645
15990
  resetDependency: currentChatId
@@ -15711,7 +16056,7 @@ function UseAIChatPanel({
15711
16056
  chatHistoryDropdown.close();
15712
16057
  }
15713
16058
  };
15714
- return /* @__PURE__ */ jsxs5(
16059
+ return /* @__PURE__ */ jsxs7(
15715
16060
  "div",
15716
16061
  {
15717
16062
  onClick: () => {
@@ -15729,7 +16074,7 @@ function UseAIChatPanel({
15729
16074
  },
15730
16075
  children: [
15731
16076
  DropZoneOverlay,
15732
- /* @__PURE__ */ jsxs5(
16077
+ /* @__PURE__ */ jsxs7(
15733
16078
  "div",
15734
16079
  {
15735
16080
  style: {
@@ -15742,7 +16087,7 @@ function UseAIChatPanel({
15742
16087
  gap: "12px"
15743
16088
  },
15744
16089
  children: [
15745
- /* @__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(
15746
16091
  "button",
15747
16092
  {
15748
16093
  "data-testid": "chat-history-dropdown-button",
@@ -15775,7 +16120,7 @@ function UseAIChatPanel({
15775
16120
  e.currentTarget.style.background = "transparent";
15776
16121
  },
15777
16122
  children: [
15778
- /* @__PURE__ */ jsx8("span", { style: {
16123
+ /* @__PURE__ */ jsx10("span", { style: {
15779
16124
  overflow: "hidden",
15780
16125
  textOverflow: "ellipsis",
15781
16126
  whiteSpace: "nowrap",
@@ -15792,13 +16137,13 @@ function UseAIChatPanel({
15792
16137
  }
15793
16138
  return strings.header.newChat;
15794
16139
  })() }),
15795
- /* @__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" }) })
15796
16141
  ]
15797
16142
  }
15798
- ) : /* @__PURE__ */ jsx8("div", { style: { fontSize: "14px", fontWeight: "600", color: theme.textColor, padding: "6px 8px" }, children: strings.header.aiAssistant }) }),
15799
- /* @__PURE__ */ jsxs5("div", { style: { display: "flex", alignItems: "center", gap: "4px" }, children: [
15800
- availableAgents && availableAgents.length > 1 && onAgentChange && /* @__PURE__ */ jsxs5("div", { style: { position: "relative" }, children: [
15801
- /* @__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(
15802
16147
  "button",
15803
16148
  {
15804
16149
  "data-testid": "agent-selector",
@@ -15827,7 +16172,7 @@ function UseAIChatPanel({
15827
16172
  },
15828
16173
  title: "Select AI model",
15829
16174
  children: [
15830
- /* @__PURE__ */ jsx8("span", { style: {
16175
+ /* @__PURE__ */ jsx10("span", { style: {
15831
16176
  overflow: "hidden",
15832
16177
  textOverflow: "ellipsis",
15833
16178
  whiteSpace: "nowrap",
@@ -15836,11 +16181,11 @@ function UseAIChatPanel({
15836
16181
  const agent = availableAgents.find((a) => a.id === (selectedAgent ?? defaultAgent));
15837
16182
  return agent?.name || "AI";
15838
16183
  })() }),
15839
- /* @__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" }) })
15840
16185
  ]
15841
16186
  }
15842
16187
  ),
15843
- agentDropdown.isOpen && /* @__PURE__ */ jsx8(
16188
+ agentDropdown.isOpen && /* @__PURE__ */ jsx10(
15844
16189
  "div",
15845
16190
  {
15846
16191
  style: {
@@ -15860,7 +16205,7 @@ function UseAIChatPanel({
15860
16205
  },
15861
16206
  children: availableAgents.map((agent) => {
15862
16207
  const isSelected = agent.id === (selectedAgent ?? defaultAgent);
15863
- return /* @__PURE__ */ jsxs5(
16208
+ return /* @__PURE__ */ jsxs7(
15864
16209
  "div",
15865
16210
  {
15866
16211
  "data-testid": "agent-option",
@@ -15890,13 +16235,13 @@ function UseAIChatPanel({
15890
16235
  }
15891
16236
  },
15892
16237
  children: [
15893
- /* @__PURE__ */ jsxs5("div", { style: { flex: 1, minWidth: 0 }, children: [
15894
- /* @__PURE__ */ jsx8("div", { style: {
16238
+ /* @__PURE__ */ jsxs7("div", { style: { flex: 1, minWidth: 0 }, children: [
16239
+ /* @__PURE__ */ jsx10("div", { style: {
15895
16240
  fontSize: "13px",
15896
16241
  fontWeight: isSelected ? "600" : "500",
15897
16242
  color: isSelected ? theme.primaryColor : theme.textColor
15898
16243
  }, children: agent.name }),
15899
- agent.annotation && /* @__PURE__ */ jsx8("div", { style: {
16244
+ agent.annotation && /* @__PURE__ */ jsx10("div", { style: {
15900
16245
  fontSize: "11px",
15901
16246
  color: theme.secondaryTextColor,
15902
16247
  marginTop: "2px",
@@ -15905,7 +16250,7 @@ function UseAIChatPanel({
15905
16250
  whiteSpace: "nowrap"
15906
16251
  }, children: agent.annotation })
15907
16252
  ] }),
15908
- 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" }) })
15909
16254
  ]
15910
16255
  },
15911
16256
  agent.id
@@ -15914,7 +16259,7 @@ function UseAIChatPanel({
15914
16259
  }
15915
16260
  )
15916
16261
  ] }),
15917
- onNewChat && /* @__PURE__ */ jsx8(
16262
+ onNewChat && /* @__PURE__ */ jsx10(
15918
16263
  "button",
15919
16264
  {
15920
16265
  "data-testid": "new-chat-button",
@@ -15941,10 +16286,10 @@ function UseAIChatPanel({
15941
16286
  e.currentTarget.style.color = theme.secondaryTextColor;
15942
16287
  },
15943
16288
  title: strings.header.newChat,
15944
- 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" }) })
15945
16290
  }
15946
16291
  ),
15947
- onDeleteChat && messages.length > 0 && /* @__PURE__ */ jsx8(
16292
+ onDeleteChat && messages.length > 0 && /* @__PURE__ */ jsx10(
15948
16293
  "button",
15949
16294
  {
15950
16295
  "data-testid": "delete-chat-button",
@@ -15968,7 +16313,7 @@ function UseAIChatPanel({
15968
16313
  e.currentTarget.style.color = theme.secondaryTextColor;
15969
16314
  },
15970
16315
  title: strings.header.deleteChat,
15971
- 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" }) })
15972
16317
  }
15973
16318
  ),
15974
16319
  closeButton
@@ -15976,7 +16321,7 @@ function UseAIChatPanel({
15976
16321
  ]
15977
16322
  }
15978
16323
  ),
15979
- chatHistoryDropdown.isOpen && onListChats && /* @__PURE__ */ jsx8(
16324
+ chatHistoryDropdown.isOpen && onListChats && /* @__PURE__ */ jsx10(
15980
16325
  "div",
15981
16326
  {
15982
16327
  style: {
@@ -15993,7 +16338,7 @@ function UseAIChatPanel({
15993
16338
  flexDirection: "column",
15994
16339
  overflow: "hidden"
15995
16340
  },
15996
- children: /* @__PURE__ */ jsx8(
16341
+ children: /* @__PURE__ */ jsx10(
15997
16342
  "div",
15998
16343
  {
15999
16344
  style: {
@@ -16001,7 +16346,7 @@ function UseAIChatPanel({
16001
16346
  overflowY: "auto",
16002
16347
  padding: "8px"
16003
16348
  },
16004
- children: chatHistory.length === 0 ? /* @__PURE__ */ jsx8(
16349
+ children: chatHistory.length === 0 ? /* @__PURE__ */ jsx10(
16005
16350
  "div",
16006
16351
  {
16007
16352
  style: {
@@ -16010,9 +16355,9 @@ function UseAIChatPanel({
16010
16355
  padding: "32px 16px",
16011
16356
  fontSize: "13px"
16012
16357
  },
16013
- children: /* @__PURE__ */ jsx8("p", { style: { margin: 0 }, children: strings.chatHistory.noChatHistory })
16358
+ children: /* @__PURE__ */ jsx10("p", { style: { margin: 0 }, children: strings.chatHistory.noChatHistory })
16014
16359
  }
16015
- ) : chatHistory.map((chat) => /* @__PURE__ */ jsxs5(
16360
+ ) : chatHistory.map((chat) => /* @__PURE__ */ jsxs7(
16016
16361
  "div",
16017
16362
  {
16018
16363
  "data-testid": "chat-history-item",
@@ -16036,10 +16381,10 @@ function UseAIChatPanel({
16036
16381
  }
16037
16382
  },
16038
16383
  children: [
16039
- /* @__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 }),
16040
- /* @__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: [
16041
16386
  new Date(chat.updatedAt).toLocaleDateString([], { month: "short", day: "numeric" }),
16042
- currentChatId === chat.id && /* @__PURE__ */ jsxs5("span", { style: {
16387
+ currentChatId === chat.id && /* @__PURE__ */ jsxs7("span", { style: {
16043
16388
  marginLeft: "8px",
16044
16389
  color: theme.primaryColor,
16045
16390
  fontWeight: "600"
@@ -16058,7 +16403,7 @@ function UseAIChatPanel({
16058
16403
  ),
16059
16404
  chatHistoryDropdown.Backdrop,
16060
16405
  agentDropdown.Backdrop,
16061
- /* @__PURE__ */ jsxs5(
16406
+ /* @__PURE__ */ jsxs7(
16062
16407
  "div",
16063
16408
  {
16064
16409
  style: {
@@ -16070,7 +16415,7 @@ function UseAIChatPanel({
16070
16415
  gap: "12px"
16071
16416
  },
16072
16417
  children: [
16073
- messages.length === 0 && /* @__PURE__ */ jsxs5(
16418
+ messages.length === 0 && /* @__PURE__ */ jsxs7(
16074
16419
  "div",
16075
16420
  {
16076
16421
  style: {
@@ -16081,12 +16426,12 @@ function UseAIChatPanel({
16081
16426
  gap: "20px"
16082
16427
  },
16083
16428
  children: [
16084
- /* @__PURE__ */ jsxs5("div", { style: { textAlign: "center", color: theme.secondaryTextColor, fontSize: "14px" }, children: [
16085
- /* @__PURE__ */ jsx8("p", { style: { margin: 0, fontSize: "32px", marginBottom: "12px" }, children: "\u{1F4AC}" }),
16086
- /* @__PURE__ */ jsx8("p", { style: { margin: 0 }, children: strings.emptyChat.startConversation }),
16087
- /* @__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 })
16088
16433
  ] }),
16089
- displayedSuggestions.length > 0 && /* @__PURE__ */ jsx8(
16434
+ displayedSuggestions.length > 0 && /* @__PURE__ */ jsx10(
16090
16435
  "div",
16091
16436
  {
16092
16437
  style: {
@@ -16096,7 +16441,7 @@ function UseAIChatPanel({
16096
16441
  width: "100%",
16097
16442
  maxWidth: "320px"
16098
16443
  },
16099
- children: displayedSuggestions.map((suggestion, index2) => /* @__PURE__ */ jsx8(
16444
+ children: displayedSuggestions.map((suggestion, index2) => /* @__PURE__ */ jsx10(
16100
16445
  "button",
16101
16446
  {
16102
16447
  "data-testid": "chat-suggestion-button",
@@ -16140,7 +16485,7 @@ function UseAIChatPanel({
16140
16485
  ]
16141
16486
  }
16142
16487
  ),
16143
- messages.map((message) => /* @__PURE__ */ jsxs5(
16488
+ messages.map((message) => /* @__PURE__ */ jsxs7(
16144
16489
  "div",
16145
16490
  {
16146
16491
  "data-testid": `chat-message-${message.role}`,
@@ -16153,7 +16498,7 @@ function UseAIChatPanel({
16153
16498
  onMouseEnter: () => message.role === "user" && setHoveredMessageId(message.id),
16154
16499
  onMouseLeave: () => setHoveredMessageId(null),
16155
16500
  children: [
16156
- /* @__PURE__ */ jsxs5(
16501
+ /* @__PURE__ */ jsxs7(
16157
16502
  "div",
16158
16503
  {
16159
16504
  style: {
@@ -16161,7 +16506,7 @@ function UseAIChatPanel({
16161
16506
  maxWidth: "80%"
16162
16507
  },
16163
16508
  children: [
16164
- 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(
16165
16510
  "button",
16166
16511
  {
16167
16512
  "data-testid": "save-command-button",
@@ -16197,14 +16542,14 @@ function UseAIChatPanel({
16197
16542
  e.currentTarget.style.transform = "scale(1)";
16198
16543
  e.currentTarget.style.boxShadow = "0 2px 6px rgba(0, 0, 0, 0.15)";
16199
16544
  },
16200
- children: /* @__PURE__ */ jsxs5("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
16201
- /* @__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" }),
16202
- /* @__PURE__ */ jsx8("polyline", { points: "17 21 17 13 7 13 7 21" }),
16203
- /* @__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" })
16204
16549
  ] })
16205
16550
  }
16206
16551
  ),
16207
- /* @__PURE__ */ jsxs5(
16552
+ /* @__PURE__ */ jsxs7(
16208
16553
  "div",
16209
16554
  {
16210
16555
  "data-testid": "chat-message-content",
@@ -16219,7 +16564,7 @@ function UseAIChatPanel({
16219
16564
  wordWrap: "break-word"
16220
16565
  },
16221
16566
  children: [
16222
- 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(
16223
16568
  FilePlaceholder,
16224
16569
  {
16225
16570
  name: part.file.name,
@@ -16227,7 +16572,7 @@ function UseAIChatPanel({
16227
16572
  },
16228
16573
  idx
16229
16574
  )) }),
16230
- 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)
16231
16576
  ]
16232
16577
  }
16233
16578
  ),
@@ -16238,7 +16583,7 @@ function UseAIChatPanel({
16238
16583
  ]
16239
16584
  }
16240
16585
  ),
16241
- /* @__PURE__ */ jsx8(
16586
+ /* @__PURE__ */ jsx10(
16242
16587
  "div",
16243
16588
  {
16244
16589
  style: {
@@ -16257,14 +16602,14 @@ function UseAIChatPanel({
16257
16602
  },
16258
16603
  message.id
16259
16604
  )),
16260
- loading && /* @__PURE__ */ jsx8(
16605
+ loading && /* @__PURE__ */ jsx10(
16261
16606
  "div",
16262
16607
  {
16263
16608
  style: {
16264
16609
  display: "flex",
16265
16610
  alignItems: "flex-start"
16266
16611
  },
16267
- children: /* @__PURE__ */ jsx8(
16612
+ children: /* @__PURE__ */ jsx10(
16268
16613
  "div",
16269
16614
  {
16270
16615
  className: "markdown-content",
@@ -16277,19 +16622,19 @@ function UseAIChatPanel({
16277
16622
  color: theme.textColor,
16278
16623
  maxWidth: "80%"
16279
16624
  },
16280
- children: streamingText ? /* @__PURE__ */ jsx8(MarkdownContent, { content: streamingText }) : /* @__PURE__ */ jsxs5(Fragment, { children: [
16281
- /* @__PURE__ */ jsx8("span", { style: { opacity: 0.6 }, children: strings.input.thinking }),
16282
- /* @__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: "..." })
16283
16628
  ] })
16284
16629
  }
16285
16630
  )
16286
16631
  }
16287
16632
  ),
16288
- /* @__PURE__ */ jsx8("div", { ref: messagesEndRef })
16633
+ /* @__PURE__ */ jsx10("div", { ref: messagesEndRef })
16289
16634
  ]
16290
16635
  }
16291
16636
  ),
16292
- /* @__PURE__ */ jsxs5(
16637
+ /* @__PURE__ */ jsxs7(
16293
16638
  "div",
16294
16639
  {
16295
16640
  style: {
@@ -16297,7 +16642,7 @@ function UseAIChatPanel({
16297
16642
  borderTop: `1px solid ${theme.borderColor}`
16298
16643
  },
16299
16644
  children: [
16300
- fileError && /* @__PURE__ */ jsx8(
16645
+ fileError && /* @__PURE__ */ jsx10(
16301
16646
  "div",
16302
16647
  {
16303
16648
  "data-testid": "file-error",
@@ -16312,7 +16657,7 @@ function UseAIChatPanel({
16312
16657
  children: fileError
16313
16658
  }
16314
16659
  ),
16315
- attachments.length > 0 && /* @__PURE__ */ jsx8(
16660
+ attachments.length > 0 && /* @__PURE__ */ jsx10(
16316
16661
  "div",
16317
16662
  {
16318
16663
  "data-testid": "file-attachments",
@@ -16322,18 +16667,19 @@ function UseAIChatPanel({
16322
16667
  gap: "8px",
16323
16668
  marginBottom: "8px"
16324
16669
  },
16325
- children: attachments.map((attachment) => /* @__PURE__ */ jsx8(
16670
+ children: attachments.map((attachment) => /* @__PURE__ */ jsx10(
16326
16671
  FileChip,
16327
16672
  {
16328
16673
  attachment,
16329
16674
  onRemove: () => removeAttachment(attachment.id),
16330
- disabled: loading
16675
+ disabled: loading,
16676
+ processingState: fileProcessingState.get(attachment.id)
16331
16677
  },
16332
16678
  attachment.id
16333
16679
  ))
16334
16680
  }
16335
16681
  ),
16336
- /* @__PURE__ */ jsxs5(
16682
+ /* @__PURE__ */ jsxs7(
16337
16683
  "div",
16338
16684
  {
16339
16685
  style: {
@@ -16345,18 +16691,19 @@ function UseAIChatPanel({
16345
16691
  },
16346
16692
  children: [
16347
16693
  slashCommands.AutocompleteComponent,
16348
- /* @__PURE__ */ jsx8(
16694
+ /* @__PURE__ */ jsx10(
16349
16695
  "input",
16350
16696
  {
16351
16697
  ref: fileInputRef,
16352
16698
  type: "file",
16353
16699
  multiple: true,
16700
+ "data-testid": "file-input",
16354
16701
  style: { display: "none" },
16355
16702
  onChange: handleFileInputChange,
16356
16703
  accept: acceptedTypes?.join(",")
16357
16704
  }
16358
16705
  ),
16359
- /* @__PURE__ */ jsx8(
16706
+ /* @__PURE__ */ jsx10(
16360
16707
  "textarea",
16361
16708
  {
16362
16709
  ref: textareaRef,
@@ -16384,7 +16731,7 @@ function UseAIChatPanel({
16384
16731
  }
16385
16732
  }
16386
16733
  ),
16387
- /* @__PURE__ */ jsxs5(
16734
+ /* @__PURE__ */ jsxs7(
16388
16735
  "div",
16389
16736
  {
16390
16737
  style: {
@@ -16394,7 +16741,7 @@ function UseAIChatPanel({
16394
16741
  padding: "4px 8px"
16395
16742
  },
16396
16743
  children: [
16397
- /* @__PURE__ */ jsx8("div", { style: { display: "flex", alignItems: "center", gap: "8px" }, children: fileUploadEnabled && /* @__PURE__ */ jsx8(
16744
+ /* @__PURE__ */ jsx10("div", { style: { display: "flex", alignItems: "center", gap: "8px" }, children: fileUploadEnabled && /* @__PURE__ */ jsx10(
16398
16745
  "button",
16399
16746
  {
16400
16747
  "data-testid": "file-picker-button",
@@ -16426,13 +16773,13 @@ function UseAIChatPanel({
16426
16773
  e.currentTarget.style.borderColor = theme.borderColor;
16427
16774
  },
16428
16775
  title: strings.fileUpload.attachFiles,
16429
- children: /* @__PURE__ */ jsxs5("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: [
16430
- /* @__PURE__ */ jsx8("line", { x1: "12", y1: "5", x2: "12", y2: "19" }),
16431
- /* @__PURE__ */ jsx8("line", { x1: "5", y1: "12", x2: "19", 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" })
16432
16779
  ] })
16433
16780
  }
16434
16781
  ) }),
16435
- /* @__PURE__ */ jsx8(
16782
+ /* @__PURE__ */ jsx10(
16436
16783
  "button",
16437
16784
  {
16438
16785
  "data-testid": "chat-send-button",
@@ -16453,9 +16800,9 @@ function UseAIChatPanel({
16453
16800
  height: "32px",
16454
16801
  transition: "all 0.2s"
16455
16802
  },
16456
- children: /* @__PURE__ */ jsxs5("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: [
16457
- /* @__PURE__ */ jsx8("line", { x1: "12", y1: "19", x2: "12", y2: "5" }),
16458
- /* @__PURE__ */ jsx8("polyline", { points: "5 12 12 5 19 12" })
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" })
16459
16806
  ] })
16460
16807
  }
16461
16808
  )
@@ -16468,7 +16815,7 @@ function UseAIChatPanel({
16468
16815
  ]
16469
16816
  }
16470
16817
  ),
16471
- /* @__PURE__ */ jsx8("style", { children: `
16818
+ /* @__PURE__ */ jsx10("style", { children: `
16472
16819
  /* Markdown content styles */
16473
16820
  .markdown-content > :first-child {
16474
16821
  margin-top: 0 !important;
@@ -16493,7 +16840,7 @@ function UseAIChatPanel({
16493
16840
  }
16494
16841
 
16495
16842
  // src/components/UseAIFloatingChatWrapper.tsx
16496
- 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";
16497
16844
  function UseAIFloatingChatWrapper({
16498
16845
  isOpen,
16499
16846
  onClose,
@@ -16501,8 +16848,8 @@ function UseAIFloatingChatWrapper({
16501
16848
  }) {
16502
16849
  const theme = useTheme();
16503
16850
  if (!isOpen) return null;
16504
- return /* @__PURE__ */ jsxs6(Fragment2, { children: [
16505
- /* @__PURE__ */ jsx9(
16851
+ return /* @__PURE__ */ jsxs8(Fragment2, { children: [
16852
+ /* @__PURE__ */ jsx11(
16506
16853
  "div",
16507
16854
  {
16508
16855
  style: {
@@ -16518,7 +16865,7 @@ function UseAIFloatingChatWrapper({
16518
16865
  onClick: onClose
16519
16866
  }
16520
16867
  ),
16521
- /* @__PURE__ */ jsx9(
16868
+ /* @__PURE__ */ jsx11(
16522
16869
  "div",
16523
16870
  {
16524
16871
  style: {
@@ -16537,7 +16884,7 @@ function UseAIFloatingChatWrapper({
16537
16884
  children
16538
16885
  }
16539
16886
  ),
16540
- /* @__PURE__ */ jsx9("style", { children: `
16887
+ /* @__PURE__ */ jsx11("style", { children: `
16541
16888
  @keyframes fadeIn {
16542
16889
  from { opacity: 0; }
16543
16890
  to { opacity: 1; }
@@ -16557,7 +16904,7 @@ function UseAIFloatingChatWrapper({
16557
16904
  }
16558
16905
  function CloseButton({ onClick }) {
16559
16906
  const theme = useTheme();
16560
- return /* @__PURE__ */ jsx9(
16907
+ return /* @__PURE__ */ jsx11(
16561
16908
  "button",
16562
16909
  {
16563
16910
  "data-testid": "chat-close-button",
@@ -16592,7 +16939,7 @@ function CloseButton({ onClick }) {
16592
16939
 
16593
16940
  // src/components/UseAIChat.tsx
16594
16941
  import { createContext as createContext3, useContext as useContext3 } from "react";
16595
- import { jsx as jsx10 } from "react/jsx-runtime";
16942
+ import { jsx as jsx12 } from "react/jsx-runtime";
16596
16943
  var __UseAIChatContext = createContext3(null);
16597
16944
  function useChatUIContext() {
16598
16945
  const context = useContext3(__UseAIChatContext);
@@ -16616,6 +16963,7 @@ function UseAIChat({ floating = false }) {
16616
16963
  onLoadChat: ctx.history.load,
16617
16964
  onDeleteChat: ctx.history.delete,
16618
16965
  onListChats: ctx.history.list,
16966
+ onGetChat: ctx.history.get,
16619
16967
  suggestions: ctx.suggestions,
16620
16968
  availableAgents: ctx.agents.available,
16621
16969
  defaultAgent: ctx.agents.default,
@@ -16628,22 +16976,22 @@ function UseAIChat({ floating = false }) {
16628
16976
  onDeleteCommand: ctx.commands.delete
16629
16977
  };
16630
16978
  if (floating) {
16631
- return /* @__PURE__ */ jsx10(
16979
+ return /* @__PURE__ */ jsx12(
16632
16980
  UseAIFloatingChatWrapper,
16633
16981
  {
16634
16982
  isOpen: ctx.ui.isOpen,
16635
16983
  onClose: () => ctx.ui.setOpen(false),
16636
- children: /* @__PURE__ */ jsx10(
16984
+ children: /* @__PURE__ */ jsx12(
16637
16985
  UseAIChatPanel,
16638
16986
  {
16639
16987
  ...chatPanelProps,
16640
- closeButton: /* @__PURE__ */ jsx10(CloseButton, { onClick: () => ctx.ui.setOpen(false) })
16988
+ closeButton: /* @__PURE__ */ jsx12(CloseButton, { onClick: () => ctx.ui.setOpen(false) })
16641
16989
  }
16642
16990
  )
16643
16991
  }
16644
16992
  );
16645
16993
  }
16646
- return /* @__PURE__ */ jsx10(UseAIChatPanel, { ...chatPanelProps });
16994
+ return /* @__PURE__ */ jsx12(UseAIChatPanel, { ...chatPanelProps });
16647
16995
  }
16648
16996
 
16649
16997
  // ../../node_modules/.bun/engine.io-parser@5.2.3/node_modules/engine.io-parser/build/esm/commons.js
@@ -20202,12 +20550,18 @@ var UseAIClient = class {
20202
20550
  return { type: "text", text: part.text };
20203
20551
  } else if (part.type === "image") {
20204
20552
  return { type: "image", url: part.url };
20205
- } else {
20553
+ } else if (part.type === "file") {
20206
20554
  return {
20207
20555
  type: "file",
20208
20556
  url: part.url,
20209
20557
  mimeType: part.mimeType
20210
20558
  };
20559
+ } else {
20560
+ return {
20561
+ type: "transformed_file",
20562
+ text: part.text,
20563
+ originalFile: part.originalFile
20564
+ };
20211
20565
  }
20212
20566
  });
20213
20567
  }
@@ -33245,7 +33599,8 @@ var LocalStorageChatRepository = class {
33245
33599
  title: options?.title,
33246
33600
  messages: [],
33247
33601
  createdAt: now,
33248
- updatedAt: now
33602
+ updatedAt: now,
33603
+ metadata: options?.metadata
33249
33604
  };
33250
33605
  await this.enforceMaxChatsLimit();
33251
33606
  await this.saveChat(chat);
@@ -33329,6 +33684,14 @@ var LocalStorageChatRepository = class {
33329
33684
  throw new Error(`Failed to clear all chats: ${error46 instanceof Error ? error46.message : "Unknown error"}`);
33330
33685
  }
33331
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
+ }
33332
33695
  getChatKey(id) {
33333
33696
  return `${STORAGE_KEY_PREFIX}${id}`;
33334
33697
  }
@@ -33367,36 +33730,12 @@ var LocalStorageChatRepository = class {
33367
33730
  }
33368
33731
  };
33369
33732
 
33370
- // src/fileUpload/EmbedFileUploadBackend.ts
33371
- var EmbedFileUploadBackend = class {
33372
- /**
33373
- * Converts a File to a base64 data URL.
33374
- *
33375
- * @param file - The File object to convert
33376
- * @returns Promise resolving to a base64 data URL (e.g., "data:image/png;base64,...")
33377
- * @throws Error if file reading fails
33378
- */
33379
- async prepareForSend(file2) {
33380
- return new Promise((resolve, reject) => {
33381
- const reader = new FileReader();
33382
- reader.onload = () => {
33383
- if (typeof reader.result === "string") {
33384
- resolve(reader.result);
33385
- } else {
33386
- reject(new Error("Failed to read file as data URL"));
33387
- }
33388
- };
33389
- reader.onerror = () => {
33390
- reject(new Error(`Failed to read file: ${file2.name}`));
33391
- };
33392
- reader.readAsDataURL(file2);
33393
- });
33394
- }
33395
- };
33396
-
33397
33733
  // src/hooks/useChatManagement.ts
33398
33734
  import { useState as useState5, useCallback as useCallback4, useRef as useRef5, useEffect as useEffect5 } from "react";
33399
33735
  var CHAT_TITLE_MAX_LENGTH = 50;
33736
+ function deepEquals(a, b) {
33737
+ return JSON.stringify(a) === JSON.stringify(b);
33738
+ }
33400
33739
  function generateChatTitle(message) {
33401
33740
  return message.length > CHAT_TITLE_MAX_LENGTH ? message.substring(0, CHAT_TITLE_MAX_LENGTH) + "..." : message;
33402
33741
  }
@@ -33427,7 +33766,11 @@ function transformMessagesToClientFormat(uiMessages) {
33427
33766
  }
33428
33767
  function useChatManagement({
33429
33768
  repository,
33430
- clientRef
33769
+ clientRef,
33770
+ onSendMessage,
33771
+ setOpen,
33772
+ connected,
33773
+ loading
33431
33774
  }) {
33432
33775
  const [currentChatId, setCurrentChatId] = useState5(null);
33433
33776
  const [pendingChatId, setPendingChatId] = useState5(null);
@@ -33460,18 +33803,18 @@ function useChatManagement({
33460
33803
  const loadedMessages = await loadChatMessages(chatId);
33461
33804
  setMessages(loadedMessages);
33462
33805
  }, [loadChatMessages]);
33463
- const createNewChat = useCallback4(async () => {
33806
+ const createNewChat = useCallback4(async (options) => {
33464
33807
  console.log("[ChatManagement] createNewChat called - currentChatId:", currentChatId, "pendingChatId:", pendingChatId, "messages.length:", messages.length);
33465
33808
  if (pendingChatId && messages.length === 0) {
33466
- console.log("[ChatManagement] Pending chat is already blank, not creating new chat");
33467
- return pendingChatId;
33468
- }
33469
- if (currentChatId && !pendingChatId && messages.length === 0) {
33470
- console.log("[ChatManagement] Current chat is already blank, not creating new chat");
33471
- 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
+ }
33472
33815
  }
33473
33816
  console.log("[ChatManagement] Creating new chat...");
33474
- const chatId = await repository.createChat();
33817
+ const chatId = await repository.createChat(options);
33475
33818
  setPendingChatId(chatId);
33476
33819
  setMessages([]);
33477
33820
  if (clientRef.current) {
@@ -33516,6 +33859,22 @@ function useChatManagement({
33516
33859
  }
33517
33860
  }
33518
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]);
33519
33878
  const activatePendingChat = useCallback4(() => {
33520
33879
  if (!pendingChatId) return null;
33521
33880
  console.log("[ChatManagement] Activating pending chat:", pendingChatId);
@@ -33534,7 +33893,7 @@ function useChatManagement({
33534
33893
  console.error("[ChatManagement] Chat not found:", chatId);
33535
33894
  return false;
33536
33895
  }
33537
- const { generateMessageId: generateMessageId2 } = await import("./types-INERONQV.js");
33896
+ const { generateMessageId: generateMessageId2 } = await import("./types-STDS67SG.js");
33538
33897
  chat.messages.push({
33539
33898
  id: generateMessageId2(),
33540
33899
  role: "user",
@@ -33570,7 +33929,7 @@ function useChatManagement({
33570
33929
  console.error("[ChatManagement] Chat not found:", currentChatIdValue);
33571
33930
  return;
33572
33931
  }
33573
- const { generateMessageId: generateMessageId2 } = await import("./types-INERONQV.js");
33932
+ const { generateMessageId: generateMessageId2 } = await import("./types-STDS67SG.js");
33574
33933
  chat.messages.push({
33575
33934
  id: generateMessageId2(),
33576
33935
  role: "assistant",
@@ -33627,6 +33986,70 @@ function useChatManagement({
33627
33986
  }
33628
33987
  }, [currentChatId, pendingChatId, createNewChat, repository, loadChatMessages, clientRef]);
33629
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]);
33630
34053
  return {
33631
34054
  currentChatId,
33632
34055
  pendingChatId,
@@ -33641,6 +34064,9 @@ function useChatManagement({
33641
34064
  saveUserMessage,
33642
34065
  saveAIResponse,
33643
34066
  reloadMessages,
34067
+ sendMessage,
34068
+ getCurrentChat,
34069
+ updateMetadata,
33644
34070
  currentChatIdSnapshot,
33645
34071
  pendingChatIdSnapshot
33646
34072
  };
@@ -34022,7 +34448,7 @@ function usePromptState({
34022
34448
  }
34023
34449
 
34024
34450
  // src/providers/useAIProvider.tsx
34025
- 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";
34026
34452
  var __UseAIContext = createContext4(null);
34027
34453
  var hasWarnedAboutMissingProvider = false;
34028
34454
  var noOpContextValue = {
@@ -34052,6 +34478,11 @@ var noOpContextValue = {
34052
34478
  },
34053
34479
  list: async () => [],
34054
34480
  clear: async () => {
34481
+ },
34482
+ sendMessage: async () => {
34483
+ },
34484
+ get: async () => null,
34485
+ updateMetadata: async () => {
34055
34486
  }
34056
34487
  },
34057
34488
  agents: {
@@ -34091,7 +34522,8 @@ function UseAIProvider({
34091
34522
  renderChat = true,
34092
34523
  theme: customTheme,
34093
34524
  strings: customStrings,
34094
- visibleAgentIds
34525
+ visibleAgentIds,
34526
+ onOpenChange
34095
34527
  }) {
34096
34528
  const fileUploadConfig = fileUploadConfigProp === false ? void 0 : fileUploadConfigProp ?? DEFAULT_FILE_UPLOAD_CONFIG;
34097
34529
  const theme = { ...defaultTheme, ...customTheme };
@@ -34099,12 +34531,17 @@ function UseAIProvider({
34099
34531
  const [connected, setConnected] = useState10(false);
34100
34532
  const [isChatOpen, setIsChatOpen] = useState10(false);
34101
34533
  const [loading, setLoading] = useState10(false);
34534
+ const handleSetChatOpen = useCallback9((open) => {
34535
+ setIsChatOpen(open);
34536
+ onOpenChange?.(open);
34537
+ }, [onOpenChange]);
34102
34538
  const [streamingText, setStreamingText] = useState10("");
34103
34539
  const streamingChatIdRef = useRef9(null);
34104
34540
  const clientRef = useRef9(null);
34105
34541
  const repositoryRef = useRef9(
34106
34542
  chatRepository || new LocalStorageChatRepository()
34107
34543
  );
34544
+ const handleSendMessageRef = useRef9(null);
34108
34545
  const {
34109
34546
  registerTools,
34110
34547
  unregisterTools,
@@ -34126,9 +34563,18 @@ function UseAIProvider({
34126
34563
  clientRef,
34127
34564
  connected
34128
34565
  });
34566
+ const stableSendMessage = useCallback9(async (message, attachments) => {
34567
+ if (handleSendMessageRef.current) {
34568
+ await handleSendMessageRef.current(message, attachments);
34569
+ }
34570
+ }, []);
34129
34571
  const chatManagement = useChatManagement({
34130
34572
  repository: repositoryRef.current,
34131
- clientRef
34573
+ clientRef,
34574
+ onSendMessage: stableSendMessage,
34575
+ setOpen: handleSetChatOpen,
34576
+ connected,
34577
+ loading
34132
34578
  });
34133
34579
  const {
34134
34580
  currentChatId,
@@ -34142,7 +34588,10 @@ function UseAIProvider({
34142
34588
  clearCurrentChat,
34143
34589
  activatePendingChat,
34144
34590
  saveUserMessage,
34145
- saveAIResponse
34591
+ saveAIResponse,
34592
+ sendMessage,
34593
+ getCurrentChat,
34594
+ updateMetadata
34146
34595
  } = chatManagement;
34147
34596
  const {
34148
34597
  availableAgents,
@@ -34283,7 +34732,6 @@ function UseAIProvider({
34283
34732
  let persistedContent = message;
34284
34733
  let multimodalContent;
34285
34734
  if (attachments && attachments.length > 0) {
34286
- const backend = fileUploadConfig?.backend ?? new EmbedFileUploadBackend();
34287
34735
  const persistedParts = [];
34288
34736
  if (message.trim()) {
34289
34737
  persistedParts.push({ type: "text", text: message });
@@ -34299,35 +34747,24 @@ function UseAIProvider({
34299
34747
  });
34300
34748
  }
34301
34749
  persistedContent = persistedParts;
34302
- const contentParts = [];
34750
+ const fileContent = await processAttachments(attachments, {
34751
+ getCurrentChat,
34752
+ backend: fileUploadConfig?.backend,
34753
+ transformers: fileUploadConfig?.transformers
34754
+ });
34755
+ multimodalContent = [];
34303
34756
  if (message.trim()) {
34304
- contentParts.push({ type: "text", text: message });
34305
- }
34306
- for (const attachment of attachments) {
34307
- try {
34308
- const url3 = await backend.prepareForSend(attachment.file);
34309
- if (attachment.file.type.startsWith("image/")) {
34310
- contentParts.push({ type: "image", url: url3 });
34311
- } else {
34312
- contentParts.push({
34313
- type: "file",
34314
- url: url3,
34315
- mimeType: attachment.file.type,
34316
- name: attachment.file.name
34317
- });
34318
- }
34319
- } catch (error46) {
34320
- console.error("[Provider] Failed to prepare file for send:", error46);
34321
- }
34757
+ multimodalContent.push({ type: "text", text: message });
34322
34758
  }
34323
- multimodalContent = contentParts;
34759
+ multimodalContent.push(...fileContent);
34324
34760
  }
34325
34761
  if (activeChatId) {
34326
34762
  await saveUserMessage(activeChatId, persistedContent);
34327
34763
  }
34328
34764
  setLoading(true);
34329
34765
  await clientRef.current.sendPrompt(message, multimodalContent);
34330
- }, [activatePendingChat, currentChatId, saveUserMessage, fileUploadConfig]);
34766
+ }, [activatePendingChat, currentChatId, saveUserMessage, fileUploadConfig, getCurrentChat]);
34767
+ handleSendMessageRef.current = handleSendMessage;
34331
34768
  const value2 = {
34332
34769
  serverUrl,
34333
34770
  connected,
@@ -34347,7 +34784,10 @@ function UseAIProvider({
34347
34784
  load: loadChat,
34348
34785
  delete: deleteChat,
34349
34786
  list: listChats,
34350
- clear: clearCurrentChat
34787
+ clear: clearCurrentChat,
34788
+ sendMessage,
34789
+ get: getCurrentChat,
34790
+ updateMetadata
34351
34791
  },
34352
34792
  agents: {
34353
34793
  available: availableAgents,
@@ -34377,7 +34817,8 @@ function UseAIProvider({
34377
34817
  create: createNewChat,
34378
34818
  load: loadChat,
34379
34819
  delete: deleteChat,
34380
- list: listChats
34820
+ list: listChats,
34821
+ get: getCurrentChat
34381
34822
  },
34382
34823
  agents: {
34383
34824
  available: availableAgents,
@@ -34393,7 +34834,7 @@ function UseAIProvider({
34393
34834
  },
34394
34835
  ui: {
34395
34836
  isOpen: isChatOpen,
34396
- setOpen: setIsChatOpen
34837
+ setOpen: handleSetChatOpen
34397
34838
  }
34398
34839
  };
34399
34840
  const isUIDisabled = CustomButton === null || CustomChat === null;
@@ -34423,21 +34864,21 @@ function UseAIProvider({
34423
34864
  };
34424
34865
  const renderDefaultChat = () => {
34425
34866
  if (isUIDisabled) return null;
34426
- 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(
34427
34868
  UseAIChatPanel,
34428
34869
  {
34429
34870
  ...chatPanelProps,
34430
- closeButton: /* @__PURE__ */ jsx11(CloseButton, { onClick: () => setIsChatOpen(false) })
34871
+ closeButton: /* @__PURE__ */ jsx13(CloseButton, { onClick: () => handleSetChatOpen(false) })
34431
34872
  }
34432
34873
  ) });
34433
34874
  };
34434
34875
  const renderCustomChat = () => {
34435
34876
  if (!CustomChat) return null;
34436
- return /* @__PURE__ */ jsx11(
34877
+ return /* @__PURE__ */ jsx13(
34437
34878
  CustomChat,
34438
34879
  {
34439
34880
  isOpen: isChatOpen,
34440
- onClose: () => setIsChatOpen(false),
34881
+ onClose: () => handleSetChatOpen(false),
34441
34882
  onSendMessage: handleSendMessage,
34442
34883
  messages,
34443
34884
  loading,
@@ -34452,18 +34893,18 @@ function UseAIProvider({
34452
34893
  };
34453
34894
  const renderBuiltInChat = () => {
34454
34895
  if (!renderChat) return null;
34455
- return /* @__PURE__ */ jsxs7(Fragment3, { children: [
34456
- ButtonComponent && /* @__PURE__ */ jsx11(
34896
+ return /* @__PURE__ */ jsxs9(Fragment3, { children: [
34897
+ ButtonComponent && /* @__PURE__ */ jsx13(
34457
34898
  ButtonComponent,
34458
34899
  {
34459
- onClick: () => setIsChatOpen(true),
34900
+ onClick: () => handleSetChatOpen(true),
34460
34901
  connected
34461
34902
  }
34462
34903
  ),
34463
34904
  hasCustomChat ? renderCustomChat() : renderDefaultChat()
34464
34905
  ] });
34465
34906
  };
34466
- 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: [
34467
34908
  children,
34468
34909
  renderBuiltInChat()
34469
34910
  ] }) }) }) });
@@ -34861,14 +35302,18 @@ export {
34861
35302
  UseAIFloatingButton,
34862
35303
  UseAIFloatingChatWrapper,
34863
35304
  UseAIProvider,
35305
+ clearTransformationCache,
34864
35306
  convertToolsToDefinitions,
34865
35307
  defaultStrings,
34866
35308
  defaultTheme,
34867
35309
  defineTool,
34868
35310
  executeDefinedTool,
35311
+ findTransformer,
34869
35312
  generateChatId,
34870
35313
  generateCommandId,
34871
35314
  generateMessageId,
35315
+ matchesMimeType,
35316
+ processAttachments,
34872
35317
  useAI,
34873
35318
  useAIContext,
34874
35319
  useAIWorkflow,