Merge branch 'main' into feat/enhance-agents

This commit is contained in:
Siddhant Rai
2025-05-12 13:45:18 +05:30
49 changed files with 2518 additions and 642 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -24,6 +24,7 @@
"clsx": "^2.1.1",
"i18next": "^24.2.0",
"i18next-browser-languagedetector": "^8.0.2",
"mermaid": "^11.6.0",
"prop-types": "^15.8.1",
"react": "^18.2.0",
"react-chartjs-2": "^5.3.0",
@@ -41,6 +42,7 @@
"remark-math": "^6.0.0"
},
"devDependencies": {
"@types/mermaid": "^9.1.0",
"@types/react": "^18.0.27",
"@types/react-dom": "^18.3.0",
"@types/react-helmet": "^6.1.11",

View File

@@ -29,7 +29,7 @@ export default function About() {
If you want to add your own documentation, please follow the
instruction below:
</p>
<p className="mt-4 ml-2">
<p className="ml-2 mt-4">
1. Navigate to{' '}
<span className="bg-gray-200 italic dark:bg-outer-space">
{' '}
@@ -37,13 +37,13 @@ export default function About() {
</span>{' '}
folder
</p>
<p className="mt-4 ml-2">
<p className="ml-2 mt-4">
2. Install dependencies from{' '}
<span className="bg-gray-200 italic dark:bg-outer-space">
pip install -r requirements.txt
</span>
</p>
<p className="mt-4 ml-2">
<p className="ml-2 mt-4">
3. Prepare a{' '}
<span className="bg-gray-200 italic dark:bg-outer-space">.env</span>{' '}
file. Copy{' '}
@@ -54,7 +54,7 @@ export default function About() {
<span className="bg-gray-200 italic dark:bg-outer-space">.env</span>{' '}
with your OpenAI API token
</p>
<p className="mt-4 ml-2">
<p className="ml-2 mt-4">
4. Run the app with{' '}
<span className="bg-gray-200 italic dark:bg-outer-space">
python app.py
@@ -64,9 +64,9 @@ export default function About() {
<p>
Currently It uses{' '}
<span className="text-blue-950 font-medium">DocsGPT</span>{' '}
<span className="font-medium text-blue-950">DocsGPT</span>{' '}
documentation, so it will respond to information relevant to{' '}
<span className="text-blue-950 font-medium">DocsGPT</span>. If you
<span className="font-medium text-blue-950">DocsGPT</span>. If you
want to train it on different documentation - please follow
<a
className="text-blue-500"

View File

@@ -19,18 +19,18 @@ export default function Hero({
}>;
return (
<div className="flex h-full w-full flex-col text-black-1000 dark:text-bright-gray items-center justify-between">
<div className="flex h-full w-full flex-col items-center justify-between text-black-1000 dark:text-bright-gray">
{/* Header Section */}
<div className="flex flex-col items-center justify-center flex-grow pt-8 md:pt-0">
<div className="flex items-center mb-4">
<div className="flex flex-grow flex-col items-center justify-center pt-8 md:pt-0">
<div className="mb-4 flex items-center">
<span className="text-4xl font-semibold">DocsGPT</span>
<img className="mb-1 inline w-14" src={DocsGPT3} alt="docsgpt" />
</div>
</div>
{/* Demo Buttons Section */}
<div className="w-full max-w-full mb-8 md:mb-16">
<div className="grid grid-cols-1 md:grid-cols-1 lg:grid-cols-2 gap-3 md:gap-4 text-xs">
<div className="mb-8 w-full max-w-full md:mb-16">
<div className="grid grid-cols-1 gap-3 text-xs md:grid-cols-1 md:gap-4 lg:grid-cols-2">
{demos?.map(
(demo: { header: string; query: string }, key: number) =>
demo.header &&
@@ -38,17 +38,12 @@ export default function Hero({
<button
key={key}
onClick={() => handleQuestion({ question: demo.query })}
className={`
w-full rounded-[66px] border bg-transparent px-6 py-[14px] text-left transition-colors
border-dark-gray text-just-black hover:bg-cultured
dark:border-dim-gray dark:text-chinese-white dark:hover:bg-charleston-green
${key >= 2 ? 'hidden md:block' : ''} // Show only 2 buttons on mobile
`}
className={`w-full rounded-[66px] border border-dark-gray bg-transparent px-6 py-[14px] text-left text-just-black transition-colors hover:bg-cultured dark:border-dim-gray dark:text-chinese-white dark:hover:bg-charleston-green ${key >= 2 ? 'hidden md:block' : ''} // Show only 2 buttons on mobile`}
>
<p className="mb-2 font-semibold text-black-1000 dark:text-bright-gray">
{demo.header}
</p>
<span className="text-gray-700 dark:text-gray-300 opacity-60 line-clamp-2">
<span className="line-clamp-2 text-gray-700 opacity-60 dark:text-gray-300">
{demo.query}
</span>
</button>

View File

@@ -176,10 +176,17 @@ export default function Navigation({ navOpen, setNavOpen }: NavigationProps) {
};
const handleConversationClick = (index: string) => {
dispatch(setSelectedAgent(null));
conversationService
.getConversation(index, token)
.then((response) => response.json())
.then((data) => {
dispatch(setConversation(data.queries));
dispatch(
updateConversationId({
query: { conversationId: index },
}),
);
if (data.agent_id) {
if (data.is_shared_usage) {
userService
@@ -205,12 +212,6 @@ export default function Navigation({ navOpen, setNavOpen }: NavigationProps) {
navigate('/');
dispatch(setSelectedAgent(null));
}
dispatch(setConversation(data.queries));
dispatch(
updateConversationId({
query: { conversationId: index },
}),
);
});
};

View File

@@ -6,7 +6,7 @@ export default function PageNotFound() {
<p className="mx-auto my-auto mt-20 flex w-full max-w-6xl flex-col place-items-center gap-6 rounded-3xl bg-gray-100 p-6 text-jet dark:bg-outer-space dark:text-gray-100 lg:p-10 xl:p-16">
<h1>404</h1>
<p>The page you are looking for does not exist.</p>
<button className="pointer-cursor mr-4 flex cursor-pointer items-center justify-center rounded-full bg-blue-1000 py-2 px-4 text-white transition-colors duration-100 hover:bg-blue-3000">
<button className="pointer-cursor mr-4 flex cursor-pointer items-center justify-center rounded-full bg-blue-1000 px-4 py-2 text-white transition-colors duration-100 hover:bg-blue-3000">
<Link to="/">Go Back Home</Link>
</button>
</p>

View File

@@ -11,7 +11,10 @@ import AgentDetailsModal from '../modals/AgentDetailsModal';
import ConfirmationModal from '../modals/ConfirmationModal';
import { ActiveState, Doc, Prompt } from '../models/misc';
import {
selectSelectedAgent, selectSourceDocs, selectToken, setSelectedAgent
selectSelectedAgent,
selectSourceDocs,
selectToken,
setSelectedAgent,
} from '../preferences/preferenceSlice';
import PromptsModal from '../preferences/PromptsModal';
import { UserToolType } from '../settings/types';
@@ -603,9 +606,9 @@ function AddPromptModal({
newPromptContent={newPromptContent}
setNewPromptContent={setNewPromptContent}
editPromptName={''}
setEditPromptName={() => {}}
setEditPromptName={() => undefined}
editPromptContent={''}
setEditPromptContent={() => {}}
setEditPromptContent={() => undefined}
currentPromptEdit={{ id: '', name: '', type: '' }}
handleAddPrompt={handleAddPrompt}
/>

View File

@@ -3,7 +3,6 @@ import { useDispatch, useSelector } from 'react-redux';
import { Route, Routes, useNavigate } from 'react-router-dom';
import userService from '../api/services/userService';
import Copy from '../assets/copy-linear.svg';
import Edit from '../assets/edit.svg';
import Monitoring from '../assets/monitoring.svg';
import Pin from '../assets/pin.svg';
@@ -205,7 +204,7 @@ function AgentSection({
</button>
)}
</div>
<div className="flex w-full flex-wrap gap-4">
<div className="grid w-full grid-cols-2 gap-2 md:flex md:flex-wrap md:gap-4">
{loading ? (
<div className="flex h-72 w-full items-center justify-center">
<Spinner />
@@ -355,7 +354,7 @@ function AgentCard({
};
return (
<div
className={`relative flex h-44 w-48 flex-col justify-between rounded-[1.2rem] bg-[#F6F6F6] px-6 py-5 hover:bg-[#ECECEC] dark:bg-[#383838] hover:dark:bg-[#383838]/80 ${agent.status === 'published' && 'cursor-pointer'}`}
className={`relative flex h-44 w-full flex-col justify-between rounded-[1.2rem] bg-[#F6F6F6] px-6 py-5 hover:bg-[#ECECEC] dark:bg-[#383838] hover:dark:bg-[#383838]/80 md:w-48 ${agent.status === 'published' && 'cursor-pointer'}`}
onClick={(e) => {
e.stopPropagation();
handleClick();

View File

@@ -32,9 +32,9 @@ export default function Accordion({
setIsOpen(!isOpen);
};
return (
<div className={`shadow-sm overflow-hidden ${className}`}>
<div className={`overflow-hidden shadow-sm ${className}`}>
<button
className={`flex items-center justify-between w-full focus:outline-none ${titleClassName}`}
className={`flex w-full items-center justify-between focus:outline-none ${titleClassName}`}
onClick={toggleAccordion}
>
<p className="break-words">{title}</p>

View File

@@ -52,31 +52,31 @@ const Pagination: React.FC<PaginationProps> = ({
};
return (
<div className="flex items-center text-xs justify-end gap-4 mt-2 p-2 border-gray-200">
<div className="mt-2 flex items-center justify-end gap-4 border-gray-200 p-2 text-xs">
{/* Rows per page dropdown */}
<div className="flex items-center gap-2 relative">
<div className="relative flex items-center gap-2">
<span className="text-gray-900 dark:text-gray-50">
{t('pagination.rowsPerPage')}:
</span>
<div className="relative">
<button
onClick={toggleDropdown}
className="px-3 py-1 border rounded dark:bg-dark-charcoal dark:text-light-gray hover:bg-gray-200 dark:hover:bg-neutral-700"
className="rounded border px-3 py-1 hover:bg-gray-200 dark:bg-dark-charcoal dark:text-light-gray dark:hover:bg-neutral-700"
>
{rowsPerPage}
</button>
<div
className={`absolute z-50 right-0 mt-1 w-28 transform bg-white dark:bg-dark-charcoal shadow-lg ring-1 ring-black ring-opacity-5 transition-all duration-200 ease-in-out ${
className={`absolute right-0 z-50 mt-1 w-28 transform bg-white shadow-lg ring-1 ring-black ring-opacity-5 transition-all duration-200 ease-in-out dark:bg-dark-charcoal ${
isDropdownOpen
? 'scale-100 opacity-100 block'
: 'scale-95 opacity-0 hidden'
? 'block scale-100 opacity-100'
: 'hidden scale-95 opacity-0'
}`}
>
{rowsPerPageOptions.map((option) => (
<div
key={option}
onClick={() => handleSelectRowsPerPage(option)}
className={`cursor-pointer px-4 py-2 text-xs hover:bg-gray-100 dark:hover:bg-neutral-700 ${
className={`cursor-pointer px-4 py-2 text-xs hover:bg-gray-100 dark:hover:bg-neutral-700 ${
rowsPerPage === option
? 'bg-gray-100 dark:bg-neutral-700 dark:text-light-gray'
: 'bg-white dark:bg-dark-charcoal dark:text-light-gray'
@@ -97,45 +97,45 @@ const Pagination: React.FC<PaginationProps> = ({
<button
onClick={handleFirstPage}
disabled={currentPage === 1}
className="px-2 py-1 border rounded disabled:opacity-50"
className="rounded border px-2 py-1 disabled:opacity-50"
>
<img
src={DoubleArrowLeft}
alt={t('pagination.firstPage')}
className="dark:invert dark:sepia dark:brightness-200"
className="dark:brightness-200 dark:invert dark:sepia"
/>
</button>
<button
onClick={handlePreviousPage}
disabled={currentPage === 1}
className="px-2 py-1 border rounded disabled:opacity-50"
className="rounded border px-2 py-1 disabled:opacity-50"
>
<img
src={SingleArrowLeft}
alt={t('pagination.previousPage')}
className="dark:invert dark:sepia dark:brightness-200"
className="dark:brightness-200 dark:invert dark:sepia"
/>
</button>
<button
onClick={handleNextPage}
disabled={currentPage === totalPages}
className="px-2 py-1 border rounded disabled:opacity-50"
className="rounded border px-2 py-1 disabled:opacity-50"
>
<img
src={SingleArrowRight}
alt={t('pagination.nextPage')}
className="dark:invert dark:sepia dark:brightness-200"
className="dark:brightness-200 dark:invert dark:sepia"
/>
</button>
<button
onClick={handleLastPage}
disabled={currentPage === totalPages}
className="px-2 py-1 border rounded disabled:opacity-50"
className="rounded border px-2 py-1 disabled:opacity-50"
>
<img
src={DoubleArrowRight}
alt={t('pagination.lastPage')}
className="dark:invert dark:sepia dark:brightness-200"
className="dark:brightness-200 dark:invert dark:sepia"
/>
</button>
</div>

View File

@@ -88,7 +88,7 @@ export default function DropdownMenu({
onClick={(e) => e.stopPropagation()}
>
<div
className={`w-28 transform rounded-md bg-white dark:bg-dark-charcoal shadow-lg ring-1 ring-black ring-opacity-5 transition-all duration-200 ease-in-out ${className}`}
className={`w-28 transform rounded-md bg-white shadow-lg ring-1 ring-black ring-opacity-5 transition-all duration-200 ease-in-out dark:bg-dark-charcoal ${className}`}
>
<div
role="menu"

View File

@@ -36,20 +36,20 @@ const Help = () => {
<button
ref={buttonRef}
onClick={toggleDropdown}
className="my-auto mx-4 w-full flex items-center h-9 gap-4 rounded-3xl hover:bg-gray-100 dark:hover:bg-[#28292E]"
className="mx-4 my-auto flex h-9 w-full items-center gap-4 rounded-3xl hover:bg-gray-100 dark:hover:bg-[#28292E]"
>
<img src={Info} alt="info" className="ml-2 w-5 filter dark:invert" />
{t('help')}
</button>
{isOpen && (
<div
className={`absolute translate-x-4 -translate-y-28 z-10 w-48 shadow-lg bg-white dark:bg-[#444654] rounded-xl`}
className={`absolute z-10 w-48 -translate-y-28 translate-x-4 rounded-xl bg-white shadow-lg dark:bg-[#444654]`}
>
<a
href="https://docs.docsgpt.cloud/"
target="_blank"
rel="noopener noreferrer"
className="flex items-start gap-4 px-4 py-2 text-black dark:text-white hover:bg-bright-gray dark:hover:bg-[#545561] rounded-t-xl"
className="flex items-start gap-4 rounded-t-xl px-4 py-2 text-black hover:bg-bright-gray dark:text-white dark:hover:bg-[#545561]"
>
<img
src={PageIcon}
@@ -61,12 +61,12 @@ const Help = () => {
</a>
<a
href="mailto:support@docsgpt.cloud"
className="flex items-start gap-4 px-4 py-2 text-black dark:text-white hover:bg-bright-gray dark:hover:bg-[#545561] rounded-b-xl"
className="flex items-start gap-4 rounded-b-xl px-4 py-2 text-black hover:bg-bright-gray dark:text-white dark:hover:bg-[#545561]"
>
<img
src={EmailIcon}
alt="Email Us"
className="filter dark:invert p-0.5"
className="p-0.5 filter dark:invert"
width={20}
/>
{t('emailUs')}

View File

@@ -0,0 +1,424 @@
import React, { useEffect, useRef, useState } from 'react';
import mermaid from 'mermaid';
import CopyButton from './CopyButton';
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
import {
oneLight,
vscDarkPlus,
} from 'react-syntax-highlighter/dist/cjs/styles/prism';
import { MermaidRendererProps } from './types';
import { useSelector } from 'react-redux';
import { selectStatus } from '../conversation/conversationSlice';
import { useDarkTheme } from '../hooks';
const MermaidRenderer: React.FC<MermaidRendererProps> = ({
code,
isLoading,
}) => {
const [isDarkTheme] = useDarkTheme();
const diagramId = useRef(`mermaid-${crypto.randomUUID()}`);
const status = useSelector(selectStatus);
const [error, setError] = useState<string | null>(null);
const [showCode, setShowCode] = useState<boolean>(false);
const [showDownloadMenu, setShowDownloadMenu] = useState<boolean>(false);
const downloadMenuRef = useRef<HTMLDivElement>(null);
const containerRef = useRef<HTMLDivElement>(null);
const [hoverPosition, setHoverPosition] = useState<{
x: number;
y: number;
} | null>(null);
const [isHovering, setIsHovering] = useState<boolean>(false);
const [zoomFactor, setZoomFactor] = useState<number>(2);
const handleMouseMove = (event: React.MouseEvent) => {
if (!containerRef.current) return;
const rect = containerRef.current.getBoundingClientRect();
const x = (event.clientX - rect.left) / rect.width;
const y = (event.clientY - rect.top) / rect.height;
setHoverPosition({ x, y });
};
const handleMouseEnter = () => setIsHovering(true);
const handleMouseLeave = () => {
setIsHovering(false);
setHoverPosition(null);
};
const handleKeyDown = (event: React.KeyboardEvent) => {
if (!isHovering) return;
if (event.key === '+' || event.key === '=') {
setZoomFactor((prev) => Math.min(6, prev + 0.5)); // Cap at 6x
event.preventDefault();
} else if (event.key === '-') {
setZoomFactor((prev) => Math.max(1, prev - 0.5)); // Minimum 1x
event.preventDefault();
}
};
const handleWheel = (event: React.WheelEvent) => {
if (!isHovering) return;
if (event.ctrlKey || event.metaKey) {
event.preventDefault();
if (event.deltaY < 0) {
setZoomFactor((prev) => Math.min(6, prev + 0.25));
} else {
setZoomFactor((prev) => Math.max(1, prev - 0.25));
}
}
};
const getTransformOrigin = () => {
if (!hoverPosition) return 'center center';
return `${hoverPosition.x * 100}% ${hoverPosition.y * 100}%`;
};
useEffect(() => {
const renderDiagram = async () => {
mermaid.initialize({
startOnLoad: true,
theme: isDarkTheme ? 'dark' : 'default',
securityLevel: 'loose',
suppressErrorRendering: true,
});
const isCurrentlyLoading =
isLoading !== undefined ? isLoading : status === 'loading';
if (!isCurrentlyLoading && code) {
try {
const element = document.getElementById(diagramId.current);
if (element) {
element.removeAttribute('data-processed');
await mermaid.parse(code); //syntax check
mermaid.contentLoaded();
}
} catch (err) {
console.error('Error rendering mermaid diagram:', err);
setError(
`Failed to render diagram: ${err instanceof Error ? err.message : String(err)}`,
);
}
}
};
renderDiagram();
}, [code, isDarkTheme, isLoading]);
useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
if (
downloadMenuRef.current &&
!downloadMenuRef.current.contains(event.target as Node)
) {
setShowDownloadMenu(false);
}
};
document.addEventListener('mousedown', handleClickOutside);
return () => {
document.removeEventListener('mousedown', handleClickOutside);
};
}, [showDownloadMenu]);
const downloadSvg = (): void => {
const element = document.getElementById(diagramId.current);
if (!element) return;
const svgElement = element.querySelector('svg');
if (!svgElement) return;
const svgClone = svgElement.cloneNode(true) as SVGElement;
if (!svgClone.hasAttribute('xmlns')) {
svgClone.setAttribute('xmlns', 'http://www.w3.org/2000/svg');
}
if (!svgClone.hasAttribute('width') || !svgClone.hasAttribute('height')) {
const viewBox = svgClone.getAttribute('viewBox')?.split(' ') || [];
if (viewBox.length === 4) {
svgClone.setAttribute('width', viewBox[2]);
svgClone.setAttribute('height', viewBox[3]);
}
}
const serializer = new XMLSerializer();
const svgString = serializer.serializeToString(svgClone);
const svgBlob = new Blob(
[`<?xml version="1.0" encoding="UTF-8" standalone="no"?>\n${svgString}`],
{ type: 'image/svg+xml' },
);
const url = URL.createObjectURL(svgBlob);
const link = document.createElement('a');
link.href = url;
link.download = 'diagram.svg';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
URL.revokeObjectURL(url);
};
const downloadPng = (): void => {
const element = document.getElementById(diagramId.current);
if (!element) return;
const svgElement = element.querySelector('svg');
if (!svgElement) return;
const svgClone = svgElement.cloneNode(true) as SVGElement;
if (!svgClone.hasAttribute('xmlns')) {
svgClone.setAttribute('xmlns', 'http://www.w3.org/2000/svg');
}
let width = parseInt(svgClone.getAttribute('width') || '0');
let height = parseInt(svgClone.getAttribute('height') || '0');
if (!width || !height) {
const viewBox = svgClone.getAttribute('viewBox')?.split(' ') || [];
if (viewBox.length === 4) {
width = parseInt(viewBox[2]);
height = parseInt(viewBox[3]);
svgClone.setAttribute('width', width.toString());
svgClone.setAttribute('height', height.toString());
} else {
width = 800;
height = 600;
svgClone.setAttribute('width', width.toString());
svgClone.setAttribute('height', height.toString());
}
}
const serializer = new XMLSerializer();
const svgString = serializer.serializeToString(svgClone);
const svgBase64 = btoa(unescape(encodeURIComponent(svgString)));
const dataUrl = `data:image/svg+xml;base64,${svgBase64}`;
const img = new Image();
img.crossOrigin = 'anonymous';
img.onload = function (): void {
const canvas = document.createElement('canvas');
canvas.width = width;
canvas.height = height;
const ctx = canvas.getContext('2d');
if (!ctx) {
console.error('Could not get canvas context');
return;
}
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(img, 0, 0, width, height);
try {
const pngUrl = canvas.toDataURL('image/png');
const link = document.createElement('a');
link.download = 'diagram.png';
link.href = pngUrl;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
} catch (e) {
console.error('Failed to create PNG:', e);
// Fallback to SVG download if PNG fails
downloadSvg();
}
};
img.src = dataUrl;
};
const downloadMmd = (): void => {
const blob = new Blob([code], { type: 'text/plain' });
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = 'diagram.mmd';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
URL.revokeObjectURL(url);
};
const downloadOptions = [
{ label: 'Download as SVG', action: downloadSvg },
{ label: 'Download as PNG', action: downloadPng },
{ label: 'Download as MMD', action: downloadMmd },
];
const isCurrentlyLoading =
isLoading !== undefined ? isLoading : status === 'loading';
const showDiagramOptions = !isCurrentlyLoading && !error;
const errorRender = !isCurrentlyLoading && error;
return (
<div className="w-inherit group relative rounded-lg border border-light-silver bg-white dark:border-raisin-black dark:bg-eerie-black">
<div className="flex items-center justify-between bg-platinum px-2 py-1 dark:bg-eerie-black-2">
<span className="text-xs font-medium text-just-black dark:text-chinese-white">
mermaid
</span>
<div className="flex items-center gap-2">
<CopyButton textToCopy={String(code).replace(/\n$/, '')} />
{showDiagramOptions && (
<div className="relative" ref={downloadMenuRef}>
<button
onClick={() => setShowDownloadMenu(!showDownloadMenu)}
className="flex h-full items-center rounded bg-gray-100 px-2 py-1 text-xs dark:bg-gray-700"
title="Download options"
>
Download <span className="ml-1"></span>
</button>
{showDownloadMenu && (
<div className="absolute right-0 z-10 mt-1 w-40 rounded border border-gray-200 bg-white shadow-lg dark:border-gray-700 dark:bg-gray-800">
<ul>
{downloadOptions.map((option, index) => (
<li key={index}>
<button
onClick={() => {
option.action();
setShowDownloadMenu(false);
}}
className="w-full px-4 py-2 text-left text-xs hover:bg-gray-100 dark:hover:bg-gray-700"
>
{option.label}
</button>
</li>
))}
</ul>
</div>
)}
</div>
)}
{showDiagramOptions && (
<button
onClick={() => setShowCode(!showCode)}
className={`flex h-full items-center rounded px-2 py-1 text-xs ${
showCode
? 'bg-blue-200 dark:bg-blue-800'
: 'bg-gray-100 dark:bg-gray-700'
}`}
title="View Code"
>
Code
</button>
)}
</div>
</div>
{isCurrentlyLoading ? (
<div className="flex items-center justify-center bg-white p-4 dark:bg-eerie-black">
<div className="text-sm text-gray-500 dark:text-gray-400">
Loading diagram...
</div>
</div>
) : errorRender ? (
<div className="m-2 rounded border-2 border-red-400 dark:border-red-700">
<div className="overflow-auto whitespace-normal break-words bg-red-100 px-4 py-2 text-sm text-red-800 dark:bg-red-900/30 dark:text-red-300">
{error}
</div>
</div>
) : (
<>
<div
ref={containerRef}
className="no-scrollbar relative block w-full bg-white p-4 dark:bg-eerie-black"
style={{
overflow: 'auto',
scrollbarWidth: 'none',
msOverflowStyle: 'none',
width: '100%',
}}
onMouseMove={handleMouseMove}
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
onKeyDown={handleKeyDown}
onWheel={handleWheel}
tabIndex={0}
>
{isHovering && (
<>
<div className="absolute right-2 top-2 z-10 flex items-center gap-2 rounded bg-black/70 px-2 py-1 text-xs text-white">
<button
onClick={() =>
setZoomFactor((prev) => Math.max(1, prev - 0.5))
}
className="rounded px-1 hover:bg-gray-600"
title="Decrease zoom"
>
-
</button>
<span
className="cursor-pointer hover:underline"
onClick={() => {
setZoomFactor(2);
}}
title="Reset zoom"
>
{zoomFactor.toFixed(1)}x
</span>
<button
onClick={() =>
setZoomFactor((prev) => Math.min(6, prev + 0.5))
}
className="rounded px-1 hover:bg-gray-600"
title="Increase zoom"
>
+
</button>
</div>
</>
)}
<pre
className="mermaid w-full select-none"
id={diagramId.current}
key={`mermaid-${diagramId.current}`}
style={{
transform: isHovering ? `scale(${zoomFactor})` : `scale(1)`,
transformOrigin: getTransformOrigin(),
transition: 'transform 0.2s ease',
cursor: 'default',
width: '100%',
display: 'flex',
justifyContent: 'center',
}}
>
{code}
</pre>
</div>
{showCode && (
<div className="border-t border-light-silver dark:border-raisin-black">
<div className="bg-platinum p-2 dark:bg-eerie-black-2">
<span className="text-xs font-medium text-just-black dark:text-chinese-white">
Mermaid Code
</span>
</div>
<SyntaxHighlighter
language="mermaid"
style={isDarkTheme ? vscDarkPlus : oneLight}
customStyle={{
margin: 0,
borderRadius: 0,
scrollbarWidth: 'thin',
maxHeight: '300px',
}}
>
{code}
</SyntaxHighlighter>
</div>
)}
</>
)}
</div>
);
};
export default MermaidRenderer;

View File

@@ -15,7 +15,7 @@ export default function ShareButton({ conversationId }: ShareButtonProps) {
onClick={() => {
setShareModalState(true);
}}
className="absolute top-4 right-20 z-20 rounded-full hover:bg-bright-gray dark:hover:bg-[#28292E]"
className="absolute right-20 top-4 z-20 rounded-full hover:bg-bright-gray dark:hover:bg-[#28292E]"
>
<img
className="m-2 h-5 w-5 filter dark:invert"

View File

@@ -45,7 +45,7 @@ export default function Sidebar({
<img className="filter dark:invert" src={Exit} />
</button>
</div>
<div className="flex h-full flex-col items-center gap-2 py-4 px-6 text-center">
<div className="flex h-full flex-col items-center gap-2 px-6 py-4 text-center">
{children}
</div>
</div>

View File

@@ -42,17 +42,17 @@ const SkeletonLoader: React.FC<SkeletonLoaderProps> = ({
<>
{[...Array(4)].map((_, idx) => (
<tr key={idx} className="animate-pulse">
<td className="py-4 px-4 w-[45%]">
<div className="h-4 bg-gray-300 dark:bg-gray-600 rounded w-full"></div>
<td className="w-[45%] px-4 py-4">
<div className="h-4 w-full rounded bg-gray-300 dark:bg-gray-600"></div>
</td>
<td className="py-4 px-4 w-[20%]">
<div className="h-4 bg-gray-300 dark:bg-gray-600 rounded w-full"></div>
<td className="w-[20%] px-4 py-4">
<div className="h-4 w-full rounded bg-gray-300 dark:bg-gray-600"></div>
</td>
<td className="py-4 px-4 w-[25%]">
<div className="h-4 bg-gray-300 dark:bg-gray-600 rounded w-full"></div>
<td className="w-[25%] px-4 py-4">
<div className="h-4 w-full rounded bg-gray-300 dark:bg-gray-600"></div>
</td>
<td className="py-4 px-4 w-[10%]">
<div className="h-4 bg-gray-300 dark:bg-gray-600 rounded w-full"></div>
<td className="w-[10%] px-4 py-4">
<div className="h-4 w-full rounded bg-gray-300 dark:bg-gray-600"></div>
</td>
</tr>
))}
@@ -64,16 +64,16 @@ const SkeletonLoader: React.FC<SkeletonLoaderProps> = ({
{[...Array(4)].map((_, idx) => (
<tr key={idx} className="animate-pulse">
<td className="p-2">
<div className="h-4 bg-gray-300 dark:bg-gray-600 rounded w-3/4 mx-auto"></div>
<div className="mx-auto h-4 w-3/4 rounded bg-gray-300 dark:bg-gray-600"></div>
</td>
<td className="p-2">
<div className="h-4 bg-gray-300 dark:bg-gray-600 rounded w-full mx-auto"></div>
<div className="mx-auto h-4 w-full rounded bg-gray-300 dark:bg-gray-600"></div>
</td>
<td className="p-2">
<div className="h-4 bg-gray-300 dark:bg-gray-600 rounded w-full mx-auto"></div>
<div className="mx-auto h-4 w-full rounded bg-gray-300 dark:bg-gray-600"></div>
</td>
<td className="p-2">
<div className="h-4 bg-gray-300 dark:bg-gray-600 rounded w-8 mx-auto"></div>
<div className="mx-auto h-4 w-8 rounded bg-gray-300 dark:bg-gray-600"></div>
</td>
</tr>
))}
@@ -82,10 +82,10 @@ const SkeletonLoader: React.FC<SkeletonLoaderProps> = ({
const renderDropdown = () => (
<div className="animate-pulse">
<div className="h-4 bg-gray-300 dark:bg-gray-600 rounded w-24 mb-2"></div>
<div className="w-[360px] h-14 bg-gray-300 dark:bg-gray-600 rounded-3xl flex items-center justify-between px-4">
<div className="h-3 bg-gray-400 dark:bg-gray-700 rounded w-24"></div>
<div className="h-3 w-3 bg-gray-400 dark:bg-gray-700 rounded"></div>
<div className="mb-2 h-4 w-24 rounded bg-gray-300 dark:bg-gray-600"></div>
<div className="flex h-14 w-[360px] items-center justify-between rounded-3xl bg-gray-300 px-4 dark:bg-gray-600">
<div className="h-3 w-24 rounded bg-gray-400 dark:bg-gray-700"></div>
<div className="h-3 w-3 rounded bg-gray-400 dark:bg-gray-700"></div>
</div>
</div>
);
@@ -95,14 +95,14 @@ const SkeletonLoader: React.FC<SkeletonLoaderProps> = ({
{[...Array(8)].map((_, idx) => (
<div
key={idx}
className="w-full flex items-start p-2 hover:bg-[#F9F9F9] hover:dark:bg-dark-charcoal"
className="flex w-full items-start p-2 hover:bg-[#F9F9F9] hover:dark:bg-dark-charcoal"
>
<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 className="flex w-full items-center gap-2">
<div className="h-3 w-3 rounded-lg bg-gray-300 dark:bg-gray-600"></div>
<div className="flex w-full flex-row items-center gap-2">
<div className="h-3 w-[30%] rounded-lg bg-gray-300 dark:bg-gray-600 lg:w-52"></div>
<div className="h-3 w-[16%] rounded-lg bg-gray-300 dark:bg-gray-600 lg:w-28"></div>
<div className="h-3 w-[40%] rounded-lg bg-gray-300 dark:bg-gray-600 lg:w-64"></div>
</div>
</div>
</div>
@@ -117,32 +117,32 @@ const SkeletonLoader: React.FC<SkeletonLoaderProps> = ({
key={idx}
className={`p-6 ${
skeletonCount === 1 ? 'w-full' : 'w-60'
} dark:bg-raisin-black rounded-3xl animate-pulse`}
} animate-pulse rounded-3xl dark:bg-raisin-black`}
>
<div className="space-y-4">
<div>
<div className="h-4 bg-gray-300 dark:bg-gray-600 rounded mb-2 w-3/4"></div>
<div className="h-4 bg-gray-300 dark:bg-gray-600 rounded mb-2 w-5/6"></div>
<div className="h-4 bg-gray-300 dark:bg-gray-600 rounded mb-2 w-1/2"></div>
<div className="h-4 bg-gray-300 dark:bg-gray-600 rounded mb-2 w-3/4"></div>
<div className="h-4 bg-gray-300 dark:bg-gray-600 rounded mb-2 w-full"></div>
<div className="mb-2 h-4 w-3/4 rounded bg-gray-300 dark:bg-gray-600"></div>
<div className="mb-2 h-4 w-5/6 rounded bg-gray-300 dark:bg-gray-600"></div>
<div className="mb-2 h-4 w-1/2 rounded bg-gray-300 dark:bg-gray-600"></div>
<div className="mb-2 h-4 w-3/4 rounded bg-gray-300 dark:bg-gray-600"></div>
<div className="mb-2 h-4 w-full rounded bg-gray-300 dark:bg-gray-600"></div>
</div>
<div className="border-t border-gray-400 dark:border-gray-700 my-4"></div>
<div className="my-4 border-t border-gray-400 dark:border-gray-700"></div>
<div>
<div className="h-4 bg-gray-300 dark:bg-gray-600 rounded mb-2 w-2/3"></div>
<div className="h-4 bg-gray-300 dark:bg-gray-600 rounded mb-2 w-1/4"></div>
<div className="h-4 bg-gray-300 dark:bg-gray-600 rounded mb-2 w-full"></div>
<div className="mb-2 h-4 w-2/3 rounded bg-gray-300 dark:bg-gray-600"></div>
<div className="mb-2 h-4 w-1/4 rounded bg-gray-300 dark:bg-gray-600"></div>
<div className="mb-2 h-4 w-full rounded bg-gray-300 dark:bg-gray-600"></div>
</div>
<div className="border-t border-gray-400 dark:border-gray-700 my-4"></div>
<div className="my-4 border-t border-gray-400 dark:border-gray-700"></div>
<div>
<div className="h-4 bg-gray-300 dark:bg-gray-600 rounded mb-2 w-5/6"></div>
<div className="h-4 bg-gray-300 dark:bg-gray-600 rounded mb-2 w-1/3"></div>
<div className="h-4 bg-gray-300 dark:bg-gray-600 rounded mb-2 w-2/3"></div>
<div className="h-4 bg-gray-300 dark:bg-gray-600 rounded mb-2 w-full"></div>
<div className="mb-2 h-4 w-5/6 rounded bg-gray-300 dark:bg-gray-600"></div>
<div className="mb-2 h-4 w-1/3 rounded bg-gray-300 dark:bg-gray-600"></div>
<div className="mb-2 h-4 w-2/3 rounded bg-gray-300 dark:bg-gray-600"></div>
<div className="mb-2 h-4 w-full rounded bg-gray-300 dark:bg-gray-600"></div>
</div>
<div className="border-t border-gray-400 dark:border-gray-700 my-4"></div>
<div className="h-4 bg-gray-300 dark:bg-gray-600 rounded w-3/4 mb-2"></div>
<div className="h-4 bg-gray-300 dark:bg-gray-600 rounded w-5/6 mb-2"></div>
<div className="my-4 border-t border-gray-400 dark:border-gray-700"></div>
<div className="mb-2 h-4 w-3/4 rounded bg-gray-300 dark:bg-gray-600"></div>
<div className="mb-2 h-4 w-5/6 rounded bg-gray-300 dark:bg-gray-600"></div>
</div>
</div>
))}
@@ -154,27 +154,27 @@ const SkeletonLoader: React.FC<SkeletonLoaderProps> = ({
{[...Array(skeletonCount)].map((_, idx) => (
<div
key={idx}
className="p-6 w-full dark:bg-raisin-black rounded-3xl animate-pulse"
className="w-full animate-pulse rounded-3xl p-6 dark:bg-raisin-black"
>
<div className="space-y-6">
<div className="space-y-2">
<div className="h-4 bg-gray-300 dark:bg-gray-600 rounded w-1/3 mb-4"></div>
<div className="grid grid-cols-6 gap-2 items-end">
<div className="h-32 bg-gray-300 dark:bg-gray-600 rounded"></div>
<div className="h-24 bg-gray-300 dark:bg-gray-600 rounded"></div>
<div className="h-40 bg-gray-300 dark:bg-gray-600 rounded"></div>
<div className="h-28 bg-gray-300 dark:bg-gray-600 rounded"></div>
<div className="h-36 bg-gray-300 dark:bg-gray-600 rounded"></div>
<div className="h-20 bg-gray-300 dark:bg-gray-600 rounded"></div>
<div className="mb-4 h-4 w-1/3 rounded bg-gray-300 dark:bg-gray-600"></div>
<div className="grid grid-cols-6 items-end gap-2">
<div className="h-32 rounded bg-gray-300 dark:bg-gray-600"></div>
<div className="h-24 rounded bg-gray-300 dark:bg-gray-600"></div>
<div className="h-40 rounded bg-gray-300 dark:bg-gray-600"></div>
<div className="h-28 rounded bg-gray-300 dark:bg-gray-600"></div>
<div className="h-36 rounded bg-gray-300 dark:bg-gray-600"></div>
<div className="h-20 rounded bg-gray-300 dark:bg-gray-600"></div>
</div>
</div>
<div className="space-y-2">
<div className="h-4 bg-gray-300 dark:bg-gray-600 rounded w-1/4 mb-4"></div>
<div className="h-32 bg-gray-300 dark:bg-gray-600 rounded"></div>
<div className="mb-4 h-4 w-1/4 rounded bg-gray-300 dark:bg-gray-600"></div>
<div className="h-32 rounded bg-gray-300 dark:bg-gray-600"></div>
</div>
<div className="grid grid-cols-2 gap-4">
<div className="h-4 bg-gray-300 dark:bg-gray-600 rounded w-full"></div>
<div className="h-4 bg-gray-300 dark:bg-gray-600 rounded w-full"></div>
<div className="h-4 w-full rounded bg-gray-300 dark:bg-gray-600"></div>
<div className="h-4 w-full rounded bg-gray-300 dark:bg-gray-600"></div>
</div>
</div>
</div>

View File

@@ -32,8 +32,13 @@ export default function SourcesPopup({
const { t } = useTranslation();
const popupRef = useRef<HTMLDivElement>(null);
const [searchTerm, setSearchTerm] = useState('');
const [popupPosition, setPopupPosition] = useState({ top: 0, left: 0, maxHeight: 0, showAbove: false });
const [popupPosition, setPopupPosition] = useState({
top: 0,
left: 0,
maxHeight: 0,
showAbove: false,
});
const embeddingsName =
import.meta.env.VITE_EMBEDDINGS_NAME ||
'huggingface_sentence-transformers/all-mpnet-base-v2';
@@ -41,16 +46,16 @@ export default function SourcesPopup({
const options = useSelector(selectSourceDocs);
const selectedDocs = useSelector(selectSelectedDocs);
const filteredOptions = options?.filter(option =>
option.name.toLowerCase().includes(searchTerm.toLowerCase())
const filteredOptions = options?.filter((option) =>
option.name.toLowerCase().includes(searchTerm.toLowerCase()),
);
useLayoutEffect(() => {
if (!isOpen || !anchorRef.current) return;
const updatePosition = () => {
if (!anchorRef.current) return;
const rect = anchorRef.current.getBoundingClientRect();
const viewportHeight = window.innerHeight;
const viewportWidth = window.innerWidth;
@@ -60,17 +65,17 @@ export default function SourcesPopup({
const maxHeight = showAbove ? spaceAbove - 16 : spaceBelow - 16;
const left = Math.min(
rect.left,
viewportWidth - Math.min(480, viewportWidth * 0.95) - 10
viewportWidth - Math.min(480, viewportWidth * 0.95) - 10,
);
setPopupPosition({
top: showAbove ? rect.top - 8 : rect.bottom + 8,
left,
maxHeight: Math.min(600, maxHeight),
showAbove
showAbove,
});
};
updatePosition();
window.addEventListener('resize', updatePosition);
return () => window.removeEventListener('resize', updatePosition);
@@ -111,10 +116,12 @@ export default function SourcesPopup({
return (
<div
ref={popupRef}
className="fixed z-50 bg-lotion dark:bg-charleston-green-2 rounded-xl shadow-[0px_9px_46px_8px_#0000001F,0px_24px_38px_3px_#00000024,0px_11px_15px_-7px_#00000033] flex flex-col"
className="fixed z-50 flex flex-col rounded-xl bg-lotion shadow-[0px_9px_46px_8px_#0000001F,0px_24px_38px_3px_#00000024,0px_11px_15px_-7px_#00000033] dark:bg-charleston-green-2"
style={{
top: popupPosition.showAbove ? popupPosition.top : undefined,
bottom: popupPosition.showAbove ? undefined : window.innerHeight - popupPosition.top,
bottom: popupPosition.showAbove
? undefined
: window.innerHeight - popupPosition.top,
left: popupPosition.left,
maxWidth: Math.min(480, window.innerWidth * 0.95),
width: '100%',
@@ -122,12 +129,12 @@ export default function SourcesPopup({
transform: popupPosition.showAbove ? 'translateY(-100%)' : 'none',
}}
>
<div className="flex flex-col h-full">
<div className="px-4 md:px-6 py-4 flex-shrink-0">
<h2 className="text-lg font-bold text-[#141414] dark:text-bright-gray mb-4 dark:text-[20px]">
<div className="flex h-full flex-col">
<div className="flex-shrink-0 px-4 py-4 md:px-6">
<h2 className="mb-4 text-lg font-bold text-[#141414] dark:text-[20px] dark:text-bright-gray">
{t('conversation.sources.text')}
</h2>
<Input
id="source-search"
name="source-search"
@@ -141,7 +148,7 @@ export default function SourcesPopup({
/>
</div>
<div className="flex-grow overflow-y-auto mx-4 border border-[#D9D9D9] dark:border-dim-gray rounded-md [&::-webkit-scrollbar-thumb]:bg-[#888] [&::-webkit-scrollbar-thumb]:hover:bg-[#555] [&::-webkit-scrollbar-track]:bg-[#E2E8F0] dark:[&::-webkit-scrollbar-track]:bg-[#2C2E3C]">
<div className="mx-4 flex-grow overflow-y-auto rounded-md border border-[#D9D9D9] dark:border-dim-gray [&::-webkit-scrollbar-thumb]:bg-[#888] [&::-webkit-scrollbar-thumb]:hover:bg-[#555] [&::-webkit-scrollbar-track]:bg-[#E2E8F0] dark:[&::-webkit-scrollbar-track]:bg-[#2C2E3C]">
{options ? (
<>
{filteredOptions?.map((option: any, index: number) => {
@@ -149,7 +156,7 @@ export default function SourcesPopup({
return (
<div
key={index}
className="flex cursor-pointer items-center p-3 hover:bg-gray-100 dark:hover:bg-[#2C2E3C] transition-colors border-b border-[#D9D9D9] dark:border-dim-gray border-opacity-80 dark:text-[14px]"
className="flex cursor-pointer items-center border-b border-[#D9D9D9] border-opacity-80 p-3 transition-colors hover:bg-gray-100 dark:border-dim-gray dark:text-[14px] dark:hover:bg-[#2C2E3C]"
onClick={() => {
dispatch(setSelectedDocs(option));
handlePostDocumentSelect(option);
@@ -159,23 +166,26 @@ export default function SourcesPopup({
<img
src={SourceIcon}
alt="Source"
width={14} height={14}
width={14}
height={14}
className="mr-3 flex-shrink-0"
/>
<span className="text-[#5D5D5D] dark:text-bright-gray font-medium flex-grow overflow-hidden overflow-ellipsis whitespace-nowrap mr-3">
<span className="mr-3 flex-grow overflow-hidden overflow-ellipsis whitespace-nowrap font-medium text-[#5D5D5D] dark:text-bright-gray">
{option.name}
</span>
<div className={`w-4 h-4 border flex-shrink-0 flex items-center justify-center p-[0.5px] dark:border-[#757783] border-[#C6C6C6]`}>
{selectedDocs &&
(option.id ?
selectedDocs.id === option.id : // For documents with MongoDB IDs
selectedDocs.date === option.date) && // For preloaded sources
<img
src={CheckIcon}
alt="Selected"
className="h-3 w-3"
/>
}
<div
className={`flex h-4 w-4 flex-shrink-0 items-center justify-center border border-[#C6C6C6] p-[0.5px] dark:border-[#757783]`}
>
{selectedDocs &&
(option.id
? selectedDocs.id === option.id // For documents with MongoDB IDs
: selectedDocs.date === option.date) && ( // For preloaded sources
<img
src={CheckIcon}
alt="Selected"
className="h-3 w-3"
/>
)}
</div>
</div>
);
@@ -183,14 +193,22 @@ export default function SourcesPopup({
return null;
})}
<div
className="flex cursor-pointer items-center p-3 hover:bg-gray-100 dark:hover:bg-[#2C2E3C] transition-colors border-b border-[#D9D9D9] dark:border-dim-gray border-opacity-80 dark:text-[14px]"
className="flex cursor-pointer items-center border-b border-[#D9D9D9] border-opacity-80 p-3 transition-colors hover:bg-gray-100 dark:border-dim-gray dark:text-[14px] dark:hover:bg-[#2C2E3C]"
onClick={handleEmptyDocumentSelect}
>
<img width={14} height={14} src={SourceIcon} alt="Source" className="mr-3 flex-shrink-0" />
<span className="text-[#5D5D5D] dark:text-bright-gray font-medium flex-grow mr-3">
<img
width={14}
height={14}
src={SourceIcon}
alt="Source"
className="mr-3 flex-shrink-0"
/>
<span className="mr-3 flex-grow font-medium text-[#5D5D5D] dark:text-bright-gray">
{t('none')}
</span>
<div className={`w-4 h-4 border flex-shrink-0 flex items-center justify-center p-[0.5px] dark:border-[#757783] border-[#C6C6C6]`}>
<div
className={`flex h-4 w-4 flex-shrink-0 items-center justify-center border border-[#C6C6C6] p-[0.5px] dark:border-[#757783]`}
>
{selectedDocs === null && (
<img src={CheckIcon} alt="Selected" className="h-3 w-3" />
)}
@@ -198,27 +216,27 @@ export default function SourcesPopup({
</div>
</>
) : (
<div className="p-4 text-center text-gray-500 dark:text-bright-gray dark:text-[14px]">
<div className="p-4 text-center text-gray-500 dark:text-[14px] dark:text-bright-gray">
{t('noSourcesAvailable')}
</div>
)}
</div>
<div className="px-4 md:px-6 py-4 opacity-75 hover:opacity-100 transition-opacity duration-200 flex-shrink-0">
<a
href="/settings/documents"
className="text-violets-are-blue text-base font-medium inline-flex items-center gap-2"
<div className="flex-shrink-0 px-4 py-4 opacity-75 transition-opacity duration-200 hover:opacity-100 md:px-6">
<a
href="/settings/documents"
className="inline-flex items-center gap-2 text-base font-medium text-violets-are-blue"
onClick={onClose}
>
Go to Documents
<img src={RedirectIcon} alt="Redirect" className="w-3 h-3" />
<img src={RedirectIcon} alt="Redirect" className="h-3 w-3" />
</a>
</div>
<div className="px-4 md:px-6 py-3 flex justify-start flex-shrink-0">
<div className="flex flex-shrink-0 justify-start px-4 py-3 md:px-6">
<button
onClick={handleUploadClick}
className="py-2 px-4 rounded-full border text-violets-are-blue hover:bg-violets-are-blue border-violets-are-blue hover:text-white transition-colors duration-200 text-[14px] font-medium w-auto"
className="w-auto rounded-full border border-violets-are-blue px-4 py-2 text-[14px] font-medium text-violets-are-blue transition-colors duration-200 hover:bg-violets-are-blue hover:text-white"
>
Upload new
</button>

View File

@@ -46,9 +46,9 @@ const ToggleSwitch: React.FC<ToggleSwitchProps> = ({
return (
<label
className={`cursor-pointer select-none flex flex-row items-center ${
className={`flex cursor-pointer select-none flex-row items-center ${
labelPosition === 'right' ? 'flex-row-reverse' : ''
} ${disabled ? 'opacity-50 cursor-not-allowed' : ''} ${className}`}
} ${disabled ? 'cursor-not-allowed opacity-50' : ''} ${className}`}
>
{label && (
<span
@@ -75,7 +75,7 @@ const ToggleSwitch: React.FC<ToggleSwitchProps> = ({
}`}
></div>
<div
className={`absolute ${toggle} flex items-center justify-center rounded-full transition bg-white opacity-80 ${
className={`absolute ${toggle} flex items-center justify-center rounded-full bg-white opacity-80 transition ${
checked ? `${translate} bg-silver` : ''
}`}
></div>

View File

@@ -29,18 +29,23 @@ export default function ToolsPopup({
const [searchTerm, setSearchTerm] = useState('');
const [isDarkTheme] = useDarkTheme();
const popupRef = useRef<HTMLDivElement>(null);
const [popupPosition, setPopupPosition] = useState({ top: 0, left: 0, maxHeight: 0, showAbove: false });
const [popupPosition, setPopupPosition] = useState({
top: 0,
left: 0,
maxHeight: 0,
showAbove: false,
});
useLayoutEffect(() => {
if (!isOpen || !anchorRef.current) return;
const updatePosition = () => {
if (!anchorRef.current) return;
const rect = anchorRef.current.getBoundingClientRect();
const viewportHeight = window.innerHeight;
const viewportWidth = window.innerWidth;
const spaceAbove = rect.top;
const spaceBelow = viewportHeight - rect.bottom;
const showAbove = spaceAbove > spaceBelow && spaceAbove >= 300;
@@ -48,17 +53,17 @@ export default function ToolsPopup({
const left = Math.min(
rect.left,
viewportWidth - Math.min(462, viewportWidth * 0.95) - 10
viewportWidth - Math.min(462, viewportWidth * 0.95) - 10,
);
setPopupPosition({
top: showAbove ? rect.top - 8 : rect.bottom + 8,
left,
maxHeight: Math.min(600, maxHeight),
showAbove
showAbove,
});
};
updatePosition();
window.addEventListener('resize', updatePosition);
return () => window.removeEventListener('resize', updatePosition);
@@ -125,16 +130,18 @@ export default function ToolsPopup({
if (!isOpen) return null;
const filteredTools = userTools.filter((tool) =>
tool.displayName.toLowerCase().includes(searchTerm.toLowerCase())
tool.displayName.toLowerCase().includes(searchTerm.toLowerCase()),
);
return (
<div
ref={popupRef}
className="fixed z-[9999] rounded-lg border border-light-silver dark:border-dim-gray bg-lotion dark:bg-charleston-green-2 shadow-[0px_9px_46px_8px_#0000001F,0px_24px_38px_3px_#00000024,0px_11px_15px_-7px_#00000033]"
className="fixed z-[9999] rounded-lg border border-light-silver bg-lotion shadow-[0px_9px_46px_8px_#0000001F,0px_24px_38px_3px_#00000024,0px_11px_15px_-7px_#00000033] dark:border-dim-gray dark:bg-charleston-green-2"
style={{
top: popupPosition.showAbove ? popupPosition.top : undefined,
bottom: popupPosition.showAbove ? undefined : window.innerHeight - popupPosition.top,
bottom: popupPosition.showAbove
? undefined
: window.innerHeight - popupPosition.top,
left: popupPosition.left,
maxWidth: Math.min(462, window.innerWidth * 0.95),
width: '100%',
@@ -142,9 +149,9 @@ export default function ToolsPopup({
transform: popupPosition.showAbove ? 'translateY(-100%)' : 'none',
}}
>
<div className="flex flex-col h-full">
<div className="p-4 flex-shrink-0">
<h3 className="text-lg font-medium text-gray-900 dark:text-white mb-4">
<div className="flex h-full flex-col">
<div className="flex-shrink-0 p-4">
<h3 className="mb-4 text-lg font-medium text-gray-900 dark:text-white">
{t('settings.tools.label')}
</h3>
@@ -162,20 +169,20 @@ export default function ToolsPopup({
</div>
{loading ? (
<div className="flex justify-center py-4 flex-grow">
<div className="animate-spin rounded-full h-6 w-6 border-b-2 border-gray-900 dark:border-white"></div>
<div className="flex flex-grow justify-center py-4">
<div className="h-6 w-6 animate-spin rounded-full border-b-2 border-gray-900 dark:border-white"></div>
</div>
) : (
<div className="mx-4 border border-[#D9D9D9] dark:border-dim-gray rounded-md overflow-hidden flex-grow">
<div className="mx-4 flex-grow overflow-hidden rounded-md border border-[#D9D9D9] dark:border-dim-gray">
<div className="h-full overflow-y-auto [&::-webkit-scrollbar-thumb]:bg-[#888] [&::-webkit-scrollbar-thumb]:hover:bg-[#555] [&::-webkit-scrollbar-track]:bg-[#E2E8F0] dark:[&::-webkit-scrollbar-track]:bg-[#2C2E3C]">
{filteredTools.length === 0 ? (
<div className="flex flex-col items-center justify-center h-full py-8">
<div className="flex h-full flex-col items-center justify-center py-8">
<img
src={isDarkTheme ? NoFilesDarkIcon : NoFilesIcon}
alt="No tools found"
className="h-24 w-24 mx-auto mb-4"
className="mx-auto mb-4 h-24 w-24"
/>
<p className="text-gray-500 dark:text-gray-400 text-center">
<p className="text-center text-gray-500 dark:text-gray-400">
{t('settings.tools.noToolsFound')}
</p>
</div>
@@ -184,22 +191,24 @@ export default function ToolsPopup({
<div
key={tool.id}
onClick={() => updateToolStatus(tool.id, !tool.status)}
className="flex items-center justify-between p-3 border-b border-[#D9D9D9] dark:border-dim-gray hover:bg-gray-100 dark:hover:bg-charleston-green-3"
className="flex items-center justify-between border-b border-[#D9D9D9] p-3 hover:bg-gray-100 dark:border-dim-gray dark:hover:bg-charleston-green-3"
>
<div className="flex items-center flex-grow mr-3">
<div className="mr-3 flex flex-grow items-center">
<img
src={`/toolIcons/tool_${tool.name}.svg`}
alt={`${tool.displayName} icon`}
className="h-5 w-5 mr-4 flex-shrink-0"
className="mr-4 h-5 w-5 flex-shrink-0"
/>
<div className="overflow-hidden">
<p className="text-xs font-medium text-gray-900 dark:text-white overflow-hidden overflow-ellipsis whitespace-nowrap">
<p className="overflow-hidden overflow-ellipsis whitespace-nowrap text-xs font-medium text-gray-900 dark:text-white">
{tool.displayName}
</p>
</div>
</div>
<div className="flex items-center flex-shrink-0">
<div className={`w-4 h-4 border flex items-center justify-center p-[0.5px] dark:border-[#757783] border-[#C6C6C6]`}>
<div className="flex flex-shrink-0 items-center">
<div
className={`flex h-4 w-4 items-center justify-center border border-[#C6C6C6] p-[0.5px] dark:border-[#757783]`}
>
{tool.status && (
<img
src={CheckmarkIcon}
@@ -217,10 +226,10 @@ export default function ToolsPopup({
</div>
)}
<div className="p-4 flex-shrink-0 opacity-75 hover:opacity-100 transition-opacity duration-200">
<div className="flex-shrink-0 p-4 opacity-75 transition-opacity duration-200 hover:opacity-100">
<a
href="/settings/tools"
className="text-base text-purple-30 font-medium inline-flex items-center"
className="inline-flex items-center text-base font-medium text-purple-30"
>
{t('settings.tools.manageTools')}
<img

View File

@@ -23,3 +23,8 @@ export type InputProps = {
e: React.KeyboardEvent<HTMLTextAreaElement | HTMLInputElement>,
) => void;
};
export type MermaidRendererProps = {
code: string;
isLoading?: boolean;
};

View File

@@ -1,11 +1,14 @@
import 'katex/dist/katex.min.css';
import { forwardRef, useRef, useState } from 'react';
import { forwardRef, Fragment, useRef, useState } from 'react';
import { useTranslation } from 'react-i18next';
import ReactMarkdown from 'react-markdown';
import { useSelector } from 'react-redux';
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
import { oneLight, vscDarkPlus } from 'react-syntax-highlighter/dist/cjs/styles/prism';
import {
oneLight,
vscDarkPlus,
} from 'react-syntax-highlighter/dist/cjs/styles/prism';
import rehypeKatex from 'rehype-katex';
import remarkGfm from 'remark-gfm';
import remarkMath from 'remark-math';
@@ -26,10 +29,14 @@ import CopyButton from '../components/CopyButton';
import Sidebar from '../components/Sidebar';
import SpeakButton from '../components/TextToSpeechButton';
import { useDarkTheme, useOutsideAlerter } from '../hooks';
import { selectChunks, selectSelectedDocs } from '../preferences/preferenceSlice';
import {
selectChunks,
selectSelectedDocs,
} from '../preferences/preferenceSlice';
import classes from './ConversationBubble.module.css';
import { FEEDBACK, MESSAGE_TYPE } from './conversationModels';
import { ToolCallsType } from './types';
import MermaidRenderer from '../components/MermaidRenderer';
const DisableSourceFE = import.meta.env.VITE_DISABLE_SOURCE_FE || false;
@@ -46,6 +53,7 @@ const ConversationBubble = forwardRef<
toolCalls?: ToolCallsType[];
retryBtn?: React.ReactElement;
questionNumber?: number;
isStreaming?: boolean;
handleUpdatedQuestionSubmission?: (
updatedquestion?: string,
updated?: boolean,
@@ -64,6 +72,7 @@ const ConversationBubble = forwardRef<
toolCalls,
retryBtn,
questionNumber,
isStreaming,
handleUpdatedQuestionSubmission,
},
ref,
@@ -103,19 +112,19 @@ const ConversationBubble = forwardRef<
>
<Avatar
size="SMALL"
className="mt-2 text-2xl flex-shrink-0"
className="mt-2 flex-shrink-0 text-2xl"
avatar={
<img className="rounded-full mr-1" width={30} src={UserIcon} />
<img className="mr-1 rounded-full" width={30} src={UserIcon} />
}
/>
{!isEditClicked && (
<>
<div className="flex flex-col mr-2">
<div className="mr-2 flex flex-col">
<div
style={{
wordBreak: 'break-word',
}}
className="text-sm sm:text-base ml-2 mr-2 flex items-center rounded-[28px] bg-gradient-to-b from-medium-purple to-slate-blue py-[14px] px-[19px] text-white max-w-full whitespace-pre-wrap leading-normal"
className="ml-2 mr-2 flex max-w-full items-center whitespace-pre-wrap rounded-[28px] bg-gradient-to-b from-medium-purple to-slate-blue px-[19px] py-[14px] text-sm leading-normal text-white sm:text-base"
>
{message}
</div>
@@ -125,7 +134,7 @@ const ConversationBubble = forwardRef<
setIsEditClicked(true);
setEditInputBox(message ?? '');
}}
className={`flex-shrink-0 h-fit mt-3 p-2 cursor-pointer rounded-full hover:bg-light-silver dark:hover:bg-[#35363B] flex items-center ${isQuestionHovered || isEditClicked ? 'visible' : 'invisible'}`}
className={`mt-3 flex h-fit flex-shrink-0 cursor-pointer items-center rounded-full p-2 hover:bg-light-silver dark:hover:bg-[#35363B] ${isQuestionHovered || isEditClicked ? 'visible' : 'invisible'}`}
>
<img src={Edit} alt="Edit" className="cursor-pointer" />
</button>
@@ -134,7 +143,7 @@ const ConversationBubble = forwardRef<
{isEditClicked && (
<div
ref={editableQueryRef}
className="w-full mx-auto bg-transparent p-4 rounded-lg flex flex-col gap-4"
className="mx-auto flex w-full flex-col gap-4 rounded-lg bg-transparent p-4"
>
<textarea
placeholder={t('conversation.edit.placeholder')}
@@ -149,17 +158,17 @@ const ConversationBubble = forwardRef<
}}
rows={5}
value={editInputBox}
className="w-full resize-none border border-silver dark:border-philippine-grey rounded-3xl px-4 py-3 text-base leading-relaxed text-carbon dark:text-chinese-white dark:bg-raisin-black focus:outline-none"
className="w-full resize-none rounded-3xl border border-silver px-4 py-3 text-base leading-relaxed text-carbon focus:outline-none dark:border-philippine-grey dark:bg-raisin-black dark:text-chinese-white"
/>
<div className="flex items-center justify-end gap-2">
<button
className="px-4 py-2 text-purple-30 text-sm font-semibold hover:text-chinese-black-2 dark:hover:text-[#B9BCBE] hover:bg-gainsboro dark:hover:bg-onyx-2 transition-colors rounded-full"
className="rounded-full px-4 py-2 text-sm font-semibold text-purple-30 transition-colors hover:bg-gainsboro hover:text-chinese-black-2 dark:hover:bg-onyx-2 dark:hover:text-[#B9BCBE]"
onClick={() => setIsEditClicked(false)}
>
{t('conversation.edit.cancel')}
</button>
<button
className="rounded-full bg-purple-30 hover:bg-violets-are-blue dark:hover:bg-royal-purple px-4 py-2 text-white text-sm font-medium transition-colors"
className="rounded-full bg-purple-30 px-4 py-2 text-sm font-medium text-white transition-colors hover:bg-violets-are-blue dark:hover:bg-royal-purple"
onClick={handleEditClick}
>
{t('conversation.edit.update')}
@@ -186,10 +195,40 @@ const ConversationBubble = forwardRef<
return inlineProcessedContent;
};
const processMarkdownContent = (content: string) => {
const processedContent = preprocessLaTeX(content);
const contentSegments: Array<{
type: 'text' | 'mermaid';
content: string;
}> = [];
let lastIndex = 0;
const regex = /```mermaid\n([\s\S]*?)```/g;
let match;
while ((match = regex.exec(processedContent)) !== null) {
const textBefore = processedContent.substring(lastIndex, match.index);
if (textBefore) {
contentSegments.push({ type: 'text', content: textBefore });
}
contentSegments.push({ type: 'mermaid', content: match[1].trim() });
lastIndex = match.index + match[0].length;
}
const textAfter = processedContent.substring(lastIndex);
if (textAfter) {
contentSegments.push({ type: 'text', content: textAfter });
}
return contentSegments;
};
bubble = (
<div
ref={ref}
className={`flex flex-wrap self-start ${className} group flex-col dark:text-bright-gray`}
className={`flex flex-wrap self-start ${className} group flex-col dark:text-bright-gray`}
>
{DisableSourceFE ||
type === 'ERROR' ||
@@ -331,7 +370,7 @@ const ConversationBubble = forwardRef<
<Thought thought={thought} preprocessLaTeX={preprocessLaTeX} />
)}
{message && (
<div className="flex flex-col flex-wrap items-start self-start lg:flex-nowrap">
<div className="flex max-w-full flex-col flex-wrap items-start self-start lg:flex-nowrap">
<div className="my-2 flex flex-row items-center justify-center gap-3">
<Avatar
className="h-[34px] w-[34px] text-2xl"
@@ -348,120 +387,158 @@ const ConversationBubble = forwardRef<
</p>
</div>
<div
className={`fade-in-bubble ml-2 mr-5 flex max-w-[90vw] rounded-[28px] bg-gray-1000 py-[18px] px-7 dark:bg-gun-metal md:max-w-[70vw] lg:max-w-[50vw] ${
className={`fade-in-bubble mr-5 flex max-w-full rounded-[28px] bg-gray-1000 px-7 py-[18px] dark:bg-gun-metal ${
type === 'ERROR'
? 'relative flex-row items-center rounded-full border border-transparent bg-[#FFE7E7] p-2 py-5 text-sm font-normal text-red-3000 dark:border-red-2000 dark:text-white'
? 'relative flex-row items-center rounded-full border border-transparent bg-[#FFE7E7] p-2 py-5 text-sm font-normal text-red-3000 dark:border-red-2000 dark:text-white'
: 'flex-col rounded-3xl'
}`}
>
<ReactMarkdown
className="fade-in whitespace-pre-wrap break-words leading-normal"
remarkPlugins={[remarkGfm, remarkMath]}
rehypePlugins={[rehypeKatex]}
components={{
code(props) {
const { children, className, node, ref, ...rest } = props;
const match = /language-(\w+)/.exec(className || '');
const language = match ? match[1] : '';
{(() => {
const contentSegments = processMarkdownContent(message);
return (
<>
{contentSegments.map((segment, index) => (
<Fragment key={index}>
{segment.type === 'text' ? (
<ReactMarkdown
className="fade-in whitespace-pre-wrap break-words leading-normal"
remarkPlugins={[remarkGfm, remarkMath]}
rehypePlugins={[rehypeKatex]}
components={{
code(props) {
const {
children,
className,
node,
ref,
...rest
} = props;
const match = /language-(\w+)/.exec(
className || '',
);
const language = match ? match[1] : '';
return match ? (
<div className="group relative rounded-[14px] overflow-hidden border border-light-silver dark:border-raisin-black">
<div className="flex justify-between items-center px-2 py-1 bg-platinum dark:bg-eerie-black-2">
<span className="text-xs font-medium text-just-black dark:text-chinese-white">
{language}
</span>
<CopyButton
textToCopy={String(children).replace(/\n$/, '')}
/>
</div>
<SyntaxHighlighter
{...rest}
PreTag="div"
language={language}
style={isDarkTheme ? vscDarkPlus : oneLight}
className="!mt-0"
customStyle={{
margin: 0,
borderRadius: 0,
scrollbarWidth: 'thin',
}}
>
{String(children).replace(/\n$/, '')}
</SyntaxHighlighter>
</div>
) : (
<code className="whitespace-pre-line rounded-[6px] bg-gray-200 px-[8px] py-[4px] text-xs font-normal dark:bg-independence dark:text-bright-gray">
{children}
</code>
);
},
ul({ children }) {
return (
<ul
className={`list-inside list-disc whitespace-normal pl-4 ${classes.list}`}
>
{children}
</ul>
);
},
ol({ children }) {
return (
<ol
className={`list-inside list-decimal whitespace-normal pl-4 ${classes.list}`}
>
{children}
</ol>
);
},
table({ children }) {
return (
<div className="relative overflow-x-auto rounded-lg border border-silver/40 dark:border-silver/40">
<table className="w-full text-left text-gray-700 dark:text-bright-gray">
{children}
</table>
</div>
);
},
thead({ children }) {
return (
<thead className="text-xs uppercase text-gray-900 dark:text-bright-gray bg-gray-50 dark:bg-[#26272E]/50">
{children}
</thead>
);
},
tr({ children }) {
return (
<tr className="border-b border-gray-200 dark:border-silver/40 odd:bg-white dark:odd:bg-[#26272E] even:bg-gray-50 dark:even:bg-[#26272E]/50">
{children}
</tr>
);
},
th({ children }) {
return <th className="px-6 py-3">{children}</th>;
},
td({ children }) {
return <td className="px-6 py-3">{children}</td>;
},
}}
>
{preprocessLaTeX(message)}
</ReactMarkdown>
return match ? (
<div className="group relative overflow-hidden rounded-[14px] border border-light-silver dark:border-raisin-black">
<div className="flex items-center justify-between bg-platinum px-2 py-1 dark:bg-eerie-black-2">
<span className="text-xs font-medium text-just-black dark:text-chinese-white">
{language}
</span>
<CopyButton
textToCopy={String(children).replace(
/\n$/,
'',
)}
/>
</div>
<SyntaxHighlighter
{...rest}
PreTag="div"
language={language}
style={
isDarkTheme ? vscDarkPlus : oneLight
}
className="!mt-0"
customStyle={{
margin: 0,
borderRadius: 0,
scrollbarWidth: 'thin',
}}
>
{String(children).replace(/\n$/, '')}
</SyntaxHighlighter>
</div>
) : (
<code className="whitespace-pre-line rounded-[6px] bg-gray-200 px-[8px] py-[4px] text-xs font-normal dark:bg-independence dark:text-bright-gray">
{children}
</code>
);
},
ul({ children }) {
return (
<ul
className={`list-inside list-disc whitespace-normal pl-4 ${classes.list}`}
>
{children}
</ul>
);
},
ol({ children }) {
return (
<ol
className={`list-inside list-decimal whitespace-normal pl-4 ${classes.list}`}
>
{children}
</ol>
);
},
table({ children }) {
return (
<div className="relative overflow-x-auto rounded-lg border border-silver/40 dark:border-silver/40">
<table className="w-full text-left text-gray-700 dark:text-bright-gray">
{children}
</table>
</div>
);
},
thead({ children }) {
return (
<thead className="bg-gray-50 text-xs uppercase text-gray-900 dark:bg-[#26272E]/50 dark:text-bright-gray">
{children}
</thead>
);
},
tr({ children }) {
return (
<tr className="border-b border-gray-200 odd:bg-white even:bg-gray-50 dark:border-silver/40 dark:odd:bg-[#26272E] dark:even:bg-[#26272E]/50">
{children}
</tr>
);
},
th({ children }) {
return (
<th className="px-6 py-3">{children}</th>
);
},
td({ children }) {
return (
<td className="px-6 py-3">{children}</td>
);
},
}}
>
{segment.content}
</ReactMarkdown>
) : (
<div
className="my-4 w-full"
style={{ minWidth: '100%' }}
>
<MermaidRenderer
code={segment.content}
isLoading={isStreaming}
/>
</div>
)}
</Fragment>
))}
</>
);
})()}
</div>
</div>
)}
{message && (
<div className="my-2 ml-2 flex justify-start">
<div
className={`relative mr-2 block items-center justify-center lg:invisible
${type !== 'ERROR' ? 'group-hover:lg:visible' : 'hidden'}`}
className={`relative mr-2 block items-center justify-center lg:invisible ${type !== 'ERROR' ? 'group-hover:lg:visible' : 'hidden'}`}
>
<div>
<CopyButton textToCopy={message} />
</div>
</div>
<div
className={`relative mr-2 block items-center justify-center lg:invisible
${type !== 'ERROR' ? 'group-hover:lg:visible' : 'hidden'}`}
className={`relative mr-2 block items-center justify-center lg:invisible ${type !== 'ERROR' ? 'group-hover:lg:visible' : 'hidden'}`}
>
<div>
<SpeakButton text={message} />
@@ -479,8 +556,7 @@ const ConversationBubble = forwardRef<
feedback === 'LIKE' || isLikeClicked
? 'visible'
: 'lg:invisible'
} ${type !== 'ERROR' ? 'group-hover:lg:visible' : ''}
${feedback === 'DISLIKE' && type !== 'ERROR' ? 'hidden' : ''}`}
} ${type !== 'ERROR' ? 'group-hover:lg:visible' : ''} ${feedback === 'DISLIKE' && type !== 'ERROR' ? 'hidden' : ''}`}
>
<div>
<div
@@ -491,12 +567,11 @@ const ConversationBubble = forwardRef<
}`}
>
<Like
className={`cursor-pointer
${
isLikeClicked || feedback === 'LIKE'
? 'fill-white-3000 stroke-purple-30 dark:fill-transparent'
: 'fill-none stroke-gray-4000'
}`}
className={`cursor-pointer ${
isLikeClicked || feedback === 'LIKE'
? 'fill-white-3000 stroke-purple-30 dark:fill-transparent'
: 'fill-none stroke-gray-4000'
}`}
onClick={() => {
if (feedback === undefined || feedback === null) {
handleFeedback?.('LIKE');
@@ -520,8 +595,7 @@ const ConversationBubble = forwardRef<
feedback === 'DISLIKE' || isLikeClicked
? 'visible'
: 'lg:invisible'
} ${type !== 'ERROR' ? 'group-hover:lg:visible' : ''}
${feedback === 'LIKE' && type !== 'ERROR' ? 'hidden' : ''}`}
} ${type !== 'ERROR' ? 'group-hover:lg:visible' : ''} ${feedback === 'LIKE' && type !== 'ERROR' ? 'hidden' : ''}`}
>
<div>
<div
@@ -535,7 +609,7 @@ const ConversationBubble = forwardRef<
className={`cursor-pointer ${
isDislikeClicked || feedback === 'DISLIKE'
? 'fill-white-3000 stroke-red-2000 dark:fill-transparent'
: 'fill-none stroke-gray-4000'
: 'fill-none stroke-gray-4000'
}`}
onClick={() => {
if (feedback === undefined || feedback === null) {
@@ -583,7 +657,7 @@ function AllSources(sources: AllSourcesProps) {
<div className="h-full w-full">
<div className="w-full">
<p className="text-left text-xl">{`${sources.sources.length} Sources`}</p>
<div className="mx-1 mt-2 h-[0.8px] w-full rounded-full bg-[#C4C4C4]/40 lg:w-[95%] "></div>
<div className="mx-1 mt-2 h-[0.8px] w-full rounded-full bg-[#C4C4C4]/40 lg:w-[95%]"></div>
</div>
<div className="mt-6 flex h-[90%] w-60 flex-col items-center gap-4 overflow-y-auto sm:w-80">
{sources.sources.map((source, index) => (
@@ -624,7 +698,7 @@ export default ConversationBubble;
function ToolCalls({ toolCalls }: { toolCalls: ToolCallsType[] }) {
const [isToolCallsOpen, setIsToolCallsOpen] = useState(false);
return (
<div className="mb-4 w-full flex flex-col flex-wrap items-start self-start lg:flex-nowrap">
<div className="mb-4 flex w-full flex-col flex-wrap items-start self-start lg:flex-nowrap">
<div className="my-2 flex flex-row items-center justify-center gap-3">
<Avatar
className="h-[26px] w-[30px] text-xl"
@@ -655,12 +729,12 @@ function ToolCalls({ toolCalls }: { toolCalls: ToolCallsType[] }) {
<Accordion
key={`tool-call-${index}`}
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]"
className="w-full rounded-[20px] bg-gray-1000 hover:bg-[#F1F1F1] dark:bg-gun-metal dark:hover:bg-[#2C2E3C]"
titleClassName="px-6 py-2 text-sm font-semibold"
>
<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">
<div className="flex flex-col rounded-2xl border border-silver dark:border-silver/20">
<p className="flex flex-row items-center justify-between break-words rounded-t-2xl bg-black/10 px-2 py-1 text-sm font-semibold dark:bg-[#191919]">
<span style={{ fontFamily: 'IBMPlexMono-Medium' }}>
Arguments
</span>{' '}
@@ -668,17 +742,17 @@ function ToolCalls({ toolCalls }: { toolCalls: ToolCallsType[] }) {
textToCopy={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">
<p className="dark:tex break-words rounded-b-2xl p-2 font-mono text-sm dark:bg-[#222327]">
<span
className="text-black dark:text-gray-400 leading-[23px]"
className="leading-[23px] text-black dark:text-gray-400"
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">
<div className="flex flex-col rounded-2xl border border-silver dark:border-silver/20">
<p className="flex flex-row items-center justify-between break-words rounded-t-2xl bg-black/10 px-2 py-1 text-sm font-semibold dark:bg-[#191919]">
<span style={{ fontFamily: 'IBMPlexMono-Medium' }}>
Response
</span>{' '}
@@ -686,9 +760,9 @@ function ToolCalls({ toolCalls }: { toolCalls: ToolCallsType[] }) {
textToCopy={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">
<p className="dark:tex break-words rounded-b-2xl p-2 font-mono text-sm dark:bg-[#222327]">
<span
className="text-black dark:text-gray-400 leading-[23px]"
className="leading-[23px] text-black dark:text-gray-400"
style={{ fontFamily: 'IBMPlexMono-Medium' }}
>
{JSON.stringify(toolCall.result, null, 2)}
@@ -716,7 +790,7 @@ function Thought({
const [isThoughtOpen, setIsThoughtOpen] = useState(true);
return (
<div className="mb-4 w-full flex flex-col flex-wrap items-start self-start lg:flex-nowrap">
<div className="mb-4 flex w-full flex-col flex-wrap items-start self-start lg:flex-nowrap">
<div className="my-2 flex flex-row items-center justify-center gap-3">
<Avatar
className="h-[26px] w-[30px] text-xl"
@@ -742,7 +816,7 @@ function Thought({
</div>
{isThoughtOpen && (
<div className="fade-in ml-2 mr-5 max-w-[90vw] md:max-w-[70vw] lg:max-w-[50vw]">
<div className="rounded-[28px] bg-gray-1000 py-[18px] px-7 dark:bg-gun-metal">
<div className="rounded-[28px] bg-gray-1000 px-7 py-[18px] dark:bg-gun-metal">
<ReactMarkdown
className="fade-in whitespace-pre-wrap break-words leading-normal"
remarkPlugins={[remarkGfm, remarkMath]}
@@ -754,8 +828,8 @@ function Thought({
const language = match ? match[1] : '';
return match ? (
<div className="group relative rounded-[14px] overflow-hidden border border-light-silver dark:border-raisin-black">
<div className="flex justify-between items-center px-2 py-1 bg-platinum dark:bg-eerie-black-2">
<div className="group relative overflow-hidden rounded-[14px] border border-light-silver dark:border-raisin-black">
<div className="flex items-center justify-between bg-platinum px-2 py-1 dark:bg-eerie-black-2">
<span className="text-xs font-medium text-just-black dark:text-chinese-white">
{language}
</span>
@@ -809,14 +883,14 @@ function Thought({
},
thead({ children }) {
return (
<thead className="text-xs uppercase text-gray-900 dark:text-bright-gray bg-gray-50 dark:bg-[#26272E]/50">
<thead className="bg-gray-50 text-xs uppercase text-gray-900 dark:bg-[#26272E]/50 dark:text-bright-gray">
{children}
</thead>
);
},
tr({ children }) {
return (
<tr className="border-b border-gray-200 dark:border-silver/40 odd:bg-white dark:odd:bg-[#26272E] even:bg-gray-50 dark:even:bg-[#26272E]/50">
<tr className="border-b border-gray-200 odd:bg-white even:bg-gray-50 dark:border-silver/40 dark:odd:bg-[#26272E] dark:even:bg-[#26272E]/50">
{children}
</tr>
);

View File

@@ -64,14 +64,19 @@ export default function ConversationMessages({
const scrollConversationToBottom = useCallback(() => {
if (!conversationRef.current || userInterruptedScroll) return;
if (status === 'idle' || !queries[queries.length - 1]?.response) {
conversationRef.current.scrollTo({
behavior: 'smooth',
top: conversationRef.current.scrollHeight,
});
} else {
conversationRef.current.scrollTop = conversationRef.current.scrollHeight;
}
requestAnimationFrame(() => {
if (!conversationRef?.current) return;
if (status === 'idle' || !queries[queries.length - 1]?.response) {
conversationRef.current.scrollTo({
behavior: 'smooth',
top: conversationRef.current.scrollHeight,
});
} else {
conversationRef.current.scrollTop =
conversationRef.current.scrollHeight;
}
});
}, [userInterruptedScroll, status, queries]);
const checkScrollPosition = useCallback(() => {
@@ -127,6 +132,8 @@ export default function ConversationMessages({
: DEFAULT_BUBBLE_MARGIN;
if (query.thought || query.response) {
const isCurrentlyStreaming =
status === 'loading' && index === queries.length - 1;
return (
<ConversationBubble
className={bubbleMargin}
@@ -137,6 +144,7 @@ export default function ConversationMessages({
sources={query.sources}
toolCalls={query.tool_calls}
feedback={query.feedback}
isStreaming={isCurrentlyStreaming}
handleFeedback={
handleFeedback
? (feedback) => handleFeedback(query, feedback, index)
@@ -195,7 +203,7 @@ export default function ConversationMessages({
>
<img
src={ArrowDown}
alt=""
alt="arrow down"
className="h-4 w-4 opacity-50 filter dark:invert md:h-5 md:w-5"
/>
</button>

View File

@@ -192,7 +192,7 @@ export default function ConversationTile({
conversationId !== conversation.id &&
selectConversation(conversation.id);
}}
className={`my-auto mx-4 mt-4 flex h-9 cursor-pointer items-center justify-between pl-4 gap-4 rounded-3xl hover:bg-bright-gray dark:hover:bg-dark-charcoal ${
className={`mx-4 my-auto mt-4 flex h-9 cursor-pointer items-center justify-between gap-4 rounded-3xl pl-4 hover:bg-bright-gray dark:hover:bg-dark-charcoal ${
conversationId === conversation.id || isOpen || isHovered || isEdit
? 'bg-bright-gray dark:bg-dark-charcoal'
: ''

View File

@@ -132,8 +132,8 @@ export const SharedConversation = () => {
content="Shared conversations with DocsGPT"
/>
</Helmet>
<div className="flex h-full flex-col items-center justify-between gap-2 overflow-y-hidden dark:bg-raisin-black ">
<div className="border-b p-2 dark:border-b-silver w-full md:w-9/12 lg:w-8/12 xl:w-8/12 2xl:w-6/12 max-w-[1200px]">
<div className="flex h-full flex-col items-center justify-between gap-2 overflow-y-hidden dark:bg-raisin-black">
<div className="w-full max-w-[1200px] border-b p-2 dark:border-b-silver md:w-9/12 lg:w-8/12 xl:w-8/12 2xl:w-6/12">
<h1 className="font-semi-bold text-4xl text-chinese-black dark:text-chinese-silver">
{title}
</h1>
@@ -153,7 +153,7 @@ export const SharedConversation = () => {
queries={queries}
status={status}
/>
<div className="flex flex-col items-center gap-4 pb-2 w-full md:w-9/12 lg:w-8/12 xl:w-8/12 2xl:w-6/12 max-w-[1200px]">
<div className="flex w-full max-w-[1200px] flex-col items-center gap-4 pb-2 md:w-9/12 lg:w-8/12 xl:w-8/12 2xl:w-6/12">
{apiKey ? (
<MessageInput
value={input}
@@ -164,13 +164,13 @@ export const SharedConversation = () => {
) : (
<button
onClick={() => navigate('/')}
className="w-fit rounded-full bg-purple-30 py-3 px-5 text-white shadow-xl transition-colors duration-200 hover:bg-violets-are-blue mb-14 sm:mb-0"
className="mb-14 w-fit rounded-full bg-purple-30 px-5 py-3 text-white shadow-xl transition-colors duration-200 hover:bg-violets-are-blue sm:mb-0"
>
{t('sharedConv.button')}
</button>
)}
<p className="text-gray-4000 hidden w-[100vw] self-center bg-transparent py-2 text-center text-xs dark:text-sonic-silver md:inline md:w-full">
<p className="hidden w-[100vw] self-center bg-transparent py-2 text-center text-xs text-gray-4000 dark:text-sonic-silver md:inline md:w-full">
{t('sharedConv.meta')}
</p>
</div>

View File

@@ -15,7 +15,7 @@ export function handleFetchAnswer(
token_limit: number,
agentId?: string,
attachments?: string[],
save_conversation: boolean = true,
save_conversation = true,
): Promise<
| {
result: any;
@@ -103,7 +103,7 @@ export function handleFetchAnswerSteaming(
indx?: number,
agentId?: string,
attachments?: string[],
save_conversation: boolean = true,
save_conversation = true,
): Promise<Answer> {
history = history.map((item) => {
return {

View File

@@ -9,7 +9,6 @@ export interface Message {
type: MESSAGE_TYPE;
}
export interface Attachment {
id?: string;
fileName: string;

View File

@@ -43,11 +43,11 @@ export default function AddActionModal({
className="sm:w-[512px]"
>
<div>
<h2 className="font-semibold text-xl text-jet dark:text-bright-gray px-3">
<h2 className="px-3 text-xl font-semibold text-jet dark:text-bright-gray">
New Action
</h2>
<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">
<div className="relative mt-6 px-3">
<span className="absolute -top-2 left-5 z-10 bg-white px-2 text-xs text-gray-4000 dark:bg-[#26272E] dark:text-silver">
Action Name
</span>
<Input
@@ -63,7 +63,7 @@ export default function AddActionModal({
placeholder={'Enter name'}
/>
<p
className={`mt-2 ml-1 text-xs italic ${
className={`ml-1 mt-2 text-xs italic ${
functionNameError ? 'text-red-500' : 'text-gray-500'
}`}
>

View File

@@ -100,26 +100,26 @@ export default function AddToolModal({
{modalState === 'ACTIVE' && (
<WrapperComponent
close={() => setModalState('INACTIVE')}
className="max-w-[950px] w-[90vw] md:w-[85vw] lg:w-[75vw] h-[85vh]"
className="h-[85vh] w-[90vw] max-w-[950px] md:w-[85vw] lg:w-[75vw]"
>
<div className="flex flex-col h-full">
<div className="flex h-full flex-col">
<div>
<h2 className="font-semibold text-xl text-jet dark:text-bright-gray px-3">
<h2 className="px-3 text-xl font-semibold text-jet dark:text-bright-gray">
{t('settings.tools.selectToolSetup')}
</h2>
<div className="mt-5 h-[73vh] overflow-auto px-3 py-px">
{loading ? (
<div className="h-full flex items-center justify-center">
<div className="flex h-full items-center justify-center">
<Spinner />
</div>
) : (
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4 auto-rows-fr pb-2">
<div className="grid auto-rows-fr grid-cols-1 gap-4 pb-2 sm:grid-cols-2 lg:grid-cols-3">
{availableTools.map((tool, index) => (
<div
role="button"
tabIndex={0}
key={index}
className="h-52 w-full p-6 border rounded-2xl border-light-gainsboro dark:border-arsenic bg-white-3000 dark:bg-gunmetal flex flex-col justify-between cursor-pointer hover:border-[#9d9d9d] hover:dark:border-[#717179]"
className="flex h-52 w-full cursor-pointer flex-col justify-between rounded-2xl border border-light-gainsboro bg-white-3000 p-6 hover:border-[#9d9d9d] dark:border-arsenic dark:bg-gunmetal hover:dark:border-[#717179]"
onClick={() => {
setSelectedTool(tool);
handleAddTool(tool);
@@ -132,7 +132,7 @@ export default function AddToolModal({
}}
>
<div className="w-full">
<div className="px-1 w-full flex items-center justify-between">
<div className="flex w-full items-center justify-between px-1">
<img
src={`/toolIcons/tool_${tool.name}.svg`}
className="h-6 w-6"
@@ -142,11 +142,11 @@ export default function AddToolModal({
<div className="mt-[9px]">
<p
title={tool.displayName}
className="px-1 text-[13px] font-semibold text-raisin-black-light dark:text-bright-gray leading-relaxed capitalize truncate"
className="truncate px-1 text-[13px] font-semibold capitalize leading-relaxed text-raisin-black-light dark:text-bright-gray"
>
{tool.displayName}
</p>
<p className="mt-1 px-1 h-24 overflow-auto text-[12px] text-old-silver dark:text-sonic-silver-light leading-relaxed">
<p className="mt-1 h-24 overflow-auto px-1 text-[12px] leading-relaxed text-old-silver dark:text-sonic-silver-light">
{tool.description}
</p>
</div>

View File

@@ -35,12 +35,12 @@ export default function ChunkModal({
<div
className={`${
modalState === 'ACTIVE' ? 'visible' : 'hidden'
} fixed top-0 left-0 z-30 h-screen w-screen bg-gray-alpha flex items-center justify-center`}
} fixed left-0 top-0 z-30 flex h-screen w-screen items-center justify-center bg-gray-alpha`}
>
<article className="flex w-11/12 sm:w-[620px] flex-col gap-4 rounded-2xl bg-white shadow-lg dark:bg-[#26272E]">
<article className="flex w-11/12 flex-col gap-4 rounded-2xl bg-white shadow-lg dark:bg-[#26272E] sm:w-[620px]">
<div className="relative">
<button
className="absolute top-3 right-4 m-2 w-3"
className="absolute right-4 top-3 m-2 w-3"
onClick={() => {
setModalState('INACTIVE');
}}
@@ -48,11 +48,11 @@ export default function ChunkModal({
<img className="filter dark:invert" src={Exit} />
</button>
<div className="p-6">
<h2 className="font-semibold text-xl text-jet dark:text-bright-gray px-3">
<h2 className="px-3 text-xl font-semibold text-jet dark:text-bright-gray">
Add Chunk
</h2>
<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">
<div className="relative mt-6 px-3">
<span className="absolute -top-2 left-5 z-10 bg-white px-2 text-xs text-gray-4000 dark:bg-[#26272E] dark:text-silver">
Title
</span>
<Input
@@ -64,14 +64,14 @@ export default function ChunkModal({
labelBgClassName="bg-white dark:bg-charleston-green-2"
></Input>
</div>
<div className="mt-6 relative px-3">
<div className="pt-3 pb-1 border border-silver dark:border-silver/40 rounded-lg">
<span className="absolute left-5 -top-2 bg-white px-2 text-xs text-gray-4000 dark:bg-[#26272E] dark:text-silver rounded-lg">
<div className="relative mt-6 px-3">
<div className="rounded-lg border border-silver pb-1 pt-3 dark:border-silver/40">
<span className="absolute -top-2 left-5 rounded-lg bg-white px-2 text-xs text-gray-4000 dark:bg-[#26272E] dark:text-silver">
Body text
</span>
<textarea
id="chunk-body-text"
className="h-60 w-full px-3 outline-none dark:bg-transparent dark:text-white"
className="h-60 w-full px-3 outline-none dark:bg-transparent dark:text-white"
value={chunkText}
onChange={(e) => setChunkText(e.target.value)}
aria-label="Prompt Text"
@@ -107,12 +107,12 @@ export default function ChunkModal({
<div
className={`${
modalState === 'ACTIVE' ? 'visible' : 'hidden'
} fixed top-0 left-0 z-30 h-screen w-screen bg-gray-alpha flex items-center justify-center`}
} fixed left-0 top-0 z-30 flex h-screen w-screen items-center justify-center bg-gray-alpha`}
>
<article className="flex w-11/12 sm:w-[620px] flex-col gap-4 rounded-2xl bg-white shadow-lg dark:bg-[#26272E]">
<article className="flex w-11/12 flex-col gap-4 rounded-2xl bg-white shadow-lg dark:bg-[#26272E] sm:w-[620px]">
<div className="relative">
<button
className="absolute top-3 right-4 m-2 w-3"
className="absolute right-4 top-3 m-2 w-3"
onClick={() => {
setModalState('INACTIVE');
}}
@@ -120,10 +120,10 @@ export default function ChunkModal({
<img className="filter dark:invert" src={Exit} />
</button>
<div className="p-6">
<h2 className="font-semibold text-xl text-jet dark:text-bright-gray px-3">
<h2 className="px-3 text-xl font-semibold text-jet dark:text-bright-gray">
Edit Chunk
</h2>
<div className="mt-6 relative px-3">
<div className="relative mt-6 px-3">
<Input
type="text"
value={title}
@@ -133,23 +133,23 @@ export default function ChunkModal({
labelBgClassName="bg-white dark:bg-charleston-green-2"
></Input>
</div>
<div className="mt-6 relative px-3">
<div className="pt-3 pb-1 border border-silver dark:border-silver/40 rounded-lg">
<span className="absolute left-5 -top-2 bg-white px-2 text-xs text-gray-4000 dark:bg-[#26272E] dark:text-silver rounded-lg">
<div className="relative mt-6 px-3">
<div className="rounded-lg border border-silver pb-1 pt-3 dark:border-silver/40">
<span className="absolute -top-2 left-5 rounded-lg bg-white px-2 text-xs text-gray-4000 dark:bg-[#26272E] dark:text-silver">
Body text
</span>
<textarea
id="chunk-body-text"
className="h-60 w-full px-3 outline-none dark:bg-transparent dark:text-white"
className="h-60 w-full px-3 outline-none dark:bg-transparent dark:text-white"
value={chunkText}
onChange={(e) => setChunkText(e.target.value)}
aria-label="Prompt Text"
></textarea>
</div>
</div>
<div className="mt-8 w-full px-3 flex items-center justify-between">
<div className="mt-8 flex w-full items-center justify-between px-3">
<button
className="rounded-full px-5 py-2 border border-solid border-red-500 text-red-500 hover:bg-red-500 hover:text-white text-nowrap text-sm"
className="text-nowrap rounded-full border border-solid border-red-500 px-5 py-2 text-sm text-red-500 hover:bg-red-500 hover:text-white"
onClick={() => {
setDeleteModal('ACTIVE');
}}

View File

@@ -51,10 +51,10 @@ export default function ConfigToolModal({
return (
<WrapperModal close={() => setModalState('INACTIVE')}>
<div>
<h2 className="font-semibold text-xl text-jet dark:text-bright-gray px-3">
<h2 className="px-3 text-xl font-semibold text-jet dark:text-bright-gray">
{t('modals.configTool.title')}
</h2>
<p className="mt-5 text-sm text-gray-600 dark:text-gray-400 px-3">
<p className="mt-5 px-3 text-sm text-gray-600 dark:text-gray-400">
{t('modals.configTool.type')}:{' '}
<span className="font-semibold">{tool?.name}</span>
</p>

View File

@@ -80,7 +80,7 @@ export default function CreateAPIKeyModal({
{t('modals.createAPIKey.label')}
</span>
</div>
<div className="relative mt-5 mb-4">
<div className="relative mb-4 mt-5">
<Input
type="text"
className="rounded-md"

View File

@@ -1,5 +1,4 @@
import React, { useState } from 'react';
import { useDispatch } from 'react-redux';
import Input from '../components/Input';
import { ActiveState } from '../models/misc';
@@ -19,13 +18,17 @@ export default function JWTModal({
if (modalState !== 'ACTIVE') return null;
return (
<WrapperModal className="p-4" isPerformingTask={true} close={() => {}}>
<WrapperModal
className="p-4"
isPerformingTask={true}
close={() => undefined}
>
<div className="mb-6">
<span className="text-lg text-jet dark:text-bright-gray">
Add JWT Token
</span>
</div>
<div className="relative mt-5 mb-4">
<div className="relative mb-4 mt-5">
<Input
name="JWT Token"
type="text"

View File

@@ -136,7 +136,7 @@ export const ShareConversationModal = ({
</div>
)}
<div className="flex items-baseline justify-between gap-2">
<span className="no-scrollbar w-full overflow-x-auto whitespace-nowrap rounded-full border-2 border-silver dark:border-silver/40 py-3 px-4 text-eerie-black dark:text-white">
<span className="no-scrollbar w-full overflow-x-auto whitespace-nowrap rounded-full border-2 border-silver px-4 py-3 text-eerie-black dark:border-silver/40 dark:text-white">
{`${domain}/share/${identifier ?? '....'}`}
</span>
{status === 'fetched' ? (

View File

@@ -46,14 +46,14 @@ export default function WrapperModal({
}, [close]);
return (
<div className="fixed top-0 left-0 z-30 flex h-screen w-screen items-center justify-center bg-gray-alpha bg-opacity-50">
<div className="fixed left-0 top-0 z-30 flex h-screen w-screen items-center justify-center bg-gray-alpha bg-opacity-50">
<div
ref={modalRef}
className={`relative w-11/12 sm:w-[512px] p-8 rounded-2xl bg-white dark:bg-[#26272E] ${className}`}
className={`relative w-11/12 rounded-2xl bg-white p-8 dark:bg-[#26272E] sm:w-[512px] ${className}`}
>
{!isPerformingTask && (
<button
className="absolute top-3 right-4 m-2 w-3 z-50"
className="absolute right-4 top-3 z-50 m-2 w-3"
onClick={close}
>
<img className="filter dark:invert" src={Exit} alt="Close" />

View File

@@ -41,7 +41,7 @@ function AddPrompt({
labelBgClassName="bg-white dark:bg-[#26272E]"
borderVariant="thin"
/>
<div className="relative top-[7px] left-3">
<div className="relative left-3 top-[7px]">
<span className="bg-white px-1 text-xs text-silver dark:bg-[#26272E] dark:text-silver">
{t('modals.prompts.promptText')}
</span>
@@ -113,7 +113,7 @@ function EditPrompt({
labelBgClassName="bg-white dark:bg-charleston-green-2"
borderVariant="thin"
/>
<div className="relative top-[7px] left-3">
<div className="relative left-3 top-[7px]">
<span className="bg-white px-1 text-xs text-silver dark:bg-charleston-green-2 dark:text-silver">
{t('modals.prompts.promptText')}
</span>
@@ -131,7 +131,7 @@ function EditPrompt({
</div>
<div className="mt-6 flex flex-row-reverse gap-4">
<button
className={`rounded-3xl bg-purple-30 disabled:hover:bg-purple-30 hover:bg-violets-are-blue px-5 py-2 text-sm text-white transition-all ${
className={`rounded-3xl bg-purple-30 px-5 py-2 text-sm text-white transition-all hover:bg-violets-are-blue disabled:hover:bg-purple-30 ${
currentPromptEdit.type === 'public'
? 'cursor-not-allowed opacity-50'
: ''
@@ -247,7 +247,7 @@ export default function PromptsModal({
setNewPromptContent('');
}
}}
className="sm:w-[512px] mt-24"
className="mt-24 sm:w-[512px]"
>
{view}
</WrapperModal>

View File

@@ -101,18 +101,18 @@ export default function APIKeys() {
}, []);
return (
<div className="flex flex-col w-full mt-8 max-w-full overflow-hidden">
<div className="flex flex-col relative flex-grow">
<div className="mt-8 flex w-full max-w-full flex-col overflow-hidden">
<div className="relative flex flex-grow flex-col">
<div className="mb-6">
<h2 className="text-base font-medium text-sonic-silver">
{t('settings.apiKeys.description')}
</h2>
</div>
<div className="mb-6 flex flex-col sm:flex-row justify-end items-start sm:items-center gap-3">
<div className="mb-6 flex flex-col items-start justify-end gap-3 sm:flex-row sm:items-center">
<button
onClick={() => setCreateModal(true)}
className="rounded-full text-sm w-[108px] h-[30px] bg-purple-30 text-white hover:bg-violets-are-blue flex items-center justify-center"
className="flex h-[30px] w-[108px] items-center justify-center rounded-full bg-purple-30 text-sm text-white hover:bg-violets-are-blue"
title={t('settings.apiKeys.createNew')}
>
{t('settings.apiKeys.createNew')}
@@ -120,18 +120,18 @@ export default function APIKeys() {
</div>
<div className="relative w-full">
<div className="border rounded-md border-gray-300 dark:border-silver/40 overflow-hidden">
<div className="overflow-x-auto table-scroll">
<div className="overflow-hidden rounded-md border border-gray-300 dark:border-silver/40">
<div className="table-scroll overflow-x-auto">
<table className="w-full table-auto">
<thead>
<tr className="border-b border-gray-300 dark:border-silver/40">
<th className="py-3 px-4 text-left text-xs font-medium text-sonic-silver w-[35%]">
<th className="w-[35%] px-4 py-3 text-left text-xs font-medium text-sonic-silver">
{t('settings.apiKeys.name')}
</th>
<th className="py-3 px-4 text-left text-xs font-medium text-sonic-silver w-[35%]">
<th className="w-[35%] px-4 py-3 text-left text-xs font-medium text-sonic-silver">
{t('settings.apiKeys.sourceDoc')}
</th>
<th className="py-3 px-4 text-left text-xs font-medium text-sonic-silver w-[25%]">
<th className="w-[25%] px-4 py-3 text-left text-xs font-medium text-sonic-silver">
<span className="hidden sm:inline">
{t('settings.apiKeys.key')}
</span>
@@ -139,7 +139,7 @@ export default function APIKeys() {
{t('settings.apiKeys.key')}
</span>
</th>
<th className="py-3 px-4 text-right text-xs font-medium text-gray-700 dark:text-[#E0E0E0] w-[5%]">
<th className="w-[5%] px-4 py-3 text-right text-xs font-medium text-gray-700 dark:text-[#E0E0E0]">
<span className="sr-only">Actions</span>
</th>
</tr>
@@ -151,7 +151,7 @@ export default function APIKeys() {
<tr>
<td
colSpan={4}
className="py-4 text-center text-gray-700 dark:text-neutral-200 bg-transparent"
className="bg-transparent py-4 text-center text-gray-700 dark:text-neutral-200"
>
{t('settings.apiKeys.noData')}
</td>
@@ -163,22 +163,22 @@ export default function APIKeys() {
key={element.id}
className="group transition-colors hover:bg-gray-50 dark:hover:bg-gray-800/50"
>
<td className="py-4 px-4 text-sm font-semibold text-gray-700 dark:text-[#E0E0E0] w-[35%] min-w-48 max-w-0">
<td className="w-[35%] min-w-48 max-w-0 px-4 py-4 text-sm font-semibold text-gray-700 dark:text-[#E0E0E0]">
<div className="truncate" title={element.name}>
{element.name}
</div>
</td>
<td className="py-4 px-4 text-sm text-gray-700 dark:text-[#E0E0E0] w-[35%] min-w-48 max-w-0">
<td className="w-[35%] min-w-48 max-w-0 px-4 py-4 text-sm text-gray-700 dark:text-[#E0E0E0]">
<div className="truncate" title={element.source}>
{element.source}
</div>
</td>
<td className="py-4 px-4 text-sm font-mono text-gray-700 dark:text-[#E0E0E0] w-[25%]">
<td className="w-[25%] px-4 py-4 font-mono text-sm text-gray-700 dark:text-[#E0E0E0]">
<div className="truncate" title={element.key}>
{element.key}
</div>
</td>
<td className="py-4 px-4 text-right w-[5%]">
<td className="w-[5%] px-4 py-4 text-right">
<div className="flex justify-end">
<button
onClick={() =>
@@ -187,7 +187,7 @@ export default function APIKeys() {
name: element.name,
})
}
className="inline-flex items-center justify-center w-8 h-8 rounded-full hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors flex-shrink-0"
className="inline-flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-full transition-colors hover:bg-gray-100 dark:hover:bg-gray-700"
>
<img
src={Trash}

View File

@@ -1,12 +1,17 @@
import {
BarElement, CategoryScale, Chart as ChartJS, Legend, LinearScale, Title, Tooltip
BarElement,
CategoryScale,
Chart as ChartJS,
Legend,
LinearScale,
Title,
Tooltip,
} from 'chart.js';
import { useEffect, useState } from 'react';
import { Bar } from 'react-chartjs-2';
import { useTranslation } from 'react-i18next';
import { useSelector } from 'react-redux';
import { Agent } from '../agents/types';
import userService from '../api/services/userService';
import Dropdown from '../components/Dropdown';
import SkeletonLoader from '../components/SkeletonLoader';

View File

@@ -285,14 +285,14 @@ export default function Documents({
}}
/>
) : (
<div className="flex flex-col mt-8 w-full max-w-full overflow-hidden">
<div className="flex flex-col relative flex-grow">
<div className="mt-8 flex w-full max-w-full flex-col overflow-hidden">
<div className="relative flex flex-grow flex-col">
<div className="mb-6">
<h2 className="text-base font-medium text-sonic-silver">
{t('settings.documents.title')}
</h2>
</div>
<div className="mb-6 flex flex-col sm:flex-row justify-between items-start sm:items-center gap-3">
<div className="mb-6 flex flex-col items-start justify-between gap-3 sm:flex-row sm:items-center">
<div className="w-full sm:w-auto">
<label htmlFor="document-search-input" className="sr-only">
{t('settings.documents.searchPlaceholder')}
@@ -312,7 +312,7 @@ export default function Documents({
/>
</div>
<button
className="rounded-full w-[108px] h-[32px] text-sm bg-purple-30 text-white hover:bg-violets-are-blue flex items-center justify-center"
className="flex h-[32px] w-[108px] items-center justify-center rounded-full bg-purple-30 text-sm text-white hover:bg-violets-are-blue"
title={t('settings.documents.addNew')}
onClick={() => {
setIsOnboarding(false);
@@ -323,27 +323,27 @@ export default function Documents({
</button>
</div>
<div className="relative w-full">
<div className="border rounded-md border-gray-300 dark:border-silver/40 overflow-hidden">
<div className="overflow-x-auto table-scroll">
<div className="overflow-hidden rounded-md border border-gray-300 dark:border-silver/40">
<div className="table-scroll overflow-x-auto">
<table className="w-full table-auto">
<thead>
<tr className="border-b border-gray-300 dark:border-silver/40">
<th className="py-3 px-4 text-left text-xs font-medium text-sonic-silver w-[45%]">
<th className="w-[45%] px-4 py-3 text-left text-xs font-medium text-sonic-silver">
{t('settings.documents.name')}
</th>
<th className="py-3 px-4 text-left text-xs font-medium text-sonic-silver w-[30%]">
<div className="flex justify-start items-center">
<th className="w-[30%] px-4 py-3 text-left text-xs font-medium text-sonic-silver">
<div className="flex items-center justify-start">
{t('settings.documents.date')}
<img
className="cursor-pointer ml-2"
className="ml-2 cursor-pointer"
onClick={() => refreshDocs('date')}
src={caretSort}
alt="sort"
/>
</div>
</th>
<th className="py-3 px-4 text-left text-xs font-medium text-sonic-silver w-[15%]">
<div className="flex justify-start items-center">
<th className="w-[15%] px-4 py-3 text-left text-xs font-medium text-sonic-silver">
<div className="flex items-center justify-start">
<span className="hidden sm:inline">
{t('settings.documents.tokenUsage')}
</span>
@@ -351,14 +351,14 @@ export default function Documents({
{t('settings.documents.tokenUsage')}
</span>
<img
className="cursor-pointer ml-2"
className="ml-2 cursor-pointer"
onClick={() => refreshDocs('tokens')}
src={caretSort}
alt="sort"
/>
</div>
</th>
<th className="py-3 px-4 sr-only w-[10%]">
<th className="sr-only w-[10%] px-4 py-3">
{t('settings.documents.actions')}
</th>
</tr>
@@ -370,7 +370,7 @@ export default function Documents({
<tr>
<td
colSpan={4}
className="py-4 text-center text-gray-700 dark:text-neutral-200 bg-transparent"
className="bg-transparent py-4 text-center text-gray-700 dark:text-neutral-200"
>
{t('settings.documents.noData')}
</td>
@@ -382,26 +382,26 @@ export default function Documents({
return (
<tr key={docId} className="group transition-colors">
<td
className="py-4 px-4 text-sm font-semibold text-gray-700 dark:text-[#E0E0E0] min-w-48 max-w-0 truncate group-hover:bg-gray-50 dark:group-hover:bg-gray-800/50"
className="min-w-48 max-w-0 truncate px-4 py-4 text-sm font-semibold text-gray-700 group-hover:bg-gray-50 dark:text-[#E0E0E0] dark:group-hover:bg-gray-800/50"
title={document.name}
>
{document.name}
</td>
<td className="py-4 px-4 text-sm text-gray-700 dark:text-[#E0E0E0] whitespace-nowrap group-hover:bg-gray-50 dark:group-hover:bg-gray-800/50">
<td className="whitespace-nowrap px-4 py-4 text-sm text-gray-700 group-hover:bg-gray-50 dark:text-[#E0E0E0] dark:group-hover:bg-gray-800/50">
{document.date ? formatDate(document.date) : ''}
</td>
<td className="py-4 px-4 text-sm text-gray-700 dark:text-[#E0E0E0] whitespace-nowrap group-hover:bg-gray-50 dark:group-hover:bg-gray-800/50">
<td className="whitespace-nowrap px-4 py-4 text-sm text-gray-700 group-hover:bg-gray-50 dark:text-[#E0E0E0] dark:group-hover:bg-gray-800/50">
{document.tokens
? formatTokens(+document.tokens)
: ''}
</td>
<td
className="py-4 px-4 text-right group-hover:bg-gray-50 dark:group-hover:bg-gray-800/50"
className="px-4 py-4 text-right group-hover:bg-gray-50 dark:group-hover:bg-gray-800/50"
onClick={(e) => e.stopPropagation()}
>
<div
ref={getMenuRef(docId)}
className="flex items-center justify-end gap-3 relative"
className="relative flex items-center justify-end gap-3"
>
{document.syncFrequency && (
<DropdownMenu
@@ -432,7 +432,7 @@ export default function Documents({
)}
<button
onClick={(e) => handleMenuClick(e, docId)}
className="inline-flex items-center justify-center w-8 h-8 rounded-full hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors flex-shrink-0"
className="inline-flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-full transition-colors hover:bg-gray-100 dark:hover:bg-gray-700"
aria-label="Open menu"
data-testid={`menu-button-${docId}`}
>
@@ -633,19 +633,19 @@ function DocumentChunks({
fetchChunks();
}, [page, perPage]);
return (
<div className="flex flex-col mt-8">
<div className="mb-3 flex items-center gap-3 text-eerie-black dark:text-bright-gray text-sm">
<div className="mt-8 flex flex-col">
<div className="mb-3 flex items-center gap-3 text-sm text-eerie-black dark:text-bright-gray">
<button
className="text-sm text-gray-400 dark:text-gray-500 border dark:border-0 dark:bg-[#28292D] dark:hover:bg-[#2E2F34] p-3 rounded-full"
className="rounded-full border p-3 text-sm text-gray-400 dark:border-0 dark:bg-[#28292D] dark:text-gray-500 dark:hover:bg-[#2E2F34]"
onClick={handleGoBack}
>
<img src={ArrowLeft} alt="left-arrow" className="w-3 h-3" />
<img src={ArrowLeft} alt="left-arrow" className="h-3 w-3" />
</button>
<p className="mt-px">Back to all documents</p>
</div>
<div className="my-3 flex justify-between items-center gap-1">
<div className="w-full sm:w-auto flex items-center gap-2 text-eerie-black dark:text-bright-gray">
<p className="font-semibold text-2xl hidden sm:flex">{`${totalChunks} Chunks`}</p>
<div className="my-3 flex items-center justify-between gap-1">
<div className="flex w-full items-center gap-2 text-eerie-black dark:text-bright-gray sm:w-auto">
<p className="hidden text-2xl font-semibold sm:flex">{`${totalChunks} Chunks`}</p>
<label htmlFor="chunk-search-input" className="sr-only">
{t('settings.documents.searchPlaceholder')}
</label>
@@ -663,7 +663,7 @@ function DocumentChunks({
/>
</div>
<button
className="rounded-full w-[108px] h-[32px] text-sm bg-purple-30 text-white hover:bg-violets-are-blue flex items-center justify-center"
className="flex h-[32px] w-[108px] items-center justify-center rounded-full bg-purple-30 text-sm text-white hover:bg-violets-are-blue"
title={t('settings.documents.addNew')}
onClick={() => setAddModal('ACTIVE')}
>
@@ -671,24 +671,24 @@ function DocumentChunks({
</button>
</div>
{loading ? (
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
<div className="h-32 flex items-center justify-center mt-24 col-span-2 lg:col-span-3">
<div className="grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3">
<div className="col-span-2 mt-24 flex h-32 items-center justify-center lg:col-span-3">
<Spinner />
</div>
</div>
) : (
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
<div className="grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3">
{paginatedChunks.filter((chunk) => {
if (!chunk.metadata?.title) return true;
return chunk.metadata.title
.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">
<div className="col-span-2 mt-24 text-center text-gray-500 dark:text-gray-400 lg:col-span-3">
<img
src={isDarkTheme ? NoFilesDarkIcon : NoFilesIcon}
alt="No tools found"
className="h-24 w-24 mx-auto mb-2"
className="mx-auto mb-2 h-24 w-24"
/>
No chunks found
</div>
@@ -703,10 +703,10 @@ function DocumentChunks({
.map((chunk, 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"
className="relative flex h-56 w-full flex-col justify-between rounded-2xl border border-silver p-6 dark:border-silver/40"
>
<div className="w-full">
<div className="w-full flex items-center justify-between">
<div className="flex w-full items-center justify-between">
<button
aria-label={'edit'}
onClick={() => {
@@ -715,7 +715,7 @@ function DocumentChunks({
chunk: chunk,
});
}}
className="absolute top-3 right-3 h-4 w-4 cursor-pointer"
className="absolute right-3 top-3 h-4 w-4 cursor-pointer"
>
<img
alt={'edit'}
@@ -725,10 +725,10 @@ function DocumentChunks({
</button>
</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">
<p className="ellipsis-text h-12 break-words text-sm font-semibold leading-relaxed text-eerie-black dark:text-[#EEEEEE]">
{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">
<p className="mt-1 h-[110px] overflow-y-auto break-words pr-1 text-[13px] leading-relaxed text-gray-600 dark:text-gray-400">
{chunk.text}
</p>
</div>
@@ -745,7 +745,7 @@ function DocumentChunks({
.toLowerCase()
.includes(searchTerm.toLowerCase());
}).length !== 0 && (
<div className="mt-10 w-full flex items-center justify-center">
<div className="mt-10 flex w-full items-center justify-center">
<Pagination
currentPage={page}
totalPages={Math.ceil(totalChunks / perPage)}

View File

@@ -88,7 +88,7 @@ export default function General() {
{' '}
<div className="flex flex-col gap-4">
{' '}
<label className="font-medium text-base text-jet dark:text-bright-gray">
<label className="text-base font-medium text-jet dark:text-bright-gray">
{t('settings.general.selectTheme')}
</label>
<Dropdown
@@ -106,7 +106,7 @@ export default function General() {
/>
</div>
<div className="flex flex-col gap-4">
<label className="font-medium text-base text-jet dark:text-bright-gray">
<label className="text-base font-medium text-jet dark:text-bright-gray">
{t('settings.general.selectLanguage')}
</label>
<Dropdown
@@ -124,7 +124,7 @@ export default function General() {
/>
</div>
<div className="flex flex-col gap-4">
<label className="font-medium text-base text-jet dark:text-bright-gray">
<label className="text-base font-medium text-jet dark:text-bright-gray">
{t('settings.general.chunks')}
</label>
<Dropdown
@@ -137,7 +137,7 @@ export default function General() {
/>
</div>
<div className="flex flex-col gap-4">
<label className="font-medium text-base text-jet dark:text-bright-gray">
<label className="text-base font-medium text-jet dark:text-bright-gray">
{t('settings.general.convHistory')}
</label>
<Dropdown
@@ -171,11 +171,11 @@ export default function General() {
setPrompts={setPrompts}
/>
</div>
<hr className="border-t w-[calc(min(665px,100%))] my-4 border-silver dark:border-silver/40" />
<hr className="my-4 w-[calc(min(665px,100%))] border-t border-silver dark:border-silver/40" />
<div className="flex flex-col gap-2">
<button
title={t('settings.general.deleteAllLabel')}
className="flex font-medium text-sm w-fit cursor-pointer items-center justify-between rounded-3xl border border-solid border-rosso-corsa bg-transparent px-5 py-3 text-rosso-corsa transition-colors hover:bg-rosso-corsa hover:text-white hover:font-bold tracking-[0.015em] hover:tracking-normal"
className="flex w-fit cursor-pointer items-center justify-between rounded-3xl border border-solid border-rosso-corsa bg-transparent px-5 py-3 text-sm font-medium tracking-[0.015em] text-rosso-corsa transition-colors hover:bg-rosso-corsa hover:font-bold hover:tracking-normal hover:text-white"
onClick={() => dispatch(setModalStateDeleteConv('ACTIVE'))}
>
{t('settings.general.deleteAllBtn')}

View File

@@ -145,7 +145,7 @@ export default function Prompts({
<p className="font-medium dark:text-bright-gray">
{t('settings.general.prompt')}
</p>
<div className="flex flex-row justify-start items-baseline gap-6">
<div className="flex flex-row items-baseline justify-start gap-6">
<Dropdown
options={prompts}
selectedValue={selectedPrompt.name}
@@ -174,7 +174,7 @@ export default function Prompts({
/>
<button
className="rounded-3xl w-20 h-10 text-sm border border-solid border-violets-are-blue text-violets-are-blue transition-colors hover:text-white hover:bg-violets-are-blue"
className="h-10 w-20 rounded-3xl border border-solid border-violets-are-blue text-sm text-violets-are-blue transition-colors hover:bg-violets-are-blue hover:text-white"
onClick={() => {
setModalType('ADD');
setModalState('ACTIVE');

View File

@@ -115,12 +115,12 @@ export default function ToolConfig({
};
return (
<div className="mt-8 flex flex-col gap-4">
<div className="mb-4 flex items-center gap-3 text-eerie-black dark:text-bright-gray text-sm">
<div className="mb-4 flex items-center gap-3 text-sm text-eerie-black dark:text-bright-gray">
<button
className="text-sm text-gray-400 dark:text-gray-500 border dark:border-0 dark:bg-[#28292D] dark:hover:bg-[#2E2F34] p-3 rounded-full"
className="rounded-full border p-3 text-sm text-gray-400 dark:border-0 dark:bg-[#28292D] dark:text-gray-500 dark:hover:bg-[#2E2F34]"
onClick={handleGoBack}
>
<img src={ArrowLeft} alt="left-arrow" className="w-3 h-3" />
<img src={ArrowLeft} alt="left-arrow" className="h-3 w-3" />
</button>
<p className="mt-px">Back to all tools</p>
</div>
@@ -128,7 +128,7 @@ export default function ToolConfig({
<p className="text-sm font-semibold text-eerie-black dark:text-bright-gray">
Type
</p>
<p className="mt-1 text-base font-normal text-eerie-black dark:text-bright-gray font-sans">
<p className="mt-1 font-sans text-base font-normal text-eerie-black dark:text-bright-gray">
{tool.name}
</p>
</div>
@@ -138,7 +138,7 @@ export default function ToolConfig({
Authentication
</p>
)}
<div className="flex mt-4 flex-col sm:flex-row items-start sm:items-center gap-2">
<div className="mt-4 flex flex-col items-start gap-2 sm:flex-row sm:items-center">
{Object.keys(tool?.config).length !== 0 &&
tool.name !== 'api_tool' && (
<div className="relative w-96">
@@ -153,13 +153,13 @@ export default function ToolConfig({
)}
<div className="flex items-center gap-2">
<button
className="rounded-full px-5 py-[10px] bg-purple-30 text-white hover:bg-violets-are-blue text-nowrap text-sm"
className="text-nowrap rounded-full bg-purple-30 px-5 py-[10px] text-sm text-white hover:bg-violets-are-blue"
onClick={handleSaveChanges}
>
Save changes
</button>
<button
className="rounded-full px-5 py-[10px] border border-solid border-red-500 text-red-500 hover:bg-red-500 hover:text-white text-nowrap text-sm"
className="text-nowrap rounded-full border border-solid border-red-500 px-5 py-[10px] text-sm text-red-500 hover:bg-red-500 hover:text-white"
onClick={handleDelete}
>
Delete
@@ -168,8 +168,8 @@ export default function ToolConfig({
</div>
</div>
<div className="flex flex-col gap-4">
<div className="mx-1 my-2 h-[0.8px] w-full rounded-full bg-[#C4C4C4]/40 lg:w-[95%] "></div>
<div className="w-full flex flex-row items-center justify-between gap-2">
<div className="mx-1 my-2 h-[0.8px] w-full rounded-full bg-[#C4C4C4]/40 lg:w-[95%]"></div>
<div className="flex w-full flex-row items-center justify-between gap-2">
<p className="text-base font-semibold text-eerie-black dark:text-bright-gray">
Actions
</p>
@@ -177,7 +177,7 @@ export default function ToolConfig({
onClick={() => {
setActionModalState('ACTIVE');
}}
className="border border-solid border-violets-are-blue text-violets-are-blue transition-colors hover:bg-violets-are-blue hover:text-white rounded-full text-sm px-5 py-1"
className="rounded-full border border-solid border-violets-are-blue px-5 py-1 text-sm text-violets-are-blue transition-colors hover:bg-violets-are-blue hover:text-white"
>
Add action
</button>
@@ -191,9 +191,9 @@ export default function ToolConfig({
return (
<div
key={actionIndex}
className="w-full border border-silver dark:border-silver/40 rounded-xl"
className="w-full rounded-xl border border-silver dark:border-silver/40"
>
<div className="h-10 bg-[#F9F9F9] dark:bg-[#28292D] rounded-t-xl border-b border-silver dark:border-silver/40 flex items-center justify-between px-5 flex-wrap">
<div className="flex h-10 flex-wrap items-center justify-between rounded-t-xl border-b border-silver bg-[#F9F9F9] px-5 dark:border-silver/40 dark:bg-[#28292D]">
<p className="font-semibold text-eerie-black dark:text-bright-gray">
{action.name}
</p>
@@ -214,10 +214,10 @@ export default function ToolConfig({
id={`actionToggle-${actionIndex}`}
/>
</div>
<div className="mt-5 relative">
<div className="relative mt-5">
<Input
type="text"
className="w-[97%] ml-5"
className="ml-5 w-[97%]"
placeholder="Enter description"
value={action.description}
onChange={(e) => {
@@ -285,7 +285,7 @@ export default function ToolConfig({
<input
key={uniqueKey}
value={param[1].description}
className="bg-transparent border border-silver dark:border-silver/40 outline-none px-2 py-1 rounded-lg text-sm"
className="rounded-lg border border-silver bg-transparent px-2 py-1 text-sm outline-none dark:border-silver/40"
onChange={(e) => {
setTool({
...tool,
@@ -321,7 +321,7 @@ export default function ToolConfig({
value={param[1].value}
key={uniqueKey}
disabled={param[1].filled_by_llm}
className={`bg-transparent border border-silver dark:border-silver/40 outline-none px-2 py-1 rounded-lg text-sm ${param[1].filled_by_llm ? 'opacity-50' : ''}`}
className={`rounded-lg border border-silver bg-transparent px-2 py-1 text-sm outline-none dark:border-silver/40 ${param[1].filled_by_llm ? 'opacity-50' : ''}`}
onChange={(e) => {
setTool({
...tool,
@@ -424,9 +424,9 @@ function APIToolConfig({
return (
<div
key={actionIndex}
className="w-full border border-silver dark:border-silver/40 rounded-xl"
className="w-full rounded-xl border border-silver dark:border-silver/40"
>
<div className="h-10 bg-[#F9F9F9] dark:bg-[#28292D] rounded-t-xl border-b border-silver dark:border-silver/40 flex items-center justify-between px-5 flex-wrap">
<div className="flex h-10 flex-wrap items-center justify-between rounded-t-xl border-b border-silver bg-[#F9F9F9] px-5 dark:border-silver/40 dark:bg-[#28292D]">
<p className="font-semibold text-eerie-black dark:text-bright-gray">
{action.name}
</p>
@@ -439,7 +439,7 @@ function APIToolConfig({
</div>
<div className="mt-8 px-5">
<div className="relative w-full">
<span className="z-10 absolute left-5 -top-2 bg-white px-2 text-xs text-gray-4000 dark:bg-raisin-black dark:text-silver">
<span className="absolute -top-2 left-5 z-10 bg-white px-2 text-xs text-gray-4000 dark:bg-raisin-black dark:text-silver">
URL
</span>
<Input
@@ -471,7 +471,7 @@ function APIToolConfig({
</div>
<div className="mt-4 px-5 py-2">
<div className="relative w-full">
<span className="absolute left-5 -top-2 z-10 bg-white px-2 text-xs text-gray-4000 dark:bg-raisin-black dark:text-silver">
<span className="absolute -top-2 left-5 z-10 bg-white px-2 text-xs text-gray-4000 dark:bg-raisin-black dark:text-silver">
Method
</span>
<Dropdown
@@ -508,7 +508,7 @@ function APIToolConfig({
</div>
<div className="mt-4 px-5 py-2">
<div className="relative w-full">
<span className="z-10 absolute left-5 -top-2 bg-white px-2 text-xs text-gray-4000 dark:bg-raisin-black dark:text-silver">
<span className="absolute -top-2 left-5 z-10 bg-white px-2 text-xs text-gray-4000 dark:bg-raisin-black dark:text-silver">
Description
</span>
<Input
@@ -713,7 +713,7 @@ function APIActionTable({
<div className="flex flex-row items-center justify-between gap-2">
<input
value={newPropertyKey}
className="min-w-[130.5px] w-full flex items-start bg-transparent border border-silver dark:border-silver/40 outline-none px-2 py-1 rounded-lg text-sm"
className="flex w-full min-w-[130.5px] items-start rounded-lg border border-silver bg-transparent px-2 py-1 text-sm outline-none dark:border-silver/40"
onChange={(e) => setNewPropertyKey(e.target.value)}
onKeyDown={(e) => {
if (e.key === 'Enter') {
@@ -724,26 +724,26 @@ function APIActionTable({
<div className="mt-1">
<button
onClick={handleRenameProperty}
className="mr-1 w-5 h-5"
className="mr-1 h-5 w-5"
>
<img
src={CircleCheck}
alt="check"
className="w-5 h-5"
className="h-5 w-5"
/>
</button>
<button
onClick={handleRenamePropertyCancel}
className="w-5 h-5"
className="h-5 w-5"
>
<img src={CircleX} alt="cancel" className="w-5 h-5" />
<img src={CircleX} alt="cancel" className="h-5 w-5" />
</button>
</div>
</div>
) : (
<input
value={key}
className="min-w-[175.5px] w-full flex items-start bg-transparent border border-silver dark:border-silver/40 outline-none px-2 py-1 rounded-lg text-sm"
className="flex w-full min-w-[175.5px] items-start rounded-lg border border-silver bg-transparent px-2 py-1 text-sm outline-none dark:border-silver/40"
onFocus={() => handleRenamePropertyStart(section, key)}
readOnly
/>
@@ -772,7 +772,7 @@ function APIActionTable({
<td className="w-10">
<input
value={param.description}
className="bg-transparent border border-silver dark:border-silver/40 outline-none px-2 py-1 rounded-lg text-sm"
className="rounded-lg border border-silver bg-transparent px-2 py-1 text-sm outline-none dark:border-silver/40"
onChange={(e) =>
handlePropertyChange(
section,
@@ -790,7 +790,7 @@ function APIActionTable({
onChange={(e) =>
handlePropertyChange(section, key, 'value', e.target.value)
}
className={`bg-transparent border border-silver dark:border-silver/40 outline-none px-2 py-1 rounded-lg text-sm ${param.filled_by_llm ? 'opacity-50' : ''}`}
className={`rounded-lg border border-silver bg-transparent px-2 py-1 text-sm outline-none dark:border-silver/40 ${param.filled_by_llm ? 'opacity-50' : ''}`}
></input>
</td>
<td
@@ -804,9 +804,9 @@ function APIActionTable({
>
<button
onClick={() => handlePorpertyDelete(section, key)}
className="w-4 h-4 opacity-60 hover:opacity-100"
className="h-4 w-4 opacity-60 hover:opacity-100"
>
<img src={Trash} alt="delete" className="w-4 h-4"></img>
<img src={Trash} alt="delete" className="h-4 w-4"></img>
</button>
</td>
</tr>
@@ -824,20 +824,20 @@ function APIActionTable({
}
}}
placeholder="New property key"
className="min-w-[130.5px] w-full flex items-start bg-transparent border border-silver dark:border-silver/40 outline-none px-2 py-1 rounded-lg text-sm"
className="flex w-full min-w-[130.5px] items-start rounded-lg border border-silver bg-transparent px-2 py-1 text-sm outline-none dark:border-silver/40"
/>
</td>
<td colSpan={4} className="text-right">
<button
onClick={handleAddProperty}
className="bg-purple-30 text-white hover:bg-violets-are-blue rounded-full px-5 py-[4px] mr-1 text-sm"
className="mr-1 rounded-full bg-purple-30 px-5 py-[4px] text-sm text-white hover:bg-violets-are-blue"
>
{' '}
Add{' '}
</button>
<button
onClick={handleAddPropertyCancel}
className="border border-solid border-red-500 text-red-500 hover:bg-red-500 hover:text-white rounded-full px-5 py-[4px] text-sm"
className="rounded-full border border-solid border-red-500 px-5 py-[4px] text-sm text-red-500 hover:bg-red-500 hover:text-white"
>
{' '}
Cancel{' '}
@@ -857,7 +857,7 @@ function APIActionTable({
<td colSpan={5}>
<button
onClick={() => handleAddPropertyStart(section)}
className="flex items-start rounded-full px-5 py-[4px] border border-solid text-violets-are-blue border-violets-are-blue transition-colors hover:bg-violets-are-blue hover:text-white text-nowrap text-sm"
className="flex items-start text-nowrap rounded-full border border-solid border-violets-are-blue px-5 py-[4px] text-sm text-violets-are-blue transition-colors hover:bg-violets-are-blue hover:text-white"
>
Add New Field
</button>

View File

@@ -101,8 +101,8 @@ export default function Tools() {
/>
) : (
<div className="mt-8">
<div className="flex flex-col relative">
<div className="my-3 flex justify-between items-center gap-1">
<div className="relative flex flex-col">
<div className="my-3 flex items-center justify-between gap-1">
<div className="p-1">
<label htmlFor="tool-search-input" className="sr-only">
{t('settings.tools.searchPlaceholder')}
@@ -119,7 +119,7 @@ export default function Tools() {
/>
</div>
<button
className="rounded-full w-[108px] h-[30px] text-sm bg-purple-30 text-white hover:bg-violets-are-blue flex items-center justify-center"
className="flex h-[30px] w-[108px] items-center justify-center rounded-full bg-purple-30 text-sm text-white hover:bg-violets-are-blue"
onClick={() => {
setAddToolModalState('ACTIVE');
}}
@@ -127,23 +127,23 @@ export default function Tools() {
{t('settings.tools.addTool')}
</button>
</div>
<div className="border-b border-light-silver dark:border-dim-gray mb-8 mt-5" />
<div className="mb-8 mt-5 border-b border-light-silver dark:border-dim-gray" />
{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">
<div className="grid grid-cols-2 gap-6 lg:grid-cols-3">
<div className="col-span-2 mt-24 flex h-32 items-center justify-center lg:col-span-3">
<Spinner />
</div>
</div>
) : (
<div className="flex flex-wrap gap-4 justify-center sm:justify-start">
<div className="flex flex-wrap justify-center gap-4 sm:justify-start">
{userTools.length === 0 ? (
<div className="flex flex-col items-center justify-center w-full py-12">
<div className="flex w-full flex-col items-center justify-center py-12">
<img
src={isDarkTheme ? NoFilesDarkIcon : NoFilesIcon}
alt="No tools found"
className="h-32 w-32 mx-auto mb-6"
className="mx-auto mb-6 h-32 w-32"
/>
<p className="text-gray-500 dark:text-gray-400 text-center text-lg">
<p className="text-center text-lg text-gray-500 dark:text-gray-400">
{t('settings.tools.noToolsFound')}
</p>
</div>
@@ -157,14 +157,14 @@ export default function Tools() {
.map((tool, index) => (
<div
key={index}
className="h-52 w-[300px] p-6 border rounded-2xl border-light-gainsboro dark:border-arsenic bg-white-3000 dark:bg-transparent flex flex-col justify-between relative"
className="relative flex h-52 w-[300px] flex-col justify-between rounded-2xl border border-light-gainsboro bg-white-3000 p-6 dark:border-arsenic dark:bg-transparent"
>
<button
onClick={() => handleSettingsClick(tool)}
aria-label={t('settings.tools.configureToolAria', {
toolName: tool.displayName,
})}
className="absolute top-4 right-4"
className="absolute right-4 top-4"
>
<img
src={CogwheelIcon}
@@ -173,7 +173,7 @@ export default function Tools() {
/>
</button>
<div className="w-full">
<div className="px-1 w-full flex items-center">
<div className="flex w-full items-center px-1">
<img
src={`/toolIcons/tool_${tool.name}.svg`}
alt={`${tool.displayName} icon`}
@@ -183,11 +183,11 @@ export default function Tools() {
<div className="mt-[9px]">
<p
title={tool.displayName}
className="px-1 text-[13px] font-semibold text-raisin-black-light dark:text-bright-gray leading-relaxed capitalize truncate"
className="truncate px-1 text-[13px] font-semibold capitalize leading-relaxed text-raisin-black-light dark:text-bright-gray"
>
{tool.displayName}
</p>
<p className="mt-1 px-1 h-24 overflow-auto text-[12px] text-old-silver dark:text-sonic-silver-light leading-relaxed">
<p className="mt-1 h-24 overflow-auto px-1 text-[12px] leading-relaxed text-old-silver dark:text-sonic-silver-light">
{tool.description}
</p>
</div>

View File

@@ -69,8 +69,8 @@ function Upload({
: 'grid-rows-[0fr] opacity-0'
}`}
>
<div className="overflow-hidden flex flex-col gap-4">
<hr className="my-4 border-[#C4C4C4]/40 border-[1px]" />
<div className="flex flex-col gap-4 overflow-hidden">
<hr className="my-4 border-[1px] border-[#C4C4C4]/40" />
<div className="flex flex-col gap-4">
{advancedFields.map((field: FormField) => renderField(field))}
</div>
@@ -212,11 +212,11 @@ function Upload({
function ProgressBar({ progressPercent }: { progressPercent: number }) {
return (
<div className="flex items-center justify-center h-full w-full my-8">
<div className="relative w-32 h-32 rounded-full">
<div className="my-8 flex h-full w-full items-center justify-center">
<div className="relative h-32 w-32 rounded-full">
<div className="absolute inset-0 rounded-full shadow-[0_0_10px_2px_rgba(0,0,0,0.3)_inset] dark:shadow-[0_0_10px_2px_rgba(0,0,0,0.3)_inset]"></div>
<div
className={`absolute inset-0 rounded-full ${progressPercent === 100 ? 'shadow-xl shadow-lime-300/50 dark:shadow-lime-300/50 bg-gradient-to-r from-white to-gray-400 dark:bg-gradient-to-br dark:from-gray-500 dark:to-gray-300' : 'shadow-[0_4px_0_#7D54D1] dark:shadow-[0_4px_0_#7D54D1]'}`}
className={`absolute inset-0 rounded-full ${progressPercent === 100 ? 'bg-gradient-to-r from-white to-gray-400 shadow-xl shadow-lime-300/50 dark:bg-gradient-to-br dark:from-gray-500 dark:to-gray-300 dark:shadow-lime-300/50' : 'shadow-[0_4px_0_#7D54D1] dark:shadow-[0_4px_0_#7D54D1]'}`}
style={{
animation: `${progressPercent === 100 ? 'none' : 'rotate 2s linear infinite'}`,
}}
@@ -270,13 +270,13 @@ function Upload({
setProgress(undefined);
setModalState('INACTIVE');
}}
className="cursor-pointer rounded-3xl text-sm h-[42px] px-[28px] py-[6px] bg-[#7D54D1] text-white hover:bg-[#6F3FD1] shadow-lg"
className="h-[42px] cursor-pointer rounded-3xl bg-[#7D54D1] px-[28px] py-[6px] text-sm text-white shadow-lg hover:bg-[#6F3FD1]"
>
{t('modals.uploadDoc.start')}
</button>
) : (
<button
className="ml-2 cursor-pointer rounded-3xl text-sm h-[42px] px-[28px] py-[6px] bg-[#7D54D14D] text-white shadow-lg"
className="ml-2 h-[42px] cursor-pointer rounded-3xl bg-[#7D54D14D] px-[28px] py-[6px] text-sm text-white shadow-lg"
disabled
>
{t('modals.uploadDoc.wait')}
@@ -570,33 +570,33 @@ function Upload({
view = <TrainingProgress></TrainingProgress>;
} else {
view = (
<div className="flex flex-col gap-4 w-full">
<p className="text-2xl text-jet dark:text-bright-gray text-center font-semibold">
<div className="flex w-full flex-col gap-4">
<p className="text-center text-2xl font-semibold text-jet dark:text-bright-gray">
{t('modals.uploadDoc.label')}
</p>
{!activeTab && (
<div>
<p className="dark text-gray-6000 dark:text-bright-gray text-sm text-center font-medium">
<p className="dark text-center text-sm font-medium text-gray-6000 dark:text-bright-gray">
{t('modals.uploadDoc.select')}
</p>
<div className="w-full gap-4 h-full p-4 flex flex-col md:flex-row md:gap-4 justify-center items-center">
<div className="flex h-full w-full flex-col items-center justify-center gap-4 p-4 md:flex-row md:gap-4">
<button
onClick={() => setActiveTab('file')}
className="opacity-85 hover:opacity-100 rounded-3xl text-sm font-medium border flex flex-col items-center justify-center hover:shadow-purple-30/30 hover:shadow-lg p-8 gap-4 bg-transparent text-[#777777] dark:bg-transparent dark:text-[#c3c3c3] hover:border-purple-30 border-[#D7D7D7] h-40 w-40 md:w-52 md:h-52"
className="flex h-40 w-40 flex-col items-center justify-center gap-4 rounded-3xl border border-[#D7D7D7] bg-transparent p-8 text-sm font-medium text-[#777777] opacity-85 hover:border-purple-30 hover:opacity-100 hover:shadow-lg hover:shadow-purple-30/30 dark:bg-transparent dark:text-[#c3c3c3] md:h-52 md:w-52"
>
<img
src={FileUpload}
className="w-12 h-12 mr-2 dark:filter dark:invert dark:brightness-50"
className="mr-2 h-12 w-12 dark:brightness-50 dark:invert dark:filter"
/>
{t('modals.uploadDoc.file')}
</button>
<button
onClick={() => setActiveTab('remote')}
className="opacity-85 hover:opacity-100 rounded-3xl text-sm font-medium border flex flex-col items-center justify-center hover:shadow-purple-30/30 hover:shadow-lg p-8 gap-4 bg-transparent text-[#777777] dark:bg-transparent dark:text-[#c3c3c3] hover:border-purple-30 border-[#D7D7D7] h-40 w-40 md:w-52 md:h-52"
className="flex h-40 w-40 flex-col items-center justify-center gap-4 rounded-3xl border border-[#D7D7D7] bg-transparent p-8 text-sm font-medium text-[#777777] opacity-85 hover:border-purple-30 hover:opacity-100 hover:shadow-lg hover:shadow-purple-30/30 dark:bg-transparent dark:text-[#c3c3c3] md:h-52 md:w-52"
>
<img
src={WebsiteCollect}
className="w-14 h-14 mr-2 dark:filter dark:invert dark:brightness-50"
className="mr-2 h-14 w-14 dark:brightness-50 dark:invert dark:filter"
/>
{t('modals.uploadDoc.remote')}
</button>
@@ -617,7 +617,7 @@ function Upload({
required={true}
/>
<div className="my-2" {...getRootProps()}>
<span className="rounded-3xl bg-transparent px-4 py-2 font-medium text-purple-30 hover:cursor-pointer dark:text-silver border border-[#7F7F82]">
<span className="rounded-3xl border border-[#7F7F82] bg-transparent px-4 py-2 font-medium text-purple-30 hover:cursor-pointer dark:text-silver">
<input type="button" {...getInputProps()} />
{t('modals.uploadDoc.choose')}
</span>
@@ -633,7 +633,7 @@ function Upload({
{files.map((file) => (
<p
key={file.name}
className="text-gray-6000 truncate overflow-hidden text-ellipsis"
className="overflow-hidden truncate text-ellipsis text-gray-6000"
title={file.name}
>
{file.name}
@@ -681,7 +681,7 @@ function Upload({
) && (
<button
onClick={() => setShowAdvancedOptions(!showAdvancedOptions)}
className="text-purple-30 text-sm font-normal pl-0 py-2 bg-transparent hover:cursor-pointer text-left"
className="bg-transparent py-2 pl-0 text-left text-sm font-normal text-purple-30 hover:cursor-pointer"
>
{showAdvancedOptions
? t('modals.uploadDoc.hideAdvanced')
@@ -694,7 +694,7 @@ function Upload({
{activeTab && (
<button
onClick={() => setActiveTab(null)}
className="rounded-3xl bg-transparent px-4 py-2 font-medium text-purple-30 hover:cursor-pointer dark:text-silver text-[14px]"
className="rounded-3xl bg-transparent px-4 py-2 text-[14px] font-medium text-purple-30 hover:cursor-pointer dark:text-silver"
>
{t('modals.uploadDoc.back')}
</button>
@@ -709,7 +709,7 @@ function Upload({
}
}}
disabled={isUploadDisabled()}
className={`rounded-3xl px-4 py-2 font-medium text-[14px] ${
className={`rounded-3xl px-4 py-2 text-[14px] font-medium ${
isUploadDisabled()
? 'cursor-not-allowed bg-gray-300 text-gray-500'
: 'cursor-pointer bg-purple-30 text-white hover:bg-violets-are-blue'