@copilotz/chat-ui 0.1.34 → 0.1.35
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 +130 -46
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.js +130 -46
- package/dist/index.js.map +1 -1
- package/dist/styles.css +7 -0
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -29,6 +29,7 @@ type AudioAttachment = Extract<MediaAttachment, {
|
|
|
29
29
|
kind: 'audio';
|
|
30
30
|
}>;
|
|
31
31
|
type VoiceComposerState = 'idle' | 'preparing' | 'waiting_for_speech' | 'listening' | 'finishing' | 'review' | 'sending' | 'error';
|
|
32
|
+
type VoiceReviewMode = 'manual' | 'armed';
|
|
32
33
|
type VoiceTranscriptMode = 'none' | 'final-only' | 'partial-and-final';
|
|
33
34
|
interface VoiceTranscript {
|
|
34
35
|
partial?: string;
|
|
@@ -130,6 +131,8 @@ interface ChatConfig {
|
|
|
130
131
|
voiceFinishing?: string;
|
|
131
132
|
voiceReview?: string;
|
|
132
133
|
voiceSending?: string;
|
|
134
|
+
voiceReviewArmedHint?: string;
|
|
135
|
+
voiceReviewPausedHint?: string;
|
|
133
136
|
voiceStart?: string;
|
|
134
137
|
voiceStop?: string;
|
|
135
138
|
voiceSendNow?: string;
|
|
@@ -202,6 +205,7 @@ interface ChatConfig {
|
|
|
202
205
|
voiceCompose?: {
|
|
203
206
|
enabled?: boolean;
|
|
204
207
|
defaultMode?: 'text' | 'voice';
|
|
208
|
+
reviewMode?: VoiceReviewMode;
|
|
205
209
|
autoSendDelayMs?: number;
|
|
206
210
|
persistComposer?: boolean;
|
|
207
211
|
showTranscriptPreview?: boolean;
|
|
@@ -669,4 +673,4 @@ declare const chatUtils: {
|
|
|
669
673
|
generateThreadTitle: (firstMessage: string) => string;
|
|
670
674
|
};
|
|
671
675
|
|
|
672
|
-
export { type AgentOption, type AudioAttachment, type ChatCallbacks, type ChatConfig, ChatHeader, type ChatHeaderConfig, type ChatHeaderProps, ChatInput, type ChatMessage, type ChatState, type ChatThread, ChatUI, type ChatUserContext, ChatUserContextProvider, type ChatV2Props, type CreateVoiceProvider, type CustomField, type FileUploadProgress, type MediaAttachment, type MemoryItem, Message, type MessageAction, type MessageActionEvent, Sidebar, type SidebarConfig, type SidebarProps, type StateCallback, type StreamingUpdate, ThreadManager, type ToolCall, type UserCustomField, UserMenu, type UserMenuCallbacks, type UserMenuConfig, type UserMenuProps, type UserMenuUser, UserProfile, type UserProfileConfig, type UserProfileProps, type UserProfileUser, type VoiceComposerState, type VoiceProvider, type VoiceProviderHandlers, type VoiceProviderOptions, type VoiceSegment, type VoiceTranscript, type VoiceTranscriptMode, chatConfigPresets, chatUtils, cn, configUtils, createObjectUrlFromDataUrl, defaultChatConfig, featureFlags, formatDate, mergeConfig, themeUtils, useChatUserContext, validateConfig };
|
|
676
|
+
export { type AgentOption, type AudioAttachment, type ChatCallbacks, type ChatConfig, ChatHeader, type ChatHeaderConfig, type ChatHeaderProps, ChatInput, type ChatMessage, type ChatState, type ChatThread, ChatUI, type ChatUserContext, ChatUserContextProvider, type ChatV2Props, type CreateVoiceProvider, type CustomField, type FileUploadProgress, type MediaAttachment, type MemoryItem, Message, type MessageAction, type MessageActionEvent, Sidebar, type SidebarConfig, type SidebarProps, type StateCallback, type StreamingUpdate, ThreadManager, type ToolCall, type UserCustomField, UserMenu, type UserMenuCallbacks, type UserMenuConfig, type UserMenuProps, type UserMenuUser, UserProfile, type UserProfileConfig, type UserProfileProps, type UserProfileUser, type VoiceComposerState, type VoiceProvider, type VoiceProviderHandlers, type VoiceProviderOptions, type VoiceReviewMode, type VoiceSegment, type VoiceTranscript, type VoiceTranscriptMode, chatConfigPresets, chatUtils, cn, configUtils, createObjectUrlFromDataUrl, defaultChatConfig, featureFlags, formatDate, mergeConfig, themeUtils, useChatUserContext, validateConfig };
|
package/dist/index.d.ts
CHANGED
|
@@ -29,6 +29,7 @@ type AudioAttachment = Extract<MediaAttachment, {
|
|
|
29
29
|
kind: 'audio';
|
|
30
30
|
}>;
|
|
31
31
|
type VoiceComposerState = 'idle' | 'preparing' | 'waiting_for_speech' | 'listening' | 'finishing' | 'review' | 'sending' | 'error';
|
|
32
|
+
type VoiceReviewMode = 'manual' | 'armed';
|
|
32
33
|
type VoiceTranscriptMode = 'none' | 'final-only' | 'partial-and-final';
|
|
33
34
|
interface VoiceTranscript {
|
|
34
35
|
partial?: string;
|
|
@@ -130,6 +131,8 @@ interface ChatConfig {
|
|
|
130
131
|
voiceFinishing?: string;
|
|
131
132
|
voiceReview?: string;
|
|
132
133
|
voiceSending?: string;
|
|
134
|
+
voiceReviewArmedHint?: string;
|
|
135
|
+
voiceReviewPausedHint?: string;
|
|
133
136
|
voiceStart?: string;
|
|
134
137
|
voiceStop?: string;
|
|
135
138
|
voiceSendNow?: string;
|
|
@@ -202,6 +205,7 @@ interface ChatConfig {
|
|
|
202
205
|
voiceCompose?: {
|
|
203
206
|
enabled?: boolean;
|
|
204
207
|
defaultMode?: 'text' | 'voice';
|
|
208
|
+
reviewMode?: VoiceReviewMode;
|
|
205
209
|
autoSendDelayMs?: number;
|
|
206
210
|
persistComposer?: boolean;
|
|
207
211
|
showTranscriptPreview?: boolean;
|
|
@@ -669,4 +673,4 @@ declare const chatUtils: {
|
|
|
669
673
|
generateThreadTitle: (firstMessage: string) => string;
|
|
670
674
|
};
|
|
671
675
|
|
|
672
|
-
export { type AgentOption, type AudioAttachment, type ChatCallbacks, type ChatConfig, ChatHeader, type ChatHeaderConfig, type ChatHeaderProps, ChatInput, type ChatMessage, type ChatState, type ChatThread, ChatUI, type ChatUserContext, ChatUserContextProvider, type ChatV2Props, type CreateVoiceProvider, type CustomField, type FileUploadProgress, type MediaAttachment, type MemoryItem, Message, type MessageAction, type MessageActionEvent, Sidebar, type SidebarConfig, type SidebarProps, type StateCallback, type StreamingUpdate, ThreadManager, type ToolCall, type UserCustomField, UserMenu, type UserMenuCallbacks, type UserMenuConfig, type UserMenuProps, type UserMenuUser, UserProfile, type UserProfileConfig, type UserProfileProps, type UserProfileUser, type VoiceComposerState, type VoiceProvider, type VoiceProviderHandlers, type VoiceProviderOptions, type VoiceSegment, type VoiceTranscript, type VoiceTranscriptMode, chatConfigPresets, chatUtils, cn, configUtils, createObjectUrlFromDataUrl, defaultChatConfig, featureFlags, formatDate, mergeConfig, themeUtils, useChatUserContext, validateConfig };
|
|
676
|
+
export { type AgentOption, type AudioAttachment, type ChatCallbacks, type ChatConfig, ChatHeader, type ChatHeaderConfig, type ChatHeaderProps, ChatInput, type ChatMessage, type ChatState, type ChatThread, ChatUI, type ChatUserContext, ChatUserContextProvider, type ChatV2Props, type CreateVoiceProvider, type CustomField, type FileUploadProgress, type MediaAttachment, type MemoryItem, Message, type MessageAction, type MessageActionEvent, Sidebar, type SidebarConfig, type SidebarProps, type StateCallback, type StreamingUpdate, ThreadManager, type ToolCall, type UserCustomField, UserMenu, type UserMenuCallbacks, type UserMenuConfig, type UserMenuProps, type UserMenuUser, UserProfile, type UserProfileConfig, type UserProfileProps, type UserProfileUser, type VoiceComposerState, type VoiceProvider, type VoiceProviderHandlers, type VoiceProviderOptions, type VoiceReviewMode, type VoiceSegment, type VoiceTranscript, type VoiceTranscriptMode, chatConfigPresets, chatUtils, cn, configUtils, createObjectUrlFromDataUrl, defaultChatConfig, featureFlags, formatDate, mergeConfig, themeUtils, useChatUserContext, validateConfig };
|
package/dist/index.js
CHANGED
|
@@ -40,6 +40,8 @@ var defaultChatConfig = {
|
|
|
40
40
|
voiceFinishing: "Finishing capture...",
|
|
41
41
|
voiceReview: "Ready to send",
|
|
42
42
|
voiceSending: "Sending...",
|
|
43
|
+
voiceReviewArmedHint: "Still listening. Speak to add more before it sends.",
|
|
44
|
+
voiceReviewPausedHint: "Tap the mic to keep adding to this message.",
|
|
43
45
|
voiceStart: "Start recording",
|
|
44
46
|
voiceStop: "Stop recording",
|
|
45
47
|
voiceSendNow: "Send now",
|
|
@@ -115,6 +117,7 @@ var defaultChatConfig = {
|
|
|
115
117
|
voiceCompose: {
|
|
116
118
|
enabled: false,
|
|
117
119
|
defaultMode: "text",
|
|
120
|
+
reviewMode: "manual",
|
|
118
121
|
autoSendDelayMs: 5e3,
|
|
119
122
|
persistComposer: true,
|
|
120
123
|
showTranscriptPreview: true,
|
|
@@ -3240,11 +3243,13 @@ var VoiceComposer = ({
|
|
|
3240
3243
|
countdownMs,
|
|
3241
3244
|
autoSendDelayMs,
|
|
3242
3245
|
isAutoSendActive,
|
|
3246
|
+
reviewMode,
|
|
3243
3247
|
errorMessage,
|
|
3244
3248
|
disabled = false,
|
|
3245
3249
|
labels,
|
|
3246
3250
|
onStart,
|
|
3247
3251
|
onStop,
|
|
3252
|
+
onPauseReview,
|
|
3248
3253
|
onCancelAutoSend,
|
|
3249
3254
|
onDiscard,
|
|
3250
3255
|
onRecordAgain,
|
|
@@ -3256,9 +3261,26 @@ var VoiceComposer = ({
|
|
|
3256
3261
|
const countdownValue = autoSendDelayMs > 0 ? Math.min(100, Math.max(0, (autoSendDelayMs - countdownMs) / autoSendDelayMs * 100)) : 100;
|
|
3257
3262
|
const isBusy = state === "preparing" || state === "finishing" || state === "sending";
|
|
3258
3263
|
const isCapturing = state === "waiting_for_speech" || state === "listening";
|
|
3259
|
-
const
|
|
3264
|
+
const hasDraft = Boolean(attachment);
|
|
3265
|
+
const isDraftLayout = hasDraft;
|
|
3266
|
+
const isArmedDraft = isDraftLayout && reviewMode === "armed" && (state === "waiting_for_speech" || state === "listening");
|
|
3260
3267
|
const levelValue = isCapturing || state === "preparing" || state === "finishing" ? Math.max(8, Math.round(audioLevel * 100)) : 0;
|
|
3261
|
-
const headerLabel = state === "error" ? labels?.voiceCaptureError || "Unable to capture audio." : resolveStateLabel(state, labels, errorMessage);
|
|
3268
|
+
const headerLabel = hasDraft && state !== "sending" && state !== "error" ? labels?.voiceReview || "Ready to send" : state === "error" ? labels?.voiceCaptureError || "Unable to capture audio." : resolveStateLabel(state, labels, errorMessage);
|
|
3269
|
+
const reviewHelperText = isArmedDraft ? labels?.voiceReviewArmedHint || "Speak to add more before it sends." : labels?.voiceReviewPausedHint || labels?.voiceRecordAgain || "Tap the mic to continue this message.";
|
|
3270
|
+
const orbIsListening = state === "listening";
|
|
3271
|
+
const orbCanStop = !isDraftLayout && (state === "waiting_for_speech" || state === "listening");
|
|
3272
|
+
const orbIsReviewBusy = state === "preparing" || state === "finishing" || state === "sending";
|
|
3273
|
+
const handleReviewOrbClick = () => {
|
|
3274
|
+
if (state === "listening") {
|
|
3275
|
+
onStop();
|
|
3276
|
+
return;
|
|
3277
|
+
}
|
|
3278
|
+
if (isArmedDraft) {
|
|
3279
|
+
onPauseReview();
|
|
3280
|
+
return;
|
|
3281
|
+
}
|
|
3282
|
+
onRecordAgain();
|
|
3283
|
+
};
|
|
3262
3284
|
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
3285
|
/* @__PURE__ */ jsxs11("div", { className: "flex items-center justify-between gap-2 sm:gap-3", children: [
|
|
3264
3286
|
/* @__PURE__ */ jsxs11("div", { className: "flex min-w-0 items-center gap-2", children: [
|
|
@@ -3281,7 +3303,7 @@ var VoiceComposer = ({
|
|
|
3281
3303
|
}
|
|
3282
3304
|
)
|
|
3283
3305
|
] }),
|
|
3284
|
-
!
|
|
3306
|
+
!isDraftLayout ? /* @__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
3307
|
/* @__PURE__ */ jsx21(
|
|
3286
3308
|
Button,
|
|
3287
3309
|
{
|
|
@@ -3323,6 +3345,27 @@ var VoiceComposer = ({
|
|
|
3323
3345
|
}
|
|
3324
3346
|
)
|
|
3325
3347
|
] }),
|
|
3348
|
+
/* @__PURE__ */ jsxs11("div", { className: "mt-4 flex flex-col items-center gap-3 text-center", children: [
|
|
3349
|
+
/* @__PURE__ */ jsx21(
|
|
3350
|
+
Button,
|
|
3351
|
+
{
|
|
3352
|
+
type: "button",
|
|
3353
|
+
size: "icon",
|
|
3354
|
+
variant: orbCanStop ? "destructive" : "outline",
|
|
3355
|
+
className: `h-16 w-16 rounded-full sm:h-20 sm:w-20 ${orbIsListening ? "border-red-500 bg-red-500 text-white hover:bg-red-600" : isArmedDraft ? "border-red-200 bg-red-50 text-red-600 shadow-[0_0_0_10px_rgba(239,68,68,0.08)] hover:bg-red-100 hover:text-red-700" : "border-red-200 bg-red-50 text-red-600 hover:bg-red-100 hover:text-red-700"}`,
|
|
3356
|
+
onClick: handleReviewOrbClick,
|
|
3357
|
+
disabled: disabled || orbIsReviewBusy,
|
|
3358
|
+
children: orbIsReviewBusy ? /* @__PURE__ */ jsx21(Loader2, { className: "h-7 w-7 animate-spin" }) : orbIsListening ? /* @__PURE__ */ jsx21(Square, { className: "h-7 w-7" }) : isArmedDraft ? /* @__PURE__ */ jsx21(Mic, { className: "h-7 w-7 animate-pulse" }) : /* @__PURE__ */ jsx21(Mic, { className: "h-7 w-7" })
|
|
3359
|
+
}
|
|
3360
|
+
),
|
|
3361
|
+
/* @__PURE__ */ jsxs11("div", { className: "w-full max-w-sm space-y-2", children: [
|
|
3362
|
+
/* @__PURE__ */ jsx21(Progress, { value: levelValue, className: "h-2" }),
|
|
3363
|
+
/* @__PURE__ */ jsxs11("div", { className: "flex items-center justify-between text-xs text-muted-foreground", children: [
|
|
3364
|
+
/* @__PURE__ */ jsx21("span", { children: formatDuration(durationMs) }),
|
|
3365
|
+
/* @__PURE__ */ jsx21("span", { className: "max-w-[15rem] text-right", children: reviewHelperText })
|
|
3366
|
+
] })
|
|
3367
|
+
] })
|
|
3368
|
+
] }),
|
|
3326
3369
|
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
3370
|
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
3371
|
isAutoSendActive && autoSendDelayMs > 0 && /* @__PURE__ */ jsxs11("div", { className: "mt-3 space-y-2", children: [
|
|
@@ -3334,19 +3377,6 @@ var VoiceComposer = ({
|
|
|
3334
3377
|
/* @__PURE__ */ jsx21(X2, { className: "h-4 w-4" }),
|
|
3335
3378
|
labels?.voiceCancel || "Cancel"
|
|
3336
3379
|
] }),
|
|
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
3380
|
/* @__PURE__ */ jsxs11(Button, { type: "button", size: "sm", onClick: onSendNow, disabled, children: [
|
|
3351
3381
|
/* @__PURE__ */ jsx21(Send, { className: "h-4 w-4" }),
|
|
3352
3382
|
labels?.voiceSendNow || "Send now"
|
|
@@ -3636,6 +3666,7 @@ var ChatInput = memo2(function ChatInput2({
|
|
|
3636
3666
|
}) {
|
|
3637
3667
|
const voiceComposeEnabled = config?.voiceCompose?.enabled === true;
|
|
3638
3668
|
const voiceDefaultMode = config?.voiceCompose?.defaultMode ?? "text";
|
|
3669
|
+
const voiceReviewMode = config?.voiceCompose?.reviewMode ?? "manual";
|
|
3639
3670
|
const voiceAutoSendDelayMs = config?.voiceCompose?.autoSendDelayMs ?? 5e3;
|
|
3640
3671
|
const voicePersistComposer = config?.voiceCompose?.persistComposer ?? true;
|
|
3641
3672
|
const voiceShowTranscriptPreview = config?.voiceCompose?.showTranscriptPreview ?? true;
|
|
@@ -3870,13 +3901,30 @@ var ChatInput = memo2(function ChatInput2({
|
|
|
3870
3901
|
setIsVoiceAutoSendActive(false);
|
|
3871
3902
|
setVoiceError(null);
|
|
3872
3903
|
}, []);
|
|
3904
|
+
const armVoiceDraftForAppend = useCallback3((segment) => {
|
|
3905
|
+
voiceAppendBaseRef.current = segment;
|
|
3906
|
+
voiceAppendBaseDurationRef.current = segment ? resolveVoiceSegmentDuration(segment) : 0;
|
|
3907
|
+
}, []);
|
|
3908
|
+
const handleVoiceProviderStateChange = useCallback3((nextState) => {
|
|
3909
|
+
if (voiceReviewMode === "armed" && (nextState === "waiting_for_speech" || nextState === "listening")) {
|
|
3910
|
+
const currentDraft = voiceDraftRef.current;
|
|
3911
|
+
if (currentDraft) {
|
|
3912
|
+
armVoiceDraftForAppend(currentDraft);
|
|
3913
|
+
}
|
|
3914
|
+
}
|
|
3915
|
+
if (voiceReviewMode === "armed" && nextState === "listening" && voiceDraftRef.current) {
|
|
3916
|
+
setVoiceCountdownMs(voiceAutoSendDelayMs);
|
|
3917
|
+
setIsVoiceAutoSendActive(false);
|
|
3918
|
+
}
|
|
3919
|
+
setVoiceState(nextState);
|
|
3920
|
+
}, [armVoiceDraftForAppend, voiceAutoSendDelayMs, voiceReviewMode]);
|
|
3873
3921
|
const ensureVoiceProvider = useCallback3(async () => {
|
|
3874
3922
|
if (voiceProviderRef.current) {
|
|
3875
3923
|
return voiceProviderRef.current;
|
|
3876
3924
|
}
|
|
3877
3925
|
const createProvider = resolveVoiceProviderFactory(config?.voiceCompose?.createProvider);
|
|
3878
3926
|
const provider = await createProvider({
|
|
3879
|
-
onStateChange:
|
|
3927
|
+
onStateChange: handleVoiceProviderStateChange,
|
|
3880
3928
|
onAudioLevelChange: setVoiceAudioLevel,
|
|
3881
3929
|
onDurationChange: (durationMs) => {
|
|
3882
3930
|
setVoiceDurationMs(voiceAppendBaseDurationRef.current + durationMs);
|
|
@@ -3892,8 +3940,6 @@ var ChatInput = memo2(function ChatInput2({
|
|
|
3892
3940
|
const previousSegment = voiceAppendBaseRef.current;
|
|
3893
3941
|
try {
|
|
3894
3942
|
const nextSegment = previousSegment ? await appendVoiceSegments(previousSegment, segment) : segment;
|
|
3895
|
-
voiceAppendBaseRef.current = null;
|
|
3896
|
-
voiceAppendBaseDurationRef.current = 0;
|
|
3897
3943
|
voiceDraftRef.current = nextSegment;
|
|
3898
3944
|
setVoiceDraft(nextSegment);
|
|
3899
3945
|
setVoiceTranscript(nextSegment.transcript ?? clearVoiceTranscript());
|
|
@@ -3902,11 +3948,15 @@ var ChatInput = memo2(function ChatInput2({
|
|
|
3902
3948
|
setVoiceCountdownMs(voiceAutoSendDelayMs);
|
|
3903
3949
|
setIsVoiceAutoSendActive(voiceAutoSendDelayMs > 0);
|
|
3904
3950
|
setVoiceError(null);
|
|
3905
|
-
|
|
3951
|
+
if (voiceReviewMode === "armed") {
|
|
3952
|
+
armVoiceDraftForAppend(nextSegment);
|
|
3953
|
+
} else {
|
|
3954
|
+
armVoiceDraftForAppend(null);
|
|
3955
|
+
}
|
|
3956
|
+
setVoiceState((currentState) => voiceReviewMode === "armed" && (currentState === "waiting_for_speech" || currentState === "listening") ? currentState : "review");
|
|
3906
3957
|
} catch (error) {
|
|
3907
3958
|
const resolvedError = resolveVoiceErrorMessage(error, config);
|
|
3908
|
-
|
|
3909
|
-
voiceAppendBaseDurationRef.current = 0;
|
|
3959
|
+
armVoiceDraftForAppend(null);
|
|
3910
3960
|
setVoiceAudioLevel(0);
|
|
3911
3961
|
setVoiceCountdownMs(0);
|
|
3912
3962
|
setIsVoiceAutoSendActive(false);
|
|
@@ -3930,8 +3980,7 @@ var ChatInput = memo2(function ChatInput2({
|
|
|
3930
3980
|
},
|
|
3931
3981
|
onError: (error) => {
|
|
3932
3982
|
const previousSegment = voiceAppendBaseRef.current;
|
|
3933
|
-
|
|
3934
|
-
voiceAppendBaseDurationRef.current = 0;
|
|
3983
|
+
armVoiceDraftForAppend(null);
|
|
3935
3984
|
setVoiceError(resolveVoiceErrorMessage(error, config));
|
|
3936
3985
|
setVoiceAudioLevel(0);
|
|
3937
3986
|
setVoiceCountdownMs(0);
|
|
@@ -3955,7 +4004,7 @@ var ChatInput = memo2(function ChatInput2({
|
|
|
3955
4004
|
});
|
|
3956
4005
|
voiceProviderRef.current = provider;
|
|
3957
4006
|
return provider;
|
|
3958
|
-
}, [config, voiceAutoSendDelayMs, voiceMaxRecordingMs]);
|
|
4007
|
+
}, [armVoiceDraftForAppend, config, handleVoiceProviderStateChange, voiceAutoSendDelayMs, voiceMaxRecordingMs, voiceReviewMode]);
|
|
3959
4008
|
const closeVoiceComposer = useCallback3(async () => {
|
|
3960
4009
|
voiceAppendBaseRef.current = null;
|
|
3961
4010
|
voiceAppendBaseDurationRef.current = 0;
|
|
@@ -4047,16 +4096,21 @@ var ChatInput = memo2(function ChatInput2({
|
|
|
4047
4096
|
void closeVoiceComposer();
|
|
4048
4097
|
}, [voicePersistComposer, resetVoiceComposerState, closeVoiceComposer]);
|
|
4049
4098
|
const sendVoiceDraft = useCallback3(() => {
|
|
4050
|
-
|
|
4051
|
-
|
|
4052
|
-
|
|
4053
|
-
|
|
4054
|
-
|
|
4055
|
-
|
|
4056
|
-
|
|
4057
|
-
|
|
4058
|
-
|
|
4059
|
-
|
|
4099
|
+
void (async () => {
|
|
4100
|
+
if (!voiceDraft || disabled || isGenerating) {
|
|
4101
|
+
return;
|
|
4102
|
+
}
|
|
4103
|
+
setVoiceState("sending");
|
|
4104
|
+
setVoiceCountdownMs(0);
|
|
4105
|
+
setIsVoiceAutoSendActive(false);
|
|
4106
|
+
if (voiceProviderRef.current) {
|
|
4107
|
+
await voiceProviderRef.current.cancel();
|
|
4108
|
+
}
|
|
4109
|
+
onSubmit("", [...attachments, voiceDraft.attachment]);
|
|
4110
|
+
onChange("");
|
|
4111
|
+
onAttachmentsChange([]);
|
|
4112
|
+
finalizeVoiceComposerAfterSend();
|
|
4113
|
+
})();
|
|
4060
4114
|
}, [
|
|
4061
4115
|
voiceDraft,
|
|
4062
4116
|
disabled,
|
|
@@ -4068,25 +4122,51 @@ var ChatInput = memo2(function ChatInput2({
|
|
|
4068
4122
|
finalizeVoiceComposerAfterSend
|
|
4069
4123
|
]);
|
|
4070
4124
|
const cancelVoiceAutoSend = useCallback3(() => {
|
|
4125
|
+
void (async () => {
|
|
4126
|
+
if (voiceReviewMode === "armed" && voiceProviderRef.current) {
|
|
4127
|
+
await voiceProviderRef.current.cancel();
|
|
4128
|
+
}
|
|
4129
|
+
armVoiceDraftForAppend(null);
|
|
4130
|
+
setVoiceAudioLevel(0);
|
|
4131
|
+
setVoiceState("review");
|
|
4132
|
+
})();
|
|
4071
4133
|
setVoiceCountdownMs(0);
|
|
4072
4134
|
setIsVoiceAutoSendActive(false);
|
|
4073
|
-
}, []);
|
|
4135
|
+
}, [armVoiceDraftForAppend, voiceReviewMode]);
|
|
4136
|
+
const pauseVoiceReview = useCallback3(async () => {
|
|
4137
|
+
if (voiceState === "listening") {
|
|
4138
|
+
await stopVoiceCapture();
|
|
4139
|
+
return;
|
|
4140
|
+
}
|
|
4141
|
+
if (voiceReviewMode === "armed" && voiceProviderRef.current) {
|
|
4142
|
+
await voiceProviderRef.current.cancel();
|
|
4143
|
+
}
|
|
4144
|
+
armVoiceDraftForAppend(null);
|
|
4145
|
+
setVoiceAudioLevel(0);
|
|
4146
|
+
setVoiceState("review");
|
|
4147
|
+
}, [armVoiceDraftForAppend, stopVoiceCapture, voiceReviewMode, voiceState]);
|
|
4074
4148
|
useEffect9(() => {
|
|
4075
|
-
if (
|
|
4149
|
+
if (!voiceDraft || voiceAutoSendDelayMs <= 0 || !isVoiceAutoSendActive) {
|
|
4150
|
+
return;
|
|
4151
|
+
}
|
|
4152
|
+
const canContinueCounting = voiceState === "review" || voiceReviewMode === "armed" && voiceState === "waiting_for_speech";
|
|
4153
|
+
if (!canContinueCounting) {
|
|
4076
4154
|
return;
|
|
4077
4155
|
}
|
|
4078
|
-
const startedAt = Date.now();
|
|
4079
|
-
setVoiceCountdownMs(voiceAutoSendDelayMs);
|
|
4080
4156
|
const timer = setInterval(() => {
|
|
4081
|
-
|
|
4082
|
-
|
|
4083
|
-
|
|
4084
|
-
|
|
4085
|
-
|
|
4086
|
-
|
|
4157
|
+
setVoiceCountdownMs((previous) => {
|
|
4158
|
+
const remaining = Math.max(0, previous - 100);
|
|
4159
|
+
if (remaining <= 0) {
|
|
4160
|
+
clearInterval(timer);
|
|
4161
|
+
queueMicrotask(() => {
|
|
4162
|
+
sendVoiceDraft();
|
|
4163
|
+
});
|
|
4164
|
+
}
|
|
4165
|
+
return remaining;
|
|
4166
|
+
});
|
|
4087
4167
|
}, 100);
|
|
4088
4168
|
return () => clearInterval(timer);
|
|
4089
|
-
}, [voiceState, voiceDraft, voiceAutoSendDelayMs, isVoiceAutoSendActive, sendVoiceDraft]);
|
|
4169
|
+
}, [voiceState, voiceDraft, voiceReviewMode, voiceAutoSendDelayMs, isVoiceAutoSendActive, sendVoiceDraft]);
|
|
4090
4170
|
const removeAttachment = (index) => {
|
|
4091
4171
|
const newAttachments = attachments.filter((_, i) => i !== index);
|
|
4092
4172
|
onAttachmentsChange(newAttachments);
|
|
@@ -4141,6 +4221,7 @@ var ChatInput = memo2(function ChatInput2({
|
|
|
4141
4221
|
countdownMs: voiceCountdownMs,
|
|
4142
4222
|
autoSendDelayMs: voiceAutoSendDelayMs,
|
|
4143
4223
|
isAutoSendActive: isVoiceAutoSendActive,
|
|
4224
|
+
reviewMode: voiceReviewMode,
|
|
4144
4225
|
errorMessage: voiceError,
|
|
4145
4226
|
disabled: disabled || isGenerating,
|
|
4146
4227
|
labels: config?.labels,
|
|
@@ -4150,6 +4231,9 @@ var ChatInput = memo2(function ChatInput2({
|
|
|
4150
4231
|
onStop: () => {
|
|
4151
4232
|
void stopVoiceCapture();
|
|
4152
4233
|
},
|
|
4234
|
+
onPauseReview: () => {
|
|
4235
|
+
void pauseVoiceReview();
|
|
4236
|
+
},
|
|
4153
4237
|
onCancelAutoSend: () => {
|
|
4154
4238
|
cancelVoiceAutoSend();
|
|
4155
4239
|
},
|