@copilotkit/react-core
Version:
<img src="https://github.com/user-attachments/assets/0a6b64d9-e193-4940-a3f6-60334ac34084" alt="banner" style="border-radius: 12px; border: 2px solid #d6d4fa;" />
1,135 lines (1,127 loc) • 374 kB
JavaScript
import * as React$1 from "react";
import React, { createContext, forwardRef, memo, useCallback, useContext, useEffect, useId, useImperativeHandle, useLayoutEffect, useMemo, useReducer, useRef, useState, useSyncExternalStore } from "react";
import { CopilotKitCore, CopilotKitCoreRuntimeConnectionStatus, ProxiedCopilotRuntimeAgent, ToolCallStatus, ɵcreateThreadStore, ɵselectHasNextPage, ɵselectIsFetchingNextPage, ɵselectThreads, ɵselectThreadsError, ɵselectThreadsIsLoading } from "@copilotkit/core";
import { HttpAgent } from "@ag-ui/client";
import { extendTailwindMerge, twMerge } from "tailwind-merge";
import { ArrowUp, Check, ChevronDown, ChevronLeft, ChevronRight, ChevronRightIcon, Copy, Edit, Loader2, MessageCircle, Mic, Play, Plus, RefreshCw, Square, ThumbsDown, ThumbsUp, Upload, Volume2, X } from "lucide-react";
import { A2UI_DEFAULT_DESIGN_GUIDELINES, A2UI_DEFAULT_GENERATION_GUIDELINES, COPILOT_CLOUD_API_URL, COPILOT_CLOUD_CHAT_URL, COPILOT_CLOUD_PUBLIC_API_KEY_HEADER, ConfigurationError, CopilotKitAgentDiscoveryError, CopilotKitApiDiscoveryError, CopilotKitError, CopilotKitErrorCode, CopilotKitLowLevelError, CopilotKitRemoteEndpointDiscoveryError, DEFAULT_AGENT_ID, ErrorVisibility, MissingPublicApiKeyError, Severity, TranscriptionErrorCode, TranscriptionErrorCode as TranscriptionErrorCode$1, copyToClipboard, createLicenseContextValue, dataToUUID, exceedsMaxSize, formatFileSize, generateVideoThumbnail, getDocumentIcon, getModalityFromMimeType, getSourceUrl, matchesAcceptFilter, parseJson, partialJSONParse, randomId, randomUUID, readFileAsBase64, schemaToJsonSchema } from "@copilotkit/shared";
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
import { Slot } from "@radix-ui/react-slot";
import { cva } from "class-variance-authority";
import { clsx } from "clsx";
import * as TooltipPrimitive from "@radix-ui/react-tooltip";
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
import { Streamdown } from "streamdown";
import { z } from "zod";
import { createComponent } from "@lit-labs/react";
import { A2UIProvider, A2UIRenderer, A2UI_SCHEMA_CONTEXT_DESCRIPTION, DEFAULT_SURFACE_ID, buildCatalogContextValue, extractCatalogComponentSchemas, initializeDefaultCatalog, injectStyles, useA2UIActions, useA2UIError, viewerTheme } from "@copilotkit/a2ui-renderer";
import { zodToJsonSchema } from "zod-to-json-schema";
import { createPortal, flushSync } from "react-dom";
import { useVirtualizer } from "@tanstack/react-virtual";
import { StickToBottom, useStickToBottomContext } from "use-stick-to-bottom";
import ReactMarkdown from "react-markdown";
//#region src/v2/lib/slots.tsx
/**
* Shallow equality comparison for objects.
*/
function shallowEqual(obj1, obj2) {
const keys1 = Object.keys(obj1);
const keys2 = Object.keys(obj2);
if (keys1.length !== keys2.length) return false;
for (const key of keys1) if (obj1[key] !== obj2[key]) return false;
return true;
}
/**
* Returns true only for plain JS objects (`{}`), excluding arrays, Dates,
* class instances, and other exotic objects that happen to have typeof "object".
*/
function isPlainObject(obj) {
return obj !== null && typeof obj === "object" && Object.prototype.toString.call(obj) === "[object Object]";
}
/**
* Returns the same reference as long as the value is shallowly equal to the
* previous render's value.
*
* - Identical references bail out immediately (O(1)).
* - Plain objects ({}) are shallow-compared key-by-key.
* - Arrays, Dates, class instances, functions, and primitives are compared by
* reference only — shallowEqual is never called on non-plain objects, which
* avoids incorrect equality for e.g. [1,2] vs [1,2] (different arrays).
*
* Typical use: stabilize inline slot props so MemoizedSlotWrapper's shallow
* equality check isn't defeated by a new object reference on every render.
*/
function useShallowStableRef(value) {
const ref = useRef(value);
if (ref.current === value) return ref.current;
if (isPlainObject(ref.current) && isPlainObject(value)) {
if (shallowEqual(ref.current, value)) return ref.current;
}
ref.current = value;
return ref.current;
}
/**
* Check if a value is a React component type (function, class, forwardRef, memo, etc.)
*/
function isReactComponentType(value) {
if (typeof value === "function") return true;
if (value && typeof value === "object" && "$$typeof" in value && !React.isValidElement(value)) return true;
return false;
}
/**
* Internal function to render a slot value as a React element (non-memoized).
*/
function renderSlotElement(slot, DefaultComponent, props) {
if (typeof slot === "string") {
const existingClassName = props.className;
return React.createElement(DefaultComponent, {
...props,
className: twMerge(existingClassName, slot)
});
}
if (isReactComponentType(slot)) return React.createElement(slot, props);
if (slot && typeof slot === "object" && !React.isValidElement(slot)) return React.createElement(DefaultComponent, {
...props,
...slot
});
return React.createElement(DefaultComponent, props);
}
/**
* Internal memoized wrapper component for renderSlot.
* Uses forwardRef to support ref forwarding.
*/
const MemoizedSlotWrapper = React.memo(React.forwardRef(function MemoizedSlotWrapper(props, ref) {
const { $slot, $component, ...rest } = props;
return renderSlotElement($slot, $component, ref !== null ? {
...rest,
ref
} : rest);
}), (prev, next) => {
if (prev.$slot !== next.$slot) return false;
if (prev.$component !== next.$component) return false;
const { $slot: _ps, $component: _pc, ...prevRest } = prev;
const { $slot: _ns, $component: _nc, ...nextRest } = next;
return shallowEqual(prevRest, nextRest);
});
/**
* Renders a slot value as a memoized React element.
* Automatically prevents unnecessary re-renders using shallow prop comparison.
* Supports ref forwarding.
*
* @example
* renderSlot(customInput, CopilotChatInput, { onSubmit: handleSubmit })
*/
function renderSlot(slot, DefaultComponent, props) {
return React.createElement(MemoizedSlotWrapper, {
...props,
$slot: slot,
$component: DefaultComponent
});
}
//#endregion
//#region src/v2/providers/CopilotChatConfigurationProvider.tsx
const CopilotChatDefaultLabels = {
chatInputPlaceholder: "Type a message...",
chatInputToolbarStartTranscribeButtonLabel: "Transcribe",
chatInputToolbarCancelTranscribeButtonLabel: "Cancel",
chatInputToolbarFinishTranscribeButtonLabel: "Finish",
chatInputToolbarAddButtonLabel: "Add attachments",
chatInputToolbarToolsButtonLabel: "Tools",
assistantMessageToolbarCopyCodeLabel: "Copy",
assistantMessageToolbarCopyCodeCopiedLabel: "Copied",
assistantMessageToolbarCopyMessageLabel: "Copy",
assistantMessageToolbarThumbsUpLabel: "Good response",
assistantMessageToolbarThumbsDownLabel: "Bad response",
assistantMessageToolbarReadAloudLabel: "Read aloud",
assistantMessageToolbarRegenerateLabel: "Regenerate",
userMessageToolbarCopyMessageLabel: "Copy",
userMessageToolbarEditMessageLabel: "Edit",
chatDisclaimerText: "AI can make mistakes. Please verify important information.",
chatToggleOpenLabel: "Open chat",
chatToggleCloseLabel: "Close chat",
modalHeaderTitle: "CopilotKit Chat",
welcomeMessageText: "How can I help you today?"
};
const CopilotChatConfiguration = createContext(null);
const CopilotChatConfigurationProvider = ({ children, labels, agentId, threadId, hasExplicitThreadId, isModalDefaultOpen }) => {
const parentConfig = useContext(CopilotChatConfiguration);
const stableLabels = useShallowStableRef(labels);
const mergedLabels = useMemo(() => ({
...CopilotChatDefaultLabels,
...parentConfig?.labels,
...stableLabels
}), [stableLabels, parentConfig?.labels]);
const resolvedAgentId = agentId ?? parentConfig?.agentId ?? DEFAULT_AGENT_ID;
const resolvedThreadId = useMemo(() => {
if (threadId) return threadId;
if (parentConfig?.threadId) return parentConfig.threadId;
return randomUUID();
}, [threadId, parentConfig?.threadId]);
const resolvedHasExplicitThreadId = (hasExplicitThreadId !== void 0 ? hasExplicitThreadId : !!threadId) || !!parentConfig?.hasExplicitThreadId;
const [internalModalOpen, setInternalModalOpen] = useState(isModalDefaultOpen ?? true);
const hasExplicitDefault = isModalDefaultOpen !== void 0;
const setAndSync = useCallback((open) => {
setInternalModalOpen(open);
parentConfig?.setModalOpen(open);
}, [parentConfig?.setModalOpen]);
const isMounted = useRef(false);
useEffect(() => {
if (!hasExplicitDefault) return;
if (!isMounted.current) {
isMounted.current = true;
return;
}
if (parentConfig?.isModalOpen === void 0) return;
setInternalModalOpen(parentConfig.isModalOpen);
}, [parentConfig?.isModalOpen, hasExplicitDefault]);
const resolvedIsModalOpen = hasExplicitDefault ? internalModalOpen : parentConfig?.isModalOpen ?? internalModalOpen;
const resolvedSetModalOpen = hasExplicitDefault ? setAndSync : parentConfig?.setModalOpen ?? setInternalModalOpen;
const configurationValue = useMemo(() => ({
labels: mergedLabels,
agentId: resolvedAgentId,
threadId: resolvedThreadId,
hasExplicitThreadId: resolvedHasExplicitThreadId,
isModalOpen: resolvedIsModalOpen,
setModalOpen: resolvedSetModalOpen
}), [
mergedLabels,
resolvedAgentId,
resolvedThreadId,
resolvedHasExplicitThreadId,
resolvedIsModalOpen,
resolvedSetModalOpen
]);
return /* @__PURE__ */ jsx(CopilotChatConfiguration.Provider, {
value: configurationValue,
children
});
};
const useCopilotChatConfiguration = () => {
return useContext(CopilotChatConfiguration);
};
//#endregion
//#region src/v2/lib/utils.ts
const twMerge$1 = extendTailwindMerge({ prefix: "cpk" });
function cn(...inputs) {
return twMerge$1(clsx(inputs));
}
//#endregion
//#region src/v2/components/ui/button.tsx
const buttonVariants = cva("cpk:inline-flex cpk:items-center cpk:justify-center cpk:gap-2 cpk:whitespace-nowrap cpk:rounded-md cpk:text-sm cpk:font-medium cpk:transition-all cpk:disabled:pointer-events-none cpk:disabled:opacity-50 cpk:[&_svg]:pointer-events-none cpk:[&_svg:not([class*='size-'])]:size-4 cpk:shrink-0 cpk:[&_svg]:shrink-0 cpk:outline-none cpk:focus-visible:border-ring cpk:focus-visible:ring-ring/50 cpk:focus-visible:ring-[3px] cpk:aria-invalid:ring-destructive/20 cpk:dark:aria-invalid:ring-destructive/40 cpk:aria-invalid:border-destructive", {
variants: {
variant: {
default: "cpk:bg-primary cpk:text-primary-foreground cpk:shadow-xs cpk:hover:bg-primary/90",
destructive: "cpk:bg-destructive cpk:text-white cpk:shadow-xs cpk:hover:bg-destructive/90 cpk:focus-visible:ring-destructive/20 cpk:dark:focus-visible:ring-destructive/40 cpk:dark:bg-destructive/60",
outline: "cpk:border cpk:bg-background cpk:shadow-xs cpk:hover:bg-accent cpk:hover:text-accent-foreground cpk:dark:bg-input/30 cpk:dark:border-input cpk:dark:hover:bg-input/50",
secondary: "cpk:bg-secondary cpk:text-secondary-foreground cpk:shadow-xs cpk:hover:bg-secondary/80",
ghost: "cpk:hover:bg-accent cpk:hover:text-accent-foreground cpk:dark:hover:bg-accent/50 cpk:cursor-pointer",
link: "cpk:text-primary cpk:underline-offset-4 cpk:hover:underline",
assistantMessageToolbarButton: [
"cpk:cursor-pointer",
"cpk:p-0 cpk:text-[rgb(93,93,93)] cpk:hover:bg-[#E8E8E8]",
"cpk:dark:text-[rgb(243,243,243)] cpk:dark:hover:bg-[#303030]",
"cpk:h-8 cpk:w-8",
"cpk:transition-colors",
"cpk:hover:text-[rgb(93,93,93)]",
"cpk:dark:hover:text-[rgb(243,243,243)]"
],
chatInputToolbarPrimary: [
"cpk:cursor-pointer",
"cpk:bg-black cpk:text-white",
"cpk:dark:bg-white cpk:dark:text-black cpk:dark:focus-visible:outline-white",
"cpk:rounded-full",
"cpk:transition-colors",
"cpk:focus:outline-none",
"cpk:hover:opacity-70 cpk:disabled:hover:opacity-100",
"cpk:disabled:cursor-not-allowed cpk:disabled:bg-[#00000014] cpk:disabled:text-[rgb(13,13,13)]",
"cpk:dark:disabled:bg-[#454545] cpk:dark:disabled:text-white "
],
chatInputToolbarSecondary: [
"cpk:cursor-pointer",
"cpk:bg-transparent cpk:text-[#444444]",
"cpk:dark:text-white cpk:dark:border-[#404040]",
"cpk:rounded-full",
"cpk:transition-colors",
"cpk:focus:outline-none",
"cpk:hover:bg-[#f8f8f8] cpk:hover:text-[#333333]",
"cpk:dark:hover:bg-[#404040] cpk:dark:hover:text-[#FFFFFF]",
"cpk:disabled:cursor-not-allowed cpk:disabled:opacity-50",
"cpk:disabled:hover:bg-transparent cpk:disabled:hover:text-[#444444]",
"cpk:dark:disabled:hover:bg-transparent cpk:dark:disabled:hover:text-[#CCCCCC]"
]
},
size: {
default: "cpk:h-9 cpk:px-4 cpk:py-2 cpk:has-[>svg]:px-3",
sm: "cpk:h-8 cpk:rounded-md cpk:gap-1.5 cpk:px-3 cpk:has-[>svg]:px-2.5",
lg: "cpk:h-10 cpk:rounded-md cpk:px-6 cpk:has-[>svg]:px-4",
icon: "cpk:size-9",
chatInputToolbarIcon: ["cpk:h-9 cpk:w-9 cpk:rounded-full"],
chatInputToolbarIconLabel: [
"cpk:h-9 cpk:px-3 cpk:rounded-full",
"cpk:gap-2",
"cpk:font-normal"
]
}
},
defaultVariants: {
variant: "default",
size: "default"
}
});
const Button = React$1.forwardRef(function Button({ className, variant, size, asChild = false, ...props }, ref) {
return /* @__PURE__ */ jsx(asChild ? Slot : "button", {
ref,
"data-slot": "button",
className: cn(buttonVariants({
variant,
size,
className
})),
...props
});
});
//#endregion
//#region src/v2/components/ui/tooltip.tsx
function TooltipProvider({ delayDuration = 0, ...props }) {
return /* @__PURE__ */ jsx(TooltipPrimitive.Provider, {
"data-slot": "tooltip-provider",
delayDuration,
...props
});
}
function Tooltip({ ...props }) {
return /* @__PURE__ */ jsx(TooltipProvider, { children: /* @__PURE__ */ jsx(TooltipPrimitive.Root, {
"data-slot": "tooltip",
...props
}) });
}
function TooltipTrigger({ ...props }) {
return /* @__PURE__ */ jsx(TooltipPrimitive.Trigger, {
"data-slot": "tooltip-trigger",
...props
});
}
function TooltipContent({ className, sideOffset = 0, children, ...props }) {
return /* @__PURE__ */ jsx(TooltipPrimitive.Portal, { children: /* @__PURE__ */ jsxs(TooltipPrimitive.Content, {
"data-copilotkit": true,
"data-slot": "tooltip-content",
sideOffset,
className: cn("cpk:bg-primary cpk:text-primary-foreground cpk:animate-in cpk:fade-in-0 cpk:zoom-in-95 cpk:data-[state=closed]:animate-out cpk:data-[state=closed]:fade-out-0 cpk:data-[state=closed]:zoom-out-95 cpk:data-[side=bottom]:slide-in-from-top-2 cpk:data-[side=left]:slide-in-from-right-2 cpk:data-[side=right]:slide-in-from-left-2 cpk:data-[side=top]:slide-in-from-bottom-2 cpk:z-50 cpk:w-fit cpk:origin-(--radix-tooltip-content-transform-origin) cpk:rounded-md cpk:px-3 cpk:py-1.5 cpk:text-xs cpk:text-balance", className),
...props,
children: [children, /* @__PURE__ */ jsx(TooltipPrimitive.Arrow, { className: "cpk:bg-primary cpk:fill-primary cpk:z-50 cpk:size-2.5 cpk:translate-y-[calc(-50%_-_2px)] cpk:rotate-45 cpk:rounded-[2px]" })]
}) });
}
//#endregion
//#region src/v2/components/ui/dropdown-menu.tsx
function DropdownMenu({ ...props }) {
return /* @__PURE__ */ jsx(DropdownMenuPrimitive.Root, {
"data-slot": "dropdown-menu",
...props
});
}
function DropdownMenuTrigger({ ...props }) {
return /* @__PURE__ */ jsx(DropdownMenuPrimitive.Trigger, {
"data-slot": "dropdown-menu-trigger",
...props
});
}
function DropdownMenuContent({ className, sideOffset = 4, ...props }) {
return /* @__PURE__ */ jsx(DropdownMenuPrimitive.Portal, { children: /* @__PURE__ */ jsx(DropdownMenuPrimitive.Content, {
"data-copilotkit": true,
"data-slot": "dropdown-menu-content",
sideOffset,
className: cn("cpk:bg-popover cpk:text-popover-foreground cpk:data-[state=open]:animate-in cpk:data-[state=closed]:animate-out cpk:data-[state=closed]:fade-out-0 cpk:data-[state=open]:fade-in-0 cpk:data-[state=closed]:zoom-out-95 cpk:data-[state=open]:zoom-in-95 cpk:data-[side=bottom]:slide-in-from-top-2 cpk:data-[side=left]:slide-in-from-right-2 cpk:data-[side=right]:slide-in-from-left-2 cpk:data-[side=top]:slide-in-from-bottom-2 cpk:z-50 cpk:max-h-(--radix-dropdown-menu-content-available-height) cpk:min-w-[8rem] cpk:origin-(--radix-dropdown-menu-content-transform-origin) cpk:overflow-x-hidden cpk:overflow-y-auto cpk:rounded-md cpk:border cpk:p-1 cpk:shadow-md", className),
...props
}) });
}
function DropdownMenuItem({ className, inset, variant = "default", ...props }) {
return /* @__PURE__ */ jsx(DropdownMenuPrimitive.Item, {
"data-slot": "dropdown-menu-item",
"data-inset": inset,
"data-variant": variant,
className: cn("cpk:focus:bg-accent cpk:focus:text-accent-foreground cpk:data-[variant=destructive]:text-destructive cpk:data-[variant=destructive]:focus:bg-destructive/10 cpk:dark:data-[variant=destructive]:focus:bg-destructive/20 cpk:data-[variant=destructive]:focus:text-destructive cpk:data-[variant=destructive]:*:[svg]:!text-destructive cpk:[&_svg:not([class*='text-'])]:text-muted-foreground cpk:relative cpk:flex cpk:cursor-default cpk:items-center cpk:gap-2 cpk:rounded-sm cpk:px-2 cpk:py-1.5 cpk:text-sm cpk:outline-hidden cpk:select-none cpk:data-[disabled]:pointer-events-none cpk:data-[disabled]:opacity-50 cpk:data-[inset]:pl-8 cpk:[&_svg]:pointer-events-none cpk:[&_svg]:shrink-0 cpk:[&_svg:not([class*='size-'])]:size-4", className),
...props
});
}
function DropdownMenuSeparator({ className, ...props }) {
return /* @__PURE__ */ jsx(DropdownMenuPrimitive.Separator, {
"data-slot": "dropdown-menu-separator",
className: cn("cpk:bg-border cpk:-mx-1 cpk:my-1 cpk:h-px", className),
...props
});
}
function DropdownMenuSub({ ...props }) {
return /* @__PURE__ */ jsx(DropdownMenuPrimitive.Sub, {
"data-slot": "dropdown-menu-sub",
...props
});
}
function DropdownMenuSubTrigger({ className, inset, children, ...props }) {
return /* @__PURE__ */ jsxs(DropdownMenuPrimitive.SubTrigger, {
"data-slot": "dropdown-menu-sub-trigger",
"data-inset": inset,
className: cn("cpk:focus:bg-accent cpk:focus:text-accent-foreground cpk:data-[state=open]:bg-accent cpk:data-[state=open]:text-accent-foreground cpk:flex cpk:cursor-default cpk:items-center cpk:rounded-sm cpk:px-2 cpk:py-1.5 cpk:text-sm cpk:outline-hidden cpk:select-none cpk:data-[inset]:pl-8", className),
...props,
children: [children, /* @__PURE__ */ jsx(ChevronRightIcon, { className: "cpk:ml-auto cpk:size-4" })]
});
}
function DropdownMenuSubContent({ className, ...props }) {
return /* @__PURE__ */ jsx(DropdownMenuPrimitive.SubContent, {
"data-slot": "dropdown-menu-sub-content",
className: cn("cpk:bg-popover cpk:text-popover-foreground cpk:data-[state=open]:animate-in cpk:data-[state=closed]:animate-out cpk:data-[state=closed]:fade-out-0 cpk:data-[state=open]:fade-in-0 cpk:data-[state=closed]:zoom-out-95 cpk:data-[state=open]:zoom-in-95 cpk:data-[side=bottom]:slide-in-from-top-2 cpk:data-[side=left]:slide-in-from-right-2 cpk:data-[side=right]:slide-in-from-left-2 cpk:data-[side=top]:slide-in-from-bottom-2 cpk:z-50 cpk:min-w-[8rem] cpk:origin-(--radix-dropdown-menu-content-transform-origin) cpk:overflow-hidden cpk:rounded-md cpk:border cpk:p-1 cpk:shadow-lg", className),
...props
});
}
//#endregion
//#region src/v2/components/chat/CopilotChatAudioRecorder.tsx
/** Error subclass so callers can `instanceof`-guard recorder failures */
var AudioRecorderError = class extends Error {
constructor(message) {
super(message);
this.name = "AudioRecorderError";
}
};
const CopilotChatAudioRecorder = forwardRef((props, ref) => {
const { className, ...divProps } = props;
const canvasRef = useRef(null);
const [recorderState, setRecorderState] = useState("idle");
const mediaRecorderRef = useRef(null);
const audioChunksRef = useRef([]);
const streamRef = useRef(null);
const analyserRef = useRef(null);
const audioContextRef = useRef(null);
const animationIdRef = useRef(null);
const amplitudeHistoryRef = useRef([]);
const frameCountRef = useRef(0);
const scrollOffsetRef = useRef(0);
const smoothedAmplitudeRef = useRef(0);
const fadeOpacityRef = useRef(0);
const cleanup = useCallback(() => {
if (animationIdRef.current) {
cancelAnimationFrame(animationIdRef.current);
animationIdRef.current = null;
}
if (mediaRecorderRef.current && mediaRecorderRef.current.state !== "inactive") try {
mediaRecorderRef.current.stop();
} catch {}
if (streamRef.current) {
streamRef.current.getTracks().forEach((track) => track.stop());
streamRef.current = null;
}
if (audioContextRef.current && audioContextRef.current.state !== "closed") {
audioContextRef.current.close().catch(() => {});
audioContextRef.current = null;
}
mediaRecorderRef.current = null;
analyserRef.current = null;
audioChunksRef.current = [];
amplitudeHistoryRef.current = [];
frameCountRef.current = 0;
scrollOffsetRef.current = 0;
smoothedAmplitudeRef.current = 0;
fadeOpacityRef.current = 0;
}, []);
const start = useCallback(async () => {
if (recorderState !== "idle") throw new AudioRecorderError("Recorder is already active");
try {
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
streamRef.current = stream;
const audioContext = new AudioContext();
audioContextRef.current = audioContext;
const source = audioContext.createMediaStreamSource(stream);
const analyser = audioContext.createAnalyser();
analyser.fftSize = 2048;
source.connect(analyser);
analyserRef.current = analyser;
const mimeType = MediaRecorder.isTypeSupported("audio/webm;codecs=opus") ? "audio/webm;codecs=opus" : MediaRecorder.isTypeSupported("audio/webm") ? "audio/webm" : MediaRecorder.isTypeSupported("audio/mp4") ? "audio/mp4" : "";
const options = mimeType ? { mimeType } : {};
const mediaRecorder = new MediaRecorder(stream, options);
mediaRecorderRef.current = mediaRecorder;
audioChunksRef.current = [];
mediaRecorder.ondataavailable = (event) => {
if (event.data.size > 0) audioChunksRef.current.push(event.data);
};
mediaRecorder.start(100);
setRecorderState("recording");
} catch (error) {
cleanup();
if (error instanceof Error && error.name === "NotAllowedError") throw new AudioRecorderError("Microphone permission denied");
if (error instanceof Error && error.name === "NotFoundError") throw new AudioRecorderError("No microphone found");
throw new AudioRecorderError(error instanceof Error ? error.message : "Failed to start recording");
}
}, [recorderState, cleanup]);
const stop = useCallback(() => {
return new Promise((resolve, reject) => {
const mediaRecorder = mediaRecorderRef.current;
if (!mediaRecorder || recorderState !== "recording") {
reject(new AudioRecorderError("No active recording"));
return;
}
setRecorderState("processing");
mediaRecorder.onstop = () => {
const mimeType = mediaRecorder.mimeType || "audio/webm";
const audioBlob = new Blob(audioChunksRef.current, { type: mimeType });
cleanup();
setRecorderState("idle");
resolve(audioBlob);
};
mediaRecorder.onerror = () => {
cleanup();
setRecorderState("idle");
reject(new AudioRecorderError("Recording failed"));
};
mediaRecorder.stop();
});
}, [recorderState, cleanup]);
const calculateAmplitude = (dataArray) => {
let sum = 0;
for (let i = 0; i < dataArray.length; i++) {
const sample = (dataArray[i] ?? 128) / 128 - 1;
sum += sample * sample;
}
return Math.sqrt(sum / dataArray.length);
};
useEffect(() => {
const canvas = canvasRef.current;
if (!canvas) return;
const ctx = canvas.getContext("2d");
if (!ctx) return;
const barWidth = 2;
const barSpacing = barWidth + 1;
const scrollSpeed = 1 / 3;
const draw = () => {
const rect = canvas.getBoundingClientRect();
const dpr = window.devicePixelRatio || 1;
if (canvas.width !== rect.width * dpr || canvas.height !== rect.height * dpr) {
canvas.width = rect.width * dpr;
canvas.height = rect.height * dpr;
ctx.scale(dpr, dpr);
}
const maxBars = Math.floor(rect.width / barSpacing) + 2;
if (analyserRef.current && recorderState === "recording") {
if (amplitudeHistoryRef.current.length === 0) amplitudeHistoryRef.current = new Array(maxBars).fill(0);
if (fadeOpacityRef.current < 1) fadeOpacityRef.current = Math.min(1, fadeOpacityRef.current + .03);
scrollOffsetRef.current += scrollSpeed;
const bufferLength = analyserRef.current.fftSize;
const dataArray = new Uint8Array(bufferLength);
analyserRef.current.getByteTimeDomainData(dataArray);
const rawAmplitude = calculateAmplitude(dataArray);
const speed = rawAmplitude > smoothedAmplitudeRef.current ? .12 : .08;
smoothedAmplitudeRef.current += (rawAmplitude - smoothedAmplitudeRef.current) * speed;
if (scrollOffsetRef.current >= barSpacing) {
scrollOffsetRef.current -= barSpacing;
amplitudeHistoryRef.current.push(smoothedAmplitudeRef.current);
if (amplitudeHistoryRef.current.length > maxBars) amplitudeHistoryRef.current = amplitudeHistoryRef.current.slice(-maxBars);
}
}
ctx.clearRect(0, 0, rect.width, rect.height);
ctx.fillStyle = getComputedStyle(canvas).color;
ctx.globalAlpha = fadeOpacityRef.current;
const centerY = rect.height / 2;
const maxAmplitude = rect.height / 2 - 2;
const history = amplitudeHistoryRef.current;
if (history.length > 0) {
const offset = scrollOffsetRef.current;
const edgeFadeWidth = 12;
for (let i = 0; i < history.length; i++) {
const amplitude = history[i] ?? 0;
const scaledAmplitude = Math.min(amplitude * 4, 1);
const barHeight = Math.max(2, scaledAmplitude * maxAmplitude * 2);
const x = rect.width - (history.length - i) * barSpacing - offset;
const y = centerY - barHeight / 2;
if (x + barWidth > 0 && x < rect.width) {
let edgeOpacity = 1;
if (x < edgeFadeWidth) edgeOpacity = Math.max(0, x / edgeFadeWidth);
else if (x > rect.width - edgeFadeWidth) edgeOpacity = Math.max(0, (rect.width - x) / edgeFadeWidth);
ctx.globalAlpha = fadeOpacityRef.current * edgeOpacity;
ctx.fillRect(x, y, barWidth, barHeight);
}
}
}
animationIdRef.current = requestAnimationFrame(draw);
};
draw();
return () => {
if (animationIdRef.current) cancelAnimationFrame(animationIdRef.current);
};
}, [recorderState]);
useEffect(() => {
return cleanup;
}, [cleanup]);
useImperativeHandle(ref, () => ({
get state() {
return recorderState;
},
start,
stop,
dispose: cleanup
}), [
recorderState,
start,
stop,
cleanup
]);
return /* @__PURE__ */ jsx("div", {
className: twMerge("cpk:w-full cpk:py-3 cpk:px-5", className),
...divProps,
children: /* @__PURE__ */ jsx("canvas", {
ref: canvasRef,
className: "cpk:block cpk:w-full cpk:h-[26px]"
})
});
});
CopilotChatAudioRecorder.displayName = "CopilotChatAudioRecorder";
//#endregion
//#region src/v2/components/chat/CopilotChatInput.tsx
const SLASH_MENU_MAX_VISIBLE_ITEMS = 5;
const SLASH_MENU_ITEM_HEIGHT_PX = 40;
function CopilotChatInput({ mode = "input", onSubmitMessage, onStop, isRunning = false, onStartTranscribe, onCancelTranscribe, onFinishTranscribe, onFinishTranscribeWithAudio, onAddFile, onChange, value, toolsMenu, autoFocus = false, positioning = "static", keyboardHeight = 0, containerRef, showDisclaimer, bottomAnchored = false, textArea, sendButton, startTranscribeButton, cancelTranscribeButton, finishTranscribeButton, addMenuButton, audioRecorder, disclaimer, children, className, ...props }) {
const isControlled = value !== void 0;
const [internalValue, setInternalValue] = useState(() => value ?? "");
useEffect(() => {
if (!isControlled && value !== void 0) setInternalValue(value);
}, [isControlled, value]);
const resolvedValue = isControlled ? value ?? "" : internalValue;
const [layout, setLayout] = useState("compact");
const ignoreResizeRef = useRef(false);
const resizeEvaluationRafRef = useRef(null);
const isExpanded = mode === "input" && layout === "expanded";
const [commandQuery, setCommandQuery] = useState(null);
const [slashHighlightIndex, setSlashHighlightIndex] = useState(0);
const inputRef = useRef(null);
const gridRef = useRef(null);
const addButtonContainerRef = useRef(null);
const actionsContainerRef = useRef(null);
const audioRecorderRef = useRef(null);
const slashMenuRef = useRef(null);
const config = useCopilotChatConfiguration();
const labels = config?.labels ?? CopilotChatDefaultLabels;
const previousModalStateRef = useRef(void 0);
const measurementCanvasRef = useRef(null);
const measurementsRef = useRef({
singleLineHeight: 0,
maxHeight: 0,
paddingLeft: 0,
paddingRight: 0
});
const containerCacheRef = useRef(null);
const commandItems = useMemo(() => {
const entries = [];
const seen = /* @__PURE__ */ new Set();
const pushItem = (item) => {
if (item === "-") return;
if (item.items && item.items.length > 0) {
for (const nested of item.items) pushItem(nested);
return;
}
if (!seen.has(item.label)) {
seen.add(item.label);
entries.push(item);
}
};
if (onAddFile) pushItem({
label: labels.chatInputToolbarAddButtonLabel,
action: onAddFile
});
if (toolsMenu && toolsMenu.length > 0) for (const item of toolsMenu) pushItem(item);
return entries;
}, [
labels.chatInputToolbarAddButtonLabel,
onAddFile,
toolsMenu
]);
const filteredCommands = useMemo(() => {
if (commandQuery === null) return [];
if (commandItems.length === 0) return [];
const query = commandQuery.trim().toLowerCase();
if (query.length === 0) return commandItems;
const startsWith = [];
const contains = [];
for (const item of commandItems) {
const label = item.label.toLowerCase();
if (label.startsWith(query)) startsWith.push(item);
else if (label.includes(query)) contains.push(item);
}
return [...startsWith, ...contains];
}, [commandItems, commandQuery]);
useEffect(() => {
if (!autoFocus) {
previousModalStateRef.current = config?.isModalOpen;
return;
}
if (config?.isModalOpen && !previousModalStateRef.current) inputRef.current?.focus({ preventScroll: true });
previousModalStateRef.current = config?.isModalOpen;
}, [config?.isModalOpen, autoFocus]);
useEffect(() => {
if (commandItems.length === 0 && commandQuery !== null) setCommandQuery(null);
}, [commandItems.length, commandQuery]);
const previousCommandQueryRef = useRef(null);
useEffect(() => {
if (commandQuery !== null && commandQuery !== previousCommandQueryRef.current && filteredCommands.length > 0) setSlashHighlightIndex(0);
previousCommandQueryRef.current = commandQuery;
}, [commandQuery, filteredCommands.length]);
useEffect(() => {
if (commandQuery === null) {
setSlashHighlightIndex(0);
return;
}
if (filteredCommands.length === 0) setSlashHighlightIndex(-1);
else if (slashHighlightIndex < 0 || slashHighlightIndex >= filteredCommands.length) setSlashHighlightIndex(0);
}, [
commandQuery,
filteredCommands,
slashHighlightIndex
]);
useEffect(() => {
const recorder = audioRecorderRef.current;
if (!recorder) return;
if (mode === "transcribe") recorder.start().catch(console.error);
else if (recorder.state === "recording") recorder.stop().catch(console.error);
}, [mode]);
useEffect(() => {
if (mode !== "input") {
setLayout("compact");
setCommandQuery(null);
}
}, [mode]);
const updateSlashState = useCallback((value) => {
if (commandItems.length === 0) {
setCommandQuery((prev) => prev === null ? prev : null);
return;
}
if (value.startsWith("/")) {
const query = (value.split(/\r?\n/, 1)[0] ?? "").slice(1);
setCommandQuery((prev) => prev === query ? prev : query);
} else setCommandQuery((prev) => prev === null ? prev : null);
}, [commandItems.length]);
useEffect(() => {
updateSlashState(resolvedValue);
}, [resolvedValue, updateSlashState]);
const handleChange = (e) => {
const nextValue = e.target.value;
if (!isControlled) setInternalValue(nextValue);
onChange?.(nextValue);
updateSlashState(nextValue);
};
const clearInputValue = useCallback(() => {
if (!isControlled) setInternalValue("");
if (onChange) onChange("");
}, [isControlled, onChange]);
const runCommand = useCallback((item) => {
clearInputValue();
item.action?.();
setCommandQuery(null);
setSlashHighlightIndex(0);
requestAnimationFrame(() => {
inputRef.current?.focus();
});
}, [clearInputValue]);
const handleKeyDown = (e) => {
if (e.nativeEvent.isComposing || e.keyCode === 229) return;
if (commandQuery !== null && mode === "input") {
if (e.key === "ArrowDown") {
if (filteredCommands.length > 0) {
e.preventDefault();
setSlashHighlightIndex((prev) => {
if (filteredCommands.length === 0) return prev;
return prev === -1 ? 0 : (prev + 1) % filteredCommands.length;
});
}
return;
}
if (e.key === "ArrowUp") {
if (filteredCommands.length > 0) {
e.preventDefault();
setSlashHighlightIndex((prev) => {
if (filteredCommands.length === 0) return prev;
if (prev === -1) return filteredCommands.length - 1;
return prev <= 0 ? filteredCommands.length - 1 : prev - 1;
});
}
return;
}
if (e.key === "Enter") {
const selected = slashHighlightIndex >= 0 ? filteredCommands[slashHighlightIndex] : void 0;
if (selected) {
e.preventDefault();
runCommand(selected);
return;
}
}
if (e.key === "Escape") {
e.preventDefault();
setCommandQuery(null);
return;
}
}
if (e.key === "Enter" && !e.shiftKey) {
e.preventDefault();
if (isProcessing) onStop?.();
else send();
}
};
const send = () => {
if (!onSubmitMessage) return;
const trimmed = resolvedValue.trim();
if (!trimmed) return;
onSubmitMessage(trimmed);
if (!isControlled) setInternalValue("");
onChange?.("");
if (inputRef.current) inputRef.current.focus();
};
const BoundTextArea = renderSlot(textArea, CopilotChatInput.TextArea, {
ref: inputRef,
value: resolvedValue,
onChange: handleChange,
onKeyDown: handleKeyDown,
onCompositionStart: () => {
isComposingRef.current = true;
},
onCompositionEnd: () => {
isComposingRef.current = false;
},
autoFocus,
className: twMerge("cpk:w-full cpk:py-3", isExpanded ? "cpk:px-5" : "cpk:pr-5")
});
const isProcessing = mode !== "transcribe" && isRunning;
const canSend = resolvedValue.trim().length > 0 && !!onSubmitMessage;
const canStop = !!onStop;
const handleSendButtonClick = () => {
if (isProcessing) {
onStop?.();
return;
}
send();
};
const BoundAudioRecorder = renderSlot(audioRecorder, CopilotChatAudioRecorder, { ref: audioRecorderRef });
const BoundSendButton = renderSlot(sendButton, CopilotChatInput.SendButton, {
onClick: handleSendButtonClick,
disabled: isProcessing ? !canStop : !canSend,
children: isProcessing && canStop ? /* @__PURE__ */ jsx(Square, { className: "cpk:size-[18px] cpk:fill-current" }) : void 0
});
const BoundStartTranscribeButton = renderSlot(startTranscribeButton, CopilotChatInput.StartTranscribeButton, { onClick: onStartTranscribe });
const BoundCancelTranscribeButton = renderSlot(cancelTranscribeButton, CopilotChatInput.CancelTranscribeButton, { onClick: onCancelTranscribe });
const handleFinishTranscribe = useCallback(async () => {
const recorder = audioRecorderRef.current;
if (recorder && recorder.state === "recording") try {
const audioBlob = await recorder.stop();
if (onFinishTranscribeWithAudio) await onFinishTranscribeWithAudio(audioBlob);
} catch (error) {
console.error("Failed to stop recording:", error);
}
onFinishTranscribe?.();
}, [onFinishTranscribe, onFinishTranscribeWithAudio]);
const BoundFinishTranscribeButton = renderSlot(finishTranscribeButton, CopilotChatInput.FinishTranscribeButton, { onClick: handleFinishTranscribe });
const BoundAddMenuButton = renderSlot(addMenuButton, CopilotChatInput.AddMenuButton, {
disabled: mode === "transcribe",
onAddFile,
toolsMenu
});
const BoundDisclaimer = renderSlot(disclaimer, CopilotChatInput.Disclaimer, {});
const shouldShowDisclaimer = showDisclaimer ?? positioning === "absolute";
if (children) {
const childProps = {
textArea: BoundTextArea,
audioRecorder: BoundAudioRecorder,
sendButton: BoundSendButton,
startTranscribeButton: BoundStartTranscribeButton,
cancelTranscribeButton: BoundCancelTranscribeButton,
finishTranscribeButton: BoundFinishTranscribeButton,
addMenuButton: BoundAddMenuButton,
disclaimer: BoundDisclaimer,
onSubmitMessage,
onStop,
isRunning,
onStartTranscribe,
onCancelTranscribe,
onFinishTranscribe,
onAddFile,
mode,
toolsMenu,
autoFocus,
positioning,
keyboardHeight,
showDisclaimer: shouldShowDisclaimer,
containerRef
};
return /* @__PURE__ */ jsx("div", {
"data-copilotkit": true,
style: { display: "contents" },
children: children(childProps)
});
}
const handleContainerClick = (e) => {
const target = e.target;
if (target.tagName !== "BUTTON" && !target.closest("button") && inputRef.current && mode === "input") inputRef.current.focus();
};
const isComposingRef = useRef(false);
const ensureMeasurements = useCallback(() => {
const textarea = inputRef.current;
if (!textarea || isComposingRef.current) return;
const previousValue = textarea.value;
const previousHeight = textarea.style.height;
textarea.style.height = "auto";
const computedStyle = window.getComputedStyle(textarea);
const paddingLeft = parseFloat(computedStyle.paddingLeft) || 0;
const paddingRight = parseFloat(computedStyle.paddingRight) || 0;
const paddingTop = parseFloat(computedStyle.paddingTop) || 0;
const paddingBottom = parseFloat(computedStyle.paddingBottom) || 0;
textarea.value = "";
const singleLineHeight = textarea.scrollHeight;
textarea.value = previousValue;
const maxHeight = (singleLineHeight - paddingTop - paddingBottom) * 5 + paddingTop + paddingBottom;
measurementsRef.current = {
singleLineHeight,
maxHeight,
paddingLeft,
paddingRight
};
textarea.style.height = previousHeight;
textarea.style.maxHeight = `${maxHeight}px`;
}, []);
const adjustTextareaHeight = useCallback(() => {
const textarea = inputRef.current;
if (!textarea) return 0;
if (measurementsRef.current.singleLineHeight === 0) ensureMeasurements();
const { maxHeight } = measurementsRef.current;
if (maxHeight) textarea.style.maxHeight = `${maxHeight}px`;
textarea.style.height = "auto";
const scrollHeight = textarea.scrollHeight;
if (maxHeight) textarea.style.height = `${Math.min(scrollHeight, maxHeight)}px`;
else textarea.style.height = `${scrollHeight}px`;
return scrollHeight;
}, [ensureMeasurements]);
const updateLayout = useCallback((nextLayout) => {
setLayout((prev) => {
if (prev === nextLayout) return prev;
ignoreResizeRef.current = true;
return nextLayout;
});
}, []);
const updateContainerCache = useCallback(() => {
const grid = gridRef.current;
const addContainer = addButtonContainerRef.current;
const actionsContainer = actionsContainerRef.current;
if (!grid || !addContainer || !actionsContainer) return null;
const gridStyles = window.getComputedStyle(grid);
const paddingLeft = parseFloat(gridStyles.paddingLeft) || 0;
const paddingRight = parseFloat(gridStyles.paddingRight) || 0;
const columnGap = parseFloat(gridStyles.columnGap) || 0;
const gridAvailableWidth = grid.clientWidth - paddingLeft - paddingRight;
if (gridAvailableWidth <= 0) return null;
const addWidth = addContainer.getBoundingClientRect().width;
const actionsWidth = actionsContainer.getBoundingClientRect().width;
const compactWidth = Math.max(gridAvailableWidth - addWidth - actionsWidth - columnGap * 2, 0);
if (compactWidth <= 0) return null;
const result = { compactWidth };
containerCacheRef.current = result;
return result;
}, []);
const evaluateLayout = useCallback(() => {
if (mode !== "input") {
updateLayout("compact");
return;
}
if (typeof window !== "undefined" && typeof window.matchMedia === "function") {
if (window.matchMedia("(max-width: 767px)").matches) {
ensureMeasurements();
adjustTextareaHeight();
updateLayout("expanded");
return;
}
}
const textarea = inputRef.current;
const grid = gridRef.current;
const addContainer = addButtonContainerRef.current;
const actionsContainer = actionsContainerRef.current;
if (!textarea || !grid || !addContainer || !actionsContainer) return;
if (measurementsRef.current.singleLineHeight === 0) ensureMeasurements();
const scrollHeight = adjustTextareaHeight();
const baseline = measurementsRef.current.singleLineHeight;
const hasExplicitBreak = resolvedValue.includes("\n");
const renderedMultiline = baseline > 0 ? scrollHeight > baseline + 1 : false;
let shouldExpand = hasExplicitBreak || renderedMultiline;
if (!shouldExpand) {
const cache = containerCacheRef.current ?? updateContainerCache();
if (cache && cache.compactWidth > 0) {
const compactInnerWidth = Math.max(cache.compactWidth - (measurementsRef.current.paddingLeft || 0) - (measurementsRef.current.paddingRight || 0), 0);
if (compactInnerWidth > 0) {
const textareaStyles = window.getComputedStyle(textarea);
let font = textareaStyles.font;
if (!font) {
const { fontStyle, fontVariant, fontWeight, fontSize, lineHeight, fontFamily } = textareaStyles;
if (fontSize && fontFamily) font = `${fontStyle} ${fontVariant} ${fontWeight} ${fontSize}/${lineHeight} ${fontFamily}`;
}
if (font?.trim()) {
const canvas = measurementCanvasRef.current ?? document.createElement("canvas");
if (!measurementCanvasRef.current) measurementCanvasRef.current = canvas;
const context = canvas.getContext("2d");
if (context) {
context.font = font;
const lines = resolvedValue.length > 0 ? resolvedValue.split("\n") : [""];
let longestWidth = 0;
for (const line of lines) {
const metrics = context.measureText(line || " ");
if (metrics.width > longestWidth) longestWidth = metrics.width;
}
if (longestWidth > compactInnerWidth) shouldExpand = true;
} else if (process.env.NODE_ENV !== "production") console.warn("[CopilotChatInput] canvas.getContext('2d') returned null. Text-width-based expansion will be unavailable.");
} else if (process.env.NODE_ENV !== "production") console.warn("[CopilotChatInput] Could not resolve textarea font for layout measurement. Text-width-based expansion will be skipped until the next evaluation.");
}
}
}
updateLayout(shouldExpand ? "expanded" : "compact");
}, [
adjustTextareaHeight,
ensureMeasurements,
mode,
resolvedValue,
updateContainerCache,
updateLayout
]);
useLayoutEffect(() => {
evaluateLayout();
}, [evaluateLayout]);
useEffect(() => {
if (typeof ResizeObserver === "undefined") return;
const textarea = inputRef.current;
const grid = gridRef.current;
const addContainer = addButtonContainerRef.current;
const actionsContainer = actionsContainerRef.current;
if (!textarea || !grid || !addContainer || !actionsContainer) return;
const containerTargets = new Set([
grid,
addContainer,
actionsContainer
]);
const scheduleEvaluation = (invalidateCache) => {
if (ignoreResizeRef.current) {
ignoreResizeRef.current = false;
return;
}
if (invalidateCache) containerCacheRef.current = null;
if (typeof window === "undefined") {
evaluateLayout();
return;
}
if (resizeEvaluationRafRef.current !== null) cancelAnimationFrame(resizeEvaluationRafRef.current);
resizeEvaluationRafRef.current = window.requestAnimationFrame(() => {
resizeEvaluationRafRef.current = null;
evaluateLayout();
});
};
const observer = new ResizeObserver((entries) => {
let shouldInvalidate = false;
for (const entry of entries) if (containerTargets.has(entry.target)) {
shouldInvalidate = true;
break;
}
scheduleEvaluation(shouldInvalidate);
});
observer.observe(grid);
observer.observe(addContainer);
observer.observe(actionsContainer);
observer.observe(textarea);
return () => {
observer.disconnect();
if (typeof window !== "undefined" && resizeEvaluationRafRef.current !== null) {
cancelAnimationFrame(resizeEvaluationRafRef.current);
resizeEvaluationRafRef.current = null;
}
};
}, [evaluateLayout]);
const slashMenuVisible = commandQuery !== null && commandItems.length > 0;
useEffect(() => {
if (!slashMenuVisible || slashHighlightIndex < 0) return;
(slashMenuRef.current?.querySelector(`[data-slash-index="${slashHighlightIndex}"]`))?.scrollIntoView({ block: "nearest" });
}, [slashMenuVisible, slashHighlightIndex]);
const slashMenu = slashMenuVisible ? /* @__PURE__ */ jsx("div", {
"data-testid": "copilot-slash-menu",
role: "listbox",
"aria-label": "Slash commands",
ref: slashMenuRef,
className: "cpk:absolute cpk:bottom-full cpk:left-0 cpk:right-0 cpk:z-30 cpk:mb-2 cpk:max-h-64 cpk:overflow-y-auto cpk:rounded-lg cpk:border cpk:border-border cpk:bg-white cpk:shadow-lg cpk:dark:border-[#3a3a3a] cpk:dark:bg-[#1f1f1f]",
style: { maxHeight: `${SLASH_MENU_MAX_VISIBLE_ITEMS * SLASH_MENU_ITEM_HEIGHT_PX}px` },
children: filteredCommands.length === 0 ? /* @__PURE__ */ jsx("div", {
className: "cpk:px-3 cpk:py-2 cpk:text-sm cpk:text-muted-foreground",
children: "No commands found"
}) : filteredCommands.map((item, index) => {
const isActive = index === slashHighlightIndex;
return /* @__PURE__ */ jsx("button", {
type: "button",
role: "option",
"aria-selected": isActive,
"data-active": isActive ? "true" : void 0,
"data-slash-index": index,
className: twMerge("cpk:w-full cpk:px-3 cpk:py-2 cpk:text-left cpk:text-sm cpk:transition-colors", "cpk:hover:bg-muted cpk:dark:hover:bg-[#2f2f2f]", isActive ? "cpk:bg-muted cpk:dark:bg-[#2f2f2f]" : "cpk:bg-transparent"),
onMouseEnter: () => setSlashHighlightIndex(index),
onMouseDown: (event) => {
event.preventDefault();
runCommand(item);
},
children: item.label
}, `${item.label}-${index}`);
})
}) : null;
const inputPill = /* @__PURE__ */ jsx("div", {
"data-testid": "copilot-chat-input",
className: twMerge("copilotKitInput", "cpk:flex cpk:w-full cpk:flex-col cpk:items-center cpk:justify-center", "cpk:cursor-text", "cpk:overflow-visible cpk:bg-clip-padding cpk:contain-inline-size", "cpk:bg-white cpk:dark:bg-[#303030]", "cpk:shadow-[0_4px_4px_0_#0000000a,0_0_1px_0_#0000009e] cpk:rounded-[28px]"),
onClick: handleContainerClick,
"data-layout": isExpanded ? "expanded" : "compact",
children: /* @__PURE__ */ jsxs("div", {
ref: gridRef,
className: twMerge("cpk:grid cpk:w-full cpk:gap-x-3 cpk:gap-y-3 cpk:px-3 cpk:py-2", isExpanded ? "cpk:grid-cols-[auto_minmax(0,1fr)_auto] cpk:grid-rows-[auto_auto]" : "cpk:grid-cols-[auto_minmax(0,1fr)_auto] cpk:items-center"),
"data-layout": isExpanded ? "expanded" : "compact",
children: [
/* @__PURE__ */ jsx("div", {
ref: addButtonContainerRef,
className: twMerge("cpk:flex cpk:items-center", isExpanded ? "cpk:row-start-2" : "cpk:row-start-1", "cpk:col-start-1"),
children: BoundAddMenuButton
}),
/* @__PURE__ */ jsx("div", {
className: twMerge("cpk:relative cpk:flex cpk:min-w-0 cpk:flex-col cpk:min-h-[50px] cpk:justify-center", isExpanded ? "cpk:col-span-3 cpk:row-start-1" : "cpk:col-start-2 cpk:row-start-1"),
children: mode === "transcribe" ? BoundAudioRecorder : mode === "processing" ? /* @__PURE__ */ jsx("div", {
className: "cpk:flex cpk:w-full cpk:items-center cpk:justify-center cpk:py-3 cpk:px-5",
children: /* @__PURE__ */ jsx(Loader2, { className: "cpk:size-[26px] cpk:animate-spin cpk:text-muted-foreground" })
}) : /* @__PURE__ */ jsxs(Fragment$1, { children: [BoundTextArea, slashMenu] })
}),
/* @__PURE__ */ jsx("div", {
ref: actionsContainerRef,
className: twMerge("cpk:flex cpk:items-center cpk:justify-end cpk:gap-2", isExpanded ? "cpk:col-start-3 cpk:row-start-2" : "cpk:col-start-3 cpk:row-start-1"),
children: mode === "transcribe" ? /* @__PURE__ */ jsxs(Fragment$1, { children: [onCancelTranscribe && BoundCancelTranscribeButton, onFinishTranscribe && BoundFinishTranscribeButton] }) : /* @__PURE__ */ jsxs(Fragment$1, { children: [onStartTranscribe && BoundStartTranscribeButton, BoundSendButton] })
})
]
})
});
return /* @__PURE__ */ jsxs("div", {
"data-copilotkit": true,
ref: containerRef,
className: cn("cpk:pointer-events-none cpk:relative cpk:z-20", positioning === "absolute" && "cpk:absolute cpk:bottom-0 cpk:left-0 cpk:right-0", className),
style: {
transform: keyboardHeight > 0 ? `translateY(-${keyboardHeight}px)` : void 0,
transition: "transform 0.2s ease-out",
...positioning === "absolute" || bottomAnchored ? { paddingBottom: "var(--copilotkit-license-banner-offset, 0px)" } : {}
},
...props,
children: [/* @__PURE__ */ jsx("div", {
className: "cpk:max-w-3xl cpk:mx-auto cpk:py-0 cpk:px-4 cpk:sm:px-0 cpk:[div[data-sidebar-chat]_&]:px-8 cpk:[div[data-popup-chat]_&]:px-4 cpk:pointer-events-auto",
children: inputPill
}), shouldShowDisclaimer && BoundDisclaimer]
});
}
(function(_CopilotChatInput) {
_CopilotChatInput.SendButton = ({ className, children, ...props }) => /* @__PURE__ */ jsx("div", {
className: "cpk:mr-[10px]",
children: /* @__PURE__ */ jsx(Button, {
type: "button",
"data-testid": "copilot-send-button",
variant: "chatInputToolbarPrimary",
size: "chatInputToolbarIcon",
className,
...props,
children: children ?? /* @__PURE__ */ jsx(ArrowUp, { className: "cpk:size-[18px]" })
})
});
const ToolbarButton = _CopilotChatInput.ToolbarButton = ({ icon, labelKey, defaultClassName, className, ...props }) => {
const labels = useCopilotChatConfiguration()?.labels ?? CopilotChatDefaultLabels;
return /* @__PURE__ */ jsxs(Tooltip, { children: [/* @