@copilotz/chat-ui 0.1.32 → 0.1.33

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.d.cts CHANGED
@@ -123,15 +123,18 @@ interface ChatConfig {
123
123
  voiceEnter?: string;
124
124
  voiceExit?: string;
125
125
  voiceTitle?: string;
126
+ voiceIdle?: string;
126
127
  voicePreparing?: string;
127
128
  voiceWaiting?: string;
128
129
  voiceListening?: string;
129
130
  voiceFinishing?: string;
130
131
  voiceReview?: string;
132
+ voiceSending?: string;
131
133
  voiceStart?: string;
132
134
  voiceStop?: string;
133
135
  voiceSendNow?: string;
134
136
  voiceCancel?: string;
137
+ voiceDiscard?: string;
135
138
  voiceRecordAgain?: string;
136
139
  voiceAutoSendIn?: string;
137
140
  voiceTranscriptPending?: string;
@@ -198,6 +201,7 @@ interface ChatConfig {
198
201
  };
199
202
  voiceCompose?: {
200
203
  enabled?: boolean;
204
+ defaultMode?: 'text' | 'voice';
201
205
  autoSendDelayMs?: number;
202
206
  persistComposer?: boolean;
203
207
  showTranscriptPreview?: boolean;
package/dist/index.d.ts CHANGED
@@ -123,15 +123,18 @@ interface ChatConfig {
123
123
  voiceEnter?: string;
124
124
  voiceExit?: string;
125
125
  voiceTitle?: string;
126
+ voiceIdle?: string;
126
127
  voicePreparing?: string;
127
128
  voiceWaiting?: string;
128
129
  voiceListening?: string;
129
130
  voiceFinishing?: string;
130
131
  voiceReview?: string;
132
+ voiceSending?: string;
131
133
  voiceStart?: string;
132
134
  voiceStop?: string;
133
135
  voiceSendNow?: string;
134
136
  voiceCancel?: string;
137
+ voiceDiscard?: string;
135
138
  voiceRecordAgain?: string;
136
139
  voiceAutoSendIn?: string;
137
140
  voiceTranscriptPending?: string;
@@ -198,6 +201,7 @@ interface ChatConfig {
198
201
  };
199
202
  voiceCompose?: {
200
203
  enabled?: boolean;
204
+ defaultMode?: 'text' | 'voice';
201
205
  autoSendDelayMs?: number;
202
206
  persistComposer?: boolean;
203
207
  showTranscriptPreview?: boolean;
package/dist/index.js CHANGED
@@ -32,16 +32,19 @@ 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",
47
+ voiceDiscard: "Delete recording",
45
48
  voiceRecordAgain: "Record again",
46
49
  voiceAutoSendIn: "Auto-sends in {{seconds}}s",
47
50
  voiceTranscriptPending: "Transcript unavailable",
@@ -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,
@@ -3063,8 +3067,8 @@ function Progress({
3063
3067
  }
3064
3068
 
3065
3069
  // 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";
3070
+ import { Keyboard, Loader2, Mic, Send, Square, Trash2 as Trash23, X as X2 } from "lucide-react";
3071
+ import { jsx as jsx21, jsxs as jsxs11 } from "react/jsx-runtime";
3068
3072
  var formatDuration = (durationMs) => {
3069
3073
  const totalSeconds = Math.max(0, Math.floor(durationMs / 1e3));
3070
3074
  const minutes = Math.floor(totalSeconds / 60);
@@ -3093,12 +3097,12 @@ var resolveStateLabel = (state, labels, errorMessage) => {
3093
3097
  case "review":
3094
3098
  return labels?.voiceReview || "Ready to send";
3095
3099
  case "sending":
3096
- return "Sending...";
3100
+ return labels?.voiceSending || "Sending...";
3097
3101
  case "error":
3098
3102
  return errorMessage || labels?.voiceCaptureError || "Unable to capture audio.";
3099
3103
  case "idle":
3100
3104
  default:
3101
- return labels?.voiceTitle || "Voice input";
3105
+ return labels?.voiceIdle || "Tap the mic to record";
3102
3106
  }
3103
3107
  };
3104
3108
  var resolveTranscriptText = (transcript, transcriptMode) => {
@@ -3115,18 +3119,21 @@ var VoiceComposer = ({
3115
3119
  transcript,
3116
3120
  transcriptMode,
3117
3121
  showTranscriptPreview,
3122
+ attachment,
3118
3123
  durationMs,
3119
3124
  audioLevel,
3120
3125
  countdownMs,
3121
3126
  autoSendDelayMs,
3127
+ isAutoSendActive,
3122
3128
  errorMessage,
3123
3129
  disabled = false,
3124
3130
  labels,
3125
3131
  onStart,
3126
3132
  onStop,
3127
- onCancel,
3128
- onSendNow,
3133
+ onCancelAutoSend,
3134
+ onDiscard,
3129
3135
  onRecordAgain,
3136
+ onSendNow,
3130
3137
  onExit
3131
3138
  }) => {
3132
3139
  const transcriptText = resolveTranscriptText(transcript, transcriptMode);
@@ -3134,12 +3141,14 @@ var VoiceComposer = ({
3134
3141
  const countdownValue = autoSendDelayMs > 0 ? Math.min(100, Math.max(0, (autoSendDelayMs - countdownMs) / autoSendDelayMs * 100)) : 100;
3135
3142
  const isBusy = state === "preparing" || state === "finishing" || state === "sending";
3136
3143
  const isCapturing = state === "waiting_for_speech" || state === "listening";
3144
+ const isReviewing = state === "review";
3137
3145
  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) })
3146
+ const headerLabel = state === "error" ? labels?.voiceCaptureError || "Unable to capture audio." : resolveStateLabel(state, labels, errorMessage);
3147
+ 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: [
3148
+ /* @__PURE__ */ jsxs11("div", { className: "flex items-center justify-between gap-2 sm:gap-3", children: [
3149
+ /* @__PURE__ */ jsxs11("div", { className: "flex min-w-0 items-center gap-2", children: [
3150
+ /* @__PURE__ */ jsx21(Badge, { variant: "outline", children: labels?.voiceTitle || "Voice" }),
3151
+ /* @__PURE__ */ jsx21("span", { className: "truncate text-xs sm:text-sm text-muted-foreground", children: headerLabel })
3143
3152
  ] }),
3144
3153
  /* @__PURE__ */ jsxs11(
3145
3154
  Button,
@@ -3147,71 +3156,89 @@ var VoiceComposer = ({
3147
3156
  type: "button",
3148
3157
  variant: "ghost",
3149
3158
  size: "sm",
3159
+ className: "shrink-0 px-2 sm:px-3",
3150
3160
  onClick: onExit,
3151
3161
  disabled: disabled || isBusy,
3152
3162
  children: [
3153
3163
  /* @__PURE__ */ jsx21(Keyboard, { className: "h-4 w-4" }),
3154
- labels?.voiceExit || "Use keyboard"
3164
+ /* @__PURE__ */ jsx21("span", { className: "hidden sm:inline", children: labels?.voiceExit || "Use keyboard" })
3155
3165
  ]
3156
3166
  }
3157
3167
  )
3158
3168
  ] }),
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: [
3169
+ !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: [
3170
+ /* @__PURE__ */ jsx21(
3171
+ Button,
3172
+ {
3173
+ type: "button",
3174
+ size: "icon",
3175
+ variant: isCapturing ? "destructive" : "outline",
3176
+ 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"}`,
3177
+ onClick: isCapturing ? onStop : onStart,
3178
+ disabled: disabled || isBusy,
3179
+ 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" })
3180
+ }
3181
+ ),
3182
+ /* @__PURE__ */ jsxs11("div", { className: "w-full space-y-2", children: [
3162
3183
  /* @__PURE__ */ jsx21(Progress, { value: levelValue, className: "h-2" }),
3163
3184
  /* @__PURE__ */ jsxs11("div", { className: "flex items-center justify-between text-xs text-muted-foreground", children: [
3164
3185
  /* @__PURE__ */ jsx21("span", { children: formatDuration(durationMs) }),
3165
- /* @__PURE__ */ jsx21("span", { children: resolveStateLabel(state, labels, errorMessage) })
3186
+ /* @__PURE__ */ jsx21("span", { children: isCapturing ? labels?.voiceStop || "Stop recording" : labels?.voiceStart || "Start recording" })
3166
3187
  ] })
3167
3188
  ] }),
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"
3189
+ 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 })
3190
+ ] }) }) : /* @__PURE__ */ jsxs11("div", { className: "mt-3 rounded-xl border bg-muted/20 p-3 sm:p-4", children: [
3191
+ /* @__PURE__ */ jsxs11("div", { className: "flex items-start justify-between gap-2", children: [
3192
+ /* @__PURE__ */ jsxs11("div", { className: "min-w-0", children: [
3193
+ /* @__PURE__ */ jsx21("div", { className: "text-sm font-medium text-foreground", children: labels?.voiceReview || "Ready to send" }),
3194
+ /* @__PURE__ */ jsx21("div", { className: "text-xs text-muted-foreground", children: formatDuration(durationMs) })
3184
3195
  ] }),
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
- ] })
3196
+ /* @__PURE__ */ jsx21(
3197
+ Button,
3198
+ {
3199
+ type: "button",
3200
+ variant: "ghost",
3201
+ size: "icon",
3202
+ className: "h-8 w-8 shrink-0 text-muted-foreground hover:text-destructive",
3203
+ onClick: onDiscard,
3204
+ disabled,
3205
+ "aria-label": labels?.voiceDiscard || "Delete recording",
3206
+ title: labels?.voiceDiscard || "Delete recording",
3207
+ children: /* @__PURE__ */ jsx21(Trash23, { className: "h-4 w-4" })
3208
+ }
3209
+ )
3210
+ ] }),
3211
+ 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 }) }) }),
3212
+ 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 }),
3213
+ isAutoSendActive && autoSendDelayMs > 0 && /* @__PURE__ */ jsxs11("div", { className: "mt-3 space-y-2", children: [
3214
+ /* @__PURE__ */ jsx21(Progress, { value: countdownValue, className: "h-2" }),
3215
+ /* @__PURE__ */ jsx21("div", { className: "text-center text-xs text-muted-foreground", children: interpolateSeconds(labels?.voiceAutoSendIn, countdownSeconds) })
3189
3216
  ] }),
