mirror of
https://github.com/arc53/DocsGPT.git
synced 2025-11-29 08:33:20 +00:00
Merge pull request #1768 from ManishMadan2882/main
Attachments : File storage changes
This commit is contained in:
@@ -28,6 +28,8 @@ from application.extensions import api
|
||||
from application.tts.google_tts import GoogleTTS
|
||||
from application.utils import check_required_fields, validate_function_name
|
||||
from application.vectorstore.vector_creator import VectorCreator
|
||||
from application.storage.storage_creator import StorageCreator
|
||||
storage = StorageCreator.get_storage()
|
||||
|
||||
mongo = MongoDB.get_client()
|
||||
db = mongo[settings.MONGO_DB_NAME]
|
||||
@@ -2976,16 +2978,16 @@ class StoreAttachment(Resource):
|
||||
|
||||
try:
|
||||
attachment_id = ObjectId()
|
||||
original_filename = secure_filename(file.filename)
|
||||
original_filename = secure_filename(os.path.basename(file.filename))
|
||||
relative_path = f"{settings.UPLOAD_FOLDER}/{user}/attachments/{str(attachment_id)}/{original_filename}"
|
||||
|
||||
file_content = file.read()
|
||||
|
||||
|
||||
metadata = storage.save_file(file, relative_path)
|
||||
|
||||
file_info = {
|
||||
"filename": original_filename,
|
||||
"attachment_id": str(attachment_id),
|
||||
"path": relative_path,
|
||||
"file_content": file_content,
|
||||
"metadata": metadata
|
||||
}
|
||||
|
||||
task = store_attachment.delay(file_info, user)
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import datetime
|
||||
import io
|
||||
import json
|
||||
import logging
|
||||
import mimetypes
|
||||
@@ -445,76 +444,61 @@ def attachment_worker(self, file_info, user):
|
||||
filename = file_info["filename"]
|
||||
attachment_id = file_info["attachment_id"]
|
||||
relative_path = file_info["path"]
|
||||
file_content = file_info["file_content"]
|
||||
metadata = file_info.get("metadata", {})
|
||||
|
||||
try:
|
||||
self.update_state(state="PROGRESS", meta={"current": 10})
|
||||
storage_type = getattr(settings, "STORAGE_TYPE", "local")
|
||||
storage = StorageCreator.create_storage(storage_type)
|
||||
storage = StorageCreator.get_storage()
|
||||
|
||||
self.update_state(
|
||||
state="PROGRESS", meta={"current": 30, "status": "Processing content"}
|
||||
)
|
||||
|
||||
with tempfile.NamedTemporaryFile(
|
||||
suffix=os.path.splitext(filename)[1]
|
||||
) as temp_file:
|
||||
temp_file.write(file_content)
|
||||
temp_file.flush()
|
||||
reader = SimpleDirectoryReader(
|
||||
input_files=[temp_file.name], exclude_hidden=True, errors="ignore"
|
||||
)
|
||||
documents = reader.load_data()
|
||||
content = storage.process_file(
|
||||
relative_path,
|
||||
lambda local_path, **kwargs: SimpleDirectoryReader(
|
||||
input_files=[local_path], exclude_hidden=True, errors="ignore"
|
||||
).load_data()[0].text
|
||||
)
|
||||
|
||||
token_count = num_tokens_from_string(content)
|
||||
|
||||
if not documents:
|
||||
logging.warning(f"No content extracted from file: {filename}")
|
||||
raise ValueError(f"Failed to extract content from file: {filename}")
|
||||
self.update_state(
|
||||
state="PROGRESS", meta={"current": 80, "status": "Storing in database"}
|
||||
)
|
||||
|
||||
content = documents[0].text
|
||||
token_count = num_tokens_from_string(content)
|
||||
mime_type = mimetypes.guess_type(filename)[0] or "application/octet-stream"
|
||||
|
||||
self.update_state(
|
||||
state="PROGRESS", meta={"current": 60, "status": "Saving file"}
|
||||
)
|
||||
file_obj = io.BytesIO(file_content)
|
||||
|
||||
metadata = storage.save_file(file_obj, relative_path)
|
||||
|
||||
mime_type = mimetypes.guess_type(filename)[0] or "application/octet-stream"
|
||||
|
||||
self.update_state(
|
||||
state="PROGRESS", meta={"current": 80, "status": "Storing in database"}
|
||||
)
|
||||
|
||||
doc_id = ObjectId(attachment_id)
|
||||
attachments_collection.insert_one(
|
||||
{
|
||||
"_id": doc_id,
|
||||
"user": user,
|
||||
"path": relative_path,
|
||||
"content": content,
|
||||
"token_count": token_count,
|
||||
"mime_type": mime_type,
|
||||
"date": datetime.datetime.now(),
|
||||
"metadata": metadata,
|
||||
}
|
||||
)
|
||||
|
||||
logging.info(
|
||||
f"Stored attachment with ID: {attachment_id}", extra={"user": user}
|
||||
)
|
||||
|
||||
self.update_state(
|
||||
state="PROGRESS", meta={"current": 100, "status": "Complete"}
|
||||
)
|
||||
|
||||
return {
|
||||
"filename": filename,
|
||||
doc_id = ObjectId(attachment_id)
|
||||
attachments_collection.insert_one(
|
||||
{
|
||||
"_id": doc_id,
|
||||
"user": user,
|
||||
"path": relative_path,
|
||||
"content": content,
|
||||
"token_count": token_count,
|
||||
"attachment_id": attachment_id,
|
||||
"mime_type": mime_type,
|
||||
"date": datetime.datetime.now(),
|
||||
"metadata": metadata,
|
||||
}
|
||||
)
|
||||
|
||||
logging.info(
|
||||
f"Stored attachment with ID: {attachment_id}", extra={"user": user}
|
||||
)
|
||||
|
||||
self.update_state(
|
||||
state="PROGRESS", meta={"current": 100, "status": "Complete"}
|
||||
)
|
||||
|
||||
return {
|
||||
"filename": filename,
|
||||
"path": relative_path,
|
||||
"token_count": token_count,
|
||||
"attachment_id": attachment_id,
|
||||
"mime_type": mime_type,
|
||||
"metadata": metadata,
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
logging.error(
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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}
|
||||
/>
|
||||
|
||||
@@ -3,7 +3,6 @@ import { useSelector, useDispatch } 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 Trash from '../assets/red-trash.svg';
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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')}
|
||||
|
||||
@@ -2,7 +2,10 @@ 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 {
|
||||
oneLight,
|
||||
vscDarkPlus,
|
||||
} from 'react-syntax-highlighter/dist/cjs/styles/prism';
|
||||
import { MermaidRendererProps } from './types';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { selectStatus } from '../conversation/conversationSlice';
|
||||
@@ -20,9 +23,12 @@ const MermaidRenderer: React.FC<MermaidRendererProps> = ({
|
||||
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 [hoverPosition, setHoverPosition] = useState<{
|
||||
x: number;
|
||||
y: number;
|
||||
} | null>(null);
|
||||
const [isHovering, setIsHovering] = useState<boolean>(false);
|
||||
const [zoomFactor, setZoomFactor] = useState<number>(2);
|
||||
const [zoomFactor, setZoomFactor] = useState<number>(2);
|
||||
|
||||
const handleMouseMove = (event: React.MouseEvent) => {
|
||||
if (!containerRef.current) return;
|
||||
@@ -40,16 +46,14 @@ const MermaidRenderer: React.FC<MermaidRendererProps> = ({
|
||||
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
|
||||
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
|
||||
} else if (event.key === '-') {
|
||||
setZoomFactor((prev) => Math.max(1, prev - 0.5)); // Minimum 1x
|
||||
event.preventDefault();
|
||||
}
|
||||
};
|
||||
@@ -57,13 +61,13 @@ const MermaidRenderer: React.FC<MermaidRendererProps> = ({
|
||||
const handleWheel = (event: React.WheelEvent) => {
|
||||
if (!isHovering) return;
|
||||
|
||||
if ( event.ctrlKey || event.metaKey) {
|
||||
if (event.ctrlKey || event.metaKey) {
|
||||
event.preventDefault();
|
||||
|
||||
if (event.deltaY < 0) {
|
||||
setZoomFactor(prev => Math.min(6, prev + 0.25));
|
||||
setZoomFactor((prev) => Math.min(6, prev + 0.25));
|
||||
} else {
|
||||
setZoomFactor(prev => Math.max(1, prev - 0.25));
|
||||
setZoomFactor((prev) => Math.max(1, prev - 0.25));
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -81,8 +85,9 @@ const MermaidRenderer: React.FC<MermaidRendererProps> = ({
|
||||
securityLevel: 'loose',
|
||||
suppressErrorRendering: true,
|
||||
});
|
||||
|
||||
const isCurrentlyLoading = isLoading !== undefined ? isLoading : status === 'loading';
|
||||
|
||||
const isCurrentlyLoading =
|
||||
isLoading !== undefined ? isLoading : status === 'loading';
|
||||
if (!isCurrentlyLoading && code) {
|
||||
try {
|
||||
const element = document.getElementById(diagramId.current);
|
||||
@@ -93,7 +98,9 @@ const MermaidRenderer: React.FC<MermaidRendererProps> = ({
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Error rendering mermaid diagram:', err);
|
||||
setError(`Failed to render diagram: ${err instanceof Error ? err.message : String(err)}`);
|
||||
setError(
|
||||
`Failed to render diagram: ${err instanceof Error ? err.message : String(err)}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -101,9 +108,6 @@ const MermaidRenderer: React.FC<MermaidRendererProps> = ({
|
||||
renderDiagram();
|
||||
}, [code, isDarkTheme, isLoading]);
|
||||
|
||||
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
const handleClickOutside = (event: MouseEvent) => {
|
||||
if (
|
||||
@@ -120,7 +124,6 @@ const MermaidRenderer: React.FC<MermaidRendererProps> = ({
|
||||
};
|
||||
}, [showDownloadMenu]);
|
||||
|
||||
|
||||
const downloadSvg = (): void => {
|
||||
const element = document.getElementById(diagramId.current);
|
||||
if (!element) return;
|
||||
@@ -146,7 +149,7 @@ const MermaidRenderer: React.FC<MermaidRendererProps> = ({
|
||||
|
||||
const svgBlob = new Blob(
|
||||
[`<?xml version="1.0" encoding="UTF-8" standalone="no"?>\n${svgString}`],
|
||||
{ type: 'image/svg+xml' }
|
||||
{ type: 'image/svg+xml' },
|
||||
);
|
||||
|
||||
const url = URL.createObjectURL(svgBlob);
|
||||
@@ -198,7 +201,7 @@ const MermaidRenderer: React.FC<MermaidRendererProps> = ({
|
||||
const img = new Image();
|
||||
img.crossOrigin = 'anonymous';
|
||||
|
||||
img.onload = function(): void {
|
||||
img.onload = function (): void {
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = width;
|
||||
canvas.height = height;
|
||||
@@ -243,20 +246,20 @@ const MermaidRenderer: React.FC<MermaidRendererProps> = ({
|
||||
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 isCurrentlyLoading =
|
||||
isLoading !== undefined ? isLoading : status === 'loading';
|
||||
const showDiagramOptions = !isCurrentlyLoading && !error;
|
||||
const errorRender = !isCurrentlyLoading && error;
|
||||
|
||||
return (
|
||||
<div className="group relative rounded-lg border border-light-silver dark:border-raisin-black bg-white dark:bg-eerie-black w-inherit">
|
||||
<div className="flex justify-between items-center px-2 py-1 bg-platinum dark:bg-eerie-black-2">
|
||||
<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>
|
||||
@@ -267,13 +270,13 @@ const MermaidRenderer: React.FC<MermaidRendererProps> = ({
|
||||
<div className="relative" ref={downloadMenuRef}>
|
||||
<button
|
||||
onClick={() => setShowDownloadMenu(!showDownloadMenu)}
|
||||
className="text-xs px-2 py-1 bg-gray-100 dark:bg-gray-700 rounded flex items-center h-full"
|
||||
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 mt-1 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded shadow-lg z-10 w-40">
|
||||
<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}>
|
||||
@@ -282,7 +285,7 @@ const MermaidRenderer: React.FC<MermaidRendererProps> = ({
|
||||
option.action();
|
||||
setShowDownloadMenu(false);
|
||||
}}
|
||||
className="text-xs px-4 py-2 w-full text-left hover:bg-gray-100 dark:hover:bg-gray-700"
|
||||
className="w-full px-4 py-2 text-left text-xs hover:bg-gray-100 dark:hover:bg-gray-700"
|
||||
>
|
||||
{option.label}
|
||||
</button>
|
||||
@@ -297,7 +300,7 @@ const MermaidRenderer: React.FC<MermaidRendererProps> = ({
|
||||
{showDiagramOptions && (
|
||||
<button
|
||||
onClick={() => setShowCode(!showCode)}
|
||||
className={`text-xs px-2 py-1 rounded flex items-center h-full ${
|
||||
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'
|
||||
@@ -311,14 +314,14 @@ const MermaidRenderer: React.FC<MermaidRendererProps> = ({
|
||||
</div>
|
||||
|
||||
{isCurrentlyLoading ? (
|
||||
<div className="p-4 bg-white dark:bg-eerie-black flex justify-center items-center">
|
||||
<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="border-2 border-red-400 dark:border-red-700 rounded m-2">
|
||||
<div className="bg-red-100 dark:bg-red-900/30 px-4 py-2 text-red-800 dark:text-red-300 text-sm whitespace-normal break-words overflow-auto">
|
||||
<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>
|
||||
@@ -326,13 +329,12 @@ const MermaidRenderer: React.FC<MermaidRendererProps> = ({
|
||||
<>
|
||||
<div
|
||||
ref={containerRef}
|
||||
className=" no-scrollbar p-4 block w-full bg-white dark:bg-eerie-black relative"
|
||||
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}
|
||||
@@ -340,39 +342,42 @@ const MermaidRenderer: React.FC<MermaidRendererProps> = ({
|
||||
onKeyDown={handleKeyDown}
|
||||
onWheel={handleWheel}
|
||||
tabIndex={0}
|
||||
|
||||
>
|
||||
{isHovering && (
|
||||
<>
|
||||
<div className="absolute top-2 right-2 bg-black/70 text-white text-xs px-2 py-1 rounded z-10 flex items-center gap-2">
|
||||
<button
|
||||
onClick={() => setZoomFactor(prev => Math.max(1, prev - 0.5))}
|
||||
className="hover:bg-gray-600 px-1 rounded"
|
||||
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="hover:bg-gray-600 px-1 rounded"
|
||||
title="Increase zoom"
|
||||
>
|
||||
+
|
||||
</button>
|
||||
</div>
|
||||
<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 select-none w-full"
|
||||
className="mermaid w-full select-none"
|
||||
id={diagramId.current}
|
||||
key={`mermaid-${diagramId.current}`}
|
||||
style={{
|
||||
@@ -382,7 +387,7 @@ const MermaidRenderer: React.FC<MermaidRendererProps> = ({
|
||||
cursor: 'default',
|
||||
width: '100%',
|
||||
display: 'flex',
|
||||
justifyContent: 'center'
|
||||
justifyContent: 'center',
|
||||
}}
|
||||
>
|
||||
{code}
|
||||
@@ -391,7 +396,7 @@ const MermaidRenderer: React.FC<MermaidRendererProps> = ({
|
||||
|
||||
{showCode && (
|
||||
<div className="border-t border-light-silver dark:border-raisin-black">
|
||||
<div className="p-2 bg-platinum dark:bg-eerie-black-2">
|
||||
<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>
|
||||
@@ -403,7 +408,7 @@ const MermaidRenderer: React.FC<MermaidRendererProps> = ({
|
||||
margin: 0,
|
||||
borderRadius: 0,
|
||||
scrollbarWidth: 'thin',
|
||||
maxHeight: '300px'
|
||||
maxHeight: '300px',
|
||||
}}
|
||||
>
|
||||
{code}
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -5,7 +5,10 @@ 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,7 +29,10 @@ 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';
|
||||
@@ -106,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>
|
||||
@@ -128,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>
|
||||
@@ -137,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')}
|
||||
@@ -152,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')}
|
||||
@@ -192,7 +198,10 @@ const ConversationBubble = forwardRef<
|
||||
const processMarkdownContent = (content: string) => {
|
||||
const processedContent = preprocessLaTeX(content);
|
||||
|
||||
const contentSegments: Array<{type: 'text' | 'mermaid', content: string}> = [];
|
||||
const contentSegments: Array<{
|
||||
type: 'text' | 'mermaid';
|
||||
content: string;
|
||||
}> = [];
|
||||
|
||||
let lastIndex = 0;
|
||||
const regex = /```mermaid\n([\s\S]*?)```/g;
|
||||
@@ -219,7 +228,7 @@ const ConversationBubble = forwardRef<
|
||||
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' ||
|
||||
@@ -378,9 +387,9 @@ 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 ml-2 mr-5 flex max-w-[90vw] rounded-[28px] bg-gray-1000 px-7 py-[18px] dark:bg-gun-metal md:max-w-[70vw] lg:max-w-[50vw] ${
|
||||
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'
|
||||
}`}
|
||||
>
|
||||
@@ -397,25 +406,38 @@ const ConversationBubble = forwardRef<
|
||||
rehypePlugins={[rehypeKatex]}
|
||||
components={{
|
||||
code(props) {
|
||||
const { children, className, node, ref, ...rest } = props;
|
||||
const match = /language-(\w+)/.exec(className || '');
|
||||
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">
|
||||
<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$/, '')}
|
||||
textToCopy={String(children).replace(
|
||||
/\n$/,
|
||||
'',
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<SyntaxHighlighter
|
||||
{...rest}
|
||||
PreTag="div"
|
||||
language={language}
|
||||
style={isDarkTheme ? vscDarkPlus : oneLight}
|
||||
style={
|
||||
isDarkTheme ? vscDarkPlus : oneLight
|
||||
}
|
||||
className="!mt-0"
|
||||
customStyle={{
|
||||
margin: 0,
|
||||
@@ -461,30 +483,37 @@ const ConversationBubble = forwardRef<
|
||||
},
|
||||
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>
|
||||
);
|
||||
},
|
||||
th({ children }) {
|
||||
return <th className="px-6 py-3">{children}</th>;
|
||||
return (
|
||||
<th className="px-6 py-3">{children}</th>
|
||||
);
|
||||
},
|
||||
td({ children }) {
|
||||
return <td className="px-6 py-3">{children}</td>;
|
||||
return (
|
||||
<td className="px-6 py-3">{children}</td>
|
||||
);
|
||||
},
|
||||
}}
|
||||
>
|
||||
{segment.content}
|
||||
</ReactMarkdown>
|
||||
) : (
|
||||
<div className="my-4 w-full" style={{ minWidth: '100%' }}>
|
||||
<div
|
||||
className="my-4 w-full"
|
||||
style={{ minWidth: '100%' }}
|
||||
>
|
||||
<MermaidRenderer
|
||||
code={segment.content}
|
||||
isLoading={isStreaming}
|
||||
@@ -502,16 +531,14 @@ const ConversationBubble = forwardRef<
|
||||
{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} />
|
||||
@@ -529,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
|
||||
@@ -541,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');
|
||||
@@ -570,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
|
||||
@@ -585,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) {
|
||||
@@ -633,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) => (
|
||||
@@ -674,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"
|
||||
@@ -705,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>{' '}
|
||||
@@ -718,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>{' '}
|
||||
@@ -736,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)}
|
||||
@@ -766,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"
|
||||
@@ -792,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]}
|
||||
@@ -804,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>
|
||||
@@ -859,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>
|
||||
);
|
||||
|
||||
@@ -38,7 +38,7 @@ export default function ConversationMessages({
|
||||
const { t } = useTranslation();
|
||||
|
||||
const conversationRef = useRef<HTMLDivElement>(null);
|
||||
const [atLast,setAtLast] = useState(true);
|
||||
const [atLast, setAtLast] = useState(true);
|
||||
const [eventInterrupt, setEventInterrupt] = useState(false);
|
||||
|
||||
const handleUserInterruption = () => {
|
||||
@@ -47,7 +47,6 @@ export default function ConversationMessages({
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
const scrollIntoView = () => {
|
||||
if (!conversationRef?.current || eventInterrupt) return;
|
||||
|
||||
@@ -60,7 +59,8 @@ export default function ConversationMessages({
|
||||
top: conversationRef.current.scrollHeight,
|
||||
});
|
||||
} else {
|
||||
conversationRef.current.scrollTop = conversationRef.current.scrollHeight;
|
||||
conversationRef.current.scrollTop =
|
||||
conversationRef.current.scrollHeight;
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -92,7 +92,8 @@ export default function ConversationMessages({
|
||||
const prepResponseView = (query: Query, index: number) => {
|
||||
let responseView;
|
||||
if (query.thought || query.response) {
|
||||
const isCurrentlyStreaming = status === 'loading' && index === queries.length - 1;
|
||||
const isCurrentlyStreaming =
|
||||
status === 'loading' && index === queries.length - 1;
|
||||
responseView = (
|
||||
<ConversationBubble
|
||||
className={`${index === queries.length - 1 ? 'mb-32' : 'mb-7'}`}
|
||||
@@ -114,7 +115,7 @@ export default function ConversationMessages({
|
||||
} else if (query.error) {
|
||||
const retryBtn = (
|
||||
<button
|
||||
className="flex items-center justify-center gap-3 self-center rounded-full py-3 px-5 text-lg text-gray-500 transition-colors delay-100 hover:border-gray-500 disabled:cursor-not-allowed dark:text-bright-gray"
|
||||
className="flex items-center justify-center gap-3 self-center rounded-full px-5 py-3 text-lg text-gray-500 transition-colors delay-100 hover:border-gray-500 disabled:cursor-not-allowed dark:text-bright-gray"
|
||||
disabled={status === 'loading'}
|
||||
onClick={() => {
|
||||
handleQuestion({
|
||||
@@ -150,7 +151,7 @@ export default function ConversationMessages({
|
||||
ref={conversationRef}
|
||||
onWheel={handleUserInterruption}
|
||||
onTouchMove={handleUserInterruption}
|
||||
className="flex justify-center w-full overflow-y-auto h-full sm:pt-12 will-change-scroll"
|
||||
className="flex h-full w-full justify-center overflow-y-auto will-change-scroll sm:pt-12"
|
||||
>
|
||||
{queries.length > 0 && !atLast && (
|
||||
<button
|
||||
@@ -161,12 +162,12 @@ export default function ConversationMessages({
|
||||
<img
|
||||
src={ArrowDown}
|
||||
alt="arrow down"
|
||||
className="h-4 w-4 opacity-50 md:h-5 md:w-5 filter dark:invert"
|
||||
className="h-4 w-4 opacity-50 filter dark:invert md:h-5 md:w-5"
|
||||
/>
|
||||
</button>
|
||||
)}
|
||||
|
||||
<div className="w-full md:w-9/12 lg:w-8/12 xl:w-8/12 2xl:w-6/12 max-w-[1300px] px-2">
|
||||
<div className="w-full max-w-[1300px] px-2 md:w-9/12 lg:w-8/12 xl:w-8/12 2xl:w-6/12">
|
||||
{queries.length > 0 ? (
|
||||
queries.map((query, index) => (
|
||||
<Fragment key={index}>
|
||||
|
||||
@@ -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'
|
||||
: ''
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -9,7 +9,6 @@ export interface Message {
|
||||
type: MESSAGE_TYPE;
|
||||
}
|
||||
|
||||
|
||||
export interface Attachment {
|
||||
id?: string;
|
||||
fileName: string;
|
||||
|
||||
@@ -50,11 +50,11 @@ body.dark {
|
||||
|
||||
@layer components {
|
||||
.table-default {
|
||||
@apply block w-full table-auto justify-center rounded-xl border border-silver dark:border-silver/40 text-center dark:text-bright-gray overflow-auto;
|
||||
@apply block w-full table-auto justify-center overflow-auto rounded-xl border border-silver text-center dark:border-silver/40 dark:text-bright-gray;
|
||||
}
|
||||
|
||||
.table-default th {
|
||||
@apply p-4 font-normal text-gray-400 text-nowrap;
|
||||
@apply text-nowrap p-4 font-normal text-gray-400;
|
||||
}
|
||||
|
||||
.table-default th {
|
||||
@@ -66,7 +66,7 @@ body.dark {
|
||||
}
|
||||
|
||||
.table-default td {
|
||||
@apply border-t w-full border-silver dark:border-silver/40 px-4 py-2;
|
||||
@apply w-full border-t border-silver px-4 py-2 dark:border-silver/40;
|
||||
}
|
||||
|
||||
.table-default td:last-child {
|
||||
|
||||
@@ -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'
|
||||
}`}
|
||||
>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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');
|
||||
}}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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' ? (
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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)}
|
||||
|
||||
@@ -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')}
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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'
|
||||
|
||||
Reference in New Issue
Block a user