mirror of
https://github.com/arc53/DocsGPT.git
synced 2026-05-07 06:30:03 +00:00
Merge pull request #2318 from siiddhantt/feat/standardize-css
feat: update styles and improve accessibility across frontend
This commit is contained in:
@@ -37,7 +37,7 @@ function MainLayout() {
|
||||
const [navOpen, setNavOpen] = useState(!(isMobile || isTablet));
|
||||
|
||||
return (
|
||||
<div className="dark:bg-raisin-black relative h-screen overflow-hidden">
|
||||
<div className="bg-background relative h-screen overflow-hidden">
|
||||
<Navigation navOpen={navOpen} setNavOpen={setNavOpen} />
|
||||
<ActionButtons showNewChat={true} showShare={true} />
|
||||
<div
|
||||
|
||||
@@ -21,10 +21,10 @@ export default function Hero({
|
||||
}>;
|
||||
|
||||
return (
|
||||
<div className="text-black-1000 dark:text-bright-gray flex h-full w-full flex-col items-center justify-between">
|
||||
<div className="text-black-1000 dark:text-foreground flex h-full w-full flex-col items-center justify-between">
|
||||
{/* Header Section */}
|
||||
<div className="flex grow flex-col items-center justify-center pt-8 md:pt-0">
|
||||
<div className="mb-4 flex items-center">
|
||||
<div className="mb-px flex items-center">
|
||||
<span className="text-4xl font-semibold">DocsGPT</span>
|
||||
<img className="mb-1 inline w-14" src={DocsGPT3} alt="docsgpt" />
|
||||
</div>
|
||||
@@ -44,9 +44,9 @@ export default function Hero({
|
||||
<button
|
||||
key={key}
|
||||
onClick={() => handleQuestion({ question: demo.query })}
|
||||
className={`border-dark-gray text-just-black hover:bg-cultured dark:border-dim-gray dark:text-chinese-white dark:hover:bg-charleston-green w-full rounded-[66px] border bg-transparent px-6 py-[14px] text-left transition-colors ${key >= 2 ? 'hidden md:block' : ''}`}
|
||||
className={`border-border text-foreground hover:bg-muted dark:hover:bg-muted/50 bg-card w-full rounded-[66px] border px-6 py-3.5 text-left transition-colors dark:bg-transparent ${key >= 2 ? 'hidden md:block' : ''}`}
|
||||
>
|
||||
<p className="text-black-1000 dark:text-bright-gray mb-2 font-semibold">
|
||||
<p className="text-black-1000 dark:text-foreground mb-2 font-semibold">
|
||||
{demo.header}
|
||||
</p>
|
||||
<span className="line-clamp-2 text-gray-700 opacity-60 dark:text-gray-300">
|
||||
|
||||
@@ -328,7 +328,7 @@ export default function Navigation({ navOpen, setNavOpen }: NavigationProps) {
|
||||
/>
|
||||
</button>
|
||||
)}
|
||||
<div className="text-gray-4000 text-[20px] font-medium">
|
||||
<div className="text-muted-foreground text-[20px] font-medium">
|
||||
DocsGPT
|
||||
</div>
|
||||
</div>
|
||||
@@ -338,7 +338,7 @@ export default function Navigation({ navOpen, setNavOpen }: NavigationProps) {
|
||||
ref={navRef}
|
||||
className={`${
|
||||
!navOpen && '-ml-96 md:-ml-72'
|
||||
} bg-lotion dark:border-r-purple-taupe dark:bg-chinese-black fixed top-0 z-20 flex h-full w-72 flex-col border-r border-b-0 transition-all duration-300 ease-in-out dark:text-white`}
|
||||
} bg-sidebar dark:border-r-sidebar-border fixed top-0 z-20 flex h-full w-72 flex-col border-r border-b-0 transition-all duration-300 ease-in-out dark:text-white`}
|
||||
>
|
||||
<div
|
||||
className={'visible mt-2 flex h-[6vh] w-full justify-between md:h-12'}
|
||||
@@ -380,7 +380,7 @@ export default function Navigation({ navOpen, setNavOpen }: NavigationProps) {
|
||||
className={({ isActive }) =>
|
||||
`${
|
||||
isActive ? 'bg-transparent' : ''
|
||||
} group border-silver hover:border-rainy-gray dark:border-purple-taupe sticky mx-4 mt-4 flex cursor-pointer gap-2.5 rounded-3xl border p-3 hover:bg-transparent dark:text-white`
|
||||
} group border-sidebar-border hover:border-sidebar-border sticky mx-4 mt-4 flex cursor-pointer gap-2.5 rounded-3xl border p-3 hover:bg-transparent dark:text-white`
|
||||
}
|
||||
>
|
||||
<img
|
||||
@@ -388,13 +388,13 @@ export default function Navigation({ navOpen, setNavOpen }: NavigationProps) {
|
||||
alt="Create new chat"
|
||||
className="opacity-80 group-hover:opacity-100"
|
||||
/>
|
||||
<p className="text-dove-gray dark:text-chinese-silver dark:group-hover:text-bright-gray text-sm group-hover:text-neutral-600">
|
||||
<p className="text-muted-foreground dark:text-foreground dark:group-hover:text-bright-gray text-sm group-hover:text-neutral-600">
|
||||
{t('newChat')}
|
||||
</p>
|
||||
</NavLink>
|
||||
<div
|
||||
id="conversationsMainDiv"
|
||||
className="mb-auto h-[78vh] overflow-x-hidden overflow-y-auto scrollbar-overlay dark:text-white"
|
||||
className="scrollbar-overlay mb-auto h-[78vh] overflow-x-hidden overflow-y-auto dark:text-white"
|
||||
>
|
||||
{conversations?.loading && !isDeletingConversation && (
|
||||
<div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 transform">
|
||||
@@ -417,9 +417,9 @@ export default function Navigation({ navOpen, setNavOpen }: NavigationProps) {
|
||||
{recentAgents.map((agent, idx) => (
|
||||
<div
|
||||
key={idx}
|
||||
className={`group hover:bg-bright-gray dark:hover:bg-dark-charcoal mx-4 my-auto mt-4 flex h-9 cursor-pointer items-center justify-between rounded-3xl pl-4 ${
|
||||
className={`group hover:bg-sidebar-accent mx-4 my-auto mt-4 flex h-9 cursor-pointer items-center justify-between rounded-3xl pl-4 ${
|
||||
agent.id === selectedAgent?.id && !conversationId
|
||||
? 'bg-bright-gray dark:bg-dark-charcoal'
|
||||
? 'bg-sidebar-accent'
|
||||
: ''
|
||||
}`}
|
||||
onClick={() => handleAgentClick(agent)}
|
||||
@@ -432,7 +432,7 @@ export default function Navigation({ navOpen, setNavOpen }: NavigationProps) {
|
||||
className="h-6 w-6 rounded-full object-contain"
|
||||
/>
|
||||
</div>
|
||||
<p className="text-eerie-black dark:text-bright-gray overflow-hidden text-sm leading-6 text-ellipsis whitespace-nowrap">
|
||||
<p className="text-foreground dark:text-foreground overflow-hidden text-sm leading-6 text-ellipsis whitespace-nowrap">
|
||||
{agent.name}
|
||||
</p>
|
||||
</div>
|
||||
@@ -456,7 +456,7 @@ export default function Navigation({ navOpen, setNavOpen }: NavigationProps) {
|
||||
))}
|
||||
</div>
|
||||
<div
|
||||
className="hover:bg-bright-gray dark:hover:bg-dark-charcoal mx-4 my-auto mt-2 flex h-9 cursor-pointer items-center gap-2 rounded-3xl pl-4"
|
||||
className="hover:bg-sidebar-accent mx-4 my-auto mt-2 flex h-9 cursor-pointer items-center gap-2 rounded-3xl pl-4"
|
||||
onClick={() => {
|
||||
dispatch(setSelectedAgent(null));
|
||||
if (isMobile || isTablet) {
|
||||
@@ -472,7 +472,7 @@ export default function Navigation({ navOpen, setNavOpen }: NavigationProps) {
|
||||
className="h-[18px] w-[18px]"
|
||||
/>
|
||||
</div>
|
||||
<p className="text-eerie-black dark:text-bright-gray overflow-hidden text-sm leading-6 text-ellipsis whitespace-nowrap">
|
||||
<p className="text-foreground dark:text-foreground overflow-hidden text-sm leading-6 text-ellipsis whitespace-nowrap">
|
||||
{t('manageAgents')}
|
||||
</p>
|
||||
</div>
|
||||
@@ -480,7 +480,7 @@ export default function Navigation({ navOpen, setNavOpen }: NavigationProps) {
|
||||
</div>
|
||||
) : (
|
||||
<div
|
||||
className="hover:bg-bright-gray dark:hover:bg-dark-charcoal mx-4 my-auto mt-2 flex h-9 cursor-pointer items-center gap-2 rounded-3xl pl-4"
|
||||
className="hover:bg-sidebar-accent mx-4 my-auto mt-2 flex h-9 cursor-pointer items-center gap-2 rounded-3xl pl-4"
|
||||
onClick={() => {
|
||||
if (isMobile || isTablet) {
|
||||
setNavOpen(false);
|
||||
@@ -496,7 +496,7 @@ export default function Navigation({ navOpen, setNavOpen }: NavigationProps) {
|
||||
className="h-[18px] w-[18px]"
|
||||
/>
|
||||
</div>
|
||||
<p className="text-eerie-black dark:text-bright-gray overflow-hidden text-sm leading-6 text-ellipsis whitespace-nowrap">
|
||||
<p className="text-foreground dark:text-foreground overflow-hidden text-sm leading-6 text-ellipsis whitespace-nowrap">
|
||||
{t('manageAgents')}
|
||||
</p>
|
||||
</div>
|
||||
@@ -529,8 +529,8 @@ export default function Navigation({ navOpen, setNavOpen }: NavigationProps) {
|
||||
<></>
|
||||
)}
|
||||
</div>
|
||||
<div className="text-eerie-black flex h-auto flex-col justify-end dark:text-white">
|
||||
<div className="dark:border-b-purple-taupe flex flex-col gap-2 border-b py-2">
|
||||
<div className="text-foreground flex h-auto flex-col justify-end dark:text-white">
|
||||
<div className="dark:border-b-sidebar-border flex flex-col gap-2 border-b py-2">
|
||||
<NavLink
|
||||
onClick={() => {
|
||||
if (isMobile || isTablet) {
|
||||
@@ -540,8 +540,8 @@ export default function Navigation({ navOpen, setNavOpen }: NavigationProps) {
|
||||
}}
|
||||
to="/settings"
|
||||
className={({ isActive }) =>
|
||||
`mx-4 my-auto flex h-9 cursor-pointer items-center gap-4 rounded-3xl hover:bg-gray-100 dark:hover:bg-[#28292E] ${
|
||||
isActive ? 'bg-gray-3000 dark:bg-transparent' : ''
|
||||
`hover:bg-sidebar-accent mx-4 my-auto flex h-9 cursor-pointer items-center gap-4 rounded-3xl ${
|
||||
isActive ? 'bg-sidebar-accent' : ''
|
||||
}`
|
||||
}
|
||||
>
|
||||
@@ -552,12 +552,12 @@ export default function Navigation({ navOpen, setNavOpen }: NavigationProps) {
|
||||
height={21}
|
||||
className="my-auto ml-2 filter dark:invert"
|
||||
/>
|
||||
<p className="text-eerie-black text-sm dark:text-white">
|
||||
<p className="text-foreground text-sm dark:text-white">
|
||||
{t('settings.label')}
|
||||
</p>
|
||||
</NavLink>
|
||||
</div>
|
||||
<div className="text-eerie-black flex flex-col justify-end dark:text-white">
|
||||
<div className="text-foreground flex flex-col justify-end dark:text-white">
|
||||
<div className="flex items-center justify-between py-1">
|
||||
<Help />
|
||||
|
||||
@@ -565,9 +565,7 @@ export default function Navigation({ navOpen, setNavOpen }: NavigationProps) {
|
||||
<NavLink
|
||||
target="_blank"
|
||||
to={'https://discord.gg/vN7YFfdMpj'}
|
||||
className={
|
||||
'rounded-full hover:bg-gray-100 dark:hover:bg-[#28292E]'
|
||||
}
|
||||
className={'hover:bg-sidebar-accent rounded-full'}
|
||||
>
|
||||
<img
|
||||
src={Discord}
|
||||
@@ -580,9 +578,7 @@ export default function Navigation({ navOpen, setNavOpen }: NavigationProps) {
|
||||
<NavLink
|
||||
target="_blank"
|
||||
to={'https://x.com/docsgptai'}
|
||||
className={
|
||||
'rounded-full hover:bg-gray-100 dark:hover:bg-[#28292E]'
|
||||
}
|
||||
className={'hover:bg-sidebar-accent rounded-full'}
|
||||
>
|
||||
<img
|
||||
src={Twitter}
|
||||
@@ -595,9 +591,7 @@ export default function Navigation({ navOpen, setNavOpen }: NavigationProps) {
|
||||
<NavLink
|
||||
target="_blank"
|
||||
to={'https://github.com/arc53/docsgpt'}
|
||||
className={
|
||||
'rounded-full hover:bg-gray-100 dark:hover:bg-[#28292E]'
|
||||
}
|
||||
className={'hover:bg-sidebar-accent rounded-full'}
|
||||
>
|
||||
<img
|
||||
src={Github}
|
||||
@@ -612,7 +606,7 @@ export default function Navigation({ navOpen, setNavOpen }: NavigationProps) {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="dark:border-b-purple-taupe dark:bg-chinese-black sticky z-10 h-16 w-full border-b-2 bg-gray-50 lg:hidden">
|
||||
<div className="dark:border-b-sidebar-border bg-sidebar sticky z-10 h-16 w-full border-b-2 lg:hidden">
|
||||
<div className="ml-6 flex h-full items-center gap-6">
|
||||
<button
|
||||
className="h-6 w-6 lg:hidden"
|
||||
@@ -624,7 +618,9 @@ export default function Navigation({ navOpen, setNavOpen }: NavigationProps) {
|
||||
className="w-7 filter dark:invert"
|
||||
/>
|
||||
</button>
|
||||
<div className="text-gray-4000 text-[20px] font-medium">DocsGPT</div>
|
||||
<div className="text-muted-foreground text-[20px] font-medium">
|
||||
DocsGPT
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<DeleteConvModal
|
||||
|
||||
@@ -5,8 +5,8 @@ export default function PageNotFound() {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<div className="dark:bg-raisin-black grid min-h-screen">
|
||||
<p className="text-jet dark:bg-outer-space 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 lg:p-10 xl:p-16 dark:text-gray-100">
|
||||
<div className="bg-background grid min-h-screen">
|
||||
<p className="text-foreground dark:bg-card 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 lg:p-10 xl:p-16">
|
||||
<h1>{t('pageNotFound.title')}</h1>
|
||||
<p>{t('pageNotFound.message')}</p>
|
||||
<button className="pointer-cursor bg-blue-1000 hover:bg-blue-3000 mr-4 flex cursor-pointer items-center justify-center rounded-full px-4 py-2 text-white transition-colors duration-100">
|
||||
|
||||
@@ -251,7 +251,7 @@ export default function AgentCard({
|
||||
};
|
||||
return (
|
||||
<div
|
||||
className={`relative flex h-44 flex-col justify-between rounded-[1.2rem] bg-[#F6F6F6] px-4 py-5 hover:bg-[#ECECEC] sm:w-48 sm:px-6 dark:bg-[#383838] dark:hover:bg-[#383838]/80 ${agent.status === 'published' && 'cursor-pointer'}`}
|
||||
className={`bg-muted hover:bg-accent relative flex h-44 flex-col justify-between rounded-[1.2rem] px-4 py-5 sm:w-48 sm:px-6 ${agent.status === 'published' && 'cursor-pointer'}`}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
handleClick();
|
||||
@@ -283,17 +283,17 @@ export default function AgentCard({
|
||||
className="h-7 w-7 rounded-full object-contain"
|
||||
/>
|
||||
{agent.status === 'draft' && (
|
||||
<p className="text-xs text-black opacity-50 dark:text-[#E0E0E0]">{`(Draft)`}</p>
|
||||
<p className="text-foreground text-xs opacity-50">{`(Draft)`}</p>
|
||||
)}
|
||||
</div>
|
||||
<div className="mt-2">
|
||||
<p
|
||||
title={agent.name}
|
||||
className="truncate px-1 text-[13px] leading-relaxed font-semibold text-[#020617] capitalize dark:text-[#E0E0E0]"
|
||||
className="text-foreground truncate px-1 text-[13px] leading-relaxed font-semibold capitalize"
|
||||
>
|
||||
{agent.name}
|
||||
</p>
|
||||
<p className="dark:text-sonic-silver-light mt-1 h-20 overflow-auto px-1 text-[12px] leading-relaxed text-[#64748B]">
|
||||
<p className="dark:text-muted-foreground text-muted-foreground mt-1 h-20 overflow-auto px-1 text-[12px] leading-relaxed">
|
||||
{agent.description}
|
||||
</p>
|
||||
</div>
|
||||
@@ -320,4 +320,4 @@ export default function AgentCard({
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,25 +41,25 @@ export default function AgentLogs() {
|
||||
<div className="p-4 md:p-12">
|
||||
<div className="flex items-center gap-3 px-4">
|
||||
<button
|
||||
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]"
|
||||
className="border-border text-muted-foreground hover:bg-accent rounded-full border p-3 text-sm"
|
||||
onClick={() => navigate('/agents')}
|
||||
>
|
||||
<img src={ArrowLeft} alt="left-arrow" className="h-3 w-3" />
|
||||
</button>
|
||||
<p className="text-eerie-black dark:text-bright-gray mt-px text-sm font-semibold">
|
||||
<p className="text-foreground dark:text-foreground mt-px text-sm font-semibold">
|
||||
{t('agents.backToAll')}
|
||||
</p>
|
||||
</div>
|
||||
<div className="mt-5 flex w-full flex-wrap items-center justify-between gap-2 px-4">
|
||||
<h1 className="text-eerie-black m-0 text-[32px] font-bold md:text-[40px] dark:text-white">
|
||||
<h1 className="text-foreground m-0 text-[32px] font-bold md:text-[40px] dark:text-white">
|
||||
{t('agents.logs.title')}
|
||||
</h1>
|
||||
</div>
|
||||
<div className="mt-6 flex flex-col gap-3 px-4">
|
||||
{agent && (
|
||||
<div className="flex flex-col gap-1">
|
||||
<p className="text-[#28292E] dark:text-[#E0E0E0]">{agent.name}</p>
|
||||
<p className="text-xs text-[#28292E] dark:text-[#E0E0E0]/40">
|
||||
<p className="text-foreground">{agent.name}</p>
|
||||
<p className="text-muted-foreground text-xs">
|
||||
{agent.last_used_at
|
||||
? t('agents.logs.lastUsedAt') +
|
||||
' ' +
|
||||
|
||||
@@ -131,7 +131,7 @@ export default function AgentPreview() {
|
||||
autoFocus={false}
|
||||
/>
|
||||
</div>
|
||||
<p className="text-gray-4000 dark:text-sonic-silver w-full bg-transparent text-center text-xs md:inline">
|
||||
<p className="text-muted-foreground w-full bg-transparent text-center text-xs md:inline">
|
||||
{t('agents.preview.testMessage')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -159,10 +159,10 @@ export default function AgentsList() {
|
||||
|
||||
return (
|
||||
<div className="p-4 md:p-12">
|
||||
<h1 className="text-eerie-black mb-0 text-[32px] font-bold lg:text-[40px] dark:text-[#E0E0E0]">
|
||||
<h1 className="text-foreground mb-0 text-[32px] font-bold lg:text-[40px]">
|
||||
{t('agents.title')}
|
||||
</h1>
|
||||
<p className="dark:text-gray-4000 mt-5 text-[15px] leading-6 text-[#71717A]">
|
||||
<p className="text-muted-foreground mt-5 text-[15px] leading-6">
|
||||
{t('agents.description')}
|
||||
</p>
|
||||
|
||||
@@ -178,7 +178,7 @@ export default function AgentsList() {
|
||||
value={searchQuery}
|
||||
onChange={(e) => setSearchQuery(e.target.value)}
|
||||
placeholder={t('agents.searchPlaceholder')}
|
||||
className="h-11 w-full rounded-full border border-[#E5E5E5] bg-white py-2 pr-5 pl-11 text-sm shadow-[0_1px_4px_rgba(0,0,0,0.06)] transition-shadow outline-none placeholder:text-[#9CA3AF] focus:shadow-[0_2px_8px_rgba(0,0,0,0.1)] dark:border-[#3A3A3A] dark:bg-[#2C2C2C] dark:text-white dark:shadow-none dark:placeholder:text-[#6B7280]"
|
||||
className="border-border bg-card text-foreground placeholder:text-muted-foreground h-11 w-full rounded-full border py-2 pr-5 pl-11 text-sm shadow-[0_1px_4px_rgba(0,0,0,0.06)] transition-shadow outline-none focus:shadow-[0_2px_8px_rgba(0,0,0,0.1)] dark:shadow-none"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -189,8 +189,8 @@ export default function AgentsList() {
|
||||
onClick={() => setActiveFilter(tab.id)}
|
||||
className={`rounded-full px-4 py-2 text-sm transition-colors ${
|
||||
activeFilter === tab.id
|
||||
? 'bg-[#E0E0E0] text-[#18181B] dark:bg-[#4A4A4A] dark:text-white'
|
||||
: 'dark:text-gray bg-transparent text-[#71717A] hover:bg-[#F5F5F5] dark:hover:bg-[#383838]/50'
|
||||
? 'bg-border text-foreground dark:bg-accent dark:text-white'
|
||||
: 'dark:text-gray text-muted-foreground hover:bg-accent/50 bg-transparent'
|
||||
}`}
|
||||
>
|
||||
{t(tab.labelKey)}
|
||||
@@ -224,7 +224,7 @@ export default function AgentsList() {
|
||||
))}
|
||||
|
||||
{showSearchEmptyState && (
|
||||
<div className="mt-12 flex flex-col items-center justify-center gap-2 text-[#71717A]">
|
||||
<div className="text-muted-foreground mt-12 flex flex-col items-center justify-center gap-2">
|
||||
<p className="text-lg">{t('agents.noSearchResults')}</p>
|
||||
<p className="text-sm">{t('agents.tryDifferentSearch')}</p>
|
||||
</div>
|
||||
@@ -399,7 +399,7 @@ function AgentSection({
|
||||
|
||||
if (isFilteredView && isSearchingWithNoResults) {
|
||||
return (
|
||||
<div className="mt-12 flex flex-col items-center justify-center gap-2 text-[#71717A]">
|
||||
<div className="text-muted-foreground mt-12 flex flex-col items-center justify-center gap-2">
|
||||
<p className="text-lg">{t('agents.noSearchResults')}</p>
|
||||
<p className="text-sm">{t('agents.tryDifferentSearch')}</p>
|
||||
</div>
|
||||
@@ -408,11 +408,11 @@ function AgentSection({
|
||||
|
||||
if (isFilteredView && hasNoAgentsAtAll) {
|
||||
return (
|
||||
<div className="mt-12 flex flex-col items-center justify-center gap-3 text-[#71717A]">
|
||||
<div className="text-muted-foreground mt-12 flex flex-col items-center justify-center gap-3">
|
||||
<p>{t(`agents.sections.${config.id}.emptyState`)}</p>
|
||||
{config.showNewAgentButton && (
|
||||
<button
|
||||
className="bg-purple-30 hover:bg-violets-are-blue rounded-full px-4 py-2 text-sm text-white"
|
||||
className="bg-primary hover:bg-primary/90 rounded-full px-4 py-2 text-sm text-white"
|
||||
onClick={() => {
|
||||
setModalFolderId(null);
|
||||
setShowAgentTypeModal(true);
|
||||
@@ -456,12 +456,12 @@ function AgentSection({
|
||||
<div className="mt-8 flex flex-col gap-4">
|
||||
<div className="flex w-full flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
|
||||
<div className="flex flex-col gap-2">
|
||||
<h2 className="flex flex-wrap items-center gap-2 text-[18px] font-semibold text-[#18181B] dark:text-[#E0E0E0]">
|
||||
<h2 className="text-foreground flex flex-wrap items-center gap-2 text-[18px] font-semibold">
|
||||
{config.id === 'user' && folderPath.length > 0 ? (
|
||||
<>
|
||||
<button
|
||||
onClick={() => handleNavigateToPath(-1)}
|
||||
className="text-[#71717A] hover:text-[#18181B] dark:hover:text-white"
|
||||
className="text-muted-foreground hover:text-foreground dark:hover:text-white"
|
||||
>
|
||||
{t(`agents.sections.${config.id}.title`)}
|
||||
</button>
|
||||
@@ -473,7 +473,7 @@ function AgentSection({
|
||||
) : (
|
||||
<button
|
||||
onClick={() => handleNavigateToPath(index)}
|
||||
className="text-[#71717A] hover:text-[#18181B] dark:hover:text-white"
|
||||
className="text-muted-foreground hover:text-foreground dark:hover:text-white"
|
||||
>
|
||||
{item.name}
|
||||
</button>
|
||||
@@ -485,7 +485,7 @@ function AgentSection({
|
||||
t(`agents.sections.${config.id}.title`)
|
||||
)}
|
||||
</h2>
|
||||
<p className="text-[13px] text-[#71717A]">
|
||||
<p className="text-muted-foreground text-[13px]">
|
||||
{t(`agents.sections.${config.id}.description`)}
|
||||
</p>
|
||||
</div>
|
||||
@@ -513,12 +513,12 @@ function AgentSection({
|
||||
}
|
||||
}}
|
||||
placeholder={t('agents.folders.newFolder')}
|
||||
className="w-28 rounded-full border border-[#E5E5E5] bg-white px-4 py-2 text-sm text-[#18181B] outline-none placeholder:text-[#9CA3AF] sm:w-auto dark:border-[#3A3A3A] dark:bg-[#2C2C2C] dark:text-white dark:placeholder:text-[#6B7280]"
|
||||
className="border-border bg-card text-foreground placeholder:text-muted-foreground w-28 rounded-full border px-4 py-2 text-sm outline-none sm:w-auto"
|
||||
autoFocus
|
||||
/>
|
||||
) : (
|
||||
<button
|
||||
className="shrink-0 rounded-full border border-[#E5E5E5] bg-white px-4 py-2 text-sm whitespace-nowrap text-[#18181B] hover:bg-[#F5F5F5] dark:border-[#3A3A3A] dark:bg-[#2C2C2C] dark:text-white dark:hover:bg-[#383838]"
|
||||
className="border-border bg-card text-foreground hover:bg-accent shrink-0 rounded-full border px-4 py-2 text-sm whitespace-nowrap"
|
||||
onClick={() => {
|
||||
setIsCreatingFolder(true);
|
||||
setTimeout(() => newFolderInputRef.current?.focus(), 0);
|
||||
@@ -529,7 +529,7 @@ function AgentSection({
|
||||
))}
|
||||
{config.showNewAgentButton && (
|
||||
<button
|
||||
className="bg-purple-30 hover:bg-violets-are-blue shrink-0 rounded-full px-4 py-2 text-sm whitespace-nowrap text-white"
|
||||
className="bg-primary hover:bg-primary/90 shrink-0 rounded-full px-4 py-2 text-sm whitespace-nowrap text-white"
|
||||
onClick={() => {
|
||||
setModalFolderId(currentFolderId);
|
||||
setShowAgentTypeModal(true);
|
||||
@@ -579,7 +579,7 @@ function AgentSection({
|
||||
))}
|
||||
</div>
|
||||
) : hasNoAgentsAtAll && currentLevelFolders.length === 0 ? (
|
||||
<div className="flex h-40 w-full flex-col items-center justify-center gap-3 text-[#71717A]">
|
||||
<div className="text-muted-foreground flex h-40 w-full flex-col items-center justify-center gap-3">
|
||||
<p>
|
||||
{currentFolderId
|
||||
? t('agents.folders.empty')
|
||||
@@ -587,7 +587,7 @@ function AgentSection({
|
||||
</p>
|
||||
{config.showNewAgentButton && !currentFolderId && (
|
||||
<button
|
||||
className="bg-purple-30 hover:bg-violets-are-blue ml-2 rounded-full px-4 py-2 text-sm text-white"
|
||||
className="bg-primary hover:bg-primary/90 ml-2 rounded-full px-4 py-2 text-sm text-white"
|
||||
onClick={() => {
|
||||
setModalFolderId(currentFolderId);
|
||||
setShowAgentTypeModal(true);
|
||||
@@ -603,4 +603,4 @@ function AgentSection({
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,17 +70,15 @@ export default function FolderCard({
|
||||
<>
|
||||
<div
|
||||
className={`relative flex cursor-pointer items-center justify-between rounded-[1.2rem] px-4 py-3 sm:w-48 ${
|
||||
isExpanded
|
||||
? 'bg-[#E5E5E5] dark:bg-[#454545]'
|
||||
: 'bg-[#F6F6F6] hover:bg-[#ECECEC] dark:bg-[#383838] dark:hover:bg-[#383838]/80'
|
||||
isExpanded ? 'bg-accent' : 'bg-muted hover:bg-accent'
|
||||
}`}
|
||||
onClick={() => onToggleExpand(folder.id)}
|
||||
>
|
||||
<div className="flex items-center gap-2 overflow-hidden">
|
||||
<span className="truncate text-sm font-medium text-[#18181B] dark:text-[#E0E0E0]">
|
||||
<span className="text-foreground truncate text-sm font-medium">
|
||||
{folder.name}
|
||||
</span>
|
||||
<span className="shrink-0 text-xs text-[#71717A]">
|
||||
<span className="text-muted-foreground shrink-0 text-xs">
|
||||
({agentCount})
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -677,17 +677,17 @@ export default function NewAgent({ mode }: { mode: 'new' | 'edit' | 'draft' }) {
|
||||
<div className="flex flex-col px-4 pt-4 pb-2 max-[1179px]:min-h-dvh min-[1180px]:h-dvh md:px-12 md:pt-12 md:pb-3">
|
||||
<div className="flex items-center gap-3 px-4">
|
||||
<button
|
||||
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]"
|
||||
className="border-border text-muted-foreground hover:bg-accent rounded-full border p-3 text-sm"
|
||||
onClick={handleCancel}
|
||||
>
|
||||
<img src={ArrowLeft} alt="left-arrow" className="h-3 w-3" />
|
||||
</button>
|
||||
<p className="text-eerie-black dark:text-bright-gray mt-px text-sm font-semibold">
|
||||
<p className="text-foreground dark:text-foreground mt-px text-sm font-semibold">
|
||||
{t('agents.backToAll')}
|
||||
</p>
|
||||
</div>
|
||||
<div className="mt-5 flex w-full flex-wrap items-center justify-between gap-2 px-4">
|
||||
<h1 className="text-eerie-black m-0 text-[32px] font-bold lg:text-[40px] dark:text-white">
|
||||
<h1 className="text-foreground m-0 text-[32px] font-bold lg:text-[40px] dark:text-white">
|
||||
{modeConfig[effectiveMode].heading}
|
||||
</h1>
|
||||
{agent.agent_type === 'workflow' && (
|
||||
@@ -697,14 +697,14 @@ export default function NewAgent({ mode }: { mode: 'new' | 'edit' | 'draft' }) {
|
||||
)}
|
||||
<div className="flex flex-wrap items-center gap-1">
|
||||
<button
|
||||
className="text-purple-30 dark:text-light-gray mr-4 rounded-3xl py-2 text-sm font-medium dark:bg-transparent"
|
||||
className="text-primary dark:text-foreground mr-4 rounded-3xl py-2 text-sm font-medium"
|
||||
onClick={handleCancel}
|
||||
>
|
||||
{t('agents.form.buttons.cancel')}
|
||||
</button>
|
||||
{modeConfig[effectiveMode].showDelete && agent.id && (
|
||||
<button
|
||||
className="group border-red-2000 text-red-2000 hover:bg-red-2000 flex items-center gap-2 rounded-3xl border border-solid px-5 py-2 text-sm font-medium transition-colors hover:text-white"
|
||||
className="group border-destructive text-destructive hover:bg-destructive flex items-center gap-2 rounded-3xl border border-solid px-5 py-2 text-sm font-medium transition-colors hover:text-white"
|
||||
onClick={() => setDeleteConfirmation('ACTIVE')}
|
||||
>
|
||||
<span className="block h-4 w-4 bg-[url('/src/assets/red-trash.svg')] bg-contain bg-center bg-no-repeat transition-all group-hover:bg-[url('/src/assets/white-trash.svg')]" />
|
||||
@@ -714,7 +714,7 @@ export default function NewAgent({ mode }: { mode: 'new' | 'edit' | 'draft' }) {
|
||||
{modeConfig[effectiveMode].showSaveDraft && (
|
||||
<button
|
||||
disabled={isJsonSchemaInvalid()}
|
||||
className={`border-violets-are-blue text-violets-are-blue hover:bg-violets-are-blue flex min-w-28 items-center justify-center rounded-3xl border border-solid px-5 py-2 text-sm font-medium whitespace-nowrap transition-colors hover:text-white ${
|
||||
className={`border-primary text-primary hover:bg-primary/90 flex min-w-28 items-center justify-center rounded-3xl border border-solid px-5 py-2 text-sm font-medium whitespace-nowrap transition-colors hover:text-white ${
|
||||
isJsonSchemaInvalid() ? 'cursor-not-allowed opacity-30' : ''
|
||||
}`}
|
||||
onClick={handleSaveDraft}
|
||||
@@ -730,7 +730,7 @@ export default function NewAgent({ mode }: { mode: 'new' | 'edit' | 'draft' }) {
|
||||
)}
|
||||
{modeConfig[effectiveMode].showAccessDetails && (
|
||||
<button
|
||||
className="group border-violets-are-blue text-violets-are-blue hover:bg-violets-are-blue flex items-center gap-2 rounded-3xl border border-solid px-5 py-2 text-sm font-medium transition-colors hover:text-white"
|
||||
className="group border-primary text-primary hover:bg-primary/90 flex items-center gap-2 rounded-3xl border border-solid px-5 py-2 text-sm font-medium transition-colors hover:text-white"
|
||||
onClick={() => navigate(`/agents/logs/${agent.id}`)}
|
||||
>
|
||||
<span className="block h-5 w-5 bg-[url('/src/assets/monitoring-purple.svg')] bg-contain bg-center bg-no-repeat transition-all group-hover:bg-[url('/src/assets/monitoring-white.svg')]" />
|
||||
@@ -739,7 +739,7 @@ export default function NewAgent({ mode }: { mode: 'new' | 'edit' | 'draft' }) {
|
||||
)}
|
||||
{modeConfig[effectiveMode].showAccessDetails && (
|
||||
<button
|
||||
className="hover:bg-vi</button>olets-are-blue border-violets-are-blue text-violets-are-blue hover:bg-violets-are-blue rounded-3xl border border-solid px-5 py-2 text-sm font-medium transition-colors hover:text-white"
|
||||
className="border-primary text-primary hover:bg-primary/90 rounded-3xl border border-solid px-5 py-2 text-sm font-medium transition-colors hover:text-white"
|
||||
onClick={() => setAgentDetails('ACTIVE')}
|
||||
>
|
||||
{t('agents.form.buttons.accessDetails')}
|
||||
@@ -747,7 +747,7 @@ export default function NewAgent({ mode }: { mode: 'new' | 'edit' | 'draft' }) {
|
||||
)}
|
||||
<button
|
||||
disabled={!isPublishable() || !hasChanges}
|
||||
className={`${!isPublishable() || !hasChanges ? 'cursor-not-allowed opacity-30' : ''} bg-purple-30 hover:bg-violets-are-blue flex min-w-28 items-center justify-center rounded-3xl px-5 py-2 text-sm font-medium whitespace-nowrap text-white`}
|
||||
className={`${!isPublishable() || !hasChanges ? 'cursor-not-allowed opacity-30' : ''} bg-primary hover:bg-primary/90 flex min-w-28 items-center justify-center rounded-3xl px-5 py-2 text-sm font-medium whitespace-nowrap text-white`}
|
||||
onClick={handlePublish}
|
||||
>
|
||||
<span className="flex items-center justify-center transition-all duration-200">
|
||||
@@ -760,21 +760,21 @@ export default function NewAgent({ mode }: { mode: 'new' | 'edit' | 'draft' }) {
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-3 flex w-full flex-1 grid-cols-5 flex-col gap-10 rounded-[30px] bg-[#F6F6F6] p-5 max-[1179px]:overflow-visible min-[1180px]:grid min-[1180px]:gap-5 min-[1180px]:overflow-hidden dark:bg-[#383838]">
|
||||
<div className="bg-muted dark:bg-background mt-3 flex w-full flex-1 grid-cols-5 flex-col gap-10 rounded-[30px] p-5 max-[1179px]:overflow-visible min-[1180px]:grid min-[1180px]:gap-5 min-[1180px]:overflow-hidden">
|
||||
<div className="scrollbar-overlay col-span-2 flex flex-col gap-5 max-[1179px]:overflow-visible min-[1180px]:max-h-full min-[1180px]:overflow-y-auto min-[1180px]:pr-3">
|
||||
<div className="dark:bg-raisin-black rounded-[30px] bg-white px-6 py-3 dark:text-[#E0E0E0]">
|
||||
<div className="bg-card rounded-[30px] px-6 py-3">
|
||||
<h2 className="text-lg font-semibold">
|
||||
{t('agents.form.sections.meta')}
|
||||
</h2>
|
||||
<input
|
||||
className="border-silver text-jet dark:bg-raisin-black dark:text-bright-gray dark:placeholder:text-silver mt-3 w-full rounded-3xl border bg-white px-5 py-3 text-sm outline-hidden placeholder:text-gray-400 dark:border-[#7E7E7E]"
|
||||
className="border-border text-foreground dark:text-foreground dark:placeholder:text-silver bg-card dark:border-border mt-3 w-full rounded-3xl border px-5 py-3 text-sm outline-hidden placeholder:text-gray-400"
|
||||
type="text"
|
||||
value={agent.name}
|
||||
placeholder={t('agents.form.placeholders.agentName')}
|
||||
onChange={(e) => setAgent({ ...agent, name: e.target.value })}
|
||||
/>
|
||||
<textarea
|
||||
className="border-silver text-jet dark:bg-raisin-black dark:text-bright-gray dark:placeholder:text-silver mt-3 h-32 w-full rounded-xl border bg-white px-5 py-4 text-sm outline-hidden placeholder:text-gray-400 dark:border-[#7E7E7E]"
|
||||
className="border-border text-foreground dark:text-foreground dark:placeholder:text-silver bg-card dark:border-border mt-3 h-32 w-full rounded-xl border px-5 py-4 text-sm outline-hidden placeholder:text-gray-400"
|
||||
placeholder={t('agents.form.placeholders.describeAgent')}
|
||||
value={agent.description}
|
||||
onChange={(e) =>
|
||||
@@ -784,7 +784,7 @@ export default function NewAgent({ mode }: { mode: 'new' | 'edit' | 'draft' }) {
|
||||
<div className="mt-3">
|
||||
<FileUpload
|
||||
showPreview
|
||||
className="dark:bg-raisin-black"
|
||||
className="bg-card"
|
||||
onUpload={handleUpload}
|
||||
onRemove={() => setImageFile(null)}
|
||||
uploadText={[
|
||||
@@ -800,7 +800,7 @@ export default function NewAgent({ mode }: { mode: 'new' | 'edit' | 'draft' }) {
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="dark:bg-raisin-black rounded-[30px] bg-white px-6 py-3 dark:text-[#E0E0E0]">
|
||||
<div className="bg-card rounded-[30px] px-6 py-3">
|
||||
<h2 className="text-lg font-semibold">
|
||||
{t('agents.form.sections.source')}
|
||||
</h2>
|
||||
@@ -809,10 +809,10 @@ export default function NewAgent({ mode }: { mode: 'new' | 'edit' | 'draft' }) {
|
||||
<button
|
||||
ref={sourceAnchorButtonRef}
|
||||
onClick={() => setIsSourcePopupOpen(!isSourcePopupOpen)}
|
||||
className={`border-silver dark:bg-raisin-black w-full truncate rounded-3xl border bg-white px-5 py-3 text-left text-sm dark:border-[#7E7E7E] ${
|
||||
className={`border-border bg-card dark:border-border w-full truncate rounded-3xl border px-5 py-3 text-left text-sm ${
|
||||
selectedSourceIds.size > 0
|
||||
? 'text-jet dark:text-bright-gray'
|
||||
: 'dark:text-silver text-gray-400'
|
||||
? 'text-foreground dark:text-foreground'
|
||||
: 'dark:text-muted-foreground text-gray-400'
|
||||
}`}
|
||||
>
|
||||
{selectedSourceIds.size > 0
|
||||
@@ -892,17 +892,13 @@ export default function NewAgent({ mode }: { mode: 'new' | 'edit' | 'draft' }) {
|
||||
}
|
||||
size="w-full"
|
||||
rounded="3xl"
|
||||
border="border"
|
||||
buttonClassName="bg-white dark:bg-[#222327] border-silver dark:border-[#7E7E7E]"
|
||||
optionsClassName="bg-white dark:bg-[#383838] border-silver dark:border-[#7E7E7E]"
|
||||
placeholder={t('agents.form.placeholders.chunksPerQuery')}
|
||||
placeholderClassName="text-gray-400 dark:text-silver"
|
||||
contentSize="text-sm"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="dark:bg-raisin-black rounded-[30px] bg-white px-6 py-3 dark:text-[#E0E0E0]">
|
||||
<div className="bg-card rounded-[30px] px-6 py-3">
|
||||
<div className="flex flex-wrap items-end gap-1">
|
||||
<div className="min-w-20 grow basis-full sm:basis-0">
|
||||
<Prompts
|
||||
@@ -920,30 +916,24 @@ export default function NewAgent({ mode }: { mode: 'new' | 'edit' | 'draft' }) {
|
||||
}
|
||||
setPrompts={(newPrompts) => dispatch(setPrompts(newPrompts))}
|
||||
title={t('agents.form.sections.prompt')}
|
||||
titleClassName="text-lg font-semibold dark:text-[#E0E0E0]"
|
||||
titleClassName="text-lg font-semibold"
|
||||
showAddButton={false}
|
||||
dropdownProps={{
|
||||
size: 'w-full',
|
||||
rounded: '3xl',
|
||||
border: 'border',
|
||||
buttonClassName:
|
||||
'bg-white dark:bg-[#222327] border-silver dark:border-[#7E7E7E]',
|
||||
optionsClassName:
|
||||
'bg-white dark:bg-[#383838] border-silver dark:border-[#7E7E7E]',
|
||||
placeholderClassName: 'text-gray-400 dark:text-silver',
|
||||
contentSize: 'text-sm',
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<button
|
||||
className="border-violets-are-blue text-violets-are-blue hover:bg-violets-are-blue min-w-20 shrink-0 basis-full rounded-3xl border-2 border-solid px-5 py-[11px] text-sm whitespace-nowrap transition-colors hover:text-white sm:basis-auto"
|
||||
className="border-primary text-primary hover:bg-primary/90 min-w-20 shrink-0 basis-full rounded-3xl border border-solid px-5 py-3 text-sm whitespace-nowrap transition-colors hover:text-white sm:basis-auto"
|
||||
onClick={() => setAddPromptModal('ACTIVE')}
|
||||
>
|
||||
{t('agents.form.buttons.add')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="dark:bg-raisin-black rounded-[30px] bg-white px-6 py-3 dark:text-[#E0E0E0]">
|
||||
<div className="bg-card rounded-[30px] px-6 py-3">
|
||||
<h2 className="text-lg font-semibold">
|
||||
{t('agents.form.sections.tools')}
|
||||
</h2>
|
||||
@@ -951,10 +941,10 @@ export default function NewAgent({ mode }: { mode: 'new' | 'edit' | 'draft' }) {
|
||||
<button
|
||||
ref={toolAnchorButtonRef}
|
||||
onClick={() => setIsToolsPopupOpen(!isToolsPopupOpen)}
|
||||
className={`border-silver dark:bg-raisin-black w-full truncate rounded-3xl border bg-white px-5 py-3 text-left text-sm dark:border-[#7E7E7E] ${
|
||||
className={`border-border bg-card dark:border-border w-full truncate rounded-3xl border px-5 py-3 text-left text-sm ${
|
||||
selectedTools.length > 0
|
||||
? 'text-jet dark:text-bright-gray'
|
||||
: 'dark:text-silver text-gray-400'
|
||||
? 'text-foreground dark:text-foreground'
|
||||
: 'dark:text-muted-foreground text-gray-400'
|
||||
}`}
|
||||
>
|
||||
{selectedTools.length > 0
|
||||
@@ -992,7 +982,7 @@ export default function NewAgent({ mode }: { mode: 'new' | 'edit' | 'draft' }) {
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="dark:bg-raisin-black rounded-[30px] bg-white px-6 py-3 dark:text-[#E0E0E0]">
|
||||
<div className="bg-card rounded-[30px] px-6 py-3">
|
||||
<h2 className="text-lg font-semibold">
|
||||
{t('agents.form.sections.agentType')}
|
||||
</h2>
|
||||
@@ -1010,16 +1000,12 @@ export default function NewAgent({ mode }: { mode: 'new' | 'edit' | 'draft' }) {
|
||||
}
|
||||
size="w-full"
|
||||
rounded="3xl"
|
||||
border="border"
|
||||
buttonClassName="bg-white dark:bg-[#222327] border-silver dark:border-[#7E7E7E]"
|
||||
optionsClassName="bg-white dark:bg-[#383838] border-silver dark:border-[#7E7E7E]"
|
||||
placeholder={t('agents.form.placeholders.selectType')}
|
||||
placeholderClassName="text-gray-400 dark:text-silver"
|
||||
contentSize="text-sm"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="dark:bg-raisin-black rounded-[30px] bg-white px-6 py-3 dark:text-[#E0E0E0]">
|
||||
<div className="bg-card rounded-[30px] px-6 py-3">
|
||||
<h2 className="text-lg font-semibold">
|
||||
{t('agents.form.sections.models')}
|
||||
</h2>
|
||||
@@ -1027,10 +1013,10 @@ export default function NewAgent({ mode }: { mode: 'new' | 'edit' | 'draft' }) {
|
||||
<button
|
||||
ref={modelAnchorButtonRef}
|
||||
onClick={() => setIsModelsPopupOpen(!isModelsPopupOpen)}
|
||||
className={`border-silver dark:bg-raisin-black w-full truncate rounded-3xl border bg-white px-5 py-3 text-left text-sm dark:border-[#7E7E7E] ${
|
||||
className={`border-border bg-card dark:border-border w-full truncate rounded-3xl border px-5 py-3 text-left text-sm ${
|
||||
selectedModelIds.size > 0
|
||||
? 'text-jet dark:text-bright-gray'
|
||||
: 'dark:text-silver text-gray-400'
|
||||
? 'text-foreground dark:text-foreground'
|
||||
: 'dark:text-muted-foreground text-gray-400'
|
||||
}`}
|
||||
>
|
||||
{selectedModelIds.size > 0
|
||||
@@ -1082,20 +1068,16 @@ export default function NewAgent({ mode }: { mode: 'new' | 'edit' | 'draft' }) {
|
||||
}
|
||||
size="w-full"
|
||||
rounded="3xl"
|
||||
border="border"
|
||||
buttonClassName="bg-white dark:bg-[#222327] border-silver dark:border-[#7E7E7E]"
|
||||
optionsClassName="bg-white dark:bg-[#383838] border-silver dark:border-[#7E7E7E]"
|
||||
placeholder={t(
|
||||
'agents.form.placeholders.selectDefaultModel',
|
||||
)}
|
||||
placeholderClassName="text-gray-400 dark:text-silver"
|
||||
contentSize="text-sm"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="dark:bg-raisin-black rounded-[30px] bg-white px-6 py-3 dark:text-[#E0E0E0]">
|
||||
<div className="bg-card rounded-[30px] px-6 py-3">
|
||||
<button
|
||||
onClick={() =>
|
||||
setIsAdvancedSectionExpanded(!isAdvancedSectionExpanded)
|
||||
@@ -1148,7 +1130,7 @@ export default function NewAgent({ mode }: { mode: 'new' | 'edit' | 'draft' }) {
|
||||
"additionalProperties": false
|
||||
}`}
|
||||
rows={9}
|
||||
className={`border-silver text-jet dark:bg-raisin-black dark:text-bright-gray mt-2 w-full rounded-2xl border bg-white px-4 py-3 font-mono text-sm outline-hidden dark:border-[#7E7E7E]`}
|
||||
className={`border-border text-foreground dark:text-foreground bg-card dark:border-border mt-2 w-full rounded-2xl border px-4 py-3 font-mono text-sm outline-hidden`}
|
||||
/>
|
||||
{jsonSchemaText.trim() !== '' && (
|
||||
<div
|
||||
@@ -1194,7 +1176,7 @@ export default function NewAgent({ mode }: { mode: 'new' | 'edit' | 'draft' }) {
|
||||
}}
|
||||
className={`relative h-6 w-11 rounded-full transition-colors ${
|
||||
agent.limited_token_mode
|
||||
? 'bg-purple-30'
|
||||
? 'bg-primary'
|
||||
: 'bg-gray-300 dark:bg-gray-600'
|
||||
}`}
|
||||
>
|
||||
@@ -1219,7 +1201,7 @@ export default function NewAgent({ mode }: { mode: 'new' | 'edit' | 'draft' }) {
|
||||
}
|
||||
disabled={!agent.limited_token_mode}
|
||||
placeholder={t('agents.form.placeholders.enterTokenLimit')}
|
||||
className={`border-silver text-jet dark:bg-raisin-black dark:text-bright-gray dark:placeholder:text-silver mt-2 w-full rounded-3xl border bg-white px-5 py-3 text-sm outline-hidden placeholder:text-gray-400 dark:border-[#7E7E7E] ${
|
||||
className={`border-border text-foreground dark:text-foreground dark:placeholder:text-silver bg-card dark:border-border mt-2 w-full rounded-3xl border px-5 py-3 text-sm outline-hidden placeholder:text-gray-400 ${
|
||||
!agent.limited_token_mode
|
||||
? 'cursor-not-allowed opacity-50'
|
||||
: ''
|
||||
@@ -1250,7 +1232,7 @@ export default function NewAgent({ mode }: { mode: 'new' | 'edit' | 'draft' }) {
|
||||
}}
|
||||
className={`relative h-6 w-11 rounded-full transition-colors ${
|
||||
agent.limited_request_mode
|
||||
? 'bg-purple-30'
|
||||
? 'bg-primary'
|
||||
: 'bg-gray-300 dark:bg-gray-600'
|
||||
}`}
|
||||
>
|
||||
@@ -1277,7 +1259,7 @@ export default function NewAgent({ mode }: { mode: 'new' | 'edit' | 'draft' }) {
|
||||
placeholder={t(
|
||||
'agents.form.placeholders.enterRequestLimit',
|
||||
)}
|
||||
className={`border-silver text-jet dark:bg-raisin-black dark:text-bright-gray dark:placeholder:text-silver mt-2 w-full rounded-3xl border bg-white px-5 py-3 text-sm outline-hidden placeholder:text-gray-400 dark:border-[#7E7E7E] ${
|
||||
className={`border-border text-foreground dark:text-foreground dark:placeholder:text-silver bg-card dark:border-border mt-2 w-full rounded-3xl border px-5 py-3 text-sm outline-hidden placeholder:text-gray-400 ${
|
||||
!agent.limited_request_mode
|
||||
? 'cursor-not-allowed opacity-50'
|
||||
: ''
|
||||
@@ -1288,7 +1270,7 @@ export default function NewAgent({ mode }: { mode: 'new' | 'edit' | 'draft' }) {
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-span-3 flex flex-col gap-2 max-[1179px]:h-auto max-[1179px]:px-0 max-[1179px]:py-0 min-[1180px]:h-full min-[1180px]:py-2 dark:text-[#E0E0E0]">
|
||||
<div className="col-span-3 flex flex-col gap-2 max-[1179px]:h-auto max-[1179px]:px-0 max-[1179px]:py-0 min-[1180px]:h-full min-[1180px]:py-2">
|
||||
<h2 className="text-lg font-semibold">
|
||||
{t('agents.form.sections.preview')}
|
||||
</h2>
|
||||
@@ -1331,7 +1313,7 @@ function AgentPreviewArea() {
|
||||
const { t } = useTranslation();
|
||||
const selectedAgent = useSelector(selectSelectedAgent);
|
||||
return (
|
||||
<div className="dark:bg-raisin-black w-full rounded-[30px] border border-[#F6F6F6] bg-white max-[1179px]:h-[600px] min-[1180px]:h-full dark:border-[#7E7E7E]">
|
||||
<div className="bg-card dark:border-border w-full rounded-[30px] border border-[#F6F6F6] max-[1179px]:h-[600px] min-[1180px]:h-full">
|
||||
{selectedAgent?.status === 'published' ? (
|
||||
<div className="flex h-full w-full flex-col overflow-hidden rounded-[30px]">
|
||||
<AgentPreview />
|
||||
@@ -1339,7 +1321,7 @@ function AgentPreviewArea() {
|
||||
) : (
|
||||
<div className="flex h-full w-full flex-col items-center justify-center gap-2">
|
||||
<span className="block h-12 w-12 bg-[url('/src/assets/science-spark.svg')] bg-contain bg-center bg-no-repeat transition-all dark:bg-[url('/src/assets/science-spark-dark.svg')]" />{' '}
|
||||
<p className="dark:text-gray-4000 text-xs text-[#18181B]">
|
||||
<p className="text-muted-foreground text-xs">
|
||||
{t('agents.form.preview.publishedPreview')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -143,7 +143,7 @@ export default function SharedAgent() {
|
||||
alt="No agent found"
|
||||
className="mx-auto mb-6 h-32 w-32"
|
||||
/>
|
||||
<p className="dark:text-gray-4000 text-center text-lg text-[#71717A]">
|
||||
<p className="text-muted-foreground text-center text-lg">
|
||||
{t('agents.shared.notFound')}
|
||||
</p>
|
||||
</div>
|
||||
@@ -157,7 +157,7 @@ export default function SharedAgent() {
|
||||
alt="agent-logo"
|
||||
className="h-6 w-6 rounded-full object-contain"
|
||||
/>
|
||||
<h2 className="text-eerie-black text-lg font-semibold dark:text-[#E0E0E0]">
|
||||
<h2 className="text-foreground text-lg font-semibold">
|
||||
{sharedAgent.name}
|
||||
</h2>
|
||||
</div>
|
||||
@@ -186,7 +186,7 @@ export default function SharedAgent() {
|
||||
autoFocus={false}
|
||||
/>
|
||||
</div>
|
||||
<p className="text-gray-4000 dark:text-sonic-silver hidden w-screen self-center bg-transparent py-2 text-center text-xs md:inline md:w-full">
|
||||
<p className="text-muted-foreground hidden w-screen self-center bg-transparent py-2 text-center text-xs md:inline md:w-full">
|
||||
{t('tagline')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -10,7 +10,7 @@ export default function SharedAgentCard({ agent }: { agent: Agent }) {
|
||||
agent.shared_metadata !== null &&
|
||||
Object.keys(agent.shared_metadata).length > 0;
|
||||
return (
|
||||
<div className="border-dark-gray dark:border-grey flex w-full max-w-[720px] flex-col rounded-3xl border p-6 shadow-xs sm:w-fit sm:min-w-[480px]">
|
||||
<div className="border-border dark:border-border flex w-full max-w-[720px] flex-col rounded-3xl border p-6 shadow-xs sm:w-fit sm:min-w-[480px]">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="flex h-12 w-12 items-center justify-center overflow-hidden rounded-full p-1">
|
||||
<AgentImage
|
||||
@@ -19,10 +19,10 @@ export default function SharedAgentCard({ agent }: { agent: Agent }) {
|
||||
/>
|
||||
</div>
|
||||
<div className="flex max-h-[92px] w-[80%] flex-col gap-px">
|
||||
<h2 className="text-eerie-black text-base font-semibold sm:text-lg dark:text-[#E0E0E0]">
|
||||
<h2 className="text-foreground text-base font-semibold sm:text-lg">
|
||||
{agent.name}
|
||||
</h2>
|
||||
<p className="dark:text-gray-4000 overflow-y-auto text-xs text-wrap break-all text-[#71717A] sm:text-sm">
|
||||
<p className="text-muted-foreground overflow-y-auto text-xs text-wrap break-all sm:text-sm">
|
||||
{agent.description}
|
||||
</p>
|
||||
</div>
|
||||
@@ -30,12 +30,12 @@ export default function SharedAgentCard({ agent }: { agent: Agent }) {
|
||||
{hasSharedMetadata && (
|
||||
<div className="mt-4 flex items-center gap-8">
|
||||
{agent.shared_metadata?.shared_by && (
|
||||
<p className="text-eerie-black text-xs font-light sm:text-sm dark:text-[#E0E0E0]">
|
||||
<p className="text-foreground text-xs font-light sm:text-sm">
|
||||
by {agent.shared_metadata.shared_by}
|
||||
</p>
|
||||
)}
|
||||
{agent.shared_metadata?.shared_at && (
|
||||
<p className="dark:text-gray-4000 text-xs font-light text-[#71717A] sm:text-sm">
|
||||
<p className="text-muted-foreground text-xs font-light sm:text-sm">
|
||||
Shared on{' '}
|
||||
{new Date(agent.shared_metadata.shared_at).toLocaleString(
|
||||
'en-US',
|
||||
@@ -54,14 +54,14 @@ export default function SharedAgentCard({ agent }: { agent: Agent }) {
|
||||
)}
|
||||
{agent.tool_details && agent.tool_details.length > 0 && (
|
||||
<div className="mt-8">
|
||||
<p className="text-eerie-black text-sm font-semibold sm:text-base dark:text-[#E0E0E0]">
|
||||
<p className="text-foreground text-sm font-semibold sm:text-base">
|
||||
Connected Tools
|
||||
</p>
|
||||
<div className="mt-2 flex flex-wrap gap-2">
|
||||
{agent.tool_details.map((tool, index) => (
|
||||
<span
|
||||
key={index}
|
||||
className="bg-bright-gray text-eerie-black dark:bg-dark-charcoal flex items-center gap-1 rounded-full px-3 py-1 text-xs font-light dark:text-[#E0E0E0]"
|
||||
className="bg-bright-gray text-foreground dark:bg-card flex items-center gap-1 rounded-full px-3 py-1 text-xs font-light"
|
||||
>
|
||||
<img
|
||||
src={`/toolIcons/tool_${tool.name}.svg`}
|
||||
|
||||
@@ -579,12 +579,12 @@ function WorkflowBuilderInner() {
|
||||
]);
|
||||
|
||||
return (
|
||||
<div className="bg-lotion dark:bg-outer-space flex h-screen w-full flex-col">
|
||||
<div className="border-light-silver dark:bg-raisin-black flex items-center justify-between border-b bg-white px-6 py-4 dark:border-[#3A3A3A]">
|
||||
<div className="bg-background flex h-screen w-full flex-col">
|
||||
<div className="border-border bg-card flex items-center justify-between border-b px-6 py-4">
|
||||
<div className="flex items-center gap-4">
|
||||
<button
|
||||
onClick={() => navigate('/agents')}
|
||||
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]"
|
||||
className="border-border text-muted-foreground hover:bg-accent rounded-full border p-3 text-sm"
|
||||
>
|
||||
<img src={ArrowLeft} alt="left-arrow" className="h-3 w-3" />
|
||||
</button>
|
||||
@@ -613,7 +613,7 @@ function WorkflowBuilderInner() {
|
||||
{showWorkflowSettings && (
|
||||
<div
|
||||
ref={workflowSettingsRef}
|
||||
className="dark:bg-raisin-black absolute top-full left-0 z-50 mt-2 w-80 rounded-xl border border-[#E5E5E5] bg-white p-4 shadow-lg dark:border-[#3A3A3A]"
|
||||
className="border-border bg-card absolute top-full left-0 z-50 mt-2 w-80 rounded-xl border p-4 shadow-lg"
|
||||
>
|
||||
<div className="mb-3">
|
||||
<label className="mb-1 block text-sm font-medium text-gray-700 dark:text-gray-300">
|
||||
@@ -623,7 +623,7 @@ function WorkflowBuilderInner() {
|
||||
type="text"
|
||||
value={workflowName}
|
||||
onChange={(e) => setWorkflowName(e.target.value)}
|
||||
className="focus:ring-purple-30 w-full rounded-lg border border-[#E5E5E5] bg-white px-3 py-2 text-sm outline-none focus:ring-2 dark:border-[#3A3A3A] dark:bg-[#2C2C2C] dark:text-white"
|
||||
className="border-border bg-card text-foreground focus:ring-ring w-full rounded-lg border px-3 py-2 text-sm outline-none focus:ring-2"
|
||||
placeholder="Enter workflow name"
|
||||
/>
|
||||
</div>
|
||||
@@ -634,14 +634,14 @@ function WorkflowBuilderInner() {
|
||||
<textarea
|
||||
value={workflowDescription}
|
||||
onChange={(e) => setWorkflowDescription(e.target.value)}
|
||||
className="focus:ring-purple-30 w-full rounded-lg border border-[#E5E5E5] bg-white px-3 py-2 text-sm outline-none focus:ring-2 dark:border-[#3A3A3A] dark:bg-[#2C2C2C] dark:text-white"
|
||||
className="border-border bg-card text-foreground focus:ring-ring w-full rounded-lg border px-3 py-2 text-sm outline-none focus:ring-2"
|
||||
rows={3}
|
||||
placeholder="Describe what this workflow does"
|
||||
/>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => setShowWorkflowSettings(false)}
|
||||
className="bg-violets-are-blue hover:bg-purple-30 w-full rounded-lg px-3 py-2 text-sm font-medium text-white"
|
||||
className="bg-primary text-primary-foreground hover:bg-primary/90 w-full rounded-lg px-3 py-2 text-sm font-medium"
|
||||
>
|
||||
Done
|
||||
</button>
|
||||
@@ -660,7 +660,7 @@ function WorkflowBuilderInner() {
|
||||
}
|
||||
setShowPreview(true);
|
||||
}}
|
||||
className="flex items-center gap-2 rounded-full border border-gray-200 bg-white px-4 py-2 text-sm font-medium text-gray-700 transition-colors hover:bg-gray-50 dark:border-[#3A3A3A] dark:bg-[#2C2C2C] dark:text-gray-200 dark:hover:bg-[#383838]"
|
||||
className="border-border bg-card text-foreground hover:bg-accent flex items-center gap-2 rounded-full border px-4 py-2 text-sm font-medium transition-colors"
|
||||
>
|
||||
<Play size={16} />
|
||||
Preview
|
||||
@@ -668,7 +668,7 @@ function WorkflowBuilderInner() {
|
||||
<button
|
||||
onClick={handlePublish}
|
||||
disabled={isPublishing}
|
||||
className="bg-violets-are-blue hover:bg-purple-30 rounded-full px-6 py-2 text-sm font-medium text-white shadow-sm transition-colors disabled:opacity-50"
|
||||
className="bg-primary text-primary-foreground hover:bg-primary/90 rounded-full px-6 py-2 text-sm font-medium shadow-sm transition-colors disabled:opacity-50"
|
||||
>
|
||||
{isPublishing ? 'Publishing...' : 'Publish'}
|
||||
</button>
|
||||
@@ -705,20 +705,20 @@ function WorkflowBuilderInner() {
|
||||
)}
|
||||
|
||||
<div className="flex flex-1 overflow-hidden">
|
||||
<div className="border-light-silver dark:bg-raisin-black flex w-64 flex-col gap-6 border-r bg-gray-50 p-4 dark:border-[#3A3A3A]">
|
||||
<div className="border-border bg-muted flex w-64 flex-col gap-6 border-r p-4">
|
||||
<div>
|
||||
<h3 className="mb-3 text-xs font-semibold tracking-wider text-gray-500 uppercase dark:text-gray-400">
|
||||
Core Nodes
|
||||
</h3>
|
||||
<div className="flex flex-col gap-2">
|
||||
<div
|
||||
className="group flex cursor-move items-center gap-3 rounded-full border bg-white px-4 py-3 shadow-sm transition-all hover:shadow-md dark:border-[#3A3A3A] dark:bg-[#2C2C2C] dark:hover:bg-[#383838]"
|
||||
className="group border-border bg-card hover:bg-accent flex cursor-move items-center gap-3 rounded-full border px-4 py-3 shadow-sm transition-all hover:shadow-md"
|
||||
draggable
|
||||
onDragStart={(e) =>
|
||||
e.dataTransfer.setData('application/reactflow', 'agent')
|
||||
}
|
||||
>
|
||||
<div className="text-violets-are-blue group-hover:bg-violets-are-blue flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-purple-100 transition-colors group-hover:text-white">
|
||||
<div className="bg-primary/10 text-primary group-hover:bg-primary group-hover:text-primary-foreground flex h-8 w-8 shrink-0 items-center justify-center rounded-full transition-colors">
|
||||
<Bot size={18} />
|
||||
</div>
|
||||
<span className="text-sm font-medium text-gray-700 dark:text-gray-200">
|
||||
@@ -726,7 +726,7 @@ function WorkflowBuilderInner() {
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
className="group flex cursor-move items-center gap-3 rounded-full border bg-white px-4 py-3 shadow-sm transition-all hover:shadow-md dark:border-[#3A3A3A] dark:bg-[#2C2C2C] dark:hover:bg-[#383838]"
|
||||
className="group border-border bg-card hover:bg-accent flex cursor-move items-center gap-3 rounded-full border px-4 py-3 shadow-sm transition-all hover:shadow-md"
|
||||
draggable
|
||||
onDragStart={(e) =>
|
||||
e.dataTransfer.setData('application/reactflow', 'end')
|
||||
@@ -740,7 +740,7 @@ function WorkflowBuilderInner() {
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
className="group flex cursor-move items-center gap-3 rounded-full border bg-white px-4 py-3 shadow-sm transition-all hover:shadow-md dark:border-[#3A3A3A] dark:bg-[#2C2C2C] dark:hover:bg-[#383838]"
|
||||
className="group border-border bg-card hover:bg-accent flex cursor-move items-center gap-3 rounded-full border px-4 py-3 shadow-sm transition-all hover:shadow-md"
|
||||
draggable
|
||||
onDragStart={(e) =>
|
||||
e.dataTransfer.setData('application/reactflow', 'note')
|
||||
@@ -762,7 +762,7 @@ function WorkflowBuilderInner() {
|
||||
</h3>
|
||||
<div className="flex flex-col gap-2">
|
||||
<div
|
||||
className="group flex cursor-move items-center gap-3 rounded-full border bg-white px-4 py-3 shadow-sm transition-all hover:shadow-md dark:border-[#3A3A3A] dark:bg-[#2C2C2C] dark:hover:bg-[#383838]"
|
||||
className="group border-border bg-card hover:bg-accent flex cursor-move items-center gap-3 rounded-full border px-4 py-3 shadow-sm transition-all hover:shadow-md"
|
||||
draggable
|
||||
onDragStart={(e) =>
|
||||
e.dataTransfer.setData('application/reactflow', 'state')
|
||||
@@ -784,10 +784,7 @@ function WorkflowBuilderInner() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
ref={reactFlowWrapper}
|
||||
className="dark:bg-raisin-black/10 relative flex-1 bg-gray-50"
|
||||
>
|
||||
<div ref={reactFlowWrapper} className="bg-muted relative flex-1">
|
||||
<ReactFlow
|
||||
nodes={nodes}
|
||||
edges={edges}
|
||||
@@ -807,9 +804,9 @@ function WorkflowBuilderInner() {
|
||||
{showNodeConfig && selectedNode && (
|
||||
<div
|
||||
ref={configPanelRef}
|
||||
className="border-light-silver dark:bg-raisin-black absolute top-4 right-4 w-96 rounded-2xl border bg-white shadow-[0px_4px_40px_-3px_#0000001A] dark:border-[#3A3A3A]"
|
||||
className="border-border bg-card absolute top-4 right-4 w-96 rounded-2xl border shadow-[0px_4px_40px_-3px_#0000001A]"
|
||||
>
|
||||
<div className="border-light-silver flex items-center justify-between border-b p-4 dark:border-[#3A3A3A]">
|
||||
<div className="border-border flex items-center justify-between border-b p-4">
|
||||
<h3 className="font-semibold text-gray-900 dark:text-white">
|
||||
{selectedNode.type === 'start' && 'Start Node'}
|
||||
{selectedNode.type === 'end' && 'End Node'}
|
||||
@@ -827,7 +824,7 @@ function WorkflowBuilderInner() {
|
||||
|
||||
<div className="max-h-[calc(100vh-200px)] overflow-y-auto p-4">
|
||||
<div className="mb-4 flex flex-col gap-2">
|
||||
<div className="rounded-lg bg-gray-50 p-3 dark:bg-[#2C2C2C]">
|
||||
<div className="bg-muted rounded-lg p-3">
|
||||
<div className="mb-1 text-xs text-gray-500 dark:text-gray-400">
|
||||
Node ID
|
||||
</div>
|
||||
@@ -856,7 +853,7 @@ function WorkflowBuilderInner() {
|
||||
label: e.target.value,
|
||||
})
|
||||
}
|
||||
className="border-light-silver focus:ring-purple-30 w-full rounded-xl border bg-white px-3 py-2 text-sm transition-all outline-none focus:ring-2 dark:border-[#3A3A3A] dark:bg-[#2C2C2C] dark:text-white"
|
||||
className="border-border bg-card text-foreground focus:ring-ring w-full rounded-xl border px-3 py-2 text-sm transition-all outline-none focus:ring-2"
|
||||
placeholder="Enter node title"
|
||||
/>
|
||||
</div>
|
||||
@@ -945,7 +942,7 @@ function WorkflowBuilderInner() {
|
||||
},
|
||||
})
|
||||
}
|
||||
className="border-light-silver focus:ring-purple-30 w-full rounded-xl border bg-white px-3 py-2 text-sm transition-all outline-none focus:ring-2 dark:border-[#3A3A3A] dark:bg-[#2C2C2C] dark:text-white"
|
||||
className="border-border bg-card text-foreground focus:ring-ring w-full rounded-xl border px-3 py-2 text-sm transition-all outline-none focus:ring-2"
|
||||
rows={3}
|
||||
placeholder="System prompt for the agent"
|
||||
/>
|
||||
@@ -967,7 +964,7 @@ function WorkflowBuilderInner() {
|
||||
},
|
||||
})
|
||||
}
|
||||
className="border-light-silver focus:ring-purple-30 w-full rounded-xl border bg-white px-3 py-2 text-sm transition-all outline-none focus:ring-2 dark:border-[#3A3A3A] dark:bg-[#2C2C2C] dark:text-white"
|
||||
className="border-border bg-card text-foreground focus:ring-ring w-full rounded-xl border px-3 py-2 text-sm transition-all outline-none focus:ring-2"
|
||||
rows={4}
|
||||
placeholder="Use {{variable}} for dynamic content"
|
||||
/>
|
||||
@@ -990,7 +987,7 @@ function WorkflowBuilderInner() {
|
||||
},
|
||||
})
|
||||
}
|
||||
className="border-light-silver focus:ring-purple-30 w-full rounded-xl border bg-white px-3 py-2 text-sm transition-all outline-none focus:ring-2 dark:border-[#3A3A3A] dark:bg-[#2C2C2C] dark:text-white"
|
||||
className="border-border bg-card text-foreground focus:ring-ring w-full rounded-xl border px-3 py-2 text-sm transition-all outline-none focus:ring-2"
|
||||
placeholder="Variable name for output"
|
||||
/>
|
||||
</div>
|
||||
@@ -1057,7 +1054,7 @@ function WorkflowBuilderInner() {
|
||||
content: e.target.value,
|
||||
})
|
||||
}
|
||||
className="border-light-silver focus:ring-purple-30 w-full rounded-xl border bg-white px-3 py-2 text-sm transition-all outline-none focus:ring-2 dark:border-[#3A3A3A] dark:bg-[#2C2C2C] dark:text-white"
|
||||
className="border-border bg-card text-foreground focus:ring-ring w-full rounded-xl border px-3 py-2 text-sm transition-all outline-none focus:ring-2"
|
||||
rows={4}
|
||||
placeholder="Enter note content"
|
||||
/>
|
||||
@@ -1078,7 +1075,7 @@ function WorkflowBuilderInner() {
|
||||
variable: e.target.value,
|
||||
})
|
||||
}
|
||||
className="border-light-silver focus:ring-purple-30 w-full rounded-xl border bg-white px-3 py-2 text-sm transition-all outline-none focus:ring-2 dark:border-[#3A3A3A] dark:bg-[#2C2C2C] dark:text-white"
|
||||
className="border-border bg-card text-foreground focus:ring-ring w-full rounded-xl border px-3 py-2 text-sm transition-all outline-none focus:ring-2"
|
||||
placeholder="e.g. analysis_type"
|
||||
/>
|
||||
</div>
|
||||
@@ -1094,7 +1091,7 @@ function WorkflowBuilderInner() {
|
||||
value: e.target.value,
|
||||
})
|
||||
}
|
||||
className="border-light-silver focus:ring-purple-30 w-full rounded-xl border bg-white px-3 py-2 text-sm transition-all outline-none focus:ring-2 dark:border-[#3A3A3A] dark:bg-[#2C2C2C] dark:text-white"
|
||||
className="border-border bg-card text-foreground focus:ring-ring w-full rounded-xl border px-3 py-2 text-sm transition-all outline-none focus:ring-2"
|
||||
placeholder="e.g. price_check"
|
||||
/>
|
||||
</div>
|
||||
@@ -1125,7 +1122,7 @@ function WorkflowBuilderInner() {
|
||||
<SheetContent
|
||||
side="right"
|
||||
showCloseButton={false}
|
||||
className="dark:bg-raisin-black w-full max-w-none p-0 sm:max-w-[600px] md:max-w-[700px] lg:max-w-[800px] dark:border-[#3A3A3A]"
|
||||
className="border-border bg-card w-full max-w-none p-0 sm:max-w-[600px] md:max-w-[700px] lg:max-w-[800px]"
|
||||
>
|
||||
<WorkflowPreview
|
||||
workflowData={{
|
||||
|
||||
@@ -41,4 +41,4 @@ export const agentSectionsConfig = [
|
||||
selectData: selectSharedAgents,
|
||||
updateAction: setSharedAgents,
|
||||
},
|
||||
];
|
||||
];
|
||||
|
||||
@@ -33,7 +33,7 @@ export default function AgentTypeModal({
|
||||
onClick={onClose}
|
||||
>
|
||||
<div
|
||||
className="relative w-full max-w-lg rounded-xl bg-white p-8 shadow-2xl dark:bg-[#1e1e1e]"
|
||||
className="bg-card relative w-full max-w-lg rounded-xl p-8 shadow-2xl"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<button
|
||||
@@ -43,7 +43,7 @@ export default function AgentTypeModal({
|
||||
<X size={20} />
|
||||
</button>
|
||||
|
||||
<h2 className="text-jet dark:text-bright-gray mb-3 text-2xl font-bold">
|
||||
<h2 className="text-foreground dark:text-foreground mb-3 text-2xl font-bold">
|
||||
Create New Agent
|
||||
</h2>
|
||||
<p className="mb-8 text-sm text-gray-500 dark:text-gray-400">
|
||||
@@ -53,13 +53,13 @@ export default function AgentTypeModal({
|
||||
<div className="flex flex-col gap-4">
|
||||
<button
|
||||
onClick={() => handleSelect('normal')}
|
||||
className="hover:border-purple-30 hover:bg-purple-30/5 dark:hover:border-purple-30 dark:hover:bg-purple-30/10 group flex items-start gap-5 rounded-xl border-2 border-gray-200 p-5 text-left transition-all dark:border-[#2E2F34]"
|
||||
className="hover:border-primary hover:bg-primary/5 dark:hover:border-primary dark:hover:bg-primary/10 group dark:border-border flex items-start gap-5 rounded-xl border-2 border-gray-200 p-5 text-left transition-all"
|
||||
>
|
||||
<div className="dark:bg-purple-30/20 bg-purple-30/10 text-purple-30 group-hover:bg-purple-30 flex h-14 w-14 shrink-0 items-center justify-center rounded-xl transition-colors group-hover:text-white dark:text-purple-300">
|
||||
<div className="dark:bg-primary/20 bg-primary/10 text-primary group-hover:bg-primary/90 flex h-14 w-14 shrink-0 items-center justify-center rounded-xl transition-colors group-hover:text-white dark:text-purple-300">
|
||||
<Bot size={28} />
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<h3 className="text-jet dark:text-bright-gray mb-2 text-lg font-semibold">
|
||||
<h3 className="text-foreground dark:text-foreground mb-2 text-lg font-semibold">
|
||||
Classic Agent
|
||||
</h3>
|
||||
<p className="text-sm leading-relaxed text-gray-600 dark:text-gray-400">
|
||||
@@ -71,13 +71,13 @@ export default function AgentTypeModal({
|
||||
|
||||
<button
|
||||
onClick={() => handleSelect('workflow')}
|
||||
className="hover:border-violets-are-blue hover:bg-violets-are-blue/5 dark:hover:border-violets-are-blue dark:hover:bg-violets-are-blue/10 group flex items-start gap-5 rounded-xl border-2 border-gray-200 p-5 text-left transition-all dark:border-[#2E2F34]"
|
||||
className="hover:border-primary hover:bg-primary/5 dark:hover:border-primary dark:hover:bg-primary/10 group dark:border-border flex items-start gap-5 rounded-xl border-2 border-gray-200 p-5 text-left transition-all"
|
||||
>
|
||||
<div className="dark:bg-violets-are-blue/20 bg-violets-are-blue/10 text-violets-are-blue group-hover:bg-violets-are-blue flex h-14 w-14 shrink-0 items-center justify-center rounded-xl transition-colors group-hover:text-white dark:text-purple-300">
|
||||
<div className="dark:bg-primary/20 bg-primary/10 text-primary group-hover:bg-primary flex h-14 w-14 shrink-0 items-center justify-center rounded-xl transition-colors group-hover:text-white dark:text-purple-300">
|
||||
<Workflow size={28} />
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<h3 className="text-jet dark:text-bright-gray mb-2 text-lg font-semibold">
|
||||
<h3 className="text-foreground dark:text-foreground mb-2 text-lg font-semibold">
|
||||
Workflow Agent
|
||||
</h3>
|
||||
<p className="text-sm leading-relaxed text-gray-600 dark:text-gray-400">
|
||||
|
||||
@@ -18,4 +18,4 @@ export default function Agents() {
|
||||
<Route path="/workflow/edit/:agentId" element={<WorkflowBuilder />} />
|
||||
</Routes>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
export type NodeType = 'start' | 'end' | 'agent' | 'note' | 'state' | 'condition';
|
||||
export type NodeType =
|
||||
| 'start'
|
||||
| 'end'
|
||||
| 'agent'
|
||||
| 'note'
|
||||
| 'state'
|
||||
| 'condition';
|
||||
|
||||
export interface ConditionCase {
|
||||
name?: string;
|
||||
|
||||
@@ -2,13 +2,13 @@ import 'reactflow/dist/style.css';
|
||||
|
||||
import {
|
||||
AlertCircle,
|
||||
ChartColumn,
|
||||
Bot,
|
||||
ChartColumn,
|
||||
Database,
|
||||
Flag,
|
||||
GitBranch,
|
||||
Loader2,
|
||||
Link,
|
||||
Loader2,
|
||||
Pencil,
|
||||
Play,
|
||||
Plus,
|
||||
@@ -1355,12 +1355,12 @@ function WorkflowBuilderInner() {
|
||||
return (
|
||||
<>
|
||||
<MobileBlocker />
|
||||
<div className="bg-lotion dark:bg-outer-space fixed inset-0 z-50 hidden h-screen w-full flex-col md:flex">
|
||||
<div className="border-light-silver dark:bg-raisin-black flex items-center justify-between border-b bg-white px-6 py-4 dark:border-[#3A3A3A]">
|
||||
<div className="bg-background dark:bg-outer-space fixed inset-0 z-50 hidden h-screen w-full flex-col md:flex">
|
||||
<div className="border-border bg-card dark:bg-background flex items-center justify-between border-b px-6 py-4">
|
||||
<div className="flex items-center gap-4">
|
||||
<button
|
||||
onClick={navigateBackToAgents}
|
||||
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]"
|
||||
className="border-border text-muted-foreground hover:bg-accent rounded-full border p-3 text-sm"
|
||||
>
|
||||
<img src={ArrowLeft} alt="left-arrow" className="h-3 w-3" />
|
||||
</button>
|
||||
@@ -1390,7 +1390,7 @@ function WorkflowBuilderInner() {
|
||||
{showWorkflowSettings && (
|
||||
<div
|
||||
ref={workflowSettingsRef}
|
||||
className="dark:bg-raisin-black absolute top-full left-0 z-50 mt-2 w-80 rounded-xl border border-[#E5E5E5] bg-white p-4 shadow-lg dark:border-[#3A3A3A]"
|
||||
className="border-border bg-card absolute top-full left-0 z-50 mt-2 w-80 rounded-xl border p-4 shadow-lg"
|
||||
>
|
||||
<div className="mb-3">
|
||||
<label className="mb-1 block text-sm font-medium text-gray-700 dark:text-gray-300">
|
||||
@@ -1400,7 +1400,7 @@ function WorkflowBuilderInner() {
|
||||
type="text"
|
||||
value={workflowName}
|
||||
onChange={(e) => setWorkflowName(e.target.value)}
|
||||
className="focus:ring-purple-30 w-full rounded-lg border border-[#E5E5E5] bg-white px-3 py-2 text-sm outline-none focus:ring-2 dark:border-[#3A3A3A] dark:bg-[#2C2C2C] dark:text-white"
|
||||
className="focus:ring-ring border-border bg-card w-full rounded-lg border px-3 py-2 text-sm outline-none focus:ring-2 dark:text-white"
|
||||
placeholder="Enter workflow name"
|
||||
/>
|
||||
</div>
|
||||
@@ -1411,7 +1411,7 @@ function WorkflowBuilderInner() {
|
||||
<textarea
|
||||
value={workflowDescription}
|
||||
onChange={(e) => setWorkflowDescription(e.target.value)}
|
||||
className="focus:ring-purple-30 w-full rounded-lg border border-[#E5E5E5] bg-white px-3 py-2 text-sm outline-none focus:ring-2 dark:border-[#3A3A3A] dark:bg-[#2C2C2C] dark:text-white"
|
||||
className="focus:ring-ring border-border bg-card w-full rounded-lg border px-3 py-2 text-sm outline-none focus:ring-2 dark:text-white"
|
||||
rows={3}
|
||||
placeholder="Describe what this workflow does"
|
||||
/>
|
||||
@@ -1441,23 +1441,23 @@ function WorkflowBuilderInner() {
|
||||
uploadText={[
|
||||
{
|
||||
text: 'Click to upload',
|
||||
colorClass: 'text-violets-are-blue',
|
||||
colorClass: 'text-primary',
|
||||
},
|
||||
{
|
||||
text: ' or drag and drop',
|
||||
colorClass: 'text-gray-500',
|
||||
colorClass: 'text-muted-foreground',
|
||||
},
|
||||
]}
|
||||
className="rounded-lg border-2 border-dashed border-[#E5E5E5] p-3 text-center transition-colors dark:border-[#3A3A3A] dark:bg-[#2C2C2C]"
|
||||
className="border-border rounded-lg border-2 border-dashed p-3 text-center transition-colors"
|
||||
/>
|
||||
<p className="mt-1 text-[11px] text-gray-500 dark:text-gray-400">
|
||||
<p className="text-muted-foreground mt-1 text-[11px]">
|
||||
Image updates are included the next time you save.
|
||||
</p>
|
||||
</div>
|
||||
<button
|
||||
onClick={handleWorkflowSettingsDone}
|
||||
disabled={isPublishing}
|
||||
className="bg-violets-are-blue hover:bg-purple-30 w-full rounded-lg px-3 py-2 text-sm font-medium text-white disabled:cursor-not-allowed disabled:opacity-50"
|
||||
className="bg-primary hover:bg-primary/90 w-full rounded-lg px-3 py-2 text-sm font-medium text-white disabled:cursor-not-allowed disabled:opacity-50"
|
||||
>
|
||||
Done
|
||||
</button>
|
||||
@@ -1468,7 +1468,7 @@ function WorkflowBuilderInner() {
|
||||
<div className="flex items-center gap-2">
|
||||
<button
|
||||
onClick={() => setShowWorkflowSettings((prev) => !prev)}
|
||||
className="flex items-center gap-2 rounded-full border border-gray-200 bg-white px-4 py-2 text-sm font-medium text-gray-700 transition-colors hover:bg-gray-50 dark:border-[#3A3A3A] dark:bg-[#2C2C2C] dark:text-gray-200 dark:hover:bg-[#383838]"
|
||||
className="border-border bg-card hover:bg-accent flex items-center gap-2 rounded-full border px-4 py-2 text-sm font-medium text-gray-700 transition-colors dark:text-gray-200"
|
||||
>
|
||||
<Settings2 size={16} />
|
||||
Details
|
||||
@@ -1476,7 +1476,7 @@ function WorkflowBuilderInner() {
|
||||
{canManageAgent && (
|
||||
<button
|
||||
onClick={() => navigate(`/agents/logs/${effectiveAgentId}`)}
|
||||
className="flex items-center gap-2 rounded-full border border-gray-200 bg-white px-4 py-2 text-sm font-medium text-gray-700 transition-colors hover:bg-gray-50 dark:border-[#3A3A3A] dark:bg-[#2C2C2C] dark:text-gray-200 dark:hover:bg-[#383838]"
|
||||
className="border-border bg-card hover:bg-accent flex items-center gap-2 rounded-full border px-4 py-2 text-sm font-medium text-gray-700 transition-colors dark:text-gray-200"
|
||||
>
|
||||
<ChartColumn size={16} />
|
||||
Logs
|
||||
@@ -1485,7 +1485,7 @@ function WorkflowBuilderInner() {
|
||||
{canManageAgent && (
|
||||
<button
|
||||
onClick={() => setAgentDetails('ACTIVE')}
|
||||
className="flex items-center gap-2 rounded-full border border-gray-200 bg-white px-4 py-2 text-sm font-medium text-gray-700 transition-colors hover:bg-gray-50 dark:border-[#3A3A3A] dark:bg-[#2C2C2C] dark:text-gray-200 dark:hover:bg-[#383838]"
|
||||
className="border-border bg-card hover:bg-accent flex items-center gap-2 rounded-full border px-4 py-2 text-sm font-medium text-gray-700 transition-colors dark:text-gray-200"
|
||||
>
|
||||
<Link size={16} />
|
||||
Access Details
|
||||
@@ -1495,7 +1495,7 @@ function WorkflowBuilderInner() {
|
||||
<button
|
||||
onClick={() => setDeleteConfirmation('ACTIVE')}
|
||||
disabled={isDeletingAgent}
|
||||
className="flex items-center gap-2 rounded-full border border-red-200 bg-white px-4 py-2 text-sm font-medium text-red-600 transition-colors hover:bg-red-50 disabled:cursor-not-allowed disabled:opacity-50 dark:border-red-900/30 dark:bg-[#2C2C2C] dark:text-red-400 dark:hover:bg-red-900/10"
|
||||
className="bg-card flex items-center gap-2 rounded-full border border-red-200 px-4 py-2 text-sm font-medium text-red-600 transition-colors hover:bg-red-50 disabled:cursor-not-allowed disabled:opacity-50 dark:border-red-900/30 dark:text-red-400 dark:hover:bg-red-900/10"
|
||||
>
|
||||
<Trash2 size={16} />
|
||||
{isDeletingAgent ? 'Deleting...' : 'Delete'}
|
||||
@@ -1511,7 +1511,7 @@ function WorkflowBuilderInner() {
|
||||
}
|
||||
setShowPreview(true);
|
||||
}}
|
||||
className="flex items-center gap-2 rounded-full border border-gray-200 bg-white px-4 py-2 text-sm font-medium text-gray-700 transition-colors hover:bg-gray-50 dark:border-[#3A3A3A] dark:bg-[#2C2C2C] dark:text-gray-200 dark:hover:bg-[#383838]"
|
||||
className="border-border bg-card hover:bg-accent flex items-center gap-2 rounded-full border px-4 py-2 text-sm font-medium text-gray-700 transition-colors dark:text-gray-200"
|
||||
>
|
||||
<Play size={16} />
|
||||
Preview
|
||||
@@ -1521,8 +1521,8 @@ function WorkflowBuilderInner() {
|
||||
disabled={isPrimaryActionDisabled}
|
||||
className={`relative inline-flex items-center justify-center rounded-full px-6 py-2 text-sm font-medium shadow-sm transition-colors disabled:cursor-not-allowed ${
|
||||
canManageAgent && !hasSavableChanges
|
||||
? 'bg-gray-200 text-gray-500 dark:bg-[#3A3A3A] dark:text-gray-400'
|
||||
: 'bg-violets-are-blue hover:bg-purple-30 text-white disabled:opacity-50'
|
||||
? 'dark:bg-accent bg-gray-200 text-gray-500 dark:text-gray-400'
|
||||
: 'bg-primary hover:bg-primary/90 text-white disabled:opacity-50'
|
||||
}`}
|
||||
>
|
||||
<span
|
||||
@@ -1571,18 +1571,18 @@ function WorkflowBuilderInner() {
|
||||
)}
|
||||
|
||||
<div className="flex flex-1 overflow-hidden">
|
||||
<div className="border-light-silver dark:bg-raisin-black flex w-64 flex-col gap-6 border-r bg-gray-50 p-4 dark:border-[#3A3A3A]">
|
||||
<div className="border-border bg-muted dark:bg-background flex w-64 flex-col gap-6 border-r p-4">
|
||||
<div>
|
||||
<h3 className="mb-3 text-xs font-semibold tracking-wider text-gray-500 uppercase dark:text-gray-400">
|
||||
Core Nodes
|
||||
</h3>
|
||||
<div className="flex flex-col gap-2">
|
||||
<div
|
||||
className="group flex cursor-move items-center gap-3 rounded-full border bg-white px-4 py-3 shadow-sm transition-all hover:shadow-md dark:border-[#3A3A3A] dark:bg-[#2C2C2C] dark:hover:bg-[#383838]"
|
||||
className="group border-border bg-card flex cursor-move items-center gap-3 rounded-full border px-4 py-3 shadow-sm transition-all hover:shadow-md"
|
||||
draggable
|
||||
onDragStart={(e) => handleNodeDragStart(e, 'agent')}
|
||||
>
|
||||
<div className="text-violets-are-blue group-hover:bg-violets-are-blue flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-purple-100 transition-colors group-hover:text-white">
|
||||
<div className="text-primary group-hover:bg-primary flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-purple-100 transition-colors group-hover:text-white">
|
||||
<Bot size={18} />
|
||||
</div>
|
||||
<span className="text-sm font-medium text-gray-700 dark:text-gray-200">
|
||||
@@ -1590,7 +1590,7 @@ function WorkflowBuilderInner() {
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
className="group flex cursor-move items-center gap-3 rounded-full border bg-white px-4 py-3 shadow-sm transition-all hover:shadow-md dark:border-[#3A3A3A] dark:bg-[#2C2C2C] dark:hover:bg-[#383838]"
|
||||
className="group border-border bg-card flex cursor-move items-center gap-3 rounded-full border px-4 py-3 shadow-sm transition-all hover:shadow-md"
|
||||
draggable
|
||||
onDragStart={(e) => handleNodeDragStart(e, 'end')}
|
||||
>
|
||||
@@ -1602,7 +1602,7 @@ function WorkflowBuilderInner() {
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
className="group flex cursor-move items-center gap-3 rounded-full border bg-white px-4 py-3 shadow-sm transition-all hover:shadow-md dark:border-[#3A3A3A] dark:bg-[#2C2C2C] dark:hover:bg-[#383838]"
|
||||
className="group border-border bg-card flex cursor-move items-center gap-3 rounded-full border px-4 py-3 shadow-sm transition-all hover:shadow-md"
|
||||
draggable
|
||||
onDragStart={(e) => handleNodeDragStart(e, 'note')}
|
||||
>
|
||||
@@ -1622,7 +1622,7 @@ function WorkflowBuilderInner() {
|
||||
</h3>
|
||||
<div className="flex flex-col gap-2">
|
||||
<div
|
||||
className="group flex cursor-move items-center gap-3 rounded-full border bg-white px-4 py-3 shadow-sm transition-all hover:shadow-md dark:border-[#3A3A3A] dark:bg-[#2C2C2C] dark:hover:bg-[#383838]"
|
||||
className="group border-border bg-card flex cursor-move items-center gap-3 rounded-full border px-4 py-3 shadow-sm transition-all hover:shadow-md"
|
||||
draggable
|
||||
onDragStart={(e) => handleNodeDragStart(e, 'state')}
|
||||
>
|
||||
@@ -1630,16 +1630,16 @@ function WorkflowBuilderInner() {
|
||||
<Database size={18} />
|
||||
</div>
|
||||
<div className="flex flex-col">
|
||||
<span className="text-sm font-medium text-gray-700 dark:text-gray-200">
|
||||
<span className="text-foreground text-sm font-medium">
|
||||
Set State
|
||||
</span>
|
||||
<span className="text-[10px] text-gray-400">
|
||||
<span className="text-muted-foreground text-[10px]">
|
||||
Modify workflow variables
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="group flex cursor-move items-center gap-3 rounded-full border bg-white px-4 py-3 shadow-sm transition-all hover:shadow-md dark:border-[#3A3A3A] dark:bg-[#2C2C2C] dark:hover:bg-[#383838]"
|
||||
className="group border-border bg-card flex cursor-move items-center gap-3 rounded-full border px-4 py-3 shadow-sm transition-all hover:shadow-md"
|
||||
draggable
|
||||
onDragStart={(e) => handleNodeDragStart(e, 'condition')}
|
||||
>
|
||||
@@ -1647,10 +1647,10 @@ function WorkflowBuilderInner() {
|
||||
<GitBranch size={18} />
|
||||
</div>
|
||||
<div className="flex flex-col">
|
||||
<span className="text-sm font-medium text-gray-700 dark:text-gray-200">
|
||||
<span className="text-foreground text-sm font-medium">
|
||||
If / Else
|
||||
</span>
|
||||
<span className="text-[10px] text-gray-400">
|
||||
<span className="text-muted-foreground text-[10px]">
|
||||
Conditional branching
|
||||
</span>
|
||||
</div>
|
||||
@@ -1661,7 +1661,7 @@ function WorkflowBuilderInner() {
|
||||
|
||||
<div
|
||||
ref={reactFlowWrapper}
|
||||
className="dark:bg-raisin-black/10 relative flex-1 bg-gray-50"
|
||||
className="bg-muted dark:bg-background/10 relative flex-1"
|
||||
>
|
||||
<ReactFlow
|
||||
nodes={nodes}
|
||||
@@ -1687,8 +1687,8 @@ function WorkflowBuilderInner() {
|
||||
className="absolute inset-0 z-10"
|
||||
onClick={handlePanelBackdropClick}
|
||||
/>
|
||||
<div className="border-light-silver dark:bg-raisin-black absolute top-4 right-4 z-20 w-96 rounded-2xl border bg-white shadow-[0px_4px_40px_-3px_#0000001A] dark:border-[#3A3A3A]">
|
||||
<div className="border-light-silver flex items-center justify-between border-b p-4 dark:border-[#3A3A3A]">
|
||||
<div className="border-border bg-card absolute top-4 right-4 z-20 w-96 rounded-2xl border shadow-[0px_4px_40px_-3px_#0000001A]">
|
||||
<div className="border-border flex items-center justify-between border-b p-4">
|
||||
<h3 className="font-semibold text-gray-900 dark:text-white">
|
||||
{selectedNode.type === 'start' && 'Start Node'}
|
||||
{selectedNode.type === 'end' && 'End Node'}
|
||||
@@ -1707,7 +1707,7 @@ function WorkflowBuilderInner() {
|
||||
|
||||
<div className="max-h-[calc(100vh-200px)] overflow-y-auto p-4">
|
||||
<div className="mb-4 flex flex-col gap-2">
|
||||
<div className="rounded-lg bg-gray-50 p-3 dark:bg-[#2C2C2C]">
|
||||
<div className="bg-muted rounded-lg p-3">
|
||||
<div className="mb-1 text-xs text-gray-500 dark:text-gray-400">
|
||||
Node ID
|
||||
</div>
|
||||
@@ -1736,7 +1736,7 @@ function WorkflowBuilderInner() {
|
||||
label: e.target.value,
|
||||
})
|
||||
}
|
||||
className="border-light-silver focus:ring-purple-30 w-full rounded-xl border bg-white px-3 py-2 text-sm transition-all outline-none focus:ring-2 dark:border-[#3A3A3A] dark:bg-[#2C2C2C] dark:text-white"
|
||||
className="border-border focus:ring-ring bg-card w-full rounded-xl border px-3 py-2 text-sm transition-all outline-none focus:ring-2 dark:text-white"
|
||||
placeholder="Enter node title"
|
||||
/>
|
||||
</div>
|
||||
@@ -1833,7 +1833,7 @@ function WorkflowBuilderInner() {
|
||||
},
|
||||
})
|
||||
}
|
||||
className="border-light-silver focus:ring-purple-30 w-full rounded-xl border bg-white px-3 py-2 text-sm transition-all outline-none focus:ring-2 dark:border-[#3A3A3A] dark:bg-[#2C2C2C] dark:text-white"
|
||||
className="border-border focus:ring-ring bg-card w-full rounded-xl border px-3 py-2 text-sm transition-all outline-none focus:ring-2 dark:text-white"
|
||||
rows={3}
|
||||
placeholder="System prompt for the agent"
|
||||
/>
|
||||
@@ -1876,7 +1876,7 @@ function WorkflowBuilderInner() {
|
||||
},
|
||||
});
|
||||
}}
|
||||
className="border-light-silver focus:ring-purple-30 w-full rounded-xl border bg-white px-3 py-2 text-sm transition-all outline-none focus:ring-2 dark:border-[#3A3A3A] dark:bg-[#2C2C2C] dark:text-white"
|
||||
className="border-border focus:ring-ring bg-card w-full rounded-xl border px-3 py-2 text-sm transition-all outline-none focus:ring-2 dark:text-white"
|
||||
placeholder="Variable name for output"
|
||||
/>
|
||||
</div>
|
||||
@@ -1969,7 +1969,7 @@ function WorkflowBuilderInner() {
|
||||
e.target.value,
|
||||
)
|
||||
}
|
||||
className="border-light-silver focus:ring-purple-30 w-full rounded-xl border bg-white px-3 py-2 font-mono text-xs transition-all outline-none focus:ring-2 dark:border-[#3A3A3A] dark:bg-[#2C2C2C] dark:text-white"
|
||||
className="border-border focus:ring-ring bg-card w-full rounded-xl border px-3 py-2 font-mono text-xs transition-all outline-none focus:ring-2 dark:text-white"
|
||||
rows={8}
|
||||
placeholder={`{
|
||||
"type": "object",
|
||||
@@ -2009,7 +2009,7 @@ function WorkflowBuilderInner() {
|
||||
content: e.target.value,
|
||||
})
|
||||
}
|
||||
className="border-light-silver focus:ring-purple-30 w-full rounded-xl border bg-white px-3 py-2 text-sm transition-all outline-none focus:ring-2 dark:border-[#3A3A3A] dark:bg-[#2C2C2C] dark:text-white"
|
||||
className="border-border focus:ring-ring bg-card w-full rounded-xl border px-3 py-2 text-sm transition-all outline-none focus:ring-2 dark:text-white"
|
||||
rows={4}
|
||||
placeholder="Enter note content"
|
||||
/>
|
||||
@@ -2034,7 +2034,7 @@ function WorkflowBuilderInner() {
|
||||
) => (
|
||||
<div
|
||||
key={idx}
|
||||
className="rounded-xl border border-gray-200 p-3 dark:border-[#3A3A3A]"
|
||||
className="border-border rounded-xl border p-3"
|
||||
>
|
||||
<div className="mb-2 flex items-center justify-between">
|
||||
<span className="text-sm font-medium text-gray-700 dark:text-gray-300">
|
||||
@@ -2084,18 +2084,18 @@ function WorkflowBuilderInner() {
|
||||
},
|
||||
});
|
||||
}}
|
||||
className="border-light-silver focus:ring-purple-30 mb-1 w-full rounded-xl border bg-white px-3 py-2 text-sm transition-all outline-none focus:ring-2 dark:border-[#3A3A3A] dark:bg-[#383838] dark:text-white"
|
||||
className="border-border focus:ring-ring bg-card dark:bg-accent mb-1 w-full rounded-xl border px-3 py-2 text-sm transition-all outline-none focus:ring-2 dark:text-white"
|
||||
rows={2}
|
||||
placeholder="input.foo + 1"
|
||||
/>
|
||||
<p className="mb-3 text-[10px] text-gray-400">
|
||||
<p className="text-muted-foreground mb-3 text-[10px]">
|
||||
Use Common Expression Language to create
|
||||
a custom expression.{' '}
|
||||
<a
|
||||
href="https://cel.dev/"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className="text-violets-are-blue underline"
|
||||
className="text-primary underline"
|
||||
>
|
||||
Learn more
|
||||
</a>
|
||||
@@ -2124,7 +2124,7 @@ function WorkflowBuilderInner() {
|
||||
},
|
||||
});
|
||||
}}
|
||||
className="border-light-silver focus:ring-purple-30 w-full rounded-xl border bg-white px-3 py-2 text-sm transition-all outline-none focus:ring-2 dark:border-[#3A3A3A] dark:bg-[#383838] dark:text-white"
|
||||
className="border-border focus:ring-ring bg-card dark:bg-accent w-full rounded-xl border px-3 py-2 text-sm transition-all outline-none focus:ring-2 dark:text-white"
|
||||
placeholder="variable_name"
|
||||
/>
|
||||
</div>
|
||||
@@ -2145,7 +2145,7 @@ function WorkflowBuilderInner() {
|
||||
},
|
||||
});
|
||||
}}
|
||||
className="flex items-center gap-1 rounded-lg px-3 py-1.5 text-sm font-medium text-gray-600 transition-colors hover:bg-gray-100 dark:text-gray-400 dark:hover:bg-[#383838]"
|
||||
className="hover:bg-accent flex items-center gap-1 rounded-lg px-3 py-1.5 text-sm font-medium text-gray-600 transition-colors dark:text-gray-400"
|
||||
>
|
||||
<Plus size={14} />
|
||||
Add
|
||||
@@ -2158,7 +2158,7 @@ function WorkflowBuilderInner() {
|
||||
<p className="text-xs text-gray-500 dark:text-gray-400">
|
||||
Create conditions to branch your workflow
|
||||
</p>
|
||||
<div className="flex overflow-hidden rounded-lg border border-gray-200 dark:border-[#3A3A3A]">
|
||||
<div className="border-border flex overflow-hidden rounded-lg border">
|
||||
<button
|
||||
onClick={() =>
|
||||
handleUpdateNodeData({
|
||||
@@ -2171,8 +2171,8 @@ function WorkflowBuilderInner() {
|
||||
className={`flex-1 px-3 py-1.5 text-xs font-medium transition-colors ${
|
||||
(selectedNode.data.config?.mode ||
|
||||
'simple') === 'simple'
|
||||
? 'bg-violets-are-blue text-white'
|
||||
: 'text-gray-600 hover:bg-gray-50 dark:text-gray-400 dark:hover:bg-[#383838]'
|
||||
? 'bg-primary text-white'
|
||||
: 'hover:bg-accent text-gray-600 dark:text-gray-400'
|
||||
}`}
|
||||
>
|
||||
Simple
|
||||
@@ -2189,8 +2189,8 @@ function WorkflowBuilderInner() {
|
||||
className={`flex-1 px-3 py-1.5 text-xs font-medium transition-colors ${
|
||||
selectedNode.data.config?.mode ===
|
||||
'advanced'
|
||||
? 'bg-violets-are-blue text-white'
|
||||
: 'text-gray-600 hover:bg-gray-50 dark:text-gray-400 dark:hover:bg-[#383838]'
|
||||
? 'bg-primary text-white'
|
||||
: 'hover:bg-accent text-gray-600 dark:text-gray-400'
|
||||
}`}
|
||||
>
|
||||
Advanced
|
||||
@@ -2201,7 +2201,7 @@ function WorkflowBuilderInner() {
|
||||
(c: ConditionCase, idx: number) => (
|
||||
<div
|
||||
key={c.sourceHandle}
|
||||
className="rounded-xl border border-gray-200 p-3 dark:border-[#3A3A3A]"
|
||||
className="border-border rounded-xl border p-3"
|
||||
>
|
||||
<div className="mb-2 flex items-center justify-between">
|
||||
<span className="text-sm font-semibold text-orange-600 dark:text-orange-400">
|
||||
@@ -2266,7 +2266,7 @@ function WorkflowBuilderInner() {
|
||||
},
|
||||
});
|
||||
}}
|
||||
className="border-light-silver focus:ring-purple-30 mb-2 w-full rounded-xl border bg-white px-3 py-2 text-sm transition-all outline-none focus:ring-2 dark:border-[#3A3A3A] dark:bg-[#383838] dark:text-white"
|
||||
className="border-border focus:ring-ring bg-card dark:bg-accent mb-2 w-full rounded-xl border px-3 py-2 text-sm transition-all outline-none focus:ring-2 dark:text-white"
|
||||
placeholder="Case name (optional)"
|
||||
/>
|
||||
{(selectedNode.data.config?.mode ||
|
||||
@@ -2302,7 +2302,7 @@ function WorkflowBuilderInner() {
|
||||
},
|
||||
});
|
||||
}}
|
||||
className="border-light-silver focus:ring-purple-30 w-full rounded-xl border bg-white px-3 py-2 text-sm outline-none focus:ring-2 dark:border-[#3A3A3A] dark:bg-[#383838] dark:text-white"
|
||||
className="border-border focus:ring-ring bg-card dark:bg-accent w-full rounded-xl border px-3 py-2 text-sm outline-none focus:ring-2 dark:text-white"
|
||||
placeholder="Variable"
|
||||
/>
|
||||
<Select
|
||||
@@ -2394,7 +2394,7 @@ function WorkflowBuilderInner() {
|
||||
},
|
||||
});
|
||||
}}
|
||||
className="border-light-silver focus:ring-purple-30 w-full rounded-xl border bg-white px-3 py-2 text-sm outline-none focus:ring-2 dark:border-[#3A3A3A] dark:bg-[#383838] dark:text-white"
|
||||
className="border-border focus:ring-ring bg-card dark:bg-accent w-full rounded-xl border px-3 py-2 text-sm outline-none focus:ring-2 dark:text-white"
|
||||
placeholder="Value"
|
||||
/>
|
||||
</div>
|
||||
@@ -2419,18 +2419,18 @@ function WorkflowBuilderInner() {
|
||||
},
|
||||
});
|
||||
}}
|
||||
className="border-light-silver focus:ring-purple-30 w-full rounded-xl border bg-white px-3 py-2 text-sm transition-all outline-none focus:ring-2 dark:border-[#3A3A3A] dark:bg-[#383838] dark:text-white"
|
||||
className="border-border focus:ring-ring bg-card dark:bg-accent w-full rounded-xl border px-3 py-2 text-sm transition-all outline-none focus:ring-2 dark:text-white"
|
||||
rows={2}
|
||||
placeholder="Enter condition, e.g. input == 5"
|
||||
/>
|
||||
<p className="mt-1 text-[10px] text-gray-400">
|
||||
<p className="text-muted-foreground mt-1 text-[10px]">
|
||||
Use Common Expression Language to
|
||||
create a custom expression.{' '}
|
||||
<a
|
||||
href="https://cel.dev/"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className="text-violets-are-blue underline"
|
||||
className="text-primary underline"
|
||||
>
|
||||
Learn more
|
||||
</a>
|
||||
@@ -2461,7 +2461,7 @@ function WorkflowBuilderInner() {
|
||||
},
|
||||
});
|
||||
}}
|
||||
className="flex items-center gap-1 rounded-lg px-3 py-1.5 text-sm font-medium text-gray-600 transition-colors hover:bg-gray-100 dark:text-gray-400 dark:hover:bg-[#383838]"
|
||||
className="hover:bg-accent flex items-center gap-1 rounded-lg px-3 py-1.5 text-sm font-medium text-gray-600 transition-colors dark:text-gray-400"
|
||||
>
|
||||
<Plus size={14} />
|
||||
Add
|
||||
@@ -2493,7 +2493,7 @@ function WorkflowBuilderInner() {
|
||||
<SheetContent
|
||||
side="right"
|
||||
showCloseButton={false}
|
||||
className="dark:bg-raisin-black w-full max-w-none p-0 sm:max-w-[600px] md:max-w-[700px] lg:max-w-[800px] dark:border-[#3A3A3A]"
|
||||
className="bg-card w-full max-w-none p-0 sm:max-w-[600px] md:max-w-[700px] lg:max-w-[800px]"
|
||||
>
|
||||
<WorkflowPreview
|
||||
workflowData={{
|
||||
|
||||
@@ -150,7 +150,7 @@ function ExecutionDetails({
|
||||
ref={(el) => {
|
||||
if (el && stepRefs) stepRefs.current.set(step.nodeId, el);
|
||||
}}
|
||||
className="rounded-xl bg-[#F5F5F5] p-3 dark:bg-[#383838]"
|
||||
className="bg-muted dark:bg-accent rounded-xl p-3"
|
||||
>
|
||||
<div className="flex items-center gap-2 text-sm">
|
||||
<span className="flex h-5 w-5 shrink-0 items-center justify-center text-xs font-medium text-gray-500 dark:text-gray-400">
|
||||
@@ -181,7 +181,7 @@ function ExecutionDetails({
|
||||
{(hasOutput || step.error || stateVars.length > 0) && (
|
||||
<div className="mt-3 space-y-2 text-sm">
|
||||
{hasOutput && (
|
||||
<div className="rounded-lg bg-white p-2 dark:bg-[#2A2A2A]">
|
||||
<div className="bg-muted rounded-lg p-2">
|
||||
<span className="font-medium text-gray-600 dark:text-gray-400">
|
||||
Output:{' '}
|
||||
</span>
|
||||
@@ -205,7 +205,7 @@ function ExecutionDetails({
|
||||
{stateVars.map(([key, value]) => (
|
||||
<span
|
||||
key={key}
|
||||
className="inline-flex items-center rounded-lg bg-white px-2 py-1 text-xs dark:bg-[#2A2A2A]"
|
||||
className="bg-muted inline-flex items-center rounded-lg px-2 py-1 text-xs"
|
||||
>
|
||||
<span className="max-w-[100px] truncate font-medium text-gray-600 dark:text-gray-400">
|
||||
{key}:
|
||||
@@ -487,10 +487,10 @@ export default function WorkflowPreview({
|
||||
queries.length > 0 ? queries[queries.length - 1].executionSteps || [] : [];
|
||||
|
||||
return (
|
||||
<div className="dark:bg-raisin-black flex h-full flex-col bg-white">
|
||||
<div className="border-light-silver dark:bg-raisin-black flex h-[77px] items-center justify-between border-b bg-white px-6 dark:border-[#3A3A3A]">
|
||||
<div className="bg-card flex h-full flex-col">
|
||||
<div className="border-border flex h-[77px] items-center justify-between border-b px-6">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="flex items-center justify-center rounded-full bg-gray-100 p-3 text-gray-600 dark:bg-[#2C2C2C] dark:text-gray-300">
|
||||
<div className="bg-muted flex items-center justify-center rounded-full p-3 text-gray-600 dark:text-gray-300">
|
||||
<Play className="h-4 w-4" />
|
||||
</div>
|
||||
<div>
|
||||
@@ -504,7 +504,7 @@ export default function WorkflowPreview({
|
||||
</div>
|
||||
</div>
|
||||
{status === 'loading' && (
|
||||
<span className="text-purple-30 dark:text-violets-are-blue flex items-center gap-1 text-xs">
|
||||
<span className="text-primary dark:text-primary flex items-center gap-1 text-xs">
|
||||
<Loader2 className="h-3 w-3 animate-spin" />
|
||||
Running
|
||||
</span>
|
||||
@@ -512,7 +512,7 @@ export default function WorkflowPreview({
|
||||
</div>
|
||||
|
||||
<div className="flex min-h-0 flex-1">
|
||||
<div className="flex w-64 shrink-0 flex-col border-r border-gray-200 dark:border-[#3A3A3A]">
|
||||
<div className="border-border flex w-64 shrink-0 flex-col border-r">
|
||||
<div className="flex items-center justify-between px-4 py-3">
|
||||
<h3 className="text-xs font-semibold tracking-wider text-gray-500 uppercase dark:text-gray-400">
|
||||
Workflow
|
||||
@@ -537,7 +537,7 @@ export default function WorkflowPreview({
|
||||
>
|
||||
{queries.length === 0 ? (
|
||||
<div className="flex h-full flex-col items-center justify-center">
|
||||
<div className="mb-2 flex size-14 shrink-0 items-center justify-center rounded-xl bg-gray-100 dark:bg-[#2C2C2C]">
|
||||
<div className="bg-muted mb-2 flex size-14 shrink-0 items-center justify-center rounded-xl">
|
||||
<MessageSquare className="size-6 text-gray-600 dark:text-gray-300" />
|
||||
</div>
|
||||
<p className="text-xl font-semibold text-gray-700 dark:text-gray-200">
|
||||
@@ -618,7 +618,7 @@ export default function WorkflowPreview({
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="dark:bg-raisin-black absolute right-0 bottom-0 left-0 flex w-full flex-col gap-2 bg-white px-4 pt-2 pb-4">
|
||||
<div className="bg-card absolute right-0 bottom-0 left-0 flex w-full flex-col gap-2 px-4 pt-2 pb-4">
|
||||
<MessageInput
|
||||
onSubmit={(text) => handleQuestionSubmission(text)}
|
||||
loading={status === 'loading'}
|
||||
|
||||
@@ -2,14 +2,14 @@ import { Monitor } from 'lucide-react';
|
||||
|
||||
export default function MobileBlocker() {
|
||||
return (
|
||||
<div className="bg-lotion dark:bg-raisin-black flex min-h-screen flex-col items-center justify-center px-6 text-center md:hidden">
|
||||
<div className="bg-violets-are-blue/10 dark:bg-violets-are-blue/20 mb-6 flex h-20 w-20 items-center justify-center rounded-2xl">
|
||||
<Monitor className="text-violets-are-blue h-10 w-10" />
|
||||
<div className="bg-background flex min-h-screen flex-col items-center justify-center px-6 text-center md:hidden">
|
||||
<div className="bg-primary/10 dark:bg-primary/20 mb-6 flex h-20 w-20 items-center justify-center rounded-2xl">
|
||||
<Monitor className="text-primary h-10 w-10" />
|
||||
</div>
|
||||
<h2 className="mb-2 text-xl font-bold text-gray-900 dark:text-white">
|
||||
<h2 className="text-foreground mb-2 text-xl font-bold">
|
||||
Desktop Required
|
||||
</h2>
|
||||
<p className="max-w-sm text-sm leading-relaxed text-gray-500 dark:text-[#E0E0E0]">
|
||||
<p className="text-muted-foreground max-w-sm text-sm leading-relaxed">
|
||||
The Workflow Builder requires a larger screen for the best experience.
|
||||
Please open this page on a desktop or laptop computer.
|
||||
</p>
|
||||
|
||||
@@ -186,7 +186,7 @@ function HighlightedOverlay({ text }: { text: string }) {
|
||||
<>
|
||||
{parts.map((part, i) =>
|
||||
/^\{\{[^}]*\}\}$/.test(part) ? (
|
||||
<span key={i} className="text-violets-are-blue font-medium">
|
||||
<span key={i} className="text-primary font-medium">
|
||||
{part}
|
||||
</span>
|
||||
) : (
|
||||
@@ -222,7 +222,7 @@ function VariableListWithSearch({
|
||||
|
||||
return (
|
||||
<div className="flex w-full flex-col overflow-hidden">
|
||||
<div className="flex items-center gap-2 border-b border-[#E5E5E5] px-3 py-2 dark:border-[#3A3A3A]">
|
||||
<div className="border-border flex items-center gap-2 border-b px-3 py-2">
|
||||
<Search className="text-muted-foreground h-3.5 w-3.5 shrink-0" />
|
||||
<input
|
||||
type="text"
|
||||
@@ -252,9 +252,9 @@ function VariableListWithSearch({
|
||||
e.stopPropagation();
|
||||
onSelect(v.templatePath);
|
||||
}}
|
||||
className="flex w-full cursor-pointer items-center gap-2 px-3 py-1.5 text-left text-sm transition-colors hover:bg-gray-50 dark:hover:bg-[#383838]"
|
||||
className="hover:bg-accent flex w-full cursor-pointer items-center gap-2 px-3 py-1.5 text-left text-sm transition-colors"
|
||||
>
|
||||
<Braces className="text-violets-are-blue h-3.5 w-3.5 shrink-0" />
|
||||
<Braces className="text-primary h-3.5 w-3.5 shrink-0" />
|
||||
<span className="truncate font-medium text-gray-800 dark:text-gray-200">
|
||||
{v.label}
|
||||
</span>
|
||||
@@ -412,7 +412,7 @@ export default function PromptTextArea({
|
||||
)}
|
||||
<div
|
||||
ref={wrapperRef}
|
||||
className="border-light-silver focus-within:ring-purple-30 relative rounded-xl border bg-white transition-all focus-within:ring-2 dark:border-[#3A3A3A] dark:bg-[#2C2C2C]"
|
||||
className="border-border focus-within:ring-ring bg-card relative rounded-xl border transition-all focus-within:ring-2"
|
||||
>
|
||||
<div
|
||||
ref={overlayRef}
|
||||
@@ -463,7 +463,7 @@ export default function PromptTextArea({
|
||||
<PopoverTrigger asChild>
|
||||
<button
|
||||
type="button"
|
||||
className="text-violets-are-blue hover:bg-violets-are-blue/10 flex items-center gap-1 rounded-md px-2 py-1 text-xs font-medium transition-colors"
|
||||
className="text-primary hover:bg-primary/10 flex items-center gap-1 rounded-md px-2 py-1 text-xs font-medium transition-colors"
|
||||
>
|
||||
<Plus className="h-3 w-3" />
|
||||
Add context
|
||||
@@ -472,7 +472,7 @@ export default function PromptTextArea({
|
||||
<PopoverContent
|
||||
align="end"
|
||||
side="top"
|
||||
className="w-60 rounded-xl border border-[#E5E5E5] bg-white p-0 shadow-lg dark:border-[#3A3A3A] dark:bg-[#2C2C2C]"
|
||||
className="border-border bg-card w-60 rounded-xl border p-0 shadow-lg"
|
||||
onOpenAutoFocus={(e) => e.preventDefault()}
|
||||
>
|
||||
<VariableListWithSearch
|
||||
@@ -486,7 +486,7 @@ export default function PromptTextArea({
|
||||
{showDropdown && filtered.length > 0 && (
|
||||
<div
|
||||
ref={dropdownRef}
|
||||
className="absolute z-50 w-64 rounded-xl border border-[#E5E5E5] bg-white shadow-lg dark:border-[#3A3A3A] dark:bg-[#2C2C2C]"
|
||||
className="border-border bg-card absolute z-50 w-64 rounded-xl border shadow-lg"
|
||||
style={{ top: dropdownPos.top, left: dropdownPos.left }}
|
||||
>
|
||||
<VariableListWithSearch
|
||||
|
||||
@@ -21,14 +21,13 @@ export const BaseNode: React.FC<BaseNodeProps> = ({
|
||||
icon,
|
||||
handles = { source: true, target: true },
|
||||
}) => {
|
||||
let bgColor = 'bg-white dark:bg-[#2C2C2C]';
|
||||
let borderColor = 'border-gray-200 dark:border-[#3A3A3A]';
|
||||
let bgColor = 'bg-card';
|
||||
let borderColor = 'border-border';
|
||||
let iconBg = 'bg-gray-100 dark:bg-gray-800';
|
||||
let iconColor = 'text-gray-600 dark:text-gray-400';
|
||||
|
||||
if (selected) {
|
||||
borderColor =
|
||||
'border-violets-are-blue ring-2 ring-purple-300 dark:ring-violets-are-blue';
|
||||
borderColor = 'border-primary ring-2 ring-primary';
|
||||
}
|
||||
|
||||
if (type === 'start') {
|
||||
@@ -56,7 +55,7 @@ export const BaseNode: React.FC<BaseNodeProps> = ({
|
||||
type="target"
|
||||
position={Position.Left}
|
||||
isConnectable={true}
|
||||
className="hover:bg-violets-are-blue! -left-1! h-3! w-3! rounded-full! border-2! border-white! bg-gray-400! transition-colors dark:border-[#2C2C2C]!"
|
||||
className="hover:bg-primary/90! border-card! -left-1! h-3! w-3! rounded-full! border-2! bg-gray-400! transition-colors!"
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -86,7 +85,7 @@ export const BaseNode: React.FC<BaseNodeProps> = ({
|
||||
type="source"
|
||||
position={Position.Right}
|
||||
isConnectable={true}
|
||||
className="hover:bg-violets-are-blue! -right-1! h-3! w-3! rounded-full! border-2! border-white! bg-gray-400! transition-colors dark:border-[#2C2C2C]!"
|
||||
className="hover:bg-primary/90! border-card! -right-1! h-3! w-3! rounded-full! border-2! bg-gray-400! transition-colors!"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -36,10 +36,10 @@ const ConditionNode = ({ data, selected }: NodeProps<ConditionNodeData>) => {
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`relative rounded-2xl border bg-white shadow-md transition-all dark:bg-[#2C2C2C] ${
|
||||
className={`bg-card relative rounded-2xl border shadow-md transition-all ${
|
||||
selected
|
||||
? 'border-violets-are-blue dark:ring-violets-are-blue scale-105 ring-2 ring-purple-300'
|
||||
: 'border-gray-200 hover:shadow-lg dark:border-[#3A3A3A]'
|
||||
? 'border-primary dark:ring-primary scale-105 ring-2 ring-purple-300'
|
||||
: 'border-border hover:shadow-lg'
|
||||
}`}
|
||||
style={{ minWidth: 180, maxWidth: 220, height }}
|
||||
>
|
||||
@@ -47,7 +47,7 @@ const ConditionNode = ({ data, selected }: NodeProps<ConditionNodeData>) => {
|
||||
type="target"
|
||||
position={Position.Left}
|
||||
isConnectable
|
||||
className="hover:bg-violets-are-blue! top-1/2! -left-1! h-3! w-3! rounded-full! border-2! border-white! bg-gray-400! transition-colors dark:border-[#2C2C2C]!"
|
||||
className="hover:bg-primary/90! border-card! top-1/2! -left-1! h-3! w-3! rounded-full! border-2! bg-gray-400! transition-colors!"
|
||||
/>
|
||||
|
||||
<div className="flex items-center gap-3 px-3 py-2">
|
||||
@@ -100,7 +100,7 @@ const ConditionNode = ({ data, selected }: NodeProps<ConditionNodeData>) => {
|
||||
id={c.sourceHandle}
|
||||
isConnectable
|
||||
style={{ top: getHandleTop(i, totalOutputs) }}
|
||||
className="hover:bg-violets-are-blue! -right-1! h-3! w-3! rounded-full! border-2! border-white! bg-orange-400! transition-colors dark:border-[#2C2C2C]!"
|
||||
className="hover:bg-primary/90! dark:border-border! -right-1! h-3! w-3! rounded-full! border-2! border-white! bg-orange-400! transition-colors"
|
||||
/>
|
||||
))}
|
||||
<Handle
|
||||
@@ -109,7 +109,7 @@ const ConditionNode = ({ data, selected }: NodeProps<ConditionNodeData>) => {
|
||||
id="else"
|
||||
isConnectable
|
||||
style={{ top: getHandleTop(cases.length, totalOutputs) }}
|
||||
className="hover:bg-violets-are-blue! -right-1! h-3! w-3! rounded-full! border-2! border-white! bg-gray-400! transition-colors dark:border-[#2C2C2C]!"
|
||||
className="hover:bg-primary/90! border-card! -right-1! h-3! w-3! rounded-full! border-2! bg-gray-400! transition-colors!"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -77,7 +77,7 @@ export const AgentNode = memo(function AgentNode({
|
||||
)}
|
||||
{config.model_id && (
|
||||
<div
|
||||
className="text-purple-30 dark:text-violets-are-blue truncate text-xs"
|
||||
className="text-primary dark:text-primary truncate text-xs"
|
||||
title={config.model_id}
|
||||
>
|
||||
{config.model_id}
|
||||
|
||||
@@ -37,7 +37,7 @@ export default function Accordion({
|
||||
className={`flex w-full items-center justify-between focus:outline-hidden ${titleClassName}`}
|
||||
onClick={toggleAccordion}
|
||||
>
|
||||
<p className="break-words">{title}</p>
|
||||
<p className="wrap-break-word">{title}</p>
|
||||
<img
|
||||
src={ChevronDown}
|
||||
className={`h-5 w-5 transform transition-transform duration-200 dark:invert ${
|
||||
|
||||
@@ -53,7 +53,7 @@ export default function ActionButtons({
|
||||
<button
|
||||
title={t('actionButtons.openNewChat')}
|
||||
onClick={newChat}
|
||||
className="hover:bg-bright-gray flex items-center gap-1 rounded-full p-2 lg:hidden dark:hover:bg-[#28292E]"
|
||||
className="hover:bg-accent dark:hover:bg-accent flex items-center gap-1 rounded-full p-2 lg:hidden"
|
||||
>
|
||||
<img
|
||||
className="filter dark:invert"
|
||||
@@ -70,7 +70,7 @@ export default function ActionButtons({
|
||||
<button
|
||||
title={t('actionButtons.share')}
|
||||
onClick={() => setShareModalState(true)}
|
||||
className="hover:bg-bright-gray rounded-full p-2 dark:hover:bg-[#28292E]"
|
||||
className="hover:bg-accent dark:hover:bg-accent rounded-full p-2"
|
||||
>
|
||||
<img
|
||||
className="filter dark:invert"
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import ReactMarkdown from 'react-markdown';
|
||||
import remarkGfm from 'remark-gfm';
|
||||
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 remarkGfm from 'remark-gfm';
|
||||
|
||||
import Exit from '../assets/exit.svg';
|
||||
import { selectToken } from '../preferences/preferenceSlice';
|
||||
import userService from '../api/services/userService';
|
||||
import Spinner from './Spinner';
|
||||
import CopyButton from './CopyButton';
|
||||
import Exit from '../assets/exit.svg';
|
||||
import { useDarkTheme } from '../hooks';
|
||||
import { selectToken } from '../preferences/preferenceSlice';
|
||||
import CopyButton from './CopyButton';
|
||||
import Spinner from './Spinner';
|
||||
|
||||
type TodoItem = {
|
||||
todo_id: number;
|
||||
@@ -61,7 +61,8 @@ const ARTIFACT_TITLE_BY_TYPE: Record<ArtifactData['artifact_type'], string> = {
|
||||
};
|
||||
|
||||
function getArtifactTitle(artifact: ArtifactData | null, toolName?: string) {
|
||||
if (artifact) return ARTIFACT_TITLE_BY_TYPE[artifact.artifact_type] ?? 'Artifact';
|
||||
if (artifact)
|
||||
return ARTIFACT_TITLE_BY_TYPE[artifact.artifact_type] ?? 'Artifact';
|
||||
|
||||
const formattedToolName = (toolName ?? '')
|
||||
.replace(/_/g, ' ')
|
||||
@@ -161,7 +162,7 @@ function NoteView({ data }: { data: NoteArtifactData }) {
|
||||
<div className="flex-1 overflow-y-auto p-4">
|
||||
{data.content ? (
|
||||
<ReactMarkdown
|
||||
className="flex flex-col gap-3 text-sm leading-normal break-words whitespace-pre-wrap text-gray-800 dark:text-gray-200"
|
||||
className="flex flex-col gap-3 text-sm leading-normal wrap-break-word whitespace-pre-wrap text-gray-800 dark:text-gray-200"
|
||||
remarkPlugins={[remarkGfm]}
|
||||
components={{
|
||||
code(props) {
|
||||
@@ -178,9 +179,9 @@ function NoteView({ data }: { data: NoteArtifactData }) {
|
||||
const language = match ? match[1] : '';
|
||||
|
||||
return match ? (
|
||||
<div className="group border-light-silver dark:border-raisin-black relative my-2 overflow-hidden rounded-[14px] border">
|
||||
<div className="bg-platinum dark:bg-eerie-black-2 flex items-center justify-between px-2 py-1">
|
||||
<span className="text-just-black dark:text-chinese-white text-xs font-medium">
|
||||
<div className="group border-border relative my-2 overflow-hidden rounded-[14px] border">
|
||||
<div className="bg-platinum flex items-center justify-between px-2 py-1">
|
||||
<span className="text-foreground dark:text-foreground text-xs font-medium">
|
||||
{language}
|
||||
</span>
|
||||
<CopyButton
|
||||
@@ -203,7 +204,7 @@ function NoteView({ data }: { data: NoteArtifactData }) {
|
||||
</div>
|
||||
) : (
|
||||
<code
|
||||
className="dark:bg-independence dark:text-bright-gray rounded-[6px] bg-gray-200 px-[8px] py-[4px] text-xs font-normal"
|
||||
className="dark:bg-accent dark:text-foreground rounded-[6px] bg-gray-200 px-2 py-1 text-xs font-normal"
|
||||
{...rest}
|
||||
>
|
||||
{children}
|
||||
@@ -315,17 +316,17 @@ export default function ArtifactSidebar({
|
||||
// Generate a unique ID for this fetch
|
||||
const fetchId = `${effectiveArtifactId}-${Date.now()}`;
|
||||
currentFetchIdRef.current = fetchId;
|
||||
|
||||
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
|
||||
|
||||
// Note: For todo artifacts, the endpoint always returns all todos for the tool; will be coversation scoped later
|
||||
userService
|
||||
.getArtifact(effectiveArtifactId, token)
|
||||
.then(async (res: any) => {
|
||||
// Ignore if this is not the current fetch
|
||||
if (currentFetchIdRef.current !== fetchId) return;
|
||||
|
||||
|
||||
const isResponseLike = res && typeof res.json === 'function';
|
||||
const status = isResponseLike ? res.status : undefined;
|
||||
const ok = isResponseLike ? Boolean(res.ok) : true;
|
||||
@@ -453,7 +454,7 @@ export default function ArtifactSidebar({
|
||||
{title}
|
||||
</span>
|
||||
<button
|
||||
className="rounded-full p-1 hover:bg-gray-100 dark:hover:bg-gray-800"
|
||||
className="hover:bg-accent dark:hover:bg-accent rounded-full p-1"
|
||||
onClick={onClose}
|
||||
>
|
||||
<img
|
||||
@@ -472,7 +473,7 @@ export default function ArtifactSidebar({
|
||||
return (
|
||||
<div ref={sidebarRef} className="h-vh relative">
|
||||
<div
|
||||
className={`dark:bg-chinese-black fixed top-0 right-0 z-50 flex h-full w-80 transform flex-col bg-white shadow-xl transition-all duration-300 sm:w-96 ${
|
||||
className={`dark:bg-card bg-card fixed top-0 right-0 z-50 flex h-full w-80 transform flex-col shadow-xl transition-all duration-300 sm:w-96 ${
|
||||
isOpen ? 'translate-x-0' : 'translate-x-full'
|
||||
} border-l border-[#9ca3af]/10`}
|
||||
>
|
||||
@@ -481,7 +482,7 @@ export default function ArtifactSidebar({
|
||||
{title}
|
||||
</span>
|
||||
<button
|
||||
className="hover:bg-gray-1000 dark:hover:bg-gun-metal rounded-full p-2"
|
||||
className="hover:bg-accent rounded-full p-2"
|
||||
onClick={onClose}
|
||||
>
|
||||
<img
|
||||
|
||||
@@ -1,25 +1,27 @@
|
||||
import React, { useState, useEffect, useRef } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { selectToken } from '../preferences/preferenceSlice';
|
||||
import { useSelector } from 'react-redux';
|
||||
|
||||
import userService from '../api/services/userService';
|
||||
import ArrowLeft from '../assets/arrow-left.svg';
|
||||
import FileIcon from '../assets/file.svg';
|
||||
import FolderIcon from '../assets/folder.svg';
|
||||
import NoFilesDarkIcon from '../assets/no-files-dark.svg';
|
||||
import NoFilesIcon from '../assets/no-files.svg';
|
||||
import SearchIcon from '../assets/search.svg';
|
||||
import {
|
||||
useDarkTheme,
|
||||
useLoaderState,
|
||||
useMediaQuery,
|
||||
useOutsideAlerter,
|
||||
} from '../hooks';
|
||||
import userService from '../api/services/userService';
|
||||
import ArrowLeft from '../assets/arrow-left.svg';
|
||||
import NoFilesIcon from '../assets/no-files.svg';
|
||||
import NoFilesDarkIcon from '../assets/no-files-dark.svg';
|
||||
import SkeletonLoader from './SkeletonLoader';
|
||||
import ConfirmationModal from '../modals/ConfirmationModal';
|
||||
import { ActiveState } from '../models/misc';
|
||||
import { selectToken } from '../preferences/preferenceSlice';
|
||||
import { ChunkType } from '../settings/types';
|
||||
import Pagination from './DocumentPagination';
|
||||
import FileIcon from '../assets/file.svg';
|
||||
import FolderIcon from '../assets/folder.svg';
|
||||
import SearchIcon from '../assets/search.svg';
|
||||
import SkeletonLoader from './SkeletonLoader';
|
||||
|
||||
interface LineNumberedTextareaProps {
|
||||
value: string;
|
||||
onChange: (value: string) => void;
|
||||
@@ -73,7 +75,7 @@ const LineNumberedTextarea: React.FC<LineNumberedTextareaProps> = ({
|
||||
))}
|
||||
</div>
|
||||
<textarea
|
||||
className={`w-full resize-none overflow-hidden border-none bg-transparent pl-8 font-['Inter'] text-[13.68px] leading-[19.93px] text-[#18181B] outline-none lg:pl-12 dark:text-white ${isMobile ? 'min-h-[calc(100vh-200px)]' : 'min-h-[calc(100vh-300px)]'} ${!editable ? 'select-none' : ''}`}
|
||||
className={`text-foreground w-full resize-none overflow-hidden border-none bg-transparent pl-8 font-['Inter'] text-[13.68px] leading-[19.93px] outline-none lg:pl-12 dark:text-white ${isMobile ? 'min-h-[calc(100vh-200px)]' : 'min-h-[calc(100vh-300px)]'} ${!editable ? 'select-none' : ''}`}
|
||||
value={value}
|
||||
onChange={editable ? handleChange : undefined}
|
||||
onDoubleClick={onDoubleClick}
|
||||
@@ -301,7 +303,7 @@ const Chunks: React.FC<ChunksProps> = ({
|
||||
<div className="mb-0 flex min-h-[38px] flex-col gap-2 text-base sm:flex-row sm:items-center sm:justify-between">
|
||||
<div className="flex w-full items-center sm:w-auto">
|
||||
<button
|
||||
className="mr-3 flex h-[29px] w-[29px] items-center justify-center rounded-full border p-2 text-sm font-medium text-gray-400 transition-all duration-200 dark:border-0 dark:bg-[#28292D] dark:text-gray-500 dark:hover:bg-[#2E2F34]"
|
||||
className="mr-3 flex h-[29px] w-[29px] items-center justify-center rounded-full border p-2 text-sm font-medium text-gray-400 transition-all duration-200 dark:border-0 dark:text-gray-500"
|
||||
onClick={
|
||||
editingChunk
|
||||
? () => setEditingChunk(null)
|
||||
@@ -315,17 +317,17 @@ const Chunks: React.FC<ChunksProps> = ({
|
||||
|
||||
<div className="flex flex-wrap items-center">
|
||||
{/* Removed the directory icon */}
|
||||
<span className="font-semibold break-words text-[#7D54D1]">
|
||||
<span className="font-semibold wrap-break-word text-[#7D54D1]">
|
||||
{documentName}
|
||||
</span>
|
||||
|
||||
{pathParts.length > 0 && (
|
||||
<>
|
||||
<span className="mx-1 flex-shrink-0 text-gray-500">/</span>
|
||||
<span className="mx-1 shrink-0 text-gray-500">/</span>
|
||||
{pathParts.map((part, index) => (
|
||||
<React.Fragment key={index}>
|
||||
<span
|
||||
className={`break-words ${
|
||||
className={`wrap-break-word ${
|
||||
index < pathParts.length - 1
|
||||
? 'font-medium text-[#7D54D1]'
|
||||
: 'text-gray-700 dark:text-gray-300'
|
||||
@@ -334,9 +336,7 @@ const Chunks: React.FC<ChunksProps> = ({
|
||||
{part}
|
||||
</span>
|
||||
{index < pathParts.length - 1 && (
|
||||
<span className="mx-1 flex-shrink-0 text-gray-500">
|
||||
/
|
||||
</span>
|
||||
<span className="mx-1 shrink-0 text-gray-500">/</span>
|
||||
)}
|
||||
</React.Fragment>
|
||||
))}
|
||||
@@ -350,7 +350,7 @@ const Chunks: React.FC<ChunksProps> = ({
|
||||
!isEditing ? (
|
||||
<>
|
||||
<button
|
||||
className="bg-purple-30 hover:bg-violets-are-blue flex h-[38px] min-w-[108px] items-center justify-center rounded-full px-4 text-[14px] font-medium whitespace-nowrap text-white"
|
||||
className="bg-primary hover:bg-primary/90 flex h-[38px] min-w-[108px] items-center justify-center rounded-full px-4 text-[14px] font-medium whitespace-nowrap text-white"
|
||||
onClick={() => setIsEditing(true)}
|
||||
>
|
||||
{t('modals.chunk.edit')}
|
||||
@@ -370,7 +370,7 @@ const Chunks: React.FC<ChunksProps> = ({
|
||||
onClick={() => {
|
||||
setIsEditing(false);
|
||||
}}
|
||||
className="dark:text-light-gray flex h-[38px] min-w-[108px] cursor-pointer items-center justify-center rounded-full px-4 py-1 text-sm font-medium text-nowrap hover:bg-gray-100 dark:bg-transparent dark:hover:bg-[#767183]/50"
|
||||
className="dark:text-foreground hover:bg-accent dark:hover:bg-accent flex h-[38px] min-w-[108px] cursor-pointer items-center justify-center rounded-full px-4 py-1 text-sm font-medium text-nowrap"
|
||||
>
|
||||
{t('modals.chunk.cancel')}
|
||||
</button>
|
||||
@@ -402,7 +402,7 @@ const Chunks: React.FC<ChunksProps> = ({
|
||||
editingText.trim() &&
|
||||
(editingTitle !== (editingChunk?.metadata?.title || '') ||
|
||||
editingText !== (editingChunk?.text || ''))
|
||||
? 'bg-purple-30 hover:bg-violets-are-blue cursor-pointer'
|
||||
? 'bg-primary hover:bg-primary/90 cursor-pointer'
|
||||
: 'cursor-not-allowed bg-gray-400'
|
||||
}`}
|
||||
>
|
||||
@@ -414,7 +414,7 @@ const Chunks: React.FC<ChunksProps> = ({
|
||||
<>
|
||||
<button
|
||||
onClick={() => setIsAddingChunk(false)}
|
||||
className="dark:text-light-gray flex h-[38px] min-w-[108px] cursor-pointer items-center justify-center rounded-full px-4 py-1 text-sm font-medium text-nowrap hover:bg-gray-100 dark:bg-transparent dark:hover:bg-[#767183]/50"
|
||||
className="dark:text-foreground hover:bg-accent dark:hover:bg-accent flex h-[38px] min-w-[108px] cursor-pointer items-center justify-center rounded-full px-4 py-1 text-sm font-medium text-nowrap"
|
||||
>
|
||||
{t('modals.chunk.cancel')}
|
||||
</button>
|
||||
@@ -428,7 +428,7 @@ const Chunks: React.FC<ChunksProps> = ({
|
||||
disabled={!editingText.trim()}
|
||||
className={`flex h-[38px] min-w-[108px] items-center justify-center rounded-full px-4 py-1 text-[14px] font-medium text-nowrap text-white transition-all ${
|
||||
editingText.trim()
|
||||
? 'bg-purple-30 hover:bg-violets-are-blue cursor-pointer'
|
||||
? 'bg-primary hover:bg-primary/90 cursor-pointer'
|
||||
: 'cursor-not-allowed bg-gray-400'
|
||||
}`}
|
||||
>
|
||||
@@ -488,14 +488,14 @@ const Chunks: React.FC<ChunksProps> = ({
|
||||
value={fileSearchQuery}
|
||||
onChange={(e) => handleFileSearchChange(e.target.value)}
|
||||
placeholder={t('settings.sources.searchFiles')}
|
||||
className={`h-[38px] w-full border border-[#D1D9E0] py-2 pr-4 pl-10 dark:border-[#6A6A6A] ${
|
||||
className={`border-border dark:border-border h-[38px] w-full border py-2 pr-4 pl-10 ${
|
||||
fileSearchQuery ? 'rounded-t-[6px]' : 'rounded-[6px]'
|
||||
} bg-transparent transition-all duration-200 focus:outline-none dark:text-[#E0E0E0]`}
|
||||
} bg-transparent transition-all duration-200 focus:outline-none`}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{fileSearchQuery && (
|
||||
<div className="absolute z-10 max-h-[calc(100vh-200px)] w-full overflow-hidden rounded-b-[6px] border border-t-0 border-[#D1D9E0] bg-white shadow-lg dark:border-[#6A6A6A] dark:bg-[#1F2023]">
|
||||
<div className="border-border bg-card dark:border-border dark:bg-card absolute z-10 max-h-[calc(100vh-200px)] w-full overflow-hidden rounded-b-[6px] border border-t-0 shadow-lg">
|
||||
<div className="max-h-[calc(100vh-200px)] overflow-x-hidden overflow-y-auto">
|
||||
{fileSearchResults.length === 0 ? (
|
||||
<div className="py-2 text-center text-sm text-gray-500 dark:text-gray-400">
|
||||
@@ -507,18 +507,18 @@ const Chunks: React.FC<ChunksProps> = ({
|
||||
key={index}
|
||||
title={result.path}
|
||||
onClick={() => handleSearchResultClick(result)}
|
||||
className={`flex cursor-pointer items-center px-3 py-2 hover:bg-[#ECEEEF] dark:hover:bg-[#27282D] ${
|
||||
className={`hover:bg-muted dark:hover:bg-muted flex cursor-pointer items-center px-3 py-2 ${
|
||||
index !== fileSearchResults.length - 1
|
||||
? 'border-b border-[#D1D9E0] dark:border-[#6A6A6A]'
|
||||
? 'border-border dark:border-border border-b'
|
||||
: ''
|
||||
}`}
|
||||
>
|
||||
<img
|
||||
src={result.isFile ? FileIcon : FolderIcon}
|
||||
alt={result.isFile ? 'File' : 'Folder'}
|
||||
className="mr-2 h-4 w-4 flex-shrink-0"
|
||||
className="mr-2 h-4 w-4 shrink-0"
|
||||
/>
|
||||
<span className="truncate text-sm dark:text-[#E0E0E0]">
|
||||
<span className="truncate text-sm">
|
||||
{result.name ||
|
||||
result.path.split('/').pop() ||
|
||||
result.path}
|
||||
@@ -546,8 +546,8 @@ const Chunks: React.FC<ChunksProps> = ({
|
||||
{!editingChunk && !isAddingChunk ? (
|
||||
<>
|
||||
<div className="mb-3 flex flex-col items-start justify-between gap-3 sm:flex-row sm:items-center">
|
||||
<div className="flex h-[38px] w-full flex-1 items-center overflow-hidden rounded-md border border-[#D1D9E0] dark:border-[#6A6A6A]">
|
||||
<div className="flex h-full items-center px-4 font-medium whitespace-nowrap text-gray-700 dark:text-[#E0E0E0]">
|
||||
<div className="border-border dark:border-border flex h-[38px] w-full flex-1 items-center overflow-hidden rounded-md border">
|
||||
<div className="dark:text-foreground flex h-full items-center px-4 font-medium whitespace-nowrap text-gray-700">
|
||||
{totalChunks > 999999
|
||||
? `${(totalChunks / 1000000).toFixed(2)}M`
|
||||
: totalChunks > 999
|
||||
@@ -555,19 +555,19 @@ const Chunks: React.FC<ChunksProps> = ({
|
||||
: totalChunks}{' '}
|
||||
{t('settings.sources.chunks')}
|
||||
</div>
|
||||
<div className="h-full w-[1px] bg-[#D1D9E0] dark:bg-[#6A6A6A]"></div>
|
||||
<div className="bg-border dark:bg-border h-full w-px"></div>
|
||||
<div className="h-full flex-1">
|
||||
<input
|
||||
type="text"
|
||||
placeholder={t('settings.sources.searchPlaceholder')}
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
className="h-full w-full border-none bg-transparent px-3 py-2 text-[13.56px] leading-[100%] font-normal outline-none dark:text-[#E0E0E0]"
|
||||
className="h-full w-full border-none bg-transparent px-3 py-2 text-[13.56px] leading-[100%] font-normal outline-none"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
className="bg-purple-30 hover:bg-violets-are-blue flex h-[38px] w-full min-w-[108px] shrink-0 items-center justify-center rounded-full px-4 text-[14px] font-medium whitespace-normal text-white sm:w-auto"
|
||||
className="bg-primary hover:bg-primary/90 flex h-[38px] w-full min-w-[108px] shrink-0 items-center justify-center rounded-full px-4 text-[14px] font-medium whitespace-normal text-white sm:w-auto"
|
||||
title={t('settings.sources.addChunk')}
|
||||
onClick={() => {
|
||||
setIsAddingChunk(true);
|
||||
@@ -579,11 +579,11 @@ const Chunks: React.FC<ChunksProps> = ({
|
||||
</button>
|
||||
</div>
|
||||
{loading ? (
|
||||
<div className="grid w-full grid-cols-1 justify-items-start gap-4 sm:[grid-template-columns:repeat(auto-fit,minmax(400px,1fr))]">
|
||||
<div className="grid w-full grid-cols-1 justify-items-start gap-4 sm:grid-cols-[repeat(auto-fit,minmax(400px,1fr))]">
|
||||
<SkeletonLoader component="chunkCards" count={perPage} />
|
||||
</div>
|
||||
) : (
|
||||
<div className="grid w-full grid-cols-1 justify-items-start gap-4 sm:[grid-template-columns:repeat(auto-fit,minmax(400px,1fr))]">
|
||||
<div className="grid w-full grid-cols-1 justify-items-start gap-4 sm:grid-cols-[repeat(auto-fit,minmax(400px,1fr))]">
|
||||
{filteredChunks.length === 0 ? (
|
||||
<div className="col-span-full flex min-h-[50vh] w-full flex-col items-center justify-center text-center text-gray-500 dark:text-gray-400">
|
||||
<img
|
||||
@@ -597,7 +597,7 @@ const Chunks: React.FC<ChunksProps> = ({
|
||||
filteredChunks.map((chunk, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="relative flex h-[197px] w-full max-w-[487px] transform cursor-pointer flex-col justify-between overflow-hidden rounded-[5.86px] border border-[#D1D9E0] transition-transform duration-200 hover:scale-105 dark:border-[#6A6A6A]"
|
||||
className="border-border dark:border-border relative flex h-[197px] w-full max-w-[487px] transform cursor-pointer flex-col justify-between overflow-hidden rounded-[5.86px] border transition-transform duration-200 hover:scale-105"
|
||||
onClick={() => {
|
||||
setEditingChunk(chunk);
|
||||
setEditingTitle(chunk.metadata?.title || '');
|
||||
@@ -605,8 +605,8 @@ const Chunks: React.FC<ChunksProps> = ({
|
||||
}}
|
||||
>
|
||||
<div className="w-full">
|
||||
<div className="flex w-full items-center justify-between border-b border-[#D1D9E0] bg-[#F6F8FA] px-4 py-3 dark:border-[#6A6A6A] dark:bg-[#27282D]">
|
||||
<div className="text-sm text-[#59636E] dark:text-[#E0E0E0]">
|
||||
<div className="border-border bg-muted dark:border-border dark:bg-card flex w-full items-center justify-between border-b px-4 py-3">
|
||||
<div className="dark:text-muted-foreground text-sm text-[#59636E]">
|
||||
{chunk.metadata.token_count
|
||||
? chunk.metadata.token_count.toLocaleString()
|
||||
: '-'}{' '}
|
||||
@@ -614,7 +614,7 @@ const Chunks: React.FC<ChunksProps> = ({
|
||||
</div>
|
||||
</div>
|
||||
<div className="px-4 pt-3 pb-6">
|
||||
<p className="line-clamp-6 font-['Inter'] text-[13.68px] leading-[19.93px] font-normal text-[#18181B] dark:text-[#E0E0E0]">
|
||||
<p className="text-foreground line-clamp-6 font-['Inter'] text-[13.68px] leading-[19.93px] font-normal">
|
||||
{chunk.text}
|
||||
</p>
|
||||
</div>
|
||||
@@ -627,7 +627,7 @@ const Chunks: React.FC<ChunksProps> = ({
|
||||
</>
|
||||
) : isAddingChunk ? (
|
||||
<div className="w-full">
|
||||
<div className="relative overflow-hidden rounded-lg border border-[#D1D9E0] dark:border-[#6A6A6A]">
|
||||
<div className="border-border dark:border-border relative overflow-hidden rounded-lg border">
|
||||
<LineNumberedTextarea
|
||||
value={editingText}
|
||||
onChange={setEditingText}
|
||||
@@ -639,9 +639,9 @@ const Chunks: React.FC<ChunksProps> = ({
|
||||
) : (
|
||||
editingChunk && (
|
||||
<div className="w-full">
|
||||
<div className="relative flex w-full flex-col overflow-hidden rounded-[5.86px] border border-[#D1D9E0] dark:border-[#6A6A6A]">
|
||||
<div className="flex w-full items-center justify-between border-b border-[#D1D9E0] bg-[#F6F8FA] px-4 py-3 dark:border-[#6A6A6A] dark:bg-[#27282D]">
|
||||
<div className="text-sm text-[#59636E] dark:text-[#E0E0E0]">
|
||||
<div className="border-border dark:border-border relative flex w-full flex-col overflow-hidden rounded-[5.86px] border">
|
||||
<div className="border-border bg-muted dark:border-border dark:bg-card flex w-full items-center justify-between border-b px-4 py-3">
|
||||
<div className="dark:text-muted-foreground text-sm text-[#59636E]">
|
||||
{editingChunk.metadata.token_count
|
||||
? editingChunk.metadata.token_count.toLocaleString()
|
||||
: '-'}{' '}
|
||||
|
||||
@@ -68,9 +68,7 @@ export default function ConfigFields({
|
||||
<div key={key} className="flex flex-col gap-1.5">
|
||||
<Label htmlFor={key}>
|
||||
{spec.label || key}
|
||||
{spec.required && (
|
||||
<span className="text-red-500">*</span>
|
||||
)}
|
||||
{spec.required && <span className="text-red-500">*</span>}
|
||||
</Label>
|
||||
<Select
|
||||
value={value || spec.default || ''}
|
||||
@@ -82,7 +80,8 @@ export default function ConfigFields({
|
||||
size="lg"
|
||||
className={cn(
|
||||
'w-full rounded-xl',
|
||||
hasError && 'border-destructive aria-invalid:ring-destructive/20',
|
||||
hasError &&
|
||||
'border-destructive aria-invalid:ring-destructive/20',
|
||||
)}
|
||||
>
|
||||
<SelectValue placeholder={spec.label || key} />
|
||||
@@ -90,13 +89,14 @@ export default function ConfigFields({
|
||||
<SelectContent>
|
||||
{spec.enum.map((v) => (
|
||||
<SelectItem key={v} value={v}>
|
||||
{v.charAt(0).toUpperCase() + v.slice(1).replace(/_/g, ' ')}
|
||||
{v.charAt(0).toUpperCase() +
|
||||
v.slice(1).replace(/_/g, ' ')}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
{hasError && (
|
||||
<p className="text-xs text-destructive">{errors[key]}</p>
|
||||
<p className="text-destructive text-xs">{errors[key]}</p>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
@@ -106,9 +106,7 @@ export default function ConfigFields({
|
||||
<div key={key} className="flex flex-col gap-1.5">
|
||||
<Label htmlFor={key}>
|
||||
{spec.label || key}
|
||||
{spec.required && (
|
||||
<span className="text-red-500">*</span>
|
||||
)}
|
||||
{spec.required && <span className="text-red-500">*</span>}
|
||||
</Label>
|
||||
<Input
|
||||
id={key}
|
||||
@@ -134,12 +132,14 @@ export default function ConfigFields({
|
||||
}}
|
||||
placeholder={placeholder || spec.description || ''}
|
||||
min={spec.type === 'number' ? 1 : undefined}
|
||||
max={spec.type === 'number' && key === 'timeout' ? 300 : undefined}
|
||||
max={
|
||||
spec.type === 'number' && key === 'timeout' ? 300 : undefined
|
||||
}
|
||||
aria-invalid={hasError || undefined}
|
||||
className={cn('rounded-xl', hasError && 'border-destructive')}
|
||||
/>
|
||||
{hasError && (
|
||||
<p className="text-xs text-destructive">{errors[key]}</p>
|
||||
<p className="text-destructive text-xs">{errors[key]}</p>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -136,7 +136,7 @@ const ConnectorAuth: React.FC<ConnectorAuthProps> = ({
|
||||
</svg>
|
||||
|
||||
<span
|
||||
className="text-sm text-[#E60000] dark:text-[#E37064]"
|
||||
className="text-sm text-[#E60000] dark:text-red-400"
|
||||
style={{
|
||||
fontFamily: 'Inter',
|
||||
lineHeight: '100%',
|
||||
|
||||
@@ -1,30 +1,31 @@
|
||||
import React, { useState, useRef, useEffect } from 'react';
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { formatBytes } from '../utils/stringUtils';
|
||||
import { selectToken } from '../preferences/preferenceSlice';
|
||||
|
||||
import userService from '../api/services/userService';
|
||||
import ArrowLeft from '../assets/arrow-left.svg';
|
||||
import CheckmarkIcon from '../assets/checkMark2.svg';
|
||||
import EyeView from '../assets/eye-view.svg';
|
||||
import FileIcon from '../assets/file.svg';
|
||||
import FolderIcon from '../assets/folder.svg';
|
||||
import SyncIcon from '../assets/sync.svg';
|
||||
import ThreeDots from '../assets/three-dots.svg';
|
||||
import { useLoaderState, useOutsideAlerter } from '../hooks';
|
||||
import ConfirmationModal from '../modals/ConfirmationModal';
|
||||
import { ActiveState } from '../models/misc';
|
||||
import { selectToken } from '../preferences/preferenceSlice';
|
||||
import { formatBytes } from '../utils/stringUtils';
|
||||
import Chunks from './Chunks';
|
||||
import ContextMenu, { MenuOption } from './ContextMenu';
|
||||
import SkeletonLoader from './SkeletonLoader';
|
||||
import ConfirmationModal from '../modals/ConfirmationModal';
|
||||
import userService from '../api/services/userService';
|
||||
import FileIcon from '../assets/file.svg';
|
||||
import FolderIcon from '../assets/folder.svg';
|
||||
import ArrowLeft from '../assets/arrow-left.svg';
|
||||
import ThreeDots from '../assets/three-dots.svg';
|
||||
import EyeView from '../assets/eye-view.svg';
|
||||
import SyncIcon from '../assets/sync.svg';
|
||||
import CheckmarkIcon from '../assets/checkMark2.svg';
|
||||
import { useOutsideAlerter, useLoaderState } from '../hooks';
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableContainer,
|
||||
TableHead,
|
||||
TableBody,
|
||||
TableRow,
|
||||
TableHeader,
|
||||
TableCell,
|
||||
TableRow,
|
||||
} from './Table';
|
||||
|
||||
interface FileNode {
|
||||
@@ -325,26 +326,26 @@ const ConnectorTree: React.FC<ConnectorTreeProps> = ({
|
||||
{/* Left side with path navigation */}
|
||||
<div className="flex w-full items-center sm:w-auto">
|
||||
<button
|
||||
className="mr-3 flex h-[29px] w-[29px] items-center justify-center rounded-full border p-2 text-sm font-medium text-gray-400 dark:border-0 dark:bg-[#28292D] dark:text-gray-500 dark:hover:bg-[#2E2F34]"
|
||||
className="text-muted-foreground mr-3 flex h-[29px] w-[29px] items-center justify-center rounded-full border p-2 text-sm font-medium dark:border-0"
|
||||
onClick={handleBackNavigation}
|
||||
>
|
||||
<img src={ArrowLeft} alt="left-arrow" className="h-3 w-3" />
|
||||
</button>
|
||||
|
||||
<div className="flex flex-wrap items-center">
|
||||
<span className="font-semibold break-words text-[#7D54D1]">
|
||||
<span className="font-semibold wrap-break-word text-[#7D54D1]">
|
||||
{sourceName}
|
||||
</span>
|
||||
{currentPath.length > 0 && (
|
||||
<>
|
||||
<span className="mx-1 flex-shrink-0 text-gray-500">/</span>
|
||||
<span className="text-muted-foreground mx-1 shrink-0">/</span>
|
||||
{currentPath.map((dir, index) => (
|
||||
<React.Fragment key={index}>
|
||||
<span className="break-words text-gray-700 dark:text-[#E0E0E0]">
|
||||
<span className="dark:text-foreground wrap-break-word text-gray-700">
|
||||
{dir}
|
||||
</span>
|
||||
{index < currentPath.length - 1 && (
|
||||
<span className="mx-1 flex-shrink-0 text-gray-500">
|
||||
<span className="text-muted-foreground mx-1 shrink-0">
|
||||
/
|
||||
</span>
|
||||
)}
|
||||
@@ -364,8 +365,8 @@ const ConnectorTree: React.FC<ConnectorTreeProps> = ({
|
||||
disabled={isSyncing}
|
||||
className={`flex h-[38px] min-w-[108px] items-center justify-center rounded-full px-4 text-[14px] font-medium whitespace-nowrap transition-colors ${
|
||||
isSyncing
|
||||
? 'cursor-not-allowed bg-gray-300 text-gray-600 dark:bg-gray-600 dark:text-gray-400'
|
||||
: 'bg-purple-30 hover:bg-violets-are-blue text-white'
|
||||
? 'dark:bg-muted dark:text-muted-foreground cursor-not-allowed bg-gray-300 text-gray-600'
|
||||
: 'bg-primary hover:bg-primary/90 text-white'
|
||||
}`}
|
||||
title={
|
||||
isSyncing
|
||||
@@ -402,7 +403,7 @@ const ConnectorTree: React.FC<ConnectorTreeProps> = ({
|
||||
<img
|
||||
src={FolderIcon}
|
||||
alt={t('settings.sources.parentFolderAlt')}
|
||||
className="mr-2 h-4 w-4 flex-shrink-0"
|
||||
className="mr-2 h-4 w-4 shrink-0"
|
||||
/>
|
||||
<span className="truncate">..</span>
|
||||
</div>
|
||||
@@ -449,7 +450,7 @@ const ConnectorTree: React.FC<ConnectorTreeProps> = ({
|
||||
<img
|
||||
src={FolderIcon}
|
||||
alt={t('settings.sources.folderAlt')}
|
||||
className="mr-2 h-4 w-4 flex-shrink-0"
|
||||
className="mr-2 h-4 w-4 shrink-0"
|
||||
/>
|
||||
<span className="truncate">{name}</span>
|
||||
</div>
|
||||
@@ -466,7 +467,7 @@ const ConnectorTree: React.FC<ConnectorTreeProps> = ({
|
||||
<div ref={menuRef} className="relative">
|
||||
<button
|
||||
onClick={(e) => handleMenuClick(e, itemId)}
|
||||
className="inline-flex h-[35px] w-[24px] shrink-0 items-center justify-center rounded-md font-medium transition-colors hover:bg-[#EBEBEB] dark:hover:bg-[#26272E]"
|
||||
className="dark:hover:bg-muted inline-flex h-[35px] w-6 shrink-0 items-center justify-center rounded-md font-medium transition-colors hover:bg-[#EBEBEB]"
|
||||
aria-label={t('settings.sources.menuAlt')}
|
||||
>
|
||||
<img
|
||||
@@ -512,7 +513,7 @@ const ConnectorTree: React.FC<ConnectorTreeProps> = ({
|
||||
<img
|
||||
src={FileIcon}
|
||||
alt={t('settings.sources.fileAlt')}
|
||||
className="mr-2 h-4 w-4 flex-shrink-0"
|
||||
className="mr-2 h-4 w-4 shrink-0"
|
||||
/>
|
||||
<span className="truncate">{displayName}</span>
|
||||
</div>
|
||||
@@ -527,7 +528,7 @@ const ConnectorTree: React.FC<ConnectorTreeProps> = ({
|
||||
<div ref={menuRef} className="relative">
|
||||
<button
|
||||
onClick={(e) => handleMenuClick(e, itemId)}
|
||||
className="inline-flex h-[35px] w-[24px] shrink-0 items-center justify-center rounded-md font-medium transition-colors hover:bg-[#EBEBEB] dark:hover:bg-[#26272E]"
|
||||
className="dark:hover:bg-muted inline-flex h-[35px] w-6 shrink-0 items-center justify-center rounded-md font-medium transition-colors hover:bg-[#EBEBEB]"
|
||||
aria-label={t('settings.sources.menuAlt')}
|
||||
>
|
||||
<img
|
||||
@@ -625,14 +626,14 @@ const ConnectorTree: React.FC<ConnectorTreeProps> = ({
|
||||
}
|
||||
}}
|
||||
placeholder={t('settings.sources.searchFiles')}
|
||||
className={`h-[38px] w-full border border-[#D1D9E0] px-4 py-2 dark:border-[#6A6A6A] ${searchQuery ? 'rounded-t-[24px]' : 'rounded-[24px]'} bg-transparent focus:outline-none dark:text-[#E0E0E0]`}
|
||||
className={`border-border dark:border-border h-[38px] w-full border px-4 py-2 ${searchQuery ? 'rounded-t-[24px]' : 'rounded-[24px]'} bg-transparent focus:outline-none`}
|
||||
/>
|
||||
|
||||
{searchQuery && (
|
||||
<div className="absolute top-full right-0 left-0 z-10 max-h-[calc(100vh-200px)] w-full overflow-hidden rounded-b-[12px] border border-t-0 border-[#D1D9E0] bg-white shadow-lg transition-all duration-200 dark:border-[#6A6A6A] dark:bg-[#1F2023]">
|
||||
<div className="border-border bg-card dark:border-border dark:bg-card absolute top-full right-0 left-0 z-10 max-h-[calc(100vh-200px)] w-full overflow-hidden rounded-b-2xl border border-t-0 shadow-lg transition-all duration-200">
|
||||
<div className="max-h-[calc(100vh-200px)] overflow-x-hidden overflow-y-auto overscroll-contain">
|
||||
{searchResults.length === 0 ? (
|
||||
<div className="py-2 text-center text-sm text-gray-500 dark:text-gray-400">
|
||||
<div className="text-muted-foreground py-2 text-center text-sm">
|
||||
{t('settings.sources.noResults')}
|
||||
</div>
|
||||
) : (
|
||||
@@ -641,9 +642,9 @@ const ConnectorTree: React.FC<ConnectorTreeProps> = ({
|
||||
key={index}
|
||||
onClick={() => handleSearchSelect(result)}
|
||||
title={result.path}
|
||||
className={`flex min-w-0 cursor-pointer items-center px-3 py-2 hover:bg-[#ECEEEF] dark:hover:bg-[#27282D] ${
|
||||
className={`hover:bg-muted dark:hover:bg-muted flex min-w-0 cursor-pointer items-center px-3 py-2 ${
|
||||
index !== searchResults.length - 1
|
||||
? 'border-b border-[#D1D9E0] dark:border-[#6A6A6A]'
|
||||
? 'border-border dark:border-border border-b'
|
||||
: ''
|
||||
}`}
|
||||
>
|
||||
@@ -654,9 +655,9 @@ const ConnectorTree: React.FC<ConnectorTreeProps> = ({
|
||||
? t('settings.sources.fileAlt')
|
||||
: t('settings.sources.folderAlt')
|
||||
}
|
||||
className="mr-2 h-4 w-4 flex-shrink-0"
|
||||
className="mr-2 h-4 w-4 shrink-0"
|
||||
/>
|
||||
<span className="flex-1 truncate text-sm dark:text-[#E0E0E0]">
|
||||
<span className="flex-1 truncate text-sm">
|
||||
{result.name}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -127,7 +127,7 @@ export default function ContextMenu({
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<div
|
||||
className="bg-lotion dark:bg-charleston-green-2 flex flex-col rounded-xl text-sm shadow-xl"
|
||||
className="bg-background dark:bg-card flex flex-col rounded-xl text-sm shadow-xl"
|
||||
style={{ minWidth: '144px' }}
|
||||
>
|
||||
{options.map((option, index) => (
|
||||
@@ -141,8 +141,8 @@ export default function ContextMenu({
|
||||
}}
|
||||
className={`flex items-center justify-start gap-4 p-3 transition-colors duration-200 ease-in-out ${index === 0 ? 'rounded-t-xl' : ''} ${index === options.length - 1 ? 'rounded-b-xl' : ''} ${
|
||||
option.variant === 'danger'
|
||||
? 'text-rosso-corsa hover:bg-bright-gray dark:text-red-2000 dark:hover:bg-charcoal-grey/20'
|
||||
: 'text-eerie-black hover:bg-bright-gray dark:text-bright-gray dark:hover:bg-charcoal-grey/20'
|
||||
? 'text-destructive hover:bg-muted'
|
||||
: 'text-foreground hover:bg-muted'
|
||||
} `}
|
||||
>
|
||||
{option.icon && (
|
||||
|
||||
@@ -39,8 +39,7 @@ export default function CopyButton({
|
||||
'flex items-center justify-center rounded-full transition-colors duration-150 ease-in-out',
|
||||
padding,
|
||||
{
|
||||
[`bg-[#FFFFFF}] dark:bg-transparent hover:bg-[#EEEEEE] dark:hover:bg-purple-taupe`]:
|
||||
!isCopied,
|
||||
[`bg-transparent hover:bg-muted`]: !isCopied,
|
||||
'bg-green-100 dark:bg-green-900 hover:bg-green-100 dark:hover:bg-green-900':
|
||||
isCopied,
|
||||
},
|
||||
|
||||
@@ -61,12 +61,12 @@ const Pagination: React.FC<PaginationProps> = ({
|
||||
<div className="relative">
|
||||
<button
|
||||
onClick={toggleDropdown}
|
||||
className="dark:bg-dark-charcoal dark:text-light-gray rounded border px-3 py-1 hover:bg-gray-200 dark:hover:bg-neutral-700"
|
||||
className="dark:bg-card dark:text-foreground hover:bg-accent dark:hover:bg-accent rounded border px-3 py-1"
|
||||
>
|
||||
{rowsPerPage}
|
||||
</button>
|
||||
<div
|
||||
className={`ring-opacity-5 dark:bg-dark-charcoal absolute right-0 z-50 mt-1 w-28 transform bg-white shadow-lg ring-1 ring-black transition-all duration-200 ease-in-out ${
|
||||
className={`ring-opacity-5 dark:bg-card bg-card absolute right-0 z-50 mt-1 w-28 transform shadow-lg ring-1 ring-black transition-all duration-200 ease-in-out ${
|
||||
isDropdownOpen
|
||||
? 'block scale-100 opacity-100'
|
||||
: 'hidden scale-95 opacity-0'
|
||||
@@ -76,10 +76,10 @@ const Pagination: React.FC<PaginationProps> = ({
|
||||
<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={`hover:bg-accent dark:hover:bg-accent cursor-pointer px-4 py-2 text-xs ${
|
||||
rowsPerPage === option
|
||||
? 'dark:text-light-gray bg-gray-100 dark:bg-neutral-700'
|
||||
: 'dark:bg-dark-charcoal dark:text-light-gray bg-white'
|
||||
? 'dark:text-foreground bg-gray-100 dark:bg-neutral-700'
|
||||
: 'bg-card dark:text-foreground'
|
||||
}`}
|
||||
>
|
||||
{option}
|
||||
|
||||
@@ -1,163 +1,227 @@
|
||||
import React from 'react';
|
||||
import { Check, ChevronDown, Pencil, Search, Trash2 } from 'lucide-react';
|
||||
import React, { useEffect, useMemo, useRef, useState } from 'react';
|
||||
|
||||
import Arrow2 from '../assets/dropdown-arrow.svg';
|
||||
import Edit from '../assets/edit.svg';
|
||||
import Trash from '../assets/trash.svg';
|
||||
import { DropdownOption, DropdownProps } from './types/Dropdown.types';
|
||||
type OptionBase = { id?: string; type?: string };
|
||||
type NameIdOption = { name: string; id: string } & OptionBase;
|
||||
type LabelValueOption = { label: string; value: string } & OptionBase;
|
||||
type ValueDescriptionOption = {
|
||||
value: number;
|
||||
description: string;
|
||||
} & OptionBase;
|
||||
|
||||
export type DropdownOption =
|
||||
| string
|
||||
| NameIdOption
|
||||
| LabelValueOption
|
||||
| ValueDescriptionOption;
|
||||
|
||||
export type { NameIdOption, LabelValueOption, ValueDescriptionOption };
|
||||
|
||||
function getOptionText(option: DropdownOption): string {
|
||||
if (typeof option === 'string') return option;
|
||||
if ('name' in option) return option.name;
|
||||
if ('label' in option) return option.label;
|
||||
if ('description' in option)
|
||||
return option.value < 1e9
|
||||
? `${option.value} (${option.description})`
|
||||
: option.description;
|
||||
return '';
|
||||
}
|
||||
|
||||
function optionMatches(
|
||||
option: DropdownOption,
|
||||
selected: DropdownOption | null,
|
||||
): boolean {
|
||||
if (!selected) return false;
|
||||
if (typeof selected === 'string' && typeof option === 'string')
|
||||
return selected === option;
|
||||
if (typeof selected === 'string') return getOptionText(option) === selected;
|
||||
if (typeof option === 'string') return getOptionText(selected) === option;
|
||||
const a = option as Record<string, unknown>;
|
||||
const b = selected as Record<string, unknown>;
|
||||
if ('name' in a && 'name' in b) return a.name === b.name;
|
||||
if ('label' in a && 'label' in b) return a.label === b.label;
|
||||
if ('value' in a && 'value' in b) return a.value === b.value;
|
||||
return false;
|
||||
}
|
||||
|
||||
export interface DropdownProps<T extends DropdownOption = DropdownOption> {
|
||||
options: T[];
|
||||
selectedValue: DropdownOption | null;
|
||||
onSelect: (value: T) => void;
|
||||
size?: string;
|
||||
rounded?: 'xl' | '3xl';
|
||||
searchable?: boolean;
|
||||
placeholder?: string;
|
||||
contentSize?: string;
|
||||
showEdit?: boolean;
|
||||
onEdit?: (value: NameIdOption) => void;
|
||||
showDelete?: boolean | ((option: T) => boolean);
|
||||
onDelete?: (id: string) => void;
|
||||
}
|
||||
|
||||
function Dropdown<T extends DropdownOption>({
|
||||
options,
|
||||
selectedValue,
|
||||
onSelect,
|
||||
size = 'w-32',
|
||||
rounded = 'xl',
|
||||
buttonClassName = 'border-silver bg-white dark:bg-transparent dark:border-dim-gray',
|
||||
optionsClassName = 'border-silver bg-white dark:border-dim-gray dark:bg-dark-charcoal',
|
||||
border = 'border-2',
|
||||
size = 'w-full',
|
||||
rounded = '3xl',
|
||||
searchable = false,
|
||||
placeholder = 'Select...',
|
||||
contentSize = 'text-sm',
|
||||
showEdit,
|
||||
onEdit,
|
||||
showDelete,
|
||||
onDelete,
|
||||
placeholder,
|
||||
placeholderClassName = 'text-gray-500 dark:text-gray-400',
|
||||
contentSize = 'text-base',
|
||||
}: DropdownProps<T>) {
|
||||
const dropdownRef = React.useRef<HTMLDivElement>(null);
|
||||
const [isOpen, setIsOpen] = React.useState(false);
|
||||
const borderRadius = rounded === 'xl' ? 'rounded-xl' : 'rounded-3xl';
|
||||
const borderTopRadius = rounded === 'xl' ? 'rounded-t-xl' : 'rounded-t-3xl';
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
const searchRef = useRef<HTMLInputElement>(null);
|
||||
const [open, setOpen] = useState(false);
|
||||
const [query, setQuery] = useState('');
|
||||
|
||||
const handleClickOutside = (event: MouseEvent) => {
|
||||
if (
|
||||
dropdownRef.current &&
|
||||
!dropdownRef.current.contains(event.target as Node)
|
||||
) {
|
||||
setIsOpen(false);
|
||||
}
|
||||
};
|
||||
const radius = rounded === '3xl' ? 'rounded-3xl' : 'rounded-xl';
|
||||
const radiusTop = rounded === '3xl' ? 'rounded-t-3xl' : 'rounded-t-xl';
|
||||
const radiusBottom = rounded === '3xl' ? 'rounded-b-3xl' : 'rounded-b-xl';
|
||||
|
||||
React.useEffect(() => {
|
||||
document.addEventListener('mousedown', handleClickOutside);
|
||||
return () => {
|
||||
document.removeEventListener('mousedown', handleClickOutside);
|
||||
useEffect(() => {
|
||||
const handler = (e: MouseEvent) => {
|
||||
if (ref.current && !ref.current.contains(e.target as Node)) {
|
||||
setOpen(false);
|
||||
setQuery('');
|
||||
}
|
||||
};
|
||||
document.addEventListener('mousedown', handler);
|
||||
return () => document.removeEventListener('mousedown', handler);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (open && searchable && searchRef.current) searchRef.current.focus();
|
||||
}, [open, searchable]);
|
||||
|
||||
const filtered = useMemo(() => {
|
||||
if (!searchable || !query.trim()) return options;
|
||||
const q = query.toLowerCase();
|
||||
return options.filter((o) => getOptionText(o).toLowerCase().includes(q));
|
||||
}, [options, query, searchable]);
|
||||
|
||||
const displayValue = selectedValue ? getOptionText(selectedValue) : null;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={[
|
||||
typeof selectedValue === 'string'
|
||||
? 'relative'
|
||||
: 'relative align-middle',
|
||||
size,
|
||||
].join(' ')}
|
||||
ref={dropdownRef}
|
||||
>
|
||||
<div className={`relative ${size}`} ref={ref}>
|
||||
<button
|
||||
onClick={() => setIsOpen(!isOpen)}
|
||||
className={`flex w-full cursor-pointer items-center justify-between ${border} ${buttonClassName} px-5 py-3 ${
|
||||
isOpen ? `${borderTopRadius}` : `${borderRadius}`
|
||||
}`}
|
||||
type="button"
|
||||
onClick={() => setOpen((v) => !v)}
|
||||
className={`border-border bg-card text-foreground flex w-full cursor-pointer items-center justify-between border px-5 py-3 ${open ? radiusTop : radius}`}
|
||||
>
|
||||
{typeof selectedValue === 'string' ? (
|
||||
<span className={`dark:text-bright-gray truncate ${contentSize}`}>
|
||||
{selectedValue}
|
||||
</span>
|
||||
) : (
|
||||
<span
|
||||
className={`truncate ${selectedValue && `dark:text-bright-gray`} ${
|
||||
!selectedValue && ` ${placeholderClassName}`
|
||||
} ${contentSize}`}
|
||||
>
|
||||
{selectedValue && 'label' in selectedValue
|
||||
? selectedValue.label
|
||||
: selectedValue && 'description' in selectedValue
|
||||
? `${
|
||||
selectedValue.value < 1e9
|
||||
? selectedValue.value + ` (${selectedValue.description})`
|
||||
: selectedValue.description
|
||||
}`
|
||||
: placeholder
|
||||
? placeholder
|
||||
: 'From URL'}
|
||||
</span>
|
||||
)}
|
||||
<img
|
||||
src={Arrow2}
|
||||
alt="arrow"
|
||||
className={`transform ${
|
||||
isOpen ? 'rotate-180' : 'rotate-0'
|
||||
} h-3 w-3 transition-transform`}
|
||||
<span
|
||||
className={`truncate ${contentSize} ${displayValue ? '' : 'text-muted-foreground'}`}
|
||||
>
|
||||
{displayValue ?? placeholder}
|
||||
</span>
|
||||
<ChevronDown
|
||||
className={`text-muted-foreground ml-2 h-4 w-4 shrink-0 transition-transform ${open ? 'rotate-180' : ''}`}
|
||||
/>
|
||||
</button>
|
||||
{isOpen && (
|
||||
|
||||
{open && (
|
||||
<div
|
||||
className={`absolute right-0 left-0 z-20 -mt-1 max-h-40 overflow-y-auto rounded-b-xl ${border} ${optionsClassName} shadow-lg`}
|
||||
className={`border-border bg-card absolute inset-x-0 z-20 -mt-px overflow-hidden border border-t-0 shadow-lg ${radiusBottom}`}
|
||||
>
|
||||
{options.map((option: any, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="hover:eerie-black flex cursor-pointer items-center justify-between hover:bg-gray-100 dark:hover:bg-[#545561]"
|
||||
>
|
||||
<span
|
||||
onClick={() => {
|
||||
onSelect(option);
|
||||
setIsOpen(false);
|
||||
}}
|
||||
className={`dark:text-light-gray ml-5 flex-1 overflow-hidden py-3 text-ellipsis whitespace-nowrap ${contentSize}`}
|
||||
>
|
||||
{typeof option === 'string'
|
||||
? option
|
||||
: option.name
|
||||
? option.name
|
||||
: option.label
|
||||
? option.label
|
||||
: `${
|
||||
option.value < 1e9
|
||||
? option.value + ` (${option.description})`
|
||||
: option.description
|
||||
}`}
|
||||
</span>
|
||||
{showEdit && onEdit && option.type !== 'public' && (
|
||||
<img
|
||||
src={Edit}
|
||||
alt="Edit"
|
||||
className="mr-4 h-4 w-4 cursor-pointer hover:opacity-50"
|
||||
onClick={() => {
|
||||
onEdit({
|
||||
id: option.id,
|
||||
name: option.name,
|
||||
type: option.type,
|
||||
});
|
||||
setIsOpen(false);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{showDelete && onDelete && (
|
||||
<button
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
onDelete?.(typeof option === 'string' ? option : option.id);
|
||||
}}
|
||||
className={`${
|
||||
typeof showDelete === 'function' && !showDelete(option)
|
||||
? 'hidden'
|
||||
: ''
|
||||
} mr-2 h-4 w-4 cursor-pointer hover:opacity-50`}
|
||||
>
|
||||
<img
|
||||
src={Trash}
|
||||
alt="Delete"
|
||||
className={`mr-2 h-4 w-4 cursor-pointer hover:opacity-50 ${
|
||||
option.type === 'public'
|
||||
? 'cursor-not-allowed opacity-50'
|
||||
: ''
|
||||
}`}
|
||||
/>
|
||||
</button>
|
||||
)}
|
||||
{searchable && (
|
||||
<div className="flex items-center px-3 py-2">
|
||||
<Search className="text-muted-foreground mr-2 h-4 w-4 shrink-0" />
|
||||
<input
|
||||
ref={searchRef}
|
||||
type="text"
|
||||
value={query}
|
||||
onChange={(e) => setQuery(e.target.value)}
|
||||
placeholder="Search..."
|
||||
className="text-foreground placeholder:text-muted-foreground w-full bg-transparent text-sm focus:outline-none"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
)}
|
||||
|
||||
<div className="scrollbar-thin border-border max-h-48 overflow-y-auto border-t">
|
||||
{filtered.length === 0 ? (
|
||||
<div className="text-muted-foreground px-4 py-3 text-center text-sm">
|
||||
No results found
|
||||
</div>
|
||||
) : (
|
||||
filtered.map((option, i) => {
|
||||
const active = optionMatches(option, selectedValue);
|
||||
const optObj =
|
||||
typeof option !== 'string'
|
||||
? (option as Record<string, unknown>)
|
||||
: null;
|
||||
|
||||
return (
|
||||
<div
|
||||
key={i}
|
||||
className={`hover:bg-accent flex cursor-pointer items-center justify-between ${active ? 'bg-accent font-medium' : ''}`}
|
||||
>
|
||||
<span
|
||||
onClick={() => {
|
||||
onSelect(option);
|
||||
setOpen(false);
|
||||
setQuery('');
|
||||
}}
|
||||
className={`text-foreground flex-1 truncate px-4 py-2.5 ${contentSize}`}
|
||||
>
|
||||
{getOptionText(option)}
|
||||
</span>
|
||||
|
||||
{active && !showEdit && !showDelete && (
|
||||
<Check className="text-primary mr-3 h-4 w-4 shrink-0" />
|
||||
)}
|
||||
|
||||
{showEdit &&
|
||||
onEdit &&
|
||||
optObj &&
|
||||
optObj.type !== 'public' && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
onEdit({
|
||||
id: optObj.id as string,
|
||||
name: optObj.name as string,
|
||||
type: optObj.type as string,
|
||||
});
|
||||
setOpen(false);
|
||||
setQuery('');
|
||||
}}
|
||||
className="hover:bg-accent mr-1 rounded p-1"
|
||||
>
|
||||
<Pencil className="text-muted-foreground h-3.5 w-3.5" />
|
||||
</button>
|
||||
)}
|
||||
|
||||
{showDelete && onDelete && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
onDelete(
|
||||
typeof option === 'string'
|
||||
? option
|
||||
: ((optObj?.id as string) ?? ''),
|
||||
);
|
||||
}}
|
||||
className={`hover:bg-accent mr-1 rounded p-1 ${
|
||||
typeof showDelete === 'function' &&
|
||||
!showDelete(option)
|
||||
? 'hidden'
|
||||
: ''
|
||||
} ${optObj?.type === 'public' ? 'pointer-events-none opacity-30' : ''}`}
|
||||
>
|
||||
<Trash2 className="text-muted-foreground h-3.5 w-3.5" />
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -88,7 +88,7 @@ export default function DropdownMenu({
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<div
|
||||
className={`ring-opacity-5 dark:bg-dark-charcoal w-28 transform rounded-md bg-white shadow-lg ring-1 ring-black transition-all duration-200 ease-in-out ${className}`}
|
||||
className={`border-border bg-card w-28 transform rounded-md border shadow-lg transition-all duration-200 ease-in-out ${className}`}
|
||||
>
|
||||
<div
|
||||
role="menu"
|
||||
@@ -99,10 +99,8 @@ export default function DropdownMenu({
|
||||
{options.map((option, idx) => (
|
||||
<div
|
||||
id={`option-${idx}`}
|
||||
className={`dark:text-light-gray dark:hover:bg-purple-taupe cursor-pointer px-4 py-2 text-xs hover:bg-gray-100 ${
|
||||
selectedOption.value === option.value
|
||||
? 'dark:bg-purple-taupe bg-gray-100'
|
||||
: 'dark:bg-dark-charcoal bg-white'
|
||||
className={`dark:text-foreground hover:bg-muted cursor-pointer px-4 py-2 text-xs ${
|
||||
selectedOption.value === option.value ? 'bg-muted' : 'bg-card'
|
||||
}`}
|
||||
role="menuitem"
|
||||
key={option.value}
|
||||
|
||||
@@ -80,15 +80,15 @@ export default function DropdownModel() {
|
||||
return (
|
||||
<div ref={dropdownRef}>
|
||||
<div
|
||||
className={`bg-gray-1000 dark:bg-dark-charcoal mx-auto flex w-full cursor-pointer justify-between p-1 dark:text-white ${isOpen ? 'rounded-t-3xl' : 'rounded-3xl'}`}
|
||||
className={`text-foreground bg-muted dark:bg-card mx-auto flex w-full cursor-pointer items-center justify-between px-3 py-4 ${isOpen ? 'rounded-t-4xl' : 'rounded-4xl'}`}
|
||||
onClick={() => setIsOpen(!isOpen)}
|
||||
>
|
||||
{selectedModel?.display_name ? (
|
||||
<p className="mx-4 my-3 truncate overflow-hidden whitespace-nowrap">
|
||||
<p className="ml-3 truncate overflow-hidden whitespace-nowrap">
|
||||
{selectedModel.display_name}
|
||||
</p>
|
||||
) : (
|
||||
<p className="mx-4 my-3 truncate overflow-hidden whitespace-nowrap">
|
||||
<p className="text-muted-foreground truncate overflow-hidden whitespace-nowrap">
|
||||
Select Model
|
||||
</p>
|
||||
)}
|
||||
@@ -101,7 +101,7 @@ export default function DropdownModel() {
|
||||
/>
|
||||
</div>
|
||||
{isOpen && (
|
||||
<div className="no-scrollbar dark:bg-dark-charcoal absolute right-0 left-0 z-20 -mt-1 max-h-52 w-full overflow-y-auto rounded-b-3xl bg-white shadow-md">
|
||||
<div className="no-scrollbar bg-muted dark:bg-card absolute right-0 left-0 z-20 -mt-1 max-h-52 w-full overflow-y-auto rounded-b-3xl shadow-md">
|
||||
{availableModels && (availableModels?.length ?? 0) > 0 ? (
|
||||
availableModels.map((model: Model) => (
|
||||
<div
|
||||
@@ -110,7 +110,7 @@ export default function DropdownModel() {
|
||||
dispatch(setSelectedModel(model));
|
||||
setIsOpen(false);
|
||||
}}
|
||||
className={`border-gray-3000/75 dark:border-purple-taupe/50 hover:bg-gray-3000/75 dark:hover:bg-purple-taupe flex h-10 w-full cursor-pointer items-center justify-between border-t`}
|
||||
className={`border-border/30 hover:bg-accent flex h-10 w-full cursor-pointer items-center justify-between border-t`}
|
||||
>
|
||||
<div className="flex w-full items-center justify-between">
|
||||
<p className="flex-1 truncate py-3 pr-2 pl-5">
|
||||
@@ -127,8 +127,10 @@ export default function DropdownModel() {
|
||||
</div>
|
||||
))
|
||||
) : (
|
||||
<div className="h-10 w-full border-x-2 border-b-2">
|
||||
<p className="ml-5 py-3 text-gray-500">No models available</p>
|
||||
<div className="border-border/30 flex h-10 w-full items-center border-t">
|
||||
<p className="text-muted-foreground pl-5 text-sm">
|
||||
No models available
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -93,9 +93,7 @@ export const FilePicker: React.FC<CloudFilePickerProps> = ({
|
||||
const [isConnected, setIsConnected] = useState(false);
|
||||
const [userEmail, setUserEmail] = useState<string>('');
|
||||
const [allowsSharedContent, setAllowsSharedContent] = useState(false);
|
||||
const [activeTab, setActiveTab] = useState<'my_files' | 'shared'>(
|
||||
'my_files',
|
||||
);
|
||||
const [activeTab, setActiveTab] = useState<'my_files' | 'shared'>('my_files');
|
||||
|
||||
const scrollContainerRef = useRef<HTMLDivElement>(null);
|
||||
const searchTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
||||
@@ -214,9 +212,7 @@ export const FilePicker: React.FC<CloudFilePickerProps> = ({
|
||||
setIsConnected(true);
|
||||
setAuthError('');
|
||||
if (provider === 'share_point') {
|
||||
setAllowsSharedContent(
|
||||
validateData.allows_shared_content ?? false,
|
||||
);
|
||||
setAllowsSharedContent(validateData.allows_shared_content ?? false);
|
||||
}
|
||||
|
||||
setFiles([]);
|
||||
@@ -369,9 +365,7 @@ export const FilePicker: React.FC<CloudFilePickerProps> = ({
|
||||
{
|
||||
id: null,
|
||||
name:
|
||||
tab === 'shared'
|
||||
? 'Shared'
|
||||
: getProviderConfig(provider).rootName,
|
||||
tab === 'shared' ? 'Shared' : getProviderConfig(provider).rootName,
|
||||
},
|
||||
]);
|
||||
const sessionToken = getSessionToken(provider);
|
||||
@@ -380,8 +374,6 @@ export const FilePicker: React.FC<CloudFilePickerProps> = ({
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
const handleFileSelect = (fileId: string, isFolder: boolean) => {
|
||||
if (isFolder) {
|
||||
const newSelectedFolders = selectedFolders.includes(fileId)
|
||||
@@ -459,10 +451,10 @@ export const FilePicker: React.FC<CloudFilePickerProps> = ({
|
||||
/>
|
||||
|
||||
{isConnected && (
|
||||
<div className="mt-3 overflow-hidden rounded-lg border border-[#D7D7D7] dark:border-[#6A6A6A]">
|
||||
<div className="rounded-t-lg border-[#EEE6FF78] dark:border-[#6A6A6A]">
|
||||
<div className="border-border dark:border-border mt-3 overflow-hidden rounded-lg border">
|
||||
<div className="border-border dark:border-border rounded-t-lg">
|
||||
{provider === 'share_point' && allowsSharedContent && (
|
||||
<div className="flex border-b border-[#D7D7D7] dark:border-[#6A6A6A]">
|
||||
<div className="border-border dark:border-border flex border-b">
|
||||
<button
|
||||
onClick={() => handleTabChange('my_files')}
|
||||
className={`px-4 py-2 text-sm font-medium ${
|
||||
@@ -485,7 +477,7 @@ export const FilePicker: React.FC<CloudFilePickerProps> = ({
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
<div className="rounded-t-lg bg-[#EEE6FF78] px-4 pt-4 dark:bg-[#2A262E]">
|
||||
<div className="dark:bg-muted rounded-t-lg bg-[#EEE6FF78] px-4 pt-4">
|
||||
<div className="mb-2 flex items-center gap-1">
|
||||
{folderPath.map((path, index) => (
|
||||
<div
|
||||
@@ -516,7 +508,7 @@ export const FilePicker: React.FC<CloudFilePickerProps> = ({
|
||||
onChange={(e) => handleSearchChange(e.target.value)}
|
||||
colorVariant="silver"
|
||||
borderVariant="thin"
|
||||
labelBgClassName="bg-[#EEE6FF78] dark:bg-[#2A262E]"
|
||||
labelBgClassName="bg-[#EEE6FF78] dark:bg-muted"
|
||||
leftIcon={
|
||||
<img src={SearchIcon} alt="Search" width={16} height={16} />
|
||||
}
|
||||
@@ -531,7 +523,7 @@ export const FilePicker: React.FC<CloudFilePickerProps> = ({
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="h-72 border-t border-[#D7D7D7] dark:border-[#6A6A6A]">
|
||||
<div className="border-border dark:border-border h-72 border-t">
|
||||
<TableContainer
|
||||
ref={scrollContainerRef}
|
||||
height="288px"
|
||||
@@ -586,7 +578,7 @@ export const FilePicker: React.FC<CloudFilePickerProps> = ({
|
||||
>
|
||||
<TableCell width="40px" align="center">
|
||||
<div
|
||||
className="mx-auto flex h-5 w-5 shrink-0 cursor-pointer items-center justify-center border border-[#EEE6FF78] p-[0.5px] text-sm dark:border-[#6A6A6A]"
|
||||
className="border-border dark:border-border mx-auto flex h-5 w-5 shrink-0 cursor-pointer items-center justify-center border p-[0.5px] text-sm"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
handleFileSelect(file.id, isFolder(file));
|
||||
@@ -615,7 +607,9 @@ export const FilePicker: React.FC<CloudFilePickerProps> = ({
|
||||
className="h-6 w-6"
|
||||
/>
|
||||
</div>
|
||||
<span className="truncate">{file.name}</span>
|
||||
<span className="truncate">
|
||||
{file.name}
|
||||
</span>
|
||||
</div>
|
||||
</TableCell>
|
||||
<TableCell className="text-xs">
|
||||
@@ -626,7 +620,8 @@ export const FilePicker: React.FC<CloudFilePickerProps> = ({
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
{isLoading && files.length > 0 &&
|
||||
{isLoading &&
|
||||
files.length > 0 &&
|
||||
Array.from({ length: 3 }).map((_, i) => (
|
||||
<TableRow key={`load-more-skeleton-${i}`}>
|
||||
<TableCell width="40px" align="center">
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
const FilesSectionSkeleton = () => (
|
||||
<div className="rounded-lg border border-[#EEE6FF78] dark:border-[#6A6A6A]">
|
||||
<div className="border-border dark:border-border rounded-lg border">
|
||||
<div className="p-4">
|
||||
<div className="mb-4 flex items-center justify-between">
|
||||
<div className="h-5 w-24 animate-pulse rounded bg-gray-200 dark:bg-gray-700"></div>
|
||||
|
||||
@@ -450,7 +450,7 @@ const FileTree: React.FC<FileTreeProps> = ({
|
||||
{/* Left side with path navigation */}
|
||||
<div className="flex w-full items-center sm:w-auto">
|
||||
<button
|
||||
className="mr-3 flex h-[29px] w-[29px] items-center justify-center rounded-full border p-2 text-sm font-medium text-gray-400 dark:border-0 dark:bg-[#28292D] dark:text-gray-500 dark:hover:bg-[#2E2F34]"
|
||||
className="mr-3 flex h-[29px] w-[29px] items-center justify-center rounded-full border p-2 text-sm font-medium text-gray-400 dark:border-0 dark:text-gray-500"
|
||||
onClick={handleBackNavigation}
|
||||
>
|
||||
<img src={ArrowLeft} alt="left-arrow" className="h-3 w-3" />
|
||||
@@ -503,7 +503,7 @@ const FileTree: React.FC<FileTreeProps> = ({
|
||||
{!processingRef.current && (
|
||||
<button
|
||||
onClick={handleAddFile}
|
||||
className="bg-purple-30 hover:bg-violets-are-blue flex h-[38px] min-w-[108px] items-center justify-center rounded-full px-4 text-[14px] font-medium whitespace-nowrap text-white"
|
||||
className="bg-primary hover:bg-primary/90 flex h-[38px] min-w-[108px] items-center justify-center rounded-full px-4 text-[14px] font-medium whitespace-nowrap text-white"
|
||||
title={t('settings.sources.addFile')}
|
||||
>
|
||||
{t('settings.sources.addFile')}
|
||||
@@ -599,7 +599,7 @@ const FileTree: React.FC<FileTreeProps> = ({
|
||||
<div ref={menuRef} className="relative">
|
||||
<button
|
||||
onClick={(e) => handleMenuClick(e, itemId)}
|
||||
className="inline-flex h-[35px] w-[24px] shrink-0 items-center justify-center rounded-md font-medium transition-colors hover:bg-[#EBEBEB] dark:hover:bg-[#26272E]"
|
||||
className="dark:hover:bg-muted inline-flex h-[35px] w-[24px] shrink-0 items-center justify-center rounded-md font-medium transition-colors hover:bg-[#EBEBEB]"
|
||||
aria-label={t('settings.sources.menuAlt')}
|
||||
>
|
||||
<img
|
||||
@@ -656,7 +656,7 @@ const FileTree: React.FC<FileTreeProps> = ({
|
||||
<div ref={menuRef} className="relative">
|
||||
<button
|
||||
onClick={(e) => handleMenuClick(e, itemId)}
|
||||
className="inline-flex h-[35px] w-[24px] shrink-0 items-center justify-center rounded-md font-medium transition-colors hover:bg-[#EBEBEB] dark:hover:bg-[#26272E]"
|
||||
className="dark:hover:bg-muted inline-flex h-[35px] w-[24px] shrink-0 items-center justify-center rounded-md font-medium transition-colors hover:bg-[#EBEBEB]"
|
||||
aria-label={t('settings.sources.menuAlt')}
|
||||
>
|
||||
<img
|
||||
@@ -754,11 +754,11 @@ const FileTree: React.FC<FileTreeProps> = ({
|
||||
}
|
||||
}}
|
||||
placeholder={t('settings.sources.searchFiles')}
|
||||
className={`h-[38px] w-full border border-[#D1D9E0] px-4 py-2 dark:border-[#6A6A6A] ${searchQuery ? 'rounded-t-[24px]' : 'rounded-[24px]'} bg-transparent focus:outline-none dark:text-[#E0E0E0]`}
|
||||
className={`border-border dark:border-border h-[38px] w-full border px-4 py-2 ${searchQuery ? 'rounded-t-[24px]' : 'rounded-[24px]'} bg-transparent focus:outline-none`}
|
||||
/>
|
||||
|
||||
{searchQuery && (
|
||||
<div className="absolute top-full right-0 left-0 z-10 max-h-[calc(100vh-200px)] w-full overflow-hidden rounded-b-[12px] border border-t-0 border-[#D1D9E0] bg-white shadow-lg transition-all duration-200 dark:border-[#6A6A6A] dark:bg-[#1F2023]">
|
||||
<div className="border-border bg-card dark:border-border dark:bg-card absolute top-full right-0 left-0 z-10 max-h-[calc(100vh-200px)] w-full overflow-hidden rounded-b-[12px] border border-t-0 shadow-lg transition-all duration-200">
|
||||
<div className="max-h-[calc(100vh-200px)] overflow-x-hidden overflow-y-auto overscroll-contain">
|
||||
{searchResults.length === 0 ? (
|
||||
<div className="py-2 text-center text-sm text-gray-500 dark:text-gray-400">
|
||||
@@ -770,9 +770,9 @@ const FileTree: React.FC<FileTreeProps> = ({
|
||||
key={index}
|
||||
onClick={() => handleSearchSelect(result)}
|
||||
title={result.path}
|
||||
className={`flex min-w-0 cursor-pointer items-center px-3 py-2 hover:bg-[#ECEEEF] dark:hover:bg-[#27282D] ${
|
||||
className={`hover:bg-muted dark:hover:bg-muted flex min-w-0 cursor-pointer items-center px-3 py-2 ${
|
||||
index !== searchResults.length - 1
|
||||
? 'border-b border-[#D1D9E0] dark:border-[#6A6A6A]'
|
||||
? 'border-border dark:border-border border-b'
|
||||
: ''
|
||||
}`}
|
||||
>
|
||||
@@ -785,7 +785,7 @@ const FileTree: React.FC<FileTreeProps> = ({
|
||||
}
|
||||
className="mr-2 h-4 w-4 flex-shrink-0"
|
||||
/>
|
||||
<span className="flex-1 truncate text-sm dark:text-[#E0E0E0]">
|
||||
<span className="flex-1 truncate text-sm">
|
||||
{result.name}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -41,7 +41,7 @@ export const FileUpload = ({
|
||||
showPreview = false,
|
||||
previewSize = 80,
|
||||
children,
|
||||
className = 'border-2 border-dashed rounded-3xl p-6 text-center cursor-pointer transition-colors border-silver dark:border-[#7E7E7E]',
|
||||
className = 'border-2 border-dashed rounded-3xl p-6 text-center cursor-pointer transition-colors border-border dark:border-border',
|
||||
activeClassName = 'border-blue-500 bg-blue-50',
|
||||
acceptClassName = 'border-green-500 dark:border-green-500 bg-green-50 dark:bg-green-50/10',
|
||||
rejectClassName = 'border-red-500 bg-red-50 dark:bg-red-500/10 dark:border-red-500',
|
||||
@@ -133,7 +133,7 @@ export const FileUpload = ({
|
||||
});
|
||||
|
||||
const currentClassName = twMerge(
|
||||
'border-2 border-dashed rounded-3xl p-8 text-center cursor-pointer transition-colors border-silver dark:border-[#7E7E7E]',
|
||||
'border-2 border-dashed rounded-3xl p-8 text-center cursor-pointer transition-colors border-border dark:border-border',
|
||||
className,
|
||||
isDragActive && activeClassName,
|
||||
isDragAccept && acceptClassName,
|
||||
|
||||
@@ -262,7 +262,7 @@ const GoogleDrivePicker: React.FC<GoogleDrivePickerProps> = ({
|
||||
/>
|
||||
|
||||
{isConnected && (
|
||||
<div className="rounded-lg border border-[#EEE6FF78] dark:border-[#6A6A6A]">
|
||||
<div className="border-border dark:border-border rounded-lg border">
|
||||
<div className="p-4">
|
||||
<div className="mb-4 flex items-center justify-between">
|
||||
<h3 className="text-sm font-medium">
|
||||
|
||||
@@ -36,20 +36,20 @@ const Help = () => {
|
||||
<button
|
||||
ref={buttonRef}
|
||||
onClick={toggleDropdown}
|
||||
className="mx-4 my-auto flex h-9 w-full items-center gap-4 rounded-3xl hover:bg-gray-100 dark:hover:bg-[#28292E]"
|
||||
className="hover:bg-sidebar-accent mx-4 my-auto flex h-9 w-full items-center gap-4 rounded-3xl"
|
||||
>
|
||||
<img src={Info} alt="info" className="ml-2 w-5 filter dark:invert" />
|
||||
{t('help')}
|
||||
</button>
|
||||
{isOpen && (
|
||||
<div
|
||||
className={`dark:bg-outer-space absolute z-10 w-48 translate-x-4 -translate-y-28 rounded-xl bg-white shadow-lg`}
|
||||
className={`dark:bg-card bg-card absolute z-10 w-48 translate-x-4 -translate-y-28 rounded-xl shadow-lg`}
|
||||
>
|
||||
<a
|
||||
href="https://docs.docsgpt.cloud/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="hover:bg-bright-gray flex items-start gap-4 rounded-t-xl px-4 py-2 text-black dark:text-white dark:hover:bg-[#545561]"
|
||||
className="hover:bg-muted text-foreground flex items-start gap-4 rounded-t-xl px-4 py-2"
|
||||
>
|
||||
<img
|
||||
src={PageIcon}
|
||||
@@ -61,7 +61,7 @@ const Help = () => {
|
||||
</a>
|
||||
<a
|
||||
href="mailto:support@docsgpt.cloud"
|
||||
className="hover:bg-bright-gray flex items-start gap-4 rounded-b-xl px-4 py-2 text-black dark:text-white dark:hover:bg-[#545561]"
|
||||
className="hover:bg-muted text-foreground flex items-start gap-4 rounded-b-xl px-4 py-2"
|
||||
>
|
||||
<img
|
||||
src={EmailIcon}
|
||||
|
||||
@@ -15,7 +15,7 @@ const Input = ({
|
||||
borderVariant = 'thick',
|
||||
textSize = 'medium',
|
||||
children,
|
||||
labelBgClassName = 'bg-white dark:bg-raisin-black',
|
||||
labelBgClassName = 'bg-card',
|
||||
leftIcon,
|
||||
onChange,
|
||||
onPaste,
|
||||
@@ -23,9 +23,9 @@ const Input = ({
|
||||
edgeRoundness = 'rounded-full',
|
||||
}: InputProps) => {
|
||||
const colorStyles = {
|
||||
silver: 'border-silver dark:border-silver/40',
|
||||
silver: 'border-border dark:border-border',
|
||||
jet: 'border-jet',
|
||||
gray: 'border-gray-5000 dark:text-silver',
|
||||
gray: 'border-gray-5000 dark:text-muted-foreground',
|
||||
};
|
||||
const borderStyles = {
|
||||
thin: 'border',
|
||||
@@ -44,7 +44,7 @@ const Input = ({
|
||||
<div className={`relative ${className}`}>
|
||||
<input
|
||||
ref={inputRef}
|
||||
className={`peer text-jet dark:text-bright-gray h-[42px] w-full ${edgeRoundness} bg-transparent ${leftIcon ? 'pl-10' : 'px-3'} py-1 placeholder-transparent outline-hidden ${colorStyles[colorVariant]} ${borderStyles[borderVariant]} ${textSizeStyles[textSize]} [&:-webkit-autofill]:appearance-none [&:-webkit-autofill]:bg-transparent [&:-webkit-autofill_selected]:bg-transparent`}
|
||||
className={`peer text-foreground dark:text-foreground h-[42px] w-full ${edgeRoundness} bg-transparent ${leftIcon ? 'pl-10' : 'px-3'} py-1 placeholder-transparent outline-hidden ${colorStyles[colorVariant]} ${borderStyles[borderVariant]} ${textSizeStyles[textSize]} [&:-webkit-autofill]:appearance-none [&:-webkit-autofill]:bg-transparent [&:-webkit-autofill_selected]:bg-transparent`}
|
||||
type={type}
|
||||
id={id}
|
||||
name={name}
|
||||
@@ -75,11 +75,11 @@ const Input = ({
|
||||
: 'peer-placeholder-shown:left-3'
|
||||
} peer-placeholder-shown:${
|
||||
textSizeStyles[textSize]
|
||||
} text-gray-4000 pointer-events-none cursor-none peer-focus:-top-2.5 peer-focus:left-3 peer-focus:text-xs dark:text-gray-400 ${labelBgClassName} max-w-[calc(100%-24px)] overflow-hidden text-ellipsis whitespace-nowrap`}
|
||||
} text-muted-foreground pointer-events-none cursor-none peer-focus:-top-2.5 peer-focus:left-3 peer-focus:text-xs ${labelBgClassName} max-w-[calc(100%-24px)] overflow-hidden text-ellipsis whitespace-nowrap`}
|
||||
>
|
||||
{placeholder}
|
||||
{required && (
|
||||
<span className="ml-0.5 text-[#D30000] dark:text-[#D42626]">*</span>
|
||||
<span className="ml-0.5 text-[#D30000] dark:text-red-500">*</span>
|
||||
)}
|
||||
</label>
|
||||
)}
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
import mermaid from 'mermaid';
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import mermaid from 'mermaid';
|
||||
import CopyButton from './CopyButton';
|
||||
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 { MermaidRendererProps } from './types';
|
||||
import { useSelector } from 'react-redux';
|
||||
|
||||
import { selectStatus } from '../conversation/conversationSlice';
|
||||
import { useDarkTheme } from '../hooks';
|
||||
import CopyButton from './CopyButton';
|
||||
import { MermaidRendererProps } from './types';
|
||||
|
||||
const MermaidRenderer: React.FC<MermaidRendererProps> = ({
|
||||
code,
|
||||
@@ -262,9 +263,9 @@ const MermaidRenderer: React.FC<MermaidRendererProps> = ({
|
||||
const errorRender = !isCurrentlyLoading && error;
|
||||
|
||||
return (
|
||||
<div className="w-inherit group border-light-silver dark:border-raisin-black dark:bg-eerie-black relative rounded-lg border bg-white">
|
||||
<div className="bg-platinum dark:bg-eerie-black-2 flex items-center justify-between px-2 py-1">
|
||||
<span className="text-just-black dark:text-chinese-white text-xs font-medium">
|
||||
<div className="w-inherit group border-border bg-card relative rounded-lg border">
|
||||
<div className="bg-platinum flex items-center justify-between px-2 py-1">
|
||||
<span className="text-foreground dark:text-foreground text-xs font-medium">
|
||||
mermaid
|
||||
</span>
|
||||
<div className="flex items-center gap-2">
|
||||
@@ -280,7 +281,7 @@ const MermaidRenderer: React.FC<MermaidRendererProps> = ({
|
||||
Download <span className="ml-1">▼</span>
|
||||
</button>
|
||||
{showDownloadMenu && (
|
||||
<div className="absolute right-0 z-10 mt-1 w-40 rounded-sm border border-gray-200 bg-white shadow-lg dark:border-gray-700 dark:bg-gray-800">
|
||||
<div className="border-border bg-card absolute right-0 z-10 mt-1 w-40 rounded-sm border shadow-lg">
|
||||
<ul>
|
||||
{downloadOptions.map((option, index) => (
|
||||
<li key={index}>
|
||||
@@ -289,7 +290,7 @@ const MermaidRenderer: React.FC<MermaidRendererProps> = ({
|
||||
option.action();
|
||||
setShowDownloadMenu(false);
|
||||
}}
|
||||
className="w-full px-4 py-2 text-left text-xs hover:bg-gray-100 dark:hover:bg-gray-700"
|
||||
className="hover:bg-muted w-full px-4 py-2 text-left text-xs"
|
||||
>
|
||||
{option.label}
|
||||
</button>
|
||||
@@ -318,14 +319,14 @@ const MermaidRenderer: React.FC<MermaidRendererProps> = ({
|
||||
</div>
|
||||
|
||||
{isCurrentlyLoading ? (
|
||||
<div className="dark:bg-eerie-black flex items-center justify-center bg-white p-4">
|
||||
<div className="bg-card flex items-center justify-center p-4">
|
||||
<div className="text-sm text-gray-500 dark:text-gray-400">
|
||||
Loading diagram...
|
||||
</div>
|
||||
</div>
|
||||
) : errorRender ? (
|
||||
<div className="m-2 rounded-sm border-2 border-red-400 dark:border-red-700">
|
||||
<div className="overflow-auto bg-red-100 px-4 py-2 text-sm break-words whitespace-normal text-red-800 dark:bg-red-900/30 dark:text-red-300">
|
||||
<div className="overflow-auto bg-red-100 px-4 py-2 text-sm wrap-break-word whitespace-normal text-red-800 dark:bg-red-900/30 dark:text-red-300">
|
||||
{error}
|
||||
</div>
|
||||
</div>
|
||||
@@ -333,7 +334,7 @@ const MermaidRenderer: React.FC<MermaidRendererProps> = ({
|
||||
<>
|
||||
<div
|
||||
ref={containerRef}
|
||||
className="no-scrollbar dark:bg-eerie-black relative block w-full bg-white p-4"
|
||||
className="no-scrollbar bg-card relative block w-full p-4"
|
||||
style={{
|
||||
overflow: 'auto',
|
||||
scrollbarWidth: 'none',
|
||||
@@ -399,9 +400,9 @@ const MermaidRenderer: React.FC<MermaidRendererProps> = ({
|
||||
</div>
|
||||
|
||||
{showCode && (
|
||||
<div className="border-light-silver dark:border-raisin-black border-t">
|
||||
<div className="bg-platinum dark:bg-eerie-black-2 p-2">
|
||||
<span className="text-just-black dark:text-chinese-white text-xs font-medium">
|
||||
<div className="border-border border-t">
|
||||
<div className="bg-platinum p-2">
|
||||
<span className="text-foreground dark:text-foreground text-xs font-medium">
|
||||
Mermaid Code
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -1438,7 +1438,7 @@ export default function MessageInput({
|
||||
onChange={handleVoiceFileAttachment}
|
||||
/>
|
||||
|
||||
<div className="border-dark-gray bg-lotion dark:border-grey relative flex w-full flex-col rounded-[23px] border dark:bg-transparent">
|
||||
<div className="border-border bg-card relative flex w-full flex-col rounded-[23px] border dark:bg-transparent">
|
||||
<div className="flex flex-wrap gap-1.5 px-2 py-2 sm:gap-2 sm:px-3">
|
||||
{attachments.map((attachment) => {
|
||||
return (
|
||||
@@ -1448,7 +1448,7 @@ export default function MessageInput({
|
||||
onDragStart={(e) => handleDragStart(e, attachment.id)}
|
||||
onDragOver={handleDragOver}
|
||||
onDrop={(e) => handleDropOn(e, attachment.id)}
|
||||
className={`group dark:text-bright-gray relative flex items-center rounded-xl bg-[#EFF3F4] px-2 py-1 text-[12px] text-[#5D5D5D] sm:px-3 sm:py-1.5 sm:text-[14px] dark:bg-[#393B3D] ${
|
||||
className={`group dark:text-foreground bg-muted text-muted-foreground dark:bg-accent relative flex items-center rounded-xl px-2 py-1 text-[12px] sm:px-3 sm:py-1.5 sm:text-[14px] ${
|
||||
attachment.status !== 'completed'
|
||||
? 'opacity-70'
|
||||
: 'opacity-100'
|
||||
@@ -1459,7 +1459,7 @@ export default function MessageInput({
|
||||
}`}
|
||||
title={attachment.fileName}
|
||||
>
|
||||
<div className="bg-purple-30 mr-2 flex h-8 w-8 items-center justify-center rounded-md p-1">
|
||||
<div className="bg-primary mr-2 flex h-8 w-8 items-center justify-center rounded-md p-1">
|
||||
{attachment.status === 'completed' && (
|
||||
<img
|
||||
src={DocumentationDark}
|
||||
@@ -1551,7 +1551,7 @@ export default function MessageInput({
|
||||
}
|
||||
tabIndex={1}
|
||||
placeholder={t('inputPlaceholder')}
|
||||
className="inputbox-style no-scrollbar bg-lotion dark:text-bright-gray dark:placeholder:text-bright-gray/50 w-full overflow-x-hidden overflow-y-auto rounded-t-[23px] px-2 text-base leading-tight whitespace-pre-wrap opacity-100 placeholder:text-gray-500 focus:outline-hidden sm:px-3 dark:bg-transparent"
|
||||
className="inputbox-style no-scrollbar dark:text-foreground dark:placeholder:text-muted-foreground/50 w-full overflow-x-hidden overflow-y-auto rounded-t-[23px] bg-transparent px-2 text-base leading-tight whitespace-pre-wrap opacity-100 placeholder:text-gray-500 focus:outline-hidden sm:px-3"
|
||||
onInput={handleInput}
|
||||
onKeyDown={handleKeyDown}
|
||||
onPaste={handlePaste}
|
||||
@@ -1564,7 +1564,7 @@ export default function MessageInput({
|
||||
{showSourceButton && (
|
||||
<button
|
||||
ref={sourceButtonRef}
|
||||
className="xs:px-3 xs:py-1.5 dark:border-purple-taupe flex max-w-[130px] items-center rounded-[32px] border border-[#AAAAAA] px-2 py-1 transition-colors hover:bg-gray-100 sm:max-w-[150px] dark:hover:bg-[#2C2E3C]"
|
||||
className="xs:px-3 xs:py-1.5 dark:border-border border-border hover:bg-accent dark:hover:bg-muted flex max-w-[130px] items-center rounded-[32px] border px-2 py-1 transition-colors sm:max-w-[150px]"
|
||||
onClick={() => setIsSourcesPopupOpen(!isSourcesPopupOpen)}
|
||||
title={
|
||||
selectedDocs && selectedDocs.length > 0
|
||||
@@ -1577,7 +1577,7 @@ export default function MessageInput({
|
||||
alt="Sources"
|
||||
className="mr-1 h-3.5 w-3.5 shrink-0 sm:mr-1.5 sm:h-4"
|
||||
/>
|
||||
<span className="xs:text-[12px] dark:text-bright-gray truncate overflow-hidden text-[10px] font-medium text-[#5D5D5D] sm:text-[14px]">
|
||||
<span className="xs:text-[12px] dark:text-foreground text-muted-foreground truncate overflow-hidden text-[10px] font-medium sm:text-[14px]">
|
||||
{selectedDocs && selectedDocs.length > 0
|
||||
? selectedDocs.length === 1
|
||||
? selectedDocs[0].name
|
||||
@@ -1595,7 +1595,7 @@ export default function MessageInput({
|
||||
{showToolButton && (
|
||||
<button
|
||||
ref={toolButtonRef}
|
||||
className="xs:px-3 xs:py-1.5 xs:max-w-[150px] dark:border-purple-taupe flex max-w-[130px] items-center rounded-[32px] border border-[#AAAAAA] px-2 py-1 transition-colors hover:bg-gray-100 dark:hover:bg-[#2C2E3C]"
|
||||
className="xs:px-3 xs:py-1.5 xs:max-w-[150px] dark:border-border border-border hover:bg-muted dark:hover:bg-muted flex max-w-[130px] items-center rounded-[32px] border px-2 py-1 transition-colors"
|
||||
onClick={() => setIsToolsPopupOpen(!isToolsPopupOpen)}
|
||||
>
|
||||
<img
|
||||
@@ -1603,7 +1603,7 @@ export default function MessageInput({
|
||||
alt="Tools"
|
||||
className="mr-1 h-3.5 w-3.5 shrink-0 sm:mr-1.5 sm:h-4 sm:w-4"
|
||||
/>
|
||||
<span className="xs:text-[12px] dark:text-bright-gray truncate overflow-hidden text-[10px] font-medium text-[#5D5D5D] sm:text-[14px]">
|
||||
<span className="xs:text-[12px] dark:text-foreground text-muted-foreground truncate overflow-hidden text-[10px] font-medium sm:text-[14px]">
|
||||
{t('settings.tools.label')}
|
||||
</span>
|
||||
</button>
|
||||
@@ -1620,7 +1620,7 @@ export default function MessageInput({
|
||||
className={`xs:px-3 xs:py-1.5 dark:border-purple-taupe flex items-center rounded-[32px] border px-2 py-1 transition-colors ${
|
||||
recordingState === 'recording'
|
||||
? 'border-[#B42318] bg-[#FEE4E2] text-[#B42318] dark:bg-[#4A2323]'
|
||||
: 'border-[#AAAAAA] hover:bg-gray-100 dark:hover:bg-[#2C2E3C]'
|
||||
: 'border-border dark:hover:bg-accent hover:bg-gray-100'
|
||||
} ${
|
||||
loading || recordingState === 'transcribing'
|
||||
? 'cursor-not-allowed opacity-60'
|
||||
@@ -1645,13 +1645,13 @@ export default function MessageInput({
|
||||
</span>
|
||||
</button>
|
||||
)}
|
||||
<label className="xs:px-3 xs:py-1.5 dark:border-purple-taupe flex cursor-pointer items-center rounded-[32px] border border-[#AAAAAA] px-2 py-1 transition-colors hover:bg-gray-100 dark:hover:bg-[#2C2E3C]">
|
||||
<label className="xs:px-3 xs:py-1.5 dark:border-border border-border hover:bg-muted dark:hover:bg-muted flex cursor-pointer items-center rounded-[32px] border px-2 py-1 transition-colors">
|
||||
<img
|
||||
src={ClipIcon}
|
||||
alt="Attach"
|
||||
className="mr-1 h-3.5 w-3.5 sm:mr-1.5 sm:h-4 sm:w-4"
|
||||
/>
|
||||
<span className="xs:text-[12px] dark:text-bright-gray text-[10px] font-medium text-[#5D5D5D] sm:text-[14px]">
|
||||
<span className="xs:text-[12px] dark:text-foreground text-muted-foreground text-[10px] font-medium sm:text-[14px]">
|
||||
{t('conversation.attachments.attach')}
|
||||
</span>
|
||||
<input
|
||||
@@ -1669,7 +1669,7 @@ export default function MessageInput({
|
||||
<button
|
||||
onClick={handleCancel}
|
||||
aria-label={t('cancel')}
|
||||
className={`ml-auto flex h-7 w-7 shrink-0 items-center justify-center rounded-full bg-[#7F54D6] text-white sm:h-9 sm:w-9`}
|
||||
className={`bg-primary ml-auto flex h-7 w-7 shrink-0 items-center justify-center rounded-full text-white sm:h-9 sm:w-9`}
|
||||
disabled={!loading}
|
||||
>
|
||||
<div className="flex h-3 w-3 items-center justify-center rounded-[3px] bg-white sm:h-3.5 sm:w-3.5" />
|
||||
@@ -1683,8 +1683,8 @@ export default function MessageInput({
|
||||
!loading &&
|
||||
recordingState !== 'recording' &&
|
||||
recordingState !== 'transcribing'
|
||||
? 'bg-purple-30 text-white'
|
||||
: 'bg-[#EDEDED] text-[#959595] dark:bg-[#37383D] dark:text-[#77787D]'
|
||||
? 'bg-primary text-white'
|
||||
: 'bg-muted text-muted-foreground dark:bg-accent dark:text-muted-foreground'
|
||||
}`}
|
||||
disabled={
|
||||
!value.trim() ||
|
||||
@@ -1729,12 +1729,12 @@ export default function MessageInput({
|
||||
|
||||
{handleDragActive &&
|
||||
createPortal(
|
||||
<div className="dark:bg-gray-alpha/50 pointer-events-none fixed top-0 left-0 z-50 flex size-full flex-col items-center justify-center bg-white/85">
|
||||
<div className="dark:bg-background/85 pointer-events-none fixed top-0 left-0 z-50 flex size-full flex-col items-center justify-center bg-white/85">
|
||||
<img className="filter dark:invert" src={DragFileUpload} />
|
||||
<span className="text-outer-space dark:text-silver px-2 text-2xl font-bold">
|
||||
<span className="text-muted-foreground dark:text-muted-foreground px-2 text-2xl font-bold">
|
||||
{t('modals.uploadDoc.drag.title')}
|
||||
</span>
|
||||
<span className="text-s text-outer-space dark:text-silver w-48 p-2 text-center">
|
||||
<span className="text-s text-muted-foreground dark:text-muted-foreground w-48 p-2 text-center">
|
||||
{t('modals.uploadDoc.drag.description')}
|
||||
</span>
|
||||
</div>,
|
||||
|
||||
@@ -168,7 +168,7 @@ export default function MultiSelectPopup({
|
||||
return (
|
||||
<div
|
||||
ref={popupRef}
|
||||
className="border-light-silver bg-lotion dark:border-dim-gray dark:bg-charleston-green-2 fixed z-9999 flex flex-col rounded-lg border shadow-[0px_9px_46px_8px_#0000001F,0px_24px_38px_3px_#00000024,0px_11px_15px_-7px_#00000033]"
|
||||
className="border-border bg-background dark:border-border dark:bg-card fixed z-9999 flex flex-col rounded-lg border shadow-[0px_9px_46px_8px_#0000001F,0px_24px_38px_3px_#00000024,0px_11px_15px_-7px_#00000033]"
|
||||
style={{
|
||||
top: popupPosition.showAbove ? undefined : popupPosition.top,
|
||||
bottom: popupPosition.showAbove
|
||||
@@ -198,7 +198,7 @@ export default function MultiSelectPopup({
|
||||
searchPlaceholder ||
|
||||
t('settings.tools.searchPlaceholder', 'Search...')
|
||||
}
|
||||
labelBgClassName="bg-lotion dark:bg-charleston-green-2"
|
||||
labelBgClassName="bg-background dark:bg-card"
|
||||
borderVariant="thin"
|
||||
className="mb-4"
|
||||
textSize="small"
|
||||
@@ -206,13 +206,13 @@ export default function MultiSelectPopup({
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<div className="dark:border-dim-gray mx-4 mb-4 grow overflow-auto rounded-md border border-[#D9D9D9]">
|
||||
<div className="dark:border-border mx-4 mb-4 grow overflow-auto rounded-md border border-[#D9D9D9]">
|
||||
{loading ? (
|
||||
<div className="flex h-full items-center 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="h-full overflow-y-auto scrollbar-overlay">
|
||||
<div className="scrollbar-overlay h-full overflow-y-auto">
|
||||
{filteredOptions.length === 0 ? (
|
||||
<div className="flex h-full flex-col items-center justify-center px-4 py-8 text-center">
|
||||
<img
|
||||
@@ -233,7 +233,7 @@ export default function MultiSelectPopup({
|
||||
<div
|
||||
key={option.id}
|
||||
onClick={() => handleOptionClick(option.id)}
|
||||
className="dark:border-dim-gray dark:hover:bg-charleston-green-3 flex cursor-pointer items-center justify-between border-b border-[#D9D9D9] p-3 last:border-b-0 hover:bg-gray-100"
|
||||
className="dark:border-border dark:hover:bg-accent hover:bg-accent flex cursor-pointer items-center justify-between border-b border-[#D9D9D9] p-3 last:border-b-0"
|
||||
role="option"
|
||||
aria-selected={isSelected}
|
||||
>
|
||||
@@ -248,7 +248,7 @@ export default function MultiSelectPopup({
|
||||
</div>
|
||||
<div className="shrink-0">
|
||||
<div
|
||||
className={`dark:bg-charleston-green-2 flex h-4 w-4 items-center justify-center rounded-xs border-2 border-[#C6C6C6] bg-white dark:border-[#757783]`}
|
||||
className={`border-border bg-card flex h-4 w-4 items-center justify-center rounded-xs border-2`}
|
||||
aria-hidden="true"
|
||||
>
|
||||
{isSelected && (
|
||||
@@ -269,7 +269,7 @@ export default function MultiSelectPopup({
|
||||
)}
|
||||
</div>
|
||||
{footerContent && (
|
||||
<div className="border-light-silver dark:border-dim-gray shrink-0 border-t p-4">
|
||||
<div className="border-border dark:border-border shrink-0 border-t p-4">
|
||||
{footerContent}
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -87,7 +87,7 @@ export default function Notification({
|
||||
))}
|
||||
</div>
|
||||
|
||||
<p className="text-white-3000 relative z-10 text-xs leading-6 font-semibold xl:text-sm xl:leading-7">
|
||||
<p className="relative z-10 text-xs leading-6 font-semibold text-white xl:text-sm xl:leading-7">
|
||||
{notificationText}
|
||||
</p>
|
||||
<span className="relative z-10 flex items-center">
|
||||
@@ -132,4 +132,4 @@ export default function Notification({
|
||||
</a>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,271 +0,0 @@
|
||||
import React from 'react';
|
||||
|
||||
import Arrow2 from '../assets/dropdown-arrow.svg';
|
||||
import Edit from '../assets/edit.svg';
|
||||
import Search from '../assets/search.svg';
|
||||
import Trash from '../assets/trash.svg';
|
||||
|
||||
/**
|
||||
* SearchableDropdown - A standalone dropdown component with built-in search functionality
|
||||
*/
|
||||
|
||||
type SearchableDropdownOptionBase = {
|
||||
id?: string;
|
||||
type?: string;
|
||||
};
|
||||
|
||||
type NameIdOption = { name: string; id: string } & SearchableDropdownOptionBase;
|
||||
|
||||
export type SearchableDropdownOption =
|
||||
| string
|
||||
| NameIdOption
|
||||
| ({ label: string; value: string } & SearchableDropdownOptionBase)
|
||||
| ({ value: number; description: string } & SearchableDropdownOptionBase);
|
||||
|
||||
export type SearchableDropdownSelectedValue = SearchableDropdownOption | null;
|
||||
|
||||
export interface SearchableDropdownProps<
|
||||
T extends SearchableDropdownOption = SearchableDropdownOption,
|
||||
> {
|
||||
options: T[];
|
||||
selectedValue: SearchableDropdownSelectedValue;
|
||||
onSelect: (value: T) => void;
|
||||
size?: string;
|
||||
/** Controls border radius for both button and dropdown menu */
|
||||
rounded?: 'xl' | '3xl';
|
||||
border?: 'border' | 'border-2';
|
||||
showEdit?: boolean;
|
||||
onEdit?: (value: NameIdOption) => void;
|
||||
showDelete?: boolean | ((option: T) => boolean);
|
||||
onDelete?: (id: string) => void;
|
||||
placeholder?: string;
|
||||
}
|
||||
|
||||
function SearchableDropdown<T extends SearchableDropdownOption>({
|
||||
options,
|
||||
selectedValue,
|
||||
onSelect,
|
||||
size = 'w-32',
|
||||
rounded = 'xl',
|
||||
border = 'border-2',
|
||||
showEdit,
|
||||
onEdit,
|
||||
showDelete,
|
||||
onDelete,
|
||||
placeholder,
|
||||
}: SearchableDropdownProps<T>) {
|
||||
const dropdownRef = React.useRef<HTMLDivElement>(null);
|
||||
const searchInputRef = React.useRef<HTMLInputElement>(null);
|
||||
const [isOpen, setIsOpen] = React.useState(false);
|
||||
const [searchQuery, setSearchQuery] = React.useState('');
|
||||
|
||||
const borderRadius = rounded === 'xl' ? 'rounded-xl' : 'rounded-3xl';
|
||||
|
||||
React.useEffect(() => {
|
||||
const handleClickOutside = (event: MouseEvent) => {
|
||||
if (
|
||||
dropdownRef.current &&
|
||||
!dropdownRef.current.contains(event.target as Node)
|
||||
) {
|
||||
setIsOpen(false);
|
||||
setSearchQuery('');
|
||||
}
|
||||
};
|
||||
|
||||
document.addEventListener('mousedown', handleClickOutside);
|
||||
return () => document.removeEventListener('mousedown', handleClickOutside);
|
||||
}, []);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (isOpen && searchInputRef.current) {
|
||||
searchInputRef.current.focus();
|
||||
}
|
||||
}, [isOpen]);
|
||||
|
||||
const getOptionText = (option: SearchableDropdownOption): string => {
|
||||
if (typeof option === 'string') return option;
|
||||
if ('name' in option) return option.name;
|
||||
if ('label' in option) return option.label;
|
||||
if ('description' in option) return option.description;
|
||||
return '';
|
||||
};
|
||||
|
||||
const filteredOptions = React.useMemo(() => {
|
||||
if (!searchQuery.trim()) return options;
|
||||
const query = searchQuery.toLowerCase();
|
||||
return options.filter((option) =>
|
||||
getOptionText(option).toLowerCase().includes(query),
|
||||
);
|
||||
}, [options, searchQuery]);
|
||||
|
||||
const getDisplayValue = (): string => {
|
||||
if (!selectedValue) return placeholder ?? 'From URL';
|
||||
if (typeof selectedValue === 'string') return selectedValue;
|
||||
if ('label' in selectedValue) return selectedValue.label;
|
||||
if ('name' in selectedValue) return selectedValue.name;
|
||||
if ('description' in selectedValue) {
|
||||
return selectedValue.value < 1e9
|
||||
? `${selectedValue.value} (${selectedValue.description})`
|
||||
: selectedValue.description;
|
||||
}
|
||||
return placeholder ?? 'From URL';
|
||||
};
|
||||
|
||||
const isOptionSelected = (option: T): boolean => {
|
||||
if (!selectedValue) return false;
|
||||
if (typeof selectedValue === 'string')
|
||||
return selectedValue === (option as unknown as string);
|
||||
if (typeof option === 'string') return false;
|
||||
|
||||
const optionObj = option as Record<string, unknown>;
|
||||
const selectedObj = selectedValue as Record<string, unknown>;
|
||||
|
||||
if ('name' in optionObj && 'name' in selectedObj)
|
||||
return selectedObj.name === optionObj.name;
|
||||
if ('label' in optionObj && 'label' in selectedObj)
|
||||
return selectedObj.label === optionObj.label;
|
||||
if ('value' in optionObj && 'value' in selectedObj)
|
||||
return selectedObj.value === optionObj.value;
|
||||
return false;
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`relative ${typeof selectedValue === 'string' ? '' : 'align-middle'} ${size}`}
|
||||
ref={dropdownRef}
|
||||
>
|
||||
<button
|
||||
onClick={() => setIsOpen(!isOpen)}
|
||||
className={`flex w-full cursor-pointer items-center justify-between ${border} border-silver dark:border-dim-gray bg-white px-5 py-3 dark:bg-transparent ${borderRadius}`}
|
||||
>
|
||||
<span
|
||||
className={`dark:text-bright-gray truncate ${!selectedValue ? 'text-gray-500 dark:text-gray-400' : ''}`}
|
||||
>
|
||||
{getDisplayValue()}
|
||||
</span>
|
||||
<img
|
||||
src={Arrow2}
|
||||
alt="arrow"
|
||||
className={`h-3 w-3 transform transition-transform ${isOpen ? 'rotate-180' : 'rotate-0'}`}
|
||||
/>
|
||||
</button>
|
||||
|
||||
{isOpen && (
|
||||
<div
|
||||
className={`absolute right-0 left-0 z-20 mt-2 ${borderRadius} dark:bg-dark-charcoal bg-[#FBFBFB] shadow-[0px_24px_48px_0px_#00000029]`}
|
||||
>
|
||||
<div
|
||||
className={`border-silver dark:border-dim-gray dark:bg-dark-charcoal sticky top-0 z-10 border-b bg-[#FBFBFB] px-3 py-2 ${rounded === 'xl' ? 'rounded-t-xl' : 'rounded-t-3xl'}`}
|
||||
>
|
||||
<div className="relative flex items-center">
|
||||
<img
|
||||
src={Search}
|
||||
alt="search"
|
||||
width={14}
|
||||
height={14}
|
||||
className="absolute left-3"
|
||||
/>
|
||||
<input
|
||||
ref={searchInputRef}
|
||||
type="text"
|
||||
value={searchQuery}
|
||||
onChange={(e) => setSearchQuery(e.target.value)}
|
||||
placeholder="Search..."
|
||||
className="dark:text-bright-gray w-full rounded-lg border-0 bg-transparent py-2 pr-3 pl-10 font-['Inter'] text-[14px] leading-[16.5px] font-normal focus:ring-0 focus:outline-none"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="max-h-40 overflow-y-auto">
|
||||
{filteredOptions.length === 0 ? (
|
||||
<div className="px-5 py-3 text-center text-sm text-gray-500 dark:text-gray-400">
|
||||
No results found
|
||||
</div>
|
||||
) : (
|
||||
filteredOptions.map((option, index) => {
|
||||
const selected = isOptionSelected(option);
|
||||
const optionObj =
|
||||
typeof option !== 'string'
|
||||
? (option as Record<string, unknown>)
|
||||
: null;
|
||||
const optionType = optionObj?.type as string | undefined;
|
||||
const optionId = optionObj?.id as string | undefined;
|
||||
const optionName = optionObj?.name as string | undefined;
|
||||
|
||||
return (
|
||||
<div
|
||||
key={index}
|
||||
className={`flex cursor-pointer items-center justify-between hover:bg-[#ECECEC] dark:hover:bg-[#545561] ${selected ? 'bg-[#ECECEC] dark:bg-[#545561]' : ''}`}
|
||||
>
|
||||
<span
|
||||
onClick={() => {
|
||||
onSelect(option);
|
||||
setIsOpen(false);
|
||||
setSearchQuery('');
|
||||
}}
|
||||
className="dark:text-light-gray ml-5 flex-1 overflow-hidden py-3 font-['Inter'] text-[14px] leading-[16.5px] font-normal text-ellipsis whitespace-nowrap"
|
||||
>
|
||||
{getOptionText(option)}
|
||||
</span>
|
||||
{showEdit &&
|
||||
onEdit &&
|
||||
optionObj &&
|
||||
optionType !== 'public' && (
|
||||
<img
|
||||
src={Edit}
|
||||
alt="Edit"
|
||||
className="mr-4 h-4 w-4 cursor-pointer hover:opacity-50"
|
||||
onClick={() => {
|
||||
if (optionName && optionId) {
|
||||
onEdit({
|
||||
id: optionId,
|
||||
name: optionName,
|
||||
type: optionType,
|
||||
});
|
||||
}
|
||||
setIsOpen(false);
|
||||
setSearchQuery('');
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{showDelete && onDelete && (
|
||||
<button
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
const id =
|
||||
typeof option === 'string'
|
||||
? option
|
||||
: (optionId ?? '');
|
||||
onDelete(id);
|
||||
}}
|
||||
className={`mr-2 h-4 w-4 cursor-pointer hover:opacity-50 ${
|
||||
typeof showDelete === 'function' &&
|
||||
!showDelete(option)
|
||||
? 'hidden'
|
||||
: ''
|
||||
}`}
|
||||
>
|
||||
<img
|
||||
src={Trash}
|
||||
alt="Delete"
|
||||
className={`mr-2 h-4 w-4 cursor-pointer hover:opacity-50 ${
|
||||
optionType === 'public'
|
||||
? 'cursor-not-allowed opacity-50'
|
||||
: ''
|
||||
}`}
|
||||
/>
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default SearchableDropdown;
|
||||
@@ -51,16 +51,16 @@ const SettingsBar = ({ setActiveTab, activeTab }: SettingsBarProps) => {
|
||||
return (
|
||||
<div className="relative mt-6 flex flex-row items-center space-x-1 overflow-auto md:space-x-0">
|
||||
<div
|
||||
className={`${hiddenGradient === 'left' ? 'hidden' : ''} dark:from-raisin-black pointer-events-none absolute inset-y-0 left-6 w-14 bg-linear-to-r from-white md:hidden`}
|
||||
className={`${hiddenGradient === 'left' ? 'hidden' : ''} dark:from-background pointer-events-none absolute inset-y-0 left-6 w-14 bg-linear-to-r from-white md:hidden`}
|
||||
></div>
|
||||
<div
|
||||
className={`${hiddenGradient === 'right' ? 'hidden' : ''} dark:from-raisin-black pointer-events-none absolute inset-y-0 right-6 w-14 bg-linear-to-l from-white md:hidden`}
|
||||
className={`${hiddenGradient === 'right' ? 'hidden' : ''} dark:from-background pointer-events-none absolute inset-y-0 right-6 w-14 bg-linear-to-l from-white md:hidden`}
|
||||
></div>
|
||||
|
||||
<div className="z-10 md:hidden">
|
||||
<button
|
||||
onClick={() => scrollTabs(-1)}
|
||||
className="flex h-6 w-6 items-center justify-center rounded-full transition-all hover:bg-gray-200 dark:hover:bg-gray-700"
|
||||
className="hover:bg-muted dark:hover:bg-accent flex h-6 w-6 items-center justify-center rounded-full transition-all"
|
||||
aria-label={t('settings.scrollTabsLeft')}
|
||||
>
|
||||
<img src={ArrowLeft} alt="left-arrow" className="h-3" />
|
||||
@@ -78,8 +78,8 @@ const SettingsBar = ({ setActiveTab, activeTab }: SettingsBarProps) => {
|
||||
onClick={() => setActiveTab(tab)}
|
||||
className={`h-9 snap-start rounded-3xl px-4 font-bold transition-colors ${
|
||||
activeTab === tab
|
||||
? 'dark:bg-dark-charcoal bg-[#F4F4F5] text-neutral-900 dark:text-white'
|
||||
: 'text-neutral-700 hover:text-neutral-900 dark:text-neutral-300 dark:hover:text-white'
|
||||
? 'bg-muted text-foreground dark:bg-accent dark:text-white'
|
||||
: 'text-muted-foreground hover:text-foreground dark:text-neutral-400 dark:hover:text-white'
|
||||
}`}
|
||||
role="tab"
|
||||
aria-selected={activeTab === tab}
|
||||
@@ -93,7 +93,7 @@ const SettingsBar = ({ setActiveTab, activeTab }: SettingsBarProps) => {
|
||||
<div className="z-10 md:hidden">
|
||||
<button
|
||||
onClick={() => scrollTabs(1)}
|
||||
className="flex h-6 w-6 items-center justify-center rounded-full hover:bg-gray-200 dark:hover:bg-gray-700"
|
||||
className="hover:bg-muted dark:hover:bg-accent flex h-6 w-6 items-center justify-center rounded-full"
|
||||
aria-label={t('settings.scrollTabsRight')}
|
||||
>
|
||||
<img src={ArrowRight} alt="right-arrow" className="h-3" />
|
||||
|
||||
@@ -33,13 +33,13 @@ export default function Sidebar({
|
||||
return (
|
||||
<div ref={sidebarRef} className="h-vh relative">
|
||||
<div
|
||||
className={`dark:bg-chinese-black fixed top-0 right-0 z-50 h-full w-64 transform bg-white shadow-xl transition-all duration-300 sm:w-80 ${
|
||||
className={`bg-card fixed top-0 right-0 z-50 h-full w-64 transform shadow-xl transition-all duration-300 sm:w-80 ${
|
||||
isOpen ? 'translate-x-[10px]' : 'translate-x-full'
|
||||
} border-l border-[#9ca3af]/10`}
|
||||
>
|
||||
<div className="flex w-full flex-row items-end justify-end px-4 pt-3">
|
||||
<button
|
||||
className="hover:bg-gray-1000 dark:hover:bg-gun-metal w-7 rounded-full p-2"
|
||||
className="hover:bg-accent w-7 rounded-full p-2"
|
||||
onClick={() => toggleState(!isOpen)}
|
||||
>
|
||||
<img className="filter dark:invert" src={Exit} />
|
||||
|
||||
@@ -97,7 +97,7 @@ const SkeletonLoader: React.FC<SkeletonLoaderProps> = ({
|
||||
{[...Array(8)].map((_, idx) => (
|
||||
<div
|
||||
key={idx}
|
||||
className="dark:hover:bg-dark-charcoal flex w-full items-start p-2 hover:bg-[#F9F9F9]"
|
||||
className="dark:hover:bg-accent hover:bg-muted flex w-full items-start p-2"
|
||||
>
|
||||
<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>
|
||||
@@ -119,7 +119,7 @@ const SkeletonLoader: React.FC<SkeletonLoaderProps> = ({
|
||||
key={idx}
|
||||
className={`p-6 ${
|
||||
skeletonCount === 1 ? 'w-full' : 'w-60'
|
||||
} dark:bg-raisin-black animate-pulse rounded-3xl`}
|
||||
} animate-pulse rounded-3xl`}
|
||||
>
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
@@ -154,10 +154,7 @@ const SkeletonLoader: React.FC<SkeletonLoaderProps> = ({
|
||||
const renderAnalysis = () => (
|
||||
<>
|
||||
{[...Array(skeletonCount)].map((_, idx) => (
|
||||
<div
|
||||
key={idx}
|
||||
className="dark:bg-raisin-black w-full animate-pulse rounded-3xl p-6"
|
||||
>
|
||||
<div key={idx} className="bg-card w-full animate-pulse rounded-3xl p-6">
|
||||
<div className="space-y-6">
|
||||
<div className="space-y-2">
|
||||
<div className="mb-4 h-4 w-1/3 rounded-sm bg-gray-300 dark:bg-gray-600"></div>
|
||||
@@ -189,10 +186,10 @@ const SkeletonLoader: React.FC<SkeletonLoaderProps> = ({
|
||||
{Array.from({ length: count }).map((_, index) => (
|
||||
<div
|
||||
key={`chunk-skel-${index}`}
|
||||
className="relative flex h-[197px] w-full max-w-[487px] animate-pulse flex-col overflow-hidden rounded-[5.86px] border border-[#D1D9E0] dark:border-[#6A6A6A]"
|
||||
className="border-border dark:border-border relative flex h-[197px] w-full max-w-[487px] animate-pulse flex-col overflow-hidden rounded-[5.86px] border"
|
||||
>
|
||||
<div className="w-full">
|
||||
<div className="flex w-full items-center justify-between border-b border-[#D1D9E0] bg-[#F6F8FA] px-4 py-3 dark:border-[#6A6A6A] dark:bg-[#27282D]">
|
||||
<div className="border-border bg-muted dark:border-border dark:bg-card flex w-full items-center justify-between border-b px-4 py-3">
|
||||
<div className="h-4 w-20 rounded bg-gray-300 dark:bg-gray-600"></div>
|
||||
</div>
|
||||
<div className="space-y-3 px-4 pt-4 pb-6">
|
||||
@@ -214,7 +211,7 @@ const SkeletonLoader: React.FC<SkeletonLoaderProps> = ({
|
||||
{Array.from({ length: count }).map((_, idx) => (
|
||||
<div
|
||||
key={`source-skel-${idx}`}
|
||||
className="flex h-[130px] w-full animate-pulse flex-col rounded-2xl bg-[#F9F9F9] p-3 dark:bg-[#383838]"
|
||||
className="bg-muted dark:bg-accent flex h-[130px] w-full animate-pulse flex-col rounded-2xl p-3"
|
||||
>
|
||||
<div className="w-full flex-1">
|
||||
<div className="flex w-full items-center justify-between gap-2">
|
||||
|
||||
@@ -107,7 +107,7 @@ export default function SourcesPopup({
|
||||
const popupContent = (
|
||||
<div
|
||||
ref={popupRef}
|
||||
className="bg-lotion dark:bg-charleston-green-2 fixed z-50 flex flex-col rounded-xl shadow-[0px_9px_46px_8px_#0000001F,0px_24px_38px_3px_#00000024,0px_11px_15px_-7px_#00000033]"
|
||||
className="bg-background dark:bg-card fixed z-50 flex flex-col rounded-xl shadow-[0px_9px_46px_8px_#0000001F,0px_24px_38px_3px_#00000024,0px_11px_15px_-7px_#00000033]"
|
||||
style={{
|
||||
top: popupPosition.showAbove ? popupPosition.top : undefined,
|
||||
bottom: popupPosition.showAbove
|
||||
@@ -122,7 +122,7 @@ export default function SourcesPopup({
|
||||
>
|
||||
<div className="flex h-full flex-col">
|
||||
<div className="shrink-0 px-4 py-4 md:px-6">
|
||||
<h2 className="dark:text-bright-gray mb-4 text-lg font-bold text-[#141414] dark:text-[20px]">
|
||||
<h2 className="dark:text-foreground mb-4 text-lg font-bold text-[#141414] dark:text-[20px]">
|
||||
{t('conversation.sources.text')}
|
||||
</h2>
|
||||
|
||||
@@ -135,11 +135,11 @@ export default function SourcesPopup({
|
||||
placeholder={t('settings.sources.searchPlaceholder')}
|
||||
borderVariant="thin"
|
||||
className="mb-4"
|
||||
labelBgClassName="bg-lotion dark:bg-charleston-green-2"
|
||||
labelBgClassName="bg-background dark:bg-card"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="dark:border-dim-gray mx-4 grow overflow-y-auto rounded-md border border-[#D9D9D9] scrollbar-overlay">
|
||||
<div className="dark:border-border scrollbar-overlay mx-4 grow overflow-y-auto rounded-md border border-[#D9D9D9]">
|
||||
{options ? (
|
||||
<>
|
||||
{filteredOptions?.map((option: any, index: number) => {
|
||||
@@ -154,7 +154,7 @@ export default function SourcesPopup({
|
||||
return (
|
||||
<div
|
||||
key={index}
|
||||
className="border-opacity-80 dark:border-dim-gray flex cursor-pointer items-center border-b border-[#D9D9D9] p-3 transition-colors hover:bg-gray-100 dark:text-[14px] dark:hover:bg-[#2C2E3C]"
|
||||
className="border-opacity-80 dark:border-border hover:bg-muted flex cursor-pointer items-center border-b border-[#D9D9D9] p-3 transition-colors dark:text-[14px]"
|
||||
onClick={() => {
|
||||
if (isSelected) {
|
||||
const updatedDocs =
|
||||
@@ -186,11 +186,11 @@ export default function SourcesPopup({
|
||||
height={14}
|
||||
className="mr-3 shrink-0"
|
||||
/>
|
||||
<span className="dark:text-bright-gray mr-3 grow overflow-hidden font-medium text-ellipsis whitespace-nowrap text-[#5D5D5D]">
|
||||
<span className="dark:text-foreground mr-3 grow overflow-hidden font-medium text-ellipsis whitespace-nowrap text-[#5D5D5D]">
|
||||
{option.name}
|
||||
</span>
|
||||
<div
|
||||
className={`flex h-4 w-4 shrink-0 items-center justify-center rounded-xs border-2 border-[#C6C6C6] p-[0.5px] dark:border-[#757783]`}
|
||||
className={`dark:border-border flex h-4 w-4 shrink-0 items-center justify-center rounded-xs border-2 border-[#C6C6C6] p-[0.5px]`}
|
||||
>
|
||||
{isSelected && (
|
||||
<img
|
||||
@@ -205,7 +205,7 @@ export default function SourcesPopup({
|
||||
})}
|
||||
</>
|
||||
) : (
|
||||
<div className="dark:text-bright-gray p-4 text-center text-gray-500 dark:text-[14px]">
|
||||
<div className="dark:text-foreground p-4 text-center text-gray-500 dark:text-[14px]">
|
||||
{t('conversation.sources.noSourcesAvailable')}
|
||||
</div>
|
||||
)}
|
||||
@@ -214,7 +214,7 @@ export default function SourcesPopup({
|
||||
<div className="shrink-0 px-4 py-4 opacity-75 transition-opacity duration-200 hover:opacity-100 md:px-6">
|
||||
<a
|
||||
href="/settings/sources"
|
||||
className="text-violets-are-blue inline-flex items-center gap-2 text-base font-medium"
|
||||
className="text-primary inline-flex items-center gap-2 text-base font-medium"
|
||||
onClick={onClose}
|
||||
>
|
||||
{t('settings.sources.goToSources')}
|
||||
@@ -225,7 +225,7 @@ export default function SourcesPopup({
|
||||
<div className="flex shrink-0 justify-start px-4 py-3 md:px-6">
|
||||
<button
|
||||
onClick={handleUploadClick}
|
||||
className="border-violets-are-blue text-violets-are-blue hover:bg-violets-are-blue w-auto rounded-full border px-4 py-2 text-[14px] font-medium transition-colors duration-200 hover:text-white"
|
||||
className="border-primary text-primary hover:bg-primary/90 w-auto rounded-full border px-4 py-2 text-[14px] font-medium transition-colors duration-200 hover:text-white"
|
||||
>
|
||||
{t('settings.sources.uploadNew')}
|
||||
</button>
|
||||
|
||||
@@ -46,7 +46,7 @@ const TableContainer = React.forwardRef<HTMLDivElement, TableContainerProps>(
|
||||
<div className={`relative rounded-[6px] ${className}`}>
|
||||
<div
|
||||
ref={ref}
|
||||
className={`w-full overflow-x-auto rounded-[6px] bg-transparent ${bordered ? 'border border-[#D7D7D7] dark:border-[#6A6A6A]' : ''}`}
|
||||
className={`w-full overflow-x-auto rounded-[6px] bg-transparent ${bordered ? 'border-border dark:border-border border' : ''}`}
|
||||
style={{
|
||||
maxHeight: height === 'auto' ? undefined : height,
|
||||
overflowY: height === 'auto' ? 'hidden' : 'auto',
|
||||
@@ -75,7 +75,7 @@ const Table: React.FC<TableProps> = ({
|
||||
const TableHead: React.FC<TableHeadProps> = ({ children, className = '' }) => {
|
||||
return (
|
||||
<thead
|
||||
className={`sticky top-0 z-10 bg-gray-100 dark:bg-[#27282D] ${className} `}
|
||||
className={`dark:bg-card sticky top-0 z-10 bg-gray-100 ${className} `}
|
||||
>
|
||||
{children}
|
||||
</thead>
|
||||
@@ -96,7 +96,7 @@ const TableRow: React.FC<TableRowProps> = ({
|
||||
onClick,
|
||||
}) => {
|
||||
const baseClasses =
|
||||
'border-b border-[#D7D7D7] hover:bg-[#ECEEEF] dark:border-[#6A6A6A] dark:hover:bg-[#27282D]';
|
||||
'border-b border-border hover:bg-muted dark:border-border dark:hover:bg-muted';
|
||||
const cursorClass = onClick ? 'cursor-pointer' : '';
|
||||
|
||||
return (
|
||||
@@ -127,7 +127,7 @@ const TableHeader: React.FC<TableCellProps> = ({
|
||||
}
|
||||
};
|
||||
|
||||
const baseClasses = `px-2 py-3 text-sm font-medium text-gray-700 lg:px-3 dark:text-[#59636E] border-b border-[#D7D7D7] dark:border-[#6A6A6A] relative box-border ${getAlignmentClass()}`;
|
||||
const baseClasses = `px-2 py-3 text-sm font-medium text-gray-700 lg:px-3 dark:text-muted-foreground border-b border-border dark:border-border relative box-border ${getAlignmentClass()}`;
|
||||
const widthClasses = minWidth ? minWidth : '';
|
||||
|
||||
return (
|
||||
@@ -158,7 +158,7 @@ const TableCell: React.FC<TableCellProps> = ({
|
||||
}
|
||||
};
|
||||
|
||||
const baseClasses = `px-2 py-2 text-sm lg:px-3 dark:text-[#E0E0E0] box-border ${getAlignmentClass()}`;
|
||||
const baseClasses = `px-2 py-2 text-sm lg:px-3 box-border ${getAlignmentClass()}`;
|
||||
const widthClasses = minWidth ? minWidth : '';
|
||||
|
||||
return (
|
||||
|
||||
@@ -176,9 +176,7 @@ export default function SpeakButton({ text }: { text: string }) {
|
||||
<button
|
||||
type="button"
|
||||
className={`flex cursor-pointer items-center justify-center rounded-full p-2 ${
|
||||
isSpeaking || isLoading
|
||||
? 'dark:bg-purple-taupe bg-[#EEEEEE]'
|
||||
: 'bg-white-3000 dark:hover:bg-purple-taupe hover:bg-[#EEEEEE] dark:bg-transparent'
|
||||
isSpeaking || isLoading ? 'bg-accent' : 'hover:bg-accent bg-transparent'
|
||||
}`}
|
||||
onClick={handleSpeakClick}
|
||||
aria-label={
|
||||
|
||||
@@ -52,7 +52,7 @@ const ToggleSwitch: React.FC<ToggleSwitchProps> = ({
|
||||
>
|
||||
{label && (
|
||||
<span
|
||||
className={`text-eerie-black dark:text-white ${
|
||||
className={`text-foreground dark:text-white ${
|
||||
labelPosition === 'left' ? 'mr-3' : 'ml-3'
|
||||
}`}
|
||||
>
|
||||
|
||||
@@ -137,7 +137,7 @@ export default function ToolsPopup({
|
||||
const popupContent = (
|
||||
<div
|
||||
ref={popupRef}
|
||||
className="border-light-silver bg-lotion dark:border-dim-gray dark:bg-charleston-green-2 fixed z-50 rounded-lg border shadow-[0px_9px_46px_8px_#0000001F,0px_24px_38px_3px_#00000024,0px_11px_15px_-7px_#00000033]"
|
||||
className="border-border bg-background dark:border-border dark:bg-card fixed z-50 rounded-lg border shadow-[0px_9px_46px_8px_#0000001F,0px_24px_38px_3px_#00000024,0px_11px_15px_-7px_#00000033]"
|
||||
style={{
|
||||
top: popupPosition.showAbove ? popupPosition.top : undefined,
|
||||
bottom: popupPosition.showAbove
|
||||
@@ -163,7 +163,7 @@ export default function ToolsPopup({
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
placeholder={t('settings.tools.searchPlaceholder')}
|
||||
labelBgClassName="bg-lotion dark:bg-charleston-green-2"
|
||||
labelBgClassName="bg-background dark:bg-card"
|
||||
borderVariant="thin"
|
||||
className="mb-4"
|
||||
/>
|
||||
@@ -174,8 +174,8 @@ export default function ToolsPopup({
|
||||
<div className="h-6 w-6 animate-spin rounded-full border-b-2 border-gray-900 dark:border-white"></div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="dark:border-dim-gray mx-4 grow overflow-hidden rounded-md border border-[#D9D9D9]">
|
||||
<div className="h-full overflow-y-auto scrollbar-overlay">
|
||||
<div className="dark:border-border mx-4 grow overflow-hidden rounded-md border border-[#D9D9D9]">
|
||||
<div className="scrollbar-overlay h-full overflow-y-auto">
|
||||
{filteredTools.length === 0 ? (
|
||||
<div className="flex h-full flex-col items-center justify-center py-8">
|
||||
<img
|
||||
@@ -192,7 +192,7 @@ export default function ToolsPopup({
|
||||
<div
|
||||
key={tool.id}
|
||||
onClick={() => updateToolStatus(tool.id, !tool.status)}
|
||||
className="dark:border-dim-gray dark:hover:bg-charleston-green-3 flex items-center justify-between border-b border-[#D9D9D9] p-3 hover:bg-gray-100"
|
||||
className="dark:border-border dark:hover:bg-accent hover:bg-accent flex items-center justify-between border-b border-[#D9D9D9] p-3"
|
||||
>
|
||||
<div className="mr-3 flex grow items-center">
|
||||
<img
|
||||
@@ -208,7 +208,7 @@ export default function ToolsPopup({
|
||||
</div>
|
||||
<div className="flex shrink-0 items-center">
|
||||
<div
|
||||
className={`flex h-4 w-4 items-center justify-center rounded-xs border-2 border-[#C6C6C6] p-[0.5px] dark:border-[#757783]`}
|
||||
className={`dark:border-border flex h-4 w-4 items-center justify-center rounded-xs border-2 border-[#C6C6C6] p-[0.5px]`}
|
||||
>
|
||||
{tool.status && (
|
||||
<img
|
||||
@@ -230,7 +230,7 @@ export default function ToolsPopup({
|
||||
<div className="shrink-0 p-4 opacity-75 transition-opacity duration-200 hover:opacity-100">
|
||||
<a
|
||||
href="/settings/tools"
|
||||
className="text-purple-30 inline-flex items-center text-base font-medium"
|
||||
className="text-primary inline-flex items-center text-base font-medium"
|
||||
>
|
||||
{t('settings.tools.manageTools')}
|
||||
<img
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { useState } from 'react';
|
||||
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { selectUploadTasks, dismissUploadTask } from '../upload/uploadSlice';
|
||||
import ChevronDown from '../assets/chevron-down.svg';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
|
||||
import CheckCircleFilled from '../assets/check-circle-filled.svg';
|
||||
import ChevronDown from '../assets/chevron-down.svg';
|
||||
import WarnIcon from '../assets/warn.svg';
|
||||
import { dismissUploadTask, selectUploadTasks } from '../upload/uploadSlice';
|
||||
|
||||
const PROGRESS_RADIUS = 10;
|
||||
const PROGRESS_CIRCUMFERENCE = 2 * Math.PI * PROGRESS_RADIUS;
|
||||
@@ -65,23 +65,17 @@ export default function UploadToast() {
|
||||
return (
|
||||
<div
|
||||
key={task.id}
|
||||
className={`w-[271px] overflow-hidden rounded-2xl border border-[#00000021] shadow-[0px_24px_48px_0px_#00000029] transition-all duration-300 ${
|
||||
task.status === 'completed'
|
||||
? 'bg-[#FBFBFB] dark:bg-[#26272E]'
|
||||
: task.status === 'failed'
|
||||
? 'bg-[#FBFBFB] dark:bg-[#26272E]'
|
||||
: 'bg-[#FBFBFB] dark:bg-[#26272E]'
|
||||
}`}
|
||||
className={`border-border bg-card w-[271px] overflow-hidden rounded-2xl border shadow-[0px_24px_48px_0px_#00000029] transition-all duration-300`}
|
||||
>
|
||||
<div className="flex flex-col">
|
||||
<div
|
||||
className={`flex items-center justify-between px-4 py-3 ${
|
||||
task.status !== 'failed'
|
||||
? 'bg-[#FBF2FE] dark:bg-transparent'
|
||||
: ''
|
||||
? 'bg-accent/50 dark:bg-muted'
|
||||
: 'bg-destructive/10 dark:bg-destructive/10'
|
||||
}`}
|
||||
>
|
||||
<h3 className="font-inter text-[14px] leading-[16.5px] font-medium text-black dark:text-[#DCDCDC]">
|
||||
<h3 className="font-inter dark:text-foreground text-[14px] leading-[16.5px] font-medium text-black">
|
||||
{getStatusHeading(task.status)}
|
||||
</h3>
|
||||
<div className="flex items-center gap-1">
|
||||
@@ -147,7 +141,7 @@ export default function UploadToast() {
|
||||
>
|
||||
<div className="flex items-center justify-between px-5 py-3">
|
||||
<p
|
||||
className="font-inter max-w-[200px] truncate text-[13px] leading-[16.5px] font-normal text-black dark:text-[#B7BAB8]"
|
||||
className="font-inter dark:text-muted-foreground max-w-[200px] truncate text-[13px] leading-[16.5px] font-normal text-black"
|
||||
title={task.fileName}
|
||||
>
|
||||
{task.fileName}
|
||||
@@ -159,7 +153,7 @@ export default function UploadToast() {
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
className="h-6 w-6 flex-shrink-0 text-[#7D54D1]"
|
||||
className="h-6 w-6 shrink-0 text-[#7D54D1]"
|
||||
role="progressbar"
|
||||
aria-valuemin={0}
|
||||
aria-valuemax={100}
|
||||
@@ -172,7 +166,7 @@ export default function UploadToast() {
|
||||
)}
|
||||
>
|
||||
<circle
|
||||
className="text-gray-300 dark:text-gray-700"
|
||||
className="text-muted dark:text-muted-foreground/30"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
cx="12"
|
||||
@@ -200,7 +194,7 @@ export default function UploadToast() {
|
||||
<img
|
||||
src={CheckCircleFilled}
|
||||
alt=""
|
||||
className="h-6 w-6 flex-shrink-0"
|
||||
className="h-6 w-6 shrink-0"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
)}
|
||||
@@ -209,7 +203,7 @@ export default function UploadToast() {
|
||||
<img
|
||||
src={WarnIcon}
|
||||
alt=""
|
||||
className="h-6 w-6 flex-shrink-0"
|
||||
className="h-6 w-6 shrink-0"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
export type DropdownOptionBase = {
|
||||
id?: string;
|
||||
type?: string;
|
||||
};
|
||||
|
||||
export type StringOption = string;
|
||||
export type NameIdOption = { name: string; id: string } & DropdownOptionBase;
|
||||
export type LabelValueOption = {
|
||||
label: string;
|
||||
value: string;
|
||||
} & DropdownOptionBase;
|
||||
export type ValueDescriptionOption = {
|
||||
value: number;
|
||||
description: string;
|
||||
} & DropdownOptionBase;
|
||||
|
||||
export type DropdownOption =
|
||||
| StringOption
|
||||
| NameIdOption
|
||||
| LabelValueOption
|
||||
| ValueDescriptionOption;
|
||||
|
||||
export type DropdownSelectedValue = DropdownOption | null;
|
||||
|
||||
export type OnSelectHandler<T extends DropdownOption = DropdownOption> = (
|
||||
value: T,
|
||||
) => void;
|
||||
|
||||
export interface DropdownProps<T extends DropdownOption = DropdownOption> {
|
||||
options: T[];
|
||||
selectedValue: DropdownSelectedValue;
|
||||
onSelect: OnSelectHandler<T>;
|
||||
size?: string;
|
||||
rounded?: 'xl' | '3xl';
|
||||
buttonClassName?: string;
|
||||
optionsClassName?: string;
|
||||
border?: 'border' | 'border-2';
|
||||
showEdit?: boolean;
|
||||
onEdit?: (value: NameIdOption) => void;
|
||||
showDelete?: boolean | ((option: T) => boolean);
|
||||
onDelete?: (id: string) => void;
|
||||
placeholder?: string;
|
||||
placeholderClassName?: string;
|
||||
contentSize?: string;
|
||||
}
|
||||
@@ -8,8 +8,8 @@ function Input({ className, type, ...props }: React.ComponentProps<'input'>) {
|
||||
type={type}
|
||||
data-slot="input"
|
||||
className={cn(
|
||||
'text-foreground file:text-foreground placeholder:text-muted-foreground border-silver h-[42px] w-full min-w-0 rounded-md border bg-transparent px-3 py-2 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm',
|
||||
'dark:border-silver/40 dark:bg-transparent dark:text-white dark:placeholder:text-gray-400',
|
||||
'text-foreground file:text-foreground placeholder:text-muted-foreground border-border h-[42px] w-full min-w-0 rounded-md border bg-transparent px-3 py-2 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm',
|
||||
'dark:border-border dark:text-white dark:placeholder:text-gray-400',
|
||||
'selection:bg-primary selection:text-primary-foreground',
|
||||
'focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]',
|
||||
'aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive',
|
||||
|
||||
@@ -70,7 +70,7 @@ export function MultiSelect({
|
||||
role="combobox"
|
||||
aria-expanded={open}
|
||||
className={cn(
|
||||
'h-auto min-h-[2.5rem] w-full justify-between border-[#E5E5E5] bg-white py-1.5 hover:bg-gray-50 dark:border-[#3A3A3A] dark:bg-[#2C2C2C] dark:hover:bg-[#383838]',
|
||||
'border-border bg-card hover:bg-accent h-auto min-h-10 w-full justify-between py-1.5',
|
||||
!selected.length && 'text-gray-500 dark:text-gray-400',
|
||||
className,
|
||||
)}
|
||||
@@ -85,7 +85,7 @@ export function MultiSelect({
|
||||
return (
|
||||
<span
|
||||
key={option?.value || label}
|
||||
className="dark:bg-purple-30/30 bg-violets-are-blue/20 inline-flex max-w-[calc(100%-1rem)] items-center gap-1 rounded-md px-2 py-0.5 text-xs font-medium text-purple-700 dark:text-purple-300"
|
||||
className="bg-primary/20 dark:bg-primary/30 inline-flex max-w-[calc(100%-1rem)] min-w-0 items-center gap-1 rounded-md px-2 py-0.5 text-xs font-medium text-purple-700 dark:text-purple-300"
|
||||
>
|
||||
<span className="truncate">{label}</span>
|
||||
<span
|
||||
@@ -124,7 +124,7 @@ export function MultiSelect({
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent
|
||||
className="w-(--radix-popover-trigger-width) border-[#E5E5E5] bg-white p-0 dark:border-[#3A3A3A] dark:bg-[#2C2C2C]"
|
||||
className="border-border bg-card w-(--radix-popover-trigger-width) p-0"
|
||||
align="start"
|
||||
>
|
||||
<Command className="bg-transparent">
|
||||
@@ -141,13 +141,13 @@ export function MultiSelect({
|
||||
key={option.value}
|
||||
value={option.label}
|
||||
onSelect={() => handleSelect(option.value)}
|
||||
className="cursor-pointer dark:hover:bg-[#383838]"
|
||||
className="cursor-pointer"
|
||||
>
|
||||
<div
|
||||
className={cn(
|
||||
'mr-2 flex h-4 w-4 items-center justify-center rounded-sm border-2',
|
||||
isSelected
|
||||
? 'border-purple-30 bg-purple-30 text-white'
|
||||
? 'border-primary bg-primary text-white'
|
||||
: 'border-gray-400 dark:border-gray-500',
|
||||
)}
|
||||
>
|
||||
|
||||
@@ -37,11 +37,11 @@ function SelectTrigger({
|
||||
data-slot="select-trigger"
|
||||
data-size={size}
|
||||
className={cn(
|
||||
"aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive flex w-fit items-center justify-between gap-2 rounded-md border px-3 py-2 text-sm whitespace-nowrap shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 data-[size=default]:h-9 data-[size=sm]:h-8 data-[size=lg]:h-[42px] *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-gray-600 dark:[&_svg:not([class*='text-'])]:text-gray-400",
|
||||
"aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive flex w-fit items-center justify-between gap-2 rounded-md border px-3 py-2 text-sm whitespace-nowrap shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 data-[size=default]:h-9 data-[size=lg]:h-[42px] data-[size=sm]:h-8 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-gray-600 dark:[&_svg:not([class*='text-'])]:text-gray-400",
|
||||
variant === 'default' &&
|
||||
'border-light-silver bg-white focus-visible:ring-purple-30/50 hover:bg-gray-50 data-placeholder:text-gray-500 dark:border-[#3A3A3A] dark:bg-[#2C2C2C] dark:hover:bg-[#383838] dark:data-placeholder:text-gray-400',
|
||||
'border-border bg-card focus-visible:ring-ring/50 hover:bg-accent data-placeholder:text-muted-foreground',
|
||||
variant === 'ghost' &&
|
||||
'border-silver bg-transparent focus-visible:ring-purple-30/50 hover:bg-gray-50 data-[state=open]:bg-gray-50 data-placeholder:text-gray-500 dark:border-silver/40 dark:bg-transparent dark:hover:bg-white/5 dark:data-[state=open]:bg-white/10 dark:data-placeholder:text-gray-400',
|
||||
'border-border focus-visible:ring-ring/50 hover:bg-accent data-[state=open]:bg-muted data-placeholder:text-muted-foreground dark:border-border dark:hover:bg-accent dark:data-[state=open]:bg-muted bg-transparent',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -66,7 +66,7 @@ function SelectContent({
|
||||
<SelectPrimitive.Content
|
||||
data-slot="select-content"
|
||||
className={cn(
|
||||
'border-light-silver bg-lotion data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 relative z-200 max-h-(--radix-select-content-available-height) min-w-32 origin-(--radix-select-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border text-gray-900 shadow-md dark:border-[#3A3A3A] dark:bg-[#2C2C2C] dark:text-white',
|
||||
'border-border bg-card data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 relative z-200 max-h-(--radix-select-content-available-height) min-w-32 origin-(--radix-select-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border text-gray-900 shadow-md dark:text-white',
|
||||
position === 'popper' &&
|
||||
'data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1',
|
||||
className,
|
||||
@@ -113,7 +113,7 @@ function SelectItem({
|
||||
<SelectPrimitive.Item
|
||||
data-slot="select-item"
|
||||
className={cn(
|
||||
"[&_svg:not([class*='text-'])]:text-muted-foreground relative flex w-full cursor-default items-center gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm outline-hidden select-none hover:bg-gray-100 data-disabled:pointer-events-none data-disabled:opacity-50 dark:hover:bg-[#383838] [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2",
|
||||
"[&_svg:not([class*='text-'])]:text-muted-foreground hover:bg-muted relative flex w-full cursor-default items-center gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm outline-hidden select-none data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
|
||||
@@ -1,29 +1,29 @@
|
||||
import * as React from "react"
|
||||
import * as SheetPrimitive from "@radix-ui/react-dialog"
|
||||
import { XIcon } from "lucide-react"
|
||||
import * as React from 'react';
|
||||
import * as SheetPrimitive from '@radix-ui/react-dialog';
|
||||
import { XIcon } from 'lucide-react';
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
function Sheet({ ...props }: React.ComponentProps<typeof SheetPrimitive.Root>) {
|
||||
return <SheetPrimitive.Root data-slot="sheet" {...props} />
|
||||
return <SheetPrimitive.Root data-slot="sheet" {...props} />;
|
||||
}
|
||||
|
||||
function SheetTrigger({
|
||||
...props
|
||||
}: React.ComponentProps<typeof SheetPrimitive.Trigger>) {
|
||||
return <SheetPrimitive.Trigger data-slot="sheet-trigger" {...props} />
|
||||
return <SheetPrimitive.Trigger data-slot="sheet-trigger" {...props} />;
|
||||
}
|
||||
|
||||
function SheetClose({
|
||||
...props
|
||||
}: React.ComponentProps<typeof SheetPrimitive.Close>) {
|
||||
return <SheetPrimitive.Close data-slot="sheet-close" {...props} />
|
||||
return <SheetPrimitive.Close data-slot="sheet-close" {...props} />;
|
||||
}
|
||||
|
||||
function SheetPortal({
|
||||
...props
|
||||
}: React.ComponentProps<typeof SheetPrimitive.Portal>) {
|
||||
return <SheetPrimitive.Portal data-slot="sheet-portal" {...props} />
|
||||
return <SheetPrimitive.Portal data-slot="sheet-portal" {...props} />;
|
||||
}
|
||||
|
||||
function SheetOverlay({
|
||||
@@ -34,23 +34,23 @@ function SheetOverlay({
|
||||
<SheetPrimitive.Overlay
|
||||
data-slot="sheet-overlay"
|
||||
className={cn(
|
||||
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",
|
||||
className
|
||||
'data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function SheetContent({
|
||||
className,
|
||||
children,
|
||||
side = "right",
|
||||
side = 'right',
|
||||
showCloseButton = true,
|
||||
...props
|
||||
}: React.ComponentProps<typeof SheetPrimitive.Content> & {
|
||||
side?: "top" | "right" | "bottom" | "left"
|
||||
showCloseButton?: boolean
|
||||
side?: 'top' | 'right' | 'bottom' | 'left';
|
||||
showCloseButton?: boolean;
|
||||
}) {
|
||||
return (
|
||||
<SheetPortal>
|
||||
@@ -58,16 +58,16 @@ function SheetContent({
|
||||
<SheetPrimitive.Content
|
||||
data-slot="sheet-content"
|
||||
className={cn(
|
||||
"bg-background data-[state=open]:animate-in data-[state=closed]:animate-out fixed z-50 flex flex-col gap-4 shadow-lg transition ease-in-out data-[state=closed]:duration-300 data-[state=open]:duration-500",
|
||||
side === "right" &&
|
||||
"data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right inset-y-0 right-0 h-full w-3/4 border-l sm:max-w-sm",
|
||||
side === "left" &&
|
||||
"data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left inset-y-0 left-0 h-full w-3/4 border-r sm:max-w-sm",
|
||||
side === "top" &&
|
||||
"data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top inset-x-0 top-0 h-auto border-b",
|
||||
side === "bottom" &&
|
||||
"data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom inset-x-0 bottom-0 h-auto border-t",
|
||||
className
|
||||
'bg-background data-[state=open]:animate-in data-[state=closed]:animate-out fixed z-50 flex flex-col gap-4 shadow-lg transition ease-in-out data-[state=closed]:duration-300 data-[state=open]:duration-500',
|
||||
side === 'right' &&
|
||||
'data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right inset-y-0 right-0 h-full w-3/4 border-l sm:max-w-sm',
|
||||
side === 'left' &&
|
||||
'data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left inset-y-0 left-0 h-full w-3/4 border-r sm:max-w-sm',
|
||||
side === 'top' &&
|
||||
'data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top inset-x-0 top-0 h-auto border-b',
|
||||
side === 'bottom' &&
|
||||
'data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom inset-x-0 bottom-0 h-auto border-t',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
@@ -80,27 +80,27 @@ function SheetContent({
|
||||
)}
|
||||
</SheetPrimitive.Content>
|
||||
</SheetPortal>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function SheetHeader({ className, ...props }: React.ComponentProps<"div">) {
|
||||
function SheetHeader({ className, ...props }: React.ComponentProps<'div'>) {
|
||||
return (
|
||||
<div
|
||||
data-slot="sheet-header"
|
||||
className={cn("flex flex-col gap-1.5 p-4", className)}
|
||||
className={cn('flex flex-col gap-1.5 p-4', className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function SheetFooter({ className, ...props }: React.ComponentProps<"div">) {
|
||||
function SheetFooter({ className, ...props }: React.ComponentProps<'div'>) {
|
||||
return (
|
||||
<div
|
||||
data-slot="sheet-footer"
|
||||
className={cn("mt-auto flex flex-col gap-2 p-4", className)}
|
||||
className={cn('mt-auto flex flex-col gap-2 p-4', className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function SheetTitle({
|
||||
@@ -110,10 +110,10 @@ function SheetTitle({
|
||||
return (
|
||||
<SheetPrimitive.Title
|
||||
data-slot="sheet-title"
|
||||
className={cn("text-foreground font-semibold", className)}
|
||||
className={cn('text-foreground font-semibold', className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function SheetDescription({
|
||||
@@ -123,10 +123,10 @@ function SheetDescription({
|
||||
return (
|
||||
<SheetPrimitive.Description
|
||||
data-slot="sheet-description"
|
||||
className={cn("text-muted-foreground text-sm", className)}
|
||||
className={cn('text-muted-foreground text-sm', className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export {
|
||||
@@ -138,4 +138,4 @@ export {
|
||||
SheetFooter,
|
||||
SheetTitle,
|
||||
SheetDescription,
|
||||
}
|
||||
};
|
||||
|
||||
@@ -263,7 +263,7 @@ export default function Conversation() {
|
||||
/>
|
||||
</div>
|
||||
|
||||
<p className="text-gray-4000 dark:text-sonic-silver hidden w-full self-center bg-transparent py-2 text-center text-xs md:inline">
|
||||
<p className="text-muted-foreground hidden w-full self-center bg-transparent py-2 text-center text-xs md:inline">
|
||||
{t('tagline')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -140,9 +140,9 @@ const ConversationBubble = forwardRef<
|
||||
<div
|
||||
key={index}
|
||||
title={file.fileName}
|
||||
className="dark:text-bright-gray flex items-center rounded-xl bg-[#EFF3F4] p-2 text-[14px] text-[#5D5D5D] dark:bg-[#393B3D]"
|
||||
className="dark:text-foreground dark:bg-accent flex items-center rounded-xl bg-[#EFF3F4] p-2 text-[14px] text-[#5D5D5D]"
|
||||
>
|
||||
<div className="bg-purple-30 mr-2 items-center justify-center rounded-lg p-[5.5px]">
|
||||
<div className="bg-primary mr-2 items-center justify-center rounded-lg p-[5.5px]">
|
||||
<img
|
||||
src={DocumentationDark}
|
||||
alt="Attachment"
|
||||
@@ -201,7 +201,7 @@ const ConversationBubble = forwardRef<
|
||||
setIsEditClicked(true);
|
||||
setEditInputBox(message ?? '');
|
||||
}}
|
||||
className={`hover:bg-light-silver mt-3 flex h-fit shrink-0 cursor-pointer items-center rounded-full p-2 pt-1.5 pl-1.5 dark:hover:bg-[#35363B] ${isEditClicked ? 'visible' : 'invisible group-hover:visible'}`}
|
||||
className={`hover:bg-accent dark:hover:bg-accent mt-3 flex h-fit shrink-0 cursor-pointer items-center rounded-full p-2 pt-1.5 pl-1.5 ${isEditClicked ? 'visible' : 'invisible group-hover:visible'}`}
|
||||
>
|
||||
<img src={Edit} alt="Edit" className="cursor-pointer" />
|
||||
</button>
|
||||
@@ -226,17 +226,17 @@ const ConversationBubble = forwardRef<
|
||||
}}
|
||||
rows={5}
|
||||
value={editInputBox}
|
||||
className="border-silver text-carbon dark:border-philippine-grey dark:bg-raisin-black dark:text-chinese-white w-full resize-none rounded-3xl border px-4 py-3 text-base leading-relaxed focus:outline-hidden"
|
||||
className="border-border text-carbon dark:border-philippine-grey dark:text-foreground w-full resize-none rounded-3xl border px-4 py-3 text-base leading-relaxed focus:outline-hidden"
|
||||
/>
|
||||
<div className="flex items-center justify-end gap-2">
|
||||
<button
|
||||
className="text-purple-30 hover:bg-gainsboro hover:text-chinese-black-2 dark:hover:bg-onyx-2 rounded-full px-4 py-2 text-sm font-semibold transition-colors dark:hover:text-[#B9BCBE]"
|
||||
className="text-primary hover:bg-muted hover:text-foreground dark:hover:bg-accent dark:hover:text-foreground rounded-full px-4 py-2 text-sm font-semibold transition-colors"
|
||||
onClick={() => setIsEditClicked(false)}
|
||||
>
|
||||
{t('conversation.edit.cancel')}
|
||||
</button>
|
||||
<button
|
||||
className="bg-purple-30 hover:bg-violets-are-blue dark:hover:bg-royal-purple rounded-full px-4 py-2 text-sm font-medium text-white transition-colors"
|
||||
className="bg-primary hover:bg-primary/90 dark:hover:bg-primary/90 rounded-full px-4 py-2 text-sm font-medium text-white transition-colors"
|
||||
onClick={handleEditClick}
|
||||
>
|
||||
{t('conversation.edit.update')}
|
||||
@@ -305,7 +305,7 @@ const ConversationBubble = forwardRef<
|
||||
bubble = (
|
||||
<div
|
||||
ref={ref}
|
||||
className={`flex flex-wrap self-start ${className} group dark:text-bright-gray flex-col`}
|
||||
className={`flex flex-wrap self-start ${className} group dark:text-foreground flex-col`}
|
||||
>
|
||||
{DisableSourceFE ||
|
||||
type === 'ERROR' ||
|
||||
@@ -332,9 +332,13 @@ const ConversationBubble = forwardRef<
|
||||
<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} id={`source-${index}`} className="relative transition-all duration-300">
|
||||
<div
|
||||
key={index}
|
||||
id={`source-${index}`}
|
||||
className="relative transition-all duration-300"
|
||||
>
|
||||
<div
|
||||
className="bg-gray-1000 dark:bg-gun-metal h-28 cursor-pointer rounded-4xl p-4 hover:bg-[#F1F1F1] dark:hover:bg-[#2C2E3C]"
|
||||
className="bg-muted hover:bg-accent dark:bg-answer-bubble dark:hover:bg-muted h-28 cursor-pointer rounded-4xl p-4"
|
||||
onMouseOver={() => setActiveTooltip(index)}
|
||||
onMouseOut={() => setActiveTooltip(null)}
|
||||
>
|
||||
@@ -344,7 +348,7 @@ const ConversationBubble = forwardRef<
|
||||
<div
|
||||
className={`mt-3.5 flex flex-row items-center gap-1.5 underline-offset-2 ${
|
||||
source.link && source.link !== 'local'
|
||||
? 'hover:text-[#007DFF] hover:underline dark:hover:text-[#48A0FF]'
|
||||
? 'hover:text-[#007DFF] hover:underline dark:hover:text-blue-400'
|
||||
: ''
|
||||
}`}
|
||||
onClick={() =>
|
||||
@@ -378,7 +382,7 @@ const ConversationBubble = forwardRef<
|
||||
</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={`dark:bg-card dark:text-foreground 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)}
|
||||
>
|
||||
@@ -391,7 +395,7 @@ const ConversationBubble = forwardRef<
|
||||
))}
|
||||
{(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-4xl p-4 hover:bg-[#F1F1F1] hover:text-[#6D3ECC] dark:hover:bg-[#2C2E3C] dark:hover:text-[#8C67D7]"
|
||||
className="bg-muted text-primary hover:bg-accent hover:text-primary dark:bg-answer-bubble dark:hover:bg-muted dark:hover:text-primary flex h-28 cursor-pointer flex-col-reverse rounded-4xl p-4"
|
||||
onClick={() => setIsSidebarOpen(true)}
|
||||
>
|
||||
<p className="ellipsis-text h-22 text-xs">
|
||||
@@ -469,9 +473,9 @@ const ConversationBubble = forwardRef<
|
||||
</p>
|
||||
</div>
|
||||
<div
|
||||
className={`fade-in-bubble bg-gray-1000 dark:bg-gun-metal mr-5 flex max-w-full rounded-[18px] px-6 py-4.5 ${
|
||||
className={`fade-in-bubble bg-answer-bubble mr-5 flex max-w-full rounded-[18px] px-6 py-4.5 ${
|
||||
type === 'ERROR'
|
||||
? 'text-red-3000 dark:border-red-2000 relative flex-row items-center rounded-full border border-transparent bg-[#FFE7E7] p-2 py-5 text-sm font-normal dark:text-white'
|
||||
? 'text-destructive/80 dark:border-destructive dark:bg-destructive/15 relative flex-row items-center rounded-full border border-transparent bg-[#FFE7E7] p-2 py-5 text-sm font-normal dark:text-white'
|
||||
: 'flex-col rounded-3xl'
|
||||
}`}
|
||||
>
|
||||
@@ -495,11 +499,26 @@ const ConversationBubble = forwardRef<
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
const el = document.getElementById(`source-${sourceIdx}`);
|
||||
const el = document.getElementById(
|
||||
`source-${sourceIdx}`,
|
||||
);
|
||||
if (el) {
|
||||
el.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
||||
el.classList.add('ring-2', 'ring-purple-500');
|
||||
setTimeout(() => el.classList.remove('ring-2', 'ring-purple-500'), 2000);
|
||||
el.scrollIntoView({
|
||||
behavior: 'smooth',
|
||||
block: 'center',
|
||||
});
|
||||
el.classList.add(
|
||||
'ring-2',
|
||||
'ring-purple-500',
|
||||
);
|
||||
setTimeout(
|
||||
() =>
|
||||
el.classList.remove(
|
||||
'ring-2',
|
||||
'ring-purple-500',
|
||||
),
|
||||
2000,
|
||||
);
|
||||
}
|
||||
}}
|
||||
className="mx-0.5 inline-flex h-5 min-w-5 items-center justify-center rounded-full bg-purple-100 px-1.5 text-xs font-semibold text-purple-700 transition-colors hover:bg-purple-200 dark:bg-purple-900/40 dark:text-purple-300 dark:hover:bg-purple-900/60"
|
||||
@@ -509,7 +528,15 @@ const ConversationBubble = forwardRef<
|
||||
</button>
|
||||
);
|
||||
}
|
||||
return <a href={href} target="_blank" rel="noopener noreferrer">{children}</a>;
|
||||
return (
|
||||
<a
|
||||
href={href}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
{children}
|
||||
</a>
|
||||
);
|
||||
},
|
||||
code(props) {
|
||||
const {
|
||||
@@ -525,9 +552,9 @@ const ConversationBubble = forwardRef<
|
||||
const language = match ? match[1] : '';
|
||||
|
||||
return match ? (
|
||||
<div className="group border-light-silver dark:border-raisin-black relative overflow-hidden rounded-[14px] border">
|
||||
<div className="bg-platinum dark:bg-eerie-black-2 flex items-center justify-between px-2 py-1">
|
||||
<span className="text-just-black dark:text-chinese-white text-xs font-medium">
|
||||
<div className="group border-border relative overflow-hidden rounded-[14px] border">
|
||||
<div className="bg-platinum flex items-center justify-between px-2 py-1">
|
||||
<span className="text-foreground dark:text-foreground text-xs font-medium">
|
||||
{language}
|
||||
</span>
|
||||
<CopyButton
|
||||
@@ -554,7 +581,7 @@ const ConversationBubble = forwardRef<
|
||||
</SyntaxHighlighter>
|
||||
</div>
|
||||
) : (
|
||||
<code className="dark:bg-independence dark:text-bright-gray rounded-[6px] bg-gray-200 px-2 py-1 text-xs font-normal whitespace-pre-line">
|
||||
<code className="dark:bg-accent dark:text-foreground rounded-[6px] bg-gray-200 px-2 py-1 text-xs font-normal whitespace-pre-line">
|
||||
{children}
|
||||
</code>
|
||||
);
|
||||
@@ -579,8 +606,8 @@ const ConversationBubble = forwardRef<
|
||||
},
|
||||
table({ children }) {
|
||||
return (
|
||||
<div className="border-silver/40 dark:border-silver/40 relative overflow-x-auto rounded-lg border">
|
||||
<table className="dark:text-bright-gray w-full text-left text-gray-700">
|
||||
<div className="border-silver/40 dark:border-border relative overflow-x-auto rounded-lg border">
|
||||
<table className="dark:text-foreground w-full text-left text-gray-700">
|
||||
{children}
|
||||
</table>
|
||||
</div>
|
||||
@@ -588,14 +615,14 @@ const ConversationBubble = forwardRef<
|
||||
},
|
||||
thead({ children }) {
|
||||
return (
|
||||
<thead className="dark:text-bright-gray bg-gray-50 text-xs text-gray-900 uppercase dark:bg-[#26272E]/50">
|
||||
<thead className="bg-muted text-foreground text-xs uppercase">
|
||||
{children}
|
||||
</thead>
|
||||
);
|
||||
},
|
||||
tr({ children }) {
|
||||
return (
|
||||
<tr className="dark:border-silver/40 border-b border-gray-200 odd:bg-white even:bg-gray-50 dark:odd:bg-[#26272E] dark:even:bg-[#26272E]/50">
|
||||
<tr className="border-border odd:bg-card even:bg-muted border-b">
|
||||
{children}
|
||||
</tr>
|
||||
);
|
||||
@@ -691,7 +718,9 @@ const ConversationBubble = forwardRef<
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
const blob = new Blob([message], { type: 'text/markdown' });
|
||||
const blob = new Blob([message], {
|
||||
type: 'text/markdown',
|
||||
});
|
||||
const url = URL.createObjectURL(blob);
|
||||
const link = document.createElement('a');
|
||||
link.href = url;
|
||||
@@ -703,8 +732,17 @@ const ConversationBubble = forwardRef<
|
||||
aria-label="Export as Markdown"
|
||||
title="Export as Markdown"
|
||||
>
|
||||
<svg className="h-5 w-5 stroke-gray-4000" fill="none" viewBox="0 0 24 24" strokeWidth={1.5}>
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M3 16.5v2.25A2.25 2.25 0 005.25 21h13.5A2.25 2.25 0 0021 18.75V16.5M16.5 12L12 16.5m0 0L7.5 12m4.5 4.5V3" />
|
||||
<svg
|
||||
className="stroke-gray-4000 h-5 w-5"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
strokeWidth={1.5}
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
d="M3 16.5v2.25A2.25 2.25 0 005.25 21h13.5A2.25 2.25 0 0021 18.75V16.5M16.5 12L12 16.5m0 0L7.5 12m4.5 4.5V3"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
@@ -717,7 +755,7 @@ const ConversationBubble = forwardRef<
|
||||
<div className="relative mr-2 flex items-center justify-center">
|
||||
<button
|
||||
type="button"
|
||||
className="bg-white-3000 dark:hover:bg-purple-taupe flex cursor-pointer items-center justify-center rounded-full p-2 hover:bg-[#EEEEEE] dark:bg-transparent"
|
||||
className="hover:bg-accent flex cursor-pointer items-center justify-center rounded-full bg-transparent p-2"
|
||||
onClick={() => {
|
||||
if (feedback === 'LIKE') {
|
||||
handleFeedback?.(null);
|
||||
@@ -730,7 +768,7 @@ const ConversationBubble = forwardRef<
|
||||
}
|
||||
>
|
||||
<Like
|
||||
className={`${feedback === 'LIKE' ? 'fill-white-3000 stroke-purple-30 dark:fill-transparent' : 'stroke-gray-4000 fill-none'}`}
|
||||
className={`${feedback === 'LIKE' ? 'stroke-primary fill-white dark:fill-transparent' : 'stroke-muted-foreground fill-none'}`}
|
||||
></Like>
|
||||
</button>
|
||||
</div>
|
||||
@@ -738,7 +776,7 @@ const ConversationBubble = forwardRef<
|
||||
<div className="relative mr-2 flex items-center justify-center">
|
||||
<button
|
||||
type="button"
|
||||
className="bg-white-3000 dark:hover:bg-purple-taupe flex cursor-pointer items-center justify-center rounded-full p-2 hover:bg-[#EEEEEE] dark:bg-transparent"
|
||||
className="hover:bg-accent flex cursor-pointer items-center justify-center rounded-full bg-transparent p-2"
|
||||
onClick={() => {
|
||||
if (feedback === 'DISLIKE') {
|
||||
handleFeedback?.(null);
|
||||
@@ -753,7 +791,7 @@ const ConversationBubble = forwardRef<
|
||||
}
|
||||
>
|
||||
<Dislike
|
||||
className={`${feedback === 'DISLIKE' ? 'fill-white-3000 stroke-red-2000 dark:fill-transparent' : 'stroke-gray-4000 fill-none'}`}
|
||||
className={`${feedback === 'DISLIKE' ? 'stroke-destructive fill-white dark:fill-transparent' : 'stroke-muted-foreground fill-none'}`}
|
||||
></Dislike>
|
||||
</button>
|
||||
</div>
|
||||
@@ -806,7 +844,7 @@ function AllSources(sources: AllSourcesProps) {
|
||||
return (
|
||||
<div
|
||||
key={index}
|
||||
className={`group/card bg-gray-1000 relative w-full rounded-4xl p-4 transition-colors hover:bg-[#F1F1F1] dark:bg-[#28292E] dark:hover:bg-[#2C2E3C] ${
|
||||
className={`group/card bg-muted hover:bg-accent dark:bg-card dark:hover:bg-muted relative w-full rounded-4xl p-4 transition-colors ${
|
||||
isExternalSource ? 'cursor-pointer' : ''
|
||||
}`}
|
||||
onClick={() =>
|
||||
@@ -817,7 +855,7 @@ function AllSources(sources: AllSourcesProps) {
|
||||
title={source.title}
|
||||
className={`ellipsis-text text-left text-sm font-semibold wrap-break-word ${
|
||||
isExternalSource
|
||||
? 'group-hover/card:text-purple-30 dark:group-hover/card:text-[#8C67D7]'
|
||||
? 'group-hover/card:text-primary dark:group-hover/card:text-[#8C67D7]'
|
||||
: ''
|
||||
}`}
|
||||
>
|
||||
@@ -834,7 +872,7 @@ function AllSources(sources: AllSourcesProps) {
|
||||
/>
|
||||
)}
|
||||
</p>
|
||||
<p className="dark:text-chinese-silver mt-3 line-clamp-4 rounded-md text-left text-xs wrap-break-word text-black">
|
||||
<p className="dark:text-foreground mt-3 line-clamp-4 rounded-md text-left text-xs wrap-break-word text-black">
|
||||
{source.text}
|
||||
</p>
|
||||
</div>
|
||||
@@ -850,105 +888,105 @@ function ToolCalls({ toolCalls }: { toolCalls: ToolCallsType[] }) {
|
||||
const [isToolCallsOpen, setIsToolCallsOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<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"
|
||||
avatar={
|
||||
<img
|
||||
src={Sources}
|
||||
alt={'ToolCalls'}
|
||||
className="h-full w-full object-fill"
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<button
|
||||
className="flex flex-row items-center gap-2"
|
||||
onClick={() => setIsToolCallsOpen(!isToolCallsOpen)}
|
||||
>
|
||||
<p className="text-base font-semibold">Tool Calls</p>
|
||||
<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"
|
||||
avatar={
|
||||
<img
|
||||
src={ChevronDown}
|
||||
alt="ChevronDown"
|
||||
className={`h-4 w-4 transform transition-transform duration-200 dark:invert ${isToolCallsOpen ? 'rotate-180' : ''}`}
|
||||
src={Sources}
|
||||
alt={'ToolCalls'}
|
||||
className="h-full w-full object-fill"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
{isToolCallsOpen && (
|
||||
<div className="fade-in mr-5 ml-3 w-[90vw] md:w-[70vw] lg:w-full">
|
||||
<div className="grid grid-cols-1 gap-2">
|
||||
{toolCalls.map((toolCall, index) => (
|
||||
<Accordion
|
||||
key={`tool-call-${index}`}
|
||||
title={`${toolCall.tool_name} - ${toolCall.action_name.substring(0, toolCall.action_name.lastIndexOf('_'))}`}
|
||||
className="bg-gray-1000 dark:bg-gun-metal w-full rounded-4xl hover:bg-[#F1F1F1] dark:hover:bg-[#2C2E3C]"
|
||||
titleClassName="px-6 py-2 text-sm font-semibold"
|
||||
>
|
||||
<div className="flex flex-col gap-1">
|
||||
<div className="border-silver dark:border-silver/20 flex flex-col rounded-2xl border">
|
||||
<p className="dark:bg-eerie-black-2 flex flex-row items-center justify-between rounded-t-2xl bg-black/10 px-2 py-1 text-sm font-semibold wrap-break-word">
|
||||
<span style={{ fontFamily: 'IBMPlexMono-Medium' }}>
|
||||
Arguments
|
||||
</span>{' '}
|
||||
<CopyButton
|
||||
textToCopy={JSON.stringify(toolCall.arguments, null, 2)}
|
||||
/>
|
||||
</p>
|
||||
<p className="dark:tex dark:bg-raisin-black rounded-b-2xl p-2 font-mono text-sm wrap-break-word">
|
||||
}
|
||||
/>
|
||||
<button
|
||||
className="flex flex-row items-center gap-2"
|
||||
onClick={() => setIsToolCallsOpen(!isToolCallsOpen)}
|
||||
>
|
||||
<p className="text-base font-semibold">Tool Calls</p>
|
||||
<img
|
||||
src={ChevronDown}
|
||||
alt="ChevronDown"
|
||||
className={`h-4 w-4 transform transition-transform duration-200 dark:invert ${isToolCallsOpen ? 'rotate-180' : ''}`}
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
{isToolCallsOpen && (
|
||||
<div className="fade-in mr-5 ml-3 w-[90vw] md:w-[70vw] lg:w-full">
|
||||
<div className="grid grid-cols-1 gap-2">
|
||||
{toolCalls.map((toolCall, index) => (
|
||||
<Accordion
|
||||
key={`tool-call-${index}`}
|
||||
title={`${toolCall.tool_name} - ${toolCall.action_name.substring(0, toolCall.action_name.lastIndexOf('_'))}`}
|
||||
className="bg-muted dark:bg-answer-bubble w-full rounded-4xl"
|
||||
titleClassName="px-6 py-2 text-sm font-semibold"
|
||||
>
|
||||
<div className="flex flex-col gap-1">
|
||||
<div className="border-border flex flex-col rounded-2xl border">
|
||||
<p className="dark:bg-background flex flex-row items-center justify-between rounded-t-2xl bg-black/10 px-2 py-1 text-sm font-semibold wrap-break-word">
|
||||
<span style={{ fontFamily: 'IBMPlexMono-Medium' }}>
|
||||
Arguments
|
||||
</span>{' '}
|
||||
<CopyButton
|
||||
textToCopy={JSON.stringify(toolCall.arguments, null, 2)}
|
||||
/>
|
||||
</p>
|
||||
<p className="dark:bg-card rounded-b-2xl p-2 font-mono text-sm wrap-break-word">
|
||||
<span
|
||||
className="dark:text-muted-foreground leading-[23px] text-black"
|
||||
style={{ fontFamily: 'IBMPlexMono-Medium' }}
|
||||
>
|
||||
{JSON.stringify(toolCall.arguments, null, 2)}
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
<div className="border-border flex flex-col rounded-2xl border">
|
||||
<p className="dark:bg-background flex flex-row items-center justify-between rounded-t-2xl bg-black/10 px-2 py-1 text-sm font-semibold wrap-break-word">
|
||||
<span style={{ fontFamily: 'IBMPlexMono-Medium' }}>
|
||||
Response
|
||||
</span>{' '}
|
||||
<CopyButton
|
||||
textToCopy={
|
||||
toolCall.status === 'error'
|
||||
? toolCall.error || 'Unknown error'
|
||||
: JSON.stringify(toolCall.result, null, 2)
|
||||
}
|
||||
/>
|
||||
</p>
|
||||
{toolCall.status === 'pending' && (
|
||||
<span className="dark:bg-card flex w-full items-center justify-center rounded-b-2xl p-2">
|
||||
<Spinner size="small" />
|
||||
</span>
|
||||
)}
|
||||
{toolCall.status === 'completed' && (
|
||||
<p className="dark:bg-card rounded-b-2xl p-2 font-mono text-sm wrap-break-word">
|
||||
<span
|
||||
className="leading-[23px] text-black dark:text-gray-400"
|
||||
className="dark:text-muted-foreground leading-[23px] text-black"
|
||||
style={{ fontFamily: 'IBMPlexMono-Medium' }}
|
||||
>
|
||||
{JSON.stringify(toolCall.arguments, null, 2)}
|
||||
{JSON.stringify(toolCall.result, null, 2)}
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
<div className="border-silver dark:border-silver/20 flex flex-col rounded-2xl border">
|
||||
<p className="dark:bg-eerie-black-2 flex flex-row items-center justify-between rounded-t-2xl bg-black/10 px-2 py-1 text-sm font-semibold wrap-break-word">
|
||||
<span style={{ fontFamily: 'IBMPlexMono-Medium' }}>
|
||||
Response
|
||||
</span>{' '}
|
||||
<CopyButton
|
||||
textToCopy={
|
||||
toolCall.status === 'error'
|
||||
? toolCall.error || 'Unknown error'
|
||||
: JSON.stringify(toolCall.result, null, 2)
|
||||
}
|
||||
/>
|
||||
</p>
|
||||
{toolCall.status === 'pending' && (
|
||||
<span className="dark:bg-raisin-black flex w-full items-center justify-center rounded-b-2xl p-2">
|
||||
<Spinner size="small" />
|
||||
)}
|
||||
{toolCall.status === 'error' && (
|
||||
<p className="dark:bg-card rounded-b-2xl p-2 font-mono text-sm wrap-break-word">
|
||||
<span
|
||||
className="leading-[23px] text-red-500 dark:text-red-400"
|
||||
style={{ fontFamily: 'IBMPlexMono-Medium' }}
|
||||
>
|
||||
{toolCall.error}
|
||||
</span>
|
||||
)}
|
||||
{toolCall.status === 'completed' && (
|
||||
<p className="dark:bg-raisin-black rounded-b-2xl p-2 font-mono text-sm wrap-break-word">
|
||||
<span
|
||||
className="leading-[23px] text-black dark:text-gray-400"
|
||||
style={{ fontFamily: 'IBMPlexMono-Medium' }}
|
||||
>
|
||||
{JSON.stringify(toolCall.result, null, 2)}
|
||||
</span>
|
||||
</p>
|
||||
)}
|
||||
{toolCall.status === 'error' && (
|
||||
<p className="dark:bg-raisin-black rounded-b-2xl p-2 font-mono text-sm wrap-break-word">
|
||||
<span
|
||||
className="leading-[23px] text-red-500 dark:text-red-400"
|
||||
style={{ fontFamily: 'IBMPlexMono-Medium' }}
|
||||
>
|
||||
{toolCall.error}
|
||||
</span>
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</Accordion>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</Accordion>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -992,7 +1030,7 @@ function Thought({
|
||||
</div>
|
||||
{isThoughtOpen && (
|
||||
<div className="fade-in mr-5 ml-2 max-w-[90vw] md:max-w-[70vw] lg:max-w-[50vw]">
|
||||
<div className="bg-gray-1000 dark:bg-gun-metal rounded-[28px] px-7 py-[18px]">
|
||||
<div className="bg-muted dark:bg-answer-bubble rounded-[28px] px-7 py-[18px]">
|
||||
<ReactMarkdown
|
||||
className="fade-in leading-normal wrap-break-word whitespace-pre-wrap"
|
||||
remarkPlugins={[remarkGfm, remarkMath]}
|
||||
@@ -1004,9 +1042,9 @@ function Thought({
|
||||
const language = match ? match[1] : '';
|
||||
|
||||
return match ? (
|
||||
<div className="group border-light-silver dark:border-raisin-black relative overflow-hidden rounded-[14px] border">
|
||||
<div className="bg-platinum dark:bg-eerie-black-2 flex items-center justify-between px-2 py-1">
|
||||
<span className="text-just-black dark:text-chinese-white text-xs font-medium">
|
||||
<div className="group border-border relative overflow-hidden rounded-[14px] border">
|
||||
<div className="bg-platinum flex items-center justify-between px-2 py-1">
|
||||
<span className="text-foreground dark:text-foreground text-xs font-medium">
|
||||
{language}
|
||||
</span>
|
||||
<CopyButton
|
||||
@@ -1028,7 +1066,7 @@ function Thought({
|
||||
</SyntaxHighlighter>
|
||||
</div>
|
||||
) : (
|
||||
<code className="dark:bg-independence dark:text-bright-gray rounded-[6px] bg-gray-200 px-2 py-1 text-xs font-normal whitespace-pre-line">
|
||||
<code className="dark:bg-accent dark:text-foreground rounded-[6px] bg-gray-200 px-2 py-1 text-xs font-normal whitespace-pre-line">
|
||||
{children}
|
||||
</code>
|
||||
);
|
||||
@@ -1049,8 +1087,8 @@ function Thought({
|
||||
},
|
||||
table({ children }) {
|
||||
return (
|
||||
<div className="border-silver/40 dark:border-silver/40 relative overflow-x-auto rounded-lg border">
|
||||
<table className="dark:text-bright-gray w-full text-left text-gray-700">
|
||||
<div className="border-silver/40 dark:border-border relative overflow-x-auto rounded-lg border">
|
||||
<table className="dark:text-foreground w-full text-left text-gray-700">
|
||||
{children}
|
||||
</table>
|
||||
</div>
|
||||
@@ -1058,14 +1096,14 @@ function Thought({
|
||||
},
|
||||
thead({ children }) {
|
||||
return (
|
||||
<thead className="dark:text-bright-gray bg-gray-50 text-xs text-gray-900 uppercase dark:bg-[#26272E]/50">
|
||||
<thead className="bg-muted text-foreground text-xs uppercase">
|
||||
{children}
|
||||
</thead>
|
||||
);
|
||||
},
|
||||
tr({ children }) {
|
||||
return (
|
||||
<tr className="dark:border-silver/40 border-b border-gray-200 odd:bg-white even:bg-gray-50 dark:odd:bg-[#26272E] dark:even:bg-[#26272E]/50">
|
||||
<tr className="border-border odd:bg-card even:bg-muted border-b">
|
||||
{children}
|
||||
</tr>
|
||||
);
|
||||
|
||||
@@ -168,7 +168,7 @@ export default function ConversationMessages({
|
||||
if (query.error) {
|
||||
const retryButton = (
|
||||
<button
|
||||
className="dark:text-bright-gray 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"
|
||||
className="dark:text-foreground 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"
|
||||
disabled={status === 'loading'}
|
||||
onClick={() => {
|
||||
const questionToRetry = queries[index].prompt;
|
||||
@@ -241,7 +241,7 @@ export default function ConversationMessages({
|
||||
scrollConversationToBottom();
|
||||
}}
|
||||
aria-label={t('Scroll to bottom') || 'Scroll to bottom'}
|
||||
className="border-gray-alpha bg-opacity-50 dark:bg-gunmetal md:bg-opacity-100 fixed right-14 bottom-40 z-10 flex h-7 w-7 items-center justify-center rounded-full border-[0.5px] bg-gray-100 md:h-9 md:w-9"
|
||||
className="border-border bg-card fixed right-14 bottom-40 z-10 flex h-7 w-7 items-center justify-center rounded-full border md:h-9 md:w-9"
|
||||
>
|
||||
<img
|
||||
src={ArrowDown}
|
||||
|
||||
@@ -192,9 +192,9 @@ export default function ConversationTile({
|
||||
conversationId !== conversation.id &&
|
||||
selectConversation(conversation.id);
|
||||
}}
|
||||
className={`hover:bg-bright-gray dark:hover:bg-dark-charcoal mx-4 my-auto mt-4 flex h-9 cursor-pointer items-center justify-between gap-4 rounded-3xl pl-4 ${
|
||||
className={`hover:bg-sidebar-accent mx-4 my-auto mt-4 flex h-9 cursor-pointer items-center justify-between gap-4 rounded-3xl pl-4 ${
|
||||
conversationId === conversation.id || isOpen || isHovered || isEdit
|
||||
? 'bg-bright-gray dark:bg-dark-charcoal'
|
||||
? 'bg-sidebar-accent'
|
||||
: ''
|
||||
}`}
|
||||
>
|
||||
@@ -203,19 +203,22 @@ export default function ConversationTile({
|
||||
<input
|
||||
autoFocus
|
||||
type="text"
|
||||
className="h-6 w-full bg-transparent px-1 text-sm leading-6 rounded-2xl font-normal outline-none"
|
||||
className="h-6 w-full rounded-2xl bg-transparent px-1 text-sm leading-6 font-normal outline-none"
|
||||
value={conversationName}
|
||||
onChange={(e) => setConversationsName(e.target.value)}
|
||||
onKeyDown={handleRenameKeyDown}
|
||||
/>
|
||||
) : (
|
||||
<p className="text-eerie-black dark:text-bright-gray my-auto overflow-hidden text-sm leading-6 font-normal text-ellipsis whitespace-nowrap">
|
||||
<p className="text-foreground dark:text-foreground my-auto overflow-hidden text-sm leading-6 font-normal text-ellipsis whitespace-nowrap">
|
||||
{conversationName}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
{(conversationId === conversation.id || isHovered || isOpen) && (
|
||||
<div className="dark:text-sonic-silver flex text-white" ref={menuRef}>
|
||||
<div
|
||||
className="dark:text-muted-foreground flex text-white"
|
||||
ref={menuRef}
|
||||
>
|
||||
{isEdit ? (
|
||||
<div className="flex gap-1">
|
||||
<img
|
||||
@@ -248,7 +251,7 @@ export default function ConversationTile({
|
||||
event.stopPropagation();
|
||||
setOpen(!isOpen);
|
||||
}}
|
||||
className="mr-2 flex h-6 w-6 items-center justify-center rounded-full transition-colors duration-200 hover:bg-gray-200 dark:hover:bg-gray-700"
|
||||
className="hover:bg-accent dark:hover:bg-accent mr-2 flex h-6 w-6 items-center justify-center rounded-full transition-colors duration-200"
|
||||
>
|
||||
<img src={threeDots} width={8} alt="menu" />
|
||||
</button>
|
||||
|
||||
@@ -4,15 +4,36 @@ import Avatar from '../components/Avatar';
|
||||
import { ResearchState } from './conversationModels';
|
||||
|
||||
const SmallCheck = () => (
|
||||
<svg className="h-3 w-3 text-green-500" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={3}>
|
||||
<svg
|
||||
className="h-3 w-3 text-green-500"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
strokeWidth={3}
|
||||
>
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M5 13l4 4L19 7" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
const SmallSpinner = () => (
|
||||
<svg className="h-3 w-3 animate-spin text-purple-500" fill="none" viewBox="0 0 24 24">
|
||||
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4" />
|
||||
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z" />
|
||||
<svg
|
||||
className="h-3 w-3 animate-spin text-purple-500"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<circle
|
||||
className="opacity-25"
|
||||
cx="12"
|
||||
cy="12"
|
||||
r="10"
|
||||
stroke="currentColor"
|
||||
strokeWidth="4"
|
||||
/>
|
||||
<path
|
||||
className="opacity-75"
|
||||
fill="currentColor"
|
||||
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
|
||||
@@ -32,7 +53,9 @@ function StatusText({ status, elapsed }: { status: string; elapsed?: number }) {
|
||||
<span className="text-xs text-gray-500 dark:text-gray-400">
|
||||
{status === 'complete' ? (
|
||||
<>
|
||||
<span className="text-green-600 dark:text-green-400">{labels.complete}</span>
|
||||
<span className="text-green-600 dark:text-green-400">
|
||||
{labels.complete}
|
||||
</span>
|
||||
{elapsed_str}
|
||||
</>
|
||||
) : (
|
||||
@@ -62,13 +85,15 @@ export default function ResearchProgress({
|
||||
|
||||
if (!plan && !status) return null;
|
||||
|
||||
const completedSteps = plan?.filter((s) => s.status === 'complete').length ?? 0;
|
||||
const completedSteps =
|
||||
plan?.filter((s) => s.status === 'complete').length ?? 0;
|
||||
const totalSteps = plan?.length ?? 0;
|
||||
|
||||
// Collapsed: single-line summary
|
||||
const summaryText = totalSteps > 0
|
||||
? `Researched ${completedSteps} topic${completedSteps !== 1 ? 's' : ''}`
|
||||
: 'Research';
|
||||
const summaryText =
|
||||
totalSteps > 0
|
||||
? `Researched ${completedSteps} topic${completedSteps !== 1 ? 's' : ''}`
|
||||
: 'Research';
|
||||
|
||||
return (
|
||||
<div className="mb-4 flex w-full flex-col flex-wrap items-start self-start lg:flex-nowrap">
|
||||
@@ -93,9 +118,16 @@ export default function ResearchProgress({
|
||||
</p>
|
||||
<svg
|
||||
className={`h-4 w-4 text-gray-500 transition-transform duration-200 dark:text-gray-400 ${isExpanded ? 'rotate-180' : ''}`}
|
||||
fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
strokeWidth={2}
|
||||
>
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M19 9l-7 7-7-7" />
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
d="M19 9l-7 7-7-7"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
{status && <StatusText status={status} elapsed={elapsed_seconds} />}
|
||||
@@ -126,13 +158,15 @@ export default function ResearchProgress({
|
||||
</div>
|
||||
{/* Step content */}
|
||||
<div className={`pb-3 ${isLast ? '' : ''}`}>
|
||||
<p className={`text-sm ${
|
||||
step.status === 'complete'
|
||||
? 'text-gray-700 dark:text-gray-300'
|
||||
: step.status === 'researching'
|
||||
? 'font-medium text-purple-700 dark:text-purple-300'
|
||||
: 'text-gray-500 dark:text-gray-500'
|
||||
}`}>
|
||||
<p
|
||||
className={`text-sm ${
|
||||
step.status === 'complete'
|
||||
? 'text-gray-700 dark:text-gray-300'
|
||||
: step.status === 'researching'
|
||||
? 'font-medium text-purple-700 dark:text-purple-300'
|
||||
: 'text-gray-500 dark:text-gray-500'
|
||||
}`}
|
||||
>
|
||||
{step.query}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -138,18 +138,18 @@ export const SharedConversation = () => {
|
||||
twitterTitle={title}
|
||||
twitterDescription="Shared conversations with DocsGPT"
|
||||
/>
|
||||
<div className="dark:bg-raisin-black flex h-full flex-col items-center justify-between gap-2 overflow-y-hidden">
|
||||
<div className="bg-background flex h-full flex-col items-center justify-between gap-2 overflow-y-hidden">
|
||||
<div className="dark:border-b-silver w-full max-w-[1200px] border-b p-2 md:w-9/12 lg:w-8/12 xl:w-8/12 2xl:w-6/12">
|
||||
<h1 className="font-semi-bold text-chinese-black dark:text-chinese-silver text-4xl">
|
||||
<h1 className="font-semi-bold text-foreground dark:text-foreground text-4xl">
|
||||
{title}
|
||||
</h1>
|
||||
<h2 className="font-semi-bold text-chinese-black dark:text-chinese-silver text-base">
|
||||
<h2 className="font-semi-bold text-foreground dark:text-foreground text-base">
|
||||
{t('sharedConv.subtitle')}{' '}
|
||||
<a href="/" className="text-[#007DFF]">
|
||||
DocsGPT
|
||||
</a>
|
||||
</h2>
|
||||
<h2 className="font-semi-bold text-chinese-black dark:text-chinese-silver text-base">
|
||||
<h2 className="font-semi-bold text-foreground dark:text-foreground text-base">
|
||||
{date}
|
||||
</h2>
|
||||
</div>
|
||||
@@ -174,13 +174,13 @@ export const SharedConversation = () => {
|
||||
) : (
|
||||
<button
|
||||
onClick={() => navigate('/')}
|
||||
className="bg-purple-30 hover:bg-violets-are-blue mb-14 w-fit rounded-full px-5 py-3 text-white shadow-xl transition-colors duration-200 sm:mb-0"
|
||||
className="bg-primary hover:bg-primary/90 mb-14 w-fit rounded-full px-5 py-3 text-white shadow-xl transition-colors duration-200 sm:mb-0"
|
||||
>
|
||||
{t('sharedConv.button')}
|
||||
</button>
|
||||
)}
|
||||
|
||||
<p className="text-gray-4000 dark:text-sonic-silver hidden w-screen self-center bg-transparent py-2 text-center text-xs md:inline md:w-full">
|
||||
<p className="text-muted-foreground hidden w-screen self-center bg-transparent py-2 text-center text-xs md:inline md:w-full">
|
||||
{t('sharedConv.meta')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -427,7 +427,10 @@ export const conversationSlice = createSlice({
|
||||
status: 'pending',
|
||||
});
|
||||
}
|
||||
if (progress.status === 'researching' || progress.status === 'complete') {
|
||||
if (
|
||||
progress.status === 'researching' ||
|
||||
progress.status === 'complete'
|
||||
) {
|
||||
research.plan[stepIndex].status = progress.status;
|
||||
}
|
||||
if (progress.query) {
|
||||
|
||||
@@ -17,12 +17,9 @@ layer(base);
|
||||
--color-gray-1000: #f6f6f6;
|
||||
--color-gray-2000: rgba(0, 0, 0, 0.5);
|
||||
--color-gray-3000: rgba(243, 243, 243, 1);
|
||||
--color-gray-4000: #949494;
|
||||
--color-gray-5000: #bbbbbb;
|
||||
--color-gray-6000: #757575;
|
||||
--color-red-1000: rgb(254, 202, 202);
|
||||
--color-red-2000: #f44336;
|
||||
--color-red-3000: #621b16;
|
||||
--color-blue-1000: #7d54d1;
|
||||
--color-blue-2000: #002b49;
|
||||
--color-blue-3000: #4b02e2;
|
||||
@@ -32,7 +29,6 @@ layer(base);
|
||||
--color-blue-5000: rgba(0, 125, 255);
|
||||
--color-green-2000: #0fff50;
|
||||
--color-light-gray: #edeef0;
|
||||
--color-white-3000: #ffffff;
|
||||
--color-just-black: #00000;
|
||||
--color-purple-taupe: #464152;
|
||||
--color-dove-gray: #6c6c6c;
|
||||
@@ -57,7 +53,6 @@ layer(base);
|
||||
--color-charleston-green-2: #26272e;
|
||||
--color-charleston-green-3: #26272a;
|
||||
--color-grey: #7e7e7e;
|
||||
--color-lotion: #fbfbfb;
|
||||
--color-platinum: #e6e6e6;
|
||||
--color-eerie-black-2: #191919;
|
||||
--color-light-silver: #d9d9d9;
|
||||
@@ -69,15 +64,11 @@ layer(base);
|
||||
--color-onyx-2: #35383c;
|
||||
--color-philippine-grey: #929292;
|
||||
--color-charcoal-grey: #53545d;
|
||||
--color-rosso-corsa: #d30000;
|
||||
--color-north-texas-green: #0c9d35;
|
||||
--color-medium-purple: #8d66dd;
|
||||
--color-slate-blue: #6f5fca;
|
||||
--color-old-silver: #848484;
|
||||
--color-arsenic: #4d4e58;
|
||||
--color-light-gainsboro: #d7d7d7;
|
||||
--color-raisin-black-light: #18181b;
|
||||
--color-gunmetal: #32333b;
|
||||
--color-sonic-silver-light: #7f7f82;
|
||||
--color-violets-are-blue: #976af3;
|
||||
}
|
||||
@@ -201,7 +192,7 @@ layer(base);
|
||||
}
|
||||
|
||||
@utility table-default {
|
||||
@apply border-silver dark:border-silver/40 dark:text-bright-gray block w-full table-auto justify-center overflow-auto rounded-xl border text-center;
|
||||
@apply border-border text-foreground block w-full table-auto justify-center overflow-auto rounded-xl border text-center;
|
||||
|
||||
& th {
|
||||
@apply p-4 font-normal text-nowrap text-gray-400;
|
||||
@@ -216,7 +207,7 @@ layer(base);
|
||||
}
|
||||
|
||||
& td {
|
||||
@apply border-silver dark:border-silver/40 w-full border-t px-4 py-2;
|
||||
@apply border-border w-full border-t px-4 py-2;
|
||||
}
|
||||
|
||||
& td:last-child {
|
||||
@@ -262,7 +253,7 @@ layer(base);
|
||||
}
|
||||
|
||||
body.dark {
|
||||
background-color: #202124; /* raisin-black */
|
||||
background-color: var(--background);
|
||||
}
|
||||
::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
@@ -868,6 +859,7 @@ Avoid over-scrolling in mobile browsers
|
||||
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
||||
--color-sidebar-border: var(--sidebar-border);
|
||||
--color-sidebar-ring: var(--sidebar-ring);
|
||||
--color-answer-bubble: var(--answer-bubble);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -876,37 +868,38 @@ Avoid over-scrolling in mobile browsers
|
||||
|
||||
:root {
|
||||
--radius: 0.625rem;
|
||||
--background: oklch(1 0 0);
|
||||
--foreground: oklch(0.145 0 0);
|
||||
--card: oklch(1 0 0);
|
||||
--card-foreground: oklch(0.145 0 0);
|
||||
--popover: oklch(1 0 0);
|
||||
--popover-foreground: oklch(0.145 0 0);
|
||||
--primary: oklch(0.554 0.185 294.8); /* purple-30 #7d54d1 */
|
||||
--primary-foreground: oklch(0.985 0 0);
|
||||
--secondary: oklch(0.914 0.035 300.2); /* purple-3000 - light purple */
|
||||
--secondary-foreground: oklch(0.554 0.185 294.8);
|
||||
--muted: oklch(0.97 0 0);
|
||||
--muted-foreground: oklch(0.556 0 0);
|
||||
--accent: oklch(0.914 0.035 300.2); /* purple-3000 - light purple hover */
|
||||
--accent-foreground: oklch(0.205 0 0);
|
||||
--destructive: oklch(0.577 0.245 27.325);
|
||||
--border: oklch(0.922 0 0);
|
||||
--input: oklch(0.870 0 0); /* neutral gray border */
|
||||
--ring: oklch(0.554 0.185 294.8); /* purple-30 focus ring */
|
||||
--background: #ffffff;
|
||||
--foreground: #171717;
|
||||
--card: #ffffff;
|
||||
--card-foreground: #171717;
|
||||
--popover: #ffffff;
|
||||
--popover-foreground: #171717;
|
||||
--primary: #7d54d1;
|
||||
--primary-foreground: #ffffff;
|
||||
--secondary: #f4f4f4;
|
||||
--secondary-foreground: #7d54d1;
|
||||
--muted: #f6f6f6;
|
||||
--muted-foreground: #737373;
|
||||
--accent: #ececec;
|
||||
--accent-foreground: #171717;
|
||||
--destructive: #ef4444;
|
||||
--border: #d9d9d9;
|
||||
--input: #d9d9d9;
|
||||
--ring: #7d54d1;
|
||||
--chart-1: oklch(0.646 0.222 41.116);
|
||||
--chart-2: oklch(0.6 0.118 184.704);
|
||||
--chart-3: oklch(0.398 0.07 227.392);
|
||||
--chart-4: oklch(0.828 0.189 84.429);
|
||||
--chart-5: oklch(0.769 0.188 70.08);
|
||||
--sidebar: oklch(0.985 0 0);
|
||||
--sidebar-foreground: oklch(0.145 0 0);
|
||||
--sidebar-primary: oklch(0.554 0.185 294.8); /* purple-30 */
|
||||
--sidebar-primary-foreground: oklch(0.985 0 0);
|
||||
--sidebar-accent: oklch(0.914 0.035 300.2); /* purple-3000 */
|
||||
--sidebar-accent-foreground: oklch(0.205 0 0);
|
||||
--sidebar-border: oklch(0.922 0 0);
|
||||
--sidebar-ring: oklch(0.554 0.185 294.8); /* purple-30 */
|
||||
--sidebar: #fbfbfb;
|
||||
--sidebar-foreground: #171717;
|
||||
--sidebar-primary: #7d54d1;
|
||||
--sidebar-primary-foreground: #ffffff;
|
||||
--sidebar-accent: #ececec;
|
||||
--sidebar-accent-foreground: #171717;
|
||||
--sidebar-border: #d9d9d9;
|
||||
--sidebar-ring: #7d54d1;
|
||||
--answer-bubble: #f6f6f6;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -914,37 +907,38 @@ Avoid over-scrolling in mobile browsers
|
||||
*/
|
||||
|
||||
.dark {
|
||||
--background: oklch(0.145 0 0);
|
||||
--foreground: oklch(0.985 0 0);
|
||||
--card: oklch(0.205 0 0);
|
||||
--card-foreground: oklch(0.985 0 0);
|
||||
--popover: oklch(0.205 0 0);
|
||||
--popover-foreground: oklch(0.985 0 0);
|
||||
--primary: oklch(0.636 0.197 295.4); /* violets-are-blue #976af3 */
|
||||
--primary-foreground: oklch(0.985 0 0);
|
||||
--secondary: oklch(0.269 0.03 295.0); /* dark muted purple */
|
||||
--secondary-foreground: oklch(0.867 0.052 300.1);
|
||||
--muted: oklch(0.269 0 0);
|
||||
--muted-foreground: oklch(0.708 0 0);
|
||||
--accent: oklch(0.269 0.04 295.0); /* dark purple hover */
|
||||
--accent-foreground: oklch(0.985 0 0);
|
||||
--destructive: oklch(0.704 0.191 22.216);
|
||||
--border: oklch(1 0 0 / 10%);
|
||||
--input: oklch(1 0 0 / 15%);
|
||||
--ring: oklch(0.636 0.197 295.4); /* violets-are-blue focus ring */
|
||||
--background: #222327;
|
||||
--foreground: #fafafa;
|
||||
--card: #2b2c31;
|
||||
--card-foreground: #fafafa;
|
||||
--popover: #2b2c31;
|
||||
--popover-foreground: #fafafa;
|
||||
--primary: #976af3;
|
||||
--primary-foreground: #ffffff;
|
||||
--secondary: #2b2c31;
|
||||
--secondary-foreground: #e0e0e0;
|
||||
--muted: #35363b;
|
||||
--muted-foreground: #a1a1a1;
|
||||
--accent: #3e3f45;
|
||||
--accent-foreground: #fafafa;
|
||||
--destructive: #dc2626;
|
||||
--border: #44454c;
|
||||
--input: #44454c;
|
||||
--ring: #976af3;
|
||||
--chart-1: oklch(0.488 0.243 264.376);
|
||||
--chart-2: oklch(0.696 0.17 162.48);
|
||||
--chart-3: oklch(0.769 0.188 70.08);
|
||||
--chart-4: oklch(0.627 0.265 303.9);
|
||||
--chart-5: oklch(0.645 0.246 16.439);
|
||||
--sidebar: oklch(0.205 0 0);
|
||||
--sidebar-foreground: oklch(0.985 0 0);
|
||||
--sidebar-primary: oklch(0.636 0.197 295.4); /* violets-are-blue */
|
||||
--sidebar-primary-foreground: oklch(0.985 0 0);
|
||||
--sidebar-accent: oklch(0.269 0.04 295.0); /* dark purple */
|
||||
--sidebar-accent-foreground: oklch(0.985 0 0);
|
||||
--sidebar-border: oklch(1 0 0 / 10%);
|
||||
--sidebar-ring: oklch(0.636 0.197 295.4); /* violets-are-blue */
|
||||
--sidebar: #161616;
|
||||
--sidebar-foreground: #fafafa;
|
||||
--sidebar-primary: #976af3;
|
||||
--sidebar-primary-foreground: #ffffff;
|
||||
--sidebar-accent: #222327;
|
||||
--sidebar-accent-foreground: #fafafa;
|
||||
--sidebar-border: #2b2c31;
|
||||
--sidebar-ring: #976af3;
|
||||
--answer-bubble: #2e303e;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -54,6 +54,7 @@
|
||||
},
|
||||
"sources": {
|
||||
"title": "Hier kannst du alle verfügbaren Quelldateien verwalten, die dir zur Verfügung stehen und die du hochgeladen hast.",
|
||||
"subtitle": "Dokumente und Wissensquellen hochladen und verwalten, die deine Antworten antreiben",
|
||||
"label": "Quellen",
|
||||
"name": "Quellenname",
|
||||
"date": "Vektor-Datum",
|
||||
@@ -118,6 +119,7 @@
|
||||
},
|
||||
"analytics": {
|
||||
"label": "Analytik",
|
||||
"subtitle": "Nachrichtenvolumen, Token-Verbrauch und Nutzerfeedback in deinem Konto verfolgen",
|
||||
"filterByChatbot": "Nach Chatbot filtern",
|
||||
"selectChatbot": "Chatbot auswählen",
|
||||
"filterOptions": {
|
||||
@@ -137,6 +139,7 @@
|
||||
},
|
||||
"logs": {
|
||||
"label": "Protokolle",
|
||||
"subtitle": "API-Aufrufe und Konversationsprotokolle einsehen und überwachen",
|
||||
"filterByChatbot": "Nach Chatbot filtern",
|
||||
"selectChatbot": "Chatbot auswählen",
|
||||
"none": "Keine",
|
||||
@@ -144,6 +147,7 @@
|
||||
},
|
||||
"tools": {
|
||||
"label": "Werkzeuge",
|
||||
"subtitle": "Werkzeuge und Integrationen durchsuchen, verbinden und konfigurieren, die deine Agenten antreiben",
|
||||
"searchPlaceholder": "Werkzeuge suchen...",
|
||||
"addTool": "Werkzeug hinzufügen",
|
||||
"noToolsFound": "Keine Werkzeuge gefunden",
|
||||
|
||||
@@ -54,6 +54,7 @@
|
||||
},
|
||||
"sources": {
|
||||
"title": "Here you can manage all of the source file that are available to you and those you have uploaded.",
|
||||
"subtitle": "Upload and manage the documents and knowledge sources used to power your responses",
|
||||
"label": "Sources",
|
||||
"name": "Source Name",
|
||||
"date": "Vector Date",
|
||||
@@ -118,6 +119,7 @@
|
||||
},
|
||||
"analytics": {
|
||||
"label": "Analytics",
|
||||
"subtitle": "Track message volume, token usage, and user feedback across your account",
|
||||
"filterByChatbot": "Filter by chatbot",
|
||||
"selectChatbot": "Select chatbot",
|
||||
"filterOptions": {
|
||||
@@ -137,6 +139,7 @@
|
||||
},
|
||||
"logs": {
|
||||
"label": "Logs",
|
||||
"subtitle": "View and monitor API calls and conversation logs",
|
||||
"filterByChatbot": "Filter by chatbot",
|
||||
"selectChatbot": "Select chatbot",
|
||||
"none": "None",
|
||||
@@ -144,6 +147,7 @@
|
||||
},
|
||||
"tools": {
|
||||
"label": "Tools",
|
||||
"subtitle": "Browse, connect, and configure the tools and integrations that power your agents",
|
||||
"searchPlaceholder": "Search tools...",
|
||||
"addTool": "Add Tool",
|
||||
"noToolsFound": "No tools found",
|
||||
|
||||
@@ -54,6 +54,7 @@
|
||||
},
|
||||
"sources": {
|
||||
"title": "Aquí puedes gestionar todos los archivos fuente que están disponibles para ti y los que has subido.",
|
||||
"subtitle": "Sube y gestiona los documentos y fuentes de conocimiento que impulsan tus respuestas",
|
||||
"label": "Fuentes",
|
||||
"name": "Nombre de la Fuente",
|
||||
"date": "Fecha de Vector",
|
||||
@@ -118,6 +119,7 @@
|
||||
},
|
||||
"analytics": {
|
||||
"label": "Analítica",
|
||||
"subtitle": "Realiza un seguimiento del volumen de mensajes, uso de tokens y comentarios de usuarios en tu cuenta",
|
||||
"filterByChatbot": "Filtrar por chatbot",
|
||||
"selectChatbot": "Seleccionar chatbot",
|
||||
"filterOptions": {
|
||||
@@ -137,6 +139,7 @@
|
||||
},
|
||||
"logs": {
|
||||
"label": "Registros",
|
||||
"subtitle": "Visualiza y monitorea las llamadas a la API y los registros de conversaciones",
|
||||
"filterByChatbot": "Filtrar por chatbot",
|
||||
"selectChatbot": "Seleccionar chatbot",
|
||||
"none": "Ninguno",
|
||||
@@ -144,6 +147,7 @@
|
||||
},
|
||||
"tools": {
|
||||
"label": "Herramientas",
|
||||
"subtitle": "Explora, conecta y configura las herramientas e integraciones que impulsan a tus agentes",
|
||||
"searchPlaceholder": "Buscar...",
|
||||
"addTool": "Agregar Herramienta",
|
||||
"noToolsFound": "No se encontraron herramientas",
|
||||
|
||||
@@ -54,6 +54,7 @@
|
||||
},
|
||||
"sources": {
|
||||
"title": "ここでは、利用可能なすべてのソースファイルとアップロードしたファイルを管理できます。",
|
||||
"subtitle": "レスポンスを機能させるドキュメントとナレッジソースをアップロードして管理",
|
||||
"label": "ソース",
|
||||
"name": "ソース名",
|
||||
"date": "ベクトル日付",
|
||||
@@ -118,6 +119,7 @@
|
||||
},
|
||||
"analytics": {
|
||||
"label": "分析",
|
||||
"subtitle": "アカウント全体のメッセージ量、トークン使用量、ユーザーフィードバックを追跡",
|
||||
"filterByChatbot": "チャットボットでフィルター",
|
||||
"selectChatbot": "チャットボットを選択",
|
||||
"filterOptions": {
|
||||
@@ -137,6 +139,7 @@
|
||||
},
|
||||
"logs": {
|
||||
"label": "ログ",
|
||||
"subtitle": "APIコールと会話ログを表示・監視",
|
||||
"filterByChatbot": "チャットボットでフィルター",
|
||||
"selectChatbot": "チャットボットを選択",
|
||||
"none": "なし",
|
||||
@@ -144,6 +147,7 @@
|
||||
},
|
||||
"tools": {
|
||||
"label": "ツール",
|
||||
"subtitle": "エージェントを強化するツールやインテグレーションを検索、接続、設定",
|
||||
"searchPlaceholder": "検索...",
|
||||
"addTool": "ツールを追加",
|
||||
"noToolsFound": "ツールが見つかりません",
|
||||
|
||||
@@ -54,6 +54,7 @@
|
||||
},
|
||||
"sources": {
|
||||
"title": "Здесь вы можете управлять всеми исходными файлами, которые доступны вам и которые вы загрузили.",
|
||||
"subtitle": "Загружайте и управляйте документами и источниками знаний, которые обеспечивают ваши ответы",
|
||||
"label": "Источники",
|
||||
"name": "Название источника",
|
||||
"date": "Дата вектора",
|
||||
@@ -118,6 +119,7 @@
|
||||
},
|
||||
"analytics": {
|
||||
"label": "Аналитика",
|
||||
"subtitle": "Отслеживайте объём сообщений, использование токенов и отзывы пользователей в вашем аккаунте",
|
||||
"filterByChatbot": "Фильтровать по чат-боту",
|
||||
"selectChatbot": "Выбрать чат-бота",
|
||||
"filterOptions": {
|
||||
@@ -137,6 +139,7 @@
|
||||
},
|
||||
"logs": {
|
||||
"label": "Журналы",
|
||||
"subtitle": "Просматривайте и отслеживайте вызовы API и журналы разговоров",
|
||||
"filterByChatbot": "Фильтровать по чат-боту",
|
||||
"selectChatbot": "Выбрать чат-бота",
|
||||
"none": "Нет",
|
||||
@@ -144,6 +147,7 @@
|
||||
},
|
||||
"tools": {
|
||||
"label": "Инструменты",
|
||||
"subtitle": "Просматривайте, подключайте и настраивайте инструменты и интеграции для ваших агентов",
|
||||
"searchPlaceholder": "Поиск...",
|
||||
"addTool": "Добавить инструмент",
|
||||
"noToolsFound": "Инструменты не найдены",
|
||||
|
||||
@@ -54,6 +54,7 @@
|
||||
},
|
||||
"sources": {
|
||||
"title": "在這裡您可以管理所有可用的來源檔案以及您上傳的檔案。",
|
||||
"subtitle": "上傳並管理您的回答所依賴的文件和知識來源",
|
||||
"label": "來源",
|
||||
"name": "來源名稱",
|
||||
"date": "向量日期",
|
||||
@@ -118,6 +119,7 @@
|
||||
},
|
||||
"analytics": {
|
||||
"label": "分析",
|
||||
"subtitle": "追蹤您賃戶的訊息量、Token 使用量和使用者回饋",
|
||||
"filterByChatbot": "按聊天機器人篩選",
|
||||
"selectChatbot": "選擇聊天機器人",
|
||||
"filterOptions": {
|
||||
@@ -137,6 +139,7 @@
|
||||
},
|
||||
"logs": {
|
||||
"label": "日誌",
|
||||
"subtitle": "查看和監控 API 呼叫及會話日誌",
|
||||
"filterByChatbot": "按聊天機器人篩選",
|
||||
"selectChatbot": "選擇聊天機器人",
|
||||
"none": "無",
|
||||
@@ -144,6 +147,7 @@
|
||||
},
|
||||
"tools": {
|
||||
"label": "工具",
|
||||
"subtitle": "瀏覽、連接並設定驅動您的智能體的工具和整合",
|
||||
"searchPlaceholder": "搜尋工具...",
|
||||
"addTool": "新增工具",
|
||||
"noToolsFound": "找不到工具",
|
||||
|
||||
@@ -54,6 +54,7 @@
|
||||
},
|
||||
"sources": {
|
||||
"title": "在这里您可以管理所有可用的源文件以及您上传的文件。",
|
||||
"subtitle": "上传并管理您的回答所依赖的文档和知识来源",
|
||||
"label": "来源",
|
||||
"name": "来源名称",
|
||||
"date": "向量日期",
|
||||
@@ -118,6 +119,7 @@
|
||||
},
|
||||
"analytics": {
|
||||
"label": "分析",
|
||||
"subtitle": "跟踪您账户的消息量、令牌使用情况和用户反馈",
|
||||
"filterByChatbot": "按聊天机器人筛选",
|
||||
"selectChatbot": "选择聊天机器人",
|
||||
"filterOptions": {
|
||||
@@ -137,6 +139,7 @@
|
||||
},
|
||||
"logs": {
|
||||
"label": "日志",
|
||||
"subtitle": "查看和监控 API 调用及会话日志",
|
||||
"filterByChatbot": "按聊天机器人筛选",
|
||||
"selectChatbot": "选择聊天机器人",
|
||||
"none": "无",
|
||||
@@ -144,6 +147,7 @@
|
||||
},
|
||||
"tools": {
|
||||
"label": "工具",
|
||||
"subtitle": "浏览、连接并配置驱动您的智能体的工具和集成",
|
||||
"searchPlaceholder": "搜索工具...",
|
||||
"addTool": "添加工具",
|
||||
"noToolsFound": "未找到工具",
|
||||
|
||||
@@ -40,7 +40,7 @@ export default function AddActionModal({
|
||||
return (
|
||||
<WrapperModal close={() => setModalState('INACTIVE')} className="sm:w-lg">
|
||||
<div>
|
||||
<h2 className="text-jet dark:text-bright-gray px-3 text-xl font-semibold">
|
||||
<h2 className="text-foreground dark:text-foreground px-3 text-xl font-semibold">
|
||||
{t('modals.addAction.title')}
|
||||
</h2>
|
||||
<div className="relative mt-6 px-3">
|
||||
@@ -53,13 +53,13 @@ export default function AddActionModal({
|
||||
setFunctionNameError(!isValidFunctionName(value));
|
||||
}}
|
||||
borderVariant="thin"
|
||||
labelBgClassName="bg-white dark:bg-charleston-green-2"
|
||||
labelBgClassName="bg-card"
|
||||
placeholder={t('modals.addAction.actionNamePlaceholder')}
|
||||
required={true}
|
||||
/>
|
||||
<p
|
||||
className={`mt-2 ml-1 text-xs italic ${
|
||||
functionNameError ? 'text-red-500' : 'text-gray-500'
|
||||
functionNameError ? 'text-red-500' : 'text-muted-foreground'
|
||||
}`}
|
||||
>
|
||||
{functionNameError
|
||||
@@ -70,7 +70,7 @@ export default function AddActionModal({
|
||||
<div className="mt-3 flex flex-row-reverse gap-1 px-3">
|
||||
<button
|
||||
onClick={handleAddAction}
|
||||
className="bg-purple-30 hover:bg-violets-are-blue rounded-3xl px-5 py-2 text-sm text-white transition-all"
|
||||
className="bg-primary hover:bg-primary/90 rounded-3xl px-5 py-2 text-sm text-white transition-all"
|
||||
>
|
||||
{t('modals.addAction.addButton')}
|
||||
</button>
|
||||
@@ -80,7 +80,7 @@ export default function AddActionModal({
|
||||
setModalState('INACTIVE');
|
||||
setActionName('');
|
||||
}}
|
||||
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"
|
||||
className="dark:text-foreground hover:bg-accent dark:hover:bg-accent cursor-pointer rounded-3xl px-5 py-2 text-sm font-medium"
|
||||
>
|
||||
{t('modals.configTool.closeButton')}
|
||||
</button>
|
||||
|
||||
@@ -116,7 +116,7 @@ export default function AddToolModal({
|
||||
>
|
||||
<div className="flex h-full flex-col">
|
||||
<div>
|
||||
<h2 className="text-jet dark:text-bright-gray px-3 text-xl font-semibold">
|
||||
<h2 className="text-foreground dark:text-foreground px-3 text-xl font-semibold">
|
||||
{t('settings.tools.selectToolSetup')}
|
||||
</h2>
|
||||
<div className="mt-5 h-[73vh] overflow-auto px-3 py-px">
|
||||
@@ -131,7 +131,7 @@ export default function AddToolModal({
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
key={index}
|
||||
className="border-light-gainsboro bg-white-3000 dark:border-arsenic dark:bg-gunmetal flex h-52 w-full cursor-pointer flex-col justify-between rounded-2xl border p-6 hover:border-[#9d9d9d] dark:hover:border-[#717179]"
|
||||
className="border-border bg-card hover:bg-accent hover:border-border/80 flex h-52 w-full cursor-pointer flex-col justify-between rounded-2xl border p-6"
|
||||
onClick={() => {
|
||||
setSelectedTool(tool);
|
||||
handleAddTool(tool);
|
||||
@@ -154,11 +154,11 @@ export default function AddToolModal({
|
||||
<div className="mt-[9px]">
|
||||
<p
|
||||
title={tool.displayName}
|
||||
className="text-raisin-black-light dark:text-bright-gray truncate px-1 text-[13px] leading-relaxed font-semibold capitalize"
|
||||
className="text-foreground dark:text-foreground truncate px-1 text-[13px] leading-relaxed font-semibold capitalize"
|
||||
>
|
||||
{tool.displayName}
|
||||
</p>
|
||||
<p className="text-old-silver dark:text-sonic-silver-light mt-1 h-24 overflow-auto px-1 text-[12px] leading-relaxed">
|
||||
<p className="text-muted-foreground mt-1 h-24 overflow-auto px-1 text-[12px] leading-relaxed">
|
||||
{tool.description}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useSelector } from 'react-redux';
|
||||
import ExternalLinkIcon from '../assets/external-link.svg';
|
||||
|
||||
import { Agent } from '../agents/types';
|
||||
import userService from '../api/services/userService';
|
||||
import ExternalLinkIcon from '../assets/external-link.svg';
|
||||
import CopyButton from '../components/CopyButton';
|
||||
import Spinner from '../components/Spinner';
|
||||
import { ActiveState } from '../models/misc';
|
||||
@@ -81,25 +82,25 @@ export default function AgentDetailsModal({
|
||||
if (modalState !== 'ACTIVE') return null;
|
||||
return (
|
||||
<WrapperModal
|
||||
className="sm:w-[512px]"
|
||||
className="sm:w-lg"
|
||||
close={() => {
|
||||
setModalState('INACTIVE');
|
||||
}}
|
||||
>
|
||||
<div>
|
||||
<h2 className="text-jet dark:text-bright-gray text-xl font-semibold">
|
||||
<h2 className="text-foreground dark:text-foreground text-xl font-semibold">
|
||||
{t('modals.agentDetails.title')}
|
||||
</h2>
|
||||
<div className="mt-8 flex flex-col gap-6">
|
||||
<div className="flex flex-col gap-3">
|
||||
<div className="flex items-center gap-2">
|
||||
<h2 className="text-jet dark:text-bright-gray text-base font-semibold">
|
||||
<h2 className="text-foreground dark:text-foreground text-base font-semibold">
|
||||
{t('modals.agentDetails.publicLink')}
|
||||
</h2>
|
||||
</div>
|
||||
{sharedToken ? (
|
||||
<div className="flex flex-col gap-2">
|
||||
<p className="font-roboto inline text-[14px] leading-normal font-medium break-all text-gray-700 dark:text-[#ECECF1]">
|
||||
<p className="font-roboto dark:text-foreground inline text-[14px] leading-normal font-medium break-all text-gray-700">
|
||||
<a
|
||||
href={`${baseURL}/shared/agent/${sharedToken}`}
|
||||
target="_blank"
|
||||
@@ -115,7 +116,7 @@ export default function AgentDetailsModal({
|
||||
</p>
|
||||
<a
|
||||
href="https://docs.docsgpt.cloud/Agents/basics#core-components-of-an-agent"
|
||||
className="text-purple-30 flex w-fit items-center gap-1 hover:underline"
|
||||
className="text-primary flex w-fit items-center gap-1 hover:underline"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
@@ -131,7 +132,7 @@ export default function AgentDetailsModal({
|
||||
</div>
|
||||
) : (
|
||||
<button
|
||||
className="border-purple-30 text-purple-30 hover:bg-purple-30 flex w-28 items-center justify-center rounded-3xl border border-solid px-5 py-2 text-sm font-medium transition-colors hover:text-white"
|
||||
className="border-primary text-primary hover:bg-primary/90 flex w-28 items-center justify-center rounded-3xl border border-solid px-5 py-2 text-sm font-medium transition-colors hover:text-white"
|
||||
onClick={handleGeneratePublicLink}
|
||||
>
|
||||
{loadingStates.publicLink ? (
|
||||
@@ -143,13 +144,13 @@ export default function AgentDetailsModal({
|
||||
)}
|
||||
</div>
|
||||
<div className="flex flex-col gap-3">
|
||||
<h2 className="text-jet dark:text-bright-gray text-base font-semibold">
|
||||
<h2 className="text-foreground dark:text-foreground text-base font-semibold">
|
||||
{t('modals.agentDetails.apiKey')}
|
||||
</h2>
|
||||
{apiKey ? (
|
||||
<div className="flex flex-col gap-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="font-roboto text-[14px] leading-normal font-medium break-all text-gray-700 dark:text-[#ECECF1]">
|
||||
<div className="font-roboto dark:text-foreground text-[14px] leading-normal font-medium break-all text-gray-700">
|
||||
{apiKey}
|
||||
{!apiKey.includes('...') && (
|
||||
<CopyButton
|
||||
@@ -162,7 +163,7 @@ export default function AgentDetailsModal({
|
||||
{!apiKey.includes('...') && (
|
||||
<a
|
||||
href={`https://widget.docsgpt.cloud/?api-key=${apiKey}`}
|
||||
className="group border-purple-30 text-purple-30 hover:bg-purple-30 ml-8 flex w-[101px] items-center justify-center gap-1 rounded-[62px] border py-1.5 text-sm font-medium transition-colors hover:text-white"
|
||||
className="group border-primary text-primary hover:bg-primary/90 ml-8 flex w-[101px] items-center justify-center gap-1 rounded-[62px] border py-1.5 text-sm font-medium transition-colors hover:text-white"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
@@ -177,20 +178,20 @@ export default function AgentDetailsModal({
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<button className="border-purple-30 text-purple-30 hover:bg-purple-30 w-28 rounded-3xl border border-solid px-5 py-2 text-sm font-medium transition-colors hover:text-white">
|
||||
<button className="border-primary text-primary hover:bg-primary/90 w-28 rounded-3xl border border-solid px-5 py-2 text-sm font-medium transition-colors hover:text-white">
|
||||
{t('modals.agentDetails.generate')}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex flex-col gap-3">
|
||||
<div className="flex items-center gap-2">
|
||||
<h2 className="text-jet dark:text-bright-gray text-base font-semibold">
|
||||
<h2 className="text-foreground dark:text-foreground text-base font-semibold">
|
||||
{t('modals.agentDetails.webhookUrl')}
|
||||
</h2>
|
||||
</div>
|
||||
{webhookUrl ? (
|
||||
<div className="flex flex-col gap-2">
|
||||
<p className="font-roboto text-[14px] leading-normal font-medium break-all text-gray-700 dark:text-[#ECECF1]">
|
||||
<p className="font-roboto dark:text-foreground text-[14px] leading-normal font-medium break-all text-gray-700">
|
||||
<a href={webhookUrl} target="_blank" rel="noreferrer">
|
||||
{webhookUrl}
|
||||
</a>
|
||||
@@ -202,7 +203,7 @@ export default function AgentDetailsModal({
|
||||
</p>
|
||||
<a
|
||||
href="https://docs.docsgpt.cloud/Agents/basics#core-components-of-an-agent"
|
||||
className="text-purple-30 flex w-fit items-center gap-1 hover:underline"
|
||||
className="text-primary flex w-fit items-center gap-1 hover:underline"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
@@ -218,7 +219,7 @@ export default function AgentDetailsModal({
|
||||
</div>
|
||||
) : (
|
||||
<button
|
||||
className="border-purple-30 text-purple-30 hover:bg-purple-30 flex w-28 items-center justify-center rounded-3xl border border-solid px-5 py-2 text-sm font-medium transition-colors hover:text-white"
|
||||
className="border-primary text-primary hover:bg-primary/90 flex w-28 items-center justify-center rounded-3xl border border-solid px-5 py-2 text-sm font-medium transition-colors hover:text-white"
|
||||
onClick={handleGenerateWebhook}
|
||||
>
|
||||
{loadingStates.webhook ? (
|
||||
|
||||
@@ -115,7 +115,7 @@ export default function ConfigToolModal({
|
||||
return (
|
||||
<WrapperModal close={handleClose}>
|
||||
<div className="w-[400px] max-w-[90vw]">
|
||||
<h2 className="text-eerie-black dark:text-bright-gray text-xl font-semibold">
|
||||
<h2 className="text-foreground dark:text-foreground text-xl font-semibold">
|
||||
{t('modals.configTool.title')}
|
||||
</h2>
|
||||
<p className="mt-2 text-sm text-gray-500 dark:text-gray-400">
|
||||
@@ -140,19 +140,21 @@ export default function ConfigToolModal({
|
||||
/>
|
||||
</div>
|
||||
|
||||
{hasConfig && <ConfigFields
|
||||
configRequirements={configRequirements}
|
||||
values={configValues}
|
||||
onChange={handleFieldChange}
|
||||
errors={errors}
|
||||
/>}
|
||||
{hasConfig && (
|
||||
<ConfigFields
|
||||
configRequirements={configRequirements}
|
||||
values={configValues}
|
||||
onChange={handleFieldChange}
|
||||
errors={errors}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="mt-8 flex flex-row-reverse gap-2">
|
||||
<button
|
||||
onClick={handleAddTool}
|
||||
disabled={saving}
|
||||
className="bg-purple-30 hover:bg-violets-are-blue disabled:opacity-60 rounded-full px-5 py-2 text-sm font-medium text-white transition-colors"
|
||||
className="bg-primary hover:bg-primary/90 rounded-full px-5 py-2 text-sm font-medium text-white transition-colors disabled:opacity-60"
|
||||
>
|
||||
{saving
|
||||
? t('modals.configTool.addButton') + '…'
|
||||
@@ -160,7 +162,7 @@ export default function ConfigToolModal({
|
||||
</button>
|
||||
<button
|
||||
onClick={handleClose}
|
||||
className="dark:text-light-gray cursor-pointer rounded-full px-5 py-2 text-sm font-medium hover:bg-gray-100 dark:bg-transparent dark:hover:bg-[#767183]/50"
|
||||
className="dark:text-foreground hover:bg-accent dark:hover:bg-accent cursor-pointer rounded-full px-5 py-2 text-sm font-medium"
|
||||
>
|
||||
{t('modals.configTool.closeButton')}
|
||||
</button>
|
||||
|
||||
@@ -26,8 +26,8 @@ export default function ConfirmationModal({
|
||||
|
||||
const submitButtonClasses =
|
||||
variant === 'danger'
|
||||
? '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';
|
||||
? 'rounded-3xl bg-destructive px-5 py-2 text-sm text-white transition-all hover:bg-destructive/90 hover:font-bold tracking-[0.019em] hover:tracking-normal'
|
||||
: 'rounded-3xl bg-primary px-5 py-2 text-sm text-white transition-all hover:bg-primary/90';
|
||||
|
||||
const handleSubmitClick = (e: React.MouseEvent) => {
|
||||
e.preventDefault();
|
||||
@@ -49,7 +49,7 @@ export default function ConfirmationModal({
|
||||
<WrapperModal close={() => setModalState('INACTIVE')}>
|
||||
<div className="relative">
|
||||
<div>
|
||||
<p className="font-base text-jet dark:text-bright-gray mb-1 w-[90%] text-lg break-words">
|
||||
<p className="font-base text-foreground dark:text-foreground mb-1 w-[90%] text-lg wrap-break-word">
|
||||
{message}
|
||||
</p>
|
||||
<div>
|
||||
@@ -62,7 +62,7 @@ export default function ConfirmationModal({
|
||||
</button>
|
||||
<button
|
||||
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"
|
||||
className="dark:text-foreground hover:bg-accent dark:hover:bg-accent cursor-pointer rounded-3xl px-5 py-2 text-sm font-medium"
|
||||
>
|
||||
{cancelLabel ? cancelLabel : t('cancel')}
|
||||
</button>
|
||||
|
||||
@@ -47,7 +47,7 @@ export default function FolderNameModal({
|
||||
return (
|
||||
<WrapperModal close={() => setModalState('INACTIVE')}>
|
||||
<div className="w-72">
|
||||
<h2 className="text-jet dark:text-bright-gray mb-4 text-lg font-semibold">
|
||||
<h2 className="text-foreground dark:text-foreground mb-4 text-lg font-semibold">
|
||||
{mode === 'create'
|
||||
? t('agents.folders.newFolder')
|
||||
: t('agents.folders.rename')}
|
||||
@@ -59,7 +59,7 @@ export default function FolderNameModal({
|
||||
onKeyDown={handleKeyDown}
|
||||
placeholder={t('agents.folders.folderName')}
|
||||
autoFocus
|
||||
className="w-full rounded-lg border border-[#E5E5E5] bg-white px-3 py-2 text-sm outline-none dark:border-[#3A3A3A] dark:bg-[#2C2C2C] dark:text-white"
|
||||
className="border-border bg-card w-full rounded-lg border px-3 py-2 text-sm outline-none dark:text-white"
|
||||
/>
|
||||
<div className="mt-6 flex justify-end gap-2">
|
||||
<button
|
||||
@@ -67,14 +67,14 @@ export default function FolderNameModal({
|
||||
setModalState('INACTIVE');
|
||||
setName('');
|
||||
}}
|
||||
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"
|
||||
className="dark:text-foreground hover:bg-accent dark:hover:bg-accent cursor-pointer rounded-3xl px-5 py-2 text-sm font-medium"
|
||||
>
|
||||
{t('cancel')}
|
||||
</button>
|
||||
<button
|
||||
onClick={handleSubmit}
|
||||
disabled={!name.trim()}
|
||||
className="bg-purple-30 hover:bg-violets-are-blue rounded-3xl px-5 py-2 text-sm text-white disabled:opacity-50"
|
||||
className="bg-primary hover:bg-primary/90 rounded-3xl px-5 py-2 text-sm text-white disabled:opacity-50"
|
||||
>
|
||||
{mode === 'create'
|
||||
? t('agents.folders.createFolder')
|
||||
|
||||
@@ -166,7 +166,7 @@ export default function ImportSpecModal({
|
||||
contentClassName="max-h-[70vh]"
|
||||
>
|
||||
<div className="flex flex-col gap-4">
|
||||
<h2 className="text-jet dark:text-bright-gray text-xl font-semibold">
|
||||
<h2 className="text-foreground dark:text-foreground text-xl font-semibold">
|
||||
{t('modals.importSpec.title')}
|
||||
</h2>
|
||||
|
||||
@@ -178,14 +178,14 @@ export default function ImportSpecModal({
|
||||
|
||||
<div
|
||||
onClick={() => fileInputRef.current?.click()}
|
||||
className="border-silver dark:border-silver/40 hover:border-purple-30 dark:hover:border-purple-30 flex cursor-pointer flex-col items-center justify-center rounded-xl border-2 border-dashed p-8 transition-colors"
|
||||
className="border-border dark:border-border hover:border-primary dark:hover:border-primary flex cursor-pointer flex-col items-center justify-center rounded-xl border-2 border-dashed p-8 transition-colors"
|
||||
>
|
||||
<img
|
||||
src={Upload}
|
||||
alt="Upload"
|
||||
className="mb-3 h-10 w-10 opacity-60 dark:invert"
|
||||
/>
|
||||
<p className="text-jet dark:text-bright-gray text-sm font-medium">
|
||||
<p className="text-foreground dark:text-foreground text-sm font-medium">
|
||||
{file ? file.name : t('modals.importSpec.dropzoneText')}
|
||||
</p>
|
||||
<p className="mt-1 text-xs text-gray-500 dark:text-gray-400">
|
||||
@@ -206,8 +206,8 @@ export default function ImportSpecModal({
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex flex-col gap-4">
|
||||
<div className="rounded-xl bg-[#F9F9F9] p-4 dark:bg-[#28292D]">
|
||||
<h3 className="text-jet dark:text-bright-gray font-medium">
|
||||
<div className="bg-muted rounded-xl p-4">
|
||||
<h3 className="text-foreground dark:text-foreground font-medium">
|
||||
{parsedResult.metadata.title}
|
||||
</h3>
|
||||
{parsedResult.metadata.description && (
|
||||
@@ -215,7 +215,7 @@ export default function ImportSpecModal({
|
||||
{parsedResult.metadata.description}
|
||||
</p>
|
||||
)}
|
||||
<p className="mt-2 text-xs text-gray-500">
|
||||
<p className="text-muted-foreground mt-2 text-xs">
|
||||
{t('modals.importSpec.version')}:{' '}
|
||||
{parsedResult.metadata.version}
|
||||
</p>
|
||||
@@ -227,7 +227,7 @@ export default function ImportSpecModal({
|
||||
type="text"
|
||||
value={baseUrl}
|
||||
onChange={(e) => setBaseUrl(e.target.value)}
|
||||
className="border-silver dark:border-silver/40 text-jet dark:text-bright-gray w-full rounded-lg border bg-white px-3 py-2 text-sm outline-hidden dark:bg-[#2C2C2C]"
|
||||
className="border-border dark:border-border text-foreground dark:text-foreground bg-card w-full rounded-lg border px-3 py-2 text-sm outline-hidden"
|
||||
placeholder={
|
||||
parsedResult.metadata.base_url || 'https://api.example.com'
|
||||
}
|
||||
@@ -236,14 +236,14 @@ export default function ImportSpecModal({
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between px-1">
|
||||
<p className="text-jet dark:text-bright-gray text-sm font-medium">
|
||||
<p className="text-foreground dark:text-foreground text-sm font-medium">
|
||||
{t('modals.importSpec.actionsFound', {
|
||||
count: parsedResult.actions.length,
|
||||
})}
|
||||
</p>
|
||||
<button
|
||||
onClick={toggleAll}
|
||||
className="text-purple-30 hover:text-violets-are-blue text-sm"
|
||||
className="text-primary hover:text-primary text-sm"
|
||||
>
|
||||
{selectedActions.size === parsedResult.actions.length
|
||||
? t('modals.importSpec.deselectAll')
|
||||
@@ -255,13 +255,13 @@ export default function ImportSpecModal({
|
||||
{parsedResult.actions.map((action, index) => (
|
||||
<label
|
||||
key={index}
|
||||
className="border-silver dark:border-silver/40 flex cursor-pointer items-start gap-3 rounded-xl border p-3 transition-colors hover:bg-[#F9F9F9] dark:hover:bg-[#28292D]"
|
||||
className="border-border dark:border-border hover:bg-muted dark:hover:bg-muted flex cursor-pointer items-start gap-3 rounded-xl border p-3 transition-colors"
|
||||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={selectedActions.has(index)}
|
||||
onChange={() => toggleAction(index)}
|
||||
className="text-purple-30 focus:ring-purple-30 mt-1 h-4 w-4 rounded border-gray-300"
|
||||
className="text-primary focus:ring-ring mt-1 h-4 w-4 rounded border-gray-300"
|
||||
/>
|
||||
<div className="min-w-0 flex-1">
|
||||
<div className="flex items-center gap-2">
|
||||
@@ -270,7 +270,7 @@ export default function ImportSpecModal({
|
||||
>
|
||||
{action.method.toUpperCase()}
|
||||
</span>
|
||||
<span className="text-jet dark:text-bright-gray truncate font-medium">
|
||||
<span className="text-foreground dark:text-foreground truncate font-medium">
|
||||
{action.name}
|
||||
</span>
|
||||
</div>
|
||||
@@ -294,7 +294,7 @@ export default function ImportSpecModal({
|
||||
<button
|
||||
onClick={handleParse}
|
||||
disabled={!file || loading}
|
||||
className="bg-purple-30 hover:bg-violets-are-blue flex w-20 items-center justify-center gap-2 rounded-3xl px-5 py-2 text-sm text-white transition-all disabled:cursor-not-allowed disabled:opacity-50"
|
||||
className="bg-primary hover:bg-primary/90 flex w-20 items-center justify-center gap-2 rounded-3xl px-5 py-2 text-sm text-white transition-all disabled:cursor-not-allowed disabled:opacity-50"
|
||||
>
|
||||
{loading && <Spinner size="small" color="white" />}
|
||||
{!loading && t('modals.importSpec.parse')}
|
||||
@@ -303,14 +303,14 @@ export default function ImportSpecModal({
|
||||
<button
|
||||
onClick={handleImport}
|
||||
disabled={selectedActions.size === 0}
|
||||
className="bg-purple-30 hover:bg-violets-are-blue rounded-3xl px-5 py-2 text-sm text-white transition-all disabled:cursor-not-allowed disabled:opacity-50"
|
||||
className="bg-primary hover:bg-primary/90 rounded-3xl px-5 py-2 text-sm text-white transition-all disabled:cursor-not-allowed disabled:opacity-50"
|
||||
>
|
||||
{t('modals.importSpec.import', { count: selectedActions.size })}
|
||||
</button>
|
||||
)}
|
||||
<button
|
||||
onClick={handleClose}
|
||||
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"
|
||||
className="dark:text-foreground hover:bg-accent dark:hover:bg-accent cursor-pointer rounded-3xl px-5 py-2 text-sm font-medium"
|
||||
>
|
||||
{t('modals.importSpec.cancel')}
|
||||
</button>
|
||||
|
||||
@@ -24,7 +24,7 @@ export default function JWTModal({
|
||||
close={() => undefined}
|
||||
>
|
||||
<div className="mb-6">
|
||||
<span className="text-jet dark:text-bright-gray text-lg">
|
||||
<span className="text-foreground dark:text-foreground text-lg">
|
||||
Add JWT Token
|
||||
</span>
|
||||
</div>
|
||||
@@ -41,7 +41,7 @@ export default function JWTModal({
|
||||
<button
|
||||
disabled={jwtToken.length === 0}
|
||||
onClick={handleTokenSubmit.bind(null, jwtToken)}
|
||||
className="bg-purple-30 float-right mt-4 rounded-full px-5 py-2 text-sm text-white hover:bg-[#6F3FD1] disabled:opacity-50"
|
||||
className="bg-primary float-right mt-4 rounded-full px-5 py-2 text-sm text-white hover:bg-[#6F3FD1] disabled:opacity-50"
|
||||
>
|
||||
Save Token
|
||||
</button>
|
||||
|
||||
@@ -559,7 +559,7 @@ export default function MCPServerModal({
|
||||
>
|
||||
<div className="flex h-full flex-col">
|
||||
<div className="px-6 py-4">
|
||||
<h2 className="text-jet dark:text-bright-gray text-xl font-semibold">
|
||||
<h2 className="text-foreground dark:text-foreground text-xl font-semibold">
|
||||
{server
|
||||
? t('settings.tools.mcp.reconnectServer', {
|
||||
defaultValue: 'Reconnect Server',
|
||||
@@ -711,7 +711,7 @@ export default function MCPServerModal({
|
||||
)}
|
||||
|
||||
{discoveredTools.length > 0 && testResult?.success && (
|
||||
<div className="border-silver dark:border-silver/40 rounded-xl border p-4">
|
||||
<div className="border-border dark:border-border rounded-xl border p-4">
|
||||
<h4 className="mb-2 text-sm font-medium text-gray-900 dark:text-white">
|
||||
{t('settings.tools.mcp.discoveredTools', {
|
||||
count: discoveredTools.length,
|
||||
@@ -724,7 +724,7 @@ export default function MCPServerModal({
|
||||
key={tool.name}
|
||||
className="flex items-start gap-2 rounded-lg bg-gray-50 px-3 py-2 text-sm dark:bg-white/5"
|
||||
>
|
||||
<span className="text-purple-30 mt-0.5">●</span>
|
||||
<span className="text-primary mt-0.5">●</span>
|
||||
<div className="min-w-0">
|
||||
<span className="font-medium text-gray-900 dark:text-white">
|
||||
{tool.name}
|
||||
@@ -753,7 +753,7 @@ export default function MCPServerModal({
|
||||
<button
|
||||
onClick={testConnection}
|
||||
disabled={testing}
|
||||
className="border-silver dark:border-silver/40 dark:text-light-gray w-full rounded-3xl border px-6 py-2 text-sm font-medium transition-all hover:bg-gray-100 disabled:opacity-50 sm:w-auto dark:hover:bg-[#767183]/50"
|
||||
className="border-border dark:border-border dark:text-foreground hover:bg-accent dark:hover:bg-muted/50 w-full rounded-3xl border px-6 py-2 text-sm font-medium transition-all disabled:opacity-50 sm:w-auto"
|
||||
>
|
||||
{testing ? (
|
||||
<div className="flex items-center justify-center">
|
||||
@@ -773,14 +773,14 @@ export default function MCPServerModal({
|
||||
setModalState('INACTIVE');
|
||||
resetForm();
|
||||
}}
|
||||
className="dark:text-light-gray w-full cursor-pointer rounded-3xl px-6 py-2 text-sm font-medium hover:bg-gray-100 sm:w-auto dark:bg-transparent dark:hover:bg-[#767183]/50"
|
||||
className="dark:text-foreground hover:bg-accent dark:hover:bg-muted/50 w-full cursor-pointer rounded-3xl px-6 py-2 text-sm font-medium sm:w-auto"
|
||||
>
|
||||
{t('settings.tools.mcp.cancel')}
|
||||
</button>
|
||||
<button
|
||||
onClick={handleSave}
|
||||
disabled={loading || !saveActive}
|
||||
className="bg-purple-30 hover:bg-violets-are-blue w-full rounded-3xl px-6 py-2 text-sm font-medium text-white transition-all disabled:opacity-50 sm:w-auto"
|
||||
className="bg-primary hover:bg-primary/90 w-full rounded-3xl px-6 py-2 text-sm font-medium text-white transition-all disabled:opacity-50 sm:w-auto"
|
||||
>
|
||||
{loading ? (
|
||||
<div className="flex items-center justify-center">
|
||||
|
||||
@@ -139,7 +139,7 @@ export default function MoveToFolderModal({
|
||||
<div className="w-[800px] max-w-[90vw]">
|
||||
<div className="px-6 pt-4">
|
||||
<h2
|
||||
className="text-jet dark:text-bright-gray mb-2 font-semibold"
|
||||
className="text-foreground dark:text-foreground mb-2 font-semibold"
|
||||
style={{
|
||||
fontFamily: 'Inter, sans-serif',
|
||||
fontSize: '22px',
|
||||
@@ -151,12 +151,12 @@ export default function MoveToFolderModal({
|
||||
</h2>
|
||||
</div>
|
||||
<div
|
||||
className="flex items-center gap-1 bg-[#F6F8FA] px-8 py-2 text-xs font-semibold text-[#59636E] dark:bg-[#2A2A2A] dark:text-gray-400"
|
||||
className="bg-muted dark:bg-muted flex items-center gap-1 px-8 py-2 text-xs font-semibold text-[#59636E] dark:text-gray-400"
|
||||
style={{ fontFamily: "'Segoe UI', sans-serif" }}
|
||||
>
|
||||
<button
|
||||
onClick={() => handleNavigateToPath(-1)}
|
||||
className={`hover:text-[#18181B] dark:hover:text-white ${folderPath.length > 0 ? 'opacity-70' : ''}`}
|
||||
className={`hover:text-foreground dark:hover:text-white ${folderPath.length > 0 ? 'opacity-70' : ''}`}
|
||||
>
|
||||
{t('agents.filters.byMe')}
|
||||
</button>
|
||||
@@ -180,7 +180,7 @@ export default function MoveToFolderModal({
|
||||
) : (
|
||||
<button
|
||||
onClick={() => handleNavigateToPath(index)}
|
||||
className="opacity-70 hover:text-[#18181B] dark:hover:text-white"
|
||||
className="hover:text-foreground opacity-70 dark:hover:text-white"
|
||||
>
|
||||
{item.name}
|
||||
</button>
|
||||
@@ -188,10 +188,10 @@ export default function MoveToFolderModal({
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
<div className="max-h-60 min-h-[200px] overflow-y-auto border-t border-gray-200 dark:border-[#3A3A3A]">
|
||||
<div className="dark:border-border max-h-60 min-h-[200px] overflow-y-auto border-t border-gray-200">
|
||||
{isLoading ? (
|
||||
<div className="flex h-[200px] items-center justify-center">
|
||||
<span className="text-[14px] text-gray-500">
|
||||
<span className="text-muted-foreground text-[14px]">
|
||||
{t('loading')}...
|
||||
</span>
|
||||
</div>
|
||||
@@ -204,10 +204,10 @@ export default function MoveToFolderModal({
|
||||
e.stopPropagation();
|
||||
setSelectedFolderId(null);
|
||||
}}
|
||||
className={`flex w-full items-center gap-2 border-b border-gray-200 px-8 py-2 text-left text-[14px] dark:border-[#3A3A3A] ${
|
||||
className={`dark:border-border flex w-full items-center gap-2 border-b border-gray-200 px-8 py-2 text-left text-[14px] ${
|
||||
selectedFolderId === null
|
||||
? 'bg-[#7D54D1] text-white'
|
||||
: 'bg-[#F9F9F9] hover:bg-gray-100 dark:bg-[#2A2A2A] dark:hover:bg-[#383838]'
|
||||
: 'bg-muted hover:bg-accent dark:bg-muted'
|
||||
}`}
|
||||
>
|
||||
<span
|
||||
@@ -226,10 +226,10 @@ export default function MoveToFolderModal({
|
||||
<button
|
||||
key={folder.id}
|
||||
onClick={() => setSelectedFolderId(folder.id)}
|
||||
className={`flex w-full cursor-pointer items-center justify-between border-b border-gray-200 px-8 py-2 text-left text-[14px] dark:border-[#3A3A3A] ${
|
||||
className={`dark:border-border flex w-full cursor-pointer items-center justify-between border-b border-gray-200 px-8 py-2 text-left text-[14px] ${
|
||||
selectedFolderId === folder.id
|
||||
? 'bg-[#7D54D1] text-white'
|
||||
: 'bg-[#F9F9F9] hover:bg-gray-100 dark:bg-[#2A2A2A] dark:hover:bg-[#383838]'
|
||||
: 'bg-muted hover:bg-accent dark:bg-muted'
|
||||
}`}
|
||||
>
|
||||
<span className="flex flex-1 items-center gap-2">
|
||||
@@ -239,7 +239,7 @@ export default function MoveToFolderModal({
|
||||
className={`h-4 w-4 ${selectedFolderId === folder.id ? 'brightness-0 invert' : ''}`}
|
||||
/>
|
||||
<span
|
||||
className={`truncate ${selectedFolderId === folder.id ? 'text-white' : 'text-[#18181B] dark:text-[#E0E0E0]'}`}
|
||||
className={`truncate ${selectedFolderId === folder.id ? 'text-white' : 'text-foreground'}`}
|
||||
>
|
||||
{folder.name}
|
||||
</span>
|
||||
@@ -271,14 +271,14 @@ export default function MoveToFolderModal({
|
||||
</button>
|
||||
))}
|
||||
{currentLevelFolders.length === 0 && folderPath.length > 0 && (
|
||||
<div className="flex h-[200px] items-center justify-center text-sm text-[#71717A]">
|
||||
<div className="text-muted-foreground flex h-[200px] items-center justify-center text-sm">
|
||||
{t('agents.folders.noSubfolders')}
|
||||
</div>
|
||||
)}
|
||||
{currentLevelFolders.length === 0 &&
|
||||
folderPath.length === 0 &&
|
||||
!currentFolderId && (
|
||||
<div className="flex h-[200px] items-center justify-center text-sm text-[#71717A]">
|
||||
<div className="text-muted-foreground flex h-[200px] items-center justify-center text-sm">
|
||||
{t('agents.folders.noFolders')}
|
||||
</div>
|
||||
)}
|
||||
@@ -286,7 +286,7 @@ export default function MoveToFolderModal({
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between border-t border-gray-200 px-8 py-4 dark:border-[#3A3A3A]">
|
||||
<div className="dark:border-border flex items-center justify-between border-t border-gray-200 px-8 py-4">
|
||||
{isCreatingFolder ? (
|
||||
<input
|
||||
ref={newFolderInputRef}
|
||||
@@ -309,7 +309,7 @@ export default function MoveToFolderModal({
|
||||
}
|
||||
}}
|
||||
placeholder={t('agents.folders.newFolder')}
|
||||
className="rounded-full border border-[#7D54D1] bg-transparent px-6 py-2 text-sm font-medium text-[#7D54D1] outline-none placeholder:text-[#7D54D1]/60 dark:text-[#B794F4] dark:placeholder:text-[#B794F4]/60"
|
||||
className="dark:text-primary rounded-full border border-[#7D54D1] bg-transparent px-6 py-2 text-sm font-medium text-[#7D54D1] outline-none placeholder:text-[#7D54D1]/60 dark:placeholder:text-[#B794F4]/60"
|
||||
autoFocus
|
||||
/>
|
||||
) : (
|
||||
@@ -336,7 +336,7 @@ export default function MoveToFolderModal({
|
||||
setModalState('INACTIVE');
|
||||
}
|
||||
}}
|
||||
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"
|
||||
className="dark:text-foreground hover:bg-accent dark:hover:bg-accent cursor-pointer rounded-3xl px-5 py-2 text-sm font-medium"
|
||||
>
|
||||
{t('cancel')}
|
||||
</button>
|
||||
@@ -351,7 +351,7 @@ export default function MoveToFolderModal({
|
||||
}
|
||||
}}
|
||||
disabled={!newFolderName.trim()}
|
||||
className="bg-purple-30 hover:bg-violets-are-blue rounded-3xl px-5 py-2 text-sm text-white disabled:opacity-50"
|
||||
className="bg-primary hover:bg-primary/90 rounded-3xl px-5 py-2 text-sm text-white disabled:opacity-50"
|
||||
>
|
||||
{t('agents.folders.createFolder')}
|
||||
</button>
|
||||
@@ -361,7 +361,7 @@ export default function MoveToFolderModal({
|
||||
e.stopPropagation();
|
||||
handleMove();
|
||||
}}
|
||||
className="bg-purple-30 hover:bg-violets-are-blue rounded-3xl px-5 py-2 text-sm text-white disabled:opacity-50"
|
||||
className="bg-primary hover:bg-primary/90 rounded-3xl px-5 py-2 text-sm text-white disabled:opacity-50"
|
||||
>
|
||||
{t('agents.folders.move')}
|
||||
</button>
|
||||
|
||||
@@ -98,14 +98,14 @@ export const ShareConversationModal = ({
|
||||
return (
|
||||
<WrapperModal close={close} contentClassName="!overflow-visible">
|
||||
<div className="flex w-[600px] max-w-[80vw] flex-col gap-2">
|
||||
<h2 className="text-eerie-black dark:text-chinese-white text-xl font-medium">
|
||||
<h2 className="text-foreground dark:text-foreground text-xl font-medium">
|
||||
{t('modals.shareConv.label')}
|
||||
</h2>
|
||||
<p className="text-eerie-black dark:text-silver/60 text-sm leading-relaxed">
|
||||
<p className="text-foreground dark:text-muted-foreground/60 text-sm leading-relaxed">
|
||||
{t('modals.shareConv.note')}
|
||||
</p>
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-eerie-black text-lg dark:text-white">
|
||||
<span className="text-foreground text-lg dark:text-white">
|
||||
{t('modals.shareConv.option')}
|
||||
</span>
|
||||
<ToggleSwitch
|
||||
@@ -129,19 +129,19 @@ export const ShareConversationModal = ({
|
||||
</div>
|
||||
)}
|
||||
<div className="flex items-baseline justify-between gap-2">
|
||||
<span className="no-scrollbar border-silver text-eerie-black dark:border-silver/40 w-full overflow-x-auto rounded-full border-2 px-4 py-3 whitespace-nowrap dark:text-white">
|
||||
<span className="no-scrollbar border-border text-foreground dark:border-border w-full overflow-x-auto rounded-full border-2 px-4 py-3 whitespace-nowrap dark:text-white">
|
||||
{`${domain}/share/${identifier ?? '....'}`}
|
||||
</span>
|
||||
{status === 'fetched' ? (
|
||||
<button
|
||||
className="bg-purple-30 hover:bg-violets-are-blue my-1 h-10 w-28 rounded-full p-2 text-sm text-white"
|
||||
className="bg-primary hover:bg-primary/90 my-1 h-10 w-28 rounded-full p-2 text-sm text-white"
|
||||
onClick={() => handleCopyKey(`${domain}/share/${identifier}`)}
|
||||
>
|
||||
{isCopied ? t('modals.saveKey.copied') : t('modals.saveKey.copy')}
|
||||
</button>
|
||||
) : (
|
||||
<button
|
||||
className="bg-purple-30 hover:bg-violets-are-blue my-1 flex h-10 w-28 items-center justify-evenly rounded-full p-2 text-center text-sm font-normal text-white"
|
||||
className="bg-primary hover:bg-primary/90 my-1 flex h-10 w-28 items-center justify-evenly rounded-full p-2 text-center text-sm font-normal text-white"
|
||||
onClick={() => {
|
||||
shareCoversationPublicly(allowPrompt);
|
||||
}}
|
||||
|
||||
@@ -32,8 +32,7 @@ export default function WrapperModal({
|
||||
)
|
||||
return;
|
||||
if (document.querySelector('[data-radix-select-content]')) return;
|
||||
if (modalRef.current && !modalRef.current.contains(target))
|
||||
close();
|
||||
if (modalRef.current && !modalRef.current.contains(target)) close();
|
||||
};
|
||||
|
||||
const handleEscapePress = (event: KeyboardEvent) => {
|
||||
@@ -61,7 +60,7 @@ export default function WrapperModal({
|
||||
/>
|
||||
<div
|
||||
ref={modalRef}
|
||||
className={`relative rounded-2xl bg-white p-8 shadow-[0px_4px_40px_-3px_#0000001A] dark:bg-[#26272E] ${className}`}
|
||||
className={`bg-card dark:bg-card relative rounded-2xl p-8 shadow-[0px_4px_40px_-3px_#0000001A] ${className}`}
|
||||
>
|
||||
{!isPerformingTask && (
|
||||
<button
|
||||
@@ -72,7 +71,7 @@ export default function WrapperModal({
|
||||
</button>
|
||||
)}
|
||||
<div
|
||||
className={`no-scrollbar overflow-y-auto text-[#18181B] dark:text-[#ECECF1] ${contentClassName}`}
|
||||
className={`no-scrollbar text-foreground dark:text-foreground overflow-y-auto ${contentClassName}`}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
|
||||
@@ -120,11 +120,11 @@ function PromptTextarea({
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
className="pointer-events-none absolute inset-0 z-0 overflow-hidden rounded bg-white px-3 py-2 dark:bg-[#26272E]"
|
||||
className="bg-card pointer-events-none absolute inset-0 z-0 overflow-hidden rounded px-3 py-2"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<div
|
||||
className="min-h-full text-base leading-[1.5] break-words whitespace-pre-wrap text-transparent"
|
||||
className="min-h-full text-base leading-normal wrap-break-word whitespace-pre-wrap text-transparent"
|
||||
style={{
|
||||
transform: `translate(${-scrollOffsets.left}px, ${-scrollOffsets.top}px)`,
|
||||
}}
|
||||
@@ -134,7 +134,7 @@ function PromptTextarea({
|
||||
</div>
|
||||
<textarea
|
||||
id={id}
|
||||
className="peer border-silver dark:border-silver/40 relative z-10 h-48 w-full resize-none rounded border-2 bg-transparent px-3 py-2 text-base text-gray-800 outline-none md:h-64 lg:h-80 dark:bg-transparent dark:text-white"
|
||||
className="peer border-border dark:border-border relative z-10 h-48 w-full resize-none rounded border-2 bg-transparent px-3 py-2 text-base text-gray-800 outline-none md:h-64 lg:h-80 dark:text-white"
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
onScroll={handleScroll}
|
||||
@@ -239,7 +239,7 @@ function AddPrompt({
|
||||
<p className="mb-1 text-xl font-semibold text-[#2B2B2B] dark:text-white">
|
||||
{t('modals.prompts.addPrompt')}
|
||||
</p>
|
||||
<p className="mb-6 text-sm text-[#6B6B6B] dark:text-[#9A9AA0]">
|
||||
<p className="dark:text-muted-foreground mb-6 text-sm text-[#6B6B6B]">
|
||||
{t('modals.prompts.addDescription')}
|
||||
</p>
|
||||
<div>
|
||||
@@ -251,7 +251,7 @@ function AddPrompt({
|
||||
textSize="medium"
|
||||
value={newPromptName}
|
||||
onChange={(e) => setNewPromptName(e.target.value)}
|
||||
labelBgClassName="bg-white dark:bg-[#26272E]"
|
||||
labelBgClassName="bg-card"
|
||||
borderVariant="thick"
|
||||
/>
|
||||
|
||||
@@ -266,7 +266,7 @@ function AddPrompt({
|
||||
htmlFor="new-prompt-content"
|
||||
className={`absolute z-20 select-none ${
|
||||
newPromptContent ? '-top-2.5 left-3 text-xs' : ''
|
||||
} text-gray-4000 pointer-events-none max-w-[calc(100%-24px)] cursor-none overflow-hidden bg-white px-2 text-ellipsis whitespace-nowrap transition-all peer-placeholder-shown:top-2.5 peer-placeholder-shown:left-3 peer-placeholder-shown:text-base peer-focus:-top-2.5 peer-focus:left-3 peer-focus:text-xs dark:bg-[#26272E] dark:text-gray-400`}
|
||||
} text-muted-foreground bg-card pointer-events-none max-w-[calc(100%-24px)] cursor-none overflow-hidden px-2 text-ellipsis whitespace-nowrap transition-all peer-placeholder-shown:top-2.5 peer-placeholder-shown:left-3 peer-placeholder-shown:text-base peer-focus:-top-2.5 peer-focus:left-3 peer-focus:text-xs`}
|
||||
>
|
||||
{t('modals.prompts.promptText')}
|
||||
</label>
|
||||
@@ -278,7 +278,7 @@ function AddPrompt({
|
||||
<span className="font-bold">
|
||||
{t('modals.prompts.variablesLabel')}
|
||||
</span>
|
||||
<span className="text-xs text-[10px] font-medium text-gray-500">
|
||||
<span className="text-muted-foreground text-xs text-[10px] font-medium">
|
||||
{t('modals.prompts.variablesDescription')}
|
||||
</span>
|
||||
</p>
|
||||
@@ -326,7 +326,6 @@ function AddPrompt({
|
||||
placeholder={t('modals.prompts.systemVariablesDropdownLabel')}
|
||||
size="w-[140px] sm:w-[185px]"
|
||||
rounded="3xl"
|
||||
border="border"
|
||||
contentSize="text-[12px] sm:text-[14px]"
|
||||
/>
|
||||
|
||||
@@ -371,7 +370,6 @@ function AddPrompt({
|
||||
placeholder="Tool Variables"
|
||||
size="w-[140px] sm:w-[171px]"
|
||||
rounded="3xl"
|
||||
border="border"
|
||||
contentSize="text-[12px] sm:text-[14px]"
|
||||
/>
|
||||
</div>
|
||||
@@ -387,7 +385,7 @@ function AddPrompt({
|
||||
<img
|
||||
src={BookIcon}
|
||||
alt=""
|
||||
className="flex h-4 w-3 flex-shrink-0 items-center justify-center"
|
||||
className="flex h-4 w-3 shrink-0 items-center justify-center"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<span className="text-[14px] font-bold">
|
||||
@@ -448,7 +446,7 @@ function EditPrompt({
|
||||
<p className="mb-1 text-xl font-semibold text-[#2B2B2B] dark:text-white">
|
||||
{t('modals.prompts.editPrompt')}
|
||||
</p>
|
||||
<p className="mb-6 text-sm text-[#6B6B6B] dark:text-[#9A9AA0]">
|
||||
<p className="dark:text-muted-foreground mb-6 text-sm text-[#6B6B6B]">
|
||||
{t('modals.prompts.editDescription')}
|
||||
</p>
|
||||
<div>
|
||||
@@ -460,7 +458,7 @@ function EditPrompt({
|
||||
textSize="medium"
|
||||
value={editPromptName}
|
||||
onChange={(e) => setEditPromptName(e.target.value)}
|
||||
labelBgClassName="bg-white dark:bg-[#26272E]"
|
||||
labelBgClassName="bg-card"
|
||||
borderVariant="thick"
|
||||
/>
|
||||
|
||||
@@ -475,7 +473,7 @@ function EditPrompt({
|
||||
htmlFor="edit-prompt-content"
|
||||
className={`absolute z-20 select-none ${
|
||||
editPromptContent ? '-top-2.5 left-3 text-xs' : ''
|
||||
} text-gray-4000 pointer-events-none max-w-[calc(100%-24px)] cursor-none overflow-hidden bg-white px-2 text-ellipsis whitespace-nowrap transition-all peer-placeholder-shown:top-2.5 peer-placeholder-shown:left-3 peer-placeholder-shown:text-base peer-focus:-top-2.5 peer-focus:left-3 peer-focus:text-xs dark:bg-[#26272E] dark:text-gray-400`}
|
||||
} text-muted-foreground bg-card pointer-events-none max-w-[calc(100%-24px)] cursor-none overflow-hidden px-2 text-ellipsis whitespace-nowrap transition-all peer-placeholder-shown:top-2.5 peer-placeholder-shown:left-3 peer-placeholder-shown:text-base peer-focus:-top-2.5 peer-focus:left-3 peer-focus:text-xs`}
|
||||
>
|
||||
{t('modals.prompts.promptText')}
|
||||
</label>
|
||||
@@ -487,7 +485,7 @@ function EditPrompt({
|
||||
<span className="font-bold">
|
||||
{t('modals.prompts.variablesLabel')}
|
||||
</span>
|
||||
<span className="text-xs text-[10px] font-medium text-gray-500">
|
||||
<span className="text-muted-foreground text-xs text-[10px] font-medium">
|
||||
{t('modals.prompts.variablesDescription')}
|
||||
</span>
|
||||
</p>
|
||||
@@ -535,7 +533,6 @@ function EditPrompt({
|
||||
placeholder={t('modals.prompts.systemVariablesDropdownLabel')}
|
||||
size="w-[140px] sm:w-[185px]"
|
||||
rounded="3xl"
|
||||
border="border"
|
||||
contentSize="text-[12px] sm:text-[14px]"
|
||||
/>
|
||||
|
||||
@@ -580,7 +577,6 @@ function EditPrompt({
|
||||
placeholder="Tool Variables"
|
||||
size="w-[140px] sm:w-[171px]"
|
||||
rounded="3xl"
|
||||
border="border"
|
||||
contentSize="text-[12px] sm:text-[14px]"
|
||||
/>
|
||||
</div>
|
||||
@@ -596,7 +592,7 @@ function EditPrompt({
|
||||
<img
|
||||
src={BookIcon}
|
||||
alt=""
|
||||
className="flex h-4 w-3 flex-shrink-0 items-center justify-center"
|
||||
className="flex h-4 w-3 shrink-0 items-center justify-center"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<span className="text-[14px] font-bold">
|
||||
@@ -763,7 +759,7 @@ export default function PromptsModal({
|
||||
setNewPromptContent('');
|
||||
}
|
||||
}}
|
||||
className="mx-4 mt-16 w-[95vw] max-w-[650px] rounded-2xl bg-white px-4 py-4 sm:px-6 sm:py-6 md:max-w-[860px] md:px-8 md:py-6 lg:max-w-[980px] dark:bg-[#1E1E2A]"
|
||||
className="bg-card dark:bg-card mx-4 mt-16 w-[95vw] max-w-[650px] rounded-2xl px-4 py-4 sm:px-6 sm:py-6 md:max-w-[860px] md:px-8 md:py-6 lg:max-w-[980px]"
|
||||
contentClassName="!overflow-visible"
|
||||
>
|
||||
{view}
|
||||
|
||||
@@ -174,12 +174,15 @@ export default function Analytics({ agentId }: AnalyticsProps) {
|
||||
fetchFeedbackData(id, filter?.value);
|
||||
}, [agentId, feedbackFilter]);
|
||||
return (
|
||||
<div className="mt-12">
|
||||
<div className="mt-8">
|
||||
<p className="text-muted-foreground mb-5 text-[15px] leading-6">
|
||||
{t('settings.analytics.subtitle')}
|
||||
</p>
|
||||
{/* Messages Analytics */}
|
||||
<div className="mt-8 flex w-full flex-col gap-3 [@media(min-width:1080px)]:flex-row">
|
||||
<div className="border-silver dark:border-silver/40 h-[345px] w-full overflow-hidden rounded-2xl border px-6 py-5 [@media(min-width:1080px)]:w-1/2">
|
||||
<div className="mt-4 flex w-full flex-col gap-3 [@media(min-width:1080px)]:flex-row">
|
||||
<div className="border-border dark:border-border h-[345px] w-full overflow-hidden rounded-2xl border px-6 py-5 [@media(min-width:1080px)]:w-1/2">
|
||||
<div className="flex flex-row items-center justify-start gap-3">
|
||||
<p className="text-jet dark:text-bright-gray font-bold">
|
||||
<p className="text-foreground dark:text-foreground font-bold">
|
||||
{t('settings.analytics.messages')}
|
||||
</p>
|
||||
<Dropdown
|
||||
@@ -191,7 +194,6 @@ export default function Analytics({ agentId }: AnalyticsProps) {
|
||||
}}
|
||||
selectedValue={messagesFilter ?? null}
|
||||
rounded="3xl"
|
||||
border="border"
|
||||
contentSize="text-sm"
|
||||
/>
|
||||
</div>
|
||||
@@ -225,9 +227,9 @@ export default function Analytics({ agentId }: AnalyticsProps) {
|
||||
</div>
|
||||
|
||||
{/* Token Usage Analytics */}
|
||||
<div className="border-silver dark:border-silver/40 h-[345px] w-full overflow-hidden rounded-2xl border px-6 py-5 [@media(min-width:1080px)]:w-1/2">
|
||||
<div className="border-border dark:border-border h-[345px] w-full overflow-hidden rounded-2xl border px-6 py-5 [@media(min-width:1080px)]:w-1/2">
|
||||
<div className="flex flex-row items-center justify-start gap-3">
|
||||
<p className="text-jet dark:text-bright-gray font-bold">
|
||||
<p className="text-foreground dark:text-foreground font-bold">
|
||||
{t('settings.analytics.tokenUsage')}
|
||||
</p>
|
||||
<Dropdown
|
||||
@@ -239,7 +241,6 @@ export default function Analytics({ agentId }: AnalyticsProps) {
|
||||
}}
|
||||
selectedValue={tokenUsageFilter ?? null}
|
||||
rounded="3xl"
|
||||
border="border"
|
||||
contentSize="text-sm"
|
||||
/>
|
||||
</div>
|
||||
@@ -275,9 +276,9 @@ export default function Analytics({ agentId }: AnalyticsProps) {
|
||||
|
||||
{/* Feedback Analytics */}
|
||||
<div className="mt-8 flex w-full flex-col gap-3">
|
||||
<div className="border-silver dark:border-silver/40 h-[345px] w-full overflow-hidden rounded-2xl border px-6 py-5">
|
||||
<div className="border-border dark:border-border h-[345px] w-full overflow-hidden rounded-2xl border px-6 py-5">
|
||||
<div className="flex flex-row items-center justify-start gap-3">
|
||||
<p className="text-jet dark:text-bright-gray font-bold">
|
||||
<p className="text-foreground dark:text-foreground font-bold">
|
||||
{t('settings.analytics.userFeedback')}
|
||||
</p>
|
||||
<Dropdown
|
||||
@@ -289,7 +290,6 @@ export default function Analytics({ agentId }: AnalyticsProps) {
|
||||
}}
|
||||
selectedValue={feedbackFilter ?? null}
|
||||
rounded="3xl"
|
||||
border="border"
|
||||
contentSize="text-sm"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -55,8 +55,7 @@ export default function General() {
|
||||
changeLanguage(selectedLanguage?.value);
|
||||
}, [selectedLanguage, changeLanguage]);
|
||||
return (
|
||||
<div className="mt-12 flex flex-col gap-4">
|
||||
{' '}
|
||||
<div className="mt-8 flex flex-col gap-6">
|
||||
<div className="flex flex-col gap-4">
|
||||
<Prompts
|
||||
prompts={prompts}
|
||||
@@ -65,11 +64,11 @@ export default function General() {
|
||||
dispatch(setPrompt({ name: name, id: id, type: type }))
|
||||
}
|
||||
setPrompts={(newPrompts) => dispatch(setPrompts(newPrompts))}
|
||||
dropdownProps={{ size: 'w-56', rounded: '3xl', border: 'border' }}
|
||||
dropdownProps={{ size: 'w-56', rounded: '3xl' }}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-col gap-4">
|
||||
<label className="text-jet dark:text-bright-gray text-base font-medium">
|
||||
<label className="text-foreground dark:text-foreground text-base font-medium">
|
||||
{t('settings.general.chunks')}
|
||||
</label>
|
||||
<Dropdown
|
||||
@@ -78,12 +77,10 @@ export default function General() {
|
||||
onSelect={(value: string) => dispatch(setChunks(value))}
|
||||
size="w-56"
|
||||
rounded="3xl"
|
||||
border="border"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-col gap-4">
|
||||
{' '}
|
||||
<label className="text-jet dark:text-bright-gray text-base font-medium">
|
||||
<label className="text-foreground dark:text-foreground text-base font-medium">
|
||||
{t('settings.general.selectTheme')}
|
||||
</label>
|
||||
<Dropdown
|
||||
@@ -97,11 +94,10 @@ export default function General() {
|
||||
}}
|
||||
size="w-56"
|
||||
rounded="3xl"
|
||||
border="border"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-col gap-4">
|
||||
<label className="text-jet dark:text-bright-gray text-base font-medium">
|
||||
<label className="text-foreground dark:text-foreground text-base font-medium">
|
||||
{t('settings.general.selectLanguage')}
|
||||
</label>
|
||||
<Dropdown
|
||||
@@ -115,14 +111,13 @@ export default function General() {
|
||||
}}
|
||||
size="w-56"
|
||||
rounded="3xl"
|
||||
border="border"
|
||||
/>
|
||||
</div>
|
||||
<hr className="border-silver dark:border-silver/40 my-4 w-[calc(min(665px,100%))] border-t" />
|
||||
<hr className="border-border dark:border-border my-4 w-[calc(min(665px,100%))] border-t" />
|
||||
<div className="flex flex-col gap-2">
|
||||
<button
|
||||
title={t('settings.general.deleteAllLabel')}
|
||||
className="border-rosso-corsa text-rosso-corsa hover:bg-rosso-corsa flex w-fit cursor-pointer items-center justify-between rounded-3xl border border-solid bg-transparent px-5 py-3 text-sm font-medium tracking-[0.015em] transition-colors hover:font-bold hover:tracking-normal hover:text-white"
|
||||
className="border-destructive text-destructive hover:bg-destructive flex w-fit cursor-pointer items-center justify-between rounded-3xl border border-solid bg-transparent px-5 py-3 text-sm font-medium tracking-[0.015em] transition-colors hover:font-bold hover:tracking-normal hover:text-white"
|
||||
onClick={() => dispatch(setModalStateDeleteConv('ACTIVE'))}
|
||||
>
|
||||
{t('settings.general.deleteAllBtn')}
|
||||
|
||||
@@ -16,6 +16,7 @@ type LogsProps = {
|
||||
};
|
||||
|
||||
export default function Logs({ agentId, tableHeader }: LogsProps) {
|
||||
const { t } = useTranslation();
|
||||
const token = useSelector(selectToken);
|
||||
const [logsByPage, setLogsByPage] = useState<Record<number, LogData[]>>({});
|
||||
const [page, setPage] = useState(1);
|
||||
@@ -56,8 +57,11 @@ export default function Logs({ agentId, tableHeader }: LogsProps) {
|
||||
if (hasMore) fetchLogs();
|
||||
}, [page, agentId]);
|
||||
return (
|
||||
<div className="mt-12">
|
||||
<div className="mt-8">
|
||||
<div className="mt-8">
|
||||
<p className="text-muted-foreground mb-5 text-[15px] leading-6">
|
||||
{t('settings.logs.subtitle')}
|
||||
</p>
|
||||
<div>
|
||||
<LogsTable
|
||||
logs={logs}
|
||||
setPage={setPage}
|
||||
@@ -113,9 +117,9 @@ function LogsTable({ logs, setPage, loading, tableHeader }: LogsTableProps) {
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="logs-table border-light-silver h-[55vh] w-full overflow-hidden rounded-xl border bg-white dark:border-transparent dark:bg-black">
|
||||
<div className="dark:bg-eerie-black-2 flex h-8 flex-col items-start justify-center bg-black/10">
|
||||
<p className="dark:text-gray-6000 px-3 text-xs">
|
||||
<div className="logs-table border-border bg-card h-[55vh] w-full overflow-hidden rounded-xl border dark:bg-black">
|
||||
<div className="flex h-8 flex-col items-start justify-center bg-black/10 dark:bg-white/5">
|
||||
<p className="text-muted-foreground px-3 text-xs">
|
||||
{tableHeader ? tableHeader : t('settings.logs.tableHeader')}
|
||||
</p>
|
||||
</div>
|
||||
@@ -164,11 +168,11 @@ function Log({
|
||||
const { id, action, timestamp, ...filteredLog } = log;
|
||||
|
||||
return (
|
||||
<div className="group dark:hover:bg-dark-charcoal w-full rounded-xl bg-transparent hover:bg-[#F9F9F9]">
|
||||
<div className="group dark:hover:bg-accent hover:bg-muted w-full rounded-xl bg-transparent">
|
||||
<div
|
||||
onClick={() => onToggle(log.id)}
|
||||
className={`flex cursor-pointer flex-row items-start gap-2 p-2 px-4 py-3 text-gray-900 ${
|
||||
isOpen ? 'rounded-t-xl bg-[#F1F1F1] dark:bg-[#1B1B1B]' : ''
|
||||
className={`text-foreground flex cursor-pointer flex-row items-start gap-2 p-2 px-4 py-3 ${
|
||||
isOpen ? 'dark:bg-background rounded-t-xl bg-[#F1F1F1]' : ''
|
||||
}`}
|
||||
>
|
||||
<img
|
||||
@@ -177,10 +181,10 @@ function Log({
|
||||
className={`mt-[3px] h-3 w-3 transition duration-300 ${isOpen ? 'rotate-90' : ''}`}
|
||||
/>
|
||||
<span className="flex flex-row gap-2">
|
||||
<h2 className="dark:text-bright-gray text-xs text-black/60">{`${log.timestamp}`}</h2>
|
||||
<h2 className="text-xs text-[#913400] dark:text-[#DF5200]">{`[${log.action}]`}</h2>
|
||||
<h2 className="dark:text-foreground text-xs text-black/60">{`${log.timestamp}`}</h2>
|
||||
<h2 className="text-xs text-[#913400] dark:text-orange-500">{`[${log.action}]`}</h2>
|
||||
<h2
|
||||
className={`max-w-72 text-xs ${logLevelColor[log.level]} break-words`}
|
||||
className={`max-w-72 text-xs ${logLevelColor[log.level]} wrap-break-word`}
|
||||
>
|
||||
{`${log.question}`.length > 250
|
||||
? `${log.question.substring(0, 250)}...`
|
||||
@@ -189,9 +193,9 @@ function Log({
|
||||
</span>
|
||||
</div>
|
||||
{isOpen && (
|
||||
<div className="rounded-b-xl bg-[#F1F1F1] px-4 py-3 dark:bg-[#1B1B1B]">
|
||||
<div className="dark:bg-background rounded-b-xl bg-[#F1F1F1] px-4 py-3">
|
||||
<div className="scrollbar-overlay overflow-y-auto">
|
||||
<pre className="px-2 font-mono text-xs leading-relaxed break-words whitespace-pre-wrap text-gray-700 dark:text-gray-400">
|
||||
<pre className="px-2 font-mono text-xs leading-relaxed wrap-break-word whitespace-pre-wrap text-gray-700 dark:text-gray-400">
|
||||
{JSON.stringify(filteredLog, null, 2)}
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
@@ -3,8 +3,7 @@ import { useTranslation } from 'react-i18next';
|
||||
import { useSelector } from 'react-redux';
|
||||
|
||||
import userService from '../api/services/userService';
|
||||
import SearchableDropdown from '../components/SearchableDropdown';
|
||||
import { DropdownProps } from '../components/types/Dropdown.types';
|
||||
import Dropdown, { DropdownProps } from '../components/Dropdown';
|
||||
import ConfirmationModal from '../modals/ConfirmationModal';
|
||||
import { ActiveState, PromptProps } from '../models/misc';
|
||||
import { selectToken } from '../preferences/preferenceSlice';
|
||||
@@ -23,7 +22,7 @@ export default function Prompts({
|
||||
onSelectPrompt,
|
||||
setPrompts,
|
||||
title,
|
||||
titleClassName = 'dark:text-bright-gray font-medium',
|
||||
titleClassName = 'dark:text-foreground font-medium',
|
||||
dropdownProps = {},
|
||||
showAddButton = true,
|
||||
}: ExtendedPromptProps) {
|
||||
@@ -186,8 +185,9 @@ export default function Prompts({
|
||||
<p className={titleClassName}>
|
||||
{title ? title : t('settings.general.prompt')}
|
||||
</p>
|
||||
<div className="flex flex-row flex-wrap items-baseline justify-start gap-6">
|
||||
<SearchableDropdown
|
||||
<div className="flex flex-row flex-wrap items-end justify-start gap-6">
|
||||
<Dropdown
|
||||
searchable
|
||||
options={prompts.map((prompt: any) =>
|
||||
typeof prompt === 'string'
|
||||
? { name: prompt, id: prompt, type: '' }
|
||||
@@ -218,7 +218,7 @@ export default function Prompts({
|
||||
/>
|
||||
{showAddButton && (
|
||||
<button
|
||||
className="border-violets-are-blue text-violets-are-blue hover:bg-violets-are-blue h-10 w-20 rounded-3xl border border-solid text-sm transition-colors hover:text-white"
|
||||
className="border-primary text-primary hover:bg-primary/90 w-20 rounded-3xl border border-solid py-3 text-sm transition-colors hover:text-white"
|
||||
onClick={() => {
|
||||
setModalType('ADD');
|
||||
setModalState('ACTIVE');
|
||||
|
||||
@@ -8,6 +8,7 @@ import EyeView from '../assets/eye-view.svg';
|
||||
import NoFilesIcon from '../assets/no-files.svg';
|
||||
import NoFilesDarkIcon from '../assets/no-files-dark.svg';
|
||||
import Trash from '../assets/red-trash.svg';
|
||||
import SearchIcon from '../assets/search.svg';
|
||||
import SyncIcon from '../assets/sync.svg';
|
||||
import ThreeDots from '../assets/three-dots.svg';
|
||||
import CalendarIcon from '../assets/calendar.svg';
|
||||
@@ -361,17 +362,20 @@ export default function Sources({
|
||||
) : (
|
||||
<div className="mt-8 flex w-full max-w-full flex-col overflow-hidden">
|
||||
<div className="relative flex grow flex-col">
|
||||
<div className="mb-6">
|
||||
<h2 className="text-sonic-silver text-base font-medium">
|
||||
{t('settings.sources.title')}
|
||||
</h2>
|
||||
</div>
|
||||
<p className="text-muted-foreground mb-5 text-[15px] leading-6">
|
||||
{t('settings.sources.subtitle')}
|
||||
</p>
|
||||
<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.sources.searchPlaceholder')}
|
||||
</label>
|
||||
<div className="relative w-[280px]">
|
||||
<div className="relative w-full max-w-md">
|
||||
<img
|
||||
src={SearchIcon}
|
||||
alt=""
|
||||
className="absolute top-1/2 left-4 h-5 w-5 -translate-y-1/2 opacity-40"
|
||||
/>
|
||||
<input
|
||||
maxLength={256}
|
||||
placeholder={t('settings.sources.searchPlaceholder')}
|
||||
@@ -383,12 +387,12 @@ export default function Sources({
|
||||
setSearchTerm(e.target.value);
|
||||
setCurrentPage(1);
|
||||
}}
|
||||
className="border-silver dark:border-silver/40 text-jet dark:text-bright-gray focus:border-silver dark:focus:border-silver/60 h-[32px] w-full rounded-full border bg-transparent px-3 text-sm outline-none placeholder:text-gray-400 dark:placeholder:text-gray-500"
|
||||
className="border-border bg-card text-foreground placeholder:text-muted-foreground h-11 w-full rounded-full border py-2 pr-5 pl-11 text-sm shadow-[0_1px_4px_rgba(0,0,0,0.06)] transition-shadow outline-none focus:shadow-[0_2px_8px_rgba(0,0,0,0.1)] dark:shadow-none"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
className="bg-purple-30 hover:bg-violets-are-blue flex h-[38px] min-w-[108px] items-center justify-center rounded-full px-4 text-[14px] whitespace-normal text-white"
|
||||
className="bg-primary hover:bg-primary/90 flex h-11 min-w-[108px] items-center justify-center rounded-full px-4 text-sm whitespace-normal text-white"
|
||||
title={t('settings.sources.addSource')}
|
||||
onClick={() => {
|
||||
setIsOnboarding(false);
|
||||
@@ -422,7 +426,7 @@ export default function Sources({
|
||||
return (
|
||||
<div key={docId} className="relative">
|
||||
<div
|
||||
className={`flex h-[130px] w-full flex-col rounded-2xl bg-[#F9F9F9] p-3 transition-all duration-200 dark:bg-[#383838] ${
|
||||
className={`bg-muted dark:bg-accent flex h-[130px] w-full flex-col rounded-2xl p-3 transition-all duration-200 ${
|
||||
activeMenuId === docId || syncMenuState.docId === docId
|
||||
? 'scale-[1.05]'
|
||||
: 'hover:scale-[1.05]'
|
||||
@@ -431,7 +435,7 @@ export default function Sources({
|
||||
<div className="w-full flex-1">
|
||||
<div className="flex w-full items-center justify-between gap-2">
|
||||
<h3
|
||||
className="font-inter dark:text-bright-gray line-clamp-3 text-[13px] leading-[18px] font-semibold break-words text-[#18181B]"
|
||||
className="font-inter dark:text-foreground text-foreground line-clamp-3 text-[13px] leading-[18px] font-semibold wrap-break-word"
|
||||
title={document.name}
|
||||
>
|
||||
{document.name}
|
||||
@@ -472,7 +476,7 @@ export default function Sources({
|
||||
e.stopPropagation();
|
||||
handleMenuClick(e, docId);
|
||||
}}
|
||||
className="inline-flex h-[35px] w-[24px] shrink-0 items-center justify-center rounded-md transition-colors hover:bg-[#EBEBEB] dark:hover:bg-[#26272E]"
|
||||
className="dark:hover:bg-muted inline-flex h-[35px] w-6 shrink-0 items-center justify-center rounded-md transition-colors hover:bg-[#EBEBEB]"
|
||||
aria-label={t('settings.sources.menuAlt')}
|
||||
data-testid={`menu-button-${docId}`}
|
||||
>
|
||||
@@ -491,19 +495,15 @@ export default function Sources({
|
||||
<img
|
||||
src={CalendarIcon}
|
||||
alt=""
|
||||
className="h-[14px] w-[14px]"
|
||||
className="h-3.5 w-3.5"
|
||||
/>
|
||||
<span className="font-inter text-[12px] leading-[18px] font-[500] text-[#848484] dark:text-[#848484]">
|
||||
<span className="font-inter text-muted-foreground text-[12px] leading-[18px] font-medium">
|
||||
{document.date ? formatDate(document.date) : ''}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<img
|
||||
src={DiscIcon}
|
||||
alt=""
|
||||
className="h-[14px] w-[14px]"
|
||||
/>
|
||||
<span className="font-inter text-[12px] leading-[18px] font-[500] text-[#848484] dark:text-[#848484]">
|
||||
<img src={DiscIcon} alt="" className="h-3.5 w-3.5" />
|
||||
<span className="font-inter text-muted-foreground text-[12px] leading-[18px] font-medium">
|
||||
{document.tokens
|
||||
? formatTokens(+document.tokens)
|
||||
: ''}
|
||||
|
||||
@@ -330,9 +330,9 @@ export default function ToolConfig({
|
||||
return (
|
||||
<div className="scrollbar-overlay mt-8 flex flex-col gap-4">
|
||||
<div className="mb-4 flex items-center justify-between">
|
||||
<div className="text-eerie-black dark:text-bright-gray flex items-center gap-3 text-sm">
|
||||
<div className="text-foreground dark:text-foreground flex items-center gap-3 text-sm">
|
||||
<button
|
||||
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]"
|
||||
className="border-border text-muted-foreground hover:bg-accent rounded-full border p-3 text-sm"
|
||||
onClick={handleBackClick}
|
||||
>
|
||||
<img src={ArrowLeft} alt="left-arrow" className="h-3 w-3" />
|
||||
@@ -340,7 +340,7 @@ export default function ToolConfig({
|
||||
<p className="mt-px">{t('settings.tools.backToAllTools')}</p>
|
||||
</div>
|
||||
<button
|
||||
className="bg-purple-30 hover:bg-violets-are-blue rounded-full px-3 py-2 text-xs text-nowrap text-white transition-colors disabled:cursor-not-allowed disabled:opacity-50 sm:px-4 sm:py-2"
|
||||
className="bg-primary hover:bg-primary/90 rounded-full px-3 py-2 text-xs text-nowrap text-white transition-colors disabled:cursor-not-allowed disabled:opacity-50 sm:px-4 sm:py-2"
|
||||
onClick={handleSaveChanges}
|
||||
disabled={!hasUnsavedChanges || saving}
|
||||
>
|
||||
@@ -353,7 +353,7 @@ export default function ToolConfig({
|
||||
</div>
|
||||
)}
|
||||
<div className="mt-1">
|
||||
<p className="text-eerie-black dark:text-bright-gray text-sm font-semibold">
|
||||
<p className="text-foreground dark:text-foreground text-sm font-semibold">
|
||||
{t('settings.tools.customName')}
|
||||
</p>
|
||||
<div className="relative mt-4 w-full max-w-96">
|
||||
@@ -370,7 +370,7 @@ export default function ToolConfig({
|
||||
{tool.name !== 'api_tool' &&
|
||||
Object.keys(configRequirements).length > 0 && (
|
||||
<div>
|
||||
<p className="text-eerie-black dark:text-bright-gray mb-4 text-sm font-semibold">
|
||||
<p className="text-foreground dark:text-foreground mb-4 text-sm font-semibold">
|
||||
{t('settings.tools.authentication')}
|
||||
</p>
|
||||
<div className="max-w-96">
|
||||
@@ -391,20 +391,20 @@ export default function ToolConfig({
|
||||
<div className="flex flex-col gap-4">
|
||||
<div className="mx-0 my-2 h-[0.8px] w-full rounded-full bg-[#C4C4C4]/40"></div>
|
||||
<div className="flex w-full flex-row items-center justify-between gap-2">
|
||||
<p className="text-eerie-black dark:text-bright-gray text-base font-semibold">
|
||||
<p className="text-foreground dark:text-foreground text-base font-semibold">
|
||||
{t('settings.tools.actions')}
|
||||
</p>
|
||||
{tool.name === 'api_tool' && (
|
||||
<div className="flex gap-2">
|
||||
<button
|
||||
onClick={() => setImportModalState('ACTIVE')}
|
||||
className="border-violets-are-blue text-violets-are-blue hover:bg-violets-are-blue rounded-full border border-solid px-5 py-1 text-sm transition-colors hover:text-white"
|
||||
className="border-primary text-primary hover:bg-primary/90 rounded-full border border-solid px-5 py-1 text-sm transition-colors hover:text-white"
|
||||
>
|
||||
{t('settings.tools.importSpec')}
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setActionModalState('ACTIVE')}
|
||||
className="border-violets-are-blue text-violets-are-blue hover:bg-violets-are-blue rounded-full border border-solid px-5 py-1 text-sm transition-colors hover:text-white"
|
||||
className="border-primary text-primary hover:bg-primary/90 rounded-full border border-solid px-5 py-1 text-sm transition-colors hover:text-white"
|
||||
>
|
||||
{t('settings.tools.addAction')}
|
||||
</button>
|
||||
@@ -434,26 +434,24 @@ export default function ToolConfig({
|
||||
{'actions' in tool && tool.actions && tool.actions.length > 0 ? (
|
||||
<>
|
||||
<div className="relative">
|
||||
<svg
|
||||
className="text-muted-foreground absolute top-1/2 left-3 h-4 w-4 -translate-y-1/2"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
strokeWidth={2}
|
||||
stroke="currentColor"
|
||||
>
|
||||
<circle cx="11" cy="11" r="8" />
|
||||
<path strokeLinecap="round" d="m21 21-4.35-4.35" />
|
||||
</svg>
|
||||
<input
|
||||
type="text"
|
||||
value={userActionsSearch}
|
||||
onChange={(e) => setUserActionsSearch(e.target.value)}
|
||||
placeholder={t('settings.tools.searchActions')}
|
||||
className="border-silver dark:border-silver/40 dark:bg-raisin-black w-full rounded-full border px-4 py-2 pl-10 text-sm outline-none focus:border-purple-500 dark:text-white dark:placeholder-gray-500"
|
||||
className="border-border text-foreground placeholder:text-muted-foreground h-10 w-full rounded-full border bg-transparent pr-4 pl-10 text-sm outline-none"
|
||||
/>
|
||||
<svg
|
||||
className="absolute top-1/2 left-3 h-4 w-4 -translate-y-1/2 text-gray-400"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
{filteredUserActions.length === 0 && userActionsSearch && (
|
||||
@@ -467,10 +465,10 @@ export default function ToolConfig({
|
||||
return (
|
||||
<div
|
||||
key={originalIndex}
|
||||
className="border-silver dark:border-silver/40 w-full rounded-xl border"
|
||||
className="border-border dark:border-border w-full rounded-xl border"
|
||||
>
|
||||
<div
|
||||
className={`border-silver dark:border-silver/40 flex cursor-pointer flex-wrap items-center justify-between ${isExpanded ? 'rounded-t-xl border-b' : 'rounded-xl'} bg-[#F9F9F9] px-4 py-3 dark:bg-[#28292D]`}
|
||||
className={`border-border dark:border-border flex cursor-pointer flex-wrap items-center justify-between ${isExpanded ? 'rounded-t-xl border-b' : 'rounded-xl'} bg-muted px-4 py-3`}
|
||||
onClick={() => toggleUserActionExpand(originalIndex)}
|
||||
>
|
||||
<div className="flex items-center gap-3">
|
||||
@@ -479,7 +477,7 @@ export default function ToolConfig({
|
||||
alt="expand"
|
||||
className={`h-4 w-4 opacity-60 transition-transform duration-200 ${isExpanded ? 'rotate-90' : ''}`}
|
||||
/>
|
||||
<p className="text-eerie-black dark:text-bright-gray font-semibold">
|
||||
<p className="text-foreground dark:text-foreground font-semibold">
|
||||
{action.name}
|
||||
</p>
|
||||
{action.description && (
|
||||
@@ -588,7 +586,7 @@ export default function ToolConfig({
|
||||
<input
|
||||
key={uniqueKey}
|
||||
value={param[1].description}
|
||||
className="border-silver dark:border-silver/40 rounded-lg border bg-transparent px-2 py-1 text-sm outline-hidden"
|
||||
className="border-border dark:border-border rounded-lg border bg-transparent px-2 py-1 text-sm outline-hidden"
|
||||
onChange={(e) => {
|
||||
setTool({
|
||||
...tool,
|
||||
@@ -626,7 +624,7 @@ export default function ToolConfig({
|
||||
value={param[1].value}
|
||||
key={uniqueKey}
|
||||
disabled={param[1].filled_by_llm}
|
||||
className={`border-silver dark:border-silver/40 rounded-lg border bg-transparent px-2 py-1 text-sm outline-hidden ${param[1].filled_by_llm ? 'opacity-50' : ''}`}
|
||||
className={`border-border dark:border-border rounded-lg border bg-transparent px-2 py-1 text-sm outline-hidden ${param[1].filled_by_llm ? 'opacity-50' : ''}`}
|
||||
onChange={(e) => {
|
||||
setTool({
|
||||
...tool,
|
||||
@@ -855,26 +853,24 @@ function APIToolConfig({
|
||||
return (
|
||||
<div className="scrollbar-overlay flex flex-col gap-4">
|
||||
<div className="relative">
|
||||
<svg
|
||||
className="text-muted-foreground absolute top-1/2 left-3 h-4 w-4 -translate-y-1/2"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
strokeWidth={2}
|
||||
stroke="currentColor"
|
||||
>
|
||||
<circle cx="11" cy="11" r="8" />
|
||||
<path strokeLinecap="round" d="m21 21-4.35-4.35" />
|
||||
</svg>
|
||||
<input
|
||||
type="text"
|
||||
value={searchQuery}
|
||||
onChange={(e) => setSearchQuery(e.target.value)}
|
||||
placeholder={t('settings.tools.searchActions')}
|
||||
className="border-silver dark:border-silver/40 dark:bg-raisin-black w-full rounded-full border px-4 py-2 pl-10 text-sm outline-none focus:border-purple-500 dark:text-white dark:placeholder-gray-500"
|
||||
className="border-border text-foreground placeholder:text-muted-foreground h-10 w-full rounded-full border bg-transparent pr-4 pl-10 text-sm outline-none"
|
||||
/>
|
||||
<svg
|
||||
className="absolute top-1/2 left-3 h-4 w-4 -translate-y-1/2 text-gray-400"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
{filteredActions.length === 0 && searchQuery && (
|
||||
@@ -889,10 +885,10 @@ function APIToolConfig({
|
||||
return (
|
||||
<div
|
||||
key={actionIndex}
|
||||
className="border-silver dark:border-silver/40 w-full rounded-xl border"
|
||||
className="border-border dark:border-border w-full rounded-xl border"
|
||||
>
|
||||
<div
|
||||
className={`border-silver dark:border-silver/40 flex cursor-pointer flex-wrap items-center justify-between ${isExpanded ? 'rounded-t-xl border-b' : 'rounded-xl'} bg-[#F9F9F9] px-4 py-3 dark:bg-[#28292D]`}
|
||||
className={`border-border dark:border-border flex cursor-pointer flex-wrap items-center justify-between ${isExpanded ? 'rounded-t-xl border-b' : 'rounded-xl'} bg-muted px-4 py-3`}
|
||||
onClick={() => toggleActionExpand(actionName)}
|
||||
>
|
||||
<div className="flex items-center gap-3">
|
||||
@@ -906,7 +902,7 @@ function APIToolConfig({
|
||||
>
|
||||
{action.method}
|
||||
</span>
|
||||
<p className="text-eerie-black dark:text-bright-gray font-semibold">
|
||||
<p className="text-foreground dark:text-foreground font-semibold">
|
||||
{action.name}
|
||||
</p>
|
||||
{action.description && (
|
||||
@@ -969,7 +965,7 @@ function APIToolConfig({
|
||||
</div>
|
||||
<div className="mt-4 px-5 py-2">
|
||||
<div className="relative w-full">
|
||||
<span className="text-gray-4000 dark:bg-raisin-black dark:text-silver absolute -top-2 left-5 z-10 bg-white px-2 text-xs">
|
||||
<span className="text-muted-foreground bg-card absolute -top-2 left-5 z-10 px-2 text-xs">
|
||||
{t('settings.tools.method')}
|
||||
</span>
|
||||
<Dropdown
|
||||
@@ -1011,7 +1007,6 @@ function APIToolConfig({
|
||||
}}
|
||||
size="w-56"
|
||||
rounded="3xl"
|
||||
border="border"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1049,7 +1044,7 @@ function APIToolConfig({
|
||||
action.method === 'OPTIONS') && (
|
||||
<div className="mt-4 px-5 py-2">
|
||||
<div className="relative w-full">
|
||||
<span className="text-gray-4000 dark:bg-raisin-black dark:text-silver absolute -top-2 left-5 z-10 bg-white px-2 text-xs">
|
||||
<span className="text-muted-foreground bg-card absolute -top-2 left-5 z-10 px-2 text-xs">
|
||||
{t('settings.tools.bodyContentType')}
|
||||
</span>
|
||||
<Dropdown
|
||||
@@ -1091,10 +1086,9 @@ function APIToolConfig({
|
||||
}}
|
||||
size="w-56"
|
||||
rounded="3xl"
|
||||
border="border"
|
||||
/>
|
||||
</div>
|
||||
<p className="text-eerie-black dark:text-bright-gray mt-2 text-xs opacity-60">
|
||||
<p className="text-foreground dark:text-foreground mt-2 text-xs opacity-60">
|
||||
{action.body_content_type === 'multipart/form-data' &&
|
||||
'For APIs requiring multipart format. File uploads not supported through LLM.'}
|
||||
{action.body_content_type ===
|
||||
@@ -1346,7 +1340,7 @@ function APIActionTable({
|
||||
<div className="flex flex-row items-center justify-between gap-2">
|
||||
<input
|
||||
value={newPropertyKey}
|
||||
className="border-silver dark:border-silver/40 flex w-full min-w-[130.5px] items-start rounded-lg border bg-transparent px-2 py-1 text-sm outline-hidden"
|
||||
className="border-border dark:border-border flex w-full min-w-[130.5px] items-start rounded-lg border bg-transparent px-2 py-1 text-sm outline-hidden"
|
||||
onChange={(e) => setNewPropertyKey(e.target.value)}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter') {
|
||||
@@ -1376,7 +1370,7 @@ function APIActionTable({
|
||||
) : (
|
||||
<input
|
||||
value={key}
|
||||
className="border-silver dark:border-silver/40 flex w-full min-w-[175.5px] items-start rounded-lg border bg-transparent px-2 py-1 text-sm outline-hidden"
|
||||
className="border-border dark:border-border flex w-full min-w-[175.5px] items-start rounded-lg border bg-transparent px-2 py-1 text-sm outline-hidden"
|
||||
onFocus={() => handleRenamePropertyStart(section, key)}
|
||||
readOnly
|
||||
/>
|
||||
@@ -1392,7 +1386,7 @@ function APIActionTable({
|
||||
e.target.value as 'string' | 'integer',
|
||||
)
|
||||
}
|
||||
className="border-silver dark:border-silver/40 rounded-lg border bg-transparent px-2 py-1 text-sm outline-hidden"
|
||||
className="border-border dark:border-border rounded-lg border bg-transparent px-2 py-1 text-sm outline-hidden"
|
||||
>
|
||||
<option value="string">string</option>
|
||||
<option value="integer">integer</option>
|
||||
@@ -1420,7 +1414,7 @@ function APIActionTable({
|
||||
<td className="w-10">
|
||||
<input
|
||||
value={param.description}
|
||||
className="border-silver dark:border-silver/40 rounded-lg border bg-transparent px-2 py-1 text-sm outline-hidden"
|
||||
className="border-border dark:border-border rounded-lg border bg-transparent px-2 py-1 text-sm outline-hidden"
|
||||
onChange={(e) =>
|
||||
handlePropertyChange(
|
||||
section,
|
||||
@@ -1438,7 +1432,7 @@ function APIActionTable({
|
||||
onChange={(e) =>
|
||||
handlePropertyChange(section, key, 'value', e.target.value)
|
||||
}
|
||||
className={`border-silver dark:border-silver/40 rounded-lg border bg-transparent px-2 py-1 text-sm outline-hidden ${param.filled_by_llm ? 'opacity-50' : ''}`}
|
||||
className={`border-border dark:border-border rounded-lg border bg-transparent px-2 py-1 text-sm outline-hidden ${param.filled_by_llm ? 'opacity-50' : ''}`}
|
||||
></input>
|
||||
</td>
|
||||
<td
|
||||
@@ -1448,7 +1442,7 @@ function APIActionTable({
|
||||
maxWidth: '50px',
|
||||
padding: '0',
|
||||
}}
|
||||
className="border-silver dark:border-silver/40 border-b"
|
||||
className="border-border dark:border-border border-b"
|
||||
>
|
||||
<button
|
||||
onClick={() => handlePorpertyDelete(section, key)}
|
||||
@@ -1472,7 +1466,7 @@ function APIActionTable({
|
||||
}
|
||||
}}
|
||||
placeholder={t('settings.tools.propertyName')}
|
||||
className="border-silver dark:border-silver/40 flex w-full min-w-[130.5px] items-start rounded-lg border bg-transparent px-2 py-1 text-sm outline-hidden"
|
||||
className="border-border dark:border-border flex w-full min-w-[130.5px] items-start rounded-lg border bg-transparent px-2 py-1 text-sm outline-hidden"
|
||||
/>
|
||||
</td>
|
||||
<td>
|
||||
@@ -1481,7 +1475,7 @@ function APIActionTable({
|
||||
onChange={(e) =>
|
||||
setNewPropertyType(e.target.value as 'string' | 'integer')
|
||||
}
|
||||
className="border-silver dark:border-silver/40 rounded-lg border bg-transparent px-2 py-1 text-sm outline-hidden"
|
||||
className="border-border dark:border-border rounded-lg border bg-transparent px-2 py-1 text-sm outline-hidden"
|
||||
>
|
||||
<option value="string">string</option>
|
||||
<option value="integer">integer</option>
|
||||
@@ -1490,7 +1484,7 @@ function APIActionTable({
|
||||
<td colSpan={3} className="text-right">
|
||||
<button
|
||||
onClick={handleAddProperty}
|
||||
className="bg-purple-30 hover:bg-violets-are-blue mr-1 rounded-full px-5 py-1 text-sm text-white"
|
||||
className="bg-primary hover:bg-primary/90 mr-1 rounded-full px-5 py-1 text-sm text-white"
|
||||
>
|
||||
{t('settings.tools.add')}
|
||||
</button>
|
||||
@@ -1515,7 +1509,7 @@ function APIActionTable({
|
||||
<td colSpan={5}>
|
||||
<button
|
||||
onClick={() => handleAddPropertyStart(section)}
|
||||
className="border-violets-are-blue text-violets-are-blue hover:bg-violets-are-blue flex items-start rounded-full border border-solid px-5 py-1 text-sm text-nowrap transition-colors hover:text-white"
|
||||
className="border-primary text-primary hover:bg-primary/90 flex items-start rounded-full border border-solid px-5 py-1 text-sm text-nowrap transition-colors hover:text-white"
|
||||
>
|
||||
{t('settings.tools.addNew')}
|
||||
</button>
|
||||
@@ -1546,7 +1540,7 @@ function APIActionTable({
|
||||
<div className="flex flex-row items-center justify-between gap-2">
|
||||
<input
|
||||
value={newPropertyKey}
|
||||
className="border-silver dark:border-silver/40 flex w-full min-w-[130.5px] items-start rounded-lg border bg-transparent px-2 py-1 text-sm outline-hidden"
|
||||
className="border-border dark:border-border flex w-full min-w-[130.5px] items-start rounded-lg border bg-transparent px-2 py-1 text-sm outline-hidden"
|
||||
onChange={(e) => setNewPropertyKey(e.target.value)}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter') {
|
||||
@@ -1576,7 +1570,7 @@ function APIActionTable({
|
||||
) : (
|
||||
<input
|
||||
value={key}
|
||||
className="border-silver dark:border-silver/40 flex w-full min-w-[175.5px] items-start rounded-lg border bg-transparent px-2 py-1 text-sm outline-hidden"
|
||||
className="border-border dark:border-border flex w-full min-w-[175.5px] items-start rounded-lg border bg-transparent px-2 py-1 text-sm outline-hidden"
|
||||
onFocus={() => handleRenamePropertyStart('headers', key)}
|
||||
readOnly
|
||||
/>
|
||||
@@ -1594,13 +1588,13 @@ function APIActionTable({
|
||||
)
|
||||
}
|
||||
placeholder="e.g., application/json"
|
||||
className="border-silver dark:border-silver/40 w-full rounded-lg border bg-transparent px-2 py-1 text-sm outline-hidden"
|
||||
className="border-border dark:border-border w-full rounded-lg border bg-transparent px-2 py-1 text-sm outline-hidden"
|
||||
/>
|
||||
</td>
|
||||
<td>
|
||||
<input
|
||||
value={param.description}
|
||||
className="border-silver dark:border-silver/40 rounded-lg border bg-transparent px-2 py-1 text-sm outline-hidden"
|
||||
className="border-border dark:border-border rounded-lg border bg-transparent px-2 py-1 text-sm outline-hidden"
|
||||
onChange={(e) =>
|
||||
handlePropertyChange(
|
||||
'headers',
|
||||
@@ -1618,7 +1612,7 @@ function APIActionTable({
|
||||
maxWidth: '50px',
|
||||
padding: '0',
|
||||
}}
|
||||
className="border-silver dark:border-silver/40 border-b"
|
||||
className="border-border dark:border-border border-b"
|
||||
>
|
||||
<button
|
||||
onClick={() => handlePorpertyDelete('headers', key)}
|
||||
@@ -1642,13 +1636,13 @@ function APIActionTable({
|
||||
}
|
||||
}}
|
||||
placeholder={t('settings.tools.propertyName')}
|
||||
className="border-silver dark:border-silver/40 flex w-full min-w-[130.5px] items-start rounded-lg border bg-transparent px-2 py-1 text-sm outline-hidden"
|
||||
className="border-border dark:border-border flex w-full min-w-[130.5px] items-start rounded-lg border bg-transparent px-2 py-1 text-sm outline-hidden"
|
||||
/>
|
||||
</td>
|
||||
<td colSpan={2} className="text-right">
|
||||
<button
|
||||
onClick={handleAddProperty}
|
||||
className="bg-purple-30 hover:bg-violets-are-blue mr-1 rounded-full px-5 py-1 text-sm text-white"
|
||||
className="bg-primary hover:bg-primary/90 mr-1 rounded-full px-5 py-1 text-sm text-white"
|
||||
>
|
||||
{t('settings.tools.add')}
|
||||
</button>
|
||||
@@ -1673,7 +1667,7 @@ function APIActionTable({
|
||||
<td colSpan={3}>
|
||||
<button
|
||||
onClick={() => handleAddPropertyStart('headers')}
|
||||
className="border-violets-are-blue text-violets-are-blue hover:bg-violets-are-blue flex items-start rounded-full border border-solid px-5 py-1 text-sm text-nowrap transition-colors hover:text-white"
|
||||
className="border-primary text-primary hover:bg-primary/90 flex items-start rounded-full border border-solid px-5 py-1 text-sm text-nowrap transition-colors hover:text-white"
|
||||
>
|
||||
{t('settings.tools.addNew')}
|
||||
</button>
|
||||
@@ -1695,19 +1689,19 @@ function APIActionTable({
|
||||
return (
|
||||
<div className="scrollbar-overlay flex flex-col gap-6">
|
||||
<div>
|
||||
<h3 className="text-eerie-black dark:text-bright-gray mb-1 text-base font-normal">
|
||||
<h3 className="text-foreground dark:text-foreground mb-1 text-base font-normal">
|
||||
{t('settings.tools.headers')}
|
||||
</h3>
|
||||
<table className="table-default">
|
||||
<thead>
|
||||
<tr>
|
||||
<th className="text-eerie-black dark:text-bright-gray px-2 py-1 text-left text-sm font-normal">
|
||||
<th className="text-foreground dark:text-foreground px-2 py-1 text-left text-sm font-normal">
|
||||
{t('settings.tools.name')}
|
||||
</th>
|
||||
<th className="text-eerie-black dark:text-bright-gray px-2 py-1 text-left text-sm font-normal">
|
||||
<th className="text-foreground dark:text-foreground px-2 py-1 text-left text-sm font-normal">
|
||||
{t('settings.tools.value')}
|
||||
</th>
|
||||
<th className="text-eerie-black dark:text-bright-gray px-2 py-1 text-left text-sm font-normal">
|
||||
<th className="text-foreground dark:text-foreground px-2 py-1 text-left text-sm font-normal">
|
||||
{t('settings.tools.description')}
|
||||
</th>
|
||||
<th
|
||||
@@ -1724,25 +1718,25 @@ function APIActionTable({
|
||||
</table>
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="text-eerie-black dark:text-bright-gray mb-1 text-base font-normal">
|
||||
<h3 className="text-foreground dark:text-foreground mb-1 text-base font-normal">
|
||||
{t('settings.tools.queryParameters')}
|
||||
</h3>
|
||||
<table className="table-default">
|
||||
<thead>
|
||||
<tr>
|
||||
<th className="text-eerie-black dark:text-bright-gray px-2 py-1 text-left text-sm font-normal">
|
||||
<th className="text-foreground dark:text-foreground px-2 py-1 text-left text-sm font-normal">
|
||||
{t('settings.tools.name')}
|
||||
</th>
|
||||
<th className="text-eerie-black dark:text-bright-gray px-2 py-1 text-left text-sm font-normal">
|
||||
<th className="text-foreground dark:text-foreground px-2 py-1 text-left text-sm font-normal">
|
||||
{t('settings.tools.type')}
|
||||
</th>
|
||||
<th className="text-eerie-black dark:text-bright-gray px-2 py-1 text-left text-sm font-normal">
|
||||
<th className="text-foreground dark:text-foreground px-2 py-1 text-left text-sm font-normal">
|
||||
{t('settings.tools.filledByLLM')}
|
||||
</th>
|
||||
<th className="text-eerie-black dark:text-bright-gray px-2 py-1 text-left text-sm font-normal">
|
||||
<th className="text-foreground dark:text-foreground px-2 py-1 text-left text-sm font-normal">
|
||||
{t('settings.tools.description')}
|
||||
</th>
|
||||
<th className="text-eerie-black dark:text-bright-gray px-2 py-1 text-left text-sm font-normal">
|
||||
<th className="text-foreground dark:text-foreground px-2 py-1 text-left text-sm font-normal">
|
||||
{t('settings.tools.value')}
|
||||
</th>
|
||||
<th
|
||||
@@ -1759,25 +1753,25 @@ function APIActionTable({
|
||||
</table>
|
||||
</div>
|
||||
<div className="mb-6">
|
||||
<h3 className="text-eerie-black dark:text-bright-gray mb-1 text-base font-normal">
|
||||
<h3 className="text-foreground dark:text-foreground mb-1 text-base font-normal">
|
||||
{t('settings.tools.body')}
|
||||
</h3>
|
||||
<table className="table-default">
|
||||
<thead>
|
||||
<tr>
|
||||
<th className="text-eerie-black dark:text-bright-gray px-2 py-1 text-left text-sm font-normal">
|
||||
<th className="text-foreground dark:text-foreground px-2 py-1 text-left text-sm font-normal">
|
||||
{t('settings.tools.name')}
|
||||
</th>
|
||||
<th className="text-eerie-black dark:text-bright-gray px-2 py-1 text-left text-sm font-normal">
|
||||
<th className="text-foreground dark:text-foreground px-2 py-1 text-left text-sm font-normal">
|
||||
{t('settings.tools.type')}
|
||||
</th>
|
||||
<th className="text-eerie-black dark:text-bright-gray px-2 py-1 text-left text-sm font-normal">
|
||||
<th className="text-foreground dark:text-foreground px-2 py-1 text-left text-sm font-normal">
|
||||
{t('settings.tools.filledByLLM')}
|
||||
</th>
|
||||
<th className="text-eerie-black dark:text-bright-gray px-2 py-1 text-left text-sm font-normal">
|
||||
<th className="text-foreground dark:text-foreground px-2 py-1 text-left text-sm font-normal">
|
||||
{t('settings.tools.description')}
|
||||
</th>
|
||||
<th className="text-eerie-black dark:text-bright-gray px-2 py-1 text-left text-sm font-normal">
|
||||
<th className="text-foreground dark:text-foreground px-2 py-1 text-left text-sm font-normal">
|
||||
{t('settings.tools.value')}
|
||||
</th>
|
||||
<th
|
||||
|
||||
@@ -7,9 +7,9 @@ import userService from '../api/services/userService';
|
||||
import Edit from '../assets/edit.svg';
|
||||
import NoFilesDarkIcon from '../assets/no-files-dark.svg';
|
||||
import NoFilesIcon from '../assets/no-files.svg';
|
||||
import SearchIcon from '../assets/search.svg';
|
||||
import ThreeDotsIcon from '../assets/three-dots.svg';
|
||||
import ContextMenu, { MenuOption } from '../components/ContextMenu';
|
||||
import Input from '../components/Input';
|
||||
import Spinner from '../components/Spinner';
|
||||
import ToggleSwitch from '../components/ToggleSwitch';
|
||||
import { useDarkTheme } from '../hooks';
|
||||
@@ -210,9 +210,17 @@ export default function Tools() {
|
||||
) : (
|
||||
<div className="mt-8">
|
||||
<div className="relative flex flex-col">
|
||||
<p className="text-muted-foreground mb-5 text-[15px] leading-6">
|
||||
{t('settings.tools.subtitle')}
|
||||
</p>
|
||||
<div className="my-3 flex flex-col items-start gap-3 sm:flex-row sm:items-center sm:justify-between">
|
||||
<div className="w-full sm:w-auto">
|
||||
<Input
|
||||
<div className="relative w-full max-w-md">
|
||||
<img
|
||||
src={SearchIcon}
|
||||
alt=""
|
||||
className="absolute top-1/2 left-4 h-5 w-5 -translate-y-1/2 opacity-40"
|
||||
/>
|
||||
<input
|
||||
maxLength={256}
|
||||
placeholder={t('settings.tools.searchPlaceholder')}
|
||||
name="Document-search-input"
|
||||
@@ -220,11 +228,11 @@ export default function Tools() {
|
||||
id="tool-search-input"
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
borderVariant="thin"
|
||||
className="border-border bg-card text-foreground placeholder:text-muted-foreground h-11 w-full rounded-full border py-2 pr-5 pl-11 text-sm shadow-[0_1px_4px_rgba(0,0,0,0.06)] transition-shadow outline-none focus:shadow-[0_2px_8px_rgba(0,0,0,0.1)] dark:shadow-none"
|
||||
/>
|
||||
</div>
|
||||
<button
|
||||
className="bg-purple-30 hover:bg-violets-are-blue flex h-8 min-w-[108px] items-center justify-center rounded-full px-4 text-sm whitespace-normal text-white"
|
||||
className="bg-primary hover:bg-primary/90 flex h-11 min-w-[108px] items-center justify-center rounded-full px-4 text-sm whitespace-normal text-white"
|
||||
onClick={() => {
|
||||
setAddToolModalState('ACTIVE');
|
||||
}}
|
||||
@@ -232,7 +240,7 @@ export default function Tools() {
|
||||
{t('settings.tools.addTool')}
|
||||
</button>
|
||||
</div>
|
||||
<div className="border-light-silver dark:border-dim-gray mt-5 mb-8 border-b" />
|
||||
<div className="border-border dark:border-border mt-5 mb-8 border-b" />
|
||||
{loading ? (
|
||||
<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">
|
||||
@@ -253,101 +261,117 @@ export default function Tools() {
|
||||
</p>
|
||||
</div>
|
||||
) : (
|
||||
userTools
|
||||
.filter((tool) =>
|
||||
(() => {
|
||||
const filtered = userTools.filter((tool) =>
|
||||
(tool.customName || tool.displayName)
|
||||
.toLowerCase()
|
||||
.includes(searchTerm.toLowerCase()),
|
||||
)
|
||||
.map((tool, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="relative flex h-52 w-[300px] flex-col justify-between overflow-hidden rounded-2xl bg-[#F5F5F5] p-6 hover:bg-[#ECECEC] dark:bg-[#383838] dark:hover:bg-[#303030]"
|
||||
>
|
||||
<div
|
||||
ref={menuRefs.current[tool.id]}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
setActiveMenuId(
|
||||
activeMenuId === tool.id ? null : tool.id,
|
||||
);
|
||||
}}
|
||||
className="absolute top-4 right-4 z-10 cursor-pointer"
|
||||
>
|
||||
<img
|
||||
src={ThreeDotsIcon}
|
||||
alt={t('settings.tools.settingsIconAlt')}
|
||||
className="h-[19px] w-[19px]"
|
||||
/>
|
||||
<ContextMenu
|
||||
isOpen={activeMenuId === tool.id}
|
||||
setIsOpen={(isOpen) => {
|
||||
setActiveMenuId(isOpen ? tool.id : null);
|
||||
}}
|
||||
options={getMenuOptions(tool)}
|
||||
anchorRef={menuRefs.current[tool.id]}
|
||||
position="bottom-right"
|
||||
offset={{ x: 0, y: 0 }}
|
||||
/>
|
||||
</div>
|
||||
<div className="w-full">
|
||||
<div className="flex w-full items-center gap-2 px-1">
|
||||
<img
|
||||
src={`/toolIcons/tool_${tool.name}.svg`}
|
||||
alt={`${tool.displayName} icon`}
|
||||
className="h-6 w-6"
|
||||
/>
|
||||
{tool.name === 'mcp_tool' &&
|
||||
mcpStatuses[tool.id] && (
|
||||
<span
|
||||
className={`inline-flex items-center rounded-full px-2 py-0.5 text-[10px] font-medium leading-none ${
|
||||
mcpStatuses[tool.id] === 'connected'
|
||||
? 'bg-green-100 text-green-700 dark:bg-green-900/40 dark:text-green-300'
|
||||
: mcpStatuses[tool.id] === 'needs_auth'
|
||||
? 'bg-amber-100 text-amber-700 dark:bg-amber-900/40 dark:text-amber-300'
|
||||
: 'bg-gray-100 text-gray-600 dark:bg-gray-700/40 dark:text-gray-300'
|
||||
}`}
|
||||
>
|
||||
{mcpStatuses[tool.id] === 'connected'
|
||||
? t('settings.tools.authStatus.connected')
|
||||
: mcpStatuses[tool.id] === 'needs_auth'
|
||||
? t('settings.tools.authStatus.needsAuth')
|
||||
: t(
|
||||
'settings.tools.authStatus.configured',
|
||||
)}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<div className="mt-[9px]">
|
||||
<p
|
||||
title={tool.customName || tool.displayName}
|
||||
className="text-raisin-black-light dark:text-bright-gray truncate px-1 text-[13px] leading-relaxed font-semibold capitalize"
|
||||
>
|
||||
{tool.customName || tool.displayName}
|
||||
</p>
|
||||
<p
|
||||
className="text-old-silver dark:text-sonic-silver-light mt-1 line-clamp-4 max-h-24 overflow-hidden px-1 text-[12px] leading-relaxed break-all"
|
||||
title={tool.description}
|
||||
>
|
||||
{tool.description}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="absolute right-4 bottom-4">
|
||||
<ToggleSwitch
|
||||
checked={tool.status}
|
||||
onChange={(checked) =>
|
||||
updateToolStatus(tool.id, checked)
|
||||
}
|
||||
size="small"
|
||||
id={`toolToggle-${index}`}
|
||||
ariaLabel={t('settings.tools.toggleToolAria', {
|
||||
toolName: tool.customName || tool.displayName,
|
||||
})}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
return filtered.length === 0 ? (
|
||||
<div className="flex w-full flex-col items-center justify-center py-12">
|
||||
<img
|
||||
src={isDarkTheme ? NoFilesDarkIcon : NoFilesIcon}
|
||||
alt={t('settings.tools.noToolsFound')}
|
||||
className="mx-auto mb-6 h-32 w-32"
|
||||
/>
|
||||
<p className="text-center text-lg text-gray-500 dark:text-gray-400">
|
||||
{t('settings.tools.noToolsFound')}
|
||||
</p>
|
||||
</div>
|
||||
))
|
||||
) : (
|
||||
filtered.map((tool, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="bg-muted hover:bg-accent relative flex h-52 w-[300px] flex-col justify-between overflow-hidden rounded-2xl p-6"
|
||||
>
|
||||
<div
|
||||
ref={menuRefs.current[tool.id]}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
setActiveMenuId(
|
||||
activeMenuId === tool.id ? null : tool.id,
|
||||
);
|
||||
}}
|
||||
className="absolute top-4 right-4 z-10 cursor-pointer"
|
||||
>
|
||||
<img
|
||||
src={ThreeDotsIcon}
|
||||
alt={t('settings.tools.settingsIconAlt')}
|
||||
className="h-[19px] w-[19px]"
|
||||
/>
|
||||
<ContextMenu
|
||||
isOpen={activeMenuId === tool.id}
|
||||
setIsOpen={(isOpen) => {
|
||||
setActiveMenuId(isOpen ? tool.id : null);
|
||||
}}
|
||||
options={getMenuOptions(tool)}
|
||||
anchorRef={menuRefs.current[tool.id]}
|
||||
position="bottom-right"
|
||||
offset={{ x: 0, y: 0 }}
|
||||
/>
|
||||
</div>
|
||||
<div className="w-full">
|
||||
<div className="flex w-full items-center gap-2 px-1">
|
||||
<img
|
||||
src={`/toolIcons/tool_${tool.name}.svg`}
|
||||
alt={`${tool.displayName} icon`}
|
||||
className="h-6 w-6"
|
||||
/>
|
||||
{tool.name === 'mcp_tool' &&
|
||||
mcpStatuses[tool.id] && (
|
||||
<span
|
||||
className={`inline-flex items-center rounded-full px-2 py-0.5 text-[10px] leading-none font-medium ${
|
||||
mcpStatuses[tool.id] === 'connected'
|
||||
? 'bg-green-100 text-green-700 dark:bg-green-900/40 dark:text-green-300'
|
||||
: mcpStatuses[tool.id] === 'needs_auth'
|
||||
? 'bg-amber-100 text-amber-700 dark:bg-amber-900/40 dark:text-amber-300'
|
||||
: 'bg-gray-100 text-gray-600 dark:bg-gray-700/40 dark:text-gray-300'
|
||||
}`}
|
||||
>
|
||||
{mcpStatuses[tool.id] === 'connected'
|
||||
? t('settings.tools.authStatus.connected')
|
||||
: mcpStatuses[tool.id] === 'needs_auth'
|
||||
? t(
|
||||
'settings.tools.authStatus.needsAuth',
|
||||
)
|
||||
: t(
|
||||
'settings.tools.authStatus.configured',
|
||||
)}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<div className="mt-[9px]">
|
||||
<p
|
||||
title={tool.customName || tool.displayName}
|
||||
className="text-foreground dark:text-foreground truncate px-1 text-[13px] leading-relaxed font-semibold capitalize"
|
||||
>
|
||||
{tool.customName || tool.displayName}
|
||||
</p>
|
||||
<p
|
||||
className="text-muted-foreground mt-1 line-clamp-4 max-h-24 overflow-hidden px-1 text-[12px] leading-relaxed break-all"
|
||||
title={tool.description}
|
||||
>
|
||||
{tool.description}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="absolute right-4 bottom-4">
|
||||
<ToggleSwitch
|
||||
checked={tool.status}
|
||||
onChange={(checked) =>
|
||||
updateToolStatus(tool.id, checked)
|
||||
}
|
||||
size="small"
|
||||
id={`toolToggle-${index}`}
|
||||
ariaLabel={t('settings.tools.toggleToolAria', {
|
||||
toolName: tool.customName || tool.displayName,
|
||||
})}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
))
|
||||
);
|
||||
})()
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -92,7 +92,7 @@ export default function Settings() {
|
||||
|
||||
return (
|
||||
<div className="h-full overflow-auto p-4 md:p-12">
|
||||
<p className="text-eerie-black dark:text-bright-gray text-2xl font-bold">
|
||||
<p className="text-foreground dark:text-foreground text-2xl font-bold">
|
||||
{t('settings.label')}
|
||||
</p>
|
||||
<SettingsBar
|
||||
|
||||
@@ -78,4 +78,4 @@ export default store;
|
||||
|
||||
// TODO : use https://redux-toolkit.js.org/tutorials/typescript#define-typed-hooks everywere instead of direct useDispatch
|
||||
|
||||
// TODO : streamline async state management
|
||||
// TODO : streamline async state management
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user