3190
- state === "review" && /* @__PURE__ */ jsxs11(Fragment4, { children: [
3191
- /* @__PURE__ */ jsxs11(Button, { type: "button", variant: "outline", onClick: onCancel, disabled, children: [
3217
+ /* @__PURE__ */ jsxs11("div", { className: "mt-3 flex items-center justify-end gap-2", children: [
3218
+ isAutoSendActive && /* @__PURE__ */ jsxs11(Button, { type: "button", variant: "ghost", size: "sm", onClick: onCancelAutoSend, disabled, children: [
3192
3219
  /* @__PURE__ */ jsx21(X2, { className: "h-4 w-4" }),
3193
3220
  labels?.voiceCancel || "Cancel"
3194
3221
  ] }),
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: [
3222
+ !isAutoSendActive && /* @__PURE__ */ jsx21(
3223
+ Button,
3224
+ {
3225
+ type: "button",
3226
+ variant: "outline",
3227
+ size: "icon",
3228
+ onClick: onRecordAgain,
3229
+ disabled,
3230
+ "aria-label": labels?.voiceRecordAgain || "Record again",
3231
+ title: labels?.voiceRecordAgain || "Record again",
3232
+ children: /* @__PURE__ */ jsx21(Mic, { className: "h-4 w-4" })
3233
+ }
3234
+ ),
3235
+ /* @__PURE__ */ jsxs11(Button, { type: "button", size: "sm", onClick: onSendNow, disabled, children: [
3200
3236
  /* @__PURE__ */ jsx21(Send, { className: "h-4 w-4" }),
3201
3237
  labels?.voiceSendNow || "Send now"
3202
3238
  ] })
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
3239
  ] })
3214
- ] })
3240
+ ] }),
3241
+ state === "error" && 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
3242
  ] });
