@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.cjs CHANGED
@@ -88,16 +88,19 @@ var defaultChatConfig = {
88
88
  recordAudioTooltip: "Record audio",
89
89
  voiceEnter: "Voice input",
90
90
  voiceExit: "Use keyboard",
91
- voiceTitle: "Voice input",
91
+ voiceTitle: "Voice",
92
+ voiceIdle: "Tap the mic to record",
92
93
  voicePreparing: "Preparing microphone...",
93
94
  voiceWaiting: "Waiting for speech...",
94
95
  voiceListening: "Listening...",
95
96
  voiceFinishing: "Finishing capture...",
96
97
  voiceReview: "Ready to send",
98
+ voiceSending: "Sending...",
97
99
  voiceStart: "Start recording",
98
100
  voiceStop: "Stop recording",
99
101
  voiceSendNow: "Send now",
100
102
  voiceCancel: "Cancel",
103
+ voiceDiscard: "Delete recording",
101
104
  voiceRecordAgain: "Record again",
102
105
  voiceAutoSendIn: "Auto-sends in {{seconds}}s",
103
106
  voiceTranscriptPending: "Transcript unavailable",
@@ -167,6 +170,7 @@ var defaultChatConfig = {
167
170
  },
168
171
  voiceCompose: {
169
172
  enabled: false,
173
+ defaultMode: "text",
170
174
  autoSendDelayMs: 5e3,
171
175
  persistComposer: true,
172
176
  showTranscriptPreview: true,
@@ -3109,12 +3113,12 @@ var resolveStateLabel = (state, labels, errorMessage) => {
3109
3113
  case "review":
3110
3114
  return labels?.voiceReview || "Ready to send";
3111
3115
  case "sending":
3112
- return "Sending...";
3116
+ return labels?.voiceSending || "Sending...";
3113
3117
  case "error":
3114
3118
  return errorMessage || labels?.voiceCaptureError || "Unable to capture audio.";
3115
3119
  case "idle":
3116
3120
  default:
3117
- return labels?.voiceTitle || "Voice input";
3121
+ return labels?.voiceIdle || "Tap the mic to record";
3118
3122
  }
3119
3123
  };
3120
3124
  var resolveTranscriptText = (transcript, transcriptMode) => {
@@ -3131,18 +3135,21 @@ var VoiceComposer = ({
3131
3135
  transcript,
3132
3136
  transcriptMode,
3133
3137
  showTranscriptPreview,
3138
+ attachment,
3134
3139
  durationMs,
3135
3140
  audioLevel,
3136
3141
  countdownMs,
3137
3142
  autoSendDelayMs,
3143
+ isAutoSendActive,
3138
3144
  errorMessage,
3139
3145
  disabled = false,
3140
3146
  labels,
3141
3147
  onStart,
3142
3148
  onStop,
3143
- onCancel,
3144
- onSendNow,
3149
+ onCancelAutoSend,
3150
+ onDiscard,
3145
3151
  onRecordAgain,
3152
+ onSendNow,
3146
3153
  onExit
3147
3154
  }) => {
3148
3155
  const transcriptText = resolveTranscriptText(transcript, transcriptMode);
@@ -3150,12 +3157,14 @@ var VoiceComposer = ({
3150
3157
  const countdownValue = autoSendDelayMs > 0 ? Math.min(100, Math.max(0, (autoSendDelayMs - countdownMs) / autoSendDelayMs * 100)) : 100;
3151
3158
  const isBusy = state === "preparing" || state === "finishing" || state === "sending";
3152
3159
  const isCapturing = state === "waiting_for_speech" || state === "listening";
3160
+ const isReviewing = state === "review";
3153
3161
  const levelValue = isCapturing || state === "preparing" || state === "finishing" ? Math.max(8, Math.round(audioLevel * 100)) : 0;
3154
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "w-full md:min-w-3xl max-w-3xl rounded-xl border bg-background p-4 shadow-sm", children: [
3155
- /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex items-center justify-between gap-3", children: [
3156
- /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex items-center gap-2", children: [
3157
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(Badge, { variant: "outline", children: labels?.voiceTitle || "Voice input" }),
3158
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "text-sm text-muted-foreground", children: resolveStateLabel(state, labels, errorMessage) })
3162
+ const headerLabel = state === "error" ? labels?.voiceCaptureError || "Unable to capture audio." : resolveStateLabel(state, labels, errorMessage);
3163
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "w-full max-w-3xl rounded-xl border bg-background p-3 shadow-sm sm:p-4 md:min-w-3xl", children: [
3164
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex items-center justify-between gap-2 sm:gap-3", children: [
3165
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex min-w-0 items-center gap-2", children: [
3166
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(Badge, { variant: "outline", children: labels?.voiceTitle || "Voice" }),
3167
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "truncate text-xs sm:text-sm text-muted-foreground", children: headerLabel })
3159
3168
  ] }),
