@copilotkit/react-ui
Version:
<div align="center"> <a href="https://copilotkit.ai" target="_blank"> <img src="https://github.com/copilotkit/copilotkit/raw/main/assets/banner.png" alt="CopilotKit Logo"> </a>
333 lines (328 loc) • 13.2 kB
JavaScript
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __propIsEnum = Object.prototype.propertyIsEnumerable;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __spreadValues = (a, b) => {
for (var prop in b || (b = {}))
if (__hasOwnProp.call(b, prop))
__defNormalProp(a, prop, b[prop]);
if (__getOwnPropSymbols)
for (var prop of __getOwnPropSymbols(b)) {
if (__propIsEnum.call(b, prop))
__defNormalProp(a, prop, b[prop]);
}
return a;
};
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var __async = (__this, __arguments, generator) => {
return new Promise((resolve, reject) => {
var fulfilled = (value) => {
try {
step(generator.next(value));
} catch (e) {
reject(e);
}
};
var rejected = (value) => {
try {
step(generator.throw(value));
} catch (e) {
reject(e);
}
};
var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
step((generator = generator.apply(__this, __arguments)).next());
});
};
// src/components/chat/Input.tsx
var Input_exports = {};
__export(Input_exports, {
Input: () => Input
});
module.exports = __toCommonJS(Input_exports);
var import_react4 = require("react");
// src/components/chat/ChatContext.tsx
var import_react = __toESM(require("react"));
var import_jsx_runtime = require("react/jsx-runtime");
var ChatContext = import_react.default.createContext(void 0);
function useChatContext() {
const context = import_react.default.useContext(ChatContext);
if (context === void 0) {
throw new Error(
"Context not found. Did you forget to wrap your app in a <ChatContextProvider> component?"
);
}
return context;
}
// src/components/chat/Textarea.tsx
var import_react2 = require("react");
var import_jsx_runtime2 = require("react/jsx-runtime");
var AutoResizingTextarea = (0, import_react2.forwardRef)(
({ maxRows = 1, placeholder, value, onChange, onKeyDown, autoFocus }, ref) => {
const internalTextareaRef = (0, import_react2.useRef)(null);
const [maxHeight, setMaxHeight] = (0, import_react2.useState)(0);
(0, import_react2.useImperativeHandle)(ref, () => internalTextareaRef.current);
(0, import_react2.useEffect)(() => {
const calculateMaxHeight = () => {
const textarea = internalTextareaRef.current;
if (textarea) {
textarea.style.height = "auto";
const singleRowHeight = textarea.scrollHeight;
setMaxHeight(singleRowHeight * maxRows);
if (autoFocus) {
textarea.focus();
}
}
};
calculateMaxHeight();
}, [maxRows]);
(0, import_react2.useEffect)(() => {
const textarea = internalTextareaRef.current;
if (textarea) {
textarea.style.height = "auto";
textarea.style.height = `${Math.min(textarea.scrollHeight, maxHeight)}px`;
}
}, [value, maxHeight]);
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
"textarea",
{
ref: internalTextareaRef,
value,
onChange,
onKeyDown,
placeholder,
style: {
overflow: "auto",
resize: "none",
maxHeight: `${maxHeight}px`
},
rows: 1
}
);
}
);
var Textarea_default = AutoResizingTextarea;
// src/hooks/use-push-to-talk.tsx
var import_react_core = require("@copilotkit/react-core");
var import_react3 = require("react");
var startRecording = (mediaStreamRef, mediaRecorderRef, audioContextRef, recordedChunks, onStop) => __async(void 0, null, function* () {
if (!mediaStreamRef.current || !audioContextRef.current) {
mediaStreamRef.current = yield navigator.mediaDevices.getUserMedia({ audio: true });
audioContextRef.current = new window.AudioContext();
yield audioContextRef.current.resume();
}
mediaRecorderRef.current = new MediaRecorder(mediaStreamRef.current);
mediaRecorderRef.current.start(1e3);
mediaRecorderRef.current.ondataavailable = (event) => {
recordedChunks.push(event.data);
};
mediaRecorderRef.current.onstop = onStop;
});
var stopRecording = (mediaRecorderRef) => {
if (mediaRecorderRef.current && mediaRecorderRef.current.state !== "inactive") {
mediaRecorderRef.current.stop();
}
};
var transcribeAudio = (recordedChunks, transcribeAudioUrl) => __async(void 0, null, function* () {
const completeBlob = new Blob(recordedChunks, { type: "audio/mp4" });
const formData = new FormData();
formData.append("file", completeBlob, "recording.mp4");
const response = yield fetch(transcribeAudioUrl, {
method: "POST",
body: formData
});
if (!response.ok) {
throw new Error(`Error: ${response.statusText}`);
}
const transcription = yield response.json();
return transcription.text;
});
var playAudioResponse = (text, textToSpeechUrl, audioContext) => {
const encodedText = encodeURIComponent(text);
const url = `${textToSpeechUrl}?text=${encodedText}`;
fetch(url).then((response) => response.arrayBuffer()).then((arrayBuffer) => audioContext.decodeAudioData(arrayBuffer)).then((audioBuffer) => {
const source = audioContext.createBufferSource();
source.buffer = audioBuffer;
source.connect(audioContext.destination);
source.start(0);
}).catch((error) => {
console.error("Error with decoding audio data", error);
});
};
var usePushToTalk = ({
sendFunction,
inProgress
}) => {
const [pushToTalkState, setPushToTalkState] = (0, import_react3.useState)("idle");
const mediaStreamRef = (0, import_react3.useRef)(null);
const audioContextRef = (0, import_react3.useRef)(null);
const mediaRecorderRef = (0, import_react3.useRef)(null);
const recordedChunks = (0, import_react3.useRef)([]);
const generalContext = (0, import_react_core.useCopilotContext)();
const messagesContext = (0, import_react_core.useCopilotMessagesContext)();
const context = __spreadValues(__spreadValues({}, generalContext), messagesContext);
const [startReadingFromMessageId, setStartReadingFromMessageId] = (0, import_react3.useState)(null);
(0, import_react3.useEffect)(() => {
if (pushToTalkState === "recording") {
startRecording(
mediaStreamRef,
mediaRecorderRef,
audioContextRef,
recordedChunks.current,
() => {
setPushToTalkState("transcribing");
}
);
} else {
stopRecording(mediaRecorderRef);
if (pushToTalkState === "transcribing") {
transcribeAudio(recordedChunks.current, context.copilotApiConfig.transcribeAudioUrl).then(
(transcription) => __async(void 0, null, function* () {
recordedChunks.current = [];
setPushToTalkState("idle");
const message = yield sendFunction(transcription);
setStartReadingFromMessageId(message.id);
})
);
}
}
return () => {
stopRecording(mediaRecorderRef);
};
}, [pushToTalkState]);
(0, import_react3.useEffect)(() => {
if (inProgress === false && startReadingFromMessageId) {
const lastMessageIndex = context.messages.findIndex(
(message) => message.id === startReadingFromMessageId
);
const messagesAfterLast = context.messages.slice(lastMessageIndex + 1).filter(
(message) => message.isTextMessage() && message.role === "assistant"
);
const text = messagesAfterLast.map((message) => message.content).join("\n");
playAudioResponse(text, context.copilotApiConfig.textToSpeechUrl, audioContextRef.current);
setStartReadingFromMessageId(null);
}
}, [startReadingFromMessageId, inProgress]);
return { pushToTalkState, setPushToTalkState };
};
// src/components/chat/Input.tsx
var import_react_core2 = require("@copilotkit/react-core");
var import_jsx_runtime3 = require("react/jsx-runtime");
var Input = ({ inProgress, onSend, isVisible = false, onStop }) => {
const context = useChatContext();
const copilotContext = (0, import_react_core2.useCopilotContext)();
const pushToTalkConfigured = copilotContext.copilotApiConfig.textToSpeechUrl !== void 0 && copilotContext.copilotApiConfig.transcribeAudioUrl !== void 0;
const textareaRef = (0, import_react4.useRef)(null);
const handleDivClick = (event) => {
var _a;
const target = event.target;
if (target.closest("button"))
return;
if (target.tagName === "TEXTAREA")
return;
(_a = textareaRef.current) == null ? void 0 : _a.focus();
};
const [text, setText] = (0, import_react4.useState)("");
const send = () => {
var _a;
if (inProgress)
return;
onSend(text);
setText("");
(_a = textareaRef.current) == null ? void 0 : _a.focus();
};
(0, import_react4.useEffect)(() => {
var _a;
if (isVisible) {
(_a = textareaRef.current) == null ? void 0 : _a.focus();
}
}, [isVisible]);
const { pushToTalkState, setPushToTalkState } = usePushToTalk({
sendFunction: onSend,
inProgress
});
const isInProgress = inProgress || pushToTalkState === "transcribing";
const buttonIcon = isInProgress ? context.icons.stopIcon : context.icons.sendIcon;
const showPushToTalk = pushToTalkConfigured && (pushToTalkState === "idle" || pushToTalkState === "recording") && !inProgress;
const canSend = () => {
var _a;
const interruptEvent = (_a = copilotContext.langGraphInterruptAction) == null ? void 0 : _a.event;
const interruptInProgress = (interruptEvent == null ? void 0 : interruptEvent.name) === "LangGraphInterruptEvent" && !(interruptEvent == null ? void 0 : interruptEvent.response);
return (isInProgress || !isInProgress && text.trim().length > 0) && pushToTalkState === "idle" && !interruptInProgress;
};
const sendDisabled = !canSend();
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "copilotKitInputContainer", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "copilotKitInput", onClick: handleDivClick, children: [
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
Textarea_default,
{
ref: textareaRef,
placeholder: context.labels.placeholder,
autoFocus: true,
maxRows: 5,
value: text,
onChange: (event) => setText(event.target.value),
onKeyDown: (event) => {
if (event.key === "Enter" && !event.shiftKey) {
event.preventDefault();
if (canSend()) {
send();
}
}
}
}
),
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "copilotKitInputControls", children: [
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { flexGrow: 1 } }),
showPushToTalk && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
"button",
{
onClick: () => setPushToTalkState(pushToTalkState === "idle" ? "recording" : "transcribing"),
className: pushToTalkState === "recording" ? "copilotKitInputControlButton copilotKitPushToTalkRecording" : "copilotKitInputControlButton",
children: context.icons.pushToTalkIcon
}
),
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
"button",
{
disabled: sendDisabled,
onClick: isInProgress ? onStop : send,
"data-copilotkit-in-progress": inProgress,
"data-test-id": inProgress ? "copilot-chat-request-in-progress" : "copilot-chat-ready",
className: "copilotKitInputControlButton",
children: buttonIcon
}
)
] })
] }) });
};
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
Input
});
//# sourceMappingURL=Input.js.map
;