mirror of
https://github.com/arc53/DocsGPT.git
synced 2025-11-29 16:43:16 +00:00
lint
This commit is contained in:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user