3160
3169
  /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
3161
3170
  Button,
@@ -3163,71 +3172,89 @@ var VoiceComposer = ({
3163
3172
  type: "button",
3164
3173
  variant: "ghost",
3165
3174
  size: "sm",
3175
+ className: "shrink-0 px-2 sm:px-3",
3166
3176
  onClick: onExit,
3167
3177
  disabled: disabled || isBusy,
3168
3178
  children: [
3169
3179
  /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react9.Keyboard, { className: "h-4 w-4" }),
3170
- labels?.voiceExit || "Use keyboard"
3180
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "hidden sm:inline", children: labels?.voiceExit || "Use keyboard" })
3171
3181
  ]
3172
3182
  }
3173
3183
  )
3174
3184
  ] }),
3175
- /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("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: [
3176
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "flex h-20 w-20 items-center justify-center rounded-full bg-primary/10", children: isBusy ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react9.Loader2, { className: "h-8 w-8 animate-spin text-primary" }) : isCapturing ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react9.Square, { className: "h-8 w-8 text-primary" }) : /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react9.Mic, { className: "h-8 w-8 text-primary" }) }),
3177
- /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "w-full max-w-md space-y-2", children: [
3185
+ !isReviewing ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("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__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "mx-auto flex w-full max-w-sm flex-col items-center gap-3", children: [
3186
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
3187
+ Button,
3188
+ {
3189
+ type: "button",
3190
+ size: "icon",
3191
+ variant: isCapturing ? "destructive" : "outline",
3192
+ 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"}`,
3193
+ onClick: isCapturing ? onStop : onStart,
3194
+ disabled: disabled || isBusy,
3195
+ children: isBusy ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react9.Loader2, { className: "h-7 w-7 animate-spin" }) : isCapturing ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react9.Square, { className: "h-7 w-7" }) : /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react9.Mic, { className: "h-7 w-7" })
3196
+ }
3197
+ ),
3198
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "w-full space-y-2", children: [
3178
3199
  /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(Progress, { value: levelValue, className: "h-2" }),
3179
3200
  /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex items-center justify-between text-xs text-muted-foreground", children: [
3180
3201
  /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { children: formatDuration(durationMs) }),
3181
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { children: resolveStateLabel(state, labels, errorMessage) })
3202
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { children: isCapturing ? labels?.voiceStop || "Stop recording" : labels?.voiceStart || "Start recording" })
3182
3203
  ] })
3183
3204
  ] }),
