@copilotz/chat-ui 0.1.32 → 0.1.34

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -32,17 +32,20 @@ var defaultChatConfig = {
32
32
  recordAudioTooltip: "Record audio",
33
33
  voiceEnter: "Voice input",
34
34
  voiceExit: "Use keyboard",
35
- voiceTitle: "Voice input",
35
+ voiceTitle: "Voice",
36
+ voiceIdle: "Tap the mic to record",
36
37
  voicePreparing: "Preparing microphone...",
37
38
  voiceWaiting: "Waiting for speech...",
38
39
  voiceListening: "Listening...",
39
40
  voiceFinishing: "Finishing capture...",
40
41
  voiceReview: "Ready to send",
42
+ voiceSending: "Sending...",
41
43
  voiceStart: "Start recording",
42
44
  voiceStop: "Stop recording",
43
45
  voiceSendNow: "Send now",
44
46
  voiceCancel: "Cancel",
45
- voiceRecordAgain: "Record again",
47
+ voiceDiscard: "Delete recording",
48
+ voiceRecordAgain: "Continue recording",
46
49
  voiceAutoSendIn: "Auto-sends in {{seconds}}s",
47
50
  voiceTranscriptPending: "Transcript unavailable",
48
51
  voicePermissionDenied: "Microphone access was denied.",
@@ -111,6 +114,7 @@ var defaultChatConfig = {
111
114
  },
112
115
  voiceCompose: {
113
116
  enabled: false,
117
+ defaultMode: "text",
114
118
  autoSendDelayMs: 5e3,
115
119
  persistComposer: true,
116
120
  showTranscriptPreview: true,
@@ -2846,6 +2850,121 @@ var blobToDataUrl = (blob) => new Promise((resolve, reject) => {
2846
2850
  reader.onerror = () => reject(reader.error ?? new Error("Failed to read recorded audio"));
2847
2851
  reader.readAsDataURL(blob);
2848
2852
  });
2853
+ var joinTranscriptParts = (...parts) => {
2854
+ const value = parts.map((part) => part?.trim()).filter((part) => Boolean(part && part.length > 0)).join(" ").trim();
2855
+ return value.length > 0 ? value : void 0;
2856
+ };
2857
+ var getAudioContextCtor = () => globalThis.AudioContext || globalThis.webkitAudioContext;
2858
+ var getOfflineAudioContextCtor = () => globalThis.OfflineAudioContext || globalThis.webkitOfflineAudioContext;
2859
+ var attachmentToArrayBuffer = async (attachment) => {
2860
+ const response = await fetch(attachment.dataUrl);
2861
+ return response.arrayBuffer();
2862
+ };
2863
+ var decodeAudioAttachment = async (attachment) => {
2864
+ const AudioContextCtor = getAudioContextCtor();
2865
+ if (!AudioContextCtor) {
2866
+ throw new Error("Audio decoding is not supported in this browser");
2867
+ }
2868
+ const audioContext = new AudioContextCtor();
2869
+ try {
2870
+ const arrayBuffer = await attachmentToArrayBuffer(attachment);
2871
+ return await audioContext.decodeAudioData(arrayBuffer.slice(0));
2872
+ } finally {
2873
+ await closeAudioContext(audioContext);
2874
+ }
2875
+ };
2876
+ var renderMergedBuffer = async (buffers) => {
2877
+ const OfflineAudioContextCtor = getOfflineAudioContextCtor();
2878
+ if (!OfflineAudioContextCtor) {
2879
+ throw new Error("Offline audio rendering is not supported in this browser");
2880
+ }
2881
+ const numberOfChannels = Math.max(...buffers.map((buffer) => buffer.numberOfChannels));
2882
+ const sampleRate = Math.max(...buffers.map((buffer) => buffer.sampleRate));
2883
+ const totalFrames = Math.max(1, Math.ceil(buffers.reduce((sum, buffer) => sum + buffer.duration * sampleRate, 0)));
2884
+ const offlineContext = new OfflineAudioContextCtor(numberOfChannels, totalFrames, sampleRate);
2885
+ let offsetSeconds = 0;
2886
+ for (const buffer of buffers) {
2887
+ const source = offlineContext.createBufferSource();
2888
+ source.buffer = buffer;
2889
+ source.connect(offlineContext.destination);
2890
+ source.start(offsetSeconds);
2891
+ offsetSeconds += buffer.duration;
2892
+ }
2893
+ return offlineContext.startRendering();
2894
+ };
2895
+ var encodeWav = (audioBuffer) => {
2896
+ const numberOfChannels = audioBuffer.numberOfChannels;
2897
+ const sampleRate = audioBuffer.sampleRate;
2898
+ const bitsPerSample = 16;
2899
+ const bytesPerSample = bitsPerSample / 8;
2900
+ const dataLength = audioBuffer.length * numberOfChannels * bytesPerSample;
2901
+ const buffer = new ArrayBuffer(44 + dataLength);
2902
+ const view = new DataView(buffer);
2903
+ const writeString = (offset2, value) => {
2904
+ for (let index = 0; index < value.length; index += 1) {
2905
+ view.setUint8(offset2 + index, value.charCodeAt(index));
2906
+ }
2907
+ };
2908
+ writeString(0, "RIFF");
2909
+ view.setUint32(4, 36 + dataLength, true);
2910
+ writeString(8, "WAVE");
2911
+ writeString(12, "fmt ");
2912
+ view.setUint32(16, 16, true);
2913
+ view.setUint16(20, 1, true);
2914
+ view.setUint16(22, numberOfChannels, true);
2915
+ view.setUint32(24, sampleRate, true);
2916
+ view.setUint32(28, sampleRate * numberOfChannels * bytesPerSample, true);
2917
+ view.setUint16(32, numberOfChannels * bytesPerSample, true);
2918
+ view.setUint16(34, bitsPerSample, true);
2919
+ writeString(36, "data");
2920
+ view.setUint32(40, dataLength, true);
2921
+ let offset = 44;
2922
+ const channelData = Array.from({ length: numberOfChannels }, (_, index) => audioBuffer.getChannelData(index));
2923
+ for (let sampleIndex = 0; sampleIndex < audioBuffer.length; sampleIndex += 1) {
2924
+ for (let channelIndex = 0; channelIndex < numberOfChannels; channelIndex += 1) {
2925
+ const sample = Math.max(-1, Math.min(1, channelData[channelIndex][sampleIndex]));
2926
+ const pcmValue = sample < 0 ? sample * 32768 : sample * 32767;
2927
+ view.setInt16(offset, pcmValue, true);
2928
+ offset += 2;
2929
+ }
2930
+ }
2931
+ return new Blob([buffer], { type: "audio/wav" });
2932
+ };
2933
+ var resolveSegmentCount = (segment) => {
2934
+ const candidate = segment?.metadata?.segmentCount;
2935
+ return typeof candidate === "number" && Number.isFinite(candidate) && candidate > 0 ? candidate : segment ? 1 : 0;
2936
+ };
2937
+ var mergeVoiceTranscripts = (previous, incoming) => ({
2938
+ final: joinTranscriptParts(previous?.final, incoming?.final),
2939
+ partial: joinTranscriptParts(previous?.final, incoming?.partial)
2940
+ });
2941
+ var appendVoiceSegments = async (previous, incoming) => {
2942
+ const [previousBuffer, incomingBuffer] = await Promise.all([
2943
+ decodeAudioAttachment(previous.attachment),
2944
+ decodeAudioAttachment(incoming.attachment)
2945
+ ]);
2946
+ const mergedBuffer = await renderMergedBuffer([previousBuffer, incomingBuffer]);
2947
+ const mergedBlob = encodeWav(mergedBuffer);
2948
+ const dataUrl = await blobToDataUrl(mergedBlob);
2949
+ const segmentCount = resolveSegmentCount(previous) + resolveSegmentCount(incoming);
2950
+ return {
2951
+ attachment: {
2952
+ kind: "audio",
2953
+ dataUrl,
2954
+ mimeType: mergedBlob.type,
2955
+ durationMs: Math.round(mergedBuffer.duration * 1e3),
2956
+ fileName: `voice-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}.wav`,
2957
+ size: mergedBlob.size
2958
+ },
2959
+ transcript: mergeVoiceTranscripts(previous.transcript, incoming.transcript),
2960
+ metadata: {
2961
+ ...previous.metadata,
2962
+ ...incoming.metadata,
2963
+ segmentCount,
2964
+ source: segmentCount > 1 ? "merged" : incoming.metadata?.source ?? previous.metadata?.source
2965
+ }
2966
+ };
2967
+ };
2849
2968
  var stopStream = (stream) => {
2850
2969
  if (!stream) return;
2851
2970
  stream.getTracks().forEach((track) => track.stop());
@@ -2967,7 +3086,7 @@ var createManualVoiceProvider = async (handlers, options = {}) => {
2967
3086
  fileName: `voice-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}.webm`,
2968
3087
  size: blob.size
2969
3088
  },
2970
- metadata: { source: "manual" }
3089
+ metadata: { source: "manual", segmentCount: 1 }
2971
3090
  });
2972
3091
  } else {
2973
3092
  handlers.onStateChange?.("idle");
@@ -3063,8 +3182,8 @@ function Progress({
3063
3182
  }
3064
3183
 
3065
3184
  // src/components/chat/VoiceComposer.tsx
3066
- import { Keyboard, Loader2, Mic, RotateCcw as RotateCcw2, Send, Square, X as X2 } from "lucide-react";
3067
- import { Fragment as Fragment4, jsx as jsx21, jsxs as jsxs11 } from "react/jsx-runtime";
3185
+ import { Keyboard, Loader2, Mic, Send, Square, Trash2 as Trash23, X as X2 } from "lucide-react";
3186
+ import { jsx as jsx21, jsxs as jsxs11 } from "react/jsx-runtime";
3068
3187
  var formatDuration = (durationMs) => {
3069
3188
  const totalSeconds = Math.max(0, Math.floor(durationMs / 1e3));
3070
3189
  const minutes = Math.floor(totalSeconds / 60);
@@ -3093,12 +3212,12 @@ var resolveStateLabel = (state, labels, errorMessage) => {
3093
3212
  case "review":
3094
3213
  return labels?.voiceReview || "Ready to send";
3095
3214
  case "sending":
3096
- return "Sending...";
3215
+ return labels?.voiceSending || "Sending...";
3097
3216
  case "error":
3098
3217
  return errorMessage || labels?.voiceCaptureError || "Unable to capture audio.";
3099
3218
  case "idle":
3100
3219
  default:
3101
- return labels?.voiceTitle || "Voice input";
3220
+ return labels?.voiceIdle || "Tap the mic to record";
3102
3221
  }
3103
3222
  };
3104
3223
  var resolveTranscriptText = (transcript, transcriptMode) => {
@@ -3115,18 +3234,21 @@ var VoiceComposer = ({
3115
3234
  transcript,
3116
3235
  transcriptMode,
3117
3236
  showTranscriptPreview,
3237
+ attachment,
3118
3238
  durationMs,
3119
3239
  audioLevel,
3120
3240
  countdownMs,
3121
3241
  autoSendDelayMs,
3242
+ isAutoSendActive,
3122
3243
  errorMessage,
3123
3244
  disabled = false,
3124
3245
  labels,
3125
3246
  onStart,
3126
3247
  onStop,
3127
- onCancel,
3128
- onSendNow,
3248
+ onCancelAutoSend,
3249
+ onDiscard,
3129
3250
  onRecordAgain,
3251
+ onSendNow,
3130
3252
  onExit
3131
3253
  }) => {
3132
3254
  const transcriptText = resolveTranscriptText(transcript, transcriptMode);
@@ -3134,12 +3256,14 @@ var VoiceComposer = ({
3134
3256
  const countdownValue = autoSendDelayMs > 0 ? Math.min(100, Math.max(0, (autoSendDelayMs - countdownMs) / autoSendDelayMs * 100)) : 100;
3135
3257
  const isBusy = state === "preparing" || state === "finishing" || state === "sending";
3136
3258
  const isCapturing = state === "waiting_for_speech" || state === "listening";
3259
+ const isReviewing = state === "review";
3137
3260
  const levelValue = isCapturing || state === "preparing" || state === "finishing" ? Math.max(8, Math.round(audioLevel * 100)) : 0;
3138
- return /* @__PURE__ */ jsxs11("div", { className: "w-full md:min-w-3xl max-w-3xl rounded-xl border bg-background p-4 shadow-sm", children: [
3139
- /* @__PURE__ */ jsxs11("div", { className: "flex items-center justify-between gap-3", children: [
3140
- /* @__PURE__ */ jsxs11("div", { className: "flex items-center gap-2", children: [
3141
- /* @__PURE__ */ jsx21(Badge, { variant: "outline", children: labels?.voiceTitle || "Voice input" }),
3142
- /* @__PURE__ */ jsx21("span", { className: "text-sm text-muted-foreground", children: resolveStateLabel(state, labels, errorMessage) })
3261
+ const headerLabel = state === "error" ? labels?.voiceCaptureError || "Unable to capture audio." : resolveStateLabel(state, labels, errorMessage);
3262
+ return /* @__PURE__ */ jsxs11("div", { className: "w-full max-w-3xl rounded-xl border bg-background p-3 shadow-sm sm:p-4 md:min-w-3xl", children: [
3263
+ /* @__PURE__ */ jsxs11("div", { className: "flex items-center justify-between gap-2 sm:gap-3", children: [
3264
+ /* @__PURE__ */ jsxs11("div", { className: "flex min-w-0 items-center gap-2", children: [
3265
+ /* @__PURE__ */ jsx21(Badge, { variant: "outline", children: labels?.voiceTitle || "Voice" }),
3266
+ /* @__PURE__ */ jsx21("span", { className: "truncate text-xs sm:text-sm text-muted-foreground", children: headerLabel })
3143
3267
  ] }),
3144
3268
  /* @__PURE__ */ jsxs11(
3145
3269
  Button,
@@ -3147,71 +3271,89 @@ var VoiceComposer = ({
3147
3271
  type: "button",
3148
3272
  variant: "ghost",
3149
3273
  size: "sm",
3274
+ className: "shrink-0 px-2 sm:px-3",
3150
3275
  onClick: onExit,
3151
3276
  disabled: disabled || isBusy,
3152
3277
  children: [
3153
3278
  /* @__PURE__ */ jsx21(Keyboard, { className: "h-4 w-4" }),
3154
- labels?.voiceExit || "Use keyboard"
3279
+ /* @__PURE__ */ jsx21("span", { className: "hidden sm:inline", children: labels?.voiceExit || "Use keyboard" })
3155
3280
  ]
3156
3281
  }
3157
3282
  )
3158
3283
  ] }),
3159
- /* @__PURE__ */ jsxs11("div", { className: "mt-4 flex flex-col items-center gap-4 rounded-xl border border-dashed border-primary/30 bg-primary/5 px-4 py-6 text-center", children: [
3160
- /* @__PURE__ */ jsx21("div", { className: "flex h-20 w-20 items-center justify-center rounded-full bg-primary/10", children: isBusy ? /* @__PURE__ */ jsx21(Loader2, { className: "h-8 w-8 animate-spin text-primary" }) : isCapturing ? /* @__PURE__ */ jsx21(Square, { className: "h-8 w-8 text-primary" }) : /* @__PURE__ */ jsx21(Mic, { className: "h-8 w-8 text-primary" }) }),
3161
- /* @__PURE__ */ jsxs11("div", { className: "w-full max-w-md space-y-2", children: [
3284
+ !isReviewing ? /* @__PURE__ */ jsx21("div", { className: "mt-3 rounded-xl border border-dashed border-primary/30 bg-primary/5 px-3 py-3 text-center sm:px-4 sm:py-4", children: /* @__PURE__ */ jsxs11("div", { className: "mx-auto flex w-full max-w-sm flex-col items-center gap-3", children: [
3285
+ /* @__PURE__ */ jsx21(
3286
+ Button,
3287
+ {
3288
+ type: "button",
3289
+ size: "icon",
3290
+ variant: isCapturing ? "destructive" : "outline",
3291
+ className: `h-16 w-16 rounded-full sm:h-20 sm:w-20 ${isCapturing ? "bg-red-500 hover:bg-red-600 text-white border-red-500" : "border-red-200 bg-red-50 text-red-600 hover:bg-red-100 hover:text-red-700"}`,
3292
+ onClick: isCapturing ? onStop : onStart,
3293
+ disabled: disabled || isBusy,
3294
+ children: isBusy ? /* @__PURE__ */ jsx21(Loader2, { className: "h-7 w-7 animate-spin" }) : isCapturing ? /* @__PURE__ */ jsx21(Square, { className: "h-7 w-7" }) : /* @__PURE__ */ jsx21(Mic, { className: "h-7 w-7" })
3295
+ }
3296
+ ),
3297
+ /* @__PURE__ */ jsxs11("div", { className: "w-full space-y-2", children: [
3162
3298
  /* @__PURE__ */ jsx21(Progress, { value: levelValue, className: "h-2" }),
3163
3299
  /* @__PURE__ */ jsxs11("div", { className: "flex items-center justify-between text-xs text-muted-foreground", children: [
3164
3300
  /* @__PURE__ */ jsx21("span", { children: formatDuration(durationMs) }),
3165
- /* @__PURE__ */ jsx21("span", { children: resolveStateLabel(state, labels, errorMessage) })
3301
+ /* @__PURE__ */ jsx21("span", { children: isCapturing ? labels?.voiceStop || "Stop recording" : labels?.voiceStart || "Start recording" })
3166
3302
  ] })
3167
3303
  ] }),
3168
- showTranscriptPreview && transcriptMode !== "none" && transcriptText && /* @__PURE__ */ jsx21("div", { className: "w-full max-w-md rounded-lg border bg-background px-3 py-2 text-left text-sm", children: transcriptText })
3169
- ] }),
3170
- state === "review" && autoSendDelayMs > 0 && /* @__PURE__ */ jsxs11("div", { className: "mt-4 space-y-2", children: [
3171
- /* @__PURE__ */ jsx21(Progress, { value: countdownValue, className: "h-2" }),
3172
- /* @__PURE__ */ jsx21("div", { className: "text-center text-xs text-muted-foreground", children: interpolateSeconds(labels?.voiceAutoSendIn, countdownSeconds) })
3173
- ] }),
3174
- state === "error" && errorMessage && /* @__PURE__ */ jsx21("div", { className: "mt-4 rounded-lg border border-destructive/30 bg-destructive/5 px-3 py-2 text-sm text-destructive", children: errorMessage }),
3175
- /* @__PURE__ */ jsxs11("div", { className: "mt-4 flex flex-wrap items-center justify-center gap-2", children: [
3176
- state === "idle" && /* @__PURE__ */ jsxs11(Button, { type: "button", onClick: onStart, disabled, children: [
3177
- /* @__PURE__ */ jsx21(Mic, { className: "h-4 w-4" }),
3178
- labels?.voiceStart || "Start recording"
3179
- ] }),
3180
- isCapturing && /* @__PURE__ */ jsxs11(Fragment4, { children: [
3181
- /* @__PURE__ */ jsxs11(Button, { type: "button", onClick: onStop, disabled, children: [
3182
- /* @__PURE__ */ jsx21(Square, { className: "h-4 w-4" }),
3183
- labels?.voiceStop || "Stop recording"
3304
+ showTranscriptPreview && transcriptMode !== "none" && transcriptText && /* @__PURE__ */ jsx21("div", { className: "w-full rounded-lg border bg-background px-3 py-2 text-left text-sm", children: transcriptText })
3305
+ ] }) }) : /* @__PURE__ */ jsxs11("div", { className: "mt-3 rounded-xl border bg-muted/20 p-3 sm:p-4", children: [
3306
+ /* @__PURE__ */ jsxs11("div", { className: "flex items-start justify-between gap-2", children: [
3307
+ /* @__PURE__ */ jsxs11("div", { className: "min-w-0", children: [
3308
+ /* @__PURE__ */ jsx21("div", { className: "text-sm font-medium text-foreground", children: labels?.voiceReview || "Ready to send" }),
3309
+ /* @__PURE__ */ jsx21("div", { className: "text-xs text-muted-foreground", children: formatDuration(durationMs) })
3184
3310
  ] }),
3185
- /* @__PURE__ */ jsxs11(Button, { type: "button", variant: "outline", onClick: onCancel, disabled, children: [
3186
- /* @__PURE__ */ jsx21(X2, { className: "h-4 w-4" }),
3187
- labels?.voiceCancel || "Cancel"
3188
- ] })
3311
+ /* @__PURE__ */ jsx21(
3312
+ Button,
3313
+ {
3314
+ type: "button",
3315
+ variant: "ghost",
3316
+ size: "icon",
3317
+ className: "h-8 w-8 shrink-0 text-muted-foreground hover:text-destructive",
3318
+ onClick: onDiscard,
3319
+ disabled,
3320
+ "aria-label": labels?.voiceDiscard || "Delete recording",
3321
+ title: labels?.voiceDiscard || "Delete recording",
3322
+ children: /* @__PURE__ */ jsx21(Trash23, { className: "h-4 w-4" })
3323
+ }
3324
+ )
3325
+ ] }),
3326
+ attachment && /* @__PURE__ */ jsx21("div", { className: "mt-3 rounded-lg bg-background p-2", children: /* @__PURE__ */ jsx21("audio", { controls: true, preload: "metadata", className: "w-full", children: /* @__PURE__ */ jsx21("source", { src: attachment.dataUrl, type: attachment.mimeType }) }) }),
3327
+ showTranscriptPreview && transcriptMode !== "none" && transcriptText && /* @__PURE__ */ jsx21("div", { className: "mt-3 rounded-lg border bg-background px-3 py-2 text-left text-sm", children: transcriptText }),
3328
+ isAutoSendActive && autoSendDelayMs > 0 && /* @__PURE__ */ jsxs11("div", { className: "mt-3 space-y-2", children: [
3329
+ /* @__PURE__ */ jsx21(Progress, { value: countdownValue, className: "h-2" }),
3330
+ /* @__PURE__ */ jsx21("div", { className: "text-center text-xs text-muted-foreground", children: interpolateSeconds(labels?.voiceAutoSendIn, countdownSeconds) })
3189
3331
  ] }),
3190
- state === "review" && /* @__PURE__ */ jsxs11(Fragment4, { children: [
3191
- /* @__PURE__ */ jsxs11(Button, { type: "button", variant: "outline", onClick: onCancel, disabled, children: [
3332
+ /* @__PURE__ */ jsxs11("div", { className: "mt-3 flex items-center justify-end gap-2", children: [
3333
+ isAutoSendActive && /* @__PURE__ */ jsxs11(Button, { type: "button", variant: "ghost", size: "sm", onClick: onCancelAutoSend, disabled, children: [
3192
3334
  /* @__PURE__ */ jsx21(X2, { className: "h-4 w-4" }),
3193
3335
  labels?.voiceCancel || "Cancel"
3194
3336
  ] }),
3195
- /* @__PURE__ */ jsxs11(Button, { type: "button", variant: "outline", onClick: onRecordAgain, disabled, children: [
3196
- /* @__PURE__ */ jsx21(RotateCcw2, { className: "h-4 w-4" }),
3197
- labels?.voiceRecordAgain || "Record again"
3198
- ] }),
3199
- /* @__PURE__ */ jsxs11(Button, { type: "button", onClick: onSendNow, disabled, children: [
3337
+ !isAutoSendActive && /* @__PURE__ */ jsx21(
3338
+ Button,
3339
+ {
3340
+ type: "button",
3341
+ variant: "outline",
3342
+ size: "icon",
3343
+ onClick: onRecordAgain,
3344
+ disabled,
3345
+ "aria-label": labels?.voiceRecordAgain || "Record again",
3346
+ title: labels?.voiceRecordAgain || "Record again",
3347
+ children: /* @__PURE__ */ jsx21(Mic, { className: "h-4 w-4" })
3348
+ }
3349
+ ),
3350
+ /* @__PURE__ */ jsxs11(Button, { type: "button", size: "sm", onClick: onSendNow, disabled, children: [
3200
3351
  /* @__PURE__ */ jsx21(Send, { className: "h-4 w-4" }),
3201
3352
  labels?.voiceSendNow || "Send now"
3202
3353
  ] })
3203
- ] }),
3204
- state === "error" && /* @__PURE__ */ jsxs11(Fragment4, { children: [
3205
- /* @__PURE__ */ jsxs11(Button, { type: "button", variant: "outline", onClick: onCancel, disabled, children: [
3206
- /* @__PURE__ */ jsx21(X2, { className: "h-4 w-4" }),
3207
- labels?.voiceCancel || "Cancel"
3208
- ] }),
3209
- /* @__PURE__ */ jsxs11(Button, { type: "button", onClick: onRecordAgain, disabled, children: [
3210
- /* @__PURE__ */ jsx21(RotateCcw2, { className: "h-4 w-4" }),
3211
- labels?.voiceRecordAgain || "Record again"
3212
- ] })
3213
3354
  ] })
3214
- ] })
3355
+ ] }),
3356
+ errorMessage && /* @__PURE__ */ jsx21("div", { className: "mt-3 rounded-lg border border-destructive/30 bg-destructive/5 px-3 py-2 text-sm text-destructive", children: errorMessage })
3215
3357
  ] });
3216
3358
  };
3217
3359
 
@@ -3229,7 +3371,7 @@ import {
3229
3371
  Pause,
3230
3372
  Loader2 as Loader22
3231
3373
  } from "lucide-react";
3232
- import { Fragment as Fragment5, jsx as jsx22, jsxs as jsxs12 } from "react/jsx-runtime";
3374
+ import { Fragment as Fragment4, jsx as jsx22, jsxs as jsxs12 } from "react/jsx-runtime";
3233
3375
  var FileUploadItem = memo2(function FileUploadItem2({ file, progress, onCancel }) {
3234
3376
  const guessTypeFromName = (name) => {
3235
3377
  const ext = (name || "").split(".").pop()?.toLowerCase();
@@ -3330,7 +3472,7 @@ var AttachmentPreview = memo2(function AttachmentPreview2({ attachment, onRemove
3330
3472
  "img",
3331
3473
  {
3332
3474
  src: attachment.dataUrl,
3333
- alt: attachment.fileName || "Anexo",
3475
+ alt: attachment.fileName || "Attachment",
3334
3476
  className: "w-full h-20 object-cover rounded"
3335
3477
  }
3336
3478
  ),
@@ -3379,7 +3521,7 @@ var AttachmentPreview = memo2(function AttachmentPreview2({ attachment, onRemove
3379
3521
  }
3380
3522
  ),
3381
3523
  /* @__PURE__ */ jsxs12("div", { className: "flex-1", children: [
3382
- /* @__PURE__ */ jsx22("p", { className: "text-xs font-medium", children: attachment.fileName || "\xC1udio" }),
3524
+ /* @__PURE__ */ jsx22("p", { className: "text-xs font-medium", children: attachment.fileName || "Audio" }),
3383
3525
  /* @__PURE__ */ jsx22("p", { className: "text-xs text-muted-foreground", children: formatDuration2(attachment.durationMs) })
3384
3526
  ] }),
3385
3527
  /* @__PURE__ */ jsx22(
@@ -3431,7 +3573,7 @@ var AudioRecorder = memo2(function AudioRecorder2({ isRecording, onStartRecordin
3431
3573
  return /* @__PURE__ */ jsx22(Card, { className: "border-red-200 bg-red-50 dark:border-red-800 dark:bg-red-950", children: /* @__PURE__ */ jsx22(CardContent, { className: "p-3", children: /* @__PURE__ */ jsxs12("div", { className: "flex items-center gap-3", children: [
3432
3574
  /* @__PURE__ */ jsxs12("div", { className: "flex items-center gap-2", children: [
3433
3575
  /* @__PURE__ */ jsx22("div", { className: "h-3 w-3 bg-red-500 rounded-full animate-pulse" }),
3434
- /* @__PURE__ */ jsx22("span", { className: "text-sm font-medium text-red-700 dark:text-red-300", children: "Gravando" })
3576
+ /* @__PURE__ */ jsx22("span", { className: "text-sm font-medium text-red-700 dark:text-red-300", children: config?.labels?.voiceListening || "Recording" })
3435
3577
  ] }),
3436
3578
  /* @__PURE__ */ jsx22(Badge, { variant: "outline", className: "text-xs", children: formatTime(recordingDuration) }),
3437
3579
  /* @__PURE__ */ jsxs12("div", { className: "flex gap-1 ml-auto", children: [
@@ -3443,7 +3585,7 @@ var AudioRecorder = memo2(function AudioRecorder2({ isRecording, onStartRecordin
3443
3585
  onClick: onCancel,
3444
3586
  children: [
3445
3587
  /* @__PURE__ */ jsx22(X3, { className: "h-3 w-3 mr-1" }),
3446
- "Cancelar"
3588
+ config?.labels?.cancel || "Cancel"
3447
3589
  ]
3448
3590
  }
3449
3591
  ),
@@ -3455,7 +3597,7 @@ var AudioRecorder = memo2(function AudioRecorder2({ isRecording, onStartRecordin
3455
3597
  onClick: onStopRecording,
3456
3598
  children: [
3457
3599
  /* @__PURE__ */ jsx22(Square2, { className: "h-3 w-3 mr-1" }),
3458
- "Parar"
3600
+ config?.labels?.voiceStop || "Stop"
3459
3601
  ]
3460
3602
  }
3461
3603
  )
@@ -3472,13 +3614,14 @@ var resolveVoiceErrorMessage = (error, config) => {
3472
3614
  return config?.labels?.voiceCaptureError || "Unable to capture audio.";
3473
3615
  };
3474
3616
  var clearVoiceTranscript = () => ({});
3617
+ var resolveVoiceSegmentDuration = (segment) => segment.attachment.durationMs ?? 0;
3475
3618
  var ChatInput = memo2(function ChatInput2({
3476
3619
  value,
3477
3620
  onChange,
3478
3621
  onSubmit,
3479
3622
  attachments,
3480
3623
  onAttachmentsChange,
3481
- placeholder = "Digite sua mensagem...",
3624
+ placeholder = "Type your message...",
3482
3625
  disabled = false,
3483
3626
  isGenerating = false,
3484
3627
  onStopGeneration,
@@ -3491,17 +3634,27 @@ var ChatInput = memo2(function ChatInput2({
3491
3634
  className = "",
3492
3635
  config
3493
3636
  }) {
3637
+ const voiceComposeEnabled = config?.voiceCompose?.enabled === true;
3638
+ const voiceDefaultMode = config?.voiceCompose?.defaultMode ?? "text";
3639
+ const voiceAutoSendDelayMs = config?.voiceCompose?.autoSendDelayMs ?? 5e3;
3640
+ const voicePersistComposer = config?.voiceCompose?.persistComposer ?? true;
3641
+ const voiceShowTranscriptPreview = config?.voiceCompose?.showTranscriptPreview ?? true;
3642
+ const voiceTranscriptMode = config?.voiceCompose?.transcriptMode ?? "final-only";
3643
+ const voiceMaxRecordingMs = config?.voiceCompose?.maxRecordingMs;
3494
3644
  const [isRecording, setIsRecording] = useState6(false);
3495
3645
  const { setContext } = useChatUserContext();
3496
3646
  const [recordingDuration, setRecordingDuration] = useState6(0);
3497
3647
  const [uploadProgress, setUploadProgress] = useState6(/* @__PURE__ */ new Map());
3498
- const [isVoiceComposerOpen, setIsVoiceComposerOpen] = useState6(false);
3648
+ const [isVoiceComposerOpen, setIsVoiceComposerOpen] = useState6(
3649
+ () => voiceComposeEnabled && voiceDefaultMode === "voice"
3650
+ );
3499
3651
  const [voiceState, setVoiceState] = useState6("idle");
3500
3652
  const [voiceDraft, setVoiceDraft] = useState6(null);
3501
3653
  const [voiceTranscript, setVoiceTranscript] = useState6(clearVoiceTranscript);
3502
3654
  const [voiceDurationMs, setVoiceDurationMs] = useState6(0);
3503
3655
  const [voiceAudioLevel, setVoiceAudioLevel] = useState6(0);
3504
3656
  const [voiceCountdownMs, setVoiceCountdownMs] = useState6(0);
3657
+ const [isVoiceAutoSendActive, setIsVoiceAutoSendActive] = useState6(false);
3505
3658
  const [voiceError, setVoiceError] = useState6(null);
3506
3659
  const textareaRef = useRef5(null);
3507
3660
  const fileInputRef = useRef5(null);
@@ -3510,12 +3663,9 @@ var ChatInput = memo2(function ChatInput2({
3510
3663
  const recordingInterval = useRef5(null);
3511
3664
  const mediaStreamRef = useRef5(null);
3512
3665
  const voiceProviderRef = useRef5(null);
3513
- const voiceComposeEnabled = config?.voiceCompose?.enabled === true;
3514
- const voiceAutoSendDelayMs = config?.voiceCompose?.autoSendDelayMs ?? 5e3;
3515
- const voicePersistComposer = config?.voiceCompose?.persistComposer ?? true;
3516
- const voiceShowTranscriptPreview = config?.voiceCompose?.showTranscriptPreview ?? true;
3517
- const voiceTranscriptMode = config?.voiceCompose?.transcriptMode ?? "final-only";
3518
- const voiceMaxRecordingMs = config?.voiceCompose?.maxRecordingMs;
3666
+ const voiceDraftRef = useRef5(null);
3667
+ const voiceAppendBaseRef = useRef5(null);
3668
+ const voiceAppendBaseDurationRef = useRef5(0);
3519
3669
  useEffect9(() => {
3520
3670
  return () => {
3521
3671
  if (mediaStreamRef.current) {
@@ -3530,6 +3680,9 @@ var ChatInput = memo2(function ChatInput2({
3530
3680
  }
3531
3681
  };
3532
3682
  }, []);
3683
+ useEffect9(() => {
3684
+ voiceDraftRef.current = voiceDraft;
3685
+ }, [voiceDraft]);
3533
3686
  const handleSubmit = (e) => {
3534
3687
  e.preventDefault();
3535
3688
  if (!value.trim() && attachments.length === 0 || disabled || isGenerating) return;
@@ -3545,7 +3698,7 @@ var ChatInput = memo2(function ChatInput2({
3545
3698
  };
3546
3699
  const processFile = async (file) => {
3547
3700
  if (file.size > maxFileSize) {
3548
- alert(`Arquivo muito grande. M\xE1ximo permitido: ${Math.round(maxFileSize / 1024 / 1024)}MB`);
3701
+ alert(`File too large. Max allowed: ${Math.round(maxFileSize / 1024 / 1024)}MB`);
3549
3702
  return null;
3550
3703
  }
3551
3704
  const fileId = `${Date.now()}_${Math.random().toString(36).slice(2)}`;
@@ -3604,7 +3757,7 @@ var ChatInput = memo2(function ChatInput2({
3604
3757
  newMap.delete(fileId);
3605
3758
  return newMap;
3606
3759
  });
3607
- alert("Erro ao processar arquivo");
3760
+ alert("Failed to process file");
3608
3761
  return null;
3609
3762
  }
3610
3763
  };
@@ -3679,7 +3832,7 @@ var ChatInput = memo2(function ChatInput2({
3679
3832
  }, 1e3);
3680
3833
  } catch (error) {
3681
3834
  console.error("Error starting recording:", error);
3682
- alert("N\xE3o foi poss\xEDvel acessar o microfone");
3835
+ alert(config?.labels?.voicePermissionDenied || "Microphone access was denied.");
3683
3836
  }
3684
3837
  };
3685
3838
  const stopRecording = () => {
@@ -3707,10 +3860,14 @@ var ChatInput = memo2(function ChatInput2({
3707
3860
  const resetVoiceComposerState = useCallback3((nextState = "idle") => {
3708
3861
  setVoiceState(nextState);
3709
3862
  setVoiceDraft(null);
3863
+ voiceDraftRef.current = null;
3864
+ voiceAppendBaseRef.current = null;
3865
+ voiceAppendBaseDurationRef.current = 0;
3710
3866
  setVoiceTranscript(clearVoiceTranscript());
3711
3867
  setVoiceDurationMs(0);
3712
3868
  setVoiceAudioLevel(0);
3713
3869
  setVoiceCountdownMs(0);
3870
+ setIsVoiceAutoSendActive(false);
3714
3871
  setVoiceError(null);
3715
3872
  }, []);
3716
3873
  const ensureVoiceProvider = useCallback3(async () => {
@@ -3721,21 +3878,76 @@ var ChatInput = memo2(function ChatInput2({
3721
3878
  const provider = await createProvider({
3722
3879
  onStateChange: setVoiceState,
3723
3880
  onAudioLevelChange: setVoiceAudioLevel,
3724
- onDurationChange: setVoiceDurationMs,
3725
- onTranscriptChange: setVoiceTranscript,
3881
+ onDurationChange: (durationMs) => {
3882
+ setVoiceDurationMs(voiceAppendBaseDurationRef.current + durationMs);
3883
+ },
3884
+ onTranscriptChange: (transcript) => {
3885
+ const baseTranscript = voiceAppendBaseRef.current?.transcript;
3886
+ setVoiceTranscript(
3887
+ baseTranscript ? mergeVoiceTranscripts(baseTranscript, transcript) : transcript
3888
+ );
3889
+ },
3726
3890
  onSegmentReady: (segment) => {
3727
- setVoiceDraft(segment);
3728
- setVoiceTranscript(segment.transcript ?? clearVoiceTranscript());
3729
- setVoiceDurationMs(segment.attachment.durationMs ?? 0);
3730
- setVoiceAudioLevel(0);
3731
- setVoiceCountdownMs(voiceAutoSendDelayMs);
3732
- setVoiceError(null);
3733
- setVoiceState("review");
3891
+ void (async () => {
3892
+ const previousSegment = voiceAppendBaseRef.current;
3893
+ try {
3894
+ const nextSegment = previousSegment ? await appendVoiceSegments(previousSegment, segment) : segment;
3895
+ voiceAppendBaseRef.current = null;
3896
+ voiceAppendBaseDurationRef.current = 0;
3897
+ voiceDraftRef.current = nextSegment;
3898
+ setVoiceDraft(nextSegment);
3899
+ setVoiceTranscript(nextSegment.transcript ?? clearVoiceTranscript());
3900
+ setVoiceDurationMs(resolveVoiceSegmentDuration(nextSegment));
3901
+ setVoiceAudioLevel(0);
3902
+ setVoiceCountdownMs(voiceAutoSendDelayMs);
3903
+ setIsVoiceAutoSendActive(voiceAutoSendDelayMs > 0);
3904
+ setVoiceError(null);
3905
+ setVoiceState("review");
3906
+ } catch (error) {
3907
+ const resolvedError = resolveVoiceErrorMessage(error, config);
3908
+ voiceAppendBaseRef.current = null;
3909
+ voiceAppendBaseDurationRef.current = 0;
3910
+ setVoiceAudioLevel(0);
3911
+ setVoiceCountdownMs(0);
3912
+ setIsVoiceAutoSendActive(false);
3913
+ if (previousSegment) {
3914
+ voiceDraftRef.current = previousSegment;
3915
+ setVoiceDraft(previousSegment);
3916
+ setVoiceTranscript(previousSegment.transcript ?? clearVoiceTranscript());
3917
+ setVoiceDurationMs(resolveVoiceSegmentDuration(previousSegment));
3918
+ setVoiceError(resolvedError);
3919
+ setVoiceState("review");
3920
+ return;
3921
+ }
3922
+ voiceDraftRef.current = null;
3923
+ setVoiceDraft(null);
3924
+ setVoiceTranscript(clearVoiceTranscript());
3925
+ setVoiceDurationMs(0);
3926
+ setVoiceError(resolvedError);
3927
+ setVoiceState("error");
3928
+ }
3929
+ })();
3734
3930
  },
3735
3931
  onError: (error) => {
3932
+ const previousSegment = voiceAppendBaseRef.current;
3933
+ voiceAppendBaseRef.current = null;
3934
+ voiceAppendBaseDurationRef.current = 0;
3736
3935
  setVoiceError(resolveVoiceErrorMessage(error, config));
3737
3936
  setVoiceAudioLevel(0);
3738
3937
  setVoiceCountdownMs(0);
3938
+ setIsVoiceAutoSendActive(false);
3939
+ if (previousSegment) {
3940
+ voiceDraftRef.current = previousSegment;
3941
+ setVoiceDraft(previousSegment);
3942
+ setVoiceTranscript(previousSegment.transcript ?? clearVoiceTranscript());
3943
+ setVoiceDurationMs(resolveVoiceSegmentDuration(previousSegment));
3944
+ setVoiceState("review");
3945
+ return;
3946
+ }
3947
+ voiceDraftRef.current = null;
3948
+ setVoiceDraft(null);
3949
+ setVoiceTranscript(clearVoiceTranscript());
3950
+ setVoiceDurationMs(0);
3739
3951
  setVoiceState("error");
3740
3952
  }
3741
3953
  }, {
@@ -3745,34 +3957,67 @@ var ChatInput = memo2(function ChatInput2({
3745
3957
  return provider;
3746
3958
  }, [config, voiceAutoSendDelayMs, voiceMaxRecordingMs]);
3747
3959
  const closeVoiceComposer = useCallback3(async () => {
3960
+ voiceAppendBaseRef.current = null;
3961
+ voiceAppendBaseDurationRef.current = 0;
3748
3962
  setIsVoiceComposerOpen(false);
3749
3963
  setVoiceError(null);
3750
3964
  setVoiceCountdownMs(0);
3751
3965
  setVoiceAudioLevel(0);
3752
3966
  setVoiceTranscript(clearVoiceTranscript());
3753
3967
  setVoiceDraft(null);
3968
+ voiceDraftRef.current = null;
3754
3969
  setVoiceDurationMs(0);
3755
3970
  setVoiceState("idle");
3756
3971
  if (voiceProviderRef.current) {
3757
3972
  await voiceProviderRef.current.cancel();
3758
3973
  }
3759
3974
  }, []);
3760
- const startVoiceCapture = useCallback3(async () => {
3975
+ const startVoiceCapture = useCallback3(async (appendToDraft = false) => {
3761
3976
  if (disabled || isGenerating) {
3762
3977
  return;
3763
3978
  }
3979
+ const previousDraft = appendToDraft ? voiceDraftRef.current : null;
3980
+ const previousDurationMs = previousDraft ? resolveVoiceSegmentDuration(previousDraft) : 0;
3764
3981
  setIsVoiceComposerOpen(true);
3765
3982
  setVoiceError(null);
3766
- setVoiceDraft(null);
3767
3983
  setVoiceCountdownMs(0);
3768
- setVoiceTranscript(clearVoiceTranscript());
3769
3984
  setVoiceAudioLevel(0);
3770
- setVoiceDurationMs(0);
3985
+ setIsVoiceAutoSendActive(false);
3986
+ voiceAppendBaseRef.current = previousDraft;
3987
+ voiceAppendBaseDurationRef.current = previousDurationMs;
3988
+ if (!previousDraft) {
3989
+ setVoiceDraft(null);
3990
+ voiceDraftRef.current = null;
3991
+ setVoiceTranscript(clearVoiceTranscript());
3992
+ setVoiceDurationMs(0);
3993
+ } else {
3994
+ setVoiceTranscript(previousDraft.transcript ?? clearVoiceTranscript());
3995
+ setVoiceDurationMs(previousDurationMs);
3996
+ }
3771
3997
  try {
3772
3998
  const provider = await ensureVoiceProvider();
3773
3999
  await provider.start();
3774
4000
  } catch (error) {
3775
- setVoiceError(resolveVoiceErrorMessage(error, config));
4001
+ const resolvedError = resolveVoiceErrorMessage(error, config);
4002
+ voiceAppendBaseRef.current = null;
4003
+ voiceAppendBaseDurationRef.current = 0;
4004
+ setVoiceAudioLevel(0);
4005
+ setVoiceCountdownMs(0);
4006
+ setIsVoiceAutoSendActive(false);
4007
+ if (previousDraft) {
4008
+ voiceDraftRef.current = previousDraft;
4009
+ setVoiceDraft(previousDraft);
4010
+ setVoiceTranscript(previousDraft.transcript ?? clearVoiceTranscript());
4011
+ setVoiceDurationMs(previousDurationMs);
4012
+ setVoiceError(resolvedError);
4013
+ setVoiceState("review");
4014
+ return;
4015
+ }
4016
+ voiceDraftRef.current = null;
4017
+ setVoiceDraft(null);
4018
+ setVoiceTranscript(clearVoiceTranscript());
4019
+ setVoiceDurationMs(0);
4020
+ setVoiceError(resolvedError);
3776
4021
  setVoiceState("error");
3777
4022
  }
3778
4023
  }, [disabled, isGenerating, ensureVoiceProvider, config]);
@@ -3786,6 +4031,8 @@ var ChatInput = memo2(function ChatInput2({
3786
4031
  }
3787
4032
  }, [config]);
3788
4033
  const cancelVoiceCapture = useCallback3(async () => {
4034
+ voiceAppendBaseRef.current = null;
4035
+ voiceAppendBaseDurationRef.current = 0;
3789
4036
  if (voiceProviderRef.current) {
3790
4037
  await voiceProviderRef.current.cancel();
3791
4038
  }
@@ -3805,6 +4052,7 @@ var ChatInput = memo2(function ChatInput2({
3805
4052
  }
3806
4053
  setVoiceState("sending");
3807
4054
  setVoiceCountdownMs(0);
4055
+ setIsVoiceAutoSendActive(false);
3808
4056
  onSubmit("", [...attachments, voiceDraft.attachment]);
3809
4057
  onChange("");
3810
4058
  onAttachmentsChange([]);
@@ -3819,12 +4067,12 @@ var ChatInput = memo2(function ChatInput2({
3819
4067
  onAttachmentsChange,
3820
4068
  finalizeVoiceComposerAfterSend
3821
4069
  ]);
3822
- const recordVoiceAgain = useCallback3(async () => {
3823
- resetVoiceComposerState("idle");
3824
- await startVoiceCapture();
3825
- }, [resetVoiceComposerState, startVoiceCapture]);
4070
+ const cancelVoiceAutoSend = useCallback3(() => {
4071
+ setVoiceCountdownMs(0);
4072
+ setIsVoiceAutoSendActive(false);
4073
+ }, []);
3826
4074
  useEffect9(() => {
3827
- if (voiceState !== "review" || !voiceDraft || voiceAutoSendDelayMs <= 0) {
4075
+ if (voiceState !== "review" || !voiceDraft || voiceAutoSendDelayMs <= 0 || !isVoiceAutoSendActive) {
3828
4076
  return;
3829
4077
  }
3830
4078
  const startedAt = Date.now();
@@ -3838,7 +4086,7 @@ var ChatInput = memo2(function ChatInput2({
3838
4086
  }
3839
4087
  }, 100);
3840
4088
  return () => clearInterval(timer);
3841
- }, [voiceState, voiceDraft, voiceAutoSendDelayMs, sendVoiceDraft]);
4089
+ }, [voiceState, voiceDraft, voiceAutoSendDelayMs, isVoiceAutoSendActive, sendVoiceDraft]);
3842
4090
  const removeAttachment = (index) => {
3843
4091
  const newAttachments = attachments.filter((_, i) => i !== index);
3844
4092
  onAttachmentsChange(newAttachments);
@@ -3887,10 +4135,12 @@ var ChatInput = memo2(function ChatInput2({
3887
4135
  transcript: voiceTranscript,
3888
4136
  transcriptMode: voiceTranscriptMode,
3889
4137
  showTranscriptPreview: voiceShowTranscriptPreview,
4138
+ attachment: voiceDraft?.attachment ?? null,
3890
4139
  durationMs: voiceDurationMs,
3891
4140
  audioLevel: voiceAudioLevel,
3892
4141
  countdownMs: voiceCountdownMs,
3893
4142
  autoSendDelayMs: voiceAutoSendDelayMs,
4143
+ isAutoSendActive: isVoiceAutoSendActive,
3894
4144
  errorMessage: voiceError,
3895
4145
  disabled: disabled || isGenerating,
3896
4146
  labels: config?.labels,
@@ -3900,13 +4150,16 @@ var ChatInput = memo2(function ChatInput2({
3900
4150
  onStop: () => {
3901
4151
  void stopVoiceCapture();
3902
4152
  },
3903
- onCancel: () => {
4153
+ onCancelAutoSend: () => {
4154
+ cancelVoiceAutoSend();
4155
+ },
4156
+ onDiscard: () => {
3904
4157
  void cancelVoiceCapture();
3905
4158
  },
3906
- onSendNow: sendVoiceDraft,
3907
4159
  onRecordAgain: () => {
3908
- void recordVoiceAgain();
4160
+ void startVoiceCapture(true);
3909
4161
  },
4162
+ onSendNow: sendVoiceDraft,
3910
4163
  onExit: () => {
3911
4164
  void closeVoiceComposer();
3912
4165
  }
@@ -3918,7 +4171,7 @@ var ChatInput = memo2(function ChatInput2({
3918
4171
  onDrop: handleDrop,
3919
4172
  onDragOver: handleDragOver,
3920
4173
  children: [
3921
- enableFileUpload && canAddMoreAttachments && /* @__PURE__ */ jsxs12(Fragment5, { children: [
4174
+ enableFileUpload && canAddMoreAttachments && /* @__PURE__ */ jsxs12(Fragment4, { children: [
3922
4175
  /* @__PURE__ */ jsx22(
3923
4176
  "input",
3924
4177
  {
@@ -4021,14 +4274,14 @@ var ChatInput = memo2(function ChatInput2({
4021
4274
  ) }),
4022
4275
  /* @__PURE__ */ jsxs12("div", { className: "text-[10px] text-muted-foreground text-center", children: [
4023
4276
  window.innerWidth > 768 ? config?.labels?.inputHelpText : "",
4024
- attachments.length > 0 && /* @__PURE__ */ jsxs12(Fragment5, { children: [
4277
+ attachments.length > 0 && /* @__PURE__ */ jsxs12(Fragment4, { children: [
4025
4278
  " \u2022 ",
4026
4279
  attachments.length,
4027
4280
  "/",
4028
4281
  maxAttachments,
4029
4282
  " anexos"
4030
4283
  ] }),
4031
- config?.labels?.footerLabel && /* @__PURE__ */ jsxs12(Fragment5, { children: [
4284
+ config?.labels?.footerLabel && /* @__PURE__ */ jsxs12(Fragment4, { children: [
4032
4285
  " \u2022 ",
4033
4286
  config.labels.footerLabel
4034
4287
  ] })
@@ -4118,7 +4371,7 @@ import {
4118
4371
  FileText as FileText2,
4119
4372
  Brain as Brain2,
4120
4373
  Plus as Plus3,
4121
- Trash2 as Trash23,
4374
+ Trash2 as Trash24,
4122
4375
  Target,
4123
4376
  Lightbulb,
4124
4377
  Info,
@@ -4128,7 +4381,7 @@ import {
4128
4381
  Check as Check3,
4129
4382
  X as X4
4130
4383
  } from "lucide-react";
4131
- import { Fragment as Fragment6, jsx as jsx24, jsxs as jsxs14 } from "react/jsx-runtime";
4384
+ import { Fragment as Fragment5, jsx as jsx24, jsxs as jsxs14 } from "react/jsx-runtime";
4132
4385
  var getInitials2 = (name, email) => {
4133
4386
  if (name) {
4134
4387
  return name.split(" ").map((n) => n[0]).slice(0, 2).join("").toUpperCase();
@@ -4321,7 +4574,7 @@ var UserProfile = ({
4321
4574
  ] })
4322
4575
  ] })
4323
4576
  ] }),
4324
- normalizedFields.length > 0 && /* @__PURE__ */ jsxs14(Fragment6, { children: [
4577
+ normalizedFields.length > 0 && /* @__PURE__ */ jsxs14(Fragment5, { children: [
4325
4578
  /* @__PURE__ */ jsx24(Separator, {}),
4326
4579
  /* @__PURE__ */ jsxs14("div", { className: "space-y-3", children: [
4327
4580
  /* @__PURE__ */ jsx24("h3", { className: "text-sm font-medium text-muted-foreground uppercase tracking-wider", children: labels.customFields }),
@@ -4465,7 +4718,7 @@ var UserProfile = ({
4465
4718
  size: "icon",
4466
4719
  className: "h-7 w-7",
4467
4720
  onClick: () => onDeleteMemory(memory.id),
4468
- children: /* @__PURE__ */ jsx24(Trash23, { className: "h-3.5 w-3.5 text-destructive" })
4721
+ children: /* @__PURE__ */ jsx24(Trash24, { className: "h-3.5 w-3.5 text-destructive" })
4469
4722
  }
4470
4723
  )
4471
4724
  ] })
@@ -5051,7 +5304,7 @@ import {
5051
5304
  MessageSquare as MessageSquare2,
5052
5305
  MoreVertical as MoreVertical2,
5053
5306
  Edit2 as Edit22,
5054
- Trash2 as Trash24,
5307
+ Trash2 as Trash25,
5055
5308
  Archive as Archive2,
5056
5309
  Search as Search2,
5057
5310
  Filter as Filter2,
@@ -5060,7 +5313,7 @@ import {
5060
5313
  X as X5,
5061
5314
  Check as Check4
5062
5315
  } from "lucide-react";
5063
- import { Fragment as Fragment7, jsx as jsx26, jsxs as jsxs16 } from "react/jsx-runtime";
5316
+ import { Fragment as Fragment6, jsx as jsx26, jsxs as jsxs16 } from "react/jsx-runtime";
5064
5317
  var ThreadItem = ({ thread, isActive, config, onSelect, onRename, onDelete, onArchive }) => {
5065
5318
  const [isEditing, setIsEditing] = useState9(false);
5066
5319
  const [editTitle, setEditTitle] = useState9(thread.title);
@@ -5105,7 +5358,7 @@ var ThreadItem = ({ thread, isActive, config, onSelect, onRename, onDelete, onAr
5105
5358
  ),
5106
5359
  /* @__PURE__ */ jsx26(Button, { size: "sm", variant: "ghost", onClick: handleSaveEdit, children: /* @__PURE__ */ jsx26(Check4, { className: "h-3 w-3" }) }),
5107
5360
  /* @__PURE__ */ jsx26(Button, { size: "sm", variant: "ghost", onClick: handleCancelEdit, children: /* @__PURE__ */ jsx26(X5, { className: "h-3 w-3" }) })
5108
- ] }) : /* @__PURE__ */ jsxs16(Fragment7, { children: [
5361
+ ] }) : /* @__PURE__ */ jsxs16(Fragment6, { children: [
5109
5362
  /* @__PURE__ */ jsx26("h4", { className: "font-medium text-sm truncate mb-1", children: thread.title }),
5110
5363
  /* @__PURE__ */ jsxs16("div", { className: "flex items-center gap-2 text-xs text-muted-foreground", children: [
5111
5364
  /* @__PURE__ */ jsxs16("div", { className: "flex items-center gap-1", children: [
@@ -5118,7 +5371,7 @@ var ThreadItem = ({ thread, isActive, config, onSelect, onRename, onDelete, onAr
5118
5371
  /* @__PURE__ */ jsx26(Calendar2, { className: "h-3 w-3" }),
5119
5372
  formatDate(thread.updatedAt, config?.labels)
5120
5373
  ] }),
5121
- thread.isArchived && /* @__PURE__ */ jsxs16(Fragment7, { children: [
5374
+ thread.isArchived && /* @__PURE__ */ jsxs16(Fragment6, { children: [
5122
5375
  /* @__PURE__ */ jsx26(Separator, { orientation: "vertical", className: "h-3" }),
5123
5376
  /* @__PURE__ */ jsxs16(Badge, { variant: "secondary", className: "text-xs", children: [
5124
5377
  /* @__PURE__ */ jsx26(Archive2, { className: "h-2 w-2 mr-1" }),
@@ -5140,7 +5393,7 @@ var ThreadItem = ({ thread, isActive, config, onSelect, onRename, onDelete, onAr
5140
5393
  ] }),
5141
5394
  /* @__PURE__ */ jsx26(DropdownMenuSeparator, {}),
5142
5395
  /* @__PURE__ */ jsxs16(DropdownMenuItem, { onClick: onDelete, className: "text-destructive", children: [
5143
- /* @__PURE__ */ jsx26(Trash24, { className: "h-4 w-4 mr-2" }),
5396
+ /* @__PURE__ */ jsx26(Trash25, { className: "h-4 w-4 mr-2" }),
5144
5397
  config?.labels?.deleteThread || "Delete"
5145
5398
  ] })
5146
5399
  ] })