Merge branch 'main' of https://github.com/arc53/DocsGPT into dependabot/npm_and_yarn/frontend/tailwindcss-4.1.10

This commit is contained in:
ManishMadan2882
2025-06-25 18:59:51 +05:30
19 changed files with 704 additions and 512 deletions

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 122.88 122.88"><defs><style>.a{fill:#d53;}.b{fill:#fff;}.c{fill:#ddd;}.d{fill:#fc0;}.e{fill:#6b5;}.f{fill:#4a4;}.g{fill:#148;}</style></defs><title>duckduckgo</title><path class="a" d="M122.88,61.44a61.44,61.44,0,1,0-61.44,61.44,61.44,61.44,0,0,0,61.44-61.44Z"/><path class="b" d="M114.37,61.44a52.92,52.92,0,1,0-15.5,37.43,52.76,52.76,0,0,0,15.5-37.43Zm-13.12-39.8A56.29,56.29,0,1,1,61.44,5.15a56.12,56.12,0,0,1,39.81,16.49Z"/><path class="c" d="M43.24,30.15C26.17,34.13,32.43,58,32.43,58l10.81,52.9,4,1.71-4-82.49Zm-4-10.24H34.7L41,22.19s-6.26,0-6.26,4C48.36,25.6,54.61,29,54.61,29l-15.36-9.1Zm0,0Z"/><path class="b" d="M75.66,115.48S62,93.87,62,79.64c0-26.73,17.63-4,17.63-25S62,28.44,62,28.44c-8.53-10.8-25-8.53-25-8.53l4,2.28s-4,1.13-5.12,2.27,10.81-1.7,15.93,2.85C30.72,29,34.13,46.08,34.13,46.08l11.95,68.27,29.58,1.13Zm0,0Z"/><path class="d" d="M75.66,60.87l21.62-5.69C116.62,58,80.78,68.84,78.51,68.27c-17.07-2.85-12,11.37,8.53,6.82s5.12,11.38-13.65,5.12c-26.74-7.39-12.52-20.48,2.27-19.34Z"/><path class="e" d="M70,105.81l1.14-1.7c12.52,4.55,13.09,6.25,12.52-5.12s0-11.38-13.09-1.71c0-2.84-7.39-1.71-8.53,0-11.95-5.12-13.09-6.83-12.52,1.14,1.14,16.5.57,13.65,11.95,8l8.53-.57Zm0,0Z"/><path class="f" d="M60.87,99.56v6.82c.57,1.14,9.67,1.14,9.67-1.14s-4.55,1.71-7.39.57S62,98.42,62,98.42l-1.14,1.14Zm0,0Z"/><path class="g" d="M48.36,43.24c-2.85-3.42-10.24-.57-8.54,4,.57-2.28,4.55-5.69,8.54-4Zm18.2,0c.57-3.42,6.26-4,8-.57a8,8,0,0,0-8,.57Zm-18.77,9.1a1.14,1.14,0,1,1,0,.57v-.57Zm-4.55,2.27a4,4,0,1,0,0-.57v.57Zm29.58-4a1.14,1.14,0,1,1,0,.57v-.57ZM69.4,52.91a3.42,3.42,0,1,0,0-.57v.57Zm0,0Z"/></svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@@ -288,136 +288,101 @@ const ConversationBubble = forwardRef<
{DisableSourceFE ||
type === 'ERROR' ||
sources?.length === 0 ||
sources?.some((source) => source.link === 'None') ? null : !sources &&
chunks !== '0' &&
selectedDocs ? (
<div className="mb-4 flex 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"
avatar={
<img
src={Sources}
alt={t('conversation.sources.title')}
className="h-full w-full object-fill"
sources?.some((source) => source.link === 'None')
? null
: sources && (
<div className="mb-4 flex 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"
avatar={
<img
src={Sources}
alt={t('conversation.sources.title')}
className="h-full w-full object-fill"
/>
}
/>
}
/>
<p className="text-base font-semibold">
{t('conversation.sources.title')}
</p>
</div>
<div className="grid grid-cols-2 gap-2 lg:grid-cols-4">
{Array.from({ length: 4 }).map((_, index) => (
<div
key={index}
className="bg-gray-1000 text-purple-30 dark:bg-gun-metal flex h-28 cursor-pointer flex-col items-start gap-1 rounded-[20px] p-4 hover:bg-[#F1F1F1] hover:text-[#6D3ECC] dark:hover:bg-[#2C2E3C] dark:hover:text-[#8C67D7]"
>
<span className="h-px w-10 animate-pulse cursor-pointer rounded-[20px] bg-[#B2B2B2] p-1"></span>
<span className="h-px w-24 animate-pulse cursor-pointer rounded-[20px] bg-[#B2B2B2] p-1"></span>
<span className="h-px w-16 animate-pulse cursor-pointer rounded-[20px] bg-[#B2B2B2] p-1"></span>
<span className="h-px w-32 animate-pulse cursor-pointer rounded-[20px] bg-[#B2B2B2] p-1"></span>
<span className="h-px w-24 animate-pulse cursor-pointer rounded-[20px] bg-[#B2B2B2] p-1"></span>
<span className="h-px w-20 animate-pulse cursor-pointer rounded-[20px] bg-[#B2B2B2] p-1"></span>
<p className="text-base font-semibold">
{t('conversation.sources.title')}
</p>
</div>
))}
</div>
</div>
) : (
sources && (
<div className="mb-4 flex 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"
avatar={
<img
src={Sources}
alt={t('conversation.sources.title')}
className="h-full w-full object-fill"
/>
}
/>
<p className="text-base font-semibold">
{t('conversation.sources.title')}
</p>
</div>
<div className="fade-in mr-5 ml-3 max-w-[90vw] md:max-w-[70vw] lg:max-w-[50vw]">
<div className="grid grid-cols-2 gap-2 lg:grid-cols-4">
{sources?.slice(0, 3)?.map((source, index) => (
<div key={index} className="relative">
<div
className="bg-gray-1000 dark:bg-gun-metal h-28 cursor-pointer rounded-[20px] p-4 hover:bg-[#F1F1F1] dark:hover:bg-[#2C2E3C]"
onMouseOver={() => setActiveTooltip(index)}
onMouseOut={() => setActiveTooltip(null)}
>
<p className="ellipsis-text h-12 text-xs break-words">
{source.text}
</p>
<div className="fade-in mr-5 ml-3 max-w-[90vw] md:max-w-[70vw] lg:max-w-[50vw]">
<div className="grid grid-cols-2 gap-2 lg:grid-cols-4">
{sources?.slice(0, 3)?.map((source, index) => (
<div key={index} className="relative">
<div
className={`mt-[14px] flex flex-row items-center gap-[6px] underline-offset-2 ${
source.link && source.link !== 'local'
? 'hover:text-[#007DFF] hover:underline dark:hover:text-[#48A0FF]'
: ''
}`}
onClick={() =>
source.link && source.link !== 'local'
? window.open(
source.link,
'_blank',
'noopener, noreferrer',
)
: null
}
>
<img
src={Document}
alt="Document"
className="h-[17px] w-[17px] object-fill"
/>
<p
className="mt-[2px] truncate text-xs"
title={
source.link && source.link !== 'local'
? source.link
: source.title
}
>
{source.link && source.link !== 'local'
? source.link
: source.title}
</p>
</div>
</div>
{activeTooltip === index && (
<div
className={`dark:bg-chinese-black dark:text-chinese-silver absolute left-1/2 z-50 max-h-48 w-40 translate-x-[-50%] translate-y-[3px] rounded-xl bg-[#FBFBFB] p-4 text-black shadow-xl sm:w-56`}
className="bg-gray-1000 dark:bg-gun-metal h-28 cursor-pointer rounded-[20px] p-4 hover:bg-[#F1F1F1] dark:hover:bg-[#2C2E3C]"
onMouseOver={() => setActiveTooltip(index)}
onMouseOut={() => setActiveTooltip(null)}
>
<p className="line-clamp-6 max-h-[164px] overflow-hidden rounded-md text-sm break-words text-ellipsis">
<p className="ellipsis-text h-12 text-xs break-words">
{source.text}
</p>
<div
className={`mt-[14px] flex flex-row items-center gap-[6px] underline-offset-2 ${
source.link && source.link !== 'local'
? 'hover:text-[#007DFF] hover:underline dark:hover:text-[#48A0FF]'
: ''
}`}
onClick={() =>
source.link && source.link !== 'local'
? window.open(
source.link,
'_blank',
'noopener, noreferrer',
)
: null
}
>
<img
src={Document}
alt="Document"
className="h-[17px] w-[17px] object-fill"
/>
<p
className="mt-[2px] truncate text-xs"
title={
source.link && source.link !== 'local'
? source.link
: source.title
}
>
{source.link && source.link !== 'local'
? source.link
: source.title}
</p>
</div>
</div>
)}
</div>
))}
{(sources?.length ?? 0) > 3 && (
<div
className="bg-gray-1000 text-purple-30 dark:bg-gun-metal flex h-28 cursor-pointer flex-col-reverse rounded-[20px] p-4 hover:bg-[#F1F1F1] hover:text-[#6D3ECC] dark:hover:bg-[#2C2E3C] dark:hover:text-[#8C67D7]"
onClick={() => setIsSidebarOpen(true)}
>
<p className="ellipsis-text h-22 text-xs">
{t('conversation.sources.view_more', {
count: sources?.length ? sources.length - 3 : 0,
})}
</p>
</div>
)}
{activeTooltip === index && (
<div
className={`dark:bg-chinese-black dark:text-chinese-silver absolute left-1/2 z-50 max-h-48 w-40 translate-x-[-50%] translate-y-[3px] rounded-xl bg-[#FBFBFB] p-4 text-black shadow-xl sm:w-56`}
onMouseOver={() => setActiveTooltip(index)}
onMouseOut={() => setActiveTooltip(null)}
>
<p className="line-clamp-6 max-h-[164px] overflow-hidden rounded-md text-sm break-words text-ellipsis">
{source.text}
</p>
</div>
)}
</div>
))}
{(sources?.length ?? 0) > 3 && (
<div
className="bg-gray-1000 text-purple-30 dark:bg-gun-metal flex h-28 cursor-pointer flex-col-reverse rounded-[20px] p-4 hover:bg-[#F1F1F1] hover:text-[#6D3ECC] dark:hover:bg-[#2C2E3C] dark:hover:text-[#8C67D7]"
onClick={() => setIsSidebarOpen(true)}
>
<p className="ellipsis-text h-22 text-xs">
{t('conversation.sources.view_more', {
count: sources?.length ? sources.length - 3 : 0,
})}
</p>
</div>
)}
</div>
</div>
</div>
</div>
)
)}
)}
{toolCalls && toolCalls.length > 0 && (
<ToolCalls toolCalls={toolCalls} />
)}