3184
- showTranscriptPreview && transcriptMode !== "none" && transcriptText && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "w-full max-w-md rounded-lg border bg-background px-3 py-2 text-left text-sm", children: transcriptText })
3185
- ] }),
3186
- state === "review" && autoSendDelayMs > 0 && /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "mt-4 space-y-2", children: [
3187
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(Progress, { value: countdownValue, className: "h-2" }),
3188
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "text-center text-xs text-muted-foreground", children: interpolateSeconds(labels?.voiceAutoSendIn, countdownSeconds) })
3189
- ] }),
3190
- state === "error" && errorMessage && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "mt-4 rounded-lg border border-destructive/30 bg-destructive/5 px-3 py-2 text-sm text-destructive", children: errorMessage }),
3191
- /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "mt-4 flex flex-wrap items-center justify-center gap-2", children: [
3192
- state === "idle" && /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(Button, { type: "button", onClick: onStart, disabled, children: [
3193
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react9.Mic, { className: "h-4 w-4" }),
3194
- labels?.voiceStart || "Start recording"
3195
- ] }),
3196
- isCapturing && /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_jsx_runtime21.Fragment, { children: [
3197
- /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(Button, { type: "button", onClick: onStop, disabled, children: [
3198
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react9.Square, { className: "h-4 w-4" }),
3199
- labels?.voiceStop || "Stop recording"
3205
+ showTranscriptPreview && transcriptMode !== "none" && transcriptText && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "w-full rounded-lg border bg-background px-3 py-2 text-left text-sm", children: transcriptText })
3206
+ ] }) }) : /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "mt-3 rounded-xl border bg-muted/20 p-3 sm:p-4", children: [
3207
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex items-start justify-between gap-2", children: [
3208
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "min-w-0", children: [
3209
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "text-sm font-medium text-foreground", children: labels?.voiceReview || "Ready to send" }),
3210
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "text-xs text-muted-foreground", children: formatDuration(durationMs) })
3200
3211
  ] }),
3201
- /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(Button, { type: "button", variant: "outline", onClick: onCancel, disabled, children: [
3202
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react9.X, { className: "h-4 w-4" }),
3203
- labels?.voiceCancel || "Cancel"
3204
- ] })
3212
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
3213
+ Button,
3214
+ {
3215
+ type: "button",
3216
+ variant: "ghost",
3217
+ size: "icon",
3218
+ className: "h-8 w-8 shrink-0 text-muted-foreground hover:text-destructive",
3219
+ onClick: onDiscard,
3220
+ disabled,
3221
+ "aria-label": labels?.voiceDiscard || "Delete recording",
3222
+ title: labels?.voiceDiscard || "Delete recording",
3223
+ children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react9.Trash2, { className: "h-4 w-4" })
3224
+ }
3225
+ )
3205
3226
  ] }),
3206
- state === "review" && /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_jsx_runtime21.Fragment, { children: [
3207
- /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(Button, { type: "button", variant: "outline", onClick: onCancel, disabled, children: [
3227
+ attachment && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "mt-3 rounded-lg bg-background p-2", children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("audio", { controls: true, preload: "metadata", className: "w-full", children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("source", { src: attachment.dataUrl, type: attachment.mimeType }) }) }),
3228
+ showTranscriptPreview && transcriptMode !== "none" && transcriptText && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "mt-3 rounded-lg border bg-background px-3 py-2 text-left text-sm", children: transcriptText }),
3229
+ isAutoSendActive && autoSendDelayMs > 0 && /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "mt-3 space-y-2", children: [
3230
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(Progress, { value: countdownValue, className: "h-2" }),
3231
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "text-center text-xs text-muted-foreground", children: interpolateSeconds(labels?.voiceAutoSendIn, countdownSeconds) })
3232
+ ] }),
3233
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "mt-3 flex items-center justify-end gap-2", children: [
3234
+ isAutoSendActive && /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(Button, { type: "button", variant: "ghost", size: "sm", onClick: onCancelAutoSend, disabled, children: [
3208
3235
  /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react9.X, { className: "h-4 w-4" }),
3209
3236
  labels?.voiceCancel || "Cancel"
3210
3237
  ] }),
3211
- /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(Button, { type: "button", variant: "outline", onClick: onRecordAgain, disabled, children: [
3212
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react9.RotateCcw, { className: "h-4 w-4" }),
3213
- labels?.voiceRecordAgain || "Record again"
3214
- ] }),
3215
- /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(Button, { type: "button", onClick: onSendNow, disabled, children: [
3238
+ !isAutoSendActive && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
3239
+ Button,
3240
+ {
3241
+ type: "button",
3242
+ variant: "outline",
3243
+ size: "icon",
3244
+ onClick: onRecordAgain,
3245
+ disabled,
3246
+ "aria-label": labels?.voiceRecordAgain || "Record again",
3247
+ title: labels?.voiceRecordAgain || "Record again",
3248
+ children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react9.Mic, { className: "h-4 w-4" })
3249
+ }
3250
+ ),
3251
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(Button, { type: "button", size: "sm", onClick: onSendNow, disabled, children: [
3216
3252
  /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react9.Send, { className: "h-4 w-4" }),
3217
3253
  labels?.voiceSendNow || "Send now"
3218
3254
  ] })
