@copilotkit/react-ui 1.1.3-feat-runtime-remote-actions.2 → 1.2.0
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/CHANGELOG.md +7 -24
- package/dist/components/chat/Button.d.mts +7 -0
- package/dist/components/chat/Button.js.map +1 -1
- package/dist/components/chat/Button.mjs +30 -6
- package/dist/components/chat/Button.mjs.map +1 -1
- package/dist/components/chat/Chat.d.mts +87 -0
- package/dist/components/chat/Chat.d.ts +2 -2
- package/dist/components/chat/Chat.js +8 -107
- package/dist/components/chat/Chat.js.map +1 -1
- package/dist/components/chat/Chat.mjs +1754 -20
- package/dist/components/chat/Chat.mjs.map +1 -1
- package/dist/components/chat/ChatContext.d.mts +105 -0
- package/dist/components/chat/ChatContext.d.ts +1 -1
- package/dist/components/chat/ChatContext.mjs +228 -7
- package/dist/components/chat/ChatContext.mjs.map +1 -1
- package/dist/components/chat/CodeBlock.d.mts +14 -0
- package/dist/components/chat/CodeBlock.js +1 -2
- package/dist/components/chat/CodeBlock.js.map +1 -1
- package/dist/components/chat/CodeBlock.mjs +482 -8
- package/dist/components/chat/CodeBlock.mjs.map +1 -1
- package/dist/components/chat/Header.d.mts +7 -0
- package/dist/components/chat/Header.mjs +23 -6
- package/dist/components/chat/Header.mjs.map +1 -1
- package/dist/components/chat/Icons.d.mts +18 -0
- package/dist/components/chat/Icons.mjs +271 -16
- package/dist/components/chat/Icons.mjs.map +1 -1
- package/dist/components/chat/Input.d.mts +7 -0
- package/dist/components/chat/Input.js +2 -4
- package/dist/components/chat/Input.js.map +1 -1
- package/dist/components/chat/Input.mjs +255 -8
- package/dist/components/chat/Input.mjs.map +1 -1
- package/dist/components/chat/Markdown.d.mts +8 -0
- package/dist/components/chat/Markdown.js +1 -2
- package/dist/components/chat/Markdown.js.map +1 -1
- package/dist/components/chat/Markdown.mjs +546 -7
- package/dist/components/chat/Markdown.mjs.map +1 -1
- package/dist/components/chat/Messages.d.mts +7 -0
- package/dist/components/chat/Messages.js +3 -48
- package/dist/components/chat/Messages.js.map +1 -1
- package/dist/components/chat/Messages.mjs +678 -8
- package/dist/components/chat/Messages.mjs.map +1 -1
- package/dist/components/chat/Modal.d.mts +51 -0
- package/dist/components/chat/Modal.d.ts +1 -1
- package/dist/components/chat/Modal.js +9 -107
- package/dist/components/chat/Modal.js.map +1 -1
- package/dist/components/chat/Modal.mjs +1956 -22
- package/dist/components/chat/Modal.mjs.map +1 -1
- package/dist/components/chat/Popup.d.mts +13 -0
- package/dist/components/chat/Popup.js +9 -107
- package/dist/components/chat/Popup.js.map +1 -1
- package/dist/components/chat/Popup.mjs +1965 -23
- package/dist/components/chat/Popup.mjs.map +1 -1
- package/dist/components/chat/Response.d.mts +7 -0
- package/dist/components/chat/Response.mjs +23 -6
- package/dist/components/chat/Response.mjs.map +1 -1
- package/dist/components/chat/Sidebar.d.mts +13 -0
- package/dist/components/chat/Sidebar.js +10 -107
- package/dist/components/chat/Sidebar.js.map +1 -1
- package/dist/components/chat/Sidebar.mjs +1977 -23
- package/dist/components/chat/Sidebar.mjs.map +1 -1
- package/dist/components/chat/Suggestion.d.mts +14 -0
- package/dist/components/chat/Suggestion.js.map +1 -1
- package/dist/components/chat/Suggestion.mjs +152 -5
- package/dist/components/chat/Suggestion.mjs.map +1 -1
- package/dist/components/chat/Textarea.d.mts +13 -0
- package/dist/components/chat/Textarea.js.map +1 -1
- package/dist/components/chat/Textarea.mjs +48 -4
- package/dist/components/chat/Textarea.mjs.map +1 -1
- package/dist/components/chat/Window.d.mts +7 -0
- package/dist/components/chat/Window.js.map +1 -1
- package/dist/components/chat/Window.mjs +125 -6
- package/dist/components/chat/Window.mjs.map +1 -1
- package/dist/components/chat/index.d.mts +11 -0
- package/dist/components/chat/index.js +10 -107
- package/dist/components/chat/index.js.map +1 -1
- package/dist/components/chat/index.mjs +1983 -31
- package/dist/components/chat/index.mjs.map +1 -1
- package/dist/components/chat/props.d.mts +35 -0
- package/dist/components/chat/props.d.ts +1 -1
- package/dist/components/chat/props.mjs +0 -1
- package/dist/components/dev-console/console.d.mts +10 -0
- package/dist/components/dev-console/console.js +0 -52
- package/dist/components/dev-console/console.js.map +1 -1
- package/dist/components/dev-console/console.mjs +426 -8
- package/dist/components/dev-console/console.mjs.map +1 -1
- package/dist/components/dev-console/icons.d.mts +9 -0
- package/dist/components/dev-console/icons.mjs +83 -8
- package/dist/components/dev-console/icons.mjs.map +1 -1
- package/dist/components/dev-console/index.d.mts +5 -0
- package/dist/components/dev-console/index.js +0 -52
- package/dist/components/dev-console/index.js.map +1 -1
- package/dist/components/dev-console/index.mjs +424 -10
- package/dist/components/dev-console/index.mjs.map +1 -1
- package/dist/components/dev-console/types.d.mts +9 -0
- package/dist/components/dev-console/types.d.ts +1 -1
- package/dist/components/dev-console/utils.d.mts +9 -0
- package/dist/components/dev-console/utils.d.ts +1 -2
- package/dist/components/dev-console/utils.js +0 -53
- package/dist/components/dev-console/utils.js.map +1 -1
- package/dist/components/dev-console/utils.mjs +103 -9
- package/dist/components/dev-console/utils.mjs.map +1 -1
- package/dist/components/index.d.mts +11 -0
- package/dist/components/index.js +10 -107
- package/dist/components/index.js.map +1 -1
- package/dist/components/index.mjs +1983 -32
- package/dist/components/index.mjs.map +1 -1
- package/dist/context/index.d.mts +2 -0
- package/dist/context/index.d.ts +1 -1
- package/dist/context/index.mjs +0 -1
- package/dist/hooks/index.d.mts +1 -0
- package/dist/hooks/index.mjs +24 -5
- package/dist/hooks/index.mjs.map +1 -1
- package/dist/hooks/use-copilot-chat-suggestions.d.mts +83 -0
- package/dist/hooks/use-copilot-chat-suggestions.mjs +24 -4
- package/dist/hooks/use-copilot-chat-suggestions.mjs.map +1 -1
- package/dist/hooks/use-copy-to-clipboard.d.mts +9 -0
- package/dist/hooks/use-copy-to-clipboard.d.ts +1 -1
- package/dist/hooks/use-copy-to-clipboard.mjs +21 -4
- package/dist/hooks/use-copy-to-clipboard.mjs.map +1 -1
- package/dist/hooks/use-push-to-talk.d.mts +19 -0
- package/dist/hooks/use-push-to-talk.d.ts +1 -1
- package/dist/hooks/use-push-to-talk.js.map +1 -1
- package/dist/hooks/use-push-to-talk.mjs +148 -6
- package/dist/hooks/use-push-to-talk.mjs.map +1 -1
- package/dist/index.css +1 -747
- package/dist/index.d.mts +13 -0
- package/dist/index.js +17 -107
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2015 -38
- package/dist/index.mjs.map +1 -1
- package/dist/lib/utils.d.mts +4 -0
- package/dist/lib/utils.mjs +20 -3
- package/dist/lib/utils.mjs.map +1 -1
- package/dist/lib/utils.test.d.mts +2 -0
- package/dist/lib/utils.test.d.ts +1 -1
- package/dist/types/css.d.mts +16 -0
- package/dist/types/css.d.ts +1 -1
- package/dist/types/index.d.mts +2 -0
- package/dist/types/index.mjs +0 -1
- package/dist/types/suggestions.d.mts +8 -0
- package/dist/types/suggestions.d.ts +1 -1
- package/package.json +19 -11
- package/src/components/chat/Chat.tsx +2 -5
- package/src/components/chat/Messages.tsx +2 -83
- package/src/components/chat/Modal.tsx +1 -0
- package/src/components/chat/Popup.tsx +1 -4
- package/src/components/chat/Sidebar.tsx +2 -3
- package/src/components/chat/Suggestion.tsx +0 -2
- package/src/components/dev-console/console.tsx +0 -6
- package/src/components/dev-console/utils.ts +0 -56
- package/tsup.config.ts +59 -15
- package/dist/chunk-54JAUBUJ.mjs +0 -26
- package/dist/chunk-54JAUBUJ.mjs.map +0 -1
- package/dist/chunk-5HHVL5WK.mjs +0 -29
- package/dist/chunk-5HHVL5WK.mjs.map +0 -1
- package/dist/chunk-B2KQEX2R.mjs +0 -91
- package/dist/chunk-B2KQEX2R.mjs.map +0 -1
- package/dist/chunk-BJPGMY3I.mjs +0 -70
- package/dist/chunk-BJPGMY3I.mjs.map +0 -1
- package/dist/chunk-EFZPSZWO.mjs +0 -1
- package/dist/chunk-EFZPSZWO.mjs.map +0 -1
- package/dist/chunk-FL4ETWFB.mjs +0 -21
- package/dist/chunk-FL4ETWFB.mjs.map +0 -1
- package/dist/chunk-FLV3J3VX.mjs +0 -18
- package/dist/chunk-FLV3J3VX.mjs.map +0 -1
- package/dist/chunk-FO7Z5LAL.mjs +0 -118
- package/dist/chunk-FO7Z5LAL.mjs.map +0 -1
- package/dist/chunk-FOZVHL73.mjs +0 -171
- package/dist/chunk-FOZVHL73.mjs.map +0 -1
- package/dist/chunk-FZC7X5PK.mjs +0 -262
- package/dist/chunk-FZC7X5PK.mjs.map +0 -1
- package/dist/chunk-H7TM5JE6.mjs +0 -146
- package/dist/chunk-H7TM5JE6.mjs.map +0 -1
- package/dist/chunk-HR36Y2FQ.mjs +0 -167
- package/dist/chunk-HR36Y2FQ.mjs.map +0 -1
- package/dist/chunk-I4QG2ZZU.mjs +0 -220
- package/dist/chunk-I4QG2ZZU.mjs.map +0 -1
- package/dist/chunk-IU3WTXLQ.mjs +0 -1
- package/dist/chunk-IU3WTXLQ.mjs.map +0 -1
- package/dist/chunk-JD7BAH7U.mjs +0 -1
- package/dist/chunk-JD7BAH7U.mjs.map +0 -1
- package/dist/chunk-MRFF7GSQ.mjs +0 -1
- package/dist/chunk-MRFF7GSQ.mjs.map +0 -1
- package/dist/chunk-MRXNTQOX.mjs +0 -59
- package/dist/chunk-MRXNTQOX.mjs.map +0 -1
- package/dist/chunk-OTPAZXVR.mjs +0 -92
- package/dist/chunk-OTPAZXVR.mjs.map +0 -1
- package/dist/chunk-QOEAEMUQ.mjs +0 -30
- package/dist/chunk-QOEAEMUQ.mjs.map +0 -1
- package/dist/chunk-T26KLXLH.mjs +0 -1
- package/dist/chunk-T26KLXLH.mjs.map +0 -1
- package/dist/chunk-U6J5DGOE.mjs +0 -83
- package/dist/chunk-U6J5DGOE.mjs.map +0 -1
- package/dist/chunk-UPTB2MVO.mjs +0 -395
- package/dist/chunk-UPTB2MVO.mjs.map +0 -1
- package/dist/chunk-V7W6IM2V.mjs +0 -1
- package/dist/chunk-V7W6IM2V.mjs.map +0 -1
- package/dist/chunk-VOBX4JOA.mjs +0 -138
- package/dist/chunk-VOBX4JOA.mjs.map +0 -1
- package/dist/chunk-WB3YULQ4.mjs +0 -1
- package/dist/chunk-WB3YULQ4.mjs.map +0 -1
- package/dist/chunk-WCPLXRZX.mjs +0 -106
- package/dist/chunk-WCPLXRZX.mjs.map +0 -1
- package/dist/chunk-XSUSSWDS.mjs +0 -18
- package/dist/chunk-XSUSSWDS.mjs.map +0 -1
- package/dist/chunk-YOH25I6N.mjs +0 -25
- package/dist/chunk-YOH25I6N.mjs.map +0 -1
- package/dist/chunk-YQ3D5IQV.mjs +0 -75
- package/dist/chunk-YQ3D5IQV.mjs.map +0 -1
- package/dist/chunk-YQFVRDNC.mjs +0 -53
- package/dist/chunk-YQFVRDNC.mjs.map +0 -1
- package/dist/index.css.map +0 -1
|
@@ -1,7 +1,24 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
}
|
|
4
|
-
|
|
1
|
+
// src/hooks/use-copy-to-clipboard.tsx
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
function useCopyToClipboard({ timeout = 2e3 }) {
|
|
4
|
+
const [isCopied, setIsCopied] = React.useState(false);
|
|
5
|
+
const copyToClipboard = (value) => {
|
|
6
|
+
var _a;
|
|
7
|
+
if (typeof window === "undefined" || !((_a = navigator.clipboard) == null ? void 0 : _a.writeText)) {
|
|
8
|
+
return;
|
|
9
|
+
}
|
|
10
|
+
if (!value) {
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
navigator.clipboard.writeText(value).then(() => {
|
|
14
|
+
setIsCopied(true);
|
|
15
|
+
setTimeout(() => {
|
|
16
|
+
setIsCopied(false);
|
|
17
|
+
}, timeout);
|
|
18
|
+
});
|
|
19
|
+
};
|
|
20
|
+
return { isCopied, copyToClipboard };
|
|
21
|
+
}
|
|
5
22
|
export {
|
|
6
23
|
useCopyToClipboard
|
|
7
24
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/hooks/use-copy-to-clipboard.tsx"],"sourcesContent":["import * as React from \"react\";\n\nexport interface useCopyToClipboardProps {\n timeout?: number;\n}\n\nexport function useCopyToClipboard({ timeout = 2000 }: useCopyToClipboardProps) {\n const [isCopied, setIsCopied] = React.useState<Boolean>(false);\n\n const copyToClipboard = (value: string) => {\n if (typeof window === \"undefined\" || !navigator.clipboard?.writeText) {\n return;\n }\n\n if (!value) {\n return;\n }\n\n navigator.clipboard.writeText(value).then(() => {\n setIsCopied(true);\n\n setTimeout(() => {\n setIsCopied(false);\n }, timeout);\n });\n };\n\n return { isCopied, copyToClipboard };\n}\n"],"mappings":";AAAA,YAAY,WAAW;AAMhB,SAAS,mBAAmB,EAAE,UAAU,IAAK,GAA4B;AAC9E,QAAM,CAAC,UAAU,WAAW,IAAU,eAAkB,KAAK;AAE7D,QAAM,kBAAkB,CAAC,UAAkB;AAT7C;AAUI,QAAI,OAAO,WAAW,eAAe,GAAC,eAAU,cAAV,mBAAqB,YAAW;AACpE;AAAA,IACF;AAEA,QAAI,CAAC,OAAO;AACV;AAAA,IACF;AAEA,cAAU,UAAU,UAAU,KAAK,EAAE,KAAK,MAAM;AAC9C,kBAAY,IAAI;AAEhB,iBAAW,MAAM;AACf,oBAAY,KAAK;AAAA,MACnB,GAAG,OAAO;AAAA,IACZ,CAAC;AAAA,EACH;AAEA,SAAO,EAAE,UAAU,gBAAgB;AACrC;","names":[]}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { Message } from '@copilotkit/runtime-client-gql';
|
|
3
|
+
|
|
4
|
+
declare const checkMicrophonePermission: () => Promise<boolean | undefined>;
|
|
5
|
+
declare const requestMicAndPlaybackPermission: () => Promise<{
|
|
6
|
+
stream: MediaStream;
|
|
7
|
+
audioContext: AudioContext;
|
|
8
|
+
} | null>;
|
|
9
|
+
type PushToTalkState = "idle" | "recording" | "transcribing";
|
|
10
|
+
type SendFunction = (text: string) => Promise<Message>;
|
|
11
|
+
declare const usePushToTalk: ({ sendFunction, inProgress, }: {
|
|
12
|
+
sendFunction: SendFunction;
|
|
13
|
+
inProgress: boolean;
|
|
14
|
+
}) => {
|
|
15
|
+
pushToTalkState: PushToTalkState;
|
|
16
|
+
setPushToTalkState: React.Dispatch<React.SetStateAction<PushToTalkState>>;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export { type PushToTalkState, type SendFunction, checkMicrophonePermission, requestMicAndPlaybackPermission, usePushToTalk };
|
|
@@ -16,4 +16,4 @@ declare const usePushToTalk: ({ sendFunction, inProgress, }: {
|
|
|
16
16
|
setPushToTalkState: React.Dispatch<React.SetStateAction<PushToTalkState>>;
|
|
17
17
|
};
|
|
18
18
|
|
|
19
|
-
export { PushToTalkState, SendFunction, checkMicrophonePermission, requestMicAndPlaybackPermission, usePushToTalk };
|
|
19
|
+
export { type PushToTalkState, type SendFunction, checkMicrophonePermission, requestMicAndPlaybackPermission, usePushToTalk };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/hooks/use-push-to-talk.tsx"],"sourcesContent":["import { useCopilotContext } from \"@copilotkit/react-core\";\nimport { Message, TextMessage } from \"@copilotkit/runtime-client-gql\";\nimport { MutableRefObject, useEffect, useRef, useState } from \"react\";\n\nexport const checkMicrophonePermission = async () => {\n try {\n const permissionStatus = await navigator.permissions.query({\n name: \"microphone\" as PermissionName,\n });\n if (permissionStatus.state === \"granted\") {\n return true;\n } else {\n return false;\n }\n } catch (err) {\n console.error(\"Error checking microphone permission\", err);\n }\n};\n\nexport const requestMicAndPlaybackPermission = async () => {\n try {\n const stream = await navigator.mediaDevices.getUserMedia({ audio: true });\n const audioContext = new window.AudioContext();\n await audioContext.resume();\n return { stream, audioContext };\n } catch (err) {\n console.error(\"Error requesting microphone and playback permissions\", err);\n return null;\n }\n};\n\nconst startRecording = async (\n mediaStreamRef: MutableRefObject<MediaStream | null>,\n mediaRecorderRef: MutableRefObject<MediaRecorder | null>,\n audioContextRef: MutableRefObject<AudioContext | null>,\n recordedChunks: Blob[],\n onStop: () => void,\n) => {\n if (!mediaStreamRef.current || !audioContextRef.current) {\n mediaStreamRef.current = await navigator.mediaDevices.getUserMedia({ audio: true });\n audioContextRef.current = new window.AudioContext();\n await audioContextRef.current.resume();\n }\n\n mediaRecorderRef.current = new MediaRecorder(mediaStreamRef.current!);\n mediaRecorderRef.current.start(1000);\n mediaRecorderRef.current.ondataavailable = (event) => {\n recordedChunks.push(event.data);\n };\n mediaRecorderRef.current.onstop = onStop;\n};\n\nconst stopRecording = (mediaRecorderRef: MutableRefObject<MediaRecorder | null>) => {\n if (mediaRecorderRef.current && mediaRecorderRef.current.state !== \"inactive\") {\n mediaRecorderRef.current.stop();\n }\n};\n\nconst transcribeAudio = async (recordedChunks: Blob[], transcribeAudioUrl: string) => {\n const completeBlob = new Blob(recordedChunks, { type: \"audio/mp4\" });\n const formData = new FormData();\n formData.append(\"file\", completeBlob, \"recording.mp4\");\n\n const response = await fetch(transcribeAudioUrl, {\n method: \"POST\",\n body: formData,\n });\n\n if (!response.ok) {\n throw new Error(`Error: ${response.statusText}`);\n }\n\n const transcription = await response.json();\n return transcription.text;\n};\n\nconst playAudioResponse = (text: string, textToSpeechUrl: string, audioContext: AudioContext) => {\n const encodedText = encodeURIComponent(text);\n const url = `${textToSpeechUrl}?text=${encodedText}`;\n\n fetch(url)\n .then((response) => response.arrayBuffer())\n .then((arrayBuffer) => audioContext.decodeAudioData(arrayBuffer))\n .then((audioBuffer) => {\n const source = audioContext.createBufferSource();\n source.buffer = audioBuffer;\n source.connect(audioContext.destination);\n source.start(0);\n })\n .catch((error) => {\n console.error(\"Error with decoding audio data\", error);\n });\n};\n\nexport type PushToTalkState = \"idle\" | \"recording\" | \"transcribing\";\n\nexport type SendFunction = (text: string) => Promise<Message>;\n\nexport const usePushToTalk = ({\n sendFunction,\n inProgress,\n}: {\n sendFunction: SendFunction;\n inProgress: boolean;\n}) => {\n const [pushToTalkState, setPushToTalkState] = useState<PushToTalkState>(\"idle\");\n const mediaStreamRef = useRef<MediaStream | null>(null);\n const audioContextRef = useRef<AudioContext | null>(null);\n const mediaRecorderRef = useRef<MediaRecorder | null>(null);\n const recordedChunks = useRef<Blob[]>([]);\n const context = useCopilotContext();\n const [startReadingFromMessageId, setStartReadingFromMessageId] = useState<string | null>(null);\n\n useEffect(() => {\n if (pushToTalkState === \"recording\") {\n startRecording(\n mediaStreamRef,\n mediaRecorderRef,\n audioContextRef,\n recordedChunks.current,\n () => {\n setPushToTalkState(\"transcribing\");\n },\n );\n } else {\n stopRecording(mediaRecorderRef);\n if (pushToTalkState === \"transcribing\") {\n transcribeAudio(recordedChunks.current, context.copilotApiConfig.transcribeAudioUrl!).then(\n async (transcription) => {\n recordedChunks.current = [];\n setPushToTalkState(\"idle\");\n const message = await sendFunction(transcription);\n setStartReadingFromMessageId(message.id);\n },\n );\n }\n }\n\n return () => {\n stopRecording(mediaRecorderRef);\n };\n }, [pushToTalkState]);\n\n useEffect(() => {\n if (inProgress === false && startReadingFromMessageId) {\n const lastMessageIndex = context.messages.findIndex(\n (message) => message.id === startReadingFromMessageId,\n );\n\n const messagesAfterLast = context.messages\n .slice(lastMessageIndex + 1)\n .filter(\n (message) => message instanceof TextMessage && message.role === \"assistant\",\n ) as TextMessage[];\n\n const text = messagesAfterLast.map((message) => message.content).join(\"\\n\");\n playAudioResponse(text, context.copilotApiConfig.textToSpeechUrl!, audioContextRef.current!);\n\n setStartReadingFromMessageId(null);\n }\n }, [startReadingFromMessageId, inProgress]);\n\n return { pushToTalkState, setPushToTalkState };\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,wBAAkC;AAClC,gCAAqC;AACrC,mBAA8D;AAEvD,IAAM,4BAA4B,MAAY;AACnD,MAAI;AACF,UAAM,mBAAmB,MAAM,UAAU,YAAY,MAAM;AAAA,MACzD,MAAM;AAAA,IACR,CAAC;AACD,QAAI,iBAAiB,UAAU,WAAW;AACxC,aAAO;AAAA,IACT,OAAO;AACL,aAAO;AAAA,IACT;AAAA,EACF,SAAS,
|
|
1
|
+
{"version":3,"sources":["../../src/hooks/use-push-to-talk.tsx"],"sourcesContent":["import { useCopilotContext } from \"@copilotkit/react-core\";\nimport { Message, TextMessage } from \"@copilotkit/runtime-client-gql\";\nimport { MutableRefObject, useEffect, useRef, useState } from \"react\";\n\nexport const checkMicrophonePermission = async () => {\n try {\n const permissionStatus = await navigator.permissions.query({\n name: \"microphone\" as PermissionName,\n });\n if (permissionStatus.state === \"granted\") {\n return true;\n } else {\n return false;\n }\n } catch (err) {\n console.error(\"Error checking microphone permission\", err);\n }\n};\n\nexport const requestMicAndPlaybackPermission = async () => {\n try {\n const stream = await navigator.mediaDevices.getUserMedia({ audio: true });\n const audioContext = new window.AudioContext();\n await audioContext.resume();\n return { stream, audioContext };\n } catch (err) {\n console.error(\"Error requesting microphone and playback permissions\", err);\n return null;\n }\n};\n\nconst startRecording = async (\n mediaStreamRef: MutableRefObject<MediaStream | null>,\n mediaRecorderRef: MutableRefObject<MediaRecorder | null>,\n audioContextRef: MutableRefObject<AudioContext | null>,\n recordedChunks: Blob[],\n onStop: () => void,\n) => {\n if (!mediaStreamRef.current || !audioContextRef.current) {\n mediaStreamRef.current = await navigator.mediaDevices.getUserMedia({ audio: true });\n audioContextRef.current = new window.AudioContext();\n await audioContextRef.current.resume();\n }\n\n mediaRecorderRef.current = new MediaRecorder(mediaStreamRef.current!);\n mediaRecorderRef.current.start(1000);\n mediaRecorderRef.current.ondataavailable = (event) => {\n recordedChunks.push(event.data);\n };\n mediaRecorderRef.current.onstop = onStop;\n};\n\nconst stopRecording = (mediaRecorderRef: MutableRefObject<MediaRecorder | null>) => {\n if (mediaRecorderRef.current && mediaRecorderRef.current.state !== \"inactive\") {\n mediaRecorderRef.current.stop();\n }\n};\n\nconst transcribeAudio = async (recordedChunks: Blob[], transcribeAudioUrl: string) => {\n const completeBlob = new Blob(recordedChunks, { type: \"audio/mp4\" });\n const formData = new FormData();\n formData.append(\"file\", completeBlob, \"recording.mp4\");\n\n const response = await fetch(transcribeAudioUrl, {\n method: \"POST\",\n body: formData,\n });\n\n if (!response.ok) {\n throw new Error(`Error: ${response.statusText}`);\n }\n\n const transcription = await response.json();\n return transcription.text;\n};\n\nconst playAudioResponse = (text: string, textToSpeechUrl: string, audioContext: AudioContext) => {\n const encodedText = encodeURIComponent(text);\n const url = `${textToSpeechUrl}?text=${encodedText}`;\n\n fetch(url)\n .then((response) => response.arrayBuffer())\n .then((arrayBuffer) => audioContext.decodeAudioData(arrayBuffer))\n .then((audioBuffer) => {\n const source = audioContext.createBufferSource();\n source.buffer = audioBuffer;\n source.connect(audioContext.destination);\n source.start(0);\n })\n .catch((error) => {\n console.error(\"Error with decoding audio data\", error);\n });\n};\n\nexport type PushToTalkState = \"idle\" | \"recording\" | \"transcribing\";\n\nexport type SendFunction = (text: string) => Promise<Message>;\n\nexport const usePushToTalk = ({\n sendFunction,\n inProgress,\n}: {\n sendFunction: SendFunction;\n inProgress: boolean;\n}) => {\n const [pushToTalkState, setPushToTalkState] = useState<PushToTalkState>(\"idle\");\n const mediaStreamRef = useRef<MediaStream | null>(null);\n const audioContextRef = useRef<AudioContext | null>(null);\n const mediaRecorderRef = useRef<MediaRecorder | null>(null);\n const recordedChunks = useRef<Blob[]>([]);\n const context = useCopilotContext();\n const [startReadingFromMessageId, setStartReadingFromMessageId] = useState<string | null>(null);\n\n useEffect(() => {\n if (pushToTalkState === \"recording\") {\n startRecording(\n mediaStreamRef,\n mediaRecorderRef,\n audioContextRef,\n recordedChunks.current,\n () => {\n setPushToTalkState(\"transcribing\");\n },\n );\n } else {\n stopRecording(mediaRecorderRef);\n if (pushToTalkState === \"transcribing\") {\n transcribeAudio(recordedChunks.current, context.copilotApiConfig.transcribeAudioUrl!).then(\n async (transcription) => {\n recordedChunks.current = [];\n setPushToTalkState(\"idle\");\n const message = await sendFunction(transcription);\n setStartReadingFromMessageId(message.id);\n },\n );\n }\n }\n\n return () => {\n stopRecording(mediaRecorderRef);\n };\n }, [pushToTalkState]);\n\n useEffect(() => {\n if (inProgress === false && startReadingFromMessageId) {\n const lastMessageIndex = context.messages.findIndex(\n (message) => message.id === startReadingFromMessageId,\n );\n\n const messagesAfterLast = context.messages\n .slice(lastMessageIndex + 1)\n .filter(\n (message) => message instanceof TextMessage && message.role === \"assistant\",\n ) as TextMessage[];\n\n const text = messagesAfterLast.map((message) => message.content).join(\"\\n\");\n playAudioResponse(text, context.copilotApiConfig.textToSpeechUrl!, audioContextRef.current!);\n\n setStartReadingFromMessageId(null);\n }\n }, [startReadingFromMessageId, inProgress]);\n\n return { pushToTalkState, setPushToTalkState };\n};\n"],"mappings":"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","names":[]}
|
|
@@ -1,9 +1,151 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
var __async = (__this, __arguments, generator) => {
|
|
2
|
+
return new Promise((resolve, reject) => {
|
|
3
|
+
var fulfilled = (value) => {
|
|
4
|
+
try {
|
|
5
|
+
step(generator.next(value));
|
|
6
|
+
} catch (e) {
|
|
7
|
+
reject(e);
|
|
8
|
+
}
|
|
9
|
+
};
|
|
10
|
+
var rejected = (value) => {
|
|
11
|
+
try {
|
|
12
|
+
step(generator.throw(value));
|
|
13
|
+
} catch (e) {
|
|
14
|
+
reject(e);
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
|
|
18
|
+
step((generator = generator.apply(__this, __arguments)).next());
|
|
19
|
+
});
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
// src/hooks/use-push-to-talk.tsx
|
|
23
|
+
import { useCopilotContext } from "@copilotkit/react-core";
|
|
24
|
+
import { TextMessage } from "@copilotkit/runtime-client-gql";
|
|
25
|
+
import { useEffect, useRef, useState } from "react";
|
|
26
|
+
var checkMicrophonePermission = () => __async(void 0, null, function* () {
|
|
27
|
+
try {
|
|
28
|
+
const permissionStatus = yield navigator.permissions.query({
|
|
29
|
+
name: "microphone"
|
|
30
|
+
});
|
|
31
|
+
if (permissionStatus.state === "granted") {
|
|
32
|
+
return true;
|
|
33
|
+
} else {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
} catch (err) {
|
|
37
|
+
console.error("Error checking microphone permission", err);
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
var requestMicAndPlaybackPermission = () => __async(void 0, null, function* () {
|
|
41
|
+
try {
|
|
42
|
+
const stream = yield navigator.mediaDevices.getUserMedia({ audio: true });
|
|
43
|
+
const audioContext = new window.AudioContext();
|
|
44
|
+
yield audioContext.resume();
|
|
45
|
+
return { stream, audioContext };
|
|
46
|
+
} catch (err) {
|
|
47
|
+
console.error("Error requesting microphone and playback permissions", err);
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
var startRecording = (mediaStreamRef, mediaRecorderRef, audioContextRef, recordedChunks, onStop) => __async(void 0, null, function* () {
|
|
52
|
+
if (!mediaStreamRef.current || !audioContextRef.current) {
|
|
53
|
+
mediaStreamRef.current = yield navigator.mediaDevices.getUserMedia({ audio: true });
|
|
54
|
+
audioContextRef.current = new window.AudioContext();
|
|
55
|
+
yield audioContextRef.current.resume();
|
|
56
|
+
}
|
|
57
|
+
mediaRecorderRef.current = new MediaRecorder(mediaStreamRef.current);
|
|
58
|
+
mediaRecorderRef.current.start(1e3);
|
|
59
|
+
mediaRecorderRef.current.ondataavailable = (event) => {
|
|
60
|
+
recordedChunks.push(event.data);
|
|
61
|
+
};
|
|
62
|
+
mediaRecorderRef.current.onstop = onStop;
|
|
63
|
+
});
|
|
64
|
+
var stopRecording = (mediaRecorderRef) => {
|
|
65
|
+
if (mediaRecorderRef.current && mediaRecorderRef.current.state !== "inactive") {
|
|
66
|
+
mediaRecorderRef.current.stop();
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
var transcribeAudio = (recordedChunks, transcribeAudioUrl) => __async(void 0, null, function* () {
|
|
70
|
+
const completeBlob = new Blob(recordedChunks, { type: "audio/mp4" });
|
|
71
|
+
const formData = new FormData();
|
|
72
|
+
formData.append("file", completeBlob, "recording.mp4");
|
|
73
|
+
const response = yield fetch(transcribeAudioUrl, {
|
|
74
|
+
method: "POST",
|
|
75
|
+
body: formData
|
|
76
|
+
});
|
|
77
|
+
if (!response.ok) {
|
|
78
|
+
throw new Error(`Error: ${response.statusText}`);
|
|
79
|
+
}
|
|
80
|
+
const transcription = yield response.json();
|
|
81
|
+
return transcription.text;
|
|
82
|
+
});
|
|
83
|
+
var playAudioResponse = (text, textToSpeechUrl, audioContext) => {
|
|
84
|
+
const encodedText = encodeURIComponent(text);
|
|
85
|
+
const url = `${textToSpeechUrl}?text=${encodedText}`;
|
|
86
|
+
fetch(url).then((response) => response.arrayBuffer()).then((arrayBuffer) => audioContext.decodeAudioData(arrayBuffer)).then((audioBuffer) => {
|
|
87
|
+
const source = audioContext.createBufferSource();
|
|
88
|
+
source.buffer = audioBuffer;
|
|
89
|
+
source.connect(audioContext.destination);
|
|
90
|
+
source.start(0);
|
|
91
|
+
}).catch((error) => {
|
|
92
|
+
console.error("Error with decoding audio data", error);
|
|
93
|
+
});
|
|
94
|
+
};
|
|
95
|
+
var usePushToTalk = ({
|
|
96
|
+
sendFunction,
|
|
97
|
+
inProgress
|
|
98
|
+
}) => {
|
|
99
|
+
const [pushToTalkState, setPushToTalkState] = useState("idle");
|
|
100
|
+
const mediaStreamRef = useRef(null);
|
|
101
|
+
const audioContextRef = useRef(null);
|
|
102
|
+
const mediaRecorderRef = useRef(null);
|
|
103
|
+
const recordedChunks = useRef([]);
|
|
104
|
+
const context = useCopilotContext();
|
|
105
|
+
const [startReadingFromMessageId, setStartReadingFromMessageId] = useState(null);
|
|
106
|
+
useEffect(() => {
|
|
107
|
+
if (pushToTalkState === "recording") {
|
|
108
|
+
startRecording(
|
|
109
|
+
mediaStreamRef,
|
|
110
|
+
mediaRecorderRef,
|
|
111
|
+
audioContextRef,
|
|
112
|
+
recordedChunks.current,
|
|
113
|
+
() => {
|
|
114
|
+
setPushToTalkState("transcribing");
|
|
115
|
+
}
|
|
116
|
+
);
|
|
117
|
+
} else {
|
|
118
|
+
stopRecording(mediaRecorderRef);
|
|
119
|
+
if (pushToTalkState === "transcribing") {
|
|
120
|
+
transcribeAudio(recordedChunks.current, context.copilotApiConfig.transcribeAudioUrl).then(
|
|
121
|
+
(transcription) => __async(void 0, null, function* () {
|
|
122
|
+
recordedChunks.current = [];
|
|
123
|
+
setPushToTalkState("idle");
|
|
124
|
+
const message = yield sendFunction(transcription);
|
|
125
|
+
setStartReadingFromMessageId(message.id);
|
|
126
|
+
})
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return () => {
|
|
131
|
+
stopRecording(mediaRecorderRef);
|
|
132
|
+
};
|
|
133
|
+
}, [pushToTalkState]);
|
|
134
|
+
useEffect(() => {
|
|
135
|
+
if (inProgress === false && startReadingFromMessageId) {
|
|
136
|
+
const lastMessageIndex = context.messages.findIndex(
|
|
137
|
+
(message) => message.id === startReadingFromMessageId
|
|
138
|
+
);
|
|
139
|
+
const messagesAfterLast = context.messages.slice(lastMessageIndex + 1).filter(
|
|
140
|
+
(message) => message instanceof TextMessage && message.role === "assistant"
|
|
141
|
+
);
|
|
142
|
+
const text = messagesAfterLast.map((message) => message.content).join("\n");
|
|
143
|
+
playAudioResponse(text, context.copilotApiConfig.textToSpeechUrl, audioContextRef.current);
|
|
144
|
+
setStartReadingFromMessageId(null);
|
|
145
|
+
}
|
|
146
|
+
}, [startReadingFromMessageId, inProgress]);
|
|
147
|
+
return { pushToTalkState, setPushToTalkState };
|
|
148
|
+
};
|
|
7
149
|
export {
|
|
8
150
|
checkMicrophonePermission,
|
|
9
151
|
requestMicAndPlaybackPermission,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/hooks/use-push-to-talk.tsx"],"sourcesContent":["import { useCopilotContext } from \"@copilotkit/react-core\";\nimport { Message, TextMessage } from \"@copilotkit/runtime-client-gql\";\nimport { MutableRefObject, useEffect, useRef, useState } from \"react\";\n\nexport const checkMicrophonePermission = async () => {\n try {\n const permissionStatus = await navigator.permissions.query({\n name: \"microphone\" as PermissionName,\n });\n if (permissionStatus.state === \"granted\") {\n return true;\n } else {\n return false;\n }\n } catch (err) {\n console.error(\"Error checking microphone permission\", err);\n }\n};\n\nexport const requestMicAndPlaybackPermission = async () => {\n try {\n const stream = await navigator.mediaDevices.getUserMedia({ audio: true });\n const audioContext = new window.AudioContext();\n await audioContext.resume();\n return { stream, audioContext };\n } catch (err) {\n console.error(\"Error requesting microphone and playback permissions\", err);\n return null;\n }\n};\n\nconst startRecording = async (\n mediaStreamRef: MutableRefObject<MediaStream | null>,\n mediaRecorderRef: MutableRefObject<MediaRecorder | null>,\n audioContextRef: MutableRefObject<AudioContext | null>,\n recordedChunks: Blob[],\n onStop: () => void,\n) => {\n if (!mediaStreamRef.current || !audioContextRef.current) {\n mediaStreamRef.current = await navigator.mediaDevices.getUserMedia({ audio: true });\n audioContextRef.current = new window.AudioContext();\n await audioContextRef.current.resume();\n }\n\n mediaRecorderRef.current = new MediaRecorder(mediaStreamRef.current!);\n mediaRecorderRef.current.start(1000);\n mediaRecorderRef.current.ondataavailable = (event) => {\n recordedChunks.push(event.data);\n };\n mediaRecorderRef.current.onstop = onStop;\n};\n\nconst stopRecording = (mediaRecorderRef: MutableRefObject<MediaRecorder | null>) => {\n if (mediaRecorderRef.current && mediaRecorderRef.current.state !== \"inactive\") {\n mediaRecorderRef.current.stop();\n }\n};\n\nconst transcribeAudio = async (recordedChunks: Blob[], transcribeAudioUrl: string) => {\n const completeBlob = new Blob(recordedChunks, { type: \"audio/mp4\" });\n const formData = new FormData();\n formData.append(\"file\", completeBlob, \"recording.mp4\");\n\n const response = await fetch(transcribeAudioUrl, {\n method: \"POST\",\n body: formData,\n });\n\n if (!response.ok) {\n throw new Error(`Error: ${response.statusText}`);\n }\n\n const transcription = await response.json();\n return transcription.text;\n};\n\nconst playAudioResponse = (text: string, textToSpeechUrl: string, audioContext: AudioContext) => {\n const encodedText = encodeURIComponent(text);\n const url = `${textToSpeechUrl}?text=${encodedText}`;\n\n fetch(url)\n .then((response) => response.arrayBuffer())\n .then((arrayBuffer) => audioContext.decodeAudioData(arrayBuffer))\n .then((audioBuffer) => {\n const source = audioContext.createBufferSource();\n source.buffer = audioBuffer;\n source.connect(audioContext.destination);\n source.start(0);\n })\n .catch((error) => {\n console.error(\"Error with decoding audio data\", error);\n });\n};\n\nexport type PushToTalkState = \"idle\" | \"recording\" | \"transcribing\";\n\nexport type SendFunction = (text: string) => Promise<Message>;\n\nexport const usePushToTalk = ({\n sendFunction,\n inProgress,\n}: {\n sendFunction: SendFunction;\n inProgress: boolean;\n}) => {\n const [pushToTalkState, setPushToTalkState] = useState<PushToTalkState>(\"idle\");\n const mediaStreamRef = useRef<MediaStream | null>(null);\n const audioContextRef = useRef<AudioContext | null>(null);\n const mediaRecorderRef = useRef<MediaRecorder | null>(null);\n const recordedChunks = useRef<Blob[]>([]);\n const context = useCopilotContext();\n const [startReadingFromMessageId, setStartReadingFromMessageId] = useState<string | null>(null);\n\n useEffect(() => {\n if (pushToTalkState === \"recording\") {\n startRecording(\n mediaStreamRef,\n mediaRecorderRef,\n audioContextRef,\n recordedChunks.current,\n () => {\n setPushToTalkState(\"transcribing\");\n },\n );\n } else {\n stopRecording(mediaRecorderRef);\n if (pushToTalkState === \"transcribing\") {\n transcribeAudio(recordedChunks.current, context.copilotApiConfig.transcribeAudioUrl!).then(\n async (transcription) => {\n recordedChunks.current = [];\n setPushToTalkState(\"idle\");\n const message = await sendFunction(transcription);\n setStartReadingFromMessageId(message.id);\n },\n );\n }\n }\n\n return () => {\n stopRecording(mediaRecorderRef);\n };\n }, [pushToTalkState]);\n\n useEffect(() => {\n if (inProgress === false && startReadingFromMessageId) {\n const lastMessageIndex = context.messages.findIndex(\n (message) => message.id === startReadingFromMessageId,\n );\n\n const messagesAfterLast = context.messages\n .slice(lastMessageIndex + 1)\n .filter(\n (message) => message instanceof TextMessage && message.role === \"assistant\",\n ) as TextMessage[];\n\n const text = messagesAfterLast.map((message) => message.content).join(\"\\n\");\n playAudioResponse(text, context.copilotApiConfig.textToSpeechUrl!, audioContextRef.current!);\n\n setStartReadingFromMessageId(null);\n }\n }, [startReadingFromMessageId, inProgress]);\n\n return { pushToTalkState, setPushToTalkState };\n};\n"],"mappings":"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","names":[]}
|