3216
3243
  };
3217
3244
 
@@ -3229,7 +3256,7 @@ import {
3229
3256
  Pause,
3230
3257
  Loader2 as Loader22
3231
3258
  } from "lucide-react";
3232
- import { Fragment as Fragment5, jsx as jsx22, jsxs as jsxs12 } from "react/jsx-runtime";
3259
+ import { Fragment as Fragment4, jsx as jsx22, jsxs as jsxs12 } from "react/jsx-runtime";
3233
3260
  var FileUploadItem = memo2(function FileUploadItem2({ file, progress, onCancel }) {
3234
3261
  const guessTypeFromName = (name) => {
3235
3262
  const ext = (name || "").split(".").pop()?.toLowerCase();
@@ -3330,7 +3357,7 @@ var AttachmentPreview = memo2(function AttachmentPreview2({ attachment, onRemove
3330
3357
  "img",
3331
3358
  {
3332
3359
  src: attachment.dataUrl,
3333
- alt: attachment.fileName || "Anexo",
3360
+ alt: attachment.fileName || "Attachment",
3334
3361
  className: "w-full h-20 object-cover rounded"
3335
3362
  }
3336
3363
  ),
@@ -3379,7 +3406,7 @@ var AttachmentPreview = memo2(function AttachmentPreview2({ attachment, onRemove
3379
3406
  }
3380
3407
  ),
3381
3408
  /* @__PURE__ */ jsxs12("div", { className: "flex-1", children: [
3382
- /* @__PURE__ */ jsx22("p", { className: "text-xs font-medium", children: attachment.fileName || "\xC1udio" }),
3409
+ /* @__PURE__ */ jsx22("p", { className: "text-xs font-medium", children: attachment.fileName || "Audio" }),
3383
3410
  /* @__PURE__ */ jsx22("p", { className: "text-xs text-muted-foreground", children: formatDuration2(attachment.durationMs) })
3384
3411
  ] }),
3385
3412
  /* @__PURE__ */ jsx22(
@@ -3431,7 +3458,7 @@ var AudioRecorder = memo2(function AudioRecorder2({ isRecording, onStartRecordin
3431
3458
  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
3459
  /* @__PURE__ */ jsxs12("div", { className: "flex items-center gap-2", children: [
3433
3460
  /* @__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" })
3461
+ /* @__PURE__ */ jsx22("span", { className: "text-sm font-medium text-red-700 dark:text-red-300", children: config?.labels?.voiceListening || "Recording" })
3435
3462
  ] }),
3436
3463
  /* @__PURE__ */ jsx22(Badge, { variant: "outline", className: "text-xs", children: formatTime(recordingDuration) }),
3437
3464
  /* @__PURE__ */ jsxs12("div", { className: "flex gap-1 ml-auto", children: [
@@ -3443,7 +3470,7 @@ var AudioRecorder = memo2(function AudioRecorder2({ isRecording, onStartRecordin
3443
3470
  onClick: onCancel,
3444
3471
  children: [
3445
3472
  /* @__PURE__ */ jsx22(X3, { className: "h-3 w-3 mr-1" }),
3446
- "Cancelar"
3473
+ config?.labels?.cancel || "Cancel"
3447
3474
  ]
3448
3475
  }
3449
3476
  ),
@@ -3455,7 +3482,7 @@ var AudioRecorder = memo2(function AudioRecorder2({ isRecording, onStartRecordin
3455
3482
  onClick: onStopRecording,
3456
3483
  children: [
3457
3484
  /* @__PURE__ */ jsx22(Square2, { className: "h-3 w-3 mr-1" }),
3458
- "Parar"
3485
+ config?.labels?.voiceStop || "Stop"
3459
3486
  ]
3460
3487
  }
3461
3488
  )
@@ -3478,7 +3505,7 @@ var ChatInput = memo2(function ChatInput2({
3478
3505
  onSubmit,
3479
3506
  attachments,
3480
3507
  onAttachmentsChange,
3481
- placeholder = "Digite sua mensagem...",
3508
+ placeholder = "Type your message...",
3482
3509
  disabled = false,
3483
3510
  isGenerating = false,
3484
3511
  onStopGeneration,
@@ -3491,17 +3518,27 @@ var ChatInput = memo2(function ChatInput2({
3491
3518
  className = "",
3492
3519
  config
3493
3520
  }) {
3521
+ const voiceComposeEnabled = config?.voiceCompose?.enabled === true;
3522
+ const voiceDefaultMode = config?.voiceCompose?.defaultMode ?? "text";
3523
+ const voiceAutoSendDelayMs = config?.voiceCompose?.autoSendDelayMs ?? 5e3;
3524
+ const voicePersistComposer = config?.voiceCompose?.persistComposer ?? true;
3525
+ const voiceShowTranscriptPreview = config?.voiceCompose?.showTranscriptPreview ?? true;
3526
+ const voiceTranscriptMode = config?.voiceCompose?.transcriptMode ?? "final-only";
3527
+ const voiceMaxRecordingMs = config?.voiceCompose?.maxRecordingMs;
3494
3528
  const [isRecording, setIsRecording] = useState6(false);
3495
3529
  const { setContext } = useChatUserContext();
3496
3530
  const [recordingDuration, setRecordingDuration] = useState6(0);
3497
3531
  const [uploadProgress, setUploadProgress] = useState6(/* @__PURE__ */ new Map());
3498
- const [isVoiceComposerOpen, setIsVoiceComposerOpen] = useState6(false);
3532
+ const [isVoiceComposerOpen, setIsVoiceComposerOpen] = useState6(
3533
+ () => voiceComposeEnabled && voiceDefaultMode === "voice"
3534
+ );
3499
3535
  const [voiceState, setVoiceState] = useState6("idle");
3500
3536
  const [voiceDraft, setVoiceDraft] = useState6(null);
3501
3537
  const [voiceTranscript, setVoiceTranscript] = useState6(clearVoiceTranscript);
3502
3538
  const [voiceDurationMs, setVoiceDurationMs] = useState6(0);
3503
3539
  const [voiceAudioLevel, setVoiceAudioLevel] = useState6(0);
3504
3540
  const [voiceCountdownMs, setVoiceCountdownMs] = useState6(0);
3541
+ const [isVoiceAutoSendActive, setIsVoiceAutoSendActive] = useState6(false);
3505
3542
  const [voiceError, setVoiceError] = useState6(null);
3506
3543
  const textareaRef = useRef5(null);
3507
3544
  const fileInputRef = useRef5(null);
@@ -3510,12 +3547,6 @@ var ChatInput = memo2(function ChatInput2({
3510
3547
  const recordingInterval = useRef5(null);
3511
3548
  const mediaStreamRef = useRef5(null);
3512
3549
  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;
3519
3550
  useEffect9(() => {
3520
3551
  return () => {
3521
3552
  if (mediaStreamRef.current) {
@@ -3545,7 +3576,7 @@ var ChatInput = memo2(function ChatInput2({
3545
3576
  };
3546
3577
  const processFile = async (file) => {
3547
3578
  if (file.size > maxFileSize) {
3548
- alert(`Arquivo muito grande. M\xE1ximo permitido: ${Math.round(maxFileSize / 1024 / 1024)}MB`);
3579
+ alert(`File too large. Max allowed: ${Math.round(maxFileSize / 1024 / 1024)}MB`);
3549
3580
  return null;
3550
3581
  }
3551
3582
  const fileId = `${Date.now()}_${Math.random().toString(36).slice(2)}`;
@@ -3604,7 +3635,7 @@ var ChatInput = memo2(function ChatInput2({
3604
3635
  newMap.delete(fileId);
3605
3636
  return newMap;
3606
3637
  });
3607
- alert("Erro ao processar arquivo");
3638
+ alert("Failed to process file");
3608
3639
  return null;
3609
3640
  }
3610
3641
  };
@@ -3679,7 +3710,7 @@ var ChatInput = memo2(function ChatInput2({
3679
3710
  }, 1e3);
3680
3711
  } catch (error) {
3681
3712
  console.error("Error starting recording:", error);
3682
- alert("N\xE3o foi poss\xEDvel acessar o microfone");
3713
+ alert(config?.labels?.voicePermissionDenied || "Microphone access was denied.");
3683
3714
  }
3684
3715
  };
3685
3716
  const stopRecording = () => {
@@ -3711,6 +3742,7 @@ var ChatInput = memo2(function ChatInput2({
3711
3742
  setVoiceDurationMs(0);
3712
3743
  setVoiceAudioLevel(0);
3713
3744
  setVoiceCountdownMs(0);
3745
+ setIsVoiceAutoSendActive(false);
3714
3746
  setVoiceError(null);
3715
3747
  }, []);
3716
3748
  const ensureVoiceProvider = useCallback3(async () => {
@@ -3729,6 +3761,7 @@ var ChatInput = memo2(function ChatInput2({
3729
3761
  setVoiceDurationMs(segment.attachment.durationMs ?? 0);
3730
3762
  setVoiceAudioLevel(0);
3731
3763
  setVoiceCountdownMs(voiceAutoSendDelayMs);
3764
+ setIsVoiceAutoSendActive(voiceAutoSendDelayMs > 0);
3732
3765
  setVoiceError(null);
3733
3766
  setVoiceState("review");
3734
3767
  },
@@ -3736,6 +3769,7 @@ var ChatInput = memo2(function ChatInput2({
3736
3769
  setVoiceError(resolveVoiceErrorMessage(error, config));
3737
3770
  setVoiceAudioLevel(0);
3738
3771
  setVoiceCountdownMs(0);
3772
+ setIsVoiceAutoSendActive(false);
3739
3773
  setVoiceState("error");
3740
3774
  }
3741
3775
  }, {
@@ -3768,6 +3802,7 @@ var ChatInput = memo2(function ChatInput2({
3768
3802
  setVoiceTranscript(clearVoiceTranscript());
3769
3803
  setVoiceAudioLevel(0);
3770
3804
  setVoiceDurationMs(0);
3805
+ setIsVoiceAutoSendActive(false);
3771
3806
  try {
3772
3807
  const provider = await ensureVoiceProvider();
3773
3808
  await provider.start();
@@ -3805,6 +3840,7 @@ var ChatInput = memo2(function ChatInput2({
3805
3840
  }
3806
3841
  setVoiceState("sending");
3807
3842
  setVoiceCountdownMs(0);
3843
+ setIsVoiceAutoSendActive(false);
3808
3844
  onSubmit("", [...attachments, voiceDraft.attachment]);
3809
3845
  onChange("");
3810
3846
  onAttachmentsChange([]);
@@ -3819,12 +3855,12 @@ var ChatInput = memo2(function ChatInput2({
3819
3855
  onAttachmentsChange,
3820
3856
  finalizeVoiceComposerAfterSend
3821
3857
  ]);
3822
- const recordVoiceAgain = useCallback3(async () => {
3823
- resetVoiceComposerState("idle");
3824
- await startVoiceCapture();
3825
- }, [resetVoiceComposerState, startVoiceCapture]);
3858
+ const cancelVoiceAutoSend = useCallback3(() => {
3859
+ setVoiceCountdownMs(0);
3860
+ setIsVoiceAutoSendActive(false);
3861
+ }, []);
3826
3862
  useEffect9(() => {
3827
- if (voiceState !== "review" || !voiceDraft || voiceAutoSendDelayMs <= 0) {
3863
+ if (voiceState !== "review" || !voiceDraft || voiceAutoSendDelayMs <= 0 || !isVoiceAutoSendActive) {
3828
3864
  return;
3829
3865
  }
3830
3866
  const startedAt = Date.now();
@@ -3838,7 +3874,7 @@ var ChatInput = memo2(function ChatInput2({
3838
3874
  }
3839
3875
  }, 100);
3840
3876
  return () => clearInterval(timer);
3841
- }, [voiceState, voiceDraft, voiceAutoSendDelayMs, sendVoiceDraft]);
3877
+ }, [voiceState, voiceDraft, voiceAutoSendDelayMs, isVoiceAutoSendActive, sendVoiceDraft]);
3842
3878
  const removeAttachment = (index) => {
3843
3879
  const newAttachments = attachments.filter((_, i) => i !== index);
3844
3880
  onAttachmentsChange(newAttachments);
@@ -3887,10 +3923,12 @@ var ChatInput = memo2(function ChatInput2({
3887
3923
  transcript: voiceTranscript,
3888
3924
  transcriptMode: voiceTranscriptMode,
3889
3925
  showTranscriptPreview: voiceShowTranscriptPreview,
3926
+ attachment: voiceDraft?.attachment ?? null,
3890
3927
  durationMs: voiceDurationMs,
3891
3928
  audioLevel: voiceAudioLevel,
3892
3929
  countdownMs: voiceCountdownMs,
3893
3930
  autoSendDelayMs: voiceAutoSendDelayMs,
3931
+ isAutoSendActive: isVoiceAutoSendActive,
3894
3932
  errorMessage: voiceError,
3895
3933
  disabled: disabled || isGenerating,
3896
3934
  labels: config?.labels,
@@ -3900,13 +3938,16 @@ var ChatInput = memo2(function ChatInput2({
3900
3938
  onStop: () => {
3901
3939
  void stopVoiceCapture();
3902
3940
  },
3903
- onCancel: () => {
3941
+ onCancelAutoSend: () => {
3942
+ cancelVoiceAutoSend();
3943
+ },
3944
+ onDiscard: () => {
3904
3945
  void cancelVoiceCapture();
3905
3946
  },
3906
- onSendNow: sendVoiceDraft,
3907
3947
  onRecordAgain: () => {
3908
- void recordVoiceAgain();
3948
+ void startVoiceCapture();
3909
3949
  },
3950
+ onSendNow: sendVoiceDraft,
3910
3951
  onExit: () => {
3911
3952
  void closeVoiceComposer();
3912
3953
  }
@@ -3918,7 +3959,7 @@ var ChatInput = memo2(function ChatInput2({
3918
3959
  onDrop: handleDrop,
3919
3960
  onDragOver: handleDragOver,
3920
3961
  children: [
3921
- enableFileUpload && canAddMoreAttachments && /* @__PURE__ */ jsxs12(Fragment5, { children: [
3962
+ enableFileUpload && canAddMoreAttachments && /* @__PURE__ */ jsxs12(Fragment4, { children: [
3922
3963
  /* @__PURE__ */ jsx22(
3923
3964
  "input",
3924
3965
  {
@@ -4021,14 +4062,14 @@ var ChatInput = memo2(function ChatInput2({
4021
4062
  ) }),
4022
4063
  /* @__PURE__ */ jsxs12("div", { className: "text-[10px] text-muted-foreground text-center", children: [
4023
4064
  window.innerWidth > 768 ? config?.labels?.inputHelpText : "",
4024
- attachments.length > 0 && /* @__PURE__ */ jsxs12(Fragment5, { children: [
4065
+ attachments.length > 0 && /* @__PURE__ */ jsxs12(Fragment4, { children: [
4025
4066
  " \u2022 ",
4026
4067
  attachments.length,
4027
4068
  "/",
4028
4069
  maxAttachments,
4029
4070
  " anexos"
4030
4071
  ] }),
4031
- config?.labels?.footerLabel && /* @__PURE__ */ jsxs12(Fragment5, { children: [
4072
+ config?.labels?.footerLabel && /* @__PURE__ */ jsxs12(Fragment4, { children: [
4032
4073
  " \u2022 ",
4033
4074
  config.labels.footerLabel
4034
4075
  ] })
@@ -4118,7 +4159,7 @@ import {
4118
4159
  FileText as FileText2,
4119
4160
  Brain as Brain2,
4120
4161
  Plus as Plus3,
4121
- Trash2 as Trash23,
4162
+ Trash2 as Trash24,
4122
4163
  Target,
4123
4164
  Lightbulb,
4124
4165
  Info,
@@ -4128,7 +4169,7 @@ import {
4128
4169
  Check as Check3,
4129
4170
  X as X4
4130
4171
  } from "lucide-react";
4131
- import { Fragment as Fragment6, jsx as jsx24, jsxs as jsxs14 } from "react/jsx-runtime";
4172
+ import { Fragment as Fragment5, jsx as jsx24, jsxs as jsxs14 } from "react/jsx-runtime";
4132
4173
  var getInitials2 = (name, email) => {
4133
4174
  if (name) {
4134
4175
  return name.split(" ").map((n) => n[0]).slice(0, 2).join("").toUpperCase();
@@ -4321,7 +4362,7 @@ var UserProfile = ({
4321
4362
  ] })
4322
4363
  ] })
4323
4364
  ] }),
4324
- normalizedFields.length > 0 && /* @__PURE__ */ jsxs14(Fragment6, { children: [
4365
+ normalizedFields.length > 0 && /* @__PURE__ */ jsxs14(Fragment5, { children: [
4325
4366
  /* @__PURE__ */ jsx24(Separator, {}),
4326
4367
  /* @__PURE__ */ jsxs14("div", { className: "space-y-3", children: [
4327
4368
  /* @__PURE__ */ jsx24("h3", { className: "text-sm font-medium text-muted-foreground uppercase tracking-wider", children: labels.customFields }),
@@ -4465,7 +4506,7 @@ var UserProfile = ({
4465
4506
  size: "icon",
4466
4507
  className: "h-7 w-7",
4467
4508
  onClick: () => onDeleteMemory(memory.id),
4468
- children: /* @__PURE__ */ jsx24(Trash23, { className: "h-3.5 w-3.5 text-destructive" })
4509
+ children: /* @__PURE__ */ jsx24(Trash24, { className: "h-3.5 w-3.5 text-destructive" })
4469
4510
  }
4470
4511
  )
4471
4512
  ] })
@@ -5051,7 +5092,7 @@ import {
5051
5092
  MessageSquare as MessageSquare2,
5052
5093
  MoreVertical as MoreVertical2,
5053
5094
  Edit2 as Edit22,
5054
- Trash2 as Trash24,
5095
+ Trash2 as Trash25,
5055
5096
  Archive as Archive2,
5056
5097
  Search as Search2,
5057
5098
  Filter as Filter2,
@@ -5060,7 +5101,7 @@ import {
5060
5101
  X as X5,
5061
5102
  Check as Check4
5062
5103
  } from "lucide-react";
5063
- import { Fragment as Fragment7, jsx as jsx26, jsxs as jsxs16 } from "react/jsx-runtime";
5104
+ import { Fragment as Fragment6, jsx as jsx26, jsxs as jsxs16 } from "react/jsx-runtime";
5064
5105
  var ThreadItem = ({ thread, isActive, config, onSelect, onRename, onDelete, onArchive }) => {
5065
5106
  const [isEditing, setIsEditing] = useState9(false);
5066
5107
  const [editTitle, setEditTitle] = useState9(thread.title);
@@ -5105,7 +5146,7 @@ var ThreadItem = ({ thread, isActive, config, onSelect, onRename, onDelete, onAr
5105
5146
  ),
5106
5147
  /* @__PURE__ */ jsx26(Button, { size: "sm", variant: "ghost", onClick: handleSaveEdit, children: /* @__PURE__ */ jsx26(Check4, { className: "h-3 w-3" }) }),
5107
5148
  /* @__PURE__ */ jsx26(Button, { size: "sm", variant: "ghost", onClick: handleCancelEdit, children: /* @__PURE__ */ jsx26(X5, { className: "h-3 w-3" }) })
5108
- ] }) : /* @__PURE__ */ jsxs16(Fragment7, { children: [
5149
+ ] }) : /* @__PURE__ */ jsxs16(Fragment6, { children: [
5109
5150
  /* @__PURE__ */ jsx26("h4", { className: "font-medium text-sm truncate mb-1", children: thread.title }),
5110
5151
  /* @__PURE__ */ jsxs16("div", { className: "flex items-center gap-2 text-xs text-muted-foreground", children: [
5111
5152
  /* @__PURE__ */ jsxs16("div", { className: "flex items-center gap-1", children: [
@@ -5118,7 +5159,7 @@ var ThreadItem = ({ thread, isActive, config, onSelect, onRename, onDelete, onAr
5118
5159
  /* @__PURE__ */ jsx26(Calendar2, { className: "h-3 w-3" }),
5119
5160
  formatDate(thread.updatedAt, config?.labels)
5120
5161
  ] }),
5121
- thread.isArchived && /* @__PURE__ */ jsxs16(Fragment7, { children: [
5162
+ thread.isArchived && /* @__PURE__ */ jsxs16(Fragment6, { children: [
5122
5163
  /* @__PURE__ */ jsx26(Separator, { orientation: "vertical", className: "h-3" }),
5123
5164
  /* @__PURE__ */ jsxs16(Badge, { variant: "secondary", className: "text-xs", children: [
5124
5165
  /* @__PURE__ */ jsx26(Archive2, { className: "h-2 w-2 mr-1" }),
@@ -5140,7 +5181,7 @@ var ThreadItem = ({ thread, isActive, config, onSelect, onRename, onDelete, onAr
5140
5181
  ] }),
5141
5182
  /* @__PURE__ */ jsx26(DropdownMenuSeparator, {}),
5142
5183
  /* @__PURE__ */ jsxs16(DropdownMenuItem, { onClick: onDelete, className: "text-destructive", children: [
5143
- /* @__PURE__ */ jsx26(Trash24, { className: "h-4 w-4 mr-2" }),
5184
+ /* @__PURE__ */ jsx26(Trash25, { className: "h-4 w-4 mr-2" }),
5144
5185
  config?.labels?.deleteThread || "Delete"
5145
5186
  ] })
5146
5187
  ] })