3219
- ] }),
3220
- state === "error" && /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_jsx_runtime21.Fragment, { children: [
3221
- /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(Button, { type: "button", variant: "outline", onClick: onCancel, disabled, children: [
3222
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react9.X, { className: "h-4 w-4" }),
3223
- labels?.voiceCancel || "Cancel"
3224
- ] }),
3225
- /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(Button, { type: "button", onClick: onRecordAgain, disabled, children: [
3226
- /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react9.RotateCcw, { className: "h-4 w-4" }),
3227
- labels?.voiceRecordAgain || "Record again"
3228
- ] })
3229
3255
  ] })
3230
- ] })
3256
+ ] }),
3257
+ state === "error" && errorMessage && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "mt-3 rounded-lg border border-destructive/30 bg-destructive/5 px-3 py-2 text-sm text-destructive", children: errorMessage })
3231
3258
  ] });
3232
3259
  };
3233
3260
 
@@ -3334,7 +3361,7 @@ var AttachmentPreview = (0, import_react5.memo)(function AttachmentPreview2({ at
3334
3361
  "img",
3335
3362
  {
3336
3363
  src: attachment.dataUrl,
3337
- alt: attachment.fileName || "Anexo",
3364
+ alt: attachment.fileName || "Attachment",
3338
3365
  className: "w-full h-20 object-cover rounded"
3339
3366
  }
3340
3367
  ),
@@ -3383,7 +3410,7 @@ var AttachmentPreview = (0, import_react5.memo)(function AttachmentPreview2({ at
3383
3410
  }
3384
3411
  ),
3385
3412
  /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "flex-1", children: [
3386
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("p", { className: "text-xs font-medium", children: attachment.fileName || "\xC1udio" }),
3413
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("p", { className: "text-xs font-medium", children: attachment.fileName || "Audio" }),
3387
3414
  /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("p", { className: "text-xs text-muted-foreground", children: formatDuration2(attachment.durationMs) })
3388
3415
  ] }),
3389
3416
  /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
@@ -3435,7 +3462,7 @@ var AudioRecorder = (0, import_react5.memo)(function AudioRecorder2({ isRecordin
3435
3462
  return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(Card, { className: "border-red-200 bg-red-50 dark:border-red-800 dark:bg-red-950", children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(CardContent, { className: "p-3", children: /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "flex items-center gap-3", children: [
3436
3463
  /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "flex items-center gap-2", children: [
3437
3464
  /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "h-3 w-3 bg-red-500 rounded-full animate-pulse" }),
3438
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "text-sm font-medium text-red-700 dark:text-red-300", children: "Gravando" })
3465
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "text-sm font-medium text-red-700 dark:text-red-300", children: config?.labels?.voiceListening || "Recording" })
3439
3466
  ] }),
3440
3467
  /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(Badge, { variant: "outline", className: "text-xs", children: formatTime(recordingDuration) }),
3441
3468
  /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "flex gap-1 ml-auto", children: [
@@ -3447,7 +3474,7 @@ var AudioRecorder = (0, import_react5.memo)(function AudioRecorder2({ isRecordin
3447
3474
  onClick: onCancel,
3448
3475
  children: [
3449
3476
  /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_lucide_react10.X, { className: "h-3 w-3 mr-1" }),
3450
- "Cancelar"
3477
+ config?.labels?.cancel || "Cancel"
3451
3478
  ]
3452
3479
  }
3453
3480
  ),
@@ -3459,7 +3486,7 @@ var AudioRecorder = (0, import_react5.memo)(function AudioRecorder2({ isRecordin
3459
3486
  onClick: onStopRecording,
3460
3487
  children: [
3461
3488
  /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_lucide_react10.Square, { className: "h-3 w-3 mr-1" }),
3462
- "Parar"
3489
+ config?.labels?.voiceStop || "Stop"
3463
3490
  ]
3464
3491
  }
3465
3492
  )
@@ -3482,7 +3509,7 @@ var ChatInput = (0, import_react5.memo)(function ChatInput2({
3482
3509
  onSubmit,
3483
3510
  attachments,
3484
3511
  onAttachmentsChange,
3485
- placeholder = "Digite sua mensagem...",
3512
+ placeholder = "Type your message...",
3486
3513
  disabled = false,
3487
3514
  isGenerating = false,
3488
3515
  onStopGeneration,
@@ -3495,17 +3522,27 @@ var ChatInput = (0, import_react5.memo)(function ChatInput2({
3495
3522
  className = "",
3496
3523
  config
3497
3524
  }) {
3525
+ const voiceComposeEnabled = config?.voiceCompose?.enabled === true;
3526
+ const voiceDefaultMode = config?.voiceCompose?.defaultMode ?? "text";
3527
+ const voiceAutoSendDelayMs = config?.voiceCompose?.autoSendDelayMs ?? 5e3;
3528
+ const voicePersistComposer = config?.voiceCompose?.persistComposer ?? true;
3529
+ const voiceShowTranscriptPreview = config?.voiceCompose?.showTranscriptPreview ?? true;
3530
+ const voiceTranscriptMode = config?.voiceCompose?.transcriptMode ?? "final-only";
3531
+ const voiceMaxRecordingMs = config?.voiceCompose?.maxRecordingMs;
3498
3532
  const [isRecording, setIsRecording] = (0, import_react5.useState)(false);
3499
3533
  const { setContext } = useChatUserContext();
3500
3534
  const [recordingDuration, setRecordingDuration] = (0, import_react5.useState)(0);
3501
3535
  const [uploadProgress, setUploadProgress] = (0, import_react5.useState)(/* @__PURE__ */ new Map());
3502
- const [isVoiceComposerOpen, setIsVoiceComposerOpen] = (0, import_react5.useState)(false);
3536
+ const [isVoiceComposerOpen, setIsVoiceComposerOpen] = (0, import_react5.useState)(
3537
+ () => voiceComposeEnabled && voiceDefaultMode === "voice"
3538
+ );
3503
3539
  const [voiceState, setVoiceState] = (0, import_react5.useState)("idle");
3504
3540
  const [voiceDraft, setVoiceDraft] = (0, import_react5.useState)(null);
3505
3541
  const [voiceTranscript, setVoiceTranscript] = (0, import_react5.useState)(clearVoiceTranscript);
3506
3542
  const [voiceDurationMs, setVoiceDurationMs] = (0, import_react5.useState)(0);
3507
3543
  const [voiceAudioLevel, setVoiceAudioLevel] = (0, import_react5.useState)(0);
3508
3544
  const [voiceCountdownMs, setVoiceCountdownMs] = (0, import_react5.useState)(0);
3545
+ const [isVoiceAutoSendActive, setIsVoiceAutoSendActive] = (0, import_react5.useState)(false);
3509
3546
  const [voiceError, setVoiceError] = (0, import_react5.useState)(null);
3510
3547
  const textareaRef = (0, import_react5.useRef)(null);
3511
3548
  const fileInputRef = (0, import_react5.useRef)(null);
@@ -3514,12 +3551,6 @@ var ChatInput = (0, import_react5.memo)(function ChatInput2({
3514
3551
  const recordingInterval = (0, import_react5.useRef)(null);
3515
3552
  const mediaStreamRef = (0, import_react5.useRef)(null);
3516
3553
  const voiceProviderRef = (0, import_react5.useRef)(null);
3517
- const voiceComposeEnabled = config?.voiceCompose?.enabled === true;
3518
- const voiceAutoSendDelayMs = config?.voiceCompose?.autoSendDelayMs ?? 5e3;
3519
- const voicePersistComposer = config?.voiceCompose?.persistComposer ?? true;
3520
- const voiceShowTranscriptPreview = config?.voiceCompose?.showTranscriptPreview ?? true;
3521
- const voiceTranscriptMode = config?.voiceCompose?.transcriptMode ?? "final-only";
3522
- const voiceMaxRecordingMs = config?.voiceCompose?.maxRecordingMs;
3523
3554
  (0, import_react5.useEffect)(() => {
3524
3555
  return () => {
3525
3556
  if (mediaStreamRef.current) {
@@ -3549,7 +3580,7 @@ var ChatInput = (0, import_react5.memo)(function ChatInput2({
3549
3580
  };
3550
3581
  const processFile = async (file) => {
3551
3582
  if (file.size > maxFileSize) {
3552
- alert(`Arquivo muito grande. M\xE1ximo permitido: ${Math.round(maxFileSize / 1024 / 1024)}MB`);
3583
+ alert(`File too large. Max allowed: ${Math.round(maxFileSize / 1024 / 1024)}MB`);
3553
3584
  return null;
3554
3585
  }
3555
3586
  const fileId = `${Date.now()}_${Math.random().toString(36).slice(2)}`;
@@ -3608,7 +3639,7 @@ var ChatInput = (0, import_react5.memo)(function ChatInput2({
3608
3639
  newMap.delete(fileId);
3609
3640
  return newMap;
3610
3641
  });
3611
- alert("Erro ao processar arquivo");
3642
+ alert("Failed to process file");
3612
3643
  return null;
3613
3644
  }
3614
3645
  };
@@ -3683,7 +3714,7 @@ var ChatInput = (0, import_react5.memo)(function ChatInput2({
3683
3714
  }, 1e3);
3684
3715
  } catch (error) {
3685
3716
  console.error("Error starting recording:", error);
3686
- alert("N\xE3o foi poss\xEDvel acessar o microfone");
3717
+ alert(config?.labels?.voicePermissionDenied || "Microphone access was denied.");
3687
3718
  }
3688
3719
  };
3689
3720
  const stopRecording = () => {
@@ -3715,6 +3746,7 @@ var ChatInput = (0, import_react5.memo)(function ChatInput2({
3715
3746
  setVoiceDurationMs(0);
3716
3747
  setVoiceAudioLevel(0);
3717
3748
  setVoiceCountdownMs(0);
3749
+ setIsVoiceAutoSendActive(false);
3718
3750
  setVoiceError(null);
3719
3751
  }, []);
3720
3752
  const ensureVoiceProvider = (0, import_react5.useCallback)(async () => {
@@ -3733,6 +3765,7 @@ var ChatInput = (0, import_react5.memo)(function ChatInput2({
3733
3765
  setVoiceDurationMs(segment.attachment.durationMs ?? 0);
3734
3766
  setVoiceAudioLevel(0);
3735
3767
  setVoiceCountdownMs(voiceAutoSendDelayMs);
3768
+ setIsVoiceAutoSendActive(voiceAutoSendDelayMs > 0);
3736
3769
  setVoiceError(null);
3737
3770
  setVoiceState("review");
3738
3771
  },
@@ -3740,6 +3773,7 @@ var ChatInput = (0, import_react5.memo)(function ChatInput2({
3740
3773
  setVoiceError(resolveVoiceErrorMessage(error, config));
3741
3774
  setVoiceAudioLevel(0);
3742
3775
  setVoiceCountdownMs(0);
3776
+ setIsVoiceAutoSendActive(false);
3743
3777
  setVoiceState("error");
3744
3778
  }
3745
3779
  }, {
@@ -3772,6 +3806,7 @@ var ChatInput = (0, import_react5.memo)(function ChatInput2({
3772
3806
  setVoiceTranscript(clearVoiceTranscript());
3773
3807
  setVoiceAudioLevel(0);
3774
3808
  setVoiceDurationMs(0);
3809
+ setIsVoiceAutoSendActive(false);
3775
3810
  try {
3776
3811
  const provider = await ensureVoiceProvider();
3777
3812
  await provider.start();
@@ -3809,6 +3844,7 @@ var ChatInput = (0, import_react5.memo)(function ChatInput2({
3809
3844
  }
3810
3845
  setVoiceState("sending");
3811
3846
  setVoiceCountdownMs(0);
3847
+ setIsVoiceAutoSendActive(false);
3812
3848
  onSubmit("", [...attachments, voiceDraft.attachment]);
3813
3849
  onChange("");
3814
3850
  onAttachmentsChange([]);
@@ -3823,12 +3859,12 @@ var ChatInput = (0, import_react5.memo)(function ChatInput2({
3823
3859
  onAttachmentsChange,
3824
3860
  finalizeVoiceComposerAfterSend
3825
3861
  ]);
3826
- const recordVoiceAgain = (0, import_react5.useCallback)(async () => {
3827
- resetVoiceComposerState("idle");
3828
- await startVoiceCapture();
3829
- }, [resetVoiceComposerState, startVoiceCapture]);
3862
+ const cancelVoiceAutoSend = (0, import_react5.useCallback)(() => {
3863
+ setVoiceCountdownMs(0);
3864
+ setIsVoiceAutoSendActive(false);
3865
+ }, []);
3830
3866
  (0, import_react5.useEffect)(() => {
3831
- if (voiceState !== "review" || !voiceDraft || voiceAutoSendDelayMs <= 0) {
3867
+ if (voiceState !== "review" || !voiceDraft || voiceAutoSendDelayMs <= 0 || !isVoiceAutoSendActive) {
3832
3868
  return;
3833
3869
  }
3834
3870
  const startedAt = Date.now();
@@ -3842,7 +3878,7 @@ var ChatInput = (0, import_react5.memo)(function ChatInput2({
3842
3878
  }
3843
3879
  }, 100);
3844
3880
  return () => clearInterval(timer);
3845
- }, [voiceState, voiceDraft, voiceAutoSendDelayMs, sendVoiceDraft]);
3881
+ }, [voiceState, voiceDraft, voiceAutoSendDelayMs, isVoiceAutoSendActive, sendVoiceDraft]);
3846
3882
  const removeAttachment = (index) => {
3847
3883
  const newAttachments = attachments.filter((_, i) => i !== index);
3848
3884
  onAttachmentsChange(newAttachments);
@@ -3891,10 +3927,12 @@ var ChatInput = (0, import_react5.memo)(function ChatInput2({
3891
3927
  transcript: voiceTranscript,
3892
3928
  transcriptMode: voiceTranscriptMode,
3893
3929
  showTranscriptPreview: voiceShowTranscriptPreview,
3930
+ attachment: voiceDraft?.attachment ?? null,
3894
3931
  durationMs: voiceDurationMs,
3895
3932
  audioLevel: voiceAudioLevel,
3896
3933
  countdownMs: voiceCountdownMs,
3897
3934
  autoSendDelayMs: voiceAutoSendDelayMs,
3935
+ isAutoSendActive: isVoiceAutoSendActive,
3898
3936
  errorMessage: voiceError,
3899
3937
  disabled: disabled || isGenerating,
3900
3938
  labels: config?.labels,
@@ -3904,13 +3942,16 @@ var ChatInput = (0, import_react5.memo)(function ChatInput2({
3904
3942
  onStop: () => {
3905
3943
  void stopVoiceCapture();
3906
3944
  },
3907
- onCancel: () => {
3945
+ onCancelAutoSend: () => {
3946
+ cancelVoiceAutoSend();
3947
+ },
3948
+ onDiscard: () => {
3908
3949
  void cancelVoiceCapture();
3909
3950
  },
3910
- onSendNow: sendVoiceDraft,
3911
3951
  onRecordAgain: () => {
3912
- void recordVoiceAgain();
3952
+ void startVoiceCapture();
3913
3953
  },
3954
+ onSendNow: sendVoiceDraft,
3914
3955
  onExit: () => {
3915
3956
  void closeVoiceComposer();
3916
3957
  }