rdesign/frontend/node_modules/@copilotkitnext/react/dist/components/chat/CopilotChatInput.cjs

682 lines
31 KiB
JavaScript

const require_runtime = require('../../_virtual/_rolldown/runtime.cjs');
const require_CopilotChatConfigurationProvider = require('../../providers/CopilotChatConfigurationProvider.cjs');
const require_utils = require('../../lib/utils.cjs');
const require_button = require('../ui/button.cjs');
const require_tooltip = require('../ui/tooltip.cjs');
const require_dropdown_menu = require('../ui/dropdown-menu.cjs');
const require_CopilotChatAudioRecorder = require('./CopilotChatAudioRecorder.cjs');
const require_slots = require('../../lib/slots.cjs');
let react = require("react");
react = require_runtime.__toESM(react);
let tailwind_merge = require("tailwind-merge");
let lucide_react = require("lucide-react");
let react_jsx_runtime = require("react/jsx-runtime");
//#region src/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 = true, positioning = "static", keyboardHeight = 0, containerRef, showDisclaimer, textArea, sendButton, startTranscribeButton, cancelTranscribeButton, finishTranscribeButton, addMenuButton, audioRecorder, disclaimer, children, className, ...props }) {
const isControlled = value !== void 0;
const [internalValue, setInternalValue] = (0, react.useState)(() => value ?? "");
(0, react.useEffect)(() => {
if (!isControlled && value !== void 0) setInternalValue(value);
}, [isControlled, value]);
const resolvedValue = isControlled ? value ?? "" : internalValue;
const [layout, setLayout] = (0, react.useState)("compact");
const ignoreResizeRef = (0, react.useRef)(false);
const resizeEvaluationRafRef = (0, react.useRef)(null);
const isExpanded = mode === "input" && layout === "expanded";
const [commandQuery, setCommandQuery] = (0, react.useState)(null);
const [slashHighlightIndex, setSlashHighlightIndex] = (0, react.useState)(0);
const inputRef = (0, react.useRef)(null);
const gridRef = (0, react.useRef)(null);
const addButtonContainerRef = (0, react.useRef)(null);
const actionsContainerRef = (0, react.useRef)(null);
const audioRecorderRef = (0, react.useRef)(null);
const slashMenuRef = (0, react.useRef)(null);
const config = require_CopilotChatConfigurationProvider.useCopilotChatConfiguration();
const labels = config?.labels ?? require_CopilotChatConfigurationProvider.CopilotChatDefaultLabels;
const previousModalStateRef = (0, react.useRef)(void 0);
const measurementCanvasRef = (0, react.useRef)(null);
const measurementsRef = (0, react.useRef)({
singleLineHeight: 0,
maxHeight: 0,
paddingLeft: 0,
paddingRight: 0
});
const commandItems = (0, react.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 = (0, react.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]);
(0, react.useEffect)(() => {
if (!autoFocus) {
previousModalStateRef.current = config?.isModalOpen;
return;
}
if (config?.isModalOpen && !previousModalStateRef.current) inputRef.current?.focus();
previousModalStateRef.current = config?.isModalOpen;
}, [config?.isModalOpen, autoFocus]);
(0, react.useEffect)(() => {
if (commandItems.length === 0 && commandQuery !== null) setCommandQuery(null);
}, [commandItems.length, commandQuery]);
const previousCommandQueryRef = (0, react.useRef)(null);
(0, react.useEffect)(() => {
if (commandQuery !== null && commandQuery !== previousCommandQueryRef.current && filteredCommands.length > 0) setSlashHighlightIndex(0);
previousCommandQueryRef.current = commandQuery;
}, [commandQuery, filteredCommands.length]);
(0, react.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
]);
(0, react.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]);
(0, react.useEffect)(() => {
if (mode !== "input") {
setLayout("compact");
setCommandQuery(null);
}
}, [mode]);
const updateSlashState = (0, react.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]);
(0, react.useEffect)(() => {
updateSlashState(resolvedValue);
}, [resolvedValue, updateSlashState]);
const handleChange = (e) => {
const nextValue = e.target.value;
if (!isControlled) setInternalValue(nextValue);
onChange?.(nextValue);
updateSlashState(nextValue);
};
const clearInputValue = (0, react.useCallback)(() => {
if (!isControlled) setInternalValue("");
if (onChange) onChange("");
}, [isControlled, onChange]);
const runCommand = (0, react.useCallback)((item) => {
clearInputValue();
item.action?.();
setCommandQuery(null);
setSlashHighlightIndex(0);
requestAnimationFrame(() => {
inputRef.current?.focus();
});
}, [clearInputValue]);
const handleKeyDown = (e) => {
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 = require_slots.renderSlot(textArea, CopilotChatInput.TextArea, {
ref: inputRef,
value: resolvedValue,
onChange: handleChange,
onKeyDown: handleKeyDown,
autoFocus,
className: (0, tailwind_merge.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 = require_slots.renderSlot(audioRecorder, require_CopilotChatAudioRecorder.CopilotChatAudioRecorder, { ref: audioRecorderRef });
const BoundSendButton = require_slots.renderSlot(sendButton, CopilotChatInput.SendButton, {
onClick: handleSendButtonClick,
disabled: isProcessing ? !canStop : !canSend,
children: isProcessing && canStop ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Square, { className: "cpk:size-[18px] cpk:fill-current" }) : void 0
});
const BoundStartTranscribeButton = require_slots.renderSlot(startTranscribeButton, CopilotChatInput.StartTranscribeButton, { onClick: onStartTranscribe });
const BoundCancelTranscribeButton = require_slots.renderSlot(cancelTranscribeButton, CopilotChatInput.CancelTranscribeButton, { onClick: onCancelTranscribe });
const handleFinishTranscribe = (0, react.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 = require_slots.renderSlot(finishTranscribeButton, CopilotChatInput.FinishTranscribeButton, { onClick: handleFinishTranscribe });
const BoundAddMenuButton = require_slots.renderSlot(addMenuButton, CopilotChatInput.AddMenuButton, {
disabled: mode === "transcribe",
onAddFile,
toolsMenu
});
const BoundDisclaimer = require_slots.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
};
return /* @__PURE__ */ (0, react_jsx_runtime.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 ensureMeasurements = (0, react.useCallback)(() => {
const textarea = inputRef.current;
if (!textarea) 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 = (0, react.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 = (0, react.useCallback)((nextLayout) => {
setLayout((prev) => {
if (prev === nextLayout) return prev;
ignoreResizeRef.current = true;
return nextLayout;
});
}, []);
const evaluateLayout = (0, react.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 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) {
const addWidth = addContainer.getBoundingClientRect().width;
const actionsWidth = actionsContainer.getBoundingClientRect().width;
const compactWidth = Math.max(gridAvailableWidth - addWidth - actionsWidth - columnGap * 2, 0);
const canvas = measurementCanvasRef.current ?? document.createElement("canvas");
if (!measurementCanvasRef.current) measurementCanvasRef.current = canvas;
const context = canvas.getContext("2d");
if (context) {
const textareaStyles = window.getComputedStyle(textarea);
context.font = textareaStyles.font || `${textareaStyles.fontStyle} ${textareaStyles.fontVariant} ${textareaStyles.fontWeight} ${textareaStyles.fontSize}/${textareaStyles.lineHeight} ${textareaStyles.fontFamily}`;
const compactInnerWidth = Math.max(compactWidth - (measurementsRef.current.paddingLeft || 0) - (measurementsRef.current.paddingRight || 0), 0);
if (compactInnerWidth > 0) {
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;
}
}
}
}
updateLayout(shouldExpand ? "expanded" : "compact");
}, [
adjustTextareaHeight,
ensureMeasurements,
mode,
resolvedValue,
updateLayout
]);
(0, react.useLayoutEffect)(() => {
evaluateLayout();
}, [evaluateLayout]);
(0, react.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 scheduleEvaluation = () => {
if (ignoreResizeRef.current) {
ignoreResizeRef.current = false;
return;
}
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(() => {
scheduleEvaluation();
});
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;
(0, react.useEffect)(() => {
if (!slashMenuVisible || slashHighlightIndex < 0) return;
(slashMenuRef.current?.querySelector(`[data-slash-index="${slashHighlightIndex}"]`))?.scrollIntoView({ block: "nearest" });
}, [slashMenuVisible, slashHighlightIndex]);
const slashMenu = slashMenuVisible ? /* @__PURE__ */ (0, react_jsx_runtime.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__ */ (0, react_jsx_runtime.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__ */ (0, react_jsx_runtime.jsx)("button", {
type: "button",
role: "option",
"aria-selected": isActive,
"data-active": isActive ? "true" : void 0,
"data-slash-index": index,
className: (0, tailwind_merge.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__ */ (0, react_jsx_runtime.jsx)("div", {
"data-testid": "copilot-chat-input",
className: (0, tailwind_merge.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__ */ (0, react_jsx_runtime.jsxs)("div", {
ref: gridRef,
className: (0, tailwind_merge.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__ */ (0, react_jsx_runtime.jsx)("div", {
ref: addButtonContainerRef,
className: (0, tailwind_merge.twMerge)("cpk:flex cpk:items-center", isExpanded ? "cpk:row-start-2" : "cpk:row-start-1", "cpk:col-start-1"),
children: BoundAddMenuButton
}),
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
className: (0, tailwind_merge.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__ */ (0, react_jsx_runtime.jsx)("div", {
className: "cpk:flex cpk:w-full cpk:items-center cpk:justify-center cpk:py-3 cpk:px-5",
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Loader2, { className: "cpk:size-[26px] cpk:animate-spin cpk:text-muted-foreground" })
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [BoundTextArea, slashMenu] })
}),
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
ref: actionsContainerRef,
className: (0, tailwind_merge.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__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [onCancelTranscribe && BoundCancelTranscribeButton, onFinishTranscribe && BoundFinishTranscribeButton] }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [onStartTranscribe && BoundStartTranscribeButton, BoundSendButton] })
})
]
})
});
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
"data-copilotkit": true,
ref: containerRef,
className: require_utils.cn(positioning === "absolute" && "cpk:absolute cpk:bottom-0 cpk:left-0 cpk:right-0 cpk:z-20 cpk:pointer-events-none", className),
style: {
transform: keyboardHeight > 0 ? `translateY(-${keyboardHeight}px)` : void 0,
transition: "transform 0.2s ease-out"
},
...props,
children: [/* @__PURE__ */ (0, react_jsx_runtime.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__ */ (0, react_jsx_runtime.jsx)("div", {
className: "cpk:mr-[10px]",
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_button.Button, {
type: "button",
"data-testid": "copilot-send-button",
variant: "chatInputToolbarPrimary",
size: "chatInputToolbarIcon",
className,
...props,
children: children ?? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.ArrowUp, { className: "cpk:size-[18px]" })
})
});
const ToolbarButton = _CopilotChatInput.ToolbarButton = ({ icon, labelKey, defaultClassName, className, ...props }) => {
const labels = require_CopilotChatConfigurationProvider.useCopilotChatConfiguration()?.labels ?? require_CopilotChatConfigurationProvider.CopilotChatDefaultLabels;
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_tooltip.Tooltip, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_tooltip.TooltipTrigger, {
asChild: true,
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_button.Button, {
type: "button",
variant: "chatInputToolbarSecondary",
size: "chatInputToolbarIcon",
className: (0, tailwind_merge.twMerge)(defaultClassName, className),
...props,
children: icon
})
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_tooltip.TooltipContent, {
side: "bottom",
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: labels[labelKey] })
})] });
};
_CopilotChatInput.StartTranscribeButton = (props) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToolbarButton, {
"data-testid": "copilot-start-transcribe-button",
icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Mic, { className: "cpk:size-[18px]" }),
labelKey: "chatInputToolbarStartTranscribeButtonLabel",
defaultClassName: "cpk:mr-2",
...props
});
_CopilotChatInput.CancelTranscribeButton = (props) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToolbarButton, {
"data-testid": "copilot-cancel-transcribe-button",
icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.X, { className: "cpk:size-[18px]" }),
labelKey: "chatInputToolbarCancelTranscribeButtonLabel",
defaultClassName: "cpk:mr-2",
...props
});
_CopilotChatInput.FinishTranscribeButton = (props) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToolbarButton, {
"data-testid": "copilot-finish-transcribe-button",
icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Check, { className: "cpk:size-[18px]" }),
labelKey: "chatInputToolbarFinishTranscribeButtonLabel",
defaultClassName: "cpk:mr-[10px]",
...props
});
_CopilotChatInput.AddMenuButton = ({ className, toolsMenu, onAddFile, disabled, ...props }) => {
const labels = require_CopilotChatConfigurationProvider.useCopilotChatConfiguration()?.labels ?? require_CopilotChatConfigurationProvider.CopilotChatDefaultLabels;
const menuItems = (0, react.useMemo)(() => {
const items = [];
if (onAddFile) items.push({
label: labels.chatInputToolbarAddButtonLabel,
action: onAddFile
});
if (toolsMenu && toolsMenu.length > 0) {
if (items.length > 0) items.push("-");
for (const item of toolsMenu) if (item === "-") {
if (items.length === 0 || items[items.length - 1] === "-") continue;
items.push(item);
} else items.push(item);
while (items.length > 0 && items[items.length - 1] === "-") items.pop();
}
return items;
}, [
onAddFile,
toolsMenu,
labels.chatInputToolbarAddButtonLabel
]);
const renderMenuItems = (0, react.useCallback)((items) => items.map((item, index) => {
if (item === "-") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_dropdown_menu.DropdownMenuSeparator, {}, `separator-${index}`);
if (item.items && item.items.length > 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_dropdown_menu.DropdownMenuSub, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_dropdown_menu.DropdownMenuSubTrigger, { children: item.label }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_dropdown_menu.DropdownMenuSubContent, { children: renderMenuItems(item.items) })] }, `group-${index}`);
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_dropdown_menu.DropdownMenuItem, {
onClick: item.action,
children: item.label
}, `item-${index}`);
}), []);
const hasMenuItems = menuItems.length > 0;
const isDisabled = disabled || !hasMenuItems;
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_dropdown_menu.DropdownMenu, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_tooltip.Tooltip, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_tooltip.TooltipTrigger, {
asChild: true,
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_dropdown_menu.DropdownMenuTrigger, {
asChild: true,
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_button.Button, {
type: "button",
"data-testid": "copilot-add-menu-button",
variant: "chatInputToolbarSecondary",
size: "chatInputToolbarIcon",
className: (0, tailwind_merge.twMerge)("cpk:ml-1", className),
disabled: isDisabled,
...props,
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Plus, { className: "cpk:size-[20px]" })
})
})
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_tooltip.TooltipContent, {
side: "bottom",
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
className: "cpk:flex cpk:items-center cpk:gap-1 cpk:text-xs cpk:font-medium",
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: "Add files and more" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", {
className: "cpk:rounded cpk:bg-[#4a4a4a] cpk:px-1 cpk:py-[1px] cpk:font-mono cpk:text-[11px] cpk:text-white cpk:dark:bg-[#e0e0e0] cpk:dark:text-black",
children: "/"
})]
})
})] }), hasMenuItems && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_dropdown_menu.DropdownMenuContent, {
side: "top",
align: "start",
children: renderMenuItems(menuItems)
})] });
};
_CopilotChatInput.TextArea = (0, react.forwardRef)(function TextArea({ style, className, autoFocus, placeholder, ...props }, ref) {
const internalTextareaRef = (0, react.useRef)(null);
const labels = require_CopilotChatConfigurationProvider.useCopilotChatConfiguration()?.labels ?? require_CopilotChatConfigurationProvider.CopilotChatDefaultLabels;
(0, react.useImperativeHandle)(ref, () => internalTextareaRef.current);
(0, react.useEffect)(() => {
const textarea = internalTextareaRef.current;
if (!textarea) return;
const handleFocus = () => {
setTimeout(() => {
textarea.scrollIntoView({
behavior: "smooth",
block: "nearest"
});
}, 300);
};
textarea.addEventListener("focus", handleFocus);
return () => textarea.removeEventListener("focus", handleFocus);
}, []);
(0, react.useEffect)(() => {
if (autoFocus) internalTextareaRef.current?.focus();
}, [autoFocus]);
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
ref: internalTextareaRef,
"data-testid": "copilot-chat-textarea",
placeholder: placeholder ?? labels.chatInputPlaceholder,
className: (0, tailwind_merge.twMerge)("cpk:bg-transparent cpk:outline-none cpk:antialiased cpk:font-regular cpk:leading-relaxed cpk:text-[16px] cpk:placeholder:text-[#00000077] cpk:dark:placeholder:text-[#fffc]", className),
style: {
overflow: "auto",
resize: "none",
...style
},
rows: 1,
...props
});
});
_CopilotChatInput.AudioRecorder = require_CopilotChatAudioRecorder.CopilotChatAudioRecorder;
_CopilotChatInput.Disclaimer = ({ className, ...props }) => {
const labels = require_CopilotChatConfigurationProvider.useCopilotChatConfiguration()?.labels ?? require_CopilotChatConfigurationProvider.CopilotChatDefaultLabels;
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
className: require_utils.cn("cpk:text-center cpk:text-xs cpk:text-muted-foreground cpk:py-3 cpk:px-4 cpk:max-w-3xl cpk:mx-auto", className),
...props,
children: labels.chatDisclaimerText
});
};
})(CopilotChatInput || (CopilotChatInput = {}));
CopilotChatInput.TextArea.displayName = "CopilotChatInput.TextArea";
CopilotChatInput.SendButton.displayName = "CopilotChatInput.SendButton";
CopilotChatInput.ToolbarButton.displayName = "CopilotChatInput.ToolbarButton";
CopilotChatInput.StartTranscribeButton.displayName = "CopilotChatInput.StartTranscribeButton";
CopilotChatInput.CancelTranscribeButton.displayName = "CopilotChatInput.CancelTranscribeButton";
CopilotChatInput.FinishTranscribeButton.displayName = "CopilotChatInput.FinishTranscribeButton";
CopilotChatInput.AddMenuButton.displayName = "CopilotChatInput.AddMenuButton";
CopilotChatInput.Disclaimer.displayName = "CopilotChatInput.Disclaimer";
var CopilotChatInput_default = CopilotChatInput;
//#endregion
exports.default = CopilotChatInput_default;
//# sourceMappingURL=CopilotChatInput.cjs.map