Merge pull request #1642 from siiddhantt/fix/minor-bugs

fix: minor bugs and enhancement
This commit is contained in:
Alex
2025-02-19 16:16:05 +00:00
committed by GitHub
9 changed files with 254 additions and 211 deletions

View File

@@ -85,7 +85,6 @@ class OpenAILLM(BaseLLM):
**kwargs,
):
messages = self._clean_messages_openai(messages)
print(messages)
if tools:
response = self.client.chat.completions.create(
model=model,

View File

@@ -91,18 +91,18 @@ const SkeletonLoader: React.FC<SkeletonLoaderProps> = ({
);
const renderLogs = () => (
<div className="animate-pulse space-y-px">
<div className="w-full animate-pulse space-y-px">
{[...Array(8)].map((_, idx) => (
<div
key={idx}
className="flex items-start p-2 hover:bg-[#F9F9F9] hover:dark:bg-dark-charcoal"
className="w-full flex items-start p-2 hover:bg-[#F9F9F9] hover:dark:bg-dark-charcoal"
>
<div className="flex items-center gap-2">
<div className="w-3 h-3 bg-gray-300 dark:bg-gray-600 rounded-sm"></div>
<div className="flex flex-row items-center gap-2">
<div className="h-3 bg-gray-300 dark:bg-gray-600 rounded w-20"></div>
<div className="h-3 bg-gray-300 dark:bg-gray-600 rounded w-14 bg-opacity-80"></div>
<div className="h-3 bg-gray-300 dark:bg-gray-600 rounded w-40"></div>
<div className="w-full flex items-center gap-2">
<div className="w-3 h-3 bg-gray-300 dark:bg-gray-600 rounded-lg"></div>
<div className="w-full flex flex-row items-center gap-2">
<div className="h-3 bg-gray-300 dark:bg-gray-600 rounded-lg w-[30%] lg:w-52"></div>
<div className="h-3 bg-gray-300 dark:bg-gray-600 rounded-lg w-[16%] lg:w-28"></div>
<div className="h-3 bg-gray-300 dark:bg-gray-600 rounded-lg w-[40%] lg:w-64"></div>
</div>
</div>
</div>

View File

@@ -634,47 +634,46 @@ function ToolCalls({ toolCalls }: { toolCalls: ToolCallsType[] }) {
title={`${toolCall.tool_name} - ${toolCall.action_name.substring(0, toolCall.action_name.lastIndexOf('_'))}`}
className="w-full rounded-[20px] bg-gray-1000 dark:bg-gun-metal hover:bg-[#F1F1F1] dark:hover:bg-[#2C2E3C]"
titleClassName="px-6 py-2 text-sm font-semibold"
children={
<div className="flex flex-col gap-1">
<div className="flex flex-col border border-silver dark:border-silver/20 rounded-2xl">
<p className="flex flex-row items-center justify-between px-2 py-1 text-sm font-semibold bg-black/10 dark:bg-[#191919] rounded-t-2xl break-words">
<span style={{ fontFamily: 'IBMPlexMono-Medium' }}>
Arguments
</span>{' '}
<CopyButton
text={JSON.stringify(toolCall.arguments, null, 2)}
/>
</p>
<p className="p-2 font-mono text-sm dark:tex dark:bg-[#222327] rounded-b-2xl break-words">
<span
className="text-black dark:text-gray-400 leading-[23px]"
style={{ fontFamily: 'IBMPlexMono-Medium' }}
>
{JSON.stringify(toolCall.arguments, null, 2)}
</span>
</p>
</div>
<div className="flex flex-col border border-silver dark:border-silver/20 rounded-2xl">
<p className="flex flex-row items-center justify-between px-2 py-1 text-sm font-semibold bg-black/10 dark:bg-[#191919] rounded-t-2xl break-words">
<span style={{ fontFamily: 'IBMPlexMono-Medium' }}>
Response
</span>{' '}
<CopyButton
text={JSON.stringify(toolCall.result, null, 2)}
/>
</p>
<p className="p-2 font-mono text-sm dark:tex dark:bg-[#222327] rounded-b-2xl break-words">
<span
className="text-black dark:text-gray-400 leading-[23px]"
style={{ fontFamily: 'IBMPlexMono-Medium' }}
>
{JSON.stringify(toolCall.result, null, 2)}
</span>
</p>
</div>
>
<div className="flex flex-col gap-1">
<div className="flex flex-col border border-silver dark:border-silver/20 rounded-2xl">
<p className="flex flex-row items-center justify-between px-2 py-1 text-sm font-semibold bg-black/10 dark:bg-[#191919] rounded-t-2xl break-words">
<span style={{ fontFamily: 'IBMPlexMono-Medium' }}>
Arguments
</span>{' '}
<CopyButton
text={JSON.stringify(toolCall.arguments, null, 2)}
/>
</p>
<p className="p-2 font-mono text-sm dark:tex dark:bg-[#222327] rounded-b-2xl break-words">
<span
className="text-black dark:text-gray-400 leading-[23px]"
style={{ fontFamily: 'IBMPlexMono-Medium' }}
>
{JSON.stringify(toolCall.arguments, null, 2)}
</span>
</p>
</div>
}
/>
<div className="flex flex-col border border-silver dark:border-silver/20 rounded-2xl">
<p className="flex flex-row items-center justify-between px-2 py-1 text-sm font-semibold bg-black/10 dark:bg-[#191919] rounded-t-2xl break-words">
<span style={{ fontFamily: 'IBMPlexMono-Medium' }}>
Response
</span>{' '}
<CopyButton
text={JSON.stringify(toolCall.result, null, 2)}
/>
</p>
<p className="p-2 font-mono text-sm dark:tex dark:bg-[#222327] rounded-b-2xl break-words">
<span
className="text-black dark:text-gray-400 leading-[23px]"
style={{ fontFamily: 'IBMPlexMono-Medium' }}
>
{JSON.stringify(toolCall.result, null, 2)}
</span>
</p>
</div>
</div>
</Accordion>
))}
</div>
</div>

View File

@@ -1,21 +1,21 @@
import React, { useState } from 'react';
import { useTranslation } from 'react-i18next';
import WrapperModal from './WrapperModal';
import Input from '../components/Input';
import { ActiveState } from '../models/misc';
import WrapperModal from './WrapperModal';
type AddActionModalProps = {
modalState: ActiveState;
setModalState: (state: ActiveState) => void;
handleSubmit: (actionName: string) => void;
};
const isValidFunctionName = (name: string): boolean => {
const pattern = /^[a-zA-Z0-9_-]+$/;
return pattern.test(name);
};
interface AddActionModalProps {
modalState: ActiveState;
setModalState: (state: ActiveState) => void;
handleSubmit: (actionName: string) => void;
}
export default function AddActionModal({
modalState,
setModalState,
@@ -23,21 +23,20 @@ export default function AddActionModal({
}: AddActionModalProps) {
const { t } = useTranslation();
const [actionName, setActionName] = React.useState('');
const [functionNameError, setFunctionNameError] = useState<boolean>(false); // New error state
const [functionNameError, setFunctionNameError] = useState<boolean>(false);
const handleAddAction = () => {
if (!isValidFunctionName(actionName)) {
setFunctionNameError(true); // Set error state if invalid
setFunctionNameError(true);
return;
}
setFunctionNameError(false); // Clear error state if valid
setFunctionNameError(false);
handleSubmit(actionName);
setActionName('');
setModalState('INACTIVE');
};
// Only render when modal is active
if (modalState !== 'ACTIVE') return null;
return (
<WrapperModal
close={() => setModalState('INACTIVE')}
@@ -47,27 +46,32 @@ export default function AddActionModal({
<h2 className="font-semibold text-xl text-jet dark:text-bright-gray px-3">
New Action
</h2>
<div className="mt-6 px-3">
<div className="mt-6 relative px-3">
<span className="z-10 absolute left-5 -top-2 bg-white px-2 text-xs text-gray-4000 dark:bg-[#26272E] dark:text-silver">
Action Name
</span>
<Input
type="text"
value={actionName}
onChange={(e) => setActionName(e.target.value)}
onChange={(e) => {
const value = e.target.value;
setActionName(value);
setFunctionNameError(!isValidFunctionName(value));
}}
borderVariant="thin"
placeholder="Enter name"
label="Action Name"
placeholder={'Enter name'}
/>
<p className="mt-1 text-gray-500 text-xs">
Use only letters, numbers, underscores, and hyphens (e.g.,
`get_user_data`, `send-report`).
<p
className={`mt-2 ml-1 text-xs italic ${
functionNameError ? 'text-red-500' : 'text-gray-500'
}`}
>
{functionNameError
? 'Invalid function name format. Use only letters, numbers, underscores, and hyphens.'
: 'Use only letters, numbers, underscores, and hyphens (e.g., `get_data`, `send_report`, etc.)'}
</p>
{functionNameError && (
<p className="mt-1 text-red-500 text-xs">
Invalid function name format. Use only letters, numbers,
underscores, and hyphens.
</p>
)}
</div>
<div className="mt-8 flex flex-row-reverse gap-1 px-3">
<div className="mt-3 flex flex-row-reverse gap-1 px-3">
<button
onClick={handleAddAction}
className="rounded-3xl bg-purple-30 px-5 py-2 text-sm text-white transition-all hover:bg-[#6F3FD1]"
@@ -75,7 +79,11 @@ export default function AddActionModal({
Add
</button>
<button
onClick={() => setModalState('INACTIVE')}
onClick={() => {
setFunctionNameError(false);
setModalState('INACTIVE');
setActionName('');
}}
className="cursor-pointer rounded-3xl px-5 py-2 text-sm font-medium hover:bg-gray-100 dark:bg-transparent dark:text-light-gray dark:hover:bg-[#767183]/50"
>
{t('modals.configTool.closeButton')}

View File

@@ -6,6 +6,7 @@ import { useOutsideAlerter } from '../hooks';
import { ActiveState } from '../models/misc';
import ConfigToolModal from './ConfigToolModal';
import { AvailableToolType } from './types';
import Spinner from '../components/Spinner';
import WrapperComponent from './WrapperModal';
export default function AddToolModal({
@@ -21,6 +22,8 @@ export default function AddToolModal({
getUserTools: () => void;
onToolAdded: (toolId: string) => void;
}) {
const { t } = useTranslation();
const modalRef = useRef<HTMLDivElement>(null);
const [availableTools, setAvailableTools] = React.useState<
AvailableToolType[]
>([]);
@@ -28,8 +31,7 @@ export default function AddToolModal({
React.useState<AvailableToolType | null>(null);
const [configModalState, setConfigModalState] =
React.useState<ActiveState>('INACTIVE');
const modalRef = useRef<HTMLDivElement>(null);
const { t } = useTranslation();
const [loading, setLoading] = React.useState(false);
useOutsideAlerter(modalRef, () => {
if (modalState === 'ACTIVE') {
@@ -38,6 +40,7 @@ export default function AddToolModal({
}, [modalState]);
const getAvailableTools = () => {
setLoading(true);
userService
.getAvailableTools()
.then((res) => {
@@ -45,6 +48,7 @@ export default function AddToolModal({
})
.then((data) => {
setAvailableTools(data.data);
setLoading(false);
});
};
@@ -85,7 +89,6 @@ export default function AddToolModal({
React.useEffect(() => {
if (modalState === 'ACTIVE') getAvailableTools();
}, [modalState]);
return (
<>
{modalState === 'ACTIVE' && (
@@ -94,46 +97,55 @@ export default function AddToolModal({
className="h-[85vh] w-[90vw] md:w-[75vw]"
>
<div className="flex flex-col gap-4 h-full">
<div className="p-6">
<div>
<h2 className="font-semibold text-xl text-jet dark:text-bright-gray px-3">
{t('settings.tools.selectToolSetup')}
</h2>
<div className="mt-5 flex flex-col sm:grid sm:grid-cols-3 gap-4 h-[73vh] overflow-auto px-3 py-px">
{availableTools.map((tool, index) => (
<div
role="button"
tabIndex={0}
key={index}
className="h-52 w-full p-6 border rounded-2xl border-silver dark:border-[#4D4E58] flex flex-col justify-between dark:bg-[#32333B] cursor-pointer"
onClick={() => {
setSelectedTool(tool);
handleAddTool(tool);
}}
onKeyDown={(e) => {
if (e.key === 'Enter' || e.key === ' ') {
{loading ? (
<div className="h-full col-span-3 flex items-center justify-center">
<Spinner />
</div>
) : (
availableTools.map((tool, index) => (
<div
role="button"
tabIndex={0}
key={index}
className="h-52 w-full p-6 border rounded-2xl border-silver dark:border-[#4D4E58] flex flex-col justify-between dark:bg-[#32333B] cursor-pointer hover:border-[#9d9d9d] hover:dark:border-[#717179]"
onClick={() => {
setSelectedTool(tool);
handleAddTool(tool);
}
}}
>
<div className="w-full">
<div className="px-1 w-full flex items-center justify-between">
<img
src={`/toolIcons/tool_${tool.name}.svg`}
className="h-8 w-8"
/>
</div>
<div className="mt-[9px]">
<p className="px-1 text-sm font-semibold text-eerie-black dark:text-white leading-relaxed capitalize">
{tool.displayName}
</p>
<p className="mt-1 px-1 h-24 overflow-auto text-sm text-gray-600 dark:text-[#8a8a8c] leading-relaxed">
{tool.description}
</p>
}}
onKeyDown={(e) => {
if (e.key === 'Enter' || e.key === ' ') {
setSelectedTool(tool);
handleAddTool(tool);
}
}}
>
<div className="w-full">
<div className="px-1 w-full flex items-center justify-between">
<img
src={`/toolIcons/tool_${tool.name}.svg`}
className="h-8 w-8"
/>
</div>
<div className="mt-[9px]">
<p
title={tool.displayName}
className="px-1 text-sm font-semibold text-eerie-black dark:text-white leading-relaxed capitalize truncate"
>
{tool.displayName}
</p>
<p className="mt-1 px-1 h-24 overflow-auto text-sm text-gray-600 dark:text-[#8a8a8c] leading-relaxed">
{tool.description}
</p>
</div>
</div>
</div>
</div>
))}
))
)}
</div>
</div>
</div>

View File

@@ -532,11 +532,12 @@ function DocumentChunks({
</div>
) : (
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
{paginatedChunks.filter((chunk) =>
chunk.metadata?.title
{paginatedChunks.filter((chunk) => {
if (!chunk.metadata?.title) return true;
return chunk.metadata.title
.toLowerCase()
.includes(searchTerm.toLowerCase()),
).length === 0 ? (
.includes(searchTerm.toLowerCase());
}).length === 0 ? (
<div className="mt-24 col-span-2 lg:col-span-3 text-center text-gray-500 dark:text-gray-400">
<img
src={isDarkTheme ? NoFilesDarkIcon : NoFilesIcon}
@@ -547,11 +548,12 @@ function DocumentChunks({
</div>
) : (
paginatedChunks
.filter((chunk) =>
chunk.metadata?.title
.filter((chunk) => {
if (!chunk.metadata?.title) return true;
return chunk.metadata.title
.toLowerCase()
.includes(searchTerm.toLowerCase()),
)
.includes(searchTerm.toLowerCase());
})
.map((chunk, index) => (
<div
key={index}
@@ -578,7 +580,7 @@ function DocumentChunks({
</div>
<div className="mt-[9px]">
<p className="h-12 text-sm font-semibold text-eerie-black dark:text-[#EEEEEE] leading-relaxed break-words ellipsis-text">
{chunk.metadata?.title}
{chunk.metadata?.title ?? 'Untitled'}
</p>
<p className="mt-1 pr-1 h-[110px] overflow-y-auto text-[13px] text-gray-600 dark:text-gray-400 leading-relaxed break-words">
{chunk.text}
@@ -591,11 +593,12 @@ function DocumentChunks({
</div>
)}
{!loading &&
paginatedChunks.filter((chunk) =>
chunk.metadata?.title
paginatedChunks.filter((chunk) => {
if (!chunk.metadata?.title) return true;
return chunk.metadata.title
.toLowerCase()
.includes(searchTerm.toLowerCase()),
).length !== 0 && (
.includes(searchTerm.toLowerCase());
}).length !== 0 && (
<div className="mt-10 w-full flex items-center justify-center">
<Pagination
currentPage={page}

View File

@@ -84,7 +84,7 @@ export default function General() {
return (
<div className="mt-12">
<div className="mb-5">
<label className="block font-bold text-jet dark:text-bright-gray">
<label className="block mb-2 font-bold text-jet dark:text-bright-gray">
{t('settings.general.selectTheme')}
</label>
<Dropdown

View File

@@ -3,11 +3,11 @@ import { useTranslation } from 'react-i18next';
import userService from '../api/services/userService';
import ChevronRight from '../assets/chevron-right.svg';
import CopyButton from '../components/CopyButton';
import Dropdown from '../components/Dropdown';
import SkeletonLoader from '../components/SkeletonLoader';
import { APIKeyData, LogData } from './types';
import CoppyButton from '../components/CopyButton';
import { useLoaderState } from '../hooks';
import { APIKeyData, LogData } from './types';
export default function Logs() {
const { t } = useTranslation();
@@ -148,7 +148,7 @@ function LogsTable({ logs, setPage, loading }: LogsTableProps) {
{logs?.map((log, index) => {
if (index === logs.length - 1) {
return (
<div ref={firstObserver} key={index}>
<div ref={firstObserver} key={index} className="w-full">
<Log log={log} />
</div>
);
@@ -170,26 +170,30 @@ function Log({ log }: { log: LogData }) {
const { id, action, timestamp, ...filteredLog } = log;
return (
<details className="group bg-transparent [&_summary::-webkit-details-marker]:hidden w-full hover:bg-[#F9F9F9] hover:dark:bg-dark-charcoal">
<summary className="flex flex-row items-center gap-2 text-gray-900 cursor-pointer p-2 group-open:bg-[#F9F9F9] dark:group-open:bg-dark-charcoal">
<summary className="flex flex-row items-start gap-2 text-gray-900 cursor-pointer p-2 group-open:bg-[#F9F9F9] dark:group-open:bg-dark-charcoal">
<img
src={ChevronRight}
alt="Expand log entry"
className="w-3 h-3 transition duration-300 group-open:rotate-90"
className="mt-[3px] w-3 h-3 transition duration-300 group-open:rotate-90"
/>
<span className="flex flex-row gap-2">
<h2 className="text-xs text-black/60 dark:text-bright-gray">{`${log.timestamp}`}</h2>
<h2 className="text-xs text-[#913400] dark:text-[#DF5200]">{`[${log.action}]`}</h2>
<h2
className={`text-xs ${logLevelColor[log.level]}`}
>{`${log.question}`}</h2>
className={`max-w-72 text-xs ${logLevelColor[log.level]} break-words`}
>
{`${log.question}`.length > 250
? `${log.question.substring(0, 250)}...`
: log.question}
</h2>
</span>
</summary>
<div className="px-4 group-open:bg-[#F9F9F9] dark:group-open:bg-dark-charcoal">
<p className="px-2 leading-relaxed text-gray-700 dark:text-gray-400 text-xs">
<p className="px-2 leading-relaxed text-gray-700 dark:text-gray-400 text-xs break-words">
{JSON.stringify(filteredLog, null, 2)}
</p>
<div className="my-px w-8">
<CoppyButton
<CopyButton
text={JSON.stringify(filteredLog)}
colorLight="transparent"
/>

View File

@@ -6,6 +6,7 @@ import CogwheelIcon from '../assets/cogwheel.svg';
import NoFilesDarkIcon from '../assets/no-files-dark.svg';
import NoFilesIcon from '../assets/no-files.svg';
import Input from '../components/Input';
import Spinner from '../components/Spinner';
import { useDarkTheme } from '../hooks';
import AddToolModal from '../modals/AddToolModal';
import { ActiveState } from '../models/misc';
@@ -22,8 +23,10 @@ export default function Tools() {
const [selectedTool, setSelectedTool] = React.useState<
UserToolType | APIToolType | null
>(null);
const [loading, setLoading] = React.useState(false);
const getUserTools = () => {
setLoading(true);
userService
.getUserTools()
.then((res) => {
@@ -31,6 +34,11 @@ export default function Tools() {
})
.then((data) => {
setUserTools(data.tools);
setLoading(false);
})
.catch((error) => {
console.error('Error fetching tools:', error);
setLoading(false);
});
};
@@ -78,7 +86,6 @@ export default function Tools() {
React.useEffect(() => {
getUserTools();
}, []);
return (
<div>
{selectedTool ? (
@@ -115,88 +122,99 @@ export default function Tools() {
{t('settings.tools.addTool')}
</button>
</div>
<div className="grid grid-cols-2 lg:grid-cols-3 gap-6">
{userTools.filter((tool) =>
tool.displayName
.toLowerCase()
.includes(searchTerm.toLowerCase()),
).length === 0 ? (
<div className="mt-24 col-span-2 lg:col-span-3 text-center text-gray-500 dark:text-gray-400">
<img
src={isDarkTheme ? NoFilesDarkIcon : NoFilesIcon}
alt="No tools found"
className="h-24 w-24 mx-auto mb-2"
/>
{t('settings.tools.noToolsFound')}
{loading ? (
<div className="grid grid-cols-2 lg:grid-cols-3 gap-6">
<div className="mt-24 h-32 col-span-2 lg:col-span-3 flex items-center justify-center">
<Spinner />
</div>
) : (
userTools
.filter((tool) =>
tool.displayName
.toLowerCase()
.includes(searchTerm.toLowerCase()),
)
.map((tool, index) => (
<div
key={index}
className="relative h-56 w-full p-6 border rounded-2xl border-silver dark:border-silver/40 flex flex-col justify-between"
>
<div className="w-full">
<div className="w-full flex items-center justify-between">
<img
src={`/toolIcons/tool_${tool.name}.svg`}
alt={`${tool.displayName} icon`}
className="h-8 w-8"
/>
<button
className="absolute top-3 right-3 cursor-pointer"
onClick={() => handleSettingsClick(tool)}
aria-label={t('settings.tools.configureToolAria', {
toolName: tool.displayName,
})}
>
</div>
) : (
<div className="grid grid-cols-2 lg:grid-cols-3 gap-6">
{userTools.filter((tool) =>
tool.displayName
.toLowerCase()
.includes(searchTerm.toLowerCase()),
).length === 0 ? (
<div className="mt-24 col-span-2 lg:col-span-3 text-center text-gray-500 dark:text-gray-400">
<img
src={isDarkTheme ? NoFilesDarkIcon : NoFilesIcon}
alt="No tools found"
className="h-24 w-24 mx-auto mb-2"
/>
{t('settings.tools.noToolsFound')}
</div>
) : (
userTools
.filter((tool) =>
tool.displayName
.toLowerCase()
.includes(searchTerm.toLowerCase()),
)
.map((tool, index) => (
<div
key={index}
className="relative h-56 w-full p-6 border rounded-2xl border-silver dark:border-silver/40 flex flex-col justify-between"
>
<div className="w-full">
<div className="w-full flex items-center justify-between">
<img
src={CogwheelIcon}
alt={t('settings.tools.settingsIconAlt')}
className="h-[19px] w-[19px]"
src={`/toolIcons/tool_${tool.name}.svg`}
alt={`${tool.displayName} icon`}
className="h-8 w-8"
/>
</button>
<button
className="absolute top-3 right-3 cursor-pointer"
onClick={() => handleSettingsClick(tool)}
aria-label={t(
'settings.tools.configureToolAria',
{
toolName: tool.displayName,
},
)}
>
<img
src={CogwheelIcon}
alt={t('settings.tools.settingsIconAlt')}
className="h-[19px] w-[19px]"
/>
</button>
</div>
<div className="mt-[9px]">
<p className="text-sm font-semibold text-eerie-black dark:text-[#EEEEEE] leading-relaxed">
{tool.displayName}
</p>
<p className="mt-1 h-16 overflow-auto text-[13px] text-gray-600 dark:text-gray-400 leading-relaxed pr-1">
{tool.description}
</p>
</div>
</div>
<div className="mt-[9px]">
<p className="text-sm font-semibold text-eerie-black dark:text-[#EEEEEE] leading-relaxed">
{tool.displayName}
</p>
<p className="mt-1 h-16 overflow-auto text-[13px] text-gray-600 dark:text-gray-400 leading-relaxed pr-1">
{tool.description}
</p>
<div className="absolute bottom-3 right-3">
<label
htmlFor={`toolToggle-${index}`}
className="relative inline-block h-6 w-10 cursor-pointer rounded-full bg-gray-300 dark:bg-[#D2D5DA33]/20 transition [-webkit-tap-highlight-color:_transparent] has-[:checked]:bg-[#0C9D35CC] has-[:checked]:dark:bg-[#0C9D35CC]"
>
<span className="sr-only">
{t('settings.tools.toggleToolAria', {
toolName: tool.displayName,
})}
</span>
<input
type="checkbox"
id={`toolToggle-${index}`}
className="peer sr-only"
checked={tool.status}
onChange={() =>
updateToolStatus(tool.id, !tool.status)
}
/>
<span className="absolute inset-y-0 start-0 m-[3px] size-[18px] rounded-full bg-white transition-all peer-checked:start-4"></span>
</label>
</div>
</div>
<div className="absolute bottom-3 right-3">
<label
htmlFor={`toolToggle-${index}`}
className="relative inline-block h-6 w-10 cursor-pointer rounded-full bg-gray-300 dark:bg-[#D2D5DA33]/20 transition [-webkit-tap-highlight-color:_transparent] has-[:checked]:bg-[#0C9D35CC] has-[:checked]:dark:bg-[#0C9D35CC]"
>
<span className="sr-only">
{t('settings.tools.toggleToolAria', {
toolName: tool.displayName,
})}
</span>
<input
type="checkbox"
id={`toolToggle-${index}`}
className="peer sr-only"
checked={tool.status}
onChange={() =>
updateToolStatus(tool.id, !tool.status)
}
/>
<span className="absolute inset-y-0 start-0 m-[3px] size-[18px] rounded-full bg-white transition-all peer-checked:start-4"></span>
</label>
</div>
</div>
))
)}
</div>
))
)}
</div>
)}
</div>
<AddToolModal
message={t('settings.tools.selectToolSetup')}