View File

@@ -284,14 +284,9 @@ export const conversationSlice = createSlice({
query: Partial<Query>;
}>,
) {
const { conversationId, index, query } = action.payload;
if (state.conversationId !== conversationId) return;
if (!state.queries[index].sources) {
state.queries[index].sources = query?.sources;
} else if (query.sources) {
state.queries[index].sources!.push(...query.sources);
}
const { index, query } = action.payload;
if (query.sources !== undefined)
state.queries[index].sources = query.sources;
},
updateToolCall(state, action) {
const { index, tool_call } = action.payload;

View File

@@ -33,6 +33,19 @@ export default function ChunkModal({
setChunkText(originalText || '');
}, [originalTitle, originalText]);
const resetForm = () => {
setTitle('');
setChunkText('');
};
const handleDeleteConfirmed = () => {
if (handleDelete) {
handleDelete();
}
setDeleteModal('INACTIVE');
setModalState('INACTIVE');
};
if (modalState !== 'ACTIVE') return null;
const content = (
@@ -71,6 +84,7 @@ export default function ChunkModal({
onClick={() => {
handleSubmit(title, chunkText);
setModalState('INACTIVE');
resetForm();
}}
className="bg-purple-30 hover:bg-violets-are-blue rounded-3xl px-5 py-2 text-sm text-white transition-all"
>
@@ -79,6 +93,7 @@ export default function ChunkModal({
<button
onClick={() => {
setModalState('INACTIVE');
resetForm();
}}
className="dark:text-light-gray cursor-pointer rounded-3xl px-5 py-2 text-sm font-medium hover:bg-gray-100 dark:bg-transparent dark:hover:bg-[#767183]/50"
>
@@ -124,6 +139,7 @@ export default function ChunkModal({
<WrapperModal
close={() => setModalState('INACTIVE')}
className="sm:w-[620px]"
isPerformingTask={true}
>
{content}
</WrapperModal>
@@ -133,13 +149,7 @@ export default function ChunkModal({
message={t('modals.chunk.deleteConfirmation')}
modalState={deleteModal}
setModalState={setDeleteModal}
handleSubmit={
handleDelete
? handleDelete
: () => {
/* no-op */
}
}
handleSubmit={handleDeleteConfirmed}
submitLabel={t('modals.chunk.delete')}
/>
)}

View File

@@ -29,33 +29,40 @@ export default function ConfirmationModal({
? 'rounded-3xl bg-rosso-corsa px-5 py-2 text-sm text-lotion transition-all hover:bg-red-2000 hover:font-bold tracking-[0.019em] hover:tracking-normal'
: 'rounded-3xl bg-purple-30 px-5 py-2 text-sm text-lotion transition-all hover:bg-violets-are-blue';
const handleSubmitClick = (e: React.MouseEvent) => {
e.preventDefault();
e.stopPropagation();
handleSubmit();
setModalState('INACTIVE');
};
const handleCancelClick = (e: React.MouseEvent) => {
e.preventDefault();
e.stopPropagation();
setModalState('INACTIVE');
handleCancel?.();
};
return (
<>
{modalState === 'ACTIVE' && (
<WrapperModal close={() => setModalState('INACTIVE')}>
<div className="relative">
<div>
<p className="font-base mb-1 w-[90%] break-words text-lg text-jet dark:text-bright-gray">
<p className="font-base text-jet dark:text-bright-gray mb-1 w-[90%] text-lg break-words">
{message}
</p>
<div>
<div className="mt-6 flex flex-row-reverse gap-1">
<button
onClick={(e) => {
e.stopPropagation();
handleSubmit();
}}
onClick={handleSubmitClick}
className={submitButtonClasses}
>
{submitLabel}
</button>
<button
onClick={(e) => {
e.stopPropagation();
setModalState('INACTIVE');
handleCancel && handleCancel();
}}
className="cursor-pointer rounded-3xl px-5 py-2 text-sm font-medium hover:bg-gray-100 dark:bg-transparent dark:text-light-gray dark:hover:bg-[#767183]/50"
onClick={handleCancelClick}
className="dark:text-light-gray cursor-pointer rounded-3xl px-5 py-2 text-sm font-medium hover:bg-gray-100 dark:bg-transparent dark:hover:bg-[#767183]/50"
>
{cancelLabel ? cancelLabel : t('cancel')}
</button>

View File

@@ -3,38 +3,33 @@ import { createPortal } from 'react-dom';
import Exit from '../assets/exit.svg';
interface WrapperModalPropsType {
type WrapperModalPropsType = {
children: React.ReactNode;
close: () => void;
isPerformingTask?: boolean;
className?: string;
contentClassName?: string;
}
};
export default function WrapperModal({
children,
close,
isPerformingTask = false,
className = '', // Default width, but can be overridden
contentClassName = '', // Default padding, but can be overridden
className = '',
contentClassName = '',
}: WrapperModalPropsType) {
const modalRef = useRef<HTMLDivElement>(null);
useEffect(() => {
if (isPerformingTask) return;
const handleClickOutside = (event: MouseEvent) => {
if (
modalRef.current &&
!modalRef.current.contains(event.target as Node)
) {
if (modalRef.current && !modalRef.current.contains(event.target as Node))
close();
}
};
const handleEscapePress = (event: KeyboardEvent) => {
if (event.key === 'Escape') {
close();
}
if (event.key === 'Escape') close();
};
document.addEventListener('mousedown', handleClickOutside);
@@ -44,17 +39,17 @@ export default function WrapperModal({
document.removeEventListener('mousedown', handleClickOutside);
document.removeEventListener('keydown', handleEscapePress);
};
}, [close]);
}, [close, isPerformingTask]);
const modalContent = (
<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 className="bg-gray-alpha bg-opacity-50 fixed top-0 left-0 z-30 flex h-screen w-screen items-center justify-center">
<div
ref={modalRef}
className={`relative w-11/12 rounded-2xl bg-white p-8 dark:bg-[#26272E] sm:w-[512px] ${className}`}
className={`relative w-11/12 rounded-2xl bg-white p-8 sm:w-[512px] dark:bg-[#26272E] ${className}`}
>
{!isPerformingTask && (
<button
className="absolute right-4 top-3 z-50 m-2 w-3"
className="absolute top-3 right-4 z-50 m-2 w-3"
onClick={close}
>
<img className="filter dark:invert" src={Exit} alt="Close" />

View File

@@ -9,8 +9,10 @@ import Edit from '../assets/edit.svg';
import EyeView from '../assets/eye-view.svg';
import NoFilesDarkIcon from '../assets/no-files-dark.svg';
import NoFilesIcon from '../assets/no-files.svg';
import SyncIcon from '../assets/sync.svg';
import Trash from '../assets/red-trash.svg';
import SyncIcon from '../assets/sync.svg';
import ThreeDots from '../assets/three-dots.svg';
import ContextMenu, { MenuOption } from '../components/ContextMenu';
import Pagination from '../components/DocumentPagination';
import DropdownMenu from '../components/DropdownMenu';
import Input from '../components/Input';
@@ -29,8 +31,6 @@ import {
import Upload from '../upload/Upload';
import { formatDate } from '../utils/dateTimeUtils';
import { ChunkType } from './types';
import ContextMenu, { MenuOption } from '../components/ContextMenu';
import ThreeDots from '../assets/three-dots.svg';
const formatTokens = (tokens: number): string => {
const roundToTwoDecimals = (num: number): string => {
@@ -766,19 +766,23 @@ function DocumentChunks({
setModalState={setAddModal}
handleSubmit={handleAddChunk}
/>
<ChunkModal
type="EDIT"
modalState={editModal.state}
setModalState={(state) => setEditModal((prev) => ({ ...prev, state }))}
handleSubmit={(title, text) => {
handleUpdateChunk(title, text, editModal.chunk as ChunkType);
}}
originalText={editModal.chunk?.text}
originalTitle={editModal.chunk?.metadata?.title}
handleDelete={() => {
handleDeleteChunk(editModal.chunk as ChunkType);
}}
/>
{editModal.chunk && (
<ChunkModal
type="EDIT"
modalState={editModal.state}
setModalState={(state) =>
setEditModal((prev) => ({ ...prev, state }))
}
handleSubmit={(title, text) => {
handleUpdateChunk(title, text, editModal.chunk as ChunkType);
}}
originalText={editModal.chunk?.text ?? ''}
originalTitle={editModal.chunk?.metadata?.title ?? ''}
handleDelete={() => {
handleDeleteChunk(editModal.chunk as ChunkType);
}}
/>
)}
</div>
);
}