@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 +119 -78
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +134 -93
- package/dist/index.js.map +1 -1
- package/dist/styles.css +86 -2
- package/package.json +1 -1
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
|
|
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?.
|
|
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
|
-
|
|
3144
|
-
|
|
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
|
-
|
|
3155
|
-
|
|
3156
|
-
|
|
3157
|
-
|
|
3158
|
-
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
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.
|
|
3176
|
-
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
3177
|
-
|
|
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:
|
|
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
|
|
3185
|
-
] }),
|
|
3186
|
-
|
|
3187
|
-
|
|
3188
|
-
|
|
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.
|
|
3202
|
-
|
|
3203
|
-
|
|
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
|
-
|
|
3207
|
-
|
|
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.
|
|
3212
|
-
|
|
3213
|
-
|
|
3214
|
-
|
|
3215
|
-
|
|
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 || "
|
|
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 || "
|
|
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: "
|
|
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
|
-
"
|
|
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
|
-
"
|
|
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 = "
|
|
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)(
|
|
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(`
|
|
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("
|
|
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(
|
|
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
|
|
3827
|
-
|
|
3828
|
-
|
|
3829
|
-
}, [
|
|
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
|
-
|
|
3945
|
+
onCancelAutoSend: () => {
|
|
3946
|
+
cancelVoiceAutoSend();
|
|
3947
|
+
},
|
|
3948
|
+
onDiscard: () => {
|
|
3908
3949
|
void cancelVoiceCapture();
|
|
3909
3950
|
},
|
|
3910
|
-
onSendNow: sendVoiceDraft,
|
|
3911
3951
|
onRecordAgain: () => {
|
|
3912
|
-
void
|
|
3952
|
+
void startVoiceCapture();
|
|
3913
3953
|
},
|
|
3954
|
+
onSendNow: sendVoiceDraft,
|
|
3914
3955
|
onExit: () => {
|
|
3915
3956
|
void closeVoiceComposer();
|
|
3916
3957
|
}
|