@copilotz/chat-ui 0.1.33 → 0.1.34
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +231 -19
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +231 -19
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -101,7 +101,7 @@ var defaultChatConfig = {
|
|
|
101
101
|
voiceSendNow: "Send now",
|
|
102
102
|
voiceCancel: "Cancel",
|
|
103
103
|
voiceDiscard: "Delete recording",
|
|
104
|
-
voiceRecordAgain: "
|
|
104
|
+
voiceRecordAgain: "Continue recording",
|
|
105
105
|
voiceAutoSendIn: "Auto-sends in {{seconds}}s",
|
|
106
106
|
voiceTranscriptPending: "Transcript unavailable",
|
|
107
107
|
voicePermissionDenied: "Microphone access was denied.",
|
|
@@ -2866,6 +2866,121 @@ var blobToDataUrl = (blob) => new Promise((resolve, reject) => {
|
|
|
2866
2866
|
reader.onerror = () => reject(reader.error ?? new Error("Failed to read recorded audio"));
|
|
2867
2867
|
reader.readAsDataURL(blob);
|
|
2868
2868
|
});
|
|
2869
|
+
var joinTranscriptParts = (...parts) => {
|
|
2870
|
+
const value = parts.map((part) => part?.trim()).filter((part) => Boolean(part && part.length > 0)).join(" ").trim();
|
|
2871
|
+
return value.length > 0 ? value : void 0;
|
|
2872
|
+
};
|
|
2873
|
+
var getAudioContextCtor = () => globalThis.AudioContext || globalThis.webkitAudioContext;
|
|
2874
|
+
var getOfflineAudioContextCtor = () => globalThis.OfflineAudioContext || globalThis.webkitOfflineAudioContext;
|
|
2875
|
+
var attachmentToArrayBuffer = async (attachment) => {
|
|
2876
|
+
const response = await fetch(attachment.dataUrl);
|
|
2877
|
+
return response.arrayBuffer();
|
|
2878
|
+
};
|
|
2879
|
+
var decodeAudioAttachment = async (attachment) => {
|
|
2880
|
+
const AudioContextCtor = getAudioContextCtor();
|
|
2881
|
+
if (!AudioContextCtor) {
|
|
2882
|
+
throw new Error("Audio decoding is not supported in this browser");
|
|
2883
|
+
}
|
|
2884
|
+
const audioContext = new AudioContextCtor();
|
|
2885
|
+
try {
|
|
2886
|
+
const arrayBuffer = await attachmentToArrayBuffer(attachment);
|
|
2887
|
+
return await audioContext.decodeAudioData(arrayBuffer.slice(0));
|
|
2888
|
+
} finally {
|
|
2889
|
+
await closeAudioContext(audioContext);
|
|
2890
|
+
}
|
|
2891
|
+
};
|
|
2892
|
+
var renderMergedBuffer = async (buffers) => {
|
|
2893
|
+
const OfflineAudioContextCtor = getOfflineAudioContextCtor();
|
|
2894
|
+
if (!OfflineAudioContextCtor) {
|
|
2895
|
+
throw new Error("Offline audio rendering is not supported in this browser");
|
|
2896
|
+
}
|
|
2897
|
+
const numberOfChannels = Math.max(...buffers.map((buffer) => buffer.numberOfChannels));
|
|
2898
|
+
const sampleRate = Math.max(...buffers.map((buffer) => buffer.sampleRate));
|
|
2899
|
+
const totalFrames = Math.max(1, Math.ceil(buffers.reduce((sum, buffer) => sum + buffer.duration * sampleRate, 0)));
|
|
2900
|
+
const offlineContext = new OfflineAudioContextCtor(numberOfChannels, totalFrames, sampleRate);
|
|
2901
|
+
let offsetSeconds = 0;
|
|
2902
|
+
for (const buffer of buffers) {
|
|
2903
|
+
const source = offlineContext.createBufferSource();
|
|
2904
|
+
source.buffer = buffer;
|
|
2905
|
+
source.connect(offlineContext.destination);
|
|
2906
|
+
source.start(offsetSeconds);
|
|
2907
|
+
offsetSeconds += buffer.duration;
|
|
2908
|
+
}
|
|
2909
|
+
return offlineContext.startRendering();
|
|
2910
|
+
};
|
|
2911
|
+
var encodeWav = (audioBuffer) => {
|
|
2912
|
+
const numberOfChannels = audioBuffer.numberOfChannels;
|
|
2913
|
+
const sampleRate = audioBuffer.sampleRate;
|
|
2914
|
+
const bitsPerSample = 16;
|
|
2915
|
+
const bytesPerSample = bitsPerSample / 8;
|
|
2916
|
+
const dataLength = audioBuffer.length * numberOfChannels * bytesPerSample;
|
|
2917
|
+
const buffer = new ArrayBuffer(44 + dataLength);
|
|
2918
|
+
const view = new DataView(buffer);
|
|
2919
|
+
const writeString = (offset2, value) => {
|
|
2920
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
2921
|
+
view.setUint8(offset2 + index, value.charCodeAt(index));
|
|
2922
|
+
}
|
|
2923
|
+
};
|
|
2924
|
+
writeString(0, "RIFF");
|
|
2925
|
+
view.setUint32(4, 36 + dataLength, true);
|
|
2926
|
+
writeString(8, "WAVE");
|
|
2927
|
+
writeString(12, "fmt ");
|
|
2928
|
+
view.setUint32(16, 16, true);
|
|
2929
|
+
view.setUint16(20, 1, true);
|
|
2930
|
+
view.setUint16(22, numberOfChannels, true);
|
|
2931
|
+
view.setUint32(24, sampleRate, true);
|
|
2932
|
+
view.setUint32(28, sampleRate * numberOfChannels * bytesPerSample, true);
|
|
2933
|
+
view.setUint16(32, numberOfChannels * bytesPerSample, true);
|
|
2934
|
+
view.setUint16(34, bitsPerSample, true);
|
|
2935
|
+
writeString(36, "data");
|
|
2936
|
+
view.setUint32(40, dataLength, true);
|
|
2937
|
+
let offset = 44;
|
|
2938
|
+
const channelData = Array.from({ length: numberOfChannels }, (_, index) => audioBuffer.getChannelData(index));
|
|
2939
|
+
for (let sampleIndex = 0; sampleIndex < audioBuffer.length; sampleIndex += 1) {
|
|
2940
|
+
for (let channelIndex = 0; channelIndex < numberOfChannels; channelIndex += 1) {
|
|
2941
|
+
const sample = Math.max(-1, Math.min(1, channelData[channelIndex][sampleIndex]));
|
|
2942
|
+
const pcmValue = sample < 0 ? sample * 32768 : sample * 32767;
|
|
2943
|
+
view.setInt16(offset, pcmValue, true);
|
|
2944
|
+
offset += 2;
|
|
2945
|
+
}
|
|
2946
|
+
}
|
|
2947
|
+
return new Blob([buffer], { type: "audio/wav" });
|
|
2948
|
+
};
|
|
2949
|
+
var resolveSegmentCount = (segment) => {
|
|
2950
|
+
const candidate = segment?.metadata?.segmentCount;
|
|
2951
|
+
return typeof candidate === "number" && Number.isFinite(candidate) && candidate > 0 ? candidate : segment ? 1 : 0;
|
|
2952
|
+
};
|
|
2953
|
+
var mergeVoiceTranscripts = (previous, incoming) => ({
|
|
2954
|
+
final: joinTranscriptParts(previous?.final, incoming?.final),
|
|
2955
|
+
partial: joinTranscriptParts(previous?.final, incoming?.partial)
|
|
2956
|
+
});
|
|
2957
|
+
var appendVoiceSegments = async (previous, incoming) => {
|
|
2958
|
+
const [previousBuffer, incomingBuffer] = await Promise.all([
|
|
2959
|
+
decodeAudioAttachment(previous.attachment),
|
|
2960
|
+
decodeAudioAttachment(incoming.attachment)
|
|
2961
|
+
]);
|
|
2962
|
+
const mergedBuffer = await renderMergedBuffer([previousBuffer, incomingBuffer]);
|
|
2963
|
+
const mergedBlob = encodeWav(mergedBuffer);
|
|
2964
|
+
const dataUrl = await blobToDataUrl(mergedBlob);
|
|
2965
|
+
const segmentCount = resolveSegmentCount(previous) + resolveSegmentCount(incoming);
|
|
2966
|
+
return {
|
|
2967
|
+
attachment: {
|
|
2968
|
+
kind: "audio",
|
|
2969
|
+
dataUrl,
|
|
2970
|
+
mimeType: mergedBlob.type,
|
|
2971
|
+
durationMs: Math.round(mergedBuffer.duration * 1e3),
|
|
2972
|
+
fileName: `voice-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}.wav`,
|
|
2973
|
+
size: mergedBlob.size
|
|
2974
|
+
},
|
|
2975
|
+
transcript: mergeVoiceTranscripts(previous.transcript, incoming.transcript),
|
|
2976
|
+
metadata: {
|
|
2977
|
+
...previous.metadata,
|
|
2978
|
+
...incoming.metadata,
|
|
2979
|
+
segmentCount,
|
|
2980
|
+
source: segmentCount > 1 ? "merged" : incoming.metadata?.source ?? previous.metadata?.source
|
|
2981
|
+
}
|
|
2982
|
+
};
|
|
2983
|
+
};
|
|
2869
2984
|
var stopStream = (stream) => {
|
|
2870
2985
|
if (!stream) return;
|
|
2871
2986
|
stream.getTracks().forEach((track) => track.stop());
|
|
@@ -2987,7 +3102,7 @@ var createManualVoiceProvider = async (handlers, options = {}) => {
|
|
|
2987
3102
|
fileName: `voice-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}.webm`,
|
|
2988
3103
|
size: blob.size
|
|
2989
3104
|
},
|
|
2990
|
-
metadata: { source: "manual" }
|
|
3105
|
+
metadata: { source: "manual", segmentCount: 1 }
|
|
2991
3106
|
});
|
|
2992
3107
|
} else {
|
|
2993
3108
|
handlers.onStateChange?.("idle");
|
|
@@ -3254,7 +3369,7 @@ var VoiceComposer = ({
|
|
|
3254
3369
|
] })
|
|
3255
3370
|
] })
|
|
3256
3371
|
] }),
|
|
3257
|
-
|
|
3372
|
+
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 })
|
|
3258
3373
|
] });
|
|
3259
3374
|
};
|
|
3260
3375
|
|
|
@@ -3503,6 +3618,7 @@ var resolveVoiceErrorMessage = (error, config) => {
|
|
|
3503
3618
|
return config?.labels?.voiceCaptureError || "Unable to capture audio.";
|
|
3504
3619
|
};
|
|
3505
3620
|
var clearVoiceTranscript = () => ({});
|
|
3621
|
+
var resolveVoiceSegmentDuration = (segment) => segment.attachment.durationMs ?? 0;
|
|
3506
3622
|
var ChatInput = (0, import_react5.memo)(function ChatInput2({
|
|
3507
3623
|
value,
|
|
3508
3624
|
onChange,
|
|
@@ -3551,6 +3667,9 @@ var ChatInput = (0, import_react5.memo)(function ChatInput2({
|
|
|
3551
3667
|
const recordingInterval = (0, import_react5.useRef)(null);
|
|
3552
3668
|
const mediaStreamRef = (0, import_react5.useRef)(null);
|
|
3553
3669
|
const voiceProviderRef = (0, import_react5.useRef)(null);
|
|
3670
|
+
const voiceDraftRef = (0, import_react5.useRef)(null);
|
|
3671
|
+
const voiceAppendBaseRef = (0, import_react5.useRef)(null);
|
|
3672
|
+
const voiceAppendBaseDurationRef = (0, import_react5.useRef)(0);
|
|
3554
3673
|
(0, import_react5.useEffect)(() => {
|
|
3555
3674
|
return () => {
|
|
3556
3675
|
if (mediaStreamRef.current) {
|
|
@@ -3565,6 +3684,9 @@ var ChatInput = (0, import_react5.memo)(function ChatInput2({
|
|
|
3565
3684
|
}
|
|
3566
3685
|
};
|
|
3567
3686
|
}, []);
|
|
3687
|
+
(0, import_react5.useEffect)(() => {
|
|
3688
|
+
voiceDraftRef.current = voiceDraft;
|
|
3689
|
+
}, [voiceDraft]);
|
|
3568
3690
|
const handleSubmit = (e) => {
|
|
3569
3691
|
e.preventDefault();
|
|
3570
3692
|
if (!value.trim() && attachments.length === 0 || disabled || isGenerating) return;
|
|
@@ -3742,6 +3864,9 @@ var ChatInput = (0, import_react5.memo)(function ChatInput2({
|
|
|
3742
3864
|
const resetVoiceComposerState = (0, import_react5.useCallback)((nextState = "idle") => {
|
|
3743
3865
|
setVoiceState(nextState);
|
|
3744
3866
|
setVoiceDraft(null);
|
|
3867
|
+
voiceDraftRef.current = null;
|
|
3868
|
+
voiceAppendBaseRef.current = null;
|
|
3869
|
+
voiceAppendBaseDurationRef.current = 0;
|
|
3745
3870
|
setVoiceTranscript(clearVoiceTranscript());
|
|
3746
3871
|
setVoiceDurationMs(0);
|
|
3747
3872
|
setVoiceAudioLevel(0);
|
|
@@ -3757,23 +3882,76 @@ var ChatInput = (0, import_react5.memo)(function ChatInput2({
|
|
|
3757
3882
|
const provider = await createProvider({
|
|
3758
3883
|
onStateChange: setVoiceState,
|
|
3759
3884
|
onAudioLevelChange: setVoiceAudioLevel,
|
|
3760
|
-
onDurationChange:
|
|
3761
|
-
|
|
3885
|
+
onDurationChange: (durationMs) => {
|
|
3886
|
+
setVoiceDurationMs(voiceAppendBaseDurationRef.current + durationMs);
|
|
3887
|
+
},
|
|
3888
|
+
onTranscriptChange: (transcript) => {
|
|
3889
|
+
const baseTranscript = voiceAppendBaseRef.current?.transcript;
|
|
3890
|
+
setVoiceTranscript(
|
|
3891
|
+
baseTranscript ? mergeVoiceTranscripts(baseTranscript, transcript) : transcript
|
|
3892
|
+
);
|
|
3893
|
+
},
|
|
3762
3894
|
onSegmentReady: (segment) => {
|
|
3763
|
-
|
|
3764
|
-
|
|
3765
|
-
|
|
3766
|
-
|
|
3767
|
-
|
|
3768
|
-
|
|
3769
|
-
|
|
3770
|
-
|
|
3895
|
+
void (async () => {
|
|
3896
|
+
const previousSegment = voiceAppendBaseRef.current;
|
|
3897
|
+
try {
|
|
3898
|
+
const nextSegment = previousSegment ? await appendVoiceSegments(previousSegment, segment) : segment;
|
|
3899
|
+
voiceAppendBaseRef.current = null;
|
|
3900
|
+
voiceAppendBaseDurationRef.current = 0;
|
|
3901
|
+
voiceDraftRef.current = nextSegment;
|
|
3902
|
+
setVoiceDraft(nextSegment);
|
|
3903
|
+
setVoiceTranscript(nextSegment.transcript ?? clearVoiceTranscript());
|
|
3904
|
+
setVoiceDurationMs(resolveVoiceSegmentDuration(nextSegment));
|
|
3905
|
+
setVoiceAudioLevel(0);
|
|
3906
|
+
setVoiceCountdownMs(voiceAutoSendDelayMs);
|
|
3907
|
+
setIsVoiceAutoSendActive(voiceAutoSendDelayMs > 0);
|
|
3908
|
+
setVoiceError(null);
|
|
3909
|
+
setVoiceState("review");
|
|
3910
|
+
} catch (error) {
|
|
3911
|
+
const resolvedError = resolveVoiceErrorMessage(error, config);
|
|
3912
|
+
voiceAppendBaseRef.current = null;
|
|
3913
|
+
voiceAppendBaseDurationRef.current = 0;
|
|
3914
|
+
setVoiceAudioLevel(0);
|
|
3915
|
+
setVoiceCountdownMs(0);
|
|
3916
|
+
setIsVoiceAutoSendActive(false);
|
|
3917
|
+
if (previousSegment) {
|
|
3918
|
+
voiceDraftRef.current = previousSegment;
|
|
3919
|
+
setVoiceDraft(previousSegment);
|
|
3920
|
+
setVoiceTranscript(previousSegment.transcript ?? clearVoiceTranscript());
|
|
3921
|
+
setVoiceDurationMs(resolveVoiceSegmentDuration(previousSegment));
|
|
3922
|
+
setVoiceError(resolvedError);
|
|
3923
|
+
setVoiceState("review");
|
|
3924
|
+
return;
|
|
3925
|
+
}
|
|
3926
|
+
voiceDraftRef.current = null;
|
|
3927
|
+
setVoiceDraft(null);
|
|
3928
|
+
setVoiceTranscript(clearVoiceTranscript());
|
|
3929
|
+
setVoiceDurationMs(0);
|
|
3930
|
+
setVoiceError(resolvedError);
|
|
3931
|
+
setVoiceState("error");
|
|
3932
|
+
}
|
|
3933
|
+
})();
|
|
3771
3934
|
},
|
|
3772
3935
|
onError: (error) => {
|
|
3936
|
+
const previousSegment = voiceAppendBaseRef.current;
|
|
3937
|
+
voiceAppendBaseRef.current = null;
|
|
3938
|
+
voiceAppendBaseDurationRef.current = 0;
|
|
3773
3939
|
setVoiceError(resolveVoiceErrorMessage(error, config));
|
|
3774
3940
|
setVoiceAudioLevel(0);
|
|
3775
3941
|
setVoiceCountdownMs(0);
|
|
3776
3942
|
setIsVoiceAutoSendActive(false);
|
|
3943
|
+
if (previousSegment) {
|
|
3944
|
+
voiceDraftRef.current = previousSegment;
|
|
3945
|
+
setVoiceDraft(previousSegment);
|
|
3946
|
+
setVoiceTranscript(previousSegment.transcript ?? clearVoiceTranscript());
|
|
3947
|
+
setVoiceDurationMs(resolveVoiceSegmentDuration(previousSegment));
|
|
3948
|
+
setVoiceState("review");
|
|
3949
|
+
return;
|
|
3950
|
+
}
|
|
3951
|
+
voiceDraftRef.current = null;
|
|
3952
|
+
setVoiceDraft(null);
|
|
3953
|
+
setVoiceTranscript(clearVoiceTranscript());
|
|
3954
|
+
setVoiceDurationMs(0);
|
|
3777
3955
|
setVoiceState("error");
|
|
3778
3956
|
}
|
|
3779
3957
|
}, {
|
|
@@ -3783,35 +3961,67 @@ var ChatInput = (0, import_react5.memo)(function ChatInput2({
|
|
|
3783
3961
|
return provider;
|
|
3784
3962
|
}, [config, voiceAutoSendDelayMs, voiceMaxRecordingMs]);
|
|
3785
3963
|
const closeVoiceComposer = (0, import_react5.useCallback)(async () => {
|
|
3964
|
+
voiceAppendBaseRef.current = null;
|
|
3965
|
+
voiceAppendBaseDurationRef.current = 0;
|
|
3786
3966
|
setIsVoiceComposerOpen(false);
|
|
3787
3967
|
setVoiceError(null);
|
|
3788
3968
|
setVoiceCountdownMs(0);
|
|
3789
3969
|
setVoiceAudioLevel(0);
|
|
3790
3970
|
setVoiceTranscript(clearVoiceTranscript());
|
|
3791
3971
|
setVoiceDraft(null);
|
|
3972
|
+
voiceDraftRef.current = null;
|
|
3792
3973
|
setVoiceDurationMs(0);
|
|
3793
3974
|
setVoiceState("idle");
|
|
3794
3975
|
if (voiceProviderRef.current) {
|
|
3795
3976
|
await voiceProviderRef.current.cancel();
|
|
3796
3977
|
}
|
|
3797
3978
|
}, []);
|
|
3798
|
-
const startVoiceCapture = (0, import_react5.useCallback)(async () => {
|
|
3979
|
+
const startVoiceCapture = (0, import_react5.useCallback)(async (appendToDraft = false) => {
|
|
3799
3980
|
if (disabled || isGenerating) {
|
|
3800
3981
|
return;
|
|
3801
3982
|
}
|
|
3983
|
+
const previousDraft = appendToDraft ? voiceDraftRef.current : null;
|
|
3984
|
+
const previousDurationMs = previousDraft ? resolveVoiceSegmentDuration(previousDraft) : 0;
|
|
3802
3985
|
setIsVoiceComposerOpen(true);
|
|
3803
3986
|
setVoiceError(null);
|
|
3804
|
-
setVoiceDraft(null);
|
|
3805
3987
|
setVoiceCountdownMs(0);
|
|
3806
|
-
setVoiceTranscript(clearVoiceTranscript());
|
|
3807
3988
|
setVoiceAudioLevel(0);
|
|
3808
|
-
setVoiceDurationMs(0);
|
|
3809
3989
|
setIsVoiceAutoSendActive(false);
|
|
3990
|
+
voiceAppendBaseRef.current = previousDraft;
|
|
3991
|
+
voiceAppendBaseDurationRef.current = previousDurationMs;
|
|
3992
|
+
if (!previousDraft) {
|
|
3993
|
+
setVoiceDraft(null);
|
|
3994
|
+
voiceDraftRef.current = null;
|
|
3995
|
+
setVoiceTranscript(clearVoiceTranscript());
|
|
3996
|
+
setVoiceDurationMs(0);
|
|
3997
|
+
} else {
|
|
3998
|
+
setVoiceTranscript(previousDraft.transcript ?? clearVoiceTranscript());
|
|
3999
|
+
setVoiceDurationMs(previousDurationMs);
|
|
4000
|
+
}
|
|
3810
4001
|
try {
|
|
3811
4002
|
const provider = await ensureVoiceProvider();
|
|
3812
4003
|
await provider.start();
|
|
3813
4004
|
} catch (error) {
|
|
3814
|
-
|
|
4005
|
+
const resolvedError = resolveVoiceErrorMessage(error, config);
|
|
4006
|
+
voiceAppendBaseRef.current = null;
|
|
4007
|
+
voiceAppendBaseDurationRef.current = 0;
|
|
4008
|
+
setVoiceAudioLevel(0);
|
|
4009
|
+
setVoiceCountdownMs(0);
|
|
4010
|
+
setIsVoiceAutoSendActive(false);
|
|
4011
|
+
if (previousDraft) {
|
|
4012
|
+
voiceDraftRef.current = previousDraft;
|
|
4013
|
+
setVoiceDraft(previousDraft);
|
|
4014
|
+
setVoiceTranscript(previousDraft.transcript ?? clearVoiceTranscript());
|
|
4015
|
+
setVoiceDurationMs(previousDurationMs);
|
|
4016
|
+
setVoiceError(resolvedError);
|
|
4017
|
+
setVoiceState("review");
|
|
4018
|
+
return;
|
|
4019
|
+
}
|
|
4020
|
+
voiceDraftRef.current = null;
|
|
4021
|
+
setVoiceDraft(null);
|
|
4022
|
+
setVoiceTranscript(clearVoiceTranscript());
|
|
4023
|
+
setVoiceDurationMs(0);
|
|
4024
|
+
setVoiceError(resolvedError);
|
|
3815
4025
|
setVoiceState("error");
|
|
3816
4026
|
}
|
|
3817
4027
|
}, [disabled, isGenerating, ensureVoiceProvider, config]);
|
|
@@ -3825,6 +4035,8 @@ var ChatInput = (0, import_react5.memo)(function ChatInput2({
|
|
|
3825
4035
|
}
|
|
3826
4036
|
}, [config]);
|
|
3827
4037
|
const cancelVoiceCapture = (0, import_react5.useCallback)(async () => {
|
|
4038
|
+
voiceAppendBaseRef.current = null;
|
|
4039
|
+
voiceAppendBaseDurationRef.current = 0;
|
|
3828
4040
|
if (voiceProviderRef.current) {
|
|
3829
4041
|
await voiceProviderRef.current.cancel();
|
|
3830
4042
|
}
|
|
@@ -3949,7 +4161,7 @@ var ChatInput = (0, import_react5.memo)(function ChatInput2({
|
|
|
3949
4161
|
void cancelVoiceCapture();
|
|
3950
4162
|
},
|
|
3951
4163
|
onRecordAgain: () => {
|
|
3952
|
-
void startVoiceCapture();
|
|
4164
|
+
void startVoiceCapture(true);
|
|
3953
4165
|
},
|
|
3954
4166
|
onSendNow: sendVoiceDraft,
|
|
3955
4167
|
onExit: